summaryrefslogtreecommitdiff
path: root/chromium/v8
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/v8
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/v8')
-rw-r--r--chromium/v8/AUTHORS64
-rw-r--r--chromium/v8/ChangeLog5744
-rw-r--r--chromium/v8/DEPS30
-rw-r--r--chromium/v8/LICENSE54
-rw-r--r--chromium/v8/LICENSE.strongtalk29
-rw-r--r--chromium/v8/LICENSE.v826
-rw-r--r--chromium/v8/LICENSE.valgrind45
-rw-r--r--chromium/v8/Makefile418
-rw-r--r--chromium/v8/Makefile.android102
-rw-r--r--chromium/v8/Makefile.nacl98
-rw-r--r--chromium/v8/OWNERS14
-rw-r--r--chromium/v8/PRESUBMIT.py75
-rw-r--r--chromium/v8/benchmarks/README.txt86
-rw-r--r--chromium/v8/benchmarks/base.js284
-rw-r--r--chromium/v8/benchmarks/crypto.js1698
-rw-r--r--chromium/v8/benchmarks/deltablue.js880
-rw-r--r--chromium/v8/benchmarks/earley-boyer.js4684
-rw-r--r--chromium/v8/benchmarks/navier-stokes.js387
-rw-r--r--chromium/v8/benchmarks/raytrace.js904
-rw-r--r--chromium/v8/benchmarks/regexp.js1764
-rw-r--r--chromium/v8/benchmarks/revisions.html104
-rw-r--r--chromium/v8/benchmarks/richards.js539
-rw-r--r--chromium/v8/benchmarks/run.html143
-rw-r--r--chromium/v8/benchmarks/run.js62
-rw-r--r--chromium/v8/benchmarks/spinning-balls/index.html11
-rw-r--r--chromium/v8/benchmarks/spinning-balls/splay-tree.js326
-rw-r--r--chromium/v8/benchmarks/spinning-balls/v.js498
-rw-r--r--chromium/v8/benchmarks/splay.js394
-rw-r--r--chromium/v8/benchmarks/style.css77
-rw-r--r--chromium/v8/benchmarks/v8-logo.pngbin0 -> 24293 bytes
-rw-r--r--chromium/v8/build/README.txt9
-rw-r--r--chromium/v8/build/all.gyp19
-rw-r--r--chromium/v8/build/android.gypi252
-rw-r--r--chromium/v8/build/features.gypi111
-rwxr-xr-xchromium/v8/build/gyp_v8171
-rw-r--r--chromium/v8/build/shim_headers.gypi73
-rw-r--r--chromium/v8/build/standalone.gypi285
-rw-r--r--chromium/v8/build/toolchain.gypi696
-rwxr-xr-xchromium/v8/include/v8-debug.h376
-rw-r--r--chromium/v8/include/v8-preparser.h84
-rw-r--r--chromium/v8/include/v8-profiler.h584
-rw-r--r--chromium/v8/include/v8-testing.h75
-rw-r--r--chromium/v8/include/v8.h6595
-rw-r--r--chromium/v8/include/v8stdint.h54
-rw-r--r--chromium/v8/preparser/preparser-process.cc372
-rw-r--r--chromium/v8/preparser/preparser.gyp58
-rw-r--r--chromium/v8/samples/count-hosts.js42
-rw-r--r--chromium/v8/samples/lineprocessor.cc450
-rw-r--r--chromium/v8/samples/process.cc654
-rw-r--r--chromium/v8/samples/samples.gyp76
-rw-r--r--chromium/v8/samples/shell.cc355
-rw-r--r--chromium/v8/src/accessors.cc857
-rw-r--r--chromium/v8/src/accessors.h125
-rw-r--r--chromium/v8/src/allocation-inl.h49
-rw-r--r--chromium/v8/src/allocation.cc123
-rw-r--r--chromium/v8/src/allocation.h142
-rw-r--r--chromium/v8/src/api.cc8182
-rw-r--r--chromium/v8/src/api.h722
-rw-r--r--chromium/v8/src/apinatives.js122
-rw-r--r--chromium/v8/src/apiutils.h49
-rw-r--r--chromium/v8/src/arguments.cc207
-rw-r--r--chromium/v8/src/arguments.h364
-rw-r--r--chromium/v8/src/arm/assembler-arm-inl.h526
-rw-r--r--chromium/v8/src/arm/assembler-arm.cc3360
-rw-r--r--chromium/v8/src/arm/assembler-arm.h1585
-rw-r--r--chromium/v8/src/arm/builtins-arm.cc1484
-rw-r--r--chromium/v8/src/arm/code-stubs-arm.cc7172
-rw-r--r--chromium/v8/src/arm/code-stubs-arm.h569
-rw-r--r--chromium/v8/src/arm/codegen-arm.cc893
-rw-r--r--chromium/v8/src/arm/codegen-arm.h115
-rw-r--r--chromium/v8/src/arm/constants-arm.cc154
-rw-r--r--chromium/v8/src/arm/constants-arm.h768
-rw-r--r--chromium/v8/src/arm/cpu-arm.cc120
-rw-r--r--chromium/v8/src/arm/debug-arm.cc334
-rw-r--r--chromium/v8/src/arm/deoptimizer-arm.cc618
-rw-r--r--chromium/v8/src/arm/disasm-arm.cc1807
-rw-r--r--chromium/v8/src/arm/frames-arm.cc53
-rw-r--r--chromium/v8/src/arm/frames-arm.h181
-rw-r--r--chromium/v8/src/arm/full-codegen-arm.cc4901
-rw-r--r--chromium/v8/src/arm/ic-arm.cc1662
-rw-r--r--chromium/v8/src/arm/lithium-arm.cc2608
-rw-r--r--chromium/v8/src/arm/lithium-arm.h2770
-rw-r--r--chromium/v8/src/arm/lithium-codegen-arm.cc5784
-rw-r--r--chromium/v8/src/arm/lithium-codegen-arm.h503
-rw-r--r--chromium/v8/src/arm/lithium-gap-resolver-arm.cc318
-rw-r--r--chromium/v8/src/arm/lithium-gap-resolver-arm.h83
-rw-r--r--chromium/v8/src/arm/macro-assembler-arm.cc3883
-rw-r--r--chromium/v8/src/arm/macro-assembler-arm.h1467
-rw-r--r--chromium/v8/src/arm/regexp-macro-assembler-arm.cc1371
-rw-r--r--chromium/v8/src/arm/regexp-macro-assembler-arm.h257
-rw-r--r--chromium/v8/src/arm/simulator-arm.cc3914
-rw-r--r--chromium/v8/src/arm/simulator-arm.h481
-rw-r--r--chromium/v8/src/arm/stub-cache-arm.cc3700
-rw-r--r--chromium/v8/src/array-iterator.js126
-rw-r--r--chromium/v8/src/array.js1665
-rw-r--r--chromium/v8/src/arraybuffer.js103
-rw-r--r--chromium/v8/src/assembler.cc1686
-rw-r--r--chromium/v8/src/assembler.h1083
-rw-r--r--chromium/v8/src/assert-scope.h184
-rw-r--r--chromium/v8/src/ast.cc1199
-rw-r--r--chromium/v8/src/ast.h3234
-rw-r--r--chromium/v8/src/atomicops.h170
-rw-r--r--chromium/v8/src/atomicops_internals_arm_gcc.h145
-rw-r--r--chromium/v8/src/atomicops_internals_mips_gcc.h174
-rw-r--r--chromium/v8/src/atomicops_internals_tsan.h400
-rw-r--r--chromium/v8/src/atomicops_internals_x86_gcc.cc135
-rw-r--r--chromium/v8/src/atomicops_internals_x86_gcc.h287
-rw-r--r--chromium/v8/src/atomicops_internals_x86_macosx.h301
-rw-r--r--chromium/v8/src/atomicops_internals_x86_msvc.h203
-rw-r--r--chromium/v8/src/bignum-dtoa.cc658
-rw-r--r--chromium/v8/src/bignum-dtoa.h81
-rw-r--r--chromium/v8/src/bignum.cc775
-rw-r--r--chromium/v8/src/bignum.h141
-rw-r--r--chromium/v8/src/bootstrapper.cc2694
-rw-r--r--chromium/v8/src/bootstrapper.h195
-rw-r--r--chromium/v8/src/builtins.cc1835
-rw-r--r--chromium/v8/src/builtins.h418
-rw-r--r--chromium/v8/src/bytecodes-irregexp.h104
-rw-r--r--chromium/v8/src/cached-powers.cc179
-rw-r--r--chromium/v8/src/cached-powers.h64
-rw-r--r--chromium/v8/src/char-predicates-inl.h106
-rw-r--r--chromium/v8/src/char-predicates.h71
-rw-r--r--chromium/v8/src/checks.cc113
-rw-r--r--chromium/v8/src/checks.h301
-rw-r--r--chromium/v8/src/circular-queue-inl.h62
-rw-r--r--chromium/v8/src/circular-queue.cc125
-rw-r--r--chromium/v8/src/circular-queue.h106
-rw-r--r--chromium/v8/src/code-stubs-hydrogen.cc907
-rw-r--r--chromium/v8/src/code-stubs.cc808
-rw-r--r--chromium/v8/src/code-stubs.h2316
-rw-r--r--chromium/v8/src/code.h74
-rw-r--r--chromium/v8/src/codegen.cc235
-rw-r--r--chromium/v8/src/codegen.h118
-rw-r--r--chromium/v8/src/collection.js407
-rw-r--r--chromium/v8/src/compilation-cache.cc535
-rw-r--r--chromium/v8/src/compilation-cache.h306
-rw-r--r--chromium/v8/src/compiler-intrinsics.h94
-rw-r--r--chromium/v8/src/compiler.cc1275
-rw-r--r--chromium/v8/src/compiler.h654
-rw-r--r--chromium/v8/src/contexts.cc365
-rw-r--r--chromium/v8/src/contexts.h520
-rw-r--r--chromium/v8/src/conversions-inl.h704
-rw-r--r--chromium/v8/src/conversions.cc437
-rw-r--r--chromium/v8/src/conversions.h158
-rw-r--r--chromium/v8/src/counters.cc85
-rw-r--r--chromium/v8/src/counters.h285
-rw-r--r--chromium/v8/src/cpu-profiler-inl.h79
-rw-r--r--chromium/v8/src/cpu-profiler.cc509
-rw-r--r--chromium/v8/src/cpu-profiler.h268
-rw-r--r--chromium/v8/src/cpu.h69
-rw-r--r--chromium/v8/src/d8-debug.cc374
-rw-r--r--chromium/v8/src/d8-debug.h156
-rw-r--r--chromium/v8/src/d8-posix.cc705
-rw-r--r--chromium/v8/src/d8-readline.cc178
-rw-r--r--chromium/v8/src/d8-windows.cc42
-rw-r--r--chromium/v8/src/d8.cc1741
-rw-r--r--chromium/v8/src/d8.gyp133
-rw-r--r--chromium/v8/src/d8.h429
-rw-r--r--chromium/v8/src/d8.js2253
-rw-r--r--chromium/v8/src/data-flow.cc66
-rw-r--r--chromium/v8/src/data-flow.h262
-rw-r--r--chromium/v8/src/date.cc384
-rw-r--r--chromium/v8/src/date.h260
-rw-r--r--chromium/v8/src/date.js829
-rw-r--r--chromium/v8/src/dateparser-inl.h334
-rw-r--r--chromium/v8/src/dateparser.cc213
-rw-r--r--chromium/v8/src/dateparser.h409
-rw-r--r--chromium/v8/src/debug-agent.cc462
-rw-r--r--chromium/v8/src/debug-agent.h132
-rw-r--r--chromium/v8/src/debug-debugger.js2638
-rw-r--r--chromium/v8/src/debug.cc3874
-rw-r--r--chromium/v8/src/debug.h1070
-rw-r--r--chromium/v8/src/deoptimizer.cc3318
-rw-r--r--chromium/v8/src/deoptimizer.h1012
-rw-r--r--chromium/v8/src/disasm.h80
-rw-r--r--chromium/v8/src/disassembler.cc369
-rw-r--r--chromium/v8/src/disassembler.h58
-rw-r--r--chromium/v8/src/diy-fp.cc59
-rw-r--r--chromium/v8/src/diy-fp.h117
-rw-r--r--chromium/v8/src/double.h232
-rw-r--r--chromium/v8/src/dtoa.cc106
-rw-r--r--chromium/v8/src/dtoa.h85
-rw-r--r--chromium/v8/src/effects.h361
-rw-r--r--chromium/v8/src/elements-kind.cc140
-rw-r--r--chromium/v8/src/elements-kind.h234
-rw-r--r--chromium/v8/src/elements.cc2073
-rw-r--r--chromium/v8/src/elements.h208
-rw-r--r--chromium/v8/src/execution.cc934
-rw-r--r--chromium/v8/src/execution.h308
-rw-r--r--chromium/v8/src/extensions/externalize-string-extension.cc145
-rw-r--r--chromium/v8/src/extensions/externalize-string-extension.h50
-rw-r--r--chromium/v8/src/extensions/gc-extension.cc63
-rw-r--r--chromium/v8/src/extensions/gc-extension.h47
-rw-r--r--chromium/v8/src/extensions/i18n/break-iterator.cc333
-rw-r--r--chromium/v8/src/extensions/i18n/break-iterator.h85
-rw-r--r--chromium/v8/src/extensions/i18n/break-iterator.js197
-rw-r--r--chromium/v8/src/extensions/i18n/collator.js209
-rw-r--r--chromium/v8/src/extensions/i18n/date-format.js474
-rw-r--r--chromium/v8/src/extensions/i18n/footer.js40
-rw-r--r--chromium/v8/src/extensions/i18n/globals.js168
-rw-r--r--chromium/v8/src/extensions/i18n/header.js41
-rw-r--r--chromium/v8/src/extensions/i18n/i18n-extension.cc77
-rw-r--r--chromium/v8/src/extensions/i18n/i18n-extension.h51
-rw-r--r--chromium/v8/src/extensions/i18n/i18n-utils.cc177
-rw-r--r--chromium/v8/src/extensions/i18n/i18n-utils.h91
-rw-r--r--chromium/v8/src/extensions/i18n/i18n-utils.js536
-rw-r--r--chromium/v8/src/extensions/i18n/locale.js190
-rw-r--r--chromium/v8/src/extensions/i18n/number-format.js289
-rw-r--r--chromium/v8/src/extensions/i18n/overrides.js220
-rw-r--r--chromium/v8/src/extensions/statistics-extension.cc159
-rw-r--r--chromium/v8/src/extensions/statistics-extension.h49
-rw-r--r--chromium/v8/src/factory.cc1619
-rw-r--r--chromium/v8/src/factory.h652
-rw-r--r--chromium/v8/src/fast-dtoa.cc738
-rw-r--r--chromium/v8/src/fast-dtoa.h83
-rw-r--r--chromium/v8/src/fixed-dtoa.cc407
-rw-r--r--chromium/v8/src/fixed-dtoa.h55
-rw-r--r--chromium/v8/src/flag-definitions.h822
-rw-r--r--chromium/v8/src/flags.cc557
-rw-r--r--chromium/v8/src/flags.h82
-rw-r--r--chromium/v8/src/frames-inl.h346
-rw-r--r--chromium/v8/src/frames.cc1637
-rw-r--r--chromium/v8/src/frames.h931
-rw-r--r--chromium/v8/src/full-codegen.cc1625
-rw-r--r--chromium/v8/src/full-codegen.h945
-rw-r--r--chromium/v8/src/func-name-inferrer.cc107
-rw-r--r--chromium/v8/src/func-name-inferrer.h131
-rw-r--r--chromium/v8/src/gdb-jit.cc2199
-rw-r--r--chromium/v8/src/gdb-jit.h146
-rw-r--r--chromium/v8/src/generator.js92
-rw-r--r--chromium/v8/src/global-handles.cc1086
-rw-r--r--chromium/v8/src/global-handles.h407
-rw-r--r--chromium/v8/src/globals.h452
-rw-r--r--chromium/v8/src/handles-inl.h212
-rw-r--r--chromium/v8/src/handles.cc918
-rw-r--r--chromium/v8/src/handles.h358
-rw-r--r--chromium/v8/src/harmony-array.js124
-rw-r--r--chromium/v8/src/harmony-string.js154
-rw-r--r--chromium/v8/src/hashmap.h364
-rw-r--r--chromium/v8/src/heap-inl.h855
-rw-r--r--chromium/v8/src/heap-profiler.cc145
-rw-r--r--chromium/v8/src/heap-profiler.h95
-rw-r--r--chromium/v8/src/heap-snapshot-generator-inl.h88
-rw-r--r--chromium/v8/src/heap-snapshot-generator.cc2712
-rw-r--r--chromium/v8/src/heap-snapshot-generator.h677
-rw-r--r--chromium/v8/src/heap.cc8054
-rw-r--r--chromium/v8/src/heap.h3049
-rw-r--r--chromium/v8/src/hydrogen-bce.cc394
-rw-r--r--chromium/v8/src/hydrogen-bce.h72
-rw-r--r--chromium/v8/src/hydrogen-bch.cc410
-rw-r--r--chromium/v8/src/hydrogen-bch.h55
-rw-r--r--chromium/v8/src/hydrogen-canonicalize.cc66
-rw-r--r--chromium/v8/src/hydrogen-canonicalize.h51
-rw-r--r--chromium/v8/src/hydrogen-dce.cc127
-rw-r--r--chromium/v8/src/hydrogen-dce.h56
-rw-r--r--chromium/v8/src/hydrogen-dehoist.cc80
-rw-r--r--chromium/v8/src/hydrogen-dehoist.h51
-rw-r--r--chromium/v8/src/hydrogen-deoptimizing-mark.cc126
-rw-r--r--chromium/v8/src/hydrogen-deoptimizing-mark.h56
-rw-r--r--chromium/v8/src/hydrogen-environment-liveness.cc248
-rw-r--r--chromium/v8/src/hydrogen-environment-liveness.h88
-rw-r--r--chromium/v8/src/hydrogen-escape-analysis.cc296
-rw-r--r--chromium/v8/src/hydrogen-escape-analysis.h88
-rw-r--r--chromium/v8/src/hydrogen-gvn.cc855
-rw-r--r--chromium/v8/src/hydrogen-gvn.h89
-rw-r--r--chromium/v8/src/hydrogen-infer-representation.cc172
-rw-r--r--chromium/v8/src/hydrogen-infer-representation.h57
-rw-r--r--chromium/v8/src/hydrogen-infer-types.cc77
-rw-r--r--chromium/v8/src/hydrogen-infer-types.h59
-rw-r--r--chromium/v8/src/hydrogen-instructions.cc4006
-rw-r--r--chromium/v8/src/hydrogen-instructions.h6797
-rw-r--r--chromium/v8/src/hydrogen-mark-deoptimize.cc84
-rw-r--r--chromium/v8/src/hydrogen-mark-deoptimize.h75
-rw-r--r--chromium/v8/src/hydrogen-minus-zero.cc83
-rw-r--r--chromium/v8/src/hydrogen-minus-zero.h56
-rw-r--r--chromium/v8/src/hydrogen-osr.cc125
-rw-r--r--chromium/v8/src/hydrogen-osr.h70
-rw-r--r--chromium/v8/src/hydrogen-range-analysis.cc200
-rw-r--r--chromium/v8/src/hydrogen-range-analysis.h59
-rw-r--r--chromium/v8/src/hydrogen-redundant-phi.cc76
-rw-r--r--chromium/v8/src/hydrogen-redundant-phi.h53
-rw-r--r--chromium/v8/src/hydrogen-removable-simulates.cc94
-rw-r--r--chromium/v8/src/hydrogen-removable-simulates.h51
-rw-r--r--chromium/v8/src/hydrogen-representation-changes.cc178
-rw-r--r--chromium/v8/src/hydrogen-representation-changes.h55
-rw-r--r--chromium/v8/src/hydrogen-sce.cc62
-rw-r--r--chromium/v8/src/hydrogen-sce.h48
-rw-r--r--chromium/v8/src/hydrogen-uint32-analysis.cc227
-rw-r--r--chromium/v8/src/hydrogen-uint32-analysis.h59
-rw-r--r--chromium/v8/src/hydrogen.cc9852
-rw-r--r--chromium/v8/src/hydrogen.h2331
-rw-r--r--chromium/v8/src/i18n.cc938
-rw-r--r--chromium/v8/src/i18n.h129
-rw-r--r--chromium/v8/src/ia32/assembler-ia32-inl.h528
-rw-r--r--chromium/v8/src/ia32/assembler-ia32.cc2717
-rw-r--r--chromium/v8/src/ia32/assembler-ia32.h1239
-rw-r--r--chromium/v8/src/ia32/builtins-ia32.cc1363
-rw-r--r--chromium/v8/src/ia32/code-stubs-ia32.cc7723
-rw-r--r--chromium/v8/src/ia32/code-stubs-ia32.h568
-rw-r--r--chromium/v8/src/ia32/codegen-ia32.cc1181
-rw-r--r--chromium/v8/src/ia32/codegen-ia32.h107
-rw-r--r--chromium/v8/src/ia32/cpu-ia32.cc91
-rw-r--r--chromium/v8/src/ia32/debug-ia32.cc372
-rw-r--r--chromium/v8/src/ia32/deoptimizer-ia32.cc727
-rw-r--r--chromium/v8/src/ia32/disasm-ia32.cc1727
-rw-r--r--chromium/v8/src/ia32/frames-ia32.cc51
-rw-r--r--chromium/v8/src/ia32/frames-ia32.h146
-rw-r--r--chromium/v8/src/ia32/full-codegen-ia32.cc4903
-rw-r--r--chromium/v8/src/ia32/ic-ia32.cc1667
-rw-r--r--chromium/v8/src/ia32/lithium-codegen-ia32.cc6461
-rw-r--r--chromium/v8/src/ia32/lithium-codegen-ia32.h514
-rw-r--r--chromium/v8/src/ia32/lithium-gap-resolver-ia32.cc549
-rw-r--r--chromium/v8/src/ia32/lithium-gap-resolver-ia32.h110
-rw-r--r--chromium/v8/src/ia32/lithium-ia32.cc2742
-rw-r--r--chromium/v8/src/ia32/lithium-ia32.h2908
-rw-r--r--chromium/v8/src/ia32/macro-assembler-ia32.cc3235
-rw-r--r--chromium/v8/src/ia32/macro-assembler-ia32.h1069
-rw-r--r--chromium/v8/src/ia32/regexp-macro-assembler-ia32.cc1332
-rw-r--r--chromium/v8/src/ia32/regexp-macro-assembler-ia32.h223
-rw-r--r--chromium/v8/src/ia32/simulator-ia32.cc30
-rw-r--r--chromium/v8/src/ia32/simulator-ia32.h74
-rw-r--r--chromium/v8/src/ia32/stub-cache-ia32.cc3777
-rw-r--r--chromium/v8/src/ic-inl.h145
-rw-r--r--chromium/v8/src/ic.cc3115
-rw-r--r--chromium/v8/src/ic.h858
-rw-r--r--chromium/v8/src/icu_util.cc62
-rw-r--r--chromium/v8/src/icu_util.h42
-rw-r--r--chromium/v8/src/incremental-marking-inl.h145
-rw-r--r--chromium/v8/src/incremental-marking.cc1042
-rw-r--r--chromium/v8/src/incremental-marking.h288
-rw-r--r--chromium/v8/src/interface.cc244
-rw-r--r--chromium/v8/src/interface.h240
-rw-r--r--chromium/v8/src/interpreter-irregexp.cc647
-rw-r--r--chromium/v8/src/interpreter-irregexp.h49
-rw-r--r--chromium/v8/src/isolate-inl.h73
-rw-r--r--chromium/v8/src/isolate.cc2534
-rw-r--r--chromium/v8/src/isolate.h1525
-rw-r--r--chromium/v8/src/json-parser.h809
-rw-r--r--chromium/v8/src/json-stringifier.h852
-rw-r--r--chromium/v8/src/json.js240
-rw-r--r--chromium/v8/src/jsregexp-inl.h106
-rw-r--r--chromium/v8/src/jsregexp.cc6134
-rw-r--r--chromium/v8/src/jsregexp.h1624
-rw-r--r--chromium/v8/src/lazy-instance.h263
-rw-r--r--chromium/v8/src/list-inl.h282
-rw-r--r--chromium/v8/src/list.h221
-rw-r--r--chromium/v8/src/lithium-allocator-inl.h163
-rw-r--r--chromium/v8/src/lithium-allocator.cc2206
-rw-r--r--chromium/v8/src/lithium-allocator.h662
-rw-r--r--chromium/v8/src/lithium.cc502
-rw-r--r--chromium/v8/src/lithium.h822
-rw-r--r--chromium/v8/src/liveedit-debugger.js1137
-rw-r--r--chromium/v8/src/liveedit.cc2137
-rw-r--r--chromium/v8/src/liveedit.h183
-rw-r--r--chromium/v8/src/log-inl.h54
-rw-r--r--chromium/v8/src/log-utils.cc264
-rw-r--r--chromium/v8/src/log-utils.h159
-rw-r--r--chromium/v8/src/log.cc1956
-rw-r--r--chromium/v8/src/log.h542
-rw-r--r--chromium/v8/src/macro-assembler.h206
-rw-r--r--chromium/v8/src/macros.py248
-rw-r--r--chromium/v8/src/mark-compact-inl.h100
-rw-r--r--chromium/v8/src/mark-compact.cc4351
-rw-r--r--chromium/v8/src/mark-compact.h1025
-rw-r--r--chromium/v8/src/marking-thread.cc89
-rw-r--r--chromium/v8/src/marking-thread.h71
-rw-r--r--chromium/v8/src/math.js298
-rw-r--r--chromium/v8/src/messages.cc202
-rw-r--r--chromium/v8/src/messages.h116
-rw-r--r--chromium/v8/src/messages.js1350
-rw-r--r--chromium/v8/src/mips/OWNERS2
-rw-r--r--chromium/v8/src/mips/assembler-mips-inl.h425
-rw-r--r--chromium/v8/src/mips/assembler-mips.cc2304
-rw-r--r--chromium/v8/src/mips/assembler-mips.h1219
-rw-r--r--chromium/v8/src/mips/builtins-mips.cc1524
-rw-r--r--chromium/v8/src/mips/code-stubs-mips.cc7588
-rw-r--r--chromium/v8/src/mips/code-stubs-mips.h696
-rw-r--r--chromium/v8/src/mips/codegen-mips.cc668
-rw-r--r--chromium/v8/src/mips/codegen-mips.h117
-rw-r--r--chromium/v8/src/mips/constants-mips.cc359
-rw-r--r--chromium/v8/src/mips/constants-mips.h808
-rw-r--r--chromium/v8/src/mips/cpu-mips.cc100
-rw-r--r--chromium/v8/src/mips/debug-mips.cc345
-rw-r--r--chromium/v8/src/mips/deoptimizer-mips.cc627
-rw-r--r--chromium/v8/src/mips/disasm-mips.cc1064
-rw-r--r--chromium/v8/src/mips/frames-mips.cc52
-rw-r--r--chromium/v8/src/mips/frames-mips.h240
-rw-r--r--chromium/v8/src/mips/full-codegen-mips.cc4933
-rw-r--r--chromium/v8/src/mips/ic-mips.cc1669
-rw-r--r--chromium/v8/src/mips/lithium-codegen-mips.cc5807
-rw-r--r--chromium/v8/src/mips/lithium-codegen-mips.h519
-rw-r--r--chromium/v8/src/mips/lithium-gap-resolver-mips.cc323
-rw-r--r--chromium/v8/src/mips/lithium-gap-resolver-mips.h83
-rw-r--r--chromium/v8/src/mips/lithium-mips.cc2522
-rw-r--r--chromium/v8/src/mips/lithium-mips.h2730
-rw-r--r--chromium/v8/src/mips/macro-assembler-mips.cc5604
-rw-r--r--chromium/v8/src/mips/macro-assembler-mips.h1582
-rw-r--r--chromium/v8/src/mips/regexp-macro-assembler-mips.cc1339
-rw-r--r--chromium/v8/src/mips/regexp-macro-assembler-mips.h258
-rw-r--r--chromium/v8/src/mips/simulator-mips.cc3002
-rw-r--r--chromium/v8/src/mips/simulator-mips.h441
-rw-r--r--chromium/v8/src/mips/stub-cache-mips.cc3800
-rw-r--r--chromium/v8/src/mirror-debugger.js2653
-rw-r--r--chromium/v8/src/misc-intrinsics.h89
-rw-r--r--chromium/v8/src/mksnapshot.cc442
-rw-r--r--chromium/v8/src/natives.h68
-rw-r--r--chromium/v8/src/object-observe.js465
-rw-r--r--chromium/v8/src/objects-debug.cc1177
-rw-r--r--chromium/v8/src/objects-inl.h6253
-rw-r--r--chromium/v8/src/objects-printer.cc1239
-rw-r--r--chromium/v8/src/objects-visiting-inl.h891
-rw-r--r--chromium/v8/src/objects-visiting.cc202
-rw-r--r--chromium/v8/src/objects-visiting.h477
-rw-r--r--chromium/v8/src/objects.cc15986
-rw-r--r--chromium/v8/src/objects.h10180
-rw-r--r--chromium/v8/src/once.cc77
-rw-r--r--chromium/v8/src/once.h123
-rw-r--r--chromium/v8/src/optimizing-compiler-thread.cc222
-rw-r--r--chromium/v8/src/optimizing-compiler-thread.h121
-rw-r--r--chromium/v8/src/parser.cc5942
-rw-r--r--chromium/v8/src/parser.h913
-rw-r--r--chromium/v8/src/platform-cygwin.cc483
-rw-r--r--chromium/v8/src/platform-freebsd.cc448
-rw-r--r--chromium/v8/src/platform-linux.cc770
-rw-r--r--chromium/v8/src/platform-macos.cc455
-rw-r--r--chromium/v8/src/platform-nullos.cc573
-rw-r--r--chromium/v8/src/platform-openbsd.cc514
-rw-r--r--chromium/v8/src/platform-posix.cc999
-rw-r--r--chromium/v8/src/platform-posix.h106
-rw-r--r--chromium/v8/src/platform-solaris.cc491
-rw-r--r--chromium/v8/src/platform-win32.cc1999
-rw-r--r--chromium/v8/src/platform.h826
-rw-r--r--chromium/v8/src/preparse-data-format.h62
-rw-r--r--chromium/v8/src/preparse-data.cc184
-rw-r--r--chromium/v8/src/preparse-data.h231
-rw-r--r--chromium/v8/src/preparser-api.cc196
-rw-r--r--chromium/v8/src/preparser.cc1832
-rw-r--r--chromium/v8/src/preparser.h697
-rw-r--r--chromium/v8/src/prettyprinter.cc1169
-rw-r--r--chromium/v8/src/prettyprinter.h121
-rw-r--r--chromium/v8/src/profile-generator-inl.h103
-rw-r--r--chromium/v8/src/profile-generator.cc716
-rw-r--r--chromium/v8/src/profile-generator.h367
-rw-r--r--chromium/v8/src/property-details.h278
-rw-r--r--chromium/v8/src/property.cc121
-rw-r--r--chromium/v8/src/property.h513
-rw-r--r--chromium/v8/src/proxy.js213
-rw-r--r--chromium/v8/src/regexp-macro-assembler-irregexp-inl.h88
-rw-r--r--chromium/v8/src/regexp-macro-assembler-irregexp.cc477
-rw-r--r--chromium/v8/src/regexp-macro-assembler-irregexp.h148
-rw-r--r--chromium/v8/src/regexp-macro-assembler-tracer.cc434
-rw-r--r--chromium/v8/src/regexp-macro-assembler-tracer.h106
-rw-r--r--chromium/v8/src/regexp-macro-assembler.cc292
-rw-r--r--chromium/v8/src/regexp-macro-assembler.h259
-rw-r--r--chromium/v8/src/regexp-stack.cc111
-rw-r--r--chromium/v8/src/regexp-stack.h148
-rw-r--r--chromium/v8/src/regexp.js485
-rw-r--r--chromium/v8/src/rewriter.cc289
-rw-r--r--chromium/v8/src/rewriter.h50
-rw-r--r--chromium/v8/src/runtime-profiler.cc445
-rw-r--r--chromium/v8/src/runtime-profiler.h93
-rw-r--r--chromium/v8/src/runtime.cc14490
-rw-r--r--chromium/v8/src/runtime.h845
-rw-r--r--chromium/v8/src/runtime.js676
-rw-r--r--chromium/v8/src/safepoint-table.cc237
-rw-r--r--chromium/v8/src/safepoint-table.h253
-rw-r--r--chromium/v8/src/sampler.cc708
-rw-r--r--chromium/v8/src/sampler.h139
-rw-r--r--chromium/v8/src/scanner-character-streams.cc325
-rw-r--r--chromium/v8/src/scanner-character-streams.h129
-rw-r--r--chromium/v8/src/scanner.cc1111
-rw-r--r--chromium/v8/src/scanner.h571
-rw-r--r--chromium/v8/src/scopeinfo.cc562
-rw-r--r--chromium/v8/src/scopeinfo.h196
-rw-r--r--chromium/v8/src/scopes.cc1410
-rw-r--r--chromium/v8/src/scopes.h647
-rw-r--r--chromium/v8/src/serialize.cc1866
-rw-r--r--chromium/v8/src/serialize.h664
-rw-r--r--chromium/v8/src/simulator.h43
-rw-r--r--chromium/v8/src/small-pointer-list.h198
-rw-r--r--chromium/v8/src/smart-pointers.h150
-rw-r--r--chromium/v8/src/snapshot-common.cc140
-rw-r--r--chromium/v8/src/snapshot-empty.cc62
-rw-r--r--chromium/v8/src/snapshot.h100
-rw-r--r--chromium/v8/src/spaces-inl.h364
-rw-r--r--chromium/v8/src/spaces.cc3218
-rw-r--r--chromium/v8/src/spaces.h2887
-rw-r--r--chromium/v8/src/splay-tree-inl.h318
-rw-r--r--chromium/v8/src/splay-tree.h226
-rw-r--r--chromium/v8/src/store-buffer-inl.h87
-rw-r--r--chromium/v8/src/store-buffer.cc727
-rw-r--r--chromium/v8/src/store-buffer.h270
-rw-r--r--chromium/v8/src/string-search.cc41
-rw-r--r--chromium/v8/src/string-search.h580
-rw-r--r--chromium/v8/src/string-stream.cc602
-rw-r--r--chromium/v8/src/string-stream.h223
-rw-r--r--chromium/v8/src/string.js1017
-rw-r--r--chromium/v8/src/strtod.cc442
-rw-r--r--chromium/v8/src/strtod.h40
-rw-r--r--chromium/v8/src/stub-cache.cc2200
-rw-r--r--chromium/v8/src/stub-cache.h1192
-rw-r--r--chromium/v8/src/sweeper-thread.cc108
-rw-r--r--chromium/v8/src/sweeper-thread.h75
-rw-r--r--chromium/v8/src/symbol.js87
-rw-r--r--chromium/v8/src/third_party/valgrind/valgrind.h4033
-rw-r--r--chromium/v8/src/third_party/vtune/ittnotify_config.h484
-rw-r--r--chromium/v8/src/third_party/vtune/ittnotify_types.h113
-rw-r--r--chromium/v8/src/third_party/vtune/jitprofiling.cc499
-rw-r--r--chromium/v8/src/third_party/vtune/jitprofiling.h298
-rw-r--r--chromium/v8/src/third_party/vtune/v8-vtune.h69
-rw-r--r--chromium/v8/src/third_party/vtune/v8vtune.gyp59
-rw-r--r--chromium/v8/src/third_party/vtune/vtune-jit.cc283
-rw-r--r--chromium/v8/src/third_party/vtune/vtune-jit.h82
-rw-r--r--chromium/v8/src/token.cc63
-rw-r--r--chromium/v8/src/token.h311
-rw-r--r--chromium/v8/src/transitions-inl.h198
-rw-r--r--chromium/v8/src/transitions.cc156
-rw-r--r--chromium/v8/src/transitions.h211
-rw-r--r--chromium/v8/src/type-info.cc708
-rw-r--r--chromium/v8/src/type-info.h353
-rw-r--r--chromium/v8/src/typedarray.js595
-rw-r--r--chromium/v8/src/types.cc538
-rw-r--r--chromium/v8/src/types.h335
-rw-r--r--chromium/v8/src/typing.cc704
-rw-r--r--chromium/v8/src/typing.h102
-rw-r--r--chromium/v8/src/unbound-queue-inl.h107
-rw-r--r--chromium/v8/src/unbound-queue.h69
-rw-r--r--chromium/v8/src/unicode-inl.h218
-rw-r--r--chromium/v8/src/unicode.cc1873
-rw-r--r--chromium/v8/src/unicode.h274
-rw-r--r--chromium/v8/src/uri.h309
-rw-r--r--chromium/v8/src/uri.js457
-rw-r--r--chromium/v8/src/utils-inl.h48
-rw-r--r--chromium/v8/src/utils.cc114
-rw-r--r--chromium/v8/src/utils.h1144
-rw-r--r--chromium/v8/src/v8-counters.cc96
-rw-r--r--chromium/v8/src/v8-counters.h402
-rw-r--r--chromium/v8/src/v8.cc337
-rw-r--r--chromium/v8/src/v8.h161
-rw-r--r--chromium/v8/src/v8checks.h64
-rw-r--r--chromium/v8/src/v8conversions.cc132
-rw-r--r--chromium/v8/src/v8conversions.h73
-rw-r--r--chromium/v8/src/v8dll-main.cc44
-rw-r--r--chromium/v8/src/v8globals.h590
-rw-r--r--chromium/v8/src/v8memory.h94
-rw-r--r--chromium/v8/src/v8natives.js1838
-rw-r--r--chromium/v8/src/v8preparserdll-main.cc39
-rw-r--r--chromium/v8/src/v8threads.cc494
-rw-r--r--chromium/v8/src/v8threads.h172
-rw-r--r--chromium/v8/src/v8utils.cc276
-rw-r--r--chromium/v8/src/v8utils.h498
-rw-r--r--chromium/v8/src/variables.cc101
-rw-r--r--chromium/v8/src/variables.h190
-rw-r--r--chromium/v8/src/version.cc116
-rw-r--r--chromium/v8/src/version.h68
-rw-r--r--chromium/v8/src/vm-state-inl.h109
-rw-r--r--chromium/v8/src/vm-state.h70
-rw-r--r--chromium/v8/src/win32-headers.h98
-rw-r--r--chromium/v8/src/win32-math.cc107
-rw-r--r--chromium/v8/src/win32-math.h61
-rw-r--r--chromium/v8/src/x64/assembler-x64-inl.h549
-rw-r--r--chromium/v8/src/x64/assembler-x64.cc3119
-rw-r--r--chromium/v8/src/x64/assembler-x64.h1668
-rw-r--r--chromium/v8/src/x64/builtins-x64.cc1443
-rw-r--r--chromium/v8/src/x64/code-stubs-x64.cc6809
-rw-r--r--chromium/v8/src/x64/code-stubs-x64.h543
-rw-r--r--chromium/v8/src/x64/codegen-x64.cc749
-rw-r--r--chromium/v8/src/x64/codegen-x64.h108
-rw-r--r--chromium/v8/src/x64/cpu-x64.cc89
-rw-r--r--chromium/v8/src/x64/debug-x64.cc362
-rw-r--r--chromium/v8/src/x64/deoptimizer-x64.cc596
-rw-r--r--chromium/v8/src/x64/disasm-x64.cc1874
-rw-r--r--chromium/v8/src/x64/frames-x64.cc51
-rw-r--r--chromium/v8/src/x64/frames-x64.h137
-rw-r--r--chromium/v8/src/x64/full-codegen-x64.cc4891
-rw-r--r--chromium/v8/src/x64/ic-x64.cc1686
-rw-r--r--chromium/v8/src/x64/lithium-codegen-x64.cc5519
-rw-r--r--chromium/v8/src/x64/lithium-codegen-x64.h446
-rw-r--r--chromium/v8/src/x64/lithium-gap-resolver-x64.cc336
-rw-r--r--chromium/v8/src/x64/lithium-gap-resolver-x64.h74
-rw-r--r--chromium/v8/src/x64/lithium-x64.cc2541
-rw-r--r--chromium/v8/src/x64/lithium-x64.h2678
-rw-r--r--chromium/v8/src/x64/macro-assembler-x64.cc4703
-rw-r--r--chromium/v8/src/x64/macro-assembler-x64.h1552
-rw-r--r--chromium/v8/src/x64/regexp-macro-assembler-x64.cc1449
-rw-r--r--chromium/v8/src/x64/regexp-macro-assembler-x64.h302
-rw-r--r--chromium/v8/src/x64/simulator-x64.cc27
-rw-r--r--chromium/v8/src/x64/simulator-x64.h72
-rw-r--r--chromium/v8/src/x64/stub-cache-x64.cc3522
-rw-r--r--chromium/v8/src/zone-inl.h120
-rw-r--r--chromium/v8/src/zone.cc226
-rw-r--r--chromium/v8/src/zone.h261
-rw-r--r--chromium/v8/test/cctest/cctest.gyp217
-rwxr-xr-xchromium/v8/tools/android-build.sh0
-rwxr-xr-xchromium/v8/tools/android-ll-prof.sh69
-rwxr-xr-xchromium/v8/tools/android-run.py109
-rwxr-xr-xchromium/v8/tools/android-sync.sh105
-rwxr-xr-xchromium/v8/tools/bash-completion.sh55
-rw-r--r--chromium/v8/tools/blink_tests/TestExpectations27
-rwxr-xr-xchromium/v8/tools/check-static-initializers.sh62
-rw-r--r--chromium/v8/tools/codemap.js292
-rw-r--r--chromium/v8/tools/common-includes.sh198
-rw-r--r--chromium/v8/tools/consarray.js93
-rw-r--r--chromium/v8/tools/csvparser.js78
-rw-r--r--chromium/v8/tools/disasm.py93
-rwxr-xr-xchromium/v8/tools/freebsd-tick-processor10
-rwxr-xr-xchromium/v8/tools/fuzz-harness.sh92
-rwxr-xr-xchromium/v8/tools/gc-nvp-trace-processor.py389
-rw-r--r--chromium/v8/tools/gcmole/Makefile43
-rw-r--r--chromium/v8/tools/gcmole/README62
-rwxr-xr-xchromium/v8/tools/gcmole/bootstrap.sh126
-rw-r--r--chromium/v8/tools/gcmole/gccause.lua62
-rw-r--r--chromium/v8/tools/gcmole/gcmole.cc1288
-rw-r--r--chromium/v8/tools/gcmole/gcmole.lua384
-rw-r--r--chromium/v8/tools/gdb-v8-support.py154
-rw-r--r--chromium/v8/tools/gen-postmortem-metadata.py479
-rw-r--r--chromium/v8/tools/generate-ten-powers.scm286
-rwxr-xr-xchromium/v8/tools/generate_shim_headers/generate_shim_headers.py122
-rwxr-xr-xchromium/v8/tools/grokdump.py2003
-rw-r--r--chromium/v8/tools/gyp/v8.gyp1041
-rw-r--r--chromium/v8/tools/js2c.py396
-rw-r--r--chromium/v8/tools/jsmin.py282
-rwxr-xr-xchromium/v8/tools/linux-tick-processor40
-rwxr-xr-xchromium/v8/tools/ll_prof.py1011
-rw-r--r--chromium/v8/tools/logreader.js184
-rwxr-xr-xchromium/v8/tools/mac-nm18
-rwxr-xr-xchromium/v8/tools/mac-tick-processor6
-rwxr-xr-xchromium/v8/tools/merge-to-branch.sh280
-rwxr-xr-xchromium/v8/tools/mingw-generate-makefiles.sh97
-rwxr-xr-xchromium/v8/tools/nacl-run.py151
-rw-r--r--chromium/v8/tools/oom_dump/README33
-rw-r--r--chromium/v8/tools/oom_dump/SConstruct42
-rw-r--r--chromium/v8/tools/oom_dump/oom_dump.cc289
-rw-r--r--chromium/v8/tools/perf_tests/chromium_revision1
-rwxr-xr-xchromium/v8/tools/plot-timer-events70
-rwxr-xr-xchromium/v8/tools/presubmit.py435
-rwxr-xr-xchromium/v8/tools/process-heap-prof.py120
-rw-r--r--chromium/v8/tools/profile.js795
-rw-r--r--chromium/v8/tools/profile_view.js219
-rw-r--r--chromium/v8/tools/profviz/composer.js580
-rw-r--r--chromium/v8/tools/profviz/gnuplot-4.6.3-emscripten.js4658
-rw-r--r--chromium/v8/tools/profviz/profviz.css138
-rw-r--r--chromium/v8/tools/profviz/profviz.html153
-rw-r--r--chromium/v8/tools/profviz/profviz.js287
-rw-r--r--chromium/v8/tools/profviz/stdio.js52
-rw-r--r--chromium/v8/tools/profviz/worker.js167
-rwxr-xr-xchromium/v8/tools/push-to-trunk.sh411
-rwxr-xr-xchromium/v8/tools/run-deopt-fuzzer.py467
-rwxr-xr-xchromium/v8/tools/run-llprof.sh69
-rwxr-xr-xchromium/v8/tools/run-tests.py392
-rwxr-xr-xchromium/v8/tools/run-valgrind.py77
-rw-r--r--chromium/v8/tools/splaytree.js327
-rwxr-xr-xchromium/v8/tools/stats-viewer.py472
-rwxr-xr-xchromium/v8/tools/status-file-converter.py39
-rwxr-xr-xchromium/v8/tools/test-server.py215
-rw-r--r--chromium/v8/tools/testrunner/README174
-rw-r--r--chromium/v8/tools/testrunner/__init__.py26
-rw-r--r--chromium/v8/tools/testrunner/local/__init__.py26
-rw-r--r--chromium/v8/tools/testrunner/local/commands.py153
-rw-r--r--chromium/v8/tools/testrunner/local/execution.py183
-rw-r--r--chromium/v8/tools/testrunner/local/junit_output.py49
-rw-r--r--chromium/v8/tools/testrunner/local/old_statusfile.py462
-rw-r--r--chromium/v8/tools/testrunner/local/progress.py284
-rw-r--r--chromium/v8/tools/testrunner/local/statusfile.py150
-rw-r--r--chromium/v8/tools/testrunner/local/testsuite.py194
-rw-r--r--chromium/v8/tools/testrunner/local/utils.py108
-rw-r--r--chromium/v8/tools/testrunner/local/verbose.py99
-rw-r--r--chromium/v8/tools/testrunner/network/__init__.py26
-rw-r--r--chromium/v8/tools/testrunner/network/distro.py90
-rw-r--r--chromium/v8/tools/testrunner/network/endpoint.py124
-rw-r--r--chromium/v8/tools/testrunner/network/network_execution.py254
-rw-r--r--chromium/v8/tools/testrunner/network/perfdata.py120
-rw-r--r--chromium/v8/tools/testrunner/objects/__init__.py26
-rw-r--r--chromium/v8/tools/testrunner/objects/context.py50
-rw-r--r--chromium/v8/tools/testrunner/objects/output.py60
-rw-r--r--chromium/v8/tools/testrunner/objects/peer.py80
-rw-r--r--chromium/v8/tools/testrunner/objects/testcase.py83
-rw-r--r--chromium/v8/tools/testrunner/objects/workpacket.py90
-rw-r--r--chromium/v8/tools/testrunner/server/__init__.py26
-rw-r--r--chromium/v8/tools/testrunner/server/compression.py111
-rw-r--r--chromium/v8/tools/testrunner/server/constants.py51
-rw-r--r--chromium/v8/tools/testrunner/server/daemon.py147
-rw-r--r--chromium/v8/tools/testrunner/server/local_handler.py119
-rw-r--r--chromium/v8/tools/testrunner/server/main.py245
-rw-r--r--chromium/v8/tools/testrunner/server/presence_handler.py120
-rw-r--r--chromium/v8/tools/testrunner/server/signatures.py63
-rw-r--r--chromium/v8/tools/testrunner/server/status_handler.py112
-rw-r--r--chromium/v8/tools/testrunner/server/work_handler.py150
-rw-r--r--chromium/v8/tools/tick-processor.html168
-rw-r--r--chromium/v8/tools/tickprocessor-driver.js62
-rw-r--r--chromium/v8/tools/tickprocessor.js913
-rwxr-xr-xchromium/v8/tools/v8-info.sh161
-rwxr-xr-xchromium/v8/tools/v8-rolls.sh120
-rw-r--r--chromium/v8/tools/v8.xcodeproj/README.txt11
-rw-r--r--chromium/v8/tools/v8heapconst.py255
-rw-r--r--chromium/v8/tools/v8heapconst.py.tmpl30
-rw-r--r--chromium/v8/tools/visual_studio/README.txt12
-rwxr-xr-xchromium/v8/tools/windows-tick-processor.bat30
698 files changed, 561352 insertions, 0 deletions
diff --git a/chromium/v8/AUTHORS b/chromium/v8/AUTHORS
new file mode 100644
index 00000000000..46e3a14bc17
--- /dev/null
+++ b/chromium/v8/AUTHORS
@@ -0,0 +1,64 @@
+# Below is a list of people and organizations that have contributed
+# to the V8 project. Names should be added to the list like so:
+#
+# Name/Organization <email address>
+
+Google Inc.
+Sigma Designs Inc.
+ARM Ltd.
+Hewlett-Packard Development Company, LP
+Igalia, S.L.
+Joyent, Inc.
+Bloomberg Finance L.P.
+NVIDIA Corporation
+
+Akinori MUSHA <knu@FreeBSD.org>
+Alexander Botero-Lowry <alexbl@FreeBSD.org>
+Alexander Karpinsky <homm86@gmail.com>
+Alexandre Vassalotti <avassalotti@gmail.com>
+Andreas Anyuru <andreas.anyuru@gmail.com>
+Bert Belder <bertbelder@gmail.com>
+Burcu Dogan <burcujdogan@gmail.com>
+Craig Schlenter <craig.schlenter@gmail.com>
+Daniel Andersson <kodandersson@gmail.com>
+Daniel James <dnljms@gmail.com>
+Derek J Conrod <dconrod@codeaurora.org>
+Dineel D Sule <dsule@codeaurora.org>
+Erich Ocean <erich.ocean@me.com>
+Fedor Indutny <fedor@indutny.com>
+Filipe David Manana <fdmanana@gmail.com>
+Haitao Feng <haitao.feng@intel.com>
+Ioseb Dzmanashvili <ioseb.dzmanashvili@gmail.com>
+Jan de Mooij <jandemooij@gmail.com>
+Jay Freeman <saurik@saurik.com>
+James Pike <g00gle@chilon.net>
+Joel Stanley <joel.stan@gmail.com>
+John Jozwiak <jjozwiak@codeaurora.org>
+Jonathan Liu <net147@gmail.com>
+Kun Zhang <zhangk@codeaurora.org>
+Luis Reis <luis.m.reis@gmail.com>
+Martyn Capewell <martyn.capewell@arm.com>
+Mathias Bynens <mathias@qiwi.be>
+Matt Hanselman <mjhanselman@gmail.com>
+Maxim Mossienko <maxim.mossienko@gmail.com>
+Michael Lutz <michi@icosahedron.de>
+Michael Smith <mike@w3.org>
+Mike Gilbert <floppymaster@gmail.com>
+Paolo Giarrusso <p.giarrusso@gmail.com>
+Patrick Gansterer <paroga@paroga.com>
+Peter Varga <pvarga@inf.u-szeged.hu>
+Rafal Krypa <rafal@krypa.net>
+Rajeev R Krithivasan <rkrithiv@codeaurora.org>
+Rene Rebe <rene@exactcode.de>
+Robert Mustacchi <rm@fingolfin.org>
+Rodolph Perfetta <rodolph.perfetta@arm.com>
+Ryan Dahl <coldredlemur@gmail.com>
+Sandro Santilli <strk@keybit.net>
+Sanjoy Das <sanjoy@playingwithpointers.com>
+Subrato K De <subratokde@codeaurora.org>
+Tobias Burnus <burnus@net-b.de>
+Vlad Burlik <vladbph@gmail.com>
+Xi Qian <xi.qian@intel.com>
+Yuqiang Xian <yuqiang.xian@intel.com>
+Zaheer Ahmad <zahmad@codeaurora.org>
+Zhongping Wang <kewpie.w.zp@gmail.com>
diff --git a/chromium/v8/ChangeLog b/chromium/v8/ChangeLog
new file mode 100644
index 00000000000..d824cec0d63
--- /dev/null
+++ b/chromium/v8/ChangeLog
@@ -0,0 +1,5744 @@
+2013-08-14: Version 3.20.17
+
+ Fixed Math.round/floor that had bogus Smi representation
+ (Chromium issue 272564)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-08-13: Version 3.20.16
+
+ Fixed bug in HPhi::SimplifyConstantInput (Chromium issue 269679)
+
+ Fixed gcmole bugs in i18n code (issue 2745)
+
+ ia32: Calls to the TranscendentalCacheStub must ensure that esi is
+ set (issue 2827)
+
+ Made sure polymorphic element access creates non-replaying
+ phis. (issue 2815)
+
+ Allowed HPhis to have an invalid merge index. (issue 2815)
+
+ Fixed smi-based math floor. (Chromium issue 270268)
+
+ Deprecated self and total time getters and total sample count
+ getter on CpuProfileNode. (Chromium issue 267595)
+
+ Fixed Object.freeze, Object.observe wrt CountOperation and
+ CompoundAssignment. (issue 2774,2779)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-08-07: Version 3.20.14
+
+ Exposed eternal handle api.
+
+ Bugfix to solve issues with enabling V8 typed arrays in Blink.
+
+ Fixed Array index dehoisting. (Chromium issue 264203)
+
+ Updated Array Iterator to use numeric indexes (issue 2818)
+
+ Return start/end profiling time in microseconds instead of milliseconds
+ (issue 2824)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-08-06: Version 3.20.14
+
+ Added new Harmony methods to Array.prototype object.
+ (issue 2776,v8:2777)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-08-01: Version 3.20.12
+
+ Removed buggy ToNumber truncation (partial fix for issue 2813)
+
+ Calling Map etc without new should throw TypeError (issue 2819)
+
+ Fixed a crash for large code objects on ARM (Chromium issue 2736)
+
+ Fixed stale unhandlified value in JSObject::SetPropertyForResult.
+ (Chromium issue 265894)
+
+ Added new Harmony methods to String.prototype object.
+ (issue 2796,v8:2797,v8:2798,v8:2799)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-30: Version 3.20.11
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-29: Version 3.20.10
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-26: Version 3.20.9
+
+ Check that ExternalString objects get aligned resources.
+
+ Fixed JSArray-specific length lookup in polymorphic array handling
+ (Chromium issues 263276, 263905).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-24: Version 3.20.8
+
+ Deprecated v8::V8::Pause/ResumeProfiler.
+
+ Fixed Chromium issues 247688, 258519 and 260203.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-22: Version 3.20.7
+
+ Deprecated some debugger methods.
+
+ Fixed wrong bailout id in polymorphic stores (Chromium issue 259787).
+
+ Fixed data race in SamplingCircularQueue (Chromium issue 251218).
+
+ Fixed type feedback in presence of negative lookups
+ (Chromium issue 252797).
+
+ Do not materialize context-allocated values for debug-evaluate
+ (Chromium issue 259300).
+
+ Synchronized Compare-Literal behavior in FullCodegen and Hydrogen
+ (Chromium issue 260345).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-17: Version 3.20.6
+
+ Try to remove invalidated stubs before falling back to checking the
+ constant state (Chromium issue 260585).
+
+ Fixed gyp_v8 to work with use_system_icu=1 (issue 2475).
+
+ Fixed sloppy-mode 'const' under Harmony flag (Chromium issue 173361).
+
+ Use internal array as API function cache (Chromium issue 260106).
+
+ Fixed possible stack overflow in range analysis
+ (Chromium issue 259452).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-15: Version 3.20.5
+
+ Ensured that the length of frozen arrays is immutable
+ (issue 2711, Chromium issue 259548).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-10: Version 3.20.4
+
+ Fixed garbage-collection issue that causes a crash on ARM
+ (Chromium issue 254570)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-08: Version 3.20.3
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-05: Version 3.20.2
+
+ Remove deprecated heap profiler methods from V8 public API
+
+ Mark i18n functions as native and set proper names
+ (issue 2745)
+
+ Correctly report stack trace when current function is FunctionApply
+ builtin (Chromium issue 252097)
+
+ Enable GDBJIT interface for standalone by default.
+
+ Fix debuggersupport=off build. (issue 2754)
+
+ Introduce -m64 flag for making x64 when the default gcc compiler is for
+ X32
+
+ Performance and stability improvements on all platforms.
+
+
+2013-07-02: Version 3.20.1
+
+ Implemented WeakMap.prototype.clear function. (issue 2753)
+
+ Ensure CheckInitialized is present independent of define.
+ (Chromium issue 255779)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-28: Version 3.20.0
+
+ Migrated several tests from blink to V8 repository.
+
+ Allowed users of the V8 API to distinguish between unset and undefined
+ HiddenValues (issue 2746).
+
+ Deprecated old style callbacks in the V8 API.
+
+ Turned on parallel recompilation.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-18: Version 3.19.18
+
+ Fixed read-only attribute of Function.length in strict mode.
+ (issue 2705)
+
+ Fixed Runtime_SetProperty to properly handle OOM failures
+ (Chromium issue 249873)
+
+ Emit deprecated check for constant function transitions.
+ (Chromium issue 250609)
+
+ Made MathFloorOfDiv optimization trigger more often
+ (Issue 2205)
+
+ Make more GCs in idle notification handler.
+ (Chromium issue 241815)
+
+ Increased default type info threshold.
+ (Issue 2730)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-14: Version 3.19.16
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-13: Version 3.19.15
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-13: Version 3.19.14
+
+ Fixed crashes when calling new Array(a) with a single argument that
+ could result in creating a holey array with a packed elements kind.
+ (Chromium issue 245480)
+
+ Fixed issues in parallel compilation.
+ (Chromium issue 248076)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-11: Version 3.19.13
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-10: Version 3.19.12
+
+ Fixed arguments array access. (Chromium issue 247303)
+
+ Fixed bug in LookupForWrite. (Chromium issue 242332)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-07: Version 3.19.11
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-06: Version 3.19.10
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-05: Version 3.19.9
+
+ Implemented Load IC support for loading properties from primitive
+ values to avoid perpetual soft deopts. (Chromium issue 242512)
+
+ Implemented Freeing of PerThreadAssertData when possible to avoid
+ memory leak. (Chromium issue 246567)
+
+ Removed V8_USE_OLD_STYLE_PERSISTENT_HANDLE_VISITORS.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-06-03: Version 3.19.8
+
+ Fixed bug with inlining 'Array' function. (Chromium issue 244461)
+
+ Fixed initialization of literal objects. (Chromium issue 245424)
+
+ Fixed function name inferred inside closures. (Chromium issue 224884)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-05-31: Version 3.19.7
+
+ Added support for //# sourceURL similar to deprecated //@ sourceURL one.
+ (issue 2702)
+
+ Made sure IfBuilder::Return clears the current block.
+ (Chromium issue 243868)
+
+ Fixed two CPU profiler tests on ARM and MIPS simulators
+ (issue 2628)
+
+ Fixed idle incremental GC for large objects.
+ (Chromium issue 241815)
+
+ Disabled --optimize-constructed-arrays due to crashes
+ (Chromium issue 244461)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-05-28: Version 3.19.6
+
+ Fixed IfBuilder::Deopt to clear the current block
+ (Chromium issue 243868).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-05-27: Version 3.19.5
+
+ Reset regexp parser flag after scanning ahead for capture groups.
+ (issue 2690)
+
+ Removed flakiness in test-cpu-profiler/SampleWhenFrameIsNotSetup.
+ (issue 2628)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-05-24: Version 3.19.4
+
+ Fixed edge case in stack trace formatting. (Chromium issue 237617)
+
+ Fixed embedded new-space pointer in LCmpObjectEqAndBranch. (Chromium
+ issue 240032)
+
+ Made Object.freeze fast (issue 1858, Chromium issue 115960)
+
+ Fixed bogus deopt in BuildEmitDeepCopy for holey arrays. (Chromium issue
+ 242924)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-05-22: Version 3.19.3
+
+ Performance and stability improvements on all platforms.
+
+
+2013-05-17: Version 3.19.2
+
+ Fill in one-word-fillers for the unused property fields
+ (Chromium issue 240056).
+
+ Removed use_system_v8 logic from the mainline gyp file
+ (Chromium issue 226860).
+
+ Skip CPU profiler samples where top function's stack frame is not
+ set up properly (issue 2628).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-05-14: Version 3.19.1
+
+ Fixed missing hole check for loads from Smi arrays when all uses are
+ changes (Chromium issue 233737)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-05-10: Version 3.19.0
+
+ Deprecated Context::New which returns Persistent.
+
+ Added Persistent<T>::Reset which disposes the handle and redirects it to
+ point to another object.
+
+ Deprecated WriteAscii and MayContainNonAscii.
+
+ Exposed AssertNoAllocation to API.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-04-30: Version 3.18.5
+
+ Allowed setting debugger breakpoints on CompareNilICs (issue 2660)
+
+ Fixed beyond-heap load on x64 Crankshafted StringCharFromCode
+ (Chromium issue 235311)
+
+ Change 'Parse error' to three more informative messages.
+ (Chromium issue 2636)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-04-26: Version 3.18.4
+
+ Added a preliminary API for ES6 ArrayBuffers
+
+ Replaced qsort with std::sort. (Chromium issue 2639)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-04-24: Version 3.18.3
+
+ Exposed the GC under a name that is less collision prone than window.gc.
+ (issue 2641)
+
+ Do not emit double values at their use sites. (Chromium issue 234101)
+
+ Added methods to allow resuming execution after calling
+ TerminateExecution(). (issue 2361)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-04-22: Version 3.18.2
+
+ OS::MemMove/OS::MemCopy: Don't call through to generated code when size
+ == 0 to avoid prefetching invalid memory (Chromium issue 233500)
+
+ Removed heap snapshot size limit. (Chromium issue 232305)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-04-18: Version 3.18.1
+
+ Removed SCons related files and deprecated test suite configurations.
+
+ Improved handling of unary plus (issue 2527).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-04-17: Version 3.18.0
+
+ Enabled pretenuring of fast literals in high promotion mode.
+
+ Removed preparser library; link preparser executable against full V8.
+
+ Fixed set-up of intrinsic's 'constructor' properties.
+ (Chromium issue 229445)
+
+ ES6 symbols: extended V8 API to support symbols (issue 2158).
+
+ Removed ARM support for VFP2.
+
+ Made __proto__ a real JavaScript accessor property.
+ (issue 1949 and issue 2606)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-04-04: Version 3.17.16
+
+ Stack trace API: poison stack frames below the first strict mode frame.
+ (issue 2564)
+
+ Made Isolate::GetHeapStatistics robust against half-initialized
+ isolates (Chromium issue 2591).
+
+ Finished implementation of ES6 symbols aka. private names (issue 2158).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-21: Version 3.17.15
+
+ Rolled back API changes to maintain compatibility with older
+ 3.17.x versions of V8.
+
+ Disable zapping of global handles in release mode.
+
+ Always mark the entire valid prefix of the descriptor array.
+ (Chromium issue 196331)
+
+ Use internal memcpy for CopyWords and when copying code.
+ (Chromium issue 196330)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-20: Version 3.17.14
+
+ Use internal memcpy when initializing code objects.
+ (Chromium issue 196330)
+
+ Disabled weak embedded maps because of crashes.
+ (Chromium issues 172489, 217858)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-19: Version 3.17.13
+
+ Turned Flags into a uint32_t typedef (Chromium issue 194749).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-18: Version 3.17.12
+
+ Unified kMaxArguments with number of bits used to encode it.
+ (Chromium issue 211741)
+
+ Fixed detection of |handle_smi| case in
+ HOptimizedGraphBuilder::HandlePolymorphicCallNamed.
+ (Chromium issue 196583)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-15: Version 3.17.11
+
+ Added a version of the v8::HandleScope constructor with an v8::Isolate
+ parameter and made AdjustAmountOfExternalAllocatedMemory an instance
+ method of v8::Isolate.
+ (issue 2487)
+
+ Fixed two register allocator bugs (off-by-one error/failure
+ propagation). (issue 2576)
+
+ Fixed huge heap snapshot when a heavily shared context has many
+ variables. (Chromium issue 145687)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-13: Version 3.17.10
+
+ Fixed heap snapshot creation for Harmony collections. (issue 2535)
+
+ Fixed register allocation corner case. (Chromium issue 177883)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-08: Version 3.17.9
+
+ Restored Function()'s expected string representation. (issue 2470)
+
+ Enabled deprecatations (again). (issue 2487)
+
+ Avoid bool to Oddball conversions by being lazy. (issue 2491)
+
+ Added %p option to --logfile.
+
+ Hardened Function()'s parsing of function literals. (issue 2470)
+
+ ES6 symbols: Refine test for getOwnPropertyNames. (issue 2158)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-07: Version 3.17.8
+
+ Added missing license headers. (Chromium issue 98597)
+
+ Inserted missing type cast in JSON.stringify. (issue 2570)
+
+ Reverted "Send SIGPROF signals on the profiler event processor thread"
+ (issue 2571)
+
+ Fixed Array.length, String.length and Function.prototype LoadICs on x64.
+ (issue 2568)
+
+ ES6 symbols: filter symbols form for-in loops and Object.keys.
+ (issue 2158)
+
+ Properly handle misses for StoreArrayLengthStub on ia32 and x64
+ (issue 2566)
+
+ Fixed x32 handling of Atomic64. (Chromium issue chromium-os:36866)
+
+ Removed "library" variable from standalone.gypi. (Chromium issue 111541)
+
+ Fixed HCheckSmiOrInt <-> HBoundsCheck interaction wrt. representations.
+ (issue 2556)
+
+ Enabled zapping of disposed global handles in release mode.
+ (Chromium issue 176056)
+
+ Added workaround for redefinition of __proto__ property. (issue 2565)
+
+ ES6 symbols: Allow symbols as property names. (issue 2158)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-03-04: Version 3.17.7
+
+ Limited recursion in regexp compilation by a budget.
+ (Chromium issue 178790)
+
+ ES6 symbols: Implemented Symbol intrinsic and basic functionality
+ (issue 2158)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-28: Version 3.17.6
+
+ Fixed materialization of arguments objects with unknown values.
+ (Chromium issue 163530)
+
+ Set default number of sweeper threads to at most four.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-27: Version 3.17.5
+
+ Made __proto__ a foreign callback on Object.prototype.
+ (issue 621, issue 1949 and issue 2441)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-25: Version 3.17.4
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-21: Version 3.17.3
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-19: Version 3.17.2
+
+ Removed bogus check for TOP register in deoptimizer.
+ (Chromium issue 176943)
+
+ Made the Isolate parameter mandatory for internal HandleScopes.
+ (issue 2487)
+
+ Fixed f.apply() optimization when declared arguments are mutated.
+ (issue 2539)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-14: Version 3.17.1
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-13: Version 3.17.0
+
+ Enabled parallel sweeping.
+
+ Don't try to unlink instructions twice during GVN
+ (Chromium issue 175141)
+
+ Fixed code flusher disabling while marking incrementally.
+ (Chromium issue 173458, 168582)
+
+ Don't use TLS for space iterators.
+ (issue 2531)
+
+ Added new GetHeapStatistics API entry and deprecated old one.
+
+ Fixed DoubleStackSlot-to-DoubleStackSlot moves on ia32. Unified
+ platform-independent code.
+ (Chromium issue 173907)
+
+ Added --trace-array-abuse to help find OOB accesses.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-06: Version 3.16.14
+
+ Performance and stability improvements on all platforms.
+
+
+2013-02-04: Version 3.16.13
+
+ Tagged stubs that rely on instance types as MEGAMORPHIC.
+ (Chromium issue 173974)
+
+ Fixed clearing of dead dependent codes and verifing of weak
+ embedded maps on full GC. (Chromium issue 172488,172489)
+
+ Made the arm port build cleanly with Clang.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-31: Version 3.16.12
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-30: Version 3.16.11
+
+ Put making embedded maps in optimized code weak behind a flag.
+ (Chromium issue 172488,172489)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-25: Version 3.16.10
+
+ Avoid excessive memory usage during redundant phi elimination.
+ (issue 2510)
+
+ Fixed additional spec violations wrt RegExp.lastIndex.
+ (issue 2437)
+
+ Added Isolate parameter to Persistent class.
+ (issue 2487)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-24: Version 3.16.9
+
+ Made embedded maps in optimized code weak.
+ (issue 2073)
+
+ Fixed corner case when JSFunction is evicted from flusher.
+ (Chromium issue 168801)
+
+ Correctly set kCanBeDivByZero flag for HMathFloorOfDiv.
+ (Chromium issue 171641)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-23: Version 3.16.8
+
+ Correctly reset lastIndex in an RegExp object.
+ (Chromium issue 170856)
+
+ Added a workaround for Windows compilation problems related to V8EXPORT.
+ (issue 2507)
+
+ tools/run-tests.py: shlex.split() the value of --command-prefix
+ (Chromium issue 171553)
+
+ Fixed pattern detection for replacing shifts by rotation.
+ (Chromium issue 2499)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-21: Version 3.16.7
+
+ Removed <(library) usage from v8.gyp.
+ (Chromium issue 111541)
+
+ Fixed out of bounds memory access in TestJSArrayForAllocationSiteInfo.
+ (Chromium issue 169928)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-18: Version 3.16.6
+
+ Made the Isolate parameter mandatory in Locker and Unlocker classes.
+ (issue 2487)
+
+ Avoid pointer underflow in CopyCharsUnsigned.
+ (issue 2493)
+
+ Generate shim headers when using system v8.
+ (Chromium issue 165264)
+
+ Fixed arguments materialization for inlined apply().
+ (issue 2489)
+
+ Sync'ed laziness between BuildFunctionInfo and MakeFunctionInfo.
+ (Chromium issue 147497)
+
+ Added sanity check to CodeFlusher::AddCandidate.
+ (Chromium issue 169209)
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-15: Version 3.16.5
+
+ Removed deprecated functions from V8's external API.
+
+ Prepared API for WebKit use of Latin-1.
+
+ Fixed V8 issue 2486.
+
+ Fixed Chromium issue 169723.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-11: Version 3.16.4
+
+ Fixed Chromium issues 168545 and 169209.
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-09: Version 3.16.3
+
+ Improved GC performance when moving parts of a FixedArray (issue 2452).
+
+ Enabled readline on d8 while building a shared lib (issue 1781).
+
+ Fixed missing exception check in typed array constructor
+ (Chromium issue 168545).
+
+ Check for read-only-ness when preparing for array sort (issue 2419).
+
+ Performance and stability improvements on all platforms.
+
+
+2013-01-04: Version 3.16.2
+
+ Added Makefile options to build for the Raspberry Pi (armv7=0,
+ arm_fpu=vfp2).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-12-27: Version 3.16.1
+
+ Fixed x64 MathMinMax for negative untagged int32 arguments.
+ (Chromium issue 164442)
+
+ Fixed FloatingPointHelper::CheckSSE2OperandIsInt32.
+ (issue 2458)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-12-21: Version 3.16.0
+
+ V8_Fatal now prints C++ stack trace in debug mode.
+
+ Added HTML-based tick processor.
+
+ Continued implementation of Object.observe (V8 issue 2409).
+
+ Fixed V8 issues 2243, 2340, 2393, 2399, 2457.
+
+ Fixed Chromium issues 125308, 165637, 166379, 166553.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-12-10: Version 3.15.11
+
+ Define CAN_USE_VFP2/3_INSTRUCTIONS based on arm_neon and arm_fpu GYP
+ flags.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-12-07: Version 3.15.10
+
+ Enabled optimisation of functions inside eval. (issue 2315)
+
+ Fixed spec violations in methods of Number.prototype. (issue 2443)
+
+ Added GCTracer metrics for a scavenger GC for DOM wrappers.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-12-06: Version 3.15.9
+
+ Fixed candidate eviction in code flusher.
+ (Chromium issue 159140)
+
+ Iterate through all arguments for side effects in Math.min/max.
+ (issue 2444)
+
+ Fixed spec violations related to regexp.lastIndex
+ (issue 2437, issue 2438)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-12-04: Version 3.15.8
+
+ Enforced stack allocation of TryCatch blocks.
+ (issue 2166,chromium:152389)
+
+ Fixed external exceptions in external try-catch handlers.
+ (issue 2166)
+
+ Activated incremental code flushing by default.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-11-30: Version 3.15.7
+
+ Activated code aging by default.
+
+ Included more information in --prof log.
+
+ Removed eager sweeping for lazy swept spaces. Try to find in
+ SlowAllocateRaw a bounded number of times a big enough memory slot.
+ (issue 2194)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-11-26: Version 3.15.6
+
+ Ensure double arrays are filled with holes when extended from
+ variations of empty arrays. (Chromium issue 162085)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-11-23: Version 3.15.5
+
+ Fixed JSON.stringify for objects with interceptor handlers.
+ (Chromium issue 161028)
+
+ Fixed corner case in x64 compare stubs. (issue 2416)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-11-16: Version 3.15.4
+
+ Fixed Array.prototype.join evaluation order. (issue 2263)
+
+ Perform CPU sampling by CPU sampling thread only iff processing thread
+ is not running. (issue 2364)
+
+ When using an Object as a set in Object.getOwnPropertyNames, null out
+ the proto. (issue 2410)
+
+ Disabled EXTRA_CHECKS in Release build.
+
+ Heap explorer: Show representation of strings.
+
+ Removed 'type' and 'arguments' properties from Error object.
+ (issue 2397)
+
+ Added atomics implementation for ThreadSanitizer v2.
+ (Chromium issue 128314)
+
+ Fixed LiveEdit crashes when object/array literal is added. (issue 2368)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-11-13: Version 3.15.3
+
+ Changed sample shell to send non-JS output (e.g. errors) to stderr
+ instead of stdout.
+
+ Correctly check for stack overflow even when interrupt is pending.
+ (issue 214)
+
+ Collect stack trace on stack overflow. (issue 2394)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-11-12: Version 3.15.2
+
+ Function::GetScriptOrigin supplies sourceURL when script name is
+ not available. (Chromium issue 159413)
+
+ Made formatting error message side-effect-free. (issue 2398)
+
+ Fixed length check in JSON.stringify. (Chromium issue 160010)
+
+ ES6: Added support for Set and Map clear method (issue 2400)
+
+ Fixed slack tracking when instance prototype changes.
+ (Chromium issue 157019)
+
+ Fixed disabling of code flusher while marking. (Chromium issue 159140)
+
+ Added a test case for object grouping in a scavenger GC (issue 2077)
+
+ Support shared library build of Android for v8.
+ (Chromium issue 158821)
+
+ ES6: Added support for size to Set and Map (issue 2395)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-11-06: Version 3.15.1
+
+ Put incremental code flushing behind a flag. (Chromium issue 159140)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-10-31: Version 3.15.0
+
+ Loosened aligned code target requirement on ARM (issue 2380)
+
+ Fixed JSON.parse to treat leading zeros correctly.
+ (Chromium issue 158185)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-10-22: Version 3.14.5
+
+ Killed off the SCons based build.
+
+ Added a faster API for creating v8::Integer objects.
+
+ Speeded up function deoptimization by avoiding quadratic pass over
+ optimized function list. (Chromium issue 155270)
+
+ Always invoke the default Array.sort functions from builtin functions.
+ (issue 2372)
+
+ Reverted recent CPU profiler changes because they broke --prof.
+ (issue 2364)
+
+ Switched code flushing to use different JSFunction field.
+ (issue 1609)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-10-15: Version 3.14.4
+
+ Allow evals for debugger even if they are prohibited in the debugee
+ context. (Chromium issue 154733)
+
+ Enabled --verify-heap in release mode (issue 2120)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-10-11: Version 3.14.3
+
+ Use native context to retrieve ErrorMessageForCodeGenerationFromStrings
+ (Chromium issue 155076).
+
+ Bumped variable limit further to 2^17 (Chromium issue 151625).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-10-10: Version 3.14.2
+
+ ARM: allowed VFP3 instructions when hardfloat is enabled.
+ (Chromium issue 152506)
+
+ Fixed instance_descriptors() and PushStackTraceAndDie regressions.
+ (Chromium issue 151749)
+
+ Made GDBJIT interface compile again. (issue 1804)
+
+ Fixed Accessors::FunctionGetPrototype's proto chain traversal.
+ (Chromium issue 143967)
+
+ Made sure that names of temporaries do not clash with real variables.
+ (issue 2322)
+
+ Rejected local module declarations. (Chromium issue 150628)
+
+ Rejected uses of lexical for-loop variable on the RHS. (issue 2322)
+
+ Fixed slot recording of code target patches.
+ (Chromium issue 152615,chromium:144230)
+
+ Changed the Android makefile to use GCC 4.6 instead of GCC 4.4.3.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-10-01: Version 3.14.1
+
+ Don't set -m32 flag when compiling with Android ARM compiler.
+ (Chromium issue 143889)
+
+ Restore the descriptor array before returning allocation failure.
+ (Chromium issue 151750)
+
+ Lowered kMaxVirtualRegisters (v8 issue 2139, Chromium issues 123822 and
+ 128252).
+
+ Pull more recent gyp in 'make dependencies'.
+
+ Made sure that the generic KeyedStoreIC changes length and element_kind
+ atomically (issue 2346).
+
+ Bumped number of allowed variables per scope to 65535, to address GWT.
+ (Chromium issue 151625)
+
+ Support sourceURL for dynamically inserted scripts (issue 2342).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-09-20: Version 3.14.0
+
+ Fixed missing slot recording during clearing of CallICs.
+ (Chromium issue 144230)
+
+ Fixed LBoundsCheck on x64 to handle (stack slot + constant) correctly.
+ (Chromium issue 150729)
+
+ Fixed minus zero test. (Issue 2133)
+
+ Fixed setting array length to zero for slow elements.
+ (Chromium issue 146910)
+
+ Fixed lost arguments dropping in HLeaveInlined.
+ (Chromium issue 150545)
+
+ Fixed casting error for receiver of interceptors.
+ (Chromium issue 149912)
+
+ Throw a more descriptive exception when blocking 'eval' via CSP.
+ (Chromium issue 140191)
+
+ Fixed debugger's eval when close to stack overflow. (issue 2318)
+
+ Added checks to live edit. (issue 2297)
+
+ Switched on code compaction on incremental GCs.
+
+ Fixed caching of optimized code for OSR. (issue 2326)
+
+ Not mask exception thrown by toString in String::UtfValue etc.
+ (issue 2317)
+
+ Fixed API check for length of external arrays. (Chromium issue 148896)
+
+ Ensure correct enumeration indices in the dict (Chromium issue 148376)
+
+ Correctly initialize regexp global cache. (Chromium issue 148378)
+
+ Fixed arguments object materialization during deopt. (issue 2261)
+
+ Introduced new API to expose external string resource regardless of
+ encoding.
+
+ Fixed CHECK failure in LCodeGen::DoWrapReceiver when
+ --deopt-every-n-times flag is present
+ (Chromium issue 148389)
+
+ Fixed edge case of extension with NULL as source string.
+ (Chromium issue 144649)
+
+ Fixed array index dehoisting. (Chromium issue 141395)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-09-11: Version 3.13.7
+
+ Enable/disable LiveEdit using the (C++) debug API.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-09-06: Version 3.13.6
+
+ Added validity checking to API functions and calls.
+
+ Disabled accessor inlining (Chromium issue 134609).
+
+ Fixed bug in Math.min/max in optimized code (Chromium issue 145961).
+
+ Directly use %ObjectKeys in json stringify (Chromium issue 2312).
+
+ Fixed VS2005 build (issue 2313).
+
+ Activated fixed ES5 readonly semantics by default.
+
+ Added hardfp flag to the Makefile.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-08-29: Version 3.13.5
+
+ Release stack trace data after firing Error.stack accessor.
+ (issue 2308)
+
+ Added a new API V8::SetJitCodeEventHandler to push code name and
+ location to users such as profilers.
+
+ Allocate block-scoped global bindings to global context.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-08-28: Version 3.13.4
+
+ Print reason for disabling optimization. Kill --trace-bailout flag.
+
+ Provided option to disable full DEBUG build on Android.
+
+ Introduced global contexts to represent lexical global scope(s).
+
+ Fixed rounding in Uint8ClampedArray setter. (issue 2294)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-08-21: Version 3.13.3
+
+ Performance and stability improvements on all platforms.
+
+
+2012-08-20: Version 3.13.2
+
+ Performance and stability improvements on all platforms.
+
+
+2012-08-16: Version 3.13.1
+
+ Performance and stability improvements on all platforms.
+
+
+2012-08-10: Version 3.13.0
+
+ Added histograms for total allocated/live heap size, as well as
+ allocated size and percentage of total for map and cell space.
+
+ Fixed parseInt's octal parsing behavior (ECMA-262 Annex E 15.1.2.2).
+ (issue 1645)
+
+ Added checks for interceptors to negative lookup code in Crankshaft.
+ (Chromium issue 140473)
+
+ Made incremental marking clear ICs and type feedback cells.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-08-01: Version 3.12.19
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-30: Version 3.12.18
+
+ Forced using bit-pattern for signed zero double. (issue 2239)
+
+ Made sure double to int conversion is correct. (issue 2260)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-27: Version 3.12.17
+
+ Always set the callee's context when calling a function from optimized
+ code.
+ (Chromium issue 138887)
+
+ Fixed building with GCC 3.x
+ (issue 2016, 2017)
+
+ Improved API calls that return empty handles.
+ (issue 2245)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-25: Version 3.12.16
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-24: Version 3.12.15
+
+ Added PRESERVE_ASCII_NULL option to String::WriteAscii.
+ (issue 2252)
+
+ Added dependency to HLoadKeyed* instructions to prevent invalid
+ hoisting. (Chromium issue 137768)
+
+ Enabled building d8 for Android on Mac.
+
+ Interpret negative hexadecimal literals as NaN.
+ (issue 2240)
+
+ Expose counters in javascript when using --track-gc-object-stats.
+
+ Enabled building and testing V8 on Android IA.
+
+ Added --trace-parse flag to parser.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-18: Version 3.12.14
+
+ Deactivated optimization of packed arrays.
+ (Chromium issue 137768)
+
+ Fixed broken accessor transition.
+ (Chromium issue 137689)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-17: Version 3.12.13
+
+ Fixed missing tagging of stack value in finally block.
+ (Chromium issue 137496)
+
+ Added more support for heap analysis.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-16: Version 3.12.12
+
+ Added an option to the tickprocessor to specify the directory for lib
+ lookup.
+
+ Fixed ICs for slow objects with native accessor (Chromium issue 137002).
+
+ Fixed transcendental cache on ARM in optimized code (issue 2234).
+
+ New heap inspection tools: counters for object sizes and counts,
+ histograms for external fragmentation.
+
+ Incorporated constness into inferred interfaces (in preparation for
+ handling imports) (issue 1569).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-12: Version 3.12.11
+
+ Renamed "mips" arch to "mipsel" in the GYP build.
+
+ Fixed computation of call targets on prototypes in Crankshaft.
+ (Chromium issue 125148)
+
+ Removed use of __lookupGetter__ when generating stack trace.
+ (issue 1591)
+
+ Turned on ES 5.2 globals semantics by default.
+ (issue 1991, Chromium issue 80591)
+
+ Synced preparser and parser wrt syntax error in switch..case.
+ (issue 2210)
+
+ Fixed reporting of octal literals in strict mode when preparsing.
+ (issue 2220)
+
+ Fixed inline constructors for Harmony Proxy prototypes.
+ (issue 2225)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-10: Version 3.12.10
+
+ Re-enabled and fixed issue with array bounds check elimination
+ (Chromium issue 132114).
+
+ Fixed Debug::Break crash. (Chromium issue 131642)
+
+ Added optimizing compiler support for JavaScript getters.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-06: Version 3.12.9
+
+ Correctly advance the scanner when scanning unicode regexp flag.
+ (Chromium issue 136084)
+
+ Fixed unhandlified code calling Harmony Proxy traps.
+ (issue 2219)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-05: Version 3.12.8
+
+ Implemented TypedArray.set and ArrayBuffer.slice in d8.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-07-03: Version 3.12.7
+
+ Fixed lazy compilation for strict eval scopes.
+ (Chromium issue 135066)
+
+ Made MACOSX_DEPLOYMENT_TARGET configurable in GYP.
+ (issue 2151)
+
+ Report "hidden properties" in heap profiler for properties case.
+ (issue 2212)
+
+ Activated optimization of packed arrays by default.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-06-29: Version 3.12.6
+
+ Cleaned up hardfp ABI detection for ARM (V8 issue 2140).
+
+ Extended TypedArray support in d8.
+
+
+2012-06-28: Version 3.12.5
+
+ Fixed lazy parsing heuristics to respect outer scope.
+ (Chromium issue 135008)
+
+ Allow using test-wrapper-gypbuild.py on Windows when no python
+ interpreter is registered.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-06-27: Version 3.12.4
+
+ Removed -fomit-frame-pointer flag from Release builds to make
+ the stack walkable by TCMalloc (Chromium issue 133723).
+
+ Ported r7868 (constant masking) to x64 (issue 1374).
+
+ Expose more detailed memory statistics (issue 2201).
+
+ Fixed Harmony Maps and WeakMaps for undefined values
+ (Chromium issue 132744).
+
+ Correctly throw reference error in strict mode with ICs disabled
+ (issue 2119).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-06-25: Version 3.12.3
+
+ Reverted r11835 'Unify promotion and allocation limit computation' due
+ to V8 Splay performance regression on Mac. (Chromium issue 134183)
+
+ Fixed sharing of literal boilerplates for optimized code. (issue 2193)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-06-22: Version 3.12.2
+
+ Made near-jump check more strict in LoadNamedFieldPolymorphic on
+ ia32/x64. (Chromium issue 134055)
+
+ Fixed lazy sweeping heuristics to prevent old-space expansion.
+ (issue 2194)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-06-21: Version 3.12.1
+
+ Performance and stability improvements on all platforms.
+
+
+2012-06-20: Version 3.12.0
+
+ Fixed Chromium issues:
+ 115100, 129628, 131994, 132727, 132741, 132742, 133211
+
+ Fixed V8 issues:
+ 915, 1914, 2034, 2087, 2094, 2134, 2156, 2166, 2172, 2177, 2179, 2185
+
+ Added --extra-code flag to mksnapshot to load JS code into the VM
+ before creating the snapshot.
+
+ Support 'restart call frame' command in the debugger.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-06-13: Version 3.11.10
+
+ Implemented heap profiler memory usage reporting.
+
+ Preserved error message during finally block in try..finally.
+ (Chromium issue 129171)
+
+ Fixed EnsureCanContainElements to properly handle double values.
+ (issue 2170)
+
+ Improved heuristics to keep objects in fast mode with inherited
+ constructors.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-06-06: Version 3.11.9
+
+ Implemented ES5-conformant semantics for inherited setters and read-only
+ properties. Currently behind --es5_readonly flag, because it breaks
+ WebKit bindings.
+
+ Exposed last seen heap object id via v8 public api.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-31: Version 3.11.8
+
+ Avoid overdeep recursion in regexp where a guarded expression with a
+ minimum repetition count is inside another quantifier.
+ (Chromium issue 129926)
+
+ Fixed missing write barrier in store field stub.
+ (issues 2143, 1465, Chromium issue 129355)
+
+ Proxies: Fixed receiver for setters inherited from proxies.
+ Proxies: Fixed ToStringArray function so that it does not reject some
+ keys.
+ (issue 1543)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-29: Version 3.11.7
+
+ Get better function names in stack traces.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-24: Version 3.11.6
+
+ Fixed RegExp.prototype.toString for incompatible receivers
+ (issue 1981).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-23: Version 3.11.5
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-22: Version 3.11.4
+
+ Some cleanup to common.gypi. This fixes some host/target combinations
+ that weren't working in the Make build on Mac.
+
+ Handle EINTR in socket functions and continue incomplete sends.
+ (issue 2098)
+
+ Fixed python deprecations. (issue 1391)
+
+ Made socket send and receive more robust and return 0 on failure.
+ (Chromium issue 15719)
+
+ Fixed GCC 4.7 (C++11) compilation. (issue 2136)
+
+ Set '-m32' option for host and target platforms
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-18: Version 3.11.3
+
+ Disable optimization for functions that have scopes that cannot be
+ reconstructed from the context chain. (issue 2071)
+
+ Define V8_EXPORT to nothing for clients of v8. (Chromium issue 90078)
+
+ Correctly check for native error objects. (Chromium issue 2138)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-16: Version 3.11.2
+
+ Revert r11496. (Chromium issue 128146)
+
+ Implement map collection for incremental marking. (issue 1465)
+
+ Add toString method to CallSite (which describes a frame of the
+ stack trace).
+
+
+2012-05-15: Version 3.11.1
+
+ Added a readbuffer function to d8 that reads a file into an ArrayBuffer.
+
+ Fix freebsd build. (V8 issue 2126)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-11: Version 3.11.0
+
+ Fixed compose-discard crasher from r11524 (issue 2123).
+
+ Activated new global semantics by default. Global variables can
+ now shadow properties of the global object (ES5.1 erratum).
+
+ Properly set ElementsKind of empty FAST_DOUBLE_ELEMENTS arrays when
+ transitioning (Chromium issue 117409).
+
+ Made Error.prototype.name writable again, as required by the spec and
+ the web (Chromium issue 69187).
+
+ Implemented map collection with incremental marking (issue 1465).
+
+ Regexp: Fixed overflow in min-match-length calculation
+ (Chromium issue 126412).
+
+ MIPS: Fixed illegal instruction use on Loongson in code for
+ Math.random() (issue 2115).
+
+ Fixed crash bug in VisitChoice (Chromium issue 126272).
+
+ Fixed unsigned-Smi check in MappedArgumentsLookup
+ (Chromium issue 126414).
+
+ Fixed LiveEdit for function with no locals (issue 825).
+
+ Fixed register clobbering in LoadIC for interceptors
+ (Chromium issue 125988).
+
+ Implemented clearing of CompareICs (issue 2102).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-05-03: Version 3.10.8
+
+ Enabled MIPS cross-compilation.
+
+ Ensured reload of elements pointer in StoreFastDoubleElement stub.
+ (Chromium issue 125515)
+
+ Fixed corner cases in truncation behavior when storing to
+ TypedArrays. (issue 2110)
+
+ Fixed failure to properly recognize and report out-of-memory
+ conditions when allocating code space pages. (Chromium issue
+ 118625)
+
+ Fixed idle notifications to perform a round of incremental GCs
+ after context disposal. (issue 2107)
+
+ Fixed preparser for try statement. (issue 2109)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-04-30: Version 3.10.7
+
+ Performance and stability improvements on all platforms.
+
+
+2012-04-26: Version 3.10.6
+
+ Fixed some bugs in accessing details of the last regexp match.
+
+ Fixed source property of empty RegExp objects. (issue 1982)
+
+ Enabled inlining some V8 API functions.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-04-23: Version 3.10.5
+
+ Put new global var semantics behind a flag until WebKit tests are
+ cleaned up.
+
+ Enabled stepping into callback passed to builtins.
+ (Chromium issue 109564)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-04-19: Version 3.10.4
+
+ Fixed issues when stressing compaction with WeakMaps.
+
+ Fixed missing GVN flag for new-space promotion. (Chromium issue 123919)
+
+ Simplify invocation sequence at monomorphic function invocation sites.
+ (issue 2079)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-04-17: Version 3.10.3
+
+ Fixed several bugs in heap profiles (including issue 2078).
+
+ Throw syntax errors on illegal escape sequences.
+
+ Implemented rudimentary module linking (behind --harmony flag)
+
+ Implemented ES5 erratum: Global declarations should shadow
+ inherited properties.
+
+ Made handling of const more consistent when combined with 'eval'
+ and 'with'.
+
+ Fixed V8 on MinGW-x64 (issue 2026).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-04-13: Version 3.10.2
+
+ Fixed native ARM build (issues 1744, 539)
+
+ Return LOOKUP variable instead of CONTEXT for non-context allocated
+ outer scope parameters (Chromium issue 119609).
+
+ Fixed regular and ElementsKind transitions interfering with each other
+ (Chromium issue 122271).
+
+ Improved performance of keyed loads/stores which have a HeapNumber
+ index (issues 1388, 1295).
+
+ Fixed WeakMap processing for evacuation candidates (issue 2060).
+
+ Bailout on possible direct eval calls (Chromium issue 122681).
+
+ Do not assume that names of function expressions are context-allocated
+ (issue 2051).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-04-10: Version 3.10.1
+
+ Fixed bug with arguments object in inlined functions (issue 2045).
+
+ Fixed performance bug with lazy initialization (Chromium issue
+ 118686).
+
+ Added suppport for Mac OS X 64bit builds with GYP.
+ (Patch contributed by Filipe David Manana <fdmanana@gmail.com>)
+
+ Fixed bug with hidden properties (issue 2034).
+
+ Fixed a performance bug when reloading pages (Chromium issue 117767,
+ V8 issue 1902).
+
+ Fixed bug when optimizing throw in top-level code (issue 2054).
+
+ Fixed two bugs with array literals (issue 2055, Chromium issue 121407).
+
+ Fixed bug with Math.min/Math.max with NaN inputs (issue 2056).
+
+ Fixed a bug with the new runtime profiler (Chromium issue 121147).
+
+ Fixed compilation of V8 using uClibc.
+
+ Optimized boot-up memory use.
+
+ Optimized regular expressions.
+
+
+2012-03-30: Version 3.10.0
+
+ Fixed store IC writability check in strict mode
+ (Chromium issue 120099).
+
+ Resynchronize timers if the Windows system time was changed.
+ (Chromium issue 119815)
+
+ Removed "-mfloat-abi=hard" from host compiler cflags when building for
+ hardfp ARM
+ (https://code.google.com/p/chrome-os-partner/issues/detail?id=8539)
+
+ Fixed edge case for case independent regexp character classes
+ (issue 2032).
+
+ Reset function info counters after context disposal.
+ (Chromium issue 117767, V8 issue 1902)
+
+ Fixed missing write barrier in CopyObjectToObjectElements.
+ (Chromium issue 119926)
+
+ Fixed missing bounds check in HasElementImpl.
+ (Chromium issue 119925)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-23: Version 3.9.24
+
+ Activated count-based profiler for ARM.
+
+ Fixed use of proxies as f.prototype properties. (issue 2021)
+
+ Enabled snapshots on MIPS.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-21: Version 3.9.23
+
+ Use correct arguments adaptation environment when inlining function
+ containing arguments. (Issue 2014)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-20: Version 3.9.22
+
+ Enabled count-based profiler by default.
+
+ Implemented a hash based look-up to speed up address checks
+ in large object space (issue 853).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-19: Version 3.9.21
+
+ Fixed push-to-trunk script (and re-push).
+
+ Added API call that identifies strings that are guaranteed only to
+ contain ASCII characters.
+
+
+2012-03-19: Version 3.9.20
+
+ Fixed declarations escaping global strict eval. (Issue 1624)
+
+ Fixed wrapping of receiver for non-strict callbacks. (Issue 1973)
+
+ Fixed function declarations overwriting read-only global properties.
+ (Chromium issue 115452)
+
+ Fixed --use-strict flag in combination with --harmony[-scoping].
+
+ Debugger: naive implementation of "step into Function.prototype.bind".
+
+ Debugger: added ability to set script source from within OnBeforeCompile
+
+ Added flag to always call DebugBreak on abort.
+
+ Re-enabled constructor inlining and inline === comparison with boolean
+ constants. (Issue 2009)
+
+ Don't use an explicit s0 in ClampDoubleToUint8. (Issue 2004)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-14: Version 3.9.19
+
+ Ensure there is a smi check of the receiver for global load and call
+ ICs (Chromium issue 117794).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-13: Version 3.9.18
+
+ Ensure consistency of Math.sqrt on Intel platforms.
+
+ Remove static initializers in v8. (issue 1859)
+
+ Add explicit dependency on v8_base in the GYP-based build.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-12: Version 3.9.17
+
+ Fixed VFP detection through compiler defines. (issue 1996)
+
+ Add Code-related fields to postmortem metadata.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-09: Version 3.9.16
+
+ Added basic interface inference for modules (behind the --harmony flag).
+
+ Added Object.is, Number.isFinite, Number.isNaN.
+
+ Updated the Unicode tables to Unicode version 6.1.0.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-06: Version 3.9.15
+
+ Fix the heap profiler crash caused by memory layout changes between
+ passes.
+
+ Fix Error.prototype.toString to throw TypeError. (issue 1980)
+
+ Fix double-rounding in strtod for MinGW. (issue 1062)
+
+ Fix corrupted snapshot serializaton on ia32. (Chromium issue v8/1985)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-03-01: Version 3.9.14
+
+ Performance and stability improvements on all platforms.
+
+
+2012-02-29: Version 3.9.13
+
+ Added code kind check before preparing for OSR. (issue 1900, 115073)
+
+ Fixed issue 1802: Pass zone explicitly to zone-allocation on x64 and
+ ARM.
+
+ Ported string construct stub to x64. (issue 849)
+
+ Performance and stability improvements on all platforms.
+
+
+2012-02-28: Version 3.9.12
+
+ Fixed the negative lookup stub to handle deleted entries in a
+ dictionary. (issue 1964)
+
+ Added a new API where the host can supply a callback function. The
+ callback function can resolve the location of a return address on stack
+ to the location where a return-address rewriting profiler stashed the
+ original return address.
+
+ Fixed Chromium issue http://crbug.com/115646: When compiling for-in
+ pass correct context value to the increment instruction.
+
+ Fixed issue 1853: Update breakpoints set with partial file name after
+ compile.
+
+
+2012-02-27: Version 3.9.11
+
+ Made 'module' a context-sensitive keyword (V8 issue 1957).
+
+
+2012-02-24: Version 3.9.10
+
+ Fixed V8 issues 1322, 1772 and 1969.
+
+ Conformance improvements.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-02-23: Version 3.9.9
+
+ Supported fast case for-in in Crankshaft.
+
+ Sped up heap snapshot serialization and dominators construction.
+
+ Randomized allocation addresses on windows. (Chromium issue 115151)
+
+ Fixed compilation with MinGW-w64. (issue 1943)
+
+ Fixed incorrect value of assignments to non-extensible properties.
+
+ Fixed a crash bug in generated code on ia32.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-02-21: Version 3.9.8
+
+ Fixed memory leak and missing #include in StartupDataDecompressor
+ (issue 1960).
+
+ Renamed static methods to avoid shadowing virtual methods and fix Clang
+ C++11 compile error.
+
+ Fixed sequence of element access in array builtins (issue 1790).
+
+ Performance and stability improvements on all platforms.
+
+
+2012-02-16: Version 3.9.7
+
+ Fixed V8 issues 1322, 1878, 1942, 1945 and Chromium issue 113924.
+
+ Fixed GCC-4.7 warnings.
+
+ Added Navier-Stokes benchmark.
+
+ Performance and stability improvements on all platforms.
+
+
+2012-02-14: Version 3.9.6
+
+ Fixed template-related linker error. (issue 1936)
+
+ Allowed inlining of functions containing object literals. (issue 1322)
+
+ Added --call-graph-size option to tickprocessor. (issue 1937)
+
+ Heap Snapshot maximum size limit is too low for really big apps. At the
+ moment the limit is 256MB. (Chromium issue 113015)
+
+ Performance and stability improvements on all platforms.
+
+
+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
+
+ Added 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
+
+ Reduced 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.
+
+ Removed the need for code delete events in CPU profiler (Issue 1466).
+
+ Fixed debugger stepping next with trycatch recursion (Issue 1639).
+
+ Fixing parallel execution in d8 (with -p) and some memory leaks.
+
+ Support for precise stepping in functions compiled before debugging was
+ started (step 1).
+
+
+2011-09-13: Version 3.6.3
+
+ Implemented better support of typed arrays in the d8 shell.
+
+ Bug fixes and performance improvements on all platforms.
+
+
+2011-09-08: Version 3.6.2
+
+ Added "dependencies" target to top-level Makefile.
+
+ Added ability to turn profiler on/off in d8.
+
+ Added "soname_version" parameter to common.gypi, v8.gyp, and Makefile.
+
+ Fixed several crash bugs.
+
+
+2011-09-07: Version 3.6.1
+
+ Fixed a bug in abrupt exit from with or catch inside finally.
+
+ Fixed possible crash in FixedDoubleArray::Initialize() (Chromium
+ issue 95113).
+
+ Fixed a bug in Page::GetRegionMaskForSpan (Chromium issue 94425).
+
+ Fixed a few clang warnings (which -Werror treated as errors).
+
+ Performance improvements on all platforms.
+
+
+2011-09-05: Version 3.6.0
+
+ Fixed a bug when optimizing named function expression (issue 1647).
+
+ Fixed a bug when optimizing f.call.apply (issue 1650).
+
+ Made arguments and caller always be null on native functions
+ (issues 1548 and 1643).
+
+ Fixed issue 1648 (cross-compiling x64 targeting ia32).
+
+ Fixed issue 371 (d8 printing of strings containing \0).
+
+ Fixed order of evaluation in arguments to parseInt (issue 1649).
+
+ Fixed a problem with large heap snapshots in Chrome DevTools
+ (issue 1658, chromium issue 89268).
+
+ Upped default maximum heap size from 512M to 700M.
+
+
+2011-08-31: Version 3.5.10
+
+ Added dependency of v8_base on WinSocket2 Windows library in
+ the GYP-build.
+
+ Various bugfixes.
+
+
+2011-08-29: Version 3.5.9
+
+ Made FromPropertyDescriptor not trigger inherited setters.
+
+ Fixed .gyp files to work on the ARM simulator.
+
+ Fixed shared library build warnings for MSVS.
+
+
+2011-08-24: Version 3.5.8
+
+ Added V8EXPORT attributes for v8::Array::CheckCast and
+ v8::Number::CheckCast.
+
+ Made a slight API change enabling opting out from null termination
+ in String::Write*().
+
+ Fixed arm build for gcc-4.6.
+
+
+2011-08-22: Version 3.5.7
+
+ Make scanner handle invalid unicode escapes in identifiers correctly.
+
+ Make regexp flag parsing stricter.
+
+ Fix several memory leaks.
+
+
+2011-08-17: Version 3.5.6
+
+ Fixed issue that could cause crashes when running with --heap-stats.
+
+ Fixed compilation on Linux 2.6.9 and older.
+
+ Fixed live-object-list to work with isolates.
+
+ Fixed memory leaks in zones and isolates.
+
+ Fixed a performance regression for TypedArrays on x64.
+
+ Stability improvements on all platforms.
+
+
+2011-08-15: Version 3.5.5
+
+ Fixed bugs involving negative zero and the optimizing compiler.
+
+ Fixed optimized version of Function.apply(x, arguments). (issue 1592)
+
+ Eliminated uses of deprecated ARM instructions.
+
+ Sped up Math.floor by using SSE 4.1 roundsd instruction on ia32.
+
+ Removed restriction on the size of disassembled code that is printed.
+
+
+2011-08-10: Version 3.5.4
+
+ Added a preliminary implementation of ES Harmony weak maps. Weak
+ maps can be enabled by the flag --harmony-weakmaps.
+
+ Introduced a toplevel Makefile to support GYP-based building. GYP
+ can be obtained from http://gyp.googlecode.com.
+
+ Fixed a bug in the length property of functions created by
+ Function.prototype.bind.
+
+ Reduced malloc heap allocation on process startup.
+
+ Several important code generation bug fixes.
+
+ Performance improvements on all platforms.
+
+
+2011-08-03: Version 3.5.3
+
+ MIPS: Port of fix to ClassOf check from ARM.
+ Patch from Paul Lind <plind44@gmail.com>.
+
+ Stopped using mprotect on Cygwin.
+ Avoided uninitialized member warning on gcc 4.3.4
+ Both patches by Bert Belder.
+
+ Bug fixes and performance improvements on all platforms.
+
+
+2011-08-01: Version 3.5.2
+
+ Performance improvements on all platforms.
+
+
+2011-07-28: Version 3.5.1
+
+ Fixed setting the readonly flag on the prototype property using the
+ API call FunctionTemplate::SetPrototypeAttributes (issue 1539).
+
+ Changed the tools/test.py script to use d8 instead of shell for
+ testing.
+
+ Fixed crash in ToBooleanStub when GC happens during invocation.
+
+ Enabled automatic unboxing of double arrays.
+
+ Performance improvements on all platforms.
+
+
+2011-07-25: Version 3.5.0
+
+ Implemented Object.prototype.{hasOwnProperty, propertyIsEnumerable} for
+ proxies.
+
+ Removed logging to memory support.
+
+ Bugfixes and performance work.
+
+
+2011-07-20: Version 3.4.14
+
+ Fix the debugger for strict-mode functions. (Chromium issue 89236)
+
+ 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)
+
+
+2011-07-18: Version 3.4.13
+
+ Improved debugger support to allow inspection of optimized frames (issue
+ 1140).
+
+ Fixed a bug in prototype transitions cache clearing introduced by r8165.
+
+ Fixed shortcutting bug in HInferRepresentation. Patch by Andy Wingo.
+
+ Fixed a memory leak in sample/shell.cc (dispose semaphores).
+
+ Simplified HClampToUint8. Patch by Andy Wingo.
+
+ Exposed APIs for detecting boxed primitives, native errors. Patch by
+ Luke Zarko.
+
+ Added map check for COW elements to crankshaft array handling code
+ (issue 1560).
+
+ Sample shell and (a light version of) D8 links against a shared library
+ now.
+
+ Fixed bug in array filter and reduce functions (issue 1559).
+
+ Avoid TLS load in AstNode constructor.
+
+ Introduced a random entropy source which can optionally be provided at
+ initialization. (Chromium issue 89462).
+
+
+2011-07-13: Version 3.4.12
+
+ Added --prof profiling option to d8 shell.
+
+ Fixed a bug where reading a directory in d8 shell hangs (issue 1533).
+
+ Fixed a potential assertion failure in const declarations.
+
+ Fixed an assertion failure in descriptor arrays (issue 1526).
+
+ Enabled fast thread-local storage by default on supported platforms.
+
+ Improved reporting of source position for global variable loads
+ (issue 1527).
+
+
+2011-07-11: Version 3.4.11
+
+ Fixed MinGW32 build.
+
+ Fixed a GC bug with RegExp code flushing.
+
+ Implemented Object.defineProperty for proxies.
+
+ Fixed a bug in for/in iteration of arguments objects (issue 1531).
+
+ Added debugger support for inspecting optimized frames (issue 1140).
+
+ Allowed JSObject::PreventExtensions to work for arguments objects.
+
+ Bugfixes and performance work.
+
+
+2011-07-06: Version 3.4.10
+
+ Fixed debugger not breaking on certain "if" statements (issue 1523).
+
+ Fixed assertion failure in runtime profiler when running on IA32
+ without snapshot (issue 1522).
+
+ Fixed ABI for API calls on IA32 (for clang compatibility).
+
+ Introduced code flushing of RegExp code to free memory used by
+ RegExps sooner.
+
+ Fixed linux-tick-processor built wrong version of v8 (issue 1532).
+
+ Fixed assertion failure in v8::TryCache::StackTrace (issue 1529).
+
+ Performance improvements on all platforms.
+
+
+2011-07-04: Version 3.4.9
+
+ Added support for debugger inspection of locals in optimized frames
+ (issue 1140).
+
+ Fixed SConstruct to pass correct defines to samples/preparser when
+ building with library=shared.
+
+ Made date parser handle ES5 Date Time Strings correctly (issue 1498).
+
+ Fixed a bug in Object.defineProperty on the arguments object.
+
+ Performance improvements on all platforms.
+
+
+2011-06-29: Version 3.4.8
+
+ Ensure 16-byte stack alignment on Solaris (issue 1505).
+
+ Fix "illegal access" when calling parseInt with a radix
+ that is not a smi. (issue 1246).
+
+
+2011-06-27: Version 3.4.7
+
+ Fixed 64-bit build on FreeBSD.
+
+ Added API to set the property attributes for the prototype
+ property on functions created from FunctionTemplates.
+
+ Bugfixes and performance work.
+
+
+2011-06-22: Version 3.4.6
+
+ Lowered limit on code space for systems with low memory supply.
+
+ Allowed compiling v8_shell with the 'host' toolset (issue 82437).
+
+ Extended setBreakpoint API to accept partial script name (issue 1418).
+
+ Made multi-line comments not count when deciding whether the '-->'
+ comment starter is first on a line. This matches Safari.
+
+ Made handling of non-array recievers in Array length setter correct
+ (issue 1491).
+
+ Added ability to heap profiler to iterate over snapshot's node
+ (issue 1481).
+
+
+2011-06-20: Version 3.4.5
+
+ Fixed issues 794, 1097, 1215(partial), 1417, 1435, 1472, 1473,
+ 1476, and 1477.
+
+ Improved code generation for !0 and !1.
+
+ Reduced memory usage for regular expressions with nested qualifiers.
+ (issue 1472)
+
+ Fixed V8 to count line terminators in multi-line comments.
+ (Chromium issue 86431)
+
+ Fixed disassembler=on option for release-mode builds. (issue 1473)
+
+ Performance improvements on all platforms.
+
+
+2011-06-15: Version 3.4.4
+
+ Added snapshot compression support and --stress-opt flag to d8.
+
+ Improved performance of try/catch.
+
+ Several GYP-related changes: Added support for building Xcode project
+ files. Make the ARM simulator build with GYP again. Generate Makefiles
+ for all architectures on Linux.
+
+ Fixed Array.prototype.{reduce,reduceRight} to pass undefined as the
+ receiver for strict mode callbacks. (issue 1436)
+
+ Fixed a bug where an array load was incorrectly hoisted by GVN.
+
+ Handle 'undefined' correctly when === has been specialized for doubles.
+ (issue 1434)
+
+ Corrected the limit of local variables in an optimized function from 64
+ to 63.
+
+ Correctly set ReadOnly flag on indexed properties when using the API Set
+ method. (issue 1470)
+
+ Give the correct error message when Object.isExtensible is called on a
+ non-object. (issue 1452)
+
+ Added GetOwnPropertyNames method for Object in the API. Patch by Peter
+ Varga.
+
+ Do not redefine properties unneccesarily in seal and freeze. (issue
+ 1447)
+
+ IsExecutionTerminating has an Isolate parameter now.
+
+ Distinguish keyed loads with a symbol key from fast elements loads,
+ avoiding some useless deoptimizations. (issue 1471)
+
+
+2011-06-08: Version 3.4.3
+
+ Clear the global thread table when an isolate is disposed
+ (issue 1433).
+
+ Converted time zone name to UTF8 on Windows (issue 1290).
+
+ Limited the number of arguments in a function call to 32766
+ (issue 1413).
+
+ Compress sources of JS libraries in addition to the snapshot.
+
+ Fixed a bug in Lithium environment iteration.
+
+ Performance improvements on all platforms.
+
+
+2011-06-06: Version 3.4.2
+
+ More work on ES-Harmony proxies. Still hidden behind a flag.
+
+ Fixed some crash bugs and improved performance.
+
+ Fixed building with gdb debugging support.
+
+ Do not install SIGPROF handler until it is needed.
+
+ Added DateTimeFormat to i18n API.
+
+ Fixed compilation on OpenBSD.
+
+ Take the ulimit into account when sizing the heap. OpenBSD users
+ may still have to increase the default ulimit to run heavy pages in
+ the browser.
+
+
+2011-06-01: Version 3.4.1
+
+ Fixed JSON stringify issue with arrays.
+
+ Changed calls to JS builtins to be passed undefined when called with
+ implicit receiver.
+
+ Implemented the set trap for Harmony proxies. Proxies still need to
+ be enabled with the --harmony-proxies flag.
+
+
+2011-05-30: Version 3.4.0
+
+ Changed calls to undefined property setters to not throw (issue 1355).
+
+ Made RegExp objects not callable.
+
+ Fixed issues on special case large JSON strings in new json parser
+ (issues http://crbug.com/83877 and http://crbug.com/84186).
+
+ Performance improvements on all platforms.
+
+
+2011-05-25: Version 3.3.10
+
+ Fixed calls of strict mode function with an implicit receiver.
+
+ Fixed fast handling of arrays to properly deal with changes to the
+ Object prototype (issue 1403).
+
+ Changed strict mode poison pill to be the same type error function
+ (issue 1387).
+
+ Fixed a debug crash in arguments object handling (issue 1227).
+
+ Fixed a bug in deoptimization on x64 (issue 1404).
+
+ Performance improvements and bug fixes on all platforms.
+
+
+2011-05-23: Version 3.3.9
+
+ Added DateTimeFormat class to experimental i18n API.
+
+ Extended preparser to give early errors for some strict mode
+ restrictions.
+
+ Removed legacy execScript function from V8.
+
+ Extended isolate API with the ability to add embedder-specific
+ data to an isolate.
+
+ Added basic support for polymorphic loads from JS and external
+ arrays.
+
+ Fixed bug in handling of switch statements in the optimizing
+ compiler.
+
+
+2011-05-18: Version 3.3.8
+
+ Added MarkIndependent to the persistent handle API. Independent
+ handles are independent of all other persistent handles and can be
+ garbage collected more frequently.
+
+ Implemented the get trap for Harmony proxies. Proxies are enabled
+ with the --harmony-proxies flag.
+
+ Performance improvements and bug fixes on all platforms.
+
+
+2011-05-16: Version 3.3.7
+
+ Updated MIPS infrastructure files.
+
+ Performance improvements and bug fixes on all platforms.
+
+
+2011-05-11: Version 3.3.6
+
+ Updated MIPS infrastructure files.
+
+ Added method IsCallable for Object to the API.
+ Patch by Peter Varga.
+
+
+2011-05-09: Version 3.3.5
+
+ Fixed build on FreeBSD. Patch by Akinori MUSHA.
+
+ Added check that receiver is JSObject on API calls.
+
+ Implemented CallAsConstructor method for Object in the API (Issue 1348).
+ Patch by Peter Varga.
+
+ Added CallAsFunction method to the Object class in the API (Issue 1336).
+ Patch by Peter Varga.
+
+ Added per-isolate locking and unlocking.
+
+ Fixed bug in x64 >>> operator (Issue 1359).
+
+
+2011-05-04: Version 3.3.4
+
+ Implemented API to disallow code generation from strings for a context
+ (issue 1258).
+
+ Fixed bug with whitespaces in parseInt (issue 955).
+
+ Fixed bug with == comparison of Date objects (issue 1356).
+
+ Added GYP variables for ARM code generation:
+ v8_can_use_vfp_instructions, v8_can_use_unaligned_accesses
+ and v8_use_arm_eabi_hardfloat.
+
+
+2011-05-02: Version 3.3.3
+
+ Added support for generating Visual Studio solution and project files
+ using GYP.
+
+ Implemented support for ARM EABI calling convention variation where
+ floating-point arguments are passed in registers (hardfloat).
+
+ Added Object::HasOwnProperty() to the API.
+
+ Added support for compressing startup data to reduce binary size. This
+ includes build time support and an API for the embedder to decompress
+ the startup data before initializing V8.
+
+ Reduced the profiling hooks overhead from >400% to 25% when using
+ ll_prof.
+
+ Performance improvements and bug fixes on all platforms.
+
+
+2011-04-27: Version 3.3.2
+
+ Fixed crash bug on ARM with no VFP3 hardware.
+
+ Fixed compilation of V8 without debugger support.
+
+ Improved performance on JSLint.
+
+ Added support Float64 WebGL arrays.
+
+ Fixed crash bug in regexp replace.
+
+
+2011-04-20: Version 3.3.1
+
+ Reduced V8 binary size by removing virtual functions from hydrogen.
+
+ Fixed crash bug on x64.
+
+ Performance improvements on ARM and IA32.
+
+
+2011-04-18: Version 3.3.0
+
+ Fixed bug in floating point rounding in Crankshaft on ARM
+ (issue 958)
+
+ Fixed a number of issues with running without VFPv3 support on ARM
+ (issue 1315)
+
+ Introduced v8Locale.Collator, a partial implementation of Collator
+ per last ECMAScript meeting + mailing list.
+
+ Minor performance improvements and bug fixes.
+
+
+2011-04-13: Version 3.2.10
+
+ Fixed bug in external float arrays on ARM (issue 1323).
+
+ Minor performance improvements and bug fixes.
+
+
+2011-04-11: Version 3.2.9
+
+ Removed support for ABI prior to EABI on ARM.
+
+ Fixed multiple crash bugs.
+
+ Added GCMole to the repository, a simple static analysis tool that
+ searches for GC-unsafe evaluation order dependent callsites.
+
+ Made preparser API be exported in shared libraries.
+
+ Fixed multiple issues in EcmaScript 5 strict mode implementation.
+
+ Fixed mutable __proto__ property if object is not extensible
+ (Issue 1309).
+
+ Fixed auto suspension of the sampler thread.
+
+
+2011-04-06: Version 3.2.8
+
+ Exposed WebGL typed array constructors in the shell sample.
+
+ Performance improvements on all platforms.
+
+
+2011-04-04: Version 3.2.7
+
+ Disabled the original 'classic' V8 code generator. Crankshaft is
+ now the default on all platforms.
+
+ Changed the heap profiler to use more descriptive names.
+
+ Performance and stability improvements to isolates on all platforms.
+
+
+2011-03-30: Version 3.2.6
+
+ Fixed xcode build warning in shell.cc (out of order initialization).
+
+ Fixed null-pointer dereference in the compiler when running without
+ SSE3 support (Chromium issue 77654).
+
+ Fixed x64 compilation error due to some dead code. (Issue 1286)
+
+ Introduced scons target to build the preparser stand-alone example.
+
+ Made FreeBSD build and pass all tests.
+
+
+2011-03-28: Version 3.2.5
+
+ Fixed build with Irregexp interpreter (issue 1266).
+
+ Added Crankshaft support for external arrays.
+
+ Fixed two potential crash bugs.
+
+
+2011-03-23: Version 3.2.4
+
+ Added isolates which allows several V8 instances in the same process.
+ This is controlled through the new Isolate class in the API.
+
+ Implemented more of EcmaScript 5 strict mode.
+
+ Reduced the time it takes to make detailed heap snapshot.
+
+ Added a number of commands to the ARM simulator and enhanced the ARM
+ disassembler.
+
+
+2011-03-17: Version 3.2.3
+
+ Fixed a number of crash bugs.
+
+ Fixed Array::New(length) to return an array with a length (issue 1256).
+
+ Fixed FreeBSD build.
+
+ Changed __defineGetter__ to not throw (matching the behavior of Safari).
+
+ Implemented more of EcmaScript 5 strict mode.
+
+ Improved Crankshaft performance on all platforms.
+
+
+2011-03-14: Version 3.2.2
+
+ Fixed a number of crash and correctness bugs.
+
+ Improved Crankshaft performance on all platforms.
+
+ Fixed Crankshaft on Solaris/Illumos.
+
+
+2011-03-10: Version 3.2.1
+
+ Fixed a number of crash bugs.
+
+ Improved Crankshaft for x64 and ARM.
+
+ Implemented more of EcmaScript 5 strict mode.
+
+
+2011-03-07: Version 3.2.0
+
+ Fixed a number of crash bugs.
+
+ Turned on Crankshaft by default on x64 and ARM.
+
+ Improved Crankshaft for x64 and ARM.
+
+ Implemented more of EcmaScript 5 strict mode.
+
+
+2011-03-02: Version 3.1.8
+
+ Fixed a number of crash bugs.
+
+ Improved Crankshaft for x64 and ARM.
+
+ Implemented more of EcmaScript 5 strict mode.
+
+ Fixed issue with unaligned reads and writes on ARM.
+
+ Improved heap profiler support.
+
+
+2011-02-28: Version 3.1.7
+
+ Fixed a number of crash bugs.
+
+ Improved Crankshaft for x64 and ARM.
+
+ Fixed implementation of indexOf/lastIndexOf for sparse
+ arrays (http://crbug.com/73940).
+
+ Fixed bug in map space compaction (http://crbug.com/59688).
+
+ Added support for direct getter accessors calls on ARM.
+
+
+2011-02-24: Version 3.1.6
+
+ Fixed a number of crash bugs.
+
+ Added support for Cygwin (issue 64).
+
+ Improved Crankshaft for x64 and ARM.
+
+ Added Crankshaft support for stores to pixel arrays.
+
+ Fixed issue in CPU profiler with Crankshaft.
+
+
+2011-02-16: Version 3.1.5
+
+ Change RegExp parsing to disallow /(*)/.
+
+ Added GDB JIT support for ARM.
+
+ Fixed several crash bugs.
+
+ Performance improvements on the IA32 platform.
+
+
+2011-02-14: Version 3.1.4
+
+ Fixed incorrect compare of prototypes of the global object (issue
+ 1082).
+
+ Fixed a bug in optimizing calls to global functions (issue 1106).
+
+ Made optimized Function.prototype.apply safe for non-JSObject first
+ arguments (issue 1128).
+
+ Fixed an error related to element accessors on Object.prototype and
+ parser errors (issue 1130).
+
+ Fixed a bug in sorting an array with large array indices (issue 1131).
+
+ Properly treat exceptions thrown while compiling (issue 1132).
+
+ Fixed bug in register requirements for function.apply (issue 1133).
+
+ Fixed a representation change bug in the Hydrogen graph construction
+ (issue 1134).
+
+ Fixed the semantics of delete on parameters (issue 1136).
+
+ Fixed a optimizer bug related to moving instructions with side effects
+ (issue 1138).
+
+ Added support for the global object in Object.keys (issue 1150).
+
+ Fixed incorrect value for Math.LOG10E
+ (issue http://code.google.com/p/chromium/issues/detail?id=72555)
+
+ Performance improvements on the IA32 platform.
+
+ Implement assignment to undefined reference in ES5 Strict Mode.
+
+
+2011-02-09: Version 3.1.3
+
+ Fixed a bug triggered by functions with huge numbers of declared
+ arguments.
+
+ Fixed zap value aliasing a real object - debug mode only (issue 866).
+
+ Fixed issue where Array.prototype.__proto__ had been set to null
+ (issue 1121).
+
+ Fixed stability bugs in Crankshaft for x86.
+
+
+2011-02-07: Version 3.1.2
+
+ Added better security checks when accessing properties via
+ Object.getOwnPropertyDescriptor.
+
+ Fixed bug in Object.defineProperty and related access bugs (issues
+ 992, 1083 and 1092).
+
+ Added LICENSE.v8, LICENSE.strongtalk and LICENSE.valgrind to ease
+ copyright notice generation for embedders.
+
+
+2011-02-02: Version 3.1.1
+
+ Perform security checks before fetching the value in
+ Object.getOwnPropertyDescriptor.
+
+ Fixed a bug in Array.prototype.splice triggered by passing no
+ arguments.
+
+ Fixed bugs in -0 in arithmetic and in Math.pow.
+
+ Fixed bugs in the register allocator and in switching from optimized
+ to unoptimized code.
+
+
+2011-01-31: Version 3.1.0
+
+ Performance improvements on all platforms.
+
+
+2011-01-28: Version 3.0.12
+
+ Added support for strict mode parameter and object property
+ validation.
+
+ Fixed a couple of crash bugs.
+
+
+2011-01-25: Version 3.0.11
+
+ Fixed a bug in deletion of lookup slots that could cause global
+ variables to be accidentally deleted (http://crbug.com/70066).
+
+ Added support for strict mode octal literal verification.
+
+ Fixed a couple of crash bugs (issues 1070 and 1071).
+
+
+2011-01-24: Version 3.0.10
+
+ Fixed External::Wrap for 64-bit addresses (issue 1037).
+
+ Fixed incorrect .arguments variable proxy handling in the full
+ code generator (issue 1060).
+
+ Introduced partial strict mode support.
+
+ Changed formatting of recursive error messages to match Firefox and
+ Safari (issue http://crbug.com/70334).
+
+ Fixed incorrect rounding for float-to-integer conversions for external
+ array types, which implement the Typed Array spec
+ (issue http://crbug.com/50972).
+
+ Performance improvements on the IA32 platform.
+
+
+2011-01-19: Version 3.0.9
+
+ Added basic GDB JIT Interface integration.
+
+ Make invalid break/continue statements a syntax error instead of a
+ runtime error.
+
+
+2011-01-17: Version 3.0.8
+
+ Exposed heap size limit to the heap statistics gathered by
+ the GetHeapStatistics API.
+
+ Wrapped external pointers more carefully (issue 1037).
+
+ Hardened the implementation of error objects to avoid setters
+ intercepting the properties set then throwing an error.
+
+ Avoided trashing the FPSCR when calculating Math.floor on ARM.
+
+ Performance improvements on the IA32 platform.
+
+
+2011-01-10: Version 3.0.7
+
+ Stopped calling inherited setters when creating object literals
+ (issue 1015).
+
+ Changed interpretation of malformed \c? escapes in RegExp to match
+ JSC.
+
+ Enhanced the command-line debugger interface and fixed some minor
+ bugs in the debugger.
+
+ Performance improvements on the IA32 platform.
+
+
+2011-01-05: Version 3.0.6
+
+ Allowed getters and setters on JSArray elements (issue 900).
+
+ Stopped JSON objects from hitting inherited setters (part of
+ issue 1015).
+
+ Allowed numbers and strings as names of getters/setters in object
+ initializer (issue 820).
+
+ Added use_system_v8 option to gyp (off by default), to make it easier
+ for Linux distributions to ship with system-provided V8 library.
+
+ Exported external array data accessors (issue 1016).
+
+ Added labelled thread names to help with debugging (on Linux).
+
+
+2011-01-03: Version 3.0.5
+
+ Fixed a couple of cast errors for gcc-3.4.3.
+
+ Performance improvements in GC and IA32 code generator.
+
+
+2010-12-21: Version 3.0.4
+
+ Added Date::ResetCache() to the API so that the cached values in the
+ Date object can be reset to allow live DST / timezone changes.
+
+ Extended existing support for printing (while debugging) the contents
+ of objects. Added support for printing objects from release builds.
+
+ Fixed V8 issues 989, 1006, and 1007.
+
+
+2010-12-17: Version 3.0.3
+
+ Reapplied all changes for version 3.0.1.
+
+ Improved debugger protocol for remote debugging.
+
+ Added experimental support for using gyp to generate build files
+ for V8.
+
+ Fixed implementation of String::Write in the API (issue 975).
+
+
+2010-12-15: Version 3.0.2
+
+ Revert version 3.0.1 and patch 3.0.1.1.
+
+
+2010-12-13: Version 3.0.1
+
+ Added support for an experimental internationalization API as an
+ extension. This extension is disabled by default but can be enabled
+ when building V8. The ECMAScript internationalization strawman is
+ at http://wiki.ecmascript.org/doku.php?id=strawman:i18n_api.
+
+ Made RegExp character class parsing stricter. This mirrors a change
+ to RegExp parsing in WebKit.
+
+ Fixed a bug in Object.defineProperty when used to change attributes
+ of an existing property. It incorrectly set the property value to
+ undefined (issue 965).
+
+ Fixed several different compilation failures on various platforms
+ caused by the 3.0.0 release.
+
+ Optimized Math.pow so it can work on unboxed doubles.
+
+ Sped up quoting of JSON strings by removing one traversal of the
+ string.
+
+
+2010-12-07: Version 3.0.0
+
+ Improved performance by (partially) addressing issue 957 on
+ IA-32. Still needs more work for the other architectures.
+
+
+2010-11-29: Version 2.5.9
+
+ Fixed crashes during GC caused by partially initialize heap
+ objects.
+
+ Fixed bug in process sample that caused memory leaks.
+
+ Improved performance on ARM by implementing missing stubs and
+ inlining.
+
+ Improved heap profiler support.
+
+ Added separate seeding on Windows of the random number generator
+ used internally by the compiler (issue 936).
+
+ Exposed API for getting the name of the function used to construct
+ an object.
+
+ Fixed date parser to handle one and two digit millisecond
+ values (issue 944).
+
+ Fixed number parsing to disallow space between sign and
+ digits (issue 946).
+
+
+2010-11-23: Version 2.5.8
+
+ Removed dependency on Gay's dtoa.
+
+ Improved heap profiler precision and speed.
+
+ Reduced overhead of callback invocations on ARM.
+
+
+2010-11-18: Version 2.5.7
+
+ Fixed obscure evaluation order bug (issue 931).
+
+ Split the random number state between JavaScript and the private API.
+
+ Fixed performance bug causing GCs when generating stack traces on
+ code from very large scripts.
+
+ Fixed bug in parser that allowed (foo):42 as a labelled statement
+ (issue 918).
+
+ Provide more accurate results about used heap size via
+ GetHeapStatistics.
+
+ Allow build-time customization of the max semispace size.
+
+ Made String.prototype.split honor limit when separator is empty
+ (issue 929).
+
+ Added missing failure check after expecting an identifier in
+ preparser (Chromium issue 62639).
+
+
+2010-11-10: Version 2.5.6
+
+ Added support for VFP rounding modes to the ARM simulator.
+
+ Fixed multiplication overflow bug (issue 927).
+
+ Added a limit for the amount of executable memory (issue 925).
+
+
+2010-11-08: Version 2.5.5
+
+ Added more aggressive GC of external objects in near out-of-memory
+ situations.
+
+ Fixed a bug that gave the incorrect result for String.split called
+ on the empty string (issue 924).
+
+
+2010-11-03: Version 2.5.4
+
+ Improved V8 VFPv3 runtime detection to address issue 914.
+
+
+2010-11-01: Version 2.5.3
+
+ Fixed a bug that prevents constants from overwriting function values
+ in object literals (issue 907).
+
+ Fixed a bug with reporting of impossible nested calls of DOM functions
+ (issue http://crbug.com/60753).
+
+
+2010-10-27: Version 2.5.2
+
+ Improved sampler resolution on Linux.
+
+ Allowed forcing the use of a simulator from the build script
+ independently of the host architecture.
+
+ Fixed FreeBSD port (issue 912).
+
+ Made windows-tick-processor respect D8_PATH.
+
+ Implemented --noinline-new flag fully on IA32, X64 and ARM platforms.
+
+
+2010-10-20: Version 2.5.1
+
+ Fixed bug causing spurious out of memory exceptions
+ (issue http://crbug.com/54580).
+
+ Fixed compilation error on Solaris platform (issue 901).
+
+ Fixed error in strtod (string to floating point number conversion)
+ due to glibc's use of 80-bit floats in the FPU on 32-bit linux.
+
+ Adjusted randomized allocations of executable memory to have 64k
+ granularity (issue http://crbug.com/56036).
+
+ Supported profiling using kernel perf_events on linux. Added ll_prof
+ script to tools and --ll-prof flag to V8.
+
+
+2010-10-18: Version 2.5.0
+
+ Fixed bug in cache handling of lastIndex on global regexps
+ (issue http://crbug.com/58740).
+
+ Added USE_SIMULATOR macro that explicitly indicates that we wish to use
+ the simulator as the execution engine (by Mark Lam <mark.lam@palm.com>
+ from Hewlett-Packard Development Company, LP).
+
+ Fixed compilation error on ARM with gcc 4.4 (issue 894).
+
+
+2010-10-13: Version 2.4.9
+
+ Fixed a bug in the handling of conditional expressions in test
+ contexts in compiler for top-level code.
+
+ Added "//@ sourceURL" information to the StackTrace API.
+
+ Exposed RegExp construction through the API.
+
+
+2010-10-04: Version 2.4.8
+
+ Fixed a bug in ResumeProfilerEx causing it to not always write out the
+ whole snapshot (issue 868).
+
+ Performance improvements on all platforms.
+
+
+2010-09-30: Version 2.4.7
+
+ Changed the command-line flag --max-new-space-size to be in kB and the
+ flag --max-old-space-size to be in MB (previously they were in bytes).
+
+ Added Debug::CancelDebugBreak to the debugger API.
+
+ Fixed a bug in getters for negative numeric property names
+ (https://bugs.webkit.org/show_bug.cgi?id=46689).
+
+ Performance improvements on all platforms.
+
+
+2010-09-27: Version 2.4.6
+
+ Fixed assertion failure related to copy-on-write arrays (issue 876).
+
+ Fixed build failure of 64-bit V8 on Windows.
+
+ Fixed a bug in RegExp (issue http://crbug.com/52801).
+
+ Improved the profiler's coverage to cover more functions (issue 858).
+
+ Fixed error in shift operators on 64-bit V8
+ (issue http://crbug.com/54521).
+
+
+2010-09-22: Version 2.4.5
+
+ Changed the RegExp benchmark to exercise the regexp engine on different
+ inputs by scrambling the input strings.
+
+ Fixed a bug in keyed loads on strings.
+
+ Fixed a bug with loading global function prototypes.
+
+ Fixed a bug with profiling RegExp calls (issue http://crbug.com/55999).
+
+ Performance improvements on all platforms.
+
+
+2010-09-15: Version 2.4.4
+
+ Fixed bug with hangs on very large sparse arrays.
+
+ Now tries harder to free up memory when running out of space.
+
+ Added heap snapshots to JSON format to API.
+
+ Recalibrated benchmarks.
+
+
+2010-09-13: Version 2.4.3
+
+ Made Date.parse properly handle TZ offsets (issue 857).
+
+ Performance improvements on all platforms.
+
+
+2010-09-08: Version 2.4.2
+
+ Fixed GC crash bug.
+
+ Fixed stack corruption bug.
+
+ Fixed compilation for newer C++ compilers that found Operand(0)
+ ambiguous.
+
+
+2010-09-06: Version 2.4.1
+
+ Added the ability for an embedding application to receive a callback
+ when V8 allocates (V8::AddMemoryAllocationCallback) or deallocates
+ (V8::RemoveMemoryAllocationCallback) from the OS.
+
+ Fixed several JSON bugs (including issue 855).
+
+ Fixed memory overrun crash bug triggered during V8's tick-based
+ profiling.
+
+ Performance improvements on all platforms.
+
+
+2010-09-01: Version 2.4.0
+
+ Fixed bug in Object.freeze and Object.seal when Array.prototype or
+ Object.prototype are changed (issue 842).
+
+ Updated Array.splice to follow Safari and Firefox when called
+ with zero arguments.
+
+ Fixed a missing live register when breaking at keyed loads on ARM.
+
+ Performance improvements on all platforms.
+
+
+2010-08-25: Version 2.3.11
+
+ Fixed bug in RegExp related to copy-on-write arrays.
+
+ Refactored tools/test.py script, including the introduction of
+ VARIANT_FLAGS that allows specification of sets of flags with which
+ all tests should be run.
+
+ Fixed a bug in the handling of debug breaks in CallIC.
+
+ Performance improvements on all platforms.
+
+
+2010-08-23: Version 2.3.10
+
+ Fixed bug in bitops on ARM.
+
+ Build fixes for unusual compilers.
+
+ Track high water mark for RWX memory.
+
+ Performance improvements on all platforms.
+
+
+2010-08-18: Version 2.3.9
+
+ Fixed compilation for ARMv4 on OpenBSD/FreeBSD.
+
+ Removed specialized handling of GCC 4.4 (issue 830).
+
+ Fixed DST cache to take into account the suspension of DST in
+ Egypt during the 2010 Ramadan (issue http://crbug.com/51855).
+
+ Performance improvements on all platforms.
+
+
+2010-08-16: Version 2.3.8
+
+ Fixed build with strict aliasing on GCC 4.4 (issue 463).
+
+ Fixed issue with incorrect handling of custom valueOf methods on
+ string wrappers (issue 760).
+
+ Fixed compilation for ARMv4 (issue 590).
+
+ Improved performance.
+
+
+2010-08-11: Version 2.3.7
+
+ Reduced size of heap snapshots produced by heap profiler (issue 783).
+
+ Introduced v8::Value::IsRegExp method.
+
+ Fixed CPU profiler crash in start / stop sequence when non-existent
+ name is passed (issue http://crbug.com/51594).
+
+ Introduced new indexed property query callbacks API (issue 816). This
+ API is guarded by USE_NEW_QUERY_CALLBACK define and is disabled
+ by default.
+
+ Removed support for object literal get/set with number/string
+ property name.
+
+ Fixed handling of JSObject::elements in CalculateNetworkSize
+ (issue 822).
+
+ Allowed compiling with strict aliasing enabled on GCC 4.4 (issue 463).
+
+
+2010-08-09: Version 2.3.6
+
+ RegExp literals create a new object every time they are evaluated
+ (issue 704).
+
+ Object.seal and Object.freeze return the modified object (issue 809).
+
+ Fixed building using GCC 4.4.4.
+
+
+2010-08-04: Version 2.3.5
+
+ Added support for ES5 property names. Object initialisers and
+ dot-notation property access now allows keywords. Also allowed
+ non-identifiers after "get" or "set" in an object initialiser.
+
+ Randomized the addresses of allocated executable memory on Windows.
+
+
+2010-08-02: Version 2.3.4
+
+ Fixed problems in implementation of ES5 function.prototype.bind.
+
+ Fixed error when using apply with arguments object on ARM (issue 784).
+
+ Added setting of global flags to debugger protocol.
+
+ Fixed an error affecting cached results of sin and cos (issue 792).
+
+ Removed memory leak from a boundary case where V8 is not initialized.
+
+ Fixed issue where debugger could set breakpoints outside the body
+ of a function.
+
+ Fixed issue in debugger when using both live edit and step in features.
+
+ Added Number-letter (Nl) category to Unicode tables. These characters
+ can now be used in identifiers.
+
+ Fixed an assert failure on X64 (issue 806).
+
+ Performance improvements on all platforms.
+
+
+2010-07-26: Version 2.3.3
+
+ Fixed error when building the d8 shell in a fresh checkout.
+
+ Implemented Function.prototype.bind (ES5 15.3.4.5).
+
+ Fixed an error in inlined stores on ia32.
+
+ Fixed an error when setting a breakpoint at the end of a function
+ that does not end with a newline character.
+
+ Performance improvements on all platforms.
+
+
+2010-07-21: Version 2.3.2
+
+ Fixed compiler warnings when building with LLVM.
+
+ Fixed a bug with for-in applied to strings (issue 785).
+
+ Performance improvements on all platforms.
+
+
+2010-07-19: Version 2.3.1
+
+ Fixed compilation and linking with V8_INTERPRETED_REGEXP flag.
+
+ Fixed bug related to code flushing while compiling a lazy
+ compilable function (issue http://crbug.com/49099).
+
+ Performance improvements on all platforms.
+
+
+2010-07-15: Version 2.3.0
+
+ Added ES5 Object.seal and Object.isSealed.
+
+ Added debugger API for scheduling debugger commands from a
+ separate thread.
+
+
+2010-07-14: Version 2.2.24
+
+ Added API for capturing stack traces for uncaught exceptions.
+
+ Fixed crash bug when preparsing from a non-external V8 string
+ (issue 775).
+
+ Fixed JSON.parse bug causing input not to be converted to string
+ (issue 764).
+
+ Added ES5 Object.freeze and Object.isFrozen.
+
+ Performance improvements on all platforms.
+
+
+2010-07-07: Version 2.2.23
+
+ API change: Convert Unicode code points outside the basic multilingual
+ plane to the replacement character. Previous behavior was to silently
+ truncate the value to 16 bits.
+
+ Fixed crash: handle all flat string types in regexp replace.
+
+ Prevent invalid pre-parsing data passed in through the API from
+ crashing V8.
+
+ Performance improvements on all platforms.
+
+
+2010-07-05: Version 2.2.22
+
+ Added ES5 Object.isExtensible and Object.preventExtensions.
+
+ Enabled building V8 as a DLL.
+
+ Fixed a bug in date code where -0 was not interpreted as 0
+ (issue 736).
+
+ Performance improvements on all platforms.
+
+
+2010-06-30: Version 2.2.21
+
+ Fixed bug in externalizing some ASCII strings (Chromium issue 47824).
+
+ Updated JSON.stringify to floor the space parameter (issue 753).
+
+ Updated the Mozilla test expectations to the newest version.
+
+ Updated the ES5 Conformance Test expectations to the latest version.
+
+ Updated the V8 benchmark suite.
+
+ Provide actual breakpoints locations in response to setBreakpoint
+ and listBreakpoints requests.
+
+
+2010-06-28: Version 2.2.20
+
+ Fixed bug with for-in on x64 platform (issue 748).
+
+ Fixed crash bug on x64 platform (issue 756).
+
+ Fixed bug in Object.getOwnPropertyNames. (chromium issue 41243).
+
+ Fixed a bug on ARM that caused the result of 1 << x to be
+ miscalculated for some inputs.
+
+ Performance improvements on all platforms.
+
+
+2010-06-23: Version 2.2.19
+
+ Fixed bug that causes the build to break when profillingsupport=off
+ (issue 738).
+
+ Added expose-externalize-string flag for testing extensions.
+
+ Resolve linker issues with using V8 as a DLL causing a number of
+ problems with unresolved symbols.
+
+ Fixed build failure for cctests when ENABLE_DEBUGGER_SUPPORT is not
+ defined.
+
+ Performance improvements on all platforms.
+
+
+2010-06-16: Version 2.2.18
+
+ Added API functions to retrieve information on indexed properties
+ managed by the embedding layer. Fixes bug 737.
+
+ Made ES5 Object.defineProperty support array elements. Fixes bug 619.
+
+ Added heap profiling to the API.
+
+ Removed old named property query from the API.
+
+ Incremental performance improvements.
+
+
+2010-06-14: Version 2.2.17
+
+ Improved debugger support for stepping out of functions.
+
+ Incremental performance improvements.
+
+
+2010-06-09: Version 2.2.16
+
+ Removed the SetExternalStringDiposeCallback API. Changed the
+ disposal of external string resources to call a virtual Dispose
+ method on the resource.
+
+ Added support for more precise break points when debugging and
+ stepping.
+
+ Memory usage improvements on all platforms.
+
+
+2010-06-07: Version 2.2.15
+
+ Added an API to control the disposal of external string resources.
+
+ Added missing initialization of a couple of variables which makes
+ some compilers complaint when compiling with -Werror.
+
+ Improved performance on all platforms.
+
+
+2010-06-02: Version 2.2.14
+
+ Fixed a crash in code generated for String.charCodeAt.
+
+ Fixed a compilation issue with some GCC versions (issue 727).
+
+ Performance optimizations on x64 and ARM platforms.
+
+
+2010-05-31: Version 2.2.13
+
+ Implemented Object.getOwnPropertyDescriptor for element indices and
+ strings (issue 599).
+
+ Fixed bug for windows 64 bit C calls from generated code.
+
+ Added new scons flag unalignedaccesses for arm builds.
+
+ Performance improvements on all platforms.
+
+
+2010-05-26: Version 2.2.12
+
+ Allowed accessors to be defined on objects rather than just object
+ templates.
+
+ Changed the ScriptData API.
+
+
+2010-05-21: Version 2.2.11
+
+ Fixed crash bug in liveedit on 64 bit.
+
+ Use 'full compiler' when debugging is active. This should increase
+ the density of possible break points, making single step more fine
+ grained. This will only take effect for functions compiled after
+ debugging has been started, so recompilation of all functions is
+ required to get the full effect. IA32 and x64 only for now.
+
+ Misc. fixes to the Solaris build.
+
+ Added new flags --print-cumulative-gc-stat and --trace-gc-nvp.
+
+ Added filtering of CPU profiles by security context.
+
+ Fixed crash bug on ARM when running without VFP2 or VFP3.
+
+ Incremental performance improvements in all backends.
+
+
+2010-05-17: Version 2.2.10
+
+ Performance improvements in the x64 and ARM backends.
+
+
+2010-05-10: Version 2.2.9
+
+ Allowed Object.create to be called with a function (issue 697).
+
+ Fixed bug with Date.parse returning a non-NaN value when called on a
+ non date string (issue 696).
+
+ Allowed unaligned memory accesses on ARM targets that support it (by
+ Subrato K De of CodeAurora <subratokde@codeaurora.org>).
+
+ C++ API for retrieving JavaScript stack trace information.
+
+
+2010-05-05: Version 2.2.8
+
+ Performance improvements in the x64 and ARM backends.
+
+
+2010-05-03: Version 2.2.7
+
+ Added support for ES5 date time string format to Date.parse.
+
+ Performance improvements in the x64 backend.
+
+
+2010-04-28: Version 2.2.6
+
+ Added "amd64" as recognized architecture in scons build script
+ (by Ryan Dahl <coldredlemur@gmail.com>).
+
+ Fixed bug in String search and replace with very simple RegExps.
+
+ Fixed bug in RegExp containing "\b^".
+
+ Performance improvements on all platforms.
+
+
+2010-04-26: Version 2.2.5
+
+ Various performance improvements (especially for ARM and x64)
+
+ Fixed bug in CPU profiling (http://crbug.com/42137)
+
+ Fixed a bug with the natives cache.
+
+ Fixed two bugs in the ARM code generator that can cause
+ wrong calculations.
+
+ Fixed a bug that may cause a wrong result for shift operations.
+
+
+2010-04-21: Version 2.2.4
+
+ Fixed warnings on arm on newer GCC versions.
+
+ Fixed a number of minor bugs.
+
+ Performance improvements on all platforms.
+
+
+2010-04-14: Version 2.2.3
+
+ Added stack command and mem command to ARM simulator debugger.
+
+ Fixed scons snapshot and ARM build, and Windows X64 build issues.
+
+ Performance improvements on all platforms.
+
+
+2010-04-12: Version 2.2.2
+
+ Introduced new profiler API.
+
+ Fixed random number generator to produce full 32 random bits.
+
+
+2010-04-06: Version 2.2.1
+
+ Debugger improvements.
+
+ Fixed minor bugs.
+
+
+2010-03-29: Version 2.2.0
+
+ Fixed a few minor bugs.
+
+ Performance improvements for string operations.
+
+
+2010-03-26: Version 2.1.10
+
+ Fixed scons build issues.
+
+ Fixed a couple of minor bugs.
+
+
+2010-03-25: Version 2.1.9
+
+ Added API support for reattaching a global object to a context.
+
+ Extended debugger API with access to the internal debugger context.
+
+ Fixed Chromium crashes (issues http://crbug.com/39128 and
+ http://crbug.com/39160)
+
+
+2010-03-24: Version 2.1.8
+
+ Added fine-grained garbage collection callbacks to the API.
+
+ Performance improvements on all platforms.
+
+
+2010-03-22: Version 2.1.7
+
+ Fixed issue 650.
+
+ Fixed a bug where __proto__ was sometimes enumerated (issue 646).
+
+ Performance improvements for arithmetic operations.
+
+ Performance improvements for string operations.
+
+ Print script name and line number information in stack trace.
+
+
+2010-03-17: Version 2.1.6
+
+ Performance improvements for arithmetic operations.
+
+ Performance improvements for string operations.
+
+
+2010-03-10: Version 2.1.4
+
+ Fixed code cache lookup for keyed IC's (issue http://crbug.com/37853).
+
+ Performance improvements on all platforms.
+
+
+2010-03-10: Version 2.1.3
+
+ Added API method for context-disposal notifications.
+
+ Added API method for accessing elements by integer index.
+
+ Added missing implementation of Uint32::Value and Value::IsUint32
+ API methods.
+
+ Added IsExecutionTerminating API method.
+
+ Disabled strict aliasing for GCC 4.4.
+
+ Fixed string-concatenation bug (issue 636).
+
+ Performance improvements on all platforms.
+
+
+2010-02-23: Version 2.1.2
+
+ Fixed a crash bug caused by wrong assert.
+
+ Fixed a bug with register names on 64-bit V8 (issue 615).
+
+ Performance improvements on all platforms.
+
+
+2010-02-19: Version 2.1.1
+
+ [ES5] Implemented Object.defineProperty.
+
+ Improved profiler support.
+
+ Added SetPrototype method in the public V8 API.
+
+ Added GetScriptOrigin and GetScriptLineNumber methods to Function
+ objects in the API.
+
+ Performance improvements on all platforms.
+
+
+2010-02-03: Version 2.1.0
+
+ Values are now always wrapped in objects when used as a receiver.
+ (issue 223).
+
+ [ES5] Implemented Object.getOwnPropertyNames.
+
+ [ES5] Restrict JSON.parse to only accept strings that conforms to the
+ JSON grammar.
+
+ Improvement of debugger agent (issue 549 and 554).
+
+ Fixed problem with skipped stack frame in profiles (issue 553).
+
+ Solaris support by Erich Ocean <erich.ocean@me.com> and Ryan Dahl
+ <ry@tinyclouds.org>.
+
+ Fixed a bug that Math.round() returns incorrect results for huge
+ integers.
+
+ Fixed enumeration order for objects created from some constructor
+ functions (isue http://crbug.com/3867).
+
+ Fixed arithmetic on some integer constants (issue 580).
+
+ Numerous performance improvements including porting of previous IA-32
+ optimizations to x64 and ARM architectures.
+
+
+2010-01-14: Version 2.0.6
+
+ Added ES5 Object.getPrototypeOf, GetOwnPropertyDescriptor,
+ GetOwnProperty, FromPropertyDescriptor.
+
+ Fixed Mac x64 build errors.
+
+ Improved performance of some math and string operations.
+
+ Improved performance of some regexp operations.
+
+ Improved performance of context creation.
+
+ Improved performance of hash tables.
+
+
+2009-12-18: Version 2.0.5
+
+ Extended to upper limit of map space to allow for 7 times as many map
+ to be allocated (issue 524).
+
+ Improved performance of code using closures.
+
+ Improved performance of some binary operations involving doubles.
+
+
+2009-12-16: Version 2.0.4
+
+ Added ECMAScript 5 Object.create.
+
+ Improved performance of Math.max and Math.min.
+
+ Optimized adding of strings on 64-bit platforms.
+
+ Improved handling of external strings by using a separate table
+ instead of weak handles. This improves garbage collection
+ performance and uses less memory.
+
+ Changed code generation for object and array literals in toplevel
+ code to be more compact by doing more work in the runtime.
+
+ Fixed a crash bug triggered when garbage collection happened during
+ generation of a callback load inline cache stub.
+
+ Fixed crash bug sometimes triggered when local variables shadowed
+ parameters in functions that used the arguments object.
+
+
+2009-12-03: Version 2.0.3
+
+ Optimized handling and adding of strings, for-in and Array.join.
+
+ Heap serialization is now non-destructive.
+
+ Improved profiler support with information on time spend in C++
+ callbacks registered through the API.
+
+ Added commands to the debugger protocol for starting/stopping
+ profiling.
+
+ Enabled the non-optimizing compiler for top-level code.
+
+ Changed the API to only allow strings to be set as data objects on
+ Contexts and scripts to avoid potentially keeping global objects
+ around for too long (issue 528).
+
+ OpenBSD support patch by Peter Valchev <pvalchev@gmail.com>.
+
+ Fixed bugs.
+
+
+2009-11-24: Version 2.0.2
+
+ Improved profiler support.
+
+ Fixed bug that broke compilation of d8 with readline support.
+
+
+2009-11-20: Version 2.0.1
+
+ Fixed crash bug in String.prototype.replace.
+
+ Reverted a change which caused Chromium interactive ui test
+ failures.
+
+
+2009-11-18: Version 2.0.0
+
+ Added support for VFP on ARM.
+
+ Added TryCatch::ReThrow method to the API.
+
+ Reduced the size of snapshots and improved the snapshot load time.
+
+ Improved heap profiler support.
+
+ 64-bit version now supported on Windows.
+
+ Fixed a number of debugger issues.
+
+ Fixed bugs.
+
+
+2009-10-29: Version 1.3.18
+
+ Reverted a change which caused crashes in RegExp replace.
+
+ Reverted a change which caused Chromium ui_tests failure.
+
+
+2009-10-28: Version 1.3.17
+
+ Added API method to get simple heap statistics.
+
+ Improved heap profiler support.
+
+ Fixed the implementation of the resource constraint API so it
+ works when using snapshots.
+
+ Fixed a number of issues in the Windows 64-bit version.
+
+ Optimized calls to API getters.
+
+ Added valgrind notification on code modification to the 64-bit version.
+
+ Fixed issue where we logged shared library addresses on Windows at
+ startup and never used them.
+
+
+2009-10-16: Version 1.3.16
+
+ X64: Convert smis to holding 32 bits of payload.
+
+ Introduced v8::Integer::NewFromUnsigned method.
+
+ Added missing null check in Context::GetCurrent.
+
+ Added trim, trimLeft and trimRight methods to String
+ Patch by Jan de Mooij <jandemooij@gmail.com>
+
+ Implement ES5 Array.isArray
+ Patch by Jan de Mooij <jandemooij@gmail.com>
+
+ Skip access checks for hidden properties.
+
+ Added String::Concat(Handle<String> left, Handle<String> right) to the
+ V8 API.
+
+ Fixed GYP-based builds of V8.
+
+
+2009-10-07: Version 1.3.15
+
+ Expanded the maximum size of the code space to 512MB for 64-bit mode.
+
+ Fixed a crash bug happening when starting profiling (issue
+ http://crbug.com/23768).
+
+
+2009-10-07: Version 1.3.14
+
+ Added GetRealNamedProperty to the API to lookup real properties
+ located on the object or in the prototype chain skipping any
+ interceptors.
+
+ Fixed the stack limits setting API to work correctly with threads. The
+ stack limit now needs to be set to each thread thich is used with V8.
+
+ Removed the high-priority flag from IdleNotification()
+
+ Ensure V8 is initialized before locking and unlocking threads.
+
+ Implemented a new JavaScript minifier for compressing the source of
+ the built-in JavaScript. This removes non-Open Source code from Douglas
+ Crockford from the project.
+
+ Added a missing optimization in StringCharAt.
+
+ Fixed some flaky socket tests.
+
+ Change by Alexander Botero-Lowry to fix profiler sampling on FreeBSD
+ in 64-bit mode.
+
+ Fixed memory leaks in the thread management code.
+
+ Fixed the result of assignment to a pixel array. The assigned value
+ is now the result.
+
+ Error reporting for invalid left-hand sides in for-in statements, pre-
+ and postfix count expressions, and assignments now matches the JSC
+ behavior in Safari 4.
+
+ Follow the spec in disallowing function declarations without a name.
+
+ Always allocate code objects within a 2 GB range. On x64 architecture
+ this is used to use near calls (32-bit displacement) in Code objects.
+
+ Optimized array construction ported to x64 and ARM architectures.
+
+ [ES5] Changed Object.keys to return strings for element indices.
+
+
+2009-09-23: Version 1.3.13
+
+ Fixed uninitialized memory problem.
+
+ Improved heap profiler support.
+
+
+2009-09-22: Version 1.3.12
+
+ Changed behavior of |function|.toString() on built-in functions to
+ be compatible with other implementations. Patch by Jan de Mooij.
+
+ Added Object::IsDirty in the API.
+
+ Optimized array construction; it is now handled purely in native
+ code.
+
+ [ES5] Made properties of the arguments array enumerable.
+
+ [ES5] Added test suite adapter for the es5conform test suite.
+
+ [ES5] Added Object.keys function.
+
+
+2009-09-15: Version 1.3.11
+
+ Fixed crash in error reporting during bootstrapping.
+
+ Optimized generated IA32 math code by using SSE2 instructions when
+ available.
+
+ Implemented missing pieces of debugger infrastructure on ARM. The
+ debugger is now fully functional on ARM.
+
+ Made 'hidden' the default visibility for gcc.
+
+
+2009-09-09: Version 1.3.10
+
+ Fixed profiler on Mac in 64-bit mode.
+
+ Optimized creation of objects from simple constructor functions on
+ ARM.
+
+ Fixed a number of debugger issues.
+
+ Reduced the amount of memory consumed by V8.
+
+
+2009-09-02: Version 1.3.9
+
+ Optimized stack guard checks on ARM.
+
+ Optimized API operations by inlining more in the API.
+
+ Optimized creation of objects from simple constructor functions.
+
+ Enabled a number of missing optimizations in the 64-bit port.
+
+ Implemented native-code support for regular expressions on ARM.
+
+ Stopped using the 'sahf' instruction on 64-bit machines that do
+ not support it.
+
+ Fixed a bug in the support for forceful termination of JavaScript
+ execution.
+
+
+2009-08-26: Version 1.3.8
+
+ Changed the handling of idle notifications to allow idle
+ notifications when V8 has not yet been initialized.
+
+ Fixed ARM simulator compilation problem on Windows.
+
+
+2009-08-25: Version 1.3.7
+
+ Reduced the size of generated code on ARM platforms by reducing
+ the size of constant pools.
+
+ Changed build files to not include the 'ENV' user environment
+ variable in the build environment.
+
+ Changed the handling of idle notifications.
+
+
+2009-08-21: Version 1.3.6
+
+ Added support for forceful termination of JavaScript execution.
+
+ Added low memory notification to the API. The embedding host can signal
+ a low memory situation to V8.
+
+ Changed the handling of global handles (persistent handles in the API
+ sense) to avoid issues regarding allocation of new global handles
+ during weak handle callbacks.
+
+ Changed the growth policy of the young space.
+
+ Fixed a GC issue introduced in version 1.3.5.
+
+
+2009-08-19: Version 1.3.5
+
+ Optimized initialization of some arrays in the builtins.
+
+ Fixed mac-nm script to support filenames with spaces.
+
+ Support for using the V8 profiler when V8 is embedded in a Windows DLL.
+
+ Changed typeof RegExp from 'object' to 'function' for compatibility.
+ Fixed bug where regexps were not callable across contexts.
+
+ Added context independent script compilation to the API.
+
+ Added API call to get the stack trace for an exception.
+
+ Added API for getting object mirrors.
+
+ Made sure that SSE3 instructions are used whenever possible even when
+ running off a snapshot generated without using SSE3 instructions.
+
+ Tweaked the handling of the initial size and growth policy of the heap.
+
+ Added native code generation for RegExp to 64-bit version.
+
+ Added JavaScript debugger support to 64-bit version.
+
+
+2009-08-13: Version 1.3.4
+
+ Added a readline() command to the d8 shell.
+
+ Fixed bug in json parsing.
+
+ Added idle notification to the API and reduced memory on idle
+ notifications.
+
+
+2009-08-12: Version 1.3.3
+
+ Fixed issue 417: incorrect %t placeholder expansion.
+
+ Added .gitignore file similar to Chromium's one.
+
+ Fixed SConstruct file to build with new logging code for Android.
+
+ API: added function to find instance of template in prototype
+ chain. Inlined Object::IsInstanceOf.
+
+ Land change to notify valgrind when we modify code on x86.
+
+ Added api call to determine whether a string can be externalized.
+
+ Added a write() command to d8.
+
+
+2009-08-05: Version 1.3.2
+
+ Started new compiler infrastructure for two-pass compilation using a
+ control flow graph constructed from the AST.
+
+ Profiler stack sampling for X64.
+
+ Safe handling of NaN to Posix platform-dependent time functions.
+
+ Added a new profiler control API to unify controlling various aspects
+ of profiling.
+
+ Fixed issue 392.
+
+
+2009-07-30: Version 1.3.1
+
+ Speed improvements to accessors and interceptors.
+
+ Added support for capturing stack information on custom errors.
+
+ Added support for morphing an object into a pixel array where its
+ indexed properties are stored in an external byte array. Values written
+ are always clamped to the 0..255 interval.
+
+ Profiler on x64 now handles C/C++ functions from shared libraries.
+
+ Changed the debugger to avoid stepping into function.call/apply if the
+ function is a built-in.
+
+ Initial implementation of constructor heap profile for JS objects.
+
+ More fine grained control of profiling aspects through the API.
+
+ Optimized the called as constructor check for API calls.
+
+
+2009-07-27: Version 1.3.0
+
+ Allowed RegExp objects to be called as functions (issue 132).
+
+ Fixed issue where global property cells would escape after
+ detaching the global object; see http://crbug.com/16276.
+
+ Added support for stepping into setters and getters in the
+ debugger.
+
+ Changed the debugger to avoid stopping in its own JavaScript code
+ and in the code of built-in functions.
+
+ Fixed issue 345 by avoiding duplicate escaping labels.
+
+ Fixed ARM code generator crash in short-circuited boolean
+ expressions and added regression tests.
+
+ Added an external allocation limit to avoid issues where small V8
+ objects would hold on to large amounts of external memory without
+ causing garbage collections.
+
+ Finished more of the inline caching stubs for x64 targets.
+
+
+2009-07-13: Version 1.2.14
+
+ Added separate paged heap space for global property cells and
+ avoid updating the write barrier when storing into them.
+
+ Improved peep-hole optimization on ARM platforms by not emitting
+ unnecessary debug information.
+
+ Re-enabled ICs for loads and calls that skip a global object
+ during lookup through the prototype chain.
+
+ Allowed access through global proxies to use ICs.
+
+ Fixed issue 401.
+
+
+2009-07-09: Version 1.2.13
+
+ Fixed issue 397, issue 398, and issue 399.
+
+ Added support for breakpoint groups.
+
+ Fixed bugs introduced with the new global object representation.
+
+ Fixed a few bugs in the ARM code generator.
+
+
+2009-07-06: Version 1.2.12
+
+ Added stack traces collection to Error objects accessible through
+ the e.stack property.
+
+ Changed RegExp parser to use a recursive data structure instead of
+ stack-based recursion.
+
+ Optimized Date object construction and string concatenation.
+
+ Improved performance of div, mod, and mul on ARM platforms.
+
+
+2009-07-02: Version 1.2.11
+
+ Improved performance on IA-32 and ARM.
+
+ Fixed profiler sampler implementation on Mac OS X.
+
+ Changed the representation of global objects to improve
+ performance of adding a lot of new properties.
+
+
+2009-06-29: Version 1.2.10
+
+ Improved debugger support.
+
+ Fixed bug in exception message reporting (issue 390).
+
+ Improved overall performance.
+
+
+2009-06-23: Version 1.2.9
+
+ Improved math performance on ARM.
+
+ Fixed profiler name-inference bug.
+
+ Fixed handling of shared libraries in the profiler tick processor
+ scripts.
+
+ Fixed handling of tests that time out in the test scripts.
+
+ Fixed compilation on MacOS X version 10.4.
+
+ Fixed two bugs in the regular expression engine.
+
+ Fixed a bug in the string type inference.
+
+ Fixed a bug in the handling of 'constant function' properties.
+
+ Improved overall performance.
+
+
+2009-06-16: Version 1.2.8
+
+ Optimized math on ARM platforms.
+
+ Fixed two crash bugs in the handling of getters and setters.
+
+ Improved the debugger support by adding scope chain information.
+
+ Improved the profiler support by compressing log data transmitted
+ to clients.
+
+ Improved overall performance.
+
+
+2009-06-08: Version 1.2.7
+
+ Improved debugger and profiler support.
+
+ Reduced compilation time by improving the handling of deferred
+ code.
+
+ Optimized interceptor accesses where the property is on the object
+ on which the interceptors is attached.
+
+ Fixed compilation problem on GCC 4.4 by changing the stack
+ alignment to 16 bytes.
+
+ Fixed handle creation to follow stric aliasing rules.
+
+ Fixed compilation on FreeBSD.
+
+ Introduced API for forcing the deletion of a property ignoring
+ interceptors and attributes.
+
+
+2009-05-29: Version 1.2.6
+
+ Added a histogram recording hit rates at different levels of the
+ compilation cache.
+
+ Added stack overflow check for the RegExp analysis phase. Previously a
+ very long regexp graph could overflow the stack with recursive calls.
+
+ Use a dynamic buffer when collecting log events in memory.
+
+ Added start/stop events to the profiler log.
+
+ Fixed infinite loop which could happen when setting a debug break while
+ executing a RegExp compiled to native code.
+
+ Fixed handling of lastIndexOf called with negative index (issue 351).
+
+ Fixed irregular crash in profiler test (issue 358).
+
+ Fixed compilation issues with some versions of gcc.
+
+
+2009-05-26: Version 1.2.5
+
+ Fixed bug in initial boundary check for Boyer-Moore text
+ search (issue 349).
+
+ Fixed compilation issues with MinGW and gcc 4.3+ and added support
+ for armv7 and cortex-a8 architectures. Patches by Lei Zhang and
+ Craig Schlenter.
+
+ Added a script cache to the debugger.
+
+ Optimized compilation performance by improving internal data
+ structures and avoiding expensive property load optimizations for
+ code that's infrequently executed.
+
+ Exposed the calling JavaScript context through the static API
+ function Context::GetCalling().
+
+
+2009-05-18: Version 1.2.4
+
+ Improved performance of floating point number allocation for ARM
+ platforms.
+
+ Fixed crash when using the instanceof operator on functions with
+ number values in their prototype chain (issue 341).
+
+ Optimized virtual frame operations in the code generator to speed
+ up compilation time and allocated the frames in the zone.
+
+ Made the representation of virtual frames and jump targets in the
+ code generator much more compact.
+
+ Avoided linear search for non-locals in scope code when resolving
+ variables inside with and eval scopes.
+
+ Optimized lexical scanner by dealing with whitespace as part of
+ the token scanning instead of as a separate step before it.
+
+ Changed the scavenging collector so that promoted objects do not
+ reside in the old generation while their remembered set is being
+ swept for pointers into the young generation.
+
+ Fixed numeric overflow handling when compiling count operations.
+
+
+2009-05-11: Version 1.2.3
+
+ Fixed bug in reporting of out-of-memory situations.
+
+ Introduced hidden prototypes on certain builtin prototype objects
+ such as String.prototype to emulate JSC's behavior of restoring
+ the original function when deleting functions from those prototype
+ objects.
+
+ Fixed crash bug in the register allocator.
+
+
+2009-05-04: Version 1.2.2
+
+ Fixed bug in array sorting for sparse arrays (issue 326).
+
+ Added support for adding a soname when building a shared library
+ on Linux (issue 151).
+
+ Fixed bug caused by morphing internal ASCII strings to external
+ two-byte strings. Slices over ASCII strings have to forward ASCII
+ checks to the underlying buffer string.
+
+ Allowed API call-as-function handlers to be called as
+ constructors.
+
+ Fixed a crash bug where an external string was disposed but a
+ slice of the external string survived as a symbol.
+
+
+2009-04-27: Version 1.2.1
+
+ Added EcmaScript 5 JSON object.
+
+ Fixed bug in preemption support on ARM.
+
+
+2009-04-23: Version 1.2.0
+
+ Optimized floating-point operations on ARM.
+
+ Added a number of extensions to the debugger API.
+
+ Changed the enumeration order for unsigned integer keys to always
+ be numerical order.
+
+ Added a "read" extension to the shell sample.
+
+ Added support for Array.prototype.reduce and
+ Array.prototype.reduceRight.
+
+ Added an option to the SCons build to control Microsoft Visual C++
+ link-time code generation.
+
+ Fixed a number of bugs (in particular issue 315, issue 316,
+ issue 317 and issue 318).
+
+
+2009-04-15: Version 1.1.10
+
+ Fixed crash bug that occurred when loading a const variable in the
+ presence of eval.
+
+ Allowed using with and eval in registered extensions in debug mode
+ by fixing bogus assert.
+
+ Fixed the source position for function returns to enable the
+ debugger to break there.
+
+
+2009-04-14: Version 1.1.9
+
+ Made the stack traversal code in the profiler robust by avoiding
+ to look into the heap.
+
+ Added name inferencing for anonymous functions to facilitate
+ debugging and profiling.
+
+ Re-enabled stats timers in the developer shell (d8).
+
+ Fixed issue 303 by avoiding to shortcut cons-symbols.
+
+
+2009-04-11: Version 1.1.8
+
+ Changed test-debug/ThreadedDebugging to be non-flaky (issue 96).
+
+ Fixed step-in handling for Function.prototype.apply and call in
+ the debugger (issue 269).
+
+ Fixed v8::Object::DeleteHiddenValue to not bail out when there
+ are no hidden properties.
+
+ Added workaround for crash bug, where external symbol table
+ entries with deleted resources would lead to NPEs when looking
+ up in the symbol table.
+
+
+2009-04-07: Version 1.1.7
+
+ Added support for easily importing additional environment
+ variables into the SCons build.
+
+ Optimized strict equality checks.
+
+ Fixed crash in indexed setters on objects without a corresponding
+ getter (issue 298).
+
+ Re-enabled script compilation cache.
+
+
+2009-04-01: Version 1.1.6
+
+ Reverted an unsafe code generator change.
+
+
+2009-04-01: Version 1.1.5
+
+ Fixed bug that caused function literals to not be optimized as
+ much as other functions.
+
+ Improved profiler support.
+
+ Fixed a crash bug in connection with debugger unloading.
+
+ Fixed a crash bug in the code generator caused by losing the
+ information that a frame element was copied.
+
+ Fixed an exception propagation bug that could cause non-null
+ return values when exceptions were thrown.
+
+
+2009-03-30: Version 1.1.4
+
+ Optimized String.prototype.match.
+
+ Improved the stack information in profiles.
+
+ Fixed bug in ARM port making it possible to compile the runtime
+ system for thumb mode again.
+
+ Implemented a number of optimizations in the code generator.
+
+ Fixed a number of memory leaks in tests.
+
+ Fixed crash bug in connection with script source code and external
+ strings.
+
+
+2009-03-24: Version 1.1.3
+
+ Fixed assertion failures in compilation of loop conditions.
+
+ Removed STL dependency from developer shell (d8).
+
+ Added infrastructure for protecting the V8 heap from corruption
+ caused by memory modifications from the outside.
+
+
+2009-03-24: Version 1.1.2
+
+ Improved frame merge code generated by the code generator.
+
+ Optimized String.prototype.replace.
+
+ Implemented __defineGetter__ and __defineSetter__ for properties
+ with integer keys on non-array objects.
+
+ Improved debugger and profiler support.
+
+ Fixed a number of portability issues to allow compilation for
+ smaller ARM devices.
+
+ Exposed object cloning through the API.
+
+ Implemented hidden properties. This is used to expose an identity
+ hash for objects through the API.
+
+ Implemented restarting of regular expressions if their input
+ string changes representation during preemption.
+
+ Fixed a code generator bug that could cause assignments in loops
+ to be ignored if using continue to break out of the loop (issue
+ 284).
+
+
+2009-03-12: Version 1.1.1
+
+ Fixed an assertion in the new compiler to take stack overflow
+ exceptions into account.
+
+ Removed exception propagation code that could cause crashes.
+
+ Fixed minor bug in debugger line number computations.
+
+ 8-byte align the C stack on Linux and Windows to speed up floating
+ point computations.
+
+
+2009-03-12: Version 1.1.0
+
+ Improved code generation infrastructure by doing simple register
+ allocation and constant folding and propagation.
+
+ Optimized regular expression matching by avoiding to create
+ intermediate string arrays and by flattening nested array
+ representations of RegExp data.
+
+ Traverse a few stack frames when recording profiler samples to
+ include partial call graphs in the profiling output.
+
+ Added support for using OProfile to profile generated code.
+
+ Added remote debugging support to the D8 developer shell.
+
+ Optimized creation of nested literals like JSON objects.
+
+ Fixed a bug in garbage collecting unused maps and turned it on by
+ default (--collect-maps).
+
+ Added support for running tests under Valgrind.
+
+
+2009-02-27: Version 1.0.3
+
+ Optimized double-to-integer conversions in bit operations by using
+ SSE3 instructions if available.
+
+ Optimized initialization sequences that store to multiple
+ properties of the same object.
+
+ Changed the D8 debugger frontend to use JSON messages.
+
+ Force garbage collections when disposing contexts.
+
+ Align code objects at 32-byte boundaries.
+
+
+2009-02-25: Version 1.0.2
+
+ Improved profiling support by performing simple call stack
+ sampling for ticks and by fixing a bug in the logging of code
+ addresses.
+
+ Fixed a number of debugger issues.
+
+ Optimized code that uses eval.
+
+ Fixed a couple of bugs in the regular expression engine.
+
+ Reduced the size of generated code for certain regular expressions.
+
+ Removed JSCRE completely.
+
+ Fixed issue where test could not be run if there was a dot in the
+ checkout path.
+
+
+2009-02-13: Version 1.0.1
+
+ Fixed two crash-bugs in irregexp (issue 231 and 233).
+
+ Fixed a number of minor bugs (issue 87, 227 and 228).
+
+ Added support for morphing strings to external strings on demand
+ to avoid having to create copies in the embedding code.
+
+ Removed experimental support for external symbol callbacks.
+
+
+2009-02-09: Version 1.0.0
+
+ Fixed crash-bug in the code generation for case independent 16 bit
+ backreferences.
+
+ Made shells more robust in the presence of string conversion
+ failures (issue 224).
+
+ Fixed a potential infinite loop when attempting to resolve
+ eval (issue 221).
+
+ Miscellaneous fixes to the new regular expression engine.
+
+ Reduced binary by stripping unneeded text from JavaScript library and
+ minifying some JavaScript files.
+
+
+2009-01-27: Version 0.4.9
+
+ Enabled new regular expression engine.
+
+ Made a number of changes to the debugger protocol.
+
+ Fixed a number of bugs in the preemption support.
+
+ Added -p option to the developer shell to run files in parallel
+ using preemption.
+
+ Fixed a number of minor bugs (including issues 176, 187, 189, 192,
+ 193, 198 and 201).
+
+ Fixed a number of bugs in the serialization/deserialization
+ support for the ARM platform.
+
+
+2009-01-19: Version 0.4.8.1
+
+ Minor patch to debugger support.
+
+
+2009-01-16: Version 0.4.8
+
+ Fixed string length bug on ARM (issue 171).
+
+ Made most methods in the API const.
+
+ Optimized object literals by improving data locality.
+
+ Fixed bug that caused incomplete functions to be cached in case of
+ stack overflow exceptions.
+
+ Fixed bugs that caused catch variables and variables introduced by
+ eval to behave incorrectly when using accessors (issues 186, 190
+ and 191).
+
+
+2009-01-06: Version 0.4.7
+
+ Minor bugfixes and optimizations.
+
+ Added command line debugger to D8 shell.
+
+ Fixed subtle bug that caused the wrong 'this' to be used when
+ calling a caught function in a catch clause.
+
+ Inline array loads within loops directly in the code instead of
+ always calling a stub.
+
+
+2008-12-11: Version 0.4.6
+
+ Fixed exception reporting bug where certain exceptions were
+ incorrectly reported as uncaught.
+
+ Improved the memory allocation strategy used during compilation to
+ make running out of memory when compiling huge scripts less
+ likely.
+
+ Optimized String.replace by avoiding the construction of certain
+ sub strings.
+
+ Fixed bug in code generation for large switch statements on ARM.
+
+ Fixed bug that caused V8 to change the global object template
+ passed in by the user.
+
+ Changed the API for creating object groups used during garbage
+ collection. Entire object groups are now passed to V8 instead of
+ individual members of the groups.
+
+
+2008-12-03: Version 0.4.5
+
+ Added experimental API support for allocating V8 symbols as
+ external strings.
+
+ Fixed bugs in debugging support on ARM.
+
+ Changed eval implementation to correctly detect whether or not a
+ call to eval is aliased.
+
+ Fixed bug caused by a combination of the compilation cache and
+ dictionary probing in native code. The bug caused us to sometimes
+ call functions that had not yet been compiled.
+
+ Added platform support for FreeBSD.
+
+ Added support for building V8 on Windows with either the shared or
+ static version of MSVCRT
+
+ Added the v8::jscre namespace around the jscre functions to avoid
+ link errors (duplicate symbols) when building Google Chrome.
+
+ Added support for calling a JavaScript function with the current
+ debugger execution context as its argument to the debugger
+ interface.
+
+ Changed the type of names of counters from wchar_t to char.
+
+ Changed the Windows system call used to compute daylight savings
+ time. The system call that we used to use became four times
+ slower on WinXP SP3.
+
+ Added support in the d8 developer shell for memory-mapped counters
+ and added a stats-viewer tool.
+
+ Fixed bug in upper/lower case mappings (issue 149).
+
+
+2008-11-17: Version 0.4.4
+
+ Reduced code size by using shorter instruction encoding when
+ possible.
+
+ Added a --help option to the shell sample and to the d8 shell.
+
+ Added visual studio project files for building the ARM simulator.
+
+ Fixed a number of ARM simulator issues.
+
+ Fixed bug in out-of-memory handling on ARM.
+
+ Implemented shell support for passing arguments to a script from
+ the command line.
+
+ Fixed bug in date code that made certain date functions return -0
+ instead of 0 for dates before the epoch.
+
+ Restricted applications of eval so it can only be used in the
+ context of the associated global object.
+
+ Treat byte-order marks as whitespace characters.
+
+
+2008-11-04: Version 0.4.3
+
+ Added support for API accessors that prohibit overwriting by
+ accessors defined in JavaScript code by using __defineGetter__ and
+ __defineSetter__.
+
+ Improved handling of conditionals in test status files.
+
+ Introduced access control in propertyIsEnumerable.
+
+ Improved performance of some string operations by caching
+ information about the type of the string between operations.
+
+ Fixed bug in fast-case code for switch statements that only have
+ integer labels.
+
+
+2008-10-30: Version 0.4.2
+
+ Improved performance of Array.prototype.concat by moving the
+ implementation to C++ (issue 123).
+
+ Fixed heap growth policy to avoid growing old space to its maximum
+ capacity before doing a garbage collection and fixed issue that
+ would lead to artificial out of memory situations (issue 129).
+
+ Fixed Date.prototype.toLocaleDateString to return the date in the
+ same format as WebKit.
+
+ Added missing initialization checks to debugger API.
+
+ Added removing of unused maps during GC.
+
+
+2008-10-28: Version 0.4.1
+
+ Added caching of RegExp data in compilation cache.
+
+ Added Visual Studio project file for d8 shell.
+
+ Fixed function call performance regression introduced in version
+ 0.4.0 when splitting the global object in two parts (issue 120).
+
+ Fixed issue 131 by checking for empty handles before throwing and
+ reporting exceptions.
+
+
+2008-10-23: Version 0.4.0
+
+ Split the global object into two parts: The state holding global
+ object and the global object proxy.
+
+ Fixed bug that affected the value of an assignment to an element
+ in certain cases (issue 116).
+
+ Added GetPropertyNames functionality (issue 33) and extra Date
+ functions (issue 77) to the API.
+
+ Changed WeakReferenceCallback to take a Persistent<Value> instead
+ of a Persistent<Object> (issue 101).
+
+ Fixed issues with message reporting for exceptions in try-finally
+ blocks (issues 73 and 75).
+
+ Optimized flattening of strings and string equality checking.
+
+ Improved Boyer-Moore implementation for faster indexOf operations.
+
+ Added development shell (d8) which includes counters and
+ completion support.
+
+ Fixed problem with the receiver passed to functions called from
+ eval (issue 124).
+
+
+2008-10-16: Version 0.3.5
+
+ Improved string hash-code distribution by excluding bit-field bits
+ from the hash-code.
+
+ Changed string search algorithm used in indexOf from KMP to
+ Boyer-Moore.
+
+ Improved the generated code for the instanceof operator.
+
+ Improved performance of slow-case string equality checks by
+ specializing the code based on the string representation.
+
+ Improve the handling of out-of-memory situations (issue 70).
+
+ Improved performance of strict equality checks.
+
+ Improved profiler output to make it easier to see anonymous
+ functions.
+
+ Improved performance of slow-case keyed loads.
+
+ Improved property access performance by allocating a number of
+ properties in the front object.
+
+ Changed the toString behavior on the built-in object constructors
+ to print [native code] instead of the actual source. Some web
+ applications do not like constructors with complex toString
+ results.
+
+
+2008-10-06: Version 0.3.4
+
+ Changed Array.prototype.sort to use quick sort.
+
+ Fixed code generation issue where leaving a finally block with
+ break or continue would accumulate elements on the expression
+ stack (issue 86).
+
+ Made sure that the name accessor on functions returns the expected
+ names for builtin JavaScript functions and C++ callback functions.
+
+ Added fast case code for extending the property storage array of
+ JavaScript objects.
+
+ Ported switch statement optimizations introduced in version 0.3.3
+ to the ARM code generator.
+
+ Allowed GCC to use strict-aliasing rules when compiling.
+
+ Improved performance of arguments object allocation by taking care
+ of arguments adaptor frames in the generated code.
+
+ Updated the V8 benchmark suite to version 2.
+
+
+2008-09-25: Version 0.3.3
+
+ Improved handling of relocation information to enable more
+ peep-hole optimizations.
+
+ Optimized switch statements where all labels are constant small
+ integers.
+
+ Optimized String.prototype.indexOf for common cases.
+
+ Fixed more build issues (issue 80).
+
+ Fixed a couple of profiler issues.
+
+ Fixed bug where the body of a function created using the Function
+ constructor was not allowed to end with a single-line comment
+ (issue 85).
+
+ Improved handling of object literals by canonicalizing object
+ literal maps. This will allow JSON objects with the same set of
+ properties to share the same map making inline caching work better
+ for JSON objects.
+
+
+2008-09-17: Version 0.3.2
+
+ Generalized the EvalCache into a CompilationCache and enabled it
+ for scripts too. The current strategy is to retire all entries
+ whenever a mark-sweep collection is started.
+
+ Fixed bug where switch statements containing only a default case
+ would lead to an unbalanced stack (issue 69).
+
+ Fixed bug that made access to the function in a named function
+ expression impossible in certain situations (issue 24).
+
+ Fixed even more build issues.
+
+ Optimized calling conventions on ARM. The conventions on ARM and
+ IA-32 now match.
+
+ Removed static initializers for flags and counters.
+
+ Improved inline caching behavior for uncommon cases where lazily
+ loading Date and RegExp code could force certain code paths go
+ megamorphic.
+
+ Removed arguments adaption for builtins written in C++. This
+ makes Array.prototype.push and Array.prototype.pop slightly
+ faster.
+
+
+2008-09-11: Version 0.3.1
+
+ Fixed a number of build issues.
+
+ Fixed problem with missing I-cache flusing on ARM.
+
+ Changed space layout in memory management by splitting up
+ code space into old data space and code space.
+
+ Added utf-8 conversion support to the API (issue 57).
+
+ Optimized repeated calls to eval with the same strings. These
+ repeated calls are common in web applications.
+
+ Added Xcode project file.
+
+ Optimized a couple of Array operation.
+
+ Fixed parser bug by checking for end-of-string when parsing break
+ and continue (issue 35).
+
+ Fixed problem where asian characters were not categorized as
+ letters.
+
+ Fixed bug that disallowed calling functions fetched from an array
+ using a string as an array index (issue 32).
+
+ Fixed bug where the internal field count on object templates were
+ sometimes ignored (issue 54).
+
+ Added -f option to the shell sample for compatibility with other
+ engines (issue 18).
+
+ Added source info to TryCatches in the API.
+
+ Fixed problem where the seed for the random number generator was
+ clipped in a double to unsigned int conversion.
+
+ Fixed bug where cons string symbols were sometimes converted to
+ non-symbol flat strings during GC.
+
+ Fixed bug in error reporting when attempting to convert null to an
+ object.
+
+
+2008-09-04: Version 0.3.0
+
+ Added support for running tests on the ARM simulator.
+
+ Fixed bug in the 'in' operator where negative indices were not
+ treated correctly.
+
+ Fixed build issues on gcc-4.3.1.
+
+ Changed Date.prototype.toLocaleTimeString to not print the
+ timezone part of the time.
+
+ Renamed debug.h to v8-debug.h to reduce the risk of name conflicts
+ with user code.
+
+
+2008-09-02: Version 0.2.5
+
+ Renamed the top level directory 'public' to 'include'.
+
+ Added 'env' option to the SCons build scripts to support
+ overriding the ENV part of the build environment. This is mostly
+ to support Windows builds in cases where SCons cannot find the
+ correct paths to the Windows SDK, as these paths cannot be passed
+ through shell environment variables.
+
+ Enabled "Buffer Security Check" on for the Windows SCons build and
+ added the linker option /OPT:ICF as an optimization.
+
+ Added the V8 benchmark suite to the repository.
+
+
+2008-09-01: Version 0.2.4
+
+ Included mjsunit JavaScript test suite and C++ unit tests.
+
+ Changed the shell sample to not print the result of executing a
+ script provided on the command line.
+
+ Fixed issue when building samples on Windows using a shared V8
+ library. Added visibility option on Linux build which makes the
+ generated library 18% smaller.
+
+ Changed build system to accept multiple build modes in one build
+ and generate separate objects, libraries and executables for each
+ mode.
+
+ Removed deferred negation optimization (a * -b => -(a * b)) since
+ this visibly changes operand conversion order.
+
+ Improved parsing performance by introducing stack guard in
+ preparsing. Without a stack guard preparsing always bails out
+ with stack overflow.
+
+ Changed shell sample to take flags directly from the command-line.
+ Added API call that implements this.
+
+ Added load, quit and version functions to the shell sample so it's
+ easier to run benchmarks and tests.
+
+ Fixed issue with building samples and cctests on 64-bit machines.
+
+ Fixed bug in the runtime system where the prototype chain was not
+ always searched for a setter when setting a property that does not
+ exist locally.
+
+
+2008-08-14: Version 0.2.3
+
+ Improved performance of garbage collection by moving the
+ function that updates pointers during compacting collection
+ into the updating visitor. This gives the compiler a better
+ chance to inline and avoid a function call per (potential)
+ pointer.
+
+ Extended the shell sample with a --runtime-flags option.
+
+ Added Visual Studio project files for the shell.cc and
+ process.cc samples.
+
+
+2008-08-13: Version 0.2.2
+
+ Improved performance of garbage collection by changing the way
+ we use the marking stack in the event of stack overflow during
+ full garbage collection and by changing the way we mark roots.
+
+ Cleaned up ARM version by removing top of stack caching and by
+ introducing push/pop elimination.
+
+ Cleaned up the way runtime functions are called to allow
+ runtime calls with no arguments.
+
+ Changed Windows build options to make sure that exceptions are
+ disabled and that optimization flags are enabled.
+
+ Added first version of Visual Studio project files.
+
+
+2008-08-06: Version 0.2.1
+
+ Improved performance of unary addition by avoiding runtime calls.
+
+ Fixed the handling of '>' and '<=' to use right-to-left conversion
+ and left-to-right evaluation as specified by ECMA-262.
+
+ Fixed a branch elimination bug on the ARM platform where incorrect
+ code was generated because of overly aggressive branch
+ elimination.
+
+ Improved performance of code that repeatedly assigns the same
+ function to the same property of different objects with the same
+ map.
+
+ Untangled DEBUG and ENABLE_DISASSEMBLER defines. The disassembler
+ no longer expects DEBUG to be defined.
+
+ Added platform-nullos.cc to serve as the basis for new platform
+ implementations.
+
+
+2008-07-30: Version 0.2.0
+
+ Changed all text files to have native svn:eol-style.
+
+ Added a few samples and support for building them. The samples
+ include a simple shell that can be used to benchmark and test V8.
+
+ Changed V8::GetVersion to return the version as a string.
+
+ Added source for lazily loaded scripts to snapshots and made
+ serialization non-destructive.
+
+ Improved ARM support by fixing the write barrier code to use
+ aligned loads and stores and by removing premature locals
+ optimization that relied on broken support for callee-saved
+ registers (removed).
+
+ Refactored the code for marking live objects during garbage
+ collection and the code for allocating objects in paged
+ spaces. Introduced an abstraction for the map word of a heap-
+ allocated object and changed the memory allocator to allocate
+ executable memory only for spaces that may contain code objects.
+
+ Moved StringBuilder to utils.h and ScopedLock to platform.h, where
+ they can be used by debugging and logging modules. Added
+ thread-safe message queues for dealing with debugger events.
+
+ Fixed the source code reported by toString for certain builtin
+ empty functions and made sure that the prototype property of a
+ function is enumerable.
+
+ Improved performance of converting values to condition flags in
+ generated code.
+
+ Merged disassembler-{arch} files.
+
+
+2008-07-28: Version 0.1.4
+
+ Added support for storing JavaScript stack traces in a stack
+ allocated buffer to make it visible in shallow core dumps.
+ Controlled by the --preallocate-message-memory flag which is
+ disabled by default.
+
+
+2008-07-25: Version 0.1.3
+
+ Fixed bug in JSObject::GetPropertyAttributePostInterceptor where
+ map transitions would count as properties.
+
+ Allowed aliased eval invocations by treating them as evals in the
+ global context. This may change in the future.
+
+ Added support for accessing the last entered context through the
+ API and renamed Context::Current to Context::GetCurrent and
+ Context::GetSecurityContext to Context::GetCurrentSecurityContext.
+
+ Fixed bug in the debugger that would cause the debugger scripts to
+ be recursively loaded and changed all disabling of interrupts to
+ be block-structured.
+
+ Made snapshot data read-only to allow it to be more easily shared
+ across multiple users of V8 when linked as a shared library.
+
+
+2008-07-16: Version 0.1.2
+
+ Fixed building on Mac OS X by recognizing i386 and friends as
+ IA-32 platforms.
+
+ Added propagation of stack overflow exceptions that occur while
+ compiling nested functions.
+
+ Improved debugger with support for recursive break points and
+ handling of exceptions that occur in the debugger JavaScript code.
+
+ Renamed GetInternal to GetInternalField and SetInternal to
+ SetInternalField in the API and moved InternalFieldCount and
+ SetInternalFieldCount from FunctionTemplate to ObjectTemplate.
+
+
+2008-07-09: Version 0.1.1
+
+ Fixed bug in stack overflow check code for IA-32 targets where a
+ non-tagged value in register eax was pushed to the stack.
+
+ Fixed potential quadratic behavior when converting strings to
+ numbers.
+
+ Fixed bug where the return value from Object::SetProperty could
+ end up being the property holder instead of the written value.
+
+ Improved debugger support by allowing nested break points and by
+ dealing with stack-overflows when compiling functions before
+ setting break points in them.
+
+
+2008-07-03: Version 0.1.0
+
+ Initial export.
+
+# Local Variables:
+# mode:text
+# End:
diff --git a/chromium/v8/DEPS b/chromium/v8/DEPS
new file mode 100644
index 00000000000..b91ae4e7667
--- /dev/null
+++ b/chromium/v8/DEPS
@@ -0,0 +1,30 @@
+# Note: The buildbots evaluate this file with CWD set to the parent
+# directory and assume that the root of the checkout is in ./v8/, so
+# all paths in here must match this assumption.
+
+deps = {
+ # Remember to keep the revision in sync with the Makefile.
+ "v8/build/gyp":
+ "http://gyp.googlecode.com/svn/trunk@1685",
+
+ "v8/third_party/icu":
+ "https://src.chromium.org/chrome/trunk/deps/third_party/icu46@214189",
+}
+
+deps_os = {
+ "win": {
+ "v8/third_party/cygwin":
+ "http://src.chromium.org/svn/trunk/deps/third_party/cygwin@66844",
+
+ "v8/third_party/python_26":
+ "http://src.chromium.org/svn/trunk/tools/third_party/python_26@89111",
+ }
+}
+
+hooks = [
+ {
+ # A change to a .gyp, .gypi, or to GYP itself should run the generator.
+ "pattern": ".",
+ "action": ["python", "v8/build/gyp_v8"],
+ },
+]
diff --git a/chromium/v8/LICENSE b/chromium/v8/LICENSE
new file mode 100644
index 00000000000..2e516bab628
--- /dev/null
+++ b/chromium/v8/LICENSE
@@ -0,0 +1,54 @@
+This license applies to all parts of V8 that are not externally
+maintained libraries. The externally maintained libraries used by V8
+are:
+
+ - PCRE test suite, located in
+ test/mjsunit/third_party/regexp-pcre.js. This is based on the
+ test suite from PCRE-7.3, which is copyrighted by the University
+ of Cambridge and Google, Inc. The copyright notice and license
+ are embedded in regexp-pcre.js.
+
+ - Layout tests, located in test/mjsunit/third_party. These are
+ based on layout tests from webkit.org which are copyrighted by
+ Apple Computer, Inc. and released under a 3-clause BSD license.
+
+ - 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-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.
+
+ - Valgrind client API header, located at third_party/valgrind/valgrind.h
+ This is release under the BSD license.
+
+These libraries have their own licenses; we recommend you read them,
+as their terms may differ from the terms below.
+
+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:
+
+ * 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.
diff --git a/chromium/v8/LICENSE.strongtalk b/chromium/v8/LICENSE.strongtalk
new file mode 100644
index 00000000000..9bd62e4f231
--- /dev/null
+++ b/chromium/v8/LICENSE.strongtalk
@@ -0,0 +1,29 @@
+Copyright (c) 1994-2006 Sun Microsystems 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.
+
+- Redistribution 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 Sun Microsystems or the names of 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.
diff --git a/chromium/v8/LICENSE.v8 b/chromium/v8/LICENSE.v8
new file mode 100644
index 00000000000..933718a9ef9
--- /dev/null
+++ b/chromium/v8/LICENSE.v8
@@ -0,0 +1,26 @@
+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.
diff --git a/chromium/v8/LICENSE.valgrind b/chromium/v8/LICENSE.valgrind
new file mode 100644
index 00000000000..fd8ebaf5099
--- /dev/null
+++ b/chromium/v8/LICENSE.valgrind
@@ -0,0 +1,45 @@
+----------------------------------------------------------------
+
+Notice that the following BSD-style license applies to this one
+file (valgrind.h) only. The rest of Valgrind is licensed under the
+terms of the GNU General Public License, version 2, unless
+otherwise indicated. See the COPYING file in the source
+distribution for details.
+
+----------------------------------------------------------------
+
+This file is part of Valgrind, a dynamic binary instrumentation
+framework.
+
+Copyright (C) 2000-2007 Julian Seward. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
diff --git a/chromium/v8/Makefile b/chromium/v8/Makefile
new file mode 100644
index 00000000000..288c257396d
--- /dev/null
+++ b/chromium/v8/Makefile
@@ -0,0 +1,418 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+# Variable default definitions. Override them by exporting them in your shell.
+CXX ?= g++
+LINK ?= g++
+OUTDIR ?= out
+TESTJOBS ?=
+GYPFLAGS ?=
+TESTFLAGS ?=
+ANDROID_NDK_ROOT ?=
+ANDROID_NDK_HOST_ARCH ?=
+ANDROID_TOOLCHAIN ?=
+ANDROID_V8 ?= /data/local/tmp/v8
+NACL_SDK_ROOT ?=
+
+# Special build flags. Use them like this: "make library=shared"
+
+# library=shared || component=shared_library
+ifeq ($(library), shared)
+ GYPFLAGS += -Dcomponent=shared_library
+endif
+ifdef component
+ GYPFLAGS += -Dcomponent=$(component)
+endif
+# console=readline
+ifdef console
+ GYPFLAGS += -Dconsole=$(console)
+endif
+# disassembler=on
+ifeq ($(disassembler), on)
+ GYPFLAGS += -Dv8_enable_disassembler=1
+endif
+# objectprint=on
+ifeq ($(objectprint), on)
+ GYPFLAGS += -Dv8_object_print=1
+endif
+# verifyheap=on
+ifeq ($(verifyheap), on)
+ GYPFLAGS += -Dv8_enable_verify_heap=1
+endif
+# backtrace=off
+ifeq ($(backtrace), off)
+ GYPFLAGS += -Dv8_enable_backtrace=0
+else
+ GYPFLAGS += -Dv8_enable_backtrace=1
+endif
+# snapshot=off
+ifeq ($(snapshot), off)
+ GYPFLAGS += -Dv8_use_snapshot='false'
+endif
+# extrachecks=on/off
+ifeq ($(extrachecks), on)
+ GYPFLAGS += -Dv8_enable_extra_checks=1
+endif
+ifeq ($(extrachecks), off)
+ GYPFLAGS += -Dv8_enable_extra_checks=0
+endif
+# gdbjit=on/off
+ifeq ($(gdbjit), on)
+ GYPFLAGS += -Dv8_enable_gdbjit=1
+endif
+ifeq ($(gdbjit), off)
+ GYPFLAGS += -Dv8_enable_gdbjit=0
+endif
+# vtunejit=on
+ifeq ($(vtunejit), on)
+ GYPFLAGS += -Dv8_enable_vtunejit=1
+endif
+# optdebug=on
+ifeq ($(optdebug), on)
+ GYPFLAGS += -Dv8_optimized_debug=1
+endif
+# debuggersupport=off
+ifeq ($(debuggersupport), off)
+ GYPFLAGS += -Dv8_enable_debugger_support=0
+endif
+# unalignedaccess=on
+ifeq ($(unalignedaccess), on)
+ GYPFLAGS += -Dv8_can_use_unaligned_accesses=true
+endif
+# soname_version=1.2.3
+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
+# regexp=interpreted
+ifeq ($(regexp), interpreted)
+ GYPFLAGS += -Dv8_interpreted_regexp=1
+endif
+# i18nsupport=on
+ifeq ($(i18nsupport), on)
+ GYPFLAGS += -Dv8_enable_i18n_support=1
+endif
+# arm specific flags.
+# armv7=false/true
+ifeq ($(armv7), false)
+ GYPFLAGS += -Darmv7=0
+else
+ifeq ($(armv7), true)
+ GYPFLAGS += -Darmv7=1
+endif
+endif
+# vfp2=off. Deprecated, use armfpu=
+# vfp3=off. Deprecated, use armfpu=
+ifeq ($(vfp3), off)
+ GYPFLAGS += -Darm_fpu=vfp
+endif
+# hardfp=on/off. Deprecated, use armfloatabi
+ifeq ($(hardfp),on)
+ GYPFLAGS += -Darm_float_abi=hard
+else
+ifeq ($(hardfp),off)
+ GYPFLAGS += -Darm_float_abi=softfp
+endif
+endif
+# armneon=on/off
+ifeq ($(armneon), on)
+ GYPFLAGS += -Darm_neon=1
+endif
+# fpu: armfpu=xxx
+# xxx: vfp, vfpv3-d16, vfpv3, neon.
+ifeq ($(armfpu),)
+ifneq ($(vfp3), off)
+ GYPFLAGS += -Darm_fpu=default
+endif
+else
+ GYPFLAGS += -Darm_fpu=$(armfpu)
+endif
+# float abi: armfloatabi=softfp/hard
+ifeq ($(armfloatabi),)
+ifeq ($(hardfp),)
+ GYPFLAGS += -Darm_float_abi=default
+endif
+else
+ GYPFLAGS += -Darm_float_abi=$(armfloatabi)
+endif
+# armthumb=on/off
+ifeq ($(armthumb), off)
+ GYPFLAGS += -Darm_thumb=0
+else
+ifeq ($(armthumb), on)
+ GYPFLAGS += -Darm_thumb=1
+endif
+endif
+# armtest=on
+# With this flag set, by default v8 will only use features implied
+# by the compiler (no probe). This is done by modifying the default
+# values of enable_armv7, enable_vfp2, enable_vfp3 and enable_32dregs.
+# Modifying these flags when launching v8 will enable the probing for
+# the specified values.
+# When using the simulator, this flag is implied.
+ifeq ($(armtest), on)
+ GYPFLAGS += -Darm_test=on
+endif
+
+# ----------------- available targets: --------------------
+# - "dependencies": pulls in external dependencies (currently: GYP)
+# - "grokdump": rebuilds heap constants lists used by grokdump
+# - 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"
+# - "android": cross-compile for Android/ARM
+# - "nacl" : cross-compile for Native Client (ia32 and x64)
+# - 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
+
+# ----------------- internal stuff ------------------------
+
+# Architectures and modes to be compiled. Consider these to be internal
+# variables, don't override them (use the targets instead).
+ARCHES = ia32 x64 arm mipsel
+DEFAULT_ARCHES = ia32 x64 arm
+MODES = release debug
+ANDROID_ARCHES = android_ia32 android_arm android_mipsel
+NACL_ARCHES = nacl_ia32 nacl_x64
+
+# List of files that trigger Makefile regeneration:
+GYPFILES = build/all.gyp build/features.gypi build/standalone.gypi \
+ build/toolchain.gypi preparser/preparser.gyp samples/samples.gyp \
+ src/d8.gyp test/cctest/cctest.gyp tools/gyp/v8.gyp
+
+# If vtunejit=on, the v8vtune.gyp will be appended.
+ifeq ($(vtunejit), on)
+ GYPFILES += src/third_party/vtune/v8vtune.gyp
+endif
+# Generates all combinations of ARCHES and MODES, e.g. "ia32.release".
+BUILDS = $(foreach mode,$(MODES),$(addsuffix .$(mode),$(ARCHES)))
+ANDROID_BUILDS = $(foreach mode,$(MODES), \
+ $(addsuffix .$(mode),$(ANDROID_ARCHES)))
+NACL_BUILDS = $(foreach mode,$(MODES), \
+ $(addsuffix .$(mode),$(NACL_ARCHES)))
+# Generates corresponding test targets, e.g. "ia32.release.check".
+CHECKS = $(addsuffix .check,$(BUILDS))
+ANDROID_CHECKS = $(addsuffix .check,$(ANDROID_BUILDS))
+NACL_CHECKS = $(addsuffix .check,$(NACL_BUILDS))
+# File where previously used GYPFLAGS are stored.
+ENVFILE = $(OUTDIR)/environment
+
+.PHONY: all check clean dependencies $(ENVFILE).new native \
+ $(ARCHES) $(MODES) $(BUILDS) $(CHECKS) $(addsuffix .clean,$(ARCHES)) \
+ $(addsuffix .check,$(MODES)) $(addsuffix .check,$(ARCHES)) \
+ $(ANDROID_ARCHES) $(ANDROID_BUILDS) $(ANDROID_CHECKS) \
+ must-set-ANDROID_NDK_ROOT_OR_TOOLCHAIN \
+ $(NACL_ARCHES) $(NACL_BUILDS) $(NACL_CHECKS) \
+ must-set-NACL_SDK_ROOT
+
+# Target definitions. "all" is the default.
+all: $(MODES)
+
+# Special target for the buildbots to use. Depends on $(OUTDIR)/Makefile
+# having been created before.
+buildbot:
+ $(MAKE) -C "$(OUTDIR)" BUILDTYPE=$(BUILDTYPE) \
+ builddir="$(abspath $(OUTDIR))/$(BUILDTYPE)"
+
+mips mips.release mips.debug:
+ @echo "V8 does not support big-endian MIPS builds at the moment," \
+ "please use little-endian builds (mipsel)."
+
+# Compile targets. MODES and ARCHES are convenience targets.
+.SECONDEXPANSION:
+$(MODES): $(addsuffix .$$@,$(DEFAULT_ARCHES))
+
+$(ARCHES): $(addprefix $$@.,$(MODES))
+
+# Defines how to build a particular target (e.g. ia32.release).
+$(BUILDS): $(OUTDIR)/Makefile.$$(basename $$@)
+ @$(MAKE) -C "$(OUTDIR)" -f Makefile.$(basename $@) \
+ CXX="$(CXX)" LINK="$(LINK)" \
+ BUILDTYPE=$(shell echo $(subst .,,$(suffix $@)) | \
+ 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)/$@"
+
+$(ANDROID_ARCHES): $(addprefix $$@.,$(MODES))
+
+$(ANDROID_BUILDS): $(GYPFILES) $(ENVFILE) build/android.gypi \
+ must-set-ANDROID_NDK_ROOT_OR_TOOLCHAIN Makefile.android
+ @$(MAKE) -f Makefile.android $@ \
+ ARCH="$(basename $@)" \
+ MODE="$(subst .,,$(suffix $@))" \
+ OUTDIR="$(OUTDIR)" \
+ GYPFLAGS="$(GYPFLAGS)"
+
+$(NACL_ARCHES): $(addprefix $$@.,$(MODES))
+
+$(NACL_BUILDS): $(GYPFILES) $(ENVFILE) \
+ Makefile.nacl must-set-NACL_SDK_ROOT
+ @$(MAKE) -f Makefile.nacl $@ \
+ ARCH="$(basename $@)" \
+ MODE="$(subst .,,$(suffix $@))" \
+ OUTDIR="$(OUTDIR)" \
+ GYPFLAGS="$(GYPFLAGS)"
+
+# Test targets.
+check: all
+ @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \
+ --arch=$(shell echo $(DEFAULT_ARCHES) | sed -e 's/ /,/g') \
+ $(TESTFLAGS)
+
+$(addsuffix .check,$(MODES)): $$(basename $$@)
+ @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \
+ --mode=$(basename $@) $(TESTFLAGS)
+
+$(addsuffix .check,$(ARCHES)): $$(basename $$@)
+ @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \
+ --arch=$(basename $@) $(TESTFLAGS)
+
+$(CHECKS): $$(basename $$@)
+ @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \
+ --arch-and-mode=$(basename $@) $(TESTFLAGS)
+
+$(addsuffix .sync, $(ANDROID_BUILDS)): $$(basename $$@)
+ @tools/android-sync.sh $(basename $@) $(OUTDIR) \
+ $(shell pwd) $(ANDROID_V8)
+
+$(addsuffix .check, $(ANDROID_BUILDS)): $$(basename $$@).sync
+ @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \
+ --arch-and-mode=$(basename $@) \
+ --timeout=600 \
+ --command-prefix="tools/android-run.py"
+
+$(addsuffix .check, $(ANDROID_ARCHES)): \
+ $(addprefix $$(basename $$@).,$(MODES)).check
+
+$(addsuffix .check, $(NACL_BUILDS)): $$(basename $$@)
+ @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \
+ --arch-and-mode=$(basename $@) \
+ --timeout=600 --nopresubmit \
+ --command-prefix="tools/nacl-run.py"
+
+$(addsuffix .check, $(NACL_ARCHES)): \
+ $(addprefix $$(basename $$@).,$(MODES)).check
+
+native.check: native
+ @tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR)/native \
+ --arch-and-mode=. $(TESTFLAGS)
+
+# Clean targets. You can clean each architecture individually, or everything.
+$(addsuffix .clean, $(ARCHES) $(ANDROID_ARCHES) $(NACL_ARCHES)):
+ rm -f $(OUTDIR)/Makefile.$(basename $@)
+ rm -rf $(OUTDIR)/$(basename $@).release
+ rm -rf $(OUTDIR)/$(basename $@).debug
+ find $(OUTDIR) -regex '.*\(host\|target\).$(basename $@)\.mk' -delete
+
+native.clean:
+ rm -f $(OUTDIR)/Makefile.native
+ rm -rf $(OUTDIR)/native
+ find $(OUTDIR) -regex '.*\(host\|target\).native\.mk' -delete
+
+clean: $(addsuffix .clean, $(ARCHES) $(ANDROID_ARCHES) $(NACL_ARCHES)) native.clean
+
+# GYP file generation targets.
+OUT_MAKEFILES = $(addprefix $(OUTDIR)/Makefile.,$(ARCHES))
+$(OUT_MAKEFILES): $(GYPFILES) $(ENVFILE)
+ PYTHONPATH="$(shell pwd)/tools/generate_shim_headers:$(PYTHONPATH)" \
+ GYP_GENERATORS=make \
+ build/gyp/gyp --generator-output="$(OUTDIR)" build/all.gyp \
+ -Ibuild/standalone.gypi --depth=. \
+ -Dv8_target_arch=$(subst .,,$(suffix $@)) \
+ -S.$(subst .,,$(suffix $@)) $(GYPFLAGS)
+
+$(OUTDIR)/Makefile.native: $(GYPFILES) $(ENVFILE)
+ PYTHONPATH="$(shell pwd)/tools/generate_shim_headers:$(PYTHONPATH)" \
+ GYP_GENERATORS=make \
+ build/gyp/gyp --generator-output="$(OUTDIR)" build/all.gyp \
+ -Ibuild/standalone.gypi --depth=. -S.native $(GYPFLAGS)
+
+must-set-ANDROID_NDK_ROOT_OR_TOOLCHAIN:
+ifndef ANDROID_NDK_ROOT
+ifndef ANDROID_TOOLCHAIN
+ $(error ANDROID_NDK_ROOT or ANDROID_TOOLCHAIN must be set))
+endif
+endif
+
+# Note that NACL_SDK_ROOT must be set to point to an appropriate
+# Native Client SDK before using this makefile. You can download
+# an SDK here:
+# https://developers.google.com/native-client/sdk/download
+# The path indicated by NACL_SDK_ROOT will typically end with
+# a folder for a pepper version such as "pepper_25" that should
+# have "tools" and "toolchain" subdirectories.
+must-set-NACL_SDK_ROOT:
+ifndef NACL_SDK_ROOT
+ $(error NACL_SDK_ROOT must be set)
+endif
+
+# Replaces the old with the new environment file if they're different, which
+# will trigger GYP to regenerate Makefiles.
+$(ENVFILE): $(ENVFILE).new
+ @if test -r $(ENVFILE) && cmp $(ENVFILE).new $(ENVFILE) > /dev/null; \
+ then rm $(ENVFILE).new; \
+ else mv $(ENVFILE).new $(ENVFILE); fi
+
+# Stores current GYPFLAGS in a file.
+$(ENVFILE).new:
+ @mkdir -p $(OUTDIR); echo "GYPFLAGS=$(GYPFLAGS)" > $(ENVFILE).new; \
+ echo "CXX=$(CXX)" >> $(ENVFILE).new
+
+# Heap constants for grokdump.
+DUMP_FILE = tools/v8heapconst.py
+grokdump: ia32.release
+ @cat $(DUMP_FILE).tmpl > $(DUMP_FILE)
+ @$(OUTDIR)/ia32.release/d8 --dump-heap-constants >> $(DUMP_FILE)
+
+# Dependencies.
+# Remember to keep these in sync with the DEPS file.
+dependencies:
+ svn checkout --force http://gyp.googlecode.com/svn/trunk build/gyp \
+ --revision 1685
+ svn checkout --force \
+ https://src.chromium.org/chrome/trunk/deps/third_party/icu46 \
+ third_party/icu --revision 214189
diff --git a/chromium/v8/Makefile.android b/chromium/v8/Makefile.android
new file mode 100644
index 00000000000..2d45d3bb122
--- /dev/null
+++ b/chromium/v8/Makefile.android
@@ -0,0 +1,102 @@
+# Copyright 2012 the V8 project authors. 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.
+
+# Those definitions should be consistent with the main Makefile
+ANDROID_ARCHES = android_ia32 android_arm android_mipsel
+MODES = release debug
+
+# Generates all combinations of ANDROID ARCHES and MODES,
+# e.g. "android_ia32.release" or "android_arm.release"
+ANDROID_BUILDS = $(foreach mode,$(MODES), \
+ $(addsuffix .$(mode),$(ANDROID_ARCHES)))
+
+HOST_OS = $(shell uname -s | sed -e 's/Linux/linux/;s/Darwin/mac/')
+ANDROID_NDK_HOST_ARCH ?= $(shell uname -m | sed -e 's/i[3456]86/x86/')
+ifeq ($(HOST_OS), linux)
+ TOOLCHAIN_DIR = linux-$(ANDROID_NDK_HOST_ARCH)
+else
+ ifeq ($(HOST_OS), mac)
+ TOOLCHAIN_DIR = darwin-$(ANDROID_NDK_HOST_ARCH)
+ else
+ $(error Host platform "${HOST_OS}" is not supported)
+ endif
+endif
+
+ifeq ($(ARCH), android_arm)
+ DEFINES = target_arch=arm v8_target_arch=arm android_target_arch=arm
+ DEFINES += arm_neon=0 armv7=1
+ TOOLCHAIN_ARCH = arm-linux-androideabi-4.6
+else
+ ifeq ($(ARCH), android_mipsel)
+ DEFINES = target_arch=mipsel v8_target_arch=mipsel android_target_arch=mips
+ DEFINES += mips_arch_variant=mips32r2
+ TOOLCHAIN_ARCH = mipsel-linux-android-4.6
+ else
+ ifeq ($(ARCH), android_ia32)
+ DEFINES = target_arch=ia32 v8_target_arch=ia32 android_target_arch=x86
+ TOOLCHAIN_ARCH = x86-4.6
+ else
+ $(error Target architecture "${ARCH}" is not supported)
+ endif
+ endif
+endif
+
+TOOLCHAIN_PATH = ${ANDROID_NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}/prebuilt
+ANDROID_TOOLCHAIN ?= ${TOOLCHAIN_PATH}/${TOOLCHAIN_DIR}
+ifeq ($(wildcard $(ANDROID_TOOLCHAIN)),)
+ $(error Cannot find Android toolchain in "${ANDROID_TOOLCHAIN}". Please \
+ check that ANDROID_NDK_ROOT and ANDROID_NDK_HOST_ARCH are set \
+ correctly)
+endif
+
+# For mksnapshot host generation.
+DEFINES += host_os=${HOST_OS}
+
+.SECONDEXPANSION:
+$(ANDROID_BUILDS): $(OUTDIR)/Makefile.$$(basename $$@)
+ @$(MAKE) -C "$(OUTDIR)" -f Makefile.$(basename $@) \
+ CXX="$(ANDROID_TOOLCHAIN)/bin/*-g++" \
+ AR="$(ANDROID_TOOLCHAIN)/bin/*-ar" \
+ RANLIB="$(ANDROID_TOOLCHAIN)/bin/*-ranlib" \
+ CC="$(ANDROID_TOOLCHAIN)/bin/*-gcc" \
+ LD="$(ANDROID_TOOLCHAIN)/bin/*-ld" \
+ LINK="$(ANDROID_TOOLCHAIN)/bin/*-g++" \
+ BUILDTYPE=$(shell echo $(subst .,,$(suffix $@)) | \
+ python -c "print raw_input().capitalize()") \
+ builddir="$(shell pwd)/$(OUTDIR)/$@"
+
+# Android GYP file generation targets.
+ANDROID_MAKEFILES = $(addprefix $(OUTDIR)/Makefile.,$(ANDROID_ARCHES))
+$(ANDROID_MAKEFILES):
+ @GYP_GENERATORS=make-android \
+ GYP_DEFINES="${DEFINES}" \
+ CC="${ANDROID_TOOLCHAIN}/bin/*-gcc" \
+ CXX="${ANDROID_TOOLCHAIN}/bin/*-g++" \
+ PYTHONPATH="$(shell pwd)/tools/generate_shim_headers:$(PYTHONPATH)" \
+ build/gyp/gyp --generator-output="${OUTDIR}" build/all.gyp \
+ -Ibuild/standalone.gypi --depth=. -Ibuild/android.gypi \
+ -S.${ARCH} ${GYPFLAGS}
diff --git a/chromium/v8/Makefile.nacl b/chromium/v8/Makefile.nacl
new file mode 100644
index 00000000000..02e83ef2bca
--- /dev/null
+++ b/chromium/v8/Makefile.nacl
@@ -0,0 +1,98 @@
+#
+# Copyright 2013 the V8 project authors. 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.
+
+# Those definitions should be consistent with the main Makefile
+NACL_ARCHES = nacl_ia32 nacl_x64
+MODES = release debug
+
+# Generates all combinations of NACL ARCHES and MODES,
+# e.g. "nacl_ia32.release" or "nacl_x64.release"
+NACL_BUILDS = $(foreach mode,$(MODES), \
+ $(addsuffix .$(mode),$(NACL_ARCHES)))
+
+HOST_OS = $(shell uname -s | sed -e 's/Linux/linux/;s/Darwin/mac/')
+ifeq ($(HOST_OS), linux)
+ TOOLCHAIN_DIR = linux_x86_glibc
+else
+ ifeq ($(HOST_OS), mac)
+ TOOLCHAIN_DIR = mac_x86_glibc
+ else
+ $(error Host platform "${HOST_OS}" is not supported)
+ endif
+endif
+
+TOOLCHAIN_PATH = $(realpath ${NACL_SDK_ROOT}/toolchain)
+NACL_TOOLCHAIN ?= ${TOOLCHAIN_PATH}/${TOOLCHAIN_DIR}
+
+ifeq ($(ARCH), nacl_ia32)
+ GYPENV = nacl_target_arch=nacl_ia32 v8_target_arch=arm v8_host_arch=ia32
+ TOOLCHAIN_ARCH = x86-4.4
+ NACL_CC = "$(NACL_TOOLCHAIN)/bin/i686-nacl-gcc"
+ NACL_CXX = "$(NACL_TOOLCHAIN)/bin/i686-nacl-g++"
+ NACL_LINK = "$(NACL_TOOLCHAIN)/bin/i686-nacl-g++"
+else
+ ifeq ($(ARCH), nacl_x64)
+ GYPENV = nacl_target_arch=nacl_x64 v8_target_arch=arm v8_host_arch=ia32
+ TOOLCHAIN_ARCH = x86-4.4
+ NACL_CC = "$(NACL_TOOLCHAIN)/bin/x86_64-nacl-gcc"
+ NACL_CXX = "$(NACL_TOOLCHAIN)/bin/x86_64-nacl-g++"
+ NACL_LINK = "$(NACL_TOOLCHAIN)/bin/x86_64-nacl-g++"
+ else
+ $(error Target architecture "${ARCH}" is not supported)
+ endif
+endif
+
+ifeq ($(wildcard $(NACL_TOOLCHAIN)),)
+ $(error Cannot find Native Client toolchain in "${NACL_TOOLCHAIN}")
+endif
+
+# For mksnapshot host generation.
+GYPENV += host_os=${HOST_OS}
+
+NACL_MAKEFILES = $(addprefix $(OUTDIR)/Makefile.,$(NACL_ARCHES))
+.SECONDEXPANSION:
+# For some reason the $$(basename $$@) expansion didn't work here...
+$(NACL_BUILDS): $(NACL_MAKEFILES)
+ @$(MAKE) -C "$(OUTDIR)" -f Makefile.$(basename $@) \
+ CXX=${NACL_CXX} \
+ LINK=${NACL_LINK} \
+ BUILDTYPE=$(shell echo $(subst .,,$(suffix $@)) | \
+ python -c "print raw_input().capitalize()") \
+ builddir="$(shell pwd)/$(OUTDIR)/$@"
+
+# NACL GYP file generation targets.
+$(NACL_MAKEFILES):
+ @GYP_GENERATORS=make \
+ GYP_DEFINES="${GYPENV}" \
+ CC=${NACL_CC} \
+ CXX=${NACL_CXX} \
+ PYTHONPATH="$(shell pwd)/tools/generate_shim_headers:$(PYTHONPATH)" \
+ build/gyp/gyp --generator-output="${OUTDIR}" build/all.gyp \
+ -Ibuild/standalone.gypi --depth=. \
+ -S.$(subst .,,$(suffix $@)) $(GYPFLAGS) \
+ -Dwno_array_bounds=-Wno-array-bounds
diff --git a/chromium/v8/OWNERS b/chromium/v8/OWNERS
new file mode 100644
index 00000000000..6fe40e21e33
--- /dev/null
+++ b/chromium/v8/OWNERS
@@ -0,0 +1,14 @@
+bmeurer@chromium.org
+danno@chromium.org
+dslomov@chromium.org
+hpayer@chromium.org
+jkummerow@chromium.org
+mmassi@chromium.org
+mstarzinger@chromium.org
+mvstanton@chromium.org
+rossberg@chromium.org
+svenpanne@chromium.org
+ulan@chromium.org
+vegorov@chromium.org
+verwaest@chromium.org
+yangguo@chromium.org
diff --git a/chromium/v8/PRESUBMIT.py b/chromium/v8/PRESUBMIT.py
new file mode 100644
index 00000000000..819331f9e5b
--- /dev/null
+++ b/chromium/v8/PRESUBMIT.py
@@ -0,0 +1,75 @@
+# Copyright 2012 the V8 project authors. 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.
+
+"""Top-level presubmit script for V8.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into gcl.
+"""
+
+def _V8PresubmitChecks(input_api, output_api):
+ """Runs the V8 presubmit checks."""
+ import sys
+ sys.path.append(input_api.os_path.join(
+ input_api.PresubmitLocalPath(), 'tools'))
+ from presubmit import CppLintProcessor
+ from presubmit import SourceProcessor
+
+ results = []
+ if not CppLintProcessor().Run(input_api.PresubmitLocalPath()):
+ results.append(output_api.PresubmitError("C++ lint check failed"))
+ if not SourceProcessor().Run(input_api.PresubmitLocalPath()):
+ results.append(output_api.PresubmitError(
+ "Copyright header, trailing whitespaces and two empty lines " \
+ "between declarations check failed"))
+ return results
+
+
+def _CommonChecks(input_api, output_api):
+ """Checks common to both upload and commit."""
+ results = []
+ results.extend(input_api.canned_checks.CheckOwners(
+ input_api, output_api, source_file_filter=None))
+ results.extend(_V8PresubmitChecks(input_api, output_api))
+ return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ results = []
+ results.extend(_CommonChecks(input_api, output_api))
+ return results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ results = []
+ results.extend(_CommonChecks(input_api, output_api))
+ results.extend(input_api.canned_checks.CheckChangeHasDescription(
+ input_api, output_api))
+ results.extend(input_api.canned_checks.CheckTreeIsOpen(
+ input_api, output_api,
+ json_url='http://v8-status.appspot.com/current?format=json'))
+ return results
diff --git a/chromium/v8/benchmarks/README.txt b/chromium/v8/benchmarks/README.txt
new file mode 100644
index 00000000000..59f76ffc81d
--- /dev/null
+++ b/chromium/v8/benchmarks/README.txt
@@ -0,0 +1,86 @@
+V8 Benchmark Suite
+==================
+
+This is the V8 benchmark suite: A collection of pure JavaScript
+benchmarks that we have used to tune V8. The licenses for the
+individual benchmarks are included in the JavaScript files.
+
+In addition to the benchmarks, the suite consists of the benchmark
+framework (base.js), which must be loaded before any of the individual
+benchmark files, and two benchmark runners: An HTML version (run.html)
+and a standalone JavaScript version (run.js).
+
+
+Changes From Version 1 To Version 2
+===================================
+
+For version 2 the crypto benchmark was fixed. Previously, the
+decryption stage was given plaintext as input, which resulted in an
+error. Now, the decryption stage is given the output of the
+encryption stage as input. The result is checked against the original
+plaintext. For this to give the correct results the crypto objects
+are reset for each iteration of the benchmark. In addition, the size
+of the plain text has been increased a little and the use of
+Math.random() and new Date() to build an RNG pool has been removed.
+
+Other benchmarks were fixed to do elementary verification of the
+results of their calculations. This is to avoid accidentally
+obtaining scores that are the result of an incorrect JavaScript engine
+optimization.
+
+
+Changes From Version 2 To Version 3
+===================================
+
+Version 3 adds a new benchmark, RegExp. The RegExp benchmark is
+generated by loading 50 of the most popular pages on the web and
+logging all regexp operations performed. Each operation is given a
+weight that is calculated from an estimate of the popularity of the
+pages where it occurs and the number of times it is executed while
+loading each page. Finally the literal letters in the data are
+encoded using ROT13 in a way that does not affect how the regexps
+match their input.
+
+
+Changes from Version 3 to Version 4
+===================================
+
+The Splay benchmark is a newcomer in version 4. It manipulates a
+splay tree by adding and removing data nodes, thus exercising the
+memory management subsystem of the JavaScript engine.
+
+Furthermore, all the unused parts of the Prototype library were
+removed from the RayTrace benchmark. This does not affect the running
+of the benchmark.
+
+
+Changes from Version 4 to Version 5
+===================================
+
+Removed duplicate line in random seed code, and changed the name of
+the Object.prototype.inherits function in the DeltaBlue benchmark to
+inheritsFrom to avoid name clashes when running in Chromium with
+extensions enabled.
+
+
+Changes from Version 5 to Version 6
+===================================
+
+Removed dead code from the RayTrace benchmark and fixed a couple of
+typos in the DeltaBlue implementation. Changed the Splay benchmark to
+avoid converting the same numeric key to a string over and over again
+and to avoid inserting and removing the same element repeatedly thus
+increasing pressure on the memory subsystem. Changed the RegExp
+benchmark to exercise the regular expression engine on different
+input strings.
+
+Furthermore, the benchmark runner was changed to run the benchmarks
+for at least a few times to stabilize the reported numbers on slower
+machines.
+
+
+Changes from Version 6 to Version 7
+===================================
+
+Added the Navier-Stokes benchmark, a 2D differential equation solver
+that stresses arithmetic computations on double arrays.
diff --git a/chromium/v8/benchmarks/base.js b/chromium/v8/benchmarks/base.js
new file mode 100644
index 00000000000..62c37e1208d
--- /dev/null
+++ b/chromium/v8/benchmarks/base.js
@@ -0,0 +1,284 @@
+// Copyright 2012 the V8 project authors. 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.
+
+
+// Simple framework for running the benchmark suites and
+// computing a score based on the timing measurements.
+
+
+// A benchmark has a name (string) and a function that will be run to
+// do the performance measurement. The optional setup and tearDown
+// arguments are functions that will be invoked before and after
+// running the benchmark, but the running time of these functions will
+// not be accounted for in the benchmark score.
+function Benchmark(name, run, setup, tearDown) {
+ this.name = name;
+ this.run = run;
+ this.Setup = setup ? setup : function() { };
+ this.TearDown = tearDown ? tearDown : function() { };
+}
+
+
+// Benchmark results hold the benchmark and the measured time used to
+// run the benchmark. The benchmark score is computed later once a
+// full benchmark suite has run to completion.
+function BenchmarkResult(benchmark, time) {
+ this.benchmark = benchmark;
+ this.time = time;
+}
+
+
+// Automatically convert results to numbers. Used by the geometric
+// mean computation.
+BenchmarkResult.prototype.valueOf = function() {
+ return this.time;
+}
+
+
+// Suites of benchmarks consist of a name and the set of benchmarks in
+// addition to the reference timing that the final score will be based
+// on. This way, all scores are relative to a reference run and higher
+// scores implies better performance.
+function BenchmarkSuite(name, reference, benchmarks) {
+ this.name = name;
+ this.reference = reference;
+ this.benchmarks = benchmarks;
+ BenchmarkSuite.suites.push(this);
+}
+
+
+// Keep track of all declared benchmark suites.
+BenchmarkSuite.suites = [];
+
+
+// Scores are not comparable across versions. Bump the version if
+// you're making changes that will affect that scores, e.g. if you add
+// a new benchmark or change an existing one.
+BenchmarkSuite.version = '7';
+
+
+// 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;
+ };
+})();
+
+
+// Runs all registered benchmark suites and optionally yields between
+// each individual benchmark to avoid running for too long in the
+// context of browsers. Once done, the final score is reported to the
+// runner.
+BenchmarkSuite.RunSuites = function(runner) {
+ var continuation = null;
+ var suites = BenchmarkSuite.suites;
+ var length = suites.length;
+ BenchmarkSuite.scores = [];
+ var index = 0;
+ function RunStep() {
+ while (continuation || index < length) {
+ if (continuation) {
+ continuation = continuation();
+ } else {
+ var suite = suites[index++];
+ if (runner.NotifyStart) runner.NotifyStart(suite.name);
+ continuation = suite.RunStep(runner);
+ }
+ if (continuation && typeof window != 'undefined' && window.setTimeout) {
+ window.setTimeout(RunStep, 25);
+ return;
+ }
+ }
+ if (runner.NotifyScore) {
+ var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);
+ var formatted = BenchmarkSuite.FormatScore(100 * score);
+ runner.NotifyScore(formatted);
+ }
+ }
+ RunStep();
+}
+
+
+// Counts the total number of registered benchmarks. Useful for
+// showing progress as a percentage.
+BenchmarkSuite.CountBenchmarks = function() {
+ var result = 0;
+ var suites = BenchmarkSuite.suites;
+ for (var i = 0; i < suites.length; i++) {
+ result += suites[i].benchmarks.length;
+ }
+ return result;
+}
+
+
+// Computes the geometric mean of a set of numbers.
+BenchmarkSuite.GeometricMean = function(numbers) {
+ var log = 0;
+ for (var i = 0; i < numbers.length; i++) {
+ log += Math.log(numbers[i]);
+ }
+ return Math.pow(Math.E, log / numbers.length);
+}
+
+
+// Converts a score value to a string with at least three significant
+// digits.
+BenchmarkSuite.FormatScore = function(value) {
+ if (value > 100) {
+ return value.toFixed(0);
+ } else {
+ return value.toPrecision(3);
+ }
+}
+
+// Notifies the runner that we're done running a single benchmark in
+// the benchmark suite. This can be useful to report progress.
+BenchmarkSuite.prototype.NotifyStep = function(result) {
+ this.results.push(result);
+ if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);
+}
+
+
+// Notifies the runner that we're done with running a suite and that
+// we have a result which can be reported to the user if needed.
+BenchmarkSuite.prototype.NotifyResult = function() {
+ var mean = BenchmarkSuite.GeometricMean(this.results);
+ var score = this.reference / mean;
+ BenchmarkSuite.scores.push(score);
+ if (this.runner.NotifyResult) {
+ var formatted = BenchmarkSuite.FormatScore(100 * score);
+ this.runner.NotifyResult(this.name, formatted);
+ }
+}
+
+
+// Notifies the runner that running a benchmark resulted in an error.
+BenchmarkSuite.prototype.NotifyError = function(error) {
+ if (this.runner.NotifyError) {
+ this.runner.NotifyError(this.name, error);
+ }
+ if (this.runner.NotifyStep) {
+ this.runner.NotifyStep(this.name);
+ }
+}
+
+
+// Runs a single benchmark for at least a second and computes the
+// average time it takes to run a single iteration.
+BenchmarkSuite.prototype.RunSingleBenchmark = function(benchmark, data) {
+ function Measure(data) {
+ var elapsed = 0;
+ var start = new Date();
+ for (var n = 0; elapsed < 1000; n++) {
+ benchmark.run();
+ elapsed = new Date() - start;
+ }
+ if (data != null) {
+ data.runs += n;
+ data.elapsed += elapsed;
+ }
+ }
+
+ if (data == null) {
+ // Measure the benchmark once for warm up and throw the result
+ // away. Return a fresh data object.
+ Measure(null);
+ return { runs: 0, elapsed: 0 };
+ } else {
+ Measure(data);
+ // If we've run too few iterations, we continue for another second.
+ if (data.runs < 32) return data;
+ var usec = (data.elapsed * 1000) / data.runs;
+ this.NotifyStep(new BenchmarkResult(benchmark, usec));
+ return null;
+ }
+}
+
+
+// This function starts running a suite, but stops between each
+// individual benchmark in the suite and returns a continuation
+// function which can be invoked to run the next benchmark. Once the
+// last benchmark has been executed, null is returned.
+BenchmarkSuite.prototype.RunStep = function(runner) {
+ this.results = [];
+ this.runner = runner;
+ var length = this.benchmarks.length;
+ var index = 0;
+ var suite = this;
+ var data;
+
+ // Run the setup, the actual benchmark, and the tear down in three
+ // separate steps to allow the framework to yield between any of the
+ // steps.
+
+ function RunNextSetup() {
+ if (index < length) {
+ try {
+ suite.benchmarks[index].Setup();
+ } catch (e) {
+ suite.NotifyError(e);
+ return null;
+ }
+ return RunNextBenchmark;
+ }
+ suite.NotifyResult();
+ return null;
+ }
+
+ function RunNextBenchmark() {
+ try {
+ data = suite.RunSingleBenchmark(suite.benchmarks[index], data);
+ } catch (e) {
+ suite.NotifyError(e);
+ return null;
+ }
+ // If data is null, we're done with this benchmark.
+ return (data == null) ? RunNextTearDown : RunNextBenchmark();
+ }
+
+ function RunNextTearDown() {
+ try {
+ suite.benchmarks[index++].TearDown();
+ } catch (e) {
+ suite.NotifyError(e);
+ return null;
+ }
+ return RunNextSetup;
+ }
+
+ // Start out running the setup.
+ return RunNextSetup();
+}
diff --git a/chromium/v8/benchmarks/crypto.js b/chromium/v8/benchmarks/crypto.js
new file mode 100644
index 00000000000..531ad456e02
--- /dev/null
+++ b/chromium/v8/benchmarks/crypto.js
@@ -0,0 +1,1698 @@
+/*
+ * Copyright (c) 2003-2005 Tom Wu
+ * 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" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
+ * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * In addition, the following condition applies:
+ *
+ * All redistributions must retain an intact copy of this copyright notice
+ * and disclaimer.
+ */
+
+
+// The code has been adapted for use as a benchmark by Google.
+var Crypto = new BenchmarkSuite('Crypto', 266181, [
+ new Benchmark("Encrypt", encrypt),
+ new Benchmark("Decrypt", decrypt)
+]);
+
+
+// Basic JavaScript BN library - subset useful for RSA encryption.
+
+// Bits per digit
+var dbits;
+var BI_DB;
+var BI_DM;
+var BI_DV;
+
+var BI_FP;
+var BI_FV;
+var BI_F1;
+var BI_F2;
+
+// JavaScript engine analysis
+var canary = 0xdeadbeefcafe;
+var j_lm = ((canary&0xffffff)==0xefcafe);
+
+// (public) Constructor
+function BigInteger(a,b,c) {
+ this.array = new Array();
+ if(a != null)
+ if("number" == typeof a) this.fromNumber(a,b,c);
+ else if(b == null && "string" != typeof a) this.fromString(a,256);
+ else this.fromString(a,b);
+}
+
+// return new, unset BigInteger
+function nbi() { return new BigInteger(null); }
+
+// am: Compute w_j += (x*this_i), propagate carries,
+// c is initial carry, returns final carry.
+// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
+// We need to select the fastest one that works in this environment.
+
+// am1: use a single mult and divide to get the high bits,
+// max digit bits should be 26 because
+// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
+function am1(i,x,w,j,c,n) {
+ var this_array = this.array;
+ var w_array = w.array;
+ while(--n >= 0) {
+ var v = x*this_array[i++]+w_array[j]+c;
+ c = Math.floor(v/0x4000000);
+ w_array[j++] = v&0x3ffffff;
+ }
+ return c;
+}
+
+// am2 avoids a big mult-and-extract completely.
+// Max digit bits should be <= 30 because we do bitwise ops
+// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
+function am2(i,x,w,j,c,n) {
+ var this_array = this.array;
+ var w_array = w.array;
+ var xl = x&0x7fff, xh = x>>15;
+ while(--n >= 0) {
+ var l = this_array[i]&0x7fff;
+ var h = this_array[i++]>>15;
+ var m = xh*l+h*xl;
+ l = xl*l+((m&0x7fff)<<15)+w_array[j]+(c&0x3fffffff);
+ c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
+ w_array[j++] = l&0x3fffffff;
+ }
+ return c;
+}
+
+// Alternately, set max digit bits to 28 since some
+// browsers slow down when dealing with 32-bit numbers.
+function am3(i,x,w,j,c,n) {
+ var this_array = this.array;
+ var w_array = w.array;
+
+ var xl = x&0x3fff, xh = x>>14;
+ while(--n >= 0) {
+ var l = this_array[i]&0x3fff;
+ var h = this_array[i++]>>14;
+ var m = xh*l+h*xl;
+ l = xl*l+((m&0x3fff)<<14)+w_array[j]+c;
+ c = (l>>28)+(m>>14)+xh*h;
+ w_array[j++] = l&0xfffffff;
+ }
+ return c;
+}
+
+// This is tailored to VMs with 2-bit tagging. It makes sure
+// that all the computations stay within the 29 bits available.
+function am4(i,x,w,j,c,n) {
+ var this_array = this.array;
+ var w_array = w.array;
+
+ var xl = x&0x1fff, xh = x>>13;
+ while(--n >= 0) {
+ var l = this_array[i]&0x1fff;
+ var h = this_array[i++]>>13;
+ var m = xh*l+h*xl;
+ l = xl*l+((m&0x1fff)<<13)+w_array[j]+c;
+ c = (l>>26)+(m>>13)+xh*h;
+ w_array[j++] = l&0x3ffffff;
+ }
+ return c;
+}
+
+// am3/28 is best for SM, Rhino, but am4/26 is best for v8.
+// Kestrel (Opera 9.5) gets its best result with am4/26.
+// IE7 does 9% better with am3/28 than with am4/26.
+// Firefox (SM) gets 10% faster with am3/28 than with am4/26.
+
+setupEngine = function(fn, bits) {
+ BigInteger.prototype.am = fn;
+ dbits = bits;
+
+ BI_DB = dbits;
+ BI_DM = ((1<<dbits)-1);
+ BI_DV = (1<<dbits);
+
+ BI_FP = 52;
+ BI_FV = Math.pow(2,BI_FP);
+ BI_F1 = BI_FP-dbits;
+ BI_F2 = 2*dbits-BI_FP;
+}
+
+
+// Digit conversions
+var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
+var BI_RC = new Array();
+var rr,vv;
+rr = "0".charCodeAt(0);
+for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
+rr = "a".charCodeAt(0);
+for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
+rr = "A".charCodeAt(0);
+for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
+
+function int2char(n) { return BI_RM.charAt(n); }
+function intAt(s,i) {
+ var c = BI_RC[s.charCodeAt(i)];
+ return (c==null)?-1:c;
+}
+
+// (protected) copy this to r
+function bnpCopyTo(r) {
+ var this_array = this.array;
+ var r_array = r.array;
+
+ for(var i = this.t-1; i >= 0; --i) r_array[i] = this_array[i];
+ r.t = this.t;
+ r.s = this.s;
+}
+
+// (protected) set from integer value x, -DV <= x < DV
+function bnpFromInt(x) {
+ var this_array = this.array;
+ this.t = 1;
+ this.s = (x<0)?-1:0;
+ if(x > 0) this_array[0] = x;
+ else if(x < -1) this_array[0] = x+DV;
+ else this.t = 0;
+}
+
+// return bigint initialized to value
+function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
+
+// (protected) set from string and radix
+function bnpFromString(s,b) {
+ var this_array = this.array;
+ var k;
+ if(b == 16) k = 4;
+ else if(b == 8) k = 3;
+ else if(b == 256) k = 8; // byte array
+ else if(b == 2) k = 1;
+ else if(b == 32) k = 5;
+ else if(b == 4) k = 2;
+ else { this.fromRadix(s,b); return; }
+ this.t = 0;
+ this.s = 0;
+ var i = s.length, mi = false, sh = 0;
+ while(--i >= 0) {
+ var x = (k==8)?s[i]&0xff:intAt(s,i);
+ if(x < 0) {
+ if(s.charAt(i) == "-") mi = true;
+ continue;
+ }
+ mi = false;
+ if(sh == 0)
+ this_array[this.t++] = x;
+ else if(sh+k > BI_DB) {
+ this_array[this.t-1] |= (x&((1<<(BI_DB-sh))-1))<<sh;
+ this_array[this.t++] = (x>>(BI_DB-sh));
+ }
+ else
+ this_array[this.t-1] |= x<<sh;
+ sh += k;
+ if(sh >= BI_DB) sh -= BI_DB;
+ }
+ if(k == 8 && (s[0]&0x80) != 0) {
+ this.s = -1;
+ if(sh > 0) this_array[this.t-1] |= ((1<<(BI_DB-sh))-1)<<sh;
+ }
+ this.clamp();
+ if(mi) BigInteger.ZERO.subTo(this,this);
+}
+
+// (protected) clamp off excess high words
+function bnpClamp() {
+ var this_array = this.array;
+ var c = this.s&BI_DM;
+ while(this.t > 0 && this_array[this.t-1] == c) --this.t;
+}
+
+// (public) return string representation in given radix
+function bnToString(b) {
+ var this_array = this.array;
+ if(this.s < 0) return "-"+this.negate().toString(b);
+ var k;
+ if(b == 16) k = 4;
+ else if(b == 8) k = 3;
+ else if(b == 2) k = 1;
+ else if(b == 32) k = 5;
+ else if(b == 4) k = 2;
+ else return this.toRadix(b);
+ var km = (1<<k)-1, d, m = false, r = "", i = this.t;
+ var p = BI_DB-(i*BI_DB)%k;
+ if(i-- > 0) {
+ if(p < BI_DB && (d = this_array[i]>>p) > 0) { m = true; r = int2char(d); }
+ while(i >= 0) {
+ if(p < k) {
+ d = (this_array[i]&((1<<p)-1))<<(k-p);
+ d |= this_array[--i]>>(p+=BI_DB-k);
+ }
+ else {
+ d = (this_array[i]>>(p-=k))&km;
+ if(p <= 0) { p += BI_DB; --i; }
+ }
+ if(d > 0) m = true;
+ if(m) r += int2char(d);
+ }
+ }
+ return m?r:"0";
+}
+
+// (public) -this
+function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
+
+// (public) |this|
+function bnAbs() { return (this.s<0)?this.negate():this; }
+
+// (public) return + if this > a, - if this < a, 0 if equal
+function bnCompareTo(a) {
+ var this_array = this.array;
+ var a_array = a.array;
+
+ var r = this.s-a.s;
+ if(r != 0) return r;
+ var i = this.t;
+ r = i-a.t;
+ if(r != 0) return r;
+ while(--i >= 0) if((r=this_array[i]-a_array[i]) != 0) return r;
+ return 0;
+}
+
+// returns bit length of the integer x
+function nbits(x) {
+ var r = 1, t;
+ if((t=x>>>16) != 0) { x = t; r += 16; }
+ if((t=x>>8) != 0) { x = t; r += 8; }
+ if((t=x>>4) != 0) { x = t; r += 4; }
+ if((t=x>>2) != 0) { x = t; r += 2; }
+ if((t=x>>1) != 0) { x = t; r += 1; }
+ return r;
+}
+
+// (public) return the number of bits in "this"
+function bnBitLength() {
+ var this_array = this.array;
+ if(this.t <= 0) return 0;
+ return BI_DB*(this.t-1)+nbits(this_array[this.t-1]^(this.s&BI_DM));
+}
+
+// (protected) r = this << n*DB
+function bnpDLShiftTo(n,r) {
+ var this_array = this.array;
+ var r_array = r.array;
+ var i;
+ for(i = this.t-1; i >= 0; --i) r_array[i+n] = this_array[i];
+ for(i = n-1; i >= 0; --i) r_array[i] = 0;
+ r.t = this.t+n;
+ r.s = this.s;
+}
+
+// (protected) r = this >> n*DB
+function bnpDRShiftTo(n,r) {
+ var this_array = this.array;
+ var r_array = r.array;
+ for(var i = n; i < this.t; ++i) r_array[i-n] = this_array[i];
+ r.t = Math.max(this.t-n,0);
+ r.s = this.s;
+}
+
+// (protected) r = this << n
+function bnpLShiftTo(n,r) {
+ var this_array = this.array;
+ var r_array = r.array;
+ var bs = n%BI_DB;
+ var cbs = BI_DB-bs;
+ var bm = (1<<cbs)-1;
+ var ds = Math.floor(n/BI_DB), c = (this.s<<bs)&BI_DM, i;
+ for(i = this.t-1; i >= 0; --i) {
+ r_array[i+ds+1] = (this_array[i]>>cbs)|c;
+ c = (this_array[i]&bm)<<bs;
+ }
+ for(i = ds-1; i >= 0; --i) r_array[i] = 0;
+ r_array[ds] = c;
+ r.t = this.t+ds+1;
+ r.s = this.s;
+ r.clamp();
+}
+
+// (protected) r = this >> n
+function bnpRShiftTo(n,r) {
+ var this_array = this.array;
+ var r_array = r.array;
+ r.s = this.s;
+ var ds = Math.floor(n/BI_DB);
+ if(ds >= this.t) { r.t = 0; return; }
+ var bs = n%BI_DB;
+ var cbs = BI_DB-bs;
+ var bm = (1<<bs)-1;
+ r_array[0] = this_array[ds]>>bs;
+ for(var i = ds+1; i < this.t; ++i) {
+ r_array[i-ds-1] |= (this_array[i]&bm)<<cbs;
+ r_array[i-ds] = this_array[i]>>bs;
+ }
+ if(bs > 0) r_array[this.t-ds-1] |= (this.s&bm)<<cbs;
+ r.t = this.t-ds;
+ r.clamp();
+}
+
+// (protected) r = this - a
+function bnpSubTo(a,r) {
+ var this_array = this.array;
+ var r_array = r.array;
+ var a_array = a.array;
+ var i = 0, c = 0, m = Math.min(a.t,this.t);
+ while(i < m) {
+ c += this_array[i]-a_array[i];
+ r_array[i++] = c&BI_DM;
+ c >>= BI_DB;
+ }
+ if(a.t < this.t) {
+ c -= a.s;
+ while(i < this.t) {
+ c += this_array[i];
+ r_array[i++] = c&BI_DM;
+ c >>= BI_DB;
+ }
+ c += this.s;
+ }
+ else {
+ c += this.s;
+ while(i < a.t) {
+ c -= a_array[i];
+ r_array[i++] = c&BI_DM;
+ c >>= BI_DB;
+ }
+ c -= a.s;
+ }
+ r.s = (c<0)?-1:0;
+ if(c < -1) r_array[i++] = BI_DV+c;
+ else if(c > 0) r_array[i++] = c;
+ r.t = i;
+ r.clamp();
+}
+
+// (protected) r = this * a, r != this,a (HAC 14.12)
+// "this" should be the larger one if appropriate.
+function bnpMultiplyTo(a,r) {
+ var this_array = this.array;
+ var r_array = r.array;
+ var x = this.abs(), y = a.abs();
+ var y_array = y.array;
+
+ var i = x.t;
+ r.t = i+y.t;
+ while(--i >= 0) r_array[i] = 0;
+ for(i = 0; i < y.t; ++i) r_array[i+x.t] = x.am(0,y_array[i],r,i,0,x.t);
+ r.s = 0;
+ r.clamp();
+ if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
+}
+
+// (protected) r = this^2, r != this (HAC 14.16)
+function bnpSquareTo(r) {
+ var x = this.abs();
+ var x_array = x.array;
+ var r_array = r.array;
+
+ var i = r.t = 2*x.t;
+ while(--i >= 0) r_array[i] = 0;
+ for(i = 0; i < x.t-1; ++i) {
+ var c = x.am(i,x_array[i],r,2*i,0,1);
+ if((r_array[i+x.t]+=x.am(i+1,2*x_array[i],r,2*i+1,c,x.t-i-1)) >= BI_DV) {
+ r_array[i+x.t] -= BI_DV;
+ r_array[i+x.t+1] = 1;
+ }
+ }
+ if(r.t > 0) r_array[r.t-1] += x.am(i,x_array[i],r,2*i,0,1);
+ r.s = 0;
+ r.clamp();
+}
+
+// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
+// r != q, this != m. q or r may be null.
+function bnpDivRemTo(m,q,r) {
+ var pm = m.abs();
+ if(pm.t <= 0) return;
+ var pt = this.abs();
+ if(pt.t < pm.t) {
+ if(q != null) q.fromInt(0);
+ if(r != null) this.copyTo(r);
+ return;
+ }
+ if(r == null) r = nbi();
+ var y = nbi(), ts = this.s, ms = m.s;
+ var pm_array = pm.array;
+ var nsh = BI_DB-nbits(pm_array[pm.t-1]); // normalize modulus
+ if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
+ else { pm.copyTo(y); pt.copyTo(r); }
+ var ys = y.t;
+
+ var y_array = y.array;
+ var y0 = y_array[ys-1];
+ if(y0 == 0) return;
+ var yt = y0*(1<<BI_F1)+((ys>1)?y_array[ys-2]>>BI_F2:0);
+ var d1 = BI_FV/yt, d2 = (1<<BI_F1)/yt, e = 1<<BI_F2;
+ var i = r.t, j = i-ys, t = (q==null)?nbi():q;
+ y.dlShiftTo(j,t);
+
+ var r_array = r.array;
+ if(r.compareTo(t) >= 0) {
+ r_array[r.t++] = 1;
+ r.subTo(t,r);
+ }
+ BigInteger.ONE.dlShiftTo(ys,t);
+ t.subTo(y,y); // "negative" y so we can replace sub with am later
+ while(y.t < ys) y_array[y.t++] = 0;
+ while(--j >= 0) {
+ // Estimate quotient digit
+ var qd = (r_array[--i]==y0)?BI_DM:Math.floor(r_array[i]*d1+(r_array[i-1]+e)*d2);
+ if((r_array[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
+ y.dlShiftTo(j,t);
+ r.subTo(t,r);
+ while(r_array[i] < --qd) r.subTo(t,r);
+ }
+ }
+ if(q != null) {
+ r.drShiftTo(ys,q);
+ if(ts != ms) BigInteger.ZERO.subTo(q,q);
+ }
+ r.t = ys;
+ r.clamp();
+ if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
+ if(ts < 0) BigInteger.ZERO.subTo(r,r);
+}
+
+// (public) this mod a
+function bnMod(a) {
+ var r = nbi();
+ this.abs().divRemTo(a,null,r);
+ if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
+ return r;
+}
+
+// Modular reduction using "classic" algorithm
+function Classic(m) { this.m = m; }
+function cConvert(x) {
+ if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
+ else return x;
+}
+function cRevert(x) { return x; }
+function cReduce(x) { x.divRemTo(this.m,null,x); }
+function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
+function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
+
+Classic.prototype.convert = cConvert;
+Classic.prototype.revert = cRevert;
+Classic.prototype.reduce = cReduce;
+Classic.prototype.mulTo = cMulTo;
+Classic.prototype.sqrTo = cSqrTo;
+
+// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
+// justification:
+// xy == 1 (mod m)
+// xy = 1+km
+// xy(2-xy) = (1+km)(1-km)
+// x[y(2-xy)] = 1-k^2m^2
+// x[y(2-xy)] == 1 (mod m^2)
+// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
+// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
+// JS multiply "overflows" differently from C/C++, so care is needed here.
+function bnpInvDigit() {
+ var this_array = this.array;
+ if(this.t < 1) return 0;
+ var x = this_array[0];
+ if((x&1) == 0) return 0;
+ var y = x&3; // y == 1/x mod 2^2
+ y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
+ y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
+ y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
+ // last step - calculate inverse mod DV directly;
+ // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
+ y = (y*(2-x*y%BI_DV))%BI_DV; // y == 1/x mod 2^dbits
+ // we really want the negative inverse, and -DV < y < DV
+ return (y>0)?BI_DV-y:-y;
+}
+
+// Montgomery reduction
+function Montgomery(m) {
+ this.m = m;
+ this.mp = m.invDigit();
+ this.mpl = this.mp&0x7fff;
+ this.mph = this.mp>>15;
+ this.um = (1<<(BI_DB-15))-1;
+ this.mt2 = 2*m.t;
+}
+
+// xR mod m
+function montConvert(x) {
+ var r = nbi();
+ x.abs().dlShiftTo(this.m.t,r);
+ r.divRemTo(this.m,null,r);
+ if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
+ return r;
+}
+
+// x/R mod m
+function montRevert(x) {
+ var r = nbi();
+ x.copyTo(r);
+ this.reduce(r);
+ return r;
+}
+
+// x = x/R mod m (HAC 14.32)
+function montReduce(x) {
+ var x_array = x.array;
+ while(x.t <= this.mt2) // pad x so am has enough room later
+ x_array[x.t++] = 0;
+ for(var i = 0; i < this.m.t; ++i) {
+ // faster way of calculating u0 = x[i]*mp mod DV
+ var j = x_array[i]&0x7fff;
+ var u0 = (j*this.mpl+(((j*this.mph+(x_array[i]>>15)*this.mpl)&this.um)<<15))&BI_DM;
+ // use am to combine the multiply-shift-add into one call
+ j = i+this.m.t;
+ x_array[j] += this.m.am(0,u0,x,i,0,this.m.t);
+ // propagate carry
+ while(x_array[j] >= BI_DV) { x_array[j] -= BI_DV; x_array[++j]++; }
+ }
+ x.clamp();
+ x.drShiftTo(this.m.t,x);
+ if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
+}
+
+// r = "x^2/R mod m"; x != r
+function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
+
+// r = "xy/R mod m"; x,y != r
+function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
+
+Montgomery.prototype.convert = montConvert;
+Montgomery.prototype.revert = montRevert;
+Montgomery.prototype.reduce = montReduce;
+Montgomery.prototype.mulTo = montMulTo;
+Montgomery.prototype.sqrTo = montSqrTo;
+
+// (protected) true iff this is even
+function bnpIsEven() {
+ var this_array = this.array;
+ return ((this.t>0)?(this_array[0]&1):this.s) == 0;
+}
+
+// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
+function bnpExp(e,z) {
+ if(e > 0xffffffff || e < 1) return BigInteger.ONE;
+ var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
+ g.copyTo(r);
+ while(--i >= 0) {
+ z.sqrTo(r,r2);
+ if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
+ else { var t = r; r = r2; r2 = t; }
+ }
+ return z.revert(r);
+}
+
+// (public) this^e % m, 0 <= e < 2^32
+function bnModPowInt(e,m) {
+ var z;
+ if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
+ return this.exp(e,z);
+}
+
+// protected
+BigInteger.prototype.copyTo = bnpCopyTo;
+BigInteger.prototype.fromInt = bnpFromInt;
+BigInteger.prototype.fromString = bnpFromString;
+BigInteger.prototype.clamp = bnpClamp;
+BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
+BigInteger.prototype.drShiftTo = bnpDRShiftTo;
+BigInteger.prototype.lShiftTo = bnpLShiftTo;
+BigInteger.prototype.rShiftTo = bnpRShiftTo;
+BigInteger.prototype.subTo = bnpSubTo;
+BigInteger.prototype.multiplyTo = bnpMultiplyTo;
+BigInteger.prototype.squareTo = bnpSquareTo;
+BigInteger.prototype.divRemTo = bnpDivRemTo;
+BigInteger.prototype.invDigit = bnpInvDigit;
+BigInteger.prototype.isEven = bnpIsEven;
+BigInteger.prototype.exp = bnpExp;
+
+// public
+BigInteger.prototype.toString = bnToString;
+BigInteger.prototype.negate = bnNegate;
+BigInteger.prototype.abs = bnAbs;
+BigInteger.prototype.compareTo = bnCompareTo;
+BigInteger.prototype.bitLength = bnBitLength;
+BigInteger.prototype.mod = bnMod;
+BigInteger.prototype.modPowInt = bnModPowInt;
+
+// "constants"
+BigInteger.ZERO = nbv(0);
+BigInteger.ONE = nbv(1);
+// Copyright (c) 2005 Tom Wu
+// All Rights Reserved.
+// See "LICENSE" for details.
+
+// Extended JavaScript BN functions, required for RSA private ops.
+
+// (public)
+function bnClone() { var r = nbi(); this.copyTo(r); return r; }
+
+// (public) return value as integer
+function bnIntValue() {
+ var this_array = this.array;
+ if(this.s < 0) {
+ if(this.t == 1) return this_array[0]-BI_DV;
+ else if(this.t == 0) return -1;
+ }
+ else if(this.t == 1) return this_array[0];
+ else if(this.t == 0) return 0;
+ // assumes 16 < DB < 32
+ return ((this_array[1]&((1<<(32-BI_DB))-1))<<BI_DB)|this_array[0];
+}
+
+// (public) return value as byte
+function bnByteValue() {
+ var this_array = this.array;
+ return (this.t==0)?this.s:(this_array[0]<<24)>>24;
+}
+
+// (public) return value as short (assumes DB>=16)
+function bnShortValue() {
+ var this_array = this.array;
+ return (this.t==0)?this.s:(this_array[0]<<16)>>16;
+}
+
+// (protected) return x s.t. r^x < DV
+function bnpChunkSize(r) { return Math.floor(Math.LN2*BI_DB/Math.log(r)); }
+
+// (public) 0 if this == 0, 1 if this > 0
+function bnSigNum() {
+ var this_array = this.array;
+ if(this.s < 0) return -1;
+ else if(this.t <= 0 || (this.t == 1 && this_array[0] <= 0)) return 0;
+ else return 1;
+}
+
+// (protected) convert to radix string
+function bnpToRadix(b) {
+ if(b == null) b = 10;
+ if(this.signum() == 0 || b < 2 || b > 36) return "0";
+ var cs = this.chunkSize(b);
+ var a = Math.pow(b,cs);
+ var d = nbv(a), y = nbi(), z = nbi(), r = "";
+ this.divRemTo(d,y,z);
+ while(y.signum() > 0) {
+ r = (a+z.intValue()).toString(b).substr(1) + r;
+ y.divRemTo(d,y,z);
+ }
+ return z.intValue().toString(b) + r;
+}
+
+// (protected) convert from radix string
+function bnpFromRadix(s,b) {
+ this.fromInt(0);
+ if(b == null) b = 10;
+ var cs = this.chunkSize(b);
+ var d = Math.pow(b,cs), mi = false, j = 0, w = 0;
+ for(var i = 0; i < s.length; ++i) {
+ var x = intAt(s,i);
+ if(x < 0) {
+ if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
+ continue;
+ }
+ w = b*w+x;
+ if(++j >= cs) {
+ this.dMultiply(d);
+ this.dAddOffset(w,0);
+ j = 0;
+ w = 0;
+ }
+ }
+ if(j > 0) {
+ this.dMultiply(Math.pow(b,j));
+ this.dAddOffset(w,0);
+ }
+ if(mi) BigInteger.ZERO.subTo(this,this);
+}
+
+// (protected) alternate constructor
+function bnpFromNumber(a,b,c) {
+ if("number" == typeof b) {
+ // new BigInteger(int,int,RNG)
+ if(a < 2) this.fromInt(1);
+ else {
+ this.fromNumber(a,c);
+ if(!this.testBit(a-1)) // force MSB set
+ this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
+ if(this.isEven()) this.dAddOffset(1,0); // force odd
+ while(!this.isProbablePrime(b)) {
+ this.dAddOffset(2,0);
+ if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
+ }
+ }
+ }
+ else {
+ // new BigInteger(int,RNG)
+ var x = new Array(), t = a&7;
+ x.length = (a>>3)+1;
+ b.nextBytes(x);
+ if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0;
+ this.fromString(x,256);
+ }
+}
+
+// (public) convert to bigendian byte array
+function bnToByteArray() {
+ var this_array = this.array;
+ var i = this.t, r = new Array();
+ r[0] = this.s;
+ var p = BI_DB-(i*BI_DB)%8, d, k = 0;
+ if(i-- > 0) {
+ if(p < BI_DB && (d = this_array[i]>>p) != (this.s&BI_DM)>>p)
+ r[k++] = d|(this.s<<(BI_DB-p));
+ while(i >= 0) {
+ if(p < 8) {
+ d = (this_array[i]&((1<<p)-1))<<(8-p);
+ d |= this_array[--i]>>(p+=BI_DB-8);
+ }
+ else {
+ d = (this_array[i]>>(p-=8))&0xff;
+ if(p <= 0) { p += BI_DB; --i; }
+ }
+ if((d&0x80) != 0) d |= -256;
+ if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
+ if(k > 0 || d != this.s) r[k++] = d;
+ }
+ }
+ return r;
+}
+
+function bnEquals(a) { return(this.compareTo(a)==0); }
+function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
+function bnMax(a) { return(this.compareTo(a)>0)?this:a; }
+
+// (protected) r = this op a (bitwise)
+function bnpBitwiseTo(a,op,r) {
+ var this_array = this.array;
+ var a_array = a.array;
+ var r_array = r.array;
+ var i, f, m = Math.min(a.t,this.t);
+ for(i = 0; i < m; ++i) r_array[i] = op(this_array[i],a_array[i]);
+ if(a.t < this.t) {
+ f = a.s&BI_DM;
+ for(i = m; i < this.t; ++i) r_array[i] = op(this_array[i],f);
+ r.t = this.t;
+ }
+ else {
+ f = this.s&BI_DM;
+ for(i = m; i < a.t; ++i) r_array[i] = op(f,a_array[i]);
+ r.t = a.t;
+ }
+ r.s = op(this.s,a.s);
+ r.clamp();
+}
+
+// (public) this & a
+function op_and(x,y) { return x&y; }
+function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }
+
+// (public) this | a
+function op_or(x,y) { return x|y; }
+function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }
+
+// (public) this ^ a
+function op_xor(x,y) { return x^y; }
+function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }
+
+// (public) this & ~a
+function op_andnot(x,y) { return x&~y; }
+function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }
+
+// (public) ~this
+function bnNot() {
+ var this_array = this.array;
+ var r = nbi();
+ var r_array = r.array;
+
+ for(var i = 0; i < this.t; ++i) r_array[i] = BI_DM&~this_array[i];
+ r.t = this.t;
+ r.s = ~this.s;
+ return r;
+}
+
+// (public) this << n
+function bnShiftLeft(n) {
+ var r = nbi();
+ if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
+ return r;
+}
+
+// (public) this >> n
+function bnShiftRight(n) {
+ var r = nbi();
+ if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
+ return r;
+}
+
+// return index of lowest 1-bit in x, x < 2^31
+function lbit(x) {
+ if(x == 0) return -1;
+ var r = 0;
+ if((x&0xffff) == 0) { x >>= 16; r += 16; }
+ if((x&0xff) == 0) { x >>= 8; r += 8; }
+ if((x&0xf) == 0) { x >>= 4; r += 4; }
+ if((x&3) == 0) { x >>= 2; r += 2; }
+ if((x&1) == 0) ++r;
+ return r;
+}
+
+// (public) returns index of lowest 1-bit (or -1 if none)
+function bnGetLowestSetBit() {
+ var this_array = this.array;
+ for(var i = 0; i < this.t; ++i)
+ if(this_array[i] != 0) return i*BI_DB+lbit(this_array[i]);
+ if(this.s < 0) return this.t*BI_DB;
+ return -1;
+}
+
+// return number of 1 bits in x
+function cbit(x) {
+ var r = 0;
+ while(x != 0) { x &= x-1; ++r; }
+ return r;
+}
+
+// (public) return number of set bits
+function bnBitCount() {
+ var r = 0, x = this.s&BI_DM;
+ for(var i = 0; i < this.t; ++i) r += cbit(this_array[i]^x);
+ return r;
+}
+
+// (public) true iff nth bit is set
+function bnTestBit(n) {
+ var this_array = this.array;
+ var j = Math.floor(n/BI_DB);
+ if(j >= this.t) return(this.s!=0);
+ return((this_array[j]&(1<<(n%BI_DB)))!=0);
+}
+
+// (protected) this op (1<<n)
+function bnpChangeBit(n,op) {
+ var r = BigInteger.ONE.shiftLeft(n);
+ this.bitwiseTo(r,op,r);
+ return r;
+}
+
+// (public) this | (1<<n)
+function bnSetBit(n) { return this.changeBit(n,op_or); }
+
+// (public) this & ~(1<<n)
+function bnClearBit(n) { return this.changeBit(n,op_andnot); }
+
+// (public) this ^ (1<<n)
+function bnFlipBit(n) { return this.changeBit(n,op_xor); }
+
+// (protected) r = this + a
+function bnpAddTo(a,r) {
+ var this_array = this.array;
+ var a_array = a.array;
+ var r_array = r.array;
+ var i = 0, c = 0, m = Math.min(a.t,this.t);
+ while(i < m) {
+ c += this_array[i]+a_array[i];
+ r_array[i++] = c&BI_DM;
+ c >>= BI_DB;
+ }
+ if(a.t < this.t) {
+ c += a.s;
+ while(i < this.t) {
+ c += this_array[i];
+ r_array[i++] = c&BI_DM;
+ c >>= BI_DB;
+ }
+ c += this.s;
+ }
+ else {
+ c += this.s;
+ while(i < a.t) {
+ c += a_array[i];
+ r_array[i++] = c&BI_DM;
+ c >>= BI_DB;
+ }
+ c += a.s;
+ }
+ r.s = (c<0)?-1:0;
+ if(c > 0) r_array[i++] = c;
+ else if(c < -1) r_array[i++] = BI_DV+c;
+ r.t = i;
+ r.clamp();
+}
+
+// (public) this + a
+function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }
+
+// (public) this - a
+function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }
+
+// (public) this * a
+function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }
+
+// (public) this / a
+function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }
+
+// (public) this % a
+function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }
+
+// (public) [this/a,this%a]
+function bnDivideAndRemainder(a) {
+ var q = nbi(), r = nbi();
+ this.divRemTo(a,q,r);
+ return new Array(q,r);
+}
+
+// (protected) this *= n, this >= 0, 1 < n < DV
+function bnpDMultiply(n) {
+ var this_array = this.array;
+ this_array[this.t] = this.am(0,n-1,this,0,0,this.t);
+ ++this.t;
+ this.clamp();
+}
+
+// (protected) this += n << w words, this >= 0
+function bnpDAddOffset(n,w) {
+ var this_array = this.array;
+ while(this.t <= w) this_array[this.t++] = 0;
+ this_array[w] += n;
+ while(this_array[w] >= BI_DV) {
+ this_array[w] -= BI_DV;
+ if(++w >= this.t) this_array[this.t++] = 0;
+ ++this_array[w];
+ }
+}
+
+// A "null" reducer
+function NullExp() {}
+function nNop(x) { return x; }
+function nMulTo(x,y,r) { x.multiplyTo(y,r); }
+function nSqrTo(x,r) { x.squareTo(r); }
+
+NullExp.prototype.convert = nNop;
+NullExp.prototype.revert = nNop;
+NullExp.prototype.mulTo = nMulTo;
+NullExp.prototype.sqrTo = nSqrTo;
+
+// (public) this^e
+function bnPow(e) { return this.exp(e,new NullExp()); }
+
+// (protected) r = lower n words of "this * a", a.t <= n
+// "this" should be the larger one if appropriate.
+function bnpMultiplyLowerTo(a,n,r) {
+ var r_array = r.array;
+ var a_array = a.array;
+ var i = Math.min(this.t+a.t,n);
+ r.s = 0; // assumes a,this >= 0
+ r.t = i;
+ while(i > 0) r_array[--i] = 0;
+ var j;
+ for(j = r.t-this.t; i < j; ++i) r_array[i+this.t] = this.am(0,a_array[i],r,i,0,this.t);
+ for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a_array[i],r,i,0,n-i);
+ r.clamp();
+}
+
+// (protected) r = "this * a" without lower n words, n > 0
+// "this" should be the larger one if appropriate.
+function bnpMultiplyUpperTo(a,n,r) {
+ var r_array = r.array;
+ var a_array = a.array;
+ --n;
+ var i = r.t = this.t+a.t-n;
+ r.s = 0; // assumes a,this >= 0
+ while(--i >= 0) r_array[i] = 0;
+ for(i = Math.max(n-this.t,0); i < a.t; ++i)
+ r_array[this.t+i-n] = this.am(n-i,a_array[i],r,0,0,this.t+i-n);
+ r.clamp();
+ r.drShiftTo(1,r);
+}
+
+// Barrett modular reduction
+function Barrett(m) {
+ // setup Barrett
+ this.r2 = nbi();
+ this.q3 = nbi();
+ BigInteger.ONE.dlShiftTo(2*m.t,this.r2);
+ this.mu = this.r2.divide(m);
+ this.m = m;
+}
+
+function barrettConvert(x) {
+ if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
+ else if(x.compareTo(this.m) < 0) return x;
+ else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
+}
+
+function barrettRevert(x) { return x; }
+
+// x = x mod m (HAC 14.42)
+function barrettReduce(x) {
+ x.drShiftTo(this.m.t-1,this.r2);
+ if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
+ this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
+ this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
+ while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
+ x.subTo(this.r2,x);
+ while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
+}
+
+// r = x^2 mod m; x != r
+function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
+
+// r = x*y mod m; x,y != r
+function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
+
+Barrett.prototype.convert = barrettConvert;
+Barrett.prototype.revert = barrettRevert;
+Barrett.prototype.reduce = barrettReduce;
+Barrett.prototype.mulTo = barrettMulTo;
+Barrett.prototype.sqrTo = barrettSqrTo;
+
+// (public) this^e % m (HAC 14.85)
+function bnModPow(e,m) {
+ var e_array = e.array;
+ var i = e.bitLength(), k, r = nbv(1), z;
+ if(i <= 0) return r;
+ else if(i < 18) k = 1;
+ else if(i < 48) k = 3;
+ else if(i < 144) k = 4;
+ else if(i < 768) k = 5;
+ else k = 6;
+ if(i < 8)
+ z = new Classic(m);
+ else if(m.isEven())
+ z = new Barrett(m);
+ else
+ z = new Montgomery(m);
+
+ // precomputation
+ var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1;
+ g[1] = z.convert(this);
+ if(k > 1) {
+ var g2 = nbi();
+ z.sqrTo(g[1],g2);
+ while(n <= km) {
+ g[n] = nbi();
+ z.mulTo(g2,g[n-2],g[n]);
+ n += 2;
+ }
+ }
+
+ var j = e.t-1, w, is1 = true, r2 = nbi(), t;
+ i = nbits(e_array[j])-1;
+ while(j >= 0) {
+ if(i >= k1) w = (e_array[j]>>(i-k1))&km;
+ else {
+ w = (e_array[j]&((1<<(i+1))-1))<<(k1-i);
+ if(j > 0) w |= e_array[j-1]>>(BI_DB+i-k1);
+ }
+
+ n = k;
+ while((w&1) == 0) { w >>= 1; --n; }
+ if((i -= n) < 0) { i += BI_DB; --j; }
+ if(is1) { // ret == 1, don't bother squaring or multiplying it
+ g[w].copyTo(r);
+ is1 = false;
+ }
+ else {
+ while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
+ if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
+ z.mulTo(r2,g[w],r);
+ }
+
+ while(j >= 0 && (e_array[j]&(1<<i)) == 0) {
+ z.sqrTo(r,r2); t = r; r = r2; r2 = t;
+ if(--i < 0) { i = BI_DB-1; --j; }
+ }
+ }
+ return z.revert(r);
+}
+
+// (public) gcd(this,a) (HAC 14.54)
+function bnGCD(a) {
+ var x = (this.s<0)?this.negate():this.clone();
+ var y = (a.s<0)?a.negate():a.clone();
+ if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
+ var i = x.getLowestSetBit(), g = y.getLowestSetBit();
+ if(g < 0) return x;
+ if(i < g) g = i;
+ if(g > 0) {
+ x.rShiftTo(g,x);
+ y.rShiftTo(g,y);
+ }
+ while(x.signum() > 0) {
+ if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);
+ if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);
+ if(x.compareTo(y) >= 0) {
+ x.subTo(y,x);
+ x.rShiftTo(1,x);
+ }
+ else {
+ y.subTo(x,y);
+ y.rShiftTo(1,y);
+ }
+ }
+ if(g > 0) y.lShiftTo(g,y);
+ return y;
+}
+
+// (protected) this % n, n < 2^26
+function bnpModInt(n) {
+ var this_array = this.array;
+ if(n <= 0) return 0;
+ var d = BI_DV%n, r = (this.s<0)?n-1:0;
+ if(this.t > 0)
+ if(d == 0) r = this_array[0]%n;
+ else for(var i = this.t-1; i >= 0; --i) r = (d*r+this_array[i])%n;
+ return r;
+}
+
+// (public) 1/this % m (HAC 14.61)
+function bnModInverse(m) {
+ var ac = m.isEven();
+ if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;
+ var u = m.clone(), v = this.clone();
+ var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
+ while(u.signum() != 0) {
+ while(u.isEven()) {
+ u.rShiftTo(1,u);
+ if(ac) {
+ if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
+ a.rShiftTo(1,a);
+ }
+ else if(!b.isEven()) b.subTo(m,b);
+ b.rShiftTo(1,b);
+ }
+ while(v.isEven()) {
+ v.rShiftTo(1,v);
+ if(ac) {
+ if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
+ c.rShiftTo(1,c);
+ }
+ else if(!d.isEven()) d.subTo(m,d);
+ d.rShiftTo(1,d);
+ }
+ if(u.compareTo(v) >= 0) {
+ u.subTo(v,u);
+ if(ac) a.subTo(c,a);
+ b.subTo(d,b);
+ }
+ else {
+ v.subTo(u,v);
+ if(ac) c.subTo(a,c);
+ d.subTo(b,d);
+ }
+ }
+ if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
+ if(d.compareTo(m) >= 0) return d.subtract(m);
+ if(d.signum() < 0) d.addTo(m,d); else return d;
+ if(d.signum() < 0) return d.add(m); else return d;
+}
+
+var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509];
+var lplim = (1<<26)/lowprimes[lowprimes.length-1];
+
+// (public) test primality with certainty >= 1-.5^t
+function bnIsProbablePrime(t) {
+ var i, x = this.abs();
+ var x_array = x.array;
+ if(x.t == 1 && x_array[0] <= lowprimes[lowprimes.length-1]) {
+ for(i = 0; i < lowprimes.length; ++i)
+ if(x_array[0] == lowprimes[i]) return true;
+ return false;
+ }
+ if(x.isEven()) return false;
+ i = 1;
+ while(i < lowprimes.length) {
+ var m = lowprimes[i], j = i+1;
+ while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
+ m = x.modInt(m);
+ while(i < j) if(m%lowprimes[i++] == 0) return false;
+ }
+ return x.millerRabin(t);
+}
+
+// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
+function bnpMillerRabin(t) {
+ var n1 = this.subtract(BigInteger.ONE);
+ var k = n1.getLowestSetBit();
+ if(k <= 0) return false;
+ var r = n1.shiftRight(k);
+ t = (t+1)>>1;
+ if(t > lowprimes.length) t = lowprimes.length;
+ var a = nbi();
+ for(var i = 0; i < t; ++i) {
+ a.fromInt(lowprimes[i]);
+ var y = a.modPow(r,this);
+ if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
+ var j = 1;
+ while(j++ < k && y.compareTo(n1) != 0) {
+ y = y.modPowInt(2,this);
+ if(y.compareTo(BigInteger.ONE) == 0) return false;
+ }
+ if(y.compareTo(n1) != 0) return false;
+ }
+ }
+ return true;
+}
+
+// protected
+BigInteger.prototype.chunkSize = bnpChunkSize;
+BigInteger.prototype.toRadix = bnpToRadix;
+BigInteger.prototype.fromRadix = bnpFromRadix;
+BigInteger.prototype.fromNumber = bnpFromNumber;
+BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
+BigInteger.prototype.changeBit = bnpChangeBit;
+BigInteger.prototype.addTo = bnpAddTo;
+BigInteger.prototype.dMultiply = bnpDMultiply;
+BigInteger.prototype.dAddOffset = bnpDAddOffset;
+BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
+BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
+BigInteger.prototype.modInt = bnpModInt;
+BigInteger.prototype.millerRabin = bnpMillerRabin;
+
+// public
+BigInteger.prototype.clone = bnClone;
+BigInteger.prototype.intValue = bnIntValue;
+BigInteger.prototype.byteValue = bnByteValue;
+BigInteger.prototype.shortValue = bnShortValue;
+BigInteger.prototype.signum = bnSigNum;
+BigInteger.prototype.toByteArray = bnToByteArray;
+BigInteger.prototype.equals = bnEquals;
+BigInteger.prototype.min = bnMin;
+BigInteger.prototype.max = bnMax;
+BigInteger.prototype.and = bnAnd;
+BigInteger.prototype.or = bnOr;
+BigInteger.prototype.xor = bnXor;
+BigInteger.prototype.andNot = bnAndNot;
+BigInteger.prototype.not = bnNot;
+BigInteger.prototype.shiftLeft = bnShiftLeft;
+BigInteger.prototype.shiftRight = bnShiftRight;
+BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
+BigInteger.prototype.bitCount = bnBitCount;
+BigInteger.prototype.testBit = bnTestBit;
+BigInteger.prototype.setBit = bnSetBit;
+BigInteger.prototype.clearBit = bnClearBit;
+BigInteger.prototype.flipBit = bnFlipBit;
+BigInteger.prototype.add = bnAdd;
+BigInteger.prototype.subtract = bnSubtract;
+BigInteger.prototype.multiply = bnMultiply;
+BigInteger.prototype.divide = bnDivide;
+BigInteger.prototype.remainder = bnRemainder;
+BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
+BigInteger.prototype.modPow = bnModPow;
+BigInteger.prototype.modInverse = bnModInverse;
+BigInteger.prototype.pow = bnPow;
+BigInteger.prototype.gcd = bnGCD;
+BigInteger.prototype.isProbablePrime = bnIsProbablePrime;
+
+// BigInteger interfaces not implemented in jsbn:
+
+// BigInteger(int signum, byte[] magnitude)
+// double doubleValue()
+// float floatValue()
+// int hashCode()
+// long longValue()
+// static BigInteger valueOf(long val)
+// prng4.js - uses Arcfour as a PRNG
+
+function Arcfour() {
+ this.i = 0;
+ this.j = 0;
+ this.S = new Array();
+}
+
+// Initialize arcfour context from key, an array of ints, each from [0..255]
+function ARC4init(key) {
+ var i, j, t;
+ for(i = 0; i < 256; ++i)
+ this.S[i] = i;
+ j = 0;
+ for(i = 0; i < 256; ++i) {
+ j = (j + this.S[i] + key[i % key.length]) & 255;
+ t = this.S[i];
+ this.S[i] = this.S[j];
+ this.S[j] = t;
+ }
+ this.i = 0;
+ this.j = 0;
+}
+
+function ARC4next() {
+ var t;
+ this.i = (this.i + 1) & 255;
+ this.j = (this.j + this.S[this.i]) & 255;
+ t = this.S[this.i];
+ this.S[this.i] = this.S[this.j];
+ this.S[this.j] = t;
+ return this.S[(t + this.S[this.i]) & 255];
+}
+
+Arcfour.prototype.init = ARC4init;
+Arcfour.prototype.next = ARC4next;
+
+// Plug in your RNG constructor here
+function prng_newstate() {
+ return new Arcfour();
+}
+
+// Pool size must be a multiple of 4 and greater than 32.
+// An array of bytes the size of the pool will be passed to init()
+var rng_psize = 256;
+// Random number generator - requires a PRNG backend, e.g. prng4.js
+
+// For best results, put code like
+// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
+// in your main HTML document.
+
+var rng_state;
+var rng_pool;
+var rng_pptr;
+
+// Mix in a 32-bit integer into the pool
+function rng_seed_int(x) {
+ rng_pool[rng_pptr++] ^= x & 255;
+ rng_pool[rng_pptr++] ^= (x >> 8) & 255;
+ rng_pool[rng_pptr++] ^= (x >> 16) & 255;
+ rng_pool[rng_pptr++] ^= (x >> 24) & 255;
+ if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
+}
+
+// Mix in the current time (w/milliseconds) into the pool
+function rng_seed_time() {
+ // Use pre-computed date to avoid making the benchmark
+ // results dependent on the current date.
+ rng_seed_int(1122926989487);
+}
+
+// Initialize the pool with junk if needed.
+if(rng_pool == null) {
+ rng_pool = new Array();
+ rng_pptr = 0;
+ var t;
+ while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
+ t = Math.floor(65536 * Math.random());
+ rng_pool[rng_pptr++] = t >>> 8;
+ rng_pool[rng_pptr++] = t & 255;
+ }
+ rng_pptr = 0;
+ rng_seed_time();
+ //rng_seed_int(window.screenX);
+ //rng_seed_int(window.screenY);
+}
+
+function rng_get_byte() {
+ if(rng_state == null) {
+ rng_seed_time();
+ rng_state = prng_newstate();
+ rng_state.init(rng_pool);
+ for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
+ rng_pool[rng_pptr] = 0;
+ rng_pptr = 0;
+ //rng_pool = null;
+ }
+ // TODO: allow reseeding after first request
+ return rng_state.next();
+}
+
+function rng_get_bytes(ba) {
+ var i;
+ for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
+}
+
+function SecureRandom() {}
+
+SecureRandom.prototype.nextBytes = rng_get_bytes;
+// Depends on jsbn.js and rng.js
+
+// convert a (hex) string to a bignum object
+function parseBigInt(str,r) {
+ return new BigInteger(str,r);
+}
+
+function linebrk(s,n) {
+ var ret = "";
+ var i = 0;
+ while(i + n < s.length) {
+ ret += s.substring(i,i+n) + "\n";
+ i += n;
+ }
+ return ret + s.substring(i,s.length);
+}
+
+function byte2Hex(b) {
+ if(b < 0x10)
+ return "0" + b.toString(16);
+ else
+ return b.toString(16);
+}
+
+// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
+function pkcs1pad2(s,n) {
+ if(n < s.length + 11) {
+ alert("Message too long for RSA");
+ return null;
+ }
+ var ba = new Array();
+ var i = s.length - 1;
+ while(i >= 0 && n > 0) ba[--n] = s.charCodeAt(i--);
+ ba[--n] = 0;
+ var rng = new SecureRandom();
+ var x = new Array();
+ while(n > 2) { // random non-zero pad
+ x[0] = 0;
+ while(x[0] == 0) rng.nextBytes(x);
+ ba[--n] = x[0];
+ }
+ ba[--n] = 2;
+ ba[--n] = 0;
+ return new BigInteger(ba);
+}
+
+// "empty" RSA key constructor
+function RSAKey() {
+ this.n = null;
+ this.e = 0;
+ this.d = null;
+ this.p = null;
+ this.q = null;
+ this.dmp1 = null;
+ this.dmq1 = null;
+ this.coeff = null;
+}
+
+// Set the public key fields N and e from hex strings
+function RSASetPublic(N,E) {
+ if(N != null && E != null && N.length > 0 && E.length > 0) {
+ this.n = parseBigInt(N,16);
+ this.e = parseInt(E,16);
+ }
+ else
+ alert("Invalid RSA public key");
+}
+
+// Perform raw public operation on "x": return x^e (mod n)
+function RSADoPublic(x) {
+ return x.modPowInt(this.e, this.n);
+}
+
+// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
+function RSAEncrypt(text) {
+ var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
+ if(m == null) return null;
+ var c = this.doPublic(m);
+ if(c == null) return null;
+ var h = c.toString(16);
+ if((h.length & 1) == 0) return h; else return "0" + h;
+}
+
+// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
+//function RSAEncryptB64(text) {
+// var h = this.encrypt(text);
+// if(h) return hex2b64(h); else return null;
+//}
+
+// protected
+RSAKey.prototype.doPublic = RSADoPublic;
+
+// public
+RSAKey.prototype.setPublic = RSASetPublic;
+RSAKey.prototype.encrypt = RSAEncrypt;
+//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
+// Depends on rsa.js and jsbn2.js
+
+// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
+function pkcs1unpad2(d,n) {
+ var b = d.toByteArray();
+ var i = 0;
+ while(i < b.length && b[i] == 0) ++i;
+ if(b.length-i != n-1 || b[i] != 2)
+ return null;
+ ++i;
+ while(b[i] != 0)
+ if(++i >= b.length) return null;
+ var ret = "";
+ while(++i < b.length)
+ ret += String.fromCharCode(b[i]);
+ return ret;
+}
+
+// Set the private key fields N, e, and d from hex strings
+function RSASetPrivate(N,E,D) {
+ if(N != null && E != null && N.length > 0 && E.length > 0) {
+ this.n = parseBigInt(N,16);
+ this.e = parseInt(E,16);
+ this.d = parseBigInt(D,16);
+ }
+ else
+ alert("Invalid RSA private key");
+}
+
+// Set the private key fields N, e, d and CRT params from hex strings
+function RSASetPrivateEx(N,E,D,P,Q,DP,DQ,C) {
+ if(N != null && E != null && N.length > 0 && E.length > 0) {
+ this.n = parseBigInt(N,16);
+ this.e = parseInt(E,16);
+ this.d = parseBigInt(D,16);
+ this.p = parseBigInt(P,16);
+ this.q = parseBigInt(Q,16);
+ this.dmp1 = parseBigInt(DP,16);
+ this.dmq1 = parseBigInt(DQ,16);
+ this.coeff = parseBigInt(C,16);
+ }
+ else
+ alert("Invalid RSA private key");
+}
+
+// Generate a new random private key B bits long, using public expt E
+function RSAGenerate(B,E) {
+ var rng = new SecureRandom();
+ var qs = B>>1;
+ this.e = parseInt(E,16);
+ var ee = new BigInteger(E,16);
+ for(;;) {
+ for(;;) {
+ this.p = new BigInteger(B-qs,1,rng);
+ if(this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break;
+ }
+ for(;;) {
+ this.q = new BigInteger(qs,1,rng);
+ if(this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break;
+ }
+ if(this.p.compareTo(this.q) <= 0) {
+ var t = this.p;
+ this.p = this.q;
+ this.q = t;
+ }
+ var p1 = this.p.subtract(BigInteger.ONE);
+ var q1 = this.q.subtract(BigInteger.ONE);
+ var phi = p1.multiply(q1);
+ if(phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
+ this.n = this.p.multiply(this.q);
+ this.d = ee.modInverse(phi);
+ this.dmp1 = this.d.mod(p1);
+ this.dmq1 = this.d.mod(q1);
+ this.coeff = this.q.modInverse(this.p);
+ break;
+ }
+ }
+}
+
+// Perform raw private operation on "x": return x^d (mod n)
+function RSADoPrivate(x) {
+ if(this.p == null || this.q == null)
+ return x.modPow(this.d, this.n);
+
+ // TODO: re-calculate any missing CRT params
+ var xp = x.mod(this.p).modPow(this.dmp1, this.p);
+ var xq = x.mod(this.q).modPow(this.dmq1, this.q);
+
+ while(xp.compareTo(xq) < 0)
+ xp = xp.add(this.p);
+ return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
+}
+
+// Return the PKCS#1 RSA decryption of "ctext".
+// "ctext" is an even-length hex string and the output is a plain string.
+function RSADecrypt(ctext) {
+ var c = parseBigInt(ctext, 16);
+ var m = this.doPrivate(c);
+ if(m == null) return null;
+ return pkcs1unpad2(m, (this.n.bitLength()+7)>>3);
+}
+
+// Return the PKCS#1 RSA decryption of "ctext".
+// "ctext" is a Base64-encoded string and the output is a plain string.
+//function RSAB64Decrypt(ctext) {
+// var h = b64tohex(ctext);
+// if(h) return this.decrypt(h); else return null;
+//}
+
+// protected
+RSAKey.prototype.doPrivate = RSADoPrivate;
+
+// public
+RSAKey.prototype.setPrivate = RSASetPrivate;
+RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
+RSAKey.prototype.generate = RSAGenerate;
+RSAKey.prototype.decrypt = RSADecrypt;
+//RSAKey.prototype.b64_decrypt = RSAB64Decrypt;
+
+
+nValue="a5261939975948bb7a58dffe5ff54e65f0498f9175f5a09288810b8975871e99af3b5dd94057b0fc07535f5f97444504fa35169d461d0d30cf0192e307727c065168c788771c561a9400fb49175e9e6aa4e23fe11af69e9412dd23b0cb6684c4c2429bce139e848ab26d0829073351f4acd36074eafd036a5eb83359d2a698d3";
+eValue="10001";
+dValue="8e9912f6d3645894e8d38cb58c0db81ff516cf4c7e5a14c7f1eddb1459d2cded4d8d293fc97aee6aefb861859c8b6a3d1dfe710463e1f9ddc72048c09751971c4a580aa51eb523357a3cc48d31cfad1d4a165066ed92d4748fb6571211da5cb14bc11b6e2df7c1a559e6d5ac1cd5c94703a22891464fba23d0d965086277a161";
+pValue="d090ce58a92c75233a6486cb0a9209bf3583b64f540c76f5294bb97d285eed33aec220bde14b2417951178ac152ceab6da7090905b478195498b352048f15e7d";
+qValue="cab575dc652bb66df15a0359609d51d1db184750c00c6698b90ef3465c99655103edbf0d54c56aec0ce3c4d22592338092a126a0cc49f65a4a30d222b411e58f";
+dmp1Value="1a24bca8e273df2f0e47c199bbf678604e7df7215480c77c8db39f49b000ce2cf7500038acfff5433b7d582a01f1826e6f4d42e1c57f5e1fef7b12aabc59fd25";
+dmq1Value="3d06982efbbe47339e1f6d36b1216b8a741d410b0c662f54f7118b27b9a4ec9d914337eb39841d8666f3034408cf94f5b62f11c402fc994fe15a05493150d9fd";
+coeffValue="3a3e731acd8960b7ff9eb81a7ff93bd1cfa74cbd56987db58b4594fb09c09084db1734c8143f98b602b981aaa9243ca28deb69b5b280ee8dcee0fd2625e53250";
+
+setupEngine(am3, 28);
+
+var TEXT = "The quick brown fox jumped over the extremely lazy frog! " +
+ "Now is the time for all good men to come to the party.";
+var encrypted;
+
+function encrypt() {
+ var RSA = new RSAKey();
+ RSA.setPublic(nValue, eValue);
+ RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue);
+ encrypted = RSA.encrypt(TEXT);
+}
+
+function decrypt() {
+ var RSA = new RSAKey();
+ RSA.setPublic(nValue, eValue);
+ RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue);
+ var decrypted = RSA.decrypt(encrypted);
+ if (decrypted != TEXT) {
+ throw new Error("Crypto operation failed");
+ }
+}
diff --git a/chromium/v8/benchmarks/deltablue.js b/chromium/v8/benchmarks/deltablue.js
new file mode 100644
index 00000000000..548fd96ffbd
--- /dev/null
+++ b/chromium/v8/benchmarks/deltablue.js
@@ -0,0 +1,880 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 1996 John Maloney and Mario Wolczko.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// This implementation of the DeltaBlue benchmark is derived
+// from the Smalltalk implementation by John Maloney and Mario
+// Wolczko. Some parts have been translated directly, whereas
+// others have been modified more aggresively to make it feel
+// more like a JavaScript program.
+
+
+var DeltaBlue = new BenchmarkSuite('DeltaBlue', 66118, [
+ new Benchmark('DeltaBlue', deltaBlue)
+]);
+
+
+/**
+ * A JavaScript implementation of the DeltaBlue constraint-solving
+ * algorithm, as described in:
+ *
+ * "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
+ * Bjorn N. Freeman-Benson and John Maloney
+ * January 1990 Communications of the ACM,
+ * also available as University of Washington TR 89-08-06.
+ *
+ * Beware: this benchmark is written in a grotesque style where
+ * the constraint model is built by side-effects from constructors.
+ * I've kept it this way to avoid deviating too much from the original
+ * implementation.
+ */
+
+
+/* --- O b j e c t M o d e l --- */
+
+Object.prototype.inheritsFrom = function (shuper) {
+ function Inheriter() { }
+ Inheriter.prototype = shuper.prototype;
+ this.prototype = new Inheriter();
+ this.superConstructor = shuper;
+}
+
+function OrderedCollection() {
+ this.elms = new Array();
+}
+
+OrderedCollection.prototype.add = function (elm) {
+ this.elms.push(elm);
+}
+
+OrderedCollection.prototype.at = function (index) {
+ return this.elms[index];
+}
+
+OrderedCollection.prototype.size = function () {
+ return this.elms.length;
+}
+
+OrderedCollection.prototype.removeFirst = function () {
+ return this.elms.pop();
+}
+
+OrderedCollection.prototype.remove = function (elm) {
+ var index = 0, skipped = 0;
+ for (var i = 0; i < this.elms.length; i++) {
+ var value = this.elms[i];
+ if (value != elm) {
+ this.elms[index] = value;
+ index++;
+ } else {
+ skipped++;
+ }
+ }
+ for (var i = 0; i < skipped; i++)
+ this.elms.pop();
+}
+
+/* --- *
+ * S t r e n g t h
+ * --- */
+
+/**
+ * Strengths are used to measure the relative importance of constraints.
+ * New strengths may be inserted in the strength hierarchy without
+ * disrupting current constraints. Strengths cannot be created outside
+ * this class, so pointer comparison can be used for value comparison.
+ */
+function Strength(strengthValue, name) {
+ this.strengthValue = strengthValue;
+ this.name = name;
+}
+
+Strength.stronger = function (s1, s2) {
+ return s1.strengthValue < s2.strengthValue;
+}
+
+Strength.weaker = function (s1, s2) {
+ return s1.strengthValue > s2.strengthValue;
+}
+
+Strength.weakestOf = function (s1, s2) {
+ return this.weaker(s1, s2) ? s1 : s2;
+}
+
+Strength.strongest = function (s1, s2) {
+ return this.stronger(s1, s2) ? s1 : s2;
+}
+
+Strength.prototype.nextWeaker = function () {
+ switch (this.strengthValue) {
+ case 0: return Strength.WEAKEST;
+ case 1: return Strength.WEAK_DEFAULT;
+ case 2: return Strength.NORMAL;
+ case 3: return Strength.STRONG_DEFAULT;
+ case 4: return Strength.PREFERRED;
+ case 5: return Strength.REQUIRED;
+ }
+}
+
+// Strength constants.
+Strength.REQUIRED = new Strength(0, "required");
+Strength.STONG_PREFERRED = new Strength(1, "strongPreferred");
+Strength.PREFERRED = new Strength(2, "preferred");
+Strength.STRONG_DEFAULT = new Strength(3, "strongDefault");
+Strength.NORMAL = new Strength(4, "normal");
+Strength.WEAK_DEFAULT = new Strength(5, "weakDefault");
+Strength.WEAKEST = new Strength(6, "weakest");
+
+/* --- *
+ * C o n s t r a i n t
+ * --- */
+
+/**
+ * An abstract class representing a system-maintainable relationship
+ * (or "constraint") between a set of variables. A constraint supplies
+ * a strength instance variable; concrete subclasses provide a means
+ * of storing the constrained variables and other information required
+ * to represent a constraint.
+ */
+function Constraint(strength) {
+ this.strength = strength;
+}
+
+/**
+ * Activate this constraint and attempt to satisfy it.
+ */
+Constraint.prototype.addConstraint = function () {
+ this.addToGraph();
+ planner.incrementalAdd(this);
+}
+
+/**
+ * Attempt to find a way to enforce this constraint. If successful,
+ * record the solution, perhaps modifying the current dataflow
+ * graph. Answer the constraint that this constraint overrides, if
+ * there is one, or nil, if there isn't.
+ * Assume: I am not already satisfied.
+ */
+Constraint.prototype.satisfy = function (mark) {
+ this.chooseMethod(mark);
+ if (!this.isSatisfied()) {
+ if (this.strength == Strength.REQUIRED)
+ alert("Could not satisfy a required constraint!");
+ return null;
+ }
+ this.markInputs(mark);
+ var out = this.output();
+ var overridden = out.determinedBy;
+ if (overridden != null) overridden.markUnsatisfied();
+ out.determinedBy = this;
+ if (!planner.addPropagate(this, mark))
+ alert("Cycle encountered");
+ out.mark = mark;
+ return overridden;
+}
+
+Constraint.prototype.destroyConstraint = function () {
+ if (this.isSatisfied()) planner.incrementalRemove(this);
+ else this.removeFromGraph();
+}
+
+/**
+ * Normal constraints are not input constraints. An input constraint
+ * is one that depends on external state, such as the mouse, the
+ * keybord, a clock, or some arbitraty piece of imperative code.
+ */
+Constraint.prototype.isInput = function () {
+ return false;
+}
+
+/* --- *
+ * U n a r y C o n s t r a i n t
+ * --- */
+
+/**
+ * Abstract superclass for constraints having a single possible output
+ * variable.
+ */
+function UnaryConstraint(v, strength) {
+ UnaryConstraint.superConstructor.call(this, strength);
+ this.myOutput = v;
+ this.satisfied = false;
+ this.addConstraint();
+}
+
+UnaryConstraint.inheritsFrom(Constraint);
+
+/**
+ * Adds this constraint to the constraint graph
+ */
+UnaryConstraint.prototype.addToGraph = function () {
+ this.myOutput.addConstraint(this);
+ this.satisfied = false;
+}
+
+/**
+ * Decides if this constraint can be satisfied and records that
+ * decision.
+ */
+UnaryConstraint.prototype.chooseMethod = function (mark) {
+ this.satisfied = (this.myOutput.mark != mark)
+ && Strength.stronger(this.strength, this.myOutput.walkStrength);
+}
+
+/**
+ * Returns true if this constraint is satisfied in the current solution.
+ */
+UnaryConstraint.prototype.isSatisfied = function () {
+ return this.satisfied;
+}
+
+UnaryConstraint.prototype.markInputs = function (mark) {
+ // has no inputs
+}
+
+/**
+ * Returns the current output variable.
+ */
+UnaryConstraint.prototype.output = function () {
+ return this.myOutput;
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this constraint. Assume
+ * this constraint is satisfied.
+ */
+UnaryConstraint.prototype.recalculate = function () {
+ this.myOutput.walkStrength = this.strength;
+ this.myOutput.stay = !this.isInput();
+ if (this.myOutput.stay) this.execute(); // Stay optimization
+}
+
+/**
+ * Records that this constraint is unsatisfied
+ */
+UnaryConstraint.prototype.markUnsatisfied = function () {
+ this.satisfied = false;
+}
+
+UnaryConstraint.prototype.inputsKnown = function () {
+ return true;
+}
+
+UnaryConstraint.prototype.removeFromGraph = function () {
+ if (this.myOutput != null) this.myOutput.removeConstraint(this);
+ this.satisfied = false;
+}
+
+/* --- *
+ * S t a y C o n s t r a i n t
+ * --- */
+
+/**
+ * Variables that should, with some level of preference, stay the same.
+ * Planners may exploit the fact that instances, if satisfied, will not
+ * change their output during plan execution. This is called "stay
+ * optimization".
+ */
+function StayConstraint(v, str) {
+ StayConstraint.superConstructor.call(this, v, str);
+}
+
+StayConstraint.inheritsFrom(UnaryConstraint);
+
+StayConstraint.prototype.execute = function () {
+ // Stay constraints do nothing
+}
+
+/* --- *
+ * E d i t C o n s t r a i n t
+ * --- */
+
+/**
+ * A unary input constraint used to mark a variable that the client
+ * wishes to change.
+ */
+function EditConstraint(v, str) {
+ EditConstraint.superConstructor.call(this, v, str);
+}
+
+EditConstraint.inheritsFrom(UnaryConstraint);
+
+/**
+ * Edits indicate that a variable is to be changed by imperative code.
+ */
+EditConstraint.prototype.isInput = function () {
+ return true;
+}
+
+EditConstraint.prototype.execute = function () {
+ // Edit constraints do nothing
+}
+
+/* --- *
+ * B i n a r y C o n s t r a i n t
+ * --- */
+
+var Direction = new Object();
+Direction.NONE = 0;
+Direction.FORWARD = 1;
+Direction.BACKWARD = -1;
+
+/**
+ * Abstract superclass for constraints having two possible output
+ * variables.
+ */
+function BinaryConstraint(var1, var2, strength) {
+ BinaryConstraint.superConstructor.call(this, strength);
+ this.v1 = var1;
+ this.v2 = var2;
+ this.direction = Direction.NONE;
+ this.addConstraint();
+}
+
+BinaryConstraint.inheritsFrom(Constraint);
+
+/**
+ * Decides if this constraint can be satisfied and which way it
+ * should flow based on the relative strength of the variables related,
+ * and record that decision.
+ */
+BinaryConstraint.prototype.chooseMethod = function (mark) {
+ if (this.v1.mark == mark) {
+ this.direction = (this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength))
+ ? Direction.FORWARD
+ : Direction.NONE;
+ }
+ if (this.v2.mark == mark) {
+ this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength))
+ ? Direction.BACKWARD
+ : Direction.NONE;
+ }
+ if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) {
+ this.direction = Strength.stronger(this.strength, this.v1.walkStrength)
+ ? Direction.BACKWARD
+ : Direction.NONE;
+ } else {
+ this.direction = Strength.stronger(this.strength, this.v2.walkStrength)
+ ? Direction.FORWARD
+ : Direction.BACKWARD
+ }
+}
+
+/**
+ * Add this constraint to the constraint graph
+ */
+BinaryConstraint.prototype.addToGraph = function () {
+ this.v1.addConstraint(this);
+ this.v2.addConstraint(this);
+ this.direction = Direction.NONE;
+}
+
+/**
+ * Answer true if this constraint is satisfied in the current solution.
+ */
+BinaryConstraint.prototype.isSatisfied = function () {
+ return this.direction != Direction.NONE;
+}
+
+/**
+ * Mark the input variable with the given mark.
+ */
+BinaryConstraint.prototype.markInputs = function (mark) {
+ this.input().mark = mark;
+}
+
+/**
+ * Returns the current input variable
+ */
+BinaryConstraint.prototype.input = function () {
+ return (this.direction == Direction.FORWARD) ? this.v1 : this.v2;
+}
+
+/**
+ * Returns the current output variable
+ */
+BinaryConstraint.prototype.output = function () {
+ return (this.direction == Direction.FORWARD) ? this.v2 : this.v1;
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this
+ * constraint. Assume this constraint is satisfied.
+ */
+BinaryConstraint.prototype.recalculate = function () {
+ var ihn = this.input(), out = this.output();
+ out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
+ out.stay = ihn.stay;
+ if (out.stay) this.execute();
+}
+
+/**
+ * Record the fact that this constraint is unsatisfied.
+ */
+BinaryConstraint.prototype.markUnsatisfied = function () {
+ this.direction = Direction.NONE;
+}
+
+BinaryConstraint.prototype.inputsKnown = function (mark) {
+ var i = this.input();
+ return i.mark == mark || i.stay || i.determinedBy == null;
+}
+
+BinaryConstraint.prototype.removeFromGraph = function () {
+ if (this.v1 != null) this.v1.removeConstraint(this);
+ if (this.v2 != null) this.v2.removeConstraint(this);
+ this.direction = Direction.NONE;
+}
+
+/* --- *
+ * S c a l e C o n s t r a i n t
+ * --- */
+
+/**
+ * Relates two variables by the linear scaling relationship: "v2 =
+ * (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
+ * this relationship but the scale factor and offset are considered
+ * read-only.
+ */
+function ScaleConstraint(src, scale, offset, dest, strength) {
+ this.direction = Direction.NONE;
+ this.scale = scale;
+ this.offset = offset;
+ ScaleConstraint.superConstructor.call(this, src, dest, strength);
+}
+
+ScaleConstraint.inheritsFrom(BinaryConstraint);
+
+/**
+ * Adds this constraint to the constraint graph.
+ */
+ScaleConstraint.prototype.addToGraph = function () {
+ ScaleConstraint.superConstructor.prototype.addToGraph.call(this);
+ this.scale.addConstraint(this);
+ this.offset.addConstraint(this);
+}
+
+ScaleConstraint.prototype.removeFromGraph = function () {
+ ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this);
+ if (this.scale != null) this.scale.removeConstraint(this);
+ if (this.offset != null) this.offset.removeConstraint(this);
+}
+
+ScaleConstraint.prototype.markInputs = function (mark) {
+ ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark);
+ this.scale.mark = this.offset.mark = mark;
+}
+
+/**
+ * Enforce this constraint. Assume that it is satisfied.
+ */
+ScaleConstraint.prototype.execute = function () {
+ if (this.direction == Direction.FORWARD) {
+ this.v2.value = this.v1.value * this.scale.value + this.offset.value;
+ } else {
+ this.v1.value = (this.v2.value - this.offset.value) / this.scale.value;
+ }
+}
+
+/**
+ * Calculate the walkabout strength, the stay flag, and, if it is
+ * 'stay', the value for the current output of this constraint. Assume
+ * this constraint is satisfied.
+ */
+ScaleConstraint.prototype.recalculate = function () {
+ var ihn = this.input(), out = this.output();
+ out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
+ out.stay = ihn.stay && this.scale.stay && this.offset.stay;
+ if (out.stay) this.execute();
+}
+
+/* --- *
+ * E q u a l i t y C o n s t r a i n t
+ * --- */
+
+/**
+ * Constrains two variables to have the same value.
+ */
+function EqualityConstraint(var1, var2, strength) {
+ EqualityConstraint.superConstructor.call(this, var1, var2, strength);
+}
+
+EqualityConstraint.inheritsFrom(BinaryConstraint);
+
+/**
+ * Enforce this constraint. Assume that it is satisfied.
+ */
+EqualityConstraint.prototype.execute = function () {
+ this.output().value = this.input().value;
+}
+
+/* --- *
+ * V a r i a b l e
+ * --- */
+
+/**
+ * A constrained variable. In addition to its value, it maintain the
+ * structure of the constraint graph, the current dataflow graph, and
+ * various parameters of interest to the DeltaBlue incremental
+ * constraint solver.
+ **/
+function Variable(name, initialValue) {
+ this.value = initialValue || 0;
+ this.constraints = new OrderedCollection();
+ this.determinedBy = null;
+ this.mark = 0;
+ this.walkStrength = Strength.WEAKEST;
+ this.stay = true;
+ this.name = name;
+}
+
+/**
+ * Add the given constraint to the set of all constraints that refer
+ * this variable.
+ */
+Variable.prototype.addConstraint = function (c) {
+ this.constraints.add(c);
+}
+
+/**
+ * Removes all traces of c from this variable.
+ */
+Variable.prototype.removeConstraint = function (c) {
+ this.constraints.remove(c);
+ if (this.determinedBy == c) this.determinedBy = null;
+}
+
+/* --- *
+ * P l a n n e r
+ * --- */
+
+/**
+ * The DeltaBlue planner
+ */
+function Planner() {
+ this.currentMark = 0;
+}
+
+/**
+ * Attempt to satisfy the given constraint and, if successful,
+ * incrementally update the dataflow graph. Details: If satifying
+ * the constraint is successful, it may override a weaker constraint
+ * on its output. The algorithm attempts to resatisfy that
+ * constraint using some other method. This process is repeated
+ * until either a) it reaches a variable that was not previously
+ * determined by any constraint or b) it reaches a constraint that
+ * is too weak to be satisfied using any of its methods. The
+ * variables of constraints that have been processed are marked with
+ * a unique mark value so that we know where we've been. This allows
+ * the algorithm to avoid getting into an infinite loop even if the
+ * constraint graph has an inadvertent cycle.
+ */
+Planner.prototype.incrementalAdd = function (c) {
+ var mark = this.newMark();
+ var overridden = c.satisfy(mark);
+ while (overridden != null)
+ overridden = overridden.satisfy(mark);
+}
+
+/**
+ * Entry point for retracting a constraint. Remove the given
+ * constraint and incrementally update the dataflow graph.
+ * Details: Retracting the given constraint may allow some currently
+ * unsatisfiable downstream constraint to be satisfied. We therefore collect
+ * a list of unsatisfied downstream constraints and attempt to
+ * satisfy each one in turn. This list is traversed by constraint
+ * strength, strongest first, as a heuristic for avoiding
+ * unnecessarily adding and then overriding weak constraints.
+ * Assume: c is satisfied.
+ */
+Planner.prototype.incrementalRemove = function (c) {
+ var out = c.output();
+ c.markUnsatisfied();
+ c.removeFromGraph();
+ var unsatisfied = this.removePropagateFrom(out);
+ var strength = Strength.REQUIRED;
+ do {
+ for (var i = 0; i < unsatisfied.size(); i++) {
+ var u = unsatisfied.at(i);
+ if (u.strength == strength)
+ this.incrementalAdd(u);
+ }
+ strength = strength.nextWeaker();
+ } while (strength != Strength.WEAKEST);
+}
+
+/**
+ * Select a previously unused mark value.
+ */
+Planner.prototype.newMark = function () {
+ return ++this.currentMark;
+}
+
+/**
+ * Extract a plan for resatisfaction starting from the given source
+ * constraints, usually a set of input constraints. This method
+ * assumes that stay optimization is desired; the plan will contain
+ * only constraints whose output variables are not stay. Constraints
+ * that do no computation, such as stay and edit constraints, are
+ * not included in the plan.
+ * Details: The outputs of a constraint are marked when it is added
+ * to the plan under construction. A constraint may be appended to
+ * the plan when all its input variables are known. A variable is
+ * known if either a) the variable is marked (indicating that has
+ * been computed by a constraint appearing earlier in the plan), b)
+ * the variable is 'stay' (i.e. it is a constant at plan execution
+ * time), or c) the variable is not determined by any
+ * constraint. The last provision is for past states of history
+ * variables, which are not stay but which are also not computed by
+ * any constraint.
+ * Assume: sources are all satisfied.
+ */
+Planner.prototype.makePlan = function (sources) {
+ var mark = this.newMark();
+ var plan = new Plan();
+ var todo = sources;
+ while (todo.size() > 0) {
+ var c = todo.removeFirst();
+ if (c.output().mark != mark && c.inputsKnown(mark)) {
+ plan.addConstraint(c);
+ c.output().mark = mark;
+ this.addConstraintsConsumingTo(c.output(), todo);
+ }
+ }
+ return plan;
+}
+
+/**
+ * Extract a plan for resatisfying starting from the output of the
+ * given constraints, usually a set of input constraints.
+ */
+Planner.prototype.extractPlanFromConstraints = function (constraints) {
+ var sources = new OrderedCollection();
+ for (var i = 0; i < constraints.size(); i++) {
+ var c = constraints.at(i);
+ if (c.isInput() && c.isSatisfied())
+ // not in plan already and eligible for inclusion
+ sources.add(c);
+ }
+ return this.makePlan(sources);
+}
+
+/**
+ * Recompute the walkabout strengths and stay flags of all variables
+ * downstream of the given constraint and recompute the actual
+ * values of all variables whose stay flag is true. If a cycle is
+ * detected, remove the given constraint and answer
+ * false. Otherwise, answer true.
+ * Details: Cycles are detected when a marked variable is
+ * encountered downstream of the given constraint. The sender is
+ * assumed to have marked the inputs of the given constraint with
+ * the given mark. Thus, encountering a marked node downstream of
+ * the output constraint means that there is a path from the
+ * constraint's output to one of its inputs.
+ */
+Planner.prototype.addPropagate = function (c, mark) {
+ var todo = new OrderedCollection();
+ todo.add(c);
+ while (todo.size() > 0) {
+ var d = todo.removeFirst();
+ if (d.output().mark == mark) {
+ this.incrementalRemove(c);
+ return false;
+ }
+ d.recalculate();
+ this.addConstraintsConsumingTo(d.output(), todo);
+ }
+ return true;
+}
+
+
+/**
+ * Update the walkabout strengths and stay flags of all variables
+ * downstream of the given constraint. Answer a collection of
+ * unsatisfied constraints sorted in order of decreasing strength.
+ */
+Planner.prototype.removePropagateFrom = function (out) {
+ out.determinedBy = null;
+ out.walkStrength = Strength.WEAKEST;
+ out.stay = true;
+ var unsatisfied = new OrderedCollection();
+ var todo = new OrderedCollection();
+ todo.add(out);
+ while (todo.size() > 0) {
+ var v = todo.removeFirst();
+ for (var i = 0; i < v.constraints.size(); i++) {
+ var c = v.constraints.at(i);
+ if (!c.isSatisfied())
+ unsatisfied.add(c);
+ }
+ var determining = v.determinedBy;
+ for (var i = 0; i < v.constraints.size(); i++) {
+ var next = v.constraints.at(i);
+ if (next != determining && next.isSatisfied()) {
+ next.recalculate();
+ todo.add(next.output());
+ }
+ }
+ }
+ return unsatisfied;
+}
+
+Planner.prototype.addConstraintsConsumingTo = function (v, coll) {
+ var determining = v.determinedBy;
+ var cc = v.constraints;
+ for (var i = 0; i < cc.size(); i++) {
+ var c = cc.at(i);
+ if (c != determining && c.isSatisfied())
+ coll.add(c);
+ }
+}
+
+/* --- *
+ * P l a n
+ * --- */
+
+/**
+ * A Plan is an ordered list of constraints to be executed in sequence
+ * to resatisfy all currently satisfiable constraints in the face of
+ * one or more changing inputs.
+ */
+function Plan() {
+ this.v = new OrderedCollection();
+}
+
+Plan.prototype.addConstraint = function (c) {
+ this.v.add(c);
+}
+
+Plan.prototype.size = function () {
+ return this.v.size();
+}
+
+Plan.prototype.constraintAt = function (index) {
+ return this.v.at(index);
+}
+
+Plan.prototype.execute = function () {
+ for (var i = 0; i < this.size(); i++) {
+ var c = this.constraintAt(i);
+ c.execute();
+ }
+}
+
+/* --- *
+ * M a i n
+ * --- */
+
+/**
+ * This is the standard DeltaBlue benchmark. A long chain of equality
+ * constraints is constructed with a stay constraint on one end. An
+ * edit constraint is then added to the opposite end and the time is
+ * measured for adding and removing this constraint, and extracting
+ * and executing a constraint satisfaction plan. There are two cases.
+ * In case 1, the added constraint is stronger than the stay
+ * constraint and values must propagate down the entire length of the
+ * chain. In case 2, the added constraint is weaker than the stay
+ * constraint so it cannot be accomodated. The cost in this case is,
+ * of course, very low. Typical situations lie somewhere between these
+ * two extremes.
+ */
+function chainTest(n) {
+ planner = new Planner();
+ var prev = null, first = null, last = null;
+
+ // Build chain of n equality constraints
+ for (var i = 0; i <= n; i++) {
+ var name = "v" + i;
+ var v = new Variable(name);
+ if (prev != null)
+ new EqualityConstraint(prev, v, Strength.REQUIRED);
+ if (i == 0) first = v;
+ if (i == n) last = v;
+ prev = v;
+ }
+
+ new StayConstraint(last, Strength.STRONG_DEFAULT);
+ var edit = new EditConstraint(first, Strength.PREFERRED);
+ var edits = new OrderedCollection();
+ edits.add(edit);
+ var plan = planner.extractPlanFromConstraints(edits);
+ for (var i = 0; i < 100; i++) {
+ first.value = i;
+ plan.execute();
+ if (last.value != i)
+ alert("Chain test failed.");
+ }
+}
+
+/**
+ * This test constructs a two sets of variables related to each
+ * other by a simple linear transformation (scale and offset). The
+ * time is measured to change a variable on either side of the
+ * mapping and to change the scale and offset factors.
+ */
+function projectionTest(n) {
+ planner = new Planner();
+ var scale = new Variable("scale", 10);
+ var offset = new Variable("offset", 1000);
+ var src = null, dst = null;
+
+ var dests = new OrderedCollection();
+ for (var i = 0; i < n; i++) {
+ src = new Variable("src" + i, i);
+ dst = new Variable("dst" + i, i);
+ dests.add(dst);
+ new StayConstraint(src, Strength.NORMAL);
+ new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED);
+ }
+
+ change(src, 17);
+ if (dst.value != 1170) alert("Projection 1 failed");
+ change(dst, 1050);
+ if (src.value != 5) alert("Projection 2 failed");
+ change(scale, 5);
+ for (var i = 0; i < n - 1; i++) {
+ if (dests.at(i).value != i * 5 + 1000)
+ alert("Projection 3 failed");
+ }
+ change(offset, 2000);
+ for (var i = 0; i < n - 1; i++) {
+ if (dests.at(i).value != i * 5 + 2000)
+ alert("Projection 4 failed");
+ }
+}
+
+function change(v, newValue) {
+ var edit = new EditConstraint(v, Strength.PREFERRED);
+ var edits = new OrderedCollection();
+ edits.add(edit);
+ var plan = planner.extractPlanFromConstraints(edits);
+ for (var i = 0; i < 10; i++) {
+ v.value = newValue;
+ plan.execute();
+ }
+ edit.destroyConstraint();
+}
+
+// Global variable holding the current planner.
+var planner = null;
+
+function deltaBlue() {
+ chainTest(100);
+ projectionTest(100);
+}
diff --git a/chromium/v8/benchmarks/earley-boyer.js b/chromium/v8/benchmarks/earley-boyer.js
new file mode 100644
index 00000000000..b2328d6ba1d
--- /dev/null
+++ b/chromium/v8/benchmarks/earley-boyer.js
@@ -0,0 +1,4684 @@
+// This file is automatically generated by scheme2js, except for the
+// benchmark harness code at the beginning and end of the file.
+
+var EarleyBoyer = new BenchmarkSuite('EarleyBoyer', 666463, [
+ new Benchmark("Earley", function () { BgL_earleyzd2benchmarkzd2(); }),
+ new Benchmark("Boyer", function () { BgL_nboyerzd2benchmarkzd2(); })
+]);
+
+
+/************* GENERATED FILE - DO NOT EDIT *************/
+/************* GENERATED FILE - DO NOT EDIT *************/
+/************* GENERATED FILE - DO NOT EDIT *************/
+/************* GENERATED FILE - DO NOT EDIT *************/
+/************* GENERATED FILE - DO NOT EDIT *************/
+/************* GENERATED FILE - DO NOT EDIT *************/
+/************* GENERATED FILE - DO NOT EDIT *************/
+/************* GENERATED FILE - DO NOT EDIT *************/
+/*
+ * To use write/prints/... the default-output port has to be set first.
+ * Simply setting SC_DEFAULT_OUT and SC_ERROR_OUT to the desired values
+ * should do the trick.
+ * In the following example the std-out and error-port are redirected to
+ * a DIV.
+function initRuntime() {
+ function escapeHTML(s) {
+ var tmp = s;
+ tmp = tmp.replace(/&/g, "&amp;");
+ tmp = tmp.replace(/</g, "&lt;");
+ tmp = tmp.replace(/>/g, "&gt;");
+ tmp = tmp.replace(/ /g, "&nbsp;");
+ tmp = tmp.replace(/\n/g, "<br />");
+ tmp = tmp.replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp");
+ return tmp;
+
+ }
+
+ document.write("<div id='stdout'></div>");
+ SC_DEFAULT_OUT = new sc_GenericOutputPort(
+ function(s) {
+ var stdout = document.getElementById('stdout');
+ stdout.innerHTML = stdout.innerHTML + escapeHTML(s);
+ });
+ SC_ERROR_OUT = SC_DEFAULT_OUT;
+}
+*/
+
+
+function sc_print_debug() {
+ sc_print.apply(null, arguments);
+}
+/*** META ((export *js*)) */
+var sc_JS_GLOBALS = this;
+
+var __sc_LINE=-1;
+var __sc_FILE="";
+
+/*** META ((export #t)) */
+function sc_alert() {
+ var len = arguments.length;
+ var s = "";
+ var i;
+
+ for( i = 0; i < len; i++ ) {
+ s += sc_toDisplayString(arguments[ i ]);
+ }
+
+ return alert( s );
+}
+
+/*** META ((export #t)) */
+function sc_typeof( x ) {
+ return typeof x;
+}
+
+/*** META ((export #t)) */
+function sc_error() {
+ var a = [sc_jsstring2symbol("*error*")];
+ for (var i = 0; i < arguments.length; i++) {
+ a[i+1] = arguments[i];
+ }
+ throw a;
+}
+
+/*** META ((export #t)
+ (peephole (prefix "throw ")))
+*/
+function sc_raise(obj) {
+ throw obj;
+}
+
+/*** META ((export with-handler-lambda)) */
+function sc_withHandlerLambda(handler, body) {
+ try {
+ return body();
+ } catch(e) {
+ if (!e._internalException)
+ return handler(e);
+ else
+ throw e;
+ }
+}
+
+var sc_properties = new Object();
+
+/*** META ((export #t)) */
+function sc_putpropBang(sym, key, val) {
+ var ht = sc_properties[sym];
+ if (!ht) {
+ ht = new Object();
+ sc_properties[sym] = ht;
+ }
+ ht[key] = val;
+}
+
+/*** META ((export #t)) */
+function sc_getprop(sym, key) {
+ var ht = sc_properties[sym];
+ if (ht) {
+ if (key in ht)
+ return ht[key];
+ else
+ return false;
+ } else
+ return false;
+}
+
+/*** META ((export #t)) */
+function sc_rempropBang(sym, key) {
+ var ht = sc_properties[sym];
+ if (ht)
+ delete ht[key];
+}
+
+/*** META ((export #t)) */
+function sc_any2String(o) {
+ return jsstring2string(sc_toDisplayString(o));
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 "==="))
+ (type bool))
+*/
+function sc_isEqv(o1, o2) {
+ return (o1 === o2);
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 "==="))
+ (type bool))
+*/
+function sc_isEq(o1, o2) {
+ return (o1 === o2);
+}
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isNumber(n) {
+ return (typeof n === "number");
+}
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isComplex(n) {
+ return sc_isNumber(n);
+}
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isReal(n) {
+ return sc_isNumber(n);
+}
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isRational(n) {
+ return sc_isReal(n);
+}
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isInteger(n) {
+ return (parseInt(n) === n);
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix ", false")))
+*/
+// we don't have exact numbers...
+function sc_isExact(n) {
+ return false;
+}
+
+/*** META ((export #t)
+ (peephole (postfix ", true"))
+ (type bool))
+*/
+function sc_isInexact(n) {
+ return true;
+}
+
+/*** META ((export = =fx =fl)
+ (type bool)
+ (peephole (infix 2 2 "===")))
+*/
+function sc_equal(x) {
+ for (var i = 1; i < arguments.length; i++)
+ if (x !== arguments[i])
+ return false;
+ return true;
+}
+
+/*** META ((export < <fx <fl)
+ (type bool)
+ (peephole (infix 2 2 "<")))
+*/
+function sc_less(x) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (x >= arguments[i])
+ return false;
+ x = arguments[i];
+ }
+ return true;
+}
+
+/*** META ((export > >fx >fl)
+ (type bool)
+ (peephole (infix 2 2 ">")))
+*/
+function sc_greater(x, y) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (x <= arguments[i])
+ return false;
+ x = arguments[i];
+ }
+ return true;
+}
+
+/*** META ((export <= <=fx <=fl)
+ (type bool)
+ (peephole (infix 2 2 "<=")))
+*/
+function sc_lessEqual(x, y) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (x > arguments[i])
+ return false;
+ x = arguments[i];
+ }
+ return true;
+}
+
+/*** META ((export >= >=fl >=fx)
+ (type bool)
+ (peephole (infix 2 2 ">=")))
+*/
+function sc_greaterEqual(x, y) {
+ for (var i = 1; i < arguments.length; i++) {
+ if (x < arguments[i])
+ return false;
+ x = arguments[i];
+ }
+ return true;
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix "=== 0")))
+*/
+function sc_isZero(x) {
+ return (x === 0);
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix "> 0")))
+*/
+function sc_isPositive(x) {
+ return (x > 0);
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix "< 0")))
+*/
+function sc_isNegative(x) {
+ return (x < 0);
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix "%2===1")))
+*/
+function sc_isOdd(x) {
+ return (x % 2 === 1);
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix "%2===0")))
+*/
+function sc_isEven(x) {
+ return (x % 2 === 0);
+}
+
+/*** META ((export #t)) */
+var sc_max = Math.max;
+/*** META ((export #t)) */
+var sc_min = Math.min;
+
+/*** META ((export + +fx +fl)
+ (peephole (infix 0 #f "+" "0")))
+*/
+function sc_plus() {
+ var sum = 0;
+ for (var i = 0; i < arguments.length; i++)
+ sum += arguments[i];
+ return sum;
+}
+
+/*** META ((export * *fx *fl)
+ (peephole (infix 0 #f "*" "1")))
+*/
+function sc_multi() {
+ var product = 1;
+ for (var i = 0; i < arguments.length; i++)
+ product *= arguments[i];
+ return product;
+}
+
+/*** META ((export - -fx -fl)
+ (peephole (minus)))
+*/
+function sc_minus(x) {
+ if (arguments.length === 1)
+ return -x;
+ else {
+ var res = x;
+ for (var i = 1; i < arguments.length; i++)
+ res -= arguments[i];
+ return res;
+ }
+}
+
+/*** META ((export / /fl)
+ (peephole (div)))
+*/
+function sc_div(x) {
+ if (arguments.length === 1)
+ return 1/x;
+ else {
+ var res = x;
+ for (var i = 1; i < arguments.length; i++)
+ res /= arguments[i];
+ return res;
+ }
+}
+
+/*** META ((export #t)) */
+var sc_abs = Math.abs;
+
+/*** META ((export quotient /fx)
+ (peephole (hole 2 "parseInt(" x "/" y ")")))
+*/
+function sc_quotient(x, y) {
+ return parseInt(x / y);
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 "%")))
+*/
+function sc_remainder(x, y) {
+ return x % y;
+}
+
+/*** META ((export #t)
+ (peephole (modulo)))
+*/
+function sc_modulo(x, y) {
+ var remainder = x % y;
+ // if they don't have the same sign
+ if ((remainder * y) < 0)
+ return remainder + y;
+ else
+ return remainder;
+}
+
+function sc_euclid_gcd(a, b) {
+ var temp;
+ if (a === 0) return b;
+ if (b === 0) return a;
+ if (a < 0) {a = -a;};
+ if (b < 0) {b = -b;};
+ if (b > a) {temp = a; a = b; b = temp;};
+ while (true) {
+ a %= b;
+ if(a === 0) {return b;};
+ b %= a;
+ if(b === 0) {return a;};
+ };
+ return b;
+}
+
+/*** META ((export #t)) */
+function sc_gcd() {
+ var gcd = 0;
+ for (var i = 0; i < arguments.length; i++)
+ gcd = sc_euclid_gcd(gcd, arguments[i]);
+ return gcd;
+}
+
+/*** META ((export #t)) */
+function sc_lcm() {
+ var lcm = 1;
+ for (var i = 0; i < arguments.length; i++) {
+ var f = Math.round(arguments[i] / sc_euclid_gcd(arguments[i], lcm));
+ lcm *= Math.abs(f);
+ }
+ return lcm;
+}
+
+// LIMITATION: numerator and denominator don't make sense in floating point world.
+//var SC_MAX_DECIMALS = 1000000
+//
+// function sc_numerator(x) {
+// var rounded = Math.round(x * SC_MAX_DECIMALS);
+// return Math.round(rounded / sc_euclid_gcd(rounded, SC_MAX_DECIMALS));
+// }
+
+// function sc_denominator(x) {
+// var rounded = Math.round(x * SC_MAX_DECIMALS);
+// return Math.round(SC_MAX_DECIMALS / sc_euclid_gcd(rounded, SC_MAX_DECIMALS));
+// }
+
+/*** META ((export #t)) */
+var sc_floor = Math.floor;
+/*** META ((export #t)) */
+var sc_ceiling = Math.ceil;
+/*** META ((export #t)) */
+var sc_truncate = parseInt;
+/*** META ((export #t)) */
+var sc_round = Math.round;
+
+// LIMITATION: sc_rationalize doesn't make sense in a floating point world.
+
+/*** META ((export #t)) */
+var sc_exp = Math.exp;
+/*** META ((export #t)) */
+var sc_log = Math.log;
+/*** META ((export #t)) */
+var sc_sin = Math.sin;
+/*** META ((export #t)) */
+var sc_cos = Math.cos;
+/*** META ((export #t)) */
+var sc_tan = Math.tan;
+/*** META ((export #t)) */
+var sc_asin = Math.asin;
+/*** META ((export #t)) */
+var sc_acos = Math.acos;
+/*** META ((export #t)) */
+var sc_atan = Math.atan;
+
+/*** META ((export #t)) */
+var sc_sqrt = Math.sqrt;
+/*** META ((export #t)) */
+var sc_expt = Math.pow;
+
+// LIMITATION: we don't have complex numbers.
+// LIMITATION: the following functions are hence not implemented.
+// LIMITATION: make-rectangular, make-polar, real-part, imag-part, magnitude, angle
+// LIMITATION: 2 argument atan
+
+/*** META ((export #t)
+ (peephole (id)))
+*/
+function sc_exact2inexact(x) {
+ return x;
+}
+
+/*** META ((export #t)
+ (peephole (id)))
+*/
+function sc_inexact2exact(x) {
+ return x;
+}
+
+function sc_number2jsstring(x, radix) {
+ if (radix)
+ return x.toString(radix);
+ else
+ return x.toString();
+}
+
+function sc_jsstring2number(s, radix) {
+ if (s === "") return false;
+
+ if (radix) {
+ var t = parseInt(s, radix);
+ if (!t && t !== 0) return false;
+ // verify that each char is in range. (parseInt ignores leading
+ // white and trailing chars)
+ var allowedChars = "01234567890abcdefghijklmnopqrstuvwxyz".substring(0, radix+1);
+ if ((new RegExp("^["+allowedChars+"]*$", "i")).test(s))
+ return t;
+ else return false;
+ } else {
+ var t = +s; // does not ignore trailing chars.
+ if (!t && t !== 0) return false;
+ // simply verify that first char is not whitespace.
+ var c = s.charAt(0);
+ // if +c is 0, but the char is not "0", then we have a whitespace.
+ if (+c === 0 && c !== "0") return false;
+ return t;
+ }
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (not)))
+*/
+function sc_not(b) {
+ return b === false;
+}
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isBoolean(b) {
+ return (b === true) || (b === false);
+}
+
+function sc_Pair(car, cdr) {
+ this.car = car;
+ this.cdr = cdr;
+}
+
+sc_Pair.prototype.toString = function() {
+ return sc_toDisplayString(this);
+};
+sc_Pair.prototype.sc_toWriteOrDisplayString = function(writeOrDisplay) {
+ var current = this;
+
+ var res = "(";
+
+ while(true) {
+ res += writeOrDisplay(current.car);
+ if (sc_isPair(current.cdr)) {
+ res += " ";
+ current = current.cdr;
+ } else if (current.cdr !== null) {
+ res += " . " + writeOrDisplay(current.cdr);
+ break;
+ } else // current.cdr == null
+ break;
+ }
+
+ res += ")";
+
+ return res;
+};
+sc_Pair.prototype.sc_toDisplayString = function() {
+ return this.sc_toWriteOrDisplayString(sc_toDisplayString);
+};
+sc_Pair.prototype.sc_toWriteString = function() {
+ return this.sc_toWriteOrDisplayString(sc_toWriteString);
+};
+// sc_Pair.prototype.sc_toWriteCircleString in IO.js
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix " instanceof sc_Pair")))
+*/
+function sc_isPair(p) {
+ return (p instanceof sc_Pair);
+}
+
+function sc_isPairEqual(p1, p2, comp) {
+ return (comp(p1.car, p2.car) && comp(p1.cdr, p2.cdr));
+}
+
+/*** META ((export #t)
+ (peephole (hole 2 "new sc_Pair(" car ", " cdr ")")))
+*/
+function sc_cons(car, cdr) {
+ return new sc_Pair(car, cdr);
+}
+
+/*** META ((export cons*)) */
+function sc_consStar() {
+ var res = arguments[arguments.length - 1];
+ for (var i = arguments.length-2; i >= 0; i--)
+ res = new sc_Pair(arguments[i], res);
+ return res;
+}
+
+/*** META ((export #t)
+ (peephole (postfix ".car")))
+*/
+function sc_car(p) {
+ return p.car;
+}
+
+/*** META ((export #t)
+ (peephole (postfix ".cdr")))
+*/
+function sc_cdr(p) {
+ return p.cdr;
+}
+
+/*** META ((export #t)
+ (peephole (hole 2 p ".car = " val)))
+*/
+function sc_setCarBang(p, val) {
+ p.car = val;
+}
+
+/*** META ((export #t)
+ (peephole (hole 2 p ".cdr = " val)))
+*/
+function sc_setCdrBang(p, val) {
+ p.cdr = val;
+}
+
+/*** META ((export #t)
+ (peephole (postfix ".car.car")))
+*/
+function sc_caar(p) { return p.car.car; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.car")))
+*/
+function sc_cadr(p) { return p.cdr.car; }
+/*** META ((export #t)
+ (peephole (postfix ".car.cdr")))
+*/
+function sc_cdar(p) { return p.car.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.cdr")))
+*/
+function sc_cddr(p) { return p.cdr.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".car.car.car")))
+*/
+function sc_caaar(p) { return p.car.car.car; }
+/*** META ((export #t)
+ (peephole (postfix ".car.cdr.car")))
+*/
+function sc_cadar(p) { return p.car.cdr.car; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.car.car")))
+*/
+function sc_caadr(p) { return p.cdr.car.car; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.cdr.car")))
+*/
+function sc_caddr(p) { return p.cdr.cdr.car; }
+/*** META ((export #t)
+ (peephole (postfix ".car.car.cdr")))
+*/
+function sc_cdaar(p) { return p.car.car.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.car.cdr")))
+*/
+function sc_cdadr(p) { return p.cdr.car.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".car.cdr.cdr")))
+*/
+function sc_cddar(p) { return p.car.cdr.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.cdr.cdr")))
+*/
+function sc_cdddr(p) { return p.cdr.cdr.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".car.car.car.car")))
+*/
+function sc_caaaar(p) { return p.car.car.car.car; }
+/*** META ((export #t)
+ (peephole (postfix ".car.cdr.car.car")))
+*/
+function sc_caadar(p) { return p.car.cdr.car.car; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.car.car.car")))
+*/
+function sc_caaadr(p) { return p.cdr.car.car.car; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.cdr.car.car")))
+*/
+function sc_caaddr(p) { return p.cdr.cdr.car.car; }
+/*** META ((export #t)
+ (peephole (postfix ".car.car.car.cdr")))
+*/
+function sc_cdaaar(p) { return p.car.car.car.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".car.cdr.car.cdr")))
+*/
+function sc_cdadar(p) { return p.car.cdr.car.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.car.car.cdr")))
+*/
+function sc_cdaadr(p) { return p.cdr.car.car.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.cdr.car.cdr")))
+*/
+function sc_cdaddr(p) { return p.cdr.cdr.car.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".car.car.cdr.car")))
+*/
+function sc_cadaar(p) { return p.car.car.cdr.car; }
+/*** META ((export #t)
+ (peephole (postfix ".car.cdr.cdr.car")))
+*/
+function sc_caddar(p) { return p.car.cdr.cdr.car; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.car.cdr.car")))
+*/
+function sc_cadadr(p) { return p.cdr.car.cdr.car; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.cdr.cdr.car")))
+*/
+function sc_cadddr(p) { return p.cdr.cdr.cdr.car; }
+/*** META ((export #t)
+ (peephole (postfix ".car.car.cdr.cdr")))
+*/
+function sc_cddaar(p) { return p.car.car.cdr.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".car.cdr.cdr.cdr")))
+*/
+function sc_cdddar(p) { return p.car.cdr.cdr.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.car.cdr.cdr")))
+*/
+function sc_cddadr(p) { return p.cdr.car.cdr.cdr; }
+/*** META ((export #t)
+ (peephole (postfix ".cdr.cdr.cdr.cdr")))
+*/
+function sc_cddddr(p) { return p.cdr.cdr.cdr.cdr; }
+
+/*** META ((export #t)) */
+function sc_lastPair(l) {
+ if (!sc_isPair(l)) sc_error("sc_lastPair: pair expected");
+ var res = l;
+ var cdr = l.cdr;
+ while (sc_isPair(cdr)) {
+ res = cdr;
+ cdr = res.cdr;
+ }
+ return res;
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix " === null")))
+*/
+function sc_isNull(o) {
+ return (o === null);
+}
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isList(o) {
+ var rabbit;
+ var turtle;
+
+ var rabbit = o;
+ var turtle = o;
+ while (true) {
+ if (rabbit === null ||
+ (rabbit instanceof sc_Pair && rabbit.cdr === null))
+ return true; // end of list
+ else if ((rabbit instanceof sc_Pair) &&
+ (rabbit.cdr instanceof sc_Pair)) {
+ rabbit = rabbit.cdr.cdr;
+ turtle = turtle.cdr;
+ if (rabbit === turtle) return false; // cycle
+ } else
+ return false; // not pair
+ }
+}
+
+/*** META ((export #t)) */
+function sc_list() {
+ var res = null;
+ var a = arguments;
+ for (var i = a.length-1; i >= 0; i--)
+ res = new sc_Pair(a[i], res);
+ return res;
+}
+
+/*** META ((export #t)) */
+function sc_iota(num, init) {
+ var res = null;
+ if (!init) init = 0;
+ for (var i = num - 1; i >= 0; i--)
+ res = new sc_Pair(i + init, res);
+ return res;
+}
+
+/*** META ((export #t)) */
+function sc_makeList(nbEls, fill) {
+ var res = null;
+ for (var i = 0; i < nbEls; i++)
+ res = new sc_Pair(fill, res);
+ return res;
+}
+
+/*** META ((export #t)) */
+function sc_length(l) {
+ var res = 0;
+ while (l !== null) {
+ res++;
+ l = l.cdr;
+ }
+ return res;
+}
+
+/*** META ((export #t)) */
+function sc_remq(o, l) {
+ var dummy = { cdr : null };
+ var tail = dummy;
+ while (l !== null) {
+ if (l.car !== o) {
+ tail.cdr = sc_cons(l.car, null);
+ tail = tail.cdr;
+ }
+ l = l.cdr;
+ }
+ return dummy.cdr;
+}
+
+/*** META ((export #t)) */
+function sc_remqBang(o, l) {
+ var dummy = { cdr : null };
+ var tail = dummy;
+ var needsAssig = true;
+ while (l !== null) {
+ if (l.car === o) {
+ needsAssig = true;
+ } else {
+ if (needsAssig) {
+ tail.cdr = l;
+ needsAssig = false;
+ }
+ tail = l;
+ }
+ l = l.cdr;
+ }
+ tail.cdr = null;
+ return dummy.cdr;
+}
+
+/*** META ((export #t)) */
+function sc_delete(o, l) {
+ var dummy = { cdr : null };
+ var tail = dummy;
+ while (l !== null) {
+ if (!sc_isEqual(l.car, o)) {
+ tail.cdr = sc_cons(l.car, null);
+ tail = tail.cdr;
+ }
+ l = l.cdr;
+ }
+ return dummy.cdr;
+}
+
+/*** META ((export #t)) */
+function sc_deleteBang(o, l) {
+ var dummy = { cdr : null };
+ var tail = dummy;
+ var needsAssig = true;
+ while (l !== null) {
+ if (sc_isEqual(l.car, o)) {
+ needsAssig = true;
+ } else {
+ if (needsAssig) {
+ tail.cdr = l;
+ needsAssig = false;
+ }
+ tail = l;
+ }
+ l = l.cdr;
+ }
+ tail.cdr = null;
+ return dummy.cdr;
+}
+
+function sc_reverseAppendBang(l1, l2) {
+ var res = l2;
+ while (l1 !== null) {
+ var tmp = res;
+ res = l1;
+ l1 = l1.cdr;
+ res.cdr = tmp;
+ }
+ return res;
+}
+
+function sc_dualAppend(l1, l2) {
+ if (l1 === null) return l2;
+ if (l2 === null) return l1;
+ var rev = sc_reverse(l1);
+ return sc_reverseAppendBang(rev, l2);
+}
+
+/*** META ((export #t)) */
+function sc_append() {
+ if (arguments.length === 0)
+ return null;
+ var res = arguments[arguments.length - 1];
+ for (var i = arguments.length - 2; i >= 0; i--)
+ res = sc_dualAppend(arguments[i], res);
+ return res;
+}
+
+function sc_dualAppendBang(l1, l2) {
+ if (l1 === null) return l2;
+ if (l2 === null) return l1;
+ var tmp = l1;
+ while (tmp.cdr !== null) tmp=tmp.cdr;
+ tmp.cdr = l2;
+ return l1;
+}
+
+/*** META ((export #t)) */
+function sc_appendBang() {
+ var res = null;
+ for (var i = 0; i < arguments.length; i++)
+ res = sc_dualAppendBang(res, arguments[i]);
+ return res;
+}
+
+/*** META ((export #t)) */
+function sc_reverse(l1) {
+ var res = null;
+ while (l1 !== null) {
+ res = sc_cons(l1.car, res);
+ l1 = l1.cdr;
+ }
+ return res;
+}
+
+/*** META ((export #t)) */
+function sc_reverseBang(l) {
+ return sc_reverseAppendBang(l, null);
+}
+
+/*** META ((export #t)) */
+function sc_listTail(l, k) {
+ var res = l;
+ for (var i = 0; i < k; i++) {
+ res = res.cdr;
+ }
+ return res;
+}
+
+/*** META ((export #t)) */
+function sc_listRef(l, k) {
+ return sc_listTail(l, k).car;
+}
+
+/* // unoptimized generic versions
+function sc_memX(o, l, comp) {
+ while (l != null) {
+ if (comp(l.car, o))
+ return l;
+ l = l.cdr;
+ }
+ return false;
+}
+function sc_memq(o, l) { return sc_memX(o, l, sc_isEq); }
+function sc_memv(o, l) { return sc_memX(o, l, sc_isEqv); }
+function sc_member(o, l) { return sc_memX(o, l, sc_isEqual); }
+*/
+
+/* optimized versions */
+/*** META ((export #t)) */
+function sc_memq(o, l) {
+ while (l !== null) {
+ if (l.car === o)
+ return l;
+ l = l.cdr;
+ }
+ return false;
+}
+/*** META ((export #t)) */
+function sc_memv(o, l) {
+ while (l !== null) {
+ if (l.car === o)
+ return l;
+ l = l.cdr;
+ }
+ return false;
+}
+/*** META ((export #t)) */
+function sc_member(o, l) {
+ while (l !== null) {
+ if (sc_isEqual(l.car,o))
+ return l;
+ l = l.cdr;
+ }
+ return false;
+}
+
+/* // generic unoptimized versions
+function sc_assX(o, al, comp) {
+ while (al != null) {
+ if (comp(al.car.car, o))
+ return al.car;
+ al = al.cdr;
+ }
+ return false;
+}
+function sc_assq(o, al) { return sc_assX(o, al, sc_isEq); }
+function sc_assv(o, al) { return sc_assX(o, al, sc_isEqv); }
+function sc_assoc(o, al) { return sc_assX(o, al, sc_isEqual); }
+*/
+// optimized versions
+/*** META ((export #t)) */
+function sc_assq(o, al) {
+ while (al !== null) {
+ if (al.car.car === o)
+ return al.car;
+ al = al.cdr;
+ }
+ return false;
+}
+/*** META ((export #t)) */
+function sc_assv(o, al) {
+ while (al !== null) {
+ if (al.car.car === o)
+ return al.car;
+ al = al.cdr;
+ }
+ return false;
+}
+/*** META ((export #t)) */
+function sc_assoc(o, al) {
+ while (al !== null) {
+ if (sc_isEqual(al.car.car, o))
+ return al.car;
+ al = al.cdr;
+ }
+ return false;
+}
+
+/* can be used for mutable strings and characters */
+function sc_isCharStringEqual(cs1, cs2) { return cs1.val === cs2.val; }
+function sc_isCharStringLess(cs1, cs2) { return cs1.val < cs2.val; }
+function sc_isCharStringGreater(cs1, cs2) { return cs1.val > cs2.val; }
+function sc_isCharStringLessEqual(cs1, cs2) { return cs1.val <= cs2.val; }
+function sc_isCharStringGreaterEqual(cs1, cs2) { return cs1.val >= cs2.val; }
+function sc_isCharStringCIEqual(cs1, cs2)
+ { return cs1.val.toLowerCase() === cs2.val.toLowerCase(); }
+function sc_isCharStringCILess(cs1, cs2)
+ { return cs1.val.toLowerCase() < cs2.val.toLowerCase(); }
+function sc_isCharStringCIGreater(cs1, cs2)
+ { return cs1.val.toLowerCase() > cs2.val.toLowerCase(); }
+function sc_isCharStringCILessEqual(cs1, cs2)
+ { return cs1.val.toLowerCase() <= cs2.val.toLowerCase(); }
+function sc_isCharStringCIGreaterEqual(cs1, cs2)
+ { return cs1.val.toLowerCase() >= cs2.val.toLowerCase(); }
+
+
+
+
+function sc_Char(c) {
+ var cached = sc_Char.lazy[c];
+ if (cached)
+ return cached;
+ this.val = c;
+ sc_Char.lazy[c] = this;
+ // add return, so FF does not complain.
+ return undefined;
+}
+sc_Char.lazy = new Object();
+// thanks to Eric
+sc_Char.char2readable = {
+ "\000": "#\\null",
+ "\007": "#\\bell",
+ "\010": "#\\backspace",
+ "\011": "#\\tab",
+ "\012": "#\\newline",
+ "\014": "#\\page",
+ "\015": "#\\return",
+ "\033": "#\\escape",
+ "\040": "#\\space",
+ "\177": "#\\delete",
+
+ /* poeticless names */
+ "\001": "#\\soh",
+ "\002": "#\\stx",
+ "\003": "#\\etx",
+ "\004": "#\\eot",
+ "\005": "#\\enq",
+ "\006": "#\\ack",
+
+ "\013": "#\\vt",
+ "\016": "#\\so",
+ "\017": "#\\si",
+
+ "\020": "#\\dle",
+ "\021": "#\\dc1",
+ "\022": "#\\dc2",
+ "\023": "#\\dc3",
+ "\024": "#\\dc4",
+ "\025": "#\\nak",
+ "\026": "#\\syn",
+ "\027": "#\\etb",
+
+ "\030": "#\\can",
+ "\031": "#\\em",
+ "\032": "#\\sub",
+ "\033": "#\\esc",
+ "\034": "#\\fs",
+ "\035": "#\\gs",
+ "\036": "#\\rs",
+ "\037": "#\\us"};
+
+sc_Char.readable2char = {
+ "null": "\000",
+ "bell": "\007",
+ "backspace": "\010",
+ "tab": "\011",
+ "newline": "\012",
+ "page": "\014",
+ "return": "\015",
+ "escape": "\033",
+ "space": "\040",
+ "delete": "\000",
+ "soh": "\001",
+ "stx": "\002",
+ "etx": "\003",
+ "eot": "\004",
+ "enq": "\005",
+ "ack": "\006",
+ "bel": "\007",
+ "bs": "\010",
+ "ht": "\011",
+ "nl": "\012",
+ "vt": "\013",
+ "np": "\014",
+ "cr": "\015",
+ "so": "\016",
+ "si": "\017",
+ "dle": "\020",
+ "dc1": "\021",
+ "dc2": "\022",
+ "dc3": "\023",
+ "dc4": "\024",
+ "nak": "\025",
+ "syn": "\026",
+ "etb": "\027",
+ "can": "\030",
+ "em": "\031",
+ "sub": "\032",
+ "esc": "\033",
+ "fs": "\034",
+ "gs": "\035",
+ "rs": "\036",
+ "us": "\037",
+ "sp": "\040",
+ "del": "\177"};
+
+sc_Char.prototype.toString = function() {
+ return this.val;
+};
+// sc_toDisplayString == toString
+sc_Char.prototype.sc_toWriteString = function() {
+ var entry = sc_Char.char2readable[this.val];
+ if (entry)
+ return entry;
+ else
+ return "#\\" + this.val;
+};
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix "instanceof sc_Char")))
+*/
+function sc_isChar(c) {
+ return (c instanceof sc_Char);
+}
+
+/*** META ((export char=?)
+ (type bool)
+ (peephole (hole 2 c1 ".val === " c2 ".val")))
+*/
+var sc_isCharEqual = sc_isCharStringEqual;
+/*** META ((export char<?)
+ (type bool)
+ (peephole (hole 2 c1 ".val < " c2 ".val")))
+*/
+var sc_isCharLess = sc_isCharStringLess;
+/*** META ((export char>?)
+ (type bool)
+ (peephole (hole 2 c1 ".val > " c2 ".val")))
+*/
+var sc_isCharGreater = sc_isCharStringGreater;
+/*** META ((export char<=?)
+ (type bool)
+ (peephole (hole 2 c1 ".val <= " c2 ".val")))
+*/
+var sc_isCharLessEqual = sc_isCharStringLessEqual;
+/*** META ((export char>=?)
+ (type bool)
+ (peephole (hole 2 c1 ".val >= " c2 ".val")))
+*/
+var sc_isCharGreaterEqual = sc_isCharStringGreaterEqual;
+/*** META ((export char-ci=?)
+ (type bool)
+ (peephole (hole 2 c1 ".val.toLowerCase() === " c2 ".val.toLowerCase()")))
+*/
+var sc_isCharCIEqual = sc_isCharStringCIEqual;
+/*** META ((export char-ci<?)
+ (type bool)
+ (peephole (hole 2 c1 ".val.toLowerCase() < " c2 ".val.toLowerCase()")))
+*/
+var sc_isCharCILess = sc_isCharStringCILess;
+/*** META ((export char-ci>?)
+ (type bool)
+ (peephole (hole 2 c1 ".val.toLowerCase() > " c2 ".val.toLowerCase()")))
+*/
+var sc_isCharCIGreater = sc_isCharStringCIGreater;
+/*** META ((export char-ci<=?)
+ (type bool)
+ (peephole (hole 2 c1 ".val.toLowerCase() <= " c2 ".val.toLowerCase()")))
+*/
+var sc_isCharCILessEqual = sc_isCharStringCILessEqual;
+/*** META ((export char-ci>=?)
+ (type bool)
+ (peephole (hole 2 c1 ".val.toLowerCase() >= " c2 ".val.toLowerCase()")))
+*/
+var sc_isCharCIGreaterEqual = sc_isCharStringCIGreaterEqual;
+
+var SC_NUMBER_CLASS = "0123456789";
+var SC_WHITESPACE_CLASS = ' \r\n\t\f';
+var SC_LOWER_CLASS = 'abcdefghijklmnopqrstuvwxyz';
+var SC_UPPER_CLASS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
+function sc_isCharOfClass(c, cl) { return (cl.indexOf(c) != -1); }
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isCharAlphabetic(c)
+ { return sc_isCharOfClass(c.val, SC_LOWER_CLASS) ||
+ sc_isCharOfClass(c.val, SC_UPPER_CLASS); }
+/*** META ((export #t)
+ (type bool)
+ (peephole (hole 1 "SC_NUMBER_CLASS.indexOf(" c ".val) != -1")))
+*/
+function sc_isCharNumeric(c)
+ { return sc_isCharOfClass(c.val, SC_NUMBER_CLASS); }
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isCharWhitespace(c) {
+ var tmp = c.val;
+ return tmp === " " || tmp === "\r" || tmp === "\n" || tmp === "\t" || tmp === "\f";
+}
+/*** META ((export #t)
+ (type bool)
+ (peephole (hole 1 "SC_UPPER_CLASS.indexOf(" c ".val) != -1")))
+*/
+function sc_isCharUpperCase(c)
+ { return sc_isCharOfClass(c.val, SC_UPPER_CLASS); }
+/*** META ((export #t)
+ (type bool)
+ (peephole (hole 1 "SC_LOWER_CLASS.indexOf(" c ".val) != -1")))
+*/
+function sc_isCharLowerCase(c)
+ { return sc_isCharOfClass(c.val, SC_LOWER_CLASS); }
+
+/*** META ((export #t)
+ (peephole (postfix ".val.charCodeAt(0)")))
+*/
+function sc_char2integer(c)
+ { return c.val.charCodeAt(0); }
+/*** META ((export #t)
+ (peephole (hole 1 "new sc_Char(String.fromCharCode(" n "))")))
+*/
+function sc_integer2char(n)
+ { return new sc_Char(String.fromCharCode(n)); }
+
+/*** META ((export #t)
+ (peephole (hole 1 "new sc_Char(" c ".val.toUpperCase())")))
+*/
+function sc_charUpcase(c)
+ { return new sc_Char(c.val.toUpperCase()); }
+/*** META ((export #t)
+ (peephole (hole 1 "new sc_Char(" c ".val.toLowerCase())")))
+*/
+function sc_charDowncase(c)
+ { return new sc_Char(c.val.toLowerCase()); }
+
+function sc_makeJSStringOfLength(k, c) {
+ var fill;
+ if (c === undefined)
+ fill = " ";
+ else
+ fill = c;
+ var res = "";
+ var len = 1;
+ // every round doubles the size of fill.
+ while (k >= len) {
+ if (k & len)
+ res = res.concat(fill);
+ fill = fill.concat(fill);
+ len *= 2;
+ }
+ return res;
+}
+
+function sc_makejsString(k, c) {
+ var fill;
+ if (c)
+ fill = c.val;
+ else
+ fill = " ";
+ return sc_makeJSStringOfLength(k, fill);
+}
+
+function sc_jsstring2list(s) {
+ var res = null;
+ for (var i = s.length - 1; i >= 0; i--)
+ res = sc_cons(new sc_Char(s.charAt(i)), res);
+ return res;
+}
+
+function sc_list2jsstring(l) {
+ var a = new Array();
+ while(l !== null) {
+ a.push(l.car.val);
+ l = l.cdr;
+ }
+ return "".concat.apply("", a);
+}
+
+var sc_Vector = Array;
+
+sc_Vector.prototype.sc_toWriteOrDisplayString = function(writeOrDisplay) {
+ if (this.length === 0) return "#()";
+
+ var res = "#(" + writeOrDisplay(this[0]);
+ for (var i = 1; i < this.length; i++)
+ res += " " + writeOrDisplay(this[i]);
+ res += ")";
+ return res;
+};
+sc_Vector.prototype.sc_toDisplayString = function() {
+ return this.sc_toWriteOrDisplayString(sc_toDisplayString);
+};
+sc_Vector.prototype.sc_toWriteString = function() {
+ return this.sc_toWriteOrDisplayString(sc_toWriteString);
+};
+
+/*** META ((export vector? array?)
+ (type bool)
+ (peephole (postfix " instanceof sc_Vector")))
+*/
+function sc_isVector(v) {
+ return (v instanceof sc_Vector);
+}
+
+// only applies to vectors
+function sc_isVectorEqual(v1, v2, comp) {
+ if (v1.length !== v2.length) return false;
+ for (var i = 0; i < v1.length; i++)
+ if (!comp(v1[i], v2[i])) return false;
+ return true;
+}
+
+/*** META ((export make-vector make-array)) */
+function sc_makeVector(size, fill) {
+ var a = new sc_Vector(size);
+ if (fill !== undefined)
+ sc_vectorFillBang(a, fill);
+ return a;
+}
+
+/*** META ((export vector array)
+ (peephole (vector)))
+*/
+function sc_vector() {
+ var a = new sc_Vector();
+ for (var i = 0; i < arguments.length; i++)
+ a.push(arguments[i]);
+ return a;
+}
+
+/*** META ((export vector-length array-length)
+ (peephole (postfix ".length")))
+*/
+function sc_vectorLength(v) {
+ return v.length;
+}
+
+/*** META ((export vector-ref array-ref)
+ (peephole (hole 2 v "[" pos "]")))
+*/
+function sc_vectorRef(v, pos) {
+ return v[pos];
+}
+
+/*** META ((export vector-set! array-set!)
+ (peephole (hole 3 v "[" pos "] = " val)))
+*/
+function sc_vectorSetBang(v, pos, val) {
+ v[pos] = val;
+}
+
+/*** META ((export vector->list array->list)) */
+function sc_vector2list(a) {
+ var res = null;
+ for (var i = a.length-1; i >= 0; i--)
+ res = sc_cons(a[i], res);
+ return res;
+}
+
+/*** META ((export list->vector list->array)) */
+function sc_list2vector(l) {
+ var a = new sc_Vector();
+ while(l !== null) {
+ a.push(l.car);
+ l = l.cdr;
+ }
+ return a;
+}
+
+/*** META ((export vector-fill! array-fill!)) */
+function sc_vectorFillBang(a, fill) {
+ for (var i = 0; i < a.length; i++)
+ a[i] = fill;
+}
+
+
+/*** META ((export #t)) */
+function sc_copyVector(a, len) {
+ if (len <= a.length)
+ return a.slice(0, len);
+ else {
+ var tmp = a.concat();
+ tmp.length = len;
+ return tmp;
+ }
+}
+
+/*** META ((export #t)
+ (peephole (hole 3 a ".slice(" start "," end ")")))
+*/
+function sc_vectorCopy(a, start, end) {
+ return a.slice(start, end);
+}
+
+/*** META ((export #t)) */
+function sc_vectorCopyBang(target, tstart, source, sstart, send) {
+ if (!sstart) sstart = 0;
+ if (!send) send = source.length;
+
+ // if target == source we don't want to overwrite not yet copied elements.
+ if (tstart <= sstart) {
+ for (var i = tstart, j = sstart; j < send; i++, j++) {
+ target[i] = source[j];
+ }
+ } else {
+ var diff = send - sstart;
+ for (var i = tstart + diff - 1, j = send - 1;
+ j >= sstart;
+ i--, j--) {
+ target[i] = source[j];
+ }
+ }
+ return target;
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (hole 1 "typeof " o " === 'function'")))
+*/
+function sc_isProcedure(o) {
+ return (typeof o === "function");
+}
+
+/*** META ((export #t)) */
+function sc_apply(proc) {
+ var args = new Array();
+ // first part of arguments are not in list-form.
+ for (var i = 1; i < arguments.length - 1; i++)
+ args.push(arguments[i]);
+ var l = arguments[arguments.length - 1];
+ while (l !== null) {
+ args.push(l.car);
+ l = l.cdr;
+ }
+ return proc.apply(null, args);
+}
+
+/*** META ((export #t)) */
+function sc_map(proc, l1) {
+ if (l1 === undefined)
+ return null;
+ // else
+ var nbApplyArgs = arguments.length - 1;
+ var applyArgs = new Array(nbApplyArgs);
+ var revres = null;
+ while (l1 !== null) {
+ for (var i = 0; i < nbApplyArgs; i++) {
+ applyArgs[i] = arguments[i + 1].car;
+ arguments[i + 1] = arguments[i + 1].cdr;
+ }
+ revres = sc_cons(proc.apply(null, applyArgs), revres);
+ }
+ return sc_reverseAppendBang(revres, null);
+}
+
+/*** META ((export #t)) */
+function sc_mapBang(proc, l1) {
+ if (l1 === undefined)
+ return null;
+ // else
+ var l1_orig = l1;
+ var nbApplyArgs = arguments.length - 1;
+ var applyArgs = new Array(nbApplyArgs);
+ while (l1 !== null) {
+ var tmp = l1;
+ for (var i = 0; i < nbApplyArgs; i++) {
+ applyArgs[i] = arguments[i + 1].car;
+ arguments[i + 1] = arguments[i + 1].cdr;
+ }
+ tmp.car = proc.apply(null, applyArgs);
+ }
+ return l1_orig;
+}
+
+/*** META ((export #t)) */
+function sc_forEach(proc, l1) {
+ if (l1 === undefined)
+ return undefined;
+ // else
+ var nbApplyArgs = arguments.length - 1;
+ var applyArgs = new Array(nbApplyArgs);
+ while (l1 !== null) {
+ for (var i = 0; i < nbApplyArgs; i++) {
+ applyArgs[i] = arguments[i + 1].car;
+ arguments[i + 1] = arguments[i + 1].cdr;
+ }
+ proc.apply(null, applyArgs);
+ }
+ // add return so FF does not complain.
+ return undefined;
+}
+
+/*** META ((export #t)) */
+function sc_filter(proc, l1) {
+ var dummy = { cdr : null };
+ var tail = dummy;
+ while (l1 !== null) {
+ if (proc(l1.car) !== false) {
+ tail.cdr = sc_cons(l1.car, null);
+ tail = tail.cdr;
+ }
+ l1 = l1.cdr;
+ }
+ return dummy.cdr;
+}
+
+/*** META ((export #t)) */
+function sc_filterBang(proc, l1) {
+ var head = sc_cons("dummy", l1);
+ var it = head;
+ var next = l1;
+ while (next !== null) {
+ if (proc(next.car) !== false) {
+ it.cdr = next
+ it = next;
+ }
+ next = next.cdr;
+ }
+ it.cdr = null;
+ return head.cdr;
+}
+
+function sc_filterMap1(proc, l1) {
+ var revres = null;
+ while (l1 !== null) {
+ var tmp = proc(l1.car)
+ if (tmp !== false) revres = sc_cons(tmp, revres);
+ l1 = l1.cdr;
+ }
+ return sc_reverseAppendBang(revres, null);
+}
+function sc_filterMap2(proc, l1, l2) {
+ var revres = null;
+ while (l1 !== null) {
+ var tmp = proc(l1.car, l2.car);
+ if(tmp !== false) revres = sc_cons(tmp, revres);
+ l1 = l1.cdr;
+ l2 = l2.cdr
+ }
+ return sc_reverseAppendBang(revres, null);
+}
+
+/*** META ((export #t)) */
+function sc_filterMap(proc, l1, l2, l3) {
+ if (l2 === undefined)
+ return sc_filterMap1(proc, l1);
+ else if (l3 === undefined)
+ return sc_filterMap2(proc, l1, l2);
+ // else
+ var nbApplyArgs = arguments.length - 1;
+ var applyArgs = new Array(nbApplyArgs);
+ var revres = null;
+ while (l1 !== null) {
+ for (var i = 0; i < nbApplyArgs; i++) {
+ applyArgs[i] = arguments[i + 1].car;
+ arguments[i + 1] = arguments[i + 1].cdr;
+ }
+ var tmp = proc.apply(null, applyArgs);
+ if(tmp !== false) revres = sc_cons(tmp, revres);
+ }
+ return sc_reverseAppendBang(revres, null);
+}
+
+/*** META ((export #t)) */
+function sc_any(proc, l) {
+ var revres = null;
+ while (l !== null) {
+ var tmp = proc(l.car);
+ if(tmp !== false) return tmp;
+ l = l.cdr;
+ }
+ return false;
+}
+
+/*** META ((export any?)
+ (peephole (hole 2 "sc_any(" proc "," l ") !== false")))
+*/
+function sc_anyPred(proc, l) {
+ return sc_any(proc, l)!== false;
+}
+
+/*** META ((export #t)) */
+function sc_every(proc, l) {
+ var revres = null;
+ var tmp = true;
+ while (l !== null) {
+ tmp = proc(l.car);
+ if (tmp === false) return false;
+ l = l.cdr;
+ }
+ return tmp;
+}
+
+/*** META ((export every?)
+ (peephole (hole 2 "sc_every(" proc "," l ") !== false")))
+*/
+function sc_everyPred(proc, l) {
+ var tmp = sc_every(proc, l);
+ if (tmp !== false) return true;
+ return false;
+}
+
+/*** META ((export #t)
+ (peephole (postfix "()")))
+*/
+function sc_force(o) {
+ return o();
+}
+
+/*** META ((export #t)) */
+function sc_makePromise(proc) {
+ var isResultReady = false;
+ var result = undefined;
+ return function() {
+ if (!isResultReady) {
+ var tmp = proc();
+ if (!isResultReady) {
+ isResultReady = true;
+ result = tmp;
+ }
+ }
+ return result;
+ };
+}
+
+function sc_Values(values) {
+ this.values = values;
+}
+
+/*** META ((export #t)
+ (peephole (values)))
+*/
+function sc_values() {
+ if (arguments.length === 1)
+ return arguments[0];
+ else
+ return new sc_Values(arguments);
+}
+
+/*** META ((export #t)) */
+function sc_callWithValues(producer, consumer) {
+ var produced = producer();
+ if (produced instanceof sc_Values)
+ return consumer.apply(null, produced.values);
+ else
+ return consumer(produced);
+}
+
+/*** META ((export #t)) */
+function sc_dynamicWind(before, thunk, after) {
+ before();
+ try {
+ var res = thunk();
+ return res;
+ } finally {
+ after();
+ }
+}
+
+
+// TODO: eval/scheme-report-environment/null-environment/interaction-environment
+
+// LIMITATION: 'load' doesn't exist without files.
+// LIMITATION: transcript-on/transcript-off doesn't exist without files.
+
+
+function sc_Struct(name) {
+ this.name = name;
+}
+sc_Struct.prototype.sc_toDisplayString = function() {
+ return "#<struct" + sc_hash(this) + ">";
+};
+sc_Struct.prototype.sc_toWriteString = sc_Struct.prototype.sc_toDisplayString;
+
+/*** META ((export #t)
+ (peephole (hole 1 "new sc_Struct(" name ")")))
+*/
+function sc_makeStruct(name) {
+ return new sc_Struct(name);
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix " instanceof sc_Struct")))
+*/
+function sc_isStruct(o) {
+ return (o instanceof sc_Struct);
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (hole 2 "(" 1 " instanceof sc_Struct) && ( " 1 ".name === " 0 ")")))
+*/
+function sc_isStructNamed(name, s) {
+ return ((s instanceof sc_Struct) && (s.name === name));
+}
+
+/*** META ((export struct-field)
+ (peephole (hole 3 0 "[" 2 "]")))
+*/
+function sc_getStructField(s, name, field) {
+ return s[field];
+}
+
+/*** META ((export struct-field-set!)
+ (peephole (hole 4 0 "[" 2 "] = " 3)))
+*/
+function sc_setStructFieldBang(s, name, field, val) {
+ s[field] = val;
+}
+
+/*** META ((export #t)
+ (peephole (prefix "~")))
+*/
+function sc_bitNot(x) {
+ return ~x;
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 "&")))
+*/
+function sc_bitAnd(x, y) {
+ return x & y;
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 "|")))
+*/
+function sc_bitOr(x, y) {
+ return x | y;
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 "^")))
+*/
+function sc_bitXor(x, y) {
+ return x ^ y;
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 "<<")))
+*/
+function sc_bitLsh(x, y) {
+ return x << y;
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 ">>")))
+*/
+function sc_bitRsh(x, y) {
+ return x >> y;
+}
+
+/*** META ((export #t)
+ (peephole (infix 2 2 ">>>")))
+*/
+function sc_bitUrsh(x, y) {
+ return x >>> y;
+}
+
+/*** META ((export js-field js-property)
+ (peephole (hole 2 o "[" field "]")))
+*/
+function sc_jsField(o, field) {
+ return o[field];
+}
+
+/*** META ((export js-field-set! js-property-set!)
+ (peephole (hole 3 o "[" field "] = " val)))
+*/
+function sc_setJsFieldBang(o, field, val) {
+ return o[field] = val;
+}
+
+/*** META ((export js-field-delete! js-property-delete!)
+ (peephole (hole 2 "delete" o "[" field "]")))
+*/
+function sc_deleteJsFieldBang(o, field) {
+ delete o[field];
+}
+
+/*** META ((export #t)
+ (peephole (jsCall)))
+*/
+function sc_jsCall(o, fun) {
+ var args = new Array();
+ for (var i = 2; i < arguments.length; i++)
+ args[i-2] = arguments[i];
+ return fun.apply(o, args);
+}
+
+/*** META ((export #t)
+ (peephole (jsMethodCall)))
+*/
+function sc_jsMethodCall(o, field) {
+ var args = new Array();
+ for (var i = 2; i < arguments.length; i++)
+ args[i-2] = arguments[i];
+ return o[field].apply(o, args);
+}
+
+/*** META ((export new js-new)
+ (peephole (jsNew)))
+*/
+function sc_jsNew(c) {
+ var evalStr = "new c(";
+ evalStr +=arguments.length > 1? "arguments[1]": "";
+ for (var i = 2; i < arguments.length; i++)
+ evalStr += ", arguments[" + i + "]";
+ evalStr +=")";
+ return eval(evalStr);
+}
+
+// ======================== RegExp ====================
+/*** META ((export #t)) */
+function sc_pregexp(re) {
+ return new RegExp(sc_string2jsstring(re));
+}
+
+/*** META ((export #t)) */
+function sc_pregexpMatch(re, s) {
+ var reg = (re instanceof RegExp) ? re : sc_pregexp(re);
+ var tmp = reg.exec(sc_string2jsstring(s));
+
+ if (tmp == null) return false;
+
+ var res = null;
+ for (var i = tmp.length-1; i >= 0; i--) {
+ if (tmp[i] !== null) {
+ res = sc_cons(sc_jsstring2string(tmp[i]), res);
+ } else {
+ res = sc_cons(false, res);
+ }
+ }
+ return res;
+}
+
+/*** META ((export #t)) */
+function sc_pregexpReplace(re, s1, s2) {
+ var reg;
+ var jss1 = sc_string2jsstring(s1);
+ var jss2 = sc_string2jsstring(s2);
+
+ if (re instanceof RegExp) {
+ if (re.global)
+ reg = re;
+ else
+ reg = new RegExp(re.source);
+ } else {
+ reg = new RegExp(sc_string2jsstring(re));
+ }
+
+ return jss1.replace(reg, jss2);
+}
+
+/*** META ((export pregexp-replace*)) */
+function sc_pregexpReplaceAll(re, s1, s2) {
+ var reg;
+ var jss1 = sc_string2jsstring(s1);
+ var jss2 = sc_string2jsstring(s2);
+
+ if (re instanceof RegExp) {
+ if (re.global)
+ reg = re;
+ else
+ reg = new RegExp(re.source, "g");
+ } else {
+ reg = new RegExp(sc_string2jsstring(re), "g");
+ }
+
+ return jss1.replace(reg, jss2);
+}
+
+/*** META ((export #t)) */
+function sc_pregexpSplit(re, s) {
+ var reg = ((re instanceof RegExp) ?
+ re :
+ new RegExp(sc_string2jsstring(re)));
+ var jss = sc_string2jsstring(s);
+ var tmp = jss.split(reg);
+
+ if (tmp == null) return false;
+
+ return sc_vector2list(tmp);
+}
+
+
+/* =========================================================================== */
+/* Other library stuff */
+/* =========================================================================== */
+
+/*** META ((export #t)
+ (peephole (hole 1 "Math.floor(Math.random()*" 'n ")")))
+*/
+function sc_random(n) {
+ return Math.floor(Math.random()*n);
+}
+
+/*** META ((export current-date)
+ (peephole (hole 0 "new Date()")))
+*/
+function sc_currentDate() {
+ return new Date();
+}
+
+function sc_Hashtable() {
+}
+sc_Hashtable.prototype.toString = function() {
+ return "#{%hashtable}";
+};
+// sc_toWriteString == sc_toDisplayString == toString
+
+function sc_HashtableElement(key, val) {
+ this.key = key;
+ this.val = val;
+}
+
+/*** META ((export #t)
+ (peephole (hole 0 "new sc_Hashtable()")))
+*/
+function sc_makeHashtable() {
+ return new sc_Hashtable();
+}
+
+/*** META ((export #t)) */
+function sc_hashtablePutBang(ht, key, val) {
+ var hash = sc_hash(key);
+ ht[hash] = new sc_HashtableElement(key, val);
+}
+
+/*** META ((export #t)) */
+function sc_hashtableGet(ht, key) {
+ var hash = sc_hash(key);
+ if (hash in ht)
+ return ht[hash].val;
+ else
+ return false;
+}
+
+/*** META ((export #t)) */
+function sc_hashtableForEach(ht, f) {
+ for (var v in ht) {
+ if (ht[v] instanceof sc_HashtableElement)
+ f(ht[v].key, ht[v].val);
+ }
+}
+
+/*** META ((export hashtable-contains?)
+ (peephole (hole 2 "sc_hash(" 1 ") in " 0)))
+*/
+function sc_hashtableContains(ht, key) {
+ var hash = sc_hash(key);
+ if (hash in ht)
+ return true;
+ else
+ return false;
+}
+
+var SC_HASH_COUNTER = 0;
+
+function sc_hash(o) {
+ if (o === null)
+ return "null";
+ else if (o === undefined)
+ return "undefined";
+ else if (o === true)
+ return "true";
+ else if (o === false)
+ return "false";
+ else if (typeof o === "number")
+ return "num-" + o;
+ else if (typeof o === "string")
+ return "jsstr-" + o;
+ else if (o.sc_getHash)
+ return o.sc_getHash();
+ else
+ return sc_counterHash.call(o);
+}
+function sc_counterHash() {
+ if (!this.sc_hash) {
+ this.sc_hash = "hash-" + SC_HASH_COUNTER;
+ SC_HASH_COUNTER++;
+ }
+ return this.sc_hash;
+}
+
+function sc_Trampoline(args, maxTailCalls) {
+ this['__trampoline return__'] = true;
+ this.args = args;
+ this.MAX_TAIL_CALLs = maxTailCalls;
+}
+// TODO: call/cc stuff
+sc_Trampoline.prototype.restart = function() {
+ var o = this;
+ while (true) {
+ // set both globals.
+ SC_TAIL_OBJECT.calls = o.MAX_TAIL_CALLs-1;
+ var fun = o.args.callee;
+ var res = fun.apply(SC_TAIL_OBJECT, o.args);
+ if (res instanceof sc_Trampoline)
+ o = res;
+ else
+ return res;
+ }
+}
+
+/*** META ((export bind-exit-lambda)) */
+function sc_bindExitLambda(proc) {
+ var escape_obj = new sc_BindExitException();
+ var escape = function(res) {
+ escape_obj.res = res;
+ throw escape_obj;
+ };
+ try {
+ return proc(escape);
+ } catch(e) {
+ if (e === escape_obj) {
+ return e.res;
+ }
+ throw e;
+ }
+}
+function sc_BindExitException() {
+ this._internalException = true;
+}
+
+var SC_SCM2JS_GLOBALS = new Object();
+
+// default tail-call depth.
+// normally the program should set it again. but just in case...
+var SC_TAIL_OBJECT = new Object();
+SC_SCM2JS_GLOBALS.TAIL_OBJECT = SC_TAIL_OBJECT;
+// ======================== I/O =======================
+
+/*------------------------------------------------------------------*/
+
+function sc_EOF() {
+}
+var SC_EOF_OBJECT = new sc_EOF();
+
+function sc_Port() {
+}
+
+/* --------------- Input ports -------------------------------------*/
+
+function sc_InputPort() {
+}
+sc_InputPort.prototype = new sc_Port();
+
+sc_InputPort.prototype.peekChar = function() {
+ if (!("peeked" in this))
+ this.peeked = this.getNextChar();
+ return this.peeked;
+}
+sc_InputPort.prototype.readChar = function() {
+ var tmp = this.peekChar();
+ delete this.peeked;
+ return tmp;
+}
+sc_InputPort.prototype.isCharReady = function() {
+ return true;
+}
+sc_InputPort.prototype.close = function() {
+ // do nothing
+}
+
+/* .............. String port ..........................*/
+function sc_ErrorInputPort() {
+};
+sc_ErrorInputPort.prototype = new sc_InputPort();
+sc_ErrorInputPort.prototype.getNextChar = function() {
+ throw "can't read from error-port.";
+};
+sc_ErrorInputPort.prototype.isCharReady = function() {
+ return false;
+};
+
+
+/* .............. String port ..........................*/
+
+function sc_StringInputPort(jsStr) {
+ // we are going to do some charAts on the str.
+ // instead of recreating all the time a String-object, we
+ // create one in the beginning. (not sure, if this is really an optim)
+ this.str = new String(jsStr);
+ this.pos = 0;
+}
+sc_StringInputPort.prototype = new sc_InputPort();
+sc_StringInputPort.prototype.getNextChar = function() {
+ if (this.pos >= this.str.length)
+ return SC_EOF_OBJECT;
+ return this.str.charAt(this.pos++);
+};
+
+/* ------------- Read and other lib-funs -------------------------------*/
+function sc_Token(type, val, pos) {
+ this.type = type;
+ this.val = val;
+ this.pos = pos;
+}
+sc_Token.EOF = 0/*EOF*/;
+sc_Token.OPEN_PAR = 1/*OPEN_PAR*/;
+sc_Token.CLOSE_PAR = 2/*CLOSE_PAR*/;
+sc_Token.OPEN_BRACE = 3/*OPEN_BRACE*/;
+sc_Token.CLOSE_BRACE = 4/*CLOSE_BRACE*/;
+sc_Token.OPEN_BRACKET = 5/*OPEN_BRACKET*/;
+sc_Token.CLOSE_BRACKET = 6/*CLOSE_BRACKET*/;
+sc_Token.WHITESPACE = 7/*WHITESPACE*/;
+sc_Token.QUOTE = 8/*QUOTE*/;
+sc_Token.ID = 9/*ID*/;
+sc_Token.DOT = 10/*DOT*/;
+sc_Token.STRING = 11/*STRING*/;
+sc_Token.NUMBER = 12/*NUMBER*/;
+sc_Token.ERROR = 13/*ERROR*/;
+sc_Token.VECTOR_BEGIN = 14/*VECTOR_BEGIN*/;
+sc_Token.TRUE = 15/*TRUE*/;
+sc_Token.FALSE = 16/*FALSE*/;
+sc_Token.UNSPECIFIED = 17/*UNSPECIFIED*/;
+sc_Token.REFERENCE = 18/*REFERENCE*/;
+sc_Token.STORE = 19/*STORE*/;
+sc_Token.CHAR = 20/*CHAR*/;
+
+var SC_ID_CLASS = SC_LOWER_CLASS + SC_UPPER_CLASS + "!$%*+-./:<=>?@^_~";
+function sc_Tokenizer(port) {
+ this.port = port;
+}
+sc_Tokenizer.prototype.peekToken = function() {
+ if (this.peeked)
+ return this.peeked;
+ var newToken = this.nextToken();
+ this.peeked = newToken;
+ return newToken;
+};
+sc_Tokenizer.prototype.readToken = function() {
+ var tmp = this.peekToken();
+ delete this.peeked;
+ return tmp;
+};
+sc_Tokenizer.prototype.nextToken = function() {
+ var port = this.port;
+
+ function isNumberChar(c) {
+ return (c >= "0" && c <= "9");
+ };
+ function isIdOrNumberChar(c) {
+ return SC_ID_CLASS.indexOf(c) != -1 || // ID-char
+ (c >= "0" && c <= "9");
+ }
+ function isWhitespace(c) {
+ return c === " " || c === "\r" || c === "\n" || c === "\t" || c === "\f";
+ };
+ function isWhitespaceOrEOF(c) {
+ return isWhitespace(c) || c === SC_EOF_OBJECT;
+ };
+
+ function readString() {
+ res = "";
+ while (true) {
+ var c = port.readChar();
+ switch (c) {
+ case '"':
+ return new sc_Token(11/*STRING*/, res);
+ case "\\":
+ var tmp = port.readChar();
+ switch (tmp) {
+ case '0': res += "\0"; break;
+ case 'a': res += "\a"; break;
+ case 'b': res += "\b"; break;
+ case 'f': res += "\f"; break;
+ case 'n': res += "\n"; break;
+ case 'r': res += "\r"; break;
+ case 't': res += "\t"; break;
+ case 'v': res += "\v"; break;
+ case '"': res += '"'; break;
+ case '\\': res += '\\'; break;
+ case 'x':
+ /* hexa-number */
+ var nb = 0;
+ while (true) {
+ var hexC = port.peekChar();
+ if (hexC >= '0' && hexC <= '9') {
+ port.readChar();
+ nb = nb * 16 + hexC.charCodeAt(0) - '0'.charCodeAt(0);
+ } else if (hexC >= 'a' && hexC <= 'f') {
+ port.readChar();
+ nb = nb * 16 + hexC.charCodeAt(0) - 'a'.charCodeAt(0);
+ } else if (hexC >= 'A' && hexC <= 'F') {
+ port.readChar();
+ nb = nb * 16 + hexC.charCodeAt(0) - 'A'.charCodeAt(0);
+ } else {
+ // next char isn't part of hex.
+ res += String.fromCharCode(nb);
+ break;
+ }
+ }
+ break;
+ default:
+ if (tmp === SC_EOF_OBJECT) {
+ return new sc_Token(13/*ERROR*/, "unclosed string-literal" + res);
+ }
+ res += tmp;
+ }
+ break;
+ default:
+ if (c === SC_EOF_OBJECT) {
+ return new sc_Token(13/*ERROR*/, "unclosed string-literal" + res);
+ }
+ res += c;
+ }
+ }
+ };
+ function readIdOrNumber(firstChar) {
+ var res = firstChar;
+ while (isIdOrNumberChar(port.peekChar()))
+ res += port.readChar();
+ if (isNaN(res))
+ return new sc_Token(9/*ID*/, res);
+ else
+ return new sc_Token(12/*NUMBER*/, res - 0);
+ };
+
+ function skipWhitespaceAndComments() {
+ var done = false;
+ while (!done) {
+ done = true;
+ while (isWhitespace(port.peekChar()))
+ port.readChar();
+ if (port.peekChar() === ';') {
+ port.readChar();
+ done = false;
+ while (true) {
+ curChar = port.readChar();
+ if (curChar === SC_EOF_OBJECT ||
+ curChar === '\n')
+ break;
+ }
+ }
+ }
+ };
+
+ function readDot() {
+ if (isWhitespace(port.peekChar()))
+ return new sc_Token(10/*DOT*/);
+ else
+ return readIdOrNumber(".");
+ };
+
+ function readSharp() {
+ var c = port.readChar();
+ if (isWhitespace(c))
+ return new sc_Token(13/*ERROR*/, "bad #-pattern0.");
+
+ // reference
+ if (isNumberChar(c)) {
+ var nb = c - 0;
+ while (isNumberChar(port.peekChar()))
+ nb = nb*10 + (port.readChar() - 0);
+ switch (port.readChar()) {
+ case '#':
+ return new sc_Token(18/*REFERENCE*/, nb);
+ case '=':
+ return new sc_Token(19/*STORE*/, nb);
+ default:
+ return new sc_Token(13/*ERROR*/, "bad #-pattern1." + nb);
+ }
+ }
+
+ if (c === "(")
+ return new sc_Token(14/*VECTOR_BEGIN*/);
+
+ if (c === "\\") { // character
+ var tmp = ""
+ while (!isWhitespaceOrEOF(port.peekChar()))
+ tmp += port.readChar();
+ switch (tmp.length) {
+ case 0: // it's escaping a whitespace char:
+ if (sc_isEOFObject(port.peekChar))
+ return new sc_Token(13/*ERROR*/, "bad #-pattern2.");
+ else
+ return new sc_Token(20/*CHAR*/, port.readChar());
+ case 1:
+ return new sc_Token(20/*CHAR*/, tmp);
+ default:
+ var entry = sc_Char.readable2char[tmp.toLowerCase()];
+ if (entry)
+ return new sc_Token(20/*CHAR*/, entry);
+ else
+ return new sc_Token(13/*ERROR*/, "unknown character description: #\\" + tmp);
+ }
+ }
+
+ // some constants (#t, #f, #unspecified)
+ var res;
+ var needing;
+ switch (c) {
+ case 't': res = new sc_Token(15/*TRUE*/, true); needing = ""; break;
+ case 'f': res = new sc_Token(16/*FALSE*/, false); needing = ""; break;
+ case 'u': res = new sc_Token(17/*UNSPECIFIED*/, undefined); needing = "nspecified"; break;
+ default:
+ return new sc_Token(13/*ERROR*/, "bad #-pattern3: " + c);
+ }
+ while(true) {
+ c = port.peekChar();
+ if ((isWhitespaceOrEOF(c) || c === ')') &&
+ needing == "")
+ return res;
+ else if (isWhitespace(c) || needing == "")
+ return new sc_Token(13/*ERROR*/, "bad #-pattern4 " + c + " " + needing);
+ else if (needing.charAt(0) == c) {
+ port.readChar(); // consume
+ needing = needing.slice(1);
+ } else
+ return new sc_Token(13/*ERROR*/, "bad #-pattern5");
+ }
+
+ };
+
+ skipWhitespaceAndComments();
+ var curChar = port.readChar();
+ if (curChar === SC_EOF_OBJECT)
+ return new sc_Token(0/*EOF*/, curChar);
+ switch (curChar)
+ {
+ case " ":
+ case "\n":
+ case "\t":
+ return readWhitespace();
+ case "(":
+ return new sc_Token(1/*OPEN_PAR*/);
+ case ")":
+ return new sc_Token(2/*CLOSE_PAR*/);
+ case "{":
+ return new sc_Token(3/*OPEN_BRACE*/);
+ case "}":
+ return new sc_Token(4/*CLOSE_BRACE*/);
+ case "[":
+ return new sc_Token(5/*OPEN_BRACKET*/);
+ case "]":
+ return new sc_Token(6/*CLOSE_BRACKET*/);
+ case "'":
+ return new sc_Token(8/*QUOTE*/);
+ case "#":
+ return readSharp();
+ case ".":
+ return readDot();
+ case '"':
+ return readString();
+ default:
+ if (isIdOrNumberChar(curChar))
+ return readIdOrNumber(curChar);
+ throw "unexpected character: " + curChar;
+ }
+};
+
+function sc_Reader(tokenizer) {
+ this.tokenizer = tokenizer;
+ this.backref = new Array();
+}
+sc_Reader.prototype.read = function() {
+ function readList(listBeginType) {
+ function matchesPeer(open, close) {
+ return open === 1/*OPEN_PAR*/ && close === 2/*CLOSE_PAR*/
+ || open === 3/*OPEN_BRACE*/ && close === 4/*CLOSE_BRACE*/
+ || open === 5/*OPEN_BRACKET*/ && close === 6/*CLOSE_BRACKET*/;
+ };
+ var res = null;
+
+ while (true) {
+ var token = tokenizer.peekToken();
+
+ switch (token.type) {
+ case 2/*CLOSE_PAR*/:
+ case 4/*CLOSE_BRACE*/:
+ case 6/*CLOSE_BRACKET*/:
+ if (matchesPeer(listBeginType, token.type)) {
+ tokenizer.readToken(); // consume token
+ return sc_reverseBang(res);
+ } else
+ throw "closing par doesn't match: " + listBeginType
+ + " " + listEndType;
+
+ case 0/*EOF*/:
+ throw "unexpected end of file";
+
+ case 10/*DOT*/:
+ tokenizer.readToken(); // consume token
+ var cdr = this.read();
+ var par = tokenizer.readToken();
+ if (!matchesPeer(listBeginType, par.type))
+ throw "closing par doesn't match: " + listBeginType
+ + " " + par.type;
+ else
+ return sc_reverseAppendBang(res, cdr);
+
+
+ default:
+ res = sc_cons(this.read(), res);
+ }
+ }
+ };
+ function readQuote() {
+ return sc_cons("quote", sc_cons(this.read(), null));
+ };
+ function readVector() {
+ // opening-parenthesis is already consumed
+ var a = new Array();
+ while (true) {
+ var token = tokenizer.peekToken();
+ switch (token.type) {
+ case 2/*CLOSE_PAR*/:
+ tokenizer.readToken();
+ return a;
+
+ default:
+ a.push(this.read());
+ }
+ }
+ };
+
+ function storeRefence(nb) {
+ var tmp = this.read();
+ this.backref[nb] = tmp;
+ return tmp;
+ };
+
+ function readReference(nb) {
+ if (nb in this.backref)
+ return this.backref[nb];
+ else
+ throw "bad reference: " + nb;
+ };
+
+ var tokenizer = this.tokenizer;
+
+ var token = tokenizer.readToken();
+
+ // handle error
+ if (token.type === 13/*ERROR*/)
+ throw token.val;
+
+ switch (token.type) {
+ case 1/*OPEN_PAR*/:
+ case 3/*OPEN_BRACE*/:
+ case 5/*OPEN_BRACKET*/:
+ return readList.call(this, token.type);
+ case 8/*QUOTE*/:
+ return readQuote.call(this);
+ case 11/*STRING*/:
+ return sc_jsstring2string(token.val);
+ case 20/*CHAR*/:
+ return new sc_Char(token.val);
+ case 14/*VECTOR_BEGIN*/:
+ return readVector.call(this);
+ case 18/*REFERENCE*/:
+ return readReference.call(this, token.val);
+ case 19/*STORE*/:
+ return storeRefence.call(this, token.val);
+ case 9/*ID*/:
+ return sc_jsstring2symbol(token.val);
+ case 0/*EOF*/:
+ case 12/*NUMBER*/:
+ case 15/*TRUE*/:
+ case 16/*FALSE*/:
+ case 17/*UNSPECIFIED*/:
+ return token.val;
+ default:
+ throw "unexpected token " + token.type + " " + token.val;
+ }
+};
+
+/*** META ((export #t)) */
+function sc_read(port) {
+ if (port === undefined) // we assume the port hasn't been given.
+ port = SC_DEFAULT_IN; // THREAD: shared var...
+ var reader = new sc_Reader(new sc_Tokenizer(port));
+ return reader.read();
+}
+/*** META ((export #t)) */
+function sc_readChar(port) {
+ if (port === undefined) // we assume the port hasn't been given.
+ port = SC_DEFAULT_IN; // THREAD: shared var...
+ var t = port.readChar();
+ return t === SC_EOF_OBJECT? t: new sc_Char(t);
+}
+/*** META ((export #t)) */
+function sc_peekChar(port) {
+ if (port === undefined) // we assume the port hasn't been given.
+ port = SC_DEFAULT_IN; // THREAD: shared var...
+ var t = port.peekChar();
+ return t === SC_EOF_OBJECT? t: new sc_Char(t);
+}
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isCharReady(port) {
+ if (port === undefined) // we assume the port hasn't been given.
+ port = SC_DEFAULT_IN; // THREAD: shared var...
+ return port.isCharReady();
+}
+/*** META ((export #t)
+ (peephole (postfix ".close()")))
+*/
+function sc_closeInputPort(p) {
+ return p.close();
+}
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix " instanceof sc_InputPort")))
+*/
+function sc_isInputPort(o) {
+ return (o instanceof sc_InputPort);
+}
+
+/*** META ((export eof-object?)
+ (type bool)
+ (peephole (postfix " === SC_EOF_OBJECT")))
+*/
+function sc_isEOFObject(o) {
+ return o === SC_EOF_OBJECT;
+}
+
+/*** META ((export #t)
+ (peephole (hole 0 "SC_DEFAULT_IN")))
+*/
+function sc_currentInputPort() {
+ return SC_DEFAULT_IN;
+}
+
+/* ------------ file operations are not supported -----------*/
+/*** META ((export #t)) */
+function sc_callWithInputFile(s, proc) {
+ throw "can't open " + s;
+}
+
+/*** META ((export #t)) */
+function sc_callWithOutputFile(s, proc) {
+ throw "can't open " + s;
+}
+
+/*** META ((export #t)) */
+function sc_withInputFromFile(s, thunk) {
+ throw "can't open " + s;
+}
+
+/*** META ((export #t)) */
+function sc_withOutputToFile(s, thunk) {
+ throw "can't open " + s;
+}
+
+/*** META ((export #t)) */
+function sc_openInputFile(s) {
+ throw "can't open " + s;
+}
+
+/*** META ((export #t)) */
+function sc_openOutputFile(s) {
+ throw "can't open " + s;
+}
+
+/* ----------------------------------------------------------------------------*/
+/*** META ((export #t)) */
+function sc_basename(p) {
+ var i = p.lastIndexOf('/');
+
+ if(i >= 0)
+ return p.substring(i + 1, p.length);
+ else
+ return '';
+}
+
+/*** META ((export #t)) */
+function sc_dirname(p) {
+ var i = p.lastIndexOf('/');
+
+ if(i >= 0)
+ return p.substring(0, i);
+ else
+ return '';
+}
+
+/* ----------------------------------------------------------------------------*/
+
+/*** META ((export #t)) */
+function sc_withInputFromPort(p, thunk) {
+ try {
+ var tmp = SC_DEFAULT_IN; // THREAD: shared var.
+ SC_DEFAULT_IN = p;
+ return thunk();
+ } finally {
+ SC_DEFAULT_IN = tmp;
+ }
+}
+
+/*** META ((export #t)) */
+function sc_withInputFromString(s, thunk) {
+ return sc_withInputFromPort(new sc_StringInputPort(sc_string2jsstring(s)), thunk);
+}
+
+/*** META ((export #t)) */
+function sc_withOutputToPort(p, thunk) {
+ try {
+ var tmp = SC_DEFAULT_OUT; // THREAD: shared var.
+ SC_DEFAULT_OUT = p;
+ return thunk();
+ } finally {
+ SC_DEFAULT_OUT = tmp;
+ }
+}
+
+/*** META ((export #t)) */
+function sc_withOutputToString(thunk) {
+ var p = new sc_StringOutputPort();
+ sc_withOutputToPort(p, thunk);
+ return p.close();
+}
+
+/*** META ((export #t)) */
+function sc_withOutputToProcedure(proc, thunk) {
+ var t = function(s) { proc(sc_jsstring2string(s)); };
+ return sc_withOutputToPort(new sc_GenericOutputPort(t), thunk);
+}
+
+/*** META ((export #t)
+ (peephole (hole 0 "new sc_StringOutputPort()")))
+*/
+function sc_openOutputString() {
+ return new sc_StringOutputPort();
+}
+
+/*** META ((export #t)) */
+function sc_openInputString(str) {
+ return new sc_StringInputPort(sc_string2jsstring(str));
+}
+
+/* ----------------------------------------------------------------------------*/
+
+function sc_OutputPort() {
+}
+sc_OutputPort.prototype = new sc_Port();
+sc_OutputPort.prototype.appendJSString = function(obj) {
+ /* do nothing */
+}
+sc_OutputPort.prototype.close = function() {
+ /* do nothing */
+}
+
+function sc_StringOutputPort() {
+ this.res = "";
+}
+sc_StringOutputPort.prototype = new sc_OutputPort();
+sc_StringOutputPort.prototype.appendJSString = function(s) {
+ this.res += s;
+}
+sc_StringOutputPort.prototype.close = function() {
+ return sc_jsstring2string(this.res);
+}
+
+/*** META ((export #t)) */
+function sc_getOutputString(sp) {
+ return sc_jsstring2string(sp.res);
+}
+
+
+function sc_ErrorOutputPort() {
+}
+sc_ErrorOutputPort.prototype = new sc_OutputPort();
+sc_ErrorOutputPort.prototype.appendJSString = function(s) {
+ throw "don't write on ErrorPort!";
+}
+sc_ErrorOutputPort.prototype.close = function() {
+ /* do nothing */
+}
+
+function sc_GenericOutputPort(appendJSString, close) {
+ this.appendJSString = appendJSString;
+ if (close)
+ this.close = close;
+}
+sc_GenericOutputPort.prototype = new sc_OutputPort();
+
+/*** META ((export #t)
+ (type bool)
+ (peephole (postfix " instanceof sc_OutputPort")))
+*/
+function sc_isOutputPort(o) {
+ return (o instanceof sc_OutputPort);
+}
+
+/*** META ((export #t)
+ (peephole (postfix ".close()")))
+*/
+function sc_closeOutputPort(p) {
+ return p.close();
+}
+
+/* ------------------ write ---------------------------------------------------*/
+
+/*** META ((export #t)) */
+function sc_write(o, p) {
+ if (p === undefined) // we assume not given
+ p = SC_DEFAULT_OUT;
+ p.appendJSString(sc_toWriteString(o));
+}
+
+function sc_toWriteString(o) {
+ if (o === null)
+ return "()";
+ else if (o === true)
+ return "#t";
+ else if (o === false)
+ return "#f";
+ else if (o === undefined)
+ return "#unspecified";
+ else if (typeof o === 'function')
+ return "#<procedure " + sc_hash(o) + ">";
+ else if (o.sc_toWriteString)
+ return o.sc_toWriteString();
+ else
+ return o.toString();
+}
+
+function sc_escapeWriteString(s) {
+ var res = "";
+ var j = 0;
+ for (i = 0; i < s.length; i++) {
+ switch (s.charAt(i)) {
+ case "\0": res += s.substring(j, i) + "\\0"; j = i + 1; break;
+ case "\b": res += s.substring(j, i) + "\\b"; j = i + 1; break;
+ case "\f": res += s.substring(j, i) + "\\f"; j = i + 1; break;
+ case "\n": res += s.substring(j, i) + "\\n"; j = i + 1; break;
+ case "\r": res += s.substring(j, i) + "\\r"; j = i + 1; break;
+ case "\t": res += s.substring(j, i) + "\\t"; j = i + 1; break;
+ case "\v": res += s.substring(j, i) + "\\v"; j = i + 1; break;
+ case '"': res += s.substring(j, i) + '\\"'; j = i + 1; break;
+ case "\\": res += s.substring(j, i) + "\\\\"; j = i + 1; break;
+ default:
+ var c = s.charAt(i);
+ if ("\a" !== "a" && c == "\a") {
+ res += s.substring(j, i) + "\\a"; j = i + 1; continue;
+ }
+ if ("\v" !== "v" && c == "\v") {
+ res += s.substring(j, i) + "\\v"; j = i + 1; continue;
+ }
+ //if (s.charAt(i) < ' ' || s.charCodeAt(i) > 127) {
+ // CARE: Manuel is this OK with HOP?
+ if (s.charAt(i) < ' ') {
+ /* non printable character and special chars */
+ res += s.substring(j, i) + "\\x" + s.charCodeAt(i).toString(16);
+ j = i + 1;
+ }
+ // else just let i increase...
+ }
+ }
+ res += s.substring(j, i);
+ return res;
+}
+
+/* ------------------ display ---------------------------------------------------*/
+
+/*** META ((export #t)) */
+function sc_display(o, p) {
+ if (p === undefined) // we assume not given
+ p = SC_DEFAULT_OUT;
+ p.appendJSString(sc_toDisplayString(o));
+}
+
+function sc_toDisplayString(o) {
+ if (o === null)
+ return "()";
+ else if (o === true)
+ return "#t";
+ else if (o === false)
+ return "#f";
+ else if (o === undefined)
+ return "#unspecified";
+ else if (typeof o === 'function')
+ return "#<procedure " + sc_hash(o) + ">";
+ else if (o.sc_toDisplayString)
+ return o.sc_toDisplayString();
+ else
+ return o.toString();
+}
+
+/* ------------------ newline ---------------------------------------------------*/
+
+/*** META ((export #t)) */
+function sc_newline(p) {
+ if (p === undefined) // we assume not given
+ p = SC_DEFAULT_OUT;
+ p.appendJSString("\n");
+}
+
+/* ------------------ write-char ---------------------------------------------------*/
+
+/*** META ((export #t)) */
+function sc_writeChar(c, p) {
+ if (p === undefined) // we assume not given
+ p = SC_DEFAULT_OUT;
+ p.appendJSString(c.val);
+}
+
+/* ------------------ write-circle ---------------------------------------------------*/
+
+/*** META ((export #t)) */
+function sc_writeCircle(o, p) {
+ if (p === undefined) // we assume not given
+ p = SC_DEFAULT_OUT;
+ p.appendJSString(sc_toWriteCircleString(o));
+}
+
+function sc_toWriteCircleString(o) {
+ var symb = sc_gensym("writeCircle");
+ var nbPointer = new Object();
+ nbPointer.nb = 0;
+ sc_prepWriteCircle(o, symb, nbPointer);
+ return sc_genToWriteCircleString(o, symb);
+}
+
+function sc_prepWriteCircle(o, symb, nbPointer) {
+ // TODO sc_Struct
+ if (o instanceof sc_Pair ||
+ o instanceof sc_Vector) {
+ if (o[symb] !== undefined) {
+ // not the first visit.
+ o[symb]++;
+ // unless there is already a number, assign one.
+ if (!o[symb + "nb"]) o[symb + "nb"] = nbPointer.nb++;
+ return;
+ }
+ o[symb] = 0;
+ if (o instanceof sc_Pair) {
+ sc_prepWriteCircle(o.car, symb, nbPointer);
+ sc_prepWriteCircle(o.cdr, symb, nbPointer);
+ } else {
+ for (var i = 0; i < o.length; i++)
+ sc_prepWriteCircle(o[i], symb, nbPointer);
+ }
+ }
+}
+
+function sc_genToWriteCircleString(o, symb) {
+ if (!(o instanceof sc_Pair ||
+ o instanceof sc_Vector))
+ return sc_toWriteString(o);
+ return o.sc_toWriteCircleString(symb);
+}
+sc_Pair.prototype.sc_toWriteCircleString = function(symb, inList) {
+ if (this[symb + "use"]) { // use-flag is set. Just use it.
+ var nb = this[symb + "nb"];
+ if (this[symb]-- === 0) { // if we are the last use. remove all fields.
+ delete this[symb];
+ delete this[symb + "nb"];
+ delete this[symb + "use"];
+ }
+ if (inList)
+ return '. #' + nb + '#';
+ else
+ return '#' + nb + '#';
+ }
+ if (this[symb]-- === 0) { // if we are the last use. remove all fields.
+ delete this[symb];
+ delete this[symb + "nb"];
+ delete this[symb + "use"];
+ }
+
+ var res = "";
+
+ if (this[symb] !== undefined) { // implies > 0
+ this[symb + "use"] = true;
+ if (inList)
+ res += '. #' + this[symb + "nb"] + '=';
+ else
+ res += '#' + this[symb + "nb"] + '=';
+ inList = false;
+ }
+
+ if (!inList)
+ res += "(";
+
+ // print car
+ res += sc_genToWriteCircleString(this.car, symb);
+
+ if (sc_isPair(this.cdr)) {
+ res += " " + this.cdr.sc_toWriteCircleString(symb, true);
+ } else if (this.cdr !== null) {
+ res += " . " + sc_genToWriteCircleString(this.cdr, symb);
+ }
+ if (!inList)
+ res += ")";
+ return res;
+};
+sc_Vector.prototype.sc_toWriteCircleString = function(symb) {
+ if (this[symb + "use"]) { // use-flag is set. Just use it.
+ var nb = this[symb + "nb"];
+ if (this[symb]-- === 0) { // if we are the last use. remove all fields.
+ delete this[symb];
+ delete this[symb + "nb"];
+ delete this[symb + "use"];
+ }
+ return '#' + nb + '#';
+ }
+ if (this[symb]-- === 0) { // if we are the last use. remove all fields.
+ delete this[symb];
+ delete this[symb + "nb"];
+ delete this[symb + "use"];
+ }
+
+ var res = "";
+ if (this[symb] !== undefined) { // implies > 0
+ this[symb + "use"] = true;
+ res += '#' + this[symb + "nb"] + '=';
+ }
+ res += "#(";
+ for (var i = 0; i < this.length; i++) {
+ res += sc_genToWriteCircleString(this[i], symb);
+ if (i < this.length - 1) res += " ";
+ }
+ res += ")";
+ return res;
+};
+
+
+/* ------------------ print ---------------------------------------------------*/
+
+/*** META ((export #t)) */
+function sc_print(s) {
+ if (arguments.length === 1) {
+ sc_display(s);
+ sc_newline();
+ }
+ else {
+ for (var i = 0; i < arguments.length; i++)
+ sc_display(arguments[i]);
+ sc_newline();
+ }
+}
+
+/* ------------------ format ---------------------------------------------------*/
+/*** META ((export #t)) */
+function sc_format(s, args) {
+ var len = s.length;
+ var p = new sc_StringOutputPort();
+ var i = 0, j = 1;
+
+ while( i < len ) {
+ var i2 = s.indexOf("~", i);
+
+ if (i2 == -1) {
+ p.appendJSString( s.substring( i, len ) );
+ return p.close();
+ } else {
+ if (i2 > i) {
+ if (i2 == (len - 1)) {
+ p.appendJSString(s.substring(i, len));
+ return p.close();
+ } else {
+ p.appendJSString(s.substring(i, i2));
+ i = i2;
+ }
+ }
+
+ switch(s.charCodeAt(i2 + 1)) {
+ case 65:
+ case 97:
+ // a
+ sc_display(arguments[j], p);
+ i += 2; j++;
+ break;
+
+ case 83:
+ case 115:
+ // s
+ sc_write(arguments[j], p);
+ i += 2; j++;
+ break;
+
+ case 86:
+ case 118:
+ // v
+ sc_display(arguments[j], p);
+ p.appendJSString("\n");
+ i += 2; j++;
+ break;
+
+ case 67:
+ case 99:
+ // c
+ p.appendJSString(String.fromCharCode(arguments[j]));
+ i += 2; j++;
+ break;
+
+ case 88:
+ case 120:
+ // x
+ p.appendJSString(arguments[j].toString(6));
+ i += 2; j++;
+ break;
+
+ case 79:
+ case 111:
+ // o
+ p.appendJSString(arguments[j].toString(8));
+ i += 2; j++;
+ break;
+
+ case 66:
+ case 98:
+ // b
+ p.appendJSString(arguments[j].toString(2));
+ i += 2; j++;
+ break;
+
+ case 37:
+ case 110:
+ // %, n
+ p.appendJSString("\n");
+ i += 2; break;
+
+ case 114:
+ // r
+ p.appendJSString("\r");
+ i += 2; break;
+
+ case 126:
+ // ~
+ p.appendJSString("~");
+ i += 2; break;
+
+ default:
+ sc_error( "format: illegal ~"
+ + String.fromCharCode(s.charCodeAt(i2 + 1))
+ + " sequence" );
+ return "";
+ }
+ }
+ }
+
+ return p.close();
+}
+
+/* ------------------ global ports ---------------------------------------------------*/
+
+var SC_DEFAULT_IN = new sc_ErrorInputPort();
+var SC_DEFAULT_OUT = new sc_ErrorOutputPort();
+var SC_ERROR_OUT = new sc_ErrorOutputPort();
+
+var sc_SYMBOL_PREFIX = "\u1E9C";
+var sc_KEYWORD_PREFIX = "\u1E9D";
+
+/*** META ((export #t)
+ (peephole (id))) */
+function sc_jsstring2string(s) {
+ return s;
+}
+
+/*** META ((export #t)
+ (peephole (prefix "'\\u1E9C' +")))
+*/
+function sc_jsstring2symbol(s) {
+ return sc_SYMBOL_PREFIX + s;
+}
+
+/*** META ((export #t)
+ (peephole (id)))
+*/
+function sc_string2jsstring(s) {
+ return s;
+}
+
+/*** META ((export #t)
+ (peephole (symbol2jsstring_immutable)))
+*/
+function sc_symbol2jsstring(s) {
+ return s.slice(1);
+}
+
+/*** META ((export #t)
+ (peephole (postfix ".slice(1)")))
+*/
+function sc_keyword2jsstring(k) {
+ return k.slice(1);
+}
+
+/*** META ((export #t)
+ (peephole (prefix "'\\u1E9D' +")))
+*/
+function sc_jsstring2keyword(s) {
+ return sc_KEYWORD_PREFIX + s;
+}
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isKeyword(s) {
+ return (typeof s === "string") &&
+ (s.charAt(0) === sc_KEYWORD_PREFIX);
+}
+
+
+/*** META ((export #t)) */
+var sc_gensym = function() {
+ var counter = 1000;
+ return function(sym) {
+ counter++;
+ if (!sym) sym = sc_SYMBOL_PREFIX;
+ return sym + "s" + counter + "~" + "^sC-GeNsYm ";
+ };
+}();
+
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isEqual(o1, o2) {
+ return ((o1 === o2) ||
+ (sc_isPair(o1) && sc_isPair(o2)
+ && sc_isPairEqual(o1, o2, sc_isEqual)) ||
+ (sc_isVector(o1) && sc_isVector(o2)
+ && sc_isVectorEqual(o1, o2, sc_isEqual)));
+}
+
+/*** META ((export number->symbol integer->symbol)) */
+function sc_number2symbol(x, radix) {
+ return sc_SYMBOL_PREFIX + sc_number2jsstring(x, radix);
+}
+
+/*** META ((export number->string integer->string)) */
+var sc_number2string = sc_number2jsstring;
+
+/*** META ((export #t)) */
+function sc_symbol2number(s, radix) {
+ return sc_jsstring2number(s.slice(1), radix);
+}
+
+/*** META ((export #t)) */
+var sc_string2number = sc_jsstring2number;
+
+/*** META ((export #t)
+ (peephole (prefix "+" s)))
+ ;; peephole will only apply if no radix is given.
+*/
+function sc_string2integer(s, radix) {
+ if (!radix) return +s;
+ return parseInt(s, radix);
+}
+
+/*** META ((export #t)
+ (peephole (prefix "+")))
+*/
+function sc_string2real(s) {
+ return +s;
+}
+
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isSymbol(s) {
+ return (typeof s === "string") &&
+ (s.charAt(0) === sc_SYMBOL_PREFIX);
+}
+
+/*** META ((export #t)
+ (peephole (symbol2string_immutable)))
+*/
+function sc_symbol2string(s) {
+ return s.slice(1);
+}
+
+/*** META ((export #t)
+ (peephole (prefix "'\\u1E9C' +")))
+*/
+function sc_string2symbol(s) {
+ return sc_SYMBOL_PREFIX + s;
+}
+
+/*** META ((export symbol-append)
+ (peephole (symbolAppend_immutable)))
+*/
+function sc_symbolAppend() {
+ var res = sc_SYMBOL_PREFIX;
+ for (var i = 0; i < arguments.length; i++)
+ res += arguments[i].slice(1);
+ return res;
+}
+
+/*** META ((export #t)
+ (peephole (postfix ".val")))
+*/
+function sc_char2string(c) { return c.val; }
+
+/*** META ((export #t)
+ (peephole (hole 1 "'\\u1E9C' + " c ".val")))
+*/
+function sc_char2symbol(c) { return sc_SYMBOL_PREFIX + c.val; }
+
+/*** META ((export #t)
+ (type bool))
+*/
+function sc_isString(s) {
+ return (typeof s === "string") &&
+ (s.charAt(0) !== sc_SYMBOL_PREFIX);
+}
+
+/*** META ((export #t)) */
+var sc_makeString = sc_makejsString;
+
+
+/*** META ((export #t)) */
+function sc_string() {
+ for (var i = 0; i < arguments.length; i++)
+ arguments[i] = arguments[i].val;
+ return "".concat.apply("", arguments);
+}
+
+/*** META ((export #t)
+ (peephole (postfix ".length")))
+*/
+function sc_stringLength(s) { return s.length; }
+
+/*** META ((export #t)) */
+function sc_stringRef(s, k) {
+ return new sc_Char(s.charAt(k));
+}
+
+/* there's no stringSet in the immutable version
+function sc_stringSet(s, k, c)
+*/
+
+
+/*** META ((export string=?)
+ (type bool)
+ (peephole (hole 2 str1 " === " str2)))
+*/
+function sc_isStringEqual(s1, s2) {
+ return s1 === s2;
+}
+/*** META ((export string<?)
+ (type bool)
+ (peephole (hole 2 str1 " < " str2)))
+*/
+function sc_isStringLess(s1, s2) {
+ return s1 < s2;
+}
+/*** META ((export string>?)
+ (type bool)
+ (peephole (hole 2 str1 " > " str2)))
+*/
+function sc_isStringGreater(s1, s2) {
+ return s1 > s2;
+}
+/*** META ((export string<=?)
+ (type bool)
+ (peephole (hole 2 str1 " <= " str2)))
+*/
+function sc_isStringLessEqual(s1, s2) {
+ return s1 <= s2;
+}
+/*** META ((export string>=?)
+ (type bool)
+ (peephole (hole 2 str1 " >= " str2)))
+*/
+function sc_isStringGreaterEqual(s1, s2) {
+ return s1 >= s2;
+}
+/*** META ((export string-ci=?)
+ (type bool)
+ (peephole (hole 2 str1 ".toLowerCase() === " str2 ".toLowerCase()")))
+*/
+function sc_isStringCIEqual(s1, s2) {
+ return s1.toLowerCase() === s2.toLowerCase();
+}
+/*** META ((export string-ci<?)
+ (type bool)
+ (peephole (hole 2 str1 ".toLowerCase() < " str2 ".toLowerCase()")))
+*/
+function sc_isStringCILess(s1, s2) {
+ return s1.toLowerCase() < s2.toLowerCase();
+}
+/*** META ((export string-ci>?)
+ (type bool)
+ (peephole (hole 2 str1 ".toLowerCase() > " str2 ".toLowerCase()")))
+*/
+function sc_isStringCIGreater(s1, s2) {
+ return s1.toLowerCase() > s2.toLowerCase();
+}
+/*** META ((export string-ci<=?)
+ (type bool)
+ (peephole (hole 2 str1 ".toLowerCase() <= " str2 ".toLowerCase()")))
+*/
+function sc_isStringCILessEqual(s1, s2) {
+ return s1.toLowerCase() <= s2.toLowerCase();
+}
+/*** META ((export string-ci>=?)
+ (type bool)
+ (peephole (hole 2 str1 ".toLowerCase() >= " str2 ".toLowerCase()")))
+*/
+function sc_isStringCIGreaterEqual(s1, s2) {
+ return s1.toLowerCase() >= s2.toLowerCase();
+}
+
+/*** META ((export #t)
+ (peephole (hole 3 s ".substring(" start ", " end ")")))
+*/
+function sc_substring(s, start, end) {
+ return s.substring(start, end);
+}
+
+/*** META ((export #t))
+*/
+function sc_isSubstring_at(s1, s2, i) {
+ return s2 == s1.substring(i, i+ s2.length);
+}
+
+/*** META ((export #t)
+ (peephole (infix 0 #f "+" "''")))
+*/
+function sc_stringAppend() {
+ return "".concat.apply("", arguments);
+}
+
+/*** META ((export #t)) */
+var sc_string2list = sc_jsstring2list;
+
+/*** META ((export #t)) */
+var sc_list2string = sc_list2jsstring;
+
+/*** META ((export #t)
+ (peephole (id)))
+*/
+function sc_stringCopy(s) {
+ return s;
+}
+
+/* there's no string-fill in the immutable version
+function sc_stringFill(s, c)
+*/
+
+/*** META ((export #t)
+ (peephole (postfix ".slice(1)")))
+*/
+function sc_keyword2string(o) {
+ return o.slice(1);
+}
+
+/*** META ((export #t)
+ (peephole (prefix "'\\u1E9D' +")))
+*/
+function sc_string2keyword(o) {
+ return sc_KEYWORD_PREFIX + o;
+}
+
+String.prototype.sc_toDisplayString = function() {
+ if (this.charAt(0) === sc_SYMBOL_PREFIX)
+ // TODO: care for symbols with spaces (escape-chars symbols).
+ return this.slice(1);
+ else if (this.charAt(0) === sc_KEYWORD_PREFIX)
+ return ":" + this.slice(1);
+ else
+ return this.toString();
+};
+
+String.prototype.sc_toWriteString = function() {
+ if (this.charAt(0) === sc_SYMBOL_PREFIX)
+ // TODO: care for symbols with spaces (escape-chars symbols).
+ return this.slice(1);
+ else if (this.charAt(0) === sc_KEYWORD_PREFIX)
+ return ":" + this.slice(1);
+ else
+ return '"' + sc_escapeWriteString(this) + '"';
+};
+/* Exported Variables */
+var BgL_testzd2boyerzd2;
+var BgL_nboyerzd2benchmarkzd2;
+var BgL_setupzd2boyerzd2;
+/* End Exports */
+
+var translate_term_nboyer;
+var translate_args_nboyer;
+var untranslate_term_nboyer;
+var BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer;
+var BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer;
+var translate_alist_nboyer;
+var apply_subst_nboyer;
+var apply_subst_lst_nboyer;
+var tautologyp_nboyer;
+var if_constructor_nboyer;
+var rewrite_count_nboyer;
+var rewrite_nboyer;
+var rewrite_args_nboyer;
+var unify_subst_nboyer;
+var one_way_unify1_nboyer;
+var false_term_nboyer;
+var true_term_nboyer;
+var trans_of_implies1_nboyer;
+var is_term_equal_nboyer;
+var is_term_member_nboyer;
+var const_nboyer;
+var sc_const_3_nboyer;
+var sc_const_4_nboyer;
+{
+ (sc_const_4_nboyer = (new sc_Pair("\u1E9Cimplies",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cu",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cu",(new sc_Pair("\u1E9Cw",null)))))),null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cw",null)))))),null)))))));
+ (sc_const_3_nboyer = sc_list((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccompile",(new sc_Pair("\u1E9Cform",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Ccodegen",(new sc_Pair((new sc_Pair("\u1E9Coptimize",(new sc_Pair("\u1E9Cform",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ceqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreaterp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clesseqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatereqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cboolean",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ciff",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ceven1",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Codd",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccountps-",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cpred",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccountps-loop",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cpred",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfact-",(new sc_Pair("\u1E9Ci",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfact-loop",(new sc_Pair("\u1E9Ci",(new sc_Pair((1),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdivides",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassume-true",(new sc_Pair("\u1E9Cvar",(new sc_Pair("\u1E9Calist",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cvar",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))),(new sc_Pair("\u1E9Calist",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassume-false",(new sc_Pair("\u1E9Cvar",(new sc_Pair("\u1E9Calist",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cvar",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))),(new sc_Pair("\u1E9Calist",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctautology-checker",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ctautologyp",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfalsify",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfalsify1",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cprime",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))),null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cprime1",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cx",null)))),null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair("\u1E9Cp",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cimplies",(new sc_Pair("\u1E9Cp",(new sc_Pair("\u1E9Cq",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cp",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cq",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair((new sc_Pair("\u1E9Cf",null)),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Ct",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))))),(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cd",(new sc_Pair("\u1E9Ce",null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cb",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cc",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cplus-fringe",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Ca",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cpds",(new sc_Pair("\u1E9Cenvrn",null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cexec",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cpds",(new sc_Pair("\u1E9Cenvrn",null)))))))),(new sc_Pair("\u1E9Cenvrn",null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmc-flatten",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cb",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Cintersect",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ck",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Ck",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ck",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair((new sc_Pair("\u1E9Cexp",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair("\u1E9Ck",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Cy",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Creverse-loop",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Csort-lp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ccount-list",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus1",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cl",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair("\u1E9Ci",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cbase",null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ci",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cj",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cj",(new sc_Pair((1),null)))))),null)))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Ci",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cpower-eval",(new sc_Pair((new sc_Pair("\u1E9Cbig-plus",(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cpower-rep",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Cbase",null)))))))))),(new sc_Pair("\u1E9Cbase",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cj",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Ca",null)))),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cw",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Cx",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cc",null)))))),null)))))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cb",(new sc_Pair("\u1E9Cc",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cz",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Cgcd",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cvalue",(new sc_Pair((new sc_Pair("\u1E9Cnormalize",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cvalue",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cy",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnlistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csamefringe",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((1),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((1),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair((new sc_Pair("\u1E9Cgreatest-factor",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ctimes-list",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cprime-list",(new sc_Pair("\u1E9Cy",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Cz",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cz",null)))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cz",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cw",(new sc_Pair((1),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cgreatereqp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cor",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cand",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cy",(new sc_Pair((1),null)))))),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((1),null)))))),(new sc_Pair(sc_list("\u1E9Cand", (new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))), (new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair("\u1E9Cb",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))), (new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Ca",null)))), (new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cb",null)))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csub1",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cl",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cl",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair("\u1E9Cl",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdsort",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Csort2",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx1",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx2",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx3",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx4",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx5",(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair("\u1E9Cx6",(new sc_Pair("\u1E9Cx7",null)))))),null)))))),null)))))),null)))))),null)))))),null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((6),(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cx7",null)))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((2),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((2),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair("\u1E9Cy",(new sc_Pair((2),null)))))),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Csigma",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ci",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Ci",null)))),null)))))),(new sc_Pair((2),null)))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cz",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnot",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cz",null)))),null)))))),null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair((new sc_Pair("\u1E9Cdelete",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmeaning",(new sc_Pair((new sc_Pair("\u1E9Cplus-tree",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair("\u1E9Ca",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cadd1",(new sc_Pair("\u1E9Cy",null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cnumberp",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cnth",(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Ci",null)))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair("\u1E9Cb",null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Ca",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Clast",(new sc_Pair("\u1E9Ca",null)))),null)))),(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair("\u1E9Cb",null)))))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clessp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ct",null)),(new sc_Pair("\u1E9Cz",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cf",null)),(new sc_Pair("\u1E9Cz",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Cassignedp",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Ca",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cassignment",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cb",null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccar",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair((new sc_Pair("\u1E9Ccdr",(new sc_Pair((new sc_Pair("\u1E9Cgopher",(new sc_Pair("\u1E9Cx",null)))),null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Clistp",(new sc_Pair("\u1E9Cx",null)))),(new sc_Pair((new sc_Pair("\u1E9Ccdr",(new sc_Pair((new sc_Pair("\u1E9Cflatten",(new sc_Pair("\u1E9Cx",null)))),null)))),(new sc_Pair((new sc_Pair("\u1E9Ccons",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cquotient",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cx",null)))))),(new sc_Pair("\u1E9Cy",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Czerop",(new sc_Pair("\u1E9Cy",null)))),(new sc_Pair((new sc_Pair("\u1E9Czero",null)),(new sc_Pair((new sc_Pair("\u1E9Cfix",(new sc_Pair("\u1E9Cx",null)))),null)))))))),null)))))), (new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cget",(new sc_Pair("\u1E9Cj",(new sc_Pair((new sc_Pair("\u1E9Cset",(new sc_Pair("\u1E9Ci",(new sc_Pair("\u1E9Cval",(new sc_Pair("\u1E9Cmem",null)))))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cif",(new sc_Pair((new sc_Pair("\u1E9Ceqp",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Ci",null)))))),(new sc_Pair("\u1E9Cval",(new sc_Pair((new sc_Pair("\u1E9Cget",(new sc_Pair("\u1E9Cj",(new sc_Pair("\u1E9Cmem",null)))))),null)))))))),null))))))));
+ (const_nboyer = (new sc_Pair((new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cc",(new sc_Pair((new sc_Pair("\u1E9Czero",null)),null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cy",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair((new sc_Pair("\u1E9Ctimes",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Cc",(new sc_Pair("\u1E9Cd",null)))))),null)))))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cz",(new sc_Pair("\u1E9Cf",(new sc_Pair((new sc_Pair("\u1E9Creverse",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair((new sc_Pair("\u1E9Cappend",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cnil",null)),null)))))),null)))),null)))))),(new sc_Pair((new sc_Pair("\u1E9Cu",(new sc_Pair("\u1E9Cequal",(new sc_Pair((new sc_Pair("\u1E9Cplus",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cdifference",(new sc_Pair("\u1E9Cx",(new sc_Pair("\u1E9Cy",null)))))),null)))))))),(new sc_Pair((new sc_Pair("\u1E9Cw",(new sc_Pair("\u1E9Clessp",(new sc_Pair((new sc_Pair("\u1E9Cremainder",(new sc_Pair("\u1E9Ca",(new sc_Pair("\u1E9Cb",null)))))),(new sc_Pair((new sc_Pair("\u1E9Cmember",(new sc_Pair("\u1E9Ca",(new sc_Pair((new sc_Pair("\u1E9Clength",(new sc_Pair("\u1E9Cb",null)))),null)))))),null)))))))),null)))))))))));
+ BgL_nboyerzd2benchmarkzd2 = function() {
+ var args = null;
+ for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) {
+ args = sc_cons(arguments[sc_tmp], args);
+ }
+ var n;
+ return ((n = ((args === null)?(0):(args.car))), (BgL_setupzd2boyerzd2()), (BgL_runzd2benchmarkzd2(("nboyer"+(sc_number2string(n))), (1), function() {
+ return (BgL_testzd2boyerzd2(n));
+ }, function(rewrites) {
+ if ((sc_isNumber(rewrites)))
+ switch (n) {
+ case (0):
+ return (rewrites===(95024));
+ break;
+ case (1):
+ return (rewrites===(591777));
+ break;
+ case (2):
+ return (rewrites===(1813975));
+ break;
+ case (3):
+ return (rewrites===(5375678));
+ break;
+ case (4):
+ return (rewrites===(16445406));
+ break;
+ case (5):
+ return (rewrites===(51507739));
+ break;
+ default:
+ return true;
+ break;
+ }
+ else
+ return false;
+ })));
+ };
+ BgL_setupzd2boyerzd2 = function() {
+ return true;
+ };
+ BgL_testzd2boyerzd2 = function() {
+ return true;
+ };
+ translate_term_nboyer = function(term) {
+ var lst;
+ return (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), ((lst = (term.cdr)), ((lst === null)?null:(new sc_Pair((translate_term_nboyer((lst.car))), (translate_args_nboyer((lst.cdr))))))))));
+ };
+ translate_args_nboyer = function(lst) {
+ var sc_lst_5;
+ var term;
+ return ((lst === null)?null:(new sc_Pair(((term = (lst.car)), (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))), ((sc_lst_5 = (lst.cdr)), ((sc_lst_5 === null)?null:(new sc_Pair((translate_term_nboyer((sc_lst_5.car))), (translate_args_nboyer((sc_lst_5.cdr))))))))));
+ };
+ untranslate_term_nboyer = function(term) {
+ var optrOpnd;
+ var tail1131;
+ var L1127;
+ var falseHead1130;
+ var symbol_record;
+ if (!(term instanceof sc_Pair))
+ return term;
+ else
+ {
+ (falseHead1130 = (new sc_Pair(null, null)));
+ (L1127 = (term.cdr));
+ (tail1131 = falseHead1130);
+ while (!(L1127 === null)) {
+ {
+ (tail1131.cdr = (new sc_Pair((untranslate_term_nboyer((L1127.car))), null)));
+ (tail1131 = (tail1131.cdr));
+ (L1127 = (L1127.cdr));
+ }
+ }
+ (optrOpnd = (falseHead1130.cdr));
+ return (new sc_Pair(((symbol_record = (term.car)), (symbol_record[(0)])), optrOpnd));
+ }
+ };
+ BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer = function(sym) {
+ var r;
+ var x;
+ return ((x = (sc_assq(sym, BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), ((x!== false)?(x.cdr):((r = [sym, null]), (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = (new sc_Pair((new sc_Pair(sym, r)), BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), r)));
+ };
+ (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null);
+ translate_alist_nboyer = function(alist) {
+ var sc_alist_6;
+ var term;
+ return ((alist === null)?null:(new sc_Pair((new sc_Pair((alist.car.car), ((term = (alist.car.cdr)), (!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))))), ((sc_alist_6 = (alist.cdr)), ((sc_alist_6 === null)?null:(new sc_Pair((new sc_Pair((sc_alist_6.car.car), (translate_term_nboyer((sc_alist_6.car.cdr))))), (translate_alist_nboyer((sc_alist_6.cdr))))))))));
+ };
+ apply_subst_nboyer = function(alist, term) {
+ var lst;
+ var temp_temp;
+ return (!(term instanceof sc_Pair)?((temp_temp = (sc_assq(term, alist))), ((temp_temp!== false)?(temp_temp.cdr):term)):(new sc_Pair((term.car), ((lst = (term.cdr)), ((lst === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (lst.car))), (apply_subst_lst_nboyer(alist, (lst.cdr))))))))));
+ };
+ apply_subst_lst_nboyer = function(alist, lst) {
+ var sc_lst_7;
+ return ((lst === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (lst.car))), ((sc_lst_7 = (lst.cdr)), ((sc_lst_7 === null)?null:(new sc_Pair((apply_subst_nboyer(alist, (sc_lst_7.car))), (apply_subst_lst_nboyer(alist, (sc_lst_7.cdr))))))))));
+ };
+ tautologyp_nboyer = function(sc_x_11, true_lst, false_lst) {
+ var tmp1125;
+ var x;
+ var tmp1126;
+ var sc_x_8;
+ var sc_tmp1125_9;
+ var sc_tmp1126_10;
+ var sc_x_11;
+ var true_lst;
+ var false_lst;
+ while (true) {
+ if ((((sc_tmp1126_10 = (is_term_equal_nboyer(sc_x_11, true_term_nboyer))), ((sc_tmp1126_10!== false)?sc_tmp1126_10:(is_term_member_nboyer(sc_x_11, true_lst))))!== false))
+ return true;
+ else
+ if ((((sc_tmp1125_9 = (is_term_equal_nboyer(sc_x_11, false_term_nboyer))), ((sc_tmp1125_9!== false)?sc_tmp1125_9:(is_term_member_nboyer(sc_x_11, false_lst))))!== false))
+ return false;
+ else
+ if (!(sc_x_11 instanceof sc_Pair))
+ return false;
+ else
+ if (((sc_x_11.car)===if_constructor_nboyer))
+ if ((((sc_x_8 = (sc_x_11.cdr.car)), (tmp1126 = (is_term_equal_nboyer(sc_x_8, true_term_nboyer))), ((tmp1126!== false)?tmp1126:(is_term_member_nboyer(sc_x_8, true_lst))))!== false))
+ (sc_x_11 = (sc_x_11.cdr.cdr.car));
+ else
+ if ((((x = (sc_x_11.cdr.car)), (tmp1125 = (is_term_equal_nboyer(x, false_term_nboyer))), ((tmp1125!== false)?tmp1125:(is_term_member_nboyer(x, false_lst))))!== false))
+ (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car));
+ else
+ if (((tautologyp_nboyer((sc_x_11.cdr.cdr.car), (new sc_Pair((sc_x_11.cdr.car), true_lst)), false_lst))!== false))
+ {
+ (false_lst = (new sc_Pair((sc_x_11.cdr.car), false_lst)));
+ (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car));
+ }
+ else
+ return false;
+ else
+ return false;
+ }
+ };
+ (if_constructor_nboyer = "\u1E9C*");
+ (rewrite_count_nboyer = (0));
+ rewrite_nboyer = function(term) {
+ var term2;
+ var sc_term_12;
+ var lst;
+ var symbol_record;
+ var sc_lst_13;
+ {
+ (++rewrite_count_nboyer);
+ if (!(term instanceof sc_Pair))
+ return term;
+ else
+ {
+ (sc_term_12 = (new sc_Pair((term.car), ((sc_lst_13 = (term.cdr)), ((sc_lst_13 === null)?null:(new sc_Pair((rewrite_nboyer((sc_lst_13.car))), (rewrite_args_nboyer((sc_lst_13.cdr))))))))));
+ (lst = ((symbol_record = (term.car)), (symbol_record[(1)])));
+ while (true) {
+ if ((lst === null))
+ return sc_term_12;
+ else
+ if ((((term2 = ((lst.car).cdr.car)), (unify_subst_nboyer = null), (one_way_unify1_nboyer(sc_term_12, term2)))!== false))
+ return (rewrite_nboyer((apply_subst_nboyer(unify_subst_nboyer, ((lst.car).cdr.cdr.car)))));
+ else
+ (lst = (lst.cdr));
+ }
+ }
+ }
+ };
+ rewrite_args_nboyer = function(lst) {
+ var sc_lst_14;
+ return ((lst === null)?null:(new sc_Pair((rewrite_nboyer((lst.car))), ((sc_lst_14 = (lst.cdr)), ((sc_lst_14 === null)?null:(new sc_Pair((rewrite_nboyer((sc_lst_14.car))), (rewrite_args_nboyer((sc_lst_14.cdr))))))))));
+ };
+ (unify_subst_nboyer = "\u1E9C*");
+ one_way_unify1_nboyer = function(term1, term2) {
+ var lst1;
+ var lst2;
+ var temp_temp;
+ if (!(term2 instanceof sc_Pair))
+ {
+ (temp_temp = (sc_assq(term2, unify_subst_nboyer)));
+ if ((temp_temp!== false))
+ return (is_term_equal_nboyer(term1, (temp_temp.cdr)));
+ else
+ if ((sc_isNumber(term2)))
+ return (sc_isEqual(term1, term2));
+ else
+ {
+ (unify_subst_nboyer = (new sc_Pair((new sc_Pair(term2, term1)), unify_subst_nboyer)));
+ return true;
+ }
+ }
+ else
+ if (!(term1 instanceof sc_Pair))
+ return false;
+ else
+ if (((term1.car)===(term2.car)))
+ {
+ (lst1 = (term1.cdr));
+ (lst2 = (term2.cdr));
+ while (true) {
+ if ((lst1 === null))
+ return (lst2 === null);
+ else
+ if ((lst2 === null))
+ return false;
+ else
+ if (((one_way_unify1_nboyer((lst1.car), (lst2.car)))!== false))
+ {
+ (lst1 = (lst1.cdr));
+ (lst2 = (lst2.cdr));
+ }
+ else
+ return false;
+ }
+ }
+ else
+ return false;
+ };
+ (false_term_nboyer = "\u1E9C*");
+ (true_term_nboyer = "\u1E9C*");
+ trans_of_implies1_nboyer = function(n) {
+ var sc_n_15;
+ return ((sc_isEqual(n, (1)))?(sc_list("\u1E9Cimplies", (0), (1))):(sc_list("\u1E9Cand", (sc_list("\u1E9Cimplies", (n-(1)), n)), ((sc_n_15 = (n-(1))), ((sc_isEqual(sc_n_15, (1)))?(sc_list("\u1E9Cimplies", (0), (1))):(sc_list("\u1E9Cand", (sc_list("\u1E9Cimplies", (sc_n_15-(1)), sc_n_15)), (trans_of_implies1_nboyer((sc_n_15-(1)))))))))));
+ };
+ is_term_equal_nboyer = function(x, y) {
+ var lst1;
+ var lst2;
+ var r2;
+ var r1;
+ if ((x instanceof sc_Pair))
+ if ((y instanceof sc_Pair))
+ if ((((r1 = (x.car)), (r2 = (y.car)), (r1===r2))!== false))
+ {
+ (lst1 = (x.cdr));
+ (lst2 = (y.cdr));
+ while (true) {
+ if ((lst1 === null))
+ return (lst2 === null);
+ else
+ if ((lst2 === null))
+ return false;
+ else
+ if (((is_term_equal_nboyer((lst1.car), (lst2.car)))!== false))
+ {
+ (lst1 = (lst1.cdr));
+ (lst2 = (lst2.cdr));
+ }
+ else
+ return false;
+ }
+ }
+ else
+ return false;
+ else
+ return false;
+ else
+ return (sc_isEqual(x, y));
+ };
+ is_term_member_nboyer = function(x, lst) {
+ var x;
+ var lst;
+ while (true) {
+ if ((lst === null))
+ return false;
+ else
+ if (((is_term_equal_nboyer(x, (lst.car)))!== false))
+ return true;
+ else
+ (lst = (lst.cdr));
+ }
+ };
+ BgL_setupzd2boyerzd2 = function() {
+ var symbol_record;
+ var value;
+ var BgL_sc_symbolzd2record_16zd2;
+ var sym;
+ var sc_sym_17;
+ var term;
+ var lst;
+ var sc_term_18;
+ var sc_term_19;
+ {
+ (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null);
+ (if_constructor_nboyer = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer("\u1E9Cif")));
+ (false_term_nboyer = ((sc_term_19 = (new sc_Pair("\u1E9Cf",null))), (!(sc_term_19 instanceof sc_Pair)?sc_term_19:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_19.car))), (translate_args_nboyer((sc_term_19.cdr))))))));
+ (true_term_nboyer = ((sc_term_18 = (new sc_Pair("\u1E9Ct",null))), (!(sc_term_18 instanceof sc_Pair)?sc_term_18:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_18.car))), (translate_args_nboyer((sc_term_18.cdr))))))));
+ (lst = sc_const_3_nboyer);
+ while (!(lst === null)) {
+ {
+ (term = (lst.car));
+ if (((term instanceof sc_Pair)&&(((term.car)==="\u1E9Cequal")&&((term.cdr.car) instanceof sc_Pair))))
+ {
+ (sc_sym_17 = ((term.cdr.car).car));
+ (value = (new sc_Pair((!(term instanceof sc_Pair)?term:(new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr)))))), ((sym = ((term.cdr.car).car)), (BgL_sc_symbolzd2record_16zd2 = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sym))), (BgL_sc_symbolzd2record_16zd2[(1)])))));
+ (symbol_record = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sc_sym_17)));
+ (symbol_record[(1)] = value);
+ }
+ else
+ (sc_error("ADD-LEMMA did not like term: ", term));
+ (lst = (lst.cdr));
+ }
+ }
+ return true;
+ }
+ };
+ BgL_testzd2boyerzd2 = function(n) {
+ var optrOpnd;
+ var term;
+ var sc_n_20;
+ var answer;
+ var sc_term_21;
+ var sc_term_22;
+ {
+ (rewrite_count_nboyer = (0));
+ (term = sc_const_4_nboyer);
+ (sc_n_20 = n);
+ while (!(sc_n_20=== 0)) {
+ {
+ (term = (sc_list("\u1E9Cor", term, (new sc_Pair("\u1E9Cf",null)))));
+ (--sc_n_20);
+ }
+ }
+ (sc_term_22 = term);
+ if (!(sc_term_22 instanceof sc_Pair))
+ (optrOpnd = sc_term_22);
+ else
+ (optrOpnd = (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_22.car))), (translate_args_nboyer((sc_term_22.cdr))))));
+ (sc_term_21 = (apply_subst_nboyer(((const_nboyer === null)?null:(new sc_Pair((new sc_Pair((const_nboyer.car.car), (translate_term_nboyer((const_nboyer.car.cdr))))), (translate_alist_nboyer((const_nboyer.cdr)))))), optrOpnd)));
+ (answer = (tautologyp_nboyer((rewrite_nboyer(sc_term_21)), null, null)));
+ (sc_write(rewrite_count_nboyer));
+ (sc_display(" rewrites"));
+ (sc_newline());
+ if ((answer!== false))
+ return rewrite_count_nboyer;
+ else
+ return false;
+ }
+ };
+}
+/* Exported Variables */
+var BgL_parsezd2ze3nbzd2treesze3;
+var BgL_earleyzd2benchmarkzd2;
+var BgL_parsezd2ze3parsedzf3zc2;
+var test;
+var BgL_parsezd2ze3treesz31;
+var BgL_makezd2parserzd2;
+/* End Exports */
+
+var const_earley;
+{
+ (const_earley = (new sc_Pair((new sc_Pair("\u1E9Cs",(new sc_Pair((new sc_Pair("\u1E9Ca",null)),(new sc_Pair((new sc_Pair("\u1E9Cs",(new sc_Pair("\u1E9Cs",null)))),null)))))),null)));
+ BgL_makezd2parserzd2 = function(grammar, lexer) {
+ var i;
+ var parser_descr;
+ var def_loop;
+ var nb_nts;
+ var names;
+ var steps;
+ var predictors;
+ var enders;
+ var starters;
+ var nts;
+ var sc_names_1;
+ var sc_steps_2;
+ var sc_predictors_3;
+ var sc_enders_4;
+ var sc_starters_5;
+ var nb_confs;
+ var BgL_sc_defzd2loop_6zd2;
+ var BgL_sc_nbzd2nts_7zd2;
+ var sc_nts_8;
+ var BgL_sc_defzd2loop_9zd2;
+ var ind;
+ {
+ ind = function(nt, sc_nts_10) {
+ var i;
+ {
+ (i = ((sc_nts_10.length)-(1)));
+ while (true) {
+ if ((i>=(0)))
+ if ((sc_isEqual((sc_nts_10[i]), nt)))
+ return i;
+ else
+ (--i);
+ else
+ return false;
+ }
+ }
+ };
+ (sc_nts_8 = ((BgL_sc_defzd2loop_9zd2 = function(defs, sc_nts_11) {
+ var rule_loop;
+ var head;
+ var def;
+ return ((defs instanceof sc_Pair)?((def = (defs.car)), (head = (def.car)), (rule_loop = function(rules, sc_nts_12) {
+ var nt;
+ var l;
+ var sc_nts_13;
+ var rule;
+ if ((rules instanceof sc_Pair))
+ {
+ (rule = (rules.car));
+ (l = rule);
+ (sc_nts_13 = sc_nts_12);
+ while ((l instanceof sc_Pair)) {
+ {
+ (nt = (l.car));
+ (l = (l.cdr));
+ (sc_nts_13 = (((sc_member(nt, sc_nts_13))!== false)?sc_nts_13:(new sc_Pair(nt, sc_nts_13))));
+ }
+ }
+ return (rule_loop((rules.cdr), sc_nts_13));
+ }
+ else
+ return (BgL_sc_defzd2loop_9zd2((defs.cdr), sc_nts_12));
+ }), (rule_loop((def.cdr), (((sc_member(head, sc_nts_11))!== false)?sc_nts_11:(new sc_Pair(head, sc_nts_11)))))):(sc_list2vector((sc_reverse(sc_nts_11)))));
+ }), (BgL_sc_defzd2loop_9zd2(grammar, null))));
+ (BgL_sc_nbzd2nts_7zd2 = (sc_nts_8.length));
+ (nb_confs = (((BgL_sc_defzd2loop_6zd2 = function(defs, BgL_sc_nbzd2confs_14zd2) {
+ var rule_loop;
+ var def;
+ return ((defs instanceof sc_Pair)?((def = (defs.car)), (rule_loop = function(rules, BgL_sc_nbzd2confs_15zd2) {
+ var l;
+ var BgL_sc_nbzd2confs_16zd2;
+ var rule;
+ if ((rules instanceof sc_Pair))
+ {
+ (rule = (rules.car));
+ (l = rule);
+ (BgL_sc_nbzd2confs_16zd2 = BgL_sc_nbzd2confs_15zd2);
+ while ((l instanceof sc_Pair)) {
+ {
+ (l = (l.cdr));
+ (++BgL_sc_nbzd2confs_16zd2);
+ }
+ }
+ return (rule_loop((rules.cdr), (BgL_sc_nbzd2confs_16zd2+(1))));
+ }
+ else
+ return (BgL_sc_defzd2loop_6zd2((defs.cdr), BgL_sc_nbzd2confs_15zd2));
+ }), (rule_loop((def.cdr), BgL_sc_nbzd2confs_14zd2))):BgL_sc_nbzd2confs_14zd2);
+ }), (BgL_sc_defzd2loop_6zd2(grammar, (0))))+BgL_sc_nbzd2nts_7zd2));
+ (sc_starters_5 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null)));
+ (sc_enders_4 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null)));
+ (sc_predictors_3 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null)));
+ (sc_steps_2 = (sc_makeVector(nb_confs, false)));
+ (sc_names_1 = (sc_makeVector(nb_confs, false)));
+ (nts = sc_nts_8);
+ (starters = sc_starters_5);
+ (enders = sc_enders_4);
+ (predictors = sc_predictors_3);
+ (steps = sc_steps_2);
+ (names = sc_names_1);
+ (nb_nts = (sc_nts_8.length));
+ (i = (nb_nts-(1)));
+ while ((i>=(0))) {
+ {
+ (sc_steps_2[i] = (i-nb_nts));
+ (sc_names_1[i] = (sc_list((sc_nts_8[i]), (0))));
+ (sc_enders_4[i] = (sc_list(i)));
+ (--i);
+ }
+ }
+ def_loop = function(defs, conf) {
+ var rule_loop;
+ var head;
+ var def;
+ return ((defs instanceof sc_Pair)?((def = (defs.car)), (head = (def.car)), (rule_loop = function(rules, conf, rule_num) {
+ var i;
+ var sc_i_17;
+ var nt;
+ var l;
+ var sc_conf_18;
+ var sc_i_19;
+ var rule;
+ if ((rules instanceof sc_Pair))
+ {
+ (rule = (rules.car));
+ (names[conf] = (sc_list(head, rule_num)));
+ (sc_i_19 = (ind(head, nts)));
+ (starters[sc_i_19] = (new sc_Pair(conf, (starters[sc_i_19]))));
+ (l = rule);
+ (sc_conf_18 = conf);
+ while ((l instanceof sc_Pair)) {
+ {
+ (nt = (l.car));
+ (steps[sc_conf_18] = (ind(nt, nts)));
+ (sc_i_17 = (ind(nt, nts)));
+ (predictors[sc_i_17] = (new sc_Pair(sc_conf_18, (predictors[sc_i_17]))));
+ (l = (l.cdr));
+ (++sc_conf_18);
+ }
+ }
+ (steps[sc_conf_18] = ((ind(head, nts))-nb_nts));
+ (i = (ind(head, nts)));
+ (enders[i] = (new sc_Pair(sc_conf_18, (enders[i]))));
+ return (rule_loop((rules.cdr), (sc_conf_18+(1)), (rule_num+(1))));
+ }
+ else
+ return (def_loop((defs.cdr), conf));
+ }), (rule_loop((def.cdr), conf, (1)))):undefined);
+ };
+ (def_loop(grammar, (sc_nts_8.length)));
+ (parser_descr = [lexer, sc_nts_8, sc_starters_5, sc_enders_4, sc_predictors_3, sc_steps_2, sc_names_1]);
+ return function(input) {
+ var optrOpnd;
+ var sc_optrOpnd_20;
+ var sc_optrOpnd_21;
+ var sc_optrOpnd_22;
+ var loop1;
+ var BgL_sc_stateza2_23za2;
+ var toks;
+ var BgL_sc_nbzd2nts_24zd2;
+ var sc_steps_25;
+ var sc_enders_26;
+ var state_num;
+ var BgL_sc_statesza2_27za2;
+ var states;
+ var i;
+ var conf;
+ var l;
+ var tok_nts;
+ var sc_i_28;
+ var sc_i_29;
+ var l1;
+ var l2;
+ var tok;
+ var tail1129;
+ var L1125;
+ var goal_enders;
+ var BgL_sc_statesza2_30za2;
+ var BgL_sc_nbzd2nts_31zd2;
+ var BgL_sc_nbzd2confs_32zd2;
+ var nb_toks;
+ var goal_starters;
+ var sc_states_33;
+ var BgL_sc_nbzd2confs_34zd2;
+ var BgL_sc_nbzd2toks_35zd2;
+ var sc_toks_36;
+ var falseHead1128;
+ var sc_names_37;
+ var sc_steps_38;
+ var sc_predictors_39;
+ var sc_enders_40;
+ var sc_starters_41;
+ var sc_nts_42;
+ var lexer;
+ var sc_ind_43;
+ var make_states;
+ var BgL_sc_confzd2setzd2getza2_44za2;
+ var conf_set_merge_new_bang;
+ var conf_set_adjoin;
+ var BgL_sc_confzd2setzd2adjoinza2_45za2;
+ var BgL_sc_confzd2setzd2adjoinza2za2_46z00;
+ var conf_set_union;
+ var forw;
+ var is_parsed;
+ var deriv_trees;
+ var BgL_sc_derivzd2treesza2_47z70;
+ var nb_deriv_trees;
+ var BgL_sc_nbzd2derivzd2treesza2_48za2;
+ {
+ sc_ind_43 = function(nt, sc_nts_49) {
+ var i;
+ {
+ (i = ((sc_nts_49.length)-(1)));
+ while (true) {
+ if ((i>=(0)))
+ if ((sc_isEqual((sc_nts_49[i]), nt)))
+ return i;
+ else
+ (--i);
+ else
+ return false;
+ }
+ }
+ };
+ make_states = function(BgL_sc_nbzd2toks_50zd2, BgL_sc_nbzd2confs_51zd2) {
+ var v;
+ var i;
+ var sc_states_52;
+ {
+ (sc_states_52 = (sc_makeVector((BgL_sc_nbzd2toks_50zd2+(1)), false)));
+ (i = BgL_sc_nbzd2toks_50zd2);
+ while ((i>=(0))) {
+ {
+ (v = (sc_makeVector((BgL_sc_nbzd2confs_51zd2+(1)), false)));
+ (v[(0)] = (-1));
+ (sc_states_52[i] = v);
+ (--i);
+ }
+ }
+ return sc_states_52;
+ }
+ };
+ BgL_sc_confzd2setzd2getza2_44za2 = function(state, BgL_sc_statezd2num_53zd2, sc_conf_54) {
+ var conf_set;
+ var BgL_sc_confzd2set_55zd2;
+ return ((BgL_sc_confzd2set_55zd2 = (state[(sc_conf_54+(1))])), ((BgL_sc_confzd2set_55zd2!== false)?BgL_sc_confzd2set_55zd2:((conf_set = (sc_makeVector((BgL_sc_statezd2num_53zd2+(6)), false))), (conf_set[(1)] = (-3)), (conf_set[(2)] = (-1)), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1)), (state[(sc_conf_54+(1))] = conf_set), conf_set)));
+ };
+ conf_set_merge_new_bang = function(conf_set) {
+ return ((conf_set[((conf_set[(1)])+(5))] = (conf_set[(4)])), (conf_set[(1)] = (conf_set[(3)])), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1)));
+ };
+ conf_set_adjoin = function(state, conf_set, sc_conf_56, i) {
+ var tail;
+ return ((tail = (conf_set[(3)])), (conf_set[(i+(5))] = (-1)), (conf_set[(tail+(5))] = i), (conf_set[(3)] = i), ((tail<(0))?((conf_set[(0)] = (state[(0)])), (state[(0)] = sc_conf_56)):undefined));
+ };
+ BgL_sc_confzd2setzd2adjoinza2_45za2 = function(sc_states_57, BgL_sc_statezd2num_58zd2, l, i) {
+ var conf_set;
+ var sc_conf_59;
+ var l1;
+ var state;
+ {
+ (state = (sc_states_57[BgL_sc_statezd2num_58zd2]));
+ (l1 = l);
+ while ((l1 instanceof sc_Pair)) {
+ {
+ (sc_conf_59 = (l1.car));
+ (conf_set = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_58zd2, sc_conf_59)));
+ if (((conf_set[(i+(5))])=== false))
+ {
+ (conf_set_adjoin(state, conf_set, sc_conf_59, i));
+ (l1 = (l1.cdr));
+ }
+ else
+ (l1 = (l1.cdr));
+ }
+ }
+ return undefined;
+ }
+ };
+ BgL_sc_confzd2setzd2adjoinza2za2_46z00 = function(sc_states_60, BgL_sc_statesza2_61za2, BgL_sc_statezd2num_62zd2, sc_conf_63, i) {
+ var BgL_sc_confzd2setza2_64z70;
+ var BgL_sc_stateza2_65za2;
+ var conf_set;
+ var state;
+ return ((state = (sc_states_60[BgL_sc_statezd2num_62zd2])), ((((conf_set = (state[(sc_conf_63+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)?((BgL_sc_stateza2_65za2 = (BgL_sc_statesza2_61za2[BgL_sc_statezd2num_62zd2])), (BgL_sc_confzd2setza2_64z70 = (BgL_sc_confzd2setzd2getza2_44za2(BgL_sc_stateza2_65za2, BgL_sc_statezd2num_62zd2, sc_conf_63))), (((BgL_sc_confzd2setza2_64z70[(i+(5))])=== false)?(conf_set_adjoin(BgL_sc_stateza2_65za2, BgL_sc_confzd2setza2_64z70, sc_conf_63, i)):undefined), true):false));
+ };
+ conf_set_union = function(state, conf_set, sc_conf_66, other_set) {
+ var i;
+ {
+ (i = (other_set[(2)]));
+ while ((i>=(0))) {
+ if (((conf_set[(i+(5))])=== false))
+ {
+ (conf_set_adjoin(state, conf_set, sc_conf_66, i));
+ (i = (other_set[(i+(5))]));
+ }
+ else
+ (i = (other_set[(i+(5))]));
+ }
+ return undefined;
+ }
+ };
+ forw = function(sc_states_67, BgL_sc_statezd2num_68zd2, sc_starters_69, sc_enders_70, sc_predictors_71, sc_steps_72, sc_nts_73) {
+ var next_set;
+ var next;
+ var conf_set;
+ var ender;
+ var l;
+ var starter_set;
+ var starter;
+ var sc_l_74;
+ var sc_loop1_75;
+ var head;
+ var BgL_sc_confzd2set_76zd2;
+ var BgL_sc_statezd2num_77zd2;
+ var state;
+ var sc_states_78;
+ var preds;
+ var BgL_sc_confzd2set_79zd2;
+ var step;
+ var sc_conf_80;
+ var BgL_sc_nbzd2nts_81zd2;
+ var sc_state_82;
+ {
+ (sc_state_82 = (sc_states_67[BgL_sc_statezd2num_68zd2]));
+ (BgL_sc_nbzd2nts_81zd2 = (sc_nts_73.length));
+ while (true) {
+ {
+ (sc_conf_80 = (sc_state_82[(0)]));
+ if ((sc_conf_80>=(0)))
+ {
+ (step = (sc_steps_72[sc_conf_80]));
+ (BgL_sc_confzd2set_79zd2 = (sc_state_82[(sc_conf_80+(1))]));
+ (head = (BgL_sc_confzd2set_79zd2[(4)]));
+ (sc_state_82[(0)] = (BgL_sc_confzd2set_79zd2[(0)]));
+ (conf_set_merge_new_bang(BgL_sc_confzd2set_79zd2));
+ if ((step>=(0)))
+ {
+ (sc_l_74 = (sc_starters_69[step]));
+ while ((sc_l_74 instanceof sc_Pair)) {
+ {
+ (starter = (sc_l_74.car));
+ (starter_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, starter)));
+ if (((starter_set[(BgL_sc_statezd2num_68zd2+(5))])=== false))
+ {
+ (conf_set_adjoin(sc_state_82, starter_set, starter, BgL_sc_statezd2num_68zd2));
+ (sc_l_74 = (sc_l_74.cdr));
+ }
+ else
+ (sc_l_74 = (sc_l_74.cdr));
+ }
+ }
+ (l = (sc_enders_70[step]));
+ while ((l instanceof sc_Pair)) {
+ {
+ (ender = (l.car));
+ if ((((conf_set = (sc_state_82[(ender+(1))])), ((conf_set!== false)?(conf_set[(BgL_sc_statezd2num_68zd2+(5))]):false))!== false))
+ {
+ (next = (sc_conf_80+(1)));
+ (next_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, next)));
+ (conf_set_union(sc_state_82, next_set, next, BgL_sc_confzd2set_79zd2));
+ (l = (l.cdr));
+ }
+ else
+ (l = (l.cdr));
+ }
+ }
+ }
+ else
+ {
+ (preds = (sc_predictors_71[(step+BgL_sc_nbzd2nts_81zd2)]));
+ (sc_states_78 = sc_states_67);
+ (state = sc_state_82);
+ (BgL_sc_statezd2num_77zd2 = BgL_sc_statezd2num_68zd2);
+ (BgL_sc_confzd2set_76zd2 = BgL_sc_confzd2set_79zd2);
+ sc_loop1_75 = function(l) {
+ var sc_state_83;
+ var BgL_sc_nextzd2set_84zd2;
+ var sc_next_85;
+ var pred_set;
+ var i;
+ var pred;
+ if ((l instanceof sc_Pair))
+ {
+ (pred = (l.car));
+ (i = head);
+ while ((i>=(0))) {
+ {
+ (pred_set = ((sc_state_83 = (sc_states_78[i])), (sc_state_83[(pred+(1))])));
+ if ((pred_set!== false))
+ {
+ (sc_next_85 = (pred+(1)));
+ (BgL_sc_nextzd2set_84zd2 = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_77zd2, sc_next_85)));
+ (conf_set_union(state, BgL_sc_nextzd2set_84zd2, sc_next_85, pred_set));
+ }
+ (i = (BgL_sc_confzd2set_76zd2[(i+(5))]));
+ }
+ }
+ return (sc_loop1_75((l.cdr)));
+ }
+ else
+ return undefined;
+ };
+ (sc_loop1_75(preds));
+ }
+ }
+ else
+ return undefined;
+ }
+ }
+ }
+ };
+ is_parsed = function(nt, i, j, sc_nts_86, sc_enders_87, sc_states_88) {
+ var conf_set;
+ var state;
+ var sc_conf_89;
+ var l;
+ var BgL_sc_ntza2_90za2;
+ {
+ (BgL_sc_ntza2_90za2 = (sc_ind_43(nt, sc_nts_86)));
+ if ((BgL_sc_ntza2_90za2!== false))
+ {
+ (sc_nts_86.length);
+ (l = (sc_enders_87[BgL_sc_ntza2_90za2]));
+ while (true) {
+ if ((l instanceof sc_Pair))
+ {
+ (sc_conf_89 = (l.car));
+ if ((((state = (sc_states_88[j])), (conf_set = (state[(sc_conf_89+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))
+ return true;
+ else
+ (l = (l.cdr));
+ }
+ else
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+ };
+ deriv_trees = function(sc_conf_91, i, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2) {
+ var sc_loop1_98;
+ var prev;
+ var name;
+ return ((name = (sc_names_94[sc_conf_91])), ((name!== false)?((sc_conf_91<BgL_sc_nbzd2nts_97zd2)?(sc_list((sc_list(name, ((sc_toks_95[i]).car))))):(sc_list((sc_list(name))))):((prev = (sc_conf_91-(1))), (sc_loop1_98 = function(l1, l2) {
+ var loop2;
+ var ender_set;
+ var state;
+ var ender;
+ var l1;
+ var l2;
+ while (true) {
+ if ((l1 instanceof sc_Pair))
+ {
+ (ender = (l1.car));
+ (ender_set = ((state = (sc_states_96[j])), (state[(ender+(1))])));
+ if ((ender_set!== false))
+ {
+ loop2 = function(k, l2) {
+ var loop3;
+ var ender_trees;
+ var prev_trees;
+ var conf_set;
+ var sc_state_99;
+ var k;
+ var l2;
+ while (true) {
+ if ((k>=(0)))
+ if (((k>=i)&&(((sc_state_99 = (sc_states_96[k])), (conf_set = (sc_state_99[(prev+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)))
+ {
+ (prev_trees = (deriv_trees(prev, i, k, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2)));
+ (ender_trees = (deriv_trees(ender, k, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2)));
+ loop3 = function(l3, l2) {
+ var l4;
+ var sc_l2_100;
+ var ender_tree;
+ if ((l3 instanceof sc_Pair))
+ {
+ (ender_tree = (sc_list((l3.car))));
+ (l4 = prev_trees);
+ (sc_l2_100 = l2);
+ while ((l4 instanceof sc_Pair)) {
+ {
+ (sc_l2_100 = (new sc_Pair((sc_append((l4.car), ender_tree)), sc_l2_100)));
+ (l4 = (l4.cdr));
+ }
+ }
+ return (loop3((l3.cdr), sc_l2_100));
+ }
+ else
+ return (loop2((ender_set[(k+(5))]), l2));
+ };
+ return (loop3(ender_trees, l2));
+ }
+ else
+ (k = (ender_set[(k+(5))]));
+ else
+ return (sc_loop1_98((l1.cdr), l2));
+ }
+ };
+ return (loop2((ender_set[(2)]), l2));
+ }
+ else
+ (l1 = (l1.cdr));
+ }
+ else
+ return l2;
+ }
+ }), (sc_loop1_98((sc_enders_92[(sc_steps_93[prev])]), null)))));
+ };
+ BgL_sc_derivzd2treesza2_47z70 = function(nt, i, j, sc_nts_101, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106) {
+ var conf_set;
+ var state;
+ var sc_conf_107;
+ var l;
+ var trees;
+ var BgL_sc_nbzd2nts_108zd2;
+ var BgL_sc_ntza2_109za2;
+ {
+ (BgL_sc_ntza2_109za2 = (sc_ind_43(nt, sc_nts_101)));
+ if ((BgL_sc_ntza2_109za2!== false))
+ {
+ (BgL_sc_nbzd2nts_108zd2 = (sc_nts_101.length));
+ (l = (sc_enders_102[BgL_sc_ntza2_109za2]));
+ (trees = null);
+ while ((l instanceof sc_Pair)) {
+ {
+ (sc_conf_107 = (l.car));
+ if ((((state = (sc_states_106[j])), (conf_set = (state[(sc_conf_107+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))
+ {
+ (l = (l.cdr));
+ (trees = (sc_append((deriv_trees(sc_conf_107, i, j, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106, BgL_sc_nbzd2nts_108zd2)), trees)));
+ }
+ else
+ (l = (l.cdr));
+ }
+ }
+ return trees;
+ }
+ else
+ return false;
+ }
+ };
+ nb_deriv_trees = function(sc_conf_110, i, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2) {
+ var sc_loop1_116;
+ var tmp1124;
+ var prev;
+ return ((prev = (sc_conf_110-(1))), ((((tmp1124 = (sc_conf_110<BgL_sc_nbzd2nts_115zd2)), ((tmp1124!== false)?tmp1124:((sc_steps_112[prev])<(0))))!== false)?(1):((sc_loop1_116 = function(l, sc_n_118) {
+ var nb_ender_trees;
+ var nb_prev_trees;
+ var conf_set;
+ var state;
+ var k;
+ var n;
+ var ender_set;
+ var sc_state_117;
+ var ender;
+ var l;
+ var sc_n_118;
+ while (true) {
+ if ((l instanceof sc_Pair))
+ {
+ (ender = (l.car));
+ (ender_set = ((sc_state_117 = (sc_states_114[j])), (sc_state_117[(ender+(1))])));
+ if ((ender_set!== false))
+ {
+ (k = (ender_set[(2)]));
+ (n = sc_n_118);
+ while ((k>=(0))) {
+ if (((k>=i)&&(((state = (sc_states_114[k])), (conf_set = (state[(prev+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false)))
+ {
+ (nb_prev_trees = (nb_deriv_trees(prev, i, k, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2)));
+ (nb_ender_trees = (nb_deriv_trees(ender, k, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2)));
+ (k = (ender_set[(k+(5))]));
+ (n +=(nb_prev_trees*nb_ender_trees));
+ }
+ else
+ (k = (ender_set[(k+(5))]));
+ }
+ return (sc_loop1_116((l.cdr), n));
+ }
+ else
+ (l = (l.cdr));
+ }
+ else
+ return sc_n_118;
+ }
+ }), (sc_loop1_116((sc_enders_111[(sc_steps_112[prev])]), (0))))));
+ };
+ BgL_sc_nbzd2derivzd2treesza2_48za2 = function(nt, i, j, sc_nts_119, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123) {
+ var conf_set;
+ var state;
+ var sc_conf_124;
+ var l;
+ var nb_trees;
+ var BgL_sc_nbzd2nts_125zd2;
+ var BgL_sc_ntza2_126za2;
+ {
+ (BgL_sc_ntza2_126za2 = (sc_ind_43(nt, sc_nts_119)));
+ if ((BgL_sc_ntza2_126za2!== false))
+ {
+ (BgL_sc_nbzd2nts_125zd2 = (sc_nts_119.length));
+ (l = (sc_enders_120[BgL_sc_ntza2_126za2]));
+ (nb_trees = (0));
+ while ((l instanceof sc_Pair)) {
+ {
+ (sc_conf_124 = (l.car));
+ if ((((state = (sc_states_123[j])), (conf_set = (state[(sc_conf_124+(1))])), ((conf_set!== false)?(conf_set[(i+(5))]):false))!== false))
+ {
+ (l = (l.cdr));
+ (nb_trees = ((nb_deriv_trees(sc_conf_124, i, j, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123, BgL_sc_nbzd2nts_125zd2))+nb_trees));
+ }
+ else
+ (l = (l.cdr));
+ }
+ }
+ return nb_trees;
+ }
+ else
+ return false;
+ }
+ };
+ (lexer = (parser_descr[(0)]));
+ (sc_nts_42 = (parser_descr[(1)]));
+ (sc_starters_41 = (parser_descr[(2)]));
+ (sc_enders_40 = (parser_descr[(3)]));
+ (sc_predictors_39 = (parser_descr[(4)]));
+ (sc_steps_38 = (parser_descr[(5)]));
+ (sc_names_37 = (parser_descr[(6)]));
+ (falseHead1128 = (new sc_Pair(null, null)));
+ (L1125 = (lexer(input)));
+ (tail1129 = falseHead1128);
+ while (!(L1125 === null)) {
+ {
+ (tok = (L1125.car));
+ (l1 = (tok.cdr));
+ (l2 = null);
+ while ((l1 instanceof sc_Pair)) {
+ {
+ (sc_i_29 = (sc_ind_43((l1.car), sc_nts_42)));
+ if ((sc_i_29!== false))
+ {
+ (l1 = (l1.cdr));
+ (l2 = (new sc_Pair(sc_i_29, l2)));
+ }
+ else
+ (l1 = (l1.cdr));
+ }
+ }
+ (sc_optrOpnd_22 = (new sc_Pair((tok.car), (sc_reverse(l2)))));
+ (sc_optrOpnd_21 = (new sc_Pair(sc_optrOpnd_22, null)));
+ (tail1129.cdr = sc_optrOpnd_21);
+ (tail1129 = (tail1129.cdr));
+ (L1125 = (L1125.cdr));
+ }
+ }
+ (sc_optrOpnd_20 = (falseHead1128.cdr));
+ (sc_toks_36 = (sc_list2vector(sc_optrOpnd_20)));
+ (BgL_sc_nbzd2toks_35zd2 = (sc_toks_36.length));
+ (BgL_sc_nbzd2confs_34zd2 = (sc_steps_38.length));
+ (sc_states_33 = (make_states(BgL_sc_nbzd2toks_35zd2, BgL_sc_nbzd2confs_34zd2)));
+ (goal_starters = (sc_starters_41[(0)]));
+ (BgL_sc_confzd2setzd2adjoinza2_45za2(sc_states_33, (0), goal_starters, (0)));
+ (forw(sc_states_33, (0), sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_nts_42));
+ (sc_i_28 = (0));
+ while ((sc_i_28<BgL_sc_nbzd2toks_35zd2)) {
+ {
+ (tok_nts = ((sc_toks_36[sc_i_28]).cdr));
+ (BgL_sc_confzd2setzd2adjoinza2_45za2(sc_states_33, (sc_i_28+(1)), tok_nts, sc_i_28));
+ (forw(sc_states_33, (sc_i_28+(1)), sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_nts_42));
+ (++sc_i_28);
+ }
+ }
+ (nb_toks = (sc_toks_36.length));
+ (BgL_sc_nbzd2confs_32zd2 = (sc_steps_38.length));
+ (BgL_sc_nbzd2nts_31zd2 = (sc_nts_42.length));
+ (BgL_sc_statesza2_30za2 = (make_states(nb_toks, BgL_sc_nbzd2confs_32zd2)));
+ (goal_enders = (sc_enders_40[(0)]));
+ (l = goal_enders);
+ while ((l instanceof sc_Pair)) {
+ {
+ (conf = (l.car));
+ (BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_33, BgL_sc_statesza2_30za2, nb_toks, conf, (0)));
+ (l = (l.cdr));
+ }
+ }
+ (i = nb_toks);
+ while ((i>=(0))) {
+ {
+ (states = sc_states_33);
+ (BgL_sc_statesza2_27za2 = BgL_sc_statesza2_30za2);
+ (state_num = i);
+ (sc_enders_26 = sc_enders_40);
+ (sc_steps_25 = sc_steps_38);
+ (BgL_sc_nbzd2nts_24zd2 = BgL_sc_nbzd2nts_31zd2);
+ (toks = sc_toks_36);
+ (BgL_sc_stateza2_23za2 = (BgL_sc_statesza2_30za2[i]));
+ loop1 = function() {
+ var sc_loop1_127;
+ var prev;
+ var BgL_sc_statesza2_128za2;
+ var sc_states_129;
+ var j;
+ var i;
+ var sc_i_130;
+ var head;
+ var conf_set;
+ var sc_conf_131;
+ {
+ (sc_conf_131 = (BgL_sc_stateza2_23za2[(0)]));
+ if ((sc_conf_131>=(0)))
+ {
+ (conf_set = (BgL_sc_stateza2_23za2[(sc_conf_131+(1))]));
+ (head = (conf_set[(4)]));
+ (BgL_sc_stateza2_23za2[(0)] = (conf_set[(0)]));
+ (conf_set_merge_new_bang(conf_set));
+ (sc_i_130 = head);
+ while ((sc_i_130>=(0))) {
+ {
+ (i = sc_i_130);
+ (j = state_num);
+ (sc_states_129 = states);
+ (BgL_sc_statesza2_128za2 = BgL_sc_statesza2_27za2);
+ (prev = (sc_conf_131-(1)));
+ if (((sc_conf_131>=BgL_sc_nbzd2nts_24zd2)&&((sc_steps_25[prev])>=(0))))
+ {
+ sc_loop1_127 = function(l) {
+ var k;
+ var ender_set;
+ var state;
+ var ender;
+ var l;
+ while (true) {
+ if ((l instanceof sc_Pair))
+ {
+ (ender = (l.car));
+ (ender_set = ((state = (sc_states_129[j])), (state[(ender+(1))])));
+ if ((ender_set!== false))
+ {
+ (k = (ender_set[(2)]));
+ while ((k>=(0))) {
+ {
+ if ((k>=i))
+ if (((BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, k, prev, i))!== false))
+ (BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, j, ender, k));
+ (k = (ender_set[(k+(5))]));
+ }
+ }
+ return (sc_loop1_127((l.cdr)));
+ }
+ else
+ (l = (l.cdr));
+ }
+ else
+ return undefined;
+ }
+ };
+ (sc_loop1_127((sc_enders_26[(sc_steps_25[prev])])));
+ }
+ (sc_i_130 = (conf_set[(sc_i_130+(5))]));
+ }
+ }
+ return (loop1());
+ }
+ else
+ return undefined;
+ }
+ };
+ (loop1());
+ (--i);
+ }
+ }
+ (optrOpnd = BgL_sc_statesza2_30za2);
+ return [sc_nts_42, sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_names_37, sc_toks_36, optrOpnd, is_parsed, BgL_sc_derivzd2treesza2_47z70, BgL_sc_nbzd2derivzd2treesza2_48za2];
+ }
+ };
+ }
+ };
+ BgL_parsezd2ze3parsedzf3zc2 = function(parse, nt, i, j) {
+ var is_parsed;
+ var states;
+ var enders;
+ var nts;
+ return ((nts = (parse[(0)])), (enders = (parse[(2)])), (states = (parse[(7)])), (is_parsed = (parse[(8)])), (is_parsed(nt, i, j, nts, enders, states)));
+ };
+ BgL_parsezd2ze3treesz31 = function(parse, nt, i, j) {
+ var BgL_sc_derivzd2treesza2_132z70;
+ var states;
+ var toks;
+ var names;
+ var steps;
+ var enders;
+ var nts;
+ return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (names = (parse[(5)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_derivzd2treesza2_132z70 = (parse[(9)])), (BgL_sc_derivzd2treesza2_132z70(nt, i, j, nts, enders, steps, names, toks, states)));
+ };
+ BgL_parsezd2ze3nbzd2treesze3 = function(parse, nt, i, j) {
+ var BgL_sc_nbzd2derivzd2treesza2_133za2;
+ var states;
+ var toks;
+ var steps;
+ var enders;
+ var nts;
+ return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_nbzd2derivzd2treesza2_133za2 = (parse[(10)])), (BgL_sc_nbzd2derivzd2treesza2_133za2(nt, i, j, nts, enders, steps, toks, states)));
+ };
+ test = function(k) {
+ var x;
+ var p;
+ return ((p = (BgL_makezd2parserzd2(const_earley, function(l) {
+ var sc_x_134;
+ var tail1134;
+ var L1130;
+ var falseHead1133;
+ {
+ (falseHead1133 = (new sc_Pair(null, null)));
+ (tail1134 = falseHead1133);
+ (L1130 = l);
+ while (!(L1130 === null)) {
+ {
+ (tail1134.cdr = (new sc_Pair(((sc_x_134 = (L1130.car)), (sc_list(sc_x_134, sc_x_134))), null)));
+ (tail1134 = (tail1134.cdr));
+ (L1130 = (L1130.cdr));
+ }
+ }
+ return (falseHead1133.cdr);
+ }
+ }))), (x = (p((sc_vector2list((sc_makeVector(k, "\u1E9Ca"))))))), (sc_length((BgL_parsezd2ze3treesz31(x, "\u1E9Cs", (0), k)))));
+ };
+ BgL_earleyzd2benchmarkzd2 = function() {
+ var args = null;
+ for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) {
+ args = sc_cons(arguments[sc_tmp], args);
+ }
+ var k;
+ return ((k = ((args === null)?(7):(args.car))), (BgL_runzd2benchmarkzd2("earley", (1), function() {
+ return (test(k));
+ }, function(result) {
+ return ((sc_display(result)), (sc_newline()), result == 132);
+ })));
+ };
+}
+
+
+/************* END OF GENERATED CODE *************/
+// Invoke this function to run a benchmark.
+// The first argument is a string identifying the benchmark.
+// The second argument is the number of times to run the benchmark.
+// The third argument is a function that runs the benchmark.
+// The fourth argument is a unary function that warns if the result
+// returned by the benchmark is incorrect.
+//
+// Example:
+// RunBenchmark("new Array()",
+// 1,
+// function () { new Array(1000000); }
+// function (v) {
+// return (v instanceof Array) && (v.length == 1000000);
+// });
+
+SC_DEFAULT_OUT = new sc_GenericOutputPort(function(s) {});
+SC_ERROR_OUT = SC_DEFAULT_OUT;
+
+function RunBenchmark(name, count, run, warn) {
+ for (var n = 0; n < count; ++n) {
+ result = run();
+ if (!warn(result)) {
+ throw new Error("Earley or Boyer did incorrect number of rewrites");
+ }
+ }
+}
+
+var BgL_runzd2benchmarkzd2 = RunBenchmark;
diff --git a/chromium/v8/benchmarks/navier-stokes.js b/chromium/v8/benchmarks/navier-stokes.js
new file mode 100644
index 00000000000..b0dc3c86457
--- /dev/null
+++ b/chromium/v8/benchmarks/navier-stokes.js
@@ -0,0 +1,387 @@
+/**
+ * Copyright 2012 the V8 project authors. All rights reserved.
+ * Copyright 2009 Oliver Hunt <http://nerget.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION 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 NavierStokes = new BenchmarkSuite('NavierStokes', 1484000,
+ [new Benchmark('NavierStokes',
+ runNavierStokes,
+ setupNavierStokes,
+ tearDownNavierStokes)]);
+
+var solver = null;
+
+function runNavierStokes()
+{
+ solver.update();
+}
+
+function setupNavierStokes()
+{
+ solver = new FluidField(null);
+ solver.setResolution(128, 128);
+ solver.setIterations(20);
+ solver.setDisplayFunction(function(){});
+ solver.setUICallback(prepareFrame);
+ solver.reset();
+}
+
+function tearDownNavierStokes()
+{
+ solver = null;
+}
+
+function addPoints(field) {
+ var n = 64;
+ for (var i = 1; i <= n; i++) {
+ field.setVelocity(i, i, n, n);
+ field.setDensity(i, i, 5);
+ field.setVelocity(i, n - i, -n, -n);
+ field.setDensity(i, n - i, 20);
+ field.setVelocity(128 - i, n + i, -n, -n);
+ field.setDensity(128 - i, n + i, 30);
+ }
+}
+
+var framesTillAddingPoints = 0;
+var framesBetweenAddingPoints = 5;
+
+function prepareFrame(field)
+{
+ if (framesTillAddingPoints == 0) {
+ addPoints(field);
+ framesTillAddingPoints = framesBetweenAddingPoints;
+ framesBetweenAddingPoints++;
+ } else {
+ framesTillAddingPoints--;
+ }
+}
+
+// Code from Oliver Hunt (http://nerget.com/fluidSim/pressure.js) starts here.
+function FluidField(canvas) {
+ function addFields(x, s, dt)
+ {
+ for (var i=0; i<size ; i++ ) x[i] += dt*s[i];
+ }
+
+ function set_bnd(b, x)
+ {
+ if (b===1) {
+ for (var i = 1; i <= width; i++) {
+ x[i] = x[i + rowSize];
+ x[i + (height+1) *rowSize] = x[i + height * rowSize];
+ }
+
+ for (var j = 1; i <= height; i++) {
+ x[j * rowSize] = -x[1 + j * rowSize];
+ x[(width + 1) + j * rowSize] = -x[width + j * rowSize];
+ }
+ } else if (b === 2) {
+ for (var i = 1; i <= width; i++) {
+ x[i] = -x[i + rowSize];
+ x[i + (height + 1) * rowSize] = -x[i + height * rowSize];
+ }
+
+ for (var j = 1; j <= height; j++) {
+ x[j * rowSize] = x[1 + j * rowSize];
+ x[(width + 1) + j * rowSize] = x[width + j * rowSize];
+ }
+ } else {
+ for (var i = 1; i <= width; i++) {
+ x[i] = x[i + rowSize];
+ x[i + (height + 1) * rowSize] = x[i + height * rowSize];
+ }
+
+ for (var j = 1; j <= height; j++) {
+ x[j * rowSize] = x[1 + j * rowSize];
+ x[(width + 1) + j * rowSize] = x[width + j * rowSize];
+ }
+ }
+ var maxEdge = (height + 1) * rowSize;
+ x[0] = 0.5 * (x[1] + x[rowSize]);
+ x[maxEdge] = 0.5 * (x[1 + maxEdge] + x[height * rowSize]);
+ x[(width+1)] = 0.5 * (x[width] + x[(width + 1) + rowSize]);
+ x[(width+1)+maxEdge] = 0.5 * (x[width + maxEdge] + x[(width + 1) + height * rowSize]);
+ }
+
+ function lin_solve(b, x, x0, a, c)
+ {
+ if (a === 0 && c === 1) {
+ for (var j=1 ; j<=height; j++) {
+ var currentRow = j * rowSize;
+ ++currentRow;
+ for (var i = 0; i < width; i++) {
+ x[currentRow] = x0[currentRow];
+ ++currentRow;
+ }
+ }
+ set_bnd(b, x);
+ } else {
+ var invC = 1 / c;
+ for (var k=0 ; k<iterations; k++) {
+ for (var j=1 ; j<=height; j++) {
+ var lastRow = (j - 1) * rowSize;
+ var currentRow = j * rowSize;
+ var nextRow = (j + 1) * rowSize;
+ var lastX = x[currentRow];
+ ++currentRow;
+ for (var i=1; i<=width; i++)
+ lastX = x[currentRow] = (x0[currentRow] + a*(lastX+x[++currentRow]+x[++lastRow]+x[++nextRow])) * invC;
+ }
+ set_bnd(b, x);
+ }
+ }
+ }
+
+ function diffuse(b, x, x0, dt)
+ {
+ var a = 0;
+ lin_solve(b, x, x0, a, 1 + 4*a);
+ }
+
+ function lin_solve2(x, x0, y, y0, a, c)
+ {
+ if (a === 0 && c === 1) {
+ for (var j=1 ; j <= height; j++) {
+ var currentRow = j * rowSize;
+ ++currentRow;
+ for (var i = 0; i < width; i++) {
+ x[currentRow] = x0[currentRow];
+ y[currentRow] = y0[currentRow];
+ ++currentRow;
+ }
+ }
+ set_bnd(1, x);
+ set_bnd(2, y);
+ } else {
+ var invC = 1/c;
+ for (var k=0 ; k<iterations; k++) {
+ for (var j=1 ; j <= height; j++) {
+ var lastRow = (j - 1) * rowSize;
+ var currentRow = j * rowSize;
+ var nextRow = (j + 1) * rowSize;
+ var lastX = x[currentRow];
+ var lastY = y[currentRow];
+ ++currentRow;
+ for (var i = 1; i <= width; i++) {
+ lastX = x[currentRow] = (x0[currentRow] + a * (lastX + x[currentRow] + x[lastRow] + x[nextRow])) * invC;
+ lastY = y[currentRow] = (y0[currentRow] + a * (lastY + y[++currentRow] + y[++lastRow] + y[++nextRow])) * invC;
+ }
+ }
+ set_bnd(1, x);
+ set_bnd(2, y);
+ }
+ }
+ }
+
+ function diffuse2(x, x0, y, y0, dt)
+ {
+ var a = 0;
+ lin_solve2(x, x0, y, y0, a, 1 + 4 * a);
+ }
+
+ function advect(b, d, d0, u, v, dt)
+ {
+ var Wdt0 = dt * width;
+ var Hdt0 = dt * height;
+ var Wp5 = width + 0.5;
+ var Hp5 = height + 0.5;
+ for (var j = 1; j<= height; j++) {
+ var pos = j * rowSize;
+ for (var i = 1; i <= width; i++) {
+ var x = i - Wdt0 * u[++pos];
+ var y = j - Hdt0 * v[pos];
+ if (x < 0.5)
+ x = 0.5;
+ else if (x > Wp5)
+ x = Wp5;
+ var i0 = x | 0;
+ var i1 = i0 + 1;
+ if (y < 0.5)
+ y = 0.5;
+ else if (y > Hp5)
+ y = Hp5;
+ var j0 = y | 0;
+ var j1 = j0 + 1;
+ var s1 = x - i0;
+ var s0 = 1 - s1;
+ var t1 = y - j0;
+ var t0 = 1 - t1;
+ var row1 = j0 * rowSize;
+ var row2 = j1 * rowSize;
+ d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) + s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]);
+ }
+ }
+ set_bnd(b, d);
+ }
+
+ function project(u, v, p, div)
+ {
+ var h = -0.5 / Math.sqrt(width * height);
+ for (var j = 1 ; j <= height; j++ ) {
+ var row = j * rowSize;
+ var previousRow = (j - 1) * rowSize;
+ var prevValue = row - 1;
+ var currentRow = row;
+ var nextValue = row + 1;
+ var nextRow = (j + 1) * rowSize;
+ for (var i = 1; i <= width; i++ ) {
+ div[++currentRow] = h * (u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]);
+ p[currentRow] = 0;
+ }
+ }
+ set_bnd(0, div);
+ set_bnd(0, p);
+
+ lin_solve(0, p, div, 1, 4 );
+ var wScale = 0.5 * width;
+ var hScale = 0.5 * height;
+ for (var j = 1; j<= height; j++ ) {
+ var prevPos = j * rowSize - 1;
+ var currentPos = j * rowSize;
+ var nextPos = j * rowSize + 1;
+ var prevRow = (j - 1) * rowSize;
+ var currentRow = j * rowSize;
+ var nextRow = (j + 1) * rowSize;
+
+ for (var i = 1; i<= width; i++) {
+ u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]);
+ v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]);
+ }
+ }
+ set_bnd(1, u);
+ set_bnd(2, v);
+ }
+
+ function dens_step(x, x0, u, v, dt)
+ {
+ addFields(x, x0, dt);
+ diffuse(0, x0, x, dt );
+ advect(0, x, x0, u, v, dt );
+ }
+
+ function vel_step(u, v, u0, v0, dt)
+ {
+ addFields(u, u0, dt );
+ addFields(v, v0, dt );
+ var temp = u0; u0 = u; u = temp;
+ var temp = v0; v0 = v; v = temp;
+ diffuse2(u,u0,v,v0, dt);
+ project(u, v, u0, v0);
+ var temp = u0; u0 = u; u = temp;
+ var temp = v0; v0 = v; v = temp;
+ advect(1, u, u0, u0, v0, dt);
+ advect(2, v, v0, u0, v0, dt);
+ project(u, v, u0, v0 );
+ }
+ var uiCallback = function(d,u,v) {};
+
+ function Field(dens, u, v) {
+ // Just exposing the fields here rather than using accessors is a measurable win during display (maybe 5%)
+ // but makes the code ugly.
+ this.setDensity = function(x, y, d) {
+ dens[(x + 1) + (y + 1) * rowSize] = d;
+ }
+ this.getDensity = function(x, y) {
+ return dens[(x + 1) + (y + 1) * rowSize];
+ }
+ this.setVelocity = function(x, y, xv, yv) {
+ u[(x + 1) + (y + 1) * rowSize] = xv;
+ v[(x + 1) + (y + 1) * rowSize] = yv;
+ }
+ this.getXVelocity = function(x, y) {
+ return u[(x + 1) + (y + 1) * rowSize];
+ }
+ this.getYVelocity = function(x, y) {
+ return v[(x + 1) + (y + 1) * rowSize];
+ }
+ this.width = function() { return width; }
+ this.height = function() { return height; }
+ }
+ function queryUI(d, u, v)
+ {
+ for (var i = 0; i < size; i++)
+ u[i] = v[i] = d[i] = 0.0;
+ uiCallback(new Field(d, u, v));
+ }
+
+ this.update = function () {
+ queryUI(dens_prev, u_prev, v_prev);
+ vel_step(u, v, u_prev, v_prev, dt);
+ dens_step(dens, dens_prev, u, v, dt);
+ displayFunc(new Field(dens, u, v));
+ }
+ this.setDisplayFunction = function(func) {
+ displayFunc = func;
+ }
+
+ this.iterations = function() { return iterations; }
+ this.setIterations = function(iters) {
+ if (iters > 0 && iters <= 100)
+ iterations = iters;
+ }
+ this.setUICallback = function(callback) {
+ uiCallback = callback;
+ }
+ var iterations = 10;
+ var visc = 0.5;
+ var dt = 0.1;
+ var dens;
+ var dens_prev;
+ var u;
+ var u_prev;
+ var v;
+ var v_prev;
+ var width;
+ var height;
+ var rowSize;
+ var size;
+ var displayFunc;
+ function reset()
+ {
+ rowSize = width + 2;
+ size = (width+2)*(height+2);
+ dens = new Array(size);
+ dens_prev = new Array(size);
+ u = new Array(size);
+ u_prev = new Array(size);
+ v = new Array(size);
+ v_prev = new Array(size);
+ for (var i = 0; i < size; i++)
+ dens_prev[i] = u_prev[i] = v_prev[i] = dens[i] = u[i] = v[i] = 0;
+ }
+ this.reset = reset;
+ this.setResolution = function (hRes, wRes)
+ {
+ var res = wRes * hRes;
+ if (res > 0 && res < 1000000 && (wRes != width || hRes != height)) {
+ width = wRes;
+ height = hRes;
+ reset();
+ return true;
+ }
+ return false;
+ }
+ this.setResolution(64, 64);
+}
diff --git a/chromium/v8/benchmarks/raytrace.js b/chromium/v8/benchmarks/raytrace.js
new file mode 100644
index 00000000000..971ef7218ee
--- /dev/null
+++ b/chromium/v8/benchmarks/raytrace.js
@@ -0,0 +1,904 @@
+// The ray tracer code in this file is written by Adam Burmister. It
+// is available in its original form from:
+//
+// http://labs.flog.nz.co/raytracer/
+//
+// It has been modified slightly by Google to work as a standalone
+// benchmark, but the all the computational code remains
+// untouched. This file also contains a copy of parts of the Prototype
+// JavaScript framework which is used by the ray tracer.
+
+var RayTrace = new BenchmarkSuite('RayTrace', 739989, [
+ new Benchmark('RayTrace', renderScene)
+]);
+
+
+// Variable used to hold a number that can be used to verify that
+// the scene was ray traced correctly.
+var checkNumber;
+
+
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+
+// The following is a copy of parts of the Prototype JavaScript library:
+
+// Prototype JavaScript framework, version 1.5.0
+// (c) 2005-2007 Sam Stephenson
+//
+// Prototype is freely distributable under the terms of an MIT-style license.
+// For details, see the Prototype web site: http://prototype.conio.net/
+
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+};
+
+
+Object.extend = function(destination, source) {
+ for (var property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+};
+
+
+// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
+
+// The rest of this file is the actual ray tracer written by Adam
+// Burmister. It's a concatenation of the following files:
+//
+// flog/color.js
+// flog/light.js
+// flog/vector.js
+// flog/ray.js
+// flog/scene.js
+// flog/material/basematerial.js
+// flog/material/solid.js
+// flog/material/chessboard.js
+// flog/shape/baseshape.js
+// flog/shape/sphere.js
+// flog/shape/plane.js
+// flog/intersectioninfo.js
+// flog/camera.js
+// flog/background.js
+// flog/engine.js
+
+
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Color = Class.create();
+
+Flog.RayTracer.Color.prototype = {
+ red : 0.0,
+ green : 0.0,
+ blue : 0.0,
+
+ initialize : function(r, g, b) {
+ if(!r) r = 0.0;
+ if(!g) g = 0.0;
+ if(!b) b = 0.0;
+
+ this.red = r;
+ this.green = g;
+ this.blue = b;
+ },
+
+ add : function(c1, c2){
+ var result = new Flog.RayTracer.Color(0,0,0);
+
+ result.red = c1.red + c2.red;
+ result.green = c1.green + c2.green;
+ result.blue = c1.blue + c2.blue;
+
+ return result;
+ },
+
+ addScalar: function(c1, s){
+ var result = new Flog.RayTracer.Color(0,0,0);
+
+ result.red = c1.red + s;
+ result.green = c1.green + s;
+ result.blue = c1.blue + s;
+
+ result.limit();
+
+ return result;
+ },
+
+ subtract: function(c1, c2){
+ var result = new Flog.RayTracer.Color(0,0,0);
+
+ result.red = c1.red - c2.red;
+ result.green = c1.green - c2.green;
+ result.blue = c1.blue - c2.blue;
+
+ return result;
+ },
+
+ multiply : function(c1, c2) {
+ var result = new Flog.RayTracer.Color(0,0,0);
+
+ result.red = c1.red * c2.red;
+ result.green = c1.green * c2.green;
+ result.blue = c1.blue * c2.blue;
+
+ return result;
+ },
+
+ multiplyScalar : function(c1, f) {
+ var result = new Flog.RayTracer.Color(0,0,0);
+
+ result.red = c1.red * f;
+ result.green = c1.green * f;
+ result.blue = c1.blue * f;
+
+ return result;
+ },
+
+ divideFactor : function(c1, f) {
+ var result = new Flog.RayTracer.Color(0,0,0);
+
+ result.red = c1.red / f;
+ result.green = c1.green / f;
+ result.blue = c1.blue / f;
+
+ return result;
+ },
+
+ limit: function(){
+ this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
+ this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
+ this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
+ },
+
+ distance : function(color) {
+ var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
+ return d;
+ },
+
+ blend: function(c1, c2, w){
+ var result = new Flog.RayTracer.Color(0,0,0);
+ result = Flog.RayTracer.Color.prototype.add(
+ Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
+ Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
+ );
+ return result;
+ },
+
+ brightness : function() {
+ var r = Math.floor(this.red*255);
+ var g = Math.floor(this.green*255);
+ var b = Math.floor(this.blue*255);
+ return (r * 77 + g * 150 + b * 29) >> 8;
+ },
+
+ toString : function () {
+ var r = Math.floor(this.red*255);
+ var g = Math.floor(this.green*255);
+ var b = Math.floor(this.blue*255);
+
+ return "rgb("+ r +","+ g +","+ b +")";
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Light = Class.create();
+
+Flog.RayTracer.Light.prototype = {
+ position: null,
+ color: null,
+ intensity: 10.0,
+
+ initialize : function(pos, color, intensity) {
+ this.position = pos;
+ this.color = color;
+ this.intensity = (intensity ? intensity : 10.0);
+ },
+
+ toString : function () {
+ return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Vector = Class.create();
+
+Flog.RayTracer.Vector.prototype = {
+ x : 0.0,
+ y : 0.0,
+ z : 0.0,
+
+ initialize : function(x, y, z) {
+ this.x = (x ? x : 0);
+ this.y = (y ? y : 0);
+ this.z = (z ? z : 0);
+ },
+
+ copy: function(vector){
+ this.x = vector.x;
+ this.y = vector.y;
+ this.z = vector.z;
+ },
+
+ normalize : function() {
+ var m = this.magnitude();
+ return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
+ },
+
+ magnitude : function() {
+ return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
+ },
+
+ cross : function(w) {
+ return new Flog.RayTracer.Vector(
+ -this.z * w.y + this.y * w.z,
+ this.z * w.x - this.x * w.z,
+ -this.y * w.x + this.x * w.y);
+ },
+
+ dot : function(w) {
+ return this.x * w.x + this.y * w.y + this.z * w.z;
+ },
+
+ add : function(v, w) {
+ return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
+ },
+
+ subtract : function(v, w) {
+ if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
+ return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
+ },
+
+ multiplyVector : function(v, w) {
+ return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
+ },
+
+ multiplyScalar : function(v, w) {
+ return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
+ },
+
+ toString : function () {
+ return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Ray = Class.create();
+
+Flog.RayTracer.Ray.prototype = {
+ position : null,
+ direction : null,
+ initialize : function(pos, dir) {
+ this.position = pos;
+ this.direction = dir;
+ },
+
+ toString : function () {
+ return 'Ray [' + this.position + ',' + this.direction + ']';
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Scene = Class.create();
+
+Flog.RayTracer.Scene.prototype = {
+ camera : null,
+ shapes : [],
+ lights : [],
+ background : null,
+
+ initialize : function() {
+ this.camera = new Flog.RayTracer.Camera(
+ new Flog.RayTracer.Vector(0,0,-5),
+ new Flog.RayTracer.Vector(0,0,1),
+ new Flog.RayTracer.Vector(0,1,0)
+ );
+ this.shapes = new Array();
+ this.lights = new Array();
+ this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
+
+Flog.RayTracer.Material.BaseMaterial = Class.create();
+
+Flog.RayTracer.Material.BaseMaterial.prototype = {
+
+ gloss: 2.0, // [0...infinity] 0 = matt
+ transparency: 0.0, // 0=opaque
+ reflection: 0.0, // [0...infinity] 0 = no reflection
+ refraction: 0.50,
+ hasTexture: false,
+
+ initialize : function() {
+
+ },
+
+ getColor: function(u, v){
+
+ },
+
+ wrapUp: function(t){
+ t = t % 2.0;
+ if(t < -1) t += 2.0;
+ if(t >= 1) t -= 2.0;
+ return t;
+ },
+
+ toString : function () {
+ return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Material.Solid = Class.create();
+
+Flog.RayTracer.Material.Solid.prototype = Object.extend(
+ new Flog.RayTracer.Material.BaseMaterial(), {
+ initialize : function(color, reflection, refraction, transparency, gloss) {
+ this.color = color;
+ this.reflection = reflection;
+ this.transparency = transparency;
+ this.gloss = gloss;
+ this.hasTexture = false;
+ },
+
+ getColor: function(u, v){
+ return this.color;
+ },
+
+ toString : function () {
+ return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+ }
+ }
+);
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Material.Chessboard = Class.create();
+
+Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
+ new Flog.RayTracer.Material.BaseMaterial(), {
+ colorEven: null,
+ colorOdd: null,
+ density: 0.5,
+
+ initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
+ this.colorEven = colorEven;
+ this.colorOdd = colorOdd;
+ this.reflection = reflection;
+ this.transparency = transparency;
+ this.gloss = gloss;
+ this.density = density;
+ this.hasTexture = true;
+ },
+
+ getColor: function(u, v){
+ var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
+
+ if(t < 0.0)
+ return this.colorEven;
+ else
+ return this.colorOdd;
+ },
+
+ toString : function () {
+ return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
+ }
+ }
+);
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+
+Flog.RayTracer.Shape.Sphere = Class.create();
+
+Flog.RayTracer.Shape.Sphere.prototype = {
+ initialize : function(pos, radius, material) {
+ this.radius = radius;
+ this.position = pos;
+ this.material = material;
+ },
+
+ intersect: function(ray){
+ var info = new Flog.RayTracer.IntersectionInfo();
+ info.shape = this;
+
+ var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
+
+ var B = dst.dot(ray.direction);
+ var C = dst.dot(dst) - (this.radius * this.radius);
+ var D = (B * B) - C;
+
+ if(D > 0){ // intersection!
+ info.isHit = true;
+ info.distance = (-B) - Math.sqrt(D);
+ info.position = Flog.RayTracer.Vector.prototype.add(
+ ray.position,
+ Flog.RayTracer.Vector.prototype.multiplyScalar(
+ ray.direction,
+ info.distance
+ )
+ );
+ info.normal = Flog.RayTracer.Vector.prototype.subtract(
+ info.position,
+ this.position
+ ).normalize();
+
+ info.color = this.material.getColor(0,0);
+ } else {
+ info.isHit = false;
+ }
+ return info;
+ },
+
+ toString : function () {
+ return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
+
+Flog.RayTracer.Shape.Plane = Class.create();
+
+Flog.RayTracer.Shape.Plane.prototype = {
+ d: 0.0,
+
+ initialize : function(pos, d, material) {
+ this.position = pos;
+ this.d = d;
+ this.material = material;
+ },
+
+ intersect: function(ray){
+ var info = new Flog.RayTracer.IntersectionInfo();
+
+ var Vd = this.position.dot(ray.direction);
+ if(Vd == 0) return info; // no intersection
+
+ var t = -(this.position.dot(ray.position) + this.d) / Vd;
+ if(t <= 0) return info;
+
+ info.shape = this;
+ info.isHit = true;
+ info.position = Flog.RayTracer.Vector.prototype.add(
+ ray.position,
+ Flog.RayTracer.Vector.prototype.multiplyScalar(
+ ray.direction,
+ t
+ )
+ );
+ info.normal = this.position;
+ info.distance = t;
+
+ if(this.material.hasTexture){
+ var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
+ var vV = vU.cross(this.position);
+ var u = info.position.dot(vU);
+ var v = info.position.dot(vV);
+ info.color = this.material.getColor(u,v);
+ } else {
+ info.color = this.material.getColor(0,0);
+ }
+
+ return info;
+ },
+
+ toString : function () {
+ return 'Plane [' + this.position + ', d=' + this.d + ']';
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.IntersectionInfo = Class.create();
+
+Flog.RayTracer.IntersectionInfo.prototype = {
+ isHit: false,
+ hitCount: 0,
+ shape: null,
+ position: null,
+ normal: null,
+ color: null,
+ distance: null,
+
+ initialize : function() {
+ this.color = new Flog.RayTracer.Color(0,0,0);
+ },
+
+ toString : function () {
+ return 'Intersection [' + this.position + ']';
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Camera = Class.create();
+
+Flog.RayTracer.Camera.prototype = {
+ position: null,
+ lookAt: null,
+ equator: null,
+ up: null,
+ screen: null,
+
+ initialize : function(pos, lookAt, up) {
+ this.position = pos;
+ this.lookAt = lookAt;
+ this.up = up;
+ this.equator = lookAt.normalize().cross(this.up);
+ this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
+ },
+
+ getRay: function(vx, vy){
+ var pos = Flog.RayTracer.Vector.prototype.subtract(
+ this.screen,
+ Flog.RayTracer.Vector.prototype.subtract(
+ Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
+ Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
+ )
+ );
+ pos.y = pos.y * -1;
+ var dir = Flog.RayTracer.Vector.prototype.subtract(
+ pos,
+ this.position
+ );
+
+ var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
+
+ return ray;
+ },
+
+ toString : function () {
+ return 'Ray []';
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Background = Class.create();
+
+Flog.RayTracer.Background.prototype = {
+ color : null,
+ ambience : 0.0,
+
+ initialize : function(color, ambience) {
+ this.color = color;
+ this.ambience = ambience;
+ }
+}
+/* Fake a Flog.* namespace */
+if(typeof(Flog) == 'undefined') var Flog = {};
+if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
+
+Flog.RayTracer.Engine = Class.create();
+
+Flog.RayTracer.Engine.prototype = {
+ canvas: null, /* 2d context we can render to */
+
+ initialize: function(options){
+ this.options = Object.extend({
+ canvasHeight: 100,
+ canvasWidth: 100,
+ pixelWidth: 2,
+ pixelHeight: 2,
+ renderDiffuse: false,
+ renderShadows: false,
+ renderHighlights: false,
+ renderReflections: false,
+ rayDepth: 2
+ }, options || {});
+
+ this.options.canvasHeight /= this.options.pixelHeight;
+ this.options.canvasWidth /= this.options.pixelWidth;
+
+ /* TODO: dynamically include other scripts */
+ },
+
+ setPixel: function(x, y, color){
+ var pxW, pxH;
+ pxW = this.options.pixelWidth;
+ pxH = this.options.pixelHeight;
+
+ if (this.canvas) {
+ this.canvas.fillStyle = color.toString();
+ this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
+ } else {
+ if (x === y) {
+ checkNumber += color.brightness();
+ }
+ // print(x * pxW, y * pxH, pxW, pxH);
+ }
+ },
+
+ renderScene: function(scene, canvas){
+ checkNumber = 0;
+ /* Get canvas */
+ if (canvas) {
+ this.canvas = canvas.getContext("2d");
+ } else {
+ this.canvas = null;
+ }
+
+ var canvasHeight = this.options.canvasHeight;
+ var canvasWidth = this.options.canvasWidth;
+
+ for(var y=0; y < canvasHeight; y++){
+ for(var x=0; x < canvasWidth; x++){
+ var yp = y * 1.0 / canvasHeight * 2 - 1;
+ var xp = x * 1.0 / canvasWidth * 2 - 1;
+
+ var ray = scene.camera.getRay(xp, yp);
+
+ var color = this.getPixelColor(ray, scene);
+
+ this.setPixel(x, y, color);
+ }
+ }
+ if (checkNumber !== 2321) {
+ throw new Error("Scene rendered incorrectly");
+ }
+ },
+
+ getPixelColor: function(ray, scene){
+ var info = this.testIntersection(ray, scene, null);
+ if(info.isHit){
+ var color = this.rayTrace(info, ray, scene, 0);
+ return color;
+ }
+ return scene.background.color;
+ },
+
+ testIntersection: function(ray, scene, exclude){
+ var hits = 0;
+ var best = new Flog.RayTracer.IntersectionInfo();
+ best.distance = 2000;
+
+ for(var i=0; i<scene.shapes.length; i++){
+ var shape = scene.shapes[i];
+
+ if(shape != exclude){
+ var info = shape.intersect(ray);
+ if(info.isHit && info.distance >= 0 && info.distance < best.distance){
+ best = info;
+ hits++;
+ }
+ }
+ }
+ best.hitCount = hits;
+ return best;
+ },
+
+ getReflectionRay: function(P,N,V){
+ var c1 = -N.dot(V);
+ var R1 = Flog.RayTracer.Vector.prototype.add(
+ Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
+ V
+ );
+ return new Flog.RayTracer.Ray(P, R1);
+ },
+
+ rayTrace: function(info, ray, scene, depth){
+ // Calc ambient
+ var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
+ var oldColor = color;
+ var shininess = Math.pow(10, info.shape.material.gloss + 1);
+
+ for(var i=0; i<scene.lights.length; i++){
+ var light = scene.lights[i];
+
+ // Calc diffuse lighting
+ var v = Flog.RayTracer.Vector.prototype.subtract(
+ light.position,
+ info.position
+ ).normalize();
+
+ if(this.options.renderDiffuse){
+ var L = v.dot(info.normal);
+ if(L > 0.0){
+ color = Flog.RayTracer.Color.prototype.add(
+ color,
+ Flog.RayTracer.Color.prototype.multiply(
+ info.color,
+ Flog.RayTracer.Color.prototype.multiplyScalar(
+ light.color,
+ L
+ )
+ )
+ );
+ }
+ }
+
+ // The greater the depth the more accurate the colours, but
+ // this is exponentially (!) expensive
+ if(depth <= this.options.rayDepth){
+ // calculate reflection ray
+ if(this.options.renderReflections && info.shape.material.reflection > 0)
+ {
+ var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
+ var refl = this.testIntersection(reflectionRay, scene, info.shape);
+
+ if (refl.isHit && refl.distance > 0){
+ refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
+ } else {
+ refl.color = scene.background.color;
+ }
+
+ color = Flog.RayTracer.Color.prototype.blend(
+ color,
+ refl.color,
+ info.shape.material.reflection
+ );
+ }
+
+ // Refraction
+ /* TODO */
+ }
+
+ /* Render shadows and highlights */
+
+ var shadowInfo = new Flog.RayTracer.IntersectionInfo();
+
+ if(this.options.renderShadows){
+ var shadowRay = new Flog.RayTracer.Ray(info.position, v);
+
+ shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
+ if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
+ var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
+ var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
+ color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
+ }
+ }
+
+ // Phong specular highlights
+ if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
+ var Lv = Flog.RayTracer.Vector.prototype.subtract(
+ info.shape.position,
+ light.position
+ ).normalize();
+
+ var E = Flog.RayTracer.Vector.prototype.subtract(
+ scene.camera.position,
+ info.shape.position
+ ).normalize();
+
+ var H = Flog.RayTracer.Vector.prototype.subtract(
+ E,
+ Lv
+ ).normalize();
+
+ var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
+ color = Flog.RayTracer.Color.prototype.add(
+ Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
+ color
+ );
+ }
+ }
+ color.limit();
+ return color;
+ }
+};
+
+
+function renderScene(){
+ var scene = new Flog.RayTracer.Scene();
+
+ scene.camera = new Flog.RayTracer.Camera(
+ new Flog.RayTracer.Vector(0, 0, -15),
+ new Flog.RayTracer.Vector(-0.2, 0, 5),
+ new Flog.RayTracer.Vector(0, 1, 0)
+ );
+
+ scene.background = new Flog.RayTracer.Background(
+ new Flog.RayTracer.Color(0.5, 0.5, 0.5),
+ 0.4
+ );
+
+ var sphere = new Flog.RayTracer.Shape.Sphere(
+ new Flog.RayTracer.Vector(-1.5, 1.5, 2),
+ 1.5,
+ new Flog.RayTracer.Material.Solid(
+ new Flog.RayTracer.Color(0,0.5,0.5),
+ 0.3,
+ 0.0,
+ 0.0,
+ 2.0
+ )
+ );
+
+ var sphere1 = new Flog.RayTracer.Shape.Sphere(
+ new Flog.RayTracer.Vector(1, 0.25, 1),
+ 0.5,
+ new Flog.RayTracer.Material.Solid(
+ new Flog.RayTracer.Color(0.9,0.9,0.9),
+ 0.1,
+ 0.0,
+ 0.0,
+ 1.5
+ )
+ );
+
+ var plane = new Flog.RayTracer.Shape.Plane(
+ new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
+ 1.2,
+ new Flog.RayTracer.Material.Chessboard(
+ new Flog.RayTracer.Color(1,1,1),
+ new Flog.RayTracer.Color(0,0,0),
+ 0.2,
+ 0.0,
+ 1.0,
+ 0.7
+ )
+ );
+
+ scene.shapes.push(plane);
+ scene.shapes.push(sphere);
+ scene.shapes.push(sphere1);
+
+ var light = new Flog.RayTracer.Light(
+ new Flog.RayTracer.Vector(5, 10, -1),
+ new Flog.RayTracer.Color(0.8, 0.8, 0.8)
+ );
+
+ var light1 = new Flog.RayTracer.Light(
+ new Flog.RayTracer.Vector(-3, 5, -15),
+ new Flog.RayTracer.Color(0.8, 0.8, 0.8),
+ 100
+ );
+
+ scene.lights.push(light);
+ scene.lights.push(light1);
+
+ var imageWidth = 100; // $F('imageWidth');
+ var imageHeight = 100; // $F('imageHeight');
+ var pixelSize = "5,5".split(','); // $F('pixelSize').split(',');
+ var renderDiffuse = true; // $F('renderDiffuse');
+ var renderShadows = true; // $F('renderShadows');
+ var renderHighlights = true; // $F('renderHighlights');
+ var renderReflections = true; // $F('renderReflections');
+ var rayDepth = 2;//$F('rayDepth');
+
+ var raytracer = new Flog.RayTracer.Engine(
+ {
+ canvasWidth: imageWidth,
+ canvasHeight: imageHeight,
+ pixelWidth: pixelSize[0],
+ pixelHeight: pixelSize[1],
+ "renderDiffuse": renderDiffuse,
+ "renderHighlights": renderHighlights,
+ "renderShadows": renderShadows,
+ "renderReflections": renderReflections,
+ "rayDepth": rayDepth
+ }
+ );
+
+ raytracer.renderScene(scene, null, 0);
+}
diff --git a/chromium/v8/benchmarks/regexp.js b/chromium/v8/benchmarks/regexp.js
new file mode 100644
index 00000000000..9c831422663
--- /dev/null
+++ b/chromium/v8/benchmarks/regexp.js
@@ -0,0 +1,1764 @@
+// 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.
+
+// Automatically generated on 2009-01-30. Manually updated on 2010-09-17.
+
+// This benchmark is generated by loading 50 of the most popular pages
+// on the web and logging all regexp operations performed. Each
+// operation is given a weight that is calculated from an estimate of
+// the popularity of the pages where it occurs and the number of times
+// it is executed while loading each page. Furthermore the literal
+// letters in the data are encoded using ROT13 in a way that does not
+// affect how the regexps match their input. Finally the strings are
+// scrambled to exercise the regexp engine on different input strings.
+
+
+var RegExp = new BenchmarkSuite('RegExp', 910985, [
+ new Benchmark("RegExp", RegExpRun, RegExpSetup, RegExpTearDown)
+]);
+
+var regExpBenchmark = null;
+
+function RegExpSetup() {
+ regExpBenchmark = new RegExpBenchmark();
+ RegExpRun(); // run once to get system initialized
+}
+
+function RegExpRun() {
+ regExpBenchmark.run();
+}
+
+function RegExpTearDown() {
+ regExpBenchmark = null;
+}
+
+// Returns an array of n different variants of the input string str.
+// The variants are computed by randomly rotating one random
+// character.
+function computeInputVariants(str, n) {
+ var variants = [ str ];
+ for (var i = 1; i < n; i++) {
+ var pos = Math.floor(Math.random() * str.length);
+ var chr = String.fromCharCode((str.charCodeAt(pos) + Math.floor(Math.random() * 128)) % 128);
+ variants[i] = str.substring(0, pos) + chr + str.substring(pos + 1, str.length);
+ }
+ return variants;
+}
+
+function RegExpBenchmark() {
+ var re0 = /^ba/;
+ var re1 = /(((\w+):\/\/)([^\/:]*)(:(\d+))?)?([^#?]*)(\?([^#]*))?(#(.*))?/;
+ var re2 = /^\s*|\s*$/g;
+ var re3 = /\bQBZPbageby_cynprubyqre\b/;
+ var re4 = /,/;
+ var re5 = /\bQBZPbageby_cynprubyqre\b/g;
+ var re6 = /^[\s\xa0]+|[\s\xa0]+$/g;
+ var re7 = /(\d*)(\D*)/g;
+ var re8 = /=/;
+ var re9 = /(^|\s)lhv\-h(\s|$)/;
+ var str0 = 'Zbmvyyn/5.0 (Jvaqbjf; H; Jvaqbjf AG 5.1; ra-HF) NccyrJroXvg/528.9 (XUGZY, yvxr Trpxb) Puebzr/2.0.157.0 Fnsnev/528.9';
+ var re10 = /\#/g;
+ var re11 = /\./g;
+ var re12 = /'/g;
+ var re13 = /\?[\w\W]*(sevraqvq|punaaryvq|tebhcvq)=([^\&\?#]*)/i;
+ var str1 = 'Fubpxjnir Synfu 9.0 e115';
+ var re14 = /\s+/g;
+ var re15 = /^\s*(\S*(\s+\S+)*)\s*$/;
+ var re16 = /(-[a-z])/i;
+
+ var s0 = computeInputVariants('pyvpx', 6511);
+ var s1 = computeInputVariants('uggc://jjj.snprobbx.pbz/ybtva.cuc', 1844);
+ var s2 = computeInputVariants('QBZPbageby_cynprubyqre', 739);
+ var s3 = computeInputVariants('uggc://jjj.snprobbx.pbz/', 598);
+ var s4 = computeInputVariants('uggc://jjj.snprobbx.pbz/fepu.cuc', 454);
+ var s5 = computeInputVariants('qqqq, ZZZ q, llll', 352);
+ var s6 = computeInputVariants('vachggrkg QBZPbageby_cynprubyqre', 312);
+ var s7 = computeInputVariants('/ZlFcnprUbzrcntr/Vaqrk-FvgrUbzr,10000000', 282);
+ var s8 = computeInputVariants('vachggrkg', 177);
+ var s9 = computeInputVariants('528.9', 170);
+ var s10 = computeInputVariants('528', 170);
+ var s11 = computeInputVariants('VCPhygher=ra-HF', 156);
+ var s12 = computeInputVariants('CersreerqPhygher=ra-HF', 156);
+ var s13 = computeInputVariants('xrlcerff', 144);
+ var s14 = computeInputVariants('521', 139);
+ var s15 = computeInputVariants(str0, 139);
+ var s16 = computeInputVariants('qvi .so_zrah', 137);
+ var s17 = computeInputVariants('qvi.so_zrah', 137);
+ var s18 = computeInputVariants('uvqqra_ryrz', 117);
+ var s19 = computeInputVariants('sevraqfgre_naba=nvq%3Qn6ss9p85n868ro9s059pn854735956o3%26ers%3Q%26df%3Q%26vpgl%3QHF', 95);
+ var s20 = computeInputVariants('uggc://ubzr.zlfcnpr.pbz/vaqrk.psz', 93);
+ var s21 = computeInputVariants(str1, 92);
+ var s22 = computeInputVariants('svefg', 85);
+ var s23 = computeInputVariants('uggc://cebsvyr.zlfcnpr.pbz/vaqrk.psz', 85);
+ var s24 = computeInputVariants('ynfg', 85);
+ var s25 = computeInputVariants('qvfcynl', 85);
+
+ function runBlock0() {
+ for (var i = 0; i < 6511; i++) {
+ re0.exec(s0[i]);
+ }
+ for (var i = 0; i < 1844; i++) {
+ re1.exec(s1[i]);
+ }
+ for (var i = 0; i < 739; i++) {
+ s2[i].replace(re2, '');
+ }
+ for (var i = 0; i < 598; i++) {
+ re1.exec(s3[i]);
+ }
+ for (var i = 0; i < 454; i++) {
+ re1.exec(s4[i]);
+ }
+ for (var i = 0; i < 352; i++) {
+ /qqqq|qqq|qq|q|ZZZZ|ZZZ|ZZ|Z|llll|ll|l|uu|u|UU|U|zz|z|ff|f|gg|g|sss|ss|s|mmm|mm|m/g.exec(s5[i]);
+ }
+ for (var i = 0; i < 312; i++) {
+ re3.exec(s6[i]);
+ }
+ for (var i = 0; i < 282; i++) {
+ re4.exec(s7[i]);
+ }
+ for (var i = 0; i < 177; i++) {
+ s8[i].replace(re5, '');
+ }
+ for (var i = 0; i < 170; i++) {
+ s9[i].replace(re6, '');
+ re7.exec(s10[i]);
+ }
+ for (var i = 0; i < 156; i++) {
+ re8.exec(s11[i]);
+ re8.exec(s12[i]);
+ }
+ for (var i = 0; i < 144; i++) {
+ re0.exec(s13[i]);
+ }
+ for (var i = 0; i < 139; i++) {
+ s14[i].replace(re6, '');
+ re7.exec(s14[i]);
+ re9.exec('');
+ /JroXvg\/(\S+)/.exec(s15[i]);
+ }
+ for (var i = 0; i < 137; i++) {
+ s16[i].replace(re10, '');
+ s16[i].replace(/\[/g, '');
+ s17[i].replace(re11, '');
+ }
+ for (var i = 0; i < 117; i++) {
+ s18[i].replace(re2, '');
+ }
+ for (var i = 0; i < 95; i++) {
+ /(?:^|;)\s*sevraqfgre_ynat=([^;]*)/.exec(s19[i]);
+ }
+ for (var i = 0; i < 93; i++) {
+ s20[i].replace(re12, '');
+ re13.exec(s20[i]);
+ }
+ for (var i = 0; i < 92; i++) {
+ s21[i].replace(/([a-zA-Z]|\s)+/, '');
+ }
+ for (var i = 0; i < 85; i++) {
+ s22[i].replace(re14, '');
+ s22[i].replace(re15, '');
+ s23[i].replace(re12, '');
+ s24[i].replace(re14, '');
+ s24[i].replace(re15, '');
+ re16.exec(s25[i]);
+ re13.exec(s23[i]);
+ }
+ }
+ var re17 = /(^|[^\\])\"\\\/Qngr\((-?[0-9]+)\)\\\/\"/g;
+ var str2 = '{"anzr":"","ahzoreSbezng":{"PheeraplQrpvznyQvtvgf":2,"PheeraplQrpvznyFrcnengbe":".","VfErnqBayl":gehr,"PheeraplTebhcFvmrf":[3],"AhzoreTebhcFvmrf":[3],"CrepragTebhcFvmrf":[3],"PheeraplTebhcFrcnengbe":",","PheeraplFlzoby":"\xa4","AnAFlzoby":"AnA","PheeraplArtngvirCnggrea":0,"AhzoreArtngvirCnggrea":1,"CrepragCbfvgvirCnggrea":0,"CrepragArtngvirCnggrea":0,"ArtngvirVasvavglFlzoby":"-Vasvavgl","ArtngvirFvta":"-","AhzoreQrpvznyQvtvgf":2,"AhzoreQrpvznyFrcnengbe":".","AhzoreTebhcFrcnengbe":",","PheeraplCbfvgvirCnggrea":0,"CbfvgvirVasvavglFlzoby":"Vasvavgl","CbfvgvirFvta":"+","CrepragQrpvznyQvtvgf":2,"CrepragQrpvznyFrcnengbe":".","CrepragTebhcFrcnengbe":",","CrepragFlzoby":"%","CreZvyyrFlzoby":"\u2030","AngvirQvtvgf":["0","1","2","3","4","5","6","7","8","9"],"QvtvgFhofgvghgvba":1},"qngrGvzrSbezng":{"NZQrfvtangbe":"NZ","Pnyraqne":{"ZvaFhccbegrqQngrGvzr":"@-62135568000000@","ZnkFhccbegrqQngrGvzr":"@253402300799999@","NytbevguzGlcr":1,"PnyraqneGlcr":1,"Renf":[1],"GjbQvtvgLrneZnk":2029,"VfErnqBayl":gehr},"QngrFrcnengbe":"/","SvefgQnlBsJrrx":0,"PnyraqneJrrxEhyr":0,"ShyyQngrGvzrCnggrea":"qqqq, qq ZZZZ llll UU:zz:ff","YbatQngrCnggrea":"qqqq, qq ZZZZ llll","YbatGvzrCnggrea":"UU:zz:ff","ZbaguQnlCnggrea":"ZZZZ qq","CZQrfvtangbe":"CZ","ESP1123Cnggrea":"qqq, qq ZZZ llll UU\':\'zz\':\'ff \'TZG\'","FubegQngrCnggrea":"ZZ/qq/llll","FubegGvzrCnggrea":"UU:zz","FbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq\'G\'UU\':\'zz\':\'ff","GvzrFrcnengbe":":","HavirefnyFbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq UU\':\'zz\':\'ff\'M\'","LrneZbaguCnggrea":"llll ZZZZ","NooerivngrqQnlAnzrf":["Fha","Zba","Ghr","Jrq","Guh","Sev","Fng"],"FubegrfgQnlAnzrf":["Fh","Zb","Gh","Jr","Gu","Se","Fn"],"QnlAnzrf":["Fhaqnl","Zbaqnl","Ghrfqnl","Jrqarfqnl","Guhefqnl","Sevqnl","Fngheqnl"],"NooerivngrqZbaguAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""],"VfErnqBayl":gehr,"AngvirPnyraqneAnzr":"Tertbevna Pnyraqne","NooerivngrqZbaguTravgvirAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguTravgvirAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""]}}';
+ var str3 = '{"anzr":"ra-HF","ahzoreSbezng":{"PheeraplQrpvznyQvtvgf":2,"PheeraplQrpvznyFrcnengbe":".","VfErnqBayl":snyfr,"PheeraplTebhcFvmrf":[3],"AhzoreTebhcFvmrf":[3],"CrepragTebhcFvmrf":[3],"PheeraplTebhcFrcnengbe":",","PheeraplFlzoby":"$","AnAFlzoby":"AnA","PheeraplArtngvirCnggrea":0,"AhzoreArtngvirCnggrea":1,"CrepragCbfvgvirCnggrea":0,"CrepragArtngvirCnggrea":0,"ArtngvirVasvavglFlzoby":"-Vasvavgl","ArtngvirFvta":"-","AhzoreQrpvznyQvtvgf":2,"AhzoreQrpvznyFrcnengbe":".","AhzoreTebhcFrcnengbe":",","PheeraplCbfvgvirCnggrea":0,"CbfvgvirVasvavglFlzoby":"Vasvavgl","CbfvgvirFvta":"+","CrepragQrpvznyQvtvgf":2,"CrepragQrpvznyFrcnengbe":".","CrepragTebhcFrcnengbe":",","CrepragFlzoby":"%","CreZvyyrFlzoby":"\u2030","AngvirQvtvgf":["0","1","2","3","4","5","6","7","8","9"],"QvtvgFhofgvghgvba":1},"qngrGvzrSbezng":{"NZQrfvtangbe":"NZ","Pnyraqne":{"ZvaFhccbegrqQngrGvzr":"@-62135568000000@","ZnkFhccbegrqQngrGvzr":"@253402300799999@","NytbevguzGlcr":1,"PnyraqneGlcr":1,"Renf":[1],"GjbQvtvgLrneZnk":2029,"VfErnqBayl":snyfr},"QngrFrcnengbe":"/","SvefgQnlBsJrrx":0,"PnyraqneJrrxEhyr":0,"ShyyQngrGvzrCnggrea":"qqqq, ZZZZ qq, llll u:zz:ff gg","YbatQngrCnggrea":"qqqq, ZZZZ qq, llll","YbatGvzrCnggrea":"u:zz:ff gg","ZbaguQnlCnggrea":"ZZZZ qq","CZQrfvtangbe":"CZ","ESP1123Cnggrea":"qqq, qq ZZZ llll UU\':\'zz\':\'ff \'TZG\'","FubegQngrCnggrea":"Z/q/llll","FubegGvzrCnggrea":"u:zz gg","FbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq\'G\'UU\':\'zz\':\'ff","GvzrFrcnengbe":":","HavirefnyFbegnoyrQngrGvzrCnggrea":"llll\'-\'ZZ\'-\'qq UU\':\'zz\':\'ff\'M\'","LrneZbaguCnggrea":"ZZZZ, llll","NooerivngrqQnlAnzrf":["Fha","Zba","Ghr","Jrq","Guh","Sev","Fng"],"FubegrfgQnlAnzrf":["Fh","Zb","Gh","Jr","Gu","Se","Fn"],"QnlAnzrf":["Fhaqnl","Zbaqnl","Ghrfqnl","Jrqarfqnl","Guhefqnl","Sevqnl","Fngheqnl"],"NooerivngrqZbaguAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""],"VfErnqBayl":snyfr,"AngvirPnyraqneAnzr":"Tertbevna Pnyraqne","NooerivngrqZbaguTravgvirAnzrf":["Wna","Sro","Zne","Nce","Znl","Wha","Why","Nht","Frc","Bpg","Abi","Qrp",""],"ZbaguTravgvirAnzrf":["Wnahnel","Sroehnel","Znepu","Ncevy","Znl","Whar","Whyl","Nhthfg","Frcgrzore","Bpgbore","Abirzore","Qrprzore",""]}}';
+ var str4 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str5 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var re18 = /^\s+|\s+$/g;
+ var str6 = 'uggc://jjj.snprobbx.pbz/vaqrk.cuc';
+ var re19 = /(?:^|\s+)ba(?:\s+|$)/;
+ var re20 = /[+, ]/;
+ var re21 = /ybnqrq|pbzcyrgr/;
+ var str7 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(d1)c=d1.EbyybssCnary;ine bo=IjTrgBow("IjCnayNQ_VQ_"+c);vs(bo&&bo.fglyr.ivfvovyvgl=="ivfvoyr"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe("U")+fns,pheL=r.pyvragL+IjBOFpe("I")+fns;ine y=IjBOEC(NQ_VQ,bo,"Y"),g=IjBOEC(NQ_VQ,bo,"G");ine e=y+d1.Cnaryf[c].Jvqgu,o=g+d1.Cnaryf[c].Urvtug;vs((pheK<y)||(pheK>e)||(pheL<g)||(pheL>o)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,"");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(d1&&d1.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(d1)d1.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag("ba"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(d1)d1.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag("ba"+z,s);}pngpu(r){}};;d1.IjTc=d2(n,c){ine nq=d1;vs(vfAnA(c)){sbe(ine v=0;v<nq.Cnaryf.yratgu;v++)vs(nq.Cnaryf[v].Anzr==c)erghea v;erghea 0;}erghea c;};;d1.IjTpy=d2(n,c,p){ine cn=d1.Cnaryf[IjTc(n,c)];vs(!cn)erghea 0;vs(vfAnA(p)){sbe(ine v=0;v<cn.Pyvpxguehf.yratgu;v++)vs(cn.Pyvpxguehf[v].Anzr==p)erghea v;erghea 0;}erghea p;};;d1.IjGenpr=d2(n,f){gel{vs(jvaqbj["Ij"+"QtQ"])jvaqbj["Ij"+"QtQ"](n,1,f);}pngpu(r){}};;d1.IjYvzvg1=d2(n,f){ine nq=d1,vh=f.fcyvg("/");sbe(ine v=0,p=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.FzV.yratgu>0)nq.FzV+="/";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;d1.IjYvzvg0=d2(n,f){ine nq=d1,vh=f.fcyvg("/");sbe(ine v=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.OvC.yratgu>0)nq.OvC+="/";nq.OvC+=vh[v];}}};;d1.IjRVST=d2(n,c){jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]=IjTrgBow("IjCnayNQ_VQ_"+c+"_Bow");vs(jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]==ahyy)frgGvzrbhg("IjRVST(NQ_VQ,"+c+")",d1.rvsg);};;d1.IjNavzSHC=d2(n,c){ine nq=d1;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j=="100%"){j=sf;en=snyfr;yn=snyfr;}vs(u=="100%"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY=="Y")yn=snyfr;vs(cn.YnY=="E")en=snyfr;vs(cn.GnY=="G")nn=snyfr;vs(cn.GnY=="O")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ <nq.NshG)frgGvzrbhg(("IjNavzSHC(NQ_VQ,"+c+")"),nq.NshC);ryfr{k=-1000;l=k;}cna.YrsgBssfrg=k;cna.GbcBssfrg=l;IjNhErcb(n,c);};;d1.IjTrgErnyCbfvgvba=d2(n,b,j){erghea IjBOEC.nccyl(guvf,nethzragf);};;d1.IjPnapryGvzrbhg=d2(n,c){c=IjTc(n,c);ine cay=d1.Cnaryf[c];vs(cay&&cay.UgU!=""){pyrneGvzrbhg(cay.UgU);}};;d1.IjPnapryNyyGvzrbhgf=d2(n){vs(d1.YbpxGvzrbhgPunatrf)erghea;sbe(ine c=0;c<d1.bac;c++)IjPnapryGvzrbhg(n,c);};;d1.IjFgnegGvzrbhg=d2(n,c,bG){c=IjTc(n,c);ine cay=d1.Cnaryf[c];vs(cay&&((cay.UvqrGvzrbhgInyhr>0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;d1.IjErfrgGvzrbhg=d2(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny("IjFgnegGvzrbhg(NQ_VQ,c"+(nethzragf.yratgu==3?",bG":"")+")");};;d1.IjErfrgNyyGvzrbhgf=d2(n){sbe(ine c=0;c<d1.bac;c++)IjErfrgGvzrbhg(n,c);};;d1.IjQrgnpure=d2(n,rig,sap){gel{vs(IjQVR5)riny("jvaqbj.qrgnpuRirag(\'ba"+rig+"\',"+sap+"NQ_VQ)");ryfr vs(!IjQVRZnp)riny("jvaqbj.erzbirRiragYvfgrare(\'"+rig+"\',"+sap+"NQ_VQ,snyfr)");}pngpu(r){}};;d1.IjPyrnaHc=d2(n){IjCvat(n,"G");ine nq=d1;sbe(ine v=0;v<nq.Cnaryf.yratgu;v++){IjUvqrCnary(n,v,gehr);}gel{IjTrgBow(nq.gya).vaareUGZY="";}pngpu(r){}vs(nq.gya!=nq.gya2)gel{IjTrgBow(nq.gya2).vaareUGZY="";}pngpu(r){}gel{d1=ahyy;}pngpu(r){}gel{IjQrgnpure(n,"haybnq","IjHayNQ_VQ");}pngpu(r){}gel{jvaqbj.IjHayNQ_VQ=ahyy;}pngpu(r){}gel{IjQrgnpure(n,"fpebyy","IjFeNQ_VQ");}pngpu(r){}gel{jvaqbj.IjFeNQ_VQ=ahyy;}pngpu(r){}gel{IjQrgnpure(n,"erfvmr","IjEmNQ_VQ");}pngpu(r){}gel{jvaqbj.IjEmNQ_VQ=ahyy;}pngpu(r){}gel{IjQrgnpure(n';
+ var str8 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(jvaqbj.IjNqNQ_VQ)c=jvaqbj.IjNqNQ_VQ.EbyybssCnary;ine bo=IjTrgBow("IjCnayNQ_VQ_"+c);vs(bo&&bo.fglyr.ivfvovyvgl=="ivfvoyr"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe("U")+fns,pheL=r.pyvragL+IjBOFpe("I")+fns;ine y=IjBOEC(NQ_VQ,bo,"Y"),g=IjBOEC(NQ_VQ,bo,"G");ine e=y+jvaqbj.IjNqNQ_VQ.Cnaryf[c].Jvqgu,o=g+jvaqbj.IjNqNQ_VQ.Cnaryf[c].Urvtug;vs((pheK<y)||(pheK>e)||(pheL<g)||(pheL>o)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,"");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(jvaqbj.IjNqNQ_VQ&&jvaqbj.IjNqNQ_VQ.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(jvaqbj.IjNqNQ_VQ)jvaqbj.IjNqNQ_VQ.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag("ba"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(jvaqbj.IjNqNQ_VQ)jvaqbj.IjNqNQ_VQ.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag("ba"+z,s);}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjTc=shapgvba(n,c){ine nq=jvaqbj.IjNqNQ_VQ;vs(vfAnA(c)){sbe(ine v=0;v<nq.Cnaryf.yratgu;v++)vs(nq.Cnaryf[v].Anzr==c)erghea v;erghea 0;}erghea c;};;jvaqbj.IjNqNQ_VQ.IjTpy=shapgvba(n,c,p){ine cn=jvaqbj.IjNqNQ_VQ.Cnaryf[IjTc(n,c)];vs(!cn)erghea 0;vs(vfAnA(p)){sbe(ine v=0;v<cn.Pyvpxguehf.yratgu;v++)vs(cn.Pyvpxguehf[v].Anzr==p)erghea v;erghea 0;}erghea p;};;jvaqbj.IjNqNQ_VQ.IjGenpr=shapgvba(n,f){gel{vs(jvaqbj["Ij"+"QtQ"])jvaqbj["Ij"+"QtQ"](n,1,f);}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjYvzvg1=shapgvba(n,f){ine nq=jvaqbj.IjNqNQ_VQ,vh=f.fcyvg("/");sbe(ine v=0,p=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.FzV.yratgu>0)nq.FzV+="/";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;jvaqbj.IjNqNQ_VQ.IjYvzvg0=shapgvba(n,f){ine nq=jvaqbj.IjNqNQ_VQ,vh=f.fcyvg("/");sbe(ine v=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.OvC.yratgu>0)nq.OvC+="/";nq.OvC+=vh[v];}}};;jvaqbj.IjNqNQ_VQ.IjRVST=shapgvba(n,c){jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]=IjTrgBow("IjCnayNQ_VQ_"+c+"_Bow");vs(jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]==ahyy)frgGvzrbhg("IjRVST(NQ_VQ,"+c+")",jvaqbj.IjNqNQ_VQ.rvsg);};;jvaqbj.IjNqNQ_VQ.IjNavzSHC=shapgvba(n,c){ine nq=jvaqbj.IjNqNQ_VQ;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j=="100%"){j=sf;en=snyfr;yn=snyfr;}vs(u=="100%"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY=="Y")yn=snyfr;vs(cn.YnY=="E")en=snyfr;vs(cn.GnY=="G")nn=snyfr;vs(cn.GnY=="O")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ <nq.NshG)frgGvzrbhg(("IjNavzSHC(NQ_VQ,"+c+")"),nq.NshC);ryfr{k=-1000;l=k;}cna.YrsgBssfrg=k;cna.GbcBssfrg=l;IjNhErcb(n,c);};;jvaqbj.IjNqNQ_VQ.IjTrgErnyCbfvgvba=shapgvba(n,b,j){erghea IjBOEC.nccyl(guvf,nethzragf);};;jvaqbj.IjNqNQ_VQ.IjPnapryGvzrbhg=shapgvba(n,c){c=IjTc(n,c);ine cay=jvaqbj.IjNqNQ_VQ.Cnaryf[c];vs(cay&&cay.UgU!=""){pyrneGvzrbhg(cay.UgU);}};;jvaqbj.IjNqNQ_VQ.IjPnapryNyyGvzrbhgf=shapgvba(n){vs(jvaqbj.IjNqNQ_VQ.YbpxGvzrbhgPunatrf)erghea;sbe(ine c=0;c<jvaqbj.IjNqNQ_VQ.bac;c++)IjPnapryGvzrbhg(n,c);};;jvaqbj.IjNqNQ_VQ.IjFgnegGvzrbhg=shapgvba(n,c,bG){c=IjTc(n,c);ine cay=jvaqbj.IjNqNQ_VQ.Cnaryf[c];vs(cay&&((cay.UvqrGvzrbhgInyhr>0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;jvaqbj.IjNqNQ_VQ.IjErfrgGvzrbhg=shapgvba(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny("IjFgnegGvzrbhg(NQ_VQ,c"+(nethzragf.yratgu==3?",bG":"")+")");};;jvaqbj.IjNqNQ_VQ.IjErfrgNyyGvzrbhgf=shapgvba(n){sbe(ine c=0;c<jvaqbj.IjNqNQ_VQ.bac;c++)IjErfrgGvzrbhg(n,c);};;jvaqbj.IjNqNQ_VQ.IjQrgnpure=shapgvba(n,rig,sap){gel{vs(IjQVR5)riny("jvaqbj.qrgnpuRirag(\'ba"+rig+"\',"+sap+"NQ_VQ)");ryfr vs(!IjQVRZnp)riny("jvaqbj.erzbir';
+ var str9 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(jvaqbj.IjNqNQ_VQ)c=jvaqbj.IjNqNQ_VQ.EbyybssCnary;ine bo=IjTrgBow("IjCnayNQ_VQ_"+c);vs(bo&&bo.fglyr.ivfvovyvgl=="ivfvoyr"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe("U")+fns,pheL=r.pyvragL+IjBOFpe("I")+fns;ine y=IjBOEC(NQ_VQ,bo,"Y"),g=IjBOEC(NQ_VQ,bo,"G");ine e=y+jvaqbj.IjNqNQ_VQ.Cnaryf[c].Jvqgu,o=g+jvaqbj.IjNqNQ_VQ.Cnaryf[c].Urvtug;vs((pheK<y)||(pheK>e)||(pheL<g)||(pheL>o)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,"");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(jvaqbj.IjNqNQ_VQ&&jvaqbj.IjNqNQ_VQ.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(jvaqbj.IjNqNQ_VQ)jvaqbj.IjNqNQ_VQ.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag("ba"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z="zbhfrzbir",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(jvaqbj.IjNqNQ_VQ)jvaqbj.IjNqNQ_VQ.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag("ba"+z,s);}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjTc=d2(n,c){ine nq=jvaqbj.IjNqNQ_VQ;vs(vfAnA(c)){sbe(ine v=0;v<nq.Cnaryf.yratgu;v++)vs(nq.Cnaryf[v].Anzr==c)erghea v;erghea 0;}erghea c;};;jvaqbj.IjNqNQ_VQ.IjTpy=d2(n,c,p){ine cn=jvaqbj.IjNqNQ_VQ.Cnaryf[IjTc(n,c)];vs(!cn)erghea 0;vs(vfAnA(p)){sbe(ine v=0;v<cn.Pyvpxguehf.yratgu;v++)vs(cn.Pyvpxguehf[v].Anzr==p)erghea v;erghea 0;}erghea p;};;jvaqbj.IjNqNQ_VQ.IjGenpr=d2(n,f){gel{vs(jvaqbj["Ij"+"QtQ"])jvaqbj["Ij"+"QtQ"](n,1,f);}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjYvzvg1=d2(n,f){ine nq=jvaqbj.IjNqNQ_VQ,vh=f.fcyvg("/");sbe(ine v=0,p=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.FzV.yratgu>0)nq.FzV+="/";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;jvaqbj.IjNqNQ_VQ.IjYvzvg0=d2(n,f){ine nq=jvaqbj.IjNqNQ_VQ,vh=f.fcyvg("/");sbe(ine v=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.OvC.yratgu>0)nq.OvC+="/";nq.OvC+=vh[v];}}};;jvaqbj.IjNqNQ_VQ.IjRVST=d2(n,c){jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]=IjTrgBow("IjCnayNQ_VQ_"+c+"_Bow");vs(jvaqbj["IjCnayNQ_VQ_"+c+"_Bow"]==ahyy)frgGvzrbhg("IjRVST(NQ_VQ,"+c+")",jvaqbj.IjNqNQ_VQ.rvsg);};;jvaqbj.IjNqNQ_VQ.IjNavzSHC=d2(n,c){ine nq=jvaqbj.IjNqNQ_VQ;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j=="100%"){j=sf;en=snyfr;yn=snyfr;}vs(u=="100%"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY=="Y")yn=snyfr;vs(cn.YnY=="E")en=snyfr;vs(cn.GnY=="G")nn=snyfr;vs(cn.GnY=="O")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ <nq.NshG)frgGvzrbhg(("IjNavzSHC(NQ_VQ,"+c+")"),nq.NshC);ryfr{k=-1000;l=k;}cna.YrsgBssfrg=k;cna.GbcBssfrg=l;IjNhErcb(n,c);};;jvaqbj.IjNqNQ_VQ.IjTrgErnyCbfvgvba=d2(n,b,j){erghea IjBOEC.nccyl(guvf,nethzragf);};;jvaqbj.IjNqNQ_VQ.IjPnapryGvzrbhg=d2(n,c){c=IjTc(n,c);ine cay=jvaqbj.IjNqNQ_VQ.Cnaryf[c];vs(cay&&cay.UgU!=""){pyrneGvzrbhg(cay.UgU);}};;jvaqbj.IjNqNQ_VQ.IjPnapryNyyGvzrbhgf=d2(n){vs(jvaqbj.IjNqNQ_VQ.YbpxGvzrbhgPunatrf)erghea;sbe(ine c=0;c<jvaqbj.IjNqNQ_VQ.bac;c++)IjPnapryGvzrbhg(n,c);};;jvaqbj.IjNqNQ_VQ.IjFgnegGvzrbhg=d2(n,c,bG){c=IjTc(n,c);ine cay=jvaqbj.IjNqNQ_VQ.Cnaryf[c];vs(cay&&((cay.UvqrGvzrbhgInyhr>0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;jvaqbj.IjNqNQ_VQ.IjErfrgGvzrbhg=d2(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny("IjFgnegGvzrbhg(NQ_VQ,c"+(nethzragf.yratgu==3?",bG":"")+")");};;jvaqbj.IjNqNQ_VQ.IjErfrgNyyGvzrbhgf=d2(n){sbe(ine c=0;c<jvaqbj.IjNqNQ_VQ.bac;c++)IjErfrgGvzrbhg(n,c);};;jvaqbj.IjNqNQ_VQ.IjQrgnpure=d2(n,rig,sap){gel{vs(IjQVR5)riny("jvaqbj.qrgnpuRirag(\'ba"+rig+"\',"+sap+"NQ_VQ)");ryfr vs(!IjQVRZnp)riny("jvaqbj.erzbirRiragYvfgrare(\'"+rig+"\',"+sap+"NQ_VQ,snyfr)");}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjPyrna';
+
+ var s26 = computeInputVariants('VC=74.125.75.1', 81);
+ var s27 = computeInputVariants('9.0 e115', 78);
+ var s28 = computeInputVariants('k',78);
+ var s29 = computeInputVariants(str2, 81);
+ var s30 = computeInputVariants(str3, 81);
+ var s31 = computeInputVariants('144631658', 78);
+ var s32 = computeInputVariants('Pbhagel=IIZ%3Q', 78);
+ var s33 = computeInputVariants('Pbhagel=IIZ=', 78);
+ var s34 = computeInputVariants('CersreerqPhygherCraqvat=', 78);
+ var s35 = computeInputVariants(str4, 78);
+ var s36 = computeInputVariants(str5, 78);
+ var s37 = computeInputVariants('__hgzp=144631658', 78);
+ var s38 = computeInputVariants('gvzrMbar=-8', 78);
+ var s39 = computeInputVariants('gvzrMbar=0', 78);
+ // var s40 = computeInputVariants(s15[i], 78);
+ var s41 = computeInputVariants('vachggrkg QBZPbageby_cynprubyqre', 78);
+ var s42 = computeInputVariants('xrlqbja', 78);
+ var s43 = computeInputVariants('xrlhc', 78);
+ var s44 = computeInputVariants('uggc://zrffntvat.zlfcnpr.pbz/vaqrk.psz', 77);
+ var s45 = computeInputVariants('FrffvbaFgbentr=%7O%22GnoThvq%22%3N%7O%22thvq%22%3N1231367125017%7Q%7Q', 73);
+ var s46 = computeInputVariants(str6, 72);
+ var s47 = computeInputVariants('3.5.0.0', 70);
+ var s48 = computeInputVariants(str7, 70);
+ var s49 = computeInputVariants(str8, 70);
+ var s50 = computeInputVariants(str9, 70);
+ var s51 = computeInputVariants('NI%3Q1_CI%3Q1_PI%3Q1_EI%3Q1_HI%3Q1_HP%3Q1_IC%3Q0.0.0.0_IH%3Q0', 70);
+ var s52 = computeInputVariants('svz_zlfcnpr_ubzrcntr_abgybttrqva,svz_zlfcnpr_aba_HTP,svz_zlfcnpr_havgrq-fgngrf', 70);
+ var s53 = computeInputVariants('ybnqvat', 70);
+ var s54 = computeInputVariants('#', 68);
+ var s55 = computeInputVariants('ybnqrq', 68);
+ var s56 = computeInputVariants('pbybe', 49);
+ var s57 = computeInputVariants('uggc://sevraqf.zlfcnpr.pbz/vaqrk.psz', 44);
+
+ function runBlock1() {
+ for (var i = 0; i < 81; i++) {
+ re8.exec(s26[i]);
+ }
+ for (var i = 0; i < 78; i++) {
+ s27[i].replace(/(\s)+e/, '');
+ s28[i].replace(/./, '');
+ s29[i].replace(re17, '');
+ s30[i].replace(re17, '');
+ re8.exec(s31[i]);
+ re8.exec(s32[i]);
+ re8.exec(s33[i]);
+ re8.exec(s34[i]);
+ re8.exec(s35[i]);
+ re8.exec(s36[i]);
+ re8.exec(s37[i]);
+ re8.exec(s38[i]);
+ re8.exec(s39[i]);
+ /Fnsnev\/(\d+\.\d+)/.exec(s15[i]);
+ re3.exec(s41[i]);
+ re0.exec(s42[i]);
+ re0.exec(s43[i]);
+ }
+ for (var i = 0; i < 77; i++) {
+ s44[i].replace(re12, '');
+ re13.exec(s44[i]);
+ }
+ for (var i = 0; i < 73; i++) {
+ s45[i].replace(re18, '');
+ }
+ for (var i = 0; i < 72; i++) {
+ re1.exec(s46[i]);
+ }
+ for (var i = 0; i < 71; i++) {
+ re19.exec('');
+ }
+ for (var i = 0; i < 70; i++) {
+ s47[i].replace(re11, '');
+ s48[i].replace(/d1/g, '');
+ s49[i].replace(/NQ_VQ/g, '');
+ s50[i].replace(/d2/g, '');
+ s51[i].replace(/_/g, '');
+ s52[i].split(re20);
+ re21.exec(s53[i]);
+ }
+ for (var i = 0; i < 68; i++) {
+ re1.exec(s54[i]);
+ /(?:ZFVR.(\d+\.\d+))|(?:(?:Sversbk|TenaCnenqvfb|Vprjrnfry).(\d+\.\d+))|(?:Bcren.(\d+\.\d+))|(?:NccyrJroXvg.(\d+(?:\.\d+)?))/.exec(s15[i]);
+ /(Znp BF K)|(Jvaqbjf;)/.exec(s15[i]);
+ /Trpxb\/([0-9]+)/.exec(s15[i]);
+ re21.exec(s55[i]);
+ }
+ for (var i = 0; i < 49; i++) {
+ re16.exec(s56[i]);
+ }
+ for (var i = 0; i < 44; i++) {
+ s57[i].replace(re12, '');
+ re13.exec(s57[i]);
+ }
+ }
+ var re22 = /\bso_zrah\b/;
+ var re23 = /^(?:(?:[^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/;
+ var re24 = /uggcf?:\/\/([^\/]+\.)?snprobbx\.pbz\//;
+ var re25 = /"/g;
+ var re26 = /^([^?#]+)(?:\?([^#]*))?(#.*)?/;
+ var s57a = computeInputVariants('fryrpgrq', 40);
+ var s58 = computeInputVariants('vachggrkg uvqqra_ryrz', 40);
+ var s59 = computeInputVariants('vachggrkg ', 40);
+ var s60 = computeInputVariants('vachggrkg', 40);
+ var s61 = computeInputVariants('uggc://jjj.snprobbx.pbz/', 40);
+ var s62 = computeInputVariants('uggc://jjj.snprobbx.pbz/ybtva.cuc', 40);
+ var s63 = computeInputVariants('Funer guvf tnqtrg', 40);
+ var s64 = computeInputVariants('uggc://jjj.tbbtyr.pbz/vt/qverpgbel', 40);
+ var s65 = computeInputVariants('419', 40);
+ var s66 = computeInputVariants('gvzrfgnzc', 40);
+
+ function runBlock2() {
+ for (var i = 0; i < 40; i++) {
+ s57a[i].replace(re14, '');
+ s57a[i].replace(re15, '');
+ }
+ for (var i = 0; i < 39; i++) {
+ s58[i].replace(/\buvqqra_ryrz\b/g, '');
+ re3.exec(s59[i]);
+ re3.exec(s60[i]);
+ re22.exec('HVYvaxOhggba');
+ re22.exec('HVYvaxOhggba_E');
+ re22.exec('HVYvaxOhggba_EJ');
+ re22.exec('zrah_ybtva_pbagnvare');
+ /\buvqqra_ryrz\b/.exec('vachgcnffjbeq');
+ }
+ for (var i = 0; i < 37; i++) {
+ re8.exec('111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904');
+ re8.exec('SbeprqRkcvengvba=633669315660164980');
+ re8.exec('FrffvbaQQS2=111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904');
+ }
+ for (var i = 0; i < 35; i++) {
+ 'puvyq p1 svefg'.replace(re14, '');
+ 'puvyq p1 svefg'.replace(re15, '');
+ 'sylbhg pybfrq'.replace(re14, '');
+ 'sylbhg pybfrq'.replace(re15, '');
+ }
+ for (var i = 0; i < 34; i++) {
+ re19.exec('gno2');
+ re19.exec('gno3');
+ re8.exec('44132r503660');
+ re8.exec('SbeprqRkcvengvba=633669316860113296');
+ re8.exec('AFP_zp_dfctwzs-aowb_80=44132r503660');
+ re8.exec('FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696');
+ re8.exec('s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696');
+ }
+ for (var i = 0; i < 32; i++) {
+ /puebzr/i.exec(s15[i]);
+ }
+ for (var i = 0; i < 31; i++) {
+ s61[i].replace(re23, '');
+ re8.exec('SbeprqRkcvengvba=633669358527244818');
+ re8.exec('VC=66.249.85.130');
+ re8.exec('FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58');
+ re8.exec('s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58');
+ re24.exec(s61[i]);
+ }
+ for (var i = 0; i < 30; i++) {
+ s65[i].replace(re6, '');
+ /(?:^|\s+)gvzrfgnzc(?:\s+|$)/.exec(s66[i]);
+ re7.exec(s65[i]);
+ }
+ for (var i = 0; i < 29; i++) {
+ s62[i].replace(re23, '');
+ }
+ for (var i = 0; i < 28; i++) {
+ s63[i].replace(re25, '');
+ s63[i].replace(re12, '');
+ re26.exec(s64[i]);
+ }
+ }
+ var re27 = /-\D/g;
+ var re28 = /\bnpgvingr\b/;
+ var re29 = /%2R/gi;
+ var re30 = /%2S/gi;
+ var re31 = /^(mu-(PA|GJ)|wn|xb)$/;
+ var re32 = /\s?;\s?/;
+ var re33 = /%\w?$/;
+ var re34 = /TNQP=([^;]*)/i;
+ var str10 = 'FrffvbaQQS2=111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669315660164980&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var str11 = 'FrffvbaQQS2=111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904; __hgzm=144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.3426875219718084000.1231363570.1231363570.1231363570.1; __hgzo=144631658.0.10.1231363570; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669315660164980&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str12 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231363514065&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231363514065&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Subzr.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1326469221.1231363557&tn_fvq=1231363557&tn_uvq=1114636509&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str13 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669315660164980&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str14 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669315660164980&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var re35 = /[<>]/g;
+ var str15 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=44132r503660';
+ var str16 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; AFP_zp_dfctwzs-aowb_80=44132r503660; __hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1; __hgzo=144631658.0.10.1231363638; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str17 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231363621014&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231363621014&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=348699119.1231363624&tn_fvq=1231363624&tn_uvq=895511034&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str18 = 'uggc://jjj.yrobapbva.se/yv';
+ var str19 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str20 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+
+ var s67 = computeInputVariants('e115', 27);
+ var s68 = computeInputVariants('qvfcynl', 27);
+ var s69 = computeInputVariants('cbfvgvba', 27);
+ var s70 = computeInputVariants('uggc://jjj.zlfcnpr.pbz/', 27);
+ var s71 = computeInputVariants('cntrivrj', 27);
+ var s72 = computeInputVariants('VC=74.125.75.3', 27);
+ var s73 = computeInputVariants('ra', 27);
+ var s74 = computeInputVariants(str10, 27);
+ var s75 = computeInputVariants(str11, 27);
+ var s76 = computeInputVariants(str12, 27);
+ var s77 = computeInputVariants(str17, 27);
+ var s78 = computeInputVariants(str18, 27);
+
+ function runBlock3() {
+ for (var i = 0; i < 27; i++) {
+ s67[i].replace(/[A-Za-z]/g, '');
+ }
+ for (var i = 0; i < 23; i++) {
+ s68[i].replace(re27, '');
+ s69[i].replace(re27, '');
+ }
+ for (var i = 0; i < 22; i++) {
+ 'unaqyr'.replace(re14, '');
+ 'unaqyr'.replace(re15, '');
+ 'yvar'.replace(re14, '');
+ 'yvar'.replace(re15, '');
+ 'cnerag puebzr6 fvatyr1 gno'.replace(re14, '');
+ 'cnerag puebzr6 fvatyr1 gno'.replace(re15, '');
+ 'fyvqre'.replace(re14, '');
+ 'fyvqre'.replace(re15, '');
+ re28.exec('');
+ }
+ for (var i = 0; i < 21; i++) {
+ s70[i].replace(re12, '');
+ re13.exec(s70[i]);
+ }
+ for (var i = 0; i < 20; i++) {
+ s71[i].replace(re29, '');
+ s71[i].replace(re30, '');
+ re19.exec('ynfg');
+ re19.exec('ba svefg');
+ re8.exec(s72[i]);
+ }
+ for (var i = 0; i < 19; i++) {
+ re31.exec(s73[i]);
+ }
+ for (var i = 0; i < 18; i++) {
+ s74[i].split(re32);
+ s75[i].split(re32);
+ s76[i].replace(re33, '');
+ re8.exec('144631658.0.10.1231363570');
+ re8.exec('144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.3426875219718084000.1231363570.1231363570.1231363570.1');
+ re8.exec(str13);
+ re8.exec(str14);
+ re8.exec('__hgzn=144631658.3426875219718084000.1231363570.1231363570.1231363570.1');
+ re8.exec('__hgzo=144631658.0.10.1231363570');
+ re8.exec('__hgzm=144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re34.exec(s74[i]);
+ re34.exec(s75[i]);
+ }
+ for (var i = 0; i < 17; i++) {
+ s15[i].match(/zfvr/gi);
+ s15[i].match(/bcren/gi);
+ str15.split(re32);
+ str16.split(re32);
+ 'ohggba'.replace(re14, '');
+ 'ohggba'.replace(re15, '');
+ 'puvyq p1 svefg sylbhg pybfrq'.replace(re14, '');
+ 'puvyq p1 svefg sylbhg pybfrq'.replace(re15, '');
+ 'pvgvrf'.replace(re14, '');
+ 'pvgvrf'.replace(re15, '');
+ 'pybfrq'.replace(re14, '');
+ 'pybfrq'.replace(re15, '');
+ 'qry'.replace(re14, '');
+ 'qry'.replace(re15, '');
+ 'uqy_zba'.replace(re14, '');
+ 'uqy_zba'.replace(re15, '');
+ s77[i].replace(re33, '');
+ s78[i].replace(/%3P/g, '');
+ s78[i].replace(/%3R/g, '');
+ s78[i].replace(/%3q/g, '');
+ s78[i].replace(re35, '');
+ 'yvaxyvfg16'.replace(re14, '');
+ 'yvaxyvfg16'.replace(re15, '');
+ 'zvahf'.replace(re14, '');
+ 'zvahf'.replace(re15, '');
+ 'bcra'.replace(re14, '');
+ 'bcra'.replace(re15, '');
+ 'cnerag puebzr5 fvatyr1 ps NU'.replace(re14, '');
+ 'cnerag puebzr5 fvatyr1 ps NU'.replace(re15, '');
+ 'cynlre'.replace(re14, '');
+ 'cynlre'.replace(re15, '');
+ 'cyhf'.replace(re14, '');
+ 'cyhf'.replace(re15, '');
+ 'cb_uqy'.replace(re14, '');
+ 'cb_uqy'.replace(re15, '');
+ 'hyJVzt'.replace(re14, '');
+ 'hyJVzt'.replace(re15, '');
+ re8.exec('144631658.0.10.1231363638');
+ re8.exec('144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.965867047679498800.1231363638.1231363638.1231363638.1');
+ re8.exec('4413268q3660');
+ re8.exec('4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n');
+ re8.exec('SbeprqRkcvengvba=633669321699093060');
+ re8.exec('VC=74.125.75.20');
+ re8.exec(str19);
+ re8.exec(str20);
+ re8.exec('AFP_zp_tfwsbrg-aowb_80=4413268q3660');
+ re8.exec('FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n');
+ re8.exec('__hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1');
+ re8.exec('__hgzo=144631658.0.10.1231363638');
+ re8.exec('__hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re34.exec(str15);
+ re34.exec(str16);
+ }
+ }
+ var re36 = /uers|fep|fryrpgrq/;
+ var re37 = /\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g;
+ var re38 = /^(\w+|\*)$/;
+ var str21 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var str22 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; __hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1; __hgzo=144631658.0.10.1231367822; __hgzp=144631658; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str23 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367803797&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367803797&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Szrffntvat.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1192552091.1231367807&tn_fvq=1231367807&tn_uvq=1155446857&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str24 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str25 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var str26 = 'hy.ynat-fryrpgbe';
+ var re39 = /\\/g;
+ var re40 = / /g;
+ var re41 = /\/\xc4\/t/;
+ var re42 = /\/\xd6\/t/;
+ var re43 = /\/\xdc\/t/;
+ var re44 = /\/\xdf\/t/;
+ var re45 = /\/\xe4\/t/;
+ var re46 = /\/\xf6\/t/;
+ var re47 = /\/\xfc\/t/;
+ var re48 = /\W/g;
+ var re49 = /uers|fep|fglyr/;
+ var s79 = computeInputVariants(str21, 16);
+ var s80 = computeInputVariants(str22, 16);
+ var s81 = computeInputVariants(str23, 16);
+ var s82 = computeInputVariants(str26, 16);
+
+ function runBlock4() {
+ for (var i = 0; i < 16; i++) {
+ ''.replace(/\*/g, '');
+ /\bnpgvir\b/.exec('npgvir');
+ /sversbk/i.exec(s15[i]);
+ re36.exec('glcr');
+ /zfvr/i.exec(s15[i]);
+ /bcren/i.exec(s15[i]);
+ }
+ for (var i = 0; i < 15; i++) {
+ s79[i].split(re32);
+ s80[i].split(re32);
+ 'uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz'.replace(re12, '');
+ s81[i].replace(re33, '');
+ 'yv'.replace(re37, '');
+ 'yv'.replace(re18, '');
+ re8.exec('144631658.0.10.1231367822');
+ re8.exec('144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.4127520630321984500.1231367822.1231367822.1231367822.1');
+ re8.exec(str24);
+ re8.exec(str25);
+ re8.exec('__hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1');
+ re8.exec('__hgzo=144631658.0.10.1231367822');
+ re8.exec('__hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re34.exec(s79[i]);
+ re34.exec(s80[i]);
+ /\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g.exec(s82[i]);
+ re13.exec('uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz');
+ re38.exec('yv');
+ }
+ for (var i = 0; i < 14; i++) {
+ ''.replace(re18, '');
+ '9.0 e115'.replace(/(\s+e|\s+o[0-9]+)/, '');
+ 'Funer guvf tnqtrg'.replace(/</g, '');
+ 'Funer guvf tnqtrg'.replace(/>/g, '');
+ 'Funer guvf tnqtrg'.replace(re39, '');
+ 'uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz'.replace(re12, '');
+ 'grnfre'.replace(re40, '');
+ 'grnfre'.replace(re41, '');
+ 'grnfre'.replace(re42, '');
+ 'grnfre'.replace(re43, '');
+ 'grnfre'.replace(re44, '');
+ 'grnfre'.replace(re45, '');
+ 'grnfre'.replace(re46, '');
+ 'grnfre'.replace(re47, '');
+ 'grnfre'.replace(re48, '');
+ re16.exec('znetva-gbc');
+ re16.exec('cbfvgvba');
+ re19.exec('gno1');
+ re9.exec('qz');
+ re9.exec('qg');
+ re9.exec('zbqobk');
+ re9.exec('zbqobkva');
+ re9.exec('zbqgvgyr');
+ re13.exec('uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz');
+ re26.exec('/vt/znvytnqtrg');
+ re49.exec('glcr');
+ }
+ }
+ var re50 = /(?:^|\s+)fryrpgrq(?:\s+|$)/;
+ var re51 = /\&/g;
+ var re52 = /\+/g;
+ var re53 = /\?/g;
+ var re54 = /\t/g;
+ var re55 = /(\$\{nqiHey\})|(\$nqiHey\b)/g;
+ var re56 = /(\$\{cngu\})|(\$cngu\b)/g;
+ function runBlock5() {
+ for (var i = 0; i < 13; i++) {
+ 'purpx'.replace(re14, '');
+ 'purpx'.replace(re15, '');
+ 'pvgl'.replace(re14, '');
+ 'pvgl'.replace(re15, '');
+ 'qrpe fyvqrgrkg'.replace(re14, '');
+ 'qrpe fyvqrgrkg'.replace(re15, '');
+ 'svefg fryrpgrq'.replace(re14, '');
+ 'svefg fryrpgrq'.replace(re15, '');
+ 'uqy_rag'.replace(re14, '');
+ 'uqy_rag'.replace(re15, '');
+ 'vape fyvqrgrkg'.replace(re14, '');
+ 'vape fyvqrgrkg'.replace(re15, '');
+ 'vachggrkg QBZPbageby_cynprubyqre'.replace(re5, '');
+ 'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re14, '');
+ 'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re15, '');
+ 'cb_guz'.replace(re14, '');
+ 'cb_guz'.replace(re15, '');
+ 'fhozvg'.replace(re14, '');
+ 'fhozvg'.replace(re15, '');
+ re50.exec('');
+ /NccyrJroXvg\/([^\s]*)/.exec(s15[i]);
+ /XUGZY/.exec(s15[i]);
+ }
+ for (var i = 0; i < 12; i++) {
+ '${cebg}://${ubfg}${cngu}/${dz}'.replace(/(\$\{cebg\})|(\$cebg\b)/g, '');
+ '1'.replace(re40, '');
+ '1'.replace(re10, '');
+ '1'.replace(re51, '');
+ '1'.replace(re52, '');
+ '1'.replace(re53, '');
+ '1'.replace(re39, '');
+ '1'.replace(re54, '');
+ '9.0 e115'.replace(/^(.*)\..*$/, '');
+ '9.0 e115'.replace(/^.*e(.*)$/, '');
+ '<!-- ${nqiHey} -->'.replace(re55, '');
+ '<fpevcg glcr="grkg/wninfpevcg" fep="${nqiHey}"></fpevcg>'.replace(re55, '');
+ s21[i].replace(/^.*\s+(\S+\s+\S+$)/, '');
+ 'tzk%2Subzrcntr%2Sfgneg%2Sqr%2S'.replace(re30, '');
+ 'tzk'.replace(re30, '');
+ 'uggc://${ubfg}${cngu}/${dz}'.replace(/(\$\{ubfg\})|(\$ubfg\b)/g, '');
+ 'uggc://nqpyvrag.hvzfrei.arg${cngu}/${dz}'.replace(re56, '');
+ 'uggc://nqpyvrag.hvzfrei.arg/wf.at/${dz}'.replace(/(\$\{dz\})|(\$dz\b)/g, '');
+ 'frpgvba'.replace(re29, '');
+ 'frpgvba'.replace(re30, '');
+ 'fvgr'.replace(re29, '');
+ 'fvgr'.replace(re30, '');
+ 'fcrpvny'.replace(re29, '');
+ 'fcrpvny'.replace(re30, '');
+ re36.exec('anzr');
+ /e/.exec('9.0 e115');
+ }
+ }
+ var re57 = /##yv4##/gi;
+ var re58 = /##yv16##/gi;
+ var re59 = /##yv19##/gi;
+ var str27 = '<hy pynff="nqi">##yv4##Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##</hy>';
+ var str28 = '<hy pynff="nqi"><yv vq="YvOYG4" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##</hy>';
+ var str29 = '<hy pynff="nqi"><yv vq="YvOYG4" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.<yv vq="YvOYG16" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg16.cat)">Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##</hy>';
+ var str30 = '<hy pynff="nqi"><yv vq="YvOYG4" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.<yv vq="YvOYG19" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg19.cat)">Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.<yv vq="YvOYG16" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg16.cat)">Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##</hy>';
+ var str31 = '<hy pynff="nqi"><yv vq="YvOYG4" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.<yv vq="YvOYG19" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg19.cat)">Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.<yv vq="YvOYG16" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg16.cat)">Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.<oe> <oe> ##N##Yrnea zber##/N##</hy>';
+ var str32 = '<hy pynff="nqi"><yv vq="YvOYG4" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.<yv vq="YvOYG19" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg19.cat)">Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.<yv vq="YvOYG16" fglyr="onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg16.cat)">Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.<oe> <oe> <n uers="uggc://znvy.yvir.pbz/znvy/nobhg.nfck" gnetrg="_oynax">Yrnea zber##/N##</hy>';
+ var str33 = 'Bar Jvaqbjf Yvir VQ trgf lbh vagb <o>Ubgznvy</o>, <o>Zrffratre</o>, <o>Kobk YVIR</o> \u2014 naq bgure cynprf lbh frr #~#argjbexybtb#~#';
+ var re60 = /(?:^|\s+)bss(?:\s+|$)/;
+ var re61 = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/;
+ var re62 = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;
+ var str34 = '${1}://${2}${3}${4}${5}';
+ var str35 = ' O=6gnyg0g4znrrn&o=3&f=gc; Q=_lyu=K3bQZGSxnT4lZzD3OS9GNmV3ZGLkAQxRpTyxNmRlZmRmAmNkAQLRqTImqNZjOUEgpTjQnJ5xMKtgoN--; SCF=qy';
+ var s83 = computeInputVariants(str27, 11);
+ var s84 = computeInputVariants(str28, 11);
+ var s85 = computeInputVariants(str29, 11);
+ var s86 = computeInputVariants(str30, 11);
+ var s87 = computeInputVariants(str31, 11);
+ var s88 = computeInputVariants(str32, 11);
+ var s89 = computeInputVariants(str33, 11);
+ var s90 = computeInputVariants(str34, 11);
+
+ function runBlock6() {
+ for (var i = 0; i < 11; i++) {
+ s83[i].replace(/##yv0##/gi, '');
+ s83[i].replace(re57, '');
+ s84[i].replace(re58, '');
+ s85[i].replace(re59, '');
+ s86[i].replace(/##\/o##/gi, '');
+ s86[i].replace(/##\/v##/gi, '');
+ s86[i].replace(/##\/h##/gi, '');
+ s86[i].replace(/##o##/gi, '');
+ s86[i].replace(/##oe##/gi, '');
+ s86[i].replace(/##v##/gi, '');
+ s86[i].replace(/##h##/gi, '');
+ s87[i].replace(/##n##/gi, '');
+ s88[i].replace(/##\/n##/gi, '');
+ s89[i].replace(/#~#argjbexybtb#~#/g, '');
+ / Zbovyr\//.exec(s15[i]);
+ /##yv1##/gi.exec(s83[i]);
+ /##yv10##/gi.exec(s84[i]);
+ /##yv11##/gi.exec(s84[i]);
+ /##yv12##/gi.exec(s84[i]);
+ /##yv13##/gi.exec(s84[i]);
+ /##yv14##/gi.exec(s84[i]);
+ /##yv15##/gi.exec(s84[i]);
+ re58.exec(s84[i]);
+ /##yv17##/gi.exec(s85[i]);
+ /##yv18##/gi.exec(s85[i]);
+ re59.exec(s85[i]);
+ /##yv2##/gi.exec(s83[i]);
+ /##yv20##/gi.exec(s86[i]);
+ /##yv21##/gi.exec(s86[i]);
+ /##yv22##/gi.exec(s86[i]);
+ /##yv23##/gi.exec(s86[i]);
+ /##yv3##/gi.exec(s83[i]);
+ re57.exec(s83[i]);
+ /##yv5##/gi.exec(s84[i]);
+ /##yv6##/gi.exec(s84[i]);
+ /##yv7##/gi.exec(s84[i]);
+ /##yv8##/gi.exec(s84[i]);
+ /##yv9##/gi.exec(s84[i]);
+ re8.exec('473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29');
+ re8.exec('SbeprqRkcvengvba=633669325184628362');
+ re8.exec('FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29');
+ /AbxvnA[^\/]*/.exec(s15[i]);
+ }
+ for (var i = 0; i < 10; i++) {
+ ' bss'.replace(/(?:^|\s+)bss(?:\s+|$)/g, '');
+ s90[i].replace(/(\$\{0\})|(\$0\b)/g, '');
+ s90[i].replace(/(\$\{1\})|(\$1\b)/g, '');
+ s90[i].replace(/(\$\{pbzcyrgr\})|(\$pbzcyrgr\b)/g, '');
+ s90[i].replace(/(\$\{sentzrag\})|(\$sentzrag\b)/g, '');
+ s90[i].replace(/(\$\{ubfgcbeg\})|(\$ubfgcbeg\b)/g, '');
+ s90[i].replace(re56, '');
+ s90[i].replace(/(\$\{cebgbpby\})|(\$cebgbpby\b)/g, '');
+ s90[i].replace(/(\$\{dhrel\})|(\$dhrel\b)/g, '');
+ 'nqfvmr'.replace(re29, '');
+ 'nqfvmr'.replace(re30, '');
+ 'uggc://${2}${3}${4}${5}'.replace(/(\$\{2\})|(\$2\b)/g, '');
+ 'uggc://wf.hv-cbegny.qr${3}${4}${5}'.replace(/(\$\{3\})|(\$3\b)/g, '');
+ 'arjf'.replace(re40, '');
+ 'arjf'.replace(re41, '');
+ 'arjf'.replace(re42, '');
+ 'arjf'.replace(re43, '');
+ 'arjf'.replace(re44, '');
+ 'arjf'.replace(re45, '');
+ 'arjf'.replace(re46, '');
+ 'arjf'.replace(re47, '');
+ 'arjf'.replace(re48, '');
+ / PC=i=(\d+)&oe=(.)/.exec(str35);
+ re60.exec(' ');
+ re60.exec(' bss');
+ re60.exec('');
+ re19.exec(' ');
+ re19.exec('svefg ba');
+ re19.exec('ynfg vtaber');
+ re19.exec('ba');
+ re9.exec('scnq so ');
+ re9.exec('zrqvgobk');
+ re9.exec('hsgy');
+ re9.exec('lhv-h');
+ /Fnsnev|Xbadhrebe|XUGZY/gi.exec(s15[i]);
+ re61.exec('uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf');
+ re62.exec('#Ybtva_rznvy');
+ }
+ }
+ var re63 = /\{0\}/g;
+ var str36 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_tfwsbrg-aowb_80=4413268q3660';
+ var str37 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; AFP_zp_tfwsbrg-aowb_80=4413268q3660; __hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1; __hgzo=144631658.0.10.1231364074; __hgzp=144631658; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str38 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231364057761&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364057761&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Ssevraqf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1667363813.1231364061&tn_fvq=1231364061&tn_uvq=1917563877&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str39 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str40 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var s91 = computeInputVariants(str36, 9);
+ var s92 = computeInputVariants(str37, 9);
+ var s93 = computeInputVariants(str38, 9);
+ function runBlock7() {
+ for (var i = 0; i < 9; i++) {
+ '0'.replace(re40, '');
+ '0'.replace(re10, '');
+ '0'.replace(re51, '');
+ '0'.replace(re52, '');
+ '0'.replace(re53, '');
+ '0'.replace(re39, '');
+ '0'.replace(re54, '');
+ 'Lrf'.replace(re40, '');
+ 'Lrf'.replace(re10, '');
+ 'Lrf'.replace(re51, '');
+ 'Lrf'.replace(re52, '');
+ 'Lrf'.replace(re53, '');
+ 'Lrf'.replace(re39, '');
+ 'Lrf'.replace(re54, '');
+ }
+ for (var i = 0; i < 8; i++) {
+ 'Pybfr {0}'.replace(re63, '');
+ 'Bcra {0}'.replace(re63, '');
+ s91[i].split(re32);
+ s92[i].split(re32);
+ 'puvyq p1 svefg gnournqref'.replace(re14, '');
+ 'puvyq p1 svefg gnournqref'.replace(re15, '');
+ 'uqy_fcb'.replace(re14, '');
+ 'uqy_fcb'.replace(re15, '');
+ 'uvag'.replace(re14, '');
+ 'uvag'.replace(re15, '');
+ s93[i].replace(re33, '');
+ 'yvfg'.replace(re14, '');
+ 'yvfg'.replace(re15, '');
+ 'at_bhgre'.replace(re30, '');
+ 'cnerag puebzr5 qbhoyr2 NU'.replace(re14, '');
+ 'cnerag puebzr5 qbhoyr2 NU'.replace(re15, '');
+ 'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re14, '');
+ 'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re15, '');
+ 'cnerag puebzr6 fvatyr1'.replace(re14, '');
+ 'cnerag puebzr6 fvatyr1'.replace(re15, '');
+ 'cb_qrs'.replace(re14, '');
+ 'cb_qrs'.replace(re15, '');
+ 'gnopbagrag'.replace(re14, '');
+ 'gnopbagrag'.replace(re15, '');
+ 'iv_svefg_gvzr'.replace(re30, '');
+ /(^|.)(ronl|qri-ehf3.wbg)(|fgberf|zbgbef|yvirnhpgvbaf|jvxv|rkcerff|punggre).(pbz(|.nh|.pa|.ux|.zl|.ft|.oe|.zk)|pb(.hx|.xe|.am)|pn|qr|se|vg|ay|or|ng|pu|vr|va|rf|cy|cu|fr)$/i.exec('cntrf.ronl.pbz');
+ re8.exec('144631658.0.10.1231364074');
+ re8.exec('144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.2294274870215848400.1231364074.1231364074.1231364074.1');
+ re8.exec('4413241q3660');
+ re8.exec('SbeprqRkcvengvba=633669357391353591');
+ re8.exec(str39);
+ re8.exec(str40);
+ re8.exec('AFP_zp_kkk-gdzogv_80=4413241q3660');
+ re8.exec('FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7');
+ re8.exec('__hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1');
+ re8.exec('__hgzo=144631658.0.10.1231364074');
+ re8.exec('__hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7');
+ re34.exec(s91[i]);
+ re34.exec(s92[i]);
+ }
+ }
+ var re64 = /\b[a-z]/g;
+ var re65 = /^uggc:\/\//;
+ var re66 = /(?:^|\s+)qvfnoyrq(?:\s+|$)/;
+ var str41 = 'uggc://cebsvyr.zlfcnpr.pbz/Zbqhyrf/Nccyvpngvbaf/Cntrf/Pnainf.nfck';
+ function runBlock8() {
+ for (var i = 0; i < 7; i++) {
+ s21[i].match(/\d+/g);
+ 'nsgre'.replace(re64, '');
+ 'orsber'.replace(re64, '');
+ 'obggbz'.replace(re64, '');
+ 'ohvygva_jrngure.kzy'.replace(re65, '');
+ 'ohggba'.replace(re37, '');
+ 'ohggba'.replace(re18, '');
+ 'qngrgvzr.kzy'.replace(re65, '');
+ 'uggc://eff.paa.pbz/eff/paa_gbcfgbevrf.eff'.replace(re65, '');
+ 'vachg'.replace(re37, '');
+ 'vachg'.replace(re18, '');
+ 'vafvqr'.replace(re64, '');
+ 'cbvagre'.replace(re27, '');
+ 'cbfvgvba'.replace(/[A-Z]/g, '');
+ 'gbc'.replace(re27, '');
+ 'gbc'.replace(re64, '');
+ 'hy'.replace(re37, '');
+ 'hy'.replace(re18, '');
+ str26.replace(re37, '');
+ str26.replace(re18, '');
+ 'lbhghor_vtbbtyr/i2/lbhghor.kzy'.replace(re65, '');
+ 'm-vaqrk'.replace(re27, '');
+ /#([\w-]+)/.exec(str26);
+ re16.exec('urvtug');
+ re16.exec('znetvaGbc');
+ re16.exec('jvqgu');
+ re19.exec('gno0 svefg ba');
+ re19.exec('gno0 ba');
+ re19.exec('gno4 ynfg');
+ re19.exec('gno4');
+ re19.exec('gno5');
+ re19.exec('gno6');
+ re19.exec('gno7');
+ re19.exec('gno8');
+ /NqborNVE\/([^\s]*)/.exec(s15[i]);
+ /NccyrJroXvg\/([^ ]*)/.exec(s15[i]);
+ /XUGZY/gi.exec(s15[i]);
+ /^(?:obql|ugzy)$/i.exec('YV');
+ re38.exec('ohggba');
+ re38.exec('vachg');
+ re38.exec('hy');
+ re38.exec(str26);
+ /^(\w+|\*)/.exec(str26);
+ /znp|jva|yvahk/i.exec('Jva32');
+ /eton?\([\d\s,]+\)/.exec('fgngvp');
+ }
+ for (var i = 0; i < 6; i++) {
+ ''.replace(/\r/g, '');
+ '/'.replace(re40, '');
+ '/'.replace(re10, '');
+ '/'.replace(re51, '');
+ '/'.replace(re52, '');
+ '/'.replace(re53, '');
+ '/'.replace(re39, '');
+ '/'.replace(re54, '');
+ 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/{0}?[NDO]&{1}&{2}&[NDR]'.replace(re63, '');
+ str41.replace(re12, '');
+ 'uggc://jjj.snprobbx.pbz/fepu.cuc'.replace(re23, '');
+ 'freivpr'.replace(re40, '');
+ 'freivpr'.replace(re41, '');
+ 'freivpr'.replace(re42, '');
+ 'freivpr'.replace(re43, '');
+ 'freivpr'.replace(re44, '');
+ 'freivpr'.replace(re45, '');
+ 'freivpr'.replace(re46, '');
+ 'freivpr'.replace(re47, '');
+ 'freivpr'.replace(re48, '');
+ /((ZFVR\s+([6-9]|\d\d)\.))/.exec(s15[i]);
+ re66.exec('');
+ re50.exec('fryrpgrq');
+ re8.exec('8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn');
+ re8.exec('SbeprqRkcvengvba=633669340386893867');
+ re8.exec('VC=74.125.75.17');
+ re8.exec('FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn');
+ /Xbadhrebe|Fnsnev|XUGZY/.exec(s15[i]);
+ re13.exec(str41);
+ re49.exec('unfsbphf');
+ }
+ }
+ var re67 = /zrah_byq/g;
+ var str42 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var str43 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; __hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1; __hgzo=144631658.0.10.1231364380; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str44 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_vzntrf_wf&qg=1231364373088&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364373088&punaary=svz_zlfcnpr_hfre-ivrj-pbzzragf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Spbzzrag.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1158737789.1231364375&tn_fvq=1231364375&tn_uvq=415520832&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str45 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str46 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var re68 = /^([#.]?)((?:[\w\u0128-\uffff*_-]|\\.)*)/;
+ var re69 = /\{1\}/g;
+ var re70 = /\s+/;
+ var re71 = /(\$\{4\})|(\$4\b)/g;
+ var re72 = /(\$\{5\})|(\$5\b)/g;
+ var re73 = /\{2\}/g;
+ var re74 = /[^+>] [^+>]/;
+ var re75 = /\bucpyv\s*=\s*([^;]*)/i;
+ var re76 = /\bucuvqr\s*=\s*([^;]*)/i;
+ var re77 = /\bucfie\s*=\s*([^;]*)/i;
+ var re78 = /\bhfucjrn\s*=\s*([^;]*)/i;
+ var re79 = /\bmvc\s*=\s*([^;]*)/i;
+ var re80 = /^((?:[\w\u0128-\uffff*_-]|\\.)+)(#)((?:[\w\u0128-\uffff*_-]|\\.)+)/;
+ var re81 = /^([>+~])\s*(\w*)/i;
+ var re82 = /^>\s*((?:[\w\u0128-\uffff*_-]|\\.)+)/;
+ var re83 = /^[\s[]?shapgvba/;
+ var re84 = /v\/g.tvs#(.*)/i;
+ var str47 = '#Zbq-Vasb-Vasb-WninFpevcgUvag';
+ var str48 = ',n.svryqOgaPnapry';
+ var str49 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_kkk-gdzogv_80=4413241q3660';
+ var str50 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; AFP_zp_kkk-gdzogv_80=4413241q3660; AFP_zp_kkk-aowb_80=4413235p3660; __hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1; __hgzo=144631658.0.10.1231367708; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str51 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367691141&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367691141&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sjjj.zlfcnpr.pbz%2S&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=320757904.1231367694&tn_fvq=1231367694&tn_uvq=1758792003&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str52 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N38%3N42%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=1024k768&p=24&x=L&oj=994&ou=634&uc=A&{2}&[NDR]';
+ var str53 = 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq qbhoyr2 ps';
+ var str54 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str55 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var str56 = 'ne;ng;nh;or;oe;pn;pu;py;pa;qr;qx;rf;sv;se;to;ux;vq;vr;va;vg;wc;xe;zk;zl;ay;ab;am;cu;cy;cg;eh;fr;ft;gu;ge;gj;mn;';
+ var str57 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886&GHVQ=1';
+ var str58 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886';
+ var str59 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1';
+ var str60 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF';
+ var str61 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/29/4RQP4969777N048NPS4RRR3PO2S7S.wct';
+ var str62 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/OQ/63NP9O94NS5OQP1249Q9S1ROP7NS3.wct';
+ var str63 = 'zbmvyyn/5.0 (jvaqbjf; h; jvaqbjf ag 5.1; ra-hf) nccyrjroxvg/528.9 (xugzy, yvxr trpxb) puebzr/2.0.157.0 fnsnev/528.9';
+ var s94 = computeInputVariants(str42, 5);
+ var s95 = computeInputVariants(str43, 5);
+ var s96 = computeInputVariants(str44, 5);
+ var s97 = computeInputVariants(str47, 5);
+ var s98 = computeInputVariants(str48, 5);
+ var s99 = computeInputVariants(str49, 5);
+ var s100 = computeInputVariants(str50, 5);
+ var s101 = computeInputVariants(str51, 5);
+ var s102 = computeInputVariants(str52, 5);
+ var s103 = computeInputVariants(str53, 5);
+
+ function runBlock9() {
+ for (var i = 0; i < 5; i++) {
+ s94[i].split(re32);
+ s95[i].split(re32);
+ 'svz_zlfcnpr_hfre-ivrj-pbzzragf,svz_zlfcnpr_havgrq-fgngrf'.split(re20);
+ s96[i].replace(re33, '');
+ 'zrah_arj zrah_arj_gbttyr zrah_gbttyr'.replace(re67, '');
+ 'zrah_byq zrah_byq_gbttyr zrah_gbttyr'.replace(re67, '');
+ re8.exec('102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98');
+ re8.exec('144631658.0.10.1231364380');
+ re8.exec('144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.3931862196947939300.1231364380.1231364380.1231364380.1');
+ re8.exec('441326q33660');
+ re8.exec('SbeprqRkcvengvba=633669341278771470');
+ re8.exec(str45);
+ re8.exec(str46);
+ re8.exec('AFP_zp_dfctwzssrwh-aowb_80=441326q33660');
+ re8.exec('FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98');
+ re8.exec('__hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1');
+ re8.exec('__hgzo=144631658.0.10.1231364380');
+ re8.exec('__hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ }
+ for (var i = 0; i < 4; i++) {
+ ' yvfg1'.replace(re14, '');
+ ' yvfg1'.replace(re15, '');
+ ' yvfg2'.replace(re14, '');
+ ' yvfg2'.replace(re15, '');
+ ' frneputebhc1'.replace(re14, '');
+ ' frneputebhc1'.replace(re15, '');
+ s97[i].replace(re68, '');
+ s97[i].replace(re18, '');
+ ''.replace(/&/g, '');
+ ''.replace(re35, '');
+ '(..-{0})(\|(\d+)|)'.replace(re63, '');
+ s98[i].replace(re18, '');
+ '//vzt.jro.qr/vij/FC/${cngu}/${anzr}/${inyhr}?gf=${abj}'.replace(re56, '');
+ '//vzt.jro.qr/vij/FC/tzk_uc/${anzr}/${inyhr}?gf=${abj}'.replace(/(\$\{anzr\})|(\$anzr\b)/g, '');
+ '<fcna pynff="urnq"><o>Jvaqbjf Yvir Ubgznvy</o></fcna><fcna pynff="zft">{1}</fcna>'.replace(re69, '');
+ '<fcna pynff="urnq"><o>{0}</o></fcna><fcna pynff="zft">{1}</fcna>'.replace(re63, '');
+ '<fcna pynff="fvtahc"><n uers=uggc://jjj.ubgznvy.pbz><o>{1}</o></n></fcna>'.replace(re69, '');
+ '<fcna pynff="fvtahc"><n uers={0}><o>{1}</o></n></fcna>'.replace(re63, '');
+ 'Vzntrf'.replace(re15, '');
+ 'ZFA'.replace(re15, '');
+ 'Zncf'.replace(re15, '');
+ 'Zbq-Vasb-Vasb-WninFpevcgUvag'.replace(re39, '');
+ 'Arjf'.replace(re15, '');
+ s99[i].split(re32);
+ s100[i].split(re32);
+ 'Ivqrb'.replace(re15, '');
+ 'Jro'.replace(re15, '');
+ 'n'.replace(re39, '');
+ 'nwnkFgneg'.split(re70);
+ 'nwnkFgbc'.split(re70);
+ 'ovaq'.replace(re14, '');
+ 'ovaq'.replace(re15, '');
+ 'oevatf lbh zber. Zber fcnpr (5TO), zber frphevgl, fgvyy serr.'.replace(re63, '');
+ 'puvyq p1 svefg qrpx'.replace(re14, '');
+ 'puvyq p1 svefg qrpx'.replace(re15, '');
+ 'puvyq p1 svefg qbhoyr2'.replace(re14, '');
+ 'puvyq p1 svefg qbhoyr2'.replace(re15, '');
+ 'puvyq p2 ynfg'.replace(re14, '');
+ 'puvyq p2 ynfg'.replace(re15, '');
+ 'puvyq p2'.replace(re14, '');
+ 'puvyq p2'.replace(re15, '');
+ 'puvyq p3'.replace(re14, '');
+ 'puvyq p3'.replace(re15, '');
+ 'puvyq p4 ynfg'.replace(re14, '');
+ 'puvyq p4 ynfg'.replace(re15, '');
+ 'pbclevtug'.replace(re14, '');
+ 'pbclevtug'.replace(re15, '');
+ 'qZFAZR_1'.replace(re14, '');
+ 'qZFAZR_1'.replace(re15, '');
+ 'qbhoyr2 ps'.replace(re14, '');
+ 'qbhoyr2 ps'.replace(re15, '');
+ 'qbhoyr2'.replace(re14, '');
+ 'qbhoyr2'.replace(re15, '');
+ 'uqy_arj'.replace(re14, '');
+ 'uqy_arj'.replace(re15, '');
+ 'uc_fubccvatobk'.replace(re30, '');
+ 'ugzy%2Rvq'.replace(re29, '');
+ 'ugzy%2Rvq'.replace(re30, '');
+ s101[i].replace(re33, '');
+ 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${4}${5}'.replace(re71, '');
+ 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${5}'.replace(re72, '');
+ s102[i].replace(re73, '');
+ 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&{1}&{2}&[NDR]'.replace(re69, '');
+ 'vztZFSG'.replace(re14, '');
+ 'vztZFSG'.replace(re15, '');
+ 'zfasbbg1 ps'.replace(re14, '');
+ 'zfasbbg1 ps'.replace(re15, '');
+ s103[i].replace(re14, '');
+ s103[i].replace(re15, '');
+ 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re14, '');
+ 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re15, '');
+ 'cevznel'.replace(re14, '');
+ 'cevznel'.replace(re15, '');
+ 'erpgnatyr'.replace(re30, '');
+ 'frpbaqnel'.replace(re14, '');
+ 'frpbaqnel'.replace(re15, '');
+ 'haybnq'.split(re70);
+ '{0}{1}1'.replace(re63, '');
+ '|{1}1'.replace(re69, '');
+ /(..-HF)(\|(\d+)|)/i.exec('xb-xe,ra-va,gu-gu');
+ re4.exec('/ZlFcnprNccf/NccPnainf,45000012');
+ re8.exec('144631658.0.10.1231367708');
+ re8.exec('144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.2770915348920628700.1231367708.1231367708.1231367708.1');
+ re8.exec('4413235p3660');
+ re8.exec('441327q73660');
+ re8.exec('9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473');
+ re8.exec('SbeprqRkcvengvba=633669350559478880');
+ re8.exec(str54);
+ re8.exec(str55);
+ re8.exec('AFP_zp_dfctwzs-aowb_80=441327q73660');
+ re8.exec('AFP_zp_kkk-aowb_80=4413235p3660');
+ re8.exec('FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473');
+ re8.exec('__hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1');
+ re8.exec('__hgzo=144631658.0.10.1231367708');
+ re8.exec('__hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re34.exec(s99[i]);
+ re34.exec(s100[i]);
+ /ZFVR\s+5[.]01/.exec(s15[i]);
+ /HF(?=;)/i.exec(str56);
+ re74.exec(s97[i]);
+ re28.exec('svefg npgvir svefgNpgvir');
+ re28.exec('ynfg');
+ /\bp:(..)/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF');
+ re75.exec(str57);
+ re75.exec(str58);
+ re76.exec(str57);
+ re76.exec(str58);
+ re77.exec(str57);
+ re77.exec(str58);
+ /\bhfucce\s*=\s*([^;]*)/i.exec(str59);
+ re78.exec(str57);
+ re78.exec(str58);
+ /\bjci\s*=\s*([^;]*)/i.exec(str59);
+ re79.exec(str58);
+ re79.exec(str60);
+ re79.exec(str59);
+ /\|p:([a-z]{2})/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1');
+ re80.exec(s97[i]);
+ re61.exec('cebgbglcr.wf');
+ re68.exec(s97[i]);
+ re81.exec(s97[i]);
+ re82.exec(s97[i]);
+ /^Fubpxjnir Synfu (\d)/.exec(s21[i]);
+ /^Fubpxjnir Synfu (\d+)/.exec(s21[i]);
+ re83.exec('[bowrpg tybony]');
+ re62.exec(s97[i]);
+ re84.exec(str61);
+ re84.exec(str62);
+ /jroxvg/.exec(str63);
+ }
+ }
+ var re85 = /eaq_zbqobkva/;
+ var str64 = '1231365729213';
+ var str65 = '74.125.75.3-1057165600.29978900';
+ var str66 = '74.125.75.3-1057165600.29978900.1231365730214';
+ var str67 = 'Frnepu%20Zvpebfbsg.pbz';
+ var str68 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var str69 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; __hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1; __hgzo=144631658.0.10.1231365779; __hgzp=144631658; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str70 = 'I=3%26THVQ=757q3ss871q44o7o805n8113n5p72q52';
+ var str71 = 'I=3&THVQ=757q3ss871q44o7o805n8113n5p72q52';
+ var str72 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365765292&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365765292&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sohyyrgvaf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1579793869.1231365768&tn_fvq=1231365768&tn_uvq=2056210897&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str73 = 'frnepu.zvpebfbsg.pbz';
+ var str74 = 'frnepu.zvpebfbsg.pbz/';
+ var str75 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str76 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ function runBlock10() {
+ for (var i = 0; i < 3; i++) {
+ '%3Szxg=ra-HF'.replace(re39, '');
+ '-8'.replace(re40, '');
+ '-8'.replace(re10, '');
+ '-8'.replace(re51, '');
+ '-8'.replace(re52, '');
+ '-8'.replace(re53, '');
+ '-8'.replace(re39, '');
+ '-8'.replace(re54, '');
+ '1.5'.replace(re40, '');
+ '1.5'.replace(re10, '');
+ '1.5'.replace(re51, '');
+ '1.5'.replace(re52, '');
+ '1.5'.replace(re53, '');
+ '1.5'.replace(re39, '');
+ '1.5'.replace(re54, '');
+ '1024k768'.replace(re40, '');
+ '1024k768'.replace(re10, '');
+ '1024k768'.replace(re51, '');
+ '1024k768'.replace(re52, '');
+ '1024k768'.replace(re53, '');
+ '1024k768'.replace(re39, '');
+ '1024k768'.replace(re54, '');
+ str64.replace(re40, '');
+ str64.replace(re10, '');
+ str64.replace(re51, '');
+ str64.replace(re52, '');
+ str64.replace(re53, '');
+ str64.replace(re39, '');
+ str64.replace(re54, '');
+ '14'.replace(re40, '');
+ '14'.replace(re10, '');
+ '14'.replace(re51, '');
+ '14'.replace(re52, '');
+ '14'.replace(re53, '');
+ '14'.replace(re39, '');
+ '14'.replace(re54, '');
+ '24'.replace(re40, '');
+ '24'.replace(re10, '');
+ '24'.replace(re51, '');
+ '24'.replace(re52, '');
+ '24'.replace(re53, '');
+ '24'.replace(re39, '');
+ '24'.replace(re54, '');
+ str65.replace(re40, '');
+ str65.replace(re10, '');
+ str65.replace(re51, '');
+ str65.replace(re52, '');
+ str65.replace(re53, '');
+ str65.replace(re39, '');
+ str65.replace(re54, '');
+ str66.replace(re40, '');
+ str66.replace(re10, '');
+ str66.replace(re51, '');
+ str66.replace(re52, '');
+ str66.replace(re53, '');
+ str66.replace(re39, '');
+ str66.replace(re54, '');
+ '9.0'.replace(re40, '');
+ '9.0'.replace(re10, '');
+ '9.0'.replace(re51, '');
+ '9.0'.replace(re52, '');
+ '9.0'.replace(re53, '');
+ '9.0'.replace(re39, '');
+ '9.0'.replace(re54, '');
+ '994k634'.replace(re40, '');
+ '994k634'.replace(re10, '');
+ '994k634'.replace(re51, '');
+ '994k634'.replace(re52, '');
+ '994k634'.replace(re53, '');
+ '994k634'.replace(re39, '');
+ '994k634'.replace(re54, '');
+ '?zxg=ra-HF'.replace(re40, '');
+ '?zxg=ra-HF'.replace(re10, '');
+ '?zxg=ra-HF'.replace(re51, '');
+ '?zxg=ra-HF'.replace(re52, '');
+ '?zxg=ra-HF'.replace(re53, '');
+ '?zxg=ra-HF'.replace(re54, '');
+ 'PAA.pbz'.replace(re25, '');
+ 'PAA.pbz'.replace(re12, '');
+ 'PAA.pbz'.replace(re39, '');
+ 'Qngr & Gvzr'.replace(re25, '');
+ 'Qngr & Gvzr'.replace(re12, '');
+ 'Qngr & Gvzr'.replace(re39, '');
+ 'Frnepu Zvpebfbsg.pbz'.replace(re40, '');
+ 'Frnepu Zvpebfbsg.pbz'.replace(re54, '');
+ str67.replace(re10, '');
+ str67.replace(re51, '');
+ str67.replace(re52, '');
+ str67.replace(re53, '');
+ str67.replace(re39, '');
+ str68.split(re32);
+ str69.split(re32);
+ str70.replace(re52, '');
+ str70.replace(re53, '');
+ str70.replace(re39, '');
+ str71.replace(re40, '');
+ str71.replace(re10, '');
+ str71.replace(re51, '');
+ str71.replace(re54, '');
+ 'Jrngure'.replace(re25, '');
+ 'Jrngure'.replace(re12, '');
+ 'Jrngure'.replace(re39, '');
+ 'LbhGhor'.replace(re25, '');
+ 'LbhGhor'.replace(re12, '');
+ 'LbhGhor'.replace(re39, '');
+ str72.replace(re33, '');
+ 'erzbgr_vsenzr_1'.replace(/^erzbgr_vsenzr_/, '');
+ str73.replace(re40, '');
+ str73.replace(re10, '');
+ str73.replace(re51, '');
+ str73.replace(re52, '');
+ str73.replace(re53, '');
+ str73.replace(re39, '');
+ str73.replace(re54, '');
+ str74.replace(re40, '');
+ str74.replace(re10, '');
+ str74.replace(re51, '');
+ str74.replace(re52, '');
+ str74.replace(re53, '');
+ str74.replace(re39, '');
+ str74.replace(re54, '');
+ 'lhv-h'.replace(/\-/g, '');
+ re9.exec('p');
+ re9.exec('qz p');
+ re9.exec('zbqynory');
+ re9.exec('lhv-h svefg');
+ re8.exec('144631658.0.10.1231365779');
+ re8.exec('144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.1877536177953918500.1231365779.1231365779.1231365779.1');
+ re8.exec(str75);
+ re8.exec(str76);
+ re8.exec('__hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1');
+ re8.exec('__hgzo=144631658.0.10.1231365779');
+ re8.exec('__hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re34.exec(str68);
+ re34.exec(str69);
+ /^$/.exec('');
+ re31.exec('qr');
+ /^znk\d+$/.exec('');
+ /^zva\d+$/.exec('');
+ /^erfgber$/.exec('');
+ re85.exec('zbqobkva zbqobk_abcnqqvat ');
+ re85.exec('zbqgvgyr');
+ re85.exec('eaq_zbqobkva ');
+ re85.exec('eaq_zbqgvgyr ');
+ /frpgvba\d+_pbagragf/.exec('obggbz_ani');
+ }
+ }
+ var re86 = /;\s*/;
+ var re87 = /(\$\{inyhr\})|(\$inyhr\b)/g;
+ var re88 = /(\$\{abj\})|(\$abj\b)/g;
+ var re89 = /\s+$/;
+ var re90 = /^\s+/;
+ var re91 = /(\\\"|\x00-|\x1f|\x7f-|\x9f|\u00ad|\u0600-|\u0604|\u070f|\u17b4|\u17b5|\u200c-|\u200f|\u2028-|\u202f|\u2060-|\u206f|\ufeff|\ufff0-|\uffff)/g;
+ var re92 = /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/;
+ var re93 = /^([:.#]*)((?:[\w\u0128-\uffff*_-]|\\.)+)/;
+ var re94 = /^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/;
+ var str77 = '#fubhgobk .pybfr';
+ var str78 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzssrwh-aowb_80=441326q33660';
+ var str79 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; AFP_zp_dfctwzssrwh-aowb_80=441326q33660; __hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1; __hgzo=144631658.0.10.1231365869; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str80 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=441327q73660';
+ var str81 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; AFP_zp_dfctwzs-aowb_80=441327q73660; __hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1; __hgzo=144631658.0.10.1231367054; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str82 = '[glcr=fhozvg]';
+ var str83 = 'n.svryqOga,n.svryqOgaPnapry';
+ var str84 = 'n.svryqOgaPnapry';
+ var str85 = 'oyvpxchaxg';
+ var str86 = 'qvi.bow-nppbeqvba qg';
+ var str87 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_nccf_wf&qg=1231367052227&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367052227&punaary=svz_zlfcnpr_nccf-pnainf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2SZbqhyrf%2SNccyvpngvbaf%2SCntrf%2SPnainf.nfck&nq_glcr=grkg&rvq=6083027&rn=0&sez=1&tn_ivq=716357910.1231367056&tn_fvq=1231367056&tn_uvq=1387206491&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str88 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365851658&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365851658&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyrrqvg.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1979828129.1231365855&tn_fvq=1231365855&tn_uvq=2085229649&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';
+ var str89 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N12%3N47%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=0k0&p=43835816&x=A&oj=994&ou=634&uc=A&{2}&[NDR]';
+ var str90 = 'zrgn[anzr=nwnkHey]';
+ var str91 = 'anpuevpugra';
+ var str92 = 'b oS={\'oT\':1.1};x $8n(B){z(B!=o9)};x $S(B){O(!$8n(B))z A;O(B.4L)z\'T\';b S=7t B;O(S==\'2P\'&&B.p4){23(B.7f){12 1:z\'T\';12 3:z/\S/.2g(B.8M)?\'ox\':\'oh\'}}O(S==\'2P\'||S==\'x\'){23(B.nE){12 2V:z\'1O\';12 7I:z\'5a\';12 18:z\'4B\'}O(7t B.I==\'4F\'){O(B.3u)z\'pG\';O(B.8e)z\'1p\'}}z S};x $2p(){b 4E={};Z(b v=0;v<1p.I;v++){Z(b X 1o 1p[v]){b nc=1p[v][X];b 6E=4E[X];O(6E&&$S(nc)==\'2P\'&&$S(6E)==\'2P\')4E[X]=$2p(6E,nc);17 4E[X]=nc}}z 4E};b $E=7p.E=x(){b 1d=1p;O(!1d[1])1d=[p,1d[0]];Z(b X 1o 1d[1])1d[0][X]=1d[1][X];z 1d[0]};b $4D=7p.pJ=x(){Z(b v=0,y=1p.I;v<y;v++){1p[v].E=x(1J){Z(b 1I 1o 1J){O(!p.1Y[1I])p.1Y[1I]=1J[1I];O(!p[1I])p[1I]=$4D.6C(1I)}}}};$4D.6C=x(1I){z x(L){z p.1Y[1I].3H(L,2V.1Y.nV.1F(1p,1))}};$4D(7F,2V,6J,nb);b 3l=x(B){B=B||{};B.E=$E;z B};b pK=Y 3l(H);b pZ=Y 3l(C);C.6f=C.35(\'6f\')[0];x $2O(B){z!!(B||B===0)};x $5S(B,n8){z $8n(B)?B:n8};x $7K(3c,1m){z 1q.na(1q.7K()*(1m-3c+1)+3c)};x $3N(){z Y 97().os()};x $4M(1U){pv(1U);pa(1U);z 1S};H.43=!!(C.5Z);O(H.nB)H.31=H[H.7q?\'py\':\'nL\']=1r;17 O(C.9N&&!C.om&&!oy.oZ)H.pF=H.4Z=H[H.43?\'pt\':\'65\']=1r;17 O(C.po!=1S)H.7J=1r;O(7t 5B==\'o9\'){b 5B=x(){};O(H.4Z)C.nd("pW");5B.1Y=(H.4Z)?H["[[oN.1Y]]"]:{}}5B.1Y.4L=1r;O(H.nL)5s{C.oX("pp",A,1r)}4K(r){};b 18=x(1X){b 63=x(){z(1p[0]!==1S&&p.1w&&$S(p.1w)==\'x\')?p.1w.3H(p,1p):p};$E(63,p);63.1Y=1X;63.nE=18;z 63};18.1z=x(){};18.1Y={E:x(1X){b 7x=Y p(1S);Z(b X 1o 1X){b nC=7x[X];7x[X]=18.nY(nC,1X[X])}z Y 18(7x)},3d:x(){Z(b v=0,y=1p.I;v<y;v++)$E(p.1Y,1p[v])}};18.nY=x(2b,2n){O(2b&&2b!=2n){b S=$S(2n);O(S!=$S(2b))z 2n;23(S){12\'x\':b 7R=x(){p.1e=1p.8e.1e;z 2n.3H(p,1p)};7R.1e=2b;z 7R;12\'2P\':z $2p(2b,2n)}}z 2n};b 8o=Y 18({oQ:x(J){p.4w=p.4w||[];p.4w.1x(J);z p},7g:x(){O(p.4w&&p.4w.I)p.4w.9J().2x(10,p)},oP:x(){p.4w=[]}});b 2d=Y 18({1V:x(S,J){O(J!=18.1z){p.$19=p.$19||{};p.$19[S]=p.$19[S]||[];p.$19[S].5j(J)}z p},1v:x(S,1d,2x){O(p.$19&&p.$19[S]){p.$19[S].1b(x(J){J.3n({\'L\':p,\'2x\':2x,\'1p\':1d})()},p)}z p},3M:x(S,J){O(p.$19&&p.$19[S])p.$19[S].2U(J);z p}});b 4v=Y 18({2H:x(){p.P=$2p.3H(1S,[p.P].E(1p));O(!p.1V)z p;Z(b 3O 1o p.P){O($S(p.P[3O]==\'x\')&&3O.2g(/^5P[N-M]/))p.1V(3O,p.P[3O])}z p}});2V.E({7y:x(J,L){Z(b v=0,w=p.I;v<w;v++)J.1F(L,p[v],v,p)},3s:x(J,L){b 54=[];Z(b v=0,w=p.I;v<w;v++){O(J.1F(L,p[v],v,p))54.1x(p[v])}z 54},2X:x(J,L){b 54=[];Z(b v=0,w=p.I;v<w;v++)54[v]=J.1F(L,p[v],v,p);z 54},4i:x(J,L){Z(b v=0,w=p.I;v<w;v++){O(!J.1F(L,p[v],v,p))z A}z 1r},ob:x(J,L){Z(b v=0,w=p.I;v<w;v++){O(J.1F(L,p[v],v,p))z 1r}z A},3F:x(3u,15){b 3A=p.I;Z(b v=(15<0)?1q.1m(0,3A+15):15||0;v<3A;v++){O(p[v]===3u)z v}z-1},8z:x(1u,I){1u=1u||0;O(1u<0)1u=p.I+1u;I=I||(p.I-1u);b 89=[];Z(b v=0;v<I;v++)89[v]=p[1u++];z 89},2U:x(3u){b v=0;b 3A=p.I;6L(v<3A){O(p[v]===3u){p.6l(v,1);3A--}17{v++}}z p},1y:x(3u,15){z p.3F(3u,15)!=-1},oz:x(1C){b B={},I=1q.3c(p.I,1C.I);Z(b v=0;v<I;v++)B[1C[v]]=p[v];z B},E:x(1O){Z(b v=0,w=1O.I;v<w;v++)p.1x(1O[v]);z p},2p:x(1O){Z(b v=0,y=1O.I;v<y;v++)p.5j(1O[v]);z p},5j:x(3u){O(!p.1y(3u))p.1x(3u);z p},oc:x(){z p[$7K(0,p.I-1)]||A},7L:x(){z p[p.I-1]||A}});2V.1Y.1b=2V.1Y.7y;2V.1Y.2g=2V.1Y.1y;x $N(1O){z 2V.8z(1O)};x $1b(3J,J,L){O(3J&&7t 3J.I==\'4F\'&&$S(3J)!=\'2P\')2V.7y(3J,J,L);17 Z(b 1j 1o 3J)J.1F(L||3J,3J[1j],1j)};6J.E({2g:x(6b,2F){z(($S(6b)==\'2R\')?Y 7I(6b,2F):6b).2g(p)},3p:x(){z 5K(p,10)},o4:x(){z 69(p)},7A:x(){z p.3y(/-\D/t,x(2G){z 2G.7G(1).nW()})},9b:x(){z p.3y(/\w[N-M]/t,x(2G){z(2G.7G(0)+\'-\'+2G.7G(1).5O())})},8V:x(){z p.3y(/\b[n-m]/t,x(2G){z 2G.nW()})},5L:x(){z p.3y(/^\s+|\s+$/t,\'\')},7j:x(){z p.3y(/\s{2,}/t,\' \').5L()},5V:x(1O){b 1i=p.2G(/\d{1,3}/t);z(1i)?1i.5V(1O):A},5U:x(1O){b 3P=p.2G(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);z(3P)?3P.nV(1).5U(1O):A},1y:x(2R,f){z(f)?(f+p+f).3F(f+2R+f)>-1:p.3F(2R)>-1},nX:x(){z p.3y(/([.*+?^${}()|[\]\/\\])/t,\'\\$1\')}});2V.E({5V:x(1O){O(p.I<3)z A;O(p.I==4&&p[3]==0&&!1O)z\'p5\';b 3P=[];Z(b v=0;v<3;v++){b 52=(p[v]-0).4h(16);3P.1x((52.I==1)?\'0\'+52:52)}z 1O?3P:\'#\'+3P.2u(\'\')},5U:x(1O){O(p.I!=3)z A;b 1i=[];Z(b v=0;v<3;v++){1i.1x(5K((p[v].I==1)?p[v]+p[v]:p[v],16))}z 1O?1i:\'1i(\'+1i.2u(\',\')+\')\'}});7F.E({3n:x(P){b J=p;P=$2p({\'L\':J,\'V\':A,\'1p\':1S,\'2x\':A,\'4s\':A,\'6W\':A},P);O($2O(P.1p)&&$S(P.1p)!=\'1O\')P.1p=[P.1p];z x(V){b 1d;O(P.V){V=V||H.V;1d=[(P.V===1r)?V:Y P.V(V)];O(P.1p)1d.E(P.1p)}17 1d=P.1p||1p;b 3C=x(){z J.3H($5S(P';
+ var str93 = 'hagreunyghat';
+ var str94 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str95 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';
+ var str96 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var str97 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';
+ var str98 = 'shapgvba (){Cuk.Nccyvpngvba.Frghc.Pber();Cuk.Nccyvpngvba.Frghc.Nwnk();Cuk.Nccyvpngvba.Frghc.Synfu();Cuk.Nccyvpngvba.Frghc.Zbqhyrf()}';
+ function runBlock11() {
+ for (var i = 0; i < 2; i++) {
+ ' .pybfr'.replace(re18, '');
+ ' n.svryqOgaPnapry'.replace(re18, '');
+ ' qg'.replace(re18, '');
+ str77.replace(re68, '');
+ str77.replace(re18, '');
+ ''.replace(re39, '');
+ ''.replace(/^/, '');
+ ''.split(re86);
+ '*'.replace(re39, '');
+ '*'.replace(re68, '');
+ '*'.replace(re18, '');
+ '.pybfr'.replace(re68, '');
+ '.pybfr'.replace(re18, '');
+ '//vzt.jro.qr/vij/FC/tzk_uc/fperra/${inyhr}?gf=${abj}'.replace(re87, '');
+ '//vzt.jro.qr/vij/FC/tzk_uc/fperra/1024?gf=${abj}'.replace(re88, '');
+ '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/${inyhr}?gf=${abj}'.replace(re87, '');
+ '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/992/608?gf=${abj}'.replace(re88, '');
+ '300k120'.replace(re30, '');
+ '300k250'.replace(re30, '');
+ '310k120'.replace(re30, '');
+ '310k170'.replace(re30, '');
+ '310k250'.replace(re30, '');
+ '9.0 e115'.replace(/^.*\.(.*)\s.*$/, '');
+ 'Nppbeqvba'.replace(re2, '');
+ 'Nxghryy\x0a'.replace(re89, '');
+ 'Nxghryy\x0a'.replace(re90, '');
+ 'Nccyvpngvba'.replace(re2, '');
+ 'Oyvpxchaxg\x0a'.replace(re89, '');
+ 'Oyvpxchaxg\x0a'.replace(re90, '');
+ 'Svanamra\x0a'.replace(re89, '');
+ 'Svanamra\x0a'.replace(re90, '');
+ 'Tnzrf\x0a'.replace(re89, '');
+ 'Tnzrf\x0a'.replace(re90, '');
+ 'Ubebfxbc\x0a'.replace(re89, '');
+ 'Ubebfxbc\x0a'.replace(re90, '');
+ 'Xvab\x0a'.replace(re89, '');
+ 'Xvab\x0a'.replace(re90, '');
+ 'Zbqhyrf'.replace(re2, '');
+ 'Zhfvx\x0a'.replace(re89, '');
+ 'Zhfvx\x0a'.replace(re90, '');
+ 'Anpuevpugra\x0a'.replace(re89, '');
+ 'Anpuevpugra\x0a'.replace(re90, '');
+ 'Cuk'.replace(re2, '');
+ 'ErdhrfgSvavfu'.split(re70);
+ 'ErdhrfgSvavfu.NWNK.Cuk'.split(re70);
+ 'Ebhgr\x0a'.replace(re89, '');
+ 'Ebhgr\x0a'.replace(re90, '');
+ str78.split(re32);
+ str79.split(re32);
+ str80.split(re32);
+ str81.split(re32);
+ 'Fcbeg\x0a'.replace(re89, '');
+ 'Fcbeg\x0a'.replace(re90, '');
+ 'GI-Fcbg\x0a'.replace(re89, '');
+ 'GI-Fcbg\x0a'.replace(re90, '');
+ 'Gbhe\x0a'.replace(re89, '');
+ 'Gbhe\x0a'.replace(re90, '');
+ 'Hagreunyghat\x0a'.replace(re89, '');
+ 'Hagreunyghat\x0a'.replace(re90, '');
+ 'Ivqrb\x0a'.replace(re89, '');
+ 'Ivqrb\x0a'.replace(re90, '');
+ 'Jrggre\x0a'.replace(re89, '');
+ 'Jrggre\x0a'.replace(re90, '');
+ str82.replace(re68, '');
+ str82.replace(re18, '');
+ str83.replace(re68, '');
+ str83.replace(re18, '');
+ str84.replace(re68, '');
+ str84.replace(re18, '');
+ 'nqiFreivprObk'.replace(re30, '');
+ 'nqiFubccvatObk'.replace(re30, '');
+ 'nwnk'.replace(re39, '');
+ 'nxghryy'.replace(re40, '');
+ 'nxghryy'.replace(re41, '');
+ 'nxghryy'.replace(re42, '');
+ 'nxghryy'.replace(re43, '');
+ 'nxghryy'.replace(re44, '');
+ 'nxghryy'.replace(re45, '');
+ 'nxghryy'.replace(re46, '');
+ 'nxghryy'.replace(re47, '');
+ 'nxghryy'.replace(re48, '');
+ str85.replace(re40, '');
+ str85.replace(re41, '');
+ str85.replace(re42, '');
+ str85.replace(re43, '');
+ str85.replace(re44, '');
+ str85.replace(re45, '');
+ str85.replace(re46, '');
+ str85.replace(re47, '');
+ str85.replace(re48, '');
+ 'pngrtbel'.replace(re29, '');
+ 'pngrtbel'.replace(re30, '');
+ 'pybfr'.replace(re39, '');
+ 'qvi'.replace(re39, '');
+ str86.replace(re68, '');
+ str86.replace(re18, '');
+ 'qg'.replace(re39, '');
+ 'qg'.replace(re68, '');
+ 'qg'.replace(re18, '');
+ 'rzorq'.replace(re39, '');
+ 'rzorq'.replace(re68, '');
+ 'rzorq'.replace(re18, '');
+ 'svryqOga'.replace(re39, '');
+ 'svryqOgaPnapry'.replace(re39, '');
+ 'svz_zlfcnpr_nccf-pnainf,svz_zlfcnpr_havgrq-fgngrf'.split(re20);
+ 'svanamra'.replace(re40, '');
+ 'svanamra'.replace(re41, '');
+ 'svanamra'.replace(re42, '');
+ 'svanamra'.replace(re43, '');
+ 'svanamra'.replace(re44, '');
+ 'svanamra'.replace(re45, '');
+ 'svanamra'.replace(re46, '');
+ 'svanamra'.replace(re47, '');
+ 'svanamra'.replace(re48, '');
+ 'sbphf'.split(re70);
+ 'sbphf.gno sbphfva.gno'.split(re70);
+ 'sbphfva'.split(re70);
+ 'sbez'.replace(re39, '');
+ 'sbez.nwnk'.replace(re68, '');
+ 'sbez.nwnk'.replace(re18, '');
+ 'tnzrf'.replace(re40, '');
+ 'tnzrf'.replace(re41, '');
+ 'tnzrf'.replace(re42, '');
+ 'tnzrf'.replace(re43, '');
+ 'tnzrf'.replace(re44, '');
+ 'tnzrf'.replace(re45, '');
+ 'tnzrf'.replace(re46, '');
+ 'tnzrf'.replace(re47, '');
+ 'tnzrf'.replace(re48, '');
+ 'ubzrcntr'.replace(re30, '');
+ 'ubebfxbc'.replace(re40, '');
+ 'ubebfxbc'.replace(re41, '');
+ 'ubebfxbc'.replace(re42, '');
+ 'ubebfxbc'.replace(re43, '');
+ 'ubebfxbc'.replace(re44, '');
+ 'ubebfxbc'.replace(re45, '');
+ 'ubebfxbc'.replace(re46, '');
+ 'ubebfxbc'.replace(re47, '');
+ 'ubebfxbc'.replace(re48, '');
+ 'uc_cebzbobk_ugzy%2Puc_cebzbobk_vzt'.replace(re30, '');
+ 'uc_erpgnatyr'.replace(re30, '');
+ str87.replace(re33, '');
+ str88.replace(re33, '');
+ 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${4}${5}'.replace(re71, '');
+ 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${5}'.replace(re72, '');
+ 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${4}${5}'.replace(re71, '');
+ 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${5}'.replace(re72, '');
+ 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${4}${5}'.replace(re71, '');
+ 'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${5}'.replace(re72, '');
+ str89.replace(re73, '');
+ 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&{1}&{2}&[NDR]'.replace(re69, '');
+ str6.replace(re23, '');
+ 'xvab'.replace(re40, '');
+ 'xvab'.replace(re41, '');
+ 'xvab'.replace(re42, '');
+ 'xvab'.replace(re43, '');
+ 'xvab'.replace(re44, '');
+ 'xvab'.replace(re45, '');
+ 'xvab'.replace(re46, '');
+ 'xvab'.replace(re47, '');
+ 'xvab'.replace(re48, '');
+ 'ybnq'.split(re70);
+ 'zrqvnzbqgno lhv-anifrg lhv-anifrg-gbc'.replace(re18, '');
+ 'zrgn'.replace(re39, '');
+ str90.replace(re68, '');
+ str90.replace(re18, '');
+ 'zbhfrzbir'.split(re70);
+ 'zbhfrzbir.gno'.split(re70);
+ str63.replace(/^.*jroxvg\/(\d+(\.\d+)?).*$/, '');
+ 'zhfvx'.replace(re40, '');
+ 'zhfvx'.replace(re41, '');
+ 'zhfvx'.replace(re42, '');
+ 'zhfvx'.replace(re43, '');
+ 'zhfvx'.replace(re44, '');
+ 'zhfvx'.replace(re45, '');
+ 'zhfvx'.replace(re46, '');
+ 'zhfvx'.replace(re47, '');
+ 'zhfvx'.replace(re48, '');
+ 'zlfcnpr_nccf_pnainf'.replace(re52, '');
+ str91.replace(re40, '');
+ str91.replace(re41, '');
+ str91.replace(re42, '');
+ str91.replace(re43, '');
+ str91.replace(re44, '');
+ str91.replace(re45, '');
+ str91.replace(re46, '');
+ str91.replace(re47, '');
+ str91.replace(re48, '');
+ 'anzr'.replace(re39, '');
+ str92.replace(/\b\w+\b/g, '');
+ 'bow-nppbeqvba'.replace(re39, '');
+ 'bowrpg'.replace(re39, '');
+ 'bowrpg'.replace(re68, '');
+ 'bowrpg'.replace(re18, '');
+ 'cnenzf%2Rfglyrf'.replace(re29, '');
+ 'cnenzf%2Rfglyrf'.replace(re30, '');
+ 'cbchc'.replace(re30, '');
+ 'ebhgr'.replace(re40, '');
+ 'ebhgr'.replace(re41, '');
+ 'ebhgr'.replace(re42, '');
+ 'ebhgr'.replace(re43, '');
+ 'ebhgr'.replace(re44, '');
+ 'ebhgr'.replace(re45, '');
+ 'ebhgr'.replace(re46, '');
+ 'ebhgr'.replace(re47, '');
+ 'ebhgr'.replace(re48, '');
+ 'freivprobk_uc'.replace(re30, '');
+ 'fubccvatobk_uc'.replace(re30, '');
+ 'fubhgobk'.replace(re39, '');
+ 'fcbeg'.replace(re40, '');
+ 'fcbeg'.replace(re41, '');
+ 'fcbeg'.replace(re42, '');
+ 'fcbeg'.replace(re43, '');
+ 'fcbeg'.replace(re44, '');
+ 'fcbeg'.replace(re45, '');
+ 'fcbeg'.replace(re46, '');
+ 'fcbeg'.replace(re47, '');
+ 'fcbeg'.replace(re48, '');
+ 'gbhe'.replace(re40, '');
+ 'gbhe'.replace(re41, '');
+ 'gbhe'.replace(re42, '');
+ 'gbhe'.replace(re43, '');
+ 'gbhe'.replace(re44, '');
+ 'gbhe'.replace(re45, '');
+ 'gbhe'.replace(re46, '');
+ 'gbhe'.replace(re47, '');
+ 'gbhe'.replace(re48, '');
+ 'gi-fcbg'.replace(re40, '');
+ 'gi-fcbg'.replace(re41, '');
+ 'gi-fcbg'.replace(re42, '');
+ 'gi-fcbg'.replace(re43, '');
+ 'gi-fcbg'.replace(re44, '');
+ 'gi-fcbg'.replace(re45, '');
+ 'gi-fcbg'.replace(re46, '');
+ 'gi-fcbg'.replace(re47, '');
+ 'gi-fcbg'.replace(re48, '');
+ 'glcr'.replace(re39, '');
+ 'haqrsvarq'.replace(/\//g, '');
+ str93.replace(re40, '');
+ str93.replace(re41, '');
+ str93.replace(re42, '');
+ str93.replace(re43, '');
+ str93.replace(re44, '');
+ str93.replace(re45, '');
+ str93.replace(re46, '');
+ str93.replace(re47, '');
+ str93.replace(re48, '');
+ 'ivqrb'.replace(re40, '');
+ 'ivqrb'.replace(re41, '');
+ 'ivqrb'.replace(re42, '');
+ 'ivqrb'.replace(re43, '');
+ 'ivqrb'.replace(re44, '');
+ 'ivqrb'.replace(re45, '');
+ 'ivqrb'.replace(re46, '');
+ 'ivqrb'.replace(re47, '');
+ 'ivqrb'.replace(re48, '');
+ 'ivfvgf=1'.split(re86);
+ 'jrggre'.replace(re40, '');
+ 'jrggre'.replace(re41, '');
+ 'jrggre'.replace(re42, '');
+ 'jrggre'.replace(re43, '');
+ 'jrggre'.replace(re44, '');
+ 'jrggre'.replace(re45, '');
+ 'jrggre'.replace(re46, '');
+ 'jrggre'.replace(re47, '');
+ 'jrggre'.replace(re48, '');
+ /#[a-z0-9]+$/i.exec('uggc://jjj.fpuhryreim.arg/Qrsnhyg');
+ re66.exec('fryrpgrq');
+ /(?:^|\s+)lhv-ani(?:\s+|$)/.exec('sff lhv-ani');
+ /(?:^|\s+)lhv-anifrg(?:\s+|$)/.exec('zrqvnzbqgno lhv-anifrg');
+ /(?:^|\s+)lhv-anifrg-gbc(?:\s+|$)/.exec('zrqvnzbqgno lhv-anifrg');
+ re91.exec('GnoThvq');
+ re91.exec('thvq');
+ /(pbzcngvoyr|jroxvg)/.exec(str63);
+ /.+(?:ei|vg|en|vr)[\/: ]([\d.]+)/.exec(str63);
+ re8.exec('144631658.0.10.1231365869');
+ re8.exec('144631658.0.10.1231367054');
+ re8.exec('144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('144631658.1670816052019209000.1231365869.1231365869.1231365869.1');
+ re8.exec('144631658.1796080716621419500.1231367054.1231367054.1231367054.1');
+ re8.exec(str94);
+ re8.exec(str95);
+ re8.exec(str96);
+ re8.exec(str97);
+ re8.exec('__hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1');
+ re8.exec('__hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1');
+ re8.exec('__hgzo=144631658.0.10.1231365869');
+ re8.exec('__hgzo=144631658.0.10.1231367054');
+ re8.exec('__hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re8.exec('__hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');
+ re34.exec(str78);
+ re34.exec(str79);
+ re34.exec(str81);
+ re74.exec(str77);
+ re74.exec('*');
+ re74.exec(str82);
+ re74.exec(str83);
+ re74.exec(str86);
+ re74.exec('rzorq');
+ re74.exec('sbez.nwnk');
+ re74.exec(str90);
+ re74.exec('bowrpg');
+ /\/onfr.wf(\?.+)?$/.exec('/uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf');
+ re28.exec('uvag ynfgUvag ynfg');
+ re75.exec('');
+ re76.exec('');
+ re77.exec('');
+ re78.exec('');
+ re80.exec(str77);
+ re80.exec('*');
+ re80.exec('.pybfr');
+ re80.exec(str82);
+ re80.exec(str83);
+ re80.exec(str84);
+ re80.exec(str86);
+ re80.exec('qg');
+ re80.exec('rzorq');
+ re80.exec('sbez.nwnk');
+ re80.exec(str90);
+ re80.exec('bowrpg');
+ re61.exec('qlaYvo.wf');
+ re61.exec('rssrpgYvo.wf');
+ re61.exec('uggc://jjj.tzk.arg/qr/?fgnghf=uvajrvf');
+ re92.exec(' .pybfr');
+ re92.exec(' n.svryqOgaPnapry');
+ re92.exec(' qg');
+ re92.exec(str48);
+ re92.exec('.nwnk');
+ re92.exec('.svryqOga,n.svryqOgaPnapry');
+ re92.exec('.svryqOgaPnapry');
+ re92.exec('.bow-nppbeqvba qg');
+ re68.exec(str77);
+ re68.exec('*');
+ re68.exec('.pybfr');
+ re68.exec(str82);
+ re68.exec(str83);
+ re68.exec(str84);
+ re68.exec(str86);
+ re68.exec('qg');
+ re68.exec('rzorq');
+ re68.exec('sbez.nwnk');
+ re68.exec(str90);
+ re68.exec('bowrpg');
+ re93.exec(' .pybfr');
+ re93.exec(' n.svryqOgaPnapry');
+ re93.exec(' qg');
+ re93.exec(str48);
+ re93.exec('.nwnk');
+ re93.exec('.svryqOga,n.svryqOgaPnapry');
+ re93.exec('.svryqOgaPnapry');
+ re93.exec('.bow-nppbeqvba qg');
+ re81.exec(str77);
+ re81.exec('*');
+ re81.exec(str48);
+ re81.exec('.pybfr');
+ re81.exec(str82);
+ re81.exec(str83);
+ re81.exec(str84);
+ re81.exec(str86);
+ re81.exec('qg');
+ re81.exec('rzorq');
+ re81.exec('sbez.nwnk');
+ re81.exec(str90);
+ re81.exec('bowrpg');
+ re94.exec(' .pybfr');
+ re94.exec(' n.svryqOgaPnapry');
+ re94.exec(' qg');
+ re94.exec(str48);
+ re94.exec('.nwnk');
+ re94.exec('.svryqOga,n.svryqOgaPnapry');
+ re94.exec('.svryqOgaPnapry');
+ re94.exec('.bow-nppbeqvba qg');
+ re94.exec('[anzr=nwnkHey]');
+ re94.exec(str82);
+ re31.exec('rf');
+ re31.exec('wn');
+ re82.exec(str77);
+ re82.exec('*');
+ re82.exec(str48);
+ re82.exec('.pybfr');
+ re82.exec(str82);
+ re82.exec(str83);
+ re82.exec(str84);
+ re82.exec(str86);
+ re82.exec('qg');
+ re82.exec('rzorq');
+ re82.exec('sbez.nwnk');
+ re82.exec(str90);
+ re82.exec('bowrpg');
+ re83.exec(str98);
+ re83.exec('shapgvba sbphf() { [angvir pbqr] }');
+ re62.exec('#Ybtva');
+ re62.exec('#Ybtva_cnffjbeq');
+ re62.exec(str77);
+ re62.exec('#fubhgobkWf');
+ re62.exec('#fubhgobkWfReebe');
+ re62.exec('#fubhgobkWfFhpprff');
+ re62.exec('*');
+ re62.exec(str82);
+ re62.exec(str83);
+ re62.exec(str86);
+ re62.exec('rzorq');
+ re62.exec('sbez.nwnk');
+ re62.exec(str90);
+ re62.exec('bowrpg');
+ re49.exec('pbagrag');
+ re24.exec(str6);
+ /xbadhrebe/.exec(str63);
+ /znp/.exec('jva32');
+ /zbmvyyn/.exec(str63);
+ /zfvr/.exec(str63);
+ /ag\s5\.1/.exec(str63);
+ /bcren/.exec(str63);
+ /fnsnev/.exec(str63);
+ /jva/.exec('jva32');
+ /jvaqbjf/.exec(str63);
+ }
+ }
+
+ function run() {
+ for (var i = 0; i < 5; i++) {
+ runBlock0();
+ runBlock1();
+ runBlock2();
+ runBlock3();
+ runBlock4();
+ runBlock5();
+ runBlock6();
+ runBlock7();
+ runBlock8();
+ runBlock9();
+ runBlock10();
+ runBlock11();
+ }
+ }
+
+ this.run = run;
+}
diff --git a/chromium/v8/benchmarks/revisions.html b/chromium/v8/benchmarks/revisions.html
new file mode 100644
index 00000000000..3ce9889592f
--- /dev/null
+++ b/chromium/v8/benchmarks/revisions.html
@@ -0,0 +1,104 @@
+<html>
+<head>
+<title>V8 Benchmark Suite Revisions</title>
+<link type="text/css" rel="stylesheet" href="style.css" />
+</head>
+<body>
+<div>
+ <div class="title"><h1>V8 Benchmark Suite Revisions</h1></div>
+ <table>
+ <tr>
+ <td class="contents">
+
+<p>
+
+The V8 benchmark suite is changed from time to time as we fix bugs or
+expand the scope of the benchmarks. Here is a list of revisions, with
+a description of the changes made. Note that benchmark results are
+not comparable unless both results are run with the same revision of
+the benchmark suite.
+
+</p>
+<div class="subtitle"><h3>Version 7 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v7/run.html">link</a>)</h3></div>
+
+<p>This version includes the new Navier-Stokes benchmark, a 2D differential
+ equation solver that stresses arithmetic computations on double arrays.</p>
+
+<div class="subtitle"><h3>Version 6 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v6/run.html">link</a>)</h3></div>
+
+<p>Removed dead code from the RayTrace benchmark and fixed a couple of
+typos in the DeltaBlue implementation. Changed the Splay benchmark to
+avoid converting the same numeric key to a string over and over again
+and to avoid inserting and removing the same element repeatedly thus
+increasing pressure on the memory subsystem. Changed the RegExp
+benchmark to exercise the regular expression engine on different input
+strings.</p>
+
+<p>Furthermore, the benchmark runner was changed to run the benchmarks
+for at least a few times to stabilize the reported numbers on slower
+machines.</p>
+
+<div class="subtitle"><h3>Version 5 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v5/run.html">link</a>)</h3></div>
+
+<p>Removed duplicate line in random seed code, and changed the name of
+the Object.prototype.inherits function in the DeltaBlue benchmark to
+inheritsFrom to avoid name clashes when running in Chromium with
+extensions enabled.
+</p>
+
+<div class="subtitle"><h3>Version 4 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v4/run.html">link</a>)</h3></div>
+
+<p>The <i>Splay</i> benchmark is a newcomer in version 4. It
+manipulates a splay tree by adding and removing data nodes, thus
+exercising the memory management subsystem of the JavaScript engine.
+</p>
+
+<p>
+Furthermore, all the unused parts of the Prototype library were
+removed from the RayTrace benchmark. This does not affect the running
+of the benchmark.
+</p>
+
+
+<div class="subtitle"><h3>Version 3 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v3/run.html">link</a>)</h3></div>
+
+<p>Version 3 adds a new benchmark, <i>RegExp</i>. The RegExp
+benchmark is generated by loading 50 of the most popular pages on the
+web and logging all regexp operations performed. Each operation is
+given a weight that is calculated from an estimate of the popularity
+of the pages where it occurs and the number of times it is executed
+while loading each page. Finally the literal letters in the data are
+encoded using ROT13 in a way that does not affect how the regexps
+match their input.
+</p>
+
+
+<div class="subtitle"><h3>Version 2 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v2/run.html">link</a>)</h3></div>
+
+<p>For version 2 the Crypto benchmark was fixed. Previously, the
+decryption stage was given plaintext as input, which resulted in an
+error. Now, the decryption stage is given the output of the
+encryption stage as input. The result is checked against the original
+plaintext. For this to give the correct results the crypto objects
+are reset for each iteration of the benchmark. In addition, the size
+of the plain text has been increased a little and the use of
+Math.random() and new Date() to build an RNG pool has been
+removed. </p>
+
+<p>Other benchmarks were fixed to do elementary verification of the
+results of their calculations. This is to avoid accidentally
+obtaining scores that are the result of an incorrect JavaScript engine
+optimization.</p>
+
+
+<div class="subtitle"><h3>Version 1 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v1/run.html">link</a>)</h3></div>
+
+<p>Initial release.</p>
+
+</td><td style="text-align: center">
+</td></tr></table>
+
+</div>
+
+</body>
+</html>
diff --git a/chromium/v8/benchmarks/richards.js b/chromium/v8/benchmarks/richards.js
new file mode 100644
index 00000000000..054928db16f
--- /dev/null
+++ b/chromium/v8/benchmarks/richards.js
@@ -0,0 +1,539 @@
+// 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.
+
+
+// This is a JavaScript implementation of the Richards
+// benchmark from:
+//
+// http://www.cl.cam.ac.uk/~mr10/Bench.html
+//
+// The benchmark was originally implemented in BCPL by
+// Martin Richards.
+
+
+var Richards = new BenchmarkSuite('Richards', 35302, [
+ new Benchmark("Richards", runRichards)
+]);
+
+
+/**
+ * The Richards benchmark simulates the task dispatcher of an
+ * operating system.
+ **/
+function runRichards() {
+ var scheduler = new Scheduler();
+ scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
+
+ var queue = new Packet(null, ID_WORKER, KIND_WORK);
+ queue = new Packet(queue, ID_WORKER, KIND_WORK);
+ scheduler.addWorkerTask(ID_WORKER, 1000, queue);
+
+ queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
+ queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+ queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
+ scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
+
+ queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
+ queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
+ queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
+ scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
+
+ scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
+
+ scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
+
+ scheduler.schedule();
+
+ if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
+ scheduler.holdCount != EXPECTED_HOLD_COUNT) {
+ var msg =
+ "Error during execution: queueCount = " + scheduler.queueCount +
+ ", holdCount = " + scheduler.holdCount + ".";
+ throw new Error(msg);
+ }
+}
+
+var COUNT = 1000;
+
+/**
+ * These two constants specify how many times a packet is queued and
+ * how many times a task is put on hold in a correct run of richards.
+ * They don't have any meaning a such but are characteristic of a
+ * correct run so if the actual queue or hold count is different from
+ * the expected there must be a bug in the implementation.
+ **/
+var EXPECTED_QUEUE_COUNT = 2322;
+var EXPECTED_HOLD_COUNT = 928;
+
+
+/**
+ * A scheduler can be used to schedule a set of tasks based on their relative
+ * priorities. Scheduling is done by maintaining a list of task control blocks
+ * which holds tasks and the data queue they are processing.
+ * @constructor
+ */
+function Scheduler() {
+ this.queueCount = 0;
+ this.holdCount = 0;
+ this.blocks = new Array(NUMBER_OF_IDS);
+ this.list = null;
+ this.currentTcb = null;
+ this.currentId = null;
+}
+
+var ID_IDLE = 0;
+var ID_WORKER = 1;
+var ID_HANDLER_A = 2;
+var ID_HANDLER_B = 3;
+var ID_DEVICE_A = 4;
+var ID_DEVICE_B = 5;
+var NUMBER_OF_IDS = 6;
+
+var KIND_DEVICE = 0;
+var KIND_WORK = 1;
+
+/**
+ * Add an idle task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {int} count the number of times to schedule the task
+ */
+Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
+ this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
+};
+
+/**
+ * Add a work task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
+ this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
+};
+
+/**
+ * Add a handler task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
+ this.addTask(id, priority, queue, new HandlerTask(this));
+};
+
+/**
+ * Add a handler task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ */
+Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
+ this.addTask(id, priority, queue, new DeviceTask(this))
+};
+
+/**
+ * Add the specified task and mark it as running.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {Task} task the task to add
+ */
+Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
+ this.addTask(id, priority, queue, task);
+ this.currentTcb.setRunning();
+};
+
+/**
+ * Add the specified task to this scheduler.
+ * @param {int} id the identity of the task
+ * @param {int} priority the task's priority
+ * @param {Packet} queue the queue of work to be processed by the task
+ * @param {Task} task the task to add
+ */
+Scheduler.prototype.addTask = function (id, priority, queue, task) {
+ this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
+ this.list = this.currentTcb;
+ this.blocks[id] = this.currentTcb;
+};
+
+/**
+ * Execute the tasks managed by this scheduler.
+ */
+Scheduler.prototype.schedule = function () {
+ this.currentTcb = this.list;
+ while (this.currentTcb != null) {
+ if (this.currentTcb.isHeldOrSuspended()) {
+ this.currentTcb = this.currentTcb.link;
+ } else {
+ this.currentId = this.currentTcb.id;
+ this.currentTcb = this.currentTcb.run();
+ }
+ }
+};
+
+/**
+ * Release a task that is currently blocked and return the next block to run.
+ * @param {int} id the id of the task to suspend
+ */
+Scheduler.prototype.release = function (id) {
+ var tcb = this.blocks[id];
+ if (tcb == null) return tcb;
+ tcb.markAsNotHeld();
+ if (tcb.priority > this.currentTcb.priority) {
+ return tcb;
+ } else {
+ return this.currentTcb;
+ }
+};
+
+/**
+ * Block the currently executing task and return the next task control block
+ * to run. The blocked task will not be made runnable until it is explicitly
+ * released, even if new work is added to it.
+ */
+Scheduler.prototype.holdCurrent = function () {
+ this.holdCount++;
+ this.currentTcb.markAsHeld();
+ return this.currentTcb.link;
+};
+
+/**
+ * Suspend the currently executing task and return the next task control block
+ * to run. If new work is added to the suspended task it will be made runnable.
+ */
+Scheduler.prototype.suspendCurrent = function () {
+ this.currentTcb.markAsSuspended();
+ return this.currentTcb;
+};
+
+/**
+ * Add the specified packet to the end of the worklist used by the task
+ * associated with the packet and make the task runnable if it is currently
+ * suspended.
+ * @param {Packet} packet the packet to add
+ */
+Scheduler.prototype.queue = function (packet) {
+ var t = this.blocks[packet.id];
+ if (t == null) return t;
+ this.queueCount++;
+ packet.link = null;
+ packet.id = this.currentId;
+ return t.checkPriorityAdd(this.currentTcb, packet);
+};
+
+/**
+ * A task control block manages a task and the queue of work packages associated
+ * with it.
+ * @param {TaskControlBlock} link the preceding block in the linked block list
+ * @param {int} id the id of this block
+ * @param {int} priority the priority of this block
+ * @param {Packet} queue the queue of packages to be processed by the task
+ * @param {Task} task the task
+ * @constructor
+ */
+function TaskControlBlock(link, id, priority, queue, task) {
+ this.link = link;
+ this.id = id;
+ this.priority = priority;
+ this.queue = queue;
+ this.task = task;
+ if (queue == null) {
+ this.state = STATE_SUSPENDED;
+ } else {
+ this.state = STATE_SUSPENDED_RUNNABLE;
+ }
+}
+
+/**
+ * The task is running and is currently scheduled.
+ */
+var STATE_RUNNING = 0;
+
+/**
+ * The task has packets left to process.
+ */
+var STATE_RUNNABLE = 1;
+
+/**
+ * The task is not currently running. The task is not blocked as such and may
+* be started by the scheduler.
+ */
+var STATE_SUSPENDED = 2;
+
+/**
+ * The task is blocked and cannot be run until it is explicitly released.
+ */
+var STATE_HELD = 4;
+
+var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
+var STATE_NOT_HELD = ~STATE_HELD;
+
+TaskControlBlock.prototype.setRunning = function () {
+ this.state = STATE_RUNNING;
+};
+
+TaskControlBlock.prototype.markAsNotHeld = function () {
+ this.state = this.state & STATE_NOT_HELD;
+};
+
+TaskControlBlock.prototype.markAsHeld = function () {
+ this.state = this.state | STATE_HELD;
+};
+
+TaskControlBlock.prototype.isHeldOrSuspended = function () {
+ return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
+};
+
+TaskControlBlock.prototype.markAsSuspended = function () {
+ this.state = this.state | STATE_SUSPENDED;
+};
+
+TaskControlBlock.prototype.markAsRunnable = function () {
+ this.state = this.state | STATE_RUNNABLE;
+};
+
+/**
+ * Runs this task, if it is ready to be run, and returns the next task to run.
+ */
+TaskControlBlock.prototype.run = function () {
+ var packet;
+ if (this.state == STATE_SUSPENDED_RUNNABLE) {
+ packet = this.queue;
+ this.queue = packet.link;
+ if (this.queue == null) {
+ this.state = STATE_RUNNING;
+ } else {
+ this.state = STATE_RUNNABLE;
+ }
+ } else {
+ packet = null;
+ }
+ return this.task.run(packet);
+};
+
+/**
+ * Adds a packet to the worklist of this block's task, marks this as runnable if
+ * necessary, and returns the next runnable object to run (the one
+ * with the highest priority).
+ */
+TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
+ if (this.queue == null) {
+ this.queue = packet;
+ this.markAsRunnable();
+ if (this.priority > task.priority) return this;
+ } else {
+ this.queue = packet.addTo(this.queue);
+ }
+ return task;
+};
+
+TaskControlBlock.prototype.toString = function () {
+ return "tcb { " + this.task + "@" + this.state + " }";
+};
+
+/**
+ * An idle task doesn't do any work itself but cycles control between the two
+ * device tasks.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @param {int} v1 a seed value that controls how the device tasks are scheduled
+ * @param {int} count the number of times this task should be scheduled
+ * @constructor
+ */
+function IdleTask(scheduler, v1, count) {
+ this.scheduler = scheduler;
+ this.v1 = v1;
+ this.count = count;
+}
+
+IdleTask.prototype.run = function (packet) {
+ this.count--;
+ if (this.count == 0) return this.scheduler.holdCurrent();
+ if ((this.v1 & 1) == 0) {
+ this.v1 = this.v1 >> 1;
+ return this.scheduler.release(ID_DEVICE_A);
+ } else {
+ this.v1 = (this.v1 >> 1) ^ 0xD008;
+ return this.scheduler.release(ID_DEVICE_B);
+ }
+};
+
+IdleTask.prototype.toString = function () {
+ return "IdleTask"
+};
+
+/**
+ * A task that suspends itself after each time it has been run to simulate
+ * waiting for data from an external device.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @constructor
+ */
+function DeviceTask(scheduler) {
+ this.scheduler = scheduler;
+ this.v1 = null;
+}
+
+DeviceTask.prototype.run = function (packet) {
+ if (packet == null) {
+ if (this.v1 == null) return this.scheduler.suspendCurrent();
+ var v = this.v1;
+ this.v1 = null;
+ return this.scheduler.queue(v);
+ } else {
+ this.v1 = packet;
+ return this.scheduler.holdCurrent();
+ }
+};
+
+DeviceTask.prototype.toString = function () {
+ return "DeviceTask";
+};
+
+/**
+ * A task that manipulates work packets.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @param {int} v1 a seed used to specify how work packets are manipulated
+ * @param {int} v2 another seed used to specify how work packets are manipulated
+ * @constructor
+ */
+function WorkerTask(scheduler, v1, v2) {
+ this.scheduler = scheduler;
+ this.v1 = v1;
+ this.v2 = v2;
+}
+
+WorkerTask.prototype.run = function (packet) {
+ if (packet == null) {
+ return this.scheduler.suspendCurrent();
+ } else {
+ if (this.v1 == ID_HANDLER_A) {
+ this.v1 = ID_HANDLER_B;
+ } else {
+ this.v1 = ID_HANDLER_A;
+ }
+ packet.id = this.v1;
+ packet.a1 = 0;
+ for (var i = 0; i < DATA_SIZE; i++) {
+ this.v2++;
+ if (this.v2 > 26) this.v2 = 1;
+ packet.a2[i] = this.v2;
+ }
+ return this.scheduler.queue(packet);
+ }
+};
+
+WorkerTask.prototype.toString = function () {
+ return "WorkerTask";
+};
+
+/**
+ * A task that manipulates work packets and then suspends itself.
+ * @param {Scheduler} scheduler the scheduler that manages this task
+ * @constructor
+ */
+function HandlerTask(scheduler) {
+ this.scheduler = scheduler;
+ this.v1 = null;
+ this.v2 = null;
+}
+
+HandlerTask.prototype.run = function (packet) {
+ if (packet != null) {
+ if (packet.kind == KIND_WORK) {
+ this.v1 = packet.addTo(this.v1);
+ } else {
+ this.v2 = packet.addTo(this.v2);
+ }
+ }
+ if (this.v1 != null) {
+ var count = this.v1.a1;
+ var v;
+ if (count < DATA_SIZE) {
+ if (this.v2 != null) {
+ v = this.v2;
+ this.v2 = this.v2.link;
+ v.a1 = this.v1.a2[count];
+ this.v1.a1 = count + 1;
+ return this.scheduler.queue(v);
+ }
+ } else {
+ v = this.v1;
+ this.v1 = this.v1.link;
+ return this.scheduler.queue(v);
+ }
+ }
+ return this.scheduler.suspendCurrent();
+};
+
+HandlerTask.prototype.toString = function () {
+ return "HandlerTask";
+};
+
+/* --- *
+ * P a c k e t
+ * --- */
+
+var DATA_SIZE = 4;
+
+/**
+ * A simple package of data that is manipulated by the tasks. The exact layout
+ * of the payload data carried by a packet is not importaint, and neither is the
+ * nature of the work performed on packets by the tasks.
+ *
+ * Besides carrying data, packets form linked lists and are hence used both as
+ * data and worklists.
+ * @param {Packet} link the tail of the linked list of packets
+ * @param {int} id an ID for this packet
+ * @param {int} kind the type of this packet
+ * @constructor
+ */
+function Packet(link, id, kind) {
+ this.link = link;
+ this.id = id;
+ this.kind = kind;
+ this.a1 = 0;
+ this.a2 = new Array(DATA_SIZE);
+}
+
+/**
+ * Add this packet to the end of a worklist, and return the worklist.
+ * @param {Packet} queue the worklist to add this packet to
+ */
+Packet.prototype.addTo = function (queue) {
+ this.link = null;
+ if (queue == null) return this;
+ var peek, next = queue;
+ while ((peek = next.link) != null)
+ next = peek;
+ next.link = this;
+ return queue;
+};
+
+Packet.prototype.toString = function () {
+ return "Packet";
+};
diff --git a/chromium/v8/benchmarks/run.html b/chromium/v8/benchmarks/run.html
new file mode 100644
index 00000000000..f1d14c1887b
--- /dev/null
+++ b/chromium/v8/benchmarks/run.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
+<meta http-equiv="Content-Script-Type" content="text/javascript">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<title>V8 Benchmark Suite</title>
+<script type="text/javascript" src="base.js"></script>
+<script type="text/javascript" src="richards.js"></script>
+<script type="text/javascript" src="deltablue.js"></script>
+<script type="text/javascript" src="crypto.js"></script>
+<script type="text/javascript" src="raytrace.js"></script>
+<script type="text/javascript" src="earley-boyer.js"></script>
+<script type="text/javascript" src="regexp.js"></script>
+<script type="text/javascript" src="splay.js"></script>
+<script type="text/javascript" src="navier-stokes.js"></script>
+<link type="text/css" rel="stylesheet" href="style.css" />
+<script type="text/javascript">
+var completed = 0;
+var benchmarks = BenchmarkSuite.CountBenchmarks();
+var success = true;
+
+function ShowProgress(name) {
+ var status = document.getElementById("status");
+ var percentage = ((++completed) / benchmarks) * 100;
+ status.innerHTML = "Running: " + Math.round(percentage) + "% completed.";
+}
+
+
+function AddResult(name, result) {
+ var text = name + ': ' + result;
+ var results = document.getElementById("results");
+ results.innerHTML += (text + "<br>");
+}
+
+
+function AddError(name, error) {
+ AddResult(name, '<b>error<\/b>');
+ success = false;
+}
+
+
+function AddScore(score) {
+ var status = document.getElementById("status");
+ if (success) {
+ status.innerHTML = "Score: " + score;
+ }
+}
+
+
+function Run() {
+ BenchmarkSuite.RunSuites({ NotifyStep: ShowProgress,
+ NotifyError: AddError,
+ NotifyResult: AddResult,
+ NotifyScore: AddScore });
+}
+
+function ShowWarningIfObsolete() {
+ // If anything goes wrong we will just catch the exception and no
+ // warning is shown, i.e., no harm is done.
+ try {
+ var xmlhttp;
+ var next_version = parseInt(BenchmarkSuite.version) + 1;
+ var next_version_url = "../v" + next_version + "/run.html";
+ if (window.XMLHttpRequest) {
+ xmlhttp = new window.XMLHttpRequest();
+ } else if (window.ActiveXObject) {
+ xmlhttp = new window.ActiveXObject("Microsoft.XMLHTTP");
+ }
+ xmlhttp.open('GET', next_version_url, true);
+ xmlhttp.onreadystatechange = function() {
+ if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
+ document.getElementById('obsolete').style.display="block";
+ }
+ };
+ xmlhttp.send(null);
+ } catch(e) {
+ // Ignore exception if check for next version fails.
+ // Hence no warning is displayed.
+ }
+}
+
+function Load() {
+ var version = BenchmarkSuite.version;
+ document.getElementById("version").innerHTML = version;
+ ShowWarningIfObsolete();
+ setTimeout(Run, 200);
+}
+</script>
+</head>
+<body onload="Load()">
+<div>
+ <div class="title"><h1>V8 Benchmark Suite - version <span id="version">?</span></h1></div>
+ <div class="warning" id="obsolete">
+Warning! This is not the latest version of the V8 benchmark
+suite. Consider running the
+<a href="http://v8.googlecode.com/svn/data/benchmarks/current/run.html">
+latest version</a>.
+ </div>
+ <table>
+ <tr>
+ <td class="contents">
+This page contains a suite of pure JavaScript benchmarks that we have
+used to tune V8. The final score is computed as the geometric mean of
+the individual results to make it independent of the running times of
+the individual benchmarks and of a reference system (score
+100). Scores are not comparable across benchmark suite versions and
+higher scores means better performance: <em>Bigger is better!</em>
+
+<ul>
+<li><b>Richards</b><br>OS kernel simulation benchmark, originally written in BCPL by Martin Richards (<i>539 lines</i>).</li>
+<li><b>DeltaBlue</b><br>One-way constraint solver, originally written in Smalltalk by John Maloney and Mario Wolczko (<i>880 lines</i>).</li>
+<li><b>Crypto</b><br>Encryption and decryption benchmark based on code by Tom Wu (<i>1698 lines</i>).</li>
+<li><b>RayTrace</b><br>Ray tracer benchmark based on code by <a href="http://flog.co.nz/">Adam Burmister</a> (<i>904 lines</i>).</li>
+<li><b>EarleyBoyer</b><br>Classic Scheme benchmarks, translated to JavaScript by Florian Loitsch's Scheme2Js compiler (<i>4684 lines</i>).</li>
+<li><b>RegExp</b><br>Regular expression benchmark generated by extracting regular expression operations from 50 of the most popular web pages
+(<i>1761 lines</i>).
+</li>
+<li><b>Splay</b><br>Data manipulation benchmark that deals with splay trees and exercises the automatic memory management subsystem (<i>394 lines</i>).</li>
+<li><b>NavierStokes</b><br>Solves NavierStokes equations in 2D, heavily manipulating double precision arrays. Based on Oliver Hunt's code (<i>387 lines</i>).</li>
+</ul>
+
+<p>
+Note that benchmark results are not comparable unless both results are
+run with the same revision of the benchmark suite. We will be making
+revisions from time to time in order to fix bugs or expand the scope
+of the benchmark suite. For previous revisions and the change log see
+the <a href="http://v8.googlecode.com/svn/data/benchmarks/current/revisions.html">revisions</a> page.
+</p>
+
+</td><td style="text-align: center">
+<div class="run">
+ <div id="status">Starting...</div>
+ <div id="results">
+ </div>
+</div>
+</td></tr></table>
+
+</div>
+
+</body>
+</html>
diff --git a/chromium/v8/benchmarks/run.js b/chromium/v8/benchmarks/run.js
new file mode 100644
index 00000000000..58f62658009
--- /dev/null
+++ b/chromium/v8/benchmarks/run.js
@@ -0,0 +1,62 @@
+// Copyright 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.
+
+
+load('base.js');
+load('richards.js');
+load('deltablue.js');
+load('crypto.js');
+load('raytrace.js');
+load('earley-boyer.js');
+load('regexp.js');
+load('splay.js');
+load('navier-stokes.js');
+
+var success = true;
+
+function PrintResult(name, result) {
+ print(name + ': ' + result);
+}
+
+
+function PrintError(name, error) {
+ PrintResult(name, error);
+ success = false;
+}
+
+
+function PrintScore(score) {
+ if (success) {
+ print('----');
+ print('Score (version ' + BenchmarkSuite.version + '): ' + score);
+ }
+}
+
+
+BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
+ NotifyError: PrintError,
+ NotifyScore: PrintScore });
diff --git a/chromium/v8/benchmarks/spinning-balls/index.html b/chromium/v8/benchmarks/spinning-balls/index.html
new file mode 100644
index 00000000000..d01f31f3730
--- /dev/null
+++ b/chromium/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/chromium/v8/benchmarks/spinning-balls/splay-tree.js b/chromium/v8/benchmarks/spinning-balls/splay-tree.js
new file mode 100644
index 00000000000..a88e4cbce16
--- /dev/null
+++ b/chromium/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/chromium/v8/benchmarks/spinning-balls/v.js b/chromium/v8/benchmarks/spinning-balls/v.js
new file mode 100644
index 00000000000..5ae11948d6d
--- /dev/null
+++ b/chromium/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/chromium/v8/benchmarks/splay.js b/chromium/v8/benchmarks/splay.js
new file mode 100644
index 00000000000..6b4f56d3213
--- /dev/null
+++ b/chromium/v8/benchmarks/splay.js
@@ -0,0 +1,394 @@
+// 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.
+
+// This benchmark is based on a JavaScript log processing module used
+// by the V8 profiler to generate execution time profiles for runs of
+// JavaScript applications, and it effectively measures how fast the
+// JavaScript engine is at allocating nodes and reclaiming the memory
+// used for old nodes. Because of the way splay trees work, the engine
+// also has to deal with a lot of changes to the large tree object
+// graph.
+
+var Splay = new BenchmarkSuite('Splay', 81491, [
+ new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown)
+]);
+
+
+// Configuration.
+var kSplayTreeSize = 8000;
+var kSplayTreeModifications = 80;
+var kSplayTreePayloadDepth = 5;
+
+var splayTree = null;
+
+
+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)
+ };
+ }
+}
+
+
+function GenerateKey() {
+ // The benchmark framework guarantees that Math.random is
+ // deterministic; see base.js.
+ return Math.random();
+}
+
+
+function InsertNewNode() {
+ // Insert new node with a unique key.
+ var key;
+ do {
+ key = GenerateKey();
+ } while (splayTree.find(key) != null);
+ var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key));
+ splayTree.insert(key, payload);
+ return key;
+}
+
+
+
+function SplaySetup() {
+ splayTree = new SplayTree();
+ for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode();
+}
+
+
+function SplayTearDown() {
+ // Allow the garbage collector to reclaim the memory
+ // used by the splay tree no matter how we exit the
+ // tear down function.
+ var keys = splayTree.exportKeys();
+ splayTree = null;
+
+ // Verify that the splay tree has the right size.
+ var length = keys.length;
+ if (length != kSplayTreeSize) {
+ throw new Error("Splay tree has wrong size");
+ }
+
+ // Verify that the splay tree has sorted, unique keys.
+ for (var i = 0; i < length - 1; i++) {
+ if (keys[i] >= keys[i + 1]) {
+ throw new Error("Splay tree not sorted");
+ }
+ }
+}
+
+
+function SplayRun() {
+ // Replace a few nodes in the splay tree.
+ for (var i = 0; i < kSplayTreeModifications; i++) {
+ var key = InsertNewNode();
+ var greatest = splayTree.findGreatestLessThan(key);
+ if (greatest == null) splayTree.remove(key);
+ else splayTree.remove(greatest.key);
+ }
+}
+
+
+/**
+ * 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;
+ }
+};
diff --git a/chromium/v8/benchmarks/style.css b/chromium/v8/benchmarks/style.css
new file mode 100644
index 00000000000..d9f4dbfc0c6
--- /dev/null
+++ b/chromium/v8/benchmarks/style.css
@@ -0,0 +1,77 @@
+hr {
+ border: 1px solid;
+ border-color: #36C;
+ margin: 1em 0;
+}
+
+h1, h2, h3, h4 {
+ margin: 0;
+ margin-bottom: 0;
+}
+
+h1 {
+ font-size: 154%;
+ height: 1.2em;
+}
+
+
+li {
+ margin: .3em 0 1em 0;
+}
+
+body {
+ font-family: Helvetica,Arial,sans-serif;
+ color: #000;
+ background-color: #fff;
+}
+
+div.title {
+ background-color: rgb(229, 236, 249);
+ border-top: 1px solid rgb(51, 102, 204);
+ text-align: center;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+ margin-bottom: 20px;
+}
+
+div.subtitle {
+ border-bottom: 1px solid rgb(51, 102, 204);
+ margin-top: 2em;
+}
+
+td.contents {
+ text-align: left;
+}
+
+div.run {
+ margin: 20px;
+ width: 300px;
+ height: 300px;
+ float: right;
+ background-color: rgb(229, 236, 249);
+ background-image: url(v8-logo.png);
+ background-position: center center;
+ background-repeat: no-repeat;
+ border: 1px solid rgb(51, 102, 204);
+}
+
+div.warning {
+ background: #ffffd9;
+ border: 1px solid #d2d26a;
+ display: none;
+ margin: 1em 0 2em;
+ padding: 8px;
+ text-align: center;
+}
+
+#status {
+ text-align: center;
+ margin-top: 50px;
+ font-size: 120%;
+ font-weight: bold;
+}
+
+#results {
+ text-align: left;
+ margin: 30px 0 0 90px;
+}
diff --git a/chromium/v8/benchmarks/v8-logo.png b/chromium/v8/benchmarks/v8-logo.png
new file mode 100644
index 00000000000..91867657f7a
--- /dev/null
+++ b/chromium/v8/benchmarks/v8-logo.png
Binary files differ
diff --git a/chromium/v8/build/README.txt b/chromium/v8/build/README.txt
new file mode 100644
index 00000000000..5f242ada342
--- /dev/null
+++ b/chromium/v8/build/README.txt
@@ -0,0 +1,9 @@
+For build instructions, please refer to:
+
+https://code.google.com/p/v8/wiki/BuildingWithGYP
+
+TL;DR version on *nix:
+$ make dependencies # Only needed once.
+$ make ia32.release -j8
+$ make ia32.release.check # Optionally: run tests.
+
diff --git a/chromium/v8/build/all.gyp b/chromium/v8/build/all.gyp
new file mode 100644
index 00000000000..4b2fe52989e
--- /dev/null
+++ b/chromium/v8/build/all.gyp
@@ -0,0 +1,19 @@
+# Copyright 2011 the V8 project authors. 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',
+ 'type': 'none',
+ 'dependencies': [
+ '../preparser/preparser.gyp:*',
+ '../samples/samples.gyp:*',
+ '../src/d8.gyp:d8',
+ '../test/cctest/cctest.gyp:*',
+ ],
+ }
+ ]
+}
+
diff --git a/chromium/v8/build/android.gypi b/chromium/v8/build/android.gypi
new file mode 100644
index 00000000000..ca097255bbb
--- /dev/null
+++ b/chromium/v8/build/android.gypi
@@ -0,0 +1,252 @@
+# Copyright 2012 the V8 project authors. 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': {
+ 'android_ndk_root%': '<!(/bin/echo -n $ANDROID_NDK_ROOT)',
+ 'android_toolchain%': '<!(/bin/echo -n $ANDROID_TOOLCHAIN)',
+ # This is set when building the Android WebView inside the Android build
+ # system, using the 'android' gyp backend.
+ 'android_webview_build%': 0,
+ },
+ 'conditions': [
+ ['android_ndk_root==""', {
+ 'variables': {
+ 'android_sysroot': '<(android_toolchain)/sysroot/',
+ 'android_stlport': '<(android_toolchain)/sources/cxx-stl/stlport/',
+ },
+ 'android_include': '<(android_sysroot)/usr/include',
+ 'android_lib': '<(android_sysroot)/usr/lib',
+ 'android_stlport_include': '<(android_stlport)/stlport',
+ 'android_stlport_libs': '<(android_stlport)/libs',
+ }, {
+ 'variables': {
+ 'android_sysroot': '<(android_ndk_root)/platforms/android-9/arch-<(android_target_arch)',
+ 'android_stlport': '<(android_ndk_root)/sources/cxx-stl/stlport/',
+ },
+ 'android_include': '<(android_sysroot)/usr/include',
+ 'android_lib': '<(android_sysroot)/usr/lib',
+ 'android_stlport_include': '<(android_stlport)/stlport',
+ 'android_stlport_libs': '<(android_stlport)/libs',
+ }],
+ ],
+ # Enable to use the system stlport, otherwise statically
+ # link the NDK one?
+ 'use_system_stlport%': '<(android_webview_build)',
+ 'android_stlport_library': 'stlport_static',
+ # Copy it out one scope.
+ 'android_webview_build%': '<(android_webview_build)',
+ 'OS': 'android',
+ }, # variables
+ 'target_defaults': {
+ 'defines': [
+ 'ANDROID',
+ 'V8_ANDROID_LOG_STDOUT',
+ ],
+ 'configurations': {
+ 'Release': {
+ 'cflags': [
+ '-fomit-frame-pointer',
+ ],
+ }, # 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_include)',
+ ],
+ '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',
+ ],
+ '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_webview_build==0', {
+ 'ldflags': [
+ '-Wl,-rpath-link=<(android_lib)',
+ '-L<(android_lib)',
+ ],
+ }],
+ ['target_arch == "arm"', {
+ 'ldflags': [
+ # Enable identical code folding to reduce size.
+ '-Wl,--icf=safe',
+ ],
+ }],
+ ['target_arch=="arm" and armv7==1', {
+ 'cflags': [
+ '-march=armv7-a',
+ '-mtune=cortex-a8',
+ '-mfpu=vfp3',
+ ],
+ }],
+ # 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_stlport_include)',
+ ],
+ 'conditions': [
+ ['target_arch=="arm" and armv7==1', {
+ 'ldflags': [
+ '-L<(android_stlport_libs)/armeabi-v7a',
+ ],
+ }],
+ ['target_arch=="arm" and armv7==0', {
+ 'ldflags': [
+ '-L<(android_stlport_libs)/armeabi',
+ ],
+ }],
+ ['target_arch=="mipsel"', {
+ 'ldflags': [
+ '-L<(android_stlport_libs)/mips',
+ ],
+ }],
+ ['target_arch=="ia32"', {
+ 'ldflags': [
+ '-L<(android_stlport_libs)/x86',
+ ],
+ }],
+ ],
+ }],
+ ['target_arch=="ia32"', {
+ # The x86 toolchain currently has problems with stack-protector.
+ 'cflags!': [
+ '-fstack-protector',
+ ],
+ 'cflags': [
+ '-fno-stack-protector',
+ ],
+ }],
+ ['target_arch=="mipsel"', {
+ # The mips toolchain currently has problems with stack-protector.
+ 'cflags!': [
+ '-fstack-protector',
+ '-U__linux__'
+ ],
+ '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_lib)/crtbegin_dynamic.o',
+ ],
+ 'libraries': [
+ # crtend_android.o needs to be the last item in libraries.
+ # Do not add any libraries after this!
+ '<(android_lib)/crtend_android.o',
+ ],
+ }],
+ ['_type=="shared_library"', {
+ 'ldflags': [
+ '-Wl,-shared,-Bsymbolic',
+ '<(android_lib)/crtbegin_so.o',
+ ],
+ }],
+ ['_type=="static_library"', {
+ 'ldflags': [
+ # Don't export symbols from statically linked libraries.
+ '-Wl,--exclude-libs=ALL',
+ ],
+ }],
+ ],
+ }], # _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
+}
diff --git a/chromium/v8/build/features.gypi b/chromium/v8/build/features.gypi
new file mode 100644
index 00000000000..3c6d25f7587
--- /dev/null
+++ b/chromium/v8/build/features.gypi
@@ -0,0 +1,111 @@
+# Copyright 2013 the V8 project authors. 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.
+
+# Compile time controlled V8 features.
+
+{
+ 'variables': {
+ 'v8_compress_startup_data%': 'off',
+
+ 'v8_enable_debugger_support%': 1,
+
+ 'v8_enable_disassembler%': 0,
+
+ 'v8_enable_gdbjit%': 0,
+
+ 'v8_object_print%': 0,
+
+ 'v8_enable_verify_heap%': 0,
+
+ 'v8_use_snapshot%': 'true',
+
+ # 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',
+
+ # Interpreted regexp engine exists as platform-independent alternative
+ # based where the regular expression is compiled to a bytecode.
+ 'v8_interpreted_regexp%': 0,
+
+ # Enable ECMAScript Internationalization API. Enabling this feature will
+ # add a dependency on the ICU library.
+ 'v8_enable_i18n_support%': 0,
+ },
+ 'target_defaults': {
+ 'conditions': [
+ ['v8_enable_debugger_support==1', {
+ 'defines': ['ENABLE_DEBUGGER_SUPPORT',],
+ }],
+ ['v8_enable_disassembler==1', {
+ 'defines': ['ENABLE_DISASSEMBLER',],
+ }],
+ ['v8_enable_gdbjit==1', {
+ 'defines': ['ENABLE_GDB_JIT_INTERFACE',],
+ }],
+ ['v8_object_print==1', {
+ 'defines': ['OBJECT_PRINT',],
+ }],
+ ['v8_enable_verify_heap==1', {
+ 'defines': ['VERIFY_HEAP',],
+ }],
+ ['v8_interpreted_regexp==1', {
+ 'defines': ['V8_INTERPRETED_REGEXP',],
+ }],
+ ['v8_enable_i18n_support==1', {
+ 'defines': ['V8_I18N_SUPPORT',],
+ }],
+ ['v8_compress_startup_data=="bz2"', {
+ 'defines': [
+ 'COMPRESS_STARTUP_DATA_BZ2',
+ ],
+ }],
+ ], # conditions
+ 'configurations': {
+ 'Debug': {
+ 'variables': {
+ 'v8_enable_extra_checks%': 1,
+ },
+ 'conditions': [
+ ['v8_enable_extra_checks==1', {
+ 'defines': ['ENABLE_EXTRA_CHECKS',],
+ }],
+ ],
+ }, # Debug
+ 'Release': {
+ 'variables': {
+ 'v8_enable_extra_checks%': 0,
+ },
+ 'conditions': [
+ ['v8_enable_extra_checks==1', {
+ 'defines': ['ENABLE_EXTRA_CHECKS',],
+ }],
+ ], # conditions
+ }, # Release
+ }, # configurations
+ }, # target_defaults
+}
diff --git a/chromium/v8/build/gyp_v8 b/chromium/v8/build/gyp_v8
new file mode 100755
index 00000000000..92e6503925c
--- /dev/null
+++ b/chromium/v8/build/gyp_v8
@@ -0,0 +1,171 @@
+#!/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.
+
+# This script is wrapper for V8 that adds some support for how GYP
+# is invoked by V8 beyond what can be done in the gclient hooks.
+
+import glob
+import os
+import platform
+import shlex
+import sys
+
+script_dir = os.path.dirname(__file__)
+v8_root = os.path.normpath(os.path.join(script_dir, os.pardir))
+
+if __name__ == '__main__':
+ os.chdir(v8_root)
+ script_dir = os.path.dirname(__file__)
+ v8_root = '.'
+
+sys.path.insert(0, os.path.join(v8_root, 'build', 'gyp', 'pylib'))
+import gyp
+
+# Add paths so that pymod_do_main(...) can import files.
+sys.path.insert(
+ 1, os.path.abspath(os.path.join(v8_root, 'tools', 'generate_shim_headers')))
+
+
+def apply_gyp_environment(file_path=None):
+ """
+ Reads in a *.gyp_env file and applies the valid keys to os.environ.
+ """
+ if not file_path or not os.path.exists(file_path):
+ return
+ file_contents = open(file_path).read()
+ try:
+ file_data = eval(file_contents, {'__builtins__': None}, None)
+ except SyntaxError, e:
+ e.filename = os.path.abspath(file_path)
+ raise
+ supported_vars = ( 'V8_GYP_FILE',
+ 'V8_GYP_SYNTAX_CHECK',
+ 'GYP_DEFINES',
+ 'GYP_GENERATOR_FLAGS',
+ 'GYP_GENERATOR_OUTPUT', )
+ for var in supported_vars:
+ val = file_data.get(var)
+ if val:
+ if var in os.environ:
+ print 'INFO: Environment value for "%s" overrides value in %s.' % (
+ var, os.path.abspath(file_path)
+ )
+ else:
+ os.environ[var] = val
+
+
+def additional_include_files(args=[]):
+ """
+ Returns a list of additional (.gypi) files to include, without
+ duplicating ones that are already specified on the command line.
+ """
+ # Determine the include files specified on the command line.
+ # This doesn't cover all the different option formats you can use,
+ # but it's mainly intended to avoid duplicating flags on the automatic
+ # makefile regeneration which only uses this format.
+ specified_includes = set()
+ for arg in args:
+ if arg.startswith('-I') and len(arg) > 2:
+ specified_includes.add(os.path.realpath(arg[2:]))
+
+ result = []
+ def AddInclude(path):
+ if os.path.realpath(path) not in specified_includes:
+ result.append(path)
+
+ # Always include standalone.gypi
+ AddInclude(os.path.join(v8_root, 'build', 'standalone.gypi'))
+
+ # Optionally add supplemental .gypi files if present.
+ supplements = glob.glob(os.path.join(v8_root, '*', 'supplement.gypi'))
+ for supplement in supplements:
+ AddInclude(supplement)
+
+ return result
+
+
+def run_gyp(args):
+ rc = gyp.main(args)
+ if rc != 0:
+ print 'Error running GYP'
+ sys.exit(rc)
+
+
+if __name__ == '__main__':
+ args = sys.argv[1:]
+
+ if 'SKIP_V8_GYP_ENV' not in os.environ:
+ # Update the environment based on v8.gyp_env
+ gyp_env_path = os.path.join(os.path.dirname(v8_root), 'v8.gyp_env')
+ apply_gyp_environment(gyp_env_path)
+
+ # This could give false positives since it doesn't actually do real option
+ # parsing. Oh well.
+ gyp_file_specified = False
+ for arg in args:
+ if arg.endswith('.gyp'):
+ gyp_file_specified = True
+ break
+
+ # If we didn't get a file, check an env var, and then fall back to
+ # assuming 'all.gyp' from the same directory as the script.
+ if not gyp_file_specified:
+ gyp_file = os.environ.get('V8_GYP_FILE')
+ if gyp_file:
+ # Note that V8_GYP_FILE values can't have backslashes as
+ # path separators even on Windows due to the use of shlex.split().
+ args.extend(shlex.split(gyp_file))
+ else:
+ # Note that this must not start with "./" or things break.
+ # So we rely on having done os.chdir(v8_root) above and use the
+ # relative path.
+ args.append(os.path.join('build', 'all.gyp'))
+
+ args.extend(['-I' + i for i in additional_include_files(args)])
+
+ # There shouldn't be a circular dependency relationship between .gyp files
+ args.append('--no-circular-check')
+
+ # Set the GYP DEPTH variable to the root of the V8 project.
+ args.append('--depth=' + v8_root)
+
+ # If V8_GYP_SYNTAX_CHECK is set to 1, it will invoke gyp with --check
+ # to enfore syntax checking.
+ syntax_check = os.environ.get('V8_GYP_SYNTAX_CHECK')
+ if syntax_check and int(syntax_check):
+ args.append('--check')
+
+ print 'Updating projects from gyp files...'
+ sys.stdout.flush()
+
+ # Generate for the architectures supported on the given platform.
+ gyp_args = list(args)
+ if platform.system() == 'Linux':
+ gyp_args.append('--generator-output=out')
+ run_gyp(gyp_args)
diff --git a/chromium/v8/build/shim_headers.gypi b/chromium/v8/build/shim_headers.gypi
new file mode 100644
index 00000000000..940211c2401
--- /dev/null
+++ b/chromium/v8/build/shim_headers.gypi
@@ -0,0 +1,73 @@
+# Copyright 2013 the V8 project authors. 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 is meant to be included into a target to handle shim headers
+# in a consistent manner. To use this the following variables need to be
+# defined:
+# headers_root_path: string: path to directory containing headers
+# header_filenames: list: list of header file names
+
+{
+ 'variables': {
+ 'shim_headers_path': '<(SHARED_INTERMEDIATE_DIR)/shim_headers/<(_target_name)/<(_toolset)',
+ 'shim_generator_additional_args%': [],
+ },
+ 'include_dirs++': [
+ '<(shim_headers_path)',
+ ],
+ 'all_dependent_settings': {
+ 'include_dirs+++': [
+ '<(shim_headers_path)',
+ ],
+ },
+ 'actions': [
+ {
+ 'variables': {
+ 'generator_path': '<(DEPTH)/tools/generate_shim_headers/generate_shim_headers.py',
+ 'generator_args': [
+ '--headers-root', '<(headers_root_path)',
+ '--output-directory', '<(shim_headers_path)',
+ '<@(shim_generator_additional_args)',
+ '<@(header_filenames)',
+ ],
+ },
+ 'action_name': 'generate_<(_target_name)_shim_headers',
+ 'inputs': [
+ '<(generator_path)',
+ ],
+ 'outputs': [
+ '<!@pymod_do_main(generate_shim_headers <@(generator_args) --outputs)',
+ ],
+ 'action': ['python',
+ '<(generator_path)',
+ '<@(generator_args)',
+ '--generate',
+ ],
+ 'message': 'Generating <(_target_name) shim headers.',
+ },
+ ],
+}
diff --git a/chromium/v8/build/standalone.gypi b/chromium/v8/build/standalone.gypi
new file mode 100644
index 00000000000..5c017d5f507
--- /dev/null
+++ b/chromium/v8/build/standalone.gypi
@@ -0,0 +1,285 @@
+# Copyright 2012 the V8 project authors. 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 to be used when building stand-alone V8 binaries.
+
+{
+ # We need to include toolchain.gypi here for third-party sources that don't
+ # directly include it themselves.
+ 'includes': ['toolchain.gypi'],
+ 'variables': {
+ 'component%': 'static_library',
+ 'clang%': 0,
+ 'visibility%': 'hidden',
+ 'v8_enable_backtrace%': 0,
+ 'v8_enable_i18n_support%': 0,
+ 'msvs_multi_core_compile%': '1',
+ 'mac_deployment_target%': '10.5',
+ 'variables': {
+ 'variables': {
+ 'variables': {
+ 'conditions': [
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or \
+ OS=="netbsd" or OS=="mac"', {
+ # This handles the Unix 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.*/mipsel/")',
+ }, {
+ # OS!="linux" and OS!="freebsd" and OS!="openbsd" and
+ # OS!="netbsd" and OS!="mac"
+ 'host_arch%': 'ia32',
+ }],
+ ],
+ },
+ 'host_arch%': '<(host_arch)',
+ 'target_arch%': '<(host_arch)',
+ },
+ 'host_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',
+
+ # .gyp files or targets should set v8_code to 1 if they build V8 specific
+ # code, as opposed to external code. This variable is used to control such
+ # things as the set of warnings to enable, and whether warnings are treated
+ # as errors.
+ 'v8_code%': 0,
+
+ 'conditions': [
+ ['(v8_target_arch=="arm" and host_arch!="arm") or \
+ (v8_target_arch=="mipsel" and host_arch!="mipsel") or \
+ (v8_target_arch=="x64" and host_arch!="x64") or \
+ (OS=="android")', {
+ 'want_separate_host_toolset': 1,
+ }, {
+ 'want_separate_host_toolset': 0,
+ }],
+ ['OS == "win"', {
+ 'os_posix%': 0,
+ }, {
+ 'os_posix%': 1,
+ }],
+ ['(v8_target_arch=="ia32" or v8_target_arch=="x64") and \
+ (OS=="linux" or OS=="mac")', {
+ 'v8_enable_gdbjit%': 1,
+ }, {
+ 'v8_enable_gdbjit%': 0,
+ }],
+ ],
+ # Default ARM variable settings.
+ 'armv7%': 'default',
+ 'arm_neon%': 0,
+ 'arm_fpu%': 'vfpv3',
+ 'arm_float_abi%': 'default',
+ 'arm_thumb': 'default',
+ },
+ 'target_defaults': {
+ 'variables': {
+ 'v8_code%': '<(v8_code)',
+ },
+ 'default_configuration': 'Debug',
+ 'configurations': {
+ 'Debug': {
+ 'cflags': [ '-g', '-O0' ],
+ },
+ 'Release': {
+ # Xcode insists on this empty entry.
+ },
+ },
+ 'target_conditions': [
+ ['v8_code == 0', {
+ 'defines!': [
+ 'DEBUG',
+ ],
+ 'conditions': [
+ ['os_posix == 1 and OS != "mac"', {
+ 'cflags!': [
+ '-Werror',
+ ],
+ }],
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO', # -Werror
+ },
+ }],
+ ['OS == "win"', {
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'WarnAsError': 'false',
+ },
+ },
+ }],
+ ],
+ }],
+ ],
+ },
+ 'conditions': [
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris" \
+ or OS=="netbsd"', {
+ 'target_defaults': {
+ 'cflags': [ '-Wall', '<(werror)', '-W', '-Wno-unused-parameter',
+ '-pthread', '-fno-exceptions', '-pedantic' ],
+ 'cflags_cc': [ '-Wnon-virtual-dtor', '-fno-rtti' ],
+ 'ldflags': [ '-pthread', ],
+ 'conditions': [
+ [ 'OS=="linux"', {
+ 'cflags': [ '-ansi' ],
+ }],
+ [ 'visibility=="hidden" and v8_enable_backtrace==0', {
+ 'cflags': [ '-fvisibility=hidden' ],
+ }],
+ [ 'component=="shared_library"', {
+ 'cflags': [ '-fPIC', ],
+ }],
+ ],
+ },
+ }],
+ # 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"
+ # or OS=="netbsd"'
+ ['OS=="win"', {
+ 'target_defaults': {
+ 'defines': [
+ '_CRT_SECURE_NO_DEPRECATE',
+ '_CRT_NONSTDC_NO_DEPRECATE',
+ ],
+ 'conditions': [
+ ['component=="static_library"', {
+ 'defines': [
+ '_HAS_EXCEPTIONS=0',
+ ],
+ }],
+ ],
+ 'msvs_cygwin_dirs': ['<(DEPTH)/third_party/cygwin'],
+ 'msvs_disabled_warnings': [4355, 4800],
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'MinimalRebuild': 'false',
+ 'BufferSecurityCheck': 'true',
+ 'EnableFunctionLevelLinking': 'true',
+ 'RuntimeTypeInfo': 'false',
+ 'WarningLevel': '3',
+ 'WarnAsError': 'true',
+ 'DebugInformationFormat': '3',
+ 'Detect64BitPortabilityProblems': 'false',
+ 'conditions': [
+ [ 'msvs_multi_core_compile', {
+ 'AdditionalOptions': ['/MP'],
+ }],
+ ['component=="shared_library"', {
+ 'ExceptionHandling': '1', # /EHsc
+ }, {
+ 'ExceptionHandling': '0',
+ }],
+ ],
+ },
+ 'VCLibrarianTool': {
+ 'AdditionalOptions': ['/ignore:4221'],
+ },
+ 'VCLinkerTool': {
+ 'AdditionalDependencies': [
+ 'ws2_32.lib',
+ ],
+ 'GenerateDebugInformation': 'true',
+ 'MapFileName': '$(OutDir)\\$(TargetName).map',
+ 'ImportLibrary': '$(OutDir)\\lib\\$(TargetName).lib',
+ 'FixedBaseAddress': '1',
+ # LinkIncremental values:
+ # 0 == default
+ # 1 == /INCREMENTAL:NO
+ # 2 == /INCREMENTAL
+ 'LinkIncremental': '1',
+ # SubSystem values:
+ # 0 == not set
+ # 1 == /SUBSYSTEM:CONSOLE
+ # 2 == /SUBSYSTEM:WINDOWS
+ 'SubSystem': '1',
+
+ 'conditions': [
+ ['v8_enable_i18n_support==1', {
+ 'AdditionalDependencies': [
+ 'advapi32.lib',
+ ],
+ }],
+ ],
+ },
+ },
+ },
+ }], # OS=="win"
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'SYMROOT': '<(DEPTH)/xcodebuild',
+ },
+ 'target_defaults': {
+ 'xcode_settings': {
+ 'ALWAYS_SEARCH_USER_PATHS': 'NO',
+ 'GCC_C_LANGUAGE_STANDARD': 'ansi', # -ansi
+ 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks
+ 'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic
+ # (Equivalent to -fPIC)
+ '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_TREAT_WARNINGS_AS_ERRORS': 'YES', # -Werror
+ 'GCC_VERSION': 'com.apple.compilers.llvmgcc42',
+ 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof
+ 'GCC_WARN_NON_VIRTUAL_DESTRUCTOR': 'YES', # -Wnon-virtual-dtor
+ # MACOSX_DEPLOYMENT_TARGET maps to -mmacosx-version-min
+ 'MACOSX_DEPLOYMENT_TARGET': '<(mac_deployment_target)',
+ 'PREBINDING': 'NO', # No -Wl,-prebind
+ 'SYMROOT': '<(DEPTH)/xcodebuild',
+ 'USE_HEADERMAP': 'NO',
+ 'OTHER_CFLAGS': [
+ '-fno-strict-aliasing',
+ ],
+ 'WARNING_CFLAGS': [
+ '-Wall',
+ '-Wendif-labels',
+ '-W',
+ '-Wno-unused-parameter',
+ ],
+ },
+ 'target_conditions': [
+ ['_type!="static_library"', {
+ 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']},
+ }],
+ ], # target_conditions
+ }, # target_defaults
+ }], # OS=="mac"
+ ],
+}
diff --git a/chromium/v8/build/toolchain.gypi b/chromium/v8/build/toolchain.gypi
new file mode 100644
index 00000000000..ddb8aafad0c
--- /dev/null
+++ b/chromium/v8/build/toolchain.gypi
@@ -0,0 +1,696 @@
+# Copyright 2013 the V8 project authors. 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.
+
+# Shared definitions for all V8-related targets.
+
+{
+ 'variables': {
+ 'msvs_use_common_release': 0,
+ 'gcc_version%': 'unknown',
+ 'CXX%': '${CXX:-$(which g++)}', # Used to assemble a shell command.
+ 'v8_target_arch%': '<(target_arch)',
+ # Native Client builds currently use the V8 ARM JIT and
+ # arm/simulator-arm.cc to defer the significant effort required
+ # for NaCl JIT support. The nacl_target_arch variable provides
+ # the 'true' target arch for places in this file that need it.
+ # TODO(bradchen): get rid of nacl_target_arch when someday
+ # NaCl V8 builds stop using the ARM simulator
+ 'nacl_target_arch%': 'none', # must be set externally
+
+ # Setting 'v8_can_use_vfp32dregs' to 'true' will cause V8 to use the VFP
+ # registers d16-d31 in the generated code, both in the snapshot and for the
+ # ARM target. Leaving the default value of 'false' will avoid the use of
+ # these registers in the snapshot and use CPU feature probing when running
+ # on the target.
+ 'v8_can_use_vfp32dregs%': 'false',
+ 'arm_test%': 'off',
+
+ # Similar to vfp but on MIPS.
+ 'v8_can_use_fpu_instructions%': 'true',
+
+ # Similar to the ARM hard float ABI but on MIPS.
+ 'v8_use_mips_abi_hardfloat%': 'true',
+
+ # Default arch variant for MIPS.
+ 'mips_arch_variant%': 'mips32r2',
+
+ 'v8_enable_backtrace%': 0,
+
+ # Speeds up Debug builds:
+ # 0 - Compiler optimizations off (debuggable) (default). This may
+ # be 5x slower than Release (or worse).
+ # 1 - Turn on compiler optimizations. This may be hard or impossible to
+ # debug. This may still be 2x slower than Release (or worse).
+ # 2 - Turn on optimizations, and also #undef DEBUG / #define NDEBUG
+ # (but leave V8_ENABLE_CHECKS and most other assertions enabled.
+ # This may cause some v8 tests to fail in the Debug configuration.
+ # This roughly matches the performance of a Release build and can
+ # be used by embedders that need to build their own code as debug
+ # but don't want or need a debug version of V8. This should produce
+ # near-release speeds.
+ 'v8_optimized_debug%': 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,
+
+ 'host_os%': '<(OS)',
+ 'werror%': '-Werror',
+ # For a shared library build, results in "libv8-<(soname_version).so".
+ 'soname_version%': '',
+
+ # Allow to suppress the array bounds warning (default is no suppression).
+ 'wno_array_bounds%': '',
+ },
+ 'target_defaults': {
+ 'conditions': [
+ ['v8_target_arch=="arm"', {
+ 'defines': [
+ 'V8_TARGET_ARCH_ARM',
+ ],
+ 'target_conditions': [
+ ['_toolset=="host"', {
+ 'variables': {
+ 'armcompiler': '<!($(echo ${CXX_host:-$(which g++)}) -v 2>&1 | grep -q "^Target: arm" && echo "yes" || echo "no")',
+ },
+ 'conditions': [
+ ['armcompiler=="yes"', {
+ 'conditions': [
+ [ 'armv7==1', {
+ 'cflags': ['-march=armv7-a',],
+ }],
+ [ 'armv7==1 or armv7=="default"', {
+ 'conditions': [
+ [ 'arm_neon==1', {
+ 'cflags': ['-mfpu=neon',],
+ },
+ {
+ 'conditions': [
+ [ 'arm_fpu!="default"', {
+ 'cflags': ['-mfpu=<(arm_fpu)',],
+ }],
+ ],
+ }],
+ ],
+ }],
+ [ 'arm_float_abi!="default"', {
+ 'cflags': ['-mfloat-abi=<(arm_float_abi)',],
+ }],
+ [ 'arm_thumb==1', {
+ 'cflags': ['-mthumb',],
+ }],
+ [ 'arm_thumb==0', {
+ 'cflags': ['-marm',],
+ }],
+ [ 'arm_test=="on"', {
+ 'defines': [
+ 'ARM_TEST',
+ ],
+ }],
+ ],
+ }, {
+ # armcompiler=="no"
+ 'conditions': [
+ [ 'armv7==1 or armv7=="default"', {
+ 'defines': [
+ 'CAN_USE_ARMV7_INSTRUCTIONS=1',
+ ],
+ 'conditions': [
+ [ 'arm_fpu=="default"', {
+ 'defines': [
+ 'CAN_USE_VFP3_INSTRUCTIONS',
+ ],
+ }],
+ [ 'arm_fpu=="vfpv3-d16"', {
+ 'defines': [
+ 'CAN_USE_VFP3_INSTRUCTIONS',
+ ],
+ }],
+ [ 'arm_fpu=="vfpv3"', {
+ 'defines': [
+ 'CAN_USE_VFP3_INSTRUCTIONS',
+ 'CAN_USE_VFP32DREGS',
+ ],
+ }],
+ [ 'arm_fpu=="neon" or arm_neon==1', {
+ 'defines': [
+ 'CAN_USE_VFP3_INSTRUCTIONS',
+ 'CAN_USE_VFP32DREGS',
+ ],
+ }],
+ ],
+ }],
+ [ 'arm_float_abi=="hard"', {
+ 'defines': [
+ 'USE_EABI_HARDFLOAT=1',
+ ],
+ }],
+ [ 'arm_float_abi=="softfp" or arm_float_abi=="default"', {
+ 'defines': [
+ 'USE_EABI_HARDFLOAT=0',
+ ],
+ }],
+ ],
+ 'defines': [
+ 'ARM_TEST',
+ ],
+ }],
+ ],
+ }], # _toolset=="host"
+ ['_toolset=="target"', {
+ 'variables': {
+ 'armcompiler': '<!($(echo ${CXX_target:-<(CXX)}) -v 2>&1 | grep -q "^Target: arm" && echo "yes" || echo "no")',
+ },
+ 'conditions': [
+ ['armcompiler=="yes"', {
+ 'conditions': [
+ [ 'armv7==1', {
+ 'cflags': ['-march=armv7-a',],
+ }],
+ [ 'armv7==1 or armv7=="default"', {
+ 'conditions': [
+ [ 'arm_neon==1', {
+ 'cflags': ['-mfpu=neon',],
+ },
+ {
+ 'conditions': [
+ [ 'arm_fpu!="default"', {
+ 'cflags': ['-mfpu=<(arm_fpu)',],
+ }],
+ ],
+ }],
+ ],
+ }],
+ [ 'arm_float_abi!="default"', {
+ 'cflags': ['-mfloat-abi=<(arm_float_abi)',],
+ }],
+ [ 'arm_thumb==1', {
+ 'cflags': ['-mthumb',],
+ }],
+ [ 'arm_thumb==0', {
+ 'cflags': ['-marm',],
+ }],
+ [ 'arm_test=="on"', {
+ 'defines': [
+ 'ARM_TEST',
+ ],
+ }],
+ ],
+ }, {
+ # armcompiler=="no"
+ 'conditions': [
+ [ 'armv7==1 or armv7=="default"', {
+ 'defines': [
+ 'CAN_USE_ARMV7_INSTRUCTIONS=1',
+ ],
+ 'conditions': [
+ [ 'arm_fpu=="default"', {
+ 'defines': [
+ 'CAN_USE_VFP3_INSTRUCTIONS',
+ ],
+ }],
+ [ 'arm_fpu=="vfpv3-d16"', {
+ 'defines': [
+ 'CAN_USE_VFP3_INSTRUCTIONS',
+ ],
+ }],
+ [ 'arm_fpu=="vfpv3"', {
+ 'defines': [
+ 'CAN_USE_VFP3_INSTRUCTIONS',
+ 'CAN_USE_VFP32DREGS',
+ ],
+ }],
+ [ 'arm_fpu=="neon" or arm_neon==1', {
+ 'defines': [
+ 'CAN_USE_VFP3_INSTRUCTIONS',
+ 'CAN_USE_VFP32DREGS',
+ ],
+ }],
+ ],
+ }],
+ [ 'arm_float_abi=="hard"', {
+ 'defines': [
+ 'USE_EABI_HARDFLOAT=1',
+ ],
+ }],
+ [ 'arm_float_abi=="softfp" or arm_float_abi=="default"', {
+ 'defines': [
+ 'USE_EABI_HARDFLOAT=0',
+ ],
+ }],
+ ],
+ 'defines': [
+ 'ARM_TEST',
+ ],
+ }],
+ ],
+ }], # _toolset=="target"
+ ],
+ }], # v8_target_arch=="arm"
+ ['v8_target_arch=="ia32"', {
+ 'defines': [
+ 'V8_TARGET_ARCH_IA32',
+ ],
+ }], # v8_target_arch=="ia32"
+ ['v8_target_arch=="mipsel"', {
+ 'defines': [
+ 'V8_TARGET_ARCH_MIPS',
+ ],
+ 'variables': {
+ 'mipscompiler': '<!($(echo <(CXX)) -v 2>&1 | grep -q "^Target: mips" && echo "yes" || echo "no")',
+ },
+ 'conditions': [
+ ['mipscompiler=="yes"', {
+ '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'],
+ }],
+ ['mips_arch_variant=="mips32r1"', {
+ 'cflags': ['-mips32', '-Wa,-mips32'],
+ }],
+ ['mips_arch_variant=="loongson"', {
+ 'cflags': ['-mips3', '-Wa,-mips3'],
+ }],
+ ],
+ }],
+ ],
+ }],
+ [ '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',],
+ }],
+ ['mips_arch_variant=="loongson"', {
+ 'defines': ['_MIPS_ARCH_LOONGSON',],
+ }],
+ ],
+ }], # v8_target_arch=="mipsel"
+ ['v8_target_arch=="x64"', {
+ 'defines': [
+ 'V8_TARGET_ARCH_X64',
+ ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'x86_64' ],
+ },
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'StackReserveSize': '2097152',
+ },
+ },
+ 'msvs_configuration_platform': 'x64',
+ }], # v8_target_arch=="x64"
+ ['OS=="win"', {
+ 'defines': [
+ 'WIN32',
+ ],
+ 'msvs_configuration_attributes': {
+ 'OutputDirectory': '<(DEPTH)\\build\\$(ConfigurationName)',
+ 'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)',
+ 'CharacterSet': '1',
+ },
+ }],
+ ['OS=="win" and v8_enable_prof==1', {
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'GenerateMapFile': 'true',
+ },
+ },
+ }],
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris" \
+ or OS=="netbsd"', {
+ 'conditions': [
+ [ 'v8_no_strict_aliasing==1', {
+ 'cflags': [ '-fno-strict-aliasing' ],
+ }],
+ ], # conditions
+ }],
+ ['OS=="solaris"', {
+ 'defines': [ '__C99FEATURES__=1' ], # isinf() etc.
+ }],
+ ['(OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris" \
+ or OS=="netbsd" or OS=="mac" or OS=="android") and \
+ (v8_target_arch=="arm" or v8_target_arch=="ia32" or \
+ v8_target_arch=="mipsel")', {
+ # Check whether the host compiler and target compiler support the
+ # '-m32' option and set it if so.
+ 'target_conditions': [
+ ['_toolset=="host"', {
+ 'variables': {
+ 'm32flag': '<!((echo | $(echo ${CXX_host:-$(which g++)}) -m32 -E - > /dev/null 2>&1) && echo "-m32" || true)',
+ },
+ 'cflags': [ '<(m32flag)' ],
+ 'ldflags': [ '<(m32flag)' ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386' ],
+ },
+ }],
+ ['_toolset=="target"', {
+ 'variables': {
+ 'm32flag': '<!((echo | $(echo ${CXX_target:-<(CXX)}) -m32 -E - > /dev/null 2>&1) && echo "-m32" || true)',
+ 'clang%': 0,
+ },
+ 'conditions': [
+ ['(OS!="android" or clang==1) and \
+ nacl_target_arch!="nacl_x64"', {
+ 'cflags': [ '<(m32flag)' ],
+ 'ldflags': [ '<(m32flag)' ],
+ }],
+ ],
+ 'xcode_settings': {
+ 'ARCHS': [ 'i386' ],
+ },
+ }],
+ ],
+ }],
+ ['(OS=="linux") and (v8_target_arch=="x64")', {
+ # Check whether the host compiler and target compiler support the
+ # '-m64' option and set it if so.
+ 'target_conditions': [
+ ['_toolset=="host"', {
+ 'variables': {
+ 'm64flag': '<!((echo | $(echo ${CXX_host:-$(which g++)}) -m64 -E - > /dev/null 2>&1) && echo "-m64" || true)',
+ },
+ 'cflags': [ '<(m64flag)' ],
+ 'ldflags': [ '<(m64flag)' ],
+ }],
+ ['_toolset=="target"', {
+ 'variables': {
+ 'm64flag': '<!((echo | $(echo ${CXX_target:-<(CXX)}) -m64 -E - > /dev/null 2>&1) && echo "-m64" || true)',
+ },
+ 'cflags': [ '<(m64flag)' ],
+ 'ldflags': [ '<(m64flag)' ],
+ }]
+ ],
+ }],
+ ['OS=="freebsd" or OS=="openbsd"', {
+ 'cflags': [ '-I/usr/local/include' ],
+ }],
+ ['OS=="netbsd"', {
+ 'cflags': [ '-I/usr/pkg/include' ],
+ }],
+ ], # conditions
+ 'configurations': {
+ 'Debug': {
+ 'defines': [
+ 'ENABLE_DISASSEMBLER',
+ 'V8_ENABLE_CHECKS',
+ 'OBJECT_PRINT',
+ 'VERIFY_HEAP',
+ ],
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'conditions': [
+ ['v8_optimized_debug==0', {
+ 'Optimization': '0',
+ 'conditions': [
+ ['component=="shared_library"', {
+ 'RuntimeLibrary': '3', # /MDd
+ }, {
+ 'RuntimeLibrary': '1', # /MTd
+ }],
+ ],
+ }],
+ ['v8_optimized_debug==1', {
+ 'Optimization': '1',
+ 'InlineFunctionExpansion': '2',
+ 'EnableIntrinsicFunctions': 'true',
+ 'FavorSizeOrSpeed': '0',
+ 'StringPooling': 'true',
+ 'BasicRuntimeChecks': '0',
+ 'conditions': [
+ ['component=="shared_library"', {
+ 'RuntimeLibrary': '3', # /MDd
+ }, {
+ 'RuntimeLibrary': '1', # /MTd
+ }],
+ ],
+ }],
+ ['v8_optimized_debug==2', {
+ 'Optimization': '2',
+ 'InlineFunctionExpansion': '2',
+ 'EnableIntrinsicFunctions': 'true',
+ 'FavorSizeOrSpeed': '0',
+ 'StringPooling': 'true',
+ 'BasicRuntimeChecks': '0',
+ 'conditions': [
+ ['component=="shared_library"', {
+ 'RuntimeLibrary': '3', #/MDd
+ }, {
+ 'RuntimeLibrary': '1', #/MTd
+ }],
+ ['v8_target_arch=="x64"', {
+ # TODO(2207): remove this option once the bug is fixed.
+ 'WholeProgramOptimization': 'true',
+ }],
+ ],
+ }],
+ ],
+ },
+ 'VCLinkerTool': {
+ 'conditions': [
+ ['v8_optimized_debug==0', {
+ 'LinkIncremental': '2',
+ }],
+ ['v8_optimized_debug==1', {
+ 'LinkIncremental': '2',
+ }],
+ ['v8_optimized_debug==2', {
+ 'LinkIncremental': '1',
+ 'OptimizeReferences': '2',
+ 'EnableCOMDATFolding': '2',
+ }],
+ ],
+ },
+ },
+ 'conditions': [
+ ['v8_optimized_debug==2', {
+ 'defines': [
+ 'NDEBUG',
+ ],
+ }, {
+ 'defines': [
+ 'DEBUG',
+ ],
+ }],
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd"', {
+ 'cflags': [ '-Wall', '<(werror)', '-W', '-Wno-unused-parameter',
+ '-Wnon-virtual-dtor', '-Woverloaded-virtual',
+ '<(wno_array_bounds)' ],
+ 'conditions': [
+ ['v8_optimized_debug==0', {
+ 'cflags!': [
+ '-O0',
+ '-O3',
+ '-O2',
+ '-O1',
+ '-Os',
+ ],
+ 'cflags': [
+ '-fdata-sections',
+ '-ffunction-sections',
+ ],
+ }],
+ ['v8_optimized_debug==1', {
+ 'cflags!': [
+ '-O0',
+ '-O3', # TODO(2807) should be -O1.
+ '-O2',
+ '-Os',
+ ],
+ 'cflags': [
+ '-fdata-sections',
+ '-ffunction-sections',
+ '-O1', # TODO(2807) should be -O3.
+ ],
+ }],
+ ['v8_optimized_debug==2', {
+ 'cflags!': [
+ '-O0',
+ '-O1',
+ '-O2',
+ '-Os',
+ ],
+ 'cflags': [
+ '-fdata-sections',
+ '-ffunction-sections',
+ '-O3',
+ ],
+ }],
+ ['v8_optimized_debug!=0 and gcc_version==44 and clang==0', {
+ 'cflags': [
+ # Avoid crashes with gcc 4.4 in the v8 test suite.
+ '-fno-tree-vrp',
+ ],
+ }],
+ ],
+ }],
+ ['OS=="linux" and v8_enable_backtrace==1', {
+ # Support for backtrace_symbols.
+ 'ldflags': [ '-rdynamic' ],
+ }],
+ ['OS=="android"', {
+ 'variables': {
+ 'android_full_debug%': 1,
+ },
+ 'conditions': [
+ ['android_full_debug==0', {
+ # Disable full debug if we want a faster v8 in a debug build.
+ # TODO(2304): pass DISABLE_DEBUG_ASSERT instead of hiding DEBUG.
+ 'defines!': [
+ 'DEBUG',
+ ],
+ }],
+ ],
+ }],
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'conditions': [
+ ['v8_optimized_debug==0', {
+ 'GCC_OPTIMIZATION_LEVEL': '0', # -O0
+ }, {
+ 'GCC_OPTIMIZATION_LEVEL': '3', # -O3
+ 'GCC_STRICT_ALIASING': 'YES',
+ }],
+ ],
+ },
+ }],
+ ],
+ }, # Debug
+ 'Release': {
+ 'conditions': [
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd"', {
+ 'cflags!': [
+ '-O2',
+ '-Os',
+ ],
+ 'cflags': [
+ '-fdata-sections',
+ '-ffunction-sections',
+ '-O3',
+ '<(wno_array_bounds)',
+ ],
+ 'conditions': [
+ [ 'gcc_version==44 and clang==0', {
+ 'cflags': [
+ # Avoid crashes with gcc 4.4 in the v8 test suite.
+ '-fno-tree-vrp',
+ ],
+ }],
+ ],
+ }],
+ ['OS=="android"', {
+ 'cflags!': [
+ '-O3',
+ '-Os',
+ ],
+ 'cflags': [
+ '-fdata-sections',
+ '-ffunction-sections',
+ '-O2',
+ ],
+ 'conditions': [
+ [ 'gcc_version==44 and clang==0', {
+ 'cflags': [
+ # Avoid crashes with gcc 4.4 in the v8 test suite.
+ '-fno-tree-vrp',
+ ],
+ }],
+ ],
+ }],
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'GCC_OPTIMIZATION_LEVEL': '3', # -O3
+
+ # -fstrict-aliasing. Mainline gcc
+ # enables this at -O2 and above,
+ # but Apple gcc does not unless it
+ # is specified explicitly.
+ 'GCC_STRICT_ALIASING': 'YES',
+ },
+ }], # OS=="mac"
+ ['OS=="win"', {
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'Optimization': '2',
+ 'InlineFunctionExpansion': '2',
+ 'EnableIntrinsicFunctions': 'true',
+ 'FavorSizeOrSpeed': '0',
+ 'StringPooling': 'true',
+ 'conditions': [
+ ['component=="shared_library"', {
+ 'RuntimeLibrary': '2', #/MD
+ }, {
+ 'RuntimeLibrary': '0', #/MT
+ }],
+ ['v8_target_arch=="x64"', {
+ # TODO(2207): remove this option once the bug is fixed.
+ 'WholeProgramOptimization': 'true',
+ }],
+ ],
+ },
+ 'VCLinkerTool': {
+ 'LinkIncremental': '1',
+ 'OptimizeReferences': '2',
+ 'EnableCOMDATFolding': '2',
+ },
+ },
+ }], # OS=="win"
+ ], # conditions
+ }, # Release
+ }, # configurations
+ }, # target_defaults
+}
diff --git a/chromium/v8/include/v8-debug.h b/chromium/v8/include/v8-debug.h
new file mode 100755
index 00000000000..bacccb61ddf
--- /dev/null
+++ b/chromium/v8/include/v8-debug.h
@@ -0,0 +1,376 @@
+// Copyright 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_V8_DEBUG_H_
+#define V8_V8_DEBUG_H_
+
+#include "v8.h"
+
+/**
+ * Debugger support for the V8 JavaScript engine.
+ */
+namespace v8 {
+
+// Debug events which can occur in the V8 JavaScript engine.
+enum DebugEvent {
+ Break = 1,
+ Exception = 2,
+ NewFunction = 3,
+ BeforeCompile = 4,
+ AfterCompile = 5,
+ ScriptCollected = 6,
+ BreakForCommand = 7
+};
+
+
+class V8_EXPORT Debug {
+ public:
+ /**
+ * A client object passed to the v8 debugger whose ownership will be taken by
+ * it. v8 is always responsible for deleting the object.
+ */
+ class ClientData {
+ public:
+ virtual ~ClientData() {}
+ };
+
+
+ /**
+ * A message object passed to the debug message handler.
+ */
+ class Message {
+ public:
+ /**
+ * Check type of message.
+ */
+ virtual bool IsEvent() const = 0;
+ virtual bool IsResponse() const = 0;
+ virtual DebugEvent GetEvent() const = 0;
+
+ /**
+ * Indicate whether this is a response to a continue command which will
+ * start the VM running after this is processed.
+ */
+ virtual bool WillStartRunning() const = 0;
+
+ /**
+ * Access to execution state and event data. Don't store these cross
+ * callbacks as their content becomes invalid. These objects are from the
+ * debugger event that started the debug message loop.
+ */
+ virtual Handle<Object> GetExecutionState() const = 0;
+ virtual Handle<Object> GetEventData() const = 0;
+
+ /**
+ * Get the debugger protocol JSON.
+ */
+ virtual Handle<String> GetJSON() const = 0;
+
+ /**
+ * Get the context active when the debug event happened. Note this is not
+ * the current active context as the JavaScript part of the debugger is
+ * running in its own context which is entered at this point.
+ */
+ virtual Handle<Context> GetEventContext() const = 0;
+
+ /**
+ * Client data passed with the corresponding request if any. This is the
+ * client_data data value passed into Debug::SendCommand along with the
+ * request that led to the message or NULL if the message is an event. The
+ * debugger takes ownership of the data and will delete it even if there is
+ * no message handler.
+ */
+ virtual ClientData* GetClientData() const = 0;
+
+ virtual ~Message() {}
+ };
+
+
+ /**
+ * An event details object passed to the debug event listener.
+ */
+ class EventDetails {
+ public:
+ /**
+ * Event type.
+ */
+ virtual DebugEvent GetEvent() const = 0;
+
+ /**
+ * Access to execution state and event data of the debug event. Don't store
+ * these cross callbacks as their content becomes invalid.
+ */
+ virtual Handle<Object> GetExecutionState() const = 0;
+ virtual Handle<Object> GetEventData() const = 0;
+
+ /**
+ * Get the context active when the debug event happened. Note this is not
+ * the current active context as the JavaScript part of the debugger is
+ * running in its own context which is entered at this point.
+ */
+ virtual Handle<Context> GetEventContext() const = 0;
+
+ /**
+ * Client data passed with the corresponding callback when it was
+ * registered.
+ */
+ virtual Handle<Value> GetCallbackData() const = 0;
+
+ /**
+ * Client data passed to DebugBreakForCommand function. The
+ * debugger takes ownership of the data and will delete it even if
+ * there is no message handler.
+ */
+ virtual ClientData* GetClientData() const = 0;
+
+ virtual ~EventDetails() {}
+ };
+
+
+ /**
+ * Debug event callback function.
+ *
+ * \param event the type of the debug event that triggered the callback
+ * (enum DebugEvent)
+ * \param exec_state execution state (JavaScript object)
+ * \param event_data event specific data (JavaScript object)
+ * \param data value passed by the user to SetDebugEventListener
+ */
+ typedef void (*EventCallback)(DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ Handle<Value> data);
+
+ /**
+ * Debug event callback function.
+ *
+ * \param event_details object providing information about the debug event
+ *
+ * A EventCallback2 does not take possession of the event data,
+ * and must not rely on the data persisting after the handler returns.
+ */
+ typedef void (*EventCallback2)(const EventDetails& event_details);
+
+ /**
+ * Debug message callback function.
+ *
+ * \param message the debug message handler message object
+ * \param length length of the message
+ * \param client_data the data value passed when registering the message handler
+
+ * A MessageHandler does not take possession of the message string,
+ * and must not rely on the data persisting after the handler returns.
+ *
+ * This message handler is deprecated. Use MessageHandler2 instead.
+ */
+ typedef void (*MessageHandler)(const uint16_t* message, int length,
+ ClientData* client_data);
+
+ /**
+ * Debug message callback function.
+ *
+ * \param message the debug message handler message object
+ *
+ * A MessageHandler does not take possession of the message data,
+ * and must not rely on the data persisting after the handler returns.
+ */
+ typedef void (*MessageHandler2)(const Message& message);
+
+ /**
+ * Debug host dispatch callback function.
+ */
+ typedef void (*HostDispatchHandler)();
+
+ /**
+ * Callback function for the host to ensure debug messages are processed.
+ */
+ typedef void (*DebugMessageDispatchHandler)();
+
+ // Set a C debug event listener.
+ V8_DEPRECATED(static bool SetDebugEventListener(
+ EventCallback that,
+ Handle<Value> data = Handle<Value>()));
+ static bool SetDebugEventListener2(EventCallback2 that,
+ Handle<Value> data = Handle<Value>());
+
+ // Set a JavaScript debug event listener.
+ static bool SetDebugEventListener(v8::Handle<v8::Object> that,
+ Handle<Value> data = Handle<Value>());
+
+ // Schedule a debugger break to happen when JavaScript code is run
+ // in the given isolate. If no isolate is provided the default
+ // isolate is used.
+ static void DebugBreak(Isolate* isolate = NULL);
+
+ // Remove scheduled debugger break in given isolate if it has not
+ // happened yet. If no isolate is provided the default isolate is
+ // used.
+ static void CancelDebugBreak(Isolate* isolate = NULL);
+
+ // Break execution of JavaScript in the given isolate (this method
+ // can be invoked from a non-VM thread) for further client command
+ // execution on a VM thread. Client data is then passed in
+ // EventDetails to EventCallback at the moment when the VM actually
+ // stops. If no isolate is provided the default isolate is used.
+ static void DebugBreakForCommand(ClientData* data = NULL,
+ Isolate* isolate = NULL);
+
+ // Message based interface. The message protocol is JSON. NOTE the message
+ // handler thread is not supported any more parameter must be false.
+ V8_DEPRECATED(static void SetMessageHandler(
+ MessageHandler handler,
+ bool message_handler_thread = false));
+ static void SetMessageHandler2(MessageHandler2 handler);
+
+ // If no isolate is provided the default isolate is
+ // used.
+ static void SendCommand(const uint16_t* command, int length,
+ ClientData* client_data = NULL,
+ Isolate* isolate = NULL);
+
+ // Dispatch interface.
+ static void SetHostDispatchHandler(HostDispatchHandler handler,
+ int period = 100);
+
+ /**
+ * Register a callback function to be called when a debug message has been
+ * received and is ready to be processed. For the debug messages to be
+ * processed V8 needs to be entered, and in certain embedding scenarios this
+ * callback can be used to make sure V8 is entered for the debug message to
+ * be processed. Note that debug messages will only be processed if there is
+ * a V8 break. This can happen automatically by using the option
+ * --debugger-auto-break.
+ * \param provide_locker requires that V8 acquires v8::Locker for you before
+ * calling handler
+ */
+ static void SetDebugMessageDispatchHandler(
+ DebugMessageDispatchHandler handler, bool provide_locker = false);
+
+ /**
+ * Run a JavaScript function in the debugger.
+ * \param fun the function to call
+ * \param data passed as second argument to the function
+ * With this call the debugger is entered and the function specified is called
+ * with the execution state as the first argument. This makes it possible to
+ * get access to information otherwise not available during normal JavaScript
+ * execution e.g. details on stack frames. Receiver of the function call will
+ * be the debugger context global object, however this is a subject to change.
+ * The following example shows a JavaScript function which when passed to
+ * v8::Debug::Call will return the current line of JavaScript execution.
+ *
+ * \code
+ * function frame_source_line(exec_state) {
+ * return exec_state.frame(0).sourceLine();
+ * }
+ * \endcode
+ */
+ static Local<Value> Call(v8::Handle<v8::Function> fun,
+ Handle<Value> data = Handle<Value>());
+
+ /**
+ * Returns a mirror object for the given object.
+ */
+ static Local<Value> GetMirror(v8::Handle<v8::Value> obj);
+
+ /**
+ * Enable the V8 builtin debug agent. The debugger agent will listen on the
+ * supplied TCP/IP port for remote debugger connection.
+ * \param name the name of the embedding application
+ * \param port the TCP/IP port to listen on
+ * \param wait_for_connection whether V8 should pause on a first statement
+ * allowing remote debugger to connect before anything interesting happened
+ */
+ static bool EnableAgent(const char* name, int port,
+ 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
+ * remote debugger) but they all must be handled synchronously: V8 cannot
+ * do 2 things at one time so normal script execution must be interrupted
+ * for a while.
+ *
+ * Generally when message arrives V8 may be in one of 3 states:
+ * 1. V8 is running script; V8 will automatically interrupt and process all
+ * pending messages (however auto_break flag should be enabled);
+ * 2. V8 is suspended on debug breakpoint; in this state V8 is dedicated
+ * to reading and processing debug messages;
+ * 3. V8 is not running at all or has called some long-working C++ function;
+ * by default it means that processing of all debug messages will be deferred
+ * until V8 gets control again; however, embedding application may improve
+ * this by manually calling this method.
+ *
+ * It makes sense to call this method whenever a new debug message arrived and
+ * V8 is not already running. Method v8::Debug::SetDebugMessageDispatchHandler
+ * should help with the former condition.
+ *
+ * Technically this method in many senses is equivalent to executing empty
+ * script:
+ * 1. It does nothing except for processing all pending debug messages.
+ * 2. It should be invoked with the same precautions and from the same context
+ * as V8 script would be invoked from, because:
+ * a. with "evaluate" command it can do whatever normal script can do,
+ * including all native calls;
+ * b. no other thread should call V8 while this method is running
+ * (v8::Locker may be used here).
+ *
+ * "Evaluate" debug command behavior currently is not specified in scope
+ * of this method.
+ */
+ static void ProcessDebugMessages();
+
+ /**
+ * Debugger is running in its own context which is entered while debugger
+ * messages are being dispatched. This is an explicit getter for this
+ * debugger context. Note that the content of the debugger context is subject
+ * to change.
+ */
+ static Local<Context> GetDebugContext();
+
+
+ /**
+ * Enable/disable LiveEdit functionality for the given Isolate
+ * (default Isolate if not provided). V8 will abort if LiveEdit is
+ * unexpectedly used. LiveEdit is enabled by default.
+ */
+ static void SetLiveEditEnabled(bool enable, Isolate* isolate = NULL);
+};
+
+
+} // namespace v8
+
+
+#undef EXPORT
+
+
+#endif // V8_V8_DEBUG_H_
diff --git a/chromium/v8/include/v8-preparser.h b/chromium/v8/include/v8-preparser.h
new file mode 100644
index 00000000000..1da77185af8
--- /dev/null
+++ b/chromium/v8/include/v8-preparser.h
@@ -0,0 +1,84 @@
+// 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 PREPARSER_H
+#define PREPARSER_H
+
+#include "v8.h"
+#include "v8stdint.h"
+
+namespace v8 {
+
+// The result of preparsing is either a stack overflow error, or an opaque
+// blob of data that can be passed back into the parser.
+class V8_EXPORT PreParserData {
+ public:
+ PreParserData(size_t size, const uint8_t* data)
+ : data_(data), size_(size) { }
+
+ // Create a PreParserData value where stack_overflow reports true.
+ static PreParserData StackOverflow() { return PreParserData(0, NULL); }
+
+ // Whether the pre-parser stopped due to a stack overflow.
+ // If this is the case, size() and data() should not be used.
+ bool stack_overflow() { return size_ == 0u; }
+
+ // The size of the data in bytes.
+ size_t size() const { return size_; }
+
+ // Pointer to the data.
+ const uint8_t* data() const { return data_; }
+
+ private:
+ const uint8_t* const data_;
+ const size_t size_;
+};
+
+
+// Interface for a stream of Unicode characters.
+class V8_EXPORT UnicodeInputStream { // NOLINT - V8_EXPORT is not a class name.
+ public:
+ virtual ~UnicodeInputStream();
+
+ // Returns the next Unicode code-point in the input, or a negative value when
+ // there is no more input in the stream.
+ virtual int32_t Next() = 0;
+};
+
+
+// Preparse a JavaScript program. The source code is provided as a
+// UnicodeInputStream. The max_stack_size limits the amount of stack
+// space that the preparser is allowed to use. If the preparser uses
+// more stack space than the limit provided, the result's stack_overflow()
+// method will return true. Otherwise the result contains preparser
+// data that can be used by the V8 parser to speed up parsing.
+PreParserData V8_EXPORT Preparse(UnicodeInputStream* input,
+ size_t max_stack_size);
+
+} // namespace v8.
+
+#endif // PREPARSER_H
diff --git a/chromium/v8/include/v8-profiler.h b/chromium/v8/include/v8-profiler.h
new file mode 100644
index 00000000000..e538f4a8406
--- /dev/null
+++ b/chromium/v8/include/v8-profiler.h
@@ -0,0 +1,584 @@
+// 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.
+
+#ifndef V8_V8_PROFILER_H_
+#define V8_V8_PROFILER_H_
+
+#include "v8.h"
+
+/**
+ * Profiler support for the V8 JavaScript engine.
+ */
+namespace v8 {
+
+typedef uint32_t SnapshotObjectId;
+
+/**
+ * CpuProfileNode represents a node in a call graph.
+ */
+class V8_EXPORT CpuProfileNode {
+ public:
+ /** Returns function name (empty string for anonymous functions.) */
+ Handle<String> GetFunctionName() const;
+
+ /** Returns id of the script where function is located. */
+ int GetScriptId() const;
+
+ /** Returns resource name for script from where the function originates. */
+ Handle<String> GetScriptResourceName() const;
+
+ /**
+ * Returns the number, 1-based, of the line where the function originates.
+ * kNoLineNumberInfo if no line number information is available.
+ */
+ int GetLineNumber() const;
+
+ /**
+ * Returns total (self + children) execution time of the function,
+ * in milliseconds, estimated by samples count.
+ */
+ V8_DEPRECATED(double GetTotalTime() const);
+
+ /**
+ * Returns self execution time of the function, in milliseconds,
+ * estimated by samples count.
+ */
+ V8_DEPRECATED(double GetSelfTime() const);
+
+ /** Returns the count of samples where function exists. */
+ V8_DEPRECATED(double GetTotalSamplesCount() const);
+
+ /** DEPRECATED. Please use GetHitCount instead.
+ * Returns the count of samples where function was currently executing.
+ */
+ double GetSelfSamplesCount() const;
+
+ /**
+ * Returns the count of samples where the function was currently executing.
+ */
+ unsigned GetHitCount() const;
+
+ /** Returns function entry UID. */
+ unsigned GetCallUid() const;
+
+ /** Returns id of the node. The id is unique within the tree */
+ unsigned GetNodeId() const;
+
+ /** Returns child nodes count of the node. */
+ int GetChildrenCount() const;
+
+ /** Retrieves a child node by index. */
+ const CpuProfileNode* GetChild(int index) const;
+
+ static const int kNoLineNumberInfo = Message::kNoLineNumberInfo;
+};
+
+
+/**
+ * CpuProfile contains a CPU profile in a form of top-down call tree
+ * (from main() down to functions that do all the work).
+ */
+class V8_EXPORT CpuProfile {
+ public:
+ /** Returns CPU profile UID (assigned by the profiler.) */
+ unsigned GetUid() const;
+
+ /** Returns CPU profile title. */
+ Handle<String> GetTitle() const;
+
+ /** Returns the root node of the top down call tree. */
+ const CpuProfileNode* GetTopDownRoot() const;
+
+ /**
+ * Returns number of samples recorded. The samples are not recorded unless
+ * |record_samples| parameter of CpuProfiler::StartCpuProfiling is true.
+ */
+ int GetSamplesCount() const;
+
+ /**
+ * Returns profile node corresponding to the top frame the sample at
+ * the given index.
+ */
+ const CpuProfileNode* GetSample(int index) const;
+
+ /**
+ * Returns time when the profile recording started (in microseconds
+ * since the Epoch).
+ */
+ int64_t GetStartTime() const;
+
+ /**
+ * Returns time when the profile recording was stopped (in microseconds
+ * since the Epoch).
+ */
+ int64_t GetEndTime() const;
+
+ /**
+ * Deletes the profile and removes it from CpuProfiler's list.
+ * All pointers to nodes previously returned become invalid.
+ * Profiles with the same uid but obtained using different
+ * security token are not deleted, but become inaccessible
+ * using FindProfile method. It is embedder's responsibility
+ * to call Delete on these profiles.
+ */
+ void Delete();
+};
+
+
+/**
+ * Interface for controlling CPU profiling. Instance of the
+ * profiler can be retrieved using v8::Isolate::GetCpuProfiler.
+ */
+class V8_EXPORT CpuProfiler {
+ public:
+ /**
+ * A note on security tokens usage. As scripts from different
+ * origins can run inside a single V8 instance, it is possible to
+ * have functions from different security contexts intermixed in a
+ * single CPU profile. To avoid exposing function names belonging to
+ * other contexts, filtering by security token is performed while
+ * obtaining profiling results.
+ */
+
+ /**
+ * Returns the number of profiles collected (doesn't include
+ * profiles that are being collected at the moment of call.)
+ */
+ int GetProfileCount();
+
+ /** Returns a profile by index. */
+ const CpuProfile* GetCpuProfile(int index);
+
+ /**
+ * Starts collecting CPU profile. Title may be an empty string. It
+ * is allowed to have several profiles being collected at
+ * once. Attempts to start collecting several profiles with the same
+ * title are silently ignored. While collecting a profile, functions
+ * from all security contexts are included in it. The token-based
+ * filtering is only performed when querying for a profile.
+ *
+ * |record_samples| parameter controls whether individual samples should
+ * be recorded in addition to the aggregated tree.
+ */
+ void StartCpuProfiling(Handle<String> title, bool record_samples = false);
+
+ /**
+ * Stops collecting CPU profile with a given title and returns it.
+ * If the title given is empty, finishes the last profile started.
+ */
+ const CpuProfile* StopCpuProfiling(Handle<String> title);
+
+ /**
+ * Deletes all existing profiles, also cancelling all profiling
+ * activity. All previously returned pointers to profiles and their
+ * contents become invalid after this call.
+ */
+ void DeleteAllCpuProfiles();
+
+ /**
+ * Tells the profiler whether the embedder is idle.
+ */
+ void SetIdle(bool is_idle);
+
+ private:
+ CpuProfiler();
+ ~CpuProfiler();
+ CpuProfiler(const CpuProfiler&);
+ CpuProfiler& operator=(const CpuProfiler&);
+};
+
+
+class HeapGraphNode;
+
+
+/**
+ * HeapSnapshotEdge represents a directed connection between heap
+ * graph nodes: from retainers to retained nodes.
+ */
+class V8_EXPORT HeapGraphEdge {
+ public:
+ enum Type {
+ kContextVariable = 0, // A variable from a function context.
+ kElement = 1, // An element of an array.
+ kProperty = 2, // A named object property.
+ kInternal = 3, // A link that can't be accessed from JS,
+ // thus, its name isn't a real property name
+ // (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
+ // sizes calculation.
+ kWeak = 6 // A weak reference (ignored by the GC).
+ };
+
+ /** Returns edge type (see HeapGraphEdge::Type). */
+ Type GetType() const;
+
+ /**
+ * Returns edge name. This can be a variable name, an element index, or
+ * a property name.
+ */
+ Handle<Value> GetName() const;
+
+ /** Returns origin node. */
+ const HeapGraphNode* GetFromNode() const;
+
+ /** Returns destination node. */
+ const HeapGraphNode* GetToNode() const;
+};
+
+
+/**
+ * HeapGraphNode represents a node in a heap graph.
+ */
+class V8_EXPORT HeapGraphNode {
+ public:
+ enum Type {
+ kHidden = 0, // Hidden node, may be filtered when shown to user.
+ kArray = 1, // An array of elements.
+ kString = 2, // A string.
+ kObject = 3, // A JS object (except for arrays and strings).
+ kCode = 4, // Compiled code.
+ kClosure = 5, // Function closure.
+ kRegExp = 6, // RegExp.
+ kHeapNumber = 7, // Number stored in the 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). */
+ Type GetType() const;
+
+ /**
+ * Returns node name. Depending on node's type this can be the name
+ * of the constructor (for objects), the name of the function (for
+ * closures), string value, or an empty string (for compiled code).
+ */
+ Handle<String> GetName() const;
+
+ /**
+ * Returns node id. For the same heap object, the id remains the same
+ * across all snapshots.
+ */
+ SnapshotObjectId GetId() const;
+
+ /** Returns node's own size, in bytes. */
+ int GetSelfSize() const;
+
+ /** Returns child nodes count of the node. */
+ int GetChildrenCount() const;
+
+ /** Retrieves a child by index. */
+ const HeapGraphEdge* GetChild(int index) const;
+
+ /**
+ * Finds and returns a value from the heap corresponding to this node,
+ * if the value is still reachable.
+ */
+ Handle<Value> GetHeapValue() const;
+};
+
+
+/**
+ * HeapSnapshots record the state of the JS heap at some moment.
+ */
+class V8_EXPORT HeapSnapshot {
+ public:
+ enum SerializationFormat {
+ kJSON = 0 // See format description near 'Serialize' method.
+ };
+
+ /** Returns heap snapshot UID (assigned by the profiler.) */
+ unsigned GetUid() const;
+
+ /** Returns heap snapshot title. */
+ Handle<String> GetTitle() const;
+
+ /** Returns the root node of the heap graph. */
+ const HeapGraphNode* GetRoot() const;
+
+ /** Returns a node by its id. */
+ const HeapGraphNode* GetNodeById(SnapshotObjectId id) const;
+
+ /** Returns total nodes count in the snapshot. */
+ int GetNodesCount() const;
+
+ /** Returns a node by index. */
+ const HeapGraphNode* GetNode(int index) const;
+
+ /** Returns a max seen JS object Id. */
+ SnapshotObjectId GetMaxSnapshotJSObjectId() const;
+
+ /**
+ * Deletes the snapshot and removes it from HeapProfiler's list.
+ * All pointers to nodes, edges and paths previously returned become
+ * invalid.
+ */
+ void Delete();
+
+ /**
+ * Prepare a serialized representation of the snapshot. The result
+ * is written into the stream provided in chunks of specified size.
+ * The total length of the serialized snapshot is unknown in
+ * advance, it can be roughly equal to JS heap size (that means,
+ * it can be really big - tens of megabytes).
+ *
+ * For the JSON format, heap contents are represented as an object
+ * with the following structure:
+ *
+ * {
+ * snapshot: {
+ * title: "...",
+ * uid: nnn,
+ * meta: { meta-info },
+ * node_count: nnn,
+ * edge_count: nnn
+ * },
+ * nodes: [nodes array],
+ * edges: [edges array],
+ * strings: [strings array]
+ * }
+ *
+ * Nodes reference strings, other nodes, and edges by their indexes
+ * in corresponding arrays.
+ */
+ void Serialize(OutputStream* stream, SerializationFormat format) const;
+};
+
+
+class RetainedObjectInfo;
+
+/**
+ * Interface for controlling heap profiling. Instance of the
+ * profiler can be retrieved using v8::Isolate::GetHeapProfiler.
+ */
+class V8_EXPORT HeapProfiler {
+ public:
+ /**
+ * Callback function invoked for obtaining RetainedObjectInfo for
+ * the given JavaScript wrapper object. It is prohibited to enter V8
+ * while the callback is running: only getters on the handle and
+ * GetPointerFromInternalField on the objects are allowed.
+ */
+ typedef RetainedObjectInfo* (*WrapperInfoCallback)
+ (uint16_t class_id, Handle<Value> wrapper);
+
+ /** Returns the number of snapshots taken. */
+ int GetSnapshotCount();
+
+ /** Returns a snapshot by index. */
+ const HeapSnapshot* GetHeapSnapshot(int index);
+
+ /**
+ * Returns SnapshotObjectId for a heap object referenced by |value| if
+ * it has been seen by the heap profiler, kUnknownObjectId otherwise.
+ */
+ SnapshotObjectId GetObjectId(Handle<Value> value);
+
+ /**
+ * A constant for invalid SnapshotObjectId. GetSnapshotObjectId will return
+ * it in case heap profiler cannot find id for the object passed as
+ * parameter. HeapSnapshot::GetNodeById will always return NULL for such id.
+ */
+ static const SnapshotObjectId kUnknownObjectId = 0;
+
+ /**
+ * Callback interface for retrieving user friendly names of global objects.
+ */
+ class ObjectNameResolver {
+ public:
+ /**
+ * Returns name to be used in the heap snapshot for given node. Returned
+ * string must stay alive until snapshot collection is completed.
+ */
+ virtual const char* GetName(Handle<Object> object) = 0;
+ protected:
+ virtual ~ObjectNameResolver() {}
+ };
+
+ /**
+ * Takes a heap snapshot and returns it. Title may be an empty string.
+ */
+ const HeapSnapshot* TakeHeapSnapshot(
+ Handle<String> title,
+ ActivityControl* control = NULL,
+ ObjectNameResolver* global_object_name_resolver = NULL);
+
+ /**
+ * Starts tracking of heap objects population statistics. After calling
+ * this method, all heap objects relocations done by the garbage collector
+ * are being registered.
+ */
+ void StartTrackingHeapObjects();
+
+ /**
+ * Adds a new time interval entry to the aggregated statistics array. The
+ * time interval entry contains information on the current heap objects
+ * population size. The method also updates aggregated statistics and
+ * reports updates for all previous time intervals via the OutputStream
+ * object. Updates on each time interval are provided as a stream of the
+ * HeapStatsUpdate structure instances.
+ * The return value of the function is the last seen heap object Id.
+ *
+ * StartTrackingHeapObjects must be called before the first call to this
+ * method.
+ */
+ SnapshotObjectId GetHeapStats(OutputStream* stream);
+
+ /**
+ * Stops tracking of heap objects population statistics, cleans up all
+ * collected data. StartHeapObjectsTracking must be called again prior to
+ * calling PushHeapObjectsStats next time.
+ */
+ void StopTrackingHeapObjects();
+
+ /**
+ * Deletes all snapshots taken. All previously returned pointers to
+ * snapshots and their contents become invalid after this call.
+ */
+ void DeleteAllHeapSnapshots();
+
+ /** Binds a callback to embedder's class ID. */
+ void SetWrapperClassInfoProvider(
+ uint16_t class_id,
+ WrapperInfoCallback callback);
+
+ /**
+ * Default value of persistent handle class ID. Must not be used to
+ * define a class. Can be used to reset a class of a persistent
+ * handle.
+ */
+ static const uint16_t kPersistentHandleNoClassId = 0;
+
+ /** Returns memory used for profiler internal data and snapshots. */
+ size_t GetProfilerMemorySize();
+
+ /**
+ * Sets a RetainedObjectInfo for an object group (see V8::SetObjectGroupId).
+ */
+ void SetRetainedObjectInfo(UniqueId id, RetainedObjectInfo* info);
+
+ private:
+ HeapProfiler();
+ ~HeapProfiler();
+ HeapProfiler(const HeapProfiler&);
+ HeapProfiler& operator=(const HeapProfiler&);
+};
+
+
+/**
+ * Interface for providing information about embedder's objects
+ * held by global handles. This information is reported in two ways:
+ *
+ * 1. When calling AddObjectGroup, an embedder may pass
+ * RetainedObjectInfo instance describing the group. To collect
+ * this information while taking a heap snapshot, V8 calls GC
+ * prologue and epilogue callbacks.
+ *
+ * 2. When a heap snapshot is collected, V8 additionally
+ * requests RetainedObjectInfos for persistent handles that
+ * were not previously reported via AddObjectGroup.
+ *
+ * Thus, if an embedder wants to provide information about native
+ * objects for heap snapshots, he can do it in a GC prologue
+ * handler, and / or by assigning wrapper class ids in the following way:
+ *
+ * 1. Bind a callback to class id by calling SetWrapperClassInfoProvider.
+ * 2. Call SetWrapperClassId on certain persistent handles.
+ *
+ * V8 takes ownership of RetainedObjectInfo instances passed to it and
+ * keeps them alive only during snapshot collection. Afterwards, they
+ * are freed by calling the Dispose class function.
+ */
+class V8_EXPORT RetainedObjectInfo { // NOLINT
+ public:
+ /** Called by V8 when it no longer needs an instance. */
+ virtual void Dispose() = 0;
+
+ /** Returns whether two instances are equivalent. */
+ virtual bool IsEquivalent(RetainedObjectInfo* other) = 0;
+
+ /**
+ * Returns hash value for the instance. Equivalent instances
+ * must have the same hash value.
+ */
+ virtual intptr_t GetHash() = 0;
+
+ /**
+ * 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.
+ */
+ virtual intptr_t GetElementCount() { return -1; }
+
+ /** Returns embedder's object size in bytes. */
+ virtual intptr_t GetSizeInBytes() { return -1; }
+
+ protected:
+ RetainedObjectInfo() {}
+ virtual ~RetainedObjectInfo() {}
+
+ private:
+ RetainedObjectInfo(const RetainedObjectInfo&);
+ RetainedObjectInfo& operator=(const RetainedObjectInfo&);
+};
+
+
+/**
+ * A struct for exporting HeapStats data from V8, using "push" model.
+ * See HeapProfiler::GetHeapStats.
+ */
+struct HeapStatsUpdate {
+ HeapStatsUpdate(uint32_t index, uint32_t count, uint32_t size)
+ : index(index), count(count), size(size) { }
+ uint32_t index; // Index of the time interval that was changed.
+ uint32_t count; // New value of count field for the interval with this index.
+ uint32_t size; // New value of size field for the interval with this index.
+};
+
+
+} // namespace v8
+
+
+#endif // V8_V8_PROFILER_H_
diff --git a/chromium/v8/include/v8-testing.h b/chromium/v8/include/v8-testing.h
new file mode 100644
index 00000000000..97b467a91b1
--- /dev/null
+++ b/chromium/v8/include/v8-testing.h
@@ -0,0 +1,75 @@
+// 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.
+
+#ifndef V8_V8_TEST_H_
+#define V8_V8_TEST_H_
+
+#include "v8.h"
+
+/**
+ * Testing support for the V8 JavaScript engine.
+ */
+namespace v8 {
+
+class V8_EXPORT Testing {
+ public:
+ enum StressType {
+ kStressTypeOpt,
+ kStressTypeDeopt
+ };
+
+ /**
+ * Set the type of stressing to do. The default if not set is kStressTypeOpt.
+ */
+ static void SetStressRunType(StressType type);
+
+ /**
+ * Get the number of runs of a given test that is required to get the full
+ * stress coverage.
+ */
+ static int GetStressRuns();
+
+ /**
+ * Indicate the number of the run which is about to start. The value of run
+ * should be between 0 and one less than the result from GetStressRuns()
+ */
+ static void PrepareStressRun(int run);
+
+ /**
+ * Force deoptimization of all functions.
+ */
+ static void DeoptimizeAll();
+};
+
+
+} // namespace v8
+
+
+#undef V8_EXPORT
+
+
+#endif // V8_V8_TEST_H_
diff --git a/chromium/v8/include/v8.h b/chromium/v8/include/v8.h
new file mode 100644
index 00000000000..3252602bcf1
--- /dev/null
+++ b/chromium/v8/include/v8.h
@@ -0,0 +1,6595 @@
+// Copyright 2012 the V8 project authors. 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.
+
+/** \mainpage V8 API Reference Guide
+ *
+ * V8 is Google's open source JavaScript engine.
+ *
+ * This set of documents provides reference material generated from the
+ * V8 header file, include/v8.h.
+ *
+ * For other documentation see http://code.google.com/apis/v8/
+ */
+
+#ifndef V8_H_
+#define V8_H_
+
+#include "v8stdint.h"
+
+// We reserve the V8_* prefix for macros defined in V8 public API and
+// assume there are no name conflicts with the embedder's code.
+
+#ifdef _WIN32
+
+// Setup for Windows DLL export/import. When building the V8 DLL the
+// BUILDING_V8_SHARED needs to be defined. When building a program which uses
+// the V8 DLL USING_V8_SHARED needs to be defined. When either building the V8
+// static library or building a program which uses the V8 static library neither
+// BUILDING_V8_SHARED nor USING_V8_SHARED should be defined.
+#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
+#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
+ build configuration to ensure that at most one of these is set
+#endif
+
+#ifdef BUILDING_V8_SHARED
+#define V8_EXPORT __declspec(dllexport)
+#elif USING_V8_SHARED
+#define V8_EXPORT __declspec(dllimport)
+#else
+#define V8_EXPORT
+#endif // BUILDING_V8_SHARED
+
+#else // _WIN32
+
+// Setup for Linux shared library export.
+#if defined(__GNUC__) && ((__GNUC__ >= 4) || \
+ (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(V8_SHARED)
+#ifdef BUILDING_V8_SHARED
+#define V8_EXPORT __attribute__ ((visibility("default")))
+#else
+#define V8_EXPORT
+#endif
+#else
+#define V8_EXPORT
+#endif
+
+#endif // _WIN32
+
+#if defined(__GNUC__) && !defined(DEBUG)
+#define V8_INLINE(declarator) inline __attribute__((always_inline)) declarator
+#elif defined(_MSC_VER) && !defined(DEBUG)
+#define V8_INLINE(declarator) __forceinline declarator
+#else
+#define V8_INLINE(declarator) inline declarator
+#endif
+
+#if defined(__GNUC__) && !V8_DISABLE_DEPRECATIONS
+#define V8_DEPRECATED(declarator) declarator __attribute__ ((deprecated))
+#elif defined(_MSC_VER) && !V8_DISABLE_DEPRECATIONS
+#define V8_DEPRECATED(declarator) __declspec(deprecated) declarator
+#else
+#define V8_DEPRECATED(declarator) declarator
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))
+ #define V8_UNLIKELY(condition) __builtin_expect((condition), 0)
+ #define V8_LIKELY(condition) __builtin_expect((condition), 1)
+#else
+ #define V8_UNLIKELY(condition) (condition)
+ #define V8_LIKELY(condition) (condition)
+#endif
+
+/**
+ * The v8 JavaScript engine.
+ */
+namespace v8 {
+
+class AccessorInfo;
+class AccessorSignature;
+class Array;
+class Boolean;
+class BooleanObject;
+class Context;
+class CpuProfiler;
+class Data;
+class Date;
+class DeclaredAccessorDescriptor;
+class External;
+class Function;
+class FunctionTemplate;
+class HeapProfiler;
+class ImplementationUtilities;
+class Int32;
+class Integer;
+class Isolate;
+class Number;
+class NumberObject;
+class Object;
+class ObjectOperationDescriptor;
+class ObjectTemplate;
+class Primitive;
+class RawOperationDescriptor;
+class Signature;
+class StackFrame;
+class StackTrace;
+class String;
+class StringObject;
+class Symbol;
+class SymbolObject;
+class Uint32;
+class Utils;
+class Value;
+template <class T> class Handle;
+template <class T> class Local;
+template <class T> class Persistent;
+class FunctionTemplate;
+class ObjectTemplate;
+class Data;
+class AccessorInfo;
+template<typename T> class PropertyCallbackInfo;
+class StackTrace;
+class StackFrame;
+class Isolate;
+class DeclaredAccessorDescriptor;
+class ObjectOperationDescriptor;
+class RawOperationDescriptor;
+class CallHandlerHelper;
+
+namespace internal {
+class Arguments;
+class Heap;
+class HeapObject;
+class Isolate;
+class Object;
+template<typename T> class CustomArguments;
+class PropertyCallbackArguments;
+class FunctionCallbackArguments;
+}
+
+
+/**
+ * General purpose unique identifier.
+ */
+class UniqueId {
+ public:
+ explicit UniqueId(intptr_t data)
+ : data_(data) {}
+
+ bool operator==(const UniqueId& other) const {
+ return data_ == other.data_;
+ }
+
+ bool operator!=(const UniqueId& other) const {
+ return data_ != other.data_;
+ }
+
+ bool operator<(const UniqueId& other) const {
+ return data_ < other.data_;
+ }
+
+ private:
+ intptr_t data_;
+};
+
+
+// --- Weak Handles ---
+
+
+/**
+ * A weak reference callback function.
+ *
+ * This callback should either explicitly invoke Dispose on |object| if
+ * V8 wrapper is not needed anymore, or 'revive' it by invocation of MakeWeak.
+ *
+ * \param object the weak global object to be reclaimed by the garbage collector
+ * \param parameter the value passed in when making the weak global object
+ */
+template<typename T, typename P>
+class WeakReferenceCallbacks {
+ public:
+ typedef void (*Revivable)(Isolate* isolate,
+ Persistent<T>* object,
+ P* parameter);
+};
+
+// --- Handles ---
+
+#define TYPE_CHECK(T, S) \
+ while (false) { \
+ *(static_cast<T* volatile*>(0)) = static_cast<S*>(0); \
+ }
+
+
+/**
+ * An object reference managed by the v8 garbage collector.
+ *
+ * All objects returned from v8 have to be tracked by the garbage
+ * collector so that it knows that the objects are still alive. Also,
+ * because the garbage collector may move objects, it is unsafe to
+ * point directly to an object. Instead, all objects are stored in
+ * handles which are known by the garbage collector and updated
+ * whenever an object moves. Handles should always be passed by value
+ * (except in cases like out-parameters) and they should never be
+ * allocated on the heap.
+ *
+ * There are two types of handles: local and persistent handles.
+ * Local handles are light-weight and transient and typically used in
+ * local operations. They are managed by HandleScopes. Persistent
+ * handles can be used when storing objects across several independent
+ * operations and have to be explicitly deallocated when they're no
+ * longer used.
+ *
+ * It is safe to extract the object stored in the handle by
+ * dereferencing the handle (for instance, to extract the Object* from
+ * a Handle<Object>); the value will still be governed by a handle
+ * behind the scenes and the same rules apply to these values as to
+ * their handles.
+ */
+template <class T> class Handle {
+ public:
+ /**
+ * Creates an empty handle.
+ */
+ V8_INLINE(Handle()) : val_(0) {}
+
+#ifdef V8_USE_UNSAFE_HANDLES
+ /**
+ * Creates a new handle for the specified value.
+ */
+ V8_INLINE(explicit Handle(T* val)) : val_(val) {}
+#endif
+
+ /**
+ * Creates a handle for the contents of the specified handle. This
+ * constructor allows you to pass handles as arguments by value and
+ * to assign between handles. However, if you try to assign between
+ * incompatible handles, for instance from a Handle<String> to a
+ * Handle<Number> it will cause a compile-time error. Assigning
+ * between compatible handles, for instance assigning a
+ * Handle<String> to a variable declared as Handle<Value>, is legal
+ * because String is a subclass of Value.
+ */
+ template <class S> V8_INLINE(Handle(Handle<S> that))
+ : val_(reinterpret_cast<T*>(*that)) {
+ /**
+ * This check fails when trying to convert between incompatible
+ * handles. For example, converting from a Handle<String> to a
+ * Handle<Number>.
+ */
+ TYPE_CHECK(T, S);
+ }
+
+ /**
+ * Returns true if the handle is empty.
+ */
+ V8_INLINE(bool IsEmpty() const) { return val_ == 0; }
+
+ /**
+ * Sets the handle to be empty. IsEmpty() will then return true.
+ */
+ V8_INLINE(void Clear()) { val_ = 0; }
+
+ V8_INLINE(T* operator->() const) { return val_; }
+
+ V8_INLINE(T* operator*() const) { return val_; }
+
+ /**
+ * Checks whether two handles are the same.
+ * Returns true if both are empty, or if the objects
+ * to which they refer are identical.
+ * The handles' references are not checked.
+ */
+ template <class S> V8_INLINE(bool operator==(const Handle<S> that) const) {
+ internal::Object** a = reinterpret_cast<internal::Object**>(**this);
+ internal::Object** b = reinterpret_cast<internal::Object**>(*that);
+ if (a == 0) return b == 0;
+ if (b == 0) return false;
+ return *a == *b;
+ }
+
+#ifndef V8_USE_UNSAFE_HANDLES
+ template <class S> V8_INLINE(
+ bool operator==(const Persistent<S>& that) const) {
+ internal::Object** a = reinterpret_cast<internal::Object**>(**this);
+ internal::Object** b = reinterpret_cast<internal::Object**>(*that);
+ if (a == 0) return b == 0;
+ if (b == 0) return false;
+ return *a == *b;
+ }
+#endif
+
+ /**
+ * Checks whether two handles are different.
+ * Returns true if only one of the handles is empty, or if
+ * the objects to which they refer are different.
+ * The handles' references are not checked.
+ */
+ template <class S> V8_INLINE(bool operator!=(Handle<S> that) const) {
+ return !operator==(that);
+ }
+
+ template <class S> V8_INLINE(static Handle<T> Cast(Handle<S> that)) {
+#ifdef V8_ENABLE_CHECKS
+ // If we're going to perform the type check then we have to check
+ // that the handle isn't empty before doing the checked cast.
+ if (that.IsEmpty()) return Handle<T>();
+#endif
+ return Handle<T>(T::Cast(*that));
+ }
+
+ template <class S> V8_INLINE(Handle<S> As()) {
+ return Handle<S>::Cast(*this);
+ }
+
+#ifndef V8_USE_UNSAFE_HANDLES
+ V8_INLINE(static Handle<T> New(Isolate* isolate, Handle<T> that)) {
+ return New(isolate, that.val_);
+ }
+ // TODO(dcarney): remove before cutover
+ V8_INLINE(static Handle<T> New(Isolate* isolate, const Persistent<T>& that)) {
+ return New(isolate, that.val_);
+ }
+
+#ifndef V8_ALLOW_ACCESS_TO_RAW_HANDLE_CONSTRUCTOR
+
+ private:
+#endif
+ /**
+ * Creates a new handle for the specified value.
+ */
+ V8_INLINE(explicit Handle(T* val)) : val_(val) {}
+#endif
+
+ private:
+ friend class Utils;
+ template<class F> friend class Persistent;
+ template<class F> friend class Local;
+ friend class Arguments;
+ template<class F> friend class FunctionCallbackInfo;
+ template<class F> friend class PropertyCallbackInfo;
+ template<class F> friend class internal::CustomArguments;
+ friend class AccessorInfo;
+ friend Handle<Primitive> Undefined(Isolate* isolate);
+ friend Handle<Primitive> Null(Isolate* isolate);
+ friend Handle<Boolean> True(Isolate* isolate);
+ friend Handle<Boolean> False(Isolate* isolate);
+ friend class Context;
+ friend class HandleScope;
+
+#ifndef V8_USE_UNSAFE_HANDLES
+ V8_INLINE(static Handle<T> New(Isolate* isolate, T* that));
+#endif
+
+ T* val_;
+};
+
+
+// A value which will never be returned by Local::Eternalize
+// Useful for static initialization
+const int kUninitializedEternalIndex = -1;
+
+
+/**
+ * A light-weight stack-allocated object handle. All operations
+ * that return objects from within v8 return them in local handles. They
+ * are created within HandleScopes, and all local handles allocated within a
+ * handle scope are destroyed when the handle scope is destroyed. Hence it
+ * is not necessary to explicitly deallocate local handles.
+ */
+// TODO(dcarney): deprecate entire class
+template <class T> class Local : public Handle<T> {
+ public:
+ V8_INLINE(Local());
+ template <class S> V8_INLINE(Local(Local<S> that))
+ : Handle<T>(reinterpret_cast<T*>(*that)) {
+ /**
+ * This check fails when trying to convert between incompatible
+ * handles. For example, converting from a Handle<String> to a
+ * Handle<Number>.
+ */
+ TYPE_CHECK(T, S);
+ }
+
+
+#ifdef V8_USE_UNSAFE_HANDLES
+ template <class S> V8_INLINE(Local(S* that) : Handle<T>(that)) { }
+#endif
+
+ template <class S> V8_INLINE(static Local<T> Cast(Local<S> that)) {
+#ifdef V8_ENABLE_CHECKS
+ // If we're going to perform the type check then we have to check
+ // that the handle isn't empty before doing the checked cast.
+ if (that.IsEmpty()) return Local<T>();
+#endif
+ return Local<T>(T::Cast(*that));
+ }
+#ifndef V8_USE_UNSAFE_HANDLES
+ template <class S> V8_INLINE(Local(Handle<S> that))
+ : Handle<T>(reinterpret_cast<T*>(*that)) {
+ TYPE_CHECK(T, S);
+ }
+#endif
+
+ template <class S> V8_INLINE(Local<S> As()) {
+ return Local<S>::Cast(*this);
+ }
+
+ // Keep this Local alive for the lifetime of the Isolate.
+ // It remains retrievable via the returned index,
+ V8_INLINE(int Eternalize(Isolate* isolate));
+ V8_INLINE(static Local<T> GetEternal(Isolate* isolate, int index));
+
+ /**
+ * Create a local handle for the content of another handle.
+ * The referee is kept alive by the local handle even when
+ * the original handle is destroyed/disposed.
+ */
+ V8_INLINE(static Local<T> New(Handle<T> that));
+ V8_INLINE(static Local<T> New(Isolate* isolate, Handle<T> that));
+#ifndef V8_USE_UNSAFE_HANDLES
+ // TODO(dcarney): remove before cutover
+ V8_INLINE(static Local<T> New(Isolate* isolate, const Persistent<T>& that));
+
+#ifndef V8_ALLOW_ACCESS_TO_RAW_HANDLE_CONSTRUCTOR
+
+ private:
+#endif
+ template <class S> V8_INLINE(Local(S* that) : Handle<T>(that)) { }
+#endif
+
+ private:
+ friend class Utils;
+ template<class F> friend class Persistent;
+ template<class F> friend class Handle;
+ friend class Arguments;
+ template<class F> friend class FunctionCallbackInfo;
+ template<class F> friend class PropertyCallbackInfo;
+ friend class String;
+ friend class Object;
+ friend class AccessorInfo;
+ friend class Context;
+ template<class F> friend class internal::CustomArguments;
+ friend class HandleScope;
+
+ V8_INLINE(static Local<T> New(Isolate* isolate, T* that));
+};
+
+/**
+ * An object reference that is independent of any handle scope. Where
+ * a Local handle only lives as long as the HandleScope in which it was
+ * allocated, a Persistent handle remains valid until it is explicitly
+ * disposed.
+ *
+ * A persistent handle contains a reference to a storage cell within
+ * the v8 engine which holds an object value and which is updated by
+ * the garbage collector whenever the object is moved. A new storage
+ * cell can be created using Persistent::New and existing handles can
+ * be disposed using Persistent::Dispose. Since persistent handles
+ * are passed by value you may have many persistent handle objects
+ * that point to the same storage cell. For instance, if you pass a
+ * persistent handle as an argument to a function you will not get two
+ * different storage cells but rather two references to the same
+ * storage cell.
+ */
+template <class T> class Persistent // NOLINT
+#ifdef V8_USE_UNSAFE_HANDLES
+ : public Handle<T> {
+#else
+ { // NOLINT
+#endif
+ public:
+#ifndef V8_USE_UNSAFE_HANDLES
+ V8_INLINE(Persistent()) : val_(0) { }
+ // TODO(dcarney): add this back before cutover.
+// V8_INLINE(~Persistent()) {
+// Dispose();
+// }
+ V8_INLINE(bool IsEmpty() const) { return val_ == 0; }
+ // TODO(dcarney): remove somehow before cutover
+ // The handle should either be 0, or a pointer to a live cell.
+ V8_INLINE(void Clear()) { val_ = 0; }
+
+ /**
+ * A constructor that creates a new global cell pointing to that. In contrast
+ * to the copy constructor, this creates a new persistent handle which needs
+ * to be separately disposed.
+ */
+ template <class S> V8_INLINE(Persistent(Isolate* isolate, Handle<S> that))
+ : val_(New(isolate, *that)) { }
+
+ template <class S> V8_INLINE(Persistent(Isolate* isolate,
+ const Persistent<S>& that)) // NOLINT
+ : val_(New(isolate, *that)) { }
+
+#else
+ /**
+ * Creates an empty persistent handle that doesn't point to any
+ * storage cell.
+ */
+ V8_INLINE(Persistent()) : Handle<T>() { }
+
+ /**
+ * Creates a persistent handle for the same storage cell as the
+ * specified handle. This constructor allows you to pass persistent
+ * handles as arguments by value and to assign between persistent
+ * handles. However, attempting to assign between incompatible
+ * persistent handles, for instance from a Persistent<String> to a
+ * Persistent<Number> will cause a compile-time error. Assigning
+ * between compatible persistent handles, for instance assigning a
+ * Persistent<String> to a variable declared as Persistent<Value>,
+ * is allowed as String is a subclass of Value.
+ */
+ template <class S> V8_INLINE(Persistent(Persistent<S> that))
+ : Handle<T>(reinterpret_cast<T*>(*that)) {
+ /**
+ * This check fails when trying to convert between incompatible
+ * handles. For example, converting from a Handle<String> to a
+ * Handle<Number>.
+ */
+ TYPE_CHECK(T, S);
+ }
+
+ template <class S> V8_INLINE(Persistent(S* that)) : Handle<T>(that) { }
+
+ /**
+ * A constructor that creates a new global cell pointing to that. In contrast
+ * to the copy constructor, this creates a new persistent handle which needs
+ * to be separately disposed.
+ */
+ template <class S> V8_INLINE(Persistent(Isolate* isolate, Handle<S> that))
+ : Handle<T>(New(isolate, that)) { }
+
+ /**
+ * "Casts" a plain handle which is known to be a persistent handle
+ * to a persistent handle.
+ */
+ template <class S> explicit V8_INLINE(Persistent(Handle<S> that))
+ : Handle<T>(*that) { }
+
+#endif
+
+#ifdef V8_USE_UNSAFE_HANDLES
+ template <class S> V8_INLINE(static Persistent<T> Cast(Persistent<S> that)) {
+#ifdef V8_ENABLE_CHECKS
+ // If we're going to perform the type check then we have to check
+ // that the handle isn't empty before doing the checked cast.
+ if (that.IsEmpty()) return Persistent<T>();
+#endif
+ return Persistent<T>(T::Cast(*that));
+ }
+
+ template <class S> V8_INLINE(Persistent<S> As()) {
+ return Persistent<S>::Cast(*this);
+ }
+
+#else
+ template <class S>
+ V8_INLINE(static Persistent<T>& Cast(Persistent<S>& that)) { // NOLINT
+#ifdef V8_ENABLE_CHECKS
+ // If we're going to perform the type check then we have to check
+ // that the handle isn't empty before doing the checked cast.
+ if (!that.IsEmpty()) T::Cast(*that);
+#endif
+ return reinterpret_cast<Persistent<T>&>(that);
+ }
+
+ template <class S> V8_INLINE(Persistent<S>& As()) { // NOLINT
+ return Persistent<S>::Cast(*this);
+ }
+#endif
+
+#ifdef V8_USE_UNSAFE_HANDLES
+ V8_DEPRECATED(static Persistent<T> New(Handle<T> that));
+ V8_INLINE(static Persistent<T> New(Isolate* isolate, Handle<T> that));
+ V8_INLINE(static Persistent<T> New(Isolate* isolate, Persistent<T> that));
+#endif
+
+#ifndef V8_USE_UNSAFE_HANDLES
+ template <class S> V8_INLINE(
+ bool operator==(const Persistent<S>& that) const) {
+ internal::Object** a = reinterpret_cast<internal::Object**>(**this);
+ internal::Object** b = reinterpret_cast<internal::Object**>(*that);
+ if (a == 0) return b == 0;
+ if (b == 0) return false;
+ return *a == *b;
+ }
+
+ template <class S> V8_INLINE(bool operator==(const Handle<S> that) const) {
+ internal::Object** a = reinterpret_cast<internal::Object**>(**this);
+ internal::Object** b = reinterpret_cast<internal::Object**>(*that);
+ if (a == 0) return b == 0;
+ if (b == 0) return false;
+ return *a == *b;
+ }
+#endif
+
+ V8_INLINE(void Dispose());
+
+ /**
+ * Releases the storage cell referenced by this persistent handle.
+ * Does not remove the reference to the cell from any handles.
+ * This handle's reference, and any other references to the storage
+ * cell remain and IsEmpty will still return false.
+ */
+ // TODO(dcarney): deprecate
+ V8_INLINE(void Dispose(Isolate* isolate)) { Dispose(); }
+
+ /**
+ * Make the reference to this object weak. When only weak handles
+ * refer to the object, the garbage collector will perform a
+ * callback to the given V8::NearDeathCallback function, passing
+ * it the object reference and the given parameters.
+ */
+ template<typename S, typename P>
+ V8_INLINE(void MakeWeak(
+ P* parameters,
+ typename WeakReferenceCallbacks<S, P>::Revivable callback));
+
+ template<typename P>
+ V8_INLINE(void MakeWeak(
+ P* parameters,
+ typename WeakReferenceCallbacks<T, P>::Revivable callback));
+
+ template<typename S, typename P>
+ V8_DEPRECATED(void MakeWeak(
+ Isolate* isolate,
+ P* parameters,
+ typename WeakReferenceCallbacks<S, P>::Revivable callback));
+
+ template<typename P>
+ V8_DEPRECATED(void MakeWeak(
+ Isolate* isolate,
+ P* parameters,
+ typename WeakReferenceCallbacks<T, P>::Revivable callback));
+
+ V8_INLINE(void ClearWeak());
+
+ // TODO(dcarney): deprecate
+ V8_INLINE(void ClearWeak(Isolate* isolate)) { ClearWeak(); }
+
+ /**
+ * Marks the reference to this object independent. Garbage collector is free
+ * to ignore any object groups containing this object. Weak callback for an
+ * independent handle should not assume that it will be preceded by a global
+ * GC prologue callback or followed by a global GC epilogue callback.
+ */
+ V8_INLINE(void MarkIndependent());
+
+ // TODO(dcarney): deprecate
+ V8_INLINE(void MarkIndependent(Isolate* isolate)) { MarkIndependent(); }
+
+ /**
+ * Marks the reference to this object partially dependent. Partially dependent
+ * handles only depend on other partially dependent handles and these
+ * dependencies are provided through object groups. It provides a way to build
+ * smaller object groups for young objects that represent only a subset of all
+ * external dependencies. This mark is automatically cleared after each
+ * garbage collection.
+ */
+ V8_INLINE(void MarkPartiallyDependent());
+
+ // TODO(dcarney): deprecate
+ V8_INLINE(void MarkPartiallyDependent(Isolate* isolate)) {
+ MarkPartiallyDependent();
+ }
+
+ V8_INLINE(bool IsIndependent() const);
+
+ // TODO(dcarney): deprecate
+ V8_INLINE(bool IsIndependent(Isolate* isolate) const) {
+ return IsIndependent();
+ }
+
+ /** Checks if the handle holds the only reference to an object. */
+ V8_INLINE(bool IsNearDeath() const);
+
+ // TODO(dcarney): deprecate
+ V8_INLINE(bool IsNearDeath(Isolate* isolate) const) { return IsNearDeath(); }
+
+ /** Returns true if the handle's reference is weak. */
+ V8_INLINE(bool IsWeak() const);
+
+ // TODO(dcarney): deprecate
+ V8_INLINE(bool IsWeak(Isolate* isolate) const) { return IsWeak(); }
+
+ /**
+ * Assigns a wrapper class ID to the handle. See RetainedObjectInfo interface
+ * description in v8-profiler.h for details.
+ */
+ V8_INLINE(void SetWrapperClassId(uint16_t class_id));
+
+ // TODO(dcarney): deprecate
+ V8_INLINE(void SetWrapperClassId(Isolate* isolate, uint16_t class_id)) {
+ SetWrapperClassId(class_id);
+ }
+
+ /**
+ * Returns the class ID previously assigned to this handle or 0 if no class ID
+ * was previously assigned.
+ */
+ V8_INLINE(uint16_t WrapperClassId() const);
+
+ // TODO(dcarney): deprecate
+ V8_INLINE(uint16_t WrapperClassId(Isolate* isolate) const) {
+ return WrapperClassId();
+ }
+
+ /**
+ * Disposes the current contents of the handle and replaces it.
+ */
+ V8_INLINE(void Reset(Isolate* isolate, const Handle<T>& other));
+
+#ifndef V8_USE_UNSAFE_HANDLES
+ V8_INLINE(void Reset(Isolate* isolate, const Persistent<T>& other));
+#endif
+
+ /**
+ * Returns the underlying raw pointer and clears the handle. The caller is
+ * responsible of eventually destroying the underlying object (by creating a
+ * Persistent handle which points to it and Disposing it). In the future,
+ * destructing a Persistent will also Dispose it. With this function, the
+ * embedder can let the Persistent go out of scope without it getting
+ * disposed.
+ */
+ V8_INLINE(T* ClearAndLeak());
+
+#ifndef V8_USE_UNSAFE_HANDLES
+
+ private:
+ // TODO(dcarney): make unlinkable before cutover
+ V8_INLINE(Persistent(const Persistent& that)) : val_(that.val_) {}
+ // TODO(dcarney): make unlinkable before cutover
+ V8_INLINE(Persistent& operator=(const Persistent& that)) { // NOLINT
+ this->val_ = that.val_;
+ return *this;
+ }
+
+ public:
+#ifndef V8_ALLOW_ACCESS_TO_RAW_HANDLE_CONSTRUCTOR
+
+ private:
+#endif
+ // TODO(dcarney): remove before cutover
+ template <class S> V8_INLINE(Persistent(S* that)) : val_(that) { }
+
+ // TODO(dcarney): remove before cutover
+ V8_INLINE(T* operator*() const) { return val_; }
+
+ private:
+ // TODO(dcarney): remove before cutover
+ V8_INLINE(T* operator->() const) { return val_; }
+ public:
+#endif
+
+ private:
+ friend class Utils;
+ template<class F> friend class Handle;
+ template<class F> friend class Local;
+ template<class F> friend class Persistent;
+ template<class F> friend class ReturnValue;
+
+ V8_INLINE(static T* New(Isolate* isolate, T* that));
+
+#ifndef V8_USE_UNSAFE_HANDLES
+ T* val_;
+#endif
+};
+
+
+ /**
+ * A stack-allocated class that governs a number of local handles.
+ * After a handle scope has been created, all local handles will be
+ * allocated within that handle scope until either the handle scope is
+ * deleted or another handle scope is created. If there is already a
+ * handle scope and a new one is created, all allocations will take
+ * place in the new handle scope until it is deleted. After that,
+ * new handles will again be allocated in the original handle scope.
+ *
+ * After the handle scope of a local handle has been deleted the
+ * garbage collector will no longer track the object stored in the
+ * handle and may deallocate it. The behavior of accessing a handle
+ * for which the handle scope has been deleted is undefined.
+ */
+class V8_EXPORT HandleScope {
+ public:
+ // TODO(svenpanne) Deprecate me when Chrome is fixed!
+ HandleScope();
+
+ HandleScope(Isolate* isolate);
+
+ ~HandleScope();
+
+ /**
+ * Closes the handle scope and returns the value as a handle in the
+ * previous scope, which is the new current scope after the call.
+ */
+ template <class T> Local<T> Close(Handle<T> value);
+
+ /**
+ * Counts the number of allocated handles.
+ */
+ static int NumberOfHandles();
+
+ /**
+ * Creates a new handle with the given value.
+ */
+ static internal::Object** CreateHandle(internal::Object* value);
+ static internal::Object** CreateHandle(internal::Isolate* isolate,
+ internal::Object* value);
+ // Faster version, uses HeapObject to obtain the current Isolate.
+ static internal::Object** CreateHandle(internal::HeapObject* value);
+
+ private:
+ // Make it hard to create heap-allocated or illegal handle scopes by
+ // disallowing certain operations.
+ HandleScope(const HandleScope&);
+ void operator=(const HandleScope&);
+ void* operator new(size_t size);
+ void operator delete(void*, size_t);
+
+ // This Data class is accessible internally as HandleScopeData through a
+ // typedef in the ImplementationUtilities class.
+ class V8_EXPORT Data {
+ public:
+ internal::Object** next;
+ internal::Object** limit;
+ int level;
+ V8_INLINE(void Initialize()) {
+ next = limit = NULL;
+ level = 0;
+ }
+ };
+
+ void Initialize(Isolate* isolate);
+ void Leave();
+
+ internal::Isolate* isolate_;
+ internal::Object** prev_next_;
+ internal::Object** prev_limit_;
+
+ // Allow for the active closing of HandleScopes which allows to pass a handle
+ // from the HandleScope being closed to the next top most HandleScope.
+ bool is_closed_;
+ internal::Object** RawClose(internal::Object** value);
+
+ friend class ImplementationUtilities;
+};
+
+
+// --- Special objects ---
+
+
+/**
+ * The superclass of values and API object templates.
+ */
+class V8_EXPORT Data {
+ private:
+ Data();
+};
+
+
+/**
+ * Pre-compilation data that can be associated with a script. This
+ * data can be calculated for a script in advance of actually
+ * compiling it, and can be stored between compilations. When script
+ * data is given to the compile method compilation will be faster.
+ */
+class V8_EXPORT ScriptData { // NOLINT
+ public:
+ virtual ~ScriptData() { }
+
+ /**
+ * Pre-compiles the specified script (context-independent).
+ *
+ * \param input Pointer to UTF-8 script source code.
+ * \param length Length of UTF-8 script source code.
+ */
+ static ScriptData* PreCompile(const char* input, int length);
+
+ /**
+ * Pre-compiles the specified script (context-independent).
+ *
+ * NOTE: Pre-compilation using this method cannot happen on another thread
+ * without using Lockers.
+ *
+ * \param source Script source code.
+ */
+ static ScriptData* PreCompile(Handle<String> source);
+
+ /**
+ * Load previous pre-compilation data.
+ *
+ * \param data Pointer to data returned by a call to Data() of a previous
+ * ScriptData. Ownership is not transferred.
+ * \param length Length of data.
+ */
+ static ScriptData* New(const char* data, int length);
+
+ /**
+ * Returns the length of Data().
+ */
+ virtual int Length() = 0;
+
+ /**
+ * Returns a serialized representation of this ScriptData that can later be
+ * passed to New(). NOTE: Serialized data is platform-dependent.
+ */
+ virtual const char* Data() = 0;
+
+ /**
+ * Returns true if the source code could not be parsed.
+ */
+ virtual bool HasError() = 0;
+};
+
+
+/**
+ * The origin, within a file, of a script.
+ */
+class ScriptOrigin {
+ public:
+ V8_INLINE(ScriptOrigin(
+ Handle<Value> resource_name,
+ Handle<Integer> resource_line_offset = Handle<Integer>(),
+ Handle<Integer> resource_column_offset = Handle<Integer>(),
+ Handle<Boolean> resource_is_shared_cross_origin = Handle<Boolean>()))
+ : resource_name_(resource_name),
+ resource_line_offset_(resource_line_offset),
+ resource_column_offset_(resource_column_offset),
+ resource_is_shared_cross_origin_(resource_is_shared_cross_origin) { }
+ V8_INLINE(Handle<Value> ResourceName() const);
+ V8_INLINE(Handle<Integer> ResourceLineOffset() const);
+ V8_INLINE(Handle<Integer> ResourceColumnOffset() const);
+ V8_INLINE(Handle<Boolean> ResourceIsSharedCrossOrigin() const);
+ private:
+ Handle<Value> resource_name_;
+ Handle<Integer> resource_line_offset_;
+ Handle<Integer> resource_column_offset_;
+ Handle<Boolean> resource_is_shared_cross_origin_;
+};
+
+
+/**
+ * A compiled JavaScript script.
+ */
+class V8_EXPORT Script {
+ public:
+ /**
+ * Compiles the specified script (context-independent).
+ *
+ * \param source Script source code.
+ * \param origin Script origin, owned by caller, no references are kept
+ * when New() returns
+ * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile()
+ * using pre_data speeds compilation if it's done multiple times.
+ * Owned by caller, no references are kept when New() returns.
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but allows data to be
+ * available to compile event handlers.
+ * \return Compiled script object (context independent; when run it
+ * will use the currently entered context).
+ */
+ static Local<Script> New(Handle<String> source,
+ ScriptOrigin* origin = NULL,
+ ScriptData* pre_data = NULL,
+ Handle<String> script_data = Handle<String>());
+
+ /**
+ * Compiles the specified script using the specified file name
+ * object (typically a string) as the script's origin.
+ *
+ * \param source Script source code.
+ * \param file_name file name object (typically a string) to be used
+ * as the script's origin.
+ * \return Compiled script object (context independent; when run it
+ * will use the currently entered context).
+ */
+ static Local<Script> New(Handle<String> source,
+ Handle<Value> file_name);
+
+ /**
+ * Compiles the specified script (bound to current context).
+ *
+ * \param source Script source code.
+ * \param origin Script origin, owned by caller, no references are kept
+ * when Compile() returns
+ * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile()
+ * using pre_data speeds compilation if it's done multiple times.
+ * Owned by caller, no references are kept when Compile() returns.
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but makes data available
+ * earlier (i.e. to compile event handlers).
+ * \return Compiled script object, bound to the context that was active
+ * when this function was called. When run it will always use this
+ * context.
+ */
+ static Local<Script> Compile(Handle<String> source,
+ ScriptOrigin* origin = NULL,
+ ScriptData* pre_data = NULL,
+ Handle<String> script_data = Handle<String>());
+
+ /**
+ * Compiles the specified script using the specified file name
+ * object (typically a string) as the script's origin.
+ *
+ * \param source Script source code.
+ * \param file_name File name to use as script's origin
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but makes data available
+ * earlier (i.e. to compile event handlers).
+ * \return Compiled script object, bound to the context that was active
+ * when this function was called. When run it will always use this
+ * context.
+ */
+ static Local<Script> Compile(Handle<String> source,
+ Handle<Value> file_name,
+ Handle<String> script_data = Handle<String>());
+
+ /**
+ * Runs the script returning the resulting value. If the script is
+ * context independent (created using ::New) it will be run in the
+ * currently entered context. If it is context specific (created
+ * using ::Compile) it will be run in the context in which it was
+ * compiled.
+ */
+ Local<Value> Run();
+
+ /**
+ * Returns the script id value.
+ * DEPRECATED: Please use GetId().
+ */
+ Local<Value> Id();
+
+ /**
+ * Returns the script id.
+ */
+ int GetId();
+
+ /**
+ * Associate an additional data object with the script. This is mainly used
+ * with the debugger as this data object is only available through the
+ * debugger API.
+ */
+ void SetData(Handle<String> data);
+
+ /**
+ * Returns the name value of one Script.
+ */
+ Handle<Value> GetScriptName();
+
+ /**
+ * Returns zero based line number of the code_pos location in the script.
+ * -1 will be returned if no information available.
+ */
+ int GetLineNumber(int code_pos);
+
+ static const int kNoScriptId = 0;
+};
+
+
+/**
+ * An error message.
+ */
+class V8_EXPORT Message {
+ public:
+ Local<String> Get() const;
+ Local<String> GetSourceLine() const;
+
+ /**
+ * Returns the resource name for the script from where the function causing
+ * the error originates.
+ */
+ Handle<Value> GetScriptResourceName() const;
+
+ /**
+ * Returns the resource data for the script from where the function causing
+ * the error originates.
+ */
+ Handle<Value> GetScriptData() const;
+
+ /**
+ * Exception stack trace. By default stack traces are not captured for
+ * uncaught exceptions. SetCaptureStackTraceForUncaughtExceptions allows
+ * to change this option.
+ */
+ Handle<StackTrace> GetStackTrace() const;
+
+ /**
+ * Returns the number, 1-based, of the line where the error occurred.
+ */
+ int GetLineNumber() const;
+
+ /**
+ * Returns the index within the script of the first character where
+ * the error occurred.
+ */
+ int GetStartPosition() const;
+
+ /**
+ * Returns the index within the script of the last character where
+ * the error occurred.
+ */
+ int GetEndPosition() const;
+
+ /**
+ * Returns the index within the line of the first character where
+ * the error occurred.
+ */
+ int GetStartColumn() const;
+
+ /**
+ * Returns the index within the line of the last character where
+ * the error occurred.
+ */
+ int GetEndColumn() const;
+
+ /**
+ * Passes on the value set by the embedder when it fed the script from which
+ * this Message was generated to V8.
+ */
+ bool IsSharedCrossOrigin() const;
+
+ // TODO(1245381): Print to a string instead of on a FILE.
+ static void PrintCurrentStackTrace(FILE* out);
+
+ static const int kNoLineNumberInfo = 0;
+ static const int kNoColumnInfo = 0;
+};
+
+
+/**
+ * Representation of a JavaScript stack trace. The information collected is a
+ * snapshot of the execution stack and the information remains valid after
+ * execution continues.
+ */
+class V8_EXPORT StackTrace {
+ public:
+ /**
+ * Flags that determine what information is placed captured for each
+ * StackFrame when grabbing the current stack trace.
+ */
+ enum StackTraceOptions {
+ kLineNumber = 1,
+ kColumnOffset = 1 << 1 | kLineNumber,
+ kScriptName = 1 << 2,
+ kFunctionName = 1 << 3,
+ kIsEval = 1 << 4,
+ kIsConstructor = 1 << 5,
+ kScriptNameOrSourceURL = 1 << 6,
+ kOverview = kLineNumber | kColumnOffset | kScriptName | kFunctionName,
+ kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL
+ };
+
+ /**
+ * Returns a StackFrame at a particular index.
+ */
+ Local<StackFrame> GetFrame(uint32_t index) const;
+
+ /**
+ * Returns the number of StackFrames.
+ */
+ int GetFrameCount() const;
+
+ /**
+ * Returns StackTrace as a v8::Array that contains StackFrame objects.
+ */
+ Local<Array> AsArray();
+
+ /**
+ * Grab a snapshot of the current JavaScript execution stack.
+ *
+ * \param frame_limit The maximum number of stack frames we want to capture.
+ * \param options Enumerates the set of things we will capture for each
+ * StackFrame.
+ */
+ static Local<StackTrace> CurrentStackTrace(
+ int frame_limit,
+ StackTraceOptions options = kOverview);
+};
+
+
+/**
+ * A single JavaScript stack frame.
+ */
+class V8_EXPORT StackFrame {
+ public:
+ /**
+ * Returns the number, 1-based, of the line for the associate function call.
+ * This method will return Message::kNoLineNumberInfo if it is unable to
+ * retrieve the line number, or if kLineNumber was not passed as an option
+ * when capturing the StackTrace.
+ */
+ int GetLineNumber() const;
+
+ /**
+ * Returns the 1-based column offset on the line for the associated function
+ * call.
+ * This method will return Message::kNoColumnInfo if it is unable to retrieve
+ * the column number, or if kColumnOffset was not passed as an option when
+ * capturing the StackTrace.
+ */
+ int GetColumn() const;
+
+ /**
+ * Returns the name of the resource that contains the script for the
+ * function for this StackFrame.
+ */
+ Local<String> GetScriptName() const;
+
+ /**
+ * Returns the name of the resource that contains the script for the
+ * function for this StackFrame or sourceURL value if the script name
+ * is undefined and its source ends with //# sourceURL=... string or
+ * deprecated //@ sourceURL=... string.
+ */
+ Local<String> GetScriptNameOrSourceURL() const;
+
+ /**
+ * Returns the name of the function associated with this stack frame.
+ */
+ Local<String> GetFunctionName() const;
+
+ /**
+ * Returns whether or not the associated function is compiled via a call to
+ * eval().
+ */
+ bool IsEval() const;
+
+ /**
+ * Returns whether or not the associated function is called as a
+ * constructor via "new".
+ */
+ bool IsConstructor() const;
+};
+
+
+/**
+ * A JSON Parser.
+ */
+class V8_EXPORT JSON {
+ public:
+ /**
+ * Tries to parse the string |json_string| and returns it as value if
+ * successful.
+ *
+ * \param json_string The string to parse.
+ * \return The corresponding value if successfully parsed.
+ */
+ static Local<Value> Parse(Local<String> json_string);
+};
+
+
+// --- Value ---
+
+
+/**
+ * The superclass of all JavaScript values and objects.
+ */
+class V8_EXPORT Value : public Data {
+ public:
+ /**
+ * Returns true if this value is the undefined value. See ECMA-262
+ * 4.3.10.
+ */
+ V8_INLINE(bool IsUndefined() const);
+
+ /**
+ * Returns true if this value is the null value. See ECMA-262
+ * 4.3.11.
+ */
+ V8_INLINE(bool IsNull() const);
+
+ /**
+ * Returns true if this value is true.
+ */
+ bool IsTrue() const;
+
+ /**
+ * Returns true if this value is false.
+ */
+ bool IsFalse() const;
+
+ /**
+ * Returns true if this value is an instance of the String type.
+ * See ECMA-262 8.4.
+ */
+ V8_INLINE(bool IsString() const);
+
+ /**
+ * Returns true if this value is a symbol.
+ * This is an experimental feature.
+ */
+ bool IsSymbol() const;
+
+ /**
+ * Returns true if this value is a function.
+ */
+ bool IsFunction() const;
+
+ /**
+ * Returns true if this value is an array.
+ */
+ bool IsArray() const;
+
+ /**
+ * Returns true if this value is an object.
+ */
+ bool IsObject() const;
+
+ /**
+ * Returns true if this value is boolean.
+ */
+ bool IsBoolean() const;
+
+ /**
+ * Returns true if this value is a number.
+ */
+ bool IsNumber() const;
+
+ /**
+ * Returns true if this value is external.
+ */
+ bool IsExternal() const;
+
+ /**
+ * Returns true if this value is a 32-bit signed integer.
+ */
+ bool IsInt32() const;
+
+ /**
+ * Returns true if this value is a 32-bit unsigned integer.
+ */
+ bool IsUint32() const;
+
+ /**
+ * Returns true if this value is a Date.
+ */
+ bool IsDate() const;
+
+ /**
+ * Returns true if this value is a Boolean object.
+ */
+ bool IsBooleanObject() const;
+
+ /**
+ * Returns true if this value is a Number object.
+ */
+ bool IsNumberObject() const;
+
+ /**
+ * Returns true if this value is a String object.
+ */
+ bool IsStringObject() const;
+
+ /**
+ * Returns true if this value is a Symbol object.
+ * This is an experimental feature.
+ */
+ bool IsSymbolObject() const;
+
+ /**
+ * Returns true if this value is a NativeError.
+ */
+ bool IsNativeError() const;
+
+ /**
+ * Returns true if this value is a RegExp.
+ */
+ bool IsRegExp() const;
+
+
+ /**
+ * Returns true if this value is an ArrayBuffer.
+ * This is an experimental feature.
+ */
+ bool IsArrayBuffer() const;
+
+ /**
+ * Returns true if this value is an ArrayBufferView.
+ * This is an experimental feature.
+ */
+ bool IsArrayBufferView() const;
+
+ /**
+ * Returns true if this value is one of TypedArrays.
+ * This is an experimental feature.
+ */
+ bool IsTypedArray() const;
+
+ /**
+ * Returns true if this value is an Uint8Array.
+ * This is an experimental feature.
+ */
+ bool IsUint8Array() const;
+
+ /**
+ * Returns true if this value is an Uint8ClampedArray.
+ * This is an experimental feature.
+ */
+ bool IsUint8ClampedArray() const;
+
+ /**
+ * Returns true if this value is an Int8Array.
+ * This is an experimental feature.
+ */
+ bool IsInt8Array() const;
+
+ /**
+ * Returns true if this value is an Uint16Array.
+ * This is an experimental feature.
+ */
+ bool IsUint16Array() const;
+
+ /**
+ * Returns true if this value is an Int16Array.
+ * This is an experimental feature.
+ */
+ bool IsInt16Array() const;
+
+ /**
+ * Returns true if this value is an Uint32Array.
+ * This is an experimental feature.
+ */
+ bool IsUint32Array() const;
+
+ /**
+ * Returns true if this value is an Int32Array.
+ * This is an experimental feature.
+ */
+ bool IsInt32Array() const;
+
+ /**
+ * Returns true if this value is a Float32Array.
+ * This is an experimental feature.
+ */
+ bool IsFloat32Array() const;
+
+ /**
+ * Returns true if this value is a Float64Array.
+ * This is an experimental feature.
+ */
+ bool IsFloat64Array() const;
+
+ /**
+ * Returns true if this value is a DataView.
+ * This is an experimental feature.
+ */
+ bool IsDataView() const;
+
+ Local<Boolean> ToBoolean() const;
+ Local<Number> ToNumber() const;
+ Local<String> ToString() const;
+ Local<String> ToDetailString() const;
+ Local<Object> ToObject() const;
+ Local<Integer> ToInteger() const;
+ Local<Uint32> ToUint32() const;
+ Local<Int32> ToInt32() const;
+
+ /**
+ * Attempts to convert a string to an array index.
+ * Returns an empty handle if the conversion fails.
+ */
+ Local<Uint32> ToArrayIndex() const;
+
+ bool BooleanValue() const;
+ double NumberValue() const;
+ int64_t IntegerValue() const;
+ uint32_t Uint32Value() const;
+ int32_t Int32Value() const;
+
+ /** JS == */
+ bool Equals(Handle<Value> that) const;
+ bool StrictEquals(Handle<Value> that) const;
+
+ template <class T> V8_INLINE(static Value* Cast(T* value));
+
+ private:
+ V8_INLINE(bool QuickIsUndefined() const);
+ V8_INLINE(bool QuickIsNull() const);
+ V8_INLINE(bool QuickIsString() const);
+ bool FullIsUndefined() const;
+ bool FullIsNull() const;
+ bool FullIsString() const;
+};
+
+
+/**
+ * The superclass of primitive values. See ECMA-262 4.3.2.
+ */
+class V8_EXPORT Primitive : public Value { };
+
+
+/**
+ * A primitive boolean value (ECMA-262, 4.3.14). Either the true
+ * or false value.
+ */
+class V8_EXPORT Boolean : public Primitive {
+ public:
+ bool Value() const;
+ V8_INLINE(static Handle<Boolean> New(bool value));
+};
+
+
+/**
+ * A JavaScript string value (ECMA-262, 4.3.17).
+ */
+class V8_EXPORT String : public Primitive {
+ public:
+ enum Encoding {
+ UNKNOWN_ENCODING = 0x1,
+ TWO_BYTE_ENCODING = 0x0,
+ ASCII_ENCODING = 0x4,
+ ONE_BYTE_ENCODING = 0x4
+ };
+ /**
+ * Returns the number of characters in this string.
+ */
+ int Length() const;
+
+ /**
+ * Returns the number of bytes in the UTF-8 encoded
+ * representation of this string.
+ */
+ int Utf8Length() const;
+
+ /**
+ * This function is no longer useful.
+ */
+ V8_DEPRECATED(V8_INLINE(bool MayContainNonAscii()) const) { return true; }
+
+ /**
+ * Returns whether this string is known to contain only one byte data.
+ * Does not read the string.
+ * False negatives are possible.
+ */
+ bool IsOneByte() const;
+
+ /**
+ * Returns whether this string contain only one byte data.
+ * Will read the entire string in some cases.
+ */
+ bool ContainsOnlyOneByte() const;
+
+ /**
+ * Write the contents of the string to an external buffer.
+ * If no arguments are given, expects the buffer to be large
+ * enough to hold the entire string and NULL terminator. Copies
+ * the contents of the string and the NULL terminator into the
+ * buffer.
+ *
+ * WriteUtf8 will not write partial UTF-8 sequences, preferring to stop
+ * before the end of the buffer.
+ *
+ * Copies up to length characters into the output buffer.
+ * Only null-terminates if there is enough space in the buffer.
+ *
+ * \param buffer The buffer into which the string will be copied.
+ * \param start The starting position within the string at which
+ * copying begins.
+ * \param length The number of characters to copy from the string. For
+ * WriteUtf8 the number of bytes in the buffer.
+ * \param nchars_ref The number of characters written, can be NULL.
+ * \param options Various options that might affect performance of this or
+ * subsequent operations.
+ * \return The number of characters copied to the buffer excluding the null
+ * terminator. For WriteUtf8: The number of bytes copied to the buffer
+ * including the null terminator (if written).
+ */
+ enum WriteOptions {
+ NO_OPTIONS = 0,
+ HINT_MANY_WRITES_EXPECTED = 1,
+ NO_NULL_TERMINATION = 2,
+ PRESERVE_ASCII_NULL = 4
+ };
+
+ // 16-bit character codes.
+ int Write(uint16_t* buffer,
+ int start = 0,
+ int length = -1,
+ int options = NO_OPTIONS) const;
+ // ASCII characters.
+ V8_DEPRECATED(int WriteAscii(char* buffer,
+ int start = 0,
+ int length = -1,
+ int options = NO_OPTIONS) const);
+ // One byte characters.
+ int WriteOneByte(uint8_t* buffer,
+ int start = 0,
+ int length = -1,
+ int options = NO_OPTIONS) const;
+ // UTF-8 encoded characters.
+ int WriteUtf8(char* buffer,
+ int length = -1,
+ int* nchars_ref = NULL,
+ int options = NO_OPTIONS) const;
+
+ /**
+ * A zero length string.
+ */
+ static v8::Local<v8::String> Empty();
+ V8_INLINE(static v8::Local<v8::String> Empty(Isolate* isolate));
+
+ /**
+ * Returns true if the string is external
+ */
+ bool IsExternal() const;
+
+ /**
+ * Returns true if the string is both external and ASCII
+ */
+ bool IsExternalAscii() const;
+
+ class V8_EXPORT ExternalStringResourceBase { // NOLINT
+ public:
+ virtual ~ExternalStringResourceBase() {}
+
+ protected:
+ ExternalStringResourceBase() {}
+
+ /**
+ * Internally V8 will call this Dispose method when the external string
+ * resource is no longer needed. The default implementation will use the
+ * delete operator. This method can be overridden in subclasses to
+ * control how allocated external string resources are disposed.
+ */
+ virtual void Dispose() { delete this; }
+
+ private:
+ // Disallow copying and assigning.
+ ExternalStringResourceBase(const ExternalStringResourceBase&);
+ void operator=(const ExternalStringResourceBase&);
+
+ friend class v8::internal::Heap;
+ };
+
+ /**
+ * An ExternalStringResource is a wrapper around a two-byte string
+ * buffer that resides outside V8's heap. Implement an
+ * ExternalStringResource to manage the life cycle of the underlying
+ * buffer. Note that the string data must be immutable.
+ */
+ class V8_EXPORT ExternalStringResource
+ : public ExternalStringResourceBase {
+ public:
+ /**
+ * Override the destructor to manage the life cycle of the underlying
+ * buffer.
+ */
+ virtual ~ExternalStringResource() {}
+
+ /**
+ * The string data from the underlying buffer.
+ */
+ virtual const uint16_t* data() const = 0;
+
+ /**
+ * The length of the string. That is, the number of two-byte characters.
+ */
+ virtual size_t length() const = 0;
+
+ protected:
+ ExternalStringResource() {}
+ };
+
+ /**
+ * An ExternalAsciiStringResource is a wrapper around an ASCII
+ * string buffer that resides outside V8's heap. Implement an
+ * ExternalAsciiStringResource to manage the life cycle of the
+ * underlying buffer. Note that the string data must be immutable
+ * and that the data must be strict (7-bit) ASCII, not Latin-1 or
+ * UTF-8, which would require special treatment internally in the
+ * engine and, in the case of UTF-8, do not allow efficient indexing.
+ * Use String::New or convert to 16 bit data for non-ASCII.
+ */
+
+ class V8_EXPORT ExternalAsciiStringResource
+ : public ExternalStringResourceBase {
+ public:
+ /**
+ * Override the destructor to manage the life cycle of the underlying
+ * buffer.
+ */
+ virtual ~ExternalAsciiStringResource() {}
+ /** The string data from the underlying buffer.*/
+ virtual const char* data() const = 0;
+ /** The number of ASCII characters in the string.*/
+ virtual size_t length() const = 0;
+ protected:
+ ExternalAsciiStringResource() {}
+ };
+
+ typedef ExternalAsciiStringResource ExternalOneByteStringResource;
+
+ /**
+ * If the string is an external string, return the ExternalStringResourceBase
+ * regardless of the encoding, otherwise return NULL. The encoding of the
+ * string is returned in encoding_out.
+ */
+ V8_INLINE(ExternalStringResourceBase* GetExternalStringResourceBase(
+ Encoding* encoding_out) const);
+
+ /**
+ * Get the ExternalStringResource for an external string. Returns
+ * NULL if IsExternal() doesn't return true.
+ */
+ V8_INLINE(ExternalStringResource* GetExternalStringResource() const);
+
+ /**
+ * Get the ExternalAsciiStringResource for an external ASCII string.
+ * Returns NULL if IsExternalAscii() doesn't return true.
+ */
+ const ExternalAsciiStringResource* GetExternalAsciiStringResource() const;
+
+ V8_INLINE(static String* Cast(v8::Value* obj));
+
+ // TODO(dcarney): deprecate
+ /**
+ * Allocates a new string from either UTF-8 encoded or ASCII data.
+ * The second parameter 'length' gives the buffer length. If omitted,
+ * the function calls 'strlen' to determine the buffer length.
+ */
+ V8_INLINE(static Local<String> New(const char* data, int length = -1));
+
+ // TODO(dcarney): deprecate
+ /** Allocates a new string from 16-bit character codes.*/
+ V8_INLINE(static Local<String> New(const uint16_t* data, int length = -1));
+
+ // TODO(dcarney): deprecate
+ /**
+ * Creates an internalized string (historically called a "symbol",
+ * not to be confused with ES6 symbols). Returns one if it exists already.
+ */
+ V8_INLINE(static Local<String> NewSymbol(const char* data, int length = -1));
+
+ enum NewStringType {
+ kNormalString, kInternalizedString, kUndetectableString
+ };
+
+ /** Allocates a new string from UTF-8 data.*/
+ static Local<String> NewFromUtf8(Isolate* isolate,
+ const char* data,
+ NewStringType type = kNormalString,
+ int length = -1);
+
+ /** Allocates a new string from Latin-1 data.*/
+ static Local<String> NewFromOneByte(
+ Isolate* isolate,
+ const uint8_t* data,
+ NewStringType type = kNormalString,
+ int length = -1);
+
+ /** Allocates a new string from UTF-16 data.*/
+ static Local<String> NewFromTwoByte(
+ Isolate* isolate,
+ const uint16_t* data,
+ NewStringType type = kNormalString,
+ int length = -1);
+
+ /**
+ * Creates a new string by concatenating the left and the right strings
+ * passed in as parameters.
+ */
+ static Local<String> Concat(Handle<String> left, Handle<String> right);
+
+ /**
+ * Creates a new external string using the data defined in the given
+ * resource. When the external string is no longer live on V8's heap the
+ * resource will be disposed by calling its Dispose method. The caller of
+ * this function should not otherwise delete or modify the resource. Neither
+ * should the underlying buffer be deallocated or modified except through the
+ * destructor of the external string resource.
+ */
+ static Local<String> NewExternal(ExternalStringResource* resource);
+
+ /**
+ * Associate an external string resource with this string by transforming it
+ * in place so that existing references to this string in the JavaScript heap
+ * will use the external string resource. The external string resource's
+ * character contents need to be equivalent to this string.
+ * Returns true if the string has been changed to be an external string.
+ * The string is not modified if the operation fails. See NewExternal for
+ * information on the lifetime of the resource.
+ */
+ bool MakeExternal(ExternalStringResource* resource);
+
+ /**
+ * Creates a new external string using the ASCII data defined in the given
+ * resource. When the external string is no longer live on V8's heap the
+ * resource will be disposed by calling its Dispose method. The caller of
+ * this function should not otherwise delete or modify the resource. Neither
+ * should the underlying buffer be deallocated or modified except through the
+ * destructor of the external string resource.
+ */
+ static Local<String> NewExternal(ExternalAsciiStringResource* resource);
+
+ /**
+ * Associate an external string resource with this string by transforming it
+ * in place so that existing references to this string in the JavaScript heap
+ * will use the external string resource. The external string resource's
+ * character contents need to be equivalent to this string.
+ * Returns true if the string has been changed to be an external string.
+ * The string is not modified if the operation fails. See NewExternal for
+ * information on the lifetime of the resource.
+ */
+ bool MakeExternal(ExternalAsciiStringResource* resource);
+
+ /**
+ * Returns true if this string can be made external.
+ */
+ bool CanMakeExternal();
+
+ // TODO(dcarney): deprecate
+ /** Creates an undetectable string from the supplied ASCII or UTF-8 data.*/
+ V8_INLINE(
+ static Local<String> NewUndetectable(const char* data, int length = -1));
+
+ // TODO(dcarney): deprecate
+ /** Creates an undetectable string from the supplied 16-bit character codes.*/
+ V8_INLINE(static Local<String> NewUndetectable(
+ const uint16_t* data, int length = -1));
+
+ /**
+ * Converts an object to a UTF-8-encoded character array. Useful if
+ * you want to print the object. If conversion to a string fails
+ * (e.g. due to an exception in the toString() method of the object)
+ * then the length() method returns 0 and the * operator returns
+ * NULL.
+ */
+ class V8_EXPORT Utf8Value {
+ public:
+ explicit Utf8Value(Handle<v8::Value> obj);
+ ~Utf8Value();
+ char* operator*() { return str_; }
+ const char* operator*() const { return str_; }
+ int length() const { return length_; }
+ private:
+ char* str_;
+ int length_;
+
+ // Disallow copying and assigning.
+ Utf8Value(const Utf8Value&);
+ void operator=(const Utf8Value&);
+ };
+
+ /**
+ * Converts an object to an ASCII string.
+ * Useful if you want to print the object.
+ * If conversion to a string fails (eg. due to an exception in the toString()
+ * method of the object) then the length() method returns 0 and the * operator
+ * returns NULL.
+ */
+ class V8_EXPORT AsciiValue {
+ public:
+ // TODO(dcarney): deprecate
+ explicit AsciiValue(Handle<v8::Value> obj);
+ ~AsciiValue();
+ char* operator*() { return str_; }
+ const char* operator*() const { return str_; }
+ int length() const { return length_; }
+ private:
+ char* str_;
+ int length_;
+
+ // Disallow copying and assigning.
+ AsciiValue(const AsciiValue&);
+ void operator=(const AsciiValue&);
+ };
+
+ /**
+ * Converts an object to a two-byte string.
+ * If conversion to a string fails (eg. due to an exception in the toString()
+ * method of the object) then the length() method returns 0 and the * operator
+ * returns NULL.
+ */
+ class V8_EXPORT Value {
+ public:
+ explicit Value(Handle<v8::Value> obj);
+ ~Value();
+ uint16_t* operator*() { return str_; }
+ const uint16_t* operator*() const { return str_; }
+ int length() const { return length_; }
+ private:
+ uint16_t* str_;
+ int length_;
+
+ // Disallow copying and assigning.
+ Value(const Value&);
+ void operator=(const Value&);
+ };
+
+ private:
+ void VerifyExternalStringResourceBase(ExternalStringResourceBase* v,
+ Encoding encoding) const;
+ void VerifyExternalStringResource(ExternalStringResource* val) const;
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript symbol (ECMA-262 edition 6)
+ *
+ * This is an experimental feature. Use at your own risk.
+ */
+class V8_EXPORT Symbol : public Primitive {
+ public:
+ // Returns the print name string of the symbol, or undefined if none.
+ Local<Value> Name() const;
+
+ // Create a symbol without a print name.
+ static Local<Symbol> New(Isolate* isolate);
+
+ // Create a symbol with a print name.
+ static Local<Symbol> New(Isolate *isolate, const char* data, int length = -1);
+
+ V8_INLINE(static Symbol* Cast(v8::Value* obj));
+ private:
+ Symbol();
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript number value (ECMA-262, 4.3.20)
+ */
+class V8_EXPORT Number : public Primitive {
+ public:
+ double Value() const;
+ static Local<Number> New(double value);
+ static Local<Number> New(Isolate* isolate, double value);
+ V8_INLINE(static Number* Cast(v8::Value* obj));
+ private:
+ Number();
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value representing a signed integer.
+ */
+class V8_EXPORT Integer : public Number {
+ public:
+ static Local<Integer> New(int32_t value);
+ static Local<Integer> NewFromUnsigned(uint32_t value);
+ static Local<Integer> New(int32_t value, Isolate*);
+ static Local<Integer> NewFromUnsigned(uint32_t value, Isolate*);
+ int64_t Value() const;
+ V8_INLINE(static Integer* Cast(v8::Value* obj));
+ private:
+ Integer();
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value representing a 32-bit signed integer.
+ */
+class V8_EXPORT Int32 : public Integer {
+ public:
+ int32_t Value() const;
+ private:
+ Int32();
+};
+
+
+/**
+ * A JavaScript value representing a 32-bit unsigned integer.
+ */
+class V8_EXPORT Uint32 : public Integer {
+ public:
+ uint32_t Value() const;
+ private:
+ Uint32();
+};
+
+
+enum PropertyAttribute {
+ None = 0,
+ ReadOnly = 1 << 0,
+ DontEnum = 1 << 1,
+ DontDelete = 1 << 2
+};
+
+enum ExternalArrayType {
+ kExternalByteArray = 1,
+ kExternalUnsignedByteArray,
+ kExternalShortArray,
+ kExternalUnsignedShortArray,
+ kExternalIntArray,
+ kExternalUnsignedIntArray,
+ kExternalFloatArray,
+ kExternalDoubleArray,
+ kExternalPixelArray
+};
+
+/**
+ * Accessor[Getter|Setter] are used as callback functions when
+ * setting|getting a particular property. See Object and ObjectTemplate's
+ * method SetAccessor.
+ */
+typedef Handle<Value> (*AccessorGetter)(Local<String> property,
+ const AccessorInfo& info);
+typedef void (*AccessorGetterCallback)(
+ Local<String> property,
+ const PropertyCallbackInfo<Value>& info);
+
+
+typedef void (*AccessorSetter)(Local<String> property,
+ Local<Value> value,
+ const AccessorInfo& info);
+typedef void (*AccessorSetterCallback)(
+ Local<String> property,
+ Local<Value> value,
+ const PropertyCallbackInfo<void>& info);
+
+
+/**
+ * Access control specifications.
+ *
+ * Some accessors should be accessible across contexts. These
+ * accessors have an explicit access control parameter which specifies
+ * the kind of cross-context access that should be allowed.
+ *
+ * Additionally, for security, accessors can prohibit overwriting by
+ * accessors defined in JavaScript. For objects that have such
+ * accessors either locally or in their prototype chain it is not
+ * possible to overwrite the accessor by using __defineGetter__ or
+ * __defineSetter__ from JavaScript code.
+ */
+enum AccessControl {
+ DEFAULT = 0,
+ ALL_CAN_READ = 1,
+ ALL_CAN_WRITE = 1 << 1,
+ PROHIBITS_OVERWRITING = 1 << 2
+};
+
+
+/**
+ * A JavaScript object (ECMA-262, 4.3.3)
+ */
+class V8_EXPORT Object : public Value {
+ public:
+ bool Set(Handle<Value> key,
+ Handle<Value> value,
+ PropertyAttribute attribs = None);
+
+ bool Set(uint32_t index, Handle<Value> value);
+
+ // Sets a local property on this object bypassing interceptors and
+ // overriding accessors or read-only properties.
+ //
+ // Note that if the object has an interceptor the property will be set
+ // locally, but since the interceptor takes precedence the local property
+ // will only be returned if the interceptor doesn't return a value.
+ //
+ // Note also that this only works for named properties.
+ bool ForceSet(Handle<Value> key,
+ Handle<Value> value,
+ PropertyAttribute attribs = None);
+
+ Local<Value> Get(Handle<Value> key);
+
+ Local<Value> Get(uint32_t index);
+
+ /**
+ * Gets the property attributes of a property which can be None or
+ * any combination of ReadOnly, DontEnum and DontDelete. Returns
+ * None when the property doesn't exist.
+ */
+ PropertyAttribute GetPropertyAttributes(Handle<Value> key);
+
+ bool Has(Handle<Value> key);
+
+ bool Delete(Handle<Value> key);
+
+ // Delete a property on this object bypassing interceptors and
+ // ignoring dont-delete attributes.
+ bool ForceDelete(Handle<Value> key);
+
+ bool Has(uint32_t index);
+
+ bool Delete(uint32_t index);
+
+ V8_DEPRECATED(bool SetAccessor(Handle<String> name,
+ AccessorGetter getter,
+ AccessorSetter setter = 0,
+ Handle<Value> data = Handle<Value>(),
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None));
+ bool SetAccessor(Handle<String> name,
+ AccessorGetterCallback getter,
+ AccessorSetterCallback setter = 0,
+ Handle<Value> data = Handle<Value>(),
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None);
+
+ // This function is not yet stable and should not be used at this time.
+ bool SetAccessor(Handle<String> name,
+ Handle<DeclaredAccessorDescriptor> descriptor,
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None);
+
+ /**
+ * Returns an array containing the names of the enumerable properties
+ * of this object, including properties from prototype objects. The
+ * array returned by this method contains the same values as would
+ * be enumerated by a for-in statement over this object.
+ */
+ Local<Array> GetPropertyNames();
+
+ /**
+ * This function has the same functionality as GetPropertyNames but
+ * the returned array doesn't contain the names of properties from
+ * prototype objects.
+ */
+ Local<Array> GetOwnPropertyNames();
+
+ /**
+ * Get the prototype object. This does not skip objects marked to
+ * be skipped by __proto__ and it does not consult the security
+ * handler.
+ */
+ Local<Value> GetPrototype();
+
+ /**
+ * Set the prototype object. This does not skip objects marked to
+ * be skipped by __proto__ and it does not consult the security
+ * handler.
+ */
+ bool SetPrototype(Handle<Value> prototype);
+
+ /**
+ * Finds an instance of the given function template in the prototype
+ * chain.
+ */
+ Local<Object> FindInstanceInPrototypeChain(Handle<FunctionTemplate> tmpl);
+
+ /**
+ * Call builtin Object.prototype.toString on this object.
+ * This is different from Value::ToString() that may call
+ * user-defined toString function. This one does not.
+ */
+ Local<String> ObjectProtoToString();
+
+ /**
+ * Returns the function invoked as a constructor for this object.
+ * May be the null value.
+ */
+ Local<Value> GetConstructor();
+
+ /**
+ * Returns the name of the function invoked as a constructor for this object.
+ */
+ Local<String> GetConstructorName();
+
+ /** Gets the number of internal fields for this Object. */
+ int InternalFieldCount();
+
+ /** Gets the value from an internal field. */
+ V8_INLINE(Local<Value> GetInternalField(int index));
+
+ /** Sets the value in an internal field. */
+ void SetInternalField(int index, Handle<Value> value);
+
+ /**
+ * Gets a 2-byte-aligned native pointer from an internal field. This field
+ * must have been set by SetAlignedPointerInInternalField, everything else
+ * leads to undefined behavior.
+ */
+ V8_INLINE(void* GetAlignedPointerFromInternalField(int index));
+
+ /**
+ * Sets a 2-byte-aligned native pointer in an internal field. To retrieve such
+ * a field, GetAlignedPointerFromInternalField must be used, everything else
+ * leads to undefined behavior.
+ */
+ void SetAlignedPointerInInternalField(int index, void* value);
+
+ // Testers for local properties.
+ bool HasOwnProperty(Handle<String> key);
+ bool HasRealNamedProperty(Handle<String> key);
+ bool HasRealIndexedProperty(uint32_t index);
+ bool HasRealNamedCallbackProperty(Handle<String> key);
+
+ /**
+ * If result.IsEmpty() no real property was located in the prototype chain.
+ * This means interceptors in the prototype chain are not called.
+ */
+ Local<Value> GetRealNamedPropertyInPrototypeChain(Handle<String> key);
+
+ /**
+ * If result.IsEmpty() no real property was located on the object or
+ * in the prototype chain.
+ * This means interceptors in the prototype chain are not called.
+ */
+ Local<Value> GetRealNamedProperty(Handle<String> key);
+
+ /** Tests for a named lookup interceptor.*/
+ bool HasNamedLookupInterceptor();
+
+ /** Tests for an index lookup interceptor.*/
+ bool HasIndexedLookupInterceptor();
+
+ /**
+ * Turns on access check on the object if the object is an instance of
+ * a template that has access check callbacks. If an object has no
+ * access check info, the object cannot be accessed by anyone.
+ */
+ void TurnOnAccessCheck();
+
+ /**
+ * Returns the identity hash for this object. The current implementation
+ * uses a hidden property on the object to store the identity hash.
+ *
+ * The return value will never be 0. Also, it is not guaranteed to be
+ * unique.
+ */
+ int GetIdentityHash();
+
+ /**
+ * Access hidden properties on JavaScript objects. These properties are
+ * hidden from the executing JavaScript and only accessible through the V8
+ * C++ API. Hidden properties introduced by V8 internally (for example the
+ * identity hash) are prefixed with "v8::".
+ */
+ bool SetHiddenValue(Handle<String> key, Handle<Value> value);
+ Local<Value> GetHiddenValue(Handle<String> key);
+ bool DeleteHiddenValue(Handle<String> key);
+
+ /**
+ * Returns true if this is an instance of an api function (one
+ * created from a function created from a function template) and has
+ * been modified since it was created. Note that this method is
+ * conservative and may return true for objects that haven't actually
+ * been modified.
+ */
+ bool IsDirty();
+
+ /**
+ * Clone this object with a fast but shallow copy. Values will point
+ * to the same values as the original object.
+ */
+ Local<Object> Clone();
+
+ /**
+ * Returns the context in which the object was created.
+ */
+ Local<Context> CreationContext();
+
+ /**
+ * Set the backing store of the indexed properties to be managed by the
+ * embedding layer. Access to the indexed properties will follow the rules
+ * spelled out in CanvasPixelArray.
+ * Note: The embedding program still owns the data and needs to ensure that
+ * the backing store is preserved while V8 has a reference.
+ */
+ void SetIndexedPropertiesToPixelData(uint8_t* data, int length);
+ bool HasIndexedPropertiesInPixelData();
+ uint8_t* GetIndexedPropertiesPixelData();
+ int GetIndexedPropertiesPixelDataLength();
+
+ /**
+ * Set the backing store of the indexed properties to be managed by the
+ * embedding layer. Access to the indexed properties will follow the rules
+ * spelled out for the CanvasArray subtypes in the WebGL specification.
+ * Note: The embedding program still owns the data and needs to ensure that
+ * the backing store is preserved while V8 has a reference.
+ */
+ void SetIndexedPropertiesToExternalArrayData(void* data,
+ ExternalArrayType array_type,
+ int number_of_elements);
+ bool HasIndexedPropertiesInExternalArrayData();
+ void* GetIndexedPropertiesExternalArrayData();
+ ExternalArrayType GetIndexedPropertiesExternalArrayDataType();
+ int GetIndexedPropertiesExternalArrayDataLength();
+
+ /**
+ * Checks whether a callback is set by the
+ * ObjectTemplate::SetCallAsFunctionHandler method.
+ * When an Object is callable this method returns true.
+ */
+ bool IsCallable();
+
+ /**
+ * Call an Object as a function if a callback is set by the
+ * ObjectTemplate::SetCallAsFunctionHandler method.
+ */
+ Local<Value> CallAsFunction(Handle<Object> recv,
+ int argc,
+ Handle<Value> argv[]);
+
+ /**
+ * Call an Object as a constructor if a callback is set by the
+ * ObjectTemplate::SetCallAsFunctionHandler method.
+ * Note: This method behaves like the Function::NewInstance method.
+ */
+ Local<Value> CallAsConstructor(int argc, Handle<Value> argv[]);
+
+ static Local<Object> New();
+ V8_INLINE(static Object* Cast(Value* obj));
+
+ private:
+ Object();
+ static void CheckCast(Value* obj);
+ Local<Value> SlowGetInternalField(int index);
+ void* SlowGetAlignedPointerFromInternalField(int index);
+};
+
+
+/**
+ * An instance of the built-in array constructor (ECMA-262, 15.4.2).
+ */
+class V8_EXPORT Array : public Object {
+ public:
+ uint32_t Length() const;
+
+ /**
+ * Clones an element at index |index|. Returns an empty
+ * handle if cloning fails (for any reason).
+ */
+ Local<Object> CloneElementAt(uint32_t index);
+
+ /**
+ * Creates a JavaScript array with the given length. If the length
+ * is negative the returned array will have length 0.
+ */
+ static Local<Array> New(int length = 0);
+
+ V8_INLINE(static Array* Cast(Value* obj));
+ private:
+ Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * A JavaScript function object (ECMA-262, 15.3).
+ */
+class V8_EXPORT Function : public Object {
+ public:
+ Local<Object> NewInstance() const;
+ Local<Object> NewInstance(int argc, Handle<Value> argv[]) const;
+ Local<Value> Call(Handle<Object> recv, int argc, Handle<Value> argv[]);
+ void SetName(Handle<String> name);
+ 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.
+ */
+ Handle<Value> GetInferredName() const;
+
+ /**
+ * Returns zero based line number of function body and
+ * kLineOffsetNotFound if no information available.
+ */
+ int GetScriptLineNumber() const;
+ /**
+ * Returns zero based column number of function body and
+ * kLineOffsetNotFound if no information available.
+ */
+ int GetScriptColumnNumber() const;
+
+ /**
+ * Returns scriptId object.
+ * DEPRECATED: use ScriptId() instead.
+ */
+ Handle<Value> GetScriptId() const;
+
+ /**
+ * Returns scriptId.
+ */
+ int ScriptId() const;
+
+ ScriptOrigin GetScriptOrigin() const;
+ V8_INLINE(static Function* Cast(Value* obj));
+ static const int kLineOffsetNotFound;
+
+ private:
+ Function();
+ static void CheckCast(Value* obj);
+};
+
+#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT
+// The number of required internal fields can be defined by embedder.
+#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2
+#endif
+
+/**
+ * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT ArrayBuffer : public Object {
+ public:
+ /**
+ * Allocator that V8 uses to allocate |ArrayBuffer|'s memory.
+ * The allocator is a global V8 setting. It should be set with
+ * V8::SetArrayBufferAllocator prior to creation of a first ArrayBuffer.
+ *
+ * This API is experimental and may change significantly.
+ */
+ class V8_EXPORT Allocator { // NOLINT
+ public:
+ virtual ~Allocator() {}
+
+ /**
+ * Allocate |length| bytes. Return NULL if allocation is not successful.
+ * Memory should be initialized to zeroes.
+ */
+ virtual void* Allocate(size_t length) = 0;
+
+ /**
+ * Allocate |length| bytes. Return NULL if allocation is not successful.
+ * Memory does not have to be initialized.
+ */
+ virtual void* AllocateUninitialized(size_t length) {
+ // Override with call to |Allocate| for compatibility
+ // with legacy version.
+ return Allocate(length);
+ }
+
+ /**
+ * Free the memory block of size |length|, pointed to by |data|.
+ * That memory is guaranteed to be previously allocated by |Allocate|.
+ */
+ virtual void Free(void* data, size_t length) {
+ // Override with call to |Free(void*)| for compatibility
+ // with legacy version.
+ Free(data);
+ }
+
+ /**
+ * Deprecated. Never called directly by V8.
+ * For compatibility with legacy version of this interface.
+ */
+ virtual void Free(void* data);
+ };
+
+ /**
+ * The contents of an |ArrayBuffer|. Externalization of |ArrayBuffer|
+ * returns an instance of this class, populated, with a pointer to data
+ * and byte length.
+ *
+ * The Data pointer of ArrayBuffer::Contents is always allocated with
+ * Allocator::Allocate that is set with V8::SetArrayBufferAllocator.
+ *
+ * This API is experimental and may change significantly.
+ */
+ class V8_EXPORT Contents { // NOLINT
+ public:
+ Contents() : data_(NULL), byte_length_(0) {}
+
+ void* Data() const { return data_; }
+ size_t ByteLength() const { return byte_length_; }
+
+ private:
+ void* data_;
+ size_t byte_length_;
+
+ friend class ArrayBuffer;
+ };
+
+
+ /**
+ * Data length in bytes.
+ */
+ size_t ByteLength() const;
+
+ /**
+ * Create a new ArrayBuffer. Allocate |byte_length| bytes.
+ * Allocated memory will be owned by a created ArrayBuffer and
+ * will be deallocated when it is garbage-collected,
+ * unless the object is externalized.
+ */
+ static Local<ArrayBuffer> New(size_t byte_length);
+
+ /**
+ * Create a new ArrayBuffer over an existing memory block.
+ * The created array buffer is immediately in externalized state.
+ * The memory block will not be reclaimed when a created ArrayBuffer
+ * is garbage-collected.
+ */
+ static Local<ArrayBuffer> New(void* data, size_t byte_length);
+
+ /**
+ * Returns true if ArrayBuffer is extrenalized, that is, does not
+ * own its memory block.
+ */
+ bool IsExternal() const;
+
+ /**
+ * Neuters this ArrayBuffer and all its views (typed arrays).
+ * Neutering sets the byte length of the buffer and all typed arrays to zero,
+ * preventing JavaScript from ever accessing underlying backing store.
+ * ArrayBuffer should have been externalized.
+ */
+ void Neuter();
+
+ /**
+ * Make this ArrayBuffer external. The pointer to underlying memory block
+ * and byte length are returned as |Contents| structure. After ArrayBuffer
+ * had been etxrenalized, it does no longer owns the memory block. The caller
+ * should take steps to free memory when it is no longer needed.
+ *
+ * The memory block is guaranteed to be allocated with |Allocator::Allocate|
+ * that has been set with V8::SetArrayBufferAllocator.
+ */
+ Contents Externalize();
+
+ V8_INLINE(static ArrayBuffer* Cast(Value* obj));
+
+ static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT;
+
+ private:
+ ArrayBuffer();
+ static void CheckCast(Value* obj);
+};
+
+
+#ifndef V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT
+// The number of required internal fields can be defined by embedder.
+#define V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT 2
+#endif
+
+
+/**
+ * A base class for an instance of one of "views" over ArrayBuffer,
+ * including TypedArrays and DataView (ES6 draft 15.13).
+ *
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT ArrayBufferView : public Object {
+ public:
+ /**
+ * Returns underlying ArrayBuffer.
+ */
+ Local<ArrayBuffer> Buffer();
+ /**
+ * Byte offset in |Buffer|.
+ */
+ size_t ByteOffset();
+ /**
+ * Size of a view in bytes.
+ */
+ size_t ByteLength();
+ /**
+ * Base address of a view.
+ */
+ void* BaseAddress();
+
+ V8_INLINE(static ArrayBufferView* Cast(Value* obj));
+
+ static const int kInternalFieldCount =
+ V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT;
+
+ private:
+ ArrayBufferView();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * A base class for an instance of TypedArray series of constructors
+ * (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT TypedArray : public ArrayBufferView {
+ public:
+ /**
+ * Number of elements in this typed array
+ * (e.g. for Int16Array, |ByteLength|/2).
+ */
+ size_t Length();
+
+ V8_INLINE(static TypedArray* Cast(Value* obj));
+
+ private:
+ TypedArray();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Uint8Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Uint8Array : public TypedArray {
+ public:
+ static Local<Uint8Array> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Uint8Array* Cast(Value* obj));
+
+ private:
+ Uint8Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Uint8ClampedArray constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Uint8ClampedArray : public TypedArray {
+ public:
+ static Local<Uint8ClampedArray> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Uint8ClampedArray* Cast(Value* obj));
+
+ private:
+ Uint8ClampedArray();
+ static void CheckCast(Value* obj);
+};
+
+/**
+ * An instance of Int8Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Int8Array : public TypedArray {
+ public:
+ static Local<Int8Array> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Int8Array* Cast(Value* obj));
+
+ private:
+ Int8Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Uint16Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Uint16Array : public TypedArray {
+ public:
+ static Local<Uint16Array> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Uint16Array* Cast(Value* obj));
+
+ private:
+ Uint16Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Int16Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Int16Array : public TypedArray {
+ public:
+ static Local<Int16Array> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Int16Array* Cast(Value* obj));
+
+ private:
+ Int16Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Uint32Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Uint32Array : public TypedArray {
+ public:
+ static Local<Uint32Array> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Uint32Array* Cast(Value* obj));
+
+ private:
+ Uint32Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Int32Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Int32Array : public TypedArray {
+ public:
+ static Local<Int32Array> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Int32Array* Cast(Value* obj));
+
+ private:
+ Int32Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Float32Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Float32Array : public TypedArray {
+ public:
+ static Local<Float32Array> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Float32Array* Cast(Value* obj));
+
+ private:
+ Float32Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of Float64Array constructor (ES6 draft 15.13.6).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT Float64Array : public TypedArray {
+ public:
+ static Local<Float64Array> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static Float64Array* Cast(Value* obj));
+
+ private:
+ Float64Array();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of DataView constructor (ES6 draft 15.13.7).
+ * This API is experimental and may change significantly.
+ */
+class V8_EXPORT DataView : public ArrayBufferView {
+ public:
+ static Local<DataView> New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t length);
+ V8_INLINE(static DataView* Cast(Value* obj));
+
+ private:
+ DataView();
+ static void CheckCast(Value* obj);
+};
+
+
+/**
+ * An instance of the built-in Date constructor (ECMA-262, 15.9).
+ */
+class V8_EXPORT Date : public Object {
+ public:
+ static Local<Value> New(double time);
+
+ // Deprecated, use Date::ValueOf() instead.
+ // TODO(svenpanne) Actually deprecate when Chrome is adapted.
+ double NumberValue() const { return ValueOf(); }
+
+ /**
+ * A specialization of Value::NumberValue that is more efficient
+ * because we know the structure of this object.
+ */
+ double ValueOf() const;
+
+ V8_INLINE(static Date* Cast(v8::Value* obj));
+
+ /**
+ * Notification that the embedder has changed the time zone,
+ * daylight savings time, or other date / time configuration
+ * parameters. V8 keeps a cache of various values used for
+ * date / time computation. This notification will reset
+ * those cached values for the current context so that date /
+ * time configuration changes would be reflected in the Date
+ * object.
+ *
+ * This API should not be called more than needed as it will
+ * negatively impact the performance of date operations.
+ */
+ static void DateTimeConfigurationChangeNotification();
+
+ private:
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A Number object (ECMA-262, 4.3.21).
+ */
+class V8_EXPORT NumberObject : public Object {
+ public:
+ static Local<Value> New(double value);
+
+ // Deprecated, use NumberObject::ValueOf() instead.
+ // TODO(svenpanne) Actually deprecate when Chrome is adapted.
+ double NumberValue() const { return ValueOf(); }
+
+ /**
+ * Returns the Number held by the object.
+ */
+ double ValueOf() const;
+
+ V8_INLINE(static NumberObject* Cast(v8::Value* obj));
+
+ private:
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A Boolean object (ECMA-262, 4.3.15).
+ */
+class V8_EXPORT BooleanObject : public Object {
+ public:
+ static Local<Value> New(bool value);
+
+ // Deprecated, use BooleanObject::ValueOf() instead.
+ // TODO(svenpanne) Actually deprecate when Chrome is adapted.
+ bool BooleanValue() const { return ValueOf(); }
+
+ /**
+ * Returns the Boolean held by the object.
+ */
+ bool ValueOf() const;
+
+ V8_INLINE(static BooleanObject* Cast(v8::Value* obj));
+
+ private:
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A String object (ECMA-262, 4.3.18).
+ */
+class V8_EXPORT StringObject : public Object {
+ public:
+ static Local<Value> New(Handle<String> value);
+
+ // Deprecated, use StringObject::ValueOf() instead.
+ // TODO(svenpanne) Actually deprecate when Chrome is adapted.
+ Local<String> StringValue() const { return ValueOf(); }
+
+ /**
+ * Returns the String held by the object.
+ */
+ Local<String> ValueOf() const;
+
+ V8_INLINE(static StringObject* Cast(v8::Value* obj));
+
+ private:
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A Symbol object (ECMA-262 edition 6).
+ *
+ * This is an experimental feature. Use at your own risk.
+ */
+class V8_EXPORT SymbolObject : public Object {
+ public:
+ static Local<Value> New(Isolate* isolate, Handle<Symbol> value);
+
+ // Deprecated, use SymbolObject::ValueOf() instead.
+ // TODO(svenpanne) Actually deprecate when Chrome is adapted.
+ Local<Symbol> SymbolValue() const { return ValueOf(); }
+
+ /**
+ * Returns the Symbol held by the object.
+ */
+ Local<Symbol> ValueOf() const;
+
+ V8_INLINE(static SymbolObject* Cast(v8::Value* obj));
+
+ private:
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * An instance of the built-in RegExp constructor (ECMA-262, 15.10).
+ */
+class V8_EXPORT RegExp : public Object {
+ public:
+ /**
+ * Regular expression flag bits. They can be or'ed to enable a set
+ * of flags.
+ */
+ enum Flags {
+ kNone = 0,
+ kGlobal = 1,
+ kIgnoreCase = 2,
+ kMultiline = 4
+ };
+
+ /**
+ * Creates a regular expression from the given pattern string and
+ * the flags bit field. May throw a JavaScript exception as
+ * described in ECMA-262, 15.10.4.1.
+ *
+ * For example,
+ * RegExp::New(v8::String::New("foo"),
+ * static_cast<RegExp::Flags>(kGlobal | kMultiline))
+ * is equivalent to evaluating "/foo/gm".
+ */
+ static Local<RegExp> New(Handle<String> pattern, Flags flags);
+
+ /**
+ * Returns the value of the source property: a string representing
+ * the regular expression.
+ */
+ Local<String> GetSource() const;
+
+ /**
+ * Returns the flags bit field.
+ */
+ Flags GetFlags() const;
+
+ V8_INLINE(static RegExp* Cast(v8::Value* obj));
+
+ private:
+ static void CheckCast(v8::Value* obj);
+};
+
+
+/**
+ * A JavaScript value that wraps a C++ void*. This type of value is mainly used
+ * to associate C++ data structures with JavaScript objects.
+ */
+class V8_EXPORT External : public Value {
+ public:
+ static Local<External> New(void* value);
+ V8_INLINE(static External* Cast(Value* obj));
+ void* Value() const;
+ private:
+ static void CheckCast(v8::Value* obj);
+};
+
+
+// --- Templates ---
+
+
+/**
+ * The superclass of object and function templates.
+ */
+class V8_EXPORT Template : public Data {
+ public:
+ /** Adds a property to each instance created by this template.*/
+ void Set(Handle<String> name, Handle<Data> value,
+ PropertyAttribute attributes = None);
+ V8_INLINE(void Set(const char* name, Handle<Data> value));
+ private:
+ Template();
+
+ friend class ObjectTemplate;
+ friend class FunctionTemplate;
+};
+
+
+template<typename T>
+class ReturnValue {
+ public:
+ template <class S> V8_INLINE(ReturnValue(const ReturnValue<S>& that))
+ : value_(that.value_) {
+ TYPE_CHECK(T, S);
+ }
+ // Handle setters
+ template <typename S> V8_INLINE(void Set(const Persistent<S>& handle));
+ template <typename S> V8_INLINE(void Set(const Handle<S> handle));
+ // Fast primitive setters
+ V8_INLINE(void Set(bool value));
+ V8_INLINE(void Set(double i));
+ V8_INLINE(void Set(int32_t i));
+ V8_INLINE(void Set(uint32_t i));
+ // Fast JS primitive setters
+ V8_INLINE(void SetNull());
+ V8_INLINE(void SetUndefined());
+ V8_INLINE(void SetEmptyString());
+ // Convenience getter for Isolate
+ V8_INLINE(Isolate* GetIsolate());
+
+ private:
+ template<class F> friend class ReturnValue;
+ template<class F> friend class FunctionCallbackInfo;
+ template<class F> friend class PropertyCallbackInfo;
+ V8_INLINE(internal::Object* GetDefaultValue());
+ V8_INLINE(explicit ReturnValue(internal::Object** slot));
+ internal::Object** value_;
+};
+
+
+/**
+ * The argument information given to function call callbacks. This
+ * class provides access to information about the context of the call,
+ * including the receiver, the number and values of arguments, and
+ * the holder of the function.
+ */
+template<typename T>
+class FunctionCallbackInfo {
+ public:
+ V8_INLINE(int Length() const);
+ V8_INLINE(Local<Value> operator[](int i) const);
+ V8_INLINE(Local<Function> Callee() const);
+ V8_INLINE(Local<Object> This() const);
+ V8_INLINE(Local<Object> Holder() const);
+ V8_INLINE(bool IsConstructCall() const);
+ V8_INLINE(Local<Value> Data() const);
+ V8_INLINE(Isolate* GetIsolate() const);
+ V8_INLINE(ReturnValue<T> GetReturnValue() const);
+ // This shouldn't be public, but the arm compiler needs it.
+ static const int kArgsLength = 6;
+
+ protected:
+ friend class internal::FunctionCallbackArguments;
+ friend class internal::CustomArguments<FunctionCallbackInfo>;
+ static const int kReturnValueIndex = 0;
+ static const int kReturnValueDefaultValueIndex = -1;
+ static const int kIsolateIndex = -2;
+ static const int kDataIndex = -3;
+ static const int kCalleeIndex = -4;
+ static const int kHolderIndex = -5;
+
+ V8_INLINE(FunctionCallbackInfo(internal::Object** implicit_args,
+ internal::Object** values,
+ int length,
+ bool is_construct_call));
+ internal::Object** implicit_args_;
+ internal::Object** values_;
+ int length_;
+ bool is_construct_call_;
+};
+
+
+class V8_EXPORT Arguments : public FunctionCallbackInfo<Value> {
+ private:
+ friend class internal::FunctionCallbackArguments;
+ V8_INLINE(Arguments(internal::Object** implicit_args,
+ internal::Object** values,
+ int length,
+ bool is_construct_call));
+};
+
+/**
+ * The information passed to a property callback about the context
+ * of the property access.
+ */
+template<typename T>
+class PropertyCallbackInfo {
+ public:
+ V8_INLINE(Isolate* GetIsolate() const);
+ V8_INLINE(Local<Value> Data() const);
+ V8_INLINE(Local<Object> This() const);
+ V8_INLINE(Local<Object> Holder() const);
+ V8_INLINE(ReturnValue<T> GetReturnValue() const);
+ // This shouldn't be public, but the arm compiler needs it.
+ static const int kArgsLength = 6;
+
+ protected:
+ friend class MacroAssembler;
+ friend class internal::PropertyCallbackArguments;
+ friend class internal::CustomArguments<PropertyCallbackInfo>;
+ static const int kThisIndex = 0;
+ static const int kHolderIndex = -1;
+ static const int kDataIndex = -2;
+ static const int kReturnValueIndex = -3;
+ static const int kReturnValueDefaultValueIndex = -4;
+ static const int kIsolateIndex = -5;
+
+ V8_INLINE(PropertyCallbackInfo(internal::Object** args))
+ : args_(args) { }
+ internal::Object** args_;
+};
+
+
+class V8_EXPORT AccessorInfo : public PropertyCallbackInfo<Value> {
+ private:
+ friend class internal::PropertyCallbackArguments;
+ V8_INLINE(AccessorInfo(internal::Object** args))
+ : PropertyCallbackInfo<Value>(args) { }
+};
+
+
+typedef Handle<Value> (*InvocationCallback)(const Arguments& args);
+typedef void (*FunctionCallback)(const FunctionCallbackInfo<Value>& info);
+
+/**
+ * NamedProperty[Getter|Setter] are used as interceptors on object.
+ * See ObjectTemplate::SetNamedPropertyHandler.
+ */
+typedef Handle<Value> (*NamedPropertyGetter)(Local<String> property,
+ const AccessorInfo& info);
+typedef void (*NamedPropertyGetterCallback)(
+ Local<String> property,
+ const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns the value if the setter intercepts the request.
+ * Otherwise, returns an empty handle.
+ */
+typedef Handle<Value> (*NamedPropertySetter)(Local<String> property,
+ Local<Value> value,
+ const AccessorInfo& info);
+typedef void (*NamedPropertySetterCallback)(
+ Local<String> property,
+ Local<Value> value,
+ const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns a non-empty handle if the interceptor intercepts the request.
+ * The result is an integer encoding property attributes (like v8::None,
+ * v8::DontEnum, etc.)
+ */
+typedef Handle<Integer> (*NamedPropertyQuery)(Local<String> property,
+ const AccessorInfo& info);
+typedef void (*NamedPropertyQueryCallback)(
+ Local<String> property,
+ const PropertyCallbackInfo<Integer>& info);
+
+
+/**
+ * Returns a non-empty handle if the deleter intercepts the request.
+ * The return value is true if the property could be deleted and false
+ * otherwise.
+ */
+typedef Handle<Boolean> (*NamedPropertyDeleter)(Local<String> property,
+ const AccessorInfo& info);
+typedef void (*NamedPropertyDeleterCallback)(
+ Local<String> property,
+ const PropertyCallbackInfo<Boolean>& info);
+
+
+/**
+ * Returns an array containing the names of the properties the named
+ * property getter intercepts.
+ */
+typedef Handle<Array> (*NamedPropertyEnumerator)(const AccessorInfo& info);
+typedef void (*NamedPropertyEnumeratorCallback)(
+ const PropertyCallbackInfo<Array>& info);
+
+
+/**
+ * Returns the value of the property if the getter intercepts the
+ * request. Otherwise, returns an empty handle.
+ */
+typedef Handle<Value> (*IndexedPropertyGetter)(uint32_t index,
+ const AccessorInfo& info);
+typedef void (*IndexedPropertyGetterCallback)(
+ uint32_t index,
+ const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns the value if the setter intercepts the request.
+ * Otherwise, returns an empty handle.
+ */
+typedef Handle<Value> (*IndexedPropertySetter)(uint32_t index,
+ Local<Value> value,
+ const AccessorInfo& info);
+typedef void (*IndexedPropertySetterCallback)(
+ uint32_t index,
+ Local<Value> value,
+ const PropertyCallbackInfo<Value>& info);
+
+
+/**
+ * Returns a non-empty handle if the interceptor intercepts the request.
+ * The result is an integer encoding property attributes.
+ */
+typedef Handle<Integer> (*IndexedPropertyQuery)(uint32_t index,
+ const AccessorInfo& info);
+typedef void (*IndexedPropertyQueryCallback)(
+ uint32_t index,
+ const PropertyCallbackInfo<Integer>& info);
+
+
+/**
+ * Returns a non-empty handle if the deleter intercepts the request.
+ * The return value is true if the property could be deleted and false
+ * otherwise.
+ */
+typedef Handle<Boolean> (*IndexedPropertyDeleter)(uint32_t index,
+ const AccessorInfo& info);
+typedef void (*IndexedPropertyDeleterCallback)(
+ uint32_t index,
+ const PropertyCallbackInfo<Boolean>& info);
+
+
+/**
+ * Returns an array containing the indices of the properties the
+ * indexed property getter intercepts.
+ */
+typedef Handle<Array> (*IndexedPropertyEnumerator)(const AccessorInfo& info);
+typedef void (*IndexedPropertyEnumeratorCallback)(
+ const PropertyCallbackInfo<Array>& info);
+
+
+/**
+ * Access type specification.
+ */
+enum AccessType {
+ ACCESS_GET,
+ ACCESS_SET,
+ ACCESS_HAS,
+ ACCESS_DELETE,
+ ACCESS_KEYS
+};
+
+
+/**
+ * Returns true if cross-context access should be allowed to the named
+ * property with the given key on the host object.
+ */
+typedef bool (*NamedSecurityCallback)(Local<Object> host,
+ Local<Value> key,
+ AccessType type,
+ Local<Value> data);
+
+
+/**
+ * Returns true if cross-context access should be allowed to the indexed
+ * property with the given index on the host object.
+ */
+typedef bool (*IndexedSecurityCallback)(Local<Object> host,
+ uint32_t index,
+ AccessType type,
+ Local<Value> data);
+
+
+/**
+ * A FunctionTemplate is used to create functions at runtime. There
+ * can only be one function created from a FunctionTemplate in a
+ * context. The lifetime of the created function is equal to the
+ * lifetime of the context. So in case the embedder needs to create
+ * temporary functions that can be collected using Scripts is
+ * preferred.
+ *
+ * A FunctionTemplate can have properties, these properties are added to the
+ * function object when it is created.
+ *
+ * A FunctionTemplate has a corresponding instance template which is
+ * used to create object instances when the function is used as a
+ * constructor. Properties added to the instance template are added to
+ * each object instance.
+ *
+ * A FunctionTemplate can have a prototype template. The prototype template
+ * is used to create the prototype object of the function.
+ *
+ * The following example shows how to use a FunctionTemplate:
+ *
+ * \code
+ * v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
+ * t->Set("func_property", v8::Number::New(1));
+ *
+ * v8::Local<v8::Template> proto_t = t->PrototypeTemplate();
+ * proto_t->Set("proto_method", v8::FunctionTemplate::New(InvokeCallback));
+ * proto_t->Set("proto_const", v8::Number::New(2));
+ *
+ * v8::Local<v8::ObjectTemplate> instance_t = t->InstanceTemplate();
+ * instance_t->SetAccessor("instance_accessor", InstanceAccessorCallback);
+ * instance_t->SetNamedPropertyHandler(PropertyHandlerCallback, ...);
+ * instance_t->Set("instance_property", Number::New(3));
+ *
+ * v8::Local<v8::Function> function = t->GetFunction();
+ * v8::Local<v8::Object> instance = function->NewInstance();
+ * \endcode
+ *
+ * Let's use "function" as the JS variable name of the function object
+ * and "instance" for the instance object created above. The function
+ * and the instance will have the following properties:
+ *
+ * \code
+ * func_property in function == true;
+ * function.func_property == 1;
+ *
+ * function.prototype.proto_method() invokes 'InvokeCallback'
+ * function.prototype.proto_const == 2;
+ *
+ * instance instanceof function == true;
+ * instance.instance_accessor calls 'InstanceAccessorCallback'
+ * instance.instance_property == 3;
+ * \endcode
+ *
+ * A FunctionTemplate can inherit from another one by calling the
+ * FunctionTemplate::Inherit method. The following graph illustrates
+ * the semantics of inheritance:
+ *
+ * \code
+ * FunctionTemplate Parent -> Parent() . prototype -> { }
+ * ^ ^
+ * | Inherit(Parent) | .__proto__
+ * | |
+ * FunctionTemplate Child -> Child() . prototype -> { }
+ * \endcode
+ *
+ * A FunctionTemplate 'Child' inherits from 'Parent', the prototype
+ * object of the Child() function has __proto__ pointing to the
+ * Parent() function's prototype object. An instance of the Child
+ * function has all properties on Parent's instance templates.
+ *
+ * Let Parent be the FunctionTemplate initialized in the previous
+ * section and create a Child FunctionTemplate by:
+ *
+ * \code
+ * Local<FunctionTemplate> parent = t;
+ * Local<FunctionTemplate> child = FunctionTemplate::New();
+ * child->Inherit(parent);
+ *
+ * Local<Function> child_function = child->GetFunction();
+ * Local<Object> child_instance = child_function->NewInstance();
+ * \endcode
+ *
+ * The Child function and Child instance will have the following
+ * properties:
+ *
+ * \code
+ * child_func.prototype.__proto__ == function.prototype;
+ * child_instance.instance_accessor calls 'InstanceAccessorCallback'
+ * child_instance.instance_property == 3;
+ * \endcode
+ */
+class V8_EXPORT FunctionTemplate : public Template {
+ public:
+ /** Creates a function template.*/
+ V8_DEPRECATED(static Local<FunctionTemplate> New(
+ InvocationCallback callback,
+ Handle<Value> data = Handle<Value>(),
+ Handle<Signature> signature = Handle<Signature>(),
+ int length = 0));
+ static Local<FunctionTemplate> New(
+ FunctionCallback callback = 0,
+ Handle<Value> data = Handle<Value>(),
+ Handle<Signature> signature = Handle<Signature>(),
+ int length = 0);
+
+ /** Returns the unique function instance in the current execution context.*/
+ Local<Function> GetFunction();
+
+ /**
+ * Set the call-handler callback for a FunctionTemplate. This
+ * callback is called whenever the function created from this
+ * FunctionTemplate is called.
+ */
+ V8_DEPRECATED(void SetCallHandler(InvocationCallback callback,
+ Handle<Value> data = Handle<Value>()));
+ void SetCallHandler(FunctionCallback callback,
+ Handle<Value> data = Handle<Value>());
+
+ /** Set the predefined length property for the FunctionTemplate. */
+ void SetLength(int length);
+
+ /** Get the InstanceTemplate. */
+ Local<ObjectTemplate> InstanceTemplate();
+
+ /** Causes the function template to inherit from a parent function template.*/
+ void Inherit(Handle<FunctionTemplate> parent);
+
+ /**
+ * A PrototypeTemplate is the template used to create the prototype object
+ * of the function created by this template.
+ */
+ Local<ObjectTemplate> PrototypeTemplate();
+
+ /**
+ * Set the class name of the FunctionTemplate. This is used for
+ * printing objects created with the function created from the
+ * FunctionTemplate as its constructor.
+ */
+ void SetClassName(Handle<String> name);
+
+ /**
+ * Determines whether the __proto__ accessor ignores instances of
+ * the function template. If instances of the function template are
+ * ignored, __proto__ skips all instances and instead returns the
+ * next object in the prototype chain.
+ *
+ * Call with a value of true to make the __proto__ accessor ignore
+ * instances of the function template. Call with a value of false
+ * to make the __proto__ accessor not ignore instances of the
+ * function template. By default, instances of a function template
+ * are not ignored.
+ */
+ void SetHiddenPrototype(bool value);
+
+ /**
+ * Sets the ReadOnly flag in the attributes of the 'prototype' property
+ * of functions created from this FunctionTemplate to true.
+ */
+ void ReadOnlyPrototype();
+
+ /**
+ * Returns true if the given object is an instance of this function
+ * template.
+ */
+ bool HasInstance(Handle<Value> object);
+
+ private:
+ FunctionTemplate();
+ // TODO(dcarney): Remove with SetCallHandler.
+ friend class v8::CallHandlerHelper;
+ void SetCallHandlerInternal(InvocationCallback callback, Handle<Value> data);
+ friend class Context;
+ friend class ObjectTemplate;
+};
+
+
+/**
+ * An ObjectTemplate is used to create objects at runtime.
+ *
+ * Properties added to an ObjectTemplate are added to each object
+ * created from the ObjectTemplate.
+ */
+class V8_EXPORT ObjectTemplate : public Template {
+ public:
+ /** Creates an ObjectTemplate. */
+ static Local<ObjectTemplate> New();
+
+ /** Creates a new instance of this template.*/
+ Local<Object> NewInstance();
+
+ /**
+ * Sets an accessor on the object template.
+ *
+ * Whenever the property with the given name is accessed on objects
+ * created from this ObjectTemplate the getter and setter callbacks
+ * are called instead of getting and setting the property directly
+ * on the JavaScript object.
+ *
+ * \param name The name of the property for which an accessor is added.
+ * \param getter The callback to invoke when getting the property.
+ * \param setter The callback to invoke when setting the property.
+ * \param data A piece of data that will be passed to the getter and setter
+ * callbacks whenever they are invoked.
+ * \param settings Access control settings for the accessor. This is a bit
+ * field consisting of one of more of
+ * DEFAULT = 0, ALL_CAN_READ = 1, or ALL_CAN_WRITE = 2.
+ * The default is to not allow cross-context access.
+ * ALL_CAN_READ means that all cross-context reads are allowed.
+ * ALL_CAN_WRITE means that all cross-context writes are allowed.
+ * The combination ALL_CAN_READ | ALL_CAN_WRITE can be used to allow all
+ * cross-context access.
+ * \param attribute The attributes of the property for which an accessor
+ * is added.
+ * \param signature The signature describes valid receivers for the accessor
+ * and is used to perform implicit instance checks against them. If the
+ * receiver is incompatible (i.e. is not an instance of the constructor as
+ * defined by FunctionTemplate::HasInstance()), an implicit TypeError is
+ * thrown and no callback is invoked.
+ */
+ V8_DEPRECATED(void SetAccessor(Handle<String> name,
+ AccessorGetter getter,
+ AccessorSetter setter = 0,
+ Handle<Value> data = Handle<Value>(),
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None,
+ Handle<AccessorSignature> signature =
+ Handle<AccessorSignature>()));
+ void SetAccessor(Handle<String> name,
+ AccessorGetterCallback getter,
+ AccessorSetterCallback setter = 0,
+ Handle<Value> data = Handle<Value>(),
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None,
+ Handle<AccessorSignature> signature =
+ Handle<AccessorSignature>());
+
+ // This function is not yet stable and should not be used at this time.
+ bool SetAccessor(Handle<String> name,
+ Handle<DeclaredAccessorDescriptor> descriptor,
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None,
+ Handle<AccessorSignature> signature =
+ Handle<AccessorSignature>());
+
+ /**
+ * Sets a named property handler on the object template.
+ *
+ * Whenever a named property is accessed on objects created from
+ * this object template, the provided callback is invoked instead of
+ * accessing the property directly on the JavaScript object.
+ *
+ * \param getter The callback to invoke when getting a property.
+ * \param setter The callback to invoke when setting a property.
+ * \param query The callback to invoke to check if a property is present,
+ * and if present, get its attributes.
+ * \param deleter The callback to invoke when deleting a property.
+ * \param enumerator The callback to invoke to enumerate all the named
+ * properties of an object.
+ * \param data A piece of data that will be passed to the callbacks
+ * whenever they are invoked.
+ */
+ V8_DEPRECATED(void SetNamedPropertyHandler(
+ NamedPropertyGetter getter,
+ NamedPropertySetter setter = 0,
+ NamedPropertyQuery query = 0,
+ NamedPropertyDeleter deleter = 0,
+ NamedPropertyEnumerator enumerator = 0,
+ Handle<Value> data = Handle<Value>()));
+ void SetNamedPropertyHandler(
+ NamedPropertyGetterCallback getter,
+ NamedPropertySetterCallback setter = 0,
+ NamedPropertyQueryCallback query = 0,
+ NamedPropertyDeleterCallback deleter = 0,
+ NamedPropertyEnumeratorCallback enumerator = 0,
+ Handle<Value> data = Handle<Value>());
+
+ /**
+ * Sets an indexed property handler on the object template.
+ *
+ * Whenever an indexed property is accessed on objects created from
+ * this object template, the provided callback is invoked instead of
+ * accessing the property directly on the JavaScript object.
+ *
+ * \param getter The callback to invoke when getting a property.
+ * \param setter The callback to invoke when setting a property.
+ * \param query The callback to invoke to check if an object has a property.
+ * \param deleter The callback to invoke when deleting a property.
+ * \param enumerator The callback to invoke to enumerate all the indexed
+ * properties of an object.
+ * \param data A piece of data that will be passed to the callbacks
+ * whenever they are invoked.
+ */
+ V8_DEPRECATED(void SetIndexedPropertyHandler(
+ IndexedPropertyGetter getter,
+ IndexedPropertySetter setter = 0,
+ IndexedPropertyQuery query = 0,
+ IndexedPropertyDeleter deleter = 0,
+ IndexedPropertyEnumerator enumerator = 0,
+ Handle<Value> data = Handle<Value>()));
+ void SetIndexedPropertyHandler(
+ IndexedPropertyGetterCallback getter,
+ IndexedPropertySetterCallback setter = 0,
+ IndexedPropertyQueryCallback query = 0,
+ IndexedPropertyDeleterCallback deleter = 0,
+ IndexedPropertyEnumeratorCallback enumerator = 0,
+ Handle<Value> data = Handle<Value>());
+
+ /**
+ * Sets the callback to be used when calling instances created from
+ * this template as a function. If no callback is set, instances
+ * behave like normal JavaScript objects that cannot be called as a
+ * function.
+ */
+ V8_DEPRECATED(void SetCallAsFunctionHandler(
+ InvocationCallback callback,
+ Handle<Value> data = Handle<Value>()));
+ void SetCallAsFunctionHandler(FunctionCallback callback,
+ Handle<Value> data = Handle<Value>());
+
+ /**
+ * Mark object instances of the template as undetectable.
+ *
+ * In many ways, undetectable objects behave as though they are not
+ * there. They behave like 'undefined' in conditionals and when
+ * printed. However, properties can be accessed and called as on
+ * normal objects.
+ */
+ void MarkAsUndetectable();
+
+ /**
+ * Sets access check callbacks on the object template.
+ *
+ * When accessing properties on instances of this object template,
+ * the access check callback will be called to determine whether or
+ * not to allow cross-context access to the properties.
+ * The last parameter specifies whether access checks are turned
+ * on by default on instances. If access checks are off by default,
+ * they can be turned on on individual instances by calling
+ * Object::TurnOnAccessCheck().
+ */
+ void SetAccessCheckCallbacks(NamedSecurityCallback named_handler,
+ IndexedSecurityCallback indexed_handler,
+ Handle<Value> data = Handle<Value>(),
+ bool turned_on_by_default = true);
+
+ /**
+ * Gets the number of internal fields for objects generated from
+ * this template.
+ */
+ int InternalFieldCount();
+
+ /**
+ * Sets the number of internal fields for objects generated from
+ * this template.
+ */
+ void SetInternalFieldCount(int value);
+
+ private:
+ ObjectTemplate();
+ static Local<ObjectTemplate> New(Handle<FunctionTemplate> constructor);
+ friend class FunctionTemplate;
+};
+
+
+/**
+ * A Signature specifies which receivers and arguments are valid
+ * parameters to a function.
+ */
+class V8_EXPORT Signature : public Data {
+ public:
+ static Local<Signature> New(Handle<FunctionTemplate> receiver =
+ Handle<FunctionTemplate>(),
+ int argc = 0,
+ Handle<FunctionTemplate> argv[] = 0);
+ private:
+ Signature();
+};
+
+
+/**
+ * An AccessorSignature specifies which receivers are valid parameters
+ * to an accessor callback.
+ */
+class V8_EXPORT AccessorSignature : public Data {
+ public:
+ static Local<AccessorSignature> New(Handle<FunctionTemplate> receiver =
+ Handle<FunctionTemplate>());
+ private:
+ AccessorSignature();
+};
+
+
+class V8_EXPORT DeclaredAccessorDescriptor : public Data {
+ private:
+ DeclaredAccessorDescriptor();
+};
+
+
+class V8_EXPORT ObjectOperationDescriptor : public Data {
+ public:
+ // This function is not yet stable and should not be used at this time.
+ static Local<RawOperationDescriptor> NewInternalFieldDereference(
+ Isolate* isolate,
+ int internal_field);
+ private:
+ ObjectOperationDescriptor();
+};
+
+
+enum DeclaredAccessorDescriptorDataType {
+ kDescriptorBoolType,
+ kDescriptorInt8Type, kDescriptorUint8Type,
+ kDescriptorInt16Type, kDescriptorUint16Type,
+ kDescriptorInt32Type, kDescriptorUint32Type,
+ kDescriptorFloatType, kDescriptorDoubleType
+};
+
+
+class V8_EXPORT RawOperationDescriptor : public Data {
+ public:
+ Local<DeclaredAccessorDescriptor> NewHandleDereference(Isolate* isolate);
+ Local<RawOperationDescriptor> NewRawDereference(Isolate* isolate);
+ Local<RawOperationDescriptor> NewRawShift(Isolate* isolate,
+ int16_t byte_offset);
+ Local<DeclaredAccessorDescriptor> NewPointerCompare(Isolate* isolate,
+ void* compare_value);
+ Local<DeclaredAccessorDescriptor> NewPrimitiveValue(
+ Isolate* isolate,
+ DeclaredAccessorDescriptorDataType data_type,
+ uint8_t bool_offset = 0);
+ Local<DeclaredAccessorDescriptor> NewBitmaskCompare8(Isolate* isolate,
+ uint8_t bitmask,
+ uint8_t compare_value);
+ Local<DeclaredAccessorDescriptor> NewBitmaskCompare16(
+ Isolate* isolate,
+ uint16_t bitmask,
+ uint16_t compare_value);
+ Local<DeclaredAccessorDescriptor> NewBitmaskCompare32(
+ Isolate* isolate,
+ uint32_t bitmask,
+ uint32_t compare_value);
+
+ private:
+ RawOperationDescriptor();
+};
+
+
+/**
+ * A utility for determining the type of objects based on the template
+ * they were constructed from.
+ */
+class V8_EXPORT TypeSwitch : public Data {
+ public:
+ static Local<TypeSwitch> New(Handle<FunctionTemplate> type);
+ static Local<TypeSwitch> New(int argc, Handle<FunctionTemplate> types[]);
+ int match(Handle<Value> value);
+ private:
+ TypeSwitch();
+};
+
+
+// --- Extensions ---
+
+class V8_EXPORT 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 V8_EXPORT 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,
+ 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() 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; }
+ bool auto_enable() { return auto_enable_; }
+
+ private:
+ const char* name_;
+ size_t source_length_; // expected to initialize before source_
+ ExternalAsciiStringResourceImpl source_;
+ int dep_count_;
+ const char** deps_;
+ bool auto_enable_;
+
+ // Disallow copying and assigning.
+ Extension(const Extension&);
+ void operator=(const Extension&);
+};
+
+
+void V8_EXPORT RegisterExtension(Extension* extension);
+
+
+/**
+ * Ignore
+ */
+class V8_EXPORT DeclareExtension {
+ public:
+ V8_INLINE(DeclareExtension(Extension* extension)) {
+ RegisterExtension(extension);
+ }
+};
+
+
+// --- Statics ---
+
+
+Handle<Primitive> V8_EXPORT Undefined();
+Handle<Primitive> V8_EXPORT Null();
+Handle<Boolean> V8_EXPORT True();
+Handle<Boolean> V8_EXPORT False();
+
+V8_INLINE(Handle<Primitive> Undefined(Isolate* isolate));
+V8_INLINE(Handle<Primitive> Null(Isolate* isolate));
+V8_INLINE(Handle<Boolean> True(Isolate* isolate));
+V8_INLINE(Handle<Boolean> False(Isolate* isolate));
+
+
+/**
+ * A set of constraints that specifies the limits of the runtime's memory use.
+ * You must set the heap size before initializing the VM - the size cannot be
+ * adjusted after the VM is initialized.
+ *
+ * If you are using threads then you should hold the V8::Locker lock while
+ * setting the stack limit and you must set a non-default stack limit separately
+ * for each thread.
+ */
+class V8_EXPORT ResourceConstraints {
+ public:
+ ResourceConstraints();
+ int max_young_space_size() const { return max_young_space_size_; }
+ void set_max_young_space_size(int value) { max_young_space_size_ = value; }
+ int max_old_space_size() const { return max_old_space_size_; }
+ void set_max_old_space_size(int value) { max_old_space_size_ = value; }
+ int max_executable_size() { return max_executable_size_; }
+ void set_max_executable_size(int value) { max_executable_size_ = value; }
+ uint32_t* stack_limit() const { return stack_limit_; }
+ // Sets an address beyond which the VM's stack may not grow.
+ void set_stack_limit(uint32_t* value) { stack_limit_ = value; }
+ private:
+ int max_young_space_size_;
+ int max_old_space_size_;
+ int max_executable_size_;
+ uint32_t* stack_limit_;
+};
+
+
+bool V8_EXPORT SetResourceConstraints(ResourceConstraints* constraints);
+
+
+// --- Exceptions ---
+
+
+typedef void (*FatalErrorCallback)(const char* location, const char* message);
+
+
+typedef void (*MessageCallback)(Handle<Message> message, Handle<Value> error);
+
+
+/**
+ * Schedules an exception to be thrown when returning to JavaScript. When an
+ * exception has been scheduled it is illegal to invoke any JavaScript
+ * operation; the caller must return immediately and only after the exception
+ * has been handled does it become legal to invoke JavaScript operations.
+ */
+Handle<Value> V8_EXPORT ThrowException(Handle<Value> exception);
+
+/**
+ * Create new error objects by calling the corresponding error object
+ * constructor with the message.
+ */
+class V8_EXPORT Exception {
+ public:
+ static Local<Value> RangeError(Handle<String> message);
+ static Local<Value> ReferenceError(Handle<String> message);
+ static Local<Value> SyntaxError(Handle<String> message);
+ static Local<Value> TypeError(Handle<String> message);
+ static Local<Value> Error(Handle<String> message);
+};
+
+
+// --- Counters Callbacks ---
+
+typedef int* (*CounterLookupCallback)(const char* name);
+
+typedef void* (*CreateHistogramCallback)(const char* name,
+ int min,
+ int max,
+ size_t buckets);
+
+typedef void (*AddHistogramSampleCallback)(void* histogram, int sample);
+
+// --- Memory Allocation Callback ---
+ enum ObjectSpace {
+ kObjectSpaceNewSpace = 1 << 0,
+ kObjectSpaceOldPointerSpace = 1 << 1,
+ kObjectSpaceOldDataSpace = 1 << 2,
+ kObjectSpaceCodeSpace = 1 << 3,
+ kObjectSpaceMapSpace = 1 << 4,
+ kObjectSpaceLoSpace = 1 << 5,
+
+ kObjectSpaceAll = kObjectSpaceNewSpace | kObjectSpaceOldPointerSpace |
+ kObjectSpaceOldDataSpace | kObjectSpaceCodeSpace | kObjectSpaceMapSpace |
+ kObjectSpaceLoSpace
+ };
+
+ enum AllocationAction {
+ kAllocationActionAllocate = 1 << 0,
+ kAllocationActionFree = 1 << 1,
+ kAllocationActionAll = kAllocationActionAllocate | kAllocationActionFree
+ };
+
+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,
+ Local<Value> data);
+
+// --- AllowCodeGenerationFromStrings callbacks ---
+
+/**
+ * Callback to check if code generation from strings is allowed. See
+ * Context::AllowCodeGenerationFromStrings.
+ */
+typedef bool (*AllowCodeGenerationFromStringsCallback)(Local<Context> context);
+
+// --- Garbage Collection Callbacks ---
+
+/**
+ * Applications can register callback functions which will be called
+ * before and after a garbage collection. Allocations are not
+ * allowed in the callback functions, you therefore cannot manipulate
+ * objects (set or delete properties for example) since it is possible
+ * such operations will result in the allocation of objects.
+ */
+enum GCType {
+ kGCTypeScavenge = 1 << 0,
+ kGCTypeMarkSweepCompact = 1 << 1,
+ kGCTypeAll = kGCTypeScavenge | kGCTypeMarkSweepCompact
+};
+
+enum GCCallbackFlags {
+ kNoGCCallbackFlags = 0,
+ kGCCallbackFlagCompacted = 1 << 0,
+ kGCCallbackFlagConstructRetainedObjectInfos = 1 << 1
+};
+
+typedef void (*GCPrologueCallback)(GCType type, GCCallbackFlags flags);
+typedef void (*GCEpilogueCallback)(GCType type, GCCallbackFlags flags);
+
+typedef void (*GCCallback)();
+
+
+/**
+ * Collection of V8 heap information.
+ *
+ * Instances of this class can be passed to v8::V8::HeapStatistics to
+ * get heap statistics from V8.
+ */
+class V8_EXPORT HeapStatistics {
+ public:
+ HeapStatistics();
+ size_t total_heap_size() { return total_heap_size_; }
+ size_t total_heap_size_executable() { return total_heap_size_executable_; }
+ size_t total_physical_size() { return total_physical_size_; }
+ size_t used_heap_size() { return used_heap_size_; }
+ size_t heap_size_limit() { return heap_size_limit_; }
+
+ private:
+ size_t total_heap_size_;
+ size_t total_heap_size_executable_;
+ size_t total_physical_size_;
+ size_t used_heap_size_;
+ size_t heap_size_limit_;
+
+ friend class V8;
+ friend class Isolate;
+};
+
+
+class RetainedObjectInfo;
+
+/**
+ * Isolate represents an isolated instance of the V8 engine. V8
+ * isolates have completely separate states. Objects from one isolate
+ * must not be used in other isolates. When V8 is initialized a
+ * 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 must be used to synchronize.
+ */
+class V8_EXPORT Isolate {
+ public:
+ /**
+ * Stack-allocated class which sets the isolate for all operations
+ * executed within a local scope.
+ */
+ class V8_EXPORT Scope {
+ public:
+ explicit Scope(Isolate* isolate) : isolate_(isolate) {
+ isolate->Enter();
+ }
+
+ ~Scope() { isolate_->Exit(); }
+
+ private:
+ Isolate* const isolate_;
+
+ // Prevent copying of Scope objects.
+ Scope(const Scope&);
+ Scope& operator=(const Scope&);
+ };
+
+ /**
+ * Creates a new isolate. Does not change the currently entered
+ * isolate.
+ *
+ * When an isolate is no longer used its resources should be freed
+ * by calling Dispose(). Using the delete operator is not allowed.
+ */
+ static Isolate* New();
+
+ /**
+ * Returns the entered isolate for the current thread or NULL in
+ * case there is no current isolate.
+ */
+ static Isolate* GetCurrent();
+
+ /**
+ * Methods below this point require holding a lock (using Locker) in
+ * a multi-threaded environment.
+ */
+
+ /**
+ * Sets this isolate as the entered one for the current thread.
+ * Saves the previously entered one (if any), so that it can be
+ * restored when exiting. Re-entering an isolate is allowed.
+ */
+ void Enter();
+
+ /**
+ * Exits this isolate by restoring the previously entered one in the
+ * current thread. The isolate may still stay the same, if it was
+ * entered more than once.
+ *
+ * Requires: this == Isolate::GetCurrent().
+ */
+ void Exit();
+
+ /**
+ * Disposes the isolate. The isolate must not be entered by any
+ * thread to be disposable.
+ */
+ void Dispose();
+
+ /**
+ * Associate embedder-specific data with the isolate
+ */
+ V8_INLINE(void SetData(void* data));
+
+ /**
+ * Retrieve embedder-specific data from the isolate.
+ * Returns NULL if SetData has never been called.
+ */
+ V8_INLINE(void* GetData());
+
+ /**
+ * Get statistics about the heap memory usage.
+ */
+ void GetHeapStatistics(HeapStatistics* heap_statistics);
+
+ /**
+ * Adjusts the amount of registered external memory. Used to give V8 an
+ * indication of the amount of externally allocated memory that is kept alive
+ * by JavaScript objects. V8 uses this to decide when to perform global
+ * garbage collections. Registering externally allocated memory will trigger
+ * global garbage collections more often than it would otherwise in an attempt
+ * to garbage collect the JavaScript objects that keep the externally
+ * allocated memory alive.
+ *
+ * \param change_in_bytes the change in externally allocated memory that is
+ * kept alive by JavaScript objects.
+ * \returns the adjusted value.
+ */
+ intptr_t AdjustAmountOfExternalAllocatedMemory(intptr_t change_in_bytes);
+
+ /**
+ * Returns heap profiler for this isolate. Will return NULL until the isolate
+ * is initialized.
+ */
+ HeapProfiler* GetHeapProfiler();
+
+ /**
+ * Returns CPU profiler for this isolate. Will return NULL unless the isolate
+ * is initialized. It is the embedder's responsibility to stop all CPU
+ * profiling activities if it has started any.
+ */
+ CpuProfiler* GetCpuProfiler();
+
+ /** Returns the context that is on the top of the stack. */
+ Local<Context> GetCurrentContext();
+
+ /**
+ * 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
+ * intended to be used in the before-garbage-collection callback
+ * function, for instance to simulate DOM tree connections among JS
+ * wrapper objects. Object groups for all dependent handles need to
+ * be provided for kGCTypeMarkSweepCompact collections, for all other
+ * garbage collection types it is sufficient to provide object groups
+ * for partially dependent handles only.
+ */
+ void SetObjectGroupId(const Persistent<Value>& object,
+ UniqueId id);
+
+ /**
+ * Allows the host application to declare implicit references from an object
+ * group to an object. If the objects of the object group are alive, the child
+ * object is alive too. After each garbage collection, all implicit references
+ * are removed. It is intended to be used in the before-garbage-collection
+ * callback function.
+ */
+ void SetReferenceFromGroup(UniqueId id,
+ const Persistent<Value>& child);
+
+ /**
+ * Allows the host application to declare implicit references from an object
+ * to another object. If the parent object is alive, the child object is alive
+ * too. After each garbage collection, all implicit references are removed. It
+ * is intended to be used in the before-garbage-collection callback function.
+ */
+ void SetReference(const Persistent<Object>& parent,
+ const Persistent<Value>& child);
+
+ private:
+ Isolate();
+ Isolate(const Isolate&);
+ ~Isolate();
+ Isolate& operator=(const Isolate&);
+ void* operator new(size_t size);
+ void operator delete(void*, size_t);
+};
+
+
+class V8_EXPORT StartupData {
+ public:
+ enum CompressionAlgorithm {
+ kUncompressed,
+ kBZip2
+ };
+
+ const char* data;
+ int compressed_size;
+ int raw_size;
+};
+
+
+/**
+ * A helper class for driving V8 startup data decompression. It is based on
+ * "CompressedStartupData" API functions from the V8 class. It isn't mandatory
+ * for an embedder to use this class, instead, API functions can be used
+ * directly.
+ *
+ * For an example of the class usage, see the "shell.cc" sample application.
+ */
+class V8_EXPORT StartupDataDecompressor { // NOLINT
+ public:
+ StartupDataDecompressor();
+ virtual ~StartupDataDecompressor();
+ int Decompress();
+
+ protected:
+ virtual int DecompressData(char* raw_data,
+ int* raw_data_size,
+ const char* compressed_data,
+ int compressed_data_size) = 0;
+
+ private:
+ char** raw_data;
+};
+
+
+/**
+ * EntropySource is used as a callback function when v8 needs a source
+ * of entropy.
+ */
+typedef bool (*EntropySource)(unsigned char* buffer, size_t length);
+
+
+/**
+ * ReturnAddressLocationResolver is used as a callback function when v8 is
+ * resolving the location of a return address on the stack. Profilers that
+ * change the return address on the stack can use this to resolve the stack
+ * location to whereever the profiler stashed the original return address.
+ *
+ * \param return_addr_location points to a location on stack where a machine
+ * return address resides.
+ * \returns either return_addr_location, or else a pointer to the profiler's
+ * copy of the original return address.
+ *
+ * \note the resolver function must not cause garbage collection.
+ */
+typedef uintptr_t (*ReturnAddressLocationResolver)(
+ uintptr_t return_addr_location);
+
+
+/**
+ * FunctionEntryHook is the type of the profile entry hook called at entry to
+ * any generated function when function-level profiling is enabled.
+ *
+ * \param function the address of the function that's being entered.
+ * \param return_addr_location points to a location on stack where the machine
+ * return address resides. This can be used to identify the caller of
+ * \p function, and/or modified to divert execution when \p function exits.
+ *
+ * \note the entry hook must not cause garbage collection.
+ */
+typedef void (*FunctionEntryHook)(uintptr_t function,
+ uintptr_t return_addr_location);
+
+
+/**
+ * A JIT code event is issued each time code is added, moved or removed.
+ *
+ * \note removal events are not currently issued.
+ */
+struct JitCodeEvent {
+ enum EventType {
+ CODE_ADDED,
+ CODE_MOVED,
+ CODE_REMOVED,
+ CODE_ADD_LINE_POS_INFO,
+ CODE_START_LINE_INFO_RECORDING,
+ CODE_END_LINE_INFO_RECORDING
+ };
+ // Definition of the code position type. The "POSITION" type means the place
+ // in the source code which are of interest when making stack traces to
+ // pin-point the source location of a stack frame as close as possible.
+ // The "STATEMENT_POSITION" means the place at the beginning of each
+ // statement, and is used to indicate possible break locations.
+ enum PositionType {
+ POSITION,
+ STATEMENT_POSITION
+ };
+
+ // Type of event.
+ EventType type;
+ // Start of the instructions.
+ void* code_start;
+ // Size of the instructions.
+ size_t code_len;
+ // Script info for CODE_ADDED event.
+ Handle<Script> script;
+ // User-defined data for *_LINE_INFO_* event. It's used to hold the source
+ // code line information which is returned from the
+ // CODE_START_LINE_INFO_RECORDING event. And it's passed to subsequent
+ // CODE_ADD_LINE_POS_INFO and CODE_END_LINE_INFO_RECORDING events.
+ void* user_data;
+
+ struct name_t {
+ // Name of the object associated with the code, note that the string is not
+ // zero-terminated.
+ const char* str;
+ // Number of chars in str.
+ size_t len;
+ };
+
+ struct line_info_t {
+ // PC offset
+ size_t offset;
+ // Code postion
+ size_t pos;
+ // The position type.
+ PositionType position_type;
+ };
+
+ union {
+ // Only valid for CODE_ADDED.
+ struct name_t name;
+
+ // Only valid for CODE_ADD_LINE_POS_INFO
+ struct line_info_t line_info;
+
+ // New location of instructions. Only valid for CODE_MOVED.
+ void* new_code_start;
+ };
+};
+
+/**
+ * Option flags passed to the SetJitCodeEventHandler function.
+ */
+enum JitCodeEventOptions {
+ kJitCodeEventDefault = 0,
+ // Generate callbacks for already existent code.
+ kJitCodeEventEnumExisting = 1
+};
+
+
+/**
+ * Callback function passed to SetJitCodeEventHandler.
+ *
+ * \param event code add, move or removal event.
+ */
+typedef void (*JitCodeEventHandler)(const JitCodeEvent* event);
+
+
+/**
+ * Interface for iterating through all external resources in the heap.
+ */
+class V8_EXPORT ExternalResourceVisitor { // NOLINT
+ public:
+ virtual ~ExternalResourceVisitor() {}
+ virtual void VisitExternalString(Handle<String> string) {}
+};
+
+
+/**
+ * Interface for iterating through all the persistent handles in the heap.
+ */
+class V8_EXPORT PersistentHandleVisitor { // NOLINT
+ public:
+ virtual ~PersistentHandleVisitor() {}
+ virtual void VisitPersistentHandle(Persistent<Value>* value,
+ uint16_t class_id) {}
+};
+
+
+/**
+ * Asserts that no action is performed that could cause a handle's value
+ * to be modified. Useful when otherwise unsafe handle operations need to
+ * be performed.
+ */
+class V8_EXPORT AssertNoGCScope {
+#ifndef DEBUG
+ // TODO(yangguo): remove isolate argument.
+ V8_INLINE(AssertNoGCScope(Isolate* isolate)) { }
+#else
+ AssertNoGCScope(Isolate* isolate);
+ ~AssertNoGCScope();
+ private:
+ void* disallow_heap_allocation_;
+#endif
+};
+
+
+/**
+ * Container class for static utility functions.
+ */
+class V8_EXPORT V8 {
+ public:
+ /** Set the callback to invoke in case of fatal errors. */
+ static void SetFatalErrorHandler(FatalErrorCallback that);
+
+ /**
+ * Set the callback to invoke to check if code generation from
+ * strings should be allowed.
+ */
+ static void SetAllowCodeGenerationFromStringsCallback(
+ AllowCodeGenerationFromStringsCallback that);
+
+ /**
+ * Set allocator to use for ArrayBuffer memory.
+ * The allocator should be set only once. The allocator should be set
+ * before any code tha uses ArrayBuffers is executed.
+ * This allocator is used in all isolates.
+ */
+ static void SetArrayBufferAllocator(ArrayBuffer::Allocator* allocator);
+
+ /**
+ * Ignore out-of-memory exceptions.
+ *
+ * V8 running out of memory is treated as a fatal error by default.
+ * This means that the fatal error handler is called and that V8 is
+ * terminated.
+ *
+ * IgnoreOutOfMemoryException can be used to not treat an
+ * out-of-memory situation as a fatal error. This way, the contexts
+ * that did not cause the out of memory problem might be able to
+ * continue execution.
+ */
+ static void IgnoreOutOfMemoryException();
+
+ /**
+ * Check if V8 is dead and therefore unusable. This is the case after
+ * fatal errors such as out-of-memory situations.
+ */
+ static bool IsDead();
+
+ /**
+ * The following 4 functions are to be used when V8 is built with
+ * the 'compress_startup_data' flag enabled. In this case, the
+ * embedder must decompress startup data prior to initializing V8.
+ *
+ * This is how interaction with V8 should look like:
+ * int compressed_data_count = v8::V8::GetCompressedStartupDataCount();
+ * v8::StartupData* compressed_data =
+ * new v8::StartupData[compressed_data_count];
+ * v8::V8::GetCompressedStartupData(compressed_data);
+ * ... decompress data (compressed_data can be updated in-place) ...
+ * v8::V8::SetDecompressedStartupData(compressed_data);
+ * ... now V8 can be initialized
+ * ... make sure the decompressed data stays valid until V8 shutdown
+ *
+ * A helper class StartupDataDecompressor is provided. It implements
+ * the protocol of the interaction described above, and can be used in
+ * most cases instead of calling these API functions directly.
+ */
+ static StartupData::CompressionAlgorithm GetCompressedStartupDataAlgorithm();
+ static int GetCompressedStartupDataCount();
+ static void GetCompressedStartupData(StartupData* compressed_data);
+ static void SetDecompressedStartupData(StartupData* decompressed_data);
+
+ /**
+ * Adds a message listener.
+ *
+ * The same message listener can be added more than once and in that
+ * case it will be called more than once for each message.
+ *
+ * If data is specified, it will be passed to the callback when it is called.
+ * Otherwise, the exception object will be passed to the callback instead.
+ */
+ static bool AddMessageListener(MessageCallback that,
+ Handle<Value> data = Handle<Value>());
+
+ /**
+ * Remove all message listeners from the specified callback function.
+ */
+ static void RemoveMessageListeners(MessageCallback that);
+
+ /**
+ * Tells V8 to capture current stack trace when uncaught exception occurs
+ * and report it to the message listeners. The option is off by default.
+ */
+ static void SetCaptureStackTraceForUncaughtExceptions(
+ bool capture,
+ int frame_limit = 10,
+ StackTrace::StackTraceOptions options = StackTrace::kOverview);
+
+ /**
+ * Sets V8 flags from a string.
+ */
+ static void SetFlagsFromString(const char* str, int length);
+
+ /**
+ * Sets V8 flags from the command line.
+ */
+ static void SetFlagsFromCommandLine(int* argc,
+ char** argv,
+ bool remove_flags);
+
+ /** Get the version string. */
+ static const char* GetVersion();
+
+ /**
+ * Enables the host application to provide a mechanism for recording
+ * statistics counters.
+ */
+ static void SetCounterFunction(CounterLookupCallback);
+
+ /**
+ * Enables the host application to provide a mechanism for recording
+ * histograms. The CreateHistogram function returns a
+ * histogram which will later be passed to the AddHistogramSample
+ * function.
+ */
+ static void SetCreateHistogramFunction(CreateHistogramCallback);
+ static void SetAddHistogramSampleFunction(AddHistogramSampleCallback);
+
+ /** Callback function for reporting failed access checks.*/
+ static void SetFailedAccessCheckCallbackFunction(FailedAccessCheckCallback);
+
+ /**
+ * Enables the host application to receive a notification before a
+ * garbage collection. Allocations are not allowed in the
+ * callback function, you therefore cannot manipulate objects (set
+ * or delete properties for example) since it is possible such
+ * operations will result in the allocation of objects. It is possible
+ * to specify the GCType filter for your callback. But it is not possible to
+ * register the same callback function two times with different
+ * GCType filters.
+ */
+ static void AddGCPrologueCallback(
+ GCPrologueCallback callback, GCType gc_type_filter = kGCTypeAll);
+
+ /**
+ * This function removes callback which was installed by
+ * AddGCPrologueCallback function.
+ */
+ static void RemoveGCPrologueCallback(GCPrologueCallback callback);
+
+ /**
+ * The function is deprecated. Please use AddGCPrologueCallback instead.
+ * Enables the host application to receive a notification before a
+ * garbage collection. Allocations are not allowed in the
+ * callback function, you therefore cannot manipulate objects (set
+ * or delete properties for example) since it is possible such
+ * operations will result in the allocation of objects.
+ */
+ V8_DEPRECATED(static void SetGlobalGCPrologueCallback(GCCallback));
+
+ /**
+ * Enables the host application to receive a notification after a
+ * garbage collection. Allocations are not allowed in the
+ * callback function, you therefore cannot manipulate objects (set
+ * or delete properties for example) since it is possible such
+ * operations will result in the allocation of objects. It is possible
+ * to specify the GCType filter for your callback. But it is not possible to
+ * register the same callback function two times with different
+ * GCType filters.
+ */
+ static void AddGCEpilogueCallback(
+ GCEpilogueCallback callback, GCType gc_type_filter = kGCTypeAll);
+
+ /**
+ * This function removes callback which was installed by
+ * AddGCEpilogueCallback function.
+ */
+ static void RemoveGCEpilogueCallback(GCEpilogueCallback callback);
+
+ /**
+ * The function is deprecated. Please use AddGCEpilogueCallback instead.
+ * Enables the host application to receive a notification after a
+ * major garbage collection. Allocations are not allowed in the
+ * callback function, you therefore cannot manipulate objects (set
+ * or delete properties for example) since it is possible such
+ * operations will result in the allocation of objects.
+ */
+ V8_DEPRECATED(static void SetGlobalGCEpilogueCallback(GCCallback));
+
+ /**
+ * Enables the host application to provide a mechanism to be notified
+ * and perform custom logging when V8 Allocates Executable Memory.
+ */
+ static void AddMemoryAllocationCallback(MemoryAllocationCallback callback,
+ ObjectSpace space,
+ AllocationAction action);
+
+ /**
+ * 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);
+
+ /**
+ * Initializes from snapshot if possible. Otherwise, attempts to
+ * initialize from scratch. This function is called implicitly if
+ * you use the API without calling it first.
+ */
+ static bool Initialize();
+
+ /**
+ * Allows the host application to provide a callback which can be used
+ * as a source of entropy for random number generators.
+ */
+ static void SetEntropySource(EntropySource source);
+
+ /**
+ * Allows the host application to provide a callback that allows v8 to
+ * cooperate with a profiler that rewrites return addresses on stack.
+ */
+ static void SetReturnAddressLocationResolver(
+ ReturnAddressLocationResolver return_address_resolver);
+
+ /**
+ * Deprecated, use the variant with the Isolate parameter below instead.
+ */
+ V8_DEPRECATED(static bool SetFunctionEntryHook(FunctionEntryHook entry_hook));
+
+ /**
+ * Allows the host application to provide the address of a function that's
+ * invoked on entry to every V8-generated function.
+ * Note that \p entry_hook is invoked at the very start of each
+ * generated function.
+ *
+ * \param isolate the isolate to operate on.
+ * \param entry_hook a function that will be invoked on entry to every
+ * V8-generated function.
+ * \returns true on success on supported platforms, false on failure.
+ * \note Setting an entry hook can only be done very early in an isolates
+ * lifetime, and once set, the entry hook cannot be revoked.
+ */
+ static bool SetFunctionEntryHook(Isolate* isolate,
+ FunctionEntryHook entry_hook);
+
+ /**
+ * Allows the host application to provide the address of a function that is
+ * notified each time code is added, moved or removed.
+ *
+ * \param options options for the JIT code event handler.
+ * \param event_handler the JIT code event handler, which will be invoked
+ * each time code is added, moved or removed.
+ * \note \p event_handler won't get notified of existent code.
+ * \note since code removal notifications are not currently issued, the
+ * \p event_handler may get notifications of code that overlaps earlier
+ * code notifications. This happens when code areas are reused, and the
+ * earlier overlapping code areas should therefore be discarded.
+ * \note the events passed to \p event_handler and the strings they point to
+ * are not guaranteed to live past each call. The \p event_handler must
+ * copy strings and other parameters it needs to keep around.
+ * \note the set of events declared in JitCodeEvent::EventType is expected to
+ * grow over time, and the JitCodeEvent structure is expected to accrue
+ * new members. The \p event_handler function must ignore event codes
+ * it does not recognize to maintain future compatibility.
+ */
+ static void SetJitCodeEventHandler(JitCodeEventOptions options,
+ JitCodeEventHandler event_handler);
+
+ // TODO(svenpanne) Really deprecate me when Chrome is fixed.
+ /** Deprecated. Use Isolate::AdjustAmountOfExternalAllocatedMemory instead. */
+ static intptr_t AdjustAmountOfExternalAllocatedMemory(
+ intptr_t change_in_bytes);
+
+ /**
+ * Suspends recording of tick samples in the profiler.
+ * When the V8 profiling mode is enabled (usually via command line
+ * switches) this function suspends recording of tick samples.
+ * Profiling ticks are discarded until ResumeProfiler() is called.
+ *
+ * See also the --prof and --prof_auto command line switches to
+ * enable V8 profiling.
+ */
+ V8_DEPRECATED(static void PauseProfiler());
+
+ /**
+ * Resumes recording of tick samples in the profiler.
+ * See also PauseProfiler().
+ */
+ V8_DEPRECATED(static void ResumeProfiler());
+
+ /**
+ * Return whether profiler is currently paused.
+ */
+ V8_DEPRECATED(static bool IsProfilerPaused());
+
+ /**
+ * Retrieve the V8 thread id of the calling thread.
+ *
+ * The thread id for a thread should only be retrieved after the V8
+ * lock has been acquired with a Locker object with that thread.
+ */
+ static int GetCurrentThreadId();
+
+ /**
+ * Forcefully terminate execution of a JavaScript thread. This can
+ * be used to terminate long-running scripts.
+ *
+ * TerminateExecution should only be called when then V8 lock has
+ * been acquired with a Locker object. Therefore, in order to be
+ * able to terminate long-running threads, preemption must be
+ * enabled to allow the user of TerminateExecution to acquire the
+ * lock.
+ *
+ * The termination is achieved by throwing an exception that is
+ * uncatchable by JavaScript exception handlers. Termination
+ * exceptions act as if they were caught by a C++ TryCatch exception
+ * handler. If forceful termination is used, any C++ TryCatch
+ * exception handler that catches an exception should check if that
+ * exception is a termination exception and immediately return if
+ * that is the case. Returning immediately in that case will
+ * continue the propagation of the termination exception if needed.
+ *
+ * The thread id passed to TerminateExecution must have been
+ * obtained by calling GetCurrentThreadId on the thread in question.
+ *
+ * \param thread_id The thread id of the thread to terminate.
+ */
+ static void TerminateExecution(int thread_id);
+
+ /**
+ * Forcefully terminate the current thread of JavaScript execution
+ * in the given isolate. If no isolate is provided, the default
+ * isolate is used.
+ *
+ * This method can be used by any thread even if that thread has not
+ * acquired the V8 lock with a Locker object.
+ *
+ * \param isolate The isolate in which to terminate the current JS execution.
+ */
+ static void TerminateExecution(Isolate* isolate = NULL);
+
+ /**
+ * Is V8 terminating JavaScript execution.
+ *
+ * Returns true if JavaScript execution is currently terminating
+ * because of a call to TerminateExecution. In that case there are
+ * still JavaScript frames on the stack and the termination
+ * exception is still active.
+ *
+ * \param isolate The isolate in which to check.
+ */
+ static bool IsExecutionTerminating(Isolate* isolate = NULL);
+
+ /**
+ * Resume execution capability in the given isolate, whose execution
+ * was previously forcefully terminated using TerminateExecution().
+ *
+ * When execution is forcefully terminated using TerminateExecution(),
+ * the isolate can not resume execution until all JavaScript frames
+ * have propagated the uncatchable exception which is generated. This
+ * method allows the program embedding the engine to handle the
+ * termination event and resume execution capability, even if
+ * JavaScript frames remain on the stack.
+ *
+ * This method can be used by any thread even if that thread has not
+ * acquired the V8 lock with a Locker object.
+ *
+ * \param isolate The isolate in which to resume execution capability.
+ */
+ static void CancelTerminateExecution(Isolate* isolate);
+
+ /**
+ * Releases any resources used by v8 and stops any utility threads
+ * that may be running. Note that disposing v8 is permanent, it
+ * cannot be reinitialized.
+ *
+ * It should generally not be necessary to dispose v8 before exiting
+ * a process, this should happen automatically. It is only necessary
+ * to use if the process needs the resources taken up by v8.
+ */
+ static bool Dispose();
+
+ /** Deprecated. Use Isolate::GetHeapStatistics instead. */
+ V8_DEPRECATED(static void GetHeapStatistics(HeapStatistics* heap_statistics));
+
+ /**
+ * Iterates through all external resources referenced from current isolate
+ * heap. GC is not invoked prior to iterating, therefore there is no
+ * guarantee that visited objects are still alive.
+ */
+ static void VisitExternalResources(ExternalResourceVisitor* visitor);
+
+ /**
+ * Iterates through all the persistent handles in the current isolate's heap
+ * that have class_ids.
+ */
+ static void VisitHandlesWithClassIds(PersistentHandleVisitor* visitor);
+
+ /**
+ * Iterates through all the persistent handles in the current isolate's heap
+ * that have class_ids and are candidates to be marked as partially dependent
+ * handles. This will visit handles to young objects created since the last
+ * garbage collection but is free to visit an arbitrary superset of these
+ * objects.
+ */
+ static void VisitHandlesForPartialDependence(
+ Isolate* isolate, PersistentHandleVisitor* 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(int hint = 1000);
+
+ /**
+ * Optional notification that the system is running low on memory.
+ * V8 uses these notifications to attempt to free memory.
+ */
+ static void LowMemoryNotification();
+
+ /**
+ * Optional notification that a context has been disposed. V8 uses
+ * these notifications to guide the GC heuristic. Returns the number
+ * of context disposals - including this one - since the last time
+ * V8 had a chance to clean up.
+ */
+ static int ContextDisposedNotification();
+
+ /**
+ * Initialize the ICU library bundled with V8. The embedder should only
+ * invoke this method when using the bundled ICU. Returns true on success.
+ */
+ static bool InitializeICU();
+
+ private:
+ V8();
+
+ static internal::Object** GlobalizeReference(internal::Isolate* isolate,
+ internal::Object** handle);
+ static void DisposeGlobal(internal::Object** global_handle);
+ typedef WeakReferenceCallbacks<Value, void>::Revivable RevivableCallback;
+ static void MakeWeak(internal::Object** global_handle,
+ void* data,
+ RevivableCallback weak_reference_callback);
+ static void ClearWeak(internal::Object** global_handle);
+ static int Eternalize(internal::Isolate* isolate,
+ internal::Object** handle);
+ static internal::Object** GetEternal(internal::Isolate* isolate, int index);
+
+ template <class T> friend class Handle;
+ template <class T> friend class Local;
+ template <class T> friend class Persistent;
+ friend class Context;
+};
+
+
+/**
+ * An external exception handler.
+ */
+class V8_EXPORT TryCatch {
+ public:
+ /**
+ * Creates a new try/catch block and registers it with v8. Note that
+ * all TryCatch blocks should be stack allocated because the memory
+ * location itself is compared against JavaScript try/catch blocks.
+ */
+ TryCatch();
+
+ /**
+ * Unregisters and deletes this try/catch block.
+ */
+ ~TryCatch();
+
+ /**
+ * Returns true if an exception has been caught by this try/catch block.
+ */
+ bool HasCaught() const;
+
+ /**
+ * For certain types of exceptions, it makes no sense to continue execution.
+ *
+ * If CanContinue returns false, the correct action is to perform any C++
+ * cleanup needed and then return. If CanContinue returns false and
+ * HasTerminated returns true, it is possible to call
+ * CancelTerminateExecution in order to continue calling into the engine.
+ */
+ bool CanContinue() const;
+
+ /**
+ * Returns true if an exception has been caught due to script execution
+ * being terminated.
+ *
+ * There is no JavaScript representation of an execution termination
+ * exception. Such exceptions are thrown when the TerminateExecution
+ * methods are called to terminate a long-running script.
+ *
+ * If such an exception has been thrown, HasTerminated will return true,
+ * indicating that it is possible to call CancelTerminateExecution in order
+ * to continue calling into the engine.
+ */
+ bool HasTerminated() const;
+
+ /**
+ * Throws the exception caught by this TryCatch in a way that avoids
+ * it being caught again by this same TryCatch. As with ThrowException
+ * it is illegal to execute any JavaScript operations after calling
+ * ReThrow; the caller must return immediately to where the exception
+ * is caught.
+ */
+ Handle<Value> ReThrow();
+
+ /**
+ * Returns the exception caught by this try/catch block. If no exception has
+ * been caught an empty handle is returned.
+ *
+ * The returned handle is valid until this TryCatch block has been destroyed.
+ */
+ Local<Value> Exception() const;
+
+ /**
+ * Returns the .stack property of the thrown object. If no .stack
+ * property is present an empty handle is returned.
+ */
+ Local<Value> StackTrace() const;
+
+ /**
+ * Returns the message associated with this exception. If there is
+ * no message associated an empty handle is returned.
+ *
+ * The returned handle is valid until this TryCatch block has been
+ * destroyed.
+ */
+ Local<v8::Message> Message() const;
+
+ /**
+ * Clears any exceptions that may have been caught by this try/catch block.
+ * After this method has been called, HasCaught() will return false.
+ *
+ * It is not necessary to clear a try/catch block before using it again; if
+ * another exception is thrown the previously caught exception will just be
+ * overwritten. However, it is often a good idea since it makes it easier
+ * to determine which operation threw a given exception.
+ */
+ void Reset();
+
+ /**
+ * Set verbosity of the external exception handler.
+ *
+ * By default, exceptions that are caught by an external exception
+ * handler are not reported. Call SetVerbose with true on an
+ * external exception handler to have exceptions caught by the
+ * handler reported as if they were not caught.
+ */
+ void SetVerbose(bool value);
+
+ /**
+ * Set whether or not this TryCatch should capture a Message object
+ * which holds source information about where the exception
+ * occurred. True by default.
+ */
+ void SetCaptureMessage(bool value);
+
+ private:
+ // Make it hard to create heap-allocated TryCatch blocks.
+ TryCatch(const TryCatch&);
+ void operator=(const TryCatch&);
+ void* operator new(size_t size);
+ void operator delete(void*, size_t);
+
+ v8::internal::Isolate* isolate_;
+ void* next_;
+ void* exception_;
+ void* message_obj_;
+ void* message_script_;
+ int message_start_pos_;
+ int message_end_pos_;
+ bool is_verbose_ : 1;
+ bool can_continue_ : 1;
+ bool capture_message_ : 1;
+ bool rethrow_ : 1;
+ bool has_terminated_ : 1;
+
+ friend class v8::internal::Isolate;
+};
+
+
+// --- Context ---
+
+
+/**
+ * Ignore
+ */
+class V8_EXPORT ExtensionConfiguration {
+ public:
+ ExtensionConfiguration(int name_count, const char* names[])
+ : name_count_(name_count), names_(names) { }
+ private:
+ friend class ImplementationUtilities;
+ int name_count_;
+ const char** names_;
+};
+
+
+/**
+ * A sandboxed execution context with its own set of built-in objects
+ * and functions.
+ */
+class V8_EXPORT Context {
+ public:
+ /**
+ * Returns the global proxy object or global object itself for
+ * detached contexts.
+ *
+ * Global proxy object is a thin wrapper whose prototype points to
+ * actual context's global object with the properties like Object, etc.
+ * This is done that way for security reasons (for more details see
+ * https://wiki.mozilla.org/Gecko:SplitWindow).
+ *
+ * Please note that changes to global proxy object prototype most probably
+ * would break VM---v8 expects only global object as a prototype of
+ * global proxy object.
+ *
+ * If DetachGlobal() has been invoked, Global() would return actual global
+ * object until global is reattached with ReattachGlobal().
+ */
+ Local<Object> Global();
+
+ /**
+ * Detaches the global object from its context before
+ * the global object can be reused to create a new context.
+ */
+ void DetachGlobal();
+
+ /**
+ * Reattaches a global object to a context. This can be used to
+ * restore the connection between a global object and a context
+ * after DetachGlobal has been called.
+ *
+ * \param global_object The global object to reattach to the
+ * context. For this to work, the global object must be the global
+ * object that was associated with this context before a call to
+ * DetachGlobal.
+ */
+ void ReattachGlobal(Handle<Object> global_object);
+
+ /**
+ * Creates a new context and returns a handle to the newly allocated
+ * context.
+ *
+ * \param isolate The isolate in which to create the context.
+ *
+ * \param extensions An optional extension configuration containing
+ * the extensions to be installed in the newly created context.
+ *
+ * \param global_template An optional object template from which the
+ * global object for the newly created context will be created.
+ *
+ * \param global_object An optional global object to be reused for
+ * the newly created context. This global object must have been
+ * created by a previous call to Context::New with the same global
+ * template. The state of the global object will be completely reset
+ * and only object identify will remain.
+ */
+ static Local<Context> New(
+ Isolate* isolate,
+ ExtensionConfiguration* extensions = NULL,
+ Handle<ObjectTemplate> global_template = Handle<ObjectTemplate>(),
+ Handle<Value> global_object = Handle<Value>());
+
+ /** Deprecated. Use Isolate version instead. */
+ V8_DEPRECATED(static Persistent<Context> New(
+ ExtensionConfiguration* extensions = NULL,
+ Handle<ObjectTemplate> global_template = Handle<ObjectTemplate>(),
+ Handle<Value> global_object = Handle<Value>()));
+
+ /** Returns the last entered context. */
+ static Local<Context> GetEntered();
+
+ // TODO(svenpanne) Actually deprecate this.
+ /** Deprecated. Use Isolate::GetCurrentContext instead. */
+ static Local<Context> GetCurrent();
+
+ /**
+ * Returns the context of the calling JavaScript code. That is the
+ * context of the top-most JavaScript frame. If there are no
+ * JavaScript frames an empty handle is returned.
+ */
+ static Local<Context> GetCalling();
+
+ /**
+ * Sets the security token for the context. To access an object in
+ * another context, the security tokens must match.
+ */
+ void SetSecurityToken(Handle<Value> token);
+
+ /** Restores the security token to the default value. */
+ void UseDefaultSecurityToken();
+
+ /** Returns the security token of this context.*/
+ Handle<Value> GetSecurityToken();
+
+ /**
+ * Enter this context. After entering a context, all code compiled
+ * and run is compiled and run in this context. If another context
+ * is already entered, this old context is saved so it can be
+ * restored when the new context is exited.
+ */
+ void Enter();
+
+ /**
+ * Exit this context. Exiting the current context restores the
+ * context that was in place when entering the current context.
+ */
+ void Exit();
+
+ /** Returns true if the context has experienced an out of memory situation. */
+ bool HasOutOfMemoryException();
+
+ /** Returns true if V8 has a current context. */
+ static bool InContext();
+
+ /** Returns an isolate associated with a current context. */
+ v8::Isolate* GetIsolate();
+
+ /**
+ * Gets the embedder data with the given index, which must have been set by a
+ * previous call to SetEmbedderData with the same index. Note that index 0
+ * currently has a special meaning for Chrome's debugger.
+ */
+ V8_INLINE(Local<Value> GetEmbedderData(int index));
+
+ /**
+ * Sets the embedder data with the given index, growing the data as
+ * needed. Note that index 0 currently has a special meaning for Chrome's
+ * debugger.
+ */
+ void SetEmbedderData(int index, Handle<Value> value);
+
+ /**
+ * Gets a 2-byte-aligned native pointer from the embedder data with the given
+ * index, which must have bees set by a previous call to
+ * SetAlignedPointerInEmbedderData with the same index. Note that index 0
+ * currently has a special meaning for Chrome's debugger.
+ */
+ V8_INLINE(void* GetAlignedPointerFromEmbedderData(int index));
+
+ /**
+ * Sets a 2-byte-aligned native pointer in the embedder data with the given
+ * index, growing the data as needed. Note that index 0 currently has a
+ * special meaning for Chrome's debugger.
+ */
+ void SetAlignedPointerInEmbedderData(int index, void* value);
+
+ /**
+ * Control whether code generation from strings is allowed. Calling
+ * this method with false will disable 'eval' and the 'Function'
+ * constructor for code running in this context. If 'eval' or the
+ * 'Function' constructor are used an exception will be thrown.
+ *
+ * If code generation from strings is not allowed the
+ * V8::AllowCodeGenerationFromStrings callback will be invoked if
+ * set before blocking the call to 'eval' or the 'Function'
+ * constructor. If that callback returns true, the call will be
+ * allowed, otherwise an exception will be thrown. If no callback is
+ * set an exception will be thrown.
+ */
+ 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();
+
+ /**
+ * Sets the error description for the exception that is thrown when
+ * code generation from strings is not allowed and 'eval' or the 'Function'
+ * constructor are called.
+ */
+ void SetErrorMessageForCodeGenerationFromStrings(Handle<String> message);
+
+ /**
+ * Stack-allocated class which sets the execution context for all
+ * operations executed within a local scope.
+ */
+ class Scope {
+ public:
+ explicit V8_INLINE(Scope(Handle<Context> context)) : context_(context) {
+ context_->Enter();
+ }
+ // TODO(dcarney): deprecate
+ V8_INLINE(Scope(Isolate* isolate, Persistent<Context>& context)) // NOLINT
+#ifndef V8_USE_UNSAFE_HANDLES
+ : context_(Handle<Context>::New(isolate, context)) {
+#else
+ : context_(Local<Context>::New(isolate, context)) {
+#endif
+ context_->Enter();
+ }
+ V8_INLINE(~Scope()) { context_->Exit(); }
+
+ private:
+ Handle<Context> context_;
+ };
+
+ private:
+ friend class Value;
+ friend class Script;
+ friend class Object;
+ friend class Function;
+
+ Local<Value> SlowGetEmbedderData(int index);
+ void* SlowGetAlignedPointerFromEmbedderData(int index);
+};
+
+
+/**
+ * Multiple threads in V8 are allowed, but only one thread at a time is allowed
+ * to use any given V8 isolate, see the comments in the Isolate class. The
+ * definition of 'using a V8 isolate' includes 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. 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.
+ *
+ * Sample usage:
+* \code
+ * ...
+ * {
+ * v8::Locker locker(isolate);
+ * v8::Isolate::Scope isolate_scope(isolate);
+ * ...
+ * // Code using V8 and isolate goes here.
+ * ...
+ * } // Destructor called here
+ * \endcode
+ *
+ * If you wish to stop using V8 in a thread A you can do this either by
+ * destroying the v8::Locker object as above or by constructing a v8::Unlocker
+ * object:
+ *
+ * \code
+ * {
+ * isolate->Exit();
+ * v8::Unlocker unlocker(isolate);
+ * ...
+ * // Code not using V8 goes here while V8 can run in another thread.
+ * ...
+ * } // Destructor called here.
+ * isolate->Enter();
+ * \endcode
+ *
+ * The Unlocker object is intended for use in a long-running callback from V8,
+ * where you want to release the V8 lock for other threads to use.
+ *
+ * The v8::Locker is a recursive lock, i.e. you can lock more than once in a
+ * given thread. This can be useful if you have code that can be called either
+ * from code that holds the lock or from code that does not. The Unlocker is
+ * not recursive so you can not have several Unlockers on the stack at once, and
+ * you can not use an Unlocker in a thread that is not inside a Locker's scope.
+ *
+ * An unlocker will unlock several lockers if it has to and reinstate the
+ * correct depth of locking on its destruction, e.g.:
+ *
+ * \code
+ * // V8 not locked.
+ * {
+ * v8::Locker locker(isolate);
+ * Isolate::Scope isolate_scope(isolate);
+ * // V8 locked.
+ * {
+ * v8::Locker another_locker(isolate);
+ * // V8 still locked (2 levels).
+ * {
+ * isolate->Exit();
+ * v8::Unlocker unlocker(isolate);
+ * // V8 not locked.
+ * }
+ * isolate->Enter();
+ * // V8 locked again (2 levels).
+ * }
+ * // V8 still locked (1 level).
+ * }
+ * // V8 Now no longer locked.
+ * \endcode
+ */
+class V8_EXPORT Unlocker {
+ public:
+ /**
+ * Initialize Unlocker for a given Isolate.
+ */
+ V8_INLINE(explicit Unlocker(Isolate* isolate)) { Initialize(isolate); }
+
+ /** Deprecated. Use Isolate version instead. */
+ V8_DEPRECATED(Unlocker());
+
+ ~Unlocker();
+ private:
+ void Initialize(Isolate* isolate);
+
+ internal::Isolate* isolate_;
+};
+
+
+class V8_EXPORT Locker {
+ public:
+ /**
+ * Initialize Locker for a given Isolate.
+ */
+ V8_INLINE(explicit Locker(Isolate* isolate)) { Initialize(isolate); }
+
+ /** Deprecated. Use Isolate version instead. */
+ V8_DEPRECATED(Locker());
+
+ ~Locker();
+
+ /**
+ * Start preemption.
+ *
+ * When preemption is started, a timer is fired every n milliseconds
+ * that will switch between multiple threads that are in contention
+ * for the V8 lock.
+ */
+ static void StartPreemption(int every_n_ms);
+
+ /**
+ * Stop preemption.
+ */
+ static void StopPreemption();
+
+ /**
+ * Returns whether or not the locker for a given isolate, is locked by the
+ * current thread.
+ */
+ static bool IsLocked(Isolate* isolate);
+
+ /**
+ * Returns whether v8::Locker is being used by this V8 instance.
+ */
+ static bool IsActive();
+
+ private:
+ void Initialize(Isolate* isolate);
+
+ bool has_lock_;
+ bool top_level_;
+ internal::Isolate* isolate_;
+
+ static bool active_;
+
+ // Disallow copying and assigning.
+ Locker(const Locker&);
+ void operator=(const Locker&);
+};
+
+
+/**
+ * A struct for exporting HeapStats data from V8, using "push" model.
+ */
+struct HeapStatsUpdate;
+
+
+/**
+ * An interface for exporting data from V8, using "push" model.
+ */
+class V8_EXPORT OutputStream { // NOLINT
+ public:
+ enum OutputEncoding {
+ kAscii = 0 // 7-bit ASCII.
+ };
+ enum WriteResult {
+ kContinue = 0,
+ kAbort = 1
+ };
+ virtual ~OutputStream() {}
+ /** Notify about the end of stream. */
+ virtual void EndOfStream() = 0;
+ /** Get preferred output chunk size. Called only once. */
+ virtual int GetChunkSize() { return 1024; }
+ /** Get preferred output encoding. Called only once. */
+ virtual OutputEncoding GetOutputEncoding() { return kAscii; }
+ /**
+ * Writes the next chunk of snapshot data into the stream. Writing
+ * can be stopped by returning kAbort as function result. EndOfStream
+ * will not be called in case writing was aborted.
+ */
+ virtual WriteResult WriteAsciiChunk(char* data, int size) = 0;
+ /**
+ * Writes the next chunk of heap stats data into the stream. Writing
+ * can be stopped by returning kAbort as function result. EndOfStream
+ * will not be called in case writing was aborted.
+ */
+ virtual WriteResult WriteHeapStatsChunk(HeapStatsUpdate* data, int count) {
+ return kAbort;
+ };
+};
+
+
+/**
+ * An interface for reporting progress and controlling long-running
+ * activities.
+ */
+class V8_EXPORT ActivityControl { // NOLINT
+ public:
+ enum ControlOption {
+ kContinue = 0,
+ kAbort = 1
+ };
+ virtual ~ActivityControl() {}
+ /**
+ * Notify about current progress. The activity can be stopped by
+ * returning kAbort as the callback result.
+ */
+ virtual ControlOption ReportProgressValue(int done, int total) = 0;
+};
+
+
+// --- Implementation ---
+
+
+namespace internal {
+
+const int kApiPointerSize = sizeof(void*); // NOLINT
+const int kApiIntSize = sizeof(int); // NOLINT
+
+// Tag information for HeapObject.
+const int kHeapObjectTag = 1;
+const int kHeapObjectTagSize = 2;
+const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1;
+
+// Tag information for Smi.
+const int kSmiTag = 0;
+const int kSmiTagSize = 1;
+const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
+
+template <size_t ptr_size> struct SmiTagging;
+
+template<int kSmiShiftSize>
+V8_INLINE(internal::Object* IntToSmi(int value)) {
+ int smi_shift_bits = kSmiTagSize + kSmiShiftSize;
+ intptr_t tagged_value =
+ (static_cast<intptr_t>(value) << smi_shift_bits) | kSmiTag;
+ return reinterpret_cast<internal::Object*>(tagged_value);
+}
+
+// Smi constants for 32-bit systems.
+template <> struct SmiTagging<4> {
+ static const int kSmiShiftSize = 0;
+ static const int kSmiValueSize = 31;
+ V8_INLINE(static int SmiToInt(internal::Object* value)) {
+ int shift_bits = kSmiTagSize + kSmiShiftSize;
+ // Throw away top 32 bits and shift down (requires >> to be sign extending).
+ return static_cast<int>(reinterpret_cast<intptr_t>(value)) >> shift_bits;
+ }
+ V8_INLINE(static internal::Object* IntToSmi(int value)) {
+ return internal::IntToSmi<kSmiShiftSize>(value);
+ }
+ V8_INLINE(static bool IsValidSmi(intptr_t value)) {
+ // To be representable as an tagged small integer, the two
+ // most-significant bits of 'value' must be either 00 or 11 due to
+ // sign-extension. To check this we add 01 to the two
+ // most-significant bits, and check if the most-significant bit is 0
+ //
+ // CAUTION: The original code below:
+ // bool result = ((value + 0x40000000) & 0x80000000) == 0;
+ // may lead to incorrect results according to the C language spec, and
+ // in fact doesn't work correctly with gcc4.1.1 in some cases: The
+ // compiler may produce undefined results in case of signed integer
+ // overflow. The computation must be done w/ unsigned ints.
+ return static_cast<uintptr_t>(value + 0x40000000U) < 0x80000000U;
+ }
+};
+
+// Smi constants for 64-bit systems.
+template <> struct SmiTagging<8> {
+ static const int kSmiShiftSize = 31;
+ static const int kSmiValueSize = 32;
+ V8_INLINE(static int SmiToInt(internal::Object* value)) {
+ int shift_bits = kSmiTagSize + kSmiShiftSize;
+ // Shift down and throw away top 32 bits.
+ return static_cast<int>(reinterpret_cast<intptr_t>(value) >> shift_bits);
+ }
+ V8_INLINE(static internal::Object* IntToSmi(int value)) {
+ return internal::IntToSmi<kSmiShiftSize>(value);
+ }
+ V8_INLINE(static bool IsValidSmi(intptr_t value)) {
+ // To be representable as a long smi, the value must be a 32-bit integer.
+ return (value == static_cast<int32_t>(value));
+ }
+};
+
+typedef SmiTagging<kApiPointerSize> PlatformSmiTagging;
+const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize;
+const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize;
+
+/**
+ * This class exports constants and functionality from within v8 that
+ * is necessary to implement inline functions in the v8 api. Don't
+ * depend on functions and constants defined here.
+ */
+class Internals {
+ public:
+ // These values match non-compiler-dependent values defined within
+ // the implementation of v8.
+ static const int kHeapObjectMapOffset = 0;
+ static const int kMapInstanceTypeOffset = 1 * kApiPointerSize + kApiIntSize;
+ static const int kStringResourceOffset = 3 * kApiPointerSize;
+
+ static const int kOddballKindOffset = 3 * kApiPointerSize;
+ static const int kForeignAddressOffset = kApiPointerSize;
+ static const int kJSObjectHeaderSize = 3 * kApiPointerSize;
+ static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
+ static const int kContextHeaderSize = 2 * kApiPointerSize;
+ static const int kContextEmbedderDataIndex = 65;
+ static const int kFullStringRepresentationMask = 0x07;
+ static const int kStringEncodingMask = 0x4;
+ static const int kExternalTwoByteRepresentationTag = 0x02;
+ static const int kExternalAsciiRepresentationTag = 0x06;
+
+ static const int kIsolateEmbedderDataOffset = 1 * kApiPointerSize;
+ static const int kIsolateRootsOffset = 3 * kApiPointerSize;
+ static const int kUndefinedValueRootIndex = 5;
+ static const int kNullValueRootIndex = 7;
+ static const int kTrueValueRootIndex = 8;
+ static const int kFalseValueRootIndex = 9;
+ static const int kEmptyStringRootIndex = 133;
+
+ static const int kNodeClassIdOffset = 1 * kApiPointerSize;
+ static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3;
+ static const int kNodeStateMask = 0xf;
+ static const int kNodeStateIsWeakValue = 2;
+ static const int kNodeStateIsPendingValue = 3;
+ static const int kNodeStateIsNearDeathValue = 4;
+ static const int kNodeIsIndependentShift = 4;
+ static const int kNodeIsPartiallyDependentShift = 5;
+
+ static const int kJSObjectType = 0xb1;
+ static const int kFirstNonstringType = 0x80;
+ static const int kOddballType = 0x83;
+ static const int kForeignType = 0x87;
+
+ static const int kUndefinedOddballKind = 5;
+ static const int kNullOddballKind = 3;
+
+ static void CheckInitializedImpl(v8::Isolate* isolate);
+ V8_INLINE(static void CheckInitialized(v8::Isolate* isolate)) {
+#ifdef V8_ENABLE_CHECKS
+ CheckInitializedImpl(isolate);
+#endif
+ }
+
+ V8_INLINE(static bool HasHeapObjectTag(internal::Object* value)) {
+ return ((reinterpret_cast<intptr_t>(value) & kHeapObjectTagMask) ==
+ kHeapObjectTag);
+ }
+
+ V8_INLINE(static int SmiValue(internal::Object* value)) {
+ return PlatformSmiTagging::SmiToInt(value);
+ }
+
+ V8_INLINE(static internal::Object* IntToSmi(int value)) {
+ return PlatformSmiTagging::IntToSmi(value);
+ }
+
+ V8_INLINE(static bool IsValidSmi(intptr_t value)) {
+ return PlatformSmiTagging::IsValidSmi(value);
+ }
+
+ V8_INLINE(static int GetInstanceType(internal::Object* obj)) {
+ typedef internal::Object O;
+ O* map = ReadField<O*>(obj, kHeapObjectMapOffset);
+ return ReadField<uint8_t>(map, kMapInstanceTypeOffset);
+ }
+
+ V8_INLINE(static int GetOddballKind(internal::Object* obj)) {
+ typedef internal::Object O;
+ return SmiValue(ReadField<O*>(obj, kOddballKindOffset));
+ }
+
+ V8_INLINE(static bool IsExternalTwoByteString(int instance_type)) {
+ int representation = (instance_type & kFullStringRepresentationMask);
+ return representation == kExternalTwoByteRepresentationTag;
+ }
+
+ V8_INLINE(static uint8_t GetNodeFlag(internal::Object** obj, int shift)) {
+ uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
+ return *addr & static_cast<uint8_t>(1U << shift);
+ }
+
+ V8_INLINE(static void UpdateNodeFlag(internal::Object** obj,
+ bool value, int shift)) {
+ uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
+ uint8_t mask = static_cast<uint8_t>(1 << shift);
+ *addr = static_cast<uint8_t>((*addr & ~mask) | (value << shift));
+ }
+
+ V8_INLINE(static uint8_t GetNodeState(internal::Object** obj)) {
+ uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
+ return *addr & kNodeStateMask;
+ }
+
+ V8_INLINE(static void UpdateNodeState(internal::Object** obj,
+ uint8_t value)) {
+ uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + kNodeFlagsOffset;
+ *addr = static_cast<uint8_t>((*addr & ~kNodeStateMask) | value);
+ }
+
+ V8_INLINE(static void SetEmbedderData(v8::Isolate* isolate, void* data)) {
+ uint8_t* addr = reinterpret_cast<uint8_t*>(isolate) +
+ kIsolateEmbedderDataOffset;
+ *reinterpret_cast<void**>(addr) = data;
+ }
+
+ V8_INLINE(static void* GetEmbedderData(v8::Isolate* isolate)) {
+ uint8_t* addr = reinterpret_cast<uint8_t*>(isolate) +
+ kIsolateEmbedderDataOffset;
+ return *reinterpret_cast<void**>(addr);
+ }
+
+ V8_INLINE(static internal::Object** GetRoot(v8::Isolate* isolate,
+ int index)) {
+ uint8_t* addr = reinterpret_cast<uint8_t*>(isolate) + kIsolateRootsOffset;
+ return reinterpret_cast<internal::Object**>(addr + index * kApiPointerSize);
+ }
+
+ template <typename T>
+ V8_INLINE(static T ReadField(Object* ptr, int offset)) {
+ uint8_t* addr = reinterpret_cast<uint8_t*>(ptr) + offset - kHeapObjectTag;
+ return *reinterpret_cast<T*>(addr);
+ }
+
+ template <typename T>
+ V8_INLINE(static T ReadEmbedderData(Context* context, int index)) {
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O* ctx = *reinterpret_cast<O**>(context);
+ int embedder_data_offset = I::kContextHeaderSize +
+ (internal::kApiPointerSize * I::kContextEmbedderDataIndex);
+ O* embedder_data = I::ReadField<O*>(ctx, embedder_data_offset);
+ int value_offset =
+ I::kFixedArrayHeaderSize + (internal::kApiPointerSize * index);
+ return I::ReadField<T>(embedder_data, value_offset);
+ }
+
+ V8_INLINE(static bool CanCastToHeapObject(void* o)) { return false; }
+ V8_INLINE(static bool CanCastToHeapObject(Context* o)) { return true; }
+ V8_INLINE(static bool CanCastToHeapObject(String* o)) { return true; }
+ V8_INLINE(static bool CanCastToHeapObject(Object* o)) { return true; }
+ V8_INLINE(static bool CanCastToHeapObject(Message* o)) { return true; }
+ V8_INLINE(static bool CanCastToHeapObject(StackTrace* o)) { return true; }
+ V8_INLINE(static bool CanCastToHeapObject(StackFrame* o)) { return true; }
+};
+
+} // namespace internal
+
+
+template <class T>
+Local<T>::Local() : Handle<T>() { }
+
+
+template <class T>
+Local<T> Local<T>::New(Handle<T> that) {
+ if (that.IsEmpty()) return Local<T>();
+ T* that_ptr = *that;
+ internal::Object** p = reinterpret_cast<internal::Object**>(that_ptr);
+ if (internal::Internals::CanCastToHeapObject(that_ptr)) {
+ return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(
+ reinterpret_cast<internal::HeapObject*>(*p))));
+ }
+ return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(*p)));
+}
+
+
+template <class T>
+Local<T> Local<T>::New(Isolate* isolate, Handle<T> that) {
+ return New(isolate, that.val_);
+}
+
+#ifndef V8_USE_UNSAFE_HANDLES
+template <class T>
+Local<T> Local<T>::New(Isolate* isolate, const Persistent<T>& that) {
+ return New(isolate, that.val_);
+}
+
+template <class T>
+Handle<T> Handle<T>::New(Isolate* isolate, T* that) {
+ if (that == NULL) return Handle<T>();
+ T* that_ptr = that;
+ internal::Object** p = reinterpret_cast<internal::Object**>(that_ptr);
+ return Handle<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(
+ reinterpret_cast<internal::Isolate*>(isolate), *p)));
+}
+#endif
+
+
+template <class T>
+Local<T> Local<T>::New(Isolate* isolate, T* that) {
+ if (that == NULL) return Local<T>();
+ T* that_ptr = that;
+ internal::Object** p = reinterpret_cast<internal::Object**>(that_ptr);
+ return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(
+ reinterpret_cast<internal::Isolate*>(isolate), *p)));
+}
+
+
+template<class T>
+int Local<T>::Eternalize(Isolate* isolate) {
+ return V8::Eternalize(reinterpret_cast<internal::Isolate*>(isolate),
+ reinterpret_cast<internal::Object**>(this->val_));
+}
+
+
+template<class T>
+Local<T> Local<T>::GetEternal(Isolate* isolate, int index) {
+ internal::Object** handle =
+ V8::GetEternal(reinterpret_cast<internal::Isolate*>(isolate), index);
+ return Local<T>(T::Cast(reinterpret_cast<Value*>(handle)));
+}
+
+
+#ifdef V8_USE_UNSAFE_HANDLES
+template <class T>
+Persistent<T> Persistent<T>::New(Handle<T> that) {
+ return New(Isolate::GetCurrent(), that.val_);
+}
+
+
+template <class T>
+Persistent<T> Persistent<T>::New(Isolate* isolate, Handle<T> that) {
+ return New(Isolate::GetCurrent(), that.val_);
+}
+
+template <class T>
+Persistent<T> Persistent<T>::New(Isolate* isolate, Persistent<T> that) {
+ return New(Isolate::GetCurrent(), that.val_);
+}
+#endif
+
+
+template <class T>
+T* Persistent<T>::New(Isolate* isolate, T* that) {
+ if (that == NULL) return NULL;
+ internal::Object** p = reinterpret_cast<internal::Object**>(that);
+ return reinterpret_cast<T*>(
+ V8::GlobalizeReference(reinterpret_cast<internal::Isolate*>(isolate),
+ p));
+}
+
+
+template <class T>
+bool Persistent<T>::IsIndependent() const {
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return false;
+ return I::GetNodeFlag(reinterpret_cast<internal::Object**>(this->val_),
+ I::kNodeIsIndependentShift);
+}
+
+
+template <class T>
+bool Persistent<T>::IsNearDeath() const {
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return false;
+ uint8_t node_state =
+ I::GetNodeState(reinterpret_cast<internal::Object**>(this->val_));
+ return node_state == I::kNodeStateIsNearDeathValue ||
+ node_state == I::kNodeStateIsPendingValue;
+}
+
+
+template <class T>
+bool Persistent<T>::IsWeak() const {
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return false;
+ return I::GetNodeState(reinterpret_cast<internal::Object**>(this->val_)) ==
+ I::kNodeStateIsWeakValue;
+}
+
+
+template <class T>
+void Persistent<T>::Dispose() {
+ if (this->IsEmpty()) return;
+ V8::DisposeGlobal(reinterpret_cast<internal::Object**>(this->val_));
+#ifndef V8_USE_UNSAFE_HANDLES
+ val_ = 0;
+#endif
+}
+
+
+template <class T>
+template <typename S, typename P>
+void Persistent<T>::MakeWeak(
+ P* parameters,
+ typename WeakReferenceCallbacks<S, P>::Revivable callback) {
+ TYPE_CHECK(S, T);
+ typedef typename WeakReferenceCallbacks<Value, void>::Revivable Revivable;
+ V8::MakeWeak(reinterpret_cast<internal::Object**>(this->val_),
+ parameters,
+ reinterpret_cast<Revivable>(callback));
+}
+
+
+template <class T>
+template <typename P>
+void Persistent<T>::MakeWeak(
+ P* parameters,
+ typename WeakReferenceCallbacks<T, P>::Revivable callback) {
+ MakeWeak<T, P>(parameters, callback);
+}
+
+
+template <class T>
+template <typename S, typename P>
+void Persistent<T>::MakeWeak(
+ Isolate* isolate,
+ P* parameters,
+ typename WeakReferenceCallbacks<S, P>::Revivable callback) {
+ MakeWeak<S, P>(parameters, callback);
+}
+
+
+template <class T>
+template<typename P>
+void Persistent<T>::MakeWeak(
+ Isolate* isolate,
+ P* parameters,
+ typename WeakReferenceCallbacks<T, P>::Revivable callback) {
+ MakeWeak<P>(parameters, callback);
+}
+
+
+template <class T>
+void Persistent<T>::ClearWeak() {
+ V8::ClearWeak(reinterpret_cast<internal::Object**>(this->val_));
+}
+
+
+template <class T>
+void Persistent<T>::MarkIndependent() {
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return;
+ I::UpdateNodeFlag(reinterpret_cast<internal::Object**>(this->val_),
+ true,
+ I::kNodeIsIndependentShift);
+}
+
+
+template <class T>
+void Persistent<T>::MarkPartiallyDependent() {
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return;
+ I::UpdateNodeFlag(reinterpret_cast<internal::Object**>(this->val_),
+ true,
+ I::kNodeIsPartiallyDependentShift);
+}
+
+
+template <class T>
+void Persistent<T>::Reset(Isolate* isolate, const Handle<T>& other) {
+ Dispose(isolate);
+#ifdef V8_USE_UNSAFE_HANDLES
+ *this = *New(isolate, other);
+#else
+ if (other.IsEmpty()) {
+ this->val_ = NULL;
+ return;
+ }
+ internal::Object** p = reinterpret_cast<internal::Object**>(other.val_);
+ this->val_ = reinterpret_cast<T*>(
+ V8::GlobalizeReference(reinterpret_cast<internal::Isolate*>(isolate), p));
+#endif
+}
+
+
+#ifndef V8_USE_UNSAFE_HANDLES
+template <class T>
+void Persistent<T>::Reset(Isolate* isolate, const Persistent<T>& other) {
+ Dispose(isolate);
+ if (other.IsEmpty()) {
+ this->val_ = NULL;
+ return;
+ }
+ internal::Object** p = reinterpret_cast<internal::Object**>(other.val_);
+ this->val_ = reinterpret_cast<T*>(
+ V8::GlobalizeReference(reinterpret_cast<internal::Isolate*>(isolate), p));
+}
+#endif
+
+
+template <class T>
+T* Persistent<T>::ClearAndLeak() {
+ T* old;
+#ifdef V8_USE_UNSAFE_HANDLES
+ old = **this;
+ *this = Persistent<T>();
+#else
+ old = val_;
+ val_ = NULL;
+#endif
+ return old;
+}
+
+
+template <class T>
+void Persistent<T>::SetWrapperClassId(uint16_t class_id) {
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return;
+ internal::Object** obj = reinterpret_cast<internal::Object**>(this->val_);
+ uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
+ *reinterpret_cast<uint16_t*>(addr) = class_id;
+}
+
+
+template <class T>
+uint16_t Persistent<T>::WrapperClassId() const {
+ typedef internal::Internals I;
+ if (this->IsEmpty()) return 0;
+ internal::Object** obj = reinterpret_cast<internal::Object**>(this->val_);
+ uint8_t* addr = reinterpret_cast<uint8_t*>(obj) + I::kNodeClassIdOffset;
+ return *reinterpret_cast<uint16_t*>(addr);
+}
+
+
+template<typename T>
+ReturnValue<T>::ReturnValue(internal::Object** slot) : value_(slot) {}
+
+template<typename T>
+template<typename S>
+void ReturnValue<T>::Set(const Persistent<S>& handle) {
+ TYPE_CHECK(T, S);
+ if (V8_UNLIKELY(handle.IsEmpty())) {
+ *value_ = GetDefaultValue();
+ } else {
+ *value_ = *reinterpret_cast<internal::Object**>(*handle);
+ }
+}
+
+template<typename T>
+template<typename S>
+void ReturnValue<T>::Set(const Handle<S> handle) {
+ TYPE_CHECK(T, S);
+ if (V8_UNLIKELY(handle.IsEmpty())) {
+ *value_ = GetDefaultValue();
+ } else {
+ *value_ = *reinterpret_cast<internal::Object**>(*handle);
+ }
+}
+
+template<typename T>
+void ReturnValue<T>::Set(double i) {
+ TYPE_CHECK(T, Number);
+ Set(Number::New(GetIsolate(), i));
+}
+
+template<typename T>
+void ReturnValue<T>::Set(int32_t i) {
+ TYPE_CHECK(T, Integer);
+ typedef internal::Internals I;
+ if (V8_LIKELY(I::IsValidSmi(i))) {
+ *value_ = I::IntToSmi(i);
+ return;
+ }
+ Set(Integer::New(i, GetIsolate()));
+}
+
+template<typename T>
+void ReturnValue<T>::Set(uint32_t i) {
+ TYPE_CHECK(T, Integer);
+ typedef internal::Internals I;
+ // Can't simply use INT32_MAX here for whatever reason.
+ bool fits_into_int32_t = (i & (1U << 31)) == 0;
+ if (V8_LIKELY(fits_into_int32_t)) {
+ Set(static_cast<int32_t>(i));
+ return;
+ }
+ Set(Integer::NewFromUnsigned(i, GetIsolate()));
+}
+
+template<typename T>
+void ReturnValue<T>::Set(bool value) {
+ TYPE_CHECK(T, Boolean);
+ typedef internal::Internals I;
+ int root_index;
+ if (value) {
+ root_index = I::kTrueValueRootIndex;
+ } else {
+ root_index = I::kFalseValueRootIndex;
+ }
+ *value_ = *I::GetRoot(GetIsolate(), root_index);
+}
+
+template<typename T>
+void ReturnValue<T>::SetNull() {
+ TYPE_CHECK(T, Primitive);
+ typedef internal::Internals I;
+ *value_ = *I::GetRoot(GetIsolate(), I::kNullValueRootIndex);
+}
+
+template<typename T>
+void ReturnValue<T>::SetUndefined() {
+ TYPE_CHECK(T, Primitive);
+ typedef internal::Internals I;
+ *value_ = *I::GetRoot(GetIsolate(), I::kUndefinedValueRootIndex);
+}
+
+template<typename T>
+void ReturnValue<T>::SetEmptyString() {
+ TYPE_CHECK(T, String);
+ typedef internal::Internals I;
+ *value_ = *I::GetRoot(GetIsolate(), I::kEmptyStringRootIndex);
+}
+
+template<typename T>
+Isolate* ReturnValue<T>::GetIsolate() {
+ // Isolate is always the pointer below the default value on the stack.
+ return *reinterpret_cast<Isolate**>(&value_[-2]);
+}
+
+template<typename T>
+internal::Object* ReturnValue<T>::GetDefaultValue() {
+ // Default value is always the pointer below value_ on the stack.
+ return value_[-1];
+}
+
+
+template<typename T>
+FunctionCallbackInfo<T>::FunctionCallbackInfo(internal::Object** implicit_args,
+ internal::Object** values,
+ int length,
+ bool is_construct_call)
+ : implicit_args_(implicit_args),
+ values_(values),
+ length_(length),
+ is_construct_call_(is_construct_call) { }
+
+
+Arguments::Arguments(internal::Object** args,
+ internal::Object** values,
+ int length,
+ bool is_construct_call)
+ : FunctionCallbackInfo<Value>(args, values, length, is_construct_call) { }
+
+
+template<typename T>
+Local<Value> FunctionCallbackInfo<T>::operator[](int i) const {
+ if (i < 0 || length_ <= i) return Local<Value>(*Undefined());
+ return Local<Value>(reinterpret_cast<Value*>(values_ - i));
+}
+
+
+template<typename T>
+Local<Function> FunctionCallbackInfo<T>::Callee() const {
+ return Local<Function>(reinterpret_cast<Function*>(
+ &implicit_args_[kCalleeIndex]));
+}
+
+
+template<typename T>
+Local<Object> FunctionCallbackInfo<T>::This() const {
+ return Local<Object>(reinterpret_cast<Object*>(values_ + 1));
+}
+
+
+template<typename T>
+Local<Object> FunctionCallbackInfo<T>::Holder() const {
+ return Local<Object>(reinterpret_cast<Object*>(
+ &implicit_args_[kHolderIndex]));
+}
+
+
+template<typename T>
+Local<Value> FunctionCallbackInfo<T>::Data() const {
+ return Local<Value>(reinterpret_cast<Value*>(&implicit_args_[kDataIndex]));
+}
+
+
+template<typename T>
+Isolate* FunctionCallbackInfo<T>::GetIsolate() const {
+ return *reinterpret_cast<Isolate**>(&implicit_args_[kIsolateIndex]);
+}
+
+
+template<typename T>
+ReturnValue<T> FunctionCallbackInfo<T>::GetReturnValue() const {
+ return ReturnValue<T>(&implicit_args_[kReturnValueIndex]);
+}
+
+
+template<typename T>
+bool FunctionCallbackInfo<T>::IsConstructCall() const {
+ return is_construct_call_;
+}
+
+
+template<typename T>
+int FunctionCallbackInfo<T>::Length() const {
+ return length_;
+}
+
+
+template <class T>
+Local<T> HandleScope::Close(Handle<T> value) {
+ internal::Object** before = reinterpret_cast<internal::Object**>(*value);
+ internal::Object** after = RawClose(before);
+ return Local<T>(reinterpret_cast<T*>(after));
+}
+
+Handle<Value> ScriptOrigin::ResourceName() const {
+ return resource_name_;
+}
+
+
+Handle<Integer> ScriptOrigin::ResourceLineOffset() const {
+ return resource_line_offset_;
+}
+
+
+Handle<Integer> ScriptOrigin::ResourceColumnOffset() const {
+ return resource_column_offset_;
+}
+
+Handle<Boolean> ScriptOrigin::ResourceIsSharedCrossOrigin() const {
+ return resource_is_shared_cross_origin_;
+}
+
+
+Handle<Boolean> Boolean::New(bool value) {
+ return value ? True() : False();
+}
+
+
+void Template::Set(const char* name, v8::Handle<Data> value) {
+ Set(v8::String::New(name), value);
+}
+
+
+Local<Value> Object::GetInternalField(int index) {
+#ifndef V8_ENABLE_CHECKS
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O* obj = *reinterpret_cast<O**>(this);
+ // Fast path: If the object is a plain JSObject, which is the common case, we
+ // know where to find the internal fields and can return the value directly.
+ if (I::GetInstanceType(obj) == I::kJSObjectType) {
+ int offset = I::kJSObjectHeaderSize + (internal::kApiPointerSize * index);
+ O* value = I::ReadField<O*>(obj, offset);
+ O** result = HandleScope::CreateHandle(value);
+ return Local<Value>(reinterpret_cast<Value*>(result));
+ }
+#endif
+ return SlowGetInternalField(index);
+}
+
+
+void* Object::GetAlignedPointerFromInternalField(int index) {
+#ifndef V8_ENABLE_CHECKS
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O* obj = *reinterpret_cast<O**>(this);
+ // Fast path: If the object is a plain JSObject, which is the common case, we
+ // know where to find the internal fields and can return the value directly.
+ if (V8_LIKELY(I::GetInstanceType(obj) == I::kJSObjectType)) {
+ int offset = I::kJSObjectHeaderSize + (internal::kApiPointerSize * index);
+ return I::ReadField<void*>(obj, offset);
+ }
+#endif
+ return SlowGetAlignedPointerFromInternalField(index);
+}
+
+
+String* String::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<String*>(value);
+}
+
+
+Local<String> String::Empty(Isolate* isolate) {
+ typedef internal::Object* S;
+ typedef internal::Internals I;
+ I::CheckInitialized(isolate);
+ S* slot = I::GetRoot(isolate, I::kEmptyStringRootIndex);
+ return Local<String>(reinterpret_cast<String*>(slot));
+}
+
+
+Local<String> String::New(const char* data, int length) {
+ return NewFromUtf8(Isolate::GetCurrent(), data, kNormalString, length);
+}
+
+
+Local<String> String::New(const uint16_t* data, int length) {
+ return NewFromTwoByte(Isolate::GetCurrent(), data, kNormalString, length);
+}
+
+
+Local<String> String::NewSymbol(const char* data, int length) {
+ return NewFromUtf8(Isolate::GetCurrent(), data, kInternalizedString, length);
+}
+
+
+Local<String> String::NewUndetectable(const char* data, int length) {
+ return NewFromUtf8(Isolate::GetCurrent(), data, kUndetectableString, length);
+}
+
+
+Local<String> String::NewUndetectable(const uint16_t* data, int length) {
+ return NewFromTwoByte(
+ Isolate::GetCurrent(), data, kUndetectableString, length);
+}
+
+
+String::ExternalStringResource* String::GetExternalStringResource() const {
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O* obj = *reinterpret_cast<O**>(const_cast<String*>(this));
+ String::ExternalStringResource* result;
+ if (I::IsExternalTwoByteString(I::GetInstanceType(obj))) {
+ void* value = I::ReadField<void*>(obj, I::kStringResourceOffset);
+ result = reinterpret_cast<String::ExternalStringResource*>(value);
+ } else {
+ result = NULL;
+ }
+#ifdef V8_ENABLE_CHECKS
+ VerifyExternalStringResource(result);
+#endif
+ return result;
+}
+
+
+String::ExternalStringResourceBase* String::GetExternalStringResourceBase(
+ String::Encoding* encoding_out) const {
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O* obj = *reinterpret_cast<O**>(const_cast<String*>(this));
+ int type = I::GetInstanceType(obj) & I::kFullStringRepresentationMask;
+ *encoding_out = static_cast<Encoding>(type & I::kStringEncodingMask);
+ ExternalStringResourceBase* resource = NULL;
+ if (type == I::kExternalAsciiRepresentationTag ||
+ type == I::kExternalTwoByteRepresentationTag) {
+ void* value = I::ReadField<void*>(obj, I::kStringResourceOffset);
+ resource = static_cast<ExternalStringResourceBase*>(value);
+ }
+#ifdef V8_ENABLE_CHECKS
+ VerifyExternalStringResourceBase(resource, *encoding_out);
+#endif
+ return resource;
+}
+
+
+bool Value::IsUndefined() const {
+#ifdef V8_ENABLE_CHECKS
+ return FullIsUndefined();
+#else
+ return QuickIsUndefined();
+#endif
+}
+
+bool Value::QuickIsUndefined() const {
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O* obj = *reinterpret_cast<O**>(const_cast<Value*>(this));
+ if (!I::HasHeapObjectTag(obj)) return false;
+ if (I::GetInstanceType(obj) != I::kOddballType) return false;
+ return (I::GetOddballKind(obj) == I::kUndefinedOddballKind);
+}
+
+
+bool Value::IsNull() const {
+#ifdef V8_ENABLE_CHECKS
+ return FullIsNull();
+#else
+ return QuickIsNull();
+#endif
+}
+
+bool Value::QuickIsNull() const {
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O* obj = *reinterpret_cast<O**>(const_cast<Value*>(this));
+ if (!I::HasHeapObjectTag(obj)) return false;
+ if (I::GetInstanceType(obj) != I::kOddballType) return false;
+ return (I::GetOddballKind(obj) == I::kNullOddballKind);
+}
+
+
+bool Value::IsString() const {
+#ifdef V8_ENABLE_CHECKS
+ return FullIsString();
+#else
+ return QuickIsString();
+#endif
+}
+
+bool Value::QuickIsString() const {
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O* obj = *reinterpret_cast<O**>(const_cast<Value*>(this));
+ if (!I::HasHeapObjectTag(obj)) return false;
+ return (I::GetInstanceType(obj) < I::kFirstNonstringType);
+}
+
+
+template <class T> Value* Value::Cast(T* value) {
+ return static_cast<Value*>(value);
+}
+
+
+Symbol* Symbol::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Symbol*>(value);
+}
+
+
+Number* Number::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Number*>(value);
+}
+
+
+Integer* Integer::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Integer*>(value);
+}
+
+
+Date* Date::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Date*>(value);
+}
+
+
+StringObject* StringObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<StringObject*>(value);
+}
+
+
+SymbolObject* SymbolObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<SymbolObject*>(value);
+}
+
+
+NumberObject* NumberObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<NumberObject*>(value);
+}
+
+
+BooleanObject* BooleanObject::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<BooleanObject*>(value);
+}
+
+
+RegExp* RegExp::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<RegExp*>(value);
+}
+
+
+Object* Object::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Object*>(value);
+}
+
+
+Array* Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Array*>(value);
+}
+
+
+ArrayBuffer* ArrayBuffer::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<ArrayBuffer*>(value);
+}
+
+
+ArrayBufferView* ArrayBufferView::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<ArrayBufferView*>(value);
+}
+
+
+TypedArray* TypedArray::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<TypedArray*>(value);
+}
+
+
+Uint8Array* Uint8Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Uint8Array*>(value);
+}
+
+
+Int8Array* Int8Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Int8Array*>(value);
+}
+
+
+Uint16Array* Uint16Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Uint16Array*>(value);
+}
+
+
+Int16Array* Int16Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Int16Array*>(value);
+}
+
+
+Uint32Array* Uint32Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Uint32Array*>(value);
+}
+
+
+Int32Array* Int32Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Int32Array*>(value);
+}
+
+
+Float32Array* Float32Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Float32Array*>(value);
+}
+
+
+Float64Array* Float64Array::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Float64Array*>(value);
+}
+
+
+Uint8ClampedArray* Uint8ClampedArray::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Uint8ClampedArray*>(value);
+}
+
+
+DataView* DataView::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<DataView*>(value);
+}
+
+
+Function* Function::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<Function*>(value);
+}
+
+
+External* External::Cast(v8::Value* value) {
+#ifdef V8_ENABLE_CHECKS
+ CheckCast(value);
+#endif
+ return static_cast<External*>(value);
+}
+
+
+template<typename T>
+Isolate* PropertyCallbackInfo<T>::GetIsolate() const {
+ return *reinterpret_cast<Isolate**>(&args_[kIsolateIndex]);
+}
+
+
+template<typename T>
+Local<Value> PropertyCallbackInfo<T>::Data() const {
+ return Local<Value>(reinterpret_cast<Value*>(&args_[kDataIndex]));
+}
+
+
+template<typename T>
+Local<Object> PropertyCallbackInfo<T>::This() const {
+ return Local<Object>(reinterpret_cast<Object*>(&args_[kThisIndex]));
+}
+
+
+template<typename T>
+Local<Object> PropertyCallbackInfo<T>::Holder() const {
+ return Local<Object>(reinterpret_cast<Object*>(&args_[kHolderIndex]));
+}
+
+
+template<typename T>
+ReturnValue<T> PropertyCallbackInfo<T>::GetReturnValue() const {
+ return ReturnValue<T>(&args_[kReturnValueIndex]);
+}
+
+
+Handle<Primitive> Undefined(Isolate* isolate) {
+ typedef internal::Object* S;
+ typedef internal::Internals I;
+ I::CheckInitialized(isolate);
+ S* slot = I::GetRoot(isolate, I::kUndefinedValueRootIndex);
+ return Handle<Primitive>(reinterpret_cast<Primitive*>(slot));
+}
+
+
+Handle<Primitive> Null(Isolate* isolate) {
+ typedef internal::Object* S;
+ typedef internal::Internals I;
+ I::CheckInitialized(isolate);
+ S* slot = I::GetRoot(isolate, I::kNullValueRootIndex);
+ return Handle<Primitive>(reinterpret_cast<Primitive*>(slot));
+}
+
+
+Handle<Boolean> True(Isolate* isolate) {
+ typedef internal::Object* S;
+ typedef internal::Internals I;
+ I::CheckInitialized(isolate);
+ S* slot = I::GetRoot(isolate, I::kTrueValueRootIndex);
+ return Handle<Boolean>(reinterpret_cast<Boolean*>(slot));
+}
+
+
+Handle<Boolean> False(Isolate* isolate) {
+ typedef internal::Object* S;
+ typedef internal::Internals I;
+ I::CheckInitialized(isolate);
+ S* slot = I::GetRoot(isolate, I::kFalseValueRootIndex);
+ return Handle<Boolean>(reinterpret_cast<Boolean*>(slot));
+}
+
+
+void Isolate::SetData(void* data) {
+ typedef internal::Internals I;
+ I::SetEmbedderData(this, data);
+}
+
+
+void* Isolate::GetData() {
+ typedef internal::Internals I;
+ return I::GetEmbedderData(this);
+}
+
+
+Local<Value> Context::GetEmbedderData(int index) {
+#ifndef V8_ENABLE_CHECKS
+ typedef internal::Object O;
+ typedef internal::Internals I;
+ O** result = HandleScope::CreateHandle(I::ReadEmbedderData<O*>(this, index));
+ return Local<Value>(reinterpret_cast<Value*>(result));
+#else
+ return SlowGetEmbedderData(index);
+#endif
+}
+
+
+void* Context::GetAlignedPointerFromEmbedderData(int index) {
+#ifndef V8_ENABLE_CHECKS
+ typedef internal::Internals I;
+ return I::ReadEmbedderData<void*>(this, index);
+#else
+ return SlowGetAlignedPointerFromEmbedderData(index);
+#endif
+}
+
+
+/**
+ * \example shell.cc
+ * A simple shell that takes a list of expressions on the
+ * command-line and executes them.
+ */
+
+
+/**
+ * \example process.cc
+ */
+
+
+} // namespace v8
+
+
+#undef TYPE_CHECK
+
+
+#endif // V8_H_
diff --git a/chromium/v8/include/v8stdint.h b/chromium/v8/include/v8stdint.h
new file mode 100644
index 00000000000..7c12e1f4907
--- /dev/null
+++ b/chromium/v8/include/v8stdint.h
@@ -0,0 +1,54 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Load definitions of standard types.
+
+#ifndef V8STDINT_H_
+#define V8STDINT_H_
+
+#include <stddef.h>
+#include <stdio.h>
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t; // NOLINT
+typedef unsigned short uint16_t; // NOLINT
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+// intptr_t and friends are defined in crtdefs.h through stdio.h.
+
+#else
+
+#include <stdint.h>
+
+#endif
+
+#endif // V8STDINT_H_
diff --git a/chromium/v8/preparser/preparser-process.cc b/chromium/v8/preparser/preparser-process.cc
new file mode 100644
index 00000000000..b8167443039
--- /dev/null
+++ b/chromium/v8/preparser/preparser-process.cc
@@ -0,0 +1,372 @@
+// Copyright 2012 the V8 project authors. 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 <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../include/v8.h"
+#include "../include/v8stdint.h"
+#include "../include/v8-preparser.h"
+
+#include "../src/preparse-data-format.h"
+
+namespace i = v8::internal;
+
+// This file is only used for testing the preparser.
+// The first argument must be the path of a JavaScript source file, or
+// the flags "-e" and the next argument is then the source of a JavaScript
+// program.
+// Optionally this can be followed by the word "throws" (case sensitive),
+// which signals that the parsing is expected to throw - the default is
+// to expect the parsing to not throw.
+// The command line can further be followed by a message text (the
+// *type* of the exception to throw), and even more optionally, the
+// start and end position reported with the exception.
+//
+// This source file is preparsed and tested against the expectations, and if
+// successful, the resulting preparser data is written to stdout.
+// Diagnostic output is output on stderr.
+// The source file must contain only ASCII characters (UTF-8 isn't supported).
+// The file is read into memory, so it should have a reasonable size.
+
+
+// Adapts an ASCII string to the UnicodeInputStream interface.
+class AsciiInputStream : public v8::UnicodeInputStream {
+ public:
+ AsciiInputStream(const uint8_t* buffer, size_t length)
+ : buffer_(buffer),
+ end_offset_(static_cast<int>(length)),
+ offset_(0) { }
+
+ virtual ~AsciiInputStream() { }
+
+ virtual void PushBack(int32_t ch) {
+ offset_--;
+#ifdef DEBUG
+ if (offset_ < 0 ||
+ (ch != ((offset_ >= end_offset_) ? -1 : buffer_[offset_]))) {
+ fprintf(stderr, "Invalid pushback: '%c' at offset %d.", ch, offset_);
+ exit(1);
+ }
+#endif
+ }
+
+ virtual int32_t Next() {
+ if (offset_ >= end_offset_) {
+ offset_++; // Increment anyway to allow symmetric pushbacks.
+ return -1;
+ }
+ uint8_t next_char = buffer_[offset_];
+#ifdef DEBUG
+ if (next_char > 0x7fu) {
+ fprintf(stderr, "Non-ASCII character in input: '%c'.", next_char);
+ exit(1);
+ }
+#endif
+ offset_++;
+ return static_cast<int32_t>(next_char);
+ }
+
+ private:
+ const uint8_t* buffer_;
+ const int end_offset_;
+ int offset_;
+};
+
+
+bool ReadBuffer(FILE* source, void* buffer, size_t length) {
+ size_t actually_read = fread(buffer, 1, length, source);
+ return (actually_read == length);
+}
+
+
+bool WriteBuffer(FILE* dest, const void* buffer, size_t length) {
+ size_t actually_written = fwrite(buffer, 1, length, dest);
+ return (actually_written == length);
+}
+
+
+class PreparseDataInterpreter {
+ public:
+ PreparseDataInterpreter(const uint8_t* data, int length)
+ : data_(data), length_(length), message_(NULL) { }
+
+ ~PreparseDataInterpreter() {
+ if (message_ != NULL) delete[] message_;
+ }
+
+ bool valid() {
+ int header_length =
+ i::PreparseDataConstants::kHeaderSize * sizeof(int); // NOLINT
+ return length_ >= header_length;
+ }
+
+ bool throws() {
+ return valid() &&
+ word(i::PreparseDataConstants::kHasErrorOffset) != 0;
+ }
+
+ const char* message() {
+ if (message_ != NULL) return message_;
+ if (!throws()) return NULL;
+ int text_pos = i::PreparseDataConstants::kHeaderSize +
+ i::PreparseDataConstants::kMessageTextPos;
+ int length = word(text_pos);
+ char* buffer = new char[length + 1];
+ for (int i = 1; i <= length; i++) {
+ int character = word(text_pos + i);
+ buffer[i - 1] = character;
+ }
+ buffer[length] = '\0';
+ message_ = buffer;
+ return buffer;
+ }
+
+ int beg_pos() {
+ if (!throws()) return -1;
+ return word(i::PreparseDataConstants::kHeaderSize +
+ i::PreparseDataConstants::kMessageStartPos);
+ }
+
+ int end_pos() {
+ if (!throws()) return -1;
+ return word(i::PreparseDataConstants::kHeaderSize +
+ i::PreparseDataConstants::kMessageEndPos);
+ }
+
+ private:
+ int word(int offset) {
+ const int* word_data = reinterpret_cast<const int*>(data_);
+ if (word_data + offset < reinterpret_cast<const int*>(data_ + length_)) {
+ return word_data[offset];
+ }
+ return -1;
+ }
+
+ const uint8_t* const data_;
+ const int length_;
+ const char* message_;
+};
+
+
+template <typename T>
+class ScopedPointer {
+ public:
+ explicit ScopedPointer() : pointer_(NULL) {}
+ explicit ScopedPointer(T* pointer) : pointer_(pointer) {}
+ ~ScopedPointer() { if (pointer_ != NULL) delete[] pointer_; }
+ T& operator[](int index) { return pointer_[index]; }
+ T* operator*() { return pointer_ ;}
+ T* operator=(T* new_value) {
+ if (pointer_ != NULL) delete[] pointer_;
+ pointer_ = new_value;
+ return new_value;
+ }
+ private:
+ T* pointer_;
+};
+
+
+
+void fail(v8::PreParserData* data, const char* message, ...) {
+ va_list args;
+ va_start(args, message);
+ vfprintf(stderr, message, args);
+ va_end(args);
+ fflush(stderr);
+ if (data != NULL) {
+ // Print preparser data to stdout.
+ uint32_t size = static_cast<uint32_t>(data->size());
+ fprintf(stderr, "LOG: data size: %u\n", size);
+ if (!WriteBuffer(stdout, data->data(), size)) {
+ perror("ERROR: Writing data");
+ fflush(stderr);
+ }
+ }
+ exit(EXIT_FAILURE);
+}
+
+
+bool IsFlag(const char* arg) {
+ // Anything starting with '-' is considered a flag.
+ // It's summarily ignored for now.
+ return arg[0] == '-';
+}
+
+
+struct ExceptionExpectation {
+ ExceptionExpectation()
+ : throws(false), type(NULL), beg_pos(-1), end_pos(-1) { }
+ bool throws;
+ const char* type;
+ int beg_pos;
+ int end_pos;
+};
+
+
+void CheckException(v8::PreParserData* data,
+ ExceptionExpectation* expects) {
+ PreparseDataInterpreter reader(data->data(), static_cast<int>(data->size()));
+ if (expects->throws) {
+ if (!reader.throws()) {
+ if (expects->type == NULL) {
+ fail(data, "Didn't throw as expected\n");
+ } else {
+ fail(data, "Didn't throw \"%s\" as expected\n", expects->type);
+ }
+ }
+ if (expects->type != NULL) {
+ const char* actual_message = reader.message();
+ if (strcmp(expects->type, actual_message)) {
+ fail(data, "Wrong error message. Expected <%s>, found <%s> at %d..%d\n",
+ expects->type, actual_message, reader.beg_pos(), reader.end_pos());
+ }
+ }
+ if (expects->beg_pos >= 0) {
+ if (expects->beg_pos != reader.beg_pos()) {
+ fail(data, "Wrong error start position: Expected %i, found %i\n",
+ expects->beg_pos, reader.beg_pos());
+ }
+ }
+ if (expects->end_pos >= 0) {
+ if (expects->end_pos != reader.end_pos()) {
+ fail(data, "Wrong error end position: Expected %i, found %i\n",
+ expects->end_pos, reader.end_pos());
+ }
+ }
+ } else if (reader.throws()) {
+ const char* message = reader.message();
+ fail(data, "Throws unexpectedly with message: %s at location %d-%d\n",
+ message, reader.beg_pos(), reader.end_pos());
+ }
+}
+
+
+ExceptionExpectation ParseExpectation(int argc, const char* argv[]) {
+ // Parse ["throws" [<exn-type> [<start> [<end>]]]].
+ ExceptionExpectation expects;
+ int arg_index = 0;
+ while (argc > arg_index && strncmp("throws", argv[arg_index], 7)) {
+ arg_index++;
+ }
+ if (argc > arg_index) {
+ expects.throws = true;
+ arg_index++;
+ if (argc > arg_index && !IsFlag(argv[arg_index])) {
+ expects.type = argv[arg_index];
+ arg_index++;
+ if (argc > arg_index && !IsFlag(argv[arg_index])) {
+ expects.beg_pos = atoi(argv[arg_index]); // NOLINT
+ arg_index++;
+ if (argc > arg_index && !IsFlag(argv[arg_index])) {
+ expects.end_pos = atoi(argv[arg_index]); // NOLINT
+ }
+ }
+ }
+ }
+ return expects;
+}
+
+
+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 -e) are ignored.
+ // Flags must not separate "throws" and its arguments.
+
+ // Check for mandatory filename argument.
+ int arg_index = 1;
+ if (argc <= arg_index) {
+ fail(NULL, "ERROR: No filename on command line.\n");
+ }
+ const uint8_t* source = NULL;
+ const char* filename = argv[arg_index];
+ if (!strcmp(filename, "-e")) {
+ arg_index++;
+ if (argc <= arg_index) {
+ fail(NULL, "ERROR: No source after -e on command line.\n");
+ }
+ source = reinterpret_cast<const uint8_t*>(argv[arg_index]);
+ }
+ // Check remainder of command line for exception expectations.
+ arg_index++;
+ ExceptionExpectation expects =
+ ParseExpectation(argc - arg_index, argv + arg_index);
+
+ v8::V8::Initialize();
+
+ ScopedPointer<uint8_t> buffer;
+ size_t length;
+
+ if (source == NULL) {
+ // Open JS file.
+ FILE* input = fopen(filename, "rb");
+ if (input == NULL) {
+ perror("ERROR: Error opening file");
+ fflush(stderr);
+ return EXIT_FAILURE;
+ }
+ // Find length of JS file.
+ if (fseek(input, 0, SEEK_END) != 0) {
+ perror("ERROR: Error during seek");
+ fflush(stderr);
+ return EXIT_FAILURE;
+ }
+ length = static_cast<size_t>(ftell(input));
+ rewind(input);
+ // Read JS file into memory buffer.
+ buffer = new uint8_t[length];
+ if (!ReadBuffer(input, *buffer, length)) {
+ perror("ERROR: Reading file");
+ fflush(stderr);
+ return EXIT_FAILURE;
+ }
+ fclose(input);
+ source = *buffer;
+ } else {
+ length = strlen(reinterpret_cast<const char*>(source));
+ }
+
+ // Preparse input file.
+ AsciiInputStream input_buffer(source, length);
+ size_t kMaxStackSize = 64 * 1024 * sizeof(void*); // NOLINT
+ v8::PreParserData data = v8::Preparse(&input_buffer, kMaxStackSize);
+
+ // Fail if stack overflow.
+ if (data.stack_overflow()) {
+ fail(&data, "ERROR: Stack overflow\n");
+ }
+
+ // Check that the expected exception is thrown, if an exception is
+ // expected.
+ CheckException(&data, &expects);
+
+ return EXIT_SUCCESS;
+}
diff --git a/chromium/v8/preparser/preparser.gyp b/chromium/v8/preparser/preparser.gyp
new file mode 100644
index 00000000000..23cbfff6446
--- /dev/null
+++ b/chromium/v8/preparser/preparser.gyp
@@ -0,0 +1,58 @@
+# 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': {
+ 'v8_code': 1,
+ },
+ 'includes': ['../build/toolchain.gypi', '../build/features.gypi'],
+ 'targets': [
+ {
+ 'target_name': 'preparser',
+ 'type': 'executable',
+ 'conditions': [
+ # preparser can't link against a shared library, so link against
+ # the underlying static targets.
+ ['v8_use_snapshot=="true"', {
+ 'dependencies': ['../tools/gyp/v8.gyp:v8_snapshot'],
+ }, {
+ 'dependencies': [
+ '../tools/gyp/v8.gyp:v8_nosnapshot.<(v8_target_arch)',
+ ],
+ }],
+ ],
+ 'include_dirs+': [
+ '../src',
+ ],
+ 'sources': [
+ 'preparser-process.cc',
+ '../include/v8-preparser.h',
+ '../src/preparser-api.cc',
+ ],
+ },
+ ],
+}
diff --git a/chromium/v8/samples/count-hosts.js b/chromium/v8/samples/count-hosts.js
new file mode 100644
index 00000000000..bea6553d27b
--- /dev/null
+++ b/chromium/v8/samples/count-hosts.js
@@ -0,0 +1,42 @@
+// Copyright 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.
+
+function Initialize() { }
+
+function Process(request) {
+ if (options.verbose) {
+ log("Processing " + request.host + request.path +
+ " from " + request.referrer + "@" + request.userAgent);
+ }
+ if (!output[request.host]) {
+ output[request.host] = 1;
+ } else {
+ output[request.host]++
+ }
+}
+
+Initialize();
diff --git a/chromium/v8/samples/lineprocessor.cc b/chromium/v8/samples/lineprocessor.cc
new file mode 100644
index 00000000000..42048202fdd
--- /dev/null
+++ b/chromium/v8/samples/lineprocessor.cc
@@ -0,0 +1,450 @@
+// Copyright 2012 the V8 project authors. 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>
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#include <v8-debug.h>
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * This sample program should demonstrate certain aspects of debugging
+ * standalone V8-based application.
+ *
+ * The program reads input stream, processes it line by line and print
+ * the result to output. The actual processing is done by custom JavaScript
+ * script. The script is specified with command line parameters.
+ *
+ * The main cycle of the program will sequentially read lines from standard
+ * input, process them and print to standard output until input closes.
+ * There are 2 possible configuration in regard to main cycle.
+ *
+ * 1. The main cycle is on C++ side. Program should be run with
+ * --main-cycle-in-cpp option. Script must declare a function named
+ * "ProcessLine". The main cycle in C++ reads lines and calls this function
+ * for processing every time. This is a sample script:
+
+function ProcessLine(input_line) {
+ return ">>>" + input_line + "<<<";
+}
+
+ *
+ * 2. The main cycle is in JavaScript. Program should be run with
+ * --main-cycle-in-js option. Script gets run one time at all and gets
+ * API of 2 global functions: "read_line" and "print". It should read input
+ * and print converted lines to output itself. This a sample script:
+
+while (true) {
+ var line = read_line();
+ if (!line) {
+ break;
+ }
+ var res = line + " | " + line;
+ print(res);
+}
+
+ *
+ * When run with "-p" argument, the program starts V8 Debugger Agent and
+ * allows remote debugger to attach and debug JavaScript code.
+ *
+ * Interesting aspects:
+ * 1. Wait for remote debugger to attach
+ * Normally the program compiles custom script and immediately runs it.
+ * If programmer needs to debug script from the very beginning, he should
+ * run this sample program with "--wait-for-connection" command line parameter.
+ * This way V8 will suspend on the first statement and wait for
+ * debugger to attach.
+ *
+ * 2. Unresponsive V8
+ * V8 Debugger Agent holds a connection with remote debugger, but it does
+ * respond only when V8 is running some script. In particular, when this program
+ * is waiting for input, all requests from debugger get deferred until V8
+ * is called again. See how "--callback" command-line parameter in this sample
+ * fixes this issue.
+ */
+
+enum MainCycleType {
+ CycleInCpp,
+ CycleInJs
+};
+
+const char* ToCString(const v8::String::Utf8Value& value);
+void ReportException(v8::Isolate* isolate, v8::TryCatch* handler);
+v8::Handle<v8::String> ReadFile(const char* name);
+v8::Handle<v8::String> ReadLine();
+
+void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
+void ReadLine(const v8::FunctionCallbackInfo<v8::Value>& args);
+bool RunCppCycle(v8::Handle<v8::Script> script,
+ v8::Local<v8::Context> context,
+ bool report_exceptions);
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+v8::Persistent<v8::Context> debug_message_context;
+
+void DispatchDebugMessages() {
+ // We are in some random thread. We should already have v8::Locker acquired
+ // (we requested this when registered this callback). We was called
+ // because new debug messages arrived; they may have already been processed,
+ // but we shouldn't worry about this.
+ //
+ // All we have to do is to set context and call ProcessDebugMessages.
+ //
+ // We should decide which V8 context to use here. This is important for
+ // "evaluate" command, because it must be executed some context.
+ // In our sample we have only one context, so there is nothing really to
+ // think about.
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate, debug_message_context);
+ v8::Context::Scope scope(context);
+
+ v8::Debug::ProcessDebugMessages();
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+int RunMain(int argc, char* argv[]) {
+ v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ v8::HandleScope handle_scope(isolate);
+
+ v8::Handle<v8::String> script_source;
+ v8::Handle<v8::Value> script_name;
+ int script_param_counter = 0;
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ int port_number = -1;
+ bool wait_for_connection = false;
+ bool support_callback = false;
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+ MainCycleType cycle_type = CycleInCpp;
+
+ for (int i = 1; i < argc; i++) {
+ const char* str = argv[i];
+ if (strcmp(str, "-f") == 0) {
+ // Ignore any -f flags for compatibility with the other stand-
+ // alone JavaScript engines.
+ continue;
+ } else if (strcmp(str, "--main-cycle-in-cpp") == 0) {
+ cycle_type = CycleInCpp;
+ } else if (strcmp(str, "--main-cycle-in-js") == 0) {
+ cycle_type = CycleInJs;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ } else if (strcmp(str, "--callback") == 0) {
+ support_callback = true;
+ } else if (strcmp(str, "--wait-for-connection") == 0) {
+ wait_for_connection = true;
+ } else if (strcmp(str, "-p") == 0 && i + 1 < argc) {
+ port_number = atoi(argv[i + 1]); // NOLINT
+ i++;
+#endif // ENABLE_DEBUGGER_SUPPORT
+ } else if (strncmp(str, "--", 2) == 0) {
+ printf("Warning: unknown flag %s.\nTry --help for options\n", str);
+ } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
+ script_source = v8::String::New(argv[i + 1]);
+ script_name = v8::String::New("unnamed");
+ i++;
+ script_param_counter++;
+ } else {
+ // Use argument as a name of file to load.
+ script_source = ReadFile(str);
+ script_name = v8::String::New(str);
+ if (script_source.IsEmpty()) {
+ printf("Error reading '%s'\n", str);
+ return 1;
+ }
+ script_param_counter++;
+ }
+ }
+
+ if (script_param_counter == 0) {
+ printf("Script is not specified\n");
+ return 1;
+ }
+ if (script_param_counter != 1) {
+ printf("Only one script may be specified\n");
+ return 1;
+ }
+
+ // Create a template for the global object.
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
+
+ // Bind the global 'print' function to the C++ Print callback.
+ global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
+
+ if (cycle_type == CycleInJs) {
+ // Bind the global 'read_line' function to the C++ Print callback.
+ global->Set(v8::String::New("read_line"),
+ v8::FunctionTemplate::New(ReadLine));
+ }
+
+ // Create a new execution environment containing the built-in
+ // functions
+ v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL, global);
+ // Enter the newly created execution environment.
+ v8::Context::Scope context_scope(context);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ debug_message_context.Reset(isolate, context);
+
+ v8::Locker locker(isolate);
+
+ if (support_callback) {
+ v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true);
+ }
+
+ if (port_number != -1) {
+ v8::Debug::EnableAgent("lineprocessor", port_number, wait_for_connection);
+ }
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+ bool report_exceptions = true;
+
+ v8::Handle<v8::Script> script;
+ {
+ // Compile script in try/catch context.
+ v8::TryCatch try_catch;
+ script = v8::Script::Compile(script_source, script_name);
+ if (script.IsEmpty()) {
+ // Print errors that happened during compilation.
+ if (report_exceptions)
+ ReportException(isolate, &try_catch);
+ return 1;
+ }
+ }
+
+ {
+ v8::TryCatch try_catch;
+
+ script->Run();
+ if (try_catch.HasCaught()) {
+ if (report_exceptions)
+ ReportException(isolate, &try_catch);
+ return 1;
+ }
+ }
+
+ if (cycle_type == CycleInCpp) {
+ bool res = RunCppCycle(script,
+ v8::Context::GetCurrent(),
+ report_exceptions);
+ return !res;
+ } else {
+ // All is already done.
+ }
+ return 0;
+}
+
+
+bool RunCppCycle(v8::Handle<v8::Script> script,
+ v8::Local<v8::Context> context,
+ bool report_exceptions) {
+ v8::Isolate* isolate = context->GetIsolate();
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ v8::Locker lock(isolate);
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+ v8::Handle<v8::String> fun_name = v8::String::New("ProcessLine");
+ v8::Handle<v8::Value> process_val = context->Global()->Get(fun_name);
+
+ // If there is no Process function, or if it is not a function,
+ // bail out
+ if (!process_val->IsFunction()) {
+ printf("Error: Script does not declare 'ProcessLine' global function.\n");
+ return 1;
+ }
+
+ // It is a function; cast it to a Function
+ v8::Handle<v8::Function> process_fun =
+ v8::Handle<v8::Function>::Cast(process_val);
+
+
+ while (!feof(stdin)) {
+ v8::HandleScope handle_scope(isolate);
+
+ v8::Handle<v8::String> input_line = ReadLine();
+ if (input_line == v8::Undefined()) {
+ continue;
+ }
+
+ const int argc = 1;
+ v8::Handle<v8::Value> argv[argc] = { input_line };
+
+ v8::Handle<v8::Value> result;
+ {
+ v8::TryCatch try_catch;
+ result = process_fun->Call(v8::Context::GetCurrent()->Global(),
+ argc, argv);
+ if (try_catch.HasCaught()) {
+ if (report_exceptions)
+ ReportException(isolate, &try_catch);
+ return false;
+ }
+ }
+ v8::String::Utf8Value str(result);
+ const char* cstr = ToCString(str);
+ printf("%s\n", cstr);
+ }
+
+ return true;
+}
+
+
+int main(int argc, char* argv[]) {
+ v8::V8::InitializeICU();
+ int result = RunMain(argc, argv);
+ v8::V8::Dispose();
+ return result;
+}
+
+
+// Extracts a C string from a V8 Utf8Value.
+const char* ToCString(const v8::String::Utf8Value& value) {
+ return *value ? *value : "<string conversion failed>";
+}
+
+
+// Reads a file into a v8 string.
+v8::Handle<v8::String> ReadFile(const char* name) {
+ FILE* file = fopen(name, "rb");
+ if (file == NULL) return v8::Handle<v8::String>();
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+ rewind(file);
+
+ char* chars = new char[size + 1];
+ chars[size] = '\0';
+ for (int i = 0; i < size;) {
+ int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
+ i += read;
+ }
+ fclose(file);
+ v8::Handle<v8::String> result = v8::String::New(chars, size);
+ delete[] chars;
+ return result;
+}
+
+
+void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
+ v8::HandleScope handle_scope(isolate);
+ v8::String::Utf8Value exception(try_catch->Exception());
+ const char* exception_string = ToCString(exception);
+ v8::Handle<v8::Message> message = try_catch->Message();
+ if (message.IsEmpty()) {
+ // V8 didn't provide any extra information about this error; just
+ // print the exception.
+ printf("%s\n", exception_string);
+ } else {
+ // Print (filename):(line number): (message).
+ v8::String::Utf8Value filename(message->GetScriptResourceName());
+ const char* filename_string = ToCString(filename);
+ int linenum = message->GetLineNumber();
+ printf("%s:%i: %s\n", filename_string, linenum, exception_string);
+ // Print line of source code.
+ v8::String::Utf8Value sourceline(message->GetSourceLine());
+ const char* sourceline_string = ToCString(sourceline);
+ printf("%s\n", sourceline_string);
+ // Print wavy underline (GetUnderline is deprecated).
+ int start = message->GetStartColumn();
+ for (int i = 0; i < start; i++) {
+ printf(" ");
+ }
+ int end = message->GetEndColumn();
+ for (int i = start; i < end; i++) {
+ printf("^");
+ }
+ printf("\n");
+ }
+}
+
+
+// The callback that is invoked by v8 whenever the JavaScript 'print'
+// function is called. Prints its arguments on stdout separated by
+// spaces and ending with a newline.
+void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ bool first = true;
+ for (int i = 0; i < args.Length(); i++) {
+ v8::HandleScope handle_scope(args.GetIsolate());
+ if (first) {
+ first = false;
+ } else {
+ printf(" ");
+ }
+ v8::String::Utf8Value str(args[i]);
+ const char* cstr = ToCString(str);
+ printf("%s", cstr);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+
+// The callback that is invoked by v8 whenever the JavaScript 'read_line'
+// function is called. Reads a string from standard input and returns.
+void ReadLine(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() > 0) {
+ v8::ThrowException(v8::String::New("Unexpected arguments"));
+ return;
+ }
+ args.GetReturnValue().Set(ReadLine());
+}
+
+
+v8::Handle<v8::String> ReadLine() {
+ const int kBufferSize = 1024 + 1;
+ char buffer[kBufferSize];
+
+ char* res;
+ {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ v8::Unlocker unlocker(v8::Isolate::GetCurrent());
+#endif // ENABLE_DEBUGGER_SUPPORT
+ res = fgets(buffer, kBufferSize, stdin);
+ }
+ if (res == NULL) {
+ v8::Handle<v8::Primitive> t = v8::Undefined();
+ return v8::Handle<v8::String>::Cast(t);
+ }
+ // Remove newline char
+ for (char* pos = buffer; *pos != '\0'; pos++) {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ }
+ return v8::String::New(buffer);
+}
diff --git a/chromium/v8/samples/process.cc b/chromium/v8/samples/process.cc
new file mode 100644
index 00000000000..844aee3d45f
--- /dev/null
+++ b/chromium/v8/samples/process.cc
@@ -0,0 +1,654 @@
+// Copyright 2012 the V8 project authors. 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 <string>
+#include <map>
+
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+#error Using compressed startup data is not supported for this sample
+#endif
+
+using namespace std;
+using namespace v8;
+
+// These interfaces represent an existing request processing interface.
+// The idea is to imagine a real application that uses these interfaces
+// and then add scripting capabilities that allow you to interact with
+// the objects through JavaScript.
+
+/**
+ * A simplified http request.
+ */
+class HttpRequest {
+ public:
+ virtual ~HttpRequest() { }
+ virtual const string& Path() = 0;
+ virtual const string& Referrer() = 0;
+ virtual const string& Host() = 0;
+ virtual const string& UserAgent() = 0;
+};
+
+
+/**
+ * The abstract superclass of http request processors.
+ */
+class HttpRequestProcessor {
+ public:
+ virtual ~HttpRequestProcessor() { }
+
+ // Initialize this processor. The map contains options that control
+ // how requests should be processed.
+ virtual bool Initialize(map<string, string>* options,
+ map<string, string>* output) = 0;
+
+ // Process a single request.
+ virtual bool Process(HttpRequest* req) = 0;
+
+ static void Log(const char* event);
+};
+
+
+/**
+ * An http request processor that is scriptable using JavaScript.
+ */
+class JsHttpRequestProcessor : public HttpRequestProcessor {
+ public:
+ // Creates a new processor that processes requests by invoking the
+ // Process function of the JavaScript script given as an argument.
+ JsHttpRequestProcessor(Isolate* isolate, Handle<String> script)
+ : isolate_(isolate), script_(script) { }
+ virtual ~JsHttpRequestProcessor();
+
+ virtual bool Initialize(map<string, string>* opts,
+ map<string, string>* output);
+ virtual bool Process(HttpRequest* req);
+
+ private:
+ // Execute the script associated with this processor and extract the
+ // Process function. Returns true if this succeeded, otherwise false.
+ bool ExecuteScript(Handle<String> script);
+
+ // Wrap the options and output map in a JavaScript objects and
+ // install it in the global namespace as 'options' and 'output'.
+ bool InstallMaps(map<string, string>* opts, map<string, string>* output);
+
+ // Constructs the template that describes the JavaScript wrapper
+ // type for requests.
+ static Handle<ObjectTemplate> MakeRequestTemplate(Isolate* isolate);
+ static Handle<ObjectTemplate> MakeMapTemplate(Isolate* isolate);
+
+ // Callbacks that access the individual fields of request objects.
+ static void GetPath(Local<String> name,
+ const PropertyCallbackInfo<Value>& info);
+ static void GetReferrer(Local<String> name,
+ const PropertyCallbackInfo<Value>& info);
+ static void GetHost(Local<String> name,
+ const PropertyCallbackInfo<Value>& info);
+ static void GetUserAgent(Local<String> name,
+ const PropertyCallbackInfo<Value>& info);
+
+ // Callbacks that access maps
+ static void MapGet(Local<String> name,
+ const PropertyCallbackInfo<Value>& info);
+ static void MapSet(Local<String> name,
+ Local<Value> value,
+ const PropertyCallbackInfo<Value>& info);
+
+ // Utility methods for wrapping C++ objects as JavaScript objects,
+ // and going back again.
+ Handle<Object> WrapMap(map<string, string>* obj);
+ static map<string, string>* UnwrapMap(Handle<Object> obj);
+ Handle<Object> WrapRequest(HttpRequest* obj);
+ static HttpRequest* UnwrapRequest(Handle<Object> obj);
+
+ Isolate* GetIsolate() { return isolate_; }
+
+ Isolate* isolate_;
+ Handle<String> script_;
+ Persistent<Context> context_;
+ Persistent<Function> process_;
+ static Persistent<ObjectTemplate> request_template_;
+ static Persistent<ObjectTemplate> map_template_;
+};
+
+
+// -------------------------
+// --- P r o c e s s o r ---
+// -------------------------
+
+
+static void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() < 1) return;
+ HandleScope scope(args.GetIsolate());
+ Handle<Value> arg = args[0];
+ String::Utf8Value value(arg);
+ HttpRequestProcessor::Log(*value);
+}
+
+
+// Execute the script and fetch the Process method.
+bool JsHttpRequestProcessor::Initialize(map<string, string>* opts,
+ map<string, string>* output) {
+ // Create a handle scope to hold the temporary references.
+ HandleScope handle_scope(GetIsolate());
+
+ // Create a template for the global object where we set the
+ // built-in global functions.
+ Handle<ObjectTemplate> global = ObjectTemplate::New();
+ global->Set(String::New("log"), FunctionTemplate::New(LogCallback));
+
+ // Each processor gets its own context so different processors don't
+ // affect each other. Context::New returns a persistent handle which
+ // is what we need for the reference to remain after we return from
+ // this method. That persistent handle has to be disposed in the
+ // destructor.
+ v8::Handle<v8::Context> context = Context::New(GetIsolate(), NULL, global);
+ context_.Reset(GetIsolate(), context);
+
+ // Enter the new context so all the following operations take place
+ // within it.
+ Context::Scope context_scope(context);
+
+ // Make the options mapping available within the context
+ if (!InstallMaps(opts, output))
+ return false;
+
+ // Compile and run the script
+ if (!ExecuteScript(script_))
+ return false;
+
+ // The script compiled and ran correctly. Now we fetch out the
+ // Process function from the global object.
+ Handle<String> process_name = String::New("Process");
+ Handle<Value> process_val = context->Global()->Get(process_name);
+
+ // If there is no Process function, or if it is not a function,
+ // bail out
+ if (!process_val->IsFunction()) return false;
+
+ // It is a function; cast it to a Function
+ Handle<Function> process_fun = Handle<Function>::Cast(process_val);
+
+ // Store the function in a Persistent handle, since we also want
+ // that to remain after this call returns
+ process_.Reset(GetIsolate(), process_fun);
+
+ // All done; all went well
+ return true;
+}
+
+
+bool JsHttpRequestProcessor::ExecuteScript(Handle<String> script) {
+ HandleScope handle_scope(GetIsolate());
+
+ // We're just about to compile the script; set up an error handler to
+ // catch any exceptions the script might throw.
+ TryCatch try_catch;
+
+ // Compile the script and check for errors.
+ Handle<Script> compiled_script = Script::Compile(script);
+ if (compiled_script.IsEmpty()) {
+ String::Utf8Value error(try_catch.Exception());
+ Log(*error);
+ // The script failed to compile; bail out.
+ return false;
+ }
+
+ // Run the script!
+ Handle<Value> result = compiled_script->Run();
+ if (result.IsEmpty()) {
+ // The TryCatch above is still in effect and will have caught the error.
+ String::Utf8Value error(try_catch.Exception());
+ Log(*error);
+ // Running the script failed; bail out.
+ return false;
+ }
+ return true;
+}
+
+
+bool JsHttpRequestProcessor::InstallMaps(map<string, string>* opts,
+ map<string, string>* output) {
+ HandleScope handle_scope(GetIsolate());
+
+ // Wrap the map object in a JavaScript wrapper
+ Handle<Object> opts_obj = WrapMap(opts);
+
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(GetIsolate(), context_);
+
+ // Set the options object as a property on the global object.
+ context->Global()->Set(String::New("options"), opts_obj);
+
+ Handle<Object> output_obj = WrapMap(output);
+ context->Global()->Set(String::New("output"), output_obj);
+
+ return true;
+}
+
+
+bool JsHttpRequestProcessor::Process(HttpRequest* request) {
+ // Create a handle scope to keep the temporary object references.
+ HandleScope handle_scope(GetIsolate());
+
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(GetIsolate(), context_);
+
+ // Enter this processor's context so all the remaining operations
+ // take place there
+ Context::Scope context_scope(context);
+
+ // Wrap the C++ request object in a JavaScript wrapper
+ Handle<Object> request_obj = WrapRequest(request);
+
+ // Set up an exception handler before calling the Process function
+ TryCatch try_catch;
+
+ // Invoke the process function, giving the global object as 'this'
+ // and one argument, the request.
+ const int argc = 1;
+ Handle<Value> argv[argc] = { request_obj };
+ v8::Local<v8::Function> process =
+ v8::Local<v8::Function>::New(GetIsolate(), process_);
+ Handle<Value> result = process->Call(context->Global(), argc, argv);
+ if (result.IsEmpty()) {
+ String::Utf8Value error(try_catch.Exception());
+ Log(*error);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+JsHttpRequestProcessor::~JsHttpRequestProcessor() {
+ // Dispose the persistent handles. When noone else has any
+ // references to the objects stored in the handles they will be
+ // automatically reclaimed.
+ Isolate* isolate = GetIsolate();
+ context_.Dispose(isolate);
+ process_.Dispose(isolate);
+}
+
+
+Persistent<ObjectTemplate> JsHttpRequestProcessor::request_template_;
+Persistent<ObjectTemplate> JsHttpRequestProcessor::map_template_;
+
+
+// -----------------------------------
+// --- A c c e s s i n g M a p s ---
+// -----------------------------------
+
+// Utility function that wraps a C++ http request object in a
+// JavaScript object.
+Handle<Object> JsHttpRequestProcessor::WrapMap(map<string, string>* obj) {
+ // Handle scope for temporary handles.
+ HandleScope handle_scope(GetIsolate());
+
+ // Fetch the template for creating JavaScript map wrappers.
+ // It only has to be created once, which we do on demand.
+ if (map_template_.IsEmpty()) {
+ Handle<ObjectTemplate> raw_template = MakeMapTemplate(GetIsolate());
+ map_template_.Reset(GetIsolate(), raw_template);
+ }
+ Handle<ObjectTemplate> templ =
+ Local<ObjectTemplate>::New(GetIsolate(), map_template_);
+
+ // Create an empty map wrapper.
+ Handle<Object> result = templ->NewInstance();
+
+ // Wrap the raw C++ pointer in an External so it can be referenced
+ // from within JavaScript.
+ Handle<External> map_ptr = External::New(obj);
+
+ // Store the map pointer in the JavaScript wrapper.
+ result->SetInternalField(0, map_ptr);
+
+ // Return the result through the current handle scope. Since each
+ // of these handles will go away when the handle scope is deleted
+ // we need to call Close to let one, the result, escape into the
+ // outer handle scope.
+ return handle_scope.Close(result);
+}
+
+
+// Utility function that extracts the C++ map pointer from a wrapper
+// object.
+map<string, string>* JsHttpRequestProcessor::UnwrapMap(Handle<Object> obj) {
+ Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+ void* ptr = field->Value();
+ return static_cast<map<string, string>*>(ptr);
+}
+
+
+// Convert a JavaScript string to a std::string. To not bother too
+// much with string encodings we just use ascii.
+string ObjectToString(Local<Value> value) {
+ String::Utf8Value utf8_value(value);
+ return string(*utf8_value);
+}
+
+
+void JsHttpRequestProcessor::MapGet(Local<String> name,
+ const PropertyCallbackInfo<Value>& info) {
+ // Fetch the map wrapped by this object.
+ map<string, string>* obj = UnwrapMap(info.Holder());
+
+ // Convert the JavaScript string to a std::string.
+ string key = ObjectToString(name);
+
+ // Look up the value if it exists using the standard STL ideom.
+ map<string, string>::iterator iter = obj->find(key);
+
+ // If the key is not present return an empty handle as signal
+ if (iter == obj->end()) return;
+
+ // Otherwise fetch the value and wrap it in a JavaScript string
+ const string& value = (*iter).second;
+ info.GetReturnValue().Set(
+ String::New(value.c_str(), static_cast<int>(value.length())));
+}
+
+
+void JsHttpRequestProcessor::MapSet(Local<String> name,
+ Local<Value> value_obj,
+ const PropertyCallbackInfo<Value>& info) {
+ // Fetch the map wrapped by this object.
+ map<string, string>* obj = UnwrapMap(info.Holder());
+
+ // Convert the key and value to std::strings.
+ string key = ObjectToString(name);
+ string value = ObjectToString(value_obj);
+
+ // Update the map.
+ (*obj)[key] = value;
+
+ // Return the value; any non-empty handle will work.
+ info.GetReturnValue().Set(value_obj);
+}
+
+
+Handle<ObjectTemplate> JsHttpRequestProcessor::MakeMapTemplate(
+ Isolate* isolate) {
+ HandleScope handle_scope(isolate);
+
+ Handle<ObjectTemplate> result = ObjectTemplate::New();
+ result->SetInternalFieldCount(1);
+ result->SetNamedPropertyHandler(MapGet, MapSet);
+
+ // Again, return the result through the current handle scope.
+ return handle_scope.Close(result);
+}
+
+
+// -------------------------------------------
+// --- A c c e s s i n g R e q u e s t s ---
+// -------------------------------------------
+
+/**
+ * Utility function that wraps a C++ http request object in a
+ * JavaScript object.
+ */
+Handle<Object> JsHttpRequestProcessor::WrapRequest(HttpRequest* request) {
+ // Handle scope for temporary handles.
+ HandleScope handle_scope(GetIsolate());
+
+ // Fetch the template for creating JavaScript http request wrappers.
+ // It only has to be created once, which we do on demand.
+ if (request_template_.IsEmpty()) {
+ Handle<ObjectTemplate> raw_template = MakeRequestTemplate(GetIsolate());
+ request_template_.Reset(GetIsolate(), raw_template);
+ }
+ Handle<ObjectTemplate> templ =
+ Local<ObjectTemplate>::New(GetIsolate(), request_template_);
+
+ // Create an empty http request wrapper.
+ Handle<Object> result = templ->NewInstance();
+
+ // Wrap the raw C++ pointer in an External so it can be referenced
+ // from within JavaScript.
+ Handle<External> request_ptr = External::New(request);
+
+ // Store the request pointer in the JavaScript wrapper.
+ result->SetInternalField(0, request_ptr);
+
+ // Return the result through the current handle scope. Since each
+ // of these handles will go away when the handle scope is deleted
+ // we need to call Close to let one, the result, escape into the
+ // outer handle scope.
+ return handle_scope.Close(result);
+}
+
+
+/**
+ * Utility function that extracts the C++ http request object from a
+ * wrapper object.
+ */
+HttpRequest* JsHttpRequestProcessor::UnwrapRequest(Handle<Object> obj) {
+ Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+ void* ptr = field->Value();
+ return static_cast<HttpRequest*>(ptr);
+}
+
+
+void JsHttpRequestProcessor::GetPath(Local<String> name,
+ const PropertyCallbackInfo<Value>& info) {
+ // Extract the C++ request object from the JavaScript wrapper.
+ HttpRequest* request = UnwrapRequest(info.Holder());
+
+ // Fetch the path.
+ const string& path = request->Path();
+
+ // Wrap the result in a JavaScript string and return it.
+ info.GetReturnValue().Set(
+ String::New(path.c_str(), static_cast<int>(path.length())));
+}
+
+
+void JsHttpRequestProcessor::GetReferrer(
+ Local<String> name,
+ const PropertyCallbackInfo<Value>& info) {
+ HttpRequest* request = UnwrapRequest(info.Holder());
+ const string& path = request->Referrer();
+ info.GetReturnValue().Set(
+ String::New(path.c_str(), static_cast<int>(path.length())));
+}
+
+
+void JsHttpRequestProcessor::GetHost(Local<String> name,
+ const PropertyCallbackInfo<Value>& info) {
+ HttpRequest* request = UnwrapRequest(info.Holder());
+ const string& path = request->Host();
+ info.GetReturnValue().Set(
+ String::New(path.c_str(), static_cast<int>(path.length())));
+}
+
+
+void JsHttpRequestProcessor::GetUserAgent(
+ Local<String> name,
+ const PropertyCallbackInfo<Value>& info) {
+ HttpRequest* request = UnwrapRequest(info.Holder());
+ const string& path = request->UserAgent();
+ info.GetReturnValue().Set(
+ String::New(path.c_str(), static_cast<int>(path.length())));
+}
+
+
+Handle<ObjectTemplate> JsHttpRequestProcessor::MakeRequestTemplate(
+ Isolate* isolate) {
+ HandleScope handle_scope(isolate);
+
+ Handle<ObjectTemplate> result = ObjectTemplate::New();
+ result->SetInternalFieldCount(1);
+
+ // Add accessors for each of the fields of the request.
+ result->SetAccessor(String::NewSymbol("path"), GetPath);
+ result->SetAccessor(String::NewSymbol("referrer"), GetReferrer);
+ result->SetAccessor(String::NewSymbol("host"), GetHost);
+ result->SetAccessor(String::NewSymbol("userAgent"), GetUserAgent);
+
+ // Again, return the result through the current handle scope.
+ return handle_scope.Close(result);
+}
+
+
+// --- Test ---
+
+
+void HttpRequestProcessor::Log(const char* event) {
+ printf("Logged: %s\n", event);
+}
+
+
+/**
+ * A simplified http request.
+ */
+class StringHttpRequest : public HttpRequest {
+ public:
+ StringHttpRequest(const string& path,
+ const string& referrer,
+ const string& host,
+ const string& user_agent);
+ virtual const string& Path() { return path_; }
+ virtual const string& Referrer() { return referrer_; }
+ virtual const string& Host() { return host_; }
+ virtual const string& UserAgent() { return user_agent_; }
+ private:
+ string path_;
+ string referrer_;
+ string host_;
+ string user_agent_;
+};
+
+
+StringHttpRequest::StringHttpRequest(const string& path,
+ const string& referrer,
+ const string& host,
+ const string& user_agent)
+ : path_(path),
+ referrer_(referrer),
+ host_(host),
+ user_agent_(user_agent) { }
+
+
+void ParseOptions(int argc,
+ char* argv[],
+ map<string, string>& options,
+ string* file) {
+ for (int i = 1; i < argc; i++) {
+ string arg = argv[i];
+ size_t index = arg.find('=', 0);
+ if (index == string::npos) {
+ *file = arg;
+ } else {
+ string key = arg.substr(0, index);
+ string value = arg.substr(index+1);
+ options[key] = value;
+ }
+ }
+}
+
+
+// Reads a file into a v8 string.
+Handle<String> ReadFile(const string& name) {
+ FILE* file = fopen(name.c_str(), "rb");
+ if (file == NULL) return Handle<String>();
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+ rewind(file);
+
+ char* chars = new char[size + 1];
+ chars[size] = '\0';
+ for (int i = 0; i < size;) {
+ int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
+ i += read;
+ }
+ fclose(file);
+ Handle<String> result = String::New(chars, size);
+ delete[] chars;
+ return result;
+}
+
+
+const int kSampleSize = 6;
+StringHttpRequest kSampleRequests[kSampleSize] = {
+ StringHttpRequest("/process.cc", "localhost", "google.com", "firefox"),
+ StringHttpRequest("/", "localhost", "google.net", "firefox"),
+ StringHttpRequest("/", "localhost", "google.org", "safari"),
+ StringHttpRequest("/", "localhost", "yahoo.com", "ie"),
+ StringHttpRequest("/", "localhost", "yahoo.com", "safari"),
+ StringHttpRequest("/", "localhost", "yahoo.com", "firefox")
+};
+
+
+bool ProcessEntries(HttpRequestProcessor* processor, int count,
+ StringHttpRequest* reqs) {
+ for (int i = 0; i < count; i++) {
+ if (!processor->Process(&reqs[i]))
+ return false;
+ }
+ return true;
+}
+
+
+void PrintMap(map<string, string>* m) {
+ for (map<string, string>::iterator i = m->begin(); i != m->end(); i++) {
+ pair<string, string> entry = *i;
+ printf("%s: %s\n", entry.first.c_str(), entry.second.c_str());
+ }
+}
+
+
+int main(int argc, char* argv[]) {
+ v8::V8::InitializeICU();
+ map<string, string> options;
+ string file;
+ ParseOptions(argc, argv, options, &file);
+ if (file.empty()) {
+ fprintf(stderr, "No script was specified.\n");
+ return 1;
+ }
+ Isolate* isolate = Isolate::GetCurrent();
+ HandleScope scope(isolate);
+ Handle<String> source = ReadFile(file);
+ if (source.IsEmpty()) {
+ fprintf(stderr, "Error reading '%s'.\n", file.c_str());
+ return 1;
+ }
+ JsHttpRequestProcessor processor(isolate, source);
+ map<string, string> output;
+ if (!processor.Initialize(&options, &output)) {
+ fprintf(stderr, "Error initializing processor.\n");
+ return 1;
+ }
+ if (!ProcessEntries(&processor, kSampleSize, kSampleRequests))
+ return 1;
+ PrintMap(&output);
+}
diff --git a/chromium/v8/samples/samples.gyp b/chromium/v8/samples/samples.gyp
new file mode 100644
index 00000000000..be7b9ea696c
--- /dev/null
+++ b/chromium/v8/samples/samples.gyp
@@ -0,0 +1,76 @@
+# Copyright 2012 the V8 project authors. 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': {
+ 'v8_code': 1,
+ 'v8_enable_i18n_support%': 0,
+ },
+ 'includes': ['../build/toolchain.gypi', '../build/features.gypi'],
+ 'target_defaults': {
+ 'type': 'executable',
+ 'dependencies': [
+ '../tools/gyp/v8.gyp:v8',
+ ],
+ 'include_dirs': [
+ '../include',
+ ],
+ 'conditions': [
+ ['v8_enable_i18n_support==1', {
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu.gyp:icui18n',
+ '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
+ ],
+ }],
+ ['OS=="win" and v8_enable_i18n_support==1', {
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu.gyp:icudata',
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'shell',
+ 'sources': [
+ 'shell.cc',
+ ],
+ },
+ {
+ 'target_name': 'process',
+ 'sources': [
+ 'process.cc',
+ ],
+ },
+ {
+ 'target_name': 'lineprocessor',
+ 'sources': [
+ 'lineprocessor.cc',
+ ],
+ }
+ ],
+}
diff --git a/chromium/v8/samples/shell.cc b/chromium/v8/samples/shell.cc
new file mode 100644
index 00000000000..710547c3419
--- /dev/null
+++ b/chromium/v8/samples/shell.cc
@@ -0,0 +1,355 @@
+// Copyright 2012 the V8 project authors. 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 <assert.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+#error Using compressed startup data is not supported for this sample
+#endif
+
+/**
+ * This sample program shows how to implement a simple javascript shell
+ * based on V8. This includes initializing V8 with command line options,
+ * creating global functions, compiling and executing strings.
+ *
+ * For a more sophisticated shell, consider using the debug shell D8.
+ */
+
+
+v8::Handle<v8::Context> CreateShellContext(v8::Isolate* isolate);
+void RunShell(v8::Handle<v8::Context> context);
+int RunMain(v8::Isolate* isolate, int argc, char* argv[]);
+bool ExecuteString(v8::Isolate* isolate,
+ v8::Handle<v8::String> source,
+ v8::Handle<v8::Value> name,
+ bool print_result,
+ bool report_exceptions);
+void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
+void Read(const v8::FunctionCallbackInfo<v8::Value>& args);
+void Load(const v8::FunctionCallbackInfo<v8::Value>& args);
+void Quit(const v8::FunctionCallbackInfo<v8::Value>& args);
+void Version(const v8::FunctionCallbackInfo<v8::Value>& args);
+v8::Handle<v8::String> ReadFile(const char* name);
+void ReportException(v8::Isolate* isolate, v8::TryCatch* handler);
+
+
+static bool run_shell;
+
+
+int main(int argc, char* argv[]) {
+ v8::V8::InitializeICU();
+ v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ run_shell = (argc == 1);
+ int result;
+ {
+ v8::HandleScope handle_scope(isolate);
+ v8::Handle<v8::Context> context = CreateShellContext(isolate);
+ if (context.IsEmpty()) {
+ fprintf(stderr, "Error creating context\n");
+ return 1;
+ }
+ context->Enter();
+ result = RunMain(isolate, argc, argv);
+ if (run_shell) RunShell(context);
+ context->Exit();
+ }
+ v8::V8::Dispose();
+ return result;
+}
+
+
+// Extracts a C string from a V8 Utf8Value.
+const char* ToCString(const v8::String::Utf8Value& value) {
+ return *value ? *value : "<string conversion failed>";
+}
+
+
+// Creates a new execution environment containing the built-in
+// functions.
+v8::Handle<v8::Context> CreateShellContext(v8::Isolate* isolate) {
+ // Create a template for the global object.
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
+ // Bind the global 'print' function to the C++ Print callback.
+ global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
+ // Bind the global 'read' function to the C++ Read callback.
+ global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read));
+ // Bind the global 'load' function to the C++ Load callback.
+ global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load));
+ // Bind the 'quit' function
+ global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit));
+ // Bind the 'version' function
+ global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version));
+
+ return v8::Context::New(isolate, NULL, global);
+}
+
+
+// The callback that is invoked by v8 whenever the JavaScript 'print'
+// function is called. Prints its arguments on stdout separated by
+// spaces and ending with a newline.
+void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ bool first = true;
+ for (int i = 0; i < args.Length(); i++) {
+ v8::HandleScope handle_scope(args.GetIsolate());
+ if (first) {
+ first = false;
+ } else {
+ printf(" ");
+ }
+ v8::String::Utf8Value str(args[i]);
+ const char* cstr = ToCString(str);
+ printf("%s", cstr);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+
+// The callback that is invoked by v8 whenever the JavaScript 'read'
+// function is called. This function loads the content of the file named in
+// the argument into a JavaScript string.
+void Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 1) {
+ v8::ThrowException(v8::String::New("Bad parameters"));
+ return;
+ }
+ v8::String::Utf8Value file(args[0]);
+ if (*file == NULL) {
+ v8::ThrowException(v8::String::New("Error loading file"));
+ return;
+ }
+ v8::Handle<v8::String> source = ReadFile(*file);
+ if (source.IsEmpty()) {
+ v8::ThrowException(v8::String::New("Error loading file"));
+ return;
+ }
+ args.GetReturnValue().Set(source);
+}
+
+
+// The callback that is invoked by v8 whenever the JavaScript 'load'
+// function is called. Loads, compiles and executes its argument
+// JavaScript file.
+void Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ for (int i = 0; i < args.Length(); i++) {
+ v8::HandleScope handle_scope(args.GetIsolate());
+ v8::String::Utf8Value file(args[i]);
+ if (*file == NULL) {
+ v8::ThrowException(v8::String::New("Error loading file"));
+ return;
+ }
+ v8::Handle<v8::String> source = ReadFile(*file);
+ if (source.IsEmpty()) {
+ v8::ThrowException(v8::String::New("Error loading file"));
+ return;
+ }
+ if (!ExecuteString(args.GetIsolate(),
+ source,
+ v8::String::New(*file),
+ false,
+ false)) {
+ v8::ThrowException(v8::String::New("Error executing file"));
+ return;
+ }
+ }
+}
+
+
+// The callback that is invoked by v8 whenever the JavaScript 'quit'
+// function is called. Quits.
+void Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ // If not arguments are given args[0] will yield undefined which
+ // converts to the integer value 0.
+ int exit_code = args[0]->Int32Value();
+ fflush(stdout);
+ fflush(stderr);
+ exit(exit_code);
+}
+
+
+void Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(v8::String::New(v8::V8::GetVersion()));
+}
+
+
+// Reads a file into a v8 string.
+v8::Handle<v8::String> ReadFile(const char* name) {
+ FILE* file = fopen(name, "rb");
+ if (file == NULL) return v8::Handle<v8::String>();
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+ rewind(file);
+
+ char* chars = new char[size + 1];
+ chars[size] = '\0';
+ for (int i = 0; i < size;) {
+ int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
+ i += read;
+ }
+ fclose(file);
+ v8::Handle<v8::String> result = v8::String::New(chars, size);
+ delete[] chars;
+ return result;
+}
+
+
+// Process remaining command line arguments and execute files
+int RunMain(v8::Isolate* isolate, int argc, char* argv[]) {
+ for (int i = 1; i < argc; i++) {
+ const char* str = argv[i];
+ if (strcmp(str, "--shell") == 0) {
+ run_shell = true;
+ } else if (strcmp(str, "-f") == 0) {
+ // Ignore any -f flags for compatibility with the other stand-
+ // alone JavaScript engines.
+ continue;
+ } else if (strncmp(str, "--", 2) == 0) {
+ fprintf(stderr,
+ "Warning: unknown flag %s.\nTry --help for options\n", str);
+ } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
+ // Execute argument given to -e option directly.
+ v8::Handle<v8::String> file_name = v8::String::New("unnamed");
+ v8::Handle<v8::String> source = v8::String::New(argv[++i]);
+ if (!ExecuteString(isolate, source, file_name, false, true)) return 1;
+ } else {
+ // Use all other arguments as names of files to load and run.
+ v8::Handle<v8::String> file_name = v8::String::New(str);
+ v8::Handle<v8::String> source = ReadFile(str);
+ if (source.IsEmpty()) {
+ fprintf(stderr, "Error reading '%s'\n", str);
+ continue;
+ }
+ if (!ExecuteString(isolate, source, file_name, false, true)) return 1;
+ }
+ }
+ return 0;
+}
+
+
+// The read-eval-execute loop of the shell.
+void RunShell(v8::Handle<v8::Context> context) {
+ fprintf(stderr, "V8 version %s [sample shell]\n", v8::V8::GetVersion());
+ static const int kBufferSize = 256;
+ // Enter the execution environment before evaluating any code.
+ v8::Context::Scope context_scope(context);
+ v8::Local<v8::String> name(v8::String::New("(shell)"));
+ while (true) {
+ char buffer[kBufferSize];
+ fprintf(stderr, "> ");
+ char* str = fgets(buffer, kBufferSize, stdin);
+ if (str == NULL) break;
+ v8::HandleScope handle_scope(context->GetIsolate());
+ ExecuteString(context->GetIsolate(),
+ v8::String::New(str),
+ name,
+ true,
+ true);
+ }
+ fprintf(stderr, "\n");
+}
+
+
+// Executes a string within the current v8 context.
+bool ExecuteString(v8::Isolate* isolate,
+ v8::Handle<v8::String> source,
+ v8::Handle<v8::Value> name,
+ bool print_result,
+ bool report_exceptions) {
+ v8::HandleScope handle_scope(isolate);
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
+ if (script.IsEmpty()) {
+ // Print errors that happened during compilation.
+ if (report_exceptions)
+ ReportException(isolate, &try_catch);
+ return false;
+ } else {
+ v8::Handle<v8::Value> result = script->Run();
+ if (result.IsEmpty()) {
+ assert(try_catch.HasCaught());
+ // Print errors that happened during execution.
+ if (report_exceptions)
+ ReportException(isolate, &try_catch);
+ return false;
+ } else {
+ assert(!try_catch.HasCaught());
+ if (print_result && !result->IsUndefined()) {
+ // If all went well and the result wasn't undefined then print
+ // the returned value.
+ v8::String::Utf8Value str(result);
+ const char* cstr = ToCString(str);
+ printf("%s\n", cstr);
+ }
+ return true;
+ }
+ }
+}
+
+
+void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
+ v8::HandleScope handle_scope(isolate);
+ v8::String::Utf8Value exception(try_catch->Exception());
+ const char* exception_string = ToCString(exception);
+ v8::Handle<v8::Message> message = try_catch->Message();
+ if (message.IsEmpty()) {
+ // V8 didn't provide any extra information about this error; just
+ // print the exception.
+ fprintf(stderr, "%s\n", exception_string);
+ } else {
+ // Print (filename):(line number): (message).
+ v8::String::Utf8Value filename(message->GetScriptResourceName());
+ const char* filename_string = ToCString(filename);
+ int linenum = message->GetLineNumber();
+ fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string);
+ // Print line of source code.
+ v8::String::Utf8Value sourceline(message->GetSourceLine());
+ const char* sourceline_string = ToCString(sourceline);
+ fprintf(stderr, "%s\n", sourceline_string);
+ // Print wavy underline (GetUnderline is deprecated).
+ int start = message->GetStartColumn();
+ for (int i = 0; i < start; i++) {
+ fprintf(stderr, " ");
+ }
+ int end = message->GetEndColumn();
+ for (int i = start; i < end; i++) {
+ fprintf(stderr, "^");
+ }
+ fprintf(stderr, "\n");
+ v8::String::Utf8Value stack_trace(try_catch->StackTrace());
+ if (stack_trace.length() > 0) {
+ const char* stack_trace_string = ToCString(stack_trace);
+ fprintf(stderr, "%s\n", stack_trace_string);
+ }
+ }
+}
diff --git a/chromium/v8/src/accessors.cc b/chromium/v8/src/accessors.cc
new file mode 100644
index 00000000000..a43eb78b870
--- /dev/null
+++ b/chromium/v8/src/accessors.cc
@@ -0,0 +1,857 @@
+// Copyright 2012 the V8 project authors. 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 "accessors.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 "property-details.h"
+
+namespace v8 {
+namespace internal {
+
+
+template <class C>
+static C* FindInstanceOf(Isolate* isolate, Object* obj) {
+ for (Object* cur = obj; !cur->IsNull(); cur = cur->GetPrototype(isolate)) {
+ if (Is<C>(cur)) return C::cast(cur);
+ }
+ return NULL;
+}
+
+
+// Entry point that never should be called.
+MaybeObject* Accessors::IllegalSetter(JSObject*, Object*, void*) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+Object* Accessors::IllegalGetAccessor(Object* object, void*) {
+ UNREACHABLE();
+ return object;
+}
+
+
+MaybeObject* Accessors::ReadOnlySetAccessor(JSObject*, Object* value, void*) {
+ // According to ECMA-262, section 8.6.2.2, page 28, setting
+ // read-only properties must be silently ignored.
+ return value;
+}
+
+
+//
+// Accessors::ArrayLength
+//
+
+
+MaybeObject* Accessors::ArrayGetLength(Object* object, void*) {
+ // Traverse the prototype chain until we reach an array.
+ JSArray* holder = FindInstanceOf<JSArray>(Isolate::Current(), object);
+ return holder == NULL ? Smi::FromInt(0) : holder->length();
+}
+
+
+// The helper function will 'flatten' Number objects.
+Object* Accessors::FlattenNumber(Object* value) {
+ if (value->IsNumber() || !value->IsJSValue()) return value;
+ JSValue* wrapper = JSValue::cast(value);
+ ASSERT(Isolate::Current()->context()->native_context()->number_function()->
+ has_initial_map());
+ Map* number_map = Isolate::Current()->context()->native_context()->
+ number_function()->initial_map();
+ if (wrapper->map() == number_map) return wrapper->value();
+ return value;
+}
+
+
+MaybeObject* Accessors::ArraySetLength(JSObject* object, Object* value, void*) {
+ Isolate* isolate = object->GetIsolate();
+
+ // This means one of the object's prototypes is a JSArray and the
+ // object does not have a 'length' property. Calling SetProperty
+ // causes an infinite loop.
+ if (!object->IsJSArray()) {
+ return object->SetLocalPropertyIgnoreAttributes(
+ isolate->heap()->length_string(), value, NONE);
+ }
+
+ value = FlattenNumber(value);
+
+ // Need to call methods that may trigger GC.
+ HandleScope scope(isolate);
+
+ // Protect raw pointers.
+ Handle<JSArray> array_handle(JSArray::cast(object), isolate);
+ Handle<Object> value_handle(value, isolate);
+
+ bool has_exception;
+ Handle<Object> uint32_v = Execution::ToUint32(value_handle, &has_exception);
+ if (has_exception) return Failure::Exception();
+ Handle<Object> number_v = Execution::ToNumber(value_handle, &has_exception);
+ if (has_exception) return Failure::Exception();
+
+ if (uint32_v->Number() == number_v->Number()) {
+ return array_handle->SetElementsLength(*uint32_v);
+ }
+ return isolate->Throw(
+ *isolate->factory()->NewRangeError("invalid_array_length",
+ HandleVector<Object>(NULL, 0)));
+}
+
+
+const AccessorDescriptor Accessors::ArrayLength = {
+ ArrayGetLength,
+ ArraySetLength,
+ 0
+};
+
+
+//
+// Accessors::StringLength
+//
+
+
+MaybeObject* Accessors::StringGetLength(Object* object, void*) {
+ Object* value = object;
+ if (object->IsJSValue()) value = JSValue::cast(object)->value();
+ if (value->IsString()) return Smi::FromInt(String::cast(value)->length());
+ // If object is not a string we return 0 to be compatible with WebKit.
+ // Note: Firefox returns the length of ToString(object).
+ return Smi::FromInt(0);
+}
+
+
+const AccessorDescriptor Accessors::StringLength = {
+ StringGetLength,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptSource
+//
+
+
+MaybeObject* Accessors::ScriptGetSource(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->source();
+}
+
+
+const AccessorDescriptor Accessors::ScriptSource = {
+ ScriptGetSource,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptName
+//
+
+
+MaybeObject* Accessors::ScriptGetName(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->name();
+}
+
+
+const AccessorDescriptor Accessors::ScriptName = {
+ ScriptGetName,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptId
+//
+
+
+MaybeObject* Accessors::ScriptGetId(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->id();
+}
+
+
+const AccessorDescriptor Accessors::ScriptId = {
+ ScriptGetId,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptLineOffset
+//
+
+
+MaybeObject* Accessors::ScriptGetLineOffset(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->line_offset();
+}
+
+
+const AccessorDescriptor Accessors::ScriptLineOffset = {
+ ScriptGetLineOffset,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptColumnOffset
+//
+
+
+MaybeObject* Accessors::ScriptGetColumnOffset(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->column_offset();
+}
+
+
+const AccessorDescriptor Accessors::ScriptColumnOffset = {
+ ScriptGetColumnOffset,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptData
+//
+
+
+MaybeObject* Accessors::ScriptGetData(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->data();
+}
+
+
+const AccessorDescriptor Accessors::ScriptData = {
+ ScriptGetData,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptType
+//
+
+
+MaybeObject* Accessors::ScriptGetType(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->type();
+}
+
+
+const AccessorDescriptor Accessors::ScriptType = {
+ ScriptGetType,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptCompilationType
+//
+
+
+MaybeObject* Accessors::ScriptGetCompilationType(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Smi::FromInt(Script::cast(script)->compilation_type());
+}
+
+
+const AccessorDescriptor Accessors::ScriptCompilationType = {
+ ScriptGetCompilationType,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptGetLineEnds
+//
+
+
+MaybeObject* Accessors::ScriptGetLineEnds(Object* object, void*) {
+ JSValue* wrapper = JSValue::cast(object);
+ Isolate* isolate = wrapper->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Script> script(Script::cast(wrapper->value()), isolate);
+ InitScriptLineEnds(script);
+ ASSERT(script->line_ends()->IsFixedArray());
+ Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
+ // We do not want anyone to modify this array from JS.
+ ASSERT(*line_ends == isolate->heap()->empty_fixed_array() ||
+ line_ends->map() == isolate->heap()->fixed_cow_array_map());
+ Handle<JSArray> js_array =
+ isolate->factory()->NewJSArrayWithElements(line_ends);
+ return *js_array;
+}
+
+
+const AccessorDescriptor Accessors::ScriptLineEnds = {
+ ScriptGetLineEnds,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptGetContextData
+//
+
+
+MaybeObject* Accessors::ScriptGetContextData(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->context_data();
+}
+
+
+const AccessorDescriptor Accessors::ScriptContextData = {
+ ScriptGetContextData,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptGetEvalFromScript
+//
+
+
+MaybeObject* Accessors::ScriptGetEvalFromScript(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ if (!Script::cast(script)->eval_from_shared()->IsUndefined()) {
+ Handle<SharedFunctionInfo> eval_from_shared(
+ SharedFunctionInfo::cast(Script::cast(script)->eval_from_shared()));
+
+ if (eval_from_shared->script()->IsScript()) {
+ Handle<Script> eval_from_script(Script::cast(eval_from_shared->script()));
+ return *GetScriptWrapper(eval_from_script);
+ }
+ }
+ return HEAP->undefined_value();
+}
+
+
+const AccessorDescriptor Accessors::ScriptEvalFromScript = {
+ ScriptGetEvalFromScript,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptGetEvalFromScriptPosition
+//
+
+
+MaybeObject* Accessors::ScriptGetEvalFromScriptPosition(Object* object, void*) {
+ Script* raw_script = Script::cast(JSValue::cast(object)->value());
+ HandleScope scope(raw_script->GetIsolate());
+ Handle<Script> script(raw_script);
+
+ // If this is not a script compiled through eval there is no eval position.
+ if (script->compilation_type() != Script::COMPILATION_TYPE_EVAL) {
+ return script->GetHeap()->undefined_value();
+ }
+
+ // Get the function from where eval was called and find the source position
+ // from the instruction offset.
+ Handle<Code> code(SharedFunctionInfo::cast(
+ script->eval_from_shared())->code());
+ return Smi::FromInt(code->SourcePosition(code->instruction_start() +
+ script->eval_from_instructions_offset()->value()));
+}
+
+
+const AccessorDescriptor Accessors::ScriptEvalFromScriptPosition = {
+ ScriptGetEvalFromScriptPosition,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::ScriptGetEvalFromFunctionName
+//
+
+
+MaybeObject* Accessors::ScriptGetEvalFromFunctionName(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(
+ Script::cast(script)->eval_from_shared()));
+
+
+ // Find the name of the function calling eval.
+ if (!shared->name()->IsUndefined()) {
+ return shared->name();
+ } else {
+ return shared->inferred_name();
+ }
+}
+
+
+const AccessorDescriptor Accessors::ScriptEvalFromFunctionName = {
+ ScriptGetEvalFromFunctionName,
+ IllegalSetter,
+ 0
+};
+
+
+//
+// Accessors::FunctionPrototype
+//
+
+
+Handle<Object> Accessors::FunctionGetPrototype(Handle<Object> object) {
+ Isolate* isolate = Isolate::Current();
+ CALL_HEAP_FUNCTION(
+ isolate, Accessors::FunctionGetPrototype(*object, 0), Object);
+}
+
+
+MaybeObject* Accessors::FunctionGetPrototype(Object* object, void*) {
+ Isolate* isolate = Isolate::Current();
+ JSFunction* function_raw = FindInstanceOf<JSFunction>(isolate, object);
+ if (function_raw == NULL) return isolate->heap()->undefined_value();
+ while (!function_raw->should_have_prototype()) {
+ function_raw = FindInstanceOf<JSFunction>(isolate,
+ function_raw->GetPrototype());
+ // There has to be one because we hit the getter.
+ ASSERT(function_raw != NULL);
+ }
+
+ if (!function_raw->has_prototype()) {
+ HandleScope scope(isolate);
+ Handle<JSFunction> function(function_raw);
+ Handle<Object> proto = isolate->factory()->NewFunctionPrototype(function);
+ JSFunction::SetPrototype(function, proto);
+ function_raw = *function;
+ }
+ return function_raw->prototype();
+}
+
+
+MaybeObject* Accessors::FunctionSetPrototype(JSObject* object,
+ Object* value_raw,
+ void*) {
+ Isolate* isolate = object->GetIsolate();
+ Heap* heap = isolate->heap();
+ JSFunction* function_raw = FindInstanceOf<JSFunction>(isolate, object);
+ if (function_raw == NULL) return heap->undefined_value();
+ if (!function_raw->should_have_prototype()) {
+ // Since we hit this accessor, object will have no prototype property.
+ return object->SetLocalPropertyIgnoreAttributes(heap->prototype_string(),
+ value_raw,
+ NONE);
+ }
+
+ HandleScope scope(isolate);
+ Handle<JSFunction> function(function_raw, isolate);
+ Handle<Object> value(value_raw, isolate);
+
+ Handle<Object> old_value;
+ bool is_observed =
+ FLAG_harmony_observation &&
+ *function == object &&
+ function->map()->is_observed();
+ if (is_observed) {
+ if (function->has_prototype())
+ old_value = handle(function->prototype(), isolate);
+ else
+ old_value = isolate->factory()->NewFunctionPrototype(function);
+ }
+
+ JSFunction::SetPrototype(function, value);
+ ASSERT(function->prototype() == *value);
+
+ if (is_observed && !old_value->SameValue(*value)) {
+ JSObject::EnqueueChangeRecord(
+ function, "updated", isolate->factory()->prototype_string(), old_value);
+ }
+
+ return *function;
+}
+
+
+const AccessorDescriptor Accessors::FunctionPrototype = {
+ FunctionGetPrototype,
+ FunctionSetPrototype,
+ 0
+};
+
+
+//
+// Accessors::FunctionLength
+//
+
+
+MaybeObject* Accessors::FunctionGetLength(Object* object, void*) {
+ Isolate* isolate = Isolate::Current();
+ JSFunction* function = FindInstanceOf<JSFunction>(isolate, object);
+ if (function == NULL) return Smi::FromInt(0);
+ // Check if already compiled.
+ if (function->shared()->is_compiled()) {
+ return Smi::FromInt(function->shared()->length());
+ }
+ // If the function isn't compiled yet, the length is not computed correctly
+ // yet. Compile it now and return the right length.
+ HandleScope scope(isolate);
+ Handle<JSFunction> handle(function);
+ if (JSFunction::CompileLazy(handle, KEEP_EXCEPTION)) {
+ return Smi::FromInt(handle->shared()->length());
+ }
+ return Failure::Exception();
+}
+
+
+const AccessorDescriptor Accessors::FunctionLength = {
+ FunctionGetLength,
+ ReadOnlySetAccessor,
+ 0
+};
+
+
+//
+// Accessors::FunctionName
+//
+
+
+MaybeObject* Accessors::FunctionGetName(Object* object, void*) {
+ Isolate* isolate = Isolate::Current();
+ JSFunction* holder = FindInstanceOf<JSFunction>(isolate, object);
+ return holder == NULL
+ ? isolate->heap()->undefined_value()
+ : holder->shared()->name();
+}
+
+
+const AccessorDescriptor Accessors::FunctionName = {
+ FunctionGetName,
+ ReadOnlySetAccessor,
+ 0
+};
+
+
+//
+// Accessors::FunctionArguments
+//
+
+
+Handle<Object> Accessors::FunctionGetArguments(Handle<Object> object) {
+ Isolate* isolate = Isolate::Current();
+ CALL_HEAP_FUNCTION(
+ isolate, Accessors::FunctionGetArguments(*object, 0), Object);
+}
+
+
+static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
+ JavaScriptFrame* frame,
+ Handle<JSFunction> inlined_function,
+ int inlined_frame_index) {
+ Isolate* isolate = inlined_function->GetIsolate();
+ Factory* factory = isolate->factory();
+ 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);
+ for (int i = 0; i < args_count; ++i) {
+ Handle<Object> value = args_slots[i].GetValue(isolate);
+ array->set(i, *value);
+ }
+ arguments->set_elements(*array);
+ args_slots.Dispose();
+
+ // Return the freshly allocated arguments object.
+ return *arguments;
+}
+
+
+MaybeObject* Accessors::FunctionGetArguments(Object* object, void*) {
+ Isolate* isolate = Isolate::Current();
+ HandleScope scope(isolate);
+ JSFunction* holder = FindInstanceOf<JSFunction>(isolate, object);
+ if (holder == NULL) return isolate->heap()->undefined_value();
+ Handle<JSFunction> function(holder, isolate);
+
+ if (function->shared()->native()) return isolate->heap()->null_value();
+ // Find the top invocation of the function by traversing frames.
+ List<JSFunction*> functions(2);
+ for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+ frame->GetFunctions(&functions);
+ for (int i = functions.length() - 1; i >= 0; i--) {
+ // Skip all frames that aren't invocations of the given function.
+ if (functions[i] != *function) continue;
+
+ if (i > 0) {
+ // The function in question was inlined. Inlined functions have the
+ // correct number of arguments and no allocated arguments object, so
+ // we can construct a fresh one by interpreting the function's
+ // deoptimization input data.
+ return ConstructArgumentsObjectForInlinedFunction(frame, function, i);
+ }
+
+ if (!frame->is_optimized()) {
+ // If there is an arguments variable in the stack, we return that.
+ Handle<ScopeInfo> scope_info(function->shared()->scope_info());
+ int index = scope_info->StackSlotIndex(
+ isolate->heap()->arguments_string());
+ if (index >= 0) {
+ Handle<Object> arguments(frame->GetExpression(index), isolate);
+ if (!arguments->IsArgumentsMarker()) return *arguments;
+ }
+ }
+
+ // If there is no arguments variable in the stack or we have an
+ // optimized frame, we find the frame that holds the actual arguments
+ // passed to the function.
+ it.AdvanceToArgumentsFrame();
+ frame = it.frame();
+
+ // Get the number of arguments and construct an arguments object
+ // mirror for the right frame.
+ const int length = frame->ComputeParametersCount();
+ Handle<JSObject> arguments = isolate->factory()->NewArgumentsObject(
+ function, length);
+ Handle<FixedArray> array = isolate->factory()->NewFixedArray(length);
+
+ // Copy the parameters to the arguments object.
+ ASSERT(array->length() == length);
+ for (int i = 0; i < length; i++) array->set(i, frame->GetParameter(i));
+ arguments->set_elements(*array);
+
+ // Return the freshly allocated arguments object.
+ return *arguments;
+ }
+ functions.Rewind(0);
+ }
+
+ // No frame corresponding to the given function found. Return null.
+ return isolate->heap()->null_value();
+}
+
+
+const AccessorDescriptor Accessors::FunctionArguments = {
+ FunctionGetArguments,
+ ReadOnlySetAccessor,
+ 0
+};
+
+
+//
+// Accessors::FunctionCaller
+//
+
+
+class FrameFunctionIterator {
+ public:
+ FrameFunctionIterator(Isolate* isolate, const DisallowHeapAllocation& promise)
+ : frame_iterator_(isolate),
+ functions_(2),
+ index_(0) {
+ GetFunctions();
+ }
+ JSFunction* next() {
+ if (functions_.length() == 0) return NULL;
+ JSFunction* next_function = functions_[index_];
+ index_--;
+ if (index_ < 0) {
+ GetFunctions();
+ }
+ return next_function;
+ }
+
+ // Iterate through functions until the first occurence of 'function'.
+ // Returns true if 'function' is found, and false if the iterator ends
+ // without finding it.
+ bool Find(JSFunction* function) {
+ JSFunction* next_function;
+ do {
+ next_function = next();
+ if (next_function == function) return true;
+ } while (next_function != NULL);
+ return false;
+ }
+
+ private:
+ void GetFunctions() {
+ functions_.Rewind(0);
+ if (frame_iterator_.done()) return;
+ JavaScriptFrame* frame = frame_iterator_.frame();
+ frame->GetFunctions(&functions_);
+ ASSERT(functions_.length() > 0);
+ frame_iterator_.Advance();
+ index_ = functions_.length() - 1;
+ }
+ JavaScriptFrameIterator frame_iterator_;
+ List<JSFunction*> functions_;
+ int index_;
+};
+
+
+MaybeObject* Accessors::FunctionGetCaller(Object* object, void*) {
+ Isolate* isolate = Isolate::Current();
+ HandleScope scope(isolate);
+ DisallowHeapAllocation no_allocation;
+ JSFunction* holder = FindInstanceOf<JSFunction>(isolate, object);
+ if (holder == NULL) return isolate->heap()->undefined_value();
+ if (holder->shared()->native()) return isolate->heap()->null_value();
+ Handle<JSFunction> function(holder, isolate);
+
+ FrameFunctionIterator it(isolate, no_allocation);
+
+ // Find the function from the frames.
+ if (!it.Find(*function)) {
+ // No frame corresponding to the given function found. Return null.
+ return isolate->heap()->null_value();
+ }
+
+ // Find previously called non-toplevel function.
+ JSFunction* caller;
+ do {
+ caller = it.next();
+ if (caller == NULL) return isolate->heap()->null_value();
+ } while (caller->shared()->is_toplevel());
+
+ // If caller is a built-in function and caller's caller is also built-in,
+ // use that instead.
+ JSFunction* potential_caller = caller;
+ while (potential_caller != NULL && potential_caller->IsBuiltin()) {
+ caller = potential_caller;
+ potential_caller = it.next();
+ }
+ if (!caller->shared()->native() && potential_caller != NULL) {
+ caller = potential_caller;
+ }
+ // 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();
+ }
+ // Censor if the caller is not a classic mode function.
+ // Change from ES5, which used to throw, see:
+ // https://bugs.ecmascript.org/show_bug.cgi?id=310
+ if (!caller->shared()->is_classic_mode()) {
+ return isolate->heap()->null_value();
+ }
+
+ return caller;
+}
+
+
+const AccessorDescriptor Accessors::FunctionCaller = {
+ FunctionGetCaller,
+ ReadOnlySetAccessor,
+ 0
+};
+
+
+//
+// Accessors::MakeModuleExport
+//
+
+static void ModuleGetExport(
+ v8::Local<v8::String> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ JSModule* instance = JSModule::cast(*v8::Utils::OpenHandle(*info.Holder()));
+ Context* context = Context::cast(instance->context());
+ ASSERT(context->IsModuleContext());
+ int slot = info.Data()->Int32Value();
+ Object* value = context->get(slot);
+ Isolate* isolate = instance->GetIsolate();
+ if (value->IsTheHole()) {
+ Handle<String> name = v8::Utils::OpenHandle(*property);
+ isolate->ScheduleThrow(
+ *isolate->factory()->NewReferenceError("not_defined",
+ HandleVector(&name, 1)));
+ return;
+ }
+ info.GetReturnValue().Set(v8::Utils::ToLocal(Handle<Object>(value, isolate)));
+}
+
+
+static void ModuleSetExport(
+ v8::Local<v8::String> property,
+ v8::Local<v8::Value> value,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ JSModule* instance = JSModule::cast(*v8::Utils::OpenHandle(*info.Holder()));
+ Context* context = Context::cast(instance->context());
+ ASSERT(context->IsModuleContext());
+ int slot = info.Data()->Int32Value();
+ Object* old_value = context->get(slot);
+ if (old_value->IsTheHole()) {
+ Handle<String> name = v8::Utils::OpenHandle(*property);
+ Isolate* isolate = instance->GetIsolate();
+ isolate->ScheduleThrow(
+ *isolate->factory()->NewReferenceError("not_defined",
+ HandleVector(&name, 1)));
+ return;
+ }
+ context->set(slot, *v8::Utils::OpenHandle(*value));
+}
+
+
+Handle<AccessorInfo> Accessors::MakeModuleExport(
+ Handle<String> name,
+ int index,
+ PropertyAttributes attributes) {
+ Factory* factory = name->GetIsolate()->factory();
+ Handle<ExecutableAccessorInfo> info = factory->NewExecutableAccessorInfo();
+ info->set_property_attributes(attributes);
+ info->set_all_can_read(true);
+ info->set_all_can_write(true);
+ info->set_name(*name);
+ info->set_data(Smi::FromInt(index));
+ Handle<Object> getter = v8::FromCData(&ModuleGetExport);
+ Handle<Object> setter = v8::FromCData(&ModuleSetExport);
+ info->set_getter(*getter);
+ if (!(attributes & ReadOnly)) info->set_setter(*setter);
+ return info;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/accessors.h b/chromium/v8/src/accessors.h
new file mode 100644
index 00000000000..ae56a3d4449
--- /dev/null
+++ b/chromium/v8/src/accessors.h
@@ -0,0 +1,125 @@
+// Copyright 2012 the V8 project authors. 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_ACCESSORS_H_
+#define V8_ACCESSORS_H_
+
+#include "allocation.h"
+#include "v8globals.h"
+
+namespace v8 {
+namespace internal {
+
+// The list of accessor descriptors. This is a second-order macro
+// taking a macro to be applied to all accessor descriptor names.
+#define ACCESSOR_DESCRIPTOR_LIST(V) \
+ V(FunctionPrototype) \
+ V(FunctionLength) \
+ V(FunctionName) \
+ V(FunctionArguments) \
+ V(FunctionCaller) \
+ V(ArrayLength) \
+ V(StringLength) \
+ V(ScriptSource) \
+ V(ScriptName) \
+ V(ScriptId) \
+ V(ScriptLineOffset) \
+ V(ScriptColumnOffset) \
+ V(ScriptData) \
+ V(ScriptType) \
+ V(ScriptCompilationType) \
+ V(ScriptLineEnds) \
+ V(ScriptContextData) \
+ V(ScriptEvalFromScript) \
+ V(ScriptEvalFromScriptPosition) \
+ V(ScriptEvalFromFunctionName)
+
+// Accessors contains all predefined proxy accessors.
+
+class Accessors : public AllStatic {
+ public:
+ // Accessor descriptors.
+#define ACCESSOR_DESCRIPTOR_DECLARATION(name) \
+ static const AccessorDescriptor name;
+ ACCESSOR_DESCRIPTOR_LIST(ACCESSOR_DESCRIPTOR_DECLARATION)
+#undef ACCESSOR_DESCRIPTOR_DECLARATION
+
+ enum DescriptorId {
+#define ACCESSOR_DESCRIPTOR_DECLARATION(name) \
+ k##name,
+ ACCESSOR_DESCRIPTOR_LIST(ACCESSOR_DESCRIPTOR_DECLARATION)
+#undef ACCESSOR_DESCRIPTOR_DECLARATION
+ descriptorCount
+ };
+
+ // Accessor functions called directly from the runtime system.
+ static Handle<Object> FunctionGetPrototype(Handle<Object> object);
+ static Handle<Object> FunctionGetArguments(Handle<Object> object);
+
+ MUST_USE_RESULT static MaybeObject* FunctionSetPrototype(JSObject* object,
+ Object* value,
+ void*);
+
+ // Accessor infos.
+ static Handle<AccessorInfo> MakeModuleExport(
+ Handle<String> name, int index, PropertyAttributes attributes);
+
+ private:
+ // Accessor functions only used through the descriptor.
+ static MaybeObject* FunctionGetPrototype(Object* object, void*);
+ static MaybeObject* FunctionGetLength(Object* object, void*);
+ static MaybeObject* FunctionGetName(Object* object, void*);
+ static MaybeObject* FunctionGetArguments(Object* object, void*);
+ static MaybeObject* FunctionGetCaller(Object* object, void*);
+ MUST_USE_RESULT static MaybeObject* ArraySetLength(JSObject* object,
+ Object* value, void*);
+ static MaybeObject* ArrayGetLength(Object* object, void*);
+ static MaybeObject* StringGetLength(Object* object, void*);
+ static MaybeObject* ScriptGetName(Object* object, void*);
+ static MaybeObject* ScriptGetId(Object* object, void*);
+ static MaybeObject* ScriptGetSource(Object* object, void*);
+ static MaybeObject* ScriptGetLineOffset(Object* object, void*);
+ static MaybeObject* ScriptGetColumnOffset(Object* object, void*);
+ static MaybeObject* ScriptGetData(Object* object, void*);
+ static MaybeObject* ScriptGetType(Object* object, void*);
+ static MaybeObject* ScriptGetCompilationType(Object* object, void*);
+ static MaybeObject* ScriptGetLineEnds(Object* object, void*);
+ static MaybeObject* ScriptGetContextData(Object* object, void*);
+ static MaybeObject* ScriptGetEvalFromScript(Object* object, void*);
+ static MaybeObject* ScriptGetEvalFromScriptPosition(Object* object, void*);
+ static MaybeObject* ScriptGetEvalFromFunctionName(Object* object, void*);
+
+ // Helper functions.
+ static Object* FlattenNumber(Object* value);
+ static MaybeObject* IllegalSetter(JSObject*, Object*, void*);
+ static Object* IllegalGetAccessor(Object* object, void*);
+ static MaybeObject* ReadOnlySetAccessor(JSObject*, Object* value, void*);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_ACCESSORS_H_
diff --git a/chromium/v8/src/allocation-inl.h b/chromium/v8/src/allocation-inl.h
new file mode 100644
index 00000000000..d32db4b17fc
--- /dev/null
+++ b/chromium/v8/src/allocation-inl.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef V8_ALLOCATION_INL_H_
+#define V8_ALLOCATION_INL_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+
+void* PreallocatedStorageAllocationPolicy::New(size_t size) {
+ return Isolate::Current()->PreallocatedStorageNew(size);
+}
+
+
+void PreallocatedStorageAllocationPolicy::Delete(void* p) {
+ return Isolate::Current()->PreallocatedStorageDelete(p);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_ALLOCATION_INL_H_
diff --git a/chromium/v8/src/allocation.cc b/chromium/v8/src/allocation.cc
new file mode 100644
index 00000000000..94aaad3fd42
--- /dev/null
+++ b/chromium/v8/src/allocation.cc
@@ -0,0 +1,123 @@
+// Copyright 2012 the V8 project authors. 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 "allocation.h"
+
+#include <stdlib.h> // For free, malloc.
+#include "checks.h"
+#include "platform.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+void* Malloced::New(size_t size) {
+ void* result = malloc(size);
+ if (result == NULL) {
+ v8::internal::FatalProcessOutOfMemory("Malloced operator new");
+ }
+ return result;
+}
+
+
+void Malloced::Delete(void* p) {
+ free(p);
+}
+
+
+void Malloced::FatalProcessOutOfMemory() {
+ v8::internal::FatalProcessOutOfMemory("Out of memory");
+}
+
+
+#ifdef DEBUG
+
+static void* invalid = static_cast<void*>(NULL);
+
+void* Embedded::operator new(size_t size) {
+ UNREACHABLE();
+ return invalid;
+}
+
+
+void Embedded::operator delete(void* p) {
+ UNREACHABLE();
+}
+
+
+void* AllStatic::operator new(size_t size) {
+ UNREACHABLE();
+ return invalid;
+}
+
+
+void AllStatic::operator delete(void* p) {
+ UNREACHABLE();
+}
+
+#endif
+
+
+char* StrDup(const char* str) {
+ int length = StrLength(str);
+ char* result = NewArray<char>(length + 1);
+ OS::MemCopy(result, str, length);
+ result[length] = '\0';
+ return result;
+}
+
+
+char* StrNDup(const char* str, int n) {
+ int length = StrLength(str);
+ if (n < length) length = n;
+ char* result = NewArray<char>(length + 1);
+ OS::MemCopy(result, str, length);
+ result[length] = '\0';
+ return result;
+}
+
+
+void PreallocatedStorage::LinkTo(PreallocatedStorage* other) {
+ next_ = other->next_;
+ other->next_->previous_ = this;
+ previous_ = other;
+ other->next_ = this;
+}
+
+
+void PreallocatedStorage::Unlink() {
+ next_->previous_ = previous_;
+ previous_->next_ = next_;
+}
+
+
+PreallocatedStorage::PreallocatedStorage(size_t size)
+ : size_(size) {
+ previous_ = next_ = this;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/allocation.h b/chromium/v8/src/allocation.h
new file mode 100644
index 00000000000..45bde4c4cb0
--- /dev/null
+++ b/chromium/v8/src/allocation.h
@@ -0,0 +1,142 @@
+// Copyright 2012 the V8 project authors. 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_ALLOCATION_H_
+#define V8_ALLOCATION_H_
+
+#include "globals.h"
+
+namespace v8 {
+namespace internal {
+
+// Called when allocation routines fail to allocate.
+// This function should not return, but should terminate the current
+// processing.
+void FatalProcessOutOfMemory(const char* message);
+
+// Superclass for classes managed with new & delete.
+class Malloced {
+ public:
+ void* operator new(size_t size) { return New(size); }
+ void operator delete(void* p) { Delete(p); }
+
+ static void FatalProcessOutOfMemory();
+ static void* New(size_t size);
+ static void Delete(void* p);
+};
+
+
+// A macro is used for defining the base class used for embedded instances.
+// The reason is some compilers allocate a minimum of one word for the
+// superclass. The macro prevents the use of new & delete in debug mode.
+// In release mode we are not willing to pay this overhead.
+
+#ifdef DEBUG
+// Superclass for classes with instances allocated inside stack
+// activations or inside other objects.
+class Embedded {
+ public:
+ void* operator new(size_t size);
+ void operator delete(void* p);
+};
+#define BASE_EMBEDDED : public Embedded
+#else
+#define BASE_EMBEDDED
+#endif
+
+
+// Superclass for classes only using statics.
+class AllStatic {
+#ifdef DEBUG
+ public:
+ void* operator new(size_t size);
+ void operator delete(void* p);
+#endif
+};
+
+
+template <typename T>
+T* NewArray(size_t size) {
+ T* result = new T[size];
+ if (result == NULL) Malloced::FatalProcessOutOfMemory();
+ return result;
+}
+
+
+template <typename T>
+void DeleteArray(T* array) {
+ delete[] array;
+}
+
+
+// The normal strdup functions use malloc. These versions of StrDup
+// and StrNDup uses new and calls the FatalProcessOutOfMemory handler
+// if allocation fails.
+char* StrDup(const char* str);
+char* StrNDup(const char* str, int n);
+
+
+// Allocation policy for allocating in the C free store using malloc
+// and free. Used as the default policy for lists.
+class FreeStoreAllocationPolicy {
+ public:
+ INLINE(void* New(size_t size)) { return Malloced::New(size); }
+ INLINE(static void Delete(void* p)) { Malloced::Delete(p); }
+};
+
+
+// Allocation policy for allocating in preallocated space.
+// Used as an allocation policy for ScopeInfo when generating
+// stack traces.
+class PreallocatedStorage {
+ public:
+ explicit PreallocatedStorage(size_t size);
+ size_t size() { return size_; }
+
+ private:
+ size_t size_;
+ PreallocatedStorage* previous_;
+ PreallocatedStorage* next_;
+
+ void LinkTo(PreallocatedStorage* other);
+ void Unlink();
+
+ friend class Isolate;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PreallocatedStorage);
+};
+
+
+struct PreallocatedStorageAllocationPolicy {
+ INLINE(void* New(size_t size));
+ INLINE(static void Delete(void* ptr));
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_ALLOCATION_H_
diff --git a/chromium/v8/src/api.cc b/chromium/v8/src/api.cc
new file mode 100644
index 00000000000..eb2ffcff180
--- /dev/null
+++ b/chromium/v8/src/api.cc
@@ -0,0 +1,8182 @@
+// Copyright 2012 the V8 project authors. 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 "api.h"
+
+#include <string.h> // For memcpy, strlen.
+#include <cmath> // For isnan.
+#include "../include/v8-debug.h"
+#include "../include/v8-profiler.h"
+#include "../include/v8-testing.h"
+#include "assert-scope.h"
+#include "bootstrapper.h"
+#include "code-stubs.h"
+#include "compiler.h"
+#include "conversions-inl.h"
+#include "counters.h"
+#include "cpu-profiler.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "execution.h"
+#include "global-handles.h"
+#include "heap-profiler.h"
+#include "heap-snapshot-generator-inl.h"
+#include "icu_util.h"
+#include "json-parser.h"
+#include "messages.h"
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+#include "natives.h"
+#endif
+#include "parser.h"
+#include "platform.h"
+#include "profile-generator-inl.h"
+#include "property-details.h"
+#include "property.h"
+#include "runtime.h"
+#include "runtime-profiler.h"
+#include "scanner-character-streams.h"
+#include "snapshot.h"
+#include "unicode-inl.h"
+#include "v8threads.h"
+#include "version.h"
+#include "vm-state-inl.h"
+
+
+#define LOG_API(isolate, expr) LOG(isolate, ApiEntryCall(expr))
+
+#define ENTER_V8(isolate) \
+ ASSERT((isolate)->IsInitialized()); \
+ i::VMState<i::OTHER> __state__((isolate))
+
+namespace v8 {
+
+#define ON_BAILOUT(isolate, location, code) \
+ if (IsDeadCheck(isolate, location) || \
+ IsExecutionTerminatingCheck(isolate)) { \
+ code; \
+ UNREACHABLE(); \
+ }
+
+
+#define EXCEPTION_PREAMBLE(isolate) \
+ (isolate)->handle_scope_implementer()->IncrementCallDepth(); \
+ ASSERT(!(isolate)->external_caught_exception()); \
+ bool has_pending_exception = false
+
+
+#define EXCEPTION_BAILOUT_CHECK_GENERIC(isolate, value, do_callback) \
+ do { \
+ i::HandleScopeImplementer* handle_scope_implementer = \
+ (isolate)->handle_scope_implementer(); \
+ handle_scope_implementer->DecrementCallDepth(); \
+ if (has_pending_exception) { \
+ if (handle_scope_implementer->CallDepthIsZero() && \
+ (isolate)->is_out_of_memory()) { \
+ if (!(isolate)->ignore_out_of_memory()) \
+ i::V8::FatalProcessOutOfMemory(NULL); \
+ } \
+ 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()) { \
+ ApiCheck(isolate->thread_manager()->IsLockedByCurrentThread(), \
+ msg, \
+ "Entering the V8 API without proper locking in place"); \
+ } \
+ } while (false)
+
+
+// --- E x c e p t i o n B e h a v i o r ---
+
+
+static void DefaultFatalErrorHandler(const char* location,
+ const char* message) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (isolate->IsInitialized()) {
+ i::VMState<i::OTHER> state(isolate);
+ API_Fatal(location, message);
+ } else {
+ API_Fatal(location, message);
+ }
+}
+
+
+static FatalErrorCallback GetFatalErrorHandler() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (isolate->exception_behavior() == NULL) {
+ isolate->set_exception_behavior(DefaultFatalErrorHandler);
+ }
+ return isolate->exception_behavior();
+}
+
+
+void i::FatalProcessOutOfMemory(const char* location) {
+ i::V8::FatalProcessOutOfMemory(location, false);
+}
+
+
+// When V8 cannot allocated memory FatalProcessOutOfMemory is called.
+// The default fatal error handler is called and execution is stopped.
+void i::V8::FatalProcessOutOfMemory(const char* location, bool take_snapshot) {
+ i::HeapStats heap_stats;
+ int start_marker;
+ heap_stats.start_marker = &start_marker;
+ int new_space_size;
+ heap_stats.new_space_size = &new_space_size;
+ int new_space_capacity;
+ heap_stats.new_space_capacity = &new_space_capacity;
+ intptr_t old_pointer_space_size;
+ heap_stats.old_pointer_space_size = &old_pointer_space_size;
+ intptr_t old_pointer_space_capacity;
+ heap_stats.old_pointer_space_capacity = &old_pointer_space_capacity;
+ intptr_t old_data_space_size;
+ heap_stats.old_data_space_size = &old_data_space_size;
+ intptr_t old_data_space_capacity;
+ heap_stats.old_data_space_capacity = &old_data_space_capacity;
+ intptr_t code_space_size;
+ heap_stats.code_space_size = &code_space_size;
+ intptr_t code_space_capacity;
+ heap_stats.code_space_capacity = &code_space_capacity;
+ intptr_t map_space_size;
+ heap_stats.map_space_size = &map_space_size;
+ intptr_t map_space_capacity;
+ heap_stats.map_space_capacity = &map_space_capacity;
+ intptr_t cell_space_size;
+ heap_stats.cell_space_size = &cell_space_size;
+ intptr_t cell_space_capacity;
+ heap_stats.cell_space_capacity = &cell_space_capacity;
+ intptr_t property_cell_space_size;
+ heap_stats.property_cell_space_size = &property_cell_space_size;
+ intptr_t property_cell_space_capacity;
+ heap_stats.property_cell_space_capacity = &property_cell_space_capacity;
+ intptr_t lo_space_size;
+ heap_stats.lo_space_size = &lo_space_size;
+ int global_handle_count;
+ heap_stats.global_handle_count = &global_handle_count;
+ int weak_global_handle_count;
+ heap_stats.weak_global_handle_count = &weak_global_handle_count;
+ int pending_global_handle_count;
+ heap_stats.pending_global_handle_count = &pending_global_handle_count;
+ int near_death_global_handle_count;
+ heap_stats.near_death_global_handle_count = &near_death_global_handle_count;
+ int free_global_handle_count;
+ heap_stats.free_global_handle_count = &free_global_handle_count;
+ intptr_t memory_allocator_size;
+ heap_stats.memory_allocator_size = &memory_allocator_size;
+ intptr_t memory_allocator_capacity;
+ heap_stats.memory_allocator_capacity = &memory_allocator_capacity;
+ int objects_per_type[LAST_TYPE + 1] = {0};
+ heap_stats.objects_per_type = objects_per_type;
+ int size_per_type[LAST_TYPE + 1] = {0};
+ heap_stats.size_per_type = size_per_type;
+ int os_error;
+ heap_stats.os_error = &os_error;
+ int end_marker;
+ heap_stats.end_marker = &end_marker;
+ i::Isolate* isolate = i::Isolate::Current();
+ if (isolate->heap()->HasBeenSetUp()) {
+ // 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();
+ const char* message = "Allocation failed - process out of memory";
+ callback(location, message);
+ // If the callback returns, we stop execution.
+ UNREACHABLE();
+}
+
+
+bool Utils::ReportApiFailure(const char* location, const char* message) {
+ FatalErrorCallback callback = GetFatalErrorHandler();
+ callback(location, message);
+ i::V8::SetFatalError();
+ return false;
+}
+
+
+bool V8::IsDead() {
+ return i::V8::IsDead();
+}
+
+
+static inline bool ApiCheck(bool condition,
+ const char* location,
+ const char* message) {
+ return condition ? true : Utils::ReportApiFailure(location, message);
+}
+
+
+static bool ReportV8Dead(const char* location) {
+ FatalErrorCallback callback = GetFatalErrorHandler();
+ callback(location, "V8 is no longer usable");
+ return true;
+}
+
+
+static bool ReportEmptyHandle(const char* location) {
+ FatalErrorCallback callback = GetFatalErrorHandler();
+ callback(location, "Reading from empty handle");
+ return true;
+}
+
+
+/**
+ * IsDeadCheck checks that the vm is usable. If, for instance, the vm has been
+ * out of memory at some point this check will fail. It should be called on
+ * entry to all methods that touch anything in the heap, except destructors
+ * which you sometimes can't avoid calling after the vm has crashed. Functions
+ * that call EnsureInitialized or ON_BAILOUT don't have to also call
+ * IsDeadCheck. ON_BAILOUT has the advantage over EnsureInitialized that you
+ * can arrange to return if the VM is dead. This is needed to ensure that no VM
+ * heap allocations are attempted on a dead VM. EnsureInitialized has the
+ * advantage over ON_BAILOUT that it actually initializes the VM if this has not
+ * yet been done.
+ */
+static inline bool IsDeadCheck(i::Isolate* isolate, const char* location) {
+ return !isolate->IsInitialized()
+ && i::V8::IsDead() ? ReportV8Dead(location) : false;
+}
+
+
+static inline bool IsExecutionTerminatingCheck(i::Isolate* isolate) {
+ if (!isolate->IsInitialized()) return false;
+ if (isolate->has_scheduled_exception()) {
+ return isolate->scheduled_exception() ==
+ isolate->heap()->termination_exception();
+ }
+ return false;
+}
+
+
+static inline bool EmptyCheck(const char* location, v8::Handle<v8::Data> obj) {
+ return obj.IsEmpty() ? ReportEmptyHandle(location) : false;
+}
+
+
+static inline bool EmptyCheck(const char* location, const v8::Data* obj) {
+ return (obj == 0) ? ReportEmptyHandle(location) : false;
+}
+
+
+// --- S t a t i c s ---
+
+
+static bool InitializeHelper(i::Isolate* isolate) {
+ // If the isolate has a function entry hook, it needs to re-build all its
+ // code stubs with entry hooks embedded, so let's deserialize a snapshot.
+ if (isolate == NULL || isolate->function_entry_hook() == NULL) {
+ if (i::Snapshot::Initialize())
+ return true;
+ }
+ return i::V8::Initialize(NULL);
+}
+
+
+static inline bool EnsureInitializedForIsolate(i::Isolate* isolate,
+ const char* location) {
+ if (IsDeadCheck(isolate, location)) return false;
+ if (isolate != NULL) {
+ if (isolate->IsInitialized()) return true;
+ }
+ ASSERT(isolate == i::Isolate::Current());
+ return ApiCheck(InitializeHelper(isolate), location, "Error initializing V8");
+}
+
+
+// Some initializing API functions are called early and may be
+// called on a thread different from static initializer thread.
+// If Isolate API is used, Isolate::Enter() will initialize TLS so
+// Isolate::Current() works. If it's a legacy case, then the thread
+// may not have TLS initialized yet. However, in initializing APIs it
+// may be too early to call EnsureInitialized() - some pre-init
+// parameters still have to be configured.
+static inline i::Isolate* EnterIsolateIfNeeded() {
+ i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+ if (isolate != NULL)
+ return isolate;
+
+ i::Isolate::EnterDefaultIsolate();
+ isolate = i::Isolate::Current();
+ return isolate;
+}
+
+
+StartupDataDecompressor::StartupDataDecompressor()
+ : raw_data(i::NewArray<char*>(V8::GetCompressedStartupDataCount())) {
+ for (int i = 0; i < V8::GetCompressedStartupDataCount(); ++i) {
+ raw_data[i] = NULL;
+ }
+}
+
+
+StartupDataDecompressor::~StartupDataDecompressor() {
+ for (int i = 0; i < V8::GetCompressedStartupDataCount(); ++i) {
+ i::DeleteArray(raw_data[i]);
+ }
+ i::DeleteArray(raw_data);
+}
+
+
+int StartupDataDecompressor::Decompress() {
+ int compressed_data_count = V8::GetCompressedStartupDataCount();
+ StartupData* compressed_data =
+ i::NewArray<StartupData>(compressed_data_count);
+ V8::GetCompressedStartupData(compressed_data);
+ for (int i = 0; i < compressed_data_count; ++i) {
+ char* decompressed = raw_data[i] =
+ i::NewArray<char>(compressed_data[i].raw_size);
+ if (compressed_data[i].compressed_size != 0) {
+ int result = DecompressData(decompressed,
+ &compressed_data[i].raw_size,
+ compressed_data[i].data,
+ compressed_data[i].compressed_size);
+ if (result != 0) return result;
+ } else {
+ ASSERT_EQ(0, compressed_data[i].raw_size);
+ }
+ compressed_data[i].data = decompressed;
+ }
+ V8::SetDecompressedStartupData(compressed_data);
+ i::DeleteArray(compressed_data);
+ return 0;
+}
+
+
+StartupData::CompressionAlgorithm V8::GetCompressedStartupDataAlgorithm() {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ return StartupData::kBZip2;
+#else
+ return StartupData::kUncompressed;
+#endif
+}
+
+
+enum CompressedStartupDataItems {
+ kSnapshot = 0,
+ kSnapshotContext,
+ kLibraries,
+ kExperimentalLibraries,
+#if defined(V8_I18N_SUPPORT)
+ kI18NExtension,
+#endif
+ kCompressedStartupDataCount
+};
+
+
+int V8::GetCompressedStartupDataCount() {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ return kCompressedStartupDataCount;
+#else
+ return 0;
+#endif
+}
+
+
+void V8::GetCompressedStartupData(StartupData* compressed_data) {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ compressed_data[kSnapshot].data =
+ reinterpret_cast<const char*>(i::Snapshot::data());
+ compressed_data[kSnapshot].compressed_size = i::Snapshot::size();
+ compressed_data[kSnapshot].raw_size = i::Snapshot::raw_size();
+
+ compressed_data[kSnapshotContext].data =
+ reinterpret_cast<const char*>(i::Snapshot::context_data());
+ compressed_data[kSnapshotContext].compressed_size =
+ i::Snapshot::context_size();
+ compressed_data[kSnapshotContext].raw_size = i::Snapshot::context_raw_size();
+
+ i::Vector<const i::byte> libraries_source = i::Natives::GetScriptsSource();
+ compressed_data[kLibraries].data =
+ reinterpret_cast<const char*>(libraries_source.start());
+ compressed_data[kLibraries].compressed_size = libraries_source.length();
+ compressed_data[kLibraries].raw_size = i::Natives::GetRawScriptsSize();
+
+ i::Vector<const i::byte> exp_libraries_source =
+ i::ExperimentalNatives::GetScriptsSource();
+ compressed_data[kExperimentalLibraries].data =
+ reinterpret_cast<const char*>(exp_libraries_source.start());
+ compressed_data[kExperimentalLibraries].compressed_size =
+ exp_libraries_source.length();
+ compressed_data[kExperimentalLibraries].raw_size =
+ i::ExperimentalNatives::GetRawScriptsSize();
+
+#if defined(V8_I18N_SUPPORT)
+ i::Vector<const ii:byte> i18n_extension_source =
+ i::I18NNatives::GetScriptsSource();
+ compressed_data[kI18NExtension].data =
+ reinterpret_cast<const char*>(i18n_extension_source.start());
+ compressed_data[kI18NExtension].compressed_size =
+ i18n_extension_source.length();
+ compressed_data[kI18NExtension].raw_size =
+ i::I18NNatives::GetRawScriptsSize();
+#endif
+#endif
+}
+
+
+void V8::SetDecompressedStartupData(StartupData* decompressed_data) {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ ASSERT_EQ(i::Snapshot::raw_size(), decompressed_data[kSnapshot].raw_size);
+ i::Snapshot::set_raw_data(
+ reinterpret_cast<const i::byte*>(decompressed_data[kSnapshot].data));
+
+ ASSERT_EQ(i::Snapshot::context_raw_size(),
+ decompressed_data[kSnapshotContext].raw_size);
+ i::Snapshot::set_context_raw_data(
+ reinterpret_cast<const i::byte*>(
+ decompressed_data[kSnapshotContext].data));
+
+ ASSERT_EQ(i::Natives::GetRawScriptsSize(),
+ decompressed_data[kLibraries].raw_size);
+ i::Vector<const char> libraries_source(
+ decompressed_data[kLibraries].data,
+ decompressed_data[kLibraries].raw_size);
+ i::Natives::SetRawScriptsSource(libraries_source);
+
+ ASSERT_EQ(i::ExperimentalNatives::GetRawScriptsSize(),
+ decompressed_data[kExperimentalLibraries].raw_size);
+ i::Vector<const char> exp_libraries_source(
+ decompressed_data[kExperimentalLibraries].data,
+ decompressed_data[kExperimentalLibraries].raw_size);
+ i::ExperimentalNatives::SetRawScriptsSource(exp_libraries_source);
+
+#if defined(V8_I18N_SUPPORT)
+ ASSERT_EQ(i::I18NNatives::GetRawScriptsSize(),
+ decompressed_data[kI18NExtension].raw_size);
+ i::Vector<const char> i18n_extension_source(
+ decompressed_data[kI18NExtension].data,
+ decompressed_data[kI18NExtension].raw_size);
+ i::I18NNatives::SetRawScriptsSource(i18n_extension_source);
+#endif
+#endif
+}
+
+
+void V8::SetFatalErrorHandler(FatalErrorCallback that) {
+ i::Isolate* isolate = EnterIsolateIfNeeded();
+ isolate->set_exception_behavior(that);
+}
+
+
+void V8::SetAllowCodeGenerationFromStringsCallback(
+ AllowCodeGenerationFromStringsCallback callback) {
+ i::Isolate* isolate = EnterIsolateIfNeeded();
+ isolate->set_allow_code_gen_callback(callback);
+}
+
+
+void V8::SetFlagsFromString(const char* str, int length) {
+ i::FlagList::SetFlagsFromString(str, length);
+}
+
+
+void V8::SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags) {
+ i::FlagList::SetFlagsFromCommandLine(argc, argv, remove_flags);
+}
+
+
+v8::Handle<Value> ThrowException(v8::Handle<v8::Value> value) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::ThrowException()")) {
+ return v8::Handle<Value>();
+ }
+ ENTER_V8(isolate);
+ // If we're passed an empty handle, we throw an undefined exception
+ // to deal more gracefully with out of memory situations.
+ if (value.IsEmpty()) {
+ isolate->ScheduleThrow(isolate->heap()->undefined_value());
+ } else {
+ isolate->ScheduleThrow(*Utils::OpenHandle(*value));
+ }
+ return v8::Undefined();
+}
+
+
+RegisteredExtension* RegisteredExtension::first_extension_ = NULL;
+
+
+RegisteredExtension::RegisteredExtension(Extension* extension)
+ : extension_(extension) { }
+
+
+void RegisteredExtension::Register(RegisteredExtension* that) {
+ that->next_ = first_extension_;
+ first_extension_ = that;
+}
+
+
+void RegisteredExtension::UnregisterAll() {
+ RegisteredExtension* re = first_extension_;
+ while (re != NULL) {
+ RegisteredExtension* next = re->next();
+ delete re;
+ re = next;
+ }
+}
+
+
+void RegisterExtension(Extension* that) {
+ RegisteredExtension* extension = new RegisteredExtension(that);
+ RegisteredExtension::Register(extension);
+}
+
+
+Extension::Extension(const char* name,
+ const char* source,
+ int dep_count,
+ const char** deps,
+ int source_length)
+ : name_(name),
+ source_length_(source_length >= 0 ?
+ source_length :
+ (source ? static_cast<int>(strlen(source)) : 0)),
+ source_(source, source_length_),
+ dep_count_(dep_count),
+ deps_(deps),
+ auto_enable_(false) {
+ CHECK(source != NULL || source_length_ == 0);
+}
+
+
+v8::Handle<Primitive> Undefined() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!EnsureInitializedForIsolate(isolate, "v8::Undefined()")) {
+ return v8::Handle<v8::Primitive>();
+ }
+ return ToApiHandle<Primitive>(isolate->factory()->undefined_value());
+}
+
+
+v8::Handle<Primitive> Null() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!EnsureInitializedForIsolate(isolate, "v8::Null()")) {
+ return v8::Handle<v8::Primitive>();
+ }
+ return ToApiHandle<Primitive>(isolate->factory()->null_value());
+}
+
+
+v8::Handle<Boolean> True() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!EnsureInitializedForIsolate(isolate, "v8::True()")) {
+ return v8::Handle<Boolean>();
+ }
+ return ToApiHandle<Boolean>(isolate->factory()->true_value());
+}
+
+
+v8::Handle<Boolean> False() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!EnsureInitializedForIsolate(isolate, "v8::False()")) {
+ return v8::Handle<Boolean>();
+ }
+ return ToApiHandle<Boolean>(isolate->factory()->false_value());
+}
+
+
+ResourceConstraints::ResourceConstraints()
+ : max_young_space_size_(0),
+ max_old_space_size_(0),
+ max_executable_size_(0),
+ stack_limit_(NULL) { }
+
+
+bool SetResourceConstraints(ResourceConstraints* constraints) {
+ i::Isolate* isolate = EnterIsolateIfNeeded();
+
+ int young_space_size = constraints->max_young_space_size();
+ int old_gen_size = constraints->max_old_space_size();
+ int max_executable_size = constraints->max_executable_size();
+ if (young_space_size != 0 || old_gen_size != 0 || max_executable_size != 0) {
+ // After initialization it's too late to change Heap constraints.
+ ASSERT(!isolate->IsInitialized());
+ bool result = isolate->heap()->ConfigureHeap(young_space_size / 2,
+ old_gen_size,
+ max_executable_size);
+ if (!result) return false;
+ }
+ if (constraints->stack_limit() != NULL) {
+ uintptr_t limit = reinterpret_cast<uintptr_t>(constraints->stack_limit());
+ isolate->stack_guard()->SetStackLimit(limit);
+ }
+ return true;
+}
+
+
+i::Object** V8::GlobalizeReference(i::Isolate* isolate, i::Object** obj) {
+ if (IsDeadCheck(isolate, "V8::Persistent::New")) return NULL;
+ LOG_API(isolate, "Persistent::New");
+ i::Handle<i::Object> result = isolate->global_handles()->Create(*obj);
+#ifdef DEBUG
+ (*obj)->Verify();
+#endif // DEBUG
+ return result.location();
+}
+
+
+void V8::MakeWeak(i::Object** object,
+ void* parameters,
+ RevivableCallback weak_reference_callback) {
+ i::GlobalHandles::MakeWeak(object,
+ parameters,
+ weak_reference_callback);
+}
+
+
+void V8::ClearWeak(i::Object** obj) {
+ i::GlobalHandles::ClearWeakness(obj);
+}
+
+
+void V8::DisposeGlobal(i::Object** obj) {
+ i::GlobalHandles::Destroy(obj);
+}
+
+
+int V8::Eternalize(i::Isolate* isolate, i::Object** handle) {
+ return isolate->eternal_handles()->Create(isolate, *handle);
+}
+
+
+i::Object** V8::GetEternal(i::Isolate* isolate, int index) {
+ return isolate->eternal_handles()->Get(index).location();
+}
+
+
+// --- H a n d l e s ---
+
+
+HandleScope::HandleScope() {
+ Initialize(reinterpret_cast<Isolate*>(i::Isolate::Current()));
+}
+
+
+HandleScope::HandleScope(Isolate* isolate) {
+ Initialize(isolate);
+}
+
+
+void HandleScope::Initialize(Isolate* isolate) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ API_ENTRY_CHECK(internal_isolate, "HandleScope::HandleScope");
+ v8::ImplementationUtilities::HandleScopeData* current =
+ internal_isolate->handle_scope_data();
+ isolate_ = internal_isolate;
+ prev_next_ = current->next;
+ prev_limit_ = current->limit;
+ is_closed_ = false;
+ current->level++;
+}
+
+
+HandleScope::~HandleScope() {
+ if (!is_closed_) {
+ Leave();
+ }
+}
+
+
+void HandleScope::Leave() {
+ return i::HandleScope::CloseScope(isolate_, prev_next_, prev_limit_);
+}
+
+
+int HandleScope::NumberOfHandles() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!EnsureInitializedForIsolate(isolate, "HandleScope::NumberOfHandles")) {
+ return 0;
+ }
+ return i::HandleScope::NumberOfHandles(isolate);
+}
+
+
+i::Object** HandleScope::CreateHandle(i::Object* value) {
+ return i::HandleScope::CreateHandle(i::Isolate::Current(), value);
+}
+
+
+i::Object** HandleScope::CreateHandle(i::Isolate* isolate, i::Object* value) {
+ ASSERT(isolate == i::Isolate::Current());
+ return i::HandleScope::CreateHandle(isolate, value);
+}
+
+
+i::Object** HandleScope::CreateHandle(i::HeapObject* value) {
+ ASSERT(value->IsHeapObject());
+ return reinterpret_cast<i::Object**>(
+ i::HandleScope::CreateHandle(value->GetIsolate(), value));
+}
+
+
+void Context::Enter() {
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ i::Isolate* isolate = env->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Context::Enter()")) return;
+ ENTER_V8(isolate);
+
+ isolate->handle_scope_implementer()->EnterContext(env);
+
+ isolate->handle_scope_implementer()->SaveContext(isolate->context());
+ isolate->set_context(*env);
+}
+
+
+void Context::Exit() {
+ // Exit is essentially a static function and doesn't use the
+ // receiver, so we have to get the current isolate from the thread
+ // local.
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!isolate->IsInitialized()) return;
+
+ if (!ApiCheck(isolate->handle_scope_implementer()->LeaveLastContext(),
+ "v8::Context::Exit()",
+ "Cannot exit non-entered context")) {
+ return;
+ }
+
+ // Content of 'last_context' could be NULL.
+ i::Context* last_context =
+ isolate->handle_scope_implementer()->RestoreContext();
+ isolate->set_context(last_context);
+}
+
+
+static void* DecodeSmiToAligned(i::Object* value, const char* location) {
+ ApiCheck(value->IsSmi(), location, "Not a Smi");
+ return reinterpret_cast<void*>(value);
+}
+
+
+static i::Smi* EncodeAlignedAsSmi(void* value, const char* location) {
+ i::Smi* smi = reinterpret_cast<i::Smi*>(value);
+ ApiCheck(smi->IsSmi(), location, "Pointer is not aligned");
+ return smi;
+}
+
+
+static i::Handle<i::FixedArray> EmbedderDataFor(Context* context,
+ int index,
+ bool can_grow,
+ const char* location) {
+ i::Handle<i::Context> env = Utils::OpenHandle(context);
+ bool ok = !IsDeadCheck(env->GetIsolate(), location) &&
+ ApiCheck(env->IsNativeContext(), location, "Not a native context") &&
+ ApiCheck(index >= 0, location, "Negative index");
+ if (!ok) return i::Handle<i::FixedArray>();
+ i::Handle<i::FixedArray> data(env->embedder_data());
+ if (index < data->length()) return data;
+ if (!can_grow) {
+ Utils::ReportApiFailure(location, "Index too large");
+ return i::Handle<i::FixedArray>();
+ }
+ int new_size = i::Max(index, data->length() << 1) + 1;
+ data = env->GetIsolate()->factory()->CopySizeFixedArray(data, new_size);
+ env->set_embedder_data(*data);
+ return data;
+}
+
+
+v8::Local<v8::Value> Context::SlowGetEmbedderData(int index) {
+ const char* location = "v8::Context::GetEmbedderData()";
+ i::Handle<i::FixedArray> data = EmbedderDataFor(this, index, false, location);
+ if (data.is_null()) return Local<Value>();
+ i::Handle<i::Object> result(data->get(index), data->GetIsolate());
+ return Utils::ToLocal(result);
+}
+
+
+void Context::SetEmbedderData(int index, v8::Handle<Value> value) {
+ const char* location = "v8::Context::SetEmbedderData()";
+ i::Handle<i::FixedArray> data = EmbedderDataFor(this, index, true, location);
+ if (data.is_null()) return;
+ i::Handle<i::Object> val = Utils::OpenHandle(*value);
+ data->set(index, *val);
+ ASSERT_EQ(*Utils::OpenHandle(*value),
+ *Utils::OpenHandle(*GetEmbedderData(index)));
+}
+
+
+void* Context::SlowGetAlignedPointerFromEmbedderData(int index) {
+ const char* location = "v8::Context::GetAlignedPointerFromEmbedderData()";
+ i::Handle<i::FixedArray> data = EmbedderDataFor(this, index, false, location);
+ if (data.is_null()) return NULL;
+ return DecodeSmiToAligned(data->get(index), location);
+}
+
+
+void Context::SetAlignedPointerInEmbedderData(int index, void* value) {
+ const char* location = "v8::Context::SetAlignedPointerInEmbedderData()";
+ i::Handle<i::FixedArray> data = EmbedderDataFor(this, index, true, location);
+ data->set(index, EncodeAlignedAsSmi(value, location));
+ ASSERT_EQ(value, GetAlignedPointerFromEmbedderData(index));
+}
+
+
+i::Object** v8::HandleScope::RawClose(i::Object** value) {
+ if (!ApiCheck(!is_closed_,
+ "v8::HandleScope::Close()",
+ "Local scope has already been closed")) {
+ return 0;
+ }
+ LOG_API(isolate_, "CloseHandleScope");
+
+ // Read the result before popping the handle block.
+ i::Object* result = NULL;
+ if (value != NULL) {
+ result = *value;
+ }
+ is_closed_ = true;
+ Leave();
+
+ if (value == NULL) {
+ return NULL;
+ }
+
+ // Allocate a new handle on the previous handle block.
+ i::Handle<i::Object> handle(result, isolate_);
+ return handle.location();
+}
+
+
+// --- N e a n d e r ---
+
+
+// A constructor cannot easily return an error value, therefore it is necessary
+// to check for a dead VM with ON_BAILOUT before constructing any Neander
+// objects. To remind you about this there is no HandleScope in the
+// NeanderObject constructor. When you add one to the site calling the
+// constructor you should check that you ensured the VM was not dead first.
+NeanderObject::NeanderObject(int size) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Nowhere");
+ ENTER_V8(isolate);
+ value_ = isolate->factory()->NewNeanderObject();
+ i::Handle<i::FixedArray> elements = isolate->factory()->NewFixedArray(size);
+ value_->set_elements(*elements);
+}
+
+
+int NeanderObject::size() {
+ return i::FixedArray::cast(value_->elements())->length();
+}
+
+
+NeanderArray::NeanderArray() : obj_(2) {
+ obj_.set(0, i::Smi::FromInt(0));
+}
+
+
+int NeanderArray::length() {
+ return i::Smi::cast(obj_.get(0))->value();
+}
+
+
+i::Object* NeanderArray::get(int offset) {
+ ASSERT(0 <= offset);
+ ASSERT(offset < length());
+ return obj_.get(offset + 1);
+}
+
+
+// This method cannot easily return an error value, therefore it is necessary
+// to check for a dead VM with ON_BAILOUT before calling it. To remind you
+// about this there is no HandleScope in this method. When you add one to the
+// site calling this method you should check that you ensured the VM was not
+// dead first.
+void NeanderArray::add(i::Handle<i::Object> value) {
+ int length = this->length();
+ int size = obj_.size();
+ if (length == size - 1) {
+ i::Factory* factory = i::Isolate::Current()->factory();
+ i::Handle<i::FixedArray> new_elms = factory->NewFixedArray(2 * size);
+ for (int i = 0; i < length; i++)
+ new_elms->set(i + 1, get(i));
+ obj_.value()->set_elements(*new_elms);
+ }
+ obj_.set(length + 1, *value);
+ obj_.set(0, i::Smi::FromInt(length + 1));
+}
+
+
+void NeanderArray::set(int index, i::Object* value) {
+ if (index < 0 || index >= this->length()) return;
+ obj_.set(index + 1, value);
+}
+
+
+// --- T e m p l a t e ---
+
+
+static void InitializeTemplate(i::Handle<i::TemplateInfo> that, int type) {
+ that->set_tag(i::Smi::FromInt(type));
+}
+
+
+void Template::Set(v8::Handle<String> name, v8::Handle<Data> value,
+ v8::PropertyAttribute attribute) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Template::Set()")) return;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::Object> list(Utils::OpenHandle(this)->property_list(), isolate);
+ if (list->IsUndefined()) {
+ list = NeanderArray().value();
+ Utils::OpenHandle(this)->set_property_list(*list);
+ }
+ NeanderArray array(list);
+ array.add(Utils::OpenHandle(*name));
+ array.add(Utils::OpenHandle(*value));
+ array.add(Utils::OpenHandle(*v8::Integer::New(attribute)));
+}
+
+
+// --- F u n c t i o n T e m p l a t e ---
+static void InitializeFunctionTemplate(
+ i::Handle<i::FunctionTemplateInfo> info) {
+ info->set_tag(i::Smi::FromInt(Consts::FUNCTION_TEMPLATE));
+ info->set_flag(0);
+}
+
+
+Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::FunctionTemplate::PrototypeTemplate()")) {
+ return Local<ObjectTemplate>();
+ }
+ ENTER_V8(isolate);
+ i::Handle<i::Object> result(Utils::OpenHandle(this)->prototype_template(),
+ isolate);
+ if (result->IsUndefined()) {
+ result = Utils::OpenHandle(*ObjectTemplate::New());
+ Utils::OpenHandle(this)->set_prototype_template(*result);
+ }
+ return ToApiHandle<ObjectTemplate>(result);
+}
+
+
+void FunctionTemplate::Inherit(v8::Handle<FunctionTemplate> value) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::FunctionTemplate::Inherit()")) return;
+ ENTER_V8(isolate);
+ Utils::OpenHandle(this)->set_parent_template(*Utils::OpenHandle(*value));
+}
+
+
+// TODO(dcarney): Remove this abstraction when old callbacks are removed.
+class CallHandlerHelper {
+ public:
+ static inline void Set(Local<FunctionTemplate> function_template,
+ InvocationCallback callback,
+ v8::Handle<Value> data) {
+ function_template->SetCallHandlerInternal(callback, data);
+ }
+ static inline void Set(Local<FunctionTemplate> function_template,
+ FunctionCallback callback,
+ v8::Handle<Value> data) {
+ function_template->SetCallHandler(callback, data);
+ }
+};
+
+
+template<typename Callback>
+static Local<FunctionTemplate> FunctionTemplateNew(
+ Callback callback,
+ v8::Handle<Value> data,
+ v8::Handle<Signature> signature,
+ int length) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::FunctionTemplate::New()");
+ LOG_API(isolate, "FunctionTemplate::New");
+ ENTER_V8(isolate);
+ i::Handle<i::Struct> struct_obj =
+ isolate->factory()->NewStruct(i::FUNCTION_TEMPLATE_INFO_TYPE);
+ i::Handle<i::FunctionTemplateInfo> obj =
+ i::Handle<i::FunctionTemplateInfo>::cast(struct_obj);
+ InitializeFunctionTemplate(obj);
+ int next_serial_number = isolate->next_serial_number();
+ isolate->set_next_serial_number(next_serial_number + 1);
+ obj->set_serial_number(i::Smi::FromInt(next_serial_number));
+ if (callback != 0) {
+ if (data.IsEmpty()) data = v8::Undefined();
+ CallHandlerHelper::Set(Utils::ToLocal(obj), callback, data);
+ }
+ obj->set_length(length);
+ obj->set_undetectable(false);
+ obj->set_needs_access_check(false);
+
+ if (!signature.IsEmpty())
+ obj->set_signature(*Utils::OpenHandle(*signature));
+ return Utils::ToLocal(obj);
+}
+
+
+Local<FunctionTemplate> FunctionTemplate::New(
+ InvocationCallback callback,
+ v8::Handle<Value> data,
+ v8::Handle<Signature> signature,
+ int length) {
+ return FunctionTemplateNew(callback, data, signature, length);
+}
+
+
+Local<FunctionTemplate> FunctionTemplate::New(
+ FunctionCallback callback,
+ v8::Handle<Value> data,
+ v8::Handle<Signature> signature,
+ int length) {
+ return FunctionTemplateNew(callback, data, signature, length);
+}
+
+
+Local<Signature> Signature::New(Handle<FunctionTemplate> receiver,
+ int argc, Handle<FunctionTemplate> argv[]) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Signature::New()");
+ LOG_API(isolate, "Signature::New");
+ ENTER_V8(isolate);
+ i::Handle<i::Struct> struct_obj =
+ isolate->factory()->NewStruct(i::SIGNATURE_INFO_TYPE);
+ i::Handle<i::SignatureInfo> obj =
+ i::Handle<i::SignatureInfo>::cast(struct_obj);
+ if (!receiver.IsEmpty()) obj->set_receiver(*Utils::OpenHandle(*receiver));
+ if (argc > 0) {
+ i::Handle<i::FixedArray> args = isolate->factory()->NewFixedArray(argc);
+ for (int i = 0; i < argc; i++) {
+ if (!argv[i].IsEmpty())
+ args->set(i, *Utils::OpenHandle(*argv[i]));
+ }
+ obj->set_args(*args);
+ }
+ return Utils::ToLocal(obj);
+}
+
+
+Local<AccessorSignature> AccessorSignature::New(
+ Handle<FunctionTemplate> receiver) {
+ return Utils::AccessorSignatureToLocal(Utils::OpenHandle(*receiver));
+}
+
+
+template<typename Operation>
+static Local<Operation> NewDescriptor(
+ Isolate* isolate,
+ const i::DeclaredAccessorDescriptorData& data,
+ Data* previous_descriptor) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ i::Handle<i::DeclaredAccessorDescriptor> previous =
+ i::Handle<i::DeclaredAccessorDescriptor>();
+ if (previous_descriptor != NULL) {
+ previous = Utils::OpenHandle(
+ static_cast<DeclaredAccessorDescriptor*>(previous_descriptor));
+ }
+ i::Handle<i::DeclaredAccessorDescriptor> descriptor =
+ i::DeclaredAccessorDescriptor::Create(internal_isolate, data, previous);
+ return Utils::Convert<i::DeclaredAccessorDescriptor, Operation>(descriptor);
+}
+
+
+Local<RawOperationDescriptor>
+ ObjectOperationDescriptor::NewInternalFieldDereference(
+ Isolate* isolate,
+ int internal_field) {
+ i::DeclaredAccessorDescriptorData data;
+ data.type = i::kDescriptorObjectDereference;
+ data.object_dereference_descriptor.internal_field = internal_field;
+ return NewDescriptor<RawOperationDescriptor>(isolate, data, NULL);
+}
+
+
+Local<RawOperationDescriptor> RawOperationDescriptor::NewRawShift(
+ Isolate* isolate,
+ int16_t byte_offset) {
+ i::DeclaredAccessorDescriptorData data;
+ data.type = i::kDescriptorPointerShift;
+ data.pointer_shift_descriptor.byte_offset = byte_offset;
+ return NewDescriptor<RawOperationDescriptor>(isolate, data, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewHandleDereference(
+ Isolate* isolate) {
+ i::DeclaredAccessorDescriptorData data;
+ data.type = i::kDescriptorReturnObject;
+ return NewDescriptor<DeclaredAccessorDescriptor>(isolate, data, this);
+}
+
+
+Local<RawOperationDescriptor> RawOperationDescriptor::NewRawDereference(
+ Isolate* isolate) {
+ i::DeclaredAccessorDescriptorData data;
+ data.type = i::kDescriptorPointerDereference;
+ return NewDescriptor<RawOperationDescriptor>(isolate, data, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewPointerCompare(
+ Isolate* isolate,
+ void* compare_value) {
+ i::DeclaredAccessorDescriptorData data;
+ data.type = i::kDescriptorPointerCompare;
+ data.pointer_compare_descriptor.compare_value = compare_value;
+ return NewDescriptor<DeclaredAccessorDescriptor>(isolate, data, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewPrimitiveValue(
+ Isolate* isolate,
+ DeclaredAccessorDescriptorDataType data_type,
+ uint8_t bool_offset) {
+ i::DeclaredAccessorDescriptorData data;
+ data.type = i::kDescriptorPrimitiveValue;
+ data.primitive_value_descriptor.data_type = data_type;
+ data.primitive_value_descriptor.bool_offset = bool_offset;
+ return NewDescriptor<DeclaredAccessorDescriptor>(isolate, data, this);
+}
+
+
+template<typename T>
+static Local<DeclaredAccessorDescriptor> NewBitmaskCompare(
+ Isolate* isolate,
+ T bitmask,
+ T compare_value,
+ RawOperationDescriptor* operation) {
+ i::DeclaredAccessorDescriptorData data;
+ data.type = i::kDescriptorBitmaskCompare;
+ data.bitmask_compare_descriptor.bitmask = bitmask;
+ data.bitmask_compare_descriptor.compare_value = compare_value;
+ data.bitmask_compare_descriptor.size = sizeof(T);
+ return NewDescriptor<DeclaredAccessorDescriptor>(isolate, data, operation);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewBitmaskCompare8(
+ Isolate* isolate,
+ uint8_t bitmask,
+ uint8_t compare_value) {
+ return NewBitmaskCompare(isolate, bitmask, compare_value, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewBitmaskCompare16(
+ Isolate* isolate,
+ uint16_t bitmask,
+ uint16_t compare_value) {
+ return NewBitmaskCompare(isolate, bitmask, compare_value, this);
+}
+
+
+Local<DeclaredAccessorDescriptor> RawOperationDescriptor::NewBitmaskCompare32(
+ Isolate* isolate,
+ uint32_t bitmask,
+ uint32_t compare_value) {
+ return NewBitmaskCompare(isolate, bitmask, compare_value, this);
+}
+
+
+Local<TypeSwitch> TypeSwitch::New(Handle<FunctionTemplate> type) {
+ Handle<FunctionTemplate> types[1] = { type };
+ return TypeSwitch::New(1, types);
+}
+
+
+Local<TypeSwitch> TypeSwitch::New(int argc, Handle<FunctionTemplate> types[]) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::TypeSwitch::New()");
+ LOG_API(isolate, "TypeSwitch::New");
+ ENTER_V8(isolate);
+ i::Handle<i::FixedArray> vector = isolate->factory()->NewFixedArray(argc);
+ for (int i = 0; i < argc; i++)
+ vector->set(i, *Utils::OpenHandle(*types[i]));
+ i::Handle<i::Struct> struct_obj =
+ isolate->factory()->NewStruct(i::TYPE_SWITCH_INFO_TYPE);
+ i::Handle<i::TypeSwitchInfo> obj =
+ i::Handle<i::TypeSwitchInfo>::cast(struct_obj);
+ obj->set_types(*vector);
+ return Utils::ToLocal(obj);
+}
+
+
+int TypeSwitch::match(v8::Handle<Value> value) {
+ i::Isolate* isolate = i::Isolate::Current();
+ LOG_API(isolate, "TypeSwitch::match");
+ USE(isolate);
+ i::Handle<i::Object> obj = Utils::OpenHandle(*value);
+ i::Handle<i::TypeSwitchInfo> info = Utils::OpenHandle(this);
+ i::FixedArray* types = i::FixedArray::cast(info->types());
+ for (int i = 0; i < types->length(); i++) {
+ if (obj->IsInstanceOf(i::FunctionTemplateInfo::cast(types->get(i))))
+ return i + 1;
+ }
+ return 0;
+}
+
+
+#define SET_FIELD_WRAPPED(obj, setter, cdata) do { \
+ i::Handle<i::Object> foreign = FromCData(cdata); \
+ (obj)->setter(*foreign); \
+ } while (false)
+
+
+template<typename Callback>
+static void FunctionTemplateSetCallHandler(FunctionTemplate* function_template,
+ Callback callback_in,
+ v8::Handle<Value> data) {
+ i::Isolate* isolate = Utils::OpenHandle(function_template)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::FunctionTemplate::SetCallHandler()")) return;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::Struct> struct_obj =
+ isolate->factory()->NewStruct(i::CALL_HANDLER_INFO_TYPE);
+ i::Handle<i::CallHandlerInfo> obj =
+ i::Handle<i::CallHandlerInfo>::cast(struct_obj);
+ FunctionCallback callback =
+ i::CallbackTable::Register(isolate, callback_in);
+ SET_FIELD_WRAPPED(obj, set_callback, callback);
+ if (data.IsEmpty()) data = v8::Undefined();
+ obj->set_data(*Utils::OpenHandle(*data));
+ Utils::OpenHandle(function_template)->set_call_code(*obj);
+}
+
+void FunctionTemplate::SetCallHandler(InvocationCallback callback,
+ v8::Handle<Value> data) {
+ FunctionTemplateSetCallHandler(this, callback, data);
+}
+
+void FunctionTemplate::SetCallHandlerInternal(InvocationCallback callback,
+ v8::Handle<Value> data) {
+ FunctionTemplateSetCallHandler(this, callback, data);
+}
+
+void FunctionTemplate::SetCallHandler(FunctionCallback callback,
+ v8::Handle<Value> data) {
+ FunctionTemplateSetCallHandler(this, callback, data);
+}
+
+static i::Handle<i::AccessorInfo> SetAccessorInfoProperties(
+ i::Handle<i::AccessorInfo> obj,
+ v8::Handle<String> name,
+ v8::AccessControl settings,
+ v8::PropertyAttribute attributes,
+ v8::Handle<AccessorSignature> signature) {
+ obj->set_name(*Utils::OpenHandle(*name));
+ if (settings & ALL_CAN_READ) obj->set_all_can_read(true);
+ if (settings & ALL_CAN_WRITE) obj->set_all_can_write(true);
+ if (settings & PROHIBITS_OVERWRITING) obj->set_prohibits_overwriting(true);
+ obj->set_property_attributes(static_cast<PropertyAttributes>(attributes));
+ if (!signature.IsEmpty()) {
+ obj->set_expected_receiver_type(*Utils::OpenHandle(*signature));
+ }
+ return obj;
+}
+
+
+template<typename Getter, typename Setter>
+static i::Handle<i::AccessorInfo> MakeAccessorInfo(
+ v8::Handle<String> name,
+ Getter getter_in,
+ Setter setter_in,
+ v8::Handle<Value> data,
+ v8::AccessControl settings,
+ v8::PropertyAttribute attributes,
+ v8::Handle<AccessorSignature> signature) {
+ i::Isolate* isolate = Utils::OpenHandle(*name)->GetIsolate();
+ i::Handle<i::ExecutableAccessorInfo> obj =
+ isolate->factory()->NewExecutableAccessorInfo();
+ AccessorGetterCallback getter =
+ i::CallbackTable::Register(isolate, getter_in);
+ SET_FIELD_WRAPPED(obj, set_getter, getter);
+ AccessorSetterCallback setter =
+ i::CallbackTable::Register(isolate, setter_in);
+ SET_FIELD_WRAPPED(obj, set_setter, setter);
+ if (data.IsEmpty()) data = v8::Undefined();
+ obj->set_data(*Utils::OpenHandle(*data));
+ return SetAccessorInfoProperties(obj, name, settings, attributes, signature);
+}
+
+
+static i::Handle<i::AccessorInfo> MakeAccessorInfo(
+ v8::Handle<String> name,
+ v8::Handle<v8::DeclaredAccessorDescriptor> descriptor,
+ void* setter_ignored,
+ void* data_ignored,
+ v8::AccessControl settings,
+ v8::PropertyAttribute attributes,
+ v8::Handle<AccessorSignature> signature) {
+ i::Isolate* isolate = Utils::OpenHandle(*name)->GetIsolate();
+ if (descriptor.IsEmpty()) return i::Handle<i::DeclaredAccessorInfo>();
+ i::Handle<i::DeclaredAccessorInfo> obj =
+ isolate->factory()->NewDeclaredAccessorInfo();
+ obj->set_descriptor(*Utils::OpenHandle(*descriptor));
+ return SetAccessorInfoProperties(obj, name, settings, attributes, signature);
+}
+
+
+Local<ObjectTemplate> FunctionTemplate::InstanceTemplate() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::FunctionTemplate::InstanceTemplate()")
+ || EmptyCheck("v8::FunctionTemplate::InstanceTemplate()", this))
+ return Local<ObjectTemplate>();
+ ENTER_V8(isolate);
+ i::Handle<i::FunctionTemplateInfo> handle = Utils::OpenHandle(this);
+ if (handle->instance_template()->IsUndefined()) {
+ Local<ObjectTemplate> templ =
+ ObjectTemplate::New(ToApiHandle<FunctionTemplate>(handle));
+ handle->set_instance_template(*Utils::OpenHandle(*templ));
+ }
+ i::Handle<i::ObjectTemplateInfo> result(
+ i::ObjectTemplateInfo::cast(handle->instance_template()));
+ return Utils::ToLocal(result);
+}
+
+
+void FunctionTemplate::SetLength(int length) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::FunctionTemplate::SetLength()")) return;
+ ENTER_V8(isolate);
+ Utils::OpenHandle(this)->set_length(length);
+}
+
+
+void FunctionTemplate::SetClassName(Handle<String> name) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::FunctionTemplate::SetClassName()")) return;
+ ENTER_V8(isolate);
+ Utils::OpenHandle(this)->set_class_name(*Utils::OpenHandle(*name));
+}
+
+
+void FunctionTemplate::SetHiddenPrototype(bool value) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::FunctionTemplate::SetHiddenPrototype()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ Utils::OpenHandle(this)->set_hidden_prototype(value);
+}
+
+
+void FunctionTemplate::ReadOnlyPrototype() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::FunctionTemplate::ReadOnlyPrototype()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ Utils::OpenHandle(this)->set_read_only_prototype(true);
+}
+
+template<
+ typename Getter,
+ typename Setter,
+ typename Query,
+ typename Deleter,
+ typename Enumerator>
+static void SetNamedInstancePropertyHandler(
+ i::Handle<i::FunctionTemplateInfo> function_template,
+ Getter getter_in,
+ Setter setter_in,
+ Query query_in,
+ Deleter remover_in,
+ Enumerator enumerator_in,
+ Handle<Value> data) {
+ i::Isolate* isolate = function_template->GetIsolate();
+ if (IsDeadCheck(isolate,
+ "v8::FunctionTemplate::SetNamedInstancePropertyHandler()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::Struct> struct_obj =
+ isolate->factory()->NewStruct(i::INTERCEPTOR_INFO_TYPE);
+ i::Handle<i::InterceptorInfo> obj =
+ i::Handle<i::InterceptorInfo>::cast(struct_obj);
+
+ NamedPropertyGetterCallback getter =
+ i::CallbackTable::Register(isolate, getter_in);
+ if (getter != 0) SET_FIELD_WRAPPED(obj, set_getter, getter);
+ NamedPropertySetterCallback setter =
+ i::CallbackTable::Register(isolate, setter_in);
+ if (setter != 0) SET_FIELD_WRAPPED(obj, set_setter, setter);
+ NamedPropertyQueryCallback query =
+ i::CallbackTable::Register(isolate, query_in);
+ if (query != 0) SET_FIELD_WRAPPED(obj, set_query, query);
+ NamedPropertyDeleterCallback remover =
+ i::CallbackTable::Register(isolate, remover_in);
+ if (remover != 0) SET_FIELD_WRAPPED(obj, set_deleter, remover);
+ NamedPropertyEnumeratorCallback enumerator =
+ i::CallbackTable::Register(isolate, enumerator_in);
+ if (enumerator != 0) SET_FIELD_WRAPPED(obj, set_enumerator, enumerator);
+
+ if (data.IsEmpty()) data = v8::Undefined();
+ obj->set_data(*Utils::OpenHandle(*data));
+ function_template->set_named_property_handler(*obj);
+}
+
+
+template<
+ typename Getter,
+ typename Setter,
+ typename Query,
+ typename Deleter,
+ typename Enumerator>
+static void SetIndexedInstancePropertyHandler(
+ i::Handle<i::FunctionTemplateInfo> function_template,
+ Getter getter_in,
+ Setter setter_in,
+ Query query_in,
+ Deleter remover_in,
+ Enumerator enumerator_in,
+ Handle<Value> data) {
+ i::Isolate* isolate = function_template->GetIsolate();
+ if (IsDeadCheck(isolate,
+ "v8::FunctionTemplate::SetIndexedInstancePropertyHandler()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::Struct> struct_obj =
+ isolate->factory()->NewStruct(i::INTERCEPTOR_INFO_TYPE);
+ i::Handle<i::InterceptorInfo> obj =
+ i::Handle<i::InterceptorInfo>::cast(struct_obj);
+
+ IndexedPropertyGetterCallback getter =
+ i::CallbackTable::Register(isolate, getter_in);
+ if (getter != 0) SET_FIELD_WRAPPED(obj, set_getter, getter);
+ IndexedPropertySetterCallback setter =
+ i::CallbackTable::Register(isolate, setter_in);
+ if (setter != 0) SET_FIELD_WRAPPED(obj, set_setter, setter);
+ IndexedPropertyQueryCallback query =
+ i::CallbackTable::Register(isolate, query_in);
+ if (query != 0) SET_FIELD_WRAPPED(obj, set_query, query);
+ IndexedPropertyDeleterCallback remover =
+ i::CallbackTable::Register(isolate, remover_in);
+ if (remover != 0) SET_FIELD_WRAPPED(obj, set_deleter, remover);
+ IndexedPropertyEnumeratorCallback enumerator =
+ i::CallbackTable::Register(isolate, enumerator_in);
+ if (enumerator != 0) SET_FIELD_WRAPPED(obj, set_enumerator, enumerator);
+
+ if (data.IsEmpty()) data = v8::Undefined();
+ obj->set_data(*Utils::OpenHandle(*data));
+ function_template->set_indexed_property_handler(*obj);
+}
+
+
+template<typename Callback>
+static void SetInstanceCallAsFunctionHandler(
+ i::Handle<i::FunctionTemplateInfo> function_template,
+ Callback callback_in,
+ Handle<Value> data) {
+ i::Isolate* isolate = function_template->GetIsolate();
+ if (IsDeadCheck(isolate,
+ "v8::FunctionTemplate::SetInstanceCallAsFunctionHandler()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::Struct> struct_obj =
+ isolate->factory()->NewStruct(i::CALL_HANDLER_INFO_TYPE);
+ i::Handle<i::CallHandlerInfo> obj =
+ i::Handle<i::CallHandlerInfo>::cast(struct_obj);
+ FunctionCallback callback =
+ i::CallbackTable::Register(isolate, callback_in);
+ SET_FIELD_WRAPPED(obj, set_callback, callback);
+ if (data.IsEmpty()) data = v8::Undefined();
+ obj->set_data(*Utils::OpenHandle(*data));
+ function_template->set_instance_call_handler(*obj);
+}
+
+
+// --- O b j e c t T e m p l a t e ---
+
+
+Local<ObjectTemplate> ObjectTemplate::New() {
+ return New(Local<FunctionTemplate>());
+}
+
+
+Local<ObjectTemplate> ObjectTemplate::New(
+ v8::Handle<FunctionTemplate> constructor) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::ObjectTemplate::New()")) {
+ return Local<ObjectTemplate>();
+ }
+ EnsureInitializedForIsolate(isolate, "v8::ObjectTemplate::New()");
+ LOG_API(isolate, "ObjectTemplate::New");
+ ENTER_V8(isolate);
+ i::Handle<i::Struct> struct_obj =
+ isolate->factory()->NewStruct(i::OBJECT_TEMPLATE_INFO_TYPE);
+ i::Handle<i::ObjectTemplateInfo> obj =
+ i::Handle<i::ObjectTemplateInfo>::cast(struct_obj);
+ InitializeTemplate(obj, Consts::OBJECT_TEMPLATE);
+ if (!constructor.IsEmpty())
+ obj->set_constructor(*Utils::OpenHandle(*constructor));
+ obj->set_internal_field_count(i::Smi::FromInt(0));
+ return Utils::ToLocal(obj);
+}
+
+
+// Ensure that the object template has a constructor. If no
+// constructor is available we create one.
+static void EnsureConstructor(ObjectTemplate* object_template) {
+ if (Utils::OpenHandle(object_template)->constructor()->IsUndefined()) {
+ Local<FunctionTemplate> templ = FunctionTemplate::New();
+ i::Handle<i::FunctionTemplateInfo> constructor = Utils::OpenHandle(*templ);
+ constructor->set_instance_template(*Utils::OpenHandle(object_template));
+ Utils::OpenHandle(object_template)->set_constructor(*constructor);
+ }
+}
+
+
+static inline void AddPropertyToFunctionTemplate(
+ i::Handle<i::FunctionTemplateInfo> cons,
+ i::Handle<i::AccessorInfo> obj) {
+ i::Handle<i::Object> list(cons->property_accessors(), cons->GetIsolate());
+ if (list->IsUndefined()) {
+ list = NeanderArray().value();
+ cons->set_property_accessors(*list);
+ }
+ NeanderArray array(list);
+ array.add(obj);
+}
+
+
+template<typename Setter, typename Getter, typename Data>
+static bool ObjectTemplateSetAccessor(
+ ObjectTemplate* object_template,
+ v8::Handle<String> name,
+ Getter getter,
+ Setter setter,
+ Data data,
+ AccessControl settings,
+ PropertyAttribute attribute,
+ v8::Handle<AccessorSignature> signature) {
+ i::Isolate* isolate = Utils::OpenHandle(object_template)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetAccessor()")) return false;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ EnsureConstructor(object_template);
+ i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast(
+ Utils::OpenHandle(object_template)->constructor());
+ i::Handle<i::FunctionTemplateInfo> cons(constructor);
+ i::Handle<i::AccessorInfo> obj = MakeAccessorInfo(
+ name, getter, setter, data, settings, attribute, signature);
+ if (obj.is_null()) return false;
+ AddPropertyToFunctionTemplate(cons, obj);
+ return true;
+}
+
+
+void ObjectTemplate::SetAccessor(v8::Handle<String> name,
+ AccessorGetter getter,
+ AccessorSetter setter,
+ v8::Handle<Value> data,
+ AccessControl settings,
+ PropertyAttribute attribute,
+ v8::Handle<AccessorSignature> signature) {
+ ObjectTemplateSetAccessor(
+ this, name, getter, setter, data, settings, attribute, signature);
+}
+
+
+void ObjectTemplate::SetAccessor(v8::Handle<String> name,
+ AccessorGetterCallback getter,
+ AccessorSetterCallback setter,
+ v8::Handle<Value> data,
+ AccessControl settings,
+ PropertyAttribute attribute,
+ v8::Handle<AccessorSignature> signature) {
+ ObjectTemplateSetAccessor(
+ this, name, getter, setter, data, settings, attribute, signature);
+}
+
+
+bool ObjectTemplate::SetAccessor(Handle<String> name,
+ Handle<DeclaredAccessorDescriptor> descriptor,
+ AccessControl settings,
+ PropertyAttribute attribute,
+ Handle<AccessorSignature> signature) {
+ void* null = NULL;
+ return ObjectTemplateSetAccessor(
+ this, name, descriptor, null, null, settings, attribute, signature);
+}
+
+
+template<
+ typename Getter,
+ typename Setter,
+ typename Query,
+ typename Deleter,
+ typename Enumerator>
+static void ObjectTemplateSetNamedPropertyHandler(
+ ObjectTemplate* object_template,
+ Getter getter,
+ Setter setter,
+ Query query,
+ Deleter remover,
+ Enumerator enumerator,
+ Handle<Value> data) {
+ i::Isolate* isolate = Utils::OpenHandle(object_template)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetNamedPropertyHandler()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ EnsureConstructor(object_template);
+ i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast(
+ Utils::OpenHandle(object_template)->constructor());
+ i::Handle<i::FunctionTemplateInfo> cons(constructor);
+ SetNamedInstancePropertyHandler(cons,
+ getter,
+ setter,
+ query,
+ remover,
+ enumerator,
+ data);
+}
+
+
+void ObjectTemplate::SetNamedPropertyHandler(
+ NamedPropertyGetter getter,
+ NamedPropertySetter setter,
+ NamedPropertyQuery query,
+ NamedPropertyDeleter remover,
+ NamedPropertyEnumerator enumerator,
+ Handle<Value> data) {
+ ObjectTemplateSetNamedPropertyHandler(
+ this, getter, setter, query, remover, enumerator, data);
+}
+
+
+void ObjectTemplate::SetNamedPropertyHandler(
+ NamedPropertyGetterCallback getter,
+ NamedPropertySetterCallback setter,
+ NamedPropertyQueryCallback query,
+ NamedPropertyDeleterCallback remover,
+ NamedPropertyEnumeratorCallback enumerator,
+ Handle<Value> data) {
+ ObjectTemplateSetNamedPropertyHandler(
+ this, getter, setter, query, remover, enumerator, data);
+}
+
+
+void ObjectTemplate::MarkAsUndetectable() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::ObjectTemplate::MarkAsUndetectable()")) return;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ EnsureConstructor(this);
+ i::FunctionTemplateInfo* constructor =
+ i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor());
+ i::Handle<i::FunctionTemplateInfo> cons(constructor);
+ cons->set_undetectable(true);
+}
+
+
+void ObjectTemplate::SetAccessCheckCallbacks(
+ NamedSecurityCallback named_callback,
+ IndexedSecurityCallback indexed_callback,
+ Handle<Value> data,
+ bool turned_on_by_default) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetAccessCheckCallbacks()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ EnsureConstructor(this);
+
+ i::Handle<i::Struct> struct_info =
+ isolate->factory()->NewStruct(i::ACCESS_CHECK_INFO_TYPE);
+ i::Handle<i::AccessCheckInfo> info =
+ i::Handle<i::AccessCheckInfo>::cast(struct_info);
+
+ SET_FIELD_WRAPPED(info, set_named_callback, named_callback);
+ SET_FIELD_WRAPPED(info, set_indexed_callback, indexed_callback);
+
+ if (data.IsEmpty()) data = v8::Undefined();
+ info->set_data(*Utils::OpenHandle(*data));
+
+ i::FunctionTemplateInfo* constructor =
+ i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor());
+ i::Handle<i::FunctionTemplateInfo> cons(constructor);
+ cons->set_access_check_info(*info);
+ cons->set_needs_access_check(turned_on_by_default);
+}
+
+
+template<
+ typename Getter,
+ typename Setter,
+ typename Query,
+ typename Deleter,
+ typename Enumerator>
+void ObjectTemplateSetIndexedPropertyHandler(
+ ObjectTemplate* object_template,
+ Getter getter,
+ Setter setter,
+ Query query,
+ Deleter remover,
+ Enumerator enumerator,
+ Handle<Value> data) {
+ i::Isolate* isolate = Utils::OpenHandle(object_template)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetIndexedPropertyHandler()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ EnsureConstructor(object_template);
+ i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast(
+ Utils::OpenHandle(object_template)->constructor());
+ i::Handle<i::FunctionTemplateInfo> cons(constructor);
+ SetIndexedInstancePropertyHandler(cons,
+ getter,
+ setter,
+ query,
+ remover,
+ enumerator,
+ data);
+}
+
+
+void ObjectTemplate::SetIndexedPropertyHandler(
+ IndexedPropertyGetter getter,
+ IndexedPropertySetter setter,
+ IndexedPropertyQuery query,
+ IndexedPropertyDeleter remover,
+ IndexedPropertyEnumerator enumerator,
+ Handle<Value> data) {
+ ObjectTemplateSetIndexedPropertyHandler(
+ this, getter, setter, query, remover, enumerator, data);
+}
+
+
+void ObjectTemplate::SetIndexedPropertyHandler(
+ IndexedPropertyGetterCallback getter,
+ IndexedPropertySetterCallback setter,
+ IndexedPropertyQueryCallback query,
+ IndexedPropertyDeleterCallback remover,
+ IndexedPropertyEnumeratorCallback enumerator,
+ Handle<Value> data) {
+ ObjectTemplateSetIndexedPropertyHandler(
+ this, getter, setter, query, remover, enumerator, data);
+}
+
+
+template<typename Callback>
+static void ObjectTemplateSetCallAsFunctionHandler(
+ ObjectTemplate* object_template,
+ Callback callback,
+ Handle<Value> data) {
+ i::Isolate* isolate = Utils::OpenHandle(object_template)->GetIsolate();
+ if (IsDeadCheck(isolate,
+ "v8::ObjectTemplate::SetCallAsFunctionHandler()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ EnsureConstructor(object_template);
+ i::FunctionTemplateInfo* constructor = i::FunctionTemplateInfo::cast(
+ Utils::OpenHandle(object_template)->constructor());
+ i::Handle<i::FunctionTemplateInfo> cons(constructor);
+ SetInstanceCallAsFunctionHandler(cons, callback, data);
+}
+
+
+void ObjectTemplate::SetCallAsFunctionHandler(InvocationCallback callback,
+ Handle<Value> data) {
+ return ObjectTemplateSetCallAsFunctionHandler(this, callback, data);
+}
+
+
+void ObjectTemplate::SetCallAsFunctionHandler(FunctionCallback callback,
+ Handle<Value> data) {
+ return ObjectTemplateSetCallAsFunctionHandler(this, callback, data);
+}
+
+
+int ObjectTemplate::InternalFieldCount() {
+ if (IsDeadCheck(Utils::OpenHandle(this)->GetIsolate(),
+ "v8::ObjectTemplate::InternalFieldCount()")) {
+ return 0;
+ }
+ return i::Smi::cast(Utils::OpenHandle(this)->internal_field_count())->value();
+}
+
+
+void ObjectTemplate::SetInternalFieldCount(int value) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetInternalFieldCount()")) {
+ return;
+ }
+ if (!ApiCheck(i::Smi::IsValid(value),
+ "v8::ObjectTemplate::SetInternalFieldCount()",
+ "Invalid internal field count")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ if (value > 0) {
+ // The internal field count is set by the constructor function's
+ // construct code, so we ensure that there is a constructor
+ // function to do the setting.
+ EnsureConstructor(this);
+ }
+ Utils::OpenHandle(this)->set_internal_field_count(i::Smi::FromInt(value));
+}
+
+
+// --- S c r i p t D a t a ---
+
+
+ScriptData* ScriptData::PreCompile(const char* input, int length) {
+ i::Utf8ToUtf16CharacterStream stream(
+ reinterpret_cast<const unsigned char*>(input), length);
+ return i::PreParserApi::PreParse(&stream);
+}
+
+
+ScriptData* ScriptData::PreCompile(v8::Handle<String> source) {
+ i::Handle<i::String> str = Utils::OpenHandle(*source);
+ if (str->IsExternalTwoByteString()) {
+ i::ExternalTwoByteStringUtf16CharacterStream stream(
+ i::Handle<i::ExternalTwoByteString>::cast(str), 0, str->length());
+ return i::PreParserApi::PreParse(&stream);
+ } else {
+ i::GenericStringUtf16CharacterStream stream(str, 0, str->length());
+ return i::PreParserApi::PreParse(&stream);
+ }
+}
+
+
+ScriptData* ScriptData::New(const char* data, int length) {
+ // Return an empty ScriptData if the length is obviously invalid.
+ if (length % sizeof(unsigned) != 0) {
+ return new i::ScriptDataImpl();
+ }
+
+ // Copy the data to ensure it is properly aligned.
+ int deserialized_data_length = length / sizeof(unsigned);
+ // If aligned, don't create a copy of the data.
+ if (reinterpret_cast<intptr_t>(data) % sizeof(unsigned) == 0) {
+ return new i::ScriptDataImpl(data, length);
+ }
+ // Copy the data to align it.
+ unsigned* deserialized_data = i::NewArray<unsigned>(deserialized_data_length);
+ i::CopyBytes(reinterpret_cast<char*>(deserialized_data),
+ data, static_cast<size_t>(length));
+
+ return new i::ScriptDataImpl(
+ i::Vector<unsigned>(deserialized_data, deserialized_data_length));
+}
+
+
+// --- S c r i p t ---
+
+
+Local<Script> Script::New(v8::Handle<String> source,
+ v8::ScriptOrigin* origin,
+ v8::ScriptData* pre_data,
+ v8::Handle<String> script_data) {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Script::New()", return Local<Script>());
+ LOG_API(isolate, "Script::New");
+ ENTER_V8(isolate);
+ i::SharedFunctionInfo* raw_result = NULL;
+ { i::HandleScope scope(isolate);
+ i::Handle<i::String> str = Utils::OpenHandle(*source);
+ i::Handle<i::Object> name_obj;
+ int line_offset = 0;
+ int column_offset = 0;
+ bool is_shared_cross_origin = false;
+ if (origin != NULL) {
+ if (!origin->ResourceName().IsEmpty()) {
+ name_obj = Utils::OpenHandle(*origin->ResourceName());
+ }
+ if (!origin->ResourceLineOffset().IsEmpty()) {
+ line_offset = static_cast<int>(origin->ResourceLineOffset()->Value());
+ }
+ if (!origin->ResourceColumnOffset().IsEmpty()) {
+ column_offset =
+ static_cast<int>(origin->ResourceColumnOffset()->Value());
+ }
+ if (!origin->ResourceIsSharedCrossOrigin().IsEmpty()) {
+ is_shared_cross_origin =
+ origin->ResourceIsSharedCrossOrigin() == v8::True();
+ }
+ }
+ EXCEPTION_PREAMBLE(isolate);
+ i::ScriptDataImpl* pre_data_impl =
+ static_cast<i::ScriptDataImpl*>(pre_data);
+ // We assert that the pre-data is sane, even though we can actually
+ // handle it if it turns out not to be in release mode.
+ ASSERT(pre_data_impl == NULL || pre_data_impl->SanityCheck());
+ // If the pre-data isn't sane we simply ignore it
+ if (pre_data_impl != NULL && !pre_data_impl->SanityCheck()) {
+ pre_data_impl = NULL;
+ }
+ i::Handle<i::SharedFunctionInfo> result =
+ i::Compiler::Compile(str,
+ name_obj,
+ line_offset,
+ column_offset,
+ is_shared_cross_origin,
+ isolate->global_context(),
+ NULL,
+ pre_data_impl,
+ Utils::OpenHandle(*script_data, true),
+ i::NOT_NATIVES_CODE);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Script>());
+ raw_result = *result;
+ }
+ i::Handle<i::SharedFunctionInfo> result(raw_result, isolate);
+ return ToApiHandle<Script>(result);
+}
+
+
+Local<Script> Script::New(v8::Handle<String> source,
+ v8::Handle<Value> file_name) {
+ ScriptOrigin origin(file_name);
+ return New(source, &origin);
+}
+
+
+Local<Script> Script::Compile(v8::Handle<String> source,
+ v8::ScriptOrigin* origin,
+ v8::ScriptData* pre_data,
+ v8::Handle<String> script_data) {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Script::Compile()", return Local<Script>());
+ LOG_API(isolate, "Script::Compile");
+ ENTER_V8(isolate);
+ Local<Script> generic = New(source, origin, pre_data, script_data);
+ if (generic.IsEmpty())
+ return generic;
+ i::Handle<i::Object> obj = Utils::OpenHandle(*generic);
+ i::Handle<i::SharedFunctionInfo> function =
+ i::Handle<i::SharedFunctionInfo>(i::SharedFunctionInfo::cast(*obj));
+ i::Handle<i::JSFunction> result =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ function,
+ isolate->global_context());
+ return ToApiHandle<Script>(result);
+}
+
+
+Local<Script> Script::Compile(v8::Handle<String> source,
+ v8::Handle<Value> file_name,
+ v8::Handle<String> script_data) {
+ ScriptOrigin origin(file_name);
+ return Compile(source, &origin, 0, script_data);
+}
+
+
+Local<Value> Script::Run() {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Script::Run()", return Local<Value>());
+ LOG_API(isolate, "Script::Run");
+ ENTER_V8(isolate);
+ i::Logger::TimerEventScope timer_scope(
+ isolate, i::Logger::TimerEventScope::v8_execute);
+ i::Object* raw_result = NULL;
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::JSFunction> fun;
+ if (obj->IsSharedFunctionInfo()) {
+ i::Handle<i::SharedFunctionInfo>
+ function_info(i::SharedFunctionInfo::cast(*obj), isolate);
+ fun = isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ function_info, isolate->global_context());
+ } else {
+ fun = i::Handle<i::JSFunction>(i::JSFunction::cast(*obj), isolate);
+ }
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> receiver(
+ isolate->context()->global_proxy(), isolate);
+ i::Handle<i::Object> result =
+ i::Execution::Call(fun, receiver, 0, NULL, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<Value>());
+ raw_result = *result;
+ }
+ i::Handle<i::Object> result(raw_result, isolate);
+ return Utils::ToLocal(result);
+}
+
+
+static i::Handle<i::SharedFunctionInfo> OpenScript(Script* script) {
+ i::Handle<i::Object> obj = Utils::OpenHandle(script);
+ i::Handle<i::SharedFunctionInfo> result;
+ if (obj->IsSharedFunctionInfo()) {
+ result =
+ i::Handle<i::SharedFunctionInfo>(i::SharedFunctionInfo::cast(*obj));
+ } else {
+ result =
+ i::Handle<i::SharedFunctionInfo>(i::JSFunction::cast(*obj)->shared());
+ }
+ return result;
+}
+
+
+Local<Value> Script::Id() {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Script::Id()", return Local<Value>());
+ LOG_API(isolate, "Script::Id");
+ i::Object* raw_id = NULL;
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::SharedFunctionInfo> function_info = OpenScript(this);
+ i::Handle<i::Script> script(i::Script::cast(function_info->script()));
+ i::Handle<i::Object> id(script->id(), isolate);
+ raw_id = *id;
+ }
+ i::Handle<i::Object> id(raw_id, isolate);
+ return Utils::ToLocal(id);
+}
+
+
+int Script::GetId() {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Script::Id()", return -1);
+ LOG_API(isolate, "Script::Id");
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::SharedFunctionInfo> function_info = OpenScript(this);
+ i::Handle<i::Script> script(i::Script::cast(function_info->script()));
+ return script->id()->value();
+ }
+}
+
+
+int Script::GetLineNumber(int code_pos) {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Script::GetLineNumber()", return -1);
+ LOG_API(isolate, "Script::GetLineNumber");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsScript()) {
+ i::Handle<i::Script> script = i::Handle<i::Script>(i::Script::cast(*obj));
+ return i::GetScriptLineNumber(script, code_pos);
+ } else {
+ return -1;
+ }
+}
+
+
+Handle<Value> Script::GetScriptName() {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Script::GetName()", return Handle<String>());
+ LOG_API(isolate, "Script::GetName");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsScript()) {
+ i::Object* name = i::Script::cast(*obj)->name();
+ return Utils::ToLocal(i::Handle<i::Object>(name, isolate));
+ } else {
+ return Handle<String>();
+ }
+}
+
+
+void Script::SetData(v8::Handle<String> data) {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Script::SetData()", return);
+ LOG_API(isolate, "Script::SetData");
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::SharedFunctionInfo> function_info = OpenScript(this);
+ i::Handle<i::Object> raw_data = Utils::OpenHandle(*data);
+ i::Handle<i::Script> script(i::Script::cast(function_info->script()));
+ script->set_data(*raw_data);
+ }
+}
+
+
+// --- E x c e p t i o n s ---
+
+
+v8::TryCatch::TryCatch()
+ : isolate_(i::Isolate::Current()),
+ next_(isolate_->try_catch_handler_address()),
+ is_verbose_(false),
+ can_continue_(true),
+ capture_message_(true),
+ rethrow_(false),
+ has_terminated_(false) {
+ Reset();
+ isolate_->RegisterTryCatchHandler(this);
+}
+
+
+v8::TryCatch::~TryCatch() {
+ ASSERT(isolate_ == i::Isolate::Current());
+ if (rethrow_) {
+ v8::HandleScope scope(reinterpret_cast<Isolate*>(isolate_));
+ v8::Local<v8::Value> exc = v8::Local<v8::Value>::New(Exception());
+ if (HasCaught() && capture_message_) {
+ // If an exception was caught and rethrow_ is indicated, the saved
+ // message, script, and location need to be restored to Isolate TLS
+ // for reuse. capture_message_ needs to be disabled so that DoThrow()
+ // does not create a new message.
+ isolate_->thread_local_top()->rethrowing_message_ = true;
+ isolate_->RestorePendingMessageFromTryCatch(this);
+ }
+ isolate_->UnregisterTryCatchHandler(this);
+ v8::ThrowException(exc);
+ ASSERT(!isolate_->thread_local_top()->rethrowing_message_);
+ } else {
+ isolate_->UnregisterTryCatchHandler(this);
+ }
+}
+
+
+bool v8::TryCatch::HasCaught() const {
+ return !reinterpret_cast<i::Object*>(exception_)->IsTheHole();
+}
+
+
+bool v8::TryCatch::CanContinue() const {
+ return can_continue_;
+}
+
+
+bool v8::TryCatch::HasTerminated() const {
+ return has_terminated_;
+}
+
+
+v8::Handle<v8::Value> v8::TryCatch::ReThrow() {
+ if (!HasCaught()) return v8::Local<v8::Value>();
+ rethrow_ = true;
+ return v8::Undefined();
+}
+
+
+v8::Local<Value> v8::TryCatch::Exception() const {
+ ASSERT(isolate_ == i::Isolate::Current());
+ if (HasCaught()) {
+ // Check for out of memory exception.
+ i::Object* exception = reinterpret_cast<i::Object*>(exception_);
+ return v8::Utils::ToLocal(i::Handle<i::Object>(exception, isolate_));
+ } else {
+ return v8::Local<Value>();
+ }
+}
+
+
+v8::Local<Value> v8::TryCatch::StackTrace() const {
+ ASSERT(isolate_ == i::Isolate::Current());
+ if (HasCaught()) {
+ i::Object* raw_obj = reinterpret_cast<i::Object*>(exception_);
+ if (!raw_obj->IsJSObject()) return v8::Local<Value>();
+ i::HandleScope scope(isolate_);
+ i::Handle<i::JSObject> obj(i::JSObject::cast(raw_obj), isolate_);
+ i::Handle<i::String> name = isolate_->factory()->stack_string();
+ if (!obj->HasProperty(*name)) return v8::Local<Value>();
+ i::Handle<i::Object> value = i::GetProperty(isolate_, obj, name);
+ if (value.is_null()) return v8::Local<Value>();
+ return v8::Utils::ToLocal(scope.CloseAndEscape(value));
+ } else {
+ return v8::Local<Value>();
+ }
+}
+
+
+v8::Local<v8::Message> v8::TryCatch::Message() const {
+ ASSERT(isolate_ == i::Isolate::Current());
+ i::Object* message = reinterpret_cast<i::Object*>(message_obj_);
+ ASSERT(message->IsJSMessageObject() || message->IsTheHole());
+ if (HasCaught() && !message->IsTheHole()) {
+ return v8::Utils::MessageToLocal(i::Handle<i::Object>(message, isolate_));
+ } else {
+ return v8::Local<v8::Message>();
+ }
+}
+
+
+void v8::TryCatch::Reset() {
+ ASSERT(isolate_ == i::Isolate::Current());
+ i::Object* the_hole = isolate_->heap()->the_hole_value();
+ exception_ = the_hole;
+ message_obj_ = the_hole;
+ message_script_ = the_hole;
+ message_start_pos_ = 0;
+ message_end_pos_ = 0;
+}
+
+
+void v8::TryCatch::SetVerbose(bool value) {
+ is_verbose_ = value;
+}
+
+
+void v8::TryCatch::SetCaptureMessage(bool value) {
+ capture_message_ = value;
+}
+
+
+// --- M e s s a g e ---
+
+
+Local<String> Message::Get() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Message::Get()", return Local<String>());
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::String> raw_result = i::MessageHandler::GetMessage(isolate, obj);
+ Local<String> result = Utils::ToLocal(raw_result);
+ return scope.Close(result);
+}
+
+
+v8::Handle<Value> Message::GetScriptResourceName() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Message::GetScriptResourceName()")) {
+ return Local<String>();
+ }
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSMessageObject> message =
+ i::Handle<i::JSMessageObject>::cast(Utils::OpenHandle(this));
+ // Return this.script.name.
+ i::Handle<i::JSValue> script =
+ i::Handle<i::JSValue>::cast(i::Handle<i::Object>(message->script(),
+ isolate));
+ i::Handle<i::Object> resource_name(i::Script::cast(script->value())->name(),
+ isolate);
+ return scope.Close(Utils::ToLocal(resource_name));
+}
+
+
+v8::Handle<Value> Message::GetScriptData() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Message::GetScriptResourceData()")) {
+ return Local<Value>();
+ }
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSMessageObject> message =
+ i::Handle<i::JSMessageObject>::cast(Utils::OpenHandle(this));
+ // Return this.script.data.
+ i::Handle<i::JSValue> script =
+ i::Handle<i::JSValue>::cast(i::Handle<i::Object>(message->script(),
+ isolate));
+ i::Handle<i::Object> data(i::Script::cast(script->value())->data(), isolate);
+ return scope.Close(Utils::ToLocal(data));
+}
+
+
+v8::Handle<v8::StackTrace> Message::GetStackTrace() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Message::GetStackTrace()")) {
+ return Local<v8::StackTrace>();
+ }
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSMessageObject> message =
+ i::Handle<i::JSMessageObject>::cast(Utils::OpenHandle(this));
+ i::Handle<i::Object> stackFramesObj(message->stack_frames(), isolate);
+ if (!stackFramesObj->IsJSArray()) return v8::Handle<v8::StackTrace>();
+ i::Handle<i::JSArray> stackTrace =
+ i::Handle<i::JSArray>::cast(stackFramesObj);
+ return scope.Close(Utils::StackTraceToLocal(stackTrace));
+}
+
+
+static i::Handle<i::Object> CallV8HeapFunction(const char* name,
+ i::Handle<i::Object> recv,
+ int argc,
+ i::Handle<i::Object> argv[],
+ bool* has_pending_exception) {
+ i::Isolate* isolate = i::Isolate::Current();
+ i::Handle<i::String> fmt_str =
+ isolate->factory()->InternalizeUtf8String(name);
+ i::Object* object_fun =
+ isolate->js_builtins_object()->GetPropertyNoExceptionThrown(*fmt_str);
+ i::Handle<i::JSFunction> fun =
+ i::Handle<i::JSFunction>(i::JSFunction::cast(object_fun));
+ i::Handle<i::Object> value =
+ i::Execution::Call(fun, recv, argc, argv, has_pending_exception);
+ return value;
+}
+
+
+static i::Handle<i::Object> CallV8HeapFunction(const char* name,
+ i::Handle<i::Object> data,
+ bool* has_pending_exception) {
+ i::Handle<i::Object> argv[] = { data };
+ return CallV8HeapFunction(name,
+ i::Isolate::Current()->js_builtins_object(),
+ ARRAY_SIZE(argv),
+ argv,
+ has_pending_exception);
+}
+
+
+int Message::GetLineNumber() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Message::GetLineNumber()", return kNoLineNumberInfo);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> result = CallV8HeapFunction("GetLineNumber",
+ Utils::OpenHandle(this),
+ &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, 0);
+ return static_cast<int>(result->Number());
+}
+
+
+int Message::GetStartPosition() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Message::GetStartPosition()")) return 0;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSMessageObject> message =
+ i::Handle<i::JSMessageObject>::cast(Utils::OpenHandle(this));
+ return message->start_position();
+}
+
+
+int Message::GetEndPosition() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Message::GetEndPosition()")) return 0;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSMessageObject> message =
+ i::Handle<i::JSMessageObject>::cast(Utils::OpenHandle(this));
+ return message->end_position();
+}
+
+
+int Message::GetStartColumn() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Message::GetStartColumn()")) {
+ return kNoColumnInfo;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> start_col_obj = CallV8HeapFunction(
+ "GetPositionInLine",
+ data_obj,
+ &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, 0);
+ return static_cast<int>(start_col_obj->Number());
+}
+
+
+int Message::GetEndColumn() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Message::GetEndColumn()")) return kNoColumnInfo;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> start_col_obj = CallV8HeapFunction(
+ "GetPositionInLine",
+ data_obj,
+ &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, 0);
+ i::Handle<i::JSMessageObject> message =
+ i::Handle<i::JSMessageObject>::cast(data_obj);
+ int start = message->start_position();
+ int end = message->end_position();
+ return static_cast<int>(start_col_obj->Number()) + (end - start);
+}
+
+
+bool Message::IsSharedCrossOrigin() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Message::IsSharedCrossOrigin()")) return 0;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSMessageObject> message =
+ i::Handle<i::JSMessageObject>::cast(Utils::OpenHandle(this));
+ i::Handle<i::JSValue> script =
+ i::Handle<i::JSValue>::cast(i::Handle<i::Object>(message->script(),
+ isolate));
+ return i::Script::cast(script->value())->is_shared_cross_origin();
+}
+
+
+Local<String> Message::GetSourceLine() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Message::GetSourceLine()", return Local<String>());
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> result = CallV8HeapFunction("GetSourceLine",
+ Utils::OpenHandle(this),
+ &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::String>());
+ if (result->IsString()) {
+ return scope.Close(Utils::ToLocal(i::Handle<i::String>::cast(result)));
+ } else {
+ return Local<String>();
+ }
+}
+
+
+void Message::PrintCurrentStackTrace(FILE* out) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Message::PrintCurrentStackTrace()")) return;
+ ENTER_V8(isolate);
+ isolate->PrintCurrentStackTrace(out);
+}
+
+
+// --- S t a c k T r a c e ---
+
+Local<StackFrame> StackTrace::GetFrame(uint32_t index) const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackTrace::GetFrame()")) {
+ return Local<StackFrame>();
+ }
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSArray> self = Utils::OpenHandle(this);
+ i::Object* raw_object = self->GetElementNoExceptionThrown(index);
+ i::Handle<i::JSObject> obj(i::JSObject::cast(raw_object));
+ return scope.Close(Utils::StackFrameToLocal(obj));
+}
+
+
+int StackTrace::GetFrameCount() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackTrace::GetFrameCount()")) return -1;
+ ENTER_V8(isolate);
+ return i::Smi::cast(Utils::OpenHandle(this)->length())->value();
+}
+
+
+Local<Array> StackTrace::AsArray() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackTrace::AsArray()")) Local<Array>();
+ ENTER_V8(isolate);
+ return Utils::ToLocal(Utils::OpenHandle(this));
+}
+
+
+Local<StackTrace> StackTrace::CurrentStackTrace(int frame_limit,
+ StackTraceOptions options) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::StackTrace::CurrentStackTrace()")) {
+ Local<StackTrace>();
+ }
+ ENTER_V8(isolate);
+ i::Handle<i::JSArray> stackTrace =
+ isolate->CaptureCurrentStackTrace(frame_limit, options);
+ return Utils::StackTraceToLocal(stackTrace);
+}
+
+
+// --- S t a c k F r a m e ---
+
+int StackFrame::GetLineNumber() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackFrame::GetLineNumber()")) {
+ return Message::kNoLineNumberInfo;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> line = GetProperty(self, "lineNumber");
+ if (!line->IsSmi()) {
+ return Message::kNoLineNumberInfo;
+ }
+ return i::Smi::cast(*line)->value();
+}
+
+
+int StackFrame::GetColumn() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackFrame::GetColumn()")) {
+ return Message::kNoColumnInfo;
+ }
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> column = GetProperty(self, "column");
+ if (!column->IsSmi()) {
+ return Message::kNoColumnInfo;
+ }
+ return i::Smi::cast(*column)->value();
+}
+
+
+Local<String> StackFrame::GetScriptName() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackFrame::GetScriptName()")) {
+ return Local<String>();
+ }
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> name = GetProperty(self, "scriptName");
+ if (!name->IsString()) {
+ return Local<String>();
+ }
+ return scope.Close(Local<String>::Cast(Utils::ToLocal(name)));
+}
+
+
+Local<String> StackFrame::GetScriptNameOrSourceURL() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackFrame::GetScriptNameOrSourceURL()")) {
+ return Local<String>();
+ }
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> name = GetProperty(self, "scriptNameOrSourceURL");
+ if (!name->IsString()) {
+ return Local<String>();
+ }
+ return scope.Close(Local<String>::Cast(Utils::ToLocal(name)));
+}
+
+
+Local<String> StackFrame::GetFunctionName() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackFrame::GetFunctionName()")) {
+ return Local<String>();
+ }
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> name = GetProperty(self, "functionName");
+ if (!name->IsString()) {
+ return Local<String>();
+ }
+ return scope.Close(Local<String>::Cast(Utils::ToLocal(name)));
+}
+
+
+bool StackFrame::IsEval() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackFrame::IsEval()")) return false;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> is_eval = GetProperty(self, "isEval");
+ return is_eval->IsTrue();
+}
+
+
+bool StackFrame::IsConstructor() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::StackFrame::IsConstructor()")) return false;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> is_constructor = GetProperty(self, "isConstructor");
+ return is_constructor->IsTrue();
+}
+
+
+// --- J S O N ---
+
+Local<Value> JSON::Parse(Local<String> json_string) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::JSON::Parse");
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::String> source = i::Handle<i::String>(
+ FlattenGetString(Utils::OpenHandle(*json_string)));
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> result;
+ if (source->IsSeqOneByteString()) {
+ result = i::JsonParser<true>::Parse(source);
+ } else {
+ result = i::JsonParser<false>::Parse(source);
+ }
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Object>());
+ return Utils::ToLocal(
+ i::Handle<i::Object>::cast(scope.CloseAndEscape(result)));
+}
+
+
+// --- D a t a ---
+
+bool Value::FullIsUndefined() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsUndefined()")) {
+ return false;
+ }
+ bool result = Utils::OpenHandle(this)->IsUndefined();
+ ASSERT_EQ(result, QuickIsUndefined());
+ return result;
+}
+
+
+bool Value::FullIsNull() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsNull()")) return false;
+ bool result = Utils::OpenHandle(this)->IsNull();
+ ASSERT_EQ(result, QuickIsNull());
+ return result;
+}
+
+
+bool Value::IsTrue() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsTrue()")) return false;
+ return Utils::OpenHandle(this)->IsTrue();
+}
+
+
+bool Value::IsFalse() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsFalse()")) return false;
+ return Utils::OpenHandle(this)->IsFalse();
+}
+
+
+bool Value::IsFunction() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsFunction()")) {
+ return false;
+ }
+ return Utils::OpenHandle(this)->IsJSFunction();
+}
+
+
+bool Value::FullIsString() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsString()")) return false;
+ bool result = Utils::OpenHandle(this)->IsString();
+ ASSERT_EQ(result, QuickIsString());
+ return result;
+}
+
+
+bool Value::IsSymbol() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsSymbol()")) return false;
+ return Utils::OpenHandle(this)->IsSymbol();
+}
+
+
+bool Value::IsArray() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsArray()")) return false;
+ return Utils::OpenHandle(this)->IsJSArray();
+}
+
+
+bool Value::IsArrayBuffer() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsArrayBuffer()"))
+ return false;
+ return Utils::OpenHandle(this)->IsJSArrayBuffer();
+}
+
+
+bool Value::IsArrayBufferView() const {
+ return Utils::OpenHandle(this)->IsJSArrayBufferView();
+}
+
+
+bool Value::IsTypedArray() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsArrayBuffer()"))
+ return false;
+ return Utils::OpenHandle(this)->IsJSTypedArray();
+}
+
+
+#define TYPED_ARRAY_LIST(F) \
+F(Uint8Array, kExternalUnsignedByteArray) \
+F(Int8Array, kExternalByteArray) \
+F(Uint16Array, kExternalUnsignedShortArray) \
+F(Int16Array, kExternalShortArray) \
+F(Uint32Array, kExternalUnsignedIntArray) \
+F(Int32Array, kExternalIntArray) \
+F(Float32Array, kExternalFloatArray) \
+F(Float64Array, kExternalDoubleArray) \
+F(Uint8ClampedArray, kExternalPixelArray)
+
+
+#define VALUE_IS_TYPED_ARRAY(TypedArray, type_const) \
+ bool Value::Is##TypedArray() const { \
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::Is" #TypedArray "()")) \
+ return false; \
+ i::Handle<i::Object> obj = Utils::OpenHandle(this); \
+ if (!obj->IsJSTypedArray()) return false; \
+ return i::JSTypedArray::cast(*obj)->type() == type_const; \
+ }
+
+TYPED_ARRAY_LIST(VALUE_IS_TYPED_ARRAY)
+
+#undef VALUE_IS_TYPED_ARRAY
+
+
+bool Value::IsDataView() const {
+ return Utils::OpenHandle(this)->IsJSDataView();
+}
+
+
+bool Value::IsObject() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsObject()")) return false;
+ return Utils::OpenHandle(this)->IsJSObject();
+}
+
+
+bool Value::IsNumber() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsNumber()")) return false;
+ return Utils::OpenHandle(this)->IsNumber();
+}
+
+
+bool Value::IsBoolean() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsBoolean()")) {
+ return false;
+ }
+ return Utils::OpenHandle(this)->IsBoolean();
+}
+
+
+bool Value::IsExternal() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsExternal()")) {
+ return false;
+ }
+ return Utils::OpenHandle(this)->IsExternal();
+}
+
+
+bool Value::IsInt32() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsInt32()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ 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;
+}
+
+
+bool Value::IsUint32() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsUint32()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ 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;
+}
+
+
+bool Value::IsDate() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::IsDate()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->HasSpecificClassOf(isolate->heap()->Date_string());
+}
+
+
+bool Value::IsStringObject() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::IsStringObject()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->HasSpecificClassOf(isolate->heap()->String_string());
+}
+
+
+bool Value::IsSymbolObject() const {
+ // TODO(svenpanne): these and other test functions should be written such
+ // that they do not use Isolate::Current().
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::IsSymbolObject()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->HasSpecificClassOf(isolate->heap()->Symbol_string());
+}
+
+
+bool Value::IsNumberObject() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::IsNumberObject()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->HasSpecificClassOf(isolate->heap()->Number_string());
+}
+
+
+static i::Object* LookupBuiltin(i::Isolate* isolate,
+ const char* builtin_name) {
+ i::Handle<i::String> string =
+ isolate->factory()->InternalizeUtf8String(builtin_name);
+ i::Handle<i::JSBuiltinsObject> builtins = isolate->js_builtins_object();
+ return builtins->GetPropertyNoExceptionThrown(*string);
+}
+
+
+static bool CheckConstructor(i::Isolate* isolate,
+ i::Handle<i::JSObject> obj,
+ const char* class_name) {
+ i::Object* constr = obj->map()->constructor();
+ if (!constr->IsJSFunction()) return false;
+ i::JSFunction* func = i::JSFunction::cast(constr);
+ return func->shared()->native() &&
+ constr == LookupBuiltin(isolate, class_name);
+}
+
+
+bool Value::IsNativeError() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::IsNativeError()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsJSObject()) {
+ i::Handle<i::JSObject> js_obj(i::JSObject::cast(*obj));
+ return CheckConstructor(isolate, js_obj, "$Error") ||
+ CheckConstructor(isolate, js_obj, "$EvalError") ||
+ CheckConstructor(isolate, js_obj, "$RangeError") ||
+ CheckConstructor(isolate, js_obj, "$ReferenceError") ||
+ CheckConstructor(isolate, js_obj, "$SyntaxError") ||
+ CheckConstructor(isolate, js_obj, "$TypeError") ||
+ CheckConstructor(isolate, js_obj, "$URIError");
+ } else {
+ return false;
+ }
+}
+
+
+bool Value::IsBooleanObject() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::IsBooleanObject()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->HasSpecificClassOf(isolate->heap()->Boolean_string());
+}
+
+
+bool Value::IsRegExp() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsRegExp()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->IsJSRegExp();
+}
+
+
+Local<String> Value::ToString() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> str;
+ if (obj->IsString()) {
+ str = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToString()")) {
+ return Local<String>();
+ }
+ LOG_API(isolate, "ToString");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ str = i::Execution::ToString(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<String>());
+ }
+ return ToApiHandle<String>(str);
+}
+
+
+Local<String> Value::ToDetailString() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> str;
+ if (obj->IsString()) {
+ str = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToDetailString()")) {
+ return Local<String>();
+ }
+ LOG_API(isolate, "ToDetailString");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ str = i::Execution::ToDetailString(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<String>());
+ }
+ return ToApiHandle<String>(str);
+}
+
+
+Local<v8::Object> Value::ToObject() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> val;
+ if (obj->IsJSObject()) {
+ val = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToObject()")) {
+ return Local<v8::Object>();
+ }
+ LOG_API(isolate, "ToObject");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ val = i::Execution::ToObject(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+ }
+ return ToApiHandle<Object>(val);
+}
+
+
+Local<Boolean> Value::ToBoolean() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsBoolean()) {
+ return ToApiHandle<Boolean>(obj);
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToBoolean()")) {
+ return Local<Boolean>();
+ }
+ LOG_API(isolate, "ToBoolean");
+ ENTER_V8(isolate);
+ i::Handle<i::Object> val =
+ isolate->factory()->ToBoolean(obj->BooleanValue());
+ return ToApiHandle<Boolean>(val);
+ }
+}
+
+
+Local<Number> Value::ToNumber() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> num;
+ if (obj->IsNumber()) {
+ num = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToNumber()")) {
+ return Local<Number>();
+ }
+ LOG_API(isolate, "ToNumber");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ num = i::Execution::ToNumber(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Number>());
+ }
+ return ToApiHandle<Number>(num);
+}
+
+
+Local<Integer> Value::ToInteger() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> num;
+ if (obj->IsSmi()) {
+ num = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToInteger()")) return Local<Integer>();
+ LOG_API(isolate, "ToInteger");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ num = i::Execution::ToInteger(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Integer>());
+ }
+ return ToApiHandle<Integer>(num);
+}
+
+
+void i::Internals::CheckInitializedImpl(v8::Isolate* external_isolate) {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(external_isolate);
+ ApiCheck(isolate != NULL && isolate->IsInitialized() && !i::V8::IsDead(),
+ "v8::internal::Internals::CheckInitialized()",
+ "Isolate is not initialized or V8 has died");
+}
+
+
+void External::CheckCast(v8::Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::External::Cast()")) return;
+ ApiCheck(Utils::OpenHandle(that)->IsExternal(),
+ "v8::External::Cast()",
+ "Could not convert to external");
+}
+
+
+void v8::Object::CheckCast(Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Object::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsJSObject(),
+ "v8::Object::Cast()",
+ "Could not convert to object");
+}
+
+
+void v8::Function::CheckCast(Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Function::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsJSFunction(),
+ "v8::Function::Cast()",
+ "Could not convert to function");
+}
+
+
+void v8::String::CheckCast(v8::Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::String::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsString(),
+ "v8::String::Cast()",
+ "Could not convert to string");
+}
+
+
+void v8::Symbol::CheckCast(v8::Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Symbol::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsSymbol(),
+ "v8::Symbol::Cast()",
+ "Could not convert to symbol");
+}
+
+
+void v8::Number::CheckCast(v8::Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Number::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsNumber(),
+ "v8::Number::Cast()",
+ "Could not convert to number");
+}
+
+
+void v8::Integer::CheckCast(v8::Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Integer::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsNumber(),
+ "v8::Integer::Cast()",
+ "Could not convert to number");
+}
+
+
+void v8::Array::CheckCast(Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Array::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsJSArray(),
+ "v8::Array::Cast()",
+ "Could not convert to array");
+}
+
+
+void v8::ArrayBuffer::CheckCast(Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::ArrayBuffer::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsJSArrayBuffer(),
+ "v8::ArrayBuffer::Cast()",
+ "Could not convert to ArrayBuffer");
+}
+
+
+void v8::ArrayBuffer::Allocator::Free(void* data) {
+ API_Fatal("v8::ArrayBuffer::Allocator::Free",
+ "Override Allocator::Free(void*, size_t)");
+}
+
+
+void v8::ArrayBufferView::CheckCast(Value* that) {
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsJSArrayBufferView(),
+ "v8::ArrayBufferView::Cast()",
+ "Could not convert to ArrayBufferView");
+}
+
+
+void v8::TypedArray::CheckCast(Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::TypedArray::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsJSTypedArray(),
+ "v8::TypedArray::Cast()",
+ "Could not convert to TypedArray");
+}
+
+
+#define CHECK_TYPED_ARRAY_CAST(ApiClass, typeConst) \
+ void v8::ApiClass::CheckCast(Value* that) { \
+ if (IsDeadCheck(i::Isolate::Current(), "v8::" #ApiClass "::Cast()")) \
+ return; \
+ i::Handle<i::Object> obj = Utils::OpenHandle(that); \
+ ApiCheck(obj->IsJSTypedArray() && \
+ i::JSTypedArray::cast(*obj)->type() == typeConst, \
+ "v8::" #ApiClass "::Cast()", \
+ "Could not convert to " #ApiClass); \
+ }
+
+
+TYPED_ARRAY_LIST(CHECK_TYPED_ARRAY_CAST)
+
+#undef CHECK_TYPED_ARRAY_CAST
+
+
+void v8::DataView::CheckCast(Value* that) {
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsJSDataView(),
+ "v8::DataView::Cast()",
+ "Could not convert to DataView");
+}
+
+
+void v8::Date::CheckCast(v8::Value* that) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Date::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->HasSpecificClassOf(isolate->heap()->Date_string()),
+ "v8::Date::Cast()",
+ "Could not convert to date");
+}
+
+
+void v8::StringObject::CheckCast(v8::Value* that) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::StringObject::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->HasSpecificClassOf(isolate->heap()->String_string()),
+ "v8::StringObject::Cast()",
+ "Could not convert to StringObject");
+}
+
+
+void v8::SymbolObject::CheckCast(v8::Value* that) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::SymbolObject::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->HasSpecificClassOf(isolate->heap()->Symbol_string()),
+ "v8::SymbolObject::Cast()",
+ "Could not convert to SymbolObject");
+}
+
+
+void v8::NumberObject::CheckCast(v8::Value* that) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::NumberObject::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->HasSpecificClassOf(isolate->heap()->Number_string()),
+ "v8::NumberObject::Cast()",
+ "Could not convert to NumberObject");
+}
+
+
+void v8::BooleanObject::CheckCast(v8::Value* that) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::BooleanObject::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->HasSpecificClassOf(isolate->heap()->Boolean_string()),
+ "v8::BooleanObject::Cast()",
+ "Could not convert to BooleanObject");
+}
+
+
+void v8::RegExp::CheckCast(v8::Value* that) {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::RegExp::Cast()")) return;
+ i::Handle<i::Object> obj = Utils::OpenHandle(that);
+ ApiCheck(obj->IsJSRegExp(),
+ "v8::RegExp::Cast()",
+ "Could not convert to regular expression");
+}
+
+
+bool Value::BooleanValue() const {
+ return Utils::OpenHandle(this)->BooleanValue();
+}
+
+
+double Value::NumberValue() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> num;
+ if (obj->IsNumber()) {
+ num = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::NumberValue()")) {
+ return i::OS::nan_value();
+ }
+ LOG_API(isolate, "NumberValue");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ num = i::Execution::ToNumber(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, i::OS::nan_value());
+ }
+ return num->Number();
+}
+
+
+int64_t Value::IntegerValue() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> num;
+ if (obj->IsNumber()) {
+ num = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::IntegerValue()")) return 0;
+ LOG_API(isolate, "IntegerValue");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ num = i::Execution::ToInteger(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, 0);
+ }
+ if (num->IsSmi()) {
+ return i::Smi::cast(*num)->value();
+ } else {
+ return static_cast<int64_t>(num->Number());
+ }
+}
+
+
+Local<Int32> Value::ToInt32() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> num;
+ if (obj->IsSmi()) {
+ num = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToInt32()")) return Local<Int32>();
+ LOG_API(isolate, "ToInt32");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ num = i::Execution::ToInt32(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Int32>());
+ }
+ return ToApiHandle<Int32>(num);
+}
+
+
+Local<Uint32> Value::ToUint32() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> num;
+ if (obj->IsSmi()) {
+ num = obj;
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToUint32()")) return Local<Uint32>();
+ LOG_API(isolate, "ToUInt32");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ num = i::Execution::ToUint32(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Uint32>());
+ }
+ return ToApiHandle<Uint32>(num);
+}
+
+
+Local<Uint32> Value::ToArrayIndex() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsSmi()) {
+ if (i::Smi::cast(*obj)->value() >= 0) return Utils::Uint32ToLocal(obj);
+ return Local<Uint32>();
+ }
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::ToArrayIndex()")) return Local<Uint32>();
+ LOG_API(isolate, "ToArrayIndex");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> string_obj =
+ i::Execution::ToString(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Uint32>());
+ i::Handle<i::String> str = i::Handle<i::String>::cast(string_obj);
+ uint32_t index;
+ if (str->AsArrayIndex(&index)) {
+ i::Handle<i::Object> value;
+ if (index <= static_cast<uint32_t>(i::Smi::kMaxValue)) {
+ value = i::Handle<i::Object>(i::Smi::FromInt(index), isolate);
+ } else {
+ value = isolate->factory()->NewNumber(index);
+ }
+ return Utils::Uint32ToLocal(value);
+ }
+ return Local<Uint32>();
+}
+
+
+int32_t Value::Int32Value() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsSmi()) {
+ return i::Smi::cast(*obj)->value();
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::Int32Value()")) return 0;
+ LOG_API(isolate, "Int32Value (slow)");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> num =
+ i::Execution::ToInt32(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, 0);
+ if (num->IsSmi()) {
+ return i::Smi::cast(*num)->value();
+ } else {
+ return static_cast<int32_t>(num->Number());
+ }
+ }
+}
+
+
+bool Value::Equals(Handle<Value> that) const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::Equals()")
+ || EmptyCheck("v8::Value::Equals()", this)
+ || EmptyCheck("v8::Value::Equals()", that)) {
+ return false;
+ }
+ LOG_API(isolate, "Equals");
+ ENTER_V8(isolate);
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> other = Utils::OpenHandle(*that);
+ // If both obj and other are JSObjects, we'd better compare by identity
+ // immediately when going into JS builtin. The reason is Invoke
+ // would overwrite global object receiver with global proxy.
+ if (obj->IsJSObject() && other->IsJSObject()) {
+ return *obj == *other;
+ }
+ i::Handle<i::Object> args[] = { other };
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> result =
+ CallV8HeapFunction("EQUALS", obj, ARRAY_SIZE(args), args,
+ &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return *result == i::Smi::FromInt(i::EQUAL);
+}
+
+
+bool Value::StrictEquals(Handle<Value> that) const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::StrictEquals()")
+ || EmptyCheck("v8::Value::StrictEquals()", this)
+ || EmptyCheck("v8::Value::StrictEquals()", that)) {
+ return false;
+ }
+ LOG_API(isolate, "StrictEquals");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> other = Utils::OpenHandle(*that);
+ // Must check HeapNumber first, since NaN !== NaN.
+ if (obj->IsHeapNumber()) {
+ if (!other->IsNumber()) return false;
+ double x = obj->Number();
+ double y = other->Number();
+ // Must check explicitly for NaN:s on Windows, but -0 works fine.
+ return x == y && !std::isnan(x) && !std::isnan(y);
+ } else if (*obj == *other) { // Also covers Booleans.
+ return true;
+ } else if (obj->IsSmi()) {
+ return other->IsNumber() && obj->Number() == other->Number();
+ } else if (obj->IsString()) {
+ return other->IsString() &&
+ i::String::cast(*obj)->Equals(i::String::cast(*other));
+ } else if (obj->IsUndefined() || obj->IsUndetectableObject()) {
+ return other->IsUndefined() || other->IsUndetectableObject();
+ } else {
+ return false;
+ }
+}
+
+
+uint32_t Value::Uint32Value() const {
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsSmi()) {
+ return i::Smi::cast(*obj)->value();
+ } else {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Value::Uint32Value()")) return 0;
+ LOG_API(isolate, "Uint32Value");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> num =
+ i::Execution::ToUint32(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, 0);
+ if (num->IsSmi()) {
+ return i::Smi::cast(*num)->value();
+ } else {
+ return static_cast<uint32_t>(num->Number());
+ }
+ }
+}
+
+
+bool v8::Object::Set(v8::Handle<Value> key, v8::Handle<Value> value,
+ v8::PropertyAttribute attribs) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::Set()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::Object> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+ i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> obj = i::SetProperty(
+ isolate,
+ self,
+ key_obj,
+ value_obj,
+ static_cast<PropertyAttributes>(attribs),
+ i::kNonStrictMode);
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return true;
+}
+
+
+bool v8::Object::Set(uint32_t index, v8::Handle<Value> value) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::Set()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ 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::JSObject::SetElement(
+ self,
+ index,
+ value_obj,
+ NONE,
+ i::kNonStrictMode);
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return true;
+}
+
+
+bool v8::Object::ForceSet(v8::Handle<Value> key,
+ v8::Handle<Value> value,
+ v8::PropertyAttribute attribs) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::ForceSet()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+ i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> obj = i::ForceSetProperty(
+ self,
+ key_obj,
+ value_obj,
+ static_cast<PropertyAttributes>(attribs));
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return true;
+}
+
+
+bool v8::Object::ForceDelete(v8::Handle<Value> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::ForceDelete()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+
+ // When deleting a property on the global object using ForceDelete
+ // deoptimize all functions as optimized code does not check for the hole
+ // value with DontDelete properties. We have to deoptimize all contexts
+ // because of possible cross-context inlined functions.
+ if (self->IsJSGlobalProxy() || self->IsGlobalObject()) {
+ i::Deoptimizer::DeoptimizeAll(isolate);
+ }
+
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> obj = i::ForceDeleteProperty(self, key_obj);
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return obj->IsTrue();
+}
+
+
+Local<Value> v8::Object::Get(v8::Handle<Value> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::Get()", return Local<v8::Value>());
+ ENTER_V8(isolate);
+ i::Handle<i::Object> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> result = i::GetProperty(isolate, self, key_obj);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+ return Utils::ToLocal(result);
+}
+
+
+Local<Value> v8::Object::Get(uint32_t index) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::Get()", return Local<v8::Value>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ EXCEPTION_PREAMBLE(isolate);
+ 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);
+}
+
+
+PropertyAttribute v8::Object::GetPropertyAttributes(v8::Handle<Value> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetPropertyAttribute()",
+ return static_cast<PropertyAttribute>(NONE));
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+ if (!key_obj->IsName()) {
+ EXCEPTION_PREAMBLE(isolate);
+ key_obj = i::Execution::ToString(key_obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, static_cast<PropertyAttribute>(NONE));
+ }
+ i::Handle<i::Name> key_name = i::Handle<i::Name>::cast(key_obj);
+ PropertyAttributes result = self->GetPropertyAttribute(*key_name);
+ if (result == ABSENT) return static_cast<PropertyAttribute>(NONE);
+ return static_cast<PropertyAttribute>(result);
+}
+
+
+Local<Value> v8::Object::GetPrototype() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetPrototype()",
+ return Local<v8::Value>());
+ ENTER_V8(isolate);
+ i::Handle<i::Object> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> result(self->GetPrototype(isolate), isolate);
+ return Utils::ToLocal(result);
+}
+
+
+bool v8::Object::SetPrototype(Handle<Value> value) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::SetPrototype()", return false);
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ // We do not allow exceptions thrown while setting the prototype
+ // to propagate outside.
+ TryCatch try_catch;
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> result = i::JSObject::SetPrototype(self, value_obj);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return true;
+}
+
+
+Local<Object> v8::Object::FindInstanceInPrototypeChain(
+ v8::Handle<FunctionTemplate> tmpl) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate,
+ "v8::Object::FindInstanceInPrototypeChain()",
+ return Local<v8::Object>());
+ ENTER_V8(isolate);
+ i::JSObject* object = *Utils::OpenHandle(this);
+ i::FunctionTemplateInfo* tmpl_info = *Utils::OpenHandle(*tmpl);
+ while (!object->IsInstanceOf(tmpl_info)) {
+ i::Object* prototype = object->GetPrototype();
+ if (!prototype->IsJSObject()) return Local<Object>();
+ object = i::JSObject::cast(prototype);
+ }
+ return Utils::ToLocal(i::Handle<i::JSObject>(object));
+}
+
+
+Local<Array> v8::Object::GetPropertyNames() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetPropertyNames()",
+ return Local<v8::Array>());
+ 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, &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.
+ i::Handle<i::FixedArray> elms = isolate->factory()->CopyFixedArray(value);
+ i::Handle<i::JSArray> result =
+ isolate->factory()->NewJSArrayWithElements(elms);
+ return Utils::ToLocal(scope.CloseAndEscape(result));
+}
+
+
+Local<Array> v8::Object::GetOwnPropertyNames() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetOwnPropertyNames()",
+ return Local<v8::Array>());
+ 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, &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.
+ i::Handle<i::FixedArray> elms = isolate->factory()->CopyFixedArray(value);
+ i::Handle<i::JSArray> result =
+ isolate->factory()->NewJSArrayWithElements(elms);
+ return Utils::ToLocal(scope.CloseAndEscape(result));
+}
+
+
+Local<String> v8::Object::ObjectProtoToString() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::ObjectProtoToString()",
+ return Local<v8::String>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+
+ i::Handle<i::Object> name(self->class_name(), isolate);
+
+ // Native implementation of Object.prototype.toString (v8natives.js):
+ // var c = %_ClassOf(this);
+ // if (c === 'Arguments') c = 'Object';
+ // return "[object " + c + "]";
+
+ if (!name->IsString()) {
+ return v8::String::New("[object ]");
+
+ } else {
+ i::Handle<i::String> class_name = i::Handle<i::String>::cast(name);
+ if (class_name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("Arguments"))) {
+ return v8::String::New("[object Object]");
+
+ } else {
+ const char* prefix = "[object ";
+ Local<String> str = Utils::ToLocal(class_name);
+ const char* postfix = "]";
+
+ int prefix_len = i::StrLength(prefix);
+ int str_len = str->Utf8Length();
+ int postfix_len = i::StrLength(postfix);
+
+ int buf_len = prefix_len + str_len + postfix_len;
+ i::ScopedVector<char> buf(buf_len);
+
+ // Write prefix.
+ char* ptr = buf.start();
+ i::OS::MemCopy(ptr, prefix, prefix_len * v8::internal::kCharSize);
+ ptr += prefix_len;
+
+ // Write real content.
+ str->WriteUtf8(ptr, str_len);
+ ptr += str_len;
+
+ // Write postfix.
+ i::OS::MemCopy(ptr, postfix, postfix_len * v8::internal::kCharSize);
+
+ // Copy the buffer into a heap-allocated string and return it.
+ Local<String> result = v8::String::New(buf.start(), buf_len);
+ return result;
+ }
+ }
+}
+
+
+Local<Value> v8::Object::GetConstructor() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetConstructor()",
+ return Local<v8::Function>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> constructor(self->GetConstructor(), isolate);
+ return Utils::ToLocal(constructor);
+}
+
+
+Local<String> v8::Object::GetConstructorName() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetConstructorName()",
+ return Local<v8::String>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::String> name(self->constructor_name());
+ return Utils::ToLocal(name);
+}
+
+
+bool v8::Object::Delete(v8::Handle<Value> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::Delete()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> obj = i::DeleteProperty(self, key_obj);
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return obj->IsTrue();
+}
+
+
+bool v8::Object::Has(v8::Handle<Value> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::Has()", return false);
+ ENTER_V8(isolate);
+ i::Handle<i::JSReceiver> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> obj = i::HasProperty(self, key_obj);
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, false);
+ return obj->IsTrue();
+}
+
+
+bool v8::Object::Delete(uint32_t index) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::DeleteProperty()",
+ return false);
+ ENTER_V8(isolate);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ return i::JSObject::DeleteElement(self, index)->IsTrue();
+}
+
+
+bool v8::Object::Has(uint32_t index) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::HasProperty()", return false);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ return self->HasElement(index);
+}
+
+
+template<typename Setter, typename Getter, typename Data>
+static inline bool ObjectSetAccessor(Object* obj,
+ Handle<String> name,
+ Setter getter,
+ Getter setter,
+ Data data,
+ AccessControl settings,
+ PropertyAttribute attributes) {
+ i::Isolate* isolate = Utils::OpenHandle(obj)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::SetAccessor()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ v8::Handle<AccessorSignature> signature;
+ i::Handle<i::AccessorInfo> info = MakeAccessorInfo(
+ name, getter, setter, data, settings, attributes, signature);
+ if (info.is_null()) return false;
+ bool fast = Utils::OpenHandle(obj)->HasFastProperties();
+ i::Handle<i::Object> result = i::SetAccessor(Utils::OpenHandle(obj), info);
+ if (result.is_null() || result->IsUndefined()) return false;
+ if (fast) i::JSObject::TransformToFastProperties(Utils::OpenHandle(obj), 0);
+ return true;
+}
+
+
+bool Object::SetAccessor(Handle<String> name,
+ AccessorGetter getter,
+ AccessorSetter setter,
+ v8::Handle<Value> data,
+ AccessControl settings,
+ PropertyAttribute attributes) {
+ return ObjectSetAccessor(
+ this, name, getter, setter, data, settings, attributes);
+}
+
+
+bool Object::SetAccessor(Handle<String> name,
+ AccessorGetterCallback getter,
+ AccessorSetterCallback setter,
+ v8::Handle<Value> data,
+ AccessControl settings,
+ PropertyAttribute attributes) {
+ return ObjectSetAccessor(
+ this, name, getter, setter, data, settings, attributes);
+}
+
+
+bool Object::SetAccessor(Handle<String> name,
+ Handle<DeclaredAccessorDescriptor> descriptor,
+ AccessControl settings,
+ PropertyAttribute attributes) {
+ void* null = NULL;
+ return ObjectSetAccessor(
+ this, name, descriptor, null, null, settings, attributes);
+}
+
+
+bool v8::Object::HasOwnProperty(Handle<String> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::HasOwnProperty()",
+ return false);
+ return Utils::OpenHandle(this)->HasLocalProperty(
+ *Utils::OpenHandle(*key));
+}
+
+
+bool v8::Object::HasRealNamedProperty(Handle<String> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::HasRealNamedProperty()",
+ return false);
+ return Utils::OpenHandle(this)->HasRealNamedProperty(
+ isolate,
+ *Utils::OpenHandle(*key));
+}
+
+
+bool v8::Object::HasRealIndexedProperty(uint32_t index) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::HasRealIndexedProperty()",
+ return false);
+ return Utils::OpenHandle(this)->HasRealElementProperty(isolate, index);
+}
+
+
+bool v8::Object::HasRealNamedCallbackProperty(Handle<String> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate,
+ "v8::Object::HasRealNamedCallbackProperty()",
+ return false);
+ ENTER_V8(isolate);
+ return Utils::OpenHandle(this)->HasRealNamedCallbackProperty(
+ isolate,
+ *Utils::OpenHandle(*key));
+}
+
+
+bool v8::Object::HasNamedLookupInterceptor() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::HasNamedLookupInterceptor()",
+ return false);
+ return Utils::OpenHandle(this)->HasNamedInterceptor();
+}
+
+
+bool v8::Object::HasIndexedLookupInterceptor() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::HasIndexedLookupInterceptor()",
+ return false);
+ return Utils::OpenHandle(this)->HasIndexedInterceptor();
+}
+
+
+static Local<Value> GetPropertyByLookup(i::Isolate* isolate,
+ i::Handle<i::JSObject> receiver,
+ i::Handle<i::String> name,
+ i::LookupResult* lookup) {
+ if (!lookup->IsProperty()) {
+ // No real property was found.
+ return Local<Value>();
+ }
+
+ // If the property being looked up is a callback, it can throw
+ // an exception.
+ EXCEPTION_PREAMBLE(isolate);
+ 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>());
+
+ return Utils::ToLocal(result);
+}
+
+
+Local<Value> v8::Object::GetRealNamedPropertyInPrototypeChain(
+ Handle<String> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate,
+ "v8::Object::GetRealNamedPropertyInPrototypeChain()",
+ return Local<Value>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self_obj = Utils::OpenHandle(this);
+ i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
+ i::LookupResult lookup(isolate);
+ self_obj->LookupRealNamedPropertyInPrototypes(*key_obj, &lookup);
+ return GetPropertyByLookup(isolate, self_obj, key_obj, &lookup);
+}
+
+
+Local<Value> v8::Object::GetRealNamedProperty(Handle<String> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetRealNamedProperty()",
+ return Local<Value>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self_obj = Utils::OpenHandle(this);
+ i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
+ i::LookupResult lookup(isolate);
+ self_obj->LookupRealNamedProperty(*key_obj, &lookup);
+ return GetPropertyByLookup(isolate, self_obj, key_obj, &lookup);
+}
+
+
+// Turns on access checks by copying the map and setting the check flag.
+// Because the object gets a new map, existing inline cache caching
+// the old map of this object will fail.
+void v8::Object::TurnOnAccessCheck() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::TurnOnAccessCheck()", return);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+
+ // When turning on access checks for a global object deoptimize all functions
+ // as optimized code does not always handle access checks.
+ i::Deoptimizer::DeoptimizeGlobalObject(*obj);
+
+ i::Handle<i::Map> new_map =
+ isolate->factory()->CopyMap(i::Handle<i::Map>(obj->map()));
+ new_map->set_is_access_check_needed(true);
+ obj->set_map(*new_map);
+}
+
+
+bool v8::Object::IsDirty() {
+ return Utils::OpenHandle(this)->IsDirty();
+}
+
+
+Local<v8::Object> v8::Object::Clone() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::Clone()", return Local<Object>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::JSObject> result = i::Copy(self);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Object>());
+ return Utils::ToLocal(result);
+}
+
+
+static i::Context* GetCreationContext(i::JSObject* object) {
+ i::Object* constructor = object->map()->constructor();
+ i::JSFunction* function;
+ if (!constructor->IsJSFunction()) {
+ // Functions have null as a constructor,
+ // but any JSFunction knows its context immediately.
+ ASSERT(object->IsJSFunction());
+ function = i::JSFunction::cast(object);
+ } else {
+ function = i::JSFunction::cast(constructor);
+ }
+ return function->context()->native_context();
+}
+
+
+Local<v8::Context> v8::Object::CreationContext() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate,
+ "v8::Object::CreationContext()", return Local<v8::Context>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Context* context = GetCreationContext(*self);
+ return Utils::ToLocal(i::Handle<i::Context>(context));
+}
+
+
+int v8::Object::GetIdentityHash() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetIdentityHash()", return 0);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ return i::JSObject::GetIdentityHash(self);
+}
+
+
+bool v8::Object::SetHiddenValue(v8::Handle<v8::String> key,
+ v8::Handle<v8::Value> value) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::SetHiddenValue()", return false);
+ if (value.IsEmpty()) return DeleteHiddenValue(key);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
+ i::Handle<i::String> key_string =
+ isolate->factory()->InternalizeString(key_obj);
+ i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ i::Handle<i::Object> result =
+ i::JSObject::SetHiddenProperty(self, key_string, value_obj);
+ return *result == *self;
+}
+
+
+v8::Local<v8::Value> v8::Object::GetHiddenValue(v8::Handle<v8::String> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::GetHiddenValue()",
+ return Local<v8::Value>());
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
+ i::Handle<i::String> key_string =
+ isolate->factory()->InternalizeString(key_obj);
+ i::Handle<i::Object> result(self->GetHiddenProperty(*key_string), isolate);
+ if (result->IsTheHole()) return v8::Local<v8::Value>();
+ return Utils::ToLocal(result);
+}
+
+
+bool v8::Object::DeleteHiddenValue(v8::Handle<v8::String> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::DeleteHiddenValue()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
+ i::Handle<i::String> key_string =
+ isolate->factory()->InternalizeString(key_obj);
+ self->DeleteHiddenProperty(*key_string);
+ return true;
+}
+
+
+namespace {
+
+static i::ElementsKind GetElementsKindFromExternalArrayType(
+ ExternalArrayType array_type) {
+ switch (array_type) {
+ case kExternalByteArray:
+ return i::EXTERNAL_BYTE_ELEMENTS;
+ break;
+ case kExternalUnsignedByteArray:
+ return i::EXTERNAL_UNSIGNED_BYTE_ELEMENTS;
+ break;
+ case kExternalShortArray:
+ return i::EXTERNAL_SHORT_ELEMENTS;
+ break;
+ case kExternalUnsignedShortArray:
+ return i::EXTERNAL_UNSIGNED_SHORT_ELEMENTS;
+ break;
+ case kExternalIntArray:
+ return i::EXTERNAL_INT_ELEMENTS;
+ break;
+ case kExternalUnsignedIntArray:
+ return i::EXTERNAL_UNSIGNED_INT_ELEMENTS;
+ break;
+ case kExternalFloatArray:
+ return i::EXTERNAL_FLOAT_ELEMENTS;
+ break;
+ case kExternalDoubleArray:
+ return i::EXTERNAL_DOUBLE_ELEMENTS;
+ break;
+ case kExternalPixelArray:
+ return i::EXTERNAL_PIXEL_ELEMENTS;
+ break;
+ }
+ UNREACHABLE();
+ return i::DICTIONARY_ELEMENTS;
+}
+
+
+void PrepareExternalArrayElements(i::Handle<i::JSObject> object,
+ void* data,
+ ExternalArrayType array_type,
+ int length) {
+ i::Isolate* isolate = object->GetIsolate();
+ i::Handle<i::ExternalArray> array =
+ isolate->factory()->NewExternalArray(length, array_type, data);
+
+ i::Handle<i::Map> external_array_map =
+ isolate->factory()->GetElementsTransitionMap(
+ object,
+ GetElementsKindFromExternalArrayType(array_type));
+
+ object->set_map(*external_array_map);
+ object->set_elements(*array);
+}
+
+} // namespace
+
+
+void v8::Object::SetIndexedPropertiesToPixelData(uint8_t* data, int length) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::SetElementsToPixelData()", return);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ if (!ApiCheck(length >= 0 && length <= i::ExternalPixelArray::kMaxLength,
+ "v8::Object::SetIndexedPropertiesToPixelData()",
+ "length exceeds max acceptable value")) {
+ return;
+ }
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ if (!ApiCheck(!self->IsJSArray(),
+ "v8::Object::SetIndexedPropertiesToPixelData()",
+ "JSArray is not supported")) {
+ return;
+ }
+ PrepareExternalArrayElements(self, data, kExternalPixelArray, length);
+}
+
+
+bool v8::Object::HasIndexedPropertiesInPixelData() {
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ ON_BAILOUT(self->GetIsolate(), "v8::HasIndexedPropertiesInPixelData()",
+ return false);
+ return self->HasExternalPixelElements();
+}
+
+
+uint8_t* v8::Object::GetIndexedPropertiesPixelData() {
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ ON_BAILOUT(self->GetIsolate(), "v8::GetIndexedPropertiesPixelData()",
+ return NULL);
+ if (self->HasExternalPixelElements()) {
+ return i::ExternalPixelArray::cast(self->elements())->
+ external_pixel_pointer();
+ } else {
+ return NULL;
+ }
+}
+
+
+int v8::Object::GetIndexedPropertiesPixelDataLength() {
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ ON_BAILOUT(self->GetIsolate(), "v8::GetIndexedPropertiesPixelDataLength()",
+ return -1);
+ if (self->HasExternalPixelElements()) {
+ return i::ExternalPixelArray::cast(self->elements())->length();
+ } else {
+ return -1;
+ }
+}
+
+
+void v8::Object::SetIndexedPropertiesToExternalArrayData(
+ void* data,
+ ExternalArrayType array_type,
+ int length) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::SetIndexedPropertiesToExternalArrayData()", return);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ if (!ApiCheck(length >= 0 && length <= i::ExternalArray::kMaxLength,
+ "v8::Object::SetIndexedPropertiesToExternalArrayData()",
+ "length exceeds max acceptable value")) {
+ return;
+ }
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ if (!ApiCheck(!self->IsJSArray(),
+ "v8::Object::SetIndexedPropertiesToExternalArrayData()",
+ "JSArray is not supported")) {
+ return;
+ }
+ PrepareExternalArrayElements(self, data, array_type, length);
+}
+
+
+bool v8::Object::HasIndexedPropertiesInExternalArrayData() {
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ ON_BAILOUT(self->GetIsolate(),
+ "v8::HasIndexedPropertiesInExternalArrayData()",
+ return false);
+ return self->HasExternalArrayElements();
+}
+
+
+void* v8::Object::GetIndexedPropertiesExternalArrayData() {
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ ON_BAILOUT(self->GetIsolate(),
+ "v8::GetIndexedPropertiesExternalArrayData()",
+ return NULL);
+ if (self->HasExternalArrayElements()) {
+ return i::ExternalArray::cast(self->elements())->external_pointer();
+ } else {
+ return NULL;
+ }
+}
+
+
+ExternalArrayType v8::Object::GetIndexedPropertiesExternalArrayDataType() {
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ ON_BAILOUT(self->GetIsolate(),
+ "v8::GetIndexedPropertiesExternalArrayDataType()",
+ return static_cast<ExternalArrayType>(-1));
+ switch (self->elements()->map()->instance_type()) {
+ case i::EXTERNAL_BYTE_ARRAY_TYPE:
+ return kExternalByteArray;
+ case i::EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ return kExternalUnsignedByteArray;
+ case i::EXTERNAL_SHORT_ARRAY_TYPE:
+ return kExternalShortArray;
+ case i::EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ return kExternalUnsignedShortArray;
+ case i::EXTERNAL_INT_ARRAY_TYPE:
+ return kExternalIntArray;
+ case i::EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ return kExternalUnsignedIntArray;
+ case i::EXTERNAL_FLOAT_ARRAY_TYPE:
+ return kExternalFloatArray;
+ case i::EXTERNAL_DOUBLE_ARRAY_TYPE:
+ return kExternalDoubleArray;
+ case i::EXTERNAL_PIXEL_ARRAY_TYPE:
+ return kExternalPixelArray;
+ default:
+ return static_cast<ExternalArrayType>(-1);
+ }
+}
+
+
+int v8::Object::GetIndexedPropertiesExternalArrayDataLength() {
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ ON_BAILOUT(self->GetIsolate(),
+ "v8::GetIndexedPropertiesExternalArrayDataLength()",
+ return 0);
+ if (self->HasExternalArrayElements()) {
+ return i::ExternalArray::cast(self->elements())->length();
+ } else {
+ return -1;
+ }
+}
+
+
+bool v8::Object::IsCallable() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::IsCallable()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ if (obj->IsJSFunction()) return true;
+ return i::Execution::GetFunctionDelegate(obj)->IsJSFunction();
+}
+
+
+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()",
+ return Local<v8::Value>());
+ LOG_API(isolate, "Object::CallAsFunction");
+ ENTER_V8(isolate);
+ i::Logger::TimerEventScope timer_scope(
+ isolate, i::Logger::TimerEventScope::v8_execute);
+ i::HandleScope scope(isolate);
+ 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::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);
+ } else {
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> delegate =
+ i::Execution::TryGetFunctionDelegate(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+ fun = i::Handle<i::JSFunction>::cast(delegate);
+ recv_obj = obj;
+ }
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> returned =
+ i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<Value>());
+ return Utils::ToLocal(scope.CloseAndEscape(returned));
+}
+
+
+Local<v8::Value> Object::CallAsConstructor(int argc,
+ v8::Handle<v8::Value> argv[]) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::CallAsConstructor()",
+ return Local<v8::Object>());
+ LOG_API(isolate, "Object::CallAsConstructor");
+ ENTER_V8(isolate);
+ i::Logger::TimerEventScope timer_scope(
+ isolate, i::Logger::TimerEventScope::v8_execute);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**));
+ 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_DO_CALLBACK(isolate, Local<v8::Object>());
+ return Utils::ToLocal(scope.CloseAndEscape(
+ i::Handle<i::JSObject>::cast(returned)));
+ }
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> delegate =
+ i::Execution::TryGetConstructorDelegate(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+ if (!delegate->IsUndefined()) {
+ i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(delegate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> returned =
+ i::Execution::Call(fun, obj, argc, args, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK_DO_CALLBACK(isolate, Local<v8::Object>());
+ ASSERT(!delegate->IsUndefined());
+ return Utils::ToLocal(scope.CloseAndEscape(returned));
+ }
+ return Local<v8::Object>();
+}
+
+
+Local<v8::Object> Function::NewInstance() const {
+ return NewInstance(0, NULL);
+}
+
+
+Local<v8::Object> Function::NewInstance(int argc,
+ v8::Handle<v8::Value> argv[]) const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Function::NewInstance()",
+ return Local<v8::Object>());
+ LOG_API(isolate, "Function::NewInstance");
+ ENTER_V8(isolate);
+ i::Logger::TimerEventScope timer_scope(
+ isolate, i::Logger::TimerEventScope::v8_execute);
+ HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Handle<i::JSFunction> function = Utils::OpenHandle(this);
+ STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**));
+ 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_DO_CALLBACK(isolate, Local<v8::Object>());
+ return scope.Close(Utils::ToLocal(i::Handle<i::JSObject>::cast(returned)));
+}
+
+
+Local<v8::Value> Function::Call(v8::Handle<v8::Object> recv, int argc,
+ v8::Handle<v8::Value> argv[]) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Function::Call()", return Local<v8::Value>());
+ LOG_API(isolate, "Function::Call");
+ ENTER_V8(isolate);
+ i::Logger::TimerEventScope timer_scope(
+ isolate, i::Logger::TimerEventScope::v8_execute);
+ i::Object* raw_result = NULL;
+ {
+ i::HandleScope scope(isolate);
+ 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::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_DO_CALLBACK(isolate, Local<Object>());
+ raw_result = *returned;
+ }
+ i::Handle<i::Object> result(raw_result, isolate);
+ return Utils::ToLocal(result);
+}
+
+
+void Function::SetName(v8::Handle<v8::String> name) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ENTER_V8(isolate);
+ USE(isolate);
+ i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
+ func->shared()->set_name(*Utils::OpenHandle(*name));
+}
+
+
+Handle<Value> Function::GetName() const {
+ i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
+ return Utils::ToLocal(i::Handle<i::Object>(func->shared()->name(),
+ func->GetIsolate()));
+}
+
+
+Handle<Value> Function::GetInferredName() const {
+ i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
+ return Utils::ToLocal(i::Handle<i::Object>(func->shared()->inferred_name(),
+ func->GetIsolate()));
+}
+
+
+ScriptOrigin Function::GetScriptOrigin() 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()));
+ i::Handle<i::Object> scriptName = GetScriptNameOrSourceURL(script);
+ v8::ScriptOrigin origin(
+ Utils::ToLocal(scriptName),
+ v8::Integer::New(script->line_offset()->value()),
+ v8::Integer::New(script->column_offset()->value()));
+ return origin;
+ }
+ return v8::ScriptOrigin(Handle<Value>());
+}
+
+
+const int Function::kLineOffsetNotFound = -1;
+
+
+int Function::GetScriptLineNumber() 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::GetScriptLineNumber(script, func->shared()->start_position());
+ }
+ return kLineOffsetNotFound;
+}
+
+
+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(), func->GetIsolate()));
+}
+
+
+int Function::ScriptId() const {
+ i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
+ if (!func->shared()->script()->IsScript()) return v8::Script::kNoScriptId;
+ i::Handle<i::Script> script(i::Script::cast(func->shared()->script()));
+ return script->id()->value();
+}
+
+
+int String::Length() const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ if (IsDeadCheck(str->GetIsolate(), "v8::String::Length()")) return 0;
+ return str->length();
+}
+
+
+bool String::IsOneByte() const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ if (IsDeadCheck(str->GetIsolate(), "v8::String::IsOneByte()")) {
+ return false;
+ }
+ return str->HasOnlyOneByteChars();
+}
+
+
+// Helpers for ContainsOnlyOneByteHelper
+template<size_t size> struct OneByteMask;
+template<> struct OneByteMask<4> {
+ static const uint32_t value = 0xFF00FF00;
+};
+template<> struct OneByteMask<8> {
+ static const uint64_t value = V8_2PART_UINT64_C(0xFF00FF00, FF00FF00);
+};
+static const uintptr_t kOneByteMask = OneByteMask<sizeof(uintptr_t)>::value;
+static const uintptr_t kAlignmentMask = sizeof(uintptr_t) - 1;
+static inline bool Unaligned(const uint16_t* chars) {
+ return reinterpret_cast<const uintptr_t>(chars) & kAlignmentMask;
+}
+
+
+static inline const uint16_t* Align(const uint16_t* chars) {
+ return reinterpret_cast<uint16_t*>(
+ reinterpret_cast<uintptr_t>(chars) & ~kAlignmentMask);
+}
+
+class ContainsOnlyOneByteHelper {
+ public:
+ ContainsOnlyOneByteHelper() : is_one_byte_(true) {}
+ bool Check(i::String* string) {
+ i::ConsString* cons_string = i::String::VisitFlat(this, string, 0);
+ if (cons_string == NULL) return is_one_byte_;
+ return CheckCons(cons_string);
+ }
+ void VisitOneByteString(const uint8_t* chars, int length) {
+ // Nothing to do.
+ }
+ void VisitTwoByteString(const uint16_t* chars, int length) {
+ // Accumulated bits.
+ uintptr_t acc = 0;
+ // Align to uintptr_t.
+ const uint16_t* end = chars + length;
+ while (Unaligned(chars) && chars != end) {
+ acc |= *chars++;
+ }
+ // Read word aligned in blocks,
+ // checking the return value at the end of each block.
+ const uint16_t* aligned_end = Align(end);
+ const int increment = sizeof(uintptr_t)/sizeof(uint16_t);
+ const int inner_loops = 16;
+ while (chars + inner_loops*increment < aligned_end) {
+ for (int i = 0; i < inner_loops; i++) {
+ acc |= *reinterpret_cast<const uintptr_t*>(chars);
+ chars += increment;
+ }
+ // Check for early return.
+ if ((acc & kOneByteMask) != 0) {
+ is_one_byte_ = false;
+ return;
+ }
+ }
+ // Read the rest.
+ while (chars != end) {
+ acc |= *chars++;
+ }
+ // Check result.
+ if ((acc & kOneByteMask) != 0) is_one_byte_ = false;
+ }
+
+ private:
+ bool CheckCons(i::ConsString* cons_string) {
+ while (true) {
+ // Check left side if flat.
+ i::String* left = cons_string->first();
+ i::ConsString* left_as_cons =
+ i::String::VisitFlat(this, left, 0);
+ if (!is_one_byte_) return false;
+ // Check right side if flat.
+ i::String* right = cons_string->second();
+ i::ConsString* right_as_cons =
+ i::String::VisitFlat(this, right, 0);
+ if (!is_one_byte_) return false;
+ // Standard recurse/iterate trick.
+ if (left_as_cons != NULL && right_as_cons != NULL) {
+ if (left->length() < right->length()) {
+ CheckCons(left_as_cons);
+ cons_string = right_as_cons;
+ } else {
+ CheckCons(right_as_cons);
+ cons_string = left_as_cons;
+ }
+ // Check fast return.
+ if (!is_one_byte_) return false;
+ continue;
+ }
+ // Descend left in place.
+ if (left_as_cons != NULL) {
+ cons_string = left_as_cons;
+ continue;
+ }
+ // Descend right in place.
+ if (right_as_cons != NULL) {
+ cons_string = right_as_cons;
+ continue;
+ }
+ // Terminate.
+ break;
+ }
+ return is_one_byte_;
+ }
+ bool is_one_byte_;
+ DISALLOW_COPY_AND_ASSIGN(ContainsOnlyOneByteHelper);
+};
+
+
+bool String::ContainsOnlyOneByte() const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ if (IsDeadCheck(str->GetIsolate(),
+ "v8::String::ContainsOnlyOneByte()")) {
+ return false;
+ }
+ if (str->HasOnlyOneByteChars()) return true;
+ ContainsOnlyOneByteHelper helper;
+ return helper.Check(*str);
+}
+
+
+class Utf8LengthHelper : public i::AllStatic {
+ public:
+ enum State {
+ kEndsWithLeadingSurrogate = 1 << 0,
+ kStartsWithTrailingSurrogate = 1 << 1,
+ kLeftmostEdgeIsCalculated = 1 << 2,
+ kRightmostEdgeIsCalculated = 1 << 3,
+ kLeftmostEdgeIsSurrogate = 1 << 4,
+ kRightmostEdgeIsSurrogate = 1 << 5
+ };
+
+ static const uint8_t kInitialState = 0;
+
+ static inline bool EndsWithSurrogate(uint8_t state) {
+ return state & kEndsWithLeadingSurrogate;
+ }
+
+ static inline bool StartsWithSurrogate(uint8_t state) {
+ return state & kStartsWithTrailingSurrogate;
+ }
+
+ class Visitor {
+ public:
+ inline explicit Visitor()
+ : utf8_length_(0),
+ state_(kInitialState) {}
+
+ void VisitOneByteString(const uint8_t* chars, int length) {
+ int utf8_length = 0;
+ // Add in length 1 for each non-ASCII character.
+ for (int i = 0; i < length; i++) {
+ utf8_length += *chars++ >> 7;
+ }
+ // Add in length 1 for each character.
+ utf8_length_ = utf8_length + length;
+ state_ = kInitialState;
+ }
+
+ void VisitTwoByteString(const uint16_t* chars, int length) {
+ int utf8_length = 0;
+ int last_character = unibrow::Utf16::kNoPreviousCharacter;
+ for (int i = 0; i < length; i++) {
+ uint16_t c = chars[i];
+ utf8_length += unibrow::Utf8::Length(c, last_character);
+ last_character = c;
+ }
+ utf8_length_ = utf8_length;
+ uint8_t state = 0;
+ if (unibrow::Utf16::IsTrailSurrogate(chars[0])) {
+ state |= kStartsWithTrailingSurrogate;
+ }
+ if (unibrow::Utf16::IsLeadSurrogate(chars[length-1])) {
+ state |= kEndsWithLeadingSurrogate;
+ }
+ state_ = state;
+ }
+
+ static i::ConsString* VisitFlat(i::String* string,
+ int* length,
+ uint8_t* state) {
+ Visitor visitor;
+ i::ConsString* cons_string = i::String::VisitFlat(&visitor, string);
+ *length = visitor.utf8_length_;
+ *state = visitor.state_;
+ return cons_string;
+ }
+
+ private:
+ int utf8_length_;
+ uint8_t state_;
+ DISALLOW_COPY_AND_ASSIGN(Visitor);
+ };
+
+ static inline void MergeLeafLeft(int* length,
+ uint8_t* state,
+ uint8_t leaf_state) {
+ bool edge_surrogate = StartsWithSurrogate(leaf_state);
+ if (!(*state & kLeftmostEdgeIsCalculated)) {
+ ASSERT(!(*state & kLeftmostEdgeIsSurrogate));
+ *state |= kLeftmostEdgeIsCalculated
+ | (edge_surrogate ? kLeftmostEdgeIsSurrogate : 0);
+ } else if (EndsWithSurrogate(*state) && edge_surrogate) {
+ *length -= unibrow::Utf8::kBytesSavedByCombiningSurrogates;
+ }
+ if (EndsWithSurrogate(leaf_state)) {
+ *state |= kEndsWithLeadingSurrogate;
+ } else {
+ *state &= ~kEndsWithLeadingSurrogate;
+ }
+ }
+
+ static inline void MergeLeafRight(int* length,
+ uint8_t* state,
+ uint8_t leaf_state) {
+ bool edge_surrogate = EndsWithSurrogate(leaf_state);
+ if (!(*state & kRightmostEdgeIsCalculated)) {
+ ASSERT(!(*state & kRightmostEdgeIsSurrogate));
+ *state |= (kRightmostEdgeIsCalculated
+ | (edge_surrogate ? kRightmostEdgeIsSurrogate : 0));
+ } else if (edge_surrogate && StartsWithSurrogate(*state)) {
+ *length -= unibrow::Utf8::kBytesSavedByCombiningSurrogates;
+ }
+ if (StartsWithSurrogate(leaf_state)) {
+ *state |= kStartsWithTrailingSurrogate;
+ } else {
+ *state &= ~kStartsWithTrailingSurrogate;
+ }
+ }
+
+ static inline void MergeTerminal(int* length,
+ uint8_t state,
+ uint8_t* state_out) {
+ ASSERT((state & kLeftmostEdgeIsCalculated) &&
+ (state & kRightmostEdgeIsCalculated));
+ if (EndsWithSurrogate(state) && StartsWithSurrogate(state)) {
+ *length -= unibrow::Utf8::kBytesSavedByCombiningSurrogates;
+ }
+ *state_out = kInitialState |
+ (state & kLeftmostEdgeIsSurrogate ? kStartsWithTrailingSurrogate : 0) |
+ (state & kRightmostEdgeIsSurrogate ? kEndsWithLeadingSurrogate : 0);
+ }
+
+ static int Calculate(i::ConsString* current, uint8_t* state_out) {
+ using namespace internal;
+ int total_length = 0;
+ uint8_t state = kInitialState;
+ while (true) {
+ i::String* left = current->first();
+ i::String* right = current->second();
+ uint8_t right_leaf_state;
+ uint8_t left_leaf_state;
+ int leaf_length;
+ ConsString* left_as_cons =
+ Visitor::VisitFlat(left, &leaf_length, &left_leaf_state);
+ if (left_as_cons == NULL) {
+ total_length += leaf_length;
+ MergeLeafLeft(&total_length, &state, left_leaf_state);
+ }
+ ConsString* right_as_cons =
+ Visitor::VisitFlat(right, &leaf_length, &right_leaf_state);
+ if (right_as_cons == NULL) {
+ total_length += leaf_length;
+ MergeLeafRight(&total_length, &state, right_leaf_state);
+ if (left_as_cons != NULL) {
+ // 1 Leaf node. Descend in place.
+ current = left_as_cons;
+ continue;
+ } else {
+ // Terminal node.
+ MergeTerminal(&total_length, state, state_out);
+ return total_length;
+ }
+ } else if (left_as_cons == NULL) {
+ // 1 Leaf node. Descend in place.
+ current = right_as_cons;
+ continue;
+ }
+ // Both strings are ConsStrings.
+ // Recurse on smallest.
+ if (left->length() < right->length()) {
+ total_length += Calculate(left_as_cons, &left_leaf_state);
+ MergeLeafLeft(&total_length, &state, left_leaf_state);
+ current = right_as_cons;
+ } else {
+ total_length += Calculate(right_as_cons, &right_leaf_state);
+ MergeLeafRight(&total_length, &state, right_leaf_state);
+ current = left_as_cons;
+ }
+ }
+ UNREACHABLE();
+ return 0;
+ }
+
+ static inline int Calculate(i::ConsString* current) {
+ uint8_t state = kInitialState;
+ return Calculate(current, &state);
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Utf8LengthHelper);
+};
+
+
+static int Utf8Length(i::String* str, i::Isolate* isolate) {
+ int length = str->length();
+ if (length == 0) return 0;
+ uint8_t state;
+ i::ConsString* cons_string =
+ Utf8LengthHelper::Visitor::VisitFlat(str, &length, &state);
+ if (cons_string == NULL) return length;
+ return Utf8LengthHelper::Calculate(cons_string);
+}
+
+
+int String::Utf8Length() const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ i::Isolate* isolate = str->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::String::Utf8Length()")) return 0;
+ return v8::Utf8Length(*str, isolate);
+}
+
+
+class Utf8WriterVisitor {
+ public:
+ Utf8WriterVisitor(
+ char* buffer, int capacity, bool skip_capacity_check)
+ : early_termination_(false),
+ last_character_(unibrow::Utf16::kNoPreviousCharacter),
+ buffer_(buffer),
+ start_(buffer),
+ capacity_(capacity),
+ skip_capacity_check_(capacity == -1 || skip_capacity_check),
+ utf16_chars_read_(0) {
+ }
+
+ static int WriteEndCharacter(uint16_t character,
+ int last_character,
+ int remaining,
+ char* const buffer) {
+ using namespace unibrow;
+ ASSERT(remaining > 0);
+ // We can't use a local buffer here because Encode needs to modify
+ // previous characters in the stream. We know, however, that
+ // exactly one character will be advanced.
+ if (Utf16::IsTrailSurrogate(character) &&
+ Utf16::IsLeadSurrogate(last_character)) {
+ int written = Utf8::Encode(buffer, character, last_character);
+ ASSERT(written == 1);
+ return written;
+ }
+ // Use a scratch buffer to check the required characters.
+ char temp_buffer[Utf8::kMaxEncodedSize];
+ // Can't encode using last_character as gcc has array bounds issues.
+ int written = Utf8::Encode(temp_buffer,
+ character,
+ Utf16::kNoPreviousCharacter);
+ // Won't fit.
+ if (written > remaining) return 0;
+ // Copy over the character from temp_buffer.
+ for (int j = 0; j < written; j++) {
+ buffer[j] = temp_buffer[j];
+ }
+ return written;
+ }
+
+ template<typename Char>
+ void Visit(const Char* chars, const int length) {
+ using namespace unibrow;
+ ASSERT(!early_termination_);
+ if (length == 0) return;
+ // Copy state to stack.
+ char* buffer = buffer_;
+ int last_character =
+ sizeof(Char) == 1 ? Utf16::kNoPreviousCharacter : last_character_;
+ int i = 0;
+ // Do a fast loop where there is no exit capacity check.
+ while (true) {
+ int fast_length;
+ if (skip_capacity_check_) {
+ fast_length = length;
+ } else {
+ int remaining_capacity = capacity_ - static_cast<int>(buffer - start_);
+ // Need enough space to write everything but one character.
+ STATIC_ASSERT(Utf16::kMaxExtraUtf8BytesForOneUtf16CodeUnit == 3);
+ int max_size_per_char = sizeof(Char) == 1 ? 2 : 3;
+ int writable_length =
+ (remaining_capacity - max_size_per_char)/max_size_per_char;
+ // Need to drop into slow loop.
+ if (writable_length <= 0) break;
+ fast_length = i + writable_length;
+ if (fast_length > length) fast_length = length;
+ }
+ // Write the characters to the stream.
+ if (sizeof(Char) == 1) {
+ for (; i < fast_length; i++) {
+ buffer +=
+ Utf8::EncodeOneByte(buffer, static_cast<uint8_t>(*chars++));
+ ASSERT(capacity_ == -1 || (buffer - start_) <= capacity_);
+ }
+ } else {
+ for (; i < fast_length; i++) {
+ uint16_t character = *chars++;
+ buffer += Utf8::Encode(buffer, character, last_character);
+ last_character = character;
+ ASSERT(capacity_ == -1 || (buffer - start_) <= capacity_);
+ }
+ }
+ // Array is fully written. Exit.
+ if (fast_length == length) {
+ // Write state back out to object.
+ last_character_ = last_character;
+ buffer_ = buffer;
+ utf16_chars_read_ += length;
+ return;
+ }
+ }
+ ASSERT(!skip_capacity_check_);
+ // Slow loop. Must check capacity on each iteration.
+ int remaining_capacity = capacity_ - static_cast<int>(buffer - start_);
+ ASSERT(remaining_capacity >= 0);
+ for (; i < length && remaining_capacity > 0; i++) {
+ uint16_t character = *chars++;
+ int written = WriteEndCharacter(character,
+ last_character,
+ remaining_capacity,
+ buffer);
+ if (written == 0) {
+ early_termination_ = true;
+ break;
+ }
+ buffer += written;
+ remaining_capacity -= written;
+ last_character = character;
+ }
+ // Write state back out to object.
+ last_character_ = last_character;
+ buffer_ = buffer;
+ utf16_chars_read_ += i;
+ }
+
+ inline bool IsDone() {
+ return early_termination_;
+ }
+
+ inline void VisitOneByteString(const uint8_t* chars, int length) {
+ Visit(chars, length);
+ }
+
+ inline void VisitTwoByteString(const uint16_t* chars, int length) {
+ Visit(chars, length);
+ }
+
+ int CompleteWrite(bool write_null, int* utf16_chars_read_out) {
+ // Write out number of utf16 characters written to the stream.
+ if (utf16_chars_read_out != NULL) {
+ *utf16_chars_read_out = utf16_chars_read_;
+ }
+ // Only null terminate if all of the string was written and there's space.
+ if (write_null &&
+ !early_termination_ &&
+ (capacity_ == -1 || (buffer_ - start_) < capacity_)) {
+ *buffer_++ = '\0';
+ }
+ return static_cast<int>(buffer_ - start_);
+ }
+
+ private:
+ bool early_termination_;
+ int last_character_;
+ char* buffer_;
+ char* const start_;
+ int capacity_;
+ bool const skip_capacity_check_;
+ int utf16_chars_read_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Utf8WriterVisitor);
+};
+
+
+static bool RecursivelySerializeToUtf8(i::String* current,
+ Utf8WriterVisitor* writer,
+ int recursion_budget) {
+ while (!writer->IsDone()) {
+ i::ConsString* cons_string = i::String::VisitFlat(writer, current);
+ if (cons_string == NULL) return true; // Leaf node.
+ if (recursion_budget <= 0) return false;
+ // Must write the left branch first.
+ i::String* first = cons_string->first();
+ bool success = RecursivelySerializeToUtf8(first,
+ writer,
+ recursion_budget - 1);
+ if (!success) return false;
+ // Inline tail recurse for right branch.
+ current = cons_string->second();
+ }
+ return true;
+}
+
+
+int String::WriteUtf8(char* buffer,
+ int capacity,
+ int* nchars_ref,
+ int options) const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::String::WriteUtf8()")) return 0;
+ LOG_API(isolate, "String::WriteUtf8");
+ ENTER_V8(isolate);
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ if (options & HINT_MANY_WRITES_EXPECTED) {
+ FlattenString(str); // Flatten the string for efficiency.
+ }
+ const int string_length = str->length();
+ bool write_null = !(options & NO_NULL_TERMINATION);
+ // First check if we can just write the string without checking capacity.
+ if (capacity == -1 || capacity / 3 >= string_length) {
+ Utf8WriterVisitor writer(buffer, capacity, true);
+ const int kMaxRecursion = 100;
+ bool success = RecursivelySerializeToUtf8(*str, &writer, kMaxRecursion);
+ if (success) return writer.CompleteWrite(write_null, nchars_ref);
+ } else if (capacity >= string_length) {
+ // First check that the buffer is large enough.
+ int utf8_bytes = v8::Utf8Length(*str, str->GetIsolate());
+ if (utf8_bytes <= capacity) {
+ // ASCII fast path.
+ if (utf8_bytes == string_length) {
+ WriteOneByte(reinterpret_cast<uint8_t*>(buffer), 0, capacity, options);
+ if (nchars_ref != NULL) *nchars_ref = string_length;
+ if (write_null && (utf8_bytes+1 <= capacity)) {
+ return string_length + 1;
+ }
+ return string_length;
+ }
+ if (write_null && (utf8_bytes+1 > capacity)) {
+ options |= NO_NULL_TERMINATION;
+ }
+ // Recurse once without a capacity limit.
+ // This will get into the first branch above.
+ // TODO(dcarney) Check max left rec. in Utf8Length and fall through.
+ return WriteUtf8(buffer, -1, nchars_ref, options);
+ }
+ }
+ // Recursive slow path can potentially be unreasonable slow. Flatten.
+ str = FlattenGetString(str);
+ Utf8WriterVisitor writer(buffer, capacity, false);
+ i::String::VisitFlat(&writer, *str);
+ return writer.CompleteWrite(write_null, nchars_ref);
+}
+
+
+int String::WriteAscii(char* buffer,
+ int start,
+ int length,
+ int options) const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::String::WriteAscii()")) return 0;
+ LOG_API(isolate, "String::WriteAscii");
+ ENTER_V8(isolate);
+ ASSERT(start >= 0 && length >= -1);
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ isolate->string_tracker()->RecordWrite(str);
+ if (options & HINT_MANY_WRITES_EXPECTED) {
+ FlattenString(str); // Flatten the string for efficiency.
+ }
+
+ int end = length;
+ if ((length == -1) || (length > str->length() - start)) {
+ end = str->length() - start;
+ }
+ if (end < 0) return 0;
+ i::StringCharacterStream write_stream(*str, isolate->write_iterator(), start);
+ int i;
+ for (i = 0; i < end; i++) {
+ char c = static_cast<char>(write_stream.GetNext());
+ if (c == '\0' && !(options & PRESERVE_ASCII_NULL)) c = ' ';
+ buffer[i] = c;
+ }
+ if (!(options & NO_NULL_TERMINATION) && (length == -1 || i < length)) {
+ buffer[i] = '\0';
+ }
+ return i;
+}
+
+
+template<typename CharType>
+static inline int WriteHelper(const String* string,
+ CharType* buffer,
+ int start,
+ int length,
+ int options) {
+ i::Isolate* isolate = Utils::OpenHandle(string)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::String::Write()")) return 0;
+ LOG_API(isolate, "String::Write");
+ ENTER_V8(isolate);
+ ASSERT(start >= 0 && length >= -1);
+ i::Handle<i::String> str = Utils::OpenHandle(string);
+ isolate->string_tracker()->RecordWrite(str);
+ if (options & String::HINT_MANY_WRITES_EXPECTED) {
+ // Flatten the string for efficiency. This applies whether we are
+ // using StringCharacterStream or Get(i) to access the characters.
+ FlattenString(str);
+ }
+ int end = start + length;
+ if ((length == -1) || (length > str->length() - start) )
+ end = str->length();
+ if (end < 0) return 0;
+ i::String::WriteToFlat(*str, buffer, start, end);
+ if (!(options & String::NO_NULL_TERMINATION) &&
+ (length == -1 || end - start < length)) {
+ buffer[end - start] = '\0';
+ }
+ return end - start;
+}
+
+
+int String::WriteOneByte(uint8_t* buffer,
+ int start,
+ int length,
+ int options) const {
+ return WriteHelper(this, buffer, start, length, options);
+}
+
+
+int String::Write(uint16_t* buffer,
+ int start,
+ int length,
+ int options) const {
+ return WriteHelper(this, buffer, start, length, options);
+}
+
+
+bool v8::String::IsExternal() const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ if (IsDeadCheck(str->GetIsolate(), "v8::String::IsExternal()")) {
+ return false;
+ }
+ EnsureInitializedForIsolate(str->GetIsolate(), "v8::String::IsExternal()");
+ return i::StringShape(*str).IsExternalTwoByte();
+}
+
+
+bool v8::String::IsExternalAscii() const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ if (IsDeadCheck(str->GetIsolate(), "v8::String::IsExternalAscii()")) {
+ return false;
+ }
+ return i::StringShape(*str).IsExternalAscii();
+}
+
+
+void v8::String::VerifyExternalStringResource(
+ v8::String::ExternalStringResource* value) const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ const v8::String::ExternalStringResource* expected;
+ if (i::StringShape(*str).IsExternalTwoByte()) {
+ const void* resource =
+ i::Handle<i::ExternalTwoByteString>::cast(str)->resource();
+ expected = reinterpret_cast<const ExternalStringResource*>(resource);
+ } else {
+ expected = NULL;
+ }
+ CHECK_EQ(expected, value);
+}
+
+void v8::String::VerifyExternalStringResourceBase(
+ v8::String::ExternalStringResourceBase* value, Encoding encoding) const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ const v8::String::ExternalStringResourceBase* expected;
+ Encoding expectedEncoding;
+ if (i::StringShape(*str).IsExternalAscii()) {
+ const void* resource =
+ i::Handle<i::ExternalAsciiString>::cast(str)->resource();
+ expected = reinterpret_cast<const ExternalStringResourceBase*>(resource);
+ expectedEncoding = ASCII_ENCODING;
+ } else if (i::StringShape(*str).IsExternalTwoByte()) {
+ const void* resource =
+ i::Handle<i::ExternalTwoByteString>::cast(str)->resource();
+ expected = reinterpret_cast<const ExternalStringResourceBase*>(resource);
+ expectedEncoding = TWO_BYTE_ENCODING;
+ } else {
+ expected = NULL;
+ expectedEncoding = str->IsOneByteRepresentation() ? ASCII_ENCODING
+ : TWO_BYTE_ENCODING;
+ }
+ CHECK_EQ(expected, value);
+ CHECK_EQ(expectedEncoding, encoding);
+}
+
+const v8::String::ExternalAsciiStringResource*
+ v8::String::GetExternalAsciiStringResource() const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ if (IsDeadCheck(str->GetIsolate(),
+ "v8::String::GetExternalAsciiStringResource()")) {
+ return NULL;
+ }
+ if (i::StringShape(*str).IsExternalAscii()) {
+ const void* resource =
+ i::Handle<i::ExternalAsciiString>::cast(str)->resource();
+ return reinterpret_cast<const ExternalAsciiStringResource*>(resource);
+ } else {
+ return NULL;
+ }
+}
+
+
+Local<Value> Symbol::Name() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Symbol::Name()"))
+ return Local<Value>();
+ i::Handle<i::Symbol> sym = Utils::OpenHandle(this);
+ i::Handle<i::Object> name(sym->name(), sym->GetIsolate());
+ return Utils::ToLocal(name);
+}
+
+
+double Number::Value() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Number::Value()")) return 0;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->Number();
+}
+
+
+bool Boolean::Value() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Boolean::Value()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ return obj->IsTrue();
+}
+
+
+int64_t Integer::Value() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Integer::Value()")) return 0;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsSmi()) {
+ return i::Smi::cast(*obj)->value();
+ } else {
+ return static_cast<int64_t>(obj->Number());
+ }
+}
+
+
+int32_t Int32::Value() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Int32::Value()")) return 0;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsSmi()) {
+ return i::Smi::cast(*obj)->value();
+ } else {
+ return static_cast<int32_t>(obj->Number());
+ }
+}
+
+
+uint32_t Uint32::Value() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::Uint32::Value()")) return 0;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsSmi()) {
+ return i::Smi::cast(*obj)->value();
+ } else {
+ return static_cast<uint32_t>(obj->Number());
+ }
+}
+
+
+int v8::Object::InternalFieldCount() {
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ if (IsDeadCheck(obj->GetIsolate(), "v8::Object::InternalFieldCount()")) {
+ return 0;
+ }
+ return obj->GetInternalFieldCount();
+}
+
+
+static bool InternalFieldOK(i::Handle<i::JSObject> obj,
+ int index,
+ const char* location) {
+ return !IsDeadCheck(obj->GetIsolate(), location) &&
+ ApiCheck(index < obj->GetInternalFieldCount(),
+ location,
+ "Internal field out of bounds");
+}
+
+
+Local<Value> v8::Object::SlowGetInternalField(int index) {
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ const char* location = "v8::Object::GetInternalField()";
+ if (!InternalFieldOK(obj, index, location)) return Local<Value>();
+ i::Handle<i::Object> value(obj->GetInternalField(index), obj->GetIsolate());
+ return Utils::ToLocal(value);
+}
+
+
+void v8::Object::SetInternalField(int index, v8::Handle<Value> value) {
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ const char* location = "v8::Object::SetInternalField()";
+ if (!InternalFieldOK(obj, index, location)) return;
+ i::Handle<i::Object> val = Utils::OpenHandle(*value);
+ obj->SetInternalField(index, *val);
+ ASSERT_EQ(value, GetInternalField(index));
+}
+
+
+void* v8::Object::SlowGetAlignedPointerFromInternalField(int index) {
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ const char* location = "v8::Object::GetAlignedPointerFromInternalField()";
+ if (!InternalFieldOK(obj, index, location)) return NULL;
+ return DecodeSmiToAligned(obj->GetInternalField(index), location);
+}
+
+
+void v8::Object::SetAlignedPointerInInternalField(int index, void* value) {
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ const char* location = "v8::Object::SetAlignedPointerInInternalField()";
+ if (!InternalFieldOK(obj, index, location)) return;
+ obj->SetInternalField(index, EncodeAlignedAsSmi(value, location));
+ ASSERT_EQ(value, GetAlignedPointerFromInternalField(index));
+}
+
+
+static void* ExternalValue(i::Object* obj) {
+ // Obscure semantics for undefined, but somehow checked in our unit tests...
+ if (obj->IsUndefined()) return NULL;
+ i::Object* foreign = i::JSObject::cast(obj)->GetInternalField(0);
+ return i::Foreign::cast(foreign)->foreign_address();
+}
+
+
+// --- E n v i r o n m e n t ---
+
+
+bool v8::V8::Initialize() {
+ i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+ if (isolate != NULL && isolate->IsInitialized()) {
+ return true;
+ }
+ return InitializeHelper(isolate);
+}
+
+
+void v8::V8::SetEntropySource(EntropySource source) {
+ i::V8::SetEntropySource(source);
+}
+
+
+void v8::V8::SetReturnAddressLocationResolver(
+ ReturnAddressLocationResolver return_address_resolver) {
+ i::V8::SetReturnAddressLocationResolver(return_address_resolver);
+}
+
+
+bool v8::V8::SetFunctionEntryHook(FunctionEntryHook entry_hook) {
+ return SetFunctionEntryHook(Isolate::GetCurrent(), entry_hook);
+}
+
+
+bool v8::V8::SetFunctionEntryHook(Isolate* ext_isolate,
+ FunctionEntryHook entry_hook) {
+ ASSERT(ext_isolate != NULL);
+ ASSERT(entry_hook != NULL);
+
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(ext_isolate);
+
+ // The entry hook can only be set before the Isolate is initialized, as
+ // otherwise the Isolate's code stubs generated at initialization won't
+ // contain entry hooks.
+ if (isolate->IsInitialized())
+ return false;
+
+ // Setting an entry hook is a one-way operation, once set, it cannot be
+ // changed or unset.
+ if (isolate->function_entry_hook() != NULL)
+ return false;
+
+ isolate->set_function_entry_hook(entry_hook);
+ return true;
+}
+
+
+void v8::V8::SetJitCodeEventHandler(
+ JitCodeEventOptions options, JitCodeEventHandler event_handler) {
+ i::Isolate* isolate = i::Isolate::Current();
+ // Ensure that logging is initialized for our isolate.
+ isolate->InitializeLoggingAndCounters();
+ isolate->logger()->SetCodeEventHandler(options, event_handler);
+}
+
+void v8::V8::SetArrayBufferAllocator(
+ ArrayBuffer::Allocator* allocator) {
+ if (!ApiCheck(i::V8::ArrayBufferAllocator() == NULL,
+ "v8::V8::SetArrayBufferAllocator",
+ "ArrayBufferAllocator might only be set once"))
+ return;
+ i::V8::SetArrayBufferAllocator(allocator);
+}
+
+
+bool v8::V8::Dispose() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!ApiCheck(isolate != NULL && isolate->IsDefaultIsolate(),
+ "v8::V8::Dispose()",
+ "Use v8::Isolate::Dispose() for a non-default isolate.")) {
+ return false;
+ }
+ i::V8::TearDown();
+ return true;
+}
+
+
+HeapStatistics::HeapStatistics(): total_heap_size_(0),
+ total_heap_size_executable_(0),
+ total_physical_size_(0),
+ used_heap_size_(0),
+ heap_size_limit_(0) { }
+
+
+void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) {
+ i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+ if (isolate == NULL || !isolate->IsInitialized()) {
+ // Isolate is unitialized thus heap is not configured yet.
+ heap_statistics->total_heap_size_ = 0;
+ heap_statistics->total_heap_size_executable_ = 0;
+ heap_statistics->total_physical_size_ = 0;
+ heap_statistics->used_heap_size_ = 0;
+ heap_statistics->heap_size_limit_ = 0;
+ return;
+ }
+ Isolate* ext_isolate = reinterpret_cast<Isolate*>(isolate);
+ return ext_isolate->GetHeapStatistics(heap_statistics);
+}
+
+
+void v8::V8::VisitExternalResources(ExternalResourceVisitor* visitor) {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::V8::VisitExternalResources");
+ isolate->heap()->VisitExternalResources(visitor);
+}
+
+
+class VisitorAdapter : public i::ObjectVisitor {
+ public:
+ explicit VisitorAdapter(PersistentHandleVisitor* visitor)
+ : visitor_(visitor) {}
+ virtual void VisitPointers(i::Object** start, i::Object** end) {
+ UNREACHABLE();
+ }
+ virtual void VisitEmbedderReference(i::Object** p, uint16_t class_id) {
+ Value* value = ToApi<Value>(i::Handle<i::Object>(p));
+ visitor_->VisitPersistentHandle(
+ reinterpret_cast<Persistent<Value>*>(&value), class_id);
+ }
+ private:
+ PersistentHandleVisitor* visitor_;
+};
+
+
+void v8::V8::VisitHandlesWithClassIds(PersistentHandleVisitor* visitor) {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::V8::VisitHandlesWithClassId");
+
+ i::DisallowHeapAllocation no_allocation;
+
+ VisitorAdapter visitor_adapter(visitor);
+ isolate->global_handles()->IterateAllRootsWithClassIds(&visitor_adapter);
+}
+
+
+void v8::V8::VisitHandlesForPartialDependence(
+ Isolate* exported_isolate, PersistentHandleVisitor* visitor) {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(exported_isolate);
+ ASSERT(isolate == i::Isolate::Current());
+ IsDeadCheck(isolate, "v8::V8::VisitHandlesForPartialDependence");
+
+ i::DisallowHeapAllocation no_allocation;
+
+ VisitorAdapter visitor_adapter(visitor);
+ isolate->global_handles()->IterateAllRootsInNewSpaceWithClassIds(
+ &visitor_adapter);
+}
+
+
+bool v8::V8::IdleNotification(int hint) {
+ // Returning true tells the caller that it need not
+ // continue to call 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 == NULL || !isolate->IsInitialized()) return;
+ isolate->heap()->CollectAllAvailableGarbage("low memory notification");
+}
+
+
+int v8::V8::ContextDisposedNotification() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!isolate->IsInitialized()) return 0;
+ return isolate->heap()->NotifyContextDisposed();
+}
+
+
+bool v8::V8::InitializeICU() {
+ return i::InitializeICU();
+}
+
+
+const char* v8::V8::GetVersion() {
+ return i::Version::GetVersion();
+}
+
+
+static i::Handle<i::FunctionTemplateInfo>
+ EnsureConstructor(i::Handle<i::ObjectTemplateInfo> templ) {
+ if (templ->constructor()->IsUndefined()) {
+ Local<FunctionTemplate> constructor = FunctionTemplate::New();
+ Utils::OpenHandle(*constructor)->set_instance_template(*templ);
+ templ->set_constructor(*Utils::OpenHandle(*constructor));
+ }
+ return i::Handle<i::FunctionTemplateInfo>(
+ i::FunctionTemplateInfo::cast(templ->constructor()));
+}
+
+
+static i::Handle<i::Context> CreateEnvironment(
+ i::Isolate* isolate,
+ v8::ExtensionConfiguration* extensions,
+ v8::Handle<ObjectTemplate> global_template,
+ v8::Handle<Value> global_object) {
+ i::Handle<i::Context> env;
+
+ // Enter V8 via an ENTER_V8 scope.
+ {
+ ENTER_V8(isolate);
+ v8::Handle<ObjectTemplate> proxy_template = global_template;
+ i::Handle<i::FunctionTemplateInfo> proxy_constructor;
+ i::Handle<i::FunctionTemplateInfo> global_constructor;
+
+ if (!global_template.IsEmpty()) {
+ // Make sure that the global_template has a constructor.
+ global_constructor =
+ EnsureConstructor(Utils::OpenHandle(*global_template));
+
+ // Create a fresh template for the global proxy object.
+ proxy_template = ObjectTemplate::New();
+ proxy_constructor =
+ EnsureConstructor(Utils::OpenHandle(*proxy_template));
+
+ // Set the global template to be the prototype template of
+ // global proxy template.
+ proxy_constructor->set_prototype_template(
+ *Utils::OpenHandle(*global_template));
+
+ // Migrate security handlers from global_template to
+ // proxy_template. Temporarily removing access check
+ // information from the global template.
+ if (!global_constructor->access_check_info()->IsUndefined()) {
+ proxy_constructor->set_access_check_info(
+ global_constructor->access_check_info());
+ proxy_constructor->set_needs_access_check(
+ global_constructor->needs_access_check());
+ global_constructor->set_needs_access_check(false);
+ global_constructor->set_access_check_info(
+ isolate->heap()->undefined_value());
+ }
+ }
+
+ // Create the environment.
+ env = isolate->bootstrapper()->CreateEnvironment(
+ Utils::OpenHandle(*global_object, true),
+ proxy_template,
+ extensions);
+
+ // Restore the access check info on the global template.
+ if (!global_template.IsEmpty()) {
+ ASSERT(!global_constructor.is_null());
+ ASSERT(!proxy_constructor.is_null());
+ global_constructor->set_access_check_info(
+ proxy_constructor->access_check_info());
+ global_constructor->set_needs_access_check(
+ proxy_constructor->needs_access_check());
+ }
+ isolate->runtime_profiler()->Reset();
+ }
+ // Leave V8.
+
+ return env;
+}
+
+#ifdef V8_USE_UNSAFE_HANDLES
+Persistent<Context> v8::Context::New(
+ v8::ExtensionConfiguration* extensions,
+ v8::Handle<ObjectTemplate> global_template,
+ v8::Handle<Value> global_object) {
+ i::Isolate::EnsureDefaultIsolate();
+ i::Isolate* isolate = i::Isolate::Current();
+ Isolate* external_isolate = reinterpret_cast<Isolate*>(isolate);
+ EnsureInitializedForIsolate(isolate, "v8::Context::New()");
+ LOG_API(isolate, "Context::New");
+ ON_BAILOUT(isolate, "v8::Context::New()", return Persistent<Context>());
+ i::HandleScope scope(isolate);
+ i::Handle<i::Context> env =
+ CreateEnvironment(isolate, extensions, global_template, global_object);
+ if (env.is_null()) return Persistent<Context>();
+ return Persistent<Context>::New(external_isolate, Utils::ToLocal(env));
+}
+#endif
+
+
+Local<Context> v8::Context::New(
+ v8::Isolate* external_isolate,
+ v8::ExtensionConfiguration* extensions,
+ v8::Handle<ObjectTemplate> global_template,
+ v8::Handle<Value> global_object) {
+ i::Isolate::EnsureDefaultIsolate();
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(external_isolate);
+ EnsureInitializedForIsolate(isolate, "v8::Context::New()");
+ LOG_API(isolate, "Context::New");
+ ON_BAILOUT(isolate, "v8::Context::New()", return Local<Context>());
+ i::HandleScope scope(isolate);
+ i::Handle<i::Context> env =
+ CreateEnvironment(isolate, extensions, global_template, global_object);
+ if (env.is_null()) return Local<Context>();
+ return Utils::ToLocal(scope.CloseAndEscape(env));
+}
+
+
+void v8::Context::SetSecurityToken(Handle<Value> token) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::SetSecurityToken()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ i::Handle<i::Object> token_handle = Utils::OpenHandle(*token);
+ env->set_security_token(*token_handle);
+}
+
+
+void v8::Context::UseDefaultSecurityToken() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate,
+ "v8::Context::UseDefaultSecurityToken()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ env->set_security_token(env->global_object());
+}
+
+
+Handle<Value> v8::Context::GetSecurityToken() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::GetSecurityToken()")) {
+ return Handle<Value>();
+ }
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ i::Object* security_token = env->security_token();
+ i::Handle<i::Object> token_handle(security_token, isolate);
+ return Utils::ToLocal(token_handle);
+}
+
+
+bool Context::HasOutOfMemoryException() {
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ return env->has_out_of_memory();
+}
+
+
+bool Context::InContext() {
+ return i::Isolate::Current()->context() != NULL;
+}
+
+
+v8::Isolate* Context::GetIsolate() {
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ return reinterpret_cast<Isolate*>(env->GetIsolate());
+}
+
+
+v8::Local<v8::Context> Context::GetEntered() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!EnsureInitializedForIsolate(isolate, "v8::Context::GetEntered()")) {
+ return Local<Context>();
+ }
+ i::Handle<i::Object> last =
+ isolate->handle_scope_implementer()->LastEnteredContext();
+ if (last.is_null()) return Local<Context>();
+ i::Handle<i::Context> context = i::Handle<i::Context>::cast(last);
+ return Utils::ToLocal(context);
+}
+
+
+v8::Local<v8::Context> Context::GetCurrent() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::GetCurrent()")) {
+ return Local<Context>();
+ }
+ return reinterpret_cast<Isolate*>(isolate)->GetCurrentContext();
+}
+
+
+v8::Local<v8::Context> Context::GetCalling() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::GetCalling()")) {
+ return Local<Context>();
+ }
+ i::Handle<i::Object> calling =
+ isolate->GetCallingNativeContext();
+ if (calling.is_null()) return Local<Context>();
+ i::Handle<i::Context> context = i::Handle<i::Context>::cast(calling);
+ return Utils::ToLocal(context);
+}
+
+
+v8::Local<v8::Object> Context::Global() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::Global()")) {
+ return Local<v8::Object>();
+ }
+ i::Object** ctx = reinterpret_cast<i::Object**>(this);
+ i::Handle<i::Context> context =
+ i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx));
+ i::Handle<i::Object> global(context->global_proxy(), isolate);
+ return Utils::ToLocal(i::Handle<i::JSObject>::cast(global));
+}
+
+
+void Context::DetachGlobal() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::DetachGlobal()")) return;
+ 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));
+ isolate->bootstrapper()->DetachGlobal(context);
+}
+
+
+void Context::ReattachGlobal(Handle<Object> global_object) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::ReattachGlobal()")) return;
+ 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));
+ i::Handle<i::JSGlobalProxy> global_proxy =
+ i::Handle<i::JSGlobalProxy>::cast(Utils::OpenHandle(*global_object));
+ isolate->bootstrapper()->ReattachGlobal(context, global_proxy);
+}
+
+
+void Context::AllowCodeGenerationFromStrings(bool allow) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::AllowCodeGenerationFromStrings()")) {
+ return;
+ }
+ 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));
+ context->set_allow_code_gen_from_strings(
+ allow ? isolate->heap()->true_value() : isolate->heap()->false_value());
+}
+
+
+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 Context::SetErrorMessageForCodeGenerationFromStrings(
+ Handle<String> error) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate,
+ "v8::Context::SetErrorMessageForCodeGenerationFromStrings()")) {
+ return;
+ }
+ 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));
+ i::Handle<i::String> error_handle = Utils::OpenHandle(*error);
+ context->set_error_message_for_code_gen_from_strings(*error_handle);
+}
+
+
+Local<v8::Object> ObjectTemplate::NewInstance() {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::ObjectTemplate::NewInstance()",
+ return Local<v8::Object>());
+ LOG_API(isolate, "ObjectTemplate::NewInstance");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> obj =
+ i::Execution::InstantiateObject(Utils::OpenHandle(this),
+ &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+ return Utils::ToLocal(i::Handle<i::JSObject>::cast(obj));
+}
+
+
+Local<v8::Function> FunctionTemplate::GetFunction() {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::FunctionTemplate::GetFunction()",
+ return Local<v8::Function>());
+ LOG_API(isolate, "FunctionTemplate::GetFunction");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> obj =
+ i::Execution::InstantiateFunction(Utils::OpenHandle(this),
+ &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Function>());
+ return Utils::ToLocal(i::Handle<i::JSFunction>::cast(obj));
+}
+
+
+bool FunctionTemplate::HasInstance(v8::Handle<v8::Value> value) {
+ ON_BAILOUT(i::Isolate::Current(), "v8::FunctionTemplate::HasInstanceOf()",
+ return false);
+ i::Object* obj = *Utils::OpenHandle(*value);
+ return obj->IsInstanceOf(*Utils::OpenHandle(this));
+}
+
+
+Local<External> v8::External::New(void* value) {
+ STATIC_ASSERT(sizeof(value) == sizeof(i::Address));
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::External::New()");
+ LOG_API(isolate, "External::New");
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> external = isolate->factory()->NewExternal(value);
+ return Utils::ExternalToLocal(external);
+}
+
+
+void* External::Value() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::External::Value()")) return NULL;
+ return ExternalValue(*Utils::OpenHandle(this));
+}
+
+
+Local<String> v8::String::Empty() {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!EnsureInitializedForIsolate(isolate, "v8::String::Empty()")) {
+ return v8::Local<String>();
+ }
+ LOG_API(isolate, "String::Empty()");
+ return Utils::ToLocal(isolate->factory()->empty_string());
+}
+
+
+// anonymous namespace for string creation helper functions
+namespace {
+
+inline int StringLength(const char* string) {
+ return i::StrLength(string);
+}
+
+
+inline int StringLength(const uint8_t* string) {
+ return i::StrLength(reinterpret_cast<const char*>(string));
+}
+
+
+inline int StringLength(const uint16_t* string) {
+ int length = 0;
+ while (string[length] != '\0')
+ length++;
+ return length;
+}
+
+
+inline i::Handle<i::String> NewString(i::Factory* factory,
+ String::NewStringType type,
+ i::Vector<const char> string) {
+ if (type ==String::kInternalizedString) {
+ return factory->InternalizeUtf8String(string);
+ }
+ return factory->NewStringFromUtf8(string);
+}
+
+
+inline i::Handle<i::String> NewString(i::Factory* factory,
+ String::NewStringType type,
+ i::Vector<const uint8_t> string) {
+ if (type == String::kInternalizedString) {
+ return factory->InternalizeOneByteString(string);
+ }
+ return factory->NewStringFromOneByte(string);
+}
+
+
+inline i::Handle<i::String> NewString(i::Factory* factory,
+ String::NewStringType type,
+ i::Vector<const uint16_t> string) {
+ if (type == String::kInternalizedString) {
+ return factory->InternalizeTwoByteString(string);
+ }
+ return factory->NewStringFromTwoByte(string);
+}
+
+
+template<typename Char>
+inline Local<String> NewString(Isolate* v8_isolate,
+ const char* location,
+ const char* env,
+ const Char* data,
+ String::NewStringType type,
+ int length) {
+ i::Isolate* isolate = reinterpret_cast<internal::Isolate*>(v8_isolate);
+ EnsureInitializedForIsolate(isolate, location);
+ LOG_API(isolate, env);
+ if (length == 0 && type != String::kUndetectableString) {
+ return String::Empty();
+ }
+ ENTER_V8(isolate);
+ if (length == -1) length = StringLength(data);
+ i::Handle<i::String> result = NewString(
+ isolate->factory(), type, i::Vector<const Char>(data, length));
+ if (type == String::kUndetectableString) {
+ result->MarkAsUndetectable();
+ }
+ return Utils::ToLocal(result);
+}
+
+} // anonymous namespace
+
+
+Local<String> String::NewFromUtf8(Isolate* isolate,
+ const char* data,
+ NewStringType type,
+ int length) {
+ return NewString(isolate,
+ "v8::String::NewFromUtf8()",
+ "String::NewFromUtf8",
+ data,
+ type,
+ length);
+}
+
+
+Local<String> String::NewFromOneByte(Isolate* isolate,
+ const uint8_t* data,
+ NewStringType type,
+ int length) {
+ return NewString(isolate,
+ "v8::String::NewFromOneByte()",
+ "String::NewFromOneByte",
+ data,
+ type,
+ length);
+}
+
+
+Local<String> String::NewFromTwoByte(Isolate* isolate,
+ const uint16_t* data,
+ NewStringType type,
+ int length) {
+ return NewString(isolate,
+ "v8::String::NewFromTwoByte()",
+ "String::NewFromTwoByte",
+ data,
+ type,
+ length);
+}
+
+
+Local<String> v8::String::Concat(Handle<String> left, Handle<String> right) {
+ i::Handle<i::String> left_string = Utils::OpenHandle(*left);
+ i::Isolate* isolate = left_string->GetIsolate();
+ EnsureInitializedForIsolate(isolate, "v8::String::New()");
+ LOG_API(isolate, "String::New(char)");
+ ENTER_V8(isolate);
+ i::Handle<i::String> right_string = Utils::OpenHandle(*right);
+ i::Handle<i::String> result = isolate->factory()->NewConsString(left_string,
+ right_string);
+ return Utils::ToLocal(result);
+}
+
+
+i::Handle<i::String> NewExternalStringHandle(i::Isolate* isolate,
+ v8::String::ExternalStringResource* resource) {
+ i::Handle<i::String> result =
+ isolate->factory()->NewExternalStringFromTwoByte(resource);
+ return result;
+}
+
+
+i::Handle<i::String> NewExternalAsciiStringHandle(i::Isolate* isolate,
+ v8::String::ExternalAsciiStringResource* resource) {
+ i::Handle<i::String> result =
+ isolate->factory()->NewExternalStringFromAscii(resource);
+ return result;
+}
+
+
+bool RedirectToExternalString(i::Isolate* isolate,
+ i::Handle<i::String> parent,
+ i::Handle<i::String> external) {
+ if (parent->IsConsString()) {
+ i::Handle<i::ConsString> cons = i::Handle<i::ConsString>::cast(parent);
+ cons->set_first(*external);
+ cons->set_second(isolate->heap()->empty_string());
+ } else {
+ ASSERT(parent->IsSlicedString());
+ i::Handle<i::SlicedString> slice = i::Handle<i::SlicedString>::cast(parent);
+ slice->set_parent(*external);
+ slice->set_offset(0);
+ }
+ return true;
+}
+
+
+Local<String> v8::String::NewExternal(
+ v8::String::ExternalStringResource* resource) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::String::NewExternal()");
+ LOG_API(isolate, "String::NewExternal");
+ ENTER_V8(isolate);
+ CHECK(resource && resource->data());
+ i::Handle<i::String> result = NewExternalStringHandle(isolate, resource);
+ isolate->heap()->external_string_table()->AddString(*result);
+ return Utils::ToLocal(result);
+}
+
+
+bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) {
+ i::Handle<i::String> obj = Utils::OpenHandle(this);
+ i::Isolate* isolate = obj->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::String::MakeExternal()")) return false;
+ if (i::StringShape(*obj).IsExternalTwoByte()) {
+ return false; // Already an external string.
+ }
+ ENTER_V8(isolate);
+ if (isolate->string_tracker()->IsFreshUnusedString(obj)) {
+ return false;
+ }
+ if (isolate->heap()->IsInGCPostProcessing()) {
+ return false;
+ }
+ CHECK(resource && resource->data());
+
+ bool result;
+ i::Handle<i::String> external;
+ if (isolate->heap()->old_pointer_space()->Contains(*obj)) {
+ // We do not allow external strings in the old pointer space. Instead of
+ // converting the string in-place, we keep the cons/sliced string and
+ // point it to a newly-allocated external string.
+ external = NewExternalStringHandle(isolate, resource);
+ result = RedirectToExternalString(isolate, obj, external);
+ } else {
+ result = obj->MakeExternal(resource);
+ external = obj;
+ }
+
+ ASSERT(external->IsExternalString());
+ if (result && !external->IsInternalizedString()) {
+ isolate->heap()->external_string_table()->AddString(*external);
+ }
+ return result;
+}
+
+
+Local<String> v8::String::NewExternal(
+ v8::String::ExternalAsciiStringResource* resource) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::String::NewExternal()");
+ LOG_API(isolate, "String::NewExternal");
+ ENTER_V8(isolate);
+ CHECK(resource && resource->data());
+ i::Handle<i::String> result = NewExternalAsciiStringHandle(isolate, resource);
+ isolate->heap()->external_string_table()->AddString(*result);
+ return Utils::ToLocal(result);
+}
+
+
+bool v8::String::MakeExternal(
+ v8::String::ExternalAsciiStringResource* resource) {
+ i::Handle<i::String> obj = Utils::OpenHandle(this);
+ i::Isolate* isolate = obj->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::String::MakeExternal()")) return false;
+ if (i::StringShape(*obj).IsExternalTwoByte()) {
+ return false; // Already an external string.
+ }
+ ENTER_V8(isolate);
+ if (isolate->string_tracker()->IsFreshUnusedString(obj)) {
+ return false;
+ }
+ if (isolate->heap()->IsInGCPostProcessing()) {
+ return false;
+ }
+ CHECK(resource && resource->data());
+
+ bool result;
+ i::Handle<i::String> external;
+ if (isolate->heap()->old_pointer_space()->Contains(*obj)) {
+ // We do not allow external strings in the old pointer space. Instead of
+ // converting the string in-place, we keep the cons/sliced string and
+ // point it to a newly-allocated external string.
+ external = NewExternalAsciiStringHandle(isolate, resource);
+ result = RedirectToExternalString(isolate, obj, external);
+ } else {
+ result = obj->MakeExternal(resource);
+ external = obj;
+ }
+
+ ASSERT(external->IsExternalString());
+ if (result && !external->IsInternalizedString()) {
+ isolate->heap()->external_string_table()->AddString(*external);
+ }
+ return result;
+}
+
+
+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;
+ int size = obj->Size(); // Byte size of the original string.
+ if (size < i::ExternalString::kShortSize) return false;
+ i::StringShape shape(*obj);
+ return !shape.IsExternal();
+}
+
+
+Local<v8::Object> v8::Object::New() {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Object::New()");
+ LOG_API(isolate, "Object::New");
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> obj =
+ isolate->factory()->NewJSObject(isolate->object_function());
+ return Utils::ToLocal(obj);
+}
+
+
+Local<v8::Value> v8::NumberObject::New(double value) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::NumberObject::New()");
+ LOG_API(isolate, "NumberObject::New");
+ ENTER_V8(isolate);
+ i::Handle<i::Object> number = isolate->factory()->NewNumber(value);
+ i::Handle<i::Object> obj = isolate->factory()->ToObject(number);
+ return Utils::ToLocal(obj);
+}
+
+
+double v8::NumberObject::ValueOf() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::NumberObject::NumberValue()")) return 0;
+ LOG_API(isolate, "NumberObject::NumberValue");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
+ return jsvalue->value()->Number();
+}
+
+
+Local<v8::Value> v8::BooleanObject::New(bool value) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::BooleanObject::New()");
+ LOG_API(isolate, "BooleanObject::New");
+ ENTER_V8(isolate);
+ i::Handle<i::Object> boolean(value
+ ? isolate->heap()->true_value()
+ : isolate->heap()->false_value(),
+ isolate);
+ i::Handle<i::Object> obj = isolate->factory()->ToObject(boolean);
+ return Utils::ToLocal(obj);
+}
+
+
+bool v8::BooleanObject::ValueOf() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::BooleanObject::BooleanValue()")) return 0;
+ LOG_API(isolate, "BooleanObject::BooleanValue");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
+ return jsvalue->value()->IsTrue();
+}
+
+
+Local<v8::Value> v8::StringObject::New(Handle<String> value) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::StringObject::New()");
+ LOG_API(isolate, "StringObject::New");
+ ENTER_V8(isolate);
+ i::Handle<i::Object> obj =
+ isolate->factory()->ToObject(Utils::OpenHandle(*value));
+ return Utils::ToLocal(obj);
+}
+
+
+Local<v8::String> v8::StringObject::ValueOf() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::StringObject::StringValue()")) {
+ return Local<v8::String>();
+ }
+ LOG_API(isolate, "StringObject::StringValue");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
+ return Utils::ToLocal(
+ i::Handle<i::String>(i::String::cast(jsvalue->value())));
+}
+
+
+Local<v8::Value> v8::SymbolObject::New(Isolate* isolate, Handle<Symbol> value) {
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ EnsureInitializedForIsolate(i_isolate, "v8::SymbolObject::New()");
+ LOG_API(i_isolate, "SymbolObject::New");
+ ENTER_V8(i_isolate);
+ i::Handle<i::Object> obj =
+ i_isolate->factory()->ToObject(Utils::OpenHandle(*value));
+ return Utils::ToLocal(obj);
+}
+
+
+Local<v8::Symbol> v8::SymbolObject::ValueOf() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::SymbolObject::SymbolValue()"))
+ return Local<v8::Symbol>();
+ LOG_API(isolate, "SymbolObject::SymbolValue");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
+ return Utils::ToLocal(
+ i::Handle<i::Symbol>(i::Symbol::cast(jsvalue->value())));
+}
+
+
+Local<v8::Value> v8::Date::New(double time) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Date::New()");
+ LOG_API(isolate, "Date::New");
+ if (std::isnan(time)) {
+ // Introduce only canonical NaN value into the VM, to avoid signaling NaNs.
+ time = i::OS::nan_value();
+ }
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> obj =
+ i::Execution::NewDate(time, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Value>());
+ return Utils::ToLocal(obj);
+}
+
+
+double v8::Date::ValueOf() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Date::NumberValue()")) return 0;
+ LOG_API(isolate, "Date::NumberValue");
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ i::Handle<i::JSDate> jsdate = i::Handle<i::JSDate>::cast(obj);
+ return jsdate->value()->Number();
+}
+
+
+void v8::Date::DateTimeConfigurationChangeNotification() {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Date::DateTimeConfigurationChangeNotification()",
+ return);
+ LOG_API(isolate, "Date::DateTimeConfigurationChangeNotification");
+ ENTER_V8(isolate);
+
+ isolate->date_cache()->ResetDateCache();
+
+ i::HandleScope scope(isolate);
+ // Get the function ResetDateCache (defined in date.js).
+ i::Handle<i::String> func_name_str =
+ isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("ResetDateCache"));
+ i::MaybeObject* result =
+ isolate->js_builtins_object()->GetProperty(*func_name_str);
+ i::Object* object_func;
+ if (!result->ToObject(&object_func)) {
+ return;
+ }
+
+ if (object_func->IsJSFunction()) {
+ i::Handle<i::JSFunction> func =
+ i::Handle<i::JSFunction>(i::JSFunction::cast(object_func));
+
+ // Call ResetDateCache(0 but expect no exceptions:
+ bool caught_exception = false;
+ i::Execution::TryCall(func,
+ isolate->js_builtins_object(),
+ 0,
+ NULL,
+ &caught_exception);
+ }
+}
+
+
+static i::Handle<i::String> RegExpFlagsToString(RegExp::Flags flags) {
+ i::Isolate* isolate = i::Isolate::Current();
+ uint8_t flags_buf[3];
+ int num_flags = 0;
+ if ((flags & RegExp::kGlobal) != 0) flags_buf[num_flags++] = 'g';
+ if ((flags & RegExp::kMultiline) != 0) flags_buf[num_flags++] = 'm';
+ if ((flags & RegExp::kIgnoreCase) != 0) flags_buf[num_flags++] = 'i';
+ ASSERT(num_flags <= static_cast<int>(ARRAY_SIZE(flags_buf)));
+ return isolate->factory()->InternalizeOneByteString(
+ i::Vector<const uint8_t>(flags_buf, num_flags));
+}
+
+
+Local<v8::RegExp> v8::RegExp::New(Handle<String> pattern,
+ Flags flags) {
+ i::Isolate* isolate = Utils::OpenHandle(*pattern)->GetIsolate();
+ EnsureInitializedForIsolate(isolate, "v8::RegExp::New()");
+ LOG_API(isolate, "RegExp::New");
+ ENTER_V8(isolate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::JSRegExp> obj = i::Execution::NewJSRegExp(
+ Utils::OpenHandle(*pattern),
+ RegExpFlagsToString(flags),
+ &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::RegExp>());
+ return Utils::ToLocal(i::Handle<i::JSRegExp>::cast(obj));
+}
+
+
+Local<v8::String> v8::RegExp::GetSource() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::RegExp::GetSource()")) {
+ return Local<v8::String>();
+ }
+ i::Handle<i::JSRegExp> obj = Utils::OpenHandle(this);
+ return Utils::ToLocal(i::Handle<i::String>(obj->Pattern()));
+}
+
+
+// Assert that the static flags cast in GetFlags is valid.
+#define REGEXP_FLAG_ASSERT_EQ(api_flag, internal_flag) \
+ STATIC_ASSERT(static_cast<int>(v8::RegExp::api_flag) == \
+ static_cast<int>(i::JSRegExp::internal_flag))
+REGEXP_FLAG_ASSERT_EQ(kNone, NONE);
+REGEXP_FLAG_ASSERT_EQ(kGlobal, GLOBAL);
+REGEXP_FLAG_ASSERT_EQ(kIgnoreCase, IGNORE_CASE);
+REGEXP_FLAG_ASSERT_EQ(kMultiline, MULTILINE);
+#undef REGEXP_FLAG_ASSERT_EQ
+
+v8::RegExp::Flags v8::RegExp::GetFlags() const {
+ if (IsDeadCheck(i::Isolate::Current(), "v8::RegExp::GetFlags()")) {
+ return v8::RegExp::kNone;
+ }
+ i::Handle<i::JSRegExp> obj = Utils::OpenHandle(this);
+ return static_cast<RegExp::Flags>(obj->GetFlags().value());
+}
+
+
+Local<v8::Array> v8::Array::New(int length) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Array::New()");
+ LOG_API(isolate, "Array::New");
+ ENTER_V8(isolate);
+ int real_length = length > 0 ? length : 0;
+ i::Handle<i::JSArray> obj = isolate->factory()->NewJSArray(real_length);
+ i::Handle<i::Object> length_obj =
+ isolate->factory()->NewNumberFromInt(real_length);
+ obj->set_length(*length_obj);
+ return Utils::ToLocal(obj);
+}
+
+
+uint32_t v8::Array::Length() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::Array::Length()")) return 0;
+ i::Handle<i::JSArray> obj = Utils::OpenHandle(this);
+ i::Object* length = obj->length();
+ if (length->IsSmi()) {
+ return i::Smi::cast(length)->value();
+ } else {
+ return static_cast<uint32_t>(length->Number());
+ }
+}
+
+
+Local<Object> Array::CloneElementAt(uint32_t index) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Array::CloneElementAt()", return Local<Object>());
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ if (!self->HasFastObjectElements()) {
+ return Local<Object>();
+ }
+ i::FixedArray* elms = i::FixedArray::cast(self->elements());
+ i::Object* paragon = elms->get(index);
+ if (!paragon->IsJSObject()) {
+ return Local<Object>();
+ }
+ i::Handle<i::JSObject> paragon_handle(i::JSObject::cast(paragon));
+ EXCEPTION_PREAMBLE(isolate);
+ ENTER_V8(isolate);
+ i::Handle<i::JSObject> result = i::Copy(paragon_handle);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Object>());
+ return Utils::ToLocal(result);
+}
+
+
+bool v8::ArrayBuffer::IsExternal() const {
+ return Utils::OpenHandle(this)->is_external();
+}
+
+
+v8::ArrayBuffer::Contents v8::ArrayBuffer::Externalize() {
+ i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
+ ApiCheck(!obj->is_external(),
+ "v8::ArrayBuffer::Externalize",
+ "ArrayBuffer already externalized");
+ obj->set_is_external(true);
+ size_t byte_length = static_cast<size_t>(obj->byte_length()->Number());
+ Contents contents;
+ contents.data_ = obj->backing_store();
+ contents.byte_length_ = byte_length;
+ return contents;
+}
+
+
+void v8::ArrayBuffer::Neuter() {
+ i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
+ i::Isolate* isolate = obj->GetIsolate();
+ ApiCheck(obj->is_external(),
+ "v8::ArrayBuffer::Neuter",
+ "Only externalized ArrayBuffers can be neutered");
+ LOG_API(obj->GetIsolate(), "v8::ArrayBuffer::Neuter()");
+ ENTER_V8(isolate);
+
+ for (i::Handle<i::Object> view_obj(obj->weak_first_view(), isolate);
+ !view_obj->IsUndefined();) {
+ i::Handle<i::JSArrayBufferView> view(i::JSArrayBufferView::cast(*view_obj));
+ if (view->IsJSTypedArray()) {
+ i::JSTypedArray::cast(*view)->Neuter();
+ } else if (view->IsJSDataView()) {
+ i::JSDataView::cast(*view)->Neuter();
+ } else {
+ UNREACHABLE();
+ }
+ view_obj = i::handle(view->weak_next(), isolate);
+ }
+ obj->Neuter();
+}
+
+
+size_t v8::ArrayBuffer::ByteLength() const {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::ArrayBuffer::ByteLength()")) return 0;
+ i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
+ return static_cast<size_t>(obj->byte_length()->Number());
+}
+
+
+Local<ArrayBuffer> v8::ArrayBuffer::New(size_t byte_length) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::ArrayBuffer::New(size_t)");
+ LOG_API(isolate, "v8::ArrayBuffer::New(size_t)");
+ ENTER_V8(isolate);
+ i::Handle<i::JSArrayBuffer> obj =
+ isolate->factory()->NewJSArrayBuffer();
+ i::Runtime::SetupArrayBufferAllocatingData(isolate, obj, byte_length);
+ return Utils::ToLocal(obj);
+}
+
+
+Local<ArrayBuffer> v8::ArrayBuffer::New(void* data, size_t byte_length) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::ArrayBuffer::New(void*, size_t)");
+ LOG_API(isolate, "v8::ArrayBuffer::New(void*, size_t)");
+ ENTER_V8(isolate);
+ i::Handle<i::JSArrayBuffer> obj =
+ isolate->factory()->NewJSArrayBuffer();
+ i::Runtime::SetupArrayBuffer(isolate, obj, true, data, byte_length);
+ return Utils::ToLocal(obj);
+}
+
+
+Local<ArrayBuffer> v8::ArrayBufferView::Buffer() {
+ i::Handle<i::JSArrayBufferView> obj = Utils::OpenHandle(this);
+ ASSERT(obj->buffer()->IsJSArrayBuffer());
+ i::Handle<i::JSArrayBuffer> buffer(i::JSArrayBuffer::cast(obj->buffer()));
+ return Utils::ToLocal(buffer);
+}
+
+
+size_t v8::ArrayBufferView::ByteOffset() {
+ i::Handle<i::JSArrayBufferView> obj = Utils::OpenHandle(this);
+ return static_cast<size_t>(obj->byte_offset()->Number());
+}
+
+
+size_t v8::ArrayBufferView::ByteLength() {
+ i::Handle<i::JSArrayBufferView> obj = Utils::OpenHandle(this);
+ return static_cast<size_t>(obj->byte_length()->Number());
+}
+
+
+void* v8::ArrayBufferView::BaseAddress() {
+ i::Handle<i::JSArrayBufferView> obj = Utils::OpenHandle(this);
+ i::Handle<i::JSArrayBuffer> buffer(i::JSArrayBuffer::cast(obj->buffer()));
+ void* buffer_data = buffer->backing_store();
+ size_t byte_offset = static_cast<size_t>(obj->byte_offset()->Number());
+ return static_cast<uint8_t*>(buffer_data) + byte_offset;
+}
+
+
+size_t v8::TypedArray::Length() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ if (IsDeadCheck(isolate, "v8::TypedArray::Length()")) return 0;
+ i::Handle<i::JSTypedArray> obj = Utils::OpenHandle(this);
+ return static_cast<size_t>(obj->length()->Number());
+}
+
+
+static inline void SetupArrayBufferView(
+ i::Isolate* isolate,
+ i::Handle<i::JSArrayBufferView> obj,
+ i::Handle<i::JSArrayBuffer> buffer,
+ size_t byte_offset,
+ size_t byte_length) {
+ ASSERT(byte_offset + byte_length <=
+ static_cast<size_t>(buffer->byte_length()->Number()));
+
+ obj->set_buffer(*buffer);
+
+ obj->set_weak_next(buffer->weak_first_view());
+ buffer->set_weak_first_view(*obj);
+
+ i::Handle<i::Object> byte_offset_object =
+ isolate->factory()->NewNumberFromSize(byte_offset);
+ obj->set_byte_offset(*byte_offset_object);
+
+ i::Handle<i::Object> byte_length_object =
+ isolate->factory()->NewNumberFromSize(byte_length);
+ obj->set_byte_length(*byte_length_object);
+}
+
+template<typename ElementType,
+ ExternalArrayType array_type,
+ i::ElementsKind elements_kind>
+i::Handle<i::JSTypedArray> NewTypedArray(
+ i::Isolate* isolate,
+ Handle<ArrayBuffer> array_buffer, size_t byte_offset, size_t length) {
+ i::Handle<i::JSTypedArray> obj =
+ isolate->factory()->NewJSTypedArray(array_type);
+ i::Handle<i::JSArrayBuffer> buffer = Utils::OpenHandle(*array_buffer);
+
+ ASSERT(byte_offset % sizeof(ElementType) == 0);
+
+ SetupArrayBufferView(
+ isolate, obj, buffer, byte_offset, length * sizeof(ElementType));
+
+ i::Handle<i::Object> length_object =
+ isolate->factory()->NewNumberFromSize(length);
+ obj->set_length(*length_object);
+
+ i::Handle<i::ExternalArray> elements =
+ isolate->factory()->NewExternalArray(
+ static_cast<int>(length), array_type,
+ static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
+ obj->set_elements(*elements);
+ return obj;
+}
+
+
+#define TYPED_ARRAY_NEW(TypedArray, element_type, array_type, elements_kind) \
+ Local<TypedArray> TypedArray::New(Handle<ArrayBuffer> array_buffer, \
+ size_t byte_offset, size_t length) { \
+ i::Isolate* isolate = i::Isolate::Current(); \
+ EnsureInitializedForIsolate(isolate, \
+ "v8::" #TypedArray "::New(Handle<ArrayBuffer>, size_t, size_t)"); \
+ LOG_API(isolate, \
+ "v8::" #TypedArray "::New(Handle<ArrayBuffer>, size_t, size_t)"); \
+ ENTER_V8(isolate); \
+ i::Handle<i::JSTypedArray> obj = \
+ NewTypedArray<element_type, array_type, elements_kind>( \
+ isolate, array_buffer, byte_offset, length); \
+ return Utils::ToLocal##TypedArray(obj); \
+ }
+
+
+TYPED_ARRAY_NEW(Uint8Array, uint8_t, kExternalUnsignedByteArray,
+ i::EXTERNAL_UNSIGNED_BYTE_ELEMENTS)
+TYPED_ARRAY_NEW(Uint8ClampedArray, uint8_t, kExternalPixelArray,
+ i::EXTERNAL_PIXEL_ELEMENTS)
+TYPED_ARRAY_NEW(Int8Array, int8_t, kExternalByteArray,
+ i::EXTERNAL_BYTE_ELEMENTS)
+TYPED_ARRAY_NEW(Uint16Array, uint16_t, kExternalUnsignedShortArray,
+ i::EXTERNAL_UNSIGNED_SHORT_ELEMENTS)
+TYPED_ARRAY_NEW(Int16Array, int16_t, kExternalShortArray,
+ i::EXTERNAL_SHORT_ELEMENTS)
+TYPED_ARRAY_NEW(Uint32Array, uint32_t, kExternalUnsignedIntArray,
+ i::EXTERNAL_UNSIGNED_INT_ELEMENTS)
+TYPED_ARRAY_NEW(Int32Array, int32_t, kExternalIntArray,
+ i::EXTERNAL_INT_ELEMENTS)
+TYPED_ARRAY_NEW(Float32Array, float, kExternalFloatArray,
+ i::EXTERNAL_FLOAT_ELEMENTS)
+TYPED_ARRAY_NEW(Float64Array, double, kExternalDoubleArray,
+ i::EXTERNAL_DOUBLE_ELEMENTS)
+
+#undef TYPED_ARRAY_NEW
+
+Local<DataView> DataView::New(Handle<ArrayBuffer> array_buffer,
+ size_t byte_offset, size_t byte_length) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(
+ isolate, "v8::DataView::New(void*, size_t, size_t)");
+ LOG_API(isolate, "v8::DataView::New(void*, size_t, size_t)");
+ ENTER_V8(isolate);
+ i::Handle<i::JSDataView> obj = isolate->factory()->NewJSDataView();
+ i::Handle<i::JSArrayBuffer> buffer = Utils::OpenHandle(*array_buffer);
+ SetupArrayBufferView(
+ isolate, obj, buffer, byte_offset, byte_length);
+ return Utils::ToLocal(obj);
+}
+
+
+Local<Symbol> v8::Symbol::New(Isolate* isolate) {
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");
+ LOG_API(i_isolate, "Symbol::New()");
+ ENTER_V8(i_isolate);
+ i::Handle<i::Symbol> result = i_isolate->factory()->NewSymbol();
+ return Utils::ToLocal(result);
+}
+
+
+Local<Symbol> v8::Symbol::New(Isolate* isolate, const char* data, int length) {
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");
+ LOG_API(i_isolate, "Symbol::New(char)");
+ ENTER_V8(i_isolate);
+ if (length == -1) length = i::StrLength(data);
+ i::Handle<i::String> name = i_isolate->factory()->NewStringFromUtf8(
+ i::Vector<const char>(data, length));
+ i::Handle<i::Symbol> result = i_isolate->factory()->NewSymbol();
+ result->set_name(*name);
+ return Utils::ToLocal(result);
+}
+
+
+Local<Number> v8::Number::New(double value) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Number::New()");
+ return Number::New(reinterpret_cast<Isolate*>(isolate), value);
+}
+
+
+Local<Number> v8::Number::New(Isolate* isolate, double value) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ ASSERT(internal_isolate->IsInitialized());
+ if (std::isnan(value)) {
+ // Introduce only canonical NaN value into the VM, to avoid signaling NaNs.
+ value = i::OS::nan_value();
+ }
+ ENTER_V8(internal_isolate);
+ i::Handle<i::Object> result = internal_isolate->factory()->NewNumber(value);
+ return Utils::NumberToLocal(result);
+}
+
+
+Local<Integer> v8::Integer::New(int32_t value) {
+ i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+ EnsureInitializedForIsolate(isolate, "v8::Integer::New()");
+ return v8::Integer::New(value, reinterpret_cast<Isolate*>(isolate));
+}
+
+
+Local<Integer> Integer::NewFromUnsigned(uint32_t value) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Integer::NewFromUnsigned()");
+ return Integer::NewFromUnsigned(value, reinterpret_cast<Isolate*>(isolate));
+}
+
+
+Local<Integer> v8::Integer::New(int32_t value, Isolate* isolate) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ ASSERT(internal_isolate->IsInitialized());
+ if (i::Smi::IsValid(value)) {
+ return Utils::IntegerToLocal(i::Handle<i::Object>(i::Smi::FromInt(value),
+ internal_isolate));
+ }
+ ENTER_V8(internal_isolate);
+ i::Handle<i::Object> result = internal_isolate->factory()->NewNumber(value);
+ return Utils::IntegerToLocal(result);
+}
+
+
+Local<Integer> v8::Integer::NewFromUnsigned(uint32_t value, Isolate* isolate) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ ASSERT(internal_isolate->IsInitialized());
+ bool fits_into_int32_t = (value & (1 << 31)) == 0;
+ if (fits_into_int32_t) {
+ return Integer::New(static_cast<int32_t>(value), isolate);
+ }
+ ENTER_V8(internal_isolate);
+ i::Handle<i::Object> result = internal_isolate->factory()->NewNumber(value);
+ return Utils::IntegerToLocal(result);
+}
+
+
+#ifdef DEBUG
+v8::AssertNoGCScope::AssertNoGCScope(v8::Isolate* isolate) {
+ disallow_heap_allocation_ = new i::DisallowHeapAllocation();
+}
+
+
+v8::AssertNoGCScope::~AssertNoGCScope() {
+ delete static_cast<i::DisallowHeapAllocation*>(disallow_heap_allocation_);
+}
+#endif
+
+
+void V8::IgnoreOutOfMemoryException() {
+ EnterIsolateIfNeeded()->set_ignore_out_of_memory(true);
+}
+
+
+bool V8::AddMessageListener(MessageCallback that, Handle<Value> data) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::V8::AddMessageListener()");
+ ON_BAILOUT(isolate, "v8::V8::AddMessageListener()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ NeanderArray listeners(isolate->factory()->message_listeners());
+ NeanderObject obj(2);
+ obj.set(0, *isolate->factory()->NewForeign(FUNCTION_ADDR(that)));
+ obj.set(1, data.IsEmpty() ? isolate->heap()->undefined_value()
+ : *Utils::OpenHandle(*data));
+ listeners.add(obj.value());
+ return true;
+}
+
+
+void V8::RemoveMessageListeners(MessageCallback that) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::V8::RemoveMessageListener()");
+ ON_BAILOUT(isolate, "v8::V8::RemoveMessageListeners()", return);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ NeanderArray listeners(isolate->factory()->message_listeners());
+ for (int i = 0; i < listeners.length(); i++) {
+ if (listeners.get(i)->IsUndefined()) continue; // skip deleted ones
+
+ NeanderObject listener(i::JSObject::cast(listeners.get(i)));
+ i::Handle<i::Foreign> callback_obj(i::Foreign::cast(listener.get(0)));
+ if (callback_obj->foreign_address() == FUNCTION_ADDR(that)) {
+ listeners.set(i, isolate->heap()->undefined_value());
+ }
+ }
+}
+
+
+void V8::SetCaptureStackTraceForUncaughtExceptions(
+ bool capture,
+ int frame_limit,
+ StackTrace::StackTraceOptions options) {
+ i::Isolate::Current()->SetCaptureStackTraceForUncaughtExceptions(
+ capture,
+ frame_limit,
+ options);
+}
+
+
+void V8::SetCounterFunction(CounterLookupCallback callback) {
+ i::Isolate* isolate = EnterIsolateIfNeeded();
+ if (IsDeadCheck(isolate, "v8::V8::SetCounterFunction()")) return;
+ isolate->stats_table()->SetCounterFunction(callback);
+}
+
+
+void V8::SetCreateHistogramFunction(CreateHistogramCallback callback) {
+ i::Isolate* isolate = EnterIsolateIfNeeded();
+ if (IsDeadCheck(isolate, "v8::V8::SetCreateHistogramFunction()")) return;
+ isolate->stats_table()->SetCreateHistogramFunction(callback);
+ isolate->InitializeLoggingAndCounters();
+ isolate->counters()->ResetHistograms();
+}
+
+
+void V8::SetAddHistogramSampleFunction(AddHistogramSampleCallback callback) {
+ i::Isolate* isolate = EnterIsolateIfNeeded();
+ if (IsDeadCheck(isolate, "v8::V8::SetAddHistogramSampleFunction()")) return;
+ isolate->stats_table()->
+ SetAddHistogramSampleFunction(callback);
+}
+
+void V8::SetFailedAccessCheckCallbackFunction(
+ FailedAccessCheckCallback callback) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::SetFailedAccessCheckCallbackFunction()")) {
+ return;
+ }
+ isolate->SetFailedAccessCheckCallback(callback);
+}
+
+
+intptr_t Isolate::AdjustAmountOfExternalAllocatedMemory(
+ intptr_t change_in_bytes) {
+ i::Heap* heap = reinterpret_cast<i::Isolate*>(this)->heap();
+ return heap->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
+}
+
+
+intptr_t V8::AdjustAmountOfExternalAllocatedMemory(intptr_t change_in_bytes) {
+ i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+ if (isolate == NULL || !isolate->IsInitialized() ||
+ IsDeadCheck(isolate, "v8::V8::AdjustAmountOfExternalAllocatedMemory()")) {
+ return 0;
+ }
+ Isolate* isolate_ext = reinterpret_cast<Isolate*>(isolate);
+ return isolate_ext->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
+}
+
+
+HeapProfiler* Isolate::GetHeapProfiler() {
+ i::HeapProfiler* heap_profiler =
+ reinterpret_cast<i::Isolate*>(this)->heap_profiler();
+ return reinterpret_cast<HeapProfiler*>(heap_profiler);
+}
+
+
+CpuProfiler* Isolate::GetCpuProfiler() {
+ i::CpuProfiler* cpu_profiler =
+ reinterpret_cast<i::Isolate*>(this)->cpu_profiler();
+ return reinterpret_cast<CpuProfiler*>(cpu_profiler);
+}
+
+
+v8::Local<v8::Context> Isolate::GetCurrentContext() {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(this);
+ i::Context* context = internal_isolate->context();
+ if (context == NULL) return Local<Context>();
+ i::Context* native_context = context->global_object()->native_context();
+ if (native_context == NULL) return Local<Context>();
+ return Utils::ToLocal(i::Handle<i::Context>(native_context));
+}
+
+
+void Isolate::SetObjectGroupId(const Persistent<Value>& object,
+ UniqueId id) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(this);
+ internal_isolate->global_handles()->SetObjectGroupId(
+ Utils::OpenPersistent(object).location(),
+ id);
+}
+
+
+void Isolate::SetReferenceFromGroup(UniqueId id,
+ const Persistent<Value>& object) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(this);
+ internal_isolate->global_handles()->SetReferenceFromGroup(
+ id,
+ Utils::OpenPersistent(object).location());
+}
+
+
+void Isolate::SetReference(const Persistent<Object>& parent,
+ const Persistent<Value>& child) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(this);
+ i::Object** parent_location = Utils::OpenPersistent(parent).location();
+ internal_isolate->global_handles()->SetReference(
+ reinterpret_cast<i::HeapObject**>(parent_location),
+ Utils::OpenPersistent(child).location());
+}
+
+
+void V8::SetGlobalGCPrologueCallback(GCCallback callback) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::SetGlobalGCPrologueCallback()")) return;
+ isolate->heap()->SetGlobalGCPrologueCallback(callback);
+}
+
+
+void V8::SetGlobalGCEpilogueCallback(GCCallback callback) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::SetGlobalGCEpilogueCallback()")) return;
+ isolate->heap()->SetGlobalGCEpilogueCallback(callback);
+}
+
+
+void V8::AddGCPrologueCallback(GCPrologueCallback callback, GCType gc_type) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::AddGCPrologueCallback()")) return;
+ isolate->heap()->AddGCPrologueCallback(callback, gc_type);
+}
+
+
+void V8::RemoveGCPrologueCallback(GCPrologueCallback callback) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::RemoveGCPrologueCallback()")) return;
+ isolate->heap()->RemoveGCPrologueCallback(callback);
+}
+
+
+void V8::AddGCEpilogueCallback(GCEpilogueCallback callback, GCType gc_type) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::AddGCEpilogueCallback()")) return;
+ isolate->heap()->AddGCEpilogueCallback(callback, gc_type);
+}
+
+
+void V8::RemoveGCEpilogueCallback(GCEpilogueCallback callback) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::RemoveGCEpilogueCallback()")) return;
+ isolate->heap()->RemoveGCEpilogueCallback(callback);
+}
+
+
+void V8::AddMemoryAllocationCallback(MemoryAllocationCallback callback,
+ ObjectSpace space,
+ AllocationAction action) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::AddMemoryAllocationCallback()")) return;
+ isolate->memory_allocator()->AddMemoryAllocationCallback(
+ callback, space, action);
+}
+
+
+void V8::RemoveMemoryAllocationCallback(MemoryAllocationCallback callback) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::RemoveMemoryAllocationCallback()")) return;
+ isolate->memory_allocator()->RemoveMemoryAllocationCallback(
+ callback);
+}
+
+
+void V8::AddCallCompletedCallback(CallCompletedCallback callback) {
+ if (callback == NULL) return;
+ i::Isolate::EnsureDefaultIsolate();
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::V8::AddLeaveScriptCallback()")) return;
+ i::V8::AddCallCompletedCallback(callback);
+}
+
+
+void V8::RemoveCallCompletedCallback(CallCompletedCallback callback) {
+ i::Isolate::EnsureDefaultIsolate();
+ 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();
+}
+
+
+void V8::ResumeProfiler() {
+ i::Isolate* isolate = i::Isolate::Current();
+ isolate->logger()->ResumeProfiler();
+}
+
+
+bool V8::IsProfilerPaused() {
+ i::Isolate* isolate = i::Isolate::Current();
+ return isolate->logger()->IsProfilerPaused();
+}
+
+
+int V8::GetCurrentThreadId() {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "V8::GetCurrentThreadId()");
+ return isolate->thread_id().ToInteger();
+}
+
+
+void V8::TerminateExecution(int thread_id) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!isolate->IsInitialized()) return;
+ API_ENTRY_CHECK(isolate, "V8::TerminateExecution()");
+ // If the thread_id identifies the current thread just terminate
+ // execution right away. Otherwise, ask the thread manager to
+ // terminate the thread with the given id if any.
+ i::ThreadId internal_tid = i::ThreadId::FromInteger(thread_id);
+ if (isolate->thread_id().Equals(internal_tid)) {
+ isolate->stack_guard()->TerminateExecution();
+ } else {
+ isolate->thread_manager()->TerminateExecution(internal_tid);
+ }
+}
+
+
+void V8::TerminateExecution(Isolate* isolate) {
+ // If no isolate is supplied, use the default isolate.
+ if (isolate != NULL) {
+ reinterpret_cast<i::Isolate*>(isolate)->stack_guard()->TerminateExecution();
+ } else {
+ i::Isolate::GetDefaultIsolateStackGuard()->TerminateExecution();
+ }
+}
+
+
+bool V8::IsExecutionTerminating(Isolate* isolate) {
+ i::Isolate* i_isolate = isolate != NULL ?
+ reinterpret_cast<i::Isolate*>(isolate) : i::Isolate::Current();
+ return IsExecutionTerminatingCheck(i_isolate);
+}
+
+
+void V8::CancelTerminateExecution(Isolate* isolate) {
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ i_isolate->stack_guard()->CancelTerminateExecution();
+}
+
+
+Isolate* Isolate::GetCurrent() {
+ i::Isolate* isolate = i::Isolate::UncheckedCurrent();
+ return reinterpret_cast<Isolate*>(isolate);
+}
+
+
+Isolate* Isolate::New() {
+ i::Isolate* isolate = new i::Isolate();
+ return reinterpret_cast<Isolate*>(isolate);
+}
+
+
+void Isolate::Dispose() {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+ if (!ApiCheck(!isolate->IsInUse(),
+ "v8::Isolate::Dispose()",
+ "Disposing the isolate that is entered by a thread.")) {
+ return;
+ }
+ isolate->TearDown();
+}
+
+
+void Isolate::Enter() {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+ isolate->Enter();
+}
+
+
+void Isolate::Exit() {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+ isolate->Exit();
+}
+
+
+void Isolate::GetHeapStatistics(HeapStatistics* heap_statistics) {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+ if (!isolate->IsInitialized()) {
+ heap_statistics->total_heap_size_ = 0;
+ heap_statistics->total_heap_size_executable_ = 0;
+ heap_statistics->total_physical_size_ = 0;
+ heap_statistics->used_heap_size_ = 0;
+ heap_statistics->heap_size_limit_ = 0;
+ return;
+ }
+ i::Heap* heap = isolate->heap();
+ heap_statistics->total_heap_size_ = heap->CommittedMemory();
+ heap_statistics->total_heap_size_executable_ =
+ heap->CommittedMemoryExecutable();
+ heap_statistics->total_physical_size_ = heap->CommittedPhysicalMemory();
+ heap_statistics->used_heap_size_ = heap->SizeOfObjects();
+ heap_statistics->heap_size_limit_ = heap->MaxReserved();
+}
+
+
+String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj)
+ : str_(NULL), length_(0) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::String::Utf8Value::Utf8Value()")) return;
+ if (obj.IsEmpty()) return;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ TryCatch try_catch;
+ Handle<String> str = obj->ToString();
+ if (str.IsEmpty()) return;
+ i::Handle<i::String> i_str = Utils::OpenHandle(*str);
+ length_ = v8::Utf8Length(*i_str, isolate);
+ str_ = i::NewArray<char>(length_ + 1);
+ str->WriteUtf8(str_);
+}
+
+
+String::Utf8Value::~Utf8Value() {
+ i::DeleteArray(str_);
+}
+
+
+String::AsciiValue::AsciiValue(v8::Handle<v8::Value> obj)
+ : str_(NULL), length_(0) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::String::AsciiValue::AsciiValue()")) return;
+ if (obj.IsEmpty()) return;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ TryCatch try_catch;
+ Handle<String> str = obj->ToString();
+ if (str.IsEmpty()) return;
+ length_ = str->Utf8Length();
+ str_ = i::NewArray<char>(length_ + 1);
+ str->WriteUtf8(str_);
+ ASSERT(i::String::NonAsciiStart(str_, length_) >= length_);
+}
+
+
+String::AsciiValue::~AsciiValue() {
+ i::DeleteArray(str_);
+}
+
+
+String::Value::Value(v8::Handle<v8::Value> obj)
+ : str_(NULL), length_(0) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::String::Value::Value()")) return;
+ if (obj.IsEmpty()) return;
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ TryCatch try_catch;
+ Handle<String> str = obj->ToString();
+ if (str.IsEmpty()) return;
+ length_ = str->Length();
+ str_ = i::NewArray<uint16_t>(length_ + 1);
+ str->Write(str_);
+}
+
+
+String::Value::~Value() {
+ i::DeleteArray(str_);
+}
+
+
+Local<Value> Exception::RangeError(v8::Handle<v8::String> raw_message) {
+ i::Isolate* isolate = i::Isolate::Current();
+ LOG_API(isolate, "RangeError");
+ ON_BAILOUT(isolate, "v8::Exception::RangeError()", return Local<Value>());
+ ENTER_V8(isolate);
+ i::Object* error;
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::String> message = Utils::OpenHandle(*raw_message);
+ i::Handle<i::Object> result = isolate->factory()->NewRangeError(message);
+ error = *result;
+ }
+ i::Handle<i::Object> result(error, isolate);
+ return Utils::ToLocal(result);
+}
+
+
+Local<Value> Exception::ReferenceError(v8::Handle<v8::String> raw_message) {
+ i::Isolate* isolate = i::Isolate::Current();
+ LOG_API(isolate, "ReferenceError");
+ ON_BAILOUT(isolate, "v8::Exception::ReferenceError()", return Local<Value>());
+ ENTER_V8(isolate);
+ i::Object* error;
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::String> message = Utils::OpenHandle(*raw_message);
+ i::Handle<i::Object> result =
+ isolate->factory()->NewReferenceError(message);
+ error = *result;
+ }
+ i::Handle<i::Object> result(error, isolate);
+ return Utils::ToLocal(result);
+}
+
+
+Local<Value> Exception::SyntaxError(v8::Handle<v8::String> raw_message) {
+ i::Isolate* isolate = i::Isolate::Current();
+ LOG_API(isolate, "SyntaxError");
+ ON_BAILOUT(isolate, "v8::Exception::SyntaxError()", return Local<Value>());
+ ENTER_V8(isolate);
+ i::Object* error;
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::String> message = Utils::OpenHandle(*raw_message);
+ i::Handle<i::Object> result = isolate->factory()->NewSyntaxError(message);
+ error = *result;
+ }
+ i::Handle<i::Object> result(error, isolate);
+ return Utils::ToLocal(result);
+}
+
+
+Local<Value> Exception::TypeError(v8::Handle<v8::String> raw_message) {
+ i::Isolate* isolate = i::Isolate::Current();
+ LOG_API(isolate, "TypeError");
+ ON_BAILOUT(isolate, "v8::Exception::TypeError()", return Local<Value>());
+ ENTER_V8(isolate);
+ i::Object* error;
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::String> message = Utils::OpenHandle(*raw_message);
+ i::Handle<i::Object> result = isolate->factory()->NewTypeError(message);
+ error = *result;
+ }
+ i::Handle<i::Object> result(error, isolate);
+ return Utils::ToLocal(result);
+}
+
+
+Local<Value> Exception::Error(v8::Handle<v8::String> raw_message) {
+ i::Isolate* isolate = i::Isolate::Current();
+ LOG_API(isolate, "Error");
+ ON_BAILOUT(isolate, "v8::Exception::Error()", return Local<Value>());
+ ENTER_V8(isolate);
+ i::Object* error;
+ {
+ i::HandleScope scope(isolate);
+ i::Handle<i::String> message = Utils::OpenHandle(*raw_message);
+ i::Handle<i::Object> result = isolate->factory()->NewError(message);
+ error = *result;
+ }
+ i::Handle<i::Object> result(error, isolate);
+ return Utils::ToLocal(result);
+}
+
+
+// --- D e b u g S u p p o r t ---
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+static void EventCallbackWrapper(const v8::Debug::EventDetails& event_details) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (isolate->debug_event_callback() != NULL) {
+ isolate->debug_event_callback()(event_details.GetEvent(),
+ event_details.GetExecutionState(),
+ event_details.GetEventData(),
+ event_details.GetCallbackData());
+ }
+}
+
+
+bool Debug::SetDebugEventListener(EventCallback that, Handle<Value> data) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Debug::SetDebugEventListener()");
+ ON_BAILOUT(isolate, "v8::Debug::SetDebugEventListener()", return false);
+ ENTER_V8(isolate);
+
+ isolate->set_debug_event_callback(that);
+
+ i::HandleScope scope(isolate);
+ i::Handle<i::Object> foreign = isolate->factory()->undefined_value();
+ if (that != NULL) {
+ foreign =
+ isolate->factory()->NewForeign(FUNCTION_ADDR(EventCallbackWrapper));
+ }
+ isolate->debugger()->SetEventListener(foreign,
+ Utils::OpenHandle(*data, true));
+ return true;
+}
+
+
+bool Debug::SetDebugEventListener2(EventCallback2 that, Handle<Value> data) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Debug::SetDebugEventListener2()");
+ ON_BAILOUT(isolate, "v8::Debug::SetDebugEventListener2()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::Object> foreign = isolate->factory()->undefined_value();
+ if (that != NULL) {
+ foreign = isolate->factory()->NewForeign(FUNCTION_ADDR(that));
+ }
+ isolate->debugger()->SetEventListener(foreign,
+ Utils::OpenHandle(*data, true));
+ return true;
+}
+
+
+bool Debug::SetDebugEventListener(v8::Handle<v8::Object> that,
+ Handle<Value> data) {
+ i::Isolate* isolate = i::Isolate::Current();
+ ON_BAILOUT(isolate, "v8::Debug::SetDebugEventListener()", return false);
+ ENTER_V8(isolate);
+ isolate->debugger()->SetEventListener(Utils::OpenHandle(*that),
+ Utils::OpenHandle(*data, true));
+ return true;
+}
+
+
+void Debug::DebugBreak(Isolate* isolate) {
+ // If no isolate is supplied, use the default isolate.
+ if (isolate != NULL) {
+ reinterpret_cast<i::Isolate*>(isolate)->stack_guard()->DebugBreak();
+ } else {
+ i::Isolate::GetDefaultIsolateStackGuard()->DebugBreak();
+ }
+}
+
+
+void Debug::CancelDebugBreak(Isolate* isolate) {
+ // If no isolate is supplied, use the default isolate.
+ if (isolate != NULL) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ internal_isolate->stack_guard()->Continue(i::DEBUGBREAK);
+ } else {
+ i::Isolate::GetDefaultIsolateStackGuard()->Continue(i::DEBUGBREAK);
+ }
+}
+
+
+void Debug::DebugBreakForCommand(ClientData* data, Isolate* isolate) {
+ // If no isolate is supplied, use the default isolate.
+ if (isolate != NULL) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ internal_isolate->debugger()->EnqueueDebugCommand(data);
+ } else {
+ i::Isolate::GetDefaultIsolateDebugger()->EnqueueDebugCommand(data);
+ }
+}
+
+
+static void MessageHandlerWrapper(const v8::Debug::Message& message) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (isolate->message_handler()) {
+ v8::String::Value json(message.GetJSON());
+ (isolate->message_handler())(*json, json.length(), message.GetClientData());
+ }
+}
+
+
+void Debug::SetMessageHandler(v8::Debug::MessageHandler handler,
+ bool message_handler_thread) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Debug::SetMessageHandler");
+ ENTER_V8(isolate);
+
+ // Message handler thread not supported any more. Parameter temporally left in
+ // the API for client compatibility reasons.
+ CHECK(!message_handler_thread);
+
+ // TODO(sgjesse) support the old message handler API through a simple wrapper.
+ isolate->set_message_handler(handler);
+ if (handler != NULL) {
+ isolate->debugger()->SetMessageHandler(MessageHandlerWrapper);
+ } else {
+ isolate->debugger()->SetMessageHandler(NULL);
+ }
+}
+
+
+void Debug::SetMessageHandler2(v8::Debug::MessageHandler2 handler) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Debug::SetMessageHandler");
+ ENTER_V8(isolate);
+ isolate->debugger()->SetMessageHandler(handler);
+}
+
+
+void Debug::SendCommand(const uint16_t* command, int length,
+ ClientData* client_data,
+ Isolate* isolate) {
+ // If no isolate is supplied, use the default isolate.
+ if (isolate != NULL) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ internal_isolate->debugger()->ProcessCommand(
+ i::Vector<const uint16_t>(command, length), client_data);
+ } else {
+ i::Isolate::GetDefaultIsolateDebugger()->ProcessCommand(
+ i::Vector<const uint16_t>(command, length), client_data);
+ }
+}
+
+
+void Debug::SetHostDispatchHandler(HostDispatchHandler handler,
+ int period) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Debug::SetHostDispatchHandler");
+ ENTER_V8(isolate);
+ isolate->debugger()->SetHostDispatchHandler(handler, period);
+}
+
+
+void Debug::SetDebugMessageDispatchHandler(
+ DebugMessageDispatchHandler handler, bool provide_locker) {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate,
+ "v8::Debug::SetDebugMessageDispatchHandler");
+ ENTER_V8(isolate);
+ isolate->debugger()->SetDebugMessageDispatchHandler(
+ handler, provide_locker);
+}
+
+
+Local<Value> Debug::Call(v8::Handle<v8::Function> fun,
+ v8::Handle<v8::Value> data) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!isolate->IsInitialized()) return Local<Value>();
+ ON_BAILOUT(isolate, "v8::Debug::Call()", return Local<Value>());
+ ENTER_V8(isolate);
+ i::Handle<i::Object> result;
+ EXCEPTION_PREAMBLE(isolate);
+ if (data.IsEmpty()) {
+ result = isolate->debugger()->Call(Utils::OpenHandle(*fun),
+ isolate->factory()->undefined_value(),
+ &has_pending_exception);
+ } else {
+ result = isolate->debugger()->Call(Utils::OpenHandle(*fun),
+ Utils::OpenHandle(*data),
+ &has_pending_exception);
+ }
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+ return Utils::ToLocal(result);
+}
+
+
+Local<Value> Debug::GetMirror(v8::Handle<v8::Value> obj) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (!isolate->IsInitialized()) return Local<Value>();
+ ON_BAILOUT(isolate, "v8::Debug::GetMirror()", return Local<Value>());
+ ENTER_V8(isolate);
+ v8::HandleScope scope(reinterpret_cast<Isolate*>(isolate));
+ i::Debug* isolate_debug = isolate->debug();
+ isolate_debug->Load();
+ i::Handle<i::JSObject> debug(isolate_debug->debug_context()->global_object());
+ i::Handle<i::String> name = isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("MakeMirror"));
+ i::Handle<i::Object> fun_obj = i::GetProperty(isolate, debug, name);
+ i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(fun_obj);
+ v8::Handle<v8::Function> v8_fun = Utils::ToLocal(fun);
+ const int kArgc = 1;
+ v8::Handle<v8::Value> argv[kArgc] = { obj };
+ EXCEPTION_PREAMBLE(isolate);
+ v8::Handle<v8::Value> result = v8_fun->Call(Utils::ToLocal(debug),
+ kArgc,
+ argv);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+ return scope.Close(result);
+}
+
+
+bool Debug::EnableAgent(const char* name, int port, bool wait_for_connection) {
+ return i::Isolate::Current()->debugger()->StartAgent(name, port,
+ wait_for_connection);
+}
+
+
+void Debug::DisableAgent() {
+ return i::Isolate::Current()->debugger()->StopAgent();
+}
+
+
+void Debug::ProcessDebugMessages() {
+ i::Execution::ProcessDebugMessages(true);
+}
+
+
+Local<Context> Debug::GetDebugContext() {
+ i::Isolate* isolate = i::Isolate::Current();
+ EnsureInitializedForIsolate(isolate, "v8::Debug::GetDebugContext()");
+ ENTER_V8(isolate);
+ return Utils::ToLocal(i::Isolate::Current()->debugger()->GetDebugContext());
+}
+
+
+void Debug::SetLiveEditEnabled(bool enable, Isolate* isolate) {
+ // If no isolate is supplied, use the default isolate.
+ i::Debugger* debugger;
+ if (isolate != NULL) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ debugger = internal_isolate->debugger();
+ } else {
+ debugger = i::Isolate::GetDefaultIsolateDebugger();
+ }
+ debugger->set_live_edit_enabled(enable);
+}
+
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+Handle<String> CpuProfileNode::GetFunctionName() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::CpuProfileNode::GetFunctionName");
+ const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
+ const i::CodeEntry* entry = node->entry();
+ if (!entry->has_name_prefix()) {
+ return ToApiHandle<String>(
+ isolate->factory()->InternalizeUtf8String(entry->name()));
+ } else {
+ return ToApiHandle<String>(isolate->factory()->NewConsString(
+ isolate->factory()->InternalizeUtf8String(entry->name_prefix()),
+ isolate->factory()->InternalizeUtf8String(entry->name())));
+ }
+}
+
+
+int CpuProfileNode::GetScriptId() const {
+ const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
+ const i::CodeEntry* entry = node->entry();
+ return entry->script_id();
+}
+
+
+Handle<String> CpuProfileNode::GetScriptResourceName() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::CpuProfileNode::GetScriptResourceName");
+ const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
+ return ToApiHandle<String>(isolate->factory()->InternalizeUtf8String(
+ node->entry()->resource_name()));
+}
+
+
+int CpuProfileNode::GetLineNumber() const {
+ return reinterpret_cast<const i::ProfileNode*>(this)->entry()->line_number();
+}
+
+
+double CpuProfileNode::GetTotalTime() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::CpuProfileNode::GetTotalTime");
+ return reinterpret_cast<const i::ProfileNode*>(this)->GetTotalMillis();
+}
+
+
+double CpuProfileNode::GetSelfTime() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::CpuProfileNode::GetSelfTime");
+ return reinterpret_cast<const i::ProfileNode*>(this)->GetSelfMillis();
+}
+
+
+double CpuProfileNode::GetTotalSamplesCount() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::CpuProfileNode::GetTotalSamplesCount");
+ return reinterpret_cast<const i::ProfileNode*>(this)->total_ticks();
+}
+
+
+double CpuProfileNode::GetSelfSamplesCount() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::CpuProfileNode::GetSelfSamplesCount");
+ return reinterpret_cast<const i::ProfileNode*>(this)->self_ticks();
+}
+
+
+unsigned CpuProfileNode::GetHitCount() const {
+ return reinterpret_cast<const i::ProfileNode*>(this)->self_ticks();
+}
+
+
+unsigned CpuProfileNode::GetCallUid() const {
+ return reinterpret_cast<const i::ProfileNode*>(this)->entry()->GetCallUid();
+}
+
+
+unsigned CpuProfileNode::GetNodeId() const {
+ return reinterpret_cast<const i::ProfileNode*>(this)->id();
+}
+
+
+int CpuProfileNode::GetChildrenCount() const {
+ return reinterpret_cast<const i::ProfileNode*>(this)->children()->length();
+}
+
+
+const CpuProfileNode* CpuProfileNode::GetChild(int index) const {
+ const i::ProfileNode* child =
+ reinterpret_cast<const i::ProfileNode*>(this)->children()->at(index);
+ return reinterpret_cast<const CpuProfileNode*>(child);
+}
+
+
+void CpuProfile::Delete() {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::CpuProfile::Delete");
+ i::CpuProfiler* profiler = isolate->cpu_profiler();
+ ASSERT(profiler != NULL);
+ profiler->DeleteProfile(reinterpret_cast<i::CpuProfile*>(this));
+ if (profiler->GetProfilesCount() == 0) {
+ // If this was the last profile, clean up all accessory data as well.
+ profiler->DeleteAllProfiles();
+ }
+}
+
+
+unsigned CpuProfile::GetUid() const {
+ return reinterpret_cast<const i::CpuProfile*>(this)->uid();
+}
+
+
+Handle<String> CpuProfile::GetTitle() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::CpuProfile::GetTitle");
+ const i::CpuProfile* profile = reinterpret_cast<const i::CpuProfile*>(this);
+ return ToApiHandle<String>(isolate->factory()->InternalizeUtf8String(
+ profile->title()));
+}
+
+
+const CpuProfileNode* CpuProfile::GetTopDownRoot() const {
+ const i::CpuProfile* profile = reinterpret_cast<const i::CpuProfile*>(this);
+ return reinterpret_cast<const CpuProfileNode*>(profile->top_down()->root());
+}
+
+
+const CpuProfileNode* CpuProfile::GetSample(int index) const {
+ const i::CpuProfile* profile = reinterpret_cast<const i::CpuProfile*>(this);
+ return reinterpret_cast<const CpuProfileNode*>(profile->sample(index));
+}
+
+
+int64_t CpuProfile::GetStartTime() const {
+ const i::CpuProfile* profile = reinterpret_cast<const i::CpuProfile*>(this);
+ return profile->start_time_us();
+}
+
+
+int64_t CpuProfile::GetEndTime() const {
+ const i::CpuProfile* profile = reinterpret_cast<const i::CpuProfile*>(this);
+ return profile->end_time_us();
+}
+
+
+int CpuProfile::GetSamplesCount() const {
+ return reinterpret_cast<const i::CpuProfile*>(this)->samples_count();
+}
+
+
+int CpuProfiler::GetProfileCount() {
+ return reinterpret_cast<i::CpuProfiler*>(this)->GetProfilesCount();
+}
+
+
+const CpuProfile* CpuProfiler::GetCpuProfile(int index) {
+ return reinterpret_cast<const CpuProfile*>(
+ reinterpret_cast<i::CpuProfiler*>(this)->GetProfile(index));
+}
+
+
+void CpuProfiler::StartCpuProfiling(Handle<String> title, bool record_samples) {
+ reinterpret_cast<i::CpuProfiler*>(this)->StartProfiling(
+ *Utils::OpenHandle(*title), record_samples);
+}
+
+
+const CpuProfile* CpuProfiler::StopCpuProfiling(Handle<String> title) {
+ return reinterpret_cast<const CpuProfile*>(
+ reinterpret_cast<i::CpuProfiler*>(this)->StopProfiling(
+ *Utils::OpenHandle(*title)));
+}
+
+
+void CpuProfiler::DeleteAllCpuProfiles() {
+ reinterpret_cast<i::CpuProfiler*>(this)->DeleteAllProfiles();
+}
+
+
+void CpuProfiler::SetIdle(bool is_idle) {
+ i::Isolate* isolate = reinterpret_cast<i::CpuProfiler*>(this)->isolate();
+ i::StateTag state = isolate->current_vm_state();
+ ASSERT(state == i::EXTERNAL || state == i::IDLE);
+ if (isolate->js_entry_sp() != NULL) return;
+ if (is_idle) {
+ isolate->set_current_vm_state(i::IDLE);
+ } else if (state == i::IDLE) {
+ isolate->set_current_vm_state(i::EXTERNAL);
+ }
+}
+
+
+static i::HeapGraphEdge* ToInternal(const HeapGraphEdge* edge) {
+ return const_cast<i::HeapGraphEdge*>(
+ reinterpret_cast<const i::HeapGraphEdge*>(edge));
+}
+
+
+HeapGraphEdge::Type HeapGraphEdge::GetType() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphEdge::GetType");
+ return static_cast<HeapGraphEdge::Type>(ToInternal(this)->type());
+}
+
+
+Handle<Value> HeapGraphEdge::GetName() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphEdge::GetName");
+ i::HeapGraphEdge* edge = ToInternal(this);
+ switch (edge->type()) {
+ case i::HeapGraphEdge::kContextVariable:
+ case i::HeapGraphEdge::kInternal:
+ case i::HeapGraphEdge::kProperty:
+ case i::HeapGraphEdge::kShortcut:
+ return ToApiHandle<String>(
+ isolate->factory()->InternalizeUtf8String(edge->name()));
+ case i::HeapGraphEdge::kElement:
+ case i::HeapGraphEdge::kHidden:
+ case i::HeapGraphEdge::kWeak:
+ return ToApiHandle<Number>(
+ isolate->factory()->NewNumberFromInt(edge->index()));
+ default: UNREACHABLE();
+ }
+ return v8::Undefined();
+}
+
+
+const HeapGraphNode* HeapGraphEdge::GetFromNode() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphEdge::GetFromNode");
+ const i::HeapEntry* from = ToInternal(this)->from();
+ return reinterpret_cast<const HeapGraphNode*>(from);
+}
+
+
+const HeapGraphNode* HeapGraphEdge::GetToNode() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphEdge::GetToNode");
+ const i::HeapEntry* to = ToInternal(this)->to();
+ return reinterpret_cast<const HeapGraphNode*>(to);
+}
+
+
+static i::HeapEntry* ToInternal(const HeapGraphNode* entry) {
+ return const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(entry));
+}
+
+
+HeapGraphNode::Type HeapGraphNode::GetType() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphNode::GetType");
+ return static_cast<HeapGraphNode::Type>(ToInternal(this)->type());
+}
+
+
+Handle<String> HeapGraphNode::GetName() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphNode::GetName");
+ return ToApiHandle<String>(
+ isolate->factory()->InternalizeUtf8String(ToInternal(this)->name()));
+}
+
+
+SnapshotObjectId HeapGraphNode::GetId() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphNode::GetId");
+ return ToInternal(this)->id();
+}
+
+
+int HeapGraphNode::GetSelfSize() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphNode::GetSelfSize");
+ return ToInternal(this)->self_size();
+}
+
+
+int HeapGraphNode::GetChildrenCount() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetChildrenCount");
+ return ToInternal(this)->children().length();
+}
+
+
+const HeapGraphEdge* HeapGraphNode::GetChild(int index) const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetChild");
+ return reinterpret_cast<const HeapGraphEdge*>(
+ ToInternal(this)->children()[index]);
+}
+
+
+v8::Handle<v8::Value> HeapGraphNode::GetHeapValue() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapGraphNode::GetHeapValue");
+ i::Handle<i::HeapObject> object = ToInternal(this)->GetHeapObject();
+ return !object.is_null() ?
+ ToApiHandle<Value>(object) :
+ ToApiHandle<Value>(isolate->factory()->undefined_value());
+}
+
+
+static i::HeapSnapshot* ToInternal(const HeapSnapshot* snapshot) {
+ return const_cast<i::HeapSnapshot*>(
+ reinterpret_cast<const i::HeapSnapshot*>(snapshot));
+}
+
+
+void HeapSnapshot::Delete() {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::Delete");
+ if (isolate->heap_profiler()->GetSnapshotsCount() > 1) {
+ ToInternal(this)->Delete();
+ } else {
+ // If this is the last snapshot, clean up all accessory data as well.
+ isolate->heap_profiler()->DeleteAllSnapshots();
+ }
+}
+
+
+unsigned HeapSnapshot::GetUid() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetUid");
+ return ToInternal(this)->uid();
+}
+
+
+Handle<String> HeapSnapshot::GetTitle() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetTitle");
+ return ToApiHandle<String>(
+ isolate->factory()->InternalizeUtf8String(ToInternal(this)->title()));
+}
+
+
+const HeapGraphNode* HeapSnapshot::GetRoot() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetHead");
+ return reinterpret_cast<const HeapGraphNode*>(ToInternal(this)->root());
+}
+
+
+const HeapGraphNode* HeapSnapshot::GetNodeById(SnapshotObjectId id) const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetNodeById");
+ return reinterpret_cast<const HeapGraphNode*>(
+ ToInternal(this)->GetEntryById(id));
+}
+
+
+int HeapSnapshot::GetNodesCount() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetNodesCount");
+ return ToInternal(this)->entries().length();
+}
+
+
+const HeapGraphNode* HeapSnapshot::GetNode(int index) const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetNode");
+ return reinterpret_cast<const HeapGraphNode*>(
+ &ToInternal(this)->entries().at(index));
+}
+
+
+SnapshotObjectId HeapSnapshot::GetMaxSnapshotJSObjectId() const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::GetMaxSnapshotJSObjectId");
+ return ToInternal(this)->max_snapshot_js_object_id();
+}
+
+
+void HeapSnapshot::Serialize(OutputStream* stream,
+ HeapSnapshot::SerializationFormat format) const {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapSnapshot::Serialize");
+ ApiCheck(format == kJSON,
+ "v8::HeapSnapshot::Serialize",
+ "Unknown serialization format");
+ ApiCheck(stream->GetOutputEncoding() == OutputStream::kAscii,
+ "v8::HeapSnapshot::Serialize",
+ "Unsupported output encoding");
+ ApiCheck(stream->GetChunkSize() > 0,
+ "v8::HeapSnapshot::Serialize",
+ "Invalid stream chunk size");
+ i::HeapSnapshotJSONSerializer serializer(ToInternal(this));
+ serializer.Serialize(stream);
+}
+
+
+int HeapProfiler::GetSnapshotCount() {
+ return reinterpret_cast<i::HeapProfiler*>(this)->GetSnapshotsCount();
+}
+
+
+const HeapSnapshot* HeapProfiler::GetHeapSnapshot(int index) {
+ return reinterpret_cast<const HeapSnapshot*>(
+ reinterpret_cast<i::HeapProfiler*>(this)->GetSnapshot(index));
+}
+
+
+SnapshotObjectId HeapProfiler::GetObjectId(Handle<Value> value) {
+ i::Handle<i::Object> obj = Utils::OpenHandle(*value);
+ return reinterpret_cast<i::HeapProfiler*>(this)->GetSnapshotObjectId(obj);
+}
+
+
+const HeapSnapshot* HeapProfiler::TakeHeapSnapshot(
+ Handle<String> title,
+ ActivityControl* control,
+ ObjectNameResolver* resolver) {
+ return reinterpret_cast<const HeapSnapshot*>(
+ reinterpret_cast<i::HeapProfiler*>(this)->TakeSnapshot(
+ *Utils::OpenHandle(*title), control, resolver));
+}
+
+
+void HeapProfiler::StartTrackingHeapObjects() {
+ reinterpret_cast<i::HeapProfiler*>(this)->StartHeapObjectsTracking();
+}
+
+
+void HeapProfiler::StopTrackingHeapObjects() {
+ reinterpret_cast<i::HeapProfiler*>(this)->StopHeapObjectsTracking();
+}
+
+
+SnapshotObjectId HeapProfiler::GetHeapStats(OutputStream* stream) {
+ return reinterpret_cast<i::HeapProfiler*>(this)->PushHeapObjectsStats(stream);
+}
+
+
+void HeapProfiler::DeleteAllHeapSnapshots() {
+ reinterpret_cast<i::HeapProfiler*>(this)->DeleteAllSnapshots();
+}
+
+
+void HeapProfiler::SetWrapperClassInfoProvider(uint16_t class_id,
+ WrapperInfoCallback callback) {
+ reinterpret_cast<i::HeapProfiler*>(this)->DefineWrapperClass(class_id,
+ callback);
+}
+
+
+size_t HeapProfiler::GetProfilerMemorySize() {
+ return reinterpret_cast<i::HeapProfiler*>(this)->
+ GetMemorySizeUsedByProfiler();
+}
+
+
+void HeapProfiler::SetRetainedObjectInfo(UniqueId id,
+ RetainedObjectInfo* info) {
+ reinterpret_cast<i::HeapProfiler*>(this)->SetRetainedObjectInfo(id, info);
+}
+
+
+v8::Testing::StressType internal::Testing::stress_type_ =
+ v8::Testing::kStressTypeOpt;
+
+
+void Testing::SetStressRunType(Testing::StressType type) {
+ internal::Testing::set_stress_type(type);
+}
+
+
+int Testing::GetStressRuns() {
+ if (internal::FLAG_stress_runs != 0) return internal::FLAG_stress_runs;
+#ifdef DEBUG
+ // In debug mode the code runs much slower so stressing will only make two
+ // runs.
+ return 2;
+#else
+ return 5;
+#endif
+}
+
+
+static void SetFlagsFromString(const char* flags) {
+ V8::SetFlagsFromString(flags, i::StrLength(flags));
+}
+
+
+void Testing::PrepareStressRun(int run) {
+ static const char* kLazyOptimizations =
+ "--prepare-always-opt "
+ "--max-inlined-source-size=999999 "
+ "--max-inlined-nodes=999999 "
+ "--max-inlined-nodes-cumulative=999999 "
+ "--noalways-opt";
+ static const char* kForcedOptimizations = "--always-opt";
+
+ // If deoptimization stressed turn on frequent deoptimization. If no value
+ // is spefified through --deopt-every-n-times use a default default value.
+ static const char* kDeoptEvery13Times = "--deopt-every-n-times=13";
+ if (internal::Testing::stress_type() == Testing::kStressTypeDeopt &&
+ internal::FLAG_deopt_every_n_times == 0) {
+ SetFlagsFromString(kDeoptEvery13Times);
+ }
+
+#ifdef DEBUG
+ // As stressing in debug mode only make two runs skip the deopt stressing
+ // here.
+ if (run == GetStressRuns() - 1) {
+ SetFlagsFromString(kForcedOptimizations);
+ } else {
+ SetFlagsFromString(kLazyOptimizations);
+ }
+#else
+ if (run == GetStressRuns() - 1) {
+ SetFlagsFromString(kForcedOptimizations);
+ } else if (run != GetStressRuns() - 2) {
+ SetFlagsFromString(kLazyOptimizations);
+ }
+#endif
+}
+
+
+// TODO(svenpanne) Deprecate this.
+void Testing::DeoptimizeAll() {
+ i::Isolate* isolate = i::Isolate::Current();
+ i::HandleScope scope(isolate);
+ internal::Deoptimizer::DeoptimizeAll(isolate);
+}
+
+
+namespace internal {
+
+
+void HandleScopeImplementer::FreeThreadResources() {
+ Free();
+}
+
+
+char* HandleScopeImplementer::ArchiveThread(char* storage) {
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate_->handle_scope_data();
+ handle_scope_data_ = *current;
+ OS::MemCopy(storage, this, sizeof(*this));
+
+ ResetAfterArchive();
+ current->Initialize();
+
+ return storage + ArchiveSpacePerThread();
+}
+
+
+int HandleScopeImplementer::ArchiveSpacePerThread() {
+ return sizeof(HandleScopeImplementer);
+}
+
+
+char* HandleScopeImplementer::RestoreThread(char* storage) {
+ OS::MemCopy(this, storage, sizeof(*this));
+ *isolate_->handle_scope_data() = handle_scope_data_;
+ return storage + ArchiveSpacePerThread();
+}
+
+
+void HandleScopeImplementer::IterateThis(ObjectVisitor* v) {
+#ifdef DEBUG
+ bool found_block_before_deferred = false;
+#endif
+ // Iterate over all handles in the blocks except for the last.
+ for (int i = blocks()->length() - 2; i >= 0; --i) {
+ Object** block = blocks()->at(i);
+ if (last_handle_before_deferred_block_ != NULL &&
+ (last_handle_before_deferred_block_ <= &block[kHandleBlockSize]) &&
+ (last_handle_before_deferred_block_ >= block)) {
+ v->VisitPointers(block, last_handle_before_deferred_block_);
+ ASSERT(!found_block_before_deferred);
+#ifdef DEBUG
+ found_block_before_deferred = true;
+#endif
+ } else {
+ v->VisitPointers(block, &block[kHandleBlockSize]);
+ }
+ }
+
+ ASSERT(last_handle_before_deferred_block_ == NULL ||
+ found_block_before_deferred);
+
+ // Iterate over live handles in the last block (if any).
+ if (!blocks()->is_empty()) {
+ v->VisitPointers(blocks()->last(), handle_scope_data_.next);
+ }
+
+ if (!saved_contexts_.is_empty()) {
+ Object** start = reinterpret_cast<Object**>(&saved_contexts_.first());
+ v->VisitPointers(start, start + saved_contexts_.length());
+ }
+}
+
+
+void HandleScopeImplementer::Iterate(ObjectVisitor* v) {
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate_->handle_scope_data();
+ handle_scope_data_ = *current;
+ IterateThis(v);
+}
+
+
+char* HandleScopeImplementer::Iterate(ObjectVisitor* v, char* storage) {
+ HandleScopeImplementer* scope_implementer =
+ reinterpret_cast<HandleScopeImplementer*>(storage);
+ scope_implementer->IterateThis(v);
+ return storage + ArchiveSpacePerThread();
+}
+
+
+DeferredHandles* HandleScopeImplementer::Detach(Object** prev_limit) {
+ DeferredHandles* deferred =
+ new DeferredHandles(isolate()->handle_scope_data()->next, isolate());
+
+ while (!blocks_.is_empty()) {
+ Object** block_start = blocks_.last();
+ Object** block_limit = &block_start[kHandleBlockSize];
+ // We should not need to check for SealHandleScope here. Assert this.
+ ASSERT(prev_limit == block_limit ||
+ !(block_start <= prev_limit && prev_limit <= block_limit));
+ if (prev_limit == block_limit) break;
+ deferred->blocks_.Add(blocks_.last());
+ blocks_.RemoveLast();
+ }
+
+ // deferred->blocks_ now contains the blocks installed on the
+ // HandleScope stack since BeginDeferredScope was called, but in
+ // reverse order.
+
+ ASSERT(prev_limit == NULL || !blocks_.is_empty());
+
+ ASSERT(!blocks_.is_empty() && prev_limit != NULL);
+ ASSERT(last_handle_before_deferred_block_ != NULL);
+ last_handle_before_deferred_block_ = NULL;
+ return deferred;
+}
+
+
+void HandleScopeImplementer::BeginDeferredScope() {
+ ASSERT(last_handle_before_deferred_block_ == NULL);
+ last_handle_before_deferred_block_ = isolate()->handle_scope_data()->next;
+}
+
+
+DeferredHandles::~DeferredHandles() {
+ isolate_->UnlinkDeferredHandles(this);
+
+ for (int i = 0; i < blocks_.length(); i++) {
+#ifdef ENABLE_EXTRA_CHECKS
+ HandleScope::ZapRange(blocks_[i], &blocks_[i][kHandleBlockSize]);
+#endif
+ isolate_->handle_scope_implementer()->ReturnBlock(blocks_[i]);
+ }
+}
+
+
+void DeferredHandles::Iterate(ObjectVisitor* v) {
+ ASSERT(!blocks_.is_empty());
+
+ ASSERT((first_block_limit_ >= blocks_.first()) &&
+ (first_block_limit_ <= &(blocks_.first())[kHandleBlockSize]));
+
+ v->VisitPointers(blocks_.first(), first_block_limit_);
+
+ for (int i = 1; i < blocks_.length(); i++) {
+ v->VisitPointers(blocks_[i], &blocks_[i][kHandleBlockSize]);
+ }
+}
+
+
+v8::Handle<v8::Value> InvokeAccessorGetter(
+ v8::Local<v8::String> property,
+ const v8::AccessorInfo& info,
+ v8::AccessorGetter getter) {
+ Isolate* isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
+ Address getter_address = reinterpret_cast<Address>(reinterpret_cast<intptr_t>(
+ getter));
+ // Leaving JavaScript.
+ VMState<EXTERNAL> state(isolate);
+ ExternalCallbackScope call_scope(isolate, getter_address);
+ return getter(property, info);
+}
+
+
+void InvokeAccessorGetterCallback(
+ v8::Local<v8::String> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info,
+ v8::AccessorGetterCallback getter) {
+ // Leaving JavaScript.
+ Isolate* isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
+ Address getter_address = reinterpret_cast<Address>(reinterpret_cast<intptr_t>(
+ getter));
+ VMState<EXTERNAL> state(isolate);
+ ExternalCallbackScope call_scope(isolate, getter_address);
+ return getter(property, info);
+}
+
+
+v8::Handle<v8::Value> InvokeInvocationCallback(
+ const v8::Arguments& args,
+ v8::InvocationCallback callback) {
+ Isolate* isolate = reinterpret_cast<Isolate*>(args.GetIsolate());
+ Address callback_address =
+ reinterpret_cast<Address>(reinterpret_cast<intptr_t>(callback));
+ VMState<EXTERNAL> state(isolate);
+ ExternalCallbackScope call_scope(isolate, callback_address);
+ return callback(args);
+}
+
+
+void InvokeFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
+ v8::FunctionCallback callback) {
+ Isolate* isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
+ Address callback_address =
+ reinterpret_cast<Address>(reinterpret_cast<intptr_t>(callback));
+ VMState<EXTERNAL> state(isolate);
+ ExternalCallbackScope call_scope(isolate, callback_address);
+ return callback(info);
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/api.h b/chromium/v8/src/api.h
new file mode 100644
index 00000000000..0f33bc815fe
--- /dev/null
+++ b/chromium/v8/src/api.h
@@ -0,0 +1,722 @@
+// Copyright 2012 the V8 project authors. 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_API_H_
+#define V8_API_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 {
+
+// Constants used in the implementation of the API. The most natural thing
+// would usually be to place these with the classes that use them, but
+// we want to keep them out of v8.h because it is an externally
+// visible file.
+class Consts {
+ public:
+ enum TemplateType {
+ FUNCTION_TEMPLATE = 0,
+ OBJECT_TEMPLATE = 1
+ };
+};
+
+
+// Utilities for working with neander-objects, primitive
+// env-independent JSObjects used by the api.
+class NeanderObject {
+ public:
+ explicit NeanderObject(int size);
+ explicit inline NeanderObject(v8::internal::Handle<v8::internal::Object> obj);
+ explicit inline NeanderObject(v8::internal::Object* obj);
+ inline v8::internal::Object* get(int index);
+ inline void set(int index, v8::internal::Object* value);
+ inline v8::internal::Handle<v8::internal::JSObject> value() { return value_; }
+ int size();
+ private:
+ v8::internal::Handle<v8::internal::JSObject> value_;
+};
+
+
+// Utilities for working with neander-arrays, a simple extensible
+// array abstraction built on neander-objects.
+class NeanderArray {
+ public:
+ NeanderArray();
+ explicit inline NeanderArray(v8::internal::Handle<v8::internal::Object> obj);
+ inline v8::internal::Handle<v8::internal::JSObject> value() {
+ return obj_.value();
+ }
+
+ void add(v8::internal::Handle<v8::internal::Object> value);
+
+ int length();
+
+ v8::internal::Object* get(int index);
+ // Change the value at an index to undefined value. If the index is
+ // out of bounds, the request is ignored. Returns the old value.
+ void set(int index, v8::internal::Object* value);
+ private:
+ NeanderObject obj_;
+};
+
+
+NeanderObject::NeanderObject(v8::internal::Handle<v8::internal::Object> obj)
+ : value_(v8::internal::Handle<v8::internal::JSObject>::cast(obj)) { }
+
+
+NeanderObject::NeanderObject(v8::internal::Object* obj)
+ : value_(v8::internal::Handle<v8::internal::JSObject>(
+ v8::internal::JSObject::cast(obj))) { }
+
+
+NeanderArray::NeanderArray(v8::internal::Handle<v8::internal::Object> obj)
+ : obj_(obj) { }
+
+
+v8::internal::Object* NeanderObject::get(int offset) {
+ ASSERT(value()->HasFastObjectElements());
+ return v8::internal::FixedArray::cast(value()->elements())->get(offset);
+}
+
+
+void NeanderObject::set(int offset, v8::internal::Object* value) {
+ ASSERT(value_->HasFastObjectElements());
+ v8::internal::FixedArray::cast(value_->elements())->set(offset, value);
+}
+
+
+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)->foreign_address()));
+}
+
+
+template <typename T>
+inline v8::internal::Handle<v8::internal::Object> FromCData(T obj) {
+ v8::internal::Isolate* isolate = v8::internal::Isolate::Current();
+ STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address));
+ return isolate->factory()->NewForeign(
+ reinterpret_cast<v8::internal::Address>(reinterpret_cast<intptr_t>(obj)));
+}
+
+
+class ApiFunction {
+ public:
+ explicit ApiFunction(v8::internal::Address addr) : addr_(addr) { }
+ v8::internal::Address address() { return addr_; }
+ private:
+ v8::internal::Address addr_;
+};
+
+
+
+class RegisteredExtension {
+ public:
+ explicit RegisteredExtension(Extension* extension);
+ static void Register(RegisteredExtension* that);
+ static void UnregisterAll();
+ Extension* extension() { return extension_; }
+ RegisteredExtension* next() { return next_; }
+ static RegisteredExtension* first_extension() { return first_extension_; }
+ private:
+ Extension* extension_;
+ RegisteredExtension* next_;
+ static RegisteredExtension* first_extension_;
+};
+
+
+#define OPEN_HANDLE_LIST(V) \
+ V(Template, TemplateInfo) \
+ V(FunctionTemplate, FunctionTemplateInfo) \
+ V(ObjectTemplate, ObjectTemplateInfo) \
+ V(Signature, SignatureInfo) \
+ V(AccessorSignature, FunctionTemplateInfo) \
+ V(TypeSwitch, TypeSwitchInfo) \
+ V(Data, Object) \
+ V(RegExp, JSRegExp) \
+ V(Object, JSObject) \
+ V(Array, JSArray) \
+ V(ArrayBuffer, JSArrayBuffer) \
+ V(ArrayBufferView, JSArrayBufferView) \
+ V(TypedArray, JSTypedArray) \
+ V(Uint8Array, JSTypedArray) \
+ V(Uint8ClampedArray, JSTypedArray) \
+ V(Int8Array, JSTypedArray) \
+ V(Uint16Array, JSTypedArray) \
+ V(Int16Array, JSTypedArray) \
+ V(Uint32Array, JSTypedArray) \
+ V(Int32Array, JSTypedArray) \
+ V(Float32Array, JSTypedArray) \
+ V(Float64Array, JSTypedArray) \
+ V(DataView, JSDataView) \
+ V(String, String) \
+ V(Symbol, Symbol) \
+ V(Script, Object) \
+ V(Function, JSFunction) \
+ V(Message, JSObject) \
+ V(Context, Context) \
+ V(External, Foreign) \
+ V(StackTrace, JSArray) \
+ V(StackFrame, JSObject) \
+ V(DeclaredAccessorDescriptor, DeclaredAccessorDescriptor)
+
+
+class Utils {
+ public:
+ static bool ReportApiFailure(const char* location, const char* message);
+
+ static Local<FunctionTemplate> ToFunctionTemplate(NeanderObject obj);
+ static Local<ObjectTemplate> ToObjectTemplate(NeanderObject obj);
+
+ static inline Local<Context> ToLocal(
+ v8::internal::Handle<v8::internal::Context> obj);
+ static inline Local<Value> ToLocal(
+ v8::internal::Handle<v8::internal::Object> obj);
+ static inline Local<Function> ToLocal(
+ v8::internal::Handle<v8::internal::JSFunction> obj);
+ static inline Local<String> ToLocal(
+ v8::internal::Handle<v8::internal::String> obj);
+ static inline Local<Symbol> ToLocal(
+ v8::internal::Handle<v8::internal::Symbol> obj);
+ static inline Local<RegExp> ToLocal(
+ v8::internal::Handle<v8::internal::JSRegExp> obj);
+ static inline Local<Object> ToLocal(
+ v8::internal::Handle<v8::internal::JSObject> obj);
+ static inline Local<Array> ToLocal(
+ v8::internal::Handle<v8::internal::JSArray> obj);
+ static inline Local<ArrayBuffer> ToLocal(
+ v8::internal::Handle<v8::internal::JSArrayBuffer> obj);
+ static inline Local<ArrayBufferView> ToLocal(
+ v8::internal::Handle<v8::internal::JSArrayBufferView> obj);
+ static inline Local<DataView> ToLocal(
+ v8::internal::Handle<v8::internal::JSDataView> obj);
+
+ static inline Local<TypedArray> ToLocal(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Uint8Array> ToLocalUint8Array(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Uint8ClampedArray> ToLocalUint8ClampedArray(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Int8Array> ToLocalInt8Array(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Uint16Array> ToLocalUint16Array(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Int16Array> ToLocalInt16Array(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Uint32Array> ToLocalUint32Array(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Int32Array> ToLocalInt32Array(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Float32Array> ToLocalFloat32Array(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+ static inline Local<Float64Array> ToLocalFloat64Array(
+ v8::internal::Handle<v8::internal::JSTypedArray> obj);
+
+ static inline Local<Message> MessageToLocal(
+ v8::internal::Handle<v8::internal::Object> obj);
+ static inline Local<StackTrace> StackTraceToLocal(
+ v8::internal::Handle<v8::internal::JSArray> obj);
+ static inline Local<StackFrame> StackFrameToLocal(
+ v8::internal::Handle<v8::internal::JSObject> obj);
+ static inline Local<Number> NumberToLocal(
+ v8::internal::Handle<v8::internal::Object> obj);
+ static inline Local<Integer> IntegerToLocal(
+ v8::internal::Handle<v8::internal::Object> obj);
+ static inline Local<Uint32> Uint32ToLocal(
+ v8::internal::Handle<v8::internal::Object> obj);
+ static inline Local<FunctionTemplate> ToLocal(
+ v8::internal::Handle<v8::internal::FunctionTemplateInfo> obj);
+ static inline Local<ObjectTemplate> ToLocal(
+ v8::internal::Handle<v8::internal::ObjectTemplateInfo> obj);
+ static inline Local<Signature> ToLocal(
+ v8::internal::Handle<v8::internal::SignatureInfo> obj);
+ static inline Local<AccessorSignature> AccessorSignatureToLocal(
+ v8::internal::Handle<v8::internal::FunctionTemplateInfo> obj);
+ static inline Local<TypeSwitch> ToLocal(
+ v8::internal::Handle<v8::internal::TypeSwitchInfo> obj);
+ static inline Local<External> ExternalToLocal(
+ v8::internal::Handle<v8::internal::JSObject> obj);
+ static inline Local<DeclaredAccessorDescriptor> ToLocal(
+ v8::internal::Handle<v8::internal::DeclaredAccessorDescriptor> obj);
+
+#define DECLARE_OPEN_HANDLE(From, To) \
+ static inline v8::internal::Handle<v8::internal::To> \
+ OpenHandle(const From* that, bool allow_empty_handle = false);
+
+OPEN_HANDLE_LIST(DECLARE_OPEN_HANDLE)
+
+#undef DECLARE_OPEN_HANDLE
+
+ template<class From, class To>
+ static inline Local<To> Convert(v8::internal::Handle<From> obj) {
+ ASSERT(obj.is_null() || !obj->IsTheHole());
+ return Local<To>(reinterpret_cast<To*>(obj.location()));
+ }
+
+ template <class T>
+ static inline v8::internal::Handle<v8::internal::Object> OpenPersistent(
+ const v8::Persistent<T>& persistent) {
+ return v8::internal::Handle<v8::internal::Object>(
+ reinterpret_cast<v8::internal::Object**>(persistent.val_));
+ }
+
+ template <class T>
+ static inline v8::internal::Handle<v8::internal::Object> OpenPersistent(
+ v8::Persistent<T>* persistent) {
+ return OpenPersistent(*persistent);
+ }
+
+ template <class From, class To>
+ static inline v8::internal::Handle<To> OpenHandle(v8::Local<From> handle) {
+ return OpenHandle(*handle);
+ }
+};
+
+
+template <class T>
+v8::internal::Handle<T> v8::internal::Handle<T>::EscapeFrom(
+ v8::HandleScope* scope) {
+ v8::internal::Handle<T> handle;
+ if (!is_null()) {
+ handle = *this;
+ }
+ return Utils::OpenHandle(*scope->Close(Utils::ToLocal(handle)), true);
+}
+
+
+template <class T>
+inline T* ToApi(v8::internal::Handle<v8::internal::Object> obj) {
+ return reinterpret_cast<T*>(obj.location());
+}
+
+template <class T>
+inline v8::Local<T> ToApiHandle(
+ v8::internal::Handle<v8::internal::Object> obj) {
+ return Utils::Convert<v8::internal::Object, T>(obj);
+}
+
+
+// Implementations of ToLocal
+
+#define MAKE_TO_LOCAL(Name, From, To) \
+ Local<v8::To> Utils::Name(v8::internal::Handle<v8::internal::From> obj) { \
+ return Convert<v8::internal::From, v8::To>(obj); \
+ }
+
+
+#define MAKE_TO_LOCAL_TYPED_ARRAY(TypedArray, typeConst) \
+ Local<v8::TypedArray> Utils::ToLocal##TypedArray( \
+ v8::internal::Handle<v8::internal::JSTypedArray> obj) { \
+ ASSERT(obj->type() == typeConst); \
+ return Convert<v8::internal::JSTypedArray, v8::TypedArray>(obj); \
+ }
+
+
+MAKE_TO_LOCAL(ToLocal, Context, Context)
+MAKE_TO_LOCAL(ToLocal, Object, Value)
+MAKE_TO_LOCAL(ToLocal, JSFunction, Function)
+MAKE_TO_LOCAL(ToLocal, String, String)
+MAKE_TO_LOCAL(ToLocal, Symbol, Symbol)
+MAKE_TO_LOCAL(ToLocal, JSRegExp, RegExp)
+MAKE_TO_LOCAL(ToLocal, JSObject, Object)
+MAKE_TO_LOCAL(ToLocal, JSArray, Array)
+MAKE_TO_LOCAL(ToLocal, JSArrayBuffer, ArrayBuffer)
+MAKE_TO_LOCAL(ToLocal, JSArrayBufferView, ArrayBufferView)
+MAKE_TO_LOCAL(ToLocal, JSDataView, DataView)
+MAKE_TO_LOCAL(ToLocal, JSTypedArray, TypedArray)
+
+MAKE_TO_LOCAL_TYPED_ARRAY(Uint8Array, kExternalUnsignedByteArray)
+MAKE_TO_LOCAL_TYPED_ARRAY(Uint8ClampedArray, kExternalPixelArray)
+MAKE_TO_LOCAL_TYPED_ARRAY(Int8Array, kExternalByteArray)
+MAKE_TO_LOCAL_TYPED_ARRAY(Uint16Array, kExternalUnsignedShortArray)
+MAKE_TO_LOCAL_TYPED_ARRAY(Int16Array, kExternalShortArray)
+MAKE_TO_LOCAL_TYPED_ARRAY(Uint32Array, kExternalUnsignedIntArray)
+MAKE_TO_LOCAL_TYPED_ARRAY(Int32Array, kExternalIntArray)
+MAKE_TO_LOCAL_TYPED_ARRAY(Float32Array, kExternalFloatArray)
+MAKE_TO_LOCAL_TYPED_ARRAY(Float64Array, kExternalDoubleArray)
+
+MAKE_TO_LOCAL(ToLocal, FunctionTemplateInfo, FunctionTemplate)
+MAKE_TO_LOCAL(ToLocal, ObjectTemplateInfo, ObjectTemplate)
+MAKE_TO_LOCAL(ToLocal, SignatureInfo, Signature)
+MAKE_TO_LOCAL(AccessorSignatureToLocal, FunctionTemplateInfo, AccessorSignature)
+MAKE_TO_LOCAL(ToLocal, TypeSwitchInfo, TypeSwitch)
+MAKE_TO_LOCAL(MessageToLocal, Object, Message)
+MAKE_TO_LOCAL(StackTraceToLocal, JSArray, StackTrace)
+MAKE_TO_LOCAL(StackFrameToLocal, JSObject, StackFrame)
+MAKE_TO_LOCAL(NumberToLocal, Object, Number)
+MAKE_TO_LOCAL(IntegerToLocal, Object, Integer)
+MAKE_TO_LOCAL(Uint32ToLocal, Object, Uint32)
+MAKE_TO_LOCAL(ExternalToLocal, JSObject, External)
+MAKE_TO_LOCAL(ToLocal, DeclaredAccessorDescriptor, DeclaredAccessorDescriptor)
+
+#undef MAKE_TO_LOCAL_TYPED_ARRAY
+#undef MAKE_TO_LOCAL
+
+
+// Implementations of OpenHandle
+
+#define MAKE_OPEN_HANDLE(From, To) \
+ v8::internal::Handle<v8::internal::To> Utils::OpenHandle( \
+ const v8::From* that, bool allow_empty_handle) { \
+ EXTRA_CHECK(allow_empty_handle || that != NULL); \
+ EXTRA_CHECK(that == NULL || \
+ !(*reinterpret_cast<v8::internal::To**>( \
+ const_cast<v8::From*>(that)))->IsFailure()); \
+ return v8::internal::Handle<v8::internal::To>( \
+ reinterpret_cast<v8::internal::To**>(const_cast<v8::From*>(that))); \
+ }
+
+OPEN_HANDLE_LIST(MAKE_OPEN_HANDLE)
+
+#undef MAKE_OPEN_HANDLE
+#undef OPEN_HANDLE_LIST
+
+
+namespace internal {
+
+// Tracks string usage to help make better decisions when
+// externalizing strings.
+//
+// Implementation note: internally this class only tracks fresh
+// strings and keeps a single use counter for them.
+class StringTracker {
+ public:
+ // Records that the given string's characters were copied to some
+ // external buffer. If this happens often we should honor
+ // externalization requests for the string.
+ void RecordWrite(Handle<String> string) {
+ Address address = reinterpret_cast<Address>(*string);
+ Address top = isolate_->heap()->NewSpaceTop();
+ if (IsFreshString(address, top)) {
+ IncrementUseCount(top);
+ }
+ }
+
+ // Estimates freshness and use frequency of the given string based
+ // on how close it is to the new space top and the recorded usage
+ // history.
+ inline bool IsFreshUnusedString(Handle<String> string) {
+ Address address = reinterpret_cast<Address>(*string);
+ Address top = isolate_->heap()->NewSpaceTop();
+ return IsFreshString(address, top) && IsUseCountLow(top);
+ }
+
+ private:
+ StringTracker() : use_count_(0), last_top_(NULL), isolate_(NULL) { }
+
+ static inline bool IsFreshString(Address string, Address top) {
+ return top - kFreshnessLimit <= string && string <= top;
+ }
+
+ inline bool IsUseCountLow(Address top) {
+ if (last_top_ != top) return true;
+ return use_count_ < kUseLimit;
+ }
+
+ inline void IncrementUseCount(Address top) {
+ if (last_top_ != top) {
+ use_count_ = 0;
+ last_top_ = top;
+ }
+ ++use_count_;
+ }
+
+ // Single use counter shared by all fresh strings.
+ int use_count_;
+
+ // Last new space top when the use count above was valid.
+ Address last_top_;
+
+ Isolate* isolate_;
+
+ // How close to the new space top a fresh string has to be.
+ static const int kFreshnessLimit = 1024;
+
+ // The number of uses required to consider a string useful.
+ static const int kUseLimit = 32;
+
+ friend class Isolate;
+
+ DISALLOW_COPY_AND_ASSIGN(StringTracker);
+};
+
+
+class DeferredHandles {
+ public:
+ ~DeferredHandles();
+
+ private:
+ DeferredHandles(Object** first_block_limit, Isolate* isolate)
+ : next_(NULL),
+ previous_(NULL),
+ first_block_limit_(first_block_limit),
+ isolate_(isolate) {
+ isolate->LinkDeferredHandles(this);
+ }
+
+ void Iterate(ObjectVisitor* v);
+
+ List<Object**> blocks_;
+ DeferredHandles* next_;
+ DeferredHandles* previous_;
+ Object** first_block_limit_;
+ Isolate* isolate_;
+
+ friend class HandleScopeImplementer;
+ friend class Isolate;
+};
+
+
+// This class is here in order to be able to declare it a friend of
+// HandleScope. Moving these methods to be members of HandleScope would be
+// neat in some ways, but it would expose internal implementation details in
+// our public header file, which is undesirable.
+//
+// An isolate has a single instance of this class to hold the current thread's
+// data. In multithreaded V8 programs this data is copied in and out of storage
+// so that the currently executing thread always has its own copy of this
+// data.
+class HandleScopeImplementer {
+ public:
+ explicit HandleScopeImplementer(Isolate* isolate)
+ : isolate_(isolate),
+ blocks_(0),
+ entered_contexts_(0),
+ saved_contexts_(0),
+ spare_(NULL),
+ call_depth_(0),
+ last_handle_before_deferred_block_(NULL) { }
+
+ ~HandleScopeImplementer() {
+ DeleteArray(spare_);
+ }
+
+ // Threading support for handle data.
+ static int ArchiveSpacePerThread();
+ char* RestoreThread(char* from);
+ char* ArchiveThread(char* to);
+ void FreeThreadResources();
+
+ // Garbage collection support.
+ void Iterate(v8::internal::ObjectVisitor* v);
+ static char* Iterate(v8::internal::ObjectVisitor* v, char* data);
+
+
+ inline internal::Object** GetSpareOrNewBlock();
+ inline void DeleteExtensions(internal::Object** prev_limit);
+
+ inline void IncrementCallDepth() {call_depth_++;}
+ inline void DecrementCallDepth() {call_depth_--;}
+ inline bool CallDepthIsZero() { return call_depth_ == 0; }
+
+ inline void EnterContext(Handle<Object> context);
+ inline bool LeaveLastContext();
+
+ // Returns the last entered context or an empty handle if no
+ // contexts have been entered.
+ inline Handle<Object> LastEnteredContext();
+
+ inline void SaveContext(Context* context);
+ inline Context* RestoreContext();
+ inline bool HasSavedContexts();
+
+ inline List<internal::Object**>* blocks() { return &blocks_; }
+ Isolate* isolate() const { return isolate_; }
+
+ void ReturnBlock(Object** block) {
+ ASSERT(block != NULL);
+ if (spare_ != NULL) DeleteArray(spare_);
+ spare_ = block;
+ }
+
+ private:
+ void ResetAfterArchive() {
+ blocks_.Initialize(0);
+ entered_contexts_.Initialize(0);
+ saved_contexts_.Initialize(0);
+ spare_ = NULL;
+ last_handle_before_deferred_block_ = NULL;
+ call_depth_ = 0;
+ }
+
+ void Free() {
+ ASSERT(blocks_.length() == 0);
+ ASSERT(entered_contexts_.length() == 0);
+ ASSERT(saved_contexts_.length() == 0);
+ blocks_.Free();
+ entered_contexts_.Free();
+ saved_contexts_.Free();
+ if (spare_ != NULL) {
+ DeleteArray(spare_);
+ spare_ = NULL;
+ }
+ ASSERT(call_depth_ == 0);
+ }
+
+ void BeginDeferredScope();
+ DeferredHandles* Detach(Object** prev_limit);
+
+ Isolate* isolate_;
+ List<internal::Object**> blocks_;
+ // Used as a stack to keep track of entered contexts.
+ List<Handle<Object> > entered_contexts_;
+ // Used as a stack to keep track of saved contexts.
+ List<Context*> saved_contexts_;
+ Object** spare_;
+ int call_depth_;
+ Object** last_handle_before_deferred_block_;
+ // This is only used for threading support.
+ v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
+
+ void IterateThis(ObjectVisitor* v);
+ char* RestoreThreadHelper(char* from);
+ char* ArchiveThreadHelper(char* to);
+
+ friend class DeferredHandles;
+ friend class DeferredHandleScope;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleScopeImplementer);
+};
+
+
+const int kHandleBlockSize = v8::internal::KB - 2; // fit in one page
+
+
+void HandleScopeImplementer::SaveContext(Context* context) {
+ saved_contexts_.Add(context);
+}
+
+
+Context* HandleScopeImplementer::RestoreContext() {
+ return saved_contexts_.RemoveLast();
+}
+
+
+bool HandleScopeImplementer::HasSavedContexts() {
+ return !saved_contexts_.is_empty();
+}
+
+
+void HandleScopeImplementer::EnterContext(Handle<Object> context) {
+ entered_contexts_.Add(context);
+}
+
+
+bool HandleScopeImplementer::LeaveLastContext() {
+ if (entered_contexts_.is_empty()) return false;
+ entered_contexts_.RemoveLast();
+ return true;
+}
+
+
+Handle<Object> HandleScopeImplementer::LastEnteredContext() {
+ if (entered_contexts_.is_empty()) return Handle<Object>::null();
+ return entered_contexts_.last();
+}
+
+
+// If there's a spare block, use it for growing the current scope.
+internal::Object** HandleScopeImplementer::GetSpareOrNewBlock() {
+ internal::Object** block = (spare_ != NULL) ?
+ spare_ :
+ NewArray<internal::Object*>(kHandleBlockSize);
+ spare_ = NULL;
+ return block;
+}
+
+
+void HandleScopeImplementer::DeleteExtensions(internal::Object** prev_limit) {
+ while (!blocks_.is_empty()) {
+ internal::Object** block_start = blocks_.last();
+ internal::Object** block_limit = block_start + kHandleBlockSize;
+#ifdef DEBUG
+ // SealHandleScope may make the prev_limit to point inside the block.
+ if (block_start <= prev_limit && prev_limit <= block_limit) {
+#ifdef ENABLE_EXTRA_CHECKS
+ internal::HandleScope::ZapRange(prev_limit, block_limit);
+#endif
+ break;
+ }
+#else
+ if (prev_limit == block_limit) break;
+#endif
+
+ blocks_.RemoveLast();
+#ifdef ENABLE_EXTRA_CHECKS
+ internal::HandleScope::ZapRange(block_start, block_limit);
+#endif
+ if (spare_ != NULL) {
+ DeleteArray(spare_);
+ }
+ spare_ = block_start;
+ }
+ ASSERT((blocks_.is_empty() && prev_limit == NULL) ||
+ (!blocks_.is_empty() && prev_limit != NULL));
+}
+
+
+// Interceptor functions called from generated inline caches to notify
+// CPU profiler that external callbacks are invoked.
+v8::Handle<v8::Value> InvokeAccessorGetter(
+ v8::Local<v8::String> property,
+ const v8::AccessorInfo& info,
+ v8::AccessorGetter getter);
+
+
+void InvokeAccessorGetterCallback(
+ v8::Local<v8::String> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info,
+ v8::AccessorGetterCallback getter);
+
+v8::Handle<v8::Value> InvokeInvocationCallback(const v8::Arguments& args,
+ v8::InvocationCallback callback);
+void InvokeFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& info,
+ v8::FunctionCallback callback);
+
+class Testing {
+ public:
+ static v8::Testing::StressType stress_type() { return stress_type_; }
+ static void set_stress_type(v8::Testing::StressType stress_type) {
+ stress_type_ = stress_type;
+ }
+
+ private:
+ static v8::Testing::StressType stress_type_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_API_H_
diff --git a/chromium/v8/src/apinatives.js b/chromium/v8/src/apinatives.js
new file mode 100644
index 00000000000..ccbedd6d397
--- /dev/null
+++ b/chromium/v8/src/apinatives.js
@@ -0,0 +1,122 @@
+// 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.
+
+// This file contains infrastructure used by the API. See
+// v8natives.js for an explanation of these files are processed and
+// loaded.
+
+
+function CreateDate(time) {
+ var date = new $Date();
+ date.setTime(time);
+ return date;
+}
+
+
+var kApiFunctionCache = new InternalArray();
+var functionCache = kApiFunctionCache;
+
+
+function Instantiate(data, name) {
+ if (!%IsTemplate(data)) return data;
+ var tag = %GetTemplateField(data, kApiTagOffset);
+ switch (tag) {
+ case kFunctionTag:
+ return InstantiateFunction(data, name);
+ case kNewObjectTag:
+ var Constructor = %GetTemplateField(data, kApiConstructorOffset);
+ // Note: Do not directly use a function template as a condition, our
+ // internal ToBoolean doesn't handle that!
+ var result = typeof Constructor === 'undefined' ?
+ {} : new (Instantiate(Constructor))();
+ ConfigureTemplateInstance(result, data);
+ result = %ToFastProperties(result);
+ return result;
+ default:
+ throw 'Unknown API tag <' + tag + '>';
+ }
+}
+
+
+function InstantiateFunction(data, name) {
+ // We need a reference to kApiFunctionCache in the stack frame
+ // if we need to bail out from a stack overflow.
+ var cache = kApiFunctionCache;
+ var serialNumber = %GetTemplateField(data, kApiSerialNumberOffset);
+ var isFunctionCached =
+ (serialNumber in cache) && (cache[serialNumber] != kUninitialized);
+ if (!isFunctionCached) {
+ try {
+ cache[serialNumber] = null;
+ var fun = %CreateApiFunction(data);
+ if (name) %FunctionSetName(fun, name);
+ cache[serialNumber] = fun;
+ var prototype = %GetTemplateField(data, kApiPrototypeTemplateOffset);
+ var flags = %GetTemplateField(data, kApiFlagOffset);
+ // Note: Do not directly use an object template as a condition, our
+ // internal ToBoolean doesn't handle that!
+ fun.prototype = typeof prototype === 'undefined' ?
+ {} : Instantiate(prototype);
+ if (flags & (1 << kReadOnlyPrototypeBit)) {
+ %FunctionSetReadOnlyPrototype(fun);
+ }
+ %SetProperty(fun.prototype, "constructor", fun, DONT_ENUM);
+ var parent = %GetTemplateField(data, kApiParentTemplateOffset);
+ // Note: Do not directly use a function template as a condition, our
+ // internal ToBoolean doesn't handle that!
+ if (!(typeof parent === 'undefined')) {
+ var parent_fun = Instantiate(parent);
+ %SetPrototype(fun.prototype, parent_fun.prototype);
+ }
+ ConfigureTemplateInstance(fun, data);
+ } catch (e) {
+ cache[serialNumber] = kUninitialized;
+ throw e;
+ }
+ }
+ return cache[serialNumber];
+}
+
+
+function ConfigureTemplateInstance(obj, data) {
+ var properties = %GetTemplateField(data, kApiPropertyListOffset);
+ if (properties) {
+ // Disable access checks while instantiating the object.
+ var requires_access_checks = %DisableAccessChecks(obj);
+ try {
+ for (var i = 0; i < properties[0]; i += 3) {
+ var name = properties[i + 1];
+ var prop_data = properties[i + 2];
+ var attributes = properties[i + 3];
+ var value = Instantiate(prop_data, name);
+ %SetProperty(obj, name, value, attributes);
+ }
+ } finally {
+ if (requires_access_checks) %EnableAccessChecks(obj);
+ }
+ }
+}
diff --git a/chromium/v8/src/apiutils.h b/chromium/v8/src/apiutils.h
new file mode 100644
index 00000000000..07655856499
--- /dev/null
+++ b/chromium/v8/src/apiutils.h
@@ -0,0 +1,49 @@
+// Copyright 2012 the V8 project authors. 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_APIUTILS_H_
+#define V8_APIUTILS_H_
+
+namespace v8 {
+class ImplementationUtilities {
+ public:
+ static int GetNameCount(ExtensionConfiguration* that) {
+ return that->name_count_;
+ }
+
+ static const char** GetNames(ExtensionConfiguration* that) {
+ return that->names_;
+ }
+
+ // Introduce an alias for the handle scope data to allow non-friends
+ // to access the HandleScope data.
+ typedef v8::HandleScope::Data HandleScopeData;
+};
+
+} // namespace v8
+
+#endif // V8_APIUTILS_H_
diff --git a/chromium/v8/src/arguments.cc b/chromium/v8/src/arguments.cc
new file mode 100644
index 00000000000..11d9279e81e
--- /dev/null
+++ b/chromium/v8/src/arguments.cc
@@ -0,0 +1,207 @@
+// Copyright 2013 the V8 project authors. 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 "arguments.h"
+
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+static bool Match(void* a, void* b) {
+ return a == b;
+}
+
+
+static uint32_t Hash(void* function) {
+ uintptr_t as_int = reinterpret_cast<uintptr_t>(function);
+ if (sizeof(function) == 4) return static_cast<uint32_t>(as_int);
+ uint64_t as_64 = static_cast<uint64_t>(as_int);
+ return
+ static_cast<uint32_t>(as_64 >> 32) ^
+ static_cast<uint32_t>(as_64);
+}
+
+
+CallbackTable::CallbackTable(): map_(Match, 64) {}
+
+
+bool CallbackTable::Contains(void* function) {
+ ASSERT(function != NULL);
+ return map_.Lookup(function, Hash(function), false) != NULL;
+}
+
+
+void CallbackTable::InsertCallback(Isolate* isolate,
+ void* function,
+ bool returns_void) {
+ if (function == NULL) return;
+ // Don't store for performance.
+ if (kStoreVoidFunctions != returns_void) return;
+ CallbackTable* table = isolate->callback_table();
+ if (table == NULL) {
+ table = new CallbackTable();
+ isolate->set_callback_table(table);
+ }
+ typedef HashMap::Entry Entry;
+ Entry* entry = table->map_.Lookup(function, Hash(function), true);
+ ASSERT(entry != NULL);
+ ASSERT(entry->value == NULL || entry->value == function);
+ entry->value = function;
+}
+
+
+template<typename T>
+template<typename V>
+v8::Handle<V> CustomArguments<T>::GetReturnValue(Isolate* isolate) {
+ // Check the ReturnValue.
+ Object** handle = &this->end()[kReturnValueOffset];
+ // Nothing was set, return empty handle as per previous behaviour.
+ if ((*handle)->IsTheHole()) return v8::Handle<V>();
+ return Utils::Convert<Object, V>(Handle<Object>(handle));
+}
+
+
+v8::Handle<v8::Value> FunctionCallbackArguments::Call(InvocationCallback f) {
+ Isolate* isolate = this->isolate();
+ void* f_as_void = CallbackTable::FunctionToVoidPtr(f);
+ bool new_style = CallbackTable::ReturnsVoid(isolate, f_as_void);
+ VMState<EXTERNAL> state(isolate);
+ ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f));
+ if (new_style) {
+ FunctionCallback c = reinterpret_cast<FunctionCallback>(f);
+ FunctionCallbackInfo<v8::Value> info(end(),
+ argv_,
+ argc_,
+ is_construct_call_);
+ c(info);
+ } else {
+ v8::Arguments args(end(),
+ argv_,
+ argc_,
+ is_construct_call_);
+ v8::Handle<v8::Value> return_value = f(args);
+ if (!return_value.IsEmpty()) return return_value;
+ }
+ return GetReturnValue<v8::Value>(isolate);
+}
+
+
+#define WRITE_CALL_0(OldFunction, NewFunction, ReturnValue) \
+v8::Handle<ReturnValue> PropertyCallbackArguments::Call(OldFunction f) { \
+ Isolate* isolate = this->isolate(); \
+ void* f_as_void = CallbackTable::FunctionToVoidPtr(f); \
+ bool new_style = CallbackTable::ReturnsVoid(isolate, f_as_void); \
+ VMState<EXTERNAL> state(isolate); \
+ ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f)); \
+ if (new_style) { \
+ NewFunction c = reinterpret_cast<NewFunction>(f); \
+ PropertyCallbackInfo<ReturnValue> info(end()); \
+ c(info); \
+ } else { \
+ v8::AccessorInfo info(end()); \
+ v8::Handle<ReturnValue> return_value = f(info); \
+ if (!return_value.IsEmpty()) return return_value; \
+ } \
+ return GetReturnValue<ReturnValue>(isolate); \
+}
+
+#define WRITE_CALL_1(OldFunction, NewFunction, ReturnValue, Arg1) \
+v8::Handle<ReturnValue> PropertyCallbackArguments::Call(OldFunction f, \
+ Arg1 arg1) { \
+ Isolate* isolate = this->isolate(); \
+ void* f_as_void = CallbackTable::FunctionToVoidPtr(f); \
+ bool new_style = CallbackTable::ReturnsVoid(isolate, f_as_void); \
+ VMState<EXTERNAL> state(isolate); \
+ ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f)); \
+ if (new_style) { \
+ NewFunction c = reinterpret_cast<NewFunction>(f); \
+ PropertyCallbackInfo<ReturnValue> info(end()); \
+ c(arg1, info); \
+ } else { \
+ v8::AccessorInfo info(end()); \
+ v8::Handle<ReturnValue> return_value = f(arg1, info); \
+ if (!return_value.IsEmpty()) return return_value; \
+ } \
+ return GetReturnValue<ReturnValue>(isolate); \
+}
+
+#define WRITE_CALL_2(OldFunction, NewFunction, ReturnValue, Arg1, Arg2) \
+v8::Handle<ReturnValue> PropertyCallbackArguments::Call(OldFunction f, \
+ Arg1 arg1, \
+ Arg2 arg2) { \
+ Isolate* isolate = this->isolate(); \
+ void* f_as_void = CallbackTable::FunctionToVoidPtr(f); \
+ bool new_style = CallbackTable::ReturnsVoid(isolate, f_as_void); \
+ VMState<EXTERNAL> state(isolate); \
+ ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f)); \
+ if (new_style) { \
+ NewFunction c = reinterpret_cast<NewFunction>(f); \
+ PropertyCallbackInfo<ReturnValue> info(end()); \
+ c(arg1, arg2, info); \
+ } else { \
+ v8::AccessorInfo info(end()); \
+ v8::Handle<ReturnValue> return_value = f(arg1, arg2, info); \
+ if (!return_value.IsEmpty()) return return_value; \
+ } \
+ return GetReturnValue<ReturnValue>(isolate); \
+}
+
+#define WRITE_CALL_2_VOID(OldFunction, NewFunction, ReturnValue, Arg1, Arg2) \
+void PropertyCallbackArguments::Call(OldFunction f, \
+ Arg1 arg1, \
+ Arg2 arg2) { \
+ Isolate* isolate = this->isolate(); \
+ void* f_as_void = CallbackTable::FunctionToVoidPtr(f); \
+ bool new_style = CallbackTable::ReturnsVoid(isolate, f_as_void); \
+ VMState<EXTERNAL> state(isolate); \
+ ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f)); \
+ if (new_style) { \
+ NewFunction c = reinterpret_cast<NewFunction>(f); \
+ PropertyCallbackInfo<ReturnValue> info(end()); \
+ c(arg1, arg2, info); \
+ } else { \
+ v8::AccessorInfo info(end()); \
+ f(arg1, arg2, info); \
+ } \
+}
+
+FOR_EACH_CALLBACK_TABLE_MAPPING_0(WRITE_CALL_0)
+FOR_EACH_CALLBACK_TABLE_MAPPING_1(WRITE_CALL_1)
+FOR_EACH_CALLBACK_TABLE_MAPPING_2(WRITE_CALL_2)
+FOR_EACH_CALLBACK_TABLE_MAPPING_2_VOID_RETURN(WRITE_CALL_2_VOID)
+
+#undef WRITE_CALL_0
+#undef WRITE_CALL_1
+#undef WRITE_CALL_2
+#undef WRITE_CALL_2_VOID
+
+
+} } // namespace v8::internal
+
diff --git a/chromium/v8/src/arguments.h b/chromium/v8/src/arguments.h
new file mode 100644
index 00000000000..f9dca110c37
--- /dev/null
+++ b/chromium/v8/src/arguments.h
@@ -0,0 +1,364 @@
+// Copyright 2012 the V8 project authors. 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_ARGUMENTS_H_
+#define V8_ARGUMENTS_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+// Arguments provides access to runtime call parameters.
+//
+// It uses the fact that the instance fields of Arguments
+// (length_, arguments_) are "overlayed" with the parameters
+// (no. of parameters, and the parameter pointer) passed so
+// that inside the C++ function, the parameters passed can
+// be accessed conveniently:
+//
+// Object* Runtime_function(Arguments args) {
+// ... use args[i] here ...
+// }
+
+class Arguments BASE_EMBEDDED {
+ public:
+ Arguments(int length, Object** arguments)
+ : length_(length), arguments_(arguments) { }
+
+ Object*& operator[] (int index) {
+ ASSERT(0 <= index && index < length_);
+ return *(reinterpret_cast<Object**>(reinterpret_cast<intptr_t>(arguments_) -
+ index * kPointerSize));
+ }
+
+ template <class S> Handle<S> at(int index) {
+ Object** value = &((*this)[index]);
+ // This cast checks that the object we're accessing does indeed have the
+ // expected type.
+ S::cast(*value);
+ return Handle<S>(reinterpret_cast<S**>(value));
+ }
+
+ int smi_at(int index) {
+ return Smi::cast((*this)[index])->value();
+ }
+
+ double number_at(int index) {
+ return (*this)[index]->Number();
+ }
+
+ // Get the total number of arguments including the receiver.
+ int length() const { return length_; }
+
+ Object** arguments() { return arguments_; }
+
+ private:
+ int length_;
+ Object** arguments_;
+};
+
+
+// mappings from old property callbacks to new ones
+// F(old name, new name, return value, parameters...)
+//
+// These aren't included in the list as they have duplicate signatures
+// F(NamedPropertyEnumerator, NamedPropertyEnumeratorCallback, ...)
+// F(NamedPropertyGetter, NamedPropertyGetterCallback, ...)
+
+#define FOR_EACH_CALLBACK_TABLE_MAPPING_0(F) \
+ F(IndexedPropertyEnumerator, IndexedPropertyEnumeratorCallback, v8::Array) \
+
+#define FOR_EACH_CALLBACK_TABLE_MAPPING_1(F) \
+ F(AccessorGetter, AccessorGetterCallback, v8::Value, v8::Local<v8::String>) \
+ F(NamedPropertyQuery, \
+ NamedPropertyQueryCallback, \
+ v8::Integer, \
+ v8::Local<v8::String>) \
+ F(NamedPropertyDeleter, \
+ NamedPropertyDeleterCallback, \
+ v8::Boolean, \
+ v8::Local<v8::String>) \
+ F(IndexedPropertyGetter, \
+ IndexedPropertyGetterCallback, \
+ v8::Value, \
+ uint32_t) \
+ F(IndexedPropertyQuery, \
+ IndexedPropertyQueryCallback, \
+ v8::Integer, \
+ uint32_t) \
+ F(IndexedPropertyDeleter, \
+ IndexedPropertyDeleterCallback, \
+ v8::Boolean, \
+ uint32_t) \
+
+#define FOR_EACH_CALLBACK_TABLE_MAPPING_2(F) \
+ F(NamedPropertySetter, \
+ NamedPropertySetterCallback, \
+ v8::Value, \
+ v8::Local<v8::String>, \
+ v8::Local<v8::Value>) \
+ F(IndexedPropertySetter, \
+ IndexedPropertySetterCallback, \
+ v8::Value, \
+ uint32_t, \
+ v8::Local<v8::Value>) \
+
+#define FOR_EACH_CALLBACK_TABLE_MAPPING_2_VOID_RETURN(F) \
+ F(AccessorSetter, \
+ AccessorSetterCallback, \
+ void, \
+ v8::Local<v8::String>, \
+ v8::Local<v8::Value>) \
+
+// All property callbacks as well as invocation callbacks
+#define FOR_EACH_CALLBACK_TABLE_MAPPING(F) \
+ F(InvocationCallback, FunctionCallback) \
+ F(AccessorGetter, AccessorGetterCallback) \
+ F(AccessorSetter, AccessorSetterCallback) \
+ F(NamedPropertySetter, NamedPropertySetterCallback) \
+ F(NamedPropertyQuery, NamedPropertyQueryCallback) \
+ F(NamedPropertyDeleter, NamedPropertyDeleterCallback) \
+ F(IndexedPropertyGetter, IndexedPropertyGetterCallback) \
+ F(IndexedPropertySetter, IndexedPropertySetterCallback) \
+ F(IndexedPropertyQuery, IndexedPropertyQueryCallback) \
+ F(IndexedPropertyDeleter, IndexedPropertyDeleterCallback) \
+ F(IndexedPropertyEnumerator, IndexedPropertyEnumeratorCallback) \
+
+
+// TODO(dcarney): Remove this class when old callbacks are gone.
+class CallbackTable {
+ public:
+ static const bool kStoreVoidFunctions = false;
+ static inline bool ReturnsVoid(Isolate* isolate, void* function) {
+ CallbackTable* table = isolate->callback_table();
+ bool contains =
+ table != NULL &&
+ table->map_.occupancy() != 0 &&
+ table->Contains(function);
+ return contains == kStoreVoidFunctions;
+ }
+
+ STATIC_ASSERT(sizeof(intptr_t) == sizeof(AccessorGetterCallback));
+
+ template<typename F>
+ static inline void* FunctionToVoidPtr(F function) {
+ return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(function));
+ }
+
+#define WRITE_REGISTER(OldFunction, NewFunction) \
+ static NewFunction Register(Isolate* isolate, OldFunction f) { \
+ InsertCallback(isolate, FunctionToVoidPtr(f), false); \
+ return reinterpret_cast<NewFunction>(f); \
+ } \
+ \
+ static NewFunction Register(Isolate* isolate, NewFunction f) { \
+ InsertCallback(isolate, FunctionToVoidPtr(f), true); \
+ return f; \
+ }
+ FOR_EACH_CALLBACK_TABLE_MAPPING(WRITE_REGISTER)
+#undef WRITE_REGISTER
+
+ private:
+ CallbackTable();
+ bool Contains(void* function);
+ static void InsertCallback(Isolate* isolate,
+ void* function,
+ bool returns_void);
+ HashMap map_;
+ DISALLOW_COPY_AND_ASSIGN(CallbackTable);
+};
+
+
+// Custom arguments replicate a small segment of stack that can be
+// accessed through an Arguments object the same way the actual stack
+// can.
+template<int kArrayLength>
+class CustomArgumentsBase : public Relocatable {
+ public:
+ virtual inline void IterateInstance(ObjectVisitor* v) {
+ v->VisitPointers(values_, values_ + kArrayLength);
+ }
+ protected:
+ inline Object** end() { return values_ + kArrayLength - 1; }
+ explicit inline CustomArgumentsBase(Isolate* isolate)
+ : Relocatable(isolate) {}
+ Object* values_[kArrayLength];
+};
+
+
+template<typename T>
+class CustomArguments : public CustomArgumentsBase<T::kArgsLength> {
+ public:
+ static const int kReturnValueOffset = T::kReturnValueIndex;
+
+ typedef CustomArgumentsBase<T::kArgsLength> Super;
+ ~CustomArguments() {
+ // TODO(dcarney): create a new zap value for this.
+ this->end()[kReturnValueOffset] =
+ reinterpret_cast<Object*>(kHandleZapValue);
+ }
+
+ protected:
+ explicit inline CustomArguments(Isolate* isolate) : Super(isolate) {}
+
+ template<typename V>
+ v8::Handle<V> GetReturnValue(Isolate* isolate);
+
+ inline Isolate* isolate() {
+ return reinterpret_cast<Isolate*>(this->end()[T::kIsolateIndex]);
+ }
+};
+
+
+class PropertyCallbackArguments
+ : public CustomArguments<PropertyCallbackInfo<Value> > {
+ public:
+ typedef PropertyCallbackInfo<Value> T;
+ typedef CustomArguments<T> Super;
+ static const int kArgsLength = T::kArgsLength;
+ static const int kThisIndex = T::kThisIndex;
+ static const int kHolderIndex = T::kHolderIndex;
+
+ PropertyCallbackArguments(Isolate* isolate,
+ Object* data,
+ Object* self,
+ JSObject* holder)
+ : Super(isolate) {
+ Object** values = this->end();
+ values[T::kThisIndex] = self;
+ values[T::kHolderIndex] = holder;
+ values[T::kDataIndex] = data;
+ values[T::kIsolateIndex] = reinterpret_cast<Object*>(isolate);
+ // Here the hole is set as default value.
+ // It cannot escape into js as it's remove in Call below.
+ values[T::kReturnValueDefaultValueIndex] =
+ isolate->heap()->the_hole_value();
+ values[T::kReturnValueIndex] = isolate->heap()->the_hole_value();
+ ASSERT(values[T::kHolderIndex]->IsHeapObject());
+ ASSERT(values[T::kIsolateIndex]->IsSmi());
+ }
+
+ /*
+ * The following Call functions wrap the calling of all callbacks to handle
+ * calling either the old or the new style callbacks depending on which one
+ * has been registered.
+ * For old callbacks which return an empty handle, the ReturnValue is checked
+ * and used if it's been set to anything inside the callback.
+ * New style callbacks always use the return value.
+ */
+#define WRITE_CALL_0(OldFunction, NewFunction, ReturnValue) \
+ v8::Handle<ReturnValue> Call(OldFunction f); \
+
+#define WRITE_CALL_1(OldFunction, NewFunction, ReturnValue, Arg1) \
+ v8::Handle<ReturnValue> Call(OldFunction f, Arg1 arg1); \
+
+#define WRITE_CALL_2(OldFunction, NewFunction, ReturnValue, Arg1, Arg2) \
+ v8::Handle<ReturnValue> Call(OldFunction f, Arg1 arg1, Arg2 arg2); \
+
+#define WRITE_CALL_2_VOID(OldFunction, NewFunction, ReturnValue, Arg1, Arg2) \
+ void Call(OldFunction f, Arg1 arg1, Arg2 arg2); \
+
+FOR_EACH_CALLBACK_TABLE_MAPPING_0(WRITE_CALL_0)
+FOR_EACH_CALLBACK_TABLE_MAPPING_1(WRITE_CALL_1)
+FOR_EACH_CALLBACK_TABLE_MAPPING_2(WRITE_CALL_2)
+FOR_EACH_CALLBACK_TABLE_MAPPING_2_VOID_RETURN(WRITE_CALL_2_VOID)
+
+#undef WRITE_CALL_0
+#undef WRITE_CALL_1
+#undef WRITE_CALL_2
+#undef WRITE_CALL_2_VOID
+};
+
+
+class FunctionCallbackArguments
+ : public CustomArguments<FunctionCallbackInfo<Value> > {
+ public:
+ typedef FunctionCallbackInfo<Value> T;
+ typedef CustomArguments<T> Super;
+ static const int kArgsLength = T::kArgsLength;
+
+ FunctionCallbackArguments(internal::Isolate* isolate,
+ internal::Object* data,
+ internal::JSFunction* callee,
+ internal::Object* holder,
+ internal::Object** argv,
+ int argc,
+ bool is_construct_call)
+ : Super(isolate),
+ argv_(argv),
+ argc_(argc),
+ is_construct_call_(is_construct_call) {
+ Object** values = end();
+ values[T::kDataIndex] = data;
+ values[T::kCalleeIndex] = callee;
+ values[T::kHolderIndex] = holder;
+ values[T::kIsolateIndex] = reinterpret_cast<internal::Object*>(isolate);
+ // Here the hole is set as default value.
+ // It cannot escape into js as it's remove in Call below.
+ values[T::kReturnValueDefaultValueIndex] =
+ isolate->heap()->the_hole_value();
+ values[T::kReturnValueIndex] = isolate->heap()->the_hole_value();
+ ASSERT(values[T::kCalleeIndex]->IsJSFunction());
+ ASSERT(values[T::kHolderIndex]->IsHeapObject());
+ ASSERT(values[T::kIsolateIndex]->IsSmi());
+ }
+
+ /*
+ * The following Call function wraps the calling of all callbacks to handle
+ * calling either the old or the new style callbacks depending on which one
+ * has been registered.
+ * For old callbacks which return an empty handle, the ReturnValue is checked
+ * and used if it's been set to anything inside the callback.
+ * New style callbacks always use the return value.
+ */
+ v8::Handle<v8::Value> Call(InvocationCallback f);
+
+ private:
+ internal::Object** argv_;
+ int argc_;
+ bool is_construct_call_;
+};
+
+
+#define DECLARE_RUNTIME_FUNCTION(Type, Name) \
+Type Name(int args_length, Object** args_object, Isolate* isolate)
+
+#define RUNTIME_FUNCTION(Type, Name) \
+static Type __RT_impl_##Name(Arguments args, Isolate* isolate); \
+Type Name(int args_length, Object** args_object, Isolate* isolate) { \
+ Arguments args(args_length, args_object); \
+ return __RT_impl_##Name(args, isolate); \
+} \
+static Type __RT_impl_##Name(Arguments args, Isolate* isolate)
+
+#define RUNTIME_ARGUMENTS(isolate, args) \
+ args.length(), args.arguments(), isolate
+
+} } // namespace v8::internal
+
+#endif // V8_ARGUMENTS_H_
diff --git a/chromium/v8/src/arm/assembler-arm-inl.h b/chromium/v8/src/arm/assembler-arm-inl.h
new file mode 100644
index 00000000000..bfe9bc8335a
--- /dev/null
+++ b/chromium/v8/src/arm/assembler-arm-inl.h
@@ -0,0 +1,526 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been modified
+// significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+#ifndef V8_ARM_ASSEMBLER_ARM_INL_H_
+#define V8_ARM_ASSEMBLER_ARM_INL_H_
+
+#include "arm/assembler-arm.h"
+
+#include "cpu.h"
+#include "debug.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+int Register::NumAllocatableRegisters() {
+ return kMaxNumAllocatableRegisters;
+}
+
+
+int DwVfpRegister::NumRegisters() {
+ return CpuFeatures::IsSupported(VFP32DREGS) ? 32 : 16;
+}
+
+
+int DwVfpRegister::NumAllocatableRegisters() {
+ return NumRegisters() - kNumReservedRegisters;
+}
+
+
+int DwVfpRegister::ToAllocationIndex(DwVfpRegister reg) {
+ ASSERT(!reg.is(kDoubleRegZero));
+ ASSERT(!reg.is(kScratchDoubleReg));
+ if (reg.code() > kDoubleRegZero.code()) {
+ return reg.code() - kNumReservedRegisters;
+ }
+ return reg.code();
+}
+
+
+DwVfpRegister DwVfpRegister::FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < NumAllocatableRegisters());
+ ASSERT(kScratchDoubleReg.code() - kDoubleRegZero.code() ==
+ kNumReservedRegisters - 1);
+ if (index >= kDoubleRegZero.code()) {
+ return from_code(index + kNumReservedRegisters);
+ }
+ return from_code(index);
+}
+
+
+void RelocInfo::apply(intptr_t delta) {
+ if (RelocInfo::IsInternalReference(rmode_)) {
+ // absolute code pointer inside code object moves with the code object.
+ int32_t* p = reinterpret_cast<int32_t*>(pc_);
+ *p += delta; // relocate entry
+ }
+ // We do not use pc relative addressing on ARM, so there is
+ // nothing else to do.
+}
+
+
+Address RelocInfo::target_address() {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
+ return Assembler::target_address_at(pc_);
+}
+
+
+Address RelocInfo::target_address_address() {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
+ || rmode_ == EMBEDDED_OBJECT
+ || rmode_ == EXTERNAL_REFERENCE);
+ return reinterpret_cast<Address>(Assembler::target_pointer_address_at(pc_));
+}
+
+
+int RelocInfo::target_address_size() {
+ return kPointerSize;
+}
+
+
+void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
+ 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));
+ }
+}
+
+
+Object* RelocInfo::target_object() {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ return reinterpret_cast<Object*>(Assembler::target_pointer_at(pc_));
+}
+
+
+Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ return Handle<Object>(reinterpret_cast<Object**>(
+ Assembler::target_pointer_at(pc_)));
+}
+
+
+Object** RelocInfo::target_object_address() {
+ // Provide a "natural pointer" to the embedded object,
+ // which can be de-referenced during heap iteration.
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ reconstructed_obj_ptr_ =
+ reinterpret_cast<Object*>(Assembler::target_pointer_at(pc_));
+ return &reconstructed_obj_ptr_;
+}
+
+
+void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ ASSERT(!target->IsConsString());
+ Assembler::set_target_pointer_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));
+ }
+}
+
+
+Address* RelocInfo::target_reference_address() {
+ ASSERT(rmode_ == EXTERNAL_REFERENCE);
+ reconstructed_adr_ptr_ = Assembler::target_address_at(pc_);
+ return &reconstructed_adr_ptr_;
+}
+
+
+Address RelocInfo::target_runtime_entry(Assembler* origin) {
+ ASSERT(IsRuntimeEntry(rmode_));
+ return target_address();
+}
+
+
+void RelocInfo::set_target_runtime_entry(Address target,
+ WriteBarrierMode mode) {
+ ASSERT(IsRuntimeEntry(rmode_));
+ if (target_address() != target) set_target_address(target, mode);
+}
+
+
+Handle<Cell> RelocInfo::target_cell_handle() {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ Address address = Memory::Address_at(pc_);
+ return Handle<Cell>(reinterpret_cast<Cell**>(address));
+}
+
+
+Cell* RelocInfo::target_cell() {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ return Cell::FromValueAddress(Memory::Address_at(pc_));
+}
+
+
+void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ Address address = cell->address() + Cell::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);
+ }
+}
+
+
+static const int kNoCodeAgeSequenceLength = 3;
+
+Code* RelocInfo::code_age_stub() {
+ ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
+ return Code::GetCodeFromTargetAddress(
+ Memory::Address_at(pc_ + Assembler::kInstrSize *
+ (kNoCodeAgeSequenceLength - 1)));
+}
+
+
+void RelocInfo::set_code_age_stub(Code* stub) {
+ ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
+ Memory::Address_at(pc_ + Assembler::kInstrSize *
+ (kNoCodeAgeSequenceLength - 1)) =
+ stub->instruction_start();
+}
+
+
+Address RelocInfo::call_address() {
+ // The 2 instructions offset assumes patched debug break slot or return
+ // sequence.
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ return Memory::Address_at(pc_ + 2 * Assembler::kInstrSize);
+}
+
+
+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));
+ }
+}
+
+
+Object* RelocInfo::call_object() {
+ return *call_object_address();
+}
+
+
+void RelocInfo::set_call_object(Object* target) {
+ *call_object_address() = target;
+}
+
+
+Object** RelocInfo::call_object_address() {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
+}
+
+
+bool RelocInfo::IsPatchedReturnSequence() {
+ Instr current_instr = Assembler::instr_at(pc_);
+ Instr next_instr = Assembler::instr_at(pc_ + Assembler::kInstrSize);
+ // A patched return sequence is:
+ // ldr ip, [pc, #0]
+ // blx ip
+ return ((current_instr & kLdrPCMask) == kLdrPCPattern)
+ && ((next_instr & kBlxRegMask) == kBlxRegPattern);
+}
+
+
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+ Instr current_instr = Assembler::instr_at(pc_);
+ return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
+}
+
+
+void RelocInfo::Visit(ObjectVisitor* visitor) {
+ RelocInfo::Mode mode = rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT) {
+ visitor->VisitEmbeddedPointer(this);
+ } else if (RelocInfo::IsCodeTarget(mode)) {
+ visitor->VisitCodeTarget(this);
+ } else if (mode == RelocInfo::CELL) {
+ visitor->VisitCell(this);
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
+ visitor->VisitExternalReference(this);
+ } else if (RelocInfo::IsCodeAgeSequence(mode)) {
+ visitor->VisitCodeAgeSequence(this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // TODO(isolates): Get a cached isolate below.
+ } else if (((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence())) &&
+ Isolate::Current()->debug()->has_break_points()) {
+ visitor->VisitDebugTarget(this);
+#endif
+ } else if (RelocInfo::IsRuntimeEntry(mode)) {
+ visitor->VisitRuntimeEntry(this);
+ }
+}
+
+
+template<typename StaticVisitor>
+void RelocInfo::Visit(Heap* heap) {
+ RelocInfo::Mode mode = rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT) {
+ StaticVisitor::VisitEmbeddedPointer(heap, this);
+ } else if (RelocInfo::IsCodeTarget(mode)) {
+ StaticVisitor::VisitCodeTarget(heap, this);
+ } else if (mode == RelocInfo::CELL) {
+ StaticVisitor::VisitCell(heap, this);
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
+ StaticVisitor::VisitExternalReference(this);
+ } else if (RelocInfo::IsCodeAgeSequence(mode)) {
+ StaticVisitor::VisitCodeAgeSequence(heap, this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ } else if (heap->isolate()->debug()->has_break_points() &&
+ ((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()))) {
+ StaticVisitor::VisitDebugTarget(heap, this);
+#endif
+ } else if (RelocInfo::IsRuntimeEntry(mode)) {
+ StaticVisitor::VisitRuntimeEntry(this);
+ }
+}
+
+
+Operand::Operand(int32_t immediate, RelocInfo::Mode rmode) {
+ rm_ = no_reg;
+ imm32_ = immediate;
+ rmode_ = rmode;
+}
+
+
+Operand::Operand(const ExternalReference& f) {
+ rm_ = no_reg;
+ imm32_ = reinterpret_cast<int32_t>(f.address());
+ rmode_ = RelocInfo::EXTERNAL_REFERENCE;
+}
+
+
+Operand::Operand(Smi* value) {
+ rm_ = no_reg;
+ imm32_ = reinterpret_cast<intptr_t>(value);
+ rmode_ = RelocInfo::NONE32;
+}
+
+
+Operand::Operand(Register rm) {
+ rm_ = rm;
+ rs_ = no_reg;
+ shift_op_ = LSL;
+ shift_imm_ = 0;
+}
+
+
+bool Operand::is_reg() const {
+ return rm_.is_valid() &&
+ rs_.is(no_reg) &&
+ shift_op_ == LSL &&
+ shift_imm_ == 0;
+}
+
+
+void Assembler::CheckBuffer() {
+ if (buffer_space() <= kGap) {
+ GrowBuffer();
+ }
+ if (pc_offset() >= next_buffer_check_) {
+ CheckConstPool(false, true);
+ }
+}
+
+
+void Assembler::emit(Instr x) {
+ CheckBuffer();
+ *reinterpret_cast<Instr*>(pc_) = x;
+ pc_ += kInstrSize;
+}
+
+
+Address Assembler::target_pointer_address_at(Address pc) {
+ Address target_pc = pc;
+ Instr instr = Memory::int32_at(target_pc);
+ // If we have a bx instruction, the instruction before the bx is
+ // what we need to patch.
+ static const int32_t kBxInstMask = 0x0ffffff0;
+ static const int32_t kBxInstPattern = 0x012fff10;
+ if ((instr & kBxInstMask) == kBxInstPattern) {
+ target_pc -= kInstrSize;
+ instr = Memory::int32_at(target_pc);
+ }
+
+ // With a blx instruction, the instruction before is what needs to be patched.
+ if ((instr & kBlxRegMask) == kBlxRegPattern) {
+ target_pc -= kInstrSize;
+ instr = Memory::int32_at(target_pc);
+ }
+
+ ASSERT(IsLdrPcImmediateOffset(instr));
+ int offset = instr & 0xfff; // offset_12 is unsigned
+ if ((instr & (1 << 23)) == 0) offset = -offset; // U bit defines offset sign
+ // Verify that the constant pool comes after the instruction referencing it.
+ ASSERT(offset >= -4);
+ return target_pc + offset + 8;
+}
+
+
+Address Assembler::target_pointer_at(Address pc) {
+ if (IsMovW(Memory::int32_at(pc))) {
+ ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
+ Instruction* instr = Instruction::At(pc);
+ Instruction* next_instr = Instruction::At(pc + kInstrSize);
+ return reinterpret_cast<Address>(
+ (next_instr->ImmedMovwMovtValue() << 16) |
+ instr->ImmedMovwMovtValue());
+ }
+ return Memory::Address_at(target_pointer_address_at(pc));
+}
+
+
+Address Assembler::target_address_from_return_address(Address pc) {
+ // Returns the address of the call target from the return address that will
+ // be returned to after a call.
+ // Call sequence on V7 or later is :
+ // movw ip, #... @ call address low 16
+ // movt ip, #... @ call address high 16
+ // blx ip
+ // @ return address
+ // Or pre-V7 or cases that need frequent patching:
+ // ldr ip, [pc, #...] @ call address
+ // blx ip
+ // @ return address
+ Address candidate = pc - 2 * Assembler::kInstrSize;
+ Instr candidate_instr(Memory::int32_at(candidate));
+ if (IsLdrPcImmediateOffset(candidate_instr)) {
+ return candidate;
+ }
+ candidate = pc - 3 * Assembler::kInstrSize;
+ ASSERT(IsMovW(Memory::int32_at(candidate)) &&
+ IsMovT(Memory::int32_at(candidate + kInstrSize)));
+ return candidate;
+}
+
+
+Address Assembler::return_address_from_call_start(Address pc) {
+ if (IsLdrPcImmediateOffset(Memory::int32_at(pc))) {
+ return pc + kInstrSize * 2;
+ } else {
+ ASSERT(IsMovW(Memory::int32_at(pc)));
+ ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
+ return pc + kInstrSize * 3;
+ }
+}
+
+
+void Assembler::deserialization_set_special_target_at(
+ Address constant_pool_entry, Address target) {
+ Memory::Address_at(constant_pool_entry) = target;
+}
+
+
+void Assembler::set_external_target_at(Address constant_pool_entry,
+ Address target) {
+ Memory::Address_at(constant_pool_entry) = target;
+}
+
+
+static Instr EncodeMovwImmediate(uint32_t immediate) {
+ ASSERT(immediate < 0x10000);
+ return ((immediate & 0xf000) << 4) | (immediate & 0xfff);
+}
+
+
+void Assembler::set_target_pointer_at(Address pc, Address target) {
+ if (IsMovW(Memory::int32_at(pc))) {
+ ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
+ uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc);
+ uint32_t immediate = reinterpret_cast<uint32_t>(target);
+ uint32_t intermediate = instr_ptr[0];
+ intermediate &= ~EncodeMovwImmediate(0xFFFF);
+ intermediate |= EncodeMovwImmediate(immediate & 0xFFFF);
+ instr_ptr[0] = intermediate;
+ intermediate = instr_ptr[1];
+ intermediate &= ~EncodeMovwImmediate(0xFFFF);
+ intermediate |= EncodeMovwImmediate(immediate >> 16);
+ instr_ptr[1] = intermediate;
+ ASSERT(IsMovW(Memory::int32_at(pc)));
+ ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize)));
+ CPU::FlushICache(pc, 2 * kInstrSize);
+ } else {
+ ASSERT(IsLdrPcImmediateOffset(Memory::int32_at(pc)));
+ Memory::Address_at(target_pointer_address_at(pc)) = target;
+ // Intuitively, we would think it is necessary to always flush the
+ // instruction cache after patching a target address in the code as follows:
+ // CPU::FlushICache(pc, sizeof(target));
+ // However, on ARM, no instruction is actually patched in the case
+ // of embedded constants of the form:
+ // ldr ip, [pc, #...]
+ // since the instruction accessing this address in the constant pool remains
+ // unchanged.
+ }
+}
+
+
+Address Assembler::target_address_at(Address pc) {
+ return target_pointer_at(pc);
+}
+
+
+void Assembler::set_target_address_at(Address pc, Address target) {
+ set_target_pointer_at(pc, target);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_ASSEMBLER_ARM_INL_H_
diff --git a/chromium/v8/src/arm/assembler-arm.cc b/chromium/v8/src/arm/assembler-arm.cc
new file mode 100644
index 00000000000..a9db5a59949
--- /dev/null
+++ b/chromium/v8/src/arm/assembler-arm.cc
@@ -0,0 +1,3360 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "arm/assembler-arm-inl.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef DEBUG
+bool CpuFeatures::initialized_ = false;
+#endif
+unsigned CpuFeatures::supported_ = 0;
+unsigned CpuFeatures::found_by_runtime_probing_only_ = 0;
+unsigned CpuFeatures::cache_line_size_ = 64;
+
+
+ExternalReference ExternalReference::cpu_features() {
+ ASSERT(CpuFeatures::initialized_);
+ return ExternalReference(&CpuFeatures::supported_);
+}
+
+
+// Get the CPU features enabled by the build. For cross compilation the
+// preprocessor symbols CAN_USE_ARMV7_INSTRUCTIONS and CAN_USE_VFP3_INSTRUCTIONS
+// can be defined to enable ARMv7 and VFPv3 instructions when building the
+// snapshot.
+static unsigned CpuFeaturesImpliedByCompiler() {
+ unsigned answer = 0;
+#ifdef CAN_USE_ARMV7_INSTRUCTIONS
+ if (FLAG_enable_armv7) {
+ answer |= 1u << ARMv7;
+ }
+#endif // CAN_USE_ARMV7_INSTRUCTIONS
+#ifdef CAN_USE_VFP3_INSTRUCTIONS
+ if (FLAG_enable_vfp3) {
+ answer |= 1u << VFP3 | 1u << ARMv7;
+ }
+#endif // CAN_USE_VFP3_INSTRUCTIONS
+#ifdef CAN_USE_VFP32DREGS
+ if (FLAG_enable_32dregs) {
+ answer |= 1u << VFP32DREGS;
+ }
+#endif // CAN_USE_VFP32DREGS
+ if ((answer & (1u << ARMv7)) && FLAG_enable_unaligned_accesses) {
+ answer |= 1u << UNALIGNED_ACCESSES;
+ }
+
+ return answer;
+}
+
+
+const char* DwVfpRegister::AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < NumAllocatableRegisters());
+ ASSERT(kScratchDoubleReg.code() - kDoubleRegZero.code() ==
+ kNumReservedRegisters - 1);
+ if (index >= kDoubleRegZero.code())
+ index += kNumReservedRegisters;
+
+ return VFPRegisters::Name(index, true);
+}
+
+
+void CpuFeatures::Probe() {
+ uint64_t standard_features = static_cast<unsigned>(
+ OS::CpuFeaturesImpliedByPlatform()) | CpuFeaturesImpliedByCompiler();
+ ASSERT(supported_ == 0 || supported_ == standard_features);
+#ifdef DEBUG
+ initialized_ = true;
+#endif
+
+ // 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_ |= standard_features;
+
+ if (Serializer::enabled()) {
+ // No probing for features if we might serialize (generate snapshot).
+ printf(" ");
+ PrintFeatures();
+ return;
+ }
+
+#ifndef __arm__
+ // For the simulator=arm build, use VFP when FLAG_enable_vfp3 is
+ // enabled. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
+ if (FLAG_enable_vfp3) {
+ supported_ |=
+ static_cast<uint64_t>(1) << VFP3 |
+ static_cast<uint64_t>(1) << ARMv7;
+ }
+ if (FLAG_enable_neon) {
+ supported_ |= 1u << NEON;
+ }
+ // For the simulator=arm build, use ARMv7 when FLAG_enable_armv7 is enabled
+ if (FLAG_enable_armv7) {
+ supported_ |= static_cast<uint64_t>(1) << ARMv7;
+ }
+
+ if (FLAG_enable_sudiv) {
+ supported_ |= static_cast<uint64_t>(1) << SUDIV;
+ }
+
+ if (FLAG_enable_movw_movt) {
+ supported_ |= static_cast<uint64_t>(1) << MOVW_MOVT_IMMEDIATE_LOADS;
+ }
+
+ if (FLAG_enable_32dregs) {
+ supported_ |= static_cast<uint64_t>(1) << VFP32DREGS;
+ }
+
+ if (FLAG_enable_unaligned_accesses) {
+ supported_ |= static_cast<uint64_t>(1) << UNALIGNED_ACCESSES;
+ }
+
+#else // __arm__
+ // Probe for additional features not already known to be available.
+ if (!IsSupported(VFP3) && FLAG_enable_vfp3 && OS::ArmCpuHasFeature(VFP3)) {
+ // This implementation also sets the VFP flags if runtime
+ // detection of VFP returns true. VFPv3 implies ARMv7, see ARM DDI
+ // 0406B, page A1-6.
+ found_by_runtime_probing_only_ |=
+ static_cast<uint64_t>(1) << VFP3 |
+ static_cast<uint64_t>(1) << ARMv7;
+ }
+
+ if (!IsSupported(NEON) && FLAG_enable_neon && OS::ArmCpuHasFeature(NEON)) {
+ found_by_runtime_probing_only_ |= 1u << NEON;
+ }
+
+ if (!IsSupported(ARMv7) && FLAG_enable_armv7 && OS::ArmCpuHasFeature(ARMv7)) {
+ found_by_runtime_probing_only_ |= static_cast<uint64_t>(1) << ARMv7;
+ }
+
+ if (!IsSupported(SUDIV) && FLAG_enable_sudiv && OS::ArmCpuHasFeature(SUDIV)) {
+ found_by_runtime_probing_only_ |= static_cast<uint64_t>(1) << SUDIV;
+ }
+
+ if (!IsSupported(UNALIGNED_ACCESSES) && FLAG_enable_unaligned_accesses
+ && OS::ArmCpuHasFeature(ARMv7)) {
+ found_by_runtime_probing_only_ |=
+ static_cast<uint64_t>(1) << UNALIGNED_ACCESSES;
+ }
+
+ CpuImplementer implementer = OS::GetCpuImplementer();
+ if (implementer == QUALCOMM_IMPLEMENTER &&
+ FLAG_enable_movw_movt && OS::ArmCpuHasFeature(ARMv7)) {
+ found_by_runtime_probing_only_ |=
+ static_cast<uint64_t>(1) << MOVW_MOVT_IMMEDIATE_LOADS;
+ }
+
+ CpuPart part = OS::GetCpuPart(implementer);
+ if ((part == CORTEX_A9) || (part == CORTEX_A5)) {
+ cache_line_size_ = 32;
+ }
+
+ if (!IsSupported(VFP32DREGS) && FLAG_enable_32dregs
+ && OS::ArmCpuHasFeature(VFP32DREGS)) {
+ found_by_runtime_probing_only_ |= static_cast<uint64_t>(1) << VFP32DREGS;
+ }
+
+ supported_ |= found_by_runtime_probing_only_;
+#endif
+
+ // Assert that VFP3 implies ARMv7.
+ ASSERT(!IsSupported(VFP3) || IsSupported(ARMv7));
+}
+
+
+void CpuFeatures::PrintTarget() {
+ const char* arm_arch = NULL;
+ const char* arm_test = "";
+ const char* arm_fpu = "";
+ const char* arm_thumb = "";
+ const char* arm_float_abi = NULL;
+
+#if defined CAN_USE_ARMV7_INSTRUCTIONS
+ arm_arch = "arm v7";
+#else
+ arm_arch = "arm v6";
+#endif
+
+#ifdef __arm__
+
+# ifdef ARM_TEST
+ arm_test = " test";
+# endif
+# if defined __ARM_NEON__
+ arm_fpu = " neon";
+# elif defined CAN_USE_VFP3_INSTRUCTIONS
+ arm_fpu = " vfp3";
+# else
+ arm_fpu = " vfp2";
+# endif
+# if (defined __thumb__) || (defined __thumb2__)
+ arm_thumb = " thumb";
+# endif
+ arm_float_abi = OS::ArmUsingHardFloat() ? "hard" : "softfp";
+
+#else // __arm__
+
+ arm_test = " simulator";
+# if defined CAN_USE_VFP3_INSTRUCTIONS
+# if defined CAN_USE_VFP32DREGS
+ arm_fpu = " vfp3";
+# else
+ arm_fpu = " vfp3-d16";
+# endif
+# else
+ arm_fpu = " vfp2";
+# endif
+# if USE_EABI_HARDFLOAT == 1
+ arm_float_abi = "hard";
+# else
+ arm_float_abi = "softfp";
+# endif
+
+#endif // __arm__
+
+ printf("target%s %s%s%s %s\n",
+ arm_test, arm_arch, arm_fpu, arm_thumb, arm_float_abi);
+}
+
+
+void CpuFeatures::PrintFeatures() {
+ printf(
+ "ARMv7=%d VFP3=%d VFP32DREGS=%d NEON=%d SUDIV=%d UNALIGNED_ACCESSES=%d "
+ "MOVW_MOVT_IMMEDIATE_LOADS=%d",
+ CpuFeatures::IsSupported(ARMv7),
+ CpuFeatures::IsSupported(VFP3),
+ CpuFeatures::IsSupported(VFP32DREGS),
+ CpuFeatures::IsSupported(NEON),
+ CpuFeatures::IsSupported(SUDIV),
+ CpuFeatures::IsSupported(UNALIGNED_ACCESSES),
+ CpuFeatures::IsSupported(MOVW_MOVT_IMMEDIATE_LOADS));
+#ifdef __arm__
+ bool eabi_hardfloat = OS::ArmUsingHardFloat();
+#elif USE_EABI_HARDFLOAT
+ bool eabi_hardfloat = true;
+#else
+ bool eabi_hardfloat = false;
+#endif
+ printf(" USE_EABI_HARDFLOAT=%d\n", eabi_hardfloat);
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of RelocInfo
+
+const int RelocInfo::kApplyMask = 0;
+
+
+bool RelocInfo::IsCodedSpecially() {
+ // The deserializer needs to know whether a pointer is specially coded. Being
+ // specially coded on ARM means that it is a movw/movt instruction. We don't
+ // generate those yet.
+ return false;
+}
+
+
+void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
+ // Patch the code at the current address with the supplied instructions.
+ Instr* pc = reinterpret_cast<Instr*>(pc_);
+ Instr* instr = reinterpret_cast<Instr*>(instructions);
+ for (int i = 0; i < instruction_count; i++) {
+ *(pc + i) = *(instr + i);
+ }
+
+ // Indicate that code has changed.
+ CPU::FlushICache(pc_, instruction_count * Assembler::kInstrSize);
+}
+
+
+// Patch the code at the current PC with a call to the target address.
+// Additional guard instructions can be added if required.
+void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
+ // Patch the code at the current address with a call to the target.
+ UNIMPLEMENTED();
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Operand and MemOperand
+// See assembler-arm-inl.h for inlined constructors
+
+Operand::Operand(Handle<Object> handle) {
+#ifdef DEBUG
+ Isolate* isolate = Isolate::Current();
+#endif
+ AllowDeferredHandleDereference using_raw_address;
+ rm_ = no_reg;
+ // Verify all Objects referred by code are NOT in new space.
+ Object* obj = *handle;
+ ASSERT(!isolate->heap()->InNewSpace(obj));
+ if (obj->IsHeapObject()) {
+ imm32_ = reinterpret_cast<intptr_t>(handle.location());
+ rmode_ = RelocInfo::EMBEDDED_OBJECT;
+ } else {
+ // no relocation needed
+ imm32_ = reinterpret_cast<intptr_t>(obj);
+ rmode_ = RelocInfo::NONE32;
+ }
+}
+
+
+Operand::Operand(Register rm, ShiftOp shift_op, int shift_imm) {
+ ASSERT(is_uint5(shift_imm));
+ ASSERT(shift_op != ROR || shift_imm != 0); // use RRX if you mean it
+ rm_ = rm;
+ rs_ = no_reg;
+ shift_op_ = shift_op;
+ shift_imm_ = shift_imm & 31;
+ if (shift_op == RRX) {
+ // encoded as ROR with shift_imm == 0
+ ASSERT(shift_imm == 0);
+ shift_op_ = ROR;
+ shift_imm_ = 0;
+ }
+}
+
+
+Operand::Operand(Register rm, ShiftOp shift_op, Register rs) {
+ ASSERT(shift_op != RRX);
+ rm_ = rm;
+ rs_ = no_reg;
+ shift_op_ = shift_op;
+ rs_ = rs;
+}
+
+
+MemOperand::MemOperand(Register rn, int32_t offset, AddrMode am) {
+ rn_ = rn;
+ rm_ = no_reg;
+ offset_ = offset;
+ am_ = am;
+}
+
+
+MemOperand::MemOperand(Register rn, Register rm, AddrMode am) {
+ rn_ = rn;
+ rm_ = rm;
+ shift_op_ = LSL;
+ shift_imm_ = 0;
+ am_ = am;
+}
+
+
+MemOperand::MemOperand(Register rn, Register rm,
+ ShiftOp shift_op, int shift_imm, AddrMode am) {
+ ASSERT(is_uint5(shift_imm));
+ rn_ = rn;
+ rm_ = rm;
+ shift_op_ = shift_op;
+ shift_imm_ = shift_imm & 31;
+ am_ = am;
+}
+
+
+NeonMemOperand::NeonMemOperand(Register rn, AddrMode am, int align) {
+ ASSERT((am == Offset) || (am == PostIndex));
+ rn_ = rn;
+ rm_ = (am == Offset) ? pc : sp;
+ SetAlignment(align);
+}
+
+
+NeonMemOperand::NeonMemOperand(Register rn, Register rm, int align) {
+ rn_ = rn;
+ rm_ = rm;
+ SetAlignment(align);
+}
+
+
+void NeonMemOperand::SetAlignment(int align) {
+ switch (align) {
+ case 0:
+ align_ = 0;
+ break;
+ case 64:
+ align_ = 1;
+ break;
+ case 128:
+ align_ = 2;
+ break;
+ case 256:
+ align_ = 3;
+ break;
+ default:
+ UNREACHABLE();
+ align_ = 0;
+ break;
+ }
+}
+
+
+NeonListOperand::NeonListOperand(DoubleRegister base, int registers_count) {
+ base_ = base;
+ switch (registers_count) {
+ case 1:
+ type_ = nlt_1;
+ break;
+ case 2:
+ type_ = nlt_2;
+ break;
+ case 3:
+ type_ = nlt_3;
+ break;
+ case 4:
+ type_ = nlt_4;
+ break;
+ default:
+ UNREACHABLE();
+ type_ = nlt_1;
+ break;
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Specific instructions, constants, and masks.
+
+// add(sp, sp, 4) instruction (aka Pop())
+const Instr kPopInstruction =
+ al | PostIndex | 4 | LeaveCC | I | kRegister_sp_Code * B16 |
+ kRegister_sp_Code * B12;
+// str(r, MemOperand(sp, 4, NegPreIndex), al) instruction (aka push(r))
+// register r is not encoded.
+const Instr kPushRegPattern =
+ al | B26 | 4 | NegPreIndex | kRegister_sp_Code * B16;
+// ldr(r, MemOperand(sp, 4, PostIndex), al) instruction (aka pop(r))
+// register r is not encoded.
+const Instr kPopRegPattern =
+ al | B26 | L | 4 | PostIndex | kRegister_sp_Code * B16;
+// mov lr, pc
+const Instr kMovLrPc = al | MOV | kRegister_pc_Code | kRegister_lr_Code * B12;
+// ldr rd, [pc, #offset]
+const Instr kLdrPCMask = 15 * B24 | 7 * B20 | 15 * B16;
+const Instr kLdrPCPattern = 5 * B24 | L | kRegister_pc_Code * B16;
+// vldr dd, [pc, #offset]
+const Instr kVldrDPCMask = 15 * B24 | 3 * B20 | 15 * B16 | 15 * B8;
+const Instr kVldrDPCPattern = 13 * B24 | L | kRegister_pc_Code * B16 | 11 * B8;
+// blxcc rm
+const Instr kBlxRegMask =
+ 15 * B24 | 15 * B20 | 15 * B16 | 15 * B12 | 15 * B8 | 15 * B4;
+const Instr kBlxRegPattern =
+ B24 | B21 | 15 * B16 | 15 * B12 | 15 * B8 | BLX;
+const Instr kBlxIp = al | kBlxRegPattern | ip.code();
+const Instr kMovMvnMask = 0x6d * B21 | 0xf * B16;
+const Instr kMovMvnPattern = 0xd * B21;
+const Instr kMovMvnFlip = B22;
+const Instr kMovLeaveCCMask = 0xdff * B16;
+const Instr kMovLeaveCCPattern = 0x1a0 * B16;
+const Instr kMovwMask = 0xff * B20;
+const Instr kMovwPattern = 0x30 * B20;
+const Instr kMovwLeaveCCFlip = 0x5 * B21;
+const Instr kCmpCmnMask = 0xdd * B20 | 0xf * B12;
+const Instr kCmpCmnPattern = 0x15 * B20;
+const Instr kCmpCmnFlip = B21;
+const Instr kAddSubFlip = 0x6 * B21;
+const Instr kAndBicFlip = 0xe * B21;
+
+// A mask for the Rd register for push, pop, ldr, str instructions.
+const Instr kLdrRegFpOffsetPattern =
+ al | B26 | L | Offset | kRegister_fp_Code * B16;
+const Instr kStrRegFpOffsetPattern =
+ al | B26 | Offset | kRegister_fp_Code * B16;
+const Instr kLdrRegFpNegOffsetPattern =
+ al | B26 | L | NegOffset | kRegister_fp_Code * B16;
+const Instr kStrRegFpNegOffsetPattern =
+ al | B26 | NegOffset | kRegister_fp_Code * B16;
+const Instr kLdrStrInstrTypeMask = 0xffff0000;
+const Instr kLdrStrInstrArgumentMask = 0x0000ffff;
+const Instr kLdrStrOffsetMask = 0x00000fff;
+
+
+Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
+ : AssemblerBase(isolate, buffer, buffer_size),
+ recorded_ast_id_(TypeFeedbackId::None()),
+ positions_recorder_(this) {
+ reloc_info_writer.Reposition(buffer_ + buffer_size_, pc_);
+ num_pending_reloc_info_ = 0;
+ num_pending_64_bit_reloc_info_ = 0;
+ next_buffer_check_ = 0;
+ const_pool_blocked_nesting_ = 0;
+ no_const_pool_before_ = 0;
+ first_const_pool_use_ = -1;
+ last_bound_pos_ = 0;
+ ClearRecordedAstId();
+}
+
+
+Assembler::~Assembler() {
+ ASSERT(const_pool_blocked_nesting_ == 0);
+}
+
+
+void Assembler::GetCode(CodeDesc* desc) {
+ // Emit constant pool if necessary.
+ CheckConstPool(true, false);
+ ASSERT(num_pending_reloc_info_ == 0);
+ ASSERT(num_pending_64_bit_reloc_info_ == 0);
+
+ // Set up code descriptor.
+ desc->buffer = buffer_;
+ desc->buffer_size = buffer_size_;
+ desc->instr_size = pc_offset();
+ desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
+}
+
+
+void Assembler::Align(int m) {
+ ASSERT(m >= 4 && IsPowerOf2(m));
+ while ((pc_offset() & (m - 1)) != 0) {
+ nop();
+ }
+}
+
+
+void Assembler::CodeTargetAlign() {
+ // Preferred alignment of jump targets on some ARM chips.
+ Align(8);
+}
+
+
+Condition Assembler::GetCondition(Instr instr) {
+ return Instruction::ConditionField(instr);
+}
+
+
+bool Assembler::IsBranch(Instr instr) {
+ return (instr & (B27 | B25)) == (B27 | B25);
+}
+
+
+int Assembler::GetBranchOffset(Instr instr) {
+ ASSERT(IsBranch(instr));
+ // Take the jump offset in the lower 24 bits, sign extend it and multiply it
+ // with 4 to get the offset in bytes.
+ return ((instr & kImm24Mask) << 8) >> 6;
+}
+
+
+bool Assembler::IsLdrRegisterImmediate(Instr instr) {
+ return (instr & (B27 | B26 | B25 | B22 | B20)) == (B26 | B20);
+}
+
+
+bool Assembler::IsVldrDRegisterImmediate(Instr instr) {
+ return (instr & (15 * B24 | 3 * B20 | 15 * B8)) == (13 * B24 | B20 | 11 * B8);
+}
+
+
+int Assembler::GetLdrRegisterImmediateOffset(Instr instr) {
+ ASSERT(IsLdrRegisterImmediate(instr));
+ bool positive = (instr & B23) == B23;
+ int offset = instr & kOff12Mask; // Zero extended offset.
+ return positive ? offset : -offset;
+}
+
+
+int Assembler::GetVldrDRegisterImmediateOffset(Instr instr) {
+ ASSERT(IsVldrDRegisterImmediate(instr));
+ bool positive = (instr & B23) == B23;
+ int offset = instr & kOff8Mask; // Zero extended offset.
+ offset <<= 2;
+ return positive ? offset : -offset;
+}
+
+
+Instr Assembler::SetLdrRegisterImmediateOffset(Instr instr, int offset) {
+ ASSERT(IsLdrRegisterImmediate(instr));
+ bool positive = offset >= 0;
+ if (!positive) offset = -offset;
+ ASSERT(is_uint12(offset));
+ // Set bit indicating whether the offset should be added.
+ instr = (instr & ~B23) | (positive ? B23 : 0);
+ // Set the actual offset.
+ return (instr & ~kOff12Mask) | offset;
+}
+
+
+Instr Assembler::SetVldrDRegisterImmediateOffset(Instr instr, int offset) {
+ ASSERT(IsVldrDRegisterImmediate(instr));
+ ASSERT((offset & ~3) == offset); // Must be 64-bit aligned.
+ bool positive = offset >= 0;
+ if (!positive) offset = -offset;
+ ASSERT(is_uint10(offset));
+ // Set bit indicating whether the offset should be added.
+ instr = (instr & ~B23) | (positive ? B23 : 0);
+ // Set the actual offset. Its bottom 2 bits are zero.
+ return (instr & ~kOff8Mask) | (offset >> 2);
+}
+
+
+bool Assembler::IsStrRegisterImmediate(Instr instr) {
+ return (instr & (B27 | B26 | B25 | B22 | B20)) == B26;
+}
+
+
+Instr Assembler::SetStrRegisterImmediateOffset(Instr instr, int offset) {
+ ASSERT(IsStrRegisterImmediate(instr));
+ bool positive = offset >= 0;
+ if (!positive) offset = -offset;
+ ASSERT(is_uint12(offset));
+ // Set bit indicating whether the offset should be added.
+ instr = (instr & ~B23) | (positive ? B23 : 0);
+ // Set the actual offset.
+ return (instr & ~kOff12Mask) | offset;
+}
+
+
+bool Assembler::IsAddRegisterImmediate(Instr instr) {
+ return (instr & (B27 | B26 | B25 | B24 | B23 | B22 | B21)) == (B25 | B23);
+}
+
+
+Instr Assembler::SetAddRegisterImmediateOffset(Instr instr, int offset) {
+ ASSERT(IsAddRegisterImmediate(instr));
+ ASSERT(offset >= 0);
+ ASSERT(is_uint12(offset));
+ // Set the offset.
+ return (instr & ~kOff12Mask) | offset;
+}
+
+
+Register Assembler::GetRd(Instr instr) {
+ Register reg;
+ reg.code_ = Instruction::RdValue(instr);
+ return reg;
+}
+
+
+Register Assembler::GetRn(Instr instr) {
+ Register reg;
+ reg.code_ = Instruction::RnValue(instr);
+ return reg;
+}
+
+
+Register Assembler::GetRm(Instr instr) {
+ Register reg;
+ reg.code_ = Instruction::RmValue(instr);
+ return reg;
+}
+
+
+bool Assembler::IsPush(Instr instr) {
+ return ((instr & ~kRdMask) == kPushRegPattern);
+}
+
+
+bool Assembler::IsPop(Instr instr) {
+ return ((instr & ~kRdMask) == kPopRegPattern);
+}
+
+
+bool Assembler::IsStrRegFpOffset(Instr instr) {
+ return ((instr & kLdrStrInstrTypeMask) == kStrRegFpOffsetPattern);
+}
+
+
+bool Assembler::IsLdrRegFpOffset(Instr instr) {
+ return ((instr & kLdrStrInstrTypeMask) == kLdrRegFpOffsetPattern);
+}
+
+
+bool Assembler::IsStrRegFpNegOffset(Instr instr) {
+ return ((instr & kLdrStrInstrTypeMask) == kStrRegFpNegOffsetPattern);
+}
+
+
+bool Assembler::IsLdrRegFpNegOffset(Instr instr) {
+ return ((instr & kLdrStrInstrTypeMask) == kLdrRegFpNegOffsetPattern);
+}
+
+
+bool Assembler::IsLdrPcImmediateOffset(Instr instr) {
+ // Check the instruction is indeed a
+ // ldr<cond> <Rd>, [pc +/- offset_12].
+ return (instr & kLdrPCMask) == kLdrPCPattern;
+}
+
+
+bool Assembler::IsVldrDPcImmediateOffset(Instr instr) {
+ // Check the instruction is indeed a
+ // vldr<cond> <Dd>, [pc +/- offset_10].
+ return (instr & kVldrDPCMask) == kVldrDPCPattern;
+}
+
+
+bool Assembler::IsTstImmediate(Instr instr) {
+ return (instr & (B27 | B26 | I | kOpCodeMask | S | kRdMask)) ==
+ (I | TST | S);
+}
+
+
+bool Assembler::IsCmpRegister(Instr instr) {
+ return (instr & (B27 | B26 | I | kOpCodeMask | S | kRdMask | B4)) ==
+ (CMP | S);
+}
+
+
+bool Assembler::IsCmpImmediate(Instr instr) {
+ return (instr & (B27 | B26 | I | kOpCodeMask | S | kRdMask)) ==
+ (I | CMP | S);
+}
+
+
+Register Assembler::GetCmpImmediateRegister(Instr instr) {
+ ASSERT(IsCmpImmediate(instr));
+ return GetRn(instr);
+}
+
+
+int Assembler::GetCmpImmediateRawImmediate(Instr instr) {
+ ASSERT(IsCmpImmediate(instr));
+ return instr & kOff12Mask;
+}
+
+
+// Labels refer to positions in the (to be) generated code.
+// There are bound, linked, and unused labels.
+//
+// Bound labels refer to known positions in the already
+// generated code. pos() is the position the label refers to.
+//
+// Linked labels refer to unknown positions in the code
+// to be generated; pos() is the position of the last
+// instruction using the label.
+//
+// The linked labels form a link chain by making the branch offset
+// in the instruction steam to point to the previous branch
+// instruction using the same label.
+//
+// The link chain is terminated by a branch offset pointing to the
+// same position.
+
+
+int Assembler::target_at(int pos) {
+ Instr instr = instr_at(pos);
+ if ((instr & ~kImm24Mask) == 0) {
+ // Emitted label constant, not part of a branch.
+ return instr - (Code::kHeaderSize - kHeapObjectTag);
+ }
+ ASSERT((instr & 7*B25) == 5*B25); // b, bl, or blx imm24
+ int imm26 = ((instr & kImm24Mask) << 8) >> 6;
+ if ((Instruction::ConditionField(instr) == kSpecialCondition) &&
+ ((instr & B24) != 0)) {
+ // blx uses bit 24 to encode bit 2 of imm26
+ imm26 += 2;
+ }
+ return pos + kPcLoadDelta + imm26;
+}
+
+
+void Assembler::target_at_put(int pos, int target_pos) {
+ Instr instr = instr_at(pos);
+ if ((instr & ~kImm24Mask) == 0) {
+ ASSERT(target_pos == pos || target_pos >= 0);
+ // Emitted label constant, not part of a branch.
+ // Make label relative to Code* of generated Code object.
+ instr_at_put(pos, target_pos + (Code::kHeaderSize - kHeapObjectTag));
+ return;
+ }
+ int imm26 = target_pos - (pos + kPcLoadDelta);
+ ASSERT((instr & 7*B25) == 5*B25); // b, bl, or blx imm24
+ if (Instruction::ConditionField(instr) == kSpecialCondition) {
+ // blx uses bit 24 to encode bit 2 of imm26
+ ASSERT((imm26 & 1) == 0);
+ instr = (instr & ~(B24 | kImm24Mask)) | ((imm26 & 2) >> 1)*B24;
+ } else {
+ ASSERT((imm26 & 3) == 0);
+ instr &= ~kImm24Mask;
+ }
+ int imm24 = imm26 >> 2;
+ ASSERT(is_int24(imm24));
+ instr_at_put(pos, instr | (imm24 & kImm24Mask));
+}
+
+
+void Assembler::print(Label* L) {
+ if (L->is_unused()) {
+ PrintF("unused label\n");
+ } else if (L->is_bound()) {
+ PrintF("bound label to %d\n", L->pos());
+ } else if (L->is_linked()) {
+ Label l = *L;
+ PrintF("unbound label");
+ while (l.is_linked()) {
+ PrintF("@ %d ", l.pos());
+ Instr instr = instr_at(l.pos());
+ if ((instr & ~kImm24Mask) == 0) {
+ PrintF("value\n");
+ } else {
+ ASSERT((instr & 7*B25) == 5*B25); // b, bl, or blx
+ Condition cond = Instruction::ConditionField(instr);
+ const char* b;
+ const char* c;
+ if (cond == kSpecialCondition) {
+ b = "blx";
+ c = "";
+ } else {
+ if ((instr & B24) != 0)
+ b = "bl";
+ else
+ b = "b";
+
+ switch (cond) {
+ case eq: c = "eq"; break;
+ case ne: c = "ne"; break;
+ case hs: c = "hs"; break;
+ case lo: c = "lo"; break;
+ case mi: c = "mi"; break;
+ case pl: c = "pl"; break;
+ case vs: c = "vs"; break;
+ case vc: c = "vc"; break;
+ case hi: c = "hi"; break;
+ case ls: c = "ls"; break;
+ case ge: c = "ge"; break;
+ case lt: c = "lt"; break;
+ case gt: c = "gt"; break;
+ case le: c = "le"; break;
+ case al: c = ""; break;
+ default:
+ c = "";
+ UNREACHABLE();
+ }
+ }
+ PrintF("%s%s\n", b, c);
+ }
+ next(&l);
+ }
+ } else {
+ PrintF("label in inconsistent state (pos = %d)\n", L->pos_);
+ }
+}
+
+
+void Assembler::bind_to(Label* L, int pos) {
+ ASSERT(0 <= pos && pos <= pc_offset()); // must have a valid binding position
+ while (L->is_linked()) {
+ int fixup_pos = L->pos();
+ next(L); // call next before overwriting link with target at fixup_pos
+ target_at_put(fixup_pos, pos);
+ }
+ L->bind_to(pos);
+
+ // Keep track of the last bound label so we don't eliminate any instructions
+ // before a bound label.
+ if (pos > last_bound_pos_)
+ last_bound_pos_ = pos;
+}
+
+
+void Assembler::bind(Label* L) {
+ ASSERT(!L->is_bound()); // label can only be bound once
+ bind_to(L, pc_offset());
+}
+
+
+void Assembler::next(Label* L) {
+ ASSERT(L->is_linked());
+ int link = target_at(L->pos());
+ if (link == L->pos()) {
+ // Branch target points to the same instuction. This is the end of the link
+ // chain.
+ L->Unuse();
+ } else {
+ ASSERT(link >= 0);
+ L->link_to(link);
+ }
+}
+
+
+// Low-level code emission routines depending on the addressing mode.
+// If this returns true then you have to use the rotate_imm and immed_8
+// that it returns, because it may have already changed the instruction
+// to match them!
+static bool fits_shifter(uint32_t imm32,
+ uint32_t* rotate_imm,
+ uint32_t* immed_8,
+ Instr* instr) {
+ // imm32 must be unsigned.
+ for (int rot = 0; rot < 16; rot++) {
+ uint32_t imm8 = (imm32 << 2*rot) | (imm32 >> (32 - 2*rot));
+ if ((imm8 <= 0xff)) {
+ *rotate_imm = rot;
+ *immed_8 = imm8;
+ return true;
+ }
+ }
+ // If the opcode is one with a complementary version and the complementary
+ // immediate fits, change the opcode.
+ if (instr != NULL) {
+ if ((*instr & kMovMvnMask) == kMovMvnPattern) {
+ if (fits_shifter(~imm32, rotate_imm, immed_8, NULL)) {
+ *instr ^= kMovMvnFlip;
+ return true;
+ } else if ((*instr & kMovLeaveCCMask) == kMovLeaveCCPattern) {
+ if (CpuFeatures::IsSupported(ARMv7)) {
+ if (imm32 < 0x10000) {
+ *instr ^= kMovwLeaveCCFlip;
+ *instr |= EncodeMovwImmediate(imm32);
+ *rotate_imm = *immed_8 = 0; // Not used for movw.
+ return true;
+ }
+ }
+ }
+ } else if ((*instr & kCmpCmnMask) == kCmpCmnPattern) {
+ if (fits_shifter(-static_cast<int>(imm32), rotate_imm, immed_8, NULL)) {
+ *instr ^= kCmpCmnFlip;
+ return true;
+ }
+ } else {
+ Instr alu_insn = (*instr & kALUMask);
+ if (alu_insn == ADD ||
+ alu_insn == SUB) {
+ if (fits_shifter(-static_cast<int>(imm32), rotate_imm, immed_8, NULL)) {
+ *instr ^= kAddSubFlip;
+ return true;
+ }
+ } else if (alu_insn == AND ||
+ alu_insn == BIC) {
+ if (fits_shifter(~imm32, rotate_imm, immed_8, NULL)) {
+ *instr ^= kAndBicFlip;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+// We have to use the temporary register for things that can be relocated even
+// if they can be encoded in the ARM's 12 bits of immediate-offset instruction
+// space. There is no guarantee that the relocated location can be similarly
+// encoded.
+bool Operand::must_output_reloc_info(const Assembler* assembler) const {
+ if (rmode_ == RelocInfo::EXTERNAL_REFERENCE) {
+#ifdef DEBUG
+ if (!Serializer::enabled()) {
+ Serializer::TooLateToEnableNow();
+ }
+#endif // def DEBUG
+ if (assembler != NULL && assembler->predictable_code_size()) return true;
+ return Serializer::enabled();
+ } else if (RelocInfo::IsNone(rmode_)) {
+ return false;
+ }
+ return true;
+}
+
+
+static bool use_movw_movt(const Operand& x, const Assembler* assembler) {
+ if (Assembler::use_immediate_embedded_pointer_loads(assembler)) {
+ return true;
+ }
+ if (x.must_output_reloc_info(assembler)) {
+ return false;
+ }
+ return CpuFeatures::IsSupported(ARMv7);
+}
+
+
+bool Operand::is_single_instruction(const Assembler* assembler,
+ Instr instr) const {
+ if (rm_.is_valid()) return true;
+ uint32_t dummy1, dummy2;
+ if (must_output_reloc_info(assembler) ||
+ !fits_shifter(imm32_, &dummy1, &dummy2, &instr)) {
+ // The immediate operand cannot be encoded as a shifter operand, or use of
+ // constant pool is required. For a mov instruction not setting the
+ // condition code additional instruction conventions can be used.
+ if ((instr & ~kCondMask) == 13*B21) { // mov, S not set
+ return !use_movw_movt(*this, assembler);
+ } else {
+ // If this is not a mov or mvn instruction there will always an additional
+ // instructions - either mov or ldr. The mov might actually be two
+ // instructions mov or movw followed by movt so including the actual
+ // instruction two or three instructions will be generated.
+ return false;
+ }
+ } else {
+ // No use of constant pool and the immediate operand can be encoded as a
+ // shifter operand.
+ return true;
+ }
+}
+
+
+void Assembler::move_32_bit_immediate(Condition cond,
+ Register rd,
+ SBit s,
+ const Operand& x) {
+ if (rd.code() != pc.code() && s == LeaveCC) {
+ if (use_movw_movt(x, this)) {
+ if (x.must_output_reloc_info(this)) {
+ RecordRelocInfo(x.rmode_, x.imm32_, DONT_USE_CONSTANT_POOL);
+ // Make sure the movw/movt doesn't get separated.
+ BlockConstPoolFor(2);
+ }
+ emit(cond | 0x30*B20 | rd.code()*B12 |
+ EncodeMovwImmediate(x.imm32_ & 0xffff));
+ movt(rd, static_cast<uint32_t>(x.imm32_) >> 16, cond);
+ return;
+ }
+ }
+
+ RecordRelocInfo(x.rmode_, x.imm32_, USE_CONSTANT_POOL);
+ ldr(rd, MemOperand(pc, 0), cond);
+}
+
+
+void Assembler::addrmod1(Instr instr,
+ Register rn,
+ Register rd,
+ const Operand& x) {
+ CheckBuffer();
+ ASSERT((instr & ~(kCondMask | kOpCodeMask | S)) == 0);
+ if (!x.rm_.is_valid()) {
+ // Immediate.
+ uint32_t rotate_imm;
+ uint32_t immed_8;
+ if (x.must_output_reloc_info(this) ||
+ !fits_shifter(x.imm32_, &rotate_imm, &immed_8, &instr)) {
+ // The immediate operand cannot be encoded as a shifter operand, so load
+ // it first to register ip and change the original instruction to use ip.
+ // However, if the original instruction is a 'mov rd, x' (not setting the
+ // condition code), then replace it with a 'ldr rd, [pc]'.
+ CHECK(!rn.is(ip)); // rn should never be ip, or will be trashed
+ Condition cond = Instruction::ConditionField(instr);
+ if ((instr & ~kCondMask) == 13*B21) { // mov, S not set
+ move_32_bit_immediate(cond, rd, LeaveCC, x);
+ } else {
+ if ((instr & kMovMvnMask) == kMovMvnPattern) {
+ // Moves need to use a constant pool entry.
+ RecordRelocInfo(x.rmode_, x.imm32_, USE_CONSTANT_POOL);
+ ldr(ip, MemOperand(pc, 0), cond);
+ } else if (x.must_output_reloc_info(this)) {
+ // Otherwise, use most efficient form of fetching from constant pool.
+ move_32_bit_immediate(cond, ip, LeaveCC, x);
+ } else {
+ // If this is not a mov or mvn instruction we may still be able to
+ // avoid a constant pool entry by using mvn or movw.
+ mov(ip, x, LeaveCC, cond);
+ }
+ addrmod1(instr, rn, rd, Operand(ip));
+ }
+ return;
+ }
+ instr |= I | rotate_imm*B8 | immed_8;
+ } else if (!x.rs_.is_valid()) {
+ // Immediate shift.
+ instr |= x.shift_imm_*B7 | x.shift_op_ | x.rm_.code();
+ } else {
+ // Register shift.
+ ASSERT(!rn.is(pc) && !rd.is(pc) && !x.rm_.is(pc) && !x.rs_.is(pc));
+ instr |= x.rs_.code()*B8 | x.shift_op_ | B4 | x.rm_.code();
+ }
+ emit(instr | rn.code()*B16 | rd.code()*B12);
+ if (rn.is(pc) || x.rm_.is(pc)) {
+ // Block constant pool emission for one instruction after reading pc.
+ BlockConstPoolFor(1);
+ }
+}
+
+
+void Assembler::addrmod2(Instr instr, Register rd, const MemOperand& x) {
+ ASSERT((instr & ~(kCondMask | B | L)) == B26);
+ int am = x.am_;
+ if (!x.rm_.is_valid()) {
+ // Immediate offset.
+ int offset_12 = x.offset_;
+ if (offset_12 < 0) {
+ offset_12 = -offset_12;
+ am ^= U;
+ }
+ if (!is_uint12(offset_12)) {
+ // Immediate offset cannot be encoded, load it first to register ip
+ // rn (and rd in a load) should never be ip, or will be trashed.
+ ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip)));
+ mov(ip, Operand(x.offset_), LeaveCC, Instruction::ConditionField(instr));
+ addrmod2(instr, rd, MemOperand(x.rn_, ip, x.am_));
+ return;
+ }
+ ASSERT(offset_12 >= 0); // no masking needed
+ instr |= offset_12;
+ } else {
+ // Register offset (shift_imm_ and shift_op_ are 0) or scaled
+ // register offset the constructors make sure than both shift_imm_
+ // and shift_op_ are initialized.
+ ASSERT(!x.rm_.is(pc));
+ instr |= B25 | x.shift_imm_*B7 | x.shift_op_ | x.rm_.code();
+ }
+ ASSERT((am & (P|W)) == P || !x.rn_.is(pc)); // no pc base with writeback
+ emit(instr | am | x.rn_.code()*B16 | rd.code()*B12);
+}
+
+
+void Assembler::addrmod3(Instr instr, Register rd, const MemOperand& x) {
+ ASSERT((instr & ~(kCondMask | L | S6 | H)) == (B4 | B7));
+ ASSERT(x.rn_.is_valid());
+ int am = x.am_;
+ if (!x.rm_.is_valid()) {
+ // Immediate offset.
+ int offset_8 = x.offset_;
+ if (offset_8 < 0) {
+ offset_8 = -offset_8;
+ am ^= U;
+ }
+ if (!is_uint8(offset_8)) {
+ // Immediate offset cannot be encoded, load it first to register ip
+ // rn (and rd in a load) should never be ip, or will be trashed.
+ ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip)));
+ mov(ip, Operand(x.offset_), LeaveCC, Instruction::ConditionField(instr));
+ addrmod3(instr, rd, MemOperand(x.rn_, ip, x.am_));
+ return;
+ }
+ ASSERT(offset_8 >= 0); // no masking needed
+ instr |= B | (offset_8 >> 4)*B8 | (offset_8 & 0xf);
+ } else if (x.shift_imm_ != 0) {
+ // Scaled register offset not supported, load index first
+ // rn (and rd in a load) should never be ip, or will be trashed.
+ ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip)));
+ mov(ip, Operand(x.rm_, x.shift_op_, x.shift_imm_), LeaveCC,
+ Instruction::ConditionField(instr));
+ addrmod3(instr, rd, MemOperand(x.rn_, ip, x.am_));
+ return;
+ } else {
+ // Register offset.
+ ASSERT((am & (P|W)) == P || !x.rm_.is(pc)); // no pc index with writeback
+ instr |= x.rm_.code();
+ }
+ ASSERT((am & (P|W)) == P || !x.rn_.is(pc)); // no pc base with writeback
+ emit(instr | am | x.rn_.code()*B16 | rd.code()*B12);
+}
+
+
+void Assembler::addrmod4(Instr instr, Register rn, RegList rl) {
+ ASSERT((instr & ~(kCondMask | P | U | W | L)) == B27);
+ ASSERT(rl != 0);
+ ASSERT(!rn.is(pc));
+ emit(instr | rn.code()*B16 | rl);
+}
+
+
+void Assembler::addrmod5(Instr instr, CRegister crd, const MemOperand& x) {
+ // Unindexed addressing is not encoded by this function.
+ ASSERT_EQ((B27 | B26),
+ (instr & ~(kCondMask | kCoprocessorMask | P | U | N | W | L)));
+ ASSERT(x.rn_.is_valid() && !x.rm_.is_valid());
+ int am = x.am_;
+ int offset_8 = x.offset_;
+ ASSERT((offset_8 & 3) == 0); // offset must be an aligned word offset
+ offset_8 >>= 2;
+ if (offset_8 < 0) {
+ offset_8 = -offset_8;
+ am ^= U;
+ }
+ ASSERT(is_uint8(offset_8)); // unsigned word offset must fit in a byte
+ ASSERT((am & (P|W)) == P || !x.rn_.is(pc)); // no pc base with writeback
+
+ // Post-indexed addressing requires W == 1; different than in addrmod2/3.
+ if ((am & P) == 0)
+ am |= W;
+
+ ASSERT(offset_8 >= 0); // no masking needed
+ emit(instr | am | x.rn_.code()*B16 | crd.code()*B12 | offset_8);
+}
+
+
+int Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
+ int target_pos;
+ if (L->is_bound()) {
+ target_pos = L->pos();
+ } else {
+ if (L->is_linked()) {
+ // Point to previous instruction that uses the link.
+ target_pos = L->pos();
+ } else {
+ // First entry of the link chain points to itself.
+ target_pos = pc_offset();
+ }
+ L->link_to(pc_offset());
+ }
+
+ // Block the emission of the constant pool, since the branch instruction must
+ // be emitted at the pc offset recorded by the label.
+ BlockConstPoolFor(1);
+ return target_pos - (pc_offset() + kPcLoadDelta);
+}
+
+
+void Assembler::label_at_put(Label* L, int at_offset) {
+ int target_pos;
+ ASSERT(!L->is_bound());
+ if (L->is_linked()) {
+ // Point to previous instruction that uses the link.
+ target_pos = L->pos();
+ } else {
+ // First entry of the link chain points to itself.
+ target_pos = at_offset;
+ }
+ L->link_to(at_offset);
+ instr_at_put(at_offset, target_pos + (Code::kHeaderSize - kHeapObjectTag));
+}
+
+
+// Branch instructions.
+void Assembler::b(int branch_offset, Condition cond) {
+ ASSERT((branch_offset & 3) == 0);
+ int imm24 = branch_offset >> 2;
+ ASSERT(is_int24(imm24));
+ emit(cond | B27 | B25 | (imm24 & kImm24Mask));
+
+ if (cond == al) {
+ // Dead code is a good location to emit the constant pool.
+ CheckConstPool(false, false);
+ }
+}
+
+
+void Assembler::bl(int branch_offset, Condition cond) {
+ positions_recorder()->WriteRecordedPositions();
+ ASSERT((branch_offset & 3) == 0);
+ int imm24 = branch_offset >> 2;
+ ASSERT(is_int24(imm24));
+ emit(cond | B27 | B25 | B24 | (imm24 & kImm24Mask));
+}
+
+
+void Assembler::blx(int branch_offset) { // v5 and above
+ positions_recorder()->WriteRecordedPositions();
+ ASSERT((branch_offset & 1) == 0);
+ int h = ((branch_offset & 2) >> 1)*B24;
+ int imm24 = branch_offset >> 2;
+ ASSERT(is_int24(imm24));
+ emit(kSpecialCondition | B27 | B25 | h | (imm24 & kImm24Mask));
+}
+
+
+void Assembler::blx(Register target, Condition cond) { // v5 and above
+ positions_recorder()->WriteRecordedPositions();
+ ASSERT(!target.is(pc));
+ emit(cond | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | BLX | target.code());
+}
+
+
+void Assembler::bx(Register target, Condition cond) { // v5 and above, plus v4t
+ positions_recorder()->WriteRecordedPositions();
+ ASSERT(!target.is(pc)); // use of pc is actually allowed, but discouraged
+ emit(cond | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | BX | target.code());
+}
+
+
+// Data-processing instructions.
+
+void Assembler::and_(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | AND | s, src1, dst, src2);
+}
+
+
+void Assembler::eor(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | EOR | s, src1, dst, src2);
+}
+
+
+void Assembler::sub(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | SUB | s, src1, dst, src2);
+}
+
+
+void Assembler::rsb(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | RSB | s, src1, dst, src2);
+}
+
+
+void Assembler::add(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | ADD | s, src1, dst, src2);
+}
+
+
+void Assembler::adc(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | ADC | s, src1, dst, src2);
+}
+
+
+void Assembler::sbc(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | SBC | s, src1, dst, src2);
+}
+
+
+void Assembler::rsc(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | RSC | s, src1, dst, src2);
+}
+
+
+void Assembler::tst(Register src1, const Operand& src2, Condition cond) {
+ addrmod1(cond | TST | S, src1, r0, src2);
+}
+
+
+void Assembler::teq(Register src1, const Operand& src2, Condition cond) {
+ addrmod1(cond | TEQ | S, src1, r0, src2);
+}
+
+
+void Assembler::cmp(Register src1, const Operand& src2, Condition cond) {
+ addrmod1(cond | CMP | S, src1, r0, src2);
+}
+
+
+void Assembler::cmp_raw_immediate(
+ Register src, int raw_immediate, Condition cond) {
+ ASSERT(is_uint12(raw_immediate));
+ emit(cond | I | CMP | S | src.code() << 16 | raw_immediate);
+}
+
+
+void Assembler::cmn(Register src1, const Operand& src2, Condition cond) {
+ addrmod1(cond | CMN | S, src1, r0, src2);
+}
+
+
+void Assembler::orr(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | ORR | s, src1, dst, src2);
+}
+
+
+void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) {
+ if (dst.is(pc)) {
+ positions_recorder()->WriteRecordedPositions();
+ }
+ // Don't allow nop instructions in the form mov rn, rn to be generated using
+ // the mov instruction. They must be generated using nop(int/NopMarkerTypes)
+ // or MarkCode(int/NopMarkerTypes) pseudo instructions.
+ ASSERT(!(src.is_reg() && src.rm().is(dst) && s == LeaveCC && cond == al));
+ addrmod1(cond | MOV | s, r0, dst, src);
+}
+
+
+void Assembler::movw(Register reg, uint32_t immediate, Condition cond) {
+ ASSERT(immediate < 0x10000);
+ // May use movw if supported, but on unsupported platforms will try to use
+ // equivalent rotated immed_8 value and other tricks before falling back to a
+ // constant pool load.
+ mov(reg, Operand(immediate), LeaveCC, cond);
+}
+
+
+void Assembler::movt(Register reg, uint32_t immediate, Condition cond) {
+ emit(cond | 0x34*B20 | reg.code()*B12 | EncodeMovwImmediate(immediate));
+}
+
+
+void Assembler::bic(Register dst, Register src1, const Operand& src2,
+ SBit s, Condition cond) {
+ addrmod1(cond | BIC | s, src1, dst, src2);
+}
+
+
+void Assembler::mvn(Register dst, const Operand& src, SBit s, Condition cond) {
+ addrmod1(cond | MVN | s, r0, dst, src);
+}
+
+
+// Multiply instructions.
+void Assembler::mla(Register dst, Register src1, Register src2, Register srcA,
+ SBit s, Condition cond) {
+ ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc) && !srcA.is(pc));
+ emit(cond | A | s | dst.code()*B16 | srcA.code()*B12 |
+ src2.code()*B8 | B7 | B4 | src1.code());
+}
+
+
+void Assembler::mls(Register dst, Register src1, Register src2, Register srcA,
+ Condition cond) {
+ ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc) && !srcA.is(pc));
+ emit(cond | B22 | B21 | dst.code()*B16 | srcA.code()*B12 |
+ src2.code()*B8 | B7 | B4 | src1.code());
+}
+
+
+void Assembler::sdiv(Register dst, Register src1, Register src2,
+ Condition cond) {
+ ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc));
+ ASSERT(IsEnabled(SUDIV));
+ emit(cond | B26 | B25| B24 | B20 | dst.code()*B16 | 0xf * B12 |
+ src2.code()*B8 | B4 | src1.code());
+}
+
+
+void Assembler::mul(Register dst, Register src1, Register src2,
+ SBit s, Condition cond) {
+ ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc));
+ // dst goes in bits 16-19 for this instruction!
+ emit(cond | s | dst.code()*B16 | src2.code()*B8 | B7 | B4 | src1.code());
+}
+
+
+void Assembler::smlal(Register dstL,
+ Register dstH,
+ Register src1,
+ Register src2,
+ SBit s,
+ Condition cond) {
+ ASSERT(!dstL.is(pc) && !dstH.is(pc) && !src1.is(pc) && !src2.is(pc));
+ ASSERT(!dstL.is(dstH));
+ emit(cond | B23 | B22 | A | s | dstH.code()*B16 | dstL.code()*B12 |
+ src2.code()*B8 | B7 | B4 | src1.code());
+}
+
+
+void Assembler::smull(Register dstL,
+ Register dstH,
+ Register src1,
+ Register src2,
+ SBit s,
+ Condition cond) {
+ ASSERT(!dstL.is(pc) && !dstH.is(pc) && !src1.is(pc) && !src2.is(pc));
+ ASSERT(!dstL.is(dstH));
+ emit(cond | B23 | B22 | s | dstH.code()*B16 | dstL.code()*B12 |
+ src2.code()*B8 | B7 | B4 | src1.code());
+}
+
+
+void Assembler::umlal(Register dstL,
+ Register dstH,
+ Register src1,
+ Register src2,
+ SBit s,
+ Condition cond) {
+ ASSERT(!dstL.is(pc) && !dstH.is(pc) && !src1.is(pc) && !src2.is(pc));
+ ASSERT(!dstL.is(dstH));
+ emit(cond | B23 | A | s | dstH.code()*B16 | dstL.code()*B12 |
+ src2.code()*B8 | B7 | B4 | src1.code());
+}
+
+
+void Assembler::umull(Register dstL,
+ Register dstH,
+ Register src1,
+ Register src2,
+ SBit s,
+ Condition cond) {
+ ASSERT(!dstL.is(pc) && !dstH.is(pc) && !src1.is(pc) && !src2.is(pc));
+ ASSERT(!dstL.is(dstH));
+ emit(cond | B23 | s | dstH.code()*B16 | dstL.code()*B12 |
+ src2.code()*B8 | B7 | B4 | src1.code());
+}
+
+
+// Miscellaneous arithmetic instructions.
+void Assembler::clz(Register dst, Register src, Condition cond) {
+ // v5 and above.
+ ASSERT(!dst.is(pc) && !src.is(pc));
+ emit(cond | B24 | B22 | B21 | 15*B16 | dst.code()*B12 |
+ 15*B8 | CLZ | src.code());
+}
+
+
+// Saturating instructions.
+
+// Unsigned saturate.
+void Assembler::usat(Register dst,
+ int satpos,
+ const Operand& src,
+ Condition cond) {
+ // v6 and above.
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
+ ASSERT(!dst.is(pc) && !src.rm_.is(pc));
+ ASSERT((satpos >= 0) && (satpos <= 31));
+ ASSERT((src.shift_op_ == ASR) || (src.shift_op_ == LSL));
+ ASSERT(src.rs_.is(no_reg));
+
+ int sh = 0;
+ if (src.shift_op_ == ASR) {
+ sh = 1;
+ }
+
+ emit(cond | 0x6*B24 | 0xe*B20 | satpos*B16 | dst.code()*B12 |
+ src.shift_imm_*B7 | sh*B6 | 0x1*B4 | src.rm_.code());
+}
+
+
+// Bitfield manipulation instructions.
+
+// Unsigned bit field extract.
+// Extracts #width adjacent bits from position #lsb in a register, and
+// writes them to the low bits of a destination register.
+// ubfx dst, src, #lsb, #width
+void Assembler::ubfx(Register dst,
+ Register src,
+ int lsb,
+ int width,
+ Condition cond) {
+ // v7 and above.
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
+ ASSERT(!dst.is(pc) && !src.is(pc));
+ ASSERT((lsb >= 0) && (lsb <= 31));
+ ASSERT((width >= 1) && (width <= (32 - lsb)));
+ emit(cond | 0xf*B23 | B22 | B21 | (width - 1)*B16 | dst.code()*B12 |
+ lsb*B7 | B6 | B4 | src.code());
+}
+
+
+// Signed bit field extract.
+// Extracts #width adjacent bits from position #lsb in a register, and
+// writes them to the low bits of a destination register. The extracted
+// value is sign extended to fill the destination register.
+// sbfx dst, src, #lsb, #width
+void Assembler::sbfx(Register dst,
+ Register src,
+ int lsb,
+ int width,
+ Condition cond) {
+ // v7 and above.
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
+ ASSERT(!dst.is(pc) && !src.is(pc));
+ ASSERT((lsb >= 0) && (lsb <= 31));
+ ASSERT((width >= 1) && (width <= (32 - lsb)));
+ emit(cond | 0xf*B23 | B21 | (width - 1)*B16 | dst.code()*B12 |
+ lsb*B7 | B6 | B4 | src.code());
+}
+
+
+// Bit field clear.
+// Sets #width adjacent bits at position #lsb in the destination register
+// to zero, preserving the value of the other bits.
+// bfc dst, #lsb, #width
+void Assembler::bfc(Register dst, int lsb, int width, Condition cond) {
+ // v7 and above.
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
+ ASSERT(!dst.is(pc));
+ ASSERT((lsb >= 0) && (lsb <= 31));
+ ASSERT((width >= 1) && (width <= (32 - lsb)));
+ int msb = lsb + width - 1;
+ emit(cond | 0x1f*B22 | msb*B16 | dst.code()*B12 | lsb*B7 | B4 | 0xf);
+}
+
+
+// Bit field insert.
+// Inserts #width adjacent bits from the low bits of the source register
+// into position #lsb of the destination register.
+// bfi dst, src, #lsb, #width
+void Assembler::bfi(Register dst,
+ Register src,
+ int lsb,
+ int width,
+ Condition cond) {
+ // v7 and above.
+ ASSERT(CpuFeatures::IsSupported(ARMv7));
+ ASSERT(!dst.is(pc) && !src.is(pc));
+ ASSERT((lsb >= 0) && (lsb <= 31));
+ ASSERT((width >= 1) && (width <= (32 - lsb)));
+ int msb = lsb + width - 1;
+ emit(cond | 0x1f*B22 | msb*B16 | dst.code()*B12 | lsb*B7 | B4 |
+ src.code());
+}
+
+
+void Assembler::pkhbt(Register dst,
+ Register src1,
+ const Operand& src2,
+ Condition cond ) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.125.
+ // cond(31-28) | 01101000(27-20) | Rn(19-16) |
+ // Rd(15-12) | imm5(11-7) | 0(6) | 01(5-4) | Rm(3-0)
+ ASSERT(!dst.is(pc));
+ ASSERT(!src1.is(pc));
+ ASSERT(!src2.rm().is(pc));
+ ASSERT(!src2.rm().is(no_reg));
+ ASSERT(src2.rs().is(no_reg));
+ ASSERT((src2.shift_imm_ >= 0) && (src2.shift_imm_ <= 31));
+ ASSERT(src2.shift_op() == LSL);
+ emit(cond | 0x68*B20 | src1.code()*B16 | dst.code()*B12 |
+ src2.shift_imm_*B7 | B4 | src2.rm().code());
+}
+
+
+void Assembler::pkhtb(Register dst,
+ Register src1,
+ const Operand& src2,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.125.
+ // cond(31-28) | 01101000(27-20) | Rn(19-16) |
+ // Rd(15-12) | imm5(11-7) | 1(6) | 01(5-4) | Rm(3-0)
+ ASSERT(!dst.is(pc));
+ ASSERT(!src1.is(pc));
+ ASSERT(!src2.rm().is(pc));
+ ASSERT(!src2.rm().is(no_reg));
+ ASSERT(src2.rs().is(no_reg));
+ ASSERT((src2.shift_imm_ >= 1) && (src2.shift_imm_ <= 32));
+ ASSERT(src2.shift_op() == ASR);
+ int asr = (src2.shift_imm_ == 32) ? 0 : src2.shift_imm_;
+ emit(cond | 0x68*B20 | src1.code()*B16 | dst.code()*B12 |
+ asr*B7 | B6 | B4 | src2.rm().code());
+}
+
+
+void Assembler::uxtb(Register dst,
+ const Operand& src,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.274.
+ // cond(31-28) | 01101110(27-20) | 1111(19-16) |
+ // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0)
+ ASSERT(!dst.is(pc));
+ ASSERT(!src.rm().is(pc));
+ ASSERT(!src.rm().is(no_reg));
+ ASSERT(src.rs().is(no_reg));
+ ASSERT((src.shift_imm_ == 0) ||
+ (src.shift_imm_ == 8) ||
+ (src.shift_imm_ == 16) ||
+ (src.shift_imm_ == 24));
+ ASSERT(src.shift_op() == ROR);
+ emit(cond | 0x6E*B20 | 0xF*B16 | dst.code()*B12 |
+ ((src.shift_imm_ >> 1)&0xC)*B8 | 7*B4 | src.rm().code());
+}
+
+
+void Assembler::uxtab(Register dst,
+ Register src1,
+ const Operand& src2,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.271.
+ // cond(31-28) | 01101110(27-20) | Rn(19-16) |
+ // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0)
+ ASSERT(!dst.is(pc));
+ ASSERT(!src1.is(pc));
+ ASSERT(!src2.rm().is(pc));
+ ASSERT(!src2.rm().is(no_reg));
+ ASSERT(src2.rs().is(no_reg));
+ ASSERT((src2.shift_imm_ == 0) ||
+ (src2.shift_imm_ == 8) ||
+ (src2.shift_imm_ == 16) ||
+ (src2.shift_imm_ == 24));
+ ASSERT(src2.shift_op() == ROR);
+ emit(cond | 0x6E*B20 | src1.code()*B16 | dst.code()*B12 |
+ ((src2.shift_imm_ >> 1) &0xC)*B8 | 7*B4 | src2.rm().code());
+}
+
+
+void Assembler::uxtb16(Register dst,
+ const Operand& src,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.275.
+ // cond(31-28) | 01101100(27-20) | 1111(19-16) |
+ // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0)
+ ASSERT(!dst.is(pc));
+ ASSERT(!src.rm().is(pc));
+ ASSERT(!src.rm().is(no_reg));
+ ASSERT(src.rs().is(no_reg));
+ ASSERT((src.shift_imm_ == 0) ||
+ (src.shift_imm_ == 8) ||
+ (src.shift_imm_ == 16) ||
+ (src.shift_imm_ == 24));
+ ASSERT(src.shift_op() == ROR);
+ emit(cond | 0x6C*B20 | 0xF*B16 | dst.code()*B12 |
+ ((src.shift_imm_ >> 1)&0xC)*B8 | 7*B4 | src.rm().code());
+}
+
+
+// Status register access instructions.
+void Assembler::mrs(Register dst, SRegister s, Condition cond) {
+ ASSERT(!dst.is(pc));
+ emit(cond | B24 | s | 15*B16 | dst.code()*B12);
+}
+
+
+void Assembler::msr(SRegisterFieldMask fields, const Operand& src,
+ Condition cond) {
+ ASSERT(fields >= B16 && fields < B20); // at least one field set
+ Instr instr;
+ if (!src.rm_.is_valid()) {
+ // Immediate.
+ uint32_t rotate_imm;
+ uint32_t immed_8;
+ if (src.must_output_reloc_info(this) ||
+ !fits_shifter(src.imm32_, &rotate_imm, &immed_8, NULL)) {
+ // Immediate operand cannot be encoded, load it first to register ip.
+ RecordRelocInfo(src.rmode_, src.imm32_);
+ ldr(ip, MemOperand(pc, 0), cond);
+ msr(fields, Operand(ip), cond);
+ return;
+ }
+ instr = I | rotate_imm*B8 | immed_8;
+ } else {
+ ASSERT(!src.rs_.is_valid() && src.shift_imm_ == 0); // only rm allowed
+ instr = src.rm_.code();
+ }
+ emit(cond | instr | B24 | B21 | fields | 15*B12);
+}
+
+
+// Load/Store instructions.
+void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) {
+ if (dst.is(pc)) {
+ positions_recorder()->WriteRecordedPositions();
+ }
+ addrmod2(cond | B26 | L, dst, src);
+}
+
+
+void Assembler::str(Register src, const MemOperand& dst, Condition cond) {
+ addrmod2(cond | B26, src, dst);
+}
+
+
+void Assembler::ldrb(Register dst, const MemOperand& src, Condition cond) {
+ addrmod2(cond | B26 | B | L, dst, src);
+}
+
+
+void Assembler::strb(Register src, const MemOperand& dst, Condition cond) {
+ addrmod2(cond | B26 | B, src, dst);
+}
+
+
+void Assembler::ldrh(Register dst, const MemOperand& src, Condition cond) {
+ addrmod3(cond | L | B7 | H | B4, dst, src);
+}
+
+
+void Assembler::strh(Register src, const MemOperand& dst, Condition cond) {
+ addrmod3(cond | B7 | H | B4, src, dst);
+}
+
+
+void Assembler::ldrsb(Register dst, const MemOperand& src, Condition cond) {
+ addrmod3(cond | L | B7 | S6 | B4, dst, src);
+}
+
+
+void Assembler::ldrsh(Register dst, const MemOperand& src, Condition cond) {
+ addrmod3(cond | L | B7 | S6 | H | B4, dst, src);
+}
+
+
+void Assembler::ldrd(Register dst1, Register dst2,
+ const MemOperand& src, Condition cond) {
+ ASSERT(IsEnabled(ARMv7));
+ ASSERT(src.rm().is(no_reg));
+ ASSERT(!dst1.is(lr)); // r14.
+ ASSERT_EQ(0, dst1.code() % 2);
+ ASSERT_EQ(dst1.code() + 1, dst2.code());
+ addrmod3(cond | B7 | B6 | B4, dst1, src);
+}
+
+
+void Assembler::strd(Register src1, Register src2,
+ const MemOperand& dst, Condition cond) {
+ ASSERT(dst.rm().is(no_reg));
+ ASSERT(!src1.is(lr)); // r14.
+ ASSERT_EQ(0, src1.code() % 2);
+ ASSERT_EQ(src1.code() + 1, src2.code());
+ ASSERT(IsEnabled(ARMv7));
+ addrmod3(cond | B7 | B6 | B5 | B4, src1, dst);
+}
+
+
+// Preload instructions.
+void Assembler::pld(const MemOperand& address) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.128.
+ // 1111(31-28) | 0111(27-24) | U(23) | R(22) | 01(21-20) | Rn(19-16) |
+ // 1111(15-12) | imm5(11-07) | type(6-5) | 0(4)| Rm(3-0) |
+ ASSERT(address.rm().is(no_reg));
+ ASSERT(address.am() == Offset);
+ int U = B23;
+ int offset = address.offset();
+ if (offset < 0) {
+ offset = -offset;
+ U = 0;
+ }
+ ASSERT(offset < 4096);
+ emit(kSpecialCondition | B26 | B24 | U | B22 | B20 | address.rn().code()*B16 |
+ 0xf*B12 | offset);
+}
+
+
+// Load/Store multiple instructions.
+void Assembler::ldm(BlockAddrMode am,
+ Register base,
+ RegList dst,
+ Condition cond) {
+ // ABI stack constraint: ldmxx base, {..sp..} base != sp is not restartable.
+ ASSERT(base.is(sp) || (dst & sp.bit()) == 0);
+
+ addrmod4(cond | B27 | am | L, base, dst);
+
+ // Emit the constant pool after a function return implemented by ldm ..{..pc}.
+ if (cond == al && (dst & pc.bit()) != 0) {
+ // There is a slight chance that the ldm instruction was actually a call,
+ // in which case it would be wrong to return into the constant pool; we
+ // recognize this case by checking if the emission of the pool was blocked
+ // at the pc of the ldm instruction by a mov lr, pc instruction; if this is
+ // the case, we emit a jump over the pool.
+ CheckConstPool(true, no_const_pool_before_ == pc_offset() - kInstrSize);
+ }
+}
+
+
+void Assembler::stm(BlockAddrMode am,
+ Register base,
+ RegList src,
+ Condition cond) {
+ addrmod4(cond | B27 | am, base, src);
+}
+
+
+// Exception-generating instructions and debugging support.
+// Stops with a non-negative code less than kNumOfWatchedStops support
+// enabling/disabling and a counter feature. See simulator-arm.h .
+void Assembler::stop(const char* msg, Condition cond, int32_t code) {
+#ifndef __arm__
+ ASSERT(code >= kDefaultStopCode);
+ {
+ // The Simulator will handle the stop instruction and get the message
+ // address. It expects to find the address just after the svc instruction.
+ BlockConstPoolScope block_const_pool(this);
+ if (code >= 0) {
+ svc(kStopCode + code, cond);
+ } else {
+ svc(kStopCode + kMaxStopCode, cond);
+ }
+ emit(reinterpret_cast<Instr>(msg));
+ }
+#else // def __arm__
+ if (cond != al) {
+ Label skip;
+ b(&skip, NegateCondition(cond));
+ bkpt(0);
+ bind(&skip);
+ } else {
+ bkpt(0);
+ }
+#endif // def __arm__
+}
+
+
+void Assembler::bkpt(uint32_t imm16) { // v5 and above
+ ASSERT(is_uint16(imm16));
+ emit(al | B24 | B21 | (imm16 >> 4)*B8 | BKPT | (imm16 & 0xf));
+}
+
+
+void Assembler::svc(uint32_t imm24, Condition cond) {
+ ASSERT(is_uint24(imm24));
+ emit(cond | 15*B24 | imm24);
+}
+
+
+// Coprocessor instructions.
+void Assembler::cdp(Coprocessor coproc,
+ int opcode_1,
+ CRegister crd,
+ CRegister crn,
+ CRegister crm,
+ int opcode_2,
+ Condition cond) {
+ ASSERT(is_uint4(opcode_1) && is_uint3(opcode_2));
+ emit(cond | B27 | B26 | B25 | (opcode_1 & 15)*B20 | crn.code()*B16 |
+ crd.code()*B12 | coproc*B8 | (opcode_2 & 7)*B5 | crm.code());
+}
+
+
+void Assembler::cdp2(Coprocessor coproc,
+ int opcode_1,
+ CRegister crd,
+ CRegister crn,
+ CRegister crm,
+ int opcode_2) { // v5 and above
+ cdp(coproc, opcode_1, crd, crn, crm, opcode_2, kSpecialCondition);
+}
+
+
+void Assembler::mcr(Coprocessor coproc,
+ int opcode_1,
+ Register rd,
+ CRegister crn,
+ CRegister crm,
+ int opcode_2,
+ Condition cond) {
+ ASSERT(is_uint3(opcode_1) && is_uint3(opcode_2));
+ emit(cond | B27 | B26 | B25 | (opcode_1 & 7)*B21 | crn.code()*B16 |
+ rd.code()*B12 | coproc*B8 | (opcode_2 & 7)*B5 | B4 | crm.code());
+}
+
+
+void Assembler::mcr2(Coprocessor coproc,
+ int opcode_1,
+ Register rd,
+ CRegister crn,
+ CRegister crm,
+ int opcode_2) { // v5 and above
+ mcr(coproc, opcode_1, rd, crn, crm, opcode_2, kSpecialCondition);
+}
+
+
+void Assembler::mrc(Coprocessor coproc,
+ int opcode_1,
+ Register rd,
+ CRegister crn,
+ CRegister crm,
+ int opcode_2,
+ Condition cond) {
+ ASSERT(is_uint3(opcode_1) && is_uint3(opcode_2));
+ emit(cond | B27 | B26 | B25 | (opcode_1 & 7)*B21 | L | crn.code()*B16 |
+ rd.code()*B12 | coproc*B8 | (opcode_2 & 7)*B5 | B4 | crm.code());
+}
+
+
+void Assembler::mrc2(Coprocessor coproc,
+ int opcode_1,
+ Register rd,
+ CRegister crn,
+ CRegister crm,
+ int opcode_2) { // v5 and above
+ mrc(coproc, opcode_1, rd, crn, crm, opcode_2, kSpecialCondition);
+}
+
+
+void Assembler::ldc(Coprocessor coproc,
+ CRegister crd,
+ const MemOperand& src,
+ LFlag l,
+ Condition cond) {
+ addrmod5(cond | B27 | B26 | l | L | coproc*B8, crd, src);
+}
+
+
+void Assembler::ldc(Coprocessor coproc,
+ CRegister crd,
+ Register rn,
+ int option,
+ LFlag l,
+ Condition cond) {
+ // Unindexed addressing.
+ ASSERT(is_uint8(option));
+ emit(cond | B27 | B26 | U | l | L | rn.code()*B16 | crd.code()*B12 |
+ coproc*B8 | (option & 255));
+}
+
+
+void Assembler::ldc2(Coprocessor coproc,
+ CRegister crd,
+ const MemOperand& src,
+ LFlag l) { // v5 and above
+ ldc(coproc, crd, src, l, kSpecialCondition);
+}
+
+
+void Assembler::ldc2(Coprocessor coproc,
+ CRegister crd,
+ Register rn,
+ int option,
+ LFlag l) { // v5 and above
+ ldc(coproc, crd, rn, option, l, kSpecialCondition);
+}
+
+
+// Support for VFP.
+
+void Assembler::vldr(const DwVfpRegister dst,
+ const Register base,
+ int offset,
+ const Condition cond) {
+ // Ddst = MEM(Rbase + offset).
+ // Instruction details available in ARM DDI 0406C.b, A8-924.
+ // cond(31-28) | 1101(27-24)| U(23) | D(22) | 01(21-20) | Rbase(19-16) |
+ // Vd(15-12) | 1011(11-8) | offset
+ int u = 1;
+ if (offset < 0) {
+ offset = -offset;
+ u = 0;
+ }
+ int vd, d;
+ dst.split_code(&vd, &d);
+
+ ASSERT(offset >= 0);
+ if ((offset % 4) == 0 && (offset / 4) < 256) {
+ emit(cond | 0xD*B24 | u*B23 | d*B22 | B20 | base.code()*B16 | vd*B12 |
+ 0xB*B8 | ((offset / 4) & 255));
+ } else {
+ // Larger offsets must be handled by computing the correct address
+ // in the ip register.
+ ASSERT(!base.is(ip));
+ if (u == 1) {
+ add(ip, base, Operand(offset));
+ } else {
+ sub(ip, base, Operand(offset));
+ }
+ emit(cond | 0xD*B24 | d*B22 | B20 | ip.code()*B16 | vd*B12 | 0xB*B8);
+ }
+}
+
+
+void Assembler::vldr(const DwVfpRegister dst,
+ const MemOperand& operand,
+ const Condition cond) {
+ ASSERT(!operand.rm().is_valid());
+ ASSERT(operand.am_ == Offset);
+ vldr(dst, operand.rn(), operand.offset(), cond);
+}
+
+
+void Assembler::vldr(const SwVfpRegister dst,
+ const Register base,
+ int offset,
+ const Condition cond) {
+ // Sdst = MEM(Rbase + offset).
+ // Instruction details available in ARM DDI 0406A, A8-628.
+ // cond(31-28) | 1101(27-24)| U001(23-20) | Rbase(19-16) |
+ // Vdst(15-12) | 1010(11-8) | offset
+ int u = 1;
+ if (offset < 0) {
+ offset = -offset;
+ u = 0;
+ }
+ int sd, d;
+ dst.split_code(&sd, &d);
+ ASSERT(offset >= 0);
+
+ if ((offset % 4) == 0 && (offset / 4) < 256) {
+ emit(cond | u*B23 | d*B22 | 0xD1*B20 | base.code()*B16 | sd*B12 |
+ 0xA*B8 | ((offset / 4) & 255));
+ } else {
+ // Larger offsets must be handled by computing the correct address
+ // in the ip register.
+ ASSERT(!base.is(ip));
+ if (u == 1) {
+ add(ip, base, Operand(offset));
+ } else {
+ sub(ip, base, Operand(offset));
+ }
+ emit(cond | d*B22 | 0xD1*B20 | ip.code()*B16 | sd*B12 | 0xA*B8);
+ }
+}
+
+
+void Assembler::vldr(const SwVfpRegister dst,
+ const MemOperand& operand,
+ const Condition cond) {
+ ASSERT(!operand.rm().is_valid());
+ ASSERT(operand.am_ == Offset);
+ vldr(dst, operand.rn(), operand.offset(), cond);
+}
+
+
+void Assembler::vstr(const DwVfpRegister src,
+ const Register base,
+ int offset,
+ const Condition cond) {
+ // MEM(Rbase + offset) = Dsrc.
+ // Instruction details available in ARM DDI 0406C.b, A8-1082.
+ // cond(31-28) | 1101(27-24)| U(23) | D(22) | 00(21-20) | Rbase(19-16) |
+ // Vd(15-12) | 1011(11-8) | (offset/4)
+ int u = 1;
+ if (offset < 0) {
+ offset = -offset;
+ u = 0;
+ }
+ ASSERT(offset >= 0);
+ int vd, d;
+ src.split_code(&vd, &d);
+
+ if ((offset % 4) == 0 && (offset / 4) < 256) {
+ emit(cond | 0xD*B24 | u*B23 | d*B22 | base.code()*B16 | vd*B12 | 0xB*B8 |
+ ((offset / 4) & 255));
+ } else {
+ // Larger offsets must be handled by computing the correct address
+ // in the ip register.
+ ASSERT(!base.is(ip));
+ if (u == 1) {
+ add(ip, base, Operand(offset));
+ } else {
+ sub(ip, base, Operand(offset));
+ }
+ emit(cond | 0xD*B24 | d*B22 | ip.code()*B16 | vd*B12 | 0xB*B8);
+ }
+}
+
+
+void Assembler::vstr(const DwVfpRegister src,
+ const MemOperand& operand,
+ const Condition cond) {
+ ASSERT(!operand.rm().is_valid());
+ ASSERT(operand.am_ == Offset);
+ vstr(src, operand.rn(), operand.offset(), cond);
+}
+
+
+void Assembler::vstr(const SwVfpRegister src,
+ const Register base,
+ int offset,
+ const Condition cond) {
+ // MEM(Rbase + offset) = SSrc.
+ // Instruction details available in ARM DDI 0406A, A8-786.
+ // cond(31-28) | 1101(27-24)| U000(23-20) | Rbase(19-16) |
+ // Vdst(15-12) | 1010(11-8) | (offset/4)
+ int u = 1;
+ if (offset < 0) {
+ offset = -offset;
+ u = 0;
+ }
+ int sd, d;
+ src.split_code(&sd, &d);
+ ASSERT(offset >= 0);
+ if ((offset % 4) == 0 && (offset / 4) < 256) {
+ emit(cond | u*B23 | d*B22 | 0xD0*B20 | base.code()*B16 | sd*B12 |
+ 0xA*B8 | ((offset / 4) & 255));
+ } else {
+ // Larger offsets must be handled by computing the correct address
+ // in the ip register.
+ ASSERT(!base.is(ip));
+ if (u == 1) {
+ add(ip, base, Operand(offset));
+ } else {
+ sub(ip, base, Operand(offset));
+ }
+ emit(cond | d*B22 | 0xD0*B20 | ip.code()*B16 | sd*B12 | 0xA*B8);
+ }
+}
+
+
+void Assembler::vstr(const SwVfpRegister src,
+ const MemOperand& operand,
+ const Condition cond) {
+ ASSERT(!operand.rm().is_valid());
+ ASSERT(operand.am_ == Offset);
+ vstr(src, operand.rn(), operand.offset(), cond);
+}
+
+
+void Assembler::vldm(BlockAddrMode am,
+ Register base,
+ DwVfpRegister first,
+ DwVfpRegister last,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8-922.
+ // cond(31-28) | 110(27-25)| PUDW1(24-20) | Rbase(19-16) |
+ // first(15-12) | 1011(11-8) | (count * 2)
+ ASSERT_LE(first.code(), last.code());
+ ASSERT(am == ia || am == ia_w || am == db_w);
+ ASSERT(!base.is(pc));
+
+ int sd, d;
+ first.split_code(&sd, &d);
+ int count = last.code() - first.code() + 1;
+ ASSERT(count <= 16);
+ emit(cond | B27 | B26 | am | d*B22 | B20 | base.code()*B16 | sd*B12 |
+ 0xB*B8 | count*2);
+}
+
+
+void Assembler::vstm(BlockAddrMode am,
+ Register base,
+ DwVfpRegister first,
+ DwVfpRegister last,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8-1080.
+ // cond(31-28) | 110(27-25)| PUDW0(24-20) | Rbase(19-16) |
+ // first(15-12) | 1011(11-8) | (count * 2)
+ ASSERT_LE(first.code(), last.code());
+ ASSERT(am == ia || am == ia_w || am == db_w);
+ ASSERT(!base.is(pc));
+
+ int sd, d;
+ first.split_code(&sd, &d);
+ int count = last.code() - first.code() + 1;
+ ASSERT(count <= 16);
+ emit(cond | B27 | B26 | am | d*B22 | base.code()*B16 | sd*B12 |
+ 0xB*B8 | count*2);
+}
+
+void Assembler::vldm(BlockAddrMode am,
+ Register base,
+ SwVfpRegister first,
+ SwVfpRegister last,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406A, A8-626.
+ // cond(31-28) | 110(27-25)| PUDW1(24-20) | Rbase(19-16) |
+ // first(15-12) | 1010(11-8) | (count/2)
+ ASSERT_LE(first.code(), last.code());
+ ASSERT(am == ia || am == ia_w || am == db_w);
+ ASSERT(!base.is(pc));
+
+ int sd, d;
+ first.split_code(&sd, &d);
+ int count = last.code() - first.code() + 1;
+ emit(cond | B27 | B26 | am | d*B22 | B20 | base.code()*B16 | sd*B12 |
+ 0xA*B8 | count);
+}
+
+
+void Assembler::vstm(BlockAddrMode am,
+ Register base,
+ SwVfpRegister first,
+ SwVfpRegister last,
+ Condition cond) {
+ // Instruction details available in ARM DDI 0406A, A8-784.
+ // cond(31-28) | 110(27-25)| PUDW0(24-20) | Rbase(19-16) |
+ // first(15-12) | 1011(11-8) | (count/2)
+ ASSERT_LE(first.code(), last.code());
+ ASSERT(am == ia || am == ia_w || am == db_w);
+ ASSERT(!base.is(pc));
+
+ int sd, d;
+ first.split_code(&sd, &d);
+ int count = last.code() - first.code() + 1;
+ emit(cond | B27 | B26 | am | d*B22 | base.code()*B16 | sd*B12 |
+ 0xA*B8 | count);
+}
+
+
+static void DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi) {
+ uint64_t i;
+ OS::MemCopy(&i, &d, 8);
+
+ *lo = i & 0xffffffff;
+ *hi = i >> 32;
+}
+
+
+// Only works for little endian floating point formats.
+// We don't support VFP on the mixed endian floating point platform.
+static bool FitsVMOVDoubleImmediate(double d, uint32_t *encoding) {
+ ASSERT(CpuFeatures::IsSupported(VFP3));
+
+ // VMOV can accept an immediate of the form:
+ //
+ // +/- m * 2^(-n) where 16 <= m <= 31 and 0 <= n <= 7
+ //
+ // The immediate is encoded using an 8-bit quantity, comprised of two
+ // 4-bit fields. For an 8-bit immediate of the form:
+ //
+ // [abcdefgh]
+ //
+ // where a is the MSB and h is the LSB, an immediate 64-bit double can be
+ // created of the form:
+ //
+ // [aBbbbbbb,bbcdefgh,00000000,00000000,
+ // 00000000,00000000,00000000,00000000]
+ //
+ // where B = ~b.
+ //
+
+ uint32_t lo, hi;
+ DoubleAsTwoUInt32(d, &lo, &hi);
+
+ // The most obvious constraint is the long block of zeroes.
+ if ((lo != 0) || ((hi & 0xffff) != 0)) {
+ return false;
+ }
+
+ // Bits 62:55 must be all clear or all set.
+ if (((hi & 0x3fc00000) != 0) && ((hi & 0x3fc00000) != 0x3fc00000)) {
+ return false;
+ }
+
+ // Bit 63 must be NOT bit 62.
+ if (((hi ^ (hi << 1)) & (0x40000000)) == 0) {
+ return false;
+ }
+
+ // Create the encoded immediate in the form:
+ // [00000000,0000abcd,00000000,0000efgh]
+ *encoding = (hi >> 16) & 0xf; // Low nybble.
+ *encoding |= (hi >> 4) & 0x70000; // Low three bits of the high nybble.
+ *encoding |= (hi >> 12) & 0x80000; // Top bit of the high nybble.
+
+ return true;
+}
+
+
+void Assembler::vmov(const DwVfpRegister dst,
+ double imm,
+ const Register scratch) {
+ uint32_t enc;
+ if (CpuFeatures::IsSupported(VFP3) && FitsVMOVDoubleImmediate(imm, &enc)) {
+ // The double can be encoded in the instruction.
+ //
+ // Dd = immediate
+ // Instruction details available in ARM DDI 0406C.b, A8-936.
+ // cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | imm4H(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | imm4L(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ emit(al | 0x1D*B23 | d*B22 | 0x3*B20 | vd*B12 | 0x5*B9 | B8 | enc);
+ } else if (FLAG_enable_vldr_imm) {
+ // TODO(jfb) Temporarily turned off until we have constant blinding or
+ // some equivalent mitigation: an attacker can otherwise control
+ // generated data which also happens to be executable, a Very Bad
+ // Thing indeed.
+ // Blinding gets tricky because we don't have xor, we probably
+ // need to add/subtract without losing precision, which requires a
+ // cookie value that Lithium is probably better positioned to
+ // choose.
+ // We could also add a few peepholes here like detecting 0.0 and
+ // -0.0 and doing a vmov from the sequestered d14, forcing denorms
+ // to zero (we set flush-to-zero), and normalizing NaN values.
+ // We could also detect redundant values.
+ // The code could also randomize the order of values, though
+ // that's tricky because vldr has a limited reach. Furthermore
+ // it breaks load locality.
+ RecordRelocInfo(imm);
+ vldr(dst, MemOperand(pc, 0));
+ } else {
+ // Synthesise the double from ARM immediates.
+ uint32_t lo, hi;
+ DoubleAsTwoUInt32(imm, &lo, &hi);
+
+ if (scratch.is(no_reg)) {
+ if (dst.code() < 16) {
+ const LowDwVfpRegister loc = LowDwVfpRegister::from_code(dst.code());
+ // Move the low part of the double into the lower of the corresponsing S
+ // registers of D register dst.
+ mov(ip, Operand(lo));
+ vmov(loc.low(), ip);
+
+ // Move the high part of the double into the higher of the
+ // corresponsing S registers of D register dst.
+ mov(ip, Operand(hi));
+ vmov(loc.high(), ip);
+ } else {
+ // D16-D31 does not have S registers, so move the low and high parts
+ // directly to the D register using vmov.32.
+ // Note: This may be slower, so we only do this when we have to.
+ mov(ip, Operand(lo));
+ vmov(dst, VmovIndexLo, ip);
+ mov(ip, Operand(hi));
+ vmov(dst, VmovIndexHi, ip);
+ }
+ } else {
+ // Move the low and high parts of the double to a D register in one
+ // instruction.
+ mov(ip, Operand(lo));
+ mov(scratch, Operand(hi));
+ vmov(dst, ip, scratch);
+ }
+ }
+}
+
+
+void Assembler::vmov(const SwVfpRegister dst,
+ const SwVfpRegister src,
+ const Condition cond) {
+ // Sd = Sm
+ // Instruction details available in ARM DDI 0406B, A8-642.
+ int sd, d, sm, m;
+ dst.split_code(&sd, &d);
+ src.split_code(&sm, &m);
+ emit(cond | 0xE*B24 | d*B22 | 0xB*B20 | sd*B12 | 0xA*B8 | B6 | m*B5 | sm);
+}
+
+
+void Assembler::vmov(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond) {
+ // Dd = Dm
+ // Instruction details available in ARM DDI 0406C.b, A8-938.
+ // cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | 0000(19-16) | Vd(15-12) |
+ // 101(11-9) | sz=1(8) | 0(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vm, m;
+ src.split_code(&vm, &m);
+ emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | vd*B12 | 0x5*B9 | B8 | B6 | m*B5 |
+ vm);
+}
+
+
+void Assembler::vmov(const DwVfpRegister dst,
+ const VmovIndex index,
+ const Register src,
+ const Condition cond) {
+ // Dd[index] = Rt
+ // Instruction details available in ARM DDI 0406C.b, A8-940.
+ // cond(31-28) | 1110(27-24) | 0(23) | opc1=0index(22-21) | 0(20) |
+ // Vd(19-16) | Rt(15-12) | 1011(11-8) | D(7) | opc2=00(6-5) | 1(4) | 0000(3-0)
+ ASSERT(index.index == 0 || index.index == 1);
+ int vd, d;
+ dst.split_code(&vd, &d);
+ emit(cond | 0xE*B24 | index.index*B21 | vd*B16 | src.code()*B12 | 0xB*B8 |
+ d*B7 | B4);
+}
+
+
+void Assembler::vmov(const Register dst,
+ const VmovIndex index,
+ const DwVfpRegister src,
+ const Condition cond) {
+ // Dd[index] = Rt
+ // Instruction details available in ARM DDI 0406C.b, A8.8.342.
+ // cond(31-28) | 1110(27-24) | U=0(23) | opc1=0index(22-21) | 1(20) |
+ // Vn(19-16) | Rt(15-12) | 1011(11-8) | N(7) | opc2=00(6-5) | 1(4) | 0000(3-0)
+ ASSERT(index.index == 0 || index.index == 1);
+ int vn, n;
+ src.split_code(&vn, &n);
+ emit(cond | 0xE*B24 | index.index*B21 | B20 | vn*B16 | dst.code()*B12 |
+ 0xB*B8 | n*B7 | B4);
+}
+
+
+void Assembler::vmov(const DwVfpRegister dst,
+ const Register src1,
+ const Register src2,
+ const Condition cond) {
+ // Dm = <Rt,Rt2>.
+ // Instruction details available in ARM DDI 0406C.b, A8-948.
+ // cond(31-28) | 1100(27-24)| 010(23-21) | op=0(20) | Rt2(19-16) |
+ // Rt(15-12) | 1011(11-8) | 00(7-6) | M(5) | 1(4) | Vm
+ ASSERT(!src1.is(pc) && !src2.is(pc));
+ int vm, m;
+ dst.split_code(&vm, &m);
+ emit(cond | 0xC*B24 | B22 | src2.code()*B16 |
+ src1.code()*B12 | 0xB*B8 | m*B5 | B4 | vm);
+}
+
+
+void Assembler::vmov(const Register dst1,
+ const Register dst2,
+ const DwVfpRegister src,
+ const Condition cond) {
+ // <Rt,Rt2> = Dm.
+ // Instruction details available in ARM DDI 0406C.b, A8-948.
+ // cond(31-28) | 1100(27-24)| 010(23-21) | op=1(20) | Rt2(19-16) |
+ // Rt(15-12) | 1011(11-8) | 00(7-6) | M(5) | 1(4) | Vm
+ ASSERT(!dst1.is(pc) && !dst2.is(pc));
+ int vm, m;
+ src.split_code(&vm, &m);
+ emit(cond | 0xC*B24 | B22 | B20 | dst2.code()*B16 |
+ dst1.code()*B12 | 0xB*B8 | m*B5 | B4 | vm);
+}
+
+
+void Assembler::vmov(const SwVfpRegister dst,
+ const Register src,
+ const Condition cond) {
+ // Sn = Rt.
+ // Instruction details available in ARM DDI 0406A, A8-642.
+ // cond(31-28) | 1110(27-24)| 000(23-21) | op=0(20) | Vn(19-16) |
+ // Rt(15-12) | 1010(11-8) | N(7)=0 | 00(6-5) | 1(4) | 0000(3-0)
+ ASSERT(!src.is(pc));
+ int sn, n;
+ dst.split_code(&sn, &n);
+ emit(cond | 0xE*B24 | sn*B16 | src.code()*B12 | 0xA*B8 | n*B7 | B4);
+}
+
+
+void Assembler::vmov(const Register dst,
+ const SwVfpRegister src,
+ const Condition cond) {
+ // Rt = Sn.
+ // Instruction details available in ARM DDI 0406A, A8-642.
+ // cond(31-28) | 1110(27-24)| 000(23-21) | op=1(20) | Vn(19-16) |
+ // Rt(15-12) | 1010(11-8) | N(7)=0 | 00(6-5) | 1(4) | 0000(3-0)
+ ASSERT(!dst.is(pc));
+ int sn, n;
+ src.split_code(&sn, &n);
+ emit(cond | 0xE*B24 | B20 | sn*B16 | dst.code()*B12 | 0xA*B8 | n*B7 | B4);
+}
+
+
+// Type of data to read from or write to VFP register.
+// Used as specifier in generic vcvt instruction.
+enum VFPType { S32, U32, F32, F64 };
+
+
+static bool IsSignedVFPType(VFPType type) {
+ switch (type) {
+ case S32:
+ return true;
+ case U32:
+ return false;
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+
+
+static bool IsIntegerVFPType(VFPType type) {
+ switch (type) {
+ case S32:
+ case U32:
+ return true;
+ case F32:
+ case F64:
+ return false;
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+
+
+static bool IsDoubleVFPType(VFPType type) {
+ switch (type) {
+ case F32:
+ return false;
+ case F64:
+ return true;
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+
+
+// Split five bit reg_code based on size of reg_type.
+// 32-bit register codes are Vm:M
+// 64-bit register codes are M:Vm
+// where Vm is four bits, and M is a single bit.
+static void SplitRegCode(VFPType reg_type,
+ int reg_code,
+ int* vm,
+ int* m) {
+ ASSERT((reg_code >= 0) && (reg_code <= 31));
+ if (IsIntegerVFPType(reg_type) || !IsDoubleVFPType(reg_type)) {
+ // 32 bit type.
+ *m = reg_code & 0x1;
+ *vm = reg_code >> 1;
+ } else {
+ // 64 bit type.
+ *m = (reg_code & 0x10) >> 4;
+ *vm = reg_code & 0x0F;
+ }
+}
+
+
+// Encode vcvt.src_type.dst_type instruction.
+static Instr EncodeVCVT(const VFPType dst_type,
+ const int dst_code,
+ const VFPType src_type,
+ const int src_code,
+ VFPConversionMode mode,
+ const Condition cond) {
+ ASSERT(src_type != dst_type);
+ int D, Vd, M, Vm;
+ SplitRegCode(src_type, src_code, &Vm, &M);
+ SplitRegCode(dst_type, dst_code, &Vd, &D);
+
+ if (IsIntegerVFPType(dst_type) || IsIntegerVFPType(src_type)) {
+ // Conversion between IEEE floating point and 32-bit integer.
+ // Instruction details available in ARM DDI 0406B, A8.6.295.
+ // cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 1(19) | opc2(18-16) |
+ // Vd(15-12) | 101(11-9) | sz(8) | op(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
+ ASSERT(!IsIntegerVFPType(dst_type) || !IsIntegerVFPType(src_type));
+
+ int sz, opc2, op;
+
+ if (IsIntegerVFPType(dst_type)) {
+ opc2 = IsSignedVFPType(dst_type) ? 0x5 : 0x4;
+ sz = IsDoubleVFPType(src_type) ? 0x1 : 0x0;
+ op = mode;
+ } else {
+ ASSERT(IsIntegerVFPType(src_type));
+ opc2 = 0x0;
+ sz = IsDoubleVFPType(dst_type) ? 0x1 : 0x0;
+ op = IsSignedVFPType(src_type) ? 0x1 : 0x0;
+ }
+
+ return (cond | 0xE*B24 | B23 | D*B22 | 0x3*B20 | B19 | opc2*B16 |
+ Vd*B12 | 0x5*B9 | sz*B8 | op*B7 | B6 | M*B5 | Vm);
+ } else {
+ // Conversion between IEEE double and single precision.
+ // Instruction details available in ARM DDI 0406B, A8.6.298.
+ // cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 0111(19-16) |
+ // Vd(15-12) | 101(11-9) | sz(8) | 1(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
+ int sz = IsDoubleVFPType(src_type) ? 0x1 : 0x0;
+ return (cond | 0xE*B24 | B23 | D*B22 | 0x3*B20 | 0x7*B16 |
+ Vd*B12 | 0x5*B9 | sz*B8 | B7 | B6 | M*B5 | Vm);
+ }
+}
+
+
+void Assembler::vcvt_f64_s32(const DwVfpRegister dst,
+ const SwVfpRegister src,
+ VFPConversionMode mode,
+ const Condition cond) {
+ emit(EncodeVCVT(F64, dst.code(), S32, src.code(), mode, cond));
+}
+
+
+void Assembler::vcvt_f32_s32(const SwVfpRegister dst,
+ const SwVfpRegister src,
+ VFPConversionMode mode,
+ const Condition cond) {
+ emit(EncodeVCVT(F32, dst.code(), S32, src.code(), mode, cond));
+}
+
+
+void Assembler::vcvt_f64_u32(const DwVfpRegister dst,
+ const SwVfpRegister src,
+ VFPConversionMode mode,
+ const Condition cond) {
+ emit(EncodeVCVT(F64, dst.code(), U32, src.code(), mode, cond));
+}
+
+
+void Assembler::vcvt_s32_f64(const SwVfpRegister dst,
+ const DwVfpRegister src,
+ VFPConversionMode mode,
+ const Condition cond) {
+ emit(EncodeVCVT(S32, dst.code(), F64, src.code(), mode, cond));
+}
+
+
+void Assembler::vcvt_u32_f64(const SwVfpRegister dst,
+ const DwVfpRegister src,
+ VFPConversionMode mode,
+ const Condition cond) {
+ emit(EncodeVCVT(U32, dst.code(), F64, src.code(), mode, cond));
+}
+
+
+void Assembler::vcvt_f64_f32(const DwVfpRegister dst,
+ const SwVfpRegister src,
+ VFPConversionMode mode,
+ const Condition cond) {
+ emit(EncodeVCVT(F64, dst.code(), F32, src.code(), mode, cond));
+}
+
+
+void Assembler::vcvt_f32_f64(const SwVfpRegister dst,
+ const DwVfpRegister src,
+ VFPConversionMode mode,
+ const Condition cond) {
+ emit(EncodeVCVT(F32, dst.code(), F64, src.code(), mode, cond));
+}
+
+
+void Assembler::vcvt_f64_s32(const DwVfpRegister dst,
+ int fraction_bits,
+ const Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8-874.
+ // cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | 1010(19-16) | Vd(15-12) |
+ // 101(11-9) | sf=1(8) | sx=1(7) | 1(6) | i(5) | 0(4) | imm4(3-0)
+ ASSERT(fraction_bits > 0 && fraction_bits <= 32);
+ ASSERT(CpuFeatures::IsSupported(VFP3));
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int i = ((32 - fraction_bits) >> 4) & 1;
+ int imm4 = (32 - fraction_bits) & 0xf;
+ emit(cond | 0xE*B24 | B23 | d*B22 | 0x3*B20 | B19 | 0x2*B16 |
+ vd*B12 | 0x5*B9 | B8 | B7 | B6 | i*B5 | imm4);
+}
+
+
+void Assembler::vneg(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8-968.
+ // cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | 0001(19-16) | Vd(15-12) |
+ // 101(11-9) | sz=1(8) | 0(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vm, m;
+ src.split_code(&vm, &m);
+
+ emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | B16 | vd*B12 | 0x5*B9 | B8 | B6 |
+ m*B5 | vm);
+}
+
+
+void Assembler::vabs(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8-524.
+ // cond(31-28) | 11101(27-23) | D(22) | 11(21-20) | 0000(19-16) | Vd(15-12) |
+ // 101(11-9) | sz=1(8) | 1(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vm, m;
+ src.split_code(&vm, &m);
+ emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | vd*B12 | 0x5*B9 | B8 | B7 | B6 |
+ m*B5 | vm);
+}
+
+
+void Assembler::vadd(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // Dd = vadd(Dn, Dm) double precision floating point addition.
+ // Dd = D:Vd; Dm=M:Vm; Dn=N:Vm.
+ // Instruction details available in ARM DDI 0406C.b, A8-830.
+ // cond(31-28) | 11100(27-23)| D(22) | 11(21-20) | Vn(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 0(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vn, n;
+ src1.split_code(&vn, &n);
+ int vm, m;
+ src2.split_code(&vm, &m);
+ emit(cond | 0x1C*B23 | d*B22 | 0x3*B20 | vn*B16 | vd*B12 | 0x5*B9 | B8 |
+ n*B7 | m*B5 | vm);
+}
+
+
+void Assembler::vsub(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // Dd = vsub(Dn, Dm) double precision floating point subtraction.
+ // Dd = D:Vd; Dm=M:Vm; Dn=N:Vm.
+ // Instruction details available in ARM DDI 0406C.b, A8-1086.
+ // cond(31-28) | 11100(27-23)| D(22) | 11(21-20) | Vn(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vn, n;
+ src1.split_code(&vn, &n);
+ int vm, m;
+ src2.split_code(&vm, &m);
+ emit(cond | 0x1C*B23 | d*B22 | 0x3*B20 | vn*B16 | vd*B12 | 0x5*B9 | B8 |
+ n*B7 | B6 | m*B5 | vm);
+}
+
+
+void Assembler::vmul(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // Dd = vmul(Dn, Dm) double precision floating point multiplication.
+ // Dd = D:Vd; Dm=M:Vm; Dn=N:Vm.
+ // Instruction details available in ARM DDI 0406C.b, A8-960.
+ // cond(31-28) | 11100(27-23)| D(22) | 10(21-20) | Vn(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 0(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vn, n;
+ src1.split_code(&vn, &n);
+ int vm, m;
+ src2.split_code(&vm, &m);
+ emit(cond | 0x1C*B23 | d*B22 | 0x2*B20 | vn*B16 | vd*B12 | 0x5*B9 | B8 |
+ n*B7 | m*B5 | vm);
+}
+
+
+void Assembler::vmla(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8-932.
+ // cond(31-28) | 11100(27-23) | D(22) | 00(21-20) | Vn(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | op=0(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vn, n;
+ src1.split_code(&vn, &n);
+ int vm, m;
+ src2.split_code(&vm, &m);
+ emit(cond | 0x1C*B23 | d*B22 | vn*B16 | vd*B12 | 0x5*B9 | B8 | n*B7 | m*B5 |
+ vm);
+}
+
+
+void Assembler::vmls(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8-932.
+ // cond(31-28) | 11100(27-23) | D(22) | 00(21-20) | Vn(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | op=1(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vn, n;
+ src1.split_code(&vn, &n);
+ int vm, m;
+ src2.split_code(&vm, &m);
+ emit(cond | 0x1C*B23 | d*B22 | vn*B16 | vd*B12 | 0x5*B9 | B8 | n*B7 | B6 |
+ m*B5 | vm);
+}
+
+
+void Assembler::vdiv(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // Dd = vdiv(Dn, Dm) double precision floating point division.
+ // Dd = D:Vd; Dm=M:Vm; Dn=N:Vm.
+ // Instruction details available in ARM DDI 0406C.b, A8-882.
+ // cond(31-28) | 11101(27-23)| D(22) | 00(21-20) | Vn(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | N(7) | 0(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vn, n;
+ src1.split_code(&vn, &n);
+ int vm, m;
+ src2.split_code(&vm, &m);
+ emit(cond | 0x1D*B23 | d*B22 | vn*B16 | vd*B12 | 0x5*B9 | B8 | n*B7 | m*B5 |
+ vm);
+}
+
+
+void Assembler::vcmp(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // vcmp(Dd, Dm) double precision floating point comparison.
+ // Instruction details available in ARM DDI 0406C.b, A8-864.
+ // cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 0100(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | E=0(7) | 1(6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ src1.split_code(&vd, &d);
+ int vm, m;
+ src2.split_code(&vm, &m);
+ emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | 0x4*B16 | vd*B12 | 0x5*B9 | B8 | B6 |
+ m*B5 | vm);
+}
+
+
+void Assembler::vcmp(const DwVfpRegister src1,
+ const double src2,
+ const Condition cond) {
+ // vcmp(Dd, #0.0) double precision floating point comparison.
+ // Instruction details available in ARM DDI 0406C.b, A8-864.
+ // cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 0101(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | E=0(7) | 1(6) | 0(5) | 0(4) | 0000(3-0)
+ ASSERT(src2 == 0.0);
+ int vd, d;
+ src1.split_code(&vd, &d);
+ emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | 0x5*B16 | vd*B12 | 0x5*B9 | B8 | B6);
+}
+
+
+void Assembler::vmsr(Register dst, Condition cond) {
+ // Instruction details available in ARM DDI 0406A, A8-652.
+ // cond(31-28) | 1110 (27-24) | 1110(23-20)| 0001 (19-16) |
+ // Rt(15-12) | 1010 (11-8) | 0(7) | 00 (6-5) | 1(4) | 0000(3-0)
+ emit(cond | 0xE*B24 | 0xE*B20 | B16 |
+ dst.code()*B12 | 0xA*B8 | B4);
+}
+
+
+void Assembler::vmrs(Register dst, Condition cond) {
+ // Instruction details available in ARM DDI 0406A, A8-652.
+ // cond(31-28) | 1110 (27-24) | 1111(23-20)| 0001 (19-16) |
+ // Rt(15-12) | 1010 (11-8) | 0(7) | 00 (6-5) | 1(4) | 0000(3-0)
+ emit(cond | 0xE*B24 | 0xF*B20 | B16 |
+ dst.code()*B12 | 0xA*B8 | B4);
+}
+
+
+void Assembler::vsqrt(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond) {
+ // Instruction details available in ARM DDI 0406C.b, A8-1058.
+ // cond(31-28) | 11101(27-23)| D(22) | 11(21-20) | 0001(19-16) |
+ // Vd(15-12) | 101(11-9) | sz=1(8) | 11(7-6) | M(5) | 0(4) | Vm(3-0)
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vm, m;
+ src.split_code(&vm, &m);
+ emit(cond | 0x1D*B23 | d*B22 | 0x3*B20 | B16 | vd*B12 | 0x5*B9 | B8 | 0x3*B6 |
+ m*B5 | vm);
+}
+
+
+// Support for NEON.
+
+void Assembler::vld1(NeonSize size,
+ const NeonListOperand& dst,
+ const NeonMemOperand& src) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.320.
+ // 1111(31-28) | 01000(27-23) | D(22) | 10(21-20) | Rn(19-16) |
+ // Vd(15-12) | type(11-8) | size(7-6) | align(5-4) | Rm(3-0)
+ ASSERT(CpuFeatures::IsSupported(NEON));
+ int vd, d;
+ dst.base().split_code(&vd, &d);
+ emit(0xFU*B28 | 4*B24 | d*B22 | 2*B20 | src.rn().code()*B16 | vd*B12 |
+ dst.type()*B8 | size*B6 | src.align()*B4 | src.rm().code());
+}
+
+
+void Assembler::vst1(NeonSize size,
+ const NeonListOperand& src,
+ const NeonMemOperand& dst) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.404.
+ // 1111(31-28) | 01000(27-23) | D(22) | 00(21-20) | Rn(19-16) |
+ // Vd(15-12) | type(11-8) | size(7-6) | align(5-4) | Rm(3-0)
+ ASSERT(CpuFeatures::IsSupported(NEON));
+ int vd, d;
+ src.base().split_code(&vd, &d);
+ emit(0xFU*B28 | 4*B24 | d*B22 | dst.rn().code()*B16 | vd*B12 | src.type()*B8 |
+ size*B6 | dst.align()*B4 | dst.rm().code());
+}
+
+
+void Assembler::vmovl(NeonDataType dt, QwNeonRegister dst, DwVfpRegister src) {
+ // Instruction details available in ARM DDI 0406C.b, A8.8.346.
+ // 1111(31-28) | 001(27-25) | U(24) | 1(23) | D(22) | imm3(21-19) |
+ // 000(18-16) | Vd(15-12) | 101000(11-6) | M(5) | 1(4) | Vm(3-0)
+ ASSERT(CpuFeatures::IsSupported(NEON));
+ int vd, d;
+ dst.split_code(&vd, &d);
+ int vm, m;
+ src.split_code(&vm, &m);
+ emit(0xFU*B28 | B25 | (dt & NeonDataTypeUMask) | B23 | d*B22 |
+ (dt & NeonDataTypeSizeMask)*B19 | vd*B12 | 0xA*B8 | m*B5 | B4 | vm);
+}
+
+
+// Pseudo instructions.
+void Assembler::nop(int type) {
+ // ARMv6{K/T2} and v7 have an actual NOP instruction but it serializes
+ // some of the CPU's pipeline and has to issue. Older ARM chips simply used
+ // MOV Rx, Rx as NOP and it performs better even in newer CPUs.
+ // We therefore use MOV Rx, Rx, even on newer CPUs, and use Rx to encode
+ // a type.
+ ASSERT(0 <= type && type <= 14); // mov pc, pc isn't a nop.
+ emit(al | 13*B21 | type*B12 | type);
+}
+
+
+bool Assembler::IsMovT(Instr instr) {
+ instr &= ~(((kNumberOfConditions - 1) << 28) | // Mask off conditions
+ ((kNumRegisters-1)*B12) | // mask out register
+ EncodeMovwImmediate(0xFFFF)); // mask out immediate value
+ return instr == 0x34*B20;
+}
+
+
+bool Assembler::IsMovW(Instr instr) {
+ instr &= ~(((kNumberOfConditions - 1) << 28) | // Mask off conditions
+ ((kNumRegisters-1)*B12) | // mask out destination
+ EncodeMovwImmediate(0xFFFF)); // mask out immediate value
+ return instr == 0x30*B20;
+}
+
+
+bool Assembler::IsNop(Instr instr, int type) {
+ ASSERT(0 <= type && type <= 14); // mov pc, pc isn't a nop.
+ // Check for mov rx, rx where x = type.
+ return instr == (al | 13*B21 | type*B12 | type);
+}
+
+
+bool Assembler::ImmediateFitsAddrMode1Instruction(int32_t imm32) {
+ uint32_t dummy1;
+ uint32_t dummy2;
+ return fits_shifter(imm32, &dummy1, &dummy2, NULL);
+}
+
+
+// Debugging.
+void Assembler::RecordJSReturn() {
+ positions_recorder()->WriteRecordedPositions();
+ CheckBuffer();
+ RecordRelocInfo(RelocInfo::JS_RETURN);
+}
+
+
+void Assembler::RecordDebugBreakSlot() {
+ positions_recorder()->WriteRecordedPositions();
+ CheckBuffer();
+ RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
+void Assembler::RecordComment(const char* msg) {
+ if (FLAG_code_comments) {
+ CheckBuffer();
+ RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg));
+ }
+}
+
+
+void Assembler::RecordConstPool(int size) {
+ // We only need this for debugger support, to correctly compute offsets in the
+ // code.
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ RecordRelocInfo(RelocInfo::CONST_POOL, static_cast<intptr_t>(size));
+#endif
+}
+
+
+void Assembler::GrowBuffer() {
+ if (!own_buffer_) FATAL("external code buffer is too small");
+
+ // Compute new buffer size.
+ CodeDesc desc; // the new buffer
+ if (buffer_size_ < 4*KB) {
+ desc.buffer_size = 4*KB;
+ } else if (buffer_size_ < 1*MB) {
+ desc.buffer_size = 2*buffer_size_;
+ } else {
+ desc.buffer_size = buffer_size_ + 1*MB;
+ }
+ CHECK_GT(desc.buffer_size, 0); // no overflow
+
+ // 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();
+
+ // Copy the data.
+ int pc_delta = desc.buffer - buffer_;
+ int rc_delta = (desc.buffer + desc.buffer_size) - (buffer_ + buffer_size_);
+ OS::MemMove(desc.buffer, buffer_, desc.instr_size);
+ OS::MemMove(reloc_info_writer.pos() + rc_delta,
+ reloc_info_writer.pos(), desc.reloc_size);
+
+ // Switch buffers.
+ DeleteArray(buffer_);
+ buffer_ = desc.buffer;
+ buffer_size_ = desc.buffer_size;
+ pc_ += pc_delta;
+ reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
+ reloc_info_writer.last_pc() + pc_delta);
+
+ // None of our relocation types are pc relative pointing outside the code
+ // buffer nor pc absolute pointing inside the code buffer, so there is no need
+ // to relocate any emitted relocation entries.
+
+ // Relocate pending relocation entries.
+ for (int i = 0; i < num_pending_reloc_info_; i++) {
+ RelocInfo& rinfo = pending_reloc_info_[i];
+ ASSERT(rinfo.rmode() != RelocInfo::COMMENT &&
+ rinfo.rmode() != RelocInfo::POSITION);
+ if (rinfo.rmode() != RelocInfo::JS_RETURN) {
+ rinfo.set_pc(rinfo.pc() + pc_delta);
+ }
+ }
+}
+
+
+void Assembler::db(uint8_t data) {
+ // No relocation info should be pending while using db. db is used
+ // to write pure data with no pointers and the constant pool should
+ // be emitted before using db.
+ ASSERT(num_pending_reloc_info_ == 0);
+ ASSERT(num_pending_64_bit_reloc_info_ == 0);
+ CheckBuffer();
+ *reinterpret_cast<uint8_t*>(pc_) = data;
+ pc_ += sizeof(uint8_t);
+}
+
+
+void Assembler::dd(uint32_t data) {
+ // No relocation info should be pending while using dd. dd is used
+ // to write pure data with no pointers and the constant pool should
+ // be emitted before using dd.
+ ASSERT(num_pending_reloc_info_ == 0);
+ ASSERT(num_pending_64_bit_reloc_info_ == 0);
+ CheckBuffer();
+ *reinterpret_cast<uint32_t*>(pc_) = data;
+ pc_ += sizeof(uint32_t);
+}
+
+
+void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data,
+ UseConstantPoolMode mode) {
+ // We do not try to reuse pool constants.
+ RelocInfo rinfo(pc_, rmode, data, NULL);
+ if (((rmode >= RelocInfo::JS_RETURN) &&
+ (rmode <= RelocInfo::DEBUG_BREAK_SLOT)) ||
+ (rmode == RelocInfo::CONST_POOL) ||
+ mode == DONT_USE_CONSTANT_POOL) {
+ // Adjust code for new modes.
+ ASSERT(RelocInfo::IsDebugBreakSlot(rmode)
+ || RelocInfo::IsJSReturn(rmode)
+ || RelocInfo::IsComment(rmode)
+ || RelocInfo::IsPosition(rmode)
+ || RelocInfo::IsConstPool(rmode)
+ || mode == DONT_USE_CONSTANT_POOL);
+ // These modes do not need an entry in the constant pool.
+ } else {
+ RecordRelocInfoConstantPoolEntryHelper(rinfo);
+ }
+ if (!RelocInfo::IsNone(rinfo.rmode())) {
+ // Don't record external references unless the heap will be serialized.
+ if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
+#ifdef DEBUG
+ if (!Serializer::enabled()) {
+ Serializer::TooLateToEnableNow();
+ }
+#endif
+ if (!Serializer::enabled() && !emit_debug_code()) {
+ return;
+ }
+ }
+ 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().ToInt(),
+ NULL);
+ ClearRecordedAstId();
+ reloc_info_writer.Write(&reloc_info_with_ast_id);
+ } else {
+ reloc_info_writer.Write(&rinfo);
+ }
+ }
+}
+
+
+void Assembler::RecordRelocInfo(double data) {
+ // We do not try to reuse pool constants.
+ RelocInfo rinfo(pc_, data);
+ RecordRelocInfoConstantPoolEntryHelper(rinfo);
+}
+
+
+void Assembler::RecordRelocInfoConstantPoolEntryHelper(const RelocInfo& rinfo) {
+ ASSERT(num_pending_reloc_info_ < kMaxNumPendingRelocInfo);
+ if (num_pending_reloc_info_ == 0) {
+ first_const_pool_use_ = pc_offset();
+ }
+ pending_reloc_info_[num_pending_reloc_info_++] = rinfo;
+ if (rinfo.rmode() == RelocInfo::NONE64) {
+ ++num_pending_64_bit_reloc_info_;
+ }
+ ASSERT(num_pending_64_bit_reloc_info_ <= num_pending_reloc_info_);
+ // Make sure the constant pool is not emitted in place of the next
+ // instruction for which we just recorded relocation info.
+ BlockConstPoolFor(1);
+}
+
+
+void Assembler::BlockConstPoolFor(int instructions) {
+ int pc_limit = pc_offset() + instructions * kInstrSize;
+ if (no_const_pool_before_ < pc_limit) {
+ // If there are some pending entries, the constant pool cannot be blocked
+ // further than constant pool instruction's reach.
+ ASSERT((num_pending_reloc_info_ == 0) ||
+ (pc_limit - first_const_pool_use_ < kMaxDistToIntPool));
+ // TODO(jfb) Also check 64-bit entries are in range (requires splitting
+ // them up from 32-bit entries).
+ no_const_pool_before_ = pc_limit;
+ }
+
+ if (next_buffer_check_ < no_const_pool_before_) {
+ next_buffer_check_ = no_const_pool_before_;
+ }
+}
+
+
+void Assembler::CheckConstPool(bool force_emit, bool require_jump) {
+ // Some short sequence of instruction mustn't be broken up by constant pool
+ // emission, such sequences are protected by calls to BlockConstPoolFor and
+ // BlockConstPoolScope.
+ if (is_const_pool_blocked()) {
+ // Something is wrong if emission is forced and blocked at the same time.
+ ASSERT(!force_emit);
+ return;
+ }
+
+ // There is nothing to do if there are no pending constant pool entries.
+ if (num_pending_reloc_info_ == 0) {
+ ASSERT(num_pending_64_bit_reloc_info_ == 0);
+ // Calculate the offset of the next check.
+ next_buffer_check_ = pc_offset() + kCheckPoolInterval;
+ return;
+ }
+
+ // Check that the code buffer is large enough before emitting the constant
+ // pool (include the jump over the pool and the constant pool marker and
+ // the gap to the relocation information).
+ // Note 64-bit values are wider, and the first one needs to be 64-bit aligned.
+ int jump_instr = require_jump ? kInstrSize : 0;
+ int size_up_to_marker = jump_instr + kInstrSize;
+ int size_after_marker = num_pending_reloc_info_ * kPointerSize;
+ bool has_fp_values = (num_pending_64_bit_reloc_info_ > 0);
+ // 64-bit values must be 64-bit aligned.
+ // We'll start emitting at PC: branch+marker, then 32-bit values, then
+ // 64-bit values which might need to be aligned.
+ bool require_64_bit_align = has_fp_values &&
+ (((uintptr_t)pc_ + size_up_to_marker + size_after_marker) & 0x3);
+ if (require_64_bit_align) {
+ size_after_marker += kInstrSize;
+ }
+ // num_pending_reloc_info_ also contains 64-bit entries, the above code
+ // therefore already counted half of the size for 64-bit entries. Add the
+ // remaining size.
+ STATIC_ASSERT(kPointerSize == kDoubleSize / 2);
+ size_after_marker += num_pending_64_bit_reloc_info_ * (kDoubleSize / 2);
+
+ int size = size_up_to_marker + size_after_marker;
+
+ // We emit a constant pool when:
+ // * requested to do so by parameter force_emit (e.g. after each function).
+ // * the distance from the first instruction accessing the constant pool to
+ // any of the constant pool entries will exceed its limit the next
+ // time the pool is checked. This is overly restrictive, but we don't emit
+ // constant pool entries in-order so it's conservatively correct.
+ // * the instruction doesn't require a jump after itself to jump over the
+ // constant pool, and we're getting close to running out of range.
+ if (!force_emit) {
+ ASSERT((first_const_pool_use_ >= 0) && (num_pending_reloc_info_ > 0));
+ int dist = pc_offset() + size - first_const_pool_use_;
+ if (has_fp_values) {
+ if ((dist < kMaxDistToFPPool - kCheckPoolInterval) &&
+ (require_jump || (dist < kMaxDistToFPPool / 2))) {
+ return;
+ }
+ } else {
+ if ((dist < kMaxDistToIntPool - kCheckPoolInterval) &&
+ (require_jump || (dist < kMaxDistToIntPool / 2))) {
+ return;
+ }
+ }
+ }
+
+ int needed_space = size + kGap;
+ while (buffer_space() <= needed_space) GrowBuffer();
+
+ {
+ // Block recursive calls to CheckConstPool.
+ BlockConstPoolScope block_const_pool(this);
+ RecordComment("[ Constant Pool");
+ RecordConstPool(size);
+
+ // Emit jump over constant pool if necessary.
+ Label after_pool;
+ if (require_jump) {
+ b(&after_pool);
+ }
+
+ // Put down constant pool marker "Undefined instruction".
+ // The data size helps disassembly know what to print.
+ emit(kConstantPoolMarker |
+ EncodeConstantPoolLength(size_after_marker / kPointerSize));
+
+ if (require_64_bit_align) {
+ emit(kConstantPoolMarker);
+ }
+
+ // Emit 64-bit constant pool entries first: their range is smaller than
+ // 32-bit entries.
+ for (int i = 0; i < num_pending_reloc_info_; i++) {
+ RelocInfo& rinfo = pending_reloc_info_[i];
+
+ if (rinfo.rmode() != RelocInfo::NONE64) {
+ // 32-bit values emitted later.
+ continue;
+ }
+
+ ASSERT(!((uintptr_t)pc_ & 0x3)); // Check 64-bit alignment.
+
+ Instr instr = instr_at(rinfo.pc());
+ // Instruction to patch must be 'vldr rd, [pc, #offset]' with offset == 0.
+ ASSERT((IsVldrDPcImmediateOffset(instr) &&
+ GetVldrDRegisterImmediateOffset(instr) == 0));
+
+ int delta = pc_ - rinfo.pc() - kPcLoadDelta;
+ ASSERT(is_uint10(delta));
+
+ instr_at_put(rinfo.pc(), SetVldrDRegisterImmediateOffset(instr, delta));
+
+ const double double_data = rinfo.data64();
+ uint64_t uint_data = 0;
+ OS::MemCopy(&uint_data, &double_data, sizeof(double_data));
+ emit(uint_data & 0xFFFFFFFF);
+ emit(uint_data >> 32);
+ }
+
+ // Emit 32-bit constant pool entries.
+ for (int i = 0; i < num_pending_reloc_info_; i++) {
+ RelocInfo& rinfo = pending_reloc_info_[i];
+ ASSERT(rinfo.rmode() != RelocInfo::COMMENT &&
+ rinfo.rmode() != RelocInfo::POSITION &&
+ rinfo.rmode() != RelocInfo::STATEMENT_POSITION &&
+ rinfo.rmode() != RelocInfo::CONST_POOL);
+
+ if (rinfo.rmode() == RelocInfo::NONE64) {
+ // 64-bit values emitted earlier.
+ continue;
+ }
+
+ Instr instr = instr_at(rinfo.pc());
+
+ // 64-bit loads shouldn't get here.
+ ASSERT(!IsVldrDPcImmediateOffset(instr));
+
+ int delta = pc_ - rinfo.pc() - kPcLoadDelta;
+ // 0 is the smallest delta:
+ // ldr rd, [pc, #0]
+ // constant pool marker
+ // data
+
+ if (IsLdrPcImmediateOffset(instr) &&
+ GetLdrRegisterImmediateOffset(instr) == 0) {
+ ASSERT(is_uint12(delta));
+ instr_at_put(rinfo.pc(), SetLdrRegisterImmediateOffset(instr, delta));
+ emit(rinfo.data());
+ } else {
+ ASSERT(IsMovW(instr));
+ emit(rinfo.data());
+ }
+ }
+
+ num_pending_reloc_info_ = 0;
+ num_pending_64_bit_reloc_info_ = 0;
+ first_const_pool_use_ = -1;
+
+ RecordComment("]");
+
+ if (after_pool.is_linked()) {
+ bind(&after_pool);
+ }
+ }
+
+ // Since a constant pool was just emitted, move the check offset forward by
+ // the standard interval.
+ next_buffer_check_ = pc_offset() + kCheckPoolInterval;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/assembler-arm.h b/chromium/v8/src/arm/assembler-arm.h
new file mode 100644
index 00000000000..f647848de56
--- /dev/null
+++ b/chromium/v8/src/arm/assembler-arm.h
@@ -0,0 +1,1585 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+// A light-weight ARM Assembler
+// Generates user mode instructions for the ARM architecture up to version 5
+
+#ifndef V8_ARM_ASSEMBLER_ARM_H_
+#define V8_ARM_ASSEMBLER_ARM_H_
+#include <stdio.h>
+#include "assembler.h"
+#include "constants-arm.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+// CpuFeatures keeps track of which features are supported by the target CPU.
+// Supported features must be enabled by a CpuFeatureScope before use.
+class CpuFeatures : public AllStatic {
+ public:
+ // Detect features of the target CPU. Set safe defaults if the serializer
+ // is enabled (snapshots must be portable).
+ static void Probe();
+
+ // Display target use when compiling.
+ static void PrintTarget();
+
+ // Display features.
+ static void PrintFeatures();
+
+ // Check whether a feature is supported by the target CPU.
+ static bool IsSupported(CpuFeature f) {
+ ASSERT(initialized_);
+ return (supported_ & (1u << f)) != 0;
+ }
+
+ static bool IsFoundByRuntimeProbingOnly(CpuFeature f) {
+ ASSERT(initialized_);
+ return (found_by_runtime_probing_only_ &
+ (static_cast<uint64_t>(1) << f)) != 0;
+ }
+
+ static bool IsSafeForSnapshot(CpuFeature f) {
+ return (IsSupported(f) &&
+ (!Serializer::enabled() || !IsFoundByRuntimeProbingOnly(f)));
+ }
+
+ static unsigned cache_line_size() { return cache_line_size_; }
+
+ private:
+#ifdef DEBUG
+ static bool initialized_;
+#endif
+ static unsigned supported_;
+ static unsigned found_by_runtime_probing_only_;
+ static unsigned cache_line_size_;
+
+ friend class ExternalReference;
+ DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
+};
+
+
+// CPU Registers.
+//
+// 1) We would prefer to use an enum, but enum values are assignment-
+// compatible with int, which has caused code-generation bugs.
+//
+// 2) We would prefer to use a class instead of a struct but we don't like
+// the register initialization to depend on the particular initialization
+// order (which appears to be different on OS X, Linux, and Windows for the
+// installed versions of C++ we tried). Using a struct permits C-style
+// "initialization". Also, the Register objects cannot be const as this
+// forces initialization stubs in MSVC, making us dependent on initialization
+// order.
+//
+// 3) By not using an enum, we are possibly preventing the compiler from
+// doing certain constant folds, which may significantly reduce the
+// code generated for some assembly instructions (because they boil down
+// to a few constants). If this is a problem, we could change the code
+// such that we use an enum in optimized mode, and the struct in debug
+// mode. This way we get the compile-time error checking in debug mode
+// and best performance in optimized code.
+
+// Core register
+struct Register {
+ static const int kNumRegisters = 16;
+ static const int kMaxNumAllocatableRegisters = 8;
+ static const int kSizeInBytes = 4;
+
+ inline static int NumAllocatableRegisters();
+
+ static int ToAllocationIndex(Register reg) {
+ ASSERT(reg.code() < kMaxNumAllocatableRegisters);
+ return reg.code();
+ }
+
+ static Register FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ return from_code(index);
+ }
+
+ static const char* AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ const char* const names[] = {
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ "r6",
+ "r7",
+ };
+ return names[index];
+ }
+
+ static Register from_code(int code) {
+ Register r = { code };
+ return r;
+ }
+
+ bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; }
+ bool is(Register reg) const { return code_ == reg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ ASSERT(is_valid());
+ return 1 << code_;
+ }
+
+ void set_code(int code) {
+ code_ = code;
+ ASSERT(is_valid());
+ }
+
+ // Unfortunately we can't make this private in a struct.
+ int code_;
+};
+
+// These constants are used in several locations, including static initializers
+const int kRegister_no_reg_Code = -1;
+const int kRegister_r0_Code = 0;
+const int kRegister_r1_Code = 1;
+const int kRegister_r2_Code = 2;
+const int kRegister_r3_Code = 3;
+const int kRegister_r4_Code = 4;
+const int kRegister_r5_Code = 5;
+const int kRegister_r6_Code = 6;
+const int kRegister_r7_Code = 7;
+const int kRegister_r8_Code = 8;
+const int kRegister_r9_Code = 9;
+const int kRegister_r10_Code = 10;
+const int kRegister_fp_Code = 11;
+const int kRegister_ip_Code = 12;
+const int kRegister_sp_Code = 13;
+const int kRegister_lr_Code = 14;
+const int kRegister_pc_Code = 15;
+
+const Register no_reg = { kRegister_no_reg_Code };
+
+const Register r0 = { kRegister_r0_Code };
+const Register r1 = { kRegister_r1_Code };
+const Register r2 = { kRegister_r2_Code };
+const Register r3 = { kRegister_r3_Code };
+const Register r4 = { kRegister_r4_Code };
+const Register r5 = { kRegister_r5_Code };
+const Register r6 = { kRegister_r6_Code };
+const Register r7 = { kRegister_r7_Code };
+// Used as context register.
+const Register r8 = { kRegister_r8_Code };
+// Used as lithium codegen scratch register.
+const Register r9 = { kRegister_r9_Code };
+// Used as roots register.
+const Register r10 = { kRegister_r10_Code };
+const Register fp = { kRegister_fp_Code };
+const Register ip = { kRegister_ip_Code };
+const Register sp = { kRegister_sp_Code };
+const Register lr = { kRegister_lr_Code };
+const Register pc = { kRegister_pc_Code };
+
+// Single word VFP register.
+struct SwVfpRegister {
+ static const int kSizeInBytes = 4;
+ bool is_valid() const { return 0 <= code_ && code_ < 32; }
+ bool is(SwVfpRegister reg) const { return code_ == reg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ ASSERT(is_valid());
+ return 1 << code_;
+ }
+ void split_code(int* vm, int* m) const {
+ ASSERT(is_valid());
+ *m = code_ & 0x1;
+ *vm = code_ >> 1;
+ }
+
+ int code_;
+};
+
+
+// Double word VFP register.
+struct DwVfpRegister {
+ static const int kMaxNumRegisters = 32;
+ // A few double registers are reserved: one as a scratch register and one to
+ // hold 0.0, that does not fit in the immediate field of vmov instructions.
+ // d14: 0.0
+ // d15: scratch register.
+ static const int kNumReservedRegisters = 2;
+ static const int kMaxNumAllocatableRegisters = kMaxNumRegisters -
+ kNumReservedRegisters;
+ static const int kSizeInBytes = 8;
+
+ // Note: the number of registers can be different at snapshot and run-time.
+ // Any code included in the snapshot must be able to run both with 16 or 32
+ // registers.
+ inline static int NumRegisters();
+ inline static int NumAllocatableRegisters();
+
+ inline static int ToAllocationIndex(DwVfpRegister reg);
+ static const char* AllocationIndexToString(int index);
+ inline static DwVfpRegister FromAllocationIndex(int index);
+
+ static DwVfpRegister from_code(int code) {
+ DwVfpRegister r = { code };
+ return r;
+ }
+
+ bool is_valid() const {
+ return 0 <= code_ && code_ < kMaxNumRegisters;
+ }
+ bool is(DwVfpRegister reg) const { return code_ == reg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ ASSERT(is_valid());
+ return 1 << code_;
+ }
+ void split_code(int* vm, int* m) const {
+ ASSERT(is_valid());
+ *m = (code_ & 0x10) >> 4;
+ *vm = code_ & 0x0F;
+ }
+
+ int code_;
+};
+
+
+typedef DwVfpRegister DoubleRegister;
+
+
+// Double word VFP register d0-15.
+struct LowDwVfpRegister {
+ public:
+ static const int kMaxNumLowRegisters = 16;
+ operator DwVfpRegister() const {
+ DwVfpRegister r = { code_ };
+ return r;
+ }
+ static LowDwVfpRegister from_code(int code) {
+ LowDwVfpRegister r = { code };
+ return r;
+ }
+
+ bool is_valid() const {
+ return 0 <= code_ && code_ < kMaxNumLowRegisters;
+ }
+ bool is(DwVfpRegister reg) const { return code_ == reg.code_; }
+ bool is(LowDwVfpRegister reg) const { return code_ == reg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ SwVfpRegister low() const {
+ SwVfpRegister reg;
+ reg.code_ = code_ * 2;
+
+ ASSERT(reg.is_valid());
+ return reg;
+ }
+ SwVfpRegister high() const {
+ SwVfpRegister reg;
+ reg.code_ = (code_ * 2) + 1;
+
+ ASSERT(reg.is_valid());
+ return reg;
+ }
+
+ int code_;
+};
+
+
+// Quad word NEON register.
+struct QwNeonRegister {
+ static const int kMaxNumRegisters = 16;
+
+ static QwNeonRegister from_code(int code) {
+ QwNeonRegister r = { code };
+ return r;
+ }
+
+ bool is_valid() const {
+ return (0 <= code_) && (code_ < kMaxNumRegisters);
+ }
+ bool is(QwNeonRegister reg) const { return code_ == reg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ void split_code(int* vm, int* m) const {
+ ASSERT(is_valid());
+ *m = (code_ & 0x10) >> 4;
+ *vm = code_ & 0x0F;
+ }
+
+ int code_;
+};
+
+
+typedef QwNeonRegister QuadRegister;
+
+
+// Support for the VFP registers s0 to s31 (d0 to d15).
+// Note that "s(N):s(N+1)" is the same as "d(N/2)".
+const SwVfpRegister s0 = { 0 };
+const SwVfpRegister s1 = { 1 };
+const SwVfpRegister s2 = { 2 };
+const SwVfpRegister s3 = { 3 };
+const SwVfpRegister s4 = { 4 };
+const SwVfpRegister s5 = { 5 };
+const SwVfpRegister s6 = { 6 };
+const SwVfpRegister s7 = { 7 };
+const SwVfpRegister s8 = { 8 };
+const SwVfpRegister s9 = { 9 };
+const SwVfpRegister s10 = { 10 };
+const SwVfpRegister s11 = { 11 };
+const SwVfpRegister s12 = { 12 };
+const SwVfpRegister s13 = { 13 };
+const SwVfpRegister s14 = { 14 };
+const SwVfpRegister s15 = { 15 };
+const SwVfpRegister s16 = { 16 };
+const SwVfpRegister s17 = { 17 };
+const SwVfpRegister s18 = { 18 };
+const SwVfpRegister s19 = { 19 };
+const SwVfpRegister s20 = { 20 };
+const SwVfpRegister s21 = { 21 };
+const SwVfpRegister s22 = { 22 };
+const SwVfpRegister s23 = { 23 };
+const SwVfpRegister s24 = { 24 };
+const SwVfpRegister s25 = { 25 };
+const SwVfpRegister s26 = { 26 };
+const SwVfpRegister s27 = { 27 };
+const SwVfpRegister s28 = { 28 };
+const SwVfpRegister s29 = { 29 };
+const SwVfpRegister s30 = { 30 };
+const SwVfpRegister s31 = { 31 };
+
+const DwVfpRegister no_dreg = { -1 };
+const LowDwVfpRegister d0 = { 0 };
+const LowDwVfpRegister d1 = { 1 };
+const LowDwVfpRegister d2 = { 2 };
+const LowDwVfpRegister d3 = { 3 };
+const LowDwVfpRegister d4 = { 4 };
+const LowDwVfpRegister d5 = { 5 };
+const LowDwVfpRegister d6 = { 6 };
+const LowDwVfpRegister d7 = { 7 };
+const LowDwVfpRegister d8 = { 8 };
+const LowDwVfpRegister d9 = { 9 };
+const LowDwVfpRegister d10 = { 10 };
+const LowDwVfpRegister d11 = { 11 };
+const LowDwVfpRegister d12 = { 12 };
+const LowDwVfpRegister d13 = { 13 };
+const LowDwVfpRegister d14 = { 14 };
+const LowDwVfpRegister d15 = { 15 };
+const DwVfpRegister d16 = { 16 };
+const DwVfpRegister d17 = { 17 };
+const DwVfpRegister d18 = { 18 };
+const DwVfpRegister d19 = { 19 };
+const DwVfpRegister d20 = { 20 };
+const DwVfpRegister d21 = { 21 };
+const DwVfpRegister d22 = { 22 };
+const DwVfpRegister d23 = { 23 };
+const DwVfpRegister d24 = { 24 };
+const DwVfpRegister d25 = { 25 };
+const DwVfpRegister d26 = { 26 };
+const DwVfpRegister d27 = { 27 };
+const DwVfpRegister d28 = { 28 };
+const DwVfpRegister d29 = { 29 };
+const DwVfpRegister d30 = { 30 };
+const DwVfpRegister d31 = { 31 };
+
+const QwNeonRegister q0 = { 0 };
+const QwNeonRegister q1 = { 1 };
+const QwNeonRegister q2 = { 2 };
+const QwNeonRegister q3 = { 3 };
+const QwNeonRegister q4 = { 4 };
+const QwNeonRegister q5 = { 5 };
+const QwNeonRegister q6 = { 6 };
+const QwNeonRegister q7 = { 7 };
+const QwNeonRegister q8 = { 8 };
+const QwNeonRegister q9 = { 9 };
+const QwNeonRegister q10 = { 10 };
+const QwNeonRegister q11 = { 11 };
+const QwNeonRegister q12 = { 12 };
+const QwNeonRegister q13 = { 13 };
+const QwNeonRegister q14 = { 14 };
+const QwNeonRegister q15 = { 15 };
+
+
+// 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
+struct CRegister {
+ bool is_valid() const { return 0 <= code_ && code_ < 16; }
+ bool is(CRegister creg) const { return code_ == creg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ ASSERT(is_valid());
+ return 1 << code_;
+ }
+
+ // Unfortunately we can't make this private in a struct.
+ int code_;
+};
+
+
+const CRegister no_creg = { -1 };
+
+const CRegister cr0 = { 0 };
+const CRegister cr1 = { 1 };
+const CRegister cr2 = { 2 };
+const CRegister cr3 = { 3 };
+const CRegister cr4 = { 4 };
+const CRegister cr5 = { 5 };
+const CRegister cr6 = { 6 };
+const CRegister cr7 = { 7 };
+const CRegister cr8 = { 8 };
+const CRegister cr9 = { 9 };
+const CRegister cr10 = { 10 };
+const CRegister cr11 = { 11 };
+const CRegister cr12 = { 12 };
+const CRegister cr13 = { 13 };
+const CRegister cr14 = { 14 };
+const CRegister cr15 = { 15 };
+
+
+// Coprocessor number
+enum Coprocessor {
+ p0 = 0,
+ p1 = 1,
+ p2 = 2,
+ p3 = 3,
+ p4 = 4,
+ p5 = 5,
+ p6 = 6,
+ p7 = 7,
+ p8 = 8,
+ p9 = 9,
+ p10 = 10,
+ p11 = 11,
+ p12 = 12,
+ p13 = 13,
+ p14 = 14,
+ p15 = 15
+};
+
+
+// -----------------------------------------------------------------------------
+// Machine instruction Operands
+
+// Class Operand represents a shifter operand in data processing instructions
+class Operand BASE_EMBEDDED {
+ public:
+ // immediate
+ INLINE(explicit Operand(int32_t immediate,
+ RelocInfo::Mode rmode = RelocInfo::NONE32));
+ INLINE(static Operand Zero()) {
+ return Operand(static_cast<int32_t>(0));
+ }
+ INLINE(explicit Operand(const ExternalReference& f));
+ explicit Operand(Handle<Object> handle);
+ INLINE(explicit Operand(Smi* value));
+
+ // rm
+ INLINE(explicit Operand(Register rm));
+
+ // rm <shift_op> shift_imm
+ explicit Operand(Register rm, ShiftOp shift_op, int shift_imm);
+ INLINE(static Operand SmiUntag(Register rm)) {
+ return Operand(rm, ASR, kSmiTagSize);
+ }
+ INLINE(static Operand PointerOffsetFromSmiKey(Register key)) {
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ return Operand(key, LSL, kPointerSizeLog2 - kSmiTagSize);
+ }
+ INLINE(static Operand DoubleOffsetFromSmiKey(Register key)) {
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kDoubleSizeLog2);
+ return Operand(key, LSL, kDoubleSizeLog2 - kSmiTagSize);
+ }
+
+ // rm <shift_op> rs
+ explicit Operand(Register rm, ShiftOp shift_op, Register rs);
+
+ // Return true if this is a register operand.
+ INLINE(bool is_reg() const);
+
+ // Return true if this operand fits in one instruction so that no
+ // 2-instruction solution with a load into the ip register is necessary. If
+ // the instruction this operand is used for is a MOV or MVN instruction the
+ // actual instruction to use is required for this calculation. For other
+ // instructions instr is ignored.
+ bool is_single_instruction(const Assembler* assembler, Instr instr = 0) const;
+ bool must_output_reloc_info(const Assembler* assembler) const;
+
+ inline int32_t immediate() const {
+ ASSERT(!rm_.is_valid());
+ return imm32_;
+ }
+
+ Register rm() const { return rm_; }
+ Register rs() const { return rs_; }
+ ShiftOp shift_op() const { return shift_op_; }
+
+ private:
+ Register rm_;
+ Register rs_;
+ ShiftOp shift_op_;
+ int shift_imm_; // valid if rm_ != no_reg && rs_ == no_reg
+ int32_t imm32_; // valid if rm_ == no_reg
+ RelocInfo::Mode rmode_;
+
+ friend class Assembler;
+};
+
+
+// Class MemOperand represents a memory operand in load and store instructions
+class MemOperand BASE_EMBEDDED {
+ public:
+ // [rn +/- offset] Offset/NegOffset
+ // [rn +/- offset]! PreIndex/NegPreIndex
+ // [rn], +/- offset PostIndex/NegPostIndex
+ // offset is any signed 32-bit value; offset is first loaded to register ip if
+ // it does not fit the addressing mode (12-bit unsigned and sign bit)
+ explicit MemOperand(Register rn, int32_t offset = 0, AddrMode am = Offset);
+
+ // [rn +/- rm] Offset/NegOffset
+ // [rn +/- rm]! PreIndex/NegPreIndex
+ // [rn], +/- rm PostIndex/NegPostIndex
+ explicit MemOperand(Register rn, Register rm, AddrMode am = Offset);
+
+ // [rn +/- rm <shift_op> shift_imm] Offset/NegOffset
+ // [rn +/- rm <shift_op> shift_imm]! PreIndex/NegPreIndex
+ // [rn], +/- rm <shift_op> shift_imm PostIndex/NegPostIndex
+ explicit MemOperand(Register rn, Register rm,
+ ShiftOp shift_op, int shift_imm, AddrMode am = Offset);
+ INLINE(static MemOperand PointerAddressFromSmiKey(Register array,
+ Register key,
+ AddrMode am = Offset)) {
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ return MemOperand(array, key, LSL, kPointerSizeLog2 - kSmiTagSize, am);
+ }
+
+ void set_offset(int32_t offset) {
+ ASSERT(rm_.is(no_reg));
+ offset_ = offset;
+ }
+
+ uint32_t offset() const {
+ ASSERT(rm_.is(no_reg));
+ return offset_;
+ }
+
+ Register rn() const { return rn_; }
+ Register rm() const { return rm_; }
+ AddrMode am() const { return am_; }
+
+ bool OffsetIsUint12Encodable() const {
+ return offset_ >= 0 ? is_uint12(offset_) : is_uint12(-offset_);
+ }
+
+ private:
+ Register rn_; // base
+ Register rm_; // register offset
+ int32_t offset_; // valid if rm_ == no_reg
+ ShiftOp shift_op_;
+ int shift_imm_; // valid if rm_ != no_reg && rs_ == no_reg
+ AddrMode am_; // bits P, U, and W
+
+ friend class Assembler;
+};
+
+
+// Class NeonMemOperand represents a memory operand in load and
+// store NEON instructions
+class NeonMemOperand BASE_EMBEDDED {
+ public:
+ // [rn {:align}] Offset
+ // [rn {:align}]! PostIndex
+ explicit NeonMemOperand(Register rn, AddrMode am = Offset, int align = 0);
+
+ // [rn {:align}], rm PostIndex
+ explicit NeonMemOperand(Register rn, Register rm, int align = 0);
+
+ Register rn() const { return rn_; }
+ Register rm() const { return rm_; }
+ int align() const { return align_; }
+
+ private:
+ void SetAlignment(int align);
+
+ Register rn_; // base
+ Register rm_; // register increment
+ int align_;
+};
+
+
+// Class NeonListOperand represents a list of NEON registers
+class NeonListOperand BASE_EMBEDDED {
+ public:
+ explicit NeonListOperand(DoubleRegister base, int registers_count = 1);
+ DoubleRegister base() const { return base_; }
+ NeonListType type() const { return type_; }
+ private:
+ DoubleRegister base_;
+ NeonListType type_;
+};
+
+extern const Instr kMovLrPc;
+extern const Instr kLdrPCMask;
+extern const Instr kLdrPCPattern;
+extern const Instr kBlxRegMask;
+extern const Instr kBlxRegPattern;
+extern const Instr kBlxIp;
+
+extern const Instr kMovMvnMask;
+extern const Instr kMovMvnPattern;
+extern const Instr kMovMvnFlip;
+
+extern const Instr kMovLeaveCCMask;
+extern const Instr kMovLeaveCCPattern;
+extern const Instr kMovwMask;
+extern const Instr kMovwPattern;
+extern const Instr kMovwLeaveCCFlip;
+
+extern const Instr kCmpCmnMask;
+extern const Instr kCmpCmnPattern;
+extern const Instr kCmpCmnFlip;
+extern const Instr kAddSubFlip;
+extern const Instr kAndBicFlip;
+
+struct VmovIndex {
+ unsigned char index;
+};
+const VmovIndex VmovIndexLo = { 0 };
+const VmovIndex VmovIndexHi = { 1 };
+
+class Assembler : public AssemblerBase {
+ public:
+ // Create an assembler. Instructions and relocation information are emitted
+ // into a buffer, with the instructions starting from the beginning and the
+ // relocation information starting from the end of the buffer. See CodeDesc
+ // for a detailed comment on the layout (globals.h).
+ //
+ // If the provided buffer is NULL, the assembler allocates and grows its own
+ // buffer, and buffer_size determines the initial buffer size. The buffer is
+ // owned by the assembler and deallocated upon destruction of the assembler.
+ //
+ // If the provided buffer is not NULL, the assembler uses the provided buffer
+ // for code generation and assumes its size to be buffer_size. If the buffer
+ // is too small, a fatal error occurs. No deallocation of the buffer is done
+ // upon destruction of the assembler.
+ Assembler(Isolate* isolate, void* buffer, int buffer_size);
+ virtual ~Assembler();
+
+ // GetCode emits any pending (non-emitted) code and fills the descriptor
+ // desc. GetCode() is idempotent; it returns the same result if no other
+ // Assembler functions are invoked in between GetCode() calls.
+ void GetCode(CodeDesc* desc);
+
+ // Label operations & relative jumps (PPUM Appendix D)
+ //
+ // Takes a branch opcode (cc) and a label (L) and generates
+ // either a backward branch or a forward branch and links it
+ // to the label fixup chain. Usage:
+ //
+ // Label L; // unbound label
+ // j(cc, &L); // forward branch to unbound label
+ // bind(&L); // bind label to the current pc
+ // j(cc, &L); // backward branch to bound label
+ // bind(&L); // illegal: a label may be bound only once
+ //
+ // Note: The same Label can be used for forward and backward branches
+ // but it may be bound only once.
+
+ void bind(Label* L); // binds an unbound label L to the current code position
+
+ // Returns the branch offset to the given label from the current code position
+ // Links the label to the current position if it is still unbound
+ // Manages the jump elimination optimization if the second parameter is true.
+ int branch_offset(Label* L, bool jump_elimination_allowed);
+
+ // Puts a labels target address at the given position.
+ // The high 8 bits are set to zero.
+ void label_at_put(Label* L, int at_offset);
+
+ // Return the address in the constant pool of the code target address used by
+ // the branch/call instruction at pc, or the object in a mov.
+ INLINE(static Address target_pointer_address_at(Address pc));
+
+ // Read/Modify the pointer in the branch/call/move instruction at pc.
+ INLINE(static Address target_pointer_at(Address pc));
+ INLINE(static void set_target_pointer_at(Address pc, Address target));
+
+ // Read/Modify the code target address in the branch/call instruction at pc.
+ INLINE(static Address target_address_at(Address pc));
+ INLINE(static void set_target_address_at(Address pc, Address target));
+
+ // Return the code target address at a call site from the return address
+ // of that call in the instruction stream.
+ INLINE(static Address target_address_from_return_address(Address pc));
+
+ // Given the address of the beginning of a call, return the address
+ // in the instruction stream that the call will return from.
+ INLINE(static Address return_address_from_call_start(Address pc));
+
+ // This sets the branch destination (which is in the constant pool on ARM).
+ // This is for calls and branches within generated code.
+ inline static void deserialization_set_special_target_at(
+ Address constant_pool_entry, Address target);
+
+ // This sets the branch destination (which is in the constant pool on ARM).
+ // This is for calls and branches to runtime code.
+ inline static void set_external_target_at(Address constant_pool_entry,
+ Address target);
+
+ // Here we are patching the address in the constant pool, not the actual call
+ // instruction. The address in the constant pool is the same size as a
+ // pointer.
+ static const int kSpecialTargetSize = kPointerSize;
+
+ // Size of an instruction.
+ static const int kInstrSize = sizeof(Instr);
+
+ // Distance between start of patched return sequence and the emitted address
+ // to jump to.
+ // Patched return sequence is:
+ // ldr ip, [pc, #0] @ emited address and start
+ // blx ip
+ static const int kPatchReturnSequenceAddressOffset = 0 * kInstrSize;
+
+ // Distance between start of patched debug break slot and the emitted address
+ // to jump to.
+ // Patched debug break slot code is:
+ // ldr ip, [pc, #0] @ emited address and start
+ // blx ip
+ static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize;
+
+ static const int kPatchDebugBreakSlotReturnOffset = 2 * kInstrSize;
+
+ // Difference between address of current opcode and value read from pc
+ // register.
+ static const int kPcLoadDelta = 8;
+
+ static const int kJSReturnSequenceInstructions = 4;
+ static const int kDebugBreakSlotInstructions = 3;
+ static const int kDebugBreakSlotLength =
+ kDebugBreakSlotInstructions * kInstrSize;
+
+ // ---------------------------------------------------------------------------
+ // Code generation
+
+ // Insert the smallest number of nop instructions
+ // possible to align the pc offset to a multiple
+ // of m. m must be a power of 2 (>= 4).
+ void Align(int m);
+ // Aligns code to something that's optimal for a jump target for the platform.
+ void CodeTargetAlign();
+
+ // Branch instructions
+ void b(int branch_offset, Condition cond = al);
+ void bl(int branch_offset, Condition cond = al);
+ void blx(int branch_offset); // v5 and above
+ void blx(Register target, Condition cond = al); // v5 and above
+ void bx(Register target, Condition cond = al); // v5 and above, plus v4t
+
+ // Convenience branch instructions using labels
+ void b(Label* L, Condition cond = al) {
+ b(branch_offset(L, cond == al), cond);
+ }
+ void b(Condition cond, Label* L) { b(branch_offset(L, cond == al), cond); }
+ void bl(Label* L, Condition cond = al) { bl(branch_offset(L, false), cond); }
+ void bl(Condition cond, Label* L) { bl(branch_offset(L, false), cond); }
+ void blx(Label* L) { blx(branch_offset(L, false)); } // v5 and above
+
+ // Data-processing instructions
+
+ void and_(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void eor(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void sub(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+ void sub(Register dst, Register src1, Register src2,
+ SBit s = LeaveCC, Condition cond = al) {
+ sub(dst, src1, Operand(src2), s, cond);
+ }
+
+ void rsb(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void add(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+ void add(Register dst, Register src1, Register src2,
+ SBit s = LeaveCC, Condition cond = al) {
+ add(dst, src1, Operand(src2), s, cond);
+ }
+
+ void adc(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void sbc(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void rsc(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void tst(Register src1, const Operand& src2, Condition cond = al);
+ void tst(Register src1, Register src2, Condition cond = al) {
+ tst(src1, Operand(src2), cond);
+ }
+
+ void teq(Register src1, const Operand& src2, Condition cond = al);
+
+ void cmp(Register src1, const Operand& src2, Condition cond = al);
+ void cmp(Register src1, Register src2, Condition cond = al) {
+ cmp(src1, Operand(src2), cond);
+ }
+ void cmp_raw_immediate(Register src1, int raw_immediate, Condition cond = al);
+
+ void cmn(Register src1, const Operand& src2, Condition cond = al);
+
+ void orr(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+ void orr(Register dst, Register src1, Register src2,
+ SBit s = LeaveCC, Condition cond = al) {
+ orr(dst, src1, Operand(src2), s, cond);
+ }
+
+ void mov(Register dst, const Operand& src,
+ SBit s = LeaveCC, Condition cond = al);
+ void mov(Register dst, Register src, SBit s = LeaveCC, Condition cond = al) {
+ mov(dst, Operand(src), s, cond);
+ }
+
+ // ARMv7 instructions for loading a 32 bit immediate in two instructions.
+ // This may actually emit a different mov instruction, but on an ARMv7 it
+ // is guaranteed to only emit one instruction.
+ void movw(Register reg, uint32_t immediate, Condition cond = al);
+ // The constant for movt should be in the range 0-0xffff.
+ void movt(Register reg, uint32_t immediate, Condition cond = al);
+
+ void bic(Register dst, Register src1, const Operand& src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void mvn(Register dst, const Operand& src,
+ SBit s = LeaveCC, Condition cond = al);
+
+ // Multiply instructions
+
+ void mla(Register dst, Register src1, Register src2, Register srcA,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void mls(Register dst, Register src1, Register src2, Register srcA,
+ Condition cond = al);
+
+ void sdiv(Register dst, Register src1, Register src2,
+ Condition cond = al);
+
+ void mul(Register dst, Register src1, Register src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void smlal(Register dstL, Register dstH, Register src1, Register src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void smull(Register dstL, Register dstH, Register src1, Register src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void umlal(Register dstL, Register dstH, Register src1, Register src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ void umull(Register dstL, Register dstH, Register src1, Register src2,
+ SBit s = LeaveCC, Condition cond = al);
+
+ // Miscellaneous arithmetic instructions
+
+ void clz(Register dst, Register src, Condition cond = al); // v5 and above
+
+ // Saturating instructions. v6 and above.
+
+ // Unsigned saturate.
+ //
+ // Saturate an optionally shifted signed value to an unsigned range.
+ //
+ // usat dst, #satpos, src
+ // usat dst, #satpos, src, lsl #sh
+ // usat dst, #satpos, src, asr #sh
+ //
+ // Register dst will contain:
+ //
+ // 0, if s < 0
+ // (1 << satpos) - 1, if s > ((1 << satpos) - 1)
+ // s, otherwise
+ //
+ // where s is the contents of src after shifting (if used.)
+ void usat(Register dst, int satpos, const Operand& src, Condition cond = al);
+
+ // Bitfield manipulation instructions. v7 and above.
+
+ void ubfx(Register dst, Register src, int lsb, int width,
+ Condition cond = al);
+
+ void sbfx(Register dst, Register src, int lsb, int width,
+ Condition cond = al);
+
+ void bfc(Register dst, int lsb, int width, Condition cond = al);
+
+ void bfi(Register dst, Register src, int lsb, int width,
+ Condition cond = al);
+
+ void pkhbt(Register dst, Register src1, const Operand& src2,
+ Condition cond = al);
+
+ void pkhtb(Register dst, Register src1, const Operand& src2,
+ Condition cond = al);
+
+ void uxtb(Register dst, const Operand& src, Condition cond = al);
+
+ void uxtab(Register dst, Register src1, const Operand& src2,
+ Condition cond = al);
+
+ void uxtb16(Register dst, const Operand& src, Condition cond = al);
+
+ // Status register access instructions
+
+ void mrs(Register dst, SRegister s, Condition cond = al);
+ void msr(SRegisterFieldMask fields, const Operand& src, Condition cond = al);
+
+ // Load/Store instructions
+ void ldr(Register dst, const MemOperand& src, Condition cond = al);
+ void str(Register src, const MemOperand& dst, Condition cond = al);
+ void ldrb(Register dst, const MemOperand& src, Condition cond = al);
+ void strb(Register src, const MemOperand& dst, Condition cond = al);
+ void ldrh(Register dst, const MemOperand& src, Condition cond = al);
+ void strh(Register src, const MemOperand& dst, Condition cond = al);
+ void ldrsb(Register dst, const MemOperand& src, Condition cond = al);
+ void ldrsh(Register dst, const MemOperand& src, Condition cond = al);
+ void ldrd(Register dst1,
+ Register dst2,
+ const MemOperand& src, Condition cond = al);
+ void strd(Register src1,
+ Register src2,
+ const MemOperand& dst, Condition cond = al);
+
+ // Preload instructions
+ void pld(const MemOperand& address);
+
+ // Load/Store multiple instructions
+ void ldm(BlockAddrMode am, Register base, RegList dst, Condition cond = al);
+ void stm(BlockAddrMode am, Register base, RegList src, Condition cond = al);
+
+ // Exception-generating instructions and debugging support
+ void stop(const char* msg,
+ Condition cond = al,
+ int32_t code = kDefaultStopCode);
+
+ void bkpt(uint32_t imm16); // v5 and above
+ void svc(uint32_t imm24, Condition cond = al);
+
+ // Coprocessor instructions
+
+ void cdp(Coprocessor coproc, int opcode_1,
+ CRegister crd, CRegister crn, CRegister crm,
+ int opcode_2, Condition cond = al);
+
+ void cdp2(Coprocessor coproc, int opcode_1,
+ CRegister crd, CRegister crn, CRegister crm,
+ int opcode_2); // v5 and above
+
+ void mcr(Coprocessor coproc, int opcode_1,
+ Register rd, CRegister crn, CRegister crm,
+ int opcode_2 = 0, Condition cond = al);
+
+ void mcr2(Coprocessor coproc, int opcode_1,
+ Register rd, CRegister crn, CRegister crm,
+ int opcode_2 = 0); // v5 and above
+
+ void mrc(Coprocessor coproc, int opcode_1,
+ Register rd, CRegister crn, CRegister crm,
+ int opcode_2 = 0, Condition cond = al);
+
+ void mrc2(Coprocessor coproc, int opcode_1,
+ Register rd, CRegister crn, CRegister crm,
+ int opcode_2 = 0); // v5 and above
+
+ void ldc(Coprocessor coproc, CRegister crd, const MemOperand& src,
+ LFlag l = Short, Condition cond = al);
+ void ldc(Coprocessor coproc, CRegister crd, Register base, int option,
+ LFlag l = Short, Condition cond = al);
+
+ void ldc2(Coprocessor coproc, CRegister crd, const MemOperand& src,
+ LFlag l = Short); // v5 and above
+ void ldc2(Coprocessor coproc, CRegister crd, Register base, int option,
+ LFlag l = Short); // v5 and above
+
+ // Support for VFP.
+ // All these APIs support S0 to S31 and D0 to D31.
+
+ void vldr(const DwVfpRegister dst,
+ const Register base,
+ int offset,
+ const Condition cond = al);
+ void vldr(const DwVfpRegister dst,
+ const MemOperand& src,
+ const Condition cond = al);
+
+ void vldr(const SwVfpRegister dst,
+ const Register base,
+ int offset,
+ const Condition cond = al);
+ void vldr(const SwVfpRegister dst,
+ const MemOperand& src,
+ const Condition cond = al);
+
+ void vstr(const DwVfpRegister src,
+ const Register base,
+ int offset,
+ const Condition cond = al);
+ void vstr(const DwVfpRegister src,
+ const MemOperand& dst,
+ const Condition cond = al);
+
+ void vstr(const SwVfpRegister src,
+ const Register base,
+ int offset,
+ const Condition cond = al);
+ void vstr(const SwVfpRegister src,
+ const MemOperand& dst,
+ const Condition cond = al);
+
+ void vldm(BlockAddrMode am,
+ Register base,
+ DwVfpRegister first,
+ DwVfpRegister last,
+ Condition cond = al);
+
+ void vstm(BlockAddrMode am,
+ Register base,
+ DwVfpRegister first,
+ DwVfpRegister last,
+ Condition cond = al);
+
+ void vldm(BlockAddrMode am,
+ Register base,
+ SwVfpRegister first,
+ SwVfpRegister last,
+ Condition cond = al);
+
+ void vstm(BlockAddrMode am,
+ Register base,
+ SwVfpRegister first,
+ SwVfpRegister last,
+ Condition cond = al);
+
+ void vmov(const DwVfpRegister dst,
+ double imm,
+ const Register scratch = no_reg);
+ void vmov(const SwVfpRegister dst,
+ const SwVfpRegister src,
+ const Condition cond = al);
+ void vmov(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond = al);
+ void vmov(const DwVfpRegister dst,
+ const VmovIndex index,
+ const Register src,
+ const Condition cond = al);
+ void vmov(const Register dst,
+ const VmovIndex index,
+ const DwVfpRegister src,
+ const Condition cond = al);
+ void vmov(const DwVfpRegister dst,
+ const Register src1,
+ const Register src2,
+ const Condition cond = al);
+ void vmov(const Register dst1,
+ const Register dst2,
+ const DwVfpRegister src,
+ const Condition cond = al);
+ void vmov(const SwVfpRegister dst,
+ const Register src,
+ const Condition cond = al);
+ void vmov(const Register dst,
+ const SwVfpRegister src,
+ const Condition cond = al);
+ void vcvt_f64_s32(const DwVfpRegister dst,
+ const SwVfpRegister src,
+ VFPConversionMode mode = kDefaultRoundToZero,
+ const Condition cond = al);
+ void vcvt_f32_s32(const SwVfpRegister dst,
+ const SwVfpRegister src,
+ VFPConversionMode mode = kDefaultRoundToZero,
+ const Condition cond = al);
+ void vcvt_f64_u32(const DwVfpRegister dst,
+ const SwVfpRegister src,
+ VFPConversionMode mode = kDefaultRoundToZero,
+ const Condition cond = al);
+ void vcvt_s32_f64(const SwVfpRegister dst,
+ const DwVfpRegister src,
+ VFPConversionMode mode = kDefaultRoundToZero,
+ const Condition cond = al);
+ void vcvt_u32_f64(const SwVfpRegister dst,
+ const DwVfpRegister src,
+ VFPConversionMode mode = kDefaultRoundToZero,
+ const Condition cond = al);
+ void vcvt_f64_f32(const DwVfpRegister dst,
+ const SwVfpRegister src,
+ VFPConversionMode mode = kDefaultRoundToZero,
+ const Condition cond = al);
+ void vcvt_f32_f64(const SwVfpRegister dst,
+ const DwVfpRegister src,
+ VFPConversionMode mode = kDefaultRoundToZero,
+ const Condition cond = al);
+ void vcvt_f64_s32(const DwVfpRegister dst,
+ int fraction_bits,
+ const Condition cond = al);
+
+ void vneg(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond = al);
+ void vabs(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond = al);
+ void vadd(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void vsub(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void vmul(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void vmla(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void vmls(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void vdiv(const DwVfpRegister dst,
+ const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void vcmp(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void vcmp(const DwVfpRegister src1,
+ const double src2,
+ const Condition cond = al);
+ void vmrs(const Register dst,
+ const Condition cond = al);
+ void vmsr(const Register dst,
+ const Condition cond = al);
+ void vsqrt(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond = al);
+
+ // Support for NEON.
+ // All these APIs support D0 to D31 and Q0 to Q15.
+
+ void vld1(NeonSize size,
+ const NeonListOperand& dst,
+ const NeonMemOperand& src);
+ void vst1(NeonSize size,
+ const NeonListOperand& src,
+ const NeonMemOperand& dst);
+ void vmovl(NeonDataType dt, QwNeonRegister dst, DwVfpRegister src);
+
+ // Pseudo instructions
+
+ // Different nop operations are used by the code generator to detect certain
+ // states of the generated code.
+ enum NopMarkerTypes {
+ NON_MARKING_NOP = 0,
+ DEBUG_BREAK_NOP,
+ // IC markers.
+ PROPERTY_ACCESS_INLINED,
+ PROPERTY_ACCESS_INLINED_CONTEXT,
+ PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE,
+ // Helper values.
+ LAST_CODE_MARKER,
+ FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED
+ };
+
+ void nop(int type = 0); // 0 is the default non-marking type.
+
+ void push(Register src, Condition cond = al) {
+ str(src, MemOperand(sp, 4, NegPreIndex), cond);
+ }
+
+ void pop(Register dst, Condition cond = al) {
+ ldr(dst, MemOperand(sp, 4, PostIndex), cond);
+ }
+
+ void pop() {
+ add(sp, sp, Operand(kPointerSize));
+ }
+
+ // Jump unconditionally to given label.
+ void jmp(Label* L) { b(L, al); }
+
+ static bool use_immediate_embedded_pointer_loads(
+ const Assembler* assembler) {
+ return CpuFeatures::IsSupported(MOVW_MOVT_IMMEDIATE_LOADS) &&
+ (assembler == NULL || !assembler->predictable_code_size());
+ }
+
+ // Check the code size generated from label to here.
+ int SizeOfCodeGeneratedSince(Label* label) {
+ return pc_offset() - label->pos();
+ }
+
+ // Check the number of instructions generated from label to here.
+ int InstructionsGeneratedSince(Label* label) {
+ return SizeOfCodeGeneratedSince(label) / kInstrSize;
+ }
+
+ // Check whether an immediate fits an addressing mode 1 instruction.
+ bool ImmediateFitsAddrMode1Instruction(int32_t imm32);
+
+ // Class for scoping postponing the constant pool generation.
+ class BlockConstPoolScope {
+ public:
+ explicit BlockConstPoolScope(Assembler* assem) : assem_(assem) {
+ assem_->StartBlockConstPool();
+ }
+ ~BlockConstPoolScope() {
+ assem_->EndBlockConstPool();
+ }
+
+ private:
+ Assembler* assem_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BlockConstPoolScope);
+ };
+
+ // Debugging
+
+ // Mark address of the ExitJSFrame code.
+ void RecordJSReturn();
+
+ // Mark address of a debug break slot.
+ void RecordDebugBreakSlot();
+
+ // Record the AST id of the CallIC being compiled, so that it can be placed
+ // in the relocation information.
+ void SetRecordedAstId(TypeFeedbackId ast_id) {
+ ASSERT(recorded_ast_id_.IsNone());
+ recorded_ast_id_ = ast_id;
+ }
+
+ TypeFeedbackId RecordedAstId() {
+ ASSERT(!recorded_ast_id_.IsNone());
+ return recorded_ast_id_;
+ }
+
+ void ClearRecordedAstId() { recorded_ast_id_ = TypeFeedbackId::None(); }
+
+ // Record a comment relocation entry that can be used by a disassembler.
+ // Use --code-comments to enable.
+ void RecordComment(const char* msg);
+
+ // Record the emission of a constant pool.
+ //
+ // The emission of constant pool depends on the size of the code generated and
+ // the number of RelocInfo recorded.
+ // The Debug mechanism needs to map code offsets between two versions of a
+ // function, compiled with and without debugger support (see for example
+ // Debug::PrepareForBreakPoints()).
+ // Compiling functions with debugger support generates additional code
+ // (Debug::GenerateSlot()). This may affect the emission of the constant
+ // pools and cause the version of the code with debugger support to have
+ // constant pools generated in different places.
+ // Recording the position and size of emitted constant pools allows to
+ // correctly compute the offset mappings between the different versions of a
+ // function in all situations.
+ //
+ // The parameter indicates the size of the constant pool (in bytes), including
+ // the marker and branch over the data.
+ void RecordConstPool(int size);
+
+ // Writes a single byte or word of data in the code stream. Used
+ // for inline tables, e.g., jump-tables. The constant pool should be
+ // emitted before any use of db and dd to ensure that constant pools
+ // are not emitted as part of the tables generated.
+ void db(uint8_t data);
+ void dd(uint32_t data);
+
+ 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;
+ }
+ static Condition GetCondition(Instr instr);
+ static bool IsBranch(Instr instr);
+ static int GetBranchOffset(Instr instr);
+ static bool IsLdrRegisterImmediate(Instr instr);
+ static bool IsVldrDRegisterImmediate(Instr instr);
+ static int GetLdrRegisterImmediateOffset(Instr instr);
+ static int GetVldrDRegisterImmediateOffset(Instr instr);
+ static Instr SetLdrRegisterImmediateOffset(Instr instr, int offset);
+ static Instr SetVldrDRegisterImmediateOffset(Instr instr, int offset);
+ static bool IsStrRegisterImmediate(Instr instr);
+ static Instr SetStrRegisterImmediateOffset(Instr instr, int offset);
+ static bool IsAddRegisterImmediate(Instr instr);
+ static Instr SetAddRegisterImmediateOffset(Instr instr, int offset);
+ static Register GetRd(Instr instr);
+ static Register GetRn(Instr instr);
+ static Register GetRm(Instr instr);
+ static bool IsPush(Instr instr);
+ static bool IsPop(Instr instr);
+ static bool IsStrRegFpOffset(Instr instr);
+ static bool IsLdrRegFpOffset(Instr instr);
+ static bool IsStrRegFpNegOffset(Instr instr);
+ static bool IsLdrRegFpNegOffset(Instr instr);
+ static bool IsLdrPcImmediateOffset(Instr instr);
+ static bool IsVldrDPcImmediateOffset(Instr instr);
+ static bool IsTstImmediate(Instr instr);
+ static bool IsCmpRegister(Instr instr);
+ static bool IsCmpImmediate(Instr instr);
+ static Register GetCmpImmediateRegister(Instr instr);
+ static int GetCmpImmediateRawImmediate(Instr instr);
+ static bool IsNop(Instr instr, int type = NON_MARKING_NOP);
+ static bool IsMovT(Instr instr);
+ static bool IsMovW(Instr instr);
+
+ // Constants in pools are accessed via pc relative addressing, which can
+ // reach +/-4KB for integer PC-relative loads and +/-1KB for floating-point
+ // PC-relative loads, thereby defining a maximum distance between the
+ // instruction and the accessed constant.
+ static const int kMaxDistToIntPool = 4*KB;
+ static const int kMaxDistToFPPool = 1*KB;
+ // All relocations could be integer, it therefore acts as the limit.
+ static const int kMaxNumPendingRelocInfo = kMaxDistToIntPool/kInstrSize;
+
+ // Postpone the generation of the constant pool for the specified number of
+ // instructions.
+ void BlockConstPoolFor(int instructions);
+
+ // Check if is time to emit a constant pool.
+ void CheckConstPool(bool force_emit, bool require_jump);
+
+ protected:
+ // Relocation for a type-recording IC has the AST id added to it. This
+ // member variable is a way to pass the information from the call site to
+ // the relocation info.
+ TypeFeedbackId recorded_ast_id_;
+
+ int buffer_space() const { return reloc_info_writer.pos() - pc_; }
+
+ // Decode branch instruction at pos and return branch target pos
+ int target_at(int pos);
+
+ // Patch branch instruction at pos to branch to given branch target pos
+ void target_at_put(int pos, int target_pos);
+
+ // Prevent contant pool emission until EndBlockConstPool is called.
+ // Call to this function can be nested but must be followed by an equal
+ // number of call to EndBlockConstpool.
+ void StartBlockConstPool() {
+ if (const_pool_blocked_nesting_++ == 0) {
+ // Prevent constant pool checks happening by setting the next check to
+ // the biggest possible offset.
+ next_buffer_check_ = kMaxInt;
+ }
+ }
+
+ // Resume constant pool emission. Need to be called as many time as
+ // StartBlockConstPool to have an effect.
+ void EndBlockConstPool() {
+ if (--const_pool_blocked_nesting_ == 0) {
+ // Check the constant pool hasn't been blocked for too long.
+ ASSERT((num_pending_reloc_info_ == 0) ||
+ (pc_offset() < (first_const_pool_use_ + kMaxDistToIntPool)));
+ ASSERT((num_pending_64_bit_reloc_info_ == 0) ||
+ (pc_offset() < (first_const_pool_use_ + kMaxDistToFPPool)));
+ // Two cases:
+ // * no_const_pool_before_ >= next_buffer_check_ and the emission is
+ // still blocked
+ // * no_const_pool_before_ < next_buffer_check_ and the next emit will
+ // trigger a check.
+ next_buffer_check_ = no_const_pool_before_;
+ }
+ }
+
+ bool is_const_pool_blocked() const {
+ return (const_pool_blocked_nesting_ > 0) ||
+ (pc_offset() < no_const_pool_before_);
+ }
+
+ private:
+ int next_buffer_check_; // pc offset of next buffer check
+
+ // Code generation
+ // The relocation writer's position is at least kGap bytes below the end of
+ // the generated instructions. This is so that multi-instruction sequences do
+ // not have to check for overflow. The same is true for writes of large
+ // relocation info entries.
+ static const int kGap = 32;
+
+ // Constant pool generation
+ // Pools are emitted in the instruction stream, preferably after unconditional
+ // jumps or after returns from functions (in dead code locations).
+ // If a long code sequence does not contain unconditional jumps, it is
+ // necessary to emit the constant pool before the pool gets too far from the
+ // location it is accessed from. In this case, we emit a jump over the emitted
+ // constant pool.
+ // Constants in the pool may be addresses of functions that gets relocated;
+ // if so, a relocation info entry is associated to the constant pool entry.
+
+ // Repeated checking whether the constant pool should be emitted is rather
+ // expensive. By default we only check again once a number of instructions
+ // has been generated. That also means that the sizing of the buffers is not
+ // an exact science, and that we rely on some slop to not overrun buffers.
+ static const int kCheckPoolIntervalInst = 32;
+ static const int kCheckPoolInterval = kCheckPoolIntervalInst * kInstrSize;
+
+
+ // Emission of the constant pool may be blocked in some code sequences.
+ int const_pool_blocked_nesting_; // Block emission if this is not zero.
+ int no_const_pool_before_; // Block emission before this pc offset.
+
+ // Keep track of the first instruction requiring a constant pool entry
+ // since the previous constant pool was emitted.
+ int first_const_pool_use_;
+
+ // Relocation info generation
+ // Each relocation is encoded as a variable size value
+ static const int kMaxRelocSize = RelocInfoWriter::kMaxSize;
+ RelocInfoWriter reloc_info_writer;
+
+ // Relocation info records are also used during code generation as temporary
+ // containers for constants and code target addresses until they are emitted
+ // to the constant pool. These pending relocation info records are temporarily
+ // stored in a separate buffer until a constant pool is emitted.
+ // If every instruction in a long sequence is accessing the pool, we need one
+ // pending relocation entry per instruction.
+
+ // the buffer of pending relocation info
+ RelocInfo pending_reloc_info_[kMaxNumPendingRelocInfo];
+ // number of pending reloc info entries in the buffer
+ int num_pending_reloc_info_;
+ // Number of pending reloc info entries included above which also happen to
+ // be 64-bit.
+ int num_pending_64_bit_reloc_info_;
+
+ // The bound position, before this we cannot do instruction elimination.
+ int last_bound_pos_;
+
+ // Code emission
+ inline void CheckBuffer();
+ void GrowBuffer();
+ inline void emit(Instr x);
+
+ // 32-bit immediate values
+ void move_32_bit_immediate(Condition cond,
+ Register rd,
+ SBit s,
+ const Operand& x);
+
+ // Instruction generation
+ void addrmod1(Instr instr, Register rn, Register rd, const Operand& x);
+ void addrmod2(Instr instr, Register rd, const MemOperand& x);
+ void addrmod3(Instr instr, Register rd, const MemOperand& x);
+ void addrmod4(Instr instr, Register rn, RegList rl);
+ void addrmod5(Instr instr, CRegister crd, const MemOperand& x);
+
+ // Labels
+ void print(Label* L);
+ void bind_to(Label* L, int pos);
+ void next(Label* L);
+
+ enum UseConstantPoolMode {
+ USE_CONSTANT_POOL,
+ DONT_USE_CONSTANT_POOL
+ };
+
+ // Record reloc info for current pc_
+ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0,
+ UseConstantPoolMode mode = USE_CONSTANT_POOL);
+ void RecordRelocInfo(double data);
+ void RecordRelocInfoConstantPoolEntryHelper(const RelocInfo& rinfo);
+
+ friend class RegExpMacroAssemblerARM;
+ friend class RelocInfo;
+ friend class CodePatcher;
+ friend class BlockConstPoolScope;
+
+ PositionsRecorder positions_recorder_;
+ friend class PositionsRecorder;
+ friend class EnsureSpace;
+};
+
+
+class EnsureSpace BASE_EMBEDDED {
+ public:
+ explicit EnsureSpace(Assembler* assembler) {
+ assembler->CheckBuffer();
+ }
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_ASSEMBLER_ARM_H_
diff --git a/chromium/v8/src/arm/builtins-arm.cc b/chromium/v8/src/arm/builtins-arm.cc
new file mode 100644
index 00000000000..5f3a999f561
--- /dev/null
+++ b/chromium/v8/src/arm/builtins-arm.cc
@@ -0,0 +1,1484 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "codegen.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+#include "runtime.h"
+
+namespace v8 {
+namespace internal {
+
+
+#define __ ACCESS_MASM(masm)
+
+
+void Builtins::Generate_Adaptor(MacroAssembler* masm,
+ CFunctionId id,
+ BuiltinExtraArguments extra_args) {
+ // ----------- S t a t e -------------
+ // -- r0 : number of arguments excluding receiver
+ // -- r1 : called function (only guaranteed when
+ // extra_args requires it)
+ // -- cp : context
+ // -- sp[0] : last argument
+ // -- ...
+ // -- sp[4 * (argc - 1)] : first argument (argc == r0)
+ // -- sp[4 * argc] : receiver
+ // -----------------------------------
+
+ // Insert extra arguments.
+ int num_extra_args = 0;
+ if (extra_args == NEEDS_CALLED_FUNCTION) {
+ num_extra_args = 1;
+ __ push(r1);
+ } else {
+ ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
+ }
+
+ // JumpToExternalReference expects r0 to contain the number of arguments
+ // including the receiver and the extra arguments.
+ __ add(r0, r0, Operand(num_extra_args + 1));
+ __ JumpToExternalReference(ExternalReference(id, masm->isolate()));
+}
+
+
+// Load the built-in InternalArray function from the current context.
+static void GenerateLoadInternalArrayFunction(MacroAssembler* masm,
+ Register result) {
+ // Load the native context.
+
+ __ ldr(result,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ ldr(result,
+ FieldMemOperand(result, GlobalObject::kNativeContextOffset));
+ // Load the InternalArray function from the native 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 native context.
+
+ __ ldr(result,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ ldr(result,
+ FieldMemOperand(result, GlobalObject::kNativeContextOffset));
+ // Load the Array function from the native context.
+ __ ldr(result,
+ MemOperand(result,
+ Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
+}
+
+
+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));
+ __ SmiTst(r2);
+ __ Assert(ne, kUnexpectedInitialMapForInternalArrayFunction);
+ __ CompareObjectType(r2, r3, r4, MAP_TYPE);
+ __ Assert(eq, kUnexpectedInitialMapForInternalArrayFunction);
+ }
+
+ // Run the native code for the InternalArray function called as a normal
+ // function.
+ // tail call a stub
+ InternalArrayConstructorStub stub(masm->isolate());
+ __ TailCallStub(&stub);
+}
+
+
+void Builtins::Generate_ArrayCode(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 Array function.
+ GenerateLoadArrayFunction(masm, r1);
+
+ if (FLAG_debug_code) {
+ // Initial map for the builtin Array functions should be maps.
+ __ ldr(r2, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset));
+ __ SmiTst(r2);
+ __ Assert(ne, kUnexpectedInitialMapForArrayFunction);
+ __ CompareObjectType(r2, r3, r4, MAP_TYPE);
+ __ Assert(eq, kUnexpectedInitialMapForArrayFunction);
+ }
+
+ // Run the native code for the Array function called as a normal function.
+ // tail call a stub
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(),
+ masm->isolate());
+ __ mov(r2, Operand(undefined_sentinel));
+ ArrayConstructorStub stub(masm->isolate());
+ __ TailCallStub(&stub);
+}
+
+
+void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r0 : number of arguments
+ // -- r1 : constructor function
+ // -- lr : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero based)
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->string_ctor_calls(), 1, r2, r3);
+
+ Register function = r1;
+ if (FLAG_debug_code) {
+ __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, r2);
+ __ cmp(function, Operand(r2));
+ __ Assert(eq, kUnexpectedStringFunction);
+ }
+
+ // Load the first arguments in r0 and get rid of the rest.
+ Label no_arguments;
+ __ cmp(r0, Operand::Zero());
+ __ b(eq, &no_arguments);
+ // First args = sp[(argc - 1) * 4].
+ __ sub(r0, r0, Operand(1));
+ __ ldr(r0, MemOperand(sp, r0, LSL, kPointerSizeLog2, PreIndex));
+ // sp now point to args[0], drop args[0] + receiver.
+ __ Drop(2);
+
+ Register argument = r2;
+ Label not_cached, argument_is_string;
+ NumberToStringStub::GenerateLookupNumberStringCache(
+ masm,
+ r0, // Input.
+ argument, // Result.
+ r3, // Scratch.
+ r4, // Scratch.
+ r5, // Scratch.
+ &not_cached);
+ __ IncrementCounter(counters->string_ctor_cached_number(), 1, r3, r4);
+ __ bind(&argument_is_string);
+
+ // ----------- S t a t e -------------
+ // -- r2 : argument converted to string
+ // -- r1 : constructor function
+ // -- lr : return address
+ // -----------------------------------
+
+ Label gc_required;
+ __ Allocate(JSValue::kSize,
+ r0, // Result.
+ r3, // Scratch.
+ r4, // Scratch.
+ &gc_required,
+ TAG_OBJECT);
+
+ // Initialising the String Object.
+ Register map = r3;
+ __ LoadGlobalFunctionInitialMap(function, map, r4);
+ if (FLAG_debug_code) {
+ __ ldrb(r4, FieldMemOperand(map, Map::kInstanceSizeOffset));
+ __ cmp(r4, Operand(JSValue::kSize >> kPointerSizeLog2));
+ __ Assert(eq, kUnexpectedStringWrapperInstanceSize);
+ __ ldrb(r4, FieldMemOperand(map, Map::kUnusedPropertyFieldsOffset));
+ __ cmp(r4, Operand::Zero());
+ __ Assert(eq, kUnexpectedUnusedPropertiesOfStringWrapper);
+ }
+ __ str(map, FieldMemOperand(r0, HeapObject::kMapOffset));
+
+ __ LoadRoot(r3, Heap::kEmptyFixedArrayRootIndex);
+ __ str(r3, FieldMemOperand(r0, JSObject::kPropertiesOffset));
+ __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
+
+ __ str(argument, FieldMemOperand(r0, JSValue::kValueOffset));
+
+ // Ensure the object is fully initialized.
+ STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
+
+ __ Ret();
+
+ // The argument was not found in the number to string cache. Check
+ // if it's a string already before calling the conversion builtin.
+ Label convert_argument;
+ __ bind(&not_cached);
+ __ JumpIfSmi(r0, &convert_argument);
+
+ // Is it a String?
+ __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ tst(r3, Operand(kIsNotStringMask));
+ __ b(ne, &convert_argument);
+ __ mov(argument, r0);
+ __ IncrementCounter(counters->string_ctor_conversions(), 1, r3, r4);
+ __ b(&argument_is_string);
+
+ // Invoke the conversion builtin and put the result into r2.
+ __ bind(&convert_argument);
+ __ push(function); // Preserve the function.
+ __ IncrementCounter(counters->string_ctor_conversions(), 1, r3, r4);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(r0);
+ __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
+ }
+ __ pop(function);
+ __ mov(argument, r0);
+ __ b(&argument_is_string);
+
+ // Load the empty string into r2, remove the receiver from the
+ // stack, and jump back to the case where the argument is a string.
+ __ bind(&no_arguments);
+ __ LoadRoot(argument, Heap::kempty_stringRootIndex);
+ __ Drop(1);
+ __ b(&argument_is_string);
+
+ // At this point the argument is already a string. Call runtime to
+ // create a string wrapper.
+ __ bind(&gc_required);
+ __ IncrementCounter(counters->string_ctor_gc_required(), 1, r3, r4);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(argument);
+ __ CallRuntime(Runtime::kNewStringWrapper, 1);
+ }
+ __ Ret();
+}
+
+
+static void GenerateTailCallToSharedCode(MacroAssembler* masm) {
+ __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kCodeOffset));
+ __ add(r2, r2, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ mov(pc, r2);
+}
+
+
+void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) {
+ GenerateTailCallToSharedCode(masm);
+}
+
+
+void Builtins::Generate_InstallRecompiledCode(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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::kInstallRecompiledCode, 1);
+ // Calculate the entry point.
+ __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Restore call kind information.
+ __ pop(r5);
+ // Restore saved function.
+ __ pop(r1);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ Jump(r2);
+}
+
+
+void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Push a copy of the function onto the stack.
+ __ push(r1);
+ // Push call kind information.
+ __ push(r5);
+
+ __ push(r1); // Function is also the parameter to the runtime call.
+ __ CallRuntime(Runtime::kParallelRecompile, 1);
+
+ // Restore call kind information.
+ __ pop(r5);
+ // Restore receiver.
+ __ pop(r1);
+
+ // Tear down internal frame.
+ }
+
+ GenerateTailCallToSharedCode(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
+ // -- lr : return address
+ // -- sp[...]: constructor arguments
+ // -----------------------------------
+
+ // Should never count constructions for api objects.
+ ASSERT(!is_api_function || !count_constructions);
+
+ Isolate* isolate = masm->isolate();
+
+ // Enter a construct frame.
+ {
+ FrameScope scope(masm, StackFrame::CONSTRUCT);
+
+ // Preserve the two incoming parameters on the stack.
+ __ SmiTag(r0);
+ __ 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);
+#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);
+ }
+
+ // Now allocate the JSObject on the heap.
+ // r1: constructor function
+ // r2: initial map
+ __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset));
+ __ Allocate(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, kUnexpectedNumberOfPreAllocatedPropertyFields);
+ }
+ __ InitializeFieldsWithFiller(r5, r0, r7);
+ // To allow for truncation.
+ __ LoadRoot(r7, Heap::kOnePointerFillerMapRootIndex);
+ }
+ __ 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, kPropertyAllocationCountFailed);
+
+ // 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));
+ __ Allocate(
+ 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);
+ __ SmiTag(r0, r3);
+ __ 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, kUndefinedValueNotLoaded);
+ }
+ __ b(&entry);
+ __ bind(&loop);
+ __ str(r7, MemOperand(r2, kPointerSize, PostIndex));
+ __ bind(&entry);
+ __ cmp(r2, r6);
+ __ b(lt, &loop);
+ }
+
+ // 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);
+ }
+
+ // 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(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
+ __ SmiUntag(r0, r3);
+
+ // Copy arguments and receiver to the expression stack.
+ // r0: number of arguments
+ // r1: constructor function
+ // 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);
+
+ // 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);
+ }
+
+ // Store offset of return address for deoptimizer.
+ if (!is_api_function && !count_constructions) {
+ masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // 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, r1, 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.
+ }
+
+ __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
+ __ add(sp, sp, Operand(kPointerSize));
+ __ IncrementCounter(isolate->counters()->constructed_objects(), 1, r1, r2);
+ __ Jump(lr);
+}
+
+
+void Builtins::Generate_JSConstructStubCountdown(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, true);
+}
+
+
+void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, false);
+}
+
+
+void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, true, false);
+}
+
+
+static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
+ bool is_construct) {
+ // Called from Generate_JS_Entry
+ // r0: code entry
+ // r1: function
+ // r2: receiver
+ // r3: argc
+ // r4: argv
+ // r5-r7, cp may be clobbered
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Clear the context before we push it when entering the internal frame.
+ __ mov(cp, Operand::Zero());
+
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Set up the context from the function argument.
+ __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+
+ __ InitializeRootRegister();
+
+ // 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));
+ }
+
+ // Invoke the code and pass argc as r0.
+ __ mov(r0, Operand(r3));
+ if (is_construct) {
+ // No type feedback cell is available
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(), masm->isolate());
+ __ mov(r2, Operand(undefined_sentinel));
+ 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
+}
+
+
+void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
+ Generate_JSEntryTrampolineHelper(masm, false);
+}
+
+
+void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
+ Generate_JSEntryTrampolineHelper(masm, true);
+}
+
+
+void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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));
+
+ // Restore call kind information.
+ __ pop(r5);
+ // Restore saved function.
+ __ pop(r1);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ Jump(r2);
+}
+
+
+void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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));
+
+ // Restore call kind information.
+ __ pop(r5);
+ // Restore saved function.
+ __ pop(r1);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ Jump(r2);
+}
+
+
+static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) {
+ // For now, we are relying on the fact that make_code_young doesn't do any
+ // garbage collection which allows us to save/restore the registers without
+ // worrying about which of them contain pointers. We also don't build an
+ // internal frame to make the code faster, since we shouldn't have to do stack
+ // crawls in MakeCodeYoung. This seems a bit fragile.
+
+ // The following registers must be saved and restored when calling through to
+ // the runtime:
+ // r0 - contains return address (beginning of patch sequence)
+ // r1 - function object
+ FrameScope scope(masm, StackFrame::MANUAL);
+ __ stm(db_w, sp, r0.bit() | r1.bit() | fp.bit() | lr.bit());
+ __ PrepareCallCFunction(1, 0, r1);
+ __ CallCFunction(
+ ExternalReference::get_make_code_young_function(masm->isolate()), 1);
+ __ ldm(ia_w, sp, r0.bit() | r1.bit() | fp.bit() | lr.bit());
+ __ mov(pc, r0);
+}
+
+#define DEFINE_CODE_AGE_BUILTIN_GENERATOR(C) \
+void Builtins::Generate_Make##C##CodeYoungAgainEvenMarking( \
+ MacroAssembler* masm) { \
+ GenerateMakeCodeYoungAgainCommon(masm); \
+} \
+void Builtins::Generate_Make##C##CodeYoungAgainOddMarking( \
+ MacroAssembler* masm) { \
+ GenerateMakeCodeYoungAgainCommon(masm); \
+}
+CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR)
+#undef DEFINE_CODE_AGE_BUILTIN_GENERATOR
+
+
+void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Preserve registers across notification, this is important for compiled
+ // stubs that tail call the runtime on deopts passing their parameters in
+ // registers.
+ __ stm(db_w, sp, kJSCallerSaved | kCalleeSaved);
+ // Pass the function and deoptimization type to the runtime system.
+ __ CallRuntime(Runtime::kNotifyStubFailure, 0);
+ __ ldm(ia_w, sp, kJSCallerSaved | kCalleeSaved);
+ }
+
+ __ add(sp, sp, Operand(kPointerSize)); // Ignore state
+ __ mov(pc, lr); // Jump to miss handler
+}
+
+
+static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
+ Deoptimizer::BailoutType type) {
+ {
+ 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));
+ __ SmiUntag(r6);
+ // Switch on the state.
+ Label with_tos_register, unknown_state;
+ __ cmp(r6, Operand(FullCodeGenerator::NO_REGISTERS));
+ __ b(ne, &with_tos_register);
+ __ add(sp, sp, Operand(1 * kPointerSize)); // Remove state.
+ __ Ret();
+
+ __ bind(&with_tos_register);
+ __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
+ __ cmp(r6, Operand(FullCodeGenerator::TOS_REG));
+ __ b(ne, &unknown_state);
+ __ add(sp, sp, Operand(2 * kPointerSize)); // Remove state.
+ __ Ret();
+
+ __ bind(&unknown_state);
+ __ stop("no cases left");
+}
+
+
+void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
+}
+
+
+void Builtins::Generate_NotifySoftDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
+}
+
+
+void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
+}
+
+
+void Builtins::Generate_NotifyOSR(MacroAssembler* masm) {
+ // 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.
+ __ stm(db_w, sp, kJSCallerSaved | kCalleeSaved | lr.bit() | fp.bit());
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ CallRuntime(Runtime::kNotifyOSR, 0);
+ }
+ __ ldm(ia_w, sp, kJSCallerSaved | kCalleeSaved | lr.bit() | fp.bit());
+ __ Ret();
+}
+
+
+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));
+ {
+ 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.
+ Label skip;
+ __ cmp(r0, Operand(Smi::FromInt(-1)));
+ __ b(ne, &skip);
+ __ Ret();
+
+ __ bind(&skip);
+ // Untag the AST id and push it on the stack.
+ __ SmiUntag(r0);
+ __ push(r0);
+
+ // Generate the code for doing the frame-to-frame translation using
+ // the deoptimizer infrastructure.
+ Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR);
+ generator.Generate();
+}
+
+
+void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
+ // 1. Make sure we have at least one argument.
+ // r0: actual number of arguments
+ { Label done;
+ __ cmp(r0, Operand::Zero());
+ __ b(ne, &done);
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ push(r2);
+ __ add(r0, r0, Operand(1));
+ __ bind(&done);
+ }
+
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ // r0: actual number of arguments
+ Label slow, non_function;
+ __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
+ __ JumpIfSmi(r1, &non_function);
+ __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
+ __ b(ne, &slow);
+
+ // 3a. Patch the first argument if necessary when calling a function.
+ // r0: actual number of arguments
+ // r1: function
+ Label shift_arguments;
+ __ mov(r4, Operand::Zero()); // indicate regular JS_FUNCTION
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+
+ // Do not transform the receiver for strict mode functions.
+ __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r3, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset));
+ __ tst(r3, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
+ kSmiTagSize)));
+ __ b(ne, &shift_arguments);
+
+ // Do not transform the receiver for native (Compilerhints already in r3).
+ __ tst(r3, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize)));
+ __ b(ne, &shift_arguments);
+
+ // Compute the receiver in non-strict mode.
+ __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
+ __ ldr(r2, MemOperand(r2, -kPointerSize));
+ // r0: actual number of arguments
+ // r1: function
+ // r2: first argument
+ __ JumpIfSmi(r2, &convert_to_object);
+
+ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
+ __ cmp(r2, r3);
+ __ b(eq, &use_global_receiver);
+ __ LoadRoot(r3, Heap::kNullValueRootIndex);
+ __ cmp(r2, r3);
+ __ b(eq, &use_global_receiver);
+
+ STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
+ __ CompareObjectType(r2, r3, r3, FIRST_SPEC_OBJECT_TYPE);
+ __ b(ge, &shift_arguments);
+
+ __ bind(&convert_to_object);
+
+ {
+ // Enter an internal frame in order to preserve argument count.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ SmiTag(r0);
+ __ push(r0);
+
+ __ push(r2);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ mov(r2, r0);
+
+ __ pop(r0);
+ __ SmiUntag(r0);
+
+ // Exit the internal frame.
+ }
+
+ // Restore the function to r1, and the flag to r4.
+ __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
+ __ mov(r4, Operand::Zero());
+ __ jmp(&patch_receiver);
+
+ // Use the global receiver object from the called function as the
+ // receiver.
+ __ bind(&use_global_receiver);
+ const int kGlobalIndex =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ __ ldr(r2, FieldMemOperand(cp, kGlobalIndex));
+ __ ldr(r2, FieldMemOperand(r2, GlobalObject::kNativeContextOffset));
+ __ ldr(r2, FieldMemOperand(r2, kGlobalIndex));
+ __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
+
+ __ bind(&patch_receiver);
+ __ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
+ __ str(r2, MemOperand(r3, -kPointerSize));
+
+ __ jmp(&shift_arguments);
+ }
+
+ // 3b. Check for function proxy.
+ __ bind(&slow);
+ __ mov(r4, Operand(1, RelocInfo::NONE32)); // indicate function proxy
+ __ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE));
+ __ b(eq, &shift_arguments);
+ __ bind(&non_function);
+ __ mov(r4, Operand(2, RelocInfo::NONE32)); // 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.
+ // r0: actual number of arguments
+ // r1: function
+ // r4: call type (0: JS function, 1: function proxy, 2: non-function)
+ __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
+ __ str(r1, MemOperand(r2, -kPointerSize));
+
+ // 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.
+ // r0: actual number of arguments
+ // r1: function
+ // r4: 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.
+ __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
+
+ __ bind(&loop);
+ __ ldr(ip, MemOperand(r2, -kPointerSize));
+ __ str(ip, MemOperand(r2));
+ __ sub(r2, r2, Operand(kPointerSize));
+ __ cmp(r2, sp);
+ __ b(ne, &loop);
+ // Adjust the actual number of arguments and remove the top element
+ // (which is a copy of the last argument).
+ __ sub(r0, r0, Operand(1));
+ __ pop();
+ }
+
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
+ // or a function proxy via CALL_FUNCTION_PROXY.
+ // r0: actual number of arguments
+ // r1: function
+ // r4: call type (0: JS function, 1: function proxy, 2: non-function)
+ { Label function, non_proxy;
+ __ tst(r4, r4);
+ __ b(eq, &function);
+ // Expected number of arguments is 0 for CALL_NON_FUNCTION.
+ __ mov(r2, Operand::Zero());
+ __ SetCallKind(r5, CALL_AS_METHOD);
+ __ cmp(r4, Operand(1));
+ __ b(ne, &non_proxy);
+
+ __ push(r1); // re-add proxy object as additional argument
+ __ add(r0, r0, Operand(1));
+ __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ __ bind(&non_proxy);
+ __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+ __ bind(&function);
+ }
+
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ // r0: actual number of arguments
+ // r1: function
+ __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r2,
+ FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ SmiUntag(r2);
+ __ ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
+ __ SetCallKind(r5, CALL_AS_METHOD);
+ __ cmp(r2, r0); // Check formal and actual parameter counts.
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET,
+ ne);
+
+ ParameterCount expected(0);
+ __ InvokeCode(r3, expected, expected, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
+}
+
+
+void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
+ const int kIndexOffset = -5 * kPointerSize;
+ const int kLimitOffset = -4 * kPointerSize;
+ const int kArgsOffset = 2 * kPointerSize;
+ const int kRecvOffset = 3 * kPointerSize;
+ const int kFunctionOffset = 4 * kPointerSize;
+
+ {
+ 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::PointerOffsetFromSmiKey(r0));
+ __ 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::Zero()); // initial index
+ __ push(r1);
+
+ // 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_OBJECT_INDEX * kPointerSize;
+ __ ldr(r0, FieldMemOperand(cp, kGlobalOffset));
+ __ ldr(r0, FieldMemOperand(r0, GlobalObject::kNativeContextOffset));
+ __ 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);
+ __ SmiUntag(r0);
+ __ 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);
+
+ // Invoke the function proxy.
+ __ bind(&call_proxy);
+ __ push(r1); // add function proxy as last argument
+ __ add(r0, r0, Operand(1));
+ __ mov(r2, Operand::Zero());
+ __ 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);
+}
+
+
+static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
+ __ SmiTag(r0);
+ __ mov(r4, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ stm(db_w, sp, r0.bit() | r1.bit() | r4.bit() | fp.bit() | lr.bit());
+ __ add(fp, sp, Operand(3 * kPointerSize));
+}
+
+
+static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r0 : result being passed through
+ // -----------------------------------
+ // Get the number of arguments passed (as a smi), tear down the frame and
+ // then tear down the parameters.
+ __ ldr(r1, MemOperand(fp, -3 * kPointerSize));
+ __ mov(sp, fp);
+ __ ldm(ia_w, sp, fp.bit() | lr.bit());
+ __ add(sp, sp, Operand::PointerOffsetFromSmiKey(r1));
+ __ add(sp, sp, Operand(kPointerSize)); // adjust for receiver
+}
+
+
+void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r0 : actual number of arguments
+ // -- r1 : function (passed through to callee)
+ // -- r2 : expected number of arguments
+ // -- r3 : code entry to call
+ // -- r5 : call kind information
+ // -----------------------------------
+
+ Label invoke, dont_adapt_arguments;
+
+ Label enough, too_few;
+ __ cmp(r0, r2);
+ __ b(lt, &too_few);
+ __ cmp(r2, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
+ __ b(eq, &dont_adapt_arguments);
+
+ { // Enough parameters: actual >= expected
+ __ bind(&enough);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Calculate copy start address into r0 and copy end address into r2.
+ // r0: actual number of arguments as a smi
+ // r1: function
+ // r2: expected number of arguments
+ // r3: code entry to call
+ __ add(r0, fp, Operand::PointerOffsetFromSmiKey(r0));
+ // adjust for return address and receiver
+ __ add(r0, r0, Operand(2 * kPointerSize));
+ __ sub(r2, r0, Operand(r2, LSL, kPointerSizeLog2));
+
+ // Copy the arguments (including the receiver) to the new stack frame.
+ // r0: copy start address
+ // r1: function
+ // r2: copy end address
+ // r3: code entry to call
+
+ Label copy;
+ __ bind(&copy);
+ __ ldr(ip, MemOperand(r0, 0));
+ __ push(ip);
+ __ cmp(r0, r2); // Compare before moving to next argument.
+ __ sub(r0, r0, Operand(kPointerSize));
+ __ b(ne, &copy);
+
+ __ b(&invoke);
+ }
+
+ { // Too few parameters: Actual < expected
+ __ bind(&too_few);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Calculate copy start address into r0 and copy end address is fp.
+ // r0: actual number of arguments as a smi
+ // r1: function
+ // r2: expected number of arguments
+ // r3: code entry to call
+ __ add(r0, fp, Operand::PointerOffsetFromSmiKey(r0));
+
+ // Copy the arguments (including the receiver) to the new stack frame.
+ // r0: copy start address
+ // r1: function
+ // r2: expected number of arguments
+ // r3: code entry to call
+ Label copy;
+ __ bind(&copy);
+ // Adjust load for return address and receiver.
+ __ ldr(ip, MemOperand(r0, 2 * kPointerSize));
+ __ push(ip);
+ __ cmp(r0, fp); // Compare before moving to next argument.
+ __ sub(r0, r0, Operand(kPointerSize));
+ __ b(ne, &copy);
+
+ // Fill the remaining expected arguments with undefined.
+ // r1: function
+ // r2: expected number of arguments
+ // r3: code entry to call
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ sub(r2, fp, Operand(r2, LSL, kPointerSizeLog2));
+ __ sub(r2, r2, Operand(4 * kPointerSize)); // Adjust for frame.
+
+ Label fill;
+ __ bind(&fill);
+ __ push(ip);
+ __ cmp(sp, r2);
+ __ b(ne, &fill);
+ }
+
+ // Call the entry point.
+ __ bind(&invoke);
+ __ Call(r3);
+
+ // Store offset of return address for deoptimizer.
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
+
+ // Exit frame and return.
+ LeaveArgumentsAdaptorFrame(masm);
+ __ Jump(lr);
+
+
+ // -------------------------------------------
+ // Dont adapt arguments.
+ // -------------------------------------------
+ __ bind(&dont_adapt_arguments);
+ __ Jump(r3);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/code-stubs-arm.cc b/chromium/v8/src/arm/code-stubs-arm.cc
new file mode 100644
index 00000000000..98a835fd1a5
--- /dev/null
+++ b/chromium/v8/src/arm/code-stubs-arm.cc
@@ -0,0 +1,7172 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "bootstrapper.h"
+#include "code-stubs.h"
+#include "regexp-macro-assembler.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+
+void ToNumberStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void FastCloneShallowArrayStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r3, r2, r1 };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kCreateArrayLiteralShallow)->entry;
+}
+
+
+void FastCloneShallowObjectStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r3, r2, r1, r0 };
+ descriptor->register_param_count_ = 4;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kCreateObjectLiteralShallow)->entry;
+}
+
+
+void CreateAllocationSiteStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r2 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedLoadFastElementStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r1, r0 };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure);
+}
+
+
+void LoadFieldStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedLoadFieldStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r1 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedStoreFastElementStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r2, r1, r0 };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure);
+}
+
+
+void TransitionElementsKindStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r0, r1 };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ Address entry =
+ Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry;
+ descriptor->deoptimization_handler_ = FUNCTION_ADDR(entry);
+}
+
+
+void CompareNilICStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(CompareNilIC_Miss);
+ descriptor->SetMissHandler(
+ ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate));
+}
+
+
+static void InitializeArrayConstructorDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor,
+ int constant_stack_parameter_count) {
+ // register state
+ // r0 -- number of arguments
+ // r1 -- function
+ // r2 -- type info cell with elements kind
+ static Register registers[] = { r1, r2 };
+ descriptor->register_param_count_ = 2;
+ if (constant_stack_parameter_count != 0) {
+ // stack param count needs (constructor pointer, and single argument)
+ descriptor->stack_parameter_count_ = &r0;
+ }
+ descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
+ descriptor->register_params_ = registers;
+ descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
+}
+
+
+static void InitializeInternalArrayConstructorDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor,
+ int constant_stack_parameter_count) {
+ // register state
+ // r0 -- number of arguments
+ // r1 -- constructor function
+ static Register registers[] = { r1 };
+ descriptor->register_param_count_ = 1;
+
+ if (constant_stack_parameter_count != 0) {
+ // stack param count needs (constructor pointer, and single argument)
+ descriptor->stack_parameter_count_ = &r0;
+ }
+ descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
+ descriptor->register_params_ = registers;
+ descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
+}
+
+
+void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, 0);
+}
+
+
+void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, 1);
+}
+
+
+void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, -1);
+}
+
+
+void ToBooleanStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(ToBooleanIC_Miss);
+ descriptor->SetMissHandler(
+ ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate));
+}
+
+
+void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0);
+}
+
+
+void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1);
+}
+
+
+void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1);
+}
+
+
+void StoreGlobalStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r1, r2, r0 };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(StoreIC_MissFromStubFailure);
+}
+
+
+void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { r0, r3, r1, r2 };
+ descriptor->register_param_count_ = 4;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss);
+}
+
+
+#define __ ACCESS_MASM(masm)
+
+
+static void EmitIdenticalObjectComparison(MacroAssembler* masm,
+ Label* slow,
+ Condition cond);
+static void EmitSmiNonsmiComparison(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* lhs_not_nan,
+ Label* slow,
+ bool strict);
+static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
+ Register lhs,
+ Register rhs);
+
+
+void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
+ // Update the static counter each time a new code stub is generated.
+ Isolate* isolate = masm->isolate();
+ isolate->counters()->code_stubs()->Increment();
+
+ CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate);
+ int param_count = descriptor->register_param_count_;
+ {
+ // Call the runtime system in a fresh internal frame.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ ASSERT(descriptor->register_param_count_ == 0 ||
+ r0.is(descriptor->register_params_[param_count - 1]));
+ // Push arguments
+ for (int i = 0; i < param_count; ++i) {
+ __ push(descriptor->register_params_[i]);
+ }
+ ExternalReference miss = descriptor->miss_handler();
+ __ CallExternalReference(miss, descriptor->register_param_count_);
+ }
+
+ __ Ret();
+}
+
+
+void FastNewClosureStub::Generate(MacroAssembler* masm) {
+ // Create a new closure from the given function info in new
+ // space. Set the context to the current context in cp.
+ Counters* counters = masm->isolate()->counters();
+
+ Label gc;
+
+ // Pop the function info from the stack.
+ __ pop(r3);
+
+ // Attempt to allocate new JSFunction in new space.
+ __ Allocate(JSFunction::kSize, r0, r1, r2, &gc, TAG_OBJECT);
+
+ __ IncrementCounter(counters->fast_new_closure_total(), 1, r6, r7);
+
+ int map_index = Context::FunctionMapIndex(language_mode_, is_generator_);
+
+ // Compute the function map in the current native context and set that
+ // as the map of the allocated object.
+ __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ ldr(r2, FieldMemOperand(r2, GlobalObject::kNativeContextOffset));
+ __ ldr(r5, MemOperand(r2, Context::SlotOffset(map_index)));
+ __ str(r5, FieldMemOperand(r0, HeapObject::kMapOffset));
+
+ // Initialize the rest of the function. We don't have to update the
+ // write barrier because the allocated object is in new space.
+ __ LoadRoot(r1, Heap::kEmptyFixedArrayRootIndex);
+ __ LoadRoot(r5, Heap::kTheHoleValueRootIndex);
+ __ str(r1, FieldMemOperand(r0, JSObject::kPropertiesOffset));
+ __ str(r1, FieldMemOperand(r0, JSObject::kElementsOffset));
+ __ str(r5, FieldMemOperand(r0, JSFunction::kPrototypeOrInitialMapOffset));
+ __ str(r3, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
+ __ str(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
+ __ str(r1, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
+
+ // Initialize the code pointer in the function to be the one
+ // found in the shared function info object.
+ // But first check if there is an optimized version for our context.
+ Label check_optimized;
+ Label install_unoptimized;
+ if (FLAG_cache_optimized_code) {
+ __ ldr(r1,
+ FieldMemOperand(r3, SharedFunctionInfo::kOptimizedCodeMapOffset));
+ __ tst(r1, r1);
+ __ b(ne, &check_optimized);
+ }
+ __ bind(&install_unoptimized);
+ __ LoadRoot(r4, Heap::kUndefinedValueRootIndex);
+ __ str(r4, FieldMemOperand(r0, JSFunction::kNextFunctionLinkOffset));
+ __ ldr(r3, FieldMemOperand(r3, SharedFunctionInfo::kCodeOffset));
+ __ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ str(r3, FieldMemOperand(r0, JSFunction::kCodeEntryOffset));
+
+ // Return result. The argument function info has been popped already.
+ __ Ret();
+
+ __ bind(&check_optimized);
+
+ __ IncrementCounter(counters->fast_new_closure_try_optimized(), 1, r6, r7);
+
+ // r2 holds native context, r1 points to fixed array of 3-element entries
+ // (native context, optimized code, literals).
+ // The optimized code map must never be empty, so check the first elements.
+ Label install_optimized;
+ // Speculatively move code object into r4.
+ __ ldr(r4, FieldMemOperand(r1, SharedFunctionInfo::kFirstCodeSlot));
+ __ ldr(r5, FieldMemOperand(r1, SharedFunctionInfo::kFirstContextSlot));
+ __ cmp(r2, r5);
+ __ b(eq, &install_optimized);
+
+ // Iterate through the rest of map backwards. r4 holds an index as a Smi.
+ Label loop;
+ __ ldr(r4, FieldMemOperand(r1, FixedArray::kLengthOffset));
+ __ bind(&loop);
+ // Do not double check first entry.
+ __ cmp(r4, Operand(Smi::FromInt(SharedFunctionInfo::kSecondEntryIndex)));
+ __ b(eq, &install_unoptimized);
+ __ sub(r4, r4, Operand(Smi::FromInt(SharedFunctionInfo::kEntryLength)));
+ __ add(r5, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ add(r5, r5, Operand::PointerOffsetFromSmiKey(r4));
+ __ ldr(r5, MemOperand(r5));
+ __ cmp(r2, r5);
+ __ b(ne, &loop);
+ // Hit: fetch the optimized code.
+ __ add(r5, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ add(r5, r5, Operand::PointerOffsetFromSmiKey(r4));
+ __ add(r5, r5, Operand(kPointerSize));
+ __ ldr(r4, MemOperand(r5));
+
+ __ bind(&install_optimized);
+ __ IncrementCounter(counters->fast_new_closure_install_optimized(),
+ 1, r6, r7);
+
+ // TODO(fschneider): Idea: store proper code pointers in the map and either
+ // unmangle them on marking or do nothing as the whole map is discarded on
+ // major GC anyway.
+ __ add(r4, r4, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ str(r4, FieldMemOperand(r0, JSFunction::kCodeEntryOffset));
+
+ // Now link a function into a list of optimized functions.
+ __ ldr(r4, ContextOperand(r2, Context::OPTIMIZED_FUNCTIONS_LIST));
+
+ __ str(r4, FieldMemOperand(r0, JSFunction::kNextFunctionLinkOffset));
+ // No need for write barrier as JSFunction (eax) is in the new space.
+
+ __ str(r0, ContextOperand(r2, Context::OPTIMIZED_FUNCTIONS_LIST));
+ // Store JSFunction (eax) into edx before issuing write barrier as
+ // it clobbers all the registers passed.
+ __ mov(r4, r0);
+ __ RecordWriteContextSlot(
+ r2,
+ Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST),
+ r4,
+ r1,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs);
+
+ // Return result. The argument function info has been popped already.
+ __ Ret();
+
+ // Create a new closure through the slower runtime call.
+ __ bind(&gc);
+ __ LoadRoot(r4, Heap::kFalseValueRootIndex);
+ __ Push(cp, r3, r4);
+ __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
+}
+
+
+void FastNewContextStub::Generate(MacroAssembler* masm) {
+ // Try to allocate the context in new space.
+ Label gc;
+ int length = slots_ + Context::MIN_CONTEXT_SLOTS;
+
+ // Attempt to allocate the context in new space.
+ __ Allocate(FixedArray::SizeFor(length), r0, r1, r2, &gc, TAG_OBJECT);
+
+ // Load the function from the stack.
+ __ ldr(r3, MemOperand(sp, 0));
+
+ // 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));
+
+ // Set up the fixed slots, copy the global object from the previous context.
+ __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_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)));
+ __ str(r2, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+
+ // Initialize the rest of the slots to undefined.
+ __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
+ for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
+ __ str(r1, MemOperand(r0, Context::SlotOffset(i)));
+ }
+
+ // Remove the on-stack argument and return.
+ __ mov(cp, r0);
+ __ pop();
+ __ Ret();
+
+ // Need to collect. Call into runtime system.
+ __ bind(&gc);
+ __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
+}
+
+
+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;
+ __ Allocate(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 native context we get a smi
+ // sentinel instead of a function. The block context should get the
+ // canonical empty function of the native context as its closure which
+ // we still have to look up.
+ Label after_sentinel;
+ __ JumpIfNotSmi(r3, &after_sentinel);
+ if (FLAG_debug_code) {
+ __ cmp(r3, Operand::Zero());
+ __ Assert(eq, kExpected0AsASmiSentinel);
+ }
+ __ ldr(r3, GlobalObjectOperand());
+ __ ldr(r3, FieldMemOperand(r3, GlobalObject::kNativeContextOffset));
+ __ 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_OBJECT_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_OBJECT_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);
+}
+
+
+// Takes a Smi and converts to an IEEE 64 bit floating point value in two
+// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
+// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
+// scratch register. Destroys the source register. No GC occurs during this
+// stub so you don't have to set up the frame.
+class ConvertToDoubleStub : public PlatformCodeStub {
+ public:
+ ConvertToDoubleStub(Register result_reg_1,
+ Register result_reg_2,
+ Register source_reg,
+ Register scratch_reg)
+ : result1_(result_reg_1),
+ result2_(result_reg_2),
+ source_(source_reg),
+ zeros_(scratch_reg) { }
+
+ private:
+ Register result1_;
+ Register result2_;
+ Register source_;
+ Register zeros_;
+
+ // Minor key encoding in 16 bits.
+ class ModeBits: public BitField<OverwriteMode, 0, 2> {};
+ class OpBits: public BitField<Token::Value, 2, 14> {};
+
+ Major MajorKey() { return ConvertToDouble; }
+ int MinorKey() {
+ // Encode the parameters in a unique 16 bit value.
+ return result1_.code() +
+ (result2_.code() << 4) +
+ (source_.code() << 8) +
+ (zeros_.code() << 12);
+ }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
+ Register exponent = result1_;
+ Register mantissa = result2_;
+
+ Label not_special;
+ __ SmiUntag(source_);
+ // Move sign bit from source to destination. This works because the sign bit
+ // in the exponent word of the double has the same position and polarity as
+ // the 2's complement sign bit in a Smi.
+ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
+ __ and_(exponent, source_, Operand(HeapNumber::kSignMask), SetCC);
+ // Subtract from 0 if source was negative.
+ __ rsb(source_, source_, Operand::Zero(), LeaveCC, ne);
+
+ // We have -1, 0 or 1, which we treat specially. Register source_ contains
+ // absolute value: it is either equal to 1 (special case of -1 and 1),
+ // greater than 1 (not a special case) or less than 1 (special case of 0).
+ __ cmp(source_, Operand(1));
+ __ b(gt, &not_special);
+
+ // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
+ const uint32_t exponent_word_for_1 =
+ HeapNumber::kExponentBias << HeapNumber::kExponentShift;
+ __ orr(exponent, exponent, Operand(exponent_word_for_1), LeaveCC, eq);
+ // 1, 0 and -1 all have 0 for the second word.
+ __ mov(mantissa, Operand::Zero());
+ __ Ret();
+
+ __ bind(&not_special);
+ __ clz(zeros_, source_);
+ // Compute exponent and or it into the exponent register.
+ // We use mantissa as a scratch register here. Use a fudge factor to
+ // divide the constant 31 + HeapNumber::kExponentBias, 0x41d, into two parts
+ // that fit in the ARM's constant field.
+ int fudge = 0x400;
+ __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias - fudge));
+ __ add(mantissa, mantissa, Operand(fudge));
+ __ orr(exponent,
+ exponent,
+ Operand(mantissa, LSL, HeapNumber::kExponentShift));
+ // Shift up the source chopping the top bit off.
+ __ add(zeros_, zeros_, Operand(1));
+ // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
+ __ mov(source_, Operand(source_, LSL, zeros_));
+ // Compute lower part of fraction (last 12 bits).
+ __ mov(mantissa, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord));
+ // And the top (top 20 bits).
+ __ orr(exponent,
+ exponent,
+ Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord));
+ __ Ret();
+}
+
+
+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(
+ Isolate* isolate) {
+ WriteInt32ToHeapNumberStub stub1(r1, r0, r2);
+ WriteInt32ToHeapNumberStub stub2(r2, r0, r3);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ stub2.GetCode(isolate)->set_is_pregenerated(true);
+}
+
+
+// See comment for class.
+void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
+ Label max_negative_int;
+ // the_int_ has the answer which is a signed int32 but not a Smi.
+ // We test for the special value that has a different exponent. This test
+ // has the neat side effect of setting the flags according to the sign.
+ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
+ __ cmp(the_int_, Operand(0x80000000u));
+ __ b(eq, &max_negative_int);
+ // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
+ // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
+ uint32_t non_smi_exponent =
+ (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
+ __ mov(scratch_, Operand(non_smi_exponent));
+ // Set the sign bit in scratch_ if the value was negative.
+ __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs);
+ // Subtract from 0 if the value was negative.
+ __ rsb(the_int_, the_int_, Operand::Zero(), LeaveCC, cs);
+ // We should be masking the implict first digit of the mantissa away here,
+ // but it just ends up combining harmlessly with the last digit of the
+ // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
+ // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
+ ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
+ const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
+ __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance));
+ __ str(scratch_, FieldMemOperand(the_heap_number_,
+ HeapNumber::kExponentOffset));
+ __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance));
+ __ str(scratch_, FieldMemOperand(the_heap_number_,
+ HeapNumber::kMantissaOffset));
+ __ Ret();
+
+ __ bind(&max_negative_int);
+ // The max negative int32 is stored as a positive number in the mantissa of
+ // a double because it uses a sign bit instead of using two's complement.
+ // The actual mantissa bits stored are all 0 because the implicit most
+ // significant 1 bit is not stored.
+ non_smi_exponent += 1 << HeapNumber::kExponentShift;
+ __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent));
+ __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
+ __ mov(ip, Operand::Zero());
+ __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
+ __ Ret();
+}
+
+
+// Handle the case where the lhs and rhs are the same object.
+// Equality is almost reflexive (everything but NaN), so this is a test
+// for "identity and not NaN".
+static void EmitIdenticalObjectComparison(MacroAssembler* masm,
+ Label* slow,
+ Condition cond) {
+ Label not_identical;
+ Label heap_number, return_equal;
+ __ cmp(r0, r1);
+ __ b(ne, &not_identical);
+
+ // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
+ // so we do the second best thing - test it ourselves.
+ // They are both equal and they are not both Smis so both of them are not
+ // Smis. If it's not a heap number, then return equal.
+ if (cond == lt || cond == gt) {
+ __ CompareObjectType(r0, r4, r4, FIRST_SPEC_OBJECT_TYPE);
+ __ b(ge, slow);
+ } else {
+ __ CompareObjectType(r0, r4, r4, HEAP_NUMBER_TYPE);
+ __ b(eq, &heap_number);
+ // Comparing JS objects with <=, >= is complicated.
+ if (cond != eq) {
+ __ cmp(r4, Operand(FIRST_SPEC_OBJECT_TYPE));
+ __ b(ge, slow);
+ // Normally here we fall through to return_equal, but undefined is
+ // special: (undefined == undefined) == true, but
+ // (undefined <= undefined) == false! See ECMAScript 11.8.5.
+ if (cond == le || cond == ge) {
+ __ cmp(r4, Operand(ODDBALL_TYPE));
+ __ b(ne, &return_equal);
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ cmp(r0, r2);
+ __ b(ne, &return_equal);
+ if (cond == le) {
+ // undefined <= undefined should fail.
+ __ mov(r0, Operand(GREATER));
+ } else {
+ // undefined >= undefined should fail.
+ __ mov(r0, Operand(LESS));
+ }
+ __ Ret();
+ }
+ }
+ }
+
+ __ bind(&return_equal);
+ if (cond == lt) {
+ __ mov(r0, Operand(GREATER)); // Things aren't less than themselves.
+ } else if (cond == gt) {
+ __ mov(r0, Operand(LESS)); // Things aren't greater than themselves.
+ } else {
+ __ mov(r0, Operand(EQUAL)); // Things are <=, >=, ==, === themselves.
+ }
+ __ Ret();
+
+ // For less and greater we don't have to check for NaN since the result of
+ // x < x is false regardless. For the others here is some code to check
+ // for NaN.
+ if (cond != lt && cond != gt) {
+ __ bind(&heap_number);
+ // It is a heap number, so return non-equal if it's NaN and equal if it's
+ // not NaN.
+
+ // The representation of NaN values has all exponent bits (52..62) set,
+ // and not all mantissa bits (0..51) clear.
+ // Read top bits of double representation (second word of value).
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
+ // Test that exponent bits are all set.
+ __ Sbfx(r3, r2, HeapNumber::kExponentShift, HeapNumber::kExponentBits);
+ // NaNs have all-one exponents so they sign extend to -1.
+ __ cmp(r3, Operand(-1));
+ __ b(ne, &return_equal);
+
+ // Shift out flag and all exponent bits, retaining only mantissa.
+ __ mov(r2, Operand(r2, LSL, HeapNumber::kNonMantissaBitsInTopWord));
+ // Or with all low-bits of mantissa.
+ __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
+ __ orr(r0, r3, Operand(r2), SetCC);
+ // For equal we already have the right value in r0: Return zero (equal)
+ // if all bits in mantissa are zero (it's an Infinity) and non-zero if
+ // not (it's a NaN). For <= and >= we need to load r0 with the failing
+ // value if it's a NaN.
+ if (cond != eq) {
+ // All-zero means Infinity means equal.
+ __ Ret(eq);
+ if (cond == le) {
+ __ mov(r0, Operand(GREATER)); // NaN <= NaN should fail.
+ } else {
+ __ mov(r0, Operand(LESS)); // NaN >= NaN should fail.
+ }
+ }
+ __ Ret();
+ }
+ // No fall through here.
+
+ __ bind(&not_identical);
+}
+
+
+// See comment at call site.
+static void EmitSmiNonsmiComparison(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* lhs_not_nan,
+ Label* slow,
+ bool strict) {
+ ASSERT((lhs.is(r0) && rhs.is(r1)) ||
+ (lhs.is(r1) && rhs.is(r0)));
+
+ Label rhs_is_smi;
+ __ JumpIfSmi(rhs, &rhs_is_smi);
+
+ // Lhs is a Smi. Check whether the rhs is a heap number.
+ __ CompareObjectType(rhs, r4, r4, HEAP_NUMBER_TYPE);
+ if (strict) {
+ // If rhs is not a number and lhs is a Smi then strict equality cannot
+ // succeed. Return non-equal
+ // If rhs is r0 then there is already a non zero value in it.
+ if (!rhs.is(r0)) {
+ __ mov(r0, Operand(NOT_EQUAL), LeaveCC, ne);
+ }
+ __ Ret(ne);
+ } else {
+ // Smi compared non-strictly with a non-Smi non-heap-number. Call
+ // the runtime.
+ __ b(ne, slow);
+ }
+
+ // Lhs is a smi, rhs is a number.
+ // Convert lhs to a double in d7.
+ __ SmiToDouble(d7, lhs);
+ // Load the double from rhs, tagged HeapNumber r0, to d6.
+ __ sub(r7, rhs, Operand(kHeapObjectTag));
+ __ vldr(d6, r7, HeapNumber::kValueOffset);
+
+ // We now have both loaded as doubles but we can skip the lhs nan check
+ // since it's a smi.
+ __ jmp(lhs_not_nan);
+
+ __ bind(&rhs_is_smi);
+ // Rhs is a smi. Check whether the non-smi lhs is a heap number.
+ __ CompareObjectType(lhs, r4, r4, HEAP_NUMBER_TYPE);
+ if (strict) {
+ // If lhs is not a number and rhs is a smi then strict equality cannot
+ // succeed. Return non-equal.
+ // If lhs is r0 then there is already a non zero value in it.
+ if (!lhs.is(r0)) {
+ __ mov(r0, Operand(NOT_EQUAL), LeaveCC, ne);
+ }
+ __ Ret(ne);
+ } else {
+ // Smi compared non-strictly with a non-smi non-heap-number. Call
+ // the runtime.
+ __ b(ne, slow);
+ }
+
+ // Rhs is a smi, lhs is a heap number.
+ // Load the double from lhs, tagged HeapNumber r1, to d7.
+ __ sub(r7, lhs, Operand(kHeapObjectTag));
+ __ vldr(d7, r7, HeapNumber::kValueOffset);
+ // Convert rhs to a double in d6 .
+ __ SmiToDouble(d6, rhs);
+ // Fall through to both_loaded_as_doubles.
+}
+
+
+// See comment at call site.
+static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
+ Register lhs,
+ Register rhs) {
+ ASSERT((lhs.is(r0) && rhs.is(r1)) ||
+ (lhs.is(r1) && rhs.is(r0)));
+
+ // 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_SPEC_OBJECT_TYPE);
+ Label first_non_object;
+ // Get the type of the first operand into r2 and compare it with
+ // FIRST_SPEC_OBJECT_TYPE.
+ __ CompareObjectType(rhs, r2, r2, FIRST_SPEC_OBJECT_TYPE);
+ __ b(lt, &first_non_object);
+
+ // Return non-zero (r0 is not zero)
+ Label return_not_equal;
+ __ bind(&return_not_equal);
+ __ Ret();
+
+ __ bind(&first_non_object);
+ // Check for oddballs: true, false, null, undefined.
+ __ cmp(r2, Operand(ODDBALL_TYPE));
+ __ b(eq, &return_not_equal);
+
+ __ CompareObjectType(lhs, r3, r3, FIRST_SPEC_OBJECT_TYPE);
+ __ b(ge, &return_not_equal);
+
+ // Check for oddballs: true, false, null, undefined.
+ __ cmp(r3, Operand(ODDBALL_TYPE));
+ __ b(eq, &return_not_equal);
+
+ // Now that we have the types we might as well check for
+ // internalized-internalized.
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ orr(r2, r2, Operand(r3));
+ __ tst(r2, Operand(kIsNotStringMask | kIsNotInternalizedMask));
+ __ b(eq, &return_not_equal);
+}
+
+
+// See comment at call site.
+static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* both_loaded_as_doubles,
+ Label* not_heap_numbers,
+ Label* slow) {
+ ASSERT((lhs.is(r0) && rhs.is(r1)) ||
+ (lhs.is(r1) && rhs.is(r0)));
+
+ __ CompareObjectType(rhs, r3, r2, HEAP_NUMBER_TYPE);
+ __ b(ne, not_heap_numbers);
+ __ ldr(r2, FieldMemOperand(lhs, HeapObject::kMapOffset));
+ __ cmp(r2, r3);
+ __ b(ne, slow); // First was a heap number, second wasn't. Go slow case.
+
+ // Both are heap numbers. Load them up then jump to the code we have
+ // for that.
+ __ sub(r7, rhs, Operand(kHeapObjectTag));
+ __ vldr(d6, r7, HeapNumber::kValueOffset);
+ __ sub(r7, lhs, Operand(kHeapObjectTag));
+ __ vldr(d7, r7, HeapNumber::kValueOffset);
+ __ jmp(both_loaded_as_doubles);
+}
+
+
+// Fast negative check for internalized-to-internalized equality.
+static void EmitCheckForInternalizedStringsOrObjects(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* possible_strings,
+ Label* not_both_strings) {
+ ASSERT((lhs.is(r0) && rhs.is(r1)) ||
+ (lhs.is(r1) && rhs.is(r0)));
+
+ // r2 is object type of rhs.
+ Label object_test;
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ tst(r2, Operand(kIsNotStringMask));
+ __ b(ne, &object_test);
+ __ tst(r2, Operand(kIsNotInternalizedMask));
+ __ b(ne, possible_strings);
+ __ CompareObjectType(lhs, r3, r3, FIRST_NONSTRING_TYPE);
+ __ b(ge, not_both_strings);
+ __ tst(r3, Operand(kIsNotInternalizedMask));
+ __ b(ne, possible_strings);
+
+ // Both are internalized. We already checked they weren't the same pointer
+ // so they are not equal.
+ __ mov(r0, Operand(NOT_EQUAL));
+ __ Ret();
+
+ __ bind(&object_test);
+ __ cmp(r2, Operand(FIRST_SPEC_OBJECT_TYPE));
+ __ b(lt, not_both_strings);
+ __ CompareObjectType(lhs, r2, r3, FIRST_SPEC_OBJECT_TYPE);
+ __ b(lt, not_both_strings);
+ // If both objects are undetectable, they are equal. Otherwise, they
+ // are not equal, since they are different objects and an object is not
+ // equal to undefined.
+ __ ldr(r3, FieldMemOperand(rhs, HeapObject::kMapOffset));
+ __ ldrb(r2, FieldMemOperand(r2, Map::kBitFieldOffset));
+ __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
+ __ and_(r0, r2, Operand(r3));
+ __ and_(r0, r0, Operand(1 << Map::kIsUndetectable));
+ __ eor(r0, r0, Operand(1 << Map::kIsUndetectable));
+ __ Ret();
+}
+
+
+void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_found) {
+ // Use of registers. Register result is used as a temporary.
+ Register number_string_cache = result;
+ Register mask = scratch3;
+
+ // Load the number string cache.
+ __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
+
+ // Make the hash mask from the length of the number string cache. It
+ // contains two elements (number and string) for each cache entry.
+ __ ldr(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
+ // Divide length by two (length is a smi).
+ __ mov(mask, Operand(mask, ASR, kSmiTagSize + 1));
+ __ sub(mask, mask, Operand(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
+ // doubles is the xor of the upper and lower words. See
+ // Heap::GetNumberStringCache.
+ Isolate* isolate = masm->isolate();
+ Label is_smi;
+ Label load_result_from_cache;
+ __ JumpIfSmi(object, &is_smi);
+ __ CheckMap(object,
+ scratch1,
+ Heap::kHeapNumberMapRootIndex,
+ not_found,
+ DONT_DO_SMI_CHECK);
+
+ STATIC_ASSERT(8 == kDoubleSize);
+ __ add(scratch1,
+ object,
+ Operand(HeapNumber::kValueOffset - kHeapObjectTag));
+ __ ldm(ia, scratch1, scratch1.bit() | scratch2.bit());
+ __ eor(scratch1, scratch1, Operand(scratch2));
+ __ and_(scratch1, scratch1, Operand(mask));
+
+ // Calculate address of entry in string cache: each entry consists
+ // of two pointer sized fields.
+ __ add(scratch1,
+ number_string_cache,
+ Operand(scratch1, LSL, kPointerSizeLog2 + 1));
+
+ Register probe = mask;
+ __ ldr(probe,
+ FieldMemOperand(scratch1, FixedArray::kHeaderSize));
+ __ JumpIfSmi(probe, not_found);
+ __ sub(scratch2, object, Operand(kHeapObjectTag));
+ __ vldr(d0, scratch2, HeapNumber::kValueOffset);
+ __ sub(probe, probe, Operand(kHeapObjectTag));
+ __ vldr(d1, probe, HeapNumber::kValueOffset);
+ __ VFPCompareAndSetFlags(d0, d1);
+ __ b(ne, not_found); // The cache did not contain this value.
+ __ b(&load_result_from_cache);
+
+ __ bind(&is_smi);
+ Register scratch = scratch1;
+ __ and_(scratch, mask, Operand(object, ASR, 1));
+ // Calculate address of entry in string cache: each entry consists
+ // of two pointer sized fields.
+ __ add(scratch,
+ number_string_cache,
+ Operand(scratch, LSL, kPointerSizeLog2 + 1));
+
+ // Check if the entry is the smi we are looking for.
+ __ ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
+ __ cmp(object, probe);
+ __ b(ne, not_found);
+
+ // Get the result from the cache.
+ __ bind(&load_result_from_cache);
+ __ ldr(result,
+ FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
+ __ IncrementCounter(isolate->counters()->number_to_string_native(),
+ 1,
+ scratch1,
+ scratch2);
+}
+
+
+void NumberToStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ __ ldr(r1, MemOperand(sp, 0));
+
+ // Generate code to lookup number in the number string cache.
+ GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, r4, &runtime);
+ __ add(sp, sp, Operand(1 * kPointerSize));
+ __ Ret();
+
+ __ bind(&runtime);
+ // Handle number to string in the runtime system if not found in the cache.
+ __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
+}
+
+
+static void ICCompareStub_CheckInputType(MacroAssembler* masm,
+ Register input,
+ Register scratch,
+ CompareIC::State expected,
+ Label* fail) {
+ Label ok;
+ if (expected == CompareIC::SMI) {
+ __ JumpIfNotSmi(input, fail);
+ } else if (expected == CompareIC::NUMBER) {
+ __ JumpIfSmi(input, &ok);
+ __ CheckMap(input, scratch, Heap::kHeapNumberMapRootIndex, fail,
+ DONT_DO_SMI_CHECK);
+ }
+ // We could be strict about internalized/non-internalized here, but as long as
+ // hydrogen doesn't care, the stub doesn't have to care either.
+ __ bind(&ok);
+}
+
+
+// On entry r1 and r2 are the values to be compared.
+// On exit r0 is 0, positive or negative to indicate the result of
+// the comparison.
+void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
+ Register lhs = r1;
+ Register rhs = r0;
+ Condition cc = GetCondition();
+
+ Label miss;
+ ICCompareStub_CheckInputType(masm, lhs, r2, left_, &miss);
+ ICCompareStub_CheckInputType(masm, rhs, r3, right_, &miss);
+
+ Label slow; // Call builtin.
+ Label not_smis, both_loaded_as_doubles, lhs_not_nan;
+
+ Label not_two_smis, smi_done;
+ __ orr(r2, r1, r0);
+ __ JumpIfNotSmi(r2, &not_two_smis);
+ __ mov(r1, Operand(r1, ASR, 1));
+ __ sub(r0, r1, Operand(r0, ASR, 1));
+ __ Ret();
+ __ bind(&not_two_smis);
+
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+ // it is certain that at least one operand isn't a smi.
+
+ // Handle the case where the objects are identical. Either returns the answer
+ // or goes to slow. Only falls through if the objects were not identical.
+ EmitIdenticalObjectComparison(masm, &slow, cc);
+
+ // If either is a Smi (we know that not both are), then they can only
+ // be strictly equal if the other is a HeapNumber.
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT_EQ(0, Smi::FromInt(0));
+ __ and_(r2, lhs, Operand(rhs));
+ __ JumpIfNotSmi(r2, &not_smis);
+ // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
+ // 1) Return the answer.
+ // 2) Go to slow.
+ // 3) Fall through to both_loaded_as_doubles.
+ // 4) Jump to lhs_not_nan.
+ // In cases 3 and 4 we have found out we were dealing with a number-number
+ // comparison. If VFP3 is supported the double values of the numbers have
+ // been loaded into d7 and d6. Otherwise, the double values have been loaded
+ // into r0, r1, r2, and r3.
+ EmitSmiNonsmiComparison(masm, lhs, rhs, &lhs_not_nan, &slow, strict());
+
+ __ bind(&both_loaded_as_doubles);
+ // The arguments have been converted to doubles and stored in d6 and d7, if
+ // VFP3 is supported, or in r0, r1, r2, and r3.
+ Isolate* isolate = masm->isolate();
+ __ bind(&lhs_not_nan);
+ Label no_nan;
+ // ARMv7 VFP3 instructions to implement double precision comparison.
+ __ VFPCompareAndSetFlags(d7, d6);
+ Label nan;
+ __ b(vs, &nan);
+ __ mov(r0, Operand(EQUAL), LeaveCC, eq);
+ __ mov(r0, Operand(LESS), LeaveCC, lt);
+ __ mov(r0, Operand(GREATER), LeaveCC, gt);
+ __ Ret();
+
+ __ bind(&nan);
+ // If one of the sides was a NaN then the v flag is set. Load r0 with
+ // whatever it takes to make the comparison fail, since comparisons with NaN
+ // always fail.
+ if (cc == lt || cc == le) {
+ __ mov(r0, Operand(GREATER));
+ } else {
+ __ mov(r0, Operand(LESS));
+ }
+ __ Ret();
+
+ __ bind(&not_smis);
+ // At this point we know we are dealing with two different objects,
+ // and neither of them is a Smi. The objects are in rhs_ and lhs_.
+ if (strict()) {
+ // This returns non-equal for some object types, or falls through if it
+ // was not lucky.
+ EmitStrictTwoHeapObjectCompare(masm, lhs, rhs);
+ }
+
+ Label check_for_internalized_strings;
+ Label flat_string_check;
+ // Check for heap-number-heap-number comparison. Can jump to slow case,
+ // or load both doubles into r0, r1, r2, r3 and jump to the code that handles
+ // that case. If the inputs are not doubles then jumps to
+ // check_for_internalized_strings.
+ // In this case r2 will contain the type of rhs_. Never falls through.
+ EmitCheckForTwoHeapNumbers(masm,
+ lhs,
+ rhs,
+ &both_loaded_as_doubles,
+ &check_for_internalized_strings,
+ &flat_string_check);
+
+ __ bind(&check_for_internalized_strings);
+ // In the strict case the EmitStrictTwoHeapObjectCompare already took care of
+ // internalized strings.
+ if (cc == eq && !strict()) {
+ // Returns an answer for two internalized strings or two detectable objects.
+ // Otherwise jumps to string case or not both strings case.
+ // Assumes that r2 is the type of rhs_ on entry.
+ EmitCheckForInternalizedStringsOrObjects(
+ masm, lhs, rhs, &flat_string_check, &slow);
+ }
+
+ // Check for both being sequential ASCII strings, and inline if that is the
+ // case.
+ __ bind(&flat_string_check);
+
+ __ JumpIfNonSmisNotBothSequentialAsciiStrings(lhs, rhs, r2, r3, &slow);
+
+ __ IncrementCounter(isolate->counters()->string_compare_native(), 1, r2, r3);
+ if (cc == eq) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
+ lhs,
+ rhs,
+ r2,
+ r3,
+ r4);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ lhs,
+ rhs,
+ r2,
+ r3,
+ r4,
+ r5);
+ }
+ // Never falls through to here.
+
+ __ bind(&slow);
+
+ __ Push(lhs, rhs);
+ // Figure out which native to call and setup the arguments.
+ Builtins::JavaScript native;
+ if (cc == eq) {
+ native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
+ } else {
+ native = Builtins::COMPARE;
+ int ncr; // NaN compare result
+ if (cc == lt || cc == le) {
+ ncr = GREATER;
+ } else {
+ ASSERT(cc == gt || cc == ge); // remaining cases
+ ncr = LESS;
+ }
+ __ mov(r0, Operand(Smi::FromInt(ncr)));
+ __ push(r0);
+ }
+
+ // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ InvokeBuiltin(native, JUMP_FUNCTION);
+
+ __ bind(&miss);
+ GenerateMiss(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());
+
+ const Register scratch = r1;
+
+ if (save_doubles_ == kSaveFPRegs) {
+ __ SaveFPRegs(sp, scratch);
+ }
+ const int argument_count = 1;
+ const int fp_argument_count = 0;
+
+ AllowExternalCallThatCantCauseGC scope(masm);
+ __ PrepareCallCFunction(argument_count, fp_argument_count, scratch);
+ __ mov(r0, Operand(ExternalReference::isolate_address(masm->isolate())));
+ __ CallCFunction(
+ ExternalReference::store_buffer_overflow_function(masm->isolate()),
+ argument_count);
+ if (save_doubles_ == kSaveFPRegs) {
+ __ RestoreFPRegs(sp, scratch);
+ }
+ __ ldm(ia_w, sp, kCallerSaved | pc.bit()); // Also pop pc to get Ret(0).
+}
+
+
+// Generates code to call a C function to do a double operation.
+// This code never falls through, but returns with a heap number containing
+// the result in r0.
+// Register heapnumber_result must be a heap number in which the
+// result of the operation will be stored.
+// Requires the following layout on entry:
+// d0: Left value.
+// d1: Right value.
+// If soft float ABI, use also r0, r1, r2, r3.
+static void CallCCodeForDoubleOperation(MacroAssembler* masm,
+ Token::Value op,
+ Register heap_number_result,
+ Register scratch) {
+ // Assert that heap_number_result is callee-saved.
+ // We currently always use r5 to pass it.
+ ASSERT(heap_number_result.is(r5));
+
+ // Push the current return address before the C call. Return will be
+ // through pop(pc) below.
+ __ push(lr);
+ __ PrepareCallCFunction(0, 2, scratch);
+ if (!masm->use_eabi_hardfloat()) {
+ __ vmov(r0, r1, d0);
+ __ vmov(r2, r3, d1);
+ }
+ {
+ 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()) {
+ __ vstr(d0, FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
+ } else {
+ __ Strd(r0, r1,
+ FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
+ }
+ // Place heap_number_result in r0 and return to the pushed return address.
+ __ mov(r0, Operand(heap_number_result));
+ __ pop(pc);
+}
+
+
+void BinaryOpStub::Initialize() {
+ platform_specific_bit_ = true; // VFP2 is a base requirement for V8
+}
+
+
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ Label get_result;
+
+ __ Push(r1, r0);
+
+ __ mov(r2, Operand(Smi::FromInt(MinorKey())));
+ __ push(r2);
+
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
+ masm->isolate()),
+ 3,
+ 1);
+}
+
+
+void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(
+ MacroAssembler* masm) {
+ UNIMPLEMENTED();
+}
+
+
+void BinaryOpStub_GenerateSmiSmiOperation(MacroAssembler* masm,
+ Token::Value op) {
+ Register left = r1;
+ Register right = r0;
+ Register scratch1 = r7;
+ Register scratch2 = r9;
+
+ ASSERT(right.is(r0));
+ STATIC_ASSERT(kSmiTag == 0);
+
+ Label not_smi_result;
+ switch (op) {
+ case Token::ADD:
+ __ add(right, left, Operand(right), SetCC); // Add optimistically.
+ __ Ret(vc);
+ __ sub(right, right, Operand(left)); // Revert optimistic add.
+ break;
+ case Token::SUB:
+ __ sub(right, left, Operand(right), SetCC); // Subtract optimistically.
+ __ Ret(vc);
+ __ sub(right, left, Operand(right)); // Revert optimistic subtract.
+ break;
+ case Token::MUL:
+ // Remove tag from one of the operands. This way the multiplication result
+ // will be a smi if it fits the smi range.
+ __ SmiUntag(ip, right);
+ // Do multiplication
+ // scratch1 = lower 32 bits of ip * left.
+ // scratch2 = higher 32 bits of ip * left.
+ __ smull(scratch1, scratch2, left, ip);
+ // Check for overflowing the smi range - no overflow if higher 33 bits of
+ // the result are identical.
+ __ mov(ip, Operand(scratch1, ASR, 31));
+ __ cmp(ip, Operand(scratch2));
+ __ b(ne, &not_smi_result);
+ // Go slow on zero result to handle -0.
+ __ cmp(scratch1, Operand::Zero());
+ __ mov(right, Operand(scratch1), LeaveCC, ne);
+ __ Ret(ne);
+ // We need -0 if we were multiplying a negative number with 0 to get 0.
+ // We know one of them was zero.
+ __ add(scratch2, right, Operand(left), SetCC);
+ __ mov(right, Operand(Smi::FromInt(0)), LeaveCC, pl);
+ __ Ret(pl); // Return smi 0 if the non-zero one was positive.
+ // We fall through here if we multiplied a negative number with 0, because
+ // that would mean we should produce -0.
+ break;
+ case Token::DIV: {
+ Label div_with_sdiv;
+
+ // Check for 0 divisor.
+ __ cmp(right, Operand::Zero());
+ __ b(eq, &not_smi_result);
+
+ // Check for power of two on the right hand side.
+ __ sub(scratch1, right, Operand(1));
+ __ tst(scratch1, right);
+ if (CpuFeatures::IsSupported(SUDIV)) {
+ __ b(ne, &div_with_sdiv);
+ // Check for no remainder.
+ __ tst(left, scratch1);
+ __ b(ne, &not_smi_result);
+ // Check for positive left hand side.
+ __ cmp(left, Operand::Zero());
+ __ b(mi, &div_with_sdiv);
+ } else {
+ __ b(ne, &not_smi_result);
+ // Check for positive and no remainder.
+ __ orr(scratch2, scratch1, Operand(0x80000000u));
+ __ tst(left, scratch2);
+ __ b(ne, &not_smi_result);
+ }
+
+ // Perform division by shifting.
+ __ clz(scratch1, scratch1);
+ __ rsb(scratch1, scratch1, Operand(31));
+ __ mov(right, Operand(left, LSR, scratch1));
+ __ Ret();
+
+ if (CpuFeatures::IsSupported(SUDIV)) {
+ CpuFeatureScope scope(masm, SUDIV);
+ Label result_not_zero;
+
+ __ bind(&div_with_sdiv);
+ // Do division.
+ __ sdiv(scratch1, left, right);
+ // Check that the remainder is zero.
+ __ mls(scratch2, scratch1, right, left);
+ __ cmp(scratch2, Operand::Zero());
+ __ b(ne, &not_smi_result);
+ // Check for negative zero result.
+ __ cmp(scratch1, Operand::Zero());
+ __ b(ne, &result_not_zero);
+ __ cmp(right, Operand::Zero());
+ __ b(lt, &not_smi_result);
+ __ bind(&result_not_zero);
+ // Check for the corner case of dividing the most negative smi by -1.
+ __ cmp(scratch1, Operand(0x40000000));
+ __ b(eq, &not_smi_result);
+ // Tag and return the result.
+ __ SmiTag(right, scratch1);
+ __ Ret();
+ }
+ break;
+ }
+ case Token::MOD: {
+ Label modulo_with_sdiv;
+
+ if (CpuFeatures::IsSupported(SUDIV)) {
+ // Check for x % 0.
+ __ cmp(right, Operand::Zero());
+ __ b(eq, &not_smi_result);
+
+ // Check for two positive smis.
+ __ orr(scratch1, left, Operand(right));
+ __ tst(scratch1, Operand(0x80000000u));
+ __ b(ne, &modulo_with_sdiv);
+
+ // Check for power of two on the right hand side.
+ __ sub(scratch1, right, Operand(1));
+ __ tst(scratch1, right);
+ __ b(ne, &modulo_with_sdiv);
+ } else {
+ // Check for two positive smis.
+ __ orr(scratch1, left, Operand(right));
+ __ tst(scratch1, Operand(0x80000000u));
+ __ b(ne, &not_smi_result);
+
+ // Check for power of two on the right hand side.
+ __ JumpIfNotPowerOfTwoOrZero(right, scratch1, &not_smi_result);
+ }
+
+ // Perform modulus by masking (scratch1 contains right - 1).
+ __ and_(right, left, Operand(scratch1));
+ __ Ret();
+
+ if (CpuFeatures::IsSupported(SUDIV)) {
+ CpuFeatureScope scope(masm, SUDIV);
+ __ bind(&modulo_with_sdiv);
+ __ mov(scratch2, right);
+ // Perform modulus with sdiv and mls.
+ __ sdiv(scratch1, left, right);
+ __ mls(right, scratch1, right, left);
+ // Return if the result is not 0.
+ __ cmp(right, Operand::Zero());
+ __ Ret(ne);
+ // The result is 0, check for -0 case.
+ __ cmp(left, Operand::Zero());
+ __ Ret(pl);
+ // This is a -0 case, restore the value of right.
+ __ mov(right, scratch2);
+ // We fall through here to not_smi_result to produce -0.
+ }
+ break;
+ }
+ case Token::BIT_OR:
+ __ orr(right, left, Operand(right));
+ __ Ret();
+ break;
+ case Token::BIT_AND:
+ __ and_(right, left, Operand(right));
+ __ Ret();
+ break;
+ case Token::BIT_XOR:
+ __ eor(right, left, Operand(right));
+ __ Ret();
+ break;
+ case Token::SAR:
+ // Remove tags from right operand.
+ __ GetLeastBitsFromSmi(scratch1, right, 5);
+ __ mov(right, Operand(left, ASR, scratch1));
+ // Smi tag result.
+ __ bic(right, right, Operand(kSmiTagMask));
+ __ Ret();
+ break;
+ case Token::SHR:
+ // Remove tags from operands. We can't do this on a 31 bit number
+ // because then the 0s get shifted into bit 30 instead of bit 31.
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ mov(scratch1, Operand(scratch1, LSR, scratch2));
+ // Unsigned shift is not allowed to produce a negative number, so
+ // check the sign bit and the sign bit after Smi tagging.
+ __ tst(scratch1, Operand(0xc0000000));
+ __ b(ne, &not_smi_result);
+ // Smi tag result.
+ __ SmiTag(right, scratch1);
+ __ Ret();
+ break;
+ case Token::SHL:
+ // Remove tags from operands.
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ mov(scratch1, Operand(scratch1, LSL, scratch2));
+ // Check that the signed result fits in a Smi.
+ __ TrySmiTag(right, scratch1, &not_smi_result);
+ __ Ret();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ __ bind(&not_smi_result);
+}
+
+
+void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
+ Register result,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ OverwriteMode mode);
+
+
+void BinaryOpStub_GenerateFPOperation(MacroAssembler* masm,
+ BinaryOpIC::TypeInfo left_type,
+ BinaryOpIC::TypeInfo right_type,
+ bool smi_operands,
+ Label* not_numbers,
+ Label* gc_required,
+ Label* miss,
+ Token::Value op,
+ OverwriteMode mode) {
+ Register left = r1;
+ Register right = r0;
+ Register scratch1 = r6;
+ Register scratch2 = r7;
+ Register scratch3 = r4;
+
+ ASSERT(smi_operands || (not_numbers != NULL));
+ if (smi_operands) {
+ __ AssertSmi(left);
+ __ AssertSmi(right);
+ }
+ if (left_type == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(left, miss);
+ }
+ if (right_type == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(right, miss);
+ }
+
+ Register heap_number_map = r9;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ switch (op) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD: {
+ // Allocate new heap number for result.
+ Register result = r5;
+ BinaryOpStub_GenerateHeapResultAllocation(
+ masm, result, heap_number_map, scratch1, scratch2, gc_required, mode);
+
+ // Load left and right operands into d0 and d1.
+ if (smi_operands) {
+ __ SmiToDouble(d1, right);
+ __ SmiToDouble(d0, left);
+ } else {
+ // Load right operand into d1.
+ if (right_type == BinaryOpIC::INT32) {
+ __ LoadNumberAsInt32Double(
+ right, d1, heap_number_map, scratch1, d8, miss);
+ } else {
+ Label* fail = (right_type == BinaryOpIC::NUMBER) ? miss : not_numbers;
+ __ LoadNumber(right, d1, heap_number_map, scratch1, fail);
+ }
+ // Load left operand into d0.
+ if (left_type == BinaryOpIC::INT32) {
+ __ LoadNumberAsInt32Double(
+ left, d0, heap_number_map, scratch1, d8, miss);
+ } else {
+ Label* fail = (left_type == BinaryOpIC::NUMBER) ? miss : not_numbers;
+ __ LoadNumber(
+ left, d0, heap_number_map, scratch1, fail);
+ }
+ }
+
+ // Calculate the result.
+ if (op != Token::MOD) {
+ // Using VFP registers:
+ // d0: Left value
+ // d1: Right value
+ switch (op) {
+ case Token::ADD:
+ __ vadd(d5, d0, d1);
+ break;
+ case Token::SUB:
+ __ vsub(d5, d0, d1);
+ break;
+ case Token::MUL:
+ __ vmul(d5, d0, d1);
+ break;
+ case Token::DIV:
+ __ vdiv(d5, d0, d1);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ __ sub(r0, result, Operand(kHeapObjectTag));
+ __ vstr(d5, r0, HeapNumber::kValueOffset);
+ __ add(r0, r0, Operand(kHeapObjectTag));
+ __ Ret();
+ } else {
+ // Call the C function to handle the double operation.
+ CallCCodeForDoubleOperation(masm, op, result, scratch1);
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
+ }
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::SHL: {
+ if (smi_operands) {
+ __ SmiUntag(r3, left);
+ __ SmiUntag(r2, right);
+ } else {
+ // Convert operands to 32-bit integers. Right in r2 and left in r3.
+ __ ConvertNumberToInt32(
+ left, r3, heap_number_map,
+ scratch1, scratch2, scratch3, d0, d1, not_numbers);
+ __ ConvertNumberToInt32(
+ right, r2, heap_number_map,
+ scratch1, scratch2, scratch3, d0, d1, not_numbers);
+ }
+
+ Label result_not_a_smi;
+ switch (op) {
+ case Token::BIT_OR:
+ __ orr(r2, r3, Operand(r2));
+ break;
+ case Token::BIT_XOR:
+ __ eor(r2, r3, Operand(r2));
+ break;
+ case Token::BIT_AND:
+ __ and_(r2, r3, Operand(r2));
+ break;
+ case Token::SAR:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(r2, r2, 5);
+ __ mov(r2, Operand(r3, ASR, r2));
+ break;
+ case Token::SHR:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(r2, r2, 5);
+ __ mov(r2, Operand(r3, LSR, r2), SetCC);
+ // SHR is special because it is required to produce a positive answer.
+ // The code below for writing into heap numbers isn't capable of
+ // writing the register as an unsigned int so we go to slow case if we
+ // hit this case.
+ __ b(mi, &result_not_a_smi);
+ break;
+ case Token::SHL:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(r2, r2, 5);
+ __ mov(r2, Operand(r3, LSL, r2));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // Check that the *signed* result fits in a smi.
+ __ TrySmiTag(r0, r2, &result_not_a_smi);
+ __ Ret();
+
+ // Allocate new heap number for result.
+ __ bind(&result_not_a_smi);
+ Register result = r5;
+ if (smi_operands) {
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ } else {
+ BinaryOpStub_GenerateHeapResultAllocation(
+ masm, result, heap_number_map, scratch1, scratch2, gc_required,
+ mode);
+ }
+
+ // r2: Answer as signed int32.
+ // r5: Heap number to write answer into.
+
+ // Nothing can go wrong now, so move the heap number to r0, which is the
+ // result.
+ __ mov(r0, Operand(r5));
+
+ // Convert the int32 in r2 to the heap number in r0. r3 is corrupted. As
+ // mentioned above SHR needs to always produce a positive result.
+ __ vmov(s0, r2);
+ if (op == Token::SHR) {
+ __ vcvt_f64_u32(d0, s0);
+ } else {
+ __ vcvt_f64_s32(d0, s0);
+ }
+ __ sub(r3, r0, Operand(kHeapObjectTag));
+ __ vstr(d0, r3, HeapNumber::kValueOffset);
+ __ Ret();
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+// Generate the smi code. If the operation on smis are successful this return is
+// generated. If the result is not a smi and heap number allocation is not
+// requested the code falls through. If number allocation is requested but a
+// heap number cannot be allocated the code jumps to the label gc_required.
+void BinaryOpStub_GenerateSmiCode(
+ MacroAssembler* masm,
+ Label* use_runtime,
+ Label* gc_required,
+ Token::Value op,
+ BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results,
+ OverwriteMode mode) {
+ Label not_smis;
+
+ Register left = r1;
+ Register right = r0;
+ Register scratch1 = r7;
+
+ // Perform combined smi check on both operands.
+ __ orr(scratch1, left, Operand(right));
+ __ JumpIfNotSmi(scratch1, &not_smis);
+
+ // If the smi-smi operation results in a smi return is generated.
+ BinaryOpStub_GenerateSmiSmiOperation(masm, op);
+
+ // If heap number results are possible generate the result in an allocated
+ // heap number.
+ if (allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS) {
+ BinaryOpStub_GenerateFPOperation(
+ masm, BinaryOpIC::UNINITIALIZED, BinaryOpIC::UNINITIALIZED, true,
+ use_runtime, gc_required, &not_smis, op, mode);
+ }
+ __ bind(&not_smis);
+}
+
+
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ Label right_arg_changed, call_runtime;
+
+ if (op_ == Token::MOD && encoded_right_arg_.has_value) {
+ // It is guaranteed that the value will fit into a Smi, because if it
+ // didn't, we wouldn't be here, see BinaryOp_Patch.
+ __ cmp(r0, Operand(Smi::FromInt(fixed_right_arg_value())));
+ __ b(ne, &right_arg_changed);
+ }
+
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
+ // Only allow smi results.
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, NULL, op_, NO_HEAPNUMBER_RESULTS, mode_);
+ } else {
+ // Allow heap number result and don't make a transition if a heap number
+ // cannot be allocated.
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, &call_runtime, op_, ALLOW_HEAPNUMBER_RESULTS,
+ mode_);
+ }
+
+ // Code falls through if the result is not returned as either a smi or heap
+ // number.
+ __ bind(&right_arg_changed);
+ GenerateTypeTransition(masm);
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = r1;
+ Register right = r0;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ CompareObjectType(left, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &call_runtime);
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ CompareObjectType(right, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &call_runtime);
+
+ StringAddStub string_add_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
+void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+ ASSERT(Max(left_type_, right_type_) == BinaryOpIC::INT32);
+
+ Register left = r1;
+ Register right = r0;
+ Register scratch1 = r7;
+ Register scratch2 = r9;
+ LowDwVfpRegister double_scratch = d0;
+
+ Register heap_number_result = no_reg;
+ Register heap_number_map = r6;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ Label call_runtime;
+ // Labels for type transition, used for wrong input or output types.
+ // Both label are currently actually bound to the same position. We use two
+ // different label to differentiate the cause leading to type transition.
+ Label transition;
+
+ // Smi-smi fast case.
+ Label skip;
+ __ orr(scratch1, left, right);
+ __ JumpIfNotSmi(scratch1, &skip);
+ BinaryOpStub_GenerateSmiSmiOperation(masm, op_);
+ // Fall through if the result is not a smi.
+ __ bind(&skip);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD: {
+ // It could be that only SMIs have been seen at either the left
+ // or the right operand. For precise type feedback, patch the IC
+ // again if this changes.
+ if (left_type_ == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(left, &transition);
+ }
+ if (right_type_ == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(right, &transition);
+ }
+ // Load both operands and check that they are 32-bit integer.
+ // Jump to type transition if they are not. The registers r0 and r1 (right
+ // and left) are preserved for the runtime call.
+ __ LoadNumberAsInt32Double(
+ right, d1, heap_number_map, scratch1, d8, &transition);
+ __ LoadNumberAsInt32Double(
+ left, d0, heap_number_map, scratch1, d8, &transition);
+
+ if (op_ != Token::MOD) {
+ Label return_heap_number;
+ switch (op_) {
+ case Token::ADD:
+ __ vadd(d5, d0, d1);
+ break;
+ case Token::SUB:
+ __ vsub(d5, d0, d1);
+ break;
+ case Token::MUL:
+ __ vmul(d5, d0, d1);
+ break;
+ case Token::DIV:
+ __ vdiv(d5, d0, d1);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (result_type_ <= BinaryOpIC::INT32) {
+ __ TryDoubleToInt32Exact(scratch1, d5, d8);
+ // If the ne condition is set, result does
+ // not fit in a 32-bit integer.
+ __ b(ne, &transition);
+ // Try to tag the result as a Smi, return heap number on overflow.
+ __ SmiTag(scratch1, SetCC);
+ __ b(vs, &return_heap_number);
+ // Check for minus zero, transition in that case (because we need
+ // to return a heap number).
+ Label not_zero;
+ ASSERT(kSmiTag == 0);
+ __ b(ne, &not_zero);
+ __ VmovHigh(scratch2, d5);
+ __ tst(scratch2, Operand(HeapNumber::kSignMask));
+ __ b(ne, &transition);
+ __ bind(&not_zero);
+ __ mov(r0, scratch1);
+ __ Ret();
+ }
+
+ __ bind(&return_heap_number);
+ // Return a heap number, or fall through to type transition or runtime
+ // call if we can't.
+ // We are using vfp registers so r5 is available.
+ heap_number_result = r5;
+ BinaryOpStub_GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime,
+ mode_);
+ __ sub(r0, heap_number_result, Operand(kHeapObjectTag));
+ __ vstr(d5, r0, HeapNumber::kValueOffset);
+ __ mov(r0, heap_number_result);
+ __ Ret();
+
+ // A DIV operation expecting an integer result falls through
+ // to type transition.
+
+ } else {
+ if (encoded_right_arg_.has_value) {
+ __ Vmov(d8, fixed_right_arg_value(), scratch1);
+ __ VFPCompareAndSetFlags(d1, d8);
+ __ b(ne, &transition);
+ }
+
+ // We preserved r0 and r1 to be able to call runtime.
+ // Save the left value on the stack.
+ __ Push(r5, r4);
+
+ Label pop_and_call_runtime;
+
+ // Allocate a heap number to store the result.
+ heap_number_result = r5;
+ BinaryOpStub_GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &pop_and_call_runtime,
+ mode_);
+
+ // Load the left value from the value saved on the stack.
+ __ Pop(r1, r0);
+
+ // Call the C function to handle the double operation.
+ CallCCodeForDoubleOperation(masm, op_, heap_number_result, scratch1);
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
+
+ __ bind(&pop_and_call_runtime);
+ __ Drop(2);
+ __ b(&call_runtime);
+ }
+
+ break;
+ }
+
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::SHL: {
+ Label return_heap_number;
+ // Convert operands to 32-bit integers. Right in r2 and left in r3. The
+ // registers r0 and r1 (right and left) are preserved for the runtime
+ // call.
+ __ LoadNumberAsInt32(left, r3, heap_number_map,
+ scratch1, d0, d1, &transition);
+ __ LoadNumberAsInt32(right, r2, heap_number_map,
+ scratch1, d0, d1, &transition);
+
+ // The ECMA-262 standard specifies that, for shift operations, only the
+ // 5 least significant bits of the shift value should be used.
+ switch (op_) {
+ case Token::BIT_OR:
+ __ orr(r2, r3, Operand(r2));
+ break;
+ case Token::BIT_XOR:
+ __ eor(r2, r3, Operand(r2));
+ break;
+ case Token::BIT_AND:
+ __ and_(r2, r3, Operand(r2));
+ break;
+ case Token::SAR:
+ __ and_(r2, r2, Operand(0x1f));
+ __ mov(r2, Operand(r3, ASR, r2));
+ break;
+ case Token::SHR:
+ __ and_(r2, r2, Operand(0x1f));
+ __ mov(r2, Operand(r3, LSR, r2), SetCC);
+ // SHR is special because it is required to produce a positive answer.
+ // We only get a negative result if the shift value (r2) is 0.
+ // This result cannot be respresented as a signed 32-bit integer, try
+ // to return a heap number if we can.
+ __ b(mi, (result_type_ <= BinaryOpIC::INT32)
+ ? &transition
+ : &return_heap_number);
+ break;
+ case Token::SHL:
+ __ and_(r2, r2, Operand(0x1f));
+ __ mov(r2, Operand(r3, LSL, r2));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // Check if the result fits in a smi. If not try to return a heap number.
+ // (We know the result is an int32).
+ __ TrySmiTag(r0, r2, &return_heap_number);
+ __ Ret();
+
+ __ bind(&return_heap_number);
+ heap_number_result = r5;
+ BinaryOpStub_GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime,
+ mode_);
+
+ if (op_ != Token::SHR) {
+ // Convert the result to a floating point value.
+ __ vmov(double_scratch.low(), r2);
+ __ vcvt_f64_s32(double_scratch, double_scratch.low());
+ } else {
+ // The result must be interpreted as an unsigned 32-bit integer.
+ __ vmov(double_scratch.low(), r2);
+ __ vcvt_f64_u32(double_scratch, double_scratch.low());
+ }
+
+ // Store the result.
+ __ sub(r0, heap_number_result, Operand(kHeapObjectTag));
+ __ vstr(double_scratch, r0, HeapNumber::kValueOffset);
+ __ mov(r0, heap_number_result);
+ __ Ret();
+
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+
+ // We never expect DIV to yield an integer result, so we always generate
+ // type transition code for DIV operations expecting an integer result: the
+ // code will fall through to this type transition.
+ if (transition.is_linked() ||
+ ((op_ == Token::DIV) && (result_type_ <= BinaryOpIC::INT32))) {
+ __ bind(&transition);
+ GenerateTypeTransition(masm);
+ }
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
+ Label call_runtime;
+
+ if (op_ == Token::ADD) {
+ // Handle string addition here, because it is the only operation
+ // that does not do a ToNumber conversion on the operands.
+ GenerateAddStrings(masm);
+ }
+
+ // Convert oddball arguments to numbers.
+ Label check, done;
+ __ CompareRoot(r1, Heap::kUndefinedValueRootIndex);
+ __ b(ne, &check);
+ if (Token::IsBitOp(op_)) {
+ __ mov(r1, Operand(Smi::FromInt(0)));
+ } else {
+ __ LoadRoot(r1, Heap::kNanValueRootIndex);
+ }
+ __ jmp(&done);
+ __ bind(&check);
+ __ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
+ __ b(ne, &done);
+ if (Token::IsBitOp(op_)) {
+ __ mov(r0, Operand(Smi::FromInt(0)));
+ } else {
+ __ LoadRoot(r0, Heap::kNanValueRootIndex);
+ }
+ __ bind(&done);
+
+ GenerateNumberStub(masm);
+}
+
+
+void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
+ Label call_runtime, transition;
+ BinaryOpStub_GenerateFPOperation(
+ masm, left_type_, right_type_, false,
+ &transition, &call_runtime, &transition, op_, mode_);
+
+ __ bind(&transition);
+ GenerateTypeTransition(masm);
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+ Label call_runtime, call_string_add_or_runtime, transition;
+
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, &call_runtime, op_, ALLOW_HEAPNUMBER_RESULTS, mode_);
+
+ BinaryOpStub_GenerateFPOperation(
+ masm, left_type_, right_type_, false,
+ &call_string_add_or_runtime, &call_runtime, &transition, op_, mode_);
+
+ __ bind(&transition);
+ GenerateTypeTransition(masm);
+
+ __ bind(&call_string_add_or_runtime);
+ if (op_ == Token::ADD) {
+ GenerateAddStrings(masm);
+ }
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
+ ASSERT(op_ == Token::ADD);
+ Label left_not_string, call_runtime;
+
+ Register left = r1;
+ Register right = r0;
+
+ // Check if left argument is a string.
+ __ JumpIfSmi(left, &left_not_string);
+ __ CompareObjectType(left, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &left_not_string);
+
+ StringAddStub string_add_left_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_left_stub);
+
+ // Left operand is not a string, test right.
+ __ bind(&left_not_string);
+ __ JumpIfSmi(right, &call_runtime);
+ __ CompareObjectType(right, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &call_runtime);
+
+ StringAddStub string_add_right_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_right_stub);
+
+ // At least one argument is not a string.
+ __ bind(&call_runtime);
+}
+
+
+void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
+ Register result,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ OverwriteMode mode) {
+ // Code below will scratch result if allocation fails. To keep both arguments
+ // intact for the runtime call result cannot be one of these.
+ ASSERT(!result.is(r0) && !result.is(r1));
+
+ if (mode == OVERWRITE_LEFT || mode == OVERWRITE_RIGHT) {
+ Label skip_allocation, allocated;
+ Register overwritable_operand = mode == OVERWRITE_LEFT ? r1 : r0;
+ // If the overwritable operand is already an object, we skip the
+ // allocation of a heap number.
+ __ JumpIfNotSmi(overwritable_operand, &skip_allocation);
+ // Allocate a heap number for the result.
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ __ b(&allocated);
+ __ bind(&skip_allocation);
+ // Use object holding the overwritable operand for result.
+ __ mov(result, Operand(overwritable_operand));
+ __ bind(&allocated);
+ } else {
+ ASSERT(mode == NO_OVERWRITE);
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ }
+}
+
+
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ Push(r1, r0);
+}
+
+
+void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
+ // Untagged case: double input in d2, double result goes
+ // into d2.
+ // Tagged case: tagged input on top of stack and in r0,
+ // tagged result (heap number) goes into r0.
+
+ Label input_not_smi;
+ Label loaded;
+ Label calculate;
+ Label invalid_cache;
+ const Register scratch0 = r9;
+ const Register scratch1 = r7;
+ const Register cache_entry = r0;
+ const bool tagged = (argument_type_ == TAGGED);
+
+ if (tagged) {
+ // Argument is a number and is on stack and in r0.
+ // Load argument and check if it is a smi.
+ __ JumpIfNotSmi(r0, &input_not_smi);
+
+ // Input is a smi. Convert to double and load the low and high words
+ // of the double into r2, r3.
+ __ SmiToDouble(d7, r0);
+ __ vmov(r2, r3, d7);
+ __ b(&loaded);
+
+ __ bind(&input_not_smi);
+ // Check if input is a HeapNumber.
+ __ CheckMap(r0,
+ r1,
+ Heap::kHeapNumberMapRootIndex,
+ &calculate,
+ DONT_DO_SMI_CHECK);
+ // Input is a HeapNumber. Load it to a double register and store the
+ // low and high words into r2, r3.
+ __ vldr(d0, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ __ vmov(r2, r3, d0);
+ } else {
+ // Input is untagged double in d2. Output goes to d2.
+ __ vmov(r2, r3, d2);
+ }
+ __ bind(&loaded);
+ // r2 = low 32 bits of double value
+ // r3 = high 32 bits of double value
+ // Compute hash (the shifts are arithmetic):
+ // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
+ __ eor(r1, r2, Operand(r3));
+ __ eor(r1, r1, Operand(r1, ASR, 16));
+ __ eor(r1, r1, Operand(r1, ASR, 8));
+ ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
+ __ And(r1, r1, Operand(TranscendentalCache::SubCache::kCacheSize - 1));
+
+ // r2 = low 32 bits of double value.
+ // r3 = high 32 bits of double value.
+ // r1 = TranscendentalCache::hash(double value).
+ Isolate* isolate = masm->isolate();
+ ExternalReference cache_array =
+ ExternalReference::transcendental_cache_array_address(isolate);
+ __ mov(cache_entry, Operand(cache_array));
+ // cache_entry points to cache array.
+ int cache_array_index
+ = type_ * sizeof(isolate->transcendental_cache()->caches_[0]);
+ __ ldr(cache_entry, MemOperand(cache_entry, cache_array_index));
+ // r0 points to the cache for the type type_.
+ // If NULL, the cache hasn't been initialized yet, so go through runtime.
+ __ cmp(cache_entry, Operand::Zero());
+ __ b(eq, &invalid_cache);
+
+#ifdef DEBUG
+ // Check that the layout of cache elements match expectations.
+ { TranscendentalCache::SubCache::Element test_elem[2];
+ char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
+ char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
+ char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
+ char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
+ char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
+ CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
+ CHECK_EQ(0, elem_in0 - elem_start);
+ CHECK_EQ(kIntSize, elem_in1 - elem_start);
+ CHECK_EQ(2 * kIntSize, elem_out - elem_start);
+ }
+#endif
+
+ // Find the address of the r1'st entry in the cache, i.e., &r0[r1*12].
+ __ add(r1, r1, Operand(r1, LSL, 1));
+ __ add(cache_entry, cache_entry, Operand(r1, LSL, 2));
+ // 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);
+ __ 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();
+ __ mov(r0, Operand(r6));
+ } else {
+ // Load result into d2.
+ __ vldr(d2, FieldMemOperand(r6, HeapNumber::kValueOffset));
+ }
+ __ Ret();
+
+ __ bind(&calculate);
+ __ IncrementCounter(
+ counters->transcendental_cache_miss(), 1, scratch0, scratch1);
+ if (tagged) {
+ __ bind(&invalid_cache);
+ ExternalReference runtime_function =
+ ExternalReference(RuntimeFunction(), masm->isolate());
+ __ TailCallExternalReference(runtime_function, 1, 1);
+ } else {
+ Label no_update;
+ Label skip_cache;
+
+ // Call C function to calculate the result and update the cache.
+ // r0: precalculated cache entry address.
+ // r2 and r3: parts of the double value.
+ // Store r0, r2 and r3 on stack for later before calling C function.
+ __ Push(r3, r2, cache_entry);
+ GenerateCallCFunction(masm, scratch0);
+ __ GetCFunctionDoubleResult(d2);
+
+ // Try to update the cache. If we cannot allocate a
+ // heap number, we return the result without updating.
+ __ Pop(r3, r2, cache_entry);
+ __ LoadRoot(r5, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r6, scratch0, scratch1, r5, &no_update);
+ __ vstr(d2, FieldMemOperand(r6, HeapNumber::kValueOffset));
+ __ stm(ia, cache_entry, r2.bit() | r3.bit() | r6.bit());
+ __ Ret();
+
+ __ bind(&invalid_cache);
+ // The cache is invalid. Call runtime which will recreate the
+ // cache.
+ __ LoadRoot(r5, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r0, scratch0, scratch1, r5, &skip_cache);
+ __ vstr(d2, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(r0);
+ __ CallRuntime(RuntimeFunction(), 1);
+ }
+ __ vldr(d2, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ __ Ret();
+
+ __ bind(&skip_cache);
+ // Call C function to calculate the result and answer directly
+ // without updating the cache.
+ GenerateCallCFunction(masm, scratch0);
+ __ GetCFunctionDoubleResult(d2);
+ __ bind(&no_update);
+
+ // We return the value in d2 without adding it to the cache, but
+ // we cause a scavenging GC so that future allocations will succeed.
+ {
+ 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();
+ }
+}
+
+
+void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm,
+ Register scratch) {
+ Isolate* isolate = masm->isolate();
+
+ __ push(lr);
+ __ PrepareCallCFunction(0, 1, scratch);
+ if (masm->use_eabi_hardfloat()) {
+ __ vmov(d0, d2);
+ } else {
+ __ vmov(r0, r1, d2);
+ }
+ AllowExternalCallThatCantCauseGC scope(masm);
+ switch (type_) {
+ case TranscendentalCache::SIN:
+ __ CallCFunction(ExternalReference::math_sin_double_function(isolate),
+ 0, 1);
+ break;
+ case TranscendentalCache::COS:
+ __ 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);
+ break;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+ __ pop(lr);
+}
+
+
+Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
+ switch (type_) {
+ // 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();
+ return Runtime::kAbort;
+ }
+}
+
+
+void StackCheckStub::Generate(MacroAssembler* masm) {
+ __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
+}
+
+
+void InterruptStub::Generate(MacroAssembler* masm) {
+ __ TailCallRuntime(Runtime::kInterrupt, 0, 1);
+}
+
+
+void MathPowStub::Generate(MacroAssembler* masm) {
+ const Register base = r1;
+ const Register exponent = r2;
+ const Register heapnumbermap = r5;
+ const Register heapnumber = r0;
+ const DwVfpRegister double_base = d1;
+ const DwVfpRegister double_exponent = d2;
+ const DwVfpRegister double_result = d3;
+ const DwVfpRegister 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));
+
+ __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex);
+
+ __ UntagAndJumpIfSmi(scratch, base, &base_is_smi);
+ __ ldr(scratch, FieldMemOperand(base, JSObject::kMapOffset));
+ __ cmp(scratch, heapnumbermap);
+ __ b(ne, &call_runtime);
+
+ __ vldr(double_base, FieldMemOperand(base, HeapNumber::kValueOffset));
+ __ jmp(&unpack_exponent);
+
+ __ bind(&base_is_smi);
+ __ vmov(single_scratch, scratch);
+ __ vcvt_f64_s32(double_base, single_scratch);
+ __ bind(&unpack_exponent);
+
+ __ UntagAndJumpIfSmi(scratch, exponent, &int_exponent);
+
+ __ ldr(scratch, FieldMemOperand(exponent, JSObject::kMapOffset));
+ __ cmp(scratch, heapnumbermap);
+ __ b(ne, &call_runtime);
+ __ 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, scratch);
+ __ 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, scratch);
+ __ 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, scratch);
+ __ 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, scratch);
+ __ 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.0, scratch);
+ __ vsqrt(double_scratch, double_scratch);
+ __ vdiv(double_result, double_result, double_scratch);
+ __ jmp(&done);
+ }
+
+ __ 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);
+ __ 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, scratch2);
+
+ // Get absolute value of exponent.
+ __ cmp(scratch, Operand::Zero());
+ __ mov(scratch2, Operand::Zero(), 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::Zero());
+ __ b(ge, &done);
+ __ vmov(double_scratch, 1.0, scratch);
+ __ 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));
+ 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(&done);
+ __ IncrementCounter(counters->math_pow(), 1, scratch, scratch2);
+ __ Ret();
+ }
+}
+
+
+bool CEntryStub::NeedsImmovableCode() {
+ return true;
+}
+
+
+bool CEntryStub::IsPregenerated() {
+ return (!save_doubles_ || ISOLATE->fp_stubs_generated()) &&
+ result_size_ == 1;
+}
+
+
+void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) {
+ CEntryStub::GenerateAheadOfTime(isolate);
+ WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ StubFailureTrampolineStub::GenerateAheadOfTime(isolate);
+ RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
+ CreateAllocationSiteStub::GenerateAheadOfTime(isolate);
+}
+
+
+void CodeStub::GenerateFPStubs(Isolate* isolate) {
+ SaveFPRegsMode mode = kSaveFPRegs;
+ CEntryStub save_doubles(1, mode);
+ StoreBufferOverflowStub stub(mode);
+ // These stubs might already be in the snapshot, detect that and don't
+ // regenerate, which would lead to code stub initialization state being messed
+ // up.
+ Code* save_doubles_code;
+ if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) {
+ save_doubles_code = *save_doubles.GetCode(isolate);
+ }
+ Code* store_buffer_overflow_code;
+ if (!stub.FindCodeInCache(&store_buffer_overflow_code, isolate)) {
+ store_buffer_overflow_code = *stub.GetCode(isolate);
+ }
+ save_doubles_code->set_is_pregenerated(true);
+ store_buffer_overflow_code->set_is_pregenerated(true);
+ isolate->set_fp_stubs_generated(true);
+}
+
+
+void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
+ CEntryStub stub(1, kDontSaveFPRegs);
+ Handle<Code> code = stub.GetCode(isolate);
+ code->set_is_pregenerated(true);
+}
+
+
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ and_(scratch, value, Operand(0xf));
+ __ cmp(scratch, Operand(0xf));
+ __ b(eq, oom_label);
+}
+
+
+void CEntryStub::GenerateCore(MacroAssembler* masm,
+ Label* throw_normal_exception,
+ Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
+ bool do_gc,
+ bool always_allocate) {
+ // r0: result parameter for PerformGC, if any
+ // r4: number of arguments including receiver (C callee-saved)
+ // r5: pointer to builtin function (C callee-saved)
+ // r6: pointer to the first argument (C callee-saved)
+ Isolate* isolate = masm->isolate();
+
+ if (do_gc) {
+ // Passing r0.
+ __ PrepareCallCFunction(1, 0, r1);
+ __ CallCFunction(ExternalReference::perform_gc_function(isolate),
+ 1, 0);
+ }
+
+ ExternalReference scope_depth =
+ ExternalReference::heap_always_allocate_scope_depth(isolate);
+ if (always_allocate) {
+ __ mov(r0, Operand(scope_depth));
+ __ ldr(r1, MemOperand(r0));
+ __ add(r1, r1, Operand(1));
+ __ str(r1, MemOperand(r0));
+ }
+
+ // Call C built-in.
+ // r0 = argc, r1 = argv
+ __ mov(r0, Operand(r4));
+ __ mov(r1, Operand(r6));
+
+#if V8_HOST_ARCH_ARM
+ int frame_alignment = MacroAssembler::ActivationFrameAlignment();
+ int frame_alignment_mask = frame_alignment - 1;
+ if (FLAG_debug_code) {
+ if (frame_alignment > kPointerSize) {
+ Label alignment_as_expected;
+ ASSERT(IsPowerOf2(frame_alignment));
+ __ tst(sp, Operand(frame_alignment_mask));
+ __ b(eq, &alignment_as_expected);
+ // Don't use Check here, as it will call Runtime_Abort re-entering here.
+ __ stop("Unexpected alignment");
+ __ bind(&alignment_as_expected);
+ }
+ }
+#endif
+
+ __ mov(r2, Operand(ExternalReference::isolate_address(isolate)));
+
+ // To let the GC traverse the return address of the exit frames, we need to
+ // know where the return address is. The CEntryStub is unmovable, so
+ // we can store the address on the stack to be able to find it again and
+ // we never have to restore it, because it will not change.
+ // Compute the return address in lr to return to after the jump below. Pc is
+ // already at '+ 8' from the current instruction but return is after three
+ // instructions so add another 4 to pc to get the return address.
+ {
+ // Prevent literal pool emission before return address.
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+ masm->add(lr, pc, Operand(4));
+ __ str(lr, MemOperand(sp, 0));
+ masm->Jump(r5);
+ }
+
+ __ VFPEnsureFPSCRState(r2);
+
+ if (always_allocate) {
+ // It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
+ // though (contain the result).
+ __ mov(r2, Operand(scope_depth));
+ __ ldr(r3, MemOperand(r2));
+ __ sub(r3, r3, Operand(1));
+ __ str(r3, MemOperand(r2));
+ }
+
+ // check for failure result
+ Label failure_returned;
+ STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
+ // Lower 2 bits of r2 are 0 iff r0 has failure tag.
+ __ add(r2, r0, Operand(1));
+ __ tst(r2, Operand(kFailureTagMask));
+ __ b(eq, &failure_returned);
+
+ // Exit C frame and return.
+ // r0:r1: result
+ // sp: stack pointer
+ // fp: frame pointer
+ // Callee-saved register r4 still holds argc.
+ __ LeaveExitFrame(save_doubles_, r4);
+ __ mov(pc, lr);
+
+ // check if we should retry or throw exception
+ Label retry;
+ __ bind(&failure_returned);
+ STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
+ __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
+ __ b(eq, &retry);
+
+ // Special handling of out of memory exceptions.
+ JumpIfOOM(masm, r0, ip, throw_out_of_memory_exception);
+
+ // Retrieve the pending exception.
+ __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ ldr(r0, MemOperand(ip));
+
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, r0, ip, throw_out_of_memory_exception);
+
+ // Clear the pending exception.
+ __ mov(r3, Operand(isolate->factory()->the_hole_value()));
+ __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ str(r3, MemOperand(ip));
+
+ // Special handling of termination exceptions which are uncatchable
+ // by javascript code.
+ __ cmp(r0, Operand(isolate->factory()->termination_exception()));
+ __ b(eq, throw_termination_exception);
+
+ // Handle normal exception.
+ __ jmp(throw_normal_exception);
+
+ __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying
+}
+
+
+void CEntryStub::Generate(MacroAssembler* masm) {
+ // Called from JavaScript; parameters are on stack as if calling JS function
+ // r0: number of arguments including receiver
+ // r1: pointer to builtin function
+ // fp: frame pointer (restored after C call)
+ // sp: stack pointer (restored as callee's sp after C call)
+ // cp: current context (C callee-saved)
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Result returned in r0 or r0+r1 by default.
+
+ // NOTE: Invocations of builtins may return failure objects
+ // instead of a proper result. The builtin entry handles
+ // this by performing a garbage collection and retrying the
+ // builtin once.
+
+ // Compute the argv pointer in a callee-saved register.
+ __ add(r6, sp, Operand(r0, LSL, kPointerSizeLog2));
+ __ sub(r6, r6, Operand(kPointerSize));
+
+ // Enter the exit frame that transitions from JavaScript to C++.
+ FrameScope scope(masm, StackFrame::MANUAL);
+ __ EnterExitFrame(save_doubles_);
+
+ // Set up argc and the builtin function in callee-saved registers.
+ __ mov(r4, Operand(r0));
+ __ mov(r5, Operand(r1));
+
+ // r4: number of arguments (C callee-saved)
+ // r5: pointer to builtin function (C callee-saved)
+ // r6: pointer to first argument (C callee-saved)
+
+ Label throw_normal_exception;
+ Label throw_termination_exception;
+ Label throw_out_of_memory_exception;
+
+ // Call into the runtime system.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ false,
+ false);
+
+ // Do space-specific GC and retry runtime call.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ false);
+
+ // Do full GC and retry runtime call one final time.
+ Failure* failure = Failure::InternalError();
+ __ mov(r0, Operand(reinterpret_cast<int32_t>(failure)));
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ true);
+
+ __ bind(&throw_out_of_memory_exception);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
+ isolate);
+ __ mov(r0, Operand(false, RelocInfo::NONE32));
+ __ mov(r2, Operand(external_caught));
+ __ str(r0, MemOperand(r2));
+
+ // Set pending exception and r0 to out of memory exception.
+ Label already_have_failure;
+ JumpIfOOM(masm, r0, ip, &already_have_failure);
+ Failure* out_of_memory = Failure::OutOfMemoryException(0x1);
+ __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+ __ bind(&already_have_failure);
+ __ mov(r2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ str(r0, MemOperand(r2));
+ // Fall through to the next label.
+
+ __ bind(&throw_termination_exception);
+ __ ThrowUncatchable(r0);
+
+ __ bind(&throw_normal_exception);
+ __ Throw(r0);
+}
+
+
+void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
+ // r0: code entry
+ // r1: function
+ // r2: receiver
+ // r3: argc
+ // [sp+0]: argv
+
+ Label invoke, handler_entry, exit;
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Called from C, so do not pop argc and args on exit (preserve sp)
+ // No need to save register-passed args
+ // Save callee-saved registers (incl. cp and fp), sp, and lr
+ __ stm(db_w, sp, kCalleeSaved | lr.bit());
+
+ // Save callee-saved vfp registers.
+ __ vstm(db_w, sp, kFirstCalleeSavedDoubleReg, kLastCalleeSavedDoubleReg);
+ // Set up the reserved register for 0.0.
+ __ vmov(kDoubleRegZero, 0.0);
+ __ VFPEnsureFPSCRState(r4);
+
+ // Get address of argv, see stm above.
+ // r0: code entry
+ // r1: function
+ // r2: receiver
+ // r3: argc
+
+ // Set up argv in r4.
+ int offset_to_argv = (kNumCalleeSaved + 1) * kPointerSize;
+ offset_to_argv += kNumDoubleCalleeSaved * kDoubleSize;
+ __ ldr(r4, MemOperand(sp, offset_to_argv));
+
+ // Push a frame with special values setup to mark it as an entry frame.
+ // r0: code entry
+ // r1: function
+ // r2: receiver
+ // r3: argc
+ // r4: argv
+ Isolate* isolate = masm->isolate();
+ __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used.
+ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
+ __ mov(r7, Operand(Smi::FromInt(marker)));
+ __ mov(r6, Operand(Smi::FromInt(marker)));
+ __ mov(r5,
+ Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate)));
+ __ ldr(r5, MemOperand(r5));
+ __ Push(r8, r7, r6, r5);
+
+ // 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.
+ Label non_outermost_js;
+ ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, isolate);
+ __ mov(r5, Operand(ExternalReference(js_entry_sp)));
+ __ ldr(r6, MemOperand(r5));
+ __ cmp(r6, Operand::Zero());
+ __ b(ne, &non_outermost_js);
+ __ str(fp, MemOperand(r5));
+ __ mov(ip, Operand(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
+ Label cont;
+ __ b(&cont);
+ __ bind(&non_outermost_js);
+ __ mov(ip, Operand(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
+ __ bind(&cont);
+ __ push(ip);
+
+ // Jump to a faked try block that does the invoke, with a faked catch
+ // block that sets the pending exception.
+ __ jmp(&invoke);
+
+ // Block literal pool emission whilst taking the position of the handler
+ // entry. This avoids making the assumption that literal pools are always
+ // emitted after an instruction is emitted, rather than before.
+ {
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+ __ 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. 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(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(r5, Operand(isolate->factory()->the_hole_value()));
+ __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ str(r5, MemOperand(ip));
+
+ // Invoke the function by calling through JS entry trampoline builtin.
+ // Notice that we cannot store a reference to the trampoline code directly in
+ // this stub, because runtime stubs are not traversed when doing GC.
+
+ // Expected registers by Builtins::JSEntryTrampoline
+ // r0: code entry
+ // r1: function
+ // r2: receiver
+ // r3: argc
+ // r4: argv
+ if (is_construct) {
+ ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
+ isolate);
+ __ mov(ip, Operand(construct_entry));
+ } else {
+ ExternalReference entry(Builtins::kJSEntryTrampoline, isolate);
+ __ mov(ip, Operand(entry));
+ }
+ __ ldr(ip, MemOperand(ip)); // deref address
+
+ // Branch and link to JSEntryTrampoline. We don't use the double underscore
+ // macro for the add instruction because we don't want the coverage tool
+ // inserting instructions here after we read the pc. We block literal pool
+ // emission for the same reason.
+ {
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+ __ mov(lr, Operand(pc));
+ masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
+ }
+
+ // Unlink this frame from the handler chain.
+ __ PopTryHandler();
+
+ __ bind(&exit); // r0 holds result
+ // Check if the current stack frame is marked as the outermost JS frame.
+ Label non_outermost_js_2;
+ __ pop(r5);
+ __ cmp(r5, Operand(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
+ __ b(ne, &non_outermost_js_2);
+ __ mov(r6, Operand::Zero());
+ __ mov(r5, Operand(ExternalReference(js_entry_sp)));
+ __ str(r6, MemOperand(r5));
+ __ bind(&non_outermost_js_2);
+
+ // Restore the top frame descriptors from the stack.
+ __ pop(r3);
+ __ mov(ip,
+ Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate)));
+ __ str(r3, MemOperand(ip));
+
+ // Reset the stack to the callee saved registers.
+ __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
+
+ // Restore callee-saved registers and return.
+#ifdef DEBUG
+ if (FLAG_debug_code) {
+ __ mov(lr, Operand(pc));
+ }
+#endif
+
+ // Restore callee-saved vfp registers.
+ __ vldm(ia_w, sp, kFirstCalleeSavedDoubleReg, kLastCalleeSavedDoubleReg);
+
+ __ ldm(ia_w, sp, kCalleeSaved | pc.bit());
+}
+
+
+// Uses registers r0 to r4.
+// Expected input (depending on whether args are in registers or on the stack):
+// * object: r0 or at sp + 1 * kPointerSize.
+// * function: r1 or at sp.
+//
+// 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 r4.
+// (See LCodeGen::DoInstanceOfKnownGlobal)
+void InstanceofStub::Generate(MacroAssembler* masm) {
+ // Call site inlining and patching implies arguments in registers.
+ ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
+ // ReturnTrueFalse is only implemented for inlined call sites.
+ ASSERT(!ReturnTrueFalseObject() || HasCallSiteInlineCheck());
+
+ // Fixed register usage throughout the stub:
+ const Register object = r0; // Object (lhs).
+ Register map = r3; // Map of the object.
+ const Register function = r1; // Function (rhs).
+ const Register prototype = r4; // Prototype of the function.
+ const Register inline_site = r9;
+ const Register scratch = r2;
+
+ const int32_t kDeltaToLoadBoolResult = 4 * kPointerSize;
+
+ Label slow, loop, is_instance, is_not_instance, not_js_object;
+
+ if (!HasArgsInRegisters()) {
+ __ ldr(object, MemOperand(sp, 1 * kPointerSize));
+ __ ldr(function, MemOperand(sp, 0));
+ }
+
+ // Check that the left hand is a JS object and load map.
+ __ JumpIfSmi(object, &not_js_object);
+ __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
+
+ // If there is a call site cache don't look in the global cache, but do the
+ // real lookup and update the call site cache.
+ if (!HasCallSiteInlineCheck()) {
+ Label miss;
+ __ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
+ __ b(ne, &miss);
+ __ CompareRoot(map, Heap::kInstanceofCacheMapRootIndex);
+ __ b(ne, &miss);
+ __ LoadRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&miss);
+ }
+
+ // Get the prototype of the function.
+ __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
+
+ // Check that the function prototype is a JS object.
+ __ JumpIfSmi(prototype, &slow);
+ __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
+
+ // Update the global instanceof or call site inlined cache with the current
+ // map and function. The cached answer will be set when it is known below.
+ if (!HasCallSiteInlineCheck()) {
+ __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
+ __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
+ } else {
+ ASSERT(HasArgsInRegisters());
+ // Patch the (relocated) inlined map check.
+
+ // The offset was stored in r4 safepoint slot.
+ // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal)
+ __ LoadFromSafepointRegisterSlot(scratch, r4);
+ __ sub(inline_site, lr, scratch);
+ // Get the map location in scratch and patch it.
+ __ GetRelocatedValueLocation(inline_site, scratch);
+ __ ldr(scratch, MemOperand(scratch));
+ __ str(map, FieldMemOperand(scratch, Cell::kValueOffset));
+ }
+
+ // Register mapping: r3 is object map and r4 is function prototype.
+ // Get prototype of object into r2.
+ __ ldr(scratch, FieldMemOperand(map, Map::kPrototypeOffset));
+
+ // We don't need map any more. Use it as a scratch register.
+ Register scratch2 = map;
+ map = no_reg;
+
+ // Loop through the prototype chain looking for the function prototype.
+ __ LoadRoot(scratch2, Heap::kNullValueRootIndex);
+ __ bind(&loop);
+ __ cmp(scratch, Operand(prototype));
+ __ b(eq, &is_instance);
+ __ cmp(scratch, scratch2);
+ __ b(eq, &is_not_instance);
+ __ ldr(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset));
+ __ ldr(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
+ __ jmp(&loop);
+
+ __ bind(&is_instance);
+ if (!HasCallSiteInlineCheck()) {
+ __ mov(r0, Operand(Smi::FromInt(0)));
+ __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ // Patch the call site to return true.
+ __ LoadRoot(r0, Heap::kTrueValueRootIndex);
+ __ add(inline_site, inline_site, Operand(kDeltaToLoadBoolResult));
+ // Get the boolean result location in scratch and patch it.
+ __ GetRelocatedValueLocation(inline_site, scratch);
+ __ str(r0, MemOperand(scratch));
+
+ if (!ReturnTrueFalseObject()) {
+ __ mov(r0, Operand(Smi::FromInt(0)));
+ }
+ }
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&is_not_instance);
+ if (!HasCallSiteInlineCheck()) {
+ __ mov(r0, Operand(Smi::FromInt(1)));
+ __ StoreRoot(r0, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ // Patch the call site to return false.
+ __ LoadRoot(r0, Heap::kFalseValueRootIndex);
+ __ add(inline_site, inline_site, Operand(kDeltaToLoadBoolResult));
+ // Get the boolean result location in scratch and patch it.
+ __ GetRelocatedValueLocation(inline_site, scratch);
+ __ str(r0, MemOperand(scratch));
+
+ if (!ReturnTrueFalseObject()) {
+ __ mov(r0, Operand(Smi::FromInt(1)));
+ }
+ }
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
+
+ Label object_not_null, object_not_null_or_smi;
+ __ bind(&not_js_object);
+ // Before null, smi and string value checks, check that the rhs is a function
+ // as for a non-function rhs an exception needs to be thrown.
+ __ JumpIfSmi(function, &slow);
+ __ CompareObjectType(function, scratch2, scratch, JS_FUNCTION_TYPE);
+ __ b(ne, &slow);
+
+ // Null is not instance of anything.
+ __ cmp(scratch, Operand(masm->isolate()->factory()->null_value()));
+ __ b(ne, &object_not_null);
+ __ mov(r0, Operand(Smi::FromInt(1)));
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&object_not_null);
+ // Smi values are not instances of anything.
+ __ JumpIfNotSmi(object, &object_not_null_or_smi);
+ __ mov(r0, Operand(Smi::FromInt(1)));
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&object_not_null_or_smi);
+ // String values are not instances of anything.
+ __ IsObjectJSStringType(object, scratch, &slow);
+ __ mov(r0, Operand(Smi::FromInt(1)));
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
+
+ // Slow-case. Tail call builtin.
+ __ bind(&slow);
+ if (!ReturnTrueFalseObject()) {
+ if (HasArgsInRegisters()) {
+ __ Push(r0, r1);
+ }
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
+ } else {
+ {
+ 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);
+ __ Ret(HasArgsInRegisters() ? 0 : 2);
+ }
+}
+
+
+void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
+ Label miss;
+ Register receiver;
+ if (kind() == Code::KEYED_LOAD_IC) {
+ // ----------- S t a t e -------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ __ cmp(r0, Operand(masm->isolate()->factory()->prototype_string()));
+ __ b(ne, &miss);
+ receiver = r1;
+ } else {
+ ASSERT(kind() == Code::LOAD_IC);
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- r0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ receiver = r0;
+ }
+
+ StubCompiler::GenerateLoadFunctionPrototype(masm, receiver, r3, r4, &miss);
+ __ bind(&miss);
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void StringLengthStub::Generate(MacroAssembler* masm) {
+ Label miss;
+ Register receiver;
+ if (kind() == Code::KEYED_LOAD_IC) {
+ // ----------- S t a t e -------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ __ cmp(r0, Operand(masm->isolate()->factory()->length_string()));
+ __ b(ne, &miss);
+ receiver = r1;
+ } else {
+ ASSERT(kind() == Code::LOAD_IC);
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- r0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ receiver = r0;
+ }
+
+ StubCompiler::GenerateLoadStringLength(masm, receiver, r3, r4, &miss,
+ support_wrapper_);
+
+ __ bind(&miss);
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void StoreArrayLengthStub::Generate(MacroAssembler* masm) {
+ // 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;
+
+ Register receiver;
+ Register value;
+ if (kind() == Code::KEYED_STORE_IC) {
+ // ----------- S t a t e -------------
+ // -- lr : return address
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -----------------------------------
+ __ cmp(r1, Operand(masm->isolate()->factory()->length_string()));
+ __ b(ne, &miss);
+ receiver = r2;
+ value = r0;
+ } else {
+ ASSERT(kind() == Code::STORE_IC);
+ // ----------- S t a t e -------------
+ // -- lr : return address
+ // -- r0 : value
+ // -- r1 : receiver
+ // -- r2 : key
+ // -----------------------------------
+ receiver = r1;
+ value = r0;
+ }
+ Register scratch = r3;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Check that the object is a JS array.
+ __ CompareObjectType(receiver, scratch, scratch, JS_ARRAY_TYPE);
+ __ b(ne, &miss);
+
+ // Check that elements are FixedArray.
+ // We rely on StoreIC_ArrayLength below to deal with all types of
+ // fast elements (including COW).
+ __ ldr(scratch, FieldMemOperand(receiver, JSArray::kElementsOffset));
+ __ 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);
+
+ // Prepare tail call to StoreIC_ArrayLength.
+ __ Push(receiver, value);
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+
+ __ bind(&miss);
+
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+Register InstanceofStub::left() { return r0; }
+
+
+Register InstanceofStub::right() { return r1; }
+
+
+void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+ // The displacement is the offset of the last parameter (if any)
+ // relative to the frame pointer.
+ const int kDisplacement =
+ StandardFrameConstants::kCallerSPOffset - kPointerSize;
+
+ // Check that the key is a smi.
+ Label slow;
+ __ JumpIfNotSmi(r1, &slow);
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor;
+ __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
+ __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ b(eq, &adaptor);
+
+ // Check index against formal parameters count limit passed in
+ // through register r0. Use unsigned comparison to get negative
+ // check for free.
+ __ cmp(r1, r0);
+ __ b(hs, &slow);
+
+ // Read the argument from the stack and return it.
+ __ sub(r3, r0, r1);
+ __ add(r3, fp, Operand::PointerOffsetFromSmiKey(r3));
+ __ ldr(r0, MemOperand(r3, kDisplacement));
+ __ Jump(lr);
+
+ // Arguments adaptor case: Check index against actual arguments
+ // limit found in the arguments adaptor frame. Use unsigned
+ // comparison to get negative check for free.
+ __ bind(&adaptor);
+ __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ cmp(r1, r0);
+ __ b(cs, &slow);
+
+ // Read the argument from the adaptor frame and return it.
+ __ sub(r3, r0, r1);
+ __ add(r3, r2, Operand::PointerOffsetFromSmiKey(r3));
+ __ ldr(r0, MemOperand(r3, kDisplacement));
+ __ Jump(lr);
+
+ // Slow-case: Handle non-smi or out-of-bounds access to arguments
+ // by calling the runtime system.
+ __ bind(&slow);
+ __ push(r1);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
+ // sp[0] : number of parameters
+ // sp[4] : receiver displacement
+ // sp[8] : function
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label runtime;
+ __ ldr(r3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(r2, MemOperand(r3, StandardFrameConstants::kContextOffset));
+ __ cmp(r2, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ b(ne, &runtime);
+
+ // Patch the arguments.length and the parameters pointer in the current frame.
+ __ ldr(r2, MemOperand(r3, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ str(r2, MemOperand(sp, 0 * kPointerSize));
+ __ add(r3, r3, Operand(r2, LSL, 1));
+ __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
+ __ str(r3, MemOperand(sp, 1 * kPointerSize));
+
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
+ // Stack layout:
+ // sp[0] : number of parameters (tagged)
+ // sp[4] : address of receiver argument
+ // sp[8] : function
+ // Registers used over whole function:
+ // r6 : allocated object (tagged)
+ // r9 : mapped parameter count (tagged)
+
+ __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
+ // r1 = parameter count (tagged)
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label runtime;
+ Label adaptor_frame, try_allocate;
+ __ ldr(r3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(r2, MemOperand(r3, StandardFrameConstants::kContextOffset));
+ __ cmp(r2, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ b(eq, &adaptor_frame);
+
+ // No adaptor, parameter count = argument count.
+ __ mov(r2, r1);
+ __ b(&try_allocate);
+
+ // We have an adaptor frame. Patch the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ ldr(r2, MemOperand(r3, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ add(r3, r3, Operand(r2, LSL, 1));
+ __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
+ __ str(r3, MemOperand(sp, 1 * kPointerSize));
+
+ // r1 = parameter count (tagged)
+ // r2 = argument count (tagged)
+ // Compute the mapped parameter count = min(r1, r2) in r1.
+ __ cmp(r1, Operand(r2));
+ __ mov(r1, Operand(r2), LeaveCC, gt);
+
+ __ bind(&try_allocate);
+
+ // Compute the sizes of backing store, parameter map, and arguments object.
+ // 1. Parameter map, has 2 extra words containing context and backing store.
+ const int kParameterMapHeaderSize =
+ FixedArray::kHeaderSize + 2 * kPointerSize;
+ // If there are no mapped parameters, we do not need the parameter_map.
+ __ cmp(r1, Operand(Smi::FromInt(0)));
+ __ mov(r9, Operand::Zero(), LeaveCC, eq);
+ __ mov(r9, Operand(r1, LSL, 1), LeaveCC, ne);
+ __ add(r9, r9, Operand(kParameterMapHeaderSize), LeaveCC, ne);
+
+ // 2. Backing store.
+ __ add(r9, r9, Operand(r2, LSL, 1));
+ __ add(r9, r9, Operand(FixedArray::kHeaderSize));
+
+ // 3. Arguments object.
+ __ add(r9, r9, Operand(Heap::kArgumentsObjectSize));
+
+ // Do the allocation of all three objects in one go.
+ __ Allocate(r9, r0, r3, r4, &runtime, TAG_OBJECT);
+
+ // r0 = address of new object(s) (tagged)
+ // r2 = argument count (tagged)
+ // Get the arguments boilerplate from the current native context into r4.
+ const int kNormalOffset =
+ Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
+ const int kAliasedOffset =
+ Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX);
+
+ __ ldr(r4, MemOperand(r8, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ ldr(r4, FieldMemOperand(r4, GlobalObject::kNativeContextOffset));
+ __ cmp(r1, Operand::Zero());
+ __ ldr(r4, MemOperand(r4, kNormalOffset), eq);
+ __ ldr(r4, MemOperand(r4, kAliasedOffset), ne);
+
+ // r0 = address of new object (tagged)
+ // r1 = mapped parameter count (tagged)
+ // r2 = argument count (tagged)
+ // r4 = address of boilerplate object (tagged)
+ // Copy the JS object part.
+ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+ __ ldr(r3, FieldMemOperand(r4, i));
+ __ str(r3, FieldMemOperand(r0, i));
+ }
+
+ // Set up the callee in-object property.
+ STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
+ __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
+ const int kCalleeOffset = JSObject::kHeaderSize +
+ Heap::kArgumentsCalleeIndex * kPointerSize;
+ __ str(r3, FieldMemOperand(r0, kCalleeOffset));
+
+ // Use the length (smi tagged) and set that as an in-object property too.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ const int kLengthOffset = JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize;
+ __ str(r2, FieldMemOperand(r0, kLengthOffset));
+
+ // 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));
+ __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
+
+ // r0 = address of new object (tagged)
+ // r1 = mapped parameter count (tagged)
+ // r2 = argument count (tagged)
+ // r4 = address of parameter map or backing store (tagged)
+ // Initialize parameter map. If there are no mapped arguments, we're done.
+ Label skip_parameter_map;
+ __ cmp(r1, Operand(Smi::FromInt(0)));
+ // Move backing store address to r3, because it is
+ // expected there when filling in the unmapped arguments.
+ __ mov(r3, r4, LeaveCC, eq);
+ __ b(eq, &skip_parameter_map);
+
+ __ LoadRoot(r6, Heap::kNonStrictArgumentsElementsMapRootIndex);
+ __ str(r6, FieldMemOperand(r4, FixedArray::kMapOffset));
+ __ add(r6, r1, Operand(Smi::FromInt(2)));
+ __ str(r6, FieldMemOperand(r4, FixedArray::kLengthOffset));
+ __ str(r8, FieldMemOperand(r4, FixedArray::kHeaderSize + 0 * kPointerSize));
+ __ add(r6, r4, Operand(r1, LSL, 1));
+ __ add(r6, r6, Operand(kParameterMapHeaderSize));
+ __ str(r6, FieldMemOperand(r4, FixedArray::kHeaderSize + 1 * kPointerSize));
+
+ // Copy the parameter slots and the holes in the arguments.
+ // We need to fill in mapped_parameter_count slots. They index the context,
+ // where parameters are stored in reverse order, at
+ // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
+ // The mapped parameter thus need to get indices
+ // MIN_CONTEXT_SLOTS+parameter_count-1 ..
+ // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
+ // We loop from right to left.
+ Label parameters_loop, parameters_test;
+ __ mov(r6, r1);
+ __ ldr(r9, MemOperand(sp, 0 * kPointerSize));
+ __ add(r9, r9, Operand(Smi::FromInt(Context::MIN_CONTEXT_SLOTS)));
+ __ sub(r9, r9, Operand(r1));
+ __ LoadRoot(r7, Heap::kTheHoleValueRootIndex);
+ __ add(r3, r4, Operand(r6, LSL, 1));
+ __ add(r3, r3, Operand(kParameterMapHeaderSize));
+
+ // r6 = loop variable (tagged)
+ // r1 = mapping index (tagged)
+ // r3 = address of backing store (tagged)
+ // r4 = address of parameter map (tagged)
+ // r5 = temporary scratch (a.o., for address calculation)
+ // r7 = the hole value
+ __ jmp(&parameters_test);
+
+ __ bind(&parameters_loop);
+ __ sub(r6, r6, Operand(Smi::FromInt(1)));
+ __ mov(r5, Operand(r6, LSL, 1));
+ __ add(r5, r5, Operand(kParameterMapHeaderSize - kHeapObjectTag));
+ __ str(r9, MemOperand(r4, r5));
+ __ sub(r5, r5, Operand(kParameterMapHeaderSize - FixedArray::kHeaderSize));
+ __ str(r7, MemOperand(r3, r5));
+ __ add(r9, r9, Operand(Smi::FromInt(1)));
+ __ bind(&parameters_test);
+ __ cmp(r6, Operand(Smi::FromInt(0)));
+ __ b(ne, &parameters_loop);
+
+ __ bind(&skip_parameter_map);
+ // r2 = argument count (tagged)
+ // r3 = address of backing store (tagged)
+ // r5 = scratch
+ // Copy arguments header and remaining slots (if there are any).
+ __ LoadRoot(r5, Heap::kFixedArrayMapRootIndex);
+ __ str(r5, FieldMemOperand(r3, FixedArray::kMapOffset));
+ __ str(r2, FieldMemOperand(r3, FixedArray::kLengthOffset));
+
+ Label arguments_loop, arguments_test;
+ __ mov(r9, r1);
+ __ ldr(r4, MemOperand(sp, 1 * kPointerSize));
+ __ sub(r4, r4, Operand(r9, LSL, 1));
+ __ jmp(&arguments_test);
+
+ __ bind(&arguments_loop);
+ __ sub(r4, r4, Operand(kPointerSize));
+ __ ldr(r6, MemOperand(r4, 0));
+ __ add(r5, r3, Operand(r9, LSL, 1));
+ __ str(r6, FieldMemOperand(r5, FixedArray::kHeaderSize));
+ __ add(r9, r9, Operand(Smi::FromInt(1)));
+
+ __ bind(&arguments_test);
+ __ cmp(r9, Operand(r2));
+ __ b(lt, &arguments_loop);
+
+ // Return and remove the on-stack parameters.
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ // Do the runtime call to allocate the arguments object.
+ // r2 = argument count (tagged)
+ __ bind(&runtime);
+ __ str(r2, MemOperand(sp, 0 * kPointerSize)); // Patch argument count.
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
+ // sp[0] : number of parameters
+ // sp[4] : receiver displacement
+ // sp[8] : function
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor_frame, try_allocate, runtime;
+ __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
+ __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ b(eq, &adaptor_frame);
+
+ // Get the length from the frame.
+ __ ldr(r1, MemOperand(sp, 0));
+ __ b(&try_allocate);
+
+ // Patch the arguments.length and the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ str(r1, MemOperand(sp, 0));
+ __ add(r3, r2, Operand::PointerOffsetFromSmiKey(r1));
+ __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
+ __ str(r3, MemOperand(sp, 1 * kPointerSize));
+
+ // Try the new space allocation. Start out with computing the size
+ // of the arguments object and the elements array in words.
+ Label add_arguments_object;
+ __ bind(&try_allocate);
+ __ SmiUntag(r1, SetCC);
+ __ b(eq, &add_arguments_object);
+ __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize));
+ __ bind(&add_arguments_object);
+ __ add(r1, r1, Operand(Heap::kArgumentsObjectSizeStrict / kPointerSize));
+
+ // Do the allocation of both objects in one go.
+ __ Allocate(r1, r0, r2, r3, &runtime,
+ static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
+
+ // Get the arguments boilerplate from the current native context.
+ __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ ldr(r4, FieldMemOperand(r4, GlobalObject::kNativeContextOffset));
+ __ ldr(r4, MemOperand(r4, Context::SlotOffset(
+ Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX)));
+
+ // Copy the JS object part.
+ __ CopyFields(r0, r4, d0, JSObject::kHeaderSize / kPointerSize);
+
+ // Get the length (smi tagged) and set that as an in-object property too.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
+ __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize));
+
+ // If there are no actual arguments, we're done.
+ Label done;
+ __ cmp(r1, Operand::Zero());
+ __ b(eq, &done);
+
+ // Get the parameters pointer from the stack.
+ __ ldr(r2, MemOperand(sp, 1 * kPointerSize));
+
+ // 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));
+ __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex);
+ __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset));
+ __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset));
+ __ SmiUntag(r1);
+
+ // Copy the fixed array slots.
+ Label loop;
+ // 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.
+ // Pre-decrement in order to skip receiver.
+ __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex));
+ // Post-increment r4 with kPointerSize on each iteration.
+ __ str(r3, MemOperand(r4, kPointerSize, PostIndex));
+ __ sub(r1, r1, Operand(1));
+ __ cmp(r1, Operand::Zero());
+ __ b(ne, &loop);
+
+ // Return and remove the on-stack parameters.
+ __ bind(&done);
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ // Do the runtime call to allocate the arguments object.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
+}
+
+
+void RegExpExecStub::Generate(MacroAssembler* masm) {
+ // Just jump directly to runtime if native RegExp is not selected at compile
+ // time or if regexp entry in generated code is turned off runtime switch or
+ // at compilation.
+#ifdef V8_INTERPRETED_REGEXP
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+#else // V8_INTERPRETED_REGEXP
+
+ // Stack frame on entry.
+ // sp[0]: last_match_info (expected JSArray)
+ // sp[4]: previous index
+ // sp[8]: subject string
+ // sp[12]: JSRegExp object
+
+ const int kLastMatchInfoOffset = 0 * kPointerSize;
+ const int kPreviousIndexOffset = 1 * kPointerSize;
+ const int kSubjectOffset = 2 * kPointerSize;
+ const int kJSRegExpOffset = 3 * kPointerSize;
+
+ Label runtime;
+ // Allocation of registers for this function. These are in callee save
+ // registers and will be preserved by the call to the native RegExp code, as
+ // this code is called using the normal C calling convention. When calling
+ // directly from generated code the native RegExp code will not do a GC and
+ // therefore the content of these registers are safe to use after the call.
+ Register subject = r4;
+ Register regexp_data = r5;
+ Register last_match_info_elements = r6;
+
+ // Ensure that a RegExp stack is allocated.
+ Isolate* isolate = masm->isolate();
+ ExternalReference address_of_regexp_stack_memory_address =
+ ExternalReference::address_of_regexp_stack_memory_address(isolate);
+ ExternalReference address_of_regexp_stack_memory_size =
+ ExternalReference::address_of_regexp_stack_memory_size(isolate);
+ __ mov(r0, Operand(address_of_regexp_stack_memory_size));
+ __ ldr(r0, MemOperand(r0, 0));
+ __ cmp(r0, Operand::Zero());
+ __ b(eq, &runtime);
+
+ // Check that the first argument is a JSRegExp object.
+ __ ldr(r0, MemOperand(sp, kJSRegExpOffset));
+ __ JumpIfSmi(r0, &runtime);
+ __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
+ __ b(ne, &runtime);
+
+ // Check that the RegExp has been compiled (data contains a fixed array).
+ __ ldr(regexp_data, FieldMemOperand(r0, JSRegExp::kDataOffset));
+ if (FLAG_debug_code) {
+ __ SmiTst(regexp_data);
+ __ Check(ne, kUnexpectedTypeForRegExpDataFixedArrayExpected);
+ __ CompareObjectType(regexp_data, r0, r0, FIXED_ARRAY_TYPE);
+ __ Check(eq, kUnexpectedTypeForRegExpDataFixedArrayExpected);
+ }
+
+ // regexp_data: RegExp data (FixedArray)
+ // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
+ __ ldr(r0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
+ __ cmp(r0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
+ __ b(ne, &runtime);
+
+ // regexp_data: RegExp data (FixedArray)
+ // Check that the number of captures fit in the static offsets vector buffer.
+ __ ldr(r2,
+ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
+ // Check (number_of_captures + 1) * 2 <= offsets vector size
+ // Or number_of_captures * 2 <= offsets vector size - 2
+ // Multiplying by 2 comes for free since r2 is smi-tagged.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
+ __ cmp(r2, Operand(Isolate::kJSRegexpStaticOffsetsVectorSize - 2));
+ __ b(hi, &runtime);
+
+ // Reset offset for possibly sliced string.
+ __ mov(r9, Operand::Zero());
+ __ ldr(subject, MemOperand(sp, kSubjectOffset));
+ __ JumpIfSmi(subject, &runtime);
+ __ mov(r3, subject); // Make a copy of the original subject string.
+ __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
+ __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
+ // subject: subject string
+ // r3: subject string
+ // r0: subject string instance type
+ // regexp_data: RegExp data (FixedArray)
+ // Handle subject string according to its encoding and representation:
+ // (1) Sequential string? If yes, go to (5).
+ // (2) Anything but sequential or cons? If yes, go to (6).
+ // (3) Cons string. If the string is flat, replace subject with first string.
+ // Otherwise bailout.
+ // (4) Is subject external? If yes, go to (7).
+ // (5) Sequential string. Load regexp code according to encoding.
+ // (E) Carry on.
+ /// [...]
+
+ // Deferred code at the end of the stub:
+ // (6) Not a long external string? If yes, go to (8).
+ // (7) External string. Make it, offset-wise, look like a sequential string.
+ // Go to (5).
+ // (8) Short external string or not a string? If yes, bail out to runtime.
+ // (9) Sliced string. Replace subject with parent. Go to (4).
+
+ Label seq_string /* 5 */, external_string /* 7 */,
+ check_underlying /* 4 */, not_seq_nor_cons /* 6 */,
+ not_long_external /* 8 */;
+
+ // (1) Sequential string? If yes, go to (5).
+ __ and_(r1,
+ r0,
+ Operand(kIsNotStringMask |
+ kStringRepresentationMask |
+ kShortExternalStringMask),
+ SetCC);
+ STATIC_ASSERT((kStringTag | kSeqStringTag) == 0);
+ __ b(eq, &seq_string); // Go to (5).
+
+ // (2) Anything but sequential or cons? If yes, go to (6).
+ STATIC_ASSERT(kConsStringTag < kExternalStringTag);
+ STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
+ STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
+ STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
+ __ cmp(r1, Operand(kExternalStringTag));
+ __ b(ge, &not_seq_nor_cons); // Go to (6).
+
+ // (3) Cons string. Check that it's flat.
+ // Replace subject with first string and reload instance type.
+ __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset));
+ __ CompareRoot(r0, Heap::kempty_stringRootIndex);
+ __ b(ne, &runtime);
+ __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
+
+ // (4) Is subject external? If yes, go to (7).
+ __ bind(&check_underlying);
+ __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
+ __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kSeqStringTag == 0);
+ __ tst(r0, Operand(kStringRepresentationMask));
+ // The underlying external string is never a short external string.
+ STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength);
+ STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength);
+ __ b(ne, &external_string); // Go to (7).
+
+ // (5) Sequential string. Load regexp code according to encoding.
+ __ bind(&seq_string);
+ // subject: sequential subject string (or look-alike, external string)
+ // r3: original subject string
+ // Load previous index and check range before r3 is overwritten. We have to
+ // use r3 instead of subject here because subject might have been only made
+ // to look like a sequential string when it actually is an external string.
+ __ ldr(r1, MemOperand(sp, kPreviousIndexOffset));
+ __ JumpIfNotSmi(r1, &runtime);
+ __ ldr(r3, FieldMemOperand(r3, String::kLengthOffset));
+ __ cmp(r3, Operand(r1));
+ __ b(ls, &runtime);
+ __ SmiUntag(r1);
+
+ STATIC_ASSERT(4 == kOneByteStringTag);
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ __ and_(r0, r0, Operand(kStringEncodingMask));
+ __ mov(r3, Operand(r0, ASR, 2), SetCC);
+ __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne);
+ __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
+
+ // (E) Carry on. String handling is done.
+ // r7: irregexp code
+ // Check that the irregexp code has been generated for the actual string
+ // encoding. If it has, the field contains a code object otherwise it contains
+ // a smi (code flushing support).
+ __ JumpIfSmi(r7, &runtime);
+
+ // r1: previous index
+ // r3: encoding of subject string (1 if ASCII, 0 if two_byte);
+ // r7: code
+ // subject: Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // All checks done. Now push arguments for native regexp code.
+ __ IncrementCounter(isolate->counters()->regexp_entry_native(), 1, r0, r2);
+
+ // Isolates: note we add an additional parameter here (isolate pointer).
+ const int kRegExpExecuteArguments = 9;
+ const int kParameterRegisters = 4;
+ __ EnterExitFrame(false, kRegExpExecuteArguments - kParameterRegisters);
+
+ // Stack pointer now points to cell where return address is to be written.
+ // Arguments are before that on the stack or in registers.
+
+ // Argument 9 (sp[20]): Pass current isolate address.
+ __ mov(r0, Operand(ExternalReference::isolate_address(isolate)));
+ __ str(r0, MemOperand(sp, 5 * kPointerSize));
+
+ // Argument 8 (sp[16]): Indicate that this is a direct call from JavaScript.
+ __ mov(r0, Operand(1));
+ __ str(r0, MemOperand(sp, 4 * kPointerSize));
+
+ // Argument 7 (sp[12]): Start (high end) of backtracking stack memory area.
+ __ mov(r0, Operand(address_of_regexp_stack_memory_address));
+ __ ldr(r0, MemOperand(r0, 0));
+ __ mov(r2, Operand(address_of_regexp_stack_memory_size));
+ __ ldr(r2, MemOperand(r2, 0));
+ __ add(r0, r0, Operand(r2));
+ __ str(r0, MemOperand(sp, 3 * kPointerSize));
+
+ // Argument 6: Set the number of capture registers to zero to force global
+ // regexps to behave as non-global. This does not affect non-global regexps.
+ __ mov(r0, Operand::Zero());
+ __ str(r0, MemOperand(sp, 2 * kPointerSize));
+
+ // Argument 5 (sp[4]): static offsets vector buffer.
+ __ mov(r0,
+ Operand(ExternalReference::address_of_static_offsets_vector(isolate)));
+ __ str(r0, 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).
+ __ 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
+ // sizes below the previous sp. (Because creating a new stack frame pushes
+ // the previous fp onto the stack and moves up sp by 2 * kPointerSize.)
+ __ ldr(subject, MemOperand(fp, kSubjectOffset + 2 * kPointerSize));
+ // If slice offset is not 0, load the length from the original sliced string.
+ // Argument 4, r3: End of string data
+ // Argument 3, r2: Start of string data
+ // Prepare start and end index of the input.
+ __ add(r9, r8, Operand(r9, LSL, r3));
+ __ add(r2, r9, Operand(r1, LSL, r3));
+
+ __ ldr(r8, FieldMemOperand(subject, String::kLengthOffset));
+ __ SmiUntag(r8);
+ __ add(r3, r9, Operand(r8, LSL, r3));
+
+ // Argument 2 (r1): Previous index.
+ // Already there
+
+ // Argument 1 (r0): Subject string.
+ __ mov(r0, subject);
+
+ // Locate the code entry and call it.
+ __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag));
+ DirectCEntryStub stub;
+ stub.GenerateCall(masm, r7);
+
+ __ LeaveExitFrame(false, no_reg);
+
+ // r0: result
+ // subject: subject string (callee saved)
+ // regexp_data: RegExp data (callee saved)
+ // last_match_info_elements: Last match info elements (callee saved)
+ // Check the result.
+ Label success;
+ __ cmp(r0, Operand(1));
+ // We expect exactly one result since we force the called regexp to behave
+ // as non-global.
+ __ b(eq, &success);
+ Label failure;
+ __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE));
+ __ b(eq, &failure);
+ __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
+ // If not exception it can only be retry. Handle that in the runtime system.
+ __ b(ne, &runtime);
+ // Result must now be exception. If there is no pending exception already a
+ // 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(isolate->factory()->the_hole_value()));
+ __ mov(r2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ ldr(r0, MemOperand(r2, 0));
+ __ cmp(r0, r1);
+ __ b(eq, &runtime);
+
+ __ str(r1, MemOperand(r2, 0)); // Clear pending exception.
+
+ // Check if the exception is a termination. If so, throw as uncatchable.
+ __ CompareRoot(r0, Heap::kTerminationExceptionRootIndex);
+
+ Label termination_exception;
+ __ b(eq, &termination_exception);
+
+ __ Throw(r0);
+
+ __ bind(&termination_exception);
+ __ ThrowUncatchable(r0);
+
+ __ bind(&failure);
+ // For failure and exception return null.
+ __ mov(r0, Operand(masm->isolate()->factory()->null_value()));
+ __ add(sp, sp, Operand(4 * kPointerSize));
+ __ Ret();
+
+ // Process the result from the native regexp code.
+ __ bind(&success);
+ __ ldr(r1,
+ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
+ // Calculate number of capture registers (number_of_captures + 1) * 2.
+ // Multiplying by 2 comes for free since r1 is smi-tagged.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ __ add(r1, r1, Operand(2)); // r1 was a smi.
+
+ __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
+ __ JumpIfSmi(r0, &runtime);
+ __ CompareObjectType(r0, r2, r2, JS_ARRAY_TYPE);
+ __ b(ne, &runtime);
+ // Check that the JSArray is in fast case.
+ __ ldr(last_match_info_elements,
+ FieldMemOperand(r0, JSArray::kElementsOffset));
+ __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
+ __ CompareRoot(r0, Heap::kFixedArrayMapRootIndex);
+ __ b(ne, &runtime);
+ // Check that the last match info has space for the capture registers and the
+ // additional information.
+ __ ldr(r0,
+ FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
+ __ add(r2, r1, Operand(RegExpImpl::kLastMatchOverhead));
+ __ cmp(r2, Operand::SmiUntag(r0));
+ __ b(gt, &runtime);
+
+ // r1: number of capture registers
+ // r4: subject string
+ // Store the capture count.
+ __ SmiTag(r2, r1);
+ __ str(r2, FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastCaptureCountOffset));
+ // Store last subject and last input.
+ __ str(subject,
+ FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastSubjectOffset));
+ __ mov(r2, subject);
+ __ RecordWriteField(last_match_info_elements,
+ RegExpImpl::kLastSubjectOffset,
+ subject,
+ r7,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs);
+ __ mov(subject, r2);
+ __ str(subject,
+ FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastInputOffset));
+ __ 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 =
+ ExternalReference::address_of_static_offsets_vector(isolate);
+ __ mov(r2, Operand(address_of_static_offsets_vector));
+
+ // r1: number of capture registers
+ // r2: offsets vector
+ Label next_capture, done;
+ // Capture register counter starts from number of capture registers and
+ // counts down until wraping after zero.
+ __ add(r0,
+ last_match_info_elements,
+ Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
+ __ bind(&next_capture);
+ __ sub(r1, r1, Operand(1), SetCC);
+ __ b(mi, &done);
+ // Read the value from the static offsets vector buffer.
+ __ ldr(r3, MemOperand(r2, kPointerSize, PostIndex));
+ // Store the smi value in the last match info.
+ __ SmiTag(r3);
+ __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
+ __ jmp(&next_capture);
+ __ bind(&done);
+
+ // Return last match info.
+ __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset));
+ __ add(sp, sp, Operand(4 * kPointerSize));
+ __ Ret();
+
+ // Do the runtime call to execute the regexp.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+
+ // Deferred code for string handling.
+ // (6) Not a long external string? If yes, go to (8).
+ __ bind(&not_seq_nor_cons);
+ // Compare flags are still set.
+ __ b(gt, &not_long_external); // Go to (8).
+
+ // (7) External string. Make it, offset-wise, look like a sequential string.
+ __ 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, kExternalStringExpectedButNotFound);
+ }
+ __ ldr(subject,
+ FieldMemOperand(subject, ExternalString::kResourceDataOffset));
+ // Move the pointer so that offset-wise, it looks like a sequential string.
+ STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
+ __ sub(subject,
+ subject,
+ Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ __ jmp(&seq_string); // Go to (5).
+
+ // (8) Short external string or not a string? If yes, bail out to runtime.
+ __ bind(&not_long_external);
+ STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
+ __ tst(r1, Operand(kIsNotStringMask | kShortExternalStringMask));
+ __ b(ne, &runtime);
+
+ // (9) Sliced string. Replace subject with parent. Go to (4).
+ // Load offset into r9 and replace subject string with parent.
+ __ ldr(r9, FieldMemOperand(subject, SlicedString::kOffsetOffset));
+ __ SmiUntag(r9);
+ __ ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
+ __ jmp(&check_underlying); // Go to (4).
+#endif // V8_INTERPRETED_REGEXP
+}
+
+
+void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
+ const int kMaxInlineLength = 100;
+ Label slowcase;
+ Label done;
+ Factory* factory = masm->isolate()->factory();
+
+ __ ldr(r1, MemOperand(sp, kPointerSize * 2));
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ __ JumpIfNotSmi(r1, &slowcase);
+ __ cmp(r1, Operand(Smi::FromInt(kMaxInlineLength)));
+ __ b(hi, &slowcase);
+ // Smi-tagging is equivalent to multiplying by 2.
+ // Allocate RegExpResult followed by FixedArray with size in ebx.
+ // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
+ // Elements: [Map][Length][..elements..]
+ // Size of JSArray with two in-object properties and the header of a
+ // FixedArray.
+ int objects_size =
+ (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
+ __ SmiUntag(r5, r1);
+ __ add(r2, r5, Operand(objects_size));
+ __ Allocate(
+ r2, // In: Size, in words.
+ r0, // Out: Start of allocation (tagged).
+ r3, // Scratch register.
+ r4, // Scratch register.
+ &slowcase,
+ static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
+ // r0: Start of allocated area, object-tagged.
+ // r1: Number of elements in array, as smi.
+ // r5: Number of elements, untagged.
+
+ // Set JSArray map to global.regexp_result_map().
+ // Set empty properties FixedArray.
+ // Set elements to point to FixedArray allocated right after the JSArray.
+ // Interleave operations for better latency.
+ __ ldr(r2, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+ __ add(r3, r0, Operand(JSRegExpResult::kSize));
+ __ mov(r4, Operand(factory->empty_fixed_array()));
+ __ ldr(r2, FieldMemOperand(r2, GlobalObject::kNativeContextOffset));
+ __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
+ __ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
+ __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
+ __ str(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
+
+ // 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));
+ __ str(r2, FieldMemOperand(r0, JSRegExpResult::kIndexOffset));
+ __ str(r6, FieldMemOperand(r0, JSArray::kLengthOffset));
+
+ // Fill out the elements FixedArray.
+ // r0: JSArray, tagged.
+ // r3: FixedArray, tagged.
+ // r5: Number of elements in array, untagged.
+
+ // Set map.
+ __ mov(r2, Operand(factory->fixed_array_map()));
+ __ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
+ // Set FixedArray length.
+ __ SmiTag(r6, r5);
+ __ str(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
+ // Fill contents of fixed-array with undefined.
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // Fill fixed array elements with undefined.
+ // r0: JSArray, tagged.
+ // r2: undefined.
+ // r3: Start of elements in FixedArray.
+ // r5: Number of elements to fill.
+ Label loop;
+ __ cmp(r5, Operand::Zero());
+ __ bind(&loop);
+ __ 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);
+
+ __ bind(&done);
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ __ bind(&slowcase);
+ __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
+}
+
+
+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 initialize, done, miss, megamorphic, not_array_function;
+
+ 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, Cell::kValueOffset));
+
+ // A monomorphic cache hit or an already megamorphic state: invoke the
+ // function without changing the state.
+ __ cmp(r3, r1);
+ __ b(eq, &done);
+
+ // If we came here, we need to see if we are the array function.
+ // If we didn't have a matching function, and we didn't find the megamorph
+ // sentinel, then we have in the cell either some other function or an
+ // AllocationSite. Do a map check on the object in ecx.
+ Handle<Map> allocation_site_map(
+ masm->isolate()->heap()->allocation_site_map(),
+ masm->isolate());
+ __ ldr(r5, FieldMemOperand(r3, 0));
+ __ CompareRoot(r5, Heap::kAllocationSiteMapRootIndex);
+ __ b(ne, &miss);
+
+ // Make sure the function is the Array() function
+ __ LoadArrayFunction(r3);
+ __ cmp(r1, r3);
+ __ b(ne, &megamorphic);
+ __ jmp(&done);
+
+ __ bind(&miss);
+
+ // A monomorphic miss (i.e, here the cache is not uninitialized) goes
+ // megamorphic.
+ __ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
+ __ b(eq, &initialize);
+ // MegamorphicSentinel is an immortal immovable object (undefined) so no
+ // write-barrier is needed.
+ __ bind(&megamorphic);
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ str(ip, FieldMemOperand(r2, Cell::kValueOffset));
+ __ jmp(&done);
+
+ // An uninitialized cache is patched with the function or sentinel to
+ // indicate the ElementsKind if function is the Array constructor.
+ __ bind(&initialize);
+ // Make sure the function is the Array() function
+ __ LoadArrayFunction(r3);
+ __ cmp(r1, r3);
+ __ b(ne, &not_array_function);
+
+ // The target function is the Array constructor,
+ // Create an AllocationSite if we don't already have it, store it in the cell
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ __ push(r0);
+ __ push(r1);
+ __ push(r2);
+
+ CreateAllocationSiteStub create_stub;
+ __ CallStub(&create_stub);
+
+ __ pop(r2);
+ __ pop(r1);
+ __ pop(r0);
+ }
+ __ b(&done);
+
+ __ bind(&not_array_function);
+ __ str(r1, FieldMemOperand(r2, Cell::kValueOffset));
+ // 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
+ // indicated by passing the hole as the receiver to the call
+ // function stub.
+ if (ReceiverMightBeImplicit()) {
+ Label call;
+ // Get the receiver from the stack.
+ // function, receiver [, arguments]
+ __ ldr(r4, MemOperand(sp, argc_ * kPointerSize));
+ // Call as function is indicated with the hole.
+ __ CompareRoot(r4, Heap::kTheHoleValueRootIndex);
+ __ b(ne, &call);
+ // Patch the receiver on the stack with the global receiver object.
+ __ ldr(r3,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ ldr(r3, FieldMemOperand(r3, GlobalObject::kGlobalReceiverOffset));
+ __ str(r3, MemOperand(sp, argc_ * kPointerSize));
+ __ bind(&call);
+ }
+
+ // Check that the function is really a JavaScript function.
+ // r1: pushed function (to be verified)
+ __ JumpIfSmi(r1, &non_function);
+ // Get the map of the function object.
+ __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
+ __ b(ne, &slow);
+
+ if (RecordCallTarget()) {
+ GenerateRecordCallTarget(masm);
+ }
+
+ // Fast-case: Invoke the function now.
+ // r1: pushed function
+ ParameterCount actual(argc_);
+
+ if (ReceiverMightBeImplicit()) {
+ Label call_as_function;
+ __ CompareRoot(r4, Heap::kTheHoleValueRootIndex);
+ __ b(eq, &call_as_function);
+ __ InvokeFunction(r1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_METHOD);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(r1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
+
+ // 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.
+ ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(masm->isolate()),
+ masm->isolate()->heap()->undefined_value());
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ str(ip, FieldMemOperand(r2, Cell::kValueOffset));
+ }
+ // Check for function proxy.
+ __ cmp(r3, Operand(JS_FUNCTION_PROXY_TYPE));
+ __ b(ne, &non_function);
+ __ push(r1); // put proxy as additional argument
+ __ mov(r0, Operand(argc_ + 1, RelocInfo::NONE32));
+ __ mov(r2, Operand::Zero());
+ __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
+ __ SetCallKind(r5, 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);
+ __ str(r1, MemOperand(sp, argc_ * kPointerSize));
+ __ mov(r0, Operand(argc_)); // Set up the number of arguments.
+ __ mov(r2, Operand::Zero());
+ __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
+ __ SetCallKind(r5, CALL_AS_METHOD);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+}
+
+
+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.
+ Register jmp_reg = r3;
+ __ ldr(jmp_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(jmp_reg, FieldMemOperand(jmp_reg,
+ SharedFunctionInfo::kConstructStubOffset));
+ __ add(pc, jmp_reg, 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::Zero());
+ __ SetCallKind(r5, CALL_AS_METHOD);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+}
+
+
+// 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.
+ __ JumpIfSmi(object_, receiver_not_string_);
+
+ // Fetch the instance type of the receiver into result register.
+ __ ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
+ __ ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
+ // If the receiver is not a string trigger the non-string case.
+ __ tst(result_, Operand(kIsNotStringMask));
+ __ b(ne, receiver_not_string_);
+
+ // If the index is non-smi trigger the non-smi case.
+ __ JumpIfNotSmi(index_, &index_not_smi_);
+ __ bind(&got_smi_index_);
+
+ // Check for index out of range.
+ __ ldr(ip, FieldMemOperand(object_, String::kLengthOffset));
+ __ cmp(ip, Operand(index_));
+ __ b(ls, index_out_of_range_);
+
+ __ SmiUntag(index_);
+
+ StringCharLoadGenerator::Generate(masm,
+ object_,
+ index_,
+ result_,
+ &call_runtime_);
+
+ __ SmiTag(result_);
+ __ bind(&exit_);
+}
+
+
+void StringCharCodeAtGenerator::GenerateSlow(
+ MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase);
+
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_,
+ result_,
+ Heap::kHeapNumberMapRootIndex,
+ index_not_number_,
+ DONT_DO_SMI_CHECK);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_); // Consumed by runtime conversion function.
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+ __ 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(index_, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ jmp(&got_smi_index_);
+
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // is too complex (e.g., when the string needs to be flattened).
+ __ bind(&call_runtime_);
+ call_helper.BeforeCall(masm);
+ __ SmiTag(index_);
+ __ Push(object_, index_);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+ __ Move(result_, r0);
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase);
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharFromCodeGenerator
+
+void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiShiftSize == 0);
+ ASSERT(IsPowerOf2(String::kMaxOneByteCharCode + 1));
+ __ tst(code_,
+ Operand(kSmiTagMask |
+ ((~String::kMaxOneByteCharCode) << kSmiTagSize)));
+ __ b(ne, &slow_case_);
+
+ __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
+ // At this point code register contains smi tagged ASCII char code.
+ __ add(result_, result_, Operand::PointerOffsetFromSmiKey(code_));
+ __ ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
+ __ CompareRoot(result_, Heap::kUndefinedValueRootIndex);
+ __ b(eq, &slow_case_);
+ __ bind(&exit_);
+}
+
+
+void StringCharFromCodeGenerator::GenerateSlow(
+ MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ __ Abort(kUnexpectedFallthroughToCharFromCodeSlowCase);
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ __ Move(result_, r0);
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase);
+}
+
+
+void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii) {
+ Label loop;
+ Label done;
+ // This loop just copies one character at a time, as it is only used for very
+ // short strings.
+ if (!ascii) {
+ __ add(count, count, Operand(count), SetCC);
+ } else {
+ __ cmp(count, Operand::Zero());
+ }
+ __ b(eq, &done);
+
+ __ bind(&loop);
+ __ ldrb(scratch, MemOperand(src, 1, PostIndex));
+ // Perform sub between load and dependent store to get the load time to
+ // complete.
+ __ sub(count, count, Operand(1), SetCC);
+ __ strb(scratch, MemOperand(dest, 1, PostIndex));
+ // last iteration.
+ __ b(gt, &loop);
+
+ __ bind(&done);
+}
+
+
+enum CopyCharactersFlags {
+ COPY_ASCII = 1,
+ DEST_ALWAYS_ALIGNED = 2
+};
+
+
+void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Register scratch5,
+ int flags) {
+ bool ascii = (flags & COPY_ASCII) != 0;
+ bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
+
+ if (dest_always_aligned && FLAG_debug_code) {
+ // Check that destination is actually word aligned if the flag says
+ // that it is.
+ __ tst(dest, Operand(kPointerAlignmentMask));
+ __ Check(eq, kDestinationOfCopyNotAligned);
+ }
+
+ const int kReadAlignment = 4;
+ const int kReadAlignmentMask = kReadAlignment - 1;
+ // Ensure that reading an entire aligned word containing the last character
+ // of a string will not read outside the allocated area (because we pad up
+ // to kObjectAlignment).
+ STATIC_ASSERT(kObjectAlignment >= kReadAlignment);
+ // Assumes word reads and writes are little endian.
+ // Nothing to do for zero characters.
+ Label done;
+ if (!ascii) {
+ __ add(count, count, Operand(count), SetCC);
+ } else {
+ __ cmp(count, Operand::Zero());
+ }
+ __ b(eq, &done);
+
+ // Assume that you cannot read (or write) unaligned.
+ Label byte_loop;
+ // Must copy at least eight bytes, otherwise just do it one byte at a time.
+ __ cmp(count, Operand(8));
+ __ add(count, dest, Operand(count));
+ Register limit = count; // Read until src equals this.
+ __ b(lt, &byte_loop);
+
+ if (!dest_always_aligned) {
+ // Align dest by byte copying. Copies between zero and three bytes.
+ __ and_(scratch4, dest, Operand(kReadAlignmentMask), SetCC);
+ Label dest_aligned;
+ __ b(eq, &dest_aligned);
+ __ cmp(scratch4, Operand(2));
+ __ ldrb(scratch1, MemOperand(src, 1, PostIndex));
+ __ ldrb(scratch2, MemOperand(src, 1, PostIndex), le);
+ __ ldrb(scratch3, MemOperand(src, 1, PostIndex), lt);
+ __ strb(scratch1, MemOperand(dest, 1, PostIndex));
+ __ strb(scratch2, MemOperand(dest, 1, PostIndex), le);
+ __ strb(scratch3, MemOperand(dest, 1, PostIndex), lt);
+ __ bind(&dest_aligned);
+ }
+
+ Label simple_loop;
+
+ __ sub(scratch4, dest, Operand(src));
+ __ and_(scratch4, scratch4, Operand(0x03), SetCC);
+ __ b(eq, &simple_loop);
+ // Shift register is number of bits in a source word that
+ // must be combined with bits in the next source word in order
+ // to create a destination word.
+
+ // Complex loop for src/dst that are not aligned the same way.
+ {
+ Label loop;
+ __ mov(scratch4, Operand(scratch4, LSL, 3));
+ Register left_shift = scratch4;
+ __ and_(src, src, Operand(~3)); // Round down to load previous word.
+ __ ldr(scratch1, MemOperand(src, 4, PostIndex));
+ // Store the "shift" most significant bits of scratch in the least
+ // signficant bits (i.e., shift down by (32-shift)).
+ __ rsb(scratch2, left_shift, Operand(32));
+ Register right_shift = scratch2;
+ __ mov(scratch1, Operand(scratch1, LSR, right_shift));
+
+ __ bind(&loop);
+ __ ldr(scratch3, MemOperand(src, 4, PostIndex));
+ __ sub(scratch5, limit, Operand(dest));
+ __ orr(scratch1, scratch1, Operand(scratch3, LSL, left_shift));
+ __ str(scratch1, MemOperand(dest, 4, PostIndex));
+ __ mov(scratch1, Operand(scratch3, LSR, right_shift));
+ // Loop if four or more bytes left to copy.
+ // Compare to eight, because we did the subtract before increasing dst.
+ __ sub(scratch5, scratch5, Operand(8), SetCC);
+ __ b(ge, &loop);
+ }
+ // There is now between zero and three bytes left to copy (negative that
+ // number is in scratch5), and between one and three bytes already read into
+ // scratch1 (eight times that number in scratch4). We may have read past
+ // the end of the string, but because objects are aligned, we have not read
+ // past the end of the object.
+ // Find the minimum of remaining characters to move and preloaded characters
+ // and write those as bytes.
+ __ add(scratch5, scratch5, Operand(4), SetCC);
+ __ b(eq, &done);
+ __ cmp(scratch4, Operand(scratch5, LSL, 3), ne);
+ // Move minimum of bytes read and bytes left to copy to scratch4.
+ __ mov(scratch5, Operand(scratch4, LSR, 3), LeaveCC, lt);
+ // Between one and three (value in scratch5) characters already read into
+ // scratch ready to write.
+ __ cmp(scratch5, Operand(2));
+ __ strb(scratch1, MemOperand(dest, 1, PostIndex));
+ __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, ge);
+ __ strb(scratch1, MemOperand(dest, 1, PostIndex), ge);
+ __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, gt);
+ __ strb(scratch1, MemOperand(dest, 1, PostIndex), gt);
+ // Copy any remaining bytes.
+ __ b(&byte_loop);
+
+ // Simple loop.
+ // Copy words from src to dst, until less than four bytes left.
+ // Both src and dest are word aligned.
+ __ bind(&simple_loop);
+ {
+ Label loop;
+ __ bind(&loop);
+ __ ldr(scratch1, MemOperand(src, 4, PostIndex));
+ __ sub(scratch3, limit, Operand(dest));
+ __ str(scratch1, MemOperand(dest, 4, PostIndex));
+ // Compare to 8, not 4, because we do the substraction before increasing
+ // dest.
+ __ cmp(scratch3, Operand(8));
+ __ b(ge, &loop);
+ }
+
+ // Copy bytes from src to dst until dst hits limit.
+ __ bind(&byte_loop);
+ __ cmp(dest, Operand(limit));
+ __ ldrb(scratch1, MemOperand(src, 1, PostIndex), lt);
+ __ b(ge, &done);
+ __ strb(scratch1, MemOperand(dest, 1, PostIndex));
+ __ b(&byte_loop);
+
+ __ bind(&done);
+}
+
+
+void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Register scratch5,
+ Label* not_found) {
+ // Register scratch3 is the general scratch register in this function.
+ Register scratch = scratch3;
+
+ // Make sure that both characters are not digits as such strings has a
+ // different hash algorithm. Don't try to look for these in the string table.
+ Label not_array_index;
+ __ sub(scratch, c1, Operand(static_cast<int>('0')));
+ __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
+ __ b(hi, &not_array_index);
+ __ sub(scratch, c2, Operand(static_cast<int>('0')));
+ __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
+
+ // If check failed combine both characters into single halfword.
+ // This is required by the contract of the method: code at the
+ // not_found branch expects this combination in c1 register
+ __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
+ __ b(ls, not_found);
+
+ __ bind(&not_array_index);
+ // Calculate the two character string hash.
+ Register hash = scratch1;
+ StringHelper::GenerateHashInit(masm, hash, c1);
+ StringHelper::GenerateHashAddCharacter(masm, hash, c2);
+ StringHelper::GenerateHashGetHash(masm, hash);
+
+ // Collect the two characters in a register.
+ Register chars = c1;
+ __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
+
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string.
+
+ // Load string table
+ // Load address of first element of the string table.
+ Register string_table = c2;
+ __ LoadRoot(string_table, Heap::kStringTableRootIndex);
+
+ Register undefined = scratch4;
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ // Calculate capacity mask from the string table capacity.
+ Register mask = scratch2;
+ __ ldr(mask, FieldMemOperand(string_table, StringTable::kCapacityOffset));
+ __ mov(mask, Operand(mask, ASR, 1));
+ __ sub(mask, mask, Operand(1));
+
+ // Calculate untagged address of the first element of the string table.
+ Register first_string_table_element = string_table;
+ __ add(first_string_table_element, string_table,
+ Operand(StringTable::kElementsStartOffset - kHeapObjectTag));
+
+ // Registers
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string
+ // mask: capacity mask
+ // first_string_table_element: address of the first element of
+ // the string table
+ // undefined: the undefined object
+ // scratch: -
+
+ // Perform a number of probes in the string table.
+ const int kProbes = 4;
+ Label found_in_string_table;
+ Label next_probe[kProbes];
+ Register candidate = scratch5; // Scratch register contains candidate.
+ for (int i = 0; i < kProbes; i++) {
+ // Calculate entry in string table.
+ if (i > 0) {
+ __ add(candidate, hash, Operand(StringTable::GetProbeOffset(i)));
+ } else {
+ __ mov(candidate, hash);
+ }
+
+ __ and_(candidate, candidate, Operand(mask));
+
+ // Load the entry from the symble table.
+ STATIC_ASSERT(StringTable::kEntrySize == 1);
+ __ ldr(candidate,
+ MemOperand(first_string_table_element,
+ candidate,
+ LSL,
+ kPointerSizeLog2));
+
+ // If entry is undefined no string with this hash can be found.
+ Label is_string;
+ __ CompareObjectType(candidate, scratch, scratch, ODDBALL_TYPE);
+ __ b(ne, &is_string);
+
+ __ cmp(undefined, candidate);
+ __ b(eq, not_found);
+ // Must be the hole (deleted entry).
+ if (FLAG_debug_code) {
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(ip, candidate);
+ __ Assert(eq, kOddballInStringTableIsNotUndefinedOrTheHole);
+ }
+ __ jmp(&next_probe[i]);
+
+ __ bind(&is_string);
+
+ // Check that the candidate is a non-external ASCII string. The instance
+ // type is still in the scratch register from the CompareObjectType
+ // operation.
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch, &next_probe[i]);
+
+ // If length is not 2 the string is not a candidate.
+ __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
+ __ cmp(scratch, Operand(Smi::FromInt(2)));
+ __ b(ne, &next_probe[i]);
+
+ // Check if the two characters match.
+ // Assumes that word load is little endian.
+ __ ldrh(scratch, FieldMemOperand(candidate, SeqOneByteString::kHeaderSize));
+ __ cmp(chars, scratch);
+ __ b(eq, &found_in_string_table);
+ __ bind(&next_probe[i]);
+ }
+
+ // No matching 2 character string found by probing.
+ __ jmp(not_found);
+
+ // Scratch register contains result when we fall through to here.
+ Register result = candidate;
+ __ bind(&found_in_string_table);
+ __ Move(r0, result);
+}
+
+
+void StringHelper::GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character) {
+ // hash = character + (character << 10);
+ __ LoadRoot(hash, Heap::kHashSeedRootIndex);
+ // Untag smi seed and add the character.
+ __ add(hash, character, Operand(hash, LSR, kSmiTagSize));
+ // hash += hash << 10;
+ __ add(hash, hash, Operand(hash, LSL, 10));
+ // hash ^= hash >> 6;
+ __ eor(hash, hash, Operand(hash, LSR, 6));
+}
+
+
+void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character) {
+ // hash += character;
+ __ add(hash, hash, Operand(character));
+ // hash += hash << 10;
+ __ add(hash, hash, Operand(hash, LSL, 10));
+ // hash ^= hash >> 6;
+ __ eor(hash, hash, Operand(hash, LSR, 6));
+}
+
+
+void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
+ Register hash) {
+ // hash += hash << 3;
+ __ add(hash, hash, Operand(hash, LSL, 3));
+ // hash ^= hash >> 11;
+ __ eor(hash, hash, Operand(hash, LSR, 11));
+ // hash += hash << 15;
+ __ add(hash, hash, Operand(hash, LSL, 15));
+
+ __ and_(hash, hash, Operand(String::kHashBitMask), SetCC);
+
+ // if (hash == 0) hash = 27;
+ __ mov(hash, Operand(StringHasher::kZeroHash), LeaveCC, eq);
+}
+
+
+void SubStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ // Stack frame on entry.
+ // lr: return address
+ // sp[0]: to
+ // sp[4]: from
+ // sp[8]: string
+
+ // This stub is called from the native-call %_SubString(...), so
+ // nothing can be assumed about the arguments. It is tested that:
+ // "string" is a sequential string,
+ // both "from" and "to" are smis, and
+ // 0 <= from <= to <= string.length.
+ // If any of these assumptions fail, we call the runtime system.
+
+ const int kToOffset = 0 * kPointerSize;
+ const int kFromOffset = 1 * kPointerSize;
+ const int kStringOffset = 2 * kPointerSize;
+
+ __ Ldrd(r2, r3, MemOperand(sp, kToOffset));
+ STATIC_ASSERT(kFromOffset == kToOffset + 4);
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+
+ // Arithmetic shift right by one un-smi-tags. In this case we rotate right
+ // instead because we bail out on non-smi values: ROR and ASR are equivalent
+ // for smis but they set the flags in a way that's easier to optimize.
+ __ mov(r2, Operand(r2, ROR, 1), SetCC);
+ __ mov(r3, Operand(r3, ROR, 1), SetCC, cc);
+ // If either to or from had the smi tag bit set, then C is set now, and N
+ // has the same value: we rotated by 1, so the bottom bit is now the top bit.
+ // 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.
+ // Executed if both r2 and r3 are untagged integers.
+ __ sub(r2, r2, Operand(r3), SetCC, cc);
+ // One of the above un-smis or the above SUB could have set N==1.
+ __ b(mi, &runtime); // Either "from" or "to" is not an smi, or from > to.
+
+ // Make sure first argument is a string.
+ __ ldr(r0, MemOperand(sp, kStringOffset));
+ // Do a JumpIfSmi, but fold its jump into the subsequent string test.
+ __ SmiTst(r0);
+ Condition is_string = masm->IsObjectStringType(r0, r1, ne);
+ ASSERT(is_string == eq);
+ __ b(NegateCondition(is_string), &runtime);
+
+ Label single_char;
+ __ cmp(r2, Operand(1));
+ __ b(eq, &single_char);
+
+ // Short-cut for the case of trivial substring.
+ Label return_r0;
+ // r0: original string
+ // r2: result string length
+ __ ldr(r4, FieldMemOperand(r0, String::kLengthOffset));
+ __ cmp(r2, Operand(r4, ASR, 1));
+ // Return original string.
+ __ b(eq, &return_r0);
+ // Longer than original string's length or negative: unsafe arguments.
+ __ b(hi, &runtime);
+ // Shorter than original string's length: an actual substring.
+
+ // 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::kempty_stringRootIndex);
+ __ 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);
+
+ __ bind(&sliced_string);
+ // Sliced string. Fetch parent and correct start index by offset.
+ __ ldr(r5, FieldMemOperand(r0, SlicedString::kParentOffset));
+ __ ldr(r4, FieldMemOperand(r0, SlicedString::kOffsetOffset));
+ __ 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);
+
+ __ bind(&seq_or_external_string);
+ // Sequential or external string. Just move string to the expected register.
+ __ mov(r5, r0);
+
+ __ 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 & kOneByteStringTag) != 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(r5, FieldMemOperand(r0, SlicedString::kParentOffset));
+ __ str(r3, FieldMemOperand(r0, SlicedString::kOffsetOffset));
+ __ 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 == SeqOneByteString::kHeaderSize);
+ __ add(r5, r5, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+
+ __ bind(&allocate_result);
+ // Sequential acii string. Allocate the result.
+ STATIC_ASSERT((kOneByteStringTag & 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(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+
+ // r0: result string
+ // r1: first character of result string
+ // r2: result string length
+ // r5: first character of substring to copy
+ STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
+ COPY_ASCII | DEST_ALWAYS_ALIGNED);
+ __ jmp(&return_r0);
+
+ // Allocate and copy the resulting two-byte string.
+ __ bind(&two_byte_sequential);
+ __ AllocateTwoByteString(r0, r2, r4, r6, r7, &runtime);
+
+ // Locate first character of substring to copy.
+ STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
+ __ add(r5, r5, Operand(r3, LSL, 1));
+ // Locate first character of result.
+ __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+
+ // r0: result string.
+ // r1: first character of result.
+ // r2: result length.
+ // r5: first character of substring to copy.
+ STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ StringHelper::GenerateCopyCharactersLong(
+ masm, r1, r5, r2, r3, r4, r6, r7, r9, DEST_ALWAYS_ALIGNED);
+
+ __ bind(&return_r0);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->sub_string_native(), 1, r3, r4);
+ __ Drop(3);
+ __ Ret();
+
+ // Just jump to runtime to create the sub string.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kSubString, 3, 1);
+
+ __ bind(&single_char);
+ // r0: original string
+ // r1: instance type
+ // r2: length
+ // r3: from index (untagged)
+ __ SmiTag(r3, r3);
+ StringCharAtGenerator generator(
+ r0, r3, r2, r0, &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm);
+ __ Drop(3);
+ __ Ret();
+ generator.SkipSlow(masm, &runtime);
+}
+
+
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label strings_not_equal, check_zero_length;
+ __ ldr(length, FieldMemOperand(left, String::kLengthOffset));
+ __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
+ __ cmp(length, scratch2);
+ __ b(eq, &check_zero_length);
+ __ bind(&strings_not_equal);
+ __ mov(r0, Operand(Smi::FromInt(NOT_EQUAL)));
+ __ Ret();
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ cmp(length, Operand::Zero());
+ __ b(ne, &compare_chars);
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)));
+ __ Ret();
+
+ // Compare characters.
+ __ bind(&compare_chars);
+ GenerateAsciiCharsCompareLoop(masm,
+ left, right, length, scratch2, scratch3,
+ &strings_not_equal);
+
+ // Characters are equal.
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)));
+ __ Ret();
+}
+
+
+void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4) {
+ Label result_not_equal, compare_lengths;
+ // Find minimum length and length difference.
+ __ ldr(scratch1, FieldMemOperand(left, String::kLengthOffset));
+ __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
+ __ sub(scratch3, scratch1, Operand(scratch2), SetCC);
+ Register length_delta = scratch3;
+ __ mov(scratch1, scratch2, LeaveCC, gt);
+ Register min_length = scratch1;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ cmp(min_length, Operand::Zero());
+ __ b(eq, &compare_lengths);
+
+ // Compare loop.
+ GenerateAsciiCharsCompareLoop(masm,
+ left, right, min_length, scratch2, scratch4,
+ &result_not_equal);
+
+ // Compare lengths - strings up to min-length are equal.
+ __ bind(&compare_lengths);
+ ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
+ // Use length_delta as result if it's zero.
+ __ mov(r0, Operand(length_delta), SetCC);
+ __ bind(&result_not_equal);
+ // Conditionally update the result based either on length_delta or
+ // the last comparion performed in the loop above.
+ __ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt);
+ __ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt);
+ __ Ret();
+}
+
+
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* chars_not_equal) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiUntag(length);
+ __ add(scratch1, length,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ add(left, left, Operand(scratch1));
+ __ add(right, right, Operand(scratch1));
+ __ rsb(length, length, Operand::Zero());
+ Register index = length; // index = -length;
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ ldrb(scratch1, MemOperand(left, index));
+ __ ldrb(scratch2, MemOperand(right, index));
+ __ cmp(scratch1, scratch2);
+ __ b(ne, chars_not_equal);
+ __ add(index, index, Operand(1), SetCC);
+ __ b(ne, &loop);
+}
+
+
+void StringCompareStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ Counters* counters = masm->isolate()->counters();
+
+ // Stack frame on entry.
+ // sp[0]: right string
+ // sp[4]: left string
+ __ Ldrd(r0 , r1, MemOperand(sp)); // Load right in r0, left in r1.
+
+ Label not_same;
+ __ cmp(r0, r1);
+ __ b(ne, &not_same);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)));
+ __ IncrementCounter(counters->string_compare_native(), 1, r1, r2);
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&not_same);
+
+ // Check that both objects are sequential ASCII strings.
+ __ JumpIfNotBothSequentialAsciiStrings(r1, r0, r2, r3, &runtime);
+
+ // Compare flat ASCII strings natively. Remove arguments from stack first.
+ __ IncrementCounter(counters->string_compare_native(), 1, r2, r3);
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ GenerateCompareFlatAsciiStrings(masm, r1, r0, r2, r3, r4, r5);
+
+ // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+}
+
+
+void StringAddStub::Generate(MacroAssembler* masm) {
+ Label call_runtime, call_builtin;
+ Builtins::JavaScript builtin_id = Builtins::ADD;
+
+ Counters* counters = masm->isolate()->counters();
+
+ // Stack on entry:
+ // sp[0]: second argument (right).
+ // sp[4]: first argument (left).
+
+ // Load the two arguments.
+ __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
+ __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
+
+ // Make sure that both arguments are strings if not known in advance.
+ // Otherwise, at least one of the arguments is definitely a string,
+ // and we convert the one that is not known to be a string.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
+ ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT);
+ ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT);
+ __ JumpIfEitherSmi(r0, r1, &call_runtime);
+ // Load instance types.
+ __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
+ __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kStringTag == 0);
+ // If either is not a string, go to runtime.
+ __ tst(r4, Operand(kIsNotStringMask));
+ __ tst(r5, Operand(kIsNotStringMask), eq);
+ __ b(ne, &call_runtime);
+ } else if ((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
+ ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == 0);
+ GenerateConvertArgument(
+ masm, 1 * kPointerSize, r0, r2, r3, r4, r5, &call_builtin);
+ builtin_id = Builtins::STRING_ADD_RIGHT;
+ } else if ((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
+ ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == 0);
+ GenerateConvertArgument(
+ masm, 0 * kPointerSize, r1, r2, r3, r4, r5, &call_builtin);
+ builtin_id = Builtins::STRING_ADD_LEFT;
+ }
+
+ // Both arguments are strings.
+ // r0: first string
+ // r1: second string
+ // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ {
+ Label strings_not_empty;
+ // Check if either of the strings are empty. In that case return the other.
+ __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
+ __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ cmp(r2, Operand(Smi::FromInt(0))); // Test if first string is empty.
+ __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
+ STATIC_ASSERT(kSmiTag == 0);
+ // Else test if second string is empty.
+ __ cmp(r3, Operand(Smi::FromInt(0)), ne);
+ __ b(ne, &strings_not_empty); // If either string was empty, return r0.
+
+ __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&strings_not_empty);
+ }
+
+ __ SmiUntag(r2);
+ __ SmiUntag(r3);
+ // Both strings are non-empty.
+ // r0: first string
+ // r1: second string
+ // r2: length of first string
+ // r3: length of second string
+ // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // Look at the length of the result of adding the two strings.
+ Label string_add_flat_result, longer_than_two;
+ // Adding two lengths can't overflow.
+ STATIC_ASSERT(String::kMaxLength < String::kMaxLength * 2);
+ __ add(r6, r2, Operand(r3));
+ // Use the string table when adding two one character strings, as it
+ // helps later optimizations to return a string here.
+ __ cmp(r6, Operand(2));
+ __ b(ne, &longer_than_two);
+
+ // Check that both strings are non-external ASCII strings.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
+ __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
+ __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
+ }
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
+ &call_runtime);
+
+ // Get the two characters forming the sub string.
+ __ ldrb(r2, FieldMemOperand(r0, SeqOneByteString::kHeaderSize));
+ __ ldrb(r3, FieldMemOperand(r1, SeqOneByteString::kHeaderSize));
+
+ // Try to lookup two character string in string table. If it is not found
+ // just allocate a new one.
+ Label make_two_character_string;
+ StringHelper::GenerateTwoCharacterStringTableProbe(
+ masm, r2, r3, r6, r7, r4, r5, r9, &make_two_character_string);
+ __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&make_two_character_string);
+ // Resulting string has length 2 and first chars of two strings
+ // are combined into single halfword in r2 register.
+ // So we can fill resulting string without two loops by a single
+ // halfword store instruction (which assumes that processor is
+ // in a little endian mode)
+ __ mov(r6, Operand(2));
+ __ AllocateAsciiString(r0, r6, r4, r5, r9, &call_runtime);
+ __ strh(r2, FieldMemOperand(r0, SeqOneByteString::kHeaderSize));
+ __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&longer_than_two);
+ // Check if resulting string will be flat.
+ __ 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, &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.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
+ __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
+ __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
+ }
+ Label non_ascii, allocated, ascii_data;
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ __ tst(r4, Operand(kStringEncodingMask));
+ __ tst(r5, Operand(kStringEncodingMask), ne);
+ __ b(eq, &non_ascii);
+
+ // Allocate an ASCII cons string.
+ __ bind(&ascii_data);
+ __ AllocateAsciiConsString(r7, r6, r4, r5, &call_runtime);
+ __ bind(&allocated);
+ // Fill the fields of the cons string.
+ Label skip_write_barrier, after_writing;
+ ExternalReference high_promotion_mode = ExternalReference::
+ new_space_high_promotion_mode_active_address(masm->isolate());
+ __ mov(r4, Operand(high_promotion_mode));
+ __ ldr(r4, MemOperand(r4, 0));
+ __ cmp(r4, Operand::Zero());
+ __ b(eq, &skip_write_barrier);
+
+ __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
+ __ RecordWriteField(r7,
+ ConsString::kFirstOffset,
+ r0,
+ r4,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs);
+ __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
+ __ RecordWriteField(r7,
+ ConsString::kSecondOffset,
+ r1,
+ r4,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs);
+ __ jmp(&after_writing);
+
+ __ bind(&skip_write_barrier);
+ __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
+ __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
+
+ __ bind(&after_writing);
+
+ __ mov(r0, Operand(r7));
+ __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&non_ascii);
+ // At least one of the strings is two-byte. Check whether it happens
+ // to contain only one byte characters.
+ // r4: first instance type.
+ // r5: second instance type.
+ __ tst(r4, Operand(kOneByteDataHintMask));
+ __ tst(r5, Operand(kOneByteDataHintMask), ne);
+ __ b(ne, &ascii_data);
+ __ eor(r4, r4, Operand(r5));
+ STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
+ __ and_(r4, r4, Operand(kOneByteStringTag | kOneByteDataHintTag));
+ __ cmp(r4, Operand(kOneByteStringTag | kOneByteDataHintTag));
+ __ b(eq, &ascii_data);
+
+ // Allocate a two byte cons string.
+ __ AllocateTwoByteConsString(r7, r6, r4, r5, &call_runtime);
+ __ jmp(&allocated);
+
+ // 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
+ // r3: length of second string
+ // 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_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
+ __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
+ __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
+ }
+
+ // 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));
+ STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
+ __ add(r7,
+ r0,
+ Operand(SeqOneByteString::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(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
+ __ add(r1,
+ r1,
+ Operand(SeqOneByteString::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.
+ // Both strings have the same encoding.
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ __ tst(r5, Operand(kStringEncodingMask));
+ __ b(eq, &non_ascii_string_add_flat_result);
+
+ __ AllocateAsciiString(r0, r6, r4, r5, r9, &call_runtime);
+ __ add(r6, r0, Operand(SeqOneByteString::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.
+ StringHelper::GenerateCopyCharacters(masm, r6, r7, r2, r4, true);
+ // r6: next character of result.
+ StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
+ __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&non_ascii_string_add_flat_result);
+ __ 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.
+ StringHelper::GenerateCopyCharacters(masm, r6, r7, r2, r4, false);
+ // r6: next character of result.
+ StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
+ __ 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(&call_runtime);
+ if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
+ GenerateRegisterArgsPop(masm);
+ // Build a frame
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ __ CallRuntime(Runtime::kStringAdd, 2);
+ }
+ __ Ret();
+ } else {
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
+ }
+
+ if (call_builtin.is_linked()) {
+ __ bind(&call_builtin);
+ if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
+ GenerateRegisterArgsPop(masm);
+ // Build a frame
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ __ InvokeBuiltin(builtin_id, CALL_FUNCTION);
+ }
+ __ Ret();
+ } else {
+ __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
+ }
+ }
+}
+
+
+void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ push(r0);
+ __ push(r1);
+}
+
+
+void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm) {
+ __ pop(r1);
+ __ pop(r0);
+}
+
+
+void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* slow) {
+ // First check if the argument is already a string.
+ Label not_string, done;
+ __ JumpIfSmi(arg, &not_string);
+ __ CompareObjectType(arg, scratch1, scratch1, FIRST_NONSTRING_TYPE);
+ __ b(lt, &done);
+
+ // Check the number to string cache.
+ Label not_cached;
+ __ bind(&not_string);
+ // Puts the cached result into scratch1.
+ NumberToStringStub::GenerateLookupNumberStringCache(masm,
+ arg,
+ scratch1,
+ scratch2,
+ scratch3,
+ scratch4,
+ &not_cached);
+ __ mov(arg, scratch1);
+ __ str(arg, MemOperand(sp, stack_offset));
+ __ jmp(&done);
+
+ // Check if the argument is a safe string wrapper.
+ __ bind(&not_cached);
+ __ JumpIfSmi(arg, slow);
+ __ CompareObjectType(
+ arg, scratch1, scratch2, JS_VALUE_TYPE); // map -> scratch1.
+ __ b(ne, slow);
+ __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset));
+ __ and_(scratch2,
+ scratch2, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ cmp(scratch2,
+ Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ b(ne, slow);
+ __ ldr(arg, FieldMemOperand(arg, JSValue::kValueOffset));
+ __ str(arg, MemOperand(sp, stack_offset));
+
+ __ bind(&done);
+}
+
+
+void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SMI);
+ Label miss;
+ __ orr(r2, r1, r0);
+ __ JumpIfNotSmi(r2, &miss);
+
+ if (GetCondition() == eq) {
+ // For equality we do not care about the sign of the result.
+ __ sub(r0, r0, r1, SetCC);
+ } else {
+ // Untag before subtracting to avoid handling overflow.
+ __ SmiUntag(r1);
+ __ sub(r0, r1, Operand::SmiUntag(r0));
+ }
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateNumbers(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::NUMBER);
+
+ Label generic_stub;
+ Label unordered, maybe_undefined1, maybe_undefined2;
+ Label miss;
+
+ if (left_ == CompareIC::SMI) {
+ __ JumpIfNotSmi(r1, &miss);
+ }
+ if (right_ == CompareIC::SMI) {
+ __ JumpIfNotSmi(r0, &miss);
+ }
+
+ // Inlining the double comparison and falling back to the general compare
+ // stub if NaN is involved.
+ // Load left and right operand.
+ Label done, left, left_smi, right_smi;
+ __ JumpIfSmi(r0, &right_smi);
+ __ CheckMap(r0, r2, Heap::kHeapNumberMapRootIndex, &maybe_undefined1,
+ DONT_DO_SMI_CHECK);
+ __ sub(r2, r0, Operand(kHeapObjectTag));
+ __ vldr(d1, r2, HeapNumber::kValueOffset);
+ __ b(&left);
+ __ bind(&right_smi);
+ __ SmiToDouble(d1, r0);
+
+ __ bind(&left);
+ __ JumpIfSmi(r1, &left_smi);
+ __ CheckMap(r1, r2, Heap::kHeapNumberMapRootIndex, &maybe_undefined2,
+ DONT_DO_SMI_CHECK);
+ __ sub(r2, r1, Operand(kHeapObjectTag));
+ __ vldr(d0, r2, HeapNumber::kValueOffset);
+ __ b(&done);
+ __ bind(&left_smi);
+ __ SmiToDouble(d0, r1);
+
+ __ bind(&done);
+ // Compare operands.
+ __ VFPCompareAndSetFlags(d0, d1);
+
+ // Don't base result on status bits when a NaN is involved.
+ __ b(vs, &unordered);
+
+ // Return a result of -1, 0, or 1, based on status bits.
+ __ mov(r0, Operand(EQUAL), LeaveCC, eq);
+ __ mov(r0, Operand(LESS), LeaveCC, lt);
+ __ mov(r0, Operand(GREATER), LeaveCC, gt);
+ __ Ret();
+
+ __ bind(&unordered);
+ __ bind(&generic_stub);
+ ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC,
+ CompareIC::GENERIC);
+ __ Jump(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+
+ __ bind(&maybe_undefined1);
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ __ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
+ __ b(ne, &miss);
+ __ JumpIfSmi(r1, &unordered);
+ __ CompareObjectType(r1, r2, r2, HEAP_NUMBER_TYPE);
+ __ b(ne, &maybe_undefined2);
+ __ jmp(&unordered);
+ }
+
+ __ bind(&maybe_undefined2);
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ __ CompareRoot(r1, Heap::kUndefinedValueRootIndex);
+ __ b(eq, &unordered);
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::INTERNALIZED_STRING);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = r1;
+ Register right = r0;
+ Register tmp1 = r2;
+ Register tmp2 = r3;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are internalized strings.
+ __ ldr(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ ldr(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ ldrb(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ ldrb(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ orr(tmp1, tmp1, Operand(tmp2));
+ __ tst(tmp1, Operand(kIsNotStringMask | kIsNotInternalizedMask));
+ __ b(ne, &miss);
+
+ // Internalized strings are compared by identity.
+ __ cmp(left, right);
+ // Make sure r0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(r0));
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)), LeaveCC, eq);
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::UNIQUE_NAME);
+ ASSERT(GetCondition() == eq);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = r1;
+ Register right = r0;
+ Register tmp1 = r2;
+ Register tmp2 = r3;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are unique names. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ ldr(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ ldr(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ ldrb(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ ldrb(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+
+ __ JumpIfNotUniqueName(tmp1, &miss);
+ __ JumpIfNotUniqueName(tmp2, &miss);
+
+ // Unique names are compared by identity.
+ __ cmp(left, right);
+ // Make sure r0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(r0));
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)), LeaveCC, eq);
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRING);
+ Label miss;
+
+ bool equality = Token::IsEqualityOp(op_);
+
+ // Registers containing left and right operands respectively.
+ Register left = r1;
+ Register right = r0;
+ Register tmp1 = r2;
+ Register tmp2 = r3;
+ Register tmp3 = r4;
+ Register tmp4 = r5;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ ldr(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ ldr(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ ldrb(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ ldrb(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ orr(tmp3, tmp1, tmp2);
+ __ tst(tmp3, Operand(kIsNotStringMask));
+ __ b(ne, &miss);
+
+ // Fast check for identical strings.
+ __ cmp(left, right);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)), LeaveCC, eq);
+ __ Ret(eq);
+
+ // Handle not identical strings.
+
+ // Check that both strings are internalized strings. If they are, we're done
+ // because we already know they are not identical. We know they are both
+ // strings.
+ if (equality) {
+ ASSERT(GetCondition() == eq);
+ STATIC_ASSERT(kInternalizedTag == 0);
+ __ orr(tmp3, tmp1, Operand(tmp2));
+ __ tst(tmp3, Operand(kIsNotInternalizedMask));
+ // Make sure r0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(r0));
+ __ Ret(eq);
+ }
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(
+ tmp1, tmp2, tmp3, tmp4, &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ if (equality) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2, tmp3);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(
+ masm, left, right, tmp1, tmp2, tmp3, tmp4);
+ }
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ Push(left, right);
+ if (equality) {
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ } else {
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::OBJECT);
+ Label miss;
+ __ and_(r2, r1, Operand(r0));
+ __ JumpIfSmi(r2, &miss);
+
+ __ CompareObjectType(r0, r2, r2, JS_OBJECT_TYPE);
+ __ b(ne, &miss);
+ __ CompareObjectType(r1, r2, r2, JS_OBJECT_TYPE);
+ __ b(ne, &miss);
+
+ ASSERT(GetCondition() == eq);
+ __ sub(r0, r0, Operand(r1));
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(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) {
+ {
+ // 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);
+ }
+
+ __ Jump(r2);
+}
+
+
+void DirectCEntryStub::Generate(MacroAssembler* masm) {
+ __ ldr(pc, MemOperand(sp, 0));
+}
+
+
+void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
+ Register target) {
+ intptr_t code =
+ reinterpret_cast<intptr_t>(GetCode(masm->isolate()).location());
+ __ mov(lr, Operand(code, RelocInfo::CODE_TARGET));
+
+ // Prevent literal pool emission during calculation of return address.
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+
+ // Push return address (accessible to GC through exit frame pc).
+ // Note that using pc with str is deprecated.
+ Label start;
+ __ bind(&start);
+ __ add(ip, pc, Operand(Assembler::kInstrSize));
+ __ str(ip, MemOperand(sp, 0));
+ __ Jump(target); // Call the C++ function.
+ ASSERT_EQ(Assembler::kInstrSize + Assembler::kPcLoadDelta,
+ masm->SizeOfCodeGeneratedSince(&start));
+ __ VFPEnsureFPSCRState(r2);
+}
+
+
+void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register receiver,
+ Register properties,
+ Handle<Name> name,
+ Register scratch0) {
+ ASSERT(name->IsUniqueName());
+ // 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
+ // (their names are the hole value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // scratch0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = scratch0;
+ // Capacity is smi 2^n.
+ __ ldr(index, FieldMemOperand(properties, kCapacityOffset));
+ __ sub(index, index, Operand(1));
+ __ and_(index, index, Operand(
+ Smi::FromInt(name->Hash() + NameDictionary::GetProbeOffset(i))));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ add(index, index, Operand(index, LSL, 1)); // index *= 3.
+
+ Register entity_name = scratch0;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ Register tmp = properties;
+ __ add(tmp, properties, Operand(index, LSL, 1));
+ __ ldr(entity_name, FieldMemOperand(tmp, kElementsStartOffset));
+
+ ASSERT(!tmp.is(entity_name));
+ __ LoadRoot(tmp, Heap::kUndefinedValueRootIndex);
+ __ cmp(entity_name, tmp);
+ __ b(eq, done);
+
+ // Load the hole ready for use below:
+ __ LoadRoot(tmp, Heap::kTheHoleValueRootIndex);
+
+ // Stop if found the property.
+ __ cmp(entity_name, Operand(Handle<Name>(name)));
+ __ b(eq, miss);
+
+ Label good;
+ __ cmp(entity_name, tmp);
+ __ b(eq, &good);
+
+ // Check if the entry name is not a unique name.
+ __ ldr(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset));
+ __ ldrb(entity_name,
+ FieldMemOperand(entity_name, Map::kInstanceTypeOffset));
+ __ JumpIfNotUniqueName(entity_name, miss);
+ __ bind(&good);
+
+ // Restore the properties.
+ __ ldr(properties,
+ FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ }
+
+ const int spill_mask =
+ (lr.bit() | r6.bit() | r5.bit() | r4.bit() | r3.bit() |
+ r2.bit() | r1.bit() | r0.bit());
+
+ __ stm(db_w, sp, spill_mask);
+ __ ldr(r0, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ __ mov(r1, Operand(Handle<Name>(name)));
+ NameDictionaryLookupStub stub(NEGATIVE_LOOKUP);
+ __ CallStub(&stub);
+ __ cmp(r0, Operand::Zero());
+ __ ldm(ia_w, sp, spill_mask);
+
+ __ b(eq, done);
+ __ b(ne, miss);
+}
+
+
+// Probe the name dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found. Jump to
+// the |miss| label otherwise.
+// If lookup was successful |scratch2| will be equal to elements + 4 * index.
+void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register scratch1,
+ Register scratch2) {
+ ASSERT(!elements.is(scratch1));
+ ASSERT(!elements.is(scratch2));
+ ASSERT(!name.is(scratch1));
+ ASSERT(!name.is(scratch2));
+
+ __ AssertName(name);
+
+ // Compute the capacity mask.
+ __ ldr(scratch1, FieldMemOperand(elements, kCapacityOffset));
+ __ SmiUntag(scratch1);
+ __ sub(scratch1, scratch1, Operand(1));
+
+ // Generate an unrolled loop that performs a few probes before
+ // giving up. Measurements done on Gmail indicate that 2 probes
+ // cover ~93% of loads from dictionaries.
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ ldr(scratch2, FieldMemOperand(name, Name::kHashFieldOffset));
+ if (i > 0) {
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(NameDictionary::GetProbeOffset(i) <
+ 1 << (32 - Name::kHashFieldOffset));
+ __ add(scratch2, scratch2, Operand(
+ NameDictionary::GetProbeOffset(i) << Name::kHashShift));
+ }
+ __ and_(scratch2, scratch1, Operand(scratch2, LSR, Name::kHashShift));
+
+ // Scale the index by multiplying by the element size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ // scratch2 = scratch2 * 3.
+ __ add(scratch2, scratch2, Operand(scratch2, LSL, 1));
+
+ // Check if the key is identical to the name.
+ __ add(scratch2, elements, Operand(scratch2, LSL, 2));
+ __ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset));
+ __ cmp(name, Operand(ip));
+ __ b(eq, done);
+ }
+
+ const int spill_mask =
+ (lr.bit() | r6.bit() | r5.bit() | r4.bit() |
+ r3.bit() | r2.bit() | r1.bit() | r0.bit()) &
+ ~(scratch1.bit() | scratch2.bit());
+
+ __ stm(db_w, sp, spill_mask);
+ if (name.is(r0)) {
+ ASSERT(!elements.is(r1));
+ __ Move(r1, name);
+ __ Move(r0, elements);
+ } else {
+ __ Move(r0, elements);
+ __ Move(r1, name);
+ }
+ NameDictionaryLookupStub stub(POSITIVE_LOOKUP);
+ __ CallStub(&stub);
+ __ cmp(r0, Operand::Zero());
+ __ mov(scratch2, Operand(r2));
+ __ ldm(ia_w, sp, spill_mask);
+
+ __ b(ne, done);
+ __ b(eq, miss);
+}
+
+
+void NameDictionaryLookupStub::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: NameDictionary to probe
+ // r1: key
+ // dictionary: NameDictionary to probe.
+ // index: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Register result = r0;
+ Register dictionary = r0;
+ Register key = r1;
+ Register index = r2;
+ Register mask = r3;
+ Register hash = r4;
+ Register undefined = r5;
+ Register entry_key = r6;
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ __ ldr(mask, FieldMemOperand(dictionary, kCapacityOffset));
+ __ SmiUntag(mask);
+ __ sub(mask, mask, Operand(1));
+
+ __ ldr(hash, FieldMemOperand(key, Name::kHashFieldOffset));
+
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ // Capacity is smi 2^n.
+ if (i > 0) {
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(NameDictionary::GetProbeOffset(i) <
+ 1 << (32 - Name::kHashFieldOffset));
+ __ add(index, hash, Operand(
+ NameDictionary::GetProbeOffset(i) << Name::kHashShift));
+ } else {
+ __ mov(index, Operand(hash));
+ }
+ __ and_(index, mask, Operand(index, LSR, Name::kHashShift));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ add(index, index, Operand(index, LSL, 1)); // index *= 3.
+
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ add(index, dictionary, Operand(index, LSL, 2));
+ __ ldr(entry_key, FieldMemOperand(index, kElementsStartOffset));
+
+ // Having undefined at this place means the name is not contained.
+ __ cmp(entry_key, Operand(undefined));
+ __ b(eq, &not_in_dictionary);
+
+ // Stop if found the property.
+ __ cmp(entry_key, Operand(key));
+ __ b(eq, &in_dictionary);
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // Check if the entry name is not a unique name.
+ __ ldr(entry_key, FieldMemOperand(entry_key, HeapObject::kMapOffset));
+ __ ldrb(entry_key,
+ FieldMemOperand(entry_key, Map::kInstanceTypeOffset));
+ __ JumpIfNotUniqueName(entry_key, &maybe_in_dictionary);
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ mov(result, Operand::Zero());
+ __ Ret();
+ }
+
+ __ bind(&in_dictionary);
+ __ mov(result, Operand(1));
+ __ Ret();
+
+ __ bind(&not_in_dictionary);
+ __ mov(result, Operand::Zero());
+ __ Ret();
+}
+
+
+struct AheadOfTimeWriteBarrierStubList {
+ Register object, value, address;
+ RememberedSetAction action;
+};
+
+
+#define REG(Name) { kRegister_ ## Name ## _Code }
+
+static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
+ // Used in RegExpExecStub.
+ { REG(r6), REG(r4), REG(r7), EMIT_REMEMBERED_SET },
+ // Used in CompileArrayPushCall.
+ // Also used in StoreIC::GenerateNormal via GenerateDictionaryStore.
+ // Also used in KeyedStoreIC::GenerateGeneric.
+ { REG(r3), REG(r4), REG(r5), EMIT_REMEMBERED_SET },
+ // Used in CompileStoreGlobal.
+ { REG(r4), REG(r1), REG(r2), OMIT_REMEMBERED_SET },
+ // Used in StoreStubCompiler::CompileStoreField via GenerateStoreField.
+ { REG(r1), REG(r2), REG(r3), EMIT_REMEMBERED_SET },
+ { REG(r3), REG(r2), REG(r1), EMIT_REMEMBERED_SET },
+ // Used in KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
+ { REG(r2), REG(r1), REG(r3), EMIT_REMEMBERED_SET },
+ { REG(r3), REG(r1), REG(r2), EMIT_REMEMBERED_SET },
+ // KeyedStoreStubCompiler::GenerateStoreFastElement.
+ { REG(r3), REG(r2), REG(r4), EMIT_REMEMBERED_SET },
+ { REG(r2), REG(r3), REG(r4), EMIT_REMEMBERED_SET },
+ // ElementsTransitionGenerator::GenerateMapChangeElementTransition
+ // and ElementsTransitionGenerator::GenerateSmiToDouble
+ // and ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(r2), REG(r3), REG(r9), EMIT_REMEMBERED_SET },
+ { REG(r2), REG(r3), REG(r9), OMIT_REMEMBERED_SET },
+ // ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(r6), REG(r2), REG(r0), EMIT_REMEMBERED_SET },
+ { REG(r2), REG(r6), REG(r9), EMIT_REMEMBERED_SET },
+ // StoreArrayLiteralElementStub::Generate
+ { REG(r5), REG(r0), REG(r6), EMIT_REMEMBERED_SET },
+ // FastNewClosureStub::Generate
+ { REG(r2), REG(r4), REG(r1), EMIT_REMEMBERED_SET },
+ // StringAddStub::Generate
+ { REG(r7), REG(r1), REG(r4), EMIT_REMEMBERED_SET },
+ { REG(r7), REG(r0), REG(r4), EMIT_REMEMBERED_SET },
+ // Null termination.
+ { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
+};
+
+#undef REG
+
+
+bool RecordWriteStub::IsPregenerated() {
+ for (const 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(
+ Isolate* isolate) {
+ StoreBufferOverflowStub stub1(kDontSaveFPRegs);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ // Hydrogen code stubs need stub2 at snapshot time.
+ StoreBufferOverflowStub stub2(kSaveFPRegs);
+ stub2.GetCode(isolate)->set_is_pregenerated(true);
+}
+
+
+void RecordWriteStub::GenerateFixedRegStubsAheadOfTime(Isolate* isolate) {
+ for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
+ !entry->object.is(no_reg);
+ entry++) {
+ RecordWriteStub stub(entry->object,
+ entry->value,
+ entry->address,
+ entry->action,
+ kDontSaveFPRegs);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+bool CodeStub::CanUseFPRegisters() {
+ return true; // VFP2 is a base requirement for V8
+}
+
+
+// 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.
+ {
+ // Block literal pool emission, as the position of these two instructions
+ // is assumed by the patching code.
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+ __ 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());
+ __ Move(r1, address);
+ __ mov(r2, Operand(ExternalReference::isolate_address(masm->isolate())));
+
+ 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;
+
+ __ and_(regs_.scratch0(), regs_.object(), Operand(~Page::kPageAlignmentMask));
+ __ ldr(regs_.scratch1(),
+ MemOperand(regs_.scratch0(),
+ MemoryChunk::kWriteBarrierCounterOffset));
+ __ sub(regs_.scratch1(), regs_.scratch1(), Operand(1), SetCC);
+ __ str(regs_.scratch1(),
+ MemOperand(regs_.scratch0(),
+ MemoryChunk::kWriteBarrierCounterOffset));
+ __ b(mi, &need_incremental);
+
+ // 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
+ // -- r3 : element index as smi
+ // -- sp[0] : array literal index in function as smi
+ // -- sp[4] : array literal
+ // clobbers r1, r2, r4
+ // -----------------------------------
+
+ Label element_done;
+ Label double_elements;
+ Label smi_element;
+ Label slow_elements;
+ Label fast_elements;
+
+ // Get array literal index, array literal and its map.
+ __ ldr(r4, MemOperand(sp, 0 * kPointerSize));
+ __ ldr(r1, MemOperand(sp, 1 * kPointerSize));
+ __ ldr(r2, FieldMemOperand(r1, JSObject::kMapOffset));
+
+ __ CheckFastElements(r2, r5, &double_elements);
+ // FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS
+ __ JumpIfSmi(r0, &smi_element);
+ __ CheckFastSmiElements(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::PointerOffsetFromSmiKey(r3));
+ __ 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_ELEMENTS or FAST_*_ELEMENTS,
+ // and value is Smi.
+ __ bind(&smi_element);
+ __ ldr(r5, FieldMemOperand(r1, JSObject::kElementsOffset));
+ __ add(r6, r5, Operand::PointerOffsetFromSmiKey(r3));
+ __ 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, r5, r6, d0, &slow_elements);
+ __ Ret();
+}
+
+
+void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
+ CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs);
+ __ Call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+ int parameter_count_offset =
+ StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
+ __ ldr(r1, MemOperand(fp, parameter_count_offset));
+ if (function_mode_ == JS_FUNCTION_STUB_MODE) {
+ __ add(r1, r1, Operand(1));
+ }
+ masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE);
+ __ mov(r1, Operand(r1, LSL, kPointerSizeLog2));
+ __ add(sp, sp, r1);
+ __ Ret();
+}
+
+
+void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
+ if (masm->isolate()->function_entry_hook() != NULL) {
+ PredictableCodeSizeScope predictable(masm, 4 * Assembler::kInstrSize);
+ AllowStubCallsScope allow_stub_calls(masm, true);
+ ProfileEntryHookStub stub;
+ __ push(lr);
+ __ CallStub(&stub);
+ __ pop(lr);
+ }
+}
+
+
+void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
+ // The entry hook is a "push lr" instruction, followed by a call.
+ const int32_t kReturnAddressDistanceFromFunctionStart =
+ 3 * Assembler::kInstrSize;
+
+ // This should contain all kCallerSaved registers.
+ const RegList kSavedRegs =
+ 1 << 0 | // r0
+ 1 << 1 | // r1
+ 1 << 2 | // r2
+ 1 << 3 | // r3
+ 1 << 5 | // r5
+ 1 << 9; // r9
+ // We also save lr, so the count here is one higher than the mask indicates.
+ const int32_t kNumSavedRegs = 7;
+
+ ASSERT((kCallerSaved & kSavedRegs) == kCallerSaved);
+
+ // Save all caller-save registers as this may be called from anywhere.
+ __ stm(db_w, sp, kSavedRegs | lr.bit());
+
+ // Compute the function's address for the first argument.
+ __ sub(r0, lr, Operand(kReturnAddressDistanceFromFunctionStart));
+
+ // The caller's return address is above the saved temporaries.
+ // Grab that for the second argument to the hook.
+ __ add(r1, sp, Operand(kNumSavedRegs * kPointerSize));
+
+ // Align the stack if necessary.
+ int frame_alignment = masm->ActivationFrameAlignment();
+ if (frame_alignment > kPointerSize) {
+ __ mov(r5, sp);
+ ASSERT(IsPowerOf2(frame_alignment));
+ __ and_(sp, sp, Operand(-frame_alignment));
+ }
+
+#if V8_HOST_ARCH_ARM
+ int32_t entry_hook =
+ reinterpret_cast<int32_t>(masm->isolate()->function_entry_hook());
+ __ mov(ip, Operand(entry_hook));
+#else
+ // Under the simulator we need to indirect the entry hook through a
+ // trampoline function at a known address.
+ ApiFunction dispatcher(FUNCTION_ADDR(EntryHookTrampoline));
+ __ mov(ip, Operand(ExternalReference(&dispatcher,
+ ExternalReference::BUILTIN_CALL,
+ masm->isolate())));
+#endif
+ __ Call(ip);
+
+ // Restore the stack pointer if needed.
+ if (frame_alignment > kPointerSize) {
+ __ mov(sp, r5);
+ }
+
+ // Also pop pc to get Ret(0).
+ __ ldm(ia_w, sp, kSavedRegs | pc.bit());
+}
+
+
+template<class T>
+static void CreateArrayDispatch(MacroAssembler* masm) {
+ int last_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= last_index; ++i) {
+ Label next;
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ __ cmp(r3, Operand(kind));
+ __ b(ne, &next);
+ T stub(kind);
+ __ TailCallStub(&stub);
+ __ bind(&next);
+ }
+
+ // If we reached this point there is a problem.
+ __ Abort(kUnexpectedElementsKindInArrayConstructor);
+}
+
+
+static void CreateArrayDispatchOneArgument(MacroAssembler* masm) {
+ // r2 - type info cell
+ // r3 - kind
+ // r0 - number of arguments
+ // r1 - constructor?
+ // sp[0] - last argument
+ ASSERT(FAST_SMI_ELEMENTS == 0);
+ ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ ASSERT(FAST_ELEMENTS == 2);
+ ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ ASSERT(FAST_DOUBLE_ELEMENTS == 4);
+ ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5);
+
+ // is the low bit set? If so, we are holey and that is good.
+ __ tst(r3, Operand(1));
+ Label normal_sequence;
+ __ b(ne, &normal_sequence);
+
+ // look at the first argument
+ __ ldr(r5, MemOperand(sp, 0));
+ __ cmp(r5, Operand::Zero());
+ __ b(eq, &normal_sequence);
+
+ // We are going to create a holey array, but our kind is non-holey.
+ // Fix kind and retry (only if we have an allocation site in the cell).
+ __ add(r3, r3, Operand(1));
+ __ CompareRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ b(eq, &normal_sequence);
+ __ ldr(r5, FieldMemOperand(r2, Cell::kValueOffset));
+ __ ldr(r5, FieldMemOperand(r5, 0));
+ __ CompareRoot(r5, Heap::kAllocationSiteMapRootIndex);
+ __ b(ne, &normal_sequence);
+
+ // Save the resulting elements kind in type info
+ __ SmiTag(r3);
+ __ ldr(r5, FieldMemOperand(r2, Cell::kValueOffset));
+ __ str(r3, FieldMemOperand(r5, AllocationSite::kTransitionInfoOffset));
+ __ SmiUntag(r3);
+
+ __ bind(&normal_sequence);
+ int last_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= last_index; ++i) {
+ Label next;
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ __ cmp(r3, Operand(kind));
+ __ b(ne, &next);
+ ArraySingleArgumentConstructorStub stub(kind);
+ __ TailCallStub(&stub);
+ __ bind(&next);
+ }
+
+ // If we reached this point there is a problem.
+ __ Abort(kUnexpectedElementsKindInArrayConstructor);
+}
+
+
+template<class T>
+static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) {
+ int to_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= to_index; ++i) {
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ T stub(kind);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ if (AllocationSite::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) {
+ T stub1(kind, CONTEXT_CHECK_REQUIRED, DISABLE_ALLOCATION_SITES);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ }
+ }
+}
+
+
+void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) {
+ ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>(
+ isolate);
+ ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
+ isolate);
+ ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>(
+ isolate);
+}
+
+
+void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(
+ Isolate* isolate) {
+ ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS };
+ for (int i = 0; i < 2; i++) {
+ // For internal arrays we only need a few things
+ InternalArrayNoArgumentConstructorStub stubh1(kinds[i]);
+ stubh1.GetCode(isolate)->set_is_pregenerated(true);
+ InternalArraySingleArgumentConstructorStub stubh2(kinds[i]);
+ stubh2.GetCode(isolate)->set_is_pregenerated(true);
+ InternalArrayNArgumentsConstructorStub stubh3(kinds[i]);
+ stubh3.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+void ArrayConstructorStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r0 : argc (only if argument_count_ == ANY)
+ // -- r1 : constructor
+ // -- r2 : type info cell
+ // -- sp[0] : return address
+ // -- sp[4] : last argument
+ // -----------------------------------
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the global and natives
+ // builtin Array functions which always have maps.
+
+ // Initial map for the builtin Array function should be a map.
+ __ ldr(r3, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ __ tst(r3, Operand(kSmiTagMask));
+ __ Assert(ne, kUnexpectedInitialMapForArrayFunction);
+ __ CompareObjectType(r3, r3, r4, MAP_TYPE);
+ __ Assert(eq, kUnexpectedInitialMapForArrayFunction);
+
+ // We should either have undefined in ebx or a valid cell
+ Label okay_here;
+ Handle<Map> cell_map = masm->isolate()->factory()->cell_map();
+ __ CompareRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ b(eq, &okay_here);
+ __ ldr(r3, FieldMemOperand(r2, 0));
+ __ cmp(r3, Operand(cell_map));
+ __ Assert(eq, kExpectedPropertyCellInRegisterEbx);
+ __ bind(&okay_here);
+ }
+
+ Label no_info, switch_ready;
+ // Get the elements kind and case on that.
+ __ CompareRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ b(eq, &no_info);
+ __ ldr(r3, FieldMemOperand(r2, Cell::kValueOffset));
+
+ // The type cell may have undefined in its value.
+ __ CompareRoot(r3, Heap::kUndefinedValueRootIndex);
+ __ b(eq, &no_info);
+
+ // The type cell has either an AllocationSite or a JSFunction
+ __ ldr(r4, FieldMemOperand(r3, 0));
+ __ CompareRoot(r4, Heap::kAllocationSiteMapRootIndex);
+ __ b(ne, &no_info);
+
+ __ ldr(r3, FieldMemOperand(r3, AllocationSite::kTransitionInfoOffset));
+ __ SmiUntag(r3);
+ __ jmp(&switch_ready);
+ __ bind(&no_info);
+ __ mov(r3, Operand(GetInitialFastElementsKind()));
+ __ bind(&switch_ready);
+
+ if (argument_count_ == ANY) {
+ Label not_zero_case, not_one_case;
+ __ tst(r0, r0);
+ __ b(ne, &not_zero_case);
+ CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
+
+ __ bind(&not_zero_case);
+ __ cmp(r0, Operand(1));
+ __ b(gt, &not_one_case);
+ CreateArrayDispatchOneArgument(masm);
+
+ __ bind(&not_one_case);
+ CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
+ } else if (argument_count_ == NONE) {
+ CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
+ } else if (argument_count_ == ONE) {
+ CreateArrayDispatchOneArgument(masm);
+ } else if (argument_count_ == MORE_THAN_ONE) {
+ CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void InternalArrayConstructorStub::GenerateCase(
+ MacroAssembler* masm, ElementsKind kind) {
+ Label not_zero_case, not_one_case;
+ Label normal_sequence;
+
+ __ tst(r0, r0);
+ __ b(ne, &not_zero_case);
+ InternalArrayNoArgumentConstructorStub stub0(kind);
+ __ TailCallStub(&stub0);
+
+ __ bind(&not_zero_case);
+ __ cmp(r0, Operand(1));
+ __ b(gt, &not_one_case);
+
+ if (IsFastPackedElementsKind(kind)) {
+ // We might need to create a holey array
+ // look at the first argument
+ __ ldr(r3, MemOperand(sp, 0));
+ __ cmp(r3, Operand::Zero());
+ __ b(eq, &normal_sequence);
+
+ InternalArraySingleArgumentConstructorStub
+ stub1_holey(GetHoleyElementsKind(kind));
+ __ TailCallStub(&stub1_holey);
+ }
+
+ __ bind(&normal_sequence);
+ InternalArraySingleArgumentConstructorStub stub1(kind);
+ __ TailCallStub(&stub1);
+
+ __ bind(&not_one_case);
+ InternalArrayNArgumentsConstructorStub stubN(kind);
+ __ TailCallStub(&stubN);
+}
+
+
+void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r0 : argc
+ // -- r1 : constructor
+ // -- sp[0] : return address
+ // -- sp[4] : last argument
+ // -----------------------------------
+
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the global and natives
+ // builtin Array functions which always have maps.
+
+ // Initial map for the builtin Array function should be a map.
+ __ ldr(r3, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ __ tst(r3, Operand(kSmiTagMask));
+ __ Assert(ne, kUnexpectedInitialMapForArrayFunction);
+ __ CompareObjectType(r3, r3, r4, MAP_TYPE);
+ __ Assert(eq, kUnexpectedInitialMapForArrayFunction);
+ }
+
+ // Figure out the right elements kind
+ __ ldr(r3, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset));
+ // 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.
+ __ ldr(r3, FieldMemOperand(r3, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ Ubfx(r3, r3, Map::kElementsKindShift, Map::kElementsKindBitCount);
+
+ if (FLAG_debug_code) {
+ Label done;
+ __ cmp(r3, Operand(FAST_ELEMENTS));
+ __ b(eq, &done);
+ __ cmp(r3, Operand(FAST_HOLEY_ELEMENTS));
+ __ Assert(eq,
+ kInvalidElementsKindForInternalArrayOrInternalPackedArray);
+ __ bind(&done);
+ }
+
+ Label fast_elements_case;
+ __ cmp(r3, Operand(FAST_ELEMENTS));
+ __ b(eq, &fast_elements_case);
+ GenerateCase(masm, FAST_HOLEY_ELEMENTS);
+
+ __ bind(&fast_elements_case);
+ GenerateCase(masm, FAST_ELEMENTS);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/code-stubs-arm.h b/chromium/v8/src/arm/code-stubs-arm.h
new file mode 100644
index 00000000000..6eab8d128ee
--- /dev/null
+++ b/chromium/v8/src/arm/code-stubs-arm.h
@@ -0,0 +1,569 @@
+// Copyright 2012 the V8 project authors. 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_ARM_CODE_STUBS_ARM_H_
+#define V8_ARM_CODE_STUBS_ARM_H_
+
+#include "ic-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code);
+
+
+// Compute a transcendental math function natively, or call the
+// TranscendentalCache runtime function.
+class TranscendentalCacheStub: public PlatformCodeStub {
+ public:
+ enum ArgumentType {
+ TAGGED = 0 << TranscendentalCache::kTranscendentalTypeBits,
+ UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
+ };
+
+ TranscendentalCacheStub(TranscendentalCache::Type type,
+ ArgumentType argument_type)
+ : type_(type), argument_type_(argument_type) { }
+ void Generate(MacroAssembler* masm);
+ private:
+ TranscendentalCache::Type type_;
+ ArgumentType argument_type_;
+ void GenerateCallCFunction(MacroAssembler* masm, Register scratch);
+
+ Major MajorKey() { return TranscendentalCache; }
+ int MinorKey() { return type_ | argument_type_; }
+ Runtime::FunctionId RuntimeFunction();
+};
+
+
+class StoreBufferOverflowStub: public PlatformCodeStub {
+ public:
+ explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
+ : save_doubles_(save_fp) {}
+
+ void Generate(MacroAssembler* masm);
+
+ virtual bool IsPregenerated() { return true; }
+ static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ private:
+ SaveFPRegsMode save_doubles_;
+
+ Major MajorKey() { return StoreBufferOverflow; }
+ int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
+};
+
+
+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 string 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 string 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 GenerateTwoCharacterStringTableProbe(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);
+};
+
+
+class StringAddStub: public PlatformCodeStub {
+ public:
+ explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
+
+ private:
+ Major MajorKey() { return StringAdd; }
+ int MinorKey() { return flags_; }
+
+ void Generate(MacroAssembler* masm);
+
+ void GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* slow);
+
+ void GenerateRegisterArgsPush(MacroAssembler* masm);
+ void GenerateRegisterArgsPop(MacroAssembler* masm);
+
+ const StringAddFlags flags_;
+};
+
+
+class SubStringStub: public PlatformCodeStub {
+ public:
+ SubStringStub() {}
+
+ private:
+ Major MajorKey() { return SubString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+
+class StringCompareStub: public PlatformCodeStub {
+ public:
+ StringCompareStub() { }
+
+ // Compares two flat ASCII strings and returns result in r0.
+ static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4);
+
+ // Compares two flat ASCII strings for equality and returns result
+ // in r0.
+ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3);
+
+ private:
+ virtual Major MajorKey() { return StringCompare; }
+ virtual int MinorKey() { return 0; }
+ virtual void Generate(MacroAssembler* masm);
+
+ static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* chars_not_equal);
+};
+
+
+// This stub can convert a signed int32 to a heap number (double). It does
+// not work for int32s that are in Smi range! No GC occurs during this stub
+// so you don't have to set up the frame.
+class WriteInt32ToHeapNumberStub : public PlatformCodeStub {
+ public:
+ WriteInt32ToHeapNumberStub(Register the_int,
+ Register the_heap_number,
+ Register scratch)
+ : the_int_(the_int),
+ the_heap_number_(the_heap_number),
+ scratch_(scratch) { }
+
+ bool IsPregenerated();
+ static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
+
+ private:
+ Register the_int_;
+ Register the_heap_number_;
+ Register scratch_;
+
+ // Minor key encoding in 16 bits.
+ class IntRegisterBits: public BitField<int, 0, 4> {};
+ class HeapNumberRegisterBits: public BitField<int, 4, 4> {};
+ class ScratchRegisterBits: public BitField<int, 8, 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());
+ }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class NumberToStringStub: public PlatformCodeStub {
+ public:
+ NumberToStringStub() { }
+
+ // Generate code to do a lookup in the number string cache. If the number in
+ // the register object is found in the cache the generated code falls through
+ // with the result in the result register. The object and the result register
+ // can be the same. If the number is not found in the cache the code jumps to
+ // the label not_found with only the content of register object unchanged.
+ static void GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_found);
+
+ private:
+ Major MajorKey() { return NumberToString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class RecordWriteStub: public PlatformCodeStub {
+ 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(Isolate* isolate);
+ 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) {
+ masm->SaveFPRegs(sp, scratch0_);
+ }
+ }
+
+ inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
+ SaveFPRegsMode mode) {
+ if (mode == kSaveFPRegs) {
+ masm->RestoreFPRegs(sp, scratch0_);
+ }
+ 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::NumAllocatableRegisters(); 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.
+class RegExpCEntryStub: public PlatformCodeStub {
+ public:
+ RegExpCEntryStub() {}
+ virtual ~RegExpCEntryStub() {}
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Major MajorKey() { return RegExpCEntry; }
+ int MinorKey() { return 0; }
+
+ bool NeedsImmovableCode() { return true; }
+};
+
+
+// Trampoline stub to call into native code. To call safely into native code
+// in the presence of compacting GC (which can move code objects) we need to
+// keep the code which called into native pinned in the memory. Currently the
+// simplest approach is to generate such stub early enough so it can never be
+// moved by GC
+class DirectCEntryStub: public PlatformCodeStub {
+ public:
+ DirectCEntryStub() {}
+ void Generate(MacroAssembler* masm);
+ void GenerateCall(MacroAssembler* masm, Register target);
+
+ private:
+ Major MajorKey() { return DirectCEntry; }
+ int MinorKey() { return 0; }
+
+ bool NeedsImmovableCode() { return true; }
+};
+
+
+class NameDictionaryLookupStub: public PlatformCodeStub {
+ public:
+ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
+
+ explicit NameDictionaryLookupStub(LookupMode mode) : mode_(mode) { }
+
+ void Generate(MacroAssembler* masm);
+
+ static void GenerateNegativeLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register receiver,
+ Register properties,
+ Handle<Name> name,
+ Register scratch0);
+
+ static void GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1);
+
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ private:
+ static const int kInlinedProbes = 4;
+ static const int kTotalProbes = 20;
+
+ static const int kCapacityOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kCapacityIndex * kPointerSize;
+
+ static const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+
+ Major MajorKey() { return NameDictionaryLookup; }
+
+ int MinorKey() {
+ return LookupModeBits::encode(mode_);
+ }
+
+ class LookupModeBits: public BitField<LookupMode, 0, 1> {};
+
+ LookupMode mode_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_CODE_STUBS_ARM_H_
diff --git a/chromium/v8/src/arm/codegen-arm.cc b/chromium/v8/src/arm/codegen-arm.cc
new file mode 100644
index 00000000000..1bcf3e3a605
--- /dev/null
+++ b/chromium/v8/src/arm/codegen-arm.cc
@@ -0,0 +1,893 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "codegen.h"
+#include "macro-assembler.h"
+#include "simulator-arm.h"
+
+namespace v8 {
+namespace internal {
+
+
+UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type) {
+ switch (type) {
+ case TranscendentalCache::SIN: return &sin;
+ case TranscendentalCache::COS: return &cos;
+ case TranscendentalCache::TAN: return &tan;
+ case TranscendentalCache::LOG: return &log;
+ default: UNIMPLEMENTED();
+ }
+ return NULL;
+}
+
+
+#define __ masm.
+
+
+#if defined(USE_SIMULATOR)
+byte* fast_exp_arm_machine_code = NULL;
+double fast_exp_simulator(double x) {
+ return Simulator::current(Isolate::Current())->CallFP(
+ fast_exp_arm_machine_code, x, 0);
+}
+#endif
+
+
+UnaryMathFunction CreateExpFunction() {
+ if (!FLAG_fast_math) return &exp;
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true));
+ if (buffer == NULL) return &exp;
+ ExternalReference::InitializeMathExpData();
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+
+ {
+ DwVfpRegister input = d0;
+ DwVfpRegister result = d1;
+ DwVfpRegister double_scratch1 = d2;
+ DwVfpRegister double_scratch2 = d3;
+ Register temp1 = r4;
+ Register temp2 = r5;
+ Register temp3 = r6;
+
+ if (masm.use_eabi_hardfloat()) {
+ // Input value is in d0 anyway, nothing to do.
+ } else {
+ __ vmov(input, r0, r1);
+ }
+ __ Push(temp3, temp2, temp1);
+ MathExpGenerator::EmitMathExp(
+ &masm, input, result, double_scratch1, double_scratch2,
+ temp1, temp2, temp3);
+ __ Pop(temp3, temp2, temp1);
+ if (masm.use_eabi_hardfloat()) {
+ __ vmov(d0, result);
+ } else {
+ __ vmov(r0, r1, result);
+ }
+ __ Ret();
+ }
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+
+#if !defined(USE_SIMULATOR)
+ return FUNCTION_CAST<UnaryMathFunction>(buffer);
+#else
+ fast_exp_arm_machine_code = buffer;
+ return &fast_exp_simulator;
+#endif
+}
+
+#if defined(V8_HOST_ARCH_ARM)
+OS::MemCopyUint8Function CreateMemCopyUint8Function(
+ OS::MemCopyUint8Function stub) {
+#if defined(USE_SIMULATOR)
+ return stub;
+#else
+ if (Serializer::enabled() || !CpuFeatures::IsSupported(UNALIGNED_ACCESSES)) {
+ return stub;
+ }
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true));
+ if (buffer == NULL) return stub;
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+
+ Register dest = r0;
+ Register src = r1;
+ Register chars = r2;
+ Register temp1 = r3;
+ Label less_4;
+
+ if (CpuFeatures::IsSupported(NEON)) {
+ Label loop, less_256, less_128, less_64, less_32, _16_or_less, _8_or_less;
+ Label size_less_than_8;
+ __ pld(MemOperand(src, 0));
+
+ __ cmp(chars, Operand(8));
+ __ b(lt, &size_less_than_8);
+ __ cmp(chars, Operand(32));
+ __ b(lt, &less_32);
+ if (CpuFeatures::cache_line_size() == 32) {
+ __ pld(MemOperand(src, 32));
+ }
+ __ cmp(chars, Operand(64));
+ __ b(lt, &less_64);
+ __ pld(MemOperand(src, 64));
+ if (CpuFeatures::cache_line_size() == 32) {
+ __ pld(MemOperand(src, 96));
+ }
+ __ cmp(chars, Operand(128));
+ __ b(lt, &less_128);
+ __ pld(MemOperand(src, 128));
+ if (CpuFeatures::cache_line_size() == 32) {
+ __ pld(MemOperand(src, 160));
+ }
+ __ pld(MemOperand(src, 192));
+ if (CpuFeatures::cache_line_size() == 32) {
+ __ pld(MemOperand(src, 224));
+ }
+ __ cmp(chars, Operand(256));
+ __ b(lt, &less_256);
+ __ sub(chars, chars, Operand(256));
+
+ __ bind(&loop);
+ __ pld(MemOperand(src, 256));
+ __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
+ if (CpuFeatures::cache_line_size() == 32) {
+ __ pld(MemOperand(src, 256));
+ }
+ __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex));
+ __ sub(chars, chars, Operand(64), SetCC);
+ __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
+ __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex));
+ __ b(ge, &loop);
+ __ add(chars, chars, Operand(256));
+
+ __ bind(&less_256);
+ __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
+ __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex));
+ __ sub(chars, chars, Operand(128));
+ __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
+ __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex));
+ __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
+ __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex));
+ __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
+ __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex));
+ __ cmp(chars, Operand(64));
+ __ b(lt, &less_64);
+
+ __ bind(&less_128);
+ __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
+ __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex));
+ __ sub(chars, chars, Operand(64));
+ __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
+ __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex));
+
+ __ bind(&less_64);
+ __ cmp(chars, Operand(32));
+ __ b(lt, &less_32);
+ __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex));
+ __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex));
+ __ sub(chars, chars, Operand(32));
+
+ __ bind(&less_32);
+ __ cmp(chars, Operand(16));
+ __ b(le, &_16_or_less);
+ __ vld1(Neon8, NeonListOperand(d0, 2), NeonMemOperand(src, PostIndex));
+ __ vst1(Neon8, NeonListOperand(d0, 2), NeonMemOperand(dest, PostIndex));
+ __ sub(chars, chars, Operand(16));
+
+ __ bind(&_16_or_less);
+ __ cmp(chars, Operand(8));
+ __ b(le, &_8_or_less);
+ __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src, PostIndex));
+ __ vst1(Neon8, NeonListOperand(d0), NeonMemOperand(dest, PostIndex));
+ __ sub(chars, chars, Operand(8));
+
+ // Do a last copy which may overlap with the previous copy (up to 8 bytes).
+ __ bind(&_8_or_less);
+ __ rsb(chars, chars, Operand(8));
+ __ sub(src, src, Operand(chars));
+ __ sub(dest, dest, Operand(chars));
+ __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src));
+ __ vst1(Neon8, NeonListOperand(d0), NeonMemOperand(dest));
+
+ __ Ret();
+
+ __ bind(&size_less_than_8);
+
+ __ bic(temp1, chars, Operand(0x3), SetCC);
+ __ b(&less_4, eq);
+ __ ldr(temp1, MemOperand(src, 4, PostIndex));
+ __ str(temp1, MemOperand(dest, 4, PostIndex));
+ } else {
+ Register temp2 = ip;
+ Label loop;
+
+ __ bic(temp2, chars, Operand(0x3), SetCC);
+ __ b(&less_4, eq);
+ __ add(temp2, dest, temp2);
+
+ __ bind(&loop);
+ __ ldr(temp1, MemOperand(src, 4, PostIndex));
+ __ str(temp1, MemOperand(dest, 4, PostIndex));
+ __ cmp(dest, temp2);
+ __ b(&loop, ne);
+ }
+
+ __ bind(&less_4);
+ __ mov(chars, Operand(chars, LSL, 31), SetCC);
+ // bit0 => Z (ne), bit1 => C (cs)
+ __ ldrh(temp1, MemOperand(src, 2, PostIndex), cs);
+ __ strh(temp1, MemOperand(dest, 2, PostIndex), cs);
+ __ ldrb(temp1, MemOperand(src), ne);
+ __ strb(temp1, MemOperand(dest), ne);
+ __ Ret();
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+ return FUNCTION_CAST<OS::MemCopyUint8Function>(buffer);
+#endif
+}
+
+
+// Convert 8 to 16. The number of character to copy must be at least 8.
+OS::MemCopyUint16Uint8Function CreateMemCopyUint16Uint8Function(
+ OS::MemCopyUint16Uint8Function stub) {
+#if defined(USE_SIMULATOR)
+ return stub;
+#else
+ if (Serializer::enabled() || !CpuFeatures::IsSupported(UNALIGNED_ACCESSES)) {
+ return stub;
+ }
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true));
+ if (buffer == NULL) return stub;
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+
+ Register dest = r0;
+ Register src = r1;
+ Register chars = r2;
+ if (CpuFeatures::IsSupported(NEON)) {
+ Register temp = r3;
+ Label loop;
+
+ __ bic(temp, chars, Operand(0x7));
+ __ sub(chars, chars, Operand(temp));
+ __ add(temp, dest, Operand(temp, LSL, 1));
+
+ __ bind(&loop);
+ __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src, PostIndex));
+ __ vmovl(NeonU8, q0, d0);
+ __ vst1(Neon16, NeonListOperand(d0, 2), NeonMemOperand(dest, PostIndex));
+ __ cmp(dest, temp);
+ __ b(&loop, ne);
+
+ // Do a last copy which will overlap with the previous copy (1 to 8 bytes).
+ __ rsb(chars, chars, Operand(8));
+ __ sub(src, src, Operand(chars));
+ __ sub(dest, dest, Operand(chars, LSL, 1));
+ __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src));
+ __ vmovl(NeonU8, q0, d0);
+ __ vst1(Neon16, NeonListOperand(d0, 2), NeonMemOperand(dest));
+ __ Ret();
+ } else {
+ Register temp1 = r3;
+ Register temp2 = ip;
+ Register temp3 = lr;
+ Register temp4 = r4;
+ Label loop;
+ Label not_two;
+
+ __ Push(lr, r4);
+ __ bic(temp2, chars, Operand(0x3));
+ __ add(temp2, dest, Operand(temp2, LSL, 1));
+
+ __ bind(&loop);
+ __ ldr(temp1, MemOperand(src, 4, PostIndex));
+ __ uxtb16(temp3, Operand(temp1, ROR, 0));
+ __ uxtb16(temp4, Operand(temp1, ROR, 8));
+ __ pkhbt(temp1, temp3, Operand(temp4, LSL, 16));
+ __ str(temp1, MemOperand(dest));
+ __ pkhtb(temp1, temp4, Operand(temp3, ASR, 16));
+ __ str(temp1, MemOperand(dest, 4));
+ __ add(dest, dest, Operand(8));
+ __ cmp(dest, temp2);
+ __ b(&loop, ne);
+
+ __ mov(chars, Operand(chars, LSL, 31), SetCC); // bit0 => ne, bit1 => cs
+ __ b(&not_two, cc);
+ __ ldrh(temp1, MemOperand(src, 2, PostIndex));
+ __ uxtb(temp3, Operand(temp1, ROR, 8));
+ __ mov(temp3, Operand(temp3, LSL, 16));
+ __ uxtab(temp3, temp3, Operand(temp1, ROR, 0));
+ __ str(temp3, MemOperand(dest, 4, PostIndex));
+ __ bind(&not_two);
+ __ ldrb(temp1, MemOperand(src), ne);
+ __ strh(temp1, MemOperand(dest), ne);
+ __ Pop(pc, r4);
+ }
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+
+ return FUNCTION_CAST<OS::MemCopyUint16Uint8Function>(buffer);
+#endif
+}
+#endif
+
+#undef __
+
+
+UnaryMathFunction CreateSqrtFunction() {
+ return &sqrt;
+}
+
+
+// -------------------------------------------------------------------------
+// Platform-specific RuntimeCallHelper functions.
+
+void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ masm->EnterFrame(StackFrame::INTERNAL);
+ ASSERT(!masm->has_frame());
+ masm->set_has_frame(true);
+}
+
+
+void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ masm->LeaveFrame(StackFrame::INTERNAL);
+ ASSERT(masm->has_frame());
+ masm->set_has_frame(false);
+}
+
+
+// -------------------------------------------------------------------------
+// Code generators
+
+#define __ ACCESS_MASM(masm)
+
+void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
+ MacroAssembler* masm, AllocationSiteMode mode,
+ Label* allocation_memento_found) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -- r3 : target map, scratch for subsequent call
+ // -- r4 : scratch (elements)
+ // -----------------------------------
+ if (mode == TRACK_ALLOCATION_SITE) {
+ ASSERT(allocation_memento_found != NULL);
+ __ TestJSArrayForAllocationMemento(r2, r4);
+ __ b(eq, allocation_memento_found);
+ }
+
+ // 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::GenerateSmiToDouble(
+ MacroAssembler* masm, AllocationSiteMode mode, 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, only_change_map, done;
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ __ TestJSArrayForAllocationMemento(r2, r4);
+ __ b(eq, fail);
+ }
+
+ // Check for empty arrays, which only require a map transition and no changes
+ // to the backing store.
+ __ ldr(r4, FieldMemOperand(r2, JSObject::kElementsOffset));
+ __ CompareRoot(r4, Heap::kEmptyFixedArrayRootIndex);
+ __ b(eq, &only_change_map);
+
+ __ push(lr);
+ __ ldr(r5, FieldMemOperand(r4, FixedArray::kLengthOffset));
+ // r4: source FixedArray
+ // r5: number of elements (smi-tagged)
+
+ // Allocate new FixedDoubleArray.
+ // Use lr as a temporary register.
+ __ mov(lr, Operand(r5, LSL, 2));
+ __ add(lr, lr, Operand(FixedDoubleArray::kHeaderSize));
+ __ Allocate(lr, r6, r7, r9, &gc_required, DOUBLE_ALIGNMENT);
+ // 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));
+ // Update receiver's map.
+ __ str(r9, MemOperand(r6, HeapObject::kMapOffset));
+
+ __ str(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
+ __ RecordWriteField(r2,
+ HeapObject::kMapOffset,
+ r3,
+ r9,
+ kLRHasBeenSaved,
+ kDontSaveFPRegs,
+ OMIT_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
+
+ __ b(&entry);
+
+ __ bind(&only_change_map);
+ __ str(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
+ __ RecordWriteField(r2,
+ HeapObject::kMapOffset,
+ r3,
+ r9,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ __ b(&done);
+
+ // 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.
+ __ vmov(s0, r9);
+ __ vcvt_f64_s32(d0, s0);
+ __ vstr(d0, r7, 0);
+ __ add(r7, r7, Operand(8));
+ __ 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, kObjectFoundInSmiOnlyArray);
+ }
+ __ Strd(r4, r5, MemOperand(r7, 8, PostIndex));
+
+ __ bind(&entry);
+ __ cmp(r7, r6);
+ __ b(lt, &loop);
+
+ __ pop(lr);
+ __ bind(&done);
+}
+
+
+void ElementsTransitionGenerator::GenerateDoubleToObject(
+ MacroAssembler* masm, AllocationSiteMode mode, 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, only_change_map;
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ __ TestJSArrayForAllocationMemento(r2, r4);
+ __ b(eq, fail);
+ }
+
+ // Check for empty arrays, which only require a map transition and no changes
+ // to the backing store.
+ __ ldr(r4, FieldMemOperand(r2, JSObject::kElementsOffset));
+ __ CompareRoot(r4, Heap::kEmptyFixedArrayRootIndex);
+ __ b(eq, &only_change_map);
+
+ __ push(lr);
+ __ 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));
+ __ Allocate(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);
+ // 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);
+
+ __ bind(&only_change_map);
+ // Update receiver's map.
+ __ str(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
+ __ RecordWriteField(r2,
+ HeapObject::kMapOffset,
+ r3,
+ r9,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+}
+
+
+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::SmiUntag(result));
+ __ 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::kempty_stringRootIndex);
+ __ 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 == SeqOneByteString::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, kExternalStringExpectedButNotFound);
+ }
+ // 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);
+}
+
+
+static MemOperand ExpConstant(int index, Register base) {
+ return MemOperand(base, index * kDoubleSize);
+}
+
+
+void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
+ DwVfpRegister input,
+ DwVfpRegister result,
+ DwVfpRegister double_scratch1,
+ DwVfpRegister double_scratch2,
+ Register temp1,
+ Register temp2,
+ Register temp3) {
+ ASSERT(!input.is(result));
+ ASSERT(!input.is(double_scratch1));
+ ASSERT(!input.is(double_scratch2));
+ ASSERT(!result.is(double_scratch1));
+ ASSERT(!result.is(double_scratch2));
+ ASSERT(!double_scratch1.is(double_scratch2));
+ ASSERT(!temp1.is(temp2));
+ ASSERT(!temp1.is(temp3));
+ ASSERT(!temp2.is(temp3));
+ ASSERT(ExternalReference::math_exp_constants(0).address() != NULL);
+
+ Label done;
+
+ __ mov(temp3, Operand(ExternalReference::math_exp_constants(0)));
+
+ __ vldr(double_scratch1, ExpConstant(0, temp3));
+ __ vmov(result, kDoubleRegZero);
+ __ VFPCompareAndSetFlags(double_scratch1, input);
+ __ b(ge, &done);
+ __ vldr(double_scratch2, ExpConstant(1, temp3));
+ __ VFPCompareAndSetFlags(input, double_scratch2);
+ __ vldr(result, ExpConstant(2, temp3));
+ __ b(ge, &done);
+ __ vldr(double_scratch1, ExpConstant(3, temp3));
+ __ vldr(result, ExpConstant(4, temp3));
+ __ vmul(double_scratch1, double_scratch1, input);
+ __ vadd(double_scratch1, double_scratch1, result);
+ __ vmov(temp2, temp1, double_scratch1);
+ __ vsub(double_scratch1, double_scratch1, result);
+ __ vldr(result, ExpConstant(6, temp3));
+ __ vldr(double_scratch2, ExpConstant(5, temp3));
+ __ vmul(double_scratch1, double_scratch1, double_scratch2);
+ __ vsub(double_scratch1, double_scratch1, input);
+ __ vsub(result, result, double_scratch1);
+ __ vmul(input, double_scratch1, double_scratch1);
+ __ vmul(result, result, input);
+ __ mov(temp1, Operand(temp2, LSR, 11));
+ __ vldr(double_scratch2, ExpConstant(7, temp3));
+ __ vmul(result, result, double_scratch2);
+ __ vsub(result, result, double_scratch1);
+ __ vldr(double_scratch2, ExpConstant(8, temp3));
+ __ vadd(result, result, double_scratch2);
+ __ movw(ip, 0x7ff);
+ __ and_(temp2, temp2, Operand(ip));
+ __ add(temp1, temp1, Operand(0x3ff));
+ __ mov(temp1, Operand(temp1, LSL, 20));
+
+ // Must not call ExpConstant() after overwriting temp3!
+ __ mov(temp3, Operand(ExternalReference::math_exp_log_table()));
+ __ ldr(ip, MemOperand(temp3, temp2, LSL, 3));
+ __ add(temp3, temp3, Operand(kPointerSize));
+ __ ldr(temp2, MemOperand(temp3, temp2, LSL, 3));
+ __ orr(temp1, temp1, temp2);
+ __ vmov(input, ip, temp1);
+ __ vmul(result, result, input);
+ __ bind(&done);
+}
+
+#undef __
+
+// add(r0, pc, Operand(-8))
+static const uint32_t kCodeAgePatchFirstInstruction = 0xe24f0008;
+
+static byte* GetNoCodeAgeSequence(uint32_t* length) {
+ // The sequence of instructions that is patched out for aging code is the
+ // following boilerplate stack-building prologue that is found in FUNCTIONS
+ static bool initialized = false;
+ static uint32_t sequence[kNoCodeAgeSequenceLength];
+ byte* byte_sequence = reinterpret_cast<byte*>(sequence);
+ *length = kNoCodeAgeSequenceLength * Assembler::kInstrSize;
+ if (!initialized) {
+ CodePatcher patcher(byte_sequence, kNoCodeAgeSequenceLength);
+ PredictableCodeSizeScope scope(patcher.masm(), *length);
+ patcher.masm()->stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
+ patcher.masm()->nop(ip.code());
+ patcher.masm()->add(fp, sp, Operand(2 * kPointerSize));
+ initialized = true;
+ }
+ return byte_sequence;
+}
+
+
+bool Code::IsYoungSequence(byte* sequence) {
+ uint32_t young_length;
+ byte* young_sequence = GetNoCodeAgeSequence(&young_length);
+ bool result = !memcmp(sequence, young_sequence, young_length);
+ ASSERT(result ||
+ Memory::uint32_at(sequence) == kCodeAgePatchFirstInstruction);
+ return result;
+}
+
+
+void Code::GetCodeAgeAndParity(byte* sequence, Age* age,
+ MarkingParity* parity) {
+ if (IsYoungSequence(sequence)) {
+ *age = kNoAge;
+ *parity = NO_MARKING_PARITY;
+ } else {
+ Address target_address = Memory::Address_at(
+ sequence + Assembler::kInstrSize * (kNoCodeAgeSequenceLength - 1));
+ Code* stub = GetCodeFromTargetAddress(target_address);
+ GetCodeAgeAndParity(stub, age, parity);
+ }
+}
+
+
+void Code::PatchPlatformCodeAge(byte* sequence,
+ Code::Age age,
+ MarkingParity parity) {
+ uint32_t young_length;
+ byte* young_sequence = GetNoCodeAgeSequence(&young_length);
+ if (age == kNoAge) {
+ CopyBytes(sequence, young_sequence, young_length);
+ CPU::FlushICache(sequence, young_length);
+ } else {
+ Code* stub = GetCodeAgeStub(age, parity);
+ CodePatcher patcher(sequence, young_length / Assembler::kInstrSize);
+ patcher.masm()->add(r0, pc, Operand(-8));
+ patcher.masm()->ldr(pc, MemOperand(pc, -4));
+ patcher.masm()->dd(reinterpret_cast<uint32_t>(stub->instruction_start()));
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/codegen-arm.h b/chromium/v8/src/arm/codegen-arm.h
new file mode 100644
index 00000000000..c020ab601c8
--- /dev/null
+++ b/chromium/v8/src/arm/codegen-arm.h
@@ -0,0 +1,115 @@
+// 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_ARM_CODEGEN_ARM_H_
+#define V8_ARM_CODEGEN_ARM_H_
+
+#include "ast.h"
+#include "ic-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations
+class CompilationInfo;
+
+enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
+
+// -------------------------------------------------------------------------
+// CodeGenerator
+
+class CodeGenerator: public AstVisitor {
+ public:
+ CodeGenerator() {
+ InitializeAstVisitor();
+ }
+
+ static bool MakeCode(CompilationInfo* info);
+
+ // Printing of AST, etc. as requested by flags.
+ static void MakeCodePrologue(CompilationInfo* info, const char* kind);
+
+ // Allocate and install the code.
+ static Handle<Code> MakeCodeEpilogue(MacroAssembler* masm,
+ Code::Flags flags,
+ CompilationInfo* info);
+
+ // Print the code after compiling it.
+ static void PrintCode(Handle<Code> code, CompilationInfo* info);
+
+ static bool ShouldGenerateLog(Expression* type);
+
+ static void SetFunctionInfo(Handle<JSFunction> fun,
+ FunctionLiteral* lit,
+ bool is_toplevel,
+ Handle<Script> script);
+
+ static bool RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here = false);
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+
+ 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);
+};
+
+
+class MathExpGenerator : public AllStatic {
+ public:
+ static void EmitMathExp(MacroAssembler* masm,
+ DwVfpRegister input,
+ DwVfpRegister result,
+ DwVfpRegister double_scratch1,
+ DwVfpRegister double_scratch2,
+ Register temp1,
+ Register temp2,
+ Register temp3);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MathExpGenerator);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_CODEGEN_ARM_H_
diff --git a/chromium/v8/src/arm/constants-arm.cc b/chromium/v8/src/arm/constants-arm.cc
new file mode 100644
index 00000000000..7d59a84b1d3
--- /dev/null
+++ b/chromium/v8/src/arm/constants-arm.cc
@@ -0,0 +1,154 @@
+// 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.
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "constants-arm.h"
+
+
+namespace v8 {
+namespace internal {
+
+double Instruction::DoubleImmedVmov() const {
+ // Reconstruct a double from the immediate encoded in the vmov instruction.
+ //
+ // instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh]
+ // double: [aBbbbbbb,bbcdefgh,00000000,00000000,
+ // 00000000,00000000,00000000,00000000]
+ //
+ // where B = ~b. Only the high 16 bits are affected.
+ uint64_t high16;
+ high16 = (Bits(17, 16) << 4) | Bits(3, 0); // xxxxxxxx,xxcdefgh.
+ high16 |= (0xff * Bit(18)) << 6; // xxbbbbbb,bbxxxxxx.
+ high16 |= (Bit(18) ^ 1) << 14; // xBxxxxxx,xxxxxxxx.
+ high16 |= Bit(19) << 15; // axxxxxxx,xxxxxxxx.
+
+ uint64_t imm = high16 << 48;
+ double d;
+ OS::MemCopy(&d, &imm, 8);
+ return d;
+}
+
+
+// These register names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+const char* Registers::names_[kNumRegisters] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "fp", "ip", "sp", "lr", "pc",
+};
+
+
+// List of alias names which can be used when referring to ARM registers.
+const Registers::RegisterAlias Registers::aliases_[] = {
+ {10, "sl"},
+ {11, "r11"},
+ {12, "r12"},
+ {13, "r13"},
+ {14, "r14"},
+ {15, "r15"},
+ {kNoRegister, NULL}
+};
+
+
+const char* Registers::Name(int reg) {
+ const char* result;
+ if ((0 <= reg) && (reg < kNumRegisters)) {
+ result = names_[reg];
+ } else {
+ result = "noreg";
+ }
+ return result;
+}
+
+
+// Support for VFP registers s0 to s31 (d0 to d15) and d16-d31.
+// Note that "sN:sM" is the same as "dN/2" up to d15.
+// These register names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+const char* VFPRegisters::names_[kNumVFPRegisters] = {
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
+ "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
+ "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
+ "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+ "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
+ "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
+ "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
+};
+
+
+const char* VFPRegisters::Name(int reg, bool is_double) {
+ ASSERT((0 <= reg) && (reg < kNumVFPRegisters));
+ return names_[reg + (is_double ? kNumVFPSingleRegisters : 0)];
+}
+
+
+int VFPRegisters::Number(const char* name, bool* is_double) {
+ for (int i = 0; i < kNumVFPRegisters; i++) {
+ if (strcmp(names_[i], name) == 0) {
+ if (i < kNumVFPSingleRegisters) {
+ *is_double = false;
+ return i;
+ } else {
+ *is_double = true;
+ return i - kNumVFPSingleRegisters;
+ }
+ }
+ }
+
+ // No register with the requested name found.
+ return kNoRegister;
+}
+
+
+int Registers::Number(const char* name) {
+ // Look through the canonical names.
+ for (int i = 0; i < kNumRegisters; i++) {
+ if (strcmp(names_[i], name) == 0) {
+ return i;
+ }
+ }
+
+ // Look through the alias names.
+ int i = 0;
+ while (aliases_[i].reg != kNoRegister) {
+ if (strcmp(aliases_[i].name, name) == 0) {
+ return aliases_[i].reg;
+ }
+ i++;
+ }
+
+ // No register with the requested name found.
+ return kNoRegister;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/constants-arm.h b/chromium/v8/src/arm/constants-arm.h
new file mode 100644
index 00000000000..9bfccf822b3
--- /dev/null
+++ b/chromium/v8/src/arm/constants-arm.h
@@ -0,0 +1,768 @@
+// 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_ARM_CONSTANTS_ARM_H_
+#define V8_ARM_CONSTANTS_ARM_H_
+
+// ARM EABI is required.
+#if defined(__arm__) && !defined(__ARM_EABI__)
+#error ARM EABI support is required.
+#endif
+
+namespace v8 {
+namespace internal {
+
+// Constant pool marker.
+// Use UDF, the permanently undefined instruction.
+const int kConstantPoolMarkerMask = 0xfff000f0;
+const int kConstantPoolMarker = 0xe7f000f0;
+const int kConstantPoolLengthMaxMask = 0xffff;
+inline int EncodeConstantPoolLength(int length) {
+ ASSERT((length & kConstantPoolLengthMaxMask) == length);
+ return ((length & 0xfff0) << 4) | (length & 0xf);
+}
+inline int DecodeConstantPoolLength(int instr) {
+ ASSERT((instr & kConstantPoolMarkerMask) == kConstantPoolMarker);
+ return ((instr >> 4) & 0xfff0) | (instr & 0xf);
+}
+
+// Number of registers in normal ARM mode.
+const int kNumRegisters = 16;
+
+// VFP support.
+const int kNumVFPSingleRegisters = 32;
+const int kNumVFPDoubleRegisters = 32;
+const int kNumVFPRegisters = kNumVFPSingleRegisters + kNumVFPDoubleRegisters;
+
+// PC is register 15.
+const int kPCRegister = 15;
+const int kNoRegister = -1;
+
+// -----------------------------------------------------------------------------
+// Conditions.
+
+// Defines constants and accessor classes to assemble, disassemble and
+// simulate ARM instructions.
+//
+// Section references in the code refer to the "ARM Architecture Reference
+// Manual" from July 2005 (available at http://www.arm.com/miscPDFs/14128.pdf)
+//
+// Constants for specific fields are defined in their respective named enums.
+// General constants are in an anonymous enum in class Instr.
+
+// Values for the condition field as defined in section A3.2
+enum Condition {
+ kNoCondition = -1,
+
+ eq = 0 << 28, // Z set Equal.
+ ne = 1 << 28, // Z clear Not equal.
+ cs = 2 << 28, // C set Unsigned higher or same.
+ cc = 3 << 28, // C clear Unsigned lower.
+ mi = 4 << 28, // N set Negative.
+ pl = 5 << 28, // N clear Positive or zero.
+ vs = 6 << 28, // V set Overflow.
+ vc = 7 << 28, // V clear No overflow.
+ hi = 8 << 28, // C set, Z clear Unsigned higher.
+ ls = 9 << 28, // C clear or Z set Unsigned lower or same.
+ ge = 10 << 28, // N == V Greater or equal.
+ lt = 11 << 28, // N != V Less than.
+ gt = 12 << 28, // Z clear, N == V Greater than.
+ le = 13 << 28, // Z set or N != V Less then or equal
+ al = 14 << 28, // Always.
+
+ kSpecialCondition = 15 << 28, // Special condition (refer to section A3.2.1).
+ kNumberOfConditions = 16,
+
+ // Aliases.
+ hs = cs, // C set Unsigned higher or same.
+ lo = cc // C clear Unsigned lower.
+};
+
+
+inline Condition NegateCondition(Condition cond) {
+ ASSERT(cond != al);
+ return static_cast<Condition>(cond ^ ne);
+}
+
+
+// Corresponds to transposing the operands of a comparison.
+inline Condition ReverseCondition(Condition cond) {
+ switch (cond) {
+ case lo:
+ return hi;
+ case hi:
+ return lo;
+ case hs:
+ return ls;
+ case ls:
+ return hs;
+ case lt:
+ return gt;
+ case gt:
+ return lt;
+ case ge:
+ return le;
+ case le:
+ return ge;
+ default:
+ return cond;
+ };
+}
+
+
+// -----------------------------------------------------------------------------
+// Instructions encoding.
+
+// Instr is merely used by the Assembler to distinguish 32bit integers
+// representing instructions from usual 32 bit values.
+// Instruction objects are pointers to 32bit values, and provide methods to
+// access the various ISA fields.
+typedef int32_t Instr;
+
+
+// Opcodes for Data-processing instructions (instructions with a type 0 and 1)
+// as defined in section A3.4
+enum Opcode {
+ AND = 0 << 21, // Logical AND.
+ EOR = 1 << 21, // Logical Exclusive OR.
+ SUB = 2 << 21, // Subtract.
+ RSB = 3 << 21, // Reverse Subtract.
+ ADD = 4 << 21, // Add.
+ ADC = 5 << 21, // Add with Carry.
+ SBC = 6 << 21, // Subtract with Carry.
+ RSC = 7 << 21, // Reverse Subtract with Carry.
+ TST = 8 << 21, // Test.
+ TEQ = 9 << 21, // Test Equivalence.
+ CMP = 10 << 21, // Compare.
+ CMN = 11 << 21, // Compare Negated.
+ ORR = 12 << 21, // Logical (inclusive) OR.
+ MOV = 13 << 21, // Move.
+ BIC = 14 << 21, // Bit Clear.
+ MVN = 15 << 21 // Move Not.
+};
+
+
+// The bits for bit 7-4 for some type 0 miscellaneous instructions.
+enum MiscInstructionsBits74 {
+ // With bits 22-21 01.
+ BX = 1 << 4,
+ BXJ = 2 << 4,
+ BLX = 3 << 4,
+ BKPT = 7 << 4,
+
+ // With bits 22-21 11.
+ CLZ = 1 << 4
+};
+
+
+// Instruction encoding bits and masks.
+enum {
+ H = 1 << 5, // Halfword (or byte).
+ S6 = 1 << 6, // Signed (or unsigned).
+ L = 1 << 20, // Load (or store).
+ S = 1 << 20, // Set condition code (or leave unchanged).
+ W = 1 << 21, // Writeback base register (or leave unchanged).
+ A = 1 << 21, // Accumulate in multiply instruction (or not).
+ B = 1 << 22, // Unsigned byte (or word).
+ N = 1 << 22, // Long (or short).
+ U = 1 << 23, // Positive (or negative) offset/index.
+ P = 1 << 24, // Offset/pre-indexed addressing (or post-indexed addressing).
+ I = 1 << 25, // Immediate shifter operand (or not).
+
+ B4 = 1 << 4,
+ B5 = 1 << 5,
+ B6 = 1 << 6,
+ B7 = 1 << 7,
+ B8 = 1 << 8,
+ B9 = 1 << 9,
+ B12 = 1 << 12,
+ B16 = 1 << 16,
+ B18 = 1 << 18,
+ B19 = 1 << 19,
+ B20 = 1 << 20,
+ B21 = 1 << 21,
+ B22 = 1 << 22,
+ B23 = 1 << 23,
+ B24 = 1 << 24,
+ B25 = 1 << 25,
+ B26 = 1 << 26,
+ B27 = 1 << 27,
+ B28 = 1 << 28,
+
+ // Instruction bit masks.
+ kCondMask = 15 << 28,
+ kALUMask = 0x6f << 21,
+ kRdMask = 15 << 12, // In str instruction.
+ kCoprocessorMask = 15 << 8,
+ kOpCodeMask = 15 << 21, // In data-processing instructions.
+ kImm24Mask = (1 << 24) - 1,
+ kOff12Mask = (1 << 12) - 1,
+ kOff8Mask = (1 << 8) - 1
+};
+
+
+// -----------------------------------------------------------------------------
+// Addressing modes and instruction variants.
+
+// Condition code updating mode.
+enum SBit {
+ SetCC = 1 << 20, // Set condition code.
+ LeaveCC = 0 << 20 // Leave condition code unchanged.
+};
+
+
+// Status register selection.
+enum SRegister {
+ CPSR = 0 << 22,
+ SPSR = 1 << 22
+};
+
+
+// Shifter types for Data-processing operands as defined in section A5.1.2.
+enum ShiftOp {
+ LSL = 0 << 5, // Logical shift left.
+ LSR = 1 << 5, // Logical shift right.
+ ASR = 2 << 5, // Arithmetic shift right.
+ ROR = 3 << 5, // Rotate right.
+
+ // RRX is encoded as ROR with shift_imm == 0.
+ // Use a special code to make the distinction. The RRX ShiftOp is only used
+ // as an argument, and will never actually be encoded. The Assembler will
+ // detect it and emit the correct ROR shift operand with shift_imm == 0.
+ RRX = -1,
+ kNumberOfShifts = 4
+};
+
+
+// Status register fields.
+enum SRegisterField {
+ CPSR_c = CPSR | 1 << 16,
+ CPSR_x = CPSR | 1 << 17,
+ CPSR_s = CPSR | 1 << 18,
+ CPSR_f = CPSR | 1 << 19,
+ SPSR_c = SPSR | 1 << 16,
+ SPSR_x = SPSR | 1 << 17,
+ SPSR_s = SPSR | 1 << 18,
+ SPSR_f = SPSR | 1 << 19
+};
+
+// Status register field mask (or'ed SRegisterField enum values).
+typedef uint32_t SRegisterFieldMask;
+
+
+// Memory operand addressing mode.
+enum AddrMode {
+ // Bit encoding P U W.
+ Offset = (8|4|0) << 21, // Offset (without writeback to base).
+ PreIndex = (8|4|1) << 21, // Pre-indexed addressing with writeback.
+ PostIndex = (0|4|0) << 21, // Post-indexed addressing with writeback.
+ NegOffset = (8|0|0) << 21, // Negative offset (without writeback to base).
+ NegPreIndex = (8|0|1) << 21, // Negative pre-indexed with writeback.
+ NegPostIndex = (0|0|0) << 21 // Negative post-indexed with writeback.
+};
+
+
+// Load/store multiple addressing mode.
+enum BlockAddrMode {
+ // Bit encoding P U W .
+ da = (0|0|0) << 21, // Decrement after.
+ ia = (0|4|0) << 21, // Increment after.
+ db = (8|0|0) << 21, // Decrement before.
+ ib = (8|4|0) << 21, // Increment before.
+ da_w = (0|0|1) << 21, // Decrement after with writeback to base.
+ ia_w = (0|4|1) << 21, // Increment after with writeback to base.
+ db_w = (8|0|1) << 21, // Decrement before with writeback to base.
+ ib_w = (8|4|1) << 21, // Increment before with writeback to base.
+
+ // Alias modes for comparison when writeback does not matter.
+ da_x = (0|0|0) << 21, // Decrement after.
+ ia_x = (0|4|0) << 21, // Increment after.
+ db_x = (8|0|0) << 21, // Decrement before.
+ ib_x = (8|4|0) << 21, // Increment before.
+
+ kBlockAddrModeMask = (8|4|1) << 21
+};
+
+
+// Coprocessor load/store operand size.
+enum LFlag {
+ Long = 1 << 22, // Long load/store coprocessor.
+ Short = 0 << 22 // Short load/store coprocessor.
+};
+
+
+// NEON data type
+enum NeonDataType {
+ NeonS8 = 0x1, // U = 0, imm3 = 0b001
+ NeonS16 = 0x2, // U = 0, imm3 = 0b010
+ NeonS32 = 0x4, // U = 0, imm3 = 0b100
+ NeonU8 = 1 << 24 | 0x1, // U = 1, imm3 = 0b001
+ NeonU16 = 1 << 24 | 0x2, // U = 1, imm3 = 0b010
+ NeonU32 = 1 << 24 | 0x4, // U = 1, imm3 = 0b100
+ NeonDataTypeSizeMask = 0x7,
+ NeonDataTypeUMask = 1 << 24
+};
+
+enum NeonListType {
+ nlt_1 = 0x7,
+ nlt_2 = 0xA,
+ nlt_3 = 0x6,
+ nlt_4 = 0x2
+};
+
+enum NeonSize {
+ Neon8 = 0x0,
+ Neon16 = 0x1,
+ Neon32 = 0x2,
+ Neon64 = 0x4
+};
+
+// -----------------------------------------------------------------------------
+// Supervisor Call (svc) specific support.
+
+// Special Software Interrupt codes when used in the presence of the ARM
+// simulator.
+// svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for
+// standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature.
+enum SoftwareInterruptCodes {
+ // transition to C code
+ kCallRtRedirected= 0x10,
+ // break point
+ kBreakpoint= 0x20,
+ // stop
+ kStopCode = 1 << 23
+};
+const uint32_t kStopCodeMask = kStopCode - 1;
+const uint32_t kMaxStopCode = kStopCode - 1;
+const int32_t kDefaultStopCode = -1;
+
+
+// Type of VFP register. Determines register encoding.
+enum VFPRegPrecision {
+ kSinglePrecision = 0,
+ kDoublePrecision = 1
+};
+
+
+// VFP FPSCR constants.
+enum VFPConversionMode {
+ kFPSCRRounding = 0,
+ kDefaultRoundToZero = 1
+};
+
+// This mask does not include the "inexact" or "input denormal" cumulative
+// exceptions flags, because we usually don't want to check for it.
+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;
+const uint32_t kVFPDefaultNaNModeControlBit = 1 << 25;
+
+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.
+enum VFPRoundingMode {
+ RN = 0 << 22, // Round to Nearest.
+ RP = 1 << 22, // Round towards Plus Infinity.
+ RM = 2 << 22, // Round towards Minus Infinity.
+ RZ = 3 << 22, // Round towards zero.
+
+ // Aliases.
+ kRoundToNearest = RN,
+ kRoundToPlusInf = RP,
+ kRoundToMinusInf = RM,
+ kRoundToZero = RZ
+};
+
+const uint32_t kVFPRoundingModeMask = 3 << 22;
+
+enum CheckForInexactConversion {
+ kCheckForInexactConversion,
+ kDontCheckForInexactConversion
+};
+
+// -----------------------------------------------------------------------------
+// Hints.
+
+// Branch hints are not used on the ARM. They are defined so that they can
+// appear in shared function signatures, but will be ignored in ARM
+// implementations.
+enum Hint { no_hint };
+
+// Hints are not used on the arm. Negating is trivial.
+inline Hint NegateHint(Hint ignored) { return no_hint; }
+
+
+// -----------------------------------------------------------------------------
+// Specific instructions, constants, and masks.
+// These constants are declared in assembler-arm.cc, as they use named registers
+// and other constants.
+
+
+// add(sp, sp, 4) instruction (aka Pop())
+extern const Instr kPopInstruction;
+
+// str(r, MemOperand(sp, 4, NegPreIndex), al) instruction (aka push(r))
+// register r is not encoded.
+extern const Instr kPushRegPattern;
+
+// ldr(r, MemOperand(sp, 4, PostIndex), al) instruction (aka pop(r))
+// register r is not encoded.
+extern const Instr kPopRegPattern;
+
+// mov lr, pc
+extern const Instr kMovLrPc;
+// ldr rd, [pc, #offset]
+extern const Instr kLdrPCMask;
+extern const Instr kLdrPCPattern;
+// vldr dd, [pc, #offset]
+extern const Instr kVldrDPCMask;
+extern const Instr kVldrDPCPattern;
+// blxcc rm
+extern const Instr kBlxRegMask;
+
+extern const Instr kBlxRegPattern;
+
+extern const Instr kMovMvnMask;
+extern const Instr kMovMvnPattern;
+extern const Instr kMovMvnFlip;
+extern const Instr kMovLeaveCCMask;
+extern const Instr kMovLeaveCCPattern;
+extern const Instr kMovwMask;
+extern const Instr kMovwPattern;
+extern const Instr kMovwLeaveCCFlip;
+extern const Instr kCmpCmnMask;
+extern const Instr kCmpCmnPattern;
+extern const Instr kCmpCmnFlip;
+extern const Instr kAddSubFlip;
+extern const Instr kAndBicFlip;
+
+// A mask for the Rd register for push, pop, ldr, str instructions.
+extern const Instr kLdrRegFpOffsetPattern;
+
+extern const Instr kStrRegFpOffsetPattern;
+
+extern const Instr kLdrRegFpNegOffsetPattern;
+
+extern const Instr kStrRegFpNegOffsetPattern;
+
+extern const Instr kLdrStrInstrTypeMask;
+extern const Instr kLdrStrInstrArgumentMask;
+extern const Instr kLdrStrOffsetMask;
+
+
+// -----------------------------------------------------------------------------
+// Instruction abstraction.
+
+// The class Instruction enables access to individual fields defined in the ARM
+// architecture instruction set encoding as described in figure A3-1.
+// Note that the Assembler uses typedef int32_t Instr.
+//
+// Example: Test whether the instruction at ptr does set the condition code
+// bits.
+//
+// bool InstructionSetsConditionCodes(byte* ptr) {
+// Instruction* instr = Instruction::At(ptr);
+// int type = instr->TypeValue();
+// return ((type == 0) || (type == 1)) && instr->HasS();
+// }
+//
+class Instruction {
+ public:
+ enum {
+ kInstrSize = 4,
+ kInstrSizeLog2 = 2,
+ kPCReadOffset = 8
+ };
+
+ // Helper macro to define static accessors.
+ // We use the cast to char* trick to bypass the strict anti-aliasing rules.
+ #define DECLARE_STATIC_TYPED_ACCESSOR(return_type, Name) \
+ static inline return_type Name(Instr instr) { \
+ char* temp = reinterpret_cast<char*>(&instr); \
+ return reinterpret_cast<Instruction*>(temp)->Name(); \
+ }
+
+ #define DECLARE_STATIC_ACCESSOR(Name) DECLARE_STATIC_TYPED_ACCESSOR(int, Name)
+
+ // Get the raw instruction bits.
+ inline Instr InstructionBits() const {
+ return *reinterpret_cast<const Instr*>(this);
+ }
+
+ // Set the raw instruction bits to value.
+ inline void SetInstructionBits(Instr value) {
+ *reinterpret_cast<Instr*>(this) = value;
+ }
+
+ // Read one particular bit out of the instruction bits.
+ inline int Bit(int nr) const {
+ return (InstructionBits() >> nr) & 1;
+ }
+
+ // Read a bit field's value out of the instruction bits.
+ inline int Bits(int hi, int lo) const {
+ return (InstructionBits() >> lo) & ((2 << (hi - lo)) - 1);
+ }
+
+ // Read a bit field out of the instruction bits.
+ inline int BitField(int hi, int lo) const {
+ return InstructionBits() & (((2 << (hi - lo)) - 1) << lo);
+ }
+
+ // Static support.
+
+ // Read one particular bit out of the instruction bits.
+ static inline int Bit(Instr instr, int nr) {
+ return (instr >> nr) & 1;
+ }
+
+ // Read the value of a bit field out of the instruction bits.
+ static inline int Bits(Instr instr, int hi, int lo) {
+ return (instr >> lo) & ((2 << (hi - lo)) - 1);
+ }
+
+
+ // Read a bit field out of the instruction bits.
+ static inline int BitField(Instr instr, int hi, int lo) {
+ return instr & (((2 << (hi - lo)) - 1) << lo);
+ }
+
+
+ // Accessors for the different named fields used in the ARM encoding.
+ // The naming of these accessor corresponds to figure A3-1.
+ //
+ // Two kind of accessors are declared:
+ // - <Name>Field() will return the raw field, i.e. the field's bits at their
+ // original place in the instruction encoding.
+ // 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.
+ // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as
+ // 0xC0810002 ConditionField(instr) will return 0xC.
+
+
+ // Generally applicable fields
+ inline Condition ConditionValue() const {
+ return static_cast<Condition>(Bits(31, 28));
+ }
+ inline Condition ConditionField() const {
+ return static_cast<Condition>(BitField(31, 28));
+ }
+ DECLARE_STATIC_TYPED_ACCESSOR(Condition, ConditionValue);
+ DECLARE_STATIC_TYPED_ACCESSOR(Condition, ConditionField);
+
+ inline int TypeValue() const { return Bits(27, 25); }
+ inline int SpecialValue() const { return Bits(27, 23); }
+
+ inline int RnValue() const { return Bits(19, 16); }
+ DECLARE_STATIC_ACCESSOR(RnValue);
+ inline int RdValue() const { return Bits(15, 12); }
+ DECLARE_STATIC_ACCESSOR(RdValue);
+
+ inline int CoprocessorValue() const { return Bits(11, 8); }
+ // Support for VFP.
+ // Vn(19-16) | Vd(15-12) | Vm(3-0)
+ inline int VnValue() const { return Bits(19, 16); }
+ inline int VmValue() const { return Bits(3, 0); }
+ inline int VdValue() const { return Bits(15, 12); }
+ inline int NValue() const { return Bit(7); }
+ inline int MValue() const { return Bit(5); }
+ inline int DValue() const { return Bit(22); }
+ inline int RtValue() const { return Bits(15, 12); }
+ inline int PValue() const { return Bit(24); }
+ inline int UValue() const { return Bit(23); }
+ inline int Opc1Value() const { return (Bit(23) << 2) | Bits(21, 20); }
+ inline int Opc2Value() const { return Bits(19, 16); }
+ inline int Opc3Value() const { return Bits(7, 6); }
+ inline int SzValue() const { return Bit(8); }
+ inline int VLValue() const { return Bit(20); }
+ inline int VCValue() const { return Bit(8); }
+ inline int VAValue() const { return Bits(23, 21); }
+ inline int VBValue() const { return Bits(6, 5); }
+ inline int VFPNRegValue(VFPRegPrecision pre) {
+ return VFPGlueRegValue(pre, 16, 7);
+ }
+ inline int VFPMRegValue(VFPRegPrecision pre) {
+ return VFPGlueRegValue(pre, 0, 5);
+ }
+ inline int VFPDRegValue(VFPRegPrecision pre) {
+ return VFPGlueRegValue(pre, 12, 22);
+ }
+
+ // Fields used in Data processing instructions
+ inline int OpcodeValue() const {
+ return static_cast<Opcode>(Bits(24, 21));
+ }
+ inline Opcode OpcodeField() const {
+ return static_cast<Opcode>(BitField(24, 21));
+ }
+ inline int SValue() const { return Bit(20); }
+ // with register
+ inline int RmValue() const { return Bits(3, 0); }
+ DECLARE_STATIC_ACCESSOR(RmValue);
+ inline int ShiftValue() const { return static_cast<ShiftOp>(Bits(6, 5)); }
+ inline ShiftOp ShiftField() const {
+ return static_cast<ShiftOp>(BitField(6, 5));
+ }
+ inline int RegShiftValue() const { return Bit(4); }
+ inline int RsValue() const { return Bits(11, 8); }
+ inline int ShiftAmountValue() const { return Bits(11, 7); }
+ // with immediate
+ inline int RotateValue() const { return Bits(11, 8); }
+ inline int Immed8Value() const { return Bits(7, 0); }
+ inline int Immed4Value() const { return Bits(19, 16); }
+ inline int ImmedMovwMovtValue() const {
+ return Immed4Value() << 12 | Offset12Value(); }
+
+ // Fields used in Load/Store instructions
+ inline int PUValue() const { return Bits(24, 23); }
+ inline int PUField() const { return BitField(24, 23); }
+ inline int BValue() const { return Bit(22); }
+ inline int WValue() const { return Bit(21); }
+ inline int LValue() const { return Bit(20); }
+ // with register uses same fields as Data processing instructions above
+ // with immediate
+ inline int Offset12Value() const { return Bits(11, 0); }
+ // multiple
+ inline int RlistValue() const { return Bits(15, 0); }
+ // extra loads and stores
+ inline int SignValue() const { return Bit(6); }
+ inline int HValue() const { return Bit(5); }
+ inline int ImmedHValue() const { return Bits(11, 8); }
+ inline int ImmedLValue() const { return Bits(3, 0); }
+
+ // Fields used in Branch instructions
+ inline int LinkValue() const { return Bit(24); }
+ inline int SImmed24Value() const { return ((InstructionBits() << 8) >> 8); }
+
+ // Fields used in Software interrupt instructions
+ inline SoftwareInterruptCodes SvcValue() const {
+ return static_cast<SoftwareInterruptCodes>(Bits(23, 0));
+ }
+
+ // Test for special encodings of type 0 instructions (extra loads and stores,
+ // as well as multiplications).
+ inline bool IsSpecialType0() const { return (Bit(7) == 1) && (Bit(4) == 1); }
+
+ // Test for miscellaneous instructions encodings of type 0 instructions.
+ inline bool IsMiscType0() const { return (Bit(24) == 1)
+ && (Bit(23) == 0)
+ && (Bit(20) == 0)
+ && ((Bit(7) == 0)); }
+
+ // Test for a nop instruction, which falls under type 1.
+ inline bool IsNopType1() const { return Bits(24, 0) == 0x0120F000; }
+
+ // Test for a stop instruction.
+ inline bool IsStop() const {
+ return (TypeValue() == 7) && (Bit(24) == 1) && (SvcValue() >= kStopCode);
+ }
+
+ // Special accessors that test for existence of a value.
+ inline bool HasS() const { return SValue() == 1; }
+ inline bool HasB() const { return BValue() == 1; }
+ inline bool HasW() const { return WValue() == 1; }
+ inline bool HasL() const { return LValue() == 1; }
+ inline bool HasU() const { return UValue() == 1; }
+ inline bool HasSign() const { return SignValue() == 1; }
+ inline bool HasH() const { return HValue() == 1; }
+ inline bool HasLink() const { return LinkValue() == 1; }
+
+ // Decoding the double immediate in the vmov instruction.
+ double DoubleImmedVmov() const;
+
+ // Instructions are read of out a code stream. The only way to get a
+ // reference to an instruction is to convert a pointer. There is no way
+ // to allocate or create instances of class Instruction.
+ // Use the At(pc) function to create references to Instruction.
+ static Instruction* At(byte* pc) {
+ return reinterpret_cast<Instruction*>(pc);
+ }
+
+
+ private:
+ // Join split register codes, depending on single or double precision.
+ // four_bit is the position of the least-significant bit of the four
+ // bit specifier. one_bit is the position of the additional single bit
+ // specifier.
+ inline int VFPGlueRegValue(VFPRegPrecision pre, int four_bit, int one_bit) {
+ if (pre == kSinglePrecision) {
+ return (Bits(four_bit + 3, four_bit) << 1) | Bit(one_bit);
+ }
+ return (Bit(one_bit) << 4) | Bits(four_bit + 3, four_bit);
+ }
+
+ // We need to prevent the creation of instances of class Instruction.
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction);
+};
+
+
+// Helper functions for converting between register numbers and names.
+class Registers {
+ public:
+ // Return the name of the register.
+ static const char* Name(int reg);
+
+ // Lookup the register number for the name provided.
+ static int Number(const char* name);
+
+ struct RegisterAlias {
+ int reg;
+ const char* name;
+ };
+
+ private:
+ static const char* names_[kNumRegisters];
+ static const RegisterAlias aliases_[];
+};
+
+// Helper functions for converting between VFP register numbers and names.
+class VFPRegisters {
+ public:
+ // Return the name of the register.
+ static const char* Name(int reg, bool is_double);
+
+ // Lookup the register number for the name provided.
+ // Set flag pointed by is_double to true if register
+ // is double-precision.
+ static int Number(const char* name, bool* is_double);
+
+ private:
+ static const char* names_[kNumVFPRegisters];
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_CONSTANTS_ARM_H_
diff --git a/chromium/v8/src/arm/cpu-arm.cc b/chromium/v8/src/arm/cpu-arm.cc
new file mode 100644
index 00000000000..8766a24bb2a
--- /dev/null
+++ b/chromium/v8/src/arm/cpu-arm.cc
@@ -0,0 +1,120 @@
+// Copyright 2006-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.
+
+// CPU specific code for arm independent of OS goes here.
+#ifdef __arm__
+#include <sys/syscall.h> // for cache flushing.
+#endif
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "cpu.h"
+#include "macro-assembler.h"
+#include "simulator.h" // for cache flushing.
+
+namespace v8 {
+namespace internal {
+
+void CPU::SetUp() {
+ CpuFeatures::Probe();
+}
+
+
+bool CPU::SupportsCrankshaft() {
+ return CpuFeatures::IsSupported(VFP3);
+}
+
+
+void CPU::FlushICache(void* start, size_t size) {
+ // Nothing to do flushing no instructions.
+ if (size == 0) {
+ return;
+ }
+
+#if defined (USE_SIMULATOR)
+ // Not generating ARM instructions for C-code. This means that we are
+ // building an ARM emulator based target. We should notify the simulator
+ // that the Icache was flushed.
+ // None of this code ends up in the snapshot so there are no issues
+ // around whether or not to generate the code when building snapshots.
+ Simulator::FlushICache(Isolate::Current()->simulator_i_cache(), start, size);
+#else
+ // Ideally, we would call
+ // syscall(__ARM_NR_cacheflush, start,
+ // reinterpret_cast<intptr_t>(start) + size, 0);
+ // however, syscall(int, ...) is not supported on all platforms, especially
+ // not when using EABI, so we call the __ARM_NR_cacheflush syscall directly.
+
+ register uint32_t beg asm("a1") = reinterpret_cast<uint32_t>(start);
+ register uint32_t end asm("a2") =
+ reinterpret_cast<uint32_t>(start) + size;
+ register uint32_t flg asm("a3") = 0;
+ #if defined (__arm__) && !defined(__thumb__)
+ // __arm__ may be defined in thumb mode.
+ register uint32_t scno asm("r7") = __ARM_NR_cacheflush;
+ asm volatile(
+ "svc 0x0"
+ : "=r" (beg)
+ : "0" (beg), "r" (end), "r" (flg), "r" (scno));
+ #else
+ // r7 is reserved by the EABI in thumb mode.
+ asm volatile(
+ "@ Enter ARM Mode \n\t"
+ "adr r3, 1f \n\t"
+ "bx r3 \n\t"
+ ".ALIGN 4 \n\t"
+ ".ARM \n"
+ "1: push {r7} \n\t"
+ "mov r7, %4 \n\t"
+ "svc 0x0 \n\t"
+ "pop {r7} \n\t"
+ "@ Enter THUMB Mode\n\t"
+ "adr r3, 2f+1 \n\t"
+ "bx r3 \n\t"
+ ".THUMB \n"
+ "2: \n\t"
+ : "=r" (beg)
+ : "0" (beg), "r" (end), "r" (flg), "r" (__ARM_NR_cacheflush)
+ : "r3");
+ #endif
+#endif
+}
+
+
+void CPU::DebugBreak() {
+#if !defined (__arm__)
+ UNIMPLEMENTED(); // when building ARM emulator target
+#else
+ asm volatile("bkpt 0");
+#endif
+}
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/debug-arm.cc b/chromium/v8/src/arm/debug-arm.cc
new file mode 100644
index 00000000000..108435f0a9f
--- /dev/null
+++ b/chromium/v8/src/arm/debug-arm.cc
@@ -0,0 +1,334 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "codegen.h"
+#include "debug.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+bool BreakLocationIterator::IsDebugBreakAtReturn() {
+ return Debug::IsDebugBreakAtReturn(rinfo());
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtReturn() {
+ // Patch the code changing the return from JS function sequence from
+ // mov sp, fp
+ // ldmia sp!, {fp, lr}
+ // add sp, sp, #4
+ // bx lr
+ // to a call to the debug break return code.
+ // ldr ip, [pc, #0]
+ // blx ip
+ // <debug break return code entry point address>
+ // bktp 0
+ CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
+ patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
+ patcher.masm()->blx(v8::internal::ip);
+ patcher.Emit(Isolate::Current()->debug()->debug_break_return()->entry());
+ patcher.masm()->bkpt(0);
+}
+
+
+// Restore the JS frame exit code.
+void BreakLocationIterator::ClearDebugBreakAtReturn() {
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kJSReturnSequenceInstructions);
+}
+
+
+// A debug break in the frame exit code is identified by the JS frame exit code
+// having been patched with a call instruction.
+bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
+ return rinfo->IsPatchedReturnSequence();
+}
+
+
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return rinfo()->IsPatchedDebugBreakSlotSequence();
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Patch the code changing the debug break slot code from
+ // mov r2, r2
+ // mov r2, r2
+ // mov r2, r2
+ // to a call to the debug break slot code.
+ // ldr ip, [pc, #0]
+ // blx ip
+ // <debug break slot code entry point address>
+ CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
+ patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
+ patcher.masm()->blx(v8::internal::ip);
+ patcher.Emit(Isolate::Current()->debug()->debug_break_slot()->entry());
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kDebugBreakSlotInstructions);
+}
+
+const bool Debug::FramePaddingLayout::kIsSupported = false;
+
+
+#define __ ACCESS_MASM(masm)
+
+
+static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
+ RegList object_regs,
+ RegList non_object_regs) {
+ {
+ 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, kUnableToEncodeValueAsSmi);
+ }
+ __ SmiTag(reg);
+ }
+ }
+ __ stm(db_w, sp, object_regs | non_object_regs);
+ }
+
+#ifdef DEBUG
+ __ RecordComment("// Calling from debug break to runtime - come in - over");
+#endif
+ __ mov(r0, Operand::Zero()); // 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) {
+ __ SmiUntag(reg);
+ }
+ if (FLAG_debug_code &&
+ (((object_regs |non_object_regs) & (1 << r)) == 0)) {
+ __ mov(reg, Operand(kDebugZapValue));
+ }
+ }
+ }
+
+ // 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
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate());
+ __ mov(ip, Operand(after_break_target));
+ __ ldr(ip, MemOperand(ip));
+ __ Jump(ip);
+}
+
+
+void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
+ // Calling convention for IC load (from ic-arm.cc).
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- r0 : receiver
+ // -- [sp] : receiver
+ // -----------------------------------
+ // Registers r0 and r2 contain objects that need to be pushed on the
+ // expression stack of the fake JS frame.
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit(), 0);
+}
+
+
+void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
+ // Calling convention for IC store (from ic-arm.cc).
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : receiver
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ // Registers r0, r1, and r2 contain objects that need to be pushed on the
+ // expression stack of the fake JS frame.
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit(), 0);
+}
+
+
+void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit(), 0);
+}
+
+
+void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit(), 0);
+}
+
+
+void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
+ // Register state for CompareNil IC
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, r0.bit(), 0);
+}
+
+
+void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
+ // Calling convention for IC call (from ic-arm.cc)
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, r2.bit(), 0);
+}
+
+
+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
+ // care.
+ Generate_DebugBreakCallHelper(masm, r0.bit(), 0);
+}
+
+
+void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
+ // Register state for CallFunctionStub (from code-stubs-arm.cc).
+ // ----------- S t a t e -------------
+ // -- r1 : function
+ // -----------------------------------
+ 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());
+}
+
+
+void Debug::GenerateSlot(MacroAssembler* masm) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the constant pool in the debug break slot code.
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ RecordDebugBreakSlot();
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ ASSERT_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_codesize));
+}
+
+
+void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
+ // In the places where a debug break slot is inserted no registers can contain
+ // object pointers.
+ Generate_DebugBreakCallHelper(masm, 0, 0);
+}
+
+
+void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnArm);
+}
+
+
+void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnArm);
+}
+
+const bool Debug::kFrameDropperSupported = false;
+
+#undef __
+
+
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/deoptimizer-arm.cc b/chromium/v8/src/arm/deoptimizer-arm.cc
new file mode 100644
index 00000000000..5b42116ad4b
--- /dev/null
+++ b/chromium/v8/src/arm/deoptimizer-arm.cc
@@ -0,0 +1,618 @@
+// Copyright 2012 the V8 project authors. 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 "codegen.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+#include "safepoint-table.h"
+
+namespace v8 {
+namespace internal {
+
+const int Deoptimizer::table_entry_size_ = 12;
+
+
+int Deoptimizer::patch_size() {
+ const int kCallInstructionSizeInWords = 3;
+ return kCallInstructionSizeInWords * Assembler::kInstrSize;
+}
+
+
+void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* 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(isolate, i, LAZY);
+ // We need calls to have a predictable size in the unoptimized code, but
+ // this is optimized code, so we don't have to have a predictable size.
+ int call_size_in_bytes =
+ MacroAssembler::CallSizeNotPredictableCodeSize(deopt_entry,
+ RelocInfo::NONE32);
+ 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::NONE32);
+ 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
+ }
+}
+
+
+static const int32_t kBranchBeforeInterrupt = 0x5a000004;
+
+// The back edge bookkeeping code matches the pattern:
+//
+// <decrement profiling counter>
+// 2a 00 00 01 bpl ok
+// e5 9f c? ?? ldr ip, [pc, <interrupt stub address>]
+// e1 2f ff 3c blx ip
+// ok-label
+//
+// We patch the code to the following form:
+//
+// <decrement profiling counter>
+// e1 a0 00 00 mov r0, r0 (NOP)
+// e5 9f c? ?? ldr ip, [pc, <on-stack replacement address>]
+// e1 2f ff 3c blx ip
+// ok-label
+
+void Deoptimizer::PatchInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ ASSERT(!InterruptCodeIsPatched(unoptimized_code,
+ pc_after,
+ interrupt_code,
+ replacement_code));
+ static const int kInstrSize = Assembler::kInstrSize;
+ // Turn the jump into nops.
+ CodePatcher patcher(pc_after - 3 * kInstrSize, 1);
+ patcher.masm()->nop();
+ // Replace the call address.
+ uint32_t interrupt_address_offset = Memory::uint16_at(pc_after -
+ 2 * kInstrSize) & 0xfff;
+ Address interrupt_address_pointer = pc_after + interrupt_address_offset;
+ Memory::uint32_at(interrupt_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::RevertInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ ASSERT(InterruptCodeIsPatched(unoptimized_code,
+ pc_after,
+ interrupt_code,
+ replacement_code));
+ static const int kInstrSize = Assembler::kInstrSize;
+ // Restore the original jump.
+ CodePatcher patcher(pc_after - 3 * kInstrSize, 1);
+ patcher.masm()->b(4 * kInstrSize, pl); // ok-label is 4 instructions later.
+ ASSERT_EQ(kBranchBeforeInterrupt,
+ Memory::int32_at(pc_after - 3 * kInstrSize));
+ // Restore the original call address.
+ uint32_t interrupt_address_offset = Memory::uint16_at(pc_after -
+ 2 * kInstrSize) & 0xfff;
+ Address interrupt_address_pointer = pc_after + interrupt_address_offset;
+ Memory::uint32_at(interrupt_address_pointer) =
+ reinterpret_cast<uint32_t>(interrupt_code->entry());
+
+ interrupt_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
+ unoptimized_code, pc_after - 2 * kInstrSize, interrupt_code);
+}
+
+
+#ifdef DEBUG
+bool Deoptimizer::InterruptCodeIsPatched(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ static const int kInstrSize = Assembler::kInstrSize;
+ ASSERT(Memory::int32_at(pc_after - kInstrSize) == kBlxIp);
+
+ uint32_t interrupt_address_offset =
+ Memory::uint16_at(pc_after - 2 * kInstrSize) & 0xfff;
+ Address interrupt_address_pointer = pc_after + interrupt_address_offset;
+
+ if (Assembler::IsNop(Assembler::instr_at(pc_after - 3 * kInstrSize))) {
+ ASSERT(Assembler::IsLdrPcImmediateOffset(
+ Assembler::instr_at(pc_after - 2 * kInstrSize)));
+ ASSERT(reinterpret_cast<uint32_t>(replacement_code->entry()) ==
+ Memory::uint32_at(interrupt_address_pointer));
+ return true;
+ } else {
+ ASSERT(Assembler::IsLdrPcImmediateOffset(
+ Assembler::instr_at(pc_after - 2 * kInstrSize)));
+ ASSERT_EQ(kBranchBeforeInterrupt,
+ Memory::int32_at(pc_after - 3 * kInstrSize));
+ ASSERT(reinterpret_cast<uint32_t>(interrupt_code->entry()) ==
+ Memory::uint32_at(interrupt_address_pointer));
+ return false;
+ }
+}
+#endif // DEBUG
+
+
+static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) {
+ ByteArray* translations = data->TranslationByteArray();
+ int length = data->DeoptCount();
+ for (int i = 0; i < length; i++) {
+ if (data->AstId(i) == 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() {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ compiled_code_->deoptimization_data());
+ unsigned ast_id = data->OsrAstId()->value();
+
+ int bailout_id = LookupBailoutId(data, BailoutId(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);
+ int closure_id = iterator.Next();
+ USE(closure_id);
+ ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
+ 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 = compiled_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_));
+ PrintFunctionName();
+ 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>(
+ compiled_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_));
+ PrintFunctionName();
+ PrintF(" => pc=0x%0x]\n", output_[0]->GetPc());
+ }
+}
+
+
+void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
+ // 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::NumAllocatableRegisters(); 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));
+ }
+}
+
+
+void Deoptimizer::SetPlatformCompiledStubRegisters(
+ FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
+ ApiFunction function(descriptor->deoptimization_handler_);
+ ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
+ intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
+ int params = descriptor->register_param_count_;
+ if (descriptor->stack_parameter_count_ != NULL) {
+ params++;
+ }
+ output_frame->SetRegister(r0.code(), params);
+ output_frame->SetRegister(r1.code(), handler);
+}
+
+
+void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
+ for (int i = 0; i < DwVfpRegister::kMaxNumRegisters; ++i) {
+ double double_value = input_->GetDoubleRegister(i);
+ output_frame->SetDoubleRegister(i, double_value);
+ }
+}
+
+
+bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
+ // There is no dynamic alignment padding on ARM in the input frame.
+ return false;
+}
+
+
+#define __ masm()->
+
+// This code tries to be close to ia32 code so that any changes can be
+// easily ported.
+void Deoptimizer::EntryGenerator::Generate() {
+ GeneratePrologue();
+
+ // Save all general purpose registers before messing with them.
+ const int kNumberOfRegisters = Register::kNumRegisters;
+
+ // Everything but pc, lr and ip which will be saved but not restored.
+ RegList restored_regs = kJSCallerSaved | kCalleeSaved | ip.bit();
+
+ const int kDoubleRegsSize =
+ kDoubleSize * DwVfpRegister::kMaxNumAllocatableRegisters;
+
+ // Save all allocatable VFP registers before messing with them.
+ ASSERT(kDoubleRegZero.code() == 14);
+ ASSERT(kScratchDoubleReg.code() == 15);
+
+ // Check CPU flags for number of registers, setting the Z condition flag.
+ __ CheckFor32DRegs(ip);
+
+ // Push registers d0-d13, and possibly d16-d31, on the stack.
+ // If d16-d31 are not pushed, decrease the stack pointer instead.
+ __ vstm(db_w, sp, d16, d31, ne);
+ __ sub(sp, sp, Operand(16 * kDoubleSize), LeaveCC, eq);
+ __ vstm(db_w, sp, d0, d13);
+
+ // Push all 16 registers (needed to populate FrameDescription::registers_).
+ // TODO(1588) Note that using pc with stm is deprecated, so we should perhaps
+ // handle this a bit differently.
+ __ stm(db_w, sp, restored_regs | sp.bit() | lr.bit() | pc.bit());
+
+ const int kSavedRegistersAreaSize =
+ (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
+
+ // Get the bailout id from the stack.
+ __ ldr(r2, MemOperand(sp, kSavedRegistersAreaSize));
+
+ // Get the address of the location in the code object (r3) (return
+ // address for lazy deoptimization) and compute the fp-to-sp delta in
+ // register r4.
+ __ mov(r3, lr);
+ // Correct one word for bailout id.
+ __ add(r4, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
+ __ sub(r4, fp, r4);
+
+ // Allocate a new deoptimizer object.
+ // Pass four arguments in r0 to r3 and fifth argument on stack.
+ __ PrepareCallCFunction(6, r5);
+ __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ mov(r1, Operand(type())); // bailout type,
+ // r2: bailout id already loaded.
+ // r3: code address or 0 already loaded.
+ __ str(r4, MemOperand(sp, 0 * kPointerSize)); // Fp-to-sp delta.
+ __ mov(r5, Operand(ExternalReference::isolate_address(isolate())));
+ __ str(r5, MemOperand(sp, 1 * kPointerSize)); // Isolate.
+ // Call Deoptimizer::New().
+ {
+ 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_);
+ __ ldr(r1, MemOperand(r0, 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();
+ __ ldr(r2, MemOperand(sp, i * kPointerSize));
+ __ str(r2, MemOperand(r1, offset));
+ }
+
+ // Copy VFP registers to
+ // double_registers_[DoubleRegister::kMaxNumAllocatableRegisters]
+ int double_regs_offset = FrameDescription::double_registers_offset();
+ for (int i = 0; i < DwVfpRegister::kMaxNumAllocatableRegisters; ++i) {
+ int dst_offset = i * kDoubleSize + double_regs_offset;
+ int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
+ __ vldr(d0, sp, src_offset);
+ __ vstr(d0, r1, dst_offset);
+ }
+
+ // Remove the bailout id and the saved registers from the stack.
+ __ add(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
+
+ // Compute a pointer to the unwinding limit in register r2; that is
+ // the first stack slot not part of the input frame.
+ __ ldr(r2, MemOperand(r1, FrameDescription::frame_size_offset()));
+ __ add(r2, r2, 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.
+ __ add(r3, r1, Operand(FrameDescription::frame_content_offset()));
+ Label pop_loop;
+ Label pop_loop_header;
+ __ b(&pop_loop_header);
+ __ bind(&pop_loop);
+ __ pop(r4);
+ __ str(r4, MemOperand(r3, 0));
+ __ add(r3, r3, Operand(sizeof(uint32_t)));
+ __ bind(&pop_loop_header);
+ __ cmp(r2, sp);
+ __ b(ne, &pop_loop);
+
+ // Compute the output frame in the deoptimizer.
+ __ push(r0); // Preserve deoptimizer object across call.
+ // r0: deoptimizer object; r1: scratch.
+ __ PrepareCallCFunction(1, r1);
+ // Call Deoptimizer::ComputeOutputFrames().
+ {
+ 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.
+ Label outer_push_loop, inner_push_loop,
+ outer_loop_header, inner_loop_header;
+ // Outer loop state: r4 = current "FrameDescription** output_",
+ // r1 = one past the last FrameDescription**.
+ __ ldr(r1, MemOperand(r0, Deoptimizer::output_count_offset()));
+ __ ldr(r4, MemOperand(r0, Deoptimizer::output_offset())); // r4 is output_.
+ __ add(r1, r4, Operand(r1, LSL, 2));
+ __ jmp(&outer_loop_header);
+ __ bind(&outer_push_loop);
+ // Inner loop state: r2 = current FrameDescription*, r3 = loop index.
+ __ ldr(r2, MemOperand(r4, 0)); // output_[ix]
+ __ ldr(r3, MemOperand(r2, FrameDescription::frame_size_offset()));
+ __ jmp(&inner_loop_header);
+ __ bind(&inner_push_loop);
+ __ sub(r3, r3, Operand(sizeof(uint32_t)));
+ __ add(r6, r2, Operand(r3));
+ __ ldr(r7, MemOperand(r6, FrameDescription::frame_content_offset()));
+ __ push(r7);
+ __ bind(&inner_loop_header);
+ __ cmp(r3, Operand::Zero());
+ __ b(ne, &inner_push_loop); // test for gt?
+ __ add(r4, r4, Operand(kPointerSize));
+ __ bind(&outer_loop_header);
+ __ cmp(r4, r1);
+ __ b(lt, &outer_push_loop);
+
+ // Check CPU flags for number of registers, setting the Z condition flag.
+ __ CheckFor32DRegs(ip);
+
+ __ ldr(r1, MemOperand(r0, Deoptimizer::input_offset()));
+ int src_offset = FrameDescription::double_registers_offset();
+ for (int i = 0; i < DwVfpRegister::kMaxNumRegisters; ++i) {
+ if (i == kDoubleRegZero.code()) continue;
+ if (i == kScratchDoubleReg.code()) continue;
+
+ const DwVfpRegister reg = DwVfpRegister::from_code(i);
+ __ vldr(reg, r1, src_offset, i < 16 ? al : ne);
+ src_offset += kDoubleSize;
+ }
+
+ // Push state, pc, and continuation from the last output frame.
+ if (type() != OSR) {
+ __ ldr(r6, MemOperand(r2, FrameDescription::state_offset()));
+ __ push(r6);
+ }
+
+ __ ldr(r6, MemOperand(r2, FrameDescription::pc_offset()));
+ __ push(r6);
+ __ ldr(r6, MemOperand(r2, FrameDescription::continuation_offset()));
+ __ push(r6);
+
+ // Push the registers from the last output frame.
+ for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
+ int offset = (i * kPointerSize) + FrameDescription::registers_offset();
+ __ ldr(r6, MemOperand(r2, offset));
+ __ push(r6);
+ }
+
+ // Restore the registers from the stack.
+ __ ldm(ia_w, sp, restored_regs); // all but pc registers.
+ __ pop(ip); // remove sp
+ __ pop(ip); // remove lr
+
+ __ InitializeRootRegister();
+
+ __ pop(ip); // remove pc
+ __ pop(r7); // get continuation, leave pc on stack
+ __ pop(lr);
+ __ Jump(r7);
+ __ stop("Unreachable.");
+}
+
+
+void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
+ // Create a sequence of deoptimization entries.
+ // Note that registers are still live when jumping to an entry.
+ Label done;
+ for (int i = 0; i < count(); i++) {
+ int start = masm()->pc_offset();
+ USE(start);
+ __ mov(ip, Operand(i));
+ __ push(ip);
+ __ b(&done);
+ ASSERT(masm()->pc_offset() - start == table_entry_size_);
+ }
+ __ bind(&done);
+}
+
+
+void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
+ SetFrameSlot(offset, value);
+}
+
+
+void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
+ SetFrameSlot(offset, value);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/arm/disasm-arm.cc b/chromium/v8/src/arm/disasm-arm.cc
new file mode 100644
index 00000000000..ecdf638a1da
--- /dev/null
+++ b/chromium/v8/src/arm/disasm-arm.cc
@@ -0,0 +1,1807 @@
+// 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.
+
+// A Disassembler object is used to disassemble a block of code instruction by
+// instruction. The default implementation of the NameConverter object can be
+// overriden to modify register names or to do symbol lookup on addresses.
+//
+// The example below will disassemble a block of code and print it to stdout.
+//
+// NameConverter converter;
+// Disassembler d(converter);
+// for (byte* pc = begin; pc < end;) {
+// v8::internal::EmbeddedVector<char, 256> buffer;
+// byte* prev_pc = pc;
+// pc += d.InstructionDecode(buffer, pc);
+// printf("%p %08x %s\n",
+// prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer);
+// }
+//
+// The Disassembler class also has a convenience method to disassemble a block
+// of code into a FILE*, meaning that the above functionality could also be
+// achieved by just calling Disassembler::Disassemble(stdout, begin, end);
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef WIN32
+#include <stdint.h>
+#endif
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "constants-arm.h"
+#include "disasm.h"
+#include "macro-assembler.h"
+#include "platform.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+//------------------------------------------------------------------------------
+
+// Decoder decodes and disassembles instructions into an output buffer.
+// It uses the converter to convert register names and call destinations into
+// more informative description.
+class Decoder {
+ public:
+ Decoder(const disasm::NameConverter& converter,
+ Vector<char> out_buffer)
+ : converter_(converter),
+ out_buffer_(out_buffer),
+ out_buffer_pos_(0) {
+ out_buffer_[out_buffer_pos_] = '\0';
+ }
+
+ ~Decoder() {}
+
+ // Writes one disassembled instruction into 'buffer' (0-terminated).
+ // Returns the length of the disassembled machine instruction in bytes.
+ int InstructionDecode(byte* instruction);
+
+ static bool IsConstantPoolAt(byte* instr_ptr);
+ static int ConstantPoolSizeAt(byte* instr_ptr);
+
+ private:
+ // Bottleneck functions to print into the out_buffer.
+ void PrintChar(const char ch);
+ void Print(const char* str);
+
+ // Printing of common values.
+ void PrintRegister(int reg);
+ void PrintSRegister(int reg);
+ void PrintDRegister(int reg);
+ int FormatVFPRegister(Instruction* instr, const char* format);
+ void PrintMovwMovt(Instruction* instr);
+ int FormatVFPinstruction(Instruction* instr, const char* format);
+ void PrintCondition(Instruction* instr);
+ void PrintShiftRm(Instruction* instr);
+ void PrintShiftImm(Instruction* instr);
+ void PrintShiftSat(Instruction* instr);
+ void PrintPU(Instruction* instr);
+ void PrintSoftwareInterrupt(SoftwareInterruptCodes svc);
+
+ // Handle formatting of instructions and their options.
+ int FormatRegister(Instruction* instr, const char* option);
+ void FormatNeonList(int Vd, int type);
+ void FormatNeonMemory(int Rn, int align, int Rm);
+ int FormatOption(Instruction* instr, const char* option);
+ void Format(Instruction* instr, const char* format);
+ void Unknown(Instruction* instr);
+
+ // Each of these functions decodes one particular instruction type, a 3-bit
+ // field in the instruction encoding.
+ // Types 0 and 1 are combined as they are largely the same except for the way
+ // they interpret the shifter operand.
+ void DecodeType01(Instruction* instr);
+ void DecodeType2(Instruction* instr);
+ void DecodeType3(Instruction* instr);
+ void DecodeType4(Instruction* instr);
+ void DecodeType5(Instruction* instr);
+ void DecodeType6(Instruction* instr);
+ // Type 7 includes special Debugger instructions.
+ int DecodeType7(Instruction* instr);
+ // For VFP support.
+ void DecodeTypeVFP(Instruction* instr);
+ void DecodeType6CoprocessorIns(Instruction* instr);
+
+ void DecodeSpecialCondition(Instruction* instr);
+
+ void DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(Instruction* instr);
+ void DecodeVCMP(Instruction* instr);
+ void DecodeVCVTBetweenDoubleAndSingle(Instruction* instr);
+ void DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr);
+
+ const disasm::NameConverter& converter_;
+ Vector<char> out_buffer_;
+ int out_buffer_pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(Decoder);
+};
+
+
+// Support for assertions in the Decoder formatting functions.
+#define STRING_STARTS_WITH(string, compare_string) \
+ (strncmp(string, compare_string, strlen(compare_string)) == 0)
+
+
+// Append the ch to the output buffer.
+void Decoder::PrintChar(const char ch) {
+ out_buffer_[out_buffer_pos_++] = ch;
+}
+
+
+// Append the str to the output buffer.
+void Decoder::Print(const char* str) {
+ char cur = *str++;
+ while (cur != '\0' && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
+ PrintChar(cur);
+ cur = *str++;
+ }
+ out_buffer_[out_buffer_pos_] = 0;
+}
+
+
+// These condition names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+static const char* cond_names[kNumberOfConditions] = {
+ "eq", "ne", "cs" , "cc" , "mi" , "pl" , "vs" , "vc" ,
+ "hi", "ls", "ge", "lt", "gt", "le", "", "invalid",
+};
+
+
+// Print the condition guarding the instruction.
+void Decoder::PrintCondition(Instruction* instr) {
+ Print(cond_names[instr->ConditionValue()]);
+}
+
+
+// Print the register name according to the active name converter.
+void Decoder::PrintRegister(int reg) {
+ Print(converter_.NameOfCPURegister(reg));
+}
+
+
+// Print the VFP S register name according to the active name converter.
+void Decoder::PrintSRegister(int reg) {
+ Print(VFPRegisters::Name(reg, false));
+}
+
+
+// Print the VFP D register name according to the active name converter.
+void Decoder::PrintDRegister(int reg) {
+ Print(VFPRegisters::Name(reg, true));
+}
+
+
+// These shift names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+static const char* const shift_names[kNumberOfShifts] = {
+ "lsl", "lsr", "asr", "ror"
+};
+
+
+// Print the register shift operands for the instruction. Generally used for
+// data processing instructions.
+void Decoder::PrintShiftRm(Instruction* instr) {
+ ShiftOp shift = instr->ShiftField();
+ int shift_index = instr->ShiftValue();
+ int shift_amount = instr->ShiftAmountValue();
+ int rm = instr->RmValue();
+
+ PrintRegister(rm);
+
+ if ((instr->RegShiftValue() == 0) && (shift == LSL) && (shift_amount == 0)) {
+ // Special case for using rm only.
+ return;
+ }
+ if (instr->RegShiftValue() == 0) {
+ // by immediate
+ if ((shift == ROR) && (shift_amount == 0)) {
+ Print(", RRX");
+ return;
+ } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) {
+ shift_amount = 32;
+ }
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ ", %s #%d",
+ shift_names[shift_index],
+ shift_amount);
+ } else {
+ // by register
+ int rs = instr->RsValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ ", %s ", shift_names[shift_index]);
+ PrintRegister(rs);
+ }
+}
+
+
+// Print the immediate operand for the instruction. Generally used for data
+// processing instructions.
+void Decoder::PrintShiftImm(Instruction* instr) {
+ int rotate = instr->RotateValue() * 2;
+ int immed8 = instr->Immed8Value();
+ int imm = (immed8 >> rotate) | (immed8 << (32 - rotate));
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "#%d", imm);
+}
+
+
+// Print the optional shift and immediate used by saturating instructions.
+void Decoder::PrintShiftSat(Instruction* instr) {
+ int shift = instr->Bits(11, 7);
+ if (shift > 0) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ ", %s #%d",
+ shift_names[instr->Bit(6) * 2],
+ instr->Bits(11, 7));
+ }
+}
+
+
+// Print PU formatting to reduce complexity of FormatOption.
+void Decoder::PrintPU(Instruction* instr) {
+ switch (instr->PUField()) {
+ case da_x: {
+ Print("da");
+ break;
+ }
+ case ia_x: {
+ Print("ia");
+ break;
+ }
+ case db_x: {
+ Print("db");
+ break;
+ }
+ case ib_x: {
+ Print("ib");
+ break;
+ }
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+// Print SoftwareInterrupt codes. Factoring this out reduces the complexity of
+// the FormatOption method.
+void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes svc) {
+ switch (svc) {
+ case kCallRtRedirected:
+ Print("call rt redirected");
+ return;
+ case kBreakpoint:
+ Print("breakpoint");
+ return;
+ default:
+ if (svc >= kStopCode) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%d - 0x%x",
+ svc & kStopCodeMask,
+ svc & kStopCodeMask);
+ } else {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%d",
+ svc);
+ }
+ return;
+ }
+}
+
+
+// Handle all register based formatting in this function to reduce the
+// complexity of FormatOption.
+int Decoder::FormatRegister(Instruction* instr, const char* format) {
+ ASSERT(format[0] == 'r');
+ if (format[1] == 'n') { // 'rn: Rn register
+ int reg = instr->RnValue();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 'd') { // 'rd: Rd register
+ int reg = instr->RdValue();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 's') { // 'rs: Rs register
+ int reg = instr->RsValue();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 'm') { // 'rm: Rm register
+ int reg = instr->RmValue();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 't') { // 'rt: Rt register
+ int reg = instr->RtValue();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 'l') {
+ // 'rlist: register list for load and store multiple instructions
+ ASSERT(STRING_STARTS_WITH(format, "rlist"));
+ int rlist = instr->RlistValue();
+ int reg = 0;
+ Print("{");
+ // Print register list in ascending order, by scanning the bit mask.
+ while (rlist != 0) {
+ if ((rlist & 1) != 0) {
+ PrintRegister(reg);
+ if ((rlist >> 1) != 0) {
+ Print(", ");
+ }
+ }
+ reg++;
+ rlist >>= 1;
+ }
+ Print("}");
+ return 5;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+// Handle all VFP register based formatting in this function to reduce the
+// complexity of FormatOption.
+int Decoder::FormatVFPRegister(Instruction* instr, const char* format) {
+ ASSERT((format[0] == 'S') || (format[0] == 'D'));
+
+ VFPRegPrecision precision =
+ format[0] == 'D' ? kDoublePrecision : kSinglePrecision;
+
+ int retval = 2;
+ int reg = -1;
+ if (format[1] == 'n') {
+ reg = instr->VFPNRegValue(precision);
+ } else if (format[1] == 'm') {
+ reg = instr->VFPMRegValue(precision);
+ } else if (format[1] == 'd') {
+ if ((instr->TypeValue() == 7) &&
+ (instr->Bit(24) == 0x0) &&
+ (instr->Bits(11, 9) == 0x5) &&
+ (instr->Bit(4) == 0x1)) {
+ // vmov.32 has Vd in a different place.
+ reg = instr->Bits(19, 16) | (instr->Bit(7) << 4);
+ } else {
+ reg = instr->VFPDRegValue(precision);
+ }
+
+ if (format[2] == '+') {
+ int immed8 = instr->Immed8Value();
+ if (format[0] == 'S') reg += immed8 - 1;
+ if (format[0] == 'D') reg += (immed8 / 2 - 1);
+ }
+ if (format[2] == '+') retval = 3;
+ } else {
+ UNREACHABLE();
+ }
+
+ if (precision == kSinglePrecision) {
+ PrintSRegister(reg);
+ } else {
+ PrintDRegister(reg);
+ }
+
+ return retval;
+}
+
+
+int Decoder::FormatVFPinstruction(Instruction* instr, const char* format) {
+ Print(format);
+ return 0;
+}
+
+
+void Decoder::FormatNeonList(int Vd, int type) {
+ if (type == nlt_1) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "{d%d}", Vd);
+ } else if (type == nlt_2) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "{d%d, d%d}", Vd, Vd + 1);
+ } else if (type == nlt_3) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "{d%d, d%d, d%d}", Vd, Vd + 1, Vd + 2);
+ } else if (type == nlt_4) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "{d%d, d%d, d%d, d%d}", Vd, Vd + 1, Vd + 2, Vd + 3);
+ }
+}
+
+
+void Decoder::FormatNeonMemory(int Rn, int align, int Rm) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "[r%d", Rn);
+ if (align != 0) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ ":%d", (1 << align) << 6);
+ }
+ if (Rm == 15) {
+ Print("]");
+ } else if (Rm == 13) {
+ Print("]!");
+ } else {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "], r%d", Rm);
+ }
+}
+
+
+// Print the movw or movt instruction.
+void Decoder::PrintMovwMovt(Instruction* instr) {
+ int imm = instr->ImmedMovwMovtValue();
+ int rd = instr->RdValue();
+ PrintRegister(rd);
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ ", #%d", imm);
+}
+
+
+// FormatOption takes a formatting string and interprets it based on
+// the current instructions. The format string points to the first
+// character of the option string (the option escape has already been
+// consumed by the caller.) FormatOption returns the number of
+// characters that were consumed from the formatting string.
+int Decoder::FormatOption(Instruction* instr, const char* format) {
+ switch (format[0]) {
+ case 'a': { // 'a: accumulate multiplies
+ if (instr->Bit(21) == 0) {
+ Print("ul");
+ } else {
+ Print("la");
+ }
+ return 1;
+ }
+ case 'b': { // 'b: byte loads or stores
+ if (instr->HasB()) {
+ Print("b");
+ }
+ return 1;
+ }
+ case 'c': { // 'cond: conditional execution
+ ASSERT(STRING_STARTS_WITH(format, "cond"));
+ PrintCondition(instr);
+ return 4;
+ }
+ case 'd': { // 'd: vmov double immediate.
+ double d = instr->DoubleImmedVmov();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "#%g", d);
+ return 1;
+ }
+ case 'f': { // 'f: bitfield instructions - v7 and above.
+ uint32_t lsbit = instr->Bits(11, 7);
+ uint32_t width = instr->Bits(20, 16) + 1;
+ if (instr->Bit(21) == 0) {
+ // BFC/BFI:
+ // Bits 20-16 represent most-significant bit. Covert to width.
+ width -= lsbit;
+ ASSERT(width > 0);
+ }
+ ASSERT((width + lsbit) <= 32);
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "#%d, #%d", lsbit, width);
+ return 1;
+ }
+ case 'h': { // 'h: halfword operation for extra loads and stores
+ if (instr->HasH()) {
+ Print("h");
+ } else {
+ Print("b");
+ }
+ return 1;
+ }
+ case 'i': { // 'i: immediate value from adjacent bits.
+ // 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');
+
+ ASSERT((width >= 1) && (width <= 32));
+ ASSERT((lsb >= 0) && (lsb <= 31));
+ ASSERT((width + lsb) <= 32);
+
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%d",
+ instr->Bits(width + lsb - 1, lsb));
+ return 8;
+ }
+ case 'l': { // 'l: branch and link
+ if (instr->HasLink()) {
+ Print("l");
+ }
+ return 1;
+ }
+ case 'm': {
+ if (format[1] == 'w') {
+ // 'mw: movt/movw instructions.
+ PrintMovwMovt(instr);
+ return 2;
+ }
+ if (format[1] == 'e') { // 'memop: load/store instructions.
+ ASSERT(STRING_STARTS_WITH(format, "memop"));
+ if (instr->HasL()) {
+ Print("ldr");
+ } else {
+ if ((instr->Bits(27, 25) == 0) && (instr->Bit(20) == 0) &&
+ (instr->Bits(7, 6) == 3) && (instr->Bit(4) == 1)) {
+ if (instr->Bit(5) == 1) {
+ Print("strd");
+ } else {
+ Print("ldrd");
+ }
+ return 5;
+ }
+ Print("str");
+ }
+ return 5;
+ }
+ // 'msg: for simulator break instructions
+ ASSERT(STRING_STARTS_WITH(format, "msg"));
+ byte* str =
+ reinterpret_cast<byte*>(instr->InstructionBits() & 0x0fffffff);
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%s", converter_.NameInCode(str));
+ return 3;
+ }
+ case 'o': {
+ if ((format[3] == '1') && (format[4] == '2')) {
+ // 'off12: 12-bit offset for load and store instructions
+ ASSERT(STRING_STARTS_WITH(format, "off12"));
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%d", instr->Offset12Value());
+ return 5;
+ } else if (format[3] == '0') {
+ // 'off0to3and8to19 16-bit immediate encoded in bits 19-8 and 3-0.
+ ASSERT(STRING_STARTS_WITH(format, "off0to3and8to19"));
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%d",
+ (instr->Bits(19, 8) << 4) +
+ instr->Bits(3, 0));
+ return 15;
+ }
+ // 'off8: 8-bit offset for extra load and store instructions
+ ASSERT(STRING_STARTS_WITH(format, "off8"));
+ int offs8 = (instr->ImmedHValue() << 4) | instr->ImmedLValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%d", offs8);
+ return 4;
+ }
+ case 'p': { // 'pu: P and U bits for load and store instructions
+ ASSERT(STRING_STARTS_WITH(format, "pu"));
+ PrintPU(instr);
+ return 2;
+ }
+ case 'r': {
+ return FormatRegister(instr, format);
+ }
+ case 's': {
+ if (format[1] == 'h') { // 'shift_op or 'shift_rm or 'shift_sat.
+ if (format[6] == 'o') { // 'shift_op
+ ASSERT(STRING_STARTS_WITH(format, "shift_op"));
+ if (instr->TypeValue() == 0) {
+ PrintShiftRm(instr);
+ } else {
+ ASSERT(instr->TypeValue() == 1);
+ PrintShiftImm(instr);
+ }
+ return 8;
+ } else if (format[6] == 's') { // 'shift_sat.
+ ASSERT(STRING_STARTS_WITH(format, "shift_sat"));
+ PrintShiftSat(instr);
+ return 9;
+ } else { // 'shift_rm
+ ASSERT(STRING_STARTS_WITH(format, "shift_rm"));
+ PrintShiftRm(instr);
+ return 8;
+ }
+ } else if (format[1] == 'v') { // 'svc
+ ASSERT(STRING_STARTS_WITH(format, "svc"));
+ PrintSoftwareInterrupt(instr->SvcValue());
+ return 3;
+ } else if (format[1] == 'i') { // 'sign: signed extra loads and stores
+ ASSERT(STRING_STARTS_WITH(format, "sign"));
+ if (instr->HasSign()) {
+ Print("s");
+ }
+ return 4;
+ }
+ // 's: S field of data processing instructions
+ if (instr->HasS()) {
+ Print("s");
+ }
+ return 1;
+ }
+ case 't': { // 'target: target of branch instructions
+ ASSERT(STRING_STARTS_WITH(format, "target"));
+ int off = (instr->SImmed24Value() << 2) + 8;
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%+d -> %s",
+ off,
+ converter_.NameOfAddress(
+ reinterpret_cast<byte*>(instr) + off));
+ return 6;
+ }
+ case 'u': { // 'u: signed or unsigned multiplies
+ // The manual gets the meaning of bit 22 backwards in the multiply
+ // instruction overview on page A3.16.2. The instructions that
+ // exist in u and s variants are the following:
+ // smull A4.1.87
+ // umull A4.1.129
+ // umlal A4.1.128
+ // smlal A4.1.76
+ // For these 0 means u and 1 means s. As can be seen on their individual
+ // pages. The other 18 mul instructions have the bit set or unset in
+ // arbitrary ways that are unrelated to the signedness of the instruction.
+ // None of these 18 instructions exist in both a 'u' and an 's' variant.
+
+ if (instr->Bit(22) == 0) {
+ Print("u");
+ } else {
+ Print("s");
+ }
+ return 1;
+ }
+ case 'v': {
+ return FormatVFPinstruction(instr, format);
+ }
+ case 'S':
+ case 'D': {
+ return FormatVFPRegister(instr, format);
+ }
+ case 'w': { // 'w: W field of load and store instructions
+ if (instr->HasW()) {
+ Print("!");
+ }
+ return 1;
+ }
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+// Format takes a formatting string for a whole instruction and prints it into
+// the output buffer. All escaped options are handed to FormatOption to be
+// parsed further.
+void Decoder::Format(Instruction* instr, const char* format) {
+ char cur = *format++;
+ while ((cur != 0) && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
+ if (cur == '\'') { // Single quote is used as the formatting escape.
+ format += FormatOption(instr, format);
+ } else {
+ out_buffer_[out_buffer_pos_++] = cur;
+ }
+ cur = *format++;
+ }
+ out_buffer_[out_buffer_pos_] = '\0';
+}
+
+
+// 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) {
+ Format(instr, "unknown");
+}
+
+
+void Decoder::DecodeType01(Instruction* instr) {
+ int type = instr->TypeValue();
+ if ((type == 0) && instr->IsSpecialType0()) {
+ // multiply instruction or extra loads and stores
+ if (instr->Bits(7, 4) == 9) {
+ if (instr->Bit(24) == 0) {
+ // multiply instructions
+ if (instr->Bit(23) == 0) {
+ if (instr->Bit(21) == 0) {
+ // The MUL instruction description (A 4.1.33) refers to Rd as being
+ // the destination for the operation, but it confusingly uses the
+ // Rn field to encode it.
+ Format(instr, "mul'cond's 'rn, 'rm, 'rs");
+ } else {
+ if (instr->Bit(22) == 0) {
+ // The MLA instruction description (A 4.1.28) refers to the order
+ // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the
+ // Rn field to encode the Rd register and the Rd field to encode
+ // the Rn register.
+ Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd");
+ } else {
+ // The MLS instruction description (A 4.1.29) refers to the order
+ // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the
+ // Rn field to encode the Rd register and the Rd field to encode
+ // the Rn register.
+ Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd");
+ }
+ }
+ } else {
+ // The signed/long multiply instructions use the terms RdHi and RdLo
+ // when referring to the target registers. They are mapped to the Rn
+ // and Rd fields as follows:
+ // RdLo == Rd field
+ // RdHi == Rn field
+ // The order of registers is: <RdLo>, <RdHi>, <Rm>, <Rs>
+ Format(instr, "'um'al'cond's 'rd, 'rn, 'rm, 'rs");
+ }
+ } else {
+ Unknown(instr); // not used by V8
+ }
+ } else if ((instr->Bit(20) == 0) && ((instr->Bits(7, 4) & 0xd) == 0xd)) {
+ // ldrd, strd
+ switch (instr->PUField()) {
+ case da_x: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond's 'rd, ['rn], -'rm");
+ } else {
+ Format(instr, "'memop'cond's 'rd, ['rn], #-'off8");
+ }
+ break;
+ }
+ case ia_x: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond's 'rd, ['rn], +'rm");
+ } else {
+ Format(instr, "'memop'cond's 'rd, ['rn], #+'off8");
+ }
+ break;
+ }
+ case db_x: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond's 'rd, ['rn, -'rm]'w");
+ } else {
+ Format(instr, "'memop'cond's 'rd, ['rn, #-'off8]'w");
+ }
+ break;
+ }
+ case ib_x: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond's 'rd, ['rn, +'rm]'w");
+ } else {
+ Format(instr, "'memop'cond's 'rd, ['rn, #+'off8]'w");
+ }
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else {
+ // extra load/store instructions
+ switch (instr->PUField()) {
+ case da_x: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm");
+ } else {
+ Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8");
+ }
+ break;
+ }
+ case ia_x: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm");
+ } else {
+ Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8");
+ }
+ break;
+ }
+ case db_x: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w");
+ } else {
+ Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w");
+ }
+ break;
+ }
+ case ib_x: {
+ if (instr->Bit(22) == 0) {
+ Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w");
+ } else {
+ Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w");
+ }
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+ return;
+ }
+ } else if ((type == 0) && instr->IsMiscType0()) {
+ if (instr->Bits(22, 21) == 1) {
+ switch (instr->BitField(7, 4)) {
+ case BX:
+ Format(instr, "bx'cond 'rm");
+ break;
+ case BLX:
+ Format(instr, "blx'cond 'rm");
+ break;
+ case BKPT:
+ Format(instr, "bkpt 'off0to3and8to19");
+ break;
+ default:
+ Unknown(instr); // not used by V8
+ break;
+ }
+ } else if (instr->Bits(22, 21) == 3) {
+ switch (instr->BitField(7, 4)) {
+ case CLZ:
+ Format(instr, "clz'cond 'rd, 'rm");
+ break;
+ default:
+ Unknown(instr); // not used by V8
+ break;
+ }
+ } else {
+ Unknown(instr); // not used by V8
+ }
+ } else if ((type == 1) && instr->IsNopType1()) {
+ Format(instr, "nop'cond");
+ } else {
+ switch (instr->OpcodeField()) {
+ case AND: {
+ Format(instr, "and'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case EOR: {
+ Format(instr, "eor'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case SUB: {
+ Format(instr, "sub'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case RSB: {
+ Format(instr, "rsb'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case ADD: {
+ Format(instr, "add'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case ADC: {
+ Format(instr, "adc'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case SBC: {
+ Format(instr, "sbc'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case RSC: {
+ Format(instr, "rsc'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case TST: {
+ if (instr->HasS()) {
+ Format(instr, "tst'cond 'rn, 'shift_op");
+ } else {
+ Format(instr, "movw'cond 'mw");
+ }
+ break;
+ }
+ case TEQ: {
+ if (instr->HasS()) {
+ Format(instr, "teq'cond 'rn, 'shift_op");
+ } else {
+ // Other instructions matching this pattern are handled in the
+ // miscellaneous instructions part above.
+ UNREACHABLE();
+ }
+ break;
+ }
+ case CMP: {
+ if (instr->HasS()) {
+ Format(instr, "cmp'cond 'rn, 'shift_op");
+ } else {
+ Format(instr, "movt'cond 'mw");
+ }
+ break;
+ }
+ case CMN: {
+ if (instr->HasS()) {
+ Format(instr, "cmn'cond 'rn, 'shift_op");
+ } else {
+ // Other instructions matching this pattern are handled in the
+ // miscellaneous instructions part above.
+ UNREACHABLE();
+ }
+ break;
+ }
+ case ORR: {
+ Format(instr, "orr'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case MOV: {
+ Format(instr, "mov'cond's 'rd, 'shift_op");
+ break;
+ }
+ case BIC: {
+ Format(instr, "bic'cond's 'rd, 'rn, 'shift_op");
+ break;
+ }
+ case MVN: {
+ Format(instr, "mvn'cond's 'rd, 'shift_op");
+ break;
+ }
+ default: {
+ // The Opcode field is a 4-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+}
+
+
+void Decoder::DecodeType2(Instruction* instr) {
+ switch (instr->PUField()) {
+ case da_x: {
+ if (instr->HasW()) {
+ Unknown(instr); // not used in V8
+ return;
+ }
+ Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12");
+ break;
+ }
+ case ia_x: {
+ if (instr->HasW()) {
+ Unknown(instr); // not used in V8
+ return;
+ }
+ Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12");
+ break;
+ }
+ case db_x: {
+ Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w");
+ break;
+ }
+ case ib_x: {
+ Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w");
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void Decoder::DecodeType3(Instruction* instr) {
+ switch (instr->PUField()) {
+ case da_x: {
+ VERIFY(!instr->HasW());
+ Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
+ break;
+ }
+ case ia_x: {
+ if (instr->Bit(4) == 0) {
+ Format(instr, "'memop'cond'b 'rd, ['rn], +'shift_rm");
+ } else {
+ if (instr->Bit(5) == 0) {
+ switch (instr->Bits(22, 21)) {
+ case 0:
+ if (instr->Bit(20) == 0) {
+ if (instr->Bit(6) == 0) {
+ Format(instr, "pkhbt'cond 'rd, 'rn, 'rm, lsl #'imm05@07");
+ } else {
+ if (instr->Bits(11, 7) == 0) {
+ Format(instr, "pkhtb'cond 'rd, 'rn, 'rm, asr #32");
+ } else {
+ Format(instr, "pkhtb'cond 'rd, 'rn, 'rm, asr #'imm05@07");
+ }
+ }
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ case 1:
+ UNREACHABLE();
+ break;
+ case 2:
+ UNREACHABLE();
+ break;
+ case 3:
+ Format(instr, "usat 'rd, #'imm05@16, 'rm'shift_sat");
+ break;
+ }
+ } else {
+ switch (instr->Bits(22, 21)) {
+ case 0:
+ UNREACHABLE();
+ break;
+ case 1:
+ UNREACHABLE();
+ break;
+ case 2:
+ if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) {
+ if (instr->Bits(19, 16) == 0xF) {
+ switch (instr->Bits(11, 10)) {
+ case 0:
+ Format(instr, "uxtb16'cond 'rd, 'rm, ror #0");
+ break;
+ case 1:
+ Format(instr, "uxtb16'cond 'rd, 'rm, ror #8");
+ break;
+ case 2:
+ Format(instr, "uxtb16'cond 'rd, 'rm, ror #16");
+ break;
+ case 3:
+ Format(instr, "uxtb16'cond 'rd, 'rm, ror #24");
+ break;
+ }
+ } else {
+ UNREACHABLE();
+ }
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ case 3:
+ if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) {
+ if (instr->Bits(19, 16) == 0xF) {
+ switch (instr->Bits(11, 10)) {
+ case 0:
+ Format(instr, "uxtb'cond 'rd, 'rm, ror #0");
+ break;
+ case 1:
+ Format(instr, "uxtb'cond 'rd, 'rm, ror #8");
+ break;
+ case 2:
+ Format(instr, "uxtb'cond 'rd, 'rm, ror #16");
+ break;
+ case 3:
+ Format(instr, "uxtb'cond 'rd, 'rm, ror #24");
+ break;
+ }
+ } else {
+ switch (instr->Bits(11, 10)) {
+ case 0:
+ Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #0");
+ break;
+ case 1:
+ Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #8");
+ break;
+ case 2:
+ Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #16");
+ break;
+ case 3:
+ Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #24");
+ break;
+ }
+ }
+ } else {
+ UNREACHABLE();
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case db_x: {
+ if (FLAG_enable_sudiv) {
+ if (!instr->HasW()) {
+ if (instr->Bits(5, 4) == 0x1) {
+ if ((instr->Bit(22) == 0x0) && (instr->Bit(20) == 0x1)) {
+ // SDIV (in V8 notation matching ARM ISA format) rn = rm/rs
+ Format(instr, "sdiv'cond'b 'rn, 'rm, 'rs");
+ break;
+ }
+ }
+ }
+ }
+ Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w");
+ break;
+ }
+ case ib_x: {
+ if (instr->HasW() && (instr->Bits(6, 4) == 0x5)) {
+ uint32_t widthminus1 = static_cast<uint32_t>(instr->Bits(20, 16));
+ uint32_t lsbit = static_cast<uint32_t>(instr->Bits(11, 7));
+ uint32_t msbit = widthminus1 + lsbit;
+ if (msbit <= 31) {
+ if (instr->Bit(22)) {
+ Format(instr, "ubfx'cond 'rd, 'rm, 'f");
+ } else {
+ Format(instr, "sbfx'cond 'rd, 'rm, 'f");
+ }
+ } else {
+ UNREACHABLE();
+ }
+ } else if (!instr->HasW() && (instr->Bits(6, 4) == 0x1)) {
+ uint32_t lsbit = static_cast<uint32_t>(instr->Bits(11, 7));
+ uint32_t msbit = static_cast<uint32_t>(instr->Bits(20, 16));
+ if (msbit >= lsbit) {
+ if (instr->RmValue() == 15) {
+ Format(instr, "bfc'cond 'rd, 'f");
+ } else {
+ Format(instr, "bfi'cond 'rd, 'rm, 'f");
+ }
+ } else {
+ UNREACHABLE();
+ }
+ } else {
+ Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w");
+ }
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void Decoder::DecodeType4(Instruction* instr) {
+ if (instr->Bit(22) != 0) {
+ // Privileged mode currently not supported.
+ Unknown(instr);
+ } else {
+ if (instr->HasL()) {
+ Format(instr, "ldm'cond'pu 'rn'w, 'rlist");
+ } else {
+ Format(instr, "stm'cond'pu 'rn'w, 'rlist");
+ }
+ }
+}
+
+
+void Decoder::DecodeType5(Instruction* instr) {
+ Format(instr, "b'l'cond 'target");
+}
+
+
+void Decoder::DecodeType6(Instruction* instr) {
+ DecodeType6CoprocessorIns(instr);
+}
+
+
+int Decoder::DecodeType7(Instruction* instr) {
+ if (instr->Bit(24) == 1) {
+ if (instr->SvcValue() >= kStopCode) {
+ Format(instr, "stop'cond 'svc");
+ // Also print the stop message. Its address is encoded
+ // in the following 4 bytes.
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "\n %p %08x stop message: %s",
+ reinterpret_cast<int32_t*>(instr
+ + Instruction::kInstrSize),
+ *reinterpret_cast<char**>(instr
+ + Instruction::kInstrSize),
+ *reinterpret_cast<char**>(instr
+ + Instruction::kInstrSize));
+ // We have decoded 2 * Instruction::kInstrSize bytes.
+ return 2 * Instruction::kInstrSize;
+ } else {
+ Format(instr, "svc'cond 'svc");
+ }
+ } else {
+ DecodeTypeVFP(instr);
+ }
+ return Instruction::kInstrSize;
+}
+
+
+// void Decoder::DecodeTypeVFP(Instruction* instr)
+// vmov: Sn = Rt
+// vmov: Rt = Sn
+// vcvt: Dd = Sm
+// vcvt: Sd = Dm
+// vcvt.f64.s32 Dd, Dd, #<fbits>
+// Dd = vabs(Dm)
+// Dd = vneg(Dm)
+// Dd = vadd(Dn, Dm)
+// Dd = vsub(Dn, Dm)
+// Dd = vmul(Dn, Dm)
+// Dd = vmla(Dn, Dm)
+// Dd = vmls(Dn, Dm)
+// Dd = vdiv(Dn, Dm)
+// vcmp(Dd, Dm)
+// vmrs
+// vmsr
+// Dd = vsqrt(Dm)
+void Decoder::DecodeTypeVFP(Instruction* instr) {
+ VERIFY((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) );
+ VERIFY(instr->Bits(11, 9) == 0x5);
+
+ if (instr->Bit(4) == 0) {
+ if (instr->Opc1Value() == 0x7) {
+ // Other data processing instructions
+ if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x1)) {
+ // vmov register to register.
+ if (instr->SzValue() == 0x1) {
+ Format(instr, "vmov'cond.f64 'Dd, 'Dm");
+ } else {
+ Format(instr, "vmov'cond.f32 'Sd, 'Sm");
+ }
+ } else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) {
+ // vabs
+ Format(instr, "vabs'cond.f64 'Dd, 'Dm");
+ } else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) {
+ // vneg
+ Format(instr, "vneg'cond.f64 'Dd, 'Dm");
+ } else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) {
+ DecodeVCVTBetweenDoubleAndSingle(instr);
+ } else if ((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) {
+ DecodeVCVTBetweenFloatingPointAndInteger(instr);
+ } else if ((instr->Opc2Value() == 0xA) && (instr->Opc3Value() == 0x3) &&
+ (instr->Bit(8) == 1)) {
+ // vcvt.f64.s32 Dd, Dd, #<fbits>
+ int fraction_bits = 32 - ((instr->Bit(5) << 4) | instr->Bits(3, 0));
+ Format(instr, "vcvt'cond.f64.s32 'Dd, 'Dd");
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ ", #%d", fraction_bits);
+ } else if (((instr->Opc2Value() >> 1) == 0x6) &&
+ (instr->Opc3Value() & 0x1)) {
+ DecodeVCVTBetweenFloatingPointAndInteger(instr);
+ } else if (((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) &&
+ (instr->Opc3Value() & 0x1)) {
+ DecodeVCMP(instr);
+ } else if (((instr->Opc2Value() == 0x1)) && (instr->Opc3Value() == 0x3)) {
+ Format(instr, "vsqrt'cond.f64 'Dd, 'Dm");
+ } else if (instr->Opc3Value() == 0x0) {
+ if (instr->SzValue() == 0x1) {
+ Format(instr, "vmov'cond.f64 'Dd, 'd");
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else if (instr->Opc1Value() == 0x3) {
+ if (instr->SzValue() == 0x1) {
+ if (instr->Opc3Value() & 0x1) {
+ Format(instr, "vsub'cond.f64 'Dd, 'Dn, 'Dm");
+ } else {
+ Format(instr, "vadd'cond.f64 'Dd, 'Dn, 'Dm");
+ }
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else if ((instr->Opc1Value() == 0x2) && !(instr->Opc3Value() & 0x1)) {
+ if (instr->SzValue() == 0x1) {
+ Format(instr, "vmul'cond.f64 'Dd, 'Dn, 'Dm");
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else if ((instr->Opc1Value() == 0x0) && !(instr->Opc3Value() & 0x1)) {
+ if (instr->SzValue() == 0x1) {
+ Format(instr, "vmla'cond.f64 'Dd, 'Dn, 'Dm");
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else if ((instr->Opc1Value() == 0x0) && (instr->Opc3Value() & 0x1)) {
+ if (instr->SzValue() == 0x1) {
+ Format(instr, "vmls'cond.f64 'Dd, 'Dn, 'Dm");
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else if ((instr->Opc1Value() == 0x4) && !(instr->Opc3Value() & 0x1)) {
+ if (instr->SzValue() == 0x1) {
+ Format(instr, "vdiv'cond.f64 'Dd, 'Dn, 'Dm");
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else {
+ if ((instr->VCValue() == 0x0) &&
+ (instr->VAValue() == 0x0)) {
+ DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr);
+ } else if ((instr->VLValue() == 0x0) &&
+ (instr->VCValue() == 0x1) &&
+ (instr->Bit(23) == 0x0)) {
+ if (instr->Bit(21) == 0x0) {
+ Format(instr, "vmov'cond.32 'Dd[0], 'rt");
+ } else {
+ Format(instr, "vmov'cond.32 'Dd[1], 'rt");
+ }
+ } else if ((instr->VLValue() == 0x1) &&
+ (instr->VCValue() == 0x1) &&
+ (instr->Bit(23) == 0x0)) {
+ if (instr->Bit(21) == 0x0) {
+ Format(instr, "vmov'cond.32 'rt, 'Dd[0]");
+ } else {
+ Format(instr, "vmov'cond.32 'rt, 'Dd[1]");
+ }
+ } else if ((instr->VCValue() == 0x0) &&
+ (instr->VAValue() == 0x7) &&
+ (instr->Bits(19, 16) == 0x1)) {
+ if (instr->VLValue() == 0) {
+ if (instr->Bits(15, 12) == 0xF) {
+ Format(instr, "vmsr'cond FPSCR, APSR");
+ } else {
+ Format(instr, "vmsr'cond FPSCR, 'rt");
+ }
+ } else {
+ if (instr->Bits(15, 12) == 0xF) {
+ Format(instr, "vmrs'cond APSR, FPSCR");
+ } else {
+ Format(instr, "vmrs'cond 'rt, FPSCR");
+ }
+ }
+ }
+ }
+}
+
+
+void Decoder::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(
+ Instruction* instr) {
+ VERIFY((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) &&
+ (instr->VAValue() == 0x0));
+
+ bool to_arm_register = (instr->VLValue() == 0x1);
+
+ if (to_arm_register) {
+ Format(instr, "vmov'cond 'rt, 'Sn");
+ } else {
+ Format(instr, "vmov'cond 'Sn, 'rt");
+ }
+}
+
+
+void Decoder::DecodeVCMP(Instruction* instr) {
+ VERIFY((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
+ VERIFY(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) &&
+ (instr->Opc3Value() & 0x1));
+
+ // Comparison.
+ bool dp_operation = (instr->SzValue() == 1);
+ bool raise_exception_for_qnan = (instr->Bit(7) == 0x1);
+
+ if (dp_operation && !raise_exception_for_qnan) {
+ if (instr->Opc2Value() == 0x4) {
+ Format(instr, "vcmp'cond.f64 'Dd, 'Dm");
+ } else if (instr->Opc2Value() == 0x5) {
+ Format(instr, "vcmp'cond.f64 'Dd, #0.0");
+ } else {
+ Unknown(instr); // invalid
+ }
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+}
+
+
+void Decoder::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) {
+ VERIFY((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
+ VERIFY((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3));
+
+ bool double_to_single = (instr->SzValue() == 1);
+
+ if (double_to_single) {
+ Format(instr, "vcvt'cond.f32.f64 'Sd, 'Dm");
+ } else {
+ Format(instr, "vcvt'cond.f64.f32 'Dd, 'Sm");
+ }
+}
+
+
+void Decoder::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) {
+ 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);
+ bool dp_operation = (instr->SzValue() == 1);
+ if (to_integer) {
+ bool unsigned_integer = (instr->Bit(16) == 0);
+
+ if (dp_operation) {
+ if (unsigned_integer) {
+ Format(instr, "vcvt'cond.u32.f64 'Sd, 'Dm");
+ } else {
+ Format(instr, "vcvt'cond.s32.f64 'Sd, 'Dm");
+ }
+ } else {
+ if (unsigned_integer) {
+ Format(instr, "vcvt'cond.u32.f32 'Sd, 'Sm");
+ } else {
+ Format(instr, "vcvt'cond.s32.f32 'Sd, 'Sm");
+ }
+ }
+ } else {
+ bool unsigned_integer = (instr->Bit(7) == 0);
+
+ if (dp_operation) {
+ if (unsigned_integer) {
+ Format(instr, "vcvt'cond.f64.u32 'Dd, 'Sm");
+ } else {
+ Format(instr, "vcvt'cond.f64.s32 'Dd, 'Sm");
+ }
+ } else {
+ if (unsigned_integer) {
+ Format(instr, "vcvt'cond.f32.u32 'Sd, 'Sm");
+ } else {
+ Format(instr, "vcvt'cond.f32.s32 'Sd, 'Sm");
+ }
+ }
+ }
+}
+
+
+// Decode Type 6 coprocessor instructions.
+// Dm = vmov(Rt, Rt2)
+// <Rt, Rt2> = vmov(Dm)
+// Ddst = MEM(Rbase + 4*offset).
+// MEM(Rbase + 4*offset) = Dsrc.
+void Decoder::DecodeType6CoprocessorIns(Instruction* instr) {
+ VERIFY(instr->TypeValue() == 6);
+
+ if (instr->CoprocessorValue() == 0xA) {
+ switch (instr->OpcodeValue()) {
+ case 0x8:
+ case 0xA:
+ if (instr->HasL()) {
+ Format(instr, "vldr'cond 'Sd, ['rn - 4*'imm08@00]");
+ } else {
+ Format(instr, "vstr'cond 'Sd, ['rn - 4*'imm08@00]");
+ }
+ break;
+ case 0xC:
+ case 0xE:
+ if (instr->HasL()) {
+ Format(instr, "vldr'cond 'Sd, ['rn + 4*'imm08@00]");
+ } else {
+ Format(instr, "vstr'cond 'Sd, ['rn + 4*'imm08@00]");
+ }
+ break;
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ case 0x9:
+ case 0xB: {
+ bool to_vfp_register = (instr->VLValue() == 0x1);
+ if (to_vfp_register) {
+ Format(instr, "vldm'cond'pu 'rn'w, {'Sd-'Sd+}");
+ } else {
+ Format(instr, "vstm'cond'pu 'rn'w, {'Sd-'Sd+}");
+ }
+ break;
+ }
+ default:
+ Unknown(instr); // Not used by V8.
+ }
+ } else if (instr->CoprocessorValue() == 0xB) {
+ switch (instr->OpcodeValue()) {
+ case 0x2:
+ // Load and store double to two GP registers
+ if (instr->Bits(7, 6) != 0 || instr->Bit(4) != 1) {
+ Unknown(instr); // Not used by V8.
+ } else if (instr->HasL()) {
+ Format(instr, "vmov'cond 'rt, 'rn, 'Dm");
+ } else {
+ Format(instr, "vmov'cond 'Dm, 'rt, 'rn");
+ }
+ break;
+ case 0x8:
+ case 0xA:
+ if (instr->HasL()) {
+ Format(instr, "vldr'cond 'Dd, ['rn - 4*'imm08@00]");
+ } else {
+ Format(instr, "vstr'cond 'Dd, ['rn - 4*'imm08@00]");
+ }
+ break;
+ case 0xC:
+ case 0xE:
+ if (instr->HasL()) {
+ Format(instr, "vldr'cond 'Dd, ['rn + 4*'imm08@00]");
+ } else {
+ Format(instr, "vstr'cond 'Dd, ['rn + 4*'imm08@00]");
+ }
+ break;
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ case 0x9:
+ case 0xB: {
+ bool to_vfp_register = (instr->VLValue() == 0x1);
+ if (to_vfp_register) {
+ Format(instr, "vldm'cond'pu 'rn'w, {'Dd-'Dd+}");
+ } else {
+ Format(instr, "vstm'cond'pu 'rn'w, {'Dd-'Dd+}");
+ }
+ break;
+ }
+ default:
+ Unknown(instr); // Not used by V8.
+ }
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+}
+
+
+void Decoder::DecodeSpecialCondition(Instruction* instr) {
+ switch (instr->SpecialValue()) {
+ case 5:
+ if ((instr->Bits(18, 16) == 0) && (instr->Bits(11, 6) == 0x28) &&
+ (instr->Bit(4) == 1)) {
+ // vmovl signed
+ int Vd = (instr->Bit(22) << 4) | instr->VdValue();
+ int Vm = (instr->Bit(5) << 4) | instr->VmValue();
+ int imm3 = instr->Bits(21, 19);
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "vmovl.s%d q%d, d%d", imm3*8, Vd, Vm);
+ } else {
+ Unknown(instr);
+ }
+ break;
+ case 7:
+ if ((instr->Bits(18, 16) == 0) && (instr->Bits(11, 6) == 0x28) &&
+ (instr->Bit(4) == 1)) {
+ // vmovl unsigned
+ int Vd = (instr->Bit(22) << 4) | instr->VdValue();
+ int Vm = (instr->Bit(5) << 4) | instr->VmValue();
+ int imm3 = instr->Bits(21, 19);
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "vmovl.u%d q%d, d%d", imm3*8, Vd, Vm);
+ } else {
+ Unknown(instr);
+ }
+ break;
+ case 8:
+ if (instr->Bits(21, 20) == 0) {
+ // vst1
+ int Vd = (instr->Bit(22) << 4) | instr->VdValue();
+ int Rn = instr->VnValue();
+ int type = instr->Bits(11, 8);
+ int size = instr->Bits(7, 6);
+ int align = instr->Bits(5, 4);
+ int Rm = instr->VmValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "vst1.%d ", (1 << size) << 3);
+ FormatNeonList(Vd, type);
+ Print(", ");
+ FormatNeonMemory(Rn, align, Rm);
+ } else if (instr->Bits(21, 20) == 2) {
+ // vld1
+ int Vd = (instr->Bit(22) << 4) | instr->VdValue();
+ int Rn = instr->VnValue();
+ int type = instr->Bits(11, 8);
+ int size = instr->Bits(7, 6);
+ int align = instr->Bits(5, 4);
+ int Rm = instr->VmValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "vld1.%d ", (1 << size) << 3);
+ FormatNeonList(Vd, type);
+ Print(", ");
+ FormatNeonMemory(Rn, align, Rm);
+ } else {
+ Unknown(instr);
+ }
+ break;
+ case 0xA:
+ case 0xB:
+ if ((instr->Bits(22, 20) == 5) && (instr->Bits(15, 12) == 0xf)) {
+ int Rn = instr->Bits(19, 16);
+ int offset = instr->Bits(11, 0);
+ if (offset == 0) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "pld [r%d]", Rn);
+ } else if (instr->Bit(23) == 0) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "pld [r%d, #-%d]", Rn, offset);
+ } else {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "pld [r%d, #+%d]", Rn, offset);
+ }
+ } else {
+ Unknown(instr);
+ }
+ break;
+ default:
+ Unknown(instr);
+ break;
+ }
+}
+
+#undef VERIFIY
+
+bool Decoder::IsConstantPoolAt(byte* instr_ptr) {
+ int instruction_bits = *(reinterpret_cast<int*>(instr_ptr));
+ return (instruction_bits & kConstantPoolMarkerMask) == kConstantPoolMarker;
+}
+
+
+int Decoder::ConstantPoolSizeAt(byte* instr_ptr) {
+ if (IsConstantPoolAt(instr_ptr)) {
+ int instruction_bits = *(reinterpret_cast<int*>(instr_ptr));
+ return DecodeConstantPoolLength(instruction_bits);
+ } else {
+ return -1;
+ }
+}
+
+
+// Disassemble the instruction at *instr_ptr into the output buffer.
+int Decoder::InstructionDecode(byte* instr_ptr) {
+ Instruction* instr = Instruction::At(instr_ptr);
+ // Print raw instruction bytes.
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%08x ",
+ instr->InstructionBits());
+ if (instr->ConditionField() == kSpecialCondition) {
+ DecodeSpecialCondition(instr);
+ return Instruction::kInstrSize;
+ }
+ int instruction_bits = *(reinterpret_cast<int*>(instr_ptr));
+ if ((instruction_bits & kConstantPoolMarkerMask) == kConstantPoolMarker) {
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "constant pool begin (length %d)",
+ DecodeConstantPoolLength(instruction_bits));
+ return Instruction::kInstrSize;
+ }
+ switch (instr->TypeValue()) {
+ case 0:
+ case 1: {
+ DecodeType01(instr);
+ break;
+ }
+ case 2: {
+ DecodeType2(instr);
+ break;
+ }
+ case 3: {
+ DecodeType3(instr);
+ break;
+ }
+ case 4: {
+ DecodeType4(instr);
+ break;
+ }
+ case 5: {
+ DecodeType5(instr);
+ break;
+ }
+ case 6: {
+ DecodeType6(instr);
+ break;
+ }
+ case 7: {
+ return DecodeType7(instr);
+ }
+ default: {
+ // The type field is 3-bits in the ARM encoding.
+ UNREACHABLE();
+ break;
+ }
+ }
+ return Instruction::kInstrSize;
+}
+
+
+} } // namespace v8::internal
+
+
+
+//------------------------------------------------------------------------------
+
+namespace disasm {
+
+
+const char* NameConverter::NameOfAddress(byte* addr) const {
+ v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
+ return tmp_buffer_.start();
+}
+
+
+const char* NameConverter::NameOfConstant(byte* addr) const {
+ return NameOfAddress(addr);
+}
+
+
+const char* NameConverter::NameOfCPURegister(int reg) const {
+ return v8::internal::Registers::Name(reg);
+}
+
+
+const char* NameConverter::NameOfByteCPURegister(int reg) const {
+ UNREACHABLE(); // ARM does not have the concept of a byte register
+ return "nobytereg";
+}
+
+
+const char* NameConverter::NameOfXMMRegister(int reg) const {
+ UNREACHABLE(); // ARM does not have any XMM registers
+ return "noxmmreg";
+}
+
+
+const char* NameConverter::NameInCode(byte* addr) const {
+ // The default name converter is called for unknown code. So we will not try
+ // to access any memory.
+ return "";
+}
+
+
+//------------------------------------------------------------------------------
+
+Disassembler::Disassembler(const NameConverter& converter)
+ : converter_(converter) {}
+
+
+Disassembler::~Disassembler() {}
+
+
+int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
+ byte* instruction) {
+ v8::internal::Decoder d(converter_, buffer);
+ return d.InstructionDecode(instruction);
+}
+
+
+int Disassembler::ConstantPoolSizeAt(byte* instruction) {
+ return v8::internal::Decoder::ConstantPoolSizeAt(instruction);
+}
+
+
+void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
+ NameConverter converter;
+ Disassembler d(converter);
+ for (byte* pc = begin; pc < end;) {
+ v8::internal::EmbeddedVector<char, 128> buffer;
+ buffer[0] = '\0';
+ byte* prev_pc = pc;
+ pc += d.InstructionDecode(buffer, pc);
+ v8::internal::PrintF(
+ f, "%p %08x %s\n",
+ prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer.start());
+ }
+}
+
+
+} // namespace disasm
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/frames-arm.cc b/chromium/v8/src/arm/frames-arm.cc
new file mode 100644
index 00000000000..b2071807d25
--- /dev/null
+++ b/chromium/v8/src/arm/frames-arm.cc
@@ -0,0 +1,53 @@
+// 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "assembler.h"
+#include "assembler-arm.h"
+#include "assembler-arm-inl.h"
+#include "frames.h"
+#include "macro-assembler.h"
+#include "macro-assembler-arm.h"
+
+namespace v8 {
+namespace internal {
+
+
+Register JavaScriptFrame::fp_register() { return v8::internal::fp; }
+Register JavaScriptFrame::context_register() { return cp; }
+
+
+Register StubFailureTrampolineFrame::fp_register() { return v8::internal::fp; }
+Register StubFailureTrampolineFrame::context_register() { return cp; }
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/frames-arm.h b/chromium/v8/src/arm/frames-arm.h
new file mode 100644
index 00000000000..d022b414b43
--- /dev/null
+++ b/chromium/v8/src/arm/frames-arm.h
@@ -0,0 +1,181 @@
+// Copyright 2012 the V8 project authors. 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_ARM_FRAMES_ARM_H_
+#define V8_ARM_FRAMES_ARM_H_
+
+namespace v8 {
+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:
+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
+const int kNumRegs = 16;
+
+
+// Caller-saved/arguments registers
+const RegList kJSCallerSaved =
+ 1 << 0 | // r0 a1
+ 1 << 1 | // r1 a2
+ 1 << 2 | // r2 a3
+ 1 << 3; // r3 a4
+
+const int kNumJSCallerSaved = 4;
+
+typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
+
+// Return the code of the n-th caller-saved register available to JavaScript
+// e.g. JSCallerSavedReg(0) returns r0.code() == 0
+int JSCallerSavedCode(int n);
+
+
+// Callee-saved registers preserved when switching from C to JavaScript
+const RegList kCalleeSaved =
+ 1 << 4 | // r4 v1
+ 1 << 5 | // r5 v2
+ 1 << 6 | // r6 v3
+ 1 << 7 | // r7 v4
+ 1 << 8 | // r8 v5 (cp in JavaScript code)
+ kR9Available << 9 | // r9 v6
+ 1 << 10 | // r10 v7
+ 1 << 11; // r11 v8 (fp in JavaScript code)
+
+// 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.
+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.
+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.
+const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved;
+const int kNumSafepointSavedRegisters = kNumJSCallerSaved + kNumCalleeSaved;
+
+// ----------------------------------------------------
+
+
+class EntryFrameConstants : public AllStatic {
+ public:
+ static const int kCallerFPOffset = -3 * kPointerSize;
+};
+
+
+class ExitFrameConstants : public AllStatic {
+ public:
+ static const int kCodeOffset = -2 * kPointerSize;
+ static const int kSPOffset = -1 * kPointerSize;
+
+ // The caller fields are below the frame pointer on the stack.
+ static const int kCallerFPOffset = 0 * kPointerSize;
+ // The calling JS function is below FP.
+ static const int kCallerPCOffset = 1 * kPointerSize;
+
+ // FP-relative displacement of the caller's SP. It points just
+ // below the saved PC.
+ static const int kCallerSPDisplacement = 2 * kPointerSize;
+};
+
+
+class JavaScriptFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
+ static const int kLastParameterOffset = +2 * kPointerSize;
+ static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
+
+ // Caller SP-relative.
+ static const int kParam0Offset = -2 * kPointerSize;
+ static const int kReceiverOffset = -1 * kPointerSize;
+};
+
+
+class ArgumentsAdaptorFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
+};
+
+
+class ConstructFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kImplicitReceiverOffset = -6 * kPointerSize;
+ static const int kConstructorOffset = -5 * kPointerSize;
+ static const int kLengthOffset = -4 * kPointerSize;
+ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
+
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + 4 * kPointerSize;
+};
+
+
+class InternalFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
+};
+
+
+inline Object* JavaScriptFrame::function_slot_object() const {
+ const int offset = JavaScriptFrameConstants::kFunctionOffset;
+ return Memory::Object_at(fp() + offset);
+}
+
+
+inline void StackHandler::SetFp(Address slot, Address fp) {
+ Memory::Address_at(slot) = fp;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_FRAMES_ARM_H_
diff --git a/chromium/v8/src/arm/full-codegen-arm.cc b/chromium/v8/src/arm/full-codegen-arm.cc
new file mode 100644
index 00000000000..b73006a17d9
--- /dev/null
+++ b/chromium/v8/src/arm/full-codegen-arm.cc
@@ -0,0 +1,4901 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "code-stubs.h"
+#include "codegen.h"
+#include "compiler.h"
+#include "debug.h"
+#include "full-codegen.h"
+#include "isolate-inl.h"
+#include "parser.h"
+#include "scopes.h"
+#include "stub-cache.h"
+
+#include "arm/code-stubs-arm.h"
+#include "arm/macro-assembler-arm.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm_)
+
+
+// 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 cmp rx, #yyy instruction, and x * 0x00000fff + yyy (raw 12 bit
+// immediate value is used) is the delta from the pc to the first instruction of
+// the patchable code.
+class JumpPatchSite BASE_EMBEDDED {
+ public:
+ explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) {
+#ifdef DEBUG
+ info_emitted_ = false;
+#endif
+ }
+
+ ~JumpPatchSite() {
+ ASSERT(patch_site_.is_bound() == info_emitted_);
+ }
+
+ // When initially emitting this ensure that a jump is always generated to skip
+ // the inlined smi code.
+ void EmitJumpIfNotSmi(Register reg, Label* target) {
+ ASSERT(!patch_site_.is_bound() && !info_emitted_);
+ Assembler::BlockConstPoolScope block_const_pool(masm_);
+ __ bind(&patch_site_);
+ __ cmp(reg, Operand(reg));
+ __ b(eq, target); // Always taken before patched.
+ }
+
+ // When initially emitting this ensure that a jump is never generated to skip
+ // the inlined smi code.
+ void EmitJumpIfSmi(Register reg, Label* target) {
+ ASSERT(!patch_site_.is_bound() && !info_emitted_);
+ Assembler::BlockConstPoolScope block_const_pool(masm_);
+ __ bind(&patch_site_);
+ __ cmp(reg, Operand(reg));
+ __ b(ne, target); // Never taken before patched.
+ }
+
+ void EmitPatchInfo() {
+ // Block literal pool emission whilst recording patch site information.
+ Assembler::BlockConstPoolScope block_const_pool(masm_);
+ if (patch_site_.is_bound()) {
+ int delta_to_patch_site = masm_->InstructionsGeneratedSince(&patch_site_);
+ Register reg;
+ reg.set_code(delta_to_patch_site / kOff12Mask);
+ __ cmp_raw_immediate(reg, delta_to_patch_site % kOff12Mask);
+#ifdef DEBUG
+ info_emitted_ = true;
+#endif
+ } else {
+ __ nop(); // Signals no inlined code.
+ }
+ }
+
+ private:
+ MacroAssembler* masm_;
+ Label patch_site_;
+#ifdef DEBUG
+ bool info_emitted_;
+#endif
+};
+
+
+// Generate code for a JS function. On entry to the function the receiver
+// and arguments have been pushed on the stack left to right. The actual
+// argument count matches the formal parameter count expected by the
+// function.
+//
+// The live registers are:
+// 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
+// o lr: return address
+//
+// The function builds a JS frame. Please see JavaScriptFrameConstants in
+// frames-arm.h for its layout.
+void FullCodeGenerator::Generate() {
+ CompilationInfo* info = info_;
+ handler_table_ =
+ isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
+ profiling_counter_ = isolate()->factory()->NewCell(
+ Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate()));
+ SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info->function()->name()->IsUtf8EqualTo(CStrVector(FLAG_stop_at))) {
+ __ stop("stop-at");
+ }
+#endif
+
+ // 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;
+ __ cmp(r5, Operand::Zero());
+ __ b(eq, &ok);
+ int receiver_offset = info->scope()->num_parameters() * kPointerSize;
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ str(r2, MemOperand(sp, receiver_offset));
+ __ 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);
+
+ info->set_prologue_offset(masm_->pc_offset());
+ {
+ PredictableCodeSizeScope predictible_code_size_scope(
+ masm_, kNoCodeAgeSequenceLength * Assembler::kInstrSize);
+ // The following three instructions must remain together and unmodified
+ // for code aging to work properly.
+ __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
+ __ nop(ip.code());
+ // Adjust FP to point to saved FP.
+ __ add(fp, sp, Operand(2 * kPointerSize));
+ }
+ info->AddNoFrameRange(0, masm_->pc_offset());
+
+ { Comment cmnt(masm_, "[ Allocate locals");
+ int locals_count = info->scope()->num_stack_slots();
+ // Generators allocate locals, if any, in context slots.
+ ASSERT(!info->function()->is_generator() || locals_count == 0);
+ if (locals_count > 0) {
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ for (int i = 0; i < locals_count; i++) {
+ __ push(ip);
+ }
+ }
+ }
+
+ bool function_in_register = true;
+
+ // Possibly allocate a local context.
+ int heap_slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ if (heap_slots > 0) {
+ // Argument to NewContext is the function, which is still in r1.
+ Comment cmnt(masm_, "[ Allocate context");
+ __ push(r1);
+ if (FLAG_harmony_scoping && info->scope()->is_global_scope()) {
+ __ Push(info->scope()->GetScopeInfo());
+ __ CallRuntime(Runtime::kNewGlobalContext, 2);
+ } else if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kNewFunctionContext, 1);
+ }
+ function_in_register = false;
+ // Context is returned in both r0 and cp. It replaces the context
+ // passed to us. It's saved in the stack and kept live in cp.
+ __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ // Copy any necessary parameters into the context.
+ int num_parameters = info->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.
+ __ ldr(r0, MemOperand(fp, parameter_offset));
+ // Store it in the context.
+ MemOperand target = ContextOperand(cp, var->index());
+ __ str(r0, target);
+
+ // Update the write barrier.
+ __ RecordWriteContextSlot(
+ cp, target.offset(), r0, r3, kLRHasBeenSaved, kDontSaveFPRegs);
+ }
+ }
+ }
+
+ Variable* arguments = scope()->arguments();
+ if (arguments != NULL) {
+ // Function uses arguments object.
+ Comment cmnt(masm_, "[ Allocate arguments object");
+ if (!function_in_register) {
+ // Load this again, if it's used by the local context below.
+ __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ } else {
+ __ mov(r3, r1);
+ }
+ // Receiver is just before the parameters on the caller's stack.
+ int num_parameters = info->scope()->num_parameters();
+ int offset = num_parameters * kPointerSize;
+ __ add(r2, fp,
+ Operand(StandardFrameConstants::kCallerSPOffset + offset));
+ __ mov(r1, Operand(Smi::FromInt(num_parameters)));
+ __ Push(r3, r2, r1);
+
+ // Arguments to ArgumentsAccessStub:
+ // function, receiver address, parameter count.
+ // The stub will rewrite receiever and parameter count if the previous
+ // stack frame was an arguments adapter frame.
+ 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, r0, r1, r2);
+ }
+
+ if (FLAG_trace) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+
+ // Visit the declarations and body unless there is an illegal
+ // redeclaration.
+ if (scope()->HasIllegalRedeclaration()) {
+ Comment cmnt(masm_, "[ Declarations");
+ scope()->VisitIllegalRedeclaration(this);
+
+ } else {
+ PrepareForBailoutForId(BailoutId::FunctionEntry(), NO_REGISTERS);
+ { Comment cmnt(masm_, "[ Declarations");
+ // For named function expressions, declare the function name as a
+ // constant.
+ if (scope()->is_function_scope() && scope()->function() != NULL) {
+ VariableDeclaration* function = scope()->function();
+ ASSERT(function->proxy()->var()->mode() == CONST ||
+ function->proxy()->var()->mode() == CONST_HARMONY);
+ ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED);
+ VisitVariableDeclaration(function);
+ }
+ VisitDeclarations(scope()->declarations());
+ }
+
+ { Comment cmnt(masm_, "[ Stack check");
+ PrepareForBailoutForId(BailoutId::Declarations(), NO_REGISTERS);
+ Label ok;
+ __ LoadRoot(ip, Heap::kStackLimitRootIndex);
+ __ cmp(sp, Operand(ip));
+ __ b(hs, &ok);
+ PredictableCodeSizeScope predictable(masm_, 2 * Assembler::kInstrSize);
+ StackCheckStub stub;
+ __ CallStub(&stub);
+ __ bind(&ok);
+ }
+
+ { Comment cmnt(masm_, "[ Body");
+ ASSERT(loop_depth() == 0);
+ VisitStatements(function()->body());
+ ASSERT(loop_depth() == 0);
+ }
+ }
+
+ // Always emit a 'return undefined' in case control fell off the end of
+ // the body.
+ { Comment cmnt(masm_, "[ return <undefined>;");
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+ }
+ EmitReturnSequence();
+
+ // Force emit the constant pool, so it doesn't get emitted in the middle
+ // of the back edge table.
+ masm()->CheckConstPool(true, false);
+}
+
+
+void FullCodeGenerator::ClearAccumulator() {
+ __ mov(r0, Operand(Smi::FromInt(0)));
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) {
+ __ mov(r2, Operand(profiling_counter_));
+ __ ldr(r3, FieldMemOperand(r2, Cell::kValueOffset));
+ __ sub(r3, r3, Operand(Smi::FromInt(delta)), SetCC);
+ __ str(r3, FieldMemOperand(r2, Cell::kValueOffset));
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterReset() {
+ int reset_value = FLAG_interrupt_budget;
+ if (info_->ShouldSelfOptimize() && !FLAG_retry_self_opt) {
+ // Self-optimization is a one-off thing: if it fails, don't try again.
+ reset_value = Smi::kMaxValue;
+ }
+ if (isolate()->IsDebuggerActive()) {
+ // Detect debug break requests as soon as possible.
+ reset_value = FLAG_interrupt_budget >> 4;
+ }
+ __ mov(r2, Operand(profiling_counter_));
+ __ mov(r3, Operand(Smi::FromInt(reset_value)));
+ __ str(r3, FieldMemOperand(r2, Cell::kValueOffset));
+}
+
+
+void FullCodeGenerator::EmitBackEdgeBookkeeping(IterationStatement* stmt,
+ Label* back_edge_target) {
+ Comment cmnt(masm_, "[ Back edge bookkeeping");
+ // Block literal pools whilst emitting back edge code.
+ Assembler::BlockConstPoolScope block_const_pool(masm_);
+ Label ok;
+
+ int weight = 1;
+ if (FLAG_weighted_back_edges) {
+ ASSERT(back_edge_target->is_bound());
+ int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target);
+ weight = Min(kMaxBackEdgeWeight,
+ Max(1, distance / kCodeSizeMultiplier));
+ }
+ EmitProfilingCounterDecrement(weight);
+ __ b(pl, &ok);
+ InterruptStub 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.
+ RecordBackEdge(stmt->OsrEntryId());
+
+ EmitProfilingCounterReset();
+
+ __ bind(&ok);
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ // Record a mapping of the OSR id to this PC. This is used if the OSR
+ // entry becomes the target of a bailout. We don't expect it to be, but
+ // we want it to work if it is.
+ PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::EmitReturnSequence() {
+ Comment cmnt(masm_, "[ Return sequence");
+ if (return_label_.is_bound()) {
+ __ b(&return_label_);
+ } else {
+ __ bind(&return_label_);
+ if (FLAG_trace) {
+ // Push the return value on the stack as the parameter.
+ // Runtime::TraceExit returns its parameter in r0.
+ __ push(r0);
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+ if (FLAG_interrupt_at_exit || FLAG_self_optimization) {
+ // Pretend that the exit is a backwards jump to the entry.
+ int weight = 1;
+ if (info_->ShouldSelfOptimize()) {
+ weight = FLAG_interrupt_budget / FLAG_self_opt_count;
+ } else if (FLAG_weighted_back_edges) {
+ int distance = masm_->pc_offset();
+ weight = Min(kMaxBackEdgeWeight,
+ Max(1, distance / kCodeSizeMultiplier));
+ }
+ EmitProfilingCounterDecrement(weight);
+ Label ok;
+ __ b(pl, &ok);
+ __ push(r0);
+ if (info_->ShouldSelfOptimize() && FLAG_direct_self_opt) {
+ __ ldr(r2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(r2);
+ __ CallRuntime(Runtime::kOptimizeFunctionOnNextCall, 1);
+ } else {
+ InterruptStub stub;
+ __ CallStub(&stub);
+ }
+ __ pop(r0);
+ EmitProfilingCounterReset();
+ __ bind(&ok);
+ }
+
+#ifdef DEBUG
+ // Add a label for checking the size of the code used for returning.
+ Label check_exit_codesize;
+ masm_->bind(&check_exit_codesize);
+#endif
+ // Make sure that the constant pool is not emitted inside of the return
+ // sequence.
+ { Assembler::BlockConstPoolScope block_const_pool(masm_);
+ // Here we use masm_-> instead of the __ macro to avoid the code coverage
+ // tool from instrumenting as we rely on the code size here.
+ int32_t sp_delta = (info_->scope()->num_parameters() + 1) * kPointerSize;
+ CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
+ // TODO(svenpanne) The code below is sometimes 4 words, sometimes 5!
+ PredictableCodeSizeScope predictable(masm_, -1);
+ __ RecordJSReturn();
+ masm_->mov(sp, fp);
+ int no_frame_start = masm_->pc_offset();
+ masm_->ldm(ia_w, sp, fp.bit() | lr.bit());
+ masm_->add(sp, sp, Operand(sp_delta));
+ masm_->Jump(lr);
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
+
+#ifdef DEBUG
+ // Check that the size of the code used for returning is large enough
+ // for the debugger's requirements.
+ ASSERT(Assembler::kJSReturnSequenceInstructions <=
+ masm_->InstructionsGeneratedSince(&check_exit_codesize));
+#endif
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ codegen()->GetVar(result_register(), var);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ codegen()->GetVar(result_register(), var);
+ __ push(result_register());
+}
+
+
+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(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Heap::RootListIndex index) const {
+ __ LoadRoot(result_register(), index);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(
+ Heap::RootListIndex index) const {
+ __ LoadRoot(result_register(), index);
+ __ push(result_register());
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ if (index == Heap::kUndefinedValueRootIndex ||
+ index == Heap::kNullValueRootIndex ||
+ index == Heap::kFalseValueRootIndex) {
+ if (false_label_ != fall_through_) __ b(false_label_);
+ } else if (index == Heap::kTrueValueRootIndex) {
+ if (true_label_ != fall_through_) __ b(true_label_);
+ } else {
+ __ LoadRoot(result_register(), index);
+ codegen()->DoTest(this);
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Handle<Object> lit) const {
+ __ mov(result_register(), Operand(lit));
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const {
+ // Immediates cannot be pushed directly.
+ __ mov(result_register(), Operand(lit));
+ __ push(result_register());
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ ASSERT(!lit->IsUndetectableObject()); // There are no undetectable literals.
+ if (lit->IsUndefined() || lit->IsNull() || lit->IsFalse()) {
+ if (false_label_ != fall_through_) __ b(false_label_);
+ } else if (lit->IsTrue() || lit->IsJSObject()) {
+ if (true_label_ != fall_through_) __ b(true_label_);
+ } else if (lit->IsString()) {
+ if (String::cast(*lit)->length() == 0) {
+ if (false_label_ != fall_through_) __ b(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ b(true_label_);
+ }
+ } else if (lit->IsSmi()) {
+ if (Smi::cast(*lit)->value() == 0) {
+ if (false_label_ != fall_through_) __ b(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ b(true_label_);
+ }
+ } else {
+ // For simplicity we always test the accumulator register.
+ __ mov(result_register(), Operand(lit));
+ codegen()->DoTest(this);
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ __ Drop(count);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::DropAndPlug(
+ int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ __ Drop(count);
+ __ Move(result_register(), reg);
+}
+
+
+void FullCodeGenerator::StackValueContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ if (count > 1) __ Drop(count - 1);
+ __ str(reg, MemOperand(sp, 0));
+}
+
+
+void FullCodeGenerator::TestContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ // For simplicity we always test the accumulator register.
+ __ Drop(count);
+ __ Move(result_register(), reg);
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Label* materialize_true,
+ Label* materialize_false) const {
+ ASSERT(materialize_true == materialize_false);
+ __ bind(materialize_true);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Label* materialize_true,
+ Label* materialize_false) const {
+ Label done;
+ __ bind(materialize_true);
+ __ LoadRoot(result_register(), Heap::kTrueValueRootIndex);
+ __ jmp(&done);
+ __ bind(materialize_false);
+ __ LoadRoot(result_register(), Heap::kFalseValueRootIndex);
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(
+ Label* materialize_true,
+ Label* materialize_false) const {
+ Label done;
+ __ bind(materialize_true);
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+ __ push(ip);
+ __ jmp(&done);
+ __ bind(materialize_false);
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
+ __ push(ip);
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Label* materialize_true,
+ Label* materialize_false) const {
+ ASSERT(materialize_true == true_label_);
+ ASSERT(materialize_false == false_label_);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(bool flag) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const {
+ Heap::RootListIndex value_root_index =
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
+ __ LoadRoot(result_register(), value_root_index);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(bool flag) const {
+ Heap::RootListIndex value_root_index =
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
+ __ LoadRoot(ip, value_root_index);
+ __ push(ip);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(bool flag) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ if (flag) {
+ if (true_label_ != fall_through_) __ b(true_label_);
+ } else {
+ if (false_label_ != fall_through_) __ b(false_label_);
+ }
+}
+
+
+void FullCodeGenerator::DoTest(Expression* condition,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ Handle<Code> ic = ToBooleanStub::GetUninitialized(isolate());
+ CallIC(ic, RelocInfo::CODE_TARGET, condition->test_id());
+ __ tst(result_register(), result_register());
+ Split(ne, if_true, if_false, fall_through);
+}
+
+
+void FullCodeGenerator::Split(Condition cond,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ if (if_false == fall_through) {
+ __ b(cond, if_true);
+ } else if (if_true == fall_through) {
+ __ b(NegateCondition(cond), if_false);
+ } else {
+ __ b(cond, if_true);
+ __ b(if_false);
+ }
+}
+
+
+MemOperand FullCodeGenerator::StackOperand(Variable* var) {
+ ASSERT(var->IsStackAllocated());
+ // Offset is negative because higher indexes are at lower addresses.
+ int offset = -var->index() * kPointerSize;
+ // Adjust by a (parameter or local) base offset.
+ if (var->IsParameter()) {
+ offset += (info_->scope()->num_parameters() + 1) * kPointerSize;
+ } else {
+ offset += JavaScriptFrameConstants::kLocal0Offset;
+ }
+ return MemOperand(fp, offset);
+}
+
+
+MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ if (var->IsContextSlot()) {
+ int context_chain_length = scope()->ContextChainLength(var->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return ContextOperand(scratch, var->index());
+ } else {
+ return StackOperand(var);
+ }
+}
+
+
+void FullCodeGenerator::GetVar(Register dest, Variable* var) {
+ // Use destination as scratch.
+ MemOperand location = VarOperand(var, dest);
+ __ ldr(dest, location);
+}
+
+
+void FullCodeGenerator::SetVar(Variable* var,
+ Register src,
+ Register scratch0,
+ Register scratch1) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ ASSERT(!scratch0.is(src));
+ ASSERT(!scratch0.is(scratch1));
+ 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()) {
+ __ RecordWriteContextSlot(scratch0,
+ location.offset(),
+ src,
+ scratch1,
+ kLRHasBeenSaved,
+ kDontSaveFPRegs);
+ }
+}
+
+
+void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
+ bool should_normalize,
+ Label* if_true,
+ Label* if_false) {
+ // Only prepare for bailouts before splits if we're in a test
+ // context. Otherwise, we let the Visit function deal with the
+ // preparation to avoid preparing with the same AST id twice.
+ if (!context()->IsTest() || !info_->IsOptimizable()) return;
+
+ Label skip;
+ if (should_normalize) __ b(&skip);
+ PrepareForBailout(expr, TOS_REG);
+ if (should_normalize) {
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+ __ cmp(r0, ip);
+ Split(eq, if_true, if_false, NULL);
+ __ bind(&skip);
+ }
+}
+
+
+void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
+ // The variable in the declaration always resides in the current function
+ // context.
+ ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
+ if (generate_debug_code_) {
+ // Check that we're not inside a with or catch context.
+ __ ldr(r1, FieldMemOperand(cp, HeapObject::kMapOffset));
+ __ CompareRoot(r1, Heap::kWithContextMapRootIndex);
+ __ Check(ne, kDeclarationInWithContext);
+ __ CompareRoot(r1, Heap::kCatchContextMapRootIndex);
+ __ Check(ne, kDeclarationInCatchContext);
+ }
+}
+
+
+void FullCodeGenerator::VisitVariableDeclaration(
+ VariableDeclaration* declaration) {
+ // 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.
+ VariableProxy* proxy = declaration->proxy();
+ VariableMode mode = declaration->mode();
+ Variable* variable = proxy->var();
+ bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ globals_->Add(variable->name(), zone());
+ globals_->Add(variable->binding_needs_init()
+ ? isolate()->factory()->the_hole_value()
+ : isolate()->factory()->undefined_value(),
+ zone());
+ break;
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ if (hole_init) {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ str(ip, StackOperand(variable));
+ }
+ break;
+
+ case Variable::CONTEXT:
+ if (hole_init) {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ str(ip, ContextOperand(cp, variable->index()));
+ // No write barrier since the_hole_value is in old space.
+ PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
+ }
+ break;
+
+ case Variable::LOOKUP: {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ __ mov(r2, Operand(variable->name()));
+ // Declaration nodes are always introduced in one of four modes.
+ ASSERT(IsDeclaredVariableMode(mode));
+ PropertyAttributes attr =
+ IsImmutableVariableMode(mode) ? 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
+ // 'undefined') because we may have a (legal) redeclaration and we
+ // must not destroy the current value.
+ if (hole_init) {
+ __ LoadRoot(r0, Heap::kTheHoleValueRootIndex);
+ __ Push(cp, r2, r1, r0);
+ } else {
+ __ mov(r0, Operand(Smi::FromInt(0))); // Indicates no initial value.
+ __ Push(cp, r2, r1, r0);
+ }
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitFunctionDeclaration(
+ FunctionDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED: {
+ globals_->Add(variable->name(), zone());
+ Handle<SharedFunctionInfo> function =
+ Compiler::BuildFunctionInfo(declaration->fun(), script());
+ // Check for stack-overflow exception.
+ if (function.is_null()) return SetStackOverflow();
+ globals_->Add(function, zone());
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ VisitForAccumulatorValue(declaration->fun());
+ __ str(result_register(), StackOperand(variable));
+ break;
+ }
+
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ VisitForAccumulatorValue(declaration->fun());
+ __ 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.
+ __ RecordWriteContextSlot(cp,
+ offset,
+ result_register(),
+ r2,
+ kLRHasBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
+ break;
+ }
+
+ case Variable::LOOKUP: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ __ mov(r2, Operand(variable->name()));
+ __ mov(r1, Operand(Smi::FromInt(NONE)));
+ __ Push(cp, r2, r1);
+ // Push initial value for function declaration.
+ VisitForStackValue(declaration->fun());
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
+ Variable* variable = declaration->proxy()->var();
+ ASSERT(variable->location() == Variable::CONTEXT);
+ ASSERT(variable->interface()->IsFrozen());
+
+ Comment cmnt(masm_, "[ ModuleDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+
+ // Load instance object.
+ __ LoadContext(r1, scope_->ContextChainLength(scope_->GlobalScope()));
+ __ ldr(r1, ContextOperand(r1, variable->interface()->Index()));
+ __ ldr(r1, ContextOperand(r1, Context::EXTENSION_INDEX));
+
+ // Assign it.
+ __ str(r1, ContextOperand(cp, variable->index()));
+ // We know that we have written a module, which is not a smi.
+ __ RecordWriteContextSlot(cp,
+ Context::SlotOffset(variable->index()),
+ r1,
+ r3,
+ kLRHasBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS);
+
+ // Traverse into body.
+ Visit(declaration->module());
+}
+
+
+void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ // TODO(rossberg)
+ break;
+
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, "[ ImportDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ // TODO(rossberg)
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ case Variable::LOOKUP:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
+ // TODO(rossberg)
+}
+
+
+void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
+ // Call the runtime to declare the globals.
+ // The context is the first argument.
+ __ mov(r1, Operand(pairs));
+ __ mov(r0, Operand(Smi::FromInt(DeclareGlobalsFlags())));
+ __ Push(cp, r1, r0);
+ __ CallRuntime(Runtime::kDeclareGlobals, 3);
+ // Return value is ignored.
+}
+
+
+void FullCodeGenerator::DeclareModules(Handle<FixedArray> descriptions) {
+ // Call the runtime to declare the modules.
+ __ Push(descriptions);
+ __ CallRuntime(Runtime::kDeclareModules, 1);
+ // Return value is ignored.
+}
+
+
+void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
+ Comment cmnt(masm_, "[ SwitchStatement");
+ Breakable nested_statement(this, stmt);
+ SetStatementPosition(stmt);
+
+ // Keep the switch value on the stack until a case matches.
+ VisitForStackValue(stmt->tag());
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+
+ ZoneList<CaseClause*>* clauses = stmt->cases();
+ CaseClause* default_clause = NULL; // Can occur anywhere in the list.
+
+ Label next_test; // Recycled for each test.
+ // Compile all the tests with branches to their bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ CaseClause* clause = clauses->at(i);
+ clause->body_target()->Unuse();
+
+ // The default is not a test, but remember it as final fall through.
+ if (clause->is_default()) {
+ default_clause = clause;
+ continue;
+ }
+
+ Comment cmnt(masm_, "[ Case comparison");
+ __ bind(&next_test);
+ next_test.Unuse();
+
+ // Compile the label expression.
+ VisitForAccumulatorValue(clause->label());
+
+ // Perform the comparison as if via '==='.
+ __ ldr(r1, MemOperand(sp, 0)); // Switch value.
+ bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ orr(r2, r1, r0);
+ patch_site.EmitJumpIfNotSmi(r2, &slow_case);
+
+ __ cmp(r1, r0);
+ __ b(ne, &next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ __ b(clause->body_target());
+ __ bind(&slow_case);
+ }
+
+ // Record position before stub call for type feedback.
+ SetSourcePosition(clause->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), Token::EQ_STRICT);
+ CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
+ patch_site.EmitPatchInfo();
+
+ __ cmp(r0, Operand::Zero());
+ __ b(ne, &next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ __ b(clause->body_target());
+ }
+
+ // Discard the test value and jump to the default if present, otherwise to
+ // the end of the statement.
+ __ bind(&next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ if (default_clause == NULL) {
+ __ b(nested_statement.break_label());
+ } else {
+ __ b(default_clause->body_target());
+ }
+
+ // Compile all the case bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ Comment cmnt(masm_, "[ Case body");
+ CaseClause* clause = clauses->at(i);
+ __ bind(clause->body_target());
+ PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
+ VisitStatements(clause->statements());
+ }
+
+ __ bind(nested_statement.break_label());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
+ Comment cmnt(masm_, "[ ForInStatement");
+ SetStatementPosition(stmt);
+
+ Label loop, exit;
+ ForIn loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // Get the object to enumerate over. If the object is null or undefined, skip
+ // over the loop. See ECMA-262 version 5, section 12.6.4.
+ VisitForAccumulatorValue(stmt->enumerable());
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(r0, ip);
+ __ b(eq, &exit);
+ Register null_value = r5;
+ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
+ __ cmp(r0, null_value);
+ __ b(eq, &exit);
+
+ PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
+
+ // Convert the object to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(r0, &convert);
+ __ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE);
+ __ b(ge, &done_convert);
+ __ bind(&convert);
+ __ push(r0);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ 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.
+ __ CheckEnumCache(null_value, &call_runtime);
+
+ // The enum cache is valid. Load the map of the object being
+ // iterated over and use the cache for the iteration.
+ Label use_cache;
+ __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ b(&use_cache);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(r0); // Duplicate the enumerable object on the stack.
+ __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
+
+ // If we got a map from the runtime call, we can do a fast
+ // modification check. Otherwise, we got a fixed array, and we have
+ // to do a slow check.
+ Label fixed_array;
+ __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kMetaMapRootIndex);
+ __ cmp(r2, ip);
+ __ b(ne, &fixed_array);
+
+ // We got a map in register r0. Get the enumeration cache from it.
+ Label no_descriptors;
+ __ bind(&use_cache);
+
+ __ EnumLength(r1, r0);
+ __ cmp(r1, Operand(Smi::FromInt(0)));
+ __ b(eq, &no_descriptors);
+
+ __ LoadInstanceDescriptors(r0, r2);
+ __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheOffset));
+ __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
+
+ // Set up the four remaining stack slots.
+ __ push(r0); // Map.
+ __ mov(r0, Operand(Smi::FromInt(0)));
+ // Push enumeration cache, enumeration cache length (as smi) and zero.
+ __ Push(r2, r1, r0);
+ __ jmp(&loop);
+
+ __ bind(&no_descriptors);
+ __ Drop(1);
+ __ jmp(&exit);
+
+ // We got a fixed array in register r0. Iterate through that.
+ Label non_proxy;
+ __ bind(&fixed_array);
+
+ Handle<Cell> cell = isolate()->factory()->NewCell(
+ Handle<Object>(Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker),
+ isolate()));
+ RecordTypeFeedbackCell(stmt->ForInFeedbackId(), cell);
+ __ LoadHeapObject(r1, cell);
+ __ mov(r2, Operand(Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker)));
+ __ str(r2, FieldMemOperand(r1, Cell::kValueOffset));
+
+ __ 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.
+
+ // Generate code for doing the condition check.
+ PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
+ __ bind(&loop);
+ // Load the current count to r0, load the length to r1.
+ __ Ldrd(r0, r1, MemOperand(sp, 0 * kPointerSize));
+ __ cmp(r0, r1); // Compare to the array length.
+ __ b(hs, loop_statement.break_label());
+
+ // Get the current entry of the array into register r3.
+ __ ldr(r2, MemOperand(sp, 2 * kPointerSize));
+ __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ ldr(r3, MemOperand::PointerAddressFromSmiKey(r2, r0));
+
+ // 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 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.
+ __ push(r1); // Enumerable.
+ __ push(r3); // Current entry.
+ __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
+ __ mov(r3, Operand(r0), SetCC);
+ __ b(eq, loop_statement.continue_label());
+
+ // Update the 'each' property or variable from the possibly filtered
+ // entry in register r3.
+ __ bind(&update_each);
+ __ mov(result_register(), r3);
+ // Perform the assignment as if via '='.
+ { EffectContext context(this);
+ EmitAssignment(stmt->each());
+ }
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Generate code for the going to the next element by incrementing
+ // the index (smi) stored on top of the stack.
+ __ bind(loop_statement.continue_label());
+ __ pop(r0);
+ __ add(r0, r0, Operand(Smi::FromInt(1)));
+ __ push(r0);
+
+ EmitBackEdgeBookkeeping(stmt, &loop);
+ __ b(&loop);
+
+ // Remove the pointers stored on the stack.
+ __ bind(loop_statement.break_label());
+ __ Drop(5);
+
+ // Exit and decrement the loop depth.
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(&exit);
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
+ Comment cmnt(masm_, "[ ForOfStatement");
+ SetStatementPosition(stmt);
+
+ Iteration loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // var iterator = iterable[@@iterator]()
+ VisitForAccumulatorValue(stmt->assign_iterator());
+
+ // As with for-in, skip the loop if the iterator is null or undefined.
+ __ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
+ __ b(eq, loop_statement.break_label());
+ __ CompareRoot(r0, Heap::kNullValueRootIndex);
+ __ b(eq, loop_statement.break_label());
+
+ // Convert the iterator to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(r0, &convert);
+ __ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE);
+ __ b(ge, &done_convert);
+ __ bind(&convert);
+ __ push(r0);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ bind(&done_convert);
+ __ push(r0);
+
+ // Loop entry.
+ __ bind(loop_statement.continue_label());
+
+ // result = iterator.next()
+ VisitForEffect(stmt->next_result());
+
+ // if (result.done) break;
+ Label result_not_done;
+ VisitForControl(stmt->result_done(),
+ loop_statement.break_label(),
+ &result_not_done,
+ &result_not_done);
+ __ bind(&result_not_done);
+
+ // each = result.value
+ VisitForEffect(stmt->assign_each());
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Check stack before looping.
+ PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS);
+ EmitBackEdgeBookkeeping(stmt, loop_statement.continue_label());
+ __ jmp(loop_statement.continue_label());
+
+ // Exit and decrement the loop depth.
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(loop_statement.break_label());
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
+ bool pretenure) {
+ // Use the fast case closure allocation code that allocates in new
+ // space for nested functions that don't need literals cloning. If
+ // we're running with the --always-opt or the --prepare-always-opt
+ // flag, we need to use the runtime function so that the new function
+ // we are creating here gets a chance to have its code optimized and
+ // doesn't just get a copy of the existing unoptimized code.
+ if (!FLAG_always_opt &&
+ !FLAG_prepare_always_opt &&
+ !pretenure &&
+ scope()->is_function_scope() &&
+ info->num_literals() == 0) {
+ FastNewClosureStub stub(info->language_mode(), info->is_generator());
+ __ mov(r0, Operand(info));
+ __ push(r0);
+ __ CallStub(&stub);
+ } else {
+ __ mov(r0, Operand(info));
+ __ LoadRoot(r1, pretenure ? Heap::kTrueValueRootIndex
+ : Heap::kFalseValueRootIndex);
+ __ Push(cp, r0, r1);
+ __ CallRuntime(Runtime::kNewClosure, 3);
+ }
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
+ Comment cmnt(masm_, "[ VariableProxy");
+ EmitVariableLoad(expr);
+}
+
+
+void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
+ TypeofState typeof_state,
+ Label* slow) {
+ Register current = cp;
+ Register next = r1;
+ Register temp = r2;
+
+ Scope* s = scope();
+ while (s != NULL) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_non_strict_eval()) {
+ // Check that extension is NULL.
+ __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ }
+ // Load next context in chain.
+ __ ldr(next, ContextOperand(current, Context::PREVIOUS_INDEX));
+ // Walk the rest of the chain without clobbering cp.
+ current = next;
+ }
+ // If no outer scope calls eval, we do not need to check more
+ // context extensions.
+ if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break;
+ s = s->outer_scope();
+ }
+
+ if (s->is_eval_scope()) {
+ Label loop, fast;
+ if (!current.is(next)) {
+ __ Move(next, current);
+ }
+ __ bind(&loop);
+ // Terminate at native context.
+ __ ldr(temp, FieldMemOperand(next, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kNativeContextMapRootIndex);
+ __ cmp(temp, ip);
+ __ b(eq, &fast);
+ // Check that extension is NULL.
+ __ ldr(temp, ContextOperand(next, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ // Load next context in chain.
+ __ ldr(next, ContextOperand(next, Context::PREVIOUS_INDEX));
+ __ b(&loop);
+ __ bind(&fast);
+ }
+
+ __ ldr(r0, GlobalObjectOperand());
+ __ mov(r2, Operand(var->name()));
+ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+ ? RelocInfo::CODE_TARGET
+ : RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, mode);
+}
+
+
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var,
+ Label* slow) {
+ ASSERT(var->IsContextSlot());
+ Register context = cp;
+ Register next = r3;
+ Register temp = r4;
+
+ for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_non_strict_eval()) {
+ // Check that extension is NULL.
+ __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ }
+ __ ldr(next, ContextOperand(context, Context::PREVIOUS_INDEX));
+ // Walk the rest of the chain without clobbering cp.
+ context = next;
+ }
+ }
+ // Check that last extension is NULL.
+ __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+
+ // This function is used only for loads, not stores, so it's safe to
+ // return an cp-based operand (the write barrier cannot be allowed to
+ // destroy the cp register).
+ return ContextOperand(context, var->index());
+}
+
+
+void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var,
+ TypeofState typeof_state,
+ Label* slow,
+ Label* done) {
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // 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() == DYNAMIC_GLOBAL) {
+ EmitLoadGlobalCheckExtensions(var, typeof_state, slow);
+ __ jmp(done);
+ } else if (var->mode() == DYNAMIC_LOCAL) {
+ Variable* local = var->local_if_not_shadowed();
+ __ ldr(r0, ContextSlotOperandCheckExtensions(local, slow));
+ if (local->mode() == LET ||
+ local->mode() == CONST ||
+ local->mode() == CONST_HARMONY) {
+ __ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
+ 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);
+ }
+}
+
+
+void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
+ // Record position before possible IC call.
+ SetSourcePosition(proxy->position());
+ Variable* var = proxy->var();
+
+ // Three cases: global variables, lookup variables, and all other types of
+ // variables.
+ switch (var->location()) {
+ case Variable::UNALLOCATED: {
+ Comment cmnt(masm_, "Global variable");
+ // Use inline caching. Variable name is passed in r2 and the global
+ // object (receiver) in r0.
+ __ ldr(r0, GlobalObjectOperand());
+ __ mov(r2, Operand(var->name()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ context()->Plug(r0);
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, var->IsContextSlot()
+ ? "Context variable"
+ : "Stack variable");
+ 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.
+ 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(var);
+ break;
+ }
+
+ case Variable::LOOKUP: {
+ Label done, slow;
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done);
+ __ bind(&slow);
+ Comment cmnt(masm_, "Lookup variable");
+ __ mov(r1, Operand(var->name()));
+ __ Push(cp, r1); // Context and name.
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ bind(&done);
+ context()->Plug(r0);
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
+ Comment cmnt(masm_, "[ RegExpLiteral");
+ Label materialized;
+ // Registers will be used as follows:
+ // r5 = materialized value (RegExp literal)
+ // r4 = JS function, literals array
+ // r3 = literal index
+ // r2 = RegExp pattern
+ // r1 = RegExp flags
+ // r0 = RegExp literal clone
+ __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ ldr(r4, FieldMemOperand(r0, JSFunction::kLiteralsOffset));
+ int literal_offset =
+ FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
+ __ ldr(r5, FieldMemOperand(r4, literal_offset));
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(r5, ip);
+ __ b(ne, &materialized);
+
+ // Create regexp literal using runtime function.
+ // Result will be in r0.
+ __ mov(r3, Operand(Smi::FromInt(expr->literal_index())));
+ __ mov(r2, Operand(expr->pattern()));
+ __ mov(r1, Operand(expr->flags()));
+ __ Push(r4, r3, r2, r1);
+ __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
+ __ mov(r5, r0);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+ __ Allocate(size, r0, r2, r3, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ push(r5);
+ __ mov(r0, Operand(Smi::FromInt(size)));
+ __ push(r0);
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ pop(r5);
+
+ __ bind(&allocated);
+ // After this, registers are used as follows:
+ // r0: Newly allocated regexp.
+ // r5: Materialized regexp.
+ // r2: temp.
+ __ CopyFields(r0, r5, d0, size / kPointerSize);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitAccessor(Expression* expression) {
+ if (expression == NULL) {
+ __ LoadRoot(r1, Heap::kNullValueRootIndex);
+ __ push(r1);
+ } else {
+ VisitForStackValue(expression);
+ }
+}
+
+
+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(constant_properties));
+ int flags = expr->fast_elements()
+ ? ObjectLiteral::kFastElements
+ : ObjectLiteral::kNoFlags;
+ flags |= expr->has_function()
+ ? ObjectLiteral::kHasFunction
+ : ObjectLiteral::kNoFlags;
+ __ mov(r0, Operand(Smi::FromInt(flags)));
+ int properties_count = constant_properties->length() / 2;
+ if ((FLAG_track_double_fields && expr->may_store_doubles()) ||
+ expr->depth() > 1) {
+ __ Push(r3, r2, r1, r0);
+ __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
+ } else if (Serializer::enabled() || flags != ObjectLiteral::kFastElements ||
+ properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) {
+ __ Push(r3, r2, r1, r0);
+ __ 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
+ // result_saved is false the result is in r0.
+ bool result_saved = false;
+
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ expr->CalculateEmitStore(zone());
+
+ AccessorTable accessor_table(zone());
+ for (int i = 0; i < expr->properties()->length(); i++) {
+ ObjectLiteral::Property* property = expr->properties()->at(i);
+ if (property->IsCompileTimeValue()) continue;
+
+ Literal* key = property->key();
+ Expression* value = property->value();
+ if (!result_saved) {
+ __ push(r0); // Save result on stack
+ result_saved = true;
+ }
+ switch (property->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ UNREACHABLE();
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+ ASSERT(!CompileTimeValue::IsCompileTimeValue(property->value()));
+ // Fall through.
+ case ObjectLiteral::Property::COMPUTED:
+ if (key->value()->IsInternalizedString()) {
+ if (property->emit_store()) {
+ VisitForAccumulatorValue(value);
+ __ mov(r2, Operand(key->value()));
+ __ ldr(r1, MemOperand(sp));
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, key->LiteralFeedbackId());
+ PrepareForBailoutForId(key->id(), NO_REGISTERS);
+ } else {
+ VisitForEffect(value);
+ }
+ break;
+ }
+ // Duplicate receiver on stack.
+ __ ldr(r0, MemOperand(sp));
+ __ push(r0);
+ VisitForStackValue(key);
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ mov(r0, Operand(Smi::FromInt(NONE))); // PropertyAttributes
+ __ push(r0);
+ __ CallRuntime(Runtime::kSetProperty, 4);
+ } else {
+ __ Drop(3);
+ }
+ break;
+ case ObjectLiteral::Property::PROTOTYPE:
+ // Duplicate receiver on stack.
+ __ ldr(r0, MemOperand(sp));
+ __ push(r0);
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ CallRuntime(Runtime::kSetPrototype, 2);
+ } else {
+ __ Drop(2);
+ }
+ break;
+
+ case ObjectLiteral::Property::GETTER:
+ accessor_table.lookup(key)->second->getter = value;
+ break;
+ case ObjectLiteral::Property::SETTER:
+ accessor_table.lookup(key)->second->setter = value;
+ break;
+ }
+ }
+
+ // Emit code to define accessors, using only a single call to the runtime for
+ // each pair of corresponding getters and setters.
+ for (AccessorTable::Iterator it = accessor_table.begin();
+ it != accessor_table.end();
+ ++it) {
+ __ ldr(r0, MemOperand(sp)); // Duplicate receiver.
+ __ push(r0);
+ VisitForStackValue(it->first);
+ EmitAccessor(it->second->getter);
+ EmitAccessor(it->second->setter);
+ __ mov(r0, Operand(Smi::FromInt(NONE)));
+ __ push(r0);
+ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ }
+
+ if (expr->has_function()) {
+ ASSERT(result_saved);
+ __ ldr(r0, MemOperand(sp));
+ __ push(r0);
+ __ CallRuntime(Runtime::kToFastProperties, 1);
+ }
+
+ if (result_saved) {
+ context()->PlugTOS();
+ } else {
+ context()->Plug(r0);
+ }
+}
+
+
+void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
+ Comment cmnt(masm_, "[ ArrayLiteral");
+
+ 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 = IsFastObjectElementsKind(constant_elements_kind);
+ 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(constant_elements));
+ if (has_fast_elements && constant_elements_values->map() ==
+ isolate()->heap()->fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS,
+ DONT_TRACK_ALLOCATION_SITE,
+ length);
+ __ CallStub(&stub);
+ __ IncrementCounter(
+ isolate()->counters()->cow_arrays_created_stub(), 1, r1, r2);
+ } else if (expr->depth() > 1) {
+ __ Push(r3, r2, r1);
+ __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
+ } else if (Serializer::enabled() ||
+ length > FastCloneShallowArrayStub::kMaximumClonedLength) {
+ __ Push(r3, r2, r1);
+ __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
+ } else {
+ ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) ||
+ FLAG_smi_only_arrays);
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
+ AllocationSiteMode allocation_site_mode = FLAG_track_allocation_sites
+ ? TRACK_ALLOCATION_SITE : DONT_TRACK_ALLOCATION_SITE;
+
+ if (has_fast_elements) {
+ mode = FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
+ }
+
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
+ __ CallStub(&stub);
+ }
+
+ bool result_saved = false; // Is the result saved to the stack?
+
+ // Emit code to evaluate all the non-constant subexpressions and to store
+ // them into the newly cloned array.
+ for (int i = 0; i < length; i++) {
+ Expression* subexpr = subexprs->at(i);
+ // If the subexpression is a literal or a simple materialized literal it
+ // is already set in the cloned array.
+ if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
+
+ if (!result_saved) {
+ __ push(r0);
+ __ Push(Smi::FromInt(expr->literal_index()));
+ result_saved = true;
+ }
+ VisitForAccumulatorValue(subexpr);
+
+ if (IsFastObjectElementsKind(constant_elements_kind)) {
+ int offset = FixedArray::kHeaderSize + (i * kPointerSize);
+ __ ldr(r6, MemOperand(sp, kPointerSize)); // 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 {
+ __ mov(r3, Operand(Smi::FromInt(i)));
+ StoreArrayLiteralElementStub stub;
+ __ CallStub(&stub);
+ }
+
+ PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
+ }
+
+ if (result_saved) {
+ __ pop(); // literal index
+ context()->PlugTOS();
+ } else {
+ context()->Plug(r0);
+ }
+}
+
+
+void FullCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // on the left-hand side.
+ if (!expr->target()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->target());
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* property = expr->target()->AsProperty();
+ if (property != NULL) {
+ assign_type = (property->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ // Evaluate LHS expression.
+ switch (assign_type) {
+ case VARIABLE:
+ // Nothing to do here.
+ break;
+ case NAMED_PROPERTY:
+ if (expr->is_compound()) {
+ // We need the receiver both on the stack and in the accumulator.
+ VisitForAccumulatorValue(property->obj());
+ __ push(result_register());
+ } else {
+ VisitForStackValue(property->obj());
+ }
+ break;
+ case KEYED_PROPERTY:
+ if (expr->is_compound()) {
+ VisitForStackValue(property->obj());
+ VisitForAccumulatorValue(property->key());
+ __ ldr(r1, MemOperand(sp, 0));
+ __ push(r0);
+ } else {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ }
+ break;
+ }
+
+ // For compound assignments we need another deoptimization point after the
+ // variable/property load.
+ if (expr->is_compound()) {
+ { AccumulatorValueContext context(this);
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableLoad(expr->target()->AsVariableProxy());
+ PrepareForBailout(expr->target(), TOS_REG);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
+ }
+ }
+
+ Token::Value op = expr->binary_op();
+ __ push(r0); // Left operand goes on the stack.
+ VisitForAccumulatorValue(expr->value());
+
+ OverwriteMode mode = expr->value()->ResultOverwriteAllowed()
+ ? OVERWRITE_RIGHT
+ : NO_OVERWRITE;
+ SetSourcePosition(expr->position() + 1);
+ AccumulatorValueContext context(this);
+ if (ShouldInlineSmiCase(op)) {
+ EmitInlineSmiBinaryOp(expr->binary_operation(),
+ op,
+ mode,
+ expr->target(),
+ expr->value());
+ } else {
+ EmitBinaryOp(expr->binary_operation(), op, mode);
+ }
+
+ // Deoptimization point in case the binary operation may have side effects.
+ PrepareForBailout(expr->binary_operation(), TOS_REG);
+ } else {
+ VisitForAccumulatorValue(expr->value());
+ }
+
+ // Record source position before possible IC call.
+ SetSourcePosition(expr->position());
+
+ // Store the value.
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ expr->op());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(r0);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyAssignment(expr);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyAssignment(expr);
+ break;
+ }
+}
+
+
+void FullCodeGenerator::VisitYield(Yield* expr) {
+ Comment cmnt(masm_, "[ Yield");
+ // Evaluate yielded value first; the initial iterator definition depends on
+ // this. It stays on the stack while we update the iterator.
+ VisitForStackValue(expr->expression());
+
+ switch (expr->yield_kind()) {
+ case Yield::SUSPEND:
+ // Pop value from top-of-stack slot; box result into result register.
+ EmitCreateIteratorResult(false);
+ __ push(result_register());
+ // Fall through.
+ case Yield::INITIAL: {
+ Label suspend, continuation, post_runtime, resume;
+
+ __ jmp(&suspend);
+
+ __ bind(&continuation);
+ __ jmp(&resume);
+
+ __ bind(&suspend);
+ VisitForAccumulatorValue(expr->generator_object());
+ ASSERT(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
+ __ mov(r1, Operand(Smi::FromInt(continuation.pos())));
+ __ str(r1, FieldMemOperand(r0, JSGeneratorObject::kContinuationOffset));
+ __ str(cp, FieldMemOperand(r0, JSGeneratorObject::kContextOffset));
+ __ mov(r1, cp);
+ __ RecordWriteField(r0, JSGeneratorObject::kContextOffset, r1, r2,
+ kLRHasBeenSaved, kDontSaveFPRegs);
+ __ add(r1, fp, Operand(StandardFrameConstants::kExpressionsOffset));
+ __ cmp(sp, r1);
+ __ b(eq, &post_runtime);
+ __ push(r0); // generator object
+ __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ __ bind(&post_runtime);
+ __ pop(result_register());
+ EmitReturnSequence();
+
+ __ bind(&resume);
+ context()->Plug(result_register());
+ break;
+ }
+
+ case Yield::FINAL: {
+ VisitForAccumulatorValue(expr->generator_object());
+ __ mov(r1, Operand(Smi::FromInt(JSGeneratorObject::kGeneratorClosed)));
+ __ str(r1, FieldMemOperand(result_register(),
+ JSGeneratorObject::kContinuationOffset));
+ // Pop value from top-of-stack slot, box result into result register.
+ EmitCreateIteratorResult(true);
+ EmitUnwindBeforeReturn();
+ EmitReturnSequence();
+ break;
+ }
+
+ case Yield::DELEGATING: {
+ VisitForStackValue(expr->generator_object());
+
+ // Initial stack layout is as follows:
+ // [sp + 1 * kPointerSize] iter
+ // [sp + 0 * kPointerSize] g
+
+ Label l_catch, l_try, l_suspend, l_continuation, l_resume;
+ Label l_next, l_call, l_loop;
+ // Initial send value is undefined.
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+ __ b(&l_next);
+
+ // catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; }
+ __ bind(&l_catch);
+ handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
+ __ LoadRoot(r2, Heap::kthrow_stringRootIndex); // "throw"
+ __ ldr(r3, MemOperand(sp, 1 * kPointerSize)); // iter
+ __ push(r3); // iter
+ __ push(r0); // exception
+ __ jmp(&l_call);
+
+ // try { received = %yield result }
+ // Shuffle the received result above a try handler and yield it without
+ // re-boxing.
+ __ bind(&l_try);
+ __ pop(r0); // result
+ __ PushTryHandler(StackHandler::CATCH, expr->index());
+ const int handler_size = StackHandlerConstants::kSize;
+ __ push(r0); // result
+ __ jmp(&l_suspend);
+ __ bind(&l_continuation);
+ __ jmp(&l_resume);
+ __ bind(&l_suspend);
+ const int generator_object_depth = kPointerSize + handler_size;
+ __ ldr(r0, MemOperand(sp, generator_object_depth));
+ __ push(r0); // g
+ ASSERT(l_continuation.pos() > 0 && Smi::IsValid(l_continuation.pos()));
+ __ mov(r1, Operand(Smi::FromInt(l_continuation.pos())));
+ __ str(r1, FieldMemOperand(r0, JSGeneratorObject::kContinuationOffset));
+ __ str(cp, FieldMemOperand(r0, JSGeneratorObject::kContextOffset));
+ __ mov(r1, cp);
+ __ RecordWriteField(r0, JSGeneratorObject::kContextOffset, r1, r2,
+ kLRHasBeenSaved, kDontSaveFPRegs);
+ __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ __ pop(r0); // result
+ EmitReturnSequence();
+ __ bind(&l_resume); // received in r0
+ __ PopTryHandler();
+
+ // receiver = iter; f = 'next'; arg = received;
+ __ bind(&l_next);
+ __ LoadRoot(r2, Heap::knext_stringRootIndex); // "next"
+ __ ldr(r3, MemOperand(sp, 1 * kPointerSize)); // iter
+ __ push(r3); // iter
+ __ push(r0); // received
+
+ // result = receiver[f](arg);
+ __ bind(&l_call);
+ Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(1);
+ CallIC(ic);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+
+ // if (!result.done) goto l_try;
+ __ bind(&l_loop);
+ __ push(r0); // save result
+ __ LoadRoot(r2, Heap::kdone_stringRootIndex); // "done"
+ Handle<Code> done_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(done_ic); // result.done in r0
+ Handle<Code> bool_ic = ToBooleanStub::GetUninitialized(isolate());
+ CallIC(bool_ic);
+ __ cmp(r0, Operand(0));
+ __ b(eq, &l_try);
+
+ // result.value
+ __ pop(r0); // result
+ __ LoadRoot(r2, Heap::kvalue_stringRootIndex); // "value"
+ Handle<Code> value_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(value_ic); // result.value in r0
+ context()->DropAndPlug(2, r0); // drop iter and g
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode) {
+ // The value stays in r0, and is ultimately read by the resumed generator, as
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. r1
+ // will hold the generator object until the activation has been resumed.
+ VisitForStackValue(generator);
+ VisitForAccumulatorValue(value);
+ __ pop(r1);
+
+ // Check generator state.
+ Label wrong_state, done;
+ __ ldr(r3, FieldMemOperand(r1, JSGeneratorObject::kContinuationOffset));
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ __ cmp(r3, Operand(Smi::FromInt(0)));
+ __ b(le, &wrong_state);
+
+ // Load suspended function and context.
+ __ ldr(cp, FieldMemOperand(r1, JSGeneratorObject::kContextOffset));
+ __ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset));
+
+ // Load receiver and store as the first argument.
+ __ ldr(r2, FieldMemOperand(r1, JSGeneratorObject::kReceiverOffset));
+ __ push(r2);
+
+ // Push holes for the rest of the arguments to the generator function.
+ __ ldr(r3, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r3,
+ FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ LoadRoot(r2, Heap::kTheHoleValueRootIndex);
+ Label push_argument_holes, push_frame;
+ __ bind(&push_argument_holes);
+ __ sub(r3, r3, Operand(Smi::FromInt(1)), SetCC);
+ __ b(mi, &push_frame);
+ __ push(r2);
+ __ jmp(&push_argument_holes);
+
+ // Enter a new JavaScript frame, and initialize its slots as they were when
+ // the generator was suspended.
+ Label resume_frame;
+ __ bind(&push_frame);
+ __ bl(&resume_frame);
+ __ jmp(&done);
+ __ bind(&resume_frame);
+ __ push(lr); // Return address.
+ __ push(fp); // Caller's frame pointer.
+ __ mov(fp, sp);
+ __ push(cp); // Callee's context.
+ __ push(r4); // Callee's JS Function.
+
+ // Load the operand stack size.
+ __ ldr(r3, FieldMemOperand(r1, JSGeneratorObject::kOperandStackOffset));
+ __ ldr(r3, FieldMemOperand(r3, FixedArray::kLengthOffset));
+ __ SmiUntag(r3);
+
+ // If we are sending a value and there is no operand stack, we can jump back
+ // in directly.
+ if (resume_mode == JSGeneratorObject::NEXT) {
+ Label slow_resume;
+ __ cmp(r3, Operand(0));
+ __ b(ne, &slow_resume);
+ __ ldr(r3, FieldMemOperand(r4, JSFunction::kCodeEntryOffset));
+ __ ldr(r2, FieldMemOperand(r1, JSGeneratorObject::kContinuationOffset));
+ __ SmiUntag(r2);
+ __ add(r3, r3, r2);
+ __ mov(r2, Operand(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting)));
+ __ str(r2, FieldMemOperand(r1, JSGeneratorObject::kContinuationOffset));
+ __ Jump(r3);
+ __ bind(&slow_resume);
+ }
+
+ // Otherwise, we push holes for the operand stack and call the runtime to fix
+ // up the stack and the handlers.
+ Label push_operand_holes, call_resume;
+ __ bind(&push_operand_holes);
+ __ sub(r3, r3, Operand(1), SetCC);
+ __ b(mi, &call_resume);
+ __ push(r2);
+ __ b(&push_operand_holes);
+ __ bind(&call_resume);
+ __ push(r1);
+ __ push(result_register());
+ __ Push(Smi::FromInt(resume_mode));
+ __ CallRuntime(Runtime::kResumeJSGeneratorObject, 3);
+ // Not reached: the runtime call returns elsewhere.
+ __ stop("not-reached");
+
+ // Throw error if we attempt to operate on a running generator.
+ __ bind(&wrong_state);
+ __ push(r1);
+ __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
+
+ __ bind(&done);
+ context()->Plug(result_register());
+}
+
+
+void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
+ Label gc_required;
+ Label allocated;
+
+ Handle<Map> map(isolate()->native_context()->generator_result_map());
+
+ __ Allocate(map->instance_size(), r0, r2, r3, &gc_required, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&gc_required);
+ __ Push(Smi::FromInt(map->instance_size()));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ ldr(context_register(),
+ MemOperand(fp, StandardFrameConstants::kContextOffset));
+
+ __ bind(&allocated);
+ __ mov(r1, Operand(map));
+ __ pop(r2);
+ __ mov(r3, Operand(isolate()->factory()->ToBoolean(done)));
+ __ mov(r4, Operand(isolate()->factory()->empty_fixed_array()));
+ ASSERT_EQ(map->instance_size(), 5 * kPointerSize);
+ __ str(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ str(r4, FieldMemOperand(r0, JSObject::kPropertiesOffset));
+ __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
+ __ str(r2,
+ FieldMemOperand(r0, JSGeneratorObject::kResultValuePropertyOffset));
+ __ str(r3,
+ FieldMemOperand(r0, JSGeneratorObject::kResultDonePropertyOffset));
+
+ // Only the value field needs a write barrier, as the other values are in the
+ // root set.
+ __ RecordWriteField(r0, JSGeneratorObject::kResultValuePropertyOffset,
+ r2, r3, kLRHasBeenSaved, kDontSaveFPRegs);
+}
+
+
+void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ Literal* key = prop->key()->AsLiteral();
+ __ mov(r2, Operand(key->value()));
+ // Call load IC. It has arguments receiver and property name r0 and r2.
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
+}
+
+
+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();
+ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
+}
+
+
+void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode,
+ Expression* left_expr,
+ Expression* right_expr) {
+ Label done, smi_case, stub_call;
+
+ Register scratch1 = r2;
+ Register scratch2 = r3;
+
+ // Get the arguments.
+ Register left = r1;
+ Register right = r0;
+ __ pop(left);
+
+ // Perform combined smi check on both operands.
+ __ orr(scratch1, left, Operand(right));
+ STATIC_ASSERT(kSmiTag == 0);
+ JumpPatchSite patch_site(masm_);
+ patch_site.EmitJumpIfSmi(scratch1, &smi_case);
+
+ __ bind(&stub_call);
+ BinaryOpStub stub(op, mode);
+ CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET,
+ expr->BinaryOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ __ jmp(&done);
+
+ __ bind(&smi_case);
+ // Smi case. This code works the same way as the smi-smi case in the type
+ // recording binary operation stub, see
+ // BinaryOpStub::GenerateSmiSmiOperation for comments.
+ switch (op) {
+ case Token::SAR:
+ __ GetLeastBitsFromSmi(scratch1, right, 5);
+ __ mov(right, Operand(left, ASR, scratch1));
+ __ bic(right, right, Operand(kSmiTagMask));
+ break;
+ case Token::SHL: {
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ mov(scratch1, Operand(scratch1, LSL, scratch2));
+ __ TrySmiTag(right, scratch1, &stub_call);
+ break;
+ }
+ case Token::SHR: {
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ mov(scratch1, Operand(scratch1, LSR, scratch2));
+ __ tst(scratch1, Operand(0xc0000000));
+ __ b(ne, &stub_call);
+ __ SmiTag(right, scratch1);
+ break;
+ }
+ case Token::ADD:
+ __ add(scratch1, left, Operand(right), SetCC);
+ __ b(vs, &stub_call);
+ __ mov(right, scratch1);
+ break;
+ case Token::SUB:
+ __ sub(scratch1, left, Operand(right), SetCC);
+ __ b(vs, &stub_call);
+ __ mov(right, scratch1);
+ break;
+ case Token::MUL: {
+ __ SmiUntag(ip, right);
+ __ smull(scratch1, scratch2, left, ip);
+ __ mov(ip, Operand(scratch1, ASR, 31));
+ __ cmp(ip, Operand(scratch2));
+ __ b(ne, &stub_call);
+ __ cmp(scratch1, Operand::Zero());
+ __ mov(right, Operand(scratch1), LeaveCC, ne);
+ __ b(ne, &done);
+ __ add(scratch2, right, Operand(left), SetCC);
+ __ mov(right, Operand(Smi::FromInt(0)), LeaveCC, pl);
+ __ b(mi, &stub_call);
+ break;
+ }
+ case Token::BIT_OR:
+ __ orr(right, left, Operand(right));
+ break;
+ case Token::BIT_AND:
+ __ and_(right, left, Operand(right));
+ break;
+ case Token::BIT_XOR:
+ __ eor(right, left, Operand(right));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ __ bind(&done);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode) {
+ __ pop(r1);
+ BinaryOpStub stub(op, mode);
+ JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code.
+ CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET,
+ expr->BinaryOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitAssignment(Expression* expr) {
+ // Invalid left-hand sides are rewritten by the parser to have a 'throw
+ // ReferenceError' on the left-hand side.
+ if (!expr->IsValidLeftHandSide()) {
+ VisitForEffect(expr);
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->AsProperty();
+ if (prop != NULL) {
+ assign_type = (prop->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ switch (assign_type) {
+ case VARIABLE: {
+ Variable* var = expr->AsVariableProxy()->var();
+ EffectContext context(this);
+ EmitVariableAssignment(var, Token::ASSIGN);
+ break;
+ }
+ case NAMED_PROPERTY: {
+ __ push(r0); // Preserve value.
+ VisitForAccumulatorValue(prop->obj());
+ __ mov(r1, r0);
+ __ pop(r0); // Restore value.
+ __ mov(r2, Operand(prop->key()->AsLiteral()->value()));
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic);
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ push(r0); // Preserve value.
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ __ mov(r1, r0);
+ __ pop(r2);
+ __ pop(r0); // Restore value.
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic);
+ break;
+ }
+ }
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitVariableAssignment(Variable* var,
+ Token::Value op) {
+ if (var->IsUnallocated()) {
+ // Global var, const, or let.
+ __ mov(r2, Operand(var->name()));
+ __ ldr(r1, GlobalObjectOperand());
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+
+ } else if (op == Token::INIT_CONST) {
+ // Const initializers need a write barrier.
+ ASSERT(!var->IsParameter()); // No const parameters.
+ if (var->IsStackLocal()) {
+ Label skip;
+ __ ldr(r1, StackOperand(var));
+ __ CompareRoot(r1, Heap::kTheHoleValueRootIndex);
+ __ b(ne, &skip);
+ __ str(result_register(), StackOperand(var));
+ __ bind(&skip);
+ } else {
+ ASSERT(var->IsContextSlot() || var->IsLookupSlot());
+ // Like var declarations, const declarations are hoisted to function
+ // scope. However, unlike var initializers, const initializers are
+ // able to drill a hole to that function context, even from inside a
+ // 'with' context. We thus bypass the normal static scope lookup for
+ // var->IsContextSlot().
+ __ push(r0);
+ __ mov(r0, Operand(var->name()));
+ __ Push(cp, r0); // Context and name.
+ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
+ }
+
+ } 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(language_mode())));
+ __ Push(cp, r1, r0); // Context, name, strict mode.
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ } else {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ Label assign;
+ MemOperand location = VarOperand(var, r1);
+ __ ldr(r3, location);
+ __ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
+ __ b(ne, &assign);
+ __ mov(r3, Operand(var->name()));
+ __ push(r3);
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ // Perform the assignment.
+ __ bind(&assign);
+ __ str(result_register(), location);
+ if (var->IsContextSlot()) {
+ // RecordWrite may destroy all its register arguments.
+ __ mov(r3, result_register());
+ int offset = Context::SlotOffset(var->index());
+ __ RecordWriteContextSlot(
+ r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs);
+ }
+ }
+
+ } 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 (generate_debug_code_ && op == Token::INIT_LET) {
+ // Check for an uninitialized let binding.
+ __ ldr(r2, location);
+ __ CompareRoot(r2, Heap::kTheHoleValueRootIndex);
+ __ Check(eq, kLetBindingReInitialization);
+ }
+ // Perform the assignment.
+ __ str(r0, location);
+ if (var->IsContextSlot()) {
+ __ mov(r3, r0);
+ 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(language_mode())));
+ __ Push(cp, r1, r0); // Context, name, strict mode.
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ }
+ }
+ // Non-initializing assignments to consts are ignored.
+}
+
+
+void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
+ // Assignment to a property, using a named store IC.
+ Property* prop = expr->target()->AsProperty();
+ ASSERT(prop != NULL);
+ ASSERT(prop->key()->AsLiteral() != NULL);
+
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ __ mov(r2, Operand(prop->key()->AsLiteral()->value()));
+ __ pop(r1);
+
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
+
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
+ // Assignment to a property, using a keyed store IC.
+
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ __ pop(r1); // Key.
+ __ pop(r2);
+
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
+
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::VisitProperty(Property* expr) {
+ Comment cmnt(masm_, "[ Property");
+ Expression* key = expr->key();
+
+ if (key->IsPropertyName()) {
+ VisitForAccumulatorValue(expr->obj());
+ EmitNamedPropertyLoad(expr);
+ PrepareForBailoutForId(expr->LoadId(), TOS_REG);
+ context()->Plug(r0);
+ } else {
+ VisitForStackValue(expr->obj());
+ VisitForAccumulatorValue(expr->key());
+ __ pop(r1);
+ EmitKeyedPropertyLoad(expr);
+ context()->Plug(r0);
+ }
+}
+
+
+void FullCodeGenerator::CallIC(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id) {
+ ic_total_count_++;
+ // All calls must have a predictable size in full-codegen code to ensure that
+ // the debugger can patch them correctly.
+ __ Call(code, rmode, ast_id, al, NEVER_INLINE_TARGET_ADDRESS);
+}
+
+void FullCodeGenerator::EmitCallWithIC(Call* expr,
+ Handle<Object> name,
+ RelocInfo::Mode mode) {
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ __ mov(r2, Operand(name));
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ CallIC(ic, mode, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
+ Expression* key) {
+ // Load the key.
+ VisitForAccumulatorValue(key);
+
+ // Swap the name of the function and the receiver on the stack to follow
+ // the calling convention for call ICs.
+ __ pop(r1);
+ __ push(r0);
+ __ push(r1);
+
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
+ __ ldr(r2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key.
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, r0); // Drop the key still on the stack.
+}
+
+
+void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
+ // Code common for calls using the call stub.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+
+ // Record call targets in unoptimized code.
+ flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<Cell> cell = isolate()->factory()->NewCell(uninitialized);
+ RecordTypeFeedbackCell(expr->CallFeedbackId(), cell);
+ __ mov(r2, Operand(cell));
+
+ CallFunctionStub stub(arg_count, flags);
+ __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ CallStub(&stub, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, r0);
+}
+
+
+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));
+ } else {
+ __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
+ }
+ __ push(r1);
+
+ // 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 language mode.
+ __ mov(r1, Operand(Smi::FromInt(language_mode())));
+ __ push(r1);
+
+ // 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);
+}
+
+
+void FullCodeGenerator::VisitCall(Call* expr) {
+#ifdef DEBUG
+ // We want to verify that RecordJSReturnSite gets called on all paths
+ // through this function. Avoid early returns.
+ expr->return_is_recorded_ = false;
+#endif
+
+ Comment cmnt(masm_, "[ Call");
+ Expression* callee = expr->expression();
+ VariableProxy* proxy = callee->AsVariableProxy();
+ Property* property = callee->AsProperty();
+
+ if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
+ // In a call to eval, we first call %ResolvePossiblyDirectEval to
+ // resolve the function we need to call and the receiver of the
+ // call. Then we call the resolved function using the given
+ // arguments.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+
+ { PreservePositionScope pos_scope(masm()->positions_recorder());
+ VisitForStackValue(callee);
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ push(r2); // Reserved receiver slot.
+
+ // Push the arguments.
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Push a copy of the function (found below the arguments) and
+ // resolve eval.
+ __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ push(r1);
+ 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.
+ __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ str(r1, MemOperand(sp, arg_count * kPointerSize));
+ }
+
+ // 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.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, r0);
+ } else if (proxy != NULL && proxy->var()->IsUnallocated()) {
+ // Push global object as receiver for the call IC.
+ __ ldr(r0, GlobalObjectOperand());
+ __ push(r0);
+ EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
+ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
+ // Call to a lookup slot (dynamically introduced variable).
+ Label slow, done;
+
+ { PreservePositionScope scope(masm()->positions_recorder());
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done);
+ }
+
+ __ bind(&slow);
+ // Call the runtime to find the function to call (returned in r0)
+ // and the object holding it (returned in edx).
+ __ push(context_register());
+ __ mov(r2, Operand(proxy->name()));
+ __ push(r2);
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ Push(r0, r1); // Function, receiver.
+
+ // If fast case code has been generated, emit code to push the
+ // function and receiver and have the slow path jump around this
+ // code.
+ if (done.is_linked()) {
+ Label call;
+ __ b(&call);
+ __ bind(&done);
+ // Push function.
+ __ push(r0);
+ // The receiver is implicitly the global receiver. Indicate this
+ // by passing the hole to the call function stub.
+ __ LoadRoot(r1, Heap::kTheHoleValueRootIndex);
+ __ push(r1);
+ __ bind(&call);
+ }
+
+ // The receiver is either the global receiver or an object found
+ // by LoadContextSlot. That object could be the hole if the
+ // receiver is implicitly the global object.
+ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
+ } else if (property != NULL) {
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(property->obj());
+ }
+ if (property->key()->IsPropertyName()) {
+ EmitCallWithIC(expr,
+ property->key()->AsLiteral()->value(),
+ RelocInfo::CODE_TARGET);
+ } else {
+ EmitKeyedCallWithIC(expr, property->key());
+ }
+ } else {
+ // Call to an arbitrary expression not handled specially above.
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(callee);
+ }
+ // Load global receiver object.
+ __ ldr(r1, GlobalObjectOperand());
+ __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
+ __ push(r1);
+ // Emit function call.
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ }
+
+#ifdef DEBUG
+ // RecordJSReturnSite should have been called.
+ ASSERT(expr->return_is_recorded_);
+#endif
+}
+
+
+void FullCodeGenerator::VisitCallNew(CallNew* expr) {
+ Comment cmnt(masm_, "[ CallNew");
+ // According to ECMA-262, section 11.2.2, page 44, the function
+ // expression in new calls must be evaluated before the
+ // arguments.
+
+ // Push constructor on the stack. If it's not a function it's used as
+ // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
+ // ignored.
+ VisitForStackValue(expr->expression());
+
+ // Push the arguments ("left-to-right") on the stack.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Call the construct call builtin that handles allocation and
+ // constructor invocation.
+ SetSourcePosition(expr->position());
+
+ // Load function and argument count into r1 and r0.
+ __ mov(r0, Operand(arg_count));
+ __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
+
+ // Record call targets in unoptimized code.
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<Cell> cell = isolate()->factory()->NewCell(uninitialized);
+ RecordTypeFeedbackCell(expr->CallNewFeedbackId(), cell);
+ __ mov(r2, Operand(cell));
+
+ CallConstructStub stub(RECORD_CALL_TARGET);
+ __ Call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL);
+ PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ SmiTst(r0);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ NonNegativeSmiTst(r0);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsObject(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(r0, if_false);
+ __ LoadRoot(ip, Heap::kNullValueRootIndex);
+ __ cmp(r0, ip);
+ __ b(eq, if_true);
+ __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
+ // Undetectable objects behave like undefined when tested with typeof.
+ __ ldrb(r1, FieldMemOperand(r2, Map::kBitFieldOffset));
+ __ tst(r1, Operand(1 << Map::kIsUndetectable));
+ __ b(ne, if_false);
+ __ ldrb(r1, FieldMemOperand(r2, Map::kInstanceTypeOffset));
+ __ cmp(r1, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ __ b(lt, if_false);
+ __ cmp(r1, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(le, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(r0, if_false);
+ __ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(ge, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(r0, if_false);
+ __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(r1, FieldMemOperand(r1, Map::kBitFieldOffset));
+ __ tst(r1, Operand(1 << Map::kIsUndetectable));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(ne, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
+ CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ AssertNotSmi(r0);
+
+ __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(ip, FieldMemOperand(r1, Map::kBitField2Offset));
+ __ tst(ip, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ b(ne, if_true);
+
+ // Check for fast case object. Generate false result for slow case object.
+ __ ldr(r2, FieldMemOperand(r0, JSObject::kPropertiesOffset));
+ __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(r2, ip);
+ __ b(eq, if_false);
+
+ // Look for valueOf name in the descriptor array, and indicate false if
+ // found. Since we omit an enumeration index check, if it is added via a
+ // transition that shares its descriptor array, this is a false positive.
+ Label entry, loop, done;
+
+ // Skip loop if no descriptors are valid.
+ __ NumberOfOwnDescriptors(r3, r1);
+ __ cmp(r3, Operand::Zero());
+ __ b(eq, &done);
+
+ __ LoadInstanceDescriptors(r1, r4);
+ // r4: descriptor array.
+ // r3: valid entries in the descriptor array.
+ __ mov(ip, Operand(DescriptorArray::kDescriptorSize));
+ __ mul(r3, r3, ip);
+ // Calculate location of the first key name.
+ __ add(r4, r4, Operand(DescriptorArray::kFirstOffset - kHeapObjectTag));
+ // Calculate the end of the descriptor array.
+ __ mov(r2, r4);
+ __ add(r2, r2, Operand::PointerOffsetFromSmiKey(r3));
+
+ // Loop through all the keys in the descriptor array. If one of these is the
+ // string "valueOf" the result is false.
+ // The use of ip to store the valueOf string assumes that it is not otherwise
+ // used in the loop below.
+ __ mov(ip, Operand(isolate()->factory()->value_of_string()));
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ ldr(r3, MemOperand(r4, 0));
+ __ cmp(r3, ip);
+ __ b(eq, if_false);
+ __ add(r4, r4, Operand(DescriptorArray::kDescriptorSize * kPointerSize));
+ __ bind(&entry);
+ __ cmp(r4, Operand(r2));
+ __ b(ne, &loop);
+
+ __ bind(&done);
+ // If a valueOf property is not found on the object check that its
+ // prototype is the un-modified String prototype. If not result is false.
+ __ ldr(r2, FieldMemOperand(r1, Map::kPrototypeOffset));
+ __ JumpIfSmi(r2, if_false);
+ __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
+ __ ldr(r3, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+ __ ldr(r3, FieldMemOperand(r3, GlobalObject::kNativeContextOffset));
+ __ ldr(r3, ContextOperand(r3, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
+ __ cmp(r2, r3);
+ __ b(ne, if_false);
+
+ // Set the bit in the map to indicate that it has been checked safe for
+ // default valueOf and set true result.
+ __ ldrb(r2, FieldMemOperand(r1, Map::kBitField2Offset));
+ __ orr(r2, r2, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ strb(r2, FieldMemOperand(r1, Map::kBitField2Offset));
+ __ jmp(if_true);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(r0, if_false);
+ __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(r0, if_false);
+ __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(r0, if_false);
+ __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+
+void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+
+ 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);
+
+ // Get the frame pointer for the calling frame.
+ __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+
+ // Skip the arguments adaptor frame if it exists.
+ Label check_frame_marker;
+ __ ldr(r1, MemOperand(r2, StandardFrameConstants::kContextOffset));
+ __ cmp(r1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ b(ne, &check_frame_marker);
+ __ ldr(r2, MemOperand(r2, StandardFrameConstants::kCallerFPOffset));
+
+ // Check the marker in the calling frame.
+ __ bind(&check_frame_marker);
+ __ ldr(r1, MemOperand(r2, StandardFrameConstants::kMarkerOffset));
+ __ cmp(r1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ // Load the two objects into registers and perform the comparison.
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ 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);
+
+ __ pop(r1);
+ __ cmp(r0, r1);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitArguments(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ // ArgumentsAccessStub expects the key in edx and the formal
+ // parameter count in r0.
+ VisitForAccumulatorValue(args->at(0));
+ __ mov(r1, r0);
+ __ mov(r0, Operand(Smi::FromInt(info_->scope()->num_parameters())));
+ ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
+ __ CallStub(&stub);
+ context()->Plug(r0);
+}
+
+
+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())));
+
+ // Check if the calling frame is an arguments adaptor frame.
+ __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
+ __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ b(ne, &exit);
+
+ // Arguments adaptor case: Read the arguments length from the
+ // adaptor frame.
+ __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ __ bind(&exit);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitClassOf(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ Label done, null, function, non_function_constructor;
+
+ VisitForAccumulatorValue(args->at(0));
+
+ // If the object is a smi, we return null.
+ __ JumpIfSmi(r0, &null);
+
+ // 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);
+ 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);
+
+ // r0 now contains the constructor function. Grab the
+ // instance class name from there.
+ __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r0, FieldMemOperand(r0, SharedFunctionInfo::kInstanceClassNameOffset));
+ __ b(&done);
+
+ // Functions have class 'Function'.
+ __ bind(&function);
+ __ LoadRoot(r0, Heap::kfunction_class_stringRootIndex);
+ __ jmp(&done);
+
+ // Objects with a non-function constructor have class 'Object'.
+ __ bind(&non_function_constructor);
+ __ LoadRoot(r0, Heap::kObject_stringRootIndex);
+ __ jmp(&done);
+
+ // Non-JS objects have class null.
+ __ bind(&null);
+ __ LoadRoot(r0, Heap::kNullValueRootIndex);
+
+ // All done.
+ __ bind(&done);
+
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitLog(CallRuntime* expr) {
+ // Conditionally generate a log call.
+ // Args:
+ // 0 (literal string): The type of logging (corresponds to the flags).
+ // This is used to determine whether or not to generate the log call.
+ // 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);
+ }
+
+ // Finally, we're expected to leave a value on the top of the stack.
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+ Label slow_allocate_heapnumber;
+ Label heapnumber_allocated;
+
+ __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r4, r1, r2, r6, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ // Allocate a heap number.
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ mov(r4, Operand(r0));
+
+ __ bind(&heapnumber_allocated);
+
+ // Convert 32 random bits in r0 to 0.(32 random bits) in a double
+ // by computing:
+ // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
+ __ PrepareCallCFunction(1, r0);
+ __ ldr(r0,
+ ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX));
+ __ ldr(r0, FieldMemOperand(r0, GlobalObject::kNativeContextOffset));
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
+
+ // 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::Zero());
+ __ vmov(d8, r0, r1);
+ // Subtract and store the result in the heap number.
+ __ vsub(d7, d7, d8);
+ __ sub(r0, r4, Operand(kHeapObjectTag));
+ __ vstr(d7, r0, HeapNumber::kValueOffset);
+ __ mov(r0, r4);
+
+ context()->Plug(r0);
+}
+
+
+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);
+ context()->Plug(r0);
+}
+
+
+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);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitValueOf(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label done;
+ // If the object is a smi return the object.
+ __ JumpIfSmi(r0, &done);
+ // If the object is not a value type, return the object.
+ __ CompareObjectType(r0, r1, r1, JS_VALUE_TYPE);
+ __ b(ne, &done);
+ __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset));
+
+ __ bind(&done);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ ASSERT_NE(NULL, args->at(1)->AsLiteral());
+ Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->value()));
+
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label runtime, done, not_date_object;
+ Register object = r0;
+ Register result = r0;
+ Register scratch0 = r9;
+ Register scratch1 = r1;
+
+ __ JumpIfSmi(object, &not_date_object);
+ __ CompareObjectType(object, scratch1, scratch1, JS_DATE_TYPE);
+ __ b(ne, &not_date_object);
+
+ if (index->value() == 0) {
+ __ ldr(result, FieldMemOperand(object, JSDate::kValueOffset));
+ __ jmp(&done);
+ } else {
+ if (index->value() < JSDate::kFirstUncachedField) {
+ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+ __ mov(scratch1, Operand(stamp));
+ __ ldr(scratch1, MemOperand(scratch1));
+ __ ldr(scratch0, FieldMemOperand(object, JSDate::kCacheStampOffset));
+ __ cmp(scratch1, scratch0);
+ __ b(ne, &runtime);
+ __ ldr(result, FieldMemOperand(object, JSDate::kValueOffset +
+ kPointerSize * index->value()));
+ __ jmp(&done);
+ }
+ __ bind(&runtime);
+ __ PrepareCallCFunction(2, scratch1);
+ __ mov(r1, Operand(index));
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ jmp(&done);
+ }
+
+ __ bind(&not_date_object);
+ __ CallRuntime(Runtime::kThrowNotDateError, 0);
+ __ bind(&done);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitSeqStringSetCharCheck(Register string,
+ Register index,
+ Register value,
+ uint32_t encoding_mask) {
+ __ SmiTst(index);
+ __ Check(eq, kNonSmiIndex);
+ __ SmiTst(value);
+ __ Check(eq, kNonSmiValue);
+
+ __ ldr(ip, FieldMemOperand(string, String::kLengthOffset));
+ __ cmp(index, ip);
+ __ Check(lt, kIndexIsTooLarge);
+
+ __ cmp(index, Operand(Smi::FromInt(0)));
+ __ Check(ge, kIndexIsNegative);
+
+ __ ldr(ip, FieldMemOperand(string, HeapObject::kMapOffset));
+ __ ldrb(ip, FieldMemOperand(ip, Map::kInstanceTypeOffset));
+
+ __ and_(ip, ip, Operand(kStringRepresentationMask | kStringEncodingMask));
+ __ cmp(ip, Operand(encoding_mask));
+ __ Check(eq, kUnexpectedStringType);
+}
+
+
+void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(3, args->length());
+
+ Register string = r0;
+ Register index = r1;
+ Register value = r2;
+
+ VisitForStackValue(args->at(1)); // index
+ VisitForStackValue(args->at(2)); // value
+ __ pop(value);
+ __ pop(index);
+ VisitForAccumulatorValue(args->at(0)); // string
+
+ if (FLAG_debug_code) {
+ static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
+ EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
+ }
+
+ __ SmiUntag(value, value);
+ __ add(ip,
+ string,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ strb(value, MemOperand(ip, index, LSR, kSmiTagSize));
+ context()->Plug(string);
+}
+
+
+void FullCodeGenerator::EmitTwoByteSeqStringSetChar(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(3, args->length());
+
+ Register string = r0;
+ Register index = r1;
+ Register value = r2;
+
+ VisitForStackValue(args->at(1)); // index
+ VisitForStackValue(args->at(2)); // value
+ __ pop(value);
+ __ pop(index);
+ VisitForAccumulatorValue(args->at(0)); // string
+
+ if (FLAG_debug_code) {
+ static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
+ EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
+ }
+
+ __ SmiUntag(value, value);
+ __ add(ip,
+ string,
+ Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
+ __ strh(value, MemOperand(ip, index));
+ context()->Plug(string);
+}
+
+
+
+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::ON_STACK);
+ __ CallStub(&stub);
+ context()->Plug(r0);
+}
+
+
+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.
+
+ Label done;
+ // If the object is a smi, return the value.
+ __ JumpIfSmi(r1, &done);
+
+ // If the object is not a value type, return the value.
+ __ CompareObjectType(r1, r2, r2, JS_VALUE_TYPE);
+ __ b(ne, &done);
+
+ // Store the value.
+ __ 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.
+ __ mov(r2, r0);
+ __ RecordWriteField(
+ r1, JSValue::kValueOffset, r2, r3, kLRHasBeenSaved, kDontSaveFPRegs);
+
+ __ bind(&done);
+ context()->Plug(r0);
+}
+
+
+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));
+
+ NumberToStringStub stub;
+ __ CallStub(&stub);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ VisitForAccumulatorValue(args->at(0));
+
+ Label done;
+ StringCharFromCodeGenerator generator(r0, r1);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(r1);
+}
+
+
+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 result = r3;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharCodeAtGenerator generator(object,
+ index,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ LoadRoot(result, Heap::kNanValueRootIndex);
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Load the undefined value into the result register, which will
+ // trigger conversion.
+ __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+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 scratch = r3;
+ Register result = r0;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharAtGenerator generator(object,
+ index,
+ scratch,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // the empty string.
+ __ LoadRoot(result, Heap::kempty_stringRootIndex);
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ mov(result, Operand(Smi::FromInt(0)));
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(STRING_ADD_CHECK_BOTH);
+ __ CallStub(&stub);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringCompareStub stub;
+ __ CallStub(&stub);
+ context()->Plug(r0);
+}
+
+
+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);
+ context()->Plug(r0);
+}
+
+
+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);
+ context()->Plug(r0);
+}
+
+
+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);
+ context()->Plug(r0);
+}
+
+
+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);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() >= 2);
+
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ VisitForAccumulatorValue(args->last()); // Function.
+
+ Label runtime, done;
+ // Check for non-function argument (including proxy).
+ __ JumpIfSmi(r0, &runtime);
+ __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
+ __ b(ne, &runtime);
+
+ // 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(&runtime);
+ __ push(r0);
+ __ CallRuntime(Runtime::kCall, args->length());
+ __ bind(&done);
+
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) {
+ 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);
+ context()->Plug(r0);
+}
+
+
+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()->value()))->value();
+
+ Handle<FixedArray> jsfunction_result_caches(
+ isolate()->native_context()->jsfunction_result_caches());
+ if (jsfunction_result_caches->length() <= cache_id) {
+ __ Abort(kAttemptToUseUndefinedCache);
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+ context()->Plug(r0);
+ return;
+ }
+
+ VisitForAccumulatorValue(args->at(1));
+
+ Register key = r0;
+ Register cache = r1;
+ __ ldr(cache, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+ __ ldr(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset));
+ __ ldr(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+ __ ldr(cache,
+ FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
+
+
+ Label done, not_found;
+ // tmp now holds finger offset as a smi.
+ __ ldr(r2, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset));
+ // r2 now holds finger offset as a smi.
+ __ add(r3, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // r3 now points to the start of fixed array elements.
+ __ ldr(r2, MemOperand::PointerAddressFromSmiKey(r3, r2, PreIndex));
+ // Note side effect of PreIndex: r3 now points to the key of the pair.
+ __ cmp(key, r2);
+ __ b(ne, &not_found);
+
+ __ ldr(r0, MemOperand(r3, kPointerSize));
+ __ b(&done);
+
+ __ bind(&not_found);
+ // Call runtime to perform the lookup.
+ __ Push(cache, key);
+ __ CallRuntime(Runtime::kGetFromCache, 2);
+
+ __ bind(&done);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ Register right = r0;
+ Register left = r1;
+ Register tmp = r2;
+ Register tmp2 = r3;
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+ __ pop(left);
+
+ Label done, fail, ok;
+ __ cmp(left, Operand(right));
+ __ b(eq, &ok);
+ // Fail if either is a non-HeapObject.
+ __ and_(tmp, left, Operand(right));
+ __ JumpIfSmi(tmp, &fail);
+ __ ldr(tmp, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ ldrb(tmp2, FieldMemOperand(tmp, Map::kInstanceTypeOffset));
+ __ cmp(tmp2, Operand(JS_REGEXP_TYPE));
+ __ b(ne, &fail);
+ __ ldr(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ cmp(tmp, Operand(tmp2));
+ __ b(ne, &fail);
+ __ ldr(tmp, FieldMemOperand(left, JSRegExp::kDataOffset));
+ __ ldr(tmp2, FieldMemOperand(right, JSRegExp::kDataOffset));
+ __ cmp(tmp, tmp2);
+ __ b(eq, &ok);
+ __ bind(&fail);
+ __ LoadRoot(r0, Heap::kFalseValueRootIndex);
+ __ jmp(&done);
+ __ bind(&ok);
+ __ LoadRoot(r0, Heap::kTrueValueRootIndex);
+ __ bind(&done);
+
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset));
+ __ tst(r0, Operand(String::kContainsCachedArrayIndexMask));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ VisitForAccumulatorValue(args->at(0));
+
+ __ AssertString(r0);
+
+ __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset));
+ __ IndexFromHash(r0, r0);
+
+ context()->Plug(r0);
+}
+
+
+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));
+
+ // All aliases of the same register have disjoint lifetimes.
+ Register array = r0;
+ Register elements = no_reg; // Will be r0.
+ Register result = no_reg; // Will be r0.
+ Register separator = r1;
+ Register array_length = r2;
+ Register result_pos = no_reg; // Will be r2
+ Register string_length = r3;
+ Register string = r4;
+ Register element = r5;
+ Register elements_end = r6;
+ Register scratch1 = r7;
+ Register scratch2 = r9;
+
+ // Separator operand is on the stack.
+ __ pop(separator);
+
+ // Check that the array is a JSArray.
+ __ JumpIfSmi(array, &bailout);
+ __ CompareObjectType(array, scratch1, scratch2, JS_ARRAY_TYPE);
+ __ b(ne, &bailout);
+
+ // Check that the array has fast elements.
+ __ CheckFastElements(scratch1, scratch2, &bailout);
+
+ // If the array has length zero, return the empty string.
+ __ ldr(array_length, FieldMemOperand(array, JSArray::kLengthOffset));
+ __ SmiUntag(array_length, SetCC);
+ __ b(ne, &non_trivial_array);
+ __ LoadRoot(r0, Heap::kempty_stringRootIndex);
+ __ b(&done);
+
+ __ bind(&non_trivial_array);
+
+ // Get the FixedArray containing array's elements.
+ elements = array;
+ __ ldr(elements, FieldMemOperand(array, JSArray::kElementsOffset));
+ array = no_reg; // End of array's live range.
+
+ // Check that all array elements are sequential ASCII strings, and
+ // accumulate the sum of their lengths, as a smi-encoded value.
+ __ mov(string_length, Operand::Zero());
+ __ add(element,
+ elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2));
+ // Loop condition: while (element < elements_end).
+ // Live values in registers:
+ // elements: Fixed array of strings.
+ // array_length: Length of the fixed array of strings (not smi)
+ // separator: Separator string
+ // string_length: Accumulated sum of string lengths (smi).
+ // element: Current array element.
+ // elements_end: Array end.
+ if (generate_debug_code_) {
+ __ cmp(array_length, Operand::Zero());
+ __ Assert(gt, kNoEmptyArraysHereInEmitFastAsciiArrayJoin);
+ }
+ __ bind(&loop);
+ __ ldr(string, MemOperand(element, kPointerSize, PostIndex));
+ __ JumpIfSmi(string, &bailout);
+ __ ldr(scratch1, FieldMemOperand(string, HeapObject::kMapOffset));
+ __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout);
+ __ ldr(scratch1, FieldMemOperand(string, SeqOneByteString::kLengthOffset));
+ __ add(string_length, string_length, Operand(scratch1), SetCC);
+ __ b(vs, &bailout);
+ __ cmp(element, elements_end);
+ __ b(lt, &loop);
+
+ // If array_length is 1, return elements[0], a string.
+ __ cmp(array_length, Operand(1));
+ __ b(ne, &not_size_one_array);
+ __ ldr(r0, FieldMemOperand(elements, FixedArray::kHeaderSize));
+ __ b(&done);
+
+ __ bind(&not_size_one_array);
+
+ // Live values in registers:
+ // separator: Separator string
+ // array_length: Length of the array.
+ // string_length: Sum of string lengths (smi).
+ // elements: FixedArray of strings.
+
+ // Check that the separator is a flat ASCII string.
+ __ JumpIfSmi(separator, &bailout);
+ __ ldr(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset));
+ __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout);
+
+ // Add (separator length times array_length) - separator length to the
+ // string_length to get the length of the result string. array_length is not
+ // smi but the other values are, so the result is a smi
+ __ ldr(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
+ __ sub(string_length, string_length, Operand(scratch1));
+ __ smull(scratch2, ip, array_length, scratch1);
+ // Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
+ // zero.
+ __ cmp(ip, Operand::Zero());
+ __ b(ne, &bailout);
+ __ tst(scratch2, Operand(0x80000000));
+ __ b(ne, &bailout);
+ __ add(string_length, string_length, Operand(scratch2), SetCC);
+ __ b(vs, &bailout);
+ __ SmiUntag(string_length);
+
+ // Get first element in the array to free up the elements register to be used
+ // for the result.
+ __ add(element,
+ elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ result = elements; // End of live range for elements.
+ elements = no_reg;
+ // Live values in registers:
+ // element: First array element
+ // separator: Separator string
+ // string_length: Length of result string (not smi)
+ // array_length: Length of the array.
+ __ AllocateAsciiString(result,
+ string_length,
+ scratch1,
+ scratch2,
+ elements_end,
+ &bailout);
+ // Prepare for looping. Set up elements_end to end of the array. Set
+ // result_pos to the position of the result where to write the first
+ // character.
+ __ add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2));
+ result_pos = array_length; // End of live range for array_length.
+ array_length = no_reg;
+ __ add(result_pos,
+ result,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+
+ // Check the length of the separator.
+ __ ldr(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
+ __ cmp(scratch1, Operand(Smi::FromInt(1)));
+ __ b(eq, &one_char_separator);
+ __ b(gt, &long_separator);
+
+ // Empty separator case
+ __ bind(&empty_separator_loop);
+ // Live values in registers:
+ // result_pos: the position to which we are currently copying characters.
+ // element: Current array element.
+ // elements_end: Array end.
+
+ // Copy next array element to the result.
+ __ ldr(string, MemOperand(element, kPointerSize, PostIndex));
+ __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ add(string,
+ string,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ __ cmp(element, elements_end);
+ __ b(lt, &empty_separator_loop); // End while (element < elements_end).
+ ASSERT(result.is(r0));
+ __ b(&done);
+
+ // One-character separator case
+ __ bind(&one_char_separator);
+ // Replace separator with its ASCII character value.
+ __ ldrb(separator, FieldMemOperand(separator, SeqOneByteString::kHeaderSize));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&one_char_separator_loop_entry);
+
+ __ bind(&one_char_separator_loop);
+ // Live values in registers:
+ // 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).
+
+ // Copy the separator character to the result.
+ __ strb(separator, MemOperand(result_pos, 1, PostIndex));
+
+ // Copy next array element to the result.
+ __ bind(&one_char_separator_loop_entry);
+ __ ldr(string, MemOperand(element, kPointerSize, PostIndex));
+ __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ add(string,
+ string,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ __ cmp(element, elements_end);
+ __ b(lt, &one_char_separator_loop); // End while (element < elements_end).
+ ASSERT(result.is(r0));
+ __ b(&done);
+
+ // Long separator case (separator is more than one character). Entry is at the
+ // label long_separator below.
+ __ bind(&long_separator_loop);
+ // Live values in registers:
+ // result_pos: the position to which we are currently copying characters.
+ // element: Current array element.
+ // elements_end: Array end.
+ // separator: Separator string.
+
+ // Copy the separator to the result.
+ __ ldr(string_length, FieldMemOperand(separator, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ add(string,
+ separator,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+
+ __ bind(&long_separator);
+ __ ldr(string, MemOperand(element, kPointerSize, PostIndex));
+ __ ldr(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ add(string,
+ string,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ __ cmp(element, elements_end);
+ __ b(lt, &long_separator_loop); // End while (element < elements_end).
+ ASSERT(result.is(r0));
+ __ b(&done);
+
+ __ bind(&bailout);
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+ __ bind(&done);
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
+ Handle<String> name = expr->name();
+ if (name->length() > 0 && name->Get(0) == '_') {
+ Comment cmnt(masm_, "[ InlineRuntimeCall");
+ EmitInlineRuntimeCall(expr);
+ return;
+ }
+
+ Comment cmnt(masm_, "[ CallRuntime");
+ ZoneList<Expression*>* args = expr->arguments();
+
+ if (expr->is_jsruntime()) {
+ // Prepare for calling JS runtime function.
+ __ ldr(r0, GlobalObjectOperand());
+ __ ldr(r0, FieldMemOperand(r0, GlobalObject::kBuiltinsOffset));
+ __ push(r0);
+ }
+
+ // Push the arguments ("left-to-right").
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ if (expr->is_jsruntime()) {
+ // Call the JS runtime function.
+ __ mov(r2, Operand(expr->name()));
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ CallIC(ic, mode, expr->CallRuntimeFeedbackId());
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ } else {
+ // Call the C runtime function.
+ __ CallRuntime(expr->function(), arg_count);
+ }
+ context()->Plug(r0);
+}
+
+
+void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
+ switch (expr->op()) {
+ case Token::DELETE: {
+ Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
+ Property* property = expr->expression()->AsProperty();
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+
+ if (property != NULL) {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ 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);
+ } else if (proxy != NULL) {
+ Variable* var = proxy->var();
+ // Delete of an unqualified identifier is disallowed in strict mode
+ // but "delete this" is allowed.
+ ASSERT(language_mode() == CLASSIC_MODE || var->is_this());
+ if (var->IsUnallocated()) {
+ __ ldr(r2, GlobalObjectOperand());
+ __ mov(r1, Operand(var->name()));
+ __ mov(r0, Operand(Smi::FromInt(kNonStrictMode)));
+ __ Push(r2, r1, r0);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
+ context()->Plug(r0);
+ } else if (var->IsStackAllocated() || var->IsContextSlot()) {
+ // Result of deleting non-global, non-dynamic variables is false.
+ // The subexpression does not have side effects.
+ context()->Plug(var->is_this());
+ } else {
+ // Non-global variable. Call the runtime to try to delete from the
+ // context where the variable was introduced.
+ __ push(context_register());
+ __ mov(r2, Operand(var->name()));
+ __ push(r2);
+ __ CallRuntime(Runtime::kDeleteContextSlot, 2);
+ context()->Plug(r0);
+ }
+ } else {
+ // Result of deleting non-property, non-variable reference is true.
+ // The subexpression may have side effects.
+ VisitForEffect(expr->expression());
+ context()->Plug(true);
+ }
+ break;
+ }
+
+ case Token::VOID: {
+ Comment cmnt(masm_, "[ UnaryOperation (VOID)");
+ VisitForEffect(expr->expression());
+ context()->Plug(Heap::kUndefinedValueRootIndex);
+ break;
+ }
+
+ case Token::NOT: {
+ Comment cmnt(masm_, "[ UnaryOperation (NOT)");
+ if (context()->IsEffect()) {
+ // 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 {
+ // 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;
+ }
+
+ case Token::TYPEOF: {
+ Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
+ { StackValueContext context(this);
+ VisitForTypeofValue(expr->expression());
+ }
+ __ CallRuntime(Runtime::kTypeof, 1);
+ context()->Plug(r0);
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
+ Comment cmnt(masm_, "[ CountOperation");
+ SetSourcePosition(expr->position());
+
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // as the left-hand side.
+ if (!expr->expression()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->expression());
+ return;
+ }
+
+ // Expression can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->expression()->AsProperty();
+ // In case of a property we use the uninitialized expression context
+ // of the key to detect a named property.
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate expression and get value.
+ if (assign_type == VARIABLE) {
+ ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
+ AccumulatorValueContext context(this);
+ EmitVariableLoad(expr->expression()->AsVariableProxy());
+ } else {
+ // Reserve space for result of postfix operation.
+ if (expr->is_postfix() && !context()->IsEffect()) {
+ __ mov(ip, Operand(Smi::FromInt(0)));
+ __ push(ip);
+ }
+ if (assign_type == NAMED_PROPERTY) {
+ // Put the object both on the stack and in the accumulator.
+ VisitForAccumulatorValue(prop->obj());
+ __ push(r0);
+ EmitNamedPropertyLoad(prop);
+ } else {
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ __ ldr(r1, MemOperand(sp, 0));
+ __ push(r0);
+ EmitKeyedPropertyLoad(prop);
+ }
+ }
+
+ // We need a second deoptimization point after loading the value
+ // in case evaluating the property load my have a side effect.
+ if (assign_type == VARIABLE) {
+ PrepareForBailout(expr->expression(), TOS_REG);
+ } else {
+ PrepareForBailoutForId(prop->LoadId(), TOS_REG);
+ }
+
+ // Call ToNumber only if operand is not a smi.
+ Label no_conversion;
+ if (ShouldInlineSmiCase(expr->op())) {
+ __ JumpIfSmi(r0, &no_conversion);
+ }
+ ToNumberStub convert_stub;
+ __ CallStub(&convert_stub);
+ __ bind(&no_conversion);
+
+ // Save result for postfix expressions.
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ // Save the result on the stack. If we have a named or keyed property
+ // we store the result under the receiver that is currently on top
+ // of the stack.
+ switch (assign_type) {
+ case VARIABLE:
+ __ push(r0);
+ break;
+ case NAMED_PROPERTY:
+ __ str(r0, MemOperand(sp, kPointerSize));
+ break;
+ case KEYED_PROPERTY:
+ __ str(r0, MemOperand(sp, 2 * kPointerSize));
+ break;
+ }
+ }
+ }
+
+
+ // Inline smi case if we are in a loop.
+ Label stub_call, done;
+ JumpPatchSite patch_site(masm_);
+
+ int count_value = expr->op() == Token::INC ? 1 : -1;
+ if (ShouldInlineSmiCase(expr->op())) {
+ __ add(r0, r0, Operand(Smi::FromInt(count_value)), SetCC);
+ __ b(vs, &stub_call);
+ // We could eliminate this smi check if we split the code at
+ // the first smi check before calling ToNumber.
+ patch_site.EmitJumpIfSmi(r0, &done);
+
+ __ bind(&stub_call);
+ // Call stub. Undo operation first.
+ __ sub(r0, r0, Operand(Smi::FromInt(count_value)));
+ }
+ __ mov(r1, r0);
+ __ mov(r0, Operand(Smi::FromInt(count_value)));
+
+ // Record position before stub call.
+ SetSourcePosition(expr->position());
+
+ BinaryOpStub stub(Token::ADD, NO_OVERWRITE);
+ CallIC(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ expr->CountBinOpFeedbackId());
+ patch_site.EmitPatchInfo();
+ __ bind(&done);
+
+ // Store the value returned in r0.
+ switch (assign_type) {
+ case VARIABLE:
+ if (expr->is_postfix()) {
+ { EffectContext context(this);
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context.Plug(r0);
+ }
+ // For all contexts except EffectConstant We have the result on
+ // top of the stack.
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(r0);
+ }
+ break;
+ case NAMED_PROPERTY: {
+ __ mov(r2, Operand(prop->key()->AsLiteral()->value()));
+ __ pop(r1);
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(r0);
+ }
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ pop(r1); // Key.
+ __ pop(r2); // Receiver.
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(r0);
+ }
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
+ ASSERT(!context()->IsEffect());
+ ASSERT(!context()->IsTest());
+ VariableProxy* proxy = expr->AsVariableProxy();
+ if (proxy != NULL && proxy->var()->IsUnallocated()) {
+ Comment cmnt(masm_, "Global variable");
+ __ ldr(r0, GlobalObjectOperand());
+ __ mov(r2, Operand(proxy->name()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ // Use a regular load, not a contextual load, to avoid a reference
+ // error.
+ CallIC(ic);
+ PrepareForBailout(expr, TOS_REG);
+ context()->Plug(r0);
+ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
+ Label done, slow;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done);
+
+ __ bind(&slow);
+ __ mov(r0, Operand(proxy->name()));
+ __ Push(cp, r0);
+ __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ PrepareForBailout(expr, TOS_REG);
+ __ bind(&done);
+
+ context()->Plug(r0);
+ } else {
+ // This expression cannot throw a reference error at the top level.
+ VisitInDuplicateContext(expr);
+ }
+}
+
+
+void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
+ 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(sub_expr);
+ }
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+
+ if (check->Equals(isolate()->heap()->number_string())) {
+ __ JumpIfSmi(r0, if_true);
+ __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
+ __ cmp(r0, ip);
+ Split(eq, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->string_string())) {
+ __ JumpIfSmi(r0, if_false);
+ // Check for undetectable objects => false.
+ __ CompareObjectType(r0, r0, r1, FIRST_NONSTRING_TYPE);
+ __ b(ge, if_false);
+ __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset));
+ __ tst(r1, Operand(1 << Map::kIsUndetectable));
+ Split(eq, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->symbol_string())) {
+ __ JumpIfSmi(r0, if_false);
+ __ CompareObjectType(r0, r0, r1, SYMBOL_TYPE);
+ Split(eq, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->boolean_string())) {
+ __ CompareRoot(r0, Heap::kTrueValueRootIndex);
+ __ b(eq, if_true);
+ __ CompareRoot(r0, Heap::kFalseValueRootIndex);
+ Split(eq, if_true, if_false, fall_through);
+ } else if (FLAG_harmony_typeof &&
+ check->Equals(isolate()->heap()->null_string())) {
+ __ CompareRoot(r0, Heap::kNullValueRootIndex);
+ Split(eq, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->undefined_string())) {
+ __ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
+ __ b(eq, if_true);
+ __ JumpIfSmi(r0, if_false);
+ // Check for undetectable objects => true.
+ __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset));
+ __ tst(r1, Operand(1 << Map::kIsUndetectable));
+ Split(ne, if_true, if_false, fall_through);
+
+ } else if (check->Equals(isolate()->heap()->function_string())) {
+ __ JumpIfSmi(r0, if_false);
+ 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_string())) {
+ __ JumpIfSmi(r0, if_false);
+ if (!FLAG_harmony_typeof) {
+ __ CompareRoot(r0, Heap::kNullValueRootIndex);
+ __ b(eq, if_true);
+ }
+ // Check for JS objects => true.
+ __ CompareObjectType(r0, r0, r1, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ b(lt, if_false);
+ __ CompareInstanceType(r0, r1, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ b(gt, if_false);
+ // Check for undetectable objects => false.
+ __ ldrb(r1, FieldMemOperand(r0, Map::kBitFieldOffset));
+ __ tst(r1, Operand(1 << Map::kIsUndetectable));
+ Split(eq, if_true, if_false, fall_through);
+ } else {
+ if (if_false != fall_through) __ jmp(if_false);
+ }
+ context()->Plug(if_true, if_false);
+}
+
+
+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;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ Token::Value op = expr->op();
+ VisitForStackValue(expr->left());
+ switch (op) {
+ case Token::IN:
+ VisitForStackValue(expr->right());
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+ __ cmp(r0, ip);
+ Split(eq, if_true, if_false, fall_through);
+ break;
+
+ case Token::INSTANCEOF: {
+ VisitForStackValue(expr->right());
+ InstanceofStub stub(InstanceofStub::kNoFlags);
+ __ CallStub(&stub);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ // The stub returns 0 for true.
+ __ tst(r0, r0);
+ Split(eq, if_true, if_false, fall_through);
+ break;
+ }
+
+ default: {
+ VisitForAccumulatorValue(expr->right());
+ Condition cond = CompareIC::ComputeCondition(op);
+ __ pop(r1);
+
+ bool inline_smi_code = ShouldInlineSmiCase(op);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ orr(r2, r0, Operand(r1));
+ patch_site.EmitJumpIfNotSmi(r2, &slow_case);
+ __ cmp(r1, r0);
+ Split(cond, if_true, if_false, NULL);
+ __ bind(&slow_case);
+ }
+
+ // Record position and call the compare IC.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ cmp(r0, Operand::Zero());
+ Split(cond, if_true, if_false, fall_through);
+ }
+ }
+
+ // Convert the result of the comparison into one expected for this
+ // expression's context.
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr,
+ Expression* sub_expr,
+ NilValue nil) {
+ 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);
+
+ VisitForAccumulatorValue(sub_expr);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ if (expr->op() == Token::EQ_STRICT) {
+ Heap::RootListIndex nil_value = nil == kNullValue ?
+ Heap::kNullValueRootIndex :
+ Heap::kUndefinedValueRootIndex;
+ __ LoadRoot(r1, nil_value);
+ __ cmp(r0, r1);
+ Split(eq, if_true, if_false, fall_through);
+ } else {
+ Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(), nil);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ __ cmp(r0, Operand(0));
+ Split(ne, if_true, if_false, fall_through);
+ }
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
+ __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ context()->Plug(r0);
+}
+
+
+Register FullCodeGenerator::result_register() {
+ return r0;
+}
+
+
+Register FullCodeGenerator::context_register() {
+ return cp;
+}
+
+
+void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
+ ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset);
+ __ str(value, MemOperand(fp, frame_offset));
+}
+
+
+void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
+ __ ldr(dst, ContextOperand(cp, context_index));
+}
+
+
+void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
+ Scope* declaration_scope = scope()->DeclarationScope();
+ if (declaration_scope->is_global_scope() ||
+ declaration_scope->is_module_scope()) {
+ // Contexts nested in the native context have a canonical empty function
+ // as their closure, not the anonymous closure containing the global
+ // code. Pass a smi sentinel and let the runtime look up the empty
+ // function.
+ __ mov(ip, Operand(Smi::FromInt(0)));
+ } else if (declaration_scope->is_eval_scope()) {
+ // Contexts created by a call to eval have the same closure as the
+ // context calling eval, not the anonymous closure containing the eval
+ // code. Fetch it from the context.
+ __ ldr(ip, ContextOperand(cp, Context::CLOSURE_INDEX));
+ } else {
+ ASSERT(declaration_scope->is_function_scope());
+ __ ldr(ip, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ }
+ __ push(ip);
+}
+
+
+// ----------------------------------------------------------------------------
+// Non-local control flow support.
+
+void FullCodeGenerator::EnterFinallyBlock() {
+ ASSERT(!result_register().is(r1));
+ // Store result register while executing finally block.
+ __ push(result_register());
+ // Cook return address in link register to stack (smi encoded Code* delta)
+ __ sub(r1, lr, Operand(masm_->CodeObject()));
+ __ SmiTag(r1);
+
+ // Store result register while executing finally block.
+ __ push(r1);
+
+ // Store pending message while executing finally block.
+ ExternalReference pending_message_obj =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ __ mov(ip, Operand(pending_message_obj));
+ __ ldr(r1, MemOperand(ip));
+ __ push(r1);
+
+ ExternalReference has_pending_message =
+ ExternalReference::address_of_has_pending_message(isolate());
+ __ mov(ip, Operand(has_pending_message));
+ __ ldr(r1, MemOperand(ip));
+ __ SmiTag(r1);
+ __ push(r1);
+
+ ExternalReference pending_message_script =
+ ExternalReference::address_of_pending_message_script(isolate());
+ __ mov(ip, Operand(pending_message_script));
+ __ ldr(r1, MemOperand(ip));
+ __ push(r1);
+}
+
+
+void FullCodeGenerator::ExitFinallyBlock() {
+ ASSERT(!result_register().is(r1));
+ // Restore pending message from stack.
+ __ pop(r1);
+ ExternalReference pending_message_script =
+ ExternalReference::address_of_pending_message_script(isolate());
+ __ mov(ip, Operand(pending_message_script));
+ __ str(r1, MemOperand(ip));
+
+ __ pop(r1);
+ __ SmiUntag(r1);
+ ExternalReference has_pending_message =
+ ExternalReference::address_of_has_pending_message(isolate());
+ __ mov(ip, Operand(has_pending_message));
+ __ str(r1, MemOperand(ip));
+
+ __ pop(r1);
+ ExternalReference pending_message_obj =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ __ mov(ip, Operand(pending_message_obj));
+ __ str(r1, MemOperand(ip));
+
+ // Restore result register from stack.
+ __ pop(r1);
+
+ // Uncook return address and return.
+ __ pop(result_register());
+ __ SmiUntag(r1);
+ __ add(pc, r1, Operand(masm_->CodeObject()));
+}
+
+
+#undef __
+
+#define __ ACCESS_MASM(masm())
+
+FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
+ int* stack_depth,
+ int* context_length) {
+ // The macros used here must preserve the result register.
+
+ // Because the handler block contains the context of the finally
+ // code, we can restore it directly from there for the finally code
+ // rather than iteratively unwinding contexts via their previous
+ // links.
+ __ Drop(*stack_depth); // Down to the handler block.
+ if (*context_length > 0) {
+ // Restore the context to its dedicated register and the stack.
+ __ ldr(cp, MemOperand(sp, StackHandlerConstants::kContextOffset));
+ __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ }
+ __ PopTryHandler();
+ __ bl(finally_entry_);
+
+ *stack_depth = 0;
+ *context_length = 0;
+ return previous_;
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/ic-arm.cc b/chromium/v8/src/arm/ic-arm.cc
new file mode 100644
index 00000000000..511a3c74f26
--- /dev/null
+++ b/chromium/v8/src/arm/ic-arm.cc
@@ -0,0 +1,1662 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "assembler-arm.h"
+#include "code-stubs.h"
+#include "codegen.h"
+#include "disasm.h"
+#include "ic-inl.h"
+#include "runtime.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+
+// ----------------------------------------------------------------------------
+// Static IC stub generators.
+//
+
+#define __ ACCESS_MASM(masm)
+
+
+static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
+ Register type,
+ Label* global_object) {
+ // Register usage:
+ // type: holds the receiver instance type on entry.
+ __ cmp(type, Operand(JS_GLOBAL_OBJECT_TYPE));
+ __ b(eq, global_object);
+ __ cmp(type, Operand(JS_BUILTINS_OBJECT_TYPE));
+ __ b(eq, global_object);
+ __ cmp(type, Operand(JS_GLOBAL_PROXY_TYPE));
+ __ b(eq, global_object);
+}
+
+
+// Generated code falls through if the receiver is a regular non-global
+// JS object with slow properties and no interceptors.
+static void GenerateNameDictionaryReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register elements,
+ Register t0,
+ Register t1,
+ Label* miss) {
+ // Register usage:
+ // receiver: holds the receiver on entry and is unchanged.
+ // elements: holds the property dictionary on fall through.
+ // Scratch registers:
+ // t0: used to holds the receiver map.
+ // t1: used to holds the receiver instance type, receiver bit mask and
+ // elements map.
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // Check that the receiver is a valid JS object.
+ __ CompareObjectType(receiver, t0, t1, FIRST_SPEC_OBJECT_TYPE);
+ __ b(lt, miss);
+
+ // If this assert fails, we have to check upper bound too.
+ STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
+
+ GenerateGlobalInstanceTypeCheck(masm, t1, miss);
+
+ // Check that the global object does not require access checks.
+ __ ldrb(t1, FieldMemOperand(t0, Map::kBitFieldOffset));
+ __ tst(t1, Operand((1 << Map::kIsAccessCheckNeeded) |
+ (1 << Map::kHasNamedInterceptor)));
+ __ b(ne, miss);
+
+ __ ldr(elements, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ __ ldr(t1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(t1, ip);
+ __ b(ne, miss);
+}
+
+
+// Helper function used from LoadIC/CallIC GenerateNormal.
+//
+// elements: Property dictionary. It is not clobbered if a jump to the miss
+// label is done.
+// name: Property name. It is not clobbered if a jump to the miss label is
+// done
+// result: Register for the result. It is only updated if a jump to the miss
+// label is not done. Can be the same as elements or name clobbering
+// one of these in the case of not jumping to the miss label.
+// The two scratch registers need to be different from elements, name and
+// result.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register name,
+ Register result,
+ Register scratch1,
+ Register scratch2) {
+ // Main use of the scratch registers.
+ // scratch1: Used as temporary and to hold the capacity of the property
+ // dictionary.
+ // scratch2: Used as temporary.
+ Label done;
+
+ // Probe the dictionary.
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
+
+ // If probing finds an entry check that the value is a normal
+ // property.
+ __ bind(&done); // scratch2 == elements + 4 * index
+ const int kElementsStartOffset = NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ __ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
+ __ tst(scratch1, Operand(PropertyDetails::TypeField::kMask << kSmiTagSize));
+ __ b(ne, miss);
+
+ // Get the value at the masked, scaled index and return.
+ __ ldr(result,
+ FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
+}
+
+
+// Helper function used from StoreIC::GenerateNormal.
+//
+// elements: Property dictionary. It is not clobbered if a jump to the miss
+// label is done.
+// name: Property name. It is not clobbered if a jump to the miss label is
+// done
+// value: The value to store.
+// The two scratch registers need to be different from elements, name and
+// result.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+static void GenerateDictionaryStore(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register name,
+ Register value,
+ Register scratch1,
+ Register scratch2) {
+ // Main use of the scratch registers.
+ // scratch1: Used as temporary and to hold the capacity of the property
+ // dictionary.
+ // scratch2: Used as temporary.
+ Label done;
+
+ // Probe the dictionary.
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
+
+ // If probing finds an entry in the dictionary check that the value
+ // is a normal property that is not read only.
+ __ bind(&done); // scratch2 == elements + 4 * index
+ const int kElementsStartOffset = NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ const int kTypeAndReadOnlyMask =
+ (PropertyDetails::TypeField::kMask |
+ PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
+ __ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
+ __ tst(scratch1, Operand(kTypeAndReadOnlyMask));
+ __ b(ne, miss);
+
+ // Store the value at the masked, scaled index and return.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ add(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
+ __ str(value, MemOperand(scratch2));
+
+ // Update the write barrier. Make sure not to clobber the value.
+ __ mov(scratch1, value);
+ __ RecordWrite(
+ elements, scratch2, scratch1, kLRHasNotBeenSaved, kDontSaveFPRegs);
+}
+
+
+// Checks the receiver for special cases (value type, slow case bits).
+// Falls through for regular JS object.
+static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register map,
+ Register scratch,
+ int interceptor_bit,
+ Label* slow) {
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, slow);
+ // Get the map of the receiver.
+ __ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ // Check bit field.
+ __ ldrb(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ tst(scratch,
+ Operand((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)));
+ __ b(ne, slow);
+ // Check that the object is some kind of JS object EXCEPT JS Value type.
+ // In the case that the object is a value-wrapper object,
+ // we enter the runtime system to make sure that indexing into string
+ // objects work as intended.
+ ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
+ __ ldrb(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ cmp(scratch, Operand(JS_OBJECT_TYPE));
+ __ b(lt, slow);
+}
+
+
+// Loads an indexed element from a fast case array.
+// If not_fast_array is NULL, doesn't perform the elements map check.
+static void GenerateFastArrayLoad(MacroAssembler* masm,
+ Register receiver,
+ Register key,
+ Register elements,
+ Register scratch1,
+ Register scratch2,
+ Register result,
+ Label* not_fast_array,
+ Label* out_of_range) {
+ // Register use:
+ //
+ // receiver - holds the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // elements - holds the elements of the receiver on exit.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the the same as 'receiver' or 'key'.
+ // Unchanged on bailout so 'receiver' and 'key' can be safely
+ // used by further computation.
+ //
+ // Scratch registers:
+ //
+ // scratch1 - used to hold elements map and elements length.
+ // Holds the elements map if not_fast_array branch is taken.
+ //
+ // scratch2 - used to hold the loaded value.
+
+ __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ if (not_fast_array != NULL) {
+ // Check that the object is in fast mode and writable.
+ __ ldr(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
+ __ cmp(scratch1, ip);
+ __ b(ne, not_fast_array);
+ } else {
+ __ AssertFastElements(elements);
+ }
+ // Check that the key (index) is within bounds.
+ __ ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
+ __ cmp(key, Operand(scratch1));
+ __ b(hs, out_of_range);
+ // Fast case: Do the load.
+ __ add(scratch1, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ ldr(scratch2, MemOperand::PointerAddressFromSmiKey(scratch1, key));
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(scratch2, ip);
+ // In case the loaded value is the_hole we have to consult GetProperty
+ // to ensure the prototype chain is searched.
+ __ b(eq, out_of_range);
+ __ mov(result, scratch2);
+}
+
+
+// Checks whether a key is an array index string or a unique name.
+// Falls through if a key is a unique name.
+static void GenerateKeyNameCheck(MacroAssembler* masm,
+ Register key,
+ Register map,
+ Register hash,
+ Label* index_string,
+ Label* not_unique) {
+ // The key is not a smi.
+ Label unique;
+ // Is it a name?
+ __ CompareObjectType(key, map, hash, LAST_UNIQUE_NAME_TYPE);
+ __ b(hi, not_unique);
+ STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
+ __ b(eq, &unique);
+
+ // Is the string an array index, with cached numeric value?
+ __ ldr(hash, FieldMemOperand(key, Name::kHashFieldOffset));
+ __ tst(hash, Operand(Name::kContainsCachedArrayIndexMask));
+ __ b(eq, index_string);
+
+ // Is the string internalized? We know it's a string, so a single
+ // bit test is enough.
+ // map: key map
+ __ ldrb(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kInternalizedTag == 0);
+ __ tst(hash, Operand(kIsNotInternalizedMask));
+ __ b(ne, not_unique);
+
+ __ bind(&unique);
+}
+
+
+// Defined in ic.cc.
+Object* CallIC_Miss(Arguments args);
+
+// The generated code does not accept smi keys.
+// The generated code falls through if both probes miss.
+void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm,
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state) {
+ // ----------- S t a t e -------------
+ // -- r1 : receiver
+ // -- r2 : name
+ // -----------------------------------
+ Label number, non_number, non_string, boolean, probe, miss;
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(kind,
+ MONOMORPHIC,
+ extra_state,
+ Code::NORMAL,
+ argc);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, r1, r2, r3, r4, r5, r6);
+
+ // If the stub cache probing failed, the receiver might be a value.
+ // For value objects, we use the map of the prototype objects for
+ // the corresponding JSValue for the cache and that is what we need
+ // to probe.
+ //
+ // Check for number.
+ __ JumpIfSmi(r1, &number);
+ __ CompareObjectType(r1, r3, r3, HEAP_NUMBER_TYPE);
+ __ b(ne, &non_number);
+ __ bind(&number);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::NUMBER_FUNCTION_INDEX, r1);
+ __ b(&probe);
+
+ // Check for string.
+ __ bind(&non_number);
+ __ cmp(r3, Operand(FIRST_NONSTRING_TYPE));
+ __ b(hs, &non_string);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::STRING_FUNCTION_INDEX, r1);
+ __ b(&probe);
+
+ // Check for boolean.
+ __ bind(&non_string);
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+ __ cmp(r1, ip);
+ __ b(eq, &boolean);
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
+ __ cmp(r1, ip);
+ __ b(ne, &miss);
+ __ bind(&boolean);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::BOOLEAN_FUNCTION_INDEX, r1);
+
+ // Probe the stub cache for the value object.
+ __ bind(&probe);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, r1, r2, r3, r4, r5, r6);
+
+ __ bind(&miss);
+}
+
+
+static void GenerateFunctionTailCall(MacroAssembler* masm,
+ int argc,
+ Label* miss,
+ Register scratch) {
+ // r1: function
+
+ // Check that the value isn't a smi.
+ __ JumpIfSmi(r1, miss);
+
+ // Check that the value is a JSFunction.
+ __ CompareObjectType(r1, scratch, scratch, JS_FUNCTION_TYPE);
+ __ b(ne, miss);
+
+ // Invoke the function.
+ ParameterCount actual(argc);
+ __ InvokeFunction(r1, actual, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
+}
+
+
+void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ Label miss;
+
+ // Get the receiver of the function from the stack into r1.
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+
+ GenerateNameDictionaryReceiverCheck(masm, r1, r0, r3, r4, &miss);
+
+ // r0: elements
+ // Search the dictionary - put result in register r1.
+ GenerateDictionaryLoad(masm, &miss, r0, r2, r1, r3, r4);
+
+ GenerateFunctionTailCall(masm, argc, &miss, r4);
+
+ __ bind(&miss);
+}
+
+
+void CallICBase::GenerateMiss(MacroAssembler* masm,
+ int argc,
+ IC::UtilityId id,
+ Code::ExtraICState extra_state) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ if (id == IC::kCallIC_Miss) {
+ __ IncrementCounter(isolate->counters()->call_miss(), 1, r3, r4);
+ } else {
+ __ IncrementCounter(isolate->counters()->keyed_call_miss(), 1, r3, r4);
+ }
+
+ // Get the receiver of the function from the stack.
+ __ ldr(r3, MemOperand(sp, argc * kPointerSize));
+
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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)));
+
+ CEntryStub stub(1);
+ __ CallStub(&stub);
+
+ // 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.
+ if (id == IC::kCallIC_Miss) {
+ Label invoke, global;
+ __ ldr(r2, MemOperand(sp, argc * kPointerSize)); // receiver
+ __ JumpIfSmi(r2, &invoke);
+ __ CompareObjectType(r2, r3, r3, JS_GLOBAL_OBJECT_TYPE);
+ __ b(eq, &global);
+ __ cmp(r3, Operand(JS_BUILTINS_OBJECT_TYPE));
+ __ b(ne, &invoke);
+
+ // Patch the receiver on the stack.
+ __ bind(&global);
+ __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
+ __ str(r2, MemOperand(sp, argc * kPointerSize));
+ __ bind(&invoke);
+ }
+
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount actual(argc);
+ __ InvokeFunction(r1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ call_kind);
+}
+
+
+void CallIC::GenerateMegamorphic(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack into r1.
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state);
+ GenerateMiss(masm, argc, extra_ic_state);
+}
+
+
+void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack into r1.
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+
+ Label do_call, slow_call, slow_load, slow_reload_receiver;
+ Label check_number_dictionary, check_name, lookup_monomorphic_cache;
+ Label index_smi, index_name;
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(r2, &check_name);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, r1, r0, r3, Map::kHasIndexedInterceptor, &slow_call);
+
+ GenerateFastArrayLoad(
+ masm, r1, r2, r4, r3, r0, r1, &check_number_dictionary, &slow_load);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->keyed_call_generic_smi_fast(), 1, r0, r3);
+
+ __ bind(&do_call);
+ // receiver in r1 is not used after this point.
+ // r2: key
+ // r1: function
+ GenerateFunctionTailCall(masm, argc, &slow_call, r0);
+
+ __ bind(&check_number_dictionary);
+ // r2: key
+ // r3: elements map
+ // r4: elements
+ // Check whether the elements is a number dictionary.
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(r3, ip);
+ __ b(ne, &slow_load);
+ __ SmiUntag(r0, r2);
+ // r0: untagged index
+ __ LoadFromNumberDictionary(&slow_load, r4, r2, r1, r0, r3, r5);
+ __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1, r0, r3);
+ __ jmp(&do_call);
+
+ __ bind(&slow_load);
+ // This branch is taken when calling KeyedCallIC_Miss is neither required
+ // nor beneficial.
+ __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1, r0, r3);
+ {
+ 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);
+
+ __ bind(&check_name);
+ GenerateKeyNameCheck(masm, r2, r0, r3, &index_name, &slow_call);
+
+ // The key is known to be a unique name.
+ // If the receiver is a regular JS object with slow properties then do
+ // a quick inline probe of the receiver's dictionary.
+ // Otherwise do the monomorphic cache probe.
+ GenerateKeyedLoadReceiverCheck(
+ masm, r1, r0, r3, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
+
+ __ ldr(r0, FieldMemOperand(r1, JSObject::kPropertiesOffset));
+ __ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(r3, ip);
+ __ b(ne, &lookup_monomorphic_cache);
+
+ GenerateDictionaryLoad(masm, &slow_load, r0, r2, r1, r3, r4);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1, r0, r3);
+ __ jmp(&do_call);
+
+ __ bind(&lookup_monomorphic_cache);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1, r0, r3);
+ GenerateMonomorphicCacheProbe(masm,
+ argc,
+ Code::KEYED_CALL_IC,
+ Code::kNoExtraICState);
+ // Fall through on miss.
+
+ __ bind(&slow_call);
+ // This branch is taken if:
+ // - the receiver requires boxing or access check,
+ // - the key is neither smi nor a unique name,
+ // - the value loaded is not a function,
+ // - there is hope that the runtime will create a monomorphic call stub
+ // that will get fetched next time.
+ __ IncrementCounter(counters->keyed_call_generic_slow(), 1, r0, r3);
+ GenerateMiss(masm, argc);
+
+ __ bind(&index_name);
+ __ IndexFromHash(r3, r2);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
+}
+
+
+void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ // Check if the name is really a name.
+ Label miss;
+ __ JumpIfSmi(r2, &miss);
+ __ IsObjectNameType(r2, r0, &miss);
+
+ CallICBase::GenerateNormal(masm, argc);
+ __ bind(&miss);
+ GenerateMiss(masm, argc);
+}
+
+
+void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- r0 : receiver
+ // -----------------------------------
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, Code::kNoExtraICState,
+ Code::NORMAL, Code::LOAD_IC);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, r0, r2, r3, r4, r5, r6);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
+}
+
+
+void LoadIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- r0 : receiver
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameDictionaryReceiverCheck(masm, r0, r1, r3, r4, &miss);
+
+ // r1: elements
+ GenerateDictionaryLoad(masm, &miss, r1, r2, r0, r3, r4);
+ __ Ret();
+
+ // Cache miss: Jump to runtime.
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void LoadIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- r0 : receiver
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ __ IncrementCounter(isolate->counters()->load_miss(), 1, r3, r4);
+
+ __ mov(r3, r0);
+ __ Push(r3, r2);
+
+ // Perform tail call to the entry.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kLoadIC_Miss), isolate);
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- r0 : receiver
+ // -----------------------------------
+
+ __ mov(r3, r0);
+ __ Push(r3, r2);
+
+ __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
+}
+
+
+static MemOperand GenerateMappedArgumentsLookup(MacroAssembler* masm,
+ Register object,
+ Register key,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* unmapped_case,
+ Label* slow_case) {
+ Heap* heap = masm->isolate()->heap();
+
+ // Check that the receiver is a JSObject. Because of the map check
+ // later, we do not need to check for interceptors or whether it
+ // requires access checks.
+ __ JumpIfSmi(object, slow_case);
+ // Check that the object is some kind of JSObject.
+ __ CompareObjectType(object, scratch1, scratch2, FIRST_JS_RECEIVER_TYPE);
+ __ b(lt, slow_case);
+
+ // Check that the key is a positive smi.
+ __ tst(key, Operand(0x80000001));
+ __ b(ne, slow_case);
+
+ // Load the elements into scratch1 and check its map.
+ Handle<Map> arguments_map(heap->non_strict_arguments_elements_map());
+ __ ldr(scratch1, FieldMemOperand(object, JSObject::kElementsOffset));
+ __ CheckMap(scratch1, scratch2, arguments_map, slow_case, DONT_DO_SMI_CHECK);
+
+ // Check if element is in the range of mapped arguments. If not, jump
+ // to the unmapped lookup with the parameter map in scratch1.
+ __ ldr(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
+ __ sub(scratch2, scratch2, Operand(Smi::FromInt(2)));
+ __ cmp(key, Operand(scratch2));
+ __ b(cs, unmapped_case);
+
+ // Load element index and check whether it is the hole.
+ const int kOffset =
+ FixedArray::kHeaderSize + 2 * kPointerSize - kHeapObjectTag;
+
+ __ mov(scratch3, Operand(kPointerSize >> 1));
+ __ mul(scratch3, key, scratch3);
+ __ add(scratch3, scratch3, Operand(kOffset));
+
+ __ ldr(scratch2, MemOperand(scratch1, scratch3));
+ __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex);
+ __ cmp(scratch2, scratch3);
+ __ b(eq, unmapped_case);
+
+ // Load value from context and return it. We can reuse scratch1 because
+ // we do not jump to the unmapped lookup (which requires the parameter
+ // map in scratch1).
+ __ ldr(scratch1, FieldMemOperand(scratch1, FixedArray::kHeaderSize));
+ __ mov(scratch3, Operand(kPointerSize >> 1));
+ __ mul(scratch3, scratch2, scratch3);
+ __ add(scratch3, scratch3, Operand(Context::kHeaderSize - kHeapObjectTag));
+ return MemOperand(scratch1, scratch3);
+}
+
+
+static MemOperand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
+ Register key,
+ Register parameter_map,
+ Register scratch,
+ Label* slow_case) {
+ // Element is in arguments backing store, which is referenced by the
+ // second element of the parameter_map. The parameter_map register
+ // must be loaded with the parameter map of the arguments object and is
+ // overwritten.
+ const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize;
+ Register backing_store = parameter_map;
+ __ ldr(backing_store, FieldMemOperand(parameter_map, kBackingStoreOffset));
+ Handle<Map> fixed_array_map(masm->isolate()->heap()->fixed_array_map());
+ __ CheckMap(backing_store, scratch, fixed_array_map, slow_case,
+ DONT_DO_SMI_CHECK);
+ __ ldr(scratch, FieldMemOperand(backing_store, FixedArray::kLengthOffset));
+ __ cmp(key, Operand(scratch));
+ __ b(cs, slow_case);
+ __ mov(scratch, Operand(kPointerSize >> 1));
+ __ mul(scratch, key, scratch);
+ __ add(scratch,
+ scratch,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ return MemOperand(backing_store, scratch);
+}
+
+
+void KeyedLoadIC::GenerateNonStrictArguments(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ Label slow, notin;
+ MemOperand mapped_location =
+ GenerateMappedArgumentsLookup(masm, r1, r0, r2, r3, r4, &notin, &slow);
+ __ ldr(r0, mapped_location);
+ __ Ret();
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in r2.
+ MemOperand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, r0, r2, r3, &slow);
+ __ ldr(r2, unmapped_location);
+ __ LoadRoot(r3, Heap::kTheHoleValueRootIndex);
+ __ cmp(r2, r3);
+ __ b(eq, &slow);
+ __ mov(r0, r2);
+ __ Ret();
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+ Label slow, notin;
+ MemOperand mapped_location =
+ GenerateMappedArgumentsLookup(masm, r2, r1, r3, r4, r5, &notin, &slow);
+ __ str(r0, mapped_location);
+ __ add(r6, r3, r5);
+ __ mov(r9, r0);
+ __ RecordWrite(r3, r6, r9, kLRHasNotBeenSaved, kDontSaveFPRegs);
+ __ Ret();
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in r3.
+ MemOperand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, r1, r3, r4, &slow);
+ __ str(r0, unmapped_location);
+ __ add(r6, r3, r4);
+ __ mov(r9, r0);
+ __ RecordWrite(r3, r6, r9, kLRHasNotBeenSaved, kDontSaveFPRegs);
+ __ Ret();
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedCallIC::GenerateNonStrictArguments(MacroAssembler* masm,
+ int argc) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ Label slow, notin;
+ // Load receiver.
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+ MemOperand mapped_location =
+ GenerateMappedArgumentsLookup(masm, r1, r2, r3, r4, r5, &notin, &slow);
+ __ ldr(r1, mapped_location);
+ GenerateFunctionTailCall(masm, argc, &slow, r3);
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in r3.
+ MemOperand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, r2, r3, r4, &slow);
+ __ ldr(r1, unmapped_location);
+ __ LoadRoot(r3, Heap::kTheHoleValueRootIndex);
+ __ cmp(r1, r3);
+ __ b(eq, &slow);
+ GenerateFunctionTailCall(masm, argc, &slow, r3);
+ __ bind(&slow);
+ GenerateMiss(masm, argc);
+}
+
+
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, r3, r4);
+
+ __ Push(r1, r0);
+
+ // Perform tail call to the entry.
+ ExternalReference ref = miss_mode == MISS_FORCE_GENERIC
+ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric), isolate)
+ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate);
+
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+
+ __ Push(r1, r0);
+
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ Label slow, check_name, index_smi, index_name, property_array_property;
+ Label probe_dictionary, check_number_dictionary;
+
+ Register key = r0;
+ Register receiver = r1;
+
+ Isolate* isolate = masm->isolate();
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(key, &check_name);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, receiver, r2, r3, Map::kHasIndexedInterceptor, &slow);
+
+ // Check the receiver's map to see if it has fast elements.
+ __ CheckFastElements(r2, r3, &check_number_dictionary);
+
+ GenerateFastArrayLoad(
+ masm, receiver, key, r4, r3, r2, r0, NULL, &slow);
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_smi(), 1, r2, r3);
+ __ Ret();
+
+ __ bind(&check_number_dictionary);
+ __ ldr(r4, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ ldr(r3, FieldMemOperand(r4, JSObject::kMapOffset));
+
+ // Check whether the elements is a number dictionary.
+ // r0: key
+ // r3: elements map
+ // r4: elements
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(r3, ip);
+ __ b(ne, &slow);
+ __ SmiUntag(r2, r0);
+ __ LoadFromNumberDictionary(&slow, r4, r0, r0, r2, r3, r5);
+ __ Ret();
+
+ // Slow case, key and receiver still in r0 and r1.
+ __ bind(&slow);
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_slow(),
+ 1, r2, r3);
+ GenerateRuntimeGetProperty(masm);
+
+ __ bind(&check_name);
+ GenerateKeyNameCheck(masm, key, r2, r3, &index_name, &slow);
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, receiver, r2, r3, Map::kHasNamedInterceptor, &slow);
+
+ // If the receiver is a fast-case object, check the keyed lookup
+ // cache. Otherwise probe the dictionary.
+ __ ldr(r3, FieldMemOperand(r1, JSObject::kPropertiesOffset));
+ __ ldr(r4, FieldMemOperand(r3, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(r4, ip);
+ __ b(eq, &probe_dictionary);
+
+ // Load the map of the receiver, compute the keyed lookup cache hash
+ // based on 32 bits of the map pointer and the name hash.
+ __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ mov(r3, Operand(r2, ASR, KeyedLookupCache::kMapHashShift));
+ __ ldr(r4, FieldMemOperand(r0, Name::kHashFieldOffset));
+ __ eor(r3, r3, Operand(r4, ASR, Name::kHashShift));
+ int mask = KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask;
+ __ And(r3, r3, Operand(mask));
+
+ // Load the key (consisting of map and unique name) 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));
+
+ 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 name
+ __ cmp(r0, r5);
+ __ b(eq, &hit_on_nth_entry[i]);
+ __ bind(&try_next_entry);
+ }
+
+ // Last entry: Load map and move r4 to name.
+ __ ldr(r5, MemOperand(r4, kPointerSize, PostIndex));
+ __ cmp(r2, r5);
+ __ b(ne, &slow);
+ __ ldr(r5, MemOperand(r4));
+ __ cmp(r0, r5);
+ __ b(ne, &slow);
+
+ // Get field offset.
+ // r0 : key
+ // r1 : receiver
+ // r2 : receiver's map
+ // r3 : lookup cache index
+ ExternalReference cache_field_offsets =
+ ExternalReference::keyed_lookup_cache_field_offsets(isolate);
+
+ // 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.
+ __ ldr(r0, MemOperand(r1, r6, LSL, kPointerSizeLog2));
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
+ 1, r2, r3);
+ __ Ret();
+
+ // Load property array property.
+ __ bind(&property_array_property);
+ __ ldr(r1, FieldMemOperand(r1, JSObject::kPropertiesOffset));
+ __ add(r1, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ ldr(r0, MemOperand(r1, r5, LSL, kPointerSizeLog2));
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
+ 1, r2, r3);
+ __ Ret();
+
+ // Do a quick inline probe of the receiver's dictionary, if it
+ // exists.
+ __ bind(&probe_dictionary);
+ // r1: receiver
+ // r0: key
+ // r3: elements
+ __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
+ GenerateGlobalInstanceTypeCheck(masm, r2, &slow);
+ // Load the property to r0.
+ GenerateDictionaryLoad(masm, &slow, r3, r0, r0, r2, r4);
+ __ IncrementCounter(
+ isolate->counters()->keyed_load_generic_symbol(), 1, r2, r3);
+ __ Ret();
+
+ __ bind(&index_name);
+ __ IndexFromHash(r3, key);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
+}
+
+
+void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key (index)
+ // -- r1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ Register receiver = r1;
+ Register index = r0;
+ Register scratch = r3;
+ Register result = r0;
+
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &miss, // When index out of range.
+ STRING_INDEX_IS_ARRAY_INDEX);
+ char_at_generator.GenerateFast(masm);
+ __ Ret();
+
+ StubRuntimeCallHelper call_helper;
+ char_at_generator.GenerateSlow(masm, call_helper);
+
+ __ bind(&miss);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ Label slow;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(r1, &slow);
+
+ // Check that the key is an array index, that is Uint32.
+ __ NonNegativeSmiTst(r0);
+ __ b(ne, &slow);
+
+ // Get the map of the receiver.
+ __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
+
+ // Check that it has indexed interceptor and access checks
+ // are not enabled for this object.
+ __ ldrb(r3, FieldMemOperand(r2, Map::kBitFieldOffset));
+ __ and_(r3, r3, Operand(kSlowCaseBitFieldMask));
+ __ cmp(r3, Operand(1 << Map::kHasIndexedInterceptor));
+ __ b(ne, &slow);
+
+ // Everything is fine, call runtime.
+ __ Push(r1, r0); // Receiver, key.
+
+ // Perform tail call to the entry.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(kKeyedLoadPropertyWithInterceptor),
+ masm->isolate()),
+ 2,
+ 1);
+
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(r2, r1, r0);
+
+ ExternalReference ref = miss_mode == MISS_FORCE_GENERIC
+ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r2 : key
+ // -- r1 : receiver
+ // -- lr : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(r1, r2, r0);
+
+ // The slow case calls into the runtime to complete the store without causing
+ // an IC miss that would otherwise cause a transition to the generic stub.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kStoreIC_Slow), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(r2, r1, r0);
+
+ // The slow case calls into the runtime to complete the store without causing
+ // an IC miss that would otherwise cause a transition to the generic stub.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(r2, r1, r0);
+
+ __ mov(r1, Operand(Smi::FromInt(NONE))); // PropertyAttributes
+ __ mov(r0, Operand(Smi::FromInt(strict_mode))); // Strict mode.
+ __ Push(r1, r0);
+
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+}
+
+
+static void KeyedStoreGenerateGenericHelper(
+ MacroAssembler* masm,
+ Label* fast_object,
+ Label* fast_double,
+ Label* slow,
+ KeyedStoreCheckMap check_map,
+ KeyedStoreIncrementLength increment_length,
+ Register value,
+ Register key,
+ Register receiver,
+ Register receiver_map,
+ Register elements_map,
+ Register elements) {
+ Label transition_smi_elements;
+ Label finish_object_store, non_double_value, transition_double_elements;
+ Label fast_double_without_map_check;
+
+ // Fast case: Do the store, could be either Object or double.
+ __ bind(fast_object);
+ Register scratch_value = r4;
+ Register address = r5;
+ if (check_map == kCheckMap) {
+ __ ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ cmp(elements_map,
+ Operand(masm->isolate()->factory()->fixed_array_map()));
+ __ b(ne, fast_double);
+ }
+ // Smi stores don't require further checks.
+ Label non_smi_value;
+ __ JumpIfNotSmi(value, &non_smi_value);
+
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ add(scratch_value, key, Operand(Smi::FromInt(1)));
+ __ str(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ }
+ // It's irrelevant whether array is smi-only or not when writing a smi.
+ __ add(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ str(value, MemOperand::PointerAddressFromSmiKey(address, key));
+ __ 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);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ add(scratch_value, key, Operand(Smi::FromInt(1)));
+ __ str(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ }
+ __ add(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ add(address, address, Operand::PointerOffsetFromSmiKey(key));
+ __ str(value, MemOperand(address));
+ // Update write barrier for the elements array address.
+ __ 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);
+ if (check_map == kCheckMap) {
+ // Check for fast double array case. If this fails, call through to the
+ // runtime.
+ __ CompareRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex);
+ __ b(ne, slow);
+ }
+ __ bind(&fast_double_without_map_check);
+ __ StoreNumberToDoubleElements(value, key, elements, r3, d0,
+ &transition_double_elements);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ add(scratch_value, key, Operand(Smi::FromInt(1)));
+ __ str(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ }
+ __ 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_ELEMENTS ->
+ // FAST_DOUBLE_ELEMENTS and complete the store.
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS,
+ receiver_map,
+ r4,
+ slow);
+ ASSERT(receiver_map.is(r3)); // Transition code expects map in r3
+ AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, slow);
+ __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ jmp(&fast_double_without_map_check);
+
+ __ bind(&non_double_value);
+ // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_ELEMENTS,
+ receiver_map,
+ r4,
+ slow);
+ ASSERT(receiver_map.is(r3)); // Transition code expects map in r3
+ mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm, mode,
+ slow);
+ __ 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
+ mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, slow);
+ __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ jmp(&finish_object_store);
+}
+
+
+void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+ Label slow, fast_object, fast_object_grow;
+ Label fast_double, fast_double_grow;
+ Label array, extra, check_if_double_array;
+
+ // Register usage.
+ Register value = r0;
+ Register key = r1;
+ Register receiver = r2;
+ 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.
+ __ JumpIfNotSmi(key, &slow);
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, &slow);
+ // Get the map of the object.
+ __ 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(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(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_OBJECT_TYPE));
+ __ b(lt, &slow);
+
+ // Object case: Check key against length in the elements array.
+ __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ // 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_object);
+
+ // Slow case, handle jump to runtime.
+ __ bind(&slow);
+ // Entry registers are intact.
+ // r0: value.
+ // r1: key.
+ // r2: 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.
+ __ b(ne, &slow); // Only support writing to writing to array[array.length].
+ // Check for room in the elements backing store.
+ // Both the key and the length of FixedArray are smis.
+ __ 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);
+ __ jmp(&fast_object_grow);
+
+ __ bind(&check_if_double_array);
+ __ cmp(elements_map,
+ Operand(masm->isolate()->factory()->fixed_double_array_map()));
+ __ b(ne, &slow);
+ __ jmp(&fast_double_grow);
+
+ // 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));
+
+ // Check the key against the length in the array.
+ __ ldr(ip, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ cmp(key, Operand(ip));
+ __ b(hs, &extra);
+
+ KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double,
+ &slow, kCheckMap, kDontIncrementLength,
+ value, key, receiver, receiver_map,
+ elements_map, elements);
+ KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow,
+ &slow, kDontCheckMap, kIncrementLength,
+ value, key, receiver, receiver_map,
+ elements_map, elements);
+}
+
+
+void StoreIC::GenerateMegamorphic(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : receiver
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ // Get the receiver from the stack and probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, strict_mode,
+ Code::NORMAL, Code::STORE_IC);
+
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, r1, r2, r3, r4, r5, r6);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
+}
+
+
+void StoreIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : receiver
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ __ Push(r1, r2, r0);
+
+ // Perform tail call to the entry.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : receiver
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameDictionaryReceiverCheck(masm, r1, r3, r4, r5, &miss);
+
+ GenerateDictionaryStore(masm, &miss, r3, r2, r0, r4, r5);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->store_normal_hit(),
+ 1, r4, r5);
+ __ Ret();
+
+ __ bind(&miss);
+ __ IncrementCounter(counters->store_normal_miss(), 1, r4, r5);
+ GenerateMiss(masm);
+}
+
+
+void StoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : receiver
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ __ Push(r1, r2, r0);
+
+ __ mov(r1, Operand(Smi::FromInt(NONE))); // PropertyAttributes
+ __ mov(r0, Operand(Smi::FromInt(strict_mode)));
+ __ Push(r1, r0);
+
+ // Do tail-call to runtime routine.
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+}
+
+
+#undef __
+
+
+Condition CompareIC::ComputeCondition(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;
+ }
+}
+
+
+bool CompareIC::HasInlinedSmiCode(Address address) {
+ // The address of the instruction following the call.
+ Address cmp_instruction_address =
+ Assembler::return_address_from_call_start(address);
+
+ // If the instruction following the call is not a cmp rx, #yyy, nothing
+ // was inlined.
+ Instr instr = Assembler::instr_at(cmp_instruction_address);
+ return Assembler::IsCmpImmediate(instr);
+}
+
+
+void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
+ Address cmp_instruction_address =
+ Assembler::return_address_from_call_start(address);
+
+ // If the instruction following the call is not a cmp rx, #yyy, nothing
+ // was inlined.
+ Instr instr = Assembler::instr_at(cmp_instruction_address);
+ if (!Assembler::IsCmpImmediate(instr)) {
+ return;
+ }
+
+ // The delta to the start of the map check instruction and the
+ // condition code uses at the patched jump.
+ int delta = Assembler::GetCmpImmediateRawImmediate(instr);
+ delta +=
+ Assembler::GetCmpImmediateRegister(instr).code() * kOff12Mask;
+ // If the delta is 0 the instruction is cmp r0, #0 which also signals that
+ // nothing was inlined.
+ if (delta == 0) {
+ return;
+ }
+
+#ifdef DEBUG
+ if (FLAG_trace_ic) {
+ PrintF("[ patching ic at %p, cmp=%p, delta=%d\n",
+ address, cmp_instruction_address, delta);
+ }
+#endif
+
+ Address patch_address =
+ cmp_instruction_address - delta * Instruction::kInstrSize;
+ Instr instr_at_patch = Assembler::instr_at(patch_address);
+ Instr branch_instr =
+ Assembler::instr_at(patch_address + Instruction::kInstrSize);
+ // This is patching a conditional "jump if not smi/jump if smi" site.
+ // Enabling by changing from
+ // cmp rx, rx
+ // b eq/ne, <target>
+ // to
+ // tst rx, #kSmiTagMask
+ // b ne/eq, <target>
+ // and vice-versa to be disabled again.
+ CodePatcher patcher(patch_address, 2);
+ Register reg = Assembler::GetRn(instr_at_patch);
+ if (check == ENABLE_INLINED_SMI_CHECK) {
+ ASSERT(Assembler::IsCmpRegister(instr_at_patch));
+ ASSERT_EQ(Assembler::GetRn(instr_at_patch).code(),
+ Assembler::GetRm(instr_at_patch).code());
+ patcher.masm()->tst(reg, Operand(kSmiTagMask));
+ } else {
+ ASSERT(check == DISABLE_INLINED_SMI_CHECK);
+ ASSERT(Assembler::IsTstImmediate(instr_at_patch));
+ patcher.masm()->cmp(reg, reg);
+ }
+ ASSERT(Assembler::IsBranch(branch_instr));
+ if (Assembler::GetCondition(branch_instr) == eq) {
+ patcher.EmitCondition(ne);
+ } else {
+ ASSERT(Assembler::GetCondition(branch_instr) == ne);
+ patcher.EmitCondition(eq);
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/lithium-arm.cc b/chromium/v8/src/arm/lithium-arm.cc
new file mode 100644
index 00000000000..998b73b62e9
--- /dev/null
+++ b/chromium/v8/src/arm/lithium-arm.cc
@@ -0,0 +1,2608 @@
+// Copyright 2012 the V8 project authors. 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 "arm/lithium-arm.h"
+#include "arm/lithium-codegen-arm.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
+
+#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 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);
+ }
+}
+
+
+void LInstruction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ for (int i = 0; i < InputCount(); i++) {
+ if (i > 0) stream->Add(" ");
+ if (InputAt(i) == NULL) {
+ stream->Add("NULL");
+ } else {
+ InputAt(i)->PrintTo(stream);
+ }
+ }
+}
+
+
+void LInstruction::PrintOutputOperandTo(StringStream* stream) {
+ if (HasResult()) result()->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::ROR: return "ror-t";
+ case Token::SHL: return "shl-t";
+ case Token::SAR: return "sar-t";
+ case Token::SHR: return "shr-t";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+bool LGoto::HasInterestingComment(LCodeGen* gen) const {
+ return !gen->IsNextEmittedBlock(block_id());
+}
+
+
+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());
+ value()->PrintTo(stream);
+}
+
+
+void LCompareNumericAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if ");
+ left()->PrintTo(stream);
+ stream->Add(" %s ", Token::String(op()));
+ right()->PrintTo(stream);
+ stream->Add(" then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_object(");
+ value()->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(");
+ value()->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(");
+ value()->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(");
+ value()->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(");
+ left()->PrintTo(stream);
+ right()->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(");
+ value()->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(");
+ value()->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(");
+ value()->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 ");
+ value()->PrintTo(stream);
+ stream->Add(" == \"%s\" then B%d else B%d",
+ *hydrogen()->type_literal()->ToCString(),
+ true_block_id(), false_block_id());
+}
+
+
+void LInnerAllocatedObject::PrintDataTo(StringStream* stream) {
+ stream->Add(" = ");
+ base_object()->PrintTo(stream);
+ stream->Add(" + %d", offset());
+}
+
+
+void LCallConstantFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("#%d / ", arity());
+}
+
+
+void LLoadContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintTo(stream);
+ stream->Add("[%d]", slot_index());
+}
+
+
+void LStoreContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintTo(stream);
+ stream->Add("[%d] <- ", slot_index());
+ value()->PrintTo(stream);
+}
+
+
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ function()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
+void LCallKeyed::PrintDataTo(StringStream* stream) {
+ stream->Add("[r2] #%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("= ");
+ constructor()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
+void LCallNewArray::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ constructor()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+ ElementsKind kind = hydrogen()->elements_kind();
+ stream->Add(" (%s) ", ElementsKindToString(kind));
+}
+
+
+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);
+ hydrogen()->access().PrintTo(stream);
+ 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 LLoadKeyed::PrintDataTo(StringStream* stream) {
+ elements()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ if (hydrogen()->IsDehoisted()) {
+ stream->Add(" + %d]", additional_index());
+ } else {
+ stream->Add("]");
+ }
+}
+
+
+void LStoreKeyed::PrintDataTo(StringStream* stream) {
+ elements()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ if (hydrogen()->IsDehoisted()) {
+ stream->Add(" + %d] <-", additional_index());
+ } else {
+ stream->Add("] <- ");
+ }
+
+ if (value() == NULL) {
+ ASSERT(hydrogen()->IsConstantHoleStore() &&
+ hydrogen()->value()->representation().IsDouble());
+ stream->Add("<the hole(nan)>");
+ } else {
+ 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());
+}
+
+
+int LPlatformChunk::GetNextSpillIndex(bool is_double) {
+ // Skip a slot if for a double-width slot.
+ if (is_double) spill_slot_count_++;
+ return spill_slot_count_++;
+}
+
+
+LOperand* LPlatformChunk::GetNextSpillSlot(bool is_double) {
+ int index = GetNextSpillIndex(is_double);
+ if (is_double) {
+ return LDoubleStackSlot::Create(index, zone());
+ } else {
+ return LStackSlot::Create(index, zone());
+ }
+}
+
+
+LPlatformChunk* LChunkBuilder::Build() {
+ ASSERT(is_unused());
+ chunk_ = new(zone()) LPlatformChunk(info(), graph());
+ LPhase phase("L_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(BailoutReason reason) {
+ info()->set_bailout_reason(reason);
+ status_ = ABORTED;
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(Register reg) {
+ return new(zone()) LUnallocated(LUnallocated::FIXED_REGISTER,
+ Register::ToAllocationIndex(reg));
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(DoubleRegister reg) {
+ return new(zone()) 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(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) {
+ return Use(value,
+ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER,
+ LUnallocated::USED_AT_START));
+}
+
+
+LOperand* LChunkBuilder::UseTempRegister(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::WRITABLE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::Use(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::NONE));
+}
+
+
+LOperand* LChunkBuilder::UseAtStart(HValue* value) {
+ return Use(value, new(zone()) 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::UseConstant(HValue* value) {
+ return chunk_->DefineConstantOperand(HConstant::cast(value));
+}
+
+
+LOperand* LChunkBuilder::UseAny(HValue* value) {
+ return value->IsConstant()
+ ? chunk_->DefineConstantOperand(HConstant::cast(value))
+ : Use(value, new(zone()) 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(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineAsSpilled(
+ LTemplateInstruction<1, I, T>* instr, int 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(zone()) 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;
+ ZoneList<HValue*> objects_to_materialize(0, zone());
+ instr->set_environment(CreateEnvironment(hydrogen_env,
+ &argument_index_accumulator,
+ &objects_to_materialize));
+ return instr;
+}
+
+
+LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
+ HInstruction* hinstr,
+ CanDeoptimize can_deoptimize) {
+ info()->MarkAsNonDeferredCalling();
+#ifdef DEBUG
+ instr->VerifyCall();
+#endif
+ instr->MarkAsCall();
+ instr = AssignPointerMap(instr);
+
+ if (hinstr->HasObservableSideEffects()) {
+ ASSERT(hinstr->next()->IsSimulate());
+ HSimulate* sim = HSimulate::cast(hinstr->next());
+ ASSERT(instruction_pending_deoptimization_environment_ == NULL);
+ ASSERT(pending_deoptimization_ast_id_.IsNone());
+ instruction_pending_deoptimization_environment_ = instr;
+ pending_deoptimization_ast_id_ = 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::AssignPointerMap(LInstruction* instr) {
+ ASSERT(!instr->HasPointerMap());
+ instr->set_pointer_map(new(zone()) LPointerMap(position_, zone()));
+ return instr;
+}
+
+
+LUnallocated* LChunkBuilder::TempRegister() {
+ LUnallocated* operand =
+ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER);
+ int vreg = allocator_->GetVirtualRegister();
+ if (!allocator_->AllocationOk()) {
+ Abort(kOutOfVirtualRegistersWhileTryingToAllocateTempRegister);
+ vreg = 0;
+ }
+ operand->set_virtual_register(vreg);
+ 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(zone()) LLabel(instr->block());
+}
+
+
+LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
+ return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value())));
+}
+
+
+LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
+ return AssignEnvironment(new(zone()) 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(), r1);
+ LOperand* right = UseFixed(instr->right(), r0);
+ LArithmeticT* result = new(zone()) LArithmeticT(op, left, right);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+ }
+
+ ASSERT(instr->representation().IsSmiOrInteger32());
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->left());
+
+ HValue* right_value = instr->right();
+ LOperand* right = NULL;
+ int constant_value = 0;
+ bool does_deopt = false;
+ if (right_value->IsConstant()) {
+ HConstant* constant = HConstant::cast(right_value);
+ right = chunk_->DefineConstantOperand(constant);
+ constant_value = constant->Integer32Value() & 0x1f;
+ // Left shifts can deoptimize if we shift by > 0 and the result cannot be
+ // truncated to smi.
+ if (instr->representation().IsSmi() && constant_value > 0) {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToSmi)) {
+ does_deopt = true;
+ break;
+ }
+ }
+ }
+ } 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.
+ if (op == Token::SHR && constant_value == 0) {
+ if (FLAG_opt_safe_uint32_operations) {
+ does_deopt = !instr->CheckFlag(HInstruction::kUint32);
+ } else {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
+ does_deopt = true;
+ break;
+ }
+ }
+ }
+ }
+
+ LInstruction* result =
+ DefineAsRegister(new(zone()) 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(zone()) 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, r1);
+ LOperand* right_operand = UseFixed(right, r0);
+ LArithmeticT* result =
+ new(zone()) LArithmeticT(op, left_operand, right_operand);
+ return MarkAsCall(DefineFixed(result, r0), 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);
+ if (phi->HasMergedIndex()) {
+ last_environment->SetValueAt(phi->merged_index(), phi);
+ }
+ }
+ for (int i = 0; i < block->deleted_phis()->length(); ++i) {
+ if (block->deleted_phis()->at(i) < last_environment->length()) {
+ 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 DEBUG
+ // Make sure that the lithium instruction has either no fixed register
+ // constraints in temps or the result OR no uses that are only used at
+ // start. If this invariant doesn't hold, the register allocator can decide
+ // to insert a split of a range immediately before the instruction due to an
+ // already allocated register needing to be used for the instruction's fixed
+ // register constraint. In this case, The register allocator won't see an
+ // interference between the split child and the use-at-start (it would if
+ // the it was just a plain use), so it is free to move the split child into
+ // the same register that is used for the use-at-start.
+ // See https://code.google.com/p/chromium/issues/detail?id=201590
+ if (!(instr->ClobbersRegisters() && instr->ClobbersDoubleRegisters())) {
+ int fixed = 0;
+ int used_at_start = 0;
+ for (UseIterator it(instr); !it.Done(); it.Advance()) {
+ LUnallocated* operand = LUnallocated::cast(it.Current());
+ if (operand->IsUsedAtStart()) ++used_at_start;
+ }
+ if (instr->Output() != NULL) {
+ if (LUnallocated::cast(instr->Output())->HasFixedPolicy()) ++fixed;
+ }
+ for (TempIterator it(instr); !it.Done(); it.Advance()) {
+ LUnallocated* operand = LUnallocated::cast(it.Current());
+ if (operand->HasFixedPolicy()) ++fixed;
+ }
+ ASSERT(fixed == 0 || used_at_start == 0);
+ }
+#endif
+
+ instr->set_position(position_);
+ 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,
+ ZoneList<HValue*>* objects_to_materialize) {
+ if (hydrogen_env == NULL) return NULL;
+
+ LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
+ argument_index_accumulator,
+ objects_to_materialize);
+ BailoutId ast_id = hydrogen_env->ast_id();
+ ASSERT(!ast_id.IsNone() ||
+ hydrogen_env->frame_type() != JS_FUNCTION);
+ int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
+ LEnvironment* result = new(zone()) LEnvironment(
+ hydrogen_env->closure(),
+ hydrogen_env->frame_type(),
+ ast_id,
+ hydrogen_env->parameter_count(),
+ argument_count_,
+ value_count,
+ outer,
+ hydrogen_env->entry(),
+ zone());
+ int argument_index = *argument_index_accumulator;
+ int object_index = objects_to_materialize->length();
+ for (int i = 0; i < hydrogen_env->length(); ++i) {
+ if (hydrogen_env->is_special_index(i)) continue;
+
+ LOperand* op;
+ HValue* value = hydrogen_env->values()->at(i);
+ if (value->IsArgumentsObject() || value->IsCapturedObject()) {
+ objects_to_materialize->Add(value, zone());
+ op = LEnvironment::materialization_marker();
+ } else if (value->IsPushArgument()) {
+ op = new(zone()) LArgument(argument_index++);
+ } else {
+ op = UseAny(value);
+ }
+ result->AddValue(op,
+ value->representation(),
+ value->CheckFlag(HInstruction::kUint32));
+ }
+
+ for (int i = object_index; i < objects_to_materialize->length(); ++i) {
+ HValue* object_to_materialize = objects_to_materialize->at(i);
+ int previously_materialized_object = -1;
+ for (int prev = 0; prev < i; ++prev) {
+ if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) {
+ previously_materialized_object = prev;
+ break;
+ }
+ }
+ int length = object_to_materialize->OperandCount();
+ bool is_arguments = object_to_materialize->IsArgumentsObject();
+ if (previously_materialized_object >= 0) {
+ result->AddDuplicateObject(previously_materialized_object);
+ continue;
+ } else {
+ result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
+ }
+ for (int i = is_arguments ? 1 : 0; i < length; ++i) {
+ LOperand* op;
+ HValue* value = object_to_materialize->OperandAt(i);
+ if (value->IsArgumentsObject() || value->IsCapturedObject()) {
+ objects_to_materialize->Add(value, zone());
+ op = LEnvironment::materialization_marker();
+ } else {
+ ASSERT(!value->IsPushArgument());
+ op = UseAny(value);
+ }
+ result->AddValue(op,
+ value->representation(),
+ value->CheckFlag(HInstruction::kUint32));
+ }
+ }
+
+ if (hydrogen_env->frame_type() == JS_FUNCTION) {
+ *argument_index_accumulator = argument_index;
+ }
+
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
+ return new(zone()) LGoto(instr->FirstSuccessor()->block_id());
+}
+
+
+LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
+ HValue* value = instr->value();
+ if (value->EmitAtUses()) {
+ HBasicBlock* successor = HConstant::cast(value)->BooleanValue()
+ ? instr->FirstSuccessor()
+ : instr->SecondSuccessor();
+ return new(zone()) LGoto(successor->block_id());
+ }
+
+ LBranch* result = new(zone()) LBranch(UseRegister(value));
+ // Tagged values that are not known smis or booleans require a
+ // deoptimization environment. If the instruction is generic no
+ // environment is needed since all cases are handled.
+ Representation rep = value->representation();
+ HType type = value->type();
+ ToBooleanStub::Types expected = instr->expected_input_types();
+ if (rep.IsTagged() && !type.IsSmi() && !type.IsBoolean() &&
+ !expected.IsGeneric()) {
+ return AssignEnvironment(result);
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoDebugBreak(HDebugBreak* instr) {
+ return new(zone()) LDebugBreak();
+}
+
+
+LInstruction* LChunkBuilder::DoCompareMap(HCompareMap* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* temp = TempRegister();
+ return new(zone()) LCmpMapAndBranch(value, temp);
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* instr) {
+ info()->MarkAsRequiresFrame();
+ LOperand* value = UseRegister(instr->value());
+ return DefineAsRegister(new(zone()) LArgumentsLength(value));
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
+ info()->MarkAsRequiresFrame();
+ return DefineAsRegister(new(zone()) LArgumentsElements);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
+ LInstanceOf* result =
+ new(zone()) LInstanceOf(UseFixed(instr->left(), r0),
+ UseFixed(instr->right(), r1));
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
+ HInstanceOfKnownGlobal* instr) {
+ LInstanceOfKnownGlobal* result =
+ new(zone()) LInstanceOfKnownGlobal(UseFixed(instr->left(), r0),
+ FixedTemp(r4));
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceSize(HInstanceSize* instr) {
+ LOperand* object = UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new(zone()) LInstanceSize(object));
+}
+
+
+LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) {
+ LOperand* receiver = UseRegisterAtStart(instr->receiver());
+ LOperand* function = UseRegisterAtStart(instr->function());
+ LWrapReceiver* result = new(zone()) LWrapReceiver(receiver, function);
+ return AssignEnvironment(DefineSameAsFirst(result));
+}
+
+
+LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) {
+ LOperand* function = UseFixed(instr->function(), r1);
+ LOperand* receiver = UseFixed(instr->receiver(), r0);
+ LOperand* length = UseFixed(instr->length(), r2);
+ LOperand* elements = UseFixed(instr->elements(), r3);
+ LApplyArguments* result = new(zone()) LApplyArguments(function,
+ receiver,
+ length,
+ elements);
+ return MarkAsCall(DefineFixed(result, r0), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
+ ++argument_count_;
+ LOperand* argument = Use(instr->argument());
+ return new(zone()) LPushArgument(argument);
+}
+
+
+LInstruction* LChunkBuilder::DoInnerAllocatedObject(
+ HInnerAllocatedObject* inner_object) {
+ LOperand* base_object = UseRegisterAtStart(inner_object->base_object());
+ LInnerAllocatedObject* result =
+ new(zone()) LInnerAllocatedObject(base_object);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoThisFunction(HThisFunction* instr) {
+ return instr->HasNoUses()
+ ? NULL
+ : DefineAsRegister(new(zone()) LThisFunction);
+}
+
+
+LInstruction* LChunkBuilder::DoContext(HContext* instr) {
+ // If there is a non-return use, the context must be allocated in a register.
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->IsReturn()) {
+ return DefineAsRegister(new(zone()) LContext);
+ }
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LOuterContext(context));
+}
+
+
+LInstruction* LChunkBuilder::DoDeclareGlobals(HDeclareGlobals* instr) {
+ return MarkAsCall(new(zone()) LDeclareGlobals, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LGlobalObject(context));
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalReceiver(HGlobalReceiver* instr) {
+ LOperand* global_object = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LGlobalReceiver(global_object));
+}
+
+
+LInstruction* LChunkBuilder::DoCallConstantFunction(
+ HCallConstantFunction* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallConstantFunction, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), r1);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new(zone()) LInvokeFunction(function);
+ return MarkAsCall(DefineFixed(result, r0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
+ switch (instr->op()) {
+ case kMathFloor: return DoMathFloor(instr);
+ case kMathRound: return DoMathRound(instr);
+ case kMathAbs: return DoMathAbs(instr);
+ case kMathLog: return DoMathLog(instr);
+ case kMathSin: return DoMathSin(instr);
+ case kMathCos: return DoMathCos(instr);
+ case kMathTan: return DoMathTan(instr);
+ case kMathExp: return DoMathExp(instr);
+ case kMathSqrt: return DoMathSqrt(instr);
+ case kMathPowHalf: return DoMathPowHalf(instr);
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMathFloor(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegister(instr->value());
+ LMathFloor* result = new(zone()) LMathFloor(input);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoMathRound(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegister(instr->value());
+ LOperand* temp = FixedTemp(d3);
+ LMathRound* result = new(zone()) LMathRound(input, temp);
+ return AssignEnvironment(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoMathAbs(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegister(instr->value());
+ LMathAbs* result = new(zone()) LMathAbs(input);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoMathLog(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), d2);
+ LMathLog* result = new(zone()) LMathLog(input);
+ return MarkAsCall(DefineFixedDouble(result, d2), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathSin(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), d2);
+ LMathSin* result = new(zone()) LMathSin(input);
+ return MarkAsCall(DefineFixedDouble(result, d2), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathCos(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), d2);
+ LMathCos* result = new(zone()) LMathCos(input);
+ return MarkAsCall(DefineFixedDouble(result, d2), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathTan(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), d2);
+ LMathTan* result = new(zone()) LMathTan(input);
+ return MarkAsCall(DefineFixedDouble(result, d2), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathExp(HUnaryMathOperation* instr) {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->value()->representation().IsDouble());
+ LOperand* input = UseTempRegister(instr->value());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LOperand* double_temp = FixedTemp(d3); // Chosen by fair dice roll.
+ LMathExp* result = new(zone()) LMathExp(input, double_temp, temp1, temp2);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathSqrt(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegister(instr->value());
+ LMathSqrt* result = new(zone()) LMathSqrt(input);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathPowHalf(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), d2);
+ LOperand* temp = FixedTemp(d3);
+ LMathPowHalf* result = new(zone()) LMathPowHalf(input, temp);
+ return DefineFixedDouble(result, d2);
+}
+
+
+LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) {
+ ASSERT(instr->key()->representation().IsTagged());
+ argument_count_ -= instr->argument_count();
+ LOperand* key = UseFixed(instr->key(), r2);
+ return MarkAsCall(DefineFixed(new(zone()) LCallKeyed(key), r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallNamed, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallGlobal(HCallGlobal* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallGlobal, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallKnownGlobal(HCallKnownGlobal* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallKnownGlobal, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
+ LOperand* constructor = UseFixed(instr->constructor(), r1);
+ argument_count_ -= instr->argument_count();
+ LCallNew* result = new(zone()) LCallNew(constructor);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) {
+ LOperand* constructor = UseFixed(instr->constructor(), r1);
+ argument_count_ -= instr->argument_count();
+ LCallNewArray* result = new(zone()) LCallNewArray(constructor);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), r1);
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallFunction(function), r0),
+ instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallRuntime, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoRor(HRor* instr) {
+ return DoShift(Token::ROR, 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().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ LOperand* right = UseOrConstantAtStart(instr->BetterRightOperand());
+ return DefineAsRegister(new(zone()) 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(zone()) LArithmeticT(instr->op(), left, right);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
+ if (instr->representation().IsDouble()) {
+ return DoArithmeticD(Token::DIV, instr);
+ } else if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+ LOperand* value = UseRegisterAtStart(instr->left());
+ LDivI* div =
+ new(zone()) LDivI(value, UseOrConstant(instr->right()), NULL);
+ return AssignEnvironment(DefineSameAsFirst(div));
+ }
+ LOperand* dividend = UseRegister(instr->left());
+ LOperand* divisor = UseRegister(instr->right());
+ LOperand* temp = CpuFeatures::IsSupported(SUDIV) ? NULL : FixedTemp(d4);
+ LDivI* div = new(zone()) LDivI(dividend, divisor, temp);
+ return AssignEnvironment(DefineAsRegister(div));
+ } else {
+ return DoArithmeticT(Token::DIV, instr);
+ }
+}
+
+
+bool LChunkBuilder::HasMagicNumberForDivisor(int32_t divisor) {
+ uint32_t divisor_abs = abs(divisor);
+ // Dividing by 0, 1, and powers of 2 is easy.
+ // Note that IsPowerOf2(0) returns true;
+ ASSERT(IsPowerOf2(0) == true);
+ if (IsPowerOf2(divisor_abs)) return true;
+
+ // We have magic numbers for a few specific divisors.
+ // Details and proofs can be found in:
+ // - Hacker's Delight, Henry S. Warren, Jr.
+ // - The PowerPC Compiler Writer’s Guide
+ // and probably many others.
+ //
+ // We handle
+ // <divisor with magic numbers> * <power of 2>
+ // but not
+ // <divisor with magic numbers> * <other divisor with magic numbers>
+ int32_t power_of_2_factor =
+ CompilerIntrinsics::CountTrailingZeros(divisor_abs);
+ DivMagicNumbers magic_numbers =
+ DivMagicNumberFor(divisor_abs >> power_of_2_factor);
+ if (magic_numbers.M != InvalidDivMagicNumber.M) return true;
+
+ return false;
+}
+
+
+HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
+ if (CpuFeatures::IsSupported(SUDIV)) {
+ // A value with an integer representation does not need to be transformed.
+ if (divisor->representation().IsInteger32()) {
+ return divisor;
+ // A change from an integer32 can be replaced by the integer32 value.
+ } else if (divisor->IsChange() &&
+ HChange::cast(divisor)->from().IsInteger32()) {
+ return HChange::cast(divisor)->value();
+ }
+ }
+
+ if (divisor->IsConstant() && HConstant::cast(divisor)->HasInteger32Value()) {
+ HConstant* constant_val = HConstant::cast(divisor);
+ int32_t int32_val = constant_val->Integer32Value();
+ if (LChunkBuilder::HasMagicNumberForDivisor(int32_val) ||
+ CpuFeatures::IsSupported(SUDIV)) {
+ return constant_val->CopyToRepresentation(Representation::Integer32(),
+ divisor->block()->zone());
+ }
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
+ HValue* right = instr->right();
+ LOperand* dividend = UseRegister(instr->left());
+ LOperand* divisor = CpuFeatures::IsSupported(SUDIV)
+ ? UseRegister(right)
+ : UseOrConstant(right);
+ LOperand* remainder = TempRegister();
+ ASSERT(CpuFeatures::IsSupported(SUDIV) ||
+ (right->IsConstant() &&
+ HConstant::cast(right)->HasInteger32Value() &&
+ HasMagicNumberForDivisor(HConstant::cast(right)->Integer32Value())));
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LMathFloorOfDiv(dividend, divisor, remainder)));
+}
+
+
+LInstruction* LChunkBuilder::DoMod(HMod* instr) {
+ HValue* left = instr->left();
+ HValue* right = instr->right();
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!right->CanBeZero());
+ LModI* mod = new(zone()) LModI(UseRegisterAtStart(left),
+ UseOrConstant(right));
+ LInstruction* result = DefineAsRegister(mod);
+ return (left->CanBeNegative() &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero))
+ ? AssignEnvironment(result)
+ : result;
+ } else if (instr->fixed_right_arg().has_value) {
+ LModI* mod = new(zone()) LModI(UseRegisterAtStart(left),
+ UseRegisterAtStart(right));
+ return AssignEnvironment(DefineAsRegister(mod));
+ } else if (CpuFeatures::IsSupported(SUDIV)) {
+ LModI* mod = new(zone()) LModI(UseRegister(left),
+ UseRegister(right));
+ LInstruction* result = DefineAsRegister(mod);
+ return (right->CanBeZero() ||
+ (left->RangeCanInclude(kMinInt) &&
+ right->RangeCanInclude(-1) &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)) ||
+ (left->CanBeNegative() &&
+ instr->CanBeZero() &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)))
+ ? AssignEnvironment(result)
+ : result;
+ } else {
+ LModI* mod = new(zone()) LModI(UseRegister(left),
+ UseRegister(right),
+ FixedTemp(d10),
+ FixedTemp(d11));
+ LInstruction* result = DefineAsRegister(mod);
+ return (right->CanBeZero() ||
+ (left->CanBeNegative() &&
+ instr->CanBeZero() &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)))
+ ? AssignEnvironment(result)
+ : result;
+ }
+ } 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.
+ LArithmeticD* mod = new(zone()) LArithmeticD(Token::MOD,
+ UseFixedDouble(left, d1),
+ UseFixedDouble(right, d2));
+ return MarkAsCall(DefineFixedDouble(mod, d1), instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMul(HMul* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left;
+ LOperand* right = UseOrConstant(instr->BetterRightOperand());
+ LOperand* temp = NULL;
+ if (instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
+ (instr->CheckFlag(HValue::kCanOverflow) ||
+ !right->IsConstantOperand())) {
+ left = UseRegister(instr->BetterLeftOperand());
+ temp = TempRegister();
+ } else {
+ left = UseRegisterAtStart(instr->BetterLeftOperand());
+ }
+ LMulI* mul = new(zone()) LMulI(left, right, temp);
+ if (instr->CheckFlag(HValue::kCanOverflow) ||
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ AssignEnvironment(mul);
+ }
+ return DefineAsRegister(mul);
+
+ } else if (instr->representation().IsDouble()) {
+ if (instr->UseCount() == 1 && (instr->uses().value()->IsAdd() ||
+ instr->uses().value()->IsSub())) {
+ HBinaryOperation* use = HBinaryOperation::cast(instr->uses().value());
+
+ if (use->IsAdd() && instr == use->left()) {
+ // This mul is the lhs of an add. The add and mul will be folded into a
+ // multiply-add in DoAdd.
+ return NULL;
+ }
+ if (instr == use->right() && use->IsAdd() && !use->left()->IsMul()) {
+ // This mul is the rhs of an add, where the lhs is not another mul.
+ // The add and mul will be folded into a multiply-add in DoAdd.
+ return NULL;
+ }
+ if (instr == use->right() && use->IsSub()) {
+ // This mul is the rhs of a sub. The sub and mul will be folded into a
+ // multiply-sub in DoSub.
+ return NULL;
+ }
+ }
+
+ return DoArithmeticD(Token::MUL, instr);
+ } else {
+ return DoArithmeticT(Token::MUL, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoSub(HSub* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+
+ if (instr->left()->IsConstant()) {
+ // If lhs is constant, do reverse subtraction instead.
+ return DoRSub(instr);
+ }
+
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ LSubI* sub = new(zone()) LSubI(left, right);
+ LInstruction* result = DefineAsRegister(sub);
+ if (instr->CheckFlag(HValue::kCanOverflow)) {
+ result = AssignEnvironment(result);
+ }
+ return result;
+ } else if (instr->representation().IsDouble()) {
+ if (instr->right()->IsMul()) {
+ return DoMultiplySub(instr->left(), HMul::cast(instr->right()));
+ }
+
+ return DoArithmeticD(Token::SUB, instr);
+ } else {
+ return DoArithmeticT(Token::SUB, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoRSub(HSub* instr) {
+ ASSERT(instr->representation().IsSmiOrInteger32());
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+
+ // Note: The lhs of the subtraction becomes the rhs of the
+ // reverse-subtraction.
+ LOperand* left = UseRegisterAtStart(instr->right());
+ LOperand* right = UseOrConstantAtStart(instr->left());
+ LRSubI* rsb = new(zone()) LRSubI(left, right);
+ LInstruction* result = DefineAsRegister(rsb);
+ if (instr->CheckFlag(HValue::kCanOverflow)) {
+ result = AssignEnvironment(result);
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoMultiplyAdd(HMul* mul, HValue* addend) {
+ LOperand* multiplier_op = UseRegisterAtStart(mul->left());
+ LOperand* multiplicand_op = UseRegisterAtStart(mul->right());
+ LOperand* addend_op = UseRegisterAtStart(addend);
+ return DefineSameAsFirst(new(zone()) LMultiplyAddD(addend_op, multiplier_op,
+ multiplicand_op));
+}
+
+
+LInstruction* LChunkBuilder::DoMultiplySub(HValue* minuend, HMul* mul) {
+ LOperand* minuend_op = UseRegisterAtStart(minuend);
+ LOperand* multiplier_op = UseRegisterAtStart(mul->left());
+ LOperand* multiplicand_op = UseRegisterAtStart(mul->right());
+
+ return DefineSameAsFirst(new(zone()) LMultiplySubD(minuend_op,
+ multiplier_op,
+ multiplicand_op));
+}
+
+
+LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ LOperand* right = UseOrConstantAtStart(instr->BetterRightOperand());
+ LAddI* add = new(zone()) LAddI(left, right);
+ LInstruction* result = DefineAsRegister(add);
+ if (instr->CheckFlag(HValue::kCanOverflow)) {
+ result = AssignEnvironment(result);
+ }
+ return result;
+ } else if (instr->representation().IsDouble()) {
+ if (instr->left()->IsMul()) {
+ return DoMultiplyAdd(HMul::cast(instr->left()), instr->right());
+ }
+
+ if (instr->right()->IsMul()) {
+ ASSERT(!instr->left()->IsMul());
+ return DoMultiplyAdd(HMul::cast(instr->right()), instr->left());
+ }
+
+ return DoArithmeticD(Token::ADD, instr);
+ } else {
+ ASSERT(instr->representation().IsTagged());
+ return DoArithmeticT(Token::ADD, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) {
+ LOperand* left = NULL;
+ LOperand* right = NULL;
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ left = UseRegisterAtStart(instr->BetterLeftOperand());
+ right = UseOrConstantAtStart(instr->BetterRightOperand());
+ } else {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->left()->representation().IsDouble());
+ ASSERT(instr->right()->representation().IsDouble());
+ left = UseRegisterAtStart(instr->left());
+ right = UseRegisterAtStart(instr->right());
+ }
+ return DefineAsRegister(new(zone()) LMathMinMax(left, right));
+}
+
+
+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(), d1);
+ LOperand* right = exponent_type.IsDouble() ?
+ UseFixedDouble(instr->right(), d2) :
+ UseFixed(instr->right(), r2);
+ LPower* result = new(zone()) LPower(left, right);
+ return MarkAsCall(DefineFixedDouble(result, d3),
+ 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(), r0);
+ LRandom* result = new(zone()) LRandom(global_object);
+ return MarkAsCall(DefineFixedDouble(result, d7), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
+ ASSERT(instr->left()->representation().IsTagged());
+ ASSERT(instr->right()->representation().IsTagged());
+ LOperand* left = UseFixed(instr->left(), r1);
+ LOperand* right = UseFixed(instr->right(), r0);
+ LCmpT* result = new(zone()) LCmpT(left, right);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareNumericAndBranch(
+ HCompareNumericAndBranch* instr) {
+ Representation r = instr->representation();
+ if (r.IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(r));
+ ASSERT(instr->right()->representation().Equals(r));
+ LOperand* left = UseRegisterOrConstantAtStart(instr->left());
+ LOperand* right = UseRegisterOrConstantAtStart(instr->right());
+ return new(zone()) LCompareNumericAndBranch(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(zone()) LCompareNumericAndBranch(left, right);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch(
+ HCompareObjectEqAndBranch* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterAtStart(instr->right());
+ return new(zone()) LCmpObjectEqAndBranch(left, right);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareHoleAndBranch(
+ HCompareHoleAndBranch* instr) {
+ LOperand* object = UseRegisterAtStart(instr->object());
+ return new(zone()) LCmpHoleAndBranch(object);
+}
+
+
+LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* temp = TempRegister();
+ return new(zone()) LIsObjectAndBranch(value, temp);
+}
+
+
+LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* temp = TempRegister();
+ return new(zone()) LIsStringAndBranch(value, temp);
+}
+
+
+LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LIsSmiAndBranch(Use(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
+ HIsUndetectableAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return new(zone()) LIsUndetectableAndBranch(value, TempRegister());
+}
+
+
+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(zone()) LStringCompareAndBranch(left, right);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
+ HHasInstanceTypeAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return new(zone()) LHasInstanceTypeAndBranch(value);
+}
+
+
+LInstruction* LChunkBuilder::DoGetCachedArrayIndex(
+ HGetCachedArrayIndex* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+
+ return DefineAsRegister(new(zone()) LGetCachedArrayIndex(value));
+}
+
+
+LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch(
+ HHasCachedArrayIndexAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LHasCachedArrayIndexAndBranch(
+ UseRegisterAtStart(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoClassOfTestAndBranch(
+ HClassOfTestAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegister(instr->value());
+ return new(zone()) LClassOfTestAndBranch(value, TempRegister());
+}
+
+
+LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
+ LOperand* map = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LMapEnumLength(map));
+}
+
+
+LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
+ LOperand* object = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LElementsKind(object));
+}
+
+
+LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
+ LOperand* object = UseRegister(instr->value());
+ LValueOf* result = new(zone()) LValueOf(object, TempRegister());
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+ LOperand* object = UseFixed(instr->value(), r0);
+ LDateField* result =
+ new(zone()) LDateField(object, FixedTemp(r1), instr->index());
+ return MarkAsCall(DefineFixed(result, r0), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) {
+ LOperand* string = UseRegister(instr->string());
+ LOperand* index = UseRegister(instr->index());
+ LOperand* value = UseTempRegister(instr->value());
+ LSeqStringSetChar* result =
+ new(zone()) LSeqStringSetChar(instr->encoding(), string, index, value);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
+ LOperand* value = UseRegisterOrConstantAtStart(instr->index());
+ LOperand* length = UseRegister(instr->length());
+ return AssignEnvironment(new(zone()) LBoundsCheck(value, length));
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheckBaseIndexInformation(
+ HBoundsCheckBaseIndexInformation* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+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(), r0);
+ return MarkAsCall(new(zone()) 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.IsSmi()) {
+ if (to.IsTagged()) {
+ LOperand* value = UseRegister(instr->value());
+ return DefineSameAsFirst(new(zone()) LDummyUse(value));
+ }
+ from = Representation::Tagged();
+ }
+ if (from.IsTagged()) {
+ if (to.IsDouble()) {
+ info()->MarkAsDeferredCalling();
+ LOperand* value = UseRegister(instr->value());
+ LNumberUntagD* res = new(zone()) LNumberUntagD(value);
+ return AssignEnvironment(DefineAsRegister(res));
+ } else if (to.IsSmi()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ if (val->type().IsSmi()) {
+ return DefineSameAsFirst(new(zone()) LDummyUse(value));
+ }
+ return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value)));
+ } else {
+ ASSERT(to.IsInteger32());
+ LOperand* value = NULL;
+ LInstruction* res = NULL;
+ if (instr->value()->type().IsSmi()) {
+ value = UseRegisterAtStart(instr->value());
+ res = DefineAsRegister(new(zone()) LSmiUntag(value, false));
+ } else {
+ value = UseRegister(instr->value());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = instr->CanTruncateToInt32() ? TempRegister()
+ : NULL;
+ LOperand* temp3 = FixedTemp(d11);
+ res = DefineSameAsFirst(new(zone()) LTaggedToI(value,
+ temp1,
+ temp2,
+ temp3));
+ res = AssignEnvironment(res);
+ }
+ return res;
+ }
+ } else if (from.IsDouble()) {
+ if (to.IsTagged()) {
+ info()->MarkAsDeferredCalling();
+ 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(zone()) LNumberTagD(value, temp1, temp2);
+ Define(result, result_temp);
+ return AssignPointerMap(result);
+ } else if (to.IsSmi()) {
+ LOperand* value = UseRegister(instr->value());
+ return AssignEnvironment(DefineAsRegister(new(zone()) LDoubleToSmi(value,
+ TempRegister(), TempRegister())));
+ } else {
+ ASSERT(to.IsInteger32());
+ LOperand* value = UseRegister(instr->value());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = instr->CanTruncateToInt32() ? TempRegister() : NULL;
+ LDoubleToI* res = new(zone()) LDoubleToI(value, temp1, temp2);
+ return AssignEnvironment(DefineAsRegister(res));
+ }
+ } else if (from.IsInteger32()) {
+ info()->MarkAsDeferredCalling();
+ if (to.IsTagged()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegisterAtStart(val);
+ if (val->CheckFlag(HInstruction::kUint32)) {
+ LNumberTagU* result = new(zone()) LNumberTagU(value);
+ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
+ } else if (val->HasRange() && val->range()->IsInSmiRange()) {
+ return DefineAsRegister(new(zone()) LSmiTag(value));
+ } else {
+ LNumberTagI* result = new(zone()) LNumberTagI(value);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+ }
+ } else if (to.IsSmi()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ LInstruction* result =
+ DefineSameAsFirst(new(zone()) LInteger32ToSmi(value));
+ if (val->HasRange() && val->range()->IsInSmiRange()) {
+ return result;
+ }
+ return AssignEnvironment(result);
+ } else {
+ ASSERT(to.IsDouble());
+ if (instr->value()->CheckFlag(HInstruction::kUint32)) {
+ return DefineAsRegister(
+ new(zone()) LUint32ToDouble(UseRegister(instr->value())));
+ } else {
+ return DefineAsRegister(
+ new(zone()) LInteger32ToDouble(Use(instr->value())));
+ }
+ }
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckHeapObject(HCheckHeapObject* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckNonSmi(value));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckSmi(value));
+}
+
+
+LInstruction* LChunkBuilder::DoIsNumberAndBranch(HIsNumberAndBranch* instr) {
+ return new(zone())
+ LIsNumberAndBranch(UseRegisterOrConstantAtStart(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LInstruction* result = new(zone()) LCheckInstanceType(value);
+ return AssignEnvironment(result);
+}
+
+
+LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckFunction(value));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMaps(HCheckMaps* instr) {
+ LOperand* value = NULL;
+ if (!instr->CanOmitMapChecks()) {
+ value = UseRegisterAtStart(instr->value());
+ if (instr->has_migration_target()) info()->MarkAsDeferredCalling();
+ }
+ LCheckMaps* result = new(zone()) LCheckMaps(value);
+ if (!instr->CanOmitMapChecks()) {
+ AssignEnvironment(result);
+ if (instr->has_migration_target()) return AssignPointerMap(result);
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ LOperand* reg = UseRegister(value);
+ if (input_rep.IsDouble()) {
+ return DefineAsRegister(new(zone()) LClampDToUint8(reg));
+ } else if (input_rep.IsInteger32()) {
+ return DefineAsRegister(new(zone()) LClampIToUint8(reg));
+ } else {
+ ASSERT(input_rep.IsSmiOrTagged());
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve d1 explicitly.
+ LClampTToUint8* result = new(zone()) LClampTToUint8(reg, FixedTemp(d11));
+ return AssignEnvironment(DefineAsRegister(result));
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
+ LOperand* parameter_count = UseRegisterOrConstant(instr->parameter_count());
+ return new(zone()) LReturn(UseFixed(instr->value(), r0),
+ parameter_count);
+}
+
+
+LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
+ Representation r = instr->representation();
+ if (r.IsSmi()) {
+ return DefineAsRegister(new(zone()) LConstantS);
+ } else if (r.IsInteger32()) {
+ return DefineAsRegister(new(zone()) LConstantI);
+ } else if (r.IsDouble()) {
+ return DefineAsRegister(new(zone()) LConstantD);
+ } else if (r.IsExternal()) {
+ return DefineAsRegister(new(zone()) LConstantE);
+ } else if (r.IsTagged()) {
+ return DefineAsRegister(new(zone()) LConstantT);
+ } else {
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
+ LLoadGlobalCell* result = new(zone()) LLoadGlobalCell;
+ return instr->RequiresHoleCheck()
+ ? AssignEnvironment(DefineAsRegister(result))
+ : DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
+ LOperand* global_object = UseFixed(instr->global_object(), r0);
+ LLoadGlobalGeneric* result = new(zone()) LLoadGlobalGeneric(global_object);
+ return MarkAsCall(DefineFixed(result, r0), 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(zone()) LStoreGlobalCell(value, TempRegister()))
+ : new(zone()) LStoreGlobalCell(value, NULL);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
+ LOperand* global_object = UseFixed(instr->global_object(), r1);
+ LOperand* value = UseFixed(instr->value(), r0);
+ LStoreGlobalGeneric* result =
+ new(zone()) LStoreGlobalGeneric(global_object, value);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ LInstruction* result =
+ DefineAsRegister(new(zone()) 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(zone()) LStoreContextSlot(context, value);
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
+ LOperand* obj = UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new(zone()) LLoadNamedField(obj));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
+ LOperand* object = UseFixed(instr->object(), r0);
+ LInstruction* result = DefineFixed(new(zone()) LLoadNamedGeneric(object), r0);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFunctionPrototype(
+ HLoadFunctionPrototype* instr) {
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LLoadFunctionPrototype(UseRegister(instr->function()))));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadExternalArrayPointer(
+ HLoadExternalArrayPointer* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LLoadExternalArrayPointer(input));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyed(HLoadKeyed* instr) {
+ ASSERT(instr->key()->representation().IsSmiOrInteger32());
+ ElementsKind elements_kind = instr->elements_kind();
+ LOperand* key = UseRegisterOrConstantAtStart(instr->key());
+ LLoadKeyed* result = NULL;
+
+ if (!instr->is_external()) {
+ LOperand* obj = NULL;
+ if (instr->representation().IsDouble()) {
+ obj = UseTempRegister(instr->elements());
+ } else {
+ ASSERT(instr->representation().IsSmiOrTagged());
+ obj = UseRegisterAtStart(instr->elements());
+ }
+ result = new(zone()) LLoadKeyed(obj, key);
+ } else {
+ ASSERT(
+ (instr->representation().IsInteger32() &&
+ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
+ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
+ (instr->representation().IsDouble() &&
+ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
+ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
+ LOperand* external_pointer = UseRegister(instr->elements());
+ result = new(zone()) LLoadKeyed(external_pointer, key);
+ }
+
+ DefineAsRegister(result);
+ // An unsigned int array load might overflow and cause a deopt, make sure it
+ // has an environment.
+ bool can_deoptimize = instr->RequiresHoleCheck() ||
+ (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS);
+ return can_deoptimize ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
+ LOperand* object = UseFixed(instr->object(), r1);
+ LOperand* key = UseFixed(instr->key(), r0);
+
+ LInstruction* result =
+ DefineFixed(new(zone()) LLoadKeyedGeneric(object, key), r0);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) {
+ ElementsKind elements_kind = instr->elements_kind();
+
+ if (!instr->is_external()) {
+ ASSERT(instr->elements()->representation().IsTagged());
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
+ LOperand* object = NULL;
+ LOperand* key = NULL;
+ LOperand* val = NULL;
+
+ if (instr->value()->representation().IsDouble()) {
+ object = UseRegisterAtStart(instr->elements());
+ val = UseTempRegister(instr->value());
+ key = UseRegisterOrConstantAtStart(instr->key());
+ } else {
+ ASSERT(instr->value()->representation().IsSmiOrTagged());
+ object = UseTempRegister(instr->elements());
+ val = needs_write_barrier ? UseTempRegister(instr->value())
+ : UseRegisterAtStart(instr->value());
+ key = needs_write_barrier ? UseTempRegister(instr->key())
+ : UseRegisterOrConstantAtStart(instr->key());
+ }
+
+ return new(zone()) LStoreKeyed(object, key, val);
+ }
+
+ ASSERT(
+ (instr->value()->representation().IsInteger32() &&
+ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
+ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
+ (instr->value()->representation().IsDouble() &&
+ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
+ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
+ ASSERT(instr->elements()->representation().IsExternal());
+ 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 = UseRegisterOrConstantAtStart(instr->key());
+ LOperand* external_pointer = UseRegister(instr->elements());
+ return new(zone()) LStoreKeyed(external_pointer, key, val);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
+ LOperand* obj = UseFixed(instr->object(), r2);
+ LOperand* key = UseFixed(instr->key(), r1);
+ LOperand* val = UseFixed(instr->value(), r0);
+
+ ASSERT(instr->object()->representation().IsTagged());
+ ASSERT(instr->key()->representation().IsTagged());
+ ASSERT(instr->value()->representation().IsTagged());
+
+ return MarkAsCall(new(zone()) LStoreKeyedGeneric(obj, key, val), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTransitionElementsKind(
+ HTransitionElementsKind* instr) {
+ LOperand* object = UseRegister(instr->object());
+ if (IsSimpleMapChangeTransition(instr->from_kind(), instr->to_kind())) {
+ LOperand* new_map_reg = TempRegister();
+ LTransitionElementsKind* result =
+ new(zone()) LTransitionElementsKind(object, new_map_reg);
+ return result;
+ } else {
+ LTransitionElementsKind* result =
+ new(zone()) LTransitionElementsKind(object, NULL);
+ return AssignPointerMap(result);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoTrapAllocationMemento(
+ HTrapAllocationMemento* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* temp = TempRegister();
+ LTrapAllocationMemento* result =
+ new(zone()) LTrapAllocationMemento(object, temp);
+ return AssignEnvironment(result);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
+ bool is_in_object = instr->access().IsInobject();
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
+ bool needs_write_barrier_for_map = instr->has_transition() &&
+ instr->NeedsWriteBarrierForMap();
+
+ LOperand* obj;
+ if (needs_write_barrier) {
+ obj = is_in_object
+ ? UseRegister(instr->object())
+ : UseTempRegister(instr->object());
+ } else {
+ obj = needs_write_barrier_for_map
+ ? UseRegister(instr->object())
+ : UseRegisterAtStart(instr->object());
+ }
+
+ LOperand* val;
+ if (needs_write_barrier ||
+ (FLAG_track_fields && instr->field_representation().IsSmi())) {
+ val = UseTempRegister(instr->value());
+ } else if (FLAG_track_double_fields &&
+ instr->field_representation().IsDouble()) {
+ val = UseRegisterAtStart(instr->value());
+ } else {
+ val = UseRegister(instr->value());
+ }
+
+ // We need a temporary register for write barrier of the map field.
+ LOperand* temp = needs_write_barrier_for_map ? TempRegister() : NULL;
+
+ LStoreNamedField* result = new(zone()) LStoreNamedField(obj, val, temp);
+ if (FLAG_track_heap_object_fields &&
+ instr->field_representation().IsHeapObject()) {
+ if (!instr->value()->type().IsHeapObject()) {
+ return AssignEnvironment(result);
+ }
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
+ LOperand* obj = UseFixed(instr->object(), r1);
+ LOperand* val = UseFixed(instr->value(), r0);
+
+ LInstruction* result = new(zone()) 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(zone()) LStringAdd(left, right), r0),
+ instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
+ LOperand* string = UseTempRegister(instr->string());
+ LOperand* index = UseTempRegister(instr->index());
+ LStringCharCodeAt* result = new(zone()) LStringCharCodeAt(string, index);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+ LOperand* char_code = UseRegister(instr->value());
+ LStringCharFromCode* result = new(zone()) LStringCharFromCode(char_code);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) {
+ info()->MarkAsDeferredCalling();
+ LOperand* size = instr->size()->IsConstant()
+ ? UseConstant(instr->size())
+ : UseTempRegister(instr->size());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LAllocate* result = new(zone()) LAllocate(size, temp1, temp2);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) {
+ return MarkAsCall(DefineFixed(new(zone()) LRegExpLiteral, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
+ return MarkAsCall(DefineFixed(new(zone()) LFunctionLiteral, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) {
+ ASSERT(argument_count_ == 0);
+ allocator_->MarkAsOsrEntry();
+ current_block_->last_environment()->set_ast_id(instr->ast_id());
+ return AssignEnvironment(new(zone()) LOsrEntry);
+}
+
+
+LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
+ LParameter* result = new(zone()) LParameter;
+ if (instr->kind() == HParameter::STACK_PARAMETER) {
+ int spill_index = chunk()->GetParameterStackSlot(instr->index());
+ return DefineAsSpilled(result, spill_index);
+ } else {
+ ASSERT(info()->IsStub());
+ CodeStubInterfaceDescriptor* descriptor =
+ info()->code_stub()->GetInterfaceDescriptor(info()->isolate());
+ int index = static_cast<int>(instr->index());
+ Register reg = DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index);
+ return DefineFixed(result, reg);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
+ int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
+ if (spill_index > LUnallocated::kMaxFixedSlotIndex) {
+ Abort(kTooManySpillSlotsNeededForOSR);
+ spill_index = 0;
+ }
+ return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index);
+}
+
+
+LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallStub, r0), 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::DoCapturedObject(HCapturedObject* instr) {
+ // There are no real uses of a captured object.
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
+ info()->MarkAsRequiresFrame();
+ LOperand* args = UseRegister(instr->arguments());
+ LOperand* length;
+ LOperand* index;
+ if (instr->length()->IsConstant() && instr->index()->IsConstant()) {
+ length = UseRegisterOrConstant(instr->length());
+ index = UseOrConstant(instr->index());
+ } else {
+ length = UseTempRegister(instr->length());
+ index = UseRegisterAtStart(instr->index());
+ }
+ return DefineAsRegister(new(zone()) LAccessArgumentsAt(args, length, index));
+}
+
+
+LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) {
+ LOperand* object = UseFixed(instr->value(), r0);
+ LToFastProperties* result = new(zone()) LToFastProperties(object);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
+ LTypeof* result = new(zone()) LTypeof(UseFixed(instr->value(), r0));
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTypeofIsAndBranch(HTypeofIsAndBranch* instr) {
+ return new(zone()) LTypeofIsAndBranch(UseTempRegister(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
+ HIsConstructCallAndBranch* instr) {
+ return new(zone()) 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 = instr->values()->length() - 1; i >= 0; --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(zone()) LLazyBailout;
+ result = AssignEnvironment(result);
+ // Store the lazy deopt environment with the instruction if needed. Right
+ // now it is only used for LInstanceOfKnownGlobal.
+ instruction_pending_deoptimization_environment_->
+ SetDeferredLazyDeoptimizationEnvironment(result->environment());
+ instruction_pending_deoptimization_environment_ = NULL;
+ pending_deoptimization_ast_id_ = BailoutId::None();
+ return result;
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) {
+ if (instr->is_function_entry()) {
+ return MarkAsCall(new(zone()) LStackCheck, instr);
+ } else {
+ ASSERT(instr->is_backwards_branch());
+ return AssignEnvironment(AssignPointerMap(new(zone()) 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->inlining_kind(),
+ instr->undefined_receiver());
+ // Only replay binding of arguments object if it wasn't removed from graph.
+ if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
+ inner->Bind(instr->arguments_var(), instr->arguments_object());
+ }
+ inner->set_entry(instr);
+ current_block_->UpdateEnvironment(inner);
+ chunk_->AddInlinedClosure(instr->closure());
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
+ LInstruction* pop = NULL;
+
+ HEnvironment* env = current_block_->last_environment();
+
+ if (env->entry()->arguments_pushed()) {
+ int argument_count = env->arguments_environment()->parameter_count();
+ pop = new(zone()) LDrop(argument_count);
+ argument_count_ -= argument_count;
+ }
+
+ HEnvironment* outer = current_block_->last_environment()->
+ DiscardInlined(false);
+ current_block_->UpdateEnvironment(outer);
+
+ return pop;
+}
+
+
+LInstruction* LChunkBuilder::DoForInPrepareMap(HForInPrepareMap* instr) {
+ LOperand* object = UseFixed(instr->enumerable(), r0);
+ LForInPrepareMap* result = new(zone()) LForInPrepareMap(object);
+ return MarkAsCall(DefineFixed(result, r0), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoForInCacheArray(HForInCacheArray* instr) {
+ LOperand* map = UseRegister(instr->map());
+ return AssignEnvironment(DefineAsRegister(new(zone()) LForInCacheArray(map)));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMapValue(HCheckMapValue* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* map = UseRegisterAtStart(instr->map());
+ return AssignEnvironment(new(zone()) LCheckMapValue(value, map));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* index = UseRegister(instr->index());
+ return DefineAsRegister(new(zone()) LLoadFieldByIndex(object, index));
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/arm/lithium-arm.h b/chromium/v8/src/arm/lithium-arm.h
new file mode 100644
index 00000000000..d81dc0f57cd
--- /dev/null
+++ b/chromium/v8/src/arm/lithium-arm.h
@@ -0,0 +1,2770 @@
+// Copyright 2012 the V8 project authors. 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_ARM_LITHIUM_ARM_H_
+#define V8_ARM_LITHIUM_ARM_H_
+
+#include "hydrogen.h"
+#include "lithium-allocator.h"
+#include "lithium.h"
+#include "safepoint-table.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class LCodeGen;
+
+#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
+ V(AccessArgumentsAt) \
+ V(AddI) \
+ V(Allocate) \
+ V(ApplyArguments) \
+ V(ArgumentsElements) \
+ V(ArgumentsLength) \
+ V(ArithmeticD) \
+ V(ArithmeticT) \
+ V(BitI) \
+ V(BoundsCheck) \
+ V(Branch) \
+ V(CallConstantFunction) \
+ V(CallFunction) \
+ V(CallGlobal) \
+ V(CallKeyed) \
+ V(CallKnownGlobal) \
+ V(CallNamed) \
+ V(CallNew) \
+ V(CallNewArray) \
+ V(CallRuntime) \
+ V(CallStub) \
+ V(CheckFunction) \
+ V(CheckInstanceType) \
+ V(CheckNonSmi) \
+ V(CheckMaps) \
+ V(CheckMapValue) \
+ V(CheckSmi) \
+ V(ClampDToUint8) \
+ V(ClampIToUint8) \
+ V(ClampTToUint8) \
+ V(ClassOfTestAndBranch) \
+ V(CompareNumericAndBranch) \
+ V(CmpObjectEqAndBranch) \
+ V(CmpHoleAndBranch) \
+ V(CmpMapAndBranch) \
+ V(CmpT) \
+ V(ConstantD) \
+ V(ConstantE) \
+ V(ConstantI) \
+ V(ConstantS) \
+ V(ConstantT) \
+ V(Context) \
+ V(DateField) \
+ V(DebugBreak) \
+ V(DeclareGlobals) \
+ V(Deoptimize) \
+ V(DivI) \
+ V(DoubleToI) \
+ V(DoubleToSmi) \
+ V(Drop) \
+ V(DummyUse) \
+ V(ElementsKind) \
+ V(ForInCacheArray) \
+ V(ForInPrepareMap) \
+ V(FunctionLiteral) \
+ V(GetCachedArrayIndex) \
+ V(GlobalObject) \
+ V(GlobalReceiver) \
+ V(Goto) \
+ V(HasCachedArrayIndexAndBranch) \
+ V(HasInstanceTypeAndBranch) \
+ V(InnerAllocatedObject) \
+ V(InstanceOf) \
+ V(InstanceOfKnownGlobal) \
+ V(InstanceSize) \
+ V(InstructionGap) \
+ V(Integer32ToDouble) \
+ V(Integer32ToSmi) \
+ V(InvokeFunction) \
+ V(IsConstructCallAndBranch) \
+ V(IsObjectAndBranch) \
+ V(IsStringAndBranch) \
+ V(IsNumberAndBranch) \
+ V(IsSmiAndBranch) \
+ V(IsUndetectableAndBranch) \
+ V(Label) \
+ V(LazyBailout) \
+ V(LoadContextSlot) \
+ V(LoadExternalArrayPointer) \
+ V(LoadFieldByIndex) \
+ V(LoadFunctionPrototype) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
+ V(LoadKeyed) \
+ V(LoadKeyedGeneric) \
+ V(LoadNamedField) \
+ V(LoadNamedGeneric) \
+ V(MapEnumLength) \
+ V(MathAbs) \
+ V(MathCos) \
+ V(MathExp) \
+ V(MathFloor) \
+ V(MathFloorOfDiv) \
+ V(MathLog) \
+ V(MathMinMax) \
+ V(MathPowHalf) \
+ V(MathRound) \
+ V(MathSin) \
+ V(MathSqrt) \
+ V(MathTan) \
+ V(ModI) \
+ V(MulI) \
+ V(MultiplyAddD) \
+ V(MultiplySubD) \
+ V(NumberTagD) \
+ V(NumberTagI) \
+ V(NumberTagU) \
+ V(NumberUntagD) \
+ V(OsrEntry) \
+ V(OuterContext) \
+ V(Parameter) \
+ V(Power) \
+ V(PushArgument) \
+ V(Random) \
+ V(RegExpLiteral) \
+ V(Return) \
+ V(SeqStringSetChar) \
+ V(ShiftI) \
+ V(SmiTag) \
+ V(SmiUntag) \
+ V(StackCheck) \
+ V(StoreContextSlot) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
+ V(StoreKeyed) \
+ V(StoreKeyedGeneric) \
+ V(StoreNamedField) \
+ V(StoreNamedGeneric) \
+ V(StringAdd) \
+ V(StringCharCodeAt) \
+ V(StringCharFromCode) \
+ V(StringCompareAndBranch) \
+ V(SubI) \
+ V(RSubI) \
+ V(TaggedToI) \
+ V(ThisFunction) \
+ V(Throw) \
+ V(ToFastProperties) \
+ V(TransitionElementsKind) \
+ V(TrapAllocationMemento) \
+ V(Typeof) \
+ V(TypeofIsAndBranch) \
+ V(Uint32ToDouble) \
+ V(UnknownOSRValue) \
+ V(ValueOf) \
+ V(WrapReceiver)
+
+
+#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()
+ : environment_(NULL),
+ hydrogen_value_(NULL),
+ bit_field_(IsCallBits::encode(false)) {
+ set_position(RelocInfo::kNoPosition);
+ }
+
+ virtual ~LInstruction() { }
+
+ virtual void CompileToNative(LCodeGen* generator) = 0;
+ virtual const char* Mnemonic() const = 0;
+ virtual void PrintTo(StringStream* stream);
+ virtual void PrintDataTo(StringStream* stream);
+ virtual void PrintOutputOperandTo(StringStream* stream);
+
+ 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(); }
+
+ // The 31 bits PositionBits is used to store the int position value. And the
+ // position value may be RelocInfo::kNoPosition (-1). The accessor always
+ // +1/-1 so that the encoded value of position in bit_field_ is always >= 0
+ // and can fit into the 31 bits PositionBits.
+ void set_position(int pos) {
+ bit_field_ = PositionBits::update(bit_field_, pos + 1);
+ }
+ int position() { return PositionBits::decode(bit_field_) - 1; }
+
+ void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
+ HValue* hydrogen_value() const { return hydrogen_value_; }
+
+ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
+
+ void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
+ bool IsCall() const { return IsCallBits::decode(bit_field_); }
+
+ // Interface to the register allocator and iterators.
+ bool ClobbersTemps() const { return IsCall(); }
+ bool ClobbersRegisters() const { return IsCall(); }
+ bool ClobbersDoubleRegisters() const { return IsCall(); }
+
+ // Interface to the register allocator and iterators.
+ bool IsMarkedAsCall() const { return IsCall(); }
+
+ virtual bool HasResult() const = 0;
+ virtual LOperand* result() const = 0;
+
+ LOperand* FirstInput() { return InputAt(0); }
+ LOperand* Output() { return HasResult() ? result() : NULL; }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return true; }
+
+#ifdef DEBUG
+ void VerifyCall();
+#endif
+
+ private:
+ // Iterator support.
+ friend class InputIterator;
+ virtual int InputCount() = 0;
+ virtual LOperand* InputAt(int i) = 0;
+
+ friend class TempIterator;
+ virtual int TempCount() = 0;
+ virtual LOperand* TempAt(int i) = 0;
+
+ class IsCallBits: public BitField<bool, 0, 1> {};
+ class PositionBits: public BitField<int, 1, 31> {};
+
+ LEnvironment* environment_;
+ SetOncePointer<LPointerMap> pointer_map_;
+ HValue* hydrogen_value_;
+ int bit_field_;
+};
+
+
+// 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 && result() != NULL; }
+ void set_result(LOperand* operand) { results_[0] = operand; }
+ LOperand* result() const { return results_[0]; }
+
+ protected:
+ EmbeddedContainer<LOperand*, R> results_;
+ EmbeddedContainer<LOperand*, I> inputs_;
+ EmbeddedContainer<LOperand*, T> temps_;
+
+ private:
+ virtual int InputCount() { return I; }
+ virtual LOperand* InputAt(int i) { return inputs_[i]; }
+
+ virtual int TempCount() { return T; }
+ virtual LOperand* TempAt(int i) { return temps_[i]; }
+};
+
+
+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;
+ }
+
+ // 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);
+ }
+
+ bool IsRedundant() const;
+
+ HBasicBlock* block() const { return block_; }
+
+ enum InnerPosition {
+ BEFORE,
+ START,
+ END,
+ AFTER,
+ FIRST_INNER_POSITION = BEFORE,
+ LAST_INNER_POSITION = AFTER
+ };
+
+ LParallelMove* GetOrCreateParallelMove(InnerPosition pos, Zone* zone) {
+ if (parallel_moves_[pos] == NULL) {
+ parallel_moves_[pos] = new(zone) LParallelMove(zone);
+ }
+ return parallel_moves_[pos];
+ }
+
+ LParallelMove* GetParallelMove(InnerPosition pos) {
+ return parallel_moves_[pos];
+ }
+
+ private:
+ LParallelMove* parallel_moves_[LAST_INNER_POSITION + 1];
+ HBasicBlock* block_;
+};
+
+
+class LInstructionGap: public LGap {
+ public:
+ explicit LInstructionGap(HBasicBlock* block) : LGap(block) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const {
+ return !IsRedundant();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap")
+};
+
+
+class LGoto: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LGoto(int block_id) : block_id_(block_id) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const;
+ 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_; }
+
+ private:
+ int gap_instructions_size_;
+};
+
+
+class LDummyUse: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDummyUse(LOperand* value) {
+ inputs_[0] = value;
+ }
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use")
+};
+
+
+class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
+ DECLARE_HYDROGEN_ACCESSOR(Deoptimize)
+};
+
+
+class LLabel: public LGap {
+ public:
+ explicit LLabel(HBasicBlock* block)
+ : LGap(block), replacement_(NULL) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ 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(); }
+ bool is_osr_entry() const { return block()->is_osr_entry(); }
+ 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:
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ 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();
+ }
+};
+
+
+class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
+ public:
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value")
+};
+
+
+template<int I, int T>
+class LControlInstruction: public LTemplateInstruction<0, I, T> {
+ public:
+ LControlInstruction() : false_label_(NULL), true_label_(NULL) { }
+
+ virtual bool IsControl() const { return true; }
+
+ int SuccessorCount() { return hydrogen()->SuccessorCount(); }
+ HBasicBlock* SuccessorAt(int i) { return hydrogen()->SuccessorAt(i); }
+
+ int TrueDestination(LChunk* chunk) {
+ return chunk->LookupDestination(true_block_id());
+ }
+ int FalseDestination(LChunk* chunk) {
+ return chunk->LookupDestination(false_block_id());
+ }
+
+ Label* TrueLabel(LChunk* chunk) {
+ if (true_label_ == NULL) {
+ true_label_ = chunk->GetAssemblyLabel(TrueDestination(chunk));
+ }
+ return true_label_;
+ }
+ Label* FalseLabel(LChunk* chunk) {
+ if (false_label_ == NULL) {
+ false_label_ = chunk->GetAssemblyLabel(FalseDestination(chunk));
+ }
+ return false_label_;
+ }
+
+ protected:
+ int true_block_id() { return SuccessorAt(0)->block_id(); }
+ int false_block_id() { return SuccessorAt(1)->block_id(); }
+
+ private:
+ HControlInstruction* hydrogen() {
+ return HControlInstruction::cast(this->hydrogen_value());
+ }
+
+ Label* false_label_;
+ Label* true_label_;
+};
+
+
+class LWrapReceiver: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LWrapReceiver(LOperand* receiver, LOperand* function) {
+ inputs_[0] = receiver;
+ inputs_[1] = function;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver")
+
+ LOperand* receiver() { return inputs_[0]; }
+ LOperand* function() { return inputs_[1]; }
+};
+
+
+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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ LOperand* elements() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length")
+};
+
+
+class LArgumentsElements: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments-elements")
+ DECLARE_HYDROGEN_ACCESSOR(ArgumentsElements)
+};
+
+
+class LModI: public LTemplateInstruction<1, 2, 2> {
+ public:
+ LModI(LOperand* left,
+ LOperand* right,
+ LOperand* temp = NULL,
+ LOperand* temp2 = NULL) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mod)
+};
+
+
+class LDivI: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LDivI(LOperand* left, LOperand* right, LOperand* temp) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
+ DECLARE_HYDROGEN_ACCESSOR(Div)
+};
+
+
+class LMathFloorOfDiv: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LMathFloorOfDiv(LOperand* left,
+ LOperand* right,
+ LOperand* temp = NULL) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
+ DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
+};
+
+
+class LMulI: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LMulI(LOperand* left, LOperand* right, LOperand* temp) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mul)
+};
+
+
+// Instruction for computing multiplier * multiplicand + addend.
+class LMultiplyAddD: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LMultiplyAddD(LOperand* addend, LOperand* multiplier,
+ LOperand* multiplicand) {
+ inputs_[0] = addend;
+ inputs_[1] = multiplier;
+ inputs_[2] = multiplicand;
+ }
+
+ LOperand* addend() { return inputs_[0]; }
+ LOperand* multiplier() { return inputs_[1]; }
+ LOperand* multiplicand() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MultiplyAddD, "multiply-add-d")
+};
+
+
+// Instruction for computing minuend - multiplier * multiplicand.
+class LMultiplySubD: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LMultiplySubD(LOperand* minuend, LOperand* multiplier,
+ LOperand* multiplicand) {
+ inputs_[0] = minuend;
+ inputs_[1] = multiplier;
+ inputs_[2] = multiplicand;
+ }
+
+ LOperand* minuend() { return inputs_[0]; }
+ LOperand* multiplier() { return inputs_[1]; }
+ LOperand* multiplicand() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MultiplySubD, "multiply-sub-d")
+};
+
+
+class LDebugBreak: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(DebugBreak, "break")
+};
+
+
+class LCompareNumericAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCompareNumericAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareNumericAndBranch,
+ "compare-numeric-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareNumericAndBranch)
+
+ Token::Value op() const { return hydrogen()->token(); }
+ bool is_double() const {
+ return hydrogen()->representation().IsDouble();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LMathFloor: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathFloor(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloor, "math-floor")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathRound: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LMathRound(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathRound, "math-round")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathAbs: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathAbs(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathAbs, "math-abs")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathLog: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathLog(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathLog, "math-log")
+};
+
+
+class LMathSin: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathSin(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathSin, "math-sin")
+};
+
+
+class LMathCos: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathCos(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathCos, "math-cos")
+};
+
+
+class LMathTan: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathTan(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathTan, "math-tan")
+};
+
+
+class LMathExp: public LTemplateInstruction<1, 1, 3> {
+ public:
+ LMathExp(LOperand* value,
+ LOperand* double_temp,
+ LOperand* temp1,
+ LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
+ temps_[2] = double_temp;
+ ExternalReference::InitializeMathExpData();
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp1() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+ LOperand* double_temp() { return temps_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathExp, "math-exp")
+};
+
+
+class LMathSqrt: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathSqrt(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathSqrt, "math-sqrt")
+};
+
+
+class LMathPowHalf: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LMathPowHalf(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathPowHalf, "math-pow-half")
+};
+
+
+class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCmpObjectEqAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, "cmp-object-eq-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareObjectEqAndBranch)
+};
+
+
+class LCmpHoleAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LCmpHoleAndBranch(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpHoleAndBranch, "cmp-hole-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareHoleAndBranch)
+};
+
+
+class LIsObjectAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LIsObjectAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsObjectAndBranch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LIsNumberAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LIsNumberAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsNumberAndBranch, "is-number-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsNumberAndBranch)
+};
+
+
+class LIsStringAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LIsStringAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get-cached-array-index")
+ DECLARE_HYDROGEN_ACCESSOR(GetCachedArrayIndex)
+};
+
+
+class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LHasCachedArrayIndexAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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:
+ LCmpT(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
+ "instance-of-known-global")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+
+ Handle<JSFunction> function() const { return hydrogen()->function(); }
+ LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
+ return lazy_deopt_env_;
+ }
+ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) {
+ lazy_deopt_env_ = env;
+ }
+
+ private:
+ LEnvironment* lazy_deopt_env_;
+};
+
+
+class LInstanceSize: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInstanceSize(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceSize, "instance-size")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceSize)
+};
+
+
+class LBoundsCheck: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LBoundsCheck(LOperand* index, LOperand* length) {
+ inputs_[0] = index;
+ inputs_[1] = length;
+ }
+
+ LOperand* index() { return inputs_[0]; }
+ LOperand* length() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check")
+ DECLARE_HYDROGEN_ACCESSOR(BoundsCheck)
+};
+
+
+class LBitI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LBitI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ Token::Value op() const { return op_; }
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SubI, "sub-i")
+ DECLARE_HYDROGEN_ACCESSOR(Sub)
+};
+
+
+class LRSubI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LRSubI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(RSubI, "rsub-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 LConstantS: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ConstantS, "constant-s")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ Smi* value() const { return Smi::FromInt(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 LConstantE: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ConstantE, "constant-e")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ ExternalReference value() const {
+ return hydrogen()->ExternalReferenceValue();
+ }
+};
+
+
+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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Branch, "branch")
+ DECLARE_HYDROGEN_ACCESSOR(Branch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LCmpMapAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LCmpMapAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMap)
+
+ Handle<Map> map() const { return hydrogen()->map(); }
+};
+
+
+class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMapEnumLength(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length")
+};
+
+
+class LElementsKind: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LElementsKind(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+};
+
+
+class LDateField: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LDateField(LOperand* date, LOperand* temp, Smi* index) : index_(index) {
+ inputs_[0] = date;
+ temps_[0] = temp;
+ }
+
+ LOperand* date() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ Smi* index() const { return index_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+
+ private:
+ Smi* index_;
+};
+
+
+class LSeqStringSetChar: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LSeqStringSetChar(String::Encoding encoding,
+ LOperand* string,
+ LOperand* index,
+ LOperand* value) : encoding_(encoding) {
+ inputs_[0] = string;
+ inputs_[1] = index;
+ inputs_[2] = value;
+ }
+
+ String::Encoding encoding() { return encoding_; }
+ LOperand* string() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar, "seq-string-set-char")
+ DECLARE_HYDROGEN_ACCESSOR(SeqStringSetChar)
+
+ private:
+ String::Encoding encoding_;
+};
+
+
+class LThrow: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LThrow(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
+};
+
+
+class LAddI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LAddI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i")
+ DECLARE_HYDROGEN_ACCESSOR(Add)
+};
+
+
+class LMathMinMax: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LMathMinMax(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathMinMax, "math-min-max")
+ DECLARE_HYDROGEN_ACCESSOR(MathMinMax)
+};
+
+
+class LPower: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LPower(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+
+ 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_; }
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ Token::Value op() const { return op_; }
+
+ virtual Opcode opcode() const { return LInstruction::kArithmeticT; }
+ virtual void CompileToNative(LCodeGen* generator);
+ virtual const char* Mnemonic() const;
+
+ private:
+ Token::Value op_;
+};
+
+
+class LReturn: public LTemplateInstruction<0, 2, 0> {
+ public:
+ explicit LReturn(LOperand* value, LOperand* parameter_count) {
+ inputs_[0] = value;
+ inputs_[1] = parameter_count;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ bool has_constant_parameter_count() {
+ return parameter_count()->IsConstantOperand();
+ }
+ LConstantOperand* constant_parameter_count() {
+ ASSERT(has_constant_parameter_count());
+ return LConstantOperand::cast(parameter_count());
+ }
+ LOperand* parameter_count() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Return, "return")
+};
+
+
+class LLoadNamedField: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadNamedField(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedField)
+};
+
+
+class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadNamedGeneric(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedGeneric)
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+};
+
+
+class LLoadFunctionPrototype: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadFunctionPrototype(LOperand* function) {
+ inputs_[0] = function;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load-function-prototype")
+ DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype)
+};
+
+
+class LLoadExternalArrayPointer: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadExternalArrayPointer(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer,
+ "load-external-array-pointer")
+};
+
+
+class LLoadKeyed: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadKeyed(LOperand* elements, LOperand* key) {
+ inputs_[0] = elements;
+ inputs_[1] = key;
+ }
+
+ LOperand* elements() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ ElementsKind elements_kind() const {
+ return hydrogen()->elements_kind();
+ }
+ bool is_external() const {
+ return hydrogen()->is_external();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyed, "load-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(LoadKeyed)
+
+ virtual void PrintDataTo(StringStream* stream);
+ uint32_t additional_index() const { return hydrogen()->index_offset(); }
+};
+
+
+class LLoadKeyedGeneric: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadKeyedGeneric(LOperand* object, LOperand* key) {
+ inputs_[0] = object;
+ inputs_[1] = key;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load-keyed-generic")
+};
+
+
+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;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell)
+};
+
+
+class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> {
+ public:
+ explicit LStoreGlobalGeneric(LOperand* global_object,
+ LOperand* value) {
+ inputs_[0] = global_object;
+ inputs_[1] = value;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric)
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LLoadContextSlot: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadContextSlot(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot)
+
+ 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;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument")
+};
+
+
+class LDrop: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LDrop(int count) : count_(count) { }
+
+ int count() const { return count_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Drop, "drop")
+
+ private:
+ int count_;
+};
+
+
+class LInnerAllocatedObject: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInnerAllocatedObject(LOperand* base_object) {
+ inputs_[0] = base_object;
+ }
+
+ LOperand* base_object() { return inputs_[0]; }
+ int offset() { return hydrogen()->offset(); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(InnerAllocatedObject, "sub-allocated-object")
+ DECLARE_HYDROGEN_ACCESSOR(InnerAllocatedObject)
+};
+
+
+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")
+ DECLARE_HYDROGEN_ACCESSOR(Context)
+};
+
+
+class LOuterContext: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LOuterContext(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context")
+};
+
+
+class LDeclareGlobals: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals, "declare-globals")
+ DECLARE_HYDROGEN_ACCESSOR(DeclareGlobals)
+};
+
+
+class LGlobalObject: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LGlobalObject(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object")
+
+ LOperand* context() { return inputs_[0]; }
+};
+
+
+class LGlobalReceiver: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LGlobalReceiver(LOperand* global_object) {
+ inputs_[0] = global_object;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver")
+};
+
+
+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;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ 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;
+ }
+
+ LOperand* key() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
+ DECLARE_HYDROGEN_ACCESSOR(CallFunction)
+
+ 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);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNew: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallNew(LOperand* constructor) {
+ inputs_[0] = constructor;
+ }
+
+ LOperand* constructor() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new")
+ DECLARE_HYDROGEN_ACCESSOR(CallNew)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNewArray: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallNewArray(LOperand* constructor) {
+ inputs_[0] = constructor;
+ }
+
+ LOperand* constructor() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNewArray, "call-new-array")
+ DECLARE_HYDROGEN_ACCESSOR(CallNewArray)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double")
+};
+
+
+class LInteger32ToSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInteger32ToSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToSmi, "int32-to-smi")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+};
+
+
+class LUint32ToDouble: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LUint32ToDouble(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double")
+};
+
+
+class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberTagI(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i")
+};
+
+
+class LNumberTagU: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberTagU(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u")
+};
+
+
+class LNumberTagD: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LNumberTagD(LOperand* value, LOperand* temp, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+};
+
+
+class LDoubleToSmi: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LDoubleToSmi(LOperand* value, LOperand* temp, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DoubleToSmi, "double-to-smi")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+
+ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+};
+
+
+// Sometimes truncating conversion from a tagged value to an int32.
+class LDoubleToI: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LDoubleToI(LOperand* value, LOperand* temp, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ 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* temp,
+ LOperand* temp2,
+ LOperand* temp3) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ temps_[2] = temp3;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+ LOperand* temp3() { return temps_[2]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag")
+};
+
+
+class LNumberUntagD: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberUntagD(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ bool needs_check() const { return needs_check_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag")
+
+ private:
+ bool needs_check_;
+};
+
+
+class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
+ public:
+ LStoreNamedField(LOperand* object, LOperand* value, LOperand* temp) {
+ inputs_[0] = object;
+ inputs_[1] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> transition() const { return hydrogen()->transition_map(); }
+ Representation representation() const {
+ return hydrogen()->field_representation();
+ }
+};
+
+
+class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LStoreNamedGeneric(LOperand* object, LOperand* value) {
+ inputs_[0] = object;
+ inputs_[1] = value;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreKeyed(LOperand* object, LOperand* key, LOperand* value) {
+ inputs_[0] = object;
+ inputs_[1] = key;
+ inputs_[2] = value;
+ }
+
+ bool is_external() const { return hydrogen()->is_external(); }
+ LOperand* elements() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+ ElementsKind elements_kind() const {
+ return hydrogen()->elements_kind();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyed, "store-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyed)
+
+ virtual void PrintDataTo(StringStream* stream);
+ bool NeedsCanonicalization() {
+ if (hydrogen()->value()->IsAdd() || hydrogen()->value()->IsSub() ||
+ hydrogen()->value()->IsMul() || hydrogen()->value()->IsDiv()) {
+ return false;
+ }
+ return hydrogen()->NeedsCanonicalization();
+ }
+ uint32_t additional_index() const { return hydrogen()->index_offset(); }
+};
+
+
+class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* value) {
+ inputs_[0] = obj;
+ inputs_[1] = key;
+ inputs_[2] = value;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LTransitionElementsKind: public LTemplateInstruction<0, 1, 1> {
+ public:
+ LTransitionElementsKind(LOperand* object,
+ LOperand* new_map_temp) {
+ inputs_[0] = object;
+ temps_[0] = new_map_temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* new_map_temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind,
+ "transition-elements-kind")
+ DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> original_map() { return hydrogen()->original_map(); }
+ Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+ ElementsKind from_kind() { return hydrogen()->from_kind(); }
+ ElementsKind to_kind() { return hydrogen()->to_kind(); }
+};
+
+
+class LTrapAllocationMemento : public LTemplateInstruction<0, 1, 1> {
+ public:
+ LTrapAllocationMemento(LOperand* object,
+ LOperand* temp) {
+ inputs_[0] = object;
+ temps_[0] = temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TrapAllocationMemento,
+ "trap-allocation-memento")
+};
+
+
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+};
+
+
+
+class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringCharCodeAt(LOperand* string, LOperand* index) {
+ inputs_[0] = string;
+ inputs_[1] = index;
+ }
+
+ LOperand* string() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt)
+};
+
+
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LStringCharFromCode(LOperand* char_code) {
+ inputs_[0] = char_code;
+ }
+
+ LOperand* char_code() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+};
+
+
+class LCheckFunction: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckFunction(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type")
+ DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType)
+};
+
+
+class LCheckMaps: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckMaps(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMaps, "check-maps")
+ DECLARE_HYDROGEN_ACCESSOR(CheckMaps)
+};
+
+
+class LCheckSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCheckSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check-smi")
+};
+
+
+class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckNonSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check-non-smi")
+ DECLARE_HYDROGEN_ACCESSOR(CheckHeapObject)
+};
+
+
+class LClampDToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampDToUint8(LOperand* unclamped) {
+ inputs_[0] = unclamped;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampIToUint8(LOperand* unclamped) {
+ inputs_[0] = unclamped;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LClampTToUint8(LOperand* unclamped, LOperand* temp) {
+ inputs_[0] = unclamped;
+ temps_[0] = temp;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8")
+};
+
+
+class LAllocate: public LTemplateInstruction<1, 2, 2> {
+ public:
+ LAllocate(LOperand* size, LOperand* temp1, LOperand* temp2) {
+ inputs_[1] = size;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
+ }
+
+ LOperand* size() { return inputs_[1]; }
+ LOperand* temp1() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Allocate, "allocate")
+ DECLARE_HYDROGEN_ACCESSOR(Allocate)
+};
+
+
+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)
+};
+
+
+class LToFastProperties: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LToFastProperties(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
+};
+
+
+class LTypeofIsAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LTypeofIsAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch,
+ "is-construct-call-and-branch")
+};
+
+
+class LOsrEntry: public LTemplateInstruction<0, 0, 0> {
+ public:
+ LOsrEntry() {}
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr-entry")
+};
+
+
+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 LForInPrepareMap: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LForInPrepareMap(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap, "for-in-prepare-map")
+};
+
+
+class LForInCacheArray: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LForInCacheArray(LOperand* map) {
+ inputs_[0] = map;
+ }
+
+ LOperand* map() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray, "for-in-cache-array")
+
+ int idx() {
+ return HForInCacheArray::cast(this->hydrogen_value())->idx();
+ }
+};
+
+
+class LCheckMapValue: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LCheckMapValue(LOperand* value, LOperand* map) {
+ inputs_[0] = value;
+ inputs_[1] = map;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* map() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMapValue, "check-map-value")
+};
+
+
+class LLoadFieldByIndex: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadFieldByIndex(LOperand* object, LOperand* index) {
+ inputs_[0] = object;
+ inputs_[1] = index;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex, "load-field-by-index")
+};
+
+
+class LChunkBuilder;
+class LPlatformChunk: public LChunk {
+ public:
+ LPlatformChunk(CompilationInfo* info, HGraph* graph)
+ : LChunk(info, graph) { }
+
+ int GetNextSpillIndex(bool is_double);
+ LOperand* GetNextSpillSlot(bool is_double);
+};
+
+
+class LChunkBuilder BASE_EMBEDDED {
+ public:
+ LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
+ : chunk_(NULL),
+ info_(info),
+ graph_(graph),
+ zone_(graph->zone()),
+ 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_(BailoutId::None()) { }
+
+ // Build the sequence for the graph.
+ LPlatformChunk* Build();
+
+ // Declare methods that deal with the individual node types.
+#define DECLARE_DO(type) LInstruction* Do##type(H##type* node);
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
+#undef DECLARE_DO
+
+ LInstruction* DoMultiplyAdd(HMul* mul, HValue* addend);
+ LInstruction* DoMultiplySub(HValue* minuend, HMul* mul);
+ LInstruction* DoRSub(HSub* instr);
+
+ static bool HasMagicNumberForDivisor(int32_t divisor);
+ static HValue* SimplifiedDivisorForMathFloorOfDiv(HValue* val);
+
+ LInstruction* DoMathFloor(HUnaryMathOperation* instr);
+ LInstruction* DoMathRound(HUnaryMathOperation* instr);
+ LInstruction* DoMathAbs(HUnaryMathOperation* instr);
+ LInstruction* DoMathLog(HUnaryMathOperation* instr);
+ LInstruction* DoMathSin(HUnaryMathOperation* instr);
+ LInstruction* DoMathCos(HUnaryMathOperation* instr);
+ LInstruction* DoMathTan(HUnaryMathOperation* instr);
+ LInstruction* DoMathExp(HUnaryMathOperation* instr);
+ LInstruction* DoMathSqrt(HUnaryMathOperation* instr);
+ LInstruction* DoMathPowHalf(HUnaryMathOperation* instr);
+
+ private:
+ enum Status {
+ UNUSED,
+ BUILDING,
+ DONE,
+ ABORTED
+ };
+
+ LPlatformChunk* chunk() const { return chunk_; }
+ CompilationInfo* info() const { return info_; }
+ HGraph* graph() const { return graph_; }
+ Zone* zone() const { return zone_; }
+
+ 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(BailoutReason reason);
+
+ // 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 a constant operand.
+ MUST_USE_RESULT LOperand* UseConstant(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);
+
+ LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
+ int* argument_index_accumulator,
+ ZoneList<HValue*>* objects_to_materialize);
+
+ void VisitInstruction(HInstruction* current);
+
+ void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
+ LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr);
+ LInstruction* DoArithmeticD(Token::Value op,
+ HArithmeticBinaryOperation* instr);
+ LInstruction* DoArithmeticT(Token::Value op,
+ HArithmeticBinaryOperation* instr);
+
+ LPlatformChunk* chunk_;
+ CompilationInfo* info_;
+ HGraph* const graph_;
+ Zone* zone_;
+ Status status_;
+ HInstruction* current_instruction_;
+ HBasicBlock* current_block_;
+ HBasicBlock* next_block_;
+ int argument_count_;
+ LAllocator* allocator_;
+ int position_;
+ LInstruction* instruction_pending_deoptimization_environment_;
+ BailoutId pending_deoptimization_ast_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
+};
+
+#undef DECLARE_HYDROGEN_ACCESSOR
+#undef DECLARE_CONCRETE_INSTRUCTION
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_LITHIUM_ARM_H_
diff --git a/chromium/v8/src/arm/lithium-codegen-arm.cc b/chromium/v8/src/arm/lithium-codegen-arm.cc
new file mode 100644
index 00000000000..9ec80f819a0
--- /dev/null
+++ b/chromium/v8/src/arm/lithium-codegen-arm.cc
@@ -0,0 +1,5784 @@
+// Copyright 2012 the V8 project authors. 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 "arm/lithium-codegen-arm.h"
+#include "arm/lithium-gap-resolver-arm.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() {
+ LPhase phase("Z_Code generation", chunk());
+ ASSERT(is_unused());
+ status_ = GENERATING;
+
+ // 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() &&
+ GenerateDeoptJumpTable() &&
+ GenerateSafepointTable();
+}
+
+
+void LCodeGen::FinishCode(Handle<Code> code) {
+ ASSERT(is_done());
+ code->set_stack_slots(GetStackSlotCount());
+ code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
+ if (FLAG_weak_embedded_maps_in_optimized_code) {
+ RegisterDependentCodeForEmbeddedMaps(code);
+ }
+ PopulateDeoptimizationData(code);
+ info()->CommitDependencies(code);
+}
+
+
+void LCodeGen::Abort(BailoutReason reason) {
+ info()->set_bailout_reason(reason);
+ 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);
+ OS::MemCopy(copy.start(), builder.Finalize(), copy.length());
+ masm()->RecordComment(copy.start());
+}
+
+
+bool LCodeGen::GeneratePrologue() {
+ ASSERT(is_generating());
+
+ if (info()->IsOptimizing()) {
+ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info_->function()->name()->IsUtf8EqualTo(CStrVector(FLAG_stop_at))) {
+ __ stop("stop_at");
+ }
+#endif
+
+ // r1: 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;
+ __ cmp(r5, Operand::Zero());
+ __ b(eq, &ok);
+ int receiver_offset = scope()->num_parameters() * kPointerSize;
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ str(r2, MemOperand(sp, receiver_offset));
+ __ bind(&ok);
+ }
+ }
+
+ info()->set_prologue_offset(masm_->pc_offset());
+ if (NeedsEagerFrame()) {
+ if (info()->IsStub()) {
+ __ stm(db_w, sp, cp.bit() | fp.bit() | lr.bit());
+ __ Push(Smi::FromInt(StackFrame::STUB));
+ // Adjust FP to point to saved FP.
+ __ add(fp, sp, Operand(2 * kPointerSize));
+ } else {
+ PredictableCodeSizeScope predictible_code_size_scope(
+ masm_, kNoCodeAgeSequenceLength * Assembler::kInstrSize);
+ // The following three instructions must remain together and unmodified
+ // for code aging to work properly.
+ __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
+ __ nop(ip.code());
+ // Adjust FP to point to saved FP.
+ __ add(fp, sp, Operand(2 * kPointerSize));
+ }
+ frame_is_built_ = true;
+ info_->AddNoFrameRange(0, masm_->pc_offset());
+ }
+
+ // Reserve space for the stack slots needed by the code.
+ int slots = GetStackSlotCount();
+ if (slots > 0) {
+ if (FLAG_debug_code) {
+ __ sub(sp, sp, Operand(slots * kPointerSize));
+ __ push(r0);
+ __ push(r1);
+ __ add(r0, sp, Operand(slots * kPointerSize));
+ __ mov(r1, Operand(kSlotsZapValue));
+ Label loop;
+ __ bind(&loop);
+ __ sub(r0, r0, Operand(kPointerSize));
+ __ str(r1, MemOperand(r0, 2 * kPointerSize));
+ __ cmp(r0, sp);
+ __ b(ne, &loop);
+ __ pop(r1);
+ __ pop(r0);
+ } else {
+ __ sub(sp, sp, Operand(slots * kPointerSize));
+ }
+ }
+
+ if (info()->saves_caller_doubles()) {
+ Comment(";;; Save clobbered callee double registers");
+ int count = 0;
+ BitVector* doubles = chunk()->allocated_double_registers();
+ BitVector::Iterator save_iterator(doubles);
+ while (!save_iterator.Done()) {
+ __ vstr(DwVfpRegister::FromAllocationIndex(save_iterator.Current()),
+ MemOperand(sp, count * kDoubleSize));
+ save_iterator.Advance();
+ count++;
+ }
+ }
+
+ // Possibly allocate a local context.
+ int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ if (heap_slots > 0) {
+ Comment(";;; Allocate local context");
+ // Argument to NewContext is the function, which is in r1.
+ __ push(r1);
+ if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kNewFunctionContext, 1);
+ }
+ RecordSafepoint(Safepoint::kNoLazyDeopt);
+ // Context is returned in both r0 and cp. It replaces the context
+ // passed to us. It's saved in the stack and kept live in cp.
+ __ str(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.
+ __ ldr(r0, MemOperand(fp, parameter_offset));
+ // Store it in the context.
+ MemOperand target = ContextOperand(cp, var->index());
+ __ str(r0, target);
+ // Update the write barrier. This clobbers r3 and r0.
+ __ RecordWriteContextSlot(
+ cp,
+ target.offset(),
+ r0,
+ r3,
+ GetLinkRegisterState(),
+ kSaveFPRegs);
+ }
+ }
+ Comment(";;; End allocate local context");
+ }
+
+ // Trace the call.
+ if (FLAG_trace && info()->IsOptimizing()) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+ 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_);
+
+ // Don't emit code for basic blocks with a replacement.
+ if (instr->IsLabel()) {
+ emit_instructions = !LLabel::cast(instr)->HasReplacement();
+ }
+ if (!emit_instructions) continue;
+
+ if (FLAG_code_comments && instr->HasInterestingComment(this)) {
+ Comment(";;; <@%d,#%d> %s",
+ current_instruction_,
+ instr->hydrogen_value()->id(),
+ instr->Mnemonic());
+ }
+
+ RecordAndUpdatePosition(instr->position());
+
+ instr->CompileToNative(this);
+ }
+ EnsureSpaceForLazyDeopt();
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ 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];
+
+ int pos = instructions_->at(code->instruction_index())->position();
+ RecordAndUpdatePosition(pos);
+
+ Comment(";;; <@%d,#%d> "
+ "-------------------- Deferred %s --------------------",
+ code->instruction_index(),
+ code->instr()->hydrogen_value()->id(),
+ code->instr()->Mnemonic());
+ __ bind(code->entry());
+ if (NeedsDeferredFrame()) {
+ Comment(";;; Build frame");
+ ASSERT(!frame_is_built_);
+ ASSERT(info()->IsStub());
+ frame_is_built_ = true;
+ __ stm(db_w, sp, cp.bit() | fp.bit() | lr.bit());
+ __ mov(scratch0(), Operand(Smi::FromInt(StackFrame::STUB)));
+ __ push(scratch0());
+ __ add(fp, sp, Operand(2 * kPointerSize));
+ Comment(";;; Deferred code");
+ }
+ code->Generate();
+ if (NeedsDeferredFrame()) {
+ Comment(";;; Destroy frame");
+ ASSERT(frame_is_built_);
+ __ pop(ip);
+ __ ldm(ia_w, sp, cp.bit() | fp.bit() | lr.bit());
+ frame_is_built_ = false;
+ }
+ __ jmp(code->exit());
+ }
+ }
+
+ // Force constant pool emission at the end of the deferred code to make
+ // sure that no constant pools are emitted after.
+ masm()->CheckConstPool(true, false);
+
+ return !is_aborted();
+}
+
+
+bool LCodeGen::GenerateDeoptJumpTable() {
+ // Check that the jump table is accessible from everywhere in the function
+ // 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.
+ // Each entry in the jump table generates one instruction and inlines one
+ // 32bit data after it.
+ if (!is_int24((masm()->pc_offset() / Assembler::kInstrSize) +
+ deopt_jump_table_.length() * 7)) {
+ Abort(kGeneratedCodeIsTooLarge);
+ }
+
+ if (deopt_jump_table_.length() > 0) {
+ Comment(";;; -------------------- Jump table --------------------");
+ }
+ Label table_start;
+ __ bind(&table_start);
+ Label needs_frame;
+ for (int i = 0; i < deopt_jump_table_.length(); i++) {
+ __ bind(&deopt_jump_table_[i].label);
+ Address entry = deopt_jump_table_[i].address;
+ Deoptimizer::BailoutType type = deopt_jump_table_[i].bailout_type;
+ int id = Deoptimizer::GetDeoptimizationId(isolate(), entry, type);
+ if (id == Deoptimizer::kNotDeoptimizationEntry) {
+ Comment(";;; jump table entry %d.", i);
+ } else {
+ Comment(";;; jump table entry %d: deoptimization bailout %d.", i, id);
+ }
+ if (deopt_jump_table_[i].needs_frame) {
+ __ mov(ip, Operand(ExternalReference::ForDeoptEntry(entry)));
+ if (needs_frame.is_bound()) {
+ __ b(&needs_frame);
+ } else {
+ __ bind(&needs_frame);
+ __ stm(db_w, sp, cp.bit() | fp.bit() | lr.bit());
+ // This variant of deopt can only be used with stubs. Since we don't
+ // have a function pointer to install in the stack frame that we're
+ // building, install a special marker there instead.
+ ASSERT(info()->IsStub());
+ __ mov(scratch0(), Operand(Smi::FromInt(StackFrame::STUB)));
+ __ push(scratch0());
+ __ add(fp, sp, Operand(2 * kPointerSize));
+ __ mov(lr, Operand(pc), LeaveCC, al);
+ __ mov(pc, ip);
+ }
+ } else {
+ __ mov(lr, Operand(pc), LeaveCC, al);
+ __ mov(pc, Operand(ExternalReference::ForDeoptEntry(entry)));
+ }
+ masm()->CheckConstPool(false, false);
+ }
+
+ // Force constant pool emission at the end of the deopt jump table to make
+ // sure that no constant pools are emitted after.
+ masm()->CheckConstPool(true, false);
+
+ // The deoptimization jump table 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::GenerateSafepointTable() {
+ ASSERT(is_done());
+ safepoints_.Emit(masm(), GetStackSlotCount());
+ return !is_aborted();
+}
+
+
+Register LCodeGen::ToRegister(int index) const {
+ return Register::FromAllocationIndex(index);
+}
+
+
+DwVfpRegister LCodeGen::ToDoubleRegister(int index) const {
+ return DwVfpRegister::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);
+ HConstant* constant = chunk_->LookupConstant(const_op);
+ Handle<Object> literal = constant->handle();
+ 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(kEmitLoadRegisterUnsupportedDoubleImmediate);
+ } else {
+ ASSERT(r.IsTagged());
+ __ LoadObject(scratch, literal);
+ }
+ return scratch;
+ } else if (op->IsStackSlot() || op->IsArgument()) {
+ __ ldr(scratch, ToMemOperand(op));
+ return scratch;
+ }
+ UNREACHABLE();
+ return scratch;
+}
+
+
+DwVfpRegister LCodeGen::ToDoubleRegister(LOperand* op) const {
+ ASSERT(op->IsDoubleRegister());
+ return ToDoubleRegister(op->index());
+}
+
+
+DwVfpRegister LCodeGen::EmitLoadDoubleRegister(LOperand* op,
+ SwVfpRegister flt_scratch,
+ DwVfpRegister dbl_scratch) {
+ if (op->IsDoubleRegister()) {
+ return ToDoubleRegister(op->index());
+ } else if (op->IsConstantOperand()) {
+ LConstantOperand* const_op = LConstantOperand::cast(op);
+ HConstant* constant = chunk_->LookupConstant(const_op);
+ Handle<Object> literal = constant->handle();
+ Representation r = chunk_->LookupLiteralRepresentation(const_op);
+ if (r.IsInteger32()) {
+ ASSERT(literal->IsNumber());
+ __ mov(ip, Operand(static_cast<int32_t>(literal->Number())));
+ __ vmov(flt_scratch, ip);
+ __ vcvt_f64_s32(dbl_scratch, flt_scratch);
+ return dbl_scratch;
+ } else if (r.IsDouble()) {
+ Abort(kUnsupportedDoubleImmediate);
+ } else if (r.IsTagged()) {
+ Abort(kUnsupportedTaggedImmediate);
+ }
+ } else if (op->IsStackSlot() || op->IsArgument()) {
+ // TODO(regis): Why is vldr not taking a MemOperand?
+ // __ vldr(dbl_scratch, ToMemOperand(op));
+ MemOperand mem_op = ToMemOperand(op);
+ __ vldr(dbl_scratch, mem_op.rn(), mem_op.offset());
+ return dbl_scratch;
+ }
+ UNREACHABLE();
+ return dbl_scratch;
+}
+
+
+Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(chunk_->LookupLiteralRepresentation(op).IsSmiOrTagged());
+ return constant->handle();
+}
+
+
+bool LCodeGen::IsInteger32(LConstantOperand* op) const {
+ return chunk_->LookupLiteralRepresentation(op).IsSmiOrInteger32();
+}
+
+
+bool LCodeGen::IsSmi(LConstantOperand* op) const {
+ return chunk_->LookupLiteralRepresentation(op).IsSmi();
+}
+
+
+int32_t LCodeGen::ToInteger32(LConstantOperand* op) const {
+ return ToRepresentation(op, Representation::Integer32());
+}
+
+
+int32_t LCodeGen::ToRepresentation(LConstantOperand* op,
+ const Representation& r) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ int32_t value = constant->Integer32Value();
+ if (r.IsInteger32()) return value;
+ ASSERT(r.IsSmiOrTagged());
+ return reinterpret_cast<int32_t>(Smi::FromInt(value));
+}
+
+
+Smi* LCodeGen::ToSmi(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ return Smi::FromInt(constant->Integer32Value());
+}
+
+
+double LCodeGen::ToDouble(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(constant->HasDoubleValue());
+ return constant->DoubleValue();
+}
+
+
+Operand LCodeGen::ToOperand(LOperand* op) {
+ if (op->IsConstantOperand()) {
+ LConstantOperand* const_op = LConstantOperand::cast(op);
+ HConstant* constant = chunk()->LookupConstant(const_op);
+ Representation r = chunk_->LookupLiteralRepresentation(const_op);
+ if (r.IsSmi()) {
+ ASSERT(constant->HasSmiValue());
+ return Operand(Smi::FromInt(constant->Integer32Value()));
+ } else if (r.IsInteger32()) {
+ ASSERT(constant->HasInteger32Value());
+ return Operand(constant->Integer32Value());
+ } else if (r.IsDouble()) {
+ Abort(kToOperandUnsupportedDoubleImmediate);
+ }
+ ASSERT(r.IsTagged());
+ return Operand(constant->handle());
+ } else if (op->IsRegister()) {
+ return Operand(ToRegister(op));
+ } else if (op->IsDoubleRegister()) {
+ Abort(kToOperandIsDoubleRegisterUnimplemented);
+ return Operand::Zero();
+ }
+ // Stack slots not implemented, use ToMemOperand instead.
+ UNREACHABLE();
+ return Operand::Zero();
+}
+
+
+MemOperand LCodeGen::ToMemOperand(LOperand* op) const {
+ ASSERT(!op->IsRegister());
+ ASSERT(!op->IsDoubleRegister());
+ ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot());
+ return MemOperand(fp, StackSlotOffset(op->index()));
+}
+
+
+MemOperand LCodeGen::ToHighMemOperand(LOperand* op) const {
+ ASSERT(op->IsDoubleStackSlot());
+ return MemOperand(fp, StackSlotOffset(op->index()) + 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->translation_size();
+ // The output frame height does not include the parameters.
+ int height = translation_size - environment->parameter_count();
+
+ WriteTranslation(environment->outer(), translation);
+ bool has_closure_id = !info()->closure().is_null() &&
+ !info()->closure().is_identical_to(environment->closure());
+ int closure_id = has_closure_id
+ ? DefineDeoptimizationLiteral(environment->closure())
+ : Translation::kSelfLiteralId;
+
+ switch (environment->frame_type()) {
+ case JS_FUNCTION:
+ translation->BeginJSFrame(environment->ast_id(), closure_id, height);
+ break;
+ case JS_CONSTRUCT:
+ translation->BeginConstructStubFrame(closure_id, translation_size);
+ break;
+ case JS_GETTER:
+ ASSERT(translation_size == 1);
+ ASSERT(height == 0);
+ translation->BeginGetterStubFrame(closure_id);
+ break;
+ case JS_SETTER:
+ ASSERT(translation_size == 2);
+ ASSERT(height == 0);
+ translation->BeginSetterStubFrame(closure_id);
+ break;
+ case STUB:
+ translation->BeginCompiledStubFrame();
+ break;
+ case ARGUMENTS_ADAPTOR:
+ translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
+ break;
+ }
+
+ int object_index = 0;
+ int dematerialized_index = 0;
+ for (int i = 0; i < translation_size; ++i) {
+ LOperand* value = environment->values()->at(i);
+ AddToTranslation(environment,
+ translation,
+ value,
+ environment->HasTaggedValueAt(i),
+ environment->HasUint32ValueAt(i),
+ &object_index,
+ &dematerialized_index);
+ }
+}
+
+
+void LCodeGen::AddToTranslation(LEnvironment* environment,
+ Translation* translation,
+ LOperand* op,
+ bool is_tagged,
+ bool is_uint32,
+ int* object_index_pointer,
+ int* dematerialized_index_pointer) {
+ if (op == LEnvironment::materialization_marker()) {
+ int object_index = (*object_index_pointer)++;
+ if (environment->ObjectIsDuplicateAt(object_index)) {
+ int dupe_of = environment->ObjectDuplicateOfAt(object_index);
+ translation->DuplicateObject(dupe_of);
+ return;
+ }
+ int object_length = environment->ObjectLengthAt(object_index);
+ if (environment->ObjectIsArgumentsAt(object_index)) {
+ translation->BeginArgumentsObject(object_length);
+ } else {
+ translation->BeginCapturedObject(object_length);
+ }
+ int dematerialized_index = *dematerialized_index_pointer;
+ int env_offset = environment->translation_size() + dematerialized_index;
+ *dematerialized_index_pointer += object_length;
+ for (int i = 0; i < object_length; ++i) {
+ LOperand* value = environment->values()->at(env_offset + i);
+ AddToTranslation(environment,
+ translation,
+ value,
+ environment->HasTaggedValueAt(env_offset + i),
+ environment->HasUint32ValueAt(env_offset + i),
+ object_index_pointer,
+ dematerialized_index_pointer);
+ }
+ return;
+ }
+
+ if (op->IsStackSlot()) {
+ if (is_tagged) {
+ translation->StoreStackSlot(op->index());
+ } else if (is_uint32) {
+ translation->StoreUint32StackSlot(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 if (is_uint32) {
+ translation->StoreUint32Register(reg);
+ } else {
+ translation->StoreInt32Register(reg);
+ }
+ } else if (op->IsDoubleRegister()) {
+ DoubleRegister reg = ToDoubleRegister(op);
+ translation->StoreDoubleRegister(reg);
+ } else if (op->IsConstantOperand()) {
+ HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op));
+ int src_index = DefineDeoptimizationLiteral(constant->handle());
+ translation->StoreLiteral(src_index);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ TargetAddressStorageMode storage_mode) {
+ CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT, storage_mode);
+}
+
+
+void LCodeGen::CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ SafepointMode safepoint_mode,
+ TargetAddressStorageMode storage_mode) {
+ EnsureSpaceForLazyDeopt();
+ ASSERT(instr != NULL);
+ // Block literal pool emission to ensure nop indicating no inlined smi code
+ // is in the correct position.
+ Assembler::BlockConstPoolScope block_const_pool(masm());
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+ __ Call(code, mode, TypeFeedbackId::None(), al, storage_mode);
+ RecordSafepointWithLazyDeopt(instr, safepoint_mode);
+
+ // Signal that we don't inline smi code before these stubs in the
+ // optimizing code generator.
+ if (code->kind() == Code::BINARY_OP_IC ||
+ code->kind() == Code::COMPARE_IC) {
+ __ nop();
+ }
+}
+
+
+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->frame_type() == JS_FUNCTION) {
+ ++jsframe_count;
+ }
+ }
+ Translation translation(&translations_, frame_count, jsframe_count, zone());
+ 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, zone());
+ }
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition condition,
+ LEnvironment* environment,
+ Deoptimizer::BailoutType bailout_type) {
+ RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt);
+ ASSERT(environment->HasBeenRegistered());
+ int id = environment->deoptimization_index();
+ ASSERT(info()->IsOptimizing() || info()->IsStub());
+ Address entry =
+ Deoptimizer::GetDeoptimizationEntry(isolate(), id, bailout_type);
+ if (entry == NULL) {
+ Abort(kBailoutWasNotPrepared);
+ return;
+ }
+
+ ASSERT(FLAG_deopt_every_n_times < 2); // Other values not supported on ARM.
+ if (FLAG_deopt_every_n_times == 1 &&
+ !info()->IsStub() &&
+ info()->opt_count() == id) {
+ ASSERT(frame_is_built_);
+ __ Call(entry, RelocInfo::RUNTIME_ENTRY);
+ return;
+ }
+
+ if (info()->ShouldTrapOnDeopt()) {
+ __ stop("trap_on_deopt", condition);
+ }
+
+ ASSERT(info()->IsStub() || frame_is_built_);
+ if (condition == al && frame_is_built_) {
+ __ Call(entry, RelocInfo::RUNTIME_ENTRY);
+ } else {
+ // We often have several deopts to the same entry, reuse the last
+ // jump entry if this is the case.
+ if (deopt_jump_table_.is_empty() ||
+ (deopt_jump_table_.last().address != entry) ||
+ (deopt_jump_table_.last().bailout_type != bailout_type) ||
+ (deopt_jump_table_.last().needs_frame != !frame_is_built_)) {
+ Deoptimizer::JumpTableEntry table_entry(entry,
+ bailout_type,
+ !frame_is_built_);
+ deopt_jump_table_.Add(table_entry, zone());
+ }
+ __ b(condition, &deopt_jump_table_.last().label);
+ }
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition condition,
+ LEnvironment* environment) {
+ Deoptimizer::BailoutType bailout_type = info()->IsStub()
+ ? Deoptimizer::LAZY
+ : Deoptimizer::EAGER;
+ DeoptimizeIf(condition, environment, bailout_type);
+}
+
+
+void LCodeGen::RegisterDependentCodeForEmbeddedMaps(Handle<Code> code) {
+ ZoneList<Handle<Map> > maps(1, zone());
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) {
+ RelocInfo::Mode mode = it.rinfo()->rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT &&
+ it.rinfo()->target_object()->IsMap()) {
+ Handle<Map> map(Map::cast(it.rinfo()->target_object()));
+ if (map->CanTransition()) {
+ maps.Add(map, zone());
+ }
+ }
+ }
+#ifdef VERIFY_HEAP
+ // This disables verification of weak embedded maps after full GC.
+ // AddDependentCode can cause a GC, which would observe the state where
+ // this code is not yet in the depended code lists of the embedded maps.
+ NoWeakEmbeddedMapsVerificationScope disable_verification_of_embedded_maps;
+#endif
+ for (int i = 0; i < maps.length(); i++) {
+ maps.at(i)->AddDependentCode(DependentCode::kWeaklyEmbeddedGroup, code);
+ }
+}
+
+
+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(isolate()->factory());
+ data->SetTranslationByteArray(*translations);
+ data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_));
+
+ Handle<FixedArray> literals =
+ factory()->NewFixedArray(deoptimization_literals_.length(), TENURED);
+ { AllowDeferredHandleDereference copy_handles;
+ 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().ToInt()));
+ 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, 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, zone());
+ 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(), zone());
+ } else if (pointer->IsRegister() && (kind & Safepoint::kWithRegisters)) {
+ safepoint.DefinePointerRegister(ToRegister(pointer), zone());
+ }
+ }
+ if (kind & Safepoint::kWithRegisters) {
+ // Register cp always contains a pointer to the context.
+ safepoint.DefinePointerRegister(cp, zone());
+ }
+}
+
+
+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, zone());
+ 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::RecordAndUpdatePosition(int position) {
+ if (position >= 0 && position != old_position_) {
+ masm()->positions_recorder()->RecordPosition(position);
+ old_position_ = position;
+ }
+}
+
+
+static const char* LabelType(LLabel* label) {
+ if (label->is_loop_header()) return " (loop header)";
+ if (label->is_osr_entry()) return " (OSR entry)";
+ return "";
+}
+
+
+void LCodeGen::DoLabel(LLabel* label) {
+ Comment(";;; <@%d,#%d> -------------------- B%d%s --------------------",
+ current_instruction_,
+ label->hydrogen_value()->id(),
+ label->block_id(),
+ LabelType(label));
+ __ 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(r0));
+ switch (instr->hydrogen()->major_key()) {
+ case CodeStub::RegExpConstructResult: {
+ RegExpConstructResultStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::RegExpExec: {
+ RegExpExecStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::SubString: {
+ SubStringStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::NumberToString: {
+ NumberToStringStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::StringCompare: {
+ StringCompareStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::TranscendentalCache: {
+ __ ldr(r0, MemOperand(sp, 0));
+ TranscendentalCacheStub stub(instr->transcendental_type(),
+ TranscendentalCacheStub::TAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) {
+ // Record the address of the first unknown OSR value as the place to enter.
+ if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset();
+}
+
+
+void LCodeGen::DoModI(LModI* instr) {
+ HMod* hmod = instr->hydrogen();
+ HValue* left = hmod->left();
+ HValue* right = hmod->right();
+ if (hmod->HasPowerOf2Divisor()) {
+ // TODO(svenpanne) We should really do the strength reduction on the
+ // Hydrogen level.
+ Register left_reg = ToRegister(instr->left());
+ Register result_reg = ToRegister(instr->result());
+
+ // Note: The code below even works when right contains kMinInt.
+ int32_t divisor = Abs(right->GetInteger32Constant());
+
+ Label left_is_not_negative, done;
+ if (left->CanBeNegative()) {
+ __ cmp(left_reg, Operand::Zero());
+ __ b(pl, &left_is_not_negative);
+ __ rsb(result_reg, left_reg, Operand::Zero());
+ __ and_(result_reg, result_reg, Operand(divisor - 1));
+ __ rsb(result_reg, result_reg, Operand::Zero(), SetCC);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(eq, instr->environment());
+ }
+ __ b(&done);
+ }
+
+ __ bind(&left_is_not_negative);
+ __ and_(result_reg, left_reg, Operand(divisor - 1));
+ __ bind(&done);
+
+ } else if (hmod->fixed_right_arg().has_value) {
+ Register left_reg = ToRegister(instr->left());
+ Register right_reg = ToRegister(instr->right());
+ Register result_reg = ToRegister(instr->result());
+
+ int32_t divisor = hmod->fixed_right_arg().value;
+ ASSERT(IsPowerOf2(divisor));
+
+ // Check if our assumption of a fixed right operand still holds.
+ __ cmp(right_reg, Operand(divisor));
+ DeoptimizeIf(ne, instr->environment());
+
+ Label left_is_not_negative, done;
+ if (left->CanBeNegative()) {
+ __ cmp(left_reg, Operand::Zero());
+ __ b(pl, &left_is_not_negative);
+ __ rsb(result_reg, left_reg, Operand::Zero());
+ __ and_(result_reg, result_reg, Operand(divisor - 1));
+ __ rsb(result_reg, result_reg, Operand::Zero(), SetCC);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(eq, instr->environment());
+ }
+ __ b(&done);
+ }
+
+ __ bind(&left_is_not_negative);
+ __ and_(result_reg, left_reg, Operand(divisor - 1));
+ __ bind(&done);
+
+ } else if (CpuFeatures::IsSupported(SUDIV)) {
+ CpuFeatureScope scope(masm(), SUDIV);
+
+ Register left_reg = ToRegister(instr->left());
+ Register right_reg = ToRegister(instr->right());
+ Register result_reg = ToRegister(instr->result());
+
+ Label done;
+ // Check for x % 0, sdiv might signal an exception. We have to deopt in this
+ // case because we can't return a NaN.
+ if (right->CanBeZero()) {
+ __ cmp(right_reg, Operand::Zero());
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+ // Check for kMinInt % -1, sdiv will return kMinInt, which is not what we
+ // want. We have to deopt if we care about -0, because we can't return that.
+ if (left->RangeCanInclude(kMinInt) && right->RangeCanInclude(-1)) {
+ Label no_overflow_possible;
+ __ cmp(left_reg, Operand(kMinInt));
+ __ b(ne, &no_overflow_possible);
+ __ cmp(right_reg, Operand(-1));
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(eq, instr->environment());
+ } else {
+ __ b(ne, &no_overflow_possible);
+ __ mov(result_reg, Operand::Zero());
+ __ jmp(&done);
+ }
+ __ bind(&no_overflow_possible);
+ }
+
+ // For 'r3 = r1 % r2' we can have the following ARM code:
+ // sdiv r3, r1, r2
+ // mls r3, r3, r2, r1
+
+ __ sdiv(result_reg, left_reg, right_reg);
+ __ mls(result_reg, result_reg, right_reg, left_reg);
+
+ // If we care about -0, test if the dividend is <0 and the result is 0.
+ if (left->CanBeNegative() &&
+ hmod->CanBeZero() &&
+ hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ cmp(result_reg, Operand::Zero());
+ __ b(ne, &done);
+ __ cmp(left_reg, Operand::Zero());
+ DeoptimizeIf(lt, instr->environment());
+ }
+ __ bind(&done);
+
+ } else {
+ // General case, without any SDIV support.
+ Register left_reg = ToRegister(instr->left());
+ Register right_reg = ToRegister(instr->right());
+ Register result_reg = ToRegister(instr->result());
+ Register scratch = scratch0();
+ ASSERT(!scratch.is(left_reg));
+ ASSERT(!scratch.is(right_reg));
+ ASSERT(!scratch.is(result_reg));
+ DwVfpRegister dividend = ToDoubleRegister(instr->temp());
+ DwVfpRegister divisor = ToDoubleRegister(instr->temp2());
+ ASSERT(!divisor.is(dividend));
+ LowDwVfpRegister quotient = double_scratch0();
+ ASSERT(!quotient.is(dividend));
+ ASSERT(!quotient.is(divisor));
+
+ Label done;
+ // Check for x % 0, we have to deopt in this case because we can't return a
+ // NaN.
+ if (right->CanBeZero()) {
+ __ cmp(right_reg, Operand::Zero());
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+ __ Move(result_reg, left_reg);
+ // Load the arguments in VFP registers. The divisor value is preloaded
+ // before. Be careful that 'right_reg' is only live on entry.
+ // TODO(svenpanne) The last comments seems to be wrong nowadays.
+ __ vmov(double_scratch0().low(), left_reg);
+ __ vcvt_f64_s32(dividend, double_scratch0().low());
+ __ vmov(double_scratch0().low(), right_reg);
+ __ vcvt_f64_s32(divisor, double_scratch0().low());
+
+ // We do not care about the sign of the divisor. Note that we still handle
+ // the kMinInt % -1 case correctly, though.
+ __ vabs(divisor, divisor);
+ // Compute the quotient and round it to a 32bit integer.
+ __ vdiv(quotient, dividend, divisor);
+ __ vcvt_s32_f64(quotient.low(), quotient);
+ __ vcvt_f64_s32(quotient, quotient.low());
+
+ // Compute the remainder in result.
+ __ vmul(double_scratch0(), divisor, quotient);
+ __ vcvt_s32_f64(double_scratch0().low(), double_scratch0());
+ __ vmov(scratch, double_scratch0().low());
+ __ sub(result_reg, left_reg, scratch, SetCC);
+
+ // If we care about -0, test if the dividend is <0 and the result is 0.
+ if (left->CanBeNegative() &&
+ hmod->CanBeZero() &&
+ hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ b(ne, &done);
+ __ cmp(left_reg, Operand::Zero());
+ DeoptimizeIf(mi, instr->environment());
+ }
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::EmitSignedIntegerDivisionByConstant(
+ Register result,
+ Register dividend,
+ int32_t divisor,
+ Register remainder,
+ Register scratch,
+ LEnvironment* environment) {
+ ASSERT(!AreAliased(dividend, scratch, ip));
+ ASSERT(LChunkBuilder::HasMagicNumberForDivisor(divisor));
+
+ uint32_t divisor_abs = abs(divisor);
+
+ int32_t power_of_2_factor =
+ CompilerIntrinsics::CountTrailingZeros(divisor_abs);
+
+ switch (divisor_abs) {
+ case 0:
+ DeoptimizeIf(al, environment);
+ return;
+
+ case 1:
+ if (divisor > 0) {
+ __ Move(result, dividend);
+ } else {
+ __ rsb(result, dividend, Operand::Zero(), SetCC);
+ DeoptimizeIf(vs, environment);
+ }
+ // Compute the remainder.
+ __ mov(remainder, Operand::Zero());
+ return;
+
+ default:
+ if (IsPowerOf2(divisor_abs)) {
+ // Branch and condition free code for integer division by a power
+ // of two.
+ int32_t power = WhichPowerOf2(divisor_abs);
+ if (power > 1) {
+ __ mov(scratch, Operand(dividend, ASR, power - 1));
+ }
+ __ add(scratch, dividend, Operand(scratch, LSR, 32 - power));
+ __ mov(result, Operand(scratch, ASR, power));
+ // Negate if necessary.
+ // We don't need to check for overflow because the case '-1' is
+ // handled separately.
+ if (divisor < 0) {
+ ASSERT(divisor != -1);
+ __ rsb(result, result, Operand::Zero());
+ }
+ // Compute the remainder.
+ if (divisor > 0) {
+ __ sub(remainder, dividend, Operand(result, LSL, power));
+ } else {
+ __ add(remainder, dividend, Operand(result, LSL, power));
+ }
+ return;
+ } else {
+ // Use magic numbers for a few specific divisors.
+ // Details and proofs can be found in:
+ // - Hacker's Delight, Henry S. Warren, Jr.
+ // - The PowerPC Compiler Writer’s Guide
+ // and probably many others.
+ //
+ // We handle
+ // <divisor with magic numbers> * <power of 2>
+ // but not
+ // <divisor with magic numbers> * <other divisor with magic numbers>
+ DivMagicNumbers magic_numbers =
+ DivMagicNumberFor(divisor_abs >> power_of_2_factor);
+ // Branch and condition free code for integer division by a power
+ // of two.
+ const int32_t M = magic_numbers.M;
+ const int32_t s = magic_numbers.s + power_of_2_factor;
+
+ __ mov(ip, Operand(M));
+ __ smull(ip, scratch, dividend, ip);
+ if (M < 0) {
+ __ add(scratch, scratch, Operand(dividend));
+ }
+ if (s > 0) {
+ __ mov(scratch, Operand(scratch, ASR, s));
+ }
+ __ add(result, scratch, Operand(dividend, LSR, 31));
+ if (divisor < 0) __ rsb(result, result, Operand::Zero());
+ // Compute the remainder.
+ __ mov(ip, Operand(divisor));
+ // This sequence could be replaced with 'mls' when
+ // it gets implemented.
+ __ mul(scratch, result, ip);
+ __ sub(remainder, dividend, scratch);
+ }
+ }
+}
+
+
+void LCodeGen::DoDivI(LDivI* instr) {
+ if (instr->hydrogen()->HasPowerOf2Divisor()) {
+ Register dividend = ToRegister(instr->left());
+ int32_t divisor = instr->hydrogen()->right()->GetInteger32Constant();
+ int32_t test_value = 0;
+ int32_t power = 0;
+
+ if (divisor > 0) {
+ test_value = divisor - 1;
+ power = WhichPowerOf2(divisor);
+ } else {
+ // Check for (0 / -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ tst(dividend, Operand(dividend));
+ DeoptimizeIf(eq, instr->environment());
+ }
+ // Check for (kMinInt / -1).
+ if (divisor == -1 && instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ __ cmp(dividend, Operand(kMinInt));
+ DeoptimizeIf(eq, instr->environment());
+ }
+ test_value = - divisor - 1;
+ power = WhichPowerOf2(-divisor);
+ }
+
+ if (test_value != 0) {
+ if (instr->hydrogen()->CheckFlag(
+ HInstruction::kAllUsesTruncatingToInt32)) {
+ __ cmp(dividend, Operand(0));
+ __ rsb(dividend, dividend, Operand(0), LeaveCC, lt);
+ __ mov(dividend, Operand(dividend, ASR, power));
+ if (divisor > 0) __ rsb(dividend, dividend, Operand(0), LeaveCC, lt);
+ return; // Don't fall through to "__ rsb" below.
+ } else {
+ // Deoptimize if remainder is not 0.
+ __ tst(dividend, Operand(test_value));
+ DeoptimizeIf(ne, instr->environment());
+ __ mov(dividend, Operand(dividend, ASR, power));
+ }
+ }
+ if (divisor < 0) __ rsb(dividend, dividend, Operand(0));
+
+ return;
+ }
+
+ const Register left = ToRegister(instr->left());
+ const Register right = ToRegister(instr->right());
+ const Register result = ToRegister(instr->result());
+
+ // Check for x / 0.
+ if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
+ __ cmp(right, Operand::Zero());
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+ // Check for (0 / -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label left_not_zero;
+ __ cmp(left, Operand::Zero());
+ __ b(ne, &left_not_zero);
+ __ cmp(right, Operand::Zero());
+ DeoptimizeIf(mi, instr->environment());
+ __ bind(&left_not_zero);
+ }
+
+ // Check for (kMinInt / -1).
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ Label left_not_min_int;
+ __ cmp(left, Operand(kMinInt));
+ __ b(ne, &left_not_min_int);
+ __ cmp(right, Operand(-1));
+ DeoptimizeIf(eq, instr->environment());
+ __ bind(&left_not_min_int);
+ }
+
+ if (CpuFeatures::IsSupported(SUDIV)) {
+ CpuFeatureScope scope(masm(), SUDIV);
+ __ sdiv(result, left, right);
+
+ if (!instr->hydrogen()->CheckFlag(
+ HInstruction::kAllUsesTruncatingToInt32)) {
+ // Compute remainder and deopt if it's not zero.
+ const Register remainder = scratch0();
+ __ mls(remainder, result, right, left);
+ __ cmp(remainder, Operand::Zero());
+ DeoptimizeIf(ne, instr->environment());
+ }
+ } else {
+ const DoubleRegister vleft = ToDoubleRegister(instr->temp());
+ const DoubleRegister vright = double_scratch0();
+ __ vmov(double_scratch0().low(), left);
+ __ vcvt_f64_s32(vleft, double_scratch0().low());
+ __ vmov(double_scratch0().low(), right);
+ __ vcvt_f64_s32(vright, double_scratch0().low());
+ __ vdiv(vleft, vleft, vright); // vleft now contains the result.
+ __ vcvt_s32_f64(double_scratch0().low(), vleft);
+ __ vmov(result, double_scratch0().low());
+
+ if (!instr->hydrogen()->CheckFlag(
+ HInstruction::kAllUsesTruncatingToInt32)) {
+ // Deopt if exact conversion to integer was not possible.
+ // Use vright as scratch register.
+ __ vcvt_f64_s32(double_scratch0(), double_scratch0().low());
+ __ VFPCompareAndSetFlags(vleft, double_scratch0());
+ DeoptimizeIf(ne, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoMultiplyAddD(LMultiplyAddD* instr) {
+ DwVfpRegister addend = ToDoubleRegister(instr->addend());
+ DwVfpRegister multiplier = ToDoubleRegister(instr->multiplier());
+ DwVfpRegister multiplicand = ToDoubleRegister(instr->multiplicand());
+
+ // This is computed in-place.
+ ASSERT(addend.is(ToDoubleRegister(instr->result())));
+
+ __ vmla(addend, multiplier, multiplicand);
+}
+
+
+void LCodeGen::DoMultiplySubD(LMultiplySubD* instr) {
+ DwVfpRegister minuend = ToDoubleRegister(instr->minuend());
+ DwVfpRegister multiplier = ToDoubleRegister(instr->multiplier());
+ DwVfpRegister multiplicand = ToDoubleRegister(instr->multiplicand());
+
+ // This is computed in-place.
+ ASSERT(minuend.is(ToDoubleRegister(instr->result())));
+
+ __ vmls(minuend, multiplier, multiplicand);
+}
+
+
+void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
+ const Register result = ToRegister(instr->result());
+ const Register left = ToRegister(instr->left());
+ const Register remainder = ToRegister(instr->temp());
+ const Register scratch = scratch0();
+
+ if (!CpuFeatures::IsSupported(SUDIV)) {
+ // If the CPU doesn't support sdiv instruction, we only optimize when we
+ // have magic numbers for the divisor. The standard integer division routine
+ // is usually slower than transitionning to VFP.
+ ASSERT(instr->right()->IsConstantOperand());
+ int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right()));
+ ASSERT(LChunkBuilder::HasMagicNumberForDivisor(divisor));
+ if (divisor < 0) {
+ __ cmp(left, Operand::Zero());
+ DeoptimizeIf(eq, instr->environment());
+ }
+ EmitSignedIntegerDivisionByConstant(result,
+ left,
+ divisor,
+ remainder,
+ scratch,
+ instr->environment());
+ // We performed a truncating division. Correct the result if necessary.
+ __ cmp(remainder, Operand::Zero());
+ __ teq(remainder, Operand(divisor), ne);
+ __ sub(result, result, Operand(1), LeaveCC, mi);
+ } else {
+ CpuFeatureScope scope(masm(), SUDIV);
+ const Register right = ToRegister(instr->right());
+
+ // Check for x / 0.
+ __ cmp(right, Operand::Zero());
+ DeoptimizeIf(eq, instr->environment());
+
+ // Check for (kMinInt / -1).
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ Label left_not_min_int;
+ __ cmp(left, Operand(kMinInt));
+ __ b(ne, &left_not_min_int);
+ __ cmp(right, Operand(-1));
+ DeoptimizeIf(eq, instr->environment());
+ __ bind(&left_not_min_int);
+ }
+
+ // Check for (0 / -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ cmp(right, Operand::Zero());
+ __ cmp(left, Operand::Zero(), mi);
+ // "right" can't be null because the code would have already been
+ // deoptimized. The Z flag is set only if (right < 0) and (left == 0).
+ // In this case we need to deoptimize to produce a -0.
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+ Label done;
+ __ sdiv(result, left, right);
+ // If both operands have the same sign then we are done.
+ __ eor(remainder, left, Operand(right), SetCC);
+ __ b(pl, &done);
+
+ // Check if the result needs to be corrected.
+ __ mls(remainder, result, right, left);
+ __ cmp(remainder, Operand::Zero());
+ __ sub(result, result, Operand(1), LeaveCC, ne);
+
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoMulI(LMulI* instr) {
+ Register scratch = scratch0();
+ Register result = ToRegister(instr->result());
+ // Note that result may alias left.
+ Register left = ToRegister(instr->left());
+ LOperand* right_op = instr->right();
+
+ 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 = ToRepresentation(
+ LConstantOperand::cast(right_op),
+ instr->hydrogen()->right()->representation());
+
+ 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.
+ __ cmp(left, Operand::Zero());
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+ switch (constant) {
+ case -1:
+ __ rsb(result, left, Operand::Zero());
+ 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.
+ __ cmp(left, Operand::Zero());
+ DeoptimizeIf(mi, instr->environment());
+ }
+ __ mov(result, Operand::Zero());
+ break;
+ case 1:
+ __ 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);
+ __ mov(result, Operand(left, LSL, shift));
+ } else if (IsPowerOf2(constant_abs - 1)) {
+ int32_t shift = WhichPowerOf2(constant_abs - 1);
+ __ add(result, left, Operand(left, LSL, shift));
+ } else if (IsPowerOf2(constant_abs + 1)) {
+ int32_t shift = WhichPowerOf2(constant_abs + 1);
+ __ rsb(result, left, Operand(left, LSL, shift));
+ }
+
+ // Correct the sign of the result is the constant is negative.
+ if (constant < 0) __ rsb(result, result, Operand::Zero());
+
+ } else {
+ // Generate standard code.
+ __ mov(ip, Operand(constant));
+ __ mul(result, left, ip);
+ }
+ }
+
+ } else {
+ Register right = EmitLoadRegister(right_op, scratch);
+ if (bailout_on_minus_zero) {
+ __ orr(ToRegister(instr->temp()), left, right);
+ }
+
+ if (can_overflow) {
+ // scratch:result = left * right.
+ if (instr->hydrogen()->representation().IsSmi()) {
+ __ SmiUntag(result, left);
+ __ smull(result, scratch, result, right);
+ } else {
+ __ smull(result, scratch, left, right);
+ }
+ __ cmp(scratch, Operand(result, ASR, 31));
+ DeoptimizeIf(ne, instr->environment());
+ } else {
+ if (instr->hydrogen()->representation().IsSmi()) {
+ __ SmiUntag(result, left);
+ __ mul(result, result, right);
+ } else {
+ __ mul(result, left, right);
+ }
+ }
+
+ if (bailout_on_minus_zero) {
+ // Bail out if the result is supposed to be negative zero.
+ Label done;
+ __ cmp(result, Operand::Zero());
+ __ b(ne, &done);
+ __ cmp(ToRegister(instr->temp()), Operand::Zero());
+ DeoptimizeIf(mi, instr->environment());
+ __ bind(&done);
+ }
+ }
+}
+
+
+void LCodeGen::DoBitI(LBitI* instr) {
+ LOperand* left_op = instr->left();
+ LOperand* right_op = instr->right();
+ 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, ip));
+ } 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:
+ __ orr(result, left, right);
+ break;
+ case Token::BIT_XOR:
+ if (right_op->IsConstantOperand() && right.immediate() == int32_t(~0)) {
+ __ mvn(result, Operand(left));
+ } else {
+ __ eor(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->right();
+ Register left = ToRegister(instr->left());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+ if (right_op->IsRegister()) {
+ // Mask the right_op operand.
+ __ and_(scratch, ToRegister(right_op), Operand(0x1F));
+ switch (instr->op()) {
+ case Token::ROR:
+ __ mov(result, Operand(left, ROR, scratch));
+ break;
+ case Token::SAR:
+ __ mov(result, Operand(left, ASR, scratch));
+ break;
+ case Token::SHR:
+ if (instr->can_deopt()) {
+ __ mov(result, Operand(left, LSR, scratch), SetCC);
+ DeoptimizeIf(mi, instr->environment());
+ } else {
+ __ mov(result, Operand(left, LSR, scratch));
+ }
+ break;
+ case Token::SHL:
+ __ mov(result, Operand(left, LSL, scratch));
+ 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::ROR:
+ if (shift_count != 0) {
+ __ mov(result, Operand(left, ROR, shift_count));
+ } else {
+ __ Move(result, left);
+ }
+ break;
+ case Token::SAR:
+ if (shift_count != 0) {
+ __ mov(result, Operand(left, ASR, shift_count));
+ } else {
+ __ Move(result, left);
+ }
+ break;
+ case Token::SHR:
+ if (shift_count != 0) {
+ __ mov(result, Operand(left, LSR, shift_count));
+ } else {
+ if (instr->can_deopt()) {
+ __ tst(left, Operand(0x80000000));
+ DeoptimizeIf(ne, instr->environment());
+ }
+ __ Move(result, left);
+ }
+ break;
+ case Token::SHL:
+ if (shift_count != 0) {
+ if (instr->hydrogen_value()->representation().IsSmi() &&
+ instr->can_deopt()) {
+ if (shift_count != 1) {
+ __ mov(result, Operand(left, LSL, shift_count - 1));
+ __ SmiTag(result, result, SetCC);
+ } else {
+ __ SmiTag(result, left, SetCC);
+ }
+ DeoptimizeIf(vs, instr->environment());
+ } else {
+ __ mov(result, Operand(left, LSL, shift_count));
+ }
+ } else {
+ __ Move(result, left);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoSubI(LSubI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ LOperand* result = instr->result();
+ bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
+ SBit set_cond = can_overflow ? SetCC : LeaveCC;
+
+ if (right->IsStackSlot() || right->IsArgument()) {
+ Register right_reg = EmitLoadRegister(right, ip);
+ __ sub(ToRegister(result), ToRegister(left), Operand(right_reg), set_cond);
+ } else {
+ ASSERT(right->IsRegister() || right->IsConstantOperand());
+ __ sub(ToRegister(result), ToRegister(left), ToOperand(right), set_cond);
+ }
+
+ if (can_overflow) {
+ DeoptimizeIf(vs, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoRSubI(LRSubI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ LOperand* result = instr->result();
+ bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
+ SBit set_cond = can_overflow ? SetCC : LeaveCC;
+
+ if (right->IsStackSlot() || right->IsArgument()) {
+ Register right_reg = EmitLoadRegister(right, ip);
+ __ rsb(ToRegister(result), ToRegister(left), Operand(right_reg), set_cond);
+ } else {
+ ASSERT(right->IsRegister() || right->IsConstantOperand());
+ __ rsb(ToRegister(result), ToRegister(left), ToOperand(right), set_cond);
+ }
+
+ if (can_overflow) {
+ DeoptimizeIf(vs, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoConstantI(LConstantI* instr) {
+ __ mov(ToRegister(instr->result()), Operand(instr->value()));
+}
+
+
+void LCodeGen::DoConstantS(LConstantS* instr) {
+ __ mov(ToRegister(instr->result()), Operand(instr->value()));
+}
+
+
+void LCodeGen::DoConstantD(LConstantD* instr) {
+ ASSERT(instr->result()->IsDoubleRegister());
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ double v = instr->value();
+ __ Vmov(result, v, scratch0());
+}
+
+
+void LCodeGen::DoConstantE(LConstantE* instr) {
+ __ mov(ToRegister(instr->result()), Operand(instr->value()));
+}
+
+
+void LCodeGen::DoConstantT(LConstantT* instr) {
+ Handle<Object> value = instr->value();
+ AllowDeferredHandleDereference smi_check;
+ __ LoadObject(ToRegister(instr->result()), value);
+}
+
+
+void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
+ Register result = ToRegister(instr->result());
+ Register map = ToRegister(instr->value());
+ __ EnumLength(result, map);
+}
+
+
+void LCodeGen::DoElementsKind(LElementsKind* instr) {
+ Register result = ToRegister(instr->result());
+ Register input = ToRegister(instr->value());
+
+ // Load map into |result|.
+ __ ldr(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.
+ __ ldr(result, FieldMemOperand(result, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ ubfx(result, result, Map::kElementsKindShift, Map::kElementsKindBitCount);
+}
+
+
+void LCodeGen::DoValueOf(LValueOf* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ Register map = ToRegister(instr->temp());
+ Label done;
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ // If the object is a smi return the object.
+ __ SmiTst(input);
+ __ Move(result, input, eq);
+ __ b(eq, &done);
+ }
+
+ // If the object is not a value type, return the object.
+ __ CompareObjectType(input, map, map, JS_VALUE_TYPE);
+ __ Move(result, input, ne);
+ __ b(ne, &done);
+ __ ldr(result, FieldMemOperand(input, JSValue::kValueOffset));
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDateField(LDateField* instr) {
+ Register object = ToRegister(instr->date());
+ Register result = ToRegister(instr->result());
+ Register scratch = ToRegister(instr->temp());
+ Smi* index = instr->index();
+ Label runtime, done;
+ ASSERT(object.is(result));
+ ASSERT(object.is(r0));
+ ASSERT(!scratch.is(scratch0()));
+ ASSERT(!scratch.is(object));
+
+ __ SmiTst(object);
+ DeoptimizeIf(eq, instr->environment());
+ __ CompareObjectType(object, scratch, scratch, JS_DATE_TYPE);
+ DeoptimizeIf(ne, instr->environment());
+
+ if (index->value() == 0) {
+ __ ldr(result, FieldMemOperand(object, JSDate::kValueOffset));
+ } else {
+ if (index->value() < JSDate::kFirstUncachedField) {
+ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+ __ mov(scratch, Operand(stamp));
+ __ ldr(scratch, MemOperand(scratch));
+ __ ldr(scratch0(), FieldMemOperand(object, JSDate::kCacheStampOffset));
+ __ cmp(scratch, scratch0());
+ __ b(ne, &runtime);
+ __ ldr(result, FieldMemOperand(object, JSDate::kValueOffset +
+ kPointerSize * index->value()));
+ __ jmp(&done);
+ }
+ __ bind(&runtime);
+ __ PrepareCallCFunction(2, scratch);
+ __ mov(r1, Operand(index));
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) {
+ Register string = ToRegister(instr->string());
+ Register index = ToRegister(instr->index());
+ Register value = ToRegister(instr->value());
+ String::Encoding encoding = instr->encoding();
+
+ if (FLAG_debug_code) {
+ __ ldr(ip, FieldMemOperand(string, HeapObject::kMapOffset));
+ __ ldrb(ip, FieldMemOperand(ip, Map::kInstanceTypeOffset));
+
+ __ and_(ip, ip, Operand(kStringRepresentationMask | kStringEncodingMask));
+ static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
+ static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
+ __ cmp(ip, Operand(encoding == String::ONE_BYTE_ENCODING
+ ? one_byte_seq_type : two_byte_seq_type));
+ __ Check(eq, kUnexpectedStringType);
+ }
+
+ __ add(ip,
+ string,
+ Operand(SeqString::kHeaderSize - kHeapObjectTag));
+ if (encoding == String::ONE_BYTE_ENCODING) {
+ __ strb(value, MemOperand(ip, index));
+ } else {
+ // MemOperand with ip as the base register is not allowed for strh, so
+ // we do the address calculation explicitly.
+ __ add(ip, ip, Operand(index, LSL, 1));
+ __ strh(value, MemOperand(ip));
+ }
+}
+
+
+void LCodeGen::DoThrow(LThrow* instr) {
+ Register input_reg = EmitLoadRegister(instr->value(), ip);
+ __ push(input_reg);
+ CallRuntime(Runtime::kThrow, 1, instr);
+
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
+}
+
+
+void LCodeGen::DoAddI(LAddI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ LOperand* result = instr->result();
+ bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
+ SBit set_cond = can_overflow ? SetCC : LeaveCC;
+
+ if (right->IsStackSlot() || right->IsArgument()) {
+ Register right_reg = EmitLoadRegister(right, ip);
+ __ add(ToRegister(result), ToRegister(left), Operand(right_reg), set_cond);
+ } else {
+ ASSERT(right->IsRegister() || right->IsConstantOperand());
+ __ add(ToRegister(result), ToRegister(left), ToOperand(right), set_cond);
+ }
+
+ if (can_overflow) {
+ DeoptimizeIf(vs, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoMathMinMax(LMathMinMax* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ HMathMinMax::Operation operation = instr->hydrogen()->operation();
+ if (instr->hydrogen()->representation().IsSmiOrInteger32()) {
+ Condition condition = (operation == HMathMinMax::kMathMin) ? le : ge;
+ Register left_reg = ToRegister(left);
+ Operand right_op = (right->IsRegister() || right->IsConstantOperand())
+ ? ToOperand(right)
+ : Operand(EmitLoadRegister(right, ip));
+ Register result_reg = ToRegister(instr->result());
+ __ cmp(left_reg, right_op);
+ __ Move(result_reg, left_reg, condition);
+ __ mov(result_reg, right_op, LeaveCC, NegateCondition(condition));
+ } else {
+ ASSERT(instr->hydrogen()->representation().IsDouble());
+ DwVfpRegister left_reg = ToDoubleRegister(left);
+ DwVfpRegister right_reg = ToDoubleRegister(right);
+ DwVfpRegister result_reg = ToDoubleRegister(instr->result());
+ Label result_is_nan, return_left, return_right, check_zero, done;
+ __ VFPCompareAndSetFlags(left_reg, right_reg);
+ if (operation == HMathMinMax::kMathMin) {
+ __ b(mi, &return_left);
+ __ b(gt, &return_right);
+ } else {
+ __ b(mi, &return_right);
+ __ b(gt, &return_left);
+ }
+ __ b(vs, &result_is_nan);
+ // Left equals right => check for -0.
+ __ VFPCompareAndSetFlags(left_reg, 0.0);
+ if (left_reg.is(result_reg) || right_reg.is(result_reg)) {
+ __ b(ne, &done); // left == right != 0.
+ } else {
+ __ b(ne, &return_left); // left == right != 0.
+ }
+ // At this point, both left and right are either 0 or -0.
+ if (operation == HMathMinMax::kMathMin) {
+ // We could use a single 'vorr' instruction here if we had NEON support.
+ __ vneg(left_reg, left_reg);
+ __ vsub(result_reg, left_reg, right_reg);
+ __ vneg(result_reg, result_reg);
+ } else {
+ // Since we operate on +0 and/or -0, vadd and vand have the same effect;
+ // the decision for vadd is easy because vand is a NEON instruction.
+ __ vadd(result_reg, left_reg, right_reg);
+ }
+ __ b(&done);
+
+ __ bind(&result_is_nan);
+ __ vadd(result_reg, left_reg, right_reg);
+ __ b(&done);
+
+ __ bind(&return_right);
+ __ Move(result_reg, right_reg);
+ if (!left_reg.is(result_reg)) {
+ __ b(&done);
+ }
+
+ __ bind(&return_left);
+ __ Move(result_reg, left_reg);
+
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
+ DwVfpRegister left = ToDoubleRegister(instr->left());
+ DwVfpRegister right = ToDoubleRegister(instr->right());
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ switch (instr->op()) {
+ case Token::ADD:
+ __ vadd(result, left, right);
+ break;
+ case Token::SUB:
+ __ vsub(result, left, right);
+ break;
+ case Token::MUL:
+ __ vmul(result, left, right);
+ break;
+ case Token::DIV:
+ __ vdiv(result, left, right);
+ break;
+ case Token::MOD: {
+ // Save r0-r3 on the stack.
+ __ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit() | r3.bit());
+
+ __ 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 r0-r3.
+ __ ldm(ia_w, sp, r0.bit() | r1.bit() | r2.bit() | r3.bit());
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
+ ASSERT(ToRegister(instr->left()).is(r1));
+ ASSERT(ToRegister(instr->right()).is(r0));
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ BinaryOpStub stub(instr->op(), NO_OVERWRITE);
+ // Block literal pool emission to ensure nop indicating no inlined smi code
+ // is in the correct position.
+ Assembler::BlockConstPoolScope block_const_pool(masm());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ __ nop(); // Signals no inlined code.
+}
+
+
+int LCodeGen::GetNextEmittedBlock() const {
+ for (int i = current_block_ + 1; i < graph()->blocks()->length(); ++i) {
+ if (!chunk_->GetLabel(i)->HasReplacement()) return i;
+ }
+ return -1;
+}
+
+template<class InstrType>
+void LCodeGen::EmitBranch(InstrType instr, Condition condition) {
+ int left_block = instr->TrueDestination(chunk_);
+ int right_block = instr->FalseDestination(chunk_);
+
+ int next_block = GetNextEmittedBlock();
+
+ if (right_block == left_block || condition == al) {
+ EmitGoto(left_block);
+ } else if (left_block == next_block) {
+ __ b(NegateCondition(condition), chunk_->GetAssemblyLabel(right_block));
+ } else if (right_block == next_block) {
+ __ b(condition, chunk_->GetAssemblyLabel(left_block));
+ } else {
+ __ b(condition, chunk_->GetAssemblyLabel(left_block));
+ __ b(chunk_->GetAssemblyLabel(right_block));
+ }
+}
+
+
+template<class InstrType>
+void LCodeGen::EmitFalseBranch(InstrType instr, Condition condition) {
+ int false_block = instr->FalseDestination(chunk_);
+ __ b(condition, chunk_->GetAssemblyLabel(false_block));
+}
+
+
+void LCodeGen::DoDebugBreak(LDebugBreak* instr) {
+ __ stop("LBreak");
+}
+
+
+void LCodeGen::DoIsNumberAndBranch(LIsNumberAndBranch* instr) {
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsSmiOrInteger32() || r.IsDouble()) {
+ EmitBranch(instr, al);
+ } else {
+ ASSERT(r.IsTagged());
+ Register reg = ToRegister(instr->value());
+ HType type = instr->hydrogen()->value()->type();
+ if (type.IsTaggedNumber()) {
+ EmitBranch(instr, al);
+ }
+ __ JumpIfSmi(reg, instr->TrueLabel(chunk_));
+ __ ldr(scratch0(), FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ CompareRoot(scratch0(), Heap::kHeapNumberMapRootIndex);
+ EmitBranch(instr, eq);
+ }
+}
+
+
+void LCodeGen::DoBranch(LBranch* instr) {
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsInteger32() || r.IsSmi()) {
+ ASSERT(!info()->IsStub());
+ Register reg = ToRegister(instr->value());
+ __ cmp(reg, Operand::Zero());
+ EmitBranch(instr, ne);
+ } else if (r.IsDouble()) {
+ ASSERT(!info()->IsStub());
+ DwVfpRegister reg = ToDoubleRegister(instr->value());
+ // Test the double value. Zero and NaN are false.
+ __ VFPCompareAndSetFlags(reg, 0.0);
+ __ cmp(r0, r0, vs); // If NaN, set the Z flag. (NaN -> false)
+ EmitBranch(instr, ne);
+ } else {
+ ASSERT(r.IsTagged());
+ Register reg = ToRegister(instr->value());
+ HType type = instr->hydrogen()->value()->type();
+ if (type.IsBoolean()) {
+ ASSERT(!info()->IsStub());
+ __ CompareRoot(reg, Heap::kTrueValueRootIndex);
+ EmitBranch(instr, eq);
+ } else if (type.IsSmi()) {
+ ASSERT(!info()->IsStub());
+ __ cmp(reg, Operand::Zero());
+ EmitBranch(instr, ne);
+ } else if (type.IsJSArray()) {
+ ASSERT(!info()->IsStub());
+ EmitBranch(instr, al);
+ } else if (type.IsHeapNumber()) {
+ ASSERT(!info()->IsStub());
+ DwVfpRegister dbl_scratch = double_scratch0();
+ __ vldr(dbl_scratch, FieldMemOperand(reg, HeapNumber::kValueOffset));
+ // Test the double value. Zero and NaN are false.
+ __ VFPCompareAndSetFlags(dbl_scratch, 0.0);
+ __ cmp(r0, r0, vs); // If NaN, set the Z flag. (NaN)
+ EmitBranch(instr, ne);
+ } else if (type.IsString()) {
+ ASSERT(!info()->IsStub());
+ __ ldr(ip, FieldMemOperand(reg, String::kLengthOffset));
+ __ cmp(ip, Operand::Zero());
+ EmitBranch(instr, ne);
+ } else {
+ 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::Types::Generic();
+
+ if (expected.Contains(ToBooleanStub::UNDEFINED)) {
+ // undefined -> false.
+ __ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
+ __ b(eq, instr->FalseLabel(chunk_));
+ }
+ if (expected.Contains(ToBooleanStub::BOOLEAN)) {
+ // Boolean -> its value.
+ __ CompareRoot(reg, Heap::kTrueValueRootIndex);
+ __ b(eq, instr->TrueLabel(chunk_));
+ __ CompareRoot(reg, Heap::kFalseValueRootIndex);
+ __ b(eq, instr->FalseLabel(chunk_));
+ }
+ if (expected.Contains(ToBooleanStub::NULL_TYPE)) {
+ // 'null' -> false.
+ __ CompareRoot(reg, Heap::kNullValueRootIndex);
+ __ b(eq, instr->FalseLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::SMI)) {
+ // Smis: 0 -> false, all other -> true.
+ __ cmp(reg, Operand::Zero());
+ __ b(eq, instr->FalseLabel(chunk_));
+ __ JumpIfSmi(reg, instr->TrueLabel(chunk_));
+ } else if (expected.NeedsMap()) {
+ // If we need a map later and have a Smi -> deopt.
+ __ SmiTst(reg);
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+ const Register map = scratch0();
+ if (expected.NeedsMap()) {
+ __ ldr(map, FieldMemOperand(reg, HeapObject::kMapOffset));
+
+ if (expected.CanBeUndetectable()) {
+ // Undetectable -> false.
+ __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ tst(ip, Operand(1 << Map::kIsUndetectable));
+ __ b(ne, instr->FalseLabel(chunk_));
+ }
+ }
+
+ if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) {
+ // spec object -> true.
+ __ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE);
+ __ b(ge, instr->TrueLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::STRING)) {
+ // String value -> false iff empty.
+ Label not_string;
+ __ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
+ __ b(ge, &not_string);
+ __ ldr(ip, FieldMemOperand(reg, String::kLengthOffset));
+ __ cmp(ip, Operand::Zero());
+ __ b(ne, instr->TrueLabel(chunk_));
+ __ b(instr->FalseLabel(chunk_));
+ __ bind(&not_string);
+ }
+
+ if (expected.Contains(ToBooleanStub::SYMBOL)) {
+ // Symbol value -> true.
+ __ CompareInstanceType(map, ip, SYMBOL_TYPE);
+ __ b(eq, instr->TrueLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
+ // heap number -> false iff +0, -0, or NaN.
+ DwVfpRegister dbl_scratch = double_scratch0();
+ Label not_heap_number;
+ __ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
+ __ b(ne, &not_heap_number);
+ __ vldr(dbl_scratch, FieldMemOperand(reg, HeapNumber::kValueOffset));
+ __ VFPCompareAndSetFlags(dbl_scratch, 0.0);
+ __ cmp(r0, r0, vs); // NaN -> false.
+ __ b(eq, instr->FalseLabel(chunk_)); // +0, -0 -> false.
+ __ b(instr->TrueLabel(chunk_));
+ __ bind(&not_heap_number);
+ }
+
+ if (!expected.IsGeneric()) {
+ // We've seen something for the first time -> deopt.
+ // This can only happen if we are not generic already.
+ DeoptimizeIf(al, instr->environment());
+ }
+ }
+ }
+}
+
+
+void LCodeGen::EmitGoto(int block) {
+ if (!IsNextEmittedBlock(block)) {
+ __ jmp(chunk_->GetAssemblyLabel(LookupDestination(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::DoCompareNumericAndBranch(LCompareNumericAndBranch* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ 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) ?
+ instr->TrueDestination(chunk_) : instr->FalseDestination(chunk_);
+ EmitGoto(next_block);
+ } else {
+ 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, instr->FalseLabel(chunk_));
+ } else {
+ if (right->IsConstantOperand()) {
+ int32_t value = ToInteger32(LConstantOperand::cast(right));
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ cmp(ToRegister(left), Operand(Smi::FromInt(value)));
+ } else {
+ __ cmp(ToRegister(left), Operand(value));
+ }
+ } else if (left->IsConstantOperand()) {
+ int32_t value = ToInteger32(LConstantOperand::cast(left));
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ cmp(ToRegister(right), Operand(Smi::FromInt(value)));
+ } else {
+ __ cmp(ToRegister(right), Operand(value));
+ }
+ // We transposed the operands. Reverse the condition.
+ cond = ReverseCondition(cond);
+ } else {
+ __ cmp(ToRegister(left), ToRegister(right));
+ }
+ }
+ EmitBranch(instr, cond);
+ }
+}
+
+
+void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) {
+ Register left = ToRegister(instr->left());
+ Register right = ToRegister(instr->right());
+
+ __ cmp(left, Operand(right));
+ EmitBranch(instr, eq);
+}
+
+
+void LCodeGen::DoCmpHoleAndBranch(LCmpHoleAndBranch* instr) {
+ if (instr->hydrogen()->representation().IsTagged()) {
+ Register input_reg = ToRegister(instr->object());
+ __ mov(ip, Operand(factory()->the_hole_value()));
+ __ cmp(input_reg, ip);
+ EmitBranch(instr, eq);
+ return;
+ }
+
+ DwVfpRegister input_reg = ToDoubleRegister(instr->object());
+ __ VFPCompareAndSetFlags(input_reg, input_reg);
+ EmitFalseBranch(instr, vc);
+
+ Register scratch = scratch0();
+ __ VmovHigh(scratch, input_reg);
+ __ cmp(scratch, Operand(kHoleNanUpper32));
+ EmitBranch(instr, eq);
+}
+
+
+Condition LCodeGen::EmitIsObject(Register input,
+ Register temp1,
+ Label* is_not_object,
+ Label* is_object) {
+ Register temp2 = scratch0();
+ __ JumpIfSmi(input, is_not_object);
+
+ __ LoadRoot(temp2, Heap::kNullValueRootIndex);
+ __ cmp(input, temp2);
+ __ b(eq, is_object);
+
+ // Load map.
+ __ ldr(temp1, FieldMemOperand(input, HeapObject::kMapOffset));
+ // Undetectable objects behave like undefined.
+ __ ldrb(temp2, FieldMemOperand(temp1, Map::kBitFieldOffset));
+ __ tst(temp2, Operand(1 << Map::kIsUndetectable));
+ __ b(ne, is_not_object);
+
+ // Load instance type and check that it is in object type range.
+ __ ldrb(temp2, FieldMemOperand(temp1, Map::kInstanceTypeOffset));
+ __ cmp(temp2, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ __ b(lt, is_not_object);
+ __ cmp(temp2, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ return le;
+}
+
+
+void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ Register temp1 = ToRegister(instr->temp());
+
+ Condition true_cond =
+ EmitIsObject(reg, temp1,
+ instr->FalseLabel(chunk_), instr->TrueLabel(chunk_));
+
+ EmitBranch(instr, true_cond);
+}
+
+
+Condition LCodeGen::EmitIsString(Register input,
+ Register temp1,
+ Label* is_not_string,
+ SmiCheck check_needed = INLINE_SMI_CHECK) {
+ if (check_needed == INLINE_SMI_CHECK) {
+ __ JumpIfSmi(input, is_not_string);
+ }
+ __ CompareObjectType(input, temp1, temp1, FIRST_NONSTRING_TYPE);
+
+ return lt;
+}
+
+
+void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ Register temp1 = ToRegister(instr->temp());
+
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ Condition true_cond =
+ EmitIsString(reg, temp1, instr->FalseLabel(chunk_), check_needed);
+
+ EmitBranch(instr, true_cond);
+}
+
+
+void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
+ Register input_reg = EmitLoadRegister(instr->value(), ip);
+ __ SmiTst(input_reg);
+ EmitBranch(instr, eq);
+}
+
+
+void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ __ JumpIfSmi(input, instr->FalseLabel(chunk_));
+ }
+ __ ldr(temp, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ ldrb(temp, FieldMemOperand(temp, Map::kBitFieldOffset));
+ __ tst(temp, Operand(1 << Map::kIsUndetectable));
+ EmitBranch(instr, ne);
+}
+
+
+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();
+
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ // This instruction also signals no smi code inlined.
+ __ cmp(r0, Operand::Zero());
+
+ Condition condition = ComputeCompareCondition(op);
+
+ EmitBranch(instr, condition);
+}
+
+
+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->value());
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ __ JumpIfSmi(input, instr->FalseLabel(chunk_));
+ }
+
+ __ CompareObjectType(input, scratch, scratch, TestType(instr->hydrogen()));
+ EmitBranch(instr, BranchCondition(instr->hydrogen()));
+}
+
+
+void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+
+ __ AssertString(input);
+
+ __ ldr(result, FieldMemOperand(input, String::kHashFieldOffset));
+ __ IndexFromHash(result, result);
+}
+
+
+void LCodeGen::DoHasCachedArrayIndexAndBranch(
+ LHasCachedArrayIndexAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register scratch = scratch0();
+
+ __ ldr(scratch,
+ FieldMemOperand(input, String::kHashFieldOffset));
+ __ tst(scratch, Operand(String::kContainsCachedArrayIndexMask));
+ EmitBranch(instr, eq);
+}
+
+
+// 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->IsOneByteEqualTo(STATIC_ASCII_VECTOR("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);
+ __ 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 {
+ // 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));
+
+ // Objects with a non-function constructor have class 'Object'.
+ __ CompareObjectType(temp, temp2, temp2, JS_FUNCTION_TYPE);
+ if (class_name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("Object"))) {
+ __ b(ne, is_true);
+ } else {
+ __ b(ne, is_false);
+ }
+
+ // temp now contains the constructor function. Grab the
+ // instance class name from there.
+ __ ldr(temp, FieldMemOperand(temp, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(temp, FieldMemOperand(temp,
+ SharedFunctionInfo::kInstanceClassNameOffset));
+ // The class name we are testing against is internalized since it's a literal.
+ // The name in the constructor is internalized 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 internalized it is sufficient to use an
+ // identity comparison.
+ __ cmp(temp, Operand(class_name));
+ // End with the answer in flags.
+}
+
+
+void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = scratch0();
+ Register temp2 = ToRegister(instr->temp());
+ Handle<String> class_name = instr->hydrogen()->class_name();
+
+ EmitClassOfTest(instr->TrueLabel(chunk_), instr->FalseLabel(chunk_),
+ class_name, input, temp, temp2);
+
+ EmitBranch(instr, eq);
+}
+
+
+void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ __ ldr(temp, FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ cmp(temp, Operand(instr->map()));
+ EmitBranch(instr, eq);
+}
+
+
+void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
+ ASSERT(ToRegister(instr->left()).is(r0)); // Object is in r0.
+ ASSERT(ToRegister(instr->right()).is(r1)); // Function is in r1.
+
+ InstanceofStub stub(InstanceofStub::kArgsInRegisters);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+
+ __ cmp(r0, Operand::Zero());
+ __ mov(r0, Operand(factory()->false_value()), LeaveCC, ne);
+ __ mov(r0, Operand(factory()->true_value()), LeaveCC, eq);
+}
+
+
+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(zone()) DeferredInstanceOfKnownGlobal(this, instr);
+
+ Label done, false_result;
+ Register object = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+ Register result = ToRegister(instr->result());
+
+ ASSERT(object.is(r0));
+ ASSERT(result.is(r0));
+
+ // 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;
+ __ ldr(map, FieldMemOperand(object, HeapObject::kMapOffset));
+ {
+ // Block constant pool emission to ensure the positions of instructions are
+ // as expected by the patcher. See InstanceofStub::Generate().
+ Assembler::BlockConstPoolScope block_const_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.
+ PredictableCodeSizeScope predictable(masm_, 5 * Assembler::kInstrSize);
+ Handle<Cell> cell = factory()->NewCell(factory()->the_hole_value());
+ __ mov(ip, Operand(Handle<Object>(cell)));
+ __ ldr(ip, FieldMemOperand(ip, PropertyCell::kValueOffset));
+ __ cmp(map, Operand(ip));
+ __ b(ne, &cache_miss);
+ // 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.
+ __ mov(result, Operand(factory()->the_hole_value()));
+ }
+ __ b(&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(ip, Heap::kNullValueRootIndex);
+ __ cmp(object, Operand(ip));
+ __ b(eq, &false_result);
+
+ // String values is not instance of anything.
+ Condition is_string = masm_->IsObjectStringType(object, temp);
+ __ b(is_string, &false_result);
+
+ // Go to the deferred code.
+ __ b(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(r0));
+
+ 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 r4 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->temp());
+ ASSERT(temp.is(r4));
+ __ LoadHeapObject(InstanceofStub::right(), instr->function());
+ static const int kAdditionalDelta = 5;
+ // Make sure that code size is predicable, since we use specific constants
+ // offsets in the code to find embedded values..
+ PredictableCodeSizeScope predictable(masm_, 6 * Assembler::kInstrSize);
+ int delta = masm_->InstructionsGeneratedSince(map_check) + kAdditionalDelta;
+ Label before_push_delta;
+ __ bind(&before_push_delta);
+ __ BlockConstPoolFor(kAdditionalDelta);
+ __ mov(temp, Operand(delta * kPointerSize));
+ // The mov above can generate one or two instructions. The delta was computed
+ // for two instructions, so we need to pad here in case of one instruction.
+ if (masm_->InstructionsGeneratedSince(&before_push_delta) != 2) {
+ ASSERT_EQ(1, masm_->InstructionsGeneratedSince(&before_push_delta));
+ __ nop();
+ }
+ __ StoreToSafepointRegisterSlot(temp, temp);
+ CallCodeGeneric(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ instr,
+ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
+ LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
+ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
+ // Put the result value into the result register slot and
+ // restore all registers.
+ __ StoreToSafepointRegisterSlot(result, result);
+}
+
+
+void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
+ Register object = ToRegister(instr->object());
+ Register result = ToRegister(instr->result());
+ __ ldr(result, FieldMemOperand(object, HeapObject::kMapOffset));
+ __ ldrb(result, FieldMemOperand(result, Map::kInstanceSizeOffset));
+}
+
+
+void LCodeGen::DoCmpT(LCmpT* instr) {
+ Token::Value op = instr->op();
+
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ // This instruction also signals no smi code inlined.
+ __ cmp(r0, Operand::Zero());
+
+ Condition condition = ComputeCompareCondition(op);
+ __ LoadRoot(ToRegister(instr->result()),
+ Heap::kTrueValueRootIndex,
+ condition);
+ __ LoadRoot(ToRegister(instr->result()),
+ Heap::kFalseValueRootIndex,
+ NegateCondition(condition));
+}
+
+
+void LCodeGen::DoReturn(LReturn* instr) {
+ if (FLAG_trace && info()->IsOptimizing()) {
+ // Push the return value on the stack as the parameter.
+ // Runtime::TraceExit returns its parameter in r0.
+ __ push(r0);
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+ if (info()->saves_caller_doubles()) {
+ ASSERT(NeedsEagerFrame());
+ BitVector* doubles = chunk()->allocated_double_registers();
+ BitVector::Iterator save_iterator(doubles);
+ int count = 0;
+ while (!save_iterator.Done()) {
+ __ vldr(DwVfpRegister::FromAllocationIndex(save_iterator.Current()),
+ MemOperand(sp, count * kDoubleSize));
+ save_iterator.Advance();
+ count++;
+ }
+ }
+ int no_frame_start = -1;
+ if (NeedsEagerFrame()) {
+ __ mov(sp, fp);
+ no_frame_start = masm_->pc_offset();
+ __ ldm(ia_w, sp, fp.bit() | lr.bit());
+ }
+ if (instr->has_constant_parameter_count()) {
+ int parameter_count = ToInteger32(instr->constant_parameter_count());
+ int32_t sp_delta = (parameter_count + 1) * kPointerSize;
+ if (sp_delta != 0) {
+ __ add(sp, sp, Operand(sp_delta));
+ }
+ } else {
+ Register reg = ToRegister(instr->parameter_count());
+ // The argument count parameter is a smi
+ __ SmiUntag(reg);
+ __ add(sp, sp, Operand(reg, LSL, kPointerSizeLog2));
+ }
+
+ __ Jump(lr);
+
+ if (no_frame_start != -1) {
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
+}
+
+
+void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
+ Register result = ToRegister(instr->result());
+ __ mov(ip, Operand(Handle<Object>(instr->hydrogen()->cell())));
+ __ ldr(result, FieldMemOperand(ip, Cell::kValueOffset));
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(result, ip);
+ DeoptimizeIf(eq, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->global_object()).is(r0));
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ __ mov(r2, 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.
+ __ 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()->RequiresHoleCheck()) {
+ // We use a temp to check the payload (CompareRoot might clobber ip).
+ Register payload = ToRegister(instr->temp());
+ __ ldr(payload, FieldMemOperand(cell, Cell::kValueOffset));
+ __ CompareRoot(payload, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+ // Store the value.
+ __ str(value, FieldMemOperand(cell, Cell::kValueOffset));
+ // Cells are always rescanned, so no write barrier here.
+}
+
+
+void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->global_object()).is(r1));
+ ASSERT(ToRegister(instr->value()).is(r0));
+
+ __ mov(r2, 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());
+ __ 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());
+ 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()) {
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ __ RecordWriteContextSlot(context,
+ target.offset(),
+ value,
+ scratch,
+ GetLinkRegisterState(),
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+
+ __ bind(&skip_assignment);
+}
+
+
+void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
+ HObjectAccess access = instr->hydrogen()->access();
+ int offset = access.offset();
+ Register object = ToRegister(instr->object());
+
+ if (access.IsExternalMemory()) {
+ Register result = ToRegister(instr->result());
+ __ ldr(result, MemOperand(object, offset));
+ return;
+ }
+
+ if (instr->hydrogen()->representation().IsDouble()) {
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ __ vldr(result, FieldMemOperand(object, offset));
+ return;
+ }
+
+ Register result = ToRegister(instr->result());
+ if (access.IsInobject()) {
+ __ ldr(result, FieldMemOperand(object, offset));
+ } else {
+ __ ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
+ __ ldr(result, FieldMemOperand(result, offset));
+ }
+}
+
+
+void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
+ ASSERT(ToRegister(instr->object()).is(r0));
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ // Name is always in r2.
+ __ mov(r2, Operand(instr->name()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS);
+}
+
+
+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.
+ __ CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE);
+ DeoptimizeIf(ne, instr->environment());
+
+ // Make sure that the function has an instance prototype.
+ Label non_instance;
+ __ ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
+ __ tst(scratch, Operand(1 << Map::kHasNonInstancePrototype));
+ __ b(ne, &non_instance);
+
+ // Get the prototype or initial map from the function.
+ __ ldr(result,
+ FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Check that the function has a prototype or an initial map.
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(result, ip);
+ DeoptimizeIf(eq, instr->environment());
+
+ // If the function does not have an initial map, we're done.
+ Label done;
+ __ CompareObjectType(result, scratch, scratch, MAP_TYPE);
+ __ b(ne, &done);
+
+ // Get the prototype from the initial map.
+ __ ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
+ __ jmp(&done);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in initial map.
+ __ bind(&non_instance);
+ __ ldr(result, FieldMemOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoLoadExternalArrayPointer(
+ LLoadExternalArrayPointer* instr) {
+ Register to_reg = ToRegister(instr->result());
+ Register from_reg = ToRegister(instr->object());
+ __ ldr(to_reg, FieldMemOperand(from_reg,
+ ExternalArray::kExternalPointerOffset));
+}
+
+
+void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
+ Register arguments = ToRegister(instr->arguments());
+ Register result = ToRegister(instr->result());
+ if (instr->length()->IsConstantOperand() &&
+ instr->index()->IsConstantOperand()) {
+ int const_index = ToInteger32(LConstantOperand::cast(instr->index()));
+ int const_length = ToInteger32(LConstantOperand::cast(instr->length()));
+ int index = (const_length - const_index) + 1;
+ __ ldr(result, MemOperand(arguments, index * kPointerSize));
+ } else {
+ Register length = ToRegister(instr->length());
+ Register index = ToRegister(instr->index());
+ // There are two words between the frame pointer and the last argument.
+ // Subtracting from length accounts for one of them add one more.
+ __ sub(length, length, index);
+ __ add(length, length, Operand(1));
+ __ ldr(result, MemOperand(arguments, length, LSL, kPointerSizeLog2));
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) {
+ Register external_pointer = ToRegister(instr->elements());
+ 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(kArrayIndexConstantValueTooBig);
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+ int element_size_shift = ElementsKindToShiftSize(elements_kind);
+ int shift_size = (instr->hydrogen()->key()->representation().IsSmi())
+ ? (element_size_shift - kSmiTagSize) : element_size_shift;
+ int additional_offset = instr->additional_index() << element_size_shift;
+
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS ||
+ elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ Operand operand = key_is_constant
+ ? Operand(constant_key << element_size_shift)
+ : Operand(key, LSL, shift_size);
+ __ add(scratch0(), external_pointer, operand);
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ __ vldr(double_scratch0().low(), scratch0(), additional_offset);
+ __ vcvt_f64_f32(result, double_scratch0().low());
+ } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS
+ __ vldr(result, scratch0(), additional_offset);
+ }
+ } else {
+ Register result = ToRegister(instr->result());
+ MemOperand mem_operand = PrepareKeyedOperand(
+ key, external_pointer, key_is_constant, constant_key,
+ element_size_shift, shift_size,
+ instr->additional_index(), additional_offset);
+ switch (elements_kind) {
+ case EXTERNAL_BYTE_ELEMENTS:
+ __ ldrsb(result, mem_operand);
+ break;
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ ldrb(result, mem_operand);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ __ ldrsh(result, mem_operand);
+ break;
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ ldrh(result, mem_operand);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ __ ldr(result, mem_operand);
+ break;
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ ldr(result, mem_operand);
+ if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) {
+ __ cmp(result, Operand(0x80000000));
+ DeoptimizeIf(cs, instr->environment());
+ }
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr) {
+ Register elements = ToRegister(instr->elements());
+ bool key_is_constant = instr->key()->IsConstantOperand();
+ Register key = no_reg;
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ Register scratch = scratch0();
+
+ int element_size_shift = ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS);
+ int shift_size = (instr->hydrogen()->key()->representation().IsSmi())
+ ? (element_size_shift - kSmiTagSize) : element_size_shift;
+ int constant_key = 0;
+ if (key_is_constant) {
+ constant_key = ToInteger32(LConstantOperand::cast(instr->key()));
+ if (constant_key & 0xF0000000) {
+ Abort(kArrayIndexConstantValueTooBig);
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+
+ int base_offset = (FixedDoubleArray::kHeaderSize - kHeapObjectTag) +
+ ((constant_key + instr->additional_index()) << element_size_shift);
+ if (!key_is_constant) {
+ __ add(elements, elements, Operand(key, LSL, shift_size));
+ }
+ __ add(elements, elements, Operand(base_offset));
+ __ vldr(result, elements, 0);
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ ldr(scratch, MemOperand(elements, sizeof(kHoleNanLower32)));
+ __ cmp(scratch, Operand(kHoleNanUpper32));
+ DeoptimizeIf(eq, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedFixedArray(LLoadKeyed* instr) {
+ Register elements = ToRegister(instr->elements());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+ Register store_base = scratch;
+ int offset = 0;
+
+ if (instr->key()->IsConstantOperand()) {
+ LConstantOperand* const_operand = LConstantOperand::cast(instr->key());
+ offset = FixedArray::OffsetOfElementAt(ToInteger32(const_operand) +
+ instr->additional_index());
+ store_base = elements;
+ } else {
+ Register key = EmitLoadRegister(instr->key(), scratch0());
+ // Even though the HLoadKeyed instruction forces the input
+ // representation for the key to be an integer, the input gets replaced
+ // during bound check elimination with the index argument to the bounds
+ // check, which can be tagged, so that case must be handled here, too.
+ if (instr->hydrogen()->key()->representation().IsSmi()) {
+ __ add(scratch, elements, Operand::PointerOffsetFromSmiKey(key));
+ } else {
+ __ add(scratch, elements, Operand(key, LSL, kPointerSizeLog2));
+ }
+ offset = FixedArray::OffsetOfElementAt(instr->additional_index());
+ }
+ __ ldr(result, FieldMemOperand(store_base, offset));
+
+ // Check for the hole value.
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ if (IsFastSmiElementsKind(instr->hydrogen()->elements_kind())) {
+ __ SmiTst(result);
+ DeoptimizeIf(ne, instr->environment());
+ } else {
+ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
+ __ cmp(result, scratch);
+ DeoptimizeIf(eq, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoLoadKeyed(LLoadKeyed* instr) {
+ if (instr->is_external()) {
+ DoLoadKeyedExternalArray(instr);
+ } else if (instr->hydrogen()->representation().IsDouble()) {
+ DoLoadKeyedFixedDoubleArray(instr);
+ } else {
+ DoLoadKeyedFixedArray(instr);
+ }
+}
+
+
+MemOperand LCodeGen::PrepareKeyedOperand(Register key,
+ Register base,
+ bool key_is_constant,
+ int constant_key,
+ int element_size,
+ int shift_size,
+ int additional_index,
+ int additional_offset) {
+ if (additional_index != 0 && !key_is_constant) {
+ additional_index *= 1 << (element_size - shift_size);
+ __ add(scratch0(), key, Operand(additional_index));
+ }
+
+ if (key_is_constant) {
+ return MemOperand(base,
+ (constant_key << element_size) + additional_offset);
+ }
+
+ if (additional_index == 0) {
+ if (shift_size >= 0) {
+ return MemOperand(base, key, LSL, shift_size);
+ } else {
+ ASSERT_EQ(-1, shift_size);
+ return MemOperand(base, key, LSR, 1);
+ }
+ }
+
+ if (shift_size >= 0) {
+ return MemOperand(base, scratch0(), LSL, shift_size);
+ } else {
+ ASSERT_EQ(-1, shift_size);
+ return MemOperand(base, scratch0(), LSR, 1);
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) {
+ ASSERT(ToRegister(instr->object()).is(r1));
+ ASSERT(ToRegister(instr->key()).is(r0));
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS);
+}
+
+
+void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
+ Register scratch = scratch0();
+ Register result = ToRegister(instr->result());
+
+ if (instr->hydrogen()->from_inlined()) {
+ __ sub(result, sp, Operand(2 * kPointerSize));
+ } else {
+ // Check if the calling frame is an arguments adaptor frame.
+ Label done, adapted;
+ __ ldr(scratch, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(result, MemOperand(scratch, StandardFrameConstants::kContextOffset));
+ __ cmp(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.
+ __ mov(result, fp, LeaveCC, ne);
+ __ mov(result, scratch, LeaveCC, eq);
+ }
+}
+
+
+void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
+ Register elem = ToRegister(instr->elements());
+ Register result = ToRegister(instr->result());
+
+ Label done;
+
+ // If no arguments adaptor frame the number of arguments is fixed.
+ __ cmp(fp, elem);
+ __ mov(result, Operand(scope()->num_parameters()));
+ __ b(eq, &done);
+
+ // Arguments adaptor frame present. Get argument length from there.
+ __ ldr(result, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ ldr(result,
+ MemOperand(result, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ SmiUntag(result);
+
+ // Argument length is in result register.
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) {
+ Register receiver = ToRegister(instr->receiver());
+ Register function = ToRegister(instr->function());
+ Register scratch = scratch0();
+
+ // 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.
+ __ ldr(scratch,
+ FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(scratch,
+ FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset));
+ __ tst(scratch,
+ Operand(1 << (SharedFunctionInfo::kStrictModeFunction + kSmiTagSize)));
+ __ b(ne, &receiver_ok);
+
+ // Do not transform the receiver to object for builtins.
+ __ tst(scratch, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize)));
+ __ b(ne, &receiver_ok);
+
+ // Normal function. Replace undefined or null with global receiver.
+ __ LoadRoot(scratch, Heap::kNullValueRootIndex);
+ __ cmp(receiver, scratch);
+ __ b(eq, &global_object);
+ __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
+ __ cmp(receiver, scratch);
+ __ b(eq, &global_object);
+
+ // Deoptimize if the receiver is not a JS object.
+ __ SmiTst(receiver);
+ DeoptimizeIf(eq, instr->environment());
+ __ CompareObjectType(receiver, scratch, scratch, FIRST_SPEC_OBJECT_TYPE);
+ DeoptimizeIf(lt, instr->environment());
+ __ jmp(&receiver_ok);
+
+ __ bind(&global_object);
+ __ ldr(receiver, GlobalObjectOperand());
+ __ ldr(receiver,
+ FieldMemOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
+ __ bind(&receiver_ok);
+}
+
+
+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(r0)); // Used for parameter count.
+ ASSERT(function.is(r1)); // Required by InvokeFunction.
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ // Copy the arguments to this function possibly from the
+ // adaptor frame below it.
+ const uint32_t kArgumentsLimit = 1 * KB;
+ __ cmp(length, Operand(kArgumentsLimit));
+ DeoptimizeIf(hi, instr->environment());
+
+ // Push the receiver and use the register to keep the original
+ // number of arguments.
+ __ push(receiver);
+ __ mov(receiver, length);
+ // The arguments are at a one pointer size offset from elements.
+ __ add(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.
+ __ cmp(length, Operand::Zero());
+ __ b(eq, &invoke);
+ __ bind(&loop);
+ __ ldr(scratch, MemOperand(elements, length, LSL, 2));
+ __ push(scratch);
+ __ sub(length, length, Operand(1), SetCC);
+ __ b(ne, &loop);
+
+ __ bind(&invoke);
+ ASSERT(instr->HasPointerMap());
+ 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 r0, as expected
+ // by InvokeFunction.
+ ParameterCount actual(receiver);
+ __ InvokeFunction(function, actual, CALL_FUNCTION,
+ safepoint_generator, CALL_AS_METHOD);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoPushArgument(LPushArgument* instr) {
+ LOperand* argument = instr->value();
+ if (argument->IsDoubleRegister() || argument->IsDoubleStackSlot()) {
+ Abort(kDoPushArgumentNotImplementedForDoubleType);
+ } else {
+ Register argument_reg = EmitLoadRegister(argument, ip);
+ __ push(argument_reg);
+ }
+}
+
+
+void LCodeGen::DoDrop(LDrop* instr) {
+ __ Drop(instr->count());
+}
+
+
+void LCodeGen::DoThisFunction(LThisFunction* instr) {
+ Register result = ToRegister(instr->result());
+ __ ldr(result, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+}
+
+
+void LCodeGen::DoContext(LContext* instr) {
+ // If there is a non-return use, the context must be moved to a register.
+ Register result = ToRegister(instr->result());
+ for (HUseIterator it(instr->hydrogen()->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->IsReturn()) {
+ __ mov(result, cp);
+ return;
+ }
+ }
+}
+
+
+void LCodeGen::DoOuterContext(LOuterContext* instr) {
+ Register context = ToRegister(instr->context());
+ Register result = ToRegister(instr->result());
+ __ ldr(result,
+ MemOperand(context, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+}
+
+
+void LCodeGen::DoDeclareGlobals(LDeclareGlobals* instr) {
+ __ push(cp); // The context is the first argument.
+ __ LoadHeapObject(scratch0(), instr->hydrogen()->pairs());
+ __ push(scratch0());
+ __ mov(scratch0(), Operand(Smi::FromInt(instr->hydrogen()->flags())));
+ __ push(scratch0());
+ CallRuntime(Runtime::kDeclareGlobals, 3, instr);
+}
+
+
+void LCodeGen::DoGlobalObject(LGlobalObject* instr) {
+ Register result = ToRegister(instr->result());
+ __ ldr(result, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+}
+
+
+void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
+ Register global = ToRegister(instr->global_object());
+ Register result = ToRegister(instr->result());
+ __ ldr(result, FieldMemOperand(global, GlobalObject::kGlobalReceiverOffset));
+}
+
+
+void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
+ int formal_parameter_count,
+ int arity,
+ LInstruction* instr,
+ CallKind call_kind,
+ R1State r1_state) {
+ bool dont_adapt_arguments =
+ formal_parameter_count == SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+ bool can_invoke_directly =
+ dont_adapt_arguments || formal_parameter_count == arity;
+
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+
+ if (can_invoke_directly) {
+ if (r1_state == R1_UNINITIALIZED) {
+ __ LoadHeapObject(r1, function);
+ }
+
+ // 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 (dont_adapt_arguments) {
+ __ 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);
+ ParameterCount expected(formal_parameter_count);
+ __ InvokeFunction(
+ function, expected, count, CALL_FUNCTION, generator, call_kind);
+ }
+
+ // Restore context.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
+ ASSERT(ToRegister(instr->result()).is(r0));
+ CallKnownFunction(instr->hydrogen()->function(),
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD,
+ R1_UNINITIALIZED);
+}
+
+
+void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+
+ // Deoptimize if not a heap number.
+ __ ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
+ __ cmp(scratch, Operand(ip));
+ DeoptimizeIf(ne, instr->environment());
+
+ Label done;
+ Register exponent = scratch0();
+ scratch = no_reg;
+ __ ldr(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset));
+ // Check the sign of the argument. If the argument is positive, just
+ // return it.
+ __ tst(exponent, Operand(HeapNumber::kSignMask));
+ // Move the input to the result if necessary.
+ __ Move(result, input);
+ __ b(eq, &done);
+
+ // 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(r1) ? r0 : r1;
+ Register tmp2 = input.is(r2) ? r0 : r2;
+ Register tmp3 = input.is(r3) ? r0 : r3;
+ Register tmp4 = input.is(r4) ? r0 : r4;
+
+ // exponent: floating point exponent value.
+
+ Label allocated, slow;
+ __ LoadRoot(tmp4, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(tmp1, tmp2, tmp3, tmp4, &slow);
+ __ b(&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(r0)) __ mov(tmp1, Operand(r0));
+ // Restore input_reg after call to runtime.
+ __ LoadFromSafepointRegisterSlot(input, input);
+ __ ldr(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset));
+
+ __ bind(&allocated);
+ // exponent: floating point exponent value.
+ // tmp1: allocated heap number.
+ __ bic(exponent, exponent, Operand(HeapNumber::kSignMask));
+ __ str(exponent, FieldMemOperand(tmp1, HeapNumber::kExponentOffset));
+ __ ldr(tmp2, FieldMemOperand(input, HeapNumber::kMantissaOffset));
+ __ str(tmp2, FieldMemOperand(tmp1, HeapNumber::kMantissaOffset));
+
+ __ StoreToSafepointRegisterSlot(tmp1, result);
+ }
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::EmitIntegerMathAbs(LMathAbs* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ __ cmp(input, Operand::Zero());
+ __ Move(result, input, pl);
+ // We can make rsb conditional because the previous cmp instruction
+ // will clear the V (overflow) flag and rsb won't set this flag
+ // if input is positive.
+ __ rsb(result, input, Operand::Zero(), SetCC, mi);
+ // Deoptimize on overflow.
+ DeoptimizeIf(vs, instr->environment());
+}
+
+
+void LCodeGen::DoMathAbs(LMathAbs* instr) {
+ // Class for deferred case.
+ class DeferredMathAbsTaggedHeapNumber: public LDeferredCode {
+ public:
+ DeferredMathAbsTaggedHeapNumber(LCodeGen* codegen, LMathAbs* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LMathAbs* instr_;
+ };
+
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsDouble()) {
+ DwVfpRegister input = ToDoubleRegister(instr->value());
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ __ vabs(result, input);
+ } else if (r.IsSmiOrInteger32()) {
+ EmitIntegerMathAbs(instr);
+ } else {
+ // Representation is tagged.
+ DeferredMathAbsTaggedHeapNumber* deferred =
+ new(zone()) DeferredMathAbsTaggedHeapNumber(this, instr);
+ Register input = ToRegister(instr->value());
+ // Smi check.
+ __ JumpIfNotSmi(input, deferred->entry());
+ // If smi, handle it directly.
+ EmitIntegerMathAbs(instr);
+ __ bind(deferred->exit());
+ }
+}
+
+
+void LCodeGen::DoMathFloor(LMathFloor* instr) {
+ DwVfpRegister input = ToDoubleRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ Register input_high = scratch0();
+ Label done, exact;
+
+ __ TryInt32Floor(result, input, input_high, double_scratch0(), &done, &exact);
+ DeoptimizeIf(al, instr->environment());
+
+ __ bind(&exact);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Test for -0.
+ __ cmp(result, Operand::Zero());
+ __ b(ne, &done);
+ __ cmp(input_high, Operand::Zero());
+ DeoptimizeIf(mi, instr->environment());
+ }
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoMathRound(LMathRound* instr) {
+ DwVfpRegister input = ToDoubleRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ DwVfpRegister double_scratch1 = ToDoubleRegister(instr->temp());
+ DwVfpRegister input_plus_dot_five = double_scratch1;
+ Register input_high = scratch0();
+ DwVfpRegister dot_five = double_scratch0();
+ Label convert, done;
+
+ __ Vmov(dot_five, 0.5, scratch0());
+ __ vabs(double_scratch1, input);
+ __ VFPCompareAndSetFlags(double_scratch1, dot_five);
+ // If input is in [-0.5, -0], the result is -0.
+ // If input is in [+0, +0.5[, the result is +0.
+ // If the input is +0.5, the result is 1.
+ __ b(hi, &convert); // Out of [-0.5, +0.5].
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ VmovHigh(input_high, input);
+ __ cmp(input_high, Operand::Zero());
+ DeoptimizeIf(mi, instr->environment()); // [-0.5, -0].
+ }
+ __ VFPCompareAndSetFlags(input, dot_five);
+ __ mov(result, Operand(1), LeaveCC, eq); // +0.5.
+ // Remaining cases: [+0, +0.5[ or [-0.5, +0.5[, depending on
+ // flag kBailoutOnMinusZero.
+ __ mov(result, Operand::Zero(), LeaveCC, ne);
+ __ b(&done);
+
+ __ bind(&convert);
+ __ vadd(input_plus_dot_five, input, dot_five);
+ // Reuse dot_five (double_scratch0) as we no longer need this value.
+ __ TryInt32Floor(result, input_plus_dot_five, input_high, double_scratch0(),
+ &done, &done);
+ DeoptimizeIf(al, instr->environment());
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoMathSqrt(LMathSqrt* instr) {
+ DwVfpRegister input = ToDoubleRegister(instr->value());
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ __ vsqrt(result, input);
+}
+
+
+void LCodeGen::DoMathPowHalf(LMathPowHalf* instr) {
+ DwVfpRegister input = ToDoubleRegister(instr->value());
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ DwVfpRegister temp = ToDoubleRegister(instr->temp());
+
+ // 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, scratch0());
+ __ 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) {
+ 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->right()->IsDoubleRegister() ||
+ ToDoubleRegister(instr->right()).is(d2));
+ ASSERT(!instr->right()->IsRegister() ||
+ ToRegister(instr->right()).is(r2));
+ ASSERT(ToDoubleRegister(instr->left()).is(d1));
+ ASSERT(ToDoubleRegister(instr->result()).is(d3));
+
+ if (exponent_type.IsSmi()) {
+ MathPowStub stub(MathPowStub::TAGGED);
+ __ CallStub(&stub);
+ } else 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()) {
+ MathPowStub stub(MathPowStub::INTEGER);
+ __ CallStub(&stub);
+ } else {
+ ASSERT(exponent_type.IsDouble());
+ MathPowStub stub(MathPowStub::DOUBLE);
+ __ CallStub(&stub);
+ }
+}
+
+
+void LCodeGen::DoRandom(LRandom* instr) {
+ class DeferredDoRandom: public LDeferredCode {
+ public:
+ DeferredDoRandom(LCodeGen* codegen, LRandom* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredRandom(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LRandom* instr_;
+ };
+
+ DeferredDoRandom* deferred = new(zone()) DeferredDoRandom(this, instr);
+
+ // Having marked this instruction as a call we can use any
+ // registers.
+ ASSERT(ToDoubleRegister(instr->result()).is(d7));
+ ASSERT(ToRegister(instr->global_object()).is(r0));
+
+ static const int kSeedSize = sizeof(uint32_t);
+ STATIC_ASSERT(kPointerSize == kSeedSize);
+
+ __ ldr(r0, FieldMemOperand(r0, GlobalObject::kNativeContextOffset));
+ static const int kRandomSeedOffset =
+ FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
+ __ ldr(r2, FieldMemOperand(r0, kRandomSeedOffset));
+ // r2: FixedArray of the native context's random seeds
+
+ // Load state[0].
+ __ ldr(r1, FieldMemOperand(r2, ByteArray::kHeaderSize));
+ __ cmp(r1, Operand::Zero());
+ __ b(eq, deferred->entry());
+ // Load state[1].
+ __ ldr(r0, FieldMemOperand(r2, ByteArray::kHeaderSize + kSeedSize));
+ // r1: state[0].
+ // r0: state[1].
+
+ // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
+ __ and_(r3, r1, Operand(0xFFFF));
+ __ mov(r4, Operand(18273));
+ __ mul(r3, r3, r4);
+ __ add(r1, r3, Operand(r1, LSR, 16));
+ // Save state[0].
+ __ str(r1, FieldMemOperand(r2, ByteArray::kHeaderSize));
+
+ // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
+ __ and_(r3, r0, Operand(0xFFFF));
+ __ mov(r4, Operand(36969));
+ __ mul(r3, r3, r4);
+ __ add(r0, r3, Operand(r0, LSR, 16));
+ // Save state[1].
+ __ str(r0, FieldMemOperand(r2, ByteArray::kHeaderSize + kSeedSize));
+
+ // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF)
+ __ and_(r0, r0, Operand(0x3FFFF));
+ __ add(r0, r0, Operand(r1, LSL, 14));
+
+ __ bind(deferred->exit());
+ // 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::Zero());
+ __ vmov(d8, r0, r1);
+ // Subtract and store the result in the heap number.
+ __ vsub(d7, d7, d8);
+}
+
+
+void LCodeGen::DoDeferredRandom(LRandom* instr) {
+ __ PrepareCallCFunction(1, scratch0());
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
+ // Return value is in r0.
+}
+
+
+void LCodeGen::DoMathExp(LMathExp* instr) {
+ DwVfpRegister input = ToDoubleRegister(instr->value());
+ DwVfpRegister result = ToDoubleRegister(instr->result());
+ DwVfpRegister double_scratch1 = ToDoubleRegister(instr->double_temp());
+ DwVfpRegister double_scratch2 = double_scratch0();
+ Register temp1 = ToRegister(instr->temp1());
+ Register temp2 = ToRegister(instr->temp2());
+
+ MathExpGenerator::EmitMathExp(
+ masm(), input, result, double_scratch1, double_scratch2,
+ temp1, temp2, scratch0());
+}
+
+
+void LCodeGen::DoMathLog(LMathLog* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(d2));
+ TranscendentalCacheStub stub(TranscendentalCache::LOG,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathTan(LMathTan* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(d2));
+ TranscendentalCacheStub stub(TranscendentalCache::TAN,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathCos(LMathCos* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(d2));
+ TranscendentalCacheStub stub(TranscendentalCache::COS,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathSin(LMathSin* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(d2));
+ TranscendentalCacheStub stub(TranscendentalCache::SIN,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(r1));
+ ASSERT(instr->HasPointerMap());
+
+ Handle<JSFunction> known_function = instr->hydrogen()->known_function();
+ if (known_function.is_null()) {
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+ SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt);
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(r1, count, CALL_FUNCTION, generator, CALL_AS_METHOD);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ } else {
+ CallKnownFunction(known_function,
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD,
+ R1_CONTAINS_TARGET);
+ }
+}
+
+
+void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ int arity = instr->arity();
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arity);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallNamed(LCallNamed* instr) {
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ int arity = instr->arity();
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
+ __ mov(r2, Operand(instr->name()));
+ CallCode(ic, mode, instr, NEVER_INLINE_TARGET_ADDRESS);
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallFunction(LCallFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(r1));
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ int arity = instr->arity();
+ CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ int arity = instr->arity();
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
+ __ mov(r2, Operand(instr->name()));
+ CallCode(ic, mode, instr, NEVER_INLINE_TARGET_ADDRESS);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) {
+ ASSERT(ToRegister(instr->result()).is(r0));
+ CallKnownFunction(instr->hydrogen()->target(),
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_FUNCTION,
+ R1_UNINITIALIZED);
+}
+
+
+void LCodeGen::DoCallNew(LCallNew* instr) {
+ ASSERT(ToRegister(instr->constructor()).is(r1));
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ __ mov(r0, Operand(instr->arity()));
+ // No cell in r2 for construct type feedback in optimized code
+ Handle<Object> undefined_value(isolate()->factory()->undefined_value());
+ __ mov(r2, Operand(undefined_value));
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+}
+
+
+void LCodeGen::DoCallNewArray(LCallNewArray* instr) {
+ ASSERT(ToRegister(instr->constructor()).is(r1));
+ ASSERT(ToRegister(instr->result()).is(r0));
+
+ __ mov(r0, Operand(instr->arity()));
+ __ mov(r2, Operand(instr->hydrogen()->property_cell()));
+ ElementsKind kind = instr->hydrogen()->elements_kind();
+ AllocationSiteOverrideMode override_mode =
+ (AllocationSite::GetMode(kind) == TRACK_ALLOCATION_SITE)
+ ? DISABLE_ALLOCATION_SITES
+ : DONT_OVERRIDE;
+ ContextCheckMode context_mode = CONTEXT_CHECK_NOT_REQUIRED;
+
+ if (instr->arity() == 0) {
+ ArrayNoArgumentConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ } else if (instr->arity() == 1) {
+ Label done;
+ if (IsFastPackedElementsKind(kind)) {
+ Label packed_case;
+ // We might need a change here
+ // look at the first argument
+ __ ldr(r5, MemOperand(sp, 0));
+ __ cmp(r5, Operand::Zero());
+ __ b(eq, &packed_case);
+
+ ElementsKind holey_kind = GetHoleyElementsKind(kind);
+ ArraySingleArgumentConstructorStub stub(holey_kind, context_mode,
+ override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ __ jmp(&done);
+ __ bind(&packed_case);
+ }
+
+ ArraySingleArgumentConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ __ bind(&done);
+ } else {
+ ArrayNArgumentsConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ }
+}
+
+
+void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
+ CallRuntime(instr->function(), instr->arity(), instr);
+}
+
+
+void LCodeGen::DoInnerAllocatedObject(LInnerAllocatedObject* instr) {
+ Register result = ToRegister(instr->result());
+ Register base = ToRegister(instr->base_object());
+ __ add(result, base, Operand(instr->offset()));
+}
+
+
+void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
+ Representation representation = instr->representation();
+
+ Register object = ToRegister(instr->object());
+ Register scratch = scratch0();
+ HObjectAccess access = instr->hydrogen()->access();
+ int offset = access.offset();
+
+ if (access.IsExternalMemory()) {
+ Register value = ToRegister(instr->value());
+ __ str(value, MemOperand(object, offset));
+ return;
+ }
+
+ Handle<Map> transition = instr->transition();
+
+ if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ Register value = ToRegister(instr->value());
+ if (!instr->hydrogen()->value()->type().IsHeapObject()) {
+ __ SmiTst(value);
+ DeoptimizeIf(eq, instr->environment());
+ }
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ ASSERT(transition.is_null());
+ ASSERT(access.IsInobject());
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ DwVfpRegister value = ToDoubleRegister(instr->value());
+ __ vstr(value, FieldMemOperand(object, offset));
+ return;
+ }
+
+ if (!transition.is_null()) {
+ __ mov(scratch, Operand(transition));
+ __ str(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ if (instr->hydrogen()->NeedsWriteBarrierForMap()) {
+ Register temp = ToRegister(instr->temp());
+ // Update the write barrier for the map field.
+ __ RecordWriteField(object,
+ HeapObject::kMapOffset,
+ scratch,
+ temp,
+ GetLinkRegisterState(),
+ kSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ }
+ }
+
+ // Do the store.
+ Register value = ToRegister(instr->value());
+ ASSERT(!object.is(value));
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ if (access.IsInobject()) {
+ __ str(value, FieldMemOperand(object, offset));
+ if (instr->hydrogen()->NeedsWriteBarrier()) {
+ // Update the write barrier for the object for in-object properties.
+ __ RecordWriteField(object,
+ offset,
+ value,
+ scratch,
+ GetLinkRegisterState(),
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+ } else {
+ __ ldr(scratch, FieldMemOperand(object, JSObject::kPropertiesOffset));
+ __ str(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,
+ GetLinkRegisterState(),
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+ }
+}
+
+
+void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
+ ASSERT(ToRegister(instr->object()).is(r1));
+ ASSERT(ToRegister(instr->value()).is(r0));
+
+ // Name is always in r2.
+ __ mov(r2, 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, NEVER_INLINE_TARGET_ADDRESS);
+}
+
+
+void LCodeGen::ApplyCheckIf(Condition condition, LBoundsCheck* check) {
+ if (FLAG_debug_code && check->hydrogen()->skip_check()) {
+ Label done;
+ __ b(NegateCondition(condition), &done);
+ __ stop("eliminated bounds check failed");
+ __ bind(&done);
+ } else {
+ DeoptimizeIf(condition, check->environment());
+ }
+}
+
+
+void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
+ if (instr->hydrogen()->skip_check()) return;
+
+ if (instr->index()->IsConstantOperand()) {
+ int constant_index =
+ ToInteger32(LConstantOperand::cast(instr->index()));
+ if (instr->hydrogen()->length()->representation().IsSmi()) {
+ __ mov(ip, Operand(Smi::FromInt(constant_index)));
+ } else {
+ __ mov(ip, Operand(constant_index));
+ }
+ __ cmp(ip, ToRegister(instr->length()));
+ } else {
+ __ cmp(ToRegister(instr->index()), ToRegister(instr->length()));
+ }
+ Condition condition = instr->hydrogen()->allow_equality() ? hi : hs;
+ ApplyCheckIf(condition, instr);
+}
+
+
+void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) {
+ Register external_pointer = ToRegister(instr->elements());
+ 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(kArrayIndexConstantValueTooBig);
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+ int element_size_shift = ElementsKindToShiftSize(elements_kind);
+ int shift_size = (instr->hydrogen()->key()->representation().IsSmi())
+ ? (element_size_shift - kSmiTagSize) : element_size_shift;
+ int additional_offset = instr->additional_index() << element_size_shift;
+
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS ||
+ elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ DwVfpRegister value(ToDoubleRegister(instr->value()));
+ Operand operand(key_is_constant
+ ? Operand(constant_key << element_size_shift)
+ : Operand(key, LSL, shift_size));
+ __ add(scratch0(), external_pointer, operand);
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ __ vcvt_f32_f64(double_scratch0().low(), value);
+ __ vstr(double_scratch0().low(), scratch0(), additional_offset);
+ } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS
+ __ vstr(value, scratch0(), additional_offset);
+ }
+ } else {
+ Register value(ToRegister(instr->value()));
+ MemOperand mem_operand = PrepareKeyedOperand(
+ key, external_pointer, key_is_constant, constant_key,
+ element_size_shift, shift_size,
+ instr->additional_index(), additional_offset);
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ strb(value, mem_operand);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ strh(value, mem_operand);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ str(value, mem_operand);
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr) {
+ DwVfpRegister 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;
+
+ // 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(kArrayIndexConstantValueTooBig);
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+ int element_size_shift = ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS);
+ int shift_size = (instr->hydrogen()->key()->representation().IsSmi())
+ ? (element_size_shift - kSmiTagSize) : element_size_shift;
+ Operand operand = key_is_constant
+ ? Operand((constant_key << element_size_shift) +
+ FixedDoubleArray::kHeaderSize - kHeapObjectTag)
+ : Operand(key, LSL, shift_size);
+ __ add(scratch, elements, operand);
+ if (!key_is_constant) {
+ __ add(scratch, scratch,
+ Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag));
+ }
+
+ if (instr->NeedsCanonicalization()) {
+ // Force a canonical NaN.
+ if (masm()->emit_debug_code()) {
+ __ vmrs(ip);
+ __ tst(ip, Operand(kVFPDefaultNaNModeControlBit));
+ __ Assert(ne, kDefaultNaNModeNotSet);
+ }
+ __ VFPCanonicalizeNaN(value);
+ }
+ __ vstr(value, scratch, instr->additional_index() << element_size_shift);
+}
+
+
+void LCodeGen::DoStoreKeyedFixedArray(LStoreKeyed* instr) {
+ Register value = ToRegister(instr->value());
+ Register elements = ToRegister(instr->elements());
+ Register key = instr->key()->IsRegister() ? ToRegister(instr->key())
+ : no_reg;
+ Register scratch = scratch0();
+ Register store_base = scratch;
+ int offset = 0;
+
+ // Do the store.
+ if (instr->key()->IsConstantOperand()) {
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ LConstantOperand* const_operand = LConstantOperand::cast(instr->key());
+ offset = FixedArray::OffsetOfElementAt(ToInteger32(const_operand) +
+ instr->additional_index());
+ store_base = elements;
+ } else {
+ // Even though the HLoadKeyed instruction forces the input
+ // representation for the key to be an integer, the input gets replaced
+ // during bound check elimination with the index argument to the bounds
+ // check, which can be tagged, so that case must be handled here, too.
+ if (instr->hydrogen()->key()->representation().IsSmi()) {
+ __ add(scratch, elements, Operand::PointerOffsetFromSmiKey(key));
+ } else {
+ __ add(scratch, elements, Operand(key, LSL, kPointerSizeLog2));
+ }
+ offset = FixedArray::OffsetOfElementAt(instr->additional_index());
+ }
+ __ str(value, FieldMemOperand(store_base, offset));
+
+ if (instr->hydrogen()->NeedsWriteBarrier()) {
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ // Compute address of modified element and store it into key register.
+ __ add(key, store_base, Operand(offset - kHeapObjectTag));
+ __ RecordWrite(elements,
+ key,
+ value,
+ GetLinkRegisterState(),
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+}
+
+
+void LCodeGen::DoStoreKeyed(LStoreKeyed* instr) {
+ // By cases: external, fast double
+ if (instr->is_external()) {
+ DoStoreKeyedExternalArray(instr);
+ } else if (instr->hydrogen()->value()->representation().IsDouble()) {
+ DoStoreKeyedFixedDoubleArray(instr);
+ } else {
+ DoStoreKeyedFixedArray(instr);
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
+ ASSERT(ToRegister(instr->object()).is(r2));
+ ASSERT(ToRegister(instr->key()).is(r1));
+ ASSERT(ToRegister(instr->value()).is(r0));
+
+ Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode)
+ ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
+ : isolate()->builtins()->KeyedStoreIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS);
+}
+
+
+void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
+ Register object_reg = ToRegister(instr->object());
+ Register scratch = scratch0();
+
+ Handle<Map> from_map = instr->original_map();
+ Handle<Map> to_map = instr->transitioned_map();
+ ElementsKind from_kind = instr->from_kind();
+ ElementsKind to_kind = instr->to_kind();
+
+ Label not_applicable;
+ __ ldr(scratch, FieldMemOperand(object_reg, HeapObject::kMapOffset));
+ __ cmp(scratch, Operand(from_map));
+ __ b(ne, &not_applicable);
+
+ if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
+ Register new_map_reg = ToRegister(instr->new_map_temp());
+ __ mov(new_map_reg, Operand(to_map));
+ __ str(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset));
+ // Write barrier.
+ __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
+ scratch, GetLinkRegisterState(), kDontSaveFPRegs);
+ } else {
+ PushSafepointRegistersScope scope(
+ this, Safepoint::kWithRegistersAndDoubles);
+ __ Move(r0, object_reg);
+ __ Move(r1, to_map);
+ TransitionElementsKindStub stub(from_kind, to_kind);
+ __ CallStub(&stub);
+ RecordSafepointWithRegistersAndDoubles(
+ instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
+ }
+ __ bind(&not_applicable);
+}
+
+
+void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
+ Register object = ToRegister(instr->object());
+ Register temp = ToRegister(instr->temp());
+ __ TestJSArrayForAllocationMemento(object, temp);
+ DeoptimizeIf(eq, instr->environment());
+}
+
+
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ __ push(ToRegister(instr->left()));
+ __ push(ToRegister(instr->right()));
+ StringAddStub stub(instr->hydrogen()->flags());
+ CallCode(stub.GetCode(isolate()), 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(zone()) 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, Operand::Zero());
+
+ 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()));
+ __ mov(scratch, Operand(Smi::FromInt(const_index)));
+ __ push(scratch);
+ } else {
+ Register index = ToRegister(instr->index());
+ __ SmiTag(index);
+ __ push(index);
+ }
+ CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr);
+ __ AssertSmi(r0);
+ __ SmiUntag(r0);
+ __ StoreToSafepointRegisterSlot(r0, 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(zone()) DeferredStringCharFromCode(this, instr);
+
+ ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+ ASSERT(!char_code.is(result));
+
+ __ cmp(char_code, Operand(String::kMaxOneByteCharCode));
+ __ b(hi, deferred->entry());
+ __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
+ __ add(result, result, Operand(char_code, LSL, kPointerSizeLog2));
+ __ ldr(result, FieldMemOperand(result, FixedArray::kHeaderSize));
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(result, ip);
+ __ b(eq, deferred->entry());
+ __ 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, Operand::Zero());
+
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+ __ SmiTag(char_code);
+ __ push(char_code);
+ CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr);
+ __ StoreToSafepointRegisterSlot(r0, result);
+}
+
+
+void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() || input->IsStackSlot());
+ LOperand* output = instr->result();
+ ASSERT(output->IsDoubleRegister());
+ SwVfpRegister single_scratch = double_scratch0().low();
+ if (input->IsStackSlot()) {
+ Register scratch = scratch0();
+ __ ldr(scratch, ToMemOperand(input));
+ __ vmov(single_scratch, scratch);
+ } else {
+ __ vmov(single_scratch, ToRegister(input));
+ }
+ __ vcvt_f64_s32(ToDoubleRegister(output), single_scratch);
+}
+
+
+void LCodeGen::DoInteger32ToSmi(LInteger32ToSmi* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ LOperand* output = instr->result();
+ ASSERT(output->IsRegister());
+ __ SmiTag(ToRegister(output), ToRegister(input), SetCC);
+ if (!instr->hydrogen()->value()->HasRange() ||
+ !instr->hydrogen()->value()->range()->IsInSmiRange()) {
+ DeoptimizeIf(vs, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) {
+ LOperand* input = instr->value();
+ LOperand* output = instr->result();
+
+ SwVfpRegister flt_scratch = double_scratch0().low();
+ __ vmov(flt_scratch, ToRegister(input));
+ __ vcvt_f64_u32(ToDoubleRegister(output), flt_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_,
+ instr_->value(),
+ SIGNED_INT32);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LNumberTagI* instr_;
+ };
+
+ Register src = ToRegister(instr->value());
+ Register dst = ToRegister(instr->result());
+
+ DeferredNumberTagI* deferred = new(zone()) DeferredNumberTagI(this, instr);
+ __ SmiTag(dst, src, SetCC);
+ __ b(vs, deferred->entry());
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
+ class DeferredNumberTagU: public LDeferredCode {
+ public:
+ DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredNumberTagI(instr_,
+ instr_->value(),
+ UNSIGNED_INT32);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LNumberTagU* instr_;
+ };
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ Register reg = ToRegister(input);
+
+ DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr);
+ __ cmp(reg, Operand(Smi::kMaxValue));
+ __ b(hi, deferred->entry());
+ __ SmiTag(reg, reg);
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredNumberTagI(LInstruction* instr,
+ LOperand* value,
+ IntegerSignedness signedness) {
+ Label slow;
+ Register src = ToRegister(value);
+ Register dst = ToRegister(instr->result());
+ LowDwVfpRegister dbl_scratch = double_scratch0();
+
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+
+ Label done;
+ if (signedness == SIGNED_INT32) {
+ // 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.
+ if (dst.is(src)) {
+ __ SmiUntag(src, dst);
+ __ eor(src, src, Operand(0x80000000));
+ }
+ __ vmov(dbl_scratch.low(), src);
+ __ vcvt_f64_s32(dbl_scratch, dbl_scratch.low());
+ } else {
+ __ vmov(dbl_scratch.low(), src);
+ __ vcvt_f64_u32(dbl_scratch, dbl_scratch.low());
+ }
+
+ if (FLAG_inline_new) {
+ __ LoadRoot(scratch0(), Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r5, r3, r4, scratch0(), &slow, DONT_TAG_RESULT);
+ __ Move(dst, r5);
+ __ b(&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.
+ __ mov(ip, Operand::Zero());
+ __ StoreToSafepointRegisterSlot(ip, dst);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
+ __ Move(dst, r0);
+ __ sub(dst, dst, Operand(kHeapObjectTag));
+
+ // Done. Put the value in dbl_scratch into the value of the allocated heap
+ // number.
+ __ bind(&done);
+ __ vstr(dbl_scratch, dst, HeapNumber::kValueOffset);
+ __ add(dst, dst, Operand(kHeapObjectTag));
+ __ 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_;
+ };
+
+ DwVfpRegister input_reg = ToDoubleRegister(instr->value());
+ Register scratch = scratch0();
+ Register reg = ToRegister(instr->result());
+ Register temp1 = ToRegister(instr->temp());
+ Register temp2 = ToRegister(instr->temp2());
+
+ DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr);
+ if (FLAG_inline_new) {
+ __ LoadRoot(scratch, Heap::kHeapNumberMapRootIndex);
+ // We want the untagged address first for performance
+ __ AllocateHeapNumber(reg, temp1, temp2, scratch, deferred->entry(),
+ DONT_TAG_RESULT);
+ } else {
+ __ jmp(deferred->entry());
+ }
+ __ bind(deferred->exit());
+ __ vstr(input_reg, reg, HeapNumber::kValueOffset);
+ // Now that we have finished with the object's real address tag it
+ __ add(reg, reg, Operand(kHeapObjectTag));
+}
+
+
+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, Operand::Zero());
+
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
+ __ sub(r0, r0, Operand(kHeapObjectTag));
+ __ StoreToSafepointRegisterSlot(r0, reg);
+}
+
+
+void LCodeGen::DoSmiTag(LSmiTag* instr) {
+ ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow));
+ __ SmiTag(ToRegister(instr->result()), ToRegister(instr->value()));
+}
+
+
+void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
+ Register input = ToRegister(instr->value());
+ 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(result, input, SetCC);
+ DeoptimizeIf(cs, instr->environment());
+ } else {
+ __ SmiUntag(result, input);
+ }
+}
+
+
+void LCodeGen::EmitNumberUntagD(Register input_reg,
+ DwVfpRegister result_reg,
+ bool can_convert_undefined_to_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode) {
+ Register scratch = scratch0();
+ SwVfpRegister flt_scratch = double_scratch0().low();
+ ASSERT(!result_reg.is(double_scratch0()));
+
+ Label load_smi, heap_number, done;
+
+ if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) {
+ // Smi check.
+ __ UntagAndJumpIfSmi(scratch, input_reg, &load_smi);
+
+ // Heap number map check.
+ __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
+ __ cmp(scratch, Operand(ip));
+ if (!can_convert_undefined_to_nan) {
+ DeoptimizeIf(ne, env);
+ } else {
+ Label heap_number, convert;
+ __ b(eq, &heap_number);
+
+ // Convert undefined (and hole) to NaN.
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(input_reg, Operand(ip));
+ DeoptimizeIf(ne, env);
+
+ __ bind(&convert);
+ __ LoadRoot(scratch, Heap::kNanValueRootIndex);
+ __ vldr(result_reg, scratch, HeapNumber::kValueOffset - kHeapObjectTag);
+ __ jmp(&done);
+
+ __ bind(&heap_number);
+ }
+ // Heap number to double register conversion.
+ __ vldr(result_reg, input_reg, HeapNumber::kValueOffset - kHeapObjectTag);
+ if (deoptimize_on_minus_zero) {
+ __ VmovLow(scratch, result_reg);
+ __ cmp(scratch, Operand::Zero());
+ __ b(ne, &done);
+ __ VmovHigh(scratch, result_reg);
+ __ cmp(scratch, Operand(HeapNumber::kSignMask));
+ DeoptimizeIf(eq, env);
+ }
+ __ jmp(&done);
+ } else {
+ __ SmiUntag(scratch, input_reg);
+ ASSERT(mode == NUMBER_CANDIDATE_IS_SMI);
+ }
+
+ // Smi to double register conversion
+ __ bind(&load_smi);
+ // scratch: untagged value of input_reg
+ __ vmov(flt_scratch, scratch);
+ __ vcvt_f64_s32(result_reg, flt_scratch);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
+ Register input_reg = ToRegister(instr->value());
+ Register scratch1 = scratch0();
+ Register scratch2 = ToRegister(instr->temp());
+ LowDwVfpRegister double_scratch = double_scratch0();
+ DwVfpRegister double_scratch2 = ToDoubleRegister(instr->temp3());
+
+ ASSERT(!scratch1.is(input_reg) && !scratch1.is(scratch2));
+ ASSERT(!scratch2.is(input_reg) && !scratch2.is(scratch1));
+
+ Label done;
+
+ // The input was optimistically untagged; revert it.
+ // The carry flag is set when we reach this deferred code as we just executed
+ // SmiUntag(heap_object, SetCC)
+ STATIC_ASSERT(kHeapObjectTag == 1);
+ __ adc(input_reg, input_reg, Operand(input_reg));
+
+ // Heap number map check.
+ __ ldr(scratch1, FieldMemOperand(input_reg, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
+ __ cmp(scratch1, Operand(ip));
+
+ if (instr->truncating()) {
+ Register scratch3 = ToRegister(instr->temp2());
+ 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;
+ __ b(eq, &heap_number);
+ // Check for undefined. Undefined is converted to zero for truncating
+ // conversions.
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(input_reg, Operand(ip));
+ DeoptimizeIf(ne, instr->environment());
+ __ mov(input_reg, Operand::Zero());
+ __ b(&done);
+
+ __ bind(&heap_number);
+ __ sub(scratch1, input_reg, Operand(kHeapObjectTag));
+ __ vldr(double_scratch2, scratch1, HeapNumber::kValueOffset);
+
+ __ ECMAToInt32(input_reg, double_scratch2,
+ scratch1, scratch2, scratch3, double_scratch);
+
+ } else {
+ // Deoptimize if we don't have a heap number.
+ DeoptimizeIf(ne, instr->environment());
+
+ __ sub(ip, input_reg, Operand(kHeapObjectTag));
+ __ vldr(double_scratch2, ip, HeapNumber::kValueOffset);
+ __ TryDoubleToInt32Exact(input_reg, double_scratch2, double_scratch);
+ DeoptimizeIf(ne, instr->environment());
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ cmp(input_reg, Operand::Zero());
+ __ b(ne, &done);
+ __ VmovHigh(scratch1, double_scratch2);
+ __ tst(scratch1, Operand(HeapNumber::kSignMask));
+ DeoptimizeIf(ne, instr->environment());
+ }
+ }
+ __ 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->value();
+ ASSERT(input->IsRegister());
+ ASSERT(input->Equals(instr->result()));
+
+ Register input_reg = ToRegister(input);
+
+ DeferredTaggedToI* deferred = new(zone()) DeferredTaggedToI(this, instr);
+
+ // Optimistically untag the input.
+ // If the input is a HeapObject, SmiUntag will set the carry flag.
+ __ SmiUntag(input_reg, SetCC);
+ // Branch to deferred code if the input was tagged.
+ // The deferred code will take care of restoring the tag.
+ __ b(cs, deferred->entry());
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ LOperand* result = instr->result();
+ ASSERT(result->IsDoubleRegister());
+
+ Register input_reg = ToRegister(input);
+ DwVfpRegister result_reg = ToDoubleRegister(result);
+
+ HValue* value = instr->hydrogen()->value();
+ NumberUntagDMode mode = value->representation().IsSmi()
+ ? NUMBER_CANDIDATE_IS_SMI : NUMBER_CANDIDATE_IS_ANY_TAGGED;
+
+ EmitNumberUntagD(input_reg, result_reg,
+ instr->hydrogen()->can_convert_undefined_to_nan(),
+ instr->hydrogen()->deoptimize_on_minus_zero(),
+ instr->environment(),
+ mode);
+}
+
+
+void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
+ Register result_reg = ToRegister(instr->result());
+ Register scratch1 = scratch0();
+ Register scratch2 = ToRegister(instr->temp());
+ DwVfpRegister double_input = ToDoubleRegister(instr->value());
+ LowDwVfpRegister double_scratch = double_scratch0();
+
+ if (instr->truncating()) {
+ Register scratch3 = ToRegister(instr->temp2());
+ __ ECMAToInt32(result_reg, double_input,
+ scratch1, scratch2, scratch3, double_scratch);
+ } else {
+ __ TryDoubleToInt32Exact(result_reg, double_input, double_scratch);
+ // Deoptimize if the input wasn't a int32 (inside a double).
+ DeoptimizeIf(ne, instr->environment());
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label done;
+ __ cmp(result_reg, Operand::Zero());
+ __ b(ne, &done);
+ __ VmovHigh(scratch1, double_input);
+ __ tst(scratch1, Operand(HeapNumber::kSignMask));
+ DeoptimizeIf(ne, instr->environment());
+ __ bind(&done);
+ }
+ }
+}
+
+
+void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) {
+ Register result_reg = ToRegister(instr->result());
+ Register scratch1 = scratch0();
+ Register scratch2 = ToRegister(instr->temp());
+ DwVfpRegister double_input = ToDoubleRegister(instr->value());
+ LowDwVfpRegister double_scratch = double_scratch0();
+
+ if (instr->truncating()) {
+ Register scratch3 = ToRegister(instr->temp2());
+ __ ECMAToInt32(result_reg, double_input,
+ scratch1, scratch2, scratch3, double_scratch);
+ } else {
+ __ TryDoubleToInt32Exact(result_reg, double_input, double_scratch);
+ // Deoptimize if the input wasn't a int32 (inside a double).
+ DeoptimizeIf(ne, instr->environment());
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label done;
+ __ cmp(result_reg, Operand::Zero());
+ __ b(ne, &done);
+ __ VmovHigh(scratch1, double_input);
+ __ tst(scratch1, Operand(HeapNumber::kSignMask));
+ DeoptimizeIf(ne, instr->environment());
+ __ bind(&done);
+ }
+ }
+ __ SmiTag(result_reg, SetCC);
+ DeoptimizeIf(vs, instr->environment());
+}
+
+
+void LCodeGen::DoCheckSmi(LCheckSmi* instr) {
+ LOperand* input = instr->value();
+ __ SmiTst(ToRegister(input));
+ DeoptimizeIf(ne, instr->environment());
+}
+
+
+void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ LOperand* input = instr->value();
+ __ SmiTst(ToRegister(input));
+ DeoptimizeIf(eq, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
+ Register input = ToRegister(instr->value());
+ Register scratch = scratch0();
+
+ __ ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
+
+ if (instr->hydrogen()->is_interval_check()) {
+ InstanceType first;
+ InstanceType last;
+ instr->hydrogen()->GetCheckInterval(&first, &last);
+
+ __ cmp(scratch, Operand(first));
+
+ // If there is only one type in the interval check for equality.
+ if (first == last) {
+ DeoptimizeIf(ne, instr->environment());
+ } else {
+ DeoptimizeIf(lo, instr->environment());
+ // Omit check for the last type.
+ if (last != LAST_TYPE) {
+ __ cmp(scratch, Operand(last));
+ DeoptimizeIf(hi, instr->environment());
+ }
+ }
+ } else {
+ uint8_t mask;
+ uint8_t tag;
+ instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
+
+ if (IsPowerOf2(mask)) {
+ ASSERT(tag == 0 || IsPowerOf2(tag));
+ __ tst(scratch, Operand(mask));
+ DeoptimizeIf(tag == 0 ? ne : eq, instr->environment());
+ } else {
+ __ and_(scratch, scratch, Operand(mask));
+ __ cmp(scratch, Operand(tag));
+ DeoptimizeIf(ne, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
+ Register reg = ToRegister(instr->value());
+ Handle<JSFunction> target = instr->hydrogen()->target();
+ AllowDeferredHandleDereference smi_check;
+ if (isolate()->heap()->InNewSpace(*target)) {
+ Register reg = ToRegister(instr->value());
+ Handle<Cell> cell = isolate()->factory()->NewCell(target);
+ __ mov(ip, Operand(Handle<Object>(cell)));
+ __ ldr(ip, FieldMemOperand(ip, Cell::kValueOffset));
+ __ cmp(reg, ip);
+ } else {
+ __ cmp(reg, Operand(target));
+ }
+ DeoptimizeIf(ne, instr->environment());
+}
+
+
+void LCodeGen::DoDeferredInstanceMigration(LCheckMaps* instr, Register object) {
+ {
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+ __ push(object);
+ CallRuntimeFromDeferred(Runtime::kMigrateInstance, 1, instr);
+ __ StoreToSafepointRegisterSlot(r0, scratch0());
+ }
+ __ tst(scratch0(), Operand(kSmiTagMask));
+ DeoptimizeIf(eq, instr->environment());
+}
+
+
+void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
+ class DeferredCheckMaps: public LDeferredCode {
+ public:
+ DeferredCheckMaps(LCodeGen* codegen, LCheckMaps* instr, Register object)
+ : LDeferredCode(codegen), instr_(instr), object_(object) {
+ SetExit(check_maps());
+ }
+ virtual void Generate() {
+ codegen()->DoDeferredInstanceMigration(instr_, object_);
+ }
+ Label* check_maps() { return &check_maps_; }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LCheckMaps* instr_;
+ Label check_maps_;
+ Register object_;
+ };
+
+ if (instr->hydrogen()->CanOmitMapChecks()) return;
+ Register map_reg = scratch0();
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ Register reg = ToRegister(input);
+
+ SmallMapList* map_set = instr->hydrogen()->map_set();
+ __ ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
+
+ DeferredCheckMaps* deferred = NULL;
+ if (instr->hydrogen()->has_migration_target()) {
+ deferred = new(zone()) DeferredCheckMaps(this, instr, reg);
+ __ bind(deferred->check_maps());
+ }
+
+ Label success;
+ for (int i = 0; i < map_set->length() - 1; i++) {
+ Handle<Map> map = map_set->at(i);
+ __ CompareMap(map_reg, map, &success);
+ __ b(eq, &success);
+ }
+
+ Handle<Map> map = map_set->last();
+ __ CompareMap(map_reg, map, &success);
+ if (instr->hydrogen()->has_migration_target()) {
+ __ b(ne, deferred->entry());
+ } else {
+ DeoptimizeIf(ne, instr->environment());
+ }
+
+ __ bind(&success);
+}
+
+
+void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
+ DwVfpRegister value_reg = ToDoubleRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ __ ClampDoubleToUint8(result_reg, value_reg, double_scratch0());
+}
+
+
+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());
+ DwVfpRegister temp_reg = ToDoubleRegister(instr->temp());
+ Label is_smi, done, heap_number;
+
+ // Both smi and heap number cases are handled.
+ __ UntagAndJumpIfSmi(result_reg, input_reg, &is_smi);
+
+ // Check for heap number
+ __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
+ __ cmp(scratch, Operand(factory()->heap_number_map()));
+ __ b(eq, &heap_number);
+
+ // Check for undefined. Undefined is converted to zero for clamping
+ // conversions.
+ __ cmp(input_reg, Operand(factory()->undefined_value()));
+ DeoptimizeIf(ne, instr->environment());
+ __ mov(result_reg, Operand::Zero());
+ __ jmp(&done);
+
+ // Heap number
+ __ bind(&heap_number);
+ __ vldr(temp_reg, FieldMemOperand(input_reg, HeapNumber::kValueOffset));
+ __ ClampDoubleToUint8(result_reg, temp_reg, double_scratch0());
+ __ jmp(&done);
+
+ // smi
+ __ bind(&is_smi);
+ __ ClampUint8(result_reg, result_reg);
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoAllocate(LAllocate* instr) {
+ class DeferredAllocate: public LDeferredCode {
+ public:
+ DeferredAllocate(LCodeGen* codegen, LAllocate* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredAllocate(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LAllocate* instr_;
+ };
+
+ DeferredAllocate* deferred =
+ new(zone()) DeferredAllocate(this, instr);
+
+ Register result = ToRegister(instr->result());
+ Register scratch = ToRegister(instr->temp1());
+ Register scratch2 = ToRegister(instr->temp2());
+
+ // Allocate memory for the object.
+ AllocationFlags flags = TAG_OBJECT;
+ if (instr->hydrogen()->MustAllocateDoubleAligned()) {
+ flags = static_cast<AllocationFlags>(flags | DOUBLE_ALIGNMENT);
+ }
+ if (instr->hydrogen()->IsOldPointerSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation());
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ flags = static_cast<AllocationFlags>(flags | PRETENURE_OLD_POINTER_SPACE);
+ } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ flags = static_cast<AllocationFlags>(flags | PRETENURE_OLD_DATA_SPACE);
+ }
+
+ if (instr->size()->IsConstantOperand()) {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ Allocate(size, result, scratch, scratch2, deferred->entry(), flags);
+ } else {
+ Register size = ToRegister(instr->size());
+ __ Allocate(size,
+ result,
+ scratch,
+ scratch2,
+ deferred->entry(),
+ flags);
+ }
+
+ __ bind(deferred->exit());
+
+ if (instr->hydrogen()->MustPrefillWithFiller()) {
+ if (instr->size()->IsConstantOperand()) {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ mov(scratch, Operand(size));
+ } else {
+ scratch = ToRegister(instr->size());
+ }
+ __ sub(scratch, scratch, Operand(kPointerSize));
+ __ sub(result, result, Operand(kHeapObjectTag));
+ Label loop;
+ __ bind(&loop);
+ __ mov(scratch2, Operand(isolate()->factory()->one_pointer_filler_map()));
+ __ str(scratch2, MemOperand(result, scratch));
+ __ sub(scratch, scratch, Operand(kPointerSize));
+ __ cmp(scratch, Operand(0));
+ __ b(ge, &loop);
+ __ add(result, result, Operand(kHeapObjectTag));
+ }
+}
+
+
+void LCodeGen::DoDeferredAllocate(LAllocate* instr) {
+ 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, Operand(Smi::FromInt(0)));
+
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+ if (instr->size()->IsRegister()) {
+ Register size = ToRegister(instr->size());
+ ASSERT(!size.is(result));
+ __ SmiTag(size);
+ __ push(size);
+ } else {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ Push(Smi::FromInt(size));
+ }
+
+ if (instr->hydrogen()->IsOldPointerSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation());
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ CallRuntimeFromDeferred(Runtime::kAllocateInOldPointerSpace, 1, instr);
+ } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ CallRuntimeFromDeferred(Runtime::kAllocateInOldDataSpace, 1, instr);
+ } else {
+ CallRuntimeFromDeferred(Runtime::kAllocateInNewSpace, 1, instr);
+ }
+ __ StoreToSafepointRegisterSlot(r0, result);
+}
+
+
+void LCodeGen::DoToFastProperties(LToFastProperties* instr) {
+ ASSERT(ToRegister(instr->value()).is(r0));
+ __ push(r0);
+ CallRuntime(Runtime::kToFastProperties, 1, instr);
+}
+
+
+void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
+ Label materialized;
+ // Registers will be used as follows:
+ // r7 = literals array.
+ // r1 = regexp literal.
+ // r0 = regexp literal clone.
+ // r2 and r4-r6 are used as temporaries.
+ int literal_offset =
+ FixedArray::OffsetOfElementAt(instr->hydrogen()->literal_index());
+ __ LoadHeapObject(r7, instr->hydrogen()->literals());
+ __ ldr(r1, FieldMemOperand(r7, literal_offset));
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(r1, ip);
+ __ b(ne, &materialized);
+
+ // Create regexp literal using runtime function
+ // Result will be in r0.
+ __ mov(r6, Operand(Smi::FromInt(instr->hydrogen()->literal_index())));
+ __ mov(r5, Operand(instr->hydrogen()->pattern()));
+ __ mov(r4, Operand(instr->hydrogen()->flags()));
+ __ Push(r7, r6, r5, r4);
+ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr);
+ __ mov(r1, r0);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+
+ __ Allocate(size, r0, r2, r3, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ mov(r0, Operand(Smi::FromInt(size)));
+ __ Push(r1, r0);
+ CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
+ __ pop(r1);
+
+ __ bind(&allocated);
+ // Copy the content into the newly allocated memory.
+ __ CopyFields(r0, r1, double_scratch0(), 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.
+ bool pretenure = instr->hydrogen()->pretenure();
+ if (!pretenure && instr->hydrogen()->has_no_literals()) {
+ FastNewClosureStub stub(instr->hydrogen()->language_mode(),
+ instr->hydrogen()->is_generator());
+ __ mov(r1, Operand(instr->hydrogen()->shared_info()));
+ __ push(r1);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ } else {
+ __ mov(r2, Operand(instr->hydrogen()->shared_info()));
+ __ mov(r1, Operand(pretenure ? factory()->true_value()
+ : factory()->false_value()));
+ __ Push(cp, r2, r1);
+ CallRuntime(Runtime::kNewClosure, 3, instr);
+ }
+}
+
+
+void LCodeGen::DoTypeof(LTypeof* instr) {
+ Register input = ToRegister(instr->value());
+ __ push(input);
+ CallRuntime(Runtime::kTypeof, 1, instr);
+}
+
+
+void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+
+ Condition final_branch_condition = EmitTypeofIs(instr->TrueLabel(chunk_),
+ instr->FalseLabel(chunk_),
+ input,
+ instr->type_literal());
+ if (final_branch_condition != kNoCondition) {
+ EmitBranch(instr, final_branch_condition);
+ }
+}
+
+
+Condition LCodeGen::EmitTypeofIs(Label* true_label,
+ Label* false_label,
+ Register input,
+ Handle<String> type_name) {
+ Condition final_branch_condition = kNoCondition;
+ Register scratch = scratch0();
+ if (type_name->Equals(heap()->number_string())) {
+ __ JumpIfSmi(input, true_label);
+ __ ldr(input, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
+ __ cmp(input, Operand(ip));
+ final_branch_condition = eq;
+
+ } else if (type_name->Equals(heap()->string_string())) {
+ __ JumpIfSmi(input, false_label);
+ __ CompareObjectType(input, input, scratch, FIRST_NONSTRING_TYPE);
+ __ b(ge, false_label);
+ __ ldrb(ip, FieldMemOperand(input, Map::kBitFieldOffset));
+ __ tst(ip, Operand(1 << Map::kIsUndetectable));
+ final_branch_condition = eq;
+
+ } else if (type_name->Equals(heap()->symbol_string())) {
+ __ JumpIfSmi(input, false_label);
+ __ CompareObjectType(input, input, scratch, SYMBOL_TYPE);
+ final_branch_condition = eq;
+
+ } else if (type_name->Equals(heap()->boolean_string())) {
+ __ CompareRoot(input, Heap::kTrueValueRootIndex);
+ __ b(eq, true_label);
+ __ CompareRoot(input, Heap::kFalseValueRootIndex);
+ final_branch_condition = eq;
+
+ } else if (FLAG_harmony_typeof && type_name->Equals(heap()->null_string())) {
+ __ CompareRoot(input, Heap::kNullValueRootIndex);
+ final_branch_condition = eq;
+
+ } else if (type_name->Equals(heap()->undefined_string())) {
+ __ CompareRoot(input, Heap::kUndefinedValueRootIndex);
+ __ b(eq, true_label);
+ __ JumpIfSmi(input, false_label);
+ // Check for undetectable objects => true.
+ __ ldr(input, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ ldrb(ip, FieldMemOperand(input, Map::kBitFieldOffset));
+ __ tst(ip, Operand(1 << Map::kIsUndetectable));
+ final_branch_condition = ne;
+
+ } else if (type_name->Equals(heap()->function_string())) {
+ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
+ __ JumpIfSmi(input, false_label);
+ __ 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_string())) {
+ __ JumpIfSmi(input, false_label);
+ if (!FLAG_harmony_typeof) {
+ __ CompareRoot(input, Heap::kNullValueRootIndex);
+ __ b(eq, true_label);
+ }
+ __ CompareObjectType(input, input, scratch,
+ FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ b(lt, false_label);
+ __ CompareInstanceType(input, scratch, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ b(gt, false_label);
+ // Check for undetectable objects => false.
+ __ ldrb(ip, FieldMemOperand(input, Map::kBitFieldOffset));
+ __ tst(ip, Operand(1 << Map::kIsUndetectable));
+ final_branch_condition = eq;
+
+ } else {
+ __ b(false_label);
+ }
+
+ return final_branch_condition;
+}
+
+
+void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) {
+ Register temp1 = ToRegister(instr->temp());
+
+ EmitIsConstructCall(temp1, scratch0());
+ EmitBranch(instr, eq);
+}
+
+
+void LCodeGen::EmitIsConstructCall(Register temp1, Register temp2) {
+ ASSERT(!temp1.is(temp2));
+ // Get the frame pointer for the calling frame.
+ __ ldr(temp1, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+
+ // Skip the arguments adaptor frame if it exists.
+ Label check_frame_marker;
+ __ ldr(temp2, MemOperand(temp1, StandardFrameConstants::kContextOffset));
+ __ cmp(temp2, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ b(ne, &check_frame_marker);
+ __ ldr(temp1, MemOperand(temp1, StandardFrameConstants::kCallerFPOffset));
+
+ // Check the marker in the calling frame.
+ __ bind(&check_frame_marker);
+ __ ldr(temp1, MemOperand(temp1, StandardFrameConstants::kMarkerOffset));
+ __ cmp(temp1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
+}
+
+
+void LCodeGen::EnsureSpaceForLazyDeopt() {
+ if (info()->IsStub()) return;
+ // 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) {
+ // Block literal pool emission for duration of padding.
+ Assembler::BlockConstPoolScope block_const_pool(masm());
+ 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;
+ }
+ }
+}
+
+
+void LCodeGen::DoLazyBailout(LLazyBailout* instr) {
+ EnsureSpaceForLazyDeopt();
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ ASSERT(instr->HasEnvironment());
+ LEnvironment* env = instr->environment();
+ RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt);
+ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
+}
+
+
+void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
+ Deoptimizer::BailoutType type = instr->hydrogen()->type();
+ // TODO(danno): Stubs expect all deopts to be lazy for historical reasons (the
+ // needed return address), even though the implementation of LAZY and EAGER is
+ // now identical. When LAZY is eventually completely folded into EAGER, remove
+ // the special case below.
+ if (info()->IsStub() && type == Deoptimizer::EAGER) {
+ type = Deoptimizer::LAZY;
+ }
+
+ Comment(";;; deoptimize: %s", instr->hydrogen()->reason());
+ DeoptimizeIf(al, instr->environment(), type);
+}
+
+
+void LCodeGen::DoDummyUse(LDummyUse* instr) {
+ // Nothing to see here, move on!
+}
+
+
+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(ip, Heap::kStackLimitRootIndex);
+ __ cmp(sp, Operand(ip));
+ __ b(hs, &done);
+ StackCheckStub stub;
+ PredictableCodeSizeScope predictable(masm_, 2 * Assembler::kInstrSize);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ EnsureSpaceForLazyDeopt();
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ __ 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(zone()) DeferredStackCheck(this, instr);
+ __ LoadRoot(ip, Heap::kStackLimitRootIndex);
+ __ cmp(sp, Operand(ip));
+ __ b(lo, deferred_stack_check->entry());
+ EnsureSpaceForLazyDeopt();
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ __ 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();
+
+ // 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);
+
+ // Normally we record the first unknown OSR value as the entrypoint to the OSR
+ // code, but if there were none, record the entrypoint here.
+ if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset();
+}
+
+
+void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(r0, ip);
+ DeoptimizeIf(eq, instr->environment());
+
+ Register null_value = r5;
+ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
+ __ cmp(r0, null_value);
+ DeoptimizeIf(eq, instr->environment());
+
+ __ SmiTst(r0);
+ DeoptimizeIf(eq, instr->environment());
+
+ STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
+ __ CompareObjectType(r0, r1, r1, LAST_JS_PROXY_TYPE);
+ DeoptimizeIf(le, instr->environment());
+
+ Label use_cache, call_runtime;
+ __ CheckEnumCache(null_value, &call_runtime);
+
+ __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ b(&use_cache);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(r0);
+ CallRuntime(Runtime::kGetPropertyNamesFast, 1, instr);
+
+ __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kMetaMapRootIndex);
+ __ cmp(r1, ip);
+ DeoptimizeIf(ne, instr->environment());
+ __ bind(&use_cache);
+}
+
+
+void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
+ Register map = ToRegister(instr->map());
+ Register result = ToRegister(instr->result());
+ Label load_cache, done;
+ __ EnumLength(result, map);
+ __ cmp(result, Operand(Smi::FromInt(0)));
+ __ b(ne, &load_cache);
+ __ mov(result, Operand(isolate()->factory()->empty_fixed_array()));
+ __ jmp(&done);
+
+ __ bind(&load_cache);
+ __ LoadInstanceDescriptors(map, result);
+ __ ldr(result,
+ FieldMemOperand(result, DescriptorArray::kEnumCacheOffset));
+ __ ldr(result,
+ FieldMemOperand(result, FixedArray::SizeFor(instr->idx())));
+ __ cmp(result, Operand::Zero());
+ DeoptimizeIf(eq, instr->environment());
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoCheckMapValue(LCheckMapValue* instr) {
+ Register object = ToRegister(instr->value());
+ Register map = ToRegister(instr->map());
+ __ ldr(scratch0(), FieldMemOperand(object, HeapObject::kMapOffset));
+ __ cmp(map, scratch0());
+ DeoptimizeIf(ne, instr->environment());
+}
+
+
+void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
+ Register object = ToRegister(instr->object());
+ Register index = ToRegister(instr->index());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+
+ Label out_of_object, done;
+ __ cmp(index, Operand::Zero());
+ __ b(lt, &out_of_object);
+
+ __ add(scratch, object, Operand::PointerOffsetFromSmiKey(index));
+ __ ldr(result, FieldMemOperand(scratch, JSObject::kHeaderSize));
+
+ __ b(&done);
+
+ __ bind(&out_of_object);
+ __ ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
+ // Index is equal to negated out of object property index plus 1.
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ sub(scratch, result, Operand::PointerOffsetFromSmiKey(index));
+ __ ldr(result, FieldMemOperand(scratch,
+ FixedArray::kHeaderSize - kPointerSize));
+ __ bind(&done);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/arm/lithium-codegen-arm.h b/chromium/v8/src/arm/lithium-codegen-arm.h
new file mode 100644
index 00000000000..d0bfcbbb94e
--- /dev/null
+++ b/chromium/v8/src/arm/lithium-codegen-arm.h
@@ -0,0 +1,503 @@
+// Copyright 2012 the V8 project authors. 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_ARM_LITHIUM_CODEGEN_ARM_H_
+#define V8_ARM_LITHIUM_CODEGEN_ARM_H_
+
+#include "arm/lithium-arm.h"
+
+#include "arm/lithium-gap-resolver-arm.h"
+#include "deoptimizer.h"
+#include "safepoint-table.h"
+#include "scopes.h"
+#include "v8utils.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class LDeferredCode;
+class SafepointGenerator;
+
+class LCodeGen BASE_EMBEDDED {
+ public:
+ LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info)
+ : zone_(info->zone()),
+ chunk_(static_cast<LPlatformChunk*>(chunk)),
+ masm_(assembler),
+ info_(info),
+ current_block_(-1),
+ current_instruction_(-1),
+ instructions_(chunk->instructions()),
+ deoptimizations_(4, info->zone()),
+ deopt_jump_table_(4, info->zone()),
+ deoptimization_literals_(8, info->zone()),
+ inlined_function_count_(0),
+ scope_(info->scope()),
+ status_(UNUSED),
+ translations_(info->zone()),
+ deferred_(8, info->zone()),
+ osr_pc_offset_(-1),
+ last_lazy_deopt_pc_(0),
+ frame_is_built_(false),
+ safepoints_(info->zone()),
+ resolver_(this),
+ expected_safepoint_kind_(Safepoint::kSimple),
+ old_position_(RelocInfo::kNoPosition) {
+ 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(); }
+ Zone* zone() const { return zone_; }
+
+ int LookupDestination(int block_id) const {
+ return chunk()->LookupDestination(block_id);
+ }
+
+ bool IsNextEmittedBlock(int block_id) const {
+ return LookupDestination(block_id) == GetNextEmittedBlock();
+ }
+
+ bool NeedsEagerFrame() const {
+ return GetStackSlotCount() > 0 ||
+ info()->is_non_deferred_calling() ||
+ !info()->IsStub() ||
+ info()->requires_frame();
+ }
+ bool NeedsDeferredFrame() const {
+ return !NeedsEagerFrame() && info()->is_deferred_calling();
+ }
+
+ LinkRegisterStatus GetLinkRegisterState() const {
+ return frame_is_built_ ? kLRHasBeenSaved : kLRHasNotBeenSaved;
+ }
+
+ // 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.
+ DwVfpRegister ToDoubleRegister(LOperand* op) const;
+
+ // LOperand is loaded into dbl_scratch, unless already a double register.
+ DwVfpRegister EmitLoadDoubleRegister(LOperand* op,
+ SwVfpRegister flt_scratch,
+ DwVfpRegister dbl_scratch);
+ int32_t ToRepresentation(LConstantOperand* op, const Representation& r) const;
+ int32_t ToInteger32(LConstantOperand* op) const;
+ Smi* ToSmi(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;
+ bool IsSmi(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();
+
+ // Finish the code by setting stack height, safepoint, and bailout
+ // information on it.
+ void FinishCode(Handle<Code> code);
+
+ // Deferred code support.
+ void DoDeferredNumberTagD(LNumberTagD* instr);
+
+ enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 };
+ void DoDeferredNumberTagI(LInstruction* instr,
+ LOperand* value,
+ IntegerSignedness signedness);
+
+ void DoDeferredTaggedToI(LTaggedToI* instr);
+ void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
+ void DoDeferredStackCheck(LStackCheck* instr);
+ void DoDeferredRandom(LRandom* instr);
+ void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+ void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
+ void DoDeferredAllocate(LAllocate* instr);
+ void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check);
+ void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
+
+ // Parallel move support.
+ void DoParallelMove(LParallelMove* move);
+ void DoGap(LGap* instr);
+
+ MemOperand PrepareKeyedOperand(Register key,
+ Register base,
+ bool key_is_constant,
+ int constant_key,
+ int element_size,
+ int shift_size,
+ int additional_index,
+ int additional_offset);
+
+ // 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;
+ }
+
+ LPlatformChunk* chunk() const { return chunk_; }
+ Scope* scope() const { return scope_; }
+ HGraph* graph() const { return chunk()->graph(); }
+
+ Register scratch0() { return r9; }
+ LowDwVfpRegister double_scratch0() { return kScratchDoubleReg; }
+
+ int GetNextEmittedBlock() const;
+ 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(); }
+
+ void Abort(BailoutReason reason);
+ void FPRINTF_CHECKING Comment(const char* format, ...);
+
+ void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code, zone()); }
+
+ // 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,
+ TargetAddressStorageMode storage_mode = CAN_INLINE_TARGET_ADDRESS);
+
+ void CallCodeGeneric(
+ Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ SafepointMode safepoint_mode,
+ TargetAddressStorageMode storage_mode = CAN_INLINE_TARGET_ADDRESS);
+
+ 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);
+
+ enum R1State {
+ R1_UNINITIALIZED,
+ R1_CONTAINS_TARGET
+ };
+
+ // Generate a direct call to a known function. Expects the function
+ // to be in r1.
+ void CallKnownFunction(Handle<JSFunction> function,
+ int formal_parameter_count,
+ int arity,
+ LInstruction* instr,
+ CallKind call_kind,
+ R1State r1_state);
+
+ void LoadHeapObject(Register result, Handle<HeapObject> object);
+
+ void RecordSafepointWithLazyDeopt(LInstruction* instr,
+ SafepointMode safepoint_mode);
+
+ void RegisterEnvironmentForDeoptimization(LEnvironment* environment,
+ Safepoint::DeoptMode mode);
+ void DeoptimizeIf(Condition condition,
+ LEnvironment* environment,
+ Deoptimizer::BailoutType bailout_type);
+ void DeoptimizeIf(Condition condition, LEnvironment* environment);
+ void ApplyCheckIf(Condition condition, LBoundsCheck* check);
+
+ void AddToTranslation(LEnvironment* environment,
+ Translation* translation,
+ LOperand* op,
+ bool is_tagged,
+ bool is_uint32,
+ int* object_index_pointer,
+ int* dematerialized_index_pointer);
+ void RegisterDependentCodeForEmbeddedMaps(Handle<Code> code);
+ void PopulateDeoptimizationData(Handle<Code> code);
+ int DefineDeoptimizationLiteral(Handle<Object> literal);
+
+ void PopulateDeoptimizationLiteralsWithInlinedFunctions();
+
+ Register ToRegister(int index) const;
+ DwVfpRegister ToDoubleRegister(int index) const;
+
+ void EmitIntegerMathAbs(LMathAbs* 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);
+ void RecordAndUpdatePosition(int position);
+
+ static Condition TokenToCondition(Token::Value op, bool is_unsigned);
+ void EmitGoto(int block);
+ template<class InstrType>
+ void EmitBranch(InstrType instr, Condition condition);
+ template<class InstrType>
+ void EmitFalseBranch(InstrType instr, Condition condition);
+ void EmitNumberUntagD(Register input,
+ DwVfpRegister result,
+ bool allow_undefined_as_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode);
+
+ // 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);
+
+ // 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,
+ 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,
+ SmiCheck check_needed);
+
+ // Emits optimized code for %_IsConstructCall().
+ // Caller should branch on equal condition.
+ void EmitIsConstructCall(Register temp1, Register temp2);
+
+ // 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,
+ AllocationSiteMode mode);
+
+ // Emit optimized code for integer division.
+ // Inputs are signed.
+ // All registers are clobbered.
+ // If 'remainder' is no_reg, it is not computed.
+ void EmitSignedIntegerDivisionByConstant(Register result,
+ Register dividend,
+ int32_t divisor,
+ Register remainder,
+ Register scratch,
+ LEnvironment* environment);
+
+ void EnsureSpaceForLazyDeopt();
+ void DoLoadKeyedExternalArray(LLoadKeyed* instr);
+ void DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr);
+ void DoLoadKeyedFixedArray(LLoadKeyed* instr);
+ void DoStoreKeyedExternalArray(LStoreKeyed* instr);
+ void DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr);
+ void DoStoreKeyedFixedArray(LStoreKeyed* instr);
+
+ Zone* zone_;
+ LPlatformChunk* const chunk_;
+ MacroAssembler* const masm_;
+ CompilationInfo* const info_;
+
+ int current_block_;
+ int current_instruction_;
+ const ZoneList<LInstruction*>* instructions_;
+ ZoneList<LEnvironment*> deoptimizations_;
+ ZoneList<Deoptimizer::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_;
+ bool frame_is_built_;
+
+ // 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_;
+
+ int old_position_;
+
+ class PushSafepointRegistersScope BASE_EMBEDDED {
+ public:
+ PushSafepointRegistersScope(LCodeGen* codegen,
+ Safepoint::Kind kind)
+ : codegen_(codegen) {
+ ASSERT(codegen_->info()->is_calling());
+ 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
+
+#endif // V8_ARM_LITHIUM_CODEGEN_ARM_H_
diff --git a/chromium/v8/src/arm/lithium-gap-resolver-arm.cc b/chromium/v8/src/arm/lithium-gap-resolver-arm.cc
new file mode 100644
index 00000000000..88ac7a2a21d
--- /dev/null
+++ b/chromium/v8/src/arm/lithium-gap-resolver-arm.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 "arm/lithium-gap-resolver-arm.h"
+#include "arm/lithium-codegen-arm.h"
+
+namespace v8 {
+namespace internal {
+
+static const Register kSavedValueRegister = { 9 };
+
+LGapResolver::LGapResolver(LCodeGen* owner)
+ : cgen_(owner), moves_(32, owner->zone()), 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, cgen_->zone());
+ }
+ 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()) {
+ __ ldr(kSavedValueRegister, cgen_->ToMemOperand(source));
+ } else if (source->IsDoubleRegister()) {
+ __ vmov(kScratchDoubleReg, cgen_->ToDoubleRegister(source));
+ } else if (source->IsDoubleStackSlot()) {
+ __ vldr(kScratchDoubleReg, 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 kSavedDoubleValueRegister.
+ if (saved_destination_->IsRegister()) {
+ __ mov(cgen_->ToRegister(saved_destination_), kSavedValueRegister);
+ } else if (saved_destination_->IsStackSlot()) {
+ __ str(kSavedValueRegister, cgen_->ToMemOperand(saved_destination_));
+ } else if (saved_destination_->IsDoubleRegister()) {
+ __ vmov(cgen_->ToDoubleRegister(saved_destination_), kScratchDoubleReg);
+ } else if (saved_destination_->IsDoubleStackSlot()) {
+ __ vstr(kScratchDoubleReg, 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());
+ __ str(source_register, cgen_->ToMemOperand(destination));
+ }
+ } else if (source->IsStackSlot()) {
+ MemOperand source_operand = cgen_->ToMemOperand(source);
+ if (destination->IsRegister()) {
+ __ ldr(cgen_->ToRegister(destination), source_operand);
+ } else {
+ ASSERT(destination->IsStackSlot());
+ MemOperand destination_operand = cgen_->ToMemOperand(destination);
+ if (in_cycle_) {
+ if (!destination_operand.OffsetIsUint12Encodable()) {
+ // ip is overwritten while saving the value to the destination.
+ // Therefore we can't use ip. It is OK if the read from the source
+ // destroys ip, since that happens before the value is read.
+ __ vldr(kScratchDoubleReg.low(), source_operand);
+ __ vstr(kScratchDoubleReg.low(), destination_operand);
+ } else {
+ __ ldr(ip, source_operand);
+ __ str(ip, destination_operand);
+ }
+ } else {
+ __ ldr(kSavedValueRegister, source_operand);
+ __ str(kSavedValueRegister, destination_operand);
+ }
+ }
+
+ } else if (source->IsConstantOperand()) {
+ LConstantOperand* constant_source = LConstantOperand::cast(source);
+ if (destination->IsRegister()) {
+ Register dst = cgen_->ToRegister(destination);
+ Representation r = cgen_->IsSmi(constant_source)
+ ? Representation::Smi() : Representation::Integer32();
+ if (cgen_->IsInteger32(constant_source)) {
+ __ mov(dst, Operand(cgen_->ToRepresentation(constant_source, r)));
+ } else {
+ __ LoadObject(dst, cgen_->ToHandle(constant_source));
+ }
+ } else if (destination->IsDoubleRegister()) {
+ DwVfpRegister result = cgen_->ToDoubleRegister(destination);
+ double v = cgen_->ToDouble(constant_source);
+ __ Vmov(result, v, ip);
+ } else {
+ ASSERT(destination->IsStackSlot());
+ ASSERT(!in_cycle_); // Constant moves happen after all cycles are gone.
+ Representation r = cgen_->IsSmi(constant_source)
+ ? Representation::Smi() : Representation::Integer32();
+ if (cgen_->IsInteger32(constant_source)) {
+ __ mov(kSavedValueRegister,
+ Operand(cgen_->ToRepresentation(constant_source, r)));
+ } else {
+ __ LoadObject(kSavedValueRegister,
+ cgen_->ToHandle(constant_source));
+ }
+ __ str(kSavedValueRegister, cgen_->ToMemOperand(destination));
+ }
+
+ } else if (source->IsDoubleRegister()) {
+ DwVfpRegister source_register = cgen_->ToDoubleRegister(source);
+ if (destination->IsDoubleRegister()) {
+ __ vmov(cgen_->ToDoubleRegister(destination), source_register);
+ } else {
+ ASSERT(destination->IsDoubleStackSlot());
+ __ vstr(source_register, cgen_->ToMemOperand(destination));
+ }
+
+ } else if (source->IsDoubleStackSlot()) {
+ MemOperand source_operand = cgen_->ToMemOperand(source);
+ if (destination->IsDoubleRegister()) {
+ __ vldr(cgen_->ToDoubleRegister(destination), source_operand);
+ } else {
+ ASSERT(destination->IsDoubleStackSlot());
+ MemOperand destination_operand = cgen_->ToMemOperand(destination);
+ if (in_cycle_) {
+ // kSavedDoubleValueRegister was used to break the cycle,
+ // but kSavedValueRegister is free.
+ MemOperand source_high_operand =
+ cgen_->ToHighMemOperand(source);
+ MemOperand destination_high_operand =
+ cgen_->ToHighMemOperand(destination);
+ __ ldr(kSavedValueRegister, source_operand);
+ __ str(kSavedValueRegister, destination_operand);
+ __ ldr(kSavedValueRegister, source_high_operand);
+ __ str(kSavedValueRegister, destination_high_operand);
+ } else {
+ __ vldr(kScratchDoubleReg, source_operand);
+ __ vstr(kScratchDoubleReg, destination_operand);
+ }
+ }
+ } else {
+ UNREACHABLE();
+ }
+
+ moves_[index].Eliminate();
+}
+
+
+#undef __
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/arm/lithium-gap-resolver-arm.h b/chromium/v8/src/arm/lithium-gap-resolver-arm.h
new file mode 100644
index 00000000000..9dd09c8d03b
--- /dev/null
+++ b/chromium/v8/src/arm/lithium-gap-resolver-arm.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_ARM_LITHIUM_GAP_RESOLVER_ARM_H_
+#define V8_ARM_LITHIUM_GAP_RESOLVER_ARM_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_ARM_LITHIUM_GAP_RESOLVER_ARM_H_
diff --git a/chromium/v8/src/arm/macro-assembler-arm.cc b/chromium/v8/src/arm/macro-assembler-arm.cc
new file mode 100644
index 00000000000..a56744bf597
--- /dev/null
+++ b/chromium/v8/src/arm/macro-assembler-arm.cc
@@ -0,0 +1,3883 @@
+// Copyright 2012 the V8 project authors. 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 <limits.h> // For LONG_MIN, LONG_MAX.
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "cpu-profiler.h"
+#include "debug.h"
+#include "runtime.h"
+
+namespace v8 {
+namespace internal {
+
+MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
+ : Assembler(arg_isolate, buffer, size),
+ generating_stub_(false),
+ allow_stub_calls_(true),
+ has_frame_(false) {
+ if (isolate() != NULL) {
+ code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
+ isolate());
+ }
+}
+
+
+void MacroAssembler::Jump(Register target, Condition cond) {
+ bx(target, cond);
+}
+
+
+void MacroAssembler::Jump(intptr_t target, RelocInfo::Mode rmode,
+ Condition cond) {
+ mov(ip, Operand(target, rmode));
+ bx(ip, cond);
+}
+
+
+void MacroAssembler::Jump(Address target, RelocInfo::Mode rmode,
+ Condition cond) {
+ ASSERT(!RelocInfo::IsCodeTarget(rmode));
+ Jump(reinterpret_cast<intptr_t>(target), rmode, cond);
+}
+
+
+void MacroAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode,
+ Condition cond) {
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ // 'code' is always generated ARM code, never THUMB code
+ AllowDeferredHandleDereference embedding_raw_address;
+ Jump(reinterpret_cast<intptr_t>(code.location()), rmode, cond);
+}
+
+
+int MacroAssembler::CallSize(Register target, Condition cond) {
+ return kInstrSize;
+}
+
+
+void MacroAssembler::Call(Register target, Condition cond) {
+ // Block constant pool for the call instruction sequence.
+ BlockConstPoolScope block_const_pool(this);
+ Label start;
+ bind(&start);
+ blx(target, cond);
+ ASSERT_EQ(CallSize(target, cond), SizeOfCodeGeneratedSince(&start));
+}
+
+
+int MacroAssembler::CallSize(
+ Address target, RelocInfo::Mode rmode, Condition cond) {
+ int size = 2 * kInstrSize;
+ Instr mov_instr = cond | MOV | LeaveCC;
+ intptr_t immediate = reinterpret_cast<intptr_t>(target);
+ if (!Operand(immediate, rmode).is_single_instruction(this, mov_instr)) {
+ size += kInstrSize;
+ }
+ return size;
+}
+
+
+int MacroAssembler::CallSizeNotPredictableCodeSize(
+ Address target, RelocInfo::Mode rmode, Condition cond) {
+ int size = 2 * kInstrSize;
+ Instr mov_instr = cond | MOV | LeaveCC;
+ intptr_t immediate = reinterpret_cast<intptr_t>(target);
+ if (!Operand(immediate, rmode).is_single_instruction(NULL, mov_instr)) {
+ size += kInstrSize;
+ }
+ return size;
+}
+
+
+void MacroAssembler::Call(Address target,
+ RelocInfo::Mode rmode,
+ Condition cond,
+ TargetAddressStorageMode mode) {
+ // Block constant pool for the call instruction sequence.
+ BlockConstPoolScope block_const_pool(this);
+ Label start;
+ bind(&start);
+
+ bool old_predictable_code_size = predictable_code_size();
+ if (mode == NEVER_INLINE_TARGET_ADDRESS) {
+ set_predictable_code_size(true);
+ }
+
+ // Call sequence on V7 or later may be :
+ // movw ip, #... @ call address low 16
+ // movt ip, #... @ call address high 16
+ // blx ip
+ // @ return address
+ // Or for pre-V7 or values that may be back-patched
+ // to avoid ICache flushes:
+ // ldr ip, [pc, #...] @ call address
+ // blx ip
+ // @ return address
+
+ // Statement positions are expected to be recorded when the target
+ // address is loaded. The mov method will automatically record
+ // positions when pc is the target, since this is not the case here
+ // we have to do it explicitly.
+ positions_recorder()->WriteRecordedPositions();
+
+ mov(ip, Operand(reinterpret_cast<int32_t>(target), rmode));
+ blx(ip, cond);
+
+ ASSERT_EQ(CallSize(target, rmode, cond), SizeOfCodeGeneratedSince(&start));
+ if (mode == NEVER_INLINE_TARGET_ADDRESS) {
+ set_predictable_code_size(old_predictable_code_size);
+ }
+}
+
+
+int MacroAssembler::CallSize(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id,
+ Condition cond) {
+ AllowDeferredHandleDereference using_raw_address;
+ return CallSize(reinterpret_cast<Address>(code.location()), rmode, cond);
+}
+
+
+void MacroAssembler::Call(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id,
+ Condition cond,
+ TargetAddressStorageMode mode) {
+ Label start;
+ bind(&start);
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) {
+ SetRecordedAstId(ast_id);
+ rmode = RelocInfo::CODE_TARGET_WITH_ID;
+ }
+ // 'code' is always generated ARM code, never THUMB code
+ AllowDeferredHandleDereference embedding_raw_address;
+ Call(reinterpret_cast<Address>(code.location()), rmode, cond, mode);
+}
+
+
+void MacroAssembler::Ret(Condition cond) {
+ bx(lr, cond);
+}
+
+
+void MacroAssembler::Drop(int count, Condition cond) {
+ if (count > 0) {
+ add(sp, sp, Operand(count * kPointerSize), LeaveCC, cond);
+ }
+}
+
+
+void MacroAssembler::Ret(int drop, Condition cond) {
+ Drop(drop, cond);
+ Ret(cond);
+}
+
+
+void MacroAssembler::Swap(Register reg1,
+ Register reg2,
+ Register scratch,
+ Condition cond) {
+ if (scratch.is(no_reg)) {
+ eor(reg1, reg1, Operand(reg2), LeaveCC, cond);
+ eor(reg2, reg2, Operand(reg1), LeaveCC, cond);
+ eor(reg1, reg1, Operand(reg2), LeaveCC, cond);
+ } else {
+ mov(scratch, reg1, LeaveCC, cond);
+ mov(reg1, reg2, LeaveCC, cond);
+ mov(reg2, scratch, LeaveCC, cond);
+ }
+}
+
+
+void MacroAssembler::Call(Label* target) {
+ bl(target);
+}
+
+
+void MacroAssembler::Push(Handle<Object> handle) {
+ mov(ip, Operand(handle));
+ push(ip);
+}
+
+
+void MacroAssembler::Move(Register dst, Handle<Object> value) {
+ mov(dst, Operand(value));
+}
+
+
+void MacroAssembler::Move(Register dst, Register src, Condition cond) {
+ if (!dst.is(src)) {
+ mov(dst, src, LeaveCC, cond);
+ }
+}
+
+
+void MacroAssembler::Move(DwVfpRegister dst, DwVfpRegister src) {
+ if (!dst.is(src)) {
+ vmov(dst, src);
+ }
+}
+
+
+void MacroAssembler::And(Register dst, Register src1, const Operand& src2,
+ Condition cond) {
+ if (!src2.is_reg() &&
+ !src2.must_output_reloc_info(this) &&
+ src2.immediate() == 0) {
+ mov(dst, Operand::Zero(), LeaveCC, cond);
+ } else if (!src2.is_single_instruction(this) &&
+ !src2.must_output_reloc_info(this) &&
+ CpuFeatures::IsSupported(ARMv7) &&
+ IsPowerOf2(src2.immediate() + 1)) {
+ ubfx(dst, src1, 0,
+ WhichPowerOf2(static_cast<uint32_t>(src2.immediate()) + 1), cond);
+ } else {
+ and_(dst, src1, src2, LeaveCC, cond);
+ }
+}
+
+
+void MacroAssembler::Ubfx(Register dst, Register src1, int lsb, int width,
+ Condition cond) {
+ ASSERT(lsb < 32);
+ if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
+ int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
+ and_(dst, src1, Operand(mask), LeaveCC, cond);
+ if (lsb != 0) {
+ mov(dst, Operand(dst, LSR, lsb), LeaveCC, cond);
+ }
+ } else {
+ ubfx(dst, src1, lsb, width, cond);
+ }
+}
+
+
+void MacroAssembler::Sbfx(Register dst, Register src1, int lsb, int width,
+ Condition cond) {
+ ASSERT(lsb < 32);
+ if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
+ int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
+ and_(dst, src1, Operand(mask), LeaveCC, cond);
+ int shift_up = 32 - lsb - width;
+ int shift_down = lsb + shift_up;
+ if (shift_up != 0) {
+ mov(dst, Operand(dst, LSL, shift_up), LeaveCC, cond);
+ }
+ if (shift_down != 0) {
+ mov(dst, Operand(dst, ASR, shift_down), LeaveCC, cond);
+ }
+ } else {
+ sbfx(dst, src1, lsb, width, cond);
+ }
+}
+
+
+void MacroAssembler::Bfi(Register dst,
+ Register src,
+ Register scratch,
+ int lsb,
+ int width,
+ Condition cond) {
+ ASSERT(0 <= lsb && lsb < 32);
+ ASSERT(0 <= width && width < 32);
+ ASSERT(lsb + width < 32);
+ ASSERT(!scratch.is(dst));
+ if (width == 0) return;
+ if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
+ int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
+ bic(dst, dst, Operand(mask));
+ and_(scratch, src, Operand((1 << width) - 1));
+ mov(scratch, Operand(scratch, LSL, lsb));
+ orr(dst, dst, scratch);
+ } else {
+ bfi(dst, src, lsb, width, cond);
+ }
+}
+
+
+void MacroAssembler::Bfc(Register dst, Register src, int lsb, int width,
+ Condition cond) {
+ ASSERT(lsb < 32);
+ if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
+ int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
+ bic(dst, src, Operand(mask));
+ } else {
+ Move(dst, src, cond);
+ bfc(dst, lsb, width, cond);
+ }
+}
+
+
+void MacroAssembler::Usat(Register dst, int satpos, const Operand& src,
+ Condition cond) {
+ if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
+ ASSERT(!dst.is(pc) && !src.rm().is(pc));
+ ASSERT((satpos >= 0) && (satpos <= 31));
+
+ // These asserts are required to ensure compatibility with the ARMv7
+ // implementation.
+ ASSERT((src.shift_op() == ASR) || (src.shift_op() == LSL));
+ ASSERT(src.rs().is(no_reg));
+
+ Label done;
+ int satval = (1 << satpos) - 1;
+
+ if (cond != al) {
+ b(NegateCondition(cond), &done); // Skip saturate if !condition.
+ }
+ if (!(src.is_reg() && dst.is(src.rm()))) {
+ mov(dst, src);
+ }
+ tst(dst, Operand(~satval));
+ b(eq, &done);
+ mov(dst, Operand::Zero(), LeaveCC, mi); // 0 if negative.
+ mov(dst, Operand(satval), LeaveCC, pl); // satval if positive.
+ bind(&done);
+ } else {
+ usat(dst, satpos, src, cond);
+ }
+}
+
+
+void MacroAssembler::LoadRoot(Register destination,
+ Heap::RootListIndex index,
+ Condition cond) {
+ if (CpuFeatures::IsSupported(MOVW_MOVT_IMMEDIATE_LOADS) &&
+ isolate()->heap()->RootCanBeTreatedAsConstant(index) &&
+ !predictable_code_size()) {
+ // The CPU supports fast immediate values, and this root will never
+ // change. We will load it as a relocatable immediate value.
+ Handle<Object> root(&isolate()->heap()->roots_array_start()[index]);
+ mov(destination, Operand(root), LeaveCC, cond);
+ return;
+ }
+ ldr(destination, MemOperand(kRootRegister, index << kPointerSizeLog2), cond);
+}
+
+
+void MacroAssembler::StoreRoot(Register source,
+ Heap::RootListIndex index,
+ Condition cond) {
+ str(source, MemOperand(kRootRegister, index << kPointerSizeLog2), cond);
+}
+
+
+void MacroAssembler::LoadHeapObject(Register result,
+ Handle<HeapObject> object) {
+ AllowDeferredHandleDereference using_raw_address;
+ if (isolate()->heap()->InNewSpace(*object)) {
+ Handle<Cell> cell = isolate()->factory()->NewCell(object);
+ mov(result, Operand(cell));
+ ldr(result, FieldMemOperand(result, Cell::kValueOffset));
+ } else {
+ mov(result, Operand(object));
+ }
+}
+
+
+void MacroAssembler::InNewSpace(Register object,
+ Register scratch,
+ Condition cond,
+ Label* branch) {
+ ASSERT(cond == eq || cond == ne);
+ and_(scratch, object, Operand(ExternalReference::new_space_mask(isolate())));
+ cmp(scratch, Operand(ExternalReference::new_space_start(isolate())));
+ b(cond, branch);
+}
+
+
+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;
+
+ // 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));
+
+ 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 clobbered input registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (emit_debug_code()) {
+ mov(value, Operand(BitCast<int32_t>(kZapValue + 4)));
+ mov(dst, Operand(BitCast<int32_t>(kZapValue + 8)));
+ }
+}
+
+
+// Will clobber 4 registers: object, address, scratch, ip. The
+// register 'object' contains a heap object pointer. The heap object
+// tag is shifted away.
+void MacroAssembler::RecordWrite(Register object,
+ Register address,
+ 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(!address.is(cp) && !value.is(cp));
+
+ if (emit_debug_code()) {
+ ldr(ip, MemOperand(address));
+ cmp(ip, value);
+ Check(eq, kWrongAddressOrValuePassedToRecordWrite);
+ }
+
+ Label done;
+
+ if (smi_check == INLINE_SMI_CHECK) {
+ 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.
+ 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 clobbered registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (emit_debug_code()) {
+ 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();
+ }
+}
+
+
+// Push and pop all registers that can hold pointers.
+void MacroAssembler::PushSafepointRegisters() {
+ // Safepoints expect a block of contiguous register values starting with r0:
+ ASSERT(((1 << kNumSafepointSavedRegisters) - 1) == kSafepointSavedRegisters);
+ // Safepoints expect a block of kNumSafepointRegisters values on the
+ // stack, so adjust the stack for unsaved registers.
+ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
+ ASSERT(num_unsaved >= 0);
+ sub(sp, sp, Operand(num_unsaved * kPointerSize));
+ stm(db_w, sp, kSafepointSavedRegisters);
+}
+
+
+void MacroAssembler::PopSafepointRegisters() {
+ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
+ ldm(ia_w, sp, kSafepointSavedRegisters);
+ add(sp, sp, Operand(num_unsaved * kPointerSize));
+}
+
+
+void MacroAssembler::PushSafepointRegistersAndDoubles() {
+ // Number of d-regs not known at snapshot time.
+ ASSERT(!Serializer::enabled());
+ PushSafepointRegisters();
+ sub(sp, sp, Operand(DwVfpRegister::NumAllocatableRegisters() *
+ kDoubleSize));
+ for (int i = 0; i < DwVfpRegister::NumAllocatableRegisters(); i++) {
+ vstr(DwVfpRegister::FromAllocationIndex(i), sp, i * kDoubleSize);
+ }
+}
+
+
+void MacroAssembler::PopSafepointRegistersAndDoubles() {
+ // Number of d-regs not known at snapshot time.
+ ASSERT(!Serializer::enabled());
+ for (int i = 0; i < DwVfpRegister::NumAllocatableRegisters(); i++) {
+ vldr(DwVfpRegister::FromAllocationIndex(i), sp, i * kDoubleSize);
+ }
+ add(sp, sp, Operand(DwVfpRegister::NumAllocatableRegisters() *
+ kDoubleSize));
+ PopSafepointRegisters();
+}
+
+void MacroAssembler::StoreToSafepointRegistersAndDoublesSlot(Register src,
+ Register dst) {
+ str(src, SafepointRegistersAndDoublesSlot(dst));
+}
+
+
+void MacroAssembler::StoreToSafepointRegisterSlot(Register src, Register dst) {
+ str(src, SafepointRegisterSlot(dst));
+}
+
+
+void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
+ ldr(dst, SafepointRegisterSlot(src));
+}
+
+
+int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
+ // The registers are pushed starting with the highest encoding,
+ // which means that lowest encodings are closest to the stack pointer.
+ ASSERT(reg_code >= 0 && reg_code < kNumSafepointRegisters);
+ return reg_code;
+}
+
+
+MemOperand MacroAssembler::SafepointRegisterSlot(Register reg) {
+ return MemOperand(sp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
+}
+
+
+MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) {
+ // Number of d-regs not known at snapshot time.
+ ASSERT(!Serializer::enabled());
+ // General purpose registers are pushed last on the stack.
+ int doubles_size = DwVfpRegister::NumAllocatableRegisters() * kDoubleSize;
+ int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize;
+ return MemOperand(sp, doubles_size + register_offset);
+}
+
+
+void MacroAssembler::Ldrd(Register dst1, Register dst2,
+ const MemOperand& src, Condition cond) {
+ ASSERT(src.rm().is(no_reg));
+ ASSERT(!dst1.is(lr)); // r14.
+
+ // V8 does not use this addressing mode, so the fallback code
+ // below doesn't support it yet.
+ ASSERT((src.am() != PreIndex) && (src.am() != NegPreIndex));
+
+ // Generate two ldr instructions if ldrd is not available.
+ if (CpuFeatures::IsSupported(ARMv7) && !predictable_code_size() &&
+ (dst1.code() % 2 == 0) && (dst1.code() + 1 == dst2.code())) {
+ CpuFeatureScope scope(this, ARMv7);
+ ldrd(dst1, dst2, src, cond);
+ } else {
+ if ((src.am() == Offset) || (src.am() == NegOffset)) {
+ MemOperand src2(src);
+ src2.set_offset(src2.offset() + 4);
+ if (dst1.is(src.rn())) {
+ ldr(dst2, src2, cond);
+ ldr(dst1, src, cond);
+ } else {
+ ldr(dst1, src, cond);
+ ldr(dst2, src2, cond);
+ }
+ } else { // PostIndex or NegPostIndex.
+ ASSERT((src.am() == PostIndex) || (src.am() == NegPostIndex));
+ if (dst1.is(src.rn())) {
+ ldr(dst2, MemOperand(src.rn(), 4, Offset), cond);
+ ldr(dst1, src, cond);
+ } else {
+ MemOperand src2(src);
+ src2.set_offset(src2.offset() - 4);
+ ldr(dst1, MemOperand(src.rn(), 4, PostIndex), cond);
+ ldr(dst2, src2, cond);
+ }
+ }
+ }
+}
+
+
+void MacroAssembler::Strd(Register src1, Register src2,
+ const MemOperand& dst, Condition cond) {
+ ASSERT(dst.rm().is(no_reg));
+ ASSERT(!src1.is(lr)); // r14.
+
+ // V8 does not use this addressing mode, so the fallback code
+ // below doesn't support it yet.
+ ASSERT((dst.am() != PreIndex) && (dst.am() != NegPreIndex));
+
+ // Generate two str instructions if strd is not available.
+ if (CpuFeatures::IsSupported(ARMv7) && !predictable_code_size() &&
+ (src1.code() % 2 == 0) && (src1.code() + 1 == src2.code())) {
+ CpuFeatureScope scope(this, ARMv7);
+ strd(src1, src2, dst, cond);
+ } else {
+ MemOperand dst2(dst);
+ if ((dst.am() == Offset) || (dst.am() == NegOffset)) {
+ dst2.set_offset(dst2.offset() + 4);
+ str(src1, dst, cond);
+ str(src2, dst2, cond);
+ } else { // PostIndex or NegPostIndex.
+ ASSERT((dst.am() == PostIndex) || (dst.am() == NegPostIndex));
+ dst2.set_offset(dst2.offset() - 4);
+ str(src1, MemOperand(dst.rn(), 4, PostIndex), cond);
+ str(src2, dst2, cond);
+ }
+ }
+}
+
+
+void MacroAssembler::VFPEnsureFPSCRState(Register scratch) {
+ // If needed, restore wanted bits of FPSCR.
+ Label fpscr_done;
+ vmrs(scratch);
+ tst(scratch, Operand(kVFPDefaultNaNModeControlBit));
+ b(ne, &fpscr_done);
+ orr(scratch, scratch, Operand(kVFPDefaultNaNModeControlBit));
+ vmsr(scratch);
+ bind(&fpscr_done);
+}
+
+void MacroAssembler::VFPCanonicalizeNaN(const DwVfpRegister value,
+ const Condition cond) {
+ vsub(value, value, kDoubleRegZero, cond);
+}
+
+
+void MacroAssembler::VFPCompareAndSetFlags(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond) {
+ // Compare and move FPSCR flags to the normal condition flags.
+ VFPCompareAndLoadFlags(src1, src2, pc, cond);
+}
+
+void MacroAssembler::VFPCompareAndSetFlags(const DwVfpRegister src1,
+ const double src2,
+ const Condition cond) {
+ // Compare and move FPSCR flags to the normal condition flags.
+ VFPCompareAndLoadFlags(src1, src2, pc, cond);
+}
+
+
+void MacroAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Register fpscr_flags,
+ const Condition cond) {
+ // Compare and load FPSCR.
+ vcmp(src1, src2, cond);
+ vmrs(fpscr_flags, cond);
+}
+
+void MacroAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1,
+ const double src2,
+ const Register fpscr_flags,
+ const Condition cond) {
+ // Compare and load FPSCR.
+ vcmp(src1, src2, cond);
+ vmrs(fpscr_flags, cond);
+}
+
+void MacroAssembler::Vmov(const DwVfpRegister dst,
+ const double imm,
+ const Register scratch) {
+ static const DoubleRepresentation minus_zero(-0.0);
+ static const DoubleRepresentation zero(0.0);
+ DoubleRepresentation value(imm);
+ // Handle special values first.
+ if (value.bits == zero.bits) {
+ vmov(dst, kDoubleRegZero);
+ } else if (value.bits == minus_zero.bits) {
+ vneg(dst, kDoubleRegZero);
+ } else {
+ vmov(dst, imm, scratch);
+ }
+}
+
+
+void MacroAssembler::VmovHigh(Register dst, DwVfpRegister src) {
+ if (src.code() < 16) {
+ const LowDwVfpRegister loc = LowDwVfpRegister::from_code(src.code());
+ vmov(dst, loc.high());
+ } else {
+ vmov(dst, VmovIndexHi, src);
+ }
+}
+
+
+void MacroAssembler::VmovHigh(DwVfpRegister dst, Register src) {
+ if (dst.code() < 16) {
+ const LowDwVfpRegister loc = LowDwVfpRegister::from_code(dst.code());
+ vmov(loc.high(), src);
+ } else {
+ vmov(dst, VmovIndexHi, src);
+ }
+}
+
+
+void MacroAssembler::VmovLow(Register dst, DwVfpRegister src) {
+ if (src.code() < 16) {
+ const LowDwVfpRegister loc = LowDwVfpRegister::from_code(src.code());
+ vmov(dst, loc.low());
+ } else {
+ vmov(dst, VmovIndexLo, src);
+ }
+}
+
+
+void MacroAssembler::VmovLow(DwVfpRegister dst, Register src) {
+ if (dst.code() < 16) {
+ const LowDwVfpRegister loc = LowDwVfpRegister::from_code(dst.code());
+ vmov(loc.low(), src);
+ } else {
+ vmov(dst, VmovIndexLo, src);
+ }
+}
+
+
+void MacroAssembler::ConvertNumberToInt32(Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch1,
+ LowDwVfpRegister double_scratch2,
+ Label* not_number) {
+ Label done;
+ UntagAndJumpIfSmi(dst, object, &done);
+ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_number);
+ vldr(double_scratch1, FieldMemOperand(object, HeapNumber::kValueOffset));
+ ECMAToInt32(dst, double_scratch1,
+ scratch1, scratch2, scratch3, double_scratch2);
+
+ bind(&done);
+}
+
+
+void MacroAssembler::LoadNumber(Register object,
+ LowDwVfpRegister dst,
+ Register heap_number_map,
+ Register scratch,
+ Label* not_number) {
+ Label is_smi, done;
+
+ UntagAndJumpIfSmi(scratch, object, &is_smi);
+ JumpIfNotHeapNumber(object, heap_number_map, scratch, not_number);
+
+ vldr(dst, FieldMemOperand(object, HeapNumber::kValueOffset));
+ b(&done);
+
+ // Handle loading a double from a smi.
+ bind(&is_smi);
+ vmov(dst.high(), scratch);
+ vcvt_f64_s32(dst, dst.high());
+
+ bind(&done);
+}
+
+
+void MacroAssembler::LoadNumberAsInt32Double(Register object,
+ DwVfpRegister double_dst,
+ Register heap_number_map,
+ Register scratch,
+ LowDwVfpRegister double_scratch,
+ Label* not_int32) {
+ ASSERT(!scratch.is(object));
+ ASSERT(!heap_number_map.is(object) && !heap_number_map.is(scratch));
+
+ Label done, obj_is_not_smi;
+
+ UntagAndJumpIfNotSmi(scratch, object, &obj_is_not_smi);
+ vmov(double_scratch.low(), scratch);
+ vcvt_f64_s32(double_dst, double_scratch.low());
+ b(&done);
+
+ bind(&obj_is_not_smi);
+ JumpIfNotHeapNumber(object, heap_number_map, scratch, not_int32);
+
+ // Load the number.
+ // Load the double value.
+ vldr(double_dst, FieldMemOperand(object, HeapNumber::kValueOffset));
+
+ TestDoubleIsInt32(double_dst, double_scratch);
+ // Jump to not_int32 if the operation did not succeed.
+ b(ne, not_int32);
+
+ bind(&done);
+}
+
+
+void MacroAssembler::LoadNumberAsInt32(Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch,
+ DwVfpRegister double_scratch0,
+ LowDwVfpRegister double_scratch1,
+ Label* not_int32) {
+ ASSERT(!dst.is(object));
+ ASSERT(!scratch.is(object));
+
+ Label done, maybe_undefined;
+
+ UntagAndJumpIfSmi(dst, object, &done);
+
+ JumpIfNotHeapNumber(object, heap_number_map, scratch, &maybe_undefined);
+
+ // Object is a heap number.
+ // Convert the floating point value to a 32-bit integer.
+ // Load the double value.
+ vldr(double_scratch0, FieldMemOperand(object, HeapNumber::kValueOffset));
+
+ TryDoubleToInt32Exact(dst, double_scratch0, double_scratch1);
+ // Jump to not_int32 if the operation did not succeed.
+ b(ne, not_int32);
+ b(&done);
+
+ bind(&maybe_undefined);
+ CompareRoot(object, Heap::kUndefinedValueRootIndex);
+ b(ne, not_int32);
+ // |undefined| is truncated to 0.
+ mov(dst, Operand(Smi::FromInt(0)));
+ // Fall through.
+
+ bind(&done);
+}
+
+
+void MacroAssembler::EnterFrame(StackFrame::Type type) {
+ // r0-r3: preserved
+ stm(db_w, sp, cp.bit() | fp.bit() | lr.bit());
+ mov(ip, Operand(Smi::FromInt(type)));
+ push(ip);
+ mov(ip, Operand(CodeObject()));
+ push(ip);
+ add(fp, sp, Operand(3 * kPointerSize)); // Adjust FP to point to saved FP.
+}
+
+
+void MacroAssembler::LeaveFrame(StackFrame::Type type) {
+ // r0: preserved
+ // r1: preserved
+ // r2: preserved
+
+ // Drop the execution stack down to the frame pointer and restore
+ // the caller frame pointer and return address.
+ mov(sp, fp);
+ ldm(ia_w, sp, fp.bit() | lr.bit());
+}
+
+
+void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space) {
+ // 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)); // Set up new frame pointer.
+ // Reserve room for saved entry sp and code object.
+ sub(sp, sp, Operand(2 * kPointerSize));
+ if (emit_debug_code()) {
+ mov(ip, Operand::Zero());
+ str(ip, MemOperand(fp, ExitFrameConstants::kSPOffset));
+ }
+ mov(ip, Operand(CodeObject()));
+ str(ip, MemOperand(fp, ExitFrameConstants::kCodeOffset));
+
+ // Save the frame pointer and the context in top.
+ mov(ip, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate())));
+ str(fp, MemOperand(ip));
+ mov(ip, Operand(ExternalReference(Isolate::kContextAddress, isolate())));
+ str(cp, MemOperand(ip));
+
+ // Optionally save all double registers.
+ if (save_doubles) {
+ SaveFPRegs(sp, ip);
+ // Note that d0 will be accessible at
+ // fp - 2 * kPointerSize - DwVfpRegister::kMaxNumRegisters * kDoubleSize,
+ // since the sp slot and code slot were pushed after the fp.
+ }
+
+ // Reserve place for the return address and stack space and align the frame
+ // preparing for calling the runtime function.
+ const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
+ sub(sp, sp, Operand((stack_space + 1) * kPointerSize));
+ if (frame_alignment > 0) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ and_(sp, sp, Operand(-frame_alignment));
+ }
+
+ // Set the exit frame sp value to point just before the return address
+ // location.
+ add(ip, sp, Operand(kPointerSize));
+ str(ip, MemOperand(fp, ExitFrameConstants::kSPOffset));
+}
+
+
+void MacroAssembler::InitializeNewString(Register string,
+ Register length,
+ Heap::RootListIndex map_index,
+ Register scratch1,
+ Register scratch2) {
+ SmiTag(scratch1, length);
+ LoadRoot(scratch2, map_index);
+ str(scratch1, FieldMemOperand(string, String::kLengthOffset));
+ mov(scratch1, Operand(String::kEmptyHashField));
+ str(scratch2, FieldMemOperand(string, HeapObject::kMapOffset));
+ str(scratch1, FieldMemOperand(string, String::kHashFieldOffset));
+}
+
+
+int MacroAssembler::ActivationFrameAlignment() {
+#if V8_HOST_ARCH_ARM
+ // Running on the real platform. Use the alignment as mandated by the local
+ // environment.
+ // Note: This will break if we ever start generating snapshots on one ARM
+ // platform for another ARM platform with a different alignment.
+ return OS::ActivationFrameAlignment();
+#else // V8_HOST_ARCH_ARM
+ // If we are using the simulator then we should always align to the expected
+ // alignment. As the simulator is used to generate snapshots we do not know
+ // if the target platform will need alignment, so this is controlled from a
+ // flag.
+ return FLAG_sim_stack_alignment;
+#endif // V8_HOST_ARCH_ARM
+}
+
+
+void MacroAssembler::LeaveExitFrame(bool save_doubles,
+ Register argument_count) {
+ // Optionally restore all double registers.
+ if (save_doubles) {
+ // Calculate the stack location of the saved doubles and restore them.
+ const int offset = 2 * kPointerSize;
+ sub(r3, fp,
+ Operand(offset + DwVfpRegister::kMaxNumRegisters * kDoubleSize));
+ RestoreFPRegs(r3, ip);
+ }
+
+ // Clear top frame.
+ mov(r3, Operand::Zero());
+ mov(ip, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate())));
+ str(r3, MemOperand(ip));
+
+ // Restore current context from top and clear it in debug mode.
+ mov(ip, Operand(ExternalReference(Isolate::kContextAddress, isolate())));
+ ldr(cp, MemOperand(ip));
+#ifdef DEBUG
+ str(r3, MemOperand(ip));
+#endif
+
+ // Tear down the exit frame, pop the arguments, and return.
+ mov(sp, Operand(fp));
+ ldm(ia_w, sp, fp.bit() | lr.bit());
+ if (argument_count.is_valid()) {
+ add(sp, sp, Operand(argument_count, LSL, kPointerSizeLog2));
+ }
+}
+
+
+void MacroAssembler::GetCFunctionDoubleResult(const DwVfpRegister dst) {
+ if (use_eabi_hardfloat()) {
+ Move(dst, d0);
+ } else {
+ vmov(dst, r0, r1);
+ }
+}
+
+
+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 r5 to
+ // follow the calling convention which requires the call type to be
+ // in r5.
+ ASSERT(dst.is(r5));
+ if (call_kind == CALL_AS_FUNCTION) {
+ mov(dst, Operand(Smi::FromInt(1)));
+ } else {
+ mov(dst, Operand(Smi::FromInt(0)));
+ }
+}
+
+
+void MacroAssembler::InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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,
+ // setup registers according to contract with ArgumentsAdaptorTrampoline:
+ // r0: actual arguments count
+ // r1: function (passed through to callee)
+ // r2: expected arguments count
+ // r3: callee code entry
+
+ // The code below is made a lot easier because the calling code already sets
+ // up actual and expected registers according to the contract if values are
+ // passed in registers.
+ ASSERT(actual.is_immediate() || actual.reg().is(r0));
+ ASSERT(expected.is_immediate() || expected.reg().is(r2));
+ ASSERT((!code_constant.is_null() && code_reg.is(no_reg)) || code_reg.is(r3));
+
+ if (expected.is_immediate()) {
+ ASSERT(actual.is_immediate());
+ if (expected.immediate() == actual.immediate()) {
+ definitely_matches = true;
+ } else {
+ mov(r0, Operand(actual.immediate()));
+ const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+ if (expected.immediate() == sentinel) {
+ // Don't worry about adapting arguments for builtins that
+ // don't want that done. Skip adaption code by making it look
+ // like we have a match between expected and actual number of
+ // arguments.
+ definitely_matches = true;
+ } else {
+ *definitely_mismatches = true;
+ mov(r2, Operand(expected.immediate()));
+ }
+ }
+ } else {
+ if (actual.is_immediate()) {
+ cmp(expected.reg(), Operand(actual.immediate()));
+ b(eq, &regular_invoke);
+ mov(r0, Operand(actual.immediate()));
+ } else {
+ cmp(expected.reg(), Operand(actual.reg()));
+ b(eq, &regular_invoke);
+ }
+ }
+
+ if (!definitely_matches) {
+ if (!code_constant.is_null()) {
+ mov(r3, Operand(code_constant));
+ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
+ }
+
+ Handle<Code> adaptor =
+ isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(adaptor));
+ SetCallKind(r5, call_kind);
+ Call(adaptor);
+ call_wrapper.AfterCall();
+ if (!*definitely_mismatches) {
+ b(done);
+ }
+ } else {
+ SetCallKind(r5, call_kind);
+ Jump(adaptor, RelocInfo::CODE_TARGET);
+ }
+ bind(&regular_invoke);
+ }
+}
+
+
+void MacroAssembler::InvokeCode(Register code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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,
+ call_wrapper, call_kind);
+ 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);
+ }
+}
+
+
+void MacroAssembler::InvokeCode(Handle<Code> code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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;
+ bool definitely_mismatches = false;
+ InvokePrologue(expected, actual, code, no_reg,
+ &done, &definitely_mismatches, flag,
+ NullCallWrapper(), call_kind);
+ 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);
+ }
+}
+
+
+void MacroAssembler::InvokeFunction(Register fun,
+ const ParameterCount& actual,
+ 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));
+
+ Register expected_reg = r2;
+ Register code_reg = r3;
+
+ ldr(code_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+ ldr(expected_reg,
+ FieldMemOperand(code_reg,
+ SharedFunctionInfo::kFormalParameterCountOffset));
+ SmiUntag(expected_reg);
+ ldr(code_reg,
+ FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
+
+ ParameterCount expected(expected_reg);
+ InvokeCode(code_reg, expected, actual, flag, call_wrapper, call_kind);
+}
+
+
+void MacroAssembler::InvokeFunction(Handle<JSFunction> function,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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());
+
+ // Get the function and setup the context.
+ LoadHeapObject(r1, function);
+ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+
+ // 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);
+}
+
+
+void MacroAssembler::IsObjectJSObjectType(Register heap_object,
+ Register map,
+ Register scratch,
+ Label* fail) {
+ ldr(map, FieldMemOperand(heap_object, HeapObject::kMapOffset));
+ IsInstanceJSObjectType(map, scratch, fail);
+}
+
+
+void MacroAssembler::IsInstanceJSObjectType(Register map,
+ Register scratch,
+ Label* fail) {
+ ldrb(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ cmp(scratch, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ b(lt, fail);
+ cmp(scratch, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ b(gt, fail);
+}
+
+
+void MacroAssembler::IsObjectJSStringType(Register object,
+ Register scratch,
+ Label* fail) {
+ ASSERT(kNotStringTag != 0);
+
+ ldr(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
+ tst(scratch, Operand(kIsNotStringMask));
+ b(ne, fail);
+}
+
+
+void MacroAssembler::IsObjectNameType(Register object,
+ Register scratch,
+ Label* fail) {
+ ldr(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
+ cmp(scratch, Operand(LAST_NAME_TYPE));
+ b(hi, fail);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void MacroAssembler::DebugBreak() {
+ mov(r0, Operand::Zero());
+ mov(r1, Operand(ExternalReference(Runtime::kDebugBreak, isolate())));
+ CEntryStub ces(1);
+ ASSERT(AllowThisStubCall(&ces));
+ Call(ces.GetCode(isolate()), RelocInfo::DEBUG_BREAK);
+}
+#endif
+
+
+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::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::Zero()); // NULL frame pointer.
+ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | ip.bit());
+ } else {
+ 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));
+}
+
+
+void MacroAssembler::PopTryHandler() {
+ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
+ pop(r1);
+ mov(ip, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
+ add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
+ str(r1, MemOperand(ip));
+}
+
+
+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::SmiUntag(r2)); // Jump
+}
+
+
+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::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 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));
+
+ // 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.
+ // (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp
+ // or cp.
+ tst(cp, cp);
+ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
+
+ JumpToHandlerEntry();
+}
+
+
+void MacroAssembler::ThrowUncatchable(Register value) {
+ // Adjust this code if not the case.
+ STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
+ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
+ 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 stack pointer to the top of 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 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();
+}
+
+
+void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss) {
+ Label same_contexts;
+
+ ASSERT(!holder_reg.is(scratch));
+ ASSERT(!holder_reg.is(ip));
+ ASSERT(!scratch.is(ip));
+
+ // Load current lexical context from the stack frame.
+ ldr(scratch, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ // In debug mode, make sure the lexical context is set.
+#ifdef DEBUG
+ cmp(scratch, Operand::Zero());
+ Check(ne, kWeShouldNotHaveAnEmptyLexicalContext);
+#endif
+
+ // Load the native context of the current context.
+ int offset =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ ldr(scratch, FieldMemOperand(scratch, offset));
+ ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset));
+
+ // Check the context is a native context.
+ if (emit_debug_code()) {
+ // Cannot use ip as a temporary in this verification code. Due to the fact
+ // that ip is clobbered as part of cmp with an object Operand.
+ push(holder_reg); // Temporarily save holder on the stack.
+ // Read the first word and compare to the native_context_map.
+ ldr(holder_reg, FieldMemOperand(scratch, HeapObject::kMapOffset));
+ LoadRoot(ip, Heap::kNativeContextMapRootIndex);
+ cmp(holder_reg, ip);
+ Check(eq, kJSGlobalObjectNativeContextShouldBeANativeContext);
+ pop(holder_reg); // Restore holder.
+ }
+
+ // Check if both contexts are the same.
+ ldr(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+ cmp(scratch, Operand(ip));
+ b(eq, &same_contexts);
+
+ // Check the context is a native context.
+ if (emit_debug_code()) {
+ // Cannot use ip as a temporary in this verification code. Due to the fact
+ // that ip is clobbered as part of cmp with an object Operand.
+ push(holder_reg); // Temporarily save holder on the stack.
+ mov(holder_reg, ip); // Move ip to its holding place.
+ LoadRoot(ip, Heap::kNullValueRootIndex);
+ cmp(holder_reg, ip);
+ Check(ne, kJSGlobalProxyContextShouldNotBeNull);
+
+ ldr(holder_reg, FieldMemOperand(holder_reg, HeapObject::kMapOffset));
+ LoadRoot(ip, Heap::kNativeContextMapRootIndex);
+ cmp(holder_reg, ip);
+ Check(eq, kJSGlobalObjectNativeContextShouldBeANativeContext);
+ // Restore ip is not needed. ip is reloaded below.
+ pop(holder_reg); // Restore holder.
+ // Restore ip to holder's context.
+ ldr(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+ }
+
+ // Check that the security token in the calling global object is
+ // compatible with the security token in the receiving global
+ // object.
+ int token_offset = Context::kHeaderSize +
+ Context::SECURITY_TOKEN_INDEX * kPointerSize;
+
+ ldr(scratch, FieldMemOperand(scratch, token_offset));
+ ldr(ip, FieldMemOperand(ip, token_offset));
+ cmp(scratch, Operand(ip));
+ b(ne, miss);
+
+ bind(&same_contexts);
+}
+
+
+void MacroAssembler::GetNumberHash(Register t0, Register scratch) {
+ // First of all we assign the hash seed to scratch.
+ LoadRoot(scratch, Heap::kHashSeedRootIndex);
+ SmiUntag(scratch);
+
+ // Xor original key with a seed.
+ eor(t0, t0, Operand(scratch));
+
+ // Compute the hash code from the untagged key. This must be kept in sync
+ // with ComputeIntegerHash in utils.h.
+ //
+ // hash = ~hash + (hash << 15);
+ mvn(scratch, Operand(t0));
+ add(t0, scratch, Operand(t0, LSL, 15));
+ // hash = hash ^ (hash >> 12);
+ eor(t0, t0, Operand(t0, LSR, 12));
+ // hash = hash + (hash << 2);
+ add(t0, t0, Operand(t0, LSL, 2));
+ // hash = hash ^ (hash >> 4);
+ eor(t0, t0, Operand(t0, LSR, 4));
+ // hash = hash * 2057;
+ 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));
+}
+
+
+void MacroAssembler::LoadFromNumberDictionary(Label* miss,
+ Register elements,
+ Register key,
+ Register result,
+ Register t0,
+ Register t1,
+ Register t2) {
+ // Register use:
+ //
+ // elements - holds the slow-case elements of the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the same as 'key' or 'result'.
+ // Unchanged on bailout so 'key' or 'result' can be used
+ // in further computation.
+ //
+ // Scratch registers:
+ //
+ // t0 - holds the untagged key on entry and holds the hash once computed.
+ //
+ // t1 - used to hold the capacity mask of the dictionary
+ //
+ // t2 - used for the index into the dictionary.
+ Label done;
+
+ GetNumberHash(t0, t1);
+
+ // Compute the capacity mask.
+ ldr(t1, FieldMemOperand(elements, SeededNumberDictionary::kCapacityOffset));
+ SmiUntag(t1);
+ sub(t1, t1, Operand(1));
+
+ // Generate an unrolled loop that performs a few probes before giving up.
+ static const int kProbes = 4;
+ for (int i = 0; i < kProbes; i++) {
+ // Use t2 for index calculations and keep the hash intact in t0.
+ mov(t2, t0);
+ // Compute the masked index: (hash + i + i * i) & mask.
+ if (i > 0) {
+ add(t2, t2, Operand(SeededNumberDictionary::GetProbeOffset(i)));
+ }
+ and_(t2, t2, Operand(t1));
+
+ // Scale the index by multiplying by the element size.
+ ASSERT(SeededNumberDictionary::kEntrySize == 3);
+ add(t2, t2, Operand(t2, LSL, 1)); // t2 = t2 * 3
+
+ // Check if the key is identical to the name.
+ add(t2, elements, Operand(t2, LSL, kPointerSizeLog2));
+ ldr(ip, FieldMemOperand(t2, SeededNumberDictionary::kElementsStartOffset));
+ cmp(key, Operand(ip));
+ if (i != kProbes - 1) {
+ b(eq, &done);
+ } else {
+ b(ne, miss);
+ }
+ }
+
+ bind(&done);
+ // Check that the value is a normal property.
+ // t2: elements + (index * kPointerSize)
+ const int kDetailsOffset =
+ SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize;
+ ldr(t1, FieldMemOperand(t2, kDetailsOffset));
+ tst(t1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask)));
+ b(ne, miss);
+
+ // Get the value at the masked, scaled index and return.
+ const int kValueOffset =
+ SeededNumberDictionary::kElementsStartOffset + kPointerSize;
+ ldr(result, FieldMemOperand(t2, kValueOffset));
+}
+
+
+void MacroAssembler::Allocate(int object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT(object_size <= Page::kMaxNonCodeHeapObjectSize);
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ mov(result, Operand(0x7091));
+ mov(scratch1, Operand(0x7191));
+ mov(scratch2, Operand(0x7291));
+ }
+ jmp(gc_required);
+ return;
+ }
+
+ ASSERT(!result.is(scratch1));
+ ASSERT(!result.is(scratch2));
+ ASSERT(!scratch1.is(scratch2));
+ ASSERT(!scratch1.is(ip));
+ ASSERT(!scratch2.is(ip));
+
+ // Make object size into bytes.
+ if ((flags & SIZE_IN_WORDS) != 0) {
+ object_size *= kPointerSize;
+ }
+ ASSERT_EQ(0, object_size & kObjectAlignmentMask);
+
+ // Check relative positions of allocation top and limit addresses.
+ // The values must be adjacent in memory to allow the use of LDM.
+ // Also, assert that the registers are numbered such that the values
+ // are loaded in the correct order.
+ ExternalReference allocation_top =
+ AllocationUtils::GetAllocationTopReference(isolate(), flags);
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+
+ intptr_t top =
+ reinterpret_cast<intptr_t>(allocation_top.address());
+ intptr_t limit =
+ reinterpret_cast<intptr_t>(allocation_limit.address());
+ ASSERT((limit - top) == kPointerSize);
+ ASSERT(result.code() < ip.code());
+
+ // Set up allocation top address and object size registers.
+ Register topaddr = scratch1;
+ Register obj_size_reg = scratch2;
+ mov(topaddr, Operand(allocation_top));
+ Operand obj_size_operand = Operand(object_size);
+ if (!obj_size_operand.is_single_instruction(this)) {
+ // We are about to steal IP, so we need to load this value first
+ mov(obj_size_reg, obj_size_operand);
+ }
+
+ // This code stores a temporary value in ip. This is OK, as the code below
+ // does not need ip for implicit literal generation.
+ if ((flags & RESULT_CONTAINS_TOP) == 0) {
+ // Load allocation top into result and allocation limit into ip.
+ ldm(ia, topaddr, result.bit() | ip.bit());
+ } else {
+ if (emit_debug_code()) {
+ // Assert that result actually contains top on entry. ip is used
+ // immediately below so this use of ip does not cause difference with
+ // respect to register content between debug and release mode.
+ ldr(ip, MemOperand(topaddr));
+ cmp(result, ip);
+ Check(eq, kUnexpectedAllocationTop);
+ }
+ // Load allocation limit into ip. Result already contains allocation top.
+ ldr(ip, MemOperand(topaddr, limit - top));
+ }
+
+ if ((flags & DOUBLE_ALIGNMENT) != 0) {
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0);
+ ASSERT(kPointerAlignment * 2 == kDoubleAlignment);
+ and_(scratch2, result, Operand(kDoubleAlignmentMask), SetCC);
+ Label aligned;
+ b(eq, &aligned);
+ mov(scratch2, Operand(isolate()->factory()->one_pointer_filler_map()));
+ str(scratch2, MemOperand(result, kDoubleSize / 2, PostIndex));
+ bind(&aligned);
+ }
+
+ // Calculate new top and bail out if new space is exhausted. Use result
+ // to calculate the new top.
+ if (obj_size_operand.is_single_instruction(this)) {
+ // We can add the size as an immediate
+ add(scratch2, result, obj_size_operand, SetCC);
+ } else {
+ // Doesn't fit in an immediate, we have to use the register
+ add(scratch2, result, obj_size_reg, SetCC);
+ }
+ b(cs, gc_required);
+ cmp(scratch2, Operand(ip));
+ b(hi, gc_required);
+ str(scratch2, MemOperand(topaddr));
+
+ // Tag object if requested.
+ if ((flags & TAG_OBJECT) != 0) {
+ add(result, result, Operand(kHeapObjectTag));
+ }
+}
+
+
+void MacroAssembler::Allocate(Register object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags) {
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ mov(result, Operand(0x7091));
+ mov(scratch1, Operand(0x7191));
+ mov(scratch2, Operand(0x7291));
+ }
+ jmp(gc_required);
+ return;
+ }
+
+ // Assert that the register arguments are different and that none of
+ // them are ip. ip is used explicitly in the code generated below.
+ 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));
+
+ // Check relative positions of allocation top and limit addresses.
+ // The values must be adjacent in memory to allow the use of LDM.
+ // Also, assert that the registers are numbered such that the values
+ // are loaded in the correct order.
+ ExternalReference allocation_top =
+ AllocationUtils::GetAllocationTopReference(isolate(), flags);
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+ intptr_t top =
+ reinterpret_cast<intptr_t>(allocation_top.address());
+ intptr_t limit =
+ reinterpret_cast<intptr_t>(allocation_limit.address());
+ ASSERT((limit - top) == kPointerSize);
+ ASSERT(result.code() < ip.code());
+
+ // Set up allocation top address.
+ Register topaddr = scratch1;
+ mov(topaddr, Operand(allocation_top));
+
+ // This code stores a temporary value in ip. This is OK, as the code below
+ // does not need ip for implicit literal generation.
+ if ((flags & RESULT_CONTAINS_TOP) == 0) {
+ // Load allocation top into result and allocation limit into ip.
+ ldm(ia, topaddr, result.bit() | ip.bit());
+ } else {
+ if (emit_debug_code()) {
+ // Assert that result actually contains top on entry. ip is used
+ // immediately below so this use of ip does not cause difference with
+ // respect to register content between debug and release mode.
+ ldr(ip, MemOperand(topaddr));
+ cmp(result, ip);
+ Check(eq, kUnexpectedAllocationTop);
+ }
+ // Load allocation limit into ip. Result already contains allocation top.
+ ldr(ip, MemOperand(topaddr, limit - top));
+ }
+
+ if ((flags & DOUBLE_ALIGNMENT) != 0) {
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0);
+ ASSERT(kPointerAlignment * 2 == kDoubleAlignment);
+ and_(scratch2, result, Operand(kDoubleAlignmentMask), SetCC);
+ Label aligned;
+ b(eq, &aligned);
+ mov(scratch2, Operand(isolate()->factory()->one_pointer_filler_map()));
+ str(scratch2, MemOperand(result, kDoubleSize / 2, PostIndex));
+ bind(&aligned);
+ }
+
+ // Calculate new top and bail out if new space is exhausted. Use result
+ // to calculate the new top. Object size may be in words so a shift is
+ // required to get the number of bytes.
+ if ((flags & SIZE_IN_WORDS) != 0) {
+ add(scratch2, result, Operand(object_size, LSL, kPointerSizeLog2), SetCC);
+ } else {
+ add(scratch2, result, Operand(object_size), SetCC);
+ }
+ b(cs, gc_required);
+ cmp(scratch2, Operand(ip));
+ b(hi, gc_required);
+
+ // Update allocation top. result temporarily holds the new top.
+ if (emit_debug_code()) {
+ tst(scratch2, Operand(kObjectAlignmentMask));
+ Check(eq, kUnalignedAllocationInNewSpace);
+ }
+ str(scratch2, MemOperand(topaddr));
+
+ // Tag object if requested.
+ if ((flags & TAG_OBJECT) != 0) {
+ add(result, result, Operand(kHeapObjectTag));
+ }
+}
+
+
+void MacroAssembler::UndoAllocationInNewSpace(Register object,
+ Register scratch) {
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+
+ // Make sure the object has no tag before resetting top.
+ and_(object, object, Operand(~kHeapObjectTagMask));
+#ifdef DEBUG
+ // Check that the object un-allocated is below the current top.
+ mov(scratch, Operand(new_space_allocation_top));
+ ldr(scratch, MemOperand(scratch));
+ cmp(object, scratch);
+ Check(lt, kUndoAllocationOfNonAllocatedMemory);
+#endif
+ // Write the address of the object to un-allocate as the current top.
+ mov(scratch, Operand(new_space_allocation_top));
+ str(object, MemOperand(scratch));
+}
+
+
+void MacroAssembler::AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ mov(scratch1, Operand(length, LSL, 1)); // Length in bytes, not chars.
+ add(scratch1, scratch1,
+ Operand(kObjectAlignmentMask + SeqTwoByteString::kHeaderSize));
+ and_(scratch1, scratch1, Operand(~kObjectAlignmentMask));
+
+ // Allocate two-byte string in new space.
+ Allocate(scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ InitializeNewString(result,
+ length,
+ Heap::kStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ ASSERT(kCharSize == 1);
+ add(scratch1, length,
+ Operand(kObjectAlignmentMask + SeqOneByteString::kHeaderSize));
+ and_(scratch1, scratch1, Operand(~kObjectAlignmentMask));
+
+ // Allocate ASCII string in new space.
+ Allocate(scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ InitializeNewString(result,
+ length,
+ Heap::kAsciiStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateTwoByteConsString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Allocate(ConsString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ InitializeNewString(result,
+ length,
+ Heap::kConsStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateAsciiConsString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Label allocate_new_space, install_map;
+ AllocationFlags flags = TAG_OBJECT;
+
+ ExternalReference high_promotion_mode = ExternalReference::
+ new_space_high_promotion_mode_active_address(isolate());
+ mov(scratch1, Operand(high_promotion_mode));
+ ldr(scratch1, MemOperand(scratch1, 0));
+ cmp(scratch1, Operand::Zero());
+ b(eq, &allocate_new_space);
+
+ Allocate(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ static_cast<AllocationFlags>(flags | PRETENURE_OLD_POINTER_SPACE));
+
+ jmp(&install_map);
+
+ bind(&allocate_new_space);
+ Allocate(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ flags);
+
+ bind(&install_map);
+
+ InitializeNewString(result,
+ length,
+ Heap::kConsAsciiStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateTwoByteSlicedString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ InitializeNewString(result,
+ length,
+ Heap::kSlicedStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateAsciiSlicedString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ InitializeNewString(result,
+ length,
+ Heap::kSlicedAsciiStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::CompareObjectType(Register object,
+ Register map,
+ Register type_reg,
+ InstanceType type) {
+ ldr(map, FieldMemOperand(object, HeapObject::kMapOffset));
+ CompareInstanceType(map, type_reg, type);
+}
+
+
+void MacroAssembler::CompareInstanceType(Register map,
+ Register type_reg,
+ InstanceType type) {
+ ldrb(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ cmp(type_reg, Operand(type));
+}
+
+
+void MacroAssembler::CompareRoot(Register obj,
+ Heap::RootListIndex index) {
+ ASSERT(!obj.is(ip));
+ LoadRoot(ip, index);
+ cmp(obj, ip);
+}
+
+
+void MacroAssembler::CheckFastElements(Register map,
+ Register scratch,
+ Label* fail) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ STATIC_ASSERT(FAST_ELEMENTS == 2);
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset));
+ cmp(scratch, Operand(Map::kMaximumBitField2FastHoleyElementValue));
+ b(hi, fail);
+}
+
+
+void MacroAssembler::CheckFastObjectElements(Register map,
+ Register scratch,
+ Label* fail) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ STATIC_ASSERT(FAST_ELEMENTS == 2);
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset));
+ cmp(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue));
+ b(ls, fail);
+ cmp(scratch, Operand(Map::kMaximumBitField2FastHoleyElementValue));
+ b(hi, fail);
+}
+
+
+void MacroAssembler::CheckFastSmiElements(Register map,
+ Register scratch,
+ Label* fail) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset));
+ cmp(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue));
+ b(hi, fail);
+}
+
+
+void MacroAssembler::StoreNumberToDoubleElements(
+ Register value_reg,
+ Register key_reg,
+ Register elements_reg,
+ Register scratch1,
+ LowDwVfpRegister double_scratch,
+ Label* fail,
+ int elements_offset) {
+ Label smi_value, store;
+
+ // 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);
+
+ vldr(double_scratch, FieldMemOperand(value_reg, HeapNumber::kValueOffset));
+ // Force a canonical NaN.
+ if (emit_debug_code()) {
+ vmrs(ip);
+ tst(ip, Operand(kVFPDefaultNaNModeControlBit));
+ Assert(ne, kDefaultNaNModeNotSet);
+ }
+ VFPCanonicalizeNaN(double_scratch);
+ b(&store);
+
+ bind(&smi_value);
+ SmiToDouble(double_scratch, value_reg);
+
+ bind(&store);
+ add(scratch1, elements_reg, Operand::DoubleOffsetFromSmiKey(key_reg));
+ vstr(double_scratch,
+ FieldMemOperand(scratch1,
+ FixedDoubleArray::kHeaderSize - elements_offset));
+}
+
+
+void MacroAssembler::CompareMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Label* early_success) {
+ ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
+ CompareMap(scratch, map, early_success);
+}
+
+
+void MacroAssembler::CompareMap(Register obj_map,
+ Handle<Map> map,
+ Label* early_success) {
+ cmp(obj_map, Operand(map));
+}
+
+
+void MacroAssembler::CheckMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Label* fail,
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, fail);
+ }
+
+ Label success;
+ CompareMap(obj, scratch, map, &success);
+ b(ne, fail);
+ bind(&success);
+}
+
+
+void MacroAssembler::CheckMap(Register obj,
+ Register scratch,
+ Heap::RootListIndex index,
+ Label* fail,
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, fail);
+ }
+ ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
+ LoadRoot(ip, index);
+ cmp(scratch, ip);
+ b(ne, fail);
+}
+
+
+void MacroAssembler::DispatchMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type) {
+ Label fail;
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, &fail);
+ }
+ ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
+ mov(ip, Operand(map));
+ cmp(scratch, ip);
+ Jump(success, RelocInfo::CODE_TARGET, eq);
+ bind(&fail);
+}
+
+
+void MacroAssembler::TryGetFunctionPrototype(Register function,
+ Register result,
+ Register scratch,
+ Label* miss,
+ bool miss_on_bound_function) {
+ // Check that the receiver isn't a smi.
+ JumpIfSmi(function, miss);
+
+ // Check that the function really is a function. Load map into result reg.
+ 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));
+ tst(scratch, Operand(1 << Map::kHasNonInstancePrototype));
+ b(ne, &non_instance);
+
+ // Get the prototype or initial map from the function.
+ ldr(result,
+ FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // 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.
+ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ cmp(result, ip);
+ b(eq, miss);
+
+ // If the function does not have an initial map, we're done.
+ Label done;
+ CompareObjectType(result, scratch, scratch, MAP_TYPE);
+ b(ne, &done);
+
+ // Get the prototype from the initial map.
+ ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
+ jmp(&done);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in initial map.
+ bind(&non_instance);
+ ldr(result, FieldMemOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ bind(&done);
+}
+
+
+void MacroAssembler::CallStub(CodeStub* stub,
+ TypeFeedbackId ast_id,
+ Condition cond) {
+ ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs.
+ Call(stub->GetCode(isolate()), RelocInfo::CODE_TARGET, ast_id, cond);
+}
+
+
+void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) {
+ ASSERT(allow_stub_calls_ ||
+ stub->CompilingCallsToThisStubIsGCSafe(isolate()));
+ Jump(stub->GetCode(isolate()), RelocInfo::CODE_TARGET, cond);
+}
+
+
+static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
+ return ref0.address() - ref1.address();
+}
+
+
+void MacroAssembler::CallApiFunctionAndReturn(ExternalReference function,
+ Address function_address,
+ ExternalReference thunk_ref,
+ Register thunk_last_arg,
+ int stack_space,
+ bool returns_handle,
+ int return_value_offset) {
+ ExternalReference next_address =
+ ExternalReference::handle_scope_next_address(isolate());
+ const int kNextOffset = 0;
+ const int kLimitOffset = AddressOffset(
+ ExternalReference::handle_scope_limit_address(isolate()),
+ next_address);
+ const int kLevelOffset = AddressOffset(
+ ExternalReference::handle_scope_level_address(isolate()),
+ next_address);
+
+ // Allocate HandleScope in callee-save registers.
+ mov(r7, Operand(next_address));
+ ldr(r4, MemOperand(r7, kNextOffset));
+ ldr(r5, MemOperand(r7, kLimitOffset));
+ ldr(r6, MemOperand(r7, kLevelOffset));
+ add(r6, r6, Operand(1));
+ str(r6, MemOperand(r7, kLevelOffset));
+
+ if (FLAG_log_timer_events) {
+ FrameScope frame(this, StackFrame::MANUAL);
+ PushSafepointRegisters();
+ PrepareCallCFunction(1, r0);
+ mov(r0, Operand(ExternalReference::isolate_address(isolate())));
+ CallCFunction(ExternalReference::log_enter_external_function(isolate()), 1);
+ PopSafepointRegisters();
+ }
+
+ ASSERT(!thunk_last_arg.is(r3));
+ Label profiler_disabled;
+ Label end_profiler_check;
+ bool* is_profiling_flag =
+ isolate()->cpu_profiler()->is_profiling_address();
+ STATIC_ASSERT(sizeof(*is_profiling_flag) == 1);
+ mov(r3, Operand(reinterpret_cast<int32_t>(is_profiling_flag)));
+ ldrb(r3, MemOperand(r3, 0));
+ cmp(r3, Operand(0));
+ b(eq, &profiler_disabled);
+
+ // Additional parameter is the address of the actual callback.
+ mov(thunk_last_arg, Operand(reinterpret_cast<int32_t>(function_address)));
+ mov(r3, Operand(thunk_ref));
+ jmp(&end_profiler_check);
+
+ bind(&profiler_disabled);
+ mov(r3, Operand(function));
+ bind(&end_profiler_check);
+
+ // Native call returns to the DirectCEntry stub which redirects to the
+ // return address pushed on stack (could have moved after GC).
+ // DirectCEntry stub itself is generated early and never moves.
+ DirectCEntryStub stub;
+ stub.GenerateCall(this, r3);
+
+ if (FLAG_log_timer_events) {
+ FrameScope frame(this, StackFrame::MANUAL);
+ PushSafepointRegisters();
+ PrepareCallCFunction(1, r0);
+ mov(r0, Operand(ExternalReference::isolate_address(isolate())));
+ CallCFunction(ExternalReference::log_leave_external_function(isolate()), 1);
+ PopSafepointRegisters();
+ }
+
+ Label promote_scheduled_exception;
+ Label delete_allocated_handles;
+ Label leave_exit_frame;
+ Label return_value_loaded;
+
+ if (returns_handle) {
+ Label load_return_value;
+ cmp(r0, Operand::Zero());
+ b(eq, &load_return_value);
+ // derefernce returned value
+ ldr(r0, MemOperand(r0));
+ b(&return_value_loaded);
+ bind(&load_return_value);
+ }
+ // load value from ReturnValue
+ ldr(r0, MemOperand(fp, return_value_offset*kPointerSize));
+ bind(&return_value_loaded);
+ // No more valid handles (the result handle was the last one). Restore
+ // previous handle scope.
+ str(r4, MemOperand(r7, kNextOffset));
+ if (emit_debug_code()) {
+ ldr(r1, MemOperand(r7, kLevelOffset));
+ cmp(r1, r6);
+ Check(eq, kUnexpectedLevelAfterReturnFromApiCall);
+ }
+ sub(r6, r6, Operand(1));
+ str(r6, MemOperand(r7, kLevelOffset));
+ ldr(ip, MemOperand(r7, kLimitOffset));
+ cmp(r5, ip);
+ b(ne, &delete_allocated_handles);
+
+ // Check if the function scheduled an exception.
+ bind(&leave_exit_frame);
+ LoadRoot(r4, Heap::kTheHoleValueRootIndex);
+ mov(ip, Operand(ExternalReference::scheduled_exception_address(isolate())));
+ ldr(r5, MemOperand(ip));
+ cmp(r4, r5);
+ b(ne, &promote_scheduled_exception);
+
+ // LeaveExitFrame expects unwind space to be in a register.
+ mov(r4, Operand(stack_space));
+ LeaveExitFrame(false, r4);
+ mov(pc, lr);
+
+ bind(&promote_scheduled_exception);
+ TailCallExternalReference(
+ ExternalReference(Runtime::kPromoteScheduledException, isolate()),
+ 0,
+ 1);
+
+ // HandleScope limit has changed. Delete allocated extensions.
+ bind(&delete_allocated_handles);
+ str(r5, MemOperand(r7, kLimitOffset));
+ mov(r4, r0);
+ PrepareCallCFunction(1, r5);
+ mov(r0, Operand(ExternalReference::isolate_address(isolate())));
+ CallCFunction(
+ ExternalReference::delete_handle_scope_extensions(isolate()), 1);
+ mov(r0, r4);
+ jmp(&leave_exit_frame);
+}
+
+
+bool MacroAssembler::AllowThisStubCall(CodeStub* stub) {
+ if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false;
+ return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe(isolate());
+}
+
+
+void MacroAssembler::IllegalOperation(int num_arguments) {
+ if (num_arguments > 0) {
+ add(sp, sp, Operand(num_arguments * kPointerSize));
+ }
+ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+}
+
+
+void MacroAssembler::IndexFromHash(Register hash, Register index) {
+ // If the hash field contains an array index pick it out. The assert checks
+ // that the constants for the maximum number of digits for an array index
+ // cached in the hash field and the number of bits reserved for it does not
+ // conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
+ // the low kHashShift bits.
+ Ubfx(hash, hash, String::kHashShift, String::kArrayIndexValueBits);
+ SmiTag(index, hash);
+}
+
+
+void MacroAssembler::SmiToDouble(LowDwVfpRegister value, Register smi) {
+ if (CpuFeatures::IsSupported(VFP3)) {
+ vmov(value.low(), smi);
+ vcvt_f64_s32(value, 1);
+ } else {
+ SmiUntag(ip, smi);
+ vmov(value.low(), ip);
+ vcvt_f64_s32(value, value.low());
+ }
+}
+
+
+void MacroAssembler::TestDoubleIsInt32(DwVfpRegister double_input,
+ LowDwVfpRegister double_scratch) {
+ ASSERT(!double_input.is(double_scratch));
+ vcvt_s32_f64(double_scratch.low(), double_input);
+ vcvt_f64_s32(double_scratch, double_scratch.low());
+ VFPCompareAndSetFlags(double_input, double_scratch);
+}
+
+
+void MacroAssembler::TryDoubleToInt32Exact(Register result,
+ DwVfpRegister double_input,
+ LowDwVfpRegister double_scratch) {
+ ASSERT(!double_input.is(double_scratch));
+ vcvt_s32_f64(double_scratch.low(), double_input);
+ vmov(result, double_scratch.low());
+ vcvt_f64_s32(double_scratch, double_scratch.low());
+ VFPCompareAndSetFlags(double_input, double_scratch);
+}
+
+
+void MacroAssembler::TryInt32Floor(Register result,
+ DwVfpRegister double_input,
+ Register input_high,
+ LowDwVfpRegister double_scratch,
+ Label* done,
+ Label* exact) {
+ ASSERT(!result.is(input_high));
+ ASSERT(!double_input.is(double_scratch));
+ Label negative, exception;
+
+ VmovHigh(input_high, double_input);
+
+ // Test for NaN and infinities.
+ Sbfx(result, input_high,
+ HeapNumber::kExponentShift, HeapNumber::kExponentBits);
+ cmp(result, Operand(-1));
+ b(eq, &exception);
+ // Test for values that can be exactly represented as a
+ // signed 32-bit integer.
+ TryDoubleToInt32Exact(result, double_input, double_scratch);
+ // If exact, return (result already fetched).
+ b(eq, exact);
+ cmp(input_high, Operand::Zero());
+ b(mi, &negative);
+
+ // Input is in ]+0, +inf[.
+ // If result equals 0x7fffffff input was out of range or
+ // in ]0x7fffffff, 0x80000000[. We ignore this last case which
+ // could fits into an int32, that means we always think input was
+ // out of range and always go to exception.
+ // If result < 0x7fffffff, go to done, result fetched.
+ cmn(result, Operand(1));
+ b(mi, &exception);
+ b(done);
+
+ // Input is in ]-inf, -0[.
+ // If x is a non integer negative number,
+ // floor(x) <=> round_to_zero(x) - 1.
+ bind(&negative);
+ sub(result, result, Operand(1), SetCC);
+ // If result is still negative, go to done, result fetched.
+ // Else, we had an overflow and we fall through exception.
+ b(mi, done);
+ bind(&exception);
+}
+
+
+void MacroAssembler::ECMAToInt32(Register result,
+ DwVfpRegister double_input,
+ Register scratch,
+ Register scratch_high,
+ Register scratch_low,
+ LowDwVfpRegister double_scratch) {
+ ASSERT(!scratch_high.is(result));
+ ASSERT(!scratch_low.is(result));
+ ASSERT(!scratch_low.is(scratch_high));
+ ASSERT(!scratch.is(result) &&
+ !scratch.is(scratch_high) &&
+ !scratch.is(scratch_low));
+ ASSERT(!double_input.is(double_scratch));
+
+ Label out_of_range, only_low, negate, done;
+
+ vcvt_s32_f64(double_scratch.low(), double_input);
+ vmov(result, double_scratch.low());
+
+ // If result is not saturated (0x7fffffff or 0x80000000), we are done.
+ sub(scratch, result, Operand(1));
+ cmp(scratch, Operand(0x7ffffffe));
+ b(lt, &done);
+
+ vmov(scratch_low, scratch_high, double_input);
+ Ubfx(scratch, scratch_high,
+ HeapNumber::kExponentShift, HeapNumber::kExponentBits);
+ // Load scratch with exponent - 1. This is faster than loading
+ // with exponent because Bias + 1 = 1024 which is an *ARM* immediate value.
+ sub(scratch, scratch, Operand(HeapNumber::kExponentBias + 1));
+ // If exponent is greater than or equal to 84, the 32 less significant
+ // bits are 0s (2^84 = 1, 52 significant bits, 32 uncoded bits),
+ // the result is 0.
+ // Compare exponent with 84 (compare exponent - 1 with 83).
+ cmp(scratch, Operand(83));
+ b(ge, &out_of_range);
+
+ // If we reach this code, 31 <= exponent <= 83.
+ // So, we don't have to handle cases where 0 <= exponent <= 20 for
+ // which we would need to shift right the high part of the mantissa.
+ // Scratch contains exponent - 1.
+ // Load scratch with 52 - exponent (load with 51 - (exponent - 1)).
+ rsb(scratch, scratch, Operand(51), SetCC);
+ b(ls, &only_low);
+ // 21 <= exponent <= 51, shift scratch_low and scratch_high
+ // to generate the result.
+ mov(scratch_low, Operand(scratch_low, LSR, scratch));
+ // Scratch contains: 52 - exponent.
+ // We needs: exponent - 20.
+ // So we use: 32 - scratch = 32 - 52 + exponent = exponent - 20.
+ rsb(scratch, scratch, Operand(32));
+ Ubfx(result, scratch_high,
+ 0, HeapNumber::kMantissaBitsInTopWord);
+ // Set the implicit 1 before the mantissa part in scratch_high.
+ orr(result, result, Operand(1 << HeapNumber::kMantissaBitsInTopWord));
+ orr(result, scratch_low, Operand(result, LSL, scratch));
+ b(&negate);
+
+ bind(&out_of_range);
+ mov(result, Operand::Zero());
+ b(&done);
+
+ bind(&only_low);
+ // 52 <= exponent <= 83, shift only scratch_low.
+ // On entry, scratch contains: 52 - exponent.
+ rsb(scratch, scratch, Operand::Zero());
+ mov(result, Operand(scratch_low, LSL, scratch));
+
+ bind(&negate);
+ // If input was positive, scratch_high ASR 31 equals 0 and
+ // scratch_high LSR 31 equals zero.
+ // New result = (result eor 0) + 0 = result.
+ // If the input was negative, we have to negate the result.
+ // Input_high ASR 31 equals 0xffffffff and scratch_high LSR 31 equals 1.
+ // New result = (result eor 0xffffffff) + 1 = 0 - result.
+ eor(result, result, Operand(scratch_high, ASR, 31));
+ add(result, result, Operand(scratch_high, LSR, 31));
+
+ bind(&done);
+}
+
+
+void MacroAssembler::GetLeastBitsFromSmi(Register dst,
+ Register src,
+ int num_least_bits) {
+ if (CpuFeatures::IsSupported(ARMv7) && !predictable_code_size()) {
+ ubfx(dst, src, kSmiTagSize, num_least_bits);
+ } else {
+ SmiUntag(dst, src);
+ and_(dst, dst, Operand((1 << num_least_bits) - 1));
+ }
+}
+
+
+void MacroAssembler::GetLeastBitsFromInt32(Register dst,
+ Register src,
+ int num_least_bits) {
+ and_(dst, src, Operand((1 << num_least_bits) - 1));
+}
+
+
+void MacroAssembler::CallRuntime(const Runtime::Function* f,
+ int num_arguments) {
+ // All parameters are on the stack. r0 has the return value after call.
+
+ // If the expected number of arguments of the runtime function is
+ // constant, we check that the actual number of arguments match the
+ // expectation.
+ if (f->nargs >= 0 && f->nargs != num_arguments) {
+ IllegalOperation(num_arguments);
+ return;
+ }
+
+ // 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));
+ mov(r1, Operand(ExternalReference(f, isolate())));
+ CEntryStub stub(1);
+ CallStub(&stub);
+}
+
+
+void MacroAssembler::CallRuntime(Runtime::FunctionId fid, int num_arguments) {
+ CallRuntime(Runtime::FunctionForId(fid), num_arguments);
+}
+
+
+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, kSaveFPRegs);
+ CallStub(&stub);
+}
+
+
+void MacroAssembler::CallExternalReference(const ExternalReference& ext,
+ int num_arguments) {
+ mov(r0, Operand(num_arguments));
+ mov(r1, Operand(ext));
+
+ CEntryStub stub(1);
+ CallStub(&stub);
+}
+
+
+void MacroAssembler::TailCallExternalReference(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));
+ JumpToExternalReference(ext);
+}
+
+
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(ExternalReference(fid, isolate()),
+ num_arguments,
+ result_size);
+}
+
+
+void MacroAssembler::JumpToExternalReference(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);
+ Jump(stub.GetCode(isolate()), RelocInfo::CODE_TARGET);
+}
+
+
+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));
+ SetCallKind(r5, CALL_AS_METHOD);
+ Call(r2);
+ call_wrapper.AfterCall();
+ } else {
+ ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(r5, CALL_AS_METHOD);
+ Jump(r2);
+ }
+}
+
+
+void MacroAssembler::GetBuiltinFunction(Register target,
+ Builtins::JavaScript id) {
+ // Load the builtins object into target register.
+ ldr(target,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ ldr(target, FieldMemOperand(target, GlobalObject::kBuiltinsOffset));
+ // Load the JavaScript builtin function from the builtins object.
+ ldr(target, FieldMemOperand(target,
+ JSBuiltinsObject::OffsetOfFunctionWithId(id)));
+}
+
+
+void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
+ ASSERT(!target.is(r1));
+ GetBuiltinFunction(r1, id);
+ // Load the code entry point from the builtins object.
+ ldr(target, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
+}
+
+
+void MacroAssembler::SetCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2) {
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ mov(scratch1, Operand(value));
+ mov(scratch2, Operand(ExternalReference(counter)));
+ str(scratch1, MemOperand(scratch2));
+ }
+}
+
+
+void MacroAssembler::IncrementCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ mov(scratch2, Operand(ExternalReference(counter)));
+ ldr(scratch1, MemOperand(scratch2));
+ add(scratch1, scratch1, Operand(value));
+ str(scratch1, MemOperand(scratch2));
+ }
+}
+
+
+void MacroAssembler::DecrementCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ mov(scratch2, Operand(ExternalReference(counter)));
+ ldr(scratch1, MemOperand(scratch2));
+ sub(scratch1, scratch1, Operand(value));
+ str(scratch1, MemOperand(scratch2));
+ }
+}
+
+
+void MacroAssembler::Assert(Condition cond, BailoutReason reason) {
+ if (emit_debug_code())
+ Check(cond, reason);
+}
+
+
+void MacroAssembler::AssertFastElements(Register elements) {
+ if (emit_debug_code()) {
+ ASSERT(!elements.is(ip));
+ Label ok;
+ push(elements);
+ ldr(elements, FieldMemOperand(elements, HeapObject::kMapOffset));
+ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
+ cmp(elements, ip);
+ b(eq, &ok);
+ LoadRoot(ip, Heap::kFixedDoubleArrayMapRootIndex);
+ cmp(elements, ip);
+ b(eq, &ok);
+ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
+ cmp(elements, ip);
+ b(eq, &ok);
+ Abort(kJSObjectWithFastElementsMapHasSlowElements);
+ bind(&ok);
+ pop(elements);
+ }
+}
+
+
+void MacroAssembler::Check(Condition cond, BailoutReason reason) {
+ Label L;
+ b(cond, &L);
+ Abort(reason);
+ // will not return here
+ bind(&L);
+}
+
+
+void MacroAssembler::Abort(BailoutReason reason) {
+ Label abort_start;
+ bind(&abort_start);
+ // We want to pass the msg string like a smi to avoid GC
+ // problems, however msg is not guaranteed to be aligned
+ // properly. Instead, we pass an aligned pointer that is
+ // a proper v8 smi, but also pass the alignment difference
+ // from the real pointer as a smi.
+ const char* msg = GetBailoutReason(reason);
+ intptr_t p1 = reinterpret_cast<intptr_t>(msg);
+ intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
+ ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
+#ifdef DEBUG
+ if (msg != NULL) {
+ RecordComment("Abort message: ");
+ RecordComment(msg);
+ }
+#endif
+
+ mov(r0, Operand(p0));
+ push(r0);
+ mov(r0, Operand(Smi::FromInt(p1 - p0)));
+ push(r0);
+ // 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
+ // instructions generated, we insert padding here to keep the size
+ // of the Abort macro constant.
+ static const int kExpectedAbortInstructions = 10;
+ int abort_instructions = InstructionsGeneratedSince(&abort_start);
+ ASSERT(abort_instructions <= kExpectedAbortInstructions);
+ while (abort_instructions++ < kExpectedAbortInstructions) {
+ nop();
+ }
+ }
+}
+
+
+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.
+ ldr(dst, MemOperand(cp, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ for (int i = 1; i < context_chain_length; i++) {
+ ldr(dst, MemOperand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ }
+ } else {
+ // Slot is in the current function context. Move it into the
+ // destination register in case we store into it (the write barrier
+ // cannot be allowed to destroy the context in esi).
+ mov(dst, cp);
+ }
+}
+
+
+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_OBJECT_INDEX)));
+ ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset));
+
+ // Check that the function's map is the same as the expected cached map.
+ ldr(scratch,
+ MemOperand(scratch,
+ Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX)));
+ size_t offset = expected_kind * kPointerSize +
+ FixedArrayBase::kHeaderSize;
+ ldr(ip, FieldMemOperand(scratch, offset));
+ cmp(map_in_out, ip);
+ b(ne, no_map_match);
+
+ // Use the transitioned cached map.
+ offset = transitioned_kind * kPointerSize +
+ FixedArrayBase::kHeaderSize;
+ ldr(map_in_out, FieldMemOperand(scratch, offset));
+}
+
+
+void MacroAssembler::LoadInitialArrayMap(
+ Register function_in, Register scratch,
+ Register map_out, bool can_have_holes) {
+ ASSERT(!function_in.is(map_out));
+ Label done;
+ ldr(map_out, FieldMemOperand(function_in,
+ JSFunction::kPrototypeOrInitialMapOffset));
+ if (!FLAG_smi_only_arrays) {
+ ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
+ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ kind,
+ map_out,
+ scratch,
+ &done);
+ } else if (can_have_holes) {
+ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_HOLEY_SMI_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_OBJECT_INDEX)));
+ // Load the native context from the global or builtins object.
+ ldr(function, FieldMemOperand(function,
+ GlobalObject::kNativeContextOffset));
+ // Load the function from the native context.
+ ldr(function, MemOperand(function, Context::SlotOffset(index)));
+}
+
+
+void MacroAssembler::LoadArrayFunction(Register function) {
+ // Load the global or builtins object from the current context.
+ ldr(function,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ // Load the global context from the global or builtins object.
+ ldr(function,
+ FieldMemOperand(function, GlobalObject::kGlobalContextOffset));
+ // Load the array function from the native context.
+ ldr(function,
+ MemOperand(function, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
+}
+
+
+void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
+ Register map,
+ Register scratch) {
+ // Load the initial map. The global functions all have initial maps.
+ ldr(map, FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+ if (emit_debug_code()) {
+ Label ok, fail;
+ CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, DO_SMI_CHECK);
+ b(&ok);
+ bind(&fail);
+ Abort(kGlobalFunctionsMustHaveInitialMap);
+ bind(&ok);
+ }
+}
+
+
+void MacroAssembler::JumpIfNotPowerOfTwoOrZero(
+ Register reg,
+ Register scratch,
+ Label* not_power_of_two_or_zero) {
+ sub(scratch, reg, Operand(1), SetCC);
+ b(mi, not_power_of_two_or_zero);
+ tst(scratch, reg);
+ b(ne, not_power_of_two_or_zero);
+}
+
+
+void MacroAssembler::JumpIfNotPowerOfTwoOrZeroAndNeg(
+ Register reg,
+ Register scratch,
+ Label* zero_and_neg,
+ Label* not_power_of_two) {
+ sub(scratch, reg, Operand(1), SetCC);
+ b(mi, zero_and_neg);
+ tst(scratch, reg);
+ b(ne, not_power_of_two);
+}
+
+
+void MacroAssembler::JumpIfNotBothSmi(Register reg1,
+ Register reg2,
+ Label* on_not_both_smi) {
+ STATIC_ASSERT(kSmiTag == 0);
+ tst(reg1, Operand(kSmiTagMask));
+ tst(reg2, Operand(kSmiTagMask), eq);
+ b(ne, on_not_both_smi);
+}
+
+
+void MacroAssembler::UntagAndJumpIfSmi(
+ Register dst, Register src, Label* smi_case) {
+ STATIC_ASSERT(kSmiTag == 0);
+ SmiUntag(dst, src, 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);
+ SmiUntag(dst, src, 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) {
+ STATIC_ASSERT(kSmiTag == 0);
+ tst(reg1, Operand(kSmiTagMask));
+ tst(reg2, Operand(kSmiTagMask), ne);
+ b(eq, on_either_smi);
+}
+
+
+void MacroAssembler::AssertNotSmi(Register object) {
+ if (emit_debug_code()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ tst(object, Operand(kSmiTagMask));
+ Check(ne, kOperandIsASmi);
+ }
+}
+
+
+void MacroAssembler::AssertSmi(Register object) {
+ if (emit_debug_code()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ tst(object, Operand(kSmiTagMask));
+ Check(eq, kOperandIsNotSmi);
+ }
+}
+
+
+void MacroAssembler::AssertString(Register object) {
+ if (emit_debug_code()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ tst(object, Operand(kSmiTagMask));
+ Check(ne, kOperandIsASmiAndNotAString);
+ push(object);
+ ldr(object, FieldMemOperand(object, HeapObject::kMapOffset));
+ CompareInstanceType(object, object, FIRST_NONSTRING_TYPE);
+ pop(object);
+ Check(lo, kOperandIsNotAString);
+ }
+}
+
+
+void MacroAssembler::AssertName(Register object) {
+ if (emit_debug_code()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ tst(object, Operand(kSmiTagMask));
+ Check(ne, kOperandIsASmiAndNotAName);
+ push(object);
+ ldr(object, FieldMemOperand(object, HeapObject::kMapOffset));
+ CompareInstanceType(object, object, LAST_NAME_TYPE);
+ pop(object);
+ Check(le, kOperandIsNotAName);
+ }
+}
+
+
+
+void MacroAssembler::AssertIsRoot(Register reg, Heap::RootListIndex index) {
+ if (emit_debug_code()) {
+ CompareRoot(reg, index);
+ Check(eq, kHeapNumberMapRegisterClobbered);
+ }
+}
+
+
+void MacroAssembler::JumpIfNotHeapNumber(Register object,
+ Register heap_number_map,
+ Register scratch,
+ Label* on_not_heap_number) {
+ ldr(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ AssertIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+ cmp(scratch, heap_number_map);
+ b(ne, on_not_heap_number);
+}
+
+
+void MacroAssembler::JumpIfNonSmisNotBothSequentialAsciiStrings(
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* failure) {
+ // Test that both first and second are sequential ASCII strings.
+ // Assume that they are non-smis.
+ ldr(scratch1, FieldMemOperand(first, HeapObject::kMapOffset));
+ ldr(scratch2, FieldMemOperand(second, HeapObject::kMapOffset));
+ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ ldrb(scratch2, FieldMemOperand(scratch2, Map::kInstanceTypeOffset));
+
+ JumpIfBothInstanceTypesAreNotSequentialAscii(scratch1,
+ scratch2,
+ scratch1,
+ scratch2,
+ failure);
+}
+
+void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* failure) {
+ // Check that neither is a smi.
+ and_(scratch1, first, Operand(second));
+ JumpIfSmi(scratch1, failure);
+ JumpIfNonSmisNotBothSequentialAsciiStrings(first,
+ second,
+ scratch1,
+ scratch2,
+ failure);
+}
+
+
+void MacroAssembler::JumpIfNotUniqueName(Register reg,
+ Label* not_unique_name) {
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ Label succeed;
+ tst(reg, Operand(kIsNotStringMask | kIsNotInternalizedMask));
+ b(eq, &succeed);
+ cmp(reg, Operand(SYMBOL_TYPE));
+ b(ne, not_unique_name);
+
+ bind(&succeed);
+}
+
+
+// Allocates a heap number or jumps to the need_gc label if the young space
+// is full and a scavenge is needed.
+void MacroAssembler::AllocateHeapNumber(Register result,
+ Register scratch1,
+ Register scratch2,
+ Register heap_number_map,
+ Label* gc_required,
+ TaggingMode tagging_mode) {
+ // Allocate an object in the heap for the heap number and tag it as a heap
+ // object.
+ Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
+ tagging_mode == TAG_RESULT ? TAG_OBJECT : NO_ALLOCATION_FLAGS);
+
+ // Store heap number map in the allocated object.
+ AssertIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+ if (tagging_mode == TAG_RESULT) {
+ str(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset));
+ } else {
+ str(heap_number_map, MemOperand(result, HeapObject::kMapOffset));
+ }
+}
+
+
+void MacroAssembler::AllocateHeapNumberWithValue(Register result,
+ DwVfpRegister value,
+ Register scratch1,
+ Register scratch2,
+ Register heap_number_map,
+ Label* gc_required) {
+ AllocateHeapNumber(result, scratch1, scratch2, heap_number_map, gc_required);
+ sub(scratch1, result, Operand(kHeapObjectTag));
+ vstr(value, scratch1, HeapNumber::kValueOffset);
+}
+
+
+// Copies a fixed number of fields of heap objects from src to dst.
+void MacroAssembler::CopyFields(Register dst,
+ Register src,
+ LowDwVfpRegister double_scratch,
+ int field_count) {
+ int double_count = field_count / (DwVfpRegister::kSizeInBytes / kPointerSize);
+ for (int i = 0; i < double_count; i++) {
+ vldr(double_scratch, FieldMemOperand(src, i * DwVfpRegister::kSizeInBytes));
+ vstr(double_scratch, FieldMemOperand(dst, i * DwVfpRegister::kSizeInBytes));
+ }
+
+ STATIC_ASSERT(SwVfpRegister::kSizeInBytes == kPointerSize);
+ STATIC_ASSERT(2 * SwVfpRegister::kSizeInBytes == DwVfpRegister::kSizeInBytes);
+
+ int remain = field_count % (DwVfpRegister::kSizeInBytes / kPointerSize);
+ if (remain != 0) {
+ vldr(double_scratch.low(),
+ FieldMemOperand(src, (field_count - 1) * kPointerSize));
+ vstr(double_scratch.low(),
+ FieldMemOperand(dst, (field_count - 1) * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::CopyBytes(Register src,
+ Register dst,
+ Register length,
+ Register scratch) {
+ Label align_loop, align_loop_1, word_loop, byte_loop, byte_loop_1, done;
+
+ // Align src before copying in word size chunks.
+ bind(&align_loop);
+ cmp(length, Operand::Zero());
+ b(eq, &done);
+ bind(&align_loop_1);
+ tst(src, Operand(kPointerSize - 1));
+ b(eq, &word_loop);
+ ldrb(scratch, MemOperand(src, 1, PostIndex));
+ strb(scratch, MemOperand(dst, 1, PostIndex));
+ sub(length, length, Operand(1), SetCC);
+ b(ne, &byte_loop_1);
+
+ // Copy bytes in word size chunks.
+ bind(&word_loop);
+ if (emit_debug_code()) {
+ tst(src, Operand(kPointerSize - 1));
+ Assert(eq, kExpectingAlignmentForCopyBytes);
+ }
+ cmp(length, Operand(kPointerSize));
+ b(lt, &byte_loop);
+ ldr(scratch, MemOperand(src, kPointerSize, PostIndex));
+ if (CpuFeatures::IsSupported(UNALIGNED_ACCESSES)) {
+ str(scratch, MemOperand(dst, kPointerSize, PostIndex));
+ } else {
+ strb(scratch, MemOperand(dst, 1, PostIndex));
+ mov(scratch, Operand(scratch, LSR, 8));
+ strb(scratch, MemOperand(dst, 1, PostIndex));
+ mov(scratch, Operand(scratch, LSR, 8));
+ strb(scratch, MemOperand(dst, 1, PostIndex));
+ mov(scratch, Operand(scratch, LSR, 8));
+ strb(scratch, MemOperand(dst, 1, PostIndex));
+ }
+ sub(length, length, Operand(kPointerSize));
+ b(&word_loop);
+
+ // Copy the last bytes if any left.
+ bind(&byte_loop);
+ cmp(length, Operand::Zero());
+ b(eq, &done);
+ bind(&byte_loop_1);
+ ldrb(scratch, MemOperand(src, 1, PostIndex));
+ strb(scratch, MemOperand(dst, 1, PostIndex));
+ sub(length, length, Operand(1), SetCC);
+ b(ne, &byte_loop_1);
+ bind(&done);
+}
+
+
+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::CheckFor32DRegs(Register scratch) {
+ mov(scratch, Operand(ExternalReference::cpu_features()));
+ ldr(scratch, MemOperand(scratch));
+ tst(scratch, Operand(1u << VFP32DREGS));
+}
+
+
+void MacroAssembler::SaveFPRegs(Register location, Register scratch) {
+ CheckFor32DRegs(scratch);
+ vstm(db_w, location, d16, d31, ne);
+ sub(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
+ vstm(db_w, location, d0, d15);
+}
+
+
+void MacroAssembler::RestoreFPRegs(Register location, Register scratch) {
+ CheckFor32DRegs(scratch);
+ vldm(ia_w, location, d0, d15);
+ vldm(ia_w, location, d16, d31, ne);
+ add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
+}
+
+
+void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* failure) {
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
+ const int kFlatAsciiStringTag =
+ kStringTag | kOneByteStringTag | kSeqStringTag;
+ and_(scratch1, first, Operand(kFlatAsciiStringMask));
+ and_(scratch2, second, Operand(kFlatAsciiStringMask));
+ cmp(scratch1, Operand(kFlatAsciiStringTag));
+ // Ignore second test if first test failed.
+ cmp(scratch2, Operand(kFlatAsciiStringTag), eq);
+ b(ne, failure);
+}
+
+
+void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(Register type,
+ Register scratch,
+ Label* failure) {
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
+ const int kFlatAsciiStringTag =
+ kStringTag | kOneByteStringTag | kSeqStringTag;
+ and_(scratch, type, Operand(kFlatAsciiStringMask));
+ cmp(scratch, Operand(kFlatAsciiStringTag));
+ b(ne, failure);
+}
+
+static const int kRegisterPassedArguments = 4;
+
+
+int MacroAssembler::CalculateStackPassedWords(int num_reg_arguments,
+ int num_double_arguments) {
+ int stack_passed_words = 0;
+ if (use_eabi_hardfloat()) {
+ // In the hard floating point calling convention, we can use
+ // all double registers to pass doubles.
+ if (num_double_arguments > DoubleRegister::NumRegisters()) {
+ stack_passed_words +=
+ 2 * (num_double_arguments - DoubleRegister::NumRegisters());
+ }
+ } else {
+ // In the soft floating point calling convention, every double
+ // argument is passed using two registers.
+ num_reg_arguments += 2 * num_double_arguments;
+ }
+ // Up to four simple arguments are passed in registers r0..r3.
+ if (num_reg_arguments > kRegisterPassedArguments) {
+ stack_passed_words += num_reg_arguments - kRegisterPassedArguments;
+ }
+ return stack_passed_words;
+}
+
+
+void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
+ int num_double_arguments,
+ Register scratch) {
+ int frame_alignment = ActivationFrameAlignment();
+ 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.
+ mov(scratch, sp);
+ sub(sp, sp, Operand((stack_passed_arguments + 1) * kPointerSize));
+ ASSERT(IsPowerOf2(frame_alignment));
+ and_(sp, sp, Operand(-frame_alignment));
+ str(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
+ } else {
+ sub(sp, sp, Operand(stack_passed_arguments * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
+ Register scratch) {
+ PrepareCallCFunction(num_reg_arguments, 0, scratch);
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DwVfpRegister dreg) {
+ if (use_eabi_hardfloat()) {
+ Move(d0, dreg);
+ } else {
+ vmov(r0, r1, dreg);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DwVfpRegister dreg1,
+ DwVfpRegister dreg2) {
+ if (use_eabi_hardfloat()) {
+ if (dreg2.is(d0)) {
+ ASSERT(!dreg1.is(d1));
+ Move(d1, dreg2);
+ Move(d0, dreg1);
+ } else {
+ Move(d0, dreg1);
+ Move(d1, dreg2);
+ }
+ } else {
+ vmov(r0, r1, dreg1);
+ vmov(r2, r3, dreg2);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DwVfpRegister dreg,
+ Register reg) {
+ if (use_eabi_hardfloat()) {
+ Move(d0, dreg);
+ Move(r0, reg);
+ } else {
+ Move(r2, reg);
+ vmov(r0, r1, dreg);
+ }
+}
+
+
+void MacroAssembler::CallCFunction(ExternalReference function,
+ int num_reg_arguments,
+ int num_double_arguments) {
+ mov(ip, Operand(function));
+ CallCFunctionHelper(ip, 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) {
+ CallCFunction(function, num_arguments, 0);
+}
+
+
+void MacroAssembler::CallCFunction(Register function,
+ int num_arguments) {
+ CallCFunction(function, num_arguments, 0);
+}
+
+
+void MacroAssembler::CallCFunctionHelper(Register function,
+ 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.
+#if V8_HOST_ARCH_ARM
+ if (emit_debug_code()) {
+ int frame_alignment = OS::ActivationFrameAlignment();
+ int frame_alignment_mask = frame_alignment - 1;
+ if (frame_alignment > kPointerSize) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ Label alignment_as_expected;
+ tst(sp, Operand(frame_alignment_mask));
+ b(eq, &alignment_as_expected);
+ // Don't use Check here, as it will call Runtime_Abort possibly
+ // re-entering here.
+ stop("Unexpected alignment");
+ bind(&alignment_as_expected);
+ }
+ }
+#endif
+
+ // Just call directly. The function called cannot cause a GC, or
+ // allow preemption, so the return address in the link register
+ // stays correct.
+ Call(function);
+ int stack_passed_arguments = CalculateStackPassedWords(
+ num_reg_arguments, num_double_arguments);
+ if (ActivationFrameAlignment() > kPointerSize) {
+ ldr(sp, MemOperand(sp, stack_passed_arguments * kPointerSize));
+ } else {
+ add(sp, sp, Operand(stack_passed_arguments * sizeof(kPointerSize)));
+ }
+}
+
+
+void MacroAssembler::GetRelocatedValueLocation(Register ldr_location,
+ Register result) {
+ const uint32_t kLdrOffsetMask = (1 << 12) - 1;
+ const int32_t kPCRegOffset = 2 * kPointerSize;
+ ldr(result, MemOperand(ldr_location));
+ if (emit_debug_code()) {
+ // Check that the instruction is a ldr reg, [pc + offset] .
+ and_(result, result, Operand(kLdrPCPattern));
+ cmp(result, Operand(kLdrPCPattern));
+ Check(eq, kTheInstructionToPatchShouldBeALoadFromPc);
+ // Result was clobbered. Restore it.
+ ldr(result, MemOperand(ldr_location));
+ }
+ // Get the address of the constant.
+ and_(result, result, Operand(kLdrOffsetMask));
+ add(result, ldr_location, Operand(result));
+ add(result, result, Operand(kPCRegOffset));
+}
+
+
+void MacroAssembler::CheckPageFlag(
+ Register object,
+ Register scratch,
+ int mask,
+ Condition cc,
+ Label* condition_met) {
+ Bfc(scratch, object, 0, kPageSizeBits);
+ ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset));
+ tst(scratch, Operand(mask));
+ b(cc, condition_met);
+}
+
+
+void MacroAssembler::CheckMapDeprecated(Handle<Map> map,
+ Register scratch,
+ Label* if_deprecated) {
+ if (map->CanBeDeprecated()) {
+ mov(scratch, Operand(map));
+ ldr(scratch, FieldMemOperand(scratch, Map::kBitField3Offset));
+ tst(scratch, Operand(Smi::FromInt(Map::Deprecated::kMask)));
+ b(ne, if_deprecated);
+ }
+}
+
+
+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(kOneByteStringTag == 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));
+}
+
+
+void MacroAssembler::ClampDoubleToUint8(Register result_reg,
+ DwVfpRegister input_reg,
+ LowDwVfpRegister double_scratch) {
+ Label above_zero;
+ Label done;
+ Label in_bounds;
+
+ VFPCompareAndSetFlags(input_reg, 0.0);
+ b(gt, &above_zero);
+
+ // Double value is less than zero, NaN or Inf, return 0.
+ mov(result_reg, Operand::Zero());
+ b(al, &done);
+
+ // Double value is >= 255, return 255.
+ bind(&above_zero);
+ Vmov(double_scratch, 255.0, result_reg);
+ VFPCompareAndSetFlags(input_reg, double_scratch);
+ b(le, &in_bounds);
+ mov(result_reg, Operand(255));
+ b(al, &done);
+
+ // In 0-255 range, round and truncate.
+ bind(&in_bounds);
+ // Save FPSCR.
+ vmrs(ip);
+ // Set rounding mode to round to the nearest integer by clearing bits[23:22].
+ bic(result_reg, ip, Operand(kVFPRoundingModeMask));
+ vmsr(result_reg);
+ vcvt_s32_f64(double_scratch.low(), input_reg, kFPSCRRounding);
+ vmov(result_reg, double_scratch.low());
+ // Restore FPSCR.
+ vmsr(ip);
+ bind(&done);
+}
+
+
+void MacroAssembler::LoadInstanceDescriptors(Register map,
+ Register descriptors) {
+ ldr(descriptors, FieldMemOperand(map, Map::kDescriptorsOffset));
+}
+
+
+void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
+ ldr(dst, FieldMemOperand(map, Map::kBitField3Offset));
+ DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
+}
+
+
+void MacroAssembler::EnumLength(Register dst, Register map) {
+ STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
+ ldr(dst, FieldMemOperand(map, Map::kBitField3Offset));
+ and_(dst, dst, Operand(Smi::FromInt(Map::EnumLengthBits::kMask)));
+}
+
+
+void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
+ Register empty_fixed_array_value = r6;
+ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
+ Label next, start;
+ mov(r2, r0);
+
+ // Check if the enum length field is properly initialized, indicating that
+ // there is an enum cache.
+ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
+
+ EnumLength(r3, r1);
+ cmp(r3, Operand(Smi::FromInt(Map::kInvalidEnumCache)));
+ b(eq, call_runtime);
+
+ jmp(&start);
+
+ bind(&next);
+ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
+
+ // For all objects but the receiver, check that the cache is empty.
+ EnumLength(r3, r1);
+ cmp(r3, Operand(Smi::FromInt(0)));
+ b(ne, call_runtime);
+
+ bind(&start);
+
+ // Check that there are no elements. Register r2 contains the current JS
+ // object we've reached through the prototype chain.
+ ldr(r2, FieldMemOperand(r2, JSObject::kElementsOffset));
+ cmp(r2, empty_fixed_array_value);
+ b(ne, call_runtime);
+
+ ldr(r2, FieldMemOperand(r1, Map::kPrototypeOffset));
+ cmp(r2, null_value);
+ b(ne, &next);
+}
+
+
+void MacroAssembler::TestJSArrayForAllocationMemento(
+ Register receiver_reg,
+ Register scratch_reg) {
+ Label no_memento_available;
+ ExternalReference new_space_start =
+ ExternalReference::new_space_start(isolate());
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+ add(scratch_reg, receiver_reg,
+ Operand(JSArray::kSize + AllocationMemento::kSize - kHeapObjectTag));
+ cmp(scratch_reg, Operand(new_space_start));
+ b(lt, &no_memento_available);
+ mov(ip, Operand(new_space_allocation_top));
+ ldr(ip, MemOperand(ip));
+ cmp(scratch_reg, ip);
+ b(gt, &no_memento_available);
+ ldr(scratch_reg, MemOperand(scratch_reg, -AllocationMemento::kSize));
+ cmp(scratch_reg,
+ Operand(Handle<Map>(isolate()->heap()->allocation_memento_map())));
+ bind(&no_memento_available);
+}
+
+
+#ifdef DEBUG
+bool AreAliased(Register reg1,
+ Register reg2,
+ Register reg3,
+ Register reg4,
+ Register reg5,
+ Register reg6) {
+ int n_of_valid_regs = reg1.is_valid() + reg2.is_valid() +
+ reg3.is_valid() + reg4.is_valid() + reg5.is_valid() + reg6.is_valid();
+
+ RegList regs = 0;
+ if (reg1.is_valid()) regs |= reg1.bit();
+ if (reg2.is_valid()) regs |= reg2.bit();
+ if (reg3.is_valid()) regs |= reg3.bit();
+ if (reg4.is_valid()) regs |= reg4.bit();
+ if (reg5.is_valid()) regs |= reg5.bit();
+ if (reg6.is_valid()) regs |= reg6.bit();
+ int n_of_non_aliasing_regs = NumRegs(regs);
+
+ return n_of_valid_regs != n_of_non_aliasing_regs;
+}
+#endif
+
+
+CodePatcher::CodePatcher(byte* address, int instructions)
+ : address_(address),
+ size_(instructions * Assembler::kInstrSize),
+ masm_(NULL, address, size_ + Assembler::kGap) {
+ // Create a new macro assembler pointing to the address of the code to patch.
+ // The size is adjusted with kGap on order for the assembler to generate size
+ // bytes of instructions without failing with buffer size constraints.
+ ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
+}
+
+
+CodePatcher::~CodePatcher() {
+ // Indicate that code has changed.
+ CPU::FlushICache(address_, size_);
+
+ // Check that the code was patched as expected.
+ ASSERT(masm_.pc_ == address_ + size_);
+ ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
+}
+
+
+void CodePatcher::Emit(Instr instr) {
+ masm()->emit(instr);
+}
+
+
+void CodePatcher::Emit(Address addr) {
+ masm()->emit(reinterpret_cast<Instr>(addr));
+}
+
+
+void CodePatcher::EmitCondition(Condition cond) {
+ Instr instr = Assembler::instr_at(masm_.pc_);
+ instr = (instr & ~kCondMask) | cond;
+ masm_.emit(instr);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/macro-assembler-arm.h b/chromium/v8/src/arm/macro-assembler-arm.h
new file mode 100644
index 00000000000..8b9fa2b2216
--- /dev/null
+++ b/chromium/v8/src/arm/macro-assembler-arm.h
@@ -0,0 +1,1467 @@
+// Copyright 2012 the V8 project authors. 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_ARM_MACRO_ASSEMBLER_ARM_H_
+#define V8_ARM_MACRO_ASSEMBLER_ARM_H_
+
+#include "assembler.h"
+#include "frames.h"
+#include "v8globals.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// Static helper functions
+
+// Generate a MemOperand for loading a field from an object.
+inline MemOperand FieldMemOperand(Register object, int offset) {
+ return MemOperand(object, offset - kHeapObjectTag);
+}
+
+
+// Give alias names to registers
+const Register cp = { 8 }; // JavaScript context pointer
+const Register kRootRegister = { 10 }; // Roots array pointer.
+
+// Flags used for AllocateHeapNumber
+enum TaggingMode {
+ // Tag the result.
+ TAG_RESULT,
+ // Don't tag
+ DONT_TAG_RESULT
+};
+
+
+enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
+enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
+enum LinkRegisterStatus { kLRHasNotBeenSaved, kLRHasBeenSaved };
+
+
+#ifdef DEBUG
+bool AreAliased(Register reg1,
+ Register reg2,
+ Register reg3 = no_reg,
+ Register reg4 = no_reg,
+ Register reg5 = no_reg,
+ Register reg6 = no_reg);
+#endif
+
+
+enum TargetAddressStorageMode {
+ CAN_INLINE_TARGET_ADDRESS,
+ NEVER_INLINE_TARGET_ADDRESS
+};
+
+// MacroAssembler implements a collection of frequently used macros.
+class MacroAssembler: public Assembler {
+ public:
+ // The isolate parameter can be NULL if the macro assembler should
+ // not use isolate-dependent functionality. In this case, it's the
+ // responsibility of the caller to never invoke such function on the
+ // macro assembler.
+ MacroAssembler(Isolate* isolate, void* buffer, int size);
+
+ // Jump, Call, and Ret pseudo instructions implementing inter-working.
+ void Jump(Register target, Condition cond = al);
+ void Jump(Address target, RelocInfo::Mode rmode, Condition cond = al);
+ void Jump(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
+ static int CallSize(Register target, Condition cond = al);
+ void Call(Register target, Condition cond = al);
+ int CallSize(Address target, RelocInfo::Mode rmode, Condition cond = al);
+ static int CallSizeNotPredictableCodeSize(Address target,
+ RelocInfo::Mode rmode,
+ Condition cond = al);
+ void Call(Address target, RelocInfo::Mode rmode,
+ Condition cond = al,
+ TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS);
+ int CallSize(Handle<Code> code,
+ RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
+ TypeFeedbackId ast_id = TypeFeedbackId::None(),
+ Condition cond = al);
+ void Call(Handle<Code> code,
+ RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
+ TypeFeedbackId ast_id = TypeFeedbackId::None(),
+ Condition cond = al,
+ TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS);
+ void Ret(Condition cond = al);
+
+ // Emit code to discard a non-negative number of pointer-sized elements
+ // from the stack, clobbering only the sp register.
+ void Drop(int count, Condition cond = al);
+
+ void Ret(int drop, Condition cond = al);
+
+ // Swap two registers. If the scratch register is omitted then a slightly
+ // less efficient form using xor instead of mov is emitted.
+ void Swap(Register reg1,
+ Register reg2,
+ Register scratch = no_reg,
+ Condition cond = al);
+
+
+ void And(Register dst, Register src1, const Operand& src2,
+ Condition cond = al);
+ void Ubfx(Register dst, Register src, int lsb, int width,
+ Condition cond = al);
+ void Sbfx(Register dst, Register src, int lsb, int width,
+ Condition cond = al);
+ // The scratch register is not used for ARMv7.
+ // scratch can be the same register as src (in which case it is trashed), but
+ // not the same as dst.
+ void Bfi(Register dst,
+ Register src,
+ Register scratch,
+ int lsb,
+ int width,
+ Condition cond = al);
+ void Bfc(Register dst, Register src, int lsb, int width, Condition cond = al);
+ void Usat(Register dst, int satpos, const Operand& src,
+ Condition cond = al);
+
+ void Call(Label* target);
+ void Push(Register src) { push(src); }
+ void Pop(Register dst) { pop(dst); }
+
+ // Register move. May do nothing if the registers are identical.
+ void Move(Register dst, Handle<Object> value);
+ void Move(Register dst, Register src, Condition cond = al);
+ void Move(DwVfpRegister dst, DwVfpRegister src);
+
+ // Load an object from the root table.
+ void LoadRoot(Register destination,
+ Heap::RootListIndex index,
+ Condition cond = al);
+ // Store an object to the root table.
+ void StoreRoot(Register source,
+ Heap::RootListIndex index,
+ Condition cond = al);
+
+ void LoadHeapObject(Register dst, Handle<HeapObject> object);
+
+ void LoadObject(Register result, Handle<Object> object) {
+ AllowDeferredHandleDereference heap_object_check;
+ 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);
+
+ void CheckMapDeprecated(Handle<Map> map,
+ Register scratch,
+ Label* if_deprecated);
+
+ // 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);
+ }
+
+ // 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);
+
+ void JumpIfBlack(Register object,
+ Register scratch0,
+ 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 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);
+ void Push(Smi* smi) { Push(Handle<Smi>(smi, isolate())); }
+
+ // Push two registers. Pushes leftmost register first (to highest address).
+ void Push(Register src1, Register src2, Condition cond = al) {
+ ASSERT(!src1.is(src2));
+ if (src1.code() > src2.code()) {
+ stm(db_w, sp, src1.bit() | src2.bit(), cond);
+ } else {
+ str(src1, MemOperand(sp, 4, NegPreIndex), cond);
+ str(src2, MemOperand(sp, 4, NegPreIndex), cond);
+ }
+ }
+
+ // Push three registers. Pushes leftmost register first (to highest address).
+ void Push(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()) {
+ stm(db_w, sp, src1.bit() | src2.bit() | src3.bit(), cond);
+ } else {
+ stm(db_w, sp, src1.bit() | src2.bit(), cond);
+ str(src3, MemOperand(sp, 4, NegPreIndex), cond);
+ }
+ } else {
+ str(src1, MemOperand(sp, 4, NegPreIndex), cond);
+ Push(src2, src3, cond);
+ }
+ }
+
+ // Push four registers. Pushes leftmost register first (to highest address).
+ 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));
+ 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()) {
+ stm(db_w,
+ sp,
+ src1.bit() | src2.bit() | src3.bit() | src4.bit(),
+ cond);
+ } else {
+ stm(db_w, sp, src1.bit() | src2.bit() | src3.bit(), cond);
+ str(src4, MemOperand(sp, 4, NegPreIndex), cond);
+ }
+ } else {
+ stm(db_w, sp, src1.bit() | src2.bit(), cond);
+ Push(src3, src4, cond);
+ }
+ } else {
+ str(src1, MemOperand(sp, 4, NegPreIndex), cond);
+ Push(src2, src3, src4, cond);
+ }
+ }
+
+ // Pop two registers. Pops rightmost register first (from lower address).
+ void Pop(Register src1, Register src2, Condition cond = al) {
+ ASSERT(!src1.is(src2));
+ if (src1.code() > src2.code()) {
+ ldm(ia_w, sp, src1.bit() | src2.bit(), cond);
+ } else {
+ ldr(src2, MemOperand(sp, 4, PostIndex), cond);
+ ldr(src1, MemOperand(sp, 4, PostIndex), cond);
+ }
+ }
+
+ // 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();
+ void PopSafepointRegisters();
+ void PushSafepointRegistersAndDoubles();
+ void PopSafepointRegistersAndDoubles();
+ // Store value in register src in the safepoint stack slot for
+ // register dst.
+ void StoreToSafepointRegisterSlot(Register src, Register dst);
+ void StoreToSafepointRegistersAndDoublesSlot(Register src, Register dst);
+ // Load the value of the src register from its safepoint stack slot
+ // into register dst.
+ void LoadFromSafepointRegisterSlot(Register dst, Register src);
+
+ // Load two consecutive registers with two consecutive memory locations.
+ void Ldrd(Register dst1,
+ Register dst2,
+ const MemOperand& src,
+ Condition cond = al);
+
+ // Store two consecutive registers to two consecutive memory locations.
+ void Strd(Register src1,
+ Register src2,
+ const MemOperand& dst,
+ Condition cond = al);
+
+ // Ensure that FPSCR contains values needed by JavaScript.
+ // We need the NaNModeControlBit to be sure that operations like
+ // vadd and vsub generate the Canonical NaN (if a NaN must be generated).
+ // In VFP3 it will be always the Canonical NaN.
+ // In VFP2 it will be either the Canonical NaN or the negative version
+ // of the Canonical NaN. It doesn't matter if we have two values. The aim
+ // is to be sure to never generate the hole NaN.
+ void VFPEnsureFPSCRState(Register scratch);
+
+ // If the value is a NaN, canonicalize the value else, do nothing.
+ void VFPCanonicalizeNaN(const DwVfpRegister value,
+ const Condition cond = al);
+
+ // Compare double values and move the result to the normal condition flags.
+ void VFPCompareAndSetFlags(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Condition cond = al);
+ void VFPCompareAndSetFlags(const DwVfpRegister src1,
+ const double src2,
+ const Condition cond = al);
+
+ // Compare double values and then load the fpscr flags to a register.
+ void VFPCompareAndLoadFlags(const DwVfpRegister src1,
+ const DwVfpRegister src2,
+ const Register fpscr_flags,
+ const Condition cond = al);
+ void VFPCompareAndLoadFlags(const DwVfpRegister src1,
+ const double src2,
+ const Register fpscr_flags,
+ const Condition cond = al);
+
+ void Vmov(const DwVfpRegister dst,
+ const double imm,
+ const Register scratch = no_reg);
+
+ void VmovHigh(Register dst, DwVfpRegister src);
+ void VmovHigh(DwVfpRegister dst, Register src);
+ void VmovLow(Register dst, DwVfpRegister src);
+ void VmovLow(DwVfpRegister dst, Register src);
+
+ // Converts the smi or heap number in object to an int32 using the rules
+ // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
+ // and brought into the range -2^31 .. +2^31 - 1.
+ void ConvertNumberToInt32(Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch1,
+ LowDwVfpRegister double_scratch2,
+ Label* not_int32);
+
+ // Loads the number from object into dst register.
+ // If |object| is neither smi nor heap number, |not_number| is jumped to
+ // with |object| still intact.
+ void LoadNumber(Register object,
+ LowDwVfpRegister dst,
+ Register heap_number_map,
+ Register scratch,
+ Label* not_number);
+
+ // Loads the number from object into double_dst in the double format.
+ // Control will jump to not_int32 if the value cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be loaded.
+ void LoadNumberAsInt32Double(Register object,
+ DwVfpRegister double_dst,
+ Register heap_number_map,
+ Register scratch,
+ LowDwVfpRegister double_scratch,
+ Label* not_int32);
+
+ // Loads the number from object into dst as a 32-bit integer.
+ // Control will jump to not_int32 if the object cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be converted.
+ void LoadNumberAsInt32(Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch,
+ DwVfpRegister double_scratch0,
+ LowDwVfpRegister double_scratch1,
+ Label* not_int32);
+
+
+ // Enter exit frame.
+ // stack_space - extra stack space, used for alignment before call to C.
+ void EnterExitFrame(bool save_doubles, int stack_space = 0);
+
+ // Leave the current exit frame. Expects the return value in r0.
+ // Expect the number of values, pushed prior to the exit frame, to
+ // remove in a register (or no_reg, if there is nothing to remove).
+ void LeaveExitFrame(bool save_doubles, Register argument_count);
+
+ // Get the actual activation frame alignment for target environment.
+ static int ActivationFrameAlignment();
+
+ void LoadContext(Register dst, int context_chain_length);
+
+ // Conditionally load the cached Array transitioned map of type
+ // transitioned_kind from the native context if the map in register
+ // map_in_out is the cached Array map in the native 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,
+ bool can_have_holes);
+
+ void LoadGlobalFunction(int index, Register function);
+ void LoadArrayFunction(Register function);
+
+ // Load the initial map from the global function. The registers
+ // function and map can be the same, function is then overwritten.
+ void LoadGlobalFunctionInitialMap(Register function,
+ Register map,
+ Register scratch);
+
+ void InitializeRootRegister() {
+ ExternalReference roots_array_start =
+ ExternalReference::roots_array_start(isolate());
+ mov(kRootRegister, Operand(roots_array_start));
+ }
+
+ // ---------------------------------------------------------------------------
+ // JavaScript invokes
+
+ // 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);
+
+ void InvokeCode(Handle<Code> code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ RelocInfo::Mode rmode,
+ InvokeFlag flag,
+ CallKind call_kind);
+
+ // Invoke the JavaScript function in the given register. Changes the
+ // current context to the context in the function before invoking.
+ void InvokeFunction(Register function,
+ const ParameterCount& actual,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ void InvokeFunction(Handle<JSFunction> function,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ void IsObjectJSObjectType(Register heap_object,
+ Register map,
+ Register scratch,
+ Label* fail);
+
+ void IsInstanceJSObjectType(Register map,
+ Register scratch,
+ Label* fail);
+
+ void IsObjectJSStringType(Register object,
+ Register scratch,
+ Label* fail);
+
+ void IsObjectNameType(Register object,
+ Register scratch,
+ Label* fail);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // ---------------------------------------------------------------------------
+ // Debugger Support
+
+ void DebugBreak();
+#endif
+
+ // ---------------------------------------------------------------------------
+ // Exception handling
+
+ // Push a new try handler and link 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.
+ // Must preserve the result register.
+ void PopTryHandler();
+
+ // Passes thrown value to the handler of top of the try handler chain.
+ void Throw(Register value);
+
+ // Propagates an uncatchable exception to the top of the current JS stack's
+ // handler chain.
+ void ThrowUncatchable(Register value);
+
+ // ---------------------------------------------------------------------------
+ // Inline caching support
+
+ // Generate code for checking access rights - used for security checks
+ // on access to global objects across environments. The holder register
+ // is left untouched, whereas both scratch registers are clobbered.
+ void CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss);
+
+ void GetNumberHash(Register t0, Register scratch);
+
+ void LoadFromNumberDictionary(Label* miss,
+ Register elements,
+ Register key,
+ Register result,
+ Register t0,
+ Register t1,
+ Register t2);
+
+
+ inline void MarkCode(NopMarkerTypes type) {
+ nop(type);
+ }
+
+ // Check if the given instruction is a 'type' marker.
+ // 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) {
+ ASSERT((FIRST_IC_MARKER <= type) && (type < LAST_CODE_MARKER));
+ return IsNop(instr, type);
+ }
+
+
+ static inline int GetCodeMarker(Instr instr) {
+ int dst_reg_offset = 12;
+ int dst_mask = 0xf << dst_reg_offset;
+ int src_mask = 0xf;
+ int dst_reg = (instr & dst_mask) >> dst_reg_offset;
+ int src_reg = instr & src_mask;
+ uint32_t non_register_mask = ~(dst_mask | src_mask);
+ uint32_t mov_mask = al | 13 << 21;
+
+ // Return <n> if we have a mov rn rn, else return -1.
+ int type = ((instr & non_register_mask) == mov_mask) &&
+ (dst_reg == src_reg) &&
+ (FIRST_IC_MARKER <= dst_reg) && (dst_reg < LAST_CODE_MARKER)
+ ? src_reg
+ : -1;
+ ASSERT((type == -1) ||
+ ((FIRST_IC_MARKER <= type) && (type < LAST_CODE_MARKER)));
+ return type;
+ }
+
+
+ // ---------------------------------------------------------------------------
+ // Allocation support
+
+ // Allocate an object in new space or old pointer space. The object_size is
+ // specified either in bytes or in words if the allocation flag SIZE_IN_WORDS
+ // is passed. If the space is exhausted control continues at the gc_required
+ // label. The allocated object is returned in result. If the flag
+ // tag_allocated_object is true the result is tagged as as a heap object.
+ // All registers are clobbered also when control continues at the gc_required
+ // label.
+ void Allocate(int object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ void Allocate(Register object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ // Undo allocation in new space. The object passed and objects allocated after
+ // it will no longer be allocated. The caller must make sure that no pointers
+ // are left to the object(s) no longer allocated as they would be invalid when
+ // allocation is undone.
+ void UndoAllocationInNewSpace(Register object, Register scratch);
+
+
+ void AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateTwoByteConsString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiConsString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateTwoByteSlicedString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiSlicedString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // Allocates a heap number or jumps to the gc_required label if the young
+ // space is full and a scavenge is needed. All registers are clobbered also
+ // when control continues at the gc_required label.
+ void AllocateHeapNumber(Register result,
+ Register scratch1,
+ Register scratch2,
+ Register heap_number_map,
+ Label* gc_required,
+ TaggingMode tagging_mode = TAG_RESULT);
+ void AllocateHeapNumberWithValue(Register result,
+ DwVfpRegister value,
+ Register scratch1,
+ Register scratch2,
+ Register heap_number_map,
+ Label* gc_required);
+
+ // Copies a fixed number of fields of heap objects from src to dst.
+ void CopyFields(Register dst,
+ Register src,
+ LowDwVfpRegister double_scratch,
+ int field_count);
+
+ // Copies a number of bytes from src to dst. All registers are clobbered. On
+ // exit src and dst will point to the place just after where the last byte was
+ // read or written and length will be zero.
+ void CopyBytes(Register src,
+ Register dst,
+ 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.
+
+ // Try to get function prototype of a function and puts the value in
+ // the result register. Checks that the function really is a
+ // function and jumps to the miss label if the fast checks fail. The
+ // function register will be untouched; the other registers may be
+ // clobbered.
+ void TryGetFunctionPrototype(Register function,
+ Register result,
+ Register scratch,
+ 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
+ // sets the flags and leaves the object type in the type_reg register.
+ // It leaves the map in the map register (unless the type_reg and map register
+ // are the same register). It leaves the heap object in the heap_object
+ // register unless the heap_object register is the same register as one of the
+ // other registers.
+ void CompareObjectType(Register heap_object,
+ Register map,
+ Register type_reg,
+ InstanceType type);
+
+ // Compare instance type in a map. map contains a valid map object whose
+ // object type should be compared with the given type. This both
+ // sets the flags and leaves the object type in the type_reg register.
+ void CompareInstanceType(Register map,
+ Register type_reg,
+ InstanceType type);
+
+
+ // Check if a map for a JSObject indicates that the object has fast elements.
+ // Jump to the specified label if it does not.
+ void CheckFastElements(Register map,
+ Register scratch,
+ Label* fail);
+
+ // 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 CheckFastSmiElements(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 elements_reg,
+ Register scratch1,
+ LowDwVfpRegister double_scratch,
+ Label* fail,
+ int elements_offset = 0);
+
+ // 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);
+
+ // As above, but the map of the object is already loaded into the register
+ // which is preserved by the code generated.
+ void CompareMap(Register obj_map,
+ Handle<Map> map,
+ Label* early_success);
+
+ // 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);
+
+
+ void CheckMap(Register obj,
+ Register scratch,
+ Heap::RootListIndex index,
+ Label* fail,
+ SmiCheckType smi_check_type);
+
+
+ // 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
+ // known to be a heap object)
+ void DispatchMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type);
+
+
+ // Compare the object in a register to a value from the root list.
+ // Uses the ip register as scratch.
+ void CompareRoot(Register obj, Heap::RootListIndex index);
+
+
+ // 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
+ // and the passed-in condition passed. If the passed-in condition failed
+ // then flags remain unchanged.
+ Condition IsObjectStringType(Register obj,
+ Register type,
+ Condition cond = al) {
+ ldr(type, FieldMemOperand(obj, HeapObject::kMapOffset), cond);
+ ldrb(type, FieldMemOperand(type, Map::kInstanceTypeOffset), cond);
+ tst(type, Operand(kIsNotStringMask), cond);
+ ASSERT_EQ(0, kStringTag);
+ return eq;
+ }
+
+
+ // Generates code for reporting that an illegal operation has
+ // occurred.
+ void IllegalOperation(int num_arguments);
+
+ // Picks out an array index from the hash field.
+ // Register use:
+ // hash - holds the index's hash. Clobbered.
+ // index - holds the overwritten index on exit.
+ void IndexFromHash(Register hash, Register index);
+
+ // Get the number of least significant bits from a register
+ void GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits);
+ void GetLeastBitsFromInt32(Register dst, Register src, int mun_least_bits);
+
+ // Load the value of a smi object into a double register.
+ // The register value must be between d0 and d15.
+ void SmiToDouble(LowDwVfpRegister value, Register smi);
+
+ // Check if a double can be exactly represented as a signed 32-bit integer.
+ // Z flag set to one if true.
+ void TestDoubleIsInt32(DwVfpRegister double_input,
+ LowDwVfpRegister double_scratch);
+
+ // Try to convert a double to a signed 32-bit integer.
+ // Z flag set to one and result assigned if the conversion is exact.
+ void TryDoubleToInt32Exact(Register result,
+ DwVfpRegister double_input,
+ LowDwVfpRegister double_scratch);
+
+ // Floor a double and writes the value to the result register.
+ // Go to exact if the conversion is exact (to be able to test -0),
+ // fall through calling code if an overflow occurred, else go to done.
+ // In return, input_high is loaded with high bits of input.
+ void TryInt32Floor(Register result,
+ DwVfpRegister double_input,
+ Register input_high,
+ LowDwVfpRegister double_scratch,
+ Label* done,
+ Label* exact);
+
+ // Performs a truncating conversion of a floating point number as used by
+ // the JS bitwise operations. See ECMA-262 9.5: ToInt32.
+ // Double_scratch must be between d0 and d15.
+ // Exits with 'result' holding the answer and all other registers clobbered.
+ void ECMAToInt32(Register result,
+ DwVfpRegister double_input,
+ Register scratch,
+ Register scratch_high,
+ Register scratch_low,
+ LowDwVfpRegister double_scratch);
+
+ // Check whether d16-d31 are available on the CPU. The result is given by the
+ // Z condition flag: Z==0 if d16-d31 available, Z==1 otherwise.
+ void CheckFor32DRegs(Register scratch);
+
+ // Does a runtime check for 16/32 FP registers. Either way, pushes 32 double
+ // values to location, saving [d0..(d15|d31)].
+ void SaveFPRegs(Register location, Register scratch);
+
+ // Does a runtime check for 16/32 FP registers. Either way, pops 32 double
+ // values to location, restoring [d0..(d15|d31)].
+ void RestoreFPRegs(Register location, Register scratch);
+
+ // ---------------------------------------------------------------------------
+ // Runtime calls
+
+ // Call a code stub.
+ void CallStub(CodeStub* stub,
+ TypeFeedbackId ast_id = TypeFeedbackId::None(),
+ Condition cond = al);
+
+ // Call a code stub.
+ void TailCallStub(CodeStub* stub, Condition cond = al);
+
+ // Call a runtime routine.
+ void CallRuntime(const Runtime::Function* f, int num_arguments);
+ void CallRuntimeSaveDoubles(Runtime::FunctionId id);
+
+ // Convenience function: Same as above, but takes the fid instead.
+ void CallRuntime(Runtime::FunctionId fid, int num_arguments);
+
+ // Convenience function: call an external reference.
+ void CallExternalReference(const ExternalReference& ext,
+ int num_arguments);
+
+ // Tail call of a runtime routine (jump).
+ // Like JumpToExternalReference, but also takes care of passing the number
+ // of parameters.
+ void TailCallExternalReference(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.
+ // After aligning the frame, non-register arguments must be stored in
+ // sp[0], sp[4], etc., not pushed. The argument count assumes all arguments
+ // are word sized. If double arguments are used, this function assumes that
+ // all double arguments are stored before core registers; otherwise the
+ // correct alignment of the double values is not guaranteed.
+ // Some compilers/platforms require the stack to be aligned when calling
+ // C++ code.
+ // Needs a scratch register to do some arithmetic. This register will be
+ // trashed.
+ void PrepareCallCFunction(int num_reg_arguments,
+ int num_double_registers,
+ Register scratch);
+ void PrepareCallCFunction(int num_reg_arguments,
+ Register scratch);
+
+ // There are two ways of passing double arguments on ARM, depending on
+ // whether soft or hard floating point ABI is used. These functions
+ // abstract parameter passing for the three different ways we call
+ // C functions from generated code.
+ void SetCallCDoubleArguments(DwVfpRegister dreg);
+ void SetCallCDoubleArguments(DwVfpRegister dreg1, DwVfpRegister dreg2);
+ void SetCallCDoubleArguments(DwVfpRegister dreg, Register reg);
+
+ // Calls a C function and cleans up the space for arguments allocated
+ // by PrepareCallCFunction. The called function is not allowed to trigger a
+ // garbage collection, since that might move the code and invalidate the
+ // return address (unless this is somehow accounted for by the called
+ // function).
+ void CallCFunction(ExternalReference function, 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 DwVfpRegister 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).
+ void CallApiFunctionAndReturn(ExternalReference function,
+ Address function_address,
+ ExternalReference thunk_ref,
+ Register thunk_last_arg,
+ int stack_space,
+ bool returns_handle,
+ int return_value_offset_from_fp);
+
+ // Jump to a runtime routine.
+ void JumpToExternalReference(const ExternalReference& builtin);
+
+ // Invoke specified builtin JavaScript function. Adds an entry to
+ // the unresolved list if the name does not resolve.
+ void InvokeBuiltin(Builtins::JavaScript id,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper = NullCallWrapper());
+
+ // Store the code object for the given builtin in the target register and
+ // setup the function in r1.
+ void GetBuiltinEntry(Register target, Builtins::JavaScript id);
+
+ // Store the function for the given builtin in the target register.
+ void GetBuiltinFunction(Register target, Builtins::JavaScript id);
+
+ Handle<Object> CodeObject() {
+ ASSERT(!code_object_.is_null());
+ return code_object_;
+ }
+
+
+ // ---------------------------------------------------------------------------
+ // StatsCounter support
+
+ void SetCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2);
+ void IncrementCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2);
+ void DecrementCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2);
+
+
+ // ---------------------------------------------------------------------------
+ // Debugging
+
+ // Calls Abort(msg) if the condition cond is not satisfied.
+ // Use --debug_code to enable.
+ void Assert(Condition cond, BailoutReason reason);
+ void AssertFastElements(Register elements);
+
+ // Like Assert(), but always enabled.
+ void Check(Condition cond, BailoutReason reason);
+
+ // Print a message to stdout and abort execution.
+ void Abort(BailoutReason msg);
+
+ // Verify restrictions about code generated in stubs.
+ void set_generating_stub(bool value) { generating_stub_ = value; }
+ 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() {
+#ifdef __arm__
+ return OS::ArmUsingHardFloat();
+#elif USE_EABI_HARDFLOAT
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ // ---------------------------------------------------------------------------
+ // Number utilities
+
+ // Check whether the value of reg is a power of two and not zero. If not
+ // control continues at the label not_power_of_two. If reg is a power of two
+ // the register scratch contains the value of (reg - 1) when control falls
+ // through.
+ void JumpIfNotPowerOfTwoOrZero(Register reg,
+ Register scratch,
+ Label* not_power_of_two_or_zero);
+ // Check whether the value of reg is a power of two and not zero.
+ // Control falls through if it is, with scratch containing the mask
+ // value (reg - 1).
+ // Otherwise control jumps to the 'zero_and_neg' label if the value of reg is
+ // zero or negative, or jumps to the 'not_power_of_two' label if the value is
+ // strictly positive but not a power of two.
+ void JumpIfNotPowerOfTwoOrZeroAndNeg(Register reg,
+ Register scratch,
+ Label* zero_and_neg,
+ Label* not_power_of_two);
+
+ // ---------------------------------------------------------------------------
+ // Smi utilities
+
+ void SmiTag(Register reg, SBit s = LeaveCC) {
+ add(reg, reg, Operand(reg), s);
+ }
+ void SmiTag(Register dst, Register src, SBit s = LeaveCC) {
+ add(dst, src, Operand(src), s);
+ }
+
+ // 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.
+ void TrySmiTag(Register reg, Label* not_a_smi) {
+ TrySmiTag(reg, reg, not_a_smi);
+ }
+ void TrySmiTag(Register reg, Register src, Label* not_a_smi) {
+ SmiTag(ip, src, SetCC);
+ b(vs, not_a_smi);
+ mov(reg, ip);
+ }
+
+
+ void SmiUntag(Register reg, SBit s = LeaveCC) {
+ mov(reg, Operand::SmiUntag(reg), s);
+ }
+ void SmiUntag(Register dst, Register src, SBit s = LeaveCC) {
+ mov(dst, Operand::SmiUntag(src), 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);
+
+ // Test if the register contains a smi (Z == 0 (eq) if true).
+ inline void SmiTst(Register value) {
+ tst(value, Operand(kSmiTagMask));
+ }
+ inline void NonNegativeSmiTst(Register value) {
+ tst(value, Operand(kSmiTagMask | kSmiSignMask));
+ }
+ // Jump if the register contains a smi.
+ inline void JumpIfSmi(Register value, Label* smi_label) {
+ tst(value, Operand(kSmiTagMask));
+ b(eq, smi_label);
+ }
+ // Jump if either of the registers contain a non-smi.
+ inline void JumpIfNotSmi(Register value, Label* not_smi_label) {
+ tst(value, Operand(kSmiTagMask));
+ b(ne, not_smi_label);
+ }
+ // Jump if either of the registers contain a non-smi.
+ void JumpIfNotBothSmi(Register reg1, Register reg2, Label* on_not_both_smi);
+ // Jump if either of the registers contain a smi.
+ void JumpIfEitherSmi(Register reg1, Register reg2, Label* on_either_smi);
+
+ // Abort execution if argument is a smi, enabled via --debug-code.
+ void AssertNotSmi(Register object);
+ void AssertSmi(Register object);
+
+ // Abort execution if argument is not a string, enabled via --debug-code.
+ void AssertString(Register object);
+
+ // Abort execution if argument is not a name, enabled via --debug-code.
+ void AssertName(Register object);
+
+ // Abort execution if reg is not the root value with the given index,
+ // enabled via --debug-code.
+ void AssertIsRoot(Register reg, Heap::RootListIndex index);
+
+ // ---------------------------------------------------------------------------
+ // HeapNumber utilities
+
+ void JumpIfNotHeapNumber(Register object,
+ Register heap_number_map,
+ Register scratch,
+ Label* on_not_heap_number);
+
+ // ---------------------------------------------------------------------------
+ // String utilities
+
+ // Checks if both objects are sequential ASCII strings and jumps to label
+ // if either is not. Assumes that neither object is a smi.
+ void JumpIfNonSmisNotBothSequentialAsciiStrings(Register object1,
+ Register object2,
+ Register scratch1,
+ Register scratch2,
+ Label* failure);
+
+ // Checks if both objects are sequential ASCII strings and jumps to label
+ // if either is not.
+ void JumpIfNotBothSequentialAsciiStrings(Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* not_flat_ascii_strings);
+
+ // Checks if both instance types are sequential ASCII strings and jumps to
+ // label if either is not.
+ void JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first_object_instance_type,
+ Register second_object_instance_type,
+ Register scratch1,
+ Register scratch2,
+ Label* failure);
+
+ // Check if instance type is sequential ASCII string and jump to label if
+ // it is not.
+ void JumpIfInstanceTypeIsNotSequentialAscii(Register type,
+ Register scratch,
+ Label* failure);
+
+ void JumpIfNotUniqueName(Register reg, Label* not_unique_name);
+
+ // ---------------------------------------------------------------------------
+ // Patching helpers.
+
+ // Get the location of a relocated constant (its address in the constant pool)
+ // from its load site.
+ void GetRelocatedValueLocation(Register ldr_location,
+ Register result);
+
+
+ void ClampUint8(Register output_reg, Register input_reg);
+
+ void ClampDoubleToUint8(Register result_reg,
+ DwVfpRegister input_reg,
+ LowDwVfpRegister double_scratch);
+
+
+ void LoadInstanceDescriptors(Register map, Register descriptors);
+ void EnumLength(Register dst, Register map);
+ void NumberOfOwnDescriptors(Register dst, Register map);
+
+ template<typename Field>
+ void DecodeField(Register reg) {
+ static const int shift = Field::kShift;
+ static const int mask = (Field::kMask >> shift) << kSmiTagSize;
+ mov(reg, Operand(reg, LSR, shift));
+ and_(reg, reg, Operand(mask));
+ }
+
+ // Activation support.
+ void EnterFrame(StackFrame::Type type);
+ void LeaveFrame(StackFrame::Type type);
+
+ // Expects object in r0 and returns map with validated enum cache
+ // in r0. Assumes that any other register can be used as a scratch.
+ void CheckEnumCache(Register null_value, Label* call_runtime);
+
+ // AllocationMemento support. Arrays may have an associated
+ // AllocationMemento object that can be checked for in order to pretransition
+ // to another type.
+ // On entry, receiver_reg should point to the array object.
+ // scratch_reg gets clobbered.
+ // If allocation info is present, condition flags are set to eq
+ void TestJSArrayForAllocationMemento(Register receiver_reg,
+ Register scratch_reg);
+
+ private:
+ void CallCFunctionHelper(Register function,
+ int num_reg_arguments,
+ int num_double_arguments);
+
+ void Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
+
+ // Helper functions for generating invokes.
+ void InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ Handle<Code> code_constant,
+ Register code_reg,
+ Label* done,
+ bool* definitely_mismatches,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ 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 generating_stub_;
+ bool allow_stub_calls_;
+ bool has_frame_;
+ // This handle will be patched with the code object on installation.
+ Handle<Object> code_object_;
+
+ // Needs access to SafepointRegisterStackIndex for compiled frame
+ // traversal.
+ friend class StandardFrame;
+};
+
+
+// The code patcher is used to patch (typically) small parts of code e.g. for
+// debugging and other types of instrumentation. When using the code patcher
+// the exact number of bytes specified must be emitted. It is not legal to emit
+// relocation information. If any of these constraints are violated it causes
+// an assertion to fail.
+class CodePatcher {
+ public:
+ CodePatcher(byte* address, int instructions);
+ virtual ~CodePatcher();
+
+ // Macro assembler to emit code.
+ MacroAssembler* masm() { return &masm_; }
+
+ // Emit an instruction directly.
+ void Emit(Instr instr);
+
+ // Emit an address directly.
+ void Emit(Address addr);
+
+ // Emit the condition part of an instruction leaving the rest of the current
+ // instruction unchanged.
+ void EmitCondition(Condition cond);
+
+ private:
+ byte* address_; // The address of the code being patched.
+ int size_; // Number of bytes of the expected patch size.
+ MacroAssembler masm_; // Macro assembler used to generate the code.
+};
+
+
+// -----------------------------------------------------------------------------
+// Static helper functions.
+
+inline MemOperand ContextOperand(Register context, int index) {
+ return MemOperand(context, Context::SlotOffset(index));
+}
+
+
+inline MemOperand GlobalObjectOperand() {
+ return ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX);
+}
+
+
+#ifdef GENERATED_CODE_COVERAGE
+#define CODE_COVERAGE_STRINGIFY(x) #x
+#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
+#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
+#define ACCESS_MASM(masm) masm->stop(__FILE_LINE__); masm->
+#else
+#define ACCESS_MASM(masm) masm->
+#endif
+
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_MACRO_ASSEMBLER_ARM_H_
diff --git a/chromium/v8/src/arm/regexp-macro-assembler-arm.cc b/chromium/v8/src/arm/regexp-macro-assembler-arm.cc
new file mode 100644
index 00000000000..189ea8d7779
--- /dev/null
+++ b/chromium/v8/src/arm/regexp-macro-assembler-arm.cc
@@ -0,0 +1,1371 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "cpu-profiler.h"
+#include "unicode.h"
+#include "log.h"
+#include "code-stubs.h"
+#include "regexp-stack.h"
+#include "macro-assembler.h"
+#include "regexp-macro-assembler.h"
+#include "arm/regexp-macro-assembler-arm.h"
+
+namespace v8 {
+namespace internal {
+
+#ifndef V8_INTERPRETED_REGEXP
+/*
+ * This assembler uses the following register assignment convention
+ * - r4 : Temporarily stores the index of capture start after a matching pass
+ * for a global regexp.
+ * - r5 : Pointer to current code object (Code*) including heap object tag.
+ * - r6 : Current position in input, as negative offset from end of string.
+ * Please notice that this is the byte offset, not the character offset!
+ * - r7 : Currently loaded character. Must be loaded using
+ * LoadCurrentCharacter before using any of the dispatch methods.
+ * - r8 : Points to tip of backtrack stack
+ * - r9 : Unused, might be used by C code and expected unchanged.
+ * - r10 : End of input (points to byte after last character in input).
+ * - r11 : Frame pointer. Used to access arguments, local variables and
+ * RegExp registers.
+ * - r12 : IP register, used by assembler. Very volatile.
+ * - r13/sp : Points to tip of C stack.
+ *
+ * The remaining registers are free for computations.
+ * Each call to a public method should retain this convention.
+ *
+ * The stack will have the following structure:
+ * - fp[56] Isolate* isolate (address of the current isolate)
+ * - fp[52] direct_call (if 1, direct call from JavaScript code,
+ * if 0, call through the runtime system).
+ * - fp[48] stack_area_base (high end of the memory area to use as
+ * backtracking stack).
+ * - fp[44] capture array size (may fit multiple sets of matches)
+ * - fp[40] int* capture_array (int[num_saved_registers_], for output).
+ * - fp[36] secondary link/return address used by native call.
+ * --- sp when called ---
+ * - fp[32] return address (lr).
+ * - fp[28] old frame pointer (r11).
+ * - fp[0..24] backup of registers r4..r10.
+ * --- frame pointer ----
+ * - fp[-4] end of input (address of end of string).
+ * - fp[-8] start of input (address of first character in string).
+ * - fp[-12] start index (character index of start).
+ * - fp[-16] void* input_string (location of a handle containing the string).
+ * - fp[-20] success counter (only for global regexps to count matches).
+ * - fp[-24] Offset of location before start of input (effectively character
+ * position -1). Used to initialize capture registers to a
+ * non-position.
+ * - fp[-28] At start (if 1, we are starting at the start of the
+ * string, otherwise 0)
+ * - fp[-32] register 0 (Only positions must be stored in the first
+ * - register 1 num_saved_registers_ registers)
+ * - ...
+ * - register num_registers-1
+ * --- sp ---
+ *
+ * The first num_saved_registers_ registers are initialized to point to
+ * "character -1" in the string (i.e., char_size() bytes before the first
+ * character of the string). The remaining registers start out as garbage.
+ *
+ * The data up to the return address must be placed there by the calling
+ * code and the remaining arguments are passed in registers, e.g. by calling the
+ * code entry as cast to a function with the signature:
+ * int (*match)(String* input_string,
+ * int start_index,
+ * Address start,
+ * Address end,
+ * Address secondary_return_address, // Only used by native call.
+ * int* capture_output_array,
+ * byte* stack_area_base,
+ * bool direct_call = false)
+ * The call is performed by NativeRegExpMacroAssembler::Execute()
+ * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro
+ * in arm/simulator-arm.h.
+ * When calling as a non-direct call (i.e., from C++ code), the return address
+ * area is overwritten with the LR register by the RegExp code. When doing a
+ * direct call from generated code, the return address is placed there by
+ * the calling code, as in a normal exit frame.
+ */
+
+#define __ ACCESS_MASM(masm_)
+
+RegExpMacroAssemblerARM::RegExpMacroAssemblerARM(
+ Mode mode,
+ int registers_to_save,
+ Zone* zone)
+ : NativeRegExpMacroAssembler(zone),
+ masm_(new MacroAssembler(zone->isolate(), NULL, kRegExpCodeSize)),
+ mode_(mode),
+ num_registers_(registers_to_save),
+ num_saved_registers_(registers_to_save),
+ entry_label_(),
+ start_label_(),
+ success_label_(),
+ backtrack_label_(),
+ exit_label_() {
+ ASSERT_EQ(0, registers_to_save % 2);
+ __ jmp(&entry_label_); // We'll write the entry code later.
+ EmitBacktrackConstantPool();
+ __ bind(&start_label_); // And then continue from here.
+}
+
+
+RegExpMacroAssemblerARM::~RegExpMacroAssemblerARM() {
+ delete masm_;
+ // Unuse labels in case we throw away the assembler without calling GetCode.
+ entry_label_.Unuse();
+ start_label_.Unuse();
+ success_label_.Unuse();
+ backtrack_label_.Unuse();
+ exit_label_.Unuse();
+ check_preempt_label_.Unuse();
+ stack_overflow_label_.Unuse();
+}
+
+
+int RegExpMacroAssemblerARM::stack_limit_slack() {
+ return RegExpStack::kStackLimitSlack;
+}
+
+
+void RegExpMacroAssemblerARM::AdvanceCurrentPosition(int by) {
+ if (by != 0) {
+ __ add(current_input_offset(),
+ current_input_offset(), Operand(by * char_size()));
+ }
+}
+
+
+void RegExpMacroAssemblerARM::AdvanceRegister(int reg, int by) {
+ ASSERT(reg >= 0);
+ ASSERT(reg < num_registers_);
+ if (by != 0) {
+ __ ldr(r0, register_location(reg));
+ __ add(r0, r0, Operand(by));
+ __ str(r0, register_location(reg));
+ }
+}
+
+
+void RegExpMacroAssemblerARM::Backtrack() {
+ CheckPreemption();
+ // Pop Code* offset from backtrack stack, add Code* and jump to location.
+ Pop(r0);
+ __ add(pc, r0, Operand(code_pointer()));
+}
+
+
+void RegExpMacroAssemblerARM::Bind(Label* label) {
+ __ bind(label);
+}
+
+
+void RegExpMacroAssemblerARM::CheckCharacter(uint32_t c, Label* on_equal) {
+ __ cmp(current_character(), Operand(c));
+ BranchOrBacktrack(eq, on_equal);
+}
+
+
+void RegExpMacroAssemblerARM::CheckCharacterGT(uc16 limit, Label* on_greater) {
+ __ cmp(current_character(), Operand(limit));
+ BranchOrBacktrack(gt, on_greater);
+}
+
+
+void RegExpMacroAssemblerARM::CheckAtStart(Label* on_at_start) {
+ Label not_at_start;
+ // Did we start the match at the start of the string at all?
+ __ ldr(r0, MemOperand(frame_pointer(), kStartIndex));
+ __ cmp(r0, Operand::Zero());
+ BranchOrBacktrack(ne, &not_at_start);
+
+ // If we did, are we still at the start of the input?
+ __ ldr(r1, MemOperand(frame_pointer(), kInputStart));
+ __ add(r0, end_of_input_address(), Operand(current_input_offset()));
+ __ cmp(r0, r1);
+ BranchOrBacktrack(eq, on_at_start);
+ __ bind(&not_at_start);
+}
+
+
+void RegExpMacroAssemblerARM::CheckNotAtStart(Label* on_not_at_start) {
+ // Did we start the match at the start of the string at all?
+ __ ldr(r0, MemOperand(frame_pointer(), kStartIndex));
+ __ cmp(r0, Operand::Zero());
+ BranchOrBacktrack(ne, on_not_at_start);
+ // If we did, are we still at the start of the input?
+ __ ldr(r1, MemOperand(frame_pointer(), kInputStart));
+ __ add(r0, end_of_input_address(), Operand(current_input_offset()));
+ __ cmp(r0, r1);
+ BranchOrBacktrack(ne, on_not_at_start);
+}
+
+
+void RegExpMacroAssemblerARM::CheckCharacterLT(uc16 limit, Label* on_less) {
+ __ cmp(current_character(), Operand(limit));
+ BranchOrBacktrack(lt, on_less);
+}
+
+
+void RegExpMacroAssemblerARM::CheckGreedyLoop(Label* on_equal) {
+ __ ldr(r0, MemOperand(backtrack_stackpointer(), 0));
+ __ cmp(current_input_offset(), r0);
+ __ add(backtrack_stackpointer(),
+ backtrack_stackpointer(), Operand(kPointerSize), LeaveCC, eq);
+ BranchOrBacktrack(eq, on_equal);
+}
+
+
+void RegExpMacroAssemblerARM::CheckNotBackReferenceIgnoreCase(
+ int start_reg,
+ Label* on_no_match) {
+ Label fallthrough;
+ __ ldr(r0, register_location(start_reg)); // Index of start of capture
+ __ ldr(r1, register_location(start_reg + 1)); // Index of end of capture
+ __ sub(r1, r1, r0, SetCC); // Length of capture.
+
+ // If length is zero, either the capture is empty or it is not participating.
+ // In either case succeed immediately.
+ __ b(eq, &fallthrough);
+
+ // Check that there are enough characters left in the input.
+ __ cmn(r1, Operand(current_input_offset()));
+ BranchOrBacktrack(gt, on_no_match);
+
+ if (mode_ == ASCII) {
+ Label success;
+ Label fail;
+ Label loop_check;
+
+ // r0 - offset of start of capture
+ // r1 - length of capture
+ __ add(r0, r0, Operand(end_of_input_address()));
+ __ add(r2, end_of_input_address(), Operand(current_input_offset()));
+ __ add(r1, r0, Operand(r1));
+
+ // r0 - Address of start of capture.
+ // r1 - Address of end of capture
+ // r2 - Address of current input position.
+
+ Label loop;
+ __ bind(&loop);
+ __ ldrb(r3, MemOperand(r0, char_size(), PostIndex));
+ __ ldrb(r4, MemOperand(r2, char_size(), PostIndex));
+ __ cmp(r4, r3);
+ __ b(eq, &loop_check);
+
+ // Mismatch, try case-insensitive match (converting letters to lower-case).
+ __ orr(r3, r3, Operand(0x20)); // Convert capture character to lower-case.
+ __ orr(r4, r4, Operand(0x20)); // Also convert input character.
+ __ cmp(r4, r3);
+ __ b(ne, &fail);
+ __ sub(r3, r3, Operand('a'));
+ __ cmp(r3, Operand('z' - 'a')); // Is r3 a lowercase letter?
+ __ b(ls, &loop_check); // In range 'a'-'z'.
+ // Latin-1: Check for values in range [224,254] but not 247.
+ __ sub(r3, r3, Operand(224 - 'a'));
+ __ cmp(r3, Operand(254 - 224));
+ __ b(hi, &fail); // Weren't Latin-1 letters.
+ __ cmp(r3, Operand(247 - 224)); // Check for 247.
+ __ b(eq, &fail);
+
+ __ bind(&loop_check);
+ __ cmp(r0, r1);
+ __ b(lt, &loop);
+ __ jmp(&success);
+
+ __ bind(&fail);
+ BranchOrBacktrack(al, on_no_match);
+
+ __ bind(&success);
+ // Compute new value of character position after the matched part.
+ __ sub(current_input_offset(), r2, end_of_input_address());
+ } else {
+ ASSERT(mode_ == UC16);
+ int argument_count = 4;
+ __ PrepareCallCFunction(argument_count, r2);
+
+ // r0 - offset of start of capture
+ // r1 - length of capture
+
+ // Put arguments into arguments registers.
+ // Parameters are
+ // r0: Address byte_offset1 - Address captured substring's start.
+ // r1: Address byte_offset2 - Address of current character position.
+ // r2: size_t byte_length - length of capture in bytes(!)
+ // r3: Isolate* isolate
+
+ // Address of start of capture.
+ __ add(r0, r0, Operand(end_of_input_address()));
+ // Length of capture.
+ __ mov(r2, Operand(r1));
+ // Save length in callee-save register for use on return.
+ __ mov(r4, Operand(r1));
+ // Address of current input position.
+ __ add(r1, current_input_offset(), Operand(end_of_input_address()));
+ // Isolate.
+ __ mov(r3, Operand(ExternalReference::isolate_address(isolate())));
+
+ {
+ AllowExternalCallThatCantCauseGC scope(masm_);
+ ExternalReference function =
+ ExternalReference::re_case_insensitive_compare_uc16(isolate());
+ __ CallCFunction(function, argument_count);
+ }
+
+ // Check if function returned non-zero for success or zero for failure.
+ __ cmp(r0, Operand::Zero());
+ BranchOrBacktrack(eq, on_no_match);
+ // On success, increment position by length of capture.
+ __ add(current_input_offset(), current_input_offset(), Operand(r4));
+ }
+
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerARM::CheckNotBackReference(
+ int start_reg,
+ Label* on_no_match) {
+ Label fallthrough;
+ Label success;
+
+ // Find length of back-referenced capture.
+ __ ldr(r0, register_location(start_reg));
+ __ ldr(r1, register_location(start_reg + 1));
+ __ sub(r1, r1, r0, SetCC); // Length to check.
+ // Succeed on empty capture (including no capture).
+ __ b(eq, &fallthrough);
+
+ // Check that there are enough characters left in the input.
+ __ cmn(r1, Operand(current_input_offset()));
+ BranchOrBacktrack(gt, on_no_match);
+
+ // Compute pointers to match string and capture string
+ __ add(r0, r0, Operand(end_of_input_address()));
+ __ add(r2, end_of_input_address(), Operand(current_input_offset()));
+ __ add(r1, r1, Operand(r0));
+
+ Label loop;
+ __ bind(&loop);
+ if (mode_ == ASCII) {
+ __ ldrb(r3, MemOperand(r0, char_size(), PostIndex));
+ __ ldrb(r4, MemOperand(r2, char_size(), PostIndex));
+ } else {
+ ASSERT(mode_ == UC16);
+ __ ldrh(r3, MemOperand(r0, char_size(), PostIndex));
+ __ ldrh(r4, MemOperand(r2, char_size(), PostIndex));
+ }
+ __ cmp(r3, r4);
+ BranchOrBacktrack(ne, on_no_match);
+ __ cmp(r0, r1);
+ __ b(lt, &loop);
+
+ // Move current character position to position after match.
+ __ sub(current_input_offset(), r2, end_of_input_address());
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerARM::CheckNotCharacter(unsigned c,
+ Label* on_not_equal) {
+ __ cmp(current_character(), Operand(c));
+ BranchOrBacktrack(ne, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerARM::CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_equal) {
+ if (c == 0) {
+ __ tst(current_character(), Operand(mask));
+ } else {
+ __ and_(r0, current_character(), Operand(mask));
+ __ cmp(r0, Operand(c));
+ }
+ BranchOrBacktrack(eq, on_equal);
+}
+
+
+void RegExpMacroAssemblerARM::CheckNotCharacterAfterAnd(unsigned c,
+ unsigned mask,
+ Label* on_not_equal) {
+ if (c == 0) {
+ __ tst(current_character(), Operand(mask));
+ } else {
+ __ and_(r0, current_character(), Operand(mask));
+ __ cmp(r0, Operand(c));
+ }
+ BranchOrBacktrack(ne, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerARM::CheckNotCharacterAfterMinusAnd(
+ uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal) {
+ ASSERT(minus < String::kMaxUtf16CodeUnit);
+ __ sub(r0, current_character(), Operand(minus));
+ __ and_(r0, r0, Operand(mask));
+ __ cmp(r0, Operand(c));
+ BranchOrBacktrack(ne, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerARM::CheckCharacterInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_in_range) {
+ __ sub(r0, current_character(), Operand(from));
+ __ cmp(r0, Operand(to - from));
+ BranchOrBacktrack(ls, on_in_range); // Unsigned lower-or-same condition.
+}
+
+
+void RegExpMacroAssemblerARM::CheckCharacterNotInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_not_in_range) {
+ __ sub(r0, current_character(), Operand(from));
+ __ cmp(r0, Operand(to - from));
+ BranchOrBacktrack(hi, on_not_in_range); // Unsigned higher condition.
+}
+
+
+void RegExpMacroAssemblerARM::CheckBitInTable(
+ Handle<ByteArray> table,
+ Label* on_bit_set) {
+ __ mov(r0, Operand(table));
+ if (mode_ != ASCII || kTableMask != String::kMaxOneByteCharCode) {
+ __ and_(r1, current_character(), Operand(kTableSize - 1));
+ __ add(r1, r1, Operand(ByteArray::kHeaderSize - kHeapObjectTag));
+ } else {
+ __ add(r1,
+ current_character(),
+ Operand(ByteArray::kHeaderSize - kHeapObjectTag));
+ }
+ __ ldrb(r0, MemOperand(r0, r1));
+ __ cmp(r0, Operand::Zero());
+ BranchOrBacktrack(ne, on_bit_set);
+}
+
+
+bool RegExpMacroAssemblerARM::CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match) {
+ // Range checks (c in min..max) are generally implemented by an unsigned
+ // (c - min) <= (max - min) check
+ switch (type) {
+ case 's':
+ // Match space-characters
+ if (mode_ == ASCII) {
+ // One byte space characters are '\t'..'\r', ' ' and \u00a0.
+ Label success;
+ __ cmp(current_character(), Operand(' '));
+ __ b(eq, &success);
+ // Check range 0x09..0x0d
+ __ sub(r0, current_character(), Operand('\t'));
+ __ cmp(r0, Operand('\r' - '\t'));
+ __ b(ls, &success);
+ // \u00a0 (NBSP).
+ __ cmp(r0, Operand(0x00a0 - '\t'));
+ BranchOrBacktrack(ne, on_no_match);
+ __ bind(&success);
+ return true;
+ }
+ return false;
+ case 'S':
+ // The emitted code for generic character classes is good enough.
+ return false;
+ case 'd':
+ // Match ASCII digits ('0'..'9')
+ __ sub(r0, current_character(), Operand('0'));
+ __ cmp(r0, Operand('9' - '0'));
+ BranchOrBacktrack(hi, on_no_match);
+ return true;
+ case 'D':
+ // Match non ASCII-digits
+ __ sub(r0, current_character(), Operand('0'));
+ __ cmp(r0, Operand('9' - '0'));
+ BranchOrBacktrack(ls, on_no_match);
+ return true;
+ case '.': {
+ // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
+ __ eor(r0, current_character(), Operand(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
+ __ sub(r0, r0, Operand(0x0b));
+ __ cmp(r0, Operand(0x0c - 0x0b));
+ BranchOrBacktrack(ls, 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(r0, r0, Operand(0x2028 - 0x0b));
+ __ cmp(r0, Operand(1));
+ BranchOrBacktrack(ls, on_no_match);
+ }
+ return true;
+ }
+ case 'n': {
+ // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
+ __ eor(r0, current_character(), Operand(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
+ __ sub(r0, r0, Operand(0x0b));
+ __ cmp(r0, Operand(0x0c - 0x0b));
+ if (mode_ == ASCII) {
+ BranchOrBacktrack(hi, on_no_match);
+ } else {
+ Label done;
+ __ b(ls, &done);
+ // 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(r0, r0, Operand(0x2028 - 0x0b));
+ __ cmp(r0, Operand(1));
+ BranchOrBacktrack(hi, on_no_match);
+ __ bind(&done);
+ }
+ return true;
+ }
+ case 'w': {
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ cmp(current_character(), Operand('z'));
+ BranchOrBacktrack(hi, on_no_match);
+ }
+ ExternalReference map = ExternalReference::re_word_character_map();
+ __ mov(r0, Operand(map));
+ __ ldrb(r0, MemOperand(r0, current_character()));
+ __ cmp(r0, Operand::Zero());
+ BranchOrBacktrack(eq, on_no_match);
+ return true;
+ }
+ case 'W': {
+ Label done;
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ cmp(current_character(), Operand('z'));
+ __ b(hi, &done);
+ }
+ ExternalReference map = ExternalReference::re_word_character_map();
+ __ mov(r0, Operand(map));
+ __ ldrb(r0, MemOperand(r0, current_character()));
+ __ cmp(r0, Operand::Zero());
+ BranchOrBacktrack(ne, on_no_match);
+ if (mode_ != ASCII) {
+ __ bind(&done);
+ }
+ return true;
+ }
+ case '*':
+ // Match any character.
+ return true;
+ // No custom implementation (yet): s(UC16), S(UC16).
+ default:
+ return false;
+ }
+}
+
+
+void RegExpMacroAssemblerARM::Fail() {
+ __ mov(r0, Operand(FAILURE));
+ __ jmp(&exit_label_);
+}
+
+
+Handle<HeapObject> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
+ Label return_r0;
+ // Finalize code - write the entry point code now we know how many
+ // registers we need.
+
+ // 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.
+ // Store link register in existing stack-cell.
+ // Order here should correspond to order of offset constants in header file.
+ RegList registers_to_retain = r4.bit() | r5.bit() | r6.bit() |
+ r7.bit() | r8.bit() | r9.bit() | r10.bit() | fp.bit();
+ RegList argument_registers = r0.bit() | r1.bit() | r2.bit() | r3.bit();
+ __ stm(db_w, sp, argument_registers | registers_to_retain | lr.bit());
+ // Set frame pointer in space for it if this is not a direct call
+ // from generated code.
+ __ add(frame_pointer(), sp, Operand(4 * kPointerSize));
+ __ mov(r0, Operand::Zero());
+ __ push(r0); // Make room for success counter and initialize it to 0.
+ __ push(r0); // Make room for "position - 1" constant (value is irrelevant).
+ // Check if we have space on the stack for registers.
+ Label stack_limit_hit;
+ Label stack_ok;
+
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ mov(r0, Operand(stack_limit));
+ __ ldr(r0, MemOperand(r0));
+ __ sub(r0, sp, r0, SetCC);
+ // Handle it if the stack pointer is already below the stack limit.
+ __ b(ls, &stack_limit_hit);
+ // Check if there is room for the variable number of registers above
+ // the stack limit.
+ __ cmp(r0, Operand(num_registers_ * kPointerSize));
+ __ b(hs, &stack_ok);
+ // Exit with OutOfMemory exception. There is not enough space on the stack
+ // for our working registers.
+ __ mov(r0, Operand(EXCEPTION));
+ __ jmp(&return_r0);
+
+ __ bind(&stack_limit_hit);
+ CallCheckStackGuardState(r0);
+ __ cmp(r0, Operand::Zero());
+ // If returned value is non-zero, we exit with the returned value as result.
+ __ b(ne, &return_r0);
+
+ __ bind(&stack_ok);
+
+ // Allocate space on stack for registers.
+ __ sub(sp, sp, Operand(num_registers_ * kPointerSize));
+ // Load string end.
+ __ ldr(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+ // Load input start.
+ __ ldr(r0, MemOperand(frame_pointer(), kInputStart));
+ // Find negative length (offset of start relative to end).
+ __ sub(current_input_offset(), r0, end_of_input_address());
+ // Set r0 to address of char before start of the input string
+ // (effectively string position -1).
+ __ ldr(r1, MemOperand(frame_pointer(), kStartIndex));
+ __ sub(r0, current_input_offset(), Operand(char_size()));
+ __ sub(r0, r0, Operand(r1, LSL, (mode_ == UC16) ? 1 : 0));
+ // Store this value in a local variable, for use when clearing
+ // position registers.
+ __ str(r0, MemOperand(frame_pointer(), kInputStartMinusOne));
+
+ // Initialize code pointer register
+ __ mov(code_pointer(), Operand(masm_->CodeObject()));
+
+ Label load_char_start_regexp, start_regexp;
+ // Load newline if index is at start, previous character otherwise.
+ __ cmp(r1, Operand::Zero());
+ __ b(ne, &load_char_start_regexp);
+ __ mov(current_character(), Operand('\n'), LeaveCC, eq);
+ __ jmp(&start_regexp);
+
+ // Global regexp restarts matching here.
+ __ bind(&load_char_start_regexp);
+ // Load previous char as initial value of current character register.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&start_regexp);
+
+ // Initialize on-stack registers.
+ if (num_saved_registers_ > 0) { // Always is, if generated from a regexp.
+ // Fill saved registers with initial value = start offset - 1
+ if (num_saved_registers_ > 8) {
+ // Address of register 0.
+ __ add(r1, frame_pointer(), Operand(kRegisterZero));
+ __ mov(r2, Operand(num_saved_registers_));
+ Label init_loop;
+ __ bind(&init_loop);
+ __ str(r0, MemOperand(r1, kPointerSize, NegPostIndex));
+ __ sub(r2, r2, Operand(1), SetCC);
+ __ b(ne, &init_loop);
+ } else {
+ for (int i = 0; i < num_saved_registers_; i++) {
+ __ str(r0, register_location(i));
+ }
+ }
+ }
+
+ // Initialize backtrack stack pointer.
+ __ ldr(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackHighEnd));
+
+ __ jmp(&start_label_);
+
+ // Exit code:
+ if (success_label_.is_linked()) {
+ // Save captures when successful.
+ __ bind(&success_label_);
+ if (num_saved_registers_ > 0) {
+ // copy captures to output
+ __ ldr(r1, MemOperand(frame_pointer(), kInputStart));
+ __ ldr(r0, MemOperand(frame_pointer(), kRegisterOutput));
+ __ ldr(r2, MemOperand(frame_pointer(), kStartIndex));
+ __ sub(r1, end_of_input_address(), r1);
+ // r1 is length of input in bytes.
+ if (mode_ == UC16) {
+ __ mov(r1, Operand(r1, LSR, 1));
+ }
+ // r1 is length of input in characters.
+ __ add(r1, r1, Operand(r2));
+ // r1 is length of string in characters.
+
+ ASSERT_EQ(0, num_saved_registers_ % 2);
+ // Always an even number of capture registers. This allows us to
+ // unroll the loop once to add an operation between a load of a register
+ // and the following use of that register.
+ for (int i = 0; i < num_saved_registers_; i += 2) {
+ __ ldr(r2, register_location(i));
+ __ ldr(r3, register_location(i + 1));
+ if (i == 0 && global_with_zero_length_check()) {
+ // Keep capture start in r4 for the zero-length check later.
+ __ mov(r4, r2);
+ }
+ if (mode_ == UC16) {
+ __ add(r2, r1, Operand(r2, ASR, 1));
+ __ add(r3, r1, Operand(r3, ASR, 1));
+ } else {
+ __ add(r2, r1, Operand(r2));
+ __ add(r3, r1, Operand(r3));
+ }
+ __ str(r2, MemOperand(r0, kPointerSize, PostIndex));
+ __ str(r3, MemOperand(r0, kPointerSize, PostIndex));
+ }
+ }
+
+ if (global()) {
+ // Restart matching if the regular expression is flagged as global.
+ __ ldr(r0, MemOperand(frame_pointer(), kSuccessfulCaptures));
+ __ ldr(r1, MemOperand(frame_pointer(), kNumOutputRegisters));
+ __ ldr(r2, MemOperand(frame_pointer(), kRegisterOutput));
+ // Increment success counter.
+ __ add(r0, r0, Operand(1));
+ __ str(r0, MemOperand(frame_pointer(), kSuccessfulCaptures));
+ // Capture results have been stored, so the number of remaining global
+ // output registers is reduced by the number of stored captures.
+ __ sub(r1, r1, Operand(num_saved_registers_));
+ // Check whether we have enough room for another set of capture results.
+ __ cmp(r1, Operand(num_saved_registers_));
+ __ b(lt, &return_r0);
+
+ __ str(r1, MemOperand(frame_pointer(), kNumOutputRegisters));
+ // Advance the location for output.
+ __ add(r2, r2, Operand(num_saved_registers_ * kPointerSize));
+ __ str(r2, MemOperand(frame_pointer(), kRegisterOutput));
+
+ // Prepare r0 to initialize registers with its value in the next run.
+ __ ldr(r0, MemOperand(frame_pointer(), kInputStartMinusOne));
+
+ if (global_with_zero_length_check()) {
+ // Special case for zero-length matches.
+ // r4: capture start index
+ __ cmp(current_input_offset(), r4);
+ // Not a zero-length match, restart.
+ __ b(ne, &load_char_start_regexp);
+ // Offset from the end is zero if we already reached the end.
+ __ cmp(current_input_offset(), Operand::Zero());
+ __ b(eq, &exit_label_);
+ // Advance current position after a zero-length match.
+ __ add(current_input_offset(),
+ current_input_offset(),
+ Operand((mode_ == UC16) ? 2 : 1));
+ }
+
+ __ b(&load_char_start_regexp);
+ } else {
+ __ mov(r0, Operand(SUCCESS));
+ }
+ }
+
+ // Exit and return r0
+ __ bind(&exit_label_);
+ if (global()) {
+ __ ldr(r0, MemOperand(frame_pointer(), kSuccessfulCaptures));
+ }
+
+ __ bind(&return_r0);
+ // Skip sp past regexp registers and local variables..
+ __ mov(sp, frame_pointer());
+ // Restore registers r4..r11 and return (restoring lr to pc).
+ __ ldm(ia_w, sp, registers_to_retain | pc.bit());
+
+ // Backtrack code (branch target for conditional backtracks).
+ if (backtrack_label_.is_linked()) {
+ __ bind(&backtrack_label_);
+ Backtrack();
+ }
+
+ Label exit_with_exception;
+
+ // Preempt-code
+ if (check_preempt_label_.is_linked()) {
+ SafeCallTarget(&check_preempt_label_);
+
+ CallCheckStackGuardState(r0);
+ __ cmp(r0, Operand::Zero());
+ // If returning non-zero, we should end execution with the given
+ // result as return value.
+ __ b(ne, &return_r0);
+
+ // String might have moved: Reload end of string from frame.
+ __ ldr(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+ SafeReturn();
+ }
+
+ // Backtrack stack overflow code.
+ if (stack_overflow_label_.is_linked()) {
+ SafeCallTarget(&stack_overflow_label_);
+ // Reached if the backtrack-stack limit has been hit.
+ Label grow_failed;
+
+ // Call GrowStack(backtrack_stackpointer(), &stack_base)
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, r0);
+ __ mov(r0, backtrack_stackpointer());
+ __ add(r1, frame_pointer(), Operand(kStackHighEnd));
+ __ mov(r2, Operand(ExternalReference::isolate_address(isolate())));
+ ExternalReference grow_stack =
+ ExternalReference::re_grow_stack(isolate());
+ __ CallCFunction(grow_stack, num_arguments);
+ // If return NULL, we have failed to grow the stack, and
+ // must exit with a stack-overflow exception.
+ __ cmp(r0, Operand::Zero());
+ __ b(eq, &exit_with_exception);
+ // Otherwise use return value as new stack pointer.
+ __ mov(backtrack_stackpointer(), r0);
+ // Restore saved registers and continue.
+ SafeReturn();
+ }
+
+ if (exit_with_exception.is_linked()) {
+ // If any of the code above needed to exit with an exception.
+ __ bind(&exit_with_exception);
+ // Exit with Result EXCEPTION(-1) to signal thrown exception.
+ __ mov(r0, Operand(EXCEPTION));
+ __ jmp(&return_r0);
+ }
+
+ CodeDesc code_desc;
+ masm_->GetCode(&code_desc);
+ Handle<Code> code = isolate()->factory()->NewCode(
+ code_desc, Code::ComputeFlags(Code::REGEXP), masm_->CodeObject());
+ PROFILE(Isolate::Current(), RegExpCodeCreateEvent(*code, *source));
+ return Handle<HeapObject>::cast(code);
+}
+
+
+void RegExpMacroAssemblerARM::GoTo(Label* to) {
+ BranchOrBacktrack(al, to);
+}
+
+
+void RegExpMacroAssemblerARM::IfRegisterGE(int reg,
+ int comparand,
+ Label* if_ge) {
+ __ ldr(r0, register_location(reg));
+ __ cmp(r0, Operand(comparand));
+ BranchOrBacktrack(ge, if_ge);
+}
+
+
+void RegExpMacroAssemblerARM::IfRegisterLT(int reg,
+ int comparand,
+ Label* if_lt) {
+ __ ldr(r0, register_location(reg));
+ __ cmp(r0, Operand(comparand));
+ BranchOrBacktrack(lt, if_lt);
+}
+
+
+void RegExpMacroAssemblerARM::IfRegisterEqPos(int reg,
+ Label* if_eq) {
+ __ ldr(r0, register_location(reg));
+ __ cmp(r0, Operand(current_input_offset()));
+ BranchOrBacktrack(eq, if_eq);
+}
+
+
+RegExpMacroAssembler::IrregexpImplementation
+ RegExpMacroAssemblerARM::Implementation() {
+ return kARMImplementation;
+}
+
+
+void RegExpMacroAssemblerARM::LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds,
+ int characters) {
+ ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
+ ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
+ if (check_bounds) {
+ CheckPosition(cp_offset + characters - 1, on_end_of_input);
+ }
+ LoadCurrentCharacterUnchecked(cp_offset, characters);
+}
+
+
+void RegExpMacroAssemblerARM::PopCurrentPosition() {
+ Pop(current_input_offset());
+}
+
+
+void RegExpMacroAssemblerARM::PopRegister(int register_index) {
+ Pop(r0);
+ __ str(r0, register_location(register_index));
+}
+
+
+static bool is_valid_memory_offset(int value) {
+ if (value < 0) value = -value;
+ return value < (1<<12);
+}
+
+
+void RegExpMacroAssemblerARM::PushBacktrack(Label* label) {
+ if (label->is_bound()) {
+ int target = label->pos();
+ __ mov(r0, Operand(target + Code::kHeaderSize - kHeapObjectTag));
+ } else {
+ int constant_offset = GetBacktrackConstantPoolEntry();
+ masm_->label_at_put(label, constant_offset);
+ // Reading pc-relative is based on the address 8 bytes ahead of
+ // the current opcode.
+ unsigned int offset_of_pc_register_read =
+ masm_->pc_offset() + Assembler::kPcLoadDelta;
+ int pc_offset_of_constant =
+ constant_offset - offset_of_pc_register_read;
+ ASSERT(pc_offset_of_constant < 0);
+ if (is_valid_memory_offset(pc_offset_of_constant)) {
+ Assembler::BlockConstPoolScope block_const_pool(masm_);
+ __ ldr(r0, MemOperand(pc, pc_offset_of_constant));
+ } else {
+ // Not a 12-bit offset, so it needs to be loaded from the constant
+ // pool.
+ Assembler::BlockConstPoolScope block_const_pool(masm_);
+ __ mov(r0, Operand(pc_offset_of_constant + Assembler::kInstrSize));
+ __ ldr(r0, MemOperand(pc, r0));
+ }
+ }
+ Push(r0);
+ CheckStackLimit();
+}
+
+
+void RegExpMacroAssemblerARM::PushCurrentPosition() {
+ Push(current_input_offset());
+}
+
+
+void RegExpMacroAssemblerARM::PushRegister(int register_index,
+ StackCheckFlag check_stack_limit) {
+ __ ldr(r0, register_location(register_index));
+ Push(r0);
+ if (check_stack_limit) CheckStackLimit();
+}
+
+
+void RegExpMacroAssemblerARM::ReadCurrentPositionFromRegister(int reg) {
+ __ ldr(current_input_offset(), register_location(reg));
+}
+
+
+void RegExpMacroAssemblerARM::ReadStackPointerFromRegister(int reg) {
+ __ ldr(backtrack_stackpointer(), register_location(reg));
+ __ ldr(r0, MemOperand(frame_pointer(), kStackHighEnd));
+ __ add(backtrack_stackpointer(), backtrack_stackpointer(), Operand(r0));
+}
+
+
+void RegExpMacroAssemblerARM::SetCurrentPositionFromEnd(int by) {
+ Label after_position;
+ __ cmp(current_input_offset(), Operand(-by * char_size()));
+ __ b(ge, &after_position);
+ __ mov(current_input_offset(), Operand(-by * char_size()));
+ // On RegExp code entry (where this operation is used), the character before
+ // the current position is expected to be already loaded.
+ // We have advanced the position, so it's safe to read backwards.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&after_position);
+}
+
+
+void RegExpMacroAssemblerARM::SetRegister(int register_index, int to) {
+ ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
+ __ mov(r0, Operand(to));
+ __ str(r0, register_location(register_index));
+}
+
+
+bool RegExpMacroAssemblerARM::Succeed() {
+ __ jmp(&success_label_);
+ return global();
+}
+
+
+void RegExpMacroAssemblerARM::WriteCurrentPositionToRegister(int reg,
+ int cp_offset) {
+ if (cp_offset == 0) {
+ __ str(current_input_offset(), register_location(reg));
+ } else {
+ __ add(r0, current_input_offset(), Operand(cp_offset * char_size()));
+ __ str(r0, register_location(reg));
+ }
+}
+
+
+void RegExpMacroAssemblerARM::ClearRegisters(int reg_from, int reg_to) {
+ ASSERT(reg_from <= reg_to);
+ __ ldr(r0, MemOperand(frame_pointer(), kInputStartMinusOne));
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ __ str(r0, register_location(reg));
+ }
+}
+
+
+void RegExpMacroAssemblerARM::WriteStackPointerToRegister(int reg) {
+ __ ldr(r1, MemOperand(frame_pointer(), kStackHighEnd));
+ __ sub(r0, backtrack_stackpointer(), r1);
+ __ str(r0, register_location(reg));
+}
+
+
+// Private methods:
+
+void RegExpMacroAssemblerARM::CallCheckStackGuardState(Register scratch) {
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, scratch);
+ // RegExp code frame pointer.
+ __ mov(r2, frame_pointer());
+ // Code* of self.
+ __ mov(r1, Operand(masm_->CodeObject()));
+ // r0 becomes return address pointer.
+ ExternalReference stack_guard_check =
+ ExternalReference::re_check_stack_guard_state(isolate());
+ CallCFunctionUsingStub(stack_guard_check, num_arguments);
+}
+
+
+// Helper function for reading a value out of a stack frame.
+template <typename T>
+static T& frame_entry(Address re_frame, int frame_offset) {
+ return reinterpret_cast<T&>(Memory::int32_at(re_frame + frame_offset));
+}
+
+
+int RegExpMacroAssemblerARM::CheckStackGuardState(Address* return_address,
+ Code* re_code,
+ Address re_frame) {
+ Isolate* isolate = frame_entry<Isolate*>(re_frame, kIsolate);
+ ASSERT(isolate == Isolate::Current());
+ if (isolate->stack_guard()->IsStackOverflow()) {
+ isolate->StackOverflow();
+ return EXCEPTION;
+ }
+
+ // If not real stack overflow the stack guard was used to interrupt
+ // execution for another purpose.
+
+ // If this is a direct call from JavaScript retry the RegExp forcing the call
+ // through the runtime system. Currently the direct call cannot handle a GC.
+ if (frame_entry<int>(re_frame, kDirectCall) == 1) {
+ return RETRY;
+ }
+
+ // Prepare for possible GC.
+ HandleScope handles(isolate);
+ Handle<Code> code_handle(re_code);
+
+ Handle<String> subject(frame_entry<String*>(re_frame, kInputString));
+
+ // Current string.
+ bool is_ascii = subject->IsOneByteRepresentationUnderneath();
+
+ ASSERT(re_code->instruction_start() <= *return_address);
+ ASSERT(*return_address <=
+ re_code->instruction_start() + re_code->instruction_size());
+
+ MaybeObject* result = Execution::HandleStackGuardInterrupt(isolate);
+
+ if (*code_handle != re_code) { // Return address no longer valid
+ int delta = code_handle->address() - re_code->address();
+ // Overwrite the return address on the stack.
+ *return_address += delta;
+ }
+
+ if (result->IsException()) {
+ return EXCEPTION;
+ }
+
+ Handle<String> subject_tmp = subject;
+ int slice_offset = 0;
+
+ // Extract the underlying string and the slice offset.
+ if (StringShape(*subject_tmp).IsCons()) {
+ subject_tmp = Handle<String>(ConsString::cast(*subject_tmp)->first());
+ } else if (StringShape(*subject_tmp).IsSliced()) {
+ SlicedString* slice = SlicedString::cast(*subject_tmp);
+ subject_tmp = Handle<String>(slice->parent());
+ slice_offset = slice->offset();
+ }
+
+ // String might have changed.
+ if (subject_tmp->IsOneByteRepresentation() != is_ascii) {
+ // If we changed between an ASCII and an UC16 string, the specialized
+ // code cannot be used, and we need to restart regexp matching from
+ // scratch (including, potentially, compiling a new version of the code).
+ return RETRY;
+ }
+
+ // Otherwise, the content of the string might have moved. It must still
+ // be a sequential or external string with the same content.
+ // Update the start and end pointers in the stack frame to the current
+ // location (whether it has actually moved or not).
+ ASSERT(StringShape(*subject_tmp).IsSequential() ||
+ StringShape(*subject_tmp).IsExternal());
+
+ // The original start address of the characters to match.
+ const byte* start_address = frame_entry<const byte*>(re_frame, kInputStart);
+
+ // Find the current start address of the same character at the current string
+ // position.
+ int start_index = frame_entry<int>(re_frame, kStartIndex);
+ const byte* new_address = StringCharacterPosition(*subject_tmp,
+ start_index + slice_offset);
+
+ if (start_address != new_address) {
+ // If there is a difference, update the object pointer and start and end
+ // addresses in the RegExp stack frame to match the new value.
+ const byte* end_address = frame_entry<const byte* >(re_frame, kInputEnd);
+ int byte_length = static_cast<int>(end_address - start_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;
+}
+
+
+MemOperand RegExpMacroAssemblerARM::register_location(int register_index) {
+ ASSERT(register_index < (1<<30));
+ if (num_registers_ <= register_index) {
+ num_registers_ = register_index + 1;
+ }
+ return MemOperand(frame_pointer(),
+ kRegisterZero - register_index * kPointerSize);
+}
+
+
+void RegExpMacroAssemblerARM::CheckPosition(int cp_offset,
+ Label* on_outside_input) {
+ __ cmp(current_input_offset(), Operand(-cp_offset * char_size()));
+ BranchOrBacktrack(ge, on_outside_input);
+}
+
+
+void RegExpMacroAssemblerARM::BranchOrBacktrack(Condition condition,
+ Label* to) {
+ if (condition == al) { // Unconditional.
+ if (to == NULL) {
+ Backtrack();
+ return;
+ }
+ __ jmp(to);
+ return;
+ }
+ if (to == NULL) {
+ __ b(condition, &backtrack_label_);
+ return;
+ }
+ __ b(condition, to);
+}
+
+
+void RegExpMacroAssemblerARM::SafeCall(Label* to, Condition cond) {
+ __ bl(to, cond);
+}
+
+
+void RegExpMacroAssemblerARM::SafeReturn() {
+ __ pop(lr);
+ __ add(pc, lr, Operand(masm_->CodeObject()));
+}
+
+
+void RegExpMacroAssemblerARM::SafeCallTarget(Label* name) {
+ __ bind(name);
+ __ sub(lr, lr, Operand(masm_->CodeObject()));
+ __ push(lr);
+}
+
+
+void RegExpMacroAssemblerARM::Push(Register source) {
+ ASSERT(!source.is(backtrack_stackpointer()));
+ __ str(source,
+ MemOperand(backtrack_stackpointer(), kPointerSize, NegPreIndex));
+}
+
+
+void RegExpMacroAssemblerARM::Pop(Register target) {
+ ASSERT(!target.is(backtrack_stackpointer()));
+ __ ldr(target,
+ MemOperand(backtrack_stackpointer(), kPointerSize, PostIndex));
+}
+
+
+void RegExpMacroAssemblerARM::CheckPreemption() {
+ // Check for preemption.
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ mov(r0, Operand(stack_limit));
+ __ ldr(r0, MemOperand(r0));
+ __ cmp(sp, r0);
+ SafeCall(&check_preempt_label_, ls);
+}
+
+
+void RegExpMacroAssemblerARM::CheckStackLimit() {
+ ExternalReference stack_limit =
+ ExternalReference::address_of_regexp_stack_limit(isolate());
+ __ mov(r0, Operand(stack_limit));
+ __ ldr(r0, MemOperand(r0));
+ __ cmp(backtrack_stackpointer(), Operand(r0));
+ SafeCall(&stack_overflow_label_, ls);
+}
+
+
+void RegExpMacroAssemblerARM::EmitBacktrackConstantPool() {
+ __ CheckConstPool(false, false);
+ Assembler::BlockConstPoolScope block_const_pool(masm_);
+ backtrack_constant_pool_offset_ = masm_->pc_offset();
+ for (int i = 0; i < kBacktrackConstantPoolSize; i++) {
+ __ emit(0);
+ }
+
+ backtrack_constant_pool_capacity_ = kBacktrackConstantPoolSize;
+}
+
+
+int RegExpMacroAssemblerARM::GetBacktrackConstantPoolEntry() {
+ while (backtrack_constant_pool_capacity_ > 0) {
+ int offset = backtrack_constant_pool_offset_;
+ backtrack_constant_pool_offset_ += kPointerSize;
+ backtrack_constant_pool_capacity_--;
+ if (masm_->pc_offset() - offset < 2 * KB) {
+ return offset;
+ }
+ }
+ Label new_pool_skip;
+ __ jmp(&new_pool_skip);
+ EmitBacktrackConstantPool();
+ __ bind(&new_pool_skip);
+ int offset = backtrack_constant_pool_offset_;
+ backtrack_constant_pool_offset_ += kPointerSize;
+ backtrack_constant_pool_capacity_--;
+ return offset;
+}
+
+
+void RegExpMacroAssemblerARM::CallCFunctionUsingStub(
+ ExternalReference function,
+ int num_arguments) {
+ // Must pass all arguments in registers. The stub pushes on the stack.
+ ASSERT(num_arguments <= 4);
+ __ mov(code_pointer(), Operand(function));
+ RegExpCEntryStub stub;
+ __ CallStub(&stub);
+ if (OS::ActivationFrameAlignment() != 0) {
+ __ ldr(sp, MemOperand(sp, 0));
+ }
+ __ mov(code_pointer(), Operand(masm_->CodeObject()));
+}
+
+
+bool RegExpMacroAssemblerARM::CanReadUnaligned() {
+ return CpuFeatures::IsSupported(UNALIGNED_ACCESSES) && !slow_safe();
+}
+
+
+void RegExpMacroAssemblerARM::LoadCurrentCharacterUnchecked(int cp_offset,
+ int characters) {
+ Register offset = current_input_offset();
+ if (cp_offset != 0) {
+ // r4 is not being used to store the capture start index at this point.
+ __ add(r4, current_input_offset(), Operand(cp_offset * char_size()));
+ offset = r4;
+ }
+ // The ldr, str, ldrh, strh instructions can do unaligned accesses, if the CPU
+ // and the operating system running on the target allow it.
+ // If unaligned load/stores are not supported then this function must only
+ // be used to load a single character at a time.
+ if (!CanReadUnaligned()) {
+ ASSERT(characters == 1);
+ }
+
+ if (mode_ == ASCII) {
+ if (characters == 4) {
+ __ ldr(current_character(), MemOperand(end_of_input_address(), offset));
+ } else if (characters == 2) {
+ __ ldrh(current_character(), MemOperand(end_of_input_address(), offset));
+ } else {
+ ASSERT(characters == 1);
+ __ ldrb(current_character(), MemOperand(end_of_input_address(), offset));
+ }
+ } else {
+ ASSERT(mode_ == UC16);
+ if (characters == 2) {
+ __ ldr(current_character(), MemOperand(end_of_input_address(), offset));
+ } else {
+ ASSERT(characters == 1);
+ __ ldrh(current_character(), MemOperand(end_of_input_address(), offset));
+ }
+ }
+}
+
+
+void RegExpCEntryStub::Generate(MacroAssembler* masm_) {
+ int stack_alignment = OS::ActivationFrameAlignment();
+ if (stack_alignment < kPointerSize) stack_alignment = kPointerSize;
+ // Stack is already aligned for call, so decrement by alignment
+ // to make room for storing the link register.
+ __ str(lr, MemOperand(sp, stack_alignment, NegPreIndex));
+ __ mov(r0, sp);
+ __ Call(r5);
+ __ ldr(pc, MemOperand(sp, stack_alignment, PostIndex));
+}
+
+#undef __
+
+#endif // V8_INTERPRETED_REGEXP
+
+}} // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/regexp-macro-assembler-arm.h b/chromium/v8/src/arm/regexp-macro-assembler-arm.h
new file mode 100644
index 00000000000..1825752ebc2
--- /dev/null
+++ b/chromium/v8/src/arm/regexp-macro-assembler-arm.h
@@ -0,0 +1,257 @@
+// Copyright 2012 the V8 project authors. 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_ARM_REGEXP_MACRO_ASSEMBLER_ARM_H_
+#define V8_ARM_REGEXP_MACRO_ASSEMBLER_ARM_H_
+
+#include "arm/assembler-arm.h"
+#include "arm/assembler-arm-inl.h"
+#include "macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+
+#ifndef V8_INTERPRETED_REGEXP
+class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler {
+ public:
+ RegExpMacroAssemblerARM(Mode mode, int registers_to_save, Zone* zone);
+ virtual ~RegExpMacroAssemblerARM();
+ virtual int stack_limit_slack();
+ virtual void AdvanceCurrentPosition(int by);
+ virtual void AdvanceRegister(int reg, int by);
+ virtual void Backtrack();
+ virtual void Bind(Label* label);
+ virtual void CheckAtStart(Label* on_at_start);
+ virtual void CheckCharacter(unsigned c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned mask,
+ Label* on_equal);
+ virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
+ virtual void CheckCharacterLT(uc16 limit, Label* on_less);
+ // A "greedy loop" is a loop that is both greedy and with a simple
+ // body. It has a particularly simple implementation.
+ virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
+ virtual void CheckNotAtStart(Label* on_not_at_start);
+ virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
+ virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
+ Label* on_no_match);
+ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned mask,
+ Label* on_not_equal);
+ virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal);
+ virtual void CheckCharacterInRange(uc16 from,
+ uc16 to,
+ Label* on_in_range);
+ virtual void CheckCharacterNotInRange(uc16 from,
+ uc16 to,
+ Label* on_not_in_range);
+ virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
+
+ // Checks whether the given offset from the current position is before
+ // the end of the string.
+ virtual void CheckPosition(int cp_offset, Label* on_outside_input);
+ virtual bool CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match);
+ virtual void Fail();
+ virtual Handle<HeapObject> GetCode(Handle<String> source);
+ virtual void GoTo(Label* label);
+ virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
+ virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
+ virtual void IfRegisterEqPos(int reg, Label* if_eq);
+ virtual IrregexpImplementation Implementation();
+ virtual void LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds = true,
+ int characters = 1);
+ virtual void PopCurrentPosition();
+ virtual void PopRegister(int register_index);
+ virtual void PushBacktrack(Label* label);
+ virtual void PushCurrentPosition();
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
+ virtual void ReadCurrentPositionFromRegister(int reg);
+ virtual void ReadStackPointerFromRegister(int reg);
+ virtual void SetCurrentPositionFromEnd(int by);
+ virtual void SetRegister(int register_index, int to);
+ virtual bool Succeed();
+ virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+ virtual void ClearRegisters(int reg_from, int reg_to);
+ virtual void WriteStackPointerToRegister(int reg);
+ virtual bool CanReadUnaligned();
+
+ // Called from RegExp if the stack-guard is triggered.
+ // If the code object is relocated, the return address is fixed before
+ // returning.
+ static int CheckStackGuardState(Address* return_address,
+ Code* re_code,
+ Address re_frame);
+
+ private:
+ // Offsets from frame_pointer() of function parameters and stored registers.
+ static const int kFramePointer = 0;
+
+ // Above the frame pointer - Stored registers and stack passed parameters.
+ // Register 4..11.
+ static const int kStoredRegisters = kFramePointer;
+ // Return address (stored from link register, read into pc on return).
+ static const int kReturnAddress = kStoredRegisters + 8 * kPointerSize;
+ static const int kSecondaryReturnAddress = kReturnAddress + kPointerSize;
+ // Stack parameters placed by caller.
+ static const int kRegisterOutput = kSecondaryReturnAddress + kPointerSize;
+ static const int kNumOutputRegisters = kRegisterOutput + kPointerSize;
+ static const int kStackHighEnd = kNumOutputRegisters + kPointerSize;
+ static const int kDirectCall = kStackHighEnd + kPointerSize;
+ static const int kIsolate = kDirectCall + kPointerSize;
+
+ // Below the frame pointer.
+ // Register parameters stored by setup code.
+ static const int kInputEnd = kFramePointer - kPointerSize;
+ static const int kInputStart = kInputEnd - kPointerSize;
+ static const int kStartIndex = kInputStart - kPointerSize;
+ static const int kInputString = kStartIndex - kPointerSize;
+ // When adding local variables remember to push space for them in
+ // the frame in GetCode.
+ static const int kSuccessfulCaptures = kInputString - kPointerSize;
+ static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
+ // First register address. Following registers are below it on the stack.
+ static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
+
+ // Initial size of code buffer.
+ static const size_t kRegExpCodeSize = 1024;
+
+ static const int kBacktrackConstantPoolSize = 4;
+
+ // Load a number of characters at the given offset from the
+ // current position, into the current-character register.
+ void LoadCurrentCharacterUnchecked(int cp_offset, int character_count);
+
+ // Check whether preemption has been requested.
+ void CheckPreemption();
+
+ // Check whether we are exceeding the stack limit on the backtrack stack.
+ void CheckStackLimit();
+
+ void EmitBacktrackConstantPool();
+ int GetBacktrackConstantPoolEntry();
+
+
+ // Generate a call to CheckStackGuardState.
+ void CallCheckStackGuardState(Register scratch);
+
+ // The ebp-relative location of a regexp register.
+ MemOperand register_location(int register_index);
+
+ // Register holding the current input position as negative offset from
+ // the end of the string.
+ inline Register current_input_offset() { return r6; }
+
+ // The register containing the current character after LoadCurrentCharacter.
+ inline Register current_character() { return r7; }
+
+ // Register holding address of the end of the input string.
+ inline Register end_of_input_address() { return r10; }
+
+ // Register holding the frame address. Local variables, parameters and
+ // regexp registers are addressed relative to this.
+ inline Register frame_pointer() { return fp; }
+
+ // The register containing the backtrack stack top. Provides a meaningful
+ // name to the register.
+ inline Register backtrack_stackpointer() { return r8; }
+
+ // Register holding pointer to the current code object.
+ inline Register code_pointer() { return r5; }
+
+ // Byte size of chars in the string to match (decided by the Mode argument)
+ inline int char_size() { return static_cast<int>(mode_); }
+
+ // Equivalent to a conditional branch to the label, unless the label
+ // is NULL, in which case it is a conditional Backtrack.
+ void BranchOrBacktrack(Condition condition, Label* to);
+
+ // Call and return internally in the generated code in a way that
+ // is GC-safe (i.e., doesn't leave absolute code addresses on the stack)
+ inline void SafeCall(Label* to, Condition cond = al);
+ inline void SafeReturn();
+ inline void SafeCallTarget(Label* name);
+
+ // Pushes the value of a register on the backtrack stack. Decrements the
+ // stack pointer by a word size and stores the register's value there.
+ inline void Push(Register source);
+
+ // Pops a value from the backtrack stack. Reads the word at the stack pointer
+ // and increments it by a word size.
+ inline void Pop(Register target);
+
+ // Calls a C function and cleans up the frame alignment done by
+ // by FrameAlign. The called function *is* allowed to trigger a garbage
+ // collection, but may not take more than four arguments (no arguments
+ // passed on the stack), and the first argument will be a pointer to the
+ // return address.
+ inline void CallCFunctionUsingStub(ExternalReference function,
+ int num_arguments);
+
+ Isolate* isolate() const { return masm_->isolate(); }
+
+ MacroAssembler* masm_;
+
+ // Which mode to generate code for (ASCII or UC16).
+ Mode mode_;
+
+ // One greater than maximal register index actually used.
+ int num_registers_;
+
+ // Number of registers to output at the end (the saved registers
+ // are always 0..num_saved_registers_-1)
+ int num_saved_registers_;
+
+ // Manage a small pre-allocated pool for writing label targets
+ // to for pushing backtrack addresses.
+ int backtrack_constant_pool_offset_;
+ int backtrack_constant_pool_capacity_;
+
+ // Labels used internally.
+ Label entry_label_;
+ Label start_label_;
+ Label success_label_;
+ Label backtrack_label_;
+ Label exit_label_;
+ Label check_preempt_label_;
+ Label stack_overflow_label_;
+};
+
+#endif // V8_INTERPRETED_REGEXP
+
+
+}} // namespace v8::internal
+
+#endif // V8_ARM_REGEXP_MACRO_ASSEMBLER_ARM_H_
diff --git a/chromium/v8/src/arm/simulator-arm.cc b/chromium/v8/src/arm/simulator-arm.cc
new file mode 100644
index 00000000000..c9e3616d9da
--- /dev/null
+++ b/chromium/v8/src/arm/simulator-arm.cc
@@ -0,0 +1,3914 @@
+// Copyright 2012 the V8 project authors. 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 <cmath>
+#include <cstdarg>
+#include "v8.h"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "disasm.h"
+#include "assembler.h"
+#include "codegen.h"
+#include "arm/constants-arm.h"
+#include "arm/simulator-arm.h"
+
+#if defined(USE_SIMULATOR)
+
+// Only build the simulator if not compiling for real ARM hardware.
+namespace v8 {
+namespace internal {
+
+// This macro provides a platform independent use of sscanf. The reason for
+// SScanF not being implemented in a platform independent way through
+// ::v8::internal::OS in the same way as SNPrintF is that the
+// Windows C Run-Time Library does not provide vsscanf.
+#define SScanF sscanf // NOLINT
+
+// The ArmDebugger class is used by the simulator while debugging simulated ARM
+// code.
+class ArmDebugger {
+ public:
+ explicit ArmDebugger(Simulator* sim) : sim_(sim) { }
+ ~ArmDebugger();
+
+ void Stop(Instruction* instr);
+ void Debug();
+
+ private:
+ static const Instr kBreakpointInstr =
+ (al | (7*B25) | (1*B24) | kBreakpoint);
+ static const Instr kNopInstr = (al | (13*B21));
+
+ Simulator* sim_;
+
+ int32_t GetRegisterValue(int regnum);
+ double GetRegisterPairDoubleValue(int regnum);
+ double GetVFPDoubleRegisterValue(int regnum);
+ bool GetValue(const char* desc, int32_t* value);
+ bool GetVFPSingleValue(const char* desc, float* value);
+ bool GetVFPDoubleValue(const char* desc, double* value);
+
+ // Set or delete a breakpoint. Returns true if successful.
+ bool SetBreakpoint(Instruction* breakpc);
+ bool DeleteBreakpoint(Instruction* breakpc);
+
+ // Undo and redo all breakpoints. This is needed to bracket disassembly and
+ // execution to skip past breakpoints when run from the debugger.
+ void UndoBreakpoints();
+ void RedoBreakpoints();
+};
+
+
+ArmDebugger::~ArmDebugger() {
+}
+
+
+
+#ifdef GENERATED_CODE_COVERAGE
+static FILE* coverage_log = NULL;
+
+
+static void InitializeCoverage() {
+ char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG");
+ if (file_name != NULL) {
+ coverage_log = fopen(file_name, "aw+");
+ }
+}
+
+
+void ArmDebugger::Stop(Instruction* instr) {
+ // Get the stop code.
+ uint32_t code = instr->SvcValue() & kStopCodeMask;
+ // Retrieve the encoded address, which comes just after this stop.
+ char** msg_address =
+ reinterpret_cast<char**>(sim_->get_pc() + Instruction::kInstrSize);
+ char* msg = *msg_address;
+ ASSERT(msg != NULL);
+
+ // Update this stop description.
+ if (isWatchedStop(code) && !watched_stops_[code].desc) {
+ watched_stops_[code].desc = msg;
+ }
+
+ if (strlen(msg) > 0) {
+ if (coverage_log != NULL) {
+ fprintf(coverage_log, "%s\n", msg);
+ fflush(coverage_log);
+ }
+ // Overwrite the instruction and address with nops.
+ instr->SetInstructionBits(kNopInstr);
+ reinterpret_cast<Instruction*>(msg_address)->SetInstructionBits(kNopInstr);
+ }
+ sim_->set_pc(sim_->get_pc() + 2 * Instruction::kInstrSize);
+}
+
+#else // ndef GENERATED_CODE_COVERAGE
+
+static void InitializeCoverage() {
+}
+
+
+void ArmDebugger::Stop(Instruction* instr) {
+ // Get the stop code.
+ uint32_t code = instr->SvcValue() & kStopCodeMask;
+ // Retrieve the encoded address, which comes just after this stop.
+ char* msg = *reinterpret_cast<char**>(sim_->get_pc()
+ + Instruction::kInstrSize);
+ // Update this stop description.
+ if (sim_->isWatchedStop(code) && !sim_->watched_stops_[code].desc) {
+ sim_->watched_stops_[code].desc = msg;
+ }
+ // Print the stop message and code if it is not the default code.
+ if (code != kMaxStopCode) {
+ PrintF("Simulator hit stop %u: %s\n", code, msg);
+ } else {
+ PrintF("Simulator hit %s\n", msg);
+ }
+ sim_->set_pc(sim_->get_pc() + 2 * Instruction::kInstrSize);
+ Debug();
+}
+#endif
+
+
+int32_t ArmDebugger::GetRegisterValue(int regnum) {
+ if (regnum == kPCRegister) {
+ return sim_->get_pc();
+ } else {
+ return sim_->get_register(regnum);
+ }
+}
+
+
+double ArmDebugger::GetRegisterPairDoubleValue(int regnum) {
+ return sim_->get_double_from_register_pair(regnum);
+}
+
+
+double ArmDebugger::GetVFPDoubleRegisterValue(int regnum) {
+ return sim_->get_double_from_d_register(regnum);
+}
+
+
+bool ArmDebugger::GetValue(const char* desc, int32_t* value) {
+ int regnum = Registers::Number(desc);
+ if (regnum != kNoRegister) {
+ *value = GetRegisterValue(regnum);
+ return true;
+ } else {
+ if (strncmp(desc, "0x", 2) == 0) {
+ return SScanF(desc + 2, "%x", reinterpret_cast<uint32_t*>(value)) == 1;
+ } else {
+ return SScanF(desc, "%u", reinterpret_cast<uint32_t*>(value)) == 1;
+ }
+ }
+ return false;
+}
+
+
+bool ArmDebugger::GetVFPSingleValue(const char* desc, float* value) {
+ bool is_double;
+ int regnum = VFPRegisters::Number(desc, &is_double);
+ if (regnum != kNoRegister && !is_double) {
+ *value = sim_->get_float_from_s_register(regnum);
+ return true;
+ }
+ return false;
+}
+
+
+bool ArmDebugger::GetVFPDoubleValue(const char* desc, double* value) {
+ bool is_double;
+ int regnum = VFPRegisters::Number(desc, &is_double);
+ if (regnum != kNoRegister && is_double) {
+ *value = sim_->get_double_from_d_register(regnum);
+ return true;
+ }
+ return false;
+}
+
+
+bool ArmDebugger::SetBreakpoint(Instruction* breakpc) {
+ // Check if a breakpoint can be set. If not return without any side-effects.
+ if (sim_->break_pc_ != NULL) {
+ return false;
+ }
+
+ // Set the breakpoint.
+ sim_->break_pc_ = breakpc;
+ sim_->break_instr_ = breakpc->InstructionBits();
+ // Not setting the breakpoint instruction in the code itself. It will be set
+ // when the debugger shell continues.
+ return true;
+}
+
+
+bool ArmDebugger::DeleteBreakpoint(Instruction* breakpc) {
+ if (sim_->break_pc_ != NULL) {
+ sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
+ }
+
+ sim_->break_pc_ = NULL;
+ sim_->break_instr_ = 0;
+ return true;
+}
+
+
+void ArmDebugger::UndoBreakpoints() {
+ if (sim_->break_pc_ != NULL) {
+ sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
+ }
+}
+
+
+void ArmDebugger::RedoBreakpoints() {
+ if (sim_->break_pc_ != NULL) {
+ sim_->break_pc_->SetInstructionBits(kBreakpointInstr);
+ }
+}
+
+
+void ArmDebugger::Debug() {
+ intptr_t last_pc = -1;
+ bool done = false;
+
+#define COMMAND_SIZE 63
+#define ARG_SIZE 255
+
+#define STR(a) #a
+#define XSTR(a) STR(a)
+
+ char cmd[COMMAND_SIZE + 1];
+ char arg1[ARG_SIZE + 1];
+ char arg2[ARG_SIZE + 1];
+ char* argv[3] = { cmd, arg1, arg2 };
+
+ // make sure to have a proper terminating character if reaching the limit
+ cmd[COMMAND_SIZE] = 0;
+ arg1[ARG_SIZE] = 0;
+ arg2[ARG_SIZE] = 0;
+
+ // Undo all set breakpoints while running in the debugger shell. This will
+ // make them invisible to all commands.
+ UndoBreakpoints();
+
+ while (!done && !sim_->has_bad_pc()) {
+ if (last_pc != sim_->get_pc()) {
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // use a reasonably large buffer
+ v8::internal::EmbeddedVector<char, 256> buffer;
+ dasm.InstructionDecode(buffer,
+ reinterpret_cast<byte*>(sim_->get_pc()));
+ PrintF(" 0x%08x %s\n", sim_->get_pc(), buffer.start());
+ last_pc = sim_->get_pc();
+ }
+ char* line = ReadLine("sim> ");
+ 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,
+ "%" XSTR(COMMAND_SIZE) "s "
+ "%" XSTR(ARG_SIZE) "s "
+ "%" XSTR(ARG_SIZE) "s",
+ cmd, arg1, arg2);
+ if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
+ sim_->InstructionDecode(reinterpret_cast<Instruction*>(sim_->get_pc()));
+ } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
+ // Execute the one instruction we broke at with breakpoints disabled.
+ sim_->InstructionDecode(reinterpret_cast<Instruction*>(sim_->get_pc()));
+ // Leave the debugger shell.
+ done = true;
+ } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
+ if (argc == 2 || (argc == 3 && strcmp(arg2, "fp") == 0)) {
+ int32_t value;
+ float svalue;
+ double dvalue;
+ if (strcmp(arg1, "all") == 0) {
+ for (int i = 0; i < kNumRegisters; i++) {
+ value = GetRegisterValue(i);
+ PrintF("%3s: 0x%08x %10d", Registers::Name(i), value, value);
+ if ((argc == 3 && strcmp(arg2, "fp") == 0) &&
+ i < 8 &&
+ (i % 2) == 0) {
+ dvalue = GetRegisterPairDoubleValue(i);
+ PrintF(" (%f)\n", dvalue);
+ } else {
+ PrintF("\n");
+ }
+ }
+ for (int i = 0; i < DwVfpRegister::NumRegisters(); i++) {
+ dvalue = GetVFPDoubleRegisterValue(i);
+ uint64_t as_words = BitCast<uint64_t>(dvalue);
+ PrintF("%3s: %f 0x%08x %08x\n",
+ VFPRegisters::Name(i, true),
+ dvalue,
+ static_cast<uint32_t>(as_words >> 32),
+ static_cast<uint32_t>(as_words & 0xffffffff));
+ }
+ } else {
+ if (GetValue(arg1, &value)) {
+ PrintF("%s: 0x%08x %d \n", arg1, value, value);
+ } else if (GetVFPSingleValue(arg1, &svalue)) {
+ uint32_t as_word = BitCast<uint32_t>(svalue);
+ PrintF("%s: %f 0x%08x\n", arg1, svalue, as_word);
+ } else if (GetVFPDoubleValue(arg1, &dvalue)) {
+ uint64_t as_words = BitCast<uint64_t>(dvalue);
+ PrintF("%s: %f 0x%08x %08x\n",
+ arg1,
+ dvalue,
+ static_cast<uint32_t>(as_words >> 32),
+ static_cast<uint32_t>(as_words & 0xffffffff));
+ } else {
+ PrintF("%s unrecognized\n", arg1);
+ }
+ }
+ } else {
+ PrintF("print <register>\n");
+ }
+ } else if ((strcmp(cmd, "po") == 0)
+ || (strcmp(cmd, "printobject") == 0)) {
+ if (argc == 2) {
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ Object* obj = reinterpret_cast<Object*>(value);
+ PrintF("%s: \n", arg1);
+#ifdef DEBUG
+ obj->PrintLn();
+#else
+ obj->ShortPrint();
+ PrintF("\n");
+#endif
+ } else {
+ PrintF("%s unrecognized\n", arg1);
+ }
+ } else {
+ PrintF("printobject <value>\n");
+ }
+ } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) {
+ int32_t* cur = NULL;
+ int32_t* end = NULL;
+ int next_arg = 1;
+
+ if (strcmp(cmd, "stack") == 0) {
+ cur = reinterpret_cast<int32_t*>(sim_->get_register(Simulator::sp));
+ } else { // "mem"
+ int32_t value;
+ if (!GetValue(arg1, &value)) {
+ PrintF("%s unrecognized\n", arg1);
+ continue;
+ }
+ cur = reinterpret_cast<int32_t*>(value);
+ next_arg++;
+ }
+
+ int32_t words;
+ if (argc == next_arg) {
+ words = 10;
+ } else {
+ if (!GetValue(argv[next_arg], &words)) {
+ words = 10;
+ }
+ }
+ end = cur + words;
+
+ while (cur < end) {
+ PrintF(" 0x%08x: 0x%08x %10d",
+ reinterpret_cast<intptr_t>(cur), *cur, *cur);
+ HeapObject* obj = reinterpret_cast<HeapObject*>(*cur);
+ int value = *cur;
+ Heap* current_heap = v8::internal::Isolate::Current()->heap();
+ if (((value & 1) == 0) || current_heap->Contains(obj)) {
+ PrintF(" (");
+ if ((value & 1) == 0) {
+ PrintF("smi %d", value / 2);
+ } else {
+ obj->ShortPrint();
+ }
+ PrintF(")");
+ }
+ PrintF("\n");
+ cur++;
+ }
+ } else if (strcmp(cmd, "disasm") == 0 || strcmp(cmd, "di") == 0) {
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // use a reasonably large buffer
+ v8::internal::EmbeddedVector<char, 256> buffer;
+
+ byte* prev = NULL;
+ byte* cur = NULL;
+ byte* end = NULL;
+
+ if (argc == 1) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ end = cur + (10 * Instruction::kInstrSize);
+ } else if (argc == 2) {
+ int regnum = Registers::Number(arg1);
+ if (regnum != kNoRegister || strncmp(arg1, "0x", 2) == 0) {
+ // The argument is an address or a register name.
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(value);
+ // Disassemble 10 instructions at <arg1>.
+ end = cur + (10 * Instruction::kInstrSize);
+ }
+ } else {
+ // The argument is the number of instructions.
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ // Disassemble <arg1> instructions.
+ end = cur + (value * Instruction::kInstrSize);
+ }
+ }
+ } else {
+ int32_t value1;
+ int32_t value2;
+ if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) {
+ cur = reinterpret_cast<byte*>(value1);
+ end = cur + (value2 * Instruction::kInstrSize);
+ }
+ }
+
+ while (cur < end) {
+ prev = cur;
+ cur += dasm.InstructionDecode(buffer, cur);
+ PrintF(" 0x%08x %s\n",
+ reinterpret_cast<intptr_t>(prev), buffer.start());
+ }
+ } else if (strcmp(cmd, "gdb") == 0) {
+ PrintF("relinquishing control to gdb\n");
+ v8::internal::OS::DebugBreak();
+ PrintF("regaining control from gdb\n");
+ } else if (strcmp(cmd, "break") == 0) {
+ if (argc == 2) {
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ if (!SetBreakpoint(reinterpret_cast<Instruction*>(value))) {
+ PrintF("setting breakpoint failed\n");
+ }
+ } else {
+ PrintF("%s unrecognized\n", arg1);
+ }
+ } else {
+ PrintF("break <address>\n");
+ }
+ } else if (strcmp(cmd, "del") == 0) {
+ if (!DeleteBreakpoint(NULL)) {
+ PrintF("deleting breakpoint failed\n");
+ }
+ } else if (strcmp(cmd, "flags") == 0) {
+ PrintF("N flag: %d; ", sim_->n_flag_);
+ PrintF("Z flag: %d; ", sim_->z_flag_);
+ PrintF("C flag: %d; ", sim_->c_flag_);
+ PrintF("V flag: %d\n", sim_->v_flag_);
+ PrintF("INVALID OP flag: %d; ", sim_->inv_op_vfp_flag_);
+ PrintF("DIV BY ZERO flag: %d; ", sim_->div_zero_vfp_flag_);
+ PrintF("OVERFLOW flag: %d; ", sim_->overflow_vfp_flag_);
+ PrintF("UNDERFLOW flag: %d; ", sim_->underflow_vfp_flag_);
+ PrintF("INEXACT flag: %d;\n", sim_->inexact_vfp_flag_);
+ } else if (strcmp(cmd, "stop") == 0) {
+ int32_t value;
+ intptr_t stop_pc = sim_->get_pc() - 2 * Instruction::kInstrSize;
+ Instruction* stop_instr = reinterpret_cast<Instruction*>(stop_pc);
+ Instruction* msg_address =
+ reinterpret_cast<Instruction*>(stop_pc + Instruction::kInstrSize);
+ if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
+ // Remove the current stop.
+ if (sim_->isStopInstruction(stop_instr)) {
+ stop_instr->SetInstructionBits(kNopInstr);
+ msg_address->SetInstructionBits(kNopInstr);
+ } else {
+ PrintF("Not at debugger stop.\n");
+ }
+ } else if (argc == 3) {
+ // Print information about all/the specified breakpoint(s).
+ if (strcmp(arg1, "info") == 0) {
+ if (strcmp(arg2, "all") == 0) {
+ PrintF("Stop information:\n");
+ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+ sim_->PrintStopInfo(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->PrintStopInfo(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ } else if (strcmp(arg1, "enable") == 0) {
+ // Enable all/the specified breakpoint(s).
+ if (strcmp(arg2, "all") == 0) {
+ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+ sim_->EnableStop(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->EnableStop(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ } else if (strcmp(arg1, "disable") == 0) {
+ // Disable all/the specified breakpoint(s).
+ if (strcmp(arg2, "all") == 0) {
+ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+ sim_->DisableStop(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->DisableStop(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ }
+ } else {
+ PrintF("Wrong usage. Use help command for more information.\n");
+ }
+ } else if ((strcmp(cmd, "t") == 0) || strcmp(cmd, "trace") == 0) {
+ ::v8::internal::FLAG_trace_sim = !::v8::internal::FLAG_trace_sim;
+ PrintF("Trace of executed instructions is %s\n",
+ ::v8::internal::FLAG_trace_sim ? "on" : "off");
+ } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
+ PrintF("cont\n");
+ PrintF(" continue execution (alias 'c')\n");
+ PrintF("stepi\n");
+ PrintF(" step one instruction (alias 'si')\n");
+ PrintF("print <register>\n");
+ PrintF(" print register content (alias 'p')\n");
+ PrintF(" use register name 'all' to print all registers\n");
+ PrintF(" add argument 'fp' to print register pair double values\n");
+ PrintF("printobject <register>\n");
+ PrintF(" print an object from a register (alias 'po')\n");
+ PrintF("flags\n");
+ PrintF(" print flags\n");
+ PrintF("stack [<words>]\n");
+ PrintF(" dump stack content, default dump 10 words)\n");
+ PrintF("mem <address> [<words>]\n");
+ PrintF(" dump memory content, default dump 10 words)\n");
+ PrintF("disasm [<instructions>]\n");
+ PrintF("disasm [<address/register>]\n");
+ PrintF("disasm [[<address/register>] <instructions>]\n");
+ PrintF(" disassemble code, default is 10 instructions\n");
+ PrintF(" from pc (alias 'di')\n");
+ PrintF("gdb\n");
+ PrintF(" enter gdb\n");
+ PrintF("break <address>\n");
+ PrintF(" set a break point on the address\n");
+ PrintF("del\n");
+ PrintF(" delete the breakpoint\n");
+ PrintF("trace (alias 't')\n");
+ PrintF(" toogle the tracing of all executed statements\n");
+ PrintF("stop feature:\n");
+ PrintF(" Description:\n");
+ PrintF(" Stops are debug instructions inserted by\n");
+ PrintF(" the Assembler::stop() function.\n");
+ PrintF(" When hitting a stop, the Simulator will\n");
+ PrintF(" stop and and give control to the ArmDebugger.\n");
+ PrintF(" The first %d stop codes are watched:\n",
+ Simulator::kNumOfWatchedStops);
+ PrintF(" - They can be enabled / disabled: the Simulator\n");
+ PrintF(" will / won't stop when hitting them.\n");
+ PrintF(" - The Simulator keeps track of how many times they \n");
+ PrintF(" are met. (See the info command.) Going over a\n");
+ PrintF(" disabled stop still increases its counter. \n");
+ PrintF(" Commands:\n");
+ PrintF(" stop info all/<code> : print infos about number <code>\n");
+ PrintF(" or all stop(s).\n");
+ PrintF(" stop enable/disable all/<code> : enables / disables\n");
+ PrintF(" all or number <code> stop(s)\n");
+ PrintF(" stop unstop\n");
+ PrintF(" ignore the stop instruction at the current location\n");
+ PrintF(" from now on\n");
+ } else {
+ PrintF("Unknown command: %s\n", cmd);
+ }
+ }
+ }
+
+ // Add all the breakpoints back to stop execution and enter the debugger
+ // shell when hit.
+ RedoBreakpoints();
+
+#undef COMMAND_SIZE
+#undef ARG_SIZE
+
+#undef STR
+#undef XSTR
+}
+
+
+static bool ICacheMatch(void* one, void* two) {
+ ASSERT((reinterpret_cast<intptr_t>(one) & CachePage::kPageMask) == 0);
+ ASSERT((reinterpret_cast<intptr_t>(two) & CachePage::kPageMask) == 0);
+ return one == two;
+}
+
+
+static uint32_t ICacheHash(void* key) {
+ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key)) >> 2;
+}
+
+
+static bool AllOnOnePage(uintptr_t start, int size) {
+ intptr_t start_page = (start & ~CachePage::kPageMask);
+ intptr_t end_page = ((start + size) & ~CachePage::kPageMask);
+ return start_page == end_page;
+}
+
+
+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) {
+ intptr_t start = reinterpret_cast<intptr_t>(start_addr);
+ int intra_line = (start & CachePage::kLineMask);
+ start -= intra_line;
+ size += intra_line;
+ size = ((size - 1) | CachePage::kLineMask) + 1;
+ int offset = (start & CachePage::kPageMask);
+ while (!AllOnOnePage(start, size - 1)) {
+ int bytes_to_flush = CachePage::kPageSize - offset;
+ FlushOnePage(i_cache, start, bytes_to_flush);
+ start += bytes_to_flush;
+ size -= bytes_to_flush;
+ ASSERT_EQ(0, start & CachePage::kPageMask);
+ offset = 0;
+ }
+ if (size != 0) {
+ FlushOnePage(i_cache, start, size);
+ }
+}
+
+
+CachePage* Simulator::GetCachePage(v8::internal::HashMap* i_cache, void* page) {
+ v8::internal::HashMap::Entry* entry = i_cache->Lookup(page,
+ ICacheHash(page),
+ true);
+ if (entry->value == NULL) {
+ CachePage* new_page = new CachePage();
+ entry->value = new_page;
+ }
+ return reinterpret_cast<CachePage*>(entry->value);
+}
+
+
+// Flush from start up to and not including start + size.
+void Simulator::FlushOnePage(v8::internal::HashMap* i_cache,
+ intptr_t start,
+ int size) {
+ ASSERT(size <= CachePage::kPageSize);
+ ASSERT(AllOnOnePage(start, size - 1));
+ ASSERT((start & CachePage::kLineMask) == 0);
+ ASSERT((size & CachePage::kLineMask) == 0);
+ void* page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
+ int offset = (start & CachePage::kPageMask);
+ CachePage* cache_page = GetCachePage(i_cache, page);
+ char* valid_bytemap = cache_page->ValidityByte(offset);
+ memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
+}
+
+
+void Simulator::CheckICache(v8::internal::HashMap* i_cache,
+ Instruction* instr) {
+ intptr_t address = reinterpret_cast<intptr_t>(instr);
+ void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
+ void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
+ int offset = (address & CachePage::kPageMask);
+ CachePage* cache_page = GetCachePage(i_cache, page);
+ char* cache_valid_byte = cache_page->ValidityByte(offset);
+ bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
+ char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask);
+ if (cache_hit) {
+ // Check that the data in memory matches the contents of the I-cache.
+ CHECK(memcmp(reinterpret_cast<void*>(instr),
+ cache_page->CachedData(offset),
+ Instruction::kInstrSize) == 0);
+ } else {
+ // Cache miss. Load memory into the cache.
+ OS::MemCopy(cached_line, line, CachePage::kLineLength);
+ *cache_valid_byte = CachePage::LINE_VALID;
+ }
+}
+
+
+void Simulator::Initialize(Isolate* isolate) {
+ if (isolate->simulator_initialized()) return;
+ isolate->set_simulator_initialized(true);
+ ::v8::internal::ExternalReference::set_redirector(isolate,
+ &RedirectExternalReference);
+}
+
+
+Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
+ i_cache_ = isolate_->simulator_i_cache();
+ if (i_cache_ == NULL) {
+ i_cache_ = new v8::internal::HashMap(&ICacheMatch);
+ isolate_->set_simulator_i_cache(i_cache_);
+ }
+ Initialize(isolate);
+ // 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));
+ pc_modified_ = false;
+ icount_ = 0;
+ break_pc_ = NULL;
+ break_instr_ = 0;
+
+ // Set up architecture state.
+ // All registers are initialized to zero to start with.
+ for (int i = 0; i < num_registers; i++) {
+ registers_[i] = 0;
+ }
+ n_flag_ = false;
+ z_flag_ = false;
+ c_flag_ = false;
+ v_flag_ = false;
+
+ // Initializing VFP registers.
+ // All registers are initialized to zero to start with
+ // even though s_registers_ & d_registers_ share the same
+ // physical registers in the target.
+ for (int i = 0; i < num_d_registers * 2; i++) {
+ vfp_registers_[i] = 0;
+ }
+ n_flag_FPSCR_ = false;
+ z_flag_FPSCR_ = false;
+ c_flag_FPSCR_ = false;
+ v_flag_FPSCR_ = false;
+ FPSCR_rounding_mode_ = RZ;
+ FPSCR_default_NaN_mode_ = true;
+
+ inv_op_vfp_flag_ = false;
+ div_zero_vfp_flag_ = false;
+ overflow_vfp_flag_ = false;
+ underflow_vfp_flag_ = false;
+ inexact_vfp_flag_ = false;
+
+ // The sp is initialized to point to the bottom (high address) of the
+ // allocated stack area. To be safe in potential stack underflows we leave
+ // some buffer below.
+ registers_[sp] = reinterpret_cast<int32_t>(stack_) + stack_size - 64;
+ // The lr and pc are initialized to a known bad value that will cause an
+ // access violation if the simulator ever tries to execute it.
+ registers_[pc] = bad_lr;
+ registers_[lr] = bad_lr;
+ InitializeCoverage();
+
+ last_debugger_input_ = NULL;
+}
+
+
+// When the generated code calls an external reference we need to catch that in
+// the simulator. The external reference will be a function compiled for the
+// host architecture. We need to call that function instead of trying to
+// execute it with the simulator. We do that by redirecting the external
+// reference to a svc (Supervisor Call) instruction that is handled by
+// the simulator. We write the original destination of the jump just at a known
+// offset from the svc instruction so the simulator knows what to call.
+class Redirection {
+ public:
+ Redirection(void* external_function, ExternalReference::Type type)
+ : external_function_(external_function),
+ swi_instruction_(al | (0xf*B24) | kCallRtRedirected),
+ type_(type),
+ next_(NULL) {
+ Isolate* isolate = Isolate::Current();
+ next_ = isolate->simulator_redirection();
+ Simulator::current(isolate)->
+ FlushICache(isolate->simulator_i_cache(),
+ reinterpret_cast<void*>(&swi_instruction_),
+ Instruction::kInstrSize);
+ isolate->set_simulator_redirection(this);
+ }
+
+ void* address_of_swi_instruction() {
+ return reinterpret_cast<void*>(&swi_instruction_);
+ }
+
+ void* external_function() { return external_function_; }
+ ExternalReference::Type type() { return type_; }
+
+ static Redirection* Get(void* external_function,
+ ExternalReference::Type type) {
+ Isolate* isolate = Isolate::Current();
+ Redirection* current = isolate->simulator_redirection();
+ for (; current != NULL; current = current->next_) {
+ if (current->external_function_ == external_function) {
+ ASSERT_EQ(current->type(), type);
+ return current;
+ }
+ }
+ return new Redirection(external_function, type);
+ }
+
+ static Redirection* FromSwiInstruction(Instruction* swi_instruction) {
+ char* addr_of_swi = reinterpret_cast<char*>(swi_instruction);
+ char* addr_of_redirection =
+ addr_of_swi - OFFSET_OF(Redirection, swi_instruction_);
+ return reinterpret_cast<Redirection*>(addr_of_redirection);
+ }
+
+ private:
+ void* external_function_;
+ uint32_t swi_instruction_;
+ ExternalReference::Type type_;
+ Redirection* next_;
+};
+
+
+void* Simulator::RedirectExternalReference(void* external_function,
+ ExternalReference::Type type) {
+ Redirection* redirection = Redirection::Get(external_function, type);
+ return redirection->address_of_swi_instruction();
+}
+
+
+// Get the active Simulator for the current thread.
+Simulator* Simulator::current(Isolate* isolate) {
+ v8::internal::Isolate::PerIsolateThreadData* isolate_data =
+ isolate->FindOrAllocatePerThreadDataForThisThread();
+ ASSERT(isolate_data != NULL);
+
+ Simulator* sim = isolate_data->simulator();
+ if (sim == NULL) {
+ // TODO(146): delete the simulator object when a thread/isolate goes away.
+ sim = new Simulator(isolate);
+ isolate_data->set_simulator(sim);
+ }
+ return sim;
+}
+
+
+// Sets the register in the architecture state. It will also deal with updating
+// Simulator internal state for special registers such as PC.
+void Simulator::set_register(int reg, int32_t value) {
+ ASSERT((reg >= 0) && (reg < num_registers));
+ if (reg == pc) {
+ pc_modified_ = true;
+ }
+ registers_[reg] = value;
+}
+
+
+// Get the register from the architecture state. This function does handle
+// the special case of accessing the PC register.
+int32_t Simulator::get_register(int reg) const {
+ ASSERT((reg >= 0) && (reg < num_registers));
+ // Stupid code added to avoid bug in GCC.
+ // See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949
+ if (reg >= num_registers) return 0;
+ // End stupid code.
+ return registers_[reg] + ((reg == pc) ? Instruction::kPCReadOffset : 0);
+}
+
+
+double Simulator::get_double_from_register_pair(int reg) {
+ ASSERT((reg >= 0) && (reg < num_registers) && ((reg % 2) == 0));
+
+ double dm_val = 0.0;
+ // Read the bits from the unsigned integer register_[] array
+ // into the double precision floating point value and return it.
+ char buffer[2 * sizeof(vfp_registers_[0])];
+ OS::MemCopy(buffer, &registers_[reg], 2 * sizeof(registers_[0]));
+ OS::MemCopy(&dm_val, buffer, 2 * sizeof(registers_[0]));
+ return(dm_val);
+}
+
+
+void Simulator::set_dw_register(int dreg, const int* dbl) {
+ ASSERT((dreg >= 0) && (dreg < num_d_registers));
+ registers_[dreg] = dbl[0];
+ registers_[dreg + 1] = dbl[1];
+}
+
+
+void Simulator::get_d_register(int dreg, uint64_t* value) {
+ ASSERT((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters()));
+ memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value));
+}
+
+
+void Simulator::set_d_register(int dreg, const uint64_t* value) {
+ ASSERT((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters()));
+ memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value));
+}
+
+
+void Simulator::get_d_register(int dreg, uint32_t* value) {
+ ASSERT((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters()));
+ memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2);
+}
+
+
+void Simulator::set_d_register(int dreg, const uint32_t* value) {
+ ASSERT((dreg >= 0) && (dreg < DwVfpRegister::NumRegisters()));
+ memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2);
+}
+
+
+void Simulator::get_q_register(int qreg, uint64_t* value) {
+ ASSERT((qreg >= 0) && (qreg < num_q_registers));
+ memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 2);
+}
+
+
+void Simulator::set_q_register(int qreg, const uint64_t* value) {
+ ASSERT((qreg >= 0) && (qreg < num_q_registers));
+ memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 2);
+}
+
+
+void Simulator::get_q_register(int qreg, uint32_t* value) {
+ ASSERT((qreg >= 0) && (qreg < num_q_registers));
+ memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 4);
+}
+
+
+void Simulator::set_q_register(int qreg, const uint32_t* value) {
+ ASSERT((qreg >= 0) && (qreg < num_q_registers));
+ memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 4);
+}
+
+
+// Raw access to the PC register.
+void Simulator::set_pc(int32_t value) {
+ pc_modified_ = true;
+ registers_[pc] = value;
+}
+
+
+bool Simulator::has_bad_pc() const {
+ return ((registers_[pc] == bad_lr) || (registers_[pc] == end_sim_pc));
+}
+
+
+// Raw access to the PC register without the special adjustment when reading.
+int32_t Simulator::get_pc() const {
+ return registers_[pc];
+}
+
+
+// Getting from and setting into VFP registers.
+void Simulator::set_s_register(int sreg, unsigned int value) {
+ ASSERT((sreg >= 0) && (sreg < num_s_registers));
+ vfp_registers_[sreg] = value;
+}
+
+
+unsigned int Simulator::get_s_register(int sreg) const {
+ ASSERT((sreg >= 0) && (sreg < num_s_registers));
+ return vfp_registers_[sreg];
+}
+
+
+template<class InputType, int register_size>
+void Simulator::SetVFPRegister(int reg_index, const InputType& value) {
+ ASSERT(reg_index >= 0);
+ if (register_size == 1) ASSERT(reg_index < num_s_registers);
+ if (register_size == 2) ASSERT(reg_index < DwVfpRegister::NumRegisters());
+
+ char buffer[register_size * sizeof(vfp_registers_[0])];
+ OS::MemCopy(buffer, &value, register_size * sizeof(vfp_registers_[0]));
+ OS::MemCopy(&vfp_registers_[reg_index * register_size], buffer,
+ register_size * sizeof(vfp_registers_[0]));
+}
+
+
+template<class ReturnType, int register_size>
+ReturnType Simulator::GetFromVFPRegister(int reg_index) {
+ ASSERT(reg_index >= 0);
+ if (register_size == 1) ASSERT(reg_index < num_s_registers);
+ if (register_size == 2) ASSERT(reg_index < DwVfpRegister::NumRegisters());
+
+ ReturnType value = 0;
+ char buffer[register_size * sizeof(vfp_registers_[0])];
+ OS::MemCopy(buffer, &vfp_registers_[register_size * reg_index],
+ register_size * sizeof(vfp_registers_[0]));
+ OS::MemCopy(&value, buffer, register_size * sizeof(vfp_registers_[0]));
+ return value;
+}
+
+
+// Runtime FP routines take up to two double arguments and zero
+// or one integer arguments. All are consructed here.
+// from r0-r3 or d0 and d1.
+void Simulator::GetFpArgs(double* x, double* y, int32_t* z) {
+ if (use_eabi_hardfloat()) {
+ *x = vfp_registers_[0];
+ *y = vfp_registers_[1];
+ *z = registers_[1];
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ // Registers 0 and 1 -> x.
+ OS::MemCopy(buffer, registers_, sizeof(*x));
+ OS::MemCopy(x, buffer, sizeof(*x));
+ // Register 2 and 3 -> y.
+ OS::MemCopy(buffer, registers_ + 2, sizeof(*y));
+ OS::MemCopy(y, buffer, sizeof(*y));
+ // Register 2 -> z
+ memcpy(buffer, registers_ + 2, sizeof(*z));
+ memcpy(z, buffer, sizeof(*z));
+ }
+}
+
+
+// The return value is either in r0/r1 or d0.
+void Simulator::SetFpResult(const double& result) {
+ if (use_eabi_hardfloat()) {
+ char buffer[2 * sizeof(vfp_registers_[0])];
+ OS::MemCopy(buffer, &result, sizeof(buffer));
+ // Copy result to d0.
+ OS::MemCopy(vfp_registers_, buffer, sizeof(buffer));
+ } else {
+ char buffer[2 * sizeof(registers_[0])];
+ OS::MemCopy(buffer, &result, sizeof(buffer));
+ // Copy result to r0 and r1.
+ OS::MemCopy(registers_, buffer, sizeof(buffer));
+ }
+}
+
+
+void Simulator::TrashCallerSaveRegisters() {
+ // We don't trash the registers with the return value.
+ registers_[2] = 0x50Bad4U;
+ registers_[3] = 0x50Bad4U;
+ registers_[12] = 0x50Bad4U;
+}
+
+
+// Some Operating Systems allow unaligned access on ARMv7 targets. We
+// assume that unaligned accesses are not allowed unless the v8 build system
+// defines the CAN_USE_UNALIGNED_ACCESSES macro to be non-zero.
+// The following statements below describes the behavior of the ARM CPUs
+// that don't support unaligned access.
+// Some ARM platforms raise an interrupt on detecting unaligned access.
+// On others it does a funky rotation thing. For now we
+// simply disallow unaligned reads. Note that simulator runs have the runtime
+// system running directly on the host system and only generated code is
+// executed in the simulator. Since the host is typically IA32 we will not
+// get the correct ARM-like behaviour on unaligned accesses for those ARM
+// targets that don't support unaligned loads and stores.
+
+
+int Simulator::ReadW(int32_t addr, Instruction* instr) {
+ if (FLAG_enable_unaligned_accesses || (addr & 3) == 0) {
+ intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
+ return *ptr;
+ } else {
+ PrintF("Unaligned read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+
+void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
+ if (FLAG_enable_unaligned_accesses || (addr & 3) == 0) {
+ intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
+ *ptr = value;
+ } else {
+ PrintF("Unaligned write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ UNIMPLEMENTED();
+ }
+}
+
+
+uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) {
+ if (FLAG_enable_unaligned_accesses || (addr & 1) == 0) {
+ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
+ return *ptr;
+ } else {
+ PrintF("Unaligned unsigned halfword read at 0x%08x, pc=0x%08"
+ V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+
+int16_t Simulator::ReadH(int32_t addr, Instruction* instr) {
+ if (FLAG_enable_unaligned_accesses || (addr & 1) == 0) {
+ int16_t* ptr = reinterpret_cast<int16_t*>(addr);
+ return *ptr;
+ } else {
+ PrintF("Unaligned signed halfword read at 0x%08x\n", addr);
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+
+void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) {
+ if (FLAG_enable_unaligned_accesses || (addr & 1) == 0) {
+ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
+ *ptr = value;
+ } else {
+ PrintF("Unaligned unsigned halfword write at 0x%08x, pc=0x%08"
+ V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ UNIMPLEMENTED();
+ }
+}
+
+
+void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) {
+ if (FLAG_enable_unaligned_accesses || (addr & 1) == 0) {
+ int16_t* ptr = reinterpret_cast<int16_t*>(addr);
+ *ptr = value;
+ } else {
+ PrintF("Unaligned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ UNIMPLEMENTED();
+ }
+}
+
+
+uint8_t Simulator::ReadBU(int32_t addr) {
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
+ return *ptr;
+}
+
+
+int8_t Simulator::ReadB(int32_t addr) {
+ int8_t* ptr = reinterpret_cast<int8_t*>(addr);
+ return *ptr;
+}
+
+
+void Simulator::WriteB(int32_t addr, uint8_t value) {
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
+ *ptr = value;
+}
+
+
+void Simulator::WriteB(int32_t addr, int8_t value) {
+ int8_t* ptr = reinterpret_cast<int8_t*>(addr);
+ *ptr = value;
+}
+
+
+int32_t* Simulator::ReadDW(int32_t addr) {
+ if (FLAG_enable_unaligned_accesses || (addr & 3) == 0) {
+ int32_t* ptr = reinterpret_cast<int32_t*>(addr);
+ return ptr;
+ } else {
+ PrintF("Unaligned read at 0x%08x\n", addr);
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+
+void Simulator::WriteDW(int32_t addr, int32_t value1, int32_t value2) {
+ if (FLAG_enable_unaligned_accesses || (addr & 3) == 0) {
+ int32_t* ptr = reinterpret_cast<int32_t*>(addr);
+ *ptr++ = value1;
+ *ptr = value2;
+ } else {
+ PrintF("Unaligned write at 0x%08x\n", addr);
+ UNIMPLEMENTED();
+ }
+}
+
+
+// Returns the limit of the stack area to enable checking for stack overflows.
+uintptr_t Simulator::StackLimit() const {
+ // Leave a safety margin of 1024 bytes to prevent overrunning the stack when
+ // pushing values.
+ return reinterpret_cast<uintptr_t>(stack_) + 1024;
+}
+
+
+// Unsupported instructions use Format to print an error and stop execution.
+void Simulator::Format(Instruction* instr, const char* format) {
+ PrintF("Simulator found unsupported instruction:\n 0x%08x: %s\n",
+ reinterpret_cast<intptr_t>(instr), format);
+ UNIMPLEMENTED();
+}
+
+
+// Checks if the current instruction should be executed based on its
+// condition bits.
+bool Simulator::ConditionallyExecute(Instruction* instr) {
+ switch (instr->ConditionField()) {
+ case eq: return z_flag_;
+ case ne: return !z_flag_;
+ case cs: return c_flag_;
+ case cc: return !c_flag_;
+ case mi: return n_flag_;
+ case pl: return !n_flag_;
+ case vs: return v_flag_;
+ case vc: return !v_flag_;
+ case hi: return c_flag_ && !z_flag_;
+ case ls: return !c_flag_ || z_flag_;
+ case ge: return n_flag_ == v_flag_;
+ case lt: return n_flag_ != v_flag_;
+ case gt: return !z_flag_ && (n_flag_ == v_flag_);
+ case le: return z_flag_ || (n_flag_ != v_flag_);
+ case al: return true;
+ default: UNREACHABLE();
+ }
+ return false;
+}
+
+
+// Calculate and set the Negative and Zero flags.
+void Simulator::SetNZFlags(int32_t val) {
+ n_flag_ = (val < 0);
+ z_flag_ = (val == 0);
+}
+
+
+// Set the Carry flag.
+void Simulator::SetCFlag(bool val) {
+ c_flag_ = val;
+}
+
+
+// Set the oVerflow flag.
+void Simulator::SetVFlag(bool val) {
+ v_flag_ = val;
+}
+
+
+// Calculate C flag value for additions.
+bool Simulator::CarryFrom(int32_t left, int32_t right, int32_t carry) {
+ uint32_t uleft = static_cast<uint32_t>(left);
+ uint32_t uright = static_cast<uint32_t>(right);
+ uint32_t urest = 0xffffffffU - uleft;
+
+ return (uright > urest) ||
+ (carry && (((uright + 1) > urest) || (uright > (urest - 1))));
+}
+
+
+// Calculate C flag value for subtractions.
+bool Simulator::BorrowFrom(int32_t left, int32_t right) {
+ uint32_t uleft = static_cast<uint32_t>(left);
+ uint32_t uright = static_cast<uint32_t>(right);
+
+ return (uright > uleft);
+}
+
+
+// Calculate V flag value for additions and subtractions.
+bool Simulator::OverflowFrom(int32_t alu_out,
+ int32_t left, int32_t right, bool addition) {
+ bool overflow;
+ if (addition) {
+ // operands have the same sign
+ overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0))
+ // and operands and result have different sign
+ && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0));
+ } else {
+ // operands have different signs
+ overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0))
+ // and first operand and result have different signs
+ && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0));
+ }
+ return overflow;
+}
+
+
+// Support for VFP comparisons.
+void Simulator::Compute_FPSCR_Flags(double val1, double val2) {
+ if (std::isnan(val1) || std::isnan(val2)) {
+ n_flag_FPSCR_ = false;
+ z_flag_FPSCR_ = false;
+ c_flag_FPSCR_ = true;
+ v_flag_FPSCR_ = true;
+ // All non-NaN cases.
+ } else if (val1 == val2) {
+ n_flag_FPSCR_ = false;
+ z_flag_FPSCR_ = true;
+ c_flag_FPSCR_ = true;
+ v_flag_FPSCR_ = false;
+ } else if (val1 < val2) {
+ n_flag_FPSCR_ = true;
+ z_flag_FPSCR_ = false;
+ c_flag_FPSCR_ = false;
+ v_flag_FPSCR_ = false;
+ } else {
+ // Case when (val1 > val2).
+ n_flag_FPSCR_ = false;
+ z_flag_FPSCR_ = false;
+ c_flag_FPSCR_ = true;
+ v_flag_FPSCR_ = false;
+ }
+}
+
+
+void Simulator::Copy_FPSCR_to_APSR() {
+ n_flag_ = n_flag_FPSCR_;
+ z_flag_ = z_flag_FPSCR_;
+ c_flag_ = c_flag_FPSCR_;
+ v_flag_ = v_flag_FPSCR_;
+}
+
+
+// Addressing Mode 1 - Data-processing operands:
+// Get the value based on the shifter_operand with register.
+int32_t Simulator::GetShiftRm(Instruction* instr, bool* carry_out) {
+ ShiftOp shift = instr->ShiftField();
+ int shift_amount = instr->ShiftAmountValue();
+ int32_t result = get_register(instr->RmValue());
+ if (instr->Bit(4) == 0) {
+ // by immediate
+ if ((shift == ROR) && (shift_amount == 0)) {
+ UNIMPLEMENTED();
+ return result;
+ } else if (((shift == LSR) || (shift == ASR)) && (shift_amount == 0)) {
+ shift_amount = 32;
+ }
+ switch (shift) {
+ case ASR: {
+ if (shift_amount == 0) {
+ if (result < 0) {
+ result = 0xffffffff;
+ *carry_out = true;
+ } else {
+ result = 0;
+ *carry_out = false;
+ }
+ } else {
+ result >>= (shift_amount - 1);
+ *carry_out = (result & 1) == 1;
+ result >>= 1;
+ }
+ break;
+ }
+
+ case LSL: {
+ if (shift_amount == 0) {
+ *carry_out = c_flag_;
+ } else {
+ result <<= (shift_amount - 1);
+ *carry_out = (result < 0);
+ result <<= 1;
+ }
+ break;
+ }
+
+ case LSR: {
+ if (shift_amount == 0) {
+ result = 0;
+ *carry_out = c_flag_;
+ } else {
+ uint32_t uresult = static_cast<uint32_t>(result);
+ uresult >>= (shift_amount - 1);
+ *carry_out = (uresult & 1) == 1;
+ uresult >>= 1;
+ result = static_cast<int32_t>(uresult);
+ }
+ break;
+ }
+
+ case ROR: {
+ if (shift_amount == 0) {
+ *carry_out = c_flag_;
+ } else {
+ uint32_t left = static_cast<uint32_t>(result) >> shift_amount;
+ uint32_t right = static_cast<uint32_t>(result) << (32 - shift_amount);
+ result = right | left;
+ *carry_out = (static_cast<uint32_t>(result) >> 31) != 0;
+ }
+ break;
+ }
+
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else {
+ // by register
+ int rs = instr->RsValue();
+ shift_amount = get_register(rs) &0xff;
+ switch (shift) {
+ case ASR: {
+ if (shift_amount == 0) {
+ *carry_out = c_flag_;
+ } else if (shift_amount < 32) {
+ result >>= (shift_amount - 1);
+ *carry_out = (result & 1) == 1;
+ result >>= 1;
+ } else {
+ ASSERT(shift_amount >= 32);
+ if (result < 0) {
+ *carry_out = true;
+ result = 0xffffffff;
+ } else {
+ *carry_out = false;
+ result = 0;
+ }
+ }
+ break;
+ }
+
+ case LSL: {
+ if (shift_amount == 0) {
+ *carry_out = c_flag_;
+ } else if (shift_amount < 32) {
+ result <<= (shift_amount - 1);
+ *carry_out = (result < 0);
+ result <<= 1;
+ } else if (shift_amount == 32) {
+ *carry_out = (result & 1) == 1;
+ result = 0;
+ } else {
+ ASSERT(shift_amount > 32);
+ *carry_out = false;
+ result = 0;
+ }
+ break;
+ }
+
+ case LSR: {
+ if (shift_amount == 0) {
+ *carry_out = c_flag_;
+ } else if (shift_amount < 32) {
+ uint32_t uresult = static_cast<uint32_t>(result);
+ uresult >>= (shift_amount - 1);
+ *carry_out = (uresult & 1) == 1;
+ uresult >>= 1;
+ result = static_cast<int32_t>(uresult);
+ } else if (shift_amount == 32) {
+ *carry_out = (result < 0);
+ result = 0;
+ } else {
+ *carry_out = false;
+ result = 0;
+ }
+ break;
+ }
+
+ case ROR: {
+ if (shift_amount == 0) {
+ *carry_out = c_flag_;
+ } else {
+ uint32_t left = static_cast<uint32_t>(result) >> shift_amount;
+ uint32_t right = static_cast<uint32_t>(result) << (32 - shift_amount);
+ result = right | left;
+ *carry_out = (static_cast<uint32_t>(result) >> 31) != 0;
+ }
+ break;
+ }
+
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+
+// Addressing Mode 1 - Data-processing operands:
+// Get the value based on the shifter_operand with immediate.
+int32_t Simulator::GetImm(Instruction* instr, bool* carry_out) {
+ int rotate = instr->RotateValue() * 2;
+ int immed8 = instr->Immed8Value();
+ int imm = (immed8 >> rotate) | (immed8 << (32 - rotate));
+ *carry_out = (rotate == 0) ? c_flag_ : (imm < 0);
+ return imm;
+}
+
+
+static int count_bits(int bit_vector) {
+ int count = 0;
+ while (bit_vector != 0) {
+ if ((bit_vector & 1) != 0) {
+ count++;
+ }
+ bit_vector >>= 1;
+ }
+ return count;
+}
+
+
+int32_t Simulator::ProcessPU(Instruction* instr,
+ int num_regs,
+ int reg_size,
+ intptr_t* start_address,
+ intptr_t* end_address) {
+ int rn = instr->RnValue();
+ int32_t rn_val = get_register(rn);
+ switch (instr->PUField()) {
+ case da_x: {
+ UNIMPLEMENTED();
+ break;
+ }
+ case ia_x: {
+ *start_address = rn_val;
+ *end_address = rn_val + (num_regs * reg_size) - reg_size;
+ rn_val = rn_val + (num_regs * reg_size);
+ break;
+ }
+ case db_x: {
+ *start_address = rn_val - (num_regs * reg_size);
+ *end_address = rn_val - reg_size;
+ rn_val = *start_address;
+ break;
+ }
+ case ib_x: {
+ *start_address = rn_val + reg_size;
+ *end_address = rn_val + (num_regs * reg_size);
+ rn_val = *end_address;
+ break;
+ }
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ return rn_val;
+}
+
+
+// Addressing Mode 4 - Load and Store Multiple
+void Simulator::HandleRList(Instruction* instr, bool load) {
+ int rlist = instr->RlistValue();
+ int num_regs = count_bits(rlist);
+
+ intptr_t start_address = 0;
+ intptr_t end_address = 0;
+ int32_t rn_val =
+ ProcessPU(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) {
+ if (load) {
+ set_register(reg, *address);
+ } else {
+ *address = get_register(reg);
+ }
+ address += 1;
+ }
+ reg++;
+ rlist >>= 1;
+ }
+ ASSERT(end_address == ((intptr_t)address) - 4);
+ if (instr->HasW()) {
+ set_register(instr->RnValue(), rn_val);
+ }
+}
+
+
+// Addressing Mode 6 - Load and Store Multiple Coprocessor registers.
+void Simulator::HandleVList(Instruction* instr) {
+ VFPRegPrecision precision =
+ (instr->SzValue() == 0) ? kSinglePrecision : kDoublePrecision;
+ int operand_size = (precision == kSinglePrecision) ? 4 : 8;
+
+ bool load = (instr->VLValue() == 0x1);
+
+ int vd;
+ int num_regs;
+ vd = instr->VFPDRegValue(precision);
+ if (precision == kSinglePrecision) {
+ num_regs = instr->Immed8Value();
+ } else {
+ num_regs = instr->Immed8Value() / 2;
+ }
+
+ intptr_t start_address = 0;
+ intptr_t end_address = 0;
+ int32_t rn_val =
+ ProcessPU(instr, num_regs, operand_size, &start_address, &end_address);
+
+ intptr_t* address = reinterpret_cast<intptr_t*>(start_address);
+ for (int reg = vd; reg < vd + num_regs; reg++) {
+ if (precision == kSinglePrecision) {
+ if (load) {
+ set_s_register_from_sinteger(
+ reg, ReadW(reinterpret_cast<int32_t>(address), instr));
+ } else {
+ WriteW(reinterpret_cast<int32_t>(address),
+ get_sinteger_from_s_register(reg), instr);
+ }
+ address += 1;
+ } else {
+ if (load) {
+ int32_t data[] = {
+ ReadW(reinterpret_cast<int32_t>(address), instr),
+ ReadW(reinterpret_cast<int32_t>(address + 1), instr)
+ };
+ double d;
+ OS::MemCopy(&d, data, 8);
+ set_d_register_from_double(reg, d);
+ } else {
+ int32_t data[2];
+ double d = get_double_from_d_register(reg);
+ OS::MemCopy(data, &d, 8);
+ WriteW(reinterpret_cast<int32_t>(address), data[0], instr);
+ WriteW(reinterpret_cast<int32_t>(address + 1), data[1], instr);
+ }
+ address += 2;
+ }
+ }
+ ASSERT(reinterpret_cast<intptr_t>(address) - operand_size == end_address);
+ if (instr->HasW()) {
+ set_register(instr->RnValue(), rn_val);
+ }
+}
+
+
+// Calls into the V8 runtime are based on this very simple interface.
+// Note: To be able to return two values from some calls the code in runtime.cc
+// uses the ObjectPair which is essentially two 32-bit values stuffed into a
+// 64-bit value. With the code below we assume that all runtime calls return
+// 64 bits of result. If they don't, the r1 result register contains a bogus
+// value, which is fine because it is caller-saved.
+typedef int64_t (*SimulatorRuntimeCall)(int32_t arg0,
+ int32_t arg1,
+ int32_t arg2,
+ int32_t arg3,
+ int32_t arg4,
+ int32_t arg5);
+
+// These prototypes handle the four types of FP calls.
+typedef int64_t (*SimulatorRuntimeCompareCall)(double darg0, double darg1);
+typedef double (*SimulatorRuntimeFPFPCall)(double darg0, double darg1);
+typedef double (*SimulatorRuntimeFPCall)(double darg0);
+typedef double (*SimulatorRuntimeFPIntCall)(double darg0, int32_t arg0);
+
+// This signature supports direct call in to API function native callback
+// (refer to InvocationCallback in v8.h).
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeDirectApiCall)(int32_t arg0);
+typedef void (*SimulatorRuntimeDirectApiCallNew)(int32_t arg0);
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeProfilingApiCall)(
+ int32_t arg0, int32_t arg1);
+typedef void (*SimulatorRuntimeProfilingApiCallNew)(int32_t arg0, int32_t arg1);
+
+// This signature supports direct call to accessor getter callback.
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeDirectGetterCall)(int32_t arg0,
+ int32_t arg1);
+typedef void (*SimulatorRuntimeDirectGetterCallNew)(int32_t arg0,
+ int32_t arg1);
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeProfilingGetterCall)(
+ int32_t arg0, int32_t arg1, int32_t arg2);
+typedef void (*SimulatorRuntimeProfilingGetterCallNew)(
+ int32_t arg0, int32_t arg1, int32_t arg2);
+
+// Software interrupt instructions are used by the simulator to call into the
+// C-based V8 runtime.
+void Simulator::SoftwareInterrupt(Instruction* instr) {
+ int svc = instr->SvcValue();
+ switch (svc) {
+ case kCallRtRedirected: {
+ // Check if stack is aligned. Error if not aligned is reported below to
+ // include information on the function called.
+ bool stack_aligned =
+ (get_register(sp)
+ & (::v8::internal::FLAG_sim_stack_alignment - 1)) == 0;
+ Redirection* redirection = Redirection::FromSwiInstruction(instr);
+ int32_t arg0 = get_register(r0);
+ int32_t arg1 = get_register(r1);
+ int32_t arg2 = get_register(r2);
+ int32_t arg3 = get_register(r3);
+ int32_t* stack_pointer = reinterpret_cast<int32_t*>(get_register(sp));
+ int32_t arg4 = stack_pointer[0];
+ int32_t arg5 = stack_pointer[1];
+ bool fp_call =
+ (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_FP_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL);
+ if (use_eabi_hardfloat()) {
+ // With the hard floating point calling convention, double
+ // arguments are passed in VFP registers. Fetch the arguments
+ // from there and call the builtin using soft floating point
+ // convention.
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ arg0 = vfp_registers_[0];
+ arg1 = vfp_registers_[1];
+ arg2 = vfp_registers_[2];
+ arg3 = vfp_registers_[3];
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ arg0 = vfp_registers_[0];
+ arg1 = vfp_registers_[1];
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ arg0 = vfp_registers_[0];
+ arg1 = vfp_registers_[1];
+ arg2 = get_register(0);
+ break;
+ default:
+ break;
+ }
+ }
+ // This is dodgy but it works because the C entry stubs are never moved.
+ // See comment in codegen-arm.cc and bug 1242173.
+ int32_t saved_lr = get_register(lr);
+ intptr_t external =
+ reinterpret_cast<intptr_t>(redirection->external_function());
+ if (fp_call) {
+ double dval0, dval1; // one or two double parameters
+ int32_t ival; // zero or one integer parameters
+ int64_t iresult = 0; // integer return value
+ double dresult = 0; // double return value
+ GetFpArgs(&dval0, &dval1, &ival);
+ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
+ SimulatorRuntimeCall generic_target =
+ reinterpret_cast<SimulatorRuntimeCall>(external);
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ PrintF("Call to host function at %p with args %f, %f",
+ FUNCTION_ADDR(generic_target), dval0, dval1);
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ PrintF("Call to host function at %p with arg %f",
+ FUNCTION_ADDR(generic_target), dval0);
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ PrintF("Call to host function at %p with args %f, %d",
+ FUNCTION_ADDR(generic_target), dval0, ival);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ if (!stack_aligned) {
+ PrintF(" with unaligned stack %08x\n", get_register(sp));
+ }
+ PrintF("\n");
+ }
+ CHECK(stack_aligned);
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_COMPARE_CALL: {
+ SimulatorRuntimeCompareCall target =
+ reinterpret_cast<SimulatorRuntimeCompareCall>(external);
+ iresult = target(dval0, dval1);
+ set_register(r0, static_cast<int32_t>(iresult));
+ set_register(r1, static_cast<int32_t>(iresult >> 32));
+ break;
+ }
+ case ExternalReference::BUILTIN_FP_FP_CALL: {
+ SimulatorRuntimeFPFPCall target =
+ reinterpret_cast<SimulatorRuntimeFPFPCall>(external);
+ dresult = target(dval0, dval1);
+ SetFpResult(dresult);
+ break;
+ }
+ case ExternalReference::BUILTIN_FP_CALL: {
+ SimulatorRuntimeFPCall target =
+ reinterpret_cast<SimulatorRuntimeFPCall>(external);
+ dresult = target(dval0);
+ SetFpResult(dresult);
+ break;
+ }
+ case ExternalReference::BUILTIN_FP_INT_CALL: {
+ SimulatorRuntimeFPIntCall target =
+ reinterpret_cast<SimulatorRuntimeFPIntCall>(external);
+ dresult = target(dval0, ival);
+ SetFpResult(dresult);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ PrintF("Returned %08x\n", static_cast<int32_t>(iresult));
+ break;
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_FP_CALL:
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ PrintF("Returned %f\n", dresult);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else if (
+ redirection->type() == ExternalReference::DIRECT_API_CALL ||
+ redirection->type() == ExternalReference::DIRECT_API_CALL_NEW) {
+ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
+ PrintF("Call to host function at %p args %08x",
+ reinterpret_cast<void*>(external), arg0);
+ if (!stack_aligned) {
+ PrintF(" with unaligned stack %08x\n", get_register(sp));
+ }
+ PrintF("\n");
+ }
+ CHECK(stack_aligned);
+ if (redirection->type() == ExternalReference::DIRECT_API_CALL) {
+ SimulatorRuntimeDirectApiCall target =
+ reinterpret_cast<SimulatorRuntimeDirectApiCall>(external);
+ v8::Handle<v8::Value> result = target(arg0);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %p\n", reinterpret_cast<void *>(*result));
+ }
+ set_register(r0, reinterpret_cast<int32_t>(*result));
+ } else {
+ SimulatorRuntimeDirectApiCallNew target =
+ reinterpret_cast<SimulatorRuntimeDirectApiCallNew>(external);
+ target(arg0);
+ }
+ } else if (
+ redirection->type() == ExternalReference::PROFILING_API_CALL ||
+ redirection->type() == ExternalReference::PROFILING_API_CALL_NEW) {
+ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
+ PrintF("Call to host function at %p args %08x %08x",
+ reinterpret_cast<void*>(external), arg0, arg1);
+ if (!stack_aligned) {
+ PrintF(" with unaligned stack %08x\n", get_register(sp));
+ }
+ PrintF("\n");
+ }
+ CHECK(stack_aligned);
+ if (redirection->type() == ExternalReference::PROFILING_API_CALL) {
+ SimulatorRuntimeProfilingApiCall target =
+ reinterpret_cast<SimulatorRuntimeProfilingApiCall>(external);
+ v8::Handle<v8::Value> result = target(arg0, arg1);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %p\n", reinterpret_cast<void *>(*result));
+ }
+ set_register(r0, reinterpret_cast<int32_t>(*result));
+ } else {
+ SimulatorRuntimeProfilingApiCallNew target =
+ reinterpret_cast<SimulatorRuntimeProfilingApiCallNew>(external);
+ target(arg0, arg1);
+ }
+ } else if (
+ redirection->type() == ExternalReference::DIRECT_GETTER_CALL ||
+ redirection->type() == ExternalReference::DIRECT_GETTER_CALL_NEW) {
+ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
+ PrintF("Call to host function at %p args %08x %08x",
+ reinterpret_cast<void*>(external), arg0, arg1);
+ if (!stack_aligned) {
+ PrintF(" with unaligned stack %08x\n", get_register(sp));
+ }
+ PrintF("\n");
+ }
+ CHECK(stack_aligned);
+ if (redirection->type() == ExternalReference::DIRECT_GETTER_CALL) {
+ SimulatorRuntimeDirectGetterCall target =
+ reinterpret_cast<SimulatorRuntimeDirectGetterCall>(external);
+ v8::Handle<v8::Value> result = target(arg0, arg1);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %p\n", reinterpret_cast<void *>(*result));
+ }
+ set_register(r0, reinterpret_cast<int32_t>(*result));
+ } else {
+ SimulatorRuntimeDirectGetterCallNew target =
+ reinterpret_cast<SimulatorRuntimeDirectGetterCallNew>(external);
+ target(arg0, arg1);
+ }
+ } else if (
+ redirection->type() == ExternalReference::PROFILING_GETTER_CALL ||
+ redirection->type() == ExternalReference::PROFILING_GETTER_CALL_NEW) {
+ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
+ PrintF("Call to host function at %p args %08x %08x %08x",
+ reinterpret_cast<void*>(external), arg0, arg1, arg2);
+ if (!stack_aligned) {
+ PrintF(" with unaligned stack %08x\n", get_register(sp));
+ }
+ PrintF("\n");
+ }
+ CHECK(stack_aligned);
+ if (redirection->type() == ExternalReference::PROFILING_GETTER_CALL) {
+ SimulatorRuntimeProfilingGetterCall target =
+ reinterpret_cast<SimulatorRuntimeProfilingGetterCall>(external);
+ v8::Handle<v8::Value> result = target(arg0, arg1, arg2);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %p\n", reinterpret_cast<void *>(*result));
+ }
+ set_register(r0, reinterpret_cast<int32_t>(*result));
+ } else {
+ SimulatorRuntimeProfilingGetterCallNew target =
+ reinterpret_cast<SimulatorRuntimeProfilingGetterCallNew>(
+ external);
+ target(arg0, arg1, arg2);
+ }
+ } else {
+ // builtin call.
+ ASSERT(redirection->type() == ExternalReference::BUILTIN_CALL);
+ SimulatorRuntimeCall target =
+ reinterpret_cast<SimulatorRuntimeCall>(external);
+ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
+ PrintF(
+ "Call to host function at %p "
+ "args %08x, %08x, %08x, %08x, %08x, %08x",
+ FUNCTION_ADDR(target),
+ arg0,
+ arg1,
+ arg2,
+ arg3,
+ arg4,
+ arg5);
+ if (!stack_aligned) {
+ PrintF(" with unaligned stack %08x\n", get_register(sp));
+ }
+ PrintF("\n");
+ }
+ CHECK(stack_aligned);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
+ int32_t lo_res = static_cast<int32_t>(result);
+ int32_t hi_res = static_cast<int32_t>(result >> 32);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %08x\n", lo_res);
+ }
+ set_register(r0, lo_res);
+ set_register(r1, hi_res);
+ }
+ set_register(lr, saved_lr);
+ set_pc(get_register(lr));
+ break;
+ }
+ case kBreakpoint: {
+ ArmDebugger dbg(this);
+ dbg.Debug();
+ break;
+ }
+ // stop uses all codes greater than 1 << 23.
+ default: {
+ if (svc >= (1 << 23)) {
+ uint32_t code = svc & kStopCodeMask;
+ if (isWatchedStop(code)) {
+ IncreaseStopCounter(code);
+ }
+ // Stop if it is enabled, otherwise go on jumping over the stop
+ // and the message address.
+ if (isEnabledStop(code)) {
+ ArmDebugger dbg(this);
+ dbg.Stop(instr);
+ } else {
+ set_pc(get_pc() + 2 * Instruction::kInstrSize);
+ }
+ } else {
+ // This is not a valid svc code.
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+}
+
+
+double Simulator::canonicalizeNaN(double value) {
+ return (FPSCR_default_NaN_mode_ && std::isnan(value)) ?
+ FixedDoubleArray::canonical_not_the_hole_nan_as_double() : value;
+}
+
+
+// Stop helper functions.
+bool Simulator::isStopInstruction(Instruction* instr) {
+ return (instr->Bits(27, 24) == 0xF) && (instr->SvcValue() >= kStopCode);
+}
+
+
+bool Simulator::isWatchedStop(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ return code < kNumOfWatchedStops;
+}
+
+
+bool Simulator::isEnabledStop(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ // Unwatched stops are always enabled.
+ return !isWatchedStop(code) ||
+ !(watched_stops_[code].count & kStopDisabledBit);
+}
+
+
+void Simulator::EnableStop(uint32_t code) {
+ ASSERT(isWatchedStop(code));
+ if (!isEnabledStop(code)) {
+ watched_stops_[code].count &= ~kStopDisabledBit;
+ }
+}
+
+
+void Simulator::DisableStop(uint32_t code) {
+ ASSERT(isWatchedStop(code));
+ if (isEnabledStop(code)) {
+ watched_stops_[code].count |= kStopDisabledBit;
+ }
+}
+
+
+void Simulator::IncreaseStopCounter(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ ASSERT(isWatchedStop(code));
+ if ((watched_stops_[code].count & ~(1 << 31)) == 0x7fffffff) {
+ PrintF("Stop counter for code %i has overflowed.\n"
+ "Enabling this code and reseting the counter to 0.\n", code);
+ watched_stops_[code].count = 0;
+ EnableStop(code);
+ } else {
+ watched_stops_[code].count++;
+ }
+}
+
+
+// Print a stop status.
+void Simulator::PrintStopInfo(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ if (!isWatchedStop(code)) {
+ PrintF("Stop not watched.");
+ } else {
+ const char* state = isEnabledStop(code) ? "Enabled" : "Disabled";
+ int32_t count = watched_stops_[code].count & ~kStopDisabledBit;
+ // Don't print the state of unused breakpoints.
+ if (count != 0) {
+ if (watched_stops_[code].desc) {
+ PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n",
+ code, code, state, count, watched_stops_[code].desc);
+ } else {
+ PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n",
+ code, code, state, count);
+ }
+ }
+ }
+}
+
+
+// Handle execution based on instruction types.
+
+// Instruction types 0 and 1 are both rolled into one function because they
+// only differ in the handling of the shifter_operand.
+void Simulator::DecodeType01(Instruction* instr) {
+ int type = instr->TypeValue();
+ if ((type == 0) && instr->IsSpecialType0()) {
+ // multiply instruction or extra loads and stores
+ if (instr->Bits(7, 4) == 9) {
+ if (instr->Bit(24) == 0) {
+ // Raw field decoding here. Multiply instructions have their Rd in
+ // funny places.
+ int rn = instr->RnValue();
+ int rm = instr->RmValue();
+ int rs = instr->RsValue();
+ int32_t rs_val = get_register(rs);
+ int32_t rm_val = get_register(rm);
+ if (instr->Bit(23) == 0) {
+ if (instr->Bit(21) == 0) {
+ // The MUL instruction description (A 4.1.33) refers to Rd as being
+ // the destination for the operation, but it confusingly uses the
+ // Rn field to encode it.
+ // Format(instr, "mul'cond's 'rn, 'rm, 'rs");
+ int rd = rn; // Remap the rn field to the Rd register.
+ int32_t alu_out = rm_val * rs_val;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ }
+ } else {
+ int rd = instr->RdValue();
+ int32_t acc_value = get_register(rd);
+ if (instr->Bit(22) == 0) {
+ // The MLA instruction description (A 4.1.28) refers to the order
+ // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the
+ // Rn field to encode the Rd register and the Rd field to encode
+ // the Rn register.
+ // Format(instr, "mla'cond's 'rn, 'rm, 'rs, 'rd");
+ int32_t mul_out = rm_val * rs_val;
+ int32_t result = acc_value + mul_out;
+ set_register(rn, result);
+ } else {
+ // Format(instr, "mls'cond's 'rn, 'rm, 'rs, 'rd");
+ int32_t mul_out = rm_val * rs_val;
+ int32_t result = acc_value - mul_out;
+ set_register(rn, result);
+ }
+ }
+ } else {
+ // The signed/long multiply instructions use the terms RdHi and RdLo
+ // when referring to the target registers. They are mapped to the Rn
+ // and Rd fields as follows:
+ // RdLo == Rd
+ // RdHi == Rn (This is confusingly stored in variable rd here
+ // because the mul instruction from above uses the
+ // Rn field to encode the Rd register. Good luck figuring
+ // this out without reading the ARM instruction manual
+ // at a very detailed level.)
+ // Format(instr, "'um'al'cond's 'rd, 'rn, 'rs, 'rm");
+ int rd_hi = rn; // Remap the rn field to the RdHi register.
+ int rd_lo = instr->RdValue();
+ int32_t hi_res = 0;
+ int32_t lo_res = 0;
+ if (instr->Bit(22) == 1) {
+ int64_t left_op = static_cast<int32_t>(rm_val);
+ int64_t right_op = static_cast<int32_t>(rs_val);
+ uint64_t result = left_op * right_op;
+ hi_res = static_cast<int32_t>(result >> 32);
+ lo_res = static_cast<int32_t>(result & 0xffffffff);
+ } else {
+ // unsigned multiply
+ uint64_t left_op = static_cast<uint32_t>(rm_val);
+ uint64_t right_op = static_cast<uint32_t>(rs_val);
+ uint64_t result = left_op * right_op;
+ hi_res = static_cast<int32_t>(result >> 32);
+ lo_res = static_cast<int32_t>(result & 0xffffffff);
+ }
+ set_register(rd_lo, lo_res);
+ set_register(rd_hi, hi_res);
+ if (instr->HasS()) {
+ UNIMPLEMENTED();
+ }
+ }
+ } else {
+ UNIMPLEMENTED(); // Not used by V8.
+ }
+ } else {
+ // extra load/store instructions
+ int rd = instr->RdValue();
+ int rn = instr->RnValue();
+ int32_t rn_val = get_register(rn);
+ int32_t addr = 0;
+ if (instr->Bit(22) == 0) {
+ int rm = instr->RmValue();
+ int32_t rm_val = get_register(rm);
+ switch (instr->PUField()) {
+ case da_x: {
+ // Format(instr, "'memop'cond'sign'h 'rd, ['rn], -'rm");
+ ASSERT(!instr->HasW());
+ addr = rn_val;
+ rn_val -= rm_val;
+ set_register(rn, rn_val);
+ break;
+ }
+ case ia_x: {
+ // Format(instr, "'memop'cond'sign'h 'rd, ['rn], +'rm");
+ ASSERT(!instr->HasW());
+ addr = rn_val;
+ rn_val += rm_val;
+ set_register(rn, rn_val);
+ break;
+ }
+ case db_x: {
+ // Format(instr, "'memop'cond'sign'h 'rd, ['rn, -'rm]'w");
+ rn_val -= rm_val;
+ addr = rn_val;
+ if (instr->HasW()) {
+ set_register(rn, rn_val);
+ }
+ break;
+ }
+ case ib_x: {
+ // Format(instr, "'memop'cond'sign'h 'rd, ['rn, +'rm]'w");
+ rn_val += rm_val;
+ addr = rn_val;
+ if (instr->HasW()) {
+ set_register(rn, rn_val);
+ }
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else {
+ int32_t imm_val = (instr->ImmedHValue() << 4) | instr->ImmedLValue();
+ switch (instr->PUField()) {
+ case da_x: {
+ // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #-'off8");
+ ASSERT(!instr->HasW());
+ addr = rn_val;
+ rn_val -= imm_val;
+ set_register(rn, rn_val);
+ break;
+ }
+ case ia_x: {
+ // Format(instr, "'memop'cond'sign'h 'rd, ['rn], #+'off8");
+ ASSERT(!instr->HasW());
+ addr = rn_val;
+ rn_val += imm_val;
+ set_register(rn, rn_val);
+ break;
+ }
+ case db_x: {
+ // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #-'off8]'w");
+ rn_val -= imm_val;
+ addr = rn_val;
+ if (instr->HasW()) {
+ set_register(rn, rn_val);
+ }
+ break;
+ }
+ case ib_x: {
+ // Format(instr, "'memop'cond'sign'h 'rd, ['rn, #+'off8]'w");
+ rn_val += imm_val;
+ addr = rn_val;
+ if (instr->HasW()) {
+ set_register(rn, rn_val);
+ }
+ break;
+ }
+ default: {
+ // The PU field is a 2-bit field.
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+ if (((instr->Bits(7, 4) & 0xd) == 0xd) && (instr->Bit(20) == 0)) {
+ ASSERT((rd % 2) == 0);
+ if (instr->HasH()) {
+ // The strd instruction.
+ int32_t value1 = get_register(rd);
+ int32_t value2 = get_register(rd+1);
+ WriteDW(addr, value1, value2);
+ } else {
+ // The ldrd instruction.
+ int* rn_data = ReadDW(addr);
+ set_dw_register(rd, rn_data);
+ }
+ } else if (instr->HasH()) {
+ if (instr->HasSign()) {
+ if (instr->HasL()) {
+ int16_t val = ReadH(addr, instr);
+ set_register(rd, val);
+ } else {
+ int16_t val = get_register(rd);
+ WriteH(addr, val, instr);
+ }
+ } else {
+ if (instr->HasL()) {
+ uint16_t val = ReadHU(addr, instr);
+ set_register(rd, val);
+ } else {
+ uint16_t val = get_register(rd);
+ WriteH(addr, val, instr);
+ }
+ }
+ } else {
+ // signed byte loads
+ ASSERT(instr->HasSign());
+ ASSERT(instr->HasL());
+ int8_t val = ReadB(addr);
+ set_register(rd, val);
+ }
+ return;
+ }
+ } else if ((type == 0) && instr->IsMiscType0()) {
+ if (instr->Bits(22, 21) == 1) {
+ int rm = instr->RmValue();
+ switch (instr->BitField(7, 4)) {
+ case BX:
+ set_pc(get_register(rm));
+ break;
+ case BLX: {
+ uint32_t old_pc = get_pc();
+ set_pc(get_register(rm));
+ set_register(lr, old_pc + Instruction::kInstrSize);
+ break;
+ }
+ case BKPT: {
+ ArmDebugger dbg(this);
+ PrintF("Simulator hit BKPT.\n");
+ dbg.Debug();
+ break;
+ }
+ default:
+ UNIMPLEMENTED();
+ }
+ } else if (instr->Bits(22, 21) == 3) {
+ int rm = instr->RmValue();
+ int rd = instr->RdValue();
+ switch (instr->BitField(7, 4)) {
+ case CLZ: {
+ uint32_t bits = get_register(rm);
+ int leading_zeros = 0;
+ if (bits == 0) {
+ leading_zeros = 32;
+ } else {
+ while ((bits & 0x80000000u) == 0) {
+ bits <<= 1;
+ leading_zeros++;
+ }
+ }
+ set_register(rd, leading_zeros);
+ break;
+ }
+ default:
+ UNIMPLEMENTED();
+ }
+ } else {
+ PrintF("%08x\n", instr->InstructionBits());
+ UNIMPLEMENTED();
+ }
+ } else if ((type == 1) && instr->IsNopType1()) {
+ // NOP.
+ } else {
+ int rd = instr->RdValue();
+ int rn = instr->RnValue();
+ int32_t rn_val = get_register(rn);
+ int32_t shifter_operand = 0;
+ bool shifter_carry_out = 0;
+ if (type == 0) {
+ shifter_operand = GetShiftRm(instr, &shifter_carry_out);
+ } else {
+ ASSERT(instr->TypeValue() == 1);
+ shifter_operand = GetImm(instr, &shifter_carry_out);
+ }
+ int32_t alu_out;
+
+ switch (instr->OpcodeField()) {
+ case AND: {
+ // Format(instr, "and'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "and'cond's 'rd, 'rn, 'imm");
+ alu_out = rn_val & shifter_operand;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(shifter_carry_out);
+ }
+ break;
+ }
+
+ case EOR: {
+ // Format(instr, "eor'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "eor'cond's 'rd, 'rn, 'imm");
+ alu_out = rn_val ^ shifter_operand;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(shifter_carry_out);
+ }
+ break;
+ }
+
+ case SUB: {
+ // Format(instr, "sub'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "sub'cond's 'rd, 'rn, 'imm");
+ alu_out = rn_val - shifter_operand;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(!BorrowFrom(rn_val, shifter_operand));
+ SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false));
+ }
+ break;
+ }
+
+ case RSB: {
+ // Format(instr, "rsb'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "rsb'cond's 'rd, 'rn, 'imm");
+ alu_out = shifter_operand - rn_val;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(!BorrowFrom(shifter_operand, rn_val));
+ SetVFlag(OverflowFrom(alu_out, shifter_operand, rn_val, false));
+ }
+ break;
+ }
+
+ case ADD: {
+ // Format(instr, "add'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "add'cond's 'rd, 'rn, 'imm");
+ alu_out = rn_val + shifter_operand;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(CarryFrom(rn_val, shifter_operand));
+ SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true));
+ }
+ break;
+ }
+
+ case ADC: {
+ // Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "adc'cond's 'rd, 'rn, 'imm");
+ alu_out = rn_val + shifter_operand + GetCarry();
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(CarryFrom(rn_val, shifter_operand, GetCarry()));
+ SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true));
+ }
+ break;
+ }
+
+ case SBC: {
+ Format(instr, "sbc'cond's 'rd, 'rn, 'shift_rm");
+ Format(instr, "sbc'cond's 'rd, 'rn, 'imm");
+ break;
+ }
+
+ case RSC: {
+ Format(instr, "rsc'cond's 'rd, 'rn, 'shift_rm");
+ Format(instr, "rsc'cond's 'rd, 'rn, 'imm");
+ break;
+ }
+
+ case TST: {
+ if (instr->HasS()) {
+ // Format(instr, "tst'cond 'rn, 'shift_rm");
+ // Format(instr, "tst'cond 'rn, 'imm");
+ alu_out = rn_val & shifter_operand;
+ SetNZFlags(alu_out);
+ SetCFlag(shifter_carry_out);
+ } else {
+ // Format(instr, "movw'cond 'rd, 'imm").
+ alu_out = instr->ImmedMovwMovtValue();
+ set_register(rd, alu_out);
+ }
+ break;
+ }
+
+ case TEQ: {
+ if (instr->HasS()) {
+ // Format(instr, "teq'cond 'rn, 'shift_rm");
+ // Format(instr, "teq'cond 'rn, 'imm");
+ alu_out = rn_val ^ shifter_operand;
+ SetNZFlags(alu_out);
+ SetCFlag(shifter_carry_out);
+ } else {
+ // Other instructions matching this pattern are handled in the
+ // miscellaneous instructions part above.
+ UNREACHABLE();
+ }
+ break;
+ }
+
+ case CMP: {
+ if (instr->HasS()) {
+ // Format(instr, "cmp'cond 'rn, 'shift_rm");
+ // Format(instr, "cmp'cond 'rn, 'imm");
+ alu_out = rn_val - shifter_operand;
+ SetNZFlags(alu_out);
+ SetCFlag(!BorrowFrom(rn_val, shifter_operand));
+ SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, false));
+ } else {
+ // Format(instr, "movt'cond 'rd, 'imm").
+ alu_out = (get_register(rd) & 0xffff) |
+ (instr->ImmedMovwMovtValue() << 16);
+ set_register(rd, alu_out);
+ }
+ break;
+ }
+
+ case CMN: {
+ if (instr->HasS()) {
+ // Format(instr, "cmn'cond 'rn, 'shift_rm");
+ // Format(instr, "cmn'cond 'rn, 'imm");
+ alu_out = rn_val + shifter_operand;
+ SetNZFlags(alu_out);
+ SetCFlag(CarryFrom(rn_val, shifter_operand));
+ SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true));
+ } else {
+ // Other instructions matching this pattern are handled in the
+ // miscellaneous instructions part above.
+ UNREACHABLE();
+ }
+ break;
+ }
+
+ case ORR: {
+ // Format(instr, "orr'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "orr'cond's 'rd, 'rn, 'imm");
+ alu_out = rn_val | shifter_operand;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(shifter_carry_out);
+ }
+ break;
+ }
+
+ case MOV: {
+ // Format(instr, "mov'cond's 'rd, 'shift_rm");
+ // Format(instr, "mov'cond's 'rd, 'imm");
+ alu_out = shifter_operand;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(shifter_carry_out);
+ }
+ break;
+ }
+
+ case BIC: {
+ // Format(instr, "bic'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "bic'cond's 'rd, 'rn, 'imm");
+ alu_out = rn_val & ~shifter_operand;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(shifter_carry_out);
+ }
+ break;
+ }
+
+ case MVN: {
+ // Format(instr, "mvn'cond's 'rd, 'shift_rm");
+ // Format(instr, "mvn'cond's 'rd, 'imm");
+ alu_out = ~shifter_operand;
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(shifter_carry_out);
+ }
+ break;
+ }
+
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+}
+
+
+void Simulator::DecodeType2(Instruction* instr) {
+ int rd = instr->RdValue();
+ int rn = instr->RnValue();
+ int32_t rn_val = get_register(rn);
+ int32_t im_val = instr->Offset12Value();
+ int32_t addr = 0;
+ switch (instr->PUField()) {
+ case da_x: {
+ // Format(instr, "'memop'cond'b 'rd, ['rn], #-'off12");
+ ASSERT(!instr->HasW());
+ addr = rn_val;
+ rn_val -= im_val;
+ set_register(rn, rn_val);
+ break;
+ }
+ case ia_x: {
+ // Format(instr, "'memop'cond'b 'rd, ['rn], #+'off12");
+ ASSERT(!instr->HasW());
+ addr = rn_val;
+ rn_val += im_val;
+ set_register(rn, rn_val);
+ break;
+ }
+ case db_x: {
+ // Format(instr, "'memop'cond'b 'rd, ['rn, #-'off12]'w");
+ rn_val -= im_val;
+ addr = rn_val;
+ if (instr->HasW()) {
+ set_register(rn, rn_val);
+ }
+ break;
+ }
+ case ib_x: {
+ // Format(instr, "'memop'cond'b 'rd, ['rn, #+'off12]'w");
+ rn_val += im_val;
+ addr = rn_val;
+ if (instr->HasW()) {
+ set_register(rn, rn_val);
+ }
+ break;
+ }
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ if (instr->HasB()) {
+ if (instr->HasL()) {
+ byte val = ReadBU(addr);
+ set_register(rd, val);
+ } else {
+ byte val = get_register(rd);
+ WriteB(addr, val);
+ }
+ } else {
+ if (instr->HasL()) {
+ set_register(rd, ReadW(addr, instr));
+ } else {
+ WriteW(addr, get_register(rd), instr);
+ }
+ }
+}
+
+
+void Simulator::DecodeType3(Instruction* instr) {
+ int rd = instr->RdValue();
+ int rn = instr->RnValue();
+ int32_t rn_val = get_register(rn);
+ bool shifter_carry_out = 0;
+ int32_t shifter_operand = GetShiftRm(instr, &shifter_carry_out);
+ int32_t addr = 0;
+ switch (instr->PUField()) {
+ case da_x: {
+ ASSERT(!instr->HasW());
+ Format(instr, "'memop'cond'b 'rd, ['rn], -'shift_rm");
+ UNIMPLEMENTED();
+ break;
+ }
+ case ia_x: {
+ if (instr->Bit(4) == 0) {
+ // Memop.
+ } else {
+ if (instr->Bit(5) == 0) {
+ switch (instr->Bits(22, 21)) {
+ case 0:
+ if (instr->Bit(20) == 0) {
+ if (instr->Bit(6) == 0) {
+ // Pkhbt.
+ uint32_t rn_val = get_register(rn);
+ uint32_t rm_val = get_register(instr->RmValue());
+ int32_t shift = instr->Bits(11, 7);
+ rm_val <<= shift;
+ set_register(rd, (rn_val & 0xFFFF) | (rm_val & 0xFFFF0000U));
+ } else {
+ // Pkhtb.
+ uint32_t rn_val = get_register(rn);
+ int32_t rm_val = get_register(instr->RmValue());
+ int32_t shift = instr->Bits(11, 7);
+ if (shift == 0) {
+ shift = 32;
+ }
+ rm_val >>= shift;
+ set_register(rd, (rn_val & 0xFFFF0000U) | (rm_val & 0xFFFF));
+ }
+ } else {
+ UNIMPLEMENTED();
+ }
+ break;
+ case 1:
+ UNIMPLEMENTED();
+ break;
+ case 2:
+ UNIMPLEMENTED();
+ break;
+ case 3: {
+ // Usat.
+ int32_t sat_pos = instr->Bits(20, 16);
+ int32_t sat_val = (1 << sat_pos) - 1;
+ int32_t shift = instr->Bits(11, 7);
+ int32_t shift_type = instr->Bit(6);
+ int32_t rm_val = get_register(instr->RmValue());
+ if (shift_type == 0) { // LSL
+ rm_val <<= shift;
+ } else { // ASR
+ rm_val >>= shift;
+ }
+ // If saturation occurs, the Q flag should be set in the CPSR.
+ // There is no Q flag yet, and no instruction (MRS) to read the
+ // CPSR directly.
+ if (rm_val > sat_val) {
+ rm_val = sat_val;
+ } else if (rm_val < 0) {
+ rm_val = 0;
+ }
+ set_register(rd, rm_val);
+ break;
+ }
+ }
+ } else {
+ switch (instr->Bits(22, 21)) {
+ case 0:
+ UNIMPLEMENTED();
+ break;
+ case 1:
+ UNIMPLEMENTED();
+ break;
+ case 2:
+ if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) {
+ if (instr->Bits(19, 16) == 0xF) {
+ // Uxtb16.
+ uint32_t rm_val = get_register(instr->RmValue());
+ int32_t rotate = instr->Bits(11, 10);
+ switch (rotate) {
+ case 0:
+ break;
+ case 1:
+ rm_val = (rm_val >> 8) | (rm_val << 24);
+ break;
+ case 2:
+ rm_val = (rm_val >> 16) | (rm_val << 16);
+ break;
+ case 3:
+ rm_val = (rm_val >> 24) | (rm_val << 8);
+ break;
+ }
+ set_register(rd,
+ (rm_val & 0xFF) | (rm_val & 0xFF0000));
+ } else {
+ UNIMPLEMENTED();
+ }
+ } else {
+ UNIMPLEMENTED();
+ }
+ break;
+ case 3:
+ if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) {
+ if (instr->Bits(19, 16) == 0xF) {
+ // Uxtb.
+ uint32_t rm_val = get_register(instr->RmValue());
+ int32_t rotate = instr->Bits(11, 10);
+ switch (rotate) {
+ case 0:
+ break;
+ case 1:
+ rm_val = (rm_val >> 8) | (rm_val << 24);
+ break;
+ case 2:
+ rm_val = (rm_val >> 16) | (rm_val << 16);
+ break;
+ case 3:
+ rm_val = (rm_val >> 24) | (rm_val << 8);
+ break;
+ }
+ set_register(rd, (rm_val & 0xFF));
+ } else {
+ // Uxtab.
+ uint32_t rn_val = get_register(rn);
+ uint32_t rm_val = get_register(instr->RmValue());
+ int32_t rotate = instr->Bits(11, 10);
+ switch (rotate) {
+ case 0:
+ break;
+ case 1:
+ rm_val = (rm_val >> 8) | (rm_val << 24);
+ break;
+ case 2:
+ rm_val = (rm_val >> 16) | (rm_val << 16);
+ break;
+ case 3:
+ rm_val = (rm_val >> 24) | (rm_val << 8);
+ break;
+ }
+ set_register(rd, rn_val + (rm_val & 0xFF));
+ }
+ } else {
+ UNIMPLEMENTED();
+ }
+ break;
+ }
+ }
+ return;
+ }
+ break;
+ }
+ case db_x: {
+ if (FLAG_enable_sudiv) {
+ if (!instr->HasW()) {
+ if (instr->Bits(5, 4) == 0x1) {
+ if ((instr->Bit(22) == 0x0) && (instr->Bit(20) == 0x1)) {
+ // sdiv (in V8 notation matching ARM ISA format) rn = rm/rs
+ // Format(instr, "'sdiv'cond'b 'rn, 'rm, 'rs);
+ int rm = instr->RmValue();
+ int32_t rm_val = get_register(rm);
+ int rs = instr->RsValue();
+ int32_t rs_val = get_register(rs);
+ int32_t ret_val = 0;
+ ASSERT(rs_val != 0);
+ ret_val = rm_val/rs_val;
+ set_register(rn, ret_val);
+ return;
+ }
+ }
+ }
+ }
+ // Format(instr, "'memop'cond'b 'rd, ['rn, -'shift_rm]'w");
+ addr = rn_val - shifter_operand;
+ if (instr->HasW()) {
+ set_register(rn, addr);
+ }
+ break;
+ }
+ case ib_x: {
+ if (instr->HasW() && (instr->Bits(6, 4) == 0x5)) {
+ uint32_t widthminus1 = static_cast<uint32_t>(instr->Bits(20, 16));
+ uint32_t lsbit = static_cast<uint32_t>(instr->Bits(11, 7));
+ uint32_t msbit = widthminus1 + lsbit;
+ if (msbit <= 31) {
+ if (instr->Bit(22)) {
+ // ubfx - unsigned bitfield extract.
+ uint32_t rm_val =
+ static_cast<uint32_t>(get_register(instr->RmValue()));
+ uint32_t extr_val = rm_val << (31 - msbit);
+ extr_val = extr_val >> (31 - widthminus1);
+ set_register(instr->RdValue(), extr_val);
+ } else {
+ // sbfx - signed bitfield extract.
+ int32_t rm_val = get_register(instr->RmValue());
+ int32_t extr_val = rm_val << (31 - msbit);
+ extr_val = extr_val >> (31 - widthminus1);
+ set_register(instr->RdValue(), extr_val);
+ }
+ } else {
+ UNREACHABLE();
+ }
+ return;
+ } else if (!instr->HasW() && (instr->Bits(6, 4) == 0x1)) {
+ uint32_t lsbit = static_cast<uint32_t>(instr->Bits(11, 7));
+ uint32_t msbit = static_cast<uint32_t>(instr->Bits(20, 16));
+ if (msbit >= lsbit) {
+ // bfc or bfi - bitfield clear/insert.
+ uint32_t rd_val =
+ static_cast<uint32_t>(get_register(instr->RdValue()));
+ uint32_t bitcount = msbit - lsbit + 1;
+ uint32_t mask = (1 << bitcount) - 1;
+ rd_val &= ~(mask << lsbit);
+ if (instr->RmValue() != 15) {
+ // bfi - bitfield insert.
+ uint32_t rm_val =
+ static_cast<uint32_t>(get_register(instr->RmValue()));
+ rm_val &= mask;
+ rd_val |= rm_val << lsbit;
+ }
+ set_register(instr->RdValue(), rd_val);
+ } else {
+ UNREACHABLE();
+ }
+ return;
+ } else {
+ // Format(instr, "'memop'cond'b 'rd, ['rn, +'shift_rm]'w");
+ addr = rn_val + shifter_operand;
+ if (instr->HasW()) {
+ set_register(rn, addr);
+ }
+ }
+ break;
+ }
+ default: {
+ UNREACHABLE();
+ break;
+ }
+ }
+ if (instr->HasB()) {
+ if (instr->HasL()) {
+ uint8_t byte = ReadB(addr);
+ set_register(rd, byte);
+ } else {
+ uint8_t byte = get_register(rd);
+ WriteB(addr, byte);
+ }
+ } else {
+ if (instr->HasL()) {
+ set_register(rd, ReadW(addr, instr));
+ } else {
+ WriteW(addr, get_register(rd), instr);
+ }
+ }
+}
+
+
+void Simulator::DecodeType4(Instruction* instr) {
+ ASSERT(instr->Bit(22) == 0); // only allowed to be set in privileged mode
+ if (instr->HasL()) {
+ // Format(instr, "ldm'cond'pu 'rn'w, 'rlist");
+ HandleRList(instr, true);
+ } else {
+ // Format(instr, "stm'cond'pu 'rn'w, 'rlist");
+ HandleRList(instr, false);
+ }
+}
+
+
+void Simulator::DecodeType5(Instruction* instr) {
+ // Format(instr, "b'l'cond 'target");
+ int off = (instr->SImmed24Value() << 2);
+ intptr_t pc_address = get_pc();
+ if (instr->HasLink()) {
+ set_register(lr, pc_address + Instruction::kInstrSize);
+ }
+ int pc_reg = get_register(pc);
+ set_pc(pc_reg + off);
+}
+
+
+void Simulator::DecodeType6(Instruction* instr) {
+ DecodeType6CoprocessorIns(instr);
+}
+
+
+void Simulator::DecodeType7(Instruction* instr) {
+ if (instr->Bit(24) == 1) {
+ SoftwareInterrupt(instr);
+ } else {
+ DecodeTypeVFP(instr);
+ }
+}
+
+
+// void Simulator::DecodeTypeVFP(Instruction* instr)
+// The Following ARMv7 VFPv instructions are currently supported.
+// vmov :Sn = Rt
+// vmov :Rt = Sn
+// vcvt: Dd = Sm
+// vcvt: Sd = Dm
+// vcvt.f64.s32 Dd, Dd, #<fbits>
+// Dd = vabs(Dm)
+// Dd = vneg(Dm)
+// Dd = vadd(Dn, Dm)
+// Dd = vsub(Dn, Dm)
+// Dd = vmul(Dn, Dm)
+// Dd = vdiv(Dn, Dm)
+// vcmp(Dd, Dm)
+// vmrs
+// Dd = vsqrt(Dm)
+void Simulator::DecodeTypeVFP(Instruction* instr) {
+ ASSERT((instr->TypeValue() == 7) && (instr->Bit(24) == 0x0) );
+ ASSERT(instr->Bits(11, 9) == 0x5);
+
+ // Obtain double precision register codes.
+ int vm = instr->VFPMRegValue(kDoublePrecision);
+ int vd = instr->VFPDRegValue(kDoublePrecision);
+ int vn = instr->VFPNRegValue(kDoublePrecision);
+
+ if (instr->Bit(4) == 0) {
+ if (instr->Opc1Value() == 0x7) {
+ // Other data processing instructions
+ if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x1)) {
+ // vmov register to register.
+ if (instr->SzValue() == 0x1) {
+ int m = instr->VFPMRegValue(kDoublePrecision);
+ int d = instr->VFPDRegValue(kDoublePrecision);
+ set_d_register_from_double(d, get_double_from_d_register(m));
+ } else {
+ int m = instr->VFPMRegValue(kSinglePrecision);
+ int d = instr->VFPDRegValue(kSinglePrecision);
+ set_s_register_from_float(d, get_float_from_s_register(m));
+ }
+ } else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) {
+ // vabs
+ double dm_value = get_double_from_d_register(vm);
+ double dd_value = fabs(dm_value);
+ dd_value = canonicalizeNaN(dd_value);
+ set_d_register_from_double(vd, dd_value);
+ } else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) {
+ // vneg
+ double dm_value = get_double_from_d_register(vm);
+ double dd_value = -dm_value;
+ dd_value = canonicalizeNaN(dd_value);
+ set_d_register_from_double(vd, dd_value);
+ } else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) {
+ DecodeVCVTBetweenDoubleAndSingle(instr);
+ } else if ((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) {
+ DecodeVCVTBetweenFloatingPointAndInteger(instr);
+ } else if ((instr->Opc2Value() == 0xA) && (instr->Opc3Value() == 0x3) &&
+ (instr->Bit(8) == 1)) {
+ // vcvt.f64.s32 Dd, Dd, #<fbits>
+ int fraction_bits = 32 - ((instr->Bit(5) << 4) | instr->Bits(3, 0));
+ int fixed_value = get_sinteger_from_s_register(vd * 2);
+ double divide = 1 << fraction_bits;
+ set_d_register_from_double(vd, fixed_value / divide);
+ } else if (((instr->Opc2Value() >> 1) == 0x6) &&
+ (instr->Opc3Value() & 0x1)) {
+ DecodeVCVTBetweenFloatingPointAndInteger(instr);
+ } else if (((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) &&
+ (instr->Opc3Value() & 0x1)) {
+ DecodeVCMP(instr);
+ } else if (((instr->Opc2Value() == 0x1)) && (instr->Opc3Value() == 0x3)) {
+ // vsqrt
+ double dm_value = get_double_from_d_register(vm);
+ double dd_value = sqrt(dm_value);
+ dd_value = canonicalizeNaN(dd_value);
+ set_d_register_from_double(vd, dd_value);
+ } else if (instr->Opc3Value() == 0x0) {
+ // vmov immediate.
+ if (instr->SzValue() == 0x1) {
+ set_d_register_from_double(vd, instr->DoubleImmedVmov());
+ } else {
+ UNREACHABLE(); // Not used by v8.
+ }
+ } else {
+ UNREACHABLE(); // Not used by V8.
+ }
+ } else if (instr->Opc1Value() == 0x3) {
+ if (instr->SzValue() != 0x1) {
+ UNREACHABLE(); // Not used by V8.
+ }
+
+ if (instr->Opc3Value() & 0x1) {
+ // vsub
+ double dn_value = get_double_from_d_register(vn);
+ double dm_value = get_double_from_d_register(vm);
+ double dd_value = dn_value - dm_value;
+ dd_value = canonicalizeNaN(dd_value);
+ set_d_register_from_double(vd, dd_value);
+ } else {
+ // vadd
+ double dn_value = get_double_from_d_register(vn);
+ double dm_value = get_double_from_d_register(vm);
+ double dd_value = dn_value + dm_value;
+ dd_value = canonicalizeNaN(dd_value);
+ set_d_register_from_double(vd, dd_value);
+ }
+ } else if ((instr->Opc1Value() == 0x2) && !(instr->Opc3Value() & 0x1)) {
+ // vmul
+ if (instr->SzValue() != 0x1) {
+ UNREACHABLE(); // Not used by V8.
+ }
+
+ double dn_value = get_double_from_d_register(vn);
+ double dm_value = get_double_from_d_register(vm);
+ double dd_value = dn_value * dm_value;
+ dd_value = canonicalizeNaN(dd_value);
+ set_d_register_from_double(vd, dd_value);
+ } else if ((instr->Opc1Value() == 0x0)) {
+ // vmla, vmls
+ const bool is_vmls = (instr->Opc3Value() & 0x1);
+
+ if (instr->SzValue() != 0x1) {
+ UNREACHABLE(); // Not used by V8.
+ }
+
+ const double dd_val = get_double_from_d_register(vd);
+ const double dn_val = get_double_from_d_register(vn);
+ const double dm_val = get_double_from_d_register(vm);
+
+ // Note: we do the mul and add/sub in separate steps to avoid getting a
+ // result with too high precision.
+ set_d_register_from_double(vd, dn_val * dm_val);
+ if (is_vmls) {
+ set_d_register_from_double(
+ vd,
+ canonicalizeNaN(dd_val - get_double_from_d_register(vd)));
+ } else {
+ set_d_register_from_double(
+ vd,
+ canonicalizeNaN(dd_val + get_double_from_d_register(vd)));
+ }
+ } else if ((instr->Opc1Value() == 0x4) && !(instr->Opc3Value() & 0x1)) {
+ // vdiv
+ if (instr->SzValue() != 0x1) {
+ UNREACHABLE(); // Not used by V8.
+ }
+
+ double dn_value = get_double_from_d_register(vn);
+ double dm_value = get_double_from_d_register(vm);
+ double dd_value = dn_value / dm_value;
+ div_zero_vfp_flag_ = (dm_value == 0);
+ dd_value = canonicalizeNaN(dd_value);
+ set_d_register_from_double(vd, dd_value);
+ } else {
+ UNIMPLEMENTED(); // Not used by V8.
+ }
+ } else {
+ if ((instr->VCValue() == 0x0) &&
+ (instr->VAValue() == 0x0)) {
+ DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr);
+ } else if ((instr->VLValue() == 0x0) &&
+ (instr->VCValue() == 0x1) &&
+ (instr->Bit(23) == 0x0)) {
+ // vmov (ARM core register to scalar)
+ int vd = instr->Bits(19, 16) | (instr->Bit(7) << 4);
+ double dd_value = get_double_from_d_register(vd);
+ int32_t data[2];
+ OS::MemCopy(data, &dd_value, 8);
+ data[instr->Bit(21)] = get_register(instr->RtValue());
+ OS::MemCopy(&dd_value, data, 8);
+ set_d_register_from_double(vd, dd_value);
+ } else if ((instr->VLValue() == 0x1) &&
+ (instr->VCValue() == 0x1) &&
+ (instr->Bit(23) == 0x0)) {
+ // vmov (scalar to ARM core register)
+ int vn = instr->Bits(19, 16) | (instr->Bit(7) << 4);
+ double dn_value = get_double_from_d_register(vn);
+ int32_t data[2];
+ OS::MemCopy(data, &dn_value, 8);
+ set_register(instr->RtValue(), data[instr->Bit(21)]);
+ } else if ((instr->VLValue() == 0x1) &&
+ (instr->VCValue() == 0x0) &&
+ (instr->VAValue() == 0x7) &&
+ (instr->Bits(19, 16) == 0x1)) {
+ // vmrs
+ uint32_t rt = instr->RtValue();
+ if (rt == 0xF) {
+ Copy_FPSCR_to_APSR();
+ } else {
+ // Emulate FPSCR from the Simulator flags.
+ uint32_t fpscr = (n_flag_FPSCR_ << 31) |
+ (z_flag_FPSCR_ << 30) |
+ (c_flag_FPSCR_ << 29) |
+ (v_flag_FPSCR_ << 28) |
+ (FPSCR_default_NaN_mode_ << 25) |
+ (inexact_vfp_flag_ << 4) |
+ (underflow_vfp_flag_ << 3) |
+ (overflow_vfp_flag_ << 2) |
+ (div_zero_vfp_flag_ << 1) |
+ (inv_op_vfp_flag_ << 0) |
+ (FPSCR_rounding_mode_);
+ set_register(rt, fpscr);
+ }
+ } else if ((instr->VLValue() == 0x0) &&
+ (instr->VCValue() == 0x0) &&
+ (instr->VAValue() == 0x7) &&
+ (instr->Bits(19, 16) == 0x1)) {
+ // vmsr
+ uint32_t rt = instr->RtValue();
+ if (rt == pc) {
+ UNREACHABLE();
+ } else {
+ uint32_t rt_value = get_register(rt);
+ n_flag_FPSCR_ = (rt_value >> 31) & 1;
+ z_flag_FPSCR_ = (rt_value >> 30) & 1;
+ c_flag_FPSCR_ = (rt_value >> 29) & 1;
+ v_flag_FPSCR_ = (rt_value >> 28) & 1;
+ FPSCR_default_NaN_mode_ = (rt_value >> 25) & 1;
+ inexact_vfp_flag_ = (rt_value >> 4) & 1;
+ underflow_vfp_flag_ = (rt_value >> 3) & 1;
+ overflow_vfp_flag_ = (rt_value >> 2) & 1;
+ div_zero_vfp_flag_ = (rt_value >> 1) & 1;
+ inv_op_vfp_flag_ = (rt_value >> 0) & 1;
+ FPSCR_rounding_mode_ =
+ static_cast<VFPRoundingMode>((rt_value) & kVFPRoundingModeMask);
+ }
+ } else {
+ UNIMPLEMENTED(); // Not used by V8.
+ }
+ }
+}
+
+
+void Simulator::DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(
+ Instruction* instr) {
+ ASSERT((instr->Bit(4) == 1) && (instr->VCValue() == 0x0) &&
+ (instr->VAValue() == 0x0));
+
+ int t = instr->RtValue();
+ int n = instr->VFPNRegValue(kSinglePrecision);
+ bool to_arm_register = (instr->VLValue() == 0x1);
+
+ if (to_arm_register) {
+ int32_t int_value = get_sinteger_from_s_register(n);
+ set_register(t, int_value);
+ } else {
+ int32_t rs_val = get_register(t);
+ set_s_register_from_sinteger(n, rs_val);
+ }
+}
+
+
+void Simulator::DecodeVCMP(Instruction* instr) {
+ ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
+ ASSERT(((instr->Opc2Value() == 0x4) || (instr->Opc2Value() == 0x5)) &&
+ (instr->Opc3Value() & 0x1));
+ // Comparison.
+
+ VFPRegPrecision precision = kSinglePrecision;
+ if (instr->SzValue() == 1) {
+ precision = kDoublePrecision;
+ }
+
+ int d = instr->VFPDRegValue(precision);
+ int m = 0;
+ if (instr->Opc2Value() == 0x4) {
+ m = instr->VFPMRegValue(precision);
+ }
+
+ if (precision == kDoublePrecision) {
+ double dd_value = get_double_from_d_register(d);
+ double dm_value = 0.0;
+ if (instr->Opc2Value() == 0x4) {
+ dm_value = get_double_from_d_register(m);
+ }
+
+ // Raise exceptions for quiet NaNs if necessary.
+ if (instr->Bit(7) == 1) {
+ if (std::isnan(dd_value)) {
+ inv_op_vfp_flag_ = true;
+ }
+ }
+
+ Compute_FPSCR_Flags(dd_value, dm_value);
+ } else {
+ UNIMPLEMENTED(); // Not used by V8.
+ }
+}
+
+
+void Simulator::DecodeVCVTBetweenDoubleAndSingle(Instruction* instr) {
+ ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7));
+ ASSERT((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3));
+
+ VFPRegPrecision dst_precision = kDoublePrecision;
+ VFPRegPrecision src_precision = kSinglePrecision;
+ if (instr->SzValue() == 1) {
+ dst_precision = kSinglePrecision;
+ src_precision = kDoublePrecision;
+ }
+
+ int dst = instr->VFPDRegValue(dst_precision);
+ int src = instr->VFPMRegValue(src_precision);
+
+ if (dst_precision == kSinglePrecision) {
+ double val = get_double_from_d_register(src);
+ set_s_register_from_float(dst, static_cast<float>(val));
+ } else {
+ float val = get_float_from_s_register(src);
+ set_d_register_from_double(dst, static_cast<double>(val));
+ }
+}
+
+bool get_inv_op_vfp_flag(VFPRoundingMode mode,
+ double val,
+ bool unsigned_) {
+ ASSERT((mode == RN) || (mode == RM) || (mode == RZ));
+ double max_uint = static_cast<double>(0xffffffffu);
+ double max_int = static_cast<double>(kMaxInt);
+ double min_int = static_cast<double>(kMinInt);
+
+ // Check for NaN.
+ if (val != val) {
+ return true;
+ }
+
+ // Check for overflow. This code works because 32bit integers can be
+ // exactly represented by ieee-754 64bit floating-point values.
+ switch (mode) {
+ case RN:
+ return unsigned_ ? (val >= (max_uint + 0.5)) ||
+ (val < -0.5)
+ : (val >= (max_int + 0.5)) ||
+ (val < (min_int - 0.5));
+
+ case RM:
+ return unsigned_ ? (val >= (max_uint + 1.0)) ||
+ (val < 0)
+ : (val >= (max_int + 1.0)) ||
+ (val < min_int);
+
+ case RZ:
+ return unsigned_ ? (val >= (max_uint + 1.0)) ||
+ (val <= -1)
+ : (val >= (max_int + 1.0)) ||
+ (val <= (min_int - 1.0));
+ default:
+ UNREACHABLE();
+ return true;
+ }
+}
+
+
+// We call this function only if we had a vfp invalid exception.
+// It returns the correct saturated value.
+int VFPConversionSaturate(double val, bool unsigned_res) {
+ if (val != val) {
+ return 0;
+ } else {
+ if (unsigned_res) {
+ return (val < 0) ? 0 : 0xffffffffu;
+ } else {
+ return (val < 0) ? kMinInt : kMaxInt;
+ }
+ }
+}
+
+
+void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) {
+ ASSERT((instr->Bit(4) == 0) && (instr->Opc1Value() == 0x7) &&
+ (instr->Bits(27, 23) == 0x1D));
+ ASSERT(((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) ||
+ (((instr->Opc2Value() >> 1) == 0x6) && (instr->Opc3Value() & 0x1)));
+
+ // Conversion between floating-point and integer.
+ bool to_integer = (instr->Bit(18) == 1);
+
+ VFPRegPrecision src_precision = (instr->SzValue() == 1) ? kDoublePrecision
+ : kSinglePrecision;
+
+ if (to_integer) {
+ // We are playing with code close to the C++ standard's limits below,
+ // hence the very simple code and heavy checks.
+ //
+ // Note:
+ // C++ defines default type casting from floating point to integer as
+ // (close to) rounding toward zero ("fractional part discarded").
+
+ int dst = instr->VFPDRegValue(kSinglePrecision);
+ int src = instr->VFPMRegValue(src_precision);
+
+ // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding
+ // mode or the default Round to Zero mode.
+ VFPRoundingMode mode = (instr->Bit(7) != 1) ? FPSCR_rounding_mode_
+ : RZ;
+ ASSERT((mode == RM) || (mode == RZ) || (mode == RN));
+
+ bool unsigned_integer = (instr->Bit(16) == 0);
+ bool double_precision = (src_precision == kDoublePrecision);
+
+ double val = double_precision ? get_double_from_d_register(src)
+ : get_float_from_s_register(src);
+
+ int temp = unsigned_integer ? static_cast<uint32_t>(val)
+ : static_cast<int32_t>(val);
+
+ inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer);
+
+ double abs_diff =
+ unsigned_integer ? fabs(val - static_cast<uint32_t>(temp))
+ : fabs(val - temp);
+
+ inexact_vfp_flag_ = (abs_diff != 0);
+
+ if (inv_op_vfp_flag_) {
+ temp = VFPConversionSaturate(val, unsigned_integer);
+ } else {
+ switch (mode) {
+ case RN: {
+ int val_sign = (val > 0) ? 1 : -1;
+ if (abs_diff > 0.5) {
+ temp += val_sign;
+ } else if (abs_diff == 0.5) {
+ // Round to even if exactly halfway.
+ temp = ((temp % 2) == 0) ? temp : temp + val_sign;
+ }
+ break;
+ }
+
+ case RM:
+ temp = temp > val ? temp - 1 : temp;
+ break;
+
+ case RZ:
+ // Nothing to do.
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ // Update the destination register.
+ set_s_register_from_sinteger(dst, temp);
+
+ } else {
+ bool unsigned_integer = (instr->Bit(7) == 0);
+
+ int dst = instr->VFPDRegValue(src_precision);
+ int src = instr->VFPMRegValue(kSinglePrecision);
+
+ int val = get_sinteger_from_s_register(src);
+
+ if (src_precision == kDoublePrecision) {
+ if (unsigned_integer) {
+ set_d_register_from_double(
+ dst, static_cast<double>(static_cast<uint32_t>(val)));
+ } else {
+ set_d_register_from_double(dst, static_cast<double>(val));
+ }
+ } else {
+ if (unsigned_integer) {
+ set_s_register_from_float(
+ dst, static_cast<float>(static_cast<uint32_t>(val)));
+ } else {
+ set_s_register_from_float(dst, static_cast<float>(val));
+ }
+ }
+ }
+}
+
+
+// void Simulator::DecodeType6CoprocessorIns(Instruction* instr)
+// Decode Type 6 coprocessor instructions.
+// Dm = vmov(Rt, Rt2)
+// <Rt, Rt2> = vmov(Dm)
+// Ddst = MEM(Rbase + 4*offset).
+// MEM(Rbase + 4*offset) = Dsrc.
+void Simulator::DecodeType6CoprocessorIns(Instruction* instr) {
+ ASSERT((instr->TypeValue() == 6));
+
+ if (instr->CoprocessorValue() == 0xA) {
+ switch (instr->OpcodeValue()) {
+ case 0x8:
+ case 0xA:
+ case 0xC:
+ case 0xE: { // Load and store single precision float to memory.
+ int rn = instr->RnValue();
+ int vd = instr->VFPDRegValue(kSinglePrecision);
+ int offset = instr->Immed8Value();
+ if (!instr->HasU()) {
+ offset = -offset;
+ }
+
+ int32_t address = get_register(rn) + 4 * offset;
+ if (instr->HasL()) {
+ // Load double from memory: vldr.
+ set_s_register_from_sinteger(vd, ReadW(address, instr));
+ } else {
+ // Store double to memory: vstr.
+ WriteW(address, get_sinteger_from_s_register(vd), instr);
+ }
+ break;
+ }
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ case 0x9:
+ case 0xB:
+ // Load/store multiple single from memory: vldm/vstm.
+ HandleVList(instr);
+ break;
+ default:
+ UNIMPLEMENTED(); // Not used by V8.
+ }
+ } else if (instr->CoprocessorValue() == 0xB) {
+ switch (instr->OpcodeValue()) {
+ case 0x2:
+ // Load and store double to two GP registers
+ if (instr->Bits(7, 6) != 0 || instr->Bit(4) != 1) {
+ UNIMPLEMENTED(); // Not used by V8.
+ } else {
+ int rt = instr->RtValue();
+ int rn = instr->RnValue();
+ int vm = instr->VFPMRegValue(kDoublePrecision);
+ if (instr->HasL()) {
+ int32_t data[2];
+ double d = get_double_from_d_register(vm);
+ OS::MemCopy(data, &d, 8);
+ set_register(rt, data[0]);
+ set_register(rn, data[1]);
+ } else {
+ int32_t data[] = { get_register(rt), get_register(rn) };
+ double d;
+ OS::MemCopy(&d, data, 8);
+ set_d_register_from_double(vm, d);
+ }
+ }
+ break;
+ case 0x8:
+ case 0xA:
+ case 0xC:
+ case 0xE: { // Load and store double to memory.
+ int rn = instr->RnValue();
+ int vd = instr->VFPDRegValue(kDoublePrecision);
+ int offset = instr->Immed8Value();
+ if (!instr->HasU()) {
+ offset = -offset;
+ }
+ int32_t address = get_register(rn) + 4 * offset;
+ if (instr->HasL()) {
+ // Load double from memory: vldr.
+ int32_t data[] = {
+ ReadW(address, instr),
+ ReadW(address + 4, instr)
+ };
+ double val;
+ OS::MemCopy(&val, data, 8);
+ set_d_register_from_double(vd, val);
+ } else {
+ // Store double to memory: vstr.
+ int32_t data[2];
+ double val = get_double_from_d_register(vd);
+ OS::MemCopy(data, &val, 8);
+ WriteW(address, data[0], instr);
+ WriteW(address + 4, data[1], instr);
+ }
+ break;
+ }
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ case 0x9:
+ case 0xB:
+ // Load/store multiple double from memory: vldm/vstm.
+ HandleVList(instr);
+ break;
+ default:
+ UNIMPLEMENTED(); // Not used by V8.
+ }
+ } else {
+ UNIMPLEMENTED(); // Not used by V8.
+ }
+}
+
+
+void Simulator::DecodeSpecialCondition(Instruction* instr) {
+ switch (instr->SpecialValue()) {
+ case 5:
+ if ((instr->Bits(18, 16) == 0) && (instr->Bits(11, 6) == 0x28) &&
+ (instr->Bit(4) == 1)) {
+ // vmovl signed
+ int Vd = (instr->Bit(22) << 4) | instr->VdValue();
+ int Vm = (instr->Bit(5) << 4) | instr->VmValue();
+ int imm3 = instr->Bits(21, 19);
+ if ((imm3 != 1) && (imm3 != 2) && (imm3 != 4)) UNIMPLEMENTED();
+ int esize = 8 * imm3;
+ int elements = 64 / esize;
+ int8_t from[8];
+ get_d_register(Vm, reinterpret_cast<uint64_t*>(from));
+ int16_t to[8];
+ int e = 0;
+ while (e < elements) {
+ to[e] = from[e];
+ e++;
+ }
+ set_q_register(Vd, reinterpret_cast<uint64_t*>(to));
+ } else {
+ UNIMPLEMENTED();
+ }
+ break;
+ case 7:
+ if ((instr->Bits(18, 16) == 0) && (instr->Bits(11, 6) == 0x28) &&
+ (instr->Bit(4) == 1)) {
+ // vmovl unsigned
+ int Vd = (instr->Bit(22) << 4) | instr->VdValue();
+ int Vm = (instr->Bit(5) << 4) | instr->VmValue();
+ int imm3 = instr->Bits(21, 19);
+ if ((imm3 != 1) && (imm3 != 2) && (imm3 != 4)) UNIMPLEMENTED();
+ int esize = 8 * imm3;
+ int elements = 64 / esize;
+ uint8_t from[8];
+ get_d_register(Vm, reinterpret_cast<uint64_t*>(from));
+ uint16_t to[8];
+ int e = 0;
+ while (e < elements) {
+ to[e] = from[e];
+ e++;
+ }
+ set_q_register(Vd, reinterpret_cast<uint64_t*>(to));
+ } else {
+ UNIMPLEMENTED();
+ }
+ break;
+ case 8:
+ if (instr->Bits(21, 20) == 0) {
+ // vst1
+ int Vd = (instr->Bit(22) << 4) | instr->VdValue();
+ int Rn = instr->VnValue();
+ int type = instr->Bits(11, 8);
+ int Rm = instr->VmValue();
+ int32_t address = get_register(Rn);
+ int regs = 0;
+ switch (type) {
+ case nlt_1:
+ regs = 1;
+ break;
+ case nlt_2:
+ regs = 2;
+ break;
+ case nlt_3:
+ regs = 3;
+ break;
+ case nlt_4:
+ regs = 4;
+ break;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+ int r = 0;
+ while (r < regs) {
+ uint32_t data[2];
+ get_d_register(Vd + r, data);
+ WriteW(address, data[0], instr);
+ WriteW(address + 4, data[1], instr);
+ address += 8;
+ r++;
+ }
+ if (Rm != 15) {
+ if (Rm == 13) {
+ set_register(Rn, address);
+ } else {
+ set_register(Rn, get_register(Rn) + get_register(Rm));
+ }
+ }
+ } else if (instr->Bits(21, 20) == 2) {
+ // vld1
+ int Vd = (instr->Bit(22) << 4) | instr->VdValue();
+ int Rn = instr->VnValue();
+ int type = instr->Bits(11, 8);
+ int Rm = instr->VmValue();
+ int32_t address = get_register(Rn);
+ int regs = 0;
+ switch (type) {
+ case nlt_1:
+ regs = 1;
+ break;
+ case nlt_2:
+ regs = 2;
+ break;
+ case nlt_3:
+ regs = 3;
+ break;
+ case nlt_4:
+ regs = 4;
+ break;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+ int r = 0;
+ while (r < regs) {
+ uint32_t data[2];
+ data[0] = ReadW(address, instr);
+ data[1] = ReadW(address + 4, instr);
+ set_d_register(Vd + r, data);
+ address += 8;
+ r++;
+ }
+ if (Rm != 15) {
+ if (Rm == 13) {
+ set_register(Rn, address);
+ } else {
+ set_register(Rn, get_register(Rn) + get_register(Rm));
+ }
+ }
+ } else {
+ UNIMPLEMENTED();
+ }
+ break;
+ case 0xA:
+ case 0xB:
+ if ((instr->Bits(22, 20) == 5) && (instr->Bits(15, 12) == 0xf)) {
+ // pld: ignore instruction.
+ } else {
+ UNIMPLEMENTED();
+ }
+ break;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+}
+
+
+// Executes the current instruction.
+void Simulator::InstructionDecode(Instruction* instr) {
+ if (v8::internal::FLAG_check_icache) {
+ CheckICache(isolate_->simulator_i_cache(), instr);
+ }
+ pc_modified_ = false;
+ if (::v8::internal::FLAG_trace_sim) {
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // use a reasonably large buffer
+ v8::internal::EmbeddedVector<char, 256> buffer;
+ dasm.InstructionDecode(buffer,
+ reinterpret_cast<byte*>(instr));
+ PrintF(" 0x%08x %s\n", reinterpret_cast<intptr_t>(instr), buffer.start());
+ }
+ if (instr->ConditionField() == kSpecialCondition) {
+ DecodeSpecialCondition(instr);
+ } else if (ConditionallyExecute(instr)) {
+ switch (instr->TypeValue()) {
+ case 0:
+ case 1: {
+ DecodeType01(instr);
+ break;
+ }
+ case 2: {
+ DecodeType2(instr);
+ break;
+ }
+ case 3: {
+ DecodeType3(instr);
+ break;
+ }
+ case 4: {
+ DecodeType4(instr);
+ break;
+ }
+ case 5: {
+ DecodeType5(instr);
+ break;
+ }
+ case 6: {
+ DecodeType6(instr);
+ break;
+ }
+ case 7: {
+ DecodeType7(instr);
+ break;
+ }
+ default: {
+ UNIMPLEMENTED();
+ break;
+ }
+ }
+ // If the instruction is a non taken conditional stop, we need to skip the
+ // inlined message address.
+ } else if (instr->IsStop()) {
+ set_pc(get_pc() + 2 * Instruction::kInstrSize);
+ }
+ if (!pc_modified_) {
+ set_register(pc, reinterpret_cast<int32_t>(instr)
+ + Instruction::kInstrSize);
+ }
+}
+
+
+void Simulator::Execute() {
+ // Get the PC to simulate. Cannot use the accessor here as we need the
+ // raw PC value and not the one used as input to arithmetic instructions.
+ int program_counter = get_pc();
+
+ if (::v8::internal::FLAG_stop_sim_at == 0) {
+ // Fast version of the dispatch loop without checking whether the simulator
+ // should be stopping at a particular executed instruction.
+ while (program_counter != end_sim_pc) {
+ Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
+ icount_++;
+ InstructionDecode(instr);
+ program_counter = get_pc();
+ }
+ } else {
+ // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when
+ // we reach the particular instuction count.
+ while (program_counter != end_sim_pc) {
+ Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
+ icount_++;
+ if (icount_ == ::v8::internal::FLAG_stop_sim_at) {
+ ArmDebugger dbg(this);
+ dbg.Debug();
+ } else {
+ InstructionDecode(instr);
+ }
+ program_counter = get_pc();
+ }
+ }
+}
+
+
+void Simulator::CallInternal(byte* entry) {
+ // Prepare to execute the code at entry
+ set_register(pc, reinterpret_cast<int32_t>(entry));
+ // Put down marker for end of simulation. The simulator will stop simulation
+ // when the PC reaches this value. By saving the "end simulation" value into
+ // the LR the simulation stops when returning to this call point.
+ set_register(lr, end_sim_pc);
+
+ // Remember the values of callee-saved registers.
+ // The code below assumes that r9 is not used as sb (static base) in
+ // simulator code and therefore is regarded as a callee-saved register.
+ int32_t r4_val = get_register(r4);
+ int32_t r5_val = get_register(r5);
+ int32_t r6_val = get_register(r6);
+ int32_t r7_val = get_register(r7);
+ int32_t r8_val = get_register(r8);
+ int32_t r9_val = get_register(r9);
+ int32_t r10_val = get_register(r10);
+ int32_t r11_val = get_register(r11);
+
+ // 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);
+ set_register(r5, callee_saved_value);
+ set_register(r6, callee_saved_value);
+ set_register(r7, callee_saved_value);
+ set_register(r8, callee_saved_value);
+ set_register(r9, callee_saved_value);
+ set_register(r10, callee_saved_value);
+ set_register(r11, callee_saved_value);
+
+ // Start the simulation
+ Execute();
+
+ // Check that the callee-saved registers have been preserved.
+ CHECK_EQ(callee_saved_value, get_register(r4));
+ CHECK_EQ(callee_saved_value, get_register(r5));
+ CHECK_EQ(callee_saved_value, get_register(r6));
+ CHECK_EQ(callee_saved_value, get_register(r7));
+ CHECK_EQ(callee_saved_value, get_register(r8));
+ CHECK_EQ(callee_saved_value, get_register(r9));
+ CHECK_EQ(callee_saved_value, get_register(r10));
+ CHECK_EQ(callee_saved_value, get_register(r11));
+
+ // Restore callee-saved registers with the original value.
+ set_register(r4, r4_val);
+ set_register(r5, r5_val);
+ set_register(r6, r6_val);
+ set_register(r7, r7_val);
+ set_register(r8, r8_val);
+ set_register(r9, r9_val);
+ set_register(r10, r10_val);
+ set_register(r11, r11_val);
+}
+
+
+int32_t Simulator::Call(byte* entry, int argument_count, ...) {
+ va_list parameters;
+ va_start(parameters, argument_count);
+ // Set up arguments
+
+ // First four arguments passed in registers.
+ ASSERT(argument_count >= 4);
+ set_register(r0, va_arg(parameters, int32_t));
+ set_register(r1, va_arg(parameters, int32_t));
+ set_register(r2, va_arg(parameters, int32_t));
+ set_register(r3, va_arg(parameters, int32_t));
+
+ // Remaining arguments passed on stack.
+ int original_stack = get_register(sp);
+ // Compute position of stack on entry to generated code.
+ int entry_stack = (original_stack - (argument_count - 4) * sizeof(int32_t));
+ if (OS::ActivationFrameAlignment() != 0) {
+ entry_stack &= -OS::ActivationFrameAlignment();
+ }
+ // Store remaining arguments on stack, from low to high memory.
+ intptr_t* stack_argument = reinterpret_cast<intptr_t*>(entry_stack);
+ for (int i = 4; i < argument_count; i++) {
+ stack_argument[i - 4] = va_arg(parameters, int32_t);
+ }
+ va_end(parameters);
+ set_register(sp, entry_stack);
+
+ CallInternal(entry);
+
+ // Pop stack passed arguments.
+ CHECK_EQ(entry_stack, get_register(sp));
+ set_register(sp, original_stack);
+
+ int32_t result = get_register(r0);
+ return result;
+}
+
+
+double Simulator::CallFP(byte* entry, double d0, double d1) {
+ if (use_eabi_hardfloat()) {
+ set_d_register_from_double(0, d0);
+ set_d_register_from_double(1, d1);
+ } else {
+ int buffer[2];
+ ASSERT(sizeof(buffer[0]) * 2 == sizeof(d0));
+ OS::MemCopy(buffer, &d0, sizeof(d0));
+ set_dw_register(0, buffer);
+ OS::MemCopy(buffer, &d1, sizeof(d1));
+ set_dw_register(2, buffer);
+ }
+ CallInternal(entry);
+ if (use_eabi_hardfloat()) {
+ return get_double_from_d_register(0);
+ } else {
+ return get_double_from_register_pair(0);
+ }
+}
+
+
+uintptr_t Simulator::PushAddress(uintptr_t address) {
+ int new_sp = get_register(sp) - sizeof(uintptr_t);
+ uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(new_sp);
+ *stack_slot = address;
+ set_register(sp, new_sp);
+ return new_sp;
+}
+
+
+uintptr_t Simulator::PopAddress() {
+ int current_sp = get_register(sp);
+ uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(current_sp);
+ uintptr_t address = *stack_slot;
+ set_register(sp, current_sp + sizeof(uintptr_t));
+ return address;
+}
+
+} } // namespace v8::internal
+
+#endif // USE_SIMULATOR
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/arm/simulator-arm.h b/chromium/v8/src/arm/simulator-arm.h
new file mode 100644
index 00000000000..7fca7432bf7
--- /dev/null
+++ b/chromium/v8/src/arm/simulator-arm.h
@@ -0,0 +1,481 @@
+// Copyright 2012 the V8 project authors. 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.
+
+
+// Declares a Simulator for ARM instructions if we are not generating a native
+// ARM binary. This Simulator allows us to run and debug ARM code generation on
+// regular desktop machines.
+// V8 calls into generated code by "calling" the CALL_GENERATED_CODE macro,
+// which will start execution in the Simulator or forwards to the real entry
+// on a ARM HW platform.
+
+#ifndef V8_ARM_SIMULATOR_ARM_H_
+#define V8_ARM_SIMULATOR_ARM_H_
+
+#include "allocation.h"
+
+#if !defined(USE_SIMULATOR)
+// Running without a simulator on a native arm platform.
+
+namespace v8 {
+namespace internal {
+
+// When running without a simulator we call the entry directly.
+#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
+ (entry(p0, p1, p2, p3, p4))
+
+typedef int (*arm_regexp_matcher)(String*, int, const byte*, const byte*,
+ void*, int*, int, Address, int, Isolate*);
+
+
+// Call the generated regexp code directly. The code at the entry address
+// should act as a function matching the type arm_regexp_matcher.
+// The fifth argument is a dummy that reserves the space used for
+// the return address added by the ExitFrame in native calls.
+#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \
+ (FUNCTION_CAST<arm_regexp_matcher>(entry)( \
+ p0, p1, p2, p3, NULL, p4, p5, p6, p7, p8))
+
+#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
+ reinterpret_cast<TryCatch*>(try_catch_address)
+
+// The stack limit beyond which we will throw stack overflow errors in
+// generated code. Because generated code on arm uses the C stack, we
+// just use the C stack limit.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+ static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
+ uintptr_t c_limit) {
+ USE(isolate);
+ return c_limit;
+ }
+
+ static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
+ return try_catch_address;
+ }
+
+ static inline void UnregisterCTryCatch() { }
+};
+
+} } // namespace v8::internal
+
+#else // !defined(USE_SIMULATOR)
+// Running with a simulator.
+
+#include "constants-arm.h"
+#include "hashmap.h"
+#include "assembler.h"
+
+namespace v8 {
+namespace internal {
+
+class CachePage {
+ public:
+ static const int LINE_VALID = 0;
+ static const int LINE_INVALID = 1;
+
+ static const int kPageShift = 12;
+ static const int kPageSize = 1 << kPageShift;
+ static const int kPageMask = kPageSize - 1;
+ static const int kLineShift = 2; // The cache line is only 4 bytes right now.
+ static const int kLineLength = 1 << kLineShift;
+ static const int kLineMask = kLineLength - 1;
+
+ CachePage() {
+ memset(&validity_map_, LINE_INVALID, sizeof(validity_map_));
+ }
+
+ char* ValidityByte(int offset) {
+ return &validity_map_[offset >> kLineShift];
+ }
+
+ char* CachedData(int offset) {
+ return &data_[offset];
+ }
+
+ private:
+ char data_[kPageSize]; // The cached data.
+ static const int kValidityMapSize = kPageSize >> kLineShift;
+ char validity_map_[kValidityMapSize]; // One byte per line.
+};
+
+
+class Simulator {
+ public:
+ friend class ArmDebugger;
+ enum Register {
+ no_reg = -1,
+ r0 = 0, r1, r2, r3, r4, r5, r6, r7,
+ r8, r9, r10, r11, r12, r13, r14, r15,
+ num_registers,
+ sp = 13,
+ lr = 14,
+ pc = 15,
+ s0 = 0, s1, s2, s3, s4, s5, s6, s7,
+ s8, s9, s10, s11, s12, s13, s14, s15,
+ s16, s17, s18, s19, s20, s21, s22, s23,
+ s24, s25, s26, s27, s28, s29, s30, s31,
+ num_s_registers = 32,
+ d0 = 0, d1, d2, d3, d4, d5, d6, d7,
+ d8, d9, d10, d11, d12, d13, d14, d15,
+ d16, d17, d18, d19, d20, d21, d22, d23,
+ d24, d25, d26, d27, d28, d29, d30, d31,
+ num_d_registers = 32,
+ q0 = 0, q1, q2, q3, q4, q5, q6, q7,
+ q8, q9, q10, q11, q12, q13, q14, q15,
+ num_q_registers = 16
+ };
+
+ explicit Simulator(Isolate* isolate);
+ ~Simulator();
+
+ // The currently executing Simulator instance. Potentially there can be one
+ // for each native thread.
+ static Simulator* current(v8::internal::Isolate* isolate);
+
+ // Accessors for register state. Reading the pc value adheres to the ARM
+ // architecture specification and is off by a 8 from the currently executing
+ // instruction.
+ void set_register(int reg, int32_t value);
+ int32_t get_register(int reg) const;
+ double get_double_from_register_pair(int reg);
+ void set_dw_register(int dreg, const int* dbl);
+
+ // Support for VFP.
+ void get_d_register(int dreg, uint64_t* value);
+ void set_d_register(int dreg, const uint64_t* value);
+ void get_d_register(int dreg, uint32_t* value);
+ void set_d_register(int dreg, const uint32_t* value);
+ void get_q_register(int qreg, uint64_t* value);
+ void set_q_register(int qreg, const uint64_t* value);
+ void get_q_register(int qreg, uint32_t* value);
+ void set_q_register(int qreg, const uint32_t* value);
+
+ void set_s_register(int reg, unsigned int value);
+ unsigned int get_s_register(int reg) const;
+
+ void set_d_register_from_double(int dreg, const double& dbl) {
+ SetVFPRegister<double, 2>(dreg, dbl);
+ }
+
+ double get_double_from_d_register(int dreg) {
+ return GetFromVFPRegister<double, 2>(dreg);
+ }
+
+ void set_s_register_from_float(int sreg, const float flt) {
+ SetVFPRegister<float, 1>(sreg, flt);
+ }
+
+ float get_float_from_s_register(int sreg) {
+ return GetFromVFPRegister<float, 1>(sreg);
+ }
+
+ void set_s_register_from_sinteger(int sreg, const int sint) {
+ SetVFPRegister<int, 1>(sreg, sint);
+ }
+
+ int get_sinteger_from_s_register(int sreg) {
+ return GetFromVFPRegister<int, 1>(sreg);
+ }
+
+ // Special case of set_register and get_register to access the raw PC value.
+ void set_pc(int32_t value);
+ int32_t get_pc() const;
+
+ // Accessor to the internal simulator stack area.
+ uintptr_t StackLimit() const;
+
+ // Executes ARM instructions until the PC reaches end_sim_pc.
+ void Execute();
+
+ // Call on program start.
+ static void Initialize(Isolate* isolate);
+
+ // V8 generally calls into generated JS code with 5 parameters and into
+ // generated RegExp code with 7 parameters. This is a convenience function,
+ // which sets up the simulator state and grabs the result on return.
+ int32_t Call(byte* entry, int argument_count, ...);
+ // Alternative: call a 2-argument double function.
+ double CallFP(byte* entry, double d0, double d1);
+
+ // Push an address onto the JS stack.
+ uintptr_t PushAddress(uintptr_t address);
+
+ // 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);
+
+ // Returns true if pc register contains one of the 'special_values' defined
+ // below (bad_lr, end_sim_pc).
+ bool has_bad_pc() const;
+
+ // EABI variant for double arguments in use.
+ bool use_eabi_hardfloat() {
+#if USE_EABI_HARDFLOAT
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ private:
+ enum special_values {
+ // Known bad pc value to ensure that the simulator does not execute
+ // without being properly setup.
+ bad_lr = -1,
+ // A pc value used to signal the simulator to stop execution. Generally
+ // the lr is set to this value on transition from native C code to
+ // simulated execution, so that the simulator can "return" to the native
+ // C code.
+ end_sim_pc = -2
+ };
+
+ // Unsupported instructions use Format to print an error and stop execution.
+ void Format(Instruction* instr, const char* format);
+
+ // Checks if the current instruction should be executed based on its
+ // condition bits.
+ bool ConditionallyExecute(Instruction* instr);
+
+ // Helper functions to set the conditional flags in the architecture state.
+ void SetNZFlags(int32_t val);
+ void SetCFlag(bool val);
+ void SetVFlag(bool val);
+ bool CarryFrom(int32_t left, int32_t right, int32_t carry = 0);
+ bool BorrowFrom(int32_t left, int32_t right);
+ bool OverflowFrom(int32_t alu_out,
+ int32_t left,
+ int32_t right,
+ bool addition);
+
+ inline int GetCarry() {
+ return c_flag_ ? 1 : 0;
+ };
+
+ // Support for VFP.
+ void Compute_FPSCR_Flags(double val1, double val2);
+ void Copy_FPSCR_to_APSR();
+ inline double canonicalizeNaN(double value);
+
+ // Helper functions to decode common "addressing" modes
+ int32_t GetShiftRm(Instruction* instr, bool* carry_out);
+ int32_t GetImm(Instruction* instr, bool* carry_out);
+ int32_t ProcessPU(Instruction* instr,
+ int num_regs,
+ int operand_size,
+ intptr_t* start_address,
+ intptr_t* end_address);
+ void HandleRList(Instruction* instr, bool load);
+ void HandleVList(Instruction* inst);
+ void SoftwareInterrupt(Instruction* instr);
+
+ // Stop helper functions.
+ inline bool isStopInstruction(Instruction* instr);
+ inline bool isWatchedStop(uint32_t bkpt_code);
+ inline bool isEnabledStop(uint32_t bkpt_code);
+ inline void EnableStop(uint32_t bkpt_code);
+ inline void DisableStop(uint32_t bkpt_code);
+ inline void IncreaseStopCounter(uint32_t bkpt_code);
+ void PrintStopInfo(uint32_t code);
+
+ // Read and write memory.
+ inline uint8_t ReadBU(int32_t addr);
+ inline int8_t ReadB(int32_t addr);
+ inline void WriteB(int32_t addr, uint8_t value);
+ inline void WriteB(int32_t addr, int8_t value);
+
+ inline uint16_t ReadHU(int32_t addr, Instruction* instr);
+ inline int16_t ReadH(int32_t addr, Instruction* instr);
+ // Note: Overloaded on the sign of the value.
+ inline void WriteH(int32_t addr, uint16_t value, Instruction* instr);
+ inline void WriteH(int32_t addr, int16_t value, Instruction* instr);
+
+ inline int ReadW(int32_t addr, Instruction* instr);
+ inline void WriteW(int32_t addr, int value, Instruction* instr);
+
+ int32_t* ReadDW(int32_t addr);
+ void WriteDW(int32_t addr, int32_t value1, int32_t value2);
+
+ // Executing is handled based on the instruction type.
+ // Both type 0 and type 1 rolled into one.
+ void DecodeType01(Instruction* instr);
+ void DecodeType2(Instruction* instr);
+ void DecodeType3(Instruction* instr);
+ void DecodeType4(Instruction* instr);
+ void DecodeType5(Instruction* instr);
+ void DecodeType6(Instruction* instr);
+ void DecodeType7(Instruction* instr);
+
+ // Support for VFP.
+ void DecodeTypeVFP(Instruction* instr);
+ void DecodeType6CoprocessorIns(Instruction* instr);
+ void DecodeSpecialCondition(Instruction* instr);
+
+ void DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(Instruction* instr);
+ void DecodeVCMP(Instruction* instr);
+ void DecodeVCVTBetweenDoubleAndSingle(Instruction* instr);
+ void DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr);
+
+ // Executes one instruction.
+ void InstructionDecode(Instruction* instr);
+
+ // ICache.
+ static void CheckICache(v8::internal::HashMap* i_cache, Instruction* instr);
+ static void FlushOnePage(v8::internal::HashMap* i_cache, intptr_t start,
+ int size);
+ static CachePage* GetCachePage(v8::internal::HashMap* i_cache, void* page);
+
+ // Runtime call support.
+ static void* RedirectExternalReference(
+ void* external_function,
+ v8::internal::ExternalReference::Type type);
+
+ // Handle arguments and return value for runtime FP functions.
+ void GetFpArgs(double* x, double* y, int32_t* z);
+ void SetFpResult(const double& result);
+ void TrashCallerSaveRegisters();
+
+ template<class ReturnType, int register_size>
+ ReturnType GetFromVFPRegister(int reg_index);
+
+ template<class InputType, int register_size>
+ void SetVFPRegister(int reg_index, const InputType& value);
+
+ void CallInternal(byte* entry);
+
+ // Architecture state.
+ // Saturating instructions require a Q flag to indicate saturation.
+ // There is currently no way to read the CPSR directly, and thus read the Q
+ // flag, so this is left unimplemented.
+ int32_t registers_[16];
+ bool n_flag_;
+ bool z_flag_;
+ bool c_flag_;
+ bool v_flag_;
+
+ // VFP architecture state.
+ unsigned int vfp_registers_[num_d_registers * 2];
+ bool n_flag_FPSCR_;
+ bool z_flag_FPSCR_;
+ bool c_flag_FPSCR_;
+ bool v_flag_FPSCR_;
+
+ // VFP rounding mode. See ARM DDI 0406B Page A2-29.
+ VFPRoundingMode FPSCR_rounding_mode_;
+ bool FPSCR_default_NaN_mode_;
+
+ // VFP FP exception flags architecture state.
+ bool inv_op_vfp_flag_;
+ bool div_zero_vfp_flag_;
+ bool overflow_vfp_flag_;
+ bool underflow_vfp_flag_;
+ bool inexact_vfp_flag_;
+
+ // Simulator support.
+ char* stack_;
+ bool pc_modified_;
+ int icount_;
+
+ // Debugger input.
+ char* last_debugger_input_;
+
+ // Icache simulation
+ v8::internal::HashMap* i_cache_;
+
+ // Registered breakpoints.
+ Instruction* break_pc_;
+ Instr break_instr_;
+
+ v8::internal::Isolate* isolate_;
+
+ // A stop is watched if its code is less than kNumOfWatchedStops.
+ // Only watched stops support enabling/disabling and the counter feature.
+ static const uint32_t kNumOfWatchedStops = 256;
+
+ // Breakpoint is disabled if bit 31 is set.
+ static const uint32_t kStopDisabledBit = 1 << 31;
+
+ // A stop is enabled, meaning the simulator will stop when meeting the
+ // instruction, if bit 31 of watched_stops_[code].count is unset.
+ // The value watched_stops_[code].count & ~(1 << 31) indicates how many times
+ // the breakpoint was hit or gone through.
+ struct StopCountAndDesc {
+ uint32_t count;
+ char* desc;
+ };
+ StopCountAndDesc watched_stops_[kNumOfWatchedStops];
+};
+
+
+// When running with the simulator transition into simulated execution at this
+// point.
+#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
+ reinterpret_cast<Object*>(Simulator::current(Isolate::Current())->Call( \
+ FUNCTION_ADDR(entry), 5, p0, p1, p2, p3, p4))
+
+#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \
+ Simulator::current(Isolate::Current())->Call( \
+ entry, 10, p0, p1, p2, p3, NULL, p4, p5, p6, p7, p8)
+
+#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
+ try_catch_address == NULL ? \
+ NULL : *(reinterpret_cast<TryCatch**>(try_catch_address))
+
+
+// The simulator has its own stack. Thus it has a different stack limit from
+// the C-based native code. Setting the c_limit to indicate a very small
+// stack cause stack overflow errors, since the simulator ignores the input.
+// This is unlikely to be an issue in practice, though it might cause testing
+// trouble down the line.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+ static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
+ uintptr_t c_limit) {
+ return Simulator::current(isolate)->StackLimit();
+ }
+
+ static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
+ Simulator* sim = Simulator::current(Isolate::Current());
+ return sim->PushAddress(try_catch_address);
+ }
+
+ static inline void UnregisterCTryCatch() {
+ Simulator::current(Isolate::Current())->PopAddress();
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // !defined(USE_SIMULATOR)
+#endif // V8_ARM_SIMULATOR_ARM_H_
diff --git a/chromium/v8/src/arm/stub-cache-arm.cc b/chromium/v8/src/arm/stub-cache-arm.cc
new file mode 100644
index 00000000000..f7fa9efaca7
--- /dev/null
+++ b/chromium/v8/src/arm/stub-cache-arm.cc
@@ -0,0 +1,3700 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_ARM
+
+#include "ic-inl.h"
+#include "codegen.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+static void ProbeTable(Isolate* isolate,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ StubCache::Table table,
+ Register receiver,
+ Register name,
+ // Number of the cache entry, not scaled.
+ Register offset,
+ Register scratch,
+ Register scratch2,
+ Register offset_scratch) {
+ ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
+ ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
+ ExternalReference map_offset(isolate->stub_cache()->map_reference(table));
+
+ uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address());
+ uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address());
+ uint32_t map_off_addr = reinterpret_cast<uint32_t>(map_offset.address());
+
+ // Check the relative positions of the address fields.
+ ASSERT(value_off_addr > key_off_addr);
+ ASSERT((value_off_addr - key_off_addr) % 4 == 0);
+ ASSERT((value_off_addr - key_off_addr) < (256 * 4));
+ ASSERT(map_off_addr > key_off_addr);
+ ASSERT((map_off_addr - key_off_addr) % 4 == 0);
+ ASSERT((map_off_addr - key_off_addr) < (256 * 4));
+
+ Label miss;
+ Register base_addr = scratch;
+ scratch = no_reg;
+
+ // Multiply by 3 because there are 3 fields per entry (name, code, map).
+ __ add(offset_scratch, offset, Operand(offset, LSL, 1));
+
+ // Calculate the base address of the entry.
+ __ mov(base_addr, Operand(key_offset));
+ __ add(base_addr, base_addr, Operand(offset_scratch, LSL, kPointerSizeLog2));
+
+ // Check that the key in the entry matches the name.
+ __ ldr(ip, MemOperand(base_addr, 0));
+ __ cmp(name, ip);
+ __ b(ne, &miss);
+
+ // Check the map matches.
+ __ ldr(ip, MemOperand(base_addr, map_off_addr - key_off_addr));
+ __ ldr(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ cmp(ip, scratch2);
+ __ b(ne, &miss);
+
+ // Get the code entry from the cache.
+ Register code = scratch2;
+ scratch2 = no_reg;
+ __ ldr(code, MemOperand(base_addr, value_off_addr - key_off_addr));
+
+ // Check that the flags match what we're looking for.
+ Register flags_reg = base_addr;
+ base_addr = no_reg;
+ __ ldr(flags_reg, FieldMemOperand(code, Code::kFlagsOffset));
+ // It's a nice optimization if this constant is encodable in the bic insn.
+
+ uint32_t mask = Code::kFlagsNotUsedInLookup;
+ ASSERT(__ ImmediateFitsAddrMode1Instruction(mask));
+ __ bic(flags_reg, flags_reg, Operand(mask));
+ __ cmp(flags_reg, Operand(flags));
+ __ b(ne, &miss);
+
+#ifdef DEBUG
+ if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
+ __ jmp(&miss);
+ } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
+ __ jmp(&miss);
+ }
+#endif
+
+ // Jump to the first instruction in the code stub.
+ __ add(pc, code, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Miss: fall through.
+ __ bind(&miss);
+}
+
+
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// 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 unique and receiver must be a heap object.
+static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ Handle<Name> name,
+ Register scratch0,
+ Register scratch1) {
+ ASSERT(name->IsUniqueName());
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
+ __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
+
+ Label done;
+
+ const int kInterceptorOrAccessCheckNeededMask =
+ (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
+
+ // Bail out if the receiver has a named interceptor or requires access checks.
+ Register map = scratch1;
+ __ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ tst(scratch0, Operand(kInterceptorOrAccessCheckNeededMask));
+ __ b(ne, miss_label);
+
+ // Check that receiver is a JSObject.
+ __ ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ cmp(scratch0, Operand(FIRST_SPEC_OBJECT_TYPE));
+ __ b(lt, miss_label);
+
+ // Load properties array.
+ Register properties = scratch0;
+ __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ // Check that the properties array is a dictionary.
+ __ ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset));
+ Register tmp = properties;
+ __ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
+ __ cmp(map, tmp);
+ __ b(ne, miss_label);
+
+ // Restore the temporarily used register.
+ __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+
+
+ NameDictionaryLookupStub::GenerateNegativeLookup(masm,
+ miss_label,
+ &done,
+ receiver,
+ properties,
+ name,
+ scratch1);
+ __ bind(&done);
+ __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
+}
+
+
+void StubCache::GenerateProbe(MacroAssembler* masm,
+ Code::Flags flags,
+ Register receiver,
+ Register name,
+ Register scratch,
+ Register extra,
+ Register extra2,
+ Register extra3) {
+ Isolate* isolate = masm->isolate();
+ Label miss;
+
+ // Make sure that code is valid. The multiplying code relies on the
+ // entry size being 12.
+ ASSERT(sizeof(Entry) == 12);
+
+ // Make sure the flags does not name a specific type.
+ ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
+
+ // Make sure 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));
+ ASSERT(!extra2.is(receiver));
+ ASSERT(!extra2.is(name));
+ ASSERT(!extra2.is(scratch));
+ ASSERT(!extra2.is(extra));
+
+ // Check scratch, extra and extra2 registers are valid.
+ ASSERT(!scratch.is(no_reg));
+ ASSERT(!extra.is(no_reg));
+ ASSERT(!extra2.is(no_reg));
+ ASSERT(!extra3.is(no_reg));
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1,
+ extra2, extra3);
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Get the map of the receiver and compute the hash.
+ __ ldr(scratch, FieldMemOperand(name, Name::kHashFieldOffset));
+ __ ldr(ip, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ add(scratch, scratch, Operand(ip));
+ uint32_t mask = kPrimaryTableSize - 1;
+ // We shift out the last two bits because they are not part of the hash and
+ // they are always 01 for maps.
+ __ mov(scratch, Operand(scratch, LSR, kHeapObjectTagSize));
+ // Mask down the eor argument to the minimum to keep the immediate
+ // ARM-encodable.
+ __ eor(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask));
+ // Prefer and_ to ubfx here because ubfx takes 2 cycles.
+ __ and_(scratch, scratch, Operand(mask));
+
+ // Probe the primary table.
+ ProbeTable(isolate,
+ masm,
+ flags,
+ kPrimary,
+ receiver,
+ name,
+ scratch,
+ extra,
+ extra2,
+ extra3);
+
+ // Primary miss: Compute hash for secondary probe.
+ __ sub(scratch, scratch, Operand(name, LSR, kHeapObjectTagSize));
+ uint32_t mask2 = kSecondaryTableSize - 1;
+ __ add(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask2));
+ __ and_(scratch, scratch, Operand(mask2));
+
+ // Probe the secondary table.
+ ProbeTable(isolate,
+ masm,
+ flags,
+ kSecondary,
+ receiver,
+ name,
+ scratch,
+ extra,
+ extra2,
+ extra3);
+
+ // Cache miss: Fall-through and let caller handle the miss by
+ // entering the runtime system.
+ __ bind(&miss);
+ __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1,
+ extra2, extra3);
+}
+
+
+void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
+ int index,
+ Register prototype) {
+ // Load the global or builtins object from the current context.
+ __ ldr(prototype,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ // Load the native context from the global or builtins object.
+ __ ldr(prototype,
+ FieldMemOperand(prototype, GlobalObject::kNativeContextOffset));
+ // Load the function from the native context.
+ __ ldr(prototype, MemOperand(prototype, Context::SlotOffset(index)));
+ // Load the initial map. The global functions all have initial maps.
+ __ ldr(prototype,
+ FieldMemOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset));
+ // Load the prototype from the initial map.
+ __ ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
+}
+
+
+void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
+ 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_OBJECT_INDEX)));
+ __ Move(ip, isolate->global_object());
+ __ cmp(prototype, ip);
+ __ b(ne, miss);
+ // Get the global function with the given index.
+ Handle<JSFunction> function(
+ JSFunction::cast(isolate->native_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.
+ __ ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
+}
+
+
+void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
+ Register dst,
+ Register src,
+ bool inobject,
+ int index,
+ Representation representation) {
+ ASSERT(!FLAG_track_double_fields || !representation.IsDouble());
+ int offset = index * kPointerSize;
+ if (!inobject) {
+ // Calculate the offset into the properties array.
+ offset = offset + FixedArray::kHeaderSize;
+ __ ldr(dst, FieldMemOperand(src, JSObject::kPropertiesOffset));
+ src = dst;
+ }
+ __ ldr(dst, FieldMemOperand(src, offset));
+}
+
+
+void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch,
+ Label* miss_label) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss_label);
+
+ // Check that the object is a JS array.
+ __ CompareObjectType(receiver, scratch, scratch, JS_ARRAY_TYPE);
+ __ b(ne, miss_label);
+
+ // Load length directly from the JS array.
+ __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Ret();
+}
+
+
+// Generate code to check if an object is a string. If the object is a
+// heap object, its map's instance type is left in the scratch1 register.
+// If this is not needed, scratch1 and scratch2 may be the same register.
+static void GenerateStringCheck(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* smi,
+ Label* non_string_object) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, smi);
+
+ // Check that the object is a string.
+ __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ and_(scratch2, scratch1, Operand(kIsNotStringMask));
+ // The cast is to resolve the overload for the argument of 0x0.
+ __ cmp(scratch2, Operand(static_cast<int32_t>(kStringTag)));
+ __ b(ne, non_string_object);
+}
+
+
+// Generate code to load the length from a string object and return the length.
+// If the receiver object is not a string or a wrapped string object the
+// execution continues at the miss label. The register containing the
+// receiver is potentially clobbered.
+void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss,
+ bool support_wrappers) {
+ Label check_wrapper;
+
+ // Check if the object is a string leaving the instance type in the
+ // scratch1 register.
+ GenerateStringCheck(masm, receiver, scratch1, scratch2, miss,
+ support_wrappers ? &check_wrapper : miss);
+
+ // Load length directly from the string.
+ __ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset));
+ __ Ret();
+
+ if (support_wrappers) {
+ // Check if the object is a JSValue wrapper.
+ __ bind(&check_wrapper);
+ __ cmp(scratch1, Operand(JS_VALUE_TYPE));
+ __ b(ne, miss);
+
+ // Unwrap the value and check if the wrapped value is a string.
+ __ ldr(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset));
+ GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss);
+ __ ldr(r0, FieldMemOperand(scratch1, String::kLengthOffset));
+ __ Ret();
+ }
+}
+
+
+void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label) {
+ __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
+ __ mov(r0, scratch1);
+ __ Ret();
+}
+
+
+// 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.
+static void GenerateCheckPropertyCell(MacroAssembler* masm,
+ Handle<GlobalObject> global,
+ Handle<Name> name,
+ Register scratch,
+ Label* miss) {
+ Handle<Cell> cell = GlobalObject::EnsurePropertyCell(global, name);
+ ASSERT(cell->value()->IsTheHole());
+ __ mov(scratch, Operand(cell));
+ __ ldr(scratch, FieldMemOperand(scratch, Cell::kValueOffset));
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(scratch, ip);
+ __ b(ne, miss);
+}
+
+
+void BaseStoreStubCompiler::GenerateNegativeHolderLookup(
+ MacroAssembler* masm,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Handle<Name> name,
+ Label* miss) {
+ if (holder->IsJSGlobalObject()) {
+ GenerateCheckPropertyCell(
+ masm, Handle<GlobalObject>::cast(holder), name, scratch1(), miss);
+ } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) {
+ GenerateDictionaryNegativeLookup(
+ masm, miss, holder_reg, name, scratch1(), scratch2());
+ }
+}
+
+
+// Generate StoreTransition code, value is passed in r0 register.
+// When leaving generated code after success, the receiver_reg and name_reg
+// may be clobbered. Upon branch to miss_label, the receiver and name
+// registers have their original values.
+void BaseStoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ Handle<Name> name,
+ Register receiver_reg,
+ Register storage_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* miss_label,
+ Label* slow) {
+ // r0 : value
+ Label exit;
+
+ int descriptor = transition->LastAdded();
+ DescriptorArray* descriptors = transition->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+ Representation representation = details.representation();
+ ASSERT(!representation.IsNone());
+
+ if (details.type() == CONSTANT) {
+ Handle<Object> constant(descriptors->GetValue(descriptor), masm->isolate());
+ __ LoadObject(scratch1, constant);
+ __ cmp(value_reg, scratch1);
+ __ b(ne, miss_label);
+ } else if (FLAG_track_fields && representation.IsSmi()) {
+ __ JumpIfNotSmi(value_reg, miss_label);
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ __ JumpIfSmi(value_reg, miss_label);
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ Label do_store, heap_number;
+ __ LoadRoot(scratch3, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow);
+
+ __ JumpIfNotSmi(value_reg, &heap_number);
+ __ SmiUntag(scratch1, value_reg);
+ __ vmov(s0, scratch1);
+ __ vcvt_f64_s32(d0, s0);
+ __ jmp(&do_store);
+
+ __ bind(&heap_number);
+ __ CheckMap(value_reg, scratch1, Heap::kHeapNumberMapRootIndex,
+ miss_label, DONT_DO_SMI_CHECK);
+ __ vldr(d0, FieldMemOperand(value_reg, HeapNumber::kValueOffset));
+
+ __ bind(&do_store);
+ __ vstr(d0, FieldMemOperand(storage_reg, HeapNumber::kValueOffset));
+ }
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ // Perform map transition for the receiver if necessary.
+ if (details.type() == FIELD &&
+ 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(transition));
+ __ Push(r2, r0);
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
+ masm->isolate()),
+ 3,
+ 1);
+ return;
+ }
+
+ // Update the map of the object.
+ __ mov(scratch1, Operand(transition));
+ __ str(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
+
+ // Update the write barrier for the map field.
+ __ RecordWriteField(receiver_reg,
+ HeapObject::kMapOffset,
+ scratch1,
+ scratch2,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+
+ if (details.type() == CONSTANT) {
+ ASSERT(value_reg.is(r0));
+ __ Ret();
+ return;
+ }
+
+ int index = transition->instance_descriptors()->GetFieldIndex(
+ transition->LastAdded());
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ // TODO(verwaest): Share this code as a code stub.
+ SmiCheck smi_check = representation.IsTagged()
+ ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ __ str(storage_reg, FieldMemOperand(receiver_reg, offset));
+ } else {
+ __ str(value_reg, FieldMemOperand(receiver_reg, offset));
+ }
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ if (!FLAG_track_double_fields || !representation.IsDouble()) {
+ __ mov(storage_reg, value_reg);
+ }
+ __ RecordWriteField(receiver_reg,
+ offset,
+ storage_reg,
+ scratch1,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array
+ __ ldr(scratch1,
+ FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ __ str(storage_reg, FieldMemOperand(scratch1, offset));
+ } else {
+ __ str(value_reg, FieldMemOperand(scratch1, offset));
+ }
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ if (!FLAG_track_double_fields || !representation.IsDouble()) {
+ __ mov(storage_reg, value_reg);
+ }
+ __ RecordWriteField(scratch1,
+ offset,
+ storage_reg,
+ receiver_reg,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ }
+
+ // Return the value (register r0).
+ ASSERT(value_reg.is(r0));
+ __ bind(&exit);
+ __ Ret();
+}
+
+
+// Generate StoreField code, value is passed in r0 register.
+// When leaving generated code after success, the receiver_reg and name_reg
+// may be clobbered. Upon branch to miss_label, the receiver and name
+// registers have their original values.
+void BaseStoreStubCompiler::GenerateStoreField(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Register receiver_reg,
+ Register name_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label) {
+ // r0 : value
+ Label exit;
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ int index = lookup->GetFieldIndex().field_index();
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ Representation representation = lookup->representation();
+ ASSERT(!representation.IsNone());
+ if (FLAG_track_fields && representation.IsSmi()) {
+ __ JumpIfNotSmi(value_reg, miss_label);
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ __ JumpIfSmi(value_reg, miss_label);
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ // Load the double storage.
+ if (index < 0) {
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ ldr(scratch1, FieldMemOperand(receiver_reg, offset));
+ } else {
+ __ ldr(scratch1,
+ FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ __ ldr(scratch1, FieldMemOperand(scratch1, offset));
+ }
+
+ // Store the value into the storage.
+ Label do_store, heap_number;
+ __ JumpIfNotSmi(value_reg, &heap_number);
+ __ SmiUntag(scratch2, value_reg);
+ __ vmov(s0, scratch2);
+ __ vcvt_f64_s32(d0, s0);
+ __ jmp(&do_store);
+
+ __ bind(&heap_number);
+ __ CheckMap(value_reg, scratch2, Heap::kHeapNumberMapRootIndex,
+ miss_label, DONT_DO_SMI_CHECK);
+ __ vldr(d0, FieldMemOperand(value_reg, HeapNumber::kValueOffset));
+
+ __ bind(&do_store);
+ __ vstr(d0, FieldMemOperand(scratch1, HeapNumber::kValueOffset));
+ // Return the value (register r0).
+ ASSERT(value_reg.is(r0));
+ __ Ret();
+ return;
+ }
+
+ // TODO(verwaest): Share this code as a code stub.
+ SmiCheck smi_check = representation.IsTagged()
+ ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ str(value_reg, FieldMemOperand(receiver_reg, offset));
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Skip updating write barrier if storing a smi.
+ __ JumpIfSmi(value_reg, &exit);
+
+ // Update the write barrier for the array address.
+ // Pass the now unused name_reg as a scratch register.
+ __ mov(name_reg, value_reg);
+ __ RecordWriteField(receiver_reg,
+ offset,
+ name_reg,
+ scratch1,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array
+ __ ldr(scratch1,
+ FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
+ __ str(value_reg, FieldMemOperand(scratch1, offset));
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Skip updating write barrier if storing a smi.
+ __ JumpIfSmi(value_reg, &exit);
+
+ // Update the write barrier for the array address.
+ // Ok to clobber receiver_reg and name_reg, since we return.
+ __ mov(name_reg, value_reg);
+ __ RecordWriteField(scratch1,
+ offset,
+ name_reg,
+ receiver_reg,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ }
+
+ // Return the value (register r0).
+ ASSERT(value_reg.is(r0));
+ __ bind(&exit);
+ __ Ret();
+}
+
+
+void BaseStoreStubCompiler::GenerateRestoreName(MacroAssembler* masm,
+ Label* label,
+ Handle<Name> name) {
+ if (!label->is_unused()) {
+ __ bind(label);
+ __ mov(this->name(), Operand(name));
+ }
+}
+
+
+static void GenerateCallFunction(MacroAssembler* masm,
+ Handle<Object> object,
+ const ParameterCount& arguments,
+ Label* miss,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- r0: receiver
+ // -- r1: function to call
+ // -----------------------------------
+
+ // Check that the function really is a function.
+ __ JumpIfSmi(r1, miss);
+ __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
+ __ b(ne, miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ ldr(r3, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
+ __ str(r3, MemOperand(sp, arguments.immediate() * kPointerSize));
+ }
+
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(r1, arguments, JUMP_FUNCTION, NullCallWrapper(), call_kind);
+}
+
+
+static void PushInterceptorArguments(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ Handle<JSObject> holder_obj) {
+ __ push(name);
+ Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor());
+ ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor));
+ Register scratch = name;
+ __ mov(scratch, Operand(interceptor));
+ __ push(scratch);
+ __ push(receiver);
+ __ push(holder);
+ __ ldr(scratch, FieldMemOperand(scratch, InterceptorInfo::kDataOffset));
+ __ push(scratch);
+ __ mov(scratch, Operand(ExternalReference::isolate_address(masm->isolate())));
+ __ push(scratch);
+}
+
+
+static void CompileCallLoadPropertyWithInterceptor(
+ MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ Handle<JSObject> holder_obj) {
+ PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly),
+ masm->isolate());
+ __ mov(r0, Operand(6));
+ __ mov(r1, Operand(ref));
+
+ CEntryStub stub(1);
+ __ CallStub(&stub);
+}
+
+
+static const int kFastApiCallArguments = FunctionCallbackArguments::kArgsLength;
+
+// Reserves space for the extra arguments to API function in the
+// caller's frame.
+//
+// These arguments are set by CheckPrototypes and GenerateFastApiDirectCall.
+static void ReserveSpaceForFastApiCall(MacroAssembler* masm,
+ Register scratch) {
+ __ mov(scratch, Operand(Smi::FromInt(0)));
+ for (int i = 0; i < kFastApiCallArguments; i++) {
+ __ push(scratch);
+ }
+}
+
+
+// Undoes the effects of ReserveSpaceForFastApiCall.
+static void FreeSpaceForFastApiCall(MacroAssembler* masm) {
+ __ Drop(kFastApiCallArguments);
+}
+
+
+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[8] : call data
+ // -- sp[12] : isolate
+ // -- sp[16] : ReturnValue default value
+ // -- sp[20] : ReturnValue
+ // -- sp[24] : last JS argument
+ // -- ...
+ // -- sp[(argc + 5) * 4] : first JS argument
+ // -- sp[(argc + 6) * 4] : receiver
+ // -----------------------------------
+ // Get the function and setup the context.
+ Handle<JSFunction> function = optimization.constant_function();
+ __ LoadHeapObject(r5, function);
+ __ ldr(cp, FieldMemOperand(r5, JSFunction::kContextOffset));
+
+ // Pass the additional arguments.
+ Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
+ Handle<Object> call_data(api_call_info->data(), masm->isolate());
+ if (masm->isolate()->heap()->InNewSpace(*call_data)) {
+ __ Move(r0, api_call_info);
+ __ ldr(r6, FieldMemOperand(r0, CallHandlerInfo::kDataOffset));
+ } else {
+ __ Move(r6, call_data);
+ }
+ __ mov(r7, Operand(ExternalReference::isolate_address(masm->isolate())));
+ // Store JS function, call data, isolate ReturnValue default and ReturnValue.
+ __ stm(ib, sp, r5.bit() | r6.bit() | r7.bit());
+ __ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
+ __ str(r5, MemOperand(sp, 4 * kPointerSize));
+ __ str(r5, MemOperand(sp, 5 * kPointerSize));
+
+ // Prepare arguments.
+ __ add(r2, sp, Operand(5 * kPointerSize));
+
+ // Allocate the v8::Arguments structure in the arguments' space since
+ // it's not controlled by GC.
+ const int kApiStackSpace = 4;
+
+ FrameScope frame_scope(masm, StackFrame::MANUAL);
+ __ EnterExitFrame(false, kApiStackSpace);
+
+ // r0 = v8::Arguments&
+ // Arguments is after the return address.
+ __ add(r0, sp, Operand(1 * kPointerSize));
+ // v8::Arguments::implicit_args_
+ __ str(r2, MemOperand(r0, 0 * kPointerSize));
+ // v8::Arguments::values_
+ __ add(ip, r2, Operand(argc * kPointerSize));
+ __ str(ip, MemOperand(r0, 1 * kPointerSize));
+ // v8::Arguments::length_ = argc
+ __ mov(ip, Operand(argc));
+ __ str(ip, MemOperand(r0, 2 * kPointerSize));
+ // v8::Arguments::is_construct_call = 0
+ __ mov(ip, Operand::Zero());
+ __ str(ip, MemOperand(r0, 3 * kPointerSize));
+
+ const int kStackUnwindSpace = argc + kFastApiCallArguments + 1;
+ Address function_address = v8::ToCData<Address>(api_call_info->callback());
+ bool returns_handle =
+ !CallbackTable::ReturnsVoid(masm->isolate(), function_address);
+ ApiFunction fun(function_address);
+ ExternalReference::Type type =
+ returns_handle ?
+ ExternalReference::DIRECT_API_CALL :
+ ExternalReference::DIRECT_API_CALL_NEW;
+ ExternalReference ref = ExternalReference(&fun,
+ type,
+ masm->isolate());
+ Address thunk_address = returns_handle
+ ? FUNCTION_ADDR(&InvokeInvocationCallback)
+ : FUNCTION_ADDR(&InvokeFunctionCallback);
+ ExternalReference::Type thunk_type =
+ returns_handle ?
+ ExternalReference::PROFILING_API_CALL :
+ ExternalReference::PROFILING_API_CALL_NEW;
+ ApiFunction thunk_fun(thunk_address);
+ ExternalReference thunk_ref = ExternalReference(&thunk_fun, thunk_type,
+ masm->isolate());
+
+ AllowExternalCallThatCantCauseGC scope(masm);
+ __ CallApiFunctionAndReturn(ref,
+ function_address,
+ thunk_ref,
+ r1,
+ kStackUnwindSpace,
+ returns_handle,
+ kFastApiCallArguments + 1);
+}
+
+
+class CallInterceptorCompiler BASE_EMBEDDED {
+ public:
+ CallInterceptorCompiler(StubCompiler* stub_compiler,
+ const ParameterCount& arguments,
+ Register name,
+ Code::ExtraICState extra_ic_state)
+ : stub_compiler_(stub_compiler),
+ arguments_(arguments),
+ name_(name),
+ extra_ic_state_(extra_ic_state) {}
+
+ void Compile(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> 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()) {
+ CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3,
+ holder, lookup, name, optimization, miss);
+ } else {
+ CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3,
+ name, holder, miss);
+ }
+ }
+
+ private:
+ void CompileCacheable(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<JSObject> interceptor_holder,
+ LookupResult* lookup,
+ Handle<Name> 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, Handle<JSObject>(lookup->holder()));
+ }
+ can_do_fast_api_call =
+ depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth;
+ }
+
+ __ IncrementCounter(counters->call_const_interceptor(), 1,
+ scratch1, scratch2);
+
+ if (can_do_fast_api_call) {
+ __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1,
+ scratch1, scratch2);
+ ReserveSpaceForFastApiCall(masm, scratch1);
+ }
+
+ // Check that the maps from receiver to interceptor's holder
+ // haven't changed and thus we can invoke interceptor.
+ 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);
+
+ // Invoke an interceptor and if it provides a value,
+ // branch to |regular_invoke|.
+ Label regular_invoke;
+ LoadWithInterceptor(masm, receiver, holder, interceptor_holder, scratch2,
+ &regular_invoke);
+
+ // Interceptor returned nothing for this property. Try to use cached
+ // constant function.
+
+ // 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()) {
+ stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
+ 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
+ // safe to omit it here, as if present, it should be fetched
+ // by the previous CheckPrototypes.
+ ASSERT(depth2 == kInvalidProtoDepth);
+ }
+
+ // Invoke function.
+ if (can_do_fast_api_call) {
+ GenerateFastApiDirectCall(masm, optimization, arguments_.immediate());
+ } else {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ Handle<JSFunction> function = optimization.constant_function();
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments_,
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+ }
+
+ // Deferred code for fast API call case---clean preallocated space.
+ if (can_do_fast_api_call) {
+ __ bind(&miss_cleanup);
+ FreeSpaceForFastApiCall(masm);
+ __ b(miss_label);
+ }
+
+ // Invoke a regular function.
+ __ bind(&regular_invoke);
+ if (can_do_fast_api_call) {
+ FreeSpaceForFastApiCall(masm);
+ }
+ }
+
+ void CompileRegular(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<Name> name,
+ Handle<JSObject> interceptor_holder,
+ Label* miss_label) {
+ Register holder =
+ stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3,
+ name, miss_label);
+
+ // Call a runtime function to load the interceptor property.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ // Save the name_ register across the call.
+ __ push(name_);
+ PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder);
+ __ CallExternalReference(
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
+ masm->isolate()),
+ 6);
+ // Restore the name_ register.
+ __ pop(name_);
+ // Leave the internal frame.
+ }
+
+ void LoadWithInterceptor(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Handle<JSObject> holder_obj,
+ Register scratch,
+ Label* interceptor_succeeded) {
+ {
+ 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);
+ __ b(ne, interceptor_succeeded);
+ }
+
+ StubCompiler* stub_compiler_;
+ const ParameterCount& arguments_;
+ Register name_;
+ Code::ExtraICState extra_ic_state_;
+};
+
+
+// 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<Name> 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()));
+ }
+}
+
+
+// Convert and store int passed in register ival to IEEE 754 single precision
+// floating point value at memory location (dst + 4 * wordoffset)
+// If VFP3 is available use it for conversion.
+static void StoreIntAsFloat(MacroAssembler* masm,
+ Register dst,
+ Register wordoffset,
+ Register ival,
+ Register scratch1) {
+ __ vmov(s0, ival);
+ __ add(scratch1, dst, Operand(wordoffset, LSL, 2));
+ __ vcvt_f32_s32(s0, s0);
+ __ vstr(s0, scratch1, 0);
+}
+
+
+void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) {
+ __ Jump(code, RelocInfo::CODE_TARGET);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ Handle<Name> name,
+ int save_at_depth,
+ Label* miss,
+ PrototypeCheckType check) {
+ // Make sure that the type feedback oracle harvests the receiver map.
+ // TODO(svenpanne) Remove this hack when all ICs are reworked.
+ __ mov(scratch1, Operand(Handle<Map>(object->map())));
+
+ Handle<JSObject> first = object;
+ // Make sure there's no overlap between holder and object registers.
+ ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
+ ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
+ && !scratch2.is(scratch1));
+
+ // Keep track of the current object in register reg.
+ Register reg = object_reg;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ __ str(reg, MemOperand(sp));
+ }
+
+ // Check the maps in the prototype chain.
+ // Traverse the prototype chain from the object and do map checks.
+ 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());
+
+ Handle<JSObject> prototype(JSObject::cast(current->GetPrototype()));
+ if (!current->HasFastProperties() &&
+ !current->IsJSGlobalObject() &&
+ !current->IsJSGlobalProxy()) {
+ if (!name->IsUniqueName()) {
+ ASSERT(name->IsString());
+ name = factory()->InternalizeString(Handle<String>::cast(name));
+ }
+ ASSERT(current->property_dictionary()->FindEntry(*name) ==
+ NameDictionary::kNotFound);
+
+ GenerateDictionaryNegativeLookup(masm(), miss, reg, name,
+ scratch1, scratch2);
+
+ __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ reg = holder_reg; // From now on the object will be in holder_reg.
+ __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
+ } else {
+ Register map_reg = scratch1;
+ if (!current.is_identical_to(first) || check == CHECK_ALL_MAPS) {
+ Handle<Map> current_map(current->map());
+ // CheckMap implicitly loads the map of |reg| into |map_reg|.
+ __ CheckMap(reg, map_reg, current_map, miss, DONT_DO_SMI_CHECK);
+ } else {
+ __ ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
+ }
+
+ // 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, scratch2, miss);
+ }
+ reg = holder_reg; // From now on the object will be in holder_reg.
+
+ 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(map_reg, Map::kPrototypeOffset));
+ } else {
+ // The prototype is in old space; load it directly.
+ __ mov(reg, Operand(prototype));
+ }
+ }
+
+ if (save_at_depth == depth) {
+ __ str(reg, MemOperand(sp));
+ }
+
+ // Go to the next object in the prototype chain.
+ current = prototype;
+ }
+
+ // Log the check depth.
+ LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
+
+ if (!holder.is_identical_to(first) || check == CHECK_ALL_MAPS) {
+ // Check the holder map.
+ __ CheckMap(reg, scratch1, Handle<Map>(holder->map()), miss,
+ DONT_DO_SMI_CHECK);
+ }
+
+ // 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.
+ GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss);
+
+ // Return the register containing the holder.
+ return reg;
+}
+
+
+void BaseLoadStubCompiler::HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) {
+ if (!miss->is_unused()) {
+ __ b(success);
+ __ bind(miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+ }
+}
+
+
+void BaseStoreStubCompiler::HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) {
+ if (!miss->is_unused()) {
+ __ b(success);
+ GenerateRestoreName(masm(), miss, name);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+ }
+}
+
+
+Register BaseLoadStubCompiler::CallbackHandlerFrontend(
+ Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* success,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label miss;
+
+ Register reg = HandlerFrontendHeader(object, object_reg, holder, name, &miss);
+
+ if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+ ASSERT(!reg.is(scratch2()));
+ ASSERT(!reg.is(scratch3()));
+ ASSERT(!reg.is(scratch4()));
+
+ // Load the properties dictionary.
+ Register dictionary = scratch4();
+ __ ldr(dictionary, FieldMemOperand(reg, JSObject::kPropertiesOffset));
+
+ // Probe the dictionary.
+ Label probe_done;
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm(),
+ &miss,
+ &probe_done,
+ dictionary,
+ this->name(),
+ scratch2(),
+ scratch3());
+ __ bind(&probe_done);
+
+ // If probing finds an entry in the dictionary, scratch3 contains the
+ // pointer into the dictionary. Check that the value is the callback.
+ Register pointer = scratch3();
+ const int kElementsStartOffset = NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ ldr(scratch2(), FieldMemOperand(pointer, kValueOffset));
+ __ cmp(scratch2(), Operand(callback));
+ __ b(ne, &miss);
+ }
+
+ HandlerFrontendFooter(name, success, &miss);
+ return reg;
+}
+
+
+void BaseLoadStubCompiler::NonexistentHandlerFrontend(
+ Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Label* success,
+ Handle<GlobalObject> global) {
+ Label miss;
+
+ HandlerFrontendHeader(object, receiver(), last, name, &miss);
+
+ // If the last object in the prototype chain is a global object,
+ // check that the global property cell is empty.
+ if (!global.is_null()) {
+ GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss);
+ }
+
+ HandlerFrontendFooter(name, success, &miss);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadField(Register reg,
+ Handle<JSObject> holder,
+ PropertyIndex field,
+ Representation representation) {
+ if (!reg.is(receiver())) __ mov(receiver(), reg);
+ if (kind() == Code::LOAD_IC) {
+ LoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode(isolate()));
+ } else {
+ KeyedLoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode(isolate()));
+ }
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadConstant(Handle<Object> value) {
+ // Return the constant value.
+ __ LoadObject(r0, value);
+ __ Ret();
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadCallback(
+ Register reg,
+ Handle<ExecutableAccessorInfo> callback) {
+ // 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_
+ if (heap()->InNewSpace(callback->data())) {
+ __ Move(scratch3(), callback);
+ __ ldr(scratch3(), FieldMemOperand(scratch3(),
+ ExecutableAccessorInfo::kDataOffset));
+ } else {
+ __ Move(scratch3(), Handle<Object>(callback->data(), isolate()));
+ }
+ __ Push(reg, scratch3());
+ __ LoadRoot(scratch3(), Heap::kUndefinedValueRootIndex);
+ __ mov(scratch4(), scratch3());
+ __ Push(scratch3(), scratch4());
+ __ mov(scratch4(),
+ Operand(ExternalReference::isolate_address(isolate())));
+ __ Push(scratch4(), name());
+ __ mov(r0, sp); // r0 = Handle<Name>
+
+ 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.
+ __ str(scratch2(), MemOperand(sp, 1 * kPointerSize));
+ __ add(r1, sp, Operand(1 * kPointerSize)); // r1 = AccessorInfo&
+
+ const int kStackUnwindSpace = kFastApiCallArguments + 1;
+ Address getter_address = v8::ToCData<Address>(callback->getter());
+ bool returns_handle =
+ !CallbackTable::ReturnsVoid(isolate(), getter_address);
+
+ ApiFunction fun(getter_address);
+ ExternalReference::Type type =
+ returns_handle ?
+ ExternalReference::DIRECT_GETTER_CALL :
+ ExternalReference::DIRECT_GETTER_CALL_NEW;
+ ExternalReference ref = ExternalReference(&fun, type, isolate());
+
+ Address thunk_address = returns_handle
+ ? FUNCTION_ADDR(&InvokeAccessorGetter)
+ : FUNCTION_ADDR(&InvokeAccessorGetterCallback);
+ ExternalReference::Type thunk_type =
+ returns_handle ?
+ ExternalReference::PROFILING_GETTER_CALL :
+ ExternalReference::PROFILING_GETTER_CALL_NEW;
+ ApiFunction thunk_fun(thunk_address);
+ ExternalReference thunk_ref = ExternalReference(&thunk_fun, thunk_type,
+ isolate());
+ __ CallApiFunctionAndReturn(ref,
+ getter_address,
+ thunk_ref,
+ r2,
+ kStackUnwindSpace,
+ returns_handle,
+ 5);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadInterceptor(
+ Register holder_reg,
+ Handle<JSObject> object,
+ Handle<JSObject> interceptor_holder,
+ LookupResult* lookup,
+ Handle<Name> name) {
+ ASSERT(interceptor_holder->HasNamedInterceptor());
+ ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // So far the most popular follow ups for interceptor loads are FIELD
+ // and CALLBACKS, so inline only them, other cases may be added
+ // later.
+ bool compile_followup_inline = false;
+ if (lookup->IsFound() && lookup->IsCacheable()) {
+ if (lookup->IsField()) {
+ compile_followup_inline = true;
+ } else if (lookup->type() == CALLBACKS &&
+ lookup->GetCallbackObject()->IsExecutableAccessorInfo()) {
+ ExecutableAccessorInfo* callback =
+ ExecutableAccessorInfo::cast(lookup->GetCallbackObject());
+ compile_followup_inline = callback->getter() != NULL &&
+ callback->IsCompatibleReceiver(*object);
+ }
+ }
+
+ if (compile_followup_inline) {
+ // Compile the interceptor call, followed by inline code to load the
+ // property from further up the prototype chain if the call fails.
+ // Check that the maps haven't changed.
+ ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
+
+ // Preserve the receiver register explicitly whenever it is different from
+ // the holder and it is needed should the interceptor return without any
+ // result. The CALLBACKS case needs the receiver to be passed into C++ code,
+ // the FIELD case might cause a miss during the prototype check.
+ bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
+ bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
+ (lookup->type() == CALLBACKS || must_perfrom_prototype_check);
+
+ // Save necessary data before invoking an interceptor.
+ // Requires a frame to make GC aware of pushed pointers.
+ {
+ FrameScope frame_scope(masm(), StackFrame::INTERNAL);
+ if (must_preserve_receiver_reg) {
+ __ Push(receiver(), holder_reg, this->name());
+ } else {
+ __ Push(holder_reg, this->name());
+ }
+ // 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,
+ this->name(),
+ 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(this->name());
+ __ pop(holder_reg);
+ if (must_preserve_receiver_reg) {
+ __ pop(receiver());
+ }
+ // Leave the internal frame.
+ }
+
+ GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup);
+ } else { // !compile_followup_inline
+ // Call the runtime system to load the interceptor.
+ // Check that the maps haven't changed.
+ PushInterceptorArguments(masm(), receiver(), holder_reg,
+ this->name(), interceptor_holder);
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad),
+ isolate());
+ __ TailCallExternalReference(ref, 6, 1);
+ }
+}
+
+
+void CallStubCompiler::GenerateNameCheck(Handle<Name> name, Label* miss) {
+ if (kind_ == Code::KEYED_CALL_IC) {
+ __ cmp(r2, Operand(name));
+ __ b(ne, miss);
+ }
+}
+
+
+void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss) {
+ ASSERT(holder->IsGlobalObject());
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ // Get the receiver from the stack.
+ __ ldr(r0, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the maps haven't changed.
+ __ JumpIfSmi(r0, miss);
+ CheckPrototypes(object, r0, holder, r3, r1, r4, name, miss);
+}
+
+
+void CallStubCompiler::GenerateLoadFunctionFromCell(
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Label* miss) {
+ // Get the value from the cell.
+ __ mov(r3, Operand(cell));
+ __ ldr(r1, FieldMemOperand(r3, Cell::kValueOffset));
+
+ // Check that the cell contains the same 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
+ // function can all use this call IC. Before we load through the
+ // function, we have to verify that it still is a function.
+ __ JumpIfSmi(r1, miss);
+ __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
+ __ b(ne, miss);
+
+ // Check the shared function info. Make sure it hasn't changed.
+ __ Move(r3, Handle<SharedFunctionInfo>(function->shared()));
+ __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ cmp(r4, r3);
+ } else {
+ __ cmp(r1, Operand(function));
+ }
+ __ b(ne, miss);
+}
+
+
+void CallStubCompiler::GenerateMissBranch() {
+ Handle<Code> code =
+ isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
+ kind_,
+ extra_state_);
+ __ Jump(code, RelocInfo::CODE_TARGET);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ PropertyIndex index,
+ Handle<Name> name) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ const int argc = arguments().immediate();
+
+ // Get the receiver of the function from the stack into r0.
+ __ ldr(r0, MemOperand(sp, argc * kPointerSize));
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(r0, &miss);
+
+ // Do the right check and compute the holder register.
+ Register reg = CheckPrototypes(object, r0, holder, r1, r3, r4, name, &miss);
+ GenerateFastPropertyLoad(masm(), r1, reg, index.is_inobject(holder),
+ index.translate(holder), Representation::Tagged());
+
+ GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::FIELD, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayCodeCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ Label miss;
+
+ // Check that function is still array
+ const int argc = arguments().immediate();
+ GenerateNameCheck(name, &miss);
+ Register receiver = r1;
+
+ if (cell.is_null()) {
+ __ 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(Handle<JSObject>::cast(object), receiver, holder, r3, r0,
+ r4, name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ Handle<AllocationSite> site = isolate()->factory()->NewAllocationSite();
+ site->set_transition_info(Smi::FromInt(GetInitialFastElementsKind()));
+ Handle<Cell> site_feedback_cell = isolate()->factory()->NewCell(site);
+ __ mov(r0, Operand(argc));
+ __ mov(r2, Operand(site_feedback_cell));
+ __ mov(r1, Operand(function));
+
+ ArrayConstructorStub stub(isolate());
+ __ TailCallStub(&stub);
+
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayPushCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ 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));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(Handle<JSObject>::cast(object), receiver, holder, r3, r0, r4,
+ name, &miss);
+
+ if (argc == 0) {
+ // Nothing to do, just return the length.
+ __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Drop(argc + 1);
+ __ Ret();
+ } else {
+ Label call_builtin;
+
+ if (argc == 1) { // Otherwise fall through to call the builtin.
+ Label attempt_to_grow_elements, with_write_barrier, check_double;
+
+ 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,
+ &check_double,
+ DONT_DO_SMI_CHECK);
+
+ // Get the array's length into r0 and calculate new length.
+ __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ add(r0, r0, Operand(Smi::FromInt(argc)));
+
+ // 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.
+ __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize));
+ __ JumpIfNotSmi(r4, &with_write_barrier);
+
+ // 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::PointerOffsetFromSmiKey(r0));
+ const int kEndElementsOffset =
+ FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize;
+ __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex));
+
+ // Check for a smi.
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&check_double);
+
+ // Check that the elements are in fast mode and writable.
+ __ CheckMap(elements,
+ r0,
+ Heap::kFixedDoubleArrayMapRootIndex,
+ &call_builtin,
+ DONT_DO_SMI_CHECK);
+
+ // Get the array's length into r0 and calculate new length.
+ __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ add(r0, r0, Operand(Smi::FromInt(argc)));
+
+ // Get the elements' length.
+ __ ldr(r4, FieldMemOperand(elements, FixedArray::kLengthOffset));
+
+ // Check if we could survive without allocation.
+ __ cmp(r0, r4);
+ __ b(gt, &call_builtin);
+
+ __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize));
+ __ StoreNumberToDoubleElements(r4, r0, elements, r5, d0,
+ &call_builtin, argc * kDoubleSize);
+
+ // Save new length.
+ __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+
+ // Check for a smi.
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&with_write_barrier);
+
+ __ 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);
+ __ CheckFastSmiElements(r3, r7, &call_builtin);
+
+ __ ldr(r7, FieldMemOperand(r4, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
+ __ cmp(r7, ip);
+ __ b(eq, &call_builtin);
+ // edx: receiver
+ // r3: map
+ Label try_holey_map;
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_ELEMENTS,
+ r3,
+ r7,
+ &try_holey_map);
+ __ mov(r2, receiver);
+ ElementsTransitionGenerator::
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
+ __ jmp(&fast_object);
+
+ __ bind(&try_holey_map);
+ __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS,
+ FAST_HOLEY_ELEMENTS,
+ r3,
+ r7,
+ &call_builtin);
+ __ mov(r2, receiver);
+ ElementsTransitionGenerator::
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
+ __ 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::PointerOffsetFromSmiKey(r0));
+ __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex));
+
+ __ RecordWrite(elements,
+ end_elements,
+ r4,
+ kLRHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&attempt_to_grow_elements);
+ // r0: array's length + 1.
+ // r4: elements' length.
+
+ if (!FLAG_inline_new) {
+ __ 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);
+
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+ ExternalReference new_space_allocation_limit =
+ ExternalReference::new_space_allocation_limit_address(isolate());
+
+ const int kAllocationDelta = 4;
+ // Load top and check if it is the end of elements.
+ __ add(end_elements, elements, Operand::PointerOffsetFromSmiKey(r0));
+ __ add(end_elements, end_elements, Operand(kEndElementsOffset));
+ __ mov(r7, Operand(new_space_allocation_top));
+ __ ldr(r3, MemOperand(r7));
+ __ cmp(end_elements, r3);
+ __ b(ne, &call_builtin);
+
+ __ mov(r9, Operand(new_space_allocation_limit));
+ __ ldr(r9, MemOperand(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(r3, MemOperand(r7));
+ // Push the argument.
+ __ str(r2, MemOperand(end_elements));
+ // Fill the rest with holes.
+ __ LoadRoot(r3, Heap::kTheHoleValueRootIndex);
+ for (int i = 1; i < kAllocationDelta; i++) {
+ __ str(r3, MemOperand(end_elements, i * kPointerSize));
+ }
+
+ // Update elements' and array's sizes.
+ __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ add(r4, r4, Operand(Smi::FromInt(kAllocationDelta)));
+ __ str(r4, FieldMemOperand(elements, FixedArray::kLengthOffset));
+
+ // Elements are in new space, so write barrier is not required.
+ __ Drop(argc + 1);
+ __ Ret();
+ }
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(
+ ExternalReference(Builtins::c_ArrayPush, isolate()), argc + 1, 1);
+ }
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayPopCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ 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(Handle<JSObject>::cast(object), receiver, holder, elements,
+ r4, r0, name, &miss);
+
+ // 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);
+
+ // Get the array's length into r4 and calculate new length.
+ __ ldr(r4, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ sub(r4, r4, Operand(Smi::FromInt(1)), SetCC);
+ __ b(lt, &return_undefined);
+
+ // Get the last element.
+ __ LoadRoot(r6, Heap::kTheHoleValueRootIndex);
+ // We can't address the last element in one operation. Compute the more
+ // expensive shift first, and use an offset later on.
+ __ add(elements, elements, Operand::PointerOffsetFromSmiKey(r4));
+ __ ldr(r0, FieldMemOperand(elements, FixedArray::kHeaderSize));
+ __ cmp(r0, r6);
+ __ b(eq, &call_builtin);
+
+ // Set the array's length.
+ __ str(r4, FieldMemOperand(receiver, JSArray::kLengthOffset));
+
+ // Fill with the hole.
+ __ str(r6, FieldMemOperand(elements, FixedArray::kHeaderSize));
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&return_undefined);
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(
+ ExternalReference(Builtins::c_ArrayPop, isolate()), argc + 1, 1);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- r2 : function name
+ // -- lr : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ 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_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.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ r0,
+ &miss);
+ ASSERT(!object.is_identical_to(holder));
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ r0, holder, r1, r3, r4, name, &miss);
+
+ Register receiver = r1;
+ Register index = r4;
+ Register result = r0;
+ __ ldr(receiver, MemOperand(sp, argc * kPointerSize));
+ if (argc > 0) {
+ __ ldr(index, MemOperand(sp, (argc - 1) * kPointerSize));
+ } else {
+ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
+ }
+
+ 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;
+ generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ LoadRoot(r0, Heap::kNanValueRootIndex);
+ __ Drop(argc + 1);
+ __ Ret();
+ }
+
+ __ bind(&miss);
+ // Restore function name in r2.
+ __ Move(r2, name);
+ __ bind(&name_miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringCharAtCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- r2 : function name
+ // -- lr : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ 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_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.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ r0,
+ &miss);
+ ASSERT(!object.is_identical_to(holder));
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ r0, holder, r1, r3, r4, name, &miss);
+
+ Register receiver = r0;
+ Register index = r4;
+ Register scratch = r3;
+ Register result = r0;
+ __ ldr(receiver, MemOperand(sp, argc * kPointerSize));
+ if (argc > 0) {
+ __ ldr(index, MemOperand(sp, (argc - 1) * kPointerSize));
+ } else {
+ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
+ }
+
+ 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;
+ generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ LoadRoot(r0, Heap::kempty_stringRootIndex);
+ __ Drop(argc + 1);
+ __ Ret();
+ }
+
+ __ bind(&miss);
+ // Restore function name in r2.
+ __ Move(r2, name);
+ __ bind(&name_miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- r2 : function name
+ // -- lr : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : 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 Handle<Code>::null();
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ ldr(r1, MemOperand(sp, 1 * kPointerSize));
+
+ __ JumpIfSmi(r1, &miss);
+
+ CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the char code argument.
+ Register code = r1;
+ __ ldr(code, MemOperand(sp, 0 * kPointerSize));
+
+ // Check the code is a smi.
+ Label slow;
+ __ JumpIfNotSmi(code, &slow);
+
+ // Convert the smi code to uint16.
+ __ and_(code, code, Operand(Smi::FromInt(0xffff)));
+
+ StringCharFromCodeGenerator generator(code, r0);
+ generator.GenerateFast(masm());
+ __ Drop(argc + 1);
+ __ Ret();
+
+ StubRuntimeCallHelper 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);
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+
+ __ bind(&miss);
+ // r2: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileMathFloorCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- r2 : function name
+ // -- lr : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : 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 Handle<Code>::null();
+
+ Label miss, slow;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ ldr(r1, MemOperand(sp, 1 * kPointerSize));
+ __ JumpIfSmi(r1, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the (only) argument into r0.
+ __ ldr(r0, MemOperand(sp, 0 * kPointerSize));
+
+ // If the argument is a smi, just return.
+ __ SmiTst(r0);
+ __ Drop(argc + 1, eq);
+ __ Ret(eq);
+
+ __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
+
+ Label smi_check, just_return;
+
+ // Load the HeapNumber value.
+ // We will need access to the value in the core registers, so we load it
+ // with ldrd and move it to the fpu. It also spares a sub instruction for
+ // updating the HeapNumber value address, as vldr expects a multiple
+ // of 4 offset.
+ __ Ldrd(r4, r5, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ __ vmov(d1, r4, r5);
+
+ // Check for NaN, Infinities and -0.
+ // They are invariant through a Math.Floor call, so just
+ // return the original argument.
+ __ Sbfx(r3, r5, HeapNumber::kExponentShift, HeapNumber::kExponentBits);
+ __ cmp(r3, Operand(-1));
+ __ b(eq, &just_return);
+ __ eor(r3, r5, Operand(0x80000000u));
+ __ orr(r3, r3, r4, SetCC);
+ __ b(eq, &just_return);
+ // Test for values that can be exactly represented as a
+ // signed 32-bit integer.
+ __ TryDoubleToInt32Exact(r0, d1, d2);
+ // If exact, check smi
+ __ b(eq, &smi_check);
+ __ cmp(r5, Operand(0));
+
+ // If input is in ]+0, +inf[, the cmp has cleared overflow and negative
+ // (V=0 and N=0), the two following instructions won't execute and
+ // we fall through smi_check to check if the result can fit into a smi.
+
+ // If input is in ]-inf, -0[, sub one and, go to slow if we have
+ // an overflow. Else we fall through smi check.
+ // Hint: if x is a negative, non integer number,
+ // floor(x) <=> round_to_zero(x) - 1.
+ __ sub(r0, r0, Operand(1), SetCC, mi);
+ __ b(vs, &slow);
+
+ __ bind(&smi_check);
+ // Check if the result can fit into an smi. If we had an overflow,
+ // the result is either 0x80000000 or 0x7FFFFFFF and won't fit into an smi.
+ // If result doesn't fit into an smi, branch to slow.
+ __ SmiTag(r0, SetCC);
+ __ b(vs, &slow);
+
+ __ bind(&just_return);
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&slow);
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+
+ __ bind(&miss);
+ // r2: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileMathAbsCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- r2 : function name
+ // -- lr : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : 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 Handle<Code>::null();
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+ if (cell.is_null()) {
+ __ ldr(r1, MemOperand(sp, 1 * kPointerSize));
+ __ JumpIfSmi(r1, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the (only) argument into r0.
+ __ ldr(r0, MemOperand(sp, 0 * kPointerSize));
+
+ // Check if the argument is a smi.
+ Label not_smi;
+ __ JumpIfNotSmi(r0, &not_smi);
+
+ // Do bitwise not or do nothing depending on the sign of the
+ // argument.
+ __ eor(r1, r0, Operand(r0, ASR, kBitsPerInt - 1));
+
+ // Add 1 or do nothing depending on the sign of the argument.
+ __ sub(r0, r1, Operand(r0, ASR, kBitsPerInt - 1), SetCC);
+
+ // If the result is still negative, go to the slow case.
+ // This only happens for the most negative smi.
+ Label slow;
+ __ b(mi, &slow);
+
+ // Smi case done.
+ __ Drop(argc + 1);
+ __ Ret();
+
+ // Check if the argument is a heap number and load its exponent and
+ // sign.
+ __ bind(&not_smi);
+ __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
+ __ ldr(r1, FieldMemOperand(r0, HeapNumber::kExponentOffset));
+
+ // Check the sign of the argument. If the argument is positive,
+ // just return it.
+ Label negative_sign;
+ __ tst(r1, Operand(HeapNumber::kSignMask));
+ __ b(ne, &negative_sign);
+ __ Drop(argc + 1);
+ __ Ret();
+
+ // If the argument is negative, clear the sign, and return a new
+ // number.
+ __ bind(&negative_sign);
+ __ eor(r1, r1, Operand(HeapNumber::kSignMask));
+ __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
+ __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r0, r4, r5, r6, &slow);
+ __ str(r1, FieldMemOperand(r0, HeapNumber::kExponentOffset));
+ __ str(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
+ __ Drop(argc + 1);
+ __ Ret();
+
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ __ bind(&slow);
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+
+ __ bind(&miss);
+ // r2: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileFastApiCall(
+ const CallOptimization& optimization,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> 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 Handle<Code>::null();
+ if (!cell.is_null()) return Handle<Code>::null();
+ if (!object->IsJSObject()) return Handle<Code>::null();
+ int depth = optimization.GetPrototypeDepthOfExpectedType(
+ 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.
+ const int argc = arguments().immediate();
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(r1, &miss_before_stack_reserved);
+
+ __ IncrementCounter(counters->call_const(), 1, r0, r3);
+ __ IncrementCounter(counters->call_const_fast_api(), 1, r0, r3);
+
+ ReserveSpaceForFastApiCall(masm(), r0);
+
+ // Check that the maps haven't changed and find a Holder as a side effect.
+ CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4, name,
+ depth, &miss);
+
+ GenerateFastApiDirectCall(masm(), optimization, argc);
+
+ __ bind(&miss);
+ FreeSpaceForFastApiCall(masm());
+
+ __ bind(&miss_before_stack_reserved);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Label* success) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack
+ const int argc = arguments().immediate();
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ if (check != NUMBER_CHECK) {
+ __ JumpIfSmi(r1, &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);
+ switch (check) {
+ case RECEIVER_MAP_CHECK:
+ __ IncrementCounter(isolate()->counters()->call_const(), 1, r0, r3);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(Handle<JSObject>::cast(object), r1, holder, r0, r3, r4,
+ name, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
+ __ str(r3, MemOperand(sp, argc * kPointerSize));
+ }
+ break;
+
+ case STRING_CHECK:
+ // Check that the object is a string.
+ __ 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(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ r0, holder, r3, r1, r4, name, &miss);
+ break;
+
+ case SYMBOL_CHECK:
+ // Check that the object is a symbol.
+ __ CompareObjectType(r1, r1, r3, SYMBOL_TYPE);
+ __ b(ne, &miss);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::SYMBOL_FUNCTION_INDEX, r0, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ r0, holder, r3, r1, r4, name, &miss);
+ break;
+
+ case NUMBER_CHECK: {
+ Label fast;
+ // Check that the object is a smi or a heap number.
+ __ JumpIfSmi(r1, &fast);
+ __ CompareObjectType(r1, r0, r0, HEAP_NUMBER_TYPE);
+ __ b(ne, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::NUMBER_FUNCTION_INDEX, r0, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ r0, holder, r3, r1, r4, name, &miss);
+ break;
+ }
+ case BOOLEAN_CHECK: {
+ Label fast;
+ // Check that the object is a boolean.
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+ __ cmp(r1, ip);
+ __ b(eq, &fast);
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
+ __ cmp(r1, ip);
+ __ b(ne, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::BOOLEAN_FUNCTION_INDEX, r0, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ r0, holder, r3, r1, r4, name, &miss);
+ break;
+ }
+ }
+
+ __ b(success);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+}
+
+
+void CallStubCompiler::CompileHandlerBackend(Handle<JSFunction> function) {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallConstant(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Handle<JSFunction> function) {
+ if (HasCustomCallGenerator(function)) {
+ Handle<Code> code = CompileCustomCall(object, holder,
+ Handle<Cell>::null(),
+ function, Handle<String>::cast(name),
+ Code::CONSTANT);
+ // A null handle means bail out to the regular compiler code below.
+ if (!code.is_null()) return code;
+ }
+
+ Label success;
+
+ CompileHandlerFrontend(object, holder, name, check, &success);
+ __ bind(&success);
+ CompileHandlerBackend(function);
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> 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(isolate());
+ LookupPostInterceptor(holder, name, &lookup);
+
+ // Get the receiver from the stack.
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+
+ 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_state_);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::INTERCEPTOR, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallGlobal(
+ Handle<JSObject> object,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ Handle<JSFunction> function,
+ Handle<Name> name) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ if (HasCustomCallGenerator(function)) {
+ Handle<Code> code = CompileCustomCall(
+ object, holder, cell, function, Handle<String>::cast(name),
+ Code::NORMAL);
+ // 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
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ ldr(r3, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
+ __ str(r3, MemOperand(sp, argc * kPointerSize));
+ }
+
+ // Set up the context (function already in r1).
+ __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+
+ // Jump to the cached code (tail call).
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->call_global_inline(), 1, r3, r4);
+ ParameterCount expected(function->shared()->formal_parameter_count());
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ // 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);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::NORMAL, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreCallback(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+
+ // Stub never generated for non-global objects that require access checks.
+ ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
+
+ __ push(receiver()); // receiver
+ __ mov(ip, Operand(callback)); // callback info
+ __ push(ip);
+ __ mov(ip, Operand(name));
+ __ Push(ip, value());
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_callback_property =
+ ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate());
+ __ TailCallExternalReference(store_callback_property, 4, 1);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void StoreStubCompiler::GenerateStoreViaSetter(
+ MacroAssembler* masm,
+ Handle<JSFunction> setter) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : receiver
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Save value register, so we can restore it later.
+ __ push(r0);
+
+ if (!setter.is_null()) {
+ // Call the JavaScript setter with receiver and value on the stack.
+ __ Push(r1, r0);
+ ParameterCount actual(1);
+ ParameterCount expected(setter);
+ __ InvokeFunction(setter, expected, actual,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+ } else {
+ // If we generate a global code snippet for deoptimization only, remember
+ // the place to continue after deoptimization.
+ masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // We have to return the passed value, not the return value of the setter.
+ __ pop(r0);
+
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ }
+ __ Ret();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> StoreStubCompiler::CompileStoreInterceptor(
+ Handle<JSObject> object,
+ Handle<Name> name) {
+ Label miss;
+
+ // Check that the map of the object hasn't changed.
+ __ CheckMap(receiver(), scratch1(), Handle<Map>(object->map()), &miss,
+ DO_SMI_CHECK);
+
+ // Perform global security token check if needed.
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(receiver(), scratch1(), &miss);
+ }
+
+ // Stub is never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ __ Push(receiver(), this->name(), value());
+
+ __ mov(scratch1(), Operand(Smi::FromInt(strict_mode())));
+ __ push(scratch1()); // strict mode
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_ic_property =
+ ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate());
+ __ TailCallExternalReference(store_ic_property, 4, 1);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::INTERCEPTOR, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreGlobal(
+ Handle<GlobalObject> object,
+ Handle<PropertyCell> cell,
+ Handle<Name> name) {
+ Label miss;
+
+ // Check that the map of the global has not changed.
+ __ ldr(scratch1(), FieldMemOperand(receiver(), HeapObject::kMapOffset));
+ __ cmp(scratch1(), Operand(Handle<Map>(object->map())));
+ __ b(ne, &miss);
+
+ // 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.
+ __ mov(scratch1(), Operand(cell));
+ __ LoadRoot(scratch2(), Heap::kTheHoleValueRootIndex);
+ __ ldr(scratch3(), FieldMemOperand(scratch1(), Cell::kValueOffset));
+ __ cmp(scratch3(), scratch2());
+ __ b(eq, &miss);
+
+ // Store the value in the cell.
+ __ str(value(), FieldMemOperand(scratch1(), Cell::kValueOffset));
+ // Cells are always rescanned, so no write barrier here.
+
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(
+ counters->named_store_global_inline(), 1, scratch1(), scratch2());
+ __ Ret();
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ __ IncrementCounter(
+ counters->named_store_global_inline_miss(), 1, scratch1(), scratch2());
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, name);
+}
+
+
+Handle<Code> LoadStubCompiler::CompileLoadNonexistent(
+ Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Handle<GlobalObject> global) {
+ Label success;
+
+ NonexistentHandlerFrontend(object, last, name, &success, global);
+
+ __ bind(&success);
+ // Return undefined if maps of the full prototype chain are still the
+ // same and no global property with this name contains a value.
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
+ __ Ret();
+
+ // Return the generated code.
+ return GetCode(kind(), Code::NONEXISTENT, name);
+}
+
+
+Register* LoadStubCompiler::registers() {
+ // receiver, name, scratch1, scratch2, scratch3, scratch4.
+ static Register registers[] = { r0, r2, r3, r1, r4, r5 };
+ return registers;
+}
+
+
+Register* KeyedLoadStubCompiler::registers() {
+ // receiver, name, scratch1, scratch2, scratch3, scratch4.
+ static Register registers[] = { r1, r0, r2, r3, r4, r5 };
+ return registers;
+}
+
+
+Register* StoreStubCompiler::registers() {
+ // receiver, name, value, scratch1, scratch2, scratch3.
+ static Register registers[] = { r1, r2, r0, r3, r4, r5 };
+ return registers;
+}
+
+
+Register* KeyedStoreStubCompiler::registers() {
+ // receiver, name, value, scratch1, scratch2, scratch3.
+ static Register registers[] = { r2, r1, r0, r3, r4, r5 };
+ return registers;
+}
+
+
+void KeyedLoadStubCompiler::GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) {
+ __ cmp(name_reg, Operand(name));
+ __ b(ne, miss);
+}
+
+
+void KeyedStoreStubCompiler::GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) {
+ __ cmp(name_reg, Operand(name));
+ __ b(ne, miss);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm,
+ Handle<JSFunction> getter) {
+ // ----------- S t a t e -------------
+ // -- r0 : receiver
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ if (!getter.is_null()) {
+ // Call the JavaScript getter with the receiver on the stack.
+ __ push(r0);
+ ParameterCount actual(0);
+ ParameterCount expected(getter);
+ __ InvokeFunction(getter, expected, actual,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+ } else {
+ // If we generate a global code snippet for deoptimization only, remember
+ // the place to continue after deoptimization.
+ masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ }
+ __ Ret();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> LoadStubCompiler::CompileLoadGlobal(
+ Handle<JSObject> object,
+ Handle<GlobalObject> global,
+ Handle<PropertyCell> cell,
+ Handle<Name> name,
+ bool is_dont_delete) {
+ Label success, miss;
+
+ __ CheckMap(
+ receiver(), scratch1(), Handle<Map>(object->map()), &miss, DO_SMI_CHECK);
+ HandlerFrontendHeader(
+ object, receiver(), Handle<JSObject>::cast(global), name, &miss);
+
+ // Get the value from the cell.
+ __ mov(r3, Operand(cell));
+ __ ldr(r4, FieldMemOperand(r3, Cell::kValueOffset));
+
+ // Check for deleted property if property can actually be deleted.
+ if (!is_dont_delete) {
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(r4, ip);
+ __ b(eq, &miss);
+ }
+
+ HandlerFrontendFooter(name, &success, &miss);
+ __ bind(&success);
+
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->named_load_global_stub(), 1, r1, r3);
+ __ mov(r0, r4);
+ __ Ret();
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, name);
+}
+
+
+Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC(
+ MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ Handle<Name> name,
+ Code::StubType type,
+ IcCheckType check) {
+ Label miss;
+
+ if (check == PROPERTY) {
+ GenerateNameCheck(name, this->name(), &miss);
+ }
+
+ __ JumpIfSmi(receiver(), &miss);
+ Register map_reg = scratch1();
+
+ int receiver_count = receiver_maps->length();
+ int number_of_handled_maps = 0;
+ __ ldr(map_reg, FieldMemOperand(receiver(), HeapObject::kMapOffset));
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map = receiver_maps->at(current);
+ if (!map->is_deprecated()) {
+ number_of_handled_maps++;
+ __ mov(ip, Operand(receiver_maps->at(current)));
+ __ cmp(map_reg, ip);
+ __ Jump(handlers->at(current), RelocInfo::CODE_TARGET, eq);
+ }
+ }
+ ASSERT(number_of_handled_maps != 0);
+
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ InlineCacheState state =
+ number_of_handled_maps > 1 ? POLYMORPHIC : MONOMORPHIC;
+ return GetICCode(kind(), type, name, state);
+}
+
+
+Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic(
+ MapHandleList* receiver_maps,
+ CodeHandleList* handler_stubs,
+ MapHandleList* transitioned_maps) {
+ Label miss;
+ __ JumpIfSmi(receiver(), &miss);
+
+ int receiver_count = receiver_maps->length();
+ __ ldr(scratch1(), FieldMemOperand(receiver(), HeapObject::kMapOffset));
+ for (int i = 0; i < receiver_count; ++i) {
+ __ mov(ip, Operand(receiver_maps->at(i)));
+ __ cmp(scratch1(), ip);
+ 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(transition_map(), Operand(transitioned_maps->at(i)));
+ __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, al);
+ __ bind(&next_map);
+ }
+ }
+
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(
+ kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void KeyedLoadStubCompiler::GenerateLoadDictionaryElement(
+ MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ Label slow, miss_force_generic;
+
+ Register key = r0;
+ Register receiver = r1;
+
+ __ UntagAndJumpIfNotSmi(r2, key, &miss_force_generic);
+ __ ldr(r4, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ LoadFromNumberDictionary(&slow, r4, key, r0, r2, r3, r5);
+ __ Ret();
+
+ __ bind(&slow);
+ __ IncrementCounter(
+ masm->isolate()->counters()->keyed_load_external_array_slow(),
+ 1, r2, r3);
+
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow);
+
+ // Miss case, call the runtime.
+ __ bind(&miss_force_generic);
+
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedLoadIC_MissForceGeneric);
+}
+
+
+static void GenerateSmiKeyCheck(MacroAssembler* masm,
+ Register key,
+ Register scratch0,
+ DwVfpRegister double_scratch0,
+ LowDwVfpRegister double_scratch1,
+ Label* fail) {
+ Label key_ok;
+ // Check for smi or a smi inside a heap number. We convert the heap
+ // number and check if the conversion is exact and fits into the smi
+ // range.
+ __ JumpIfSmi(key, &key_ok);
+ __ CheckMap(key,
+ scratch0,
+ Heap::kHeapNumberMapRootIndex,
+ fail,
+ DONT_DO_SMI_CHECK);
+ __ sub(ip, key, Operand(kHeapObjectTag));
+ __ vldr(double_scratch0, ip, HeapNumber::kValueOffset);
+ __ TryDoubleToInt32Exact(scratch0, double_scratch0, double_scratch1);
+ __ b(ne, fail);
+ __ TrySmiTag(key, scratch0, fail);
+ __ bind(&key_ok);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreExternalArray(
+ MacroAssembler* masm,
+ ElementsKind elements_kind) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+ Label slow, check_heap_number, miss_force_generic;
+
+ // Register usage.
+ Register value = r0;
+ Register key = r1;
+ Register receiver = r2;
+ // r3 mostly holds the elements array or the destination external array.
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, key, r4, d1, d2, &miss_force_generic);
+
+ __ ldr(r3, FieldMemOperand(receiver, JSObject::kElementsOffset));
+
+ // Check that the index is in range
+ __ ldr(ip, FieldMemOperand(r3, ExternalArray::kLengthOffset));
+ __ cmp(key, ip);
+ // Unsigned comparison catches both negative and too-large values.
+ __ b(hs, &miss_force_generic);
+
+ // Handle both smis and HeapNumbers in the fast path. Go to the
+ // runtime for all other kinds of values.
+ // r3: external array.
+ if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) {
+ // Double to pixel conversion is only implemented in the runtime for now.
+ __ UntagAndJumpIfNotSmi(r5, value, &slow);
+ } else {
+ __ UntagAndJumpIfNotSmi(r5, value, &check_heap_number);
+ }
+ __ ldr(r3, FieldMemOperand(r3, ExternalArray::kExternalPointerOffset));
+
+ // r3: base pointer of external storage.
+ // r5: value (integer).
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ // Clamp the value to [0..255].
+ __ Usat(r5, 8, Operand(r5));
+ __ strb(r5, MemOperand(r3, key, LSR, 1));
+ break;
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ strb(r5, MemOperand(r3, key, LSR, 1));
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ strh(r5, MemOperand(r3, key, LSL, 0));
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ str(r5, MemOperand(r3, key, LSL, 1));
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ // Perform int-to-float conversion and store to memory.
+ __ SmiUntag(r4, key);
+ StoreIntAsFloat(masm, r3, r4, r5, r7);
+ break;
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ __ vmov(s2, r5);
+ __ vcvt_f64_s32(d0, s2);
+ __ add(r3, r3, Operand(key, LSL, 2));
+ // r3: effective address of the double element
+ __ vstr(d0, r3, 0);
+ break;
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+
+ // Entry registers are intact, r0 holds the value which is the return value.
+ __ Ret();
+
+ if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) {
+ // r3: external array.
+ __ bind(&check_heap_number);
+ __ CompareObjectType(value, r5, r6, HEAP_NUMBER_TYPE);
+ __ b(ne, &slow);
+
+ __ ldr(r3, FieldMemOperand(r3, ExternalArray::kExternalPointerOffset));
+
+ // r3: base pointer of external storage.
+
+ // The WebGL specification leaves the behavior of storing NaN and
+ // +/-Infinity into integer arrays basically undefined. For more
+ // reproducible behavior, convert these to zero.
+
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ // vldr requires offset to be a multiple of 4 so we can not
+ // include -kHeapObjectTag into it.
+ __ sub(r5, r0, Operand(kHeapObjectTag));
+ __ vldr(d0, r5, HeapNumber::kValueOffset);
+ __ add(r5, r3, Operand(key, LSL, 1));
+ __ vcvt_f32_f64(s0, d0);
+ __ vstr(s0, r5, 0);
+ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ __ sub(r5, r0, Operand(kHeapObjectTag));
+ __ vldr(d0, r5, HeapNumber::kValueOffset);
+ __ add(r5, r3, Operand(key, LSL, 2));
+ __ vstr(d0, r5, 0);
+ } else {
+ // Hoisted load. vldr requires offset to be a multiple of 4 so we can
+ // not include -kHeapObjectTag into it.
+ __ sub(r5, value, Operand(kHeapObjectTag));
+ __ vldr(d0, r5, HeapNumber::kValueOffset);
+ __ ECMAToInt32(r5, d0, r6, r7, r9, d1);
+
+ switch (elements_kind) {
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ strb(r5, MemOperand(r3, key, LSR, 1));
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ strh(r5, MemOperand(r3, key, LSL, 0));
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ str(r5, MemOperand(r3, key, LSL, 1));
+ break;
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ // Entry registers are intact, r0 holds the value which is the return
+ // value.
+ __ Ret();
+ }
+
+ // Slow case, key and receiver still in r0 and r1.
+ __ bind(&slow);
+ __ IncrementCounter(
+ masm->isolate()->counters()->keyed_load_external_array_slow(),
+ 1, r2, r3);
+
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+
+ // Miss case, call the runtime.
+ __ bind(&miss_force_generic);
+
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastElement(
+ MacroAssembler* masm,
+ bool is_js_array,
+ ElementsKind elements_kind,
+ KeyedAccessStoreMode store_mode) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -- r3 : scratch
+ // -- r4 : scratch (elements)
+ // -----------------------------------
+ Label miss_force_generic, transition_elements_kind, grow, slow;
+ Label finish_store, check_capacity;
+
+ Register value_reg = r0;
+ Register key_reg = r1;
+ Register receiver_reg = r2;
+ Register scratch = r4;
+ Register elements_reg = r3;
+ Register length_reg = r5;
+ Register scratch2 = r6;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, key_reg, r4, d1, d2, &miss_force_generic);
+
+ if (IsFastSmiElementsKind(elements_kind)) {
+ __ JumpIfNotSmi(value_reg, &transition_elements_kind);
+ }
+
+ // Check that the key is within bounds.
+ __ ldr(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ if (is_js_array) {
+ __ ldr(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ } else {
+ __ ldr(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ }
+ // Compare smis.
+ __ cmp(key_reg, scratch);
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ __ b(hs, &grow);
+ } else {
+ __ b(hs, &miss_force_generic);
+ }
+
+ // Make sure elements is a fast element array, not 'cow'.
+ __ CheckMap(elements_reg,
+ scratch,
+ Heap::kFixedArrayMapRootIndex,
+ &miss_force_generic,
+ DONT_DO_SMI_CHECK);
+
+ __ bind(&finish_store);
+ if (IsFastSmiElementsKind(elements_kind)) {
+ __ add(scratch,
+ elements_reg,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ add(scratch, scratch, Operand::PointerOffsetFromSmiKey(key_reg));
+ __ str(value_reg, MemOperand(scratch));
+ } else {
+ ASSERT(IsFastObjectElementsKind(elements_kind));
+ __ add(scratch,
+ elements_reg,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ add(scratch, scratch, Operand::PointerOffsetFromSmiKey(key_reg));
+ __ 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();
+
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+
+ __ bind(&transition_elements_kind);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Miss);
+
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ // Grow the array by a single element if possible.
+ __ bind(&grow);
+
+ // Make sure the array is only growing by a single element, anything else
+ // must be handled by the runtime. Flags already set by previous compare.
+ __ b(ne, &miss_force_generic);
+
+ // Check for the empty array, and preallocate a small backing store if
+ // possible.
+ __ ldr(length_reg,
+ FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ ldr(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ CompareRoot(elements_reg, Heap::kEmptyFixedArrayRootIndex);
+ __ b(ne, &check_capacity);
+
+ int size = FixedArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ Allocate(size, elements_reg, scratch, scratch2, &slow, TAG_OBJECT);
+
+ __ LoadRoot(scratch, Heap::kFixedArrayMapRootIndex);
+ __ str(scratch, FieldMemOperand(elements_reg, JSObject::kMapOffset));
+ __ mov(scratch, Operand(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
+ __ str(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
+ for (int i = 1; i < JSArray::kPreallocatedArrayElements; ++i) {
+ __ str(scratch, FieldMemOperand(elements_reg, FixedArray::SizeFor(i)));
+ }
+
+ // Store the element at index zero.
+ __ str(value_reg, FieldMemOperand(elements_reg, FixedArray::SizeFor(0)));
+
+ // Install the new backing store in the JSArray.
+ __ str(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ RecordWriteField(receiver_reg, JSObject::kElementsOffset, elements_reg,
+ scratch, kLRHasNotBeenSaved, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+
+ // Increment the length of the array.
+ __ mov(length_reg, Operand(Smi::FromInt(1)));
+ __ str(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ Ret();
+
+ __ bind(&check_capacity);
+ // Check for cow elements, in general they are not handled by this stub
+ __ CheckMap(elements_reg,
+ scratch,
+ Heap::kFixedCOWArrayMapRootIndex,
+ &miss_force_generic,
+ DONT_DO_SMI_CHECK);
+
+ __ ldr(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ __ cmp(length_reg, scratch);
+ __ b(hs, &slow);
+
+ // Grow the array and finish the store.
+ __ add(length_reg, length_reg, Operand(Smi::FromInt(1)));
+ __ str(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ jmp(&finish_store);
+
+ __ bind(&slow);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+ }
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
+ MacroAssembler* masm,
+ bool is_js_array,
+ KeyedAccessStoreMode store_mode) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -- r3 : scratch (elements backing store)
+ // -- r4 : scratch
+ // -- r5 : scratch
+ // -----------------------------------
+ Label miss_force_generic, transition_elements_kind, grow, slow;
+ Label finish_store, check_capacity;
+
+ Register value_reg = r0;
+ Register key_reg = r1;
+ Register receiver_reg = r2;
+ Register elements_reg = r3;
+ Register scratch1 = r4;
+ Register scratch2 = r5;
+ Register length_reg = r7;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, key_reg, r4, d1, d2, &miss_force_generic);
+
+ __ ldr(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+
+ // Check that the key is within bounds.
+ if (is_js_array) {
+ __ ldr(scratch1, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ } else {
+ __ ldr(scratch1,
+ FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ }
+ // Compare smis, unsigned compare catches both negative and out-of-bound
+ // indexes.
+ __ cmp(key_reg, scratch1);
+ if (IsGrowStoreMode(store_mode)) {
+ __ b(hs, &grow);
+ } else {
+ __ b(hs, &miss_force_generic);
+ }
+
+ __ bind(&finish_store);
+ __ StoreNumberToDoubleElements(value_reg, key_reg, elements_reg,
+ scratch1, d0, &transition_elements_kind);
+ __ Ret();
+
+ // Handle store cache miss, replacing the ic with the generic stub.
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+
+ __ bind(&transition_elements_kind);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Miss);
+
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ // Grow the array by a single element if possible.
+ __ bind(&grow);
+
+ // Make sure the array is only growing by a single element, anything else
+ // must be handled by the runtime. Flags already set by previous compare.
+ __ b(ne, &miss_force_generic);
+
+ // Transition on values that can't be stored in a FixedDoubleArray.
+ Label value_is_smi;
+ __ JumpIfSmi(value_reg, &value_is_smi);
+ __ ldr(scratch1, FieldMemOperand(value_reg, HeapObject::kMapOffset));
+ __ CompareRoot(scratch1, Heap::kHeapNumberMapRootIndex);
+ __ b(ne, &transition_elements_kind);
+ __ bind(&value_is_smi);
+
+ // Check for the empty array, and preallocate a small backing store if
+ // possible.
+ __ ldr(length_reg,
+ FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ ldr(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ CompareRoot(elements_reg, Heap::kEmptyFixedArrayRootIndex);
+ __ b(ne, &check_capacity);
+
+ int size = FixedDoubleArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ Allocate(size, elements_reg, scratch1, scratch2, &slow, TAG_OBJECT);
+
+ // Initialize the new FixedDoubleArray.
+ __ LoadRoot(scratch1, Heap::kFixedDoubleArrayMapRootIndex);
+ __ str(scratch1, FieldMemOperand(elements_reg, JSObject::kMapOffset));
+ __ mov(scratch1,
+ Operand(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
+ __ str(scratch1,
+ FieldMemOperand(elements_reg, FixedDoubleArray::kLengthOffset));
+
+ __ mov(scratch1, elements_reg);
+ __ StoreNumberToDoubleElements(value_reg, key_reg, scratch1,
+ scratch2, d0, &transition_elements_kind);
+
+ __ mov(scratch1, Operand(kHoleNanLower32));
+ __ mov(scratch2, Operand(kHoleNanUpper32));
+ for (int i = 1; i < JSArray::kPreallocatedArrayElements; i++) {
+ int offset = FixedDoubleArray::OffsetOfElementAt(i);
+ __ str(scratch1, FieldMemOperand(elements_reg, offset));
+ __ str(scratch2, FieldMemOperand(elements_reg, offset + kPointerSize));
+ }
+
+ // Install the new backing store in the JSArray.
+ __ str(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ RecordWriteField(receiver_reg, JSObject::kElementsOffset, elements_reg,
+ scratch1, kLRHasNotBeenSaved, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+
+ // Increment the length of the array.
+ __ mov(length_reg, Operand(Smi::FromInt(1)));
+ __ str(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ ldr(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ Ret();
+
+ __ bind(&check_capacity);
+ // Make sure that the backing store can hold additional elements.
+ __ ldr(scratch1,
+ FieldMemOperand(elements_reg, FixedDoubleArray::kLengthOffset));
+ __ cmp(length_reg, scratch1);
+ __ b(hs, &slow);
+
+ // Grow the array and finish the store.
+ __ add(length_reg, length_reg, Operand(Smi::FromInt(1)));
+ __ str(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ jmp(&finish_store);
+
+ __ bind(&slow);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+ }
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_ARM
diff --git a/chromium/v8/src/array-iterator.js b/chromium/v8/src/array-iterator.js
new file mode 100644
index 00000000000..defd7342ab2
--- /dev/null
+++ b/chromium/v8/src/array-iterator.js
@@ -0,0 +1,126 @@
+// Copyright 2013 the V8 project authors. 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.
+
+'use strict';
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js:
+// var $Array = global.Array;
+
+var ARRAY_ITERATOR_KIND_KEYS = 1;
+var ARRAY_ITERATOR_KIND_VALUES = 2;
+var ARRAY_ITERATOR_KIND_ENTRIES = 3;
+// The spec draft also has "sparse" but it is never used.
+
+var iteratorObjectSymbol = %CreateSymbol(void 0);
+var arrayIteratorNextIndexSymbol = %CreateSymbol(void 0);
+var arrayIterationKindSymbol = %CreateSymbol(void 0);
+
+function ArrayIterator() {}
+
+// 15.4.5.1 CreateArrayIterator Abstract Operation
+function CreateArrayIterator(array, kind) {
+ var object = ToObject(array);
+ var iterator = new ArrayIterator;
+ iterator[iteratorObjectSymbol] = object;
+ iterator[arrayIteratorNextIndexSymbol] = 0;
+ iterator[arrayIterationKindSymbol] = kind;
+ return iterator;
+}
+
+// 15.19.4.3.4 CreateItrResultObject
+function CreateIteratorResultObject(value, done) {
+ return {value: value, done: done};
+}
+
+// 15.4.5.2.2 ArrayIterator.prototype.next( )
+function ArrayIteratorNext() {
+ var iterator = ToObject(this);
+ var array = iterator[iteratorObjectSymbol];
+ if (!array) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['Array Iterator.prototype.next']);
+ }
+
+ var index = iterator[arrayIteratorNextIndexSymbol];
+ var itemKind = iterator[arrayIterationKindSymbol];
+ var length = TO_UINT32(array.length);
+
+ // "sparse" is never used.
+
+ if (index >= length) {
+ iterator[arrayIteratorNextIndexSymbol] = 1 / 0; // Infinity
+ return CreateIteratorResultObject(void 0, true);
+ }
+
+ iterator[arrayIteratorNextIndexSymbol] = index + 1;
+
+ if (itemKind == ARRAY_ITERATOR_KIND_VALUES)
+ return CreateIteratorResultObject(array[index], false);
+
+ if (itemKind == ARRAY_ITERATOR_KIND_ENTRIES)
+ return CreateIteratorResultObject([index, array[index]], false);
+
+ return CreateIteratorResultObject(index, false);
+}
+
+function ArrayEntries() {
+ return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_ENTRIES);
+}
+
+function ArrayValues() {
+ return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_VALUES);
+}
+
+function ArrayKeys() {
+ return CreateArrayIterator(this, ARRAY_ITERATOR_KIND_KEYS);
+}
+
+function SetUpArrayIterator() {
+ %CheckIsBootstrapping();
+
+ %FunctionSetInstanceClassName(ArrayIterator, 'Array Iterator');
+ %FunctionSetReadOnlyPrototype(ArrayIterator);
+
+ InstallFunctions(ArrayIterator.prototype, DONT_ENUM, $Array(
+ 'next', ArrayIteratorNext
+ ));
+}
+
+SetUpArrayIterator();
+
+function ExtendArrayPrototype() {
+ %CheckIsBootstrapping();
+
+ InstallFunctions($Array.prototype, DONT_ENUM, $Array(
+ 'entries', ArrayEntries,
+ 'values', ArrayValues,
+ 'keys', ArrayKeys
+ ));
+}
+
+ExtendArrayPrototype();
diff --git a/chromium/v8/src/array.js b/chromium/v8/src/array.js
new file mode 100644
index 00000000000..5f89ebb7a6b
--- /dev/null
+++ b/chromium/v8/src/array.js
@@ -0,0 +1,1665 @@
+// Copyright 2012 the V8 project authors. 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 declarations have been made
+// in runtime.js:
+// var $Array = global.Array;
+
+// -------------------------------------------------------------------
+
+// Global list of arrays visited during toString, toLocaleString and
+// join invocations.
+var visited_arrays = new InternalArray();
+
+
+// Gets a sorted array of array keys. Useful for operations on sparse
+// arrays. Dupes have not been removed.
+function GetSortedArrayKeys(array, indices) {
+ var keys = new InternalArray();
+ if (IS_NUMBER(indices)) {
+ // It's an interval
+ var limit = indices;
+ for (var i = 0; i < limit; ++i) {
+ var e = array[i];
+ if (!IS_UNDEFINED(e) || i in array) {
+ keys.push(i);
+ }
+ }
+ } else {
+ var length = indices.length;
+ for (var k = 0; k < length; ++k) {
+ var key = indices[k];
+ if (!IS_UNDEFINED(key)) {
+ var e = array[key];
+ if (!IS_UNDEFINED(e) || key in array) {
+ keys.push(key);
+ }
+ }
+ }
+ %_CallFunction(keys, function(a, b) { return a - b; }, ArraySort);
+ }
+ return keys;
+}
+
+
+function SparseJoinWithSeparator(array, len, convert, separator) {
+ var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+ var totalLength = 0;
+ var elements = new InternalArray(keys.length * 2);
+ var previousKey = -1;
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ if (key != previousKey) { // keys may contain duplicates.
+ var e = array[key];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[i * 2] = key;
+ elements[i * 2 + 1] = e;
+ previousKey = key;
+ }
+ }
+ return %SparseJoinWithSeparator(elements, len, separator);
+}
+
+
+// Optimized for sparse arrays if separator is ''.
+function SparseJoin(array, len, convert) {
+ var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+ var last_key = -1;
+ var keys_length = keys.length;
+
+ var elements = new InternalArray(keys_length);
+ var elements_length = 0;
+
+ for (var i = 0; i < keys_length; i++) {
+ var key = keys[i];
+ if (key != last_key) {
+ var e = array[key];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[elements_length++] = e;
+ last_key = key;
+ }
+ }
+ return %StringBuilderConcat(elements, elements_length, '');
+}
+
+
+function UseSparseVariant(object, length, is_array) {
+ return is_array &&
+ length > 1000 &&
+ (!%_IsSmi(length) ||
+ %EstimateNumberOfElements(object) < (length >> 2));
+}
+
+
+function Join(array, length, separator, convert) {
+ if (length == 0) return '';
+
+ var is_array = IS_ARRAY(array);
+
+ if (is_array) {
+ // If the array is cyclic, return the empty string for already
+ // visited arrays.
+ if (!%PushIfAbsent(visited_arrays, array)) return '';
+ }
+
+ // Attempt to convert the elements.
+ try {
+ if (UseSparseVariant(array, length, is_array)) {
+ if (separator.length == 0) {
+ return SparseJoin(array, length, convert);
+ } else {
+ return SparseJoinWithSeparator(array, length, convert, separator);
+ }
+ }
+
+ // Fast case for one-element arrays.
+ if (length == 1) {
+ var e = array[0];
+ if (IS_STRING(e)) return e;
+ return convert(e);
+ }
+
+ // Construct an array for the elements.
+ var elements = new InternalArray(length);
+
+ // We pull the empty separator check outside the loop for speed!
+ if (separator.length == 0) {
+ var elements_length = 0;
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[elements_length++] = e;
+ }
+ elements.length = elements_length;
+ var result = %_FastAsciiArrayJoin(elements, '');
+ if (!IS_UNDEFINED(result)) return result;
+ return %StringBuilderConcat(elements, elements_length, '');
+ }
+ // Non-empty separator case.
+ // If the first element is a number then use the heuristic that the
+ // remaining elements are also likely to be numbers.
+ if (!IS_NUMBER(array[0])) {
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[i] = e;
+ }
+ } else {
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (IS_NUMBER(e)) {
+ e = %_NumberToString(e);
+ } else if (!IS_STRING(e)) {
+ e = convert(e);
+ }
+ elements[i] = e;
+ }
+ }
+ var result = %_FastAsciiArrayJoin(elements, separator);
+ if (!IS_UNDEFINED(result)) return result;
+
+ return %StringBuilderJoin(elements, length, separator);
+ } finally {
+ // Make sure to remove the last element of the visited array no
+ // matter what happens.
+ if (is_array) visited_arrays.length = visited_arrays.length - 1;
+ }
+}
+
+
+function ConvertToString(x) {
+ // Assumes x is a non-string.
+ if (IS_NUMBER(x)) return %_NumberToString(x);
+ if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
+ return (IS_NULL_OR_UNDEFINED(x)) ? '' : %ToString(%DefaultString(x));
+}
+
+
+function ConvertToLocaleString(e) {
+ if (IS_NULL_OR_UNDEFINED(e)) {
+ return '';
+ } else {
+ // 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);
+ return %ToString(e_obj.toLocaleString());
+ }
+}
+
+
+// This function implements the optimized splice implementation that can use
+// special array operations to handle sparse arrays in a sensible fashion.
+function SmartSlice(array, start_i, del_count, len, deleted_elements) {
+ // Move deleted elements to a new array (the return value from splice).
+ var indices = %GetArrayKeys(array, start_i + del_count);
+ if (IS_NUMBER(indices)) {
+ var limit = indices;
+ for (var i = start_i; i < limit; ++i) {
+ var current = array[i];
+ if (!IS_UNDEFINED(current) || i in array) {
+ deleted_elements[i - start_i] = current;
+ }
+ }
+ } else {
+ var length = indices.length;
+ for (var k = 0; k < length; ++k) {
+ var key = indices[k];
+ if (!IS_UNDEFINED(key)) {
+ if (key >= start_i) {
+ var current = array[key];
+ if (!IS_UNDEFINED(current) || key in array) {
+ deleted_elements[key - start_i] = current;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// This function implements the optimized splice implementation that can use
+// special array operations to handle sparse arrays in a sensible fashion.
+function SmartMove(array, start_i, del_count, len, num_additional_args) {
+ // Move data to new array.
+ var new_array = new InternalArray(len - del_count + num_additional_args);
+ var indices = %GetArrayKeys(array, len);
+ if (IS_NUMBER(indices)) {
+ var limit = indices;
+ for (var i = 0; i < start_i && i < limit; ++i) {
+ var current = array[i];
+ if (!IS_UNDEFINED(current) || i in array) {
+ new_array[i] = current;
+ }
+ }
+ for (var i = start_i + del_count; i < limit; ++i) {
+ var current = array[i];
+ if (!IS_UNDEFINED(current) || i in array) {
+ new_array[i - del_count + num_additional_args] = current;
+ }
+ }
+ } else {
+ var length = indices.length;
+ for (var k = 0; k < length; ++k) {
+ var key = indices[k];
+ if (!IS_UNDEFINED(key)) {
+ if (key < start_i) {
+ var current = array[key];
+ if (!IS_UNDEFINED(current) || key in array) {
+ new_array[key] = current;
+ }
+ } else if (key >= start_i + del_count) {
+ var current = array[key];
+ if (!IS_UNDEFINED(current) || key in array) {
+ new_array[key - del_count + num_additional_args] = current;
+ }
+ }
+ }
+ }
+ }
+ // Move contents of new_array into this array
+ %MoveArrayContents(new_array, array);
+}
+
+
+// This is part of the old simple-minded splice. We are using it either
+// because the receiver is not an array (so we have no choice) or because we
+// know we are not deleting or moving a lot of elements.
+function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
+ for (var i = 0; i < del_count; i++) {
+ var index = start_i + i;
+ // The spec could also be interpreted such that %HasLocalProperty
+ // would be the appropriate test. We follow KJS in consulting the
+ // prototype.
+ var current = array[index];
+ if (!IS_UNDEFINED(current) || index in array) {
+ deleted_elements[i] = current;
+ }
+ }
+}
+
+
+function SimpleMove(array, start_i, del_count, len, num_additional_args) {
+ if (num_additional_args !== del_count) {
+ // Move the existing elements after the elements to be deleted
+ // to the right position in the resulting array.
+ if (num_additional_args > del_count) {
+ for (var i = len - del_count; i > start_i; i--) {
+ var from_index = i + del_count - 1;
+ var to_index = i + num_additional_args - 1;
+ // The spec could also be interpreted such that
+ // %HasLocalProperty would be the appropriate test. We follow
+ // KJS in consulting the prototype.
+ var current = array[from_index];
+ if (!IS_UNDEFINED(current) || from_index in array) {
+ array[to_index] = current;
+ } else {
+ delete array[to_index];
+ }
+ }
+ } else {
+ for (var i = start_i; i < len - del_count; i++) {
+ var from_index = i + del_count;
+ var to_index = i + num_additional_args;
+ // The spec could also be interpreted such that
+ // %HasLocalProperty would be the appropriate test. We follow
+ // KJS in consulting the prototype.
+ var current = array[from_index];
+ if (!IS_UNDEFINED(current) || from_index in array) {
+ array[to_index] = current;
+ } else {
+ delete array[to_index];
+ }
+ }
+ for (var i = len; i > len - del_count + num_additional_args; i--) {
+ delete array[i - 1];
+ }
+ }
+ }
+}
+
+
+// -------------------------------------------------------------------
+
+
+function ArrayToString() {
+ 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;
+ }
+ if (!IS_SPEC_FUNCTION(func)) {
+ return %_CallFunction(array, ObjectToString);
+ }
+ return %_CallFunction(array, func);
+}
+
+
+function ArrayToLocaleString() {
+ var array = ToObject(this);
+ var arrayLen = array.length;
+ var len = TO_UINT32(arrayLen);
+ if (len === 0) return "";
+ return Join(array, len, ',', ConvertToLocaleString);
+}
+
+
+function ArrayJoin(separator) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.join"]);
+ }
+
+ var length = TO_UINT32(this.length);
+ if (IS_UNDEFINED(separator)) {
+ separator = ',';
+ } else if (!IS_STRING(separator)) {
+ separator = NonStringToString(separator);
+ }
+
+ var result = %_FastAsciiArrayJoin(this, separator);
+ if (!IS_UNDEFINED(result)) return result;
+
+ return Join(this, length, separator, ConvertToString);
+}
+
+
+function ObservedArrayPop(n) {
+ n--;
+ var value = this[n];
+
+ EnqueueSpliceRecord(this, n, [value], 0);
+
+ try {
+ BeginPerformSplice(this);
+ delete this[n];
+ this.length = n;
+ } finally {
+ EndPerformSplice(this);
+ }
+
+ return value;
+}
+
+// Removes the last element from the array and returns it. See
+// ECMA-262, section 15.4.4.6.
+function ArrayPop() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.pop"]);
+ }
+
+ var n = TO_UINT32(this.length);
+ if (n == 0) {
+ this.length = n;
+ return;
+ }
+
+ if (%IsObserved(this))
+ return ObservedArrayPop.call(this, n);
+
+ n--;
+ var value = this[n];
+ delete this[n];
+ this.length = n;
+ return value;
+}
+
+
+function ObservedArrayPush() {
+ var n = TO_UINT32(this.length);
+ var m = %_ArgumentsLength();
+
+ EnqueueSpliceRecord(this, n, [], m);
+
+ try {
+ BeginPerformSplice(this);
+ for (var i = 0; i < m; i++) {
+ this[i+n] = %_Arguments(i);
+ }
+ this.length = n + m;
+ } finally {
+ EndPerformSplice(this);
+ }
+
+ return this.length;
+}
+
+// Appends the arguments to the end of the array and returns the new
+// length of the array. See ECMA-262, section 15.4.4.7.
+function ArrayPush() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.push"]);
+ }
+
+ if (%IsObserved(this))
+ return ObservedArrayPush.apply(this, arguments);
+
+ var n = TO_UINT32(this.length);
+ var m = %_ArgumentsLength();
+ for (var i = 0; i < m; i++) {
+ this[i+n] = %_Arguments(i);
+ }
+ this.length = n + m;
+ return this.length;
+}
+
+
+// Returns an array containing the array elements of the object followed
+// by the array elements of each argument in order. See ECMA-262,
+// section 15.4.4.7.
+function ArrayConcat(arg1) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.concat"]);
+ }
+
+ var array = ToObject(this);
+ var arg_count = %_ArgumentsLength();
+ var arrays = new InternalArray(1 + arg_count);
+ arrays[0] = array;
+ for (var i = 0; i < arg_count; i++) {
+ arrays[i + 1] = %_Arguments(i);
+ }
+
+ return %ArrayConcat(arrays);
+}
+
+
+// For implementing reverse() on large, sparse arrays.
+function SparseReverse(array, len) {
+ var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+ var high_counter = keys.length - 1;
+ var low_counter = 0;
+ while (low_counter <= high_counter) {
+ var i = keys[low_counter];
+ var j = keys[high_counter];
+
+ var j_complement = len - j - 1;
+ var low, high;
+
+ if (j_complement <= i) {
+ high = j;
+ while (keys[--high_counter] == j) { }
+ low = j_complement;
+ }
+ if (j_complement >= i) {
+ low = i;
+ while (keys[++low_counter] == i) { }
+ high = len - i - 1;
+ }
+
+ var current_i = array[low];
+ if (!IS_UNDEFINED(current_i) || low in array) {
+ var current_j = array[high];
+ if (!IS_UNDEFINED(current_j) || high in array) {
+ array[low] = current_j;
+ array[high] = current_i;
+ } else {
+ array[high] = current_i;
+ delete array[low];
+ }
+ } else {
+ var current_j = array[high];
+ if (!IS_UNDEFINED(current_j) || high in array) {
+ array[low] = current_j;
+ delete array[high];
+ }
+ }
+ }
+}
+
+
+function ArrayReverse() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.reverse"]);
+ }
+
+ var j = TO_UINT32(this.length) - 1;
+
+ if (UseSparseVariant(this, j, IS_ARRAY(this))) {
+ SparseReverse(this, j+1);
+ return this;
+ }
+
+ for (var i = 0; i < j; i++, j--) {
+ var current_i = this[i];
+ if (!IS_UNDEFINED(current_i) || i in this) {
+ var current_j = this[j];
+ if (!IS_UNDEFINED(current_j) || j in this) {
+ this[i] = current_j;
+ this[j] = current_i;
+ } else {
+ this[j] = current_i;
+ delete this[i];
+ }
+ } else {
+ var current_j = this[j];
+ if (!IS_UNDEFINED(current_j) || j in this) {
+ this[i] = current_j;
+ delete this[j];
+ }
+ }
+ }
+ return this;
+}
+
+
+function ObservedArrayShift(len) {
+ var first = this[0];
+
+ EnqueueSpliceRecord(this, 0, [first], 0);
+
+ try {
+ BeginPerformSplice(this);
+ SimpleMove(this, 0, 1, len, 0);
+ this.length = len - 1;
+ } finally {
+ EndPerformSplice(this);
+ }
+
+ return first;
+}
+
+function ArrayShift() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.shift"]);
+ }
+
+ var len = TO_UINT32(this.length);
+
+ if (len === 0) {
+ this.length = 0;
+ return;
+ }
+
+ if (%IsObserved(this))
+ return ObservedArrayShift.call(this, len);
+
+ var first = this[0];
+
+ if (IS_ARRAY(this)) {
+ SmartMove(this, 0, 1, len, 0);
+ } else {
+ SimpleMove(this, 0, 1, len, 0);
+ }
+
+ this.length = len - 1;
+
+ return first;
+}
+
+function ObservedArrayUnshift() {
+ var len = TO_UINT32(this.length);
+ var num_arguments = %_ArgumentsLength();
+
+ EnqueueSpliceRecord(this, 0, [], num_arguments);
+
+ try {
+ BeginPerformSplice(this);
+ SimpleMove(this, 0, 0, len, num_arguments);
+ for (var i = 0; i < num_arguments; i++) {
+ this[i] = %_Arguments(i);
+ }
+ this.length = len + num_arguments;
+ } finally {
+ EndPerformSplice(this);
+ }
+
+ return len + num_arguments;
+}
+
+function ArrayUnshift(arg1) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.unshift"]);
+ }
+
+ if (%IsObserved(this))
+ return ObservedArrayUnshift.apply(this, arguments);
+
+ var len = TO_UINT32(this.length);
+ var num_arguments = %_ArgumentsLength();
+
+ if (IS_ARRAY(this)) {
+ SmartMove(this, 0, 0, len, num_arguments);
+ } else {
+ SimpleMove(this, 0, 0, len, num_arguments);
+ }
+
+ for (var i = 0; i < num_arguments; i++) {
+ this[i] = %_Arguments(i);
+ }
+
+ this.length = len + num_arguments;
+
+ return len + num_arguments;
+}
+
+
+function ArraySlice(start, end) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.slice"]);
+ }
+
+ var len = TO_UINT32(this.length);
+ var start_i = TO_INTEGER(start);
+ var end_i = len;
+
+ if (end !== void 0) end_i = TO_INTEGER(end);
+
+ if (start_i < 0) {
+ start_i += len;
+ if (start_i < 0) start_i = 0;
+ } else {
+ if (start_i > len) start_i = len;
+ }
+
+ if (end_i < 0) {
+ end_i += len;
+ if (end_i < 0) end_i = 0;
+ } else {
+ if (end_i > len) end_i = len;
+ }
+
+ var result = [];
+
+ if (end_i < start_i) return result;
+
+ if (IS_ARRAY(this) &&
+ !%IsObserved(this) &&
+ (end_i > 1000) &&
+ (%EstimateNumberOfElements(this) < end_i)) {
+ SmartSlice(this, start_i, end_i - start_i, len, result);
+ } else {
+ SimpleSlice(this, start_i, end_i - start_i, len, result);
+ }
+
+ result.length = end_i - start_i;
+
+ return result;
+}
+
+
+function ComputeSpliceStartIndex(start_i, len) {
+ if (start_i < 0) {
+ start_i += len;
+ return start_i < 0 ? 0 : start_i;
+ }
+
+ return start_i > len ? len : start_i;
+}
+
+
+function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
+ // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+ // given as a request to delete all the elements from the start.
+ // And it differs from the case of undefined delete count.
+ // This does not follow ECMA-262, but we do the same for
+ // compatibility.
+ var del_count = 0;
+ if (num_arguments == 1)
+ return len - start_i;
+
+ del_count = TO_INTEGER(delete_count);
+ if (del_count < 0)
+ return 0;
+
+ if (del_count > len - start_i)
+ return len - start_i;
+
+ return del_count;
+}
+
+
+function ObservedArraySplice(start, delete_count) {
+ var num_arguments = %_ArgumentsLength();
+ var len = TO_UINT32(this.length);
+ var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
+ var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
+ start_i);
+ var deleted_elements = [];
+ deleted_elements.length = del_count;
+ var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
+
+ try {
+ BeginPerformSplice(this);
+
+ SimpleSlice(this, start_i, del_count, len, deleted_elements);
+ SimpleMove(this, start_i, del_count, len, num_elements_to_add);
+
+ // Insert the arguments into the resulting array in
+ // place of the deleted elements.
+ var i = start_i;
+ var arguments_index = 2;
+ var arguments_length = %_ArgumentsLength();
+ while (arguments_index < arguments_length) {
+ this[i++] = %_Arguments(arguments_index++);
+ }
+ this.length = len - del_count + num_elements_to_add;
+
+ } finally {
+ EndPerformSplice(this);
+ if (deleted_elements.length || num_elements_to_add) {
+ EnqueueSpliceRecord(this,
+ start_i,
+ deleted_elements.slice(),
+ num_elements_to_add);
+ }
+ }
+
+ // Return the deleted elements.
+ return deleted_elements;
+}
+
+
+function ArraySplice(start, delete_count) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.splice"]);
+ }
+
+ if (%IsObserved(this))
+ return ObservedArraySplice.apply(this, arguments);
+
+ var num_arguments = %_ArgumentsLength();
+ var len = TO_UINT32(this.length);
+ var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
+ var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
+ start_i);
+ var deleted_elements = [];
+ deleted_elements.length = del_count;
+ var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
+
+ var use_simple_splice = true;
+ if (IS_ARRAY(this) &&
+ num_elements_to_add !== del_count) {
+ // If we are only deleting/moving a few things near the end of the
+ // array then the simple version is going to be faster, because it
+ // doesn't touch most of the array.
+ var estimated_non_hole_elements = %EstimateNumberOfElements(this);
+ if (len > 20 && (estimated_non_hole_elements >> 2) < (len - start_i)) {
+ use_simple_splice = false;
+ }
+ }
+
+ if (use_simple_splice) {
+ SimpleSlice(this, start_i, del_count, len, deleted_elements);
+ SimpleMove(this, start_i, del_count, len, num_elements_to_add);
+ } else {
+ SmartSlice(this, start_i, del_count, len, deleted_elements);
+ SmartMove(this, start_i, del_count, len, num_elements_to_add);
+ }
+
+ // Insert the arguments into the resulting array in
+ // place of the deleted elements.
+ var i = start_i;
+ var arguments_index = 2;
+ var arguments_length = %_ArgumentsLength();
+ while (arguments_index < arguments_length) {
+ this[i++] = %_Arguments(arguments_index++);
+ }
+ this.length = len - del_count + num_elements_to_add;
+
+ // Return the deleted elements.
+ return deleted_elements;
+}
+
+
+function ArraySort(comparefn) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.sort"]);
+ }
+
+ // In-place QuickSort algorithm.
+ // For short (length <= 22) arrays, insertion sort is used for efficiency.
+
+ if (!IS_SPEC_FUNCTION(comparefn)) {
+ comparefn = function (x, y) {
+ if (x === y) return 0;
+ if (%_IsSmi(x) && %_IsSmi(y)) {
+ return %SmiLexicographicCompare(x, y);
+ }
+ x = ToString(x);
+ y = ToString(y);
+ if (x == y) return 0;
+ else return x < y ? -1 : 1;
+ };
+ }
+ var receiver = %GetDefaultReceiver(comparefn);
+
+ var InsertionSort = function InsertionSort(a, from, to) {
+ for (var i = from + 1; i < to; i++) {
+ var element = a[i];
+ for (var j = i - 1; j >= from; j--) {
+ var tmp = a[j];
+ var order = %_CallFunction(receiver, tmp, element, comparefn);
+ if (order > 0) {
+ a[j + 1] = tmp;
+ } else {
+ break;
+ }
+ }
+ a[j + 1] = element;
+ }
+ };
+
+ var GetThirdIndex = function(a, from, to) {
+ var t_array = [];
+ // Use both 'from' and 'to' to determine the pivot candidates.
+ var increment = 200 + ((to - from) & 15);
+ for (var i = from + 1; i < to - 1; i += increment) {
+ t_array.push([i, a[i]]);
+ }
+ t_array.sort(function(a, b) {
+ return %_CallFunction(receiver, a[1], b[1], comparefn) } );
+ var third_index = t_array[t_array.length >> 1][0];
+ return third_index;
+ }
+
+ var QuickSort = function QuickSort(a, from, to) {
+ var third_index = 0;
+ while (true) {
+ // Insertion sort is faster for short arrays.
+ if (to - from <= 10) {
+ InsertionSort(a, from, to);
+ return;
+ }
+ if (to - from > 1000) {
+ third_index = GetThirdIndex(a, from, to);
+ } else {
+ third_index = from + ((to - from) >> 1);
+ }
+ // Find a pivot as the median of first, last and middle element.
+ var v0 = a[from];
+ var v1 = a[to - 1];
+ var v2 = a[third_index];
+ var c01 = %_CallFunction(receiver, v0, v1, comparefn);
+ if (c01 > 0) {
+ // v1 < v0, so swap them.
+ var tmp = v0;
+ v0 = v1;
+ v1 = tmp;
+ } // v0 <= v1.
+ var c02 = %_CallFunction(receiver, v0, v2, comparefn);
+ if (c02 >= 0) {
+ // v2 <= v0 <= v1.
+ var tmp = v0;
+ v0 = v2;
+ v2 = v1;
+ v1 = tmp;
+ } else {
+ // v0 <= v1 && v0 < v2
+ var c12 = %_CallFunction(receiver, v1, v2, comparefn);
+ if (c12 > 0) {
+ // v0 <= v2 < v1
+ var tmp = v1;
+ v1 = v2;
+ v2 = tmp;
+ }
+ }
+ // v0 <= v1 <= v2
+ a[from] = v0;
+ a[to - 1] = v2;
+ var pivot = v1;
+ var low_end = from + 1; // Upper bound of elements lower than pivot.
+ var high_start = to - 1; // Lower bound of elements greater than pivot.
+ a[third_index] = a[low_end];
+ a[low_end] = pivot;
+
+ // From low_end to i are elements equal to pivot.
+ // From i to high_start are elements that haven't been compared yet.
+ partition: for (var i = low_end + 1; i < high_start; i++) {
+ var element = a[i];
+ var order = %_CallFunction(receiver, element, pivot, comparefn);
+ if (order < 0) {
+ a[i] = a[low_end];
+ a[low_end] = element;
+ low_end++;
+ } else if (order > 0) {
+ do {
+ high_start--;
+ if (high_start == i) break partition;
+ var top_elem = a[high_start];
+ order = %_CallFunction(receiver, top_elem, pivot, comparefn);
+ } while (order > 0);
+ a[i] = a[high_start];
+ a[high_start] = element;
+ if (order < 0) {
+ element = a[i];
+ a[i] = a[low_end];
+ a[low_end] = element;
+ low_end++;
+ }
+ }
+ }
+ if (to - high_start < low_end - from) {
+ QuickSort(a, high_start, to);
+ to = low_end;
+ } else {
+ QuickSort(a, from, low_end);
+ from = high_start;
+ }
+ }
+ };
+
+ // Copy elements in the range 0..length from obj's prototype chain
+ // to obj itself, if obj has holes. Return one more than the maximal index
+ // of a prototype property.
+ var CopyFromPrototype = function CopyFromPrototype(obj, length) {
+ var max = 0;
+ for (var proto = %GetPrototype(obj); proto; proto = %GetPrototype(proto)) {
+ var indices = %GetArrayKeys(proto, length);
+ if (IS_NUMBER(indices)) {
+ // It's an interval.
+ var proto_length = indices;
+ for (var i = 0; i < proto_length; i++) {
+ if (!obj.hasOwnProperty(i) && proto.hasOwnProperty(i)) {
+ obj[i] = proto[i];
+ if (i >= max) { max = i + 1; }
+ }
+ }
+ } else {
+ for (var i = 0; i < indices.length; i++) {
+ var index = indices[i];
+ if (!IS_UNDEFINED(index) &&
+ !obj.hasOwnProperty(index) && proto.hasOwnProperty(index)) {
+ obj[index] = proto[index];
+ if (index >= max) { max = index + 1; }
+ }
+ }
+ }
+ }
+ return max;
+ };
+
+ // Set a value of "undefined" on all indices in the range from..to
+ // where a prototype of obj has an element. I.e., shadow all prototype
+ // elements in that range.
+ var ShadowPrototypeElements = function(obj, from, to) {
+ for (var proto = %GetPrototype(obj); proto; proto = %GetPrototype(proto)) {
+ var indices = %GetArrayKeys(proto, to);
+ if (IS_NUMBER(indices)) {
+ // It's an interval.
+ var proto_length = indices;
+ for (var i = from; i < proto_length; i++) {
+ if (proto.hasOwnProperty(i)) {
+ obj[i] = void 0;
+ }
+ }
+ } else {
+ for (var i = 0; i < indices.length; i++) {
+ var index = indices[i];
+ if (!IS_UNDEFINED(index) && from <= index &&
+ proto.hasOwnProperty(index)) {
+ obj[index] = void 0;
+ }
+ }
+ }
+ }
+ };
+
+ var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
+ // Copy defined elements from the end to fill in all holes and undefineds
+ // in the beginning of the array. Write undefineds and holes at the end
+ // after loop is finished.
+ var first_undefined = 0;
+ var last_defined = length - 1;
+ var num_holes = 0;
+ while (first_undefined < last_defined) {
+ // Find first undefined element.
+ while (first_undefined < last_defined &&
+ !IS_UNDEFINED(obj[first_undefined])) {
+ first_undefined++;
+ }
+ // Maintain the invariant num_holes = the number of holes in the original
+ // array with indices <= first_undefined or > last_defined.
+ if (!obj.hasOwnProperty(first_undefined)) {
+ num_holes++;
+ }
+
+ // Find last defined element.
+ while (first_undefined < last_defined &&
+ IS_UNDEFINED(obj[last_defined])) {
+ if (!obj.hasOwnProperty(last_defined)) {
+ num_holes++;
+ }
+ last_defined--;
+ }
+ if (first_undefined < last_defined) {
+ // Fill in hole or undefined.
+ obj[first_undefined] = obj[last_defined];
+ obj[last_defined] = void 0;
+ }
+ }
+ // If there were any undefineds in the entire array, first_undefined
+ // points to one past the last defined element. Make this true if
+ // there were no undefineds, as well, so that first_undefined == number
+ // of defined elements.
+ if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
+ // Fill in the undefineds and the holes. There may be a hole where
+ // an undefined should be and vice versa.
+ var i;
+ for (i = first_undefined; i < length - num_holes; i++) {
+ obj[i] = void 0;
+ }
+ for (i = length - num_holes; i < length; i++) {
+ // For compatability with Webkit, do not expose elements in the prototype.
+ if (i in %GetPrototype(obj)) {
+ obj[i] = void 0;
+ } else {
+ delete obj[i];
+ }
+ }
+
+ // Return the number of defined elements.
+ return first_undefined;
+ };
+
+ var length = TO_UINT32(this.length);
+ if (length < 2) return this;
+
+ var is_array = IS_ARRAY(this);
+ var max_prototype_element;
+ if (!is_array) {
+ // For compatibility with JSC, we also sort elements inherited from
+ // the prototype chain on non-Array objects.
+ // We do this by copying them to this object and sorting only
+ // local elements. This is not very efficient, but sorting with
+ // inherited elements happens very, very rarely, if at all.
+ // The specification allows "implementation dependent" behavior
+ // if an element on the prototype chain has an element that
+ // might interact with sorting.
+ max_prototype_element = CopyFromPrototype(this, length);
+ }
+
+ var num_non_undefined = %IsObserved(this) ?
+ -1 : %RemoveArrayHoles(this, length);
+
+ if (num_non_undefined == -1) {
+ // The array is observed, or there were indexed accessors in the array.
+ // Move array holes and undefineds to the end using a Javascript function
+ // that is safe in the presence of accessors and is observable.
+ num_non_undefined = SafeRemoveArrayHoles(this);
+ }
+
+ QuickSort(this, 0, num_non_undefined);
+
+ if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
+ // For compatibility with JSC, we shadow any elements in the prototype
+ // chain that has become exposed by sort moving a hole to its position.
+ ShadowPrototypeElements(this, num_non_undefined, max_prototype_element);
+ }
+
+ return this;
+}
+
+
+// The following functions cannot be made efficient on sparse arrays while
+// preserving the semantics, since the calls to the receiver function can add
+// or delete elements from the array.
+function ArrayFilter(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["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) && %IsClassicModeFunction(f)) {
+ receiver = ToObject(receiver);
+ }
+
+ var result = new $Array();
+ var accumulator = new InternalArray();
+ var accumulator_length = 0;
+ if (%DebugCallbackSupportsStepping(f)) {
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ // Prepare break slots for debugger step in.
+ %DebugPrepareStepInIfStepping(f);
+ if (%_CallFunction(receiver, element, i, array, f)) {
+ accumulator[accumulator_length++] = element;
+ }
+ }
+ }
+ } else {
+ // This is a duplicate of the previous loop sans debug stepping.
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ if (%_CallFunction(receiver, element, i, array, f)) {
+ accumulator[accumulator_length++] = element;
+ }
+ }
+ }
+ // End of duplicate.
+ }
+ %MoveArrayContents(accumulator, result);
+ return result;
+}
+
+
+function ArrayForEach(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["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) && %IsClassicModeFunction(f)) {
+ receiver = ToObject(receiver);
+ }
+
+ if (%DebugCallbackSupportsStepping(f)) {
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ // Prepare break slots for debugger step in.
+ %DebugPrepareStepInIfStepping(f);
+ %_CallFunction(receiver, element, i, array, f);
+ }
+ }
+ } else {
+ // This is a duplicate of the previous loop sans debug stepping.
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ %_CallFunction(receiver, element, i, array, f);
+ }
+ }
+ // End of duplicate.
+ }
+}
+
+
+// Executes the function once for each element present in the
+// array until it finds one where callback returns true.
+function ArraySome(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["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) && %IsClassicModeFunction(f)) {
+ receiver = ToObject(receiver);
+ }
+
+ if (%DebugCallbackSupportsStepping(f)) {
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ // Prepare break slots for debugger step in.
+ %DebugPrepareStepInIfStepping(f);
+ if (%_CallFunction(receiver, element, i, array, f)) return true;
+ }
+ }
+ } else {
+ // This is a duplicate of the previous loop sans debug stepping.
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ if (%_CallFunction(receiver, element, i, array, f)) return true;
+ }
+ }
+ // End of duplicate.
+ }
+ return false;
+}
+
+
+function ArrayEvery(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["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) && %IsClassicModeFunction(f)) {
+ receiver = ToObject(receiver);
+ }
+
+ if (%DebugCallbackSupportsStepping(f)) {
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ // Prepare break slots for debugger step in.
+ %DebugPrepareStepInIfStepping(f);
+ if (!%_CallFunction(receiver, element, i, array, f)) return false;
+ }
+ }
+ } else {
+ // This is a duplicate of the previous loop sans debug stepping.
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ if (!%_CallFunction(receiver, element, i, array, f)) return false;
+ }
+ }
+ // End of duplicate.
+ }
+ return true;
+}
+
+function ArrayMap(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["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) && %IsClassicModeFunction(f)) {
+ receiver = ToObject(receiver);
+ }
+
+ var result = new $Array();
+ var accumulator = new InternalArray(length);
+ if (%DebugCallbackSupportsStepping(f)) {
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ // Prepare break slots for debugger step in.
+ %DebugPrepareStepInIfStepping(f);
+ accumulator[i] = %_CallFunction(receiver, element, i, array, f);
+ }
+ }
+ } else {
+ // This is a duplicate of the previous loop sans debug stepping.
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ accumulator[i] = %_CallFunction(receiver, element, i, array, f);
+ }
+ }
+ // End of duplicate.
+ }
+ %MoveArrayContents(accumulator, result);
+ return result;
+}
+
+
+function ArrayIndexOf(element, index) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.indexOf"]);
+ }
+
+ var length = TO_UINT32(this.length);
+ if (length == 0) return -1;
+ if (IS_UNDEFINED(index)) {
+ index = 0;
+ } else {
+ index = TO_INTEGER(index);
+ // If index is negative, index from the end of the array.
+ if (index < 0) {
+ index = length + index;
+ // If index is still negative, search the entire array.
+ if (index < 0) index = 0;
+ }
+ }
+ var min = index;
+ var max = length;
+ if (UseSparseVariant(this, length, IS_ARRAY(this))) {
+ var indices = %GetArrayKeys(this, length);
+ if (IS_NUMBER(indices)) {
+ // It's an interval.
+ max = indices; // Capped by length already.
+ // Fall through to loop below.
+ } else {
+ if (indices.length == 0) return -1;
+ // Get all the keys in sorted order.
+ var sortedKeys = GetSortedArrayKeys(this, indices);
+ var n = sortedKeys.length;
+ var i = 0;
+ while (i < n && sortedKeys[i] < index) i++;
+ while (i < n) {
+ var key = sortedKeys[i];
+ if (!IS_UNDEFINED(key) && this[key] === element) return key;
+ i++;
+ }
+ return -1;
+ }
+ }
+ // Lookup through the array.
+ if (!IS_UNDEFINED(element)) {
+ for (var i = min; i < max; i++) {
+ if (this[i] === element) return i;
+ }
+ return -1;
+ }
+ // Lookup through the array.
+ for (var i = min; i < max; i++) {
+ if (IS_UNDEFINED(this[i]) && i in this) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+function ArrayLastIndexOf(element, index) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.lastIndexOf"]);
+ }
+
+ var length = TO_UINT32(this.length);
+ if (length == 0) return -1;
+ if (%_ArgumentsLength() < 2) {
+ index = length - 1;
+ } else {
+ index = TO_INTEGER(index);
+ // If index is negative, index from end of the array.
+ if (index < 0) index += length;
+ // If index is still negative, do not search the array.
+ if (index < 0) return -1;
+ else if (index >= length) index = length - 1;
+ }
+ var min = 0;
+ var max = index;
+ if (UseSparseVariant(this, length, IS_ARRAY(this))) {
+ var indices = %GetArrayKeys(this, index + 1);
+ if (IS_NUMBER(indices)) {
+ // It's an interval.
+ max = indices; // Capped by index already.
+ // Fall through to loop below.
+ } else {
+ if (indices.length == 0) return -1;
+ // Get all the keys in sorted order.
+ var sortedKeys = GetSortedArrayKeys(this, indices);
+ var i = sortedKeys.length - 1;
+ while (i >= 0) {
+ var key = sortedKeys[i];
+ if (!IS_UNDEFINED(key) && this[key] === element) return key;
+ i--;
+ }
+ return -1;
+ }
+ }
+ // Lookup through the array.
+ if (!IS_UNDEFINED(element)) {
+ for (var i = max; i >= min; i--) {
+ if (this[i] === element) return i;
+ }
+ return -1;
+ }
+ for (var i = max; i >= min; i--) {
+ if (IS_UNDEFINED(this[i]) && i in this) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+function ArrayReduce(callback, current) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["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]);
+ }
+
+ var i = 0;
+ find_initial: if (%_ArgumentsLength() < 2) {
+ for (; i < length; i++) {
+ current = array[i];
+ if (!IS_UNDEFINED(current) || i in array) {
+ i++;
+ break find_initial;
+ }
+ }
+ throw MakeTypeError('reduce_no_initial', []);
+ }
+
+ var receiver = %GetDefaultReceiver(callback);
+
+ if (%DebugCallbackSupportsStepping(callback)) {
+ for (; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ // Prepare break slots for debugger step in.
+ %DebugPrepareStepInIfStepping(callback);
+ current =
+ %_CallFunction(receiver, current, element, i, array, callback);
+ }
+ }
+ } else {
+ // This is a duplicate of the previous loop sans debug stepping.
+ for (; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ current =
+ %_CallFunction(receiver, current, element, i, array, callback);
+ }
+ }
+ // End of duplicate.
+ }
+ return current;
+}
+
+function ArrayReduceRight(callback, current) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["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 = length - 1;
+ find_initial: if (%_ArgumentsLength() < 2) {
+ for (; i >= 0; i--) {
+ current = array[i];
+ if (!IS_UNDEFINED(current) || i in array) {
+ i--;
+ break find_initial;
+ }
+ }
+ throw MakeTypeError('reduce_no_initial', []);
+ }
+
+ var receiver = %GetDefaultReceiver(callback);
+
+ if (%DebugCallbackSupportsStepping(callback)) {
+ for (; i >= 0; i--) {
+ if (i in array) {
+ var element = array[i];
+ // Prepare break slots for debugger step in.
+ %DebugPrepareStepInIfStepping(callback);
+ current =
+ %_CallFunction(receiver, current, element, i, array, callback);
+ }
+ }
+ } else {
+ // This is a duplicate of the previous loop sans debug stepping.
+ for (; i >= 0; i--) {
+ if (i in array) {
+ var element = array[i];
+ current =
+ %_CallFunction(receiver, current, element, i, array, callback);
+ }
+ }
+ // End of duplicate.
+ }
+ return current;
+}
+
+// ES5, 15.4.3.2
+function ArrayIsArray(obj) {
+ return IS_ARRAY(obj);
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpArray() {
+ %CheckIsBootstrapping();
+
+ // Set up non-enumerable constructor property on the Array.prototype
+ // object.
+ %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM);
+
+ // Set up non-enumerable functions on the Array object.
+ InstallFunctions($Array, DONT_ENUM, $Array(
+ "isArray", ArrayIsArray
+ ));
+
+ var specialFunctions = %SpecialArrayFunctions({});
+
+ var getFunction = function(name, jsBuiltin, len) {
+ var f = jsBuiltin;
+ if (specialFunctions.hasOwnProperty(name)) {
+ f = specialFunctions[name];
+ }
+ if (!IS_UNDEFINED(len)) {
+ %FunctionSetLength(f, len);
+ }
+ return f;
+ };
+
+ // Set up non-enumerable functions of the Array.prototype object and
+ // set their names.
+ // Manipulate the length of some of the functions to meet
+ // expectations set by ECMA-262 or Mozilla.
+ InstallFunctions($Array.prototype, DONT_ENUM, $Array(
+ "toString", getFunction("toString", ArrayToString),
+ "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
+ "join", getFunction("join", ArrayJoin),
+ "pop", getFunction("pop", ArrayPop),
+ "push", getFunction("push", ArrayPush, 1),
+ "concat", getFunction("concat", ArrayConcat, 1),
+ "reverse", getFunction("reverse", ArrayReverse),
+ "shift", getFunction("shift", ArrayShift),
+ "unshift", getFunction("unshift", ArrayUnshift, 1),
+ "slice", getFunction("slice", ArraySlice, 2),
+ "splice", getFunction("splice", ArraySplice, 2),
+ "sort", getFunction("sort", ArraySort),
+ "filter", getFunction("filter", ArrayFilter, 1),
+ "forEach", getFunction("forEach", ArrayForEach, 1),
+ "some", getFunction("some", ArraySome, 1),
+ "every", getFunction("every", ArrayEvery, 1),
+ "map", getFunction("map", ArrayMap, 1),
+ "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
+ "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
+ "reduce", getFunction("reduce", ArrayReduce, 1),
+ "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1)
+ ));
+
+ %FinishArrayPrototypeSetup($Array.prototype);
+
+ // The internal Array prototype doesn't need to be fancy, since it's never
+ // exposed to user code.
+ // Adding only the functions that are actually used.
+ SetUpLockedPrototype(InternalArray, $Array(), $Array(
+ "concat", getFunction("concat", ArrayConcat),
+ "indexOf", getFunction("indexOf", ArrayIndexOf),
+ "join", getFunction("join", ArrayJoin),
+ "pop", getFunction("pop", ArrayPop),
+ "push", getFunction("push", ArrayPush),
+ "splice", getFunction("splice", ArraySplice)
+ ));
+
+ SetUpLockedPrototype(InternalPackedArray, $Array(), $Array(
+ "join", getFunction("join", ArrayJoin),
+ "pop", getFunction("pop", ArrayPop),
+ "push", getFunction("push", ArrayPush)
+ ));
+}
+
+SetUpArray();
diff --git a/chromium/v8/src/arraybuffer.js b/chromium/v8/src/arraybuffer.js
new file mode 100644
index 00000000000..4a4f5701465
--- /dev/null
+++ b/chromium/v8/src/arraybuffer.js
@@ -0,0 +1,103 @@
+// Copyright 2013 the V8 project authors. 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.
+
+"use strict";
+
+var $ArrayBuffer = global.ArrayBuffer;
+
+// -------------------------------------------------------------------
+
+function ArrayBufferConstructor(length) { // length = 1
+ if (%_IsConstructCall()) {
+ var byteLength = ToPositiveInteger(length, 'invalid_array_buffer_length');
+ %ArrayBufferInitialize(this, byteLength);
+ } else {
+ throw MakeTypeError('constructor_not_function', ["ArrayBuffer"]);
+ }
+}
+
+function ArrayBufferGetByteLength() {
+ if (!IS_ARRAYBUFFER(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['ArrayBuffer.prototype.byteLength', this]);
+ }
+ return %ArrayBufferGetByteLength(this);
+}
+
+// ES6 Draft 15.13.5.5.3
+function ArrayBufferSlice(start, end) {
+ if (!IS_ARRAYBUFFER(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['ArrayBuffer.prototype.slice', this]);
+ }
+
+ var relativeStart = TO_INTEGER(start);
+ var first;
+ if (relativeStart < 0) {
+ first = MathMax(this.byteLength + relativeStart, 0);
+ } else {
+ first = MathMin(relativeStart, this.byteLength);
+ }
+ var relativeEnd = IS_UNDEFINED(end) ? this.byteLength : TO_INTEGER(end);
+ var fin;
+ if (relativeEnd < 0) {
+ fin = MathMax(this.byteLength + relativeEnd, 0);
+ } else {
+ fin = MathMin(relativeEnd, this.byteLength);
+ }
+
+ if (fin < first) {
+ fin = first;
+ }
+ var newLen = fin - first;
+ // TODO(dslomov): implement inheritance
+ var result = new $ArrayBuffer(newLen);
+
+ %ArrayBufferSliceImpl(this, result, first);
+ return result;
+}
+
+function SetUpArrayBuffer() {
+ %CheckIsBootstrapping();
+
+ // Set up the ArrayBuffer constructor function.
+ %SetCode($ArrayBuffer, ArrayBufferConstructor);
+ %FunctionSetPrototype($ArrayBuffer, new $Object());
+
+ // Set up the constructor property on the ArrayBuffer prototype object.
+ %SetProperty($ArrayBuffer.prototype, "constructor", $ArrayBuffer, DONT_ENUM);
+
+ InstallGetter($ArrayBuffer.prototype, "byteLength", ArrayBufferGetByteLength);
+
+ InstallFunctions($ArrayBuffer.prototype, DONT_ENUM, $Array(
+ "slice", ArrayBufferSlice
+ ));
+}
+
+SetUpArrayBuffer();
+
+
diff --git a/chromium/v8/src/assembler.cc b/chromium/v8/src/assembler.cc
new file mode 100644
index 00000000000..ae8a0b58ba8
--- /dev/null
+++ b/chromium/v8/src/assembler.cc
@@ -0,0 +1,1686 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+#include "assembler.h"
+
+#include <cmath>
+#include "api.h"
+#include "builtins.h"
+#include "counters.h"
+#include "cpu.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "execution.h"
+#include "ic.h"
+#include "isolate.h"
+#include "jsregexp.h"
+#include "lazy-instance.h"
+#include "platform.h"
+#include "regexp-macro-assembler.h"
+#include "regexp-stack.h"
+#include "runtime.h"
+#include "serialize.h"
+#include "store-buffer-inl.h"
+#include "stub-cache.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
+#include "ia32/regexp-macro-assembler-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/regexp-macro-assembler-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/regexp-macro-assembler-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/regexp-macro-assembler-mips.h"
+#else // Unknown architecture.
+#error "Unknown architecture."
+#endif // Target architecture.
+#endif // V8_INTERPRETED_REGEXP
+
+namespace v8 {
+namespace internal {
+
+// -----------------------------------------------------------------------------
+// Common double constants.
+
+struct DoubleConstant BASE_EMBEDDED {
+ double min_int;
+ double one_half;
+ double minus_one_half;
+ double minus_zero;
+ double zero;
+ double uint8_max_value;
+ double negative_infinity;
+ double canonical_non_hole_nan;
+ double the_hole_nan;
+};
+
+static DoubleConstant double_constants;
+
+const char* const RelocInfo::kFillerCommentString = "DEOPTIMIZATION PADDING";
+
+static bool math_exp_data_initialized = false;
+static Mutex* math_exp_data_mutex = NULL;
+static double* math_exp_constants_array = NULL;
+static double* math_exp_log_table_array = NULL;
+
+// -----------------------------------------------------------------------------
+// Implementation of AssemblerBase
+
+AssemblerBase::AssemblerBase(Isolate* isolate, void* buffer, int buffer_size)
+ : isolate_(isolate),
+ jit_cookie_(0),
+ enabled_cpu_features_(0),
+ emit_debug_code_(FLAG_debug_code),
+ predictable_code_size_(false) {
+ if (FLAG_mask_constants_with_cookie && isolate != NULL) {
+ jit_cookie_ = V8::RandomPrivate(isolate);
+ }
+
+ if (buffer == NULL) {
+ // Do our own buffer management.
+ if (buffer_size <= kMinimalBufferSize) {
+ buffer_size = kMinimalBufferSize;
+ if (isolate->assembler_spare_buffer() != NULL) {
+ buffer = isolate->assembler_spare_buffer();
+ isolate->set_assembler_spare_buffer(NULL);
+ }
+ }
+ if (buffer == NULL) buffer = NewArray<byte>(buffer_size);
+ own_buffer_ = true;
+ } else {
+ // Use externally provided buffer instead.
+ ASSERT(buffer_size > 0);
+ own_buffer_ = false;
+ }
+ buffer_ = static_cast<byte*>(buffer);
+ buffer_size_ = buffer_size;
+
+ pc_ = buffer_;
+}
+
+
+AssemblerBase::~AssemblerBase() {
+ if (own_buffer_) {
+ if (isolate() != NULL &&
+ isolate()->assembler_spare_buffer() == NULL &&
+ buffer_size_ == kMinimalBufferSize) {
+ isolate()->set_assembler_spare_buffer(buffer_);
+ } else {
+ DeleteArray(buffer_);
+ }
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of PredictableCodeSizeScope
+
+PredictableCodeSizeScope::PredictableCodeSizeScope(AssemblerBase* assembler,
+ int expected_size)
+ : assembler_(assembler),
+ expected_size_(expected_size),
+ start_offset_(assembler->pc_offset()),
+ old_value_(assembler->predictable_code_size()) {
+ assembler_->set_predictable_code_size(true);
+}
+
+
+PredictableCodeSizeScope::~PredictableCodeSizeScope() {
+ // TODO(svenpanne) Remove the 'if' when everything works.
+ if (expected_size_ >= 0) {
+ CHECK_EQ(expected_size_, assembler_->pc_offset() - start_offset_);
+ }
+ assembler_->set_predictable_code_size(old_value_);
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of CpuFeatureScope
+
+#ifdef DEBUG
+CpuFeatureScope::CpuFeatureScope(AssemblerBase* assembler, CpuFeature f)
+ : assembler_(assembler) {
+ ASSERT(CpuFeatures::IsSafeForSnapshot(f));
+ old_enabled_ = assembler_->enabled_cpu_features();
+ uint64_t mask = static_cast<uint64_t>(1) << f;
+ // TODO(svenpanne) This special case below doesn't belong here!
+#if V8_TARGET_ARCH_ARM
+ // ARMv7 is implied by VFP3.
+ if (f == VFP3) {
+ mask |= static_cast<uint64_t>(1) << ARMv7;
+ }
+#endif
+ assembler_->set_enabled_cpu_features(old_enabled_ | mask);
+}
+
+
+CpuFeatureScope::~CpuFeatureScope() {
+ assembler_->set_enabled_cpu_features(old_enabled_);
+}
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Label
+
+int Label::pos() const {
+ if (pos_ < 0) return -pos_ - 1;
+ if (pos_ > 0) return pos_ - 1;
+ UNREACHABLE();
+ return 0;
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of RelocInfoWriter and RelocIterator
+//
+// Relocation information is written backwards in memory, from high addresses
+// towards low addresses, byte by byte. Therefore, in the encodings listed
+// below, the first byte listed it at the highest address, and successive
+// bytes in the record are at progressively lower addresses.
+//
+// Encoding
+//
+// The most common modes are given single-byte encodings. Also, it is
+// easy to identify the type of reloc info and skip unwanted modes in
+// an iteration.
+//
+// The encoding relies on the fact that there are fewer than 14
+// different relocation modes using standard non-compact encoding.
+//
+// The first byte of a relocation record has a tag in its low 2 bits:
+// Here are the record schemes, depending on the low tag and optional higher
+// tags.
+//
+// Low tag:
+// 00: embedded_object: [6-bit pc delta] 00
+//
+// 01: code_target: [6-bit pc delta] 01
+//
+// 10: short_data_record: [6-bit pc delta] 10 followed by
+// [6-bit data delta] [2-bit data type tag]
+//
+// 11: long_record [2-bit high tag][4 bit middle_tag] 11
+// followed by variable data depending on type.
+//
+// 2-bit data type tags, used in short_data_record and data_jump long_record:
+// code_target_with_id: 00
+// position: 01
+// statement_position: 10
+// comment: 11 (not used in short_data_record)
+//
+// Long record format:
+// 4-bit middle_tag:
+// 0000 - 1100 : Short record for RelocInfo::Mode middle_tag + 2
+// (The middle_tag encodes rmode - RelocInfo::LAST_COMPACT_ENUM,
+// and is between 0000 and 1100)
+// The format is:
+// 00 [4 bit middle_tag] 11 followed by
+// 00 [6 bit pc delta]
+//
+// 1101: constant pool. Used on ARM only for now.
+// The format is: 11 1101 11
+// signed int (size of the constant pool).
+// 1110: long_data_record
+// The format is: [2-bit data_type_tag] 1110 11
+// signed intptr_t, lowest byte written first
+// (except data_type code_target_with_id, which
+// is followed by a signed int, not intptr_t.)
+//
+// 1111: long_pc_jump
+// The format is:
+// pc-jump: 00 1111 11,
+// 00 [6 bits pc delta]
+// or
+// pc-jump (variable length):
+// 01 1111 11,
+// [7 bits data] 0
+// ...
+// [7 bits data] 1
+// (Bits 6..31 of pc delta, with leading zeroes
+// dropped, and last non-zero chunk tagged with 1.)
+
+
+const int kMaxStandardNonCompactModes = 14;
+
+const int kTagBits = 2;
+const int kTagMask = (1 << kTagBits) - 1;
+const int kExtraTagBits = 4;
+const int kLocatableTypeTagBits = 2;
+const int kSmallDataBits = kBitsPerByte - kLocatableTypeTagBits;
+
+const int kEmbeddedObjectTag = 0;
+const int kCodeTargetTag = 1;
+const int kLocatableTag = 2;
+const int kDefaultTag = 3;
+
+const int kPCJumpExtraTag = (1 << kExtraTagBits) - 1;
+
+const int kSmallPCDeltaBits = kBitsPerByte - kTagBits;
+const int kSmallPCDeltaMask = (1 << kSmallPCDeltaBits) - 1;
+const int RelocInfo::kMaxSmallPCDelta = kSmallPCDeltaMask;
+
+const int kVariableLengthPCJumpTopTag = 1;
+const int kChunkBits = 7;
+const int kChunkMask = (1 << kChunkBits) - 1;
+const int kLastChunkTagBits = 1;
+const int kLastChunkTagMask = 1;
+const int kLastChunkTag = 1;
+
+
+const int kDataJumpExtraTag = kPCJumpExtraTag - 1;
+
+const int kCodeWithIdTag = 0;
+const int kNonstatementPositionTag = 1;
+const int kStatementPositionTag = 2;
+const int kCommentTag = 3;
+
+const int kConstPoolExtraTag = kPCJumpExtraTag - 2;
+const int kConstPoolTag = 3;
+
+
+uint32_t RelocInfoWriter::WriteVariableLengthPCJump(uint32_t pc_delta) {
+ // Return if the pc_delta can fit in kSmallPCDeltaBits bits.
+ // Otherwise write a variable length PC jump for the bits that do
+ // not fit in the kSmallPCDeltaBits bits.
+ if (is_uintn(pc_delta, kSmallPCDeltaBits)) return pc_delta;
+ WriteExtraTag(kPCJumpExtraTag, kVariableLengthPCJumpTopTag);
+ uint32_t pc_jump = pc_delta >> kSmallPCDeltaBits;
+ ASSERT(pc_jump > 0);
+ // Write kChunkBits size chunks of the pc_jump.
+ for (; pc_jump > 0; pc_jump = pc_jump >> kChunkBits) {
+ byte b = pc_jump & kChunkMask;
+ *--pos_ = b << kLastChunkTagBits;
+ }
+ // Tag the last chunk so it can be identified.
+ *pos_ = *pos_ | kLastChunkTag;
+ // Return the remaining kSmallPCDeltaBits of the pc_delta.
+ return pc_delta & kSmallPCDeltaMask;
+}
+
+
+void RelocInfoWriter::WriteTaggedPC(uint32_t pc_delta, int tag) {
+ // Write a byte of tagged pc-delta, possibly preceded by var. length pc-jump.
+ pc_delta = WriteVariableLengthPCJump(pc_delta);
+ *--pos_ = pc_delta << kTagBits | tag;
+}
+
+
+void RelocInfoWriter::WriteTaggedData(intptr_t data_delta, int tag) {
+ *--pos_ = static_cast<byte>(data_delta << kLocatableTypeTagBits | tag);
+}
+
+
+void RelocInfoWriter::WriteExtraTag(int extra_tag, int top_tag) {
+ *--pos_ = static_cast<int>(top_tag << (kTagBits + kExtraTagBits) |
+ extra_tag << kTagBits |
+ kDefaultTag);
+}
+
+
+void RelocInfoWriter::WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag) {
+ // Write two-byte tagged pc-delta, possibly preceded by var. length pc-jump.
+ pc_delta = WriteVariableLengthPCJump(pc_delta);
+ WriteExtraTag(extra_tag, 0);
+ *--pos_ = pc_delta;
+}
+
+
+void RelocInfoWriter::WriteExtraTaggedIntData(int data_delta, int top_tag) {
+ WriteExtraTag(kDataJumpExtraTag, top_tag);
+ for (int i = 0; i < kIntSize; i++) {
+ *--pos_ = static_cast<byte>(data_delta);
+ // Signed right shift is arithmetic shift. Tested in test-utils.cc.
+ data_delta = data_delta >> kBitsPerByte;
+ }
+}
+
+
+void RelocInfoWriter::WriteExtraTaggedConstPoolData(int data) {
+ WriteExtraTag(kConstPoolExtraTag, kConstPoolTag);
+ for (int i = 0; i < kIntSize; i++) {
+ *--pos_ = static_cast<byte>(data);
+ // Signed right shift is arithmetic shift. Tested in test-utils.cc.
+ data = data >> kBitsPerByte;
+ }
+}
+
+
+void RelocInfoWriter::WriteExtraTaggedData(intptr_t data_delta, int top_tag) {
+ WriteExtraTag(kDataJumpExtraTag, top_tag);
+ for (int i = 0; i < kIntptrSize; i++) {
+ *--pos_ = static_cast<byte>(data_delta);
+ // Signed right shift is arithmetic shift. Tested in test-utils.cc.
+ data_delta = data_delta >> kBitsPerByte;
+ }
+}
+
+
+void RelocInfoWriter::Write(const RelocInfo* rinfo) {
+#ifdef DEBUG
+ byte* begin_pos = pos_;
+#endif
+ ASSERT(rinfo->rmode() < RelocInfo::NUMBER_OF_MODES);
+ ASSERT(rinfo->pc() - last_pc_ >= 0);
+ ASSERT(RelocInfo::LAST_STANDARD_NONCOMPACT_ENUM - RelocInfo::LAST_COMPACT_ENUM
+ <= kMaxStandardNonCompactModes);
+ // Use unsigned delta-encoding for pc.
+ uint32_t pc_delta = static_cast<uint32_t>(rinfo->pc() - last_pc_);
+ RelocInfo::Mode rmode = rinfo->rmode();
+
+ // The two most common modes are given small tags, and usually fit in a byte.
+ if (rmode == RelocInfo::EMBEDDED_OBJECT) {
+ WriteTaggedPC(pc_delta, kEmbeddedObjectTag);
+ } else if (rmode == RelocInfo::CODE_TARGET) {
+ WriteTaggedPC(pc_delta, kCodeTargetTag);
+ ASSERT(begin_pos - pos_ <= RelocInfo::kMaxCallSize);
+ } else if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
+ // Use signed delta-encoding for id.
+ ASSERT(static_cast<int>(rinfo->data()) == rinfo->data());
+ int id_delta = static_cast<int>(rinfo->data()) - last_id_;
+ // Check if delta is small enough to fit in a tagged byte.
+ if (is_intn(id_delta, kSmallDataBits)) {
+ WriteTaggedPC(pc_delta, kLocatableTag);
+ WriteTaggedData(id_delta, kCodeWithIdTag);
+ } else {
+ // Otherwise, use costly encoding.
+ WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
+ WriteExtraTaggedIntData(id_delta, kCodeWithIdTag);
+ }
+ last_id_ = static_cast<int>(rinfo->data());
+ } else if (RelocInfo::IsPosition(rmode)) {
+ // Use signed delta-encoding for position.
+ ASSERT(static_cast<int>(rinfo->data()) == rinfo->data());
+ int pos_delta = static_cast<int>(rinfo->data()) - last_position_;
+ int pos_type_tag = (rmode == RelocInfo::POSITION) ? kNonstatementPositionTag
+ : kStatementPositionTag;
+ // Check if delta is small enough to fit in a tagged byte.
+ if (is_intn(pos_delta, kSmallDataBits)) {
+ WriteTaggedPC(pc_delta, kLocatableTag);
+ WriteTaggedData(pos_delta, pos_type_tag);
+ } else {
+ // Otherwise, use costly encoding.
+ WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
+ WriteExtraTaggedIntData(pos_delta, pos_type_tag);
+ }
+ last_position_ = static_cast<int>(rinfo->data());
+ } else if (RelocInfo::IsComment(rmode)) {
+ // Comments are normally not generated, so we use the costly encoding.
+ WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
+ WriteExtraTaggedData(rinfo->data(), kCommentTag);
+ ASSERT(begin_pos - pos_ >= RelocInfo::kMinRelocCommentSize);
+ } else if (RelocInfo::IsConstPool(rmode)) {
+ WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
+ WriteExtraTaggedConstPoolData(static_cast<int>(rinfo->data()));
+ } else {
+ ASSERT(rmode > RelocInfo::LAST_COMPACT_ENUM);
+ int saved_mode = rmode - RelocInfo::LAST_COMPACT_ENUM;
+ // For all other modes we simply use the mode as the extra tag.
+ // None of these modes need a data component.
+ ASSERT(saved_mode < kPCJumpExtraTag && saved_mode < kDataJumpExtraTag);
+ WriteExtraTaggedPC(pc_delta, saved_mode);
+ }
+ last_pc_ = rinfo->pc();
+#ifdef DEBUG
+ ASSERT(begin_pos - pos_ <= kMaxSize);
+#endif
+}
+
+
+inline int RelocIterator::AdvanceGetTag() {
+ return *--pos_ & kTagMask;
+}
+
+
+inline int RelocIterator::GetExtraTag() {
+ return (*pos_ >> kTagBits) & ((1 << kExtraTagBits) - 1);
+}
+
+
+inline int RelocIterator::GetTopTag() {
+ return *pos_ >> (kTagBits + kExtraTagBits);
+}
+
+
+inline void RelocIterator::ReadTaggedPC() {
+ rinfo_.pc_ += *pos_ >> kTagBits;
+}
+
+
+inline void RelocIterator::AdvanceReadPC() {
+ rinfo_.pc_ += *--pos_;
+}
+
+
+void RelocIterator::AdvanceReadId() {
+ int x = 0;
+ for (int i = 0; i < kIntSize; i++) {
+ x |= static_cast<int>(*--pos_) << i * kBitsPerByte;
+ }
+ last_id_ += x;
+ rinfo_.data_ = last_id_;
+}
+
+
+void RelocIterator::AdvanceReadConstPoolData() {
+ int x = 0;
+ for (int i = 0; i < kIntSize; i++) {
+ x |= static_cast<int>(*--pos_) << i * kBitsPerByte;
+ }
+ rinfo_.data_ = x;
+}
+
+
+void RelocIterator::AdvanceReadPosition() {
+ int x = 0;
+ for (int i = 0; i < kIntSize; i++) {
+ x |= static_cast<int>(*--pos_) << i * kBitsPerByte;
+ }
+ last_position_ += x;
+ rinfo_.data_ = last_position_;
+}
+
+
+void RelocIterator::AdvanceReadData() {
+ intptr_t x = 0;
+ for (int i = 0; i < kIntptrSize; i++) {
+ x |= static_cast<intptr_t>(*--pos_) << i * kBitsPerByte;
+ }
+ rinfo_.data_ = x;
+}
+
+
+void RelocIterator::AdvanceReadVariableLengthPCJump() {
+ // Read the 32-kSmallPCDeltaBits most significant bits of the
+ // pc jump in kChunkBits bit chunks and shift them into place.
+ // Stop when the last chunk is encountered.
+ uint32_t pc_jump = 0;
+ for (int i = 0; i < kIntSize; i++) {
+ byte pc_jump_part = *--pos_;
+ pc_jump |= (pc_jump_part >> kLastChunkTagBits) << i * kChunkBits;
+ if ((pc_jump_part & kLastChunkTagMask) == 1) break;
+ }
+ // The least significant kSmallPCDeltaBits bits will be added
+ // later.
+ rinfo_.pc_ += pc_jump << kSmallPCDeltaBits;
+}
+
+
+inline int RelocIterator::GetLocatableTypeTag() {
+ return *pos_ & ((1 << kLocatableTypeTagBits) - 1);
+}
+
+
+inline void RelocIterator::ReadTaggedId() {
+ int8_t signed_b = *pos_;
+ // Signed right shift is arithmetic shift. Tested in test-utils.cc.
+ last_id_ += signed_b >> kLocatableTypeTagBits;
+ rinfo_.data_ = last_id_;
+}
+
+
+inline void RelocIterator::ReadTaggedPosition() {
+ int8_t signed_b = *pos_;
+ // Signed right shift is arithmetic shift. Tested in test-utils.cc.
+ last_position_ += signed_b >> kLocatableTypeTagBits;
+ rinfo_.data_ = last_position_;
+}
+
+
+static inline RelocInfo::Mode GetPositionModeFromTag(int tag) {
+ ASSERT(tag == kNonstatementPositionTag ||
+ tag == kStatementPositionTag);
+ return (tag == kNonstatementPositionTag) ?
+ RelocInfo::POSITION :
+ RelocInfo::STATEMENT_POSITION;
+}
+
+
+void RelocIterator::next() {
+ ASSERT(!done());
+ // Basically, do the opposite of RelocInfoWriter::Write.
+ // Reading of data is as far as possible avoided for unwanted modes,
+ // but we must always update the pc.
+ //
+ // We exit this loop by returning when we find a mode we want.
+ while (pos_ > end_) {
+ int tag = AdvanceGetTag();
+ if (tag == kEmbeddedObjectTag) {
+ ReadTaggedPC();
+ if (SetMode(RelocInfo::EMBEDDED_OBJECT)) return;
+ } else if (tag == kCodeTargetTag) {
+ ReadTaggedPC();
+ if (SetMode(RelocInfo::CODE_TARGET)) return;
+ } else if (tag == kLocatableTag) {
+ ReadTaggedPC();
+ Advance();
+ int locatable_tag = GetLocatableTypeTag();
+ if (locatable_tag == kCodeWithIdTag) {
+ if (SetMode(RelocInfo::CODE_TARGET_WITH_ID)) {
+ ReadTaggedId();
+ return;
+ }
+ } else {
+ // Compact encoding is never used for comments,
+ // so it must be a position.
+ ASSERT(locatable_tag == kNonstatementPositionTag ||
+ locatable_tag == kStatementPositionTag);
+ if (mode_mask_ & RelocInfo::kPositionMask) {
+ ReadTaggedPosition();
+ if (SetMode(GetPositionModeFromTag(locatable_tag))) return;
+ }
+ }
+ } else {
+ ASSERT(tag == kDefaultTag);
+ int extra_tag = GetExtraTag();
+ if (extra_tag == kPCJumpExtraTag) {
+ if (GetTopTag() == kVariableLengthPCJumpTopTag) {
+ AdvanceReadVariableLengthPCJump();
+ } else {
+ AdvanceReadPC();
+ }
+ } else if (extra_tag == kDataJumpExtraTag) {
+ int locatable_tag = GetTopTag();
+ if (locatable_tag == kCodeWithIdTag) {
+ if (SetMode(RelocInfo::CODE_TARGET_WITH_ID)) {
+ AdvanceReadId();
+ return;
+ }
+ Advance(kIntSize);
+ } else if (locatable_tag != kCommentTag) {
+ ASSERT(locatable_tag == kNonstatementPositionTag ||
+ locatable_tag == kStatementPositionTag);
+ if (mode_mask_ & RelocInfo::kPositionMask) {
+ AdvanceReadPosition();
+ if (SetMode(GetPositionModeFromTag(locatable_tag))) return;
+ } else {
+ Advance(kIntSize);
+ }
+ } else {
+ ASSERT(locatable_tag == kCommentTag);
+ if (SetMode(RelocInfo::COMMENT)) {
+ AdvanceReadData();
+ return;
+ }
+ Advance(kIntptrSize);
+ }
+ } else if ((extra_tag == kConstPoolExtraTag) &&
+ (GetTopTag() == kConstPoolTag)) {
+ if (SetMode(RelocInfo::CONST_POOL)) {
+ AdvanceReadConstPoolData();
+ return;
+ }
+ Advance(kIntSize);
+ } else {
+ AdvanceReadPC();
+ int rmode = extra_tag + RelocInfo::LAST_COMPACT_ENUM;
+ if (SetMode(static_cast<RelocInfo::Mode>(rmode))) return;
+ }
+ }
+ }
+ if (code_age_sequence_ != NULL) {
+ byte* old_code_age_sequence = code_age_sequence_;
+ code_age_sequence_ = NULL;
+ if (SetMode(RelocInfo::CODE_AGE_SEQUENCE)) {
+ rinfo_.data_ = 0;
+ rinfo_.pc_ = old_code_age_sequence;
+ return;
+ }
+ }
+ done_ = true;
+}
+
+
+RelocIterator::RelocIterator(Code* code, int mode_mask) {
+ rinfo_.host_ = code;
+ rinfo_.pc_ = code->instruction_start();
+ rinfo_.data_ = 0;
+ // Relocation info is read backwards.
+ pos_ = code->relocation_start() + code->relocation_size();
+ end_ = code->relocation_start();
+ done_ = false;
+ mode_mask_ = mode_mask;
+ last_id_ = 0;
+ last_position_ = 0;
+ byte* sequence = code->FindCodeAgeSequence();
+ if (sequence != NULL && !Code::IsYoungSequence(sequence)) {
+ code_age_sequence_ = sequence;
+ } else {
+ code_age_sequence_ = NULL;
+ }
+ if (mode_mask_ == 0) pos_ = end_;
+ next();
+}
+
+
+RelocIterator::RelocIterator(const CodeDesc& desc, int mode_mask) {
+ rinfo_.pc_ = desc.buffer;
+ rinfo_.data_ = 0;
+ // Relocation info is read backwards.
+ pos_ = desc.buffer + desc.buffer_size;
+ end_ = pos_ - desc.reloc_size;
+ done_ = false;
+ mode_mask_ = mode_mask;
+ last_id_ = 0;
+ last_position_ = 0;
+ code_age_sequence_ = NULL;
+ if (mode_mask_ == 0) pos_ = end_;
+ next();
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of RelocInfo
+
+
+#ifdef DEBUG
+bool RelocInfo::RequiresRelocation(const CodeDesc& desc) {
+ // Ensure there are no code targets or embedded objects present in the
+ // deoptimization entries, they would require relocation after code
+ // generation.
+ int mode_mask = RelocInfo::kCodeTargetMask |
+ RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
+ RelocInfo::ModeMask(RelocInfo::CELL) |
+ RelocInfo::kApplyMask;
+ RelocIterator it(desc, mode_mask);
+ return !it.done();
+}
+#endif
+
+
+#ifdef ENABLE_DISASSEMBLER
+const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
+ switch (rmode) {
+ case RelocInfo::NONE32:
+ return "no reloc 32";
+ case RelocInfo::NONE64:
+ return "no reloc 64";
+ case RelocInfo::EMBEDDED_OBJECT:
+ return "embedded object";
+ case RelocInfo::CONSTRUCT_CALL:
+ return "code target (js construct call)";
+ case RelocInfo::CODE_TARGET_CONTEXT:
+ return "code target (context)";
+ case RelocInfo::DEBUG_BREAK:
+#ifndef ENABLE_DEBUGGER_SUPPORT
+ UNREACHABLE();
+#endif
+ return "debug break";
+ case RelocInfo::CODE_TARGET:
+ return "code target";
+ case RelocInfo::CODE_TARGET_WITH_ID:
+ return "code target with id";
+ case RelocInfo::CELL:
+ return "property cell";
+ case RelocInfo::RUNTIME_ENTRY:
+ return "runtime entry";
+ case RelocInfo::JS_RETURN:
+ return "js return";
+ case RelocInfo::COMMENT:
+ return "comment";
+ case RelocInfo::POSITION:
+ return "position";
+ case RelocInfo::STATEMENT_POSITION:
+ return "statement position";
+ case RelocInfo::EXTERNAL_REFERENCE:
+ return "external reference";
+ case RelocInfo::INTERNAL_REFERENCE:
+ return "internal reference";
+ case RelocInfo::CONST_POOL:
+ return "constant pool";
+ case RelocInfo::DEBUG_BREAK_SLOT:
+#ifndef ENABLE_DEBUGGER_SUPPORT
+ UNREACHABLE();
+#endif
+ return "debug break slot";
+ case RelocInfo::CODE_AGE_SEQUENCE:
+ return "code_age_sequence";
+ case RelocInfo::NUMBER_OF_MODES:
+ UNREACHABLE();
+ return "number_of_modes";
+ }
+ return "unknown relocation type";
+}
+
+
+void RelocInfo::Print(Isolate* isolate, FILE* out) {
+ PrintF(out, "%p %s", pc_, RelocModeName(rmode_));
+ if (IsComment(rmode_)) {
+ PrintF(out, " (%s)", reinterpret_cast<char*>(data_));
+ } else if (rmode_ == EMBEDDED_OBJECT) {
+ PrintF(out, " (");
+ target_object()->ShortPrint(out);
+ PrintF(out, ")");
+ } else if (rmode_ == EXTERNAL_REFERENCE) {
+ ExternalReferenceEncoder ref_encoder;
+ PrintF(out, " (%s) (%p)",
+ ref_encoder.NameOfAddress(*target_reference_address()),
+ *target_reference_address());
+ } else if (IsCodeTarget(rmode_)) {
+ Code* code = Code::GetCodeFromTargetAddress(target_address());
+ PrintF(out, " (%s) (%p)", Code::Kind2String(code->kind()),
+ target_address());
+ if (rmode_ == CODE_TARGET_WITH_ID) {
+ PrintF(" (id=%d)", static_cast<int>(data_));
+ }
+ } else if (IsPosition(rmode_)) {
+ PrintF(out, " (%" V8_PTR_PREFIX "d)", data());
+ } else if (IsRuntimeEntry(rmode_) &&
+ isolate->deoptimizer_data() != NULL) {
+ // Depotimization bailouts are stored as runtime entries.
+ int id = Deoptimizer::GetDeoptimizationId(
+ isolate, target_address(), Deoptimizer::EAGER);
+ if (id != Deoptimizer::kNotDeoptimizationEntry) {
+ PrintF(out, " (deoptimization bailout %d)", id);
+ }
+ }
+
+ PrintF(out, "\n");
+}
+#endif // ENABLE_DISASSEMBLER
+
+
+#ifdef VERIFY_HEAP
+void RelocInfo::Verify() {
+ switch (rmode_) {
+ case EMBEDDED_OBJECT:
+ Object::VerifyPointer(target_object());
+ break;
+ case CELL:
+ Object::VerifyPointer(target_cell());
+ break;
+ case DEBUG_BREAK:
+#ifndef ENABLE_DEBUGGER_SUPPORT
+ UNREACHABLE();
+ break;
+#endif
+ case CONSTRUCT_CALL:
+ case CODE_TARGET_CONTEXT:
+ case CODE_TARGET_WITH_ID:
+ case CODE_TARGET: {
+ // convert inline target address to code object
+ Address addr = target_address();
+ CHECK(addr != NULL);
+ // Check that we can find the right code object.
+ Code* code = Code::GetCodeFromTargetAddress(addr);
+ Object* found = code->GetIsolate()->FindCodeObject(addr);
+ CHECK(found->IsCode());
+ CHECK(code->address() == HeapObject::cast(found)->address());
+ break;
+ }
+ case RUNTIME_ENTRY:
+ case JS_RETURN:
+ case COMMENT:
+ case POSITION:
+ case STATEMENT_POSITION:
+ case EXTERNAL_REFERENCE:
+ case INTERNAL_REFERENCE:
+ case CONST_POOL:
+ case DEBUG_BREAK_SLOT:
+ case NONE32:
+ case NONE64:
+ break;
+ case NUMBER_OF_MODES:
+ UNREACHABLE();
+ break;
+ case CODE_AGE_SEQUENCE:
+ ASSERT(Code::IsYoungSequence(pc_) || code_age_stub()->IsCode());
+ break;
+ }
+}
+#endif // VERIFY_HEAP
+
+
+// -----------------------------------------------------------------------------
+// Implementation of ExternalReference
+
+void ExternalReference::SetUp() {
+ double_constants.min_int = kMinInt;
+ double_constants.one_half = 0.5;
+ double_constants.minus_one_half = -0.5;
+ double_constants.minus_zero = -0.0;
+ double_constants.uint8_max_value = 255;
+ double_constants.zero = 0.0;
+ double_constants.canonical_non_hole_nan = OS::nan_value();
+ double_constants.the_hole_nan = BitCast<double>(kHoleNanInt64);
+ double_constants.negative_infinity = -V8_INFINITY;
+
+ math_exp_data_mutex = OS::CreateMutex();
+}
+
+
+void ExternalReference::InitializeMathExpData() {
+ // Early return?
+ if (math_exp_data_initialized) return;
+
+ math_exp_data_mutex->Lock();
+ if (!math_exp_data_initialized) {
+ // If this is changed, generated code must be adapted too.
+ const int kTableSizeBits = 11;
+ const int kTableSize = 1 << kTableSizeBits;
+ const double kTableSizeDouble = static_cast<double>(kTableSize);
+
+ math_exp_constants_array = new double[9];
+ // Input values smaller than this always return 0.
+ math_exp_constants_array[0] = -708.39641853226408;
+ // Input values larger than this always return +Infinity.
+ math_exp_constants_array[1] = 709.78271289338397;
+ math_exp_constants_array[2] = V8_INFINITY;
+ // The rest is black magic. Do not attempt to understand it. It is
+ // loosely based on the "expd" function published at:
+ // http://herumi.blogspot.com/2011/08/fast-double-precision-exponential.html
+ const double constant3 = (1 << kTableSizeBits) / log(2.0);
+ math_exp_constants_array[3] = constant3;
+ math_exp_constants_array[4] =
+ static_cast<double>(static_cast<int64_t>(3) << 51);
+ math_exp_constants_array[5] = 1 / constant3;
+ math_exp_constants_array[6] = 3.0000000027955394;
+ math_exp_constants_array[7] = 0.16666666685227835;
+ math_exp_constants_array[8] = 1;
+
+ math_exp_log_table_array = new double[kTableSize];
+ for (int i = 0; i < kTableSize; i++) {
+ double value = pow(2, i / kTableSizeDouble);
+ uint64_t bits = BitCast<uint64_t, double>(value);
+ bits &= (static_cast<uint64_t>(1) << 52) - 1;
+ double mantissa = BitCast<double, uint64_t>(bits);
+ math_exp_log_table_array[i] = mantissa;
+ }
+
+ math_exp_data_initialized = true;
+ }
+ math_exp_data_mutex->Unlock();
+}
+
+
+void ExternalReference::TearDownMathExpData() {
+ delete[] math_exp_constants_array;
+ delete[] math_exp_log_table_array;
+ delete math_exp_data_mutex;
+}
+
+
+ExternalReference::ExternalReference(Builtins::CFunctionId id, Isolate* isolate)
+ : address_(Redirect(isolate, Builtins::c_function_address(id))) {}
+
+
+ExternalReference::ExternalReference(
+ ApiFunction* fun,
+ Type type = ExternalReference::BUILTIN_CALL,
+ Isolate* isolate = NULL)
+ : address_(Redirect(isolate, fun->address(), type)) {}
+
+
+ExternalReference::ExternalReference(Builtins::Name name, Isolate* isolate)
+ : address_(isolate->builtins()->builtin_address(name)) {}
+
+
+ExternalReference::ExternalReference(Runtime::FunctionId id,
+ Isolate* isolate)
+ : address_(Redirect(isolate, Runtime::FunctionForId(id)->entry)) {}
+
+
+ExternalReference::ExternalReference(const Runtime::Function* f,
+ Isolate* isolate)
+ : address_(Redirect(isolate, f->entry)) {}
+
+
+ExternalReference ExternalReference::isolate_address(Isolate* isolate) {
+ return ExternalReference(isolate);
+}
+
+
+ExternalReference::ExternalReference(const IC_Utility& ic_utility,
+ Isolate* isolate)
+ : address_(Redirect(isolate, ic_utility.address())) {}
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ExternalReference::ExternalReference(const Debug_Address& debug_address,
+ Isolate* isolate)
+ : address_(debug_address.address(isolate)) {}
+#endif
+
+ExternalReference::ExternalReference(StatsCounter* counter)
+ : address_(reinterpret_cast<Address>(counter->GetInternalPointer())) {}
+
+
+ExternalReference::ExternalReference(Isolate::AddressId id, Isolate* isolate)
+ : address_(isolate->get_address_from_id(id)) {}
+
+
+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)));
+}
+
+
+ExternalReference ExternalReference::fill_heap_number_with_random_function(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(
+ isolate,
+ FUNCTION_ADDR(V8::FillHeapNumberWithRandom)));
+}
+
+
+ExternalReference ExternalReference::delete_handle_scope_extensions(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(
+ isolate,
+ FUNCTION_ADDR(HandleScope::DeleteExtensions)));
+}
+
+
+ExternalReference ExternalReference::random_uint32_function(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(isolate, FUNCTION_ADDR(V8::Random)));
+}
+
+
+ExternalReference ExternalReference::get_date_field_function(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(isolate, FUNCTION_ADDR(JSDate::GetField)));
+}
+
+
+ExternalReference ExternalReference::get_make_code_young_function(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(
+ isolate, FUNCTION_ADDR(Code::MakeCodeAgeSequenceYoung)));
+}
+
+
+ExternalReference ExternalReference::date_cache_stamp(Isolate* isolate) {
+ return ExternalReference(isolate->date_cache()->stamp_address());
+}
+
+
+ExternalReference ExternalReference::stress_deopt_count(Isolate* isolate) {
+ return ExternalReference(isolate->stress_deopt_count_address());
+}
+
+
+ExternalReference ExternalReference::transcendental_cache_array_address(
+ Isolate* isolate) {
+ return ExternalReference(
+ isolate->transcendental_cache()->cache_array_address());
+}
+
+
+ExternalReference ExternalReference::new_deoptimizer_function(
+ Isolate* isolate) {
+ return ExternalReference(
+ Redirect(isolate, FUNCTION_ADDR(Deoptimizer::New)));
+}
+
+
+ExternalReference ExternalReference::compute_output_frames_function(
+ Isolate* isolate) {
+ return ExternalReference(
+ Redirect(isolate, FUNCTION_ADDR(Deoptimizer::ComputeOutputFrames)));
+}
+
+
+ExternalReference ExternalReference::log_enter_external_function(
+ Isolate* isolate) {
+ return ExternalReference(
+ Redirect(isolate, FUNCTION_ADDR(Logger::EnterExternal)));
+}
+
+
+ExternalReference ExternalReference::log_leave_external_function(
+ Isolate* isolate) {
+ return ExternalReference(
+ Redirect(isolate, FUNCTION_ADDR(Logger::LeaveExternal)));
+}
+
+
+ExternalReference ExternalReference::keyed_lookup_cache_keys(Isolate* isolate) {
+ return ExternalReference(isolate->keyed_lookup_cache()->keys_address());
+}
+
+
+ExternalReference ExternalReference::keyed_lookup_cache_field_offsets(
+ Isolate* isolate) {
+ return ExternalReference(
+ isolate->keyed_lookup_cache()->field_offsets_address());
+}
+
+
+ExternalReference ExternalReference::roots_array_start(Isolate* isolate) {
+ return ExternalReference(isolate->heap()->roots_array_start());
+}
+
+
+ExternalReference ExternalReference::allocation_sites_list_address(
+ Isolate* isolate) {
+ return ExternalReference(isolate->heap()->allocation_sites_list_address());
+}
+
+
+ExternalReference ExternalReference::address_of_stack_limit(Isolate* isolate) {
+ return ExternalReference(isolate->stack_guard()->address_of_jslimit());
+}
+
+
+ExternalReference ExternalReference::address_of_real_stack_limit(
+ Isolate* isolate) {
+ return ExternalReference(isolate->stack_guard()->address_of_real_jslimit());
+}
+
+
+ExternalReference ExternalReference::address_of_regexp_stack_limit(
+ Isolate* isolate) {
+ return ExternalReference(isolate->regexp_stack()->limit_address());
+}
+
+
+ExternalReference ExternalReference::new_space_start(Isolate* isolate) {
+ return ExternalReference(isolate->heap()->NewSpaceStart());
+}
+
+
+ExternalReference ExternalReference::store_buffer_top(Isolate* isolate) {
+ return ExternalReference(isolate->heap()->store_buffer()->TopAddress());
+}
+
+
+ExternalReference ExternalReference::new_space_mask(Isolate* isolate) {
+ return ExternalReference(reinterpret_cast<Address>(
+ isolate->heap()->NewSpaceMask()));
+}
+
+
+ExternalReference ExternalReference::new_space_allocation_top_address(
+ Isolate* isolate) {
+ return ExternalReference(isolate->heap()->NewSpaceAllocationTopAddress());
+}
+
+
+ExternalReference ExternalReference::heap_always_allocate_scope_depth(
+ Isolate* isolate) {
+ Heap* heap = isolate->heap();
+ return ExternalReference(heap->always_allocate_scope_depth_address());
+}
+
+
+ExternalReference ExternalReference::new_space_allocation_limit_address(
+ Isolate* isolate) {
+ return ExternalReference(isolate->heap()->NewSpaceAllocationLimitAddress());
+}
+
+
+ExternalReference ExternalReference::old_pointer_space_allocation_top_address(
+ Isolate* isolate) {
+ return ExternalReference(
+ isolate->heap()->OldPointerSpaceAllocationTopAddress());
+}
+
+
+ExternalReference ExternalReference::old_pointer_space_allocation_limit_address(
+ Isolate* isolate) {
+ return ExternalReference(
+ isolate->heap()->OldPointerSpaceAllocationLimitAddress());
+}
+
+
+ExternalReference ExternalReference::old_data_space_allocation_top_address(
+ Isolate* isolate) {
+ return ExternalReference(
+ isolate->heap()->OldDataSpaceAllocationTopAddress());
+}
+
+
+ExternalReference ExternalReference::old_data_space_allocation_limit_address(
+ Isolate* isolate) {
+ return ExternalReference(
+ isolate->heap()->OldDataSpaceAllocationLimitAddress());
+}
+
+
+ExternalReference ExternalReference::
+ new_space_high_promotion_mode_active_address(Isolate* isolate) {
+ return ExternalReference(
+ isolate->heap()->NewSpaceHighPromotionModeActiveAddress());
+}
+
+
+ExternalReference ExternalReference::handle_scope_level_address(
+ Isolate* isolate) {
+ return ExternalReference(HandleScope::current_level_address(isolate));
+}
+
+
+ExternalReference ExternalReference::handle_scope_next_address(
+ Isolate* isolate) {
+ return ExternalReference(HandleScope::current_next_address(isolate));
+}
+
+
+ExternalReference ExternalReference::handle_scope_limit_address(
+ Isolate* isolate) {
+ return ExternalReference(HandleScope::current_limit_address(isolate));
+}
+
+
+ExternalReference ExternalReference::scheduled_exception_address(
+ Isolate* isolate) {
+ return ExternalReference(isolate->scheduled_exception_address());
+}
+
+
+ExternalReference ExternalReference::address_of_pending_message_obj(
+ Isolate* isolate) {
+ return ExternalReference(isolate->pending_message_obj_address());
+}
+
+
+ExternalReference ExternalReference::address_of_has_pending_message(
+ Isolate* isolate) {
+ return ExternalReference(isolate->has_pending_message_address());
+}
+
+
+ExternalReference ExternalReference::address_of_pending_message_script(
+ Isolate* isolate) {
+ return ExternalReference(isolate->pending_message_script_address());
+}
+
+
+ExternalReference ExternalReference::address_of_min_int() {
+ return ExternalReference(reinterpret_cast<void*>(&double_constants.min_int));
+}
+
+
+ExternalReference ExternalReference::address_of_one_half() {
+ return ExternalReference(reinterpret_cast<void*>(&double_constants.one_half));
+}
+
+
+ExternalReference ExternalReference::address_of_minus_one_half() {
+ return ExternalReference(
+ reinterpret_cast<void*>(&double_constants.minus_one_half));
+}
+
+
+ExternalReference ExternalReference::address_of_minus_zero() {
+ return ExternalReference(
+ reinterpret_cast<void*>(&double_constants.minus_zero));
+}
+
+
+ExternalReference ExternalReference::address_of_zero() {
+ return ExternalReference(reinterpret_cast<void*>(&double_constants.zero));
+}
+
+
+ExternalReference ExternalReference::address_of_uint8_max_value() {
+ return ExternalReference(
+ reinterpret_cast<void*>(&double_constants.uint8_max_value));
+}
+
+
+ExternalReference ExternalReference::address_of_negative_infinity() {
+ return ExternalReference(
+ reinterpret_cast<void*>(&double_constants.negative_infinity));
+}
+
+
+ExternalReference ExternalReference::address_of_canonical_non_hole_nan() {
+ return ExternalReference(
+ reinterpret_cast<void*>(&double_constants.canonical_non_hole_nan));
+}
+
+
+ExternalReference ExternalReference::address_of_the_hole_nan() {
+ return ExternalReference(
+ reinterpret_cast<void*>(&double_constants.the_hole_nan));
+}
+
+
+#ifndef V8_INTERPRETED_REGEXP
+
+ExternalReference ExternalReference::re_check_stack_guard_state(
+ Isolate* isolate) {
+ Address function;
+#if V8_TARGET_ARCH_X64
+ function = FUNCTION_ADDR(RegExpMacroAssemblerX64::CheckStackGuardState);
+#elif V8_TARGET_ARCH_IA32
+ function = FUNCTION_ADDR(RegExpMacroAssemblerIA32::CheckStackGuardState);
+#elif V8_TARGET_ARCH_ARM
+ function = FUNCTION_ADDR(RegExpMacroAssemblerARM::CheckStackGuardState);
+#elif V8_TARGET_ARCH_MIPS
+ function = FUNCTION_ADDR(RegExpMacroAssemblerMIPS::CheckStackGuardState);
+#else
+ UNREACHABLE();
+#endif
+ return ExternalReference(Redirect(isolate, function));
+}
+
+
+ExternalReference ExternalReference::re_grow_stack(Isolate* isolate) {
+ return ExternalReference(
+ Redirect(isolate, FUNCTION_ADDR(NativeRegExpMacroAssembler::GrowStack)));
+}
+
+ExternalReference ExternalReference::re_case_insensitive_compare_uc16(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(
+ isolate,
+ FUNCTION_ADDR(NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16)));
+}
+
+
+ExternalReference ExternalReference::re_word_character_map() {
+ return ExternalReference(
+ NativeRegExpMacroAssembler::word_character_map_address());
+}
+
+ExternalReference ExternalReference::address_of_static_offsets_vector(
+ Isolate* isolate) {
+ return ExternalReference(
+ reinterpret_cast<Address>(isolate->jsregexp_static_offsets_vector()));
+}
+
+ExternalReference ExternalReference::address_of_regexp_stack_memory_address(
+ Isolate* isolate) {
+ return ExternalReference(
+ isolate->regexp_stack()->memory_address());
+}
+
+ExternalReference ExternalReference::address_of_regexp_stack_memory_size(
+ Isolate* isolate) {
+ return ExternalReference(isolate->regexp_stack()->memory_size_address());
+}
+
+#endif // V8_INTERPRETED_REGEXP
+
+
+static double add_two_doubles(double x, double y) {
+ return x + y;
+}
+
+
+static double sub_two_doubles(double x, double y) {
+ return x - y;
+}
+
+
+static double mul_two_doubles(double x, double y) {
+ return x * y;
+}
+
+
+static double div_two_doubles(double x, double y) {
+ return x / y;
+}
+
+
+static double mod_two_doubles(double x, double y) {
+ return modulo(x, y);
+}
+
+
+static double math_sin_double(double x) {
+ return sin(x);
+}
+
+
+static double math_cos_double(double x) {
+ return cos(x);
+}
+
+
+static double math_tan_double(double x) {
+ return tan(x);
+}
+
+
+static double math_log_double(double x) {
+ return log(x);
+}
+
+
+ExternalReference ExternalReference::math_sin_double_function(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(isolate,
+ FUNCTION_ADDR(math_sin_double),
+ BUILTIN_FP_CALL));
+}
+
+
+ExternalReference ExternalReference::math_cos_double_function(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(isolate,
+ FUNCTION_ADDR(math_cos_double),
+ BUILTIN_FP_CALL));
+}
+
+
+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,
+ FUNCTION_ADDR(math_log_double),
+ BUILTIN_FP_CALL));
+}
+
+
+ExternalReference ExternalReference::math_exp_constants(int constant_index) {
+ ASSERT(math_exp_data_initialized);
+ return ExternalReference(
+ reinterpret_cast<void*>(math_exp_constants_array + constant_index));
+}
+
+
+ExternalReference ExternalReference::math_exp_log_table() {
+ ASSERT(math_exp_data_initialized);
+ return ExternalReference(reinterpret_cast<void*>(math_exp_log_table_array));
+}
+
+
+ExternalReference ExternalReference::page_flags(Page* page) {
+ return ExternalReference(reinterpret_cast<Address>(page) +
+ MemoryChunk::kFlagsOffset);
+}
+
+
+ExternalReference ExternalReference::ForDeoptEntry(Address entry) {
+ return ExternalReference(entry);
+}
+
+
+double power_helper(double x, double y) {
+ int y_int = static_cast<int>(y);
+ if (y == y_int) {
+ return power_double_int(x, y_int); // Returns 1 if exponent is 0.
+ }
+ if (y == 0.5) {
+ return (std::isinf(x)) ? V8_INFINITY
+ : fast_sqrt(x + 0.0); // Convert -0 to +0.
+ }
+ if (y == -0.5) {
+ return (std::isinf(x)) ? 0 : 1.0 / fast_sqrt(x + 0.0); // Convert -0 to +0.
+ }
+ return power_double_double(x, y);
+}
+
+
+// Helper function to compute x^y, where y is known to be an
+// integer. Uses binary decomposition to limit the number of
+// multiplications; see the discussion in "Hacker's Delight" by Henry
+// S. Warren, Jr., figure 11-6, page 213.
+double power_double_int(double x, int y) {
+ double m = (y < 0) ? 1 / x : x;
+ unsigned n = (y < 0) ? -y : y;
+ double p = 1;
+ while (n != 0) {
+ if ((n & 1) != 0) p *= m;
+ m *= m;
+ if ((n & 2) != 0) p *= m;
+ m *= m;
+ n >>= 2;
+ }
+ return p;
+}
+
+
+double power_double_double(double x, double y) {
+#if defined(__MINGW64_VERSION_MAJOR) && \
+ (!defined(__MINGW64_VERSION_RC) || __MINGW64_VERSION_RC < 1)
+ // MinGW64 has a custom implementation for pow. This handles certain
+ // special cases that are different.
+ if ((x == 0.0 || std::isinf(x)) && std::isfinite(y)) {
+ double f;
+ if (modf(y, &f) != 0.0) return ((x == 0.0) ^ (y > 0)) ? V8_INFINITY : 0;
+ }
+
+ if (x == 2.0) {
+ int y_int = static_cast<int>(y);
+ if (y == y_int) return ldexp(1.0, y_int);
+ }
+#endif
+
+ // The checks for special cases can be dropped in ia32 because it has already
+ // been done in generated code before bailing out here.
+ if (std::isnan(y) || ((x == 1 || x == -1) && std::isinf(y))) {
+ return OS::nan_value();
+ }
+ return pow(x, y);
+}
+
+
+ExternalReference ExternalReference::power_double_double_function(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(isolate,
+ FUNCTION_ADDR(power_double_double),
+ BUILTIN_FP_FP_CALL));
+}
+
+
+ExternalReference ExternalReference::power_double_int_function(
+ Isolate* isolate) {
+ return ExternalReference(Redirect(isolate,
+ FUNCTION_ADDR(power_double_int),
+ BUILTIN_FP_INT_CALL));
+}
+
+
+static int native_compare_doubles(double y, double x) {
+ if (x == y) return EQUAL;
+ return x < y ? LESS : GREATER;
+}
+
+
+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);
+ BinaryFPOperation* function = NULL;
+ switch (operation) {
+ case Token::ADD:
+ function = &add_two_doubles;
+ break;
+ case Token::SUB:
+ function = &sub_two_doubles;
+ break;
+ case Token::MUL:
+ function = &mul_two_doubles;
+ break;
+ case Token::DIV:
+ function = &div_two_doubles;
+ break;
+ case Token::MOD:
+ function = &mod_two_doubles;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return ExternalReference(Redirect(isolate,
+ FUNCTION_ADDR(function),
+ BUILTIN_FP_FP_CALL));
+}
+
+
+ExternalReference ExternalReference::compare_doubles(Isolate* isolate) {
+ return ExternalReference(Redirect(isolate,
+ FUNCTION_ADDR(native_compare_doubles),
+ BUILTIN_COMPARE_CALL));
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ExternalReference ExternalReference::debug_break(Isolate* isolate) {
+ return ExternalReference(Redirect(isolate, FUNCTION_ADDR(Debug_Break)));
+}
+
+
+ExternalReference ExternalReference::debug_step_in_fp_address(
+ Isolate* isolate) {
+ return ExternalReference(isolate->debug()->step_in_fp_addr());
+}
+#endif
+
+
+void PositionsRecorder::RecordPosition(int pos) {
+ ASSERT(pos != RelocInfo::kNoPosition);
+ ASSERT(pos >= 0);
+ state_.current_position = pos;
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ if (gdbjit_lineinfo_ != NULL) {
+ gdbjit_lineinfo_->SetPosition(assembler_->pc_offset(), pos, false);
+ }
+#endif
+ LOG_CODE_EVENT(assembler_->isolate(),
+ CodeLinePosInfoAddPositionEvent(jit_handler_data_,
+ assembler_->pc_offset(),
+ pos));
+}
+
+
+void PositionsRecorder::RecordStatementPosition(int pos) {
+ ASSERT(pos != RelocInfo::kNoPosition);
+ ASSERT(pos >= 0);
+ state_.current_statement_position = pos;
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ if (gdbjit_lineinfo_ != NULL) {
+ gdbjit_lineinfo_->SetPosition(assembler_->pc_offset(), pos, true);
+ }
+#endif
+ LOG_CODE_EVENT(assembler_->isolate(),
+ CodeLinePosInfoAddStatementPositionEvent(
+ jit_handler_data_,
+ assembler_->pc_offset(),
+ pos));
+}
+
+
+bool PositionsRecorder::WriteRecordedPositions() {
+ bool written = false;
+
+ // Write the statement position if it is different from what was written last
+ // time.
+ if (state_.current_statement_position != state_.written_statement_position) {
+ EnsureSpace ensure_space(assembler_);
+ assembler_->RecordRelocInfo(RelocInfo::STATEMENT_POSITION,
+ state_.current_statement_position);
+ state_.written_statement_position = state_.current_statement_position;
+ written = true;
+ }
+
+ // Write the position if it is different from what was written last time and
+ // also different from the written statement position.
+ if (state_.current_position != state_.written_position &&
+ state_.current_position != state_.written_statement_position) {
+ EnsureSpace ensure_space(assembler_);
+ assembler_->RecordRelocInfo(RelocInfo::POSITION, state_.current_position);
+ state_.written_position = state_.current_position;
+ written = true;
+ }
+
+ // Return whether something was written.
+ return written;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/assembler.h b/chromium/v8/src/assembler.h
new file mode 100644
index 00000000000..d70d5aa928b
--- /dev/null
+++ b/chromium/v8/src/assembler.h
@@ -0,0 +1,1083 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// 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 {
+
+class StatsCounter;
+// -----------------------------------------------------------------------------
+// Platform independent assembler base class.
+
+class AssemblerBase: public Malloced {
+ public:
+ AssemblerBase(Isolate* isolate, void* buffer, int buffer_size);
+ virtual ~AssemblerBase();
+
+ Isolate* isolate() const { return isolate_; }
+ int jit_cookie() const { return jit_cookie_; }
+
+ bool emit_debug_code() const { return emit_debug_code_; }
+ void set_emit_debug_code(bool value) { emit_debug_code_ = value; }
+
+ bool predictable_code_size() const { return predictable_code_size_; }
+ void set_predictable_code_size(bool value) { predictable_code_size_ = value; }
+
+ uint64_t enabled_cpu_features() const { return enabled_cpu_features_; }
+ void set_enabled_cpu_features(uint64_t features) {
+ enabled_cpu_features_ = features;
+ }
+ bool IsEnabled(CpuFeature f) {
+ return (enabled_cpu_features_ & (static_cast<uint64_t>(1) << f)) != 0;
+ }
+
+ // Overwrite a host NaN with a quiet target NaN. Used by mksnapshot for
+ // cross-snapshotting.
+ static void QuietNaN(HeapObject* nan) { }
+
+ int pc_offset() const { return static_cast<int>(pc_ - buffer_); }
+
+ static const int kMinimalBufferSize = 4*KB;
+
+ protected:
+ // The buffer into which code and relocation info are generated. It could
+ // either be owned by the assembler or be provided externally.
+ byte* buffer_;
+ int buffer_size_;
+ bool own_buffer_;
+
+ // The program counter, which points into the buffer above and moves forward.
+ byte* pc_;
+
+ private:
+ Isolate* isolate_;
+ int jit_cookie_;
+ uint64_t enabled_cpu_features_;
+ bool emit_debug_code_;
+ bool predictable_code_size_;
+};
+
+
+// Avoids using instructions that vary in size in unpredictable ways between the
+// snapshot and the running VM.
+class PredictableCodeSizeScope {
+ public:
+ PredictableCodeSizeScope(AssemblerBase* assembler, int expected_size);
+ ~PredictableCodeSizeScope();
+
+ private:
+ AssemblerBase* assembler_;
+ int expected_size_;
+ int start_offset_;
+ bool old_value_;
+};
+
+
+// Enable a specified feature within a scope.
+class CpuFeatureScope BASE_EMBEDDED {
+ public:
+#ifdef DEBUG
+ CpuFeatureScope(AssemblerBase* assembler, CpuFeature f);
+ ~CpuFeatureScope();
+
+ private:
+ AssemblerBase* assembler_;
+ uint64_t old_enabled_;
+#else
+ CpuFeatureScope(AssemblerBase* assembler, CpuFeature f) {}
+#endif
+};
+
+
+// -----------------------------------------------------------------------------
+// Labels represent pc locations; they are typically jump or call targets.
+// After declaration, a label can be freely used to denote known or (yet)
+// unknown pc location. Assembler::bind() is used to bind a label to the
+// current pc. A label can be bound only once.
+
+class Label BASE_EMBEDDED {
+ public:
+ enum Distance {
+ kNear, kFar
+ };
+
+ INLINE(Label()) {
+ Unuse();
+ UnuseNear();
+ }
+
+ INLINE(~Label()) {
+ ASSERT(!is_linked());
+ ASSERT(!is_near_linked());
+ }
+
+ INLINE(void Unuse()) { pos_ = 0; }
+ INLINE(void UnuseNear()) { near_link_pos_ = 0; }
+
+ INLINE(bool is_bound() const) { return pos_ < 0; }
+ INLINE(bool is_unused() const) { return pos_ == 0 && near_link_pos_ == 0; }
+ INLINE(bool is_linked() const) { return pos_ > 0; }
+ INLINE(bool is_near_linked() const) { return near_link_pos_ > 0; }
+
+ // Returns the position of bound or linked labels. Cannot be used
+ // for unused labels.
+ int pos() const;
+ int near_link_pos() const { return near_link_pos_ - 1; }
+
+ private:
+ // pos_ encodes both the binding state (via its sign)
+ // and the binding position (via its value) of a label.
+ //
+ // pos_ < 0 bound label, pos() returns the jump target position
+ // pos_ == 0 unused label
+ // pos_ > 0 linked label, pos() returns the last reference position
+ int pos_;
+
+ // Behaves like |pos_| in the "> 0" case, but for near jumps to this label.
+ int near_link_pos_;
+
+ void bind_to(int pos) {
+ pos_ = -pos - 1;
+ ASSERT(is_bound());
+ }
+ void link_to(int pos, Distance distance = kFar) {
+ if (distance == kNear) {
+ near_link_pos_ = pos + 1;
+ ASSERT(is_near_linked());
+ } else {
+ pos_ = pos + 1;
+ ASSERT(is_linked());
+ }
+ }
+
+ friend class Assembler;
+ friend class RegexpAssembler;
+ friend class Displacement;
+ friend class RegExpMacroAssemblerIrregexp;
+};
+
+
+enum SaveFPRegsMode { kDontSaveFPRegs, kSaveFPRegs };
+
+
+// -----------------------------------------------------------------------------
+// Relocation information
+
+
+// Relocation information consists of the address (pc) of the datum
+// to which the relocation information applies, the relocation mode
+// (rmode), and an optional data field. The relocation mode may be
+// "descriptive" and not indicate a need for relocation, but simply
+// describe a property of the datum. Such rmodes are useful for GC
+// and nice disassembly output.
+
+class RelocInfo BASE_EMBEDDED {
+ public:
+ // The constant kNoPosition is used with the collecting of source positions
+ // in the relocation information. Two types of source positions are collected
+ // "position" (RelocMode position) and "statement position" (RelocMode
+ // statement_position). The "position" is collected at places in the source
+ // code which are of interest when making stack traces to pin-point the source
+ // location of a stack frame as close as possible. The "statement position" is
+ // collected at the beginning at each statement, and is used to indicate
+ // possible break locations. kNoPosition is used to indicate an
+ // invalid/uninitialized position value.
+ static const int kNoPosition = -1;
+
+ // This string is used to add padding comments to the reloc info in cases
+ // where we are not sure to have enough space for patching in during
+ // lazy deoptimization. This is the case if we have indirect calls for which
+ // we do not normally record relocation info.
+ static const char* const kFillerCommentString;
+
+ // The minimum size of a comment is equal to three bytes for the extra tagged
+ // pc + the tag for the data, and kPointerSize for the actual pointer to the
+ // comment.
+ static const int kMinRelocCommentSize = 3 + kPointerSize;
+
+ // The maximum size for a call instruction including pc-jump.
+ static const int kMaxCallSize = 6;
+
+ // The maximum pc delta that will use the short encoding.
+ static const int kMaxSmallPCDelta;
+
+ enum Mode {
+ // Please note the order is important (see IsCodeTarget, IsGCRelocMode).
+ CODE_TARGET, // Code target which is not any of the above.
+ CODE_TARGET_WITH_ID,
+ CONSTRUCT_CALL, // code target that is a call to a JavaScript constructor.
+ CODE_TARGET_CONTEXT, // Code target used for contextual loads and stores.
+ DEBUG_BREAK, // Code target for the debugger statement.
+ EMBEDDED_OBJECT,
+ CELL,
+
+ // Everything after runtime_entry (inclusive) is not GC'ed.
+ RUNTIME_ENTRY,
+ JS_RETURN, // Marks start of the ExitJSFrame code.
+ COMMENT,
+ POSITION, // See comment for kNoPosition above.
+ STATEMENT_POSITION, // See comment for kNoPosition above.
+ DEBUG_BREAK_SLOT, // Additional code inserted for debug break slot.
+ EXTERNAL_REFERENCE, // The address of an external C++ function.
+ INTERNAL_REFERENCE, // An address inside the same function.
+
+ // Marks a constant pool. Only used on ARM.
+ // It uses a custom noncompact encoding.
+ CONST_POOL,
+
+ // add more as needed
+ // Pseudo-types
+ NUMBER_OF_MODES, // There are at most 15 modes with noncompact encoding.
+ NONE32, // never recorded 32-bit value
+ NONE64, // never recorded 64-bit value
+ CODE_AGE_SEQUENCE, // Not stored in RelocInfo array, used explictly by
+ // code aging.
+ FIRST_REAL_RELOC_MODE = CODE_TARGET,
+ LAST_REAL_RELOC_MODE = CONST_POOL,
+ FIRST_PSEUDO_RELOC_MODE = CODE_AGE_SEQUENCE,
+ LAST_PSEUDO_RELOC_MODE = CODE_AGE_SEQUENCE,
+ LAST_CODE_ENUM = DEBUG_BREAK,
+ LAST_GCED_ENUM = CELL,
+ // Modes <= LAST_COMPACT_ENUM are guaranteed to have compact encoding.
+ LAST_COMPACT_ENUM = CODE_TARGET_WITH_ID,
+ LAST_STANDARD_NONCOMPACT_ENUM = INTERNAL_REFERENCE
+ };
+
+
+ RelocInfo() {}
+
+ RelocInfo(byte* pc, Mode rmode, intptr_t data, Code* host)
+ : pc_(pc), rmode_(rmode), data_(data), host_(host) {
+ }
+ RelocInfo(byte* pc, double data64)
+ : pc_(pc), rmode_(NONE64), data64_(data64), host_(NULL) {
+ }
+
+ static inline bool IsRealRelocMode(Mode mode) {
+ return mode >= FIRST_REAL_RELOC_MODE &&
+ mode <= LAST_REAL_RELOC_MODE;
+ }
+ static inline bool IsPseudoRelocMode(Mode mode) {
+ ASSERT(!IsRealRelocMode(mode));
+ return mode >= FIRST_PSEUDO_RELOC_MODE &&
+ mode <= LAST_PSEUDO_RELOC_MODE;
+ }
+ static inline bool IsConstructCall(Mode mode) {
+ return mode == CONSTRUCT_CALL;
+ }
+ static inline bool IsCodeTarget(Mode mode) {
+ return mode <= LAST_CODE_ENUM;
+ }
+ static inline bool IsEmbeddedObject(Mode mode) {
+ return mode == EMBEDDED_OBJECT;
+ }
+ static inline bool IsRuntimeEntry(Mode mode) {
+ return mode == RUNTIME_ENTRY;
+ }
+ // Is the relocation mode affected by GC?
+ static inline bool IsGCRelocMode(Mode mode) {
+ return mode <= LAST_GCED_ENUM;
+ }
+ static inline bool IsJSReturn(Mode mode) {
+ return mode == JS_RETURN;
+ }
+ static inline bool IsComment(Mode mode) {
+ return mode == COMMENT;
+ }
+ static inline bool IsConstPool(Mode mode) {
+ return mode == CONST_POOL;
+ }
+ static inline bool IsPosition(Mode mode) {
+ return mode == POSITION || mode == STATEMENT_POSITION;
+ }
+ static inline bool IsStatementPosition(Mode mode) {
+ return mode == STATEMENT_POSITION;
+ }
+ static inline bool IsExternalReference(Mode mode) {
+ return mode == EXTERNAL_REFERENCE;
+ }
+ static inline bool IsInternalReference(Mode mode) {
+ return mode == INTERNAL_REFERENCE;
+ }
+ static inline bool IsDebugBreakSlot(Mode mode) {
+ return mode == DEBUG_BREAK_SLOT;
+ }
+ static inline bool IsNone(Mode mode) {
+ return mode == NONE32 || mode == NONE64;
+ }
+ static inline bool IsCodeAgeSequence(Mode mode) {
+ return mode == CODE_AGE_SEQUENCE;
+ }
+ static inline int ModeMask(Mode mode) { return 1 << mode; }
+
+ // Accessors
+ byte* pc() const { return pc_; }
+ void set_pc(byte* pc) { pc_ = pc; }
+ Mode rmode() const { return rmode_; }
+ intptr_t data() const { return data_; }
+ double data64() const { return data64_; }
+ 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 (e.g. relative or patched into a series of
+ // instructions).
+ bool IsCodedSpecially();
+
+ // Read/modify the code target in the branch/call instruction
+ // this relocation applies to;
+ // can only be called if IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
+ INLINE(Address target_address());
+ 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,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER));
+ INLINE(Address target_runtime_entry(Assembler* origin));
+ INLINE(void set_target_runtime_entry(Address target,
+ WriteBarrierMode mode =
+ UPDATE_WRITE_BARRIER));
+ INLINE(Cell* target_cell());
+ INLINE(Handle<Cell> target_cell_handle());
+ INLINE(void set_target_cell(Cell* cell,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER));
+ INLINE(Code* code_age_stub());
+ INLINE(void set_code_age_stub(Code* stub));
+
+ // Read the address of the word containing the target_address in an
+ // instruction stream. What this means exactly is architecture-independent.
+ // The only architecture-independent user of this function is the serializer.
+ // The serializer uses it to find out how many raw bytes of instruction to
+ // output before the next target. Architecture-independent code shouldn't
+ // dereference the pointer it gets back from this.
+ INLINE(Address target_address_address());
+ // This indicates how much space a target takes up when deserializing a code
+ // stream. For most architectures this is just the size of a pointer. For
+ // an instruction like movw/movt where the target bits are mixed into the
+ // instruction bits the size of the target will be zero, indicating that the
+ // serializer should not step forwards in memory after a target is resolved
+ // and written. In this case the target_address_address function above
+ // should return the end of the instructions to be patched, allowing the
+ // deserializer to deserialize the instructions as raw bytes and put them in
+ // place, ready to be patched with the target.
+ INLINE(int target_address_size());
+
+ // Read/modify the reference in the instruction this relocation
+ // applies to; can only be called if rmode_ is external_reference
+ INLINE(Address* target_reference_address());
+
+ // Read/modify the address of a call instruction. This is used to relocate
+ // the break points where straight-line code is patched with a call
+ // instruction.
+ INLINE(Address call_address());
+ INLINE(void set_call_address(Address target));
+ INLINE(Object* call_object());
+ INLINE(void set_call_object(Object* target));
+ INLINE(Object** call_object_address());
+
+ template<typename StaticVisitor> inline void Visit(Heap* heap);
+ inline void Visit(ObjectVisitor* v);
+
+ // Patch the code with some other code.
+ void PatchCode(byte* instructions, int instruction_count);
+
+ // Patch the code with a call.
+ void PatchCodeWithCall(Address target, int guard_bytes);
+
+ // Check whether this return sequence has been patched
+ // with a call to the debugger.
+ INLINE(bool IsPatchedReturnSequence());
+
+ // Check whether this debug break slot has been patched with a call to the
+ // debugger.
+ INLINE(bool IsPatchedDebugBreakSlotSequence());
+
+#ifdef DEBUG
+ // Check whether the given code contains relocation information that
+ // either is position-relative or movable by the garbage collector.
+ static bool RequiresRelocation(const CodeDesc& desc);
+#endif
+
+#ifdef ENABLE_DISASSEMBLER
+ // Printing
+ static const char* RelocModeName(Mode rmode);
+ void Print(Isolate* isolate, FILE* out);
+#endif // ENABLE_DISASSEMBLER
+#ifdef VERIFY_HEAP
+ void Verify();
+#endif
+
+ static const int kCodeTargetMask = (1 << (LAST_CODE_ENUM + 1)) - 1;
+ static const int kPositionMask = 1 << POSITION | 1 << STATEMENT_POSITION;
+ static const int kDataMask =
+ (1 << CODE_TARGET_WITH_ID) | kPositionMask | (1 << COMMENT);
+ static const int kApplyMask; // Modes affected by apply. Depends on arch.
+
+ private:
+ // On ARM, note that pc_ is the address of the constant pool entry
+ // to be relocated and not the address of the instruction
+ // referencing the constant pool entry (except when rmode_ ==
+ // comment).
+ byte* pc_;
+ Mode rmode_;
+ union {
+ intptr_t data_;
+ double data64_;
+ };
+ Code* host_;
+ // Code and Embedded Object pointers on some platforms 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 naturally
+ // when accessed via the Iterator.
+ Object* reconstructed_obj_ptr_;
+ // External-reference pointers are also split across instruction-pairs
+ // on some platforms, but are accessed via indirect pointers. This location
+ // provides a place for that pointer to exist naturally. Its address
+ // is returned by RelocInfo::target_reference_address().
+ Address reconstructed_adr_ptr_;
+ friend class RelocIterator;
+};
+
+
+// RelocInfoWriter serializes a stream of relocation info. It writes towards
+// lower addresses.
+class RelocInfoWriter BASE_EMBEDDED {
+ public:
+ RelocInfoWriter() : pos_(NULL),
+ last_pc_(NULL),
+ last_id_(0),
+ last_position_(0) {}
+ RelocInfoWriter(byte* pos, byte* pc) : pos_(pos),
+ last_pc_(pc),
+ last_id_(0),
+ last_position_(0) {}
+
+ byte* pos() const { return pos_; }
+ byte* last_pc() const { return last_pc_; }
+
+ void Write(const RelocInfo* rinfo);
+
+ // Update the state of the stream after reloc info buffer
+ // and/or code is moved while the stream is active.
+ void Reposition(byte* pos, byte* pc) {
+ pos_ = pos;
+ last_pc_ = pc;
+ }
+
+ // Max size (bytes) of a written RelocInfo. Longest encoding is
+ // ExtraTag, VariableLengthPCJump, ExtraTag, pc_delta, ExtraTag, data_delta.
+ // On ia32 and arm this is 1 + 4 + 1 + 1 + 1 + 4 = 12.
+ // On x64 this is 1 + 4 + 1 + 1 + 1 + 8 == 16;
+ // Here we use the maximum of the two.
+ static const int kMaxSize = 16;
+
+ private:
+ inline uint32_t WriteVariableLengthPCJump(uint32_t pc_delta);
+ inline void WriteTaggedPC(uint32_t pc_delta, int tag);
+ inline void WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag);
+ inline void WriteExtraTaggedIntData(int data_delta, int top_tag);
+ inline void WriteExtraTaggedConstPoolData(int data);
+ inline void WriteExtraTaggedData(intptr_t data_delta, int top_tag);
+ inline void WriteTaggedData(intptr_t data_delta, int tag);
+ inline void WriteExtraTag(int extra_tag, int top_tag);
+
+ byte* pos_;
+ byte* last_pc_;
+ int last_id_;
+ int last_position_;
+ DISALLOW_COPY_AND_ASSIGN(RelocInfoWriter);
+};
+
+
+// A RelocIterator iterates over relocation information.
+// Typical use:
+//
+// for (RelocIterator it(code); !it.done(); it.next()) {
+// // do something with it.rinfo() here
+// }
+//
+// A mask can be specified to skip unwanted modes.
+class RelocIterator: public Malloced {
+ public:
+ // Create a new iterator positioned at
+ // the beginning of the reloc info.
+ // Relocation information with mode k is included in the
+ // iteration iff bit k of mode_mask is set.
+ explicit RelocIterator(Code* code, int mode_mask = -1);
+ explicit RelocIterator(const CodeDesc& desc, int mode_mask = -1);
+
+ // Iteration
+ bool done() const { return done_; }
+ void next();
+
+ // Return pointer valid until next next().
+ RelocInfo* rinfo() {
+ ASSERT(!done());
+ return &rinfo_;
+ }
+
+ private:
+ // Advance* moves the position before/after reading.
+ // *Read* reads from current byte(s) into rinfo_.
+ // *Get* just reads and returns info on current byte.
+ void Advance(int bytes = 1) { pos_ -= bytes; }
+ int AdvanceGetTag();
+ int GetExtraTag();
+ int GetTopTag();
+ void ReadTaggedPC();
+ void AdvanceReadPC();
+ void AdvanceReadId();
+ void AdvanceReadConstPoolData();
+ void AdvanceReadPosition();
+ void AdvanceReadData();
+ void AdvanceReadVariableLengthPCJump();
+ int GetLocatableTypeTag();
+ void ReadTaggedId();
+ void ReadTaggedPosition();
+
+ // If the given mode is wanted, set it in rinfo_ and return true.
+ // Else return false. Used for efficiently skipping unwanted modes.
+ bool SetMode(RelocInfo::Mode mode) {
+ return (mode_mask_ & (1 << mode)) ? (rinfo_.rmode_ = mode, true) : false;
+ }
+
+ byte* pos_;
+ byte* end_;
+ byte* code_age_sequence_;
+ RelocInfo rinfo_;
+ bool done_;
+ int mode_mask_;
+ int last_id_;
+ int last_position_;
+ DISALLOW_COPY_AND_ASSIGN(RelocIterator);
+};
+
+
+//------------------------------------------------------------------------------
+// External function
+
+//----------------------------------------------------------------------------
+class IC_Utility;
+class SCTableReference;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+class Debug_Address;
+#endif
+
+
+// An ExternalReference represents a C++ address used in the generated
+// code. All references to C++ functions and variables must be encapsulated in
+// an ExternalReference instance. This is done in order to track the origin of
+// all external references in the code so that they can be bound to the correct
+// addresses when deserializing a heap.
+class ExternalReference BASE_EMBEDDED {
+ public:
+ // Used in the simulator to support different native api calls.
+ enum Type {
+ // Builtin call.
+ // MaybeObject* f(v8::internal::Arguments).
+ BUILTIN_CALL, // default
+
+ // Builtin that takes float arguments and returns an int.
+ // int f(double, double).
+ BUILTIN_COMPARE_CALL,
+
+ // Builtin call that returns floating point.
+ // double f(double, double).
+ BUILTIN_FP_FP_CALL,
+
+ // Builtin call that returns floating point.
+ // double f(double).
+ BUILTIN_FP_CALL,
+
+ // Builtin call that returns floating point.
+ // double f(double, int).
+ BUILTIN_FP_INT_CALL,
+
+ // Direct call to API function callback.
+ // Handle<Value> f(v8::Arguments&)
+ DIRECT_API_CALL,
+
+ // Call to invocation callback via InvokeInvocationCallback.
+ // Handle<Value> f(v8::Arguments&, v8::InvocationCallback)
+ PROFILING_API_CALL,
+
+ // Direct call to API function callback.
+ // void f(v8::Arguments&)
+ DIRECT_API_CALL_NEW,
+
+ // Call to function callback via InvokeFunctionCallback.
+ // void f(v8::Arguments&, v8::FunctionCallback)
+ PROFILING_API_CALL_NEW,
+
+ // Direct call to accessor getter callback.
+ // Handle<value> f(Local<String> property, AccessorInfo& info)
+ DIRECT_GETTER_CALL,
+
+ // Call to accessor getter callback via InvokeAccessorGetter.
+ // Handle<value> f(Local<String> property, AccessorInfo& info,
+ // AccessorGetter getter)
+ PROFILING_GETTER_CALL,
+
+ // Direct call to accessor getter callback.
+ // void f(Local<String> property, AccessorInfo& info)
+ DIRECT_GETTER_CALL_NEW,
+
+ // Call to accessor getter callback via InvokeAccessorGetterCallback.
+ // void f(Local<String> property, AccessorInfo& info,
+ // AccessorGetterCallback callback)
+ PROFILING_GETTER_CALL_NEW
+ };
+
+ static void SetUp();
+ static void InitializeMathExpData();
+ static void TearDownMathExpData();
+
+ typedef void* ExternalReferenceRedirector(void* original, Type type);
+
+ ExternalReference() : address_(NULL) {}
+
+ ExternalReference(Builtins::CFunctionId id, Isolate* isolate);
+
+ ExternalReference(ApiFunction* ptr, Type type, Isolate* isolate);
+
+ ExternalReference(Builtins::Name name, Isolate* isolate);
+
+ ExternalReference(Runtime::FunctionId id, Isolate* isolate);
+
+ ExternalReference(const Runtime::Function* f, Isolate* isolate);
+
+ ExternalReference(const IC_Utility& ic_utility, Isolate* isolate);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ ExternalReference(const Debug_Address& debug_address, Isolate* isolate);
+#endif
+
+ explicit ExternalReference(StatsCounter* counter);
+
+ ExternalReference(Isolate::AddressId id, Isolate* isolate);
+
+ explicit ExternalReference(const SCTableReference& table_ref);
+
+ // Isolate::Current() as an external reference.
+ static ExternalReference isolate_address(Isolate* isolate);
+
+ // One-of-a-kind references. These references are not part of a general
+ // 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);
+ static ExternalReference random_uint32_function(Isolate* isolate);
+ static ExternalReference transcendental_cache_array_address(Isolate* isolate);
+ static ExternalReference delete_handle_scope_extensions(Isolate* isolate);
+
+ static ExternalReference get_date_field_function(Isolate* isolate);
+ static ExternalReference date_cache_stamp(Isolate* isolate);
+
+ static ExternalReference get_make_code_young_function(Isolate* isolate);
+
+ // Deoptimization support.
+ static ExternalReference new_deoptimizer_function(Isolate* isolate);
+ static ExternalReference compute_output_frames_function(Isolate* isolate);
+
+ // Log support.
+ static ExternalReference log_enter_external_function(Isolate* isolate);
+ static ExternalReference log_leave_external_function(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 Heap::roots_array_start()
+ static ExternalReference roots_array_start(Isolate* isolate);
+
+ // Static variable Heap::allocation_sites_list_address()
+ static ExternalReference allocation_sites_list_address(Isolate* isolate);
+
+ // Static variable StackGuard::address_of_jslimit()
+ static ExternalReference address_of_stack_limit(Isolate* isolate);
+
+ // Static variable StackGuard::address_of_real_jslimit()
+ static ExternalReference address_of_real_stack_limit(Isolate* isolate);
+
+ // Static variable RegExpStack::limit_address()
+ static ExternalReference address_of_regexp_stack_limit(Isolate* isolate);
+
+ // Static variables for RegExp.
+ static ExternalReference address_of_static_offsets_vector(Isolate* isolate);
+ static ExternalReference address_of_regexp_stack_memory_address(
+ Isolate* isolate);
+ static ExternalReference address_of_regexp_stack_memory_size(
+ Isolate* isolate);
+
+ // Static variable Heap::NewSpaceStart()
+ 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);
+ static ExternalReference new_space_allocation_limit_address(Isolate* isolate);
+ static ExternalReference old_pointer_space_allocation_top_address(
+ Isolate* isolate);
+ static ExternalReference old_pointer_space_allocation_limit_address(
+ Isolate* isolate);
+ static ExternalReference old_data_space_allocation_top_address(
+ Isolate* isolate);
+ static ExternalReference old_data_space_allocation_limit_address(
+ Isolate* isolate);
+ static ExternalReference new_space_high_promotion_mode_active_address(
+ Isolate* isolate);
+
+ static ExternalReference double_fp_operation(Token::Value operation,
+ Isolate* isolate);
+ static ExternalReference compare_doubles(Isolate* isolate);
+ static ExternalReference power_double_double_function(Isolate* isolate);
+ static ExternalReference power_double_int_function(Isolate* isolate);
+
+ static ExternalReference handle_scope_next_address(Isolate* isolate);
+ static ExternalReference handle_scope_limit_address(Isolate* isolate);
+ static ExternalReference handle_scope_level_address(Isolate* isolate);
+
+ static ExternalReference scheduled_exception_address(Isolate* isolate);
+ static ExternalReference address_of_pending_message_obj(Isolate* isolate);
+ static ExternalReference address_of_has_pending_message(Isolate* isolate);
+ static ExternalReference address_of_pending_message_script(Isolate* isolate);
+
+ // Static variables containing common double constants.
+ static ExternalReference address_of_min_int();
+ static ExternalReference address_of_one_half();
+ static ExternalReference address_of_minus_one_half();
+ static ExternalReference address_of_minus_zero();
+ static ExternalReference address_of_zero();
+ static ExternalReference address_of_uint8_max_value();
+ static ExternalReference address_of_negative_infinity();
+ static ExternalReference address_of_canonical_non_hole_nan();
+ static ExternalReference address_of_the_hole_nan();
+
+ 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);
+
+ static ExternalReference math_exp_constants(int constant_index);
+ static ExternalReference math_exp_log_table();
+
+ static ExternalReference page_flags(Page* page);
+
+ static ExternalReference ForDeoptEntry(Address entry);
+
+ static ExternalReference cpu_features();
+
+ Address address() const { return reinterpret_cast<Address>(address_); }
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Function Debug::Break()
+ static ExternalReference debug_break(Isolate* isolate);
+
+ // Used to check if single stepping is enabled in generated code.
+ static ExternalReference debug_step_in_fp_address(Isolate* isolate);
+#endif
+
+#ifndef V8_INTERPRETED_REGEXP
+ // C functions called from RegExp generated code.
+
+ // Function NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16()
+ static ExternalReference re_case_insensitive_compare_uc16(Isolate* isolate);
+
+ // Function RegExpMacroAssembler*::CheckStackGuardState()
+ static ExternalReference re_check_stack_guard_state(Isolate* isolate);
+
+ // Function NativeRegExpMacroAssembler::GrowStack()
+ static ExternalReference re_grow_stack(Isolate* isolate);
+
+ // byte NativeRegExpMacroAssembler::word_character_bitmap
+ static ExternalReference re_word_character_map();
+
+#endif
+
+ // This lets you register a function that rewrites all external references.
+ // Used by the ARM simulator to catch calls to external references.
+ static void set_redirector(Isolate* isolate,
+ ExternalReferenceRedirector* redirector) {
+ // We can't stack them.
+ ASSERT(isolate->external_reference_redirector() == NULL);
+ isolate->set_external_reference_redirector(
+ reinterpret_cast<ExternalReferenceRedirectorPointer*>(redirector));
+ }
+
+ static ExternalReference stress_deopt_count(Isolate* isolate);
+
+ bool operator==(const ExternalReference& other) const {
+ return address_ == other.address_;
+ }
+
+ bool operator!=(const ExternalReference& other) const {
+ return !(*this == other);
+ }
+
+ private:
+ explicit ExternalReference(void* address)
+ : address_(address) {}
+
+ static void* Redirect(Isolate* isolate,
+ void* address,
+ Type type = ExternalReference::BUILTIN_CALL) {
+ ExternalReferenceRedirector* redirector =
+ reinterpret_cast<ExternalReferenceRedirector*>(
+ isolate->external_reference_redirector());
+ if (redirector == NULL) return address;
+ void* answer = (*redirector)(address, type);
+ return answer;
+ }
+
+ static void* Redirect(Isolate* isolate,
+ Address address_arg,
+ Type type = ExternalReference::BUILTIN_CALL) {
+ ExternalReferenceRedirector* redirector =
+ reinterpret_cast<ExternalReferenceRedirector*>(
+ isolate->external_reference_redirector());
+ void* address = reinterpret_cast<void*>(address_arg);
+ void* answer = (redirector == NULL) ?
+ address :
+ (*redirector)(address, type);
+ return answer;
+ }
+
+ void* address_;
+};
+
+
+// -----------------------------------------------------------------------------
+// Position recording support
+
+struct PositionState {
+ PositionState() : current_position(RelocInfo::kNoPosition),
+ written_position(RelocInfo::kNoPosition),
+ current_statement_position(RelocInfo::kNoPosition),
+ written_statement_position(RelocInfo::kNoPosition) {}
+
+ int current_position;
+ int written_position;
+
+ int current_statement_position;
+ int written_statement_position;
+};
+
+
+class PositionsRecorder BASE_EMBEDDED {
+ public:
+ explicit PositionsRecorder(Assembler* assembler)
+ : assembler_(assembler) {
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ gdbjit_lineinfo_ = NULL;
+#endif
+ jit_handler_data_ = NULL;
+ }
+
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ ~PositionsRecorder() {
+ delete gdbjit_lineinfo_;
+ }
+
+ void StartGDBJITLineInfoRecording() {
+ if (FLAG_gdbjit) {
+ gdbjit_lineinfo_ = new GDBJITLineInfo();
+ }
+ }
+
+ GDBJITLineInfo* DetachGDBJITLineInfo() {
+ GDBJITLineInfo* lineinfo = gdbjit_lineinfo_;
+ gdbjit_lineinfo_ = NULL; // To prevent deallocation in destructor.
+ return lineinfo;
+ }
+#endif
+ void AttachJITHandlerData(void* user_data) {
+ jit_handler_data_ = user_data;
+ }
+
+ void* DetachJITHandlerData() {
+ void* old_data = jit_handler_data_;
+ jit_handler_data_ = NULL;
+ return old_data;
+ }
+ // Set current position to pos.
+ void RecordPosition(int pos);
+
+ // Set current statement position to pos.
+ void RecordStatementPosition(int pos);
+
+ // Write recorded positions to relocation information.
+ bool WriteRecordedPositions();
+
+ int current_position() const { return state_.current_position; }
+
+ int current_statement_position() const {
+ return state_.current_statement_position;
+ }
+
+ private:
+ Assembler* assembler_;
+ PositionState state_;
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ GDBJITLineInfo* gdbjit_lineinfo_;
+#endif
+
+ // Currently jit_handler_data_ is used to store JITHandler-specific data
+ // over the lifetime of a PositionsRecorder
+ void* jit_handler_data_;
+ friend class PreservePositionScope;
+
+ DISALLOW_COPY_AND_ASSIGN(PositionsRecorder);
+};
+
+
+class PreservePositionScope BASE_EMBEDDED {
+ public:
+ explicit PreservePositionScope(PositionsRecorder* positions_recorder)
+ : positions_recorder_(positions_recorder),
+ saved_state_(positions_recorder->state_) {}
+
+ ~PreservePositionScope() {
+ positions_recorder_->state_ = saved_state_;
+ }
+
+ private:
+ PositionsRecorder* positions_recorder_;
+ const PositionState saved_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(PreservePositionScope);
+};
+
+
+// -----------------------------------------------------------------------------
+// Utility functions
+
+inline bool is_intn(int x, int n) {
+ return -(1 << (n-1)) <= x && x < (1 << (n-1));
+}
+
+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); }
+
+inline bool is_uintn(int x, int n) {
+ return (x & -(1 << n)) == 0;
+}
+
+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;
+ }
+ 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_helper(double x, double y);
+double power_double_int(double x, int y);
+double power_double_double(double x, double y);
+
+// Helper class for generating code or data associated with the code
+// right after a call instruction. As an example this can be used to
+// generate safepoint data after calls for crankshaft.
+class CallWrapper {
+ public:
+ CallWrapper() { }
+ virtual ~CallWrapper() { }
+ // Called just before emitting a call. Argument is the size of the generated
+ // call code.
+ virtual void BeforeCall(int call_size) const = 0;
+ // Called just after emitting a call, i.e., at the return site for the call.
+ virtual void AfterCall() const = 0;
+};
+
+class NullCallWrapper : public CallWrapper {
+ public:
+ NullCallWrapper() { }
+ virtual ~NullCallWrapper() { }
+ virtual void BeforeCall(int call_size) const { }
+ virtual void AfterCall() const { }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_ASSEMBLER_H_
diff --git a/chromium/v8/src/assert-scope.h b/chromium/v8/src/assert-scope.h
new file mode 100644
index 00000000000..269b280d027
--- /dev/null
+++ b/chromium/v8/src/assert-scope.h
@@ -0,0 +1,184 @@
+// Copyright 2013 the V8 project authors. 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_ASSERT_SCOPE_H_
+#define V8_ASSERT_SCOPE_H_
+
+#include "allocation.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+class Isolate;
+
+enum PerThreadAssertType {
+ HEAP_ALLOCATION_ASSERT,
+ HANDLE_ALLOCATION_ASSERT,
+ HANDLE_DEREFERENCE_ASSERT,
+ DEFERRED_HANDLE_DEREFERENCE_ASSERT,
+ CODE_DEPENDENCY_CHANGE_ASSERT,
+ LAST_PER_THREAD_ASSERT_TYPE
+};
+
+
+#ifdef DEBUG
+class PerThreadAssertData {
+ public:
+ PerThreadAssertData() : nesting_level_(0) {
+ for (int i = 0; i < LAST_PER_THREAD_ASSERT_TYPE; i++) {
+ assert_states_[i] = true;
+ }
+ }
+
+ void set(PerThreadAssertType type, bool allow) {
+ assert_states_[type] = allow;
+ }
+
+ bool get(PerThreadAssertType type) const {
+ return assert_states_[type];
+ }
+
+ void increment_level() { ++nesting_level_; }
+ bool decrement_level() { return --nesting_level_ == 0; }
+
+ private:
+ bool assert_states_[LAST_PER_THREAD_ASSERT_TYPE];
+ int nesting_level_;
+
+ DISALLOW_COPY_AND_ASSIGN(PerThreadAssertData);
+};
+#endif // DEBUG
+
+
+class PerThreadAssertScopeBase {
+#ifdef DEBUG
+
+ protected:
+ PerThreadAssertScopeBase() {
+ data_ = GetAssertData();
+ if (data_ == NULL) {
+ data_ = new PerThreadAssertData();
+ SetThreadLocalData(data_);
+ }
+ data_->increment_level();
+ }
+
+ ~PerThreadAssertScopeBase() {
+ if (!data_->decrement_level()) return;
+ for (int i = 0; i < LAST_PER_THREAD_ASSERT_TYPE; i++) {
+ ASSERT(data_->get(static_cast<PerThreadAssertType>(i)));
+ }
+ delete data_;
+ SetThreadLocalData(NULL);
+ }
+
+ static PerThreadAssertData* GetAssertData() {
+ return reinterpret_cast<PerThreadAssertData*>(
+ Thread::GetThreadLocal(thread_local_key));
+ }
+
+ static Thread::LocalStorageKey thread_local_key;
+ PerThreadAssertData* data_;
+ friend class Isolate;
+
+ private:
+ static void SetThreadLocalData(PerThreadAssertData* data) {
+ Thread::SetThreadLocal(thread_local_key, data);
+ }
+#endif // DEBUG
+};
+
+
+
+template <PerThreadAssertType type, bool allow>
+class PerThreadAssertScope : public PerThreadAssertScopeBase {
+ public:
+#ifndef DEBUG
+ PerThreadAssertScope() { }
+ static void SetIsAllowed(bool is_allowed) { }
+#else
+ PerThreadAssertScope() {
+ old_state_ = data_->get(type);
+ data_->set(type, allow);
+ }
+
+ ~PerThreadAssertScope() { data_->set(type, old_state_); }
+
+ static bool IsAllowed() {
+ PerThreadAssertData* data = GetAssertData();
+ return data == NULL || data->get(type);
+ }
+
+ private:
+ bool old_state_;
+#endif
+};
+
+// Scope to document where we do not expect handles to be created.
+typedef PerThreadAssertScope<HANDLE_ALLOCATION_ASSERT, false>
+ DisallowHandleAllocation;
+
+// Scope to introduce an exception to DisallowHandleAllocation.
+typedef PerThreadAssertScope<HANDLE_ALLOCATION_ASSERT, true>
+ AllowHandleAllocation;
+
+// Scope to document where we do not expect any allocation and GC.
+typedef PerThreadAssertScope<HEAP_ALLOCATION_ASSERT, false>
+ DisallowHeapAllocation;
+
+// Scope to introduce an exception to DisallowHeapAllocation.
+typedef PerThreadAssertScope<HEAP_ALLOCATION_ASSERT, true>
+ AllowHeapAllocation;
+
+// Scope to document where we do not expect any handle dereferences.
+typedef PerThreadAssertScope<HANDLE_DEREFERENCE_ASSERT, false>
+ DisallowHandleDereference;
+
+// Scope to introduce an exception to DisallowHandleDereference.
+typedef PerThreadAssertScope<HANDLE_DEREFERENCE_ASSERT, true>
+ AllowHandleDereference;
+
+// Scope to document where we do not expect deferred handles to be dereferenced.
+typedef PerThreadAssertScope<DEFERRED_HANDLE_DEREFERENCE_ASSERT, false>
+ DisallowDeferredHandleDereference;
+
+// Scope to introduce an exception to DisallowDeferredHandleDereference.
+typedef PerThreadAssertScope<DEFERRED_HANDLE_DEREFERENCE_ASSERT, true>
+ AllowDeferredHandleDereference;
+
+// Scope to document where we do not expect deferred handles to be dereferenced.
+typedef PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, false>
+ DisallowCodeDependencyChange;
+
+// Scope to introduce an exception to DisallowDeferredHandleDereference.
+typedef PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, true>
+ AllowCodeDependencyChange;
+
+} } // namespace v8::internal
+
+#endif // V8_ASSERT_SCOPE_H_
diff --git a/chromium/v8/src/ast.cc b/chromium/v8/src/ast.cc
new file mode 100644
index 00000000000..23b680d47f4
--- /dev/null
+++ b/chromium/v8/src/ast.cc
@@ -0,0 +1,1199 @@
+// Copyright 2012 the V8 project authors. 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 "ast.h"
+
+#include <cmath> // For isfinite.
+#include "builtins.h"
+#include "code-stubs.h"
+#include "contexts.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"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// All the Accept member functions for each syntax tree node type.
+
+#define DECL_ACCEPT(type) \
+ void type::Accept(AstVisitor* v) { v->Visit##type(this); }
+AST_NODE_LIST(DECL_ACCEPT)
+#undef DECL_ACCEPT
+
+
+// ----------------------------------------------------------------------------
+// Implementation of other node functionality.
+
+
+bool Expression::IsSmiLiteral() {
+ return AsLiteral() != NULL && AsLiteral()->value()->IsSmi();
+}
+
+
+bool Expression::IsStringLiteral() {
+ return AsLiteral() != NULL && AsLiteral()->value()->IsString();
+}
+
+
+bool Expression::IsNullLiteral() {
+ return AsLiteral() != NULL && AsLiteral()->value()->IsNull();
+}
+
+
+bool Expression::IsUndefinedLiteral(Isolate* isolate) {
+ VariableProxy* var_proxy = AsVariableProxy();
+ if (var_proxy == NULL) return false;
+ Variable* var = var_proxy->var();
+ // The global identifier "undefined" is immutable. Everything
+ // else could be reassigned.
+ return var != NULL && var->location() == Variable::UNALLOCATED &&
+ var_proxy->name()->Equals(isolate->heap()->undefined_string());
+}
+
+
+VariableProxy::VariableProxy(Isolate* isolate, Variable* var)
+ : Expression(isolate),
+ name_(var->name()),
+ var_(NULL), // Will be set by the call to BindTo.
+ is_this_(var->is_this()),
+ is_trivial_(false),
+ is_lvalue_(false),
+ position_(RelocInfo::kNoPosition),
+ interface_(var->interface()) {
+ BindTo(var);
+}
+
+
+VariableProxy::VariableProxy(Isolate* isolate,
+ Handle<String> name,
+ bool is_this,
+ Interface* interface,
+ int position)
+ : Expression(isolate),
+ name_(name),
+ var_(NULL),
+ is_this_(is_this),
+ is_trivial_(false),
+ is_lvalue_(false),
+ position_(position),
+ interface_(interface) {
+ // Names must be canonicalized for fast equality checks.
+ ASSERT(name->IsInternalizedString());
+}
+
+
+void VariableProxy::BindTo(Variable* var) {
+ ASSERT(var_ == NULL); // must be bound only once
+ ASSERT(var != NULL); // must bind
+ ASSERT(!FLAG_harmony_modules || interface_->IsUnified(var->interface()));
+ ASSERT((is_this() && var->is_this()) || name_.is_identical_to(var->name()));
+ // Ideally CONST-ness should match. However, this is very hard to achieve
+ // because we don't know the exact semantics of conflicting (const and
+ // non-const) multiple variable declarations, const vars introduced via
+ // eval() etc. Const-ness and variable declarations are a complete mess
+ // in JS. Sigh...
+ var_ = var;
+ var->set_is_used(true);
+}
+
+
+Assignment::Assignment(Isolate* isolate,
+ Token::Value op,
+ Expression* target,
+ Expression* value,
+ int pos)
+ : Expression(isolate),
+ op_(op),
+ target_(target),
+ value_(value),
+ pos_(pos),
+ binary_operation_(NULL),
+ assignment_id_(GetNextId(isolate)),
+ is_monomorphic_(false),
+ is_uninitialized_(false),
+ store_mode_(STANDARD_STORE) { }
+
+
+Token::Value Assignment::binary_op() const {
+ switch (op_) {
+ case Token::ASSIGN_BIT_OR: return Token::BIT_OR;
+ case Token::ASSIGN_BIT_XOR: return Token::BIT_XOR;
+ case Token::ASSIGN_BIT_AND: return Token::BIT_AND;
+ case Token::ASSIGN_SHL: return Token::SHL;
+ case Token::ASSIGN_SAR: return Token::SAR;
+ case Token::ASSIGN_SHR: return Token::SHR;
+ case Token::ASSIGN_ADD: return Token::ADD;
+ case Token::ASSIGN_SUB: return Token::SUB;
+ case Token::ASSIGN_MUL: return Token::MUL;
+ case Token::ASSIGN_DIV: return Token::DIV;
+ case Token::ASSIGN_MOD: return Token::MOD;
+ default: UNREACHABLE();
+ }
+ return Token::ILLEGAL;
+}
+
+
+bool FunctionLiteral::AllowsLazyCompilation() {
+ return scope()->AllowsLazyCompilation();
+}
+
+
+bool FunctionLiteral::AllowsLazyCompilationWithoutContext() {
+ return scope()->AllowsLazyCompilationWithoutContext();
+}
+
+
+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();
+}
+
+
+ObjectLiteralProperty::ObjectLiteralProperty(Literal* key,
+ Expression* value,
+ Isolate* isolate) {
+ emit_store_ = true;
+ key_ = key;
+ value_ = value;
+ Object* k = *key->value();
+ if (k->IsInternalizedString() &&
+ isolate->heap()->proto_string()->Equals(String::cast(k))) {
+ kind_ = PROTOTYPE;
+ } else if (value_->AsMaterializedLiteral() != NULL) {
+ kind_ = MATERIALIZED_LITERAL;
+ } else if (value_->AsLiteral() != NULL) {
+ kind_ = CONSTANT;
+ } else {
+ kind_ = COMPUTED;
+ }
+}
+
+
+ObjectLiteralProperty::ObjectLiteralProperty(bool is_getter,
+ FunctionLiteral* value) {
+ emit_store_ = true;
+ value_ = value;
+ kind_ = is_getter ? GETTER : SETTER;
+}
+
+
+bool ObjectLiteral::Property::IsCompileTimeValue() {
+ return kind_ == CONSTANT ||
+ (kind_ == MATERIALIZED_LITERAL &&
+ CompileTimeValue::IsCompileTimeValue(value_));
+}
+
+
+void ObjectLiteral::Property::set_emit_store(bool emit_store) {
+ emit_store_ = emit_store;
+}
+
+
+bool ObjectLiteral::Property::emit_store() {
+ return emit_store_;
+}
+
+
+bool IsEqualString(void* first, void* second) {
+ ASSERT((*reinterpret_cast<String**>(first))->IsString());
+ ASSERT((*reinterpret_cast<String**>(second))->IsString());
+ Handle<String> h1(reinterpret_cast<String**>(first));
+ Handle<String> h2(reinterpret_cast<String**>(second));
+ return (*h1)->Equals(*h2);
+}
+
+
+bool IsEqualNumber(void* first, void* second) {
+ ASSERT((*reinterpret_cast<Object**>(first))->IsNumber());
+ ASSERT((*reinterpret_cast<Object**>(second))->IsNumber());
+
+ Handle<Object> h1(reinterpret_cast<Object**>(first));
+ Handle<Object> h2(reinterpret_cast<Object**>(second));
+ if (h1->IsSmi()) {
+ return h2->IsSmi() && *h1 == *h2;
+ }
+ if (h2->IsSmi()) return false;
+ Handle<HeapNumber> n1 = Handle<HeapNumber>::cast(h1);
+ Handle<HeapNumber> n2 = Handle<HeapNumber>::cast(h2);
+ ASSERT(std::isfinite(n1->value()));
+ ASSERT(std::isfinite(n2->value()));
+ return n1->value() == n2->value();
+}
+
+
+void ObjectLiteral::CalculateEmitStore(Zone* zone) {
+ ZoneAllocationPolicy allocator(zone);
+
+ ZoneHashMap table(Literal::Match, ZoneHashMap::kDefaultHashMapCapacity,
+ allocator);
+ for (int i = properties()->length() - 1; i >= 0; i--) {
+ ObjectLiteral::Property* property = properties()->at(i);
+ Literal* literal = property->key();
+ if (literal->value()->IsNull()) continue;
+ uint32_t hash = literal->Hash();
+ // If the key of a computed property is in the table, do not emit
+ // a store for the property later.
+ if ((property->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL ||
+ property->kind() == ObjectLiteral::Property::COMPUTED) &&
+ table.Lookup(literal, hash, false, allocator) != NULL) {
+ property->set_emit_store(false);
+ } else {
+ // Add key to the table.
+ table.Lookup(literal, hash, true, allocator);
+ }
+ }
+}
+
+
+void TargetCollector::AddTarget(Label* target, Zone* zone) {
+ // Add the label to the collector, but discard duplicates.
+ int length = targets_.length();
+ for (int i = 0; i < length; i++) {
+ if (targets_[i] == target) return;
+ }
+ targets_.Add(target, zone);
+}
+
+
+void UnaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) {
+ // TODO(olivf) If this Operation is used in a test context, then the
+ // expression has a ToBoolean stub and we want to collect the type
+ // information. However the GraphBuilder expects it to be on the instruction
+ // corresponding to the TestContext, therefore we have to store it here and
+ // not on the operand.
+ set_to_boolean_types(oracle->ToBooleanTypes(expression()->test_id()));
+}
+
+
+void BinaryOperation::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) {
+ // TODO(olivf) If this Operation is used in a test context, then the right
+ // hand side has a ToBoolean stub and we want to collect the type information.
+ // However the GraphBuilder expects it to be on the instruction corresponding
+ // to the TestContext, therefore we have to store it here and not on the
+ // right hand operand.
+ set_to_boolean_types(oracle->ToBooleanTypes(right()->test_id()));
+}
+
+
+bool BinaryOperation::ResultOverwriteAllowed() {
+ switch (op_) {
+ case Token::COMMA:
+ case Token::OR:
+ case Token::AND:
+ return false;
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SHL:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ return true;
+ default:
+ UNREACHABLE();
+ }
+ return false;
+}
+
+
+static bool IsTypeof(Expression* expr) {
+ UnaryOperation* maybe_unary = expr->AsUnaryOperation();
+ return maybe_unary != NULL && maybe_unary->op() == Token::TYPEOF;
+}
+
+
+// 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()->value());
+ return true;
+ }
+ return false;
+}
+
+
+bool CompareOperation::IsLiteralCompareTypeof(Expression** expr,
+ Handle<String>* check) {
+ return MatchLiteralCompareTypeof(left_, op_, right_, expr, check) ||
+ MatchLiteralCompareTypeof(right_, op_, left_, expr, check);
+}
+
+
+static bool IsVoidOfLiteral(Expression* expr) {
+ UnaryOperation* maybe_unary = expr->AsUnaryOperation();
+ return maybe_unary != NULL &&
+ maybe_unary->op() == Token::VOID &&
+ maybe_unary->expression()->AsLiteral() != NULL;
+}
+
+
+// Check for the pattern: void <literal> equals <expression> or
+// undefined equals <expression>
+static bool MatchLiteralCompareUndefined(Expression* left,
+ Token::Value op,
+ Expression* right,
+ Expression** expr,
+ Isolate* isolate) {
+ if (IsVoidOfLiteral(left) && Token::IsEqualityOp(op)) {
+ *expr = right;
+ return true;
+ }
+ if (left->IsUndefinedLiteral(isolate) && Token::IsEqualityOp(op)) {
+ *expr = right;
+ return true;
+ }
+ return false;
+}
+
+
+bool CompareOperation::IsLiteralCompareUndefined(
+ Expression** expr, Isolate* isolate) {
+ return MatchLiteralCompareUndefined(left_, op_, right_, expr, isolate) ||
+ MatchLiteralCompareUndefined(right_, op_, left_, expr, isolate);
+}
+
+
+// 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 false;
+}
+
+
+bool CompareOperation::IsLiteralCompareNull(Expression** expr) {
+ return MatchLiteralCompareNull(left_, op_, right_, expr) ||
+ MatchLiteralCompareNull(right_, op_, left_, expr);
+}
+
+
+// ----------------------------------------------------------------------------
+// Inlining support
+
+bool Declaration::IsInlineable() const {
+ return proxy()->var()->IsStackAllocated();
+}
+
+bool FunctionDeclaration::IsInlineable() const {
+ return false;
+}
+
+
+// ----------------------------------------------------------------------------
+// Recording of type feedback
+
+// TODO(rossberg): all RecordTypeFeedback functions should disappear
+// once we use the common type field in the AST consistently.
+
+
+void ForInStatement::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
+ for_in_type_ = static_cast<ForInType>(oracle->ForInType(this));
+}
+
+
+void Expression::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) {
+ to_boolean_types_ = oracle->ToBooleanTypes(test_id());
+}
+
+
+void Property::RecordTypeFeedback(TypeFeedbackOracle* oracle,
+ Zone* zone) {
+ // Record type feedback from the oracle in the AST.
+ is_uninitialized_ = oracle->LoadIsUninitialized(this);
+ if (is_uninitialized_) return;
+
+ is_monomorphic_ = oracle->LoadIsMonomorphicNormal(this);
+ receiver_types_.Clear();
+ if (key()->IsPropertyName()) {
+ FunctionPrototypeStub proto_stub(Code::LOAD_IC);
+ StringLengthStub string_stub(Code::LOAD_IC, false);
+ if (oracle->LoadIsStub(this, &string_stub)) {
+ is_string_length_ = true;
+ } else if (oracle->LoadIsStub(this, &proto_stub)) {
+ is_function_prototype_ = true;
+ } else {
+ Literal* lit_key = key()->AsLiteral();
+ ASSERT(lit_key != NULL && lit_key->value()->IsString());
+ Handle<String> name = Handle<String>::cast(lit_key->value());
+ oracle->LoadReceiverTypes(this, name, &receiver_types_);
+ }
+ } else if (oracle->LoadIsBuiltin(this, Builtins::kKeyedLoadIC_String)) {
+ is_string_access_ = true;
+ } else if (is_monomorphic_) {
+ receiver_types_.Add(oracle->LoadMonomorphicReceiverType(this),
+ zone);
+ } else if (oracle->LoadIsPolymorphic(this)) {
+ receiver_types_.Reserve(kMaxKeyedPolymorphism, zone);
+ oracle->CollectKeyedReceiverTypes(PropertyFeedbackId(), &receiver_types_);
+ }
+}
+
+
+void Assignment::RecordTypeFeedback(TypeFeedbackOracle* oracle,
+ Zone* zone) {
+ Property* prop = target()->AsProperty();
+ ASSERT(prop != NULL);
+ TypeFeedbackId id = AssignmentFeedbackId();
+ is_uninitialized_ = oracle->StoreIsUninitialized(id);
+ if (is_uninitialized_) return;
+ is_monomorphic_ = oracle->StoreIsMonomorphicNormal(id);
+ receiver_types_.Clear();
+ if (prop->key()->IsPropertyName()) {
+ Literal* lit_key = prop->key()->AsLiteral();
+ ASSERT(lit_key != NULL && lit_key->value()->IsString());
+ Handle<String> name = Handle<String>::cast(lit_key->value());
+ oracle->StoreReceiverTypes(this, name, &receiver_types_);
+ } else if (is_monomorphic_) {
+ // Record receiver type for monomorphic keyed stores.
+ receiver_types_.Add(oracle->StoreMonomorphicReceiverType(id), zone);
+ store_mode_ = oracle->GetStoreMode(id);
+ } else if (oracle->StoreIsKeyedPolymorphic(id)) {
+ receiver_types_.Reserve(kMaxKeyedPolymorphism, zone);
+ oracle->CollectKeyedReceiverTypes(id, &receiver_types_);
+ store_mode_ = oracle->GetStoreMode(id);
+ }
+}
+
+
+void CountOperation::RecordTypeFeedback(TypeFeedbackOracle* oracle,
+ Zone* zone) {
+ TypeFeedbackId id = CountStoreFeedbackId();
+ is_monomorphic_ = oracle->StoreIsMonomorphicNormal(id);
+ receiver_types_.Clear();
+ if (is_monomorphic_) {
+ // Record receiver type for monomorphic keyed stores.
+ receiver_types_.Add(
+ oracle->StoreMonomorphicReceiverType(id), zone);
+ } else if (oracle->StoreIsKeyedPolymorphic(id)) {
+ receiver_types_.Reserve(kMaxKeyedPolymorphism, zone);
+ oracle->CollectKeyedReceiverTypes(id, &receiver_types_);
+ } else {
+ oracle->CollectPolymorphicStoreReceiverTypes(id, &receiver_types_);
+ }
+ store_mode_ = oracle->GetStoreMode(id);
+ type_ = oracle->IncrementType(this);
+}
+
+
+void CaseClause::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
+ compare_type_ = oracle->ClauseType(CompareId());
+}
+
+
+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.
+ holder_ = Handle<JSObject>::null();
+ }
+ LookupResult lookup(type->GetIsolate());
+ while (true) {
+ // If a dictionary map is found in the prototype chain before the actual
+ // target, a new target can always appear. In that case, bail out.
+ // TODO(verwaest): Alternatively a runtime negative lookup on the normal
+ // receiver or prototype could be added.
+ if (type->is_dictionary_map()) return false;
+ type->LookupDescriptor(NULL, *name, &lookup);
+ if (lookup.IsFound()) {
+ switch (lookup.type()) {
+ case CONSTANT: {
+ // We surely know the target for a constant function.
+ Handle<Object> constant(lookup.GetConstantFromMap(*type),
+ type->GetIsolate());
+ if (constant->IsJSFunction()) {
+ target_ = Handle<JSFunction>::cast(constant);
+ return true;
+ }
+ // Fall through.
+ }
+ case NORMAL:
+ case FIELD:
+ case CALLBACKS:
+ case HANDLER:
+ case INTERCEPTOR:
+ // We don't know the target.
+ return false;
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+ // 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());
+ }
+}
+
+
+bool Call::ComputeGlobalTarget(Handle<GlobalObject> global,
+ LookupResult* lookup) {
+ target_ = Handle<JSFunction>::null();
+ cell_ = Handle<Cell>::null();
+ ASSERT(lookup->IsFound() &&
+ lookup->type() == NORMAL &&
+ lookup->holder() == *global);
+ cell_ = Handle<Cell>(global->GetPropertyCell(lookup));
+ if (cell_->value()->IsJSFunction()) {
+ 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)) {
+ target_ = candidate;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+Handle<JSObject> Call::GetPrototypeForPrimitiveCheck(
+ CheckType check, Isolate* isolate) {
+ v8::internal::Context* native_context = isolate->context()->native_context();
+ JSFunction* function = NULL;
+ switch (check) {
+ case RECEIVER_MAP_CHECK:
+ UNREACHABLE();
+ break;
+ case STRING_CHECK:
+ function = native_context->string_function();
+ break;
+ case SYMBOL_CHECK:
+ function = native_context->symbol_function();
+ break;
+ case NUMBER_CHECK:
+ function = native_context->number_function();
+ break;
+ case BOOLEAN_CHECK:
+ function = native_context->boolean_function();
+ break;
+ }
+ ASSERT(function != NULL);
+ return Handle<JSObject>(JSObject::cast(function->instance_prototype()));
+}
+
+
+void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle,
+ CallKind call_kind) {
+ is_monomorphic_ = oracle->CallIsMonomorphic(this);
+ Property* property = expression()->AsProperty();
+ 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->value()->IsString());
+ Handle<String> name = Handle<String>::cast(key->value());
+ 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);
+ }
+ }
+#endif
+ 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_ = GetPrototypeForPrimitiveCheck(check_type_, oracle->isolate());
+ map = Handle<Map>(holder_->map());
+ }
+ is_monomorphic_ = ComputeTarget(map, name);
+ }
+ }
+}
+
+
+void CallNew::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
+ allocation_info_cell_ = oracle->GetCallNewAllocationInfoCell(this);
+ is_monomorphic_ = oracle->CallNewIsMonomorphic(this);
+ if (is_monomorphic_) {
+ target_ = oracle->GetCallNewTarget(this);
+ Object* value = allocation_info_cell_->value();
+ ASSERT(!value->IsTheHole());
+ if (value->IsAllocationSite()) {
+ AllocationSite* site = AllocationSite::cast(value);
+ elements_kind_ = site->GetElementsKind();
+ }
+ }
+}
+
+
+void ObjectLiteral::Property::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
+ receiver_type_ = oracle->ObjectLiteralStoreIsMonomorphic(this)
+ ? oracle->GetObjectLiteralStoreMap(this)
+ : Handle<Map>::null();
+}
+
+
+// ----------------------------------------------------------------------------
+// Implementation of AstVisitor
+
+void AstVisitor::VisitDeclarations(ZoneList<Declaration*>* declarations) {
+ for (int i = 0; i < declarations->length(); i++) {
+ Visit(declarations->at(i));
+ }
+}
+
+
+void AstVisitor::VisitStatements(ZoneList<Statement*>* statements) {
+ for (int i = 0; i < statements->length(); i++) {
+ Visit(statements->at(i));
+ }
+}
+
+
+void AstVisitor::VisitExpressions(ZoneList<Expression*>* expressions) {
+ for (int i = 0; i < expressions->length(); i++) {
+ // The variable statement visiting code may pass NULL expressions
+ // to this code. Maybe this should be handled by introducing an
+ // undefined expression or literal? Revisit this code if this
+ // changes
+ Expression* expression = expressions->at(i);
+ if (expression != NULL) Visit(expression);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// Regular expressions
+
+#define MAKE_ACCEPT(Name) \
+ void* RegExp##Name::Accept(RegExpVisitor* visitor, void* data) { \
+ return visitor->Visit##Name(this, data); \
+ }
+FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ACCEPT)
+#undef MAKE_ACCEPT
+
+#define MAKE_TYPE_CASE(Name) \
+ RegExp##Name* RegExpTree::As##Name() { \
+ return NULL; \
+ } \
+ bool RegExpTree::Is##Name() { return false; }
+FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
+#undef MAKE_TYPE_CASE
+
+#define MAKE_TYPE_CASE(Name) \
+ RegExp##Name* RegExp##Name::As##Name() { \
+ return this; \
+ } \
+ bool RegExp##Name::Is##Name() { return true; }
+FOR_EACH_REG_EXP_TREE_TYPE(MAKE_TYPE_CASE)
+#undef MAKE_TYPE_CASE
+
+
+static Interval ListCaptureRegisters(ZoneList<RegExpTree*>* children) {
+ Interval result = Interval::Empty();
+ for (int i = 0; i < children->length(); i++)
+ result = result.Union(children->at(i)->CaptureRegisters());
+ return result;
+}
+
+
+Interval RegExpAlternative::CaptureRegisters() {
+ return ListCaptureRegisters(nodes());
+}
+
+
+Interval RegExpDisjunction::CaptureRegisters() {
+ return ListCaptureRegisters(alternatives());
+}
+
+
+Interval RegExpLookahead::CaptureRegisters() {
+ return body()->CaptureRegisters();
+}
+
+
+Interval RegExpCapture::CaptureRegisters() {
+ Interval self(StartRegister(index()), EndRegister(index()));
+ return self.Union(body()->CaptureRegisters());
+}
+
+
+Interval RegExpQuantifier::CaptureRegisters() {
+ return body()->CaptureRegisters();
+}
+
+
+bool RegExpAssertion::IsAnchoredAtStart() {
+ return assertion_type() == RegExpAssertion::START_OF_INPUT;
+}
+
+
+bool RegExpAssertion::IsAnchoredAtEnd() {
+ return assertion_type() == RegExpAssertion::END_OF_INPUT;
+}
+
+
+bool RegExpAlternative::IsAnchoredAtStart() {
+ ZoneList<RegExpTree*>* nodes = this->nodes();
+ for (int i = 0; i < nodes->length(); i++) {
+ RegExpTree* node = nodes->at(i);
+ if (node->IsAnchoredAtStart()) { return true; }
+ if (node->max_match() > 0) { return false; }
+ }
+ return false;
+}
+
+
+bool RegExpAlternative::IsAnchoredAtEnd() {
+ ZoneList<RegExpTree*>* nodes = this->nodes();
+ for (int i = nodes->length() - 1; i >= 0; i--) {
+ RegExpTree* node = nodes->at(i);
+ if (node->IsAnchoredAtEnd()) { return true; }
+ if (node->max_match() > 0) { return false; }
+ }
+ return false;
+}
+
+
+bool RegExpDisjunction::IsAnchoredAtStart() {
+ ZoneList<RegExpTree*>* alternatives = this->alternatives();
+ for (int i = 0; i < alternatives->length(); i++) {
+ if (!alternatives->at(i)->IsAnchoredAtStart())
+ return false;
+ }
+ return true;
+}
+
+
+bool RegExpDisjunction::IsAnchoredAtEnd() {
+ ZoneList<RegExpTree*>* alternatives = this->alternatives();
+ for (int i = 0; i < alternatives->length(); i++) {
+ if (!alternatives->at(i)->IsAnchoredAtEnd())
+ return false;
+ }
+ return true;
+}
+
+
+bool RegExpLookahead::IsAnchoredAtStart() {
+ return is_positive() && body()->IsAnchoredAtStart();
+}
+
+
+bool RegExpCapture::IsAnchoredAtStart() {
+ return body()->IsAnchoredAtStart();
+}
+
+
+bool RegExpCapture::IsAnchoredAtEnd() {
+ return body()->IsAnchoredAtEnd();
+}
+
+
+// Convert regular expression trees to a simple sexp representation.
+// This representation should be different from the input grammar
+// in as many cases as possible, to make it more difficult for incorrect
+// parses to look as correct ones which is likely if the input and
+// output formats are alike.
+class RegExpUnparser: public RegExpVisitor {
+ public:
+ explicit RegExpUnparser(Zone* zone);
+ void VisitCharacterRange(CharacterRange that);
+ SmartArrayPointer<const char> ToString() { return stream_.ToCString(); }
+#define MAKE_CASE(Name) virtual void* Visit##Name(RegExp##Name*, void* data);
+ FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
+#undef MAKE_CASE
+ private:
+ StringStream* stream() { return &stream_; }
+ HeapStringAllocator alloc_;
+ StringStream stream_;
+ Zone* zone_;
+};
+
+
+RegExpUnparser::RegExpUnparser(Zone* zone) : stream_(&alloc_), zone_(zone) {
+}
+
+
+void* RegExpUnparser::VisitDisjunction(RegExpDisjunction* that, void* data) {
+ stream()->Add("(|");
+ for (int i = 0; i < that->alternatives()->length(); i++) {
+ stream()->Add(" ");
+ that->alternatives()->at(i)->Accept(this, data);
+ }
+ stream()->Add(")");
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitAlternative(RegExpAlternative* that, void* data) {
+ stream()->Add("(:");
+ for (int i = 0; i < that->nodes()->length(); i++) {
+ stream()->Add(" ");
+ that->nodes()->at(i)->Accept(this, data);
+ }
+ stream()->Add(")");
+ return NULL;
+}
+
+
+void RegExpUnparser::VisitCharacterRange(CharacterRange that) {
+ stream()->Add("%k", that.from());
+ if (!that.IsSingleton()) {
+ stream()->Add("-%k", that.to());
+ }
+}
+
+
+
+void* RegExpUnparser::VisitCharacterClass(RegExpCharacterClass* that,
+ void* data) {
+ if (that->is_negated())
+ stream()->Add("^");
+ stream()->Add("[");
+ for (int i = 0; i < that->ranges(zone_)->length(); i++) {
+ if (i > 0) stream()->Add(" ");
+ VisitCharacterRange(that->ranges(zone_)->at(i));
+ }
+ stream()->Add("]");
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitAssertion(RegExpAssertion* that, void* data) {
+ switch (that->assertion_type()) {
+ case RegExpAssertion::START_OF_INPUT:
+ stream()->Add("@^i");
+ break;
+ case RegExpAssertion::END_OF_INPUT:
+ stream()->Add("@$i");
+ break;
+ case RegExpAssertion::START_OF_LINE:
+ stream()->Add("@^l");
+ break;
+ case RegExpAssertion::END_OF_LINE:
+ stream()->Add("@$l");
+ break;
+ case RegExpAssertion::BOUNDARY:
+ stream()->Add("@b");
+ break;
+ case RegExpAssertion::NON_BOUNDARY:
+ stream()->Add("@B");
+ break;
+ }
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitAtom(RegExpAtom* that, void* data) {
+ stream()->Add("'");
+ Vector<const uc16> chardata = that->data();
+ for (int i = 0; i < chardata.length(); i++) {
+ stream()->Add("%k", chardata[i]);
+ }
+ stream()->Add("'");
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitText(RegExpText* that, void* data) {
+ if (that->elements()->length() == 1) {
+ that->elements()->at(0).data.u_atom->Accept(this, data);
+ } else {
+ stream()->Add("(!");
+ for (int i = 0; i < that->elements()->length(); i++) {
+ stream()->Add(" ");
+ that->elements()->at(i).data.u_atom->Accept(this, data);
+ }
+ stream()->Add(")");
+ }
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitQuantifier(RegExpQuantifier* that, void* data) {
+ stream()->Add("(# %i ", that->min());
+ if (that->max() == RegExpTree::kInfinity) {
+ stream()->Add("- ");
+ } else {
+ stream()->Add("%i ", that->max());
+ }
+ stream()->Add(that->is_greedy() ? "g " : that->is_possessive() ? "p " : "n ");
+ that->body()->Accept(this, data);
+ stream()->Add(")");
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitCapture(RegExpCapture* that, void* data) {
+ stream()->Add("(^ ");
+ that->body()->Accept(this, data);
+ stream()->Add(")");
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitLookahead(RegExpLookahead* that, void* data) {
+ stream()->Add("(-> ");
+ stream()->Add(that->is_positive() ? "+ " : "- ");
+ that->body()->Accept(this, data);
+ stream()->Add(")");
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitBackReference(RegExpBackReference* that,
+ void* data) {
+ stream()->Add("(<- %i)", that->index());
+ return NULL;
+}
+
+
+void* RegExpUnparser::VisitEmpty(RegExpEmpty* that, void* data) {
+ stream()->Put('%');
+ return NULL;
+}
+
+
+SmartArrayPointer<const char> RegExpTree::ToString(Zone* zone) {
+ RegExpUnparser unparser(zone);
+ Accept(&unparser, NULL);
+ return unparser.ToString();
+}
+
+
+RegExpDisjunction::RegExpDisjunction(ZoneList<RegExpTree*>* alternatives)
+ : alternatives_(alternatives) {
+ ASSERT(alternatives->length() > 1);
+ RegExpTree* first_alternative = alternatives->at(0);
+ min_match_ = first_alternative->min_match();
+ max_match_ = first_alternative->max_match();
+ for (int i = 1; i < alternatives->length(); i++) {
+ RegExpTree* alternative = alternatives->at(i);
+ min_match_ = Min(min_match_, alternative->min_match());
+ max_match_ = Max(max_match_, alternative->max_match());
+ }
+}
+
+
+static int IncreaseBy(int previous, int increase) {
+ if (RegExpTree::kInfinity - previous < increase) {
+ return RegExpTree::kInfinity;
+ } else {
+ return previous + increase;
+ }
+}
+
+RegExpAlternative::RegExpAlternative(ZoneList<RegExpTree*>* nodes)
+ : nodes_(nodes) {
+ ASSERT(nodes->length() > 1);
+ min_match_ = 0;
+ max_match_ = 0;
+ for (int i = 0; i < nodes->length(); i++) {
+ RegExpTree* node = nodes->at(i);
+ int node_min_match = node->min_match();
+ min_match_ = IncreaseBy(min_match_, node_min_match);
+ int node_max_match = node->max_match();
+ max_match_ = IncreaseBy(max_match_, node_max_match);
+ }
+}
+
+
+CaseClause::CaseClause(Isolate* isolate,
+ Expression* label,
+ ZoneList<Statement*>* statements,
+ int pos)
+ : label_(label),
+ statements_(statements),
+ position_(pos),
+ compare_type_(Type::None(), isolate),
+ compare_id_(AstNode::GetNextId(isolate)),
+ entry_id_(AstNode::GetNextId(isolate)) {
+}
+
+
+#define REGULAR_NODE(NodeType) \
+ void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \
+ increase_node_count(); \
+ }
+#define DONT_OPTIMIZE_NODE(NodeType) \
+ void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \
+ increase_node_count(); \
+ add_flag(kDontOptimize); \
+ add_flag(kDontInline); \
+ add_flag(kDontSelfOptimize); \
+ }
+#define DONT_SELFOPTIMIZE_NODE(NodeType) \
+ void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \
+ increase_node_count(); \
+ add_flag(kDontSelfOptimize); \
+ }
+#define DONT_CACHE_NODE(NodeType) \
+ void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \
+ increase_node_count(); \
+ add_flag(kDontOptimize); \
+ add_flag(kDontInline); \
+ add_flag(kDontSelfOptimize); \
+ add_flag(kDontCache); \
+ }
+
+REGULAR_NODE(VariableDeclaration)
+REGULAR_NODE(FunctionDeclaration)
+REGULAR_NODE(Block)
+REGULAR_NODE(ExpressionStatement)
+REGULAR_NODE(EmptyStatement)
+REGULAR_NODE(IfStatement)
+REGULAR_NODE(ContinueStatement)
+REGULAR_NODE(BreakStatement)
+REGULAR_NODE(ReturnStatement)
+REGULAR_NODE(SwitchStatement)
+REGULAR_NODE(Conditional)
+REGULAR_NODE(Literal)
+REGULAR_NODE(ArrayLiteral)
+REGULAR_NODE(ObjectLiteral)
+REGULAR_NODE(RegExpLiteral)
+REGULAR_NODE(FunctionLiteral)
+REGULAR_NODE(Assignment)
+REGULAR_NODE(Throw)
+REGULAR_NODE(Property)
+REGULAR_NODE(UnaryOperation)
+REGULAR_NODE(CountOperation)
+REGULAR_NODE(BinaryOperation)
+REGULAR_NODE(CompareOperation)
+REGULAR_NODE(ThisFunction)
+REGULAR_NODE(Call)
+REGULAR_NODE(CallNew)
+// In theory, for VariableProxy we'd have to add:
+// if (node->var()->IsLookupSlot()) add_flag(kDontInline);
+// But node->var() is usually not bound yet at VariableProxy creation time, and
+// LOOKUP variables only result from constructs that cannot be inlined anyway.
+REGULAR_NODE(VariableProxy)
+
+// We currently do not optimize any modules.
+DONT_OPTIMIZE_NODE(ModuleDeclaration)
+DONT_OPTIMIZE_NODE(ImportDeclaration)
+DONT_OPTIMIZE_NODE(ExportDeclaration)
+DONT_OPTIMIZE_NODE(ModuleVariable)
+DONT_OPTIMIZE_NODE(ModulePath)
+DONT_OPTIMIZE_NODE(ModuleUrl)
+DONT_OPTIMIZE_NODE(ModuleStatement)
+DONT_OPTIMIZE_NODE(Yield)
+DONT_OPTIMIZE_NODE(WithStatement)
+DONT_OPTIMIZE_NODE(TryCatchStatement)
+DONT_OPTIMIZE_NODE(TryFinallyStatement)
+DONT_OPTIMIZE_NODE(DebuggerStatement)
+DONT_OPTIMIZE_NODE(SharedFunctionInfoLiteral)
+
+DONT_SELFOPTIMIZE_NODE(DoWhileStatement)
+DONT_SELFOPTIMIZE_NODE(WhileStatement)
+DONT_SELFOPTIMIZE_NODE(ForStatement)
+DONT_SELFOPTIMIZE_NODE(ForInStatement)
+DONT_SELFOPTIMIZE_NODE(ForOfStatement)
+
+DONT_CACHE_NODE(ModuleLiteral)
+
+void AstConstructionVisitor::VisitCallRuntime(CallRuntime* node) {
+ increase_node_count();
+ 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()->IsOneByteEqualTo(
+ STATIC_ASCII_VECTOR("_ArgumentsLength")) ||
+ node->name()->IsOneByteEqualTo(STATIC_ASCII_VECTOR("_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);
+ }
+}
+
+#undef REGULAR_NODE
+#undef DONT_OPTIMIZE_NODE
+#undef DONT_SELFOPTIMIZE_NODE
+#undef DONT_CACHE_NODE
+
+
+Handle<String> Literal::ToString() {
+ if (value_->IsString()) return Handle<String>::cast(value_);
+ Factory* factory = Isolate::Current()->factory();
+ ASSERT(value_->IsNumber());
+ char arr[100];
+ Vector<char> buffer(arr, ARRAY_SIZE(arr));
+ const char* str;
+ if (value_->IsSmi()) {
+ // Optimization only, the heap number case would subsume this.
+ OS::SNPrintF(buffer, "%d", Smi::cast(*value_)->value());
+ str = arr;
+ } else {
+ str = DoubleToCString(value_->Number(), buffer);
+ }
+ return factory->NewStringFromAscii(CStrVector(str));
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/ast.h b/chromium/v8/src/ast.h
new file mode 100644
index 00000000000..a8b74213adb
--- /dev/null
+++ b/chromium/v8/src/ast.h
@@ -0,0 +1,3234 @@
+// Copyright 2012 the V8 project authors. 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_H_
+#define V8_AST_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-pointers.h"
+#include "token.h"
+#include "type-info.h" // TODO(rossberg): this should eventually be removed
+#include "types.h"
+#include "utils.h"
+#include "variables.h"
+#include "interface.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// The abstract syntax tree is an intermediate, light-weight
+// representation of the parsed JavaScript code suitable for
+// compilation to native code.
+
+// Nodes are allocated in a separate zone, which allows faster
+// allocation and constant-time deallocation of the entire syntax
+// tree.
+
+
+// ----------------------------------------------------------------------------
+// Nodes of the abstract syntax tree. Only concrete classes are
+// enumerated here.
+
+#define DECLARATION_NODE_LIST(V) \
+ V(VariableDeclaration) \
+ V(FunctionDeclaration) \
+ V(ModuleDeclaration) \
+ V(ImportDeclaration) \
+ V(ExportDeclaration) \
+
+#define MODULE_NODE_LIST(V) \
+ V(ModuleLiteral) \
+ V(ModuleVariable) \
+ V(ModulePath) \
+ V(ModuleUrl)
+
+#define STATEMENT_NODE_LIST(V) \
+ V(Block) \
+ V(ModuleStatement) \
+ V(ExpressionStatement) \
+ V(EmptyStatement) \
+ V(IfStatement) \
+ V(ContinueStatement) \
+ V(BreakStatement) \
+ V(ReturnStatement) \
+ V(WithStatement) \
+ V(SwitchStatement) \
+ V(DoWhileStatement) \
+ V(WhileStatement) \
+ V(ForStatement) \
+ V(ForInStatement) \
+ V(ForOfStatement) \
+ V(TryCatchStatement) \
+ V(TryFinallyStatement) \
+ V(DebuggerStatement)
+
+#define EXPRESSION_NODE_LIST(V) \
+ V(FunctionLiteral) \
+ V(SharedFunctionInfoLiteral) \
+ V(Conditional) \
+ V(VariableProxy) \
+ V(Literal) \
+ V(RegExpLiteral) \
+ V(ObjectLiteral) \
+ V(ArrayLiteral) \
+ V(Assignment) \
+ V(Yield) \
+ V(Throw) \
+ V(Property) \
+ V(Call) \
+ V(CallNew) \
+ V(CallRuntime) \
+ V(UnaryOperation) \
+ V(CountOperation) \
+ V(BinaryOperation) \
+ V(CompareOperation) \
+ V(ThisFunction)
+
+#define AST_NODE_LIST(V) \
+ DECLARATION_NODE_LIST(V) \
+ MODULE_NODE_LIST(V) \
+ STATEMENT_NODE_LIST(V) \
+ EXPRESSION_NODE_LIST(V)
+
+#ifdef WIN32
+#undef Yield
+#endif
+
+// Forward declarations
+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
+
+
+// Typedef only introduced to avoid unreadable code.
+// Please do appreciate the required space in "> >".
+typedef ZoneList<Handle<String> > ZoneStringList;
+typedef ZoneList<Handle<Object> > ZoneObjectList;
+
+
+#define DECLARE_NODE_TYPE(type) \
+ virtual void Accept(AstVisitor* v); \
+ virtual AstNode::NodeType node_type() const { return AstNode::k##type; } \
+ template<class> friend class AstNodeFactory;
+
+
+enum AstPropertiesFlag {
+ kDontInline,
+ kDontOptimize,
+ kDontSelfOptimize,
+ kDontSoftInline,
+ kDontCache
+};
+
+
+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 {
+ public:
+#define DECLARE_TYPE_ENUM(type) k##type,
+ enum NodeType {
+ AST_NODE_LIST(DECLARE_TYPE_ENUM)
+ kInvalid = -1
+ };
+#undef DECLARE_TYPE_ENUM
+
+ void* operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+ }
+
+ AstNode() { }
+
+ virtual ~AstNode() { }
+
+ virtual void Accept(AstVisitor* v) = 0;
+ virtual NodeType node_type() const = 0;
+
+ // Type testing & conversion functions overridden by concrete subclasses.
+#define DECLARE_NODE_FUNCTIONS(type) \
+ 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 TargetCollector* AsTargetCollector() { return NULL; }
+ virtual BreakableStatement* AsBreakableStatement() { return NULL; }
+ virtual IterationStatement* AsIterationStatement() { return NULL; }
+ virtual MaterializedLiteral* AsMaterializedLiteral() { return NULL; }
+
+ protected:
+ static int GetNextId(Isolate* isolate) {
+ return ReserveIdRange(isolate, 1);
+ }
+
+ static int ReserveIdRange(Isolate* isolate, int n) {
+ int tmp = isolate->ast_node_id();
+ isolate->set_ast_node_id(tmp + n);
+ return tmp;
+ }
+
+ // Some nodes re-use bailout IDs for type feedback.
+ static TypeFeedbackId reuse(BailoutId id) {
+ return TypeFeedbackId(id.ToInt());
+ }
+
+
+ private:
+ // Hidden to prevent accidental usage. It would have to load the
+ // current zone from the TLS.
+ void* operator new(size_t size);
+
+ friend class CaseClause; // Generates AST IDs.
+};
+
+
+class Statement: public AstNode {
+ public:
+ Statement() : statement_pos_(RelocInfo::kNoPosition) {}
+
+ bool IsEmpty() { return AsEmptyStatement() != NULL; }
+ virtual bool IsJump() const { return false; }
+
+ void set_statement_pos(int statement_pos) { statement_pos_ = statement_pos; }
+ int statement_pos() const { return statement_pos_; }
+
+ private:
+ int statement_pos_;
+};
+
+
+class SmallMapList {
+ public:
+ SmallMapList() {}
+ SmallMapList(int capacity, Zone* zone) : list_(capacity, zone) {}
+
+ void Reserve(int capacity, Zone* zone) { list_.Reserve(capacity, zone); }
+ void Clear() { list_.Clear(); }
+ void Sort() { list_.Sort(); }
+
+ bool is_empty() const { return list_.is_empty(); }
+ int length() const { return list_.length(); }
+
+ void AddMapIfMissing(Handle<Map> map, Zone* zone) {
+ Map* updated = map->CurrentMapForDeprecated();
+ if (updated == NULL) return;
+ map = Handle<Map>(updated);
+ for (int i = 0; i < length(); ++i) {
+ if (at(i).is_identical_to(map)) return;
+ }
+ Add(map, zone);
+ }
+
+ void Add(Handle<Map> handle, Zone* zone) {
+ list_.Add(handle.location(), zone);
+ }
+
+ Handle<Map> at(int i) const {
+ return Handle<Map>(list_.at(i));
+ }
+
+ Handle<Map> first() const { return at(0); }
+ Handle<Map> last() const { return at(length() - 1); }
+
+ private:
+ // The list stores pointers to Map*, that is Map**, so it's GC safe.
+ SmallPointerList<Map*> list_;
+
+ DISALLOW_COPY_AND_ASSIGN(SmallMapList);
+};
+
+
+class Expression: public AstNode {
+ public:
+ enum Context {
+ // Not assigned a context yet, or else will not be visited during
+ // code generation.
+ kUninitialized,
+ // Evaluated for its side effects.
+ kEffect,
+ // Evaluated for its value (and side effects).
+ kValue,
+ // Evaluated for control flow (and side effects).
+ kTest
+ };
+
+ virtual int position() const {
+ UNREACHABLE();
+ return 0;
+ }
+
+ virtual bool IsValidLeftHandSide() { return false; }
+
+ // Helpers for ToBoolean conversion.
+ virtual bool ToBooleanIsTrue() { return false; }
+ virtual bool ToBooleanIsFalse() { return false; }
+
+ // Symbols that cannot be parsed as array indices are considered property
+ // names. We do not treat symbols that can be array indexes as property
+ // names because [] for string objects is handled only by keyed ICs.
+ virtual bool IsPropertyName() { return false; }
+
+ // 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.
+ bool IsSmiLiteral();
+
+ // True iff the expression is a string literal.
+ bool IsStringLiteral();
+
+ // True iff the expression is the null literal.
+ bool IsNullLiteral();
+
+ // True if we can prove that the expression is the undefined literal.
+ bool IsUndefinedLiteral(Isolate* isolate);
+
+ // Expression type bounds
+ Bounds bounds() { return bounds_; }
+ void set_bounds(Bounds bounds) { bounds_ = bounds; }
+
+ // Type feedback information for assignments and properties.
+ virtual bool IsMonomorphic() {
+ UNREACHABLE();
+ return false;
+ }
+ virtual SmallMapList* GetReceiverTypes() {
+ UNREACHABLE();
+ return NULL;
+ }
+ Handle<Map> GetMonomorphicReceiverType() {
+ ASSERT(IsMonomorphic());
+ SmallMapList* types = GetReceiverTypes();
+ ASSERT(types != NULL && types->length() == 1);
+ return types->at(0);
+ }
+ virtual KeyedAccessStoreMode GetStoreMode() {
+ UNREACHABLE();
+ return STANDARD_STORE;
+ }
+
+ // TODO(rossberg): this should move to its own AST node eventually.
+ virtual void RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle);
+ byte to_boolean_types() const { return to_boolean_types_; }
+
+ BailoutId id() const { return id_; }
+ TypeFeedbackId test_id() const { return test_id_; }
+
+ protected:
+ explicit Expression(Isolate* isolate)
+ : bounds_(Bounds::Unbounded(isolate)),
+ id_(GetNextId(isolate)),
+ test_id_(GetNextId(isolate)) {}
+ void set_to_boolean_types(byte types) { to_boolean_types_ = types; }
+
+ private:
+ Bounds bounds_;
+ byte to_boolean_types_;
+
+ const BailoutId id_;
+ const TypeFeedbackId test_id_;
+};
+
+
+class BreakableStatement: public Statement {
+ public:
+ enum BreakableType {
+ TARGET_FOR_ANONYMOUS,
+ TARGET_FOR_NAMED_ONLY
+ };
+
+ // The labels associated with this statement. May be NULL;
+ // if it is != NULL, guaranteed to contain at least one entry.
+ ZoneStringList* labels() const { return labels_; }
+
+ // Type testing & conversion.
+ virtual BreakableStatement* AsBreakableStatement() { return this; }
+
+ // Code generation
+ Label* break_target() { return &break_target_; }
+
+ // Testers.
+ bool is_target_for_anonymous() const {
+ return breakable_type_ == TARGET_FOR_ANONYMOUS;
+ }
+
+ BailoutId EntryId() const { return entry_id_; }
+ BailoutId ExitId() const { return exit_id_; }
+
+ protected:
+ BreakableStatement(
+ Isolate* isolate, ZoneStringList* labels, BreakableType breakable_type)
+ : labels_(labels),
+ breakable_type_(breakable_type),
+ entry_id_(GetNextId(isolate)),
+ exit_id_(GetNextId(isolate)) {
+ ASSERT(labels == NULL || labels->length() > 0);
+ }
+
+
+ private:
+ ZoneStringList* labels_;
+ BreakableType breakable_type_;
+ Label break_target_;
+ const BailoutId entry_id_;
+ const BailoutId exit_id_;
+};
+
+
+class Block: public BreakableStatement {
+ public:
+ DECLARE_NODE_TYPE(Block)
+
+ void AddStatement(Statement* statement, Zone* zone) {
+ statements_.Add(statement, zone);
+ }
+
+ ZoneList<Statement*>* statements() { return &statements_; }
+ bool is_initializer_block() const { return is_initializer_block_; }
+
+ virtual bool IsJump() const {
+ return !statements_.is_empty() && statements_.last()->IsJump()
+ && labels() == NULL; // Good enough as an approximation...
+ }
+
+ Scope* scope() const { return scope_; }
+ void set_scope(Scope* scope) { scope_ = scope; }
+
+ protected:
+ Block(Isolate* isolate,
+ ZoneStringList* labels,
+ int capacity,
+ bool is_initializer_block,
+ Zone* zone)
+ : BreakableStatement(isolate, labels, TARGET_FOR_NAMED_ONLY),
+ statements_(capacity, zone),
+ is_initializer_block_(is_initializer_block),
+ scope_(NULL) {
+ }
+
+ private:
+ ZoneList<Statement*> statements_;
+ bool is_initializer_block_;
+ Scope* scope_;
+};
+
+
+class Declaration: public AstNode {
+ public:
+ VariableProxy* proxy() const { return proxy_; }
+ VariableMode mode() const { return mode_; }
+ Scope* scope() const { return scope_; }
+ virtual InitializationFlag initialization() const = 0;
+ virtual bool IsInlineable() const;
+
+ protected:
+ Declaration(VariableProxy* proxy,
+ VariableMode mode,
+ Scope* scope)
+ : proxy_(proxy),
+ mode_(mode),
+ scope_(scope) {
+ ASSERT(IsDeclaredVariableMode(mode));
+ }
+
+ private:
+ VariableProxy* proxy_;
+ VariableMode mode_;
+
+ // Nested scope from which the declaration originated.
+ Scope* scope_;
+};
+
+
+class VariableDeclaration: public Declaration {
+ public:
+ DECLARE_NODE_TYPE(VariableDeclaration)
+
+ virtual InitializationFlag initialization() const {
+ return mode() == VAR ? kCreatedInitialized : kNeedsInitialization;
+ }
+
+ protected:
+ VariableDeclaration(VariableProxy* proxy,
+ VariableMode mode,
+ Scope* scope)
+ : Declaration(proxy, mode, scope) {
+ }
+};
+
+
+class FunctionDeclaration: public Declaration {
+ public:
+ DECLARE_NODE_TYPE(FunctionDeclaration)
+
+ FunctionLiteral* fun() const { return fun_; }
+ virtual InitializationFlag initialization() const {
+ return kCreatedInitialized;
+ }
+ virtual bool IsInlineable() const;
+
+ protected:
+ FunctionDeclaration(VariableProxy* proxy,
+ VariableMode mode,
+ FunctionLiteral* fun,
+ Scope* scope)
+ : Declaration(proxy, mode, scope),
+ fun_(fun) {
+ // At the moment there are no "const functions" in JavaScript...
+ ASSERT(mode == VAR || mode == LET);
+ ASSERT(fun != NULL);
+ }
+
+ private:
+ FunctionLiteral* fun_;
+};
+
+
+class ModuleDeclaration: public Declaration {
+ public:
+ DECLARE_NODE_TYPE(ModuleDeclaration)
+
+ Module* module() const { return module_; }
+ virtual InitializationFlag initialization() const {
+ return kCreatedInitialized;
+ }
+
+ protected:
+ ModuleDeclaration(VariableProxy* proxy,
+ Module* module,
+ Scope* scope)
+ : Declaration(proxy, MODULE, scope),
+ module_(module) {
+ }
+
+ private:
+ Module* module_;
+};
+
+
+class ImportDeclaration: public Declaration {
+ public:
+ DECLARE_NODE_TYPE(ImportDeclaration)
+
+ Module* module() const { return module_; }
+ virtual InitializationFlag initialization() const {
+ return kCreatedInitialized;
+ }
+
+ protected:
+ ImportDeclaration(VariableProxy* proxy,
+ Module* module,
+ Scope* scope)
+ : Declaration(proxy, LET, scope),
+ module_(module) {
+ }
+
+ private:
+ Module* module_;
+};
+
+
+class ExportDeclaration: public Declaration {
+ public:
+ DECLARE_NODE_TYPE(ExportDeclaration)
+
+ virtual InitializationFlag initialization() const {
+ return kCreatedInitialized;
+ }
+
+ protected:
+ ExportDeclaration(VariableProxy* proxy, Scope* scope)
+ : Declaration(proxy, LET, scope) {}
+};
+
+
+class Module: public AstNode {
+ public:
+ Interface* interface() const { return interface_; }
+ Block* body() const { return body_; }
+
+ protected:
+ explicit Module(Zone* zone)
+ : interface_(Interface::NewModule(zone)),
+ body_(NULL) {}
+ explicit Module(Interface* interface, Block* body = NULL)
+ : interface_(interface),
+ body_(body) {}
+
+ private:
+ Interface* interface_;
+ Block* body_;
+};
+
+
+class ModuleLiteral: public Module {
+ public:
+ DECLARE_NODE_TYPE(ModuleLiteral)
+
+ protected:
+ ModuleLiteral(Block* body, Interface* interface) : Module(interface, body) {}
+};
+
+
+class ModuleVariable: public Module {
+ public:
+ DECLARE_NODE_TYPE(ModuleVariable)
+
+ VariableProxy* proxy() const { return proxy_; }
+
+ protected:
+ inline explicit ModuleVariable(VariableProxy* proxy);
+
+ private:
+ VariableProxy* proxy_;
+};
+
+
+class ModulePath: public Module {
+ public:
+ DECLARE_NODE_TYPE(ModulePath)
+
+ Module* module() const { return module_; }
+ Handle<String> name() const { return name_; }
+
+ protected:
+ ModulePath(Module* module, Handle<String> name, Zone* zone)
+ : Module(zone),
+ 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:
+ ModuleUrl(Handle<String> url, Zone* zone)
+ : Module(zone), url_(url) {
+ }
+
+ private:
+ Handle<String> url_;
+};
+
+
+class ModuleStatement: public Statement {
+ public:
+ DECLARE_NODE_TYPE(ModuleStatement)
+
+ VariableProxy* proxy() const { return proxy_; }
+ Block* body() const { return body_; }
+
+ protected:
+ ModuleStatement(VariableProxy* proxy, Block* body)
+ : proxy_(proxy),
+ body_(body) {
+ }
+
+ private:
+ VariableProxy* proxy_;
+ Block* body_;
+};
+
+
+class IterationStatement: public BreakableStatement {
+ public:
+ // Type testing & conversion.
+ virtual IterationStatement* AsIterationStatement() { return this; }
+
+ Statement* body() const { return body_; }
+
+ BailoutId OsrEntryId() const { return osr_entry_id_; }
+ virtual BailoutId ContinueId() const = 0;
+ virtual BailoutId StackCheckId() const = 0;
+
+ // Code generation
+ Label* continue_target() { return &continue_target_; }
+
+ protected:
+ IterationStatement(Isolate* isolate, ZoneStringList* labels)
+ : BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS),
+ body_(NULL),
+ osr_entry_id_(GetNextId(isolate)) {
+ }
+
+ void Initialize(Statement* body) {
+ body_ = body;
+ }
+
+ private:
+ Statement* body_;
+ Label continue_target_;
+
+ const BailoutId osr_entry_id_;
+};
+
+
+class DoWhileStatement: public IterationStatement {
+ public:
+ DECLARE_NODE_TYPE(DoWhileStatement)
+
+ void Initialize(Expression* cond, Statement* body) {
+ IterationStatement::Initialize(body);
+ cond_ = cond;
+ }
+
+ Expression* cond() const { return cond_; }
+
+ // Position where condition expression starts. We need it to make
+ // the loop's condition a breakable location.
+ int condition_position() { return condition_position_; }
+ void set_condition_position(int pos) { condition_position_ = pos; }
+
+ virtual BailoutId ContinueId() const { return continue_id_; }
+ virtual BailoutId StackCheckId() const { return back_edge_id_; }
+ BailoutId BackEdgeId() const { return back_edge_id_; }
+
+ protected:
+ 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_;
+
+ int condition_position_;
+
+ const BailoutId continue_id_;
+ const BailoutId back_edge_id_;
+};
+
+
+class WhileStatement: public IterationStatement {
+ public:
+ DECLARE_NODE_TYPE(WhileStatement)
+
+ void Initialize(Expression* cond, Statement* body) {
+ IterationStatement::Initialize(body);
+ cond_ = cond;
+ }
+
+ Expression* cond() const { return cond_; }
+ bool may_have_function_literal() const {
+ return may_have_function_literal_;
+ }
+ void set_may_have_function_literal(bool value) {
+ may_have_function_literal_ = value;
+ }
+
+ virtual BailoutId ContinueId() const { return EntryId(); }
+ virtual BailoutId StackCheckId() const { return body_id_; }
+ BailoutId BodyId() const { return body_id_; }
+
+ protected:
+ 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.
+ bool may_have_function_literal_;
+
+ const BailoutId body_id_;
+};
+
+
+class ForStatement: public IterationStatement {
+ public:
+ DECLARE_NODE_TYPE(ForStatement)
+
+ void Initialize(Statement* init,
+ Expression* cond,
+ Statement* next,
+ Statement* body) {
+ IterationStatement::Initialize(body);
+ init_ = init;
+ cond_ = cond;
+ next_ = next;
+ }
+
+ Statement* init() const { return init_; }
+ Expression* cond() const { return cond_; }
+ Statement* next() const { return next_; }
+
+ bool may_have_function_literal() const {
+ return may_have_function_literal_;
+ }
+ void set_may_have_function_literal(bool value) {
+ may_have_function_literal_ = value;
+ }
+
+ virtual BailoutId ContinueId() const { return continue_id_; }
+ virtual BailoutId StackCheckId() const { return body_id_; }
+ BailoutId BodyId() const { return body_id_; }
+
+ bool is_fast_smi_loop() { return loop_variable_ != NULL; }
+ Variable* loop_variable() { return loop_variable_; }
+ void set_loop_variable(Variable* var) { loop_variable_ = var; }
+
+ protected:
+ 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_;
+ Expression* cond_;
+ Statement* next_;
+
+ // True if there is a function literal subexpression in the condition.
+ bool may_have_function_literal_;
+ Variable* loop_variable_;
+
+ const BailoutId continue_id_;
+ const BailoutId body_id_;
+};
+
+
+class ForEachStatement: public IterationStatement {
+ public:
+ enum VisitMode {
+ ENUMERATE, // for (each in subject) body;
+ ITERATE // for (each of subject) body;
+ };
+
+ void Initialize(Expression* each, Expression* subject, Statement* body) {
+ IterationStatement::Initialize(body);
+ each_ = each;
+ subject_ = subject;
+ }
+
+ Expression* each() const { return each_; }
+ Expression* subject() const { return subject_; }
+
+ protected:
+ ForEachStatement(Isolate* isolate, ZoneStringList* labels)
+ : IterationStatement(isolate, labels),
+ each_(NULL),
+ subject_(NULL) {
+ }
+
+ private:
+ Expression* each_;
+ Expression* subject_;
+};
+
+
+class ForInStatement: public ForEachStatement {
+ public:
+ DECLARE_NODE_TYPE(ForInStatement)
+
+ Expression* enumerable() const {
+ return subject();
+ }
+
+ TypeFeedbackId ForInFeedbackId() const { return reuse(PrepareId()); }
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle);
+ enum ForInType { FAST_FOR_IN, SLOW_FOR_IN };
+ ForInType for_in_type() const { return for_in_type_; }
+
+ BailoutId BodyId() const { return body_id_; }
+ BailoutId PrepareId() const { return prepare_id_; }
+ virtual BailoutId ContinueId() const { return EntryId(); }
+ virtual BailoutId StackCheckId() const { return body_id_; }
+
+ protected:
+ ForInStatement(Isolate* isolate, ZoneStringList* labels)
+ : ForEachStatement(isolate, labels),
+ for_in_type_(SLOW_FOR_IN),
+ body_id_(GetNextId(isolate)),
+ prepare_id_(GetNextId(isolate)) {
+ }
+
+ ForInType for_in_type_;
+ const BailoutId body_id_;
+ const BailoutId prepare_id_;
+};
+
+
+class ForOfStatement: public ForEachStatement {
+ public:
+ DECLARE_NODE_TYPE(ForOfStatement)
+
+ void Initialize(Expression* each,
+ Expression* subject,
+ Statement* body,
+ Expression* assign_iterator,
+ Expression* next_result,
+ Expression* result_done,
+ Expression* assign_each) {
+ ForEachStatement::Initialize(each, subject, body);
+ assign_iterator_ = assign_iterator;
+ next_result_ = next_result;
+ result_done_ = result_done;
+ assign_each_ = assign_each;
+ }
+
+ Expression* iterable() const {
+ return subject();
+ }
+
+ // var iterator = iterable;
+ Expression* assign_iterator() const {
+ return assign_iterator_;
+ }
+
+ // var result = iterator.next();
+ Expression* next_result() const {
+ return next_result_;
+ }
+
+ // result.done
+ Expression* result_done() const {
+ return result_done_;
+ }
+
+ // each = result.value
+ Expression* assign_each() const {
+ return assign_each_;
+ }
+
+ virtual BailoutId ContinueId() const { return EntryId(); }
+ virtual BailoutId StackCheckId() const { return BackEdgeId(); }
+
+ BailoutId BackEdgeId() const { return back_edge_id_; }
+
+ protected:
+ ForOfStatement(Isolate* isolate, ZoneStringList* labels)
+ : ForEachStatement(isolate, labels),
+ assign_iterator_(NULL),
+ next_result_(NULL),
+ result_done_(NULL),
+ assign_each_(NULL),
+ back_edge_id_(GetNextId(isolate)) {
+ }
+
+ Expression* assign_iterator_;
+ Expression* next_result_;
+ Expression* result_done_;
+ Expression* assign_each_;
+ const BailoutId back_edge_id_;
+};
+
+
+class ExpressionStatement: public Statement {
+ public:
+ DECLARE_NODE_TYPE(ExpressionStatement)
+
+ void set_expression(Expression* e) { expression_ = e; }
+ Expression* expression() const { return expression_; }
+ virtual bool IsJump() const { return expression_->IsThrow(); }
+
+ protected:
+ explicit ExpressionStatement(Expression* expression)
+ : expression_(expression) { }
+
+ private:
+ Expression* expression_;
+};
+
+
+class JumpStatement: public Statement {
+ public:
+ virtual bool IsJump() const { return true; }
+
+ protected:
+ JumpStatement() {}
+};
+
+
+class ContinueStatement: public JumpStatement {
+ public:
+ DECLARE_NODE_TYPE(ContinueStatement)
+
+ IterationStatement* target() const { return target_; }
+
+ protected:
+ explicit ContinueStatement(IterationStatement* target)
+ : target_(target) { }
+
+ private:
+ IterationStatement* target_;
+};
+
+
+class BreakStatement: public JumpStatement {
+ public:
+ DECLARE_NODE_TYPE(BreakStatement)
+
+ BreakableStatement* target() const { return target_; }
+
+ protected:
+ explicit BreakStatement(BreakableStatement* target)
+ : target_(target) { }
+
+ private:
+ BreakableStatement* target_;
+};
+
+
+class ReturnStatement: public JumpStatement {
+ public:
+ DECLARE_NODE_TYPE(ReturnStatement)
+
+ Expression* expression() const { return expression_; }
+
+ protected:
+ explicit ReturnStatement(Expression* expression)
+ : expression_(expression) { }
+
+ private:
+ Expression* expression_;
+};
+
+
+class WithStatement: public Statement {
+ public:
+ DECLARE_NODE_TYPE(WithStatement)
+
+ Scope* scope() { return scope_; }
+ Expression* expression() const { return expression_; }
+ Statement* statement() const { return statement_; }
+
+ protected:
+ WithStatement(Scope* scope, Expression* expression, Statement* statement)
+ : scope_(scope),
+ expression_(expression),
+ statement_(statement) { }
+
+ private:
+ Scope* scope_;
+ Expression* expression_;
+ Statement* statement_;
+};
+
+
+class CaseClause: public ZoneObject {
+ public:
+ CaseClause(Isolate* isolate,
+ Expression* label,
+ ZoneList<Statement*>* statements,
+ int pos);
+
+ bool is_default() const { return label_ == NULL; }
+ Expression* label() const {
+ CHECK(!is_default());
+ return label_;
+ }
+ Label* body_target() { return &body_target_; }
+ ZoneList<Statement*>* statements() const { return statements_; }
+
+ int position() const { return position_; }
+ void set_position(int pos) { position_ = pos; }
+
+ BailoutId EntryId() const { return entry_id_; }
+
+ // Type feedback information.
+ TypeFeedbackId CompareId() { return compare_id_; }
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle);
+ Handle<Type> compare_type() { return compare_type_; }
+
+ private:
+ Expression* label_;
+ Label body_target_;
+ ZoneList<Statement*>* statements_;
+ int position_;
+ Handle<Type> compare_type_;
+
+ const TypeFeedbackId compare_id_;
+ const BailoutId entry_id_;
+};
+
+
+class SwitchStatement: public BreakableStatement {
+ public:
+ DECLARE_NODE_TYPE(SwitchStatement)
+
+ void Initialize(Expression* tag, ZoneList<CaseClause*>* cases) {
+ tag_ = tag;
+ cases_ = cases;
+ switch_type_ = UNKNOWN_SWITCH;
+ }
+
+ Expression* tag() const { return tag_; }
+ ZoneList<CaseClause*>* cases() const { return cases_; }
+
+ enum SwitchType { UNKNOWN_SWITCH, SMI_SWITCH, STRING_SWITCH, GENERIC_SWITCH };
+ SwitchType switch_type() const { return switch_type_; }
+ void set_switch_type(SwitchType switch_type) { switch_type_ = switch_type; }
+
+ protected:
+ SwitchStatement(Isolate* isolate, ZoneStringList* labels)
+ : BreakableStatement(isolate, labels, TARGET_FOR_ANONYMOUS),
+ tag_(NULL),
+ cases_(NULL) { }
+
+ private:
+ Expression* tag_;
+ ZoneList<CaseClause*>* cases_;
+ SwitchType switch_type_;
+};
+
+
+// If-statements always have non-null references to their then- and
+// else-parts. When parsing if-statements with no explicit else-part,
+// the parser implicitly creates an empty statement. Use the
+// HasThenStatement() and HasElseStatement() functions to check if a
+// given if-statement has a then- or an else-part containing code.
+class IfStatement: public Statement {
+ public:
+ DECLARE_NODE_TYPE(IfStatement)
+
+ bool HasThenStatement() const { return !then_statement()->IsEmpty(); }
+ bool HasElseStatement() const { return !else_statement()->IsEmpty(); }
+
+ Expression* condition() const { return condition_; }
+ Statement* then_statement() const { return then_statement_; }
+ Statement* else_statement() const { return else_statement_; }
+
+ virtual bool IsJump() const {
+ return HasThenStatement() && then_statement()->IsJump()
+ && HasElseStatement() && else_statement()->IsJump();
+ }
+
+ BailoutId IfId() const { return if_id_; }
+ BailoutId ThenId() const { return then_id_; }
+ BailoutId ElseId() const { return else_id_; }
+
+ protected:
+ 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_;
+ Statement* else_statement_;
+ const BailoutId if_id_;
+ const BailoutId then_id_;
+ const BailoutId else_id_;
+};
+
+
+// NOTE: TargetCollectors are represented as nodes to fit in the target
+// stack in the compiler; this should probably be reworked.
+class TargetCollector: public AstNode {
+ public:
+ explicit TargetCollector(Zone* zone) : targets_(0, zone) { }
+
+ // 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
+ // references to something on the stack.
+ void AddTarget(Label* target, Zone* zone);
+
+ // Virtual behaviour. TargetCollectors are never part of the AST.
+ virtual void Accept(AstVisitor* v) { UNREACHABLE(); }
+ virtual NodeType node_type() const { return kInvalid; }
+ virtual TargetCollector* AsTargetCollector() { return this; }
+
+ ZoneList<Label*>* targets() { return &targets_; }
+
+ private:
+ ZoneList<Label*> targets_;
+};
+
+
+class TryStatement: public Statement {
+ public:
+ 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_; }
+
+ 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_;
+};
+
+
+class TryCatchStatement: public TryStatement {
+ public:
+ DECLARE_NODE_TYPE(TryCatchStatement)
+
+ Scope* scope() { return scope_; }
+ Variable* variable() { return variable_; }
+ Block* catch_block() const { return catch_block_; }
+
+ protected:
+ TryCatchStatement(int index,
+ Block* try_block,
+ Scope* scope,
+ Variable* variable,
+ Block* catch_block)
+ : TryStatement(index, try_block),
+ scope_(scope),
+ variable_(variable),
+ catch_block_(catch_block) {
+ }
+
+ private:
+ Scope* scope_;
+ Variable* variable_;
+ Block* catch_block_;
+};
+
+
+class TryFinallyStatement: public TryStatement {
+ public:
+ DECLARE_NODE_TYPE(TryFinallyStatement)
+
+ Block* finally_block() const { return finally_block_; }
+
+ protected:
+ TryFinallyStatement(int index, Block* try_block, Block* finally_block)
+ : TryStatement(index, try_block),
+ finally_block_(finally_block) { }
+
+ private:
+ Block* finally_block_;
+};
+
+
+class DebuggerStatement: public Statement {
+ public:
+ DECLARE_NODE_TYPE(DebuggerStatement)
+
+ protected:
+ DebuggerStatement() {}
+};
+
+
+class EmptyStatement: public Statement {
+ public:
+ DECLARE_NODE_TYPE(EmptyStatement)
+
+ protected:
+ EmptyStatement() {}
+};
+
+
+class Literal: public Expression {
+ public:
+ DECLARE_NODE_TYPE(Literal)
+
+ virtual bool IsPropertyName() {
+ if (value_->IsInternalizedString()) {
+ uint32_t ignored;
+ return !String::cast(*value_)->AsArrayIndex(&ignored);
+ }
+ return false;
+ }
+
+ Handle<String> AsPropertyName() {
+ ASSERT(IsPropertyName());
+ return Handle<String>::cast(value_);
+ }
+
+ virtual bool ToBooleanIsTrue() { return value_->BooleanValue(); }
+ virtual bool ToBooleanIsFalse() { return !value_->BooleanValue(); }
+
+ // Identity testers.
+ bool IsNull() const {
+ ASSERT(!value_.is_null());
+ return value_->IsNull();
+ }
+ bool IsTrue() const {
+ ASSERT(!value_.is_null());
+ return value_->IsTrue();
+ }
+ bool IsFalse() const {
+ ASSERT(!value_.is_null());
+ return value_->IsFalse();
+ }
+
+ Handle<Object> value() const { return value_; }
+
+ // Support for using Literal as a HashMap key. NOTE: Currently, this works
+ // only for string and number literals!
+ uint32_t Hash() { return ToString()->Hash(); }
+
+ static bool Match(void* literal1, void* literal2) {
+ Handle<String> s1 = static_cast<Literal*>(literal1)->ToString();
+ Handle<String> s2 = static_cast<Literal*>(literal2)->ToString();
+ return s1->Equals(*s2);
+ }
+
+ TypeFeedbackId LiteralFeedbackId() const { return reuse(id()); }
+
+ protected:
+ Literal(Isolate* isolate, Handle<Object> value)
+ : Expression(isolate),
+ value_(value) { }
+
+ private:
+ Handle<String> ToString();
+
+ Handle<Object> value_;
+};
+
+
+// Base class for literals that needs space in the corresponding JSFunction.
+class MaterializedLiteral: public Expression {
+ public:
+ virtual MaterializedLiteral* AsMaterializedLiteral() { return this; }
+
+ int literal_index() { return literal_index_; }
+
+ // A materialized literal is simple if the values consist of only
+ // constants and simple object and array literals.
+ bool is_simple() const { return is_simple_; }
+
+ int depth() const { return depth_; }
+
+ 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_;
+ bool is_simple_;
+ int depth_;
+};
+
+
+// Property is used for passing information
+// about an object literal's properties from the parser
+// to the code generator.
+class ObjectLiteralProperty: public ZoneObject {
+ public:
+ enum Kind {
+ CONSTANT, // Property with constant value (compile time).
+ COMPUTED, // Property with computed value (execution time).
+ MATERIALIZED_LITERAL, // Property value is a materialized literal.
+ GETTER, SETTER, // Property is an accessor function.
+ PROTOTYPE // Property is __proto__.
+ };
+
+ ObjectLiteralProperty(Literal* key, Expression* value, Isolate* isolate);
+
+ Literal* key() { return key_; }
+ Expression* value() { return value_; }
+ Kind kind() { return kind_; }
+
+ // Type feedback information.
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle);
+ bool IsMonomorphic() { return !receiver_type_.is_null(); }
+ Handle<Map> GetReceiverType() { return receiver_type_; }
+
+ bool IsCompileTimeValue();
+
+ void set_emit_store(bool emit_store);
+ bool emit_store();
+
+ protected:
+ template<class> friend class AstNodeFactory;
+
+ ObjectLiteralProperty(bool is_getter, FunctionLiteral* value);
+ void set_key(Literal* key) { key_ = key; }
+
+ private:
+ Literal* key_;
+ Expression* value_;
+ Kind kind_;
+ bool emit_store_;
+ Handle<Map> receiver_type_;
+};
+
+
+// An object literal has a boilerplate object that is used
+// for minimizing the work when constructing it at runtime.
+class ObjectLiteral: public MaterializedLiteral {
+ public:
+ typedef ObjectLiteralProperty Property;
+
+ DECLARE_NODE_TYPE(ObjectLiteral)
+
+ Handle<FixedArray> constant_properties() const {
+ return constant_properties_;
+ }
+ ZoneList<Property*>* properties() const { return properties_; }
+ bool fast_elements() const { return fast_elements_; }
+ bool may_store_doubles() const { return may_store_doubles_; }
+ bool has_function() const { return has_function_; }
+
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ void CalculateEmitStore(Zone* zone);
+
+ enum Flags {
+ kNoFlags = 0,
+ kFastElements = 1,
+ kHasFunction = 1 << 1
+ };
+
+ struct Accessors: public ZoneObject {
+ Accessors() : getter(NULL), setter(NULL) { }
+ Expression* getter;
+ Expression* setter;
+ };
+
+ protected:
+ ObjectLiteral(Isolate* isolate,
+ Handle<FixedArray> constant_properties,
+ ZoneList<Property*>* properties,
+ int literal_index,
+ bool is_simple,
+ bool fast_elements,
+ int depth,
+ bool may_store_doubles,
+ bool has_function)
+ : MaterializedLiteral(isolate, literal_index, is_simple, depth),
+ constant_properties_(constant_properties),
+ properties_(properties),
+ fast_elements_(fast_elements),
+ may_store_doubles_(may_store_doubles),
+ has_function_(has_function) {}
+
+ private:
+ Handle<FixedArray> constant_properties_;
+ ZoneList<Property*>* properties_;
+ bool fast_elements_;
+ bool may_store_doubles_;
+ bool has_function_;
+};
+
+
+// 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:
+ RegExpLiteral(Isolate* isolate,
+ Handle<String> pattern,
+ Handle<String> flags,
+ int literal_index)
+ : MaterializedLiteral(isolate, literal_index, false, 1),
+ pattern_(pattern),
+ flags_(flags) {}
+
+ private:
+ Handle<String> pattern_;
+ Handle<String> flags_;
+};
+
+// An array literal has a literals object that is used
+// 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.
+ BailoutId GetIdForElement(int i) {
+ return BailoutId(first_element_id_.ToInt() + i);
+ }
+
+ protected:
+ ArrayLiteral(Isolate* isolate,
+ Handle<FixedArray> constant_elements,
+ ZoneList<Expression*>* values,
+ int literal_index,
+ bool is_simple,
+ int depth)
+ : MaterializedLiteral(isolate, literal_index, is_simple, depth),
+ constant_elements_(constant_elements),
+ values_(values),
+ first_element_id_(ReserveIdRange(isolate, values->length())) {}
+
+ private:
+ Handle<FixedArray> constant_elements_;
+ ZoneList<Expression*>* values_;
+ const BailoutId first_element_id_;
+};
+
+
+class VariableProxy: public Expression {
+ public:
+ DECLARE_NODE_TYPE(VariableProxy)
+
+ virtual bool IsValidLeftHandSide() {
+ return var_ == NULL ? true : var_->IsValidLeftHandSide();
+ }
+
+ 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_; }
+ int position() const { return position_; }
+ Interface* interface() const { return interface_; }
+
+
+ void MarkAsTrivial() { is_trivial_ = true; }
+ void MarkAsLValue() { is_lvalue_ = true; }
+
+ // Bind this proxy to the variable var. Interfaces must match.
+ void BindTo(Variable* var);
+
+ protected:
+ VariableProxy(Isolate* isolate, Variable* var);
+
+ VariableProxy(Isolate* isolate,
+ Handle<String> name,
+ bool is_this,
+ Interface* interface,
+ int position);
+
+ 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_;
+ Interface* interface_;
+};
+
+
+class Property: public Expression {
+ public:
+ DECLARE_NODE_TYPE(Property)
+
+ virtual bool IsValidLeftHandSide() { return true; }
+
+ Expression* obj() const { return obj_; }
+ Expression* key() const { return key_; }
+ virtual int position() const { return pos_; }
+
+ BailoutId LoadId() const { return load_id_; }
+
+ bool IsStringLength() const { return is_string_length_; }
+ bool IsStringAccess() const { return is_string_access_; }
+ bool IsFunctionPrototype() const { return is_function_prototype_; }
+
+ // Type feedback information.
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle, Zone* zone);
+ virtual bool IsMonomorphic() { return is_monomorphic_; }
+ virtual SmallMapList* GetReceiverTypes() { return &receiver_types_; }
+ virtual KeyedAccessStoreMode GetStoreMode() {
+ return STANDARD_STORE;
+ }
+ bool IsUninitialized() { return is_uninitialized_; }
+ TypeFeedbackId PropertyFeedbackId() { return reuse(id()); }
+
+ protected:
+ Property(Isolate* isolate,
+ Expression* obj,
+ Expression* key,
+ int pos)
+ : Expression(isolate),
+ obj_(obj),
+ key_(key),
+ pos_(pos),
+ load_id_(GetNextId(isolate)),
+ is_monomorphic_(false),
+ is_uninitialized_(false),
+ is_string_length_(false),
+ is_string_access_(false),
+ is_function_prototype_(false) { }
+
+ private:
+ Expression* obj_;
+ Expression* key_;
+ int pos_;
+ const BailoutId load_id_;
+
+ SmallMapList receiver_types_;
+ bool is_monomorphic_ : 1;
+ bool is_uninitialized_ : 1;
+ bool is_string_length_ : 1;
+ bool is_string_access_ : 1;
+ bool is_function_prototype_ : 1;
+};
+
+
+class Call: public Expression {
+ public:
+ DECLARE_NODE_TYPE(Call)
+
+ Expression* expression() const { return expression_; }
+ ZoneList<Expression*>* arguments() const { return arguments_; }
+ virtual int position() const { return pos_; }
+
+ // Type feedback information.
+ TypeFeedbackId CallFeedbackId() const { return reuse(id()); }
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle, CallKind call_kind);
+ virtual SmallMapList* GetReceiverTypes() { return &receiver_types_; }
+ virtual bool IsMonomorphic() { return is_monomorphic_; }
+ CheckType check_type() const { return check_type_; }
+
+ void set_string_check(Handle<JSObject> holder) {
+ holder_ = holder;
+ check_type_ = STRING_CHECK;
+ }
+
+ void set_number_check(Handle<JSObject> holder) {
+ holder_ = holder;
+ check_type_ = NUMBER_CHECK;
+ }
+
+ void set_map_check() {
+ holder_ = Handle<JSObject>::null();
+ check_type_ = RECEIVER_MAP_CHECK;
+ }
+
+ Handle<JSFunction> target() { return target_; }
+
+ // A cache for the holder, set as a side effect of computing the target of the
+ // call. Note that it contains the null handle when the receiver is the same
+ // as the holder!
+ Handle<JSObject> holder() { return holder_; }
+
+ Handle<Cell> cell() { return cell_; }
+
+ bool ComputeTarget(Handle<Map> type, Handle<String> name);
+ bool ComputeGlobalTarget(Handle<GlobalObject> global, LookupResult* lookup);
+
+ BailoutId ReturnId() const { return return_id_; }
+
+ // TODO(rossberg): this should really move somewhere else (and be merged with
+ // various similar methods in objets.cc), but for now...
+ static Handle<JSObject> GetPrototypeForPrimitiveCheck(
+ CheckType check, Isolate* isolate);
+
+#ifdef DEBUG
+ // Used to assert that the FullCodeGenerator records the return site.
+ bool return_is_recorded_;
+#endif
+
+ protected:
+ 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_;
+ int pos_;
+
+ bool is_monomorphic_;
+ CheckType check_type_;
+ SmallMapList receiver_types_;
+ Handle<JSFunction> target_;
+ Handle<JSObject> holder_;
+ Handle<Cell> cell_;
+
+ const BailoutId return_id_;
+};
+
+
+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_; }
+
+ // Type feedback information.
+ TypeFeedbackId CallNewFeedbackId() const { return reuse(id()); }
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle);
+ virtual bool IsMonomorphic() { return is_monomorphic_; }
+ Handle<JSFunction> target() const { return target_; }
+ ElementsKind elements_kind() const { return elements_kind_; }
+ Handle<Cell> allocation_info_cell() const {
+ return allocation_info_cell_;
+ }
+
+ BailoutId ReturnId() const { return return_id_; }
+
+ protected:
+ CallNew(Isolate* isolate,
+ Expression* expression,
+ ZoneList<Expression*>* arguments,
+ int pos)
+ : Expression(isolate),
+ expression_(expression),
+ arguments_(arguments),
+ pos_(pos),
+ is_monomorphic_(false),
+ elements_kind_(GetInitialFastElementsKind()),
+ return_id_(GetNextId(isolate)) { }
+
+ private:
+ Expression* expression_;
+ ZoneList<Expression*>* arguments_;
+ int pos_;
+
+ bool is_monomorphic_;
+ Handle<JSFunction> target_;
+ ElementsKind elements_kind_;
+ Handle<Cell> allocation_info_cell_;
+
+ const BailoutId return_id_;
+};
+
+
+// The CallRuntime class does not represent any official JavaScript
+// language construct. Instead it is used to call a C or JS function
+// with a set of arguments. This is used from the builtins that are
+// 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; }
+
+ TypeFeedbackId CallRuntimeFeedbackId() const { return reuse(id()); }
+
+ protected:
+ CallRuntime(Isolate* isolate,
+ Handle<String> name,
+ const Runtime::Function* function,
+ ZoneList<Expression*>* arguments)
+ : Expression(isolate),
+ name_(name),
+ function_(function),
+ arguments_(arguments) { }
+
+ private:
+ Handle<String> name_;
+ const Runtime::Function* function_;
+ ZoneList<Expression*>* arguments_;
+};
+
+
+class UnaryOperation: public Expression {
+ public:
+ DECLARE_NODE_TYPE(UnaryOperation)
+
+ Token::Value op() const { return op_; }
+ Expression* expression() const { return expression_; }
+ virtual int position() const { return pos_; }
+
+ BailoutId MaterializeTrueId() { return materialize_true_id_; }
+ BailoutId MaterializeFalseId() { return materialize_false_id_; }
+
+ virtual void RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle);
+
+ protected:
+ UnaryOperation(Isolate* isolate,
+ Token::Value op,
+ Expression* expression,
+ int pos)
+ : Expression(isolate),
+ op_(op),
+ expression_(expression),
+ pos_(pos),
+ materialize_true_id_(GetNextId(isolate)),
+ materialize_false_id_(GetNextId(isolate)) {
+ ASSERT(Token::IsUnaryOp(op));
+ }
+
+ 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.
+ const BailoutId materialize_true_id_;
+ const BailoutId materialize_false_id_;
+};
+
+
+class BinaryOperation: public Expression {
+ public:
+ DECLARE_NODE_TYPE(BinaryOperation)
+
+ virtual bool ResultOverwriteAllowed();
+
+ Token::Value op() const { return op_; }
+ Expression* left() const { return left_; }
+ Expression* right() const { return right_; }
+ virtual int position() const { return pos_; }
+
+ BailoutId RightId() const { return right_id_; }
+
+ TypeFeedbackId BinaryOperationFeedbackId() const { return reuse(id()); }
+ Maybe<int> fixed_right_arg() const { return fixed_right_arg_; }
+ void set_fixed_right_arg(Maybe<int> arg) { fixed_right_arg_ = arg; }
+
+ virtual void RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle);
+
+ protected:
+ BinaryOperation(Isolate* isolate,
+ Token::Value op,
+ Expression* left,
+ Expression* right,
+ int pos)
+ : Expression(isolate),
+ op_(op),
+ left_(left),
+ right_(right),
+ pos_(pos),
+ right_id_(GetNextId(isolate)) {
+ ASSERT(Token::IsBinaryOp(op));
+ }
+
+ private:
+ Token::Value op_;
+ Expression* left_;
+ Expression* right_;
+ int pos_;
+
+ // TODO(rossberg): the fixed arg should probably be represented as a Constant
+ // type for the RHS.
+ Maybe<int> fixed_right_arg_;
+
+ // The short-circuit logical operations need an AST ID for their
+ // right-hand subexpression.
+ const BailoutId right_id_;
+};
+
+
+class CountOperation: public Expression {
+ public:
+ DECLARE_NODE_TYPE(CountOperation)
+
+ bool is_prefix() const { return is_prefix_; }
+ bool is_postfix() const { return !is_prefix_; }
+
+ Token::Value op() const { return op_; }
+ Token::Value binary_op() {
+ return (op() == Token::INC) ? Token::ADD : Token::SUB;
+ }
+
+ Expression* expression() const { return expression_; }
+ virtual int position() const { return pos_; }
+
+ virtual void MarkAsStatement() { is_prefix_ = true; }
+
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle, Zone* znoe);
+ virtual bool IsMonomorphic() { return is_monomorphic_; }
+ virtual SmallMapList* GetReceiverTypes() { return &receiver_types_; }
+ virtual KeyedAccessStoreMode GetStoreMode() {
+ return store_mode_;
+ }
+ TypeInfo type() const { return type_; }
+
+ BailoutId AssignmentId() const { return assignment_id_; }
+
+ TypeFeedbackId CountBinOpFeedbackId() const { return count_id_; }
+ TypeFeedbackId CountStoreFeedbackId() const { return reuse(id()); }
+
+ protected:
+ CountOperation(Isolate* isolate,
+ Token::Value op,
+ bool is_prefix,
+ Expression* expr,
+ int pos)
+ : Expression(isolate),
+ op_(op),
+ is_prefix_(is_prefix),
+ is_monomorphic_(false),
+ store_mode_(STANDARD_STORE),
+ expression_(expr),
+ pos_(pos),
+ assignment_id_(GetNextId(isolate)),
+ count_id_(GetNextId(isolate)) {}
+
+ private:
+ Token::Value op_;
+ bool is_prefix_ : 1;
+ bool is_monomorphic_ : 1;
+ KeyedAccessStoreMode store_mode_ : 5; // Windows treats as signed,
+ // must have extra bit.
+ TypeInfo type_;
+
+ Expression* expression_;
+ int pos_;
+ const BailoutId assignment_id_;
+ const TypeFeedbackId count_id_;
+ SmallMapList receiver_types_;
+};
+
+
+class CompareOperation: public Expression {
+ public:
+ DECLARE_NODE_TYPE(CompareOperation)
+
+ Token::Value op() const { return op_; }
+ Expression* left() const { return left_; }
+ Expression* right() const { return right_; }
+ virtual int position() const { return pos_; }
+
+ // Type feedback information.
+ TypeFeedbackId CompareOperationFeedbackId() const { return reuse(id()); }
+ Handle<Type> combined_type() const { return combined_type_; }
+ void set_combined_type(Handle<Type> type) { combined_type_ = type; }
+
+ // Match special cases.
+ bool IsLiteralCompareTypeof(Expression** expr, Handle<String>* check);
+ bool IsLiteralCompareUndefined(Expression** expr, Isolate* isolate);
+ bool IsLiteralCompareNull(Expression** expr);
+
+ protected:
+ CompareOperation(Isolate* isolate,
+ Token::Value op,
+ Expression* left,
+ Expression* right,
+ int pos)
+ : Expression(isolate),
+ op_(op),
+ left_(left),
+ right_(right),
+ pos_(pos) {
+ ASSERT(Token::IsCompareOp(op));
+ }
+
+ private:
+ Token::Value op_;
+ Expression* left_;
+ Expression* right_;
+ int pos_;
+
+ Handle<Type> combined_type_;
+};
+
+
+class Conditional: public Expression {
+ public:
+ DECLARE_NODE_TYPE(Conditional)
+
+ 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_; }
+
+ BailoutId ThenId() const { return then_id_; }
+ BailoutId ElseId() const { return else_id_; }
+
+ protected:
+ Conditional(Isolate* isolate,
+ Expression* condition,
+ Expression* then_expression,
+ Expression* else_expression,
+ int then_expression_position,
+ int else_expression_position)
+ : Expression(isolate),
+ condition_(condition),
+ then_expression_(then_expression),
+ else_expression_(else_expression),
+ then_expression_position_(then_expression_position),
+ else_expression_position_(else_expression_position),
+ then_id_(GetNextId(isolate)),
+ else_id_(GetNextId(isolate)) { }
+
+ private:
+ Expression* condition_;
+ Expression* then_expression_;
+ Expression* else_expression_;
+ int then_expression_position_;
+ int else_expression_position_;
+ const BailoutId then_id_;
+ const BailoutId else_id_;
+};
+
+
+class Assignment: public Expression {
+ public:
+ DECLARE_NODE_TYPE(Assignment)
+
+ Assignment* AsSimpleAssignment() { return !is_compound() ? this : NULL; }
+
+ Token::Value binary_op() const;
+
+ Token::Value op() const { return op_; }
+ Expression* target() const { return target_; }
+ Expression* value() const { return value_; }
+ virtual int position() const { return pos_; }
+ BinaryOperation* binary_operation() const { return binary_operation_; }
+
+ // This check relies on the definition order of token in token.h.
+ bool is_compound() const { return op() > Token::ASSIGN; }
+
+ BailoutId AssignmentId() const { return assignment_id_; }
+
+ // Type feedback information.
+ TypeFeedbackId AssignmentFeedbackId() { return reuse(id()); }
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle, Zone* zone);
+ virtual bool IsMonomorphic() { return is_monomorphic_; }
+ bool IsUninitialized() { return is_uninitialized_; }
+ virtual SmallMapList* GetReceiverTypes() { return &receiver_types_; }
+ virtual KeyedAccessStoreMode GetStoreMode() {
+ return store_mode_;
+ }
+
+ protected:
+ 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);
+ }
+ }
+
+ private:
+ Token::Value op_;
+ Expression* target_;
+ Expression* value_;
+ int pos_;
+ BinaryOperation* binary_operation_;
+ const BailoutId assignment_id_;
+
+ bool is_monomorphic_ : 1;
+ bool is_uninitialized_ : 1;
+ KeyedAccessStoreMode store_mode_ : 5; // Windows treats as signed,
+ // must have extra bit.
+ SmallMapList receiver_types_;
+};
+
+
+class Yield: public Expression {
+ public:
+ DECLARE_NODE_TYPE(Yield)
+
+ enum Kind {
+ INITIAL, // The initial yield that returns the unboxed generator object.
+ SUSPEND, // A normal yield: { value: EXPRESSION, done: false }
+ DELEGATING, // A yield*.
+ FINAL // A return: { value: EXPRESSION, done: true }
+ };
+
+ Expression* generator_object() const { return generator_object_; }
+ Expression* expression() const { return expression_; }
+ Kind yield_kind() const { return yield_kind_; }
+ virtual int position() const { return pos_; }
+
+ // Delegating yield surrounds the "yield" in a "try/catch". This index
+ // locates the catch handler in the handler table, and is equivalent to
+ // TryCatchStatement::index().
+ int index() const {
+ ASSERT(yield_kind() == DELEGATING);
+ return index_;
+ }
+ void set_index(int index) {
+ ASSERT(yield_kind() == DELEGATING);
+ index_ = index;
+ }
+
+ protected:
+ Yield(Isolate* isolate,
+ Expression* generator_object,
+ Expression* expression,
+ Kind yield_kind,
+ int pos)
+ : Expression(isolate),
+ generator_object_(generator_object),
+ expression_(expression),
+ yield_kind_(yield_kind),
+ index_(-1),
+ pos_(pos) { }
+
+ private:
+ Expression* generator_object_;
+ Expression* expression_;
+ Kind yield_kind_;
+ int index_;
+ int pos_;
+};
+
+
+class Throw: public Expression {
+ public:
+ DECLARE_NODE_TYPE(Throw)
+
+ Expression* exception() const { return exception_; }
+ virtual int position() const { return pos_; }
+
+ protected:
+ Throw(Isolate* isolate, Expression* exception, int pos)
+ : Expression(isolate), exception_(exception), pos_(pos) {}
+
+ private:
+ Expression* exception_;
+ int pos_;
+};
+
+
+class FunctionLiteral: public Expression {
+ public:
+ enum FunctionType {
+ ANONYMOUS_EXPRESSION,
+ NAMED_EXPRESSION,
+ DECLARATION
+ };
+
+ enum ParameterFlag {
+ kNoDuplicateParameters = 0,
+ kHasDuplicateParameters = 1
+ };
+
+ enum IsFunctionFlag {
+ kGlobalOrEval,
+ kIsFunction
+ };
+
+ enum IsParenthesizedFlag {
+ kIsParenthesized,
+ kNotParenthesized
+ };
+
+ enum IsGeneratorFlag {
+ kIsGenerator,
+ kNotGenerator
+ };
+
+ DECLARE_NODE_TYPE(FunctionLiteral)
+
+ Handle<String> name() const { return name_; }
+ Scope* scope() const { return scope_; }
+ 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;
+ int end_position() const;
+ int SourceSize() const { return end_position() - start_position(); }
+ 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_; }
+ int parameter_count() { return parameter_count_; }
+
+ bool AllowsLazyCompilation();
+ bool AllowsLazyCompilationWithoutContext();
+
+ Handle<String> debug_name() const {
+ if (name_->length() > 0) return name_;
+ return inferred_name();
+ }
+
+ Handle<String> inferred_name() const { return inferred_name_; }
+ void set_inferred_name(Handle<String> inferred_name) {
+ inferred_name_ = inferred_name;
+ }
+
+ bool pretenure() { return Pretenure::decode(bitfield_); }
+ void set_pretenure() { bitfield_ |= Pretenure::encode(true); }
+
+ bool has_duplicate_parameters() {
+ return HasDuplicateParameters::decode(bitfield_);
+ }
+
+ bool is_function() { return IsFunction::decode(bitfield_) == kIsFunction; }
+
+ // This is used as a heuristic on when to eagerly compile a function
+ // literal. We consider the following constructs as hints that the
+ // function will be called immediately:
+ // - (function() { ... })();
+ // - var x = function() { ... }();
+ bool is_parenthesized() {
+ return IsParenthesized::decode(bitfield_) == kIsParenthesized;
+ }
+ void set_parenthesized() {
+ bitfield_ = IsParenthesized::update(bitfield_, kIsParenthesized);
+ }
+
+ bool is_generator() {
+ return IsGenerator::decode(bitfield_) == kIsGenerator;
+ }
+
+ 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:
+ FunctionLiteral(Isolate* isolate,
+ Handle<String> name,
+ Scope* scope,
+ ZoneList<Statement*>* body,
+ int materialized_literal_count,
+ int expected_property_count,
+ int handler_count,
+ int parameter_count,
+ FunctionType function_type,
+ ParameterFlag has_duplicate_parameters,
+ IsFunctionFlag is_function,
+ IsParenthesizedFlag is_parenthesized,
+ IsGeneratorFlag is_generator)
+ : Expression(isolate),
+ name_(name),
+ scope_(scope),
+ body_(body),
+ 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_ =
+ IsExpression::encode(function_type != DECLARATION) |
+ IsAnonymous::encode(function_type == ANONYMOUS_EXPRESSION) |
+ Pretenure::encode(false) |
+ HasDuplicateParameters::encode(has_duplicate_parameters) |
+ IsFunction::encode(is_function) |
+ IsParenthesized::encode(is_parenthesized) |
+ IsGenerator::encode(is_generator);
+ }
+
+ private:
+ Handle<String> name_;
+ Scope* scope_;
+ ZoneList<Statement*>* body_;
+ Handle<String> inferred_name_;
+ AstProperties ast_properties_;
+
+ int materialized_literal_count_;
+ int expected_property_count_;
+ int handler_count_;
+ int parameter_count_;
+ int function_token_position_;
+
+ unsigned bitfield_;
+ class IsExpression: public BitField<bool, 0, 1> {};
+ class IsAnonymous: public BitField<bool, 1, 1> {};
+ class Pretenure: public BitField<bool, 2, 1> {};
+ class HasDuplicateParameters: public BitField<ParameterFlag, 3, 1> {};
+ class IsFunction: public BitField<IsFunctionFlag, 4, 1> {};
+ class IsParenthesized: public BitField<IsParenthesizedFlag, 5, 1> {};
+ class IsGenerator: public BitField<IsGeneratorFlag, 6, 1> {};
+};
+
+
+class SharedFunctionInfoLiteral: public Expression {
+ public:
+ DECLARE_NODE_TYPE(SharedFunctionInfoLiteral)
+
+ Handle<SharedFunctionInfo> shared_function_info() const {
+ return shared_function_info_;
+ }
+
+ protected:
+ SharedFunctionInfoLiteral(
+ Isolate* isolate,
+ Handle<SharedFunctionInfo> shared_function_info)
+ : Expression(isolate),
+ shared_function_info_(shared_function_info) { }
+
+ private:
+ Handle<SharedFunctionInfo> shared_function_info_;
+};
+
+
+class ThisFunction: public Expression {
+ public:
+ DECLARE_NODE_TYPE(ThisFunction)
+
+ protected:
+ explicit ThisFunction(Isolate* isolate): Expression(isolate) {}
+};
+
+#undef DECLARE_NODE_TYPE
+
+
+// ----------------------------------------------------------------------------
+// Regular expressions
+
+
+class RegExpVisitor BASE_EMBEDDED {
+ public:
+ virtual ~RegExpVisitor() { }
+#define MAKE_CASE(Name) \
+ virtual void* Visit##Name(RegExp##Name*, void* data) = 0;
+ FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
+#undef MAKE_CASE
+};
+
+
+class RegExpTree: public ZoneObject {
+ public:
+ static const int kInfinity = kMaxInt;
+ virtual ~RegExpTree() { }
+ virtual void* Accept(RegExpVisitor* visitor, void* data) = 0;
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) = 0;
+ virtual bool IsTextElement() { return false; }
+ virtual bool IsAnchoredAtStart() { return false; }
+ virtual bool IsAnchoredAtEnd() { return false; }
+ virtual int min_match() = 0;
+ virtual int max_match() = 0;
+ // Returns the interval of registers used for captures within this
+ // expression.
+ virtual Interval CaptureRegisters() { return Interval::Empty(); }
+ virtual void AppendToText(RegExpText* text, Zone* zone);
+ SmartArrayPointer<const char> ToString(Zone* zone);
+#define MAKE_ASTYPE(Name) \
+ virtual RegExp##Name* As##Name(); \
+ virtual bool Is##Name();
+ FOR_EACH_REG_EXP_TREE_TYPE(MAKE_ASTYPE)
+#undef MAKE_ASTYPE
+};
+
+
+class RegExpDisjunction: public RegExpTree {
+ public:
+ explicit RegExpDisjunction(ZoneList<RegExpTree*>* alternatives);
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpDisjunction* AsDisjunction();
+ virtual Interval CaptureRegisters();
+ virtual bool IsDisjunction();
+ virtual bool IsAnchoredAtStart();
+ virtual bool IsAnchoredAtEnd();
+ virtual int min_match() { return min_match_; }
+ virtual int max_match() { return max_match_; }
+ ZoneList<RegExpTree*>* alternatives() { return alternatives_; }
+ private:
+ ZoneList<RegExpTree*>* alternatives_;
+ int min_match_;
+ int max_match_;
+};
+
+
+class RegExpAlternative: public RegExpTree {
+ public:
+ explicit RegExpAlternative(ZoneList<RegExpTree*>* nodes);
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpAlternative* AsAlternative();
+ virtual Interval CaptureRegisters();
+ virtual bool IsAlternative();
+ virtual bool IsAnchoredAtStart();
+ virtual bool IsAnchoredAtEnd();
+ virtual int min_match() { return min_match_; }
+ virtual int max_match() { return max_match_; }
+ ZoneList<RegExpTree*>* nodes() { return nodes_; }
+ private:
+ ZoneList<RegExpTree*>* nodes_;
+ int min_match_;
+ int max_match_;
+};
+
+
+class RegExpAssertion: public RegExpTree {
+ public:
+ enum AssertionType {
+ START_OF_LINE,
+ START_OF_INPUT,
+ END_OF_LINE,
+ END_OF_INPUT,
+ BOUNDARY,
+ NON_BOUNDARY
+ };
+ explicit RegExpAssertion(AssertionType type) : assertion_type_(type) { }
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpAssertion* AsAssertion();
+ virtual bool IsAssertion();
+ virtual bool IsAnchoredAtStart();
+ virtual bool IsAnchoredAtEnd();
+ virtual int min_match() { return 0; }
+ virtual int max_match() { return 0; }
+ AssertionType assertion_type() { return assertion_type_; }
+ private:
+ AssertionType assertion_type_;
+};
+
+
+class CharacterSet BASE_EMBEDDED {
+ public:
+ explicit CharacterSet(uc16 standard_set_type)
+ : ranges_(NULL),
+ standard_set_type_(standard_set_type) {}
+ explicit CharacterSet(ZoneList<CharacterRange>* ranges)
+ : ranges_(ranges),
+ standard_set_type_(0) {}
+ ZoneList<CharacterRange>* ranges(Zone* zone);
+ uc16 standard_set_type() { return standard_set_type_; }
+ void set_standard_set_type(uc16 special_set_type) {
+ standard_set_type_ = special_set_type;
+ }
+ bool is_standard() { return standard_set_type_ != 0; }
+ void Canonicalize();
+ private:
+ ZoneList<CharacterRange>* ranges_;
+ // If non-zero, the value represents a standard set (e.g., all whitespace
+ // characters) without having to expand the ranges.
+ uc16 standard_set_type_;
+};
+
+
+class RegExpCharacterClass: public RegExpTree {
+ public:
+ RegExpCharacterClass(ZoneList<CharacterRange>* ranges, bool is_negated)
+ : set_(ranges),
+ is_negated_(is_negated) { }
+ explicit RegExpCharacterClass(uc16 type)
+ : set_(type),
+ is_negated_(false) { }
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpCharacterClass* AsCharacterClass();
+ virtual bool IsCharacterClass();
+ virtual bool IsTextElement() { return true; }
+ virtual int min_match() { return 1; }
+ virtual int max_match() { return 1; }
+ virtual void AppendToText(RegExpText* text, Zone* zone);
+ CharacterSet character_set() { return set_; }
+ // TODO(lrn): Remove need for complex version if is_standard that
+ // recognizes a mangled standard set and just do { return set_.is_special(); }
+ bool is_standard(Zone* zone);
+ // Returns a value representing the standard character set if is_standard()
+ // returns true.
+ // Currently used values are:
+ // s : unicode whitespace
+ // S : unicode non-whitespace
+ // w : ASCII word character (digit, letter, underscore)
+ // W : non-ASCII word character
+ // d : ASCII digit
+ // D : non-ASCII digit
+ // . : non-unicode non-newline
+ // * : All characters
+ uc16 standard_type() { return set_.standard_set_type(); }
+ ZoneList<CharacterRange>* ranges(Zone* zone) { return set_.ranges(zone); }
+ bool is_negated() { return is_negated_; }
+
+ private:
+ CharacterSet set_;
+ bool is_negated_;
+};
+
+
+class RegExpAtom: public RegExpTree {
+ public:
+ explicit RegExpAtom(Vector<const uc16> data) : data_(data) { }
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpAtom* AsAtom();
+ virtual bool IsAtom();
+ virtual bool IsTextElement() { return true; }
+ virtual int min_match() { return data_.length(); }
+ virtual int max_match() { return data_.length(); }
+ virtual void AppendToText(RegExpText* text, Zone* zone);
+ Vector<const uc16> data() { return data_; }
+ int length() { return data_.length(); }
+ private:
+ Vector<const uc16> data_;
+};
+
+
+class RegExpText: public RegExpTree {
+ public:
+ explicit RegExpText(Zone* zone) : elements_(2, zone), length_(0) {}
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpText* AsText();
+ virtual bool IsText();
+ virtual bool IsTextElement() { return true; }
+ virtual int min_match() { return length_; }
+ virtual int max_match() { return length_; }
+ virtual void AppendToText(RegExpText* text, Zone* zone);
+ void AddElement(TextElement elm, Zone* zone) {
+ elements_.Add(elm, zone);
+ length_ += elm.length();
+ }
+ ZoneList<TextElement>* elements() { return &elements_; }
+ private:
+ ZoneList<TextElement> elements_;
+ int length_;
+};
+
+
+class RegExpQuantifier: public RegExpTree {
+ public:
+ enum QuantifierType { GREEDY, NON_GREEDY, POSSESSIVE };
+ RegExpQuantifier(int min, int max, QuantifierType type, RegExpTree* body)
+ : body_(body),
+ min_(min),
+ max_(max),
+ min_match_(min * body->min_match()),
+ quantifier_type_(type) {
+ if (max > 0 && body->max_match() > kInfinity / max) {
+ max_match_ = kInfinity;
+ } else {
+ max_match_ = max * body->max_match();
+ }
+ }
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ static RegExpNode* ToNode(int min,
+ int max,
+ bool is_greedy,
+ RegExpTree* body,
+ RegExpCompiler* compiler,
+ RegExpNode* on_success,
+ bool not_at_start = false);
+ virtual RegExpQuantifier* AsQuantifier();
+ virtual Interval CaptureRegisters();
+ virtual bool IsQuantifier();
+ virtual int min_match() { return min_match_; }
+ virtual int max_match() { return max_match_; }
+ int min() { return min_; }
+ int max() { return max_; }
+ bool is_possessive() { return quantifier_type_ == POSSESSIVE; }
+ bool is_non_greedy() { return quantifier_type_ == NON_GREEDY; }
+ bool is_greedy() { return quantifier_type_ == GREEDY; }
+ RegExpTree* body() { return body_; }
+
+ private:
+ RegExpTree* body_;
+ int min_;
+ int max_;
+ int min_match_;
+ int max_match_;
+ QuantifierType quantifier_type_;
+};
+
+
+class RegExpCapture: public RegExpTree {
+ public:
+ explicit RegExpCapture(RegExpTree* body, int index)
+ : body_(body), index_(index) { }
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ static RegExpNode* ToNode(RegExpTree* body,
+ int index,
+ RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpCapture* AsCapture();
+ virtual bool IsAnchoredAtStart();
+ virtual bool IsAnchoredAtEnd();
+ virtual Interval CaptureRegisters();
+ virtual bool IsCapture();
+ virtual int min_match() { return body_->min_match(); }
+ virtual int max_match() { return body_->max_match(); }
+ RegExpTree* body() { return body_; }
+ int index() { return index_; }
+ static int StartRegister(int index) { return index * 2; }
+ static int EndRegister(int index) { return index * 2 + 1; }
+
+ private:
+ RegExpTree* body_;
+ int index_;
+};
+
+
+class RegExpLookahead: public RegExpTree {
+ public:
+ RegExpLookahead(RegExpTree* body,
+ bool is_positive,
+ int capture_count,
+ int capture_from)
+ : body_(body),
+ is_positive_(is_positive),
+ capture_count_(capture_count),
+ capture_from_(capture_from) { }
+
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpLookahead* AsLookahead();
+ virtual Interval CaptureRegisters();
+ virtual bool IsLookahead();
+ virtual bool IsAnchoredAtStart();
+ virtual int min_match() { return 0; }
+ virtual int max_match() { return 0; }
+ RegExpTree* body() { return body_; }
+ bool is_positive() { return is_positive_; }
+ int capture_count() { return capture_count_; }
+ int capture_from() { return capture_from_; }
+
+ private:
+ RegExpTree* body_;
+ bool is_positive_;
+ int capture_count_;
+ int capture_from_;
+};
+
+
+class RegExpBackReference: public RegExpTree {
+ public:
+ explicit RegExpBackReference(RegExpCapture* capture)
+ : capture_(capture) { }
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpBackReference* AsBackReference();
+ virtual bool IsBackReference();
+ virtual int min_match() { return 0; }
+ virtual int max_match() { return capture_->max_match(); }
+ int index() { return capture_->index(); }
+ RegExpCapture* capture() { return capture_; }
+ private:
+ RegExpCapture* capture_;
+};
+
+
+class RegExpEmpty: public RegExpTree {
+ public:
+ RegExpEmpty() { }
+ virtual void* Accept(RegExpVisitor* visitor, void* data);
+ virtual RegExpNode* ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success);
+ virtual RegExpEmpty* AsEmpty();
+ virtual bool IsEmpty();
+ virtual int min_match() { return 0; }
+ virtual int max_match() { return 0; }
+ static RegExpEmpty* GetInstance() {
+ static RegExpEmpty* instance = ::new RegExpEmpty();
+ return instance;
+ }
+};
+
+
+// ----------------------------------------------------------------------------
+// Out-of-line inline constructors (to side-step cyclic dependencies).
+
+inline ModuleVariable::ModuleVariable(VariableProxy* proxy)
+ : Module(proxy->interface()),
+ proxy_(proxy) {
+}
+
+
+// ----------------------------------------------------------------------------
+// Basic visitor
+// - leaf node visitors are abstract.
+
+class AstVisitor BASE_EMBEDDED {
+ public:
+ AstVisitor() {}
+ virtual ~AstVisitor() { }
+
+ // Stack overflow check and dynamic dispatch.
+ virtual void Visit(AstNode* node) = 0;
+
+ // Iteration left-to-right.
+ virtual void VisitDeclarations(ZoneList<Declaration*>* declarations);
+ virtual void VisitStatements(ZoneList<Statement*>* statements);
+ virtual void VisitExpressions(ZoneList<Expression*>* expressions);
+
+ // Individual AST nodes.
+#define DEF_VISIT(type) \
+ virtual void Visit##type(type* node) = 0;
+ AST_NODE_LIST(DEF_VISIT)
+#undef DEF_VISIT
+};
+
+
+#define DEFINE_AST_VISITOR_SUBCLASS_MEMBERS() \
+public: \
+ virtual void Visit(AstNode* node) { \
+ if (!CheckStackOverflow()) node->Accept(this); \
+ } \
+ \
+ void SetStackOverflow() { stack_overflow_ = true; } \
+ void ClearStackOverflow() { stack_overflow_ = false; } \
+ bool HasStackOverflow() const { return stack_overflow_; } \
+ \
+ bool CheckStackOverflow() { \
+ if (stack_overflow_) return true; \
+ StackLimitCheck check(isolate_); \
+ if (!check.HasOverflowed()) return false; \
+ return (stack_overflow_ = true); \
+ } \
+ \
+private: \
+ void InitializeAstVisitor() { \
+ isolate_ = Isolate::Current(); \
+ stack_overflow_ = false; \
+ } \
+ Isolate* isolate() { return isolate_; } \
+ \
+ Isolate* isolate_; \
+ bool stack_overflow_
+
+
+// ----------------------------------------------------------------------------
+// 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:
+ AstNodeFactory(Isolate* isolate, Zone* zone)
+ : isolate_(isolate),
+ zone_(zone) { }
+
+ Visitor* visitor() { return &visitor_; }
+
+#define VISIT_AND_RETURN(NodeType, node) \
+ visitor_.Visit##NodeType((node)); \
+ return node;
+
+ VariableDeclaration* NewVariableDeclaration(VariableProxy* proxy,
+ VariableMode mode,
+ Scope* scope) {
+ VariableDeclaration* decl =
+ new(zone_) VariableDeclaration(proxy, mode, scope);
+ VISIT_AND_RETURN(VariableDeclaration, decl)
+ }
+
+ FunctionDeclaration* NewFunctionDeclaration(VariableProxy* proxy,
+ VariableMode mode,
+ FunctionLiteral* fun,
+ Scope* scope) {
+ FunctionDeclaration* decl =
+ new(zone_) FunctionDeclaration(proxy, mode, fun, scope);
+ VISIT_AND_RETURN(FunctionDeclaration, decl)
+ }
+
+ ModuleDeclaration* NewModuleDeclaration(VariableProxy* proxy,
+ Module* module,
+ Scope* scope) {
+ ModuleDeclaration* decl =
+ new(zone_) ModuleDeclaration(proxy, module, scope);
+ VISIT_AND_RETURN(ModuleDeclaration, decl)
+ }
+
+ ImportDeclaration* NewImportDeclaration(VariableProxy* proxy,
+ Module* module,
+ Scope* scope) {
+ ImportDeclaration* decl =
+ new(zone_) ImportDeclaration(proxy, module, scope);
+ VISIT_AND_RETURN(ImportDeclaration, decl)
+ }
+
+ ExportDeclaration* NewExportDeclaration(VariableProxy* proxy,
+ Scope* scope) {
+ ExportDeclaration* decl =
+ new(zone_) ExportDeclaration(proxy, scope);
+ VISIT_AND_RETURN(ExportDeclaration, decl)
+ }
+
+ ModuleLiteral* NewModuleLiteral(Block* body, Interface* interface) {
+ ModuleLiteral* module = new(zone_) ModuleLiteral(body, interface);
+ VISIT_AND_RETURN(ModuleLiteral, module)
+ }
+
+ ModuleVariable* NewModuleVariable(VariableProxy* proxy) {
+ ModuleVariable* module = new(zone_) ModuleVariable(proxy);
+ VISIT_AND_RETURN(ModuleVariable, module)
+ }
+
+ ModulePath* NewModulePath(Module* origin, Handle<String> name) {
+ ModulePath* module = new(zone_) ModulePath(origin, name, zone_);
+ VISIT_AND_RETURN(ModulePath, module)
+ }
+
+ ModuleUrl* NewModuleUrl(Handle<String> url) {
+ ModuleUrl* module = new(zone_) ModuleUrl(url, zone_);
+ VISIT_AND_RETURN(ModuleUrl, module)
+ }
+
+ Block* NewBlock(ZoneStringList* labels,
+ int capacity,
+ bool is_initializer_block) {
+ Block* block = new(zone_) Block(
+ isolate_, labels, capacity, is_initializer_block, zone_);
+ 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(SwitchStatement)
+#undef STATEMENT_WITH_LABELS
+
+ ForEachStatement* NewForEachStatement(ForEachStatement::VisitMode visit_mode,
+ ZoneStringList* labels) {
+ switch (visit_mode) {
+ case ForEachStatement::ENUMERATE: {
+ ForInStatement* stmt = new(zone_) ForInStatement(isolate_, labels);
+ VISIT_AND_RETURN(ForInStatement, stmt);
+ }
+ case ForEachStatement::ITERATE: {
+ ForOfStatement* stmt = new(zone_) ForOfStatement(isolate_, labels);
+ VISIT_AND_RETURN(ForOfStatement, stmt);
+ }
+ }
+ UNREACHABLE();
+ return NULL;
+ }
+
+ ModuleStatement* NewModuleStatement(VariableProxy* proxy, Block* body) {
+ ModuleStatement* stmt = new(zone_) ModuleStatement(proxy, body);
+ VISIT_AND_RETURN(ModuleStatement, stmt)
+ }
+
+ 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(Scope* scope,
+ Expression* expression,
+ Statement* statement) {
+ WithStatement* stmt = new(zone_) WithStatement(
+ scope, 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 may_store_doubles,
+ bool has_function) {
+ ObjectLiteral* lit = new(zone_) ObjectLiteral(
+ isolate_, constant_properties, properties, literal_index,
+ is_simple, fast_elements, depth, may_store_doubles, 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,
+ Interface* interface = Interface::NewValue(),
+ int position = RelocInfo::kNoPosition) {
+ VariableProxy* proxy =
+ new(zone_) VariableProxy(isolate_, name, is_this, interface, 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)
+ }
+
+ Yield* NewYield(Expression *generator_object,
+ Expression* expression,
+ Yield::Kind yield_kind,
+ int pos) {
+ Yield* yield = new(zone_) Yield(
+ isolate_, generator_object, expression, yield_kind, pos);
+ VISIT_AND_RETURN(Yield, yield)
+ }
+
+ 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,
+ int parameter_count,
+ FunctionLiteral::ParameterFlag has_duplicate_parameters,
+ FunctionLiteral::FunctionType function_type,
+ FunctionLiteral::IsFunctionFlag is_function,
+ FunctionLiteral::IsParenthesizedFlag is_parenthesized,
+ FunctionLiteral::IsGeneratorFlag is_generator) {
+ FunctionLiteral* lit = new(zone_) FunctionLiteral(
+ isolate_, name, scope, body,
+ materialized_literal_count, expected_property_count, handler_count,
+ parameter_count, function_type, has_duplicate_parameters, is_function,
+ is_parenthesized, is_generator);
+ // Top-level literal doesn't count for the AST's properties.
+ if (is_function == FunctionLiteral::kIsFunction) {
+ 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/chromium/v8/src/atomicops.h b/chromium/v8/src/atomicops.h
new file mode 100644
index 00000000000..789721edfc6
--- /dev/null
+++ b/chromium/v8/src/atomicops.h
@@ -0,0 +1,170 @@
+// 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.
+
+// The routines exported by this module are subtle. If you use them, even if
+// you get the code right, it will depend on careful reasoning about atomicity
+// and memory ordering; it will be less readable, and harder to maintain. If
+// you plan to use these routines, you should have a good reason, such as solid
+// evidence that performance would otherwise suffer, or there being no
+// alternative. You should assume only properties explicitly guaranteed by the
+// specifications in this file. You are almost certainly _not_ writing code
+// just for the x86; if you assume x86 semantics, x86 hardware bugs and
+// implementations on other archtectures will cause your code to break. If you
+// do not know what you are doing, avoid these routines, and use a Mutex.
+//
+// It is incorrect to make direct assignments to/from an atomic variable.
+// You should use one of the Load or Store routines. The NoBarrier
+// versions are provided when no barriers are needed:
+// NoBarrier_Store()
+// NoBarrier_Load()
+// Although there are currently no compiler enforcement, you are encouraged
+// to use these.
+//
+
+#ifndef V8_ATOMICOPS_H_
+#define V8_ATOMICOPS_H_
+
+#include "../include/v8.h"
+#include "globals.h"
+
+namespace v8 {
+namespace internal {
+
+typedef int32_t Atomic32;
+#ifdef V8_HOST_ARCH_64_BIT
+// We need to be able to go between Atomic64 and AtomicWord implicitly. This
+// means Atomic64 and AtomicWord should be the same type on 64-bit.
+#if defined(__ILP32__) || defined(__APPLE__)
+// MacOS is an exception to the implicit conversion rule above,
+// because it uses long for intptr_t.
+typedef int64_t Atomic64;
+#else
+typedef intptr_t Atomic64;
+#endif
+#endif
+
+// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
+// Atomic64 routines below, depending on your architecture.
+#if defined(__OpenBSD__) && defined(__i386__)
+typedef Atomic32 AtomicWord;
+#else
+typedef intptr_t AtomicWord;
+#endif
+
+// Atomically execute:
+// result = *ptr;
+// if (*ptr == old_value)
+// *ptr = new_value;
+// return result;
+//
+// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
+// Always return the old value of "*ptr"
+//
+// This routine implies no memory barriers.
+Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value);
+
+// Atomically store new_value into *ptr, returning the previous value held in
+// *ptr. This routine implies no memory barriers.
+Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value);
+
+// Atomically increment *ptr by "increment". Returns the new value of
+// *ptr with the increment applied. This routine implies no memory barriers.
+Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment);
+
+Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment);
+
+// These following lower-level operations are typically useful only to people
+// implementing higher-level synchronization operations like spinlocks,
+// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or
+// a store with appropriate memory-ordering instructions. "Acquire" operations
+// ensure that no later memory access can be reordered ahead of the operation.
+// "Release" operations ensure that no previous memory access can be reordered
+// after the operation. "Barrier" operations have both "Acquire" and "Release"
+// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
+// access.
+Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value);
+Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value);
+
+void MemoryBarrier();
+void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
+void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
+void Release_Store(volatile Atomic32* ptr, Atomic32 value);
+
+Atomic32 NoBarrier_Load(volatile const Atomic32* ptr);
+Atomic32 Acquire_Load(volatile const Atomic32* ptr);
+Atomic32 Release_Load(volatile const Atomic32* ptr);
+
+// 64-bit atomic operations (only available on 64-bit processors).
+#ifdef V8_HOST_ARCH_64_BIT
+Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value);
+Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value);
+Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
+Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
+
+Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value);
+Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value);
+void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value);
+void Acquire_Store(volatile Atomic64* ptr, Atomic64 value);
+void Release_Store(volatile Atomic64* ptr, Atomic64 value);
+Atomic64 NoBarrier_Load(volatile const Atomic64* ptr);
+Atomic64 Acquire_Load(volatile const Atomic64* ptr);
+Atomic64 Release_Load(volatile const Atomic64* ptr);
+#endif // V8_HOST_ARCH_64_BIT
+
+} } // namespace v8::internal
+
+// Include our platform specific implementation.
+#if defined(THREAD_SANITIZER)
+#include "atomicops_internals_tsan.h"
+#elif defined(_MSC_VER) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
+#include "atomicops_internals_x86_msvc.h"
+#elif defined(__APPLE__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
+#include "atomicops_internals_x86_macosx.h"
+#elif defined(__GNUC__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
+#include "atomicops_internals_x86_gcc.h"
+#elif defined(__GNUC__) && V8_HOST_ARCH_ARM
+#include "atomicops_internals_arm_gcc.h"
+#elif defined(__GNUC__) && V8_HOST_ARCH_MIPS
+#include "atomicops_internals_mips_gcc.h"
+#else
+#error "Atomic operations are not supported on your platform"
+#endif
+
+#endif // V8_ATOMICOPS_H_
diff --git a/chromium/v8/src/atomicops_internals_arm_gcc.h b/chromium/v8/src/atomicops_internals_arm_gcc.h
new file mode 100644
index 00000000000..6c30256d93d
--- /dev/null
+++ b/chromium/v8/src/atomicops_internals_arm_gcc.h
@@ -0,0 +1,145 @@
+// 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.
+
+// This file is an internal atomic implementation, use atomicops.h instead.
+//
+// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
+
+#ifndef V8_ATOMICOPS_INTERNALS_ARM_GCC_H_
+#define V8_ATOMICOPS_INTERNALS_ARM_GCC_H_
+
+namespace v8 {
+namespace internal {
+
+// 0xffff0fc0 is the hard coded address of a function provided by
+// the kernel which implements an atomic compare-exchange. On older
+// ARM architecture revisions (pre-v6) this may be implemented using
+// a syscall. This address is stable, and in active use (hard coded)
+// by at least glibc-2.7 and the Android C library.
+typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value,
+ Atomic32 new_value,
+ volatile Atomic32* ptr);
+LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg __attribute__((weak)) =
+ (LinuxKernelCmpxchgFunc) 0xffff0fc0;
+
+typedef void (*LinuxKernelMemoryBarrierFunc)(void);
+LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
+ (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
+
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev_value = *ptr;
+ do {
+ if (!pLinuxKernelCmpxchg(old_value, new_value,
+ const_cast<Atomic32*>(ptr))) {
+ return old_value;
+ }
+ prev_value = *ptr;
+ } while (prev_value == old_value);
+ return prev_value;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ Atomic32 old_value;
+ do {
+ old_value = *ptr;
+ } while (pLinuxKernelCmpxchg(old_value, new_value,
+ const_cast<Atomic32*>(ptr)));
+ return old_value;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ for (;;) {
+ // Atomic exchange the old value with an incremented one.
+ Atomic32 old_value = *ptr;
+ Atomic32 new_value = old_value + increment;
+ if (pLinuxKernelCmpxchg(old_value, new_value,
+ const_cast<Atomic32*>(ptr)) == 0) {
+ // The exchange took place as expected.
+ return new_value;
+ }
+ // Otherwise, *ptr changed mid-loop and we need to retry.
+ }
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+}
+
+inline void MemoryBarrier() {
+ pLinuxKernelMemoryBarrier();
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+ MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ MemoryBarrier();
+ *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ Atomic32 value = *ptr;
+ MemoryBarrier();
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_ATOMICOPS_INTERNALS_ARM_GCC_H_
diff --git a/chromium/v8/src/atomicops_internals_mips_gcc.h b/chromium/v8/src/atomicops_internals_mips_gcc.h
new file mode 100644
index 00000000000..cb8f8b9d954
--- /dev/null
+++ b/chromium/v8/src/atomicops_internals_mips_gcc.h
@@ -0,0 +1,174 @@
+// 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.
+
+// This file is an internal atomic implementation, use atomicops.h instead.
+
+#ifndef V8_ATOMICOPS_INTERNALS_MIPS_GCC_H_
+#define V8_ATOMICOPS_INTERNALS_MIPS_GCC_H_
+
+namespace v8 {
+namespace internal {
+
+// Atomically execute:
+// result = *ptr;
+// if (*ptr == old_value)
+// *ptr = new_value;
+// return result;
+//
+// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
+// Always return the old value of "*ptr"
+//
+// This routine implies no memory barriers.
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ 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
+ "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"
+ ".set pop\n"
+ : "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
+ : "Ir" (old_value), "r" (new_value), "m" (*ptr)
+ : "memory");
+ return prev;
+}
+
+// Atomically store new_value into *ptr, returning the previous value held in
+// *ptr. This routine implies no memory barriers.
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ Atomic32 temp, old;
+ __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");
+
+ return old;
+}
+
+// Atomically increment *ptr by "increment". Returns the new value of
+// *ptr with the increment applied. This routine implies no memory barriers.
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ Atomic32 temp, temp2;
+
+ __asm__ __volatile__(".set push\n"
+ ".set noreorder\n"
+ "1:\n"
+ "ll %0, %2\n" // temp = *ptr
+ "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");
+ // temp2 now holds the final value.
+ return temp2;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ MemoryBarrier();
+ Atomic32 res = NoBarrier_AtomicIncrement(ptr, increment);
+ MemoryBarrier();
+ return res;
+}
+
+// "Acquire" operations
+// ensure that no later memory access can be reordered ahead of the operation.
+// "Release" operations ensure that no previous memory access can be reordered
+// after the operation. "Barrier" operations have both "Acquire" and "Release"
+// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
+// access.
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ MemoryBarrier();
+ return res;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ MemoryBarrier();
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+}
+
+inline void MemoryBarrier() {
+ __asm__ __volatile__("sync" : : : "memory");
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+ MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ MemoryBarrier();
+ *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ Atomic32 value = *ptr;
+ MemoryBarrier();
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_ATOMICOPS_INTERNALS_MIPS_GCC_H_
diff --git a/chromium/v8/src/atomicops_internals_tsan.h b/chromium/v8/src/atomicops_internals_tsan.h
new file mode 100644
index 00000000000..b5162bad9f6
--- /dev/null
+++ b/chromium/v8/src/atomicops_internals_tsan.h
@@ -0,0 +1,400 @@
+// Copyright 2012 the V8 project authors. 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 is an internal atomic implementation for compiler-based
+// ThreadSanitizer. Use base/atomicops.h instead.
+
+#ifndef V8_ATOMICOPS_INTERNALS_TSAN_H_
+#define V8_ATOMICOPS_INTERNALS_TSAN_H_
+
+namespace v8 {
+namespace internal {
+
+#ifndef TSAN_INTERFACE_ATOMIC_H
+#define TSAN_INTERFACE_ATOMIC_H
+
+// This struct is not part of the public API of this module; clients may not
+// use it. (However, it's exported via BASE_EXPORT because clients implicitly
+// do use it at link time by inlining these functions.)
+// Features of this x86. Values may not be correct before main() is run,
+// but are set conservatively.
+struct AtomicOps_x86CPUFeatureStruct {
+ bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence
+ // after acquire compare-and-swap.
+ bool has_sse2; // Processor has SSE2.
+};
+extern struct AtomicOps_x86CPUFeatureStruct
+ AtomicOps_Internalx86CPUFeatures;
+
+#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef char __tsan_atomic8;
+typedef short __tsan_atomic16; // NOLINT
+typedef int __tsan_atomic32;
+typedef long __tsan_atomic64; // NOLINT
+
+#if defined(__SIZEOF_INT128__) \
+ || (__clang_major__ * 100 + __clang_minor__ >= 302)
+typedef __int128 __tsan_atomic128;
+#define __TSAN_HAS_INT128 1
+#else
+typedef char __tsan_atomic128;
+#define __TSAN_HAS_INT128 0
+#endif
+
+typedef enum {
+ __tsan_memory_order_relaxed,
+ __tsan_memory_order_consume,
+ __tsan_memory_order_acquire,
+ __tsan_memory_order_release,
+ __tsan_memory_order_acq_rel,
+ __tsan_memory_order_seq_cst,
+} __tsan_memory_order;
+
+__tsan_atomic8 __tsan_atomic8_load(const volatile __tsan_atomic8 *a,
+ __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_load(const volatile __tsan_atomic16 *a,
+ __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_load(const volatile __tsan_atomic32 *a,
+ __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_load(const volatile __tsan_atomic64 *a,
+ __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_load(const volatile __tsan_atomic128 *a,
+ __tsan_memory_order mo);
+
+void __tsan_atomic8_store(volatile __tsan_atomic8 *a, __tsan_atomic8 v,
+ __tsan_memory_order mo);
+void __tsan_atomic16_store(volatile __tsan_atomic16 *a, __tsan_atomic16 v,
+ __tsan_memory_order mo);
+void __tsan_atomic32_store(volatile __tsan_atomic32 *a, __tsan_atomic32 v,
+ __tsan_memory_order mo);
+void __tsan_atomic64_store(volatile __tsan_atomic64 *a, __tsan_atomic64 v,
+ __tsan_memory_order mo);
+void __tsan_atomic128_store(volatile __tsan_atomic128 *a, __tsan_atomic128 v,
+ __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_exchange(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_exchange(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_exchange(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_exchange(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_exchange(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_add(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_add(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_add(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_add(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_add(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_and(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_and(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_and(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_and(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_and(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_or(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_or(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_or(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_or(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_or(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_xor(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_xor(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_xor(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_xor(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_xor(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo);
+
+__tsan_atomic8 __tsan_atomic8_fetch_nand(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo);
+__tsan_atomic16 __tsan_atomic16_fetch_nand(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo);
+__tsan_atomic32 __tsan_atomic32_fetch_nand(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo);
+__tsan_atomic64 __tsan_atomic64_fetch_nand(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+__tsan_atomic128 __tsan_atomic128_fetch_nand(volatile __tsan_atomic128 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo);
+
+int __tsan_atomic8_compare_exchange_weak(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic16_compare_exchange_weak(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic32_compare_exchange_weak(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic64_compare_exchange_weak(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic128_compare_exchange_weak(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+
+int __tsan_atomic8_compare_exchange_strong(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic16_compare_exchange_strong(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic32_compare_exchange_strong(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic64_compare_exchange_strong(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+int __tsan_atomic128_compare_exchange_strong(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo);
+
+__tsan_atomic8 __tsan_atomic8_compare_exchange_val(
+ volatile __tsan_atomic8 *a, __tsan_atomic8 c, __tsan_atomic8 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+__tsan_atomic16 __tsan_atomic16_compare_exchange_val(
+ volatile __tsan_atomic16 *a, __tsan_atomic16 c, __tsan_atomic16 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+__tsan_atomic32 __tsan_atomic32_compare_exchange_val(
+ volatile __tsan_atomic32 *a, __tsan_atomic32 c, __tsan_atomic32 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+__tsan_atomic64 __tsan_atomic64_compare_exchange_val(
+ volatile __tsan_atomic64 *a, __tsan_atomic64 c, __tsan_atomic64 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+__tsan_atomic128 __tsan_atomic128_compare_exchange_val(
+ volatile __tsan_atomic128 *a, __tsan_atomic128 c, __tsan_atomic128 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+
+void __tsan_atomic_thread_fence(__tsan_memory_order mo);
+void __tsan_atomic_signal_fence(__tsan_memory_order mo);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // #ifndef TSAN_INTERFACE_ATOMIC_H
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 cmp = old_value;
+ __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_relaxed, __tsan_memory_order_relaxed);
+ return cmp;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
+ Atomic32 new_value) {
+ return __tsan_atomic32_exchange(ptr, new_value,
+ __tsan_memory_order_relaxed);
+}
+
+inline Atomic32 Acquire_AtomicExchange(volatile Atomic32 *ptr,
+ Atomic32 new_value) {
+ return __tsan_atomic32_exchange(ptr, new_value,
+ __tsan_memory_order_acquire);
+}
+
+inline Atomic32 Release_AtomicExchange(volatile Atomic32 *ptr,
+ Atomic32 new_value) {
+ return __tsan_atomic32_exchange(ptr, new_value,
+ __tsan_memory_order_release);
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr,
+ Atomic32 increment) {
+ return increment + __tsan_atomic32_fetch_add(ptr, increment,
+ __tsan_memory_order_relaxed);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr,
+ Atomic32 increment) {
+ return increment + __tsan_atomic32_fetch_add(ptr, increment,
+ __tsan_memory_order_acq_rel);
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 cmp = old_value;
+ __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_acquire, __tsan_memory_order_acquire);
+ return cmp;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 cmp = old_value;
+ __tsan_atomic32_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_release, __tsan_memory_order_relaxed);
+ return cmp;
+}
+
+inline void NoBarrier_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ __tsan_atomic32_store(ptr, value, __tsan_memory_order_relaxed);
+}
+
+inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ __tsan_atomic32_store(ptr, value, __tsan_memory_order_relaxed);
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+}
+
+inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
+ __tsan_atomic32_store(ptr, value, __tsan_memory_order_release);
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32 *ptr) {
+ return __tsan_atomic32_load(ptr, __tsan_memory_order_relaxed);
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
+ return __tsan_atomic32_load(ptr, __tsan_memory_order_acquire);
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+ return __tsan_atomic32_load(ptr, __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 cmp = old_value;
+ __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_relaxed, __tsan_memory_order_relaxed);
+ return cmp;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
+ Atomic64 new_value) {
+ return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 Acquire_AtomicExchange(volatile Atomic64 *ptr,
+ Atomic64 new_value) {
+ return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_acquire);
+}
+
+inline Atomic64 Release_AtomicExchange(volatile Atomic64 *ptr,
+ Atomic64 new_value) {
+ return __tsan_atomic64_exchange(ptr, new_value, __tsan_memory_order_release);
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr,
+ Atomic64 increment) {
+ return increment + __tsan_atomic64_fetch_add(ptr, increment,
+ __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr,
+ Atomic64 increment) {
+ return increment + __tsan_atomic64_fetch_add(ptr, increment,
+ __tsan_memory_order_acq_rel);
+}
+
+inline void NoBarrier_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ __tsan_atomic64_store(ptr, value, __tsan_memory_order_relaxed);
+}
+
+inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ __tsan_atomic64_store(ptr, value, __tsan_memory_order_relaxed);
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+}
+
+inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
+ __tsan_atomic64_store(ptr, value, __tsan_memory_order_release);
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64 *ptr) {
+ return __tsan_atomic64_load(ptr, __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
+ return __tsan_atomic64_load(ptr, __tsan_memory_order_acquire);
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64 *ptr) {
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+ return __tsan_atomic64_load(ptr, __tsan_memory_order_relaxed);
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 cmp = old_value;
+ __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_acquire, __tsan_memory_order_acquire);
+ return cmp;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 cmp = old_value;
+ __tsan_atomic64_compare_exchange_strong(ptr, &cmp, new_value,
+ __tsan_memory_order_release, __tsan_memory_order_relaxed);
+ return cmp;
+}
+
+inline void MemoryBarrier() {
+ __tsan_atomic_thread_fence(__tsan_memory_order_seq_cst);
+}
+
+} // namespace internal
+} // namespace v8
+
+#undef ATOMICOPS_COMPILER_BARRIER
+
+#endif // V8_ATOMICOPS_INTERNALS_TSAN_H_
diff --git a/chromium/v8/src/atomicops_internals_x86_gcc.cc b/chromium/v8/src/atomicops_internals_x86_gcc.cc
new file mode 100644
index 00000000000..950b423f413
--- /dev/null
+++ b/chromium/v8/src/atomicops_internals_x86_gcc.cc
@@ -0,0 +1,135 @@
+// 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.
+
+// This module gets enough CPU information to optimize the
+// atomicops module on x86.
+
+#include <string.h>
+
+#include "atomicops.h"
+#include "platform.h"
+
+// This file only makes sense with atomicops_internals_x86_gcc.h -- it
+// depends on structs that are defined in that file. If atomicops.h
+// doesn't sub-include that file, then we aren't needed, and shouldn't
+// try to do anything.
+#ifdef V8_ATOMICOPS_INTERNALS_X86_GCC_H_
+
+// Inline cpuid instruction. In PIC compilations, %ebx contains the address
+// of the global offset table. To avoid breaking such executables, this code
+// must preserve that register's value across cpuid instructions.
+#if defined(__i386__)
+#define cpuid(a, b, c, d, inp) \
+ asm("mov %%ebx, %%edi\n" \
+ "cpuid\n" \
+ "xchg %%edi, %%ebx\n" \
+ : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
+#elif defined(__x86_64__)
+#define cpuid(a, b, c, d, inp) \
+ asm("mov %%rbx, %%rdi\n" \
+ "cpuid\n" \
+ "xchg %%rdi, %%rbx\n" \
+ : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
+#endif
+
+#if defined(cpuid) // initialize the struct only on x86
+
+namespace v8 {
+namespace internal {
+
+// Set the flags so that code will run correctly and conservatively, so even
+// if we haven't been initialized yet, we're probably single threaded, and our
+// default values should hopefully be pretty safe.
+struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = {
+ false, // bug can't exist before process spawns multiple threads
+ false, // no SSE2
+};
+
+} } // namespace v8::internal
+
+namespace {
+
+// Initialize the AtomicOps_Internalx86CPUFeatures struct.
+void AtomicOps_Internalx86CPUFeaturesInit() {
+ using v8::internal::AtomicOps_Internalx86CPUFeatures;
+
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+
+ // Get vendor string (issue CPUID with eax = 0)
+ cpuid(eax, ebx, ecx, edx, 0);
+ char vendor[13];
+ v8::internal::OS::MemCopy(vendor, &ebx, 4);
+ v8::internal::OS::MemCopy(vendor + 4, &edx, 4);
+ v8::internal::OS::MemCopy(vendor + 8, &ecx, 4);
+ vendor[12] = 0;
+
+ // get feature flags in ecx/edx, and family/model in eax
+ cpuid(eax, ebx, ecx, edx, 1);
+
+ int family = (eax >> 8) & 0xf; // family and model fields
+ int model = (eax >> 4) & 0xf;
+ if (family == 0xf) { // use extended family and model fields
+ family += (eax >> 20) & 0xff;
+ model += ((eax >> 16) & 0xf) << 4;
+ }
+
+ // Opteron Rev E has a bug in which on very rare occasions a locked
+ // instruction doesn't act as a read-acquire barrier if followed by a
+ // non-locked read-modify-write instruction. Rev F has this bug in
+ // pre-release versions, but not in versions released to customers,
+ // so we test only for Rev E, which is family 15, model 32..63 inclusive.
+ if (strcmp(vendor, "AuthenticAMD") == 0 && // AMD
+ family == 15 &&
+ 32 <= model && model <= 63) {
+ AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = true;
+ } else {
+ AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = false;
+ }
+
+ // edx bit 26 is SSE2 which we use to tell use whether we can use mfence
+ AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1);
+}
+
+class AtomicOpsx86Initializer {
+ public:
+ AtomicOpsx86Initializer() {
+ AtomicOps_Internalx86CPUFeaturesInit();
+ }
+};
+
+
+// A global to get use initialized on startup via static initialization :/
+AtomicOpsx86Initializer g_initer;
+
+} // namespace
+
+#endif // if x86
+
+#endif // ifdef V8_ATOMICOPS_INTERNALS_X86_GCC_H_
diff --git a/chromium/v8/src/atomicops_internals_x86_gcc.h b/chromium/v8/src/atomicops_internals_x86_gcc.h
new file mode 100644
index 00000000000..e58d598fbd4
--- /dev/null
+++ b/chromium/v8/src/atomicops_internals_x86_gcc.h
@@ -0,0 +1,287 @@
+// 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.
+
+// This file is an internal atomic implementation, use atomicops.h instead.
+
+#ifndef V8_ATOMICOPS_INTERNALS_X86_GCC_H_
+#define V8_ATOMICOPS_INTERNALS_X86_GCC_H_
+
+namespace v8 {
+namespace internal {
+
+// This struct is not part of the public API of this module; clients may not
+// use it.
+// Features of this x86. Values may not be correct before main() is run,
+// but are set conservatively.
+struct AtomicOps_x86CPUFeatureStruct {
+ bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence
+ // after acquire compare-and-swap.
+ bool has_sse2; // Processor has SSE2.
+};
+extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures;
+
+#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
+
+// 32-bit low-level operations on any platform.
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev;
+ __asm__ __volatile__("lock; cmpxchgl %1,%2"
+ : "=a" (prev)
+ : "q" (new_value), "m" (*ptr), "0" (old_value)
+ : "memory");
+ return prev;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ __asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg.
+ : "=r" (new_value)
+ : "m" (*ptr), "0" (new_value)
+ : "memory");
+ return new_value; // Now it's the previous value.
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ Atomic32 temp = increment;
+ __asm__ __volatile__("lock; xaddl %0,%1"
+ : "+r" (temp), "+m" (*ptr)
+ : : "memory");
+ // temp now holds the old value of *ptr
+ return temp + increment;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ Atomic32 temp = increment;
+ __asm__ __volatile__("lock; xaddl %0,%1"
+ : "+r" (temp), "+m" (*ptr)
+ : : "memory");
+ // temp now holds the old value of *ptr
+ if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+ __asm__ __volatile__("lfence" : : : "memory");
+ }
+ return temp + increment;
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+ __asm__ __volatile__("lfence" : : : "memory");
+ }
+ return x;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+}
+
+#if defined(__x86_64__)
+
+// 64-bit implementations of memory barrier can be simpler, because it
+// "mfence" is guaranteed to exist.
+inline void MemoryBarrier() {
+ __asm__ __volatile__("mfence" : : : "memory");
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+ MemoryBarrier();
+}
+
+#else
+
+inline void MemoryBarrier() {
+ if (AtomicOps_Internalx86CPUFeatures.has_sse2) {
+ __asm__ __volatile__("mfence" : : : "memory");
+ } else { // mfence is faster but not present on PIII
+ Atomic32 x = 0;
+ NoBarrier_AtomicExchange(&x, 0); // acts as a barrier on PIII
+ }
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ if (AtomicOps_Internalx86CPUFeatures.has_sse2) {
+ *ptr = value;
+ __asm__ __volatile__("mfence" : : : "memory");
+ } else {
+ NoBarrier_AtomicExchange(ptr, value);
+ // acts as a barrier on PIII
+ }
+}
+#endif
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ ATOMICOPS_COMPILER_BARRIER();
+ *ptr = value; // An x86 store acts as a release barrier.
+ // See comments in Atomic64 version of Release_Store(), below.
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ Atomic32 value = *ptr; // An x86 load acts as a acquire barrier.
+ // See comments in Atomic64 version of Release_Store(), below.
+ ATOMICOPS_COMPILER_BARRIER();
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+#if defined(__x86_64__) && defined(V8_HOST_ARCH_64_BIT)
+
+// 64-bit low-level operations on 64-bit platform.
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 prev;
+ __asm__ __volatile__("lock; cmpxchgq %1,%2"
+ : "=a" (prev)
+ : "q" (new_value), "m" (*ptr), "0" (old_value)
+ : "memory");
+ return prev;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ __asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg.
+ : "=r" (new_value)
+ : "m" (*ptr), "0" (new_value)
+ : "memory");
+ return new_value; // Now it's the previous value.
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ Atomic64 temp = increment;
+ __asm__ __volatile__("lock; xaddq %0,%1"
+ : "+r" (temp), "+m" (*ptr)
+ : : "memory");
+ // temp now contains the previous value of *ptr
+ return temp + increment;
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ Atomic64 temp = increment;
+ __asm__ __volatile__("lock; xaddq %0,%1"
+ : "+r" (temp), "+m" (*ptr)
+ : : "memory");
+ // temp now contains the previous value of *ptr
+ if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+ __asm__ __volatile__("lfence" : : : "memory");
+ }
+ return temp + increment;
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+ *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+ *ptr = value;
+ MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+ ATOMICOPS_COMPILER_BARRIER();
+
+ *ptr = value; // An x86 store acts as a release barrier
+ // for current AMD/Intel chips as of Jan 2008.
+ // See also Acquire_Load(), below.
+
+ // When new chips come out, check:
+ // IA-32 Intel Architecture Software Developer's Manual, Volume 3:
+ // System Programming Guide, Chatper 7: Multiple-processor management,
+ // Section 7.2, Memory Ordering.
+ // Last seen at:
+ // http://developer.intel.com/design/pentium4/manuals/index_new.htm
+ //
+ // x86 stores/loads fail to act as barriers for a few instructions (clflush
+ // maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are
+ // not generated by the compiler, and are rare. Users of these instructions
+ // need to know about cache behaviour in any case since all of these involve
+ // either flushing cache lines or non-temporal cache hints.
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ Atomic64 value = *ptr; // An x86 load acts as a acquire barrier,
+ // for current AMD/Intel chips as of Jan 2008.
+ // See also Release_Store(), above.
+ ATOMICOPS_COMPILER_BARRIER();
+ return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
+ __asm__ __volatile__("lfence" : : : "memory");
+ }
+ return x;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+#endif // defined(__x86_64__)
+
+} } // namespace v8::internal
+
+#undef ATOMICOPS_COMPILER_BARRIER
+
+#endif // V8_ATOMICOPS_INTERNALS_X86_GCC_H_
diff --git a/chromium/v8/src/atomicops_internals_x86_macosx.h b/chromium/v8/src/atomicops_internals_x86_macosx.h
new file mode 100644
index 00000000000..bfb02b3851f
--- /dev/null
+++ b/chromium/v8/src/atomicops_internals_x86_macosx.h
@@ -0,0 +1,301 @@
+// 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.
+
+// This file is an internal atomic implementation, use atomicops.h instead.
+
+#ifndef V8_ATOMICOPS_INTERNALS_X86_MACOSX_H_
+#define V8_ATOMICOPS_INTERNALS_X86_MACOSX_H_
+
+#include <libkern/OSAtomic.h>
+
+namespace v8 {
+namespace internal {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev_value;
+ do {
+ if (OSAtomicCompareAndSwap32(old_value, new_value,
+ const_cast<Atomic32*>(ptr))) {
+ return old_value;
+ }
+ prev_value = *ptr;
+ } while (prev_value == old_value);
+ return prev_value;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ Atomic32 old_value;
+ do {
+ old_value = *ptr;
+ } while (!OSAtomicCompareAndSwap32(old_value, new_value,
+ const_cast<Atomic32*>(ptr)));
+ return old_value;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return OSAtomicAdd32(increment, const_cast<Atomic32*>(ptr));
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr));
+}
+
+inline void MemoryBarrier() {
+ OSMemoryBarrier();
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev_value;
+ do {
+ if (OSAtomicCompareAndSwap32Barrier(old_value, new_value,
+ const_cast<Atomic32*>(ptr))) {
+ return old_value;
+ }
+ prev_value = *ptr;
+ } while (prev_value == old_value);
+ return prev_value;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return Acquire_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+ MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ MemoryBarrier();
+ *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ Atomic32 value = *ptr;
+ MemoryBarrier();
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+#ifdef __LP64__
+
+// 64-bit implementation on 64-bit platform
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 prev_value;
+ do {
+ if (OSAtomicCompareAndSwap64(old_value, new_value,
+ const_cast<Atomic64*>(ptr))) {
+ return old_value;
+ }
+ prev_value = *ptr;
+ } while (prev_value == old_value);
+ return prev_value;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ Atomic64 old_value;
+ do {
+ old_value = *ptr;
+ } while (!OSAtomicCompareAndSwap64(old_value, new_value,
+ const_cast<Atomic64*>(ptr)));
+ return old_value;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return OSAtomicAdd64(increment, const_cast<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,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 prev_value;
+ do {
+ if (OSAtomicCompareAndSwap64Barrier(old_value, new_value,
+ const_cast<Atomic64*>(ptr))) {
+ return old_value;
+ }
+ prev_value = *ptr;
+ } while (prev_value == old_value);
+ return prev_value;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ // The lib kern interface does not distinguish between
+ // Acquire and Release memory barriers; they are equivalent.
+ return Acquire_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+ *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+ *ptr = value;
+ MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+ MemoryBarrier();
+ *ptr = value;
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ Atomic64 value = *ptr;
+ MemoryBarrier();
+ return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+#endif // defined(__LP64__)
+
+// MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different
+// on the Mac, even when they are the same size. We need to explicitly cast
+// from AtomicWord to Atomic32/64 to implement the AtomicWord interface.
+#ifdef __LP64__
+#define AtomicWordCastType Atomic64
+#else
+#define AtomicWordCastType Atomic32
+#endif
+
+inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr,
+ AtomicWord old_value,
+ AtomicWord new_value) {
+ return NoBarrier_CompareAndSwap(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr),
+ old_value, new_value);
+}
+
+inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr,
+ AtomicWord new_value) {
+ return NoBarrier_AtomicExchange(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr), new_value);
+}
+
+inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr,
+ AtomicWord increment) {
+ return NoBarrier_AtomicIncrement(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr), increment);
+}
+
+inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr,
+ AtomicWord increment) {
+ return Barrier_AtomicIncrement(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr), increment);
+}
+
+inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr,
+ AtomicWord old_value,
+ AtomicWord new_value) {
+ return v8::internal::Acquire_CompareAndSwap(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr),
+ old_value, new_value);
+}
+
+inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr,
+ AtomicWord old_value,
+ AtomicWord new_value) {
+ return v8::internal::Release_CompareAndSwap(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr),
+ old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile AtomicWord* ptr, AtomicWord value) {
+ NoBarrier_Store(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
+}
+
+inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) {
+ return v8::internal::Acquire_Store(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
+}
+
+inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) {
+ return v8::internal::Release_Store(
+ reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
+}
+
+inline AtomicWord NoBarrier_Load(volatile const AtomicWord* ptr) {
+ return NoBarrier_Load(
+ reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
+}
+
+inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) {
+ return v8::internal::Acquire_Load(
+ reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
+}
+
+inline AtomicWord Release_Load(volatile const AtomicWord* ptr) {
+ return v8::internal::Release_Load(
+ reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
+}
+
+#undef AtomicWordCastType
+
+} } // namespace v8::internal
+
+#endif // V8_ATOMICOPS_INTERNALS_X86_MACOSX_H_
diff --git a/chromium/v8/src/atomicops_internals_x86_msvc.h b/chromium/v8/src/atomicops_internals_x86_msvc.h
new file mode 100644
index 00000000000..fcf6a651077
--- /dev/null
+++ b/chromium/v8/src/atomicops_internals_x86_msvc.h
@@ -0,0 +1,203 @@
+// 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.
+
+// This file is an internal atomic implementation, use atomicops.h instead.
+
+#ifndef V8_ATOMICOPS_INTERNALS_X86_MSVC_H_
+#define V8_ATOMICOPS_INTERNALS_X86_MSVC_H_
+
+#include "checks.h"
+#include "win32-headers.h"
+
+namespace v8 {
+namespace internal {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ LONG result = InterlockedCompareExchange(
+ reinterpret_cast<volatile LONG*>(ptr),
+ static_cast<LONG>(new_value),
+ static_cast<LONG>(old_value));
+ return static_cast<Atomic32>(result);
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ LONG result = InterlockedExchange(
+ reinterpret_cast<volatile LONG*>(ptr),
+ static_cast<LONG>(new_value));
+ return static_cast<Atomic32>(result);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return InterlockedExchangeAdd(
+ reinterpret_cast<volatile LONG*>(ptr),
+ static_cast<LONG>(increment)) + increment;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return Barrier_AtomicIncrement(ptr, increment);
+}
+
+#if !(defined(_MSC_VER) && _MSC_VER >= 1400)
+#error "We require at least vs2005 for MemoryBarrier"
+#endif
+inline void MemoryBarrier() {
+ // We use MemoryBarrier from WinNT.h
+ ::MemoryBarrier();
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ NoBarrier_AtomicExchange(ptr, value);
+ // acts as a barrier in this implementation
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value; // works w/o barrier for current Intel chips as of June 2005
+ // See comments in Atomic64 version of Release_Store() below.
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ Atomic32 value = *ptr;
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+#if defined(_WIN64)
+
+// 64-bit low-level operations on 64-bit platform.
+
+STATIC_ASSERT(sizeof(Atomic64) == sizeof(PVOID));
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ PVOID result = InterlockedCompareExchangePointer(
+ reinterpret_cast<volatile PVOID*>(ptr),
+ reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
+ return reinterpret_cast<Atomic64>(result);
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ PVOID result = InterlockedExchangePointer(
+ reinterpret_cast<volatile PVOID*>(ptr),
+ reinterpret_cast<PVOID>(new_value));
+ return reinterpret_cast<Atomic64>(result);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return InterlockedExchangeAdd64(
+ reinterpret_cast<volatile LONGLONG*>(ptr),
+ static_cast<LONGLONG>(increment)) + increment;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+ *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+ NoBarrier_AtomicExchange(ptr, value);
+ // acts as a barrier in this implementation
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+ *ptr = value; // works w/o barrier for current Intel chips as of June 2005
+
+ // When new chips come out, check:
+ // IA-32 Intel Architecture Software Developer's Manual, Volume 3:
+ // System Programming Guide, Chatper 7: Multiple-processor management,
+ // Section 7.2, Memory Ordering.
+ // Last seen at:
+ // http://developer.intel.com/design/pentium4/manuals/index_new.htm
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ Atomic64 value = *ptr;
+ return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+
+#endif // defined(_WIN64)
+
+} } // namespace v8::internal
+
+#endif // V8_ATOMICOPS_INTERNALS_X86_MSVC_H_
diff --git a/chromium/v8/src/bignum-dtoa.cc b/chromium/v8/src/bignum-dtoa.cc
new file mode 100644
index 00000000000..c5ad4420c81
--- /dev/null
+++ b/chromium/v8/src/bignum-dtoa.cc
@@ -0,0 +1,658 @@
+// 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 <cmath>
+
+#include "../include/v8stdint.h"
+#include "checks.h"
+#include "utils.h"
+
+#include "bignum-dtoa.h"
+
+#include "bignum.h"
+#include "double.h"
+
+namespace v8 {
+namespace internal {
+
+static int NormalizedExponent(uint64_t significand, int exponent) {
+ ASSERT(significand != 0);
+ while ((significand & Double::kHiddenBit) == 0) {
+ significand = significand << 1;
+ exponent = exponent - 1;
+ }
+ return exponent;
+}
+
+
+// Forward declarations:
+// Returns an estimation of k such that 10^(k-1) <= v < 10^k.
+static int EstimatePower(int exponent);
+// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
+// and denominator.
+static void InitialScaledStartValues(double v,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus);
+// Multiplies numerator/denominator so that its values lies in the range 1-10.
+// Returns decimal_point s.t.
+// v = numerator'/denominator' * 10^(decimal_point-1)
+// where numerator' and denominator' are the values of numerator and
+// denominator after the call to this function.
+static void FixupMultiply10(int estimated_power, bool is_even,
+ int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus);
+// Generates digits from the left to the right and stops when the generated
+// digits yield the shortest decimal representation of v.
+static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer, int* length);
+// Generates 'requested_digits' after the decimal point.
+static void BignumToFixed(int requested_digits, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length);
+// Generates 'count' digits of numerator/denominator.
+// Once 'count' digits have been produced rounds the result depending on the
+// remainder (remainders of exactly .5 round upwards). Might update the
+// decimal_point when rounding up (for example for 0.9999).
+static void GenerateCountedDigits(int count, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length);
+
+
+void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
+ Vector<char> buffer, int* length, int* decimal_point) {
+ ASSERT(v > 0);
+ ASSERT(!Double(v).IsSpecial());
+ uint64_t significand = Double(v).Significand();
+ bool is_even = (significand & 1) == 0;
+ int exponent = Double(v).Exponent();
+ int normalized_exponent = NormalizedExponent(significand, exponent);
+ // estimated_power might be too low by 1.
+ int estimated_power = EstimatePower(normalized_exponent);
+
+ // Shortcut for Fixed.
+ // The requested digits correspond to the digits after the point. If the
+ // number is much too small, then there is no need in trying to get any
+ // digits.
+ if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) {
+ buffer[0] = '\0';
+ *length = 0;
+ // Set decimal-point to -requested_digits. This is what Gay does.
+ // Note that it should not have any effect anyways since the string is
+ // empty.
+ *decimal_point = -requested_digits;
+ return;
+ }
+
+ Bignum numerator;
+ Bignum denominator;
+ Bignum delta_minus;
+ Bignum delta_plus;
+ // Make sure the bignum can grow large enough. The smallest double equals
+ // 4e-324. In this case the denominator needs fewer than 324*4 binary digits.
+ // The maximum double is 1.7976931348623157e308 which needs fewer than
+ // 308*4 binary digits.
+ ASSERT(Bignum::kMaxSignificantBits >= 324*4);
+ bool need_boundary_deltas = (mode == BIGNUM_DTOA_SHORTEST);
+ InitialScaledStartValues(v, estimated_power, need_boundary_deltas,
+ &numerator, &denominator,
+ &delta_minus, &delta_plus);
+ // We now have v = (numerator / denominator) * 10^estimated_power.
+ FixupMultiply10(estimated_power, is_even, decimal_point,
+ &numerator, &denominator,
+ &delta_minus, &delta_plus);
+ // We now have v = (numerator / denominator) * 10^(decimal_point-1), and
+ // 1 <= (numerator + delta_plus) / denominator < 10
+ switch (mode) {
+ case BIGNUM_DTOA_SHORTEST:
+ GenerateShortestDigits(&numerator, &denominator,
+ &delta_minus, &delta_plus,
+ is_even, buffer, length);
+ break;
+ case BIGNUM_DTOA_FIXED:
+ BignumToFixed(requested_digits, decimal_point,
+ &numerator, &denominator,
+ buffer, length);
+ break;
+ case BIGNUM_DTOA_PRECISION:
+ GenerateCountedDigits(requested_digits, decimal_point,
+ &numerator, &denominator,
+ buffer, length);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ buffer[*length] = '\0';
+}
+
+
+// The procedure starts generating digits from the left to the right and stops
+// when the generated digits yield the shortest decimal representation of v. A
+// decimal representation of v is a number lying closer to v than to any other
+// double, so it converts to v when read.
+//
+// This is true if d, the decimal representation, is between m- and m+, the
+// upper and lower boundaries. d must be strictly between them if !is_even.
+// m- := (numerator - delta_minus) / denominator
+// m+ := (numerator + delta_plus) / denominator
+//
+// Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
+// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
+// will be produced. This should be the standard precondition.
+static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer, int* length) {
+ // Small optimization: if delta_minus and delta_plus are the same just reuse
+ // one of the two bignums.
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_plus = delta_minus;
+ }
+ *length = 0;
+ while (true) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive.
+ // digit = numerator / denominator (integer division).
+ // numerator = numerator % denominator.
+ buffer[(*length)++] = digit + '0';
+
+ // Can we stop already?
+ // If the remainder of the division is less than the distance to the lower
+ // boundary we can stop. In this case we simply round down (discarding the
+ // remainder).
+ // Similarly we test if we can round up (using the upper boundary).
+ bool in_delta_room_minus;
+ bool in_delta_room_plus;
+ if (is_even) {
+ in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus);
+ } else {
+ in_delta_room_minus = Bignum::Less(*numerator, *delta_minus);
+ }
+ if (is_even) {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (!in_delta_room_minus && !in_delta_room_plus) {
+ // Prepare for next iteration.
+ numerator->Times10();
+ delta_minus->Times10();
+ // We optimized delta_plus to be equal to delta_minus (if they share the
+ // same value). So don't multiply delta_plus if they point to the same
+ // object.
+ if (delta_minus != delta_plus) {
+ delta_plus->Times10();
+ }
+ } else if (in_delta_room_minus && in_delta_room_plus) {
+ // Let's see if 2*numerator < denominator.
+ // If yes, then the next digit would be < 5 and we can round down.
+ int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator);
+ if (compare < 0) {
+ // Remaining digits are less than .5. -> Round down (== do nothing).
+ } else if (compare > 0) {
+ // Remaining digits are more than .5 of denominator. -> Round up.
+ // Note that the last digit could not be a '9' as otherwise the whole
+ // loop would have stopped earlier.
+ // We still have an assert here in case the preconditions were not
+ // satisfied.
+ ASSERT(buffer[(*length) - 1] != '9');
+ buffer[(*length) - 1]++;
+ } else {
+ // Halfway case.
+ // TODO(floitsch): need a way to solve half-way cases.
+ // For now let's round towards even (since this is what Gay seems to
+ // do).
+
+ if ((buffer[(*length) - 1] - '0') % 2 == 0) {
+ // Round down => Do nothing.
+ } else {
+ ASSERT(buffer[(*length) - 1] != '9');
+ buffer[(*length) - 1]++;
+ }
+ }
+ return;
+ } else if (in_delta_room_minus) {
+ // Round down (== do nothing).
+ return;
+ } else { // in_delta_room_plus
+ // Round up.
+ // Note again that the last digit could not be '9' since this would have
+ // stopped the loop earlier.
+ // We still have an ASSERT here, in case the preconditions were not
+ // satisfied.
+ ASSERT(buffer[(*length) -1] != '9');
+ buffer[(*length) - 1]++;
+ return;
+ }
+ }
+}
+
+
+// Let v = numerator / denominator < 10.
+// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
+// from left to right. Once 'count' digits have been produced we decide wether
+// to round up or down. Remainders of exactly .5 round upwards. Numbers such
+// as 9.999999 propagate a carry all the way, and change the
+// exponent (decimal_point), when rounding upwards.
+static void GenerateCountedDigits(int count, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length) {
+ ASSERT(count >= 0);
+ for (int i = 0; i < count - 1; ++i) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive.
+ // digit = numerator / denominator (integer division).
+ // numerator = numerator % denominator.
+ buffer[i] = digit + '0';
+ // Prepare for next iteration.
+ numerator->Times10();
+ }
+ // Generate the last digit.
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ digit++;
+ }
+ buffer[count - 1] = digit + '0';
+ // Correct bad digits (in case we had a sequence of '9's). Propagate the
+ // carry until we hat a non-'9' or til we reach the first digit.
+ for (int i = count - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ if (buffer[0] == '0' + 10) {
+ // Propagate a carry past the top place.
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+ *length = count;
+}
+
+
+// Generates 'requested_digits' after the decimal point. It might omit
+// trailing '0's. If the input number is too small then no digits at all are
+// generated (ex.: 2 fixed digits for 0.00001).
+//
+// Input verifies: 1 <= (numerator + delta) / denominator < 10.
+static void BignumToFixed(int requested_digits, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length) {
+ // Note that we have to look at more than just the requested_digits, since
+ // a number could be rounded up. Example: v=0.5 with requested_digits=0.
+ // Even though the power of v equals 0 we can't just stop here.
+ if (-(*decimal_point) > requested_digits) {
+ // The number is definitively too small.
+ // Ex: 0.001 with requested_digits == 1.
+ // Set decimal-point to -requested_digits. This is what Gay does.
+ // Note that it should not have any effect anyways since the string is
+ // empty.
+ *decimal_point = -requested_digits;
+ *length = 0;
+ return;
+ } else if (-(*decimal_point) == requested_digits) {
+ // We only need to verify if the number rounds down or up.
+ // Ex: 0.04 and 0.06 with requested_digits == 1.
+ ASSERT(*decimal_point == -requested_digits);
+ // Initially the fraction lies in range (1, 10]. Multiply the denominator
+ // by 10 so that we can compare more easily.
+ denominator->Times10();
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ // If the fraction is >= 0.5 then we have to include the rounded
+ // digit.
+ buffer[0] = '1';
+ *length = 1;
+ (*decimal_point)++;
+ } else {
+ // Note that we caught most of similar cases earlier.
+ *length = 0;
+ }
+ return;
+ } else {
+ // The requested digits correspond to the digits after the point.
+ // The variable 'needed_digits' includes the digits before the point.
+ int needed_digits = (*decimal_point) + requested_digits;
+ GenerateCountedDigits(needed_digits, decimal_point,
+ numerator, denominator,
+ buffer, length);
+ }
+}
+
+
+// Returns an estimation of k such that 10^(k-1) <= v < 10^k where
+// v = f * 2^exponent and 2^52 <= f < 2^53.
+// v is hence a normalized double with the given exponent. The output is an
+// approximation for the exponent of the decimal approimation .digits * 10^k.
+//
+// The result might undershoot by 1 in which case 10^k <= v < 10^k+1.
+// Note: this property holds for v's upper boundary m+ too.
+// 10^k <= m+ < 10^k+1.
+// (see explanation below).
+//
+// Examples:
+// EstimatePower(0) => 16
+// EstimatePower(-52) => 0
+//
+// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0.
+static int EstimatePower(int exponent) {
+ // This function estimates log10 of v where v = f*2^e (with e == exponent).
+ // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)).
+ // Note that f is bounded by its container size. Let p = 53 (the double's
+ // significand size). Then 2^(p-1) <= f < 2^p.
+ //
+ // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close
+ // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)).
+ // The computed number undershoots by less than 0.631 (when we compute log3
+ // and not log10).
+ //
+ // Optimization: since we only need an approximated result this computation
+ // can be performed on 64 bit integers. On x86/x64 architecture the speedup is
+ // not really measurable, though.
+ //
+ // Since we want to avoid overshooting we decrement by 1e10 so that
+ // floating-point imprecisions don't affect us.
+ //
+ // Explanation for v's boundary m+: the computation takes advantage of
+ // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement
+ // (even for denormals where the delta can be much more important).
+
+ const double k1Log10 = 0.30102999566398114; // 1/lg(10)
+
+ // For doubles len(f) == 53 (don't forget the hidden bit).
+ const int kSignificandSize = 53;
+ double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
+ return static_cast<int>(estimate);
+}
+
+
+// See comments for InitialScaledStartValues.
+static void InitialScaledStartValuesPositiveExponent(
+ double v, int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ // A positive exponent implies a positive power.
+ ASSERT(estimated_power >= 0);
+ // Since the estimated_power is positive we simply multiply the denominator
+ // by 10^estimated_power.
+
+ // numerator = v.
+ numerator->AssignUInt64(Double(v).Significand());
+ numerator->ShiftLeft(Double(v).Exponent());
+ // denominator = 10^estimated_power.
+ denominator->AssignPowerUInt16(10, estimated_power);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
+ // denominator (of 2) delta_plus equals 2^e.
+ delta_plus->AssignUInt16(1);
+ delta_plus->ShiftLeft(Double(v).Exponent());
+ // Same for delta_minus (with adjustments below if f == 2^p-1).
+ delta_minus->AssignUInt16(1);
+ delta_minus->ShiftLeft(Double(v).Exponent());
+
+ // If the significand (without the hidden bit) is 0, then the lower
+ // boundary is closer than just half a ulp (unit in the last place).
+ // There is only one exception: if the next lower number is a denormal then
+ // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we
+ // have to test it in the other function where exponent < 0).
+ uint64_t v_bits = Double(v).AsUint64();
+ if ((v_bits & Double::kSignificandMask) == 0) {
+ // The lower boundary is closer at half the distance of "normal" numbers.
+ // Increase the common denominator and adapt all but the delta_minus.
+ denominator->ShiftLeft(1); // *2
+ numerator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+ }
+}
+
+
+// See comments for InitialScaledStartValues
+static void InitialScaledStartValuesNegativeExponentPositivePower(
+ double v, int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+ // v = f * 2^e with e < 0, and with estimated_power >= 0.
+ // This means that e is close to 0 (have a look at how estimated_power is
+ // computed).
+
+ // numerator = significand
+ // since v = significand * 2^exponent this is equivalent to
+ // numerator = v * / 2^-exponent
+ numerator->AssignUInt64(significand);
+ // denominator = 10^estimated_power * 2^-exponent (with exponent < 0)
+ denominator->AssignPowerUInt16(10, estimated_power);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
+ // denominator (of 2) delta_plus equals 2^e.
+ // Given that the denominator already includes v's exponent the distance
+ // to the boundaries is simply 1.
+ delta_plus->AssignUInt16(1);
+ // Same for delta_minus (with adjustments below if f == 2^p-1).
+ delta_minus->AssignUInt16(1);
+
+ // If the significand (without the hidden bit) is 0, then the lower
+ // boundary is closer than just one ulp (unit in the last place).
+ // There is only one exception: if the next lower number is a denormal
+ // then the distance is 1 ulp. Since the exponent is close to zero
+ // (otherwise estimated_power would have been negative) this cannot happen
+ // here either.
+ uint64_t v_bits = Double(v).AsUint64();
+ if ((v_bits & Double::kSignificandMask) == 0) {
+ // The lower boundary is closer at half the distance of "normal" numbers.
+ // Increase the denominator and adapt all but the delta_minus.
+ denominator->ShiftLeft(1); // *2
+ numerator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+ }
+}
+
+
+// See comments for InitialScaledStartValues
+static void InitialScaledStartValuesNegativeExponentNegativePower(
+ double v, int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ const uint64_t kMinimalNormalizedExponent =
+ V8_2PART_UINT64_C(0x00100000, 00000000);
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+ // Instead of multiplying the denominator with 10^estimated_power we
+ // multiply all values (numerator and deltas) by 10^-estimated_power.
+
+ // Use numerator as temporary container for power_ten.
+ Bignum* power_ten = numerator;
+ power_ten->AssignPowerUInt16(10, -estimated_power);
+
+ if (need_boundary_deltas) {
+ // Since power_ten == numerator we must make a copy of 10^estimated_power
+ // before we complete the computation of the numerator.
+ // delta_plus = delta_minus = 10^estimated_power
+ delta_plus->AssignBignum(*power_ten);
+ delta_minus->AssignBignum(*power_ten);
+ }
+
+ // numerator = significand * 2 * 10^-estimated_power
+ // since v = significand * 2^exponent this is equivalent to
+ // numerator = v * 10^-estimated_power * 2 * 2^-exponent.
+ // Remember: numerator has been abused as power_ten. So no need to assign it
+ // to itself.
+ ASSERT(numerator == power_ten);
+ numerator->MultiplyByUInt64(significand);
+
+ // denominator = 2 * 2^-exponent with exponent < 0.
+ denominator->AssignUInt16(1);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ numerator->ShiftLeft(1);
+ denominator->ShiftLeft(1);
+ // With this shift the boundaries have their correct value, since
+ // delta_plus = 10^-estimated_power, and
+ // delta_minus = 10^-estimated_power.
+ // These assignments have been done earlier.
+
+ // The special case where the lower boundary is twice as close.
+ // This time we have to look out for the exception too.
+ uint64_t v_bits = Double(v).AsUint64();
+ if ((v_bits & Double::kSignificandMask) == 0 &&
+ // The only exception where a significand == 0 has its boundaries at
+ // "normal" distances:
+ (v_bits & Double::kExponentMask) != kMinimalNormalizedExponent) {
+ numerator->ShiftLeft(1); // *2
+ denominator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+ }
+}
+
+
+// Let v = significand * 2^exponent.
+// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
+// and denominator. The functions GenerateShortestDigits and
+// GenerateCountedDigits will then convert this ratio to its decimal
+// representation d, with the required accuracy.
+// Then d * 10^estimated_power is the representation of v.
+// (Note: the fraction and the estimated_power might get adjusted before
+// generating the decimal representation.)
+//
+// The initial start values consist of:
+// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power.
+// - a scaled (common) denominator.
+// optionally (used by GenerateShortestDigits to decide if it has the shortest
+// decimal converting back to v):
+// - v - m-: the distance to the lower boundary.
+// - m+ - v: the distance to the upper boundary.
+//
+// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator.
+//
+// Let ep == estimated_power, then the returned values will satisfy:
+// v / 10^ep = numerator / denominator.
+// v's boundarys m- and m+:
+// m- / 10^ep == v / 10^ep - delta_minus / denominator
+// m+ / 10^ep == v / 10^ep + delta_plus / denominator
+// Or in other words:
+// m- == v - delta_minus * 10^ep / denominator;
+// m+ == v + delta_plus * 10^ep / denominator;
+//
+// Since 10^(k-1) <= v < 10^k (with k == estimated_power)
+// or 10^k <= v < 10^(k+1)
+// we then have 0.1 <= numerator/denominator < 1
+// or 1 <= numerator/denominator < 10
+//
+// It is then easy to kickstart the digit-generation routine.
+//
+// The boundary-deltas are only filled if need_boundary_deltas is set.
+static void InitialScaledStartValues(double v,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus) {
+ if (Double(v).Exponent() >= 0) {
+ InitialScaledStartValuesPositiveExponent(
+ v, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ } else if (estimated_power >= 0) {
+ InitialScaledStartValuesNegativeExponentPositivePower(
+ v, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ } else {
+ InitialScaledStartValuesNegativeExponentNegativePower(
+ v, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ }
+}
+
+
+// This routine multiplies numerator/denominator so that its values lies in the
+// range 1-10. That is after a call to this function we have:
+// 1 <= (numerator + delta_plus) /denominator < 10.
+// Let numerator the input before modification and numerator' the argument
+// after modification, then the output-parameter decimal_point is such that
+// numerator / denominator * 10^estimated_power ==
+// numerator' / denominator' * 10^(decimal_point - 1)
+// In some cases estimated_power was too low, and this is already the case. We
+// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
+// estimated_power) but do not touch the numerator or denominator.
+// Otherwise the routine multiplies the numerator and the deltas by 10.
+static void FixupMultiply10(int estimated_power, bool is_even,
+ int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ bool in_range;
+ if (is_even) {
+ // For IEEE doubles half-way cases (in decimal system numbers ending with 5)
+ // are rounded to the closest floating-point number with even significand.
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (in_range) {
+ // Since numerator + delta_plus >= denominator we already have
+ // 1 <= numerator/denominator < 10. Simply update the estimated_power.
+ *decimal_point = estimated_power + 1;
+ } else {
+ *decimal_point = estimated_power;
+ numerator->Times10();
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_minus->Times10();
+ delta_plus->AssignBignum(*delta_minus);
+ } else {
+ delta_minus->Times10();
+ delta_plus->Times10();
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/bignum-dtoa.h b/chromium/v8/src/bignum-dtoa.h
new file mode 100644
index 00000000000..93ec1f77061
--- /dev/null
+++ b/chromium/v8/src/bignum-dtoa.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef V8_BIGNUM_DTOA_H_
+#define V8_BIGNUM_DTOA_H_
+
+namespace v8 {
+namespace internal {
+
+enum BignumDtoaMode {
+ // Return the shortest correct representation.
+ // For example the output of 0.299999999999999988897 is (the less accurate but
+ // correct) 0.3.
+ BIGNUM_DTOA_SHORTEST,
+ // Return a fixed number of digits after the decimal point.
+ // For instance fixed(0.1, 4) becomes 0.1000
+ // If the input number is big, the output will be big.
+ BIGNUM_DTOA_FIXED,
+ // Return a fixed number of digits, no matter what the exponent is.
+ BIGNUM_DTOA_PRECISION
+};
+
+// Converts the given double 'v' to ASCII.
+// The result should be interpreted as buffer * 10^(point-length).
+// The buffer will be null-terminated.
+//
+// The input v must be > 0 and different from NaN, and Infinity.
+//
+// The output depends on the given mode:
+// - SHORTEST: produce the least amount of digits for which the internal
+// identity requirement is still satisfied. If the digits are printed
+// (together with the correct exponent) then reading this number will give
+// 'v' again. The buffer will choose the representation that is closest to
+// 'v'. If there are two at the same distance, than the number is round up.
+// In this mode the 'requested_digits' parameter is ignored.
+// - FIXED: produces digits necessary to print a given number with
+// 'requested_digits' digits after the decimal point. The produced digits
+// might be too short in which case the caller has to fill the gaps with '0's.
+// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
+// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns
+// buffer="2", point=0.
+// Note: the length of the returned buffer has no meaning wrt the significance
+// of its digits. That is, just because it contains '0's does not mean that
+// any other digit would not satisfy the internal identity requirement.
+// - PRECISION: produces 'requested_digits' where the first digit is not '0'.
+// Even though the length of produced digits usually equals
+// 'requested_digits', the function is allowed to return fewer digits, in
+// which case the caller has to fill the missing digits with '0's.
+// Halfway cases are again rounded up.
+// 'BignumDtoa' expects the given buffer to be big enough to hold all digits
+// and a terminating null-character.
+void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
+ Vector<char> buffer, int* length, int* point);
+
+} } // namespace v8::internal
+
+#endif // V8_BIGNUM_DTOA_H_
diff --git a/chromium/v8/src/bignum.cc b/chromium/v8/src/bignum.cc
new file mode 100644
index 00000000000..af0edde6d58
--- /dev/null
+++ b/chromium/v8/src/bignum.cc
@@ -0,0 +1,775 @@
+// 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 "../include/v8stdint.h"
+#include "utils.h"
+#include "bignum.h"
+
+namespace v8 {
+namespace internal {
+
+Bignum::Bignum()
+ : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) {
+ for (int i = 0; i < kBigitCapacity; ++i) {
+ bigits_[i] = 0;
+ }
+}
+
+
+template<typename S>
+static int BitSize(S value) {
+ return 8 * sizeof(value);
+}
+
+
+// Guaranteed to lie in one Bigit.
+void Bignum::AssignUInt16(uint16_t value) {
+ ASSERT(kBigitSize >= BitSize(value));
+ Zero();
+ if (value == 0) return;
+
+ EnsureCapacity(1);
+ bigits_[0] = value;
+ used_digits_ = 1;
+}
+
+
+void Bignum::AssignUInt64(uint64_t value) {
+ const int kUInt64Size = 64;
+
+ Zero();
+ if (value == 0) return;
+
+ int needed_bigits = kUInt64Size / kBigitSize + 1;
+ EnsureCapacity(needed_bigits);
+ for (int i = 0; i < needed_bigits; ++i) {
+ bigits_[i] = static_cast<Chunk>(value & kBigitMask);
+ value = value >> kBigitSize;
+ }
+ used_digits_ = needed_bigits;
+ Clamp();
+}
+
+
+void Bignum::AssignBignum(const Bignum& other) {
+ exponent_ = other.exponent_;
+ for (int i = 0; i < other.used_digits_; ++i) {
+ bigits_[i] = other.bigits_[i];
+ }
+ // Clear the excess digits (if there were any).
+ for (int i = other.used_digits_; i < used_digits_; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ = other.used_digits_;
+}
+
+
+static uint64_t ReadUInt64(Vector<const char> buffer,
+ int from,
+ int digits_to_read) {
+ uint64_t result = 0;
+ for (int i = from; i < from + digits_to_read; ++i) {
+ int digit = buffer[i] - '0';
+ ASSERT(0 <= digit && digit <= 9);
+ result = result * 10 + digit;
+ }
+ return result;
+}
+
+
+void Bignum::AssignDecimalString(Vector<const char> value) {
+ // 2^64 = 18446744073709551616 > 10^19
+ const int kMaxUint64DecimalDigits = 19;
+ Zero();
+ int length = value.length();
+ int pos = 0;
+ // Let's just say that each digit needs 4 bits.
+ while (length >= kMaxUint64DecimalDigits) {
+ uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits);
+ pos += kMaxUint64DecimalDigits;
+ length -= kMaxUint64DecimalDigits;
+ MultiplyByPowerOfTen(kMaxUint64DecimalDigits);
+ AddUInt64(digits);
+ }
+ uint64_t digits = ReadUInt64(value, pos, length);
+ MultiplyByPowerOfTen(length);
+ AddUInt64(digits);
+ Clamp();
+}
+
+
+static int HexCharValue(char c) {
+ if ('0' <= c && c <= '9') return c - '0';
+ if ('a' <= c && c <= 'f') return 10 + c - 'a';
+ if ('A' <= c && c <= 'F') return 10 + c - 'A';
+ UNREACHABLE();
+ return 0; // To make compiler happy.
+}
+
+
+void Bignum::AssignHexString(Vector<const char> value) {
+ Zero();
+ int length = value.length();
+
+ int needed_bigits = length * 4 / kBigitSize + 1;
+ EnsureCapacity(needed_bigits);
+ int string_index = length - 1;
+ for (int i = 0; i < needed_bigits - 1; ++i) {
+ // These bigits are guaranteed to be "full".
+ Chunk current_bigit = 0;
+ for (int j = 0; j < kBigitSize / 4; j++) {
+ current_bigit += HexCharValue(value[string_index--]) << (j * 4);
+ }
+ bigits_[i] = current_bigit;
+ }
+ used_digits_ = needed_bigits - 1;
+
+ Chunk most_significant_bigit = 0; // Could be = 0;
+ for (int j = 0; j <= string_index; ++j) {
+ most_significant_bigit <<= 4;
+ most_significant_bigit += HexCharValue(value[j]);
+ }
+ if (most_significant_bigit != 0) {
+ bigits_[used_digits_] = most_significant_bigit;
+ used_digits_++;
+ }
+ Clamp();
+}
+
+
+void Bignum::AddUInt64(uint64_t operand) {
+ if (operand == 0) return;
+ Bignum other;
+ other.AssignUInt64(operand);
+ AddBignum(other);
+}
+
+
+void Bignum::AddBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+
+ // If this has a greater exponent than other append zero-bigits to this.
+ // After this call exponent_ <= other.exponent_.
+ Align(other);
+
+ // There are two possibilities:
+ // aaaaaaaaaaa 0000 (where the 0s represent a's exponent)
+ // bbbbb 00000000
+ // ----------------
+ // ccccccccccc 0000
+ // or
+ // aaaaaaaaaa 0000
+ // bbbbbbbbb 0000000
+ // -----------------
+ // cccccccccccc 0000
+ // In both cases we might need a carry bigit.
+
+ EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_);
+ Chunk carry = 0;
+ int bigit_pos = other.exponent_ - exponent_;
+ ASSERT(bigit_pos >= 0);
+ for (int i = 0; i < other.used_digits_; ++i) {
+ Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry;
+ bigits_[bigit_pos] = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ bigit_pos++;
+ }
+
+ while (carry != 0) {
+ Chunk sum = bigits_[bigit_pos] + carry;
+ bigits_[bigit_pos] = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ bigit_pos++;
+ }
+ used_digits_ = Max(bigit_pos, used_digits_);
+ ASSERT(IsClamped());
+}
+
+
+void Bignum::SubtractBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+ // We require this to be bigger than other.
+ ASSERT(LessEqual(other, *this));
+
+ Align(other);
+
+ int offset = other.exponent_ - exponent_;
+ Chunk borrow = 0;
+ int i;
+ for (i = 0; i < other.used_digits_; ++i) {
+ ASSERT((borrow == 0) || (borrow == 1));
+ Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow;
+ bigits_[i + offset] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ while (borrow != 0) {
+ Chunk difference = bigits_[i + offset] - borrow;
+ bigits_[i + offset] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ ++i;
+ }
+ Clamp();
+}
+
+
+void Bignum::ShiftLeft(int shift_amount) {
+ if (used_digits_ == 0) return;
+ exponent_ += shift_amount / kBigitSize;
+ int local_shift = shift_amount % kBigitSize;
+ EnsureCapacity(used_digits_ + 1);
+ BigitsShiftLeft(local_shift);
+}
+
+
+void Bignum::MultiplyByUInt32(uint32_t factor) {
+ if (factor == 1) return;
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ if (used_digits_ == 0) return;
+
+ // The product of a bigit with the factor is of size kBigitSize + 32.
+ // Assert that this number + 1 (for the carry) fits into double chunk.
+ ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1);
+ DoubleChunk carry = 0;
+ for (int i = 0; i < used_digits_; ++i) {
+ DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry;
+ bigits_[i] = static_cast<Chunk>(product & kBigitMask);
+ carry = (product >> kBigitSize);
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_digits_ + 1);
+ bigits_[used_digits_] = static_cast<Chunk>(carry & kBigitMask);
+ used_digits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+
+void Bignum::MultiplyByUInt64(uint64_t factor) {
+ if (factor == 1) return;
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ ASSERT(kBigitSize < 32);
+ uint64_t carry = 0;
+ uint64_t low = factor & 0xFFFFFFFF;
+ uint64_t high = factor >> 32;
+ for (int i = 0; i < used_digits_; ++i) {
+ uint64_t product_low = low * bigits_[i];
+ uint64_t product_high = high * bigits_[i];
+ uint64_t tmp = (carry & kBigitMask) + product_low;
+ bigits_[i] = static_cast<Chunk>(tmp & kBigitMask);
+ carry = (carry >> kBigitSize) + (tmp >> kBigitSize) +
+ (product_high << (32 - kBigitSize));
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_digits_ + 1);
+ bigits_[used_digits_] = static_cast<Chunk>(carry & kBigitMask);
+ used_digits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+
+void Bignum::MultiplyByPowerOfTen(int exponent) {
+ const uint64_t kFive27 = V8_2PART_UINT64_C(0x6765c793, fa10079d);
+ const uint16_t kFive1 = 5;
+ const uint16_t kFive2 = kFive1 * 5;
+ const uint16_t kFive3 = kFive2 * 5;
+ const uint16_t kFive4 = kFive3 * 5;
+ const uint16_t kFive5 = kFive4 * 5;
+ const uint16_t kFive6 = kFive5 * 5;
+ const uint32_t kFive7 = kFive6 * 5;
+ const uint32_t kFive8 = kFive7 * 5;
+ const uint32_t kFive9 = kFive8 * 5;
+ const uint32_t kFive10 = kFive9 * 5;
+ const uint32_t kFive11 = kFive10 * 5;
+ const uint32_t kFive12 = kFive11 * 5;
+ const uint32_t kFive13 = kFive12 * 5;
+ const uint32_t kFive1_to_12[] =
+ { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6,
+ kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 };
+
+ ASSERT(exponent >= 0);
+ if (exponent == 0) return;
+ if (used_digits_ == 0) return;
+
+ // We shift by exponent at the end just before returning.
+ int remaining_exponent = exponent;
+ while (remaining_exponent >= 27) {
+ MultiplyByUInt64(kFive27);
+ remaining_exponent -= 27;
+ }
+ while (remaining_exponent >= 13) {
+ MultiplyByUInt32(kFive13);
+ remaining_exponent -= 13;
+ }
+ if (remaining_exponent > 0) {
+ MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]);
+ }
+ ShiftLeft(exponent);
+}
+
+
+void Bignum::Square() {
+ ASSERT(IsClamped());
+ int product_length = 2 * used_digits_;
+ EnsureCapacity(product_length);
+
+ // Comba multiplication: compute each column separately.
+ // Example: r = a2a1a0 * b2b1b0.
+ // r = 1 * a0b0 +
+ // 10 * (a1b0 + a0b1) +
+ // 100 * (a2b0 + a1b1 + a0b2) +
+ // 1000 * (a2b1 + a1b2) +
+ // 10000 * a2b2
+ //
+ // In the worst case we have to accumulate nb-digits products of digit*digit.
+ //
+ // Assert that the additional number of bits in a DoubleChunk are enough to
+ // sum up used_digits of Bigit*Bigit.
+ if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) {
+ UNIMPLEMENTED();
+ }
+ DoubleChunk accumulator = 0;
+ // First shift the digits so we don't overwrite them.
+ int copy_offset = used_digits_;
+ for (int i = 0; i < used_digits_; ++i) {
+ bigits_[copy_offset + i] = bigits_[i];
+ }
+ // We have two loops to avoid some 'if's in the loop.
+ for (int i = 0; i < used_digits_; ++i) {
+ // Process temporary digit i with power i.
+ // The sum of the two indices must be equal to i.
+ int bigit_index1 = i;
+ int bigit_index2 = 0;
+ // Sum all of the sub-products.
+ while (bigit_index1 >= 0) {
+ Chunk chunk1 = bigits_[copy_offset + bigit_index1];
+ Chunk chunk2 = bigits_[copy_offset + bigit_index2];
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+ for (int i = used_digits_; i < product_length; ++i) {
+ int bigit_index1 = used_digits_ - 1;
+ int bigit_index2 = i - bigit_index1;
+ // Invariant: sum of both indices is again equal to i.
+ // Inner loop runs 0 times on last iteration, emptying accumulator.
+ while (bigit_index2 < used_digits_) {
+ Chunk chunk1 = bigits_[copy_offset + bigit_index1];
+ Chunk chunk2 = bigits_[copy_offset + bigit_index2];
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ // The overwritten bigits_[i] will never be read in further loop iterations,
+ // because bigit_index1 and bigit_index2 are always greater
+ // than i - used_digits_.
+ bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+ // Since the result was guaranteed to lie inside the number the
+ // accumulator must be 0 now.
+ ASSERT(accumulator == 0);
+
+ // Don't forget to update the used_digits and the exponent.
+ used_digits_ = product_length;
+ exponent_ *= 2;
+ Clamp();
+}
+
+
+void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) {
+ ASSERT(base != 0);
+ ASSERT(power_exponent >= 0);
+ if (power_exponent == 0) {
+ AssignUInt16(1);
+ return;
+ }
+ Zero();
+ int shifts = 0;
+ // We expect base to be in range 2-32, and most often to be 10.
+ // It does not make much sense to implement different algorithms for counting
+ // the bits.
+ while ((base & 1) == 0) {
+ base >>= 1;
+ shifts++;
+ }
+ int bit_size = 0;
+ int tmp_base = base;
+ while (tmp_base != 0) {
+ tmp_base >>= 1;
+ bit_size++;
+ }
+ int final_size = bit_size * power_exponent;
+ // 1 extra bigit for the shifting, and one for rounded final_size.
+ EnsureCapacity(final_size / kBigitSize + 2);
+
+ // Left to Right exponentiation.
+ int mask = 1;
+ while (power_exponent >= mask) mask <<= 1;
+
+ // The mask is now pointing to the bit above the most significant 1-bit of
+ // power_exponent.
+ // Get rid of first 1-bit;
+ mask >>= 2;
+ uint64_t this_value = base;
+
+ bool delayed_multipliciation = false;
+ const uint64_t max_32bits = 0xFFFFFFFF;
+ while (mask != 0 && this_value <= max_32bits) {
+ this_value = this_value * this_value;
+ // Verify that there is enough space in this_value to perform the
+ // multiplication. The first bit_size bits must be 0.
+ if ((power_exponent & mask) != 0) {
+ uint64_t base_bits_mask =
+ ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1);
+ bool high_bits_zero = (this_value & base_bits_mask) == 0;
+ if (high_bits_zero) {
+ this_value *= base;
+ } else {
+ delayed_multipliciation = true;
+ }
+ }
+ mask >>= 1;
+ }
+ AssignUInt64(this_value);
+ if (delayed_multipliciation) {
+ MultiplyByUInt32(base);
+ }
+
+ // Now do the same thing as a bignum.
+ while (mask != 0) {
+ Square();
+ if ((power_exponent & mask) != 0) {
+ MultiplyByUInt32(base);
+ }
+ mask >>= 1;
+ }
+
+ // And finally add the saved shifts.
+ ShiftLeft(shifts * power_exponent);
+}
+
+
+// Precondition: this/other < 16bit.
+uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+ ASSERT(other.used_digits_ > 0);
+
+ // Easy case: if we have less digits than the divisor than the result is 0.
+ // Note: this handles the case where this == 0, too.
+ if (BigitLength() < other.BigitLength()) {
+ return 0;
+ }
+
+ Align(other);
+
+ uint16_t result = 0;
+
+ // Start by removing multiples of 'other' until both numbers have the same
+ // number of digits.
+ while (BigitLength() > other.BigitLength()) {
+ // This naive approach is extremely inefficient if the this divided other
+ // might be big. This function is implemented for doubleToString where
+ // the result should be small (less than 10).
+ ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16));
+ // Remove the multiples of the first digit.
+ // Example this = 23 and other equals 9. -> Remove 2 multiples.
+ result += bigits_[used_digits_ - 1];
+ SubtractTimes(other, bigits_[used_digits_ - 1]);
+ }
+
+ ASSERT(BigitLength() == other.BigitLength());
+
+ // Both bignums are at the same length now.
+ // Since other has more than 0 digits we know that the access to
+ // bigits_[used_digits_ - 1] is safe.
+ Chunk this_bigit = bigits_[used_digits_ - 1];
+ Chunk other_bigit = other.bigits_[other.used_digits_ - 1];
+
+ if (other.used_digits_ == 1) {
+ // Shortcut for easy (and common) case.
+ int quotient = this_bigit / other_bigit;
+ bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient;
+ result += quotient;
+ Clamp();
+ return result;
+ }
+
+ int division_estimate = this_bigit / (other_bigit + 1);
+ result += division_estimate;
+ SubtractTimes(other, division_estimate);
+
+ if (other_bigit * (division_estimate + 1) > this_bigit) {
+ // No need to even try to subtract. Even if other's remaining digits were 0
+ // another subtraction would be too much.
+ return result;
+ }
+
+ while (LessEqual(other, *this)) {
+ SubtractBignum(other);
+ result++;
+ }
+ return result;
+}
+
+
+template<typename S>
+static int SizeInHexChars(S number) {
+ ASSERT(number > 0);
+ int result = 0;
+ while (number != 0) {
+ number >>= 4;
+ result++;
+ }
+ return result;
+}
+
+
+static char HexCharOfValue(int value) {
+ ASSERT(0 <= value && value <= 16);
+ if (value < 10) return value + '0';
+ return value - 10 + 'A';
+}
+
+
+bool Bignum::ToHexString(char* buffer, int buffer_size) const {
+ ASSERT(IsClamped());
+ // Each bigit must be printable as separate hex-character.
+ ASSERT(kBigitSize % 4 == 0);
+ const int kHexCharsPerBigit = kBigitSize / 4;
+
+ if (used_digits_ == 0) {
+ if (buffer_size < 2) return false;
+ buffer[0] = '0';
+ buffer[1] = '\0';
+ return true;
+ }
+ // We add 1 for the terminating '\0' character.
+ int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit +
+ SizeInHexChars(bigits_[used_digits_ - 1]) + 1;
+ if (needed_chars > buffer_size) return false;
+ int string_index = needed_chars - 1;
+ buffer[string_index--] = '\0';
+ for (int i = 0; i < exponent_; ++i) {
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = '0';
+ }
+ }
+ for (int i = 0; i < used_digits_ - 1; ++i) {
+ Chunk current_bigit = bigits_[i];
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = HexCharOfValue(current_bigit & 0xF);
+ current_bigit >>= 4;
+ }
+ }
+ // And finally the last bigit.
+ Chunk most_significant_bigit = bigits_[used_digits_ - 1];
+ while (most_significant_bigit != 0) {
+ buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF);
+ most_significant_bigit >>= 4;
+ }
+ return true;
+}
+
+
+Bignum::Chunk Bignum::BigitAt(int index) const {
+ if (index >= BigitLength()) return 0;
+ if (index < exponent_) return 0;
+ return bigits_[index - exponent_];
+}
+
+
+int Bignum::Compare(const Bignum& a, const Bignum& b) {
+ ASSERT(a.IsClamped());
+ ASSERT(b.IsClamped());
+ int bigit_length_a = a.BigitLength();
+ int bigit_length_b = b.BigitLength();
+ if (bigit_length_a < bigit_length_b) return -1;
+ if (bigit_length_a > bigit_length_b) return +1;
+ for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) {
+ Chunk bigit_a = a.BigitAt(i);
+ Chunk bigit_b = b.BigitAt(i);
+ if (bigit_a < bigit_b) return -1;
+ if (bigit_a > bigit_b) return +1;
+ // Otherwise they are equal up to this digit. Try the next digit.
+ }
+ return 0;
+}
+
+
+int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) {
+ ASSERT(a.IsClamped());
+ ASSERT(b.IsClamped());
+ ASSERT(c.IsClamped());
+ if (a.BigitLength() < b.BigitLength()) {
+ return PlusCompare(b, a, c);
+ }
+ if (a.BigitLength() + 1 < c.BigitLength()) return -1;
+ if (a.BigitLength() > c.BigitLength()) return +1;
+ // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than
+ // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one
+ // of 'a'.
+ if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) {
+ return -1;
+ }
+
+ Chunk borrow = 0;
+ // Starting at min_exponent all digits are == 0. So no need to compare them.
+ int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_);
+ for (int i = c.BigitLength() - 1; i >= min_exponent; --i) {
+ Chunk chunk_a = a.BigitAt(i);
+ Chunk chunk_b = b.BigitAt(i);
+ Chunk chunk_c = c.BigitAt(i);
+ Chunk sum = chunk_a + chunk_b;
+ if (sum > chunk_c + borrow) {
+ return +1;
+ } else {
+ borrow = chunk_c + borrow - sum;
+ if (borrow > 1) return -1;
+ borrow <<= kBigitSize;
+ }
+ }
+ if (borrow == 0) return 0;
+ return -1;
+}
+
+
+void Bignum::Clamp() {
+ while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) {
+ used_digits_--;
+ }
+ if (used_digits_ == 0) {
+ // Zero.
+ exponent_ = 0;
+ }
+}
+
+
+bool Bignum::IsClamped() const {
+ return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0;
+}
+
+
+void Bignum::Zero() {
+ for (int i = 0; i < used_digits_; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ = 0;
+ exponent_ = 0;
+}
+
+
+void Bignum::Align(const Bignum& other) {
+ if (exponent_ > other.exponent_) {
+ // If "X" represents a "hidden" digit (by the exponent) then we are in the
+ // following case (a == this, b == other):
+ // a: aaaaaaXXXX or a: aaaaaXXX
+ // b: bbbbbbX b: bbbbbbbbXX
+ // We replace some of the hidden digits (X) of a with 0 digits.
+ // a: aaaaaa000X or a: aaaaa0XX
+ int zero_digits = exponent_ - other.exponent_;
+ EnsureCapacity(used_digits_ + zero_digits);
+ for (int i = used_digits_ - 1; i >= 0; --i) {
+ bigits_[i + zero_digits] = bigits_[i];
+ }
+ for (int i = 0; i < zero_digits; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ += zero_digits;
+ exponent_ -= zero_digits;
+ ASSERT(used_digits_ >= 0);
+ ASSERT(exponent_ >= 0);
+ }
+}
+
+
+void Bignum::BigitsShiftLeft(int shift_amount) {
+ ASSERT(shift_amount < kBigitSize);
+ ASSERT(shift_amount >= 0);
+ Chunk carry = 0;
+ for (int i = 0; i < used_digits_; ++i) {
+ Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount);
+ bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask;
+ carry = new_carry;
+ }
+ if (carry != 0) {
+ bigits_[used_digits_] = carry;
+ used_digits_++;
+ }
+}
+
+
+void Bignum::SubtractTimes(const Bignum& other, int factor) {
+#ifdef DEBUG
+ Bignum a, b;
+ a.AssignBignum(*this);
+ b.AssignBignum(other);
+ b.MultiplyByUInt32(factor);
+ a.SubtractBignum(b);
+#endif
+ ASSERT(exponent_ <= other.exponent_);
+ if (factor < 3) {
+ for (int i = 0; i < factor; ++i) {
+ SubtractBignum(other);
+ }
+ return;
+ }
+ Chunk borrow = 0;
+ int exponent_diff = other.exponent_ - exponent_;
+ for (int i = 0; i < other.used_digits_; ++i) {
+ DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i];
+ DoubleChunk remove = borrow + product;
+ Chunk difference =
+ bigits_[i + exponent_diff] - static_cast<Chunk>(remove & kBigitMask);
+ bigits_[i + exponent_diff] = difference & kBigitMask;
+ borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) +
+ (remove >> kBigitSize));
+ }
+ for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) {
+ if (borrow == 0) return;
+ Chunk difference = bigits_[i] - borrow;
+ bigits_[i] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ Clamp();
+ ASSERT(Bignum::Equal(a, *this));
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/bignum.h b/chromium/v8/src/bignum.h
new file mode 100644
index 00000000000..dcc4fa702aa
--- /dev/null
+++ b/chromium/v8/src/bignum.h
@@ -0,0 +1,141 @@
+// 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.
+
+#ifndef V8_BIGNUM_H_
+#define V8_BIGNUM_H_
+
+namespace v8 {
+namespace internal {
+
+class Bignum {
+ public:
+ // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
+ // This bignum can encode much bigger numbers, since it contains an
+ // exponent.
+ static const int kMaxSignificantBits = 3584;
+
+ Bignum();
+ void AssignUInt16(uint16_t value);
+ void AssignUInt64(uint64_t value);
+ void AssignBignum(const Bignum& other);
+
+ void AssignDecimalString(Vector<const char> value);
+ void AssignHexString(Vector<const char> value);
+
+ void AssignPowerUInt16(uint16_t base, int exponent);
+
+ void AddUInt16(uint16_t operand);
+ void AddUInt64(uint64_t operand);
+ void AddBignum(const Bignum& other);
+ // Precondition: this >= other.
+ void SubtractBignum(const Bignum& other);
+
+ void Square();
+ void ShiftLeft(int shift_amount);
+ void MultiplyByUInt32(uint32_t factor);
+ void MultiplyByUInt64(uint64_t factor);
+ void MultiplyByPowerOfTen(int exponent);
+ void Times10() { return MultiplyByUInt32(10); }
+ // Pseudocode:
+ // int result = this / other;
+ // this = this % other;
+ // In the worst case this function is in O(this/other).
+ uint16_t DivideModuloIntBignum(const Bignum& other);
+
+ bool ToHexString(char* buffer, int buffer_size) const;
+
+ static int Compare(const Bignum& a, const Bignum& b);
+ static bool Equal(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) == 0;
+ }
+ static bool LessEqual(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) <= 0;
+ }
+ static bool Less(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) < 0;
+ }
+ // Returns Compare(a + b, c);
+ static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c);
+ // Returns a + b == c
+ static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) == 0;
+ }
+ // Returns a + b <= c
+ static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) <= 0;
+ }
+ // Returns a + b < c
+ static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) < 0;
+ }
+
+ private:
+ typedef uint32_t Chunk;
+ typedef uint64_t DoubleChunk;
+
+ static const int kChunkSize = sizeof(Chunk) * 8;
+ static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8;
+ // With bigit size of 28 we loose some bits, but a double still fits easily
+ // into two chunks, and more importantly we can use the Comba multiplication.
+ static const int kBigitSize = 28;
+ static const Chunk kBigitMask = (1 << kBigitSize) - 1;
+ // Every instance allocates kBigitLength chunks on the stack. Bignums cannot
+ // grow. There are no checks if the stack-allocated space is sufficient.
+ static const int kBigitCapacity = kMaxSignificantBits / kBigitSize;
+
+ void EnsureCapacity(int size) {
+ if (size > kBigitCapacity) {
+ UNREACHABLE();
+ }
+ }
+ void Align(const Bignum& other);
+ void Clamp();
+ bool IsClamped() const;
+ void Zero();
+ // Requires this to have enough capacity (no tests done).
+ // Updates used_digits_ if necessary.
+ // by must be < kBigitSize.
+ void BigitsShiftLeft(int shift_amount);
+ // BigitLength includes the "hidden" digits encoded in the exponent.
+ int BigitLength() const { return used_digits_ + exponent_; }
+ Chunk BigitAt(int index) const;
+ void SubtractTimes(const Bignum& other, int factor);
+
+ Chunk bigits_buffer_[kBigitCapacity];
+ // A vector backed by bigits_buffer_. This way accesses to the array are
+ // checked for out-of-bounds errors.
+ Vector<Chunk> bigits_;
+ int used_digits_;
+ // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
+ int exponent_;
+
+ DISALLOW_COPY_AND_ASSIGN(Bignum);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_BIGNUM_H_
diff --git a/chromium/v8/src/bootstrapper.cc b/chromium/v8/src/bootstrapper.cc
new file mode 100644
index 00000000000..2a385aa4865
--- /dev/null
+++ b/chromium/v8/src/bootstrapper.cc
@@ -0,0 +1,2694 @@
+// Copyright 2012 the V8 project authors. 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 "accessors.h"
+#include "api.h"
+#include "bootstrapper.h"
+#include "compiler.h"
+#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"
+#include "extensions/statistics-extension.h"
+#include "code-stubs.h"
+
+#if defined(V8_I18N_SUPPORT)
+#include "extensions/i18n/i18n-extension.h"
+#endif
+
+namespace v8 {
+namespace internal {
+
+
+NativesExternalStringResource::NativesExternalStringResource(
+ Bootstrapper* bootstrapper,
+ const char* source,
+ size_t length)
+ : data_(source), length_(length) {
+ if (bootstrapper->delete_these_non_arrays_on_tear_down_ == NULL) {
+ bootstrapper->delete_these_non_arrays_on_tear_down_ = new List<char*>(2);
+ }
+ // The resources are small objects and we only make a fixed number of
+ // them, but let's clean them up on exit for neatness.
+ bootstrapper->delete_these_non_arrays_on_tear_down_->
+ Add(reinterpret_cast<char*>(this));
+}
+
+
+Bootstrapper::Bootstrapper(Isolate* isolate)
+ : isolate_(isolate),
+ nesting_(0),
+ extensions_cache_(Script::TYPE_EXTENSION),
+ delete_these_non_arrays_on_tear_down_(NULL),
+ delete_these_arrays_on_tear_down_(NULL) {
+}
+
+
+Handle<String> Bootstrapper::NativesSourceLookup(int index) {
+ ASSERT(0 <= index && index < Natives::GetBuiltinsCount());
+ Heap* heap = isolate_->heap();
+ if (heap->natives_source_cache()->get(index)->IsUndefined()) {
+ // 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 =
+ isolate_->factory()->NewExternalStringFromAscii(resource);
+ heap->natives_source_cache()->set(index, *source_code);
+ }
+ Handle<Object> cached_source(heap->natives_source_cache()->get(index),
+ isolate_);
+ return Handle<String>::cast(cached_source);
+}
+
+
+void Bootstrapper::Initialize(bool create_heap_objects) {
+ extensions_cache_.Initialize(create_heap_objects);
+}
+
+
+void Bootstrapper::InitializeOncePerProcess() {
+ GCExtension::Register();
+ ExternalizeStringExtension::Register();
+ StatisticsExtension::Register();
+#if defined(V8_I18N_SUPPORT)
+ v8_i18n::Extension::Register();
+#endif
+}
+
+
+char* Bootstrapper::AllocateAutoDeletedArray(int bytes) {
+ char* memory = new char[bytes];
+ if (memory != NULL) {
+ if (delete_these_arrays_on_tear_down_ == NULL) {
+ delete_these_arrays_on_tear_down_ = new List<char*>(2);
+ }
+ delete_these_arrays_on_tear_down_->Add(memory);
+ }
+ return memory;
+}
+
+
+void Bootstrapper::TearDown() {
+ if (delete_these_non_arrays_on_tear_down_ != NULL) {
+ int len = delete_these_non_arrays_on_tear_down_->length();
+ ASSERT(len < 20); // Don't use this mechanism for unbounded allocations.
+ for (int i = 0; i < len; i++) {
+ delete delete_these_non_arrays_on_tear_down_->at(i);
+ delete_these_non_arrays_on_tear_down_->at(i) = NULL;
+ }
+ delete delete_these_non_arrays_on_tear_down_;
+ delete_these_non_arrays_on_tear_down_ = NULL;
+ }
+
+ if (delete_these_arrays_on_tear_down_ != NULL) {
+ int len = delete_these_arrays_on_tear_down_->length();
+ ASSERT(len < 1000); // Don't use this mechanism for unbounded allocations.
+ for (int i = 0; i < len; i++) {
+ delete[] delete_these_arrays_on_tear_down_->at(i);
+ delete_these_arrays_on_tear_down_->at(i) = NULL;
+ }
+ delete delete_these_arrays_on_tear_down_;
+ delete_these_arrays_on_tear_down_ = NULL;
+ }
+
+ extensions_cache_.Initialize(false); // Yes, symmetrical
+}
+
+
+class Genesis BASE_EMBEDDED {
+ public:
+ Genesis(Isolate* isolate,
+ Handle<Object> global_object,
+ v8::Handle<v8::ObjectTemplate> global_template,
+ v8::ExtensionConfiguration* extensions);
+ ~Genesis() { }
+
+ Isolate* isolate() const { return isolate_; }
+ Factory* factory() const { return isolate_->factory(); }
+ Heap* heap() const { return isolate_->heap(); }
+
+ Handle<Context> result() { return result_; }
+
+ private:
+ Handle<Context> native_context() { return native_context_; }
+
+ // Creates some basic objects. Used for creating a context from scratch.
+ void CreateRoots();
+ // Creates the empty function. Used for creating a context from scratch.
+ Handle<JSFunction> CreateEmptyFunction(Isolate* isolate);
+ // Creates the ThrowTypeError function. ECMA 5th Ed. 13.2.3
+ Handle<JSFunction> GetThrowTypeErrorFunction();
+
+ void CreateStrictModeFunctionMaps(Handle<JSFunction> empty);
+
+ // Make the "arguments" and "caller" properties throw a TypeError on access.
+ void PoisonArgumentsAndCaller(Handle<Map> map);
+
+ // Creates the global objects using the global and the template passed in
+ // through the API. We call this regardless of whether we are building a
+ // context from scratch or using a deserialized one from the partial snapshot
+ // but in the latter case we don't use the objects it produces directly, as
+ // we have to used the deserialized ones that are linked together with the
+ // rest of the context snapshot.
+ Handle<JSGlobalProxy> CreateNewGlobals(
+ v8::Handle<v8::ObjectTemplate> global_template,
+ Handle<Object> global_object,
+ Handle<GlobalObject>* global_proxy_out);
+ // Hooks the given global proxy into the context. If the context was created
+ // by deserialization then this will unhook the global proxy that was
+ // deserialized, leaving the GC to pick it up.
+ void HookUpGlobalProxy(Handle<GlobalObject> inner_global,
+ Handle<JSGlobalProxy> global_proxy);
+ // Similarly, we want to use the inner global that has been created by the
+ // templates passed through the API. The inner global from the snapshot is
+ // detached from the other objects in the snapshot.
+ void HookUpInnerGlobal(Handle<GlobalObject> inner_global);
+ // New context initialization. Used for creating a context from scratch.
+ void InitializeGlobal(Handle<GlobalObject> inner_global,
+ Handle<JSFunction> empty_function);
+ void InitializeExperimentalGlobal();
+ // Installs the contents of the native .js files on the global objects.
+ // Used for creating a context from scratch.
+ void InstallNativeFunctions();
+ void InstallExperimentalNativeFunctions();
+ Handle<JSFunction> InstallInternalArray(Handle<JSBuiltinsObject> builtins,
+ const char* name,
+ ElementsKind elements_kind);
+ bool InstallNatives();
+
+ Handle<JSFunction> InstallTypedArray(const char* name,
+ ElementsKind elementsKind);
+ bool InstallExperimentalNatives();
+ 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:
+ 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> native_context,
+ v8::ExtensionConfiguration* extensions);
+ static bool InstallExtension(Isolate* isolate,
+ const char* name,
+ ExtensionStates* extension_states);
+ static bool InstallExtension(Isolate* isolate,
+ v8::RegisteredExtension* current,
+ ExtensionStates* extension_states);
+ static void InstallSpecialObjects(Handle<Context> native_context);
+ bool InstallJSBuiltins(Handle<JSBuiltinsObject> builtins);
+ bool ConfigureApiObject(Handle<JSObject> object,
+ Handle<ObjectTemplateInfo> object_template);
+ bool ConfigureGlobalObjects(v8::Handle<v8::ObjectTemplate> global_template);
+
+ // Migrates all properties from the 'from' object to the 'to'
+ // object and overrides the prototype in 'to' with the one from
+ // 'from'.
+ void TransferObject(Handle<JSObject> from, Handle<JSObject> to);
+ void TransferNamedProperties(Handle<JSObject> from, Handle<JSObject> to);
+ void TransferIndexedProperties(Handle<JSObject> from, Handle<JSObject> to);
+
+ enum PrototypePropertyMode {
+ DONT_ADD_PROTOTYPE,
+ ADD_READONLY_PROTOTYPE,
+ ADD_WRITEABLE_PROTOTYPE
+ };
+
+ Handle<Map> CreateFunctionMap(PrototypePropertyMode prototype_mode);
+
+ void SetFunctionInstanceDescriptor(Handle<Map> map,
+ PrototypePropertyMode prototypeMode);
+ void MakeFunctionInstancePrototypeWritable();
+
+ Handle<Map> CreateStrictModeFunctionMap(
+ PrototypePropertyMode prototype_mode,
+ Handle<JSFunction> empty_function);
+
+ void SetStrictFunctionInstanceDescriptor(Handle<Map> map,
+ PrototypePropertyMode propertyMode);
+
+ static bool CompileBuiltin(Isolate* isolate, int index);
+ static bool CompileExperimentalBuiltin(Isolate* isolate, int index);
+ static bool CompileNative(Isolate* isolate,
+ Vector<const char> name,
+ Handle<String> source);
+ static bool CompileScriptCached(Isolate* isolate,
+ Vector<const char> name,
+ Handle<String> source,
+ SourceCodeCache* cache,
+ v8::Extension* extension,
+ Handle<Context> top_context,
+ bool use_runtime_context);
+
+ Isolate* isolate_;
+ Handle<Context> result_;
+ Handle<Context> native_context_;
+
+ // Function maps. Function maps are created initially with a read only
+ // prototype for the processing of JS builtins. Later the function maps are
+ // replaced in order to make prototype writable. These are the final, writable
+ // prototype, maps.
+ Handle<Map> function_map_writable_prototype_;
+ Handle<Map> strict_mode_function_map_writable_prototype_;
+ Handle<JSFunction> throw_type_error_function;
+
+ BootstrapperActive active_;
+ friend class Bootstrapper;
+};
+
+
+void Bootstrapper::Iterate(ObjectVisitor* v) {
+ extensions_cache_.Iterate(v);
+ v->Synchronize(VisitorSynchronization::kExtensions);
+}
+
+
+Handle<Context> Bootstrapper::CreateEnvironment(
+ Handle<Object> global_object,
+ v8::Handle<v8::ObjectTemplate> global_template,
+ v8::ExtensionConfiguration* extensions) {
+ HandleScope scope(isolate_);
+ Genesis genesis(isolate_, global_object, global_template, extensions);
+ Handle<Context> env = genesis.result();
+ if (env.is_null() || !InstallExtensions(env, extensions)) {
+ return Handle<Context>();
+ }
+ return scope.CloseAndEscape(env);
+}
+
+
+static void SetObjectPrototype(Handle<JSObject> object, Handle<Object> proto) {
+ // object.__proto__ = proto;
+ Factory* factory = object->GetIsolate()->factory();
+ Handle<Map> old_to_map = Handle<Map>(object->map());
+ Handle<Map> new_to_map = factory->CopyMap(old_to_map);
+ new_to_map->set_prototype(*proto);
+ object->set_map(*new_to_map);
+}
+
+
+void Bootstrapper::DetachGlobal(Handle<Context> env) {
+ Factory* factory = env->GetIsolate()->factory();
+ Handle<JSGlobalProxy> global_proxy(JSGlobalProxy::cast(env->global_proxy()));
+ global_proxy->set_native_context(*factory->null_value());
+ SetObjectPrototype(global_proxy, factory->null_value());
+ env->set_global_proxy(env->global_object());
+ env->global_object()->set_global_receiver(env->global_object());
+}
+
+
+void Bootstrapper::ReattachGlobal(Handle<Context> env,
+ Handle<JSGlobalProxy> global_proxy) {
+ env->global_object()->set_global_receiver(*global_proxy);
+ env->set_global_proxy(*global_proxy);
+ SetObjectPrototype(global_proxy, Handle<JSObject>(env->global_object()));
+ global_proxy->set_native_context(*env);
+}
+
+
+static Handle<JSFunction> InstallFunction(Handle<JSObject> target,
+ const char* name,
+ InstanceType type,
+ int instance_size,
+ Handle<JSObject> prototype,
+ Builtins::Name call,
+ bool install_initial_map,
+ bool set_instance_class_name) {
+ Isolate* isolate = target->GetIsolate();
+ Factory* factory = isolate->factory();
+ Handle<String> internalized_name = factory->InternalizeUtf8String(name);
+ Handle<Code> call_code = Handle<Code>(isolate->builtins()->builtin(call));
+ Handle<JSFunction> function = prototype.is_null() ?
+ factory->NewFunctionWithoutPrototype(internalized_name, call_code) :
+ factory->NewFunctionWithPrototype(internalized_name,
+ type,
+ instance_size,
+ prototype,
+ call_code,
+ install_initial_map);
+ PropertyAttributes attributes;
+ if (target->IsJSBuiltinsObject()) {
+ attributes =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
+ } else {
+ attributes = DONT_ENUM;
+ }
+ CHECK_NOT_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ target, internalized_name, function, attributes));
+ if (set_instance_class_name) {
+ function->shared()->set_instance_class_name(*internalized_name);
+ }
+ function->shared()->set_native(true);
+ return function;
+}
+
+
+void Genesis::SetFunctionInstanceDescriptor(
+ Handle<Map> map, PrototypePropertyMode prototypeMode) {
+ int size = (prototypeMode == DONT_ADD_PROTOTYPE) ? 4 : 5;
+ Handle<DescriptorArray> descriptors(factory()->NewDescriptorArray(0, size));
+ DescriptorArray::WhitenessWitness witness(*descriptors);
+
+ Handle<Foreign> length(factory()->NewForeign(&Accessors::FunctionLength));
+ Handle<Foreign> name(factory()->NewForeign(&Accessors::FunctionName));
+ Handle<Foreign> args(factory()->NewForeign(&Accessors::FunctionArguments));
+ Handle<Foreign> caller(factory()->NewForeign(&Accessors::FunctionCaller));
+ Handle<Foreign> prototype;
+ if (prototypeMode != DONT_ADD_PROTOTYPE) {
+ prototype = factory()->NewForeign(&Accessors::FunctionPrototype);
+ }
+ PropertyAttributes attribs = static_cast<PropertyAttributes>(
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ map->set_instance_descriptors(*descriptors);
+
+ { // Add length.
+ CallbacksDescriptor d(*factory()->length_string(), *length, attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+ { // Add name.
+ CallbacksDescriptor d(*factory()->name_string(), *name, attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+ { // Add arguments.
+ CallbacksDescriptor d(*factory()->arguments_string(), *args, attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+ { // Add caller.
+ CallbacksDescriptor d(*factory()->caller_string(), *caller, attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+ if (prototypeMode != DONT_ADD_PROTOTYPE) {
+ // Add prototype.
+ if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) {
+ attribs = static_cast<PropertyAttributes>(attribs & ~READ_ONLY);
+ }
+ CallbacksDescriptor d(*factory()->prototype_string(), *prototype, attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+}
+
+
+Handle<Map> Genesis::CreateFunctionMap(PrototypePropertyMode prototype_mode) {
+ Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
+ SetFunctionInstanceDescriptor(map, prototype_mode);
+ map->set_function_with_prototype(prototype_mode != DONT_ADD_PROTOTYPE);
+ return map;
+}
+
+
+Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
+ // Allocate the map for function instances. Maps are allocated first and their
+ // prototypes patched later, once empty function is created.
+
+ // Functions with this map will not have a 'prototype' property, and
+ // can not be used as constructors.
+ Handle<Map> function_without_prototype_map =
+ CreateFunctionMap(DONT_ADD_PROTOTYPE);
+ native_context()->set_function_without_prototype_map(
+ *function_without_prototype_map);
+
+ // Allocate the function map. This map is temporary, used only for processing
+ // of builtins.
+ // Later the map is replaced with writable prototype map, allocated below.
+ Handle<Map> function_map = CreateFunctionMap(ADD_READONLY_PROTOTYPE);
+ native_context()->set_function_map(*function_map);
+
+ // The final map for functions. Writeable prototype.
+ // This map is installed in MakeFunctionInstancePrototypeWritable.
+ function_map_writable_prototype_ = CreateFunctionMap(ADD_WRITEABLE_PROTOTYPE);
+
+ Factory* factory = isolate->factory();
+
+ Handle<String> object_name = factory->Object_string();
+
+ { // --- O b j e c t ---
+ Handle<JSFunction> object_fun =
+ factory->NewFunction(object_name, factory->null_value());
+ Handle<Map> object_function_map =
+ factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ object_fun->set_initial_map(*object_function_map);
+ object_function_map->set_constructor(*object_fun);
+
+ native_context()->set_object_function(*object_fun);
+
+ // Allocate a new prototype for the object function.
+ Handle<JSObject> prototype = factory->NewJSObject(
+ isolate->object_function(),
+ TENURED);
+
+ native_context()->set_initial_object_prototype(*prototype);
+ // For bootstrapping set the array prototype to be the same as the object
+ // prototype, otherwise the missing initial_array_prototype will cause
+ // assertions during startup.
+ native_context()->set_initial_array_prototype(*prototype);
+ SetPrototype(object_fun, prototype);
+ }
+
+ // Allocate the empty function as the prototype for function ECMAScript
+ // 262 15.3.4.
+ Handle<String> empty_string =
+ factory->InternalizeOneByteString(STATIC_ASCII_VECTOR("Empty"));
+ Handle<JSFunction> empty_function =
+ factory->NewFunctionWithoutPrototype(empty_string, CLASSIC_MODE);
+
+ // --- E m p t y ---
+ Handle<Code> code =
+ Handle<Code>(isolate->builtins()->builtin(
+ Builtins::kEmptyFunction));
+ empty_function->set_code(*code);
+ empty_function->shared()->set_code(*code);
+ Handle<String> source =
+ factory->NewStringFromOneByte(STATIC_ASCII_VECTOR("() {}"));
+ Handle<Script> script = factory->NewScript(source);
+ script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
+ empty_function->shared()->set_script(*script);
+ empty_function->shared()->set_start_position(0);
+ empty_function->shared()->set_end_position(source->length());
+ empty_function->shared()->DontAdaptArguments();
+
+ // Set prototypes for the function maps.
+ native_context()->function_map()->set_prototype(*empty_function);
+ native_context()->function_without_prototype_map()->
+ set_prototype(*empty_function);
+ function_map_writable_prototype_->set_prototype(*empty_function);
+
+ // Allocate the function map first and then patch the prototype later
+ Handle<Map> empty_function_map = CreateFunctionMap(DONT_ADD_PROTOTYPE);
+ empty_function_map->set_prototype(
+ native_context()->object_function()->prototype());
+ empty_function->set_map(*empty_function_map);
+ return empty_function;
+}
+
+
+void Genesis::SetStrictFunctionInstanceDescriptor(
+ Handle<Map> map, PrototypePropertyMode prototypeMode) {
+ int size = (prototypeMode == DONT_ADD_PROTOTYPE) ? 4 : 5;
+ Handle<DescriptorArray> descriptors(factory()->NewDescriptorArray(0, size));
+ DescriptorArray::WhitenessWitness witness(*descriptors);
+
+ Handle<Foreign> length(factory()->NewForeign(&Accessors::FunctionLength));
+ Handle<Foreign> name(factory()->NewForeign(&Accessors::FunctionName));
+ Handle<AccessorPair> arguments(factory()->NewAccessorPair());
+ Handle<AccessorPair> caller(factory()->NewAccessorPair());
+ Handle<Foreign> prototype;
+ if (prototypeMode != DONT_ADD_PROTOTYPE) {
+ prototype = factory()->NewForeign(&Accessors::FunctionPrototype);
+ }
+ PropertyAttributes rw_attribs =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
+ PropertyAttributes ro_attribs =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
+ map->set_instance_descriptors(*descriptors);
+
+ { // Add length.
+ CallbacksDescriptor d(*factory()->length_string(), *length, ro_attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+ { // Add name.
+ CallbacksDescriptor d(*factory()->name_string(), *name, rw_attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+ { // Add arguments.
+ CallbacksDescriptor d(*factory()->arguments_string(), *arguments,
+ rw_attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+ { // Add caller.
+ CallbacksDescriptor d(*factory()->caller_string(), *caller, rw_attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+ if (prototypeMode != DONT_ADD_PROTOTYPE) {
+ // Add prototype.
+ PropertyAttributes attribs =
+ prototypeMode == ADD_WRITEABLE_PROTOTYPE ? rw_attribs : ro_attribs;
+ CallbacksDescriptor d(*factory()->prototype_string(), *prototype, attribs);
+ map->AppendDescriptor(&d, witness);
+ }
+}
+
+
+// ECMAScript 5th Edition, 13.2.3
+Handle<JSFunction> Genesis::GetThrowTypeErrorFunction() {
+ if (throw_type_error_function.is_null()) {
+ Handle<String> name = factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("ThrowTypeError"));
+ throw_type_error_function =
+ factory()->NewFunctionWithoutPrototype(name, CLASSIC_MODE);
+ Handle<Code> code(isolate()->builtins()->builtin(
+ Builtins::kStrictModePoisonPill));
+ throw_type_error_function->set_map(
+ native_context()->function_map());
+ throw_type_error_function->set_code(*code);
+ throw_type_error_function->shared()->set_code(*code);
+ throw_type_error_function->shared()->DontAdaptArguments();
+
+ JSObject::PreventExtensions(throw_type_error_function);
+ }
+ return throw_type_error_function;
+}
+
+
+Handle<Map> Genesis::CreateStrictModeFunctionMap(
+ PrototypePropertyMode prototype_mode,
+ Handle<JSFunction> empty_function) {
+ Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
+ SetStrictFunctionInstanceDescriptor(map, prototype_mode);
+ map->set_function_with_prototype(prototype_mode != DONT_ADD_PROTOTYPE);
+ map->set_prototype(*empty_function);
+ return map;
+}
+
+
+void Genesis::CreateStrictModeFunctionMaps(Handle<JSFunction> empty) {
+ // Allocate map for the prototype-less strict mode instances.
+ Handle<Map> strict_mode_function_without_prototype_map =
+ CreateStrictModeFunctionMap(DONT_ADD_PROTOTYPE, empty);
+ native_context()->set_strict_mode_function_without_prototype_map(
+ *strict_mode_function_without_prototype_map);
+
+ // Allocate map for the strict mode functions. This map is temporary, used
+ // only for processing of builtins.
+ // Later the map is replaced with writable prototype map, allocated below.
+ Handle<Map> strict_mode_function_map =
+ CreateStrictModeFunctionMap(ADD_READONLY_PROTOTYPE, empty);
+ native_context()->set_strict_mode_function_map(
+ *strict_mode_function_map);
+
+ // The final map for the strict mode functions. Writeable prototype.
+ // This map is installed in MakeFunctionInstancePrototypeWritable.
+ strict_mode_function_map_writable_prototype_ =
+ CreateStrictModeFunctionMap(ADD_WRITEABLE_PROTOTYPE, empty);
+
+ // Complete the callbacks.
+ PoisonArgumentsAndCaller(strict_mode_function_without_prototype_map);
+ PoisonArgumentsAndCaller(strict_mode_function_map);
+ PoisonArgumentsAndCaller(strict_mode_function_map_writable_prototype_);
+}
+
+
+static void SetAccessors(Handle<Map> map,
+ Handle<String> name,
+ Handle<JSFunction> func) {
+ DescriptorArray* descs = map->instance_descriptors();
+ int number = descs->SearchWithCache(*name, *map);
+ AccessorPair* accessors = AccessorPair::cast(descs->GetValue(number));
+ accessors->set_getter(*func);
+ accessors->set_setter(*func);
+}
+
+
+void Genesis::PoisonArgumentsAndCaller(Handle<Map> map) {
+ SetAccessors(map, factory()->arguments_string(), GetThrowTypeErrorFunction());
+ SetAccessors(map, factory()->caller_string(), GetThrowTypeErrorFunction());
+}
+
+
+static void AddToWeakNativeContextList(Context* context) {
+ ASSERT(context->IsNativeContext());
+ Heap* heap = context->GetIsolate()->heap();
+#ifdef DEBUG
+ { // NOLINT
+ ASSERT(context->get(Context::NEXT_CONTEXT_LINK)->IsUndefined());
+ // Check that context is not in the list yet.
+ for (Object* current = heap->native_contexts_list();
+ !current->IsUndefined();
+ current = Context::cast(current)->get(Context::NEXT_CONTEXT_LINK)) {
+ ASSERT(current != context);
+ }
+ }
+#endif
+ context->set(Context::NEXT_CONTEXT_LINK, heap->native_contexts_list());
+ heap->set_native_contexts_list(context);
+}
+
+
+void Genesis::CreateRoots() {
+ // Allocate the native context FixedArray first and then patch the
+ // closure and extension object later (we need the empty function
+ // and the global object, but in order to create those, we need the
+ // native context).
+ native_context_ = factory()->NewNativeContext();
+ AddToWeakNativeContextList(*native_context());
+ isolate()->set_context(*native_context());
+
+ // Allocate the message listeners object.
+ {
+ v8::NeanderArray listeners;
+ native_context()->set_message_listeners(*listeners.value());
+ }
+}
+
+
+Handle<JSGlobalProxy> Genesis::CreateNewGlobals(
+ v8::Handle<v8::ObjectTemplate> global_template,
+ Handle<Object> global_object,
+ Handle<GlobalObject>* inner_global_out) {
+ // The argument global_template aka data is an ObjectTemplateInfo.
+ // It has a constructor pointer that points at global_constructor which is a
+ // FunctionTemplateInfo.
+ // The global_constructor is used to create or reinitialize the global_proxy.
+ // The global_constructor also has a prototype_template pointer that points at
+ // js_global_template which is an ObjectTemplateInfo.
+ // That in turn has a constructor pointer that points at
+ // js_global_constructor which is a FunctionTemplateInfo.
+ // js_global_constructor is used to make js_global_function
+ // js_global_function is used to make the new inner_global.
+ //
+ // --- G l o b a l ---
+ // Step 1: Create a fresh inner JSGlobalObject.
+ Handle<JSFunction> js_global_function;
+ Handle<ObjectTemplateInfo> js_global_template;
+ if (!global_template.IsEmpty()) {
+ // Get prototype template of the global_template.
+ Handle<ObjectTemplateInfo> data =
+ v8::Utils::OpenHandle(*global_template);
+ Handle<FunctionTemplateInfo> global_constructor =
+ Handle<FunctionTemplateInfo>(
+ FunctionTemplateInfo::cast(data->constructor()));
+ Handle<Object> proto_template(global_constructor->prototype_template(),
+ isolate());
+ if (!proto_template->IsUndefined()) {
+ js_global_template =
+ Handle<ObjectTemplateInfo>::cast(proto_template);
+ }
+ }
+
+ if (js_global_template.is_null()) {
+ Handle<String> name = Handle<String>(heap()->empty_string());
+ Handle<Code> code = Handle<Code>(isolate()->builtins()->builtin(
+ Builtins::kIllegal));
+ js_global_function =
+ factory()->NewFunction(name, JS_GLOBAL_OBJECT_TYPE,
+ JSGlobalObject::kSize, code, true);
+ // Change the constructor property of the prototype of the
+ // hidden global function to refer to the Object function.
+ Handle<JSObject> prototype =
+ Handle<JSObject>(
+ JSObject::cast(js_global_function->instance_prototype()));
+ CHECK_NOT_EMPTY_HANDLE(isolate(),
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ prototype, factory()->constructor_string(),
+ isolate()->object_function(), NONE));
+ } else {
+ Handle<FunctionTemplateInfo> js_global_constructor(
+ FunctionTemplateInfo::cast(js_global_template->constructor()));
+ js_global_function =
+ factory()->CreateApiFunction(js_global_constructor,
+ factory()->InnerGlobalObject);
+ }
+
+ js_global_function->initial_map()->set_is_hidden_prototype();
+ js_global_function->initial_map()->set_dictionary_map(true);
+ Handle<GlobalObject> inner_global =
+ factory()->NewGlobalObject(js_global_function);
+ if (inner_global_out != NULL) {
+ *inner_global_out = inner_global;
+ }
+
+ // Step 2: create or re-initialize the global proxy object.
+ Handle<JSFunction> global_proxy_function;
+ if (global_template.IsEmpty()) {
+ Handle<String> name = Handle<String>(heap()->empty_string());
+ Handle<Code> code = Handle<Code>(isolate()->builtins()->builtin(
+ Builtins::kIllegal));
+ global_proxy_function =
+ factory()->NewFunction(name, JS_GLOBAL_PROXY_TYPE,
+ JSGlobalProxy::kSize, code, true);
+ } else {
+ Handle<ObjectTemplateInfo> data =
+ v8::Utils::OpenHandle(*global_template);
+ Handle<FunctionTemplateInfo> global_constructor(
+ FunctionTemplateInfo::cast(data->constructor()));
+ global_proxy_function =
+ factory()->CreateApiFunction(global_constructor,
+ factory()->OuterGlobalObject);
+ }
+
+ Handle<String> global_name = factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("global"));
+ global_proxy_function->shared()->set_instance_class_name(*global_name);
+ global_proxy_function->initial_map()->set_is_access_check_needed(true);
+
+ // Set global_proxy.__proto__ to js_global after ConfigureGlobalObjects
+ // Return the global proxy.
+
+ if (global_object.location() != NULL) {
+ ASSERT(global_object->IsJSGlobalProxy());
+ return ReinitializeJSGlobalProxy(
+ global_proxy_function,
+ Handle<JSGlobalProxy>::cast(global_object));
+ } else {
+ return Handle<JSGlobalProxy>::cast(
+ factory()->NewJSObject(global_proxy_function, TENURED));
+ }
+}
+
+
+void Genesis::HookUpGlobalProxy(Handle<GlobalObject> inner_global,
+ Handle<JSGlobalProxy> global_proxy) {
+ // Set the native context for the global object.
+ inner_global->set_native_context(*native_context());
+ inner_global->set_global_context(*native_context());
+ inner_global->set_global_receiver(*global_proxy);
+ global_proxy->set_native_context(*native_context());
+ native_context()->set_global_proxy(*global_proxy);
+}
+
+
+void Genesis::HookUpInnerGlobal(Handle<GlobalObject> inner_global) {
+ Handle<GlobalObject> inner_global_from_snapshot(
+ GlobalObject::cast(native_context()->extension()));
+ Handle<JSBuiltinsObject> builtins_global(native_context()->builtins());
+ native_context()->set_extension(*inner_global);
+ native_context()->set_global_object(*inner_global);
+ native_context()->set_security_token(*inner_global);
+ static const PropertyAttributes attributes =
+ static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
+ ForceSetProperty(builtins_global,
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("global")),
+ inner_global,
+ attributes);
+ // 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);
+}
+
+
+// This is only called if we are not using snapshots. The equivalent
+// work in the snapshot case is done in HookUpInnerGlobal.
+void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
+ Handle<JSFunction> empty_function) {
+ // --- G l o b a l C o n t e x t ---
+ // Use the empty function as closure (no scope info).
+ native_context()->set_closure(*empty_function);
+ native_context()->set_previous(NULL);
+ // Set extension and global object.
+ native_context()->set_extension(*inner_global);
+ native_context()->set_global_object(*inner_global);
+ // Security setup: Set the security token of the global object to
+ // its the inner global. This makes the security check between two
+ // different contexts fail by default even in case of global
+ // object reinitialization.
+ native_context()->set_security_token(*inner_global);
+
+ Isolate* isolate = inner_global->GetIsolate();
+ Factory* factory = isolate->factory();
+ Heap* heap = isolate->heap();
+
+ Handle<String> object_name = factory->Object_string();
+ CHECK_NOT_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ inner_global, object_name,
+ isolate->object_function(), DONT_ENUM));
+
+ Handle<JSObject> global = Handle<JSObject>(native_context()->global_object());
+
+ // Install global Function object
+ InstallFunction(global, "Function", JS_FUNCTION_TYPE, JSFunction::kSize,
+ empty_function, Builtins::kIllegal, true, true);
+
+ { // --- A r r a y ---
+ Handle<JSFunction> array_function =
+ InstallFunction(global, "Array", JS_ARRAY_TYPE, JSArray::kSize,
+ isolate->initial_object_prototype(),
+ Builtins::kArrayCode, true, true);
+ array_function->shared()->DontAdaptArguments();
+ array_function->shared()->set_function_data(Smi::FromInt(kArrayCode));
+
+ // This seems a bit hackish, but we need to make sure Array.length
+ // is 1.
+ array_function->shared()->set_length(1);
+
+ Handle<Map> initial_map(array_function->initial_map());
+
+ // This assert protects an optimization in
+ // HGraphBuilder::JSArrayBuilder::EmitMapCode()
+ ASSERT(initial_map->elements_kind() == GetInitialFastElementsKind());
+
+ Handle<DescriptorArray> array_descriptors(
+ factory->NewDescriptorArray(0, 1));
+ DescriptorArray::WhitenessWitness witness(*array_descriptors);
+
+ Handle<Foreign> array_length(factory->NewForeign(&Accessors::ArrayLength));
+ PropertyAttributes attribs = static_cast<PropertyAttributes>(
+ DONT_ENUM | DONT_DELETE);
+ initial_map->set_instance_descriptors(*array_descriptors);
+
+ { // Add length.
+ CallbacksDescriptor d(*factory->length_string(), *array_length, attribs);
+ array_function->initial_map()->AppendDescriptor(&d, witness);
+ }
+
+ // 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.
+ native_context()->set_array_function(*array_function);
+
+ // Cache the array maps, needed by ArrayConstructorStub
+ CacheInitialJSArrayMaps(native_context(), initial_map);
+ ArrayConstructorStub array_constructor_stub(isolate);
+ Handle<Code> code = array_constructor_stub.GetCode(isolate);
+ array_function->shared()->set_construct_stub(*code);
+ }
+
+ { // --- N u m b e r ---
+ Handle<JSFunction> number_fun =
+ InstallFunction(global, "Number", JS_VALUE_TYPE, JSValue::kSize,
+ isolate->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ native_context()->set_number_function(*number_fun);
+ }
+
+ { // --- B o o l e a n ---
+ Handle<JSFunction> boolean_fun =
+ InstallFunction(global, "Boolean", JS_VALUE_TYPE, JSValue::kSize,
+ isolate->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ native_context()->set_boolean_function(*boolean_fun);
+ }
+
+ { // --- S t r i n g ---
+ Handle<JSFunction> string_fun =
+ InstallFunction(global, "String", JS_VALUE_TYPE, JSValue::kSize,
+ isolate->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ string_fun->shared()->set_construct_stub(
+ isolate->builtins()->builtin(Builtins::kStringConstructCode));
+ native_context()->set_string_function(*string_fun);
+
+ Handle<Map> string_map =
+ Handle<Map>(native_context()->string_function()->initial_map());
+ Handle<DescriptorArray> string_descriptors(
+ factory->NewDescriptorArray(0, 1));
+ DescriptorArray::WhitenessWitness witness(*string_descriptors);
+
+ Handle<Foreign> string_length(
+ factory->NewForeign(&Accessors::StringLength));
+ PropertyAttributes attribs = static_cast<PropertyAttributes>(
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ string_map->set_instance_descriptors(*string_descriptors);
+
+ { // Add length.
+ CallbacksDescriptor d(*factory->length_string(), *string_length, attribs);
+ string_map->AppendDescriptor(&d, witness);
+ }
+ }
+
+ { // --- D a t e ---
+ // Builtin functions for Date.prototype.
+ Handle<JSFunction> date_fun =
+ InstallFunction(global, "Date", JS_DATE_TYPE, JSDate::kSize,
+ isolate->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+
+ native_context()->set_date_function(*date_fun);
+ }
+
+
+ { // -- R e g E x p
+ // Builtin functions for RegExp.prototype.
+ Handle<JSFunction> regexp_fun =
+ InstallFunction(global, "RegExp", JS_REGEXP_TYPE, JSRegExp::kSize,
+ isolate->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ native_context()->set_regexp_function(*regexp_fun);
+
+ ASSERT(regexp_fun->has_initial_map());
+ Handle<Map> initial_map(regexp_fun->initial_map());
+
+ ASSERT_EQ(0, initial_map->inobject_properties());
+
+ PropertyAttributes final =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
+ Handle<DescriptorArray> descriptors = factory->NewDescriptorArray(0, 5);
+ DescriptorArray::WhitenessWitness witness(*descriptors);
+ initial_map->set_instance_descriptors(*descriptors);
+
+ {
+ // ECMA-262, section 15.10.7.1.
+ FieldDescriptor field(heap->source_string(),
+ JSRegExp::kSourceFieldIndex,
+ final,
+ Representation::Tagged());
+ initial_map->AppendDescriptor(&field, witness);
+ }
+ {
+ // ECMA-262, section 15.10.7.2.
+ FieldDescriptor field(heap->global_string(),
+ JSRegExp::kGlobalFieldIndex,
+ final,
+ Representation::Tagged());
+ initial_map->AppendDescriptor(&field, witness);
+ }
+ {
+ // ECMA-262, section 15.10.7.3.
+ FieldDescriptor field(heap->ignore_case_string(),
+ JSRegExp::kIgnoreCaseFieldIndex,
+ final,
+ Representation::Tagged());
+ initial_map->AppendDescriptor(&field, witness);
+ }
+ {
+ // ECMA-262, section 15.10.7.4.
+ FieldDescriptor field(heap->multiline_string(),
+ JSRegExp::kMultilineFieldIndex,
+ final,
+ Representation::Tagged());
+ initial_map->AppendDescriptor(&field, witness);
+ }
+ {
+ // ECMA-262, section 15.10.7.5.
+ PropertyAttributes writable =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
+ FieldDescriptor field(heap->last_index_string(),
+ JSRegExp::kLastIndexFieldIndex,
+ writable,
+ Representation::Tagged());
+ initial_map->AppendDescriptor(&field, witness);
+ }
+
+ initial_map->set_inobject_properties(5);
+ initial_map->set_pre_allocated_property_fields(5);
+ initial_map->set_unused_property_fields(0);
+ initial_map->set_instance_size(
+ initial_map->instance_size() + 5 * kPointerSize);
+ initial_map->set_visitor_id(StaticVisitorBase::GetVisitorId(*initial_map));
+
+ // RegExp prototype object is itself a RegExp.
+ Handle<Map> proto_map = factory->CopyMap(initial_map);
+ proto_map->set_prototype(native_context()->initial_object_prototype());
+ Handle<JSObject> proto = factory->NewJSObjectFromMap(proto_map);
+ proto->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex,
+ heap->query_colon_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());
+ JSFunction::SetInstancePrototype(cons,
+ Handle<Object>(native_context()->initial_object_prototype(), isolate));
+ cons->SetInstanceClassName(*name);
+ Handle<JSObject> json_object = factory->NewJSObject(cons, TENURED);
+ ASSERT(json_object->IsJSObject());
+ CHECK_NOT_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ global, name, json_object, DONT_ENUM));
+ native_context()->set_json_object(*json_object);
+ }
+
+ { // --- arguments_boilerplate_
+ // Make sure we can recognize argument objects at runtime.
+ // This is done by introducing an anonymous function with
+ // class_name equals 'Arguments'.
+ Handle<String> arguments_string = factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("Arguments"));
+ Handle<Code> code = Handle<Code>(
+ isolate->builtins()->builtin(Builtins::kIllegal));
+ Handle<JSObject> prototype =
+ Handle<JSObject>(
+ JSObject::cast(native_context()->object_function()->prototype()));
+
+ Handle<JSFunction> function =
+ factory->NewFunctionWithPrototype(arguments_string,
+ JS_OBJECT_TYPE,
+ JSObject::kHeaderSize,
+ prototype,
+ code,
+ false);
+ ASSERT(!function->has_initial_map());
+ function->shared()->set_instance_class_name(*arguments_string);
+ function->shared()->set_expected_nof_properties(2);
+ Handle<JSObject> result = factory->NewJSObject(function);
+
+ native_context()->set_arguments_boilerplate(*result);
+ // Note: length must be added as the first property and
+ // callee must be added as the second property.
+ CHECK_NOT_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ result, factory->length_string(),
+ factory->undefined_value(), DONT_ENUM,
+ Object::FORCE_TAGGED, JSReceiver::FORCE_FIELD));
+ CHECK_NOT_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ result, factory->callee_string(),
+ factory->undefined_value(), DONT_ENUM,
+ Object::FORCE_TAGGED, JSReceiver::FORCE_FIELD));
+
+#ifdef DEBUG
+ LookupResult lookup(isolate);
+ result->LocalLookup(heap->callee_string(), &lookup);
+ ASSERT(lookup.IsField());
+ ASSERT(lookup.GetFieldIndex().field_index() == Heap::kArgumentsCalleeIndex);
+
+ result->LocalLookup(heap->length_string(), &lookup);
+ ASSERT(lookup.IsField());
+ ASSERT(lookup.GetFieldIndex().field_index() == Heap::kArgumentsLengthIndex);
+
+ ASSERT(result->map()->inobject_properties() > Heap::kArgumentsCalleeIndex);
+ ASSERT(result->map()->inobject_properties() > Heap::kArgumentsLengthIndex);
+
+ // Check the state of the object.
+ ASSERT(result->HasFastProperties());
+ ASSERT(result->HasFastObjectElements());
+#endif
+ }
+
+ { // --- aliased_arguments_boilerplate_
+ // 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());
+ Handle<FixedArray> array;
+ array = factory->NewFixedArray(0);
+ elements->set(0, *array);
+ array = factory->NewFixedArray(0);
+ elements->set(1, *array);
+
+ Handle<Map> old_map(native_context()->arguments_boilerplate()->map());
+ Handle<Map> new_map = factory->CopyMap(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());
+ native_context()->set_aliased_arguments_boilerplate(*result);
+ }
+
+ { // --- strict mode arguments boilerplate
+ const PropertyAttributes attributes =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
+
+ // Create the ThrowTypeError functions.
+ Handle<AccessorPair> callee = factory->NewAccessorPair();
+ Handle<AccessorPair> caller = factory->NewAccessorPair();
+
+ Handle<JSFunction> throw_function =
+ GetThrowTypeErrorFunction();
+
+ // Install the ThrowTypeError functions.
+ callee->set_getter(*throw_function);
+ callee->set_setter(*throw_function);
+ caller->set_getter(*throw_function);
+ caller->set_setter(*throw_function);
+
+ // Create the map. Allocate one in-object field for length.
+ Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE,
+ Heap::kArgumentsObjectSizeStrict);
+ // Create the descriptor array for the arguments object.
+ Handle<DescriptorArray> descriptors = factory->NewDescriptorArray(0, 3);
+ DescriptorArray::WhitenessWitness witness(*descriptors);
+ map->set_instance_descriptors(*descriptors);
+
+ { // length
+ FieldDescriptor d(
+ *factory->length_string(), 0, DONT_ENUM, Representation::Tagged());
+ map->AppendDescriptor(&d, witness);
+ }
+ { // callee
+ CallbacksDescriptor d(*factory->callee_string(),
+ *callee,
+ attributes);
+ map->AppendDescriptor(&d, witness);
+ }
+ { // caller
+ CallbacksDescriptor d(*factory->caller_string(),
+ *caller,
+ attributes);
+ map->AppendDescriptor(&d, witness);
+ }
+
+ map->set_function_with_prototype(true);
+ map->set_prototype(native_context()->object_function()->prototype());
+ map->set_pre_allocated_property_fields(1);
+ map->set_inobject_properties(1);
+
+ // Copy constructor from the non-strict arguments boilerplate.
+ map->set_constructor(
+ native_context()->arguments_boilerplate()->map()->constructor());
+
+ // Allocate the arguments boilerplate object.
+ Handle<JSObject> result = factory->NewJSObjectFromMap(map);
+ native_context()->set_strict_mode_arguments_boilerplate(*result);
+
+ // Add length property only for strict mode boilerplate.
+ CHECK_NOT_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ result, factory->length_string(),
+ factory->undefined_value(), DONT_ENUM));
+
+#ifdef DEBUG
+ LookupResult lookup(isolate);
+ result->LocalLookup(heap->length_string(), &lookup);
+ ASSERT(lookup.IsField());
+ ASSERT(lookup.GetFieldIndex().field_index() == Heap::kArgumentsLengthIndex);
+
+ ASSERT(result->map()->inobject_properties() > Heap::kArgumentsLengthIndex);
+
+ // Check the state of the object.
+ ASSERT(result->HasFastProperties());
+ ASSERT(result->HasFastObjectElements());
+#endif
+ }
+
+ { // --- context extension
+ // Create a function for the context extension objects.
+ Handle<Code> code = Handle<Code>(
+ isolate->builtins()->builtin(Builtins::kIllegal));
+ Handle<JSFunction> context_extension_fun =
+ factory->NewFunction(factory->empty_string(),
+ JS_CONTEXT_EXTENSION_OBJECT_TYPE,
+ JSObject::kHeaderSize,
+ code,
+ true);
+
+ Handle<String> name = factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("context_extension"));
+ context_extension_fun->shared()->set_instance_class_name(*name);
+ native_context()->set_context_extension_function(*context_extension_fun);
+ }
+
+
+ {
+ // Set up the call-as-function delegate.
+ Handle<Code> code =
+ Handle<Code>(isolate->builtins()->builtin(
+ Builtins::kHandleApiCallAsFunction));
+ Handle<JSFunction> delegate =
+ factory->NewFunction(factory->empty_string(), JS_OBJECT_TYPE,
+ JSObject::kHeaderSize, code, true);
+ native_context()->set_call_as_function_delegate(*delegate);
+ delegate->shared()->DontAdaptArguments();
+ }
+
+ {
+ // Set up the call-as-constructor delegate.
+ Handle<Code> code =
+ Handle<Code>(isolate->builtins()->builtin(
+ Builtins::kHandleApiCallAsConstructor));
+ Handle<JSFunction> delegate =
+ factory->NewFunction(factory->empty_string(), JS_OBJECT_TYPE,
+ JSObject::kHeaderSize, code, true);
+ native_context()->set_call_as_constructor_delegate(*delegate);
+ delegate->shared()->DontAdaptArguments();
+ }
+
+ // Initialize the out of memory slot.
+ native_context()->set_out_of_memory(heap->false_value());
+
+ // Initialize the embedder data slot.
+ Handle<FixedArray> embedder_data = factory->NewFixedArray(2);
+ native_context()->set_embedder_data(*embedder_data);
+
+ {
+ // Initialize the random seed slot.
+ Handle<ByteArray> zeroed_byte_array(
+ factory->NewByteArray(kRandomStateSize));
+ native_context()->set_random_seed(*zeroed_byte_array);
+ memset(zeroed_byte_array->GetDataStartAddress(), 0, kRandomStateSize);
+ }
+}
+
+
+Handle<JSFunction> Genesis::InstallTypedArray(
+ const char* name, ElementsKind elementsKind) {
+ Handle<JSObject> global = Handle<JSObject>(native_context()->global_object());
+ Handle<JSFunction> result = InstallFunction(global, name, JS_TYPED_ARRAY_TYPE,
+ JSTypedArray::kSize, isolate()->initial_object_prototype(),
+ Builtins::kIllegal, false, true);
+
+ Handle<Map> initial_map = isolate()->factory()->NewMap(
+ JS_TYPED_ARRAY_TYPE, JSTypedArray::kSizeWithInternalFields, elementsKind);
+ result->set_initial_map(*initial_map);
+ initial_map->set_constructor(*result);
+ return result;
+}
+
+
+void Genesis::InitializeExperimentalGlobal() {
+ Handle<JSObject> global = Handle<JSObject>(native_context()->global_object());
+
+ // TODO(mstarzinger): Move this into Genesis::InitializeGlobal once we no
+ // longer need to live behind flags, so functions get added to the snapshot.
+
+ if (FLAG_harmony_symbols) {
+ // --- S y m b o l ---
+ Handle<JSFunction> symbol_fun =
+ InstallFunction(global, "Symbol", JS_VALUE_TYPE, JSValue::kSize,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ native_context()->set_symbol_function(*symbol_fun);
+ }
+
+ if (FLAG_harmony_collections) {
+ { // -- S e t
+ InstallFunction(global, "Set", JS_SET_TYPE, JSSet::kSize,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ }
+ { // -- M a p
+ InstallFunction(global, "Map", JS_MAP_TYPE, JSMap::kSize,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ }
+ { // -- W e a k M a p
+ InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ }
+ { // -- W e a k S e t
+ InstallFunction(global, "WeakSet", JS_WEAK_SET_TYPE, JSWeakSet::kSize,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ }
+ }
+
+ if (FLAG_harmony_array_buffer) {
+ // -- A r r a y B u f f e r
+ Handle<JSFunction> array_buffer_fun =
+ InstallFunction(
+ global, "ArrayBuffer", JS_ARRAY_BUFFER_TYPE,
+ JSArrayBuffer::kSizeWithInternalFields,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ native_context()->set_array_buffer_fun(*array_buffer_fun);
+ }
+
+ if (FLAG_harmony_typed_arrays) {
+ // -- T y p e d A r r a y s
+ Handle<JSFunction> int8_fun = InstallTypedArray("Int8Array",
+ EXTERNAL_BYTE_ELEMENTS);
+ native_context()->set_int8_array_fun(*int8_fun);
+ Handle<JSFunction> uint8_fun = InstallTypedArray("Uint8Array",
+ EXTERNAL_UNSIGNED_BYTE_ELEMENTS);
+ native_context()->set_uint8_array_fun(*uint8_fun);
+ Handle<JSFunction> int16_fun = InstallTypedArray("Int16Array",
+ EXTERNAL_SHORT_ELEMENTS);
+ native_context()->set_int16_array_fun(*int16_fun);
+ Handle<JSFunction> uint16_fun = InstallTypedArray("Uint16Array",
+ EXTERNAL_UNSIGNED_SHORT_ELEMENTS);
+ native_context()->set_uint16_array_fun(*uint16_fun);
+ Handle<JSFunction> int32_fun = InstallTypedArray("Int32Array",
+ EXTERNAL_INT_ELEMENTS);
+ native_context()->set_int32_array_fun(*int32_fun);
+ Handle<JSFunction> uint32_fun = InstallTypedArray("Uint32Array",
+ EXTERNAL_UNSIGNED_INT_ELEMENTS);
+ native_context()->set_uint32_array_fun(*uint32_fun);
+ Handle<JSFunction> float_fun = InstallTypedArray("Float32Array",
+ EXTERNAL_FLOAT_ELEMENTS);
+ native_context()->set_float_array_fun(*float_fun);
+ Handle<JSFunction> double_fun = InstallTypedArray("Float64Array",
+ EXTERNAL_DOUBLE_ELEMENTS);
+ native_context()->set_double_array_fun(*double_fun);
+ Handle<JSFunction> uint8c_fun = InstallTypedArray("Uint8ClampedArray",
+ EXTERNAL_PIXEL_ELEMENTS);
+ native_context()->set_uint8c_array_fun(*uint8c_fun);
+
+ Handle<JSFunction> data_view_fun =
+ InstallFunction(
+ global, "DataView", JS_DATA_VIEW_TYPE,
+ JSDataView::kSizeWithInternalFields,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, true, true);
+ native_context()->set_data_view_fun(*data_view_fun);
+ }
+
+ if (FLAG_harmony_generators) {
+ // Create generator meta-objects and install them on the builtins object.
+ Handle<JSObject> builtins(native_context()->builtins());
+ Handle<JSObject> generator_object_prototype =
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
+ Handle<JSFunction> generator_function_prototype =
+ InstallFunction(builtins, "GeneratorFunctionPrototype",
+ JS_FUNCTION_TYPE, JSFunction::kHeaderSize,
+ generator_object_prototype, Builtins::kIllegal,
+ false, false);
+ InstallFunction(builtins, "GeneratorFunction",
+ JS_FUNCTION_TYPE, JSFunction::kSize,
+ generator_function_prototype, Builtins::kIllegal,
+ false, false);
+
+ // Create maps for generator functions and their prototypes. Store those
+ // maps in the native context.
+ Handle<Map> function_map(native_context()->function_map());
+ Handle<Map> generator_function_map = factory()->CopyMap(function_map);
+ generator_function_map->set_prototype(*generator_function_prototype);
+ native_context()->set_generator_function_map(*generator_function_map);
+
+ Handle<Map> strict_mode_function_map(
+ native_context()->strict_mode_function_map());
+ Handle<Map> strict_mode_generator_function_map = factory()->CopyMap(
+ strict_mode_function_map);
+ strict_mode_generator_function_map->set_prototype(
+ *generator_function_prototype);
+ native_context()->set_strict_mode_generator_function_map(
+ *strict_mode_generator_function_map);
+
+ Handle<Map> object_map(native_context()->object_function()->initial_map());
+ Handle<Map> generator_object_prototype_map = factory()->CopyMap(
+ object_map, 0);
+ generator_object_prototype_map->set_prototype(
+ *generator_object_prototype);
+ native_context()->set_generator_object_prototype_map(
+ *generator_object_prototype_map);
+
+ // Create a map for generator result objects.
+ ASSERT(object_map->inobject_properties() == 0);
+ STATIC_ASSERT(JSGeneratorObject::kResultPropertyCount == 2);
+ Handle<Map> generator_result_map = factory()->CopyMap(object_map,
+ JSGeneratorObject::kResultPropertyCount);
+ ASSERT(generator_result_map->inobject_properties() ==
+ JSGeneratorObject::kResultPropertyCount);
+
+ Handle<DescriptorArray> descriptors = factory()->NewDescriptorArray(0,
+ JSGeneratorObject::kResultPropertyCount);
+ DescriptorArray::WhitenessWitness witness(*descriptors);
+ generator_result_map->set_instance_descriptors(*descriptors);
+
+ Handle<String> value_string = factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("value"));
+ FieldDescriptor value_descr(*value_string,
+ JSGeneratorObject::kResultValuePropertyIndex,
+ NONE,
+ Representation::Tagged());
+ generator_result_map->AppendDescriptor(&value_descr, witness);
+
+ Handle<String> done_string = factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("done"));
+ FieldDescriptor done_descr(*done_string,
+ JSGeneratorObject::kResultDonePropertyIndex,
+ NONE,
+ Representation::Tagged());
+ generator_result_map->AppendDescriptor(&done_descr, witness);
+
+ generator_result_map->set_unused_property_fields(0);
+ ASSERT_EQ(JSGeneratorObject::kResultSize,
+ generator_result_map->instance_size());
+ native_context()->set_generator_result_map(*generator_result_map);
+ }
+}
+
+
+bool Genesis::CompileBuiltin(Isolate* isolate, int index) {
+ Vector<const char> name = Natives::GetScriptName(index);
+ Handle<String> source_code =
+ isolate->bootstrapper()->NativesSourceLookup(index);
+ return CompileNative(isolate, name, source_code);
+}
+
+
+bool Genesis::CompileExperimentalBuiltin(Isolate* isolate, int index) {
+ Vector<const char> name = ExperimentalNatives::GetScriptName(index);
+ Factory* factory = isolate->factory();
+ Handle<String> source_code =
+ factory->NewStringFromAscii(
+ ExperimentalNatives::GetRawScriptSource(index));
+ return CompileNative(isolate, name, source_code);
+}
+
+
+bool Genesis::CompileNative(Isolate* isolate,
+ Vector<const char> name,
+ Handle<String> source) {
+ HandleScope scope(isolate);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ isolate->debugger()->set_compiling_natives(true);
+#endif
+ // During genesis, the boilerplate for stack overflow won't work until the
+ // environment has been at least partially initialized. Add a stack check
+ // before entering JS code to catch overflow early.
+ StackLimitCheck check(isolate);
+ if (check.HasOverflowed()) return false;
+
+ bool result = CompileScriptCached(isolate,
+ name,
+ source,
+ NULL,
+ NULL,
+ Handle<Context>(isolate->context()),
+ true);
+ ASSERT(isolate->has_pending_exception() != result);
+ if (!result) isolate->clear_pending_exception();
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ isolate->debugger()->set_compiling_natives(false);
+#endif
+ return result;
+}
+
+
+bool Genesis::CompileScriptCached(Isolate* isolate,
+ Vector<const char> name,
+ Handle<String> source,
+ SourceCodeCache* cache,
+ v8::Extension* extension,
+ Handle<Context> top_context,
+ bool use_runtime_context) {
+ Factory* factory = isolate->factory();
+ HandleScope scope(isolate);
+ Handle<SharedFunctionInfo> function_info;
+
+ // If we can't find the function in the cache, we compile a new
+ // function and insert it into the cache.
+ if (cache == NULL || !cache->Lookup(name, &function_info)) {
+ ASSERT(source->IsOneByteRepresentation());
+ Handle<String> script_name = factory->NewStringFromUtf8(name);
+ function_info = Compiler::Compile(
+ source,
+ script_name,
+ 0,
+ 0,
+ false,
+ top_context,
+ extension,
+ NULL,
+ Handle<String>::null(),
+ use_runtime_context ? NATIVES_CODE : NOT_NATIVES_CODE);
+ if (function_info.is_null()) return false;
+ if (cache != NULL) cache->Add(name, function_info);
+ }
+
+ // 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->IsNativeContext());
+ Handle<Context> context =
+ Handle<Context>(use_runtime_context
+ ? Handle<Context>(top_context->runtime_context())
+ : top_context);
+ Handle<JSFunction> fun =
+ factory->NewFunctionFromSharedFunctionInfo(function_info, context);
+
+ // Call function using either the runtime object or the global
+ // object as the receiver. Provide no parameters.
+ Handle<Object> receiver =
+ Handle<Object>(use_runtime_context
+ ? top_context->builtins()
+ : top_context->global_object(),
+ isolate);
+ bool has_pending_exception;
+ Execution::Call(fun, receiver, 0, NULL, &has_pending_exception);
+ if (has_pending_exception) return false;
+ return true;
+}
+
+
+#define INSTALL_NATIVE(Type, name, var) \
+ Handle<String> var##_name = \
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR(name)); \
+ Object* var##_native = \
+ native_context()->builtins()->GetPropertyNoExceptionThrown( \
+ *var##_name); \
+ native_context()->set_##var(Type::cast(var##_native));
+
+
+void Genesis::InstallNativeFunctions() {
+ HandleScope scope(isolate());
+ INSTALL_NATIVE(JSFunction, "CreateDate", create_date_fun);
+ INSTALL_NATIVE(JSFunction, "ToNumber", to_number_fun);
+ INSTALL_NATIVE(JSFunction, "ToString", to_string_fun);
+ INSTALL_NATIVE(JSFunction, "ToDetailString", to_detail_string_fun);
+ INSTALL_NATIVE(JSFunction, "ToObject", to_object_fun);
+ INSTALL_NATIVE(JSFunction, "ToInteger", to_integer_fun);
+ INSTALL_NATIVE(JSFunction, "ToUint32", to_uint32_fun);
+ INSTALL_NATIVE(JSFunction, "ToInt32", to_int32_fun);
+ INSTALL_NATIVE(JSFunction, "GlobalEval", global_eval_fun);
+ INSTALL_NATIVE(JSFunction, "Instantiate", instantiate_fun);
+ INSTALL_NATIVE(JSFunction, "ConfigureTemplateInstance",
+ 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() {
+ if (FLAG_harmony_proxies) {
+ 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);
+ }
+ if (FLAG_harmony_observation) {
+ INSTALL_NATIVE(JSFunction, "NotifyChange", observers_notify_change);
+ INSTALL_NATIVE(JSFunction, "EnqueueSpliceRecord", observers_enqueue_splice);
+ INSTALL_NATIVE(JSFunction, "BeginPerformSplice",
+ observers_begin_perform_splice);
+ INSTALL_NATIVE(JSFunction, "EndPerformSplice",
+ observers_end_perform_splice);
+ INSTALL_NATIVE(JSFunction, "DeliverChangeRecords",
+ observers_deliver_changes);
+ }
+}
+
+#undef INSTALL_NATIVE
+
+
+Handle<JSFunction> Genesis::InstallInternalArray(
+ Handle<JSBuiltinsObject> builtins,
+ const char* name,
+ ElementsKind elements_kind) {
+ // --- I n t e r n a l A r r a y ---
+ // An array constructor on the builtins object that works like
+ // the public Array constructor, except that its prototype
+ // doesn't inherit from Object.prototype.
+ // To be used only for internal work by builtins. Instances
+ // must not be leaked to user code.
+ Handle<JSFunction> array_function =
+ InstallFunction(builtins,
+ name,
+ JS_ARRAY_TYPE,
+ JSArray::kSize,
+ isolate()->initial_object_prototype(),
+ Builtins::kInternalArrayCode,
+ true, true);
+ Handle<JSObject> prototype =
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
+ SetPrototype(array_function, prototype);
+
+ InternalArrayConstructorStub internal_array_constructor_stub(isolate());
+ Handle<Code> code = internal_array_constructor_stub.GetCode(isolate());
+ array_function->shared()->set_construct_stub(*code);
+ array_function->shared()->DontAdaptArguments();
+
+ Handle<Map> original_map(array_function->initial_map());
+ Handle<Map> initial_map = factory()->CopyMap(original_map);
+ initial_map->set_elements_kind(elements_kind);
+ array_function->set_initial_map(*initial_map);
+
+ // Make "length" magic on instances.
+ Handle<DescriptorArray> array_descriptors(
+ factory()->NewDescriptorArray(0, 1));
+ DescriptorArray::WhitenessWitness witness(*array_descriptors);
+
+ Handle<Foreign> array_length(factory()->NewForeign(
+ &Accessors::ArrayLength));
+ PropertyAttributes attribs = static_cast<PropertyAttributes>(
+ DONT_ENUM | DONT_DELETE);
+ initial_map->set_instance_descriptors(*array_descriptors);
+
+ { // Add length.
+ CallbacksDescriptor d(
+ *factory()->length_string(), *array_length, attribs);
+ array_function->initial_map()->AppendDescriptor(&d, witness);
+ }
+
+ return array_function;
+}
+
+
+bool Genesis::InstallNatives() {
+ HandleScope scope(isolate());
+
+ // Create a function for the builtins object. Allocate space for the
+ // JavaScript builtins, a reference to the builtins object
+ // (itself) and a reference to the native_context directly in the object.
+ Handle<Code> code = Handle<Code>(
+ isolate()->builtins()->builtin(Builtins::kIllegal));
+ Handle<JSFunction> builtins_fun =
+ factory()->NewFunction(factory()->empty_string(),
+ JS_BUILTINS_OBJECT_TYPE,
+ JSBuiltinsObject::kSize, code, true);
+
+ Handle<String> name =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("builtins"));
+ builtins_fun->shared()->set_instance_class_name(*name);
+ builtins_fun->initial_map()->set_dictionary_map(true);
+ builtins_fun->initial_map()->set_prototype(heap()->null_value());
+
+ // Allocate the builtins object.
+ Handle<JSBuiltinsObject> builtins =
+ Handle<JSBuiltinsObject>::cast(factory()->NewGlobalObject(builtins_fun));
+ builtins->set_builtins(*builtins);
+ builtins->set_native_context(*native_context());
+ builtins->set_global_context(*native_context());
+ builtins->set_global_receiver(*builtins);
+
+ // 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.
+ static const PropertyAttributes attributes =
+ static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
+ Handle<String> global_string =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("global"));
+ Handle<Object> global_obj(native_context()->global_object(), isolate());
+ CHECK_NOT_EMPTY_HANDLE(isolate(),
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ builtins, global_string, global_obj, attributes));
+
+ // Set up the reference from the global object to the builtins object.
+ JSGlobalObject::cast(native_context()->global_object())->
+ set_builtins(*builtins);
+
+ // Create a bridge function that has context in the native context.
+ Handle<JSFunction> bridge =
+ factory()->NewFunction(factory()->empty_string(),
+ factory()->undefined_value());
+ ASSERT(bridge->context() == *isolate()->native_context());
+
+ // Allocate the builtins context.
+ Handle<Context> context =
+ factory()->NewFunctionContext(Context::MIN_CONTEXT_SLOTS, bridge);
+ context->set_global_object(*builtins); // override builtins global object
+
+ native_context()->set_runtime_context(*context);
+
+ { // -- S c r i p t
+ // Builtin functions for Script.
+ Handle<JSFunction> script_fun =
+ InstallFunction(builtins, "Script", JS_VALUE_TYPE, JSValue::kSize,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, false, false);
+ Handle<JSObject> prototype =
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
+ SetPrototype(script_fun, prototype);
+ native_context()->set_script_function(*script_fun);
+
+ Handle<Map> script_map = Handle<Map>(script_fun->initial_map());
+
+ Handle<DescriptorArray> script_descriptors(
+ factory()->NewDescriptorArray(0, 13));
+ DescriptorArray::WhitenessWitness witness(*script_descriptors);
+
+ Handle<Foreign> script_source(
+ factory()->NewForeign(&Accessors::ScriptSource));
+ Handle<Foreign> script_name(factory()->NewForeign(&Accessors::ScriptName));
+ Handle<String> id_string(factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("id")));
+ Handle<Foreign> script_id(factory()->NewForeign(&Accessors::ScriptId));
+ Handle<String> line_offset_string(
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("line_offset")));
+ Handle<Foreign> script_line_offset(
+ factory()->NewForeign(&Accessors::ScriptLineOffset));
+ Handle<String> column_offset_string(
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("column_offset")));
+ Handle<Foreign> script_column_offset(
+ factory()->NewForeign(&Accessors::ScriptColumnOffset));
+ Handle<String> data_string(factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("data")));
+ Handle<Foreign> script_data(factory()->NewForeign(&Accessors::ScriptData));
+ Handle<String> type_string(factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("type")));
+ Handle<Foreign> script_type(factory()->NewForeign(&Accessors::ScriptType));
+ Handle<String> compilation_type_string(
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("compilation_type")));
+ Handle<Foreign> script_compilation_type(
+ factory()->NewForeign(&Accessors::ScriptCompilationType));
+ Handle<String> line_ends_string(factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("line_ends")));
+ Handle<Foreign> script_line_ends(
+ factory()->NewForeign(&Accessors::ScriptLineEnds));
+ Handle<String> context_data_string(
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("context_data")));
+ Handle<Foreign> script_context_data(
+ factory()->NewForeign(&Accessors::ScriptContextData));
+ Handle<String> eval_from_script_string(
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("eval_from_script")));
+ Handle<Foreign> script_eval_from_script(
+ factory()->NewForeign(&Accessors::ScriptEvalFromScript));
+ Handle<String> eval_from_script_position_string(
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("eval_from_script_position")));
+ Handle<Foreign> script_eval_from_script_position(
+ factory()->NewForeign(&Accessors::ScriptEvalFromScriptPosition));
+ Handle<String> eval_from_function_name_string(
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("eval_from_function_name")));
+ Handle<Foreign> script_eval_from_function_name(
+ factory()->NewForeign(&Accessors::ScriptEvalFromFunctionName));
+ PropertyAttributes attribs =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
+ script_map->set_instance_descriptors(*script_descriptors);
+
+ {
+ CallbacksDescriptor d(
+ *factory()->source_string(), *script_source, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(*factory()->name_string(), *script_name, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(*id_string, *script_id, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(*line_offset_string, *script_line_offset, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(
+ *column_offset_string, *script_column_offset, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(*data_string, *script_data, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(*type_string, *script_type, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(
+ *compilation_type_string, *script_compilation_type, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(*line_ends_string, *script_line_ends, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(
+ *context_data_string, *script_context_data, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(
+ *eval_from_script_string, *script_eval_from_script, attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(
+ *eval_from_script_position_string,
+ *script_eval_from_script_position,
+ attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ {
+ CallbacksDescriptor d(
+ *eval_from_function_name_string,
+ *script_eval_from_function_name,
+ attribs);
+ script_map->AppendDescriptor(&d, witness);
+ }
+
+ // Allocate the empty script.
+ Handle<Script> script = factory()->NewScript(factory()->empty_string());
+ script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
+ heap()->public_set_empty_script(*script);
+ }
+ {
+ // Builtin function for OpaqueReference -- a JSValue-based object,
+ // that keeps its field isolated from JavaScript code. It may store
+ // objects, that JavaScript code may not access.
+ Handle<JSFunction> opaque_reference_fun =
+ InstallFunction(builtins, "OpaqueReference", JS_VALUE_TYPE,
+ JSValue::kSize,
+ isolate()->initial_object_prototype(),
+ Builtins::kIllegal, false, false);
+ Handle<JSObject> prototype =
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
+ SetPrototype(opaque_reference_fun, prototype);
+ native_context()->set_opaque_reference_function(*opaque_reference_fun);
+ }
+
+ // 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.
+ {
+ Handle<JSFunction> array_function =
+ InstallInternalArray(builtins, "InternalArray", FAST_HOLEY_ELEMENTS);
+ native_context()->set_internal_array_function(*array_function);
+ }
+
+ {
+ InstallInternalArray(builtins, "InternalPackedArray", FAST_ELEMENTS);
+ }
+
+ if (FLAG_disable_native_files) {
+ PrintF("Warning: Running without installed natives!\n");
+ return true;
+ }
+
+ // Install natives.
+ for (int i = Natives::GetDebuggerCount();
+ i < Natives::GetBuiltinsCount();
+ i++) {
+ if (!CompileBuiltin(isolate(), i)) return false;
+ // TODO(ager): We really only need to install the JS builtin
+ // functions on the builtins object after compiling and running
+ // runtime.js.
+ if (!InstallJSBuiltins(builtins)) return false;
+ }
+
+ InstallNativeFunctions();
+
+ // Store the map for the string prototype after the natives has been compiled
+ // and the String function has been set up.
+ Handle<JSFunction> string_function(native_context()->string_function());
+ ASSERT(JSObject::cast(
+ string_function->initial_map()->prototype())->HasFastProperties());
+ native_context()->set_string_function_prototype_map(
+ HeapObject::cast(string_function->initial_map()->prototype())->map());
+
+ // Install Function.prototype.call and apply.
+ { Handle<String> key = factory()->function_class_string();
+ Handle<JSFunction> function =
+ Handle<JSFunction>::cast(
+ GetProperty(isolate(), isolate()->global_object(), key));
+ Handle<JSObject> proto =
+ Handle<JSObject>(JSObject::cast(function->instance_prototype()));
+
+ // Install the call and the apply functions.
+ Handle<JSFunction> call =
+ InstallFunction(proto, "call", JS_OBJECT_TYPE, JSObject::kHeaderSize,
+ Handle<JSObject>::null(),
+ Builtins::kFunctionCall,
+ false, false);
+ Handle<JSFunction> apply =
+ InstallFunction(proto, "apply", JS_OBJECT_TYPE, JSObject::kHeaderSize,
+ Handle<JSObject>::null(),
+ Builtins::kFunctionApply,
+ false, false);
+
+ // Make sure that Function.prototype.call appears to be compiled.
+ // The code will never be called, but inline caching for call will
+ // only work if it appears to be compiled.
+ call->shared()->DontAdaptArguments();
+ ASSERT(call->is_compiled());
+
+ // Set the expected parameters for apply to 2; required by builtin.
+ apply->shared()->set_formal_parameter_count(2);
+
+ // Set the lengths for the functions to satisfy ECMA-262.
+ call->shared()->set_length(1);
+ apply->shared()->set_length(2);
+ }
+
+ InstallBuiltinFunctionIds();
+
+ // Create a constructor for RegExp results (a variant of Array that
+ // predefines the two properties index and match).
+ {
+ // RegExpResult initial map.
+
+ // Find global.Array.prototype to inherit from.
+ Handle<JSFunction> array_constructor(native_context()->array_function());
+ Handle<JSObject> array_prototype(
+ JSObject::cast(array_constructor->instance_prototype()));
+
+ // Add initial map.
+ Handle<Map> initial_map =
+ factory()->NewMap(JS_ARRAY_TYPE, JSRegExpResult::kSize);
+ initial_map->set_constructor(*array_constructor);
+
+ // Set prototype on map.
+ initial_map->set_non_instance_prototype(false);
+ initial_map->set_prototype(*array_prototype);
+
+ // Update map with length accessor from Array and add "index" and "input".
+ Handle<DescriptorArray> reresult_descriptors =
+ factory()->NewDescriptorArray(0, 3);
+ DescriptorArray::WhitenessWitness witness(*reresult_descriptors);
+ initial_map->set_instance_descriptors(*reresult_descriptors);
+
+ {
+ JSFunction* array_function = native_context()->array_function();
+ Handle<DescriptorArray> array_descriptors(
+ array_function->initial_map()->instance_descriptors());
+ String* length = heap()->length_string();
+ int old = array_descriptors->SearchWithCache(
+ length, array_function->initial_map());
+ ASSERT(old != DescriptorArray::kNotFound);
+ CallbacksDescriptor desc(length,
+ array_descriptors->GetValue(old),
+ array_descriptors->GetDetails(old).attributes());
+ initial_map->AppendDescriptor(&desc, witness);
+ }
+ {
+ FieldDescriptor index_field(heap()->index_string(),
+ JSRegExpResult::kIndexIndex,
+ NONE,
+ Representation::Tagged());
+ initial_map->AppendDescriptor(&index_field, witness);
+ }
+
+ {
+ FieldDescriptor input_field(heap()->input_string(),
+ JSRegExpResult::kInputIndex,
+ NONE,
+ Representation::Tagged());
+ initial_map->AppendDescriptor(&input_field, witness);
+ }
+
+ initial_map->set_inobject_properties(2);
+ initial_map->set_pre_allocated_property_fields(2);
+ initial_map->set_unused_property_fields(0);
+
+ native_context()->set_regexp_result_map(*initial_map);
+ }
+
+#ifdef VERIFY_HEAP
+ builtins->Verify();
+#endif
+
+ return true;
+}
+
+
+bool Genesis::InstallExperimentalNatives() {
+ for (int i = ExperimentalNatives::GetDebuggerCount();
+ i < ExperimentalNatives::GetBuiltinsCount();
+ i++) {
+ if (FLAG_harmony_symbols &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native symbol.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_proxies &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native proxy.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_collections &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native collection.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_observation &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native object-observe.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_array_buffer &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native arraybuffer.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_typed_arrays &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native typedarray.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_generators &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native generator.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_iteration &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native array-iterator.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_strings &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native harmony-string.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ if (FLAG_harmony_arrays &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native harmony-array.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ }
+
+ InstallExperimentalNativeFunctions();
+
+ return true;
+}
+
+
+static Handle<JSObject> ResolveBuiltinIdHolder(
+ Handle<Context> native_context,
+ const char* holder_expr) {
+ Isolate* isolate = native_context->GetIsolate();
+ Factory* factory = isolate->factory();
+ Handle<GlobalObject> global(native_context->global_object());
+ const char* period_pos = strchr(holder_expr, '.');
+ if (period_pos == NULL) {
+ return Handle<JSObject>::cast(GetProperty(
+ isolate, global, factory->InternalizeUtf8String(holder_expr)));
+ }
+ ASSERT_EQ(".prototype", period_pos);
+ Vector<const char> property(holder_expr,
+ static_cast<int>(period_pos - holder_expr));
+ Handle<JSFunction> function = Handle<JSFunction>::cast(
+ GetProperty(isolate, global, factory->InternalizeUtf8String(property)));
+ return Handle<JSObject>(JSObject::cast(function->prototype()));
+}
+
+
+static void InstallBuiltinFunctionId(Handle<JSObject> holder,
+ const char* function_name,
+ BuiltinFunctionId id) {
+ Factory* factory = holder->GetIsolate()->factory();
+ Handle<String> name = factory->InternalizeUtf8String(function_name);
+ Object* function_object = holder->GetProperty(*name)->ToObjectUnchecked();
+ Handle<JSFunction> function(JSFunction::cast(function_object));
+ function->shared()->set_function_data(Smi::FromInt(id));
+}
+
+
+void Genesis::InstallBuiltinFunctionIds() {
+ HandleScope scope(isolate());
+#define INSTALL_BUILTIN_ID(holder_expr, fun_name, name) \
+ { \
+ Handle<JSObject> holder = ResolveBuiltinIdHolder( \
+ native_context(), #holder_expr); \
+ BuiltinFunctionId id = k##name; \
+ InstallBuiltinFunctionId(holder, #fun_name, id); \
+ }
+ FUNCTIONS_WITH_ID_LIST(INSTALL_BUILTIN_ID)
+#undef INSTALL_BUILTIN_ID
+}
+
+
+// Do not forget to update macros.py with named constant
+// of cache id.
+#define JSFUNCTION_RESULT_CACHE_LIST(F) \
+ F(16, native_context()->regexp_function())
+
+
+static FixedArray* CreateCache(int size, Handle<JSFunction> factory_function) {
+ Factory* factory = factory_function->GetIsolate()->factory();
+ // Caches are supposed to live for a long time, allocate in old space.
+ int array_size = JSFunctionResultCache::kEntriesIndex + 2 * size;
+ // Cannot use cast as object is not fully initialized yet.
+ JSFunctionResultCache* cache = reinterpret_cast<JSFunctionResultCache*>(
+ *factory->NewFixedArrayWithHoles(array_size, TENURED));
+ cache->set(JSFunctionResultCache::kFactoryIndex, *factory_function);
+ cache->MakeZeroSize();
+ return cache;
+}
+
+
+void Genesis::InstallJSFunctionResultCaches() {
+ const int kNumberOfCaches = 0 +
+#define F(size, func) + 1
+ JSFUNCTION_RESULT_CACHE_LIST(F)
+#undef F
+ ;
+
+ Handle<FixedArray> caches =
+ factory()->NewFixedArray(kNumberOfCaches, TENURED);
+
+ int index = 0;
+
+#define F(size, func) do { \
+ FixedArray* cache = CreateCache((size), Handle<JSFunction>(func)); \
+ caches->set(index++, cache); \
+ } while (false)
+
+ JSFUNCTION_RESULT_CACHE_LIST(F);
+
+#undef F
+
+ native_context()->set_jsfunction_result_caches(*caches);
+}
+
+
+void Genesis::InitializeNormalizedMapCaches() {
+ Handle<FixedArray> array(
+ factory()->NewFixedArray(NormalizedMapCache::kEntries, TENURED));
+ native_context()->set_normalized_map_cache(NormalizedMapCache::cast(*array));
+}
+
+
+bool Bootstrapper::InstallExtensions(Handle<Context> native_context,
+ v8::ExtensionConfiguration* extensions) {
+ BootstrapperActive active(this);
+ SaveContext saved_context(isolate_);
+ isolate_->set_context(*native_context);
+ if (!Genesis::InstallExtensions(native_context, extensions)) return false;
+ Genesis::InstallSpecialObjects(native_context);
+ return true;
+}
+
+
+void Genesis::InstallSpecialObjects(Handle<Context> native_context) {
+ Isolate* isolate = native_context->GetIsolate();
+ Factory* factory = isolate->factory();
+ HandleScope scope(isolate);
+ Handle<JSGlobalObject> global(JSGlobalObject::cast(
+ native_context->global_object()));
+ // 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 =
+ factory->InternalizeUtf8String(FLAG_expose_natives_as);
+ CHECK_NOT_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ global, natives,
+ Handle<JSObject>(global->builtins()),
+ DONT_ENUM));
+ }
+
+ Handle<Object> Error = GetProperty(global, "Error");
+ if (Error->IsJSObject()) {
+ Handle<String> name = factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("stackTraceLimit"));
+ Handle<Smi> stack_trace_limit(
+ Smi::FromInt(FLAG_stack_trace_limit), isolate);
+ CHECK_NOT_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ Handle<JSObject>::cast(Error), name,
+ stack_trace_limit, NONE));
+ }
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Expose the debug global object in global if a name for it is specified.
+ if (FLAG_expose_debug_as != NULL && strlen(FLAG_expose_debug_as) != 0) {
+ Debug* debug = isolate->debug();
+ // If loading fails we just bail out without installing the
+ // debugger but without tanking the whole context.
+ if (!debug->Load()) return;
+ // Set the security token for the debugger context to the same as
+ // the shell native context to allow calling between these (otherwise
+ // exposing debug global object doesn't make much sense).
+ debug->debug_context()->set_security_token(
+ native_context->security_token());
+
+ Handle<String> debug_string =
+ factory->InternalizeUtf8String(FLAG_expose_debug_as);
+ Handle<Object> global_proxy(
+ debug->debug_context()->global_proxy(), isolate);
+ 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()
+ : map_(MatchRegisteredExtensions, 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> native_context,
+ v8::ExtensionConfiguration* extensions) {
+ Isolate* isolate = native_context->GetIsolate();
+ ExtensionStates extension_states; // All extensions have state UNVISITED.
+ // Install auto extensions.
+ v8::RegisteredExtension* current = v8::RegisteredExtension::first_extension();
+ while (current != NULL) {
+ if (current->extension()->auto_enable())
+ InstallExtension(isolate, current, &extension_states);
+ current = current->next();
+ }
+
+ if (FLAG_expose_gc) InstallExtension(isolate, "v8/gc", &extension_states);
+ if (FLAG_expose_externalize_string) {
+ InstallExtension(isolate, "v8/externalize", &extension_states);
+ }
+ if (FLAG_track_gc_object_stats) {
+ InstallExtension(isolate, "v8/statistics", &extension_states);
+ }
+
+#if defined(V8_I18N_SUPPORT)
+ if (FLAG_enable_i18n) {
+ InstallExtension(isolate, "v8/i18n", &extension_states);
+ }
+#endif
+
+ 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(isolate, names[i], &extension_states))
+ return false;
+ }
+
+ return true;
+}
+
+
+// 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(Isolate* isolate,
+ const char* name,
+ ExtensionStates* extension_states) {
+ v8::RegisteredExtension* current = v8::RegisteredExtension::first_extension();
+ // Loop until we find the relevant extension
+ while (current != NULL) {
+ if (strcmp(name, current->extension()->name()) == 0) break;
+ current = current->next();
+ }
+ // Didn't find the extension; fail.
+ if (current == NULL) {
+ v8::Utils::ReportApiFailure(
+ "v8::Context::New()", "Cannot find required extension");
+ return false;
+ }
+ return InstallExtension(isolate, current, extension_states);
+}
+
+
+bool Genesis::InstallExtension(Isolate* isolate,
+ v8::RegisteredExtension* current,
+ ExtensionStates* extension_states) {
+ HandleScope scope(isolate);
+
+ 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 (extension_states->get_state(current) == VISITED) {
+ v8::Utils::ReportApiFailure(
+ "v8::Context::New()", "Circular extension dependency");
+ return false;
+ }
+ 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(isolate,
+ extension->dependencies()[i],
+ extension_states)) {
+ return false;
+ }
+ }
+ Handle<String> source_code =
+ isolate->factory()->NewExternalStringFromAscii(extension->source());
+ bool result = CompileScriptCached(isolate,
+ 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();
+ }
+ extension_states->set_state(current, INSTALLED);
+ isolate->NotifyExtensionInstalled();
+ return result;
+}
+
+
+bool Genesis::InstallJSBuiltins(Handle<JSBuiltinsObject> builtins) {
+ HandleScope scope(isolate());
+ for (int i = 0; i < Builtins::NumberOfJavaScriptBuiltins(); i++) {
+ Builtins::JavaScript id = static_cast<Builtins::JavaScript>(i);
+ Handle<String> name =
+ factory()->InternalizeUtf8String(Builtins::GetName(id));
+ Object* function_object = builtins->GetPropertyNoExceptionThrown(*name);
+ Handle<JSFunction> function
+ = Handle<JSFunction>(JSFunction::cast(function_object));
+ builtins->set_javascript_builtin(id, *function);
+ if (!JSFunction::CompileLazy(function, CLEAR_EXCEPTION)) {
+ return false;
+ }
+ builtins->set_javascript_builtin_code(id, function->shared()->code());
+ }
+ return true;
+}
+
+
+bool Genesis::ConfigureGlobalObjects(
+ v8::Handle<v8::ObjectTemplate> global_proxy_template) {
+ Handle<JSObject> global_proxy(
+ JSObject::cast(native_context()->global_proxy()));
+ Handle<JSObject> inner_global(
+ JSObject::cast(native_context()->global_object()));
+
+ if (!global_proxy_template.IsEmpty()) {
+ // Configure the global proxy object.
+ Handle<ObjectTemplateInfo> proxy_data =
+ v8::Utils::OpenHandle(*global_proxy_template);
+ if (!ConfigureApiObject(global_proxy, proxy_data)) return false;
+
+ // Configure the inner global object.
+ Handle<FunctionTemplateInfo> proxy_constructor(
+ FunctionTemplateInfo::cast(proxy_data->constructor()));
+ if (!proxy_constructor->prototype_template()->IsUndefined()) {
+ Handle<ObjectTemplateInfo> inner_data(
+ ObjectTemplateInfo::cast(proxy_constructor->prototype_template()));
+ if (!ConfigureApiObject(inner_global, inner_data)) return false;
+ }
+ }
+
+ SetObjectPrototype(global_proxy, inner_global);
+
+ native_context()->set_initial_array_prototype(
+ JSArray::cast(native_context()->array_function()->prototype()));
+
+ return true;
+}
+
+
+bool Genesis::ConfigureApiObject(Handle<JSObject> object,
+ Handle<ObjectTemplateInfo> object_template) {
+ ASSERT(!object_template.is_null());
+ ASSERT(object->IsInstanceOf(
+ FunctionTemplateInfo::cast(object_template->constructor())));
+
+ bool pending_exception = false;
+ Handle<JSObject> obj =
+ Execution::InstantiateObject(object_template, &pending_exception);
+ if (pending_exception) {
+ ASSERT(isolate()->has_pending_exception());
+ isolate()->clear_pending_exception();
+ return false;
+ }
+ TransferObject(obj, object);
+ return true;
+}
+
+
+void Genesis::TransferNamedProperties(Handle<JSObject> from,
+ Handle<JSObject> to) {
+ if (from->HasFastProperties()) {
+ Handle<DescriptorArray> descs =
+ Handle<DescriptorArray>(from->map()->instance_descriptors());
+ for (int i = 0; i < from->map()->NumberOfOwnDescriptors(); i++) {
+ PropertyDetails details = descs->GetDetails(i);
+ switch (details.type()) {
+ case FIELD: {
+ HandleScope inner(isolate());
+ Handle<Name> key = Handle<Name>(descs->GetKey(i));
+ int index = descs->GetFieldIndex(i);
+ ASSERT(!descs->GetDetails(i).representation().IsDouble());
+ Handle<Object> value = Handle<Object>(from->RawFastPropertyAt(index),
+ isolate());
+ CHECK_NOT_EMPTY_HANDLE(isolate(),
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ to, key, value, details.attributes()));
+ break;
+ }
+ case CONSTANT: {
+ HandleScope inner(isolate());
+ Handle<Name> key = Handle<Name>(descs->GetKey(i));
+ Handle<Object> constant(descs->GetConstant(i), isolate());
+ CHECK_NOT_EMPTY_HANDLE(isolate(),
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ to, key, constant, details.attributes()));
+ break;
+ }
+ case CALLBACKS: {
+ LookupResult result(isolate());
+ to->LocalLookup(descs->GetKey(i), &result);
+ // If the property is already there we skip it
+ if (result.IsFound()) continue;
+ HandleScope inner(isolate());
+ ASSERT(!to->HasFastProperties());
+ // Add to dictionary.
+ Handle<Name> key = Handle<Name>(descs->GetKey(i));
+ Handle<Object> callbacks(descs->GetCallbacksObject(i), isolate());
+ PropertyDetails d = PropertyDetails(
+ details.attributes(), CALLBACKS, i + 1);
+ JSObject::SetNormalizedProperty(to, key, callbacks, d);
+ break;
+ }
+ case NORMAL:
+ // Do not occur since the from object has fast properties.
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ // No element in instance descriptors have proxy or interceptor type.
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else {
+ Handle<NameDictionary> properties =
+ Handle<NameDictionary>(from->property_dictionary());
+ int capacity = properties->Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Object* raw_key(properties->KeyAt(i));
+ if (properties->IsKey(raw_key)) {
+ ASSERT(raw_key->IsName());
+ // If the property is already there we skip it.
+ LookupResult result(isolate());
+ to->LocalLookup(Name::cast(raw_key), &result);
+ if (result.IsFound()) continue;
+ // Set the property.
+ Handle<Name> key = Handle<Name>(Name::cast(raw_key));
+ Handle<Object> value = Handle<Object>(properties->ValueAt(i),
+ isolate());
+ ASSERT(!value->IsCell());
+ if (value->IsPropertyCell()) {
+ value = Handle<Object>(PropertyCell::cast(*value)->value(),
+ isolate());
+ }
+ PropertyDetails details = properties->DetailsAt(i);
+ CHECK_NOT_EMPTY_HANDLE(isolate(),
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ to, key, value, details.attributes()));
+ }
+ }
+ }
+}
+
+
+void Genesis::TransferIndexedProperties(Handle<JSObject> from,
+ Handle<JSObject> to) {
+ // Cloning the elements array is sufficient.
+ Handle<FixedArray> from_elements =
+ Handle<FixedArray>(FixedArray::cast(from->elements()));
+ Handle<FixedArray> to_elements = factory()->CopyFixedArray(from_elements);
+ to->set_elements(*to_elements);
+}
+
+
+void Genesis::TransferObject(Handle<JSObject> from, Handle<JSObject> to) {
+ HandleScope outer(isolate());
+
+ ASSERT(!from->IsJSArray());
+ ASSERT(!to->IsJSArray());
+
+ TransferNamedProperties(from, to);
+ TransferIndexedProperties(from, to);
+
+ // Transfer the prototype (new map is needed).
+ Handle<Map> old_to_map = Handle<Map>(to->map());
+ Handle<Map> new_to_map = factory()->CopyMap(old_to_map);
+ new_to_map->set_prototype(from->map()->prototype());
+ to->set_map(*new_to_map);
+}
+
+
+void Genesis::MakeFunctionInstancePrototypeWritable() {
+ // The maps with writable prototype are created in CreateEmptyFunction
+ // and CreateStrictModeFunctionMaps respectively. Initially the maps are
+ // created with read-only prototype for JS builtins processing.
+ ASSERT(!function_map_writable_prototype_.is_null());
+ ASSERT(!strict_mode_function_map_writable_prototype_.is_null());
+
+ // Replace function instance maps to make prototype writable.
+ native_context()->set_function_map(*function_map_writable_prototype_);
+ native_context()->set_strict_mode_function_map(
+ *strict_mode_function_map_writable_prototype_);
+}
+
+
+Genesis::Genesis(Isolate* isolate,
+ Handle<Object> global_object,
+ v8::Handle<v8::ObjectTemplate> global_template,
+ v8::ExtensionConfiguration* extensions)
+ : isolate_(isolate),
+ active_(isolate->bootstrapper()) {
+ result_ = Handle<Context>::null();
+ // If V8 isn't running and cannot be initialized, just return.
+ if (!V8::IsRunning() && !V8::Initialize(NULL)) return;
+
+ // Before creating the roots we must save the context and restore it
+ // on all function exits.
+ SaveContext saved_context(isolate);
+
+ // During genesis, the boilerplate for stack overflow won't work until the
+ // environment has been at least partially initialized. Add a stack check
+ // before entering JS code to catch overflow early.
+ StackLimitCheck check(isolate);
+ if (check.HasOverflowed()) return;
+
+ // We can only de-serialize a context if the isolate was initialized from
+ // a snapshot. Otherwise we have to build the context from scratch.
+ if (isolate->initialized_from_snapshot()) {
+ native_context_ = Snapshot::NewContextFromSnapshot();
+ } else {
+ native_context_ = Handle<Context>();
+ }
+
+ if (!native_context().is_null()) {
+ AddToWeakNativeContextList(*native_context());
+ isolate->set_context(*native_context());
+ isolate->counters()->contexts_created_by_snapshot()->Increment();
+ Handle<GlobalObject> inner_global;
+ Handle<JSGlobalProxy> global_proxy =
+ CreateNewGlobals(global_template,
+ global_object,
+ &inner_global);
+
+ HookUpGlobalProxy(inner_global, global_proxy);
+ HookUpInnerGlobal(inner_global);
+
+ if (!ConfigureGlobalObjects(global_template)) return;
+ } else {
+ // We get here if there was no context snapshot.
+ CreateRoots();
+ Handle<JSFunction> empty_function = CreateEmptyFunction(isolate);
+ CreateStrictModeFunctionMaps(empty_function);
+ Handle<GlobalObject> inner_global;
+ Handle<JSGlobalProxy> global_proxy =
+ CreateNewGlobals(global_template, global_object, &inner_global);
+ HookUpGlobalProxy(inner_global, global_proxy);
+ InitializeGlobal(inner_global, empty_function);
+ InstallJSFunctionResultCaches();
+ InitializeNormalizedMapCaches();
+ if (!InstallNatives()) return;
+
+ MakeFunctionInstancePrototypeWritable();
+
+ if (!ConfigureGlobalObjects(global_template)) return;
+ isolate->counters()->contexts_created_from_scratch()->Increment();
+ }
+
+ // Initialize experimental globals and install experimental natives.
+ InitializeExperimentalGlobal();
+ if (!InstallExperimentalNatives()) return;
+
+ result_ = native_context();
+}
+
+
+// Support for thread preemption.
+
+// Reserve space for statics needing saving and restoring.
+int Bootstrapper::ArchiveSpacePerThread() {
+ return sizeof(NestingCounterType);
+}
+
+
+// Archive statics that are thread local.
+char* Bootstrapper::ArchiveState(char* to) {
+ *reinterpret_cast<NestingCounterType*>(to) = nesting_;
+ nesting_ = 0;
+ return to + sizeof(NestingCounterType);
+}
+
+
+// Restore statics that are thread local.
+char* Bootstrapper::RestoreState(char* from) {
+ nesting_ = *reinterpret_cast<NestingCounterType*>(from);
+ return from + sizeof(NestingCounterType);
+}
+
+
+// Called when the top-level V8 mutex is destroyed.
+void Bootstrapper::FreeThreadResources() {
+ ASSERT(!IsActive());
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/bootstrapper.h b/chromium/v8/src/bootstrapper.h
new file mode 100644
index 00000000000..30978003905
--- /dev/null
+++ b/chromium/v8/src/bootstrapper.h
@@ -0,0 +1,195 @@
+// 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_BOOTSTRAPPER_H_
+#define V8_BOOTSTRAPPER_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+
+// A SourceCodeCache uses a FixedArray to store pairs of
+// (AsciiString*, JSFunction*), mapping names of native code files
+// (runtime.js, etc.) to precompiled functions. Instead of mapping
+// names to functions it might make sense to let the JS2C tool
+// generate an index for each native JS file.
+class SourceCodeCache BASE_EMBEDDED {
+ public:
+ explicit SourceCodeCache(Script::Type type): type_(type), cache_(NULL) { }
+
+ void Initialize(bool create_heap_objects) {
+ cache_ = create_heap_objects ? HEAP->empty_fixed_array() : NULL;
+ }
+
+ void Iterate(ObjectVisitor* v) {
+ v->VisitPointer(BitCast<Object**, FixedArray**>(&cache_));
+ }
+
+ bool Lookup(Vector<const char> name, Handle<SharedFunctionInfo>* handle) {
+ for (int i = 0; i < cache_->length(); i+=2) {
+ SeqOneByteString* str = SeqOneByteString::cast(cache_->get(i));
+ if (str->IsUtf8EqualTo(name)) {
+ *handle = Handle<SharedFunctionInfo>(
+ SharedFunctionInfo::cast(cache_->get(i + 1)));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void Add(Vector<const char> name, Handle<SharedFunctionInfo> shared) {
+ Isolate* isolate = shared->GetIsolate();
+ Factory* factory = isolate->factory();
+ HandleScope scope(isolate);
+ int length = cache_->length();
+ Handle<FixedArray> new_array = factory->NewFixedArray(length + 2, TENURED);
+ cache_->CopyTo(0, *new_array, 0, cache_->length());
+ cache_ = *new_array;
+ Handle<String> str = factory->NewStringFromAscii(name, TENURED);
+ cache_->set(length, *str);
+ cache_->set(length + 1, *shared);
+ Script::cast(shared->script())->set_type(Smi::FromInt(type_));
+ }
+
+ private:
+ Script::Type type_;
+ FixedArray* cache_;
+ DISALLOW_COPY_AND_ASSIGN(SourceCodeCache);
+};
+
+
+// The Boostrapper is the public interface for creating a JavaScript global
+// context.
+class Bootstrapper {
+ public:
+ static void InitializeOncePerProcess();
+
+ // Requires: Heap::SetUp has been called.
+ void Initialize(bool create_heap_objects);
+ void TearDown();
+
+ // Creates a JavaScript Global Context with initial object graph.
+ // The returned value is a global handle casted to V8Environment*.
+ Handle<Context> CreateEnvironment(
+ Handle<Object> global_object,
+ v8::Handle<v8::ObjectTemplate> global_template,
+ v8::ExtensionConfiguration* extensions);
+
+ // Detach the environment from its outer global object.
+ void DetachGlobal(Handle<Context> env);
+
+ // Reattach an outer global object to an environment.
+ void ReattachGlobal(Handle<Context> env, Handle<JSGlobalProxy> global_proxy);
+
+ // Traverses the pointers for memory management.
+ void Iterate(ObjectVisitor* v);
+
+ // Accessor for the native scripts source code.
+ Handle<String> NativesSourceLookup(int index);
+
+ // Tells whether bootstrapping is active.
+ bool IsActive() const { return nesting_ != 0; }
+
+ // Support for thread preemption.
+ static int ArchiveSpacePerThread();
+ char* ArchiveState(char* to);
+ char* RestoreState(char* from);
+ void FreeThreadResources();
+
+ // This will allocate a char array that is deleted when V8 is shut down.
+ // It should only be used for strictly finite allocations.
+ char* AllocateAutoDeletedArray(int bytes);
+
+ // Used for new context creation.
+ bool InstallExtensions(Handle<Context> native_context,
+ v8::ExtensionConfiguration* extensions);
+
+ SourceCodeCache* extensions_cache() { return &extensions_cache_; }
+
+ private:
+ Isolate* isolate_;
+ typedef int NestingCounterType;
+ NestingCounterType nesting_;
+ SourceCodeCache extensions_cache_;
+ // This is for delete, not delete[].
+ List<char*>* delete_these_non_arrays_on_tear_down_;
+ // This is for delete[]
+ List<char*>* delete_these_arrays_on_tear_down_;
+
+ friend class BootstrapperActive;
+ friend class Isolate;
+ friend class NativesExternalStringResource;
+
+ explicit Bootstrapper(Isolate* isolate);
+
+ DISALLOW_COPY_AND_ASSIGN(Bootstrapper);
+};
+
+
+class BootstrapperActive BASE_EMBEDDED {
+ public:
+ explicit BootstrapperActive(Bootstrapper* bootstrapper)
+ : bootstrapper_(bootstrapper) {
+ ++bootstrapper_->nesting_;
+ }
+
+ ~BootstrapperActive() {
+ --bootstrapper_->nesting_;
+ }
+
+ private:
+ Bootstrapper* bootstrapper_;
+
+ DISALLOW_COPY_AND_ASSIGN(BootstrapperActive);
+};
+
+
+class NativesExternalStringResource
+ : public v8::String::ExternalAsciiStringResource {
+ public:
+ NativesExternalStringResource(Bootstrapper* bootstrapper,
+ const char* source,
+ size_t length);
+
+ const char* data() const {
+ return data_;
+ }
+
+ size_t length() const {
+ return length_;
+ }
+ private:
+ const char* data_;
+ size_t length_;
+};
+
+}} // namespace v8::internal
+
+#endif // V8_BOOTSTRAPPER_H_
diff --git a/chromium/v8/src/builtins.cc b/chromium/v8/src/builtins.cc
new file mode 100644
index 00000000000..4a5cd03b6f9
--- /dev/null
+++ b/chromium/v8/src/builtins.cc
@@ -0,0 +1,1835 @@
+// Copyright 2012 the V8 project authors. 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 "api.h"
+#include "arguments.h"
+#include "bootstrapper.h"
+#include "builtins.h"
+#include "cpu-profiler.h"
+#include "gdb-jit.h"
+#include "ic-inl.h"
+#include "heap-profiler.h"
+#include "mark-compact.h"
+#include "stub-cache.h"
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+namespace {
+
+// Arguments object passed to C++ builtins.
+template <BuiltinExtraArguments extra_args>
+class BuiltinArguments : public Arguments {
+ public:
+ BuiltinArguments(int length, Object** arguments)
+ : Arguments(length, arguments) { }
+
+ Object*& operator[] (int index) {
+ ASSERT(index < length());
+ return Arguments::operator[](index);
+ }
+
+ template <class S> Handle<S> at(int index) {
+ ASSERT(index < length());
+ return Arguments::at<S>(index);
+ }
+
+ Handle<Object> receiver() {
+ return Arguments::at<Object>(0);
+ }
+
+ Handle<JSFunction> called_function() {
+ STATIC_ASSERT(extra_args == NEEDS_CALLED_FUNCTION);
+ return Arguments::at<JSFunction>(Arguments::length() - 1);
+ }
+
+ // Gets the total number of arguments including the receiver (but
+ // excluding extra arguments).
+ int length() const {
+ STATIC_ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
+ return Arguments::length();
+ }
+
+#ifdef DEBUG
+ void Verify() {
+ // Check we have at least the receiver.
+ ASSERT(Arguments::length() >= 1);
+ }
+#endif
+};
+
+
+// Specialize BuiltinArguments for the called function extra argument.
+
+template <>
+int BuiltinArguments<NEEDS_CALLED_FUNCTION>::length() const {
+ return Arguments::length() - 1;
+}
+
+#ifdef DEBUG
+template <>
+void BuiltinArguments<NEEDS_CALLED_FUNCTION>::Verify() {
+ // Check we have at least the receiver and the called function.
+ ASSERT(Arguments::length() >= 2);
+ // Make sure cast to JSFunction succeeds.
+ called_function();
+}
+#endif
+
+
+#define DEF_ARG_TYPE(name, spec) \
+ typedef BuiltinArguments<spec> name##ArgumentsType;
+BUILTIN_LIST_C(DEF_ARG_TYPE)
+#undef DEF_ARG_TYPE
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+// Support macro for defining builtins in C++.
+// ----------------------------------------------------------------------------
+//
+// A builtin function is defined by writing:
+//
+// BUILTIN(name) {
+// ...
+// }
+//
+// In the body of the builtin function the arguments can be accessed
+// through the BuiltinArguments object args.
+
+#ifdef DEBUG
+
+#define BUILTIN(name) \
+ MUST_USE_RESULT static MaybeObject* Builtin_Impl_##name( \
+ name##ArgumentsType args, Isolate* isolate); \
+ MUST_USE_RESULT static MaybeObject* Builtin_##name( \
+ int args_length, Object** args_object, Isolate* isolate) { \
+ name##ArgumentsType args(args_length, args_object); \
+ ASSERT(isolate == Isolate::Current()); \
+ args.Verify(); \
+ return Builtin_Impl_##name(args, isolate); \
+ } \
+ MUST_USE_RESULT static MaybeObject* Builtin_Impl_##name( \
+ name##ArgumentsType args, Isolate* isolate)
+
+#else // For release mode.
+
+#define BUILTIN(name) \
+ static MaybeObject* Builtin_impl##name( \
+ name##ArgumentsType args, Isolate* isolate); \
+ static MaybeObject* Builtin_##name( \
+ int args_length, Object** args_object, Isolate* isolate) { \
+ name##ArgumentsType args(args_length, args_object); \
+ return Builtin_impl##name(args, isolate); \
+ } \
+ static MaybeObject* Builtin_impl##name( \
+ name##ArgumentsType args, Isolate* isolate)
+#endif
+
+
+static inline bool CalledAsConstructor(Isolate* isolate) {
+#ifdef DEBUG
+ // Calculate the result using a full stack frame iterator and check
+ // that the state of the stack is as we assume it to be in the
+ // code below.
+ StackFrameIterator it(isolate);
+ ASSERT(it.frame()->is_exit());
+ it.Advance();
+ StackFrame* frame = it.frame();
+ bool reference_result = frame->is_construct();
+#endif
+ Address fp = Isolate::c_entry_fp(isolate->thread_local_top());
+ // Because we know fp points to an exit frame we can use the relevant
+ // part of ExitFrame::ComputeCallerState directly.
+ const int kCallerOffset = ExitFrameConstants::kCallerFPOffset;
+ Address caller_fp = Memory::Address_at(fp + kCallerOffset);
+ // This inlines the part of StackFrame::ComputeType that grabs the
+ // type of the current frame. Note that StackFrame::ComputeType
+ // has been specialized for each architecture so if any one of them
+ // changes this code has to be changed as well.
+ const int kMarkerOffset = StandardFrameConstants::kMarkerOffset;
+ const Smi* kConstructMarker = Smi::FromInt(StackFrame::CONSTRUCT);
+ Object* marker = Memory::Object_at(caller_fp + kMarkerOffset);
+ bool result = (marker == kConstructMarker);
+ ASSERT_EQ(result, reference_result);
+ return result;
+}
+
+
+// ----------------------------------------------------------------------------
+
+BUILTIN(Illegal) {
+ UNREACHABLE();
+ return isolate->heap()->undefined_value(); // Make compiler happy.
+}
+
+
+BUILTIN(EmptyFunction) {
+ return isolate->heap()->undefined_value();
+}
+
+
+static MaybeObject* ArrayCodeGenericCommon(Arguments* args,
+ Isolate* isolate,
+ JSFunction* constructor) {
+ ASSERT(args->length() >= 1);
+ Heap* heap = isolate->heap();
+ isolate->counters()->array_function_runtime()->Increment();
+
+ JSArray* array;
+ if (CalledAsConstructor(isolate)) {
+ 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.
+ MaybeObject* maybe_array = array->Initialize(0);
+ if (maybe_array->IsFailure()) return maybe_array;
+
+ AllocationMemento* memento = AllocationMemento::FindForJSObject(array);
+ if (memento != NULL && memento->IsValid()) {
+ AllocationSite* site = memento->GetAllocationSite();
+ ElementsKind to_kind = site->GetElementsKind();
+ if (IsMoreGeneralElementsKindTransition(array->GetElementsKind(),
+ to_kind)) {
+ // We have advice that we should change the elements kind
+ if (FLAG_trace_track_allocation_sites) {
+ PrintF("AllocationSite: pre-transitioning array %p(%s->%s)\n",
+ reinterpret_cast<void*>(array),
+ ElementsKindToString(array->GetElementsKind()),
+ ElementsKindToString(to_kind));
+ }
+
+ maybe_array = array->TransitionElementsKind(to_kind);
+ if (maybe_array->IsFailure()) return maybe_array;
+ }
+ }
+
+ if (!FLAG_smi_only_arrays) {
+ Context* native_context = isolate->context()->native_context();
+ if (array->GetElementsKind() == GetInitialFastElementsKind() &&
+ !native_context->js_array_maps()->IsUndefined()) {
+ FixedArray* map_array =
+ FixedArray::cast(native_context->js_array_maps());
+ array->set_map(Map::cast(map_array->
+ get(TERMINAL_FAST_ELEMENTS_KIND)));
+ }
+ }
+ } else {
+ // Allocate the JS Array
+ MaybeObject* maybe_obj = heap->AllocateJSObject(constructor);
+ if (!maybe_obj->To(&array)) return maybe_obj;
+ }
+
+ Arguments adjusted_arguments(args->length() - 1, args->arguments() - 1);
+ ASSERT(adjusted_arguments.length() < 1 ||
+ adjusted_arguments[0] == (*args)[1]);
+ return ArrayConstructInitializeElements(array, &adjusted_arguments);
+}
+
+
+BUILTIN(InternalArrayCodeGeneric) {
+ return ArrayCodeGenericCommon(
+ &args,
+ isolate,
+ isolate->context()->native_context()->internal_array_function());
+}
+
+
+BUILTIN(ArrayCodeGeneric) {
+ return ArrayCodeGenericCommon(
+ &args,
+ isolate,
+ isolate->context()->native_context()->array_function());
+}
+
+
+static void MoveDoubleElements(FixedDoubleArray* dst,
+ int dst_index,
+ FixedDoubleArray* src,
+ int src_index,
+ int len) {
+ if (len == 0) return;
+ OS::MemMove(dst->data_start() + dst_index,
+ src->data_start() + src_index,
+ len * kDoubleSize);
+}
+
+
+static void FillWithHoles(Heap* heap, FixedArray* dst, int from, int to) {
+ ASSERT(dst->map() != heap->fixed_cow_array_map());
+ MemsetPointer(dst->data_start() + from, heap->the_hole_value(), to - from);
+}
+
+
+static void FillWithHoles(FixedDoubleArray* dst, int from, int to) {
+ for (int i = from; i < to; i++) {
+ dst->set_the_hole(i);
+ }
+}
+
+
+static FixedArrayBase* LeftTrimFixedArray(Heap* heap,
+ FixedArrayBase* elms,
+ int to_trim) {
+ Map* map = elms->map();
+ int entry_size;
+ if (elms->IsFixedArray()) {
+ entry_size = kPointerSize;
+ } else {
+ entry_size = kDoubleSize;
+ }
+ ASSERT(elms->map() != HEAP->fixed_cow_array_map());
+ // For now this trick is only applied to fixed arrays in new and paged space.
+ // In large object space the object's start must coincide with chunk
+ // and thus the trick is just not applicable.
+ ASSERT(!HEAP->lo_space()->Contains(elms));
+
+ STATIC_ASSERT(FixedArrayBase::kMapOffset == 0);
+ STATIC_ASSERT(FixedArrayBase::kLengthOffset == kPointerSize);
+ STATIC_ASSERT(FixedArrayBase::kHeaderSize == 2 * kPointerSize);
+
+ Object** former_start = HeapObject::RawField(elms, 0);
+
+ const int len = elms->length();
+
+ if (to_trim * entry_size > FixedArrayBase::kHeaderSize &&
+ elms->IsFixedArray() &&
+ !heap->new_space()->Contains(elms)) {
+ // If we are doing a big trim in old space then we zap the space that was
+ // formerly part of the array so that the GC (aided by the card-based
+ // remembered set) won't find pointers to new-space there.
+ Object** zap = reinterpret_cast<Object**>(elms->address());
+ zap++; // Header of filler must be at least one word so skip that.
+ for (int i = 1; i < to_trim; i++) {
+ *zap++ = Smi::FromInt(0);
+ }
+ }
+ // Technically in new space this write might be omitted (except for
+ // debug mode which iterates through the heap), but to play safer
+ // we still do it.
+ heap->CreateFillerObjectAt(elms->address(), to_trim * entry_size);
+
+ int new_start_index = to_trim * (entry_size / kPointerSize);
+ former_start[new_start_index] = map;
+ former_start[new_start_index + 1] = Smi::FromInt(len - to_trim);
+
+ // Maintain marking consistency for HeapObjectIterator and
+ // IncrementalMarking.
+ int size_delta = to_trim * entry_size;
+ if (heap->marking()->TransferMark(elms->address(),
+ elms->address() + size_delta)) {
+ MemoryChunk::IncrementLiveBytesFromMutator(elms->address(), -size_delta);
+ }
+
+ HEAP_PROFILE(heap, ObjectMoveEvent(elms->address(),
+ elms->address() + size_delta));
+ return FixedArrayBase::cast(HeapObject::FromAddress(
+ elms->address() + to_trim * entry_size));
+}
+
+
+static bool ArrayPrototypeHasNoElements(Heap* heap,
+ Context* native_context,
+ JSObject* array_proto) {
+ // This method depends on non writability of Object and Array prototype
+ // fields.
+ if (array_proto->elements() != heap->empty_fixed_array()) return false;
+ // Object.prototype
+ Object* proto = array_proto->GetPrototype();
+ if (proto == heap->null_value()) return false;
+ array_proto = JSObject::cast(proto);
+ if (array_proto != native_context->initial_object_prototype()) return false;
+ if (array_proto->elements() != heap->empty_fixed_array()) return false;
+ return array_proto->GetPrototype()->IsNull();
+}
+
+
+MUST_USE_RESULT
+static inline MaybeObject* EnsureJSArrayWithWritableFastElements(
+ Heap* heap, Object* receiver, Arguments* args, int first_added_arg) {
+ if (!receiver->IsJSArray()) return NULL;
+ JSArray* array = JSArray::cast(receiver);
+ HeapObject* elms = array->elements();
+ Map* map = elms->map();
+ if (map == heap->fixed_array_map()) {
+ if (args == NULL || array->HasFastObjectElements()) return elms;
+ } else if (map == heap->fixed_cow_array_map()) {
+ MaybeObject* maybe_writable_result = array->EnsureWritableFastElements();
+ if (args == NULL || array->HasFastObjectElements() ||
+ !maybe_writable_result->To(&elms)) {
+ return maybe_writable_result;
+ }
+ } else if (map == heap->fixed_double_array_map()) {
+ if (args == NULL) return elms;
+ } else {
+ 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();
+
+ ElementsKind origin_kind = array->map()->elements_kind();
+ ASSERT(!IsFastObjectElementsKind(origin_kind));
+ ElementsKind target_kind = origin_kind;
+ int arg_count = args->length() - first_added_arg;
+ Object** arguments = args->arguments() - first_added_arg - (arg_count - 1);
+ for (int i = 0; i < arg_count; i++) {
+ Object* arg = arguments[i];
+ if (arg->IsHeapObject()) {
+ if (arg->IsHeapNumber()) {
+ target_kind = FAST_DOUBLE_ELEMENTS;
+ } else {
+ target_kind = FAST_ELEMENTS;
+ break;
+ }
+ }
+ }
+ if (target_kind != origin_kind) {
+ MaybeObject* maybe_failure = array->TransitionElementsKind(target_kind);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ return array->elements();
+ }
+ return elms;
+}
+
+
+static inline bool IsJSArrayFastElementMovingAllowed(Heap* heap,
+ JSArray* receiver) {
+ if (!FLAG_clever_optimizations) return false;
+ Context* native_context = heap->isolate()->context()->native_context();
+ JSObject* array_proto =
+ JSObject::cast(native_context->array_function()->prototype());
+ return receiver->GetPrototype() == array_proto &&
+ ArrayPrototypeHasNoElements(heap, native_context, array_proto);
+}
+
+
+MUST_USE_RESULT static MaybeObject* CallJsBuiltin(
+ Isolate* isolate,
+ const char* name,
+ BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
+ HandleScope handleScope(isolate);
+
+ Handle<Object> js_builtin =
+ GetProperty(Handle<JSObject>(isolate->native_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;
+ Handle<Object> result = Execution::Call(function,
+ args.receiver(),
+ argc,
+ argv.start(),
+ &pending_exception);
+ if (pending_exception) return Failure::Exception();
+ return *result;
+}
+
+
+BUILTIN(ArrayPush) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ FixedArrayBase* elms_obj;
+ MaybeObject* maybe_elms_obj =
+ EnsureJSArrayWithWritableFastElements(heap, receiver, &args, 1);
+ if (maybe_elms_obj == NULL) {
+ return CallJsBuiltin(isolate, "ArrayPush", args);
+ }
+ if (!maybe_elms_obj->To(&elms_obj)) return maybe_elms_obj;
+
+ JSArray* array = JSArray::cast(receiver);
+ ASSERT(!array->map()->is_observed());
+
+ ElementsKind kind = array->GetElementsKind();
+
+ if (IsFastSmiOrObjectElementsKind(kind)) {
+ FixedArray* elms = FixedArray::cast(elms_obj);
+
+ int len = Smi::cast(array->length())->value();
+ int to_add = args.length() - 1;
+ if (to_add == 0) {
+ return Smi::FromInt(len);
+ }
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT(to_add <= (Smi::kMaxValue - len));
+
+ int new_length = len + to_add;
+
+ if (new_length > elms->length()) {
+ // New backing storage is needed.
+ int capacity = new_length + (new_length >> 1) + 16;
+ FixedArray* new_elms;
+ MaybeObject* maybe_obj = heap->AllocateUninitializedFixedArray(capacity);
+ if (!maybe_obj->To(&new_elms)) return maybe_obj;
+
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ MaybeObject* maybe_failure = accessor->CopyElements(
+ NULL, 0, kind, new_elms, 0,
+ ElementsAccessor::kCopyToEndAndInitializeToHole, elms_obj);
+ ASSERT(!maybe_failure->IsFailure());
+ USE(maybe_failure);
+
+ elms = new_elms;
+ }
+
+ // Add the provided values.
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ for (int index = 0; index < to_add; index++) {
+ 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);
+ } else {
+ int len = Smi::cast(array->length())->value();
+ int elms_len = elms_obj->length();
+
+ int to_add = args.length() - 1;
+ if (to_add == 0) {
+ return Smi::FromInt(len);
+ }
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT(to_add <= (Smi::kMaxValue - len));
+
+ int new_length = len + to_add;
+
+ FixedDoubleArray* new_elms;
+
+ if (new_length > elms_len) {
+ // New backing storage is needed.
+ int capacity = new_length + (new_length >> 1) + 16;
+ MaybeObject* maybe_obj =
+ heap->AllocateUninitializedFixedDoubleArray(capacity);
+ if (!maybe_obj->To(&new_elms)) return maybe_obj;
+
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ MaybeObject* maybe_failure = accessor->CopyElements(
+ NULL, 0, kind, new_elms, 0,
+ ElementsAccessor::kCopyToEndAndInitializeToHole, elms_obj);
+ ASSERT(!maybe_failure->IsFailure());
+ USE(maybe_failure);
+ } else {
+ // to_add is > 0 and new_length <= elms_len, so elms_obj cannot be the
+ // empty_fixed_array.
+ new_elms = FixedDoubleArray::cast(elms_obj);
+ }
+
+ // Add the provided values.
+ DisallowHeapAllocation no_gc;
+ int index;
+ for (index = 0; index < to_add; index++) {
+ Object* arg = args[index + 1];
+ new_elms->set(index + len, arg->Number());
+ }
+
+ if (new_elms != array->elements()) {
+ array->set_elements(new_elms);
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(new_length));
+ return Smi::FromInt(new_length);
+ }
+}
+
+
+BUILTIN(ArrayPop) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ FixedArrayBase* elms_obj;
+ MaybeObject* maybe_elms =
+ EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
+ if (maybe_elms == NULL) return CallJsBuiltin(isolate, "ArrayPop", args);
+ if (!maybe_elms->To(&elms_obj)) return maybe_elms;
+
+ JSArray* array = JSArray::cast(receiver);
+ ASSERT(!array->map()->is_observed());
+
+ int len = Smi::cast(array->length())->value();
+ if (len == 0) return heap->undefined_value();
+
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ int new_length = len - 1;
+ MaybeObject* maybe_result;
+ if (accessor->HasElement(array, array, new_length, elms_obj)) {
+ maybe_result = accessor->Get(array, array, new_length, elms_obj);
+ } else {
+ maybe_result = array->GetPrototype()->GetElement(len - 1);
+ }
+ if (maybe_result->IsFailure()) return maybe_result;
+ MaybeObject* maybe_failure =
+ accessor->SetLength(array, Smi::FromInt(new_length));
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ return maybe_result;
+}
+
+
+BUILTIN(ArrayShift) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ FixedArrayBase* elms_obj;
+ MaybeObject* maybe_elms_obj =
+ EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
+ if (maybe_elms_obj == NULL)
+ return CallJsBuiltin(isolate, "ArrayShift", args);
+ if (!maybe_elms_obj->To(&elms_obj)) return maybe_elms_obj;
+
+ if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
+ return CallJsBuiltin(isolate, "ArrayShift", args);
+ }
+ JSArray* array = JSArray::cast(receiver);
+ ASSERT(!array->map()->is_observed());
+
+ int len = Smi::cast(array->length())->value();
+ if (len == 0) return heap->undefined_value();
+
+ // Get first element
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ Object* first;
+ MaybeObject* maybe_first = accessor->Get(receiver, array, 0, elms_obj);
+ if (!maybe_first->To(&first)) return maybe_first;
+ if (first->IsTheHole()) {
+ first = heap->undefined_value();
+ }
+
+ if (!heap->lo_space()->Contains(elms_obj)) {
+ array->set_elements(LeftTrimFixedArray(heap, elms_obj, 1));
+ } else {
+ // Shift the elements.
+ if (elms_obj->IsFixedArray()) {
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ DisallowHeapAllocation no_gc;
+ heap->MoveElements(elms, 0, 1, len - 1);
+ elms->set(len - 1, heap->the_hole_value());
+ } else {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj);
+ MoveDoubleElements(elms, 0, elms, 1, len - 1);
+ elms->set_the_hole(len - 1);
+ }
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(len - 1));
+
+ return first;
+}
+
+
+BUILTIN(ArrayUnshift) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ FixedArrayBase* elms_obj;
+ MaybeObject* maybe_elms_obj =
+ EnsureJSArrayWithWritableFastElements(heap, receiver, NULL, 0);
+ if (maybe_elms_obj == NULL)
+ return CallJsBuiltin(isolate, "ArrayUnshift", args);
+ if (!maybe_elms_obj->To(&elms_obj)) return maybe_elms_obj;
+
+ if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
+ return CallJsBuiltin(isolate, "ArrayUnshift", args);
+ }
+ JSArray* array = JSArray::cast(receiver);
+ ASSERT(!array->map()->is_observed());
+ if (!array->HasFastSmiOrObjectElements()) {
+ return CallJsBuiltin(isolate, "ArrayUnshift", args);
+ }
+ FixedArray* elms = FixedArray::cast(elms_obj);
+
+ int len = Smi::cast(array->length())->value();
+ int to_add = args.length() - 1;
+ int new_length = len + to_add;
+ // Currently fixed arrays cannot grow too big, so
+ // 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;
+ FixedArray* new_elms;
+ MaybeObject* maybe_elms = heap->AllocateUninitializedFixedArray(capacity);
+ if (!maybe_elms->To(&new_elms)) return maybe_elms;
+
+ ElementsKind kind = array->GetElementsKind();
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ MaybeObject* maybe_failure = accessor->CopyElements(
+ NULL, 0, kind, new_elms, to_add,
+ ElementsAccessor::kCopyToEndAndInitializeToHole, elms);
+ ASSERT(!maybe_failure->IsFailure());
+ USE(maybe_failure);
+
+ elms = new_elms;
+ array->set_elements(elms);
+ } else {
+ DisallowHeapAllocation no_gc;
+ heap->MoveElements(elms, to_add, 0, len);
+ }
+
+ // Add the provided values.
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ for (int i = 0; i < to_add; i++) {
+ elms->set(i, args[i + 1], mode);
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(new_length));
+ return Smi::FromInt(new_length);
+}
+
+
+BUILTIN(ArraySlice) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ FixedArrayBase* elms;
+ int len = -1;
+ if (receiver->IsJSArray()) {
+ JSArray* array = JSArray::cast(receiver);
+ if (!IsJSArrayFastElementMovingAllowed(heap, array)) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+
+ if (array->HasFastElements()) {
+ elms = array->elements();
+ } else {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+
+ len = Smi::cast(array->length())->value();
+ } else {
+ // Array.slice(arguments, ...) is quite a common idiom (notably more
+ // than 50% of invocations in Web apps). Treat it in C++ as well.
+ Map* arguments_map =
+ isolate->context()->native_context()->arguments_boilerplate()->map();
+
+ bool is_arguments_object_with_fast_elements =
+ receiver->IsJSObject() &&
+ JSObject::cast(receiver)->map() == arguments_map;
+ if (!is_arguments_object_with_fast_elements) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ JSObject* object = JSObject::cast(receiver);
+
+ if (object->HasFastElements()) {
+ elms = object->elements();
+ } else {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ Object* len_obj = object->InObjectPropertyAt(Heap::kArgumentsLengthIndex);
+ if (!len_obj->IsSmi()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ len = Smi::cast(len_obj)->value();
+ if (len > elms->length()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ }
+
+ JSObject* object = JSObject::cast(receiver);
+
+ ASSERT(len >= 0);
+ int n_arguments = args.length() - 1;
+
+ // Note carefully choosen defaults---if argument is missing,
+ // it's undefined which gets converted to 0 for relative_start
+ // and to len for relative_end.
+ int relative_start = 0;
+ int relative_end = len;
+ if (n_arguments > 0) {
+ Object* arg1 = args[1];
+ if (arg1->IsSmi()) {
+ relative_start = Smi::cast(arg1)->value();
+ } else if (arg1->IsHeapNumber()) {
+ double start = HeapNumber::cast(arg1)->value();
+ if (start < kMinInt || start > kMaxInt) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ relative_start = std::isnan(start) ? 0 : static_cast<int>(start);
+ } else if (!arg1->IsUndefined()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ if (n_arguments > 1) {
+ Object* arg2 = args[2];
+ if (arg2->IsSmi()) {
+ relative_end = Smi::cast(arg2)->value();
+ } else if (arg2->IsHeapNumber()) {
+ double end = HeapNumber::cast(arg2)->value();
+ if (end < kMinInt || end > kMaxInt) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ relative_end = std::isnan(end) ? 0 : static_cast<int>(end);
+ } else if (!arg2->IsUndefined()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ }
+ }
+
+ // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
+ int k = (relative_start < 0) ? Max(len + relative_start, 0)
+ : Min(relative_start, len);
+
+ // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
+ int final = (relative_end < 0) ? Max(len + relative_end, 0)
+ : Min(relative_end, len);
+
+ // Calculate the length of result array.
+ int result_len = Max(final - k, 0);
+
+ ElementsKind kind = object->GetElementsKind();
+ if (IsHoleyElementsKind(kind)) {
+ bool packed = true;
+ ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
+ for (int i = k; i < final; i++) {
+ if (!accessor->HasElement(object, object, i, elms)) {
+ packed = false;
+ break;
+ }
+ }
+ if (packed) {
+ kind = GetPackedElementsKind(kind);
+ } else if (!receiver->IsJSArray()) {
+ return CallJsBuiltin(isolate, "ArraySlice", args);
+ }
+ }
+
+ JSArray* result_array;
+ MaybeObject* maybe_array = heap->AllocateJSArrayAndStorage(kind,
+ result_len,
+ result_len);
+
+ DisallowHeapAllocation no_gc;
+ if (result_len == 0) return maybe_array;
+ if (!maybe_array->To(&result_array)) return maybe_array;
+
+ ElementsAccessor* accessor = object->GetElementsAccessor();
+ MaybeObject* maybe_failure = accessor->CopyElements(
+ NULL, k, kind, result_array->elements(), 0, result_len, elms);
+ ASSERT(!maybe_failure->IsFailure());
+ USE(maybe_failure);
+
+ return result_array;
+}
+
+
+BUILTIN(ArraySplice) {
+ Heap* heap = isolate->heap();
+ Object* receiver = *args.receiver();
+ FixedArrayBase* elms_obj;
+ MaybeObject* maybe_elms =
+ EnsureJSArrayWithWritableFastElements(heap, receiver, &args, 3);
+ if (maybe_elms == NULL) {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+ if (!maybe_elms->To(&elms_obj)) return maybe_elms;
+
+ if (!IsJSArrayFastElementMovingAllowed(heap, JSArray::cast(receiver))) {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+ JSArray* array = JSArray::cast(receiver);
+ ASSERT(!array->map()->is_observed());
+
+ int len = Smi::cast(array->length())->value();
+
+ int n_arguments = args.length() - 1;
+
+ int relative_start = 0;
+ if (n_arguments > 0) {
+ Object* arg1 = args[1];
+ if (arg1->IsSmi()) {
+ relative_start = Smi::cast(arg1)->value();
+ } else if (arg1->IsHeapNumber()) {
+ double start = HeapNumber::cast(arg1)->value();
+ if (start < kMinInt || start > kMaxInt) {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+ relative_start = std::isnan(start) ? 0 : static_cast<int>(start);
+ } else if (!arg1->IsUndefined()) {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+ }
+ int actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
+ : Min(relative_start, len);
+
+ // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+ // given as a request to delete all the elements from the start.
+ // And it differs from the case of undefined delete count.
+ // This does not follow ECMA-262, but we do the same for
+ // compatibility.
+ int actual_delete_count;
+ if (n_arguments == 1) {
+ ASSERT(len - actual_start >= 0);
+ actual_delete_count = len - actual_start;
+ } else {
+ int value = 0; // ToInteger(undefined) == 0
+ if (n_arguments > 1) {
+ Object* arg2 = args[2];
+ if (arg2->IsSmi()) {
+ value = Smi::cast(arg2)->value();
+ } else {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+ }
+ actual_delete_count = Min(Max(value, 0), len - actual_start);
+ }
+
+ ElementsKind elements_kind = array->GetElementsKind();
+
+ int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
+ int new_length = len - actual_delete_count + item_count;
+
+ // For double mode we do not support changing the length.
+ if (new_length > len && IsFastDoubleElementsKind(elements_kind)) {
+ return CallJsBuiltin(isolate, "ArraySplice", args);
+ }
+
+ if (new_length == 0) {
+ MaybeObject* maybe_array = heap->AllocateJSArrayWithElements(
+ elms_obj, elements_kind, actual_delete_count);
+ if (maybe_array->IsFailure()) return maybe_array;
+ array->set_elements(heap->empty_fixed_array());
+ array->set_length(Smi::FromInt(0));
+ return maybe_array;
+ }
+
+ JSArray* result_array = NULL;
+ MaybeObject* maybe_array =
+ heap->AllocateJSArrayAndStorage(elements_kind,
+ actual_delete_count,
+ actual_delete_count);
+ if (!maybe_array->To(&result_array)) return maybe_array;
+
+ if (actual_delete_count > 0) {
+ DisallowHeapAllocation no_gc;
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ MaybeObject* maybe_failure = accessor->CopyElements(
+ NULL, actual_start, elements_kind, result_array->elements(),
+ 0, actual_delete_count, elms_obj);
+ // Cannot fail since the origin and target array are of the same elements
+ // kind.
+ ASSERT(!maybe_failure->IsFailure());
+ USE(maybe_failure);
+ }
+
+ bool elms_changed = false;
+ if (item_count < actual_delete_count) {
+ // Shrink the array.
+ const bool trim_array = !heap->lo_space()->Contains(elms_obj) &&
+ ((actual_start + item_count) <
+ (len - actual_delete_count - actual_start));
+ if (trim_array) {
+ const int delta = actual_delete_count - item_count;
+
+ if (elms_obj->IsFixedDoubleArray()) {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj);
+ MoveDoubleElements(elms, delta, elms, 0, actual_start);
+ } else {
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ DisallowHeapAllocation no_gc;
+ heap->MoveElements(elms, delta, 0, actual_start);
+ }
+
+ elms_obj = LeftTrimFixedArray(heap, elms_obj, delta);
+
+ elms_changed = true;
+ } else {
+ if (elms_obj->IsFixedDoubleArray()) {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj);
+ MoveDoubleElements(elms, actual_start + item_count,
+ elms, actual_start + actual_delete_count,
+ (len - actual_delete_count - actual_start));
+ FillWithHoles(elms, new_length, len);
+ } else {
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ DisallowHeapAllocation no_gc;
+ heap->MoveElements(elms, actual_start + item_count,
+ actual_start + actual_delete_count,
+ (len - actual_delete_count - actual_start));
+ FillWithHoles(heap, elms, new_length, len);
+ }
+ }
+ } else if (item_count > actual_delete_count) {
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT((item_count - actual_delete_count) <= (Smi::kMaxValue - len));
+
+ // Check if array need to grow.
+ if (new_length > elms->length()) {
+ // New backing storage is needed.
+ int capacity = new_length + (new_length >> 1) + 16;
+ FixedArray* new_elms;
+ MaybeObject* maybe_obj = heap->AllocateUninitializedFixedArray(capacity);
+ if (!maybe_obj->To(&new_elms)) return maybe_obj;
+
+ DisallowHeapAllocation no_gc;
+
+ ElementsKind kind = array->GetElementsKind();
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ if (actual_start > 0) {
+ // Copy the part before actual_start as is.
+ MaybeObject* maybe_failure = accessor->CopyElements(
+ NULL, 0, kind, new_elms, 0, actual_start, elms);
+ ASSERT(!maybe_failure->IsFailure());
+ USE(maybe_failure);
+ }
+ MaybeObject* maybe_failure = accessor->CopyElements(
+ NULL, actual_start + actual_delete_count, kind, new_elms,
+ actual_start + item_count,
+ ElementsAccessor::kCopyToEndAndInitializeToHole, elms);
+ ASSERT(!maybe_failure->IsFailure());
+ USE(maybe_failure);
+
+ elms_obj = new_elms;
+ elms_changed = true;
+ } else {
+ DisallowHeapAllocation no_gc;
+ heap->MoveElements(elms, actual_start + item_count,
+ actual_start + actual_delete_count,
+ (len - actual_delete_count - actual_start));
+ }
+ }
+
+ if (IsFastDoubleElementsKind(elements_kind)) {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elms_obj);
+ for (int k = actual_start; k < actual_start + item_count; k++) {
+ Object* arg = args[3 + k - actual_start];
+ if (arg->IsSmi()) {
+ elms->set(k, Smi::cast(arg)->value());
+ } else {
+ elms->set(k, HeapNumber::cast(arg)->value());
+ }
+ }
+ } else {
+ FixedArray* elms = FixedArray::cast(elms_obj);
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ for (int k = actual_start; k < actual_start + item_count; k++) {
+ elms->set(k, args[3 + k - actual_start], mode);
+ }
+ }
+
+ if (elms_changed) {
+ array->set_elements(elms_obj);
+ }
+ // Set the length.
+ array->set_length(Smi::FromInt(new_length));
+
+ return result_array;
+}
+
+
+BUILTIN(ArrayConcat) {
+ Heap* heap = isolate->heap();
+ Context* native_context = isolate->context()->native_context();
+ JSObject* array_proto =
+ JSObject::cast(native_context->array_function()->prototype());
+ if (!ArrayPrototypeHasNoElements(heap, native_context, array_proto)) {
+ return CallJsBuiltin(isolate, "ArrayConcat", args);
+ }
+
+ // Iterate through all the arguments performing checks
+ // and calculating total length.
+ int n_arguments = args.length();
+ int result_len = 0;
+ ElementsKind elements_kind = GetInitialFastElementsKind();
+ bool has_double = false;
+ bool is_holey = false;
+ for (int i = 0; i < n_arguments; i++) {
+ Object* arg = args[i];
+ if (!arg->IsJSArray() ||
+ !JSArray::cast(arg)->HasFastElements() ||
+ JSArray::cast(arg)->GetPrototype() != array_proto) {
+ return CallJsBuiltin(isolate, "ArrayConcat", args);
+ }
+ int len = Smi::cast(JSArray::cast(arg)->length())->value();
+
+ // We shouldn't overflow when adding another len.
+ const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2);
+ STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt);
+ USE(kHalfOfMaxInt);
+ result_len += len;
+ ASSERT(result_len >= 0);
+
+ if (result_len > FixedDoubleArray::kMaxLength) {
+ return CallJsBuiltin(isolate, "ArrayConcat", args);
+ }
+
+ ElementsKind arg_kind = JSArray::cast(arg)->map()->elements_kind();
+ has_double = has_double || IsFastDoubleElementsKind(arg_kind);
+ is_holey = is_holey || IsFastHoleyElementsKind(arg_kind);
+ if (IsMoreGeneralElementsKindTransition(elements_kind, arg_kind)) {
+ elements_kind = arg_kind;
+ }
+ }
+
+ if (is_holey) elements_kind = GetHoleyElementsKind(elements_kind);
+
+ // If a double array is concatted into a fast elements array, the fast
+ // elements array needs to be initialized to contain proper holes, since
+ // boxing doubles may cause incremental marking.
+ ArrayStorageAllocationMode mode =
+ has_double && IsFastObjectElementsKind(elements_kind)
+ ? INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE : DONT_INITIALIZE_ARRAY_ELEMENTS;
+ JSArray* result_array;
+ // Allocate result.
+ MaybeObject* maybe_array =
+ heap->AllocateJSArrayAndStorage(elements_kind,
+ result_len,
+ result_len,
+ mode);
+ if (!maybe_array->To(&result_array)) return maybe_array;
+ if (result_len == 0) return result_array;
+
+ int j = 0;
+ FixedArrayBase* storage = result_array->elements();
+ ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind);
+ for (int i = 0; i < n_arguments; i++) {
+ JSArray* array = JSArray::cast(args[i]);
+ int len = Smi::cast(array->length())->value();
+ ElementsKind from_kind = array->GetElementsKind();
+ if (len > 0) {
+ MaybeObject* maybe_failure =
+ accessor->CopyElements(array, 0, from_kind, storage, j, len);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ j += len;
+ }
+ }
+
+ ASSERT(j == result_len);
+
+ return result_array;
+}
+
+
+// -----------------------------------------------------------------------------
+// Strict mode poison pills
+
+
+BUILTIN(StrictModePoisonPill) {
+ HandleScope scope(isolate);
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_poison_pill", HandleVector<Object>(NULL, 0)));
+}
+
+
+// -----------------------------------------------------------------------------
+//
+
+
+// Searches the hidden prototype chain of the given object for the first
+// object that is an instance of the given type. If no such object can
+// be found then Heap::null_value() is returned.
+static inline Object* FindHidden(Heap* heap,
+ Object* object,
+ FunctionTemplateInfo* type) {
+ if (object->IsInstanceOf(type)) return object;
+ Object* proto = object->GetPrototype(heap->isolate());
+ if (proto->IsJSObject() &&
+ JSObject::cast(proto)->map()->is_hidden_prototype()) {
+ return FindHidden(heap, proto, type);
+ }
+ return heap->null_value();
+}
+
+
+// Returns the holder JSObject if the function can legally be called
+// with this receiver. Returns Heap::null_value() if the call is
+// illegal. Any arguments that don't fit the expected type is
+// overwritten with undefined. Note that holder and the arguments are
+// implicitly rewritten with the first object in the hidden prototype
+// chain that actually has the expected type.
+static inline Object* TypeCheck(Heap* heap,
+ int argc,
+ Object** argv,
+ FunctionTemplateInfo* info) {
+ Object* recv = argv[0];
+ // API calls are only supported with JSObject receivers.
+ if (!recv->IsJSObject()) return heap->null_value();
+ Object* sig_obj = info->signature();
+ if (sig_obj->IsUndefined()) return recv;
+ SignatureInfo* sig = SignatureInfo::cast(sig_obj);
+ // If necessary, check the receiver
+ Object* recv_type = sig->receiver();
+ Object* holder = recv;
+ if (!recv_type->IsUndefined()) {
+ holder = FindHidden(heap, holder, FunctionTemplateInfo::cast(recv_type));
+ if (holder == heap->null_value()) return heap->null_value();
+ }
+ Object* args_obj = sig->args();
+ // If there is no argument signature we're done
+ if (args_obj->IsUndefined()) return holder;
+ FixedArray* args = FixedArray::cast(args_obj);
+ int length = args->length();
+ if (argc <= length) length = argc - 1;
+ for (int i = 0; i < length; i++) {
+ Object* argtype = args->get(i);
+ if (argtype->IsUndefined()) continue;
+ Object** arg = &argv[-1 - i];
+ Object* current = *arg;
+ current = FindHidden(heap, current, FunctionTemplateInfo::cast(argtype));
+ if (current == heap->null_value()) current = heap->undefined_value();
+ *arg = current;
+ }
+ return holder;
+}
+
+
+template <bool is_construct>
+MUST_USE_RESULT static MaybeObject* HandleApiCallHelper(
+ BuiltinArguments<NEEDS_CALLED_FUNCTION> args, Isolate* isolate) {
+ ASSERT(is_construct == CalledAsConstructor(isolate));
+ Heap* heap = isolate->heap();
+
+ HandleScope scope(isolate);
+ Handle<JSFunction> function = args.called_function();
+ ASSERT(function->shared()->IsApiFunction());
+
+ FunctionTemplateInfo* fun_data = function->shared()->get_api_func_data();
+ if (is_construct) {
+ Handle<FunctionTemplateInfo> desc(fun_data, isolate);
+ bool pending_exception = false;
+ isolate->factory()->ConfigureInstance(
+ desc, Handle<JSObject>::cast(args.receiver()), &pending_exception);
+ ASSERT(isolate->has_pending_exception() == pending_exception);
+ if (pending_exception) return Failure::Exception();
+ fun_data = *desc;
+ }
+
+ Object* raw_holder = TypeCheck(heap, args.length(), &args[0], fun_data);
+
+ if (raw_holder->IsNull()) {
+ // This function cannot be called with the given receiver. Abort!
+ Handle<Object> obj =
+ isolate->factory()->NewTypeError(
+ "illegal_invocation", HandleVector(&function, 1));
+ return isolate->Throw(*obj);
+ }
+
+ Object* raw_call_data = fun_data->call_code();
+ if (!raw_call_data->IsUndefined()) {
+ CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
+ Object* callback_obj = call_data->callback();
+ v8::InvocationCallback callback =
+ v8::ToCData<v8::InvocationCallback>(callback_obj);
+ Object* data_obj = call_data->data();
+ Object* result;
+
+ LOG(isolate, ApiObjectAccess("call", JSObject::cast(*args.receiver())));
+ ASSERT(raw_holder->IsJSObject());
+
+ FunctionCallbackArguments custom(isolate,
+ data_obj,
+ *function,
+ raw_holder,
+ &args[0] - 1,
+ args.length() - 1,
+ is_construct);
+
+ v8::Handle<v8::Value> value = custom.Call(callback);
+ if (value.IsEmpty()) {
+ result = heap->undefined_value();
+ } else {
+ result = *reinterpret_cast<Object**>(*value);
+ result->VerifyApiCallResultType();
+ }
+
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!is_construct || result->IsJSObject()) return result;
+ }
+
+ return *args.receiver();
+}
+
+
+BUILTIN(HandleApiCall) {
+ return HandleApiCallHelper<false>(args, isolate);
+}
+
+
+BUILTIN(HandleApiCallConstruct) {
+ return HandleApiCallHelper<true>(args, isolate);
+}
+
+
+// Helper function to handle calls to non-function objects created through the
+// API. The object can be called as either a constructor (using new) or just as
+// a function (without new).
+MUST_USE_RESULT static MaybeObject* HandleApiCallAsFunctionOrConstructor(
+ Isolate* isolate,
+ bool is_construct_call,
+ BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
+ // Non-functions are never called as constructors. Even if this is an object
+ // called as a constructor the delegate call is not a construct call.
+ ASSERT(!CalledAsConstructor(isolate));
+ Heap* heap = isolate->heap();
+
+ Handle<Object> receiver = args.receiver();
+
+ // Get the object called.
+ JSObject* obj = JSObject::cast(*receiver);
+
+ // Get the invocation callback from the function descriptor that was
+ // used to create the called object.
+ ASSERT(obj->map()->has_instance_call_handler());
+ JSFunction* constructor = JSFunction::cast(obj->map()->constructor());
+ ASSERT(constructor->shared()->IsApiFunction());
+ Object* handler =
+ constructor->shared()->get_api_func_data()->instance_call_handler();
+ ASSERT(!handler->IsUndefined());
+ CallHandlerInfo* call_data = CallHandlerInfo::cast(handler);
+ Object* callback_obj = call_data->callback();
+ v8::InvocationCallback callback =
+ v8::ToCData<v8::InvocationCallback>(callback_obj);
+
+ // Get the data for the call and perform the callback.
+ Object* result;
+ {
+ HandleScope scope(isolate);
+ LOG(isolate, ApiObjectAccess("call non-function", obj));
+
+ FunctionCallbackArguments custom(isolate,
+ call_data->data(),
+ constructor,
+ obj,
+ &args[0] - 1,
+ args.length() - 1,
+ is_construct_call);
+ v8::Handle<v8::Value> value = custom.Call(callback);
+ if (value.IsEmpty()) {
+ result = heap->undefined_value();
+ } else {
+ result = *reinterpret_cast<Object**>(*value);
+ result->VerifyApiCallResultType();
+ }
+ }
+ // Check for exceptions and return result.
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return result;
+}
+
+
+// Handle calls to non-function objects created through the API. This delegate
+// function is used when the call is a normal function call.
+BUILTIN(HandleApiCallAsFunction) {
+ return HandleApiCallAsFunctionOrConstructor(isolate, false, args);
+}
+
+
+// Handle calls to non-function objects created through the API. This delegate
+// function is used when the call is a construct call.
+BUILTIN(HandleApiCallAsConstructor) {
+ return HandleApiCallAsFunctionOrConstructor(isolate, true, args);
+}
+
+
+static void Generate_LoadIC_Initialize(MacroAssembler* masm) {
+ LoadIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_LoadIC_PreMonomorphic(MacroAssembler* masm) {
+ LoadIC::GeneratePreMonomorphic(masm);
+}
+
+
+static void Generate_LoadIC_Miss(MacroAssembler* masm) {
+ LoadIC::GenerateMiss(masm);
+}
+
+
+static void Generate_LoadIC_Megamorphic(MacroAssembler* masm) {
+ LoadIC::GenerateMegamorphic(masm);
+}
+
+
+static void Generate_LoadIC_Normal(MacroAssembler* masm) {
+ LoadIC::GenerateNormal(masm);
+}
+
+
+static void Generate_LoadIC_Getter_ForDeopt(MacroAssembler* masm) {
+ LoadStubCompiler::GenerateLoadViaGetter(masm, Handle<JSFunction>());
+}
+
+
+static void Generate_LoadIC_Slow(MacroAssembler* masm) {
+ LoadIC::GenerateRuntimeGetProperty(masm);
+}
+
+
+static void Generate_KeyedLoadIC_Initialize(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_KeyedLoadIC_Slow(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateRuntimeGetProperty(masm);
+}
+
+
+static void Generate_KeyedLoadIC_Miss(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateMiss(masm, MISS);
+}
+
+
+static void Generate_KeyedLoadIC_MissForceGeneric(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateMiss(masm, MISS_FORCE_GENERIC);
+}
+
+
+static void Generate_KeyedLoadIC_Generic(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateGeneric(masm);
+}
+
+
+static void Generate_KeyedLoadIC_String(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateString(masm);
+}
+
+
+static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) {
+ KeyedLoadIC::GeneratePreMonomorphic(masm);
+}
+
+
+static void Generate_KeyedLoadIC_IndexedInterceptor(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateIndexedInterceptor(masm);
+}
+
+
+static void Generate_KeyedLoadIC_NonStrictArguments(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateNonStrictArguments(masm);
+}
+
+
+static void Generate_StoreIC_Slow(MacroAssembler* masm) {
+ StoreIC::GenerateSlow(masm);
+}
+
+
+static void Generate_StoreIC_Initialize(MacroAssembler* masm) {
+ StoreIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_StoreIC_Initialize_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_StoreIC_Miss(MacroAssembler* masm) {
+ StoreIC::GenerateMiss(masm);
+}
+
+
+static void Generate_StoreIC_Normal(MacroAssembler* masm) {
+ StoreIC::GenerateNormal(masm);
+}
+
+
+static void Generate_StoreIC_Normal_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateNormal(masm);
+}
+
+
+static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) {
+ StoreIC::GenerateMegamorphic(masm, kNonStrictMode);
+}
+
+
+static void Generate_StoreIC_Megamorphic_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateMegamorphic(masm, kStrictMode);
+}
+
+
+static void Generate_StoreIC_GlobalProxy(MacroAssembler* masm) {
+ StoreIC::GenerateRuntimeSetProperty(masm, kNonStrictMode);
+}
+
+
+static void Generate_StoreIC_GlobalProxy_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateRuntimeSetProperty(masm, kStrictMode);
+}
+
+
+static void Generate_StoreIC_Setter_ForDeopt(MacroAssembler* masm) {
+ StoreStubCompiler::GenerateStoreViaSetter(masm, Handle<JSFunction>());
+}
+
+
+static void Generate_StoreIC_Generic(MacroAssembler* masm) {
+ StoreIC::GenerateRuntimeSetProperty(masm, kNonStrictMode);
+}
+
+
+static void Generate_StoreIC_Generic_Strict(MacroAssembler* masm) {
+ StoreIC::GenerateRuntimeSetProperty(masm, kStrictMode);
+}
+
+
+static void Generate_KeyedStoreIC_Generic(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateGeneric(masm, kNonStrictMode);
+}
+
+
+static void Generate_KeyedStoreIC_Generic_Strict(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateGeneric(masm, kStrictMode);
+}
+
+
+static void Generate_KeyedStoreIC_Miss(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateMiss(masm, MISS);
+}
+
+
+static void Generate_KeyedStoreIC_MissForceGeneric(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateMiss(masm, MISS_FORCE_GENERIC);
+}
+
+
+static void Generate_KeyedStoreIC_Slow(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateSlow(masm);
+}
+
+
+static void Generate_KeyedStoreIC_Initialize(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_KeyedStoreIC_Initialize_Strict(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateInitialize(masm);
+}
+
+
+static void Generate_KeyedStoreIC_NonStrictArguments(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateNonStrictArguments(masm);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateLoadICDebugBreak(masm);
+}
+
+
+static void Generate_StoreIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateStoreICDebugBreak(masm);
+}
+
+
+static void Generate_KeyedLoadIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateKeyedLoadICDebugBreak(masm);
+}
+
+
+static void Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateKeyedStoreICDebugBreak(masm);
+}
+
+
+static void Generate_CompareNilIC_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateCompareNilICDebugBreak(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_CallConstructStub_Recording_DebugBreak(
+ MacroAssembler* masm) {
+ Debug::GenerateCallConstructStubRecordDebugBreak(masm);
+}
+
+
+static void Generate_Slot_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateSlotDebugBreak(masm);
+}
+
+
+static void Generate_PlainReturn_LiveEdit(MacroAssembler* masm) {
+ Debug::GeneratePlainReturnLiveEdit(masm);
+}
+
+
+static void Generate_FrameDropper_LiveEdit(MacroAssembler* masm) {
+ Debug::GenerateFrameDropperLiveEdit(masm);
+}
+#endif
+
+
+Builtins::Builtins() : initialized_(false) {
+ memset(builtins_, 0, sizeof(builtins_[0]) * builtin_count);
+ memset(names_, 0, sizeof(names_[0]) * builtin_count);
+}
+
+
+Builtins::~Builtins() {
+}
+
+
+#define DEF_ENUM_C(name, ignore) FUNCTION_ADDR(Builtin_##name),
+Address const Builtins::c_functions_[cfunction_count] = {
+ BUILTIN_LIST_C(DEF_ENUM_C)
+};
+#undef DEF_ENUM_C
+
+#define DEF_JS_NAME(name, ignore) #name,
+#define DEF_JS_ARGC(ignore, argc) argc,
+const char* const Builtins::javascript_names_[id_count] = {
+ BUILTINS_LIST_JS(DEF_JS_NAME)
+};
+
+int const Builtins::javascript_argc_[id_count] = {
+ BUILTINS_LIST_JS(DEF_JS_ARGC)
+};
+#undef DEF_JS_NAME
+#undef DEF_JS_ARGC
+
+struct BuiltinDesc {
+ byte* generator;
+ byte* c_code;
+ const char* s_name; // name is only used for generating log information.
+ int name;
+ Code::Flags flags;
+ BuiltinExtraArguments extra_args;
+};
+
+#define BUILTIN_FUNCTION_TABLE_INIT { V8_ONCE_INIT, {} }
+
+class BuiltinFunctionTable {
+ public:
+ BuiltinDesc* functions() {
+ CallOnce(&once_, &Builtins::InitBuiltinFunctionTable);
+ return functions_;
+ }
+
+ OnceType once_;
+ BuiltinDesc functions_[Builtins::builtin_count + 1];
+
+ friend class Builtins;
+};
+
+static BuiltinFunctionTable builtin_function_table =
+ BUILTIN_FUNCTION_TABLE_INIT;
+
+// Define array of pointers to generators and C builtin functions.
+// We do this in a sort of roundabout way so that we can do the initialization
+// within the lexical scope of Builtins:: and within a context where
+// Code::Flags names a non-abstract type.
+void Builtins::InitBuiltinFunctionTable() {
+ BuiltinDesc* functions = builtin_function_table.functions_;
+ functions[builtin_count].generator = NULL;
+ functions[builtin_count].c_code = NULL;
+ functions[builtin_count].s_name = NULL;
+ functions[builtin_count].name = builtin_count;
+ functions[builtin_count].flags = static_cast<Code::Flags>(0);
+ functions[builtin_count].extra_args = NO_EXTRA_ARGUMENTS;
+
+#define DEF_FUNCTION_PTR_C(aname, aextra_args) \
+ functions->generator = FUNCTION_ADDR(Generate_Adaptor); \
+ functions->c_code = FUNCTION_ADDR(Builtin_##aname); \
+ functions->s_name = #aname; \
+ functions->name = c_##aname; \
+ functions->flags = Code::ComputeFlags(Code::BUILTIN); \
+ functions->extra_args = aextra_args; \
+ ++functions;
+
+#define DEF_FUNCTION_PTR_A(aname, kind, state, extra) \
+ functions->generator = FUNCTION_ADDR(Generate_##aname); \
+ functions->c_code = NULL; \
+ functions->s_name = #aname; \
+ functions->name = k##aname; \
+ functions->flags = Code::ComputeFlags(Code::kind, \
+ state, \
+ extra); \
+ functions->extra_args = NO_EXTRA_ARGUMENTS; \
+ ++functions;
+
+ BUILTIN_LIST_C(DEF_FUNCTION_PTR_C)
+ BUILTIN_LIST_A(DEF_FUNCTION_PTR_A)
+ BUILTIN_LIST_DEBUG_A(DEF_FUNCTION_PTR_A)
+
+#undef DEF_FUNCTION_PTR_C
+#undef DEF_FUNCTION_PTR_A
+}
+
+
+void Builtins::SetUp(bool create_heap_objects) {
+ ASSERT(!initialized_);
+ Isolate* isolate = Isolate::Current();
+ Heap* heap = isolate->heap();
+
+ // Create a scope for the handles in the builtins.
+ HandleScope scope(isolate);
+
+ const BuiltinDesc* functions = builtin_function_table.functions();
+
+ // For now we generate builtin adaptor code into a stack-allocated
+ // 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[8*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, 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;
+ masm.GetCode(&desc);
+ Code::Flags flags = functions[i].flags;
+ Object* code = NULL;
+ {
+ // During startup it's OK to always allocate and defer GC to later.
+ // This simplifies things because we don't need to retry.
+ AlwaysAllocateScope __scope__;
+ { MaybeObject* maybe_code =
+ heap->CreateCode(desc, flags, masm.CodeObject());
+ if (!maybe_code->ToObject(&code)) {
+ v8::internal::V8::FatalProcessOutOfMemory("CreateCode");
+ }
+ }
+ }
+ // Log the event and add the code to the builtins array.
+ PROFILE(isolate,
+ CodeCreateEvent(Logger::BUILTIN_TAG,
+ Code::cast(code),
+ functions[i].s_name));
+ GDBJIT(AddCode(GDBJITInterface::BUILTIN,
+ functions[i].s_name,
+ Code::cast(code)));
+ builtins_[i] = code;
+#ifdef ENABLE_DISASSEMBLER
+ if (FLAG_print_builtin_code) {
+ PrintF("Builtin: %s\n", functions[i].s_name);
+ Code::cast(code)->Disassemble(functions[i].s_name);
+ PrintF("\n");
+ }
+#endif
+ } else {
+ // Deserializing. The values will be filled in during IterateBuiltins.
+ builtins_[i] = NULL;
+ }
+ names_[i] = functions[i].s_name;
+ }
+
+ // Mark as initialized.
+ initialized_ = true;
+}
+
+
+void Builtins::TearDown() {
+ initialized_ = false;
+}
+
+
+void Builtins::IterateBuiltins(ObjectVisitor* v) {
+ v->VisitPointers(&builtins_[0], &builtins_[0] + builtin_count);
+}
+
+
+const char* Builtins::Lookup(byte* pc) {
+ // may be called during initialization (disassembler!)
+ if (initialized_) {
+ for (int i = 0; i < builtin_count; i++) {
+ Code* entry = Code::cast(builtins_[i]);
+ if (entry->contains(pc)) {
+ return names_[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+
+#define DEFINE_BUILTIN_ACCESSOR_C(name, ignore) \
+Handle<Code> Builtins::name() { \
+ Code** code_address = \
+ reinterpret_cast<Code**>(builtin_address(k##name)); \
+ return Handle<Code>(code_address); \
+}
+#define DEFINE_BUILTIN_ACCESSOR_A(name, kind, state, extra) \
+Handle<Code> Builtins::name() { \
+ Code** code_address = \
+ reinterpret_cast<Code**>(builtin_address(k##name)); \
+ return Handle<Code>(code_address); \
+}
+BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C)
+BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A)
+BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A)
+#undef DEFINE_BUILTIN_ACCESSOR_C
+#undef DEFINE_BUILTIN_ACCESSOR_A
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/builtins.h b/chromium/v8/src/builtins.h
new file mode 100644
index 00000000000..bb36c0251d9
--- /dev/null
+++ b/chromium/v8/src/builtins.h
@@ -0,0 +1,418 @@
+// 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_BUILTINS_H_
+#define V8_BUILTINS_H_
+
+namespace v8 {
+namespace internal {
+
+// Specifies extra arguments required by a C++ builtin.
+enum BuiltinExtraArguments {
+ NO_EXTRA_ARGUMENTS = 0,
+ NEEDS_CALLED_FUNCTION = 1
+};
+
+
+#define CODE_AGE_LIST_WITH_ARG(V, A) \
+ V(Quadragenarian, A) \
+ V(Quinquagenarian, A) \
+ V(Sexagenarian, A) \
+ V(Septuagenarian, A) \
+ V(Octogenarian, A)
+
+#define CODE_AGE_LIST_IGNORE_ARG(X, V) V(X)
+
+#define CODE_AGE_LIST(V) \
+ CODE_AGE_LIST_WITH_ARG(CODE_AGE_LIST_IGNORE_ARG, V)
+
+#define DECLARE_CODE_AGE_BUILTIN(C, V) \
+ V(Make##C##CodeYoungAgainOddMarking, BUILTIN, \
+ UNINITIALIZED, Code::kNoExtraICState) \
+ V(Make##C##CodeYoungAgainEvenMarking, BUILTIN, \
+ UNINITIALIZED, Code::kNoExtraICState)
+
+
+// Define list of builtins implemented in C++.
+#define BUILTIN_LIST_C(V) \
+ V(Illegal, NO_EXTRA_ARGUMENTS) \
+ \
+ V(EmptyFunction, NO_EXTRA_ARGUMENTS) \
+ \
+ V(InternalArrayCodeGeneric, NO_EXTRA_ARGUMENTS) \
+ V(ArrayCodeGeneric, NO_EXTRA_ARGUMENTS) \
+ \
+ V(ArrayPush, NO_EXTRA_ARGUMENTS) \
+ V(ArrayPop, NO_EXTRA_ARGUMENTS) \
+ V(ArrayShift, NO_EXTRA_ARGUMENTS) \
+ V(ArrayUnshift, NO_EXTRA_ARGUMENTS) \
+ V(ArraySlice, NO_EXTRA_ARGUMENTS) \
+ V(ArraySplice, NO_EXTRA_ARGUMENTS) \
+ V(ArrayConcat, NO_EXTRA_ARGUMENTS) \
+ \
+ V(HandleApiCall, NEEDS_CALLED_FUNCTION) \
+ V(HandleApiCallConstruct, NEEDS_CALLED_FUNCTION) \
+ V(HandleApiCallAsFunction, NO_EXTRA_ARGUMENTS) \
+ V(HandleApiCallAsConstructor, NO_EXTRA_ARGUMENTS) \
+ \
+ V(StrictModePoisonPill, NO_EXTRA_ARGUMENTS)
+
+// Define list of builtins implemented in assembly.
+#define BUILTIN_LIST_A(V) \
+ V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(InRecompileQueue, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(InstallRecompiledCode, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructStubCountdown, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructStubApi, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(LazyCompile, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(LazyRecompile, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(ParallelRecompile, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(NotifyDeoptimized, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(NotifySoftDeoptimized, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(NotifyLazyDeoptimized, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(NotifyStubFailure, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(NotifyOSR, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ \
+ V(LoadIC_Miss, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_MissForceGeneric, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_Slow, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Miss, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Slow, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedStoreIC_MissForceGeneric, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedStoreIC_Slow, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(LoadIC_Initialize, LOAD_IC, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(LoadIC_PreMonomorphic, LOAD_IC, PREMONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_Normal, LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_Getter_ForDeopt, LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_Slow, LOAD_IC, GENERIC, \
+ Code::kNoExtraICState) \
+ \
+ V(KeyedLoadIC_Initialize, KEYED_LOAD_IC, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_Generic, KEYED_LOAD_IC, GENERIC, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_NonStrictArguments, KEYED_LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ \
+ V(StoreIC_Initialize, STORE_IC, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Normal, STORE_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Generic, STORE_IC, GENERIC, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Generic_Strict, STORE_IC, GENERIC, \
+ kStrictMode) \
+ V(StoreIC_GlobalProxy, STORE_IC, GENERIC, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Initialize_Strict, STORE_IC, UNINITIALIZED, \
+ kStrictMode) \
+ V(StoreIC_Normal_Strict, STORE_IC, MONOMORPHIC, \
+ kStrictMode) \
+ V(StoreIC_Megamorphic_Strict, STORE_IC, MEGAMORPHIC, \
+ kStrictMode) \
+ V(StoreIC_GlobalProxy_Strict, STORE_IC, GENERIC, \
+ kStrictMode) \
+ V(StoreIC_Setter_ForDeopt, STORE_IC, MONOMORPHIC, \
+ kStrictMode) \
+ \
+ V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedStoreIC_Generic, KEYED_STORE_IC, GENERIC, \
+ Code::kNoExtraICState) \
+ \
+ V(KeyedStoreIC_Initialize_Strict, KEYED_STORE_IC, UNINITIALIZED, \
+ kStrictMode) \
+ V(KeyedStoreIC_Generic_Strict, KEYED_STORE_IC, GENERIC, \
+ kStrictMode) \
+ V(KeyedStoreIC_NonStrictArguments, KEYED_STORE_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ \
+ /* Uses KeyedLoadIC_Initialize; must be after in list. */ \
+ V(FunctionCall, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(FunctionApply, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ \
+ V(InternalArrayCode, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(ArrayCode, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ \
+ V(StringConstructCode, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ \
+ V(OnStackReplacement, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ CODE_AGE_LIST_WITH_ARG(DECLARE_CODE_AGE_BUILTIN, V)
+
+#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_STUB, \
+ DEBUG_BREAK) \
+ V(CallFunctionStub_DebugBreak, BUILTIN, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(CallFunctionStub_Recording_DebugBreak, BUILTIN, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(CallConstructStub_DebugBreak, BUILTIN, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(CallConstructStub_Recording_DebugBreak, BUILTIN, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(LoadIC_DebugBreak, LOAD_IC, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(KeyedLoadIC_DebugBreak, KEYED_LOAD_IC, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(StoreIC_DebugBreak, STORE_IC, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(KeyedStoreIC_DebugBreak, KEYED_STORE_IC, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(CompareNilIC_DebugBreak, COMPARE_NIL_IC, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(Slot_DebugBreak, BUILTIN, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(PlainReturn_LiveEdit, BUILTIN, DEBUG_STUB, \
+ DEBUG_BREAK) \
+ V(FrameDropper_LiveEdit, BUILTIN, DEBUG_STUB, \
+ DEBUG_BREAK)
+#else
+#define BUILTIN_LIST_DEBUG_A(V)
+#endif
+
+// Define list of builtins implemented in JavaScript.
+#define BUILTINS_LIST_JS(V) \
+ V(EQUALS, 1) \
+ V(STRICT_EQUALS, 1) \
+ V(COMPARE, 2) \
+ V(ADD, 1) \
+ V(SUB, 1) \
+ V(MUL, 1) \
+ V(DIV, 1) \
+ V(MOD, 1) \
+ V(BIT_OR, 1) \
+ V(BIT_AND, 1) \
+ V(BIT_XOR, 1) \
+ V(SHL, 1) \
+ V(SAR, 1) \
+ V(SHR, 1) \
+ V(DELETE, 2) \
+ V(IN, 1) \
+ V(INSTANCE_OF, 1) \
+ V(FILTER_KEY, 1) \
+ V(CALL_NON_FUNCTION, 0) \
+ V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \
+ V(CALL_FUNCTION_PROXY, 1) \
+ V(CALL_FUNCTION_PROXY_AS_CONSTRUCTOR, 1) \
+ V(TO_OBJECT, 0) \
+ V(TO_NUMBER, 0) \
+ V(TO_STRING, 0) \
+ V(STRING_ADD_LEFT, 1) \
+ V(STRING_ADD_RIGHT, 1) \
+ V(APPLY_PREPARE, 1) \
+ V(APPLY_OVERFLOW, 1)
+
+class BuiltinFunctionTable;
+class ObjectVisitor;
+
+
+class Builtins {
+ public:
+ ~Builtins();
+
+ // Generate all builtin code objects. Should be called once during
+ // isolate initialization.
+ void SetUp(bool create_heap_objects);
+ void TearDown();
+
+ // Garbage collection support.
+ void IterateBuiltins(ObjectVisitor* v);
+
+ // Disassembler support.
+ const char* Lookup(byte* pc);
+
+ enum Name {
+#define DEF_ENUM_C(name, ignore) k##name,
+#define DEF_ENUM_A(name, kind, state, extra) k##name,
+ BUILTIN_LIST_C(DEF_ENUM_C)
+ BUILTIN_LIST_A(DEF_ENUM_A)
+ BUILTIN_LIST_DEBUG_A(DEF_ENUM_A)
+#undef DEF_ENUM_C
+#undef DEF_ENUM_A
+ builtin_count
+ };
+
+ enum CFunctionId {
+#define DEF_ENUM_C(name, ignore) c_##name,
+ BUILTIN_LIST_C(DEF_ENUM_C)
+#undef DEF_ENUM_C
+ cfunction_count
+ };
+
+ enum JavaScript {
+#define DEF_ENUM(name, ignore) name,
+ BUILTINS_LIST_JS(DEF_ENUM)
+#undef DEF_ENUM
+ id_count
+ };
+
+#define DECLARE_BUILTIN_ACCESSOR_C(name, ignore) Handle<Code> name();
+#define DECLARE_BUILTIN_ACCESSOR_A(name, kind, state, extra) \
+ Handle<Code> name();
+ BUILTIN_LIST_C(DECLARE_BUILTIN_ACCESSOR_C)
+ BUILTIN_LIST_A(DECLARE_BUILTIN_ACCESSOR_A)
+ BUILTIN_LIST_DEBUG_A(DECLARE_BUILTIN_ACCESSOR_A)
+#undef DECLARE_BUILTIN_ACCESSOR_C
+#undef DECLARE_BUILTIN_ACCESSOR_A
+
+ Code* builtin(Name name) {
+ // Code::cast cannot be used here since we access builtins
+ // during the marking phase of mark sweep. See IC::Clear.
+ return reinterpret_cast<Code*>(builtins_[name]);
+ }
+
+ Address builtin_address(Name name) {
+ return reinterpret_cast<Address>(&builtins_[name]);
+ }
+
+ static Address c_function_address(CFunctionId id) {
+ return c_functions_[id];
+ }
+
+ static const char* GetName(JavaScript id) { return javascript_names_[id]; }
+ static int GetArgumentsCount(JavaScript id) { return javascript_argc_[id]; }
+ Handle<Code> GetCode(JavaScript id, bool* resolved);
+ static int NumberOfJavaScriptBuiltins() { return id_count; }
+
+ bool is_initialized() const { return initialized_; }
+
+ private:
+ Builtins();
+
+ // The external C++ functions called from the code.
+ static Address const c_functions_[cfunction_count];
+
+ // Note: These are always Code objects, but to conform with
+ // IterateBuiltins() above which assumes Object**'s for the callback
+ // function f, we use an Object* array here.
+ Object* builtins_[builtin_count];
+ const char* names_[builtin_count];
+ static const char* const javascript_names_[id_count];
+ static int const javascript_argc_[id_count];
+
+ static void Generate_Adaptor(MacroAssembler* masm,
+ CFunctionId id,
+ BuiltinExtraArguments extra_args);
+ static void Generate_InRecompileQueue(MacroAssembler* masm);
+ static void Generate_InstallRecompiledCode(MacroAssembler* masm);
+ static void Generate_ParallelRecompile(MacroAssembler* masm);
+ static void Generate_JSConstructStubCountdown(MacroAssembler* masm);
+ static void Generate_JSConstructStubGeneric(MacroAssembler* masm);
+ static void Generate_JSConstructStubApi(MacroAssembler* masm);
+ static void Generate_JSEntryTrampoline(MacroAssembler* masm);
+ static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
+ static void Generate_LazyCompile(MacroAssembler* masm);
+ static void Generate_LazyRecompile(MacroAssembler* masm);
+ static void Generate_NotifyDeoptimized(MacroAssembler* masm);
+ static void Generate_NotifySoftDeoptimized(MacroAssembler* masm);
+ static void Generate_NotifyLazyDeoptimized(MacroAssembler* masm);
+ static void Generate_NotifyOSR(MacroAssembler* masm);
+ static void Generate_NotifyStubFailure(MacroAssembler* masm);
+ static void Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm);
+
+ 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_StringConstructCode(MacroAssembler* masm);
+ static void Generate_OnStackReplacement(MacroAssembler* masm);
+
+#define DECLARE_CODE_AGE_BUILTIN_GENERATOR(C) \
+ static void Generate_Make##C##CodeYoungAgainEvenMarking( \
+ MacroAssembler* masm); \
+ static void Generate_Make##C##CodeYoungAgainOddMarking( \
+ MacroAssembler* masm);
+ CODE_AGE_LIST(DECLARE_CODE_AGE_BUILTIN_GENERATOR)
+#undef DECLARE_CODE_AGE_BUILTIN_GENERATOR
+
+ static void InitBuiltinFunctionTable();
+
+ bool initialized_;
+
+ friend class BuiltinFunctionTable;
+ friend class Isolate;
+
+ DISALLOW_COPY_AND_ASSIGN(Builtins);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_BUILTINS_H_
diff --git a/chromium/v8/src/bytecodes-irregexp.h b/chromium/v8/src/bytecodes-irregexp.h
new file mode 100644
index 00000000000..c7cc66e5275
--- /dev/null
+++ b/chromium/v8/src/bytecodes-irregexp.h
@@ -0,0 +1,104 @@
+// 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_BYTECODES_IRREGEXP_H_
+#define V8_BYTECODES_IRREGEXP_H_
+
+namespace v8 {
+namespace internal {
+
+
+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.
+const unsigned int MAX_FIRST_ARG = 0x7fffffu;
+const int BYTECODE_SHIFT = 8;
+
+#define BYTECODE_ITERATOR(V) \
+V(BREAK, 0, 4) /* bc8 */ \
+V(PUSH_CP, 1, 4) /* bc8 pad24 */ \
+V(PUSH_BT, 2, 8) /* bc8 pad24 offset32 */ \
+V(PUSH_REGISTER, 3, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER_TO_CP, 4, 8) /* bc8 reg_idx24 offset32 */ \
+V(SET_CP_TO_REGISTER, 5, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER_TO_SP, 6, 4) /* bc8 reg_idx24 */ \
+V(SET_SP_TO_REGISTER, 7, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER, 8, 8) /* bc8 reg_idx24 value32 */ \
+V(ADVANCE_REGISTER, 9, 8) /* bc8 reg_idx24 value32 */ \
+V(POP_CP, 10, 4) /* bc8 pad24 */ \
+V(POP_BT, 11, 4) /* bc8 pad24 */ \
+V(POP_REGISTER, 12, 4) /* bc8 reg_idx24 */ \
+V(FAIL, 13, 4) /* bc8 pad24 */ \
+V(SUCCEED, 14, 4) /* bc8 pad24 */ \
+V(ADVANCE_CP, 15, 4) /* bc8 offset24 */ \
+V(GOTO, 16, 8) /* bc8 pad24 addr32 */ \
+V(LOAD_CURRENT_CHAR, 17, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_CURRENT_CHAR_UNCHECKED, 18, 4) /* bc8 offset24 */ \
+V(LOAD_2_CURRENT_CHARS, 19, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_2_CURRENT_CHARS_UNCHECKED, 20, 4) /* bc8 offset24 */ \
+V(LOAD_4_CURRENT_CHARS, 21, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_4_CURRENT_CHARS_UNCHECKED, 22, 4) /* bc8 offset24 */ \
+V(CHECK_4_CHARS, 23, 12) /* bc8 pad24 uint32 addr32 */ \
+V(CHECK_CHAR, 24, 8) /* bc8 pad8 uint16 addr32 */ \
+V(CHECK_NOT_4_CHARS, 25, 12) /* bc8 pad24 uint32 addr32 */ \
+V(CHECK_NOT_CHAR, 26, 8) /* bc8 pad8 uint16 addr32 */ \
+V(AND_CHECK_4_CHARS, 27, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
+V(AND_CHECK_CHAR, 28, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
+V(AND_CHECK_NOT_4_CHARS, 29, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
+V(AND_CHECK_NOT_CHAR, 30, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
+V(MINUS_AND_CHECK_NOT_CHAR, 31, 12) /* bc8 pad8 uc16 uc16 uc16 addr32 */ \
+V(CHECK_CHAR_IN_RANGE, 32, 12) /* bc8 pad24 uc16 uc16 addr32 */ \
+V(CHECK_CHAR_NOT_IN_RANGE, 33, 12) /* bc8 pad24 uc16 uc16 addr32 */ \
+V(CHECK_BIT_IN_TABLE, 34, 24) /* bc8 pad24 addr32 bits128 */ \
+V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
+V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
+V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_NOT_REGS_EQUAL, 39, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
+V(CHECK_REGISTER_LT, 40, 12) /* bc8 reg_idx24 value32 addr32 */ \
+V(CHECK_REGISTER_GE, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \
+V(CHECK_REGISTER_EQ_POS, 42, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_AT_START, 43, 8) /* bc8 pad24 addr32 */ \
+V(CHECK_NOT_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
+V(CHECK_GREEDY, 45, 8) /* bc8 pad24 addr32 */ \
+V(ADVANCE_CP_AND_GOTO, 46, 8) /* bc8 offset24 addr32 */ \
+V(SET_CURRENT_POSITION_FROM_END, 47, 4) /* bc8 idx24 */
+
+#define DECLARE_BYTECODES(name, code, length) \
+ static const int BC_##name = code;
+BYTECODE_ITERATOR(DECLARE_BYTECODES)
+#undef DECLARE_BYTECODES
+
+#define DECLARE_BYTECODE_LENGTH(name, code, length) \
+ static const int BC_##name##_LENGTH = length;
+BYTECODE_ITERATOR(DECLARE_BYTECODE_LENGTH)
+#undef DECLARE_BYTECODE_LENGTH
+} }
+
+#endif // V8_BYTECODES_IRREGEXP_H_
diff --git a/chromium/v8/src/cached-powers.cc b/chromium/v8/src/cached-powers.cc
new file mode 100644
index 00000000000..fbfaf26159a
--- /dev/null
+++ b/chromium/v8/src/cached-powers.cc
@@ -0,0 +1,179 @@
+// 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 <stdarg.h>
+#include <limits.h>
+#include <cmath>
+
+#include "../include/v8stdint.h"
+#include "globals.h"
+#include "checks.h"
+#include "cached-powers.h"
+
+namespace v8 {
+namespace internal {
+
+struct CachedPower {
+ uint64_t significand;
+ int16_t binary_exponent;
+ int16_t decimal_exponent;
+};
+
+static const CachedPower kCachedPowers[] = {
+ {V8_2PART_UINT64_C(0xfa8fd5a0, 081c0288), -1220, -348},
+ {V8_2PART_UINT64_C(0xbaaee17f, a23ebf76), -1193, -340},
+ {V8_2PART_UINT64_C(0x8b16fb20, 3055ac76), -1166, -332},
+ {V8_2PART_UINT64_C(0xcf42894a, 5dce35ea), -1140, -324},
+ {V8_2PART_UINT64_C(0x9a6bb0aa, 55653b2d), -1113, -316},
+ {V8_2PART_UINT64_C(0xe61acf03, 3d1a45df), -1087, -308},
+ {V8_2PART_UINT64_C(0xab70fe17, c79ac6ca), -1060, -300},
+ {V8_2PART_UINT64_C(0xff77b1fc, bebcdc4f), -1034, -292},
+ {V8_2PART_UINT64_C(0xbe5691ef, 416bd60c), -1007, -284},
+ {V8_2PART_UINT64_C(0x8dd01fad, 907ffc3c), -980, -276},
+ {V8_2PART_UINT64_C(0xd3515c28, 31559a83), -954, -268},
+ {V8_2PART_UINT64_C(0x9d71ac8f, ada6c9b5), -927, -260},
+ {V8_2PART_UINT64_C(0xea9c2277, 23ee8bcb), -901, -252},
+ {V8_2PART_UINT64_C(0xaecc4991, 4078536d), -874, -244},
+ {V8_2PART_UINT64_C(0x823c1279, 5db6ce57), -847, -236},
+ {V8_2PART_UINT64_C(0xc2109436, 4dfb5637), -821, -228},
+ {V8_2PART_UINT64_C(0x9096ea6f, 3848984f), -794, -220},
+ {V8_2PART_UINT64_C(0xd77485cb, 25823ac7), -768, -212},
+ {V8_2PART_UINT64_C(0xa086cfcd, 97bf97f4), -741, -204},
+ {V8_2PART_UINT64_C(0xef340a98, 172aace5), -715, -196},
+ {V8_2PART_UINT64_C(0xb23867fb, 2a35b28e), -688, -188},
+ {V8_2PART_UINT64_C(0x84c8d4df, d2c63f3b), -661, -180},
+ {V8_2PART_UINT64_C(0xc5dd4427, 1ad3cdba), -635, -172},
+ {V8_2PART_UINT64_C(0x936b9fce, bb25c996), -608, -164},
+ {V8_2PART_UINT64_C(0xdbac6c24, 7d62a584), -582, -156},
+ {V8_2PART_UINT64_C(0xa3ab6658, 0d5fdaf6), -555, -148},
+ {V8_2PART_UINT64_C(0xf3e2f893, dec3f126), -529, -140},
+ {V8_2PART_UINT64_C(0xb5b5ada8, aaff80b8), -502, -132},
+ {V8_2PART_UINT64_C(0x87625f05, 6c7c4a8b), -475, -124},
+ {V8_2PART_UINT64_C(0xc9bcff60, 34c13053), -449, -116},
+ {V8_2PART_UINT64_C(0x964e858c, 91ba2655), -422, -108},
+ {V8_2PART_UINT64_C(0xdff97724, 70297ebd), -396, -100},
+ {V8_2PART_UINT64_C(0xa6dfbd9f, b8e5b88f), -369, -92},
+ {V8_2PART_UINT64_C(0xf8a95fcf, 88747d94), -343, -84},
+ {V8_2PART_UINT64_C(0xb9447093, 8fa89bcf), -316, -76},
+ {V8_2PART_UINT64_C(0x8a08f0f8, bf0f156b), -289, -68},
+ {V8_2PART_UINT64_C(0xcdb02555, 653131b6), -263, -60},
+ {V8_2PART_UINT64_C(0x993fe2c6, d07b7fac), -236, -52},
+ {V8_2PART_UINT64_C(0xe45c10c4, 2a2b3b06), -210, -44},
+ {V8_2PART_UINT64_C(0xaa242499, 697392d3), -183, -36},
+ {V8_2PART_UINT64_C(0xfd87b5f2, 8300ca0e), -157, -28},
+ {V8_2PART_UINT64_C(0xbce50864, 92111aeb), -130, -20},
+ {V8_2PART_UINT64_C(0x8cbccc09, 6f5088cc), -103, -12},
+ {V8_2PART_UINT64_C(0xd1b71758, e219652c), -77, -4},
+ {V8_2PART_UINT64_C(0x9c400000, 00000000), -50, 4},
+ {V8_2PART_UINT64_C(0xe8d4a510, 00000000), -24, 12},
+ {V8_2PART_UINT64_C(0xad78ebc5, ac620000), 3, 20},
+ {V8_2PART_UINT64_C(0x813f3978, f8940984), 30, 28},
+ {V8_2PART_UINT64_C(0xc097ce7b, c90715b3), 56, 36},
+ {V8_2PART_UINT64_C(0x8f7e32ce, 7bea5c70), 83, 44},
+ {V8_2PART_UINT64_C(0xd5d238a4, abe98068), 109, 52},
+ {V8_2PART_UINT64_C(0x9f4f2726, 179a2245), 136, 60},
+ {V8_2PART_UINT64_C(0xed63a231, d4c4fb27), 162, 68},
+ {V8_2PART_UINT64_C(0xb0de6538, 8cc8ada8), 189, 76},
+ {V8_2PART_UINT64_C(0x83c7088e, 1aab65db), 216, 84},
+ {V8_2PART_UINT64_C(0xc45d1df9, 42711d9a), 242, 92},
+ {V8_2PART_UINT64_C(0x924d692c, a61be758), 269, 100},
+ {V8_2PART_UINT64_C(0xda01ee64, 1a708dea), 295, 108},
+ {V8_2PART_UINT64_C(0xa26da399, 9aef774a), 322, 116},
+ {V8_2PART_UINT64_C(0xf209787b, b47d6b85), 348, 124},
+ {V8_2PART_UINT64_C(0xb454e4a1, 79dd1877), 375, 132},
+ {V8_2PART_UINT64_C(0x865b8692, 5b9bc5c2), 402, 140},
+ {V8_2PART_UINT64_C(0xc83553c5, c8965d3d), 428, 148},
+ {V8_2PART_UINT64_C(0x952ab45c, fa97a0b3), 455, 156},
+ {V8_2PART_UINT64_C(0xde469fbd, 99a05fe3), 481, 164},
+ {V8_2PART_UINT64_C(0xa59bc234, db398c25), 508, 172},
+ {V8_2PART_UINT64_C(0xf6c69a72, a3989f5c), 534, 180},
+ {V8_2PART_UINT64_C(0xb7dcbf53, 54e9bece), 561, 188},
+ {V8_2PART_UINT64_C(0x88fcf317, f22241e2), 588, 196},
+ {V8_2PART_UINT64_C(0xcc20ce9b, d35c78a5), 614, 204},
+ {V8_2PART_UINT64_C(0x98165af3, 7b2153df), 641, 212},
+ {V8_2PART_UINT64_C(0xe2a0b5dc, 971f303a), 667, 220},
+ {V8_2PART_UINT64_C(0xa8d9d153, 5ce3b396), 694, 228},
+ {V8_2PART_UINT64_C(0xfb9b7cd9, a4a7443c), 720, 236},
+ {V8_2PART_UINT64_C(0xbb764c4c, a7a44410), 747, 244},
+ {V8_2PART_UINT64_C(0x8bab8eef, b6409c1a), 774, 252},
+ {V8_2PART_UINT64_C(0xd01fef10, a657842c), 800, 260},
+ {V8_2PART_UINT64_C(0x9b10a4e5, e9913129), 827, 268},
+ {V8_2PART_UINT64_C(0xe7109bfb, a19c0c9d), 853, 276},
+ {V8_2PART_UINT64_C(0xac2820d9, 623bf429), 880, 284},
+ {V8_2PART_UINT64_C(0x80444b5e, 7aa7cf85), 907, 292},
+ {V8_2PART_UINT64_C(0xbf21e440, 03acdd2d), 933, 300},
+ {V8_2PART_UINT64_C(0x8e679c2f, 5e44ff8f), 960, 308},
+ {V8_2PART_UINT64_C(0xd433179d, 9c8cb841), 986, 316},
+ {V8_2PART_UINT64_C(0x9e19db92, b4e31ba9), 1013, 324},
+ {V8_2PART_UINT64_C(0xeb96bf6e, badf77d9), 1039, 332},
+ {V8_2PART_UINT64_C(0xaf87023b, 9bf0ee6b), 1066, 340},
+};
+
+static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers);
+static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent.
+static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
+// 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,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent) {
+ int kQ = DiyFp::kSignificandSize;
+ // Some platforms return incorrect sign on 0 result. We can ignore that here,
+ // which means we can avoid depending on platform.h.
+ double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
+ int foo = kCachedPowersOffset;
+ int index =
+ (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
+ ASSERT(0 <= index && index < kCachedPowersLength);
+ CachedPower cached_power = kCachedPowers[index];
+ ASSERT(min_exponent <= cached_power.binary_exponent);
+ ASSERT(cached_power.binary_exponent <= max_exponent);
+ *decimal_exponent = cached_power.decimal_exponent;
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+}
+
+
+void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent) {
+ ASSERT(kMinDecimalExponent <= requested_exponent);
+ ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
+ int index =
+ (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
+ CachedPower cached_power = kCachedPowers[index];
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+ *found_exponent = cached_power.decimal_exponent;
+ ASSERT(*found_exponent <= requested_exponent);
+ ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/cached-powers.h b/chromium/v8/src/cached-powers.h
new file mode 100644
index 00000000000..88df22260c3
--- /dev/null
+++ b/chromium/v8/src/cached-powers.h
@@ -0,0 +1,64 @@
+// 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.
+
+#ifndef V8_CACHED_POWERS_H_
+#define V8_CACHED_POWERS_H_
+
+#include "diy-fp.h"
+
+namespace v8 {
+namespace internal {
+
+class PowersOfTenCache {
+ public:
+ // Not all powers of ten are cached. The decimal exponent of two neighboring
+ // cached numbers will differ by kDecimalExponentDistance.
+ static const int kDecimalExponentDistance;
+
+ static const int kMinDecimalExponent;
+ static const int kMaxDecimalExponent;
+
+ // Returns a cached power-of-ten with a binary exponent in the range
+ // [min_exponent; max_exponent] (boundaries included).
+ static void GetCachedPowerForBinaryExponentRange(int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent);
+
+ // Returns a cached power of ten x ~= 10^k such that
+ // k <= decimal_exponent < k + kCachedPowersDecimalDistance.
+ // The given decimal_exponent must satisfy
+ // kMinDecimalExponent <= requested_exponent, and
+ // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance.
+ static void GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_CACHED_POWERS_H_
diff --git a/chromium/v8/src/char-predicates-inl.h b/chromium/v8/src/char-predicates-inl.h
new file mode 100644
index 00000000000..dee9ccd3815
--- /dev/null
+++ b/chromium/v8/src/char-predicates-inl.h
@@ -0,0 +1,106 @@
+// 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_CHAR_PREDICATES_INL_H_
+#define V8_CHAR_PREDICATES_INL_H_
+
+#include "char-predicates.h"
+
+namespace v8 {
+namespace internal {
+
+
+// If c is in 'A'-'Z' or 'a'-'z', return its lower-case.
+// Else, return something outside of 'A'-'Z' and 'a'-'z'.
+// Note: it ignores LOCALE.
+inline int AsciiAlphaToLower(uc32 c) {
+ return c | 0x20;
+}
+
+
+inline bool IsCarriageReturn(uc32 c) {
+ return c == 0x000D;
+}
+
+
+inline bool IsLineFeed(uc32 c) {
+ return c == 0x000A;
+}
+
+
+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);
+}
+
+
+inline bool IsDecimalDigit(uc32 c) {
+ // ECMA-262, 3rd, 7.8.3 (p 16)
+ return IsInRange(c, '0', '9');
+}
+
+
+inline bool IsHexDigit(uc32 c) {
+ // ECMA-262, 3rd, 7.6 (p 15)
+ return IsDecimalDigit(c) || IsInRange(AsciiAlphaToLower(c), 'a', 'f');
+}
+
+
+inline bool IsOctalDigit(uc32 c) {
+ // ECMA-262, 6th, 7.8.3
+ return IsInRange(c, '0', '7');
+}
+
+
+inline bool IsBinaryDigit(uc32 c) {
+ // ECMA-262, 6th, 7.8.3
+ return c == '0' || c == '1';
+}
+
+
+inline bool IsRegExpWord(uc16 c) {
+ return IsInRange(AsciiAlphaToLower(c), 'a', 'z')
+ || IsDecimalDigit(c)
+ || (c == '_');
+}
+
+
+inline bool IsRegExpNewline(uc16 c) {
+ switch (c) {
+ // CR LF LS PS
+ case 0x000A: case 0x000D: case 0x2028: case 0x2029:
+ return false;
+ default:
+ return true;
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_CHAR_PREDICATES_INL_H_
diff --git a/chromium/v8/src/char-predicates.h b/chromium/v8/src/char-predicates.h
new file mode 100644
index 00000000000..767ad6513af
--- /dev/null
+++ b/chromium/v8/src/char-predicates.h
@@ -0,0 +1,71 @@
+// 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_CHAR_PREDICATES_H_
+#define V8_CHAR_PREDICATES_H_
+
+#include "unicode.h"
+
+namespace v8 {
+namespace internal {
+
+// Unicode character predicates as defined by ECMA-262, 3rd,
+// used for lexical analysis.
+
+inline bool IsCarriageReturn(uc32 c);
+inline bool IsLineFeed(uc32 c);
+inline bool IsDecimalDigit(uc32 c);
+inline bool IsHexDigit(uc32 c);
+inline bool IsOctalDigit(uc32 c);
+inline bool IsBinaryDigit(uc32 c);
+inline bool IsRegExpWord(uc32 c);
+inline bool IsRegExpNewline(uc32 c);
+
+struct IdentifierStart {
+ static inline bool Is(uc32 c) {
+ switch (c) {
+ case '$': case '_': case '\\': return true;
+ default: return unibrow::Letter::Is(c);
+ }
+ }
+};
+
+
+struct IdentifierPart {
+ static inline bool Is(uc32 c) {
+ return IdentifierStart::Is(c)
+ || unibrow::Number::Is(c)
+ || c == 0x200C // U+200C is Zero-Width Non-Joiner.
+ || c == 0x200D // U+200D is Zero-Width Joiner.
+ || unibrow::CombiningMark::Is(c)
+ || unibrow::ConnectorPunctuation::Is(c);
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_CHAR_PREDICATES_H_
diff --git a/chromium/v8/src/checks.cc b/chromium/v8/src/checks.cc
new file mode 100644
index 00000000000..82086824dd2
--- /dev/null
+++ b/chromium/v8/src/checks.cc
@@ -0,0 +1,113 @@
+// 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.
+
+#include <stdarg.h>
+
+#include "v8.h"
+
+#include "platform.h"
+
+// TODO(isolates): is it necessary to lift this?
+static int fatal_error_handler_nesting_depth = 0;
+
+// Contains protection against recursive calls (faults while handling faults).
+extern "C" void V8_Fatal(const char* file, int line, const char* format, ...) {
+ i::AllowHandleDereference allow_deref;
+ i::AllowDeferredHandleDereference allow_deferred_deref;
+ fflush(stdout);
+ fflush(stderr);
+ fatal_error_handler_nesting_depth++;
+ // First time we try to print an error message
+ if (fatal_error_handler_nesting_depth < 2) {
+ i::OS::PrintError("\n\n#\n# Fatal error in %s, line %d\n# ", file, line);
+ va_list arguments;
+ va_start(arguments, format);
+ i::OS::VPrintError(format, arguments);
+ va_end(arguments);
+ i::OS::PrintError("\n#\n");
+ i::OS::DumpBacktrace();
+ }
+ // First two times we may try to print a stack dump.
+ if (fatal_error_handler_nesting_depth < 3) {
+ if (i::FLAG_stack_trace_on_abort) {
+ // Call this one twice on double fault
+ i::Isolate::Current()->PrintStack(stderr);
+ }
+ }
+ i::OS::Abort();
+}
+
+
+void CheckEqualsHelper(const char* file,
+ int line,
+ const char* expected_source,
+ v8::Handle<v8::Value> expected,
+ const char* value_source,
+ v8::Handle<v8::Value> value) {
+ if (!expected->Equals(value)) {
+ v8::String::Utf8Value value_str(value);
+ v8::String::Utf8Value expected_str(expected);
+ V8_Fatal(file, line,
+ "CHECK_EQ(%s, %s) failed\n# Expected: %s\n# Found: %s",
+ expected_source, value_source, *expected_str, *value_str);
+ }
+}
+
+
+void CheckNonEqualsHelper(const char* file,
+ int line,
+ const char* unexpected_source,
+ v8::Handle<v8::Value> unexpected,
+ const char* value_source,
+ v8::Handle<v8::Value> value) {
+ if (unexpected->Equals(value)) {
+ v8::String::Utf8Value value_str(value);
+ V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %s",
+ unexpected_source, value_source, *value_str);
+ }
+}
+
+
+void API_Fatal(const char* location, const char* format, ...) {
+ i::OS::PrintError("\n#\n# Fatal error in %s\n# ", location);
+ va_list arguments;
+ va_start(arguments, format);
+ i::OS::VPrintError(format, arguments);
+ va_end(arguments);
+ i::OS::PrintError("\n#\n\n");
+ i::OS::Abort();
+}
+
+
+namespace v8 { namespace internal {
+
+ bool EnableSlowAsserts() { return FLAG_enable_slow_asserts; }
+
+ intptr_t HeapObjectTagMask() { return kHeapObjectTagMask; }
+
+} } // namespace v8::internal
+
diff --git a/chromium/v8/src/checks.h b/chromium/v8/src/checks.h
new file mode 100644
index 00000000000..b309e2c42c7
--- /dev/null
+++ b/chromium/v8/src/checks.h
@@ -0,0 +1,301 @@
+// Copyright 2012 the V8 project authors. 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_CHECKS_H_
+#define V8_CHECKS_H_
+
+#include <string.h>
+
+#include "../include/v8stdint.h"
+extern "C" void V8_Fatal(const char* file, int line, const char* format, ...);
+
+// The FATAL, UNREACHABLE and UNIMPLEMENTED macros are useful during
+// development, but they should not be relied on in the final product.
+#ifdef DEBUG
+#define FATAL(msg) \
+ V8_Fatal(__FILE__, __LINE__, "%s", (msg))
+#define UNIMPLEMENTED() \
+ V8_Fatal(__FILE__, __LINE__, "unimplemented code")
+#define UNREACHABLE() \
+ V8_Fatal(__FILE__, __LINE__, "unreachable code")
+#else
+#define FATAL(msg) \
+ V8_Fatal("", 0, "%s", (msg))
+#define UNIMPLEMENTED() \
+ V8_Fatal("", 0, "unimplemented code")
+#define UNREACHABLE() ((void) 0)
+#endif
+
+
+// The CHECK macro checks that the given condition is true; if not, it
+// prints a message to stderr and aborts.
+#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.
+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",
+ expected_source, value_source, expected, value);
+ }
+}
+
+
+// Helper function used by the CHECK_EQ function when given int64_t
+// arguments. Should not be called directly.
+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.
+ V8_Fatal(file, line,
+ "CHECK_EQ(%s, %s) failed\n#"
+ " Expected: 0x%08x%08x\n# Found: 0x%08x%08x",
+ expected_source, value_source,
+ static_cast<uint32_t>(expected >> 32),
+ static_cast<uint32_t>(expected),
+ static_cast<uint32_t>(value >> 32),
+ static_cast<uint32_t>(value));
+ }
+}
+
+
+// Helper function used by the CHECK_NE function when given int
+// arguments. Should not be called directly.
+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);
+ }
+}
+
+
+// Helper function used by the CHECK function when given string
+// arguments. Should not be called directly.
+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)) {
+ V8_Fatal(file, line,
+ "CHECK_EQ(%s, %s) failed\n# Expected: %s\n# Found: %s",
+ expected_source, value_source, expected, 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",
+ expected_source, value_source, value);
+ }
+}
+
+
+// Helper function used by the CHECK function when given pointer
+// arguments. Should not be called directly.
+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",
+ expected_source, value_source,
+ expected, 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);
+ }
+}
+
+
+// Helper function used by the CHECK function when given floating
+// point arguments. Should not be called directly.
+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;
+ volatile double* val = new double[1];
+ *val = value;
+ if (*exp != *val) {
+ V8_Fatal(file, line,
+ "CHECK_EQ(%s, %s) failed\n# Expected: %f\n# Found: %f",
+ expected_source, value_source, *exp, *val);
+ }
+ delete[] exp;
+ delete[] val;
+}
+
+
+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;
+ volatile double* val = new double[1];
+ *val = value;
+ if (*exp == *val) {
+ V8_Fatal(file, line,
+ "CHECK_NE(%s, %s) failed\n# Value: %f",
+ expected_source, value_source, *val);
+ }
+ delete[] exp;
+ delete[] val;
+}
+
+
+#define CHECK_EQ(expected, value) CheckEqualsHelper(__FILE__, __LINE__, \
+ #expected, expected, #value, value)
+
+
+#define CHECK_NE(unexpected, value) CheckNonEqualsHelper(__FILE__, __LINE__, \
+ #unexpected, unexpected, #value, value)
+
+
+#define CHECK_GT(a, b) CHECK((a) > (b))
+#define CHECK_GE(a, b) CHECK((a) >= (b))
+#define CHECK_LT(a, b) CHECK((a) < (b))
+#define CHECK_LE(a, b) CHECK((a) <= (b))
+
+
+// Use C++11 static_assert if possible, which gives error
+// messages that are easier to understand on first sight.
+#if __cplusplus >= 201103L
+#define STATIC_CHECK(test) static_assert(test, #test)
+#else
+// This is inspired by the static assertion facility in boost. This
+// is pretty magical. If it causes you trouble on a platform you may
+// find a fix in the boost code.
+template <bool> class StaticAssertion;
+template <> class StaticAssertion<true> { };
+// This macro joins two tokens. If one of the tokens is a macro the
+// helper call causes it to be resolved before joining.
+#define SEMI_STATIC_JOIN(a, b) SEMI_STATIC_JOIN_HELPER(a, b)
+#define SEMI_STATIC_JOIN_HELPER(a, b) a##b
+// Causes an error during compilation of the condition is not
+// statically known to be true. It is formulated as a typedef so that
+// it can be used wherever a typedef can be used. Beware that this
+// actually causes each use to introduce a new defined type with a
+// name depending on the source line.
+template <int> class StaticAssertionHelper { };
+#define STATIC_CHECK(test) \
+ typedef \
+ StaticAssertionHelper<sizeof(StaticAssertion<static_cast<bool>((test))>)> \
+ SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__)
+#endif
+
+
+extern bool FLAG_enable_slow_asserts;
+
+
+// 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) CHECK(!FLAG_enable_slow_asserts || (condition))
+#else
+#define ASSERT_RESULT(expr) (expr)
+#define ASSERT(condition) ((void) 0)
+#define ASSERT_EQ(v1, v2) ((void) 0)
+#define ASSERT_NE(v1, v2) ((void) 0)
+#define ASSERT_GE(v1, v2) ((void) 0)
+#define ASSERT_LT(v1, v2) ((void) 0)
+#define ASSERT_LE(v1, v2) ((void) 0)
+#define SLOW_ASSERT(condition) ((void) 0)
+#endif
+// Static asserts has no impact on runtime performance, so they can be
+// safely enabled in release mode. Moreover, the ((void) 0) expression
+// obeys different syntax rules than typedef's, e.g. it can't appear
+// inside class declaration, this leads to inconsistency between debug
+// and release compilation modes behavior.
+#define STATIC_ASSERT(test) STATIC_CHECK(test)
+
+#define ASSERT_NOT_NULL(p) ASSERT_NE(NULL, p)
+
+// "Extra checks" are lightweight checks that are enabled in some release
+// builds.
+#ifdef ENABLE_EXTRA_CHECKS
+#define EXTRA_CHECK(condition) CHECK(condition)
+#else
+#define EXTRA_CHECK(condition) ((void) 0)
+#endif
+
+#endif // V8_CHECKS_H_
diff --git a/chromium/v8/src/circular-queue-inl.h b/chromium/v8/src/circular-queue-inl.h
new file mode 100644
index 00000000000..b48070ab5d2
--- /dev/null
+++ b/chromium/v8/src/circular-queue-inl.h
@@ -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.
+
+#ifndef V8_CIRCULAR_QUEUE_INL_H_
+#define V8_CIRCULAR_QUEUE_INL_H_
+
+#include "circular-queue.h"
+
+namespace v8 {
+namespace internal {
+
+
+void* SamplingCircularQueue::Enqueue() {
+ if (producer_pos_->enqueue_pos == producer_pos_->next_chunk_pos) {
+ if (producer_pos_->enqueue_pos == buffer_ + buffer_size_) {
+ producer_pos_->next_chunk_pos = buffer_;
+ producer_pos_->enqueue_pos = buffer_;
+ }
+ Acquire_Store(producer_pos_->next_chunk_pos, kEnqueueStarted);
+ // Skip marker.
+ producer_pos_->enqueue_pos += 1;
+ producer_pos_->next_chunk_pos += chunk_size_;
+ }
+ void* result = producer_pos_->enqueue_pos;
+ producer_pos_->enqueue_pos += record_size_;
+ return result;
+}
+
+
+void SamplingCircularQueue::WrapPositionIfNeeded(
+ SamplingCircularQueue::Cell** pos) {
+ if (*pos == buffer_ + buffer_size_) *pos = buffer_;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_CIRCULAR_QUEUE_INL_H_
diff --git a/chromium/v8/src/circular-queue.cc b/chromium/v8/src/circular-queue.cc
new file mode 100644
index 00000000000..0aea3435927
--- /dev/null
+++ b/chromium/v8/src/circular-queue.cc
@@ -0,0 +1,125 @@
+// 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.
+
+#include "v8.h"
+
+#include "circular-queue-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+SamplingCircularQueue::SamplingCircularQueue(size_t record_size_in_bytes,
+ size_t desired_chunk_size_in_bytes,
+ unsigned buffer_size_in_chunks)
+ : record_size_(record_size_in_bytes / sizeof(Cell)),
+ chunk_size_in_bytes_(desired_chunk_size_in_bytes / record_size_in_bytes *
+ record_size_in_bytes + sizeof(Cell)),
+ chunk_size_(chunk_size_in_bytes_ / sizeof(Cell)),
+ buffer_size_(chunk_size_ * buffer_size_in_chunks),
+ buffer_(NewArray<Cell>(buffer_size_)) {
+ ASSERT(record_size_ * sizeof(Cell) == record_size_in_bytes);
+ ASSERT(chunk_size_ * sizeof(Cell) == chunk_size_in_bytes_);
+ ASSERT(buffer_size_in_chunks > 2);
+ // Mark all chunks as clear.
+ for (size_t i = 0; i < buffer_size_; i += chunk_size_) {
+ buffer_[i] = kClear;
+ }
+
+ // Layout producer and consumer position pointers each on their own
+ // cache lines to avoid cache lines thrashing due to simultaneous
+ // updates of positions by different processor cores.
+ const int positions_size =
+ RoundUp(1, kProcessorCacheLineSize) +
+ RoundUp(static_cast<int>(sizeof(ProducerPosition)),
+ kProcessorCacheLineSize) +
+ RoundUp(static_cast<int>(sizeof(ConsumerPosition)),
+ kProcessorCacheLineSize);
+ positions_ = NewArray<byte>(positions_size);
+
+ producer_pos_ = reinterpret_cast<ProducerPosition*>(
+ RoundUp(positions_, kProcessorCacheLineSize));
+ producer_pos_->next_chunk_pos = buffer_;
+ producer_pos_->enqueue_pos = buffer_;
+
+ consumer_pos_ = reinterpret_cast<ConsumerPosition*>(
+ reinterpret_cast<byte*>(producer_pos_) + kProcessorCacheLineSize);
+ ASSERT(reinterpret_cast<byte*>(consumer_pos_ + 1) <=
+ positions_ + positions_size);
+ consumer_pos_->dequeue_chunk_pos = buffer_;
+ // The distance ensures that producer and consumer never step on
+ // each other's chunks and helps eviction of produced data from
+ // the CPU cache (having that chunk size is bigger than the cache.)
+ const size_t producer_consumer_distance = (2 * chunk_size_);
+ consumer_pos_->dequeue_chunk_poll_pos = buffer_ + producer_consumer_distance;
+ consumer_pos_->dequeue_pos = NULL;
+}
+
+
+SamplingCircularQueue::~SamplingCircularQueue() {
+ DeleteArray(positions_);
+ DeleteArray(buffer_);
+}
+
+
+void* SamplingCircularQueue::StartDequeue() {
+ if (consumer_pos_->dequeue_pos != NULL) {
+ return consumer_pos_->dequeue_pos;
+ } else {
+ if (Acquire_Load(consumer_pos_->dequeue_chunk_poll_pos) != kClear) {
+ // Skip marker.
+ consumer_pos_->dequeue_pos = consumer_pos_->dequeue_chunk_pos + 1;
+ consumer_pos_->dequeue_end_pos =
+ consumer_pos_->dequeue_chunk_pos + chunk_size_;
+ return consumer_pos_->dequeue_pos;
+ } else {
+ return NULL;
+ }
+ }
+}
+
+
+void SamplingCircularQueue::FinishDequeue() {
+ consumer_pos_->dequeue_pos += record_size_;
+ if (consumer_pos_->dequeue_pos < consumer_pos_->dequeue_end_pos) return;
+ // Move to next chunk.
+ consumer_pos_->dequeue_pos = NULL;
+ *consumer_pos_->dequeue_chunk_pos = kClear;
+ consumer_pos_->dequeue_chunk_pos += chunk_size_;
+ WrapPositionIfNeeded(&consumer_pos_->dequeue_chunk_pos);
+ consumer_pos_->dequeue_chunk_poll_pos += chunk_size_;
+ WrapPositionIfNeeded(&consumer_pos_->dequeue_chunk_poll_pos);
+}
+
+
+void SamplingCircularQueue::FlushResidualRecords() {
+ // Eliminate producer / consumer distance.
+ consumer_pos_->dequeue_chunk_poll_pos = consumer_pos_->dequeue_chunk_pos;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/circular-queue.h b/chromium/v8/src/circular-queue.h
new file mode 100644
index 00000000000..4ad4f4b5505
--- /dev/null
+++ b/chromium/v8/src/circular-queue.h
@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef V8_CIRCULAR_QUEUE_H_
+#define V8_CIRCULAR_QUEUE_H_
+
+namespace v8 {
+namespace internal {
+
+
+// Lock-free cache-friendly sampling circular queue for large
+// records. Intended for fast transfer of large records between a
+// single producer and a single consumer. If the queue is full,
+// previous unread records are overwritten. The queue is designed with
+// a goal in mind to evade cache lines thrashing by preventing
+// simultaneous reads and writes to adjanced memory locations.
+//
+// IMPORTANT: as a producer never checks for chunks cleanness, it is
+// possible that it can catch up and overwrite a chunk that a consumer
+// is currently reading, resulting in a corrupt record being read.
+class SamplingCircularQueue {
+ public:
+ // Executed on the application thread.
+ SamplingCircularQueue(size_t record_size_in_bytes,
+ size_t desired_chunk_size_in_bytes,
+ unsigned buffer_size_in_chunks);
+ ~SamplingCircularQueue();
+
+ // Enqueue returns a pointer to a memory location for storing the next
+ // record.
+ INLINE(void* Enqueue());
+
+ // Executed on the consumer (analyzer) thread.
+ // StartDequeue returns a pointer to a memory location for retrieving
+ // the next record. After the record had been read by a consumer,
+ // FinishDequeue must be called. Until that moment, subsequent calls
+ // to StartDequeue will return the same pointer.
+ void* StartDequeue();
+ void FinishDequeue();
+ // Due to a presence of slipping between the producer and the consumer,
+ // the queue must be notified whether producing has been finished in order
+ // to process remaining records from the buffer.
+ void FlushResidualRecords();
+
+ typedef AtomicWord Cell;
+
+ private:
+ // Reserved values for the chunk marker (first Cell in each chunk).
+ enum {
+ kClear, // Marks clean (processed) chunks.
+ kEnqueueStarted // Marks chunks where enqueue started.
+ };
+
+ struct ProducerPosition {
+ Cell* next_chunk_pos;
+ Cell* enqueue_pos;
+ };
+ struct ConsumerPosition {
+ Cell* dequeue_chunk_pos;
+ Cell* dequeue_chunk_poll_pos;
+ Cell* dequeue_pos;
+ Cell* dequeue_end_pos;
+ };
+
+ INLINE(void WrapPositionIfNeeded(Cell** pos));
+
+ const size_t record_size_;
+ const size_t chunk_size_in_bytes_;
+ const size_t chunk_size_;
+ const size_t buffer_size_;
+ Cell* buffer_;
+ byte* positions_;
+ ProducerPosition* producer_pos_;
+ ConsumerPosition* consumer_pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(SamplingCircularQueue);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_CIRCULAR_QUEUE_H_
diff --git a/chromium/v8/src/code-stubs-hydrogen.cc b/chromium/v8/src/code-stubs-hydrogen.cc
new file mode 100644
index 00000000000..4f6db35dd93
--- /dev/null
+++ b/chromium/v8/src/code-stubs-hydrogen.cc
@@ -0,0 +1,907 @@
+// Copyright 2012 the V8 project authors. 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 "code-stubs.h"
+#include "hydrogen.h"
+#include "lithium.h"
+
+namespace v8 {
+namespace internal {
+
+
+static LChunk* OptimizeGraph(HGraph* graph) {
+ DisallowHeapAllocation no_allocation;
+ DisallowHandleAllocation no_handles;
+ DisallowHandleDereference no_deref;
+
+ ASSERT(graph != NULL);
+ BailoutReason bailout_reason = kNoReason;
+ if (!graph->Optimize(&bailout_reason)) {
+ FATAL(GetBailoutReason(bailout_reason));
+ }
+ LChunk* chunk = LChunk::NewChunk(graph);
+ if (chunk == NULL) {
+ FATAL(GetBailoutReason(graph->info()->bailout_reason()));
+ }
+ return chunk;
+}
+
+
+class CodeStubGraphBuilderBase : public HGraphBuilder {
+ public:
+ CodeStubGraphBuilderBase(Isolate* isolate, HydrogenCodeStub* stub)
+ : HGraphBuilder(&info_),
+ arguments_length_(NULL),
+ info_(stub, isolate),
+ context_(NULL) {
+ descriptor_ = stub->GetInterfaceDescriptor(isolate);
+ parameters_.Reset(new HParameter*[descriptor_->register_param_count_]);
+ }
+ virtual bool BuildGraph();
+
+ protected:
+ virtual HValue* BuildCodeStub() = 0;
+ HParameter* GetParameter(int parameter) {
+ ASSERT(parameter < descriptor_->register_param_count_);
+ return parameters_[parameter];
+ }
+ HValue* GetArgumentsLength() {
+ // This is initialized in BuildGraph()
+ ASSERT(arguments_length_ != NULL);
+ return arguments_length_;
+ }
+ CompilationInfo* info() { return &info_; }
+ HydrogenCodeStub* stub() { return info_.code_stub(); }
+ HContext* context() { return context_; }
+ Isolate* isolate() { return info_.isolate(); }
+
+ class ArrayContextChecker {
+ public:
+ ArrayContextChecker(HGraphBuilder* builder, HValue* constructor,
+ HValue* array_function)
+ : checker_(builder) {
+ checker_.If<HCompareObjectEqAndBranch, HValue*>(constructor,
+ array_function);
+ checker_.Then();
+ }
+
+ ~ArrayContextChecker() {
+ checker_.ElseDeopt("Array constructor called from different context");
+ checker_.End();
+ }
+ private:
+ IfBuilder checker_;
+ };
+
+ enum ArgumentClass {
+ NONE,
+ SINGLE,
+ MULTIPLE
+ };
+
+ HValue* BuildArrayConstructor(ElementsKind kind,
+ ContextCheckMode context_mode,
+ AllocationSiteOverrideMode override_mode,
+ ArgumentClass argument_class);
+ HValue* BuildInternalArrayConstructor(ElementsKind kind,
+ ArgumentClass argument_class);
+
+ private:
+ HValue* BuildArraySingleArgumentConstructor(JSArrayBuilder* builder);
+ HValue* BuildArrayNArgumentsConstructor(JSArrayBuilder* builder,
+ ElementsKind kind);
+
+ SmartArrayPointer<HParameter*> parameters_;
+ HValue* arguments_length_;
+ CompilationInfoWithZone info_;
+ CodeStubInterfaceDescriptor* descriptor_;
+ HContext* context_;
+};
+
+
+bool CodeStubGraphBuilderBase::BuildGraph() {
+ // Update the static counter each time a new code stub is generated.
+ isolate()->counters()->code_stubs()->Increment();
+
+ if (FLAG_trace_hydrogen_stubs) {
+ const char* name = CodeStub::MajorName(stub()->MajorKey(), false);
+ PrintF("-----------------------------------------------------------\n");
+ PrintF("Compiling stub %s using hydrogen\n", name);
+ isolate()->GetHTracer()->TraceCompilation(&info_);
+ }
+
+ int param_count = descriptor_->register_param_count_;
+ HEnvironment* start_environment = graph()->start_environment();
+ HBasicBlock* next_block = CreateBasicBlock(start_environment);
+ current_block()->Goto(next_block);
+ next_block->SetJoinId(BailoutId::StubEntry());
+ set_current_block(next_block);
+
+ HConstant* undefined_constant =
+ Add<HConstant>(isolate()->factory()->undefined_value());
+ graph()->set_undefined_constant(undefined_constant);
+
+ for (int i = 0; i < param_count; ++i) {
+ HParameter* param =
+ Add<HParameter>(i, HParameter::REGISTER_PARAMETER);
+ start_environment->Bind(i, param);
+ parameters_[i] = param;
+ }
+
+ HInstruction* stack_parameter_count;
+ if (descriptor_->stack_parameter_count_ != NULL) {
+ ASSERT(descriptor_->environment_length() == (param_count + 1));
+ stack_parameter_count = New<HParameter>(param_count,
+ HParameter::REGISTER_PARAMETER,
+ Representation::Integer32());
+ stack_parameter_count->set_type(HType::Smi());
+ // It's essential to bind this value to the environment in case of deopt.
+ AddInstruction(stack_parameter_count);
+ start_environment->Bind(param_count, stack_parameter_count);
+ arguments_length_ = stack_parameter_count;
+ } else {
+ ASSERT(descriptor_->environment_length() == param_count);
+ stack_parameter_count = graph()->GetConstantMinus1();
+ arguments_length_ = graph()->GetConstant0();
+ }
+
+ context_ = New<HContext>();
+ AddInstruction(context_);
+ start_environment->BindContext(context_);
+
+ Add<HSimulate>(BailoutId::StubEntry());
+
+ NoObservableSideEffectsScope no_effects(this);
+
+ HValue* return_value = BuildCodeStub();
+
+ // We might have extra expressions to pop from the stack in addition to the
+ // arguments above.
+ HInstruction* stack_pop_count = stack_parameter_count;
+ if (descriptor_->function_mode_ == JS_FUNCTION_STUB_MODE) {
+ if (!stack_parameter_count->IsConstant() &&
+ descriptor_->hint_stack_parameter_count_ < 0) {
+ HInstruction* amount = graph()->GetConstant1();
+ stack_pop_count = Add<HAdd>(stack_parameter_count, amount);
+ stack_pop_count->ChangeRepresentation(Representation::Integer32());
+ stack_pop_count->ClearFlag(HValue::kCanOverflow);
+ } else {
+ int count = descriptor_->hint_stack_parameter_count_;
+ stack_pop_count = Add<HConstant>(count);
+ }
+ }
+
+ if (current_block() != NULL) {
+ HReturn* hreturn_instruction = New<HReturn>(return_value,
+ stack_pop_count);
+ current_block()->Finish(hreturn_instruction);
+ set_current_block(NULL);
+ }
+ return true;
+}
+
+
+template <class Stub>
+class CodeStubGraphBuilder: public CodeStubGraphBuilderBase {
+ public:
+ explicit CodeStubGraphBuilder(Stub* stub)
+ : CodeStubGraphBuilderBase(Isolate::Current(), stub) {}
+
+ protected:
+ virtual HValue* BuildCodeStub() {
+ if (casted_stub()->IsUninitialized()) {
+ return BuildCodeUninitializedStub();
+ } else {
+ return BuildCodeInitializedStub();
+ }
+ }
+
+ virtual HValue* BuildCodeInitializedStub() {
+ UNIMPLEMENTED();
+ return NULL;
+ }
+
+ virtual HValue* BuildCodeUninitializedStub() {
+ // Force a deopt that falls back to the runtime.
+ HValue* undefined = graph()->GetConstantUndefined();
+ IfBuilder builder(this);
+ builder.IfNot<HCompareObjectEqAndBranch, HValue*>(undefined, undefined);
+ builder.Then();
+ builder.ElseDeopt("Forced deopt to runtime");
+ return undefined;
+ }
+
+ Stub* casted_stub() { return static_cast<Stub*>(stub()); }
+};
+
+
+Handle<Code> HydrogenCodeStub::GenerateLightweightMissCode(Isolate* isolate) {
+ Factory* factory = isolate->factory();
+
+ // Generate the new code.
+ MacroAssembler masm(isolate, NULL, 256);
+
+ {
+ // Update the static counter each time a new code stub is generated.
+ isolate->counters()->code_stubs()->Increment();
+
+ // 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);
+ GenerateLightweightMiss(&masm);
+ }
+
+ // Create the code object.
+ CodeDesc desc;
+ masm.GetCode(&desc);
+
+ // Copy the generated code into a heap object.
+ Code::Flags flags = Code::ComputeFlags(
+ GetCodeKind(),
+ GetICState(),
+ GetExtraICState(),
+ GetStubType(),
+ GetStubFlags());
+ Handle<Code> new_object = factory->NewCode(
+ desc, flags, masm.CodeObject(), NeedsImmovableCode());
+ return new_object;
+}
+
+
+template <class Stub>
+static Handle<Code> DoGenerateCode(Stub* stub) {
+ Isolate* isolate = Isolate::Current();
+ CodeStub::Major major_key =
+ static_cast<HydrogenCodeStub*>(stub)->MajorKey();
+ CodeStubInterfaceDescriptor* descriptor =
+ isolate->code_stub_interface_descriptor(major_key);
+ if (descriptor->register_param_count_ < 0) {
+ stub->InitializeInterfaceDescriptor(isolate, descriptor);
+ }
+
+ // If we are uninitialized we can use a light-weight stub to enter
+ // the runtime that is significantly faster than using the standard
+ // stub-failure deopt mechanism.
+ if (stub->IsUninitialized() && descriptor->has_miss_handler()) {
+ ASSERT(descriptor->stack_parameter_count_ == NULL);
+ return stub->GenerateLightweightMissCode(isolate);
+ }
+ CodeStubGraphBuilder<Stub> builder(stub);
+ LChunk* chunk = OptimizeGraph(builder.CreateGraph());
+ return chunk->Codegen();
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<ToNumberStub>::BuildCodeStub() {
+ HValue* value = GetParameter(0);
+
+ // Check if the parameter is already a SMI or heap number.
+ IfBuilder if_number(this);
+ if_number.If<HIsSmiAndBranch>(value);
+ if_number.OrIf<HCompareMap>(value, isolate()->factory()->heap_number_map());
+ if_number.Then();
+
+ // Return the number.
+ Push(value);
+
+ if_number.Else();
+
+ // Convert the parameter to number using the builtin.
+ HValue* function = AddLoadJSBuiltin(Builtins::TO_NUMBER);
+ Add<HPushArgument>(value);
+ Push(Add<HInvokeFunction>(function, 1));
+
+ if_number.End();
+
+ return Pop();
+}
+
+
+Handle<Code> ToNumberStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<FastCloneShallowArrayStub>::BuildCodeStub() {
+ Factory* factory = isolate()->factory();
+ HValue* undefined = graph()->GetConstantUndefined();
+ AllocationSiteMode alloc_site_mode = casted_stub()->allocation_site_mode();
+ FastCloneShallowArrayStub::Mode mode = casted_stub()->mode();
+ int length = casted_stub()->length();
+
+ HInstruction* allocation_site = Add<HLoadKeyed>(GetParameter(0),
+ GetParameter(1),
+ static_cast<HValue*>(NULL),
+ FAST_ELEMENTS);
+ IfBuilder checker(this);
+ checker.IfNot<HCompareObjectEqAndBranch, HValue*>(allocation_site,
+ undefined);
+ checker.Then();
+
+ HObjectAccess access = HObjectAccess::ForAllocationSiteTransitionInfo();
+ HInstruction* boilerplate = Add<HLoadNamedField>(allocation_site, access);
+ if (mode == FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS) {
+ HValue* elements = AddLoadElements(boilerplate, NULL);
+
+ IfBuilder if_fixed_cow(this);
+ if_fixed_cow.If<HCompareMap>(elements, factory->fixed_cow_array_map());
+ if_fixed_cow.Then();
+ environment()->Push(BuildCloneShallowArray(boilerplate,
+ allocation_site,
+ alloc_site_mode,
+ FAST_ELEMENTS,
+ 0/*copy-on-write*/));
+ if_fixed_cow.Else();
+
+ IfBuilder if_fixed(this);
+ if_fixed.If<HCompareMap>(elements, factory->fixed_array_map());
+ if_fixed.Then();
+ environment()->Push(BuildCloneShallowArray(boilerplate,
+ allocation_site,
+ alloc_site_mode,
+ FAST_ELEMENTS,
+ length));
+ if_fixed.Else();
+ environment()->Push(BuildCloneShallowArray(boilerplate,
+ allocation_site,
+ alloc_site_mode,
+ FAST_DOUBLE_ELEMENTS,
+ length));
+ } else {
+ ElementsKind elements_kind = casted_stub()->ComputeElementsKind();
+ environment()->Push(BuildCloneShallowArray(boilerplate,
+ allocation_site,
+ alloc_site_mode,
+ elements_kind,
+ length));
+ }
+
+ checker.ElseDeopt("Uninitialized boilerplate literals");
+ checker.End();
+
+ return environment()->Pop();
+}
+
+
+Handle<Code> FastCloneShallowArrayStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<FastCloneShallowObjectStub>::BuildCodeStub() {
+ Zone* zone = this->zone();
+ HValue* undefined = graph()->GetConstantUndefined();
+
+ HInstruction* boilerplate = Add<HLoadKeyed>(GetParameter(0),
+ GetParameter(1),
+ static_cast<HValue*>(NULL),
+ FAST_ELEMENTS);
+
+ IfBuilder checker(this);
+ checker.IfNot<HCompareObjectEqAndBranch, HValue*>(boilerplate,
+ undefined);
+ checker.And();
+
+ int size = JSObject::kHeaderSize + casted_stub()->length() * kPointerSize;
+ HValue* boilerplate_size =
+ AddInstruction(new(zone) HInstanceSize(boilerplate));
+ HValue* size_in_words = Add<HConstant>(size >> kPointerSizeLog2);
+ checker.If<HCompareNumericAndBranch>(boilerplate_size,
+ size_in_words, Token::EQ);
+ checker.Then();
+
+ HValue* size_in_bytes = Add<HConstant>(size);
+
+ HInstruction* object = Add<HAllocate>(size_in_bytes, HType::JSObject(),
+ isolate()->heap()->GetPretenureMode(), JS_OBJECT_TYPE);
+
+ for (int i = 0; i < size; i += kPointerSize) {
+ HObjectAccess access = HObjectAccess::ForJSObjectOffset(i);
+ Add<HStoreNamedField>(object, access,
+ Add<HLoadNamedField>(boilerplate, access));
+ }
+
+ environment()->Push(object);
+ checker.ElseDeopt("Uninitialized boilerplate in fast clone");
+ checker.End();
+
+ return environment()->Pop();
+}
+
+
+Handle<Code> FastCloneShallowObjectStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<CreateAllocationSiteStub>::BuildCodeStub() {
+ HValue* size = Add<HConstant>(AllocationSite::kSize);
+ HInstruction* object = Add<HAllocate>(size, HType::JSObject(), TENURED,
+ JS_OBJECT_TYPE);
+
+ // Store the map
+ Handle<Map> allocation_site_map(isolate()->heap()->allocation_site_map(),
+ isolate());
+ AddStoreMapConstant(object, allocation_site_map);
+
+ // Store the payload (smi elements kind)
+ HValue* initial_elements_kind = Add<HConstant>(GetInitialFastElementsKind());
+ Add<HStoreNamedField>(object,
+ HObjectAccess::ForAllocationSiteTransitionInfo(),
+ initial_elements_kind);
+
+ // Link the object to the allocation site list
+ HValue* site_list = Add<HConstant>(
+ ExternalReference::allocation_sites_list_address(isolate()));
+ HValue* site = Add<HLoadNamedField>(site_list,
+ HObjectAccess::ForAllocationSiteList());
+ HStoreNamedField* store =
+ Add<HStoreNamedField>(object, HObjectAccess::ForAllocationSiteWeakNext(),
+ site);
+ store->SkipWriteBarrier();
+ Add<HStoreNamedField>(site_list, HObjectAccess::ForAllocationSiteList(),
+ object);
+
+ // We use a hammer (SkipWriteBarrier()) to indicate that we know the input
+ // cell is really a Cell, and so no write barrier is needed.
+ // TODO(mvstanton): Add a debug_code check to verify the input cell is really
+ // a cell. (perhaps with a new instruction, HAssert).
+ HInstruction* cell = GetParameter(0);
+ HObjectAccess access = HObjectAccess::ForCellValue();
+ store = Add<HStoreNamedField>(cell, access, object);
+ store->SkipWriteBarrier();
+ return cell;
+}
+
+
+Handle<Code> CreateAllocationSiteStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<KeyedLoadFastElementStub>::BuildCodeStub() {
+ HInstruction* load = BuildUncheckedMonomorphicElementAccess(
+ GetParameter(0), GetParameter(1), NULL, NULL,
+ casted_stub()->is_js_array(), casted_stub()->elements_kind(),
+ false, NEVER_RETURN_HOLE, STANDARD_STORE);
+ return load;
+}
+
+
+Handle<Code> KeyedLoadFastElementStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template<>
+HValue* CodeStubGraphBuilder<LoadFieldStub>::BuildCodeStub() {
+ Representation rep = casted_stub()->representation();
+ HObjectAccess access = casted_stub()->is_inobject() ?
+ HObjectAccess::ForJSObjectOffset(casted_stub()->offset(), rep) :
+ HObjectAccess::ForBackingStoreOffset(casted_stub()->offset(), rep);
+ return AddInstruction(BuildLoadNamedField(GetParameter(0), access, NULL));
+}
+
+
+Handle<Code> LoadFieldStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template<>
+HValue* CodeStubGraphBuilder<KeyedLoadFieldStub>::BuildCodeStub() {
+ Representation rep = casted_stub()->representation();
+ HObjectAccess access = casted_stub()->is_inobject() ?
+ HObjectAccess::ForJSObjectOffset(casted_stub()->offset(), rep) :
+ HObjectAccess::ForBackingStoreOffset(casted_stub()->offset(), rep);
+ return AddInstruction(BuildLoadNamedField(GetParameter(0), access, NULL));
+}
+
+
+Handle<Code> KeyedLoadFieldStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<KeyedStoreFastElementStub>::BuildCodeStub() {
+ BuildUncheckedMonomorphicElementAccess(
+ GetParameter(0), GetParameter(1), GetParameter(2), NULL,
+ casted_stub()->is_js_array(), casted_stub()->elements_kind(),
+ true, NEVER_RETURN_HOLE, casted_stub()->store_mode());
+
+ return GetParameter(2);
+}
+
+
+Handle<Code> KeyedStoreFastElementStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<TransitionElementsKindStub>::BuildCodeStub() {
+ info()->MarkAsSavesCallerDoubles();
+
+ BuildTransitionElementsKind(GetParameter(0),
+ GetParameter(1),
+ casted_stub()->from_kind(),
+ casted_stub()->to_kind(),
+ true);
+
+ return GetParameter(0);
+}
+
+
+Handle<Code> TransitionElementsKindStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+HValue* CodeStubGraphBuilderBase::BuildArrayConstructor(
+ ElementsKind kind,
+ ContextCheckMode context_mode,
+ AllocationSiteOverrideMode override_mode,
+ ArgumentClass argument_class) {
+ HValue* constructor = GetParameter(ArrayConstructorStubBase::kConstructor);
+ if (context_mode == CONTEXT_CHECK_REQUIRED) {
+ HInstruction* array_function = BuildGetArrayFunction();
+ ArrayContextChecker checker(this, constructor, array_function);
+ }
+
+ HValue* property_cell = GetParameter(ArrayConstructorStubBase::kPropertyCell);
+ // Walk through the property cell to the AllocationSite
+ HValue* alloc_site = Add<HLoadNamedField>(property_cell,
+ HObjectAccess::ForCellValue());
+ JSArrayBuilder array_builder(this, kind, alloc_site, constructor,
+ override_mode);
+ HValue* result = NULL;
+ switch (argument_class) {
+ case NONE:
+ result = array_builder.AllocateEmptyArray();
+ break;
+ case SINGLE:
+ result = BuildArraySingleArgumentConstructor(&array_builder);
+ break;
+ case MULTIPLE:
+ result = BuildArrayNArgumentsConstructor(&array_builder, kind);
+ break;
+ }
+
+ return result;
+}
+
+
+HValue* CodeStubGraphBuilderBase::BuildInternalArrayConstructor(
+ ElementsKind kind, ArgumentClass argument_class) {
+ HValue* constructor = GetParameter(
+ InternalArrayConstructorStubBase::kConstructor);
+ JSArrayBuilder array_builder(this, kind, constructor);
+
+ HValue* result = NULL;
+ switch (argument_class) {
+ case NONE:
+ result = array_builder.AllocateEmptyArray();
+ break;
+ case SINGLE:
+ result = BuildArraySingleArgumentConstructor(&array_builder);
+ break;
+ case MULTIPLE:
+ result = BuildArrayNArgumentsConstructor(&array_builder, kind);
+ break;
+ }
+ return result;
+}
+
+
+HValue* CodeStubGraphBuilderBase::BuildArraySingleArgumentConstructor(
+ JSArrayBuilder* array_builder) {
+ // Smi check and range check on the input arg.
+ HValue* constant_one = graph()->GetConstant1();
+ HValue* constant_zero = graph()->GetConstant0();
+
+ HInstruction* elements = Add<HArgumentsElements>(false);
+ HInstruction* argument = AddInstruction(
+ new(zone()) HAccessArgumentsAt(elements, constant_one, constant_zero));
+
+ HConstant* max_alloc_length =
+ Add<HConstant>(JSObject::kInitialMaxFastElementArray);
+ const int initial_capacity = JSArray::kPreallocatedArrayElements;
+ HConstant* initial_capacity_node = New<HConstant>(initial_capacity);
+ AddInstruction(initial_capacity_node);
+
+ HInstruction* checked_arg = Add<HBoundsCheck>(argument, max_alloc_length);
+ IfBuilder if_builder(this);
+ if_builder.If<HCompareNumericAndBranch>(checked_arg, constant_zero,
+ Token::EQ);
+ if_builder.Then();
+ Push(initial_capacity_node); // capacity
+ Push(constant_zero); // length
+ if_builder.Else();
+ Push(checked_arg); // capacity
+ Push(checked_arg); // length
+ if_builder.End();
+
+ // Figure out total size
+ HValue* length = Pop();
+ HValue* capacity = Pop();
+ return array_builder->AllocateArray(capacity, length, true);
+}
+
+
+HValue* CodeStubGraphBuilderBase::BuildArrayNArgumentsConstructor(
+ JSArrayBuilder* array_builder, ElementsKind kind) {
+ // We need to fill with the hole if it's a smi array in the multi-argument
+ // case because we might have to bail out while copying arguments into
+ // the array because they aren't compatible with a smi array.
+ // If it's a double array, no problem, and if it's fast then no
+ // problem either because doubles are boxed.
+ HValue* length = GetArgumentsLength();
+ bool fill_with_hole = IsFastSmiElementsKind(kind);
+ HValue* new_object = array_builder->AllocateArray(length,
+ length,
+ fill_with_hole);
+ HValue* elements = array_builder->GetElementsLocation();
+ ASSERT(elements != NULL);
+
+ // Now populate the elements correctly.
+ LoopBuilder builder(this,
+ context(),
+ LoopBuilder::kPostIncrement);
+ HValue* start = graph()->GetConstant0();
+ HValue* key = builder.BeginBody(start, length, Token::LT);
+ HInstruction* argument_elements = Add<HArgumentsElements>(false);
+ HInstruction* argument = AddInstruction(new(zone()) HAccessArgumentsAt(
+ argument_elements, length, key));
+
+ Add<HStoreKeyed>(elements, key, argument, kind);
+ builder.EndBody();
+ return new_object;
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<ArrayNoArgumentConstructorStub>::BuildCodeStub() {
+ ElementsKind kind = casted_stub()->elements_kind();
+ ContextCheckMode context_mode = casted_stub()->context_mode();
+ AllocationSiteOverrideMode override_mode = casted_stub()->override_mode();
+ return BuildArrayConstructor(kind, context_mode, override_mode, NONE);
+}
+
+
+Handle<Code> ArrayNoArgumentConstructorStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<ArraySingleArgumentConstructorStub>::
+ BuildCodeStub() {
+ ElementsKind kind = casted_stub()->elements_kind();
+ ContextCheckMode context_mode = casted_stub()->context_mode();
+ AllocationSiteOverrideMode override_mode = casted_stub()->override_mode();
+ return BuildArrayConstructor(kind, context_mode, override_mode, SINGLE);
+}
+
+
+Handle<Code> ArraySingleArgumentConstructorStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<ArrayNArgumentsConstructorStub>::BuildCodeStub() {
+ ElementsKind kind = casted_stub()->elements_kind();
+ ContextCheckMode context_mode = casted_stub()->context_mode();
+ AllocationSiteOverrideMode override_mode = casted_stub()->override_mode();
+ return BuildArrayConstructor(kind, context_mode, override_mode, MULTIPLE);
+}
+
+
+Handle<Code> ArrayNArgumentsConstructorStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<InternalArrayNoArgumentConstructorStub>::
+ BuildCodeStub() {
+ ElementsKind kind = casted_stub()->elements_kind();
+ return BuildInternalArrayConstructor(kind, NONE);
+}
+
+
+Handle<Code> InternalArrayNoArgumentConstructorStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<InternalArraySingleArgumentConstructorStub>::
+ BuildCodeStub() {
+ ElementsKind kind = casted_stub()->elements_kind();
+ return BuildInternalArrayConstructor(kind, SINGLE);
+}
+
+
+Handle<Code> InternalArraySingleArgumentConstructorStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<InternalArrayNArgumentsConstructorStub>::
+ BuildCodeStub() {
+ ElementsKind kind = casted_stub()->elements_kind();
+ return BuildInternalArrayConstructor(kind, MULTIPLE);
+}
+
+
+Handle<Code> InternalArrayNArgumentsConstructorStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<CompareNilICStub>::BuildCodeInitializedStub() {
+ Isolate* isolate = graph()->isolate();
+ CompareNilICStub* stub = casted_stub();
+ HIfContinuation continuation;
+ Handle<Map> sentinel_map(isolate->heap()->meta_map());
+ Handle<Type> type = stub->GetType(isolate, sentinel_map);
+ BuildCompareNil(GetParameter(0), type, RelocInfo::kNoPosition, &continuation);
+ IfBuilder if_nil(this, &continuation);
+ if_nil.Then();
+ if (continuation.IsFalseReachable()) {
+ if_nil.Else();
+ if_nil.Return(graph()->GetConstant0());
+ }
+ if_nil.End();
+ return continuation.IsTrueReachable()
+ ? graph()->GetConstant1()
+ : graph()->GetConstantUndefined();
+}
+
+
+Handle<Code> CompareNilICStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<ToBooleanStub>::BuildCodeInitializedStub() {
+ ToBooleanStub* stub = casted_stub();
+
+ IfBuilder if_true(this);
+ if_true.If<HBranch>(GetParameter(0), stub->GetTypes());
+ if_true.Then();
+ if_true.Return(graph()->GetConstant1());
+ if_true.Else();
+ if_true.End();
+ return graph()->GetConstant0();
+}
+
+
+Handle<Code> ToBooleanStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template <>
+HValue* CodeStubGraphBuilder<StoreGlobalStub>::BuildCodeInitializedStub() {
+ StoreGlobalStub* stub = casted_stub();
+ Handle<Object> hole(isolate()->heap()->the_hole_value(), isolate());
+ Handle<Object> placeholer_value(Smi::FromInt(0), isolate());
+ Handle<PropertyCell> placeholder_cell =
+ isolate()->factory()->NewPropertyCell(placeholer_value);
+
+ HParameter* receiver = GetParameter(0);
+ HParameter* value = GetParameter(2);
+
+ // Check that the map of the global has not changed: use a placeholder map
+ // that will be replaced later with the global object's map.
+ Handle<Map> placeholder_map = isolate()->factory()->meta_map();
+ Add<HCheckMaps>(receiver, placeholder_map, top_info());
+
+ HValue* cell = Add<HConstant>(placeholder_cell);
+ HObjectAccess access(HObjectAccess::ForCellPayload(isolate()));
+ HValue* cell_contents = Add<HLoadNamedField>(cell, access);
+
+ if (stub->is_constant()) {
+ IfBuilder builder(this);
+ builder.If<HCompareObjectEqAndBranch>(cell_contents, value);
+ builder.Then();
+ builder.ElseDeopt("Unexpected cell contents in constant global store");
+ builder.End();
+ } else {
+ // Load the payload of the global parameter cell. A hole indicates that the
+ // property has been deleted and that the store must be handled by the
+ // runtime.
+ IfBuilder builder(this);
+ HValue* hole_value = Add<HConstant>(hole);
+ builder.If<HCompareObjectEqAndBranch>(cell_contents, hole_value);
+ builder.Then();
+ builder.Deopt("Unexpected cell contents in global store");
+ builder.Else();
+ Add<HStoreNamedField>(cell, access, value);
+ builder.End();
+ }
+
+ return value;
+}
+
+
+Handle<Code> StoreGlobalStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+template<>
+HValue* CodeStubGraphBuilder<ElementsTransitionAndStoreStub>::BuildCodeStub() {
+ HValue* value = GetParameter(0);
+ HValue* map = GetParameter(1);
+ HValue* key = GetParameter(2);
+ HValue* object = GetParameter(3);
+
+ if (FLAG_trace_elements_transitions) {
+ // Tracing elements transitions is the job of the runtime.
+ Add<HDeoptimize>("Deopt due to --trace-elements-transitions",
+ Deoptimizer::EAGER);
+ } else {
+ info()->MarkAsSavesCallerDoubles();
+
+ BuildTransitionElementsKind(object, map,
+ casted_stub()->from_kind(),
+ casted_stub()->to_kind(),
+ casted_stub()->is_jsarray());
+
+ BuildUncheckedMonomorphicElementAccess(object, key, value, NULL,
+ casted_stub()->is_jsarray(),
+ casted_stub()->to_kind(),
+ true, ALLOW_RETURN_HOLE,
+ casted_stub()->store_mode());
+ }
+
+ return value;
+}
+
+
+Handle<Code> ElementsTransitionAndStoreStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/code-stubs.cc b/chromium/v8/src/code-stubs.cc
new file mode 100644
index 00000000000..f656bf7d96b
--- /dev/null
+++ b/chromium/v8/src/code-stubs.cc
@@ -0,0 +1,808 @@
+// Copyright 2012 the V8 project authors. 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 "bootstrapper.h"
+#include "code-stubs.h"
+#include "cpu-profiler.h"
+#include "stub-cache.h"
+#include "factory.h"
+#include "gdb-jit.h"
+#include "macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+
+CodeStubInterfaceDescriptor::CodeStubInterfaceDescriptor()
+ : register_param_count_(-1),
+ stack_parameter_count_(NULL),
+ hint_stack_parameter_count_(-1),
+ function_mode_(NOT_JS_FUNCTION_STUB_MODE),
+ register_params_(NULL),
+ deoptimization_handler_(NULL),
+ miss_handler_(IC_Utility(IC::kUnreachable), Isolate::Current()),
+ has_miss_handler_(false) { }
+
+
+bool CodeStub::FindCodeInCache(Code** code_out, Isolate* isolate) {
+ UnseededNumberDictionary* stubs = isolate->heap()->code_stubs();
+ int index = stubs->FindEntry(GetKey());
+ if (index != UnseededNumberDictionary::kNotFound) {
+ *code_out = Code::cast(stubs->ValueAt(index));
+ return true;
+ }
+ return false;
+}
+
+
+SmartArrayPointer<const char> CodeStub::GetName() {
+ char buffer[100];
+ NoAllocationStringAllocator allocator(buffer,
+ static_cast<unsigned>(sizeof(buffer)));
+ StringStream stream(&allocator);
+ PrintName(&stream);
+ return stream.ToCString();
+}
+
+
+void CodeStub::RecordCodeGeneration(Code* code, Isolate* isolate) {
+ SmartArrayPointer<const char> name = GetName();
+ PROFILE(isolate, CodeCreateEvent(Logger::STUB_TAG, code, *name));
+ GDBJIT(AddCode(GDBJITInterface::STUB, *name, code));
+ Counters* counters = isolate->counters();
+ counters->total_stubs_code_size()->Increment(code->instruction_size());
+}
+
+
+Code::Kind CodeStub::GetCodeKind() const {
+ return Code::STUB;
+}
+
+
+Handle<Code> CodeStub::GetCodeCopyFromTemplate(Isolate* isolate) {
+ Handle<Code> ic = GetCode(isolate);
+ ic = isolate->factory()->CopyCode(ic);
+ RecordCodeGeneration(*ic, isolate);
+ return ic;
+}
+
+
+Handle<Code> PlatformCodeStub::GenerateCode() {
+ Isolate* isolate = Isolate::Current();
+ Factory* factory = isolate->factory();
+
+ // Generate the new code.
+ MacroAssembler masm(isolate, NULL, 256);
+
+ {
+ // Update the static counter each time a new code stub is generated.
+ isolate->counters()->code_stubs()->Increment();
+
+ // 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);
+ }
+
+ // Create the code object.
+ CodeDesc desc;
+ masm.GetCode(&desc);
+
+ // Copy the generated code into a heap object.
+ Code::Flags flags = Code::ComputeFlags(
+ GetCodeKind(),
+ GetICState(),
+ GetExtraICState(),
+ GetStubType(),
+ GetStubFlags());
+ Handle<Code> new_object = factory->NewCode(
+ desc, flags, masm.CodeObject(), NeedsImmovableCode());
+ return new_object;
+}
+
+
+Handle<Code> CodeStub::GetCode(Isolate* isolate) {
+ Factory* factory = isolate->factory();
+ Heap* heap = isolate->heap();
+ Code* code;
+ if (UseSpecialCache()
+ ? FindCodeInSpecialCache(&code, isolate)
+ : FindCodeInCache(&code, isolate)) {
+ ASSERT(IsPregenerated() == code->is_pregenerated());
+ return Handle<Code>(code);
+ }
+
+ {
+ HandleScope scope(isolate);
+
+ Handle<Code> new_object = GenerateCode();
+ new_object->set_major_key(MajorKey());
+ FinishCode(new_object);
+ RecordCodeGeneration(*new_object, isolate);
+
+#ifdef ENABLE_DISASSEMBLER
+ if (FLAG_print_code_stubs) {
+ new_object->Disassemble(*GetName());
+ PrintF("\n");
+ }
+#endif
+
+ 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) ||
+ heap->code_space()->FirstPage()->Contains(code->address()));
+ return Handle<Code>(code, isolate);
+}
+
+
+const char* CodeStub::MajorName(CodeStub::Major major_key,
+ bool allow_unknown_keys) {
+ switch (major_key) {
+#define DEF_CASE(name) case name: return #name "Stub";
+ CODE_STUB_LIST(DEF_CASE)
+#undef DEF_CASE
+ default:
+ if (!allow_unknown_keys) {
+ UNREACHABLE();
+ }
+ return NULL;
+ }
+}
+
+
+void CodeStub::PrintBaseName(StringStream* stream) {
+ stream->Add("%s", MajorName(MajorKey(), false));
+}
+
+
+void CodeStub::PrintName(StringStream* stream) {
+ PrintBaseName(stream);
+ PrintState(stream);
+}
+
+
+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);
+
+ BinaryOpIC::TypeInfo operands_type = Max(left_type_, right_type_);
+ if (left_type_ == BinaryOpIC::ODDBALL && right_type_ == BinaryOpIC::ODDBALL) {
+ // The OddballStub handles a number and an oddball, not two oddballs.
+ operands_type = BinaryOpIC::GENERIC;
+ }
+ switch (operands_type) {
+ case BinaryOpIC::UNINITIALIZED:
+ GenerateTypeTransition(masm);
+ break;
+ case BinaryOpIC::SMI:
+ GenerateSmiStub(masm);
+ break;
+ case BinaryOpIC::INT32:
+ GenerateInt32Stub(masm);
+ break;
+ case BinaryOpIC::NUMBER:
+ GenerateNumberStub(masm);
+ break;
+ case BinaryOpIC::ODDBALL:
+ GenerateOddballStub(masm);
+ break;
+ case BinaryOpIC::STRING:
+ GenerateStringStub(masm);
+ break;
+ case BinaryOpIC::GENERIC:
+ GenerateGeneric(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+#define __ ACCESS_MASM(masm)
+
+
+void BinaryOpStub::GenerateCallRuntime(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::ADD:
+ __ InvokeBuiltin(Builtins::ADD, CALL_FUNCTION);
+ break;
+ case Token::SUB:
+ __ InvokeBuiltin(Builtins::SUB, CALL_FUNCTION);
+ break;
+ case Token::MUL:
+ __ InvokeBuiltin(Builtins::MUL, CALL_FUNCTION);
+ break;
+ case Token::DIV:
+ __ InvokeBuiltin(Builtins::DIV, CALL_FUNCTION);
+ break;
+ case Token::MOD:
+ __ InvokeBuiltin(Builtins::MOD, CALL_FUNCTION);
+ break;
+ case Token::BIT_OR:
+ __ InvokeBuiltin(Builtins::BIT_OR, CALL_FUNCTION);
+ break;
+ case Token::BIT_AND:
+ __ InvokeBuiltin(Builtins::BIT_AND, CALL_FUNCTION);
+ break;
+ case Token::BIT_XOR:
+ __ InvokeBuiltin(Builtins::BIT_XOR, CALL_FUNCTION);
+ break;
+ case Token::SAR:
+ __ InvokeBuiltin(Builtins::SAR, CALL_FUNCTION);
+ break;
+ case Token::SHR:
+ __ InvokeBuiltin(Builtins::SHR, CALL_FUNCTION);
+ break;
+ case Token::SHL:
+ __ InvokeBuiltin(Builtins::SHL, CALL_FUNCTION);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+#undef __
+
+
+void BinaryOpStub::PrintName(StringStream* stream) {
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name;
+ switch (mode_) {
+ case NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
+ case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
+ default: overwrite_name = "UnknownOverwrite"; break;
+ }
+ stream->Add("BinaryOpStub_%s_%s_%s+%s",
+ op_name,
+ overwrite_name,
+ BinaryOpIC::GetName(left_type_),
+ BinaryOpIC::GetName(right_type_));
+}
+
+
+void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
+ ASSERT(left_type_ == BinaryOpIC::STRING || right_type_ == BinaryOpIC::STRING);
+ ASSERT(op_ == Token::ADD);
+ if (left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING) {
+ GenerateBothStringStub(masm);
+ return;
+ }
+ // Try to add arguments as strings, otherwise, transition to the generic
+ // BinaryOpIC type.
+ GenerateAddStrings(masm);
+ GenerateTypeTransition(masm);
+}
+
+
+InlineCacheState ICCompareStub::GetICState() {
+ CompareIC::State state = Max(left_, right_);
+ switch (state) {
+ case CompareIC::UNINITIALIZED:
+ return ::v8::internal::UNINITIALIZED;
+ case CompareIC::SMI:
+ case CompareIC::NUMBER:
+ case CompareIC::INTERNALIZED_STRING:
+ case CompareIC::STRING:
+ case CompareIC::UNIQUE_NAME:
+ case CompareIC::OBJECT:
+ case CompareIC::KNOWN_OBJECT:
+ return MONOMORPHIC;
+ case CompareIC::GENERIC:
+ return ::v8::internal::GENERIC;
+ }
+ UNREACHABLE();
+ return ::v8::internal::UNINITIALIZED;
+}
+
+
+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_,
+ strict() ?
+ factory->strict_compare_ic_string() :
+ factory->compare_ic_string(),
+ new_object);
+}
+
+
+bool ICCompareStub::FindCodeInSpecialCache(Code** code_out, Isolate* isolate) {
+ Factory* factory = isolate->factory();
+ Code::Flags flags = Code::ComputeFlags(
+ GetCodeKind(),
+ UNINITIALIZED);
+ ASSERT(op_ == Token::EQ || op_ == Token::EQ_STRICT);
+ Handle<Object> probe(
+ known_map_->FindInCodeCache(
+ strict() ?
+ *factory->strict_compare_ic_string() :
+ *factory->compare_ic_string(),
+ flags),
+ isolate);
+ if (probe->IsCode()) {
+ *code_out = Code::cast(*probe);
+#ifdef DEBUG
+ Token::Value cached_op;
+ ICCompareStub::DecodeMinorKey((*code_out)->stub_info(), NULL, NULL, NULL,
+ &cached_op);
+ ASSERT(op_ == cached_op);
+#endif
+ return true;
+ }
+ return false;
+}
+
+
+int ICCompareStub::MinorKey() {
+ return OpField::encode(op_ - Token::EQ) |
+ LeftStateField::encode(left_) |
+ RightStateField::encode(right_) |
+ HandlerStateField::encode(state_);
+}
+
+
+void ICCompareStub::DecodeMinorKey(int minor_key,
+ CompareIC::State* left_state,
+ CompareIC::State* right_state,
+ CompareIC::State* handler_state,
+ Token::Value* op) {
+ if (left_state) {
+ *left_state =
+ static_cast<CompareIC::State>(LeftStateField::decode(minor_key));
+ }
+ if (right_state) {
+ *right_state =
+ static_cast<CompareIC::State>(RightStateField::decode(minor_key));
+ }
+ if (handler_state) {
+ *handler_state =
+ static_cast<CompareIC::State>(HandlerStateField::decode(minor_key));
+ }
+ if (op) {
+ *op = static_cast<Token::Value>(OpField::decode(minor_key) + Token::EQ);
+ }
+}
+
+
+void ICCompareStub::Generate(MacroAssembler* masm) {
+ switch (state_) {
+ case CompareIC::UNINITIALIZED:
+ GenerateMiss(masm);
+ break;
+ case CompareIC::SMI:
+ GenerateSmis(masm);
+ break;
+ case CompareIC::NUMBER:
+ GenerateNumbers(masm);
+ break;
+ case CompareIC::STRING:
+ GenerateStrings(masm);
+ break;
+ case CompareIC::INTERNALIZED_STRING:
+ GenerateInternalizedStrings(masm);
+ break;
+ case CompareIC::UNIQUE_NAME:
+ GenerateUniqueNames(masm);
+ break;
+ case CompareIC::OBJECT:
+ GenerateObjects(masm);
+ break;
+ case CompareIC::KNOWN_OBJECT:
+ ASSERT(*known_map_ != NULL);
+ GenerateKnownObjects(masm);
+ break;
+ case CompareIC::GENERIC:
+ GenerateGeneric(masm);
+ break;
+ }
+}
+
+
+void CompareNilICStub::UpdateStatus(Handle<Object> object) {
+ ASSERT(!state_.Contains(GENERIC));
+ State old_state(state_);
+ if (object->IsNull()) {
+ state_.Add(NULL_TYPE);
+ } else if (object->IsUndefined()) {
+ state_.Add(UNDEFINED);
+ } else if (object->IsUndetectableObject() ||
+ object->IsOddball() ||
+ !object->IsHeapObject()) {
+ state_.RemoveAll();
+ state_.Add(GENERIC);
+ } else if (IsMonomorphic()) {
+ state_.RemoveAll();
+ state_.Add(GENERIC);
+ } else {
+ state_.Add(MONOMORPHIC_MAP);
+ }
+ TraceTransition(old_state, state_);
+}
+
+
+template<class StateType>
+void HydrogenCodeStub::TraceTransition(StateType from, StateType to) {
+ // Note: Although a no-op transition is semantically OK, it is hinting at a
+ // bug somewhere in our state transition machinery.
+ ASSERT(from != to);
+ #ifdef DEBUG
+ if (!FLAG_trace_ic) return;
+ char buffer[100];
+ NoAllocationStringAllocator allocator(buffer,
+ static_cast<unsigned>(sizeof(buffer)));
+ StringStream stream(&allocator);
+ stream.Add("[");
+ PrintBaseName(&stream);
+ stream.Add(": ");
+ from.Print(&stream);
+ stream.Add("=>");
+ to.Print(&stream);
+ stream.Add("]\n");
+ stream.OutputToStdOut();
+ #endif
+}
+
+
+void CompareNilICStub::PrintBaseName(StringStream* stream) {
+ CodeStub::PrintBaseName(stream);
+ stream->Add((nil_value_ == kNullValue) ? "(NullValue)":
+ "(UndefinedValue)");
+}
+
+
+void CompareNilICStub::PrintState(StringStream* stream) {
+ state_.Print(stream);
+}
+
+
+void CompareNilICStub::State::Print(StringStream* stream) const {
+ stream->Add("(");
+ SimpleListPrinter printer(stream);
+ if (IsEmpty()) printer.Add("None");
+ if (Contains(UNDEFINED)) printer.Add("Undefined");
+ if (Contains(NULL_TYPE)) printer.Add("Null");
+ if (Contains(MONOMORPHIC_MAP)) printer.Add("MonomorphicMap");
+ if (Contains(GENERIC)) printer.Add("Generic");
+ stream->Add(")");
+}
+
+
+Handle<Type> CompareNilICStub::GetType(
+ Isolate* isolate,
+ Handle<Map> map) {
+ if (state_.Contains(CompareNilICStub::GENERIC)) {
+ return handle(Type::Any(), isolate);
+ }
+
+ Handle<Type> result(Type::None(), isolate);
+ if (state_.Contains(CompareNilICStub::UNDEFINED)) {
+ result = handle(Type::Union(result, handle(Type::Undefined(), isolate)),
+ isolate);
+ }
+ if (state_.Contains(CompareNilICStub::NULL_TYPE)) {
+ result = handle(Type::Union(result, handle(Type::Null(), isolate)),
+ isolate);
+ }
+ if (state_.Contains(CompareNilICStub::MONOMORPHIC_MAP)) {
+ Type* type = map.is_null() ? Type::Detectable() : Type::Class(map);
+ result = handle(Type::Union(result, handle(type, isolate)), isolate);
+ }
+
+ return result;
+}
+
+
+Handle<Type> CompareNilICStub::GetInputType(
+ Isolate* isolate,
+ Handle<Map> map) {
+ Handle<Type> output_type = GetType(isolate, map);
+ Handle<Type> nil_type = handle(nil_value_ == kNullValue
+ ? Type::Null() : Type::Undefined(), isolate);
+ return handle(Type::Union(output_type, nil_type), isolate);
+}
+
+
+void InstanceofStub::PrintName(StringStream* stream) {
+ const char* args = "";
+ if (HasArgsInRegisters()) {
+ args = "_REGS";
+ }
+
+ const char* inline_check = "";
+ if (HasCallSiteInlineCheck()) {
+ inline_check = "_INLINE";
+ }
+
+ const char* return_true_false_object = "";
+ if (ReturnTrueFalseObject()) {
+ return_true_false_object = "_TRUEFALSE";
+ }
+
+ stream->Add("InstanceofStub%s%s%s",
+ args,
+ inline_check,
+ return_true_false_object);
+}
+
+
+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 KeyedLoadDictionaryElementStub::Generate(MacroAssembler* masm) {
+ KeyedLoadStubCompiler::GenerateLoadDictionaryElement(masm);
+}
+
+
+void CreateAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) {
+ CreateAllocationSiteStub stub;
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+}
+
+
+void KeyedStoreElementStub::Generate(MacroAssembler* masm) {
+ switch (elements_kind_) {
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS: {
+ KeyedStoreStubCompiler::GenerateStoreFastElement(masm,
+ is_js_array_,
+ elements_kind_,
+ store_mode_);
+ }
+ break;
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm,
+ is_js_array_,
+ store_mode_);
+ 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:
+ KeyedStoreStubCompiler::GenerateStoreExternalArray(masm, elements_kind_);
+ break;
+ case DICTIONARY_ELEMENTS:
+ KeyedStoreStubCompiler::GenerateStoreDictionaryElement(masm);
+ break;
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void ArgumentsAccessStub::PrintName(StringStream* stream) {
+ stream->Add("ArgumentsAccessStub_");
+ switch (type_) {
+ 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;
+ }
+}
+
+
+void CallFunctionStub::PrintName(StringStream* stream) {
+ 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");
+}
+
+
+bool ToBooleanStub::UpdateStatus(Handle<Object> object) {
+ Types old_types(types_);
+ bool to_boolean_value = types_.UpdateStatus(object);
+ TraceTransition(old_types, types_);
+ return to_boolean_value;
+}
+
+
+void ToBooleanStub::PrintState(StringStream* stream) {
+ types_.Print(stream);
+}
+
+
+void ToBooleanStub::Types::Print(StringStream* stream) const {
+ stream->Add("(");
+ SimpleListPrinter printer(stream);
+ if (IsEmpty()) printer.Add("None");
+ if (Contains(UNDEFINED)) printer.Add("Undefined");
+ if (Contains(BOOLEAN)) printer.Add("Bool");
+ if (Contains(NULL_TYPE)) printer.Add("Null");
+ if (Contains(SMI)) printer.Add("Smi");
+ if (Contains(SPEC_OBJECT)) printer.Add("SpecObject");
+ if (Contains(STRING)) printer.Add("String");
+ if (Contains(SYMBOL)) printer.Add("Symbol");
+ if (Contains(HEAP_NUMBER)) printer.Add("HeapNumber");
+ stream->Add(")");
+}
+
+
+bool ToBooleanStub::Types::UpdateStatus(Handle<Object> object) {
+ if (object->IsUndefined()) {
+ Add(UNDEFINED);
+ return false;
+ } else if (object->IsBoolean()) {
+ Add(BOOLEAN);
+ return object->IsTrue();
+ } else if (object->IsNull()) {
+ Add(NULL_TYPE);
+ return false;
+ } else if (object->IsSmi()) {
+ Add(SMI);
+ return Smi::cast(*object)->value() != 0;
+ } else if (object->IsSpecObject()) {
+ Add(SPEC_OBJECT);
+ return !object->IsUndetectableObject();
+ } else if (object->IsString()) {
+ Add(STRING);
+ return !object->IsUndetectableObject() &&
+ String::cast(*object)->length() != 0;
+ } else if (object->IsSymbol()) {
+ Add(SYMBOL);
+ return true;
+ } else if (object->IsHeapNumber()) {
+ ASSERT(!object->IsUndetectableObject());
+ Add(HEAP_NUMBER);
+ double value = HeapNumber::cast(*object)->value();
+ return value != 0 && !std::isnan(value);
+ } else {
+ // We should never see an internal object at runtime here!
+ UNREACHABLE();
+ return true;
+ }
+}
+
+
+bool ToBooleanStub::Types::NeedsMap() const {
+ return Contains(ToBooleanStub::SPEC_OBJECT)
+ || Contains(ToBooleanStub::STRING)
+ || Contains(ToBooleanStub::SYMBOL)
+ || Contains(ToBooleanStub::HEAP_NUMBER);
+}
+
+
+bool ToBooleanStub::Types::CanBeUndetectable() const {
+ return Contains(ToBooleanStub::SPEC_OBJECT)
+ || Contains(ToBooleanStub::STRING);
+}
+
+
+void StubFailureTrampolineStub::GenerateAheadOfTime(Isolate* isolate) {
+ StubFailureTrampolineStub stub1(NOT_JS_FUNCTION_STUB_MODE);
+ StubFailureTrampolineStub stub2(JS_FUNCTION_STUB_MODE);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ stub2.GetCode(isolate)->set_is_pregenerated(true);
+}
+
+
+void ProfileEntryHookStub::EntryHookTrampoline(intptr_t function,
+ intptr_t stack_pointer) {
+ FunctionEntryHook entry_hook = Isolate::Current()->function_entry_hook();
+ ASSERT(entry_hook != NULL);
+ entry_hook(function, stack_pointer);
+}
+
+
+static void InstallDescriptor(Isolate* isolate, HydrogenCodeStub* stub) {
+ int major_key = stub->MajorKey();
+ CodeStubInterfaceDescriptor* descriptor =
+ isolate->code_stub_interface_descriptor(major_key);
+ if (!descriptor->initialized()) {
+ stub->InitializeInterfaceDescriptor(isolate, descriptor);
+ }
+}
+
+
+void ArrayConstructorStubBase::InstallDescriptors(Isolate* isolate) {
+ ArrayNoArgumentConstructorStub stub1(GetInitialFastElementsKind());
+ InstallDescriptor(isolate, &stub1);
+ ArraySingleArgumentConstructorStub stub2(GetInitialFastElementsKind());
+ InstallDescriptor(isolate, &stub2);
+ ArrayNArgumentsConstructorStub stub3(GetInitialFastElementsKind());
+ InstallDescriptor(isolate, &stub3);
+}
+
+
+ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate)
+ : argument_count_(ANY) {
+ ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
+}
+
+
+ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate,
+ int argument_count) {
+ if (argument_count == 0) {
+ argument_count_ = NONE;
+ } else if (argument_count == 1) {
+ argument_count_ = ONE;
+ } else if (argument_count >= 2) {
+ argument_count_ = MORE_THAN_ONE;
+ } else {
+ UNREACHABLE();
+ }
+ ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
+}
+
+
+void InternalArrayConstructorStubBase::InstallDescriptors(Isolate* isolate) {
+ InternalArrayNoArgumentConstructorStub stub1(FAST_ELEMENTS);
+ InstallDescriptor(isolate, &stub1);
+ InternalArraySingleArgumentConstructorStub stub2(FAST_ELEMENTS);
+ InstallDescriptor(isolate, &stub2);
+ InternalArrayNArgumentsConstructorStub stub3(FAST_ELEMENTS);
+ InstallDescriptor(isolate, &stub3);
+}
+
+InternalArrayConstructorStub::InternalArrayConstructorStub(
+ Isolate* isolate) {
+ InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/code-stubs.h b/chromium/v8/src/code-stubs.h
new file mode 100644
index 00000000000..c58acd6b16d
--- /dev/null
+++ b/chromium/v8/src/code-stubs.h
@@ -0,0 +1,2316 @@
+// Copyright 2012 the V8 project authors. 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_CODE_STUBS_H_
+#define V8_CODE_STUBS_H_
+
+#include "allocation.h"
+#include "assembler.h"
+#include "globals.h"
+#include "codegen.h"
+
+namespace v8 {
+namespace internal {
+
+// List of code stubs used on all platforms.
+#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
+ V(CallFunction) \
+ V(CallConstruct) \
+ V(BinaryOp) \
+ V(StringAdd) \
+ V(SubString) \
+ V(StringCompare) \
+ V(Compare) \
+ V(CompareIC) \
+ V(CompareNilIC) \
+ V(MathPow) \
+ V(StringLength) \
+ V(FunctionPrototype) \
+ V(StoreArrayLength) \
+ V(RecordWrite) \
+ V(StoreBufferOverflow) \
+ V(RegExpExec) \
+ V(TranscendentalCache) \
+ V(Instanceof) \
+ V(ConvertToDouble) \
+ V(WriteInt32ToHeapNumber) \
+ V(StackCheck) \
+ V(Interrupt) \
+ V(FastNewClosure) \
+ V(FastNewContext) \
+ V(FastNewBlockContext) \
+ V(FastCloneShallowArray) \
+ V(FastCloneShallowObject) \
+ V(CreateAllocationSite) \
+ V(ToBoolean) \
+ V(ToNumber) \
+ V(ArgumentsAccess) \
+ V(RegExpConstructResult) \
+ V(NumberToString) \
+ V(DoubleToI) \
+ V(CEntry) \
+ V(JSEntry) \
+ V(KeyedLoadElement) \
+ V(ArrayNoArgumentConstructor) \
+ V(ArraySingleArgumentConstructor) \
+ V(ArrayNArgumentsConstructor) \
+ V(InternalArrayNoArgumentConstructor) \
+ V(InternalArraySingleArgumentConstructor) \
+ V(InternalArrayNArgumentsConstructor) \
+ V(KeyedStoreElement) \
+ V(DebuggerStatement) \
+ V(NameDictionaryLookup) \
+ V(ElementsTransitionAndStore) \
+ V(TransitionElementsKind) \
+ V(StoreArrayLiteralElement) \
+ V(StubFailureTrampoline) \
+ V(ArrayConstructor) \
+ V(InternalArrayConstructor) \
+ V(ProfileEntryHook) \
+ V(StoreGlobal) \
+ /* IC Handler stubs */ \
+ V(LoadField) \
+ V(KeyedLoadField)
+
+// List of code stubs only used on ARM platforms.
+#if V8_TARGET_ARCH_ARM
+#define CODE_STUB_LIST_ARM(V) \
+ V(GetProperty) \
+ V(SetProperty) \
+ V(InvokeBuiltin) \
+ V(RegExpCEntry) \
+ V(DirectCEntry)
+#else
+#define CODE_STUB_LIST_ARM(V)
+#endif
+
+// List of code stubs only used on MIPS platforms.
+#if V8_TARGET_ARCH_MIPS
+#define CODE_STUB_LIST_MIPS(V) \
+ V(RegExpCEntry) \
+ V(DirectCEntry)
+#else
+#define CODE_STUB_LIST_MIPS(V)
+#endif
+
+// Combined list of code stubs.
+#define CODE_STUB_LIST(V) \
+ CODE_STUB_LIST_ALL_PLATFORMS(V) \
+ CODE_STUB_LIST_ARM(V) \
+ CODE_STUB_LIST_MIPS(V)
+
+// Mode to overwrite BinaryExpression values.
+enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
+
+// Stub is base classes of all stubs.
+class CodeStub BASE_EMBEDDED {
+ public:
+ enum Major {
+#define DEF_ENUM(name) name,
+ CODE_STUB_LIST(DEF_ENUM)
+#undef DEF_ENUM
+ NoCache, // marker for stubs that do custom caching
+ NUMBER_OF_IDS
+ };
+
+ // Retrieve the code for the stub. Generate the code if needed.
+ Handle<Code> GetCode(Isolate* isolate);
+
+ // Retrieve the code for the stub, make and return a copy of the code.
+ Handle<Code> GetCodeCopyFromTemplate(Isolate* isolate);
+ static Major MajorKeyFromKey(uint32_t key) {
+ return static_cast<Major>(MajorKeyBits::decode(key));
+ }
+ static int MinorKeyFromKey(uint32_t key) {
+ return MinorKeyBits::decode(key);
+ }
+
+ // Gets the major key from a code object that is a code stub or binary op IC.
+ static Major GetMajorKey(Code* code_stub) {
+ return static_cast<Major>(code_stub->major_key());
+ }
+
+ static const char* MajorName(Major major_key, bool allow_unknown_keys);
+
+ virtual ~CodeStub() {}
+
+ bool CompilingCallsToThisStubIsGCSafe(Isolate* isolate) {
+ bool is_pregenerated = IsPregenerated();
+ Code* code = NULL;
+ CHECK(!is_pregenerated || FindCodeInCache(&code, isolate));
+ return is_pregenerated;
+ }
+
+ // See comment above, where Instanceof is defined.
+ virtual bool IsPregenerated() { return false; }
+
+ static void GenerateStubsAheadOfTime(Isolate* isolate);
+ static void GenerateFPStubs(Isolate* isolate);
+
+ // 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, Isolate* isolate);
+
+ // Returns information for computing the number key.
+ virtual Major MajorKey() = 0;
+ virtual int MinorKey() = 0;
+
+ virtual InlineCacheState GetICState() {
+ return UNINITIALIZED;
+ }
+ virtual Code::ExtraICState GetExtraICState() {
+ return Code::kNoExtraICState;
+ }
+ virtual Code::StubType GetStubType() {
+ return Code::NORMAL;
+ }
+ virtual int GetStubFlags() {
+ return -1;
+ }
+
+ virtual void PrintName(StringStream* stream);
+
+ protected:
+ static bool CanUseFPRegisters();
+
+ // Generates the assembler code for the stub.
+ virtual Handle<Code> GenerateCode() = 0;
+
+
+ // Returns whether the code generated for this stub needs to be allocated as
+ // a fixed (non-moveable) code object.
+ virtual bool NeedsImmovableCode() { return false; }
+
+ // Returns a name for logging/debugging purposes.
+ SmartArrayPointer<const char> GetName();
+ virtual void PrintBaseName(StringStream* stream);
+ virtual void PrintState(StringStream* stream) { }
+
+ private:
+ // Perform bookkeeping required after code generation when stub code is
+ // initially generated.
+ void RecordCodeGeneration(Code* code, Isolate* isolate);
+
+ // Finish the code object after it has been generated.
+ virtual void FinishCode(Handle<Code> code) { }
+
+ // Activate newly generated stub. Is called after
+ // registering stub in the stub cache.
+ virtual void Activate(Code* code) { }
+
+ // BinaryOpStub needs to override this.
+ virtual Code::Kind GetCodeKind() const;
+
+ // 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, Isolate* isolate) {
+ return false;
+ }
+
+ // If a stub uses a special cache override this.
+ virtual bool UseSpecialCache() { return false; }
+
+ // Computes the key based on major and minor.
+ uint32_t GetKey() {
+ ASSERT(static_cast<int>(MajorKey()) < NUMBER_OF_IDS);
+ return MinorKeyBits::encode(MinorKey()) |
+ MajorKeyBits::encode(MajorKey());
+ }
+
+ class MajorKeyBits: public BitField<uint32_t, 0, kStubMajorKeyBits> {};
+ class MinorKeyBits: public BitField<uint32_t,
+ kStubMajorKeyBits, kStubMinorKeyBits> {}; // NOLINT
+
+ friend class BreakPointIterator;
+};
+
+
+class PlatformCodeStub : public CodeStub {
+ public:
+ // Retrieve the code for the stub. Generate the code if needed.
+ virtual Handle<Code> GenerateCode();
+
+ virtual Code::Kind GetCodeKind() const { return Code::STUB; }
+
+ protected:
+ // Generates the assembler code for the stub.
+ virtual void Generate(MacroAssembler* masm) = 0;
+};
+
+
+enum StubFunctionMode { NOT_JS_FUNCTION_STUB_MODE, JS_FUNCTION_STUB_MODE };
+
+
+struct CodeStubInterfaceDescriptor {
+ CodeStubInterfaceDescriptor();
+ int register_param_count_;
+ const Register* stack_parameter_count_;
+ // if hint_stack_parameter_count_ > 0, the code stub can optimize the
+ // return sequence. Default value is -1, which means it is ignored.
+ int hint_stack_parameter_count_;
+ StubFunctionMode function_mode_;
+ Register* register_params_;
+ Address deoptimization_handler_;
+
+ int environment_length() const {
+ if (stack_parameter_count_ != NULL) {
+ return register_param_count_ + 1;
+ }
+ return register_param_count_;
+ }
+
+ bool initialized() const { return register_param_count_ >= 0; }
+
+ void SetMissHandler(ExternalReference handler) {
+ miss_handler_ = handler;
+ has_miss_handler_ = true;
+ }
+
+ ExternalReference miss_handler() {
+ ASSERT(has_miss_handler_);
+ return miss_handler_;
+ }
+
+ bool has_miss_handler() {
+ return has_miss_handler_;
+ }
+
+ private:
+ ExternalReference miss_handler_;
+ bool has_miss_handler_;
+};
+
+// A helper to make up for the fact that type Register is not fully
+// defined outside of the platform directories
+#define DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index) \
+ ((index) == (descriptor)->register_param_count_) \
+ ? *((descriptor)->stack_parameter_count_) \
+ : (descriptor)->register_params_[(index)]
+
+
+class HydrogenCodeStub : public CodeStub {
+ public:
+ enum InitializationState {
+ UNINITIALIZED,
+ INITIALIZED
+ };
+
+ explicit HydrogenCodeStub(InitializationState state = INITIALIZED) {
+ is_uninitialized_ = (state == UNINITIALIZED);
+ }
+
+ virtual Code::Kind GetCodeKind() const { return Code::STUB; }
+
+ CodeStubInterfaceDescriptor* GetInterfaceDescriptor(Isolate* isolate) {
+ return isolate->code_stub_interface_descriptor(MajorKey());
+ }
+
+ bool IsUninitialized() { return is_uninitialized_; }
+
+ template<class SubClass>
+ static Handle<Code> GetUninitialized(Isolate* isolate) {
+ SubClass::GenerateAheadOfTime(isolate);
+ return SubClass().GetCode(isolate);
+ }
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) = 0;
+
+ // Retrieve the code for the stub. Generate the code if needed.
+ virtual Handle<Code> GenerateCode() = 0;
+
+ virtual int NotMissMinorKey() = 0;
+
+ Handle<Code> GenerateLightweightMissCode(Isolate* isolate);
+
+ template<class StateType>
+ void TraceTransition(StateType from, StateType to);
+
+ private:
+ class MinorKeyBits: public BitField<int, 0, kStubMinorKeyBits - 1> {};
+ class IsMissBits: public BitField<bool, kStubMinorKeyBits - 1, 1> {};
+
+ void GenerateLightweightMiss(MacroAssembler* masm);
+ virtual int MinorKey() {
+ return IsMissBits::encode(is_uninitialized_) |
+ MinorKeyBits::encode(NotMissMinorKey());
+ }
+
+ bool is_uninitialized_;
+};
+
+
+// Helper interface to prepare to/restore after making runtime calls.
+class RuntimeCallHelper {
+ public:
+ virtual ~RuntimeCallHelper() {}
+
+ virtual void BeforeCall(MacroAssembler* masm) const = 0;
+
+ virtual void AfterCall(MacroAssembler* masm) const = 0;
+
+ protected:
+ RuntimeCallHelper() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RuntimeCallHelper);
+};
+
+
+// TODO(bmeurer): Move to the StringAddStub declaration once we're
+// done with the translation to a hydrogen code stub.
+enum StringAddFlags {
+ // Omit both parameter checks.
+ STRING_ADD_CHECK_NONE = 0,
+ // Check left parameter.
+ STRING_ADD_CHECK_LEFT = 1 << 0,
+ // Check right parameter.
+ STRING_ADD_CHECK_RIGHT = 1 << 1,
+ // Check both parameters.
+ STRING_ADD_CHECK_BOTH = STRING_ADD_CHECK_LEFT | STRING_ADD_CHECK_RIGHT,
+ // Stub needs a frame before calling the runtime
+ STRING_ADD_ERECT_FRAME = 1 << 2
+};
+
+} } // namespace v8::internal
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/code-stubs-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/code-stubs-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/code-stubs-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/code-stubs-mips.h"
+#else
+#error Unsupported target architecture.
+#endif
+
+namespace v8 {
+namespace internal {
+
+
+// RuntimeCallHelper implementation used in stubs: enters/leaves a
+// newly created internal frame before/after the runtime call.
+class StubRuntimeCallHelper : public RuntimeCallHelper {
+ public:
+ StubRuntimeCallHelper() {}
+
+ virtual void BeforeCall(MacroAssembler* masm) const;
+
+ virtual void AfterCall(MacroAssembler* masm) const;
+};
+
+
+// Trivial RuntimeCallHelper implementation.
+class NopRuntimeCallHelper : public RuntimeCallHelper {
+ public:
+ NopRuntimeCallHelper() {}
+
+ virtual void BeforeCall(MacroAssembler* masm) const {}
+
+ virtual void AfterCall(MacroAssembler* masm) const {}
+};
+
+
+class StackCheckStub : public PlatformCodeStub {
+ public:
+ StackCheckStub() { }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Major MajorKey() { return StackCheck; }
+ int MinorKey() { return 0; }
+};
+
+
+class InterruptStub : public PlatformCodeStub {
+ public:
+ InterruptStub() { }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Major MajorKey() { return Interrupt; }
+ int MinorKey() { return 0; }
+};
+
+
+class ToNumberStub: public HydrogenCodeStub {
+ public:
+ ToNumberStub() { }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Major MajorKey() { return ToNumber; }
+ int NotMissMinorKey() { return 0; }
+};
+
+
+class FastNewClosureStub : public PlatformCodeStub {
+ public:
+ explicit FastNewClosureStub(LanguageMode language_mode, bool is_generator)
+ : language_mode_(language_mode),
+ is_generator_(is_generator) { }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ class StrictModeBits: public BitField<bool, 0, 1> {};
+ class IsGeneratorBits: public BitField<bool, 1, 1> {};
+
+ Major MajorKey() { return FastNewClosure; }
+ int MinorKey() {
+ return StrictModeBits::encode(language_mode_ != CLASSIC_MODE) |
+ IsGeneratorBits::encode(is_generator_);
+ }
+
+ LanguageMode language_mode_;
+ bool is_generator_;
+};
+
+
+class FastNewContextStub : public PlatformCodeStub {
+ public:
+ static const int kMaximumSlots = 64;
+
+ explicit FastNewContextStub(int slots) : slots_(slots) {
+ ASSERT(slots_ > 0 && slots_ <= kMaximumSlots);
+ }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ int slots_;
+
+ Major MajorKey() { return FastNewContext; }
+ int MinorKey() { return slots_; }
+};
+
+
+class FastNewBlockContextStub : public PlatformCodeStub {
+ 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 StoreGlobalStub : public HydrogenCodeStub {
+ public:
+ StoreGlobalStub(StrictModeFlag strict_mode, bool is_constant) {
+ bit_field_ = StrictModeBits::encode(strict_mode) |
+ IsConstantBits::encode(is_constant);
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ virtual Code::Kind GetCodeKind() const { return Code::STORE_IC; }
+ virtual InlineCacheState GetICState() { return MONOMORPHIC; }
+ virtual Code::ExtraICState GetExtraICState() { return bit_field_; }
+
+ bool is_constant() {
+ return IsConstantBits::decode(bit_field_);
+ }
+ void set_is_constant(bool value) {
+ bit_field_ = IsConstantBits::update(bit_field_, value);
+ }
+
+ Representation representation() {
+ return Representation::FromKind(RepresentationBits::decode(bit_field_));
+ }
+ void set_representation(Representation r) {
+ bit_field_ = RepresentationBits::update(bit_field_, r.kind());
+ }
+
+ private:
+ virtual int NotMissMinorKey() { return GetExtraICState(); }
+ Major MajorKey() { return StoreGlobal; }
+
+ class StrictModeBits: public BitField<StrictModeFlag, 0, 1> {};
+ class IsConstantBits: public BitField<bool, 1, 1> {};
+ class RepresentationBits: public BitField<Representation::Kind, 2, 8> {};
+
+ int bit_field_;
+
+ DISALLOW_COPY_AND_ASSIGN(StoreGlobalStub);
+};
+
+
+class FastCloneShallowArrayStub : public HydrogenCodeStub {
+ public:
+ // Maximum length of copied elements array.
+ static const int kMaximumClonedLength = 8;
+ enum Mode {
+ CLONE_ELEMENTS,
+ CLONE_DOUBLE_ELEMENTS,
+ COPY_ON_WRITE_ELEMENTS,
+ CLONE_ANY_ELEMENTS,
+ LAST_CLONE_MODE = CLONE_ANY_ELEMENTS
+ };
+
+ static const int kFastCloneModeCount = LAST_CLONE_MODE + 1;
+
+ FastCloneShallowArrayStub(Mode mode,
+ AllocationSiteMode allocation_site_mode,
+ int length)
+ : mode_(mode),
+ allocation_site_mode_(allocation_site_mode),
+ length_((mode == COPY_ON_WRITE_ELEMENTS) ? 0 : length) {
+ ASSERT_GE(length_, 0);
+ ASSERT_LE(length_, kMaximumClonedLength);
+ }
+
+ Mode mode() const { return mode_; }
+ int length() const { return length_; }
+ AllocationSiteMode allocation_site_mode() const {
+ return allocation_site_mode_;
+ }
+
+ ElementsKind ComputeElementsKind() const {
+ switch (mode()) {
+ case CLONE_ELEMENTS:
+ case COPY_ON_WRITE_ELEMENTS:
+ return FAST_ELEMENTS;
+ case CLONE_DOUBLE_ELEMENTS:
+ return FAST_DOUBLE_ELEMENTS;
+ case CLONE_ANY_ELEMENTS:
+ /*fall-through*/;
+ }
+ UNREACHABLE();
+ return LAST_ELEMENTS_KIND;
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Mode mode_;
+ AllocationSiteMode allocation_site_mode_;
+ int length_;
+
+ class AllocationSiteModeBits: public BitField<AllocationSiteMode, 0, 1> {};
+ class ModeBits: public BitField<Mode, 1, 4> {};
+ class LengthBits: public BitField<int, 5, 4> {};
+ // Ensure data fits within available bits.
+ STATIC_ASSERT(LAST_ALLOCATION_SITE_MODE == 1);
+ STATIC_ASSERT(kFastCloneModeCount < 16);
+ STATIC_ASSERT(kMaximumClonedLength < 16);
+ Major MajorKey() { return FastCloneShallowArray; }
+ int NotMissMinorKey() {
+ return AllocationSiteModeBits::encode(allocation_site_mode_)
+ | ModeBits::encode(mode_)
+ | LengthBits::encode(length_);
+ }
+};
+
+
+class FastCloneShallowObjectStub : public HydrogenCodeStub {
+ 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);
+ }
+
+ int length() const { return length_; }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ int length_;
+
+ Major MajorKey() { return FastCloneShallowObject; }
+ int NotMissMinorKey() { return length_; }
+
+ DISALLOW_COPY_AND_ASSIGN(FastCloneShallowObjectStub);
+};
+
+
+class CreateAllocationSiteStub : public HydrogenCodeStub {
+ public:
+ explicit CreateAllocationSiteStub() { }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual bool IsPregenerated() { return true; }
+
+ static void GenerateAheadOfTime(Isolate* isolate);
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Major MajorKey() { return CreateAllocationSite; }
+ int NotMissMinorKey() { return 0; }
+
+ DISALLOW_COPY_AND_ASSIGN(CreateAllocationSiteStub);
+};
+
+
+class InstanceofStub: public PlatformCodeStub {
+ public:
+ enum Flags {
+ kNoFlags = 0,
+ kArgsInRegisters = 1 << 0,
+ kCallSiteInlineCheck = 1 << 1,
+ kReturnTrueFalseObject = 1 << 2
+ };
+
+ explicit InstanceofStub(Flags flags) : flags_(flags) { }
+
+ static Register left();
+ static Register right();
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Major MajorKey() { return Instanceof; }
+ int MinorKey() { return static_cast<int>(flags_); }
+
+ bool HasArgsInRegisters() const {
+ return (flags_ & kArgsInRegisters) != 0;
+ }
+
+ bool HasCallSiteInlineCheck() const {
+ return (flags_ & kCallSiteInlineCheck) != 0;
+ }
+
+ bool ReturnTrueFalseObject() const {
+ return (flags_ & kReturnTrueFalseObject) != 0;
+ }
+
+ virtual void PrintName(StringStream* stream);
+
+ Flags flags_;
+};
+
+
+class ArrayConstructorStub: public PlatformCodeStub {
+ public:
+ enum ArgumentCountKey { ANY, NONE, ONE, MORE_THAN_ONE };
+ ArrayConstructorStub(Isolate* isolate, int argument_count);
+ explicit ArrayConstructorStub(Isolate* isolate);
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ virtual CodeStub::Major MajorKey() { return ArrayConstructor; }
+ virtual int MinorKey() { return argument_count_; }
+
+ ArgumentCountKey argument_count_;
+};
+
+
+class InternalArrayConstructorStub: public PlatformCodeStub {
+ public:
+ explicit InternalArrayConstructorStub(Isolate* isolate);
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ virtual CodeStub::Major MajorKey() { return InternalArrayConstructor; }
+ virtual int MinorKey() { return 0; }
+
+ void GenerateCase(MacroAssembler* masm, ElementsKind kind);
+};
+
+
+class MathPowStub: public PlatformCodeStub {
+ public:
+ 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 exponent_type_; }
+
+ ExponentType exponent_type_;
+};
+
+
+class ICStub: public PlatformCodeStub {
+ public:
+ explicit ICStub(Code::Kind kind) : kind_(kind) { }
+ virtual Code::Kind GetCodeKind() const { return kind_; }
+ virtual InlineCacheState GetICState() { return MONOMORPHIC; }
+
+ bool Describes(Code* code) {
+ return GetMajorKey(code) == MajorKey() && code->stub_info() == MinorKey();
+ }
+
+ protected:
+ class KindBits: public BitField<Code::Kind, 0, 4> {};
+ virtual void FinishCode(Handle<Code> code) {
+ code->set_stub_info(MinorKey());
+ }
+ Code::Kind kind() { return kind_; }
+
+ virtual int MinorKey() {
+ return KindBits::encode(kind_);
+ }
+
+ private:
+ Code::Kind kind_;
+};
+
+
+class FunctionPrototypeStub: public ICStub {
+ public:
+ explicit FunctionPrototypeStub(Code::Kind kind) : ICStub(kind) { }
+ virtual void Generate(MacroAssembler* masm);
+
+ private:
+ virtual CodeStub::Major MajorKey() { return FunctionPrototype; }
+};
+
+
+class StringLengthStub: public ICStub {
+ public:
+ StringLengthStub(Code::Kind kind, bool support_wrapper)
+ : ICStub(kind), support_wrapper_(support_wrapper) { }
+ virtual void Generate(MacroAssembler* masm);
+
+ private:
+ STATIC_ASSERT(KindBits::kSize == 4);
+ class WrapperModeBits: public BitField<bool, 4, 1> {};
+ virtual CodeStub::Major MajorKey() { return StringLength; }
+ virtual int MinorKey() {
+ return KindBits::encode(kind()) | WrapperModeBits::encode(support_wrapper_);
+ }
+
+ bool support_wrapper_;
+};
+
+
+class StoreICStub: public ICStub {
+ public:
+ StoreICStub(Code::Kind kind, StrictModeFlag strict_mode)
+ : ICStub(kind), strict_mode_(strict_mode) { }
+
+ protected:
+ virtual Code::ExtraICState GetExtraICState() {
+ return strict_mode_;
+ }
+
+ private:
+ STATIC_ASSERT(KindBits::kSize == 4);
+ class StrictModeBits: public BitField<bool, 4, 1> {};
+ virtual int MinorKey() {
+ return KindBits::encode(kind()) | StrictModeBits::encode(strict_mode_);
+ }
+
+ StrictModeFlag strict_mode_;
+};
+
+
+class StoreArrayLengthStub: public StoreICStub {
+ public:
+ explicit StoreArrayLengthStub(Code::Kind kind, StrictModeFlag strict_mode)
+ : StoreICStub(kind, strict_mode) { }
+ virtual void Generate(MacroAssembler* masm);
+
+ private:
+ virtual CodeStub::Major MajorKey() { return StoreArrayLength; }
+};
+
+
+class HICStub: public HydrogenCodeStub {
+ public:
+ virtual Code::Kind GetCodeKind() const { return kind(); }
+ virtual InlineCacheState GetICState() { return MONOMORPHIC; }
+
+ protected:
+ HICStub() { }
+ class KindBits: public BitField<Code::Kind, 0, 4> {};
+ virtual Code::Kind kind() const = 0;
+};
+
+
+class HandlerStub: public HICStub {
+ public:
+ virtual Code::Kind GetCodeKind() const { return Code::STUB; }
+ virtual int GetStubFlags() { return kind(); }
+
+ protected:
+ HandlerStub() : HICStub() { }
+};
+
+
+class LoadFieldStub: public HandlerStub {
+ public:
+ LoadFieldStub(bool inobject, int index, Representation representation)
+ : HandlerStub() {
+ Initialize(Code::LOAD_IC, inobject, index, representation);
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ Representation representation() {
+ if (unboxed_double()) return Representation::Double();
+ return Representation::Tagged();
+ }
+
+ virtual Code::Kind kind() const {
+ return KindBits::decode(bit_field_);
+ }
+
+ bool is_inobject() {
+ return InobjectBits::decode(bit_field_);
+ }
+
+ int offset() {
+ int index = IndexBits::decode(bit_field_);
+ int offset = index * kPointerSize;
+ if (is_inobject()) return offset;
+ return FixedArray::kHeaderSize + offset;
+ }
+
+ bool unboxed_double() {
+ return UnboxedDoubleBits::decode(bit_field_);
+ }
+
+ virtual Code::StubType GetStubType() { return Code::FIELD; }
+
+ protected:
+ LoadFieldStub() : HandlerStub() { }
+
+ void Initialize(Code::Kind kind,
+ bool inobject,
+ int index,
+ Representation representation) {
+ bool unboxed_double = FLAG_track_double_fields && representation.IsDouble();
+ bit_field_ = KindBits::encode(kind)
+ | InobjectBits::encode(inobject)
+ | IndexBits::encode(index)
+ | UnboxedDoubleBits::encode(unboxed_double);
+ }
+
+ private:
+ STATIC_ASSERT(KindBits::kSize == 4);
+ class InobjectBits: public BitField<bool, 4, 1> {};
+ class IndexBits: public BitField<int, 5, 11> {};
+ class UnboxedDoubleBits: public BitField<bool, 16, 1> {};
+ virtual CodeStub::Major MajorKey() { return LoadField; }
+ virtual int NotMissMinorKey() { return bit_field_; }
+
+ int bit_field_;
+};
+
+
+class KeyedLoadFieldStub: public LoadFieldStub {
+ public:
+ KeyedLoadFieldStub(bool inobject, int index, Representation representation)
+ : LoadFieldStub() {
+ Initialize(Code::KEYED_LOAD_IC, inobject, index, representation);
+ }
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ virtual Handle<Code> GenerateCode();
+
+ private:
+ virtual CodeStub::Major MajorKey() { return KeyedLoadField; }
+};
+
+
+class BinaryOpStub: public PlatformCodeStub {
+ public:
+ BinaryOpStub(Token::Value op, OverwriteMode mode)
+ : op_(op),
+ mode_(mode),
+ platform_specific_bit_(false),
+ left_type_(BinaryOpIC::UNINITIALIZED),
+ right_type_(BinaryOpIC::UNINITIALIZED),
+ result_type_(BinaryOpIC::UNINITIALIZED),
+ encoded_right_arg_(false, encode_arg_value(1)) {
+ Initialize();
+ ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
+ }
+
+ BinaryOpStub(
+ int key,
+ BinaryOpIC::TypeInfo left_type,
+ BinaryOpIC::TypeInfo right_type,
+ BinaryOpIC::TypeInfo result_type,
+ Maybe<int32_t> fixed_right_arg)
+ : op_(OpBits::decode(key)),
+ mode_(ModeBits::decode(key)),
+ platform_specific_bit_(PlatformSpecificBits::decode(key)),
+ left_type_(left_type),
+ right_type_(right_type),
+ result_type_(result_type),
+ encoded_right_arg_(fixed_right_arg.has_value,
+ encode_arg_value(fixed_right_arg.value)) { }
+
+ static void decode_types_from_minor_key(int minor_key,
+ BinaryOpIC::TypeInfo* left_type,
+ BinaryOpIC::TypeInfo* right_type,
+ BinaryOpIC::TypeInfo* result_type) {
+ *left_type =
+ static_cast<BinaryOpIC::TypeInfo>(LeftTypeBits::decode(minor_key));
+ *right_type =
+ static_cast<BinaryOpIC::TypeInfo>(RightTypeBits::decode(minor_key));
+ *result_type =
+ static_cast<BinaryOpIC::TypeInfo>(ResultTypeBits::decode(minor_key));
+ }
+
+ static Token::Value decode_op_from_minor_key(int minor_key) {
+ return static_cast<Token::Value>(OpBits::decode(minor_key));
+ }
+
+ static Maybe<int> decode_fixed_right_arg_from_minor_key(int minor_key) {
+ return Maybe<int>(
+ HasFixedRightArgBits::decode(minor_key),
+ decode_arg_value(FixedRightArgValueBits::decode(minor_key)));
+ }
+
+ int fixed_right_arg_value() const {
+ return decode_arg_value(encoded_right_arg_.value);
+ }
+
+ static bool can_encode_arg_value(int32_t value) {
+ return value > 0 &&
+ IsPowerOf2(value) &&
+ FixedRightArgValueBits::is_valid(WhichPowerOf2(value));
+ }
+
+ enum SmiCodeGenerateHeapNumberResults {
+ ALLOW_HEAPNUMBER_RESULTS,
+ NO_HEAPNUMBER_RESULTS
+ };
+
+ private:
+ Token::Value op_;
+ OverwriteMode mode_;
+ bool platform_specific_bit_; // Indicates SSE3 on IA32.
+
+ // Operand type information determined at runtime.
+ BinaryOpIC::TypeInfo left_type_;
+ BinaryOpIC::TypeInfo right_type_;
+ BinaryOpIC::TypeInfo result_type_;
+
+ Maybe<int> encoded_right_arg_;
+
+ static int encode_arg_value(int32_t value) {
+ ASSERT(can_encode_arg_value(value));
+ return WhichPowerOf2(value);
+ }
+
+ static int32_t decode_arg_value(int value) {
+ return 1 << value;
+ }
+
+ virtual void PrintName(StringStream* stream);
+
+ // Minor key encoding in all 25 bits FFFFFHTTTRRRLLLPOOOOOOOMM.
+ // Note: We actually do not need 7 bits for the operation, just 4 bits to
+ // encode ADD, SUB, MUL, DIV, MOD, BIT_OR, BIT_AND, BIT_XOR, SAR, SHL, SHR.
+ class ModeBits: public BitField<OverwriteMode, 0, 2> {};
+ class OpBits: public BitField<Token::Value, 2, 7> {};
+ class PlatformSpecificBits: public BitField<bool, 9, 1> {};
+ class LeftTypeBits: public BitField<BinaryOpIC::TypeInfo, 10, 3> {};
+ class RightTypeBits: public BitField<BinaryOpIC::TypeInfo, 13, 3> {};
+ class ResultTypeBits: public BitField<BinaryOpIC::TypeInfo, 16, 3> {};
+ class HasFixedRightArgBits: public BitField<bool, 19, 1> {};
+ class FixedRightArgValueBits: public BitField<int, 20, 5> {};
+
+ Major MajorKey() { return BinaryOp; }
+ int MinorKey() {
+ return OpBits::encode(op_)
+ | ModeBits::encode(mode_)
+ | PlatformSpecificBits::encode(platform_specific_bit_)
+ | LeftTypeBits::encode(left_type_)
+ | RightTypeBits::encode(right_type_)
+ | ResultTypeBits::encode(result_type_)
+ | HasFixedRightArgBits::encode(encoded_right_arg_.has_value)
+ | FixedRightArgValueBits::encode(encoded_right_arg_.value);
+ }
+
+
+ // Platform-independent implementation.
+ void Generate(MacroAssembler* masm);
+ void GenerateCallRuntime(MacroAssembler* masm);
+
+ // Platform-independent signature, platform-specific implementation.
+ void Initialize();
+ void GenerateAddStrings(MacroAssembler* masm);
+ void GenerateBothStringStub(MacroAssembler* masm);
+ void GenerateGeneric(MacroAssembler* masm);
+ void GenerateGenericStub(MacroAssembler* masm);
+ void GenerateNumberStub(MacroAssembler* masm);
+ void GenerateInt32Stub(MacroAssembler* masm);
+ void GenerateLoadArguments(MacroAssembler* masm);
+ void GenerateOddballStub(MacroAssembler* masm);
+ void GenerateRegisterArgsPush(MacroAssembler* masm);
+ void GenerateReturn(MacroAssembler* masm);
+ void GenerateSmiStub(MacroAssembler* masm);
+ void GenerateStringStub(MacroAssembler* masm);
+ void GenerateTypeTransition(MacroAssembler* masm);
+ void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
+ void GenerateUninitializedStub(MacroAssembler* masm);
+
+ // Entirely platform-specific methods are defined as static helper
+ // functions in the <arch>/code-stubs-<arch>.cc files.
+
+ virtual Code::Kind GetCodeKind() const { return Code::BINARY_OP_IC; }
+
+ virtual InlineCacheState GetICState() {
+ return BinaryOpIC::ToState(Max(left_type_, right_type_));
+ }
+
+ virtual void FinishCode(Handle<Code> code) {
+ code->set_stub_info(MinorKey());
+ }
+
+ friend class CodeGenerator;
+};
+
+
+class ICCompareStub: public PlatformCodeStub {
+ public:
+ ICCompareStub(Token::Value op,
+ CompareIC::State left,
+ CompareIC::State right,
+ CompareIC::State handler)
+ : op_(op),
+ left_(left),
+ right_(right),
+ state_(handler) {
+ ASSERT(Token::IsCompareOp(op));
+ }
+
+ virtual void Generate(MacroAssembler* masm);
+
+ void set_known_map(Handle<Map> map) { known_map_ = map; }
+
+ static void DecodeMinorKey(int minor_key,
+ CompareIC::State* left_state,
+ CompareIC::State* right_state,
+ CompareIC::State* handler_state,
+ Token::Value* op);
+
+ static CompareIC::State CompareState(int minor_key) {
+ return static_cast<CompareIC::State>(HandlerStateField::decode(minor_key));
+ }
+
+ virtual InlineCacheState GetICState();
+
+ private:
+ class OpField: public BitField<int, 0, 3> { };
+ class LeftStateField: public BitField<int, 3, 4> { };
+ class RightStateField: public BitField<int, 7, 4> { };
+ class HandlerStateField: public BitField<int, 11, 4> { };
+
+ virtual void FinishCode(Handle<Code> code) {
+ code->set_stub_info(MinorKey());
+ }
+
+ virtual CodeStub::Major MajorKey() { return CompareIC; }
+ virtual int MinorKey();
+
+ virtual Code::Kind GetCodeKind() const { return Code::COMPARE_IC; }
+
+ void GenerateSmis(MacroAssembler* masm);
+ void GenerateNumbers(MacroAssembler* masm);
+ void GenerateInternalizedStrings(MacroAssembler* masm);
+ void GenerateStrings(MacroAssembler* masm);
+ void GenerateUniqueNames(MacroAssembler* masm);
+ void GenerateObjects(MacroAssembler* masm);
+ void GenerateMiss(MacroAssembler* masm);
+ void GenerateKnownObjects(MacroAssembler* masm);
+ void GenerateGeneric(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, Isolate* isolate);
+ virtual bool UseSpecialCache() { return state_ == CompareIC::KNOWN_OBJECT; }
+
+ Token::Value op_;
+ CompareIC::State left_;
+ CompareIC::State right_;
+ CompareIC::State state_;
+ Handle<Map> known_map_;
+};
+
+
+class CompareNilICStub : public HydrogenCodeStub {
+ public:
+ Handle<Type> GetType(Isolate* isolate, Handle<Map> map = Handle<Map>());
+ Handle<Type> GetInputType(Isolate* isolate, Handle<Map> map);
+
+ explicit CompareNilICStub(NilValue nil) : nil_value_(nil) { }
+
+ CompareNilICStub(Code::ExtraICState ic_state,
+ InitializationState init_state = INITIALIZED)
+ : HydrogenCodeStub(init_state),
+ nil_value_(NilValueField::decode(ic_state)),
+ state_(State(TypesField::decode(ic_state))) {
+ }
+
+ static Handle<Code> GetUninitialized(Isolate* isolate,
+ NilValue nil) {
+ return CompareNilICStub(nil, UNINITIALIZED).GetCode(isolate);
+ }
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ static void InitializeForIsolate(Isolate* isolate) {
+ CompareNilICStub compare_stub(kNullValue, UNINITIALIZED);
+ compare_stub.InitializeInterfaceDescriptor(
+ isolate,
+ isolate->code_stub_interface_descriptor(CodeStub::CompareNilIC));
+ }
+
+ virtual InlineCacheState GetICState() {
+ if (state_.Contains(GENERIC)) {
+ return MEGAMORPHIC;
+ } else if (state_.Contains(MONOMORPHIC_MAP)) {
+ return MONOMORPHIC;
+ } else {
+ return PREMONOMORPHIC;
+ }
+ }
+
+ virtual Code::Kind GetCodeKind() const { return Code::COMPARE_NIL_IC; }
+
+ Handle<Code> GenerateCode();
+
+ virtual Code::ExtraICState GetExtraICState() {
+ return NilValueField::encode(nil_value_) |
+ TypesField::encode(state_.ToIntegral());
+ }
+
+ void UpdateStatus(Handle<Object> object);
+
+ bool IsMonomorphic() const { return state_.Contains(MONOMORPHIC_MAP); }
+ NilValue GetNilValue() const { return nil_value_; }
+ void ClearState() { state_.RemoveAll(); }
+
+ virtual void PrintState(StringStream* stream);
+ virtual void PrintBaseName(StringStream* stream);
+
+ private:
+ friend class CompareNilIC;
+
+ enum CompareNilType {
+ UNDEFINED,
+ NULL_TYPE,
+ MONOMORPHIC_MAP,
+ GENERIC,
+ NUMBER_OF_TYPES
+ };
+
+ // At most 6 different types can be distinguished, because the Code object
+ // only has room for a single byte to hold a set and there are two more
+ // boolean flags we need to store. :-P
+ STATIC_ASSERT(NUMBER_OF_TYPES <= 6);
+
+ class State : public EnumSet<CompareNilType, byte> {
+ public:
+ State() : EnumSet<CompareNilType, byte>(0) { }
+ explicit State(byte bits) : EnumSet<CompareNilType, byte>(bits) { }
+
+ void Print(StringStream* stream) const;
+ };
+
+ CompareNilICStub(NilValue nil, InitializationState init_state)
+ : HydrogenCodeStub(init_state), nil_value_(nil) { }
+
+ class NilValueField : public BitField<NilValue, 0, 1> {};
+ class TypesField : public BitField<byte, 1, NUMBER_OF_TYPES> {};
+
+ virtual CodeStub::Major MajorKey() { return CompareNilIC; }
+ virtual int NotMissMinorKey() { return GetExtraICState(); }
+
+ NilValue nil_value_;
+ State state_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompareNilICStub);
+};
+
+
+class CEntryStub : public PlatformCodeStub {
+ public:
+ explicit CEntryStub(int result_size,
+ SaveFPRegsMode save_doubles = kDontSaveFPRegs)
+ : result_size_(result_size), save_doubles_(save_doubles) { }
+
+ void Generate(MacroAssembler* masm);
+
+ // 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(Isolate* isolate);
+
+ private:
+ void GenerateCore(MacroAssembler* masm,
+ Label* throw_normal_exception,
+ Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
+ bool do_gc,
+ bool always_allocate_scope);
+
+ // Number of pointers/values returned.
+ const int result_size_;
+ SaveFPRegsMode save_doubles_;
+
+ Major MajorKey() { return CEntry; }
+ int MinorKey();
+
+ bool NeedsImmovableCode();
+};
+
+
+class JSEntryStub : public PlatformCodeStub {
+ public:
+ JSEntryStub() { }
+
+ void Generate(MacroAssembler* masm) { GenerateBody(masm, false); }
+
+ protected:
+ void GenerateBody(MacroAssembler* masm, bool is_construct);
+
+ private:
+ Major MajorKey() { return JSEntry; }
+ int MinorKey() { return 0; }
+
+ virtual void FinishCode(Handle<Code> code);
+
+ int handler_offset_;
+};
+
+
+class JSConstructEntryStub : public JSEntryStub {
+ public:
+ JSConstructEntryStub() { }
+
+ void Generate(MacroAssembler* masm) { GenerateBody(masm, true); }
+
+ private:
+ int MinorKey() { return 1; }
+
+ virtual void PrintName(StringStream* stream) {
+ stream->Add("JSConstructEntryStub");
+ }
+};
+
+
+class ArgumentsAccessStub: public PlatformCodeStub {
+ public:
+ enum Type {
+ READ_ELEMENT,
+ NEW_NON_STRICT_FAST,
+ NEW_NON_STRICT_SLOW,
+ NEW_STRICT
+ };
+
+ explicit ArgumentsAccessStub(Type type) : type_(type) { }
+
+ private:
+ Type type_;
+
+ Major MajorKey() { return ArgumentsAccess; }
+ int MinorKey() { return type_; }
+
+ void Generate(MacroAssembler* masm);
+ void GenerateReadElement(MacroAssembler* masm);
+ void GenerateNewStrict(MacroAssembler* masm);
+ void GenerateNewNonStrictFast(MacroAssembler* masm);
+ void GenerateNewNonStrictSlow(MacroAssembler* masm);
+
+ virtual void PrintName(StringStream* stream);
+};
+
+
+class RegExpExecStub: public PlatformCodeStub {
+ public:
+ RegExpExecStub() { }
+
+ private:
+ Major MajorKey() { return RegExpExec; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class RegExpConstructResultStub: public PlatformCodeStub {
+ public:
+ RegExpConstructResultStub() { }
+
+ private:
+ Major MajorKey() { return RegExpConstructResult; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class CallFunctionStub: public PlatformCodeStub {
+ public:
+ CallFunctionStub(int argc, CallFunctionFlags flags)
+ : argc_(argc), flags_(flags) { }
+
+ 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);
+ }
+
+ private:
+ int argc_;
+ CallFunctionFlags flags_;
+
+ virtual void PrintName(StringStream* stream);
+
+ // Minor key encoding in 32 bits with Bitfield <Type, shift, size>.
+ class FlagBits: public BitField<CallFunctionFlags, 0, 2> {};
+ class ArgcBits: public BitField<unsigned, 2, 32 - 2> {};
+
+ Major MajorKey() { return CallFunction; }
+ int MinorKey() {
+ // Encode the parameters in a unique 32 bit value.
+ return FlagBits::encode(flags_) | ArgcBits::encode(argc_);
+ }
+
+ bool ReceiverMightBeImplicit() {
+ return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0;
+ }
+
+ bool RecordCallTarget() {
+ return (flags_ & RECORD_CALL_TARGET) != 0;
+ }
+};
+
+
+class CallConstructStub: public PlatformCodeStub {
+ 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;
+ }
+};
+
+
+enum StringIndexFlags {
+ // Accepts smis or heap numbers.
+ STRING_INDEX_IS_NUMBER,
+
+ // Accepts smis or heap numbers that are valid array indices
+ // (ECMA-262 15.4). Invalid indices are reported as being out of
+ // range.
+ STRING_INDEX_IS_ARRAY_INDEX
+};
+
+
+// Generates code implementing String.prototype.charCodeAt.
+//
+// Only supports the case when the receiver is a string and the index
+// is a number (smi or heap number) that is a valid index into the
+// string. Additional index constraints are specified by the
+// flags. Otherwise, bails out to the provided labels.
+//
+// Register usage: |object| may be changed to another string in a way
+// that doesn't affect charCodeAt/charAt semantics, |index| is
+// preserved, |scratch| and |result| are clobbered.
+class StringCharCodeAtGenerator {
+ public:
+ StringCharCodeAtGenerator(Register object,
+ Register index,
+ Register result,
+ Label* receiver_not_string,
+ Label* index_not_number,
+ Label* index_out_of_range,
+ StringIndexFlags index_flags)
+ : object_(object),
+ index_(index),
+ 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(!result_.is(object_));
+ ASSERT(!result_.is(index_));
+ }
+
+ // Generates the fast case code. On the fallthrough path |result|
+ // register contains the result.
+ void GenerateFast(MacroAssembler* masm);
+
+ // Generates the slow case code. Must not be naturally
+ // reachable. Expected to be put after a ret instruction (e.g., in
+ // deferred code). Always jumps back to the fast case.
+ void GenerateSlow(MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper);
+
+ // Skip handling slow case and directly jump to bailout.
+ void SkipSlow(MacroAssembler* masm, Label* bailout) {
+ masm->bind(&index_not_smi_);
+ masm->bind(&call_runtime_);
+ masm->jmp(bailout);
+ }
+
+ private:
+ Register object_;
+ Register index_;
+ Register result_;
+
+ Label* receiver_not_string_;
+ Label* index_not_number_;
+ Label* index_out_of_range_;
+
+ StringIndexFlags index_flags_;
+
+ Label call_runtime_;
+ Label index_not_smi_;
+ Label got_smi_index_;
+ Label exit_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringCharCodeAtGenerator);
+};
+
+
+// Generates code for creating a one-char string from a char code.
+class StringCharFromCodeGenerator {
+ public:
+ StringCharFromCodeGenerator(Register code,
+ Register result)
+ : code_(code),
+ result_(result) {
+ ASSERT(!code_.is(result_));
+ }
+
+ // Generates the fast case code. On the fallthrough path |result|
+ // register contains the result.
+ void GenerateFast(MacroAssembler* masm);
+
+ // Generates the slow case code. Must not be naturally
+ // reachable. Expected to be put after a ret instruction (e.g., in
+ // deferred code). Always jumps back to the fast case.
+ void GenerateSlow(MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper);
+
+ // Skip handling slow case and directly jump to bailout.
+ void SkipSlow(MacroAssembler* masm, Label* bailout) {
+ masm->bind(&slow_case_);
+ masm->jmp(bailout);
+ }
+
+ private:
+ Register code_;
+ Register result_;
+
+ Label slow_case_;
+ Label exit_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringCharFromCodeGenerator);
+};
+
+
+// Generates code implementing String.prototype.charAt.
+//
+// Only supports the case when the receiver is a string and the index
+// is a number (smi or heap number) that is a valid index into the
+// string. Additional index constraints are specified by the
+// flags. Otherwise, bails out to the provided labels.
+//
+// Register usage: |object| may be changed to another string in a way
+// that doesn't affect charCodeAt/charAt semantics, |index| is
+// preserved, |scratch1|, |scratch2|, and |result| are clobbered.
+class StringCharAtGenerator {
+ public:
+ StringCharAtGenerator(Register object,
+ Register index,
+ Register scratch,
+ Register result,
+ Label* receiver_not_string,
+ Label* index_not_number,
+ Label* index_out_of_range,
+ StringIndexFlags index_flags)
+ : char_code_at_generator_(object,
+ index,
+ scratch,
+ receiver_not_string,
+ index_not_number,
+ index_out_of_range,
+ index_flags),
+ char_from_code_generator_(scratch, result) {}
+
+ // Generates the fast case code. On the fallthrough path |result|
+ // register contains the result.
+ void GenerateFast(MacroAssembler* masm) {
+ char_code_at_generator_.GenerateFast(masm);
+ char_from_code_generator_.GenerateFast(masm);
+ }
+
+ // Generates the slow case code. Must not be naturally
+ // reachable. Expected to be put after a ret instruction (e.g., in
+ // deferred code). Always jumps back to the fast case.
+ void GenerateSlow(MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ char_code_at_generator_.GenerateSlow(masm, call_helper);
+ char_from_code_generator_.GenerateSlow(masm, call_helper);
+ }
+
+ // Skip handling slow case and directly jump to bailout.
+ void SkipSlow(MacroAssembler* masm, Label* bailout) {
+ char_code_at_generator_.SkipSlow(masm, bailout);
+ char_from_code_generator_.SkipSlow(masm, bailout);
+ }
+
+ private:
+ StringCharCodeAtGenerator char_code_at_generator_;
+ StringCharFromCodeGenerator char_from_code_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringCharAtGenerator);
+};
+
+
+class AllowStubCallsScope {
+ public:
+ AllowStubCallsScope(MacroAssembler* masm, bool allow)
+ : masm_(masm), previous_allow_(masm->allow_stub_calls()) {
+ masm_->set_allow_stub_calls(allow);
+ }
+ ~AllowStubCallsScope() {
+ masm_->set_allow_stub_calls(previous_allow_);
+ }
+
+ private:
+ MacroAssembler* masm_;
+ bool previous_allow_;
+
+ DISALLOW_COPY_AND_ASSIGN(AllowStubCallsScope);
+};
+
+
+class KeyedLoadDictionaryElementStub : public PlatformCodeStub {
+ public:
+ KeyedLoadDictionaryElementStub() {}
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Major MajorKey() { return KeyedLoadElement; }
+ int MinorKey() { return DICTIONARY_ELEMENTS; }
+
+ DISALLOW_COPY_AND_ASSIGN(KeyedLoadDictionaryElementStub);
+};
+
+
+class DoubleToIStub : public PlatformCodeStub {
+ public:
+ DoubleToIStub(Register source,
+ Register destination,
+ int offset,
+ bool is_truncating) : bit_field_(0) {
+ bit_field_ = SourceRegisterBits::encode(source.code_) |
+ DestinationRegisterBits::encode(destination.code_) |
+ OffsetBits::encode(offset) |
+ IsTruncatingBits::encode(is_truncating);
+ }
+
+ Register source() {
+ Register result = { SourceRegisterBits::decode(bit_field_) };
+ return result;
+ }
+
+ Register destination() {
+ Register result = { DestinationRegisterBits::decode(bit_field_) };
+ return result;
+ }
+
+ bool is_truncating() {
+ return IsTruncatingBits::decode(bit_field_);
+ }
+
+ int offset() {
+ return OffsetBits::decode(bit_field_);
+ }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ static const int kBitsPerRegisterNumber = 6;
+ STATIC_ASSERT((1L << kBitsPerRegisterNumber) >= Register::kNumRegisters);
+ class SourceRegisterBits:
+ public BitField<int, 0, kBitsPerRegisterNumber> {}; // NOLINT
+ class DestinationRegisterBits:
+ public BitField<int, kBitsPerRegisterNumber,
+ kBitsPerRegisterNumber> {}; // NOLINT
+ class IsTruncatingBits:
+ public BitField<bool, 2 * kBitsPerRegisterNumber, 1> {}; // NOLINT
+ class OffsetBits:
+ public BitField<int, 2 * kBitsPerRegisterNumber + 1, 3> {}; // NOLINT
+
+ Major MajorKey() { return DoubleToI; }
+ int MinorKey() { return bit_field_; }
+
+ int bit_field_;
+
+ DISALLOW_COPY_AND_ASSIGN(DoubleToIStub);
+};
+
+
+class KeyedLoadFastElementStub : public HydrogenCodeStub {
+ public:
+ KeyedLoadFastElementStub(bool is_js_array, ElementsKind elements_kind) {
+ bit_field_ = ElementsKindBits::encode(elements_kind) |
+ IsJSArrayBits::encode(is_js_array);
+ }
+
+ bool is_js_array() const {
+ return IsJSArrayBits::decode(bit_field_);
+ }
+
+ ElementsKind elements_kind() const {
+ return ElementsKindBits::decode(bit_field_);
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ class ElementsKindBits: public BitField<ElementsKind, 0, 8> {};
+ class IsJSArrayBits: public BitField<bool, 8, 1> {};
+ uint32_t bit_field_;
+
+ Major MajorKey() { return KeyedLoadElement; }
+ int NotMissMinorKey() { return bit_field_; }
+
+ DISALLOW_COPY_AND_ASSIGN(KeyedLoadFastElementStub);
+};
+
+
+class KeyedStoreFastElementStub : public HydrogenCodeStub {
+ public:
+ KeyedStoreFastElementStub(bool is_js_array,
+ ElementsKind elements_kind,
+ KeyedAccessStoreMode mode) {
+ bit_field_ = ElementsKindBits::encode(elements_kind) |
+ IsJSArrayBits::encode(is_js_array) |
+ StoreModeBits::encode(mode);
+ }
+
+ bool is_js_array() const {
+ return IsJSArrayBits::decode(bit_field_);
+ }
+
+ ElementsKind elements_kind() const {
+ return ElementsKindBits::decode(bit_field_);
+ }
+
+ KeyedAccessStoreMode store_mode() const {
+ return StoreModeBits::decode(bit_field_);
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ class ElementsKindBits: public BitField<ElementsKind, 0, 8> {};
+ class StoreModeBits: public BitField<KeyedAccessStoreMode, 8, 4> {};
+ class IsJSArrayBits: public BitField<bool, 12, 1> {};
+ uint32_t bit_field_;
+
+ Major MajorKey() { return KeyedStoreElement; }
+ int NotMissMinorKey() { return bit_field_; }
+
+ DISALLOW_COPY_AND_ASSIGN(KeyedStoreFastElementStub);
+};
+
+
+class TransitionElementsKindStub : public HydrogenCodeStub {
+ public:
+ TransitionElementsKindStub(ElementsKind from_kind,
+ ElementsKind to_kind) {
+ bit_field_ = FromKindBits::encode(from_kind) |
+ ToKindBits::encode(to_kind);
+ }
+
+ ElementsKind from_kind() const {
+ return FromKindBits::decode(bit_field_);
+ }
+
+ ElementsKind to_kind() const {
+ return ToKindBits::decode(bit_field_);
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ class FromKindBits: public BitField<ElementsKind, 8, 8> {};
+ class ToKindBits: public BitField<ElementsKind, 0, 8> {};
+ uint32_t bit_field_;
+
+ Major MajorKey() { return TransitionElementsKind; }
+ int NotMissMinorKey() { return bit_field_; }
+
+ DISALLOW_COPY_AND_ASSIGN(TransitionElementsKindStub);
+};
+
+
+enum ContextCheckMode {
+ CONTEXT_CHECK_REQUIRED,
+ CONTEXT_CHECK_NOT_REQUIRED,
+ LAST_CONTEXT_CHECK_MODE = CONTEXT_CHECK_NOT_REQUIRED
+};
+
+
+enum AllocationSiteOverrideMode {
+ DONT_OVERRIDE,
+ DISABLE_ALLOCATION_SITES,
+ LAST_ALLOCATION_SITE_OVERRIDE_MODE = DISABLE_ALLOCATION_SITES
+};
+
+
+class ArrayConstructorStubBase : public HydrogenCodeStub {
+ public:
+ ArrayConstructorStubBase(ElementsKind kind, ContextCheckMode context_mode,
+ AllocationSiteOverrideMode override_mode) {
+ // It only makes sense to override local allocation site behavior
+ // if there is a difference between the global allocation site policy
+ // for an ElementsKind and the desired usage of the stub.
+ ASSERT(override_mode != DISABLE_ALLOCATION_SITES ||
+ AllocationSite::GetMode(kind) == TRACK_ALLOCATION_SITE);
+ bit_field_ = ElementsKindBits::encode(kind) |
+ AllocationSiteOverrideModeBits::encode(override_mode) |
+ ContextCheckModeBits::encode(context_mode);
+ }
+
+ ElementsKind elements_kind() const {
+ return ElementsKindBits::decode(bit_field_);
+ }
+
+ AllocationSiteOverrideMode override_mode() const {
+ return AllocationSiteOverrideModeBits::decode(bit_field_);
+ }
+
+ ContextCheckMode context_mode() const {
+ return ContextCheckModeBits::decode(bit_field_);
+ }
+
+ virtual bool IsPregenerated() {
+ // We only pre-generate stubs that verify correct context
+ return context_mode() == CONTEXT_CHECK_REQUIRED;
+ }
+
+ static void GenerateStubsAheadOfTime(Isolate* isolate);
+ static void InstallDescriptors(Isolate* isolate);
+
+ // Parameters accessed via CodeStubGraphBuilder::GetParameter()
+ static const int kConstructor = 0;
+ static const int kPropertyCell = 1;
+
+ private:
+ int NotMissMinorKey() { return bit_field_; }
+
+ // Ensure data fits within available bits.
+ STATIC_ASSERT(LAST_ALLOCATION_SITE_OVERRIDE_MODE == 1);
+ STATIC_ASSERT(LAST_CONTEXT_CHECK_MODE == 1);
+
+ class ElementsKindBits: public BitField<ElementsKind, 0, 8> {};
+ class AllocationSiteOverrideModeBits: public
+ BitField<AllocationSiteOverrideMode, 8, 1> {}; // NOLINT
+ class ContextCheckModeBits: public BitField<ContextCheckMode, 9, 1> {};
+ uint32_t bit_field_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArrayConstructorStubBase);
+};
+
+
+class ArrayNoArgumentConstructorStub : public ArrayConstructorStubBase {
+ public:
+ ArrayNoArgumentConstructorStub(
+ ElementsKind kind,
+ ContextCheckMode context_mode = CONTEXT_CHECK_REQUIRED,
+ AllocationSiteOverrideMode override_mode = DONT_OVERRIDE)
+ : ArrayConstructorStubBase(kind, context_mode, override_mode) {
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Major MajorKey() { return ArrayNoArgumentConstructor; }
+
+ DISALLOW_COPY_AND_ASSIGN(ArrayNoArgumentConstructorStub);
+};
+
+
+class ArraySingleArgumentConstructorStub : public ArrayConstructorStubBase {
+ public:
+ ArraySingleArgumentConstructorStub(
+ ElementsKind kind,
+ ContextCheckMode context_mode = CONTEXT_CHECK_REQUIRED,
+ AllocationSiteOverrideMode override_mode = DONT_OVERRIDE)
+ : ArrayConstructorStubBase(kind, context_mode, override_mode) {
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Major MajorKey() { return ArraySingleArgumentConstructor; }
+
+ DISALLOW_COPY_AND_ASSIGN(ArraySingleArgumentConstructorStub);
+};
+
+
+class ArrayNArgumentsConstructorStub : public ArrayConstructorStubBase {
+ public:
+ ArrayNArgumentsConstructorStub(
+ ElementsKind kind,
+ ContextCheckMode context_mode = CONTEXT_CHECK_REQUIRED,
+ AllocationSiteOverrideMode override_mode = DONT_OVERRIDE)
+ : ArrayConstructorStubBase(kind, context_mode, override_mode) {
+ }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Major MajorKey() { return ArrayNArgumentsConstructor; }
+
+ DISALLOW_COPY_AND_ASSIGN(ArrayNArgumentsConstructorStub);
+};
+
+
+class InternalArrayConstructorStubBase : public HydrogenCodeStub {
+ public:
+ explicit InternalArrayConstructorStubBase(ElementsKind kind) {
+ kind_ = kind;
+ }
+
+ virtual bool IsPregenerated() { return true; }
+ static void GenerateStubsAheadOfTime(Isolate* isolate);
+ static void InstallDescriptors(Isolate* isolate);
+
+ // Parameters accessed via CodeStubGraphBuilder::GetParameter()
+ static const int kConstructor = 0;
+
+ ElementsKind elements_kind() const { return kind_; }
+
+ private:
+ int NotMissMinorKey() { return kind_; }
+
+ ElementsKind kind_;
+
+ DISALLOW_COPY_AND_ASSIGN(InternalArrayConstructorStubBase);
+};
+
+
+class InternalArrayNoArgumentConstructorStub : public
+ InternalArrayConstructorStubBase {
+ public:
+ explicit InternalArrayNoArgumentConstructorStub(ElementsKind kind)
+ : InternalArrayConstructorStubBase(kind) { }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Major MajorKey() { return InternalArrayNoArgumentConstructor; }
+
+ DISALLOW_COPY_AND_ASSIGN(InternalArrayNoArgumentConstructorStub);
+};
+
+
+class InternalArraySingleArgumentConstructorStub : public
+ InternalArrayConstructorStubBase {
+ public:
+ explicit InternalArraySingleArgumentConstructorStub(ElementsKind kind)
+ : InternalArrayConstructorStubBase(kind) { }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Major MajorKey() { return InternalArraySingleArgumentConstructor; }
+
+ DISALLOW_COPY_AND_ASSIGN(InternalArraySingleArgumentConstructorStub);
+};
+
+
+class InternalArrayNArgumentsConstructorStub : public
+ InternalArrayConstructorStubBase {
+ public:
+ explicit InternalArrayNArgumentsConstructorStub(ElementsKind kind)
+ : InternalArrayConstructorStubBase(kind) { }
+
+ virtual Handle<Code> GenerateCode();
+
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ Major MajorKey() { return InternalArrayNArgumentsConstructor; }
+
+ DISALLOW_COPY_AND_ASSIGN(InternalArrayNArgumentsConstructorStub);
+};
+
+
+class KeyedStoreElementStub : public PlatformCodeStub {
+ public:
+ KeyedStoreElementStub(bool is_js_array,
+ ElementsKind elements_kind,
+ KeyedAccessStoreMode store_mode)
+ : is_js_array_(is_js_array),
+ elements_kind_(elements_kind),
+ store_mode_(store_mode),
+ fp_registers_(CanUseFPRegisters()) { }
+
+ Major MajorKey() { return KeyedStoreElement; }
+ int MinorKey() {
+ return ElementsKindBits::encode(elements_kind_) |
+ IsJSArrayBits::encode(is_js_array_) |
+ StoreModeBits::encode(store_mode_) |
+ FPRegisters::encode(fp_registers_);
+ }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ class ElementsKindBits: public BitField<ElementsKind, 0, 8> {};
+ class StoreModeBits: public BitField<KeyedAccessStoreMode, 8, 4> {};
+ class IsJSArrayBits: public BitField<bool, 12, 1> {};
+ class FPRegisters: public BitField<bool, 13, 1> {};
+
+ bool is_js_array_;
+ ElementsKind elements_kind_;
+ KeyedAccessStoreMode store_mode_;
+ bool fp_registers_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyedStoreElementStub);
+};
+
+
+class ToBooleanStub: public HydrogenCodeStub {
+ public:
+ enum Type {
+ UNDEFINED,
+ BOOLEAN,
+ NULL_TYPE,
+ SMI,
+ SPEC_OBJECT,
+ STRING,
+ SYMBOL,
+ HEAP_NUMBER,
+ NUMBER_OF_TYPES
+ };
+
+ // At most 8 different types can be distinguished, because the Code object
+ // only has room for a single byte to hold a set of these types. :-P
+ STATIC_ASSERT(NUMBER_OF_TYPES <= 8);
+
+ class Types : public EnumSet<Type, byte> {
+ public:
+ Types() : EnumSet<Type, byte>(0) {}
+ explicit Types(byte bits) : EnumSet<Type, byte>(bits) {}
+
+ byte ToByte() const { return ToIntegral(); }
+ void Print(StringStream* stream) const;
+ bool UpdateStatus(Handle<Object> object);
+ bool NeedsMap() const;
+ bool CanBeUndetectable() const;
+ bool IsGeneric() const { return ToIntegral() == Generic().ToIntegral(); }
+
+ static Types Generic() { return Types((1 << NUMBER_OF_TYPES) - 1); }
+ };
+
+ explicit ToBooleanStub(Types types = Types())
+ : types_(types) { }
+ explicit ToBooleanStub(Code::ExtraICState state)
+ : types_(static_cast<byte>(state)) { }
+
+ bool UpdateStatus(Handle<Object> object);
+ Types GetTypes() { return types_; }
+
+ virtual Handle<Code> GenerateCode();
+ virtual void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ virtual Code::Kind GetCodeKind() const { return Code::TO_BOOLEAN_IC; }
+ virtual void PrintState(StringStream* stream);
+
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ static void InitializeForIsolate(Isolate* isolate) {
+ ToBooleanStub stub;
+ stub.InitializeInterfaceDescriptor(
+ isolate,
+ isolate->code_stub_interface_descriptor(CodeStub::ToBoolean));
+ }
+
+ static Handle<Code> GetUninitialized(Isolate* isolate) {
+ return ToBooleanStub(UNINITIALIZED).GetCode(isolate);
+ }
+
+ virtual Code::ExtraICState GetExtraICState() {
+ return types_.ToIntegral();
+ }
+
+ virtual InlineCacheState GetICState() {
+ if (types_.IsEmpty()) {
+ return ::v8::internal::UNINITIALIZED;
+ } else {
+ return MONOMORPHIC;
+ }
+ }
+
+ private:
+ Major MajorKey() { return ToBoolean; }
+ int NotMissMinorKey() { return GetExtraICState(); }
+
+ explicit ToBooleanStub(InitializationState init_state) :
+ HydrogenCodeStub(init_state) {}
+
+ Types types_;
+};
+
+
+class ElementsTransitionAndStoreStub : public HydrogenCodeStub {
+ public:
+ ElementsTransitionAndStoreStub(ElementsKind from_kind,
+ ElementsKind to_kind,
+ bool is_jsarray,
+ KeyedAccessStoreMode store_mode)
+ : from_kind_(from_kind),
+ to_kind_(to_kind),
+ is_jsarray_(is_jsarray),
+ store_mode_(store_mode) {}
+
+ ElementsKind from_kind() const { return from_kind_; }
+ ElementsKind to_kind() const { return to_kind_; }
+ bool is_jsarray() const { return is_jsarray_; }
+ KeyedAccessStoreMode store_mode() const { return store_mode_; }
+
+ Handle<Code> GenerateCode();
+
+ void InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor);
+
+ private:
+ class FromBits: public BitField<ElementsKind, 0, 8> {};
+ class ToBits: public BitField<ElementsKind, 8, 8> {};
+ class IsJSArrayBits: public BitField<bool, 16, 1> {};
+ class StoreModeBits: public BitField<KeyedAccessStoreMode, 17, 4> {};
+
+ Major MajorKey() { return ElementsTransitionAndStore; }
+ int NotMissMinorKey() {
+ return FromBits::encode(from_kind_) |
+ ToBits::encode(to_kind_) |
+ IsJSArrayBits::encode(is_jsarray_) |
+ StoreModeBits::encode(store_mode_);
+ }
+
+ ElementsKind from_kind_;
+ ElementsKind to_kind_;
+ bool is_jsarray_;
+ KeyedAccessStoreMode store_mode_;
+
+ DISALLOW_COPY_AND_ASSIGN(ElementsTransitionAndStoreStub);
+};
+
+
+class StoreArrayLiteralElementStub : public PlatformCodeStub {
+ public:
+ StoreArrayLiteralElementStub()
+ : fp_registers_(CanUseFPRegisters()) { }
+
+ private:
+ class FPRegisters: public BitField<bool, 0, 1> {};
+
+ Major MajorKey() { return StoreArrayLiteralElement; }
+ int MinorKey() { return FPRegisters::encode(fp_registers_); }
+
+ void Generate(MacroAssembler* masm);
+
+ bool fp_registers_;
+
+ DISALLOW_COPY_AND_ASSIGN(StoreArrayLiteralElementStub);
+};
+
+
+class StubFailureTrampolineStub : public PlatformCodeStub {
+ public:
+ explicit StubFailureTrampolineStub(StubFunctionMode function_mode)
+ : fp_registers_(CanUseFPRegisters()), function_mode_(function_mode) {}
+
+ virtual bool IsPregenerated() { return true; }
+
+ static void GenerateAheadOfTime(Isolate* isolate);
+
+ private:
+ class FPRegisters: public BitField<bool, 0, 1> {};
+ class FunctionModeField: public BitField<StubFunctionMode, 1, 1> {};
+
+ Major MajorKey() { return StubFailureTrampoline; }
+ int MinorKey() {
+ return FPRegisters::encode(fp_registers_) |
+ FunctionModeField::encode(function_mode_);
+ }
+
+ void Generate(MacroAssembler* masm);
+
+ bool fp_registers_;
+ StubFunctionMode function_mode_;
+
+ DISALLOW_COPY_AND_ASSIGN(StubFailureTrampolineStub);
+};
+
+
+class ProfileEntryHookStub : public PlatformCodeStub {
+ public:
+ explicit ProfileEntryHookStub() {}
+
+ // The profile entry hook function is not allowed to cause a GC.
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ // Generates a call to the entry hook if it's enabled.
+ static void MaybeCallEntryHook(MacroAssembler* masm);
+
+ private:
+ static void EntryHookTrampoline(intptr_t function,
+ intptr_t stack_pointer);
+
+ Major MajorKey() { return ProfileEntryHook; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileEntryHookStub);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_CODE_STUBS_H_
diff --git a/chromium/v8/src/code.h b/chromium/v8/src/code.h
new file mode 100644
index 00000000000..791420cf399
--- /dev/null
+++ b/chromium/v8/src/code.h
@@ -0,0 +1,74 @@
+// 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_CODE_H_
+#define V8_CODE_H_
+
+#include "allocation.h"
+#include "handles.h"
+#include "objects.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Wrapper class for passing expected and actual parameter counts as
+// either registers or immediate values. Used to make sure that the
+// caller provides exactly the expected number of parameters to the
+// callee.
+class ParameterCount BASE_EMBEDDED {
+ public:
+ explicit ParameterCount(Register reg)
+ : reg_(reg), immediate_(0) { }
+ explicit ParameterCount(int immediate)
+ : reg_(no_reg), immediate_(immediate) { }
+ explicit ParameterCount(Handle<JSFunction> f)
+ : reg_(no_reg), immediate_(f->shared()->formal_parameter_count()) { }
+
+ bool is_reg() const { return !reg_.is(no_reg); }
+ bool is_immediate() const { return !is_reg(); }
+
+ Register reg() const {
+ ASSERT(is_reg());
+ return reg_;
+ }
+ int immediate() const {
+ ASSERT(is_immediate());
+ return immediate_;
+ }
+
+ private:
+ const Register reg_;
+ const int immediate_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ParameterCount);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_CODE_H_
diff --git a/chromium/v8/src/codegen.cc b/chromium/v8/src/codegen.cc
new file mode 100644
index 00000000000..2031b321a4b
--- /dev/null
+++ b/chromium/v8/src/codegen.cc
@@ -0,0 +1,235 @@
+// Copyright 2012 the V8 project authors. 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 "bootstrapper.h"
+#include "codegen.h"
+#include "compiler.h"
+#include "cpu-profiler.h"
+#include "debug.h"
+#include "prettyprinter.h"
+#include "rewriter.h"
+#include "runtime.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm_)
+
+#ifdef DEBUG
+
+Comment::Comment(MacroAssembler* masm, const char* msg)
+ : masm_(masm), msg_(msg) {
+ __ RecordComment(msg);
+}
+
+
+Comment::~Comment() {
+ if (msg_[0] == '[') __ RecordComment("]");
+}
+
+#endif // DEBUG
+
+#undef __
+
+
+void CodeGenerator::MakeCodePrologue(CompilationInfo* info, const char* kind) {
+ bool print_source = false;
+ bool print_ast = false;
+ const char* ftype;
+
+ if (info->isolate()->bootstrapper()->IsActive()) {
+ print_source = FLAG_print_builtin_source;
+ print_ast = FLAG_print_builtin_ast;
+ ftype = "builtin";
+ } else {
+ print_source = FLAG_print_source;
+ print_ast = FLAG_print_ast;
+ ftype = "user-defined";
+ }
+
+ if (FLAG_trace_codegen || print_source || print_ast) {
+ PrintF("[generating %s code for %s function: ", kind, ftype);
+ if (info->IsStub()) {
+ const char* name =
+ CodeStub::MajorName(info->code_stub()->MajorKey(), true);
+ PrintF("%s", name == NULL ? "<unknown>" : name);
+ } else {
+ PrintF("%s", *info->function()->debug_name()->ToCString());
+ }
+ PrintF("]\n");
+ }
+
+#ifdef DEBUG
+ if (!info->IsStub() && print_source) {
+ PrintF("--- Source from AST ---\n%s\n",
+ PrettyPrinter().PrintProgram(info->function()));
+ }
+
+ if (!info->IsStub() && print_ast) {
+ PrintF("--- AST ---\n%s\n",
+ AstPrinter().PrintProgram(info->function()));
+ }
+#endif // DEBUG
+}
+
+
+Handle<Code> CodeGenerator::MakeCodeEpilogue(MacroAssembler* masm,
+ Code::Flags flags,
+ CompilationInfo* info) {
+ Isolate* isolate = info->isolate();
+
+ // Allocate and install the code.
+ CodeDesc desc;
+ bool is_crankshafted =
+ Code::ExtractKindFromFlags(flags) == Code::OPTIMIZED_FUNCTION ||
+ info->IsStub();
+ masm->GetCode(&desc);
+ Handle<Code> code =
+ isolate->factory()->NewCode(desc, flags, masm->CodeObject(),
+ false, is_crankshafted);
+ if (!code.is_null()) {
+ isolate->counters()->total_compiled_code_size()->Increment(
+ code->instruction_size());
+ code->set_prologue_offset(info->prologue_offset());
+ }
+ return code;
+}
+
+
+void CodeGenerator::PrintCode(Handle<Code> code, CompilationInfo* info) {
+#ifdef ENABLE_DISASSEMBLER
+ AllowDeferredHandleDereference allow_deference_for_print_code;
+ bool print_code = Isolate::Current()->bootstrapper()->IsActive()
+ ? FLAG_print_builtin_code
+ : (FLAG_print_code ||
+ (info->IsStub() && FLAG_print_code_stubs) ||
+ (info->IsOptimizing() && FLAG_print_opt_code));
+ if (print_code) {
+ // Print the source code if available.
+ FunctionLiteral* function = info->function();
+ if (code->kind() == Code::OPTIMIZED_FUNCTION) {
+ Handle<Script> script = info->script();
+ if (!script->IsUndefined() && !script->source()->IsUndefined()) {
+ PrintF("--- Raw source ---\n");
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(String::cast(script->source()),
+ &op,
+ function->start_position());
+ // fun->end_position() points to the last character in the stream. We
+ // need to compensate by adding one to calculate the length.
+ int source_len =
+ function->end_position() - function->start_position() + 1;
+ for (int i = 0; i < source_len; i++) {
+ if (stream.HasMore()) PrintF("%c", stream.GetNext());
+ }
+ PrintF("\n\n");
+ }
+ }
+ if (info->IsOptimizing()) {
+ if (FLAG_print_unopt_code) {
+ PrintF("--- Unoptimized code ---\n");
+ info->closure()->shared()->code()->Disassemble(
+ *function->debug_name()->ToCString());
+ }
+ PrintF("--- Optimized code ---\n");
+ } else {
+ PrintF("--- Code ---\n");
+ }
+ if (info->IsStub()) {
+ CodeStub::Major major_key = info->code_stub()->MajorKey();
+ code->Disassemble(CodeStub::MajorName(major_key, false));
+ } else {
+ code->Disassemble(*function->debug_name()->ToCString());
+ }
+ }
+#endif // ENABLE_DISASSEMBLER
+}
+
+
+bool CodeGenerator::ShouldGenerateLog(Expression* type) {
+ ASSERT(type != NULL);
+ Isolate* isolate = Isolate::Current();
+ if (!isolate->logger()->is_logging() &&
+ !isolate->cpu_profiler()->is_profiling()) {
+ return false;
+ }
+ Handle<String> name = Handle<String>::cast(type->AsLiteral()->value());
+ if (FLAG_log_regexp) {
+ if (name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("regexp")))
+ return true;
+ }
+ return false;
+}
+
+
+bool CodeGenerator::RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here) {
+ if (pos != RelocInfo::kNoPosition) {
+ masm->positions_recorder()->RecordStatementPosition(pos);
+ masm->positions_recorder()->RecordPosition(pos);
+ if (right_here) {
+ return masm->positions_recorder()->WriteRecordedPositions();
+ }
+ }
+ return false;
+}
+
+
+void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
+ switch (type_) {
+ case READ_ELEMENT:
+ GenerateReadElement(masm);
+ break;
+ case NEW_NON_STRICT_FAST:
+ GenerateNewNonStrictFast(masm);
+ break;
+ case NEW_NON_STRICT_SLOW:
+ GenerateNewNonStrictSlow(masm);
+ break;
+ case NEW_STRICT:
+ GenerateNewStrict(masm);
+ break;
+ }
+}
+
+
+int CEntryStub::MinorKey() {
+ int result = (save_doubles_ == kSaveFPRegs) ? 1 : 0;
+ ASSERT(result_size_ == 1 || result_size_ == 2);
+#ifdef _WIN64
+ return result | ((result_size_ == 1) ? 0 : 2);
+#else
+ return result;
+#endif
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/codegen.h b/chromium/v8/src/codegen.h
new file mode 100644
index 00000000000..ea202969162
--- /dev/null
+++ b/chromium/v8/src/codegen.h
@@ -0,0 +1,118 @@
+// Copyright 2012 the V8 project authors. 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_CODEGEN_H_
+#define V8_CODEGEN_H_
+
+#include "code-stubs.h"
+#include "runtime.h"
+#include "type-info.h"
+
+// Include the declaration of the architecture defined class CodeGenerator.
+// The contract to the shared code is that the the CodeGenerator is a subclass
+// of Visitor and that the following methods are available publicly:
+// MakeCode
+// MakeCodePrologue
+// MakeCodeEpilogue
+// masm
+// frame
+// script
+// has_valid_frame
+// SetFrame
+// DeleteFrame
+// allocator
+// AddDeferred
+// in_spilled_code
+// set_in_spilled_code
+// RecordPositions
+//
+// These methods are either used privately by the shared code or implemented as
+// shared code:
+// CodeGenerator
+// ~CodeGenerator
+// Generate
+// ComputeLazyCompile
+// BuildFunctionInfo
+// ProcessDeclarations
+// DeclareGlobals
+// CheckForInlineRuntimeCall
+// AnalyzeCondition
+// CodeForFunctionPosition
+// CodeForReturnPosition
+// CodeForStatementPosition
+// CodeForDoWhileConditionPosition
+// CodeForSourcePosition
+
+enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/codegen-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/codegen-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/codegen-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/codegen-mips.h"
+#else
+#error Unsupported target architecture.
+#endif
+
+namespace v8 {
+namespace internal {
+
+// Results of the library implementation of transcendental functions may differ
+// from the one we use in our generated code. Therefore we use the same
+// generated code both in runtime and compiled code.
+typedef double (*UnaryMathFunction)(double x);
+
+UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type);
+UnaryMathFunction CreateExpFunction();
+UnaryMathFunction CreateSqrtFunction();
+
+
+class ElementsTransitionGenerator : public AllStatic {
+ public:
+ // If |mode| is set to DONT_TRACK_ALLOCATION_SITE,
+ // |allocation_memento_found| may be NULL.
+ static void GenerateMapChangeElementsTransition(MacroAssembler* masm,
+ AllocationSiteMode mode,
+ Label* allocation_memento_found);
+ static void GenerateSmiToDouble(MacroAssembler* masm,
+ AllocationSiteMode mode,
+ Label* fail);
+ static void GenerateDoubleToObject(MacroAssembler* masm,
+ AllocationSiteMode mode,
+ Label* fail);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ElementsTransitionGenerator);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_CODEGEN_H_
diff --git a/chromium/v8/src/collection.js b/chromium/v8/src/collection.js
new file mode 100644
index 00000000000..01537e87b09
--- /dev/null
+++ b/chromium/v8/src/collection.js
@@ -0,0 +1,407 @@
+// Copyright 2012 the V8 project authors. 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.
+
+"use strict";
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js:
+// var $Array = global.Array;
+
+var $Set = global.Set;
+var $Map = global.Map;
+var $WeakMap = global.WeakMap;
+var $WeakSet = global.WeakSet;
+
+// Global sentinel to be used instead of undefined keys, which are not
+// supported internally but required for Harmony sets and maps.
+var undefined_sentinel = {};
+
+// -------------------------------------------------------------------
+// Harmony Set
+
+function SetConstructor() {
+ if (%_IsConstructCall()) {
+ %SetInitialize(this);
+ } else {
+ throw MakeTypeError('constructor_not_function', ['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;
+ }
+ if (%SetHas(this, key)) {
+ %SetDelete(this, key);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+function SetGetSize() {
+ if (!IS_SET(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['Set.prototype.size', this]);
+ }
+ return %SetGetSize(this);
+}
+
+
+function SetClear() {
+ if (!IS_SET(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['Set.prototype.clear', this]);
+ }
+ // Replace the internal table with a new empty table.
+ %SetInitialize(this);
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpSet() {
+ %CheckIsBootstrapping();
+
+ %SetCode($Set, SetConstructor);
+ %FunctionSetPrototype($Set, new $Object());
+ %SetProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
+
+ // Set up the non-enumerable functions on the Set prototype object.
+ InstallGetter($Set.prototype, "size", SetGetSize);
+ InstallFunctions($Set.prototype, DONT_ENUM, $Array(
+ "add", SetAdd,
+ "has", SetHas,
+ "delete", SetDelete,
+ "clear", SetClear
+ ));
+}
+
+SetUpSet();
+
+
+// -------------------------------------------------------------------
+// Harmony Map
+
+function MapConstructor() {
+ if (%_IsConstructCall()) {
+ %MapInitialize(this);
+ } else {
+ throw MakeTypeError('constructor_not_function', ['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 %MapHas(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;
+ }
+ return %MapDelete(this, key);
+}
+
+
+function MapGetSize() {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['Map.prototype.size', this]);
+ }
+ return %MapGetSize(this);
+}
+
+
+function MapClear() {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['Map.prototype.clear', this]);
+ }
+ // Replace the internal table with a new empty table.
+ %MapInitialize(this);
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpMap() {
+ %CheckIsBootstrapping();
+
+ %SetCode($Map, MapConstructor);
+ %FunctionSetPrototype($Map, new $Object());
+ %SetProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
+
+ // Set up the non-enumerable functions on the Map prototype object.
+ InstallGetter($Map.prototype, "size", MapGetSize);
+ InstallFunctions($Map.prototype, DONT_ENUM, $Array(
+ "get", MapGet,
+ "set", MapSet,
+ "has", MapHas,
+ "delete", MapDelete,
+ "clear", MapClear
+ ));
+}
+
+SetUpMap();
+
+
+// -------------------------------------------------------------------
+// Harmony WeakMap
+
+function WeakMapConstructor() {
+ if (%_IsConstructCall()) {
+ %WeakCollectionInitialize(this);
+ } else {
+ throw MakeTypeError('constructor_not_function', ['WeakMap']);
+ }
+}
+
+
+function WeakMapGet(key) {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakMap.prototype.get', this]);
+ }
+ if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) {
+ throw %MakeTypeError('invalid_weakmap_key', [this, key]);
+ }
+ return %WeakCollectionGet(this, key);
+}
+
+
+function WeakMapSet(key, value) {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakMap.prototype.set', this]);
+ }
+ if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) {
+ throw %MakeTypeError('invalid_weakmap_key', [this, key]);
+ }
+ return %WeakCollectionSet(this, key, value);
+}
+
+
+function WeakMapHas(key) {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakMap.prototype.has', this]);
+ }
+ if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) {
+ throw %MakeTypeError('invalid_weakmap_key', [this, key]);
+ }
+ return %WeakCollectionHas(this, key);
+}
+
+
+function WeakMapDelete(key) {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakMap.prototype.delete', this]);
+ }
+ if (!(IS_SPEC_OBJECT(key) || IS_SYMBOL(key))) {
+ throw %MakeTypeError('invalid_weakmap_key', [this, key]);
+ }
+ return %WeakCollectionDelete(this, key);
+}
+
+
+function WeakMapClear() {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakMap.prototype.clear', this]);
+ }
+ // Replace the internal table with a new empty table.
+ %WeakCollectionInitialize(this);
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpWeakMap() {
+ %CheckIsBootstrapping();
+
+ %SetCode($WeakMap, WeakMapConstructor);
+ %FunctionSetPrototype($WeakMap, new $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,
+ "clear", WeakMapClear
+ ));
+}
+
+SetUpWeakMap();
+
+
+// -------------------------------------------------------------------
+// Harmony WeakSet
+
+function WeakSetConstructor() {
+ if (%_IsConstructCall()) {
+ %WeakCollectionInitialize(this);
+ } else {
+ throw MakeTypeError('constructor_not_function', ['WeakSet']);
+ }
+}
+
+
+function WeakSetAdd(value) {
+ if (!IS_WEAKSET(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakSet.prototype.add', this]);
+ }
+ if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) {
+ throw %MakeTypeError('invalid_weakset_value', [this, value]);
+ }
+ return %WeakCollectionSet(this, value, true);
+}
+
+
+function WeakSetHas(value) {
+ if (!IS_WEAKSET(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakSet.prototype.has', this]);
+ }
+ if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) {
+ throw %MakeTypeError('invalid_weakset_value', [this, value]);
+ }
+ return %WeakCollectionHas(this, value);
+}
+
+
+function WeakSetDelete(value) {
+ if (!IS_WEAKSET(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakSet.prototype.delete', this]);
+ }
+ if (!(IS_SPEC_OBJECT(value) || IS_SYMBOL(value))) {
+ throw %MakeTypeError('invalid_weakset_value', [this, value]);
+ }
+ return %WeakCollectionDelete(this, value);
+}
+
+
+function WeakSetClear() {
+ if (!IS_WEAKSET(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['WeakSet.prototype.clear', this]);
+ }
+ // Replace the internal table with a new empty table.
+ %WeakCollectionInitialize(this);
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpWeakSet() {
+ %CheckIsBootstrapping();
+
+ %SetCode($WeakSet, WeakSetConstructor);
+ %FunctionSetPrototype($WeakSet, new $Object());
+ %SetProperty($WeakSet.prototype, "constructor", $WeakSet, DONT_ENUM);
+
+ // Set up the non-enumerable functions on the WeakSet prototype object.
+ InstallFunctions($WeakSet.prototype, DONT_ENUM, $Array(
+ "add", WeakSetAdd,
+ "has", WeakSetHas,
+ "delete", WeakSetDelete,
+ "clear", WeakSetClear
+ ));
+}
+
+SetUpWeakSet();
diff --git a/chromium/v8/src/compilation-cache.cc b/chromium/v8/src/compilation-cache.cc
new file mode 100644
index 00000000000..fffe5da71da
--- /dev/null
+++ b/chromium/v8/src/compilation-cache.cc
@@ -0,0 +1,535 @@
+// 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 "assembler.h"
+#include "compilation-cache.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+
+// The number of generations for each sub cache.
+// The number of ScriptGenerations is carefully chosen based on histograms.
+// See issue 458: http://code.google.com/p/v8/issues/detail?id=458
+static const int kScriptGenerations = 5;
+static const int kEvalGlobalGenerations = 2;
+static const int kEvalContextualGenerations = 2;
+static const int kRegExpGenerations = 2;
+
+// Initial size of each compilation cache table allocated.
+static const int kInitialCacheSize = 64;
+
+
+CompilationCache::CompilationCache(Isolate* isolate)
+ : isolate_(isolate),
+ script_(isolate, kScriptGenerations),
+ eval_global_(isolate, kEvalGlobalGenerations),
+ eval_contextual_(isolate, kEvalContextualGenerations),
+ reg_exp_(isolate, kRegExpGenerations),
+ enabled_(true) {
+ CompilationSubCache* subcaches[kSubCacheCount] =
+ {&script_, &eval_global_, &eval_contextual_, &reg_exp_};
+ for (int i = 0; i < kSubCacheCount; ++i) {
+ subcaches_[i] = subcaches[i];
+ }
+}
+
+
+CompilationCache::~CompilationCache() {}
+
+
+static Handle<CompilationCacheTable> AllocateTable(Isolate* isolate, int size) {
+ CALL_HEAP_FUNCTION(isolate,
+ CompilationCacheTable::Allocate(isolate->heap(), size),
+ CompilationCacheTable);
+}
+
+
+Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
+ ASSERT(generation < generations_);
+ Handle<CompilationCacheTable> result;
+ if (tables_[generation]->IsUndefined()) {
+ result = AllocateTable(isolate(), kInitialCacheSize);
+ tables_[generation] = *result;
+ } else {
+ CompilationCacheTable* table =
+ CompilationCacheTable::cast(tables_[generation]);
+ result = Handle<CompilationCacheTable>(table, isolate());
+ }
+ return result;
+}
+
+
+void CompilationSubCache::Age() {
+ // Age the generations implicitly killing off the oldest.
+ for (int i = generations_ - 1; i > 0; i--) {
+ tables_[i] = tables_[i - 1];
+ }
+
+ // Set the first generation as unborn.
+ tables_[0] = isolate()->heap()->undefined_value();
+}
+
+
+void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
+ Object* undefined = isolate()->heap()->undefined_value();
+ for (int i = 0; i < generations_; i++) {
+ if (tables_[i] != undefined) {
+ reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
+ }
+ }
+}
+
+
+void CompilationSubCache::Iterate(ObjectVisitor* v) {
+ v->VisitPointers(&tables_[0], &tables_[generations_]);
+}
+
+
+void CompilationSubCache::Clear() {
+ MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
+}
+
+
+void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
+ // Probe the script generation tables. Make sure not to leak handles
+ // into the caller's handle scope.
+ { HandleScope scope(isolate());
+ for (int generation = 0; generation < generations(); generation++) {
+ Handle<CompilationCacheTable> table = GetTable(generation);
+ table->Remove(*function_info);
+ }
+ }
+}
+
+
+CompilationCacheScript::CompilationCacheScript(Isolate* isolate,
+ int generations)
+ : CompilationSubCache(isolate, generations),
+ script_histogram_(NULL),
+ script_histogram_initialized_(false) { }
+
+
+// We only re-use a cached function for some script source code if the
+// script originates from the same place. This is to avoid issues
+// when reporting errors, etc.
+bool CompilationCacheScript::HasOrigin(
+ Handle<SharedFunctionInfo> function_info,
+ Handle<Object> name,
+ int line_offset,
+ int column_offset,
+ bool is_shared_cross_origin) {
+ Handle<Script> script =
+ Handle<Script>(Script::cast(function_info->script()), isolate());
+ // If the script name isn't set, the boilerplate script should have
+ // an undefined name to have the same origin.
+ if (name.is_null()) {
+ return script->name()->IsUndefined();
+ }
+ // Do the fast bailout checks first.
+ if (line_offset != script->line_offset()->value()) return false;
+ if (column_offset != script->column_offset()->value()) return false;
+ // Check that both names are strings. If not, no match.
+ if (!name->IsString() || !script->name()->IsString()) return false;
+ // Were both scripts tagged by the embedder as being shared cross-origin?
+ if (is_shared_cross_origin != script->is_shared_cross_origin()) return false;
+ // Compare the two name strings for equality.
+ return String::cast(*name)->Equals(String::cast(script->name()));
+}
+
+
+// TODO(245): Need to allow identical code from different contexts to
+// be cached in the same script generation. Currently the first use
+// will be cached, but subsequent code from different source / line
+// won't.
+Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(
+ Handle<String> source,
+ Handle<Object> name,
+ int line_offset,
+ int column_offset,
+ bool is_shared_cross_origin,
+ Handle<Context> context) {
+ Object* result = NULL;
+ int generation;
+
+ // Probe the script generation tables. Make sure not to leak handles
+ // into the caller's handle scope.
+ { HandleScope scope(isolate());
+ for (generation = 0; generation < generations(); generation++) {
+ Handle<CompilationCacheTable> table = GetTable(generation);
+ Handle<Object> probe(table->Lookup(*source, *context), isolate());
+ if (probe->IsSharedFunctionInfo()) {
+ Handle<SharedFunctionInfo> function_info =
+ Handle<SharedFunctionInfo>::cast(probe);
+ // Break when we've found a suitable shared function info that
+ // matches the origin.
+ if (HasOrigin(function_info,
+ name,
+ line_offset,
+ column_offset,
+ is_shared_cross_origin)) {
+ result = *function_info;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!script_histogram_initialized_) {
+ script_histogram_ = isolate()->stats_table()->CreateHistogram(
+ "V8.ScriptCache",
+ 0,
+ kScriptGenerations,
+ kScriptGenerations + 1);
+ script_histogram_initialized_ = true;
+ }
+
+ if (script_histogram_ != NULL) {
+ // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss.
+ isolate()->stats_table()->AddHistogramSample(script_histogram_, generation);
+ }
+
+ // Once outside the manacles of the handle scope, we need to recheck
+ // to see if we actually found a cached script. If so, we return a
+ // handle created in the caller's handle scope.
+ if (result != NULL) {
+ Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result),
+ isolate());
+ ASSERT(HasOrigin(shared,
+ name,
+ line_offset,
+ column_offset,
+ is_shared_cross_origin));
+ // If the script was found in a later generation, we promote it to
+ // the first generation to let it survive longer in the cache.
+ if (generation != 0) Put(source, context, shared);
+ isolate()->counters()->compilation_cache_hits()->Increment();
+ return shared;
+ } else {
+ isolate()->counters()->compilation_cache_misses()->Increment();
+ return Handle<SharedFunctionInfo>::null();
+ }
+}
+
+
+MaybeObject* CompilationCacheScript::TryTablePut(
+ Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info) {
+ Handle<CompilationCacheTable> table = GetFirstTable();
+ return table->Put(*source, *context, *function_info);
+}
+
+
+Handle<CompilationCacheTable> CompilationCacheScript::TablePut(
+ Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info) {
+ CALL_HEAP_FUNCTION(isolate(),
+ TryTablePut(source, context, function_info),
+ CompilationCacheTable);
+}
+
+
+void CompilationCacheScript::Put(Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info) {
+ HandleScope scope(isolate());
+ SetFirstTable(TablePut(source, context, function_info));
+}
+
+
+Handle<SharedFunctionInfo> CompilationCacheEval::Lookup(
+ Handle<String> source,
+ Handle<Context> context,
+ 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.
+ Object* result = NULL;
+ int generation;
+ { HandleScope scope(isolate());
+ for (generation = 0; generation < generations(); generation++) {
+ Handle<CompilationCacheTable> table = GetTable(generation);
+ result = table->LookupEval(
+ *source, *context, language_mode, scope_position);
+ if (result->IsSharedFunctionInfo()) {
+ break;
+ }
+ }
+ }
+ if (result->IsSharedFunctionInfo()) {
+ Handle<SharedFunctionInfo>
+ function_info(SharedFunctionInfo::cast(result), isolate());
+ if (generation != 0) {
+ Put(source, context, function_info, scope_position);
+ }
+ isolate()->counters()->compilation_cache_hits()->Increment();
+ return function_info;
+ } else {
+ isolate()->counters()->compilation_cache_misses()->Increment();
+ return Handle<SharedFunctionInfo>::null();
+ }
+}
+
+
+MaybeObject* CompilationCacheEval::TryTablePut(
+ Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info,
+ int scope_position) {
+ Handle<CompilationCacheTable> table = GetFirstTable();
+ return table->PutEval(*source, *context, *function_info, scope_position);
+}
+
+
+Handle<CompilationCacheTable> CompilationCacheEval::TablePut(
+ Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info,
+ int scope_position) {
+ CALL_HEAP_FUNCTION(isolate(),
+ TryTablePut(
+ source, context, function_info, scope_position),
+ CompilationCacheTable);
+}
+
+
+void CompilationCacheEval::Put(Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info,
+ int scope_position) {
+ HandleScope scope(isolate());
+ SetFirstTable(TablePut(source, context, function_info, scope_position));
+}
+
+
+Handle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
+ JSRegExp::Flags flags) {
+ // 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.
+ Object* result = NULL;
+ int generation;
+ { HandleScope scope(isolate());
+ for (generation = 0; generation < generations(); generation++) {
+ Handle<CompilationCacheTable> table = GetTable(generation);
+ result = table->LookupRegExp(*source, flags);
+ if (result->IsFixedArray()) {
+ break;
+ }
+ }
+ }
+ if (result->IsFixedArray()) {
+ Handle<FixedArray> data(FixedArray::cast(result), isolate());
+ if (generation != 0) {
+ Put(source, flags, data);
+ }
+ isolate()->counters()->compilation_cache_hits()->Increment();
+ return data;
+ } else {
+ isolate()->counters()->compilation_cache_misses()->Increment();
+ return Handle<FixedArray>::null();
+ }
+}
+
+
+MaybeObject* CompilationCacheRegExp::TryTablePut(
+ Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data) {
+ Handle<CompilationCacheTable> table = GetFirstTable();
+ return table->PutRegExp(*source, flags, *data);
+}
+
+
+Handle<CompilationCacheTable> CompilationCacheRegExp::TablePut(
+ Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data) {
+ CALL_HEAP_FUNCTION(isolate(),
+ TryTablePut(source, flags, data),
+ CompilationCacheTable);
+}
+
+
+void CompilationCacheRegExp::Put(Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data) {
+ HandleScope scope(isolate());
+ SetFirstTable(TablePut(source, flags, data));
+}
+
+
+void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
+ if (!IsEnabled()) return;
+
+ eval_global_.Remove(function_info);
+ eval_contextual_.Remove(function_info);
+ script_.Remove(function_info);
+}
+
+
+Handle<SharedFunctionInfo> CompilationCache::LookupScript(
+ Handle<String> source,
+ Handle<Object> name,
+ int line_offset,
+ int column_offset,
+ bool is_shared_cross_origin,
+ Handle<Context> context) {
+ if (!IsEnabled()) {
+ return Handle<SharedFunctionInfo>::null();
+ }
+
+ return script_.Lookup(source,
+ name,
+ line_offset,
+ column_offset,
+ is_shared_cross_origin,
+ context);
+}
+
+
+Handle<SharedFunctionInfo> CompilationCache::LookupEval(
+ Handle<String> source,
+ Handle<Context> context,
+ bool is_global,
+ LanguageMode language_mode,
+ int scope_position) {
+ if (!IsEnabled()) {
+ return Handle<SharedFunctionInfo>::null();
+ }
+
+ Handle<SharedFunctionInfo> result;
+ if (is_global) {
+ result = eval_global_.Lookup(
+ source, context, language_mode, scope_position);
+ } else {
+ ASSERT(scope_position != RelocInfo::kNoPosition);
+ result = eval_contextual_.Lookup(
+ source, context, language_mode, scope_position);
+ }
+ return result;
+}
+
+
+Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
+ JSRegExp::Flags flags) {
+ if (!IsEnabled()) {
+ return Handle<FixedArray>::null();
+ }
+
+ return reg_exp_.Lookup(source, flags);
+}
+
+
+void CompilationCache::PutScript(Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ script_.Put(source, context, function_info);
+}
+
+
+void CompilationCache::PutEval(Handle<String> source,
+ Handle<Context> context,
+ bool is_global,
+ Handle<SharedFunctionInfo> function_info,
+ int scope_position) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ HandleScope scope(isolate());
+ if (is_global) {
+ eval_global_.Put(source, context, function_info, scope_position);
+ } else {
+ ASSERT(scope_position != RelocInfo::kNoPosition);
+ eval_contextual_.Put(source, context, function_info, scope_position);
+ }
+}
+
+
+
+void CompilationCache::PutRegExp(Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ reg_exp_.Put(source, flags, data);
+}
+
+
+void CompilationCache::Clear() {
+ for (int i = 0; i < kSubCacheCount; i++) {
+ subcaches_[i]->Clear();
+ }
+}
+
+
+void CompilationCache::Iterate(ObjectVisitor* v) {
+ for (int i = 0; i < kSubCacheCount; i++) {
+ subcaches_[i]->Iterate(v);
+ }
+}
+
+
+void CompilationCache::IterateFunctions(ObjectVisitor* v) {
+ for (int i = 0; i < kSubCacheCount; i++) {
+ subcaches_[i]->IterateFunctions(v);
+ }
+}
+
+
+void CompilationCache::MarkCompactPrologue() {
+ for (int i = 0; i < kSubCacheCount; i++) {
+ subcaches_[i]->Age();
+ }
+}
+
+
+void CompilationCache::Enable() {
+ enabled_ = true;
+}
+
+
+void CompilationCache::Disable() {
+ enabled_ = false;
+ Clear();
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/compilation-cache.h b/chromium/v8/src/compilation-cache.h
new file mode 100644
index 00000000000..414e09e655c
--- /dev/null
+++ b/chromium/v8/src/compilation-cache.h
@@ -0,0 +1,306 @@
+// Copyright 2012 the V8 project authors. 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_COMPILATION_CACHE_H_
+#define V8_COMPILATION_CACHE_H_
+
+namespace v8 {
+namespace internal {
+
+// The compilation cache consists of several generational sub-caches which uses
+// this class as a base class. A sub-cache contains a compilation cache tables
+// for each generation of the sub-cache. Since the same source code string has
+// different compiled code for scripts and evals, we use separate sub-caches
+// for different compilation modes, to avoid retrieving the wrong result.
+class CompilationSubCache {
+ public:
+ CompilationSubCache(Isolate* isolate, int generations)
+ : isolate_(isolate),
+ generations_(generations) {
+ tables_ = NewArray<Object*>(generations);
+ }
+
+ ~CompilationSubCache() { DeleteArray(tables_); }
+
+ // Index for the first generation in the cache.
+ static const int kFirstGeneration = 0;
+
+ // Get the compilation cache tables for a specific generation.
+ Handle<CompilationCacheTable> GetTable(int generation);
+
+ // Accessors for first generation.
+ Handle<CompilationCacheTable> GetFirstTable() {
+ return GetTable(kFirstGeneration);
+ }
+ void SetFirstTable(Handle<CompilationCacheTable> value) {
+ ASSERT(kFirstGeneration < generations_);
+ tables_[kFirstGeneration] = *value;
+ }
+
+ // Age the sub-cache by evicting the oldest generation and creating a new
+ // young generation.
+ void Age();
+
+ // GC support.
+ void Iterate(ObjectVisitor* v);
+ void IterateFunctions(ObjectVisitor* v);
+
+ // Clear this sub-cache evicting all its content.
+ void Clear();
+
+ // Remove given shared function info from sub-cache.
+ void Remove(Handle<SharedFunctionInfo> function_info);
+
+ // Number of generations in this sub-cache.
+ inline int generations() { return generations_; }
+
+ protected:
+ Isolate* isolate() { return isolate_; }
+
+ private:
+ Isolate* isolate_;
+ int generations_; // Number of generations.
+ Object** tables_; // Compilation cache tables - one for each generation.
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationSubCache);
+};
+
+
+// Sub-cache for scripts.
+class CompilationCacheScript : public CompilationSubCache {
+ public:
+ CompilationCacheScript(Isolate* isolate, int generations);
+
+ Handle<SharedFunctionInfo> Lookup(Handle<String> source,
+ Handle<Object> name,
+ int line_offset,
+ int column_offset,
+ bool is_shared_cross_origin,
+ Handle<Context> context);
+ void Put(Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info);
+
+ private:
+ MUST_USE_RESULT MaybeObject* TryTablePut(
+ Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info);
+
+ // Note: Returns a new hash table if operation results in expansion.
+ Handle<CompilationCacheTable> TablePut(
+ Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info);
+
+ bool HasOrigin(Handle<SharedFunctionInfo> function_info,
+ Handle<Object> name,
+ int line_offset,
+ int column_offset,
+ bool is_shared_cross_origin);
+
+ void* script_histogram_;
+ bool script_histogram_initialized_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheScript);
+};
+
+
+// Sub-cache for eval scripts. Two caches for eval are used. One for eval calls
+// in native 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)
+ : CompilationSubCache(isolate, generations) { }
+
+ Handle<SharedFunctionInfo> Lookup(Handle<String> source,
+ Handle<Context> context,
+ LanguageMode language_mode,
+ int scope_position);
+
+ void Put(Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info,
+ int scope_position);
+
+ private:
+ MUST_USE_RESULT MaybeObject* TryTablePut(
+ Handle<String> source,
+ Handle<Context> context,
+ 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,
+ int scope_position);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval);
+};
+
+
+// Sub-cache for regular expressions.
+class CompilationCacheRegExp: public CompilationSubCache {
+ public:
+ CompilationCacheRegExp(Isolate* isolate, int generations)
+ : CompilationSubCache(isolate, generations) { }
+
+ Handle<FixedArray> Lookup(Handle<String> source, JSRegExp::Flags flags);
+
+ void Put(Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data);
+ private:
+ MUST_USE_RESULT MaybeObject* TryTablePut(Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data);
+
+ // Note: Returns a new hash table if operation results in expansion.
+ Handle<CompilationCacheTable> TablePut(Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheRegExp);
+};
+
+
+// The compilation cache keeps shared function infos for compiled
+// scripts and evals. The shared function infos are looked up using
+// the source string as the key. For regular expressions the
+// compilation data is cached.
+class CompilationCache {
+ public:
+ // Finds the script shared function info for a source
+ // string. Returns an empty handle if the cache doesn't contain a
+ // script for the given source string with the right origin.
+ Handle<SharedFunctionInfo> LookupScript(Handle<String> source,
+ Handle<Object> name,
+ int line_offset,
+ int column_offset,
+ bool is_shared_cross_origin,
+ Handle<Context> context);
+
+ // Finds the shared function info for a source string for eval in a
+ // given context. Returns an empty handle if the cache doesn't
+ // contain a script for the given source string.
+ Handle<SharedFunctionInfo> LookupEval(Handle<String> source,
+ Handle<Context> context,
+ bool is_global,
+ LanguageMode language_mode,
+ int scope_position);
+
+ // Returns the regexp data associated with the given regexp if it
+ // is in cache, otherwise an empty handle.
+ Handle<FixedArray> LookupRegExp(Handle<String> source,
+ JSRegExp::Flags flags);
+
+ // Associate the (source, kind) pair to the shared function
+ // info. This may overwrite an existing mapping.
+ void PutScript(Handle<String> source,
+ Handle<Context> context,
+ Handle<SharedFunctionInfo> function_info);
+
+ // Associate the (source, context->closure()->shared(), kind) triple
+ // with the shared function info. This may overwrite an existing mapping.
+ void PutEval(Handle<String> source,
+ Handle<Context> context,
+ bool is_global,
+ Handle<SharedFunctionInfo> function_info,
+ int scope_position);
+
+ // Associate the (source, flags) pair to the given regexp data.
+ // This may overwrite an existing mapping.
+ void PutRegExp(Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data);
+
+ // Clear the cache - also used to initialize the cache at startup.
+ void Clear();
+
+ // Remove given shared function info from all caches.
+ void Remove(Handle<SharedFunctionInfo> function_info);
+
+ // GC support.
+ void Iterate(ObjectVisitor* v);
+ void IterateFunctions(ObjectVisitor* v);
+
+ // Notify the cache that a mark-sweep garbage collection is about to
+ // take place. This is used to retire entries from the cache to
+ // avoid keeping them alive too long without using them.
+ void MarkCompactPrologue();
+
+ // Enable/disable compilation cache. Used by debugger to disable compilation
+ // cache during debugging to make sure new scripts are always compiled.
+ void Enable();
+ void Disable();
+
+ private:
+ explicit CompilationCache(Isolate* isolate);
+ ~CompilationCache();
+
+ HashMap* EagerOptimizingSet();
+
+ // The number of sub caches covering the different types to cache.
+ static const int kSubCacheCount = 4;
+
+ bool IsEnabled() { return FLAG_compilation_cache && enabled_; }
+
+ Isolate* isolate() { return isolate_; }
+
+ Isolate* isolate_;
+
+ CompilationCacheScript script_;
+ CompilationCacheEval eval_global_;
+ CompilationCacheEval eval_contextual_;
+ CompilationCacheRegExp reg_exp_;
+ CompilationSubCache* subcaches_[kSubCacheCount];
+
+ // Current enable state of the compilation cache.
+ bool enabled_;
+
+ friend class Isolate;
+
+ DISALLOW_COPY_AND_ASSIGN(CompilationCache);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_COMPILATION_CACHE_H_
diff --git a/chromium/v8/src/compiler-intrinsics.h b/chromium/v8/src/compiler-intrinsics.h
new file mode 100644
index 00000000000..b73e8ac750d
--- /dev/null
+++ b/chromium/v8/src/compiler-intrinsics.h
@@ -0,0 +1,94 @@
+// 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));
+
+ // Returns the number of bits set.
+ INLINE(static int CountSetBits(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);
+}
+
+int CompilerIntrinsics::CountSetBits(uint32_t value) {
+ return __builtin_popcount(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);
+}
+
+int CompilerIntrinsics::CountSetBits(uint32_t value) {
+ // Manually count set bits.
+ value = ((value >> 1) & 0x55555555) + (value & 0x55555555);
+ value = ((value >> 2) & 0x33333333) + (value & 0x33333333);
+ value = ((value >> 4) & 0x0f0f0f0f) + (value & 0x0f0f0f0f);
+ value = ((value >> 8) & 0x00ff00ff) + (value & 0x00ff00ff);
+ value = ((value >> 16) & 0x0000ffff) + (value & 0x0000ffff);
+ return value;
+}
+
+#else
+#error Unsupported compiler
+#endif
+
+} } // namespace v8::internal
+
+#endif // V8_COMPILER_INTRINSICS_H_
diff --git a/chromium/v8/src/compiler.cc b/chromium/v8/src/compiler.cc
new file mode 100644
index 00000000000..f6e5daac854
--- /dev/null
+++ b/chromium/v8/src/compiler.cc
@@ -0,0 +1,1275 @@
+// Copyright 2012 the V8 project authors. 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 "compiler.h"
+
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "compilation-cache.h"
+#include "cpu-profiler.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+#include "gdb-jit.h"
+#include "typing.h"
+#include "hydrogen.h"
+#include "isolate-inl.h"
+#include "lithium.h"
+#include "liveedit.h"
+#include "parser.h"
+#include "rewriter.h"
+#include "runtime-profiler.h"
+#include "scanner-character-streams.h"
+#include "scopeinfo.h"
+#include "scopes.h"
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+CompilationInfo::CompilationInfo(Handle<Script> script,
+ Zone* zone)
+ : flags_(LanguageModeField::encode(CLASSIC_MODE)),
+ script_(script),
+ osr_ast_id_(BailoutId::None()) {
+ Initialize(script->GetIsolate(), BASE, zone);
+}
+
+
+CompilationInfo::CompilationInfo(Handle<SharedFunctionInfo> shared_info,
+ Zone* zone)
+ : flags_(LanguageModeField::encode(CLASSIC_MODE) | IsLazy::encode(true)),
+ shared_info_(shared_info),
+ script_(Handle<Script>(Script::cast(shared_info->script()))),
+ osr_ast_id_(BailoutId::None()) {
+ Initialize(script_->GetIsolate(), BASE, zone);
+}
+
+
+CompilationInfo::CompilationInfo(Handle<JSFunction> closure,
+ Zone* zone)
+ : flags_(LanguageModeField::encode(CLASSIC_MODE) | IsLazy::encode(true)),
+ closure_(closure),
+ shared_info_(Handle<SharedFunctionInfo>(closure->shared())),
+ script_(Handle<Script>(Script::cast(shared_info_->script()))),
+ context_(closure->context()),
+ osr_ast_id_(BailoutId::None()) {
+ Initialize(script_->GetIsolate(), BASE, zone);
+}
+
+
+CompilationInfo::CompilationInfo(HydrogenCodeStub* stub,
+ Isolate* isolate,
+ Zone* zone)
+ : flags_(LanguageModeField::encode(CLASSIC_MODE) |
+ IsLazy::encode(true)),
+ osr_ast_id_(BailoutId::None()) {
+ Initialize(isolate, STUB, zone);
+ code_stub_ = stub;
+}
+
+
+void CompilationInfo::Initialize(Isolate* isolate,
+ Mode mode,
+ Zone* zone) {
+ isolate_ = isolate;
+ function_ = NULL;
+ scope_ = NULL;
+ global_scope_ = NULL;
+ extension_ = NULL;
+ pre_parse_data_ = NULL;
+ zone_ = zone;
+ deferred_handles_ = NULL;
+ code_stub_ = NULL;
+ prologue_offset_ = kPrologueOffsetNotSet;
+ opt_count_ = shared_info().is_null() ? 0 : shared_info()->opt_count();
+ no_frame_ranges_ = isolate->cpu_profiler()->is_profiling()
+ ? new List<OffsetRange>(2) : NULL;
+ for (int i = 0; i < DependentCode::kGroupCount; i++) {
+ dependencies_[i] = NULL;
+ }
+ if (mode == STUB) {
+ mode_ = STUB;
+ return;
+ }
+ mode_ = V8::UseCrankshaft() ? mode : NONOPT;
+ abort_due_to_dependency_ = false;
+ if (script_->type()->value() == Script::TYPE_NATIVE) {
+ MarkAsNative();
+ }
+ if (!shared_info_.is_null()) {
+ ASSERT(language_mode() == CLASSIC_MODE);
+ SetLanguageMode(shared_info_->language_mode());
+ }
+ set_bailout_reason(kUnknown);
+}
+
+
+CompilationInfo::~CompilationInfo() {
+ delete deferred_handles_;
+ delete no_frame_ranges_;
+#ifdef DEBUG
+ // Check that no dependent maps have been added or added dependent maps have
+ // been rolled back or committed.
+ for (int i = 0; i < DependentCode::kGroupCount; i++) {
+ ASSERT_EQ(NULL, dependencies_[i]);
+ }
+#endif // DEBUG
+}
+
+
+void CompilationInfo::CommitDependencies(Handle<Code> code) {
+ for (int i = 0; i < DependentCode::kGroupCount; i++) {
+ ZoneList<Handle<HeapObject> >* group_objects = dependencies_[i];
+ if (group_objects == NULL) continue;
+ ASSERT(!object_wrapper_.is_null());
+ for (int j = 0; j < group_objects->length(); j++) {
+ DependentCode::DependencyGroup group =
+ static_cast<DependentCode::DependencyGroup>(i);
+ DependentCode* dependent_code =
+ DependentCode::ForObject(group_objects->at(j), group);
+ dependent_code->UpdateToFinishedCode(group, this, *code);
+ }
+ dependencies_[i] = NULL; // Zone-allocated, no need to delete.
+ }
+}
+
+
+void CompilationInfo::RollbackDependencies() {
+ // Unregister from all dependent maps if not yet committed.
+ for (int i = 0; i < DependentCode::kGroupCount; i++) {
+ ZoneList<Handle<HeapObject> >* group_objects = dependencies_[i];
+ if (group_objects == NULL) continue;
+ for (int j = 0; j < group_objects->length(); j++) {
+ DependentCode::DependencyGroup group =
+ static_cast<DependentCode::DependencyGroup>(i);
+ DependentCode* dependent_code =
+ DependentCode::ForObject(group_objects->at(j), group);
+ dependent_code->RemoveCompilationInfo(group, this);
+ }
+ dependencies_[i] = NULL; // Zone-allocated, no need to delete.
+ }
+}
+
+
+int CompilationInfo::num_parameters() const {
+ ASSERT(!IsStub());
+ return scope()->num_parameters();
+}
+
+
+int CompilationInfo::num_heap_slots() const {
+ if (IsStub()) {
+ return 0;
+ } else {
+ return scope()->num_heap_slots();
+ }
+}
+
+
+Code::Flags CompilationInfo::flags() const {
+ if (IsStub()) {
+ return Code::ComputeFlags(code_stub()->GetCodeKind(),
+ code_stub()->GetICState(),
+ code_stub()->GetExtraICState(),
+ code_stub()->GetStubType(),
+ code_stub()->GetStubFlags());
+ } else {
+ return Code::ComputeFlags(Code::OPTIMIZED_FUNCTION);
+ }
+}
+
+
+// Disable optimization for the rest of the compilation pipeline.
+void CompilationInfo::DisableOptimization() {
+ bool is_optimizable_closure =
+ FLAG_optimize_closures &&
+ closure_.is_null() &&
+ !scope_->HasTrivialOuterContext() &&
+ !scope_->outer_scope_calls_non_strict_eval() &&
+ !scope_->inside_with();
+ SetMode(is_optimizable_closure ? BASE : NONOPT);
+}
+
+
+// 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 &&
+ !function()->flags()->Contains(kDontSelfOptimize) &&
+ !function()->flags()->Contains(kDontOptimize) &&
+ function()->scope()->AllowsLazyCompilation() &&
+ (shared_info().is_null() || !shared_info()->optimization_disabled());
+}
+
+
+void CompilationInfo::AbortOptimization() {
+ Handle<Code> code(shared_info()->code());
+ SetCode(code);
+}
+
+
+// Determine whether to use the full compiler for all code. If the flag
+// --always-full-compiler is specified this is the case. For the virtual frame
+// based compiler the full compiler is also used if a debugger is connected, as
+// the code from the full compiler supports mode precise break points. For the
+// crankshaft adaptive compiler debugging the optimized code is not possible at
+// all. However crankshaft support recompilation of functions, so in this case
+// the full compiler need not be be used if a debugger is attached, but only if
+// break points has actually been set.
+static bool IsDebuggerActive(Isolate* isolate) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ return V8::UseCrankshaft() ?
+ isolate->debug()->has_break_points() :
+ isolate->debugger()->IsDebuggerActive();
+#else
+ return false;
+#endif
+}
+
+
+static bool AlwaysFullCompiler(Isolate* isolate) {
+ return FLAG_always_full_compiler || IsDebuggerActive(isolate);
+}
+
+
+void OptimizingCompiler::RecordOptimizationStats() {
+ Handle<JSFunction> function = info()->closure();
+ int opt_count = function->shared()->opt_count();
+ function->shared()->set_opt_count(opt_count + 1);
+ double ms_creategraph =
+ static_cast<double>(time_taken_to_create_graph_) / 1000;
+ double ms_optimize = static_cast<double>(time_taken_to_optimize_) / 1000;
+ double ms_codegen = static_cast<double>(time_taken_to_codegen_) / 1000;
+ if (FLAG_trace_opt) {
+ PrintF("[optimizing ");
+ function->ShortPrint();
+ PrintF(" - took %0.3f, %0.3f, %0.3f ms]\n", ms_creategraph, ms_optimize,
+ ms_codegen);
+ }
+ if (FLAG_trace_opt_stats) {
+ static double compilation_time = 0.0;
+ static int compiled_functions = 0;
+ static int code_size = 0;
+
+ compilation_time += (ms_creategraph + ms_optimize + ms_codegen);
+ compiled_functions++;
+ code_size += function->shared()->SourceSize();
+ PrintF("Compiled: %d functions with %d byte source size in %fms.\n",
+ compiled_functions,
+ code_size,
+ compilation_time);
+ }
+ if (FLAG_hydrogen_stats) {
+ isolate()->GetHStatistics()->IncrementSubtotals(time_taken_to_create_graph_,
+ time_taken_to_optimize_,
+ time_taken_to_codegen_);
+ }
+}
+
+
+// A return value of true indicates the compilation pipeline is still
+// going, not necessarily that we optimized the code.
+static bool MakeCrankshaftCode(CompilationInfo* info) {
+ OptimizingCompiler compiler(info);
+ OptimizingCompiler::Status status = compiler.CreateGraph();
+
+ if (status != OptimizingCompiler::SUCCEEDED) {
+ return status != OptimizingCompiler::FAILED;
+ }
+ status = compiler.OptimizeGraph();
+ if (status != OptimizingCompiler::SUCCEEDED) {
+ status = compiler.AbortOptimization();
+ return status != OptimizingCompiler::FAILED;
+ }
+ status = compiler.GenerateAndInstallCode();
+ return status != OptimizingCompiler::FAILED;
+}
+
+
+OptimizingCompiler::Status OptimizingCompiler::CreateGraph() {
+ ASSERT(V8::UseCrankshaft());
+ ASSERT(info()->IsOptimizing());
+ ASSERT(!info()->IsCompilingForDebugging());
+
+ // We should never arrive here if there is no code object on the
+ // shared function object.
+ Handle<Code> code(info()->shared_info()->code());
+ ASSERT(code->kind() == Code::FUNCTION);
+
+ // We should never arrive here if optimization has been disabled on the
+ // shared function info.
+ ASSERT(!info()->shared_info()->optimization_disabled());
+
+ // 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(isolate())) {
+ info()->SetCode(code);
+ return SetLastStatus(BAILED_OUT);
+ }
+
+ // Limit the number of times we re-compile a functions with
+ // the optimizing compiler.
+ const int kMaxOptCount =
+ FLAG_deopt_every_n_times == 0 ? FLAG_max_opt_count : 1000;
+ if (info()->opt_count() > kMaxOptCount) {
+ info()->set_bailout_reason(kOptimizedTooManyTimes);
+ return AbortOptimization();
+ }
+
+ // Due to an encoding limit on LUnallocated operands in the Lithium
+ // language, we cannot optimize functions with too many formal parameters
+ // or perform on-stack replacement for function with too many
+ // stack-allocated local variables.
+ //
+ // The encoding is as a signed value, with parameters and receiver using
+ // the negative indices and locals the non-negative ones.
+ const int parameter_limit = -LUnallocated::kMinFixedSlotIndex;
+ Scope* scope = info()->scope();
+ if ((scope->num_parameters() + 1) > parameter_limit) {
+ info()->set_bailout_reason(kTooManyParameters);
+ return AbortOptimization();
+ }
+
+ const int locals_limit = LUnallocated::kMaxFixedSlotIndex;
+ if (!info()->osr_ast_id().IsNone() &&
+ scope->num_parameters() + 1 + scope->num_stack_slots() > locals_limit) {
+ info()->set_bailout_reason(kTooManyParametersLocals);
+ return AbortOptimization();
+ }
+
+ // Take --hydrogen-filter into account.
+ if (!info()->closure()->PassesHydrogenFilter()) {
+ info()->SetCode(code);
+ return SetLastStatus(BAILED_OUT);
+ }
+
+ // Recompile the unoptimized version of the code if the current version
+ // doesn't have deoptimization support. Alternatively, we may decide to
+ // run the full code generator to get a baseline for the compile-time
+ // performance of the hydrogen-based compiler.
+ bool should_recompile = !info()->shared_info()->has_deoptimization_support();
+ if (should_recompile || FLAG_hydrogen_stats) {
+ int64_t start_ticks = 0;
+ if (FLAG_hydrogen_stats) {
+ start_ticks = OS::Ticks();
+ }
+ CompilationInfoWithZone unoptimized(info()->shared_info());
+ // Note that we use the same AST that we will use for generating the
+ // optimized code.
+ unoptimized.SetFunction(info()->function());
+ unoptimized.SetScope(info()->scope());
+ unoptimized.SetContext(info()->context());
+ if (should_recompile) unoptimized.EnableDeoptimizationSupport();
+ bool succeeded = FullCodeGenerator::MakeCode(&unoptimized);
+ if (should_recompile) {
+ if (!succeeded) return SetLastStatus(FAILED);
+ Handle<SharedFunctionInfo> shared = info()->shared_info();
+ shared->EnableDeoptimizationSupport(*unoptimized.code());
+ // The existing unoptimized code was replaced with the new one.
+ Compiler::RecordFunctionCompilation(
+ Logger::LAZY_COMPILE_TAG, &unoptimized, shared);
+ }
+ if (FLAG_hydrogen_stats) {
+ int64_t ticks = OS::Ticks() - start_ticks;
+ isolate()->GetHStatistics()->IncrementFullCodeGen(ticks);
+ }
+ }
+
+ // Check that the unoptimized, shared code is ready for
+ // optimizations. When using the always_opt flag we disregard the
+ // optimizable marker in the code object and optimize anyway. This
+ // is safe as long as the unoptimized code has deoptimization
+ // support.
+ ASSERT(FLAG_always_opt || code->optimizable());
+ ASSERT(info()->shared_info()->has_deoptimization_support());
+
+ if (FLAG_trace_hydrogen) {
+ Handle<String> name = info()->function()->debug_name();
+ PrintF("-----------------------------------------------------------\n");
+ PrintF("Compiling method %s using hydrogen\n", *name->ToCString());
+ isolate()->GetHTracer()->TraceCompilation(info());
+ }
+
+ // Type-check the function.
+ AstTyper::Run(info());
+
+ graph_builder_ = new(info()->zone()) HOptimizedGraphBuilder(info());
+
+ Timer t(this, &time_taken_to_create_graph_);
+ graph_ = graph_builder_->CreateGraph();
+
+ if (isolate()->has_pending_exception()) {
+ info()->SetCode(Handle<Code>::null());
+ return SetLastStatus(FAILED);
+ }
+
+ // The function being compiled may have bailed out due to an inline
+ // candidate bailing out. In such a case, we don't disable
+ // optimization on the shared_info.
+ ASSERT(!graph_builder_->inline_bailout() || graph_ == NULL);
+ if (graph_ == NULL) {
+ if (graph_builder_->inline_bailout()) {
+ info_->AbortOptimization();
+ return SetLastStatus(BAILED_OUT);
+ } else {
+ return AbortOptimization();
+ }
+ }
+
+ if (info()->HasAbortedDueToDependencyChange()) {
+ info_->set_bailout_reason(kBailedOutDueToDependencyChange);
+ info_->AbortOptimization();
+ return SetLastStatus(BAILED_OUT);
+ }
+
+ return SetLastStatus(SUCCEEDED);
+}
+
+
+OptimizingCompiler::Status OptimizingCompiler::OptimizeGraph() {
+ DisallowHeapAllocation no_allocation;
+ DisallowHandleAllocation no_handles;
+ DisallowHandleDereference no_deref;
+ DisallowCodeDependencyChange no_dependency_change;
+
+ ASSERT(last_status() == SUCCEEDED);
+ Timer t(this, &time_taken_to_optimize_);
+ ASSERT(graph_ != NULL);
+ BailoutReason bailout_reason = kNoReason;
+ if (!graph_->Optimize(&bailout_reason)) {
+ if (bailout_reason == kNoReason) graph_builder_->Bailout(bailout_reason);
+ return SetLastStatus(BAILED_OUT);
+ } else {
+ chunk_ = LChunk::NewChunk(graph_);
+ if (chunk_ == NULL) {
+ return SetLastStatus(BAILED_OUT);
+ }
+ }
+ return SetLastStatus(SUCCEEDED);
+}
+
+
+OptimizingCompiler::Status OptimizingCompiler::GenerateAndInstallCode() {
+ ASSERT(last_status() == SUCCEEDED);
+ ASSERT(!info()->HasAbortedDueToDependencyChange());
+ DisallowCodeDependencyChange no_dependency_change;
+ { // Scope for timer.
+ Timer timer(this, &time_taken_to_codegen_);
+ ASSERT(chunk_ != NULL);
+ ASSERT(graph_ != NULL);
+ // Deferred handles reference objects that were accessible during
+ // graph creation. To make sure that we don't encounter inconsistencies
+ // between graph creation and code generation, we disallow accessing
+ // objects through deferred handles during the latter, with exceptions.
+ DisallowDeferredHandleDereference no_deferred_handle_deref;
+ Handle<Code> optimized_code = chunk_->Codegen();
+ if (optimized_code.is_null()) {
+ if (info()->bailout_reason() == kNoReason) {
+ info()->set_bailout_reason(kCodeGenerationFailed);
+ }
+ return AbortOptimization();
+ }
+ info()->SetCode(optimized_code);
+ }
+ RecordOptimizationStats();
+ return SetLastStatus(SUCCEEDED);
+}
+
+
+static bool GenerateCode(CompilationInfo* info) {
+ bool is_optimizing = V8::UseCrankshaft() &&
+ !info->IsCompilingForDebugging() &&
+ info->IsOptimizing();
+ if (is_optimizing) {
+ Logger::TimerEventScope timer(
+ info->isolate(), Logger::TimerEventScope::v8_recompile_synchronous);
+ return MakeCrankshaftCode(info);
+ } else {
+ if (info->IsOptimizing()) {
+ // Have the CompilationInfo decide if the compilation should be
+ // BASE or NONOPT.
+ info->DisableOptimization();
+ }
+ Logger::TimerEventScope timer(
+ info->isolate(), Logger::TimerEventScope::v8_compile_full_code);
+ return FullCodeGenerator::MakeCode(info);
+ }
+}
+
+
+static bool MakeCode(CompilationInfo* info) {
+ // Precondition: code has been parsed. Postcondition: the code field in
+ // the compilation info is set if compilation succeeded.
+ ASSERT(info->function() != NULL);
+ return Rewriter::Rewrite(info) && Scope::Analyze(info) && GenerateCode(info);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+bool Compiler::MakeCodeForLiveEdit(CompilationInfo* info) {
+ // Precondition: code has been parsed. Postcondition: the code field in
+ // the compilation info is set if compilation succeeded.
+ bool succeeded = MakeCode(info);
+ if (!info->shared_info().is_null()) {
+ Handle<ScopeInfo> scope_info = ScopeInfo::Create(info->scope(),
+ info->zone());
+ info->shared_info()->set_scope_info(*scope_info);
+ }
+ return succeeded;
+}
+#endif
+
+
+static bool DebuggerWantsEagerCompilation(CompilationInfo* info,
+ bool allow_lazy_without_ctx = false) {
+ return LiveEditFunctionTracker::IsActive(info->isolate()) ||
+ (info->isolate()->DebuggerHasBreakPoints() && !allow_lazy_without_ctx);
+}
+
+
+static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
+ Isolate* isolate = info->isolate();
+ PostponeInterruptsScope postpone(isolate);
+
+ ASSERT(!isolate->native_context().is_null());
+ Handle<Script> script = info->script();
+ // TODO(svenpanne) Obscure place for this, perhaps move to OnBeforeCompile?
+ FixedArray* array = isolate->native_context()->embedder_data();
+ script->set_context_data(array->get(0));
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (info->is_eval()) {
+ script->set_compilation_type(Script::COMPILATION_TYPE_EVAL);
+ // For eval scripts add information on the function from which eval was
+ // called.
+ if (info->is_eval()) {
+ StackTraceFrameIterator it(isolate);
+ if (!it.done()) {
+ script->set_eval_from_shared(it.frame()->function()->shared());
+ Code* code = it.frame()->LookupCode();
+ int offset = static_cast<int>(
+ it.frame()->pc() - code->instruction_start());
+ script->set_eval_from_instructions_offset(Smi::FromInt(offset));
+ }
+ }
+ }
+
+ // Notify debugger
+ isolate->debugger()->OnBeforeCompile(script);
+#endif
+
+ // Only allow non-global compiles for eval.
+ ASSERT(info->is_eval() || info->is_global());
+ {
+ Parser parser(info);
+ if ((info->pre_parse_data() != NULL ||
+ String::cast(script->source())->length() > FLAG_min_preparse_length) &&
+ !DebuggerWantsEagerCompilation(info))
+ parser.set_allow_lazy(true);
+ if (!parser.Parse()) {
+ 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
+ // parsing statistics.
+ HistogramTimer* rate = info->is_eval()
+ ? info->isolate()->counters()->compile_eval()
+ : info->isolate()->counters()->compile();
+ HistogramTimerScope timer(rate);
+
+ // Compile the code.
+ FunctionLiteral* lit = info->function();
+ LiveEditFunctionTracker live_edit_tracker(isolate, lit);
+ if (!MakeCode(info)) {
+ if (!isolate->has_pending_exception()) isolate->StackOverflow();
+ return Handle<SharedFunctionInfo>::null();
+ }
+
+ // Allocate function.
+ ASSERT(!info->code().is_null());
+ Handle<SharedFunctionInfo> result =
+ isolate->factory()->NewSharedFunctionInfo(
+ lit->name(),
+ lit->materialized_literal_count(),
+ lit->is_generator(),
+ info->code(),
+ ScopeInfo::Create(info->scope(), info->zone()));
+
+ ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
+ Compiler::SetFunctionInfo(result, lit, true, script);
+
+ if (script->name()->IsString()) {
+ PROFILE(isolate, CodeCreateEvent(
+ info->is_eval()
+ ? Logger::EVAL_TAG
+ : Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
+ *info->code(),
+ *result,
+ info,
+ String::cast(script->name())));
+ GDBJIT(AddCode(Handle<String>(String::cast(script->name())),
+ script,
+ info->code(),
+ info));
+ } else {
+ PROFILE(isolate, CodeCreateEvent(
+ info->is_eval()
+ ? Logger::EVAL_TAG
+ : Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
+ *info->code(),
+ *result,
+ info,
+ isolate->heap()->empty_string()));
+ GDBJIT(AddCode(Handle<String>(), script, info->code(), info));
+ }
+
+ // Hint to the runtime system used when allocating space for initial
+ // property space by setting the expected number of properties for
+ // the instances of the function.
+ SetExpectedNofPropertiesFromEstimate(result, lit->expected_property_count());
+
+ script->set_compilation_state(Script::COMPILATION_STATE_COMPILED);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Notify debugger
+ isolate->debugger()->OnAfterCompile(
+ script, Debugger::NO_AFTER_COMPILE_FLAGS);
+#endif
+
+ live_edit_tracker.RecordFunctionInfo(result, lit, info->zone());
+
+ return result;
+}
+
+
+Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
+ Handle<Object> script_name,
+ int line_offset,
+ int column_offset,
+ bool is_shared_cross_origin,
+ Handle<Context> context,
+ v8::Extension* extension,
+ ScriptDataImpl* pre_data,
+ Handle<Object> script_data,
+ NativesFlag natives) {
+ Isolate* isolate = source->GetIsolate();
+ int source_length = source->length();
+ isolate->counters()->total_load_size()->Increment(source_length);
+ isolate->counters()->total_compile_size()->Increment(source_length);
+
+ // The VM is in the COMPILER state until exiting this function.
+ VMState<COMPILER> state(isolate);
+
+ CompilationCache* compilation_cache = isolate->compilation_cache();
+
+ // Do a lookup in the compilation cache but not for extensions.
+ Handle<SharedFunctionInfo> result;
+ if (extension == NULL) {
+ result = compilation_cache->LookupScript(source,
+ script_name,
+ line_offset,
+ column_offset,
+ is_shared_cross_origin,
+ context);
+ }
+
+ if (result.is_null()) {
+ // No cache entry found. Do pre-parsing, if it makes sense, and compile
+ // the script.
+ // Building preparse data that is only used immediately after is only a
+ // saving if we might skip building the AST for lazily compiled functions.
+ // I.e., preparse data isn't relevant when the lazy flag is off, and
+ // 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.
+
+ // Create a script object describing the script to be compiled.
+ Handle<Script> script = isolate->factory()->NewScript(source);
+ if (natives == NATIVES_CODE) {
+ script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
+ }
+ if (!script_name.is_null()) {
+ script->set_name(*script_name);
+ script->set_line_offset(Smi::FromInt(line_offset));
+ script->set_column_offset(Smi::FromInt(column_offset));
+ }
+ script->set_is_shared_cross_origin(is_shared_cross_origin);
+
+ script->set_data(script_data.is_null() ? HEAP->undefined_value()
+ : *script_data);
+
+ // Compile the function and add it to the cache.
+ CompilationInfoWithZone info(script);
+ info.MarkAsGlobal();
+ info.SetExtension(extension);
+ info.SetPreParseData(pre_data);
+ info.SetContext(context);
+ if (FLAG_use_strict) {
+ info.SetLanguageMode(FLAG_harmony_scoping ? EXTENDED_MODE : STRICT_MODE);
+ }
+ result = MakeFunctionInfo(&info);
+ if (extension == NULL && !result.is_null() && !result->dont_cache()) {
+ compilation_cache->PutScript(source, context, result);
+ }
+ } else {
+ if (result->ic_age() != HEAP->global_ic_age()) {
+ result->ResetForNewContext(HEAP->global_ic_age());
+ }
+ }
+
+ if (result.is_null()) isolate->ReportPendingMessages();
+ return result;
+}
+
+
+Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
+ Handle<Context> context,
+ bool is_global,
+ LanguageMode language_mode,
+ ParseRestriction restriction,
+ int scope_position) {
+ Isolate* isolate = source->GetIsolate();
+ int source_length = source->length();
+ isolate->counters()->total_eval_size()->Increment(source_length);
+ isolate->counters()->total_compile_size()->Increment(source_length);
+
+ // The VM is in the COMPILER state until exiting this function.
+ VMState<COMPILER> state(isolate);
+
+ // Do a lookup in the compilation cache; if the entry is not there, invoke
+ // the compiler and add the result to the cache.
+ Handle<SharedFunctionInfo> result;
+ CompilationCache* compilation_cache = isolate->compilation_cache();
+ result = compilation_cache->LookupEval(source,
+ context,
+ is_global,
+ language_mode,
+ scope_position);
+
+ if (result.is_null()) {
+ // Create a script object describing the script to be compiled.
+ Handle<Script> script = isolate->factory()->NewScript(source);
+ CompilationInfoWithZone info(script);
+ info.MarkAsEval();
+ if (is_global) info.MarkAsGlobal();
+ info.SetLanguageMode(language_mode);
+ info.SetParseRestriction(restriction);
+ info.SetContext(context);
+ result = MakeFunctionInfo(&info);
+ if (!result.is_null()) {
+ // Explicitly disable optimization for eval code. We're not yet prepared
+ // to handle eval-code in the optimizing compiler.
+ result->DisableOptimization(kEval);
+
+ // 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(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());
+ if (!result->dont_cache()) {
+ compilation_cache->PutEval(
+ source, context, is_global, result, scope_position);
+ }
+ }
+ } else {
+ if (result->ic_age() != HEAP->global_ic_age()) {
+ result->ResetForNewContext(HEAP->global_ic_age());
+ }
+ }
+
+ return result;
+}
+
+
+static bool InstallFullCode(CompilationInfo* info) {
+ // 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 setting the code object last we avoid this.
+ Handle<SharedFunctionInfo> shared = info->shared_info();
+ Handle<Code> code = info->code();
+ CHECK(code->kind() == Code::FUNCTION);
+ Handle<JSFunction> function = info->closure();
+ Handle<ScopeInfo> scope_info =
+ ScopeInfo::Create(info->scope(), info->zone());
+ shared->set_scope_info(*scope_info);
+ shared->ReplaceCode(*code);
+ if (!function.is_null()) {
+ function->ReplaceCode(*code);
+ ASSERT(!function->IsOptimized());
+ }
+
+ // Set the expected number of properties for instances.
+ FunctionLiteral* lit = info->function();
+ int expected = lit->expected_property_count();
+ SetExpectedNofPropertiesFromEstimate(shared, expected);
+
+ // Check the function has compiled code.
+ ASSERT(shared->is_compiled());
+ shared->set_dont_optimize(lit->flags()->Contains(kDontOptimize));
+ shared->set_dont_inline(lit->flags()->Contains(kDontInline));
+ shared->set_ast_node_count(lit->ast_node_count());
+
+ if (V8::UseCrankshaft() &&
+ !function.is_null() &&
+ !shared->optimization_disabled()) {
+ // If we're asked to always optimize, we compile the optimized
+ // version of the function right away - unless the debugger is
+ // active as it makes no sense to compile optimized code then.
+ if (FLAG_always_opt &&
+ !Isolate::Current()->DebuggerHasBreakPoints()) {
+ CompilationInfoWithZone optimized(function);
+ optimized.SetOptimizing(BailoutId::None());
+ return Compiler::CompileLazy(&optimized);
+ }
+ }
+ return true;
+}
+
+
+static void InstallCodeCommon(CompilationInfo* info) {
+ Handle<SharedFunctionInfo> shared = info->shared_info();
+ Handle<Code> code = info->code();
+ ASSERT(!code.is_null());
+
+ // Set optimizable to false if this is disallowed by the shared
+ // function info, e.g., we might have flushed the code and must
+ // reset this bit when lazy compiling the code again.
+ if (shared->optimization_disabled()) code->set_optimizable(false);
+
+ if (shared->code() == *code) {
+ // Do not send compilation event for the same code twice.
+ return;
+ }
+ Compiler::RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, info, shared);
+}
+
+
+static void InsertCodeIntoOptimizedCodeMap(CompilationInfo* info) {
+ Handle<Code> code = info->code();
+ if (FLAG_cache_optimized_code &&
+ info->osr_ast_id().IsNone() &&
+ code->kind() == Code::OPTIMIZED_FUNCTION) {
+ Handle<JSFunction> function = info->closure();
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<FixedArray> literals(function->literals());
+ Handle<Context> native_context(function->context()->native_context());
+ SharedFunctionInfo::AddToOptimizedCodeMap(
+ shared, native_context, code, literals);
+ }
+}
+
+
+static bool InstallCodeFromOptimizedCodeMap(CompilationInfo* info) {
+ if (FLAG_cache_optimized_code &&
+ info->osr_ast_id().IsNone() &&
+ info->IsOptimizing()) {
+ Handle<SharedFunctionInfo> shared = info->shared_info();
+ Handle<JSFunction> function = info->closure();
+ ASSERT(!function.is_null());
+ Handle<Context> native_context(function->context()->native_context());
+ int index = shared->SearchOptimizedCodeMap(*native_context);
+ if (index > 0) {
+ if (FLAG_trace_opt) {
+ PrintF("[found optimized code for ");
+ function->ShortPrint();
+ PrintF("]\n");
+ }
+ // Caching of optimized code enabled and optimized code found.
+ shared->InstallFromOptimizedCodeMap(*function, index);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Compiler::CompileLazy(CompilationInfo* info) {
+ Isolate* isolate = info->isolate();
+
+ // The VM is in the COMPILER state until exiting this function.
+ VMState<COMPILER> state(isolate);
+
+ PostponeInterruptsScope postpone(isolate);
+
+ Handle<SharedFunctionInfo> shared = info->shared_info();
+ int compiled_size = shared->end_position() - shared->start_position();
+ isolate->counters()->total_compile_size()->Increment(compiled_size);
+
+ if (InstallCodeFromOptimizedCodeMap(info)) return true;
+
+ // Generate the AST for the lazily compiled function.
+ if (Parser::Parse(info)) {
+ // 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 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)) {
+ if (!isolate->has_pending_exception()) {
+ isolate->StackOverflow();
+ }
+ } else {
+ InstallCodeCommon(info);
+
+ if (info->IsOptimizing()) {
+ Handle<Code> code = info->code();
+ ASSERT(shared->scope_info() != ScopeInfo::Empty(isolate));
+ info->closure()->ReplaceCode(*code);
+ InsertCodeIntoOptimizedCodeMap(info);
+ return true;
+ } else {
+ return InstallFullCode(info);
+ }
+ }
+ }
+
+ ASSERT(info->code().is_null());
+ return false;
+}
+
+
+void Compiler::RecompileParallel(Handle<JSFunction> closure) {
+ ASSERT(closure->IsMarkedForParallelRecompilation());
+
+ Isolate* isolate = closure->GetIsolate();
+ // Here we prepare compile data for the parallel recompilation thread, but
+ // this still happens synchronously and interrupts execution.
+ Logger::TimerEventScope timer(
+ isolate, Logger::TimerEventScope::v8_recompile_synchronous);
+
+ if (!isolate->optimizing_compiler_thread()->IsQueueAvailable()) {
+ if (FLAG_trace_parallel_recompilation) {
+ PrintF(" ** Compilation queue full, will retry optimizing ");
+ closure->PrintName();
+ PrintF(" on next run.\n");
+ }
+ return;
+ }
+
+ SmartPointer<CompilationInfo> info(new CompilationInfoWithZone(closure));
+ VMState<COMPILER> state(isolate);
+ PostponeInterruptsScope postpone(isolate);
+
+ Handle<SharedFunctionInfo> shared = info->shared_info();
+ int compiled_size = shared->end_position() - shared->start_position();
+ isolate->counters()->total_compile_size()->Increment(compiled_size);
+ info->SetOptimizing(BailoutId::None());
+
+ {
+ CompilationHandleScope handle_scope(*info);
+
+ if (InstallCodeFromOptimizedCodeMap(*info)) {
+ return;
+ }
+
+ if (Parser::Parse(*info)) {
+ LanguageMode language_mode = info->function()->language_mode();
+ info->SetLanguageMode(language_mode);
+ shared->set_language_mode(language_mode);
+ info->SaveHandles();
+
+ if (Rewriter::Rewrite(*info) && Scope::Analyze(*info)) {
+ OptimizingCompiler* compiler =
+ new(info->zone()) OptimizingCompiler(*info);
+ OptimizingCompiler::Status status = compiler->CreateGraph();
+ if (status == OptimizingCompiler::SUCCEEDED) {
+ info.Detach();
+ shared->code()->set_profiler_ticks(0);
+ isolate->optimizing_compiler_thread()->QueueForOptimization(compiler);
+ } else if (status == OptimizingCompiler::BAILED_OUT) {
+ isolate->clear_pending_exception();
+ InstallFullCode(*info);
+ }
+ }
+ }
+ }
+
+ if (shared->code()->back_edges_patched_for_osr()) {
+ // At this point we either put the function on recompilation queue or
+ // aborted optimization. In either case we want to continue executing
+ // the unoptimized code without running into OSR. If the unoptimized
+ // code has been patched for OSR, unpatch it.
+ InterruptStub interrupt_stub;
+ Handle<Code> interrupt_code = interrupt_stub.GetCode(isolate);
+ Handle<Code> replacement_code =
+ isolate->builtins()->OnStackReplacement();
+ Deoptimizer::RevertInterruptCode(shared->code(),
+ *interrupt_code,
+ *replacement_code);
+ }
+
+ if (isolate->has_pending_exception()) isolate->clear_pending_exception();
+}
+
+
+void Compiler::InstallOptimizedCode(OptimizingCompiler* optimizing_compiler) {
+ SmartPointer<CompilationInfo> info(optimizing_compiler->info());
+ // The function may have already been optimized by OSR. Simply continue.
+ // Except when OSR already disabled optimization for some reason.
+ if (info->shared_info()->optimization_disabled()) {
+ info->AbortOptimization();
+ InstallFullCode(*info);
+ if (FLAG_trace_parallel_recompilation) {
+ PrintF(" ** aborting optimization for ");
+ info->closure()->PrintName();
+ PrintF(" as it has been disabled.\n");
+ }
+ ASSERT(!info->closure()->IsMarkedForInstallingRecompiledCode());
+ return;
+ }
+
+ Isolate* isolate = info->isolate();
+ VMState<COMPILER> state(isolate);
+ Logger::TimerEventScope timer(
+ isolate, Logger::TimerEventScope::v8_recompile_synchronous);
+ // If crankshaft succeeded, install the optimized code else install
+ // the unoptimized code.
+ OptimizingCompiler::Status status = optimizing_compiler->last_status();
+ if (info->HasAbortedDueToDependencyChange()) {
+ info->set_bailout_reason(kBailedOutDueToDependencyChange);
+ status = optimizing_compiler->AbortOptimization();
+ } else if (status != OptimizingCompiler::SUCCEEDED) {
+ info->set_bailout_reason(kFailedBailedOutLastTime);
+ status = optimizing_compiler->AbortOptimization();
+ } else if (isolate->DebuggerHasBreakPoints()) {
+ info->set_bailout_reason(kDebuggerIsActive);
+ status = optimizing_compiler->AbortOptimization();
+ } else {
+ status = optimizing_compiler->GenerateAndInstallCode();
+ ASSERT(status == OptimizingCompiler::SUCCEEDED ||
+ status == OptimizingCompiler::BAILED_OUT);
+ }
+
+ InstallCodeCommon(*info);
+ if (status == OptimizingCompiler::SUCCEEDED) {
+ Handle<Code> code = info->code();
+ ASSERT(info->shared_info()->scope_info() != ScopeInfo::Empty(isolate));
+ info->closure()->ReplaceCode(*code);
+ if (info->shared_info()->SearchOptimizedCodeMap(
+ info->closure()->context()->native_context()) == -1) {
+ InsertCodeIntoOptimizedCodeMap(*info);
+ }
+ if (FLAG_trace_parallel_recompilation) {
+ PrintF(" ** Optimized code for ");
+ info->closure()->PrintName();
+ PrintF(" installed.\n");
+ }
+ } else {
+ info->SetCode(Handle<Code>(info->shared_info()->code()));
+ InstallFullCode(*info);
+ }
+ // Optimized code is finally replacing unoptimized code. Reset the latter's
+ // profiler ticks to prevent too soon re-opt after a deopt.
+ info->shared_info()->code()->set_profiler_ticks(0);
+ ASSERT(!info->closure()->IsMarkedForInstallingRecompiledCode());
+}
+
+
+Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal,
+ Handle<Script> script) {
+ // Precondition: code has been parsed and scopes have been analyzed.
+ CompilationInfoWithZone info(script);
+ info.SetFunction(literal);
+ info.SetScope(literal->scope());
+ info.SetLanguageMode(literal->scope()->language_mode());
+
+ Isolate* isolate = info.isolate();
+ Factory* factory = isolate->factory();
+ LiveEditFunctionTracker live_edit_tracker(isolate, literal);
+ // Determine if the function can be lazily compiled. This is necessary to
+ // allow some of our builtin JS files to be lazily compiled. These
+ // builtins cannot be handled lazily by the parser, since we have to know
+ // if a function uses the special natives syntax, which is something the
+ // parser records.
+ // If the debugger requests compilation for break points, we cannot be
+ // aggressive about lazy compilation, because it might trigger compilation
+ // of functions without an outer context when setting a breakpoint through
+ // Debug::FindSharedFunctionInfoInScript.
+ bool allow_lazy_without_ctx = literal->AllowsLazyCompilationWithoutContext();
+ bool allow_lazy = literal->AllowsLazyCompilation() &&
+ !DebuggerWantsEagerCompilation(&info, allow_lazy_without_ctx);
+
+ Handle<ScopeInfo> scope_info(ScopeInfo::Empty(isolate));
+
+ // Generate code
+ if (FLAG_lazy && allow_lazy && !literal->is_parenthesized()) {
+ Handle<Code> code = isolate->builtins()->LazyCompile();
+ info.SetCode(code);
+ } else if (GenerateCode(&info)) {
+ ASSERT(!info.code().is_null());
+ scope_info = ScopeInfo::Create(info.scope(), info.zone());
+ } else {
+ return Handle<SharedFunctionInfo>::null();
+ }
+
+ // Create a shared function info object.
+ Handle<SharedFunctionInfo> result =
+ factory->NewSharedFunctionInfo(literal->name(),
+ literal->materialized_literal_count(),
+ literal->is_generator(),
+ info.code(),
+ scope_info);
+ SetFunctionInfo(result, literal, false, script);
+ RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, result);
+ result->set_allows_lazy_compilation(allow_lazy);
+ result->set_allows_lazy_compilation_without_context(allow_lazy_without_ctx);
+
+ // Set the expected number of properties for instances and return
+ // the resulting function.
+ SetExpectedNofPropertiesFromEstimate(result,
+ literal->expected_property_count());
+ live_edit_tracker.RecordFunctionInfo(result, literal, info.zone());
+ return result;
+}
+
+
+// Sets the function info on a function.
+// The start_position points to the first '(' character after the function name
+// in the full script source. When counting characters in the script source the
+// the first character is number 0 (not 1).
+void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info,
+ FunctionLiteral* lit,
+ bool is_toplevel,
+ Handle<Script> script) {
+ 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());
+ function_info->set_end_position(lit->end_position());
+ function_info->set_is_expression(lit->is_expression());
+ function_info->set_is_anonymous(lit->is_anonymous());
+ function_info->set_is_toplevel(is_toplevel);
+ function_info->set_inferred_name(*lit->inferred_name());
+ function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
+ function_info->set_allows_lazy_compilation_without_context(
+ lit->AllowsLazyCompilationWithoutContext());
+ 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_is_function(lit->is_function());
+ function_info->set_dont_optimize(lit->flags()->Contains(kDontOptimize));
+ function_info->set_dont_inline(lit->flags()->Contains(kDontInline));
+ function_info->set_dont_cache(lit->flags()->Contains(kDontCache));
+ function_info->set_is_generator(lit->is_generator());
+}
+
+
+void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag,
+ CompilationInfo* info,
+ Handle<SharedFunctionInfo> shared) {
+ // SharedFunctionInfo is passed separately, because if CompilationInfo
+ // was created using Script object, it will not have it.
+
+ // Log the code generation. If source information is available include
+ // script name and line number. Check explicitly whether logging is
+ // enabled as finding the line number is not free.
+ if (info->isolate()->logger()->is_logging_code_events() ||
+ info->isolate()->cpu_profiler()->is_profiling()) {
+ Handle<Script> script = info->script();
+ Handle<Code> code = info->code();
+ if (*code == info->isolate()->builtins()->builtin(Builtins::kLazyCompile))
+ return;
+ int line_num = GetScriptLineNumber(script, shared->start_position()) + 1;
+ USE(line_num);
+ if (script->name()->IsString()) {
+ PROFILE(info->isolate(),
+ CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
+ *code,
+ *shared,
+ info,
+ String::cast(script->name()),
+ line_num));
+ } else {
+ PROFILE(info->isolate(),
+ CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
+ *code,
+ *shared,
+ info,
+ info->isolate()->heap()->empty_string(),
+ line_num));
+ }
+ }
+
+ GDBJIT(AddCode(Handle<String>(shared->DebugName()),
+ Handle<Script>(info->script()),
+ Handle<Code>(info->code()),
+ info));
+}
+
+
+CompilationPhase::CompilationPhase(const char* name, CompilationInfo* info)
+ : name_(name), info_(info), zone_(info->isolate()) {
+ if (FLAG_hydrogen_stats) {
+ info_zone_start_allocation_size_ = info->zone()->allocation_size();
+ start_ticks_ = OS::Ticks();
+ }
+}
+
+
+CompilationPhase::~CompilationPhase() {
+ if (FLAG_hydrogen_stats) {
+ unsigned size = zone()->allocation_size();
+ size += info_->zone()->allocation_size() - info_zone_start_allocation_size_;
+ int64_t ticks = OS::Ticks() - start_ticks_;
+ isolate()->GetHStatistics()->SaveTiming(name_, ticks, size);
+ }
+}
+
+
+bool CompilationPhase::ShouldProduceTraceOutput() const {
+ // Trace if the appropriate trace flag is set and the phase name's first
+ // character is in the FLAG_trace_phase command line parameter.
+ bool tracing_on = info()->IsStub() ?
+ FLAG_trace_hydrogen_stubs :
+ FLAG_trace_hydrogen;
+ return (tracing_on &&
+ OS::StrChr(const_cast<char*>(FLAG_trace_phase), name_[0]) != NULL);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/compiler.h b/chromium/v8/src/compiler.h
new file mode 100644
index 00000000000..7d442f9d461
--- /dev/null
+++ b/chromium/v8/src/compiler.h
@@ -0,0 +1,654 @@
+// Copyright 2012 the V8 project authors. 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_H_
+#define V8_COMPILER_H_
+
+#include "allocation.h"
+#include "ast.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+static const int kPrologueOffsetNotSet = -1;
+
+class ScriptDataImpl;
+class HydrogenCodeStub;
+
+// ParseRestriction is used to restrict the set of valid statements in a
+// unit of compilation. Restriction violations cause a syntax error.
+enum ParseRestriction {
+ NO_PARSE_RESTRICTION, // All expressions are allowed.
+ ONLY_SINGLE_FUNCTION_LITERAL // Only a single FunctionLiteral expression.
+};
+
+struct OffsetRange {
+ OffsetRange(int from, int to) : from(from), to(to) {}
+ int from;
+ int to;
+};
+
+// CompilationInfo encapsulates some information known at compile time. It
+// is constructed based on the resources available at compile-time.
+class CompilationInfo {
+ public:
+ CompilationInfo(Handle<JSFunction> closure, Zone* zone);
+ virtual ~CompilationInfo();
+
+ Isolate* isolate() {
+ ASSERT(Isolate::Current() == isolate_);
+ return isolate_;
+ }
+ Zone* zone() { return zone_; }
+ 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_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_; }
+ Handle<Script> script() const { return script_; }
+ HydrogenCodeStub* code_stub() const {return code_stub_; }
+ v8::Extension* extension() const { return extension_; }
+ ScriptDataImpl* pre_parse_data() const { return pre_parse_data_; }
+ Handle<Context> context() const { return context_; }
+ BailoutId osr_ast_id() const { return osr_ast_id_; }
+ int opt_count() const { return opt_count_; }
+ int num_parameters() const;
+ int num_heap_slots() const;
+ Code::Flags flags() const;
+
+ void MarkAsEval() {
+ ASSERT(!is_lazy());
+ flags_ |= IsEval::encode(true);
+ }
+ void MarkAsGlobal() {
+ ASSERT(!is_lazy());
+ flags_ |= IsGlobal::encode(true);
+ }
+ 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());
+ flags_ |= IsInLoop::encode(true);
+ }
+ void MarkAsNative() {
+ flags_ |= IsNative::encode(true);
+ }
+
+ bool is_native() const {
+ return IsNative::decode(flags_);
+ }
+
+ bool is_calling() const {
+ return is_deferred_calling() || is_non_deferred_calling();
+ }
+
+ void MarkAsDeferredCalling() {
+ flags_ |= IsDeferredCalling::encode(true);
+ }
+
+ bool is_deferred_calling() const {
+ return IsDeferredCalling::decode(flags_);
+ }
+
+ void MarkAsNonDeferredCalling() {
+ flags_ |= IsNonDeferredCalling::encode(true);
+ }
+
+ bool is_non_deferred_calling() const {
+ return IsNonDeferredCalling::decode(flags_);
+ }
+
+ void MarkAsSavesCallerDoubles() {
+ flags_ |= SavesCallerDoubles::encode(true);
+ }
+
+ bool saves_caller_doubles() const {
+ return SavesCallerDoubles::decode(flags_);
+ }
+
+ void MarkAsRequiresFrame() {
+ flags_ |= RequiresFrame::encode(true);
+ }
+
+ bool requires_frame() const {
+ return RequiresFrame::decode(flags_);
+ }
+
+ void SetParseRestriction(ParseRestriction restriction) {
+ flags_ = ParseRestricitonField::update(flags_, restriction);
+ }
+
+ ParseRestriction parse_restriction() const {
+ return ParseRestricitonField::decode(flags_);
+ }
+
+ void SetFunction(FunctionLiteral* literal) {
+ ASSERT(function_ == NULL);
+ function_ = literal;
+ }
+ void SetScope(Scope* scope) {
+ 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());
+ extension_ = extension;
+ }
+ void SetPreParseData(ScriptDataImpl* pre_parse_data) {
+ ASSERT(!is_lazy());
+ pre_parse_data_ = pre_parse_data;
+ }
+ void SetContext(Handle<Context> context) {
+ context_ = context;
+ }
+ 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 ShouldTrapOnDeopt() const {
+ return (FLAG_trap_on_deopt && IsOptimizing()) ||
+ (FLAG_trap_on_stub_deopt && IsStub());
+ }
+
+ bool has_global_object() const {
+ return !closure().is_null() &&
+ (closure()->context()->global_object() != NULL);
+ }
+
+ GlobalObject* global_object() const {
+ return has_global_object() ? closure()->context()->global_object() : NULL;
+ }
+
+ // Accessors for the different compilation modes.
+ bool IsOptimizing() const { return mode_ == OPTIMIZE; }
+ bool IsOptimizable() const { return mode_ == BASE; }
+ bool IsStub() const { return mode_ == STUB; }
+ void SetOptimizing(BailoutId osr_ast_id) {
+ SetMode(OPTIMIZE);
+ osr_ast_id_ = osr_ast_id;
+ }
+ void DisableOptimization();
+
+ // Deoptimization support.
+ bool HasDeoptimizationSupport() const {
+ return SupportsDeoptimization::decode(flags_);
+ }
+ void EnableDeoptimizationSupport() {
+ ASSERT(IsOptimizable());
+ flags_ |= SupportsDeoptimization::encode(true);
+ }
+
+ // 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();
+
+ void set_deferred_handles(DeferredHandles* deferred_handles) {
+ ASSERT(deferred_handles_ == NULL);
+ deferred_handles_ = deferred_handles;
+ }
+
+ ZoneList<Handle<HeapObject> >* dependencies(
+ DependentCode::DependencyGroup group) {
+ if (dependencies_[group] == NULL) {
+ dependencies_[group] = new(zone_) ZoneList<Handle<HeapObject> >(2, zone_);
+ }
+ return dependencies_[group];
+ }
+
+ void CommitDependencies(Handle<Code> code);
+
+ void RollbackDependencies();
+
+ void SaveHandles() {
+ SaveHandle(&closure_);
+ SaveHandle(&shared_info_);
+ SaveHandle(&context_);
+ SaveHandle(&script_);
+ }
+
+ BailoutReason bailout_reason() const { return bailout_reason_; }
+ void set_bailout_reason(BailoutReason reason) { bailout_reason_ = reason; }
+
+ int prologue_offset() const {
+ ASSERT_NE(kPrologueOffsetNotSet, prologue_offset_);
+ return prologue_offset_;
+ }
+
+ void set_prologue_offset(int prologue_offset) {
+ ASSERT_EQ(kPrologueOffsetNotSet, prologue_offset_);
+ prologue_offset_ = prologue_offset;
+ }
+
+ // Adds offset range [from, to) where fp register does not point
+ // to the current frame base. Used in CPU profiler to detect stack
+ // samples where top frame is not set up.
+ inline void AddNoFrameRange(int from, int to) {
+ if (no_frame_ranges_) no_frame_ranges_->Add(OffsetRange(from, to));
+ }
+
+ List<OffsetRange>* ReleaseNoFrameRanges() {
+ List<OffsetRange>* result = no_frame_ranges_;
+ no_frame_ranges_ = NULL;
+ return result;
+ }
+
+ Handle<Foreign> object_wrapper() {
+ if (object_wrapper_.is_null()) {
+ object_wrapper_ =
+ isolate()->factory()->NewForeign(reinterpret_cast<Address>(this));
+ }
+ return object_wrapper_;
+ }
+
+ void AbortDueToDependencyChange() {
+ ASSERT(!isolate()->optimizing_compiler_thread()->IsOptimizerThread());
+ abort_due_to_dependency_ = true;
+ }
+
+ bool HasAbortedDueToDependencyChange() {
+ ASSERT(!isolate()->optimizing_compiler_thread()->IsOptimizerThread());
+ return abort_due_to_dependency_;
+ }
+
+ protected:
+ CompilationInfo(Handle<Script> script,
+ Zone* zone);
+ CompilationInfo(Handle<SharedFunctionInfo> shared_info,
+ Zone* zone);
+ CompilationInfo(HydrogenCodeStub* stub,
+ Isolate* isolate,
+ Zone* zone);
+
+ private:
+ Isolate* isolate_;
+
+ // Compilation mode.
+ // BASE is generated by the full codegen, optionally prepared for bailouts.
+ // OPTIMIZE is optimized code generated by the Hydrogen-based backend.
+ // NONOPT is generated by the full codegen and is not prepared for
+ // recompilation/bailouts. These functions are never recompiled.
+ enum Mode {
+ BASE,
+ OPTIMIZE,
+ NONOPT,
+ STUB
+ };
+
+ void Initialize(Isolate* isolate, Mode mode, Zone* zone);
+
+ void SetMode(Mode mode) {
+ ASSERT(V8::UseCrankshaft());
+ mode_ = mode;
+ }
+
+ // Flags using template class BitField<type, start, length>. All are
+ // false by default.
+ //
+ // Compilation is either eager or lazy.
+ class IsLazy: public BitField<bool, 0, 1> {};
+ // Flags that can be set for eager compilation.
+ class IsEval: public BitField<bool, 1, 1> {};
+ class IsGlobal: public BitField<bool, 2, 1> {};
+ // Flags that can be set for lazy compilation.
+ class IsInLoop: public BitField<bool, 3, 1> {};
+ // Strict mode - used in eager compilation.
+ 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> {};
+ // If the compiled code contains calls that require building a frame
+ class IsCalling: public BitField<bool, 9, 1> {};
+ // If the compiled code contains calls that require building a frame
+ class IsDeferredCalling: public BitField<bool, 10, 1> {};
+ // If the compiled code contains calls that require building a frame
+ class IsNonDeferredCalling: public BitField<bool, 11, 1> {};
+ // If the compiled code saves double caller registers that it clobbers.
+ class SavesCallerDoubles: public BitField<bool, 12, 1> {};
+ // If the set of valid statements is restricted.
+ class ParseRestricitonField: public BitField<ParseRestriction, 13, 1> {};
+ // If the function requires a frame (for unspecified reasons)
+ class RequiresFrame: public BitField<bool, 14, 1> {};
+
+ unsigned flags_;
+
+ // Fields filled in by the compilation pipeline.
+ // AST filled in by the parser.
+ FunctionLiteral* function_;
+ // 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_;
+ // For compiled stubs, the stub object
+ HydrogenCodeStub* code_stub_;
+ // The compiled code.
+ Handle<Code> code_;
+
+ // Possible initial inputs to the compilation process.
+ Handle<JSFunction> closure_;
+ Handle<SharedFunctionInfo> shared_info_;
+ Handle<Script> script_;
+
+ // Fields possibly needed for eager compilation, NULL by default.
+ v8::Extension* extension_;
+ ScriptDataImpl* pre_parse_data_;
+
+ // The context of the caller for eval code, and the global context for a
+ // global script. Will be a null handle otherwise.
+ Handle<Context> context_;
+
+ // Compilation mode flag and whether deoptimization is allowed.
+ Mode mode_;
+ BailoutId osr_ast_id_;
+
+ // Flag whether compilation needs to be aborted due to dependency change.
+ bool abort_due_to_dependency_;
+
+ // The zone from which the compilation pipeline working on this
+ // CompilationInfo allocates.
+ Zone* zone_;
+
+ DeferredHandles* deferred_handles_;
+
+ ZoneList<Handle<HeapObject> >* dependencies_[DependentCode::kGroupCount];
+
+ template<typename T>
+ void SaveHandle(Handle<T> *object) {
+ if (!object->is_null()) {
+ Handle<T> handle(*(*object));
+ *object = handle;
+ }
+ }
+
+ BailoutReason bailout_reason_;
+
+ int prologue_offset_;
+
+ List<OffsetRange>* no_frame_ranges_;
+
+ // A copy of shared_info()->opt_count() to avoid handle deref
+ // during graph optimization.
+ int opt_count_;
+
+ Handle<Foreign> object_wrapper_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompilationInfo);
+};
+
+
+// Exactly like a CompilationInfo, except also creates and enters a
+// Zone on construction and deallocates it on exit.
+class CompilationInfoWithZone: public CompilationInfo {
+ public:
+ explicit CompilationInfoWithZone(Handle<Script> script)
+ : CompilationInfo(script, &zone_),
+ zone_(script->GetIsolate()) {}
+ explicit CompilationInfoWithZone(Handle<SharedFunctionInfo> shared_info)
+ : CompilationInfo(shared_info, &zone_),
+ zone_(shared_info->GetIsolate()) {}
+ explicit CompilationInfoWithZone(Handle<JSFunction> closure)
+ : CompilationInfo(closure, &zone_),
+ zone_(closure->GetIsolate()) {}
+ CompilationInfoWithZone(HydrogenCodeStub* stub, Isolate* isolate)
+ : CompilationInfo(stub, isolate, &zone_),
+ zone_(isolate) {}
+
+ // Virtual destructor because a CompilationInfoWithZone has to exit the
+ // zone scope and get rid of dependent maps even when the destructor is
+ // called when cast as a CompilationInfo.
+ virtual ~CompilationInfoWithZone() {
+ RollbackDependencies();
+ }
+
+ private:
+ Zone zone_;
+};
+
+
+// A wrapper around a CompilationInfo that detaches the Handles from
+// the underlying DeferredHandleScope and stores them in info_ on
+// destruction.
+class CompilationHandleScope BASE_EMBEDDED {
+ public:
+ explicit CompilationHandleScope(CompilationInfo* info)
+ : deferred_(info->isolate()), info_(info) {}
+ ~CompilationHandleScope() {
+ info_->set_deferred_handles(deferred_.Detach());
+ }
+
+ private:
+ DeferredHandleScope deferred_;
+ CompilationInfo* info_;
+};
+
+
+class HGraph;
+class HOptimizedGraphBuilder;
+class LChunk;
+
+// A helper class that calls the three compilation phases in
+// Crankshaft and keeps track of its state. The three phases
+// CreateGraph, OptimizeGraph and GenerateAndInstallCode can either
+// fail, bail-out to the full code generator or succeed. Apart from
+// their return value, the status of the phase last run can be checked
+// using last_status().
+class OptimizingCompiler: public ZoneObject {
+ public:
+ explicit OptimizingCompiler(CompilationInfo* info)
+ : info_(info),
+ graph_builder_(NULL),
+ graph_(NULL),
+ chunk_(NULL),
+ time_taken_to_create_graph_(0),
+ time_taken_to_optimize_(0),
+ time_taken_to_codegen_(0),
+ last_status_(FAILED) { }
+
+ enum Status {
+ FAILED, BAILED_OUT, SUCCEEDED
+ };
+
+ MUST_USE_RESULT Status CreateGraph();
+ MUST_USE_RESULT Status OptimizeGraph();
+ MUST_USE_RESULT Status GenerateAndInstallCode();
+
+ Status last_status() const { return last_status_; }
+ CompilationInfo* info() const { return info_; }
+ Isolate* isolate() const { return info()->isolate(); }
+
+ MUST_USE_RESULT Status AbortOptimization() {
+ info_->AbortOptimization();
+ info_->shared_info()->DisableOptimization(info_->bailout_reason());
+ return SetLastStatus(BAILED_OUT);
+ }
+
+ private:
+ CompilationInfo* info_;
+ HOptimizedGraphBuilder* graph_builder_;
+ HGraph* graph_;
+ LChunk* chunk_;
+ int64_t time_taken_to_create_graph_;
+ int64_t time_taken_to_optimize_;
+ int64_t time_taken_to_codegen_;
+ Status last_status_;
+
+ MUST_USE_RESULT Status SetLastStatus(Status status) {
+ last_status_ = status;
+ return last_status_;
+ }
+ void RecordOptimizationStats();
+
+ struct Timer {
+ Timer(OptimizingCompiler* compiler, int64_t* location)
+ : compiler_(compiler),
+ start_(OS::Ticks()),
+ location_(location) { }
+
+ ~Timer() {
+ *location_ += (OS::Ticks() - start_);
+ }
+
+ OptimizingCompiler* compiler_;
+ int64_t start_;
+ int64_t* location_;
+ };
+};
+
+
+// The V8 compiler
+//
+// General strategy: Source code is translated into an anonymous function w/o
+// parameters which then can be executed. If the source code contains other
+// functions, they will be compiled and allocated as part of the compilation
+// of the source code.
+
+// Please note this interface returns shared function infos. This means you
+// need to call Factory::NewFunctionFromSharedFunctionInfo before you have a
+// real function with a context.
+
+class Compiler : public AllStatic {
+ public:
+ // 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.
+
+ // Compile a String source within a context.
+ static Handle<SharedFunctionInfo> Compile(Handle<String> source,
+ Handle<Object> script_name,
+ int line_offset,
+ int column_offset,
+ bool is_shared_cross_origin,
+ Handle<Context> context,
+ v8::Extension* extension,
+ ScriptDataImpl* pre_data,
+ Handle<Object> script_data,
+ NativesFlag is_natives_code);
+
+ // Compile a String source within a context for Eval.
+ static Handle<SharedFunctionInfo> CompileEval(Handle<String> source,
+ Handle<Context> context,
+ bool is_global,
+ LanguageMode language_mode,
+ ParseRestriction restriction,
+ 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.
+ static bool CompileLazy(CompilationInfo* info);
+
+ static void RecompileParallel(Handle<JSFunction> function);
+
+ // Compile a shared function info object (the function is possibly lazily
+ // compiled).
+ static Handle<SharedFunctionInfo> BuildFunctionInfo(FunctionLiteral* node,
+ Handle<Script> script);
+
+ // Set the function info for a newly compiled function.
+ static void SetFunctionInfo(Handle<SharedFunctionInfo> function_info,
+ FunctionLiteral* lit,
+ bool is_toplevel,
+ Handle<Script> script);
+
+ static void InstallOptimizedCode(OptimizingCompiler* info);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ static bool MakeCodeForLiveEdit(CompilationInfo* info);
+#endif
+
+ static void RecordFunctionCompilation(Logger::LogEventsAndTags tag,
+ CompilationInfo* info,
+ Handle<SharedFunctionInfo> shared);
+};
+
+
+class CompilationPhase BASE_EMBEDDED {
+ public:
+ CompilationPhase(const char* name, CompilationInfo* info);
+ ~CompilationPhase();
+
+ protected:
+ bool ShouldProduceTraceOutput() const;
+
+ const char* name() const { return name_; }
+ CompilationInfo* info() const { return info_; }
+ Isolate* isolate() const { return info()->isolate(); }
+ Zone* zone() { return &zone_; }
+
+ private:
+ const char* name_;
+ CompilationInfo* info_;
+ Zone zone_;
+ unsigned info_zone_start_allocation_size_;
+ int64_t start_ticks_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompilationPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_COMPILER_H_
diff --git a/chromium/v8/src/contexts.cc b/chromium/v8/src/contexts.cc
new file mode 100644
index 00000000000..0fddfdf5058
--- /dev/null
+++ b/chromium/v8/src/contexts.cc
@@ -0,0 +1,365 @@
+// 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 "bootstrapper.h"
+#include "debug.h"
+#include "scopeinfo.h"
+
+namespace v8 {
+namespace internal {
+
+Context* Context::declaration_context() {
+ Context* current = this;
+ while (!current->IsFunctionContext() && !current->IsNativeContext()) {
+ current = current->previous();
+ ASSERT(current->closure() == closure());
+ }
+ return current;
+}
+
+
+JSBuiltinsObject* Context::builtins() {
+ GlobalObject* object = global_object();
+ if (object->IsJSGlobalObject()) {
+ return JSGlobalObject::cast(object)->builtins();
+ } else {
+ ASSERT(object->IsJSBuiltinsObject());
+ return JSBuiltinsObject::cast(object);
+ }
+}
+
+
+Context* Context::global_context() {
+ Context* current = this;
+ while (!current->IsGlobalContext()) {
+ current = current->previous();
+ }
+ return current;
+}
+
+
+Context* Context::native_context() {
+ // Fast case: the global object for this context has been set. In
+ // that case, the global object has a direct pointer to the global
+ // context.
+ if (global_object()->IsGlobalObject()) {
+ return global_object()->native_context();
+ }
+
+ // During bootstrapping, the global object might not be set and we
+ // have to search the context chain to find the native context.
+ ASSERT(Isolate::Current()->bootstrapper()->IsActive());
+ Context* current = this;
+ while (!current->IsNativeContext()) {
+ JSFunction* closure = JSFunction::cast(current->closure());
+ current = Context::cast(closure->context());
+ }
+ return current;
+}
+
+
+JSObject* Context::global_proxy() {
+ return native_context()->global_proxy_object();
+}
+
+
+void Context::set_global_proxy(JSObject* object) {
+ native_context()->set_global_proxy_object(object);
+}
+
+
+Handle<Object> Context::Lookup(Handle<String> name,
+ ContextLookupFlags flags,
+ 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;
+ *attributes = ABSENT;
+ *binding_flags = MISSING_BINDING;
+
+ if (FLAG_trace_contexts) {
+ PrintF("Context::Lookup(");
+ name->ShortPrint();
+ PrintF(")\n");
+ }
+
+ do {
+ if (FLAG_trace_contexts) {
+ PrintF(" - looking in context %p", reinterpret_cast<void*>(*context));
+ if (context->IsNativeContext()) PrintF(" (native context)");
+ PrintF("\n");
+ }
+
+ // 1. Check global objects, subjects of with, and extension objects.
+ if (context->IsNativeContext() ||
+ context->IsWithContext() ||
+ (context->IsFunctionContext() && context->has_extension())) {
+ Handle<JSReceiver> object(
+ JSReceiver::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 {
+ *attributes = object->GetPropertyAttribute(*name);
+ }
+ if (isolate->has_pending_exception()) return Handle<Object>();
+
+ if (*attributes != ABSENT) {
+ if (FLAG_trace_contexts) {
+ PrintF("=> found property in context object %p\n",
+ reinterpret_cast<void*>(*object));
+ }
+ return object;
+ }
+ }
+
+ // 2. Check the context proper if it has slots.
+ if (context->IsFunctionContext() || context->IsBlockContext()) {
+ // Use serialized scope information of functions and blocks to search
+ // for the context index.
+ Handle<ScopeInfo> scope_info;
+ if (context->IsFunctionContext()) {
+ scope_info = Handle<ScopeInfo>(
+ context->closure()->shared()->scope_info(), isolate);
+ } else {
+ scope_info = Handle<ScopeInfo>(
+ ScopeInfo::cast(context->extension()), isolate);
+ }
+ 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",
+ slot_index, mode);
+ }
+ *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
+ // scope. Thus, the DYNAMIC mode (which corresponds to dynamically
+ // declared variables that were introduced through declaration nodes)
+ // must not appear here.
+ switch (mode) {
+ case INTERNAL: // Fall through.
+ case VAR:
+ *attributes = NONE;
+ *binding_flags = MUTABLE_IS_INITIALIZED;
+ break;
+ case LET:
+ *attributes = NONE;
+ *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 CONST_HARMONY:
+ *attributes = READ_ONLY;
+ *binding_flags = (init_flag == kNeedsInitialization)
+ ? IMMUTABLE_CHECK_INITIALIZED_HARMONY :
+ IMMUTABLE_IS_INITIALIZED_HARMONY;
+ break;
+ case MODULE:
+ *attributes = READ_ONLY;
+ *binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY;
+ break;
+ case DYNAMIC:
+ case DYNAMIC_GLOBAL:
+ case DYNAMIC_LOCAL:
+ case TEMPORARY:
+ UNREACHABLE();
+ break;
+ }
+ return context;
+ }
+
+ // Check the slot corresponding to the intermediate context holding
+ // only the function name variable.
+ 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",
+ function_index);
+ }
+ *index = function_index;
+ *attributes = READ_ONLY;
+ 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;
+ }
+ }
+
+ // 3. Prepare to continue with the previous (next outermost) context.
+ if (context->IsNativeContext()) {
+ follow_context_chain = false;
+ } else {
+ context = Handle<Context>(context->previous(), isolate);
+ }
+ } while (follow_context_chain);
+
+ if (FLAG_trace_contexts) {
+ PrintF("=> no property/slot found\n");
+ }
+ return Handle<Object>::null();
+}
+
+
+void Context::AddOptimizedFunction(JSFunction* function) {
+ ASSERT(IsNativeContext());
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
+ while (!element->IsUndefined()) {
+ CHECK(element != function);
+ element = JSFunction::cast(element)->next_function_link();
+ }
+ }
+
+ // Check that the context belongs to the weak native contexts list.
+ bool found = false;
+ Object* context = GetHeap()->native_contexts_list();
+ while (!context->IsUndefined()) {
+ if (context == this) {
+ found = true;
+ break;
+ }
+ context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
+ }
+ CHECK(found);
+#endif
+
+ // If the function link field is already used then the function was
+ // enqueued as a code flushing candidate and we remove it now.
+ if (!function->next_function_link()->IsUndefined()) {
+ CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
+ flusher->EvictCandidate(function);
+ }
+
+ ASSERT(function->next_function_link()->IsUndefined());
+
+ function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST));
+ set(OPTIMIZED_FUNCTIONS_LIST, function);
+}
+
+
+void Context::RemoveOptimizedFunction(JSFunction* function) {
+ ASSERT(IsNativeContext());
+ Object* element = get(OPTIMIZED_FUNCTIONS_LIST);
+ JSFunction* prev = NULL;
+ while (!element->IsUndefined()) {
+ JSFunction* element_function = JSFunction::cast(element);
+ ASSERT(element_function->next_function_link()->IsUndefined() ||
+ element_function->next_function_link()->IsJSFunction());
+ if (element_function == function) {
+ if (prev == NULL) {
+ set(OPTIMIZED_FUNCTIONS_LIST, element_function->next_function_link());
+ } else {
+ prev->set_next_function_link(element_function->next_function_link());
+ }
+ element_function->set_next_function_link(GetHeap()->undefined_value());
+ return;
+ }
+ prev = element_function;
+ element = element_function->next_function_link();
+ }
+ UNREACHABLE();
+}
+
+
+Object* Context::OptimizedFunctionsListHead() {
+ ASSERT(IsNativeContext());
+ return get(OPTIMIZED_FUNCTIONS_LIST);
+}
+
+
+void Context::ClearOptimizedFunctions() {
+ set(OPTIMIZED_FUNCTIONS_LIST, GetHeap()->undefined_value());
+}
+
+
+Handle<Object> Context::ErrorMessageForCodeGenerationFromStrings() {
+ Handle<Object> result(error_message_for_code_gen_from_strings(),
+ GetIsolate());
+ if (!result->IsUndefined()) return result;
+ return GetIsolate()->factory()->NewStringFromAscii(i::CStrVector(
+ "Code generation from strings disallowed for this context"));
+}
+
+
+#ifdef DEBUG
+bool Context::IsBootstrappingOrValidParentContext(
+ Object* object, Context* child) {
+ // During bootstrapping we allow all objects to pass as
+ // contexts. This is necessary to fix circular dependencies.
+ if (child->GetIsolate()->bootstrapper()->IsActive()) return true;
+ if (!object->IsContext()) return false;
+ Context* context = Context::cast(object);
+ return context->IsNativeContext() || context->IsGlobalContext() ||
+ context->IsModuleContext() || !child->IsModuleContext();
+}
+
+
+bool Context::IsBootstrappingOrGlobalObject(Object* object) {
+ // During bootstrapping we allow all objects to pass as global
+ // objects. This is necessary to fix circular dependencies.
+ Isolate* isolate = Isolate::Current();
+ return isolate->heap()->gc_state() != Heap::NOT_IN_GC ||
+ isolate->bootstrapper()->IsActive() ||
+ object->IsGlobalObject();
+}
+#endif
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/contexts.h b/chromium/v8/src/contexts.h
new file mode 100644
index 00000000000..fdf6d27ef55
--- /dev/null
+++ b/chromium/v8/src/contexts.h
@@ -0,0 +1,520 @@
+// Copyright 2012 the V8 project authors. 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_CONTEXTS_H_
+#define V8_CONTEXTS_H_
+
+#include "heap.h"
+#include "objects.h"
+
+namespace v8 {
+namespace internal {
+
+
+enum ContextLookupFlags {
+ FOLLOW_CONTEXT_CHAIN = 1,
+ FOLLOW_PROTOTYPE_CHAIN = 2,
+
+ DONT_FOLLOW_CHAINS = 0,
+ FOLLOW_CHAINS = FOLLOW_CONTEXT_CHAIN | FOLLOW_PROTOTYPE_CHAIN
+};
+
+
+// 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. 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. 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
+};
+
+
+// Heap-allocated activation contexts.
+//
+// Contexts are implemented as FixedArray objects; the Context
+// class is a convenience interface casted on a FixedArray object.
+//
+// Note: Context must have no virtual functions and Context objects
+// must always be allocated via Heap::AllocateContext() or
+// Factory::NewContext.
+
+#define NATIVE_CONTEXT_FIELDS(V) \
+ V(GLOBAL_PROXY_INDEX, JSObject, global_proxy_object) \
+ V(SECURITY_TOKEN_INDEX, Object, security_token) \
+ V(BOOLEAN_FUNCTION_INDEX, JSFunction, boolean_function) \
+ V(NUMBER_FUNCTION_INDEX, JSFunction, number_function) \
+ V(STRING_FUNCTION_INDEX, JSFunction, string_function) \
+ V(STRING_FUNCTION_PROTOTYPE_MAP_INDEX, Map, string_function_prototype_map) \
+ V(SYMBOL_FUNCTION_INDEX, JSFunction, symbol_function) \
+ V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \
+ V(INTERNAL_ARRAY_FUNCTION_INDEX, JSFunction, internal_array_function) \
+ V(ARRAY_FUNCTION_INDEX, JSFunction, array_function) \
+ V(JS_ARRAY_MAPS_INDEX, Object, js_array_maps) \
+ V(DATE_FUNCTION_INDEX, JSFunction, date_function) \
+ V(JSON_OBJECT_INDEX, JSObject, json_object) \
+ V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
+ V(INITIAL_OBJECT_PROTOTYPE_INDEX, JSObject, initial_object_prototype) \
+ V(INITIAL_ARRAY_PROTOTYPE_INDEX, JSObject, initial_array_prototype) \
+ V(CREATE_DATE_FUN_INDEX, JSFunction, create_date_fun) \
+ V(TO_NUMBER_FUN_INDEX, JSFunction, to_number_fun) \
+ V(TO_STRING_FUN_INDEX, JSFunction, to_string_fun) \
+ V(TO_DETAIL_STRING_FUN_INDEX, JSFunction, to_detail_string_fun) \
+ V(TO_OBJECT_FUN_INDEX, JSFunction, to_object_fun) \
+ V(TO_INTEGER_FUN_INDEX, JSFunction, to_integer_fun) \
+ V(TO_UINT32_FUN_INDEX, JSFunction, to_uint32_fun) \
+ V(TO_INT32_FUN_INDEX, JSFunction, to_int32_fun) \
+ V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
+ V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \
+ V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \
+ V(ARRAY_BUFFER_FUN_INDEX, JSFunction, array_buffer_fun) \
+ V(UINT8_ARRAY_FUN_INDEX, JSFunction, uint8_array_fun) \
+ V(INT8_ARRAY_FUN_INDEX, JSFunction, int8_array_fun) \
+ V(UINT16_ARRAY_FUN_INDEX, JSFunction, uint16_array_fun) \
+ V(INT16_ARRAY_FUN_INDEX, JSFunction, int16_array_fun) \
+ V(UINT32_ARRAY_FUN_INDEX, JSFunction, uint32_array_fun) \
+ V(INT32_ARRAY_FUN_INDEX, JSFunction, int32_array_fun) \
+ V(FLOAT_ARRAY_FUN_INDEX, JSFunction, float_array_fun) \
+ V(DOUBLE_ARRAY_FUN_INDEX, JSFunction, double_array_fun) \
+ V(UINT8C_ARRAY_FUN_INDEX, JSFunction, uint8c_array_fun) \
+ V(DATA_VIEW_FUN_INDEX, JSFunction, data_view_fun) \
+ V(FUNCTION_MAP_INDEX, Map, function_map) \
+ V(STRICT_MODE_FUNCTION_MAP_INDEX, Map, strict_mode_function_map) \
+ V(FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, Map, function_without_prototype_map) \
+ V(STRICT_MODE_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, Map, \
+ strict_mode_function_without_prototype_map) \
+ V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)\
+ V(ARGUMENTS_BOILERPLATE_INDEX, JSObject, arguments_boilerplate) \
+ V(ALIASED_ARGUMENTS_BOILERPLATE_INDEX, JSObject, \
+ aliased_arguments_boilerplate) \
+ V(STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX, JSObject, \
+ strict_mode_arguments_boilerplate) \
+ V(MESSAGE_LISTENERS_INDEX, JSObject, message_listeners) \
+ V(MAKE_MESSAGE_FUN_INDEX, JSFunction, make_message_fun) \
+ V(GET_STACK_TRACE_LINE_INDEX, JSFunction, get_stack_trace_line_fun) \
+ V(CONFIGURE_GLOBAL_INDEX, JSFunction, configure_global_fun) \
+ V(FUNCTION_CACHE_INDEX, JSObject, function_cache) \
+ V(JSFUNCTION_RESULT_CACHES_INDEX, FixedArray, jsfunction_result_caches) \
+ V(NORMALIZED_MAP_CACHE_INDEX, NormalizedMapCache, normalized_map_cache) \
+ V(RUNTIME_CONTEXT_INDEX, Context, runtime_context) \
+ V(CALL_AS_FUNCTION_DELEGATE_INDEX, JSFunction, call_as_function_delegate) \
+ V(CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, JSFunction, \
+ call_as_constructor_delegate) \
+ V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
+ V(OPAQUE_REFERENCE_FUNCTION_INDEX, JSFunction, opaque_reference_function) \
+ V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
+ V(OUT_OF_MEMORY_INDEX, Object, out_of_memory) \
+ V(MAP_CACHE_INDEX, Object, map_cache) \
+ V(EMBEDDER_DATA_INDEX, FixedArray, embedder_data) \
+ V(ALLOW_CODE_GEN_FROM_STRINGS_INDEX, Object, allow_code_gen_from_strings) \
+ V(ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX, Object, \
+ error_message_for_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(PROXY_ENUMERATE_INDEX, JSFunction, proxy_enumerate) \
+ V(OBSERVERS_NOTIFY_CHANGE_INDEX, JSFunction, observers_notify_change) \
+ V(OBSERVERS_ENQUEUE_SPLICE_INDEX, JSFunction, observers_enqueue_splice) \
+ V(OBSERVERS_BEGIN_SPLICE_INDEX, JSFunction, \
+ observers_begin_perform_splice) \
+ V(OBSERVERS_END_SPLICE_INDEX, JSFunction, \
+ observers_end_perform_splice) \
+ V(OBSERVERS_DELIVER_CHANGES_INDEX, JSFunction, observers_deliver_changes) \
+ V(GENERATOR_FUNCTION_MAP_INDEX, Map, generator_function_map) \
+ V(STRICT_MODE_GENERATOR_FUNCTION_MAP_INDEX, Map, \
+ strict_mode_generator_function_map) \
+ V(GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, Map, \
+ generator_object_prototype_map) \
+ V(GENERATOR_RESULT_MAP_INDEX, Map, generator_result_map) \
+ 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
+// dynamically pushed 'with' contexts (or 'scopes' in ECMA-262 speak).
+//
+// At runtime, the contexts build a stack in parallel to the execution
+// stack, with the top-most context being the current context. All contexts
+// have the following slots:
+//
+// [ closure ] This is the current function. It is the same for all
+// contexts inside a function. It provides access to the
+// incoming context (i.e., the outer context, which may
+// or may not become the current function's context), and
+// it provides access to the functions code and thus it's
+// scope information, which in turn contains the names of
+// statically allocated context slots. The names are needed
+// for dynamic lookups in the presence of 'with' or 'eval'.
+//
+// [ previous ] A pointer to the previous context. It is NULL for
+// function contexts, and non-NULL for 'with' contexts.
+// Used to implement the 'with' statement.
+//
+// [ extension ] A pointer to an extension JSObject, or NULL. Used to
+// implement 'with' statements and dynamic declarations
+// (through 'eval'). The object in a 'with' statement is
+// stored in the extension slot of a 'with' context.
+// Dynamically declared variables/functions are also added
+// to lazily allocated extension object. Context::Lookup
+// searches the extension object for properties.
+// For global and block contexts, contains the respective
+// ScopeInfo.
+// For module contexts, points back to the respective JSModule.
+//
+// [ global_object ] A pointer to the global object. Provided for quick
+// access to the global object from inside the code (since
+// we always have a context pointer).
+//
+// In addition, function contexts may have statically allocated context slots
+// to store local variables/functions that are accessed from inner functions
+// (via static context addresses) or through 'eval' (dynamic context lookups).
+// Finally, the native context contains additional slots for fast access to
+// native properties.
+
+class Context: public FixedArray {
+ public:
+ // Conversions.
+ static Context* cast(Object* context) {
+ ASSERT(context->IsContext());
+ return reinterpret_cast<Context*>(context);
+ }
+
+ // The default context slot layout; indices are FixedArray slot indices.
+ enum {
+ // These slots are in all contexts.
+ CLOSURE_INDEX,
+ 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), the serialized
+ // scope info (block contexts), or the module instance (module contexts).
+ EXTENSION_INDEX,
+ GLOBAL_OBJECT_INDEX,
+ MIN_CONTEXT_SLOTS,
+
+ // This slot holds the thrown value in catch contexts.
+ THROWN_OBJECT_INDEX = MIN_CONTEXT_SLOTS,
+
+ // These slots are only in native contexts.
+ GLOBAL_PROXY_INDEX = MIN_CONTEXT_SLOTS,
+ SECURITY_TOKEN_INDEX,
+ ARGUMENTS_BOILERPLATE_INDEX,
+ ALIASED_ARGUMENTS_BOILERPLATE_INDEX,
+ STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX,
+ REGEXP_RESULT_MAP_INDEX,
+ FUNCTION_MAP_INDEX,
+ STRICT_MODE_FUNCTION_MAP_INDEX,
+ FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
+ STRICT_MODE_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX,
+ INITIAL_OBJECT_PROTOTYPE_INDEX,
+ INITIAL_ARRAY_PROTOTYPE_INDEX,
+ BOOLEAN_FUNCTION_INDEX,
+ NUMBER_FUNCTION_INDEX,
+ STRING_FUNCTION_INDEX,
+ STRING_FUNCTION_PROTOTYPE_MAP_INDEX,
+ SYMBOL_FUNCTION_INDEX,
+ OBJECT_FUNCTION_INDEX,
+ INTERNAL_ARRAY_FUNCTION_INDEX,
+ ARRAY_FUNCTION_INDEX,
+ JS_ARRAY_MAPS_INDEX,
+ DATE_FUNCTION_INDEX,
+ JSON_OBJECT_INDEX,
+ REGEXP_FUNCTION_INDEX,
+ CREATE_DATE_FUN_INDEX,
+ TO_NUMBER_FUN_INDEX,
+ TO_STRING_FUN_INDEX,
+ TO_DETAIL_STRING_FUN_INDEX,
+ TO_OBJECT_FUN_INDEX,
+ TO_INTEGER_FUN_INDEX,
+ TO_UINT32_FUN_INDEX,
+ TO_INT32_FUN_INDEX,
+ TO_BOOLEAN_FUN_INDEX,
+ GLOBAL_EVAL_FUN_INDEX,
+ INSTANTIATE_FUN_INDEX,
+ CONFIGURE_INSTANCE_FUN_INDEX,
+ ARRAY_BUFFER_FUN_INDEX,
+ UINT8_ARRAY_FUN_INDEX,
+ INT8_ARRAY_FUN_INDEX,
+ UINT16_ARRAY_FUN_INDEX,
+ INT16_ARRAY_FUN_INDEX,
+ UINT32_ARRAY_FUN_INDEX,
+ INT32_ARRAY_FUN_INDEX,
+ FLOAT_ARRAY_FUN_INDEX,
+ DOUBLE_ARRAY_FUN_INDEX,
+ UINT8C_ARRAY_FUN_INDEX,
+ DATA_VIEW_FUN_INDEX,
+ MESSAGE_LISTENERS_INDEX,
+ MAKE_MESSAGE_FUN_INDEX,
+ GET_STACK_TRACE_LINE_INDEX,
+ CONFIGURE_GLOBAL_INDEX,
+ FUNCTION_CACHE_INDEX,
+ JSFUNCTION_RESULT_CACHES_INDEX,
+ NORMALIZED_MAP_CACHE_INDEX,
+ RUNTIME_CONTEXT_INDEX,
+ CALL_AS_FUNCTION_DELEGATE_INDEX,
+ CALL_AS_CONSTRUCTOR_DELEGATE_INDEX,
+ SCRIPT_FUNCTION_INDEX,
+ OPAQUE_REFERENCE_FUNCTION_INDEX,
+ CONTEXT_EXTENSION_FUNCTION_INDEX,
+ OUT_OF_MEMORY_INDEX,
+ EMBEDDER_DATA_INDEX,
+ ALLOW_CODE_GEN_FROM_STRINGS_INDEX,
+ ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX,
+ TO_COMPLETE_PROPERTY_DESCRIPTOR_INDEX,
+ DERIVED_HAS_TRAP_INDEX,
+ DERIVED_GET_TRAP_INDEX,
+ DERIVED_SET_TRAP_INDEX,
+ PROXY_ENUMERATE_INDEX,
+ OBSERVERS_NOTIFY_CHANGE_INDEX,
+ OBSERVERS_ENQUEUE_SPLICE_INDEX,
+ OBSERVERS_BEGIN_SPLICE_INDEX,
+ OBSERVERS_END_SPLICE_INDEX,
+ OBSERVERS_DELIVER_CHANGES_INDEX,
+ GENERATOR_FUNCTION_MAP_INDEX,
+ STRICT_MODE_GENERATOR_FUNCTION_MAP_INDEX,
+ GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX,
+ GENERATOR_RESULT_MAP_INDEX,
+ RANDOM_SEED_INDEX,
+
+ // Properties from here are treated as weak references by the full GC.
+ // Scavenge treats them as strong references.
+ OPTIMIZED_FUNCTIONS_LIST, // Weak.
+ MAP_CACHE_INDEX, // Weak.
+ NEXT_CONTEXT_LINK, // Weak.
+
+ // Total number of slots.
+ NATIVE_CONTEXT_SLOTS,
+
+ FIRST_WEAK_SLOT = OPTIMIZED_FUNCTIONS_LIST
+ };
+
+ // Direct slot access.
+ JSFunction* closure() { return JSFunction::cast(get(CLOSURE_INDEX)); }
+ void set_closure(JSFunction* closure) { set(CLOSURE_INDEX, closure); }
+
+ Context* previous() {
+ Object* result = unchecked_previous();
+ ASSERT(IsBootstrappingOrValidParentContext(result, this));
+ return reinterpret_cast<Context*>(result);
+ }
+ void set_previous(Context* context) { set(PREVIOUS_INDEX, context); }
+
+ bool has_extension() { return extension() != NULL; }
+ Object* extension() { return get(EXTENSION_INDEX); }
+ void set_extension(Object* object) { set(EXTENSION_INDEX, object); }
+
+ JSModule* module() { return JSModule::cast(get(EXTENSION_INDEX)); }
+ void set_module(JSModule* module) { set(EXTENSION_INDEX, module); }
+
+ // Get the context where var declarations will be hoisted to, which
+ // may be the context itself.
+ Context* declaration_context();
+
+ GlobalObject* global_object() {
+ Object* result = get(GLOBAL_OBJECT_INDEX);
+ ASSERT(IsBootstrappingOrGlobalObject(result));
+ return reinterpret_cast<GlobalObject*>(result);
+ }
+ void set_global_object(GlobalObject* object) {
+ set(GLOBAL_OBJECT_INDEX, object);
+ }
+
+ // Returns a JSGlobalProxy object or null.
+ JSObject* global_proxy();
+ void set_global_proxy(JSObject* global);
+
+ // The builtins object.
+ JSBuiltinsObject* builtins();
+
+ // Get the innermost global context by traversing the context chain.
+ Context* global_context();
+
+ // Compute the native context by traversing the context chain.
+ Context* native_context();
+
+ // Predicates for context types. IsNativeContext is also defined on Object
+ // because we frequently have to know if arbitrary objects are natives
+ // contexts.
+ bool IsNativeContext() {
+ Map* map = this->map();
+ return map == map->GetHeap()->native_context_map();
+ }
+ bool IsFunctionContext() {
+ Map* map = this->map();
+ return map == map->GetHeap()->function_context_map();
+ }
+ bool IsCatchContext() {
+ Map* map = this->map();
+ return map == map->GetHeap()->catch_context_map();
+ }
+ bool IsWithContext() {
+ Map* map = this->map();
+ return map == map->GetHeap()->with_context_map();
+ }
+ bool IsBlockContext() {
+ Map* map = this->map();
+ return map == map->GetHeap()->block_context_map();
+ }
+ bool IsModuleContext() {
+ Map* map = this->map();
+ return map == map->GetHeap()->module_context_map();
+ }
+ bool IsGlobalContext() {
+ Map* map = this->map();
+ return map == map->GetHeap()->global_context_map();
+ }
+
+ // Tells whether the native context is marked with out of memory.
+ inline bool has_out_of_memory();
+
+ // Mark the native context with out of memory.
+ inline void mark_out_of_memory();
+
+ // A native context hold a list of all functions which have been optimized.
+ void AddOptimizedFunction(JSFunction* function);
+ void RemoveOptimizedFunction(JSFunction* function);
+ Object* OptimizedFunctionsListHead();
+ void ClearOptimizedFunctions();
+
+ Handle<Object> ErrorMessageForCodeGenerationFromStrings();
+
+#define NATIVE_CONTEXT_FIELD_ACCESSORS(index, type, name) \
+ void set_##name(type* value) { \
+ ASSERT(IsNativeContext()); \
+ set(index, value); \
+ } \
+ bool is_##name(type* value) { \
+ ASSERT(IsNativeContext()); \
+ return type::cast(get(index)) == value; \
+ } \
+ type* name() { \
+ ASSERT(IsNativeContext()); \
+ return type::cast(get(index)); \
+ }
+ NATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELD_ACCESSORS)
+#undef NATIVE_CONTEXT_FIELD_ACCESSORS
+
+ // Lookup the slot called name, starting with the current context.
+ // There are three possibilities:
+ //
+ // 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) 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) 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,
+ PropertyAttributes* attributes,
+ BindingFlags* binding_flags);
+
+ // Code generation support.
+ static int SlotOffset(int index) {
+ return kHeaderSize + index * kPointerSize - kHeapObjectTag;
+ }
+
+ static int FunctionMapIndex(LanguageMode language_mode, bool is_generator) {
+ return is_generator
+ ? (language_mode == CLASSIC_MODE
+ ? GENERATOR_FUNCTION_MAP_INDEX
+ : STRICT_MODE_GENERATOR_FUNCTION_MAP_INDEX)
+ : (language_mode == CLASSIC_MODE
+ ? FUNCTION_MAP_INDEX
+ : STRICT_MODE_FUNCTION_MAP_INDEX);
+ }
+
+ static const int kSize = kHeaderSize + NATIVE_CONTEXT_SLOTS * kPointerSize;
+
+ // GC support.
+ typedef FixedBodyDescriptor<
+ kHeaderSize, kSize, kSize> ScavengeBodyDescriptor;
+
+ typedef FixedBodyDescriptor<
+ kHeaderSize,
+ kHeaderSize + FIRST_WEAK_SLOT * kPointerSize,
+ kSize> MarkCompactBodyDescriptor;
+
+ private:
+ // Unchecked access to the slots.
+ Object* unchecked_previous() { return get(PREVIOUS_INDEX); }
+
+#ifdef DEBUG
+ // Bootstrapping-aware type checks.
+ static bool IsBootstrappingOrValidParentContext(Object* object, Context* kid);
+ static bool IsBootstrappingOrGlobalObject(Object* object);
+#endif
+
+ STATIC_CHECK(kHeaderSize == Internals::kContextHeaderSize);
+ STATIC_CHECK(EMBEDDER_DATA_INDEX == Internals::kContextEmbedderDataIndex);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_CONTEXTS_H_
diff --git a/chromium/v8/src/conversions-inl.h b/chromium/v8/src/conversions-inl.h
new file mode 100644
index 00000000000..2f0a399d1a4
--- /dev/null
+++ b/chromium/v8/src/conversions-inl.h
@@ -0,0 +1,704 @@
+// 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_CONVERSIONS_INL_H_
+#define V8_CONVERSIONS_INL_H_
+
+#include <limits.h> // Required for INT_MAX etc.
+#include <float.h> // Required for DBL_MAX and on Win32 for finite()
+#include <stdarg.h>
+#include <cmath>
+#include "globals.h" // Required for V8_INFINITY
+
+// ----------------------------------------------------------------------------
+// Extra POSIX/ANSI functions for Win32/MSVC.
+
+#include "conversions.h"
+#include "double.h"
+#include "platform.h"
+#include "scanner.h"
+#include "strtod.h"
+
+namespace v8 {
+namespace internal {
+
+inline double JunkStringValue() {
+ return BitCast<double, uint64_t>(kQuietNaNMask);
+}
+
+
+inline double SignedZero(bool negative) {
+ return negative ? uint64_to_double(Double::kSignMask) : 0.0;
+}
+
+
+// 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.
+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.
+
+ // Convert "small enough" doubles to uint32_t by fixing the 32
+ // least significant non-fractional bits in the low 32 bits of the
+ // double, and reading them from there.
+ const double k2Pow52 = 4503599627370496.0;
+ bool negative = x < 0;
+ if (negative) {
+ x = -x;
+ }
+ if (x < k2Pow52) {
+ x += k2Pow52;
+ uint32_t result;
+ Address mantissa_ptr = reinterpret_cast<Address>(&x);
+ // Copy least significant 32 bits of mantissa.
+ OS::MemCopy(&result, mantissa_ptr, sizeof(result));
+ return negative ? ~result + 1 : result;
+ }
+ // Large number (outside uint32 range), Infinity or NaN.
+ return 0x80000000u; // Return integer indefinite.
+}
+
+
+inline double DoubleToInteger(double x) {
+ if (std::isnan(x)) return 0;
+ if (!std::isfinite(x) || x == 0) return x;
+ return (x >= 0) ? floor(x) : ceil(x);
+}
+
+
+int32_t DoubleToInt32(double x) {
+ int32_t i = FastD2I(x);
+ if (FastI2D(i) == x) return i;
+ Double d(x);
+ int exponent = d.Exponent();
+ if (exponent < 0) {
+ if (exponent <= -Double::kSignificandSize) return 0;
+ return d.Sign() * static_cast<int32_t>(d.Significand() >> -exponent);
+ } else {
+ if (exponent > 31) return 0;
+ return d.Sign() * static_cast<int32_t>(d.Significand() << exponent);
+ }
+}
+
+
+template <class Iterator, class EndMark>
+bool SubStringEquals(Iterator* current,
+ EndMark end,
+ const char* substring) {
+ ASSERT(**current == *substring);
+ for (substring++; *substring != '\0'; substring++) {
+ ++*current;
+ if (*current == end || **current != *substring) return false;
+ }
+ ++*current;
+ return true;
+}
+
+
+// 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>
+inline bool AdvanceToNonspace(UnicodeCache* unicode_cache,
+ Iterator* current,
+ EndMark end) {
+ while (*current != end) {
+ if (!unicode_cache->IsWhiteSpace(**current)) return true;
+ ++*current;
+ }
+ return false;
+}
+
+
+// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end.
+template <int radix_log_2, class Iterator, class EndMark>
+double InternalStringToIntDouble(UnicodeCache* unicode_cache,
+ Iterator current,
+ EndMark end,
+ bool negative,
+ bool allow_trailing_junk) {
+ ASSERT(current != end);
+
+ // Skip leading 0s.
+ while (*current == '0') {
+ ++current;
+ if (current == end) return SignedZero(negative);
+ }
+
+ int64_t number = 0;
+ int exponent = 0;
+ const int radix = (1 << radix_log_2);
+
+ do {
+ int digit;
+ if (*current >= '0' && *current <= '9' && *current < '0' + radix) {
+ digit = static_cast<char>(*current) - '0';
+ } else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) {
+ digit = static_cast<char>(*current) - 'a' + 10;
+ } else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) {
+ digit = static_cast<char>(*current) - 'A' + 10;
+ } else {
+ if (allow_trailing_junk ||
+ !AdvanceToNonspace(unicode_cache, &current, end)) {
+ break;
+ } else {
+ return JunkStringValue();
+ }
+ }
+
+ number = number * radix + digit;
+ int overflow = static_cast<int>(number >> 53);
+ if (overflow != 0) {
+ // Overflow occurred. Need to determine which direction to round the
+ // result.
+ int overflow_bits_count = 1;
+ while (overflow > 1) {
+ overflow_bits_count++;
+ overflow >>= 1;
+ }
+
+ int dropped_bits_mask = ((1 << overflow_bits_count) - 1);
+ int dropped_bits = static_cast<int>(number) & dropped_bits_mask;
+ number >>= overflow_bits_count;
+ exponent = overflow_bits_count;
+
+ bool zero_tail = true;
+ while (true) {
+ ++current;
+ if (current == end || !isDigit(*current, radix)) break;
+ zero_tail = zero_tail && *current == '0';
+ exponent += radix_log_2;
+ }
+
+ if (!allow_trailing_junk &&
+ AdvanceToNonspace(unicode_cache, &current, end)) {
+ return JunkStringValue();
+ }
+
+ int middle_value = (1 << (overflow_bits_count - 1));
+ if (dropped_bits > middle_value) {
+ number++; // Rounding up.
+ } else if (dropped_bits == middle_value) {
+ // Rounding to even to consistency with decimals: half-way case rounds
+ // up if significant part is odd and down otherwise.
+ if ((number & 1) != 0 || !zero_tail) {
+ number++; // Rounding up.
+ }
+ }
+
+ // Rounding up may cause overflow.
+ if ((number & (static_cast<int64_t>(1) << 53)) != 0) {
+ exponent++;
+ number >>= 1;
+ }
+ break;
+ }
+ ++current;
+ } while (current != end);
+
+ ASSERT(number < ((int64_t)1 << 53));
+ ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number);
+
+ if (exponent == 0) {
+ if (negative) {
+ if (number == 0) return -0.0;
+ number = -number;
+ }
+ return static_cast<double>(number);
+ }
+
+ ASSERT(number != 0);
+ return ldexp(static_cast<double>(negative ? -number : number), exponent);
+}
+
+
+template <class Iterator, class EndMark>
+double InternalStringToInt(UnicodeCache* unicode_cache,
+ Iterator current,
+ EndMark end,
+ int radix) {
+ const bool allow_trailing_junk = true;
+ const double empty_string_val = JunkStringValue();
+
+ if (!AdvanceToNonspace(unicode_cache, &current, end)) {
+ return empty_string_val;
+ }
+
+ bool negative = false;
+ bool leading_zero = false;
+
+ if (*current == '+') {
+ // Ignore leading sign; skip following spaces.
+ ++current;
+ if (current == end) {
+ return JunkStringValue();
+ }
+ } else if (*current == '-') {
+ ++current;
+ if (current == end) {
+ return JunkStringValue();
+ }
+ negative = true;
+ }
+
+ if (radix == 0) {
+ // Radix detection.
+ radix = 10;
+ if (*current == '0') {
+ ++current;
+ if (current == end) return SignedZero(negative);
+ if (*current == 'x' || *current == 'X') {
+ radix = 16;
+ ++current;
+ if (current == end) return JunkStringValue();
+ } else {
+ leading_zero = true;
+ }
+ }
+ } else if (radix == 16) {
+ if (*current == '0') {
+ // Allow "0x" prefix.
+ ++current;
+ if (current == end) return SignedZero(negative);
+ if (*current == 'x' || *current == 'X') {
+ ++current;
+ if (current == end) return JunkStringValue();
+ } else {
+ leading_zero = true;
+ }
+ }
+ }
+
+ if (radix < 2 || radix > 36) return JunkStringValue();
+
+ // Skip leading zeros.
+ while (*current == '0') {
+ leading_zero = true;
+ ++current;
+ if (current == end) return SignedZero(negative);
+ }
+
+ if (!leading_zero && !isDigit(*current, radix)) {
+ return JunkStringValue();
+ }
+
+ if (IsPowerOf2(radix)) {
+ switch (radix) {
+ case 2:
+ return InternalStringToIntDouble<1>(
+ unicode_cache, current, end, negative, allow_trailing_junk);
+ case 4:
+ return InternalStringToIntDouble<2>(
+ unicode_cache, current, end, negative, allow_trailing_junk);
+ case 8:
+ return InternalStringToIntDouble<3>(
+ unicode_cache, current, end, negative, allow_trailing_junk);
+
+ case 16:
+ return InternalStringToIntDouble<4>(
+ unicode_cache, current, end, negative, allow_trailing_junk);
+
+ case 32:
+ return InternalStringToIntDouble<5>(
+ unicode_cache, current, end, negative, allow_trailing_junk);
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (radix == 10) {
+ // Parsing with strtod.
+ const int kMaxSignificantDigits = 309; // Doubles are less than 1.8e308.
+ // The buffer may contain up to kMaxSignificantDigits + 1 digits and a zero
+ // end.
+ const int kBufferSize = kMaxSignificantDigits + 2;
+ char buffer[kBufferSize];
+ int buffer_pos = 0;
+ while (*current >= '0' && *current <= '9') {
+ if (buffer_pos <= kMaxSignificantDigits) {
+ // If the number has more than kMaxSignificantDigits it will be parsed
+ // as infinity.
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ }
+ ++current;
+ if (current == end) break;
+ }
+
+ if (!allow_trailing_junk &&
+ AdvanceToNonspace(unicode_cache, &current, end)) {
+ return JunkStringValue();
+ }
+
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos] = '\0';
+ Vector<const char> buffer_vector(buffer, buffer_pos);
+ return negative ? -Strtod(buffer_vector, 0) : Strtod(buffer_vector, 0);
+ }
+
+ // The following code causes accumulating rounding error for numbers greater
+ // than ~2^56. It's explicitly allowed in the spec: "if R is not 2, 4, 8, 10,
+ // 16, or 32, then mathInt may be an implementation-dependent approximation to
+ // the mathematical integer value" (15.1.2.2).
+
+ int lim_0 = '0' + (radix < 10 ? radix : 10);
+ int lim_a = 'a' + (radix - 10);
+ int lim_A = 'A' + (radix - 10);
+
+ // NOTE: The code for computing the value may seem a bit complex at
+ // first glance. It is structured to use 32-bit multiply-and-add
+ // loops as long as possible to avoid loosing precision.
+
+ double v = 0.0;
+ bool done = false;
+ do {
+ // Parse the longest part of the string starting at index j
+ // possible while keeping the multiplier, and thus the part
+ // itself, within 32 bits.
+ unsigned int part = 0, multiplier = 1;
+ while (true) {
+ int d;
+ if (*current >= '0' && *current < lim_0) {
+ d = *current - '0';
+ } else if (*current >= 'a' && *current < lim_a) {
+ d = *current - 'a' + 10;
+ } else if (*current >= 'A' && *current < lim_A) {
+ d = *current - 'A' + 10;
+ } else {
+ done = true;
+ break;
+ }
+
+ // Update the value of the part as long as the multiplier fits
+ // in 32 bits. When we can't guarantee that the next iteration
+ // will not overflow the multiplier, we stop parsing the part
+ // by leaving the loop.
+ const unsigned int kMaximumMultiplier = 0xffffffffU / 36;
+ uint32_t m = multiplier * radix;
+ if (m > kMaximumMultiplier) break;
+ part = part * radix + d;
+ multiplier = m;
+ ASSERT(multiplier > part);
+
+ ++current;
+ if (current == end) {
+ done = true;
+ break;
+ }
+ }
+
+ // Update the value and skip the part in the string.
+ v = v * multiplier + part;
+ } while (!done);
+
+ if (!allow_trailing_junk &&
+ AdvanceToNonspace(unicode_cache, &current, end)) {
+ return JunkStringValue();
+ }
+
+ return negative ? -v : v;
+}
+
+
+// Converts a string to a double value. Assumes the Iterator supports
+// the following operations:
+// 1. current == end (other ops are not allowed), current != end.
+// 2. *current - gets the current character in the sequence.
+// 3. ++current (advances the position).
+template <class Iterator, class EndMark>
+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'.
+ // 2. If AdvanceToNonspace returned false then current == end.
+ // 3. If 'current' becomes be equal to 'end' the function returns or goes to
+ // 'parsing_done'.
+ // 4. 'current' is not dereferenced after the 'parsing_done' label.
+ // 5. Code before 'parsing_done' may rely on 'current != end'.
+ if (!AdvanceToNonspace(unicode_cache, &current, end)) {
+ return empty_string_val;
+ }
+
+ const bool allow_trailing_junk = (flags & ALLOW_TRAILING_JUNK) != 0;
+
+ // The longest form of simplified number is: "-<significant digits>'.1eXXX\0".
+ const int kBufferSize = kMaxSignificantDigits + 10;
+ char buffer[kBufferSize]; // NOLINT: size is known at compile time.
+ int buffer_pos = 0;
+
+ // Exponent will be adjusted if insignificant digits of the integer part
+ // or insignificant leading zeros of the fractional part are dropped.
+ int exponent = 0;
+ int significant_digits = 0;
+ int insignificant_digits = 0;
+ bool nonzero_digit_dropped = false;
+
+ enum Sign {
+ NONE,
+ NEGATIVE,
+ POSITIVE
+ };
+
+ Sign sign = NONE;
+
+ if (*current == '+') {
+ // Ignore leading sign.
+ ++current;
+ if (current == end) return JunkStringValue();
+ sign = POSITIVE;
+ } else if (*current == '-') {
+ ++current;
+ if (current == end) return JunkStringValue();
+ sign = NEGATIVE;
+ }
+
+ static const char kInfinityString[] = "Infinity";
+ if (*current == kInfinityString[0]) {
+ if (!SubStringEquals(&current, end, kInfinityString)) {
+ return JunkStringValue();
+ }
+
+ if (!allow_trailing_junk &&
+ AdvanceToNonspace(unicode_cache, &current, end)) {
+ return JunkStringValue();
+ }
+
+ ASSERT(buffer_pos == 0);
+ return (sign == NEGATIVE) ? -V8_INFINITY : V8_INFINITY;
+ }
+
+ bool leading_zero = false;
+ if (*current == '0') {
+ ++current;
+ if (current == end) return SignedZero(sign == NEGATIVE);
+
+ leading_zero = true;
+
+ // It could be hexadecimal value.
+ if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
+ ++current;
+ if (current == end || !isDigit(*current, 16) || sign != NONE) {
+ return JunkStringValue(); // "0x".
+ }
+
+ return InternalStringToIntDouble<4>(unicode_cache,
+ current,
+ end,
+ false,
+ allow_trailing_junk);
+
+ // It could be an explicit octal value.
+ } else if ((flags & ALLOW_OCTAL) && (*current == 'o' || *current == 'O')) {
+ ++current;
+ if (current == end || !isDigit(*current, 8) || sign != NONE) {
+ return JunkStringValue(); // "0o".
+ }
+
+ return InternalStringToIntDouble<3>(unicode_cache,
+ current,
+ end,
+ false,
+ allow_trailing_junk);
+
+ // It could be a binary value.
+ } else if ((flags & ALLOW_BINARY) && (*current == 'b' || *current == 'B')) {
+ ++current;
+ if (current == end || !isBinaryDigit(*current) || sign != NONE) {
+ return JunkStringValue(); // "0b".
+ }
+
+ return InternalStringToIntDouble<1>(unicode_cache,
+ current,
+ end,
+ false,
+ allow_trailing_junk);
+ }
+
+ // Ignore leading zeros in the integer part.
+ while (*current == '0') {
+ ++current;
+ if (current == end) return SignedZero(sign == NEGATIVE);
+ }
+ }
+
+ bool octal = leading_zero && (flags & ALLOW_IMPLICIT_OCTAL) != 0;
+
+ // Copy significant digits of the integer part (if any) to the buffer.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ // Will later check if it's an octal in the buffer.
+ } else {
+ insignificant_digits++; // Move the digit into the exponential part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ octal = octal && *current < '8';
+ ++current;
+ if (current == end) goto parsing_done;
+ }
+
+ if (significant_digits == 0) {
+ octal = false;
+ }
+
+ if (*current == '.') {
+ if (octal && !allow_trailing_junk) return JunkStringValue();
+ if (octal) goto parsing_done;
+
+ ++current;
+ if (current == end) {
+ if (significant_digits == 0 && !leading_zero) {
+ return JunkStringValue();
+ } else {
+ goto parsing_done;
+ }
+ }
+
+ if (significant_digits == 0) {
+ // octal = false;
+ // Integer part consists of 0 or is absent. Significant digits start after
+ // leading zeros (if any).
+ while (*current == '0') {
+ ++current;
+ if (current == end) return SignedZero(sign == NEGATIVE);
+ exponent--; // Move this 0 into the exponent.
+ }
+ }
+
+ // There is a fractional part. We don't emit a '.', but adjust the exponent
+ // instead.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ exponent--;
+ } else {
+ // Ignore insignificant digits in the fractional part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ ++current;
+ if (current == end) goto parsing_done;
+ }
+ }
+
+ if (!leading_zero && exponent == 0 && significant_digits == 0) {
+ // If leading_zeros is true then the string contains zeros.
+ // If exponent < 0 then string was [+-]\.0*...
+ // If significant_digits != 0 the string is not equal to 0.
+ // Otherwise there are no digits in the string.
+ return JunkStringValue();
+ }
+
+ // Parse exponential part.
+ if (*current == 'e' || *current == 'E') {
+ if (octal) return JunkStringValue();
+ ++current;
+ if (current == end) {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return JunkStringValue();
+ }
+ }
+ char sign = '+';
+ if (*current == '+' || *current == '-') {
+ sign = static_cast<char>(*current);
+ ++current;
+ if (current == end) {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return JunkStringValue();
+ }
+ }
+ }
+
+ if (current == end || *current < '0' || *current > '9') {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return JunkStringValue();
+ }
+ }
+
+ const int max_exponent = INT_MAX / 2;
+ ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2);
+ int num = 0;
+ do {
+ // Check overflow.
+ int digit = *current - '0';
+ if (num >= max_exponent / 10
+ && !(num == max_exponent / 10 && digit <= max_exponent % 10)) {
+ num = max_exponent;
+ } else {
+ num = num * 10 + digit;
+ }
+ ++current;
+ } while (current != end && *current >= '0' && *current <= '9');
+
+ exponent += (sign == '-' ? -num : num);
+ }
+
+ if (!allow_trailing_junk &&
+ AdvanceToNonspace(unicode_cache, &current, end)) {
+ return JunkStringValue();
+ }
+
+ parsing_done:
+ exponent += insignificant_digits;
+
+ if (octal) {
+ return InternalStringToIntDouble<3>(unicode_cache,
+ buffer,
+ buffer + buffer_pos,
+ sign == NEGATIVE,
+ allow_trailing_junk);
+ }
+
+ if (nonzero_digit_dropped) {
+ buffer[buffer_pos++] = '1';
+ exponent--;
+ }
+
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos] = '\0';
+
+ double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
+ return (sign == NEGATIVE) ? -converted : converted;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_CONVERSIONS_INL_H_
diff --git a/chromium/v8/src/conversions.cc b/chromium/v8/src/conversions.cc
new file mode 100644
index 00000000000..cdc42e34d9e
--- /dev/null
+++ b/chromium/v8/src/conversions.cc
@@ -0,0 +1,437 @@
+// 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 <stdarg.h>
+#include <limits.h>
+#include <cmath>
+
+#include "conversions-inl.h"
+#include "dtoa.h"
+#include "strtod.h"
+#include "utils.h"
+
+#ifndef _STLP_VENDOR_CSTD
+// STLPort doesn't import fpclassify into the std namespace.
+using std::fpclassify;
+#endif
+
+namespace v8 {
+namespace internal {
+
+
+double StringToDouble(UnicodeCache* unicode_cache,
+ const char* str, int flags, double empty_string_val) {
+ const char* end = str + StrLength(str);
+ return InternalStringToDouble(unicode_cache, str, end, flags,
+ empty_string_val);
+}
+
+
+double StringToDouble(UnicodeCache* unicode_cache,
+ Vector<const char> str,
+ int flags,
+ double empty_string_val) {
+ const char* end = str.start() + str.length();
+ return InternalStringToDouble(unicode_cache, str.start(), end, flags,
+ empty_string_val);
+}
+
+double StringToDouble(UnicodeCache* unicode_cache,
+ Vector<const uc16> str,
+ int flags,
+ double empty_string_val) {
+ const uc16* end = str.start() + str.length();
+ return InternalStringToDouble(unicode_cache, str.start(), end, flags,
+ empty_string_val);
+}
+
+
+const char* DoubleToCString(double v, Vector<char> buffer) {
+ switch (fpclassify(v)) {
+ case FP_NAN: return "NaN";
+ case FP_INFINITE: return (v < 0.0 ? "-Infinity" : "Infinity");
+ case FP_ZERO: return "0";
+ default: {
+ SimpleStringBuilder builder(buffer.start(), buffer.length());
+ int decimal_point;
+ int sign;
+ const int kV8DtoaBufferCapacity = kBase10MaximalLength + 1;
+ char decimal_rep[kV8DtoaBufferCapacity];
+ int length;
+
+ DoubleToAscii(v, DTOA_SHORTEST, 0,
+ Vector<char>(decimal_rep, kV8DtoaBufferCapacity),
+ &sign, &length, &decimal_point);
+
+ if (sign) builder.AddCharacter('-');
+
+ if (length <= decimal_point && decimal_point <= 21) {
+ // ECMA-262 section 9.8.1 step 6.
+ builder.AddString(decimal_rep);
+ builder.AddPadding('0', decimal_point - length);
+
+ } else if (0 < decimal_point && decimal_point <= 21) {
+ // ECMA-262 section 9.8.1 step 7.
+ builder.AddSubstring(decimal_rep, decimal_point);
+ builder.AddCharacter('.');
+ builder.AddString(decimal_rep + decimal_point);
+
+ } else if (decimal_point <= 0 && decimal_point > -6) {
+ // ECMA-262 section 9.8.1 step 8.
+ builder.AddString("0.");
+ builder.AddPadding('0', -decimal_point);
+ builder.AddString(decimal_rep);
+
+ } else {
+ // ECMA-262 section 9.8.1 step 9 and 10 combined.
+ builder.AddCharacter(decimal_rep[0]);
+ if (length != 1) {
+ builder.AddCharacter('.');
+ builder.AddString(decimal_rep + 1);
+ }
+ builder.AddCharacter('e');
+ builder.AddCharacter((decimal_point >= 0) ? '+' : '-');
+ int exponent = decimal_point - 1;
+ if (exponent < 0) exponent = -exponent;
+ builder.AddDecimalInteger(exponent);
+ }
+ return builder.Finalize();
+ }
+ }
+}
+
+
+const char* IntToCString(int n, Vector<char> buffer) {
+ bool negative = false;
+ if (n < 0) {
+ // We must not negate the most negative int.
+ if (n == kMinInt) return DoubleToCString(n, buffer);
+ negative = true;
+ n = -n;
+ }
+ // Build the string backwards from the least significant digit.
+ int i = buffer.length();
+ buffer[--i] = '\0';
+ do {
+ buffer[--i] = '0' + (n % 10);
+ n /= 10;
+ } while (n);
+ if (negative) buffer[--i] = '-';
+ return buffer.start() + i;
+}
+
+
+char* DoubleToFixedCString(double value, int f) {
+ const int kMaxDigitsBeforePoint = 21;
+ const double kFirstNonFixed = 1e21;
+ const int kMaxDigitsAfterPoint = 20;
+ ASSERT(f >= 0);
+ ASSERT(f <= kMaxDigitsAfterPoint);
+
+ bool negative = false;
+ double abs_value = value;
+ if (value < 0) {
+ abs_value = -value;
+ negative = true;
+ }
+
+ // If abs_value has more than kMaxDigitsBeforePoint digits before the point
+ // use the non-fixed conversion routine.
+ if (abs_value >= kFirstNonFixed) {
+ char arr[100];
+ Vector<char> buffer(arr, ARRAY_SIZE(arr));
+ return StrDup(DoubleToCString(value, buffer));
+ }
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ int sign;
+ // Add space for the '\0' byte.
+ const int kDecimalRepCapacity =
+ kMaxDigitsBeforePoint + kMaxDigitsAfterPoint + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+ DoubleToAscii(value, DTOA_FIXED, f,
+ Vector<char>(decimal_rep, kDecimalRepCapacity),
+ &sign, &decimal_rep_length, &decimal_point);
+
+ // Create a representation that is padded with zeros if needed.
+ int zero_prefix_length = 0;
+ int zero_postfix_length = 0;
+
+ if (decimal_point <= 0) {
+ zero_prefix_length = -decimal_point + 1;
+ decimal_point = 1;
+ }
+
+ if (zero_prefix_length + decimal_rep_length < decimal_point + f) {
+ zero_postfix_length = decimal_point + f - decimal_rep_length -
+ zero_prefix_length;
+ }
+
+ unsigned rep_length =
+ zero_prefix_length + decimal_rep_length + zero_postfix_length;
+ SimpleStringBuilder rep_builder(rep_length + 1);
+ rep_builder.AddPadding('0', zero_prefix_length);
+ rep_builder.AddString(decimal_rep);
+ rep_builder.AddPadding('0', zero_postfix_length);
+ char* rep = rep_builder.Finalize();
+
+ // Create the result string by appending a minus and putting in a
+ // decimal point if needed.
+ unsigned result_size = decimal_point + f + 2;
+ SimpleStringBuilder builder(result_size + 1);
+ if (negative) builder.AddCharacter('-');
+ builder.AddSubstring(rep, decimal_point);
+ if (f > 0) {
+ builder.AddCharacter('.');
+ builder.AddSubstring(rep + decimal_point, f);
+ }
+ DeleteArray(rep);
+ return builder.Finalize();
+}
+
+
+static char* CreateExponentialRepresentation(char* decimal_rep,
+ int exponent,
+ bool negative,
+ int significant_digits) {
+ bool negative_exponent = false;
+ if (exponent < 0) {
+ negative_exponent = true;
+ exponent = -exponent;
+ }
+
+ // Leave room in the result for appending a minus, for a period, the
+ // letter 'e', a minus or a plus depending on the exponent, and a
+ // three digit exponent.
+ unsigned result_size = significant_digits + 7;
+ SimpleStringBuilder builder(result_size + 1);
+
+ if (negative) builder.AddCharacter('-');
+ builder.AddCharacter(decimal_rep[0]);
+ if (significant_digits != 1) {
+ builder.AddCharacter('.');
+ builder.AddString(decimal_rep + 1);
+ int rep_length = StrLength(decimal_rep);
+ builder.AddPadding('0', significant_digits - rep_length);
+ }
+
+ builder.AddCharacter('e');
+ builder.AddCharacter(negative_exponent ? '-' : '+');
+ builder.AddDecimalInteger(exponent);
+ return builder.Finalize();
+}
+
+
+
+char* DoubleToExponentialCString(double value, int f) {
+ const int kMaxDigitsAfterPoint = 20;
+ // f might be -1 to signal that f was undefined in JavaScript.
+ ASSERT(f >= -1 && f <= kMaxDigitsAfterPoint);
+
+ bool negative = false;
+ if (value < 0) {
+ value = -value;
+ negative = true;
+ }
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ int sign;
+ // f corresponds to the digits after the point. There is always one digit
+ // before the point. The number of requested_digits equals hence f + 1.
+ // And we have to add one character for the null-terminator.
+ const int kV8DtoaBufferCapacity = kMaxDigitsAfterPoint + 1 + 1;
+ // Make sure that the buffer is big enough, even if we fall back to the
+ // shortest representation (which happens when f equals -1).
+ ASSERT(kBase10MaximalLength <= kMaxDigitsAfterPoint + 1);
+ char decimal_rep[kV8DtoaBufferCapacity];
+ int decimal_rep_length;
+
+ if (f == -1) {
+ DoubleToAscii(value, DTOA_SHORTEST, 0,
+ Vector<char>(decimal_rep, kV8DtoaBufferCapacity),
+ &sign, &decimal_rep_length, &decimal_point);
+ f = decimal_rep_length - 1;
+ } else {
+ DoubleToAscii(value, DTOA_PRECISION, f + 1,
+ Vector<char>(decimal_rep, kV8DtoaBufferCapacity),
+ &sign, &decimal_rep_length, &decimal_point);
+ }
+ ASSERT(decimal_rep_length > 0);
+ ASSERT(decimal_rep_length <= f + 1);
+
+ int exponent = decimal_point - 1;
+ char* result =
+ CreateExponentialRepresentation(decimal_rep, exponent, negative, f+1);
+
+ return result;
+}
+
+
+char* DoubleToPrecisionCString(double value, int p) {
+ const int kMinimalDigits = 1;
+ const int kMaximalDigits = 21;
+ ASSERT(p >= kMinimalDigits && p <= kMaximalDigits);
+ USE(kMinimalDigits);
+
+ bool negative = false;
+ if (value < 0) {
+ value = -value;
+ negative = true;
+ }
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ int sign;
+ // Add one for the terminating null character.
+ const int kV8DtoaBufferCapacity = kMaximalDigits + 1;
+ char decimal_rep[kV8DtoaBufferCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, DTOA_PRECISION, p,
+ Vector<char>(decimal_rep, kV8DtoaBufferCapacity),
+ &sign, &decimal_rep_length, &decimal_point);
+ ASSERT(decimal_rep_length <= p);
+
+ int exponent = decimal_point - 1;
+
+ char* result = NULL;
+
+ if (exponent < -6 || exponent >= p) {
+ result =
+ CreateExponentialRepresentation(decimal_rep, exponent, negative, p);
+ } else {
+ // Use fixed notation.
+ //
+ // Leave room in the result for appending a minus, a period and in
+ // the case where decimal_point is not positive for a zero in
+ // front of the period.
+ unsigned result_size = (decimal_point <= 0)
+ ? -decimal_point + p + 3
+ : p + 2;
+ SimpleStringBuilder builder(result_size + 1);
+ if (negative) builder.AddCharacter('-');
+ if (decimal_point <= 0) {
+ builder.AddString("0.");
+ builder.AddPadding('0', -decimal_point);
+ builder.AddString(decimal_rep);
+ builder.AddPadding('0', p - decimal_rep_length);
+ } else {
+ const int m = Min(decimal_rep_length, decimal_point);
+ builder.AddSubstring(decimal_rep, m);
+ builder.AddPadding('0', decimal_point - decimal_rep_length);
+ if (decimal_point < p) {
+ builder.AddCharacter('.');
+ const int extra = negative ? 2 : 1;
+ if (decimal_rep_length > decimal_point) {
+ const int len = StrLength(decimal_rep + decimal_point);
+ const int n = Min(len, p - (builder.position() - extra));
+ builder.AddSubstring(decimal_rep + decimal_point, n);
+ }
+ builder.AddPadding('0', extra + (p - builder.position()));
+ }
+ }
+ result = builder.Finalize();
+ }
+
+ return result;
+}
+
+
+char* DoubleToRadixCString(double value, int radix) {
+ ASSERT(radix >= 2 && radix <= 36);
+
+ // Character array used for conversion.
+ static const char chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+ // Buffer for the integer part of the result. 1024 chars is enough
+ // for max integer value in radix 2. We need room for a sign too.
+ static const int kBufferSize = 1100;
+ char integer_buffer[kBufferSize];
+ integer_buffer[kBufferSize - 1] = '\0';
+
+ // Buffer for the decimal part of the result. We only generate up
+ // to kBufferSize - 1 chars for the decimal part.
+ char decimal_buffer[kBufferSize];
+ decimal_buffer[kBufferSize - 1] = '\0';
+
+ // Make sure the value is positive.
+ bool is_negative = value < 0.0;
+ if (is_negative) value = -value;
+
+ // Get the integer part and the decimal part.
+ double integer_part = floor(value);
+ double decimal_part = value - integer_part;
+
+ // Convert the integer part starting from the back. Always generate
+ // at least one digit.
+ int integer_pos = kBufferSize - 2;
+ do {
+ integer_buffer[integer_pos--] =
+ chars[static_cast<int>(fmod(integer_part, radix))];
+ integer_part /= radix;
+ } while (integer_part >= 1.0);
+ // Sanity check.
+ ASSERT(integer_pos > 0);
+ // Add sign if needed.
+ if (is_negative) integer_buffer[integer_pos--] = '-';
+
+ // Convert the decimal part. Repeatedly multiply by the radix to
+ // generate the next char. Never generate more than kBufferSize - 1
+ // chars.
+ //
+ // TODO(1093998): We will often generate a full decimal_buffer of
+ // chars because hitting zero will often not happen. The right
+ // solution would be to continue until the string representation can
+ // be read back and yield the original value. To implement this
+ // efficiently, we probably have to modify dtoa.
+ int decimal_pos = 0;
+ while ((decimal_part > 0.0) && (decimal_pos < kBufferSize - 1)) {
+ decimal_part *= radix;
+ decimal_buffer[decimal_pos++] =
+ chars[static_cast<int>(floor(decimal_part))];
+ decimal_part -= floor(decimal_part);
+ }
+ decimal_buffer[decimal_pos] = '\0';
+
+ // Compute the result size.
+ int integer_part_size = kBufferSize - 2 - integer_pos;
+ // Make room for zero termination.
+ unsigned result_size = integer_part_size + decimal_pos;
+ // If the number has a decimal part, leave room for the period.
+ if (decimal_pos > 0) result_size++;
+ // Allocate result and fill in the parts.
+ SimpleStringBuilder builder(result_size + 1);
+ builder.AddSubstring(integer_buffer + integer_pos + 1, integer_part_size);
+ if (decimal_pos > 0) builder.AddCharacter('.');
+ builder.AddSubstring(decimal_buffer, decimal_pos);
+ return builder.Finalize();
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/conversions.h b/chromium/v8/src/conversions.h
new file mode 100644
index 00000000000..7aa2d3fb3a8
--- /dev/null
+++ b/chromium/v8/src/conversions.h
@@ -0,0 +1,158 @@
+// 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_CONVERSIONS_H_
+#define V8_CONVERSIONS_H_
+
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+class UnicodeCache;
+
+// Maximum number of significant digits in decimal representation.
+// The longest possible double in decimal representation is
+// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074
+// (768 digits). If we parse a number whose first digits are equal to a
+// mean of 2 adjacent doubles (that could have up to 769 digits) the result
+// must be rounded to the bigger one unless the tail consists of zeros, so
+// we don't need to preserve all the digits.
+const int kMaxSignificantDigits = 772;
+
+
+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);
+}
+
+
+inline bool isBinaryDigit(int x) {
+ return x == '0' || x == '1';
+}
+
+
+// The fast double-to-(unsigned-)int conversion routine does not guarantee
+// rounding towards zero.
+// For NaN and values outside the int range, return INT_MIN or INT_MAX.
+inline int FastD2IChecked(double x) {
+ if (!(x >= INT_MIN)) return INT_MIN; // Negation to catch NaNs.
+ if (x > INT_MAX) return INT_MAX;
+ return static_cast<int>(x);
+}
+
+
+// The fast double-to-(unsigned-)int conversion routine does not guarantee
+// 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.
+inline int FastD2I(double x) {
+ return static_cast<int>(x);
+}
+
+inline unsigned int FastD2UI(double 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.
+ return static_cast<double>(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.
+ return static_cast<double>(x);
+}
+
+
+// This function should match the exact semantics of ECMA-262 9.4.
+inline double DoubleToInteger(double x);
+
+
+// This function should match the exact semantics of ECMA-262 9.5.
+inline int32_t DoubleToInt32(double x);
+
+
+// This function should match the exact semantics of ECMA-262 9.6.
+inline uint32_t DoubleToUint32(double x) {
+ return static_cast<uint32_t>(DoubleToInt32(x));
+}
+
+
+// Enumeration for allowing octals and ignoring junk when converting
+// strings to numbers.
+enum ConversionFlags {
+ NO_FLAGS = 0,
+ ALLOW_HEX = 1,
+ ALLOW_OCTAL = 2,
+ ALLOW_IMPLICIT_OCTAL = 4,
+ ALLOW_BINARY = 8,
+ ALLOW_TRAILING_JUNK = 16
+};
+
+
+// Converts a string into a double value according to ECMA-262 9.3.1
+double StringToDouble(UnicodeCache* unicode_cache,
+ Vector<const char> str,
+ int flags,
+ double empty_string_val = 0);
+double StringToDouble(UnicodeCache* unicode_cache,
+ Vector<const uc16> str,
+ int flags,
+ double empty_string_val = 0);
+// This version expects a zero-terminated character array.
+double StringToDouble(UnicodeCache* unicode_cache,
+ const char* str,
+ int flags,
+ double empty_string_val = 0);
+
+const int kDoubleToCStringMinBufferSize = 100;
+
+// Converts a double to a string value according to ECMA-262 9.8.1.
+// The buffer should be large enough for any floating point number.
+// 100 characters is enough.
+const char* DoubleToCString(double value, Vector<char> buffer);
+
+// Convert an int to a null-terminated string. The returned string is
+// located inside the buffer, but not necessarily at the start.
+const char* IntToCString(int n, Vector<char> buffer);
+
+// Additional number to string conversions for the number type.
+// The caller is responsible for calling free on the returned pointer.
+char* DoubleToFixedCString(double value, int f);
+char* DoubleToExponentialCString(double value, int f);
+char* DoubleToPrecisionCString(double value, int f);
+char* DoubleToRadixCString(double value, int radix);
+
+} } // namespace v8::internal
+
+#endif // V8_CONVERSIONS_H_
diff --git a/chromium/v8/src/counters.cc b/chromium/v8/src/counters.cc
new file mode 100644
index 00000000000..183941206e9
--- /dev/null
+++ b/chromium/v8/src/counters.cc
@@ -0,0 +1,85 @@
+// Copyright 2012 the V8 project authors. 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 "counters.h"
+#include "isolate.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+StatsTable::StatsTable()
+ : lookup_function_(NULL),
+ create_histogram_function_(NULL),
+ add_histogram_sample_function_(NULL) {}
+
+
+int* StatsCounter::FindLocationInStatsTable() const {
+ return Isolate::Current()->stats_table()->FindLocation(name_);
+}
+
+
+void Histogram::AddSample(int sample) {
+ if (Enabled()) {
+ isolate()->stats_table()->AddHistogramSample(histogram_, sample);
+ }
+}
+
+void* Histogram::CreateHistogram() const {
+ return isolate()->stats_table()->
+ CreateHistogram(name_, min_, max_, num_buckets_);
+}
+
+
+// Start the timer.
+void HistogramTimer::Start() {
+ if (Enabled()) {
+ stop_time_ = 0;
+ start_time_ = OS::Ticks();
+ }
+ if (FLAG_log_internal_timer_events) {
+ LOG(isolate(), TimerEvent(Logger::START, name()));
+ }
+}
+
+
+// Stop the timer and record the results.
+void HistogramTimer::Stop() {
+ if (Enabled()) {
+ stop_time_ = OS::Ticks();
+ // Compute the delta between start and stop, in milliseconds.
+ int milliseconds = static_cast<int>(stop_time_ - start_time_) / 1000;
+ AddSample(milliseconds);
+ }
+ if (FLAG_log_internal_timer_events) {
+ LOG(isolate(), TimerEvent(Logger::END, name()));
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/counters.h b/chromium/v8/src/counters.h
new file mode 100644
index 00000000000..a633fea7798
--- /dev/null
+++ b/chromium/v8/src/counters.h
@@ -0,0 +1,285 @@
+// Copyright 2012 the V8 project authors. 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_COUNTERS_H_
+#define V8_COUNTERS_H_
+
+#include "../include/v8.h"
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+// StatsCounters is an interface for plugging into external
+// counters for monitoring. Counters can be looked up and
+// manipulated by name.
+
+class StatsTable {
+ public:
+ // Register an application-defined function where
+ // counters can be looked up.
+ void SetCounterFunction(CounterLookupCallback f) {
+ lookup_function_ = f;
+ }
+
+ // Register an application-defined function to create
+ // a histogram for passing to the AddHistogramSample function
+ void SetCreateHistogramFunction(CreateHistogramCallback f) {
+ create_histogram_function_ = f;
+ }
+
+ // Register an application-defined function to add a sample
+ // to a histogram created with CreateHistogram function
+ void SetAddHistogramSampleFunction(AddHistogramSampleCallback f) {
+ add_histogram_sample_function_ = f;
+ }
+
+ bool HasCounterFunction() const {
+ return lookup_function_ != NULL;
+ }
+
+ // Lookup the location of a counter by name. If the lookup
+ // is successful, returns a non-NULL pointer for writing the
+ // value of the counter. Each thread calling this function
+ // may receive a different location to store it's counter.
+ // The return value must not be cached and re-used across
+ // threads, although a single thread is free to cache it.
+ int* FindLocation(const char* name) {
+ if (!lookup_function_) return NULL;
+ return lookup_function_(name);
+ }
+
+ // Create a histogram by name. If the create is successful,
+ // returns a non-NULL pointer for use with AddHistogramSample
+ // function. min and max define the expected minimum and maximum
+ // sample values. buckets is the maximum number of buckets
+ // that the samples will be grouped into.
+ void* CreateHistogram(const char* name,
+ int min,
+ int max,
+ size_t buckets) {
+ if (!create_histogram_function_) return NULL;
+ return create_histogram_function_(name, min, max, buckets);
+ }
+
+ // Add a sample to a histogram created with the CreateHistogram
+ // function.
+ void AddHistogramSample(void* histogram, int sample) {
+ if (!add_histogram_sample_function_) return;
+ return add_histogram_sample_function_(histogram, sample);
+ }
+
+ private:
+ StatsTable();
+
+ CounterLookupCallback lookup_function_;
+ CreateHistogramCallback create_histogram_function_;
+ AddHistogramSampleCallback add_histogram_sample_function_;
+
+ friend class Isolate;
+
+ DISALLOW_COPY_AND_ASSIGN(StatsTable);
+};
+
+// StatsCounters are dynamically created values which can be tracked in
+// the StatsTable. They are designed to be lightweight to create and
+// easy to use.
+//
+// Internally, a counter represents a value in a row of a StatsTable.
+// The row has a 32bit value for each process/thread in the table and also
+// a name (stored in the table metadata). Since the storage location can be
+// thread-specific, this class cannot be shared across threads.
+class StatsCounter {
+ public:
+ StatsCounter() { }
+ explicit StatsCounter(const char* name)
+ : name_(name), ptr_(NULL), lookup_done_(false) { }
+
+ // Sets the counter to a specific value.
+ void Set(int value) {
+ int* loc = GetPtr();
+ if (loc) *loc = value;
+ }
+
+ // Increments the counter.
+ void Increment() {
+ int* loc = GetPtr();
+ if (loc) (*loc)++;
+ }
+
+ void Increment(int value) {
+ int* loc = GetPtr();
+ if (loc)
+ (*loc) += value;
+ }
+
+ // Decrements the counter.
+ void Decrement() {
+ int* loc = GetPtr();
+ if (loc) (*loc)--;
+ }
+
+ void Decrement(int value) {
+ int* loc = GetPtr();
+ if (loc) (*loc) -= value;
+ }
+
+ // Is this counter enabled?
+ // Returns false if table is full.
+ bool Enabled() {
+ return GetPtr() != NULL;
+ }
+
+ // Get the internal pointer to the counter. This is used
+ // by the code generator to emit code that manipulates a
+ // given counter without calling the runtime system.
+ int* GetInternalPointer() {
+ int* loc = GetPtr();
+ ASSERT(loc != NULL);
+ return loc;
+ }
+
+ protected:
+ // Returns the cached address of this counter location.
+ int* GetPtr() {
+ if (lookup_done_) return ptr_;
+ lookup_done_ = true;
+ ptr_ = FindLocationInStatsTable();
+ return ptr_;
+ }
+
+ private:
+ int* FindLocationInStatsTable() const;
+
+ const char* name_;
+ int* ptr_;
+ bool lookup_done_;
+};
+
+// A Histogram represents a dynamically created histogram in the StatsTable.
+// It will be registered with the histogram system on first use.
+class Histogram {
+ public:
+ Histogram() { }
+ Histogram(const char* name,
+ int min,
+ int max,
+ int num_buckets,
+ Isolate* isolate)
+ : name_(name),
+ min_(min),
+ max_(max),
+ num_buckets_(num_buckets),
+ histogram_(NULL),
+ lookup_done_(false),
+ isolate_(isolate) { }
+
+ // Add a single sample to this histogram.
+ void AddSample(int sample);
+
+ // Returns true if this histogram is enabled.
+ bool Enabled() {
+ return GetHistogram() != NULL;
+ }
+
+ // Reset the cached internal pointer.
+ void Reset() {
+ lookup_done_ = false;
+ }
+
+ protected:
+ // Returns the handle to the histogram.
+ void* GetHistogram() {
+ if (!lookup_done_) {
+ lookup_done_ = true;
+ histogram_ = CreateHistogram();
+ }
+ return histogram_;
+ }
+
+ const char* name() { return name_; }
+ Isolate* isolate() const { return isolate_; }
+
+ private:
+ void* CreateHistogram() const;
+
+ const char* name_;
+ int min_;
+ int max_;
+ int num_buckets_;
+ void* histogram_;
+ bool lookup_done_;
+ Isolate* isolate_;
+};
+
+// A HistogramTimer allows distributions of results to be created.
+class HistogramTimer : public Histogram {
+ public:
+ HistogramTimer() { }
+ HistogramTimer(const char* name,
+ int min,
+ int max,
+ int num_buckets,
+ Isolate* isolate)
+ : Histogram(name, min, max, num_buckets, isolate),
+ start_time_(0),
+ stop_time_(0) { }
+
+ // Start the timer.
+ void Start();
+
+ // Stop the timer and record the results.
+ void Stop();
+
+ // Returns true if the timer is running.
+ bool Running() {
+ return Enabled() && (start_time_ != 0) && (stop_time_ == 0);
+ }
+
+ private:
+ int64_t start_time_;
+ int64_t stop_time_;
+};
+
+// Helper class for scoping a HistogramTimer.
+class HistogramTimerScope BASE_EMBEDDED {
+ public:
+ explicit HistogramTimerScope(HistogramTimer* timer) :
+ timer_(timer) {
+ timer_->Start();
+ }
+ ~HistogramTimerScope() {
+ timer_->Stop();
+ }
+ private:
+ HistogramTimer* timer_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_COUNTERS_H_
diff --git a/chromium/v8/src/cpu-profiler-inl.h b/chromium/v8/src/cpu-profiler-inl.h
new file mode 100644
index 00000000000..868ec64fd61
--- /dev/null
+++ b/chromium/v8/src/cpu-profiler-inl.h
@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef V8_CPU_PROFILER_INL_H_
+#define V8_CPU_PROFILER_INL_H_
+
+#include "cpu-profiler.h"
+
+#include <new>
+#include "circular-queue-inl.h"
+#include "profile-generator-inl.h"
+#include "unbound-queue-inl.h"
+
+namespace v8 {
+namespace internal {
+
+void CodeCreateEventRecord::UpdateCodeMap(CodeMap* code_map) {
+ code_map->AddCode(start, entry, size);
+ if (shared != NULL) {
+ entry->set_shared_id(code_map->GetSharedId(shared));
+ }
+}
+
+
+void CodeMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
+ code_map->MoveCode(from, to);
+}
+
+
+void SharedFunctionInfoMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
+ code_map->MoveCode(from, to);
+}
+
+
+void ReportBuiltinEventRecord::UpdateCodeMap(CodeMap* code_map) {
+ CodeEntry* entry = code_map->FindEntry(start);
+ if (!entry) {
+ // Code objects for builtins should already have been added to the map but
+ // some of them have been filtered out by CpuProfiler.
+ return;
+ }
+ entry->SetBuiltinId(builtin_id);
+}
+
+
+TickSample* ProfilerEventsProcessor::TickSampleEvent() {
+ TickSampleEventRecord* evt =
+ new(ticks_buffer_.Enqueue()) TickSampleEventRecord(last_code_event_id_);
+ return &evt->sample;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_CPU_PROFILER_INL_H_
diff --git a/chromium/v8/src/cpu-profiler.cc b/chromium/v8/src/cpu-profiler.cc
new file mode 100644
index 00000000000..747542f7378
--- /dev/null
+++ b/chromium/v8/src/cpu-profiler.cc
@@ -0,0 +1,509 @@
+// Copyright 2012 the V8 project authors. 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 "cpu-profiler-inl.h"
+
+#include "compiler.h"
+#include "frames-inl.h"
+#include "hashmap.h"
+#include "log-inl.h"
+#include "vm-state-inl.h"
+
+#include "../include/v8-profiler.h"
+
+namespace v8 {
+namespace internal {
+
+static const int kTickSamplesBufferChunkSize = 64 * KB;
+static const int kTickSamplesBufferChunksCount = 16;
+static const int kProfilerStackSize = 64 * KB;
+
+
+ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
+ : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
+ generator_(generator),
+ running_(true),
+ ticks_buffer_(sizeof(TickSampleEventRecord),
+ kTickSamplesBufferChunkSize,
+ kTickSamplesBufferChunksCount),
+ last_code_event_id_(0), last_processed_code_event_id_(0) {
+}
+
+
+void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
+ event.generic.order = ++last_code_event_id_;
+ events_buffer_.Enqueue(event);
+}
+
+
+void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) {
+ TickSampleEventRecord record(last_code_event_id_);
+ TickSample* sample = &record.sample;
+ sample->state = isolate->current_vm_state();
+ sample->pc = reinterpret_cast<Address>(sample); // Not NULL.
+ for (StackTraceFrameIterator it(isolate);
+ !it.done() && sample->frames_count < TickSample::kMaxFramesCount;
+ it.Advance()) {
+ sample->stack[sample->frames_count++] = it.frame()->pc();
+ }
+ ticks_from_vm_buffer_.Enqueue(record);
+}
+
+
+void ProfilerEventsProcessor::StopSynchronously() {
+ if (!running_) return;
+ running_ = false;
+ Join();
+}
+
+
+bool ProfilerEventsProcessor::ProcessCodeEvent() {
+ CodeEventsContainer record;
+ if (events_buffer_.Dequeue(&record)) {
+ switch (record.generic.type) {
+#define PROFILER_TYPE_CASE(type, clss) \
+ case CodeEventRecord::type: \
+ record.clss##_.UpdateCodeMap(generator_->code_map()); \
+ break;
+
+ CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
+
+#undef PROFILER_TYPE_CASE
+ default: return true; // Skip record.
+ }
+ last_processed_code_event_id_ = record.generic.order;
+ return true;
+ }
+ return false;
+}
+
+
+bool ProfilerEventsProcessor::ProcessTicks() {
+ while (true) {
+ while (!ticks_from_vm_buffer_.IsEmpty()
+ && ticks_from_vm_buffer_.Peek()->order ==
+ last_processed_code_event_id_) {
+ TickSampleEventRecord record;
+ ticks_from_vm_buffer_.Dequeue(&record);
+ generator_->RecordTickSample(record.sample);
+ }
+
+ const TickSampleEventRecord* rec =
+ TickSampleEventRecord::cast(ticks_buffer_.StartDequeue());
+ if (rec == NULL) return !ticks_from_vm_buffer_.IsEmpty();
+ // Make a local copy of tick sample record to ensure that it won't
+ // be modified as we are processing it. This is possible as the
+ // sampler writes w/o any sync to the queue, so if the processor
+ // will get far behind, a record may be modified right under its
+ // feet.
+ TickSampleEventRecord record = *rec;
+ if (record.order != last_processed_code_event_id_) return true;
+
+ // A paranoid check to make sure that we don't get a memory overrun
+ // in case of frames_count having a wild value.
+ if (record.sample.frames_count < 0
+ || record.sample.frames_count > TickSample::kMaxFramesCount)
+ record.sample.frames_count = 0;
+ generator_->RecordTickSample(record.sample);
+ ticks_buffer_.FinishDequeue();
+ }
+}
+
+
+void ProfilerEventsProcessor::Run() {
+ while (running_) {
+ // Process ticks until we have any.
+ if (ProcessTicks()) {
+ // All ticks of the current last_processed_code_event_id_ are processed,
+ // proceed to the next code event.
+ ProcessCodeEvent();
+ }
+ YieldCPU();
+ }
+
+ // Process remaining tick events.
+ ticks_buffer_.FlushResidualRecords();
+ do {
+ ProcessTicks();
+ } while (ProcessCodeEvent());
+}
+
+
+int CpuProfiler::GetProfilesCount() {
+ // The count of profiles doesn't depend on a security token.
+ return profiles_->profiles()->length();
+}
+
+
+CpuProfile* CpuProfiler::GetProfile(int index) {
+ return profiles_->profiles()->at(index);
+}
+
+
+TickSample* CpuProfiler::TickSampleEvent() {
+ if (is_profiling_) return processor_->TickSampleEvent();
+ return NULL;
+}
+
+
+void CpuProfiler::DeleteAllProfiles() {
+ if (is_profiling_) StopProcessor();
+ ResetProfiles();
+}
+
+
+void CpuProfiler::DeleteProfile(CpuProfile* profile) {
+ profiles_->RemoveProfile(profile);
+ delete profile;
+}
+
+
+static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag) {
+ return FLAG_prof_browser_mode
+ && (tag != Logger::CALLBACK_TAG
+ && tag != Logger::FUNCTION_TAG
+ && tag != Logger::LAZY_COMPILE_TAG
+ && tag != Logger::REG_EXP_TAG
+ && tag != Logger::SCRIPT_TAG);
+}
+
+
+void CpuProfiler::CallbackEvent(Name* name, Address entry_point) {
+ if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = entry_point;
+ rec->entry = profiles_->NewCodeEntry(
+ Logger::CALLBACK_TAG,
+ profiles_->GetName(name));
+ rec->size = 1;
+ rec->shared = NULL;
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ const char* name) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = code->address();
+ rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
+ rec->size = code->ExecutableSize();
+ rec->shared = NULL;
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ Name* name) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = code->address();
+ rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
+ rec->size = code->ExecutableSize();
+ rec->shared = NULL;
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* name) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = code->address();
+ rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name));
+ if (info) {
+ rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
+ }
+ if (shared->script()->IsScript()) {
+ ASSERT(Script::cast(shared->script()));
+ Script* script = Script::cast(shared->script());
+ rec->entry->set_script_id(script->id()->value());
+ }
+ rec->size = code->ExecutableSize();
+ rec->shared = shared->address();
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* source, int line) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = code->address();
+ rec->entry = profiles_->NewCodeEntry(
+ tag,
+ profiles_->GetFunctionName(shared->DebugName()),
+ CodeEntry::kEmptyNamePrefix,
+ profiles_->GetName(source),
+ line);
+ if (info) {
+ rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges());
+ }
+ ASSERT(Script::cast(shared->script()));
+ Script* script = Script::cast(shared->script());
+ rec->entry->set_script_id(script->id()->value());
+ rec->size = code->ExecutableSize();
+ rec->shared = shared->address();
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ int args_count) {
+ if (FilterOutCodeCreateEvent(tag)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = code->address();
+ rec->entry = profiles_->NewCodeEntry(
+ tag,
+ profiles_->GetName(args_count),
+ "args_count: ");
+ rec->size = code->ExecutableSize();
+ rec->shared = NULL;
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::CodeMoveEvent(Address from, Address to) {
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
+ CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
+ rec->from = from;
+ rec->to = to;
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::CodeDeleteEvent(Address from) {
+}
+
+
+void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) {
+ CodeEventsContainer evt_rec(CodeEventRecord::SHARED_FUNC_MOVE);
+ SharedFunctionInfoMoveEventRecord* rec =
+ &evt_rec.SharedFunctionInfoMoveEventRecord_;
+ rec->from = from;
+ rec->to = to;
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) {
+ if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = entry_point;
+ rec->entry = profiles_->NewCodeEntry(
+ Logger::CALLBACK_TAG,
+ profiles_->GetName(name),
+ "get ");
+ rec->size = 1;
+ rec->shared = NULL;
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) {
+ if (FilterOutCodeCreateEvent(Logger::REG_EXP_TAG)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = code->address();
+ rec->entry = profiles_->NewCodeEntry(
+ Logger::REG_EXP_TAG,
+ profiles_->GetName(source),
+ "RegExp: ");
+ rec->size = code->ExecutableSize();
+ processor_->Enqueue(evt_rec);
+}
+
+
+void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) {
+ if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return;
+ CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
+ CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
+ rec->start = entry_point;
+ rec->entry = profiles_->NewCodeEntry(
+ Logger::CALLBACK_TAG,
+ profiles_->GetName(name),
+ "set ");
+ rec->size = 1;
+ rec->shared = NULL;
+ processor_->Enqueue(evt_rec);
+}
+
+
+CpuProfiler::CpuProfiler(Isolate* isolate)
+ : isolate_(isolate),
+ profiles_(new CpuProfilesCollection()),
+ next_profile_uid_(1),
+ generator_(NULL),
+ processor_(NULL),
+ need_to_stop_sampler_(false),
+ is_profiling_(false) {
+}
+
+
+CpuProfiler::CpuProfiler(Isolate* isolate,
+ CpuProfilesCollection* test_profiles,
+ ProfileGenerator* test_generator,
+ ProfilerEventsProcessor* test_processor)
+ : isolate_(isolate),
+ profiles_(test_profiles),
+ next_profile_uid_(1),
+ generator_(test_generator),
+ processor_(test_processor),
+ need_to_stop_sampler_(false),
+ is_profiling_(false) {
+}
+
+
+CpuProfiler::~CpuProfiler() {
+ ASSERT(!is_profiling_);
+ delete profiles_;
+}
+
+
+void CpuProfiler::ResetProfiles() {
+ delete profiles_;
+ profiles_ = new CpuProfilesCollection();
+}
+
+
+void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
+ if (profiles_->StartProfiling(title, next_profile_uid_++, record_samples)) {
+ StartProcessorIfNotStarted();
+ }
+ processor_->AddCurrentStack(isolate_);
+}
+
+
+void CpuProfiler::StartProfiling(String* title, bool record_samples) {
+ StartProfiling(profiles_->GetName(title), record_samples);
+}
+
+
+void CpuProfiler::StartProcessorIfNotStarted() {
+ if (processor_ == NULL) {
+ Logger* logger = isolate_->logger();
+ // Disable logging when using the new implementation.
+ saved_logging_nesting_ = logger->logging_nesting_;
+ logger->logging_nesting_ = 0;
+ generator_ = new ProfileGenerator(profiles_);
+ processor_ = new ProfilerEventsProcessor(generator_);
+ is_profiling_ = true;
+ processor_->StartSynchronously();
+ // Enumerate stuff we already have in the heap.
+ ASSERT(isolate_->heap()->HasBeenSetUp());
+ if (!FLAG_prof_browser_mode) {
+ logger->LogCodeObjects();
+ }
+ logger->LogCompiledFunctions();
+ logger->LogAccessorCallbacks();
+ LogBuiltins();
+ // Enable stack sampling.
+ Sampler* sampler = logger->sampler();
+ sampler->IncreaseProfilingDepth();
+ if (!sampler->IsActive()) {
+ sampler->Start();
+ need_to_stop_sampler_ = true;
+ }
+ }
+}
+
+
+CpuProfile* CpuProfiler::StopProfiling(const char* title) {
+ if (!is_profiling_) return NULL;
+ StopProcessorIfLastProfile(title);
+ CpuProfile* result = profiles_->StopProfiling(title);
+ if (result != NULL) {
+ result->Print();
+ }
+ return result;
+}
+
+
+CpuProfile* CpuProfiler::StopProfiling(String* title) {
+ if (!is_profiling_) return NULL;
+ const char* profile_title = profiles_->GetName(title);
+ StopProcessorIfLastProfile(profile_title);
+ return profiles_->StopProfiling(profile_title);
+}
+
+
+void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
+ if (profiles_->IsLastProfile(title)) StopProcessor();
+}
+
+
+void CpuProfiler::StopProcessor() {
+ Logger* logger = isolate_->logger();
+ Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_);
+ sampler->DecreaseProfilingDepth();
+ if (need_to_stop_sampler_) {
+ sampler->Stop();
+ need_to_stop_sampler_ = false;
+ }
+ is_profiling_ = false;
+ processor_->StopSynchronously();
+ delete processor_;
+ delete generator_;
+ processor_ = NULL;
+ generator_ = NULL;
+ logger->logging_nesting_ = saved_logging_nesting_;
+}
+
+
+void CpuProfiler::LogBuiltins() {
+ Builtins* builtins = isolate_->builtins();
+ ASSERT(builtins->is_initialized());
+ for (int i = 0; i < Builtins::builtin_count; i++) {
+ CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
+ ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
+ Builtins::Name id = static_cast<Builtins::Name>(i);
+ rec->start = builtins->builtin(id)->address();
+ rec->builtin_id = id;
+ processor_->Enqueue(evt_rec);
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/cpu-profiler.h b/chromium/v8/src/cpu-profiler.h
new file mode 100644
index 00000000000..1dd405e5d76
--- /dev/null
+++ b/chromium/v8/src/cpu-profiler.h
@@ -0,0 +1,268 @@
+// Copyright 2012 the V8 project authors. 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_CPU_PROFILER_H_
+#define V8_CPU_PROFILER_H_
+
+#include "allocation.h"
+#include "atomicops.h"
+#include "circular-queue.h"
+#include "sampler.h"
+#include "unbound-queue.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class CodeEntry;
+class CodeMap;
+class CompilationInfo;
+class CpuProfile;
+class CpuProfilesCollection;
+class ProfileGenerator;
+
+#define CODE_EVENTS_TYPE_LIST(V) \
+ V(CODE_CREATION, CodeCreateEventRecord) \
+ V(CODE_MOVE, CodeMoveEventRecord) \
+ V(SHARED_FUNC_MOVE, SharedFunctionInfoMoveEventRecord) \
+ V(REPORT_BUILTIN, ReportBuiltinEventRecord)
+
+
+class CodeEventRecord {
+ public:
+#define DECLARE_TYPE(type, ignore) type,
+ enum Type {
+ NONE = 0,
+ CODE_EVENTS_TYPE_LIST(DECLARE_TYPE)
+ NUMBER_OF_TYPES
+ };
+#undef DECLARE_TYPE
+
+ Type type;
+ mutable unsigned order;
+};
+
+
+class CodeCreateEventRecord : public CodeEventRecord {
+ public:
+ Address start;
+ CodeEntry* entry;
+ unsigned size;
+ Address shared;
+
+ INLINE(void UpdateCodeMap(CodeMap* code_map));
+};
+
+
+class CodeMoveEventRecord : public CodeEventRecord {
+ public:
+ Address from;
+ Address to;
+
+ INLINE(void UpdateCodeMap(CodeMap* code_map));
+};
+
+
+class SharedFunctionInfoMoveEventRecord : public CodeEventRecord {
+ public:
+ Address from;
+ Address to;
+
+ INLINE(void UpdateCodeMap(CodeMap* code_map));
+};
+
+
+class ReportBuiltinEventRecord : public CodeEventRecord {
+ public:
+ Address start;
+ Builtins::Name builtin_id;
+
+ INLINE(void UpdateCodeMap(CodeMap* code_map));
+};
+
+
+class TickSampleEventRecord {
+ public:
+ // The parameterless constructor is used when we dequeue data from
+ // the ticks buffer.
+ TickSampleEventRecord() { }
+ explicit TickSampleEventRecord(unsigned order) : order(order) { }
+
+ unsigned order;
+ TickSample sample;
+
+ static TickSampleEventRecord* cast(void* value) {
+ return reinterpret_cast<TickSampleEventRecord*>(value);
+ }
+};
+
+
+class CodeEventsContainer {
+ public:
+ explicit CodeEventsContainer(
+ CodeEventRecord::Type type = CodeEventRecord::NONE) {
+ generic.type = type;
+ }
+ union {
+ CodeEventRecord generic;
+#define DECLARE_CLASS(ignore, type) type type##_;
+ CODE_EVENTS_TYPE_LIST(DECLARE_CLASS)
+#undef DECLARE_TYPE
+ };
+};
+
+
+// This class implements both the profile events processor thread and
+// methods called by event producers: VM and stack sampler threads.
+class ProfilerEventsProcessor : public Thread {
+ public:
+ explicit ProfilerEventsProcessor(ProfileGenerator* generator);
+ virtual ~ProfilerEventsProcessor() {}
+
+ // Thread control.
+ virtual void Run();
+ void StopSynchronously();
+ INLINE(bool running()) { return running_; }
+ void Enqueue(const CodeEventsContainer& event);
+
+ // Puts current stack into tick sample events buffer.
+ void AddCurrentStack(Isolate* isolate);
+
+ // Tick sample events are filled directly in the buffer of the circular
+ // queue (because the structure is of fixed width, but usually not all
+ // stack frame entries are filled.) This method returns a pointer to the
+ // next record of the buffer.
+ INLINE(TickSample* TickSampleEvent());
+
+ private:
+ // Called from events processing thread (Run() method.)
+ bool ProcessCodeEvent();
+ bool ProcessTicks();
+
+ ProfileGenerator* generator_;
+ bool running_;
+ UnboundQueue<CodeEventsContainer> events_buffer_;
+ SamplingCircularQueue ticks_buffer_;
+ UnboundQueue<TickSampleEventRecord> ticks_from_vm_buffer_;
+ unsigned last_code_event_id_;
+ unsigned last_processed_code_event_id_;
+};
+
+
+#define PROFILE(IsolateGetter, Call) \
+ do { \
+ Isolate* cpu_profiler_isolate = (IsolateGetter); \
+ v8::internal::Logger* logger = cpu_profiler_isolate->logger(); \
+ CpuProfiler* cpu_profiler = cpu_profiler_isolate->cpu_profiler(); \
+ if (logger->is_logging_code_events() || cpu_profiler->is_profiling()) { \
+ logger->Call; \
+ } \
+ } while (false)
+
+
+class CpuProfiler : public CodeEventListener {
+ public:
+ explicit CpuProfiler(Isolate* isolate);
+
+ CpuProfiler(Isolate* isolate,
+ CpuProfilesCollection* test_collection,
+ ProfileGenerator* test_generator,
+ ProfilerEventsProcessor* test_processor);
+
+ virtual ~CpuProfiler();
+
+ void StartProfiling(const char* title, bool record_samples = false);
+ void StartProfiling(String* title, bool record_samples);
+ CpuProfile* StopProfiling(const char* title);
+ CpuProfile* StopProfiling(String* title);
+ int GetProfilesCount();
+ CpuProfile* GetProfile(int index);
+ void DeleteAllProfiles();
+ void DeleteProfile(CpuProfile* profile);
+
+ // Invoked from stack sampler (thread or signal handler.)
+ TickSample* TickSampleEvent();
+
+ // Must be called via PROFILE macro, otherwise will crash when
+ // profiling is not enabled.
+ virtual void CallbackEvent(Name* name, Address entry_point);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code, const char* comment);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code, Name* name);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* name);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* source, int line);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code, int args_count);
+ virtual void CodeMovingGCEvent() {}
+ virtual void CodeMoveEvent(Address from, Address to);
+ virtual void CodeDeleteEvent(Address from);
+ virtual void GetterCallbackEvent(Name* name, Address entry_point);
+ virtual void RegExpCodeCreateEvent(Code* code, String* source);
+ virtual void SetterCallbackEvent(Name* name, Address entry_point);
+ virtual void SharedFunctionInfoMoveEvent(Address from, Address to);
+
+ INLINE(bool is_profiling() const) { return is_profiling_; }
+ bool* is_profiling_address() {
+ return &is_profiling_;
+ }
+
+ ProfileGenerator* generator() const { return generator_; }
+ ProfilerEventsProcessor* processor() const { return processor_; }
+ Isolate* isolate() const { return isolate_; }
+
+ private:
+ void StartProcessorIfNotStarted();
+ void StopProcessorIfLastProfile(const char* title);
+ void StopProcessor();
+ void ResetProfiles();
+ void LogBuiltins();
+
+ Isolate* isolate_;
+ CpuProfilesCollection* profiles_;
+ unsigned next_profile_uid_;
+ ProfileGenerator* generator_;
+ ProfilerEventsProcessor* processor_;
+ int saved_logging_nesting_;
+ bool need_to_stop_sampler_;
+ bool is_profiling_;
+
+ DISALLOW_COPY_AND_ASSIGN(CpuProfiler);
+};
+
+} } // namespace v8::internal
+
+
+#endif // V8_CPU_PROFILER_H_
diff --git a/chromium/v8/src/cpu.h b/chromium/v8/src/cpu.h
new file mode 100644
index 00000000000..247af71aa35
--- /dev/null
+++ b/chromium/v8/src/cpu.h
@@ -0,0 +1,69 @@
+// 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.
+
+// This module contains the architecture-specific code. This make the rest of
+// the code less dependent on differences between different processor
+// architecture.
+// The classes have the same definition for all architectures. The
+// implementation for a particular architecture is put in cpu_<arch>.cc.
+// The build system then uses the implementation for the target architecture.
+//
+
+#ifndef V8_CPU_H_
+#define V8_CPU_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// CPU
+//
+// This class has static methods for the architecture specific functions. Add
+// methods here to cope with differences between the supported architectures.
+//
+// For each architecture the file cpu_<arch>.cc contains the implementation of
+// these functions.
+
+class CPU : public AllStatic {
+ public:
+ // Initializes the cpu architecture support. Called once at VM startup.
+ static void SetUp();
+
+ static bool SupportsCrankshaft();
+
+ // Flush instruction cache.
+ static void FlushICache(void* start, size_t size);
+
+ // Try to activate a system level debugger.
+ static void DebugBreak();
+};
+
+} } // namespace v8::internal
+
+#endif // V8_CPU_H_
diff --git a/chromium/v8/src/d8-debug.cc b/chromium/v8/src/d8-debug.cc
new file mode 100644
index 00000000000..9a72518f4cb
--- /dev/null
+++ b/chromium/v8/src/d8-debug.cc
@@ -0,0 +1,374 @@
+// Copyright 2012 the V8 project authors. 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.
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+#include "d8.h"
+#include "d8-debug.h"
+#include "platform.h"
+#include "debug-agent.h"
+
+
+namespace v8 {
+
+static bool was_running = true;
+
+void PrintPrompt(bool is_running) {
+ const char* prompt = is_running? "> " : "dbg> ";
+ was_running = is_running;
+ printf("%s", prompt);
+ fflush(stdout);
+}
+
+
+void PrintPrompt() {
+ PrintPrompt(was_running);
+}
+
+
+void HandleDebugEvent(const Debug::EventDetails& event_details) {
+ // TODO(svenpanne) There should be a way to retrieve this in the callback.
+ Isolate* isolate = Isolate::GetCurrent();
+ HandleScope scope(isolate);
+
+ DebugEvent event = event_details.GetEvent();
+ // Check for handled event.
+ if (event != Break && event != Exception && event != AfterCompile) {
+ return;
+ }
+
+ TryCatch try_catch;
+
+ // Get the toJSONProtocol function on the event and get the JSON format.
+ Local<String> to_json_fun_name = String::New("toJSONProtocol");
+ Handle<Object> event_data = event_details.GetEventData();
+ Local<Function> to_json_fun =
+ Local<Function>::Cast(event_data->Get(to_json_fun_name));
+ Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(isolate, &try_catch);
+ return;
+ }
+
+ // Print the event details.
+ Handle<Object> details =
+ Shell::DebugMessageDetails(isolate, Handle<String>::Cast(event_json));
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(isolate, &try_catch);
+ return;
+ }
+ String::Utf8Value str(details->Get(String::New("text")));
+ if (str.length() == 0) {
+ // Empty string is used to signal not to process this event.
+ return;
+ }
+ printf("%s\n", *str);
+
+ // Get the debug command processor.
+ Local<String> fun_name = String::New("debugCommandProcessor");
+ Handle<Object> exec_state = event_details.GetExecutionState();
+ Local<Function> fun = Local<Function>::Cast(exec_state->Get(fun_name));
+ Local<Object> cmd_processor =
+ Local<Object>::Cast(fun->Call(exec_state, 0, NULL));
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(isolate, &try_catch);
+ return;
+ }
+
+ static const int kBufferSize = 256;
+ bool running = false;
+ while (!running) {
+ char command[kBufferSize];
+ PrintPrompt(running);
+ char* str = fgets(command, kBufferSize, stdin);
+ if (str == NULL) break;
+
+ // Ignore empty commands.
+ if (strlen(command) == 0) continue;
+
+ TryCatch try_catch;
+
+ // Convert the debugger command to a JSON debugger request.
+ Handle<Value> request =
+ Shell::DebugCommandToJSONRequest(isolate, String::New(command));
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(isolate, &try_catch);
+ continue;
+ }
+
+ // If undefined is returned the command was handled internally and there is
+ // no JSON to send.
+ if (request->IsUndefined()) {
+ continue;
+ }
+
+ Handle<String> fun_name;
+ Handle<Function> fun;
+ // All the functions used below take one argument.
+ static const int kArgc = 1;
+ Handle<Value> args[kArgc];
+
+ // Invoke the JavaScript to convert the debug command line to a JSON
+ // request, invoke the JSON request and convert the JSON respose to a text
+ // representation.
+ fun_name = String::New("processDebugRequest");
+ fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
+ args[0] = request;
+ Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(isolate, &try_catch);
+ continue;
+ }
+ Handle<String> response = Handle<String>::Cast(response_val);
+
+ // Convert the debugger response into text details and the running state.
+ Handle<Object> response_details =
+ Shell::DebugMessageDetails(isolate, response);
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(isolate, &try_catch);
+ continue;
+ }
+ String::Utf8Value text_str(response_details->Get(String::New("text")));
+ if (text_str.length() > 0) {
+ printf("%s\n", *text_str);
+ }
+ running =
+ response_details->Get(String::New("running"))->ToBoolean()->Value();
+ }
+}
+
+
+void RunRemoteDebugger(Isolate* isolate, int port) {
+ RemoteDebugger debugger(isolate, port);
+ debugger.Run();
+}
+
+
+void RemoteDebugger::Run() {
+ bool ok;
+
+ // Make sure that socket support is initialized.
+ ok = i::Socket::SetUp();
+ if (!ok) {
+ printf("Unable to initialize socket support %d\n", i::Socket::LastError());
+ return;
+ }
+
+ // Connect to the debugger agent.
+ conn_ = i::OS::CreateSocket();
+ static const int kPortStrSize = 6;
+ char port_str[kPortStrSize];
+ i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
+ ok = conn_->Connect("localhost", port_str);
+ if (!ok) {
+ printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
+ return;
+ }
+
+ // Start the receiver thread.
+ ReceiverThread receiver(this);
+ receiver.Start();
+
+ // Start the keyboard thread.
+ KeyboardThread keyboard(this);
+ keyboard.Start();
+ PrintPrompt();
+
+ // Process events received from debugged VM and from the keyboard.
+ bool terminate = false;
+ while (!terminate) {
+ event_available_->Wait();
+ RemoteDebuggerEvent* event = GetEvent();
+ switch (event->type()) {
+ case RemoteDebuggerEvent::kMessage:
+ HandleMessageReceived(event->data());
+ break;
+ case RemoteDebuggerEvent::kKeyboard:
+ HandleKeyboardCommand(event->data());
+ break;
+ case RemoteDebuggerEvent::kDisconnect:
+ terminate = true;
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ delete event;
+ }
+
+ // Wait for the receiver thread to end.
+ receiver.Join();
+}
+
+
+void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
+ RemoteDebuggerEvent* event =
+ new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
+ AddEvent(event);
+}
+
+
+void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
+ RemoteDebuggerEvent* event =
+ new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
+ AddEvent(event);
+}
+
+
+void RemoteDebugger::ConnectionClosed() {
+ RemoteDebuggerEvent* event =
+ new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
+ i::SmartArrayPointer<char>());
+ AddEvent(event);
+}
+
+
+void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
+ i::ScopedLock lock(event_access_);
+ if (head_ == NULL) {
+ ASSERT(tail_ == NULL);
+ head_ = event;
+ tail_ = event;
+ } else {
+ ASSERT(tail_ != NULL);
+ tail_->set_next(event);
+ tail_ = event;
+ }
+ event_available_->Signal();
+}
+
+
+RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
+ i::ScopedLock lock(event_access_);
+ ASSERT(head_ != NULL);
+ RemoteDebuggerEvent* result = head_;
+ head_ = head_->next();
+ if (head_ == NULL) {
+ ASSERT(tail_ == result);
+ tail_ = NULL;
+ }
+ return result;
+}
+
+
+void RemoteDebugger::HandleMessageReceived(char* message) {
+ Locker lock(isolate_);
+ HandleScope scope(isolate_);
+
+ // Print the event details.
+ TryCatch try_catch;
+ Handle<Object> details =
+ Shell::DebugMessageDetails(isolate_,
+ Handle<String>::Cast(String::New(message)));
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(isolate_, &try_catch);
+ PrintPrompt();
+ return;
+ }
+ String::Utf8Value str(details->Get(String::New("text")));
+ if (str.length() == 0) {
+ // Empty string is used to signal not to process this event.
+ return;
+ }
+ if (*str != NULL) {
+ printf("%s\n", *str);
+ } else {
+ printf("???\n");
+ }
+
+ bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
+ PrintPrompt(is_running);
+}
+
+
+void RemoteDebugger::HandleKeyboardCommand(char* command) {
+ Locker lock(isolate_);
+ HandleScope scope(isolate_);
+
+ // Convert the debugger command to a JSON debugger request.
+ TryCatch try_catch;
+ Handle<Value> request =
+ Shell::DebugCommandToJSONRequest(isolate_, String::New(command));
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(isolate_, &try_catch);
+ PrintPrompt();
+ return;
+ }
+
+ // If undefined is returned the command was handled internally and there is
+ // no JSON to send.
+ if (request->IsUndefined()) {
+ PrintPrompt();
+ return;
+ }
+
+ // Send the JSON debugger request.
+ i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
+}
+
+
+void ReceiverThread::Run() {
+ // Receive the connect message (with empty body).
+ i::SmartArrayPointer<char> message =
+ i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
+ ASSERT(*message == NULL);
+
+ while (true) {
+ // Receive a message.
+ i::SmartArrayPointer<char> message =
+ i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
+ if (*message == NULL) {
+ remote_debugger_->ConnectionClosed();
+ return;
+ }
+
+ // Pass the message to the main thread.
+ remote_debugger_->MessageReceived(message);
+ }
+}
+
+
+void KeyboardThread::Run() {
+ static const int kBufferSize = 256;
+ while (true) {
+ // read keyboard input.
+ char command[kBufferSize];
+ char* str = fgets(command, kBufferSize, stdin);
+ if (str == NULL) {
+ break;
+ }
+
+ // Pass the keyboard command to the main thread.
+ remote_debugger_->KeyboardCommand(
+ i::SmartArrayPointer<char>(i::StrDup(command)));
+ }
+}
+
+
+} // namespace v8
+
+#endif // ENABLE_DEBUGGER_SUPPORT
diff --git a/chromium/v8/src/d8-debug.h b/chromium/v8/src/d8-debug.h
new file mode 100644
index 00000000000..2386b6bd6c4
--- /dev/null
+++ b/chromium/v8/src/d8-debug.h
@@ -0,0 +1,156 @@
+// Copyright 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_D8_DEBUG_H_
+#define V8_D8_DEBUG_H_
+
+
+#include "d8.h"
+#include "debug.h"
+
+
+namespace v8 {
+
+
+void HandleDebugEvent(const Debug::EventDetails& event_details);
+
+// Start the remove debugger connecting to a V8 debugger agent on the specified
+// port.
+void RunRemoteDebugger(Isolate* isolate, int port);
+
+// Forward declerations.
+class RemoteDebuggerEvent;
+class ReceiverThread;
+
+
+// Remote debugging class.
+class RemoteDebugger {
+ public:
+ explicit RemoteDebugger(Isolate* isolate, int port)
+ : isolate_(isolate),
+ port_(port),
+ event_access_(i::OS::CreateMutex()),
+ event_available_(i::OS::CreateSemaphore(0)),
+ head_(NULL), tail_(NULL) {}
+ void Run();
+
+ // Handle events from the subordinate threads.
+ void MessageReceived(i::SmartArrayPointer<char> message);
+ void KeyboardCommand(i::SmartArrayPointer<char> command);
+ void ConnectionClosed();
+
+ private:
+ // Add new debugger event to the list.
+ void AddEvent(RemoteDebuggerEvent* event);
+ // Read next debugger event from the list.
+ RemoteDebuggerEvent* GetEvent();
+
+ // Handle a message from the debugged V8.
+ void HandleMessageReceived(char* message);
+ // Handle a keyboard command.
+ void HandleKeyboardCommand(char* command);
+
+ // Get connection to agent in debugged V8.
+ i::Socket* conn() { return conn_; }
+
+ Isolate* isolate_;
+ int port_; // Port used to connect to debugger V8.
+ i::Socket* conn_; // Connection to debugger agent in debugged V8.
+
+ // Linked list of events from debugged V8 and from keyboard input. Access to
+ // the list is guarded by a mutex and a semaphore signals new items in the
+ // list.
+ i::Mutex* event_access_;
+ i::Semaphore* event_available_;
+ RemoteDebuggerEvent* head_;
+ RemoteDebuggerEvent* tail_;
+
+ friend class ReceiverThread;
+};
+
+
+// Thread reading from debugged V8 instance.
+class ReceiverThread: public i::Thread {
+ public:
+ explicit ReceiverThread(RemoteDebugger* remote_debugger)
+ : Thread("d8:ReceiverThrd"),
+ remote_debugger_(remote_debugger) {}
+ ~ReceiverThread() {}
+
+ void Run();
+
+ private:
+ RemoteDebugger* remote_debugger_;
+};
+
+
+// Thread reading keyboard input.
+class KeyboardThread: public i::Thread {
+ public:
+ explicit KeyboardThread(RemoteDebugger* remote_debugger)
+ : Thread("d8:KeyboardThrd"),
+ remote_debugger_(remote_debugger) {}
+ ~KeyboardThread() {}
+
+ void Run();
+
+ private:
+ RemoteDebugger* remote_debugger_;
+};
+
+
+// Events processed by the main deubgger thread.
+class RemoteDebuggerEvent {
+ public:
+ RemoteDebuggerEvent(int type, i::SmartArrayPointer<char> data)
+ : type_(type), data_(data), next_(NULL) {
+ ASSERT(type == kMessage || type == kKeyboard || type == kDisconnect);
+ }
+
+ static const int kMessage = 1;
+ static const int kKeyboard = 2;
+ static const int kDisconnect = 3;
+
+ int type() { return type_; }
+ char* data() { return *data_; }
+
+ private:
+ void set_next(RemoteDebuggerEvent* event) { next_ = event; }
+ RemoteDebuggerEvent* next() { return next_; }
+
+ int type_;
+ i::SmartArrayPointer<char> data_;
+ RemoteDebuggerEvent* next_;
+
+ friend class RemoteDebugger;
+};
+
+
+} // namespace v8
+
+
+#endif // V8_D8_DEBUG_H_
diff --git a/chromium/v8/src/d8-posix.cc b/chromium/v8/src/d8-posix.cc
new file mode 100644
index 00000000000..424dbbb3930
--- /dev/null
+++ b/chromium/v8/src/d8-posix.cc
@@ -0,0 +1,705 @@
+// 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.
+
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+
+#include "d8.h"
+#include "d8-debug.h"
+#include "debug.h"
+
+
+namespace v8 {
+
+
+// If the buffer ends in the middle of a UTF-8 sequence then we return
+// the length of the string up to but not including the incomplete UTF-8
+// sequence. If the buffer ends with a valid UTF-8 sequence then we
+// return the whole buffer.
+static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
+ int answer = len;
+ // 1-byte encoding.
+ static const int kUtf8SingleByteMask = 0x80;
+ static const int kUtf8SingleByteValue = 0x00;
+ // 2-byte encoding.
+ static const int kUtf8TwoByteMask = 0xe0;
+ static const int kUtf8TwoByteValue = 0xc0;
+ // 3-byte encoding.
+ static const int kUtf8ThreeByteMask = 0xf0;
+ static const int kUtf8ThreeByteValue = 0xe0;
+ // 4-byte encoding.
+ static const int kUtf8FourByteMask = 0xf8;
+ static const int kUtf8FourByteValue = 0xf0;
+ // Subsequent bytes of a multi-byte encoding.
+ static const int kMultiByteMask = 0xc0;
+ static const int kMultiByteValue = 0x80;
+ int multi_byte_bytes_seen = 0;
+ while (answer > 0) {
+ int c = buffer[answer - 1];
+ // Ends in valid single-byte sequence?
+ if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
+ // Ends in one or more subsequent bytes of a multi-byte value?
+ if ((c & kMultiByteMask) == kMultiByteValue) {
+ multi_byte_bytes_seen++;
+ answer--;
+ } else {
+ if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
+ if (multi_byte_bytes_seen >= 1) {
+ return answer + 2;
+ }
+ return answer - 1;
+ } else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
+ if (multi_byte_bytes_seen >= 2) {
+ return answer + 3;
+ }
+ return answer - 1;
+ } else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
+ if (multi_byte_bytes_seen >= 3) {
+ return answer + 4;
+ }
+ return answer - 1;
+ } else {
+ return answer; // Malformed UTF-8.
+ }
+ }
+ }
+ return 0;
+}
+
+
+// Suspends the thread until there is data available from the child process.
+// Returns false on timeout, true on data ready.
+static bool WaitOnFD(int fd,
+ int read_timeout,
+ int total_timeout,
+ struct timeval& start_time) {
+ fd_set readfds, writefds, exceptfds;
+ struct timeval timeout;
+ int gone = 0;
+ if (total_timeout != -1) {
+ struct timeval time_now;
+ gettimeofday(&time_now, NULL);
+ int seconds = time_now.tv_sec - start_time.tv_sec;
+ gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000;
+ if (gone >= total_timeout) return false;
+ }
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(fd, &readfds);
+ FD_SET(fd, &exceptfds);
+ if (read_timeout == -1 ||
+ (total_timeout != -1 && total_timeout - gone < read_timeout)) {
+ read_timeout = total_timeout - gone;
+ }
+ timeout.tv_usec = (read_timeout % 1000) * 1000;
+ timeout.tv_sec = read_timeout / 1000;
+ int number_of_fds_ready = select(fd + 1,
+ &readfds,
+ &writefds,
+ &exceptfds,
+ read_timeout != -1 ? &timeout : NULL);
+ return number_of_fds_ready == 1;
+}
+
+
+// Checks whether we ran out of time on the timeout. Returns true if we ran out
+// of time, false if we still have time.
+static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
+ if (total_time == -1) return false;
+ struct timeval time_now;
+ gettimeofday(&time_now, NULL);
+ // Careful about overflow.
+ int seconds = time_now.tv_sec - start_time.tv_sec;
+ if (seconds > 100) {
+ if (seconds * 1000 > total_time) return true;
+ return false;
+ }
+ int useconds = time_now.tv_usec - start_time.tv_usec;
+ if (seconds * 1000000 + useconds > total_time * 1000) {
+ return true;
+ }
+ return false;
+}
+
+
+// A utility class that does a non-hanging waitpid on the child process if we
+// bail out of the System() function early. If you don't ever do a waitpid on
+// a subprocess then it turns into one of those annoying 'zombie processes'.
+class ZombieProtector {
+ public:
+ explicit ZombieProtector(int pid): pid_(pid) { }
+ ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
+ void ChildIsDeadNow() { pid_ = 0; }
+ private:
+ int pid_;
+};
+
+
+// A utility class that closes a file descriptor when it goes out of scope.
+class OpenFDCloser {
+ public:
+ explicit OpenFDCloser(int fd): fd_(fd) { }
+ ~OpenFDCloser() { close(fd_); }
+ private:
+ int fd_;
+};
+
+
+// A utility class that takes the array of command arguments and puts then in an
+// array of new[]ed UTF-8 C strings. Deallocates them again when it goes out of
+// scope.
+class ExecArgs {
+ public:
+ ExecArgs() {
+ exec_args_[0] = NULL;
+ }
+ bool Init(Handle<Value> arg0, Handle<Array> command_args) {
+ String::Utf8Value prog(arg0);
+ if (*prog == NULL) {
+ const char* message =
+ "os.system(): String conversion of program name failed";
+ ThrowException(String::New(message));
+ return false;
+ }
+ int len = prog.length() + 3;
+ char* c_arg = new char[len];
+ snprintf(c_arg, len, "%s", *prog);
+ exec_args_[0] = c_arg;
+ int i = 1;
+ for (unsigned j = 0; j < command_args->Length(); i++, j++) {
+ Handle<Value> arg(command_args->Get(Integer::New(j)));
+ String::Utf8Value utf8_arg(arg);
+ if (*utf8_arg == NULL) {
+ exec_args_[i] = NULL; // Consistent state for destructor.
+ const char* message =
+ "os.system(): String conversion of argument failed.";
+ ThrowException(String::New(message));
+ return false;
+ }
+ int len = utf8_arg.length() + 1;
+ char* c_arg = new char[len];
+ snprintf(c_arg, len, "%s", *utf8_arg);
+ exec_args_[i] = c_arg;
+ }
+ exec_args_[i] = NULL;
+ return true;
+ }
+ ~ExecArgs() {
+ for (unsigned i = 0; i < kMaxArgs; i++) {
+ if (exec_args_[i] == NULL) {
+ return;
+ }
+ delete [] exec_args_[i];
+ exec_args_[i] = 0;
+ }
+ }
+ static const unsigned kMaxArgs = 1000;
+ char** arg_array() { return exec_args_; }
+ char* arg0() { return exec_args_[0]; }
+
+ private:
+ char* exec_args_[kMaxArgs + 1];
+};
+
+
+// Gets the optional timeouts from the arguments to the system() call.
+static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
+ int* read_timeout,
+ int* total_timeout) {
+ if (args.Length() > 3) {
+ if (args[3]->IsNumber()) {
+ *total_timeout = args[3]->Int32Value();
+ } else {
+ ThrowException(String::New("system: Argument 4 must be a number"));
+ return false;
+ }
+ }
+ if (args.Length() > 2) {
+ if (args[2]->IsNumber()) {
+ *read_timeout = args[2]->Int32Value();
+ } else {
+ ThrowException(String::New("system: Argument 3 must be a number"));
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static const int kReadFD = 0;
+static const int kWriteFD = 1;
+
+
+// This is run in the child process after fork() but before exec(). It normally
+// ends with the child process being replaced with the desired child program.
+// It only returns if an error occurred.
+static void ExecSubprocess(int* exec_error_fds,
+ int* stdout_fds,
+ ExecArgs& exec_args) {
+ close(exec_error_fds[kReadFD]); // Don't need this in the child.
+ close(stdout_fds[kReadFD]); // Don't need this in the child.
+ close(1); // Close stdout.
+ dup2(stdout_fds[kWriteFD], 1); // Dup pipe fd to stdout.
+ close(stdout_fds[kWriteFD]); // Don't need the original fd now.
+ fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
+ execvp(exec_args.arg0(), exec_args.arg_array());
+ // Only get here if the exec failed. Write errno to the parent to tell
+ // them it went wrong. If it went well the pipe is closed.
+ int err = errno;
+ int bytes_written;
+ do {
+ bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
+ } while (bytes_written == -1 && errno == EINTR);
+ // Return (and exit child process).
+}
+
+
+// Runs in the parent process. Checks that the child was able to exec (closing
+// the file desriptor), or reports an error if it failed.
+static bool ChildLaunchedOK(int* exec_error_fds) {
+ int bytes_read;
+ int err;
+ do {
+ bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
+ } while (bytes_read == -1 && errno == EINTR);
+ if (bytes_read != 0) {
+ ThrowException(String::New(strerror(err)));
+ return false;
+ }
+ return true;
+}
+
+
+// Accumulates the output from the child in a string handle. Returns true if it
+// succeeded or false if an exception was thrown.
+static Handle<Value> GetStdout(int child_fd,
+ struct timeval& start_time,
+ int read_timeout,
+ int total_timeout) {
+ Handle<String> accumulator = String::Empty();
+
+ int fullness = 0;
+ static const int kStdoutReadBufferSize = 4096;
+ char buffer[kStdoutReadBufferSize];
+
+ if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
+ return ThrowException(String::New(strerror(errno)));
+ }
+
+ int bytes_read;
+ do {
+ bytes_read = read(child_fd,
+ buffer + fullness,
+ kStdoutReadBufferSize - fullness);
+ if (bytes_read == -1) {
+ if (errno == EAGAIN) {
+ if (!WaitOnFD(child_fd,
+ read_timeout,
+ total_timeout,
+ start_time) ||
+ (TimeIsOut(start_time, total_timeout))) {
+ return ThrowException(String::New("Timed out waiting for output"));
+ }
+ continue;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (bytes_read + fullness > 0) {
+ int length = bytes_read == 0 ?
+ bytes_read + fullness :
+ LengthWithoutIncompleteUtf8(buffer, bytes_read + fullness);
+ Handle<String> addition = String::New(buffer, length);
+ accumulator = String::Concat(accumulator, addition);
+ fullness = bytes_read + fullness - length;
+ memcpy(buffer, buffer + length, fullness);
+ }
+ } while (bytes_read != 0);
+ return accumulator;
+}
+
+
+// Modern Linux has the waitid call, which is like waitpid, but more useful
+// if you want a timeout. If we don't have waitid we can't limit the time
+// waiting for the process to exit without losing the information about
+// whether it exited normally. In the common case this doesn't matter because
+// we don't get here before the child has closed stdout and most programs don't
+// do that before they exit.
+//
+// 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__) \
+ && !defined(__NetBSD__)
+#if !defined(__FreeBSD__)
+#define HAS_WAITID 1
+#endif
+#endif
+
+
+// Get exit status of child.
+static bool WaitForChild(int pid,
+ ZombieProtector& child_waiter,
+ struct timeval& start_time,
+ int read_timeout,
+ int total_timeout) {
+#ifdef HAS_WAITID
+
+ siginfo_t child_info;
+ child_info.si_pid = 0;
+ int useconds = 1;
+ // Wait for child to exit.
+ while (child_info.si_pid == 0) {
+ waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
+ usleep(useconds);
+ if (useconds < 1000000) useconds <<= 1;
+ if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
+ (TimeIsOut(start_time, total_timeout))) {
+ ThrowException(String::New("Timed out waiting for process to terminate"));
+ kill(pid, SIGINT);
+ return false;
+ }
+ }
+ if (child_info.si_code == CLD_KILLED) {
+ char message[999];
+ snprintf(message,
+ sizeof(message),
+ "Child killed by signal %d",
+ child_info.si_status);
+ ThrowException(String::New(message));
+ return false;
+ }
+ if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
+ char message[999];
+ snprintf(message,
+ sizeof(message),
+ "Child exited with status %d",
+ child_info.si_status);
+ ThrowException(String::New(message));
+ return false;
+ }
+
+#else // No waitid call.
+
+ int child_status;
+ waitpid(pid, &child_status, 0); // We hang here if the child doesn't exit.
+ child_waiter.ChildIsDeadNow();
+ if (WIFSIGNALED(child_status)) {
+ char message[999];
+ snprintf(message,
+ sizeof(message),
+ "Child killed by signal %d",
+ WTERMSIG(child_status));
+ ThrowException(String::New(message));
+ return false;
+ }
+ if (WEXITSTATUS(child_status) != 0) {
+ char message[999];
+ int exit_status = WEXITSTATUS(child_status);
+ snprintf(message,
+ sizeof(message),
+ "Child exited with status %d",
+ exit_status);
+ ThrowException(String::New(message));
+ return false;
+ }
+
+#endif // No waitid call.
+
+ return true;
+}
+
+
+// Implementation of the system() function (see d8.h for details).
+void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ HandleScope scope(args.GetIsolate());
+ int read_timeout = -1;
+ int total_timeout = -1;
+ if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
+ Handle<Array> command_args;
+ if (args.Length() > 1) {
+ if (!args[1]->IsArray()) {
+ ThrowException(String::New("system: Argument 2 must be an array"));
+ return;
+ }
+ command_args = Handle<Array>::Cast(args[1]);
+ } else {
+ command_args = Array::New(0);
+ }
+ if (command_args->Length() > ExecArgs::kMaxArgs) {
+ ThrowException(String::New("Too many arguments to system()"));
+ return;
+ }
+ if (args.Length() < 1) {
+ ThrowException(String::New("Too few arguments to system()"));
+ return;
+ }
+
+ struct timeval start_time;
+ gettimeofday(&start_time, NULL);
+
+ ExecArgs exec_args;
+ if (!exec_args.Init(args[0], command_args)) {
+ return;
+ }
+ int exec_error_fds[2];
+ int stdout_fds[2];
+
+ if (pipe(exec_error_fds) != 0) {
+ ThrowException(String::New("pipe syscall failed."));
+ return;
+ }
+ if (pipe(stdout_fds) != 0) {
+ ThrowException(String::New("pipe syscall failed."));
+ return;
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) { // Child process.
+ ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
+ exit(1);
+ }
+
+ // Parent process. Ensure that we clean up if we exit this function early.
+ ZombieProtector child_waiter(pid);
+ close(exec_error_fds[kWriteFD]);
+ close(stdout_fds[kWriteFD]);
+ OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
+ OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
+
+ if (!ChildLaunchedOK(exec_error_fds)) return;
+
+ Handle<Value> accumulator = GetStdout(stdout_fds[kReadFD],
+ start_time,
+ read_timeout,
+ total_timeout);
+ if (accumulator->IsUndefined()) {
+ kill(pid, SIGINT); // On timeout, kill the subprocess.
+ args.GetReturnValue().Set(accumulator);
+ return;
+ }
+
+ if (!WaitForChild(pid,
+ child_waiter,
+ start_time,
+ read_timeout,
+ total_timeout)) {
+ return;
+ }
+
+ args.GetReturnValue().Set(accumulator);
+}
+
+
+void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 1) {
+ const char* message = "chdir() takes one argument";
+ ThrowException(String::New(message));
+ return;
+ }
+ String::Utf8Value directory(args[0]);
+ if (*directory == NULL) {
+ const char* message = "os.chdir(): String conversion of argument failed.";
+ ThrowException(String::New(message));
+ return;
+ }
+ if (chdir(*directory) != 0) {
+ ThrowException(String::New(strerror(errno)));
+ return;
+ }
+}
+
+
+void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 1) {
+ const char* message = "umask() takes one argument";
+ ThrowException(String::New(message));
+ return;
+ }
+ if (args[0]->IsNumber()) {
+ mode_t mask = args[0]->Int32Value();
+ int previous = umask(mask);
+ args.GetReturnValue().Set(previous);
+ return;
+ } else {
+ const char* message = "umask() argument must be numeric";
+ ThrowException(String::New(message));
+ return;
+ }
+}
+
+
+static bool CheckItsADirectory(char* directory) {
+ struct stat stat_buf;
+ int stat_result = stat(directory, &stat_buf);
+ if (stat_result != 0) {
+ ThrowException(String::New(strerror(errno)));
+ return false;
+ }
+ if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
+ ThrowException(String::New(strerror(EEXIST)));
+ return false;
+}
+
+
+// Returns true for success. Creates intermediate directories as needed. No
+// error if the directory exists already.
+static bool mkdirp(char* directory, mode_t mask) {
+ int result = mkdir(directory, mask);
+ if (result == 0) return true;
+ if (errno == EEXIST) {
+ return CheckItsADirectory(directory);
+ } else if (errno == ENOENT) { // Intermediate path element is missing.
+ char* last_slash = strrchr(directory, '/');
+ if (last_slash == NULL) {
+ ThrowException(String::New(strerror(errno)));
+ return false;
+ }
+ *last_slash = 0;
+ if (!mkdirp(directory, mask)) return false;
+ *last_slash = '/';
+ result = mkdir(directory, mask);
+ if (result == 0) return true;
+ if (errno == EEXIST) {
+ return CheckItsADirectory(directory);
+ }
+ ThrowException(String::New(strerror(errno)));
+ return false;
+ } else {
+ ThrowException(String::New(strerror(errno)));
+ return false;
+ }
+}
+
+
+void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ mode_t mask = 0777;
+ if (args.Length() == 2) {
+ if (args[1]->IsNumber()) {
+ mask = args[1]->Int32Value();
+ } else {
+ const char* message = "mkdirp() second argument must be numeric";
+ ThrowException(String::New(message));
+ return;
+ }
+ } else if (args.Length() != 1) {
+ const char* message = "mkdirp() takes one or two arguments";
+ ThrowException(String::New(message));
+ return;
+ }
+ String::Utf8Value directory(args[0]);
+ if (*directory == NULL) {
+ const char* message = "os.mkdirp(): String conversion of argument failed.";
+ ThrowException(String::New(message));
+ return;
+ }
+ mkdirp(*directory, mask);
+}
+
+
+void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 1) {
+ const char* message = "rmdir() takes one or two arguments";
+ ThrowException(String::New(message));
+ return;
+ }
+ String::Utf8Value directory(args[0]);
+ if (*directory == NULL) {
+ const char* message = "os.rmdir(): String conversion of argument failed.";
+ ThrowException(String::New(message));
+ return;
+ }
+ rmdir(*directory);
+}
+
+
+void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 2) {
+ const char* message = "setenv() takes two arguments";
+ ThrowException(String::New(message));
+ return;
+ }
+ String::Utf8Value var(args[0]);
+ String::Utf8Value value(args[1]);
+ if (*var == NULL) {
+ const char* message =
+ "os.setenv(): String conversion of variable name failed.";
+ ThrowException(String::New(message));
+ return;
+ }
+ if (*value == NULL) {
+ const char* message =
+ "os.setenv(): String conversion of variable contents failed.";
+ ThrowException(String::New(message));
+ return;
+ }
+ setenv(*var, *value, 1);
+}
+
+
+void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 1) {
+ const char* message = "unsetenv() takes one argument";
+ ThrowException(String::New(message));
+ return;
+ }
+ String::Utf8Value var(args[0]);
+ if (*var == NULL) {
+ const char* message =
+ "os.setenv(): String conversion of variable name failed.";
+ ThrowException(String::New(message));
+ return;
+ }
+ unsetenv(*var);
+}
+
+
+void Shell::AddOSMethods(Handle<ObjectTemplate> os_templ) {
+ os_templ->Set(String::New("system"), FunctionTemplate::New(System));
+ os_templ->Set(String::New("chdir"), FunctionTemplate::New(ChangeDirectory));
+ os_templ->Set(String::New("setenv"), FunctionTemplate::New(SetEnvironment));
+ os_templ->Set(String::New("unsetenv"),
+ FunctionTemplate::New(UnsetEnvironment));
+ os_templ->Set(String::New("umask"), FunctionTemplate::New(SetUMask));
+ os_templ->Set(String::New("mkdirp"), FunctionTemplate::New(MakeDirectory));
+ os_templ->Set(String::New("rmdir"), FunctionTemplate::New(RemoveDirectory));
+}
+
+} // namespace v8
diff --git a/chromium/v8/src/d8-readline.cc b/chromium/v8/src/d8-readline.cc
new file mode 100644
index 00000000000..298518d72ab
--- /dev/null
+++ b/chromium/v8/src/d8-readline.cc
@@ -0,0 +1,178 @@
+// Copyright 2012 the V8 project authors. 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 <cstdio> // NOLINT
+#include <string.h> // NOLINT
+#include <readline/readline.h> // NOLINT
+#include <readline/history.h> // NOLINT
+
+// The readline includes leaves RETURN defined which breaks V8 compilation.
+#undef RETURN
+
+#include "d8.h"
+
+// There are incompatibilities between different versions and different
+// implementations of readline. This smooths out one known incompatibility.
+#if RL_READLINE_VERSION >= 0x0500
+#define completion_matches rl_completion_matches
+#endif
+
+
+namespace v8 {
+
+
+class ReadLineEditor: public LineEditor {
+ public:
+ ReadLineEditor() : LineEditor(LineEditor::READLINE, "readline") { }
+ virtual Handle<String> Prompt(const char* prompt);
+ virtual bool Open(Isolate* isolate);
+ virtual bool Close();
+ virtual void AddHistory(const char* str);
+
+ static const char* kHistoryFileName;
+ static const int kMaxHistoryEntries;
+
+ private:
+#ifndef V8_SHARED
+ static char** AttemptedCompletion(const char* text, int start, int end);
+ static char* CompletionGenerator(const char* text, int state);
+#endif // V8_SHARED
+ static char kWordBreakCharacters[];
+
+ Isolate* isolate_;
+};
+
+
+static ReadLineEditor read_line_editor;
+char ReadLineEditor::kWordBreakCharacters[] = {' ', '\t', '\n', '"',
+ '\\', '\'', '`', '@', '.', '>', '<', '=', ';', '|', '&', '{', '(',
+ '\0'};
+
+
+const char* ReadLineEditor::kHistoryFileName = ".d8_history";
+const int ReadLineEditor::kMaxHistoryEntries = 1000;
+
+
+bool ReadLineEditor::Open(Isolate* isolate) {
+ isolate_ = isolate;
+
+ rl_initialize();
+
+#ifdef V8_SHARED
+ // Don't do completion on shared library mode
+ // http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC24
+ rl_bind_key('\t', rl_insert);
+#else
+ rl_attempted_completion_function = AttemptedCompletion;
+#endif // V8_SHARED
+
+ rl_completer_word_break_characters = kWordBreakCharacters;
+ rl_bind_key('\t', rl_complete);
+ using_history();
+ stifle_history(kMaxHistoryEntries);
+ return read_history(kHistoryFileName) == 0;
+}
+
+
+bool ReadLineEditor::Close() {
+ return write_history(kHistoryFileName) == 0;
+}
+
+
+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);
+}
+
+
+void ReadLineEditor::AddHistory(const char* str) {
+ // Do not record empty input.
+ if (strlen(str) == 0) return;
+ // Remove duplicate history entry.
+ history_set_pos(history_length-1);
+ if (current_history()) {
+ do {
+ if (strcmp(current_history()->line, str) == 0) {
+ remove_history(where_history());
+ break;
+ }
+ } while (previous_history());
+ }
+ add_history(str);
+}
+
+
+#ifndef V8_SHARED
+char** ReadLineEditor::AttemptedCompletion(const char* text,
+ int start,
+ int end) {
+ char** result = completion_matches(text, CompletionGenerator);
+ rl_attempted_completion_over = true;
+ return result;
+}
+
+
+char* ReadLineEditor::CompletionGenerator(const char* text, int state) {
+ static unsigned current_index;
+ static Persistent<Array> current_completions;
+ Isolate* isolate = read_line_editor.isolate_;
+ Locker lock(isolate);
+ HandleScope scope;
+ Handle<Array> completions;
+ if (state == 0) {
+ Local<String> full_text = String::New(rl_line_buffer, rl_point);
+ completions = Shell::GetCompletions(isolate, String::New(text), full_text);
+ current_completions.Reset(isolate, completions);
+ current_index = 0;
+ } else {
+ completions = Local<Array>::New(isolate, current_completions);
+ }
+ if (current_index < completions->Length()) {
+ Handle<Integer> index = Integer::New(current_index);
+ Handle<Value> str_obj = completions->Get(index);
+ current_index++;
+ String::Utf8Value str(str_obj);
+ return strdup(*str);
+ } else {
+ current_completions.Dispose(isolate);
+ current_completions.Clear();
+ return NULL;
+ }
+}
+#endif // V8_SHARED
+
+
+} // namespace v8
diff --git a/chromium/v8/src/d8-windows.cc b/chromium/v8/src/d8-windows.cc
new file mode 100644
index 00000000000..eeb4735bbc2
--- /dev/null
+++ b/chromium/v8/src/d8-windows.cc
@@ -0,0 +1,42 @@
+// 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.
+
+
+#include "d8.h"
+#include "d8-debug.h"
+#include "debug.h"
+#include "api.h"
+
+
+namespace v8 {
+
+
+void Shell::AddOSMethods(Handle<ObjectTemplate> os_templ) {
+}
+
+
+} // namespace v8
diff --git a/chromium/v8/src/d8.cc b/chromium/v8/src/d8.cc
new file mode 100644
index 00000000000..c7b66c2a150
--- /dev/null
+++ b/chromium/v8/src/d8.cc
@@ -0,0 +1,1741 @@
+// Copyright 2012 the V8 project authors. 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.
+
+
+// Defined when linking against shared lib on Windows.
+#if defined(USING_V8_SHARED) && !defined(V8_SHARED)
+#define V8_SHARED
+#endif
+
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+#include <bzlib.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef V8_SHARED
+#include <assert.h>
+#endif // V8_SHARED
+
+#ifndef V8_SHARED
+#include <algorithm>
+#endif // !V8_SHARED
+
+#ifdef V8_SHARED
+#include "../include/v8-testing.h"
+#endif // V8_SHARED
+
+#ifdef ENABLE_VTUNE_JIT_INTERFACE
+#include "third_party/vtune/v8-vtune.h"
+#endif
+
+#include "d8.h"
+
+#ifndef V8_SHARED
+#include "api.h"
+#include "checks.h"
+#include "d8-debug.h"
+#include "debug.h"
+#include "natives.h"
+#include "platform.h"
+#include "v8.h"
+#endif // V8_SHARED
+
+#if !defined(_WIN32) && !defined(_WIN64)
+#include <unistd.h> // NOLINT
+#endif
+
+#ifndef ASSERT
+#define ASSERT(condition) assert(condition)
+#endif
+
+namespace v8 {
+
+
+static Handle<Value> Throw(const char* message) {
+ return ThrowException(String::New(message));
+}
+
+
+
+class PerIsolateData {
+ public:
+ explicit PerIsolateData(Isolate* isolate) : isolate_(isolate), realms_(NULL) {
+ HandleScope scope(isolate);
+ isolate->SetData(this);
+ }
+
+ ~PerIsolateData() {
+ isolate_->SetData(NULL); // Not really needed, just to be sure...
+ }
+
+ inline static PerIsolateData* Get(Isolate* isolate) {
+ return reinterpret_cast<PerIsolateData*>(isolate->GetData());
+ }
+
+ class RealmScope {
+ public:
+ explicit RealmScope(PerIsolateData* data);
+ ~RealmScope();
+ private:
+ PerIsolateData* data_;
+ };
+
+ private:
+ friend class Shell;
+ friend class RealmScope;
+ Isolate* isolate_;
+ int realm_count_;
+ int realm_current_;
+ int realm_switch_;
+ Persistent<Context>* realms_;
+ Persistent<Value> realm_shared_;
+
+ int RealmFind(Handle<Context> context);
+};
+
+
+LineEditor *LineEditor::current_ = NULL;
+
+
+LineEditor::LineEditor(Type type, const char* name)
+ : type_(type), name_(name) {
+ if (current_ == NULL || current_->type_ < type) current_ = this;
+}
+
+
+class DumbLineEditor: public LineEditor {
+ public:
+ explicit DumbLineEditor(Isolate* isolate)
+ : LineEditor(LineEditor::DUMB, "dumb"), isolate_(isolate) { }
+ virtual Handle<String> Prompt(const char* prompt);
+ private:
+ Isolate* isolate_;
+};
+
+
+Handle<String> DumbLineEditor::Prompt(const char* prompt) {
+ printf("%s", prompt);
+#if defined(__native_client__)
+ // Native Client libc is used to being embedded in Chrome and
+ // has trouble recognizing when to flush.
+ fflush(stdout);
+#endif
+ return Shell::ReadFromStdin(isolate_);
+}
+
+
+#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_;
+#endif // V8_SHARED
+
+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);
+ const char* name2 = reinterpret_cast<const char*>(key2);
+ return strcmp(name1, name2) == 0;
+}
+#endif // V8_SHARED
+
+
+// Converts a V8 value to a C string.
+const char* Shell::ToCString(const v8::String::Utf8Value& value) {
+ return *value ? *value : "<string conversion failed>";
+}
+
+
+// Executes a string within the current v8 context.
+bool Shell::ExecuteString(Isolate* isolate,
+ Handle<String> source,
+ Handle<Value> name,
+ bool print_result,
+ bool report_exceptions) {
+#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
+ bool FLAG_debugger = i::FLAG_debugger;
+#else
+ bool FLAG_debugger = false;
+#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
+ HandleScope handle_scope(isolate);
+ TryCatch try_catch;
+ options.script_executed = true;
+ if (FLAG_debugger) {
+ // When debugging make exceptions appear to be uncaught.
+ try_catch.SetVerbose(true);
+ }
+ Handle<Script> script = Script::New(source, name);
+ if (script.IsEmpty()) {
+ // Print errors that happened during compilation.
+ if (report_exceptions && !FLAG_debugger)
+ ReportException(isolate, &try_catch);
+ return false;
+ } else {
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ Local<Context> realm =
+ Local<Context>::New(isolate, data->realms_[data->realm_current_]);
+ realm->Enter();
+ Handle<Value> result = script->Run();
+ realm->Exit();
+ data->realm_current_ = data->realm_switch_;
+ if (result.IsEmpty()) {
+ ASSERT(try_catch.HasCaught());
+ // Print errors that happened during execution.
+ if (report_exceptions && !FLAG_debugger)
+ ReportException(isolate, &try_catch);
+ return false;
+ } else {
+ ASSERT(!try_catch.HasCaught());
+ if (print_result) {
+#if !defined(V8_SHARED)
+ if (options.test_shell) {
+#endif
+ if (!result->IsUndefined()) {
+ // 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);
+ printf("\n");
+ }
+#if !defined(V8_SHARED)
+ } else {
+ v8::TryCatch try_catch;
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate, utility_context_);
+ v8::Context::Scope context_scope(context);
+ Handle<Object> global = context->Global();
+ Handle<Value> fun = global->Get(String::New("Stringify"));
+ Handle<Value> argv[1] = { result };
+ Handle<Value> s = Handle<Function>::Cast(fun)->Call(global, 1, argv);
+ if (try_catch.HasCaught()) return true;
+ v8::String::Utf8Value str(s);
+ fwrite(*str, sizeof(**str), str.length(), stdout);
+ printf("\n");
+ }
+#endif
+ }
+ return true;
+ }
+ }
+}
+
+
+PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
+ data_->realm_count_ = 1;
+ data_->realm_current_ = 0;
+ data_->realm_switch_ = 0;
+ data_->realms_ = new Persistent<Context>[1];
+ data_->realms_[0].Reset(data_->isolate_, Context::GetEntered());
+ data_->realm_shared_.Clear();
+}
+
+
+PerIsolateData::RealmScope::~RealmScope() {
+ // Drop realms to avoid keeping them alive.
+ for (int i = 0; i < data_->realm_count_; ++i)
+ data_->realms_[i].Dispose(data_->isolate_);
+ delete[] data_->realms_;
+ if (!data_->realm_shared_.IsEmpty())
+ data_->realm_shared_.Dispose(data_->isolate_);
+}
+
+
+int PerIsolateData::RealmFind(Handle<Context> context) {
+ for (int i = 0; i < realm_count_; ++i) {
+ if (realms_[i] == context) return i;
+ }
+ return -1;
+}
+
+
+// Realm.current() returns the index of the currently active realm.
+void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ int index = data->RealmFind(Context::GetEntered());
+ if (index == -1) return;
+ args.GetReturnValue().Set(index);
+}
+
+
+// Realm.owner(o) returns the index of the realm that created o.
+void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ if (args.Length() < 1 || !args[0]->IsObject()) {
+ Throw("Invalid argument");
+ return;
+ }
+ int index = data->RealmFind(args[0]->ToObject()->CreationContext());
+ if (index == -1) return;
+ args.GetReturnValue().Set(index);
+}
+
+
+// Realm.global(i) returns the global object of realm i.
+// (Note that properties of global objects cannot be read/written cross-realm.)
+void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
+ if (args.Length() < 1 || !args[0]->IsNumber()) {
+ Throw("Invalid argument");
+ return;
+ }
+ int index = args[0]->Uint32Value();
+ if (index >= data->realm_count_ || data->realms_[index].IsEmpty()) {
+ Throw("Invalid realm index");
+ return;
+ }
+ args.GetReturnValue().Set(
+ Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
+}
+
+
+// Realm.create() creates a new realm and returns its index.
+void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ Persistent<Context>* old_realms = data->realms_;
+ int index = data->realm_count_;
+ data->realms_ = new Persistent<Context>[++data->realm_count_];
+ for (int i = 0; i < index; ++i) {
+ data->realms_[i].Reset(isolate, old_realms[i]);
+ }
+ delete[] old_realms;
+ Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
+ data->realms_[index].Reset(
+ isolate, Context::New(isolate, NULL, global_template));
+ args.GetReturnValue().Set(index);
+}
+
+
+// Realm.dispose(i) disposes the reference to the realm i.
+void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ if (args.Length() < 1 || !args[0]->IsNumber()) {
+ Throw("Invalid argument");
+ return;
+ }
+ int index = args[0]->Uint32Value();
+ if (index >= data->realm_count_ || data->realms_[index].IsEmpty() ||
+ index == 0 ||
+ index == data->realm_current_ || index == data->realm_switch_) {
+ Throw("Invalid realm index");
+ return;
+ }
+ data->realms_[index].Dispose(isolate);
+ data->realms_[index].Clear();
+}
+
+
+// Realm.switch(i) switches to the realm i for consecutive interactive inputs.
+void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ if (args.Length() < 1 || !args[0]->IsNumber()) {
+ Throw("Invalid argument");
+ return;
+ }
+ int index = args[0]->Uint32Value();
+ if (index >= data->realm_count_ || data->realms_[index].IsEmpty()) {
+ Throw("Invalid realm index");
+ return;
+ }
+ data->realm_switch_ = index;
+}
+
+
+// Realm.eval(i, s) evaluates s in realm i and returns the result.
+void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsString()) {
+ Throw("Invalid argument");
+ return;
+ }
+ int index = args[0]->Uint32Value();
+ if (index >= data->realm_count_ || data->realms_[index].IsEmpty()) {
+ Throw("Invalid realm index");
+ return;
+ }
+ Handle<Script> script = Script::New(args[1]->ToString());
+ if (script.IsEmpty()) return;
+ Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
+ realm->Enter();
+ Handle<Value> result = script->Run();
+ realm->Exit();
+ args.GetReturnValue().Set(result);
+}
+
+
+// Realm.shared is an accessor for a single shared value across realms.
+void Shell::RealmSharedGet(Local<String> property,
+ const PropertyCallbackInfo<Value>& info) {
+ Isolate* isolate = info.GetIsolate();
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ if (data->realm_shared_.IsEmpty()) return;
+ info.GetReturnValue().Set(data->realm_shared_);
+}
+
+void Shell::RealmSharedSet(Local<String> property,
+ Local<Value> value,
+ const PropertyCallbackInfo<void>& info) {
+ Isolate* isolate = info.GetIsolate();
+ PerIsolateData* data = PerIsolateData::Get(isolate);
+ if (!data->realm_shared_.IsEmpty()) data->realm_shared_.Dispose(isolate);
+ data->realm_shared_.Reset(isolate, value);
+}
+
+
+void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Write(args);
+ printf("\n");
+ fflush(stdout);
+}
+
+
+void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ for (int i = 0; i < args.Length(); i++) {
+ HandleScope handle_scope(args.GetIsolate());
+ if (i != 0) {
+ printf(" ");
+ }
+
+ // Explicitly catch potential exceptions in toString().
+ v8::TryCatch try_catch;
+ Handle<String> str_obj = args[i]->ToString();
+ if (try_catch.HasCaught()) {
+ try_catch.ReThrow();
+ return;
+ }
+
+ v8::String::Utf8Value str(str_obj);
+ int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), stdout));
+ if (n != str.length()) {
+ printf("Error in fwrite\n");
+ Exit(1);
+ }
+ }
+}
+
+
+void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ String::Utf8Value file(args[0]);
+ if (*file == NULL) {
+ Throw("Error loading file");
+ return;
+ }
+ Handle<String> source = ReadFile(args.GetIsolate(), *file);
+ if (source.IsEmpty()) {
+ Throw("Error loading file");
+ return;
+ }
+ args.GetReturnValue().Set(source);
+}
+
+
+Handle<String> Shell::ReadFromStdin(Isolate* isolate) {
+ static const int kBufferSize = 256;
+ char buffer[kBufferSize];
+ Handle<String> accumulator = String::New("");
+ int length;
+ while (true) {
+ // 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.
+ char* input = NULL;
+ { // Release lock for blocking input.
+ Unlocker unlock(isolate);
+ input = fgets(buffer, kBufferSize, stdin);
+ }
+ if (input == NULL) return Handle<String>();
+ length = static_cast<int>(strlen(buffer));
+ if (length == 0) {
+ return accumulator;
+ } else if (buffer[length-1] != '\n') {
+ accumulator = String::Concat(accumulator, String::New(buffer, length));
+ } else if (length > 1 && buffer[length-2] == '\\') {
+ buffer[length-2] = '\n';
+ accumulator = String::Concat(accumulator, String::New(buffer, length-1));
+ } else {
+ return String::Concat(accumulator, String::New(buffer, length-1));
+ }
+ }
+}
+
+
+void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ for (int i = 0; i < args.Length(); i++) {
+ HandleScope handle_scope(args.GetIsolate());
+ String::Utf8Value file(args[i]);
+ if (*file == NULL) {
+ Throw("Error loading file");
+ return;
+ }
+ Handle<String> source = ReadFile(args.GetIsolate(), *file);
+ if (source.IsEmpty()) {
+ Throw("Error loading file");
+ return;
+ }
+ if (!ExecuteString(args.GetIsolate(),
+ source,
+ String::New(*file),
+ false,
+ true)) {
+ Throw("Error executing file");
+ return;
+ }
+ }
+}
+
+
+void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ int exit_code = args[0]->Int32Value();
+ OnExit();
+ exit(exit_code);
+}
+
+
+void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(String::New(V8::GetVersion()));
+}
+
+
+void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
+ HandleScope handle_scope(isolate);
+#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
+ Handle<Context> utility_context;
+ bool enter_context = !Context::InContext();
+ if (enter_context) {
+ utility_context = Local<Context>::New(isolate, utility_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();
+ if (message.IsEmpty()) {
+ // V8 didn't provide any extra information about this error; just
+ // print the exception.
+ printf("%s\n", exception_string);
+ } else {
+ // Print (filename):(line number): (message).
+ v8::String::Utf8Value filename(message->GetScriptResourceName());
+ const char* filename_string = ToCString(filename);
+ int linenum = message->GetLineNumber();
+ printf("%s:%i: %s\n", filename_string, linenum, exception_string);
+ // Print line of source code.
+ v8::String::Utf8Value sourceline(message->GetSourceLine());
+ const char* sourceline_string = ToCString(sourceline);
+ printf("%s\n", sourceline_string);
+ // Print wavy underline (GetUnderline is deprecated).
+ int start = message->GetStartColumn();
+ for (int i = 0; i < start; i++) {
+ printf(" ");
+ }
+ int end = message->GetEndColumn();
+ for (int i = start; i < end; i++) {
+ printf("^");
+ }
+ printf("\n");
+ v8::String::Utf8Value stack_trace(try_catch->StackTrace());
+ if (stack_trace.length() > 0) {
+ const char* stack_trace_string = ToCString(stack_trace);
+ printf("%s\n", stack_trace_string);
+ }
+ }
+ printf("\n");
+#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
+ if (enter_context) utility_context->Exit();
+#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
+}
+
+
+#ifndef V8_SHARED
+Handle<Array> Shell::GetCompletions(Isolate* isolate,
+ Handle<String> text,
+ Handle<String> full) {
+ HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> utility_context =
+ v8::Local<v8::Context>::New(isolate, utility_context_);
+ v8::Context::Scope context_scope(utility_context);
+ Handle<Object> global = utility_context->Global();
+ Handle<Value> fun = global->Get(String::New("GetCompletions"));
+ static const int kArgc = 3;
+ v8::Local<v8::Context> evaluation_context =
+ v8::Local<v8::Context>::New(isolate, evaluation_context_);
+ Handle<Value> argv[kArgc] = { evaluation_context->Global(), text, full };
+ Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
+ return handle_scope.Close(Handle<Array>::Cast(val));
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+Handle<Object> Shell::DebugMessageDetails(Isolate* isolate,
+ Handle<String> message) {
+ HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate, utility_context_);
+ v8::Context::Scope context_scope(context);
+ Handle<Object> global = context->Global();
+ Handle<Value> fun = global->Get(String::New("DebugMessageDetails"));
+ static const int kArgc = 1;
+ Handle<Value> argv[kArgc] = { message };
+ Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
+ return Handle<Object>::Cast(val);
+}
+
+
+Handle<Value> Shell::DebugCommandToJSONRequest(Isolate* isolate,
+ Handle<String> command) {
+ HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate, utility_context_);
+ v8::Context::Scope context_scope(context);
+ Handle<Object> global = context->Global();
+ Handle<Value> fun = global->Get(String::New("DebugCommandToJSONRequest"));
+ static const int kArgc = 1;
+ Handle<Value> argv[kArgc] = { command };
+ Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
+ return val;
+}
+
+
+void Shell::DispatchDebugMessages() {
+ Isolate* isolate = v8::Isolate::GetCurrent();
+ HandleScope handle_scope(isolate);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate, Shell::evaluation_context_);
+ v8::Context::Scope context_scope(context);
+ v8::Debug::ProcessDebugMessages();
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+#endif // V8_SHARED
+
+
+#ifndef V8_SHARED
+int32_t* Counter::Bind(const char* name, bool is_histogram) {
+ int i;
+ for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
+ name_[i] = static_cast<char>(name[i]);
+ name_[i] = '\0';
+ is_histogram_ = is_histogram;
+ return ptr();
+}
+
+
+void Counter::AddSample(int32_t sample) {
+ count_++;
+ sample_total_ += sample;
+}
+
+
+CounterCollection::CounterCollection() {
+ magic_number_ = 0xDEADFACE;
+ max_counters_ = kMaxCounters;
+ max_name_size_ = Counter::kMaxNameSize;
+ counters_in_use_ = 0;
+}
+
+
+Counter* CounterCollection::GetNextCounter() {
+ if (counters_in_use_ == kMaxCounters) return NULL;
+ return &counters_[counters_in_use_++];
+}
+
+
+void Shell::MapCounters(const char* name) {
+ counters_file_ = i::OS::MemoryMappedFile::create(
+ name, sizeof(CounterCollection), &local_counters_);
+ void* memory = (counters_file_ == NULL) ?
+ NULL : counters_file_->memory();
+ if (memory == NULL) {
+ printf("Could not map counters file %s\n", name);
+ Exit(1);
+ }
+ counters_ = static_cast<CounterCollection*>(memory);
+ V8::SetCounterFunction(LookupCounter);
+ V8::SetCreateHistogramFunction(CreateHistogram);
+ V8::SetAddHistogramSampleFunction(AddHistogramSample);
+}
+
+
+int CounterMap::Hash(const char* name) {
+ int h = 0;
+ int c;
+ while ((c = *name++) != 0) {
+ h += h << 5;
+ h += c;
+ }
+ return h;
+}
+
+
+Counter* Shell::GetCounter(const char* name, bool is_histogram) {
+ Counter* counter = counter_map_->Lookup(name);
+
+ if (counter == NULL) {
+ counter = counters_->GetNextCounter();
+ if (counter != NULL) {
+ counter_map_->Set(name, counter);
+ counter->Bind(name, is_histogram);
+ }
+ } else {
+ ASSERT(counter->is_histogram() == is_histogram);
+ }
+ return counter;
+}
+
+
+int* Shell::LookupCounter(const char* name) {
+ Counter* counter = GetCounter(name, false);
+
+ if (counter != NULL) {
+ return counter->ptr();
+ } else {
+ return NULL;
+ }
+}
+
+
+void* Shell::CreateHistogram(const char* name,
+ int min,
+ int max,
+ size_t buckets) {
+ return GetCounter(name, true);
+}
+
+
+void Shell::AddHistogramSample(void* histogram, int sample) {
+ Counter* counter = reinterpret_cast<Counter*>(histogram);
+ counter->AddSample(sample);
+}
+
+
+void Shell::InstallUtilityScript(Isolate* isolate) {
+ Locker lock(isolate);
+ HandleScope scope(isolate);
+ // If we use the utility context, we have to set the security tokens so that
+ // utility, evaluation and debug context can all access each other.
+ v8::Local<v8::Context> utility_context =
+ v8::Local<v8::Context>::New(isolate, utility_context_);
+ v8::Local<v8::Context> evaluation_context =
+ v8::Local<v8::Context>::New(isolate, evaluation_context_);
+ utility_context->SetSecurityToken(Undefined(isolate));
+ evaluation_context->SetSecurityToken(Undefined(isolate));
+ v8::Context::Scope context_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();
+ i::Handle<i::JSObject> js_debug
+ = i::Handle<i::JSObject>(debug->debug_context()->global_object());
+ utility_context->Global()->Set(String::New("$debug"),
+ Utils::ToLocal(js_debug));
+ debug->debug_context()->set_security_token(HEAP->undefined_value());
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+ // Run the d8 shell utility script in the utility context
+ int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
+ i::Vector<const char> shell_source =
+ i::NativesCollection<i::D8>::GetRawScriptSource(source_index);
+ i::Vector<const char> shell_source_name =
+ i::NativesCollection<i::D8>::GetScriptName(source_index);
+ Handle<String> source = String::New(shell_source.start(),
+ shell_source.length());
+ Handle<String> name = String::New(shell_source_name.start(),
+ shell_source_name.length());
+ Handle<Script> script = Script::Compile(source, name);
+ script->Run();
+ // Mark the d8 shell script as native to avoid it showing up as normal source
+ // in the debugger.
+ i::Handle<i::Object> compiled_script = Utils::OpenHandle(*script);
+ i::Handle<i::Script> script_object = compiled_script->IsJSFunction()
+ ? i::Handle<i::Script>(i::Script::cast(
+ i::JSFunction::cast(*compiled_script)->shared()->script()))
+ : i::Handle<i::Script>(i::Script::cast(
+ i::SharedFunctionInfo::cast(*compiled_script)->script()));
+ script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE));
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Start the in-process debugger if requested.
+ if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
+ v8::Debug::SetDebugEventListener2(HandleDebugEvent);
+ }
+#endif // ENABLE_DEBUGGER_SUPPORT
+}
+#endif // V8_SHARED
+
+
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+class BZip2Decompressor : public v8::StartupDataDecompressor {
+ public:
+ virtual ~BZip2Decompressor() { }
+
+ protected:
+ virtual int DecompressData(char* raw_data,
+ int* raw_data_size,
+ const char* compressed_data,
+ int compressed_data_size) {
+ ASSERT_EQ(v8::StartupData::kBZip2,
+ v8::V8::GetCompressedStartupDataAlgorithm());
+ unsigned int decompressed_size = *raw_data_size;
+ int result =
+ BZ2_bzBuffToBuffDecompress(raw_data,
+ &decompressed_size,
+ const_cast<char*>(compressed_data),
+ compressed_data_size,
+ 0, 1);
+ if (result == BZ_OK) {
+ *raw_data_size = decompressed_size;
+ }
+ return result;
+ }
+};
+#endif
+
+
+Handle<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
+ Handle<ObjectTemplate> global_template = ObjectTemplate::New();
+ 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("readbuffer"),
+ FunctionTemplate::New(ReadBuffer));
+ global_template->Set(String::New("readline"),
+ FunctionTemplate::New(ReadLine));
+ global_template->Set(String::New("load"), FunctionTemplate::New(Load));
+ global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
+ global_template->Set(String::New("version"), FunctionTemplate::New(Version));
+
+ // Bind the Realm object.
+ Handle<ObjectTemplate> realm_template = ObjectTemplate::New();
+ realm_template->Set(String::New("current"),
+ FunctionTemplate::New(RealmCurrent));
+ realm_template->Set(String::New("owner"),
+ FunctionTemplate::New(RealmOwner));
+ realm_template->Set(String::New("global"),
+ FunctionTemplate::New(RealmGlobal));
+ realm_template->Set(String::New("create"),
+ FunctionTemplate::New(RealmCreate));
+ realm_template->Set(String::New("dispose"),
+ FunctionTemplate::New(RealmDispose));
+ realm_template->Set(String::New("switch"),
+ FunctionTemplate::New(RealmSwitch));
+ realm_template->Set(String::New("eval"),
+ FunctionTemplate::New(RealmEval));
+ realm_template->SetAccessor(String::New("shared"),
+ RealmSharedGet, RealmSharedSet);
+ global_template->Set(String::New("Realm"), realm_template);
+
+#if !defined(V8_SHARED) && !defined(_WIN32) && !defined(_WIN64)
+ Handle<ObjectTemplate> os_templ = ObjectTemplate::New();
+ AddOSMethods(os_templ);
+ global_template->Set(String::New("os"), os_templ);
+#endif // V8_SHARED
+
+ return global_template;
+}
+
+
+void Shell::Initialize(Isolate* isolate) {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ BZip2Decompressor startup_data_decompressor;
+ int bz2_result = startup_data_decompressor.Decompress();
+ if (bz2_result != BZ_OK) {
+ fprintf(stderr, "bzip error code: %d\n", bz2_result);
+ Exit(1);
+ }
+#endif
+
+#ifndef V8_SHARED
+ Shell::counter_map_ = new CounterMap();
+ // Set up counters
+ if (i::StrLength(i::FLAG_map_counters) != 0)
+ MapCounters(i::FLAG_map_counters);
+ if (i::FLAG_dump_counters || i::FLAG_track_gc_object_stats) {
+ V8::SetCounterFunction(LookupCounter);
+ V8::SetCreateHistogramFunction(CreateHistogram);
+ V8::SetAddHistogramSampleFunction(AddHistogramSample);
+ }
+#endif // V8_SHARED
+}
+
+
+void Shell::InitializeDebugger(Isolate* isolate) {
+ if (options.test_shell) return;
+#ifndef V8_SHARED
+ Locker lock(isolate);
+ HandleScope scope(isolate);
+ Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
+ utility_context_.Reset(isolate,
+ Context::New(isolate, NULL, global_template));
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // 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
+}
+
+
+Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
+#ifndef V8_SHARED
+ // This needs to be a critical section since this is not thread-safe
+ i::ScopedLock lock(context_mutex_);
+#endif // V8_SHARED
+ // Initialize the global objects
+ Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
+ HandleScope handle_scope(isolate);
+ Local<Context> context = Context::New(isolate, NULL, global_template);
+ ASSERT(!context.IsEmpty());
+ Context::Scope scope(context);
+
+#ifndef V8_SHARED
+ i::Factory* factory = i::Isolate::Current()->factory();
+ i::JSArguments js_args = i::FLAG_js_arguments;
+ i::Handle<i::FixedArray> arguments_array =
+ factory->NewFixedArray(js_args.argc());
+ for (int j = 0; j < js_args.argc(); j++) {
+ i::Handle<i::String> arg =
+ factory->NewStringFromUtf8(i::CStrVector(js_args[j]));
+ arguments_array->set(j, *arg);
+ }
+ i::Handle<i::JSArray> arguments_jsarray =
+ factory->NewJSArrayWithElements(arguments_array);
+ context->Global()->Set(String::New("arguments"),
+ Utils::ToLocal(arguments_jsarray));
+#endif // V8_SHARED
+ return handle_scope.Close(context);
+}
+
+
+void Shell::Exit(int exit_code) {
+ // Use _exit instead of exit to avoid races between isolate
+ // threads and static destructors.
+ fflush(stdout);
+ fflush(stderr);
+ _exit(exit_code);
+}
+
+
+#ifndef V8_SHARED
+struct CounterAndKey {
+ Counter* counter;
+ const char* key;
+};
+
+
+inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) {
+ return strcmp(lhs.key, rhs.key) < 0;
+}
+#endif // V8_SHARED
+
+
+void Shell::OnExit() {
+ LineEditor* line_editor = LineEditor::Get();
+ if (line_editor) line_editor->Close();
+#ifndef V8_SHARED
+ if (i::FLAG_dump_counters) {
+ int number_of_counters = 0;
+ for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) {
+ 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();
+ }
+ std::sort(counters, counters + number_of_counters);
+ 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:%-60s | %11i |\n", key, counter->count());
+ printf("| t:%-60s | %11i |\n", key, counter->sample_total());
+ } else {
+ printf("| %-62s | %11i |\n", key, counter->count());
+ }
+ }
+ printf("+----------------------------------------------------------------+"
+ "-------------+\n");
+ delete [] counters;
+ }
+ delete context_mutex_;
+ delete counters_file_;
+ delete counter_map_;
+#endif // V8_SHARED
+}
+
+
+
+static FILE* FOpen(const char* path, const char* mode) {
+#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
+ FILE* result;
+ if (fopen_s(&result, path, mode) == 0) {
+ return result;
+ } else {
+ return NULL;
+ }
+#else
+ FILE* file = fopen(path, mode);
+ if (file == NULL) return NULL;
+ struct stat file_stat;
+ if (fstat(fileno(file), &file_stat) != 0) return NULL;
+ bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
+ if (is_regular_file) return file;
+ fclose(file);
+ return NULL;
+#endif
+}
+
+
+static char* ReadChars(Isolate* isolate, const char* name, int* size_out) {
+ // Release the V8 lock while reading files.
+ v8::Unlocker unlocker(isolate);
+ FILE* file = FOpen(name, "rb");
+ if (file == NULL) return NULL;
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+ rewind(file);
+
+ char* chars = new char[size + 1];
+ chars[size] = '\0';
+ for (int i = 0; i < size;) {
+ int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
+ i += read;
+ }
+ fclose(file);
+ *size_out = size;
+ return chars;
+}
+
+static void ReadBufferWeakCallback(v8::Isolate* isolate,
+ Persistent<ArrayBuffer>* array_buffer,
+ uint8_t* data) {
+ size_t byte_length =
+ Local<ArrayBuffer>::New(isolate, *array_buffer)->ByteLength();
+ isolate->AdjustAmountOfExternalAllocatedMemory(
+ -static_cast<intptr_t>(byte_length));
+
+ delete[] data;
+ array_buffer->Dispose();
+}
+
+
+void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ ASSERT(sizeof(char) == sizeof(uint8_t)); // NOLINT
+ String::Utf8Value filename(args[0]);
+ int length;
+ if (*filename == NULL) {
+ Throw("Error loading file");
+ return;
+ }
+
+ Isolate* isolate = args.GetIsolate();
+ uint8_t* data = reinterpret_cast<uint8_t*>(
+ ReadChars(args.GetIsolate(), *filename, &length));
+ if (data == NULL) {
+ Throw("Error reading file");
+ return;
+ }
+ Handle<v8::ArrayBuffer> buffer = ArrayBuffer::New(data, length);
+ v8::Persistent<v8::ArrayBuffer> weak_handle(isolate, buffer);
+ weak_handle.MakeWeak(data, ReadBufferWeakCallback);
+ weak_handle.MarkIndependent();
+ isolate->AdjustAmountOfExternalAllocatedMemory(length);
+
+ args.GetReturnValue().Set(buffer);
+}
+
+
+#ifndef V8_SHARED
+static char* ReadToken(char* data, char token) {
+ char* next = i::OS::StrChr(data, token);
+ if (next != NULL) {
+ *next = '\0';
+ return (next + 1);
+ }
+
+ return NULL;
+}
+
+
+static char* ReadLine(char* data) {
+ return ReadToken(data, '\n');
+}
+
+
+static char* ReadWord(char* data) {
+ return ReadToken(data, ' ');
+}
+#endif // V8_SHARED
+
+
+// Reads a file into a v8 string.
+Handle<String> Shell::ReadFile(Isolate* isolate, const char* name) {
+ int size = 0;
+ char* chars = ReadChars(isolate, name, &size);
+ if (chars == NULL) return Handle<String>();
+ Handle<String> result = String::New(chars, size);
+ delete[] chars;
+ return result;
+}
+
+
+void Shell::RunShell(Isolate* isolate) {
+ Locker locker(isolate);
+ HandleScope outer_scope(isolate);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate, evaluation_context_);
+ v8::Context::Scope context_scope(context);
+ PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
+ Handle<String> name = String::New("(d8)");
+ LineEditor* console = LineEditor::Get();
+ printf("V8 version %s [console: %s]\n", V8::GetVersion(), console->name());
+ console->Open(isolate);
+ while (true) {
+ HandleScope inner_scope(isolate);
+ Handle<String> input = console->Prompt(Shell::kPrompt);
+ if (input.IsEmpty()) break;
+ ExecuteString(isolate, input, name, true, true);
+ }
+ printf("\n");
+}
+
+
+#ifndef V8_SHARED
+class ShellThread : public i::Thread {
+ public:
+ // Takes ownership of the underlying char array of |files|.
+ ShellThread(Isolate* isolate, char* files)
+ : Thread("d8:ShellThread"),
+ isolate_(isolate), files_(files) { }
+
+ ~ShellThread() {
+ delete[] files_;
+ }
+
+ virtual void Run();
+ private:
+ Isolate* isolate_;
+ char* files_;
+};
+
+
+void ShellThread::Run() {
+ char* ptr = files_;
+ while ((ptr != NULL) && (*ptr != '\0')) {
+ // For each newline-separated line.
+ char* next_line = ReadLine(ptr);
+
+ if (*ptr == '#') {
+ // Skip comment lines.
+ ptr = next_line;
+ continue;
+ }
+
+ // Prepare the context for this thread.
+ Locker locker(isolate_);
+ HandleScope outer_scope(isolate_);
+ Local<Context> thread_context =
+ Shell::CreateEvaluationContext(isolate_);
+ Context::Scope context_scope(thread_context);
+ PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate_));
+
+ while ((ptr != NULL) && (*ptr != '\0')) {
+ HandleScope inner_scope(isolate_);
+ char* filename = ptr;
+ ptr = ReadWord(ptr);
+
+ // Skip empty strings.
+ if (strlen(filename) == 0) {
+ continue;
+ }
+
+ Handle<String> str = Shell::ReadFile(isolate_, filename);
+ if (str.IsEmpty()) {
+ printf("File '%s' not found\n", filename);
+ Shell::Exit(1);
+ }
+
+ Shell::ExecuteString(isolate_, str, String::New(filename), false, false);
+ }
+
+ ptr = next_line;
+ }
+}
+#endif // V8_SHARED
+
+
+SourceGroup::~SourceGroup() {
+#ifndef V8_SHARED
+ delete next_semaphore_;
+ next_semaphore_ = NULL;
+ delete done_semaphore_;
+ done_semaphore_ = NULL;
+ delete thread_;
+ thread_ = NULL;
+#endif // V8_SHARED
+}
+
+
+void SourceGroup::Execute(Isolate* isolate) {
+ for (int i = begin_offset_; i < end_offset_; ++i) {
+ const char* arg = argv_[i];
+ if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
+ // Execute argument given to -e option directly.
+ HandleScope handle_scope(isolate);
+ Handle<String> file_name = String::New("unnamed");
+ Handle<String> source = String::New(argv_[i + 1]);
+ if (!Shell::ExecuteString(isolate, source, file_name, false, true)) {
+ Shell::Exit(1);
+ }
+ ++i;
+ } else if (arg[0] == '-') {
+ // Ignore other options. They have been parsed already.
+ } else {
+ // Use all other arguments as names of files to load and run.
+ HandleScope handle_scope(isolate);
+ Handle<String> file_name = String::New(arg);
+ Handle<String> source = ReadFile(isolate, arg);
+ if (source.IsEmpty()) {
+ printf("Error reading '%s'\n", arg);
+ Shell::Exit(1);
+ }
+ if (!Shell::ExecuteString(isolate, source, file_name, false, true)) {
+ Shell::Exit(1);
+ }
+ }
+ }
+}
+
+
+Handle<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
+ int size;
+ char* chars = ReadChars(isolate, name, &size);
+ if (chars == NULL) return Handle<String>();
+ Handle<String> result = String::New(chars, size);
+ delete[] chars;
+ return result;
+}
+
+
+#ifndef V8_SHARED
+i::Thread::Options SourceGroup::GetThreadOptions() {
+ // 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. 2Mbytes seems to be enough.
+ return i::Thread::Options("IsolateThread", 2 * MB);
+}
+
+
+void SourceGroup::ExecuteInThread() {
+ Isolate* isolate = Isolate::New();
+ do {
+ if (next_semaphore_ != NULL) next_semaphore_->Wait();
+ {
+ Isolate::Scope iscope(isolate);
+ Locker lock(isolate);
+ {
+ HandleScope scope(isolate);
+ PerIsolateData data(isolate);
+ Local<Context> context = Shell::CreateEvaluationContext(isolate);
+ {
+ Context::Scope cscope(context);
+ PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
+ Execute(isolate);
+ }
+ }
+ if (Shell::options.send_idle_notification) {
+ const int kLongIdlePauseInMs = 1000;
+ V8::ContextDisposedNotification();
+ V8::IdleNotification(kLongIdlePauseInMs);
+ }
+ }
+ if (done_semaphore_ != NULL) done_semaphore_->Signal();
+ } while (!Shell::options.last_run);
+ isolate->Dispose();
+}
+
+
+void SourceGroup::StartExecuteInThread() {
+ if (thread_ == NULL) {
+ thread_ = new IsolateThread(this);
+ thread_->Start();
+ }
+ next_semaphore_->Signal();
+}
+
+
+void SourceGroup::WaitForThread() {
+ if (thread_ == NULL) return;
+ if (Shell::options.last_run) {
+ thread_->Join();
+ } else {
+ done_semaphore_->Wait();
+ }
+}
+#endif // V8_SHARED
+
+
+bool Shell::SetOptions(int argc, char* argv[]) {
+ for (int i = 0; i < argc; i++) {
+ if (strcmp(argv[i], "--stress-opt") == 0) {
+ options.stress_opt = true;
+ argv[i] = NULL;
+ } else if (strcmp(argv[i], "--stress-deopt") == 0) {
+ options.stress_deopt = true;
+ argv[i] = NULL;
+ } else if (strcmp(argv[i], "--noalways-opt") == 0) {
+ // No support for stressing if we can't use --always-opt.
+ options.stress_opt = false;
+ options.stress_deopt = false;
+ } else if (strcmp(argv[i], "--shell") == 0) {
+ options.interactive_shell = true;
+ argv[i] = NULL;
+ } else if (strcmp(argv[i], "--test") == 0) {
+ options.test_shell = true;
+ argv[i] = NULL;
+ } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
+ options.send_idle_notification = true;
+ argv[i] = NULL;
+ } else if (strcmp(argv[i], "--preemption") == 0) {
+#ifdef V8_SHARED
+ printf("D8 with shared library does not support multi-threading\n");
+ return false;
+#else
+ options.use_preemption = true;
+ argv[i] = NULL;
+#endif // V8_SHARED
+ } else if (strcmp(argv[i], "--nopreemption") == 0) {
+#ifdef V8_SHARED
+ printf("D8 with shared library does not support multi-threading\n");
+ return false;
+#else
+ options.use_preemption = false;
+ argv[i] = NULL;
+#endif // V8_SHARED
+ } else if (strcmp(argv[i], "--preemption-interval") == 0) {
+#ifdef V8_SHARED
+ printf("D8 with shared library does not support multi-threading\n");
+ return false;
+#else
+ if (++i < argc) {
+ argv[i-1] = NULL;
+ char* end = NULL;
+ options.preemption_interval = strtol(argv[i], &end, 10); // NOLINT
+ if (options.preemption_interval <= 0
+ || *end != '\0'
+ || errno == ERANGE) {
+ printf("Invalid value for --preemption-interval '%s'\n", argv[i]);
+ return false;
+ }
+ argv[i] = NULL;
+ } else {
+ printf("Missing value for --preemption-interval\n");
+ return false;
+ }
+#endif // V8_SHARED
+ } else if (strcmp(argv[i], "-f") == 0) {
+ // Ignore any -f flags for compatibility with other stand-alone
+ // JavaScript engines.
+ continue;
+ } else if (strcmp(argv[i], "--isolate") == 0) {
+#ifdef V8_SHARED
+ printf("D8 with shared library does not support multi-threading\n");
+ return false;
+#endif // V8_SHARED
+ options.num_isolates++;
+ } else if (strcmp(argv[i], "-p") == 0) {
+#ifdef V8_SHARED
+ printf("D8 with shared library does not support multi-threading\n");
+ return false;
+#else
+ options.num_parallel_files++;
+#endif // V8_SHARED
+ } else if (strcmp(argv[i], "--dump-heap-constants") == 0) {
+#ifdef V8_SHARED
+ printf("D8 with shared library does not support constant dumping\n");
+ return false;
+#else
+ options.dump_heap_constants = true;
+ argv[i] = NULL;
+#endif
+ }
+#ifdef V8_SHARED
+ else if (strcmp(argv[i], "--dump-counters") == 0) {
+ printf("D8 with shared library does not include counters\n");
+ return false;
+ } else if (strcmp(argv[i], "--debugger") == 0) {
+ printf("Javascript debugger not included\n");
+ return false;
+ }
+#endif // V8_SHARED
+ }
+
+#ifndef V8_SHARED
+ // Run parallel threads if we are not using --isolate
+ options.parallel_files = new char*[options.num_parallel_files];
+ int parallel_files_set = 0;
+ for (int i = 1; i < argc; i++) {
+ if (argv[i] == NULL) continue;
+ if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
+ if (options.num_isolates > 1) {
+ printf("-p is not compatible with --isolate\n");
+ return false;
+ }
+ argv[i] = NULL;
+ i++;
+ options.parallel_files[parallel_files_set] = argv[i];
+ parallel_files_set++;
+ argv[i] = NULL;
+ }
+ }
+ if (parallel_files_set != options.num_parallel_files) {
+ printf("-p requires a file containing a list of files as parameter\n");
+ return false;
+ }
+#endif // V8_SHARED
+
+ v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
+
+ // Set up isolated source groups.
+ options.isolate_sources = new SourceGroup[options.num_isolates];
+ SourceGroup* current = options.isolate_sources;
+ current->Begin(argv, 1);
+ for (int i = 1; i < argc; i++) {
+ const char* str = argv[i];
+ if (strcmp(str, "--isolate") == 0) {
+ current->End(i);
+ current++;
+ current->Begin(argv, i + 1);
+ } else if (strncmp(argv[i], "--", 2) == 0) {
+ printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]);
+ }
+ }
+ current->End(argc);
+
+ return true;
+}
+
+
+int Shell::RunMain(Isolate* isolate, int argc, char* argv[]) {
+#ifndef V8_SHARED
+ i::List<i::Thread*> threads(1);
+ if (options.parallel_files != NULL) {
+ for (int i = 0; i < options.num_parallel_files; i++) {
+ char* files = NULL;
+ { Locker lock(isolate);
+ int size = 0;
+ files = ReadChars(isolate, options.parallel_files[i], &size);
+ }
+ if (files == NULL) {
+ printf("File list '%s' not found\n", options.parallel_files[i]);
+ Exit(1);
+ }
+ ShellThread* thread = new ShellThread(isolate, files);
+ thread->Start();
+ threads.Add(thread);
+ }
+ }
+ for (int i = 1; i < options.num_isolates; ++i) {
+ options.isolate_sources[i].StartExecuteInThread();
+ }
+#endif // V8_SHARED
+ { // NOLINT
+ Locker lock(isolate);
+ {
+ HandleScope scope(isolate);
+ Local<Context> context = CreateEvaluationContext(isolate);
+ if (options.last_run) {
+ // Keep using the same context in the interactive shell.
+ evaluation_context_.Reset(isolate, 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(isolate);
+ }
+#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
+ }
+ {
+ Context::Scope cscope(context);
+ PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
+ options.isolate_sources[0].Execute(isolate);
+ }
+ }
+ if (!options.last_run) {
+ if (options.send_idle_notification) {
+ const int kLongIdlePauseInMs = 1000;
+ V8::ContextDisposedNotification();
+ V8::IdleNotification(kLongIdlePauseInMs);
+ }
+ }
+
+#ifndef V8_SHARED
+ // Start preemption if threads have been created and preemption is enabled.
+ if (threads.length() > 0
+ && options.use_preemption) {
+ Locker::StartPreemption(options.preemption_interval);
+ }
+#endif // V8_SHARED
+ }
+
+#ifndef V8_SHARED
+ for (int i = 1; i < options.num_isolates; ++i) {
+ options.isolate_sources[i].WaitForThread();
+ }
+
+ for (int i = 0; i < threads.length(); i++) {
+ i::Thread* thread = threads[i];
+ thread->Join();
+ delete thread;
+ }
+
+ if (threads.length() > 0 && options.use_preemption) {
+ Locker lock(isolate);
+ Locker::StopPreemption();
+ }
+#endif // V8_SHARED
+ return 0;
+}
+
+
+#ifdef V8_SHARED
+static void SetStandaloneFlagsViaCommandLine() {
+ int fake_argc = 3;
+ char **fake_argv = new char*[3];
+ fake_argv[0] = NULL;
+ fake_argv[1] = strdup("--harmony-typed-arrays");
+ fake_argv[2] = strdup("--trace-hydrogen-file=hydrogen.cfg");
+ v8::V8::SetFlagsFromCommandLine(&fake_argc, fake_argv, false);
+ free(fake_argv[1]);
+ delete[] fake_argv;
+}
+#endif
+
+
+#ifndef V8_SHARED
+static void DumpHeapConstants(i::Isolate* isolate) {
+ i::Heap* heap = isolate->heap();
+
+ // Dump the INSTANCE_TYPES table to the console.
+ printf("# List of known V8 instance types.\n");
+#define DUMP_TYPE(T) printf(" %d: \"%s\",\n", i::T, #T);
+ printf("INSTANCE_TYPES = {\n");
+ INSTANCE_TYPE_LIST(DUMP_TYPE)
+ printf("}\n");
+#undef DUMP_TYPE
+
+ // Dump the KNOWN_MAP table to the console.
+ printf("\n# List of known V8 maps.\n");
+#define ROOT_LIST_CASE(type, name, camel_name) \
+ if (n == NULL && o == heap->name()) n = #camel_name;
+#define STRUCT_LIST_CASE(upper_name, camel_name, name) \
+ if (n == NULL && o == heap->name##_map()) n = #camel_name "Map";
+ i::HeapObjectIterator it(heap->map_space());
+ printf("KNOWN_MAPS = {\n");
+ for (i::Object* o = it.Next(); o != NULL; o = it.Next()) {
+ i::Map* m = i::Map::cast(o);
+ const char* n = NULL;
+ intptr_t p = reinterpret_cast<intptr_t>(m) & 0xfffff;
+ int t = m->instance_type();
+ ROOT_LIST(ROOT_LIST_CASE)
+ STRUCT_LIST(STRUCT_LIST_CASE)
+ if (n == NULL) continue;
+ printf(" 0x%05" V8PRIxPTR ": (%d, \"%s\"),\n", p, t, n);
+ }
+ printf("}\n");
+#undef STRUCT_LIST_CASE
+#undef ROOT_LIST_CASE
+
+ // Dump the KNOWN_OBJECTS table to the console.
+ printf("\n# List of known V8 objects.\n");
+#define ROOT_LIST_CASE(type, name, camel_name) \
+ if (n == NULL && o == heap->name()) n = #camel_name;
+ i::OldSpaces spit(heap);
+ printf("KNOWN_OBJECTS = {\n");
+ for (i::PagedSpace* s = spit.next(); s != NULL; s = spit.next()) {
+ i::HeapObjectIterator it(s);
+ const char* sname = AllocationSpaceName(s->identity());
+ for (i::Object* o = it.Next(); o != NULL; o = it.Next()) {
+ const char* n = NULL;
+ intptr_t p = reinterpret_cast<intptr_t>(o) & 0xfffff;
+ ROOT_LIST(ROOT_LIST_CASE)
+ if (n == NULL) continue;
+ printf(" (\"%s\", 0x%05" V8PRIxPTR "): \"%s\",\n", sname, p, n);
+ }
+ }
+ printf("}\n");
+#undef ROOT_LIST_CASE
+}
+#endif // V8_SHARED
+
+
+class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
+ public:
+ virtual void* Allocate(size_t length) {
+ void* result = malloc(length);
+ memset(result, 0, length);
+ return result;
+ }
+ virtual void* AllocateUninitialized(size_t length) {
+ return malloc(length);
+ }
+ virtual void Free(void* data, size_t) { free(data); }
+ // TODO(dslomov): Remove when v8:2823 is fixed.
+ virtual void Free(void* data) {
+#ifndef V8_SHARED
+ UNREACHABLE();
+#endif
+ }
+};
+
+
+int Shell::Main(int argc, char* argv[]) {
+ if (!SetOptions(argc, argv)) return 1;
+ v8::V8::InitializeICU();
+#ifndef V8_SHARED
+ i::FLAG_harmony_array_buffer = true;
+ i::FLAG_harmony_typed_arrays = true;
+ i::FLAG_trace_hydrogen_file = "hydrogen.cfg";
+#else
+ SetStandaloneFlagsViaCommandLine();
+#endif
+ ShellArrayBufferAllocator array_buffer_allocator;
+ v8::V8::SetArrayBufferAllocator(&array_buffer_allocator);
+ int result = 0;
+ Isolate* isolate = Isolate::GetCurrent();
+ DumbLineEditor dumb_line_editor(isolate);
+ {
+ Initialize(isolate);
+#ifdef ENABLE_VTUNE_JIT_INTERFACE
+ vTune::InitializeVtuneForV8();
+#endif
+ PerIsolateData data(isolate);
+ InitializeDebugger(isolate);
+
+#ifndef V8_SHARED
+ if (options.dump_heap_constants) {
+ DumpHeapConstants(reinterpret_cast<i::Isolate*>(isolate));
+ return 0;
+ }
+#endif
+
+ if (options.stress_opt || options.stress_deopt) {
+ Testing::SetStressRunType(options.stress_opt
+ ? Testing::kStressTypeOpt
+ : Testing::kStressTypeDeopt);
+ int stress_runs = Testing::GetStressRuns();
+ for (int i = 0; i < stress_runs && result == 0; i++) {
+ printf("============ Stress %d/%d ============\n", i + 1, stress_runs);
+ Testing::PrepareStressRun(i);
+ options.last_run = (i == stress_runs - 1);
+ result = RunMain(isolate, argc, 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);
+ options.last_run = (i == stress_runs - 1);
+ result = RunMain(isolate, argc, argv);
+ }
+#endif
+ } else {
+ result = RunMain(isolate, argc, argv);
+ }
+
+
+#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
+ // Run remote debugger if requested, but never on --test
+ if (i::FLAG_remote_debugger && !options.test_shell) {
+ InstallUtilityScript(isolate);
+ RunRemoteDebugger(isolate, i::FLAG_debugger_port);
+ return 0;
+ }
+#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
+
+ // Run interactive shell if explicitly requested or if no script has been
+ // executed, but never on --test
+
+ if (( options.interactive_shell || !options.script_executed )
+ && !options.test_shell ) {
+#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
+ if (!i::FLAG_debugger) {
+ InstallUtilityScript(isolate);
+ }
+#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
+ RunShell(isolate);
+ }
+ }
+ V8::Dispose();
+
+ OnExit();
+
+ return result;
+}
+
+} // namespace v8
+
+
+#ifndef GOOGLE3
+int main(int argc, char* argv[]) {
+ return v8::Shell::Main(argc, argv);
+}
+#endif
diff --git a/chromium/v8/src/d8.gyp b/chromium/v8/src/d8.gyp
new file mode 100644
index 00000000000..15d342dece7
--- /dev/null
+++ b/chromium/v8/src/d8.gyp
@@ -0,0 +1,133 @@
+# Copyright 2012 the V8 project authors. 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': {
+ 'v8_code': 1,
+ 'console%': '',
+ # Enable support for Intel VTune. Supported on ia32/x64 only
+ 'v8_enable_vtunejit%': 0,
+ 'v8_enable_i18n_support%': 0,
+ },
+ 'includes': ['../build/toolchain.gypi', '../build/features.gypi'],
+ 'targets': [
+ {
+ 'target_name': 'd8',
+ 'type': 'executable',
+ 'dependencies': [
+ '../tools/gyp/v8.gyp:v8',
+ ],
+ # Generated source files need this explicitly:
+ 'include_dirs+': [
+ '../src',
+ ],
+ 'sources': [
+ 'd8.cc',
+ ],
+ 'conditions': [
+ [ 'console=="readline"', {
+ 'libraries': [ '-lreadline', ],
+ 'sources': [ 'd8-readline.cc' ],
+ }],
+ [ 'component!="shared_library"', {
+ 'sources': [ 'd8-debug.cc', '<(SHARED_INTERMEDIATE_DIR)/d8-js.cc', ],
+ 'conditions': [
+ [ 'want_separate_host_toolset==1', {
+ 'dependencies': [
+ 'd8_js2c#host',
+ ],
+ }, {
+ 'dependencies': [
+ 'd8_js2c',
+ ],
+ }],
+ ['(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"', {
+ 'sources': [ 'd8-windows.cc', ]
+ }],
+ ],
+ }],
+ ['v8_enable_vtunejit==1', {
+ 'dependencies': [
+ '../src/third_party/vtune/v8vtune.gyp:v8_vtune',
+ ],
+ }],
+ ['v8_enable_i18n_support==1', {
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu.gyp:icui18n',
+ '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
+ ],
+ }],
+ ['OS=="win" and v8_enable_i18n_support==1', {
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu.gyp:icudata',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'd8_js2c',
+ 'type': 'none',
+ 'variables': {
+ 'js_files': [
+ 'd8.js',
+ 'macros.py',
+ ],
+ },
+ 'conditions': [
+ [ 'want_separate_host_toolset==1', {
+ 'toolsets': ['host'],
+ }, {
+ 'toolsets': ['target'],
+ }]
+ ],
+ 'actions': [
+ {
+ 'action_name': 'd8_js2c',
+ 'inputs': [
+ '../tools/js2c.py',
+ '<@(js_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/d8-js.cc',
+ ],
+ 'action': [
+ 'python',
+ '../tools/js2c.py',
+ '<@(_outputs)',
+ 'D8',
+ 'off', # compress startup data
+ '<@(js_files)'
+ ],
+ },
+ ],
+ }
+ ],
+}
diff --git a/chromium/v8/src/d8.h b/chromium/v8/src/d8.h
new file mode 100644
index 00000000000..3b06985ca25
--- /dev/null
+++ b/chromium/v8/src/d8.h
@@ -0,0 +1,429 @@
+// Copyright 2012 the V8 project authors. 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_D8_H_
+#define V8_D8_H_
+
+#ifndef V8_SHARED
+#include "allocation.h"
+#include "hashmap.h"
+#include "smart-pointers.h"
+#include "v8.h"
+#else
+#include "../include/v8.h"
+#endif // V8_SHARED
+
+namespace v8 {
+
+
+#ifndef V8_SHARED
+// A single counter in a counter collection.
+class Counter {
+ public:
+ static const int kMaxNameSize = 64;
+ int32_t* Bind(const char* name, bool histogram);
+ int32_t* ptr() { return &count_; }
+ int32_t count() { return count_; }
+ int32_t sample_total() { return sample_total_; }
+ bool is_histogram() { return is_histogram_; }
+ void AddSample(int32_t sample);
+ private:
+ int32_t count_;
+ int32_t sample_total_;
+ bool is_histogram_;
+ uint8_t name_[kMaxNameSize];
+};
+
+
+// A set of counters and associated information. An instance of this
+// class is stored directly in the memory-mapped counters file if
+// the --map-counters options is used
+class CounterCollection {
+ public:
+ CounterCollection();
+ Counter* GetNextCounter();
+ private:
+ static const unsigned kMaxCounters = 512;
+ uint32_t magic_number_;
+ uint32_t max_counters_;
+ uint32_t max_name_size_;
+ uint32_t counters_in_use_;
+ Counter counters_[kMaxCounters];
+};
+
+
+class CounterMap {
+ public:
+ CounterMap(): hash_map_(Match) { }
+ Counter* Lookup(const char* name) {
+ i::HashMap::Entry* answer = hash_map_.Lookup(
+ const_cast<char*>(name),
+ Hash(name),
+ false);
+ if (!answer) return NULL;
+ return reinterpret_cast<Counter*>(answer->value);
+ }
+ void Set(const char* name, Counter* value) {
+ i::HashMap::Entry* answer = hash_map_.Lookup(
+ const_cast<char*>(name),
+ Hash(name),
+ true);
+ ASSERT(answer != NULL);
+ answer->value = value;
+ }
+ class Iterator {
+ public:
+ explicit Iterator(CounterMap* map)
+ : map_(&map->hash_map_), entry_(map_->Start()) { }
+ void Next() { entry_ = map_->Next(entry_); }
+ bool More() { return entry_ != NULL; }
+ const char* CurrentKey() { return static_cast<const char*>(entry_->key); }
+ Counter* CurrentValue() { return static_cast<Counter*>(entry_->value); }
+ private:
+ i::HashMap* map_;
+ i::HashMap::Entry* entry_;
+ };
+
+ private:
+ static int Hash(const char* name);
+ static bool Match(void* key1, void* key2);
+ i::HashMap hash_map_;
+};
+#endif // V8_SHARED
+
+
+class LineEditor {
+ public:
+ enum Type { DUMB = 0, READLINE = 1 };
+ LineEditor(Type type, const char* name);
+ virtual ~LineEditor() { }
+
+ virtual Handle<String> Prompt(const char* prompt) = 0;
+ virtual bool Open(Isolate* isolate) { return true; }
+ virtual bool Close() { return true; }
+ virtual void AddHistory(const char* str) { }
+
+ const char* name() { return name_; }
+ static LineEditor* Get() { return current_; }
+ private:
+ Type type_;
+ const char* name_;
+ static LineEditor* current_;
+};
+
+
+class SourceGroup {
+ public:
+ SourceGroup() :
+#ifndef V8_SHARED
+ next_semaphore_(v8::internal::OS::CreateSemaphore(0)),
+ done_semaphore_(v8::internal::OS::CreateSemaphore(0)),
+ thread_(NULL),
+#endif // V8_SHARED
+ argv_(NULL),
+ begin_offset_(0),
+ end_offset_(0) {}
+
+ ~SourceGroup();
+
+ void Begin(char** argv, int offset) {
+ argv_ = const_cast<const char**>(argv);
+ begin_offset_ = offset;
+ }
+
+ void End(int offset) { end_offset_ = offset; }
+
+ void Execute(Isolate* isolate);
+
+#ifndef V8_SHARED
+ void StartExecuteInThread();
+ void WaitForThread();
+
+ private:
+ class IsolateThread : public i::Thread {
+ public:
+ explicit IsolateThread(SourceGroup* group)
+ : i::Thread(GetThreadOptions()), group_(group) {}
+
+ virtual void Run() {
+ group_->ExecuteInThread();
+ }
+
+ private:
+ SourceGroup* group_;
+ };
+
+ static i::Thread::Options GetThreadOptions();
+ void ExecuteInThread();
+
+ i::Semaphore* next_semaphore_;
+ i::Semaphore* done_semaphore_;
+ i::Thread* thread_;
+#endif // V8_SHARED
+
+ void ExitShell(int exit_code);
+ Handle<String> ReadFile(Isolate* isolate, const char* name);
+
+ const char** argv_;
+ int begin_offset_;
+ int end_offset_;
+};
+
+
+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() :
+#ifndef V8_SHARED
+ use_preemption(true),
+ preemption_interval(10),
+ num_parallel_files(0),
+ parallel_files(NULL),
+#endif // V8_SHARED
+ script_executed(false),
+ last_run(true),
+ send_idle_notification(false),
+ stress_opt(false),
+ stress_deopt(false),
+ interactive_shell(false),
+ test_shell(false),
+ dump_heap_constants(false),
+ num_isolates(1),
+ isolate_sources(NULL) { }
+
+ ~ShellOptions() {
+#ifndef V8_SHARED
+ delete[] parallel_files;
+#endif // V8_SHARED
+ delete[] isolate_sources;
+ }
+
+#ifndef V8_SHARED
+ bool use_preemption;
+ int preemption_interval;
+ int num_parallel_files;
+ char** parallel_files;
+#endif // V8_SHARED
+ bool script_executed;
+ bool last_run;
+ bool send_idle_notification;
+ bool stress_opt;
+ bool stress_deopt;
+ bool interactive_shell;
+ bool test_shell;
+ bool dump_heap_constants;
+ int num_isolates;
+ SourceGroup* isolate_sources;
+};
+
+#ifdef V8_SHARED
+class Shell {
+#else
+class Shell : public i::AllStatic {
+#endif // V8_SHARED
+
+ public:
+ static bool ExecuteString(Isolate* isolate,
+ Handle<String> source,
+ Handle<Value> name,
+ bool print_result,
+ bool report_exceptions);
+ static const char* ToCString(const v8::String::Utf8Value& value);
+ static void ReportException(Isolate* isolate, TryCatch* try_catch);
+ static Handle<String> ReadFile(Isolate* isolate, const char* name);
+ static Local<Context> CreateEvaluationContext(Isolate* isolate);
+ static int RunMain(Isolate* isolate, int argc, char* argv[]);
+ static int Main(int argc, char* argv[]);
+ static void Exit(int exit_code);
+ static void OnExit();
+
+#ifndef V8_SHARED
+ static Handle<Array> GetCompletions(Isolate* isolate,
+ Handle<String> text,
+ Handle<String> full);
+ static int* LookupCounter(const char* name);
+ static void* CreateHistogram(const char* name,
+ int min,
+ int max,
+ size_t buckets);
+ static void AddHistogramSample(void* histogram, int sample);
+ static void MapCounters(const char* name);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ static Handle<Object> DebugMessageDetails(Isolate* isolate,
+ Handle<String> message);
+ static Handle<Value> DebugCommandToJSONRequest(Isolate* isolate,
+ Handle<String> command);
+ static void DispatchDebugMessages();
+#endif // ENABLE_DEBUGGER_SUPPORT
+#endif // V8_SHARED
+
+ static void RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RealmSharedGet(Local<String> property,
+ const PropertyCallbackInfo<Value>& info);
+ static void RealmSharedSet(Local<String> property,
+ Local<Value> value,
+ const PropertyCallbackInfo<void>& info);
+
+ static void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Write(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Quit(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Version(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Read(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static Handle<String> ReadFromStdin(Isolate* isolate);
+ static void ReadLine(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ args.GetReturnValue().Set(ReadFromStdin(args.GetIsolate()));
+ }
+ static void Load(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void ArrayBuffer(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Int8Array(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Uint8Array(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Int16Array(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Uint16Array(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Int32Array(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Uint32Array(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Float32Array(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Float64Array(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Uint8ClampedArray(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void ArrayBufferSlice(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void ArraySubArray(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void ArraySet(const v8::FunctionCallbackInfo<v8::Value>& args);
+ // The OS object on the global object contains methods for performing
+ // operating system calls:
+ //
+ // os.system("program_name", ["arg1", "arg2", ...], timeout1, timeout2) will
+ // run the command, passing the arguments to the program. The standard output
+ // of the program will be picked up and returned as a multiline string. If
+ // timeout1 is present then it should be a number. -1 indicates no timeout
+ // and a positive number is used as a timeout in milliseconds that limits the
+ // time spent waiting between receiving output characters from the program.
+ // timeout2, if present, should be a number indicating the limit in
+ // milliseconds on the total running time of the program. Exceptions are
+ // thrown on timeouts or other errors or if the exit status of the program
+ // indicates an error.
+ //
+ // os.chdir(dir) changes directory to the given directory. Throws an
+ // exception/ on error.
+ //
+ // os.setenv(variable, value) sets an environment variable. Repeated calls to
+ // this method leak memory due to the API of setenv in the standard C library.
+ //
+ // os.umask(alue) calls the umask system call and returns the old umask.
+ //
+ // os.mkdirp(name, mask) creates a directory. The mask (if present) is anded
+ // with the current umask. Intermediate directories are created if necessary.
+ // An exception is not thrown if the directory already exists. Analogous to
+ // the "mkdir -p" command.
+ static void OSObject(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void System(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ static void AddOSMethods(Handle<ObjectTemplate> os_template);
+
+ static const char* kPrompt;
+ static ShellOptions options;
+
+ private:
+ static Persistent<Context> evaluation_context_;
+#ifndef V8_SHARED
+ static Persistent<Context> utility_context_;
+ static CounterMap* counter_map_;
+ // We statically allocate a set of local counters to be used if we
+ // don't want to store the stats in a memory-mapped file
+ static CounterCollection local_counters_;
+ static CounterCollection* counters_;
+ static i::OS::MemoryMappedFile* counters_file_;
+ static i::Mutex* context_mutex_;
+
+ static Counter* GetCounter(const char* name, bool is_histogram);
+ static void InstallUtilityScript(Isolate* isolate);
+#endif // V8_SHARED
+ static void Initialize(Isolate* isolate);
+ static void InitializeDebugger(Isolate* isolate);
+ static void RunShell(Isolate* isolate);
+ static bool SetOptions(int argc, char* argv[]);
+ static Handle<ObjectTemplate> CreateGlobalTemplate(Isolate* isolate);
+ static Handle<FunctionTemplate> CreateArrayBufferTemplate(InvocationCallback);
+ static Handle<FunctionTemplate> CreateArrayTemplate(InvocationCallback);
+ static Handle<Value> CreateExternalArrayBuffer(Isolate* isolate,
+ Handle<Object> buffer,
+ int32_t size);
+ static Handle<Object> CreateExternalArray(Isolate* isolate,
+ Handle<Object> array,
+ Handle<Object> buffer,
+ ExternalArrayType type,
+ int32_t length,
+ int32_t byteLength,
+ int32_t byteOffset,
+ int32_t element_size);
+ static void CreateExternalArray(
+ const v8::FunctionCallbackInfo<v8::Value>& args,
+ ExternalArrayType type,
+ int32_t element_size);
+ static void ExternalArrayWeakCallback(Isolate* isolate,
+ Persistent<Object>* object,
+ uint8_t* data);
+};
+
+
+} // namespace v8
+
+
+#endif // V8_D8_H_
diff --git a/chromium/v8/src/d8.js b/chromium/v8/src/d8.js
new file mode 100644
index 00000000000..3efea063787
--- /dev/null
+++ b/chromium/v8/src/d8.js
@@ -0,0 +1,2253 @@
+// Copyright 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.
+
+"use strict";
+
+String.prototype.startsWith = function (str) {
+ if (str.length > this.length) {
+ return false;
+ }
+ return this.substr(0, str.length) == str;
+};
+
+function log10(num) {
+ return Math.log(num)/Math.log(10);
+}
+
+function ToInspectableObject(obj) {
+ if (!obj && typeof obj === 'object') {
+ return void 0;
+ } else {
+ return Object(obj);
+ }
+}
+
+function GetCompletions(global, last, full) {
+ var full_tokens = full.split();
+ full = full_tokens.pop();
+ var parts = full.split('.');
+ parts.pop();
+ var current = global;
+ for (var i = 0; i < parts.length; i++) {
+ var part = parts[i];
+ var next = current[part];
+ if (!next) {
+ return [];
+ }
+ current = next;
+ }
+ var result = [];
+ current = ToInspectableObject(current);
+ while (typeof current !== 'undefined') {
+ var mirror = new $debug.ObjectMirror(current);
+ var properties = mirror.properties();
+ for (var i = 0; i < properties.length; i++) {
+ var name = properties[i].name();
+ if (typeof name === 'string' && name.startsWith(last)) {
+ result.push(name);
+ }
+ }
+ current = ToInspectableObject(Object.getPrototypeOf(current));
+ }
+ return result;
+}
+
+
+// Global object holding debugger related constants and state.
+var Debug = {};
+
+
+// Debug events which can occour in the V8 JavaScript engine. These originate
+// from the API include file v8-debug.h.
+Debug.DebugEvent = { Break: 1,
+ Exception: 2,
+ NewFunction: 3,
+ BeforeCompile: 4,
+ AfterCompile: 5 };
+
+
+// The different types of scripts matching enum ScriptType in objects.h.
+Debug.ScriptType = { Native: 0,
+ Extension: 1,
+ Normal: 2 };
+
+
+// The different types of script compilations matching enum
+// Script::CompilationType in objects.h.
+Debug.ScriptCompilationType = { Host: 0,
+ Eval: 1,
+ JSON: 2 };
+
+
+// The different types of scopes matching constants runtime.cc.
+Debug.ScopeType = { Global: 0,
+ Local: 1,
+ With: 2,
+ Closure: 3,
+ Catch: 4,
+ Block: 5 };
+
+
+// Current debug state.
+var kNoFrame = -1;
+Debug.State = {
+ currentFrame: kNoFrame,
+ 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 = '';
+var repeat_cmd_line = '';
+var is_running = true;
+// Global variable used to store whether a handle was requested.
+var lookup_handle = null;
+
+// Copied from debug-delay.js. This is needed below:
+function ScriptTypeFlag(type) {
+ return (1 << type);
+}
+
+
+// Process a debugger JSON message into a display text and a running status.
+// This function returns an object with properties "text" and "running" holding
+// this information.
+function DebugMessageDetails(message) {
+ if (trace_debug_json) {
+ print("received: '" + message + "'");
+ }
+ // Convert the JSON string to an object.
+ var response = new ProtocolPackage(message);
+ is_running = response.running();
+
+ if (response.type() == 'event') {
+ return DebugEventDetails(response);
+ } else {
+ return DebugResponseDetails(response);
+ }
+}
+
+function DebugEventDetails(response) {
+ var details = {text:'', running:false};
+
+ // Get the running state.
+ details.running = response.running();
+
+ var body = response.body();
+ var result = '';
+ switch (response.event()) {
+ case 'break':
+ if (body.breakpoints) {
+ result += 'breakpoint';
+ if (body.breakpoints.length > 1) {
+ result += 's';
+ }
+ result += ' #';
+ for (var i = 0; i < body.breakpoints.length; i++) {
+ if (i > 0) {
+ result += ', #';
+ }
+ result += body.breakpoints[i];
+ }
+ } else {
+ result += 'break';
+ }
+ result += ' in ';
+ result += body.invocationText;
+ result += ', ';
+ result += SourceInfo(body);
+ result += '\n';
+ result += SourceUnderline(body.sourceLineText, body.sourceColumn);
+ Debug.State.currentSourceLine = body.sourceLine;
+ Debug.State.displaySourceStartLine = -1;
+ Debug.State.displaySourceEndLine = -1;
+ Debug.State.currentFrame = 0;
+ details.text = result;
+ break;
+
+ case 'exception':
+ if (body.uncaught) {
+ result += 'Uncaught: ';
+ } else {
+ result += 'Exception: ';
+ }
+ result += '"';
+ result += body.exception.text;
+ result += '"';
+ if (body.sourceLine >= 0) {
+ result += ', ';
+ result += SourceInfo(body);
+ result += '\n';
+ result += SourceUnderline(body.sourceLineText, body.sourceColumn);
+ Debug.State.currentSourceLine = body.sourceLine;
+ Debug.State.displaySourceStartLine = -1;
+ Debug.State.displaySourceEndLine = -1;
+ Debug.State.currentFrame = 0;
+ } else {
+ result += ' (empty stack)';
+ Debug.State.currentSourceLine = -1;
+ Debug.State.displaySourceStartLine = -1;
+ Debug.State.displaySourceEndLine = -1;
+ Debug.State.currentFrame = kNoFrame;
+ }
+ details.text = result;
+ break;
+
+ case 'afterCompile':
+ if (trace_compile) {
+ result = 'Source ' + body.script.name + ' compiled:\n';
+ var source = body.script.source;
+ if (!(source[source.length - 1] == '\n')) {
+ result += source;
+ } else {
+ result += source.substring(0, source.length - 1);
+ }
+ }
+ details.text = result;
+ break;
+
+ case 'scriptCollected':
+ details.text = result;
+ break;
+
+ default:
+ details.text = 'Unknown debug event ' + response.event();
+ }
+
+ return details;
+}
+
+
+function SourceInfo(body) {
+ var result = '';
+
+ if (body.script) {
+ if (body.script.name) {
+ result += body.script.name;
+ } else {
+ result += '[unnamed]';
+ }
+ }
+ result += ' line ';
+ result += body.sourceLine + 1;
+ result += ' column ';
+ result += body.sourceColumn + 1;
+
+ return result;
+}
+
+
+function SourceUnderline(source_text, position) {
+ if (!source_text) {
+ return;
+ }
+
+ // Create an underline with a caret pointing to the source position. If the
+ // source contains a tab character the underline will have a tab character in
+ // the same place otherwise the underline will have a space character.
+ var underline = '';
+ for (var i = 0; i < position; i++) {
+ if (source_text[i] == '\t') {
+ underline += '\t';
+ } else {
+ underline += ' ';
+ }
+ }
+ underline += '^';
+
+ // Return the source line text with the underline beneath.
+ return source_text + '\n' + underline;
+}
+
+
+// Converts a text command to a JSON request.
+function DebugCommandToJSONRequest(cmd_line) {
+ var result = new DebugRequest(cmd_line).JSONRequest();
+ if (trace_debug_json && result) {
+ print("sending: '" + result + "'");
+ }
+ return result;
+}
+
+
+function DebugRequest(cmd_line) {
+ // If the very first character is a { assume that a JSON request have been
+ // entered as a command. Converting that to a JSON request is trivial.
+ if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') {
+ this.request_ = cmd_line;
+ return;
+ }
+
+ // Check for a simple carriage return to repeat the last command:
+ var is_repeating = false;
+ if (cmd_line == '\n') {
+ if (is_running) {
+ cmd_line = 'break'; // Not in debugger mode, break with a frame request.
+ } else {
+ cmd_line = repeat_cmd_line; // use command to repeat.
+ is_repeating = true;
+ }
+ }
+ if (!is_running) { // Only save the command if in debugger mode.
+ repeat_cmd_line = cmd_line; // save last command.
+ }
+
+ // Trim string for leading and trailing whitespace.
+ cmd_line = cmd_line.replace(/^\s+|\s+$/g, '');
+
+ // Find the command.
+ var pos = cmd_line.indexOf(' ');
+ var cmd;
+ var args;
+ if (pos == -1) {
+ cmd = cmd_line;
+ args = '';
+ } else {
+ cmd = cmd_line.slice(0, pos);
+ args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, '');
+ }
+
+ if ((cmd === undefined) || !cmd) {
+ this.request_ = void 0;
+ return;
+ }
+
+ last_cmd = cmd;
+
+ // Switch on command.
+ switch (cmd) {
+ case 'continue':
+ case 'c':
+ this.request_ = this.continueCommandToJSONRequest_(args);
+ break;
+
+ case 'step':
+ case 's':
+ this.request_ = this.stepCommandToJSONRequest_(args, 'in');
+ break;
+
+ case 'stepi':
+ case 'si':
+ this.request_ = this.stepCommandToJSONRequest_(args, 'min');
+ break;
+
+ case 'next':
+ case 'n':
+ this.request_ = this.stepCommandToJSONRequest_(args, 'next');
+ break;
+
+ case 'finish':
+ case 'fin':
+ this.request_ = this.stepCommandToJSONRequest_(args, 'out');
+ break;
+
+ case 'backtrace':
+ case 'bt':
+ this.request_ = this.backtraceCommandToJSONRequest_(args);
+ break;
+
+ case 'frame':
+ case 'f':
+ this.request_ = this.frameCommandToJSONRequest_(args);
+ break;
+
+ case 'scopes':
+ this.request_ = this.scopesCommandToJSONRequest_(args);
+ break;
+
+ case 'scope':
+ this.request_ = this.scopeCommandToJSONRequest_(args);
+ break;
+
+ case 'disconnect':
+ case 'exit':
+ case 'quit':
+ this.request_ = this.disconnectCommandToJSONRequest_(args);
+ break;
+
+ case 'up':
+ this.request_ =
+ this.frameCommandToJSONRequest_('' +
+ (Debug.State.currentFrame + 1));
+ break;
+
+ case 'down':
+ case 'do':
+ this.request_ =
+ this.frameCommandToJSONRequest_('' +
+ (Debug.State.currentFrame - 1));
+ break;
+
+ case 'set':
+ case 'print':
+ case 'p':
+ this.request_ = this.printCommandToJSONRequest_(args);
+ break;
+
+ case 'dir':
+ this.request_ = this.dirCommandToJSONRequest_(args);
+ break;
+
+ case 'references':
+ this.request_ = this.referencesCommandToJSONRequest_(args);
+ break;
+
+ case 'instances':
+ this.request_ = this.instancesCommandToJSONRequest_(args);
+ break;
+
+ case 'list':
+ case 'l':
+ this.request_ = this.listCommandToJSONRequest_(args);
+ break;
+ case 'source':
+ this.request_ = this.sourceCommandToJSONRequest_(args);
+ break;
+
+ case 'scripts':
+ case 'script':
+ case 'scr':
+ this.request_ = this.scriptsCommandToJSONRequest_(args);
+ break;
+
+ case 'break':
+ case 'b':
+ this.request_ = this.breakCommandToJSONRequest_(args);
+ break;
+
+ case 'breakpoints':
+ case 'bb':
+ this.request_ = this.breakpointsCommandToJSONRequest_(args);
+ break;
+
+ case 'clear':
+ case 'delete':
+ case 'd':
+ this.request_ = this.clearCommandToJSONRequest_(args);
+ break;
+
+ case 'threads':
+ this.request_ = this.threadsCommandToJSONRequest_(args);
+ break;
+
+ case 'cond':
+ this.request_ = this.changeBreakpointCommandToJSONRequest_(args, 'cond');
+ break;
+
+ case 'enable':
+ case 'en':
+ this.request_ =
+ this.changeBreakpointCommandToJSONRequest_(args, 'enable');
+ break;
+
+ case 'disable':
+ case 'dis':
+ this.request_ =
+ this.changeBreakpointCommandToJSONRequest_(args, 'disable');
+ break;
+
+ case 'ignore':
+ this.request_ =
+ this.changeBreakpointCommandToJSONRequest_(args, 'ignore');
+ break;
+
+ case 'info':
+ case 'inf':
+ this.request_ = this.infoCommandToJSONRequest_(args);
+ break;
+
+ case 'flags':
+ this.request_ = this.v8FlagsToJSONRequest_(args);
+ break;
+
+ case 'gc':
+ this.request_ = this.gcToJSONRequest_(args);
+ break;
+
+ case 'trace':
+ case 'tr':
+ // Return undefined to indicate command handled internally (no JSON).
+ this.request_ = void 0;
+ this.traceCommand_(args);
+ break;
+
+ case 'help':
+ case '?':
+ this.helpCommand_(args);
+ // Return undefined to indicate command handled internally (no JSON).
+ this.request_ = void 0;
+ break;
+
+ default:
+ throw new Error('Unknown command "' + cmd + '"');
+ }
+}
+
+DebugRequest.prototype.JSONRequest = function() {
+ return this.request_;
+};
+
+
+function RequestPacket(command) {
+ this.seq = 0;
+ this.type = 'request';
+ this.command = command;
+}
+
+
+RequestPacket.prototype.toJSONProtocol = function() {
+ // Encode the protocol header.
+ var json = '{';
+ json += '"seq":' + this.seq;
+ json += ',"type":"' + this.type + '"';
+ if (this.command) {
+ json += ',"command":' + StringToJSON_(this.command);
+ }
+ if (this.arguments) {
+ json += ',"arguments":';
+ // Encode the arguments part.
+ if (this.arguments.toJSONProtocol) {
+ json += this.arguments.toJSONProtocol();
+ } else {
+ json += SimpleObjectToJSON_(this.arguments);
+ }
+ }
+ json += '}';
+ return json;
+};
+
+
+DebugRequest.prototype.createRequest = function(command) {
+ return new RequestPacket(command);
+};
+
+
+// Create a JSON request for the evaluation command.
+DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
+ lookup_handle = null;
+
+ // Check if the expression is a handle id in the form #<handle>#.
+ var handle_match = expression.match(/^#([0-9]*)#$/);
+ if (handle_match) {
+ // Remember the handle requested in a global variable.
+ lookup_handle = parseInt(handle_match[1]);
+ // Build a lookup request.
+ var request = this.createRequest('lookup');
+ request.arguments = {};
+ request.arguments.handles = [ lookup_handle ];
+ return request.toJSONProtocol();
+ } else {
+ // Build an evaluate request.
+ var request = this.createRequest('evaluate');
+ request.arguments = {};
+ request.arguments.expression = expression;
+ // Request a global evaluation if there is no current frame.
+ if (Debug.State.currentFrame == kNoFrame) {
+ request.arguments.global = true;
+ }
+ return request.toJSONProtocol();
+ }
+};
+
+
+// Create a JSON request for the references/instances command.
+DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) {
+ // Build a references request.
+ var handle_match = handle.match(/^#([0-9]*)#$/);
+ if (handle_match) {
+ var request = this.createRequest('references');
+ request.arguments = {};
+ request.arguments.type = type;
+ request.arguments.handle = parseInt(handle_match[1]);
+ return request.toJSONProtocol();
+ } else {
+ throw new Error('Invalid object id.');
+ }
+};
+
+
+// Create a JSON request for the continue command.
+DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
+ var request = this.createRequest('continue');
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the step command.
+DebugRequest.prototype.stepCommandToJSONRequest_ = function(args, type) {
+ // Requesting a step is through the continue command with additional
+ // arguments.
+ var request = this.createRequest('continue');
+ request.arguments = {};
+
+ // Process arguments if any.
+
+ // Only process args if the command is 'step' which is indicated by type being
+ // set to 'in'. For all other commands, ignore the args.
+ if (args && args.length > 0) {
+ args = args.split(/\s+/g);
+
+ if (args.length > 2) {
+ throw new Error('Invalid step arguments.');
+ }
+
+ if (args.length > 0) {
+ // Check if we have a gdb stype step command. If so, the 1st arg would
+ // be the step count. If it's not a number, then assume that we're
+ // parsing for the legacy v8 step command.
+ var stepcount = Number(args[0]);
+ if (stepcount == Number.NaN) {
+ // No step count at arg 1. Process as legacy d8 step command:
+ if (args.length == 2) {
+ var stepcount = parseInt(args[1]);
+ if (isNaN(stepcount) || stepcount <= 0) {
+ throw new Error('Invalid step count argument "' + args[0] + '".');
+ }
+ request.arguments.stepcount = stepcount;
+ }
+
+ // Get the step action.
+ switch (args[0]) {
+ case 'in':
+ case 'i':
+ request.arguments.stepaction = 'in';
+ break;
+
+ case 'min':
+ case 'm':
+ request.arguments.stepaction = 'min';
+ break;
+
+ case 'next':
+ case 'n':
+ request.arguments.stepaction = 'next';
+ break;
+
+ case 'out':
+ case 'o':
+ request.arguments.stepaction = 'out';
+ break;
+
+ default:
+ throw new Error('Invalid step argument "' + args[0] + '".');
+ }
+
+ } else {
+ // gdb style step commands:
+ request.arguments.stepaction = type;
+ request.arguments.stepcount = stepcount;
+ }
+ }
+ } else {
+ // Default is step of the specified type.
+ request.arguments.stepaction = type;
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the backtrace command.
+DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) {
+ // Build a backtrace request from the text command.
+ var request = this.createRequest('backtrace');
+
+ // Default is to show top 10 frames.
+ request.arguments = {};
+ request.arguments.fromFrame = 0;
+ request.arguments.toFrame = 10;
+
+ args = args.split(/\s*[ ]+\s*/g);
+ if (args.length == 1 && args[0].length > 0) {
+ var frameCount = parseInt(args[0]);
+ if (frameCount > 0) {
+ // Show top frames.
+ request.arguments.fromFrame = 0;
+ request.arguments.toFrame = frameCount;
+ } else {
+ // Show bottom frames.
+ request.arguments.fromFrame = 0;
+ request.arguments.toFrame = -frameCount;
+ request.arguments.bottom = true;
+ }
+ } else if (args.length == 2) {
+ var fromFrame = parseInt(args[0]);
+ var toFrame = parseInt(args[1]);
+ if (isNaN(fromFrame) || fromFrame < 0) {
+ throw new Error('Invalid start frame argument "' + args[0] + '".');
+ }
+ if (isNaN(toFrame) || toFrame < 0) {
+ throw new Error('Invalid end frame argument "' + args[1] + '".');
+ }
+ if (fromFrame > toFrame) {
+ throw new Error('Invalid arguments start frame cannot be larger ' +
+ 'than end frame.');
+ }
+ // Show frame range.
+ request.arguments.fromFrame = fromFrame;
+ request.arguments.toFrame = toFrame + 1;
+ } else if (args.length > 2) {
+ throw new Error('Invalid backtrace arguments.');
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the frame command.
+DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) {
+ // Build a frame request from the text command.
+ var request = this.createRequest('frame');
+ args = args.split(/\s*[ ]+\s*/g);
+ if (args.length > 0 && args[0].length > 0) {
+ request.arguments = {};
+ request.arguments.number = args[0];
+ }
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the scopes command.
+DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) {
+ // Build a scopes request from the text command.
+ var request = this.createRequest('scopes');
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the scope command.
+DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) {
+ // Build a scope request from the text command.
+ var request = this.createRequest('scope');
+ args = args.split(/\s*[ ]+\s*/g);
+ if (args.length > 0 && args[0].length > 0) {
+ request.arguments = {};
+ request.arguments.number = args[0];
+ }
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the print command.
+DebugRequest.prototype.printCommandToJSONRequest_ = function(args) {
+ // Build an evaluate request from the text command.
+ if (args.length == 0) {
+ throw new Error('Missing expression.');
+ }
+ return this.makeEvaluateJSONRequest_(args);
+};
+
+
+// Create a JSON request for the dir command.
+DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
+ // Build an evaluate request from the text command.
+ if (args.length == 0) {
+ throw new Error('Missing expression.');
+ }
+ return this.makeEvaluateJSONRequest_(args);
+};
+
+
+// Create a JSON request for the references command.
+DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) {
+ // Build an evaluate request from the text command.
+ if (args.length == 0) {
+ throw new Error('Missing object id.');
+ }
+
+ return this.makeReferencesJSONRequest_(args, 'referencedBy');
+};
+
+
+// Create a JSON request for the instances command.
+DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
+ // Build an evaluate request from the text command.
+ if (args.length == 0) {
+ throw new Error('Missing object id.');
+ }
+
+ // Build a references request.
+ return this.makeReferencesJSONRequest_(args, 'constructedBy');
+};
+
+
+// Create a JSON request for the list command.
+DebugRequest.prototype.listCommandToJSONRequest_ = function(args) {
+
+ // Default is ten lines starting five lines before the current location.
+ if (Debug.State.displaySourceEndLine == -1) {
+ // If we list forwards, we will start listing after the last source end
+ // line. Set it to start from 5 lines before the current location.
+ Debug.State.displaySourceEndLine = Debug.State.currentSourceLine - 5;
+ // If we list backwards, we will start listing backwards from the last
+ // source start line. Set it to start from 1 lines before the current
+ // location.
+ Debug.State.displaySourceStartLine = Debug.State.currentSourceLine + 1;
+ }
+
+ var from = Debug.State.displaySourceEndLine + 1;
+ var lines = 10;
+
+ // Parse the arguments.
+ args = args.split(/\s*,\s*/g);
+ if (args == '') {
+ } else if ((args.length == 1) && (args[0] == '-')) {
+ from = Debug.State.displaySourceStartLine - lines;
+ } else if (args.length == 2) {
+ from = parseInt(args[0]);
+ lines = parseInt(args[1]) - from + 1; // inclusive of the ending line.
+ } else {
+ throw new Error('Invalid list arguments.');
+ }
+ Debug.State.displaySourceStartLine = from;
+ Debug.State.displaySourceEndLine = from + lines - 1;
+ var sourceArgs = '' + from + ' ' + lines;
+ return this.sourceCommandToJSONRequest_(sourceArgs);
+};
+
+
+// Create a JSON request for the source command.
+DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
+ // Build a evaluate request from the text command.
+ var request = this.createRequest('source');
+
+ // Default is ten lines starting five lines before the current location.
+ var from = Debug.State.currentSourceLine - 5;
+ var lines = 10;
+
+ // Parse the arguments.
+ args = args.split(/\s*[ ]+\s*/g);
+ if (args.length > 1 && args[0].length > 0 && args[1].length > 0) {
+ from = parseInt(args[0]) - 1;
+ lines = parseInt(args[1]);
+ } else if (args.length > 0 && args[0].length > 0) {
+ from = parseInt(args[0]) - 1;
+ }
+
+ if (from < 0) from = 0;
+ if (lines < 0) lines = 10;
+
+ // Request source arround current source location.
+ request.arguments = {};
+ request.arguments.fromLine = from;
+ request.arguments.toLine = from + lines;
+
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the scripts command.
+DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) {
+ // Build a evaluate request from the text command.
+ var request = this.createRequest('scripts');
+
+ // Process arguments if any.
+ if (args && args.length > 0) {
+ args = args.split(/\s*[ ]+\s*/g);
+
+ if (args.length > 1) {
+ throw new Error('Invalid scripts arguments.');
+ }
+
+ request.arguments = {};
+ switch (args[0]) {
+ case 'natives':
+ request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native);
+ break;
+
+ case 'extensions':
+ request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension);
+ break;
+
+ case 'all':
+ request.arguments.types =
+ ScriptTypeFlag(Debug.ScriptType.Normal) |
+ ScriptTypeFlag(Debug.ScriptType.Native) |
+ ScriptTypeFlag(Debug.ScriptType.Extension);
+ break;
+
+ default:
+ // If the arg is not one of the know one aboves, then it must be a
+ // filter used for filtering the results:
+ request.arguments.filter = args[0];
+ break;
+ }
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the break command.
+DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) {
+ // Build a evaluate request from the text command.
+ // Process arguments if any.
+ if (args && args.length > 0) {
+ var target = args;
+ var type = 'function';
+ var line;
+ var column;
+ var condition;
+ var pos;
+
+ var request = this.createRequest('setbreakpoint');
+
+ // Break the args into target spec and condition if appropriate.
+
+ // Check for breakpoint condition.
+ pos = args.indexOf(' ');
+ if (pos > 0) {
+ target = args.substring(0, pos);
+ condition = args.substring(pos + 1, args.length);
+ }
+
+ // Check for script breakpoint (name:line[:column]). If no ':' in break
+ // specification it is considered a function break point.
+ pos = target.indexOf(':');
+ if (pos > 0) {
+ var tmp = target.substring(pos + 1, target.length);
+ target = target.substring(0, pos);
+ if (target[0] == '/' && target[target.length - 1] == '/') {
+ type = 'scriptRegExp';
+ target = target.substring(1, target.length - 1);
+ } else {
+ type = 'script';
+ }
+
+ // Check for both line and column.
+ pos = tmp.indexOf(':');
+ if (pos > 0) {
+ column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1;
+ line = parseInt(tmp.substring(0, pos)) - 1;
+ } else {
+ line = parseInt(tmp) - 1;
+ }
+ } else if (target[0] == '#' && target[target.length - 1] == '#') {
+ type = 'handle';
+ target = target.substring(1, target.length - 1);
+ } else {
+ type = 'function';
+ }
+
+ request.arguments = {};
+ request.arguments.type = type;
+ request.arguments.target = target;
+ request.arguments.line = line;
+ request.arguments.column = column;
+ request.arguments.condition = condition;
+ } else {
+ var request = this.createRequest('suspend');
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+DebugRequest.prototype.breakpointsCommandToJSONRequest_ = function(args) {
+ if (args && args.length > 0) {
+ throw new Error('Unexpected arguments.');
+ }
+ var request = this.createRequest('listbreakpoints');
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the clear command.
+DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) {
+ // Build a evaluate request from the text command.
+ var request = this.createRequest('clearbreakpoint');
+
+ // Process arguments if any.
+ if (args && args.length > 0) {
+ request.arguments = {};
+ request.arguments.breakpoint = parseInt(args);
+ } else {
+ throw new Error('Invalid break arguments.');
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the change breakpoint command.
+DebugRequest.prototype.changeBreakpointCommandToJSONRequest_ =
+ function(args, command) {
+
+ var request;
+
+ // Check for exception breaks first:
+ // en[able] exc[eptions] [all|unc[aught]]
+ // en[able] [all|unc[aught]] exc[eptions]
+ // dis[able] exc[eptions] [all|unc[aught]]
+ // dis[able] [all|unc[aught]] exc[eptions]
+ if ((command == 'enable' || command == 'disable') &&
+ args && args.length > 1) {
+ var nextPos = args.indexOf(' ');
+ var arg1 = (nextPos > 0) ? args.substring(0, nextPos) : args;
+ var excType = null;
+
+ // Check for:
+ // en[able] exc[eptions] [all|unc[aught]]
+ // dis[able] exc[eptions] [all|unc[aught]]
+ if (arg1 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
+
+ var arg2 = (nextPos > 0) ?
+ args.substring(nextPos + 1, args.length) : 'all';
+ if (!arg2) {
+ arg2 = 'all'; // if unspecified, set for all.
+ } else if (arg2 == 'unc') { // check for short cut.
+ arg2 = 'uncaught';
+ }
+ excType = arg2;
+
+ // Check for:
+ // en[able] [all|unc[aught]] exc[eptions]
+ // dis[able] [all|unc[aught]] exc[eptions]
+ } else if (arg1 == 'all' || arg1 == 'unc' || arg1 == 'uncaught') {
+
+ var arg2 = (nextPos > 0) ?
+ args.substring(nextPos + 1, args.length) : null;
+ if (arg2 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') {
+ excType = arg1;
+ if (excType == 'unc') {
+ excType = 'uncaught';
+ }
+ }
+ }
+
+ // If we matched one of the command formats, then excType will be non-null:
+ if (excType) {
+ // Build a evaluate request from the text command.
+ request = this.createRequest('setexceptionbreak');
+
+ request.arguments = {};
+ request.arguments.type = excType;
+ request.arguments.enabled = (command == 'enable');
+
+ return request.toJSONProtocol();
+ }
+ }
+
+ // Build a evaluate request from the text command.
+ request = this.createRequest('changebreakpoint');
+
+ // Process arguments if any.
+ if (args && args.length > 0) {
+ request.arguments = {};
+ var pos = args.indexOf(' ');
+ var breakpointArg = args;
+ var otherArgs;
+ if (pos > 0) {
+ breakpointArg = args.substring(0, pos);
+ otherArgs = args.substring(pos + 1, args.length);
+ }
+
+ request.arguments.breakpoint = parseInt(breakpointArg);
+
+ switch(command) {
+ case 'cond':
+ request.arguments.condition = otherArgs ? otherArgs : null;
+ break;
+ case 'enable':
+ request.arguments.enabled = true;
+ break;
+ case 'disable':
+ request.arguments.enabled = false;
+ break;
+ case 'ignore':
+ request.arguments.ignoreCount = parseInt(otherArgs);
+ break;
+ default:
+ throw new Error('Invalid arguments.');
+ }
+ } else {
+ throw new Error('Invalid arguments.');
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the disconnect command.
+DebugRequest.prototype.disconnectCommandToJSONRequest_ = function(args) {
+ var request;
+ request = this.createRequest('disconnect');
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the info command.
+DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) {
+ var request;
+ if (args && (args == 'break' || args == 'br')) {
+ // Build a evaluate request from the text command.
+ request = this.createRequest('listbreakpoints');
+ last_cmd = 'info break';
+ } else if (args && (args == 'locals' || args == 'lo')) {
+ // Build a evaluate request from the text command.
+ request = this.createRequest('frame');
+ last_cmd = 'info locals';
+ } else if (args && (args == 'args' || args == 'ar')) {
+ // Build a evaluate request from the text command.
+ request = this.createRequest('frame');
+ last_cmd = 'info args';
+ } else {
+ throw new Error('Invalid info arguments.');
+ }
+
+ return request.toJSONProtocol();
+};
+
+
+DebugRequest.prototype.v8FlagsToJSONRequest_ = function(args) {
+ var request;
+ request = this.createRequest('v8flags');
+ request.arguments = {};
+ request.arguments.flags = args;
+ return request.toJSONProtocol();
+};
+
+
+DebugRequest.prototype.gcToJSONRequest_ = function(args) {
+ var request;
+ if (!args) {
+ args = 'all';
+ }
+ var args = args.split(/\s+/g);
+ var cmd = args[0];
+
+ switch(cmd) {
+ case 'all':
+ case 'quick':
+ case 'full':
+ case 'young':
+ case 'old':
+ case 'compact':
+ case 'sweep':
+ case 'scavenge': {
+ if (cmd == 'young') { cmd = 'quick'; }
+ else if (cmd == 'old') { cmd = 'full'; }
+
+ request = this.createRequest('gc');
+ request.arguments = {};
+ request.arguments.type = cmd;
+ break;
+ }
+ // Else fall thru to the default case below to report the error.
+ default:
+ throw new Error('Missing arguments after ' + cmd + '.');
+ }
+ return request.toJSONProtocol();
+};
+
+
+// Create a JSON request for the threads command.
+DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) {
+ // Build a threads request from the text command.
+ var request = this.createRequest('threads');
+ return request.toJSONProtocol();
+};
+
+
+// Handle the trace command.
+DebugRequest.prototype.traceCommand_ = function(args) {
+ // Process arguments.
+ if (args && args.length > 0) {
+ if (args == 'compile') {
+ trace_compile = !trace_compile;
+ print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off'));
+ } else if (args === 'debug json' || args === 'json' || args === 'packets') {
+ trace_debug_json = !trace_debug_json;
+ print('Tracing of debug json packets ' +
+ (trace_debug_json ? 'on' : 'off'));
+ } else {
+ throw new Error('Invalid trace arguments.');
+ }
+ } else {
+ throw new Error('Invalid trace arguments.');
+ }
+};
+
+// Handle the help command.
+DebugRequest.prototype.helpCommand_ = function(args) {
+ // Help os quite simple.
+ if (args && args.length > 0) {
+ print('warning: arguments to \'help\' are ignored');
+ }
+
+ print('Note: <> denotes symbollic values to be replaced with real values.');
+ print('Note: [] denotes optional parts of commands, or optional options / arguments.');
+ print(' e.g. d[elete] - you get the same command if you type d or delete.');
+ print('');
+ print('[break] - break as soon as possible');
+ print('b[reak] location [condition]');
+ print(' - break on named function: location is a function name');
+ print(' - break on function: location is #<id>#');
+ print(' - break on script position: location is name:line[:column]');
+ print('');
+ print('clear <breakpoint #> - deletes the specified user defined breakpoint');
+ print('d[elete] <breakpoint #> - deletes the specified user defined breakpoint');
+ print('dis[able] <breakpoint #> - disables the specified user defined breakpoint');
+ print('dis[able] exc[eptions] [[all] | unc[aught]]');
+ print(' - disables breaking on exceptions');
+ print('en[able] <breakpoint #> - enables the specified user defined breakpoint');
+ print('en[able] exc[eptions] [[all] | unc[aught]]');
+ print(' - enables breaking on exceptions');
+ print('');
+ print('b[ack]t[race] [n] | [-n] | [from to]');
+ print(' - prints the stack back trace');
+ print('f[rame] - prints info about the current frame context');
+ print('f[rame] <frame #> - set context to specified frame #');
+ print('scopes');
+ print('scope <scope #>');
+ print('');
+ print('up - set context to caller of current frame');
+ print('do[wn] - set context to callee of current frame');
+ print('inf[o] br[eak] - prints info about breakpoints in use');
+ print('inf[o] ar[gs] - prints info about arguments of the current function');
+ print('inf[o] lo[cals] - prints info about locals in the current function');
+ print('');
+ print('step [in | next | out| min [step count]]');
+ print('c[ontinue] - continue executing after a breakpoint');
+ print('s[tep] [<N>] - step into the next N callees (default N is 1)');
+ print('s[tep]i [<N>] - step into the next N callees (default N is 1)');
+ print('n[ext] [<N>] - step over the next N callees (default N is 1)');
+ print('fin[ish] [<N>] - step out of N frames (default N is 1)');
+ print('');
+ print('p[rint] <expression> - prints the result of the specified expression');
+ print('dir <expression> - prints the object structure of the result');
+ print('set <var> = <expression> - executes the specified statement');
+ print('');
+ print('l[ist] - list the source code around for the current pc');
+ print('l[ist] [- | <start>,<end>] - list the specified range of source code');
+ print('source [from line [num lines]]');
+ print('scr[ipts] [native|extensions|all]');
+ print('scr[ipts] [<filter text>] - list scripts with the specified text in its description');
+ print('');
+ print('gc - runs the garbage collector');
+ print('');
+ print('trace compile');
+ // hidden command: trace debug json - toggles tracing of debug json packets
+ print('');
+ print('disconnect|exit|quit - disconnects and quits the debugger');
+ print('help - prints this help information');
+};
+
+
+function formatHandleReference_(value) {
+ if (value.handle() >= 0) {
+ return '#' + value.handle() + '#';
+ } else {
+ return '#Transient#';
+ }
+}
+
+
+function formatObject_(value, include_properties) {
+ var result = '';
+ result += formatHandleReference_(value);
+ result += ', type: object';
+ result += ', constructor ';
+ var ctor = value.constructorFunctionValue();
+ result += formatHandleReference_(ctor);
+ result += ', __proto__ ';
+ var proto = value.protoObjectValue();
+ result += formatHandleReference_(proto);
+ result += ', ';
+ result += value.propertyCount();
+ result += ' properties.';
+ if (include_properties) {
+ result += '\n';
+ for (var i = 0; i < value.propertyCount(); i++) {
+ result += ' ';
+ result += value.propertyName(i);
+ result += ': ';
+ var property_value = value.propertyValue(i);
+ if (property_value instanceof ProtocolReference) {
+ result += '<no type>';
+ } else {
+ if (property_value && property_value.type()) {
+ result += property_value.type();
+ } else {
+ result += '<no type>';
+ }
+ }
+ result += ' ';
+ result += formatHandleReference_(property_value);
+ result += '\n';
+ }
+ }
+ return result;
+}
+
+
+function formatScope_(scope) {
+ var result = '';
+ var index = scope.index;
+ result += '#' + (index <= 9 ? '0' : '') + index;
+ result += ' ';
+ switch (scope.type) {
+ case Debug.ScopeType.Global:
+ result += 'Global, ';
+ result += '#' + scope.object.ref + '#';
+ break;
+ case Debug.ScopeType.Local:
+ result += 'Local';
+ break;
+ case Debug.ScopeType.With:
+ result += 'With, ';
+ result += '#' + scope.object.ref + '#';
+ break;
+ case Debug.ScopeType.Catch:
+ result += 'Catch, ';
+ result += '#' + scope.object.ref + '#';
+ break;
+ case Debug.ScopeType.Closure:
+ result += 'Closure';
+ break;
+ default:
+ result += 'UNKNOWN';
+ }
+ return result;
+}
+
+
+function refObjectToString_(protocolPackage, handle) {
+ var value = protocolPackage.lookup(handle);
+ var result = '';
+ if (value.isString()) {
+ result = '"' + value.value() + '"';
+ } else if (value.isPrimitive()) {
+ result = value.valueString();
+ } else if (value.isObject()) {
+ result += formatObject_(value, true);
+ }
+ return result;
+}
+
+
+// Rounds number 'num' to 'length' decimal places.
+function roundNumber(num, length) {
+ var factor = Math.pow(10, length);
+ return Math.round(num * factor) / factor;
+}
+
+
+// Convert a JSON response to text for display in a text based debugger.
+function DebugResponseDetails(response) {
+ var details = { text: '', running: false };
+
+ try {
+ if (!response.success()) {
+ details.text = response.message();
+ return details;
+ }
+
+ // Get the running state.
+ details.running = response.running();
+
+ var body = response.body();
+ var result = '';
+ switch (response.command()) {
+ case 'suspend':
+ details.text = 'stopped';
+ break;
+
+ case 'setbreakpoint':
+ result = 'set breakpoint #';
+ result += body.breakpoint;
+ details.text = result;
+ break;
+
+ case 'clearbreakpoint':
+ result = 'cleared breakpoint #';
+ result += body.breakpoint;
+ details.text = result;
+ break;
+
+ case 'changebreakpoint':
+ result = 'successfully changed breakpoint';
+ details.text = result;
+ break;
+
+ case 'listbreakpoints':
+ result = 'breakpoints: (' + body.breakpoints.length + ')';
+ for (var i = 0; i < body.breakpoints.length; i++) {
+ var breakpoint = body.breakpoints[i];
+ result += '\n id=' + breakpoint.number;
+ result += ' type=' + breakpoint.type;
+ if (breakpoint.script_id) {
+ result += ' script_id=' + breakpoint.script_id;
+ }
+ if (breakpoint.script_name) {
+ result += ' script_name=' + breakpoint.script_name;
+ }
+ if (breakpoint.script_regexp) {
+ result += ' script_regexp=' + breakpoint.script_regexp;
+ }
+ result += ' line=' + (breakpoint.line + 1);
+ if (breakpoint.column != null) {
+ result += ' column=' + (breakpoint.column + 1);
+ }
+ if (breakpoint.groupId) {
+ result += ' groupId=' + breakpoint.groupId;
+ }
+ if (breakpoint.ignoreCount) {
+ result += ' ignoreCount=' + breakpoint.ignoreCount;
+ }
+ if (breakpoint.active === false) {
+ result += ' inactive';
+ }
+ if (breakpoint.condition) {
+ result += ' condition=' + breakpoint.condition;
+ }
+ result += ' hit_count=' + breakpoint.hit_count;
+ }
+ if (body.breakpoints.length === 0) {
+ result = "No user defined breakpoints\n";
+ } else {
+ result += '\n';
+ }
+ if (body.breakOnExceptions) {
+ result += '* breaking on ALL exceptions is enabled\n';
+ } else if (body.breakOnUncaughtExceptions) {
+ result += '* breaking on UNCAUGHT exceptions is enabled\n';
+ } else {
+ result += '* all exception breakpoints are disabled\n';
+ }
+ details.text = result;
+ break;
+
+ case 'setexceptionbreak':
+ result = 'Break on ' + body.type + ' exceptions: ';
+ result += body.enabled ? 'enabled' : 'disabled';
+ details.text = result;
+ break;
+
+ case 'backtrace':
+ if (body.totalFrames == 0) {
+ result = '(empty stack)';
+ } else {
+ var result = 'Frames #' + body.fromFrame + ' to #' +
+ (body.toFrame - 1) + ' of ' + body.totalFrames + '\n';
+ for (i = 0; i < body.frames.length; i++) {
+ if (i != 0) result += '\n';
+ result += body.frames[i].text;
+ }
+ }
+ details.text = result;
+ break;
+
+ case 'frame':
+ if (last_cmd === 'info locals') {
+ var locals = body.locals;
+ if (locals.length === 0) {
+ result = 'No locals';
+ } else {
+ for (var i = 0; i < locals.length; i++) {
+ var local = locals[i];
+ result += local.name + ' = ';
+ result += refObjectToString_(response, local.value.ref);
+ result += '\n';
+ }
+ }
+ } else if (last_cmd === 'info args') {
+ var args = body.arguments;
+ if (args.length === 0) {
+ result = 'No arguments';
+ } else {
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+ result += arg.name + ' = ';
+ result += refObjectToString_(response, arg.value.ref);
+ result += '\n';
+ }
+ }
+ } else {
+ result = SourceUnderline(body.sourceLineText,
+ body.column);
+ Debug.State.currentSourceLine = body.line;
+ Debug.State.currentFrame = body.index;
+ Debug.State.displaySourceStartLine = -1;
+ Debug.State.displaySourceEndLine = -1;
+ }
+ details.text = result;
+ break;
+
+ case 'scopes':
+ if (body.totalScopes == 0) {
+ result = '(no scopes)';
+ } else {
+ result = 'Scopes #' + body.fromScope + ' to #' +
+ (body.toScope - 1) + ' of ' + body.totalScopes + '\n';
+ for (i = 0; i < body.scopes.length; i++) {
+ if (i != 0) {
+ result += '\n';
+ }
+ result += formatScope_(body.scopes[i]);
+ }
+ }
+ details.text = result;
+ break;
+
+ case 'scope':
+ result += formatScope_(body);
+ result += '\n';
+ var scope_object_value = response.lookup(body.object.ref);
+ result += formatObject_(scope_object_value, true);
+ details.text = result;
+ break;
+
+ case 'evaluate':
+ case 'lookup':
+ case 'getobj':
+ if (last_cmd == 'p' || last_cmd == 'print') {
+ result = body.text;
+ } else {
+ var value;
+ if (lookup_handle) {
+ value = response.bodyValue(lookup_handle);
+ } else {
+ value = response.bodyValue();
+ }
+ if (value.isObject()) {
+ result += formatObject_(value, true);
+ } else {
+ result += 'type: ';
+ result += value.type();
+ if (!value.isUndefined() && !value.isNull()) {
+ result += ', ';
+ if (value.isString()) {
+ result += '"';
+ }
+ result += value.value();
+ if (value.isString()) {
+ result += '"';
+ }
+ }
+ result += '\n';
+ }
+ }
+ details.text = result;
+ break;
+
+ case 'references':
+ var count = body.length;
+ result += 'found ' + count + ' objects';
+ result += '\n';
+ for (var i = 0; i < count; i++) {
+ var value = response.bodyValue(i);
+ result += formatObject_(value, false);
+ result += '\n';
+ }
+ details.text = result;
+ break;
+
+ case 'source':
+ // Get the source from the response.
+ var source = body.source;
+ var from_line = body.fromLine + 1;
+ var lines = source.split('\n');
+ var maxdigits = 1 + Math.floor(log10(from_line + lines.length));
+ if (maxdigits < 3) {
+ maxdigits = 3;
+ }
+ var result = '';
+ for (var num = 0; num < lines.length; num++) {
+ // Check if there's an extra newline at the end.
+ if (num == (lines.length - 1) && lines[num].length == 0) {
+ break;
+ }
+
+ var current_line = from_line + num;
+ var spacer = maxdigits - (1 + Math.floor(log10(current_line)));
+ if (current_line == Debug.State.currentSourceLine + 1) {
+ for (var i = 0; i < maxdigits; i++) {
+ result += '>';
+ }
+ result += ' ';
+ } else {
+ for (var i = 0; i < spacer; i++) {
+ result += ' ';
+ }
+ result += current_line + ': ';
+ }
+ result += lines[num];
+ result += '\n';
+ }
+ details.text = result;
+ break;
+
+ case 'scripts':
+ var result = '';
+ for (i = 0; i < body.length; i++) {
+ if (i != 0) result += '\n';
+ if (body[i].id) {
+ result += body[i].id;
+ } else {
+ result += '[no id]';
+ }
+ result += ', ';
+ if (body[i].name) {
+ result += body[i].name;
+ } else {
+ if (body[i].compilationType == Debug.ScriptCompilationType.Eval
+ && body[i].evalFromScript
+ ) {
+ result += 'eval from ';
+ var script_value = response.lookup(body[i].evalFromScript.ref);
+ result += ' ' + script_value.field('name');
+ result += ':' + (body[i].evalFromLocation.line + 1);
+ result += ':' + body[i].evalFromLocation.column;
+ } else if (body[i].compilationType ==
+ Debug.ScriptCompilationType.JSON) {
+ result += 'JSON ';
+ } else { // body[i].compilation == Debug.ScriptCompilationType.Host
+ result += '[unnamed] ';
+ }
+ }
+ result += ' (lines: ';
+ result += body[i].lineCount;
+ result += ', length: ';
+ result += body[i].sourceLength;
+ if (body[i].type == Debug.ScriptType.Native) {
+ result += ', native';
+ } else if (body[i].type == Debug.ScriptType.Extension) {
+ result += ', extension';
+ }
+ result += '), [';
+ var sourceStart = body[i].sourceStart;
+ if (sourceStart.length > 40) {
+ sourceStart = sourceStart.substring(0, 37) + '...';
+ }
+ result += sourceStart;
+ result += ']';
+ }
+ if (body.length == 0) {
+ result = "no matching scripts found";
+ }
+ details.text = result;
+ break;
+
+ case 'threads':
+ var result = 'Active V8 threads: ' + body.totalThreads + '\n';
+ body.threads.sort(function(a, b) { return a.id - b.id; });
+ for (i = 0; i < body.threads.length; i++) {
+ result += body.threads[i].current ? '*' : ' ';
+ result += ' ';
+ result += body.threads[i].id;
+ result += '\n';
+ }
+ details.text = result;
+ break;
+
+ case 'continue':
+ details.text = "(running)";
+ break;
+
+ case 'v8flags':
+ details.text = "flags set";
+ break;
+
+ case 'gc':
+ details.text = "GC " + body.before + " => " + body.after;
+ if (body.after > (1024*1024)) {
+ details.text +=
+ " (" + roundNumber(body.before/(1024*1024), 1) + "M => " +
+ roundNumber(body.after/(1024*1024), 1) + "M)";
+ } else if (body.after > 1024) {
+ details.text +=
+ " (" + roundNumber(body.before/1024, 1) + "K => " +
+ roundNumber(body.after/1024, 1) + "K)";
+ }
+ break;
+
+ default:
+ details.text =
+ 'Response for unknown command \'' + response.command() + '\'' +
+ ' (' + response.raw_json() + ')';
+ }
+ } catch (e) {
+ details.text = 'Error: "' + e + '" formatting response';
+ }
+
+ return details;
+}
+
+
+/**
+ * Protocol packages send from the debugger.
+ * @param {string} json - raw protocol packet as JSON string.
+ * @constructor
+ */
+function ProtocolPackage(json) {
+ this.raw_json_ = json;
+ this.packet_ = JSON.parse(json);
+ this.refs_ = [];
+ if (this.packet_.refs) {
+ for (var i = 0; i < this.packet_.refs.length; i++) {
+ this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i];
+ }
+ }
+}
+
+
+/**
+ * Get the packet type.
+ * @return {String} the packet type
+ */
+ProtocolPackage.prototype.type = function() {
+ return this.packet_.type;
+};
+
+
+/**
+ * Get the packet event.
+ * @return {Object} the packet event
+ */
+ProtocolPackage.prototype.event = function() {
+ return this.packet_.event;
+};
+
+
+/**
+ * Get the packet request sequence.
+ * @return {number} the packet request sequence
+ */
+ProtocolPackage.prototype.requestSeq = function() {
+ return this.packet_.request_seq;
+};
+
+
+/**
+ * Get the packet request sequence.
+ * @return {number} the packet request sequence
+ */
+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) {
+ if (index != null) {
+ return new ProtocolValue(this.packet_.body[index], this);
+ } else {
+ return new ProtocolValue(this.packet_.body, this);
+ }
+};
+
+
+ProtocolPackage.prototype.body = function() {
+ return this.packet_.body;
+};
+
+
+ProtocolPackage.prototype.lookup = function(handle) {
+ var value = this.refs_[handle];
+ if (value) {
+ return new ProtocolValue(value, this);
+ } else {
+ return new ProtocolReference(handle);
+ }
+};
+
+
+ProtocolPackage.prototype.raw_json = function() {
+ return this.raw_json_;
+};
+
+
+function ProtocolValue(value, packet) {
+ this.value_ = value;
+ this.packet_ = packet;
+}
+
+
+/**
+ * Get the value type.
+ * @return {String} the value type
+ */
+ProtocolValue.prototype.type = function() {
+ return this.value_.type;
+};
+
+
+/**
+ * Get a metadata field from a protocol value.
+ * @return {Object} the metadata field value
+ */
+ProtocolValue.prototype.field = function(name) {
+ return this.value_[name];
+};
+
+
+/**
+ * Check is the value is a primitive value.
+ * @return {boolean} true if the value is primitive
+ */
+ProtocolValue.prototype.isPrimitive = function() {
+ return this.isUndefined() || this.isNull() || this.isBoolean() ||
+ this.isNumber() || this.isString();
+};
+
+
+/**
+ * Get the object handle.
+ * @return {number} the value handle
+ */
+ProtocolValue.prototype.handle = function() {
+ return this.value_.handle;
+};
+
+
+/**
+ * Check is the value is undefined.
+ * @return {boolean} true if the value is undefined
+ */
+ProtocolValue.prototype.isUndefined = function() {
+ return this.value_.type == 'undefined';
+};
+
+
+/**
+ * Check is the value is null.
+ * @return {boolean} true if the value is null
+ */
+ProtocolValue.prototype.isNull = function() {
+ return this.value_.type == 'null';
+};
+
+
+/**
+ * Check is the value is a boolean.
+ * @return {boolean} true if the value is a boolean
+ */
+ProtocolValue.prototype.isBoolean = function() {
+ return this.value_.type == 'boolean';
+};
+
+
+/**
+ * Check is the value is a number.
+ * @return {boolean} true if the value is a number
+ */
+ProtocolValue.prototype.isNumber = function() {
+ return this.value_.type == 'number';
+};
+
+
+/**
+ * Check is the value is a string.
+ * @return {boolean} true if the value is a string
+ */
+ProtocolValue.prototype.isString = function() {
+ return this.value_.type == 'string';
+};
+
+
+/**
+ * Check is the value is an object.
+ * @return {boolean} true if the value is an object
+ */
+ProtocolValue.prototype.isObject = function() {
+ return this.value_.type == 'object' || this.value_.type == 'function' ||
+ this.value_.type == 'error' || this.value_.type == 'regexp';
+};
+
+
+/**
+ * Get the constructor function
+ * @return {ProtocolValue} constructor function
+ */
+ProtocolValue.prototype.constructorFunctionValue = function() {
+ var ctor = this.value_.constructorFunction;
+ return this.packet_.lookup(ctor.ref);
+};
+
+
+/**
+ * Get the __proto__ value
+ * @return {ProtocolValue} __proto__ value
+ */
+ProtocolValue.prototype.protoObjectValue = function() {
+ var proto = this.value_.protoObject;
+ return this.packet_.lookup(proto.ref);
+};
+
+
+/**
+ * Get the number og properties.
+ * @return {number} the number of properties
+ */
+ProtocolValue.prototype.propertyCount = function() {
+ return this.value_.properties ? this.value_.properties.length : 0;
+};
+
+
+/**
+ * Get the specified property name.
+ * @return {string} property name
+ */
+ProtocolValue.prototype.propertyName = function(index) {
+ var property = this.value_.properties[index];
+ return property.name;
+};
+
+
+/**
+ * Return index for the property name.
+ * @param name The property name to look for
+ * @return {number} index for the property name
+ */
+ProtocolValue.prototype.propertyIndex = function(name) {
+ for (var i = 0; i < this.propertyCount(); i++) {
+ if (this.value_.properties[i].name == name) {
+ return i;
+ }
+ }
+ return null;
+};
+
+
+/**
+ * Get the specified property value.
+ * @return {ProtocolValue} property value
+ */
+ProtocolValue.prototype.propertyValue = function(index) {
+ var property = this.value_.properties[index];
+ return this.packet_.lookup(property.ref);
+};
+
+
+/**
+ * Check is the value is a string.
+ * @return {boolean} true if the value is a string
+ */
+ProtocolValue.prototype.value = function() {
+ return this.value_.value;
+};
+
+
+ProtocolValue.prototype.valueString = function() {
+ return this.value_.text;
+};
+
+
+function ProtocolReference(handle) {
+ this.handle_ = handle;
+}
+
+
+ProtocolReference.prototype.handle = function() {
+ return this.handle_;
+};
+
+
+function MakeJSONPair_(name, value) {
+ return '"' + name + '":' + value;
+}
+
+
+function ArrayToJSONObject_(content) {
+ return '{' + content.join(',') + '}';
+}
+
+
+function ArrayToJSONArray_(content) {
+ return '[' + content.join(',') + ']';
+}
+
+
+function BooleanToJSON_(value) {
+ return String(value);
+}
+
+
+function NumberToJSON_(value) {
+ return String(value);
+}
+
+
+// Mapping of some control characters to avoid the \uXXXX syntax for most
+// commonly used control cahracters.
+var ctrlCharMap_ = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+};
+
+
+// Regular expression testing for ", \ and control characters (0x00 - 0x1F).
+var ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]');
+
+
+// Regular expression matching ", \ and control characters (0x00 - 0x1F)
+// globally.
+var ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g');
+
+
+/**
+ * Convert a String to its JSON representation (see http://www.json.org/). To
+ * avoid depending on the String object this method calls the functions in
+ * string.js directly and not through the value.
+ * @param {String} value The String value to format as JSON
+ * @return {string} JSON formatted String value
+ */
+function StringToJSON_(value) {
+ // Check for" , \ and control characters (0x00 - 0x1F). No need to call
+ // RegExpTest as ctrlchar is constructed using RegExp.
+ if (ctrlCharTest_.test(value)) {
+ // Replace ", \ and control characters (0x00 - 0x1F).
+ return '"' +
+ value.replace(ctrlCharMatch_, function (char) {
+ // Use charmap if possible.
+ var mapped = ctrlCharMap_[char];
+ if (mapped) return mapped;
+ mapped = char.charCodeAt();
+ // Convert control character to unicode escape sequence.
+ return '\\u00' +
+ '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) +
+ '0'; // TODO %NumberToRadixString(mapped % 16, 16)
+ })
+ + '"';
+ }
+
+ // Simple string with no special characters.
+ return '"' + value + '"';
+}
+
+
+/**
+ * Convert a Date to ISO 8601 format. To avoid depending on the Date object
+ * this method calls the functions in date.js directly and not through the
+ * value.
+ * @param {Date} value The Date value to format as JSON
+ * @return {string} JSON formatted Date value
+ */
+function DateToISO8601_(value) {
+ var f = function(n) {
+ return n < 10 ? '0' + n : n;
+ };
+ var g = function(n) {
+ return n < 10 ? '00' + n : n < 100 ? '0' + n : n;
+ };
+ return builtins.GetUTCFullYearFrom(value) + '-' +
+ f(builtins.GetUTCMonthFrom(value) + 1) + '-' +
+ f(builtins.GetUTCDateFrom(value)) + 'T' +
+ f(builtins.GetUTCHoursFrom(value)) + ':' +
+ f(builtins.GetUTCMinutesFrom(value)) + ':' +
+ f(builtins.GetUTCSecondsFrom(value)) + '.' +
+ g(builtins.GetUTCMillisecondsFrom(value)) + 'Z';
+}
+
+
+/**
+ * Convert a Date to ISO 8601 format. To avoid depending on the Date object
+ * this method calls the functions in date.js directly and not through the
+ * value.
+ * @param {Date} value The Date value to format as JSON
+ * @return {string} JSON formatted Date value
+ */
+function DateToJSON_(value) {
+ return '"' + DateToISO8601_(value) + '"';
+}
+
+
+/**
+ * Convert an Object to its JSON representation (see http://www.json.org/).
+ * This implementation simply runs through all string property names and adds
+ * each property to the JSON representation for some predefined types. For type
+ * "object" the function calls itself recursively unless the object has the
+ * function property "toJSONProtocol" in which case that is used. This is not
+ * a general implementation but sufficient for the debugger. Note that circular
+ * structures will cause infinite recursion.
+ * @param {Object} object The object to format as JSON
+ * @return {string} JSON formatted object value
+ */
+function SimpleObjectToJSON_(object) {
+ var content = [];
+ for (var key in object) {
+ // Only consider string keys.
+ if (typeof key == 'string') {
+ var property_value = object[key];
+
+ // Format the value based on its type.
+ var property_value_json;
+ switch (typeof property_value) {
+ case 'object':
+ if (property_value === null) {
+ property_value_json = 'null';
+ } else if (typeof property_value.toJSONProtocol == 'function') {
+ property_value_json = property_value.toJSONProtocol(true);
+ } else if (property_value.constructor.name == 'Array'){
+ property_value_json = SimpleArrayToJSON_(property_value);
+ } else {
+ property_value_json = SimpleObjectToJSON_(property_value);
+ }
+ break;
+
+ case 'boolean':
+ property_value_json = BooleanToJSON_(property_value);
+ break;
+
+ case 'number':
+ property_value_json = NumberToJSON_(property_value);
+ break;
+
+ case 'string':
+ property_value_json = StringToJSON_(property_value);
+ break;
+
+ default:
+ property_value_json = null;
+ }
+
+ // Add the property if relevant.
+ if (property_value_json) {
+ content.push(StringToJSON_(key) + ':' + property_value_json);
+ }
+ }
+ }
+
+ // Make JSON object representation.
+ return '{' + content.join(',') + '}';
+}
+
+
+/**
+ * Convert an array to its JSON representation. This is a VERY simple
+ * implementation just to support what is needed for the debugger.
+ * @param {Array} arrya The array to format as JSON
+ * @return {string} JSON formatted array value
+ */
+function SimpleArrayToJSON_(array) {
+ // Make JSON array representation.
+ var json = '[';
+ for (var i = 0; i < array.length; i++) {
+ if (i != 0) {
+ json += ',';
+ }
+ var elem = array[i];
+ if (elem.toJSONProtocol) {
+ json += elem.toJSONProtocol(true);
+ } else if (typeof(elem) === 'object') {
+ json += SimpleObjectToJSON_(elem);
+ } else if (typeof(elem) === 'boolean') {
+ json += BooleanToJSON_(elem);
+ } else if (typeof(elem) === 'number') {
+ json += NumberToJSON_(elem);
+ } else if (typeof(elem) === 'string') {
+ json += StringToJSON_(elem);
+ } else {
+ json += elem;
+ }
+ }
+ json += ']';
+ return json;
+}
+
+
+// A more universal stringify that supports more types than JSON.
+// Used by the d8 shell to output results.
+var stringifyDepthLimit = 4; // To avoid crashing on cyclic objects
+
+function Stringify(x, depth) {
+ if (depth === undefined)
+ depth = stringifyDepthLimit;
+ else if (depth === 0)
+ return "*";
+ switch (typeof x) {
+ case "undefined":
+ return "undefined";
+ case "boolean":
+ case "number":
+ case "function":
+ return x.toString();
+ case "string":
+ return "\"" + x.toString() + "\"";
+ case "symbol":
+ return "Symbol(" + (x.name ? Stringify(x.name, depth) : "") + ")"
+ case "object":
+ if (x === null) return "null";
+ if (x.constructor && x.constructor.name === "Array") {
+ var elems = [];
+ for (var i = 0; i < x.length; ++i) {
+ elems.push(
+ {}.hasOwnProperty.call(x, i) ? Stringify(x[i], depth - 1) : "");
+ }
+ return "[" + elems.join(", ") + "]";
+ }
+ try {
+ var string = String(x);
+ if (string && string !== "[object Object]") return string;
+ } catch(e) {}
+ var props = [];
+ for (var name in x) {
+ var desc = Object.getOwnPropertyDescriptor(x, name);
+ if (desc === void 0) continue;
+ if ("value" in desc) {
+ props.push(name + ": " + Stringify(desc.value, depth - 1));
+ }
+ if ("get" in desc) {
+ var getter = desc.get.toString();
+ props.push("get " + name + getter.slice(getter.indexOf('(')));
+ }
+ if ("set" in desc) {
+ var setter = desc.set.toString();
+ props.push("set " + name + setter.slice(setter.indexOf('(')));
+ }
+ }
+ return "{" + props.join(", ") + "}";
+ default:
+ return "[crazy non-standard shit]";
+ }
+}
diff --git a/chromium/v8/src/data-flow.cc b/chromium/v8/src/data-flow.cc
new file mode 100644
index 00000000000..6a3b05cc86d
--- /dev/null
+++ b/chromium/v8/src/data-flow.cc
@@ -0,0 +1,66 @@
+// 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.
+
+#include "v8.h"
+
+#include "data-flow.h"
+#include "scopes.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef DEBUG
+void BitVector::Print() {
+ bool first = true;
+ PrintF("{");
+ for (int i = 0; i < length(); i++) {
+ if (Contains(i)) {
+ if (!first) PrintF(",");
+ first = false;
+ PrintF("%d", i);
+ }
+ }
+ PrintF("}");
+}
+#endif
+
+
+void BitVector::Iterator::Advance() {
+ current_++;
+ uint32_t val = current_value_;
+ while (val == 0) {
+ current_index_++;
+ if (Done()) return;
+ val = target_->data_[current_index_];
+ current_ = current_index_ << 5;
+ }
+ val = SkipZeroBytes(val);
+ val = SkipZeroBits(val);
+ current_value_ = val >> 1;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/data-flow.h b/chromium/v8/src/data-flow.h
new file mode 100644
index 00000000000..8ceccf67c54
--- /dev/null
+++ b/chromium/v8/src/data-flow.h
@@ -0,0 +1,262 @@
+// Copyright 2012 the V8 project authors. 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_DATAFLOW_H_
+#define V8_DATAFLOW_H_
+
+#include "v8.h"
+
+#include "allocation.h"
+#include "ast.h"
+#include "compiler.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+class BitVector: public ZoneObject {
+ public:
+ // Iterator for the elements of this BitVector.
+ class Iterator BASE_EMBEDDED {
+ public:
+ explicit Iterator(BitVector* target)
+ : target_(target),
+ current_index_(0),
+ current_value_(target->data_[0]),
+ current_(-1) {
+ ASSERT(target->data_length_ > 0);
+ Advance();
+ }
+ ~Iterator() { }
+
+ bool Done() const { return current_index_ >= target_->data_length_; }
+ void Advance();
+
+ int Current() const {
+ ASSERT(!Done());
+ return current_;
+ }
+
+ private:
+ uint32_t SkipZeroBytes(uint32_t val) {
+ while ((val & 0xFF) == 0) {
+ val >>= 8;
+ current_ += 8;
+ }
+ return val;
+ }
+ uint32_t SkipZeroBits(uint32_t val) {
+ while ((val & 0x1) == 0) {
+ val >>= 1;
+ current_++;
+ }
+ return val;
+ }
+
+ BitVector* target_;
+ int current_index_;
+ uint32_t current_value_;
+ int current_;
+
+ friend class BitVector;
+ };
+
+ BitVector(int length, Zone* zone)
+ : length_(length),
+ data_length_(SizeFor(length)),
+ data_(zone->NewArray<uint32_t>(data_length_)) {
+ ASSERT(length > 0);
+ Clear();
+ }
+
+ BitVector(const BitVector& other, Zone* zone)
+ : length_(other.length()),
+ data_length_(SizeFor(length_)),
+ data_(zone->NewArray<uint32_t>(data_length_)) {
+ CopyFrom(other);
+ }
+
+ static int SizeFor(int length) {
+ return 1 + ((length - 1) / 32);
+ }
+
+ BitVector& operator=(const BitVector& rhs) {
+ if (this != &rhs) CopyFrom(rhs);
+ return *this;
+ }
+
+ void CopyFrom(const BitVector& other) {
+ ASSERT(other.length() <= length());
+ for (int i = 0; i < other.data_length_; i++) {
+ data_[i] = other.data_[i];
+ }
+ for (int i = other.data_length_; i < data_length_; i++) {
+ data_[i] = 0;
+ }
+ }
+
+ bool Contains(int i) const {
+ ASSERT(i >= 0 && i < length());
+ uint32_t block = data_[i / 32];
+ return (block & (1U << (i % 32))) != 0;
+ }
+
+ void Add(int i) {
+ ASSERT(i >= 0 && i < length());
+ data_[i / 32] |= (1U << (i % 32));
+ }
+
+ void Remove(int i) {
+ ASSERT(i >= 0 && i < length());
+ data_[i / 32] &= ~(1U << (i % 32));
+ }
+
+ void Union(const BitVector& other) {
+ ASSERT(other.length() == length());
+ for (int i = 0; i < data_length_; i++) {
+ data_[i] |= other.data_[i];
+ }
+ }
+
+ bool UnionIsChanged(const BitVector& other) {
+ ASSERT(other.length() == length());
+ bool changed = false;
+ for (int i = 0; i < data_length_; i++) {
+ uint32_t old_data = data_[i];
+ data_[i] |= other.data_[i];
+ if (data_[i] != old_data) changed = true;
+ }
+ return changed;
+ }
+
+ void Intersect(const BitVector& other) {
+ ASSERT(other.length() == length());
+ for (int i = 0; i < data_length_; i++) {
+ data_[i] &= other.data_[i];
+ }
+ }
+
+ void Subtract(const BitVector& other) {
+ ASSERT(other.length() == length());
+ for (int i = 0; i < data_length_; i++) {
+ data_[i] &= ~other.data_[i];
+ }
+ }
+
+ void Clear() {
+ for (int i = 0; i < data_length_; i++) {
+ data_[i] = 0;
+ }
+ }
+
+ bool IsEmpty() const {
+ for (int i = 0; i < data_length_; i++) {
+ if (data_[i] != 0) return false;
+ }
+ return true;
+ }
+
+ bool Equals(const BitVector& other) {
+ for (int i = 0; i < data_length_; i++) {
+ if (data_[i] != other.data_[i]) return false;
+ }
+ return true;
+ }
+
+ int length() const { return length_; }
+
+#ifdef DEBUG
+ void Print();
+#endif
+
+ private:
+ int length_;
+ int data_length_;
+ uint32_t* data_;
+};
+
+class GrowableBitVector BASE_EMBEDDED {
+ public:
+ class Iterator BASE_EMBEDDED {
+ public:
+ Iterator(const GrowableBitVector* target, Zone* zone)
+ : it_(target->bits_ == NULL
+ ? new(zone) BitVector(1, zone)
+ : target->bits_) { }
+ bool Done() const { return it_.Done(); }
+ void Advance() { it_.Advance(); }
+ int Current() const { return it_.Current(); }
+ private:
+ BitVector::Iterator it_;
+ };
+
+ GrowableBitVector() : bits_(NULL) { }
+ GrowableBitVector(int length, Zone* zone)
+ : bits_(new(zone) BitVector(length, zone)) { }
+
+ bool Contains(int value) const {
+ if (!InBitsRange(value)) return false;
+ return bits_->Contains(value);
+ }
+
+ void Add(int value, Zone* zone) {
+ EnsureCapacity(value, zone);
+ bits_->Add(value);
+ }
+
+ void Union(const GrowableBitVector& other, Zone* zone) {
+ for (Iterator it(&other, zone); !it.Done(); it.Advance()) {
+ Add(it.Current(), zone);
+ }
+ }
+
+ void Clear() { if (bits_ != NULL) bits_->Clear(); }
+
+ private:
+ static const int kInitialLength = 1024;
+
+ bool InBitsRange(int value) const {
+ return bits_ != NULL && bits_->length() > value;
+ }
+
+ void EnsureCapacity(int value, Zone* zone) {
+ if (InBitsRange(value)) return;
+ int new_length = bits_ == NULL ? kInitialLength : bits_->length();
+ while (new_length <= value) new_length *= 2;
+ BitVector* new_bits = new(zone) BitVector(new_length, zone);
+ if (bits_ != NULL) new_bits->CopyFrom(*bits_);
+ bits_ = new_bits;
+ }
+
+ BitVector* bits_;
+};
+
+
+} } // namespace v8::internal
+
+
+#endif // V8_DATAFLOW_H_
diff --git a/chromium/v8/src/date.cc b/chromium/v8/src/date.cc
new file mode 100644
index 00000000000..a377451770f
--- /dev/null
+++ b/chromium/v8/src/date.cc
@@ -0,0 +1,384 @@
+// Copyright 2012 the V8 project authors. 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 "date.h"
+
+#include "v8.h"
+
+#include "objects.h"
+#include "objects-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
+static const int kDaysIn4Years = 4 * 365 + 1;
+static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
+static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
+static const int kDays1970to2000 = 30 * 365 + 7;
+static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
+ kDays1970to2000;
+static const int kYearsOffset = 400000;
+static const char kDaysInMonths[] =
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+
+void DateCache::ResetDateCache() {
+ static const int kMaxStamp = Smi::kMaxValue;
+ stamp_ = Smi::FromInt(stamp_->value() + 1);
+ if (stamp_->value() > kMaxStamp) {
+ stamp_ = Smi::FromInt(0);
+ }
+ ASSERT(stamp_ != Smi::FromInt(kInvalidStamp));
+ for (int i = 0; i < kDSTSize; ++i) {
+ ClearSegment(&dst_[i]);
+ }
+ dst_usage_counter_ = 0;
+ before_ = &dst_[0];
+ after_ = &dst_[1];
+ local_offset_ms_ = kInvalidLocalOffsetInMs;
+ ymd_valid_ = false;
+}
+
+
+void DateCache::ClearSegment(DST* segment) {
+ segment->start_sec = kMaxEpochTimeInSec;
+ segment->end_sec = -kMaxEpochTimeInSec;
+ segment->offset_ms = 0;
+ segment->last_used = 0;
+}
+
+
+void DateCache::YearMonthDayFromDays(
+ int days, int* year, int* month, int* day) {
+ if (ymd_valid_) {
+ // Check conservatively if the given 'days' has
+ // the same year and month as the cached 'days'.
+ int new_day = ymd_day_ + (days - ymd_days_);
+ if (new_day >= 1 && new_day <= 28) {
+ ymd_day_ = new_day;
+ ymd_days_ = days;
+ *year = ymd_year_;
+ *month = ymd_month_;
+ *day = new_day;
+ return;
+ }
+ }
+ int save_days = days;
+
+ days += kDaysOffset;
+ *year = 400 * (days / kDaysIn400Years) - kYearsOffset;
+ days %= kDaysIn400Years;
+
+ ASSERT(DaysFromYearMonth(*year, 0) + days == save_days);
+
+ days--;
+ int yd1 = days / kDaysIn100Years;
+ days %= kDaysIn100Years;
+ *year += 100 * yd1;
+
+ days++;
+ int yd2 = days / kDaysIn4Years;
+ days %= kDaysIn4Years;
+ *year += 4 * yd2;
+
+ days--;
+ int yd3 = days / 365;
+ days %= 365;
+ *year += yd3;
+
+
+ bool is_leap = (!yd1 || yd2) && !yd3;
+
+ ASSERT(days >= -1);
+ ASSERT(is_leap || (days >= 0));
+ ASSERT((days < 365) || (is_leap && (days < 366)));
+ ASSERT(is_leap == ((*year % 4 == 0) && (*year % 100 || (*year % 400 == 0))));
+ ASSERT(is_leap || ((DaysFromYearMonth(*year, 0) + days) == save_days));
+ ASSERT(!is_leap || ((DaysFromYearMonth(*year, 0) + days + 1) == save_days));
+
+ days += is_leap;
+
+ // Check if the date is after February.
+ if (days >= 31 + 28 + is_leap) {
+ days -= 31 + 28 + is_leap;
+ // Find the date starting from March.
+ for (int i = 2; i < 12; i++) {
+ if (days < kDaysInMonths[i]) {
+ *month = i;
+ *day = days + 1;
+ break;
+ }
+ days -= kDaysInMonths[i];
+ }
+ } else {
+ // Check January and February.
+ if (days < 31) {
+ *month = 0;
+ *day = days + 1;
+ } else {
+ *month = 1;
+ *day = days - 31 + 1;
+ }
+ }
+ ASSERT(DaysFromYearMonth(*year, *month) + *day - 1 == save_days);
+ ymd_valid_ = true;
+ ymd_year_ = *year;
+ ymd_month_ = *month;
+ ymd_day_ = *day;
+ ymd_days_ = save_days;
+}
+
+
+int DateCache::DaysFromYearMonth(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,
+ 182, 213, 244, 274, 305, 335};
+
+ year += month / 12;
+ month %= 12;
+ if (month < 0) {
+ year--;
+ month += 12;
+ }
+
+ ASSERT(month >= 0);
+ ASSERT(month < 12);
+
+ // year_delta is an arbitrary number such that:
+ // a) year_delta = -1 (mod 400)
+ // b) year + year_delta > 0 for years in the range defined by
+ // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
+ // Jan 1 1970. This is required so that we don't run into integer
+ // division of negative numbers.
+ // c) there shouldn't be an overflow for 32-bit integers in the following
+ // operations.
+ static const int year_delta = 399999;
+ static const int base_day = 365 * (1970 + year_delta) +
+ (1970 + year_delta) / 4 -
+ (1970 + year_delta) / 100 +
+ (1970 + year_delta) / 400;
+
+ int year1 = year + year_delta;
+ int day_from_year = 365 * year1 +
+ year1 / 4 -
+ year1 / 100 +
+ year1 / 400 -
+ base_day;
+
+ 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];
+}
+
+
+void DateCache::ExtendTheAfterSegment(int time_sec, int offset_ms) {
+ if (after_->offset_ms == offset_ms &&
+ after_->start_sec <= time_sec + kDefaultDSTDeltaInSec &&
+ time_sec <= after_->end_sec) {
+ // Extend the after_ segment.
+ after_->start_sec = time_sec;
+ } else {
+ // The after_ segment is either invalid or starts too late.
+ if (after_->start_sec <= after_->end_sec) {
+ // If the after_ segment is valid, replace it with a new segment.
+ after_ = LeastRecentlyUsedDST(before_);
+ }
+ after_->start_sec = time_sec;
+ after_->end_sec = time_sec;
+ after_->offset_ms = offset_ms;
+ after_->last_used = ++dst_usage_counter_;
+ }
+}
+
+
+int DateCache::DaylightSavingsOffsetInMs(int64_t time_ms) {
+ int time_sec = (time_ms >= 0 && time_ms <= kMaxEpochTimeInMs)
+ ? static_cast<int>(time_ms / 1000)
+ : static_cast<int>(EquivalentTime(time_ms) / 1000);
+
+ // Invalidate cache if the usage counter is close to overflow.
+ // Note that dst_usage_counter is incremented less than ten times
+ // in this function.
+ if (dst_usage_counter_ >= kMaxInt - 10) {
+ dst_usage_counter_ = 0;
+ for (int i = 0; i < kDSTSize; ++i) {
+ ClearSegment(&dst_[i]);
+ }
+ }
+
+ // Optimistic fast check.
+ if (before_->start_sec <= time_sec &&
+ time_sec <= before_->end_sec) {
+ // Cache hit.
+ before_->last_used = ++dst_usage_counter_;
+ return before_->offset_ms;
+ }
+
+ ProbeDST(time_sec);
+
+ ASSERT(InvalidSegment(before_) || before_->start_sec <= time_sec);
+ ASSERT(InvalidSegment(after_) || time_sec < after_->start_sec);
+
+ if (InvalidSegment(before_)) {
+ // Cache miss.
+ before_->start_sec = time_sec;
+ before_->end_sec = time_sec;
+ before_->offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
+ before_->last_used = ++dst_usage_counter_;
+ return before_->offset_ms;
+ }
+
+ if (time_sec <= before_->end_sec) {
+ // Cache hit.
+ before_->last_used = ++dst_usage_counter_;
+ return before_->offset_ms;
+ }
+
+ if (time_sec > before_->end_sec + kDefaultDSTDeltaInSec) {
+ // If the before_ segment ends too early, then just
+ // query for the offset of the time_sec
+ int offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
+ ExtendTheAfterSegment(time_sec, offset_ms);
+ // This swap helps the optimistic fast check in subsequent invocations.
+ DST* temp = before_;
+ before_ = after_;
+ after_ = temp;
+ return offset_ms;
+ }
+
+ // Now the time_sec is between
+ // before_->end_sec and before_->end_sec + default DST delta.
+ // Update the usage counter of before_ since it is going to be used.
+ before_->last_used = ++dst_usage_counter_;
+
+ // Check if after_ segment is invalid or starts too late.
+ // Note that start_sec of invalid segments is kMaxEpochTimeInSec.
+ if (before_->end_sec + kDefaultDSTDeltaInSec <= after_->start_sec) {
+ int new_after_start_sec = before_->end_sec + kDefaultDSTDeltaInSec;
+ int new_offset_ms = GetDaylightSavingsOffsetFromOS(new_after_start_sec);
+ ExtendTheAfterSegment(new_after_start_sec, new_offset_ms);
+ } else {
+ ASSERT(!InvalidSegment(after_));
+ // Update the usage counter of after_ since it is going to be used.
+ after_->last_used = ++dst_usage_counter_;
+ }
+
+ // Now the time_sec is between before_->end_sec and after_->start_sec.
+ // Only one daylight savings offset change can occur in this interval.
+
+ if (before_->offset_ms == after_->offset_ms) {
+ // Merge two segments if they have the same offset.
+ before_->end_sec = after_->end_sec;
+ ClearSegment(after_);
+ return before_->offset_ms;
+ }
+
+ // Binary search for daylight savings offset change point,
+ // but give up if we don't find it in four iterations.
+ for (int i = 4; i >= 0; --i) {
+ int delta = after_->start_sec - before_->end_sec;
+ int middle_sec = (i == 0) ? time_sec : before_->end_sec + delta / 2;
+ int offset_ms = GetDaylightSavingsOffsetFromOS(middle_sec);
+ if (before_->offset_ms == offset_ms) {
+ before_->end_sec = middle_sec;
+ if (time_sec <= before_->end_sec) {
+ return offset_ms;
+ }
+ } else {
+ ASSERT(after_->offset_ms == offset_ms);
+ after_->start_sec = middle_sec;
+ if (time_sec >= after_->start_sec) {
+ // This swap helps the optimistic fast check in subsequent invocations.
+ DST* temp = before_;
+ before_ = after_;
+ after_ = temp;
+ return offset_ms;
+ }
+ }
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+
+void DateCache::ProbeDST(int time_sec) {
+ DST* before = NULL;
+ DST* after = NULL;
+ ASSERT(before_ != after_);
+
+ for (int i = 0; i < kDSTSize; ++i) {
+ if (dst_[i].start_sec <= time_sec) {
+ if (before == NULL || before->start_sec < dst_[i].start_sec) {
+ before = &dst_[i];
+ }
+ } else if (time_sec < dst_[i].end_sec) {
+ if (after == NULL || after->end_sec > dst_[i].end_sec) {
+ after = &dst_[i];
+ }
+ }
+ }
+
+ // If before or after segments were not found,
+ // then set them to any invalid segment.
+ if (before == NULL) {
+ before = InvalidSegment(before_) ? before_ : LeastRecentlyUsedDST(after);
+ }
+ if (after == NULL) {
+ after = InvalidSegment(after_) && before != after_
+ ? after_ : LeastRecentlyUsedDST(before);
+ }
+
+ ASSERT(before != NULL);
+ ASSERT(after != NULL);
+ ASSERT(before != after);
+ ASSERT(InvalidSegment(before) || before->start_sec <= time_sec);
+ ASSERT(InvalidSegment(after) || time_sec < after->start_sec);
+ ASSERT(InvalidSegment(before) || InvalidSegment(after) ||
+ before->end_sec < after->start_sec);
+
+ before_ = before;
+ after_ = after;
+}
+
+
+DateCache::DST* DateCache::LeastRecentlyUsedDST(DST* skip) {
+ DST* result = NULL;
+ for (int i = 0; i < kDSTSize; ++i) {
+ if (&dst_[i] == skip) continue;
+ if (result == NULL || result->last_used > dst_[i].last_used) {
+ result = &dst_[i];
+ }
+ }
+ ClearSegment(result);
+ return result;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/date.h b/chromium/v8/src/date.h
new file mode 100644
index 00000000000..fcd61db0467
--- /dev/null
+++ b/chromium/v8/src/date.h
@@ -0,0 +1,260 @@
+// Copyright 2012 the V8 project authors. 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_DATE_H_
+#define V8_DATE_H_
+
+#include "allocation.h"
+#include "globals.h"
+#include "platform.h"
+
+
+namespace v8 {
+namespace internal {
+
+class DateCache {
+ public:
+ static const int kMsPerMin = 60 * 1000;
+ static const int kSecPerDay = 24 * 60 * 60;
+ static const int64_t kMsPerDay = kSecPerDay * 1000;
+
+ // The largest time that can be passed to OS date-time library functions.
+ static const int kMaxEpochTimeInSec = kMaxInt;
+ static const int64_t kMaxEpochTimeInMs =
+ static_cast<int64_t>(kMaxInt) * 1000;
+
+ // The largest time that can be stored in JSDate.
+ static const int64_t kMaxTimeInMs =
+ static_cast<int64_t>(864000000) * 10000000;
+
+ // Conservative upper bound on time that can be stored in JSDate
+ // before UTC conversion.
+ static const int64_t kMaxTimeBeforeUTCInMs =
+ kMaxTimeInMs + 10 * kMsPerDay;
+
+ // Sentinel that denotes an invalid local offset.
+ static const int kInvalidLocalOffsetInMs = kMaxInt;
+ // Sentinel that denotes an invalid cache stamp.
+ // It is an invariant of DateCache that cache stamp is non-negative.
+ static const int kInvalidStamp = -1;
+
+ DateCache() : stamp_(0) {
+ ResetDateCache();
+ }
+
+ virtual ~DateCache() {}
+
+
+ // Clears cached timezone information and increments the cache stamp.
+ void ResetDateCache();
+
+
+ // Computes floor(time_ms / kMsPerDay).
+ static int DaysFromTime(int64_t time_ms) {
+ if (time_ms < 0) time_ms -= (kMsPerDay - 1);
+ return static_cast<int>(time_ms / kMsPerDay);
+ }
+
+
+ // Computes modulo(time_ms, kMsPerDay) given that
+ // days = floor(time_ms / kMsPerDay).
+ static int TimeInDay(int64_t time_ms, int days) {
+ return static_cast<int>(time_ms - days * kMsPerDay);
+ }
+
+
+ // Given the number of days since the epoch, computes the weekday.
+ // ECMA 262 - 15.9.1.6.
+ int Weekday(int days) {
+ int result = (days + 4) % 7;
+ return result >= 0 ? result : result + 7;
+ }
+
+
+ bool IsLeap(int year) {
+ return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+ }
+
+
+ // ECMA 262 - 15.9.1.7.
+ int LocalOffsetInMs() {
+ if (local_offset_ms_ == kInvalidLocalOffsetInMs) {
+ local_offset_ms_ = GetLocalOffsetFromOS();
+ }
+ return local_offset_ms_;
+ }
+
+
+ const char* LocalTimezone(int64_t time_ms) {
+ if (time_ms < 0 || time_ms > kMaxEpochTimeInMs) {
+ time_ms = EquivalentTime(time_ms);
+ }
+ return OS::LocalTimezone(static_cast<double>(time_ms));
+ }
+
+ // ECMA 262 - 15.9.5.26
+ int TimezoneOffset(int64_t time_ms) {
+ int64_t local_ms = ToLocal(time_ms);
+ return static_cast<int>((time_ms - local_ms) / kMsPerMin);
+ }
+
+ // ECMA 262 - 15.9.1.9
+ int64_t ToLocal(int64_t time_ms) {
+ return time_ms + LocalOffsetInMs() + DaylightSavingsOffsetInMs(time_ms);
+ }
+
+ // ECMA 262 - 15.9.1.9
+ int64_t ToUTC(int64_t time_ms) {
+ time_ms -= LocalOffsetInMs();
+ return time_ms - DaylightSavingsOffsetInMs(time_ms);
+ }
+
+
+ // Computes a time equivalent to the given time according
+ // to ECMA 262 - 15.9.1.9.
+ // The issue here is that some library calls don't work right for dates
+ // that cannot be represented using a non-negative signed 32 bit integer
+ // (measured in whole seconds based on the 1970 epoch).
+ // We solve this by mapping the time to a year with same leap-year-ness
+ // and same starting day for the year. The ECMAscript specification says
+ // we must do this, but for compatibility with other browsers, we use
+ // the actual year if it is in the range 1970..2037
+ int64_t EquivalentTime(int64_t time_ms) {
+ int days = DaysFromTime(time_ms);
+ int time_within_day_ms = static_cast<int>(time_ms - days * kMsPerDay);
+ int year, month, day;
+ YearMonthDayFromDays(days, &year, &month, &day);
+ int new_days = DaysFromYearMonth(EquivalentYear(year), month) + day - 1;
+ return static_cast<int64_t>(new_days) * kMsPerDay + time_within_day_ms;
+ }
+
+ // Returns an equivalent year in the range [2008-2035] matching
+ // - leap year,
+ // - week day of first day.
+ // ECMA 262 - 15.9.1.9.
+ int EquivalentYear(int year) {
+ int week_day = Weekday(DaysFromYearMonth(year, 0));
+ int recent_year = (IsLeap(year) ? 1956 : 1967) + (week_day * 12) % 28;
+ // Find the year in the range 2008..2037 that is equivalent mod 28.
+ // Add 3*28 to give a positive argument to the modulus operator.
+ return 2008 + (recent_year + 3 * 28 - 2008) % 28;
+ }
+
+ // Given the number of days since the epoch, computes
+ // the corresponding year, month, and day.
+ void YearMonthDayFromDays(int days, int* year, int* month, int* day);
+
+ // Computes the number of days since the epoch for
+ // the first day of the given month in the given year.
+ int DaysFromYearMonth(int year, int month);
+
+ // Cache stamp is used for invalidating caches in JSDate.
+ // We increment the stamp each time when the timezone information changes.
+ // JSDate objects perform stamp check and invalidate their caches if
+ // their saved stamp is not equal to the current stamp.
+ Smi* stamp() { return stamp_; }
+ void* stamp_address() { return &stamp_; }
+
+ // These functions are virtual so that we can override them when testing.
+ virtual int GetDaylightSavingsOffsetFromOS(int64_t time_sec) {
+ double time_ms = static_cast<double>(time_sec * 1000);
+ return static_cast<int>(OS::DaylightSavingsOffset(time_ms));
+ }
+
+ virtual int GetLocalOffsetFromOS() {
+ double offset = OS::LocalTimeOffset();
+ ASSERT(offset < kInvalidLocalOffsetInMs);
+ return static_cast<int>(offset);
+ }
+
+ private:
+ // The implementation relies on the fact that no time zones have
+ // more than one daylight savings offset change per 19 days.
+ // In Egypt in 2010 they decided to suspend DST during Ramadan. This
+ // led to a short interval where DST is in effect from September 10 to
+ // September 30.
+ static const int kDefaultDSTDeltaInSec = 19 * kSecPerDay;
+
+ // Size of the Daylight Savings Time cache.
+ static const int kDSTSize = 32;
+
+ // Daylight Savings Time segment stores a segment of time where
+ // daylight savings offset does not change.
+ struct DST {
+ int start_sec;
+ int end_sec;
+ int offset_ms;
+ int last_used;
+ };
+
+ // Computes the daylight savings offset for the given time.
+ // ECMA 262 - 15.9.1.8
+ int DaylightSavingsOffsetInMs(int64_t time_ms);
+
+ // Sets the before_ and the after_ segments from the DST cache such that
+ // the before_ segment starts earlier than the given time and
+ // the after_ segment start later than the given time.
+ // Both segments might be invalid.
+ // The last_used counters of the before_ and after_ are updated.
+ void ProbeDST(int time_sec);
+
+ // Finds the least recently used segment from the DST cache that is not
+ // equal to the given 'skip' segment.
+ DST* LeastRecentlyUsedDST(DST* skip);
+
+ // Extends the after_ segment with the given point or resets it
+ // if it starts later than the given time + kDefaultDSTDeltaInSec.
+ inline void ExtendTheAfterSegment(int time_sec, int offset_ms);
+
+ // Makes the given segment invalid.
+ inline void ClearSegment(DST* segment);
+
+ bool InvalidSegment(DST* segment) {
+ return segment->start_sec > segment->end_sec;
+ }
+
+ Smi* stamp_;
+
+ // Daylight Saving Time cache.
+ DST dst_[kDSTSize];
+ int dst_usage_counter_;
+ DST* before_;
+ DST* after_;
+
+ int local_offset_ms_;
+
+ // Year/Month/Day cache.
+ bool ymd_valid_;
+ int ymd_days_;
+ int ymd_year_;
+ int ymd_month_;
+ int ymd_day_;
+};
+
+} } // namespace v8::internal
+
+#endif
diff --git a/chromium/v8/src/date.js b/chromium/v8/src/date.js
new file mode 100644
index 00000000000..62999e9de63
--- /dev/null
+++ b/chromium/v8/src/date.js
@@ -0,0 +1,829 @@
+// 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.
+
+// This file relies on the fact that the following declarations have been made
+// in v8natives.js:
+// var $isFinite = GlobalIsFinite;
+
+var $Date = global.Date;
+
+// -------------------------------------------------------------------
+
+// This file contains date support implemented in JavaScript.
+
+// Helper function to throw error.
+function ThrowDateTypeError() {
+ throw new $TypeError('this is not a Date object.');
+}
+
+
+var timezone_cache_time = $NaN;
+var timezone_cache_timezone;
+
+function LocalTimezone(t) {
+ if (NUMBER_IS_NAN(t)) return "";
+ if (t == timezone_cache_time) {
+ return timezone_cache_timezone;
+ }
+ var timezone = %DateLocalTimezone(t);
+ timezone_cache_time = t;
+ timezone_cache_timezone = timezone;
+ return timezone;
+}
+
+
+function UTC(time) {
+ if (NUMBER_IS_NAN(time)) return time;
+ // local_time_offset is needed before the call to DaylightSavingsOffset,
+ // so it may be uninitialized.
+ return %DateToUTC(time);
+}
+
+
+// ECMA 262 - 15.9.1.11
+function MakeTime(hour, min, sec, ms) {
+ if (!$isFinite(hour)) return $NaN;
+ if (!$isFinite(min)) return $NaN;
+ if (!$isFinite(sec)) return $NaN;
+ if (!$isFinite(ms)) return $NaN;
+ return TO_INTEGER(hour) * msPerHour
+ + TO_INTEGER(min) * msPerMinute
+ + TO_INTEGER(sec) * msPerSecond
+ + TO_INTEGER(ms);
+}
+
+
+// ECMA 262 - 15.9.1.12
+function TimeInYear(year) {
+ return DaysInYear(year) * msPerDay;
+}
+
+
+// Compute number of days given a year, month, date.
+// Note that month and date can lie outside the normal range.
+// For example:
+// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
+// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
+// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
+function MakeDay(year, month, date) {
+ if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
+
+ // Convert to integer and map -0 to 0.
+ year = TO_INTEGER_MAP_MINUS_ZERO(year);
+ month = TO_INTEGER_MAP_MINUS_ZERO(month);
+ date = TO_INTEGER_MAP_MINUS_ZERO(date);
+
+ if (year < kMinYear || year > kMaxYear ||
+ month < kMinMonth || month > kMaxMonth) {
+ return $NaN;
+ }
+
+ // Now we rely on year and month being SMIs.
+ return %DateMakeDay(year | 0, month | 0) + date - 1;
+}
+
+
+// ECMA 262 - 15.9.1.13
+function MakeDate(day, time) {
+ var time = day * msPerDay + time;
+ // Some of our runtime funtions for computing UTC(time) rely on
+ // times not being significantly larger than MAX_TIME_MS. If there
+ // is no way that the time can be within range even after UTC
+ // conversion we return NaN immediately instead of relying on
+ // TimeClip to do it.
+ if ($abs(time) > MAX_TIME_BEFORE_UTC) return $NaN;
+ return time;
+}
+
+
+// ECMA 262 - 15.9.1.14
+function TimeClip(time) {
+ if (!$isFinite(time)) return $NaN;
+ if ($abs(time) > MAX_TIME_MS) return $NaN;
+ return TO_INTEGER(time);
+}
+
+
+// The Date cache is used to limit the cost of parsing the same Date
+// strings over and over again.
+var Date_cache = {
+ // Cached time value.
+ time: $NaN,
+ // String input for which the cached time is valid.
+ string: null
+};
+
+
+function DateConstructor(year, month, date, hours, minutes, seconds, ms) {
+ if (!%_IsConstructCall()) {
+ // ECMA 262 - 15.9.2
+ return (new $Date()).toString();
+ }
+
+ // ECMA 262 - 15.9.3
+ var argc = %_ArgumentsLength();
+ var value;
+ if (argc == 0) {
+ value = %DateCurrentTime();
+ SET_UTC_DATE_VALUE(this, value);
+ } else if (argc == 1) {
+ if (IS_NUMBER(year)) {
+ value = year;
+ } else if (IS_STRING(year)) {
+ // Probe the Date cache. If we already have a time value for the
+ // given time, we re-use that instead of parsing the string again.
+ var cache = Date_cache;
+ if (cache.string === year) {
+ value = cache.time;
+ } else {
+ value = DateParse(year);
+ if (!NUMBER_IS_NAN(value)) {
+ cache.time = value;
+ cache.string = year;
+ }
+ }
+
+ } else {
+ // According to ECMA 262, no hint should be given for this
+ // conversion. However, ToPrimitive defaults to STRING_HINT for
+ // Date objects which will lose precision when the Date
+ // constructor is called with another Date object as its
+ // argument. We therefore use NUMBER_HINT for the conversion,
+ // which is the default for everything else than Date objects.
+ // This makes us behave like KJS and SpiderMonkey.
+ var time = ToPrimitive(year, NUMBER_HINT);
+ value = IS_STRING(time) ? DateParse(time) : ToNumber(time);
+ }
+ SET_UTC_DATE_VALUE(this, value);
+ } else {
+ year = ToNumber(year);
+ month = ToNumber(month);
+ date = argc > 2 ? ToNumber(date) : 1;
+ hours = argc > 3 ? ToNumber(hours) : 0;
+ 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;
+ var day = MakeDay(year, month, date);
+ var time = MakeTime(hours, minutes, seconds, ms);
+ value = MakeDate(day, time);
+ SET_LOCAL_DATE_VALUE(this, value);
+ }
+}
+
+
+var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+
+function TwoDigitString(value) {
+ return value < 10 ? "0" + value : "" + value;
+}
+
+
+function DateString(date) {
+ return WeekDays[LOCAL_WEEKDAY(date)] + ' '
+ + Months[LOCAL_MONTH(date)] + ' '
+ + TwoDigitString(LOCAL_DAY(date)) + ' '
+ + LOCAL_YEAR(date);
+}
+
+
+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(date) {
+ return LongWeekDays[LOCAL_WEEKDAY(date)] + ', '
+ + LongMonths[LOCAL_MONTH(date)] + ' '
+ + TwoDigitString(LOCAL_DAY(date)) + ', '
+ + LOCAL_YEAR(date);
+}
+
+
+function TimeString(date) {
+ return TwoDigitString(LOCAL_HOUR(date)) + ':'
+ + TwoDigitString(LOCAL_MIN(date)) + ':'
+ + TwoDigitString(LOCAL_SEC(date));
+}
+
+
+function TimeStringUTC(date) {
+ return TwoDigitString(UTC_HOUR(date)) + ':'
+ + TwoDigitString(UTC_MIN(date)) + ':'
+ + TwoDigitString(UTC_SEC(date));
+}
+
+
+function LocalTimezoneString(date) {
+ var timezone = LocalTimezone(UTC_DATE_VALUE(date));
+
+ var timezoneOffset = -TIMEZONE_OFFSET(date);
+ var sign = (timezoneOffset >= 0) ? 1 : -1;
+ var hours = FLOOR((sign * timezoneOffset)/60);
+ var min = FLOOR((sign * timezoneOffset)%60);
+ var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
+ TwoDigitString(hours) + TwoDigitString(min);
+ return gmt + ' (' + timezone + ')';
+}
+
+
+function DatePrintString(date) {
+ return DateString(date) + ' ' + TimeString(date);
+}
+
+// -------------------------------------------------------------------
+
+// Reused output buffer. Used when parsing date strings.
+var parse_buffer = $Array(8);
+
+// ECMA 262 - 15.9.4.2
+function DateParse(string) {
+ var arr = %DateParseString(ToString(string), parse_buffer);
+ if (IS_NULL(arr)) return $NaN;
+
+ var day = MakeDay(arr[0], arr[1], arr[2]);
+ var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
+ var date = MakeDate(day, time);
+
+ if (IS_NULL(arr[7])) {
+ return TimeClip(UTC(date));
+ } else {
+ return TimeClip(date - arr[7] * 1000);
+ }
+}
+
+
+// ECMA 262 - 15.9.4.3
+function DateUTC(year, month, date, hours, minutes, seconds, ms) {
+ year = ToNumber(year);
+ month = ToNumber(month);
+ var argc = %_ArgumentsLength();
+ date = argc > 2 ? ToNumber(date) : 1;
+ hours = argc > 3 ? ToNumber(hours) : 0;
+ 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;
+ var day = MakeDay(year, month, date);
+ var time = MakeTime(hours, minutes, seconds, ms);
+ return TimeClip(MakeDate(day, time));
+}
+
+
+// Mozilla-specific extension. Returns the number of milliseconds
+// elapsed since 1 January 1970 00:00:00 UTC.
+function DateNow() {
+ return %DateCurrentTime();
+}
+
+
+// ECMA 262 - 15.9.5.2
+function DateToString() {
+ var t = UTC_DATE_VALUE(this)
+ if (NUMBER_IS_NAN(t)) return kInvalidDate;
+ var time_zone_string = LocalTimezoneString(this)
+ return DatePrintString(this) + time_zone_string;
+}
+
+
+// ECMA 262 - 15.9.5.3
+function DateToDateString() {
+ var t = UTC_DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return kInvalidDate;
+ return DateString(this);
+}
+
+
+// ECMA 262 - 15.9.5.4
+function DateToTimeString() {
+ var t = UTC_DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return kInvalidDate;
+ var time_zone_string = LocalTimezoneString(this);
+ return TimeString(this) + time_zone_string;
+}
+
+
+// ECMA 262 - 15.9.5.5
+function DateToLocaleString() {
+ return %_CallFunction(this, DateToString);
+}
+
+
+// ECMA 262 - 15.9.5.6
+function DateToLocaleDateString() {
+ var t = UTC_DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return kInvalidDate;
+ return LongDateString(this);
+}
+
+
+// ECMA 262 - 15.9.5.7
+function DateToLocaleTimeString() {
+ var t = UTC_DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return kInvalidDate;
+ return TimeString(this);
+}
+
+
+// ECMA 262 - 15.9.5.8
+function DateValueOf() {
+ return UTC_DATE_VALUE(this);
+}
+
+
+// ECMA 262 - 15.9.5.9
+function DateGetTime() {
+ return UTC_DATE_VALUE(this);
+}
+
+
+// ECMA 262 - 15.9.5.10
+function DateGetFullYear() {
+ return LOCAL_YEAR(this);
+}
+
+
+// ECMA 262 - 15.9.5.11
+function DateGetUTCFullYear() {
+ return UTC_YEAR(this);
+}
+
+
+// ECMA 262 - 15.9.5.12
+function DateGetMonth() {
+ return LOCAL_MONTH(this);
+}
+
+
+// ECMA 262 - 15.9.5.13
+function DateGetUTCMonth() {
+ return UTC_MONTH(this);
+}
+
+
+// ECMA 262 - 15.9.5.14
+function DateGetDate() {
+ return LOCAL_DAY(this);
+}
+
+
+// ECMA 262 - 15.9.5.15
+function DateGetUTCDate() {
+ return UTC_DAY(this);
+}
+
+
+// ECMA 262 - 15.9.5.16
+function DateGetDay() {
+ return LOCAL_WEEKDAY(this);
+}
+
+
+// ECMA 262 - 15.9.5.17
+function DateGetUTCDay() {
+ return UTC_WEEKDAY(this);
+}
+
+
+// ECMA 262 - 15.9.5.18
+function DateGetHours() {
+ return LOCAL_HOUR(this);
+}
+
+
+// ECMA 262 - 15.9.5.19
+function DateGetUTCHours() {
+ return UTC_HOUR(this);
+}
+
+
+// ECMA 262 - 15.9.5.20
+function DateGetMinutes() {
+ return LOCAL_MIN(this);
+}
+
+
+// ECMA 262 - 15.9.5.21
+function DateGetUTCMinutes() {
+ return UTC_MIN(this);
+}
+
+
+// ECMA 262 - 15.9.5.22
+function DateGetSeconds() {
+ return LOCAL_SEC(this);
+}
+
+
+// ECMA 262 - 15.9.5.23
+function DateGetUTCSeconds() {
+ return UTC_SEC(this)
+}
+
+
+// ECMA 262 - 15.9.5.24
+function DateGetMilliseconds() {
+ return LOCAL_MS(this);
+}
+
+
+// ECMA 262 - 15.9.5.25
+function DateGetUTCMilliseconds() {
+ return UTC_MS(this);
+}
+
+
+// ECMA 262 - 15.9.5.26
+function DateGetTimezoneOffset() {
+ return TIMEZONE_OFFSET(this);
+}
+
+
+// ECMA 262 - 15.9.5.27
+function DateSetTime(ms) {
+ CHECK_DATE(this);
+ SET_UTC_DATE_VALUE(this, ToNumber(ms));
+ return UTC_DATE_VALUE(this);
+}
+
+
+// ECMA 262 - 15.9.5.28
+function DateSetMilliseconds(ms) {
+ var t = LOCAL_DATE_VALUE(this);
+ ms = ToNumber(ms);
+ var time = MakeTime(LOCAL_HOUR(this), LOCAL_MIN(this), LOCAL_SEC(this), ms);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
+}
+
+
+// ECMA 262 - 15.9.5.29
+function DateSetUTCMilliseconds(ms) {
+ var t = UTC_DATE_VALUE(this);
+ ms = ToNumber(ms);
+ var time = MakeTime(UTC_HOUR(this),
+ UTC_MIN(this),
+ UTC_SEC(this),
+ ms);
+ return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
+}
+
+
+// ECMA 262 - 15.9.5.30
+function DateSetSeconds(sec, ms) {
+ var t = LOCAL_DATE_VALUE(this);
+ sec = ToNumber(sec);
+ ms = %_ArgumentsLength() < 2 ? LOCAL_MS(this) : ToNumber(ms);
+ var time = MakeTime(LOCAL_HOUR(this), LOCAL_MIN(this), sec, ms);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
+}
+
+
+// ECMA 262 - 15.9.5.31
+function DateSetUTCSeconds(sec, ms) {
+ var t = UTC_DATE_VALUE(this);
+ sec = ToNumber(sec);
+ ms = %_ArgumentsLength() < 2 ? UTC_MS(this) : ToNumber(ms);
+ var time = MakeTime(UTC_HOUR(this), UTC_MIN(this), sec, ms);
+ return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
+}
+
+
+// ECMA 262 - 15.9.5.33
+function DateSetMinutes(min, sec, ms) {
+ var t = LOCAL_DATE_VALUE(this);
+ min = ToNumber(min);
+ var argc = %_ArgumentsLength();
+ sec = argc < 2 ? LOCAL_SEC(this) : ToNumber(sec);
+ ms = argc < 3 ? LOCAL_MS(this) : ToNumber(ms);
+ var time = MakeTime(LOCAL_HOUR(this), min, sec, ms);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
+}
+
+
+// ECMA 262 - 15.9.5.34
+function DateSetUTCMinutes(min, sec, ms) {
+ var t = UTC_DATE_VALUE(this);
+ min = ToNumber(min);
+ var argc = %_ArgumentsLength();
+ sec = argc < 2 ? UTC_SEC(this) : ToNumber(sec);
+ ms = argc < 3 ? UTC_MS(this) : ToNumber(ms);
+ var time = MakeTime(UTC_HOUR(this), min, sec, ms);
+ return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
+}
+
+
+// ECMA 262 - 15.9.5.35
+function DateSetHours(hour, min, sec, ms) {
+ var t = LOCAL_DATE_VALUE(this);
+ hour = ToNumber(hour);
+ var argc = %_ArgumentsLength();
+ min = argc < 2 ? LOCAL_MIN(this) : ToNumber(min);
+ sec = argc < 3 ? LOCAL_SEC(this) : ToNumber(sec);
+ ms = argc < 4 ? LOCAL_MS(this) : ToNumber(ms);
+ var time = MakeTime(hour, min, sec, ms);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
+}
+
+
+// ECMA 262 - 15.9.5.34
+function DateSetUTCHours(hour, min, sec, ms) {
+ var t = UTC_DATE_VALUE(this);
+ hour = ToNumber(hour);
+ var argc = %_ArgumentsLength();
+ min = argc < 2 ? UTC_MIN(this) : ToNumber(min);
+ sec = argc < 3 ? UTC_SEC(this) : ToNumber(sec);
+ ms = argc < 4 ? UTC_MS(this) : ToNumber(ms);
+ var time = MakeTime(hour, min, sec, ms);
+ return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
+}
+
+
+// ECMA 262 - 15.9.5.36
+function DateSetDate(date) {
+ var t = LOCAL_DATE_VALUE(this);
+ date = ToNumber(date);
+ var day = MakeDay(LOCAL_YEAR(this), LOCAL_MONTH(this), date);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(day, LOCAL_TIME_IN_DAY(this)));
+}
+
+
+// ECMA 262 - 15.9.5.37
+function DateSetUTCDate(date) {
+ var t = UTC_DATE_VALUE(this);
+ date = ToNumber(date);
+ var day = MakeDay(UTC_YEAR(this), UTC_MONTH(this), date);
+ return SET_UTC_DATE_VALUE(this, MakeDate(day, UTC_TIME_IN_DAY(this)));
+}
+
+
+// ECMA 262 - 15.9.5.38
+function DateSetMonth(month, date) {
+ var t = LOCAL_DATE_VALUE(this);
+ month = ToNumber(month);
+ date = %_ArgumentsLength() < 2 ? LOCAL_DAY(this) : ToNumber(date);
+ var day = MakeDay(LOCAL_YEAR(this), month, date);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(day, LOCAL_TIME_IN_DAY(this)));
+}
+
+
+// ECMA 262 - 15.9.5.39
+function DateSetUTCMonth(month, date) {
+ var t = UTC_DATE_VALUE(this);
+ month = ToNumber(month);
+ date = %_ArgumentsLength() < 2 ? UTC_DAY(this) : ToNumber(date);
+ var day = MakeDay(UTC_YEAR(this), month, date);
+ return SET_UTC_DATE_VALUE(this, MakeDate(day, UTC_TIME_IN_DAY(this)));
+}
+
+
+// ECMA 262 - 15.9.5.40
+function DateSetFullYear(year, month, date) {
+ var t = LOCAL_DATE_VALUE(this);
+ year = ToNumber(year);
+ var argc = %_ArgumentsLength();
+ var time ;
+ if (NUMBER_IS_NAN(t)) {
+ month = argc < 2 ? 0 : ToNumber(month);
+ date = argc < 3 ? 1 : ToNumber(date);
+ time = 0;
+ } else {
+ month = argc < 2 ? LOCAL_MONTH(this) : ToNumber(month);
+ date = argc < 3 ? LOCAL_DAY(this) : ToNumber(date);
+ time = LOCAL_TIME_IN_DAY(this);
+ }
+ var day = MakeDay(year, month, date);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(day, time));
+}
+
+
+// ECMA 262 - 15.9.5.41
+function DateSetUTCFullYear(year, month, date) {
+ var t = UTC_DATE_VALUE(this);
+ year = ToNumber(year);
+ var argc = %_ArgumentsLength();
+ var time ;
+ if (NUMBER_IS_NAN(t)) {
+ month = argc < 2 ? 0 : ToNumber(month);
+ date = argc < 3 ? 1 : ToNumber(date);
+ time = 0;
+ } else {
+ month = argc < 2 ? UTC_MONTH(this) : ToNumber(month);
+ date = argc < 3 ? UTC_DAY(this) : ToNumber(date);
+ time = UTC_TIME_IN_DAY(this);
+ }
+ var day = MakeDay(year, month, date);
+ return SET_UTC_DATE_VALUE(this, MakeDate(day, time));
+}
+
+
+// ECMA 262 - 15.9.5.42
+function DateToUTCString() {
+ var t = UTC_DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return kInvalidDate;
+ // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
+ return WeekDays[UTC_WEEKDAY(this)] + ', '
+ + TwoDigitString(UTC_DAY(this)) + ' '
+ + Months[UTC_MONTH(this)] + ' '
+ + UTC_YEAR(this) + ' '
+ + TimeStringUTC(this) + ' GMT';
+}
+
+
+// ECMA 262 - B.2.4
+function DateGetYear() {
+ return LOCAL_YEAR(this) - 1900;
+}
+
+
+// ECMA 262 - B.2.5
+function DateSetYear(year) {
+ CHECK_DATE(this);
+ year = ToNumber(year);
+ if (NUMBER_IS_NAN(year)) return SET_UTC_DATE_VALUE(this, $NaN);
+ year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
+ ? 1900 + TO_INTEGER(year) : year;
+ var t = LOCAL_DATE_VALUE(this);
+ var month, date, time;
+ if (NUMBER_IS_NAN(t)) {
+ month = 0;
+ date = 1;
+ time = 0;
+ } else {
+ month = LOCAL_MONTH(this);
+ date = LOCAL_DAY(this);
+ time = LOCAL_TIME_IN_DAY(this);
+ }
+ var day = MakeDay(year, month, date);
+ return SET_LOCAL_DATE_VALUE(this, MakeDate(day, time));
+}
+
+
+// ECMA 262 - B.2.6
+//
+// Notice that this does not follow ECMA 262 completely. ECMA 262
+// says that toGMTString should be the same Function object as
+// toUTCString. JSC does not do this, so for compatibility we do not
+// do that either. Instead, we create a new function whose name
+// property will return toGMTString.
+function DateToGMTString() {
+ return %_CallFunction(this, DateToUTCString);
+}
+
+
+function PadInt(n, digits) {
+ if (digits == 1) return n;
+ return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
+}
+
+
+// ECMA 262 - 15.9.5.43
+function DateToISOString() {
+ var t = UTC_DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) throw MakeRangeError("invalid_time_value", []);
+ var year = this.getUTCFullYear();
+ var year_string;
+ if (year >= 0 && year <= 9999) {
+ year_string = PadInt(year, 4);
+ } else {
+ if (year < 0) {
+ year_string = "-" + PadInt(-year, 6);
+ } else {
+ year_string = "+" + PadInt(year, 6);
+ }
+ }
+ return year_string +
+ '-' + PadInt(this.getUTCMonth() + 1, 2) +
+ '-' + PadInt(this.getUTCDate(), 2) +
+ 'T' + PadInt(this.getUTCHours(), 2) +
+ ':' + PadInt(this.getUTCMinutes(), 2) +
+ ':' + PadInt(this.getUTCSeconds(), 2) +
+ '.' + PadInt(this.getUTCMilliseconds(), 3) +
+ 'Z';
+}
+
+
+function DateToJSON(key) {
+ var o = ToObject(this);
+ var tv = DefaultNumber(o);
+ if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) {
+ return null;
+ }
+ return o.toISOString();
+}
+
+
+function ResetDateCache() {
+ // Reset the timezone cache:
+ timezone_cache_time = $NaN;
+ timezone_cache_timezone = undefined;
+
+ // Reset the date cache:
+ cache = Date_cache;
+ cache.time = $NaN;
+ cache.string = null;
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpDate() {
+ %CheckIsBootstrapping();
+
+ %SetCode($Date, DateConstructor);
+ %FunctionSetPrototype($Date, new $Date($NaN));
+
+ // Set up non-enumerable properties of the Date object itself.
+ InstallFunctions($Date, DONT_ENUM, $Array(
+ "UTC", DateUTC,
+ "parse", DateParse,
+ "now", DateNow
+ ));
+
+ // Set up non-enumerable constructor property of the Date prototype object.
+ %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
+
+ // Set up non-enumerable functions of the Date prototype object and
+ // set their names.
+ InstallFunctions($Date.prototype, DONT_ENUM, $Array(
+ "toString", DateToString,
+ "toDateString", DateToDateString,
+ "toTimeString", DateToTimeString,
+ "toLocaleString", DateToLocaleString,
+ "toLocaleDateString", DateToLocaleDateString,
+ "toLocaleTimeString", DateToLocaleTimeString,
+ "valueOf", DateValueOf,
+ "getTime", DateGetTime,
+ "getFullYear", DateGetFullYear,
+ "getUTCFullYear", DateGetUTCFullYear,
+ "getMonth", DateGetMonth,
+ "getUTCMonth", DateGetUTCMonth,
+ "getDate", DateGetDate,
+ "getUTCDate", DateGetUTCDate,
+ "getDay", DateGetDay,
+ "getUTCDay", DateGetUTCDay,
+ "getHours", DateGetHours,
+ "getUTCHours", DateGetUTCHours,
+ "getMinutes", DateGetMinutes,
+ "getUTCMinutes", DateGetUTCMinutes,
+ "getSeconds", DateGetSeconds,
+ "getUTCSeconds", DateGetUTCSeconds,
+ "getMilliseconds", DateGetMilliseconds,
+ "getUTCMilliseconds", DateGetUTCMilliseconds,
+ "getTimezoneOffset", DateGetTimezoneOffset,
+ "setTime", DateSetTime,
+ "setMilliseconds", DateSetMilliseconds,
+ "setUTCMilliseconds", DateSetUTCMilliseconds,
+ "setSeconds", DateSetSeconds,
+ "setUTCSeconds", DateSetUTCSeconds,
+ "setMinutes", DateSetMinutes,
+ "setUTCMinutes", DateSetUTCMinutes,
+ "setHours", DateSetHours,
+ "setUTCHours", DateSetUTCHours,
+ "setDate", DateSetDate,
+ "setUTCDate", DateSetUTCDate,
+ "setMonth", DateSetMonth,
+ "setUTCMonth", DateSetUTCMonth,
+ "setFullYear", DateSetFullYear,
+ "setUTCFullYear", DateSetUTCFullYear,
+ "toGMTString", DateToGMTString,
+ "toUTCString", DateToUTCString,
+ "getYear", DateGetYear,
+ "setYear", DateSetYear,
+ "toISOString", DateToISOString,
+ "toJSON", DateToJSON
+ ));
+}
+
+SetUpDate();
diff --git a/chromium/v8/src/dateparser-inl.h b/chromium/v8/src/dateparser-inl.h
new file mode 100644
index 00000000000..3cb36fa4339
--- /dev/null
+++ b/chromium/v8/src/dateparser-inl.h
@@ -0,0 +1,334 @@
+// 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_DATEPARSER_INL_H_
+#define V8_DATEPARSER_INL_H_
+
+#include "dateparser.h"
+
+namespace v8 {
+namespace internal {
+
+template <typename Char>
+bool DateParser::Parse(Vector<Char> str,
+ FixedArray* out,
+ UnicodeCache* unicode_cache) {
+ ASSERT(out->length() >= OUTPUT_SIZE);
+ InputReader<Char> in(unicode_cache, str);
+ DateStringTokenizer<Char> scanner(&in);
+ TimeZoneComposer tz;
+ TimeComposer time;
+ DayComposer day;
+
+ // Specification:
+ // Accept ES5 ISO 8601 date-time-strings or legacy dates compatible
+ // with Safari.
+ // ES5 ISO 8601 dates:
+ // [('-'|'+')yy]yyyy[-MM[-DD]][THH:mm[:ss[.sss]][Z|(+|-)hh:mm]]
+ // where yyyy is in the range 0000..9999 and
+ // +/-yyyyyy is in the range -999999..+999999 -
+ // but -000000 is invalid (year zero must be positive),
+ // MM is in the range 01..12,
+ // DD is in the range 01..31,
+ // MM and DD defaults to 01 if missing,,
+ // HH is generally in the range 00..23, but can be 24 if mm, ss
+ // and sss are zero (or missing), representing midnight at the
+ // end of a day,
+ // mm and ss are in the range 00..59,
+ // sss is in the range 000..999,
+ // hh is in the range 00..23,
+ // mm, ss, and sss default to 00 if missing, and
+ // timezone defaults to Z if missing
+ // (following Safari, ISO actually demands local time).
+ // Extensions:
+ // We also allow sss to have more or less than three digits (but at
+ // least one).
+ // We allow hh:mm to be specified as hhmm.
+ // Legacy dates:
+ // Any unrecognized word before the first number is ignored.
+ // Parenthesized text is ignored.
+ // An unsigned number followed by ':' is a time value, and is
+ // added to the TimeComposer. A number followed by '::' adds a second
+ // zero as well. A number followed by '.' is also a time and must be
+ // followed by milliseconds.
+ // Any other number is a date component and is added to DayComposer.
+ // A month name (or really: any word having the same first three letters
+ // as a month name) is recorded as a named month in the Day composer.
+ // A word recognizable as a time-zone is recorded as such, as is
+ // '(+|-)(hhmm|hh:)'.
+ // Legacy dates don't allow extra signs ('+' or '-') or umatched ')'
+ // after a number has been read (before the first number, any garbage
+ // is allowed).
+ // Intersection of the two:
+ // A string that matches both formats (e.g. 1970-01-01) will be
+ // parsed as an ES5 date-time string - which means it will default
+ // to UTC time-zone. That's unavoidable if following the ES5
+ // specification.
+ // After a valid "T" has been read while scanning an ES5 datetime string,
+ // the input can no longer be a valid legacy date, since the "T" is a
+ // garbage string after a number has been read.
+
+ // First try getting as far as possible with as ES5 Date Time String.
+ DateToken next_unhandled_token = ParseES5DateTime(&scanner, &day, &time, &tz);
+ if (next_unhandled_token.IsInvalid()) return false;
+ bool has_read_number = !day.IsEmpty();
+ // If there's anything left, continue with the legacy parser.
+ for (DateToken token = next_unhandled_token;
+ !token.IsEndOfInput();
+ token = scanner.Next()) {
+ if (token.IsNumber()) {
+ has_read_number = true;
+ int n = token.number();
+ if (scanner.SkipSymbol(':')) {
+ if (scanner.SkipSymbol(':')) {
+ // n + "::"
+ if (!time.IsEmpty()) return false;
+ time.Add(n);
+ time.Add(0);
+ } else {
+ // n + ":"
+ if (!time.Add(n)) return false;
+ if (scanner.Peek().IsSymbol('.')) scanner.Next();
+ }
+ } else if (scanner.SkipSymbol('.') && time.IsExpecting(n)) {
+ time.Add(n);
+ if (!scanner.Peek().IsNumber()) return false;
+ int n = ReadMilliseconds(scanner.Next());
+ if (n < 0) return false;
+ time.AddFinal(n);
+ } else if (tz.IsExpecting(n)) {
+ tz.SetAbsoluteMinute(n);
+ } else if (time.IsExpecting(n)) {
+ time.AddFinal(n);
+ // Require end, white space, "Z", "+" or "-" immediately after
+ // finalizing time.
+ DateToken peek = scanner.Peek();
+ if (!peek.IsEndOfInput() &&
+ !peek.IsWhiteSpace() &&
+ !peek.IsKeywordZ() &&
+ !peek.IsAsciiSign()) return false;
+ } else {
+ if (!day.Add(n)) return false;
+ scanner.SkipSymbol('-');
+ }
+ } else if (token.IsKeyword()) {
+ // Parse a "word" (sequence of chars. >= 'A').
+ KeywordType type = token.keyword_type();
+ int value = token.keyword_value();
+ if (type == AM_PM && !time.IsEmpty()) {
+ time.SetHourOffset(value);
+ } else if (type == MONTH_NAME) {
+ day.SetNamedMonth(value);
+ scanner.SkipSymbol('-');
+ } else if (type == TIME_ZONE_NAME && has_read_number) {
+ tz.Set(value);
+ } else {
+ // Garbage words are illegal if a number has been read.
+ if (has_read_number) return false;
+ // The first number has to be separated from garbage words by
+ // whitespace or other separators.
+ if (scanner.Peek().IsNumber()) return false;
+ }
+ } else if (token.IsAsciiSign() && (tz.IsUTC() || !time.IsEmpty())) {
+ // Parse UTC offset (only after UTC or time).
+ tz.SetSign(token.ascii_sign());
+ // The following number may be empty.
+ int n = 0;
+ if (scanner.Peek().IsNumber()) {
+ n = scanner.Next().number();
+ }
+ has_read_number = true;
+
+ if (scanner.Peek().IsSymbol(':')) {
+ tz.SetAbsoluteHour(n);
+ tz.SetAbsoluteMinute(kNone);
+ } else {
+ tz.SetAbsoluteHour(n / 100);
+ tz.SetAbsoluteMinute(n % 100);
+ }
+ } else if ((token.IsAsciiSign() || token.IsSymbol(')')) &&
+ has_read_number) {
+ // Extra sign or ')' is illegal if a number has been read.
+ return false;
+ } else {
+ // Ignore other characters and whitespace.
+ }
+ }
+
+ return day.Write(out) && time.Write(out) && tz.Write(out);
+}
+
+
+template<typename CharType>
+DateParser::DateToken DateParser::DateStringTokenizer<CharType>::Scan() {
+ int pre_pos = in_->position();
+ if (in_->IsEnd()) return DateToken::EndOfInput();
+ if (in_->IsAsciiDigit()) {
+ int n = in_->ReadUnsignedNumeral();
+ int length = in_->position() - pre_pos;
+ return DateToken::Number(n, length);
+ }
+ if (in_->Skip(':')) return DateToken::Symbol(':');
+ if (in_->Skip('-')) return DateToken::Symbol('-');
+ if (in_->Skip('+')) return DateToken::Symbol('+');
+ if (in_->Skip('.')) return DateToken::Symbol('.');
+ if (in_->Skip(')')) return DateToken::Symbol(')');
+ if (in_->IsAsciiAlphaOrAbove()) {
+ ASSERT(KeywordTable::kPrefixLength == 3);
+ uint32_t buffer[3] = {0, 0, 0};
+ int length = in_->ReadWord(buffer, 3);
+ int index = KeywordTable::Lookup(buffer, length);
+ return DateToken::Keyword(KeywordTable::GetType(index),
+ KeywordTable::GetValue(index),
+ length);
+ }
+ if (in_->SkipWhiteSpace()) {
+ return DateToken::WhiteSpace(in_->position() - pre_pos);
+ }
+ if (in_->SkipParentheses()) {
+ return DateToken::Unknown();
+ }
+ in_->Next();
+ return DateToken::Unknown();
+}
+
+
+template <typename Char>
+DateParser::DateToken DateParser::ParseES5DateTime(
+ DateStringTokenizer<Char>* scanner,
+ DayComposer* day,
+ TimeComposer* time,
+ TimeZoneComposer* tz) {
+ ASSERT(day->IsEmpty());
+ ASSERT(time->IsEmpty());
+ ASSERT(tz->IsEmpty());
+
+ // Parse mandatory date string: [('-'|'+')yy]yyyy[':'MM[':'DD]]
+ if (scanner->Peek().IsAsciiSign()) {
+ // Keep the sign token, so we can pass it back to the legacy
+ // parser if we don't use it.
+ DateToken sign_token = scanner->Next();
+ if (!scanner->Peek().IsFixedLengthNumber(6)) return sign_token;
+ int sign = sign_token.ascii_sign();
+ int year = scanner->Next().number();
+ if (sign < 0 && year == 0) return sign_token;
+ day->Add(sign * year);
+ } else if (scanner->Peek().IsFixedLengthNumber(4)) {
+ day->Add(scanner->Next().number());
+ } else {
+ return scanner->Next();
+ }
+ if (scanner->SkipSymbol('-')) {
+ if (!scanner->Peek().IsFixedLengthNumber(2) ||
+ !DayComposer::IsMonth(scanner->Peek().number())) return scanner->Next();
+ day->Add(scanner->Next().number());
+ if (scanner->SkipSymbol('-')) {
+ if (!scanner->Peek().IsFixedLengthNumber(2) ||
+ !DayComposer::IsDay(scanner->Peek().number())) return scanner->Next();
+ day->Add(scanner->Next().number());
+ }
+ }
+ // Check for optional time string: 'T'HH':'mm[':'ss['.'sss]]Z
+ if (!scanner->Peek().IsKeywordType(TIME_SEPARATOR)) {
+ if (!scanner->Peek().IsEndOfInput()) return scanner->Next();
+ } else {
+ // ES5 Date Time String time part is present.
+ scanner->Next();
+ if (!scanner->Peek().IsFixedLengthNumber(2) ||
+ !Between(scanner->Peek().number(), 0, 24)) {
+ return DateToken::Invalid();
+ }
+ // Allow 24:00[:00[.000]], but no other time starting with 24.
+ bool hour_is_24 = (scanner->Peek().number() == 24);
+ time->Add(scanner->Next().number());
+ if (!scanner->SkipSymbol(':')) return DateToken::Invalid();
+ if (!scanner->Peek().IsFixedLengthNumber(2) ||
+ !TimeComposer::IsMinute(scanner->Peek().number()) ||
+ (hour_is_24 && scanner->Peek().number() > 0)) {
+ return DateToken::Invalid();
+ }
+ time->Add(scanner->Next().number());
+ if (scanner->SkipSymbol(':')) {
+ if (!scanner->Peek().IsFixedLengthNumber(2) ||
+ !TimeComposer::IsSecond(scanner->Peek().number()) ||
+ (hour_is_24 && scanner->Peek().number() > 0)) {
+ return DateToken::Invalid();
+ }
+ time->Add(scanner->Next().number());
+ if (scanner->SkipSymbol('.')) {
+ if (!scanner->Peek().IsNumber() ||
+ (hour_is_24 && scanner->Peek().number() > 0)) {
+ return DateToken::Invalid();
+ }
+ // Allow more or less than the mandated three digits.
+ time->Add(ReadMilliseconds(scanner->Next()));
+ }
+ }
+ // Check for optional timezone designation: 'Z' | ('+'|'-')hh':'mm
+ if (scanner->Peek().IsKeywordZ()) {
+ scanner->Next();
+ tz->Set(0);
+ } else if (scanner->Peek().IsSymbol('+') ||
+ scanner->Peek().IsSymbol('-')) {
+ tz->SetSign(scanner->Next().symbol() == '+' ? 1 : -1);
+ if (scanner->Peek().IsFixedLengthNumber(4)) {
+ // hhmm extension syntax.
+ int hourmin = scanner->Next().number();
+ int hour = hourmin / 100;
+ int min = hourmin % 100;
+ if (!TimeComposer::IsHour(hour) || !TimeComposer::IsMinute(min)) {
+ return DateToken::Invalid();
+ }
+ tz->SetAbsoluteHour(hour);
+ tz->SetAbsoluteMinute(min);
+ } else {
+ // hh:mm standard syntax.
+ if (!scanner->Peek().IsFixedLengthNumber(2) ||
+ !TimeComposer::IsHour(scanner->Peek().number())) {
+ return DateToken::Invalid();
+ }
+ tz->SetAbsoluteHour(scanner->Next().number());
+ if (!scanner->SkipSymbol(':')) return DateToken::Invalid();
+ if (!scanner->Peek().IsFixedLengthNumber(2) ||
+ !TimeComposer::IsMinute(scanner->Peek().number())) {
+ return DateToken::Invalid();
+ }
+ tz->SetAbsoluteMinute(scanner->Next().number());
+ }
+ }
+ if (!scanner->Peek().IsEndOfInput()) return DateToken::Invalid();
+ }
+ // Successfully parsed ES5 Date Time String. Default to UTC if no TZ given.
+ if (tz->IsEmpty()) tz->Set(0);
+ day->set_iso_date();
+ return DateToken::EndOfInput();
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_DATEPARSER_INL_H_
diff --git a/chromium/v8/src/dateparser.cc b/chromium/v8/src/dateparser.cc
new file mode 100644
index 00000000000..3964e811780
--- /dev/null
+++ b/chromium/v8/src/dateparser.cc
@@ -0,0 +1,213 @@
+// 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 "dateparser.h"
+
+namespace v8 {
+namespace internal {
+
+bool DateParser::DayComposer::Write(FixedArray* output) {
+ if (index_ < 1) return false;
+ // Day and month defaults to 1.
+ while (index_ < kSize) {
+ comp_[index_++] = 1;
+ }
+
+ int year = 0; // Default year is 0 (=> 2000) for KJS compatibility.
+ int month = kNone;
+ int day = kNone;
+
+ if (named_month_ == kNone) {
+ if (is_iso_date_ || (index_ == 3 && !IsDay(comp_[0]))) {
+ // YMD
+ year = comp_[0];
+ month = comp_[1];
+ day = comp_[2];
+ } else {
+ // MD(Y)
+ month = comp_[0];
+ day = comp_[1];
+ if (index_ == 3) year = comp_[2];
+ }
+ } else {
+ month = named_month_;
+ if (index_ == 1) {
+ // MD or DM
+ day = comp_[0];
+ } else if (!IsDay(comp_[0])) {
+ // YMD, MYD, or YDM
+ year = comp_[0];
+ day = comp_[1];
+ } else {
+ // DMY, MDY, or DYM
+ day = comp_[0];
+ year = comp_[1];
+ }
+ }
+
+ if (!is_iso_date_) {
+ if (Between(year, 0, 49)) year += 2000;
+ else if (Between(year, 50, 99)) year += 1900;
+ }
+
+ if (!Smi::IsValid(year) || !IsMonth(month) || !IsDay(day)) return false;
+
+ output->set(YEAR, Smi::FromInt(year));
+ output->set(MONTH, Smi::FromInt(month - 1)); // 0-based
+ output->set(DAY, Smi::FromInt(day));
+ return true;
+}
+
+
+bool DateParser::TimeComposer::Write(FixedArray* output) {
+ // All time slots default to 0
+ while (index_ < kSize) {
+ comp_[index_++] = 0;
+ }
+
+ int& hour = comp_[0];
+ int& minute = comp_[1];
+ int& second = comp_[2];
+ int& millisecond = comp_[3];
+
+ if (hour_offset_ != kNone) {
+ if (!IsHour12(hour)) return false;
+ hour %= 12;
+ hour += hour_offset_;
+ }
+
+ if (!IsHour(hour) || !IsMinute(minute) ||
+ !IsSecond(second) || !IsMillisecond(millisecond)) return false;
+
+ output->set(HOUR, Smi::FromInt(hour));
+ output->set(MINUTE, Smi::FromInt(minute));
+ output->set(SECOND, Smi::FromInt(second));
+ output->set(MILLISECOND, Smi::FromInt(millisecond));
+ return true;
+}
+
+
+bool DateParser::TimeZoneComposer::Write(FixedArray* output) {
+ if (sign_ != kNone) {
+ if (hour_ == kNone) hour_ = 0;
+ if (minute_ == kNone) minute_ = 0;
+ int total_seconds = sign_ * (hour_ * 3600 + minute_ * 60);
+ if (!Smi::IsValid(total_seconds)) return false;
+ output->set(UTC_OFFSET, Smi::FromInt(total_seconds));
+ } else {
+ output->set_null(UTC_OFFSET);
+ }
+ return true;
+}
+
+const int8_t DateParser::KeywordTable::
+ array[][DateParser::KeywordTable::kEntrySize] = {
+ {'j', 'a', 'n', DateParser::MONTH_NAME, 1},
+ {'f', 'e', 'b', DateParser::MONTH_NAME, 2},
+ {'m', 'a', 'r', DateParser::MONTH_NAME, 3},
+ {'a', 'p', 'r', DateParser::MONTH_NAME, 4},
+ {'m', 'a', 'y', DateParser::MONTH_NAME, 5},
+ {'j', 'u', 'n', DateParser::MONTH_NAME, 6},
+ {'j', 'u', 'l', DateParser::MONTH_NAME, 7},
+ {'a', 'u', 'g', DateParser::MONTH_NAME, 8},
+ {'s', 'e', 'p', DateParser::MONTH_NAME, 9},
+ {'o', 'c', 't', DateParser::MONTH_NAME, 10},
+ {'n', 'o', 'v', DateParser::MONTH_NAME, 11},
+ {'d', 'e', 'c', DateParser::MONTH_NAME, 12},
+ {'a', 'm', '\0', DateParser::AM_PM, 0},
+ {'p', 'm', '\0', DateParser::AM_PM, 12},
+ {'u', 't', '\0', DateParser::TIME_ZONE_NAME, 0},
+ {'u', 't', 'c', DateParser::TIME_ZONE_NAME, 0},
+ {'z', '\0', '\0', DateParser::TIME_ZONE_NAME, 0},
+ {'g', 'm', 't', DateParser::TIME_ZONE_NAME, 0},
+ {'c', 'd', 't', DateParser::TIME_ZONE_NAME, -5},
+ {'c', 's', 't', DateParser::TIME_ZONE_NAME, -6},
+ {'e', 'd', 't', DateParser::TIME_ZONE_NAME, -4},
+ {'e', 's', 't', DateParser::TIME_ZONE_NAME, -5},
+ {'m', 'd', 't', DateParser::TIME_ZONE_NAME, -6},
+ {'m', 's', 't', DateParser::TIME_ZONE_NAME, -7},
+ {'p', 'd', 't', DateParser::TIME_ZONE_NAME, -7},
+ {'p', 's', 't', DateParser::TIME_ZONE_NAME, -8},
+ {'t', '\0', '\0', DateParser::TIME_SEPARATOR, 0},
+ {'\0', '\0', '\0', DateParser::INVALID, 0},
+};
+
+
+// We could use perfect hashing here, but this is not a bottleneck.
+int DateParser::KeywordTable::Lookup(const uint32_t* pre, int len) {
+ int i;
+ for (i = 0; array[i][kTypeOffset] != INVALID; i++) {
+ int j = 0;
+ while (j < kPrefixLength &&
+ pre[j] == static_cast<uint32_t>(array[i][j])) {
+ j++;
+ }
+ // Check if we have a match and the length is legal.
+ // Word longer than keyword is only allowed for month names.
+ if (j == kPrefixLength &&
+ (len <= kPrefixLength || array[i][kTypeOffset] == MONTH_NAME)) {
+ return i;
+ }
+ }
+ return i;
+}
+
+
+int DateParser::ReadMilliseconds(DateToken token) {
+ // Read first three significant digits of the original numeral,
+ // as inferred from the value and the number of digits.
+ // I.e., use the number of digits to see if there were
+ // leading zeros.
+ int number = token.number();
+ int length = token.length();
+ if (length < 3) {
+ // Less than three digits. Multiply to put most significant digit
+ // in hundreds position.
+ if (length == 1) {
+ number *= 100;
+ } else if (length == 2) {
+ number *= 10;
+ }
+ } else if (length > 3) {
+ if (length > kMaxSignificantDigits) length = kMaxSignificantDigits;
+ // More than three digits. Divide by 10^(length - 3) to get three
+ // most significant digits.
+ int factor = 1;
+ do {
+ ASSERT(factor <= 100000000); // factor won't overflow.
+ factor *= 10;
+ length--;
+ } while (length > 3);
+ number /= factor;
+ }
+ return number;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/dateparser.h b/chromium/v8/src/dateparser.h
new file mode 100644
index 00000000000..27584ce39ef
--- /dev/null
+++ b/chromium/v8/src/dateparser.h
@@ -0,0 +1,409 @@
+// 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_DATEPARSER_H_
+#define V8_DATEPARSER_H_
+
+#include "allocation.h"
+#include "char-predicates-inl.h"
+
+namespace v8 {
+namespace internal {
+
+class DateParser : public AllStatic {
+ public:
+ // Parse the string as a date. If parsing succeeds, return true after
+ // filling out the output array as follows (all integers are Smis):
+ // [0]: year
+ // [1]: month (0 = Jan, 1 = Feb, ...)
+ // [2]: day
+ // [3]: hour
+ // [4]: minute
+ // [5]: second
+ // [6]: millisecond
+ // [7]: UTC offset in seconds, or null value if no timezone specified
+ // If parsing fails, return false (content of output array is not defined).
+ template <typename Char>
+ static bool Parse(Vector<Char> str, FixedArray* output, UnicodeCache* cache);
+
+ enum {
+ YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, UTC_OFFSET, OUTPUT_SIZE
+ };
+
+ private:
+ // Range testing
+ static inline bool Between(int x, int lo, int hi) {
+ return static_cast<unsigned>(x - lo) <= static_cast<unsigned>(hi - lo);
+ }
+
+ // Indicates a missing value.
+ static const int kNone = kMaxInt;
+
+ // Maximal number of digits used to build the value of a numeral.
+ // Remaining digits are ignored.
+ static const int kMaxSignificantDigits = 9;
+
+ // InputReader provides basic string parsing and character classification.
+ template <typename Char>
+ class InputReader BASE_EMBEDDED {
+ public:
+ InputReader(UnicodeCache* unicode_cache, Vector<Char> s)
+ : index_(0),
+ buffer_(s),
+ unicode_cache_(unicode_cache) {
+ Next();
+ }
+
+ int position() { return index_; }
+
+ // Advance to the next character of the string.
+ void Next() {
+ ch_ = (index_ < buffer_.length()) ? buffer_[index_] : 0;
+ index_++;
+ }
+
+ // Read a string of digits as an unsigned number. Cap value at
+ // kMaxSignificantDigits, but skip remaining digits if the numeral
+ // is longer.
+ int ReadUnsignedNumeral() {
+ int n = 0;
+ int i = 0;
+ while (IsAsciiDigit()) {
+ if (i < kMaxSignificantDigits) n = n * 10 + ch_ - '0';
+ i++;
+ Next();
+ }
+ return n;
+ }
+
+ // Read a word (sequence of chars. >= 'A'), fill the given buffer with a
+ // lower-case prefix, and pad any remainder of the buffer with zeroes.
+ // Return word length.
+ int ReadWord(uint32_t* prefix, int prefix_size) {
+ int len;
+ for (len = 0; IsAsciiAlphaOrAbove(); Next(), len++) {
+ if (len < prefix_size) prefix[len] = AsciiAlphaToLower(ch_);
+ }
+ for (int i = len; i < prefix_size; i++) prefix[i] = 0;
+ return len;
+ }
+
+ // The skip methods return whether they actually skipped something.
+ bool Skip(uint32_t c) {
+ if (ch_ == c) {
+ Next();
+ return true;
+ }
+ return false;
+ }
+
+ bool SkipWhiteSpace() {
+ if (unicode_cache_->IsWhiteSpace(ch_)) {
+ Next();
+ return true;
+ }
+ return false;
+ }
+
+ bool SkipParentheses() {
+ if (ch_ != '(') return false;
+ int balance = 0;
+ do {
+ if (ch_ == ')') --balance;
+ else if (ch_ == '(') ++balance;
+ Next();
+ } while (balance > 0 && ch_);
+ return true;
+ }
+
+ // Character testing/classification. Non-ASCII digits are not supported.
+ bool Is(uint32_t c) const { return ch_ == c; }
+ bool IsEnd() const { return ch_ == 0; }
+ bool IsAsciiDigit() const { return IsDecimalDigit(ch_); }
+ bool IsAsciiAlphaOrAbove() const { return ch_ >= 'A'; }
+ bool IsAsciiSign() const { return ch_ == '+' || ch_ == '-'; }
+
+ // Return 1 for '+' and -1 for '-'.
+ int GetAsciiSignValue() const { return 44 - static_cast<int>(ch_); }
+
+ private:
+ int index_;
+ Vector<Char> buffer_;
+ uint32_t ch_;
+ UnicodeCache* unicode_cache_;
+ };
+
+ enum KeywordType {
+ INVALID, MONTH_NAME, TIME_ZONE_NAME, TIME_SEPARATOR, AM_PM
+ };
+
+ struct DateToken {
+ public:
+ bool IsInvalid() { return tag_ == kInvalidTokenTag; }
+ bool IsUnknown() { return tag_ == kUnknownTokenTag; }
+ bool IsNumber() { return tag_ == kNumberTag; }
+ bool IsSymbol() { return tag_ == kSymbolTag; }
+ bool IsWhiteSpace() { return tag_ == kWhiteSpaceTag; }
+ bool IsEndOfInput() { return tag_ == kEndOfInputTag; }
+ bool IsKeyword() { return tag_ >= kKeywordTagStart; }
+
+ int length() { return length_; }
+
+ int number() {
+ ASSERT(IsNumber());
+ return value_;
+ }
+ KeywordType keyword_type() {
+ ASSERT(IsKeyword());
+ return static_cast<KeywordType>(tag_);
+ }
+ int keyword_value() {
+ ASSERT(IsKeyword());
+ return value_;
+ }
+ char symbol() {
+ ASSERT(IsSymbol());
+ return static_cast<char>(value_);
+ }
+ bool IsSymbol(char symbol) {
+ return IsSymbol() && this->symbol() == symbol;
+ }
+ bool IsKeywordType(KeywordType tag) {
+ return tag_ == tag;
+ }
+ bool IsFixedLengthNumber(int length) {
+ return IsNumber() && length_ == length;
+ }
+ bool IsAsciiSign() {
+ return tag_ == kSymbolTag && (value_ == '-' || value_ == '+');
+ }
+ int ascii_sign() {
+ ASSERT(IsAsciiSign());
+ return 44 - value_;
+ }
+ bool IsKeywordZ() {
+ return IsKeywordType(TIME_ZONE_NAME) && length_ == 1 && value_ == 0;
+ }
+ bool IsUnknown(int character) {
+ return IsUnknown() && value_ == character;
+ }
+ // Factory functions.
+ static DateToken Keyword(KeywordType tag, int value, int length) {
+ return DateToken(tag, length, value);
+ }
+ static DateToken Number(int value, int length) {
+ return DateToken(kNumberTag, length, value);
+ }
+ static DateToken Symbol(char symbol) {
+ return DateToken(kSymbolTag, 1, symbol);
+ }
+ static DateToken EndOfInput() {
+ return DateToken(kEndOfInputTag, 0, -1);
+ }
+ static DateToken WhiteSpace(int length) {
+ return DateToken(kWhiteSpaceTag, length, -1);
+ }
+ static DateToken Unknown() {
+ return DateToken(kUnknownTokenTag, 1, -1);
+ }
+ static DateToken Invalid() {
+ return DateToken(kInvalidTokenTag, 0, -1);
+ }
+
+ private:
+ enum TagType {
+ kInvalidTokenTag = -6,
+ kUnknownTokenTag = -5,
+ kWhiteSpaceTag = -4,
+ kNumberTag = -3,
+ kSymbolTag = -2,
+ kEndOfInputTag = -1,
+ kKeywordTagStart = 0
+ };
+ DateToken(int tag, int length, int value)
+ : tag_(tag),
+ length_(length),
+ value_(value) { }
+
+ int tag_;
+ int length_; // Number of characters.
+ int value_;
+ };
+
+ template <typename Char>
+ class DateStringTokenizer {
+ public:
+ explicit DateStringTokenizer(InputReader<Char>* in)
+ : in_(in), next_(Scan()) { }
+ DateToken Next() {
+ DateToken result = next_;
+ next_ = Scan();
+ return result;
+ }
+
+ DateToken Peek() {
+ return next_;
+ }
+ bool SkipSymbol(char symbol) {
+ if (next_.IsSymbol(symbol)) {
+ next_ = Scan();
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ DateToken Scan();
+
+ InputReader<Char>* in_;
+ DateToken next_;
+ };
+
+ static int ReadMilliseconds(DateToken number);
+
+ // KeywordTable maps names of months, time zones, am/pm to numbers.
+ class KeywordTable : public AllStatic {
+ public:
+ // Look up a word in the keyword table and return an index.
+ // 'pre' contains a prefix of the word, zero-padded to size kPrefixLength
+ // and 'len' is the word length.
+ static int Lookup(const uint32_t* pre, int len);
+ // Get the type of the keyword at index i.
+ static KeywordType GetType(int i) {
+ return static_cast<KeywordType>(array[i][kTypeOffset]);
+ }
+ // Get the value of the keyword at index i.
+ static int GetValue(int i) { return array[i][kValueOffset]; }
+
+ static const int kPrefixLength = 3;
+ static const int kTypeOffset = kPrefixLength;
+ static const int kValueOffset = kTypeOffset + 1;
+ static const int kEntrySize = kValueOffset + 1;
+ static const int8_t array[][kEntrySize];
+ };
+
+ class TimeZoneComposer BASE_EMBEDDED {
+ public:
+ TimeZoneComposer() : sign_(kNone), hour_(kNone), minute_(kNone) {}
+ void Set(int offset_in_hours) {
+ sign_ = offset_in_hours < 0 ? -1 : 1;
+ hour_ = offset_in_hours * sign_;
+ minute_ = 0;
+ }
+ void SetSign(int sign) { sign_ = sign < 0 ? -1 : 1; }
+ void SetAbsoluteHour(int hour) { hour_ = hour; }
+ void SetAbsoluteMinute(int minute) { minute_ = minute; }
+ bool IsExpecting(int n) const {
+ return hour_ != kNone && minute_ == kNone && TimeComposer::IsMinute(n);
+ }
+ bool IsUTC() const { return hour_ == 0 && minute_ == 0; }
+ bool Write(FixedArray* output);
+ bool IsEmpty() { return hour_ == kNone; }
+ private:
+ int sign_;
+ int hour_;
+ int minute_;
+ };
+
+ class TimeComposer BASE_EMBEDDED {
+ public:
+ TimeComposer() : index_(0), hour_offset_(kNone) {}
+ bool IsEmpty() const { return index_ == 0; }
+ bool IsExpecting(int n) const {
+ return (index_ == 1 && IsMinute(n)) ||
+ (index_ == 2 && IsSecond(n)) ||
+ (index_ == 3 && IsMillisecond(n));
+ }
+ bool Add(int n) {
+ return index_ < kSize ? (comp_[index_++] = n, true) : false;
+ }
+ bool AddFinal(int n) {
+ if (!Add(n)) return false;
+ while (index_ < kSize) comp_[index_++] = 0;
+ return true;
+ }
+ void SetHourOffset(int n) { hour_offset_ = n; }
+ bool Write(FixedArray* output);
+
+ static bool IsMinute(int x) { return Between(x, 0, 59); }
+ static bool IsHour(int x) { return Between(x, 0, 23); }
+ static bool IsSecond(int x) { return Between(x, 0, 59); }
+
+ private:
+ static bool IsHour12(int x) { return Between(x, 0, 12); }
+ static bool IsMillisecond(int x) { return Between(x, 0, 999); }
+
+ static const int kSize = 4;
+ int comp_[kSize];
+ int index_;
+ int hour_offset_;
+ };
+
+ class DayComposer BASE_EMBEDDED {
+ public:
+ DayComposer() : index_(0), named_month_(kNone), is_iso_date_(false) {}
+ bool IsEmpty() const { return index_ == 0; }
+ bool Add(int n) {
+ if (index_ < kSize) {
+ comp_[index_] = n;
+ index_++;
+ return true;
+ }
+ return false;
+ }
+ void SetNamedMonth(int n) { named_month_ = n; }
+ bool Write(FixedArray* output);
+ void set_iso_date() { is_iso_date_ = true; }
+ static bool IsMonth(int x) { return Between(x, 1, 12); }
+ static bool IsDay(int x) { return Between(x, 1, 31); }
+
+ private:
+ static const int kSize = 3;
+ int comp_[kSize];
+ int index_;
+ int named_month_;
+ // If set, ensures that data is always parsed in year-month-date order.
+ bool is_iso_date_;
+ };
+
+ // Tries to parse an ES5 Date Time String. Returns the next token
+ // to continue with in the legacy date string parser. If parsing is
+ // complete, returns DateToken::EndOfInput(). If terminally unsuccessful,
+ // returns DateToken::Invalid(). Otherwise parsing continues in the
+ // legacy parser.
+ template <typename Char>
+ static DateParser::DateToken ParseES5DateTime(
+ DateStringTokenizer<Char>* scanner,
+ DayComposer* day,
+ TimeComposer* time,
+ TimeZoneComposer* tz);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_DATEPARSER_H_
diff --git a/chromium/v8/src/debug-agent.cc b/chromium/v8/src/debug-agent.cc
new file mode 100644
index 00000000000..811c00e0cc6
--- /dev/null
+++ b/chromium/v8/src/debug-agent.cc
@@ -0,0 +1,462 @@
+// Copyright 2012 the V8 project authors. 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 "debug.h"
+#include "debug-agent.h"
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+namespace v8 {
+namespace internal {
+
+// Public V8 debugger API message handler function. This function just delegates
+// to the debugger agent through it's data parameter.
+void DebuggerAgentMessageHandler(const v8::Debug::Message& message) {
+ DebuggerAgent* agent = Isolate::Current()->debugger_agent_instance();
+ ASSERT(agent != NULL);
+ agent->DebuggerMessage(message);
+}
+
+
+// Debugger agent main thread.
+void DebuggerAgent::Run() {
+ const int kOneSecondInMicros = 1000000;
+
+ // Allow this socket to reuse port even if still in TIME_WAIT.
+ server_->SetReuseAddress(true);
+
+ // First bind the socket to the requested port.
+ bool bound = false;
+ while (!bound && !terminate_) {
+ bound = server_->Bind(port_);
+
+ // If an error occurred wait a bit before retrying. The most common error
+ // would be that the port is already in use so this avoids a busy loop and
+ // make the agent take over the port when it becomes free.
+ if (!bound) {
+ PrintF("Failed to open socket on port %d, "
+ "waiting %d ms before retrying\n", port_, kOneSecondInMicros / 1000);
+ terminate_now_->Wait(kOneSecondInMicros);
+ }
+ }
+
+ // Accept connections on the bound port.
+ while (!terminate_) {
+ bool ok = server_->Listen(1);
+ listening_->Signal();
+ if (ok) {
+ // Accept the new connection.
+ Socket* client = server_->Accept();
+ ok = client != NULL;
+ if (ok) {
+ // Create and start a new session.
+ CreateSession(client);
+ }
+ }
+ }
+}
+
+
+void DebuggerAgent::Shutdown() {
+ // Set the termination flag.
+ terminate_ = true;
+
+ // Signal termination and make the server exit either its listen call or its
+ // binding loop. This makes sure that no new sessions can be established.
+ terminate_now_->Signal();
+ server_->Shutdown();
+ Join();
+
+ // Close existing session if any.
+ CloseSession();
+}
+
+
+void DebuggerAgent::WaitUntilListening() {
+ listening_->Wait();
+}
+
+static const char* kCreateSessionMessage =
+ "Remote debugging session already active\r\n";
+
+void DebuggerAgent::CreateSession(Socket* client) {
+ ScopedLock with(session_access_);
+
+ // If another session is already established terminate this one.
+ if (session_ != NULL) {
+ client->Send(kCreateSessionMessage, StrLength(kCreateSessionMessage));
+ delete client;
+ return;
+ }
+
+ // Create a new session and hook up the debug message handler.
+ session_ = new DebuggerAgentSession(this, client);
+ isolate_->debugger()->SetMessageHandler(DebuggerAgentMessageHandler);
+ session_->Start();
+}
+
+
+void DebuggerAgent::CloseSession() {
+ ScopedLock with(session_access_);
+
+ // Terminate the session.
+ if (session_ != NULL) {
+ session_->Shutdown();
+ session_->Join();
+ delete session_;
+ session_ = NULL;
+ }
+}
+
+
+void DebuggerAgent::DebuggerMessage(const v8::Debug::Message& message) {
+ ScopedLock with(session_access_);
+
+ // Forward the message handling to the session.
+ if (session_ != NULL) {
+ v8::String::Value val(message.GetJSON());
+ session_->DebuggerMessage(Vector<uint16_t>(const_cast<uint16_t*>(*val),
+ val.length()));
+ }
+}
+
+
+void DebuggerAgent::OnSessionClosed(DebuggerAgentSession* session) {
+ // Don't do anything during termination.
+ if (terminate_) {
+ return;
+ }
+
+ // Terminate the session.
+ ScopedLock with(session_access_);
+ ASSERT(session == session_);
+ if (session == session_) {
+ session_->Shutdown();
+ delete session_;
+ session_ = NULL;
+ }
+}
+
+
+void DebuggerAgentSession::Run() {
+ // Send the hello message.
+ bool ok = DebuggerAgentUtil::SendConnectMessage(client_, *agent_->name_);
+ if (!ok) return;
+
+ while (true) {
+ // Read data from the debugger front end.
+ SmartArrayPointer<char> message =
+ DebuggerAgentUtil::ReceiveMessage(client_);
+
+ const char* msg = *message;
+ bool is_closing_session = (msg == NULL);
+
+ if (msg == NULL) {
+ // If we lost the connection, then simulate a disconnect msg:
+ msg = "{\"seq\":1,\"type\":\"request\",\"command\":\"disconnect\"}";
+
+ } else {
+ // Check if we're getting a disconnect request:
+ const char* disconnectRequestStr =
+ "\"type\":\"request\",\"command\":\"disconnect\"}";
+ const char* result = strstr(msg, disconnectRequestStr);
+ if (result != NULL) {
+ is_closing_session = true;
+ }
+ }
+
+ // Convert UTF-8 to UTF-16.
+ unibrow::Utf8Decoder<128> decoder(msg, StrLength(msg));
+ int utf16_length = decoder.Utf16Length();
+ ScopedVector<uint16_t> temp(utf16_length + 1);
+ decoder.WriteUtf16(temp.start(), utf16_length);
+
+ // Send the request received to the debugger.
+ v8::Debug::SendCommand(temp.start(),
+ utf16_length,
+ NULL,
+ reinterpret_cast<v8::Isolate*>(agent_->isolate()));
+
+ if (is_closing_session) {
+ // Session is closed.
+ agent_->OnSessionClosed(this);
+ return;
+ }
+ }
+}
+
+
+void DebuggerAgentSession::DebuggerMessage(Vector<uint16_t> message) {
+ DebuggerAgentUtil::SendMessage(client_, message);
+}
+
+
+void DebuggerAgentSession::Shutdown() {
+ // Shutdown the socket to end the blocking receive.
+ client_->Shutdown();
+}
+
+
+const char* const DebuggerAgentUtil::kContentLength = "Content-Length";
+
+
+SmartArrayPointer<char> DebuggerAgentUtil::ReceiveMessage(const Socket* conn) {
+ int received;
+
+ // Read header.
+ int content_length = 0;
+ while (true) {
+ const int kHeaderBufferSize = 80;
+ char header_buffer[kHeaderBufferSize];
+ int header_buffer_position = 0;
+ char c = '\0'; // One character receive buffer.
+ char prev_c = '\0'; // Previous character.
+
+ // Read until CRLF.
+ while (!(c == '\n' && prev_c == '\r')) {
+ prev_c = c;
+ received = conn->Receive(&c, 1);
+ if (received == 0) {
+ PrintF("Error %d\n", Socket::LastError());
+ return SmartArrayPointer<char>();
+ }
+
+ // Add character to header buffer.
+ if (header_buffer_position < kHeaderBufferSize) {
+ header_buffer[header_buffer_position++] = c;
+ }
+ }
+
+ // Check for end of header (empty header line).
+ if (header_buffer_position == 2) { // Receive buffer contains CRLF.
+ break;
+ }
+
+ // Terminate header.
+ ASSERT(header_buffer_position > 1); // At least CRLF is received.
+ ASSERT(header_buffer_position <= kHeaderBufferSize);
+ header_buffer[header_buffer_position - 2] = '\0';
+
+ // Split header.
+ char* key = header_buffer;
+ char* value = NULL;
+ for (int i = 0; header_buffer[i] != '\0'; i++) {
+ if (header_buffer[i] == ':') {
+ header_buffer[i] = '\0';
+ value = header_buffer + i + 1;
+ while (*value == ' ') {
+ value++;
+ }
+ break;
+ }
+ }
+
+ // Check that key is Content-Length.
+ if (strcmp(key, kContentLength) == 0) {
+ // Get the content length value if present and within a sensible range.
+ if (value == NULL || strlen(value) > 7) {
+ return SmartArrayPointer<char>();
+ }
+ for (int i = 0; value[i] != '\0'; i++) {
+ // Bail out if illegal data.
+ if (value[i] < '0' || value[i] > '9') {
+ return SmartArrayPointer<char>();
+ }
+ content_length = 10 * content_length + (value[i] - '0');
+ }
+ } else {
+ // For now just print all other headers than Content-Length.
+ PrintF("%s: %s\n", key, value != NULL ? value : "(no value)");
+ }
+ }
+
+ // Return now if no body.
+ if (content_length == 0) {
+ return SmartArrayPointer<char>();
+ }
+
+ // Read body.
+ char* buffer = NewArray<char>(content_length + 1);
+ received = ReceiveAll(conn, buffer, content_length);
+ if (received < content_length) {
+ PrintF("Error %d\n", Socket::LastError());
+ return SmartArrayPointer<char>();
+ }
+ buffer[content_length] = '\0';
+
+ return SmartArrayPointer<char>(buffer);
+}
+
+
+bool DebuggerAgentUtil::SendConnectMessage(const Socket* conn,
+ const char* embedding_host) {
+ static const int kBufferSize = 80;
+ char buffer[kBufferSize]; // Sending buffer.
+ bool ok;
+ int len;
+
+ // Send the header.
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
+ "Type: connect\r\n");
+ ok = conn->Send(buffer, len);
+ if (!ok) return false;
+
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
+ "V8-Version: %s\r\n", v8::V8::GetVersion());
+ ok = conn->Send(buffer, len);
+ if (!ok) return false;
+
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
+ "Protocol-Version: 1\r\n");
+ ok = conn->Send(buffer, len);
+ if (!ok) return false;
+
+ if (embedding_host != NULL) {
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
+ "Embedding-Host: %s\r\n", embedding_host);
+ ok = conn->Send(buffer, len);
+ if (!ok) return false;
+ }
+
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
+ "%s: 0\r\n", kContentLength);
+ ok = conn->Send(buffer, len);
+ if (!ok) return false;
+
+ // Terminate header with empty line.
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize), "\r\n");
+ ok = conn->Send(buffer, len);
+ if (!ok) return false;
+
+ // No body for connect message.
+
+ return true;
+}
+
+
+bool DebuggerAgentUtil::SendMessage(const Socket* conn,
+ const Vector<uint16_t> message) {
+ static const int kBufferSize = 80;
+ char buffer[kBufferSize]; // Sending buffer both for header and body.
+
+ // Calculate the message size in UTF-8 encoding.
+ int utf8_len = 0;
+ int previous = unibrow::Utf16::kNoPreviousCharacter;
+ for (int i = 0; i < message.length(); i++) {
+ uint16_t character = message[i];
+ utf8_len += unibrow::Utf8::Length(character, previous);
+ previous = character;
+ }
+
+ // Send the header.
+ int len;
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
+ "%s: %d\r\n", kContentLength, utf8_len);
+ conn->Send(buffer, len);
+
+ // Terminate header with empty line.
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize), "\r\n");
+ conn->Send(buffer, len);
+
+ // Send message body as UTF-8.
+ int buffer_position = 0; // Current buffer position.
+ previous = unibrow::Utf16::kNoPreviousCharacter;
+ for (int i = 0; i < message.length(); i++) {
+ // Write next UTF-8 encoded character to buffer.
+ uint16_t character = message[i];
+ buffer_position +=
+ unibrow::Utf8::Encode(buffer + buffer_position, character, previous);
+ ASSERT(buffer_position <= kBufferSize);
+
+ // Send buffer if full or last character is encoded.
+ if (kBufferSize - buffer_position <
+ unibrow::Utf16::kMaxExtraUtf8BytesForOneUtf16CodeUnit ||
+ i == message.length() - 1) {
+ if (unibrow::Utf16::IsLeadSurrogate(character)) {
+ const int kEncodedSurrogateLength =
+ unibrow::Utf16::kUtf8BytesToCodeASurrogate;
+ ASSERT(buffer_position >= kEncodedSurrogateLength);
+ conn->Send(buffer, buffer_position - kEncodedSurrogateLength);
+ for (int i = 0; i < kEncodedSurrogateLength; i++) {
+ buffer[i] = buffer[buffer_position + i];
+ }
+ buffer_position = kEncodedSurrogateLength;
+ } else {
+ conn->Send(buffer, buffer_position);
+ buffer_position = 0;
+ }
+ }
+ previous = character;
+ }
+
+ return true;
+}
+
+
+bool DebuggerAgentUtil::SendMessage(const Socket* conn,
+ const v8::Handle<v8::String> request) {
+ static const int kBufferSize = 80;
+ char buffer[kBufferSize]; // Sending buffer both for header and body.
+
+ // Convert the request to UTF-8 encoding.
+ v8::String::Utf8Value utf8_request(request);
+
+ // Send the header.
+ int len;
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize),
+ "Content-Length: %d\r\n", utf8_request.length());
+ conn->Send(buffer, len);
+
+ // Terminate header with empty line.
+ len = OS::SNPrintF(Vector<char>(buffer, kBufferSize), "\r\n");
+ conn->Send(buffer, len);
+
+ // Send message body as UTF-8.
+ conn->Send(*utf8_request, utf8_request.length());
+
+ return true;
+}
+
+
+// Receive the full buffer before returning unless an error occours.
+int DebuggerAgentUtil::ReceiveAll(const Socket* conn, char* data, int len) {
+ int total_received = 0;
+ while (total_received < len) {
+ int received = conn->Receive(data + total_received, len - total_received);
+ if (received == 0) {
+ return total_received;
+ }
+ total_received += received;
+ }
+ return total_received;
+}
+
+} } // namespace v8::internal
+
+#endif // ENABLE_DEBUGGER_SUPPORT
diff --git a/chromium/v8/src/debug-agent.h b/chromium/v8/src/debug-agent.h
new file mode 100644
index 00000000000..61151900f01
--- /dev/null
+++ b/chromium/v8/src/debug-agent.h
@@ -0,0 +1,132 @@
+// 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_DEBUG_AGENT_H_
+#define V8_DEBUG_AGENT_H_
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#include "../include/v8-debug.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward decelrations.
+class DebuggerAgentSession;
+
+
+// Debugger agent which starts a socket listener on the debugger port and
+// handles connection from a remote debugger.
+class DebuggerAgent: public Thread {
+ public:
+ DebuggerAgent(const char* name, int port)
+ : Thread(name),
+ isolate_(Isolate::Current()),
+ name_(StrDup(name)), port_(port),
+ server_(OS::CreateSocket()), terminate_(false),
+ session_access_(OS::CreateMutex()), session_(NULL),
+ terminate_now_(OS::CreateSemaphore(0)),
+ listening_(OS::CreateSemaphore(0)) {
+ ASSERT(isolate_->debugger_agent_instance() == NULL);
+ isolate_->set_debugger_agent_instance(this);
+ }
+ ~DebuggerAgent() {
+ isolate_->set_debugger_agent_instance(NULL);
+ delete server_;
+ }
+
+ void Shutdown();
+ void WaitUntilListening();
+
+ Isolate* isolate() { return isolate_; }
+
+ private:
+ void Run();
+ void CreateSession(Socket* socket);
+ void DebuggerMessage(const v8::Debug::Message& message);
+ void CloseSession();
+ void OnSessionClosed(DebuggerAgentSession* session);
+
+ Isolate* isolate_;
+ SmartArrayPointer<const char> name_; // Name of the embedding application.
+ int port_; // Port to use for the agent.
+ Socket* server_; // Server socket for listen/accept.
+ bool terminate_; // Termination flag.
+ Mutex* session_access_; // Mutex guarging access to session_.
+ DebuggerAgentSession* session_; // Current active session if any.
+ Semaphore* terminate_now_; // Semaphore to signal termination.
+ Semaphore* listening_;
+
+ friend class DebuggerAgentSession;
+ friend void DebuggerAgentMessageHandler(const v8::Debug::Message& message);
+
+ DISALLOW_COPY_AND_ASSIGN(DebuggerAgent);
+};
+
+
+// Debugger agent session. The session receives requests from the remote
+// debugger and sends debugger events/responses to the remote debugger.
+class DebuggerAgentSession: public Thread {
+ public:
+ DebuggerAgentSession(DebuggerAgent* agent, Socket* client)
+ : Thread("v8:DbgAgntSessn"),
+ agent_(agent), client_(client) {}
+
+ void DebuggerMessage(Vector<uint16_t> message);
+ void Shutdown();
+
+ private:
+ void Run();
+
+ void DebuggerMessage(Vector<char> message);
+
+ DebuggerAgent* agent_;
+ Socket* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(DebuggerAgentSession);
+};
+
+
+// Utility methods factored out to be used by the D8 shell as well.
+class DebuggerAgentUtil {
+ public:
+ static const char* const kContentLength;
+
+ static SmartArrayPointer<char> ReceiveMessage(const Socket* conn);
+ static bool SendConnectMessage(const Socket* conn,
+ const char* embedding_host);
+ static bool SendMessage(const Socket* conn, const Vector<uint16_t> message);
+ static bool SendMessage(const Socket* conn,
+ const v8::Handle<v8::String> message);
+ static int ReceiveAll(const Socket* conn, char* data, int len);
+};
+
+} } // namespace v8::internal
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+#endif // V8_DEBUG_AGENT_H_
diff --git a/chromium/v8/src/debug-debugger.js b/chromium/v8/src/debug-debugger.js
new file mode 100644
index 00000000000..a588b4c21d7
--- /dev/null
+++ b/chromium/v8/src/debug-debugger.js
@@ -0,0 +1,2638 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Default number of frames to include in the response to backtrace request.
+var kDefaultBacktraceLength = 10;
+
+var Debug = {};
+
+// Regular expression to skip "crud" at the beginning of a source line which is
+// not really code. Currently the regular expression matches whitespace and
+// comments.
+var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
+
+// Debug events which can occour in the V8 JavaScript engine. These originate
+// from the API include file debug.h.
+Debug.DebugEvent = { Break: 1,
+ Exception: 2,
+ NewFunction: 3,
+ BeforeCompile: 4,
+ AfterCompile: 5,
+ ScriptCollected: 6 };
+
+// Types of exceptions that can be broken upon.
+Debug.ExceptionBreak = { Caught : 0,
+ Uncaught: 1 };
+
+// The different types of steps.
+Debug.StepAction = { StepOut: 0,
+ StepNext: 1,
+ StepIn: 2,
+ StepMin: 3,
+ StepInMin: 4 };
+
+// The different types of scripts matching enum ScriptType in objects.h.
+Debug.ScriptType = { Native: 0,
+ Extension: 1,
+ Normal: 2 };
+
+// The different types of script compilations matching enum
+// Script::CompilationType in objects.h.
+Debug.ScriptCompilationType = { Host: 0,
+ Eval: 1,
+ JSON: 2 };
+
+// The different script break point types.
+Debug.ScriptBreakPointType = { ScriptId: 0,
+ ScriptName: 1,
+ ScriptRegExp: 2 };
+
+// The different types of breakpoint position alignments.
+// Must match BreakPositionAlignment in debug.h.
+Debug.BreakPositionAlignment = {
+ Statement: 0,
+ BreakPosition: 1
+};
+
+function ScriptTypeFlag(type) {
+ return (1 << type);
+}
+
+// Globals.
+var next_response_seq = 0;
+var next_break_point_number = 1;
+var break_points = [];
+var script_break_points = [];
+var debugger_flags = {
+ breakPointsActive: {
+ value: true,
+ getValue: function() { return this.value; },
+ setValue: function(value) {
+ this.value = !!value;
+ %SetDisableBreak(!this.value);
+ }
+ },
+ breakOnCaughtException: {
+ getValue: function() { return Debug.isBreakOnException(); },
+ setValue: function(value) {
+ if (value) {
+ Debug.setBreakOnException();
+ } else {
+ Debug.clearBreakOnException();
+ }
+ }
+ },
+ breakOnUncaughtException: {
+ getValue: function() { return Debug.isBreakOnUncaughtException(); },
+ setValue: function(value) {
+ if (value) {
+ Debug.setBreakOnUncaughtException();
+ } else {
+ Debug.clearBreakOnUncaughtException();
+ }
+ }
+ },
+};
+
+
+// Create a new break point object and add it to the list of break points.
+function MakeBreakPoint(source_position, opt_script_break_point) {
+ var break_point = new BreakPoint(source_position, opt_script_break_point);
+ break_points.push(break_point);
+ return break_point;
+}
+
+
+// Object representing a break point.
+// NOTE: This object does not have a reference to the function having break
+// point as this would cause function not to be garbage collected when it is
+// not used any more. We do not want break points to keep functions alive.
+function BreakPoint(source_position, opt_script_break_point) {
+ this.source_position_ = source_position;
+ if (opt_script_break_point) {
+ this.script_break_point_ = opt_script_break_point;
+ } else {
+ this.number_ = next_break_point_number++;
+ }
+ this.hit_count_ = 0;
+ this.active_ = true;
+ this.condition_ = null;
+ this.ignoreCount_ = 0;
+}
+
+
+BreakPoint.prototype.number = function() {
+ return this.number_;
+};
+
+
+BreakPoint.prototype.func = function() {
+ return this.func_;
+};
+
+
+BreakPoint.prototype.source_position = function() {
+ return this.source_position_;
+};
+
+
+BreakPoint.prototype.hit_count = function() {
+ return this.hit_count_;
+};
+
+
+BreakPoint.prototype.active = function() {
+ if (this.script_break_point()) {
+ return this.script_break_point().active();
+ }
+ return this.active_;
+};
+
+
+BreakPoint.prototype.condition = function() {
+ if (this.script_break_point() && this.script_break_point().condition()) {
+ return this.script_break_point().condition();
+ }
+ return this.condition_;
+};
+
+
+BreakPoint.prototype.ignoreCount = function() {
+ return this.ignoreCount_;
+};
+
+
+BreakPoint.prototype.script_break_point = function() {
+ return this.script_break_point_;
+};
+
+
+BreakPoint.prototype.enable = function() {
+ this.active_ = true;
+};
+
+
+BreakPoint.prototype.disable = function() {
+ this.active_ = false;
+};
+
+
+BreakPoint.prototype.setCondition = function(condition) {
+ this.condition_ = condition;
+};
+
+
+BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
+ this.ignoreCount_ = ignoreCount;
+};
+
+
+BreakPoint.prototype.isTriggered = function(exec_state) {
+ // Break point not active - not triggered.
+ if (!this.active()) return false;
+
+ // Check for conditional break point.
+ if (this.condition()) {
+ // If break point has condition try to evaluate it in the top frame.
+ try {
+ var mirror = exec_state.frame(0).evaluate(this.condition());
+ // If no sensible mirror or non true value break point not triggered.
+ if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) {
+ return false;
+ }
+ } catch (e) {
+ // Exception evaluating condition counts as not triggered.
+ return false;
+ }
+ }
+
+ // Update the hit count.
+ this.hit_count_++;
+ if (this.script_break_point_) {
+ this.script_break_point_.hit_count_++;
+ }
+
+ // If the break point has an ignore count it is not triggered.
+ if (this.ignoreCount_ > 0) {
+ this.ignoreCount_--;
+ return false;
+ }
+
+ // Break point triggered.
+ return true;
+};
+
+
+// Function called from the runtime when a break point is hit. Returns true if
+// the break point is triggered and supposed to break execution.
+function IsBreakPointTriggered(break_id, break_point) {
+ return break_point.isTriggered(MakeExecutionState(break_id));
+}
+
+
+// Object representing a script break point. The script is referenced by its
+// script name or script id and the break point is represented as line and
+// column.
+function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
+ opt_groupId, opt_position_alignment) {
+ this.type_ = type;
+ if (type == Debug.ScriptBreakPointType.ScriptId) {
+ this.script_id_ = script_id_or_name;
+ } else if (type == Debug.ScriptBreakPointType.ScriptName) {
+ this.script_name_ = script_id_or_name;
+ } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) {
+ this.script_regexp_object_ = new RegExp(script_id_or_name);
+ } else {
+ throw new Error("Unexpected breakpoint type " + type);
+ }
+ this.line_ = opt_line || 0;
+ this.column_ = opt_column;
+ this.groupId_ = opt_groupId;
+ this.position_alignment_ = IS_UNDEFINED(opt_position_alignment)
+ ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
+ this.hit_count_ = 0;
+ this.active_ = true;
+ this.condition_ = null;
+ this.ignoreCount_ = 0;
+ this.break_points_ = [];
+}
+
+
+//Creates a clone of script breakpoint that is linked to another script.
+ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) {
+ var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
+ other_script.id, this.line_, this.column_, this.groupId_,
+ this.position_alignment_);
+ copy.number_ = next_break_point_number++;
+ script_break_points.push(copy);
+
+ copy.hit_count_ = this.hit_count_;
+ copy.active_ = this.active_;
+ copy.condition_ = this.condition_;
+ copy.ignoreCount_ = this.ignoreCount_;
+ return copy;
+};
+
+
+ScriptBreakPoint.prototype.number = function() {
+ return this.number_;
+};
+
+
+ScriptBreakPoint.prototype.groupId = function() {
+ return this.groupId_;
+};
+
+
+ScriptBreakPoint.prototype.type = function() {
+ return this.type_;
+};
+
+
+ScriptBreakPoint.prototype.script_id = function() {
+ return this.script_id_;
+};
+
+
+ScriptBreakPoint.prototype.script_name = function() {
+ return this.script_name_;
+};
+
+
+ScriptBreakPoint.prototype.script_regexp_object = function() {
+ return this.script_regexp_object_;
+};
+
+
+ScriptBreakPoint.prototype.line = function() {
+ return this.line_;
+};
+
+
+ScriptBreakPoint.prototype.column = function() {
+ return this.column_;
+};
+
+
+ScriptBreakPoint.prototype.actual_locations = function() {
+ var locations = [];
+ for (var i = 0; i < this.break_points_.length; i++) {
+ 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() {
+ return this.hit_count_;
+};
+
+
+ScriptBreakPoint.prototype.active = function() {
+ return this.active_;
+};
+
+
+ScriptBreakPoint.prototype.condition = function() {
+ return this.condition_;
+};
+
+
+ScriptBreakPoint.prototype.ignoreCount = function() {
+ return this.ignoreCount_;
+};
+
+
+ScriptBreakPoint.prototype.enable = function() {
+ this.active_ = true;
+};
+
+
+ScriptBreakPoint.prototype.disable = function() {
+ this.active_ = false;
+};
+
+
+ScriptBreakPoint.prototype.setCondition = function(condition) {
+ this.condition_ = condition;
+};
+
+
+ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
+ this.ignoreCount_ = ignoreCount;
+
+ // Set ignore count on all break points created from this script break point.
+ for (var i = 0; i < this.break_points_.length; i++) {
+ this.break_points_[i].setIgnoreCount(ignoreCount);
+ }
+};
+
+
+// Check whether a script matches this script break point. Currently this is
+// only based on script name.
+ScriptBreakPoint.prototype.matchesScript = function(script) {
+ if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
+ return this.script_id_ == script.id;
+ } else {
+ // We might want to account columns here as well.
+ if (!(script.line_offset <= this.line_ &&
+ this.line_ < script.line_offset + script.lineCount())) {
+ return false;
+ }
+ if (this.type_ == Debug.ScriptBreakPointType.ScriptName) {
+ return this.script_name_ == script.nameOrSourceURL();
+ } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) {
+ return this.script_regexp_object_.test(script.nameOrSourceURL());
+ } else {
+ throw new Error("Unexpected breakpoint type " + this.type_);
+ }
+ }
+};
+
+
+// Set the script break point in a script.
+ScriptBreakPoint.prototype.set = function (script) {
+ var column = this.column();
+ var line = this.line();
+ // If the column is undefined the break is on the line. To help locate the
+ // first piece of breakable code on the line try to find the column on the
+ // line which contains some source.
+ if (IS_UNDEFINED(column)) {
+ var source_line = script.sourceLine(this.line());
+
+ // Allocate array for caching the columns where the actual source starts.
+ if (!script.sourceColumnStart_) {
+ script.sourceColumnStart_ = new Array(script.lineCount());
+ }
+
+ // Fill cache if needed and get column where the actual source starts.
+ if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
+ script.sourceColumnStart_[line] =
+ source_line.match(sourceLineBeginningSkip)[0].length;
+ }
+ column = script.sourceColumnStart_[line];
+ }
+
+ // Convert the line and column into an absolute position within the script.
+ var position = Debug.findScriptSourcePosition(script, this.line(), column);
+
+ // If the position is not found in the script (the script might be shorter
+ // than it used to be) just ignore it.
+ if (position === null) return;
+
+ // Create a break point object and set the break point.
+ break_point = MakeBreakPoint(position, this);
+ break_point.setIgnoreCount(this.ignoreCount());
+ var actual_position = %SetScriptBreakPoint(script, position,
+ this.position_alignment_,
+ break_point);
+ if (IS_UNDEFINED(actual_position)) {
+ actual_position = position;
+ }
+ var actual_location = script.locationFromPosition(actual_position, true);
+ break_point.actual_location = { line: actual_location.line,
+ column: actual_location.column,
+ script_id: script.id };
+ this.break_points_.push(break_point);
+ return break_point;
+};
+
+
+// Clear all the break points created from this script break point
+ScriptBreakPoint.prototype.clear = function () {
+ var remaining_break_points = [];
+ for (var i = 0; i < break_points.length; i++) {
+ if (break_points[i].script_break_point() &&
+ break_points[i].script_break_point() === this) {
+ %ClearBreakPoint(break_points[i]);
+ } else {
+ remaining_break_points.push(break_points[i]);
+ }
+ }
+ break_points = remaining_break_points;
+ this.break_points_ = [];
+};
+
+
+// Function called from runtime when a new script is compiled to set any script
+// break points set in this script.
+function UpdateScriptBreakPoints(script) {
+ for (var i = 0; i < script_break_points.length; i++) {
+ var break_point = script_break_points[i];
+ if ((break_point.type() == Debug.ScriptBreakPointType.ScriptName ||
+ break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) &&
+ break_point.matchesScript(script)) {
+ break_point.set(script);
+ }
+ }
+}
+
+
+function GetScriptBreakPoints(script) {
+ var result = [];
+ for (var i = 0; i < script_break_points.length; i++) {
+ if (script_break_points[i].matchesScript(script)) {
+ result.push(script_break_points[i]);
+ }
+ }
+ return result;
+}
+
+
+Debug.setListener = function(listener, opt_data) {
+ if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
+ throw new Error('Parameters have wrong types.');
+ }
+ %SetDebugEventListener(listener, opt_data);
+};
+
+
+Debug.breakExecution = function(f) {
+ %Break();
+};
+
+Debug.breakLocations = function(f, opt_position_aligment) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ var position_aligment = IS_UNDEFINED(opt_position_aligment)
+ ? Debug.BreakPositionAlignment.Statement : opt_position_aligment;
+ return %GetBreakLocations(f, position_aligment);
+};
+
+// Returns a Script object. If the parameter is a function the return value
+// is the script in which the function is defined. If the parameter is a string
+// the return value is the script for which the script name has that string
+// value. If it is a regexp and there is a unique script whose name matches
+// we return that, otherwise undefined.
+Debug.findScript = function(func_or_script_name) {
+ if (IS_FUNCTION(func_or_script_name)) {
+ return %FunctionGetScript(func_or_script_name);
+ } else if (IS_REGEXP(func_or_script_name)) {
+ var scripts = Debug.scripts();
+ var last_result = null;
+ var result_count = 0;
+ for (var i in scripts) {
+ var script = scripts[i];
+ if (func_or_script_name.test(script.name)) {
+ last_result = script;
+ result_count++;
+ }
+ }
+ // Return the unique script matching the regexp. If there are more
+ // than one we don't return a value since there is no good way to
+ // decide which one to return. Returning a "random" one, say the
+ // first, would introduce nondeterminism (or something close to it)
+ // because the order is the heap iteration order.
+ if (result_count == 1) {
+ return last_result;
+ } else {
+ return undefined;
+ }
+ } else {
+ return %GetScript(func_or_script_name);
+ }
+};
+
+// Returns the script source. If the parameter is a function the return value
+// is the script source for the script in which the function is defined. If the
+// parameter is a string the return value is the script for which the script
+// name has that string value.
+Debug.scriptSource = function(func_or_script_name) {
+ return this.findScript(func_or_script_name).source;
+};
+
+Debug.source = function(f) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ return %FunctionGetSourceCode(f);
+};
+
+Debug.disassemble = function(f) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ return %DebugDisassembleFunction(f);
+};
+
+Debug.disassembleConstructor = function(f) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ return %DebugDisassembleConstructor(f);
+};
+
+Debug.ExecuteInDebugContext = function(f, without_debugger) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ return %ExecuteInDebugContext(f, !!without_debugger);
+};
+
+Debug.sourcePosition = function(f) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ return %FunctionGetScriptSourcePosition(f);
+};
+
+
+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
+// optional position within that line.
+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) {
+ var break_point;
+ for (var i = 0; i < break_points.length; i++) {
+ if (break_points[i].number() == break_point_number) {
+ break_point = break_points[i];
+ // Remove the break point from the list if requested.
+ if (remove) {
+ break_points.splice(i, 1);
+ }
+ break;
+ }
+ }
+ if (break_point) {
+ return break_point;
+ } else {
+ return this.findScriptBreakPoint(break_point_number, remove);
+ }
+};
+
+Debug.findBreakPointActualLocations = function(break_point_number) {
+ for (var i = 0; i < script_break_points.length; i++) {
+ if (script_break_points[i].number() == break_point_number) {
+ return script_break_points[i].actual_locations();
+ }
+ }
+ for (var i = 0; i < break_points.length; i++) {
+ if (break_points[i].number() == break_point_number) {
+ return [break_points[i].actual_location];
+ }
+ }
+ return [];
+};
+
+Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
+ if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
+ // Break points in API functions are not supported.
+ if (%FunctionIsAPIFunction(func)) {
+ throw new Error('Cannot set break point in native code.');
+ }
+ // Find source position relative to start of the function
+ var break_position =
+ this.findFunctionSourceLocation(func, opt_line, opt_column).position;
+ var source_position = break_position - this.sourcePosition(func);
+ // Find the script for the function.
+ var script = %FunctionGetScript(func);
+ // Break in builtin JavaScript code is not supported.
+ if (script.type == Debug.ScriptType.Native) {
+ throw new Error('Cannot set break point in native code.');
+ }
+ // If the script for the function has a name convert this to a script break
+ // point.
+ if (script && script.id) {
+ // Adjust the source position to be script relative.
+ source_position += %FunctionGetScriptSourcePosition(func);
+ // Find line and column for the position in the script and set a script
+ // break point from that.
+ var location = script.locationFromPosition(source_position, false);
+ return this.setScriptBreakPointById(script.id,
+ location.line, location.column,
+ opt_condition);
+ } else {
+ // Set a break point directly on the function.
+ var break_point = MakeBreakPoint(source_position);
+ var actual_position =
+ %SetFunctionBreakPoint(func, source_position, break_point);
+ actual_position += this.sourcePosition(func);
+ var actual_location = script.locationFromPosition(actual_position, true);
+ break_point.actual_location = { line: actual_location.line,
+ column: actual_location.column,
+ script_id: script.id };
+ break_point.setCondition(opt_condition);
+ return break_point.number();
+ }
+};
+
+
+Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
+ condition, enabled,
+ opt_position_alignment)
+{
+ break_point = MakeBreakPoint(position);
+ break_point.setCondition(condition);
+ if (!enabled) {
+ break_point.disable();
+ }
+ var scripts = this.scripts();
+ var position_alignment = IS_UNDEFINED(opt_position_alignment)
+ ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
+ for (var i = 0; i < scripts.length; i++) {
+ if (script_id == scripts[i].id) {
+ break_point.actual_position = %SetScriptBreakPoint(scripts[i], position,
+ position_alignment, break_point);
+ break;
+ }
+ }
+ return break_point;
+};
+
+
+Debug.enableBreakPoint = function(break_point_number) {
+ var break_point = this.findBreakPoint(break_point_number, false);
+ // Only enable if the breakpoint hasn't been deleted:
+ if (break_point) {
+ break_point.enable();
+ }
+};
+
+
+Debug.disableBreakPoint = function(break_point_number) {
+ var break_point = this.findBreakPoint(break_point_number, false);
+ // Only enable if the breakpoint hasn't been deleted:
+ if (break_point) {
+ break_point.disable();
+ }
+};
+
+
+Debug.changeBreakPointCondition = function(break_point_number, condition) {
+ var break_point = this.findBreakPoint(break_point_number, false);
+ break_point.setCondition(condition);
+};
+
+
+Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
+ if (ignoreCount < 0) {
+ throw new Error('Invalid argument');
+ }
+ var break_point = this.findBreakPoint(break_point_number, false);
+ break_point.setIgnoreCount(ignoreCount);
+};
+
+
+Debug.clearBreakPoint = function(break_point_number) {
+ var break_point = this.findBreakPoint(break_point_number, true);
+ if (break_point) {
+ return %ClearBreakPoint(break_point);
+ } else {
+ break_point = this.findScriptBreakPoint(break_point_number, true);
+ if (!break_point) {
+ throw new Error('Invalid breakpoint');
+ }
+ }
+};
+
+
+Debug.clearAllBreakPoints = function() {
+ for (var i = 0; i < break_points.length; i++) {
+ break_point = break_points[i];
+ %ClearBreakPoint(break_point);
+ }
+ break_points = [];
+};
+
+
+Debug.disableAllBreakPoints = function() {
+ // Disable all user defined breakpoints:
+ for (var i = 1; i < next_break_point_number; i++) {
+ Debug.disableBreakPoint(i);
+ }
+ // Disable all exception breakpoints:
+ %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
+ %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
+};
+
+
+Debug.findScriptBreakPoint = function(break_point_number, remove) {
+ var script_break_point;
+ for (var i = 0; i < script_break_points.length; i++) {
+ if (script_break_points[i].number() == break_point_number) {
+ script_break_point = script_break_points[i];
+ // Remove the break point from the list if requested.
+ if (remove) {
+ script_break_point.clear();
+ script_break_points.splice(i,1);
+ }
+ break;
+ }
+ }
+ return script_break_point;
+};
+
+
+// Sets a breakpoint in a script identified through id or name at the
+// specified source line and column within that line.
+Debug.setScriptBreakPoint = function(type, script_id_or_name,
+ opt_line, opt_column, opt_condition,
+ opt_groupId, opt_position_alignment) {
+ // Create script break point object.
+ var script_break_point =
+ new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
+ opt_groupId, opt_position_alignment);
+
+ // Assign number to the new script break point and add it.
+ script_break_point.number_ = next_break_point_number++;
+ script_break_point.setCondition(opt_condition);
+ script_break_points.push(script_break_point);
+
+ // Run through all scripts to see if this script break point matches any
+ // loaded scripts.
+ var scripts = this.scripts();
+ for (var i = 0; i < scripts.length; i++) {
+ if (script_break_point.matchesScript(scripts[i])) {
+ script_break_point.set(scripts[i]);
+ }
+ }
+
+ return script_break_point.number();
+};
+
+
+Debug.setScriptBreakPointById = function(script_id,
+ opt_line, opt_column,
+ opt_condition, opt_groupId,
+ opt_position_alignment) {
+ return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
+ script_id, opt_line, opt_column,
+ opt_condition, opt_groupId,
+ opt_position_alignment);
+};
+
+
+Debug.setScriptBreakPointByName = function(script_name,
+ opt_line, opt_column,
+ opt_condition, opt_groupId) {
+ return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
+ script_name, opt_line, opt_column,
+ opt_condition, opt_groupId);
+};
+
+
+Debug.setScriptBreakPointByRegExp = function(script_regexp,
+ opt_line, opt_column,
+ opt_condition, opt_groupId) {
+ return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp,
+ script_regexp, opt_line, opt_column,
+ opt_condition, opt_groupId);
+};
+
+
+Debug.enableScriptBreakPoint = function(break_point_number) {
+ var script_break_point = this.findScriptBreakPoint(break_point_number, false);
+ script_break_point.enable();
+};
+
+
+Debug.disableScriptBreakPoint = function(break_point_number) {
+ var script_break_point = this.findScriptBreakPoint(break_point_number, false);
+ script_break_point.disable();
+};
+
+
+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) {
+ if (ignoreCount < 0) {
+ throw new Error('Invalid argument');
+ }
+ var script_break_point = this.findScriptBreakPoint(break_point_number, false);
+ script_break_point.setIgnoreCount(ignoreCount);
+};
+
+
+Debug.scriptBreakPoints = function() {
+ return script_break_points;
+};
+
+
+Debug.clearStepping = function() {
+ %ClearStepping();
+};
+
+Debug.setBreakOnException = function() {
+ return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true);
+};
+
+Debug.clearBreakOnException = function() {
+ return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
+};
+
+Debug.isBreakOnException = function() {
+ return !!%IsBreakOnException(Debug.ExceptionBreak.Caught);
+};
+
+Debug.setBreakOnUncaughtException = function() {
+ return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
+};
+
+Debug.clearBreakOnUncaughtException = function() {
+ return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
+};
+
+Debug.isBreakOnUncaughtException = function() {
+ return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught);
+};
+
+Debug.showBreakPoints = function(f, full, opt_position_alignment) {
+ if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
+ var source = full ? this.scriptSource(f) : this.source(f);
+ var offset = full ? this.sourcePosition(f) : 0;
+ var locations = this.breakLocations(f, opt_position_alignment);
+ if (!locations) return source;
+ locations.sort(function(x, y) { return x - y; });
+ var result = "";
+ var prev_pos = 0;
+ var pos;
+ for (var i = 0; i < locations.length; i++) {
+ pos = locations[i] - offset;
+ result += source.slice(prev_pos, pos);
+ result += "[B" + i + "]";
+ prev_pos = pos;
+ }
+ pos = source.length;
+ result += source.substring(prev_pos, pos);
+ return result;
+};
+
+
+// Get all the scripts currently loaded. Locating all the scripts is based on
+// scanning the heap.
+Debug.scripts = function() {
+ // Collect all scripts in the heap.
+ return %DebugGetLoadedScripts();
+};
+
+
+Debug.debuggerFlags = function() {
+ return debugger_flags;
+};
+
+Debug.MakeMirror = MakeMirror;
+
+function MakeExecutionState(break_id) {
+ return new ExecutionState(break_id);
+}
+
+function ExecutionState(break_id) {
+ this.break_id = break_id;
+ this.selected_frame = 0;
+}
+
+ExecutionState.prototype.prepareStep = function(opt_action, opt_count) {
+ var action = Debug.StepAction.StepIn;
+ if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action);
+ 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) {
+ return MakeMirror(%DebugEvaluateGlobal(this.break_id, source,
+ Boolean(disable_break),
+ opt_additional_context));
+};
+
+ExecutionState.prototype.frameCount = function() {
+ return %GetFrameCount(this.break_id);
+};
+
+ExecutionState.prototype.threadCount = function() {
+ return %GetThreadCount(this.break_id);
+};
+
+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()) {
+ throw new Error('Illegal frame index.');
+ }
+ return new FrameMirror(this.break_id, opt_index);
+};
+
+ExecutionState.prototype.setSelectedFrame = function(index) {
+ var i = %ToNumber(index);
+ if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
+ this.selected_frame = i;
+};
+
+ExecutionState.prototype.selectedFrame = function() {
+ return this.selected_frame;
+};
+
+ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
+ return new DebugCommandProcessor(this, opt_is_running);
+};
+
+
+function MakeBreakEvent(exec_state, break_points_hit) {
+ return new BreakEvent(exec_state, break_points_hit);
+}
+
+
+function BreakEvent(exec_state, break_points_hit) {
+ this.exec_state_ = exec_state;
+ this.break_points_hit_ = break_points_hit;
+}
+
+
+BreakEvent.prototype.executionState = function() {
+ return this.exec_state_;
+};
+
+
+BreakEvent.prototype.eventType = function() {
+ return Debug.DebugEvent.Break;
+};
+
+
+BreakEvent.prototype.func = function() {
+ return this.exec_state_.frame(0).func();
+};
+
+
+BreakEvent.prototype.sourceLine = function() {
+ return this.exec_state_.frame(0).sourceLine();
+};
+
+
+BreakEvent.prototype.sourceColumn = function() {
+ return this.exec_state_.frame(0).sourceColumn();
+};
+
+
+BreakEvent.prototype.sourceLineText = function() {
+ return this.exec_state_.frame(0).sourceLineText();
+};
+
+
+BreakEvent.prototype.breakPointsHit = function() {
+ return this.break_points_hit_;
+};
+
+
+BreakEvent.prototype.toJSONProtocol = function() {
+ var o = { seq: next_response_seq++,
+ type: "event",
+ event: "break",
+ body: { invocationText: this.exec_state_.frame(0).invocationText(),
+ }
+ };
+
+ // Add script related information to the event if available.
+ var script = this.func().script();
+ if (script) {
+ o.body.sourceLine = this.sourceLine(),
+ o.body.sourceColumn = this.sourceColumn(),
+ o.body.sourceLineText = this.sourceLineText(),
+ o.body.script = MakeScriptObject_(script, false);
+ }
+
+ // Add an Array of break points hit if any.
+ if (this.breakPointsHit()) {
+ o.body.breakpoints = [];
+ for (var i = 0; i < this.breakPointsHit().length; i++) {
+ // Find the break point number. For break points originating from a
+ // script break point supply the script break point number.
+ var breakpoint = this.breakPointsHit()[i];
+ var script_break_point = breakpoint.script_break_point();
+ var number;
+ if (script_break_point) {
+ number = script_break_point.number();
+ } else {
+ number = breakpoint.number();
+ }
+ o.body.breakpoints.push(number);
+ }
+ }
+ return JSON.stringify(ObjectToProtocolObject_(o));
+};
+
+
+function MakeExceptionEvent(exec_state, exception, uncaught) {
+ return new ExceptionEvent(exec_state, exception, uncaught);
+}
+
+
+function ExceptionEvent(exec_state, exception, uncaught) {
+ this.exec_state_ = exec_state;
+ this.exception_ = exception;
+ this.uncaught_ = uncaught;
+}
+
+
+ExceptionEvent.prototype.executionState = function() {
+ return this.exec_state_;
+};
+
+
+ExceptionEvent.prototype.eventType = function() {
+ return Debug.DebugEvent.Exception;
+};
+
+
+ExceptionEvent.prototype.exception = function() {
+ return this.exception_;
+};
+
+
+ExceptionEvent.prototype.uncaught = function() {
+ return this.uncaught_;
+};
+
+
+ExceptionEvent.prototype.func = function() {
+ return this.exec_state_.frame(0).func();
+};
+
+
+ExceptionEvent.prototype.sourceLine = function() {
+ return this.exec_state_.frame(0).sourceLine();
+};
+
+
+ExceptionEvent.prototype.sourceColumn = function() {
+ return this.exec_state_.frame(0).sourceColumn();
+};
+
+
+ExceptionEvent.prototype.sourceLineText = function() {
+ return this.exec_state_.frame(0).sourceLineText();
+};
+
+
+ExceptionEvent.prototype.toJSONProtocol = function() {
+ var o = new ProtocolMessage();
+ o.event = "exception";
+ o.body = { uncaught: this.uncaught_,
+ exception: MakeMirror(this.exception_)
+ };
+
+ // Exceptions might happen whithout any JavaScript frames.
+ if (this.exec_state_.frameCount() > 0) {
+ o.body.sourceLine = this.sourceLine();
+ o.body.sourceColumn = this.sourceColumn();
+ o.body.sourceLineText = this.sourceLineText();
+
+ // Add script information to the event if available.
+ var script = this.func().script();
+ if (script) {
+ o.body.script = MakeScriptObject_(script, false);
+ }
+ } else {
+ o.body.sourceLine = -1;
+ }
+
+ return o.toJSONProtocol();
+};
+
+
+function MakeCompileEvent(exec_state, script, before) {
+ return new CompileEvent(exec_state, script, before);
+}
+
+
+function CompileEvent(exec_state, script, before) {
+ this.exec_state_ = exec_state;
+ this.script_ = MakeMirror(script);
+ this.before_ = before;
+}
+
+
+CompileEvent.prototype.executionState = function() {
+ return this.exec_state_;
+};
+
+
+CompileEvent.prototype.eventType = function() {
+ if (this.before_) {
+ return Debug.DebugEvent.BeforeCompile;
+ } else {
+ return Debug.DebugEvent.AfterCompile;
+ }
+};
+
+
+CompileEvent.prototype.script = function() {
+ return this.script_;
+};
+
+
+CompileEvent.prototype.toJSONProtocol = function() {
+ var o = new ProtocolMessage();
+ o.running = true;
+ if (this.before_) {
+ o.event = "beforeCompile";
+ } else {
+ o.event = "afterCompile";
+ }
+ o.body = {};
+ o.body.script = this.script_;
+
+ return o.toJSONProtocol();
+};
+
+
+function MakeNewFunctionEvent(func) {
+ return new NewFunctionEvent(func);
+}
+
+
+function NewFunctionEvent(func) {
+ this.func = func;
+}
+
+
+NewFunctionEvent.prototype.eventType = function() {
+ return Debug.DebugEvent.NewFunction;
+};
+
+
+NewFunctionEvent.prototype.name = function() {
+ return this.func.name;
+};
+
+
+NewFunctionEvent.prototype.setBreakPoint = function(p) {
+ Debug.setBreakPoint(this.func, p || 0);
+};
+
+
+function MakeScriptCollectedEvent(exec_state, id) {
+ return new ScriptCollectedEvent(exec_state, id);
+}
+
+
+function ScriptCollectedEvent(exec_state, id) {
+ this.exec_state_ = exec_state;
+ this.id_ = id;
+}
+
+
+ScriptCollectedEvent.prototype.id = function() {
+ return this.id_;
+};
+
+
+ScriptCollectedEvent.prototype.executionState = function() {
+ return this.exec_state_;
+};
+
+
+ScriptCollectedEvent.prototype.toJSONProtocol = function() {
+ var o = new ProtocolMessage();
+ o.running = true;
+ o.event = "scriptCollected";
+ o.body = {};
+ o.body.script = { id: this.id() };
+ return o.toJSONProtocol();
+};
+
+
+function MakeScriptObject_(script, include_source) {
+ var o = { id: script.id(),
+ name: script.name(),
+ lineOffset: script.lineOffset(),
+ columnOffset: script.columnOffset(),
+ lineCount: script.lineCount(),
+ };
+ if (!IS_UNDEFINED(script.data())) {
+ o.data = script.data();
+ }
+ if (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) {
+ // Update sequence number.
+ this.seq = next_response_seq++;
+
+ if (request) {
+ // If message is based on a request this is a response. Fill the initial
+ // response from the request.
+ this.type = 'response';
+ this.request_seq = request.seq;
+ this.command = request.command;
+ } else {
+ // If message is not based on a request it is a dabugger generated event.
+ this.type = 'event';
+ }
+ this.success = true;
+ // Handler may set this field to control debugger state.
+ this.running = undefined;
+}
+
+
+ProtocolMessage.prototype.setOption = function(name, value) {
+ if (!this.options_) {
+ this.options_ = {};
+ }
+ this.options_[name] = value;
+};
+
+
+ProtocolMessage.prototype.failed = function(message, opt_details) {
+ this.success = false;
+ this.message = message;
+ if (IS_OBJECT(opt_details)) {
+ this.error_details = opt_details;
+ }
+};
+
+
+ProtocolMessage.prototype.toJSONProtocol = function() {
+ // Encode the protocol header.
+ var json = {};
+ json.seq= this.seq;
+ if (this.request_seq) {
+ json.request_seq = this.request_seq;
+ }
+ json.type = this.type;
+ if (this.event) {
+ json.event = this.event;
+ }
+ if (this.command) {
+ json.command = this.command;
+ }
+ if (this.success) {
+ json.success = this.success;
+ } else {
+ json.success = false;
+ }
+ if (this.body) {
+ // Encode the body part.
+ var bodyJson;
+ var serializer = MakeMirrorSerializer(true, this.options_);
+ if (this.body instanceof Mirror) {
+ bodyJson = serializer.serializeValue(this.body);
+ } else if (this.body instanceof Array) {
+ bodyJson = [];
+ for (var i = 0; i < this.body.length; i++) {
+ if (this.body[i] instanceof Mirror) {
+ bodyJson.push(serializer.serializeValue(this.body[i]));
+ } else {
+ bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer));
+ }
+ }
+ } else {
+ bodyJson = ObjectToProtocolObject_(this.body, serializer);
+ }
+ json.body = bodyJson;
+ json.refs = serializer.serializeReferencedObjects();
+ }
+ if (this.message) {
+ json.message = this.message;
+ }
+ if (this.error_details) {
+ json.error_details = this.error_details;
+ }
+ json.running = this.running;
+ return JSON.stringify(json);
+};
+
+
+DebugCommandProcessor.prototype.createResponse = function(request) {
+ return new ProtocolMessage(request);
+};
+
+
+DebugCommandProcessor.prototype.processDebugJSONRequest = function(
+ json_request) {
+ var request; // Current request.
+ var response; // Generated response.
+ try {
+ try {
+ // Convert the JSON string to an object.
+ request = JSON.parse(json_request);
+
+ // Create an initial response.
+ response = this.createResponse(request);
+
+ if (!request.type) {
+ throw new Error('Type not specified');
+ }
+
+ if (request.type != 'request') {
+ throw new Error("Illegal type '" + request.type + "' in request");
+ }
+
+ if (!request.command) {
+ throw new Error('Command not specified');
+ }
+
+ if (request.arguments) {
+ var args = request.arguments;
+ // TODO(yurys): remove request.arguments.compactFormat check once
+ // ChromeDevTools are switched to 'inlineRefs'
+ if (args.inlineRefs || args.compactFormat) {
+ response.setOption('inlineRefs', true);
+ }
+ if (!IS_UNDEFINED(args.maxStringLength)) {
+ response.setOption('maxStringLength', args.maxStringLength);
+ }
+ }
+
+ if (request.command == 'continue') {
+ this.continueRequest_(request, response);
+ } else if (request.command == 'break') {
+ this.breakRequest_(request, response);
+ } else if (request.command == 'setbreakpoint') {
+ this.setBreakPointRequest_(request, response);
+ } else if (request.command == 'changebreakpoint') {
+ this.changeBreakPointRequest_(request, response);
+ } else if (request.command == 'clearbreakpoint') {
+ this.clearBreakPointRequest_(request, response);
+ } else if (request.command == 'clearbreakpointgroup') {
+ this.clearBreakPointGroupRequest_(request, response);
+ } else if (request.command == 'disconnect') {
+ this.disconnectRequest_(request, response);
+ } else if (request.command == 'setexceptionbreak') {
+ this.setExceptionBreakRequest_(request, response);
+ } else if (request.command == 'listbreakpoints') {
+ this.listBreakpointsRequest_(request, response);
+ } else if (request.command == 'backtrace') {
+ this.backtraceRequest_(request, response);
+ } else if (request.command == 'frame') {
+ this.frameRequest_(request, response);
+ } else if (request.command == 'scopes') {
+ this.scopesRequest_(request, response);
+ } else if (request.command == 'scope') {
+ this.scopeRequest_(request, response);
+ } else if (request.command == 'setVariableValue') {
+ this.setVariableValueRequest_(request, response);
+ } else if (request.command == 'evaluate') {
+ this.evaluateRequest_(request, response);
+ } else if (request.command == 'lookup') {
+ this.lookupRequest_(request, response);
+ } else if (request.command == 'references') {
+ this.referencesRequest_(request, response);
+ } else if (request.command == 'source') {
+ this.sourceRequest_(request, response);
+ } else if (request.command == 'scripts') {
+ this.scriptsRequest_(request, response);
+ } else if (request.command == 'threads') {
+ this.threadsRequest_(request, response);
+ } else if (request.command == 'suspend') {
+ this.suspendRequest_(request, response);
+ } else if (request.command == 'version') {
+ this.versionRequest_(request, response);
+ } else if (request.command == 'changelive') {
+ this.changeLiveRequest_(request, response);
+ } else if (request.command == 'restartframe') {
+ this.restartFrameRequest_(request, response);
+ } else if (request.command == 'flags') {
+ this.debuggerFlagsRequest_(request, response);
+ } else if (request.command == 'v8flags') {
+ this.v8FlagsRequest_(request, response);
+
+ // GC tools:
+ } else if (request.command == 'gc') {
+ this.gcRequest_(request, response);
+
+ } else {
+ throw new Error('Unknown command "' + request.command + '" in request');
+ }
+ } catch (e) {
+ // If there is no response object created one (without command).
+ if (!response) {
+ response = this.createResponse();
+ }
+ response.success = false;
+ response.message = %ToString(e);
+ }
+
+ // Return the response as a JSON encoded string.
+ try {
+ if (!IS_UNDEFINED(response.running)) {
+ // Response controls running state.
+ this.running_ = response.running;
+ }
+ response.running = this.running_;
+ return response.toJSONProtocol();
+ } catch (e) {
+ // Failed to generate response - return generic error.
+ return '{"seq":' + response.seq + ',' +
+ '"request_seq":' + request.seq + ',' +
+ '"type":"response",' +
+ '"success":false,' +
+ '"message":"Internal error: ' + %ToString(e) + '"}';
+ }
+ } catch (e) {
+ // Failed in one of the catch blocks above - most generic error.
+ return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
+ }
+};
+
+
+DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
+ // Check for arguments for continue.
+ if (request.arguments) {
+ var count = 1;
+ var action = Debug.StepAction.StepIn;
+
+ // Pull out arguments.
+ var stepaction = request.arguments.stepaction;
+ var stepcount = request.arguments.stepcount;
+
+ // Get the stepcount argument if any.
+ if (stepcount) {
+ count = %ToNumber(stepcount);
+ if (count < 0) {
+ throw new Error('Invalid stepcount argument "' + stepcount + '".');
+ }
+ }
+
+ // Get the stepaction argument.
+ if (stepaction) {
+ if (stepaction == 'in') {
+ action = Debug.StepAction.StepIn;
+ } else if (stepaction == 'min') {
+ action = Debug.StepAction.StepMin;
+ } else if (stepaction == 'next') {
+ action = Debug.StepAction.StepNext;
+ } else if (stepaction == 'out') {
+ action = Debug.StepAction.StepOut;
+ } else {
+ throw new Error('Invalid stepaction argument "' + stepaction + '".');
+ }
+ }
+
+ // Set up the VM for stepping.
+ this.exec_state_.prepareStep(action, count);
+ }
+
+ // VM should be running after executing this request.
+ response.running = true;
+};
+
+
+DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
+ // Ignore as break command does not do anything when broken.
+};
+
+
+DebugCommandProcessor.prototype.setBreakPointRequest_ =
+ function(request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var type = request.arguments.type;
+ var target = request.arguments.target;
+ var line = request.arguments.line;
+ var column = request.arguments.column;
+ var enabled = IS_UNDEFINED(request.arguments.enabled) ?
+ true : request.arguments.enabled;
+ var condition = request.arguments.condition;
+ var ignoreCount = request.arguments.ignoreCount;
+ var groupId = request.arguments.groupId;
+
+ // Check for legal arguments.
+ if (!type || IS_UNDEFINED(target)) {
+ response.failed('Missing argument "type" or "target"');
+ return;
+ }
+
+ // Either function or script break point.
+ var break_point_number;
+ if (type == 'function') {
+ // Handle function break point.
+ if (!IS_STRING(target)) {
+ response.failed('Argument "target" is not a string value');
+ return;
+ }
+ var f;
+ try {
+ // Find the function through a global evaluate.
+ f = this.exec_state_.evaluateGlobal(target).value();
+ } catch (e) {
+ response.failed('Error: "' + %ToString(e) +
+ '" evaluating "' + target + '"');
+ return;
+ }
+ if (!IS_FUNCTION(f)) {
+ response.failed('"' + target + '" does not evaluate to a function');
+ return;
+ }
+
+ // Set function break point.
+ break_point_number = Debug.setBreakPoint(f, line, column, condition);
+ } else if (type == 'handle') {
+ // Find the object pointed by the specified handle.
+ var handle = parseInt(target, 10);
+ var mirror = LookupMirror(handle);
+ if (!mirror) {
+ return response.failed('Object #' + handle + '# not found');
+ }
+ if (!mirror.isFunction()) {
+ return response.failed('Object #' + handle + '# is not a function');
+ }
+
+ // Set function break point.
+ break_point_number = Debug.setBreakPoint(mirror.value(),
+ line, column, condition);
+ } else if (type == 'script') {
+ // set script break point.
+ break_point_number =
+ Debug.setScriptBreakPointByName(target, line, column, condition,
+ groupId);
+ } else if (type == 'scriptId') {
+ break_point_number =
+ Debug.setScriptBreakPointById(target, line, column, condition, groupId);
+ } else if (type == 'scriptRegExp') {
+ break_point_number =
+ Debug.setScriptBreakPointByRegExp(target, line, column, condition,
+ groupId);
+ } else {
+ response.failed('Illegal type "' + type + '"');
+ return;
+ }
+
+ // Set additional break point properties.
+ var break_point = Debug.findBreakPoint(break_point_number);
+ if (ignoreCount) {
+ Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
+ }
+ if (!enabled) {
+ Debug.disableBreakPoint(break_point_number);
+ }
+
+ // Add the break point number to the response.
+ response.body = { type: type,
+ breakpoint: break_point_number };
+
+ // Add break point information to the response.
+ if (break_point instanceof ScriptBreakPoint) {
+ if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
+ response.body.type = 'scriptId';
+ response.body.script_id = break_point.script_id();
+ } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
+ response.body.type = 'scriptName';
+ response.body.script_name = break_point.script_name();
+ } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
+ 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());
+ }
+ response.body.line = break_point.line();
+ response.body.column = break_point.column();
+ response.body.actual_locations = break_point.actual_locations();
+ } else {
+ response.body.type = 'function';
+ response.body.actual_locations = [break_point.actual_location];
+ }
+};
+
+
+DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(
+ request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var break_point = %ToNumber(request.arguments.breakpoint);
+ var enabled = request.arguments.enabled;
+ var condition = request.arguments.condition;
+ var ignoreCount = request.arguments.ignoreCount;
+
+ // Check for legal arguments.
+ if (!break_point) {
+ response.failed('Missing argument "breakpoint"');
+ return;
+ }
+
+ // Change enabled state if supplied.
+ if (!IS_UNDEFINED(enabled)) {
+ if (enabled) {
+ Debug.enableBreakPoint(break_point);
+ } else {
+ Debug.disableBreakPoint(break_point);
+ }
+ }
+
+ // Change condition if supplied
+ if (!IS_UNDEFINED(condition)) {
+ Debug.changeBreakPointCondition(break_point, condition);
+ }
+
+ // Change ignore count if supplied
+ if (!IS_UNDEFINED(ignoreCount)) {
+ Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
+ }
+};
+
+
+DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(
+ request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var group_id = request.arguments.groupId;
+
+ // Check for legal arguments.
+ if (!group_id) {
+ response.failed('Missing argument "groupId"');
+ return;
+ }
+
+ var cleared_break_points = [];
+ var new_script_break_points = [];
+ for (var i = 0; i < script_break_points.length; i++) {
+ var next_break_point = script_break_points[i];
+ if (next_break_point.groupId() == group_id) {
+ cleared_break_points.push(next_break_point.number());
+ next_break_point.clear();
+ } else {
+ new_script_break_points.push(next_break_point);
+ }
+ }
+ script_break_points = new_script_break_points;
+
+ // Add the cleared break point numbers to the response.
+ response.body = { breakpoints: cleared_break_points };
+};
+
+
+DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(
+ request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var break_point = %ToNumber(request.arguments.breakpoint);
+
+ // Check for legal arguments.
+ if (!break_point) {
+ response.failed('Missing argument "breakpoint"');
+ return;
+ }
+
+ // Clear break point.
+ Debug.clearBreakPoint(break_point);
+
+ // Add the cleared break point number to the response.
+ response.body = { breakpoint: break_point };
+};
+
+
+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];
+
+ var description = {
+ number: break_point.number(),
+ line: break_point.line(),
+ column: break_point.column(),
+ groupId: break_point.groupId(),
+ hit_count: break_point.hit_count(),
+ active: break_point.active(),
+ condition: break_point.condition(),
+ ignoreCount: break_point.ignoreCount(),
+ actual_locations: break_point.actual_locations()
+ };
+
+ if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
+ description.type = 'scriptId';
+ description.script_id = break_point.script_id();
+ } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
+ description.type = 'scriptName';
+ description.script_name = break_point.script_name();
+ } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
+ description.type = 'scriptRegExp';
+ description.script_regexp = break_point.script_regexp_object().source;
+ } else {
+ throw new Error("Internal error: Unexpected breakpoint type: " +
+ break_point.type());
+ }
+ array.push(description);
+ }
+
+ response.body = {
+ breakpoints: array,
+ breakOnExceptions: Debug.isBreakOnException(),
+ breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException()
+ };
+};
+
+
+DebugCommandProcessor.prototype.disconnectRequest_ =
+ function(request, response) {
+ Debug.disableAllBreakPoints();
+ this.continueRequest_(request, response);
+};
+
+
+DebugCommandProcessor.prototype.setExceptionBreakRequest_ =
+ function(request, response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out and check the 'type' argument:
+ var type = request.arguments.type;
+ if (!type) {
+ response.failed('Missing argument "type"');
+ return;
+ }
+
+ // Initialize the default value of enable:
+ var enabled;
+ if (type == 'all') {
+ enabled = !Debug.isBreakOnException();
+ } else if (type == 'uncaught') {
+ enabled = !Debug.isBreakOnUncaughtException();
+ }
+
+ // Pull out and check the 'enabled' argument if present:
+ if (!IS_UNDEFINED(request.arguments.enabled)) {
+ enabled = request.arguments.enabled;
+ if ((enabled != true) && (enabled != false)) {
+ response.failed('Illegal value for "enabled":"' + enabled + '"');
+ }
+ }
+
+ // Now set the exception break state:
+ if (type == 'all') {
+ %ChangeBreakOnException(Debug.ExceptionBreak.Caught, enabled);
+ } else if (type == 'uncaught') {
+ %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, enabled);
+ } else {
+ response.failed('Unknown "type":"' + type + '"');
+ }
+
+ // Add the cleared break point number to the response.
+ response.body = { 'type': type, 'enabled': enabled };
+};
+
+
+DebugCommandProcessor.prototype.backtraceRequest_ = function(
+ request, response) {
+ // Get the number of frames.
+ var total_frames = this.exec_state_.frameCount();
+
+ // Create simple response if there are no frames.
+ if (total_frames == 0) {
+ response.body = {
+ totalFrames: total_frames
+ };
+ return;
+ }
+
+ // Default frame range to include in backtrace.
+ var from_index = 0;
+ var to_index = kDefaultBacktraceLength;
+
+ // Get the range from the arguments.
+ if (request.arguments) {
+ if (request.arguments.fromFrame) {
+ from_index = request.arguments.fromFrame;
+ }
+ if (request.arguments.toFrame) {
+ to_index = request.arguments.toFrame;
+ }
+ if (request.arguments.bottom) {
+ var tmp_index = total_frames - from_index;
+ from_index = total_frames - to_index;
+ to_index = tmp_index;
+ }
+ if (from_index < 0 || to_index < 0) {
+ return response.failed('Invalid frame number');
+ }
+ }
+
+ // Adjust the index.
+ to_index = Math.min(total_frames, to_index);
+
+ if (to_index <= from_index) {
+ var error = 'Invalid frame range';
+ return response.failed(error);
+ }
+
+ // Create the response body.
+ var frames = [];
+ for (var i = from_index; i < to_index; i++) {
+ frames.push(this.exec_state_.frame(i));
+ }
+ response.body = {
+ fromFrame: from_index,
+ toFrame: to_index,
+ totalFrames: total_frames,
+ frames: frames
+ };
+};
+
+
+DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
+ // No frames no source.
+ if (this.exec_state_.frameCount() == 0) {
+ return response.failed('No frames');
+ }
+
+ // With no arguments just keep the selected frame.
+ if (request.arguments) {
+ var index = request.arguments.number;
+ if (index < 0 || this.exec_state_.frameCount() <= index) {
+ return response.failed('Invalid frame number');
+ }
+
+ this.exec_state_.setSelectedFrame(request.arguments.number);
+ }
+ response.body = this.exec_state_.frame();
+};
+
+
+DebugCommandProcessor.prototype.resolveFrameFromScopeDescription_ =
+ function(scope_description) {
+ // Get the frame for which the scope or scopes are requested.
+ // With no frameNumber argument use the currently selected frame.
+ if (scope_description && !IS_UNDEFINED(scope_description.frameNumber)) {
+ frame_index = scope_description.frameNumber;
+ if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
+ throw new Error('Invalid frame number');
+ }
+ return this.exec_state_.frame(frame_index);
+ } else {
+ return this.exec_state_.frame();
+ }
+};
+
+
+// Gets scope host object from request. It is either a function
+// ('functionHandle' argument must be specified) or a stack frame
+// ('frameNumber' may be specified and the current frame is taken by default).
+DebugCommandProcessor.prototype.resolveScopeHolder_ =
+ function(scope_description) {
+ if (scope_description && "functionHandle" in scope_description) {
+ if (!IS_NUMBER(scope_description.functionHandle)) {
+ throw new Error('Function handle must be a number');
+ }
+ var function_mirror = LookupMirror(scope_description.functionHandle);
+ if (!function_mirror) {
+ throw new Error('Failed to find function object by handle');
+ }
+ if (!function_mirror.isFunction()) {
+ throw new Error('Value of non-function type is found by handle');
+ }
+ return function_mirror;
+ } else {
+ // No frames no scopes.
+ if (this.exec_state_.frameCount() == 0) {
+ throw new Error('No scopes');
+ }
+
+ // Get the frame for which the scopes are requested.
+ var frame = this.resolveFrameFromScopeDescription_(scope_description);
+ return frame;
+ }
+}
+
+
+DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
+ var scope_holder = this.resolveScopeHolder_(request.arguments);
+
+ // Fill all scopes for this frame or function.
+ var total_scopes = scope_holder.scopeCount();
+ var scopes = [];
+ for (var i = 0; i < total_scopes; i++) {
+ scopes.push(scope_holder.scope(i));
+ }
+ response.body = {
+ fromScope: 0,
+ toScope: total_scopes,
+ totalScopes: total_scopes,
+ scopes: scopes
+ };
+};
+
+
+DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
+ // Get the frame or function for which the scope is requested.
+ var scope_holder = this.resolveScopeHolder_(request.arguments);
+
+ // With no scope argument just return top scope.
+ var scope_index = 0;
+ if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
+ scope_index = %ToNumber(request.arguments.number);
+ if (scope_index < 0 || scope_holder.scopeCount() <= scope_index) {
+ return response.failed('Invalid scope number');
+ }
+ }
+
+ response.body = scope_holder.scope(scope_index);
+};
+
+
+// Reads value from protocol description. Description may be in form of type
+// (for singletons), raw value (primitive types supported in JSON),
+// string value description plus type (for primitive values) or handle id.
+// Returns raw value or throws exception.
+DebugCommandProcessor.resolveValue_ = function(value_description) {
+ if ("handle" in value_description) {
+ var value_mirror = LookupMirror(value_description.handle);
+ if (!value_mirror) {
+ throw new Error("Failed to resolve value by handle, ' #" +
+ mapping.handle + "# not found");
+ }
+ return value_mirror.value();
+ } else if ("stringDescription" in value_description) {
+ if (value_description.type == BOOLEAN_TYPE) {
+ return Boolean(value_description.stringDescription);
+ } else if (value_description.type == NUMBER_TYPE) {
+ return Number(value_description.stringDescription);
+ } if (value_description.type == STRING_TYPE) {
+ return String(value_description.stringDescription);
+ } else {
+ throw new Error("Unknown type");
+ }
+ } else if ("value" in value_description) {
+ return value_description.value;
+ } else if (value_description.type == UNDEFINED_TYPE) {
+ return void 0;
+ } else if (value_description.type == NULL_TYPE) {
+ return null;
+ } else {
+ throw new Error("Failed to parse value description");
+ }
+};
+
+
+DebugCommandProcessor.prototype.setVariableValueRequest_ =
+ function(request, response) {
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ if (IS_UNDEFINED(request.arguments.name)) {
+ response.failed('Missing variable name');
+ }
+ var variable_name = request.arguments.name;
+
+ var scope_description = request.arguments.scope;
+
+ // Get the frame or function for which the scope is requested.
+ var scope_holder = this.resolveScopeHolder_(scope_description);
+
+ if (IS_UNDEFINED(scope_description.number)) {
+ response.failed('Missing scope number');
+ }
+ var scope_index = %ToNumber(scope_description.number);
+
+ var scope = scope_holder.scope(scope_index);
+
+ var new_value =
+ DebugCommandProcessor.resolveValue_(request.arguments.newValue);
+
+ scope.setVariableValue(variable_name, new_value);
+
+ var new_value_mirror = MakeMirror(new_value);
+
+ response.body = {
+ newValue: new_value_mirror
+ };
+};
+
+
+DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+
+ // Pull out arguments.
+ var expression = request.arguments.expression;
+ var frame = request.arguments.frame;
+ var global = request.arguments.global;
+ var disable_break = request.arguments.disable_break;
+ var additional_context = request.arguments.additional_context;
+
+ // The expression argument could be an integer so we convert it to a
+ // string.
+ try {
+ expression = String(expression);
+ } catch(e) {
+ return response.failed('Failed to convert expression argument to string');
+ }
+
+ // Check for legal arguments.
+ if (!IS_UNDEFINED(frame) && global) {
+ return response.failed('Arguments "frame" and "global" are exclusive');
+ }
+
+ var additional_context_object;
+ if (additional_context) {
+ additional_context_object = {};
+ for (var i = 0; i < additional_context.length; i++) {
+ var mapping = additional_context[i];
+
+ if (!IS_STRING(mapping.name)) {
+ return response.failed("Context element #" + i +
+ " doesn't contain name:string property");
+ }
+
+ var raw_value = DebugCommandProcessor.resolveValue_(mapping);
+ additional_context_object[mapping.name] = raw_value;
+ }
+ }
+
+ // Global evaluate.
+ if (global) {
+ // Evaluate in the native context.
+ response.body = this.exec_state_.evaluateGlobal(
+ expression, Boolean(disable_break), additional_context_object);
+ return;
+ }
+
+ // Default value for disable_break is true.
+ if (IS_UNDEFINED(disable_break)) {
+ disable_break = true;
+ }
+
+ // No frames no evaluate in frame.
+ if (this.exec_state_.frameCount() == 0) {
+ return response.failed('No frames');
+ }
+
+ // Check whether a frame was specified.
+ if (!IS_UNDEFINED(frame)) {
+ var frame_number = %ToNumber(frame);
+ if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
+ return response.failed('Invalid frame "' + frame + '"');
+ }
+ // Evaluate in the specified frame.
+ response.body = this.exec_state_.frame(frame_number).evaluate(
+ expression, Boolean(disable_break), additional_context_object);
+ return;
+ } else {
+ // Evaluate in the selected frame.
+ response.body = this.exec_state_.frame().evaluate(
+ expression, Boolean(disable_break), additional_context_object);
+ return;
+ }
+};
+
+
+DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+
+ // Pull out arguments.
+ var handles = request.arguments.handles;
+
+ // Check for legal arguments.
+ if (IS_UNDEFINED(handles)) {
+ return response.failed('Argument "handles" missing');
+ }
+
+ // Set 'includeSource' option for script lookup.
+ if (!IS_UNDEFINED(request.arguments.includeSource)) {
+ includeSource = %ToBoolean(request.arguments.includeSource);
+ response.setOption('includeSource', includeSource);
+ }
+
+ // Lookup handles.
+ var mirrors = {};
+ for (var i = 0; i < handles.length; i++) {
+ var handle = handles[i];
+ var mirror = LookupMirror(handle);
+ if (!mirror) {
+ return response.failed('Object #' + handle + '# not found');
+ }
+ mirrors[handle] = mirror;
+ }
+ response.body = mirrors;
+};
+
+
+DebugCommandProcessor.prototype.referencesRequest_ =
+ function(request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+
+ // Pull out arguments.
+ var type = request.arguments.type;
+ var handle = request.arguments.handle;
+
+ // Check for legal arguments.
+ if (IS_UNDEFINED(type)) {
+ return response.failed('Argument "type" missing');
+ }
+ if (IS_UNDEFINED(handle)) {
+ return response.failed('Argument "handle" missing');
+ }
+ if (type != 'referencedBy' && type != 'constructedBy') {
+ return response.failed('Invalid type "' + type + '"');
+ }
+
+ // Lookup handle and return objects with references the object.
+ var mirror = LookupMirror(handle);
+ if (mirror) {
+ if (type == 'referencedBy') {
+ response.body = mirror.referencedBy();
+ } else {
+ response.body = mirror.constructedBy();
+ }
+ } else {
+ return response.failed('Object #' + handle + '# not found');
+ }
+};
+
+
+DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
+ // No frames no source.
+ if (this.exec_state_.frameCount() == 0) {
+ return response.failed('No source');
+ }
+
+ var from_line;
+ var to_line;
+ var frame = this.exec_state_.frame();
+ if (request.arguments) {
+ // Pull out arguments.
+ from_line = request.arguments.fromLine;
+ to_line = request.arguments.toLine;
+
+ if (!IS_UNDEFINED(request.arguments.frame)) {
+ var frame_number = %ToNumber(request.arguments.frame);
+ if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
+ return response.failed('Invalid frame "' + frame + '"');
+ }
+ frame = this.exec_state_.frame(frame_number);
+ }
+ }
+
+ // Get the script selected.
+ var script = frame.func().script();
+ if (!script) {
+ return response.failed('No source');
+ }
+
+ // Get the source slice and fill it into the response.
+ var slice = script.sourceSlice(from_line, to_line);
+ if (!slice) {
+ return response.failed('Invalid line interval');
+ }
+ response.body = {};
+ response.body.source = slice.sourceText();
+ response.body.fromLine = slice.from_line;
+ response.body.toLine = slice.to_line;
+ response.body.fromPosition = slice.from_position;
+ response.body.toPosition = slice.to_position;
+ response.body.totalLines = script.lineCount();
+};
+
+
+DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
+ var types = ScriptTypeFlag(Debug.ScriptType.Normal);
+ var includeSource = false;
+ var idsToInclude = null;
+ if (request.arguments) {
+ // Pull out arguments.
+ if (!IS_UNDEFINED(request.arguments.types)) {
+ types = %ToNumber(request.arguments.types);
+ if (isNaN(types) || types < 0) {
+ return response.failed('Invalid types "' +
+ request.arguments.types + '"');
+ }
+ }
+
+ if (!IS_UNDEFINED(request.arguments.includeSource)) {
+ includeSource = %ToBoolean(request.arguments.includeSource);
+ response.setOption('includeSource', includeSource);
+ }
+
+ if (IS_ARRAY(request.arguments.ids)) {
+ idsToInclude = {};
+ var ids = request.arguments.ids;
+ for (var i = 0; i < ids.length; i++) {
+ idsToInclude[ids[i]] = true;
+ }
+ }
+
+ var filterStr = null;
+ var filterNum = null;
+ if (!IS_UNDEFINED(request.arguments.filter)) {
+ var num = %ToNumber(request.arguments.filter);
+ if (!isNaN(num)) {
+ filterNum = num;
+ }
+ filterStr = request.arguments.filter;
+ }
+ }
+
+ // Collect all scripts in the heap.
+ var scripts = %DebugGetLoadedScripts();
+
+ response.body = [];
+
+ for (var i = 0; i < scripts.length; i++) {
+ if (idsToInclude && !idsToInclude[scripts[i].id]) {
+ continue;
+ }
+ if (filterStr || filterNum) {
+ var script = scripts[i];
+ var found = false;
+ if (filterNum && !found) {
+ if (script.id && script.id === filterNum) {
+ found = true;
+ }
+ }
+ if (filterStr && !found) {
+ if (script.name && script.name.indexOf(filterStr) >= 0) {
+ found = true;
+ }
+ }
+ if (!found) continue;
+ }
+ if (types & ScriptTypeFlag(scripts[i].type)) {
+ response.body.push(MakeMirror(scripts[i]));
+ }
+ }
+};
+
+
+DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
+ // Get the number of threads.
+ var total_threads = this.exec_state_.threadCount();
+
+ // Get information for all threads.
+ var threads = [];
+ for (var i = 0; i < total_threads; i++) {
+ var details = %GetThreadDetails(this.exec_state_.break_id, i);
+ var thread_info = { current: details[0],
+ id: details[1]
+ };
+ threads.push(thread_info);
+ }
+
+ // Create the response body.
+ response.body = {
+ totalThreads: total_threads,
+ threads: threads
+ };
+};
+
+
+DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
+ response.running = false;
+};
+
+
+DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
+ response.body = {
+ V8Version: %GetV8Version()
+ };
+};
+
+
+DebugCommandProcessor.prototype.changeLiveRequest_ = function(
+ request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+ var script_id = request.arguments.script_id;
+ var preview_only = !!request.arguments.preview_only;
+
+ var scripts = %DebugGetLoadedScripts();
+
+ var the_script = null;
+ for (var i = 0; i < scripts.length; i++) {
+ if (scripts[i].id == script_id) {
+ the_script = scripts[i];
+ }
+ }
+ if (!the_script) {
+ response.failed('Script not found');
+ return;
+ }
+
+ var change_log = new Array();
+
+ if (!IS_STRING(request.arguments.new_source)) {
+ throw "new_source argument expected";
+ }
+
+ var new_source = request.arguments.new_source;
+
+ var result_description;
+ try {
+ result_description = Debug.LiveEdit.SetScriptSource(the_script,
+ new_source, preview_only, change_log);
+ } catch (e) {
+ if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
+ response.failed(e.message, e.details);
+ return;
+ }
+ throw e;
+ }
+ response.body = {change_log: change_log, result: result_description};
+
+ if (!preview_only && !this.running_ && result_description.stack_modified) {
+ response.body.stepin_recommended = true;
+ }
+};
+
+
+DebugCommandProcessor.prototype.restartFrameRequest_ = function(
+ request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+ var frame = request.arguments.frame;
+
+ // No frames to evaluate in frame.
+ if (this.exec_state_.frameCount() == 0) {
+ return response.failed('No frames');
+ }
+
+ var frame_mirror;
+ // Check whether a frame was specified.
+ if (!IS_UNDEFINED(frame)) {
+ var frame_number = %ToNumber(frame);
+ if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
+ return response.failed('Invalid frame "' + frame + '"');
+ }
+ // Restart specified frame.
+ frame_mirror = this.exec_state_.frame(frame_number);
+ } else {
+ // Restart selected frame.
+ frame_mirror = this.exec_state_.frame();
+ }
+
+ var result_description = Debug.LiveEdit.RestartFrame(frame_mirror);
+ response.body = {result: result_description};
+};
+
+
+DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request,
+ response) {
+ // Check for legal request.
+ if (!request.arguments) {
+ response.failed('Missing arguments');
+ return;
+ }
+
+ // Pull out arguments.
+ var flags = request.arguments.flags;
+
+ response.body = { flags: [] };
+ if (!IS_UNDEFINED(flags)) {
+ for (var i = 0; i < flags.length; i++) {
+ var name = flags[i].name;
+ var debugger_flag = debugger_flags[name];
+ if (!debugger_flag) {
+ continue;
+ }
+ if ('value' in flags[i]) {
+ debugger_flag.setValue(flags[i].value);
+ }
+ response.body.flags.push({ name: name, value: debugger_flag.getValue() });
+ }
+ } else {
+ for (var name in debugger_flags) {
+ var value = debugger_flags[name].getValue();
+ response.body.flags.push({ name: name, value: value });
+ }
+ }
+};
+
+
+DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) {
+ var flags = request.arguments.flags;
+ if (!flags) flags = '';
+ %SetFlags(flags);
+};
+
+
+DebugCommandProcessor.prototype.gcRequest_ = function(request, response) {
+ var type = request.arguments.type;
+ if (!type) type = 'all';
+
+ var before = %GetHeapUsage();
+ %CollectGarbage(type);
+ var after = %GetHeapUsage();
+
+ response.body = { "before": before, "after": after };
+};
+
+
+// Check whether the previously processed command caused the VM to become
+// running.
+DebugCommandProcessor.prototype.isRunning = function() {
+ return this.running_;
+};
+
+
+DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
+ return %SystemBreak();
+};
+
+
+function NumberToHex8Str(n) {
+ var r = "";
+ for (var i = 0; i < 8; ++i) {
+ var c = hexCharArray[n & 0x0F]; // hexCharArray is defined in uri.js
+ r = c + r;
+ n = n >>> 4;
+ }
+ return r;
+}
+
+
+/**
+ * Convert an Object to its debugger protocol representation. The representation
+ * may be serilized to a JSON object using JSON.stringify().
+ * This implementation simply runs through all string property names, converts
+ * each property value to a protocol value and adds the property to the result
+ * object. For type "object" the function will be called recursively. Note that
+ * circular structures will cause infinite recursion.
+ * @param {Object} object The object to format as protocol object.
+ * @param {MirrorSerializer} mirror_serializer The serializer to use if any
+ * mirror objects are encountered.
+ * @return {Object} Protocol object value.
+ */
+function ObjectToProtocolObject_(object, mirror_serializer) {
+ var content = {};
+ for (var key in object) {
+ // Only consider string keys.
+ if (typeof key == 'string') {
+ // Format the value based on its type.
+ var property_value_json = ValueToProtocolValue_(object[key],
+ mirror_serializer);
+ // Add the property if relevant.
+ if (!IS_UNDEFINED(property_value_json)) {
+ content[key] = property_value_json;
+ }
+ }
+ }
+
+ return content;
+}
+
+
+/**
+ * Convert an array to its debugger protocol representation. It will convert
+ * each array element to a protocol value.
+ * @param {Array} array The array to format as protocol array.
+ * @param {MirrorSerializer} mirror_serializer The serializer to use if any
+ * mirror objects are encountered.
+ * @return {Array} Protocol array value.
+ */
+function ArrayToProtocolArray_(array, mirror_serializer) {
+ var json = [];
+ for (var i = 0; i < array.length; i++) {
+ json.push(ValueToProtocolValue_(array[i], mirror_serializer));
+ }
+ return json;
+}
+
+
+/**
+ * Convert a value to its debugger protocol representation.
+ * @param {*} value The value to format as protocol value.
+ * @param {MirrorSerializer} mirror_serializer The serializer to use if any
+ * mirror objects are encountered.
+ * @return {*} Protocol value.
+ */
+function ValueToProtocolValue_(value, mirror_serializer) {
+ // Format the value based on its type.
+ var json;
+ switch (typeof value) {
+ case 'object':
+ if (value instanceof Mirror) {
+ json = mirror_serializer.serializeValue(value);
+ } else if (IS_ARRAY(value)){
+ json = ArrayToProtocolArray_(value, mirror_serializer);
+ } else {
+ json = ObjectToProtocolObject_(value, mirror_serializer);
+ }
+ break;
+
+ case 'boolean':
+ case 'string':
+ case 'number':
+ json = value;
+ break;
+
+ default:
+ json = null;
+ }
+ return json;
+}
+
+Debug.TestApi = {
+ CommandProcessorResolveValue: DebugCommandProcessor.resolveValue_
+};
diff --git a/chromium/v8/src/debug.cc b/chromium/v8/src/debug.cc
new file mode 100644
index 00000000000..4966713baab
--- /dev/null
+++ b/chromium/v8/src/debug.cc
@@ -0,0 +1,3874 @@
+// Copyright 2012 the V8 project authors. 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 "api.h"
+#include "arguments.h"
+#include "bootstrapper.h"
+#include "code-stubs.h"
+#include "codegen.h"
+#include "compilation-cache.h"
+#include "compiler.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "execution.h"
+#include "full-codegen.h"
+#include "global-handles.h"
+#include "ic.h"
+#include "ic-inl.h"
+#include "isolate-inl.h"
+#include "list.h"
+#include "messages.h"
+#include "natives.h"
+#include "stub-cache.h"
+#include "log.h"
+
+#include "../include/v8-debug.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+
+Debug::Debug(Isolate* isolate)
+ : has_break_points_(false),
+ script_cache_(NULL),
+ debug_info_list_(NULL),
+ disable_break_(false),
+ break_on_exception_(false),
+ break_on_uncaught_exception_(false),
+ debug_break_return_(NULL),
+ debug_break_slot_(NULL),
+ isolate_(isolate) {
+ memset(registers_, 0, sizeof(JSCallerSavedBuffer));
+}
+
+
+Debug::~Debug() {
+}
+
+
+static void PrintLn(v8::Local<v8::Value> value) {
+ v8::Local<v8::String> s = value->ToString();
+ ScopedVector<char> data(s->Utf8Length() + 1);
+ if (data.start() == NULL) {
+ V8::FatalProcessOutOfMemory("PrintLn");
+ return;
+ }
+ s->WriteUtf8(data.start());
+ PrintF("%s\n", data.start());
+}
+
+
+static Handle<Code> ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) {
+ Isolate* isolate = Isolate::Current();
+ return isolate->stub_cache()->ComputeCallDebugPrepareStepIn(argc, kind);
+}
+
+
+static v8::Handle<v8::Context> GetDebugEventContext(Isolate* isolate) {
+ Handle<Context> context = isolate->debug()->debugger_entry()->GetContext();
+ // Isolate::context() may have been NULL when "script collected" event
+ // occured.
+ if (context.is_null()) return v8::Local<v8::Context>();
+ Handle<Context> native_context(context->native_context());
+ return v8::Utils::ToLocal(native_context);
+}
+
+
+BreakLocationIterator::BreakLocationIterator(Handle<DebugInfo> debug_info,
+ BreakLocatorType type) {
+ debug_info_ = debug_info;
+ type_ = type;
+ reloc_iterator_ = NULL;
+ reloc_iterator_original_ = NULL;
+ Reset(); // Initialize the rest of the member variables.
+}
+
+
+BreakLocationIterator::~BreakLocationIterator() {
+ ASSERT(reloc_iterator_ != NULL);
+ ASSERT(reloc_iterator_original_ != NULL);
+ delete reloc_iterator_;
+ delete reloc_iterator_original_;
+}
+
+
+void BreakLocationIterator::Next() {
+ DisallowHeapAllocation no_gc;
+ ASSERT(!RinfoDone());
+
+ // Iterate through reloc info for code and original code stopping at each
+ // breakable code target.
+ bool first = break_point_ == -1;
+ while (!RinfoDone()) {
+ if (!first) RinfoNext();
+ first = false;
+ if (RinfoDone()) return;
+
+ // Whenever a statement position or (plain) position is passed update the
+ // current value of these.
+ if (RelocInfo::IsPosition(rmode())) {
+ if (RelocInfo::IsStatementPosition(rmode())) {
+ statement_position_ = static_cast<int>(
+ rinfo()->data() - debug_info_->shared()->start_position());
+ }
+ // Always update the position as we don't want that to be before the
+ // statement position.
+ position_ = static_cast<int>(
+ rinfo()->data() - debug_info_->shared()->start_position());
+ ASSERT(position_ >= 0);
+ ASSERT(statement_position_ >= 0);
+ }
+
+ if (IsDebugBreakSlot()) {
+ // There is always a possible break point at a debug break slot.
+ break_point_++;
+ return;
+ } else if (RelocInfo::IsCodeTarget(rmode())) {
+ // Check for breakable code target. Look in the original code as setting
+ // break points can cause the code targets in the running (debugged) code
+ // to be of a different kind than in the original code.
+ Address target = original_rinfo()->target_address();
+ Code* code = Code::GetCodeFromTargetAddress(target);
+ if ((code->is_inline_cache_stub() &&
+ !code->is_binary_op_stub() &&
+ !code->is_compare_ic_stub() &&
+ !code->is_to_boolean_ic_stub()) ||
+ RelocInfo::IsConstructCall(rmode())) {
+ break_point_++;
+ return;
+ }
+ if (code->kind() == Code::STUB) {
+ if (IsDebuggerStatement()) {
+ break_point_++;
+ return;
+ }
+ if (type_ == ALL_BREAK_LOCATIONS) {
+ if (Debug::IsBreakStub(code)) {
+ break_point_++;
+ return;
+ }
+ } else {
+ ASSERT(type_ == SOURCE_BREAK_LOCATIONS);
+ if (Debug::IsSourceBreakStub(code)) {
+ break_point_++;
+ return;
+ }
+ }
+ }
+ }
+
+ // Check for break at return.
+ if (RelocInfo::IsJSReturn(rmode())) {
+ // Set the positions to the end of the function.
+ if (debug_info_->shared()->HasSourceCode()) {
+ position_ = debug_info_->shared()->end_position() -
+ debug_info_->shared()->start_position() - 1;
+ } else {
+ position_ = 0;
+ }
+ statement_position_ = position_;
+ break_point_++;
+ return;
+ }
+ }
+}
+
+
+void BreakLocationIterator::Next(int count) {
+ while (count > 0) {
+ Next();
+ count--;
+ }
+}
+
+
+// Find the break point at the supplied address, or the closest one before
+// the address.
+void BreakLocationIterator::FindBreakLocationFromAddress(Address pc) {
+ // Run through all break points to locate the one closest to the address.
+ int closest_break_point = 0;
+ int distance = kMaxInt;
+ while (!Done()) {
+ // Check if this break point is closer that what was previously found.
+ if (this->pc() <= pc && pc - this->pc() < distance) {
+ closest_break_point = break_point();
+ distance = static_cast<int>(pc - this->pc());
+ // Check whether we can't get any closer.
+ if (distance == 0) break;
+ }
+ Next();
+ }
+
+ // Move to the break point found.
+ Reset();
+ Next(closest_break_point);
+}
+
+
+// Find the break point closest to the supplied source position.
+void BreakLocationIterator::FindBreakLocationFromPosition(int position,
+ BreakPositionAlignment alignment) {
+ // Run through all break points to locate the one closest to the source
+ // position.
+ int closest_break_point = 0;
+ int distance = kMaxInt;
+
+ while (!Done()) {
+ int next_position;
+ switch (alignment) {
+ case STATEMENT_ALIGNED:
+ next_position = this->statement_position();
+ break;
+ case BREAK_POSITION_ALIGNED:
+ next_position = this->position();
+ break;
+ default:
+ UNREACHABLE();
+ next_position = this->statement_position();
+ }
+ // Check if this break point is closer that what was previously found.
+ if (position <= next_position && next_position - position < distance) {
+ closest_break_point = break_point();
+ distance = next_position - position;
+ // Check whether we can't get any closer.
+ if (distance == 0) break;
+ }
+ Next();
+ }
+
+ // Move to the break point found.
+ Reset();
+ Next(closest_break_point);
+}
+
+
+void BreakLocationIterator::Reset() {
+ // Create relocation iterators for the two code objects.
+ if (reloc_iterator_ != NULL) delete reloc_iterator_;
+ if (reloc_iterator_original_ != NULL) delete reloc_iterator_original_;
+ reloc_iterator_ = new RelocIterator(
+ debug_info_->code(),
+ ~RelocInfo::ModeMask(RelocInfo::CODE_AGE_SEQUENCE));
+ reloc_iterator_original_ = new RelocIterator(
+ debug_info_->original_code(),
+ ~RelocInfo::ModeMask(RelocInfo::CODE_AGE_SEQUENCE));
+
+ // Position at the first break point.
+ break_point_ = -1;
+ position_ = 1;
+ statement_position_ = 1;
+ Next();
+}
+
+
+bool BreakLocationIterator::Done() const {
+ return RinfoDone();
+}
+
+
+void BreakLocationIterator::SetBreakPoint(Handle<Object> break_point_object) {
+ // If there is not already a real break point here patch code with debug
+ // break.
+ if (!HasBreakPoint()) {
+ SetDebugBreak();
+ }
+ ASSERT(IsDebugBreak() || IsDebuggerStatement());
+ // Set the break point information.
+ DebugInfo::SetBreakPoint(debug_info_, code_position(),
+ position(), statement_position(),
+ break_point_object);
+}
+
+
+void BreakLocationIterator::ClearBreakPoint(Handle<Object> break_point_object) {
+ // Clear the break point information.
+ DebugInfo::ClearBreakPoint(debug_info_, code_position(), break_point_object);
+ // If there are no more break points here remove the debug break.
+ if (!HasBreakPoint()) {
+ ClearDebugBreak();
+ ASSERT(!IsDebugBreak());
+ }
+}
+
+
+void BreakLocationIterator::SetOneShot() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) {
+ return;
+ }
+
+ // If there is a real break point here no more to do.
+ if (HasBreakPoint()) {
+ ASSERT(IsDebugBreak());
+ return;
+ }
+
+ // Patch code with debug break.
+ SetDebugBreak();
+}
+
+
+void BreakLocationIterator::ClearOneShot() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) {
+ return;
+ }
+
+ // If there is a real break point here no more to do.
+ if (HasBreakPoint()) {
+ ASSERT(IsDebugBreak());
+ return;
+ }
+
+ // Patch code removing debug break.
+ ClearDebugBreak();
+ ASSERT(!IsDebugBreak());
+}
+
+
+void BreakLocationIterator::SetDebugBreak() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) {
+ return;
+ }
+
+ // If there is already a break point here just return. This might happen if
+ // the same code is flooded with break points twice. Flooding the same
+ // function twice might happen when stepping in a function with an exception
+ // handler as the handler and the function is the same.
+ if (IsDebugBreak()) {
+ return;
+ }
+
+ if (RelocInfo::IsJSReturn(rmode())) {
+ // Patch the frame exit code with a break point.
+ SetDebugBreakAtReturn();
+ } else if (IsDebugBreakSlot()) {
+ // Patch the code in the break slot.
+ SetDebugBreakAtSlot();
+ } else {
+ // Patch the IC call.
+ SetDebugBreakAtIC();
+ }
+ ASSERT(IsDebugBreak());
+}
+
+
+void BreakLocationIterator::ClearDebugBreak() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) {
+ return;
+ }
+
+ if (RelocInfo::IsJSReturn(rmode())) {
+ // Restore the frame exit code.
+ ClearDebugBreakAtReturn();
+ } else if (IsDebugBreakSlot()) {
+ // Restore the code in the break slot.
+ ClearDebugBreakAtSlot();
+ } else {
+ // Patch the IC call.
+ ClearDebugBreakAtIC();
+ }
+ ASSERT(!IsDebugBreak());
+}
+
+
+bool BreakLocationIterator::IsStepInLocation(Isolate* isolate) {
+ if (RelocInfo::IsConstructCall(rmode())) {
+ return true;
+ } else if (RelocInfo::IsCodeTarget(rmode())) {
+ HandleScope scope(debug_info_->GetIsolate());
+ Address target = rinfo()->target_address();
+ Handle<Code> target_code(Code::GetCodeFromTargetAddress(target));
+ if (target_code->kind() == Code::STUB) {
+ return target_code->major_key() == CodeStub::CallFunction;
+ }
+ return target_code->is_call_stub() || target_code->is_keyed_call_stub();
+ } else {
+ return false;
+ }
+}
+
+
+void BreakLocationIterator::PrepareStepIn(Isolate* isolate) {
+ HandleScope scope(isolate);
+
+ // 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> 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(
+ target_code->arguments_count(), target_code->kind());
+ if (IsDebugBreak()) {
+ original_rinfo()->set_target_address(stub->entry());
+ } else {
+ rinfo()->set_target_address(stub->entry());
+ }
+ } else {
+#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 = target_code;
+ if (IsDebugBreak()) {
+ Address original_target = original_rinfo()->target_address();
+ maybe_call_function_stub =
+ Handle<Code>(Code::GetCodeFromTargetAddress(original_target));
+ }
+ bool is_call_function_stub =
+ (maybe_call_function_stub->kind() == Code::STUB &&
+ maybe_call_function_stub->major_key() == CodeStub::CallFunction);
+
+ // Step in through construct call requires no changes to the running code.
+ // Step in through getters/setters should already be prepared as well
+ // because caller of this function (Debug::PrepareStep) is expected to
+ // flood the top frame's function with one shot breakpoints.
+ // 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()) ||
+ target_code->is_inline_cache_stub() ||
+ is_call_function_stub);
+#endif
+ }
+}
+
+
+// Check whether the break point is at a position which will exit the function.
+bool BreakLocationIterator::IsExit() const {
+ return (RelocInfo::IsJSReturn(rmode()));
+}
+
+
+bool BreakLocationIterator::HasBreakPoint() {
+ return debug_info_->HasBreakPoint(code_position());
+}
+
+
+// Check whether there is a debug break at the current position.
+bool BreakLocationIterator::IsDebugBreak() {
+ if (RelocInfo::IsJSReturn(rmode())) {
+ return IsDebugBreakAtReturn();
+ } else if (IsDebugBreakSlot()) {
+ return IsDebugBreakAtSlot();
+ } else {
+ return Debug::IsDebugBreak(rinfo()->target_address());
+ }
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtIC() {
+ // Patch the original code with the current address as the current address
+ // might have changed by the inline caching since the code was copied.
+ original_rinfo()->set_target_address(rinfo()->target_address());
+
+ RelocInfo::Mode mode = rmode();
+ if (RelocInfo::IsCodeTarget(mode)) {
+ Address target = rinfo()->target_address();
+ 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(target_code, mode));
+ rinfo()->set_target_address(dbgbrk_code->entry());
+ }
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtIC() {
+ // Patch the code to the original invoke.
+ rinfo()->set_target_address(original_rinfo()->target_address());
+}
+
+
+bool BreakLocationIterator::IsDebuggerStatement() {
+ return RelocInfo::DEBUG_BREAK == rmode();
+}
+
+
+bool BreakLocationIterator::IsDebugBreakSlot() {
+ return RelocInfo::DEBUG_BREAK_SLOT == rmode();
+}
+
+
+Object* BreakLocationIterator::BreakPointObjects() {
+ return debug_info_->GetBreakPointObjects(code_position());
+}
+
+
+// Clear out all the debug break code. This is ONLY supposed to be used when
+// shutting down the debugger as it will leave the break point information in
+// DebugInfo even though the code is patched back to the non break point state.
+void BreakLocationIterator::ClearAllDebugBreak() {
+ while (!Done()) {
+ ClearDebugBreak();
+ Next();
+ }
+}
+
+
+bool BreakLocationIterator::RinfoDone() const {
+ ASSERT(reloc_iterator_->done() == reloc_iterator_original_->done());
+ return reloc_iterator_->done();
+}
+
+
+void BreakLocationIterator::RinfoNext() {
+ reloc_iterator_->next();
+ reloc_iterator_original_->next();
+#ifdef DEBUG
+ ASSERT(reloc_iterator_->done() == reloc_iterator_original_->done());
+ if (!reloc_iterator_->done()) {
+ ASSERT(rmode() == original_rmode());
+ }
+#endif
+}
+
+
+// Threading support.
+void Debug::ThreadInit() {
+ thread_local_.break_count_ = 0;
+ thread_local_.break_id_ = 0;
+ thread_local_.break_frame_id_ = StackFrame::NO_ID;
+ thread_local_.last_step_action_ = StepNone;
+ thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
+ thread_local_.step_count_ = 0;
+ thread_local_.last_fp_ = 0;
+ thread_local_.queued_step_count_ = 0;
+ thread_local_.step_into_fp_ = 0;
+ thread_local_.step_out_fp_ = 0;
+ thread_local_.after_break_target_ = 0;
+ // TODO(isolates): frames_are_dropped_?
+ thread_local_.debugger_entry_ = NULL;
+ thread_local_.pending_interrupts_ = 0;
+ thread_local_.restarter_frame_function_pointer_ = NULL;
+}
+
+
+char* Debug::ArchiveDebug(char* storage) {
+ char* to = storage;
+ OS::MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
+ to += sizeof(ThreadLocal);
+ OS::MemCopy(to, reinterpret_cast<char*>(&registers_), sizeof(registers_));
+ ThreadInit();
+ ASSERT(to <= storage + ArchiveSpacePerThread());
+ return storage + ArchiveSpacePerThread();
+}
+
+
+char* Debug::RestoreDebug(char* storage) {
+ char* from = storage;
+ OS::MemCopy(
+ reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
+ from += sizeof(ThreadLocal);
+ OS::MemCopy(reinterpret_cast<char*>(&registers_), from, sizeof(registers_));
+ ASSERT(from <= storage + ArchiveSpacePerThread());
+ return storage + ArchiveSpacePerThread();
+}
+
+
+int Debug::ArchiveSpacePerThread() {
+ return sizeof(ThreadLocal) + sizeof(JSCallerSavedBuffer);
+}
+
+
+// Frame structure (conforms InternalFrame structure):
+// -- code
+// -- SMI maker
+// -- function (slot is called "context")
+// -- frame base
+Object** Debug::SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
+ Handle<Code> code) {
+ ASSERT(bottom_js_frame->is_java_script());
+
+ Address fp = bottom_js_frame->fp();
+
+ // Move function pointer into "context" slot.
+ Memory::Object_at(fp + StandardFrameConstants::kContextOffset) =
+ Memory::Object_at(fp + JavaScriptFrameConstants::kFunctionOffset);
+
+ Memory::Object_at(fp + InternalFrameConstants::kCodeOffset) = *code;
+ Memory::Object_at(fp + StandardFrameConstants::kMarkerOffset) =
+ Smi::FromInt(StackFrame::INTERNAL);
+
+ return reinterpret_cast<Object**>(&Memory::Object_at(
+ fp + StandardFrameConstants::kContextOffset));
+}
+
+const int Debug::kFrameDropperFrameSize = 4;
+
+
+void ScriptCache::Add(Handle<Script> script) {
+ GlobalHandles* global_handles = Isolate::Current()->global_handles();
+ // Create an entry in the hash map for the script.
+ int id = script->id()->value();
+ HashMap::Entry* entry =
+ HashMap::Lookup(reinterpret_cast<void*>(id), Hash(id), true);
+ if (entry->value != NULL) {
+ ASSERT(*script == *reinterpret_cast<Script**>(entry->value));
+ return;
+ }
+ // Globalize the script object, make it weak and use the location of the
+ // global handle as the value in the hash map.
+ Handle<Script> script_ =
+ Handle<Script>::cast(
+ (global_handles->Create(*script)));
+ global_handles->MakeWeak(reinterpret_cast<Object**>(script_.location()),
+ this,
+ ScriptCache::HandleWeakScript);
+ entry->value = script_.location();
+}
+
+
+Handle<FixedArray> ScriptCache::GetScripts() {
+ Factory* factory = Isolate::Current()->factory();
+ Handle<FixedArray> instances = factory->NewFixedArray(occupancy());
+ int count = 0;
+ for (HashMap::Entry* entry = Start(); entry != NULL; entry = Next(entry)) {
+ ASSERT(entry->value != NULL);
+ if (entry->value != NULL) {
+ instances->set(count, *reinterpret_cast<Script**>(entry->value));
+ count++;
+ }
+ }
+ return instances;
+}
+
+
+void ScriptCache::ProcessCollectedScripts() {
+ Debugger* debugger = Isolate::Current()->debugger();
+ for (int i = 0; i < collected_scripts_.length(); i++) {
+ debugger->OnScriptCollected(collected_scripts_[i]);
+ }
+ collected_scripts_.Clear();
+}
+
+
+void ScriptCache::Clear() {
+ GlobalHandles* global_handles = Isolate::Current()->global_handles();
+ // Iterate the script cache to get rid of all the weak handles.
+ for (HashMap::Entry* entry = Start(); entry != NULL; entry = Next(entry)) {
+ ASSERT(entry != NULL);
+ Object** location = reinterpret_cast<Object**>(entry->value);
+ ASSERT((*location)->IsScript());
+ global_handles->ClearWeakness(location);
+ global_handles->Destroy(location);
+ }
+ // Clear the content of the hash map.
+ HashMap::Clear();
+}
+
+
+void ScriptCache::HandleWeakScript(v8::Isolate* isolate,
+ v8::Persistent<v8::Value>* obj,
+ void* data) {
+ ScriptCache* script_cache = reinterpret_cast<ScriptCache*>(data);
+ // Find the location of the global handle.
+ Script** location =
+ reinterpret_cast<Script**>(Utils::OpenPersistent(*obj).location());
+ ASSERT((*location)->IsScript());
+
+ // Remove the entry from the cache.
+ int id = (*location)->id()->value();
+ script_cache->Remove(reinterpret_cast<void*>(id), Hash(id));
+ script_cache->collected_scripts_.Add(id);
+
+ // Clear the weak handle.
+ obj->Dispose(isolate);
+}
+
+
+void Debug::SetUp(bool create_heap_objects) {
+ ThreadInit();
+ if (create_heap_objects) {
+ // Get code to handle debug break on return.
+ debug_break_return_ =
+ isolate_->builtins()->builtin(Builtins::kReturn_DebugBreak);
+ ASSERT(debug_break_return_->IsCode());
+ // Get code to handle debug break in debug break slots.
+ debug_break_slot_ =
+ isolate_->builtins()->builtin(Builtins::kSlot_DebugBreak);
+ ASSERT(debug_break_slot_->IsCode());
+ }
+}
+
+
+void Debug::HandleWeakDebugInfo(v8::Isolate* isolate,
+ v8::Persistent<v8::Value>* obj,
+ void* data) {
+ Debug* debug = reinterpret_cast<Isolate*>(isolate)->debug();
+ DebugInfoListNode* node = reinterpret_cast<DebugInfoListNode*>(data);
+ // We need to clear all breakpoints associated with the function to restore
+ // original code and avoid patching the code twice later because
+ // the function will live in the heap until next gc, and can be found by
+ // Debug::FindSharedFunctionInfoInScript.
+ BreakLocationIterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
+ it.ClearAllDebugBreak();
+ debug->RemoveDebugInfo(node->debug_info());
+#ifdef DEBUG
+ node = debug->debug_info_list_;
+ while (node != NULL) {
+ ASSERT(node != reinterpret_cast<DebugInfoListNode*>(data));
+ node = node->next();
+ }
+#endif
+}
+
+
+DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) {
+ GlobalHandles* global_handles = Isolate::Current()->global_handles();
+ // Globalize the request debug info object and make it weak.
+ debug_info_ = Handle<DebugInfo>::cast(
+ (global_handles->Create(debug_info)));
+ global_handles->MakeWeak(reinterpret_cast<Object**>(debug_info_.location()),
+ this,
+ Debug::HandleWeakDebugInfo);
+}
+
+
+DebugInfoListNode::~DebugInfoListNode() {
+ Isolate::Current()->global_handles()->Destroy(
+ reinterpret_cast<Object**>(debug_info_.location()));
+}
+
+
+bool Debug::CompileDebuggerScript(int index) {
+ Isolate* isolate = Isolate::Current();
+ Factory* factory = isolate->factory();
+ HandleScope scope(isolate);
+
+ // Bail out if the index is invalid.
+ if (index == -1) {
+ return false;
+ }
+
+ // Find source and name for the requested script.
+ Handle<String> source_code =
+ isolate->bootstrapper()->NativesSourceLookup(index);
+ Vector<const char> name = Natives::GetScriptName(index);
+ Handle<String> script_name = factory->NewStringFromAscii(name);
+ Handle<Context> context = isolate->native_context();
+
+ // Compile the script.
+ Handle<SharedFunctionInfo> function_info;
+ function_info = Compiler::Compile(source_code,
+ script_name,
+ 0, 0,
+ false,
+ context,
+ NULL, NULL,
+ Handle<String>::null(),
+ NATIVES_CODE);
+
+ // Silently ignore stack overflows during compilation.
+ if (function_info.is_null()) {
+ ASSERT(isolate->has_pending_exception());
+ isolate->clear_pending_exception();
+ return false;
+ }
+
+ // Execute the shared function in the debugger context.
+ bool caught_exception;
+ Handle<JSFunction> function =
+ factory->NewFunctionFromSharedFunctionInfo(function_info, context);
+
+ Handle<Object> exception =
+ Execution::TryCall(function,
+ Handle<Object>(context->global_object(), isolate),
+ 0,
+ NULL,
+ &caught_exception);
+
+ // Check for caught exceptions.
+ if (caught_exception) {
+ ASSERT(!isolate->has_pending_exception());
+ MessageLocation computed_location;
+ isolate->ComputeLocation(&computed_location);
+ Handle<Object> message = MessageHandler::MakeMessageObject(
+ isolate, "error_loading_debugger", &computed_location,
+ Vector<Handle<Object> >::empty(), Handle<String>(), Handle<JSArray>());
+ ASSERT(!isolate->has_pending_exception());
+ if (!exception.is_null()) {
+ isolate->set_pending_exception(*exception);
+ MessageHandler::ReportMessage(Isolate::Current(), NULL, message);
+ isolate->clear_pending_exception();
+ }
+ return false;
+ }
+
+ // Mark this script as native and return successfully.
+ Handle<Script> script(Script::cast(function->shared()->script()));
+ script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
+ return true;
+}
+
+
+bool Debug::Load() {
+ // Return if debugger is already loaded.
+ if (IsLoaded()) return true;
+
+ Debugger* debugger = isolate_->debugger();
+
+ // Bail out if we're already in the process of compiling the native
+ // JavaScript source code for the debugger.
+ if (debugger->compiling_natives() ||
+ debugger->is_loading_debugger())
+ return false;
+ debugger->set_loading_debugger(true);
+
+ // Disable breakpoints and interrupts while compiling and running the
+ // debugger scripts including the context creation code.
+ DisableBreak disable(true);
+ PostponeInterruptsScope postpone(isolate_);
+
+ // Create the debugger context.
+ HandleScope scope(isolate_);
+ Handle<Context> context =
+ isolate_->bootstrapper()->CreateEnvironment(
+ Handle<Object>::null(),
+ v8::Handle<ObjectTemplate>(),
+ NULL);
+
+ // Fail if no context could be created.
+ if (context.is_null()) return false;
+
+ // Use the debugger context.
+ SaveContext save(isolate_);
+ isolate_->set_context(*context);
+
+ // Expose the builtins object in the debugger context.
+ Handle<String> key = isolate_->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("builtins"));
+ Handle<GlobalObject> global = Handle<GlobalObject>(context->global_object());
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate_,
+ JSReceiver::SetProperty(global,
+ key,
+ Handle<Object>(global->builtins(), isolate_),
+ NONE,
+ kNonStrictMode),
+ false);
+
+ // Compile the JavaScript for the debugger in the debugger context.
+ debugger->set_compiling_natives(true);
+ bool caught_exception =
+ !CompileDebuggerScript(Natives::GetIndex("mirror")) ||
+ !CompileDebuggerScript(Natives::GetIndex("debug"));
+
+ if (FLAG_enable_liveedit) {
+ caught_exception = caught_exception ||
+ !CompileDebuggerScript(Natives::GetIndex("liveedit"));
+ }
+
+ debugger->set_compiling_natives(false);
+
+ // Make sure we mark the debugger as not loading before we might
+ // return.
+ debugger->set_loading_debugger(false);
+
+ // Check for caught exceptions.
+ if (caught_exception) return false;
+
+ // Debugger loaded, create debugger context global handle.
+ debug_context_ = Handle<Context>::cast(
+ isolate_->global_handles()->Create(*context));
+
+ return true;
+}
+
+
+void Debug::Unload() {
+ // Return debugger is not loaded.
+ if (!IsLoaded()) {
+ return;
+ }
+
+ // Clear the script cache.
+ DestroyScriptCache();
+
+ // Clear debugger context global handle.
+ isolate_->global_handles()->Destroy(
+ reinterpret_cast<Object**>(debug_context_.location()));
+ debug_context_ = Handle<Context>();
+}
+
+
+// Set the flag indicating that preemption happened during debugging.
+void Debug::PreemptionWhileInDebugger() {
+ ASSERT(InDebugger());
+ Debug::set_interrupts_pending(PREEMPT);
+}
+
+
+void Debug::Iterate(ObjectVisitor* v) {
+ v->VisitPointer(BitCast<Object**>(&(debug_break_return_)));
+ v->VisitPointer(BitCast<Object**>(&(debug_break_slot_)));
+}
+
+
+Object* Debug::Break(Arguments args) {
+ Heap* heap = isolate_->heap();
+ HandleScope scope(isolate_);
+ ASSERT(args.length() == 0);
+
+ thread_local_.frame_drop_mode_ = FRAMES_UNTOUCHED;
+
+ // Get the top-most JavaScript frame.
+ JavaScriptFrameIterator it(isolate_);
+ JavaScriptFrame* frame = it.frame();
+
+ // Just continue if breaks are disabled or debugger cannot be loaded.
+ if (disable_break() || !Load()) {
+ SetAfterBreakTarget(frame);
+ return heap->undefined_value();
+ }
+
+ // Enter the debugger.
+ EnterDebugger debugger;
+ if (debugger.FailedToEnter()) {
+ return heap->undefined_value();
+ }
+
+ // Postpone interrupt during breakpoint processing.
+ PostponeInterruptsScope postpone(isolate_);
+
+ // Get the debug info (create it if it does not exist).
+ Handle<SharedFunctionInfo> shared =
+ Handle<SharedFunctionInfo>(frame->function()->shared());
+ Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+
+ // Find the break point where execution has stopped.
+ BreakLocationIterator break_location_iterator(debug_info,
+ ALL_BREAK_LOCATIONS);
+ // pc points to the instruction after the current one, possibly a break
+ // location as well. So the "- 1" to exclude it from the search.
+ break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
+
+ // Check whether step next reached a new statement.
+ if (!StepNextContinue(&break_location_iterator, frame)) {
+ // Decrease steps left if performing multiple steps.
+ if (thread_local_.step_count_ > 0) {
+ thread_local_.step_count_--;
+ }
+ }
+
+ // If there is one or more real break points check whether any of these are
+ // triggered.
+ Handle<Object> break_points_hit(heap->undefined_value(), isolate_);
+ if (break_location_iterator.HasBreakPoint()) {
+ Handle<Object> break_point_objects =
+ Handle<Object>(break_location_iterator.BreakPointObjects(), isolate_);
+ break_points_hit = CheckBreakPoints(break_point_objects);
+ }
+
+ // If step out is active skip everything until the frame where we need to step
+ // out to is reached, unless real breakpoint is hit.
+ if (StepOutActive() && frame->fp() != step_out_fp() &&
+ break_points_hit->IsUndefined() ) {
+ // Step count should always be 0 for StepOut.
+ ASSERT(thread_local_.step_count_ == 0);
+ } else if (!break_points_hit->IsUndefined() ||
+ (thread_local_.last_step_action_ != StepNone &&
+ thread_local_.step_count_ == 0)) {
+ // Notify debugger if a real break point is triggered or if performing
+ // single stepping with no more steps to perform. Otherwise do another step.
+
+ // Clear all current stepping setup.
+ ClearStepping();
+
+ if (thread_local_.queued_step_count_ > 0) {
+ // Perform queued steps
+ int step_count = thread_local_.queued_step_count_;
+
+ // Clear queue
+ thread_local_.queued_step_count_ = 0;
+
+ PrepareStep(StepNext, step_count);
+ } else {
+ // Notify the debug event listeners.
+ isolate_->debugger()->OnDebugBreak(break_points_hit, false);
+ }
+ } else if (thread_local_.last_step_action_ != StepNone) {
+ // Hold on to last step action as it is cleared by the call to
+ // ClearStepping.
+ StepAction step_action = thread_local_.last_step_action_;
+ int step_count = thread_local_.step_count_;
+
+ // If StepNext goes deeper in code, StepOut until original frame
+ // and keep step count queued up in the meantime.
+ if (step_action == StepNext && frame->fp() < thread_local_.last_fp_) {
+ // Count frames until target frame
+ int count = 0;
+ JavaScriptFrameIterator it(isolate_);
+ while (!it.done() && it.frame()->fp() < thread_local_.last_fp_) {
+ count++;
+ it.Advance();
+ }
+
+ // Check that we indeed found the frame we are looking for.
+ CHECK(!it.done() && (it.frame()->fp() == thread_local_.last_fp_));
+ if (step_count > 1) {
+ // Save old count and action to continue stepping after StepOut.
+ thread_local_.queued_step_count_ = step_count - 1;
+ }
+
+ // Set up for StepOut to reach target frame.
+ step_action = StepOut;
+ step_count = count;
+ }
+
+ // Clear all current stepping setup.
+ ClearStepping();
+
+ // Set up for the remaining steps.
+ PrepareStep(step_action, step_count);
+ }
+
+ if (thread_local_.frame_drop_mode_ == FRAMES_UNTOUCHED) {
+ SetAfterBreakTarget(frame);
+ } else if (thread_local_.frame_drop_mode_ ==
+ FRAME_DROPPED_IN_IC_CALL) {
+ // We must have been calling IC stub. Do not go there anymore.
+ Code* plain_return = isolate_->builtins()->builtin(
+ Builtins::kPlainReturn_LiveEdit);
+ thread_local_.after_break_target_ = plain_return->entry();
+ } else if (thread_local_.frame_drop_mode_ ==
+ FRAME_DROPPED_IN_DEBUG_SLOT_CALL) {
+ // Debug break slot stub does not return normally, instead it manually
+ // cleans the stack and jumps. We should patch the jump address.
+ Code* plain_return = isolate_->builtins()->builtin(
+ Builtins::kFrameDropper_LiveEdit);
+ thread_local_.after_break_target_ = plain_return->entry();
+ } else if (thread_local_.frame_drop_mode_ ==
+ FRAME_DROPPED_IN_DIRECT_CALL) {
+ // Nothing to do, after_break_target is not used here.
+ } else if (thread_local_.frame_drop_mode_ ==
+ FRAME_DROPPED_IN_RETURN_CALL) {
+ Code* plain_return = isolate_->builtins()->builtin(
+ Builtins::kFrameDropper_LiveEdit);
+ thread_local_.after_break_target_ = plain_return->entry();
+ } else {
+ UNREACHABLE();
+ }
+
+ return heap->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(Object*, Debug_Break) {
+ return isolate->debug()->Break(args);
+}
+
+
+// Check the break point objects for whether one or more are actually
+// triggered. This function returns a JSArray with the break point objects
+// which is triggered.
+Handle<Object> Debug::CheckBreakPoints(Handle<Object> break_point_objects) {
+ Factory* factory = isolate_->factory();
+
+ // Count the number of break points hit. If there are multiple break points
+ // they are in a FixedArray.
+ Handle<FixedArray> break_points_hit;
+ int break_points_hit_count = 0;
+ ASSERT(!break_point_objects->IsUndefined());
+ if (break_point_objects->IsFixedArray()) {
+ Handle<FixedArray> array(FixedArray::cast(*break_point_objects));
+ break_points_hit = factory->NewFixedArray(array->length());
+ for (int i = 0; i < array->length(); i++) {
+ Handle<Object> o(array->get(i), isolate_);
+ if (CheckBreakPoint(o)) {
+ break_points_hit->set(break_points_hit_count++, *o);
+ }
+ }
+ } else {
+ break_points_hit = factory->NewFixedArray(1);
+ if (CheckBreakPoint(break_point_objects)) {
+ break_points_hit->set(break_points_hit_count++, *break_point_objects);
+ }
+ }
+
+ // Return undefined if no break points were triggered.
+ if (break_points_hit_count == 0) {
+ return factory->undefined_value();
+ }
+ // Return break points hit as a JSArray.
+ Handle<JSArray> result = factory->NewJSArrayWithElements(break_points_hit);
+ result->set_length(Smi::FromInt(break_points_hit_count));
+ return result;
+}
+
+
+// Check whether a single break point object is triggered.
+bool Debug::CheckBreakPoint(Handle<Object> break_point_object) {
+ Factory* factory = isolate_->factory();
+ HandleScope scope(isolate_);
+
+ // Ignore check if break point object is not a JSObject.
+ if (!break_point_object->IsJSObject()) return true;
+
+ // Get the function IsBreakPointTriggered (defined in debug-debugger.js).
+ Handle<String> is_break_point_triggered_string =
+ factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("IsBreakPointTriggered"));
+ Handle<JSFunction> check_break_point =
+ Handle<JSFunction>(JSFunction::cast(
+ debug_context()->global_object()->GetPropertyNoExceptionThrown(
+ *is_break_point_triggered_string)));
+
+ // Get the break id as an object.
+ Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id());
+
+ // Call HandleBreakPointx.
+ bool caught_exception;
+ Handle<Object> argv[] = { break_id, break_point_object };
+ Handle<Object> result = Execution::TryCall(check_break_point,
+ 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()) {
+ return false;
+ }
+
+ // Return whether the break point is triggered.
+ ASSERT(!result.is_null());
+ return (*result)->IsTrue();
+}
+
+
+// Check whether the function has debug information.
+bool Debug::HasDebugInfo(Handle<SharedFunctionInfo> shared) {
+ return !shared->debug_info()->IsUndefined();
+}
+
+
+// Return the debug info for this function. EnsureDebugInfo must be called
+// prior to ensure the debug info has been generated for shared.
+Handle<DebugInfo> Debug::GetDebugInfo(Handle<SharedFunctionInfo> shared) {
+ ASSERT(HasDebugInfo(shared));
+ return Handle<DebugInfo>(DebugInfo::cast(shared->debug_info()));
+}
+
+
+void Debug::SetBreakPoint(Handle<JSFunction> function,
+ Handle<Object> break_point_object,
+ int* source_position) {
+ HandleScope scope(isolate_);
+
+ PrepareForBreakPoints();
+
+ // Make sure the function is compiled and has set up the debug info.
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
+ // Return if retrieving debug info failed.
+ return;
+ }
+
+ Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+ // Source positions starts with zero.
+ ASSERT(*source_position >= 0);
+
+ // Find the break point and change it.
+ BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
+ it.FindBreakLocationFromPosition(*source_position, STATEMENT_ALIGNED);
+ it.SetBreakPoint(break_point_object);
+
+ *source_position = it.position();
+
+ // At least one active break point now.
+ ASSERT(debug_info->GetBreakPointCount() > 0);
+}
+
+
+bool Debug::SetBreakPointForScript(Handle<Script> script,
+ Handle<Object> break_point_object,
+ int* source_position,
+ BreakPositionAlignment alignment) {
+ HandleScope scope(isolate_);
+
+ PrepareForBreakPoints();
+
+ // Obtain shared function info for the function.
+ Object* result = FindSharedFunctionInfoInScript(script, *source_position);
+ if (result->IsUndefined()) return false;
+
+ // Make sure the function has set up the debug info.
+ Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
+ if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) {
+ // Return if retrieving debug info failed.
+ return false;
+ }
+
+ // Find position within function. The script position might be before the
+ // source position of the first function.
+ int position;
+ if (shared->start_position() > *source_position) {
+ position = 0;
+ } else {
+ position = *source_position - shared->start_position();
+ }
+
+ Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+ // Source positions starts with zero.
+ ASSERT(position >= 0);
+
+ // Find the break point and change it.
+ BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
+ it.FindBreakLocationFromPosition(position, alignment);
+ it.SetBreakPoint(break_point_object);
+
+ *source_position = it.position() + shared->start_position();
+
+ // At least one active break point now.
+ ASSERT(debug_info->GetBreakPointCount() > 0);
+ return true;
+}
+
+
+void Debug::ClearBreakPoint(Handle<Object> break_point_object) {
+ HandleScope scope(isolate_);
+
+ DebugInfoListNode* node = debug_info_list_;
+ while (node != NULL) {
+ Object* result = DebugInfo::FindBreakPointInfo(node->debug_info(),
+ break_point_object);
+ if (!result->IsUndefined()) {
+ // Get information in the break point.
+ BreakPointInfo* break_point_info = BreakPointInfo::cast(result);
+ Handle<DebugInfo> debug_info = node->debug_info();
+
+ // Find the break point and clear it.
+ BreakLocationIterator it(debug_info, SOURCE_BREAK_LOCATIONS);
+ it.FindBreakLocationFromAddress(debug_info->code()->entry() +
+ break_point_info->code_position()->value());
+ it.ClearBreakPoint(break_point_object);
+
+ // If there are no more break points left remove the debug info for this
+ // function.
+ if (debug_info->GetBreakPointCount() == 0) {
+ RemoveDebugInfo(debug_info);
+ }
+
+ return;
+ }
+ node = node->next();
+ }
+}
+
+
+void Debug::ClearAllBreakPoints() {
+ DebugInfoListNode* node = debug_info_list_;
+ while (node != NULL) {
+ // Remove all debug break code.
+ BreakLocationIterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
+ it.ClearAllDebugBreak();
+ node = node->next();
+ }
+
+ // Remove all debug info.
+ while (debug_info_list_ != NULL) {
+ RemoveDebugInfo(debug_info_list_->debug_info());
+ }
+}
+
+
+void Debug::FloodWithOneShot(Handle<JSFunction> function) {
+ PrepareForBreakPoints();
+
+ // Make sure the function is compiled and has set up the debug info.
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
+ // Return if we failed to retrieve the debug info.
+ return;
+ }
+
+ // Flood the function with break points.
+ BreakLocationIterator it(GetDebugInfo(shared), ALL_BREAK_LOCATIONS);
+ while (!it.Done()) {
+ it.SetOneShot();
+ it.Next();
+ }
+}
+
+
+void Debug::FloodBoundFunctionWithOneShot(Handle<JSFunction> function) {
+ Handle<FixedArray> new_bindings(function->function_bindings());
+ Handle<Object> bindee(new_bindings->get(JSFunction::kBoundFunctionIndex),
+ isolate_);
+
+ if (!bindee.is_null() && bindee->IsJSFunction() &&
+ !JSFunction::cast(*bindee)->IsBuiltin()) {
+ Handle<JSFunction> bindee_function(JSFunction::cast(*bindee));
+ Debug::FloodWithOneShot(bindee_function);
+ }
+}
+
+
+void Debug::FloodHandlerWithOneShot() {
+ // Iterate through the JavaScript stack looking for handlers.
+ StackFrame::Id id = break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there is no JavaScript stack don't do anything.
+ return;
+ }
+ for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+ if (frame->HasHandler()) {
+ // Flood the function with the catch block with break points
+ FloodWithOneShot(Handle<JSFunction>(frame->function()));
+ return;
+ }
+ }
+}
+
+
+void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) {
+ if (type == BreakUncaughtException) {
+ break_on_uncaught_exception_ = enable;
+ } else {
+ break_on_exception_ = enable;
+ }
+}
+
+
+bool Debug::IsBreakOnException(ExceptionBreakType type) {
+ if (type == BreakUncaughtException) {
+ return break_on_uncaught_exception_;
+ } else {
+ return break_on_exception_;
+ }
+}
+
+
+void Debug::PrepareStep(StepAction step_action, int step_count) {
+ HandleScope scope(isolate_);
+
+ PrepareForBreakPoints();
+
+ ASSERT(Debug::InDebugger());
+
+ // Remember this step action and count.
+ thread_local_.last_step_action_ = step_action;
+ if (step_action == StepOut) {
+ // For step out target frame will be found on the stack so there is no need
+ // to set step counter for it. It's expected to always be 0 for StepOut.
+ thread_local_.step_count_ = 0;
+ } else {
+ thread_local_.step_count_ = step_count;
+ }
+
+ // Get the frame where the execution has stopped and skip the debug frame if
+ // any. The debug frame will only be present if execution was stopped due to
+ // hitting a break point. In other situations (e.g. unhandled exception) the
+ // debug frame is not present.
+ StackFrame::Id id = break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there is no JavaScript stack don't do anything.
+ return;
+ }
+ JavaScriptFrameIterator frames_it(isolate_, id);
+ JavaScriptFrame* frame = frames_it.frame();
+
+ // First of all ensure there is one-shot break points in the top handler
+ // if any.
+ FloodHandlerWithOneShot();
+
+ // If the function on the top frame is unresolved perform step out. This will
+ // be the case when calling unknown functions and having the debugger stopped
+ // in an unhandled exception.
+ if (!frame->function()->IsJSFunction()) {
+ // Step out: Find the calling JavaScript frame and flood it with
+ // breakpoints.
+ frames_it.Advance();
+ // Fill the function to return to with one-shot break points.
+ JSFunction* function = frames_it.frame()->function();
+ FloodWithOneShot(Handle<JSFunction>(function));
+ return;
+ }
+
+ // Get the debug info (create it if it does not exist).
+ Handle<JSFunction> function(frame->function());
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
+ // Return if ensuring debug info failed.
+ return;
+ }
+ Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+
+ // Find the break location where execution has stopped.
+ BreakLocationIterator it(debug_info, ALL_BREAK_LOCATIONS);
+ // pc points to the instruction after the current one, possibly a break
+ // location as well. So the "- 1" to exclude it from the search.
+ it.FindBreakLocationFromAddress(frame->pc() - 1);
+
+ // Compute whether or not the target is a call target.
+ bool is_load_or_store = false;
+ bool is_inline_cache_stub = false;
+ bool is_at_restarted_function = false;
+ Handle<Code> call_function_stub;
+
+ if (thread_local_.restarter_frame_function_pointer_ == NULL) {
+ if (RelocInfo::IsCodeTarget(it.rinfo()->rmode())) {
+ bool is_call_target = false;
+ Address target = it.rinfo()->target_address();
+ Code* code = Code::GetCodeFromTargetAddress(target);
+ if (code->is_call_stub() || code->is_keyed_call_stub()) {
+ is_call_target = true;
+ }
+ if (code->is_inline_cache_stub()) {
+ is_inline_cache_stub = true;
+ is_load_or_store = !is_call_target;
+ }
+
+ // Check if target code is CallFunction stub.
+ Code* maybe_call_function_stub = code;
+ // If there is a breakpoint at this line look at the original code to
+ // check if it is a CallFunction stub.
+ if (it.IsDebugBreak()) {
+ Address original_target = it.original_rinfo()->target_address();
+ maybe_call_function_stub =
+ Code::GetCodeFromTargetAddress(original_target);
+ }
+ if (maybe_call_function_stub->kind() == Code::STUB &&
+ maybe_call_function_stub->major_key() == CodeStub::CallFunction) {
+ // Save reference to the code as we may need it to find out arguments
+ // count for 'step in' later.
+ call_function_stub = Handle<Code>(maybe_call_function_stub);
+ }
+ }
+ } else {
+ is_at_restarted_function = true;
+ }
+
+ // If this is the last break code target step out is the only possibility.
+ if (it.IsExit() || step_action == StepOut) {
+ if (step_action == StepOut) {
+ // Skip step_count frames starting with the current one.
+ while (step_count-- > 0 && !frames_it.done()) {
+ frames_it.Advance();
+ }
+ } else {
+ ASSERT(it.IsExit());
+ frames_it.Advance();
+ }
+ // Skip builtin functions on the stack.
+ while (!frames_it.done() && frames_it.frame()->function()->IsBuiltin()) {
+ frames_it.Advance();
+ }
+ // Step out: If there is a JavaScript caller frame, we need to
+ // flood it with breakpoints.
+ if (!frames_it.done()) {
+ // Fill the function to return to with one-shot break points.
+ JSFunction* function = frames_it.frame()->function();
+ FloodWithOneShot(Handle<JSFunction>(function));
+ // Set target frame pointer.
+ ActivateStepOut(frames_it.frame());
+ }
+ } else if (!(is_inline_cache_stub || RelocInfo::IsConstructCall(it.rmode()) ||
+ !call_function_stub.is_null() || is_at_restarted_function)
+ || step_action == StepNext || step_action == StepMin) {
+ // Step next or step min.
+
+ // Fill the current function with one-shot break points.
+ FloodWithOneShot(function);
+
+ // Remember source position and frame to handle step next.
+ thread_local_.last_statement_position_ =
+ debug_info->code()->SourceStatementPosition(frame->pc());
+ thread_local_.last_fp_ = frame->UnpaddedFP();
+ } else {
+ // If there's restarter frame on top of the stack, just get the pointer
+ // to function which is going to be restarted.
+ if (is_at_restarted_function) {
+ Handle<JSFunction> restarted_function(
+ JSFunction::cast(*thread_local_.restarter_frame_function_pointer_));
+ FloodWithOneShot(restarted_function);
+ } else if (!call_function_stub.is_null()) {
+ // If it's CallFunction stub ensure target function is compiled and flood
+ // it with one shot breakpoints.
+
+ // Find out number of arguments from the stub minor key.
+ // Reverse lookup required as the minor key cannot be retrieved
+ // from the code object.
+ Handle<Object> obj(
+ isolate_->heap()->code_stubs()->SlowReverseLookup(
+ *call_function_stub),
+ isolate_);
+ ASSERT(!obj.is_null());
+ ASSERT(!(*obj)->IsUndefined());
+ ASSERT(obj->IsSmi());
+ // Get the STUB key and extract major and minor key.
+ uint32_t key = Smi::cast(*obj)->value();
+ // Argc in the stub is the number of arguments passed - not the
+ // expected arguments of the called function.
+ int call_function_arg_count =
+ CallFunctionStub::ExtractArgcFromMinorKey(
+ CodeStub::MinorKeyFromKey(key));
+ ASSERT(call_function_stub->major_key() ==
+ CodeStub::MajorKeyFromKey(key));
+
+ // Find target function on the expression stack.
+ // Expression stack looks like this (top to bottom):
+ // argN
+ // ...
+ // arg0
+ // Receiver
+ // Function to call
+ int expressions_count = frame->ComputeExpressionsCount();
+ ASSERT(expressions_count - 2 - call_function_arg_count >= 0);
+ Object* fun = frame->GetExpression(
+ expressions_count - 2 - call_function_arg_count);
+ if (fun->IsJSFunction()) {
+ Handle<JSFunction> js_function(JSFunction::cast(fun));
+ if (js_function->shared()->bound()) {
+ Debug::FloodBoundFunctionWithOneShot(js_function);
+ } else if (!js_function->IsBuiltin()) {
+ // Don't step into builtins.
+ // It will also compile target function if it's not compiled yet.
+ FloodWithOneShot(js_function);
+ }
+ }
+ }
+
+ // Fill the current function with one-shot break points even for step in on
+ // a call target as the function called might be a native function for
+ // which step in will not stop. It also prepares for stepping in
+ // getters/setters.
+ FloodWithOneShot(function);
+
+ if (is_load_or_store) {
+ // Remember source position and frame to handle step in getter/setter. If
+ // there is a custom getter/setter it will be handled in
+ // Object::Get/SetPropertyWithCallback, otherwise the step action will be
+ // propagated on the next Debug::Break.
+ thread_local_.last_statement_position_ =
+ debug_info->code()->SourceStatementPosition(frame->pc());
+ thread_local_.last_fp_ = frame->UnpaddedFP();
+ }
+
+ // Step in or Step in min
+ it.PrepareStepIn(isolate_);
+ ActivateStepIn(frame);
+ }
+}
+
+
+// Check whether the current debug break should be reported to the debugger. It
+// is used to have step next and step in only report break back to the debugger
+// if on a different frame or in a different statement. In some situations
+// there will be several break points in the same statement when the code is
+// flooded with one-shot break points. This function helps to perform several
+// steps before reporting break back to the debugger.
+bool Debug::StepNextContinue(BreakLocationIterator* break_location_iterator,
+ JavaScriptFrame* frame) {
+ // StepNext and StepOut shouldn't bring us deeper in code, so last frame
+ // shouldn't be a parent of current frame.
+ if (thread_local_.last_step_action_ == StepNext ||
+ thread_local_.last_step_action_ == StepOut) {
+ if (frame->fp() < thread_local_.last_fp_) return true;
+ }
+
+ // If the step last action was step next or step in make sure that a new
+ // statement is hit.
+ if (thread_local_.last_step_action_ == StepNext ||
+ thread_local_.last_step_action_ == StepIn) {
+ // Never continue if returning from function.
+ if (break_location_iterator->IsExit()) return false;
+
+ // Continue if we are still on the same frame and in the same statement.
+ int current_statement_position =
+ break_location_iterator->code()->SourceStatementPosition(frame->pc());
+ return thread_local_.last_fp_ == frame->UnpaddedFP() &&
+ thread_local_.last_statement_position_ == current_statement_position;
+ }
+
+ // No step next action - don't continue.
+ return false;
+}
+
+
+// Check whether the code object at the specified address is a debug break code
+// object.
+bool Debug::IsDebugBreak(Address addr) {
+ Code* code = Code::GetCodeFromTargetAddress(addr);
+ return code->is_debug_break();
+}
+
+
+// Check whether a code stub with the specified major key is a possible break
+// point location when looking for source break locations.
+bool Debug::IsSourceBreakStub(Code* code) {
+ CodeStub::Major major_key = CodeStub::GetMajorKey(code);
+ return major_key == CodeStub::CallFunction;
+}
+
+
+// Check whether a code stub with the specified major key is a possible break
+// location.
+bool Debug::IsBreakStub(Code* code) {
+ CodeStub::Major major_key = CodeStub::GetMajorKey(code);
+ return major_key == CodeStub::CallFunction;
+}
+
+
+// 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 isolate->stub_cache()->ComputeCallDebugBreak(
+ code->arguments_count(), code->kind());
+
+ case Code::LOAD_IC:
+ return isolate->builtins()->LoadIC_DebugBreak();
+
+ case Code::STORE_IC:
+ return isolate->builtins()->StoreIC_DebugBreak();
+
+ case Code::KEYED_LOAD_IC:
+ return isolate->builtins()->KeyedLoadIC_DebugBreak();
+
+ case Code::KEYED_STORE_IC:
+ return isolate->builtins()->KeyedStoreIC_DebugBreak();
+
+ case Code::COMPARE_NIL_IC:
+ return isolate->builtins()->CompareNilIC_DebugBreak();
+
+ default:
+ UNREACHABLE();
+ }
+ }
+ if (RelocInfo::IsConstructCall(mode)) {
+ 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);
+ if (code->has_function_cache()) {
+ return isolate->builtins()->CallFunctionStub_Recording_DebugBreak();
+ } else {
+ return isolate->builtins()->CallFunctionStub_DebugBreak();
+ }
+ }
+
+ UNREACHABLE();
+ return Handle<Code>::null();
+}
+
+
+// Simple function for returning the source positions for active break points.
+Handle<Object> Debug::GetSourceBreakLocations(
+ Handle<SharedFunctionInfo> shared,
+ BreakPositionAlignment position_alignment) {
+ Isolate* isolate = Isolate::Current();
+ Heap* heap = isolate->heap();
+ if (!HasDebugInfo(shared)) {
+ return Handle<Object>(heap->undefined_value(), isolate);
+ }
+ Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+ if (debug_info->GetBreakPointCount() == 0) {
+ return Handle<Object>(heap->undefined_value(), isolate);
+ }
+ Handle<FixedArray> locations =
+ isolate->factory()->NewFixedArray(debug_info->GetBreakPointCount());
+ int count = 0;
+ for (int i = 0; i < debug_info->break_points()->length(); i++) {
+ if (!debug_info->break_points()->get(i)->IsUndefined()) {
+ BreakPointInfo* break_point_info =
+ BreakPointInfo::cast(debug_info->break_points()->get(i));
+ if (break_point_info->GetBreakPointCount() > 0) {
+ Smi* position;
+ switch (position_alignment) {
+ case STATEMENT_ALIGNED:
+ position = break_point_info->statement_position();
+ break;
+ case BREAK_POSITION_ALIGNED:
+ position = break_point_info->source_position();
+ break;
+ default:
+ UNREACHABLE();
+ position = break_point_info->statement_position();
+ }
+
+ locations->set(count++, position);
+ }
+ }
+ }
+ return locations;
+}
+
+
+void Debug::NewBreak(StackFrame::Id break_frame_id) {
+ thread_local_.break_frame_id_ = break_frame_id;
+ thread_local_.break_id_ = ++thread_local_.break_count_;
+}
+
+
+void Debug::SetBreak(StackFrame::Id break_frame_id, int break_id) {
+ thread_local_.break_frame_id_ = break_frame_id;
+ thread_local_.break_id_ = break_id;
+}
+
+
+// Handle stepping into a function.
+void Debug::HandleStepIn(Handle<JSFunction> function,
+ Handle<Object> holder,
+ Address fp,
+ bool is_constructor) {
+ Isolate* isolate = function->GetIsolate();
+ // If the frame pointer is not supplied by the caller find it.
+ if (fp == 0) {
+ StackFrameIterator it(isolate);
+ it.Advance();
+ // For constructor functions skip another frame.
+ if (is_constructor) {
+ ASSERT(it.frame()->is_construct());
+ it.Advance();
+ }
+ fp = it.frame()->fp();
+ }
+
+ // Flood the function with one-shot break points if it is called from where
+ // step into was requested.
+ if (fp == step_in_fp()) {
+ if (function->shared()->bound()) {
+ // Handle Function.prototype.bind
+ Debug::FloodBoundFunctionWithOneShot(function);
+ } else if (!function->IsBuiltin()) {
+ // Don't allow step into functions in the native context.
+ if (function->shared()->code() ==
+ isolate->builtins()->builtin(Builtins::kFunctionApply) ||
+ function->shared()->code() ==
+ isolate->builtins()->builtin(Builtins::kFunctionCall)) {
+ // Handle function.apply and function.call separately to flood the
+ // function to be called and not the code for Builtins::FunctionApply or
+ // Builtins::FunctionCall. The receiver of call/apply is the target
+ // function.
+ if (!holder.is_null() && holder->IsJSFunction() &&
+ !JSFunction::cast(*holder)->IsBuiltin()) {
+ Handle<JSFunction> js_function = Handle<JSFunction>::cast(holder);
+ Debug::FloodWithOneShot(js_function);
+ }
+ } else {
+ Debug::FloodWithOneShot(function);
+ }
+ }
+ }
+}
+
+
+void Debug::ClearStepping() {
+ // Clear the various stepping setup.
+ ClearOneShot();
+ ClearStepIn();
+ ClearStepOut();
+ ClearStepNext();
+
+ // Clear multiple step counter.
+ thread_local_.step_count_ = 0;
+}
+
+
+// Clears all the one-shot break points that are currently set. Normally this
+// function is called each time a break point is hit as one shot break points
+// are used to support stepping.
+void Debug::ClearOneShot() {
+ // The current implementation just runs through all the breakpoints. When the
+ // last break point for a function is removed that function is automatically
+ // removed from the list.
+
+ DebugInfoListNode* node = debug_info_list_;
+ while (node != NULL) {
+ BreakLocationIterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
+ while (!it.Done()) {
+ it.ClearOneShot();
+ it.Next();
+ }
+ node = node->next();
+ }
+}
+
+
+void Debug::ActivateStepIn(StackFrame* frame) {
+ ASSERT(!StepOutActive());
+ thread_local_.step_into_fp_ = frame->UnpaddedFP();
+}
+
+
+void Debug::ClearStepIn() {
+ thread_local_.step_into_fp_ = 0;
+}
+
+
+void Debug::ActivateStepOut(StackFrame* frame) {
+ ASSERT(!StepInActive());
+ thread_local_.step_out_fp_ = frame->UnpaddedFP();
+}
+
+
+void Debug::ClearStepOut() {
+ thread_local_.step_out_fp_ = 0;
+}
+
+
+void Debug::ClearStepNext() {
+ thread_local_.last_step_action_ = StepNone;
+ thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
+ thread_local_.last_fp_ = 0;
+}
+
+
+// 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<JSFunction> function,
+ Handle<Code> current_code) {
+ ASSERT(!current_code->has_debug_break_slots());
+
+ CompilationInfoWithZone info(function);
+ 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(function->shared()->code());
+ ASSERT(new_code->has_debug_break_slots());
+ ASSERT(current_code->is_compiled_optimizable() ==
+ new_code->is_compiled_optimizable());
+ }
+#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(FLAG_max_inlining_levels + 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 = 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 = 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;
+ }
+
+ // Iterate over the RelocInfo in the original code to compute the sum of the
+ // constant pools sizes. (See Assembler::CheckConstPool())
+ // Note that this is only useful for architectures using constant pools.
+ int constpool_mask = RelocInfo::ModeMask(RelocInfo::CONST_POOL);
+ int frame_const_pool_size = 0;
+ for (RelocIterator it(*frame_code, constpool_mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ if (info->pc() >= frame->pc()) break;
+ frame_const_pool_size += static_cast<int>(info->data());
+ }
+ intptr_t frame_offset =
+ frame->pc() - frame_code->instruction_start() - frame_const_pool_size;
+
+ // Iterate over the RelocInfo for new code to find the number of bytes
+ // generated for debug slots and constant pools.
+ int debug_break_slot_bytes = 0;
+ int new_code_const_pool_size = 0;
+ int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) |
+ RelocInfo::ModeMask(RelocInfo::CONST_POOL);
+ 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();
+ intptr_t new_offset = info->pc() - new_code->instruction_start() -
+ new_code_const_pool_size - debug_break_slot_bytes;
+ if (new_offset >= frame_offset) {
+ break;
+ }
+
+ if (RelocInfo::IsDebugBreakSlot(info->rmode())) {
+ debug_break_slot_bytes += Assembler::kDebugBreakSlotLength;
+ } else {
+ ASSERT(RelocInfo::IsConstPool(info->rmode()));
+ // The size of the constant pool is encoded in the data.
+ new_code_const_pool_size += static_cast<int>(info->data());
+ }
+ }
+
+ // Compute the equivalent pc in the new code.
+ byte* new_pc = new_code->instruction_start() + frame_offset +
+ debug_break_slot_bytes + new_code_const_pool_size;
+
+ 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_pc));
+ }
+
+ // Patch the return address to return into the code with
+ // debug break slots.
+ frame->set_pc(new_pc);
+ }
+}
+
+
+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_) {
+ if (FLAG_parallel_recompilation) {
+ isolate_->optimizing_compiler_thread()->Flush();
+ }
+
+ Deoptimizer::DeoptimizeAll(isolate_);
+
+ Handle<Code> lazy_compile =
+ Handle<Code>(isolate_->builtins()->builtin(Builtins::kLazyCompile));
+
+ // There will be at least one break point when we are done.
+ has_break_points_ = true;
+
+ // 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.
+ Heap* heap = isolate_->heap();
+ 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.
+ DisallowHeapAllocation no_allocation;
+
+ Object* active_code_marker = 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(heap);
+ 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()) continue;
+ if (!shared->script()->IsScript()) continue;
+ if (shared->code()->gc_metadata() == active_code_marker) continue;
+
+ Code::Kind kind = function->code()->kind();
+ if (kind == Code::FUNCTION &&
+ !function->code()->has_debug_break_slots()) {
+ function->set_code(*lazy_compile);
+ function->shared()->set_code(*lazy_compile);
+ } else if (kind == Code::BUILTIN &&
+ (function->IsMarkedForInstallingRecompiledCode() ||
+ function->IsInRecompileQueue() ||
+ function->IsMarkedForLazyRecompilation() ||
+ function->IsMarkedForParallelRecompilation())) {
+ // Abort in-flight compilation.
+ Code* shared_code = function->shared()->code();
+ if (shared_code->kind() == Code::FUNCTION &&
+ shared_code->has_debug_break_slots()) {
+ function->set_code(shared_code);
+ } else {
+ 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];
+ Handle<SharedFunctionInfo> shared(function->shared());
+
+ if (function->code()->kind() == Code::FUNCTION &&
+ function->code()->has_debug_break_slots()) {
+ // Nothing to do. Function code already had debug break slots.
+ continue;
+ }
+
+ // 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());
+ 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(function, 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);
+ }
+}
+
+
+Object* Debug::FindSharedFunctionInfoInScript(Handle<Script> script,
+ int position) {
+ // Iterate the heap looking for SharedFunctionInfo generated from the
+ // script. The inner most SharedFunctionInfo containing the source position
+ // for the requested break point is found.
+ // NOTE: This might require several heap iterations. If the SharedFunctionInfo
+ // which is found is not compiled it is compiled and the heap is iterated
+ // again as the compilation might create inner functions from the newly
+ // compiled function and the actual requested break point might be in one of
+ // these functions.
+ // NOTE: The below fix-point iteration depends on all functions that cannot be
+ // compiled lazily without a context to not be compiled at all. Compilation
+ // will be triggered at points where we do not need a context.
+ bool done = false;
+ // The current candidate for the source position:
+ int target_start_position = RelocInfo::kNoPosition;
+ Handle<JSFunction> target_function;
+ Handle<SharedFunctionInfo> target;
+ Heap* heap = isolate_->heap();
+ while (!done) {
+ { // Extra scope for iterator and no-allocation.
+ heap->EnsureHeapIsIterable();
+ DisallowHeapAllocation no_alloc_during_heap_iteration;
+ HeapIterator iterator(heap);
+ for (HeapObject* obj = iterator.next();
+ obj != NULL; obj = iterator.next()) {
+ bool found_next_candidate = false;
+ Handle<JSFunction> function;
+ Handle<SharedFunctionInfo> shared;
+ if (obj->IsJSFunction()) {
+ function = Handle<JSFunction>(JSFunction::cast(obj));
+ shared = Handle<SharedFunctionInfo>(function->shared());
+ ASSERT(shared->allows_lazy_compilation() || shared->is_compiled());
+ found_next_candidate = true;
+ } else if (obj->IsSharedFunctionInfo()) {
+ shared = Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(obj));
+ // Skip functions that we cannot compile lazily without a context,
+ // which is not available here, because there is no closure.
+ found_next_candidate = shared->is_compiled() ||
+ shared->allows_lazy_compilation_without_context();
+ }
+ if (!found_next_candidate) continue;
+ 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_function = function;
+ target = shared;
+ } else {
+ if (target_start_position == start_position &&
+ shared->end_position() == target->end_position()) {
+ // If a top-level function contains only one function
+ // declaration 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_function = function;
+ 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_function = function;
+ target = shared;
+ }
+ }
+ }
+ }
+ } // End for loop.
+ } // End no-allocation scope.
+
+ if (target.is_null()) return heap->undefined_value();
+
+ // There will be at least one break point when we are done.
+ has_break_points_ = true;
+
+ // If the candidate found is compiled we are done.
+ done = target->is_compiled();
+ if (!done) {
+ // If the candidate is not compiled, compile it to reveal any inner
+ // functions which might contain the requested source position. This
+ // will compile all inner functions that cannot be compiled without a
+ // context, because Compiler::BuildFunctionInfo checks whether the
+ // debugger is active.
+ if (target_function.is_null()) {
+ SharedFunctionInfo::CompileLazy(target, KEEP_EXCEPTION);
+ } else {
+ JSFunction::CompileLazy(target_function, KEEP_EXCEPTION);
+ }
+ }
+ } // End while loop.
+
+ return *target;
+}
+
+
+// Ensures the debug information is present for shared.
+bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
+ Handle<JSFunction> function) {
+ Isolate* isolate = shared->GetIsolate();
+
+ // Return if we already have the debug info for shared.
+ if (HasDebugInfo(shared)) {
+ ASSERT(shared->is_compiled());
+ return true;
+ }
+
+ // There will be at least one break point when we are done.
+ has_break_points_ = true;
+
+ // Ensure function is compiled. Return false if this failed.
+ if (!function.is_null() &&
+ !JSFunction::EnsureCompiled(function, CLEAR_EXCEPTION)) {
+ return false;
+ }
+
+ // Create the debug info object.
+ Handle<DebugInfo> debug_info = isolate->factory()->NewDebugInfo(shared);
+
+ // Add debug info to the list.
+ DebugInfoListNode* node = new DebugInfoListNode(*debug_info);
+ node->set_next(debug_info_list_);
+ debug_info_list_ = node;
+
+ return true;
+}
+
+
+void Debug::RemoveDebugInfo(Handle<DebugInfo> debug_info) {
+ ASSERT(debug_info_list_ != NULL);
+ // Run through the debug info objects to find this one and remove it.
+ DebugInfoListNode* prev = NULL;
+ DebugInfoListNode* current = debug_info_list_;
+ while (current != NULL) {
+ if (*current->debug_info() == *debug_info) {
+ // Unlink from list. If prev is NULL we are looking at the first element.
+ if (prev == NULL) {
+ debug_info_list_ = current->next();
+ } else {
+ prev->set_next(current->next());
+ }
+ current->debug_info()->shared()->set_debug_info(
+ isolate_->heap()->undefined_value());
+ delete current;
+
+ // If there are no more debug info objects there are not more break
+ // points.
+ has_break_points_ = debug_info_list_ != NULL;
+
+ return;
+ }
+ // Move to next in list.
+ prev = current;
+ current = current->next();
+ }
+ UNREACHABLE();
+}
+
+
+void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
+ HandleScope scope(isolate_);
+
+ PrepareForBreakPoints();
+
+ // Get the executing function in which the debug break occurred.
+ Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
+ // Return if we failed to retrieve the debug info.
+ return;
+ }
+ Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+ Handle<Code> code(debug_info->code());
+ Handle<Code> original_code(debug_info->original_code());
+#ifdef DEBUG
+ // Get the code which is actually executing.
+ Handle<Code> frame_code(frame->LookupCode());
+ ASSERT(frame_code.is_identical_to(code));
+#endif
+
+ // Find the call address in the running code. This address holds the call to
+ // either a DebugBreakXXX or to the debug break return entry code if the
+ // break point is still active after processing the break point.
+ Address addr = frame->pc() - Assembler::kPatchDebugBreakSlotReturnOffset;
+
+ // Check if the location is at JS exit or debug break slot.
+ bool at_js_return = false;
+ bool break_at_js_return_active = false;
+ bool at_debug_break_slot = false;
+ RelocIterator it(debug_info->code());
+ while (!it.done() && !at_js_return && !at_debug_break_slot) {
+ if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
+ at_js_return = (it.rinfo()->pc() ==
+ addr - Assembler::kPatchReturnSequenceAddressOffset);
+ break_at_js_return_active = it.rinfo()->IsPatchedReturnSequence();
+ }
+ if (RelocInfo::IsDebugBreakSlot(it.rinfo()->rmode())) {
+ at_debug_break_slot = (it.rinfo()->pc() ==
+ addr - Assembler::kPatchDebugBreakSlotAddressOffset);
+ }
+ it.next();
+ }
+
+ // Handle the jump to continue execution after break point depending on the
+ // break location.
+ if (at_js_return) {
+ // If the break point as return is still active jump to the corresponding
+ // place in the original code. If not the break point was removed during
+ // break point processing.
+ if (break_at_js_return_active) {
+ addr += original_code->instruction_start() - code->instruction_start();
+ }
+
+ // Move back to where the call instruction sequence started.
+ thread_local_.after_break_target_ =
+ addr - Assembler::kPatchReturnSequenceAddressOffset;
+ } else if (at_debug_break_slot) {
+ // Address of where the debug break slot starts.
+ addr = addr - Assembler::kPatchDebugBreakSlotAddressOffset;
+
+ // Continue just after the slot.
+ thread_local_.after_break_target_ = addr + Assembler::kDebugBreakSlotLength;
+ } else if (IsDebugBreak(Assembler::target_address_at(addr))) {
+ // We now know that there is still a debug break call at the target address,
+ // so the break point is still there and the original code will hold the
+ // address to jump to in order to complete the call which is replaced by a
+ // call to DebugBreakXXX.
+
+ // Find the corresponding address in the original code.
+ addr += original_code->instruction_start() - code->instruction_start();
+
+ // Install jump to the call address in the original code. This will be the
+ // call which was overwritten by the call to DebugBreakXXX.
+ thread_local_.after_break_target_ = Assembler::target_address_at(addr);
+ } else {
+ // There is no longer a break point present. Don't try to look in the
+ // original code as the running code will have the right address. This takes
+ // care of the case where the last break point is removed from the function
+ // and therefore no "original code" is available.
+ thread_local_.after_break_target_ = Assembler::target_address_at(addr);
+ }
+}
+
+
+bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
+ HandleScope scope(isolate_);
+
+ // If there are no break points this cannot be break at return, as
+ // the debugger statement and stack guard bebug break cannot be at
+ // return.
+ if (!has_break_points_) {
+ return false;
+ }
+
+ PrepareForBreakPoints();
+
+ // Get the executing function in which the debug break occurred.
+ Handle<JSFunction> function(JSFunction::cast(frame->function()));
+ Handle<SharedFunctionInfo> shared(function->shared());
+ if (!EnsureDebugInfo(shared, function)) {
+ // Return if we failed to retrieve the debug info.
+ return false;
+ }
+ Handle<DebugInfo> debug_info = GetDebugInfo(shared);
+ Handle<Code> code(debug_info->code());
+#ifdef DEBUG
+ // Get the code which is actually executing.
+ Handle<Code> frame_code(frame->LookupCode());
+ ASSERT(frame_code.is_identical_to(code));
+#endif
+
+ // Find the call address in the running code.
+ Address addr = frame->pc() - Assembler::kPatchDebugBreakSlotReturnOffset;
+
+ // Check if the location is at JS return.
+ RelocIterator it(debug_info->code());
+ while (!it.done()) {
+ if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
+ return (it.rinfo()->pc() ==
+ addr - Assembler::kPatchReturnSequenceAddressOffset);
+ }
+ it.next();
+ }
+ return false;
+}
+
+
+void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
+ FrameDropMode mode,
+ Object** restarter_frame_function_pointer) {
+ if (mode != CURRENTLY_SET_MODE) {
+ thread_local_.frame_drop_mode_ = mode;
+ }
+ thread_local_.break_frame_id_ = new_break_frame_id;
+ thread_local_.restarter_frame_function_pointer_ =
+ restarter_frame_function_pointer;
+}
+
+
+const int Debug::FramePaddingLayout::kInitialSize = 1;
+
+
+// Any even value bigger than kInitialSize as needed for stack scanning.
+const int Debug::FramePaddingLayout::kPaddingValue = kInitialSize + 1;
+
+
+bool Debug::IsDebugGlobal(GlobalObject* global) {
+ return IsLoaded() && global == debug_context()->global_object();
+}
+
+
+void Debug::ClearMirrorCache() {
+ PostponeInterruptsScope postpone(isolate_);
+ HandleScope scope(isolate_);
+ ASSERT(isolate_->context() == *Debug::debug_context());
+
+ // Clear the mirror cache.
+ Handle<String> function_name = isolate_->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("ClearMirrorCache"));
+ Handle<Object> fun(
+ isolate_->global_object()->GetPropertyNoExceptionThrown(*function_name),
+ isolate_);
+ ASSERT(fun->IsJSFunction());
+ bool caught_exception;
+ Execution::TryCall(Handle<JSFunction>::cast(fun),
+ Handle<JSObject>(Debug::debug_context()->global_object()),
+ 0, NULL, &caught_exception);
+}
+
+
+void Debug::CreateScriptCache() {
+ Heap* heap = isolate_->heap();
+ HandleScope scope(isolate_);
+
+ // 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. 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();
+
+ // Scan heap for Script objects.
+ int count = 0;
+ HeapIterator iterator(heap);
+ DisallowHeapAllocation 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)));
+ count++;
+ }
+ }
+}
+
+
+void Debug::DestroyScriptCache() {
+ // Get rid of the script cache if it was created.
+ if (script_cache_ != NULL) {
+ delete script_cache_;
+ script_cache_ = NULL;
+ }
+}
+
+
+void Debug::AddScriptToScriptCache(Handle<Script> script) {
+ if (script_cache_ != NULL) {
+ script_cache_->Add(script);
+ }
+}
+
+
+Handle<FixedArray> Debug::GetLoadedScripts() {
+ // Create and fill the script cache when the loaded scripts is requested for
+ // the first time.
+ if (script_cache_ == NULL) {
+ CreateScriptCache();
+ }
+
+ // If the script cache is not active just return an empty array.
+ ASSERT(script_cache_ != NULL);
+ if (script_cache_ == NULL) {
+ isolate_->factory()->NewFixedArray(0);
+ }
+
+ // Perform GC to get unreferenced scripts evicted from the cache before
+ // returning the content.
+ isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
+ "Debug::GetLoadedScripts");
+
+ // Get the scripts from the cache.
+ return script_cache_->GetScripts();
+}
+
+
+void Debug::AfterGarbageCollection() {
+ // Generate events for collected scripts.
+ if (script_cache_ != NULL) {
+ script_cache_->ProcessCollectedScripts();
+ }
+}
+
+
+Debugger::Debugger(Isolate* isolate)
+ : debugger_access_(isolate->debugger_access()),
+ event_listener_(Handle<Object>()),
+ event_listener_data_(Handle<Object>()),
+ compiling_natives_(false),
+ is_loading_debugger_(false),
+ live_edit_enabled_(true),
+ never_unload_debugger_(false),
+ force_debugger_active_(false),
+ message_handler_(NULL),
+ debugger_unload_pending_(false),
+ host_dispatch_handler_(NULL),
+ dispatch_handler_access_(OS::CreateMutex()),
+ debug_message_dispatch_handler_(NULL),
+ message_dispatch_helper_thread_(NULL),
+ host_dispatch_micros_(100 * 1000),
+ agent_(NULL),
+ command_queue_(isolate->logger(), kQueueInitialSize),
+ command_received_(OS::CreateSemaphore(0)),
+ event_command_queue_(isolate->logger(), kQueueInitialSize),
+ isolate_(isolate) {
+}
+
+
+Debugger::~Debugger() {
+ delete dispatch_handler_access_;
+ dispatch_handler_access_ = 0;
+ delete command_received_;
+ command_received_ = 0;
+}
+
+
+Handle<Object> Debugger::MakeJSObject(Vector<const char> constructor_name,
+ int argc,
+ Handle<Object> argv[],
+ bool* caught_exception) {
+ ASSERT(isolate_->context() == *isolate_->debug()->debug_context());
+
+ // Create the execution state object.
+ Handle<String> constructor_str =
+ isolate_->factory()->InternalizeUtf8String(constructor_name);
+ Handle<Object> constructor(
+ isolate_->global_object()->GetPropertyNoExceptionThrown(*constructor_str),
+ isolate_);
+ ASSERT(constructor->IsJSFunction());
+ if (!constructor->IsJSFunction()) {
+ *caught_exception = true;
+ return isolate_->factory()->undefined_value();
+ }
+ Handle<Object> js_object = Execution::TryCall(
+ Handle<JSFunction>::cast(constructor),
+ Handle<JSObject>(isolate_->debug()->debug_context()->global_object()),
+ argc,
+ argv,
+ caught_exception);
+ return js_object;
+}
+
+
+Handle<Object> Debugger::MakeExecutionState(bool* caught_exception) {
+ // Create the execution state object.
+ Handle<Object> break_id = isolate_->factory()->NewNumberFromInt(
+ isolate_->debug()->break_id());
+ Handle<Object> argv[] = { break_id };
+ return MakeJSObject(CStrVector("MakeExecutionState"),
+ ARRAY_SIZE(argv),
+ argv,
+ caught_exception);
+}
+
+
+Handle<Object> Debugger::MakeBreakEvent(Handle<Object> exec_state,
+ Handle<Object> break_points_hit,
+ bool* caught_exception) {
+ // Create the new break event object.
+ Handle<Object> argv[] = { exec_state, break_points_hit };
+ return MakeJSObject(CStrVector("MakeBreakEvent"),
+ ARRAY_SIZE(argv),
+ argv,
+ caught_exception);
+}
+
+
+Handle<Object> Debugger::MakeExceptionEvent(Handle<Object> exec_state,
+ Handle<Object> exception,
+ bool uncaught,
+ bool* caught_exception) {
+ Factory* factory = isolate_->factory();
+ // Create the new exception event object.
+ Handle<Object> argv[] = { exec_state,
+ exception,
+ factory->ToBoolean(uncaught) };
+ return MakeJSObject(CStrVector("MakeExceptionEvent"),
+ ARRAY_SIZE(argv),
+ argv,
+ caught_exception);
+}
+
+
+Handle<Object> Debugger::MakeNewFunctionEvent(Handle<Object> function,
+ bool* caught_exception) {
+ // Create the new function event object.
+ Handle<Object> argv[] = { function };
+ return MakeJSObject(CStrVector("MakeNewFunctionEvent"),
+ ARRAY_SIZE(argv),
+ argv,
+ caught_exception);
+}
+
+
+Handle<Object> Debugger::MakeCompileEvent(Handle<Script> script,
+ bool before,
+ bool* caught_exception) {
+ Factory* factory = isolate_->factory();
+ // Create the compile event object.
+ Handle<Object> exec_state = MakeExecutionState(caught_exception);
+ Handle<Object> script_wrapper = GetScriptWrapper(script);
+ Handle<Object> argv[] = { exec_state,
+ script_wrapper,
+ factory->ToBoolean(before) };
+ return MakeJSObject(CStrVector("MakeCompileEvent"),
+ ARRAY_SIZE(argv),
+ argv,
+ caught_exception);
+}
+
+
+Handle<Object> Debugger::MakeScriptCollectedEvent(int id,
+ bool* caught_exception) {
+ // Create the script collected event object.
+ Handle<Object> exec_state = MakeExecutionState(caught_exception);
+ Handle<Object> id_object = Handle<Smi>(Smi::FromInt(id), isolate_);
+ Handle<Object> argv[] = { exec_state, id_object };
+
+ return MakeJSObject(CStrVector("MakeScriptCollectedEvent"),
+ ARRAY_SIZE(argv),
+ argv,
+ caught_exception);
+}
+
+
+void Debugger::OnException(Handle<Object> exception, bool uncaught) {
+ HandleScope scope(isolate_);
+ Debug* debug = isolate_->debug();
+
+ // Bail out based on state or if there is no listener for this event
+ if (debug->InDebugger()) return;
+ if (!Debugger::EventActive(v8::Exception)) return;
+
+ // Bail out if exception breaks are not active
+ if (uncaught) {
+ // Uncaught exceptions are reported by either flags.
+ if (!(debug->break_on_uncaught_exception() ||
+ debug->break_on_exception())) return;
+ } else {
+ // Caught exceptions are reported is activated.
+ if (!debug->break_on_exception()) return;
+ }
+
+ // Enter the debugger.
+ EnterDebugger debugger;
+ if (debugger.FailedToEnter()) return;
+
+ // Clear all current stepping setup.
+ debug->ClearStepping();
+ // Create the event data object.
+ bool caught_exception = false;
+ Handle<Object> exec_state = MakeExecutionState(&caught_exception);
+ Handle<Object> event_data;
+ if (!caught_exception) {
+ event_data = MakeExceptionEvent(exec_state, exception, uncaught,
+ &caught_exception);
+ }
+ // Bail out and don't call debugger if exception.
+ if (caught_exception) {
+ return;
+ }
+
+ // Process debug event.
+ ProcessDebugEvent(v8::Exception, Handle<JSObject>::cast(event_data), false);
+ // Return to continue execution from where the exception was thrown.
+}
+
+
+void Debugger::OnDebugBreak(Handle<Object> break_points_hit,
+ bool auto_continue) {
+ HandleScope scope(isolate_);
+
+ // Debugger has already been entered by caller.
+ ASSERT(isolate_->context() == *isolate_->debug()->debug_context());
+
+ // Bail out if there is no listener for this event
+ if (!Debugger::EventActive(v8::Break)) return;
+
+ // Debugger must be entered in advance.
+ ASSERT(isolate_->context() == *isolate_->debug()->debug_context());
+
+ // Create the event data object.
+ bool caught_exception = false;
+ Handle<Object> exec_state = MakeExecutionState(&caught_exception);
+ Handle<Object> event_data;
+ if (!caught_exception) {
+ event_data = MakeBreakEvent(exec_state, break_points_hit,
+ &caught_exception);
+ }
+ // Bail out and don't call debugger if exception.
+ if (caught_exception) {
+ return;
+ }
+
+ // Process debug event.
+ ProcessDebugEvent(v8::Break,
+ Handle<JSObject>::cast(event_data),
+ auto_continue);
+}
+
+
+void Debugger::OnBeforeCompile(Handle<Script> script) {
+ HandleScope scope(isolate_);
+
+ // Bail out based on state or if there is no listener for this event
+ if (isolate_->debug()->InDebugger()) return;
+ if (compiling_natives()) return;
+ if (!EventActive(v8::BeforeCompile)) return;
+
+ // Enter the debugger.
+ EnterDebugger debugger;
+ if (debugger.FailedToEnter()) return;
+
+ // Create the event data object.
+ bool caught_exception = false;
+ Handle<Object> event_data = MakeCompileEvent(script, true, &caught_exception);
+ // Bail out and don't call debugger if exception.
+ if (caught_exception) {
+ return;
+ }
+
+ // Process debug event.
+ ProcessDebugEvent(v8::BeforeCompile,
+ Handle<JSObject>::cast(event_data),
+ true);
+}
+
+
+// Handle debugger actions when a new script is compiled.
+void Debugger::OnAfterCompile(Handle<Script> script,
+ AfterCompileFlags after_compile_flags) {
+ HandleScope scope(isolate_);
+ Debug* debug = isolate_->debug();
+
+ // Add the newly compiled script to the script cache.
+ debug->AddScriptToScriptCache(script);
+
+ // No more to do if not debugging.
+ if (!IsDebuggerActive()) return;
+
+ // No compile events while compiling natives.
+ if (compiling_natives()) return;
+
+ // Store whether in debugger before entering debugger.
+ bool in_debugger = debug->InDebugger();
+
+ // Enter the debugger.
+ EnterDebugger debugger;
+ if (debugger.FailedToEnter()) return;
+
+ // If debugging there might be script break points registered for this
+ // script. Make sure that these break points are set.
+
+ // Get the function UpdateScriptBreakPoints (defined in debug-debugger.js).
+ Handle<String> update_script_break_points_string =
+ isolate_->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("UpdateScriptBreakPoints"));
+ Handle<Object> update_script_break_points =
+ Handle<Object>(
+ debug->debug_context()->global_object()->GetPropertyNoExceptionThrown(
+ *update_script_break_points_string),
+ isolate_);
+ if (!update_script_break_points->IsJSFunction()) {
+ return;
+ }
+ ASSERT(update_script_break_points->IsJSFunction());
+
+ // Wrap the script object in a proper JS object before passing it
+ // to JavaScript.
+ Handle<JSValue> wrapper = GetScriptWrapper(script);
+
+ // Call UpdateScriptBreakPoints expect no exceptions.
+ bool caught_exception;
+ Handle<Object> argv[] = { wrapper };
+ Execution::TryCall(Handle<JSFunction>::cast(update_script_break_points),
+ Isolate::Current()->js_builtins_object(),
+ ARRAY_SIZE(argv),
+ argv,
+ &caught_exception);
+ if (caught_exception) {
+ return;
+ }
+ // Bail out based on state or if there is no listener for this event
+ if (in_debugger && (after_compile_flags & SEND_WHEN_DEBUGGING) == 0) return;
+ if (!Debugger::EventActive(v8::AfterCompile)) return;
+
+ // Create the compile state object.
+ Handle<Object> event_data = MakeCompileEvent(script,
+ false,
+ &caught_exception);
+ // Bail out and don't call debugger if exception.
+ if (caught_exception) {
+ return;
+ }
+ // Process debug event.
+ ProcessDebugEvent(v8::AfterCompile,
+ Handle<JSObject>::cast(event_data),
+ true);
+}
+
+
+void Debugger::OnScriptCollected(int id) {
+ HandleScope scope(isolate_);
+
+ // No more to do if not debugging.
+ if (isolate_->debug()->InDebugger()) return;
+ if (!IsDebuggerActive()) return;
+ if (!Debugger::EventActive(v8::ScriptCollected)) return;
+
+ // Enter the debugger.
+ EnterDebugger debugger;
+ if (debugger.FailedToEnter()) return;
+
+ // Create the script collected state object.
+ bool caught_exception = false;
+ Handle<Object> event_data = MakeScriptCollectedEvent(id,
+ &caught_exception);
+ // Bail out and don't call debugger if exception.
+ if (caught_exception) {
+ return;
+ }
+
+ // Process debug event.
+ ProcessDebugEvent(v8::ScriptCollected,
+ Handle<JSObject>::cast(event_data),
+ true);
+}
+
+
+void Debugger::ProcessDebugEvent(v8::DebugEvent event,
+ Handle<JSObject> event_data,
+ bool auto_continue) {
+ HandleScope scope(isolate_);
+
+ // Clear any pending debug break if this is a real break.
+ if (!auto_continue) {
+ isolate_->debug()->clear_interrupt_pending(DEBUGBREAK);
+ }
+
+ // Create the execution state.
+ bool caught_exception = false;
+ Handle<Object> exec_state = MakeExecutionState(&caught_exception);
+ if (caught_exception) {
+ return;
+ }
+ // First notify the message handler if any.
+ if (message_handler_ != NULL) {
+ NotifyMessageHandler(event,
+ Handle<JSObject>::cast(exec_state),
+ event_data,
+ auto_continue);
+ }
+ // Notify registered debug event listener. This can be either a C or
+ // a JavaScript function. Don't call event listener for v8::Break
+ // here, if it's only a debug command -- they will be processed later.
+ if ((event != v8::Break || !auto_continue) && !event_listener_.is_null()) {
+ CallEventCallback(event, exec_state, event_data, NULL);
+ }
+ // Process pending debug commands.
+ if (event == v8::Break) {
+ while (!event_command_queue_.IsEmpty()) {
+ CommandMessage command = event_command_queue_.Get();
+ if (!event_listener_.is_null()) {
+ CallEventCallback(v8::BreakForCommand,
+ exec_state,
+ event_data,
+ command.client_data());
+ }
+ command.Dispose();
+ }
+ }
+}
+
+
+void Debugger::CallEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data) {
+ if (event_listener_->IsForeign()) {
+ CallCEventCallback(event, exec_state, event_data, client_data);
+ } else {
+ CallJSEventCallback(event, exec_state, event_data);
+ }
+}
+
+
+void Debugger::CallCEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ 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->foreign_address());
+ EventDetailsImpl event_details(
+ event,
+ Handle<JSObject>::cast(exec_state),
+ Handle<JSObject>::cast(event_data),
+ event_listener_data_,
+ client_data);
+ callback(event_details);
+}
+
+
+void Debugger::CallJSEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data) {
+ ASSERT(event_listener_->IsJSFunction());
+ Handle<JSFunction> fun(Handle<JSFunction>::cast(event_listener_));
+
+ // Invoke the JavaScript debug event listener.
+ Handle<Object> argv[] = { Handle<Object>(Smi::FromInt(event), isolate_),
+ exec_state,
+ event_data,
+ event_listener_data_ };
+ bool caught_exception;
+ Execution::TryCall(fun,
+ isolate_->global_object(),
+ ARRAY_SIZE(argv),
+ argv,
+ &caught_exception);
+ // Silently ignore exceptions from debug event listeners.
+}
+
+
+Handle<Context> Debugger::GetDebugContext() {
+ never_unload_debugger_ = true;
+ EnterDebugger debugger;
+ return isolate_->debug()->debug_context();
+}
+
+
+void Debugger::UnloadDebugger() {
+ Debug* debug = isolate_->debug();
+
+ // Make sure that there are no breakpoints left.
+ debug->ClearAllBreakPoints();
+
+ // Unload the debugger if feasible.
+ if (!never_unload_debugger_) {
+ debug->Unload();
+ }
+
+ // Clear the flag indicating that the debugger should be unloaded.
+ debugger_unload_pending_ = false;
+}
+
+
+void Debugger::NotifyMessageHandler(v8::DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ bool auto_continue) {
+ HandleScope scope(isolate_);
+
+ if (!isolate_->debug()->Load()) return;
+
+ // Process the individual events.
+ bool sendEventMessage = false;
+ switch (event) {
+ case v8::Break:
+ case v8::BreakForCommand:
+ sendEventMessage = !auto_continue;
+ break;
+ case v8::Exception:
+ sendEventMessage = true;
+ break;
+ case v8::BeforeCompile:
+ break;
+ case v8::AfterCompile:
+ sendEventMessage = true;
+ break;
+ case v8::ScriptCollected:
+ sendEventMessage = true;
+ break;
+ case v8::NewFunction:
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // The debug command interrupt flag might have been set when the command was
+ // added. It should be enough to clear the flag only once while we are in the
+ // debugger.
+ ASSERT(isolate_->debug()->InDebugger());
+ isolate_->stack_guard()->Continue(DEBUGCOMMAND);
+
+ // Notify the debugger that a debug event has occurred unless auto continue is
+ // active in which case no event is send.
+ if (sendEventMessage) {
+ MessageImpl message = MessageImpl::NewEvent(
+ event,
+ auto_continue,
+ Handle<JSObject>::cast(exec_state),
+ Handle<JSObject>::cast(event_data));
+ InvokeMessageHandler(message);
+ }
+
+ // If auto continue don't make the event cause a break, but process messages
+ // in the queue if any. For script collected events don't even process
+ // messages in the queue as the execution state might not be what is expected
+ // by the client.
+ if ((auto_continue && !HasCommands()) || event == v8::ScriptCollected) {
+ return;
+ }
+
+ v8::TryCatch try_catch;
+
+ // DebugCommandProcessor goes here.
+ v8::Local<v8::Object> cmd_processor;
+ {
+ v8::Local<v8::Object> api_exec_state =
+ v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state));
+ v8::Local<v8::String> fun_name =
+ v8::String::New("debugCommandProcessor");
+ v8::Local<v8::Function> fun =
+ v8::Local<v8::Function>::Cast(api_exec_state->Get(fun_name));
+
+ v8::Handle<v8::Boolean> running =
+ auto_continue ? v8::True() : v8::False();
+ static const int kArgc = 1;
+ v8::Handle<Value> argv[kArgc] = { running };
+ cmd_processor = v8::Local<v8::Object>::Cast(
+ fun->Call(api_exec_state, kArgc, argv));
+ if (try_catch.HasCaught()) {
+ PrintLn(try_catch.Exception());
+ return;
+ }
+ }
+
+ bool running = auto_continue;
+
+ // Process requests from the debugger.
+ while (true) {
+ // Wait for new command in the queue.
+ if (Debugger::host_dispatch_handler_) {
+ // In case there is a host dispatch - do periodic dispatches.
+ if (!command_received_->Wait(host_dispatch_micros_)) {
+ // Timout expired, do the dispatch.
+ Debugger::host_dispatch_handler_();
+ continue;
+ }
+ } else {
+ // In case there is no host dispatch - just wait.
+ command_received_->Wait();
+ }
+
+ // Get the command from the queue.
+ CommandMessage command = command_queue_.Get();
+ isolate_->logger()->DebugTag(
+ "Got request from command queue, in interactive loop.");
+ if (!Debugger::IsDebuggerActive()) {
+ // Delete command text and user data.
+ command.Dispose();
+ return;
+ }
+
+ // Invoke JavaScript to process the debug request.
+ v8::Local<v8::String> fun_name;
+ v8::Local<v8::Function> fun;
+ v8::Local<v8::Value> request;
+ v8::TryCatch try_catch;
+ fun_name = v8::String::New("processDebugRequest");
+ fun = v8::Local<v8::Function>::Cast(cmd_processor->Get(fun_name));
+
+ request = v8::String::New(command.text().start(),
+ command.text().length());
+ static const int kArgc = 1;
+ v8::Handle<Value> argv[kArgc] = { request };
+ v8::Local<v8::Value> response_val = fun->Call(cmd_processor, kArgc, argv);
+
+ // Get the response.
+ v8::Local<v8::String> response;
+ if (!try_catch.HasCaught()) {
+ // Get response string.
+ if (!response_val->IsUndefined()) {
+ response = v8::Local<v8::String>::Cast(response_val);
+ } else {
+ response = v8::String::New("");
+ }
+
+ // Log the JSON request/response.
+ if (FLAG_trace_debug_json) {
+ PrintLn(request);
+ PrintLn(response);
+ }
+
+ // Get the running state.
+ fun_name = v8::String::New("isRunning");
+ fun = v8::Local<v8::Function>::Cast(cmd_processor->Get(fun_name));
+ static const int kArgc = 1;
+ v8::Handle<Value> argv[kArgc] = { response };
+ v8::Local<v8::Value> running_val = fun->Call(cmd_processor, kArgc, argv);
+ if (!try_catch.HasCaught()) {
+ running = running_val->ToBoolean()->Value();
+ }
+ } else {
+ // In case of failure the result text is the exception text.
+ response = try_catch.Exception()->ToString();
+ }
+
+ // Return the result.
+ MessageImpl message = MessageImpl::NewResponse(
+ event,
+ running,
+ Handle<JSObject>::cast(exec_state),
+ Handle<JSObject>::cast(event_data),
+ Handle<String>(Utils::OpenHandle(*response)),
+ command.client_data());
+ InvokeMessageHandler(message);
+ command.Dispose();
+
+ // Return from debug event processing if either the VM is put into the
+ // running state (through a continue command) or auto continue is active
+ // and there are no more commands queued.
+ if (running && !HasCommands()) {
+ return;
+ }
+ }
+}
+
+
+void Debugger::SetEventListener(Handle<Object> callback,
+ Handle<Object> data) {
+ HandleScope scope(isolate_);
+ GlobalHandles* global_handles = isolate_->global_handles();
+
+ // Clear the global handles for the event listener and the event listener data
+ // object.
+ if (!event_listener_.is_null()) {
+ global_handles->Destroy(
+ reinterpret_cast<Object**>(event_listener_.location()));
+ event_listener_ = Handle<Object>();
+ }
+ if (!event_listener_data_.is_null()) {
+ global_handles->Destroy(
+ reinterpret_cast<Object**>(event_listener_data_.location()));
+ event_listener_data_ = Handle<Object>();
+ }
+
+ // If there is a new debug event listener register it together with its data
+ // object.
+ if (!callback->IsUndefined() && !callback->IsNull()) {
+ event_listener_ = Handle<Object>::cast(
+ global_handles->Create(*callback));
+ if (data.is_null()) {
+ data = isolate_->factory()->undefined_value();
+ }
+ event_listener_data_ = Handle<Object>::cast(
+ global_handles->Create(*data));
+ }
+
+ ListenersChanged();
+}
+
+
+void Debugger::SetMessageHandler(v8::Debug::MessageHandler2 handler) {
+ ScopedLock with(debugger_access_);
+
+ message_handler_ = handler;
+ ListenersChanged();
+ if (handler == NULL) {
+ // Send an empty command to the debugger if in a break to make JavaScript
+ // run again if the debugger is closed.
+ if (isolate_->debug()->InDebugger()) {
+ ProcessCommand(Vector<const uint16_t>::empty());
+ }
+ }
+}
+
+
+void Debugger::ListenersChanged() {
+ if (IsDebuggerActive()) {
+ // Disable the compilation cache when the debugger is active.
+ isolate_->compilation_cache()->Disable();
+ debugger_unload_pending_ = false;
+ } else {
+ isolate_->compilation_cache()->Enable();
+ // Unload the debugger if event listener and message handler cleared.
+ // Schedule this for later, because we may be in non-V8 thread.
+ debugger_unload_pending_ = true;
+ }
+}
+
+
+void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
+ int period) {
+ host_dispatch_handler_ = handler;
+ host_dispatch_micros_ = period * 1000;
+}
+
+
+void Debugger::SetDebugMessageDispatchHandler(
+ v8::Debug::DebugMessageDispatchHandler handler, bool provide_locker) {
+ ScopedLock with(dispatch_handler_access_);
+ debug_message_dispatch_handler_ = handler;
+
+ if (provide_locker && message_dispatch_helper_thread_ == NULL) {
+ message_dispatch_helper_thread_ = new MessageDispatchHelperThread(isolate_);
+ message_dispatch_helper_thread_->Start();
+ }
+}
+
+
+// Calls the registered debug message handler. This callback is part of the
+// public API.
+void Debugger::InvokeMessageHandler(MessageImpl message) {
+ ScopedLock with(debugger_access_);
+
+ if (message_handler_ != NULL) {
+ message_handler_(message);
+ }
+}
+
+
+// Puts a command coming from the public API on the queue. Creates
+// a copy of the command string managed by the debugger. Up to this
+// point, the command data was managed by the API client. Called
+// by the API client thread.
+void Debugger::ProcessCommand(Vector<const uint16_t> command,
+ v8::Debug::ClientData* client_data) {
+ // Need to cast away const.
+ CommandMessage message = CommandMessage::New(
+ Vector<uint16_t>(const_cast<uint16_t*>(command.start()),
+ command.length()),
+ client_data);
+ isolate_->logger()->DebugTag("Put command on command_queue.");
+ command_queue_.Put(message);
+ command_received_->Signal();
+
+ // Set the debug command break flag to have the command processed.
+ if (!isolate_->debug()->InDebugger()) {
+ isolate_->stack_guard()->DebugCommand();
+ }
+
+ MessageDispatchHelperThread* dispatch_thread;
+ {
+ ScopedLock with(dispatch_handler_access_);
+ dispatch_thread = message_dispatch_helper_thread_;
+ }
+
+ if (dispatch_thread == NULL) {
+ CallMessageDispatchHandler();
+ } else {
+ dispatch_thread->Schedule();
+ }
+}
+
+
+bool Debugger::HasCommands() {
+ return !command_queue_.IsEmpty();
+}
+
+
+void Debugger::EnqueueDebugCommand(v8::Debug::ClientData* client_data) {
+ CommandMessage message = CommandMessage::New(Vector<uint16_t>(), client_data);
+ event_command_queue_.Put(message);
+
+ // Set the debug command break flag to have the command processed.
+ if (!isolate_->debug()->InDebugger()) {
+ isolate_->stack_guard()->DebugCommand();
+ }
+}
+
+
+bool Debugger::IsDebuggerActive() {
+ ScopedLock with(debugger_access_);
+
+ return message_handler_ != NULL ||
+ !event_listener_.is_null() ||
+ force_debugger_active_;
+}
+
+
+Handle<Object> Debugger::Call(Handle<JSFunction> fun,
+ Handle<Object> data,
+ bool* pending_exception) {
+ // When calling functions in the debugger prevent it from beeing unloaded.
+ Debugger::never_unload_debugger_ = true;
+
+ // Enter the debugger.
+ EnterDebugger debugger;
+ if (debugger.FailedToEnter()) {
+ return isolate_->factory()->undefined_value();
+ }
+
+ // Create the execution state.
+ bool caught_exception = false;
+ Handle<Object> exec_state = MakeExecutionState(&caught_exception);
+ if (caught_exception) {
+ return isolate_->factory()->undefined_value();
+ }
+
+ Handle<Object> argv[] = { exec_state, data };
+ Handle<Object> result = Execution::Call(
+ fun,
+ Handle<Object>(isolate_->debug()->debug_context_->global_proxy(),
+ isolate_),
+ ARRAY_SIZE(argv),
+ argv,
+ pending_exception);
+ return result;
+}
+
+
+static void StubMessageHandler2(const v8::Debug::Message& message) {
+ // Simply ignore message.
+}
+
+
+bool Debugger::StartAgent(const char* name, int port,
+ bool wait_for_connection) {
+ ASSERT(Isolate::Current() == isolate_);
+ if (wait_for_connection) {
+ // Suspend V8 if it is already running or set V8 to suspend whenever
+ // it starts.
+ // Provide stub message handler; V8 auto-continues each suspend
+ // when there is no message handler; we doesn't need it.
+ // Once become suspended, V8 will stay so indefinitely long, until remote
+ // debugger connects and issues "continue" command.
+ Debugger::message_handler_ = StubMessageHandler2;
+ v8::Debug::DebugBreak();
+ }
+
+ if (Socket::SetUp()) {
+ if (agent_ == NULL) {
+ agent_ = new DebuggerAgent(name, port);
+ agent_->Start();
+ }
+ return true;
+ }
+
+ return false;
+}
+
+
+void Debugger::StopAgent() {
+ ASSERT(Isolate::Current() == isolate_);
+ if (agent_ != NULL) {
+ agent_->Shutdown();
+ agent_->Join();
+ delete agent_;
+ agent_ = NULL;
+ }
+}
+
+
+void Debugger::WaitForAgent() {
+ ASSERT(Isolate::Current() == isolate_);
+ if (agent_ != NULL)
+ agent_->WaitUntilListening();
+}
+
+
+void Debugger::CallMessageDispatchHandler() {
+ v8::Debug::DebugMessageDispatchHandler handler;
+ {
+ ScopedLock with(dispatch_handler_access_);
+ handler = Debugger::debug_message_dispatch_handler_;
+ }
+ if (handler != NULL) {
+ handler();
+ }
+}
+
+
+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 (!load_failed_ && 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,
+ Handle<JSObject> event_data) {
+ MessageImpl message(true, event, running,
+ exec_state, event_data, Handle<String>(), NULL);
+ return message;
+}
+
+
+MessageImpl MessageImpl::NewResponse(DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<String> response_json,
+ v8::Debug::ClientData* client_data) {
+ MessageImpl message(false, event, running,
+ exec_state, event_data, response_json, client_data);
+ return message;
+}
+
+
+MessageImpl::MessageImpl(bool is_event,
+ DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<String> response_json,
+ v8::Debug::ClientData* client_data)
+ : is_event_(is_event),
+ event_(event),
+ running_(running),
+ exec_state_(exec_state),
+ event_data_(event_data),
+ response_json_(response_json),
+ client_data_(client_data) {}
+
+
+bool MessageImpl::IsEvent() const {
+ return is_event_;
+}
+
+
+bool MessageImpl::IsResponse() const {
+ return !is_event_;
+}
+
+
+DebugEvent MessageImpl::GetEvent() const {
+ return event_;
+}
+
+
+bool MessageImpl::WillStartRunning() const {
+ return running_;
+}
+
+
+v8::Handle<v8::Object> MessageImpl::GetExecutionState() const {
+ return v8::Utils::ToLocal(exec_state_);
+}
+
+
+v8::Handle<v8::Object> MessageImpl::GetEventData() const {
+ return v8::Utils::ToLocal(event_data_);
+}
+
+
+v8::Handle<v8::String> MessageImpl::GetJSON() const {
+ v8::HandleScope scope(
+ reinterpret_cast<v8::Isolate*>(event_data_->GetIsolate()));
+
+ if (IsEvent()) {
+ // Call toJSONProtocol on the debug event object.
+ Handle<Object> fun = GetProperty(event_data_, "toJSONProtocol");
+ if (!fun->IsJSFunction()) {
+ return v8::Handle<v8::String>();
+ }
+ bool caught_exception;
+ Handle<Object> json = Execution::TryCall(Handle<JSFunction>::cast(fun),
+ event_data_,
+ 0, NULL, &caught_exception);
+ if (caught_exception || !json->IsString()) {
+ return v8::Handle<v8::String>();
+ }
+ return scope.Close(v8::Utils::ToLocal(Handle<String>::cast(json)));
+ } else {
+ return v8::Utils::ToLocal(response_json_);
+ }
+}
+
+
+v8::Handle<v8::Context> MessageImpl::GetEventContext() const {
+ Isolate* isolate = Isolate::Current();
+ v8::Handle<v8::Context> context = GetDebugEventContext(isolate);
+ // Isolate::context() may be NULL when "script collected" event occures.
+ ASSERT(!context.IsEmpty() || event_ == v8::ScriptCollected);
+ return context;
+}
+
+
+v8::Debug::ClientData* MessageImpl::GetClientData() const {
+ return client_data_;
+}
+
+
+EventDetailsImpl::EventDetailsImpl(DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<Object> callback_data,
+ v8::Debug::ClientData* client_data)
+ : event_(event),
+ exec_state_(exec_state),
+ event_data_(event_data),
+ callback_data_(callback_data),
+ client_data_(client_data) {}
+
+
+DebugEvent EventDetailsImpl::GetEvent() const {
+ return event_;
+}
+
+
+v8::Handle<v8::Object> EventDetailsImpl::GetExecutionState() const {
+ return v8::Utils::ToLocal(exec_state_);
+}
+
+
+v8::Handle<v8::Object> EventDetailsImpl::GetEventData() const {
+ return v8::Utils::ToLocal(event_data_);
+}
+
+
+v8::Handle<v8::Context> EventDetailsImpl::GetEventContext() const {
+ return GetDebugEventContext(Isolate::Current());
+}
+
+
+v8::Handle<v8::Value> EventDetailsImpl::GetCallbackData() const {
+ return v8::Utils::ToLocal(callback_data_);
+}
+
+
+v8::Debug::ClientData* EventDetailsImpl::GetClientData() const {
+ return client_data_;
+}
+
+
+CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()),
+ client_data_(NULL) {
+}
+
+
+CommandMessage::CommandMessage(const Vector<uint16_t>& text,
+ v8::Debug::ClientData* data)
+ : text_(text),
+ client_data_(data) {
+}
+
+
+CommandMessage::~CommandMessage() {
+}
+
+
+void CommandMessage::Dispose() {
+ text_.Dispose();
+ delete client_data_;
+ client_data_ = NULL;
+}
+
+
+CommandMessage CommandMessage::New(const Vector<uint16_t>& command,
+ v8::Debug::ClientData* data) {
+ return CommandMessage(command.Clone(), data);
+}
+
+
+CommandMessageQueue::CommandMessageQueue(int size) : start_(0), end_(0),
+ size_(size) {
+ messages_ = NewArray<CommandMessage>(size);
+}
+
+
+CommandMessageQueue::~CommandMessageQueue() {
+ while (!IsEmpty()) {
+ CommandMessage m = Get();
+ m.Dispose();
+ }
+ DeleteArray(messages_);
+}
+
+
+CommandMessage CommandMessageQueue::Get() {
+ ASSERT(!IsEmpty());
+ int result = start_;
+ start_ = (start_ + 1) % size_;
+ return messages_[result];
+}
+
+
+void CommandMessageQueue::Put(const CommandMessage& message) {
+ if ((end_ + 1) % size_ == start_) {
+ Expand();
+ }
+ messages_[end_] = message;
+ end_ = (end_ + 1) % size_;
+}
+
+
+void CommandMessageQueue::Expand() {
+ CommandMessageQueue new_queue(size_ * 2);
+ while (!IsEmpty()) {
+ new_queue.Put(Get());
+ }
+ CommandMessage* array_to_free = messages_;
+ *this = new_queue;
+ new_queue.messages_ = array_to_free;
+ // Make the new_queue empty so that it doesn't call Dispose on any messages.
+ new_queue.start_ = new_queue.end_;
+ // Automatic destructor called on new_queue, freeing array_to_free.
+}
+
+
+LockingCommandMessageQueue::LockingCommandMessageQueue(Logger* logger, int size)
+ : logger_(logger), queue_(size) {
+ lock_ = OS::CreateMutex();
+}
+
+
+LockingCommandMessageQueue::~LockingCommandMessageQueue() {
+ delete lock_;
+}
+
+
+bool LockingCommandMessageQueue::IsEmpty() const {
+ ScopedLock sl(lock_);
+ return queue_.IsEmpty();
+}
+
+
+CommandMessage LockingCommandMessageQueue::Get() {
+ ScopedLock sl(lock_);
+ CommandMessage result = queue_.Get();
+ logger_->DebugEvent("Get", result.text());
+ return result;
+}
+
+
+void LockingCommandMessageQueue::Put(const CommandMessage& message) {
+ ScopedLock sl(lock_);
+ queue_.Put(message);
+ logger_->DebugEvent("Put", message.text());
+}
+
+
+void LockingCommandMessageQueue::Clear() {
+ ScopedLock sl(lock_);
+ queue_.Clear();
+}
+
+
+MessageDispatchHelperThread::MessageDispatchHelperThread(Isolate* isolate)
+ : Thread("v8:MsgDispHelpr"),
+ isolate_(isolate), sem_(OS::CreateSemaphore(0)),
+ mutex_(OS::CreateMutex()), already_signalled_(false) {
+}
+
+
+MessageDispatchHelperThread::~MessageDispatchHelperThread() {
+ delete mutex_;
+ delete sem_;
+}
+
+
+void MessageDispatchHelperThread::Schedule() {
+ {
+ ScopedLock lock(mutex_);
+ if (already_signalled_) {
+ return;
+ }
+ already_signalled_ = true;
+ }
+ sem_->Signal();
+}
+
+
+void MessageDispatchHelperThread::Run() {
+ while (true) {
+ sem_->Wait();
+ {
+ ScopedLock lock(mutex_);
+ already_signalled_ = false;
+ }
+ {
+ Locker locker(reinterpret_cast<v8::Isolate*>(isolate_));
+ isolate_->debugger()->CallMessageDispatchHandler();
+ }
+ }
+}
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/debug.h b/chromium/v8/src/debug.h
new file mode 100644
index 00000000000..67debc7543c
--- /dev/null
+++ b/chromium/v8/src/debug.h
@@ -0,0 +1,1070 @@
+// Copyright 2012 the V8 project authors. 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_DEBUG_H_
+#define V8_DEBUG_H_
+
+#include "allocation.h"
+#include "arguments.h"
+#include "assembler.h"
+#include "debug-agent.h"
+#include "execution.h"
+#include "factory.h"
+#include "flags.h"
+#include "frames-inl.h"
+#include "hashmap.h"
+#include "platform.h"
+#include "string-stream.h"
+#include "v8threads.h"
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#include "../include/v8-debug.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Forward declarations.
+class EnterDebugger;
+
+
+// Step actions. NOTE: These values are in macros.py as well.
+enum StepAction {
+ StepNone = -1, // Stepping not prepared.
+ StepOut = 0, // Step out of the current function.
+ StepNext = 1, // Step to the next statement in the current function.
+ StepIn = 2, // Step into new functions invoked or the next statement
+ // in the current function.
+ StepMin = 3, // Perform a minimum step in the current function.
+ StepInMin = 4 // Step into new functions invoked or perform a minimum step
+ // in the current function.
+};
+
+
+// Type of exception break. NOTE: These values are in macros.py as well.
+enum ExceptionBreakType {
+ BreakException = 0,
+ BreakUncaughtException = 1
+};
+
+
+// Type of exception break. NOTE: These values are in macros.py as well.
+enum BreakLocatorType {
+ ALL_BREAK_LOCATIONS = 0,
+ SOURCE_BREAK_LOCATIONS = 1
+};
+
+
+// The different types of breakpoint position alignments.
+// Must match Debug.BreakPositionAlignment in debug-debugger.js
+enum BreakPositionAlignment {
+ STATEMENT_ALIGNED = 0,
+ BREAK_POSITION_ALIGNED = 1
+};
+
+
+// Class for iterating through the break points in a function and changing
+// them.
+class BreakLocationIterator {
+ public:
+ explicit BreakLocationIterator(Handle<DebugInfo> debug_info,
+ BreakLocatorType type);
+ virtual ~BreakLocationIterator();
+
+ void Next();
+ void Next(int count);
+ void FindBreakLocationFromAddress(Address pc);
+ void FindBreakLocationFromPosition(int position,
+ BreakPositionAlignment alignment);
+ void Reset();
+ bool Done() const;
+ void SetBreakPoint(Handle<Object> break_point_object);
+ void ClearBreakPoint(Handle<Object> break_point_object);
+ void SetOneShot();
+ void ClearOneShot();
+ bool IsStepInLocation(Isolate* isolate);
+ void PrepareStepIn(Isolate* isolate);
+ bool IsExit() const;
+ bool HasBreakPoint();
+ bool IsDebugBreak();
+ Object* BreakPointObjects();
+ void ClearAllDebugBreak();
+
+
+ inline int code_position() {
+ return static_cast<int>(pc() - debug_info_->code()->entry());
+ }
+ inline int break_point() { return break_point_; }
+ inline int position() { return position_; }
+ inline int statement_position() { return statement_position_; }
+ inline Address pc() { return reloc_iterator_->rinfo()->pc(); }
+ inline Code* code() { return debug_info_->code(); }
+ inline RelocInfo* rinfo() { return reloc_iterator_->rinfo(); }
+ inline RelocInfo::Mode rmode() const {
+ return reloc_iterator_->rinfo()->rmode();
+ }
+ inline RelocInfo* original_rinfo() {
+ return reloc_iterator_original_->rinfo();
+ }
+ inline RelocInfo::Mode original_rmode() const {
+ return reloc_iterator_original_->rinfo()->rmode();
+ }
+
+ bool IsDebuggerStatement();
+
+ protected:
+ bool RinfoDone() const;
+ void RinfoNext();
+
+ BreakLocatorType type_;
+ int break_point_;
+ int position_;
+ int statement_position_;
+ Handle<DebugInfo> debug_info_;
+ RelocIterator* reloc_iterator_;
+ RelocIterator* reloc_iterator_original_;
+
+ private:
+ void SetDebugBreak();
+ void ClearDebugBreak();
+
+ void SetDebugBreakAtIC();
+ void ClearDebugBreakAtIC();
+
+ bool IsDebugBreakAtReturn();
+ void SetDebugBreakAtReturn();
+ void ClearDebugBreakAtReturn();
+
+ bool IsDebugBreakSlot();
+ bool IsDebugBreakAtSlot();
+ void SetDebugBreakAtSlot();
+ void ClearDebugBreakAtSlot();
+
+ DISALLOW_COPY_AND_ASSIGN(BreakLocationIterator);
+};
+
+
+// Cache of all script objects in the heap. When a script is added a weak handle
+// to it is created and that weak handle is stored in the cache. The weak handle
+// callback takes care of removing the script from the cache. The key used in
+// the cache is the script id.
+class ScriptCache : private HashMap {
+ public:
+ ScriptCache() : HashMap(ScriptMatch), collected_scripts_(10) {}
+ virtual ~ScriptCache() { Clear(); }
+
+ // Add script to the cache.
+ void Add(Handle<Script> script);
+
+ // Return the scripts in the cache.
+ Handle<FixedArray> GetScripts();
+
+ // Generate debugger events for collected scripts.
+ void ProcessCollectedScripts();
+
+ private:
+ // Calculate the hash value from the key (script id).
+ static uint32_t Hash(int key) {
+ return ComputeIntegerHash(key, v8::internal::kZeroHashSeed);
+ }
+
+ // Scripts match if their keys (script id) match.
+ static bool ScriptMatch(void* key1, void* key2) { return key1 == key2; }
+
+ // Clear the cache releasing all the weak handles.
+ void Clear();
+
+ // Weak handle callback for scripts in the cache.
+ static void HandleWeakScript(v8::Isolate* isolate,
+ v8::Persistent<v8::Value>* obj,
+ void* data);
+
+ // List used during GC to temporarily store id's of collected scripts.
+ List<int> collected_scripts_;
+};
+
+
+// Linked list holding debug info objects. The debug info objects are kept as
+// weak handles to avoid a debug info object to keep a function alive.
+class DebugInfoListNode {
+ public:
+ explicit DebugInfoListNode(DebugInfo* debug_info);
+ virtual ~DebugInfoListNode();
+
+ DebugInfoListNode* next() { return next_; }
+ void set_next(DebugInfoListNode* next) { next_ = next; }
+ Handle<DebugInfo> debug_info() { return debug_info_; }
+
+ private:
+ // Global (weak) handle to the debug info object.
+ Handle<DebugInfo> debug_info_;
+
+ // Next pointer for linked list.
+ DebugInfoListNode* next_;
+};
+
+// This class contains the debugger support. The main purpose is to handle
+// setting break points in the code.
+//
+// This class controls the debug info for all functions which currently have
+// active breakpoints in them. This debug info is held in the heap root object
+// debug_info which is a FixedArray. Each entry in this list is of class
+// DebugInfo.
+class Debug {
+ public:
+ void SetUp(bool create_heap_objects);
+ bool Load();
+ void Unload();
+ bool IsLoaded() { return !debug_context_.is_null(); }
+ bool InDebugger() { return thread_local_.debugger_entry_ != NULL; }
+ void PreemptionWhileInDebugger();
+ void Iterate(ObjectVisitor* v);
+
+ Object* Break(Arguments args);
+ void SetBreakPoint(Handle<JSFunction> function,
+ Handle<Object> break_point_object,
+ int* source_position);
+ bool SetBreakPointForScript(Handle<Script> script,
+ Handle<Object> break_point_object,
+ int* source_position,
+ BreakPositionAlignment alignment);
+ void ClearBreakPoint(Handle<Object> break_point_object);
+ void ClearAllBreakPoints();
+ void FloodWithOneShot(Handle<JSFunction> function);
+ void FloodBoundFunctionWithOneShot(Handle<JSFunction> function);
+ void FloodHandlerWithOneShot();
+ void ChangeBreakOnException(ExceptionBreakType type, bool enable);
+ bool IsBreakOnException(ExceptionBreakType type);
+ void PrepareStep(StepAction step_action, int step_count);
+ void ClearStepping();
+ void ClearStepOut();
+ bool IsStepping() { return thread_local_.step_count_ > 0; }
+ bool StepNextContinue(BreakLocationIterator* break_location_iterator,
+ JavaScriptFrame* frame);
+ static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
+ static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
+
+ void PrepareForBreakPoints();
+
+ // This function is used in FunctionNameUsing* tests.
+ Object* FindSharedFunctionInfoInScript(Handle<Script> script, int position);
+
+ // Returns whether the operation succeeded. Compilation can only be triggered
+ // if a valid closure is passed as the second argument, otherwise the shared
+ // function needs to be compiled already.
+ bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared,
+ Handle<JSFunction> function);
+
+ // Returns true if the current stub call is patched to call the debugger.
+ static bool IsDebugBreak(Address addr);
+ // Returns true if the current return statement has been patched to be
+ // a debugger breakpoint.
+ static bool IsDebugBreakAtReturn(RelocInfo* rinfo);
+
+ // Check whether a code stub with the specified major key is a possible break
+ // point location.
+ static bool IsSourceBreakStub(Code* code);
+ static bool IsBreakStub(Code* code);
+
+ // Find the builtin to use for invoking the debug break
+ static Handle<Code> FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode);
+
+ static Handle<Object> GetSourceBreakLocations(
+ Handle<SharedFunctionInfo> shared,
+ BreakPositionAlignment position_aligment);
+
+ // Getter for the debug_context.
+ inline Handle<Context> debug_context() { return debug_context_; }
+
+ // Check whether a global object is the debug global object.
+ bool IsDebugGlobal(GlobalObject* global);
+
+ // Check whether this frame is just about to return.
+ bool IsBreakAtReturn(JavaScriptFrame* frame);
+
+ // Fast check to see if any break points are active.
+ inline bool has_break_points() { return has_break_points_; }
+
+ void NewBreak(StackFrame::Id break_frame_id);
+ void SetBreak(StackFrame::Id break_frame_id, int break_id);
+ StackFrame::Id break_frame_id() {
+ return thread_local_.break_frame_id_;
+ }
+ int break_id() { return thread_local_.break_id_; }
+
+ bool StepInActive() { return thread_local_.step_into_fp_ != 0; }
+ void HandleStepIn(Handle<JSFunction> function,
+ Handle<Object> holder,
+ Address fp,
+ bool is_constructor);
+ Address step_in_fp() { return thread_local_.step_into_fp_; }
+ Address* step_in_fp_addr() { return &thread_local_.step_into_fp_; }
+
+ bool StepOutActive() { return thread_local_.step_out_fp_ != 0; }
+ Address step_out_fp() { return thread_local_.step_out_fp_; }
+
+ EnterDebugger* debugger_entry() {
+ return thread_local_.debugger_entry_;
+ }
+ void set_debugger_entry(EnterDebugger* entry) {
+ thread_local_.debugger_entry_ = entry;
+ }
+
+ // Check whether any of the specified interrupts are pending.
+ bool is_interrupt_pending(InterruptFlag what) {
+ return (thread_local_.pending_interrupts_ & what) != 0;
+ }
+
+ // Set specified interrupts as pending.
+ void set_interrupts_pending(InterruptFlag what) {
+ thread_local_.pending_interrupts_ |= what;
+ }
+
+ // Clear specified interrupts from pending.
+ void clear_interrupt_pending(InterruptFlag what) {
+ thread_local_.pending_interrupts_ &= ~static_cast<int>(what);
+ }
+
+ // Getter and setter for the disable break state.
+ bool disable_break() { return disable_break_; }
+ void set_disable_break(bool disable_break) {
+ disable_break_ = disable_break;
+ }
+
+ // Getters for the current exception break state.
+ bool break_on_exception() { return break_on_exception_; }
+ bool break_on_uncaught_exception() {
+ return break_on_uncaught_exception_;
+ }
+
+ enum AddressId {
+ k_after_break_target_address,
+ k_debug_break_return_address,
+ k_debug_break_slot_address,
+ k_restarter_frame_function_pointer
+ };
+
+ // Support for setting the address to jump to when returning from break point.
+ Address* after_break_target_address() {
+ return reinterpret_cast<Address*>(&thread_local_.after_break_target_);
+ }
+ Address* restarter_frame_function_pointer_address() {
+ Object*** address = &thread_local_.restarter_frame_function_pointer_;
+ return reinterpret_cast<Address*>(address);
+ }
+
+ // Support for saving/restoring registers when handling debug break calls.
+ Object** register_address(int r) {
+ return &registers_[r];
+ }
+
+ // Access to the debug break on return code.
+ Code* debug_break_return() { return debug_break_return_; }
+ Code** debug_break_return_address() {
+ return &debug_break_return_;
+ }
+
+ // Access to the debug break in debug break slot code.
+ Code* debug_break_slot() { return debug_break_slot_; }
+ Code** debug_break_slot_address() {
+ return &debug_break_slot_;
+ }
+
+ static const int kEstimatedNofDebugInfoEntries = 16;
+ static const int kEstimatedNofBreakPointsInFunction = 16;
+
+ // Passed to MakeWeak.
+ static void HandleWeakDebugInfo(v8::Isolate* isolate,
+ v8::Persistent<v8::Value>* obj,
+ void* data);
+
+ friend class Debugger;
+ friend Handle<FixedArray> GetDebuggedFunctions(); // In test-debug.cc
+ friend void CheckDebuggerUnloaded(bool check_functions); // In test-debug.cc
+
+ // Threading support.
+ char* ArchiveDebug(char* to);
+ char* RestoreDebug(char* from);
+ static int ArchiveSpacePerThread();
+ void FreeThreadResources() { }
+
+ // Mirror cache handling.
+ void ClearMirrorCache();
+
+ // Script cache handling.
+ void CreateScriptCache();
+ void DestroyScriptCache();
+ void AddScriptToScriptCache(Handle<Script> script);
+ Handle<FixedArray> GetLoadedScripts();
+
+ // Garbage collection notifications.
+ void AfterGarbageCollection();
+
+ // Code generator routines.
+ static void GenerateSlot(MacroAssembler* masm);
+ static void GenerateLoadICDebugBreak(MacroAssembler* masm);
+ static void GenerateStoreICDebugBreak(MacroAssembler* masm);
+ static void GenerateKeyedLoadICDebugBreak(MacroAssembler* masm);
+ static void GenerateKeyedStoreICDebugBreak(MacroAssembler* masm);
+ static void GenerateCompareNilICDebugBreak(MacroAssembler* masm);
+ static void GenerateReturnDebugBreak(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);
+
+ // FrameDropper is a code replacement for a JavaScript frame with possibly
+ // several frames above.
+ // There is no calling conventions here, because it never actually gets
+ // called, it only gets returned to.
+ static void GenerateFrameDropperLiveEdit(MacroAssembler* masm);
+
+ // Called from stub-cache.cc.
+ static void GenerateCallICDebugBreak(MacroAssembler* masm);
+
+ // Describes how exactly a frame has been dropped from stack.
+ enum FrameDropMode {
+ // No frame has been dropped.
+ FRAMES_UNTOUCHED,
+ // The top JS frame had been calling IC stub. IC stub mustn't be called now.
+ FRAME_DROPPED_IN_IC_CALL,
+ // The top JS frame had been calling debug break slot stub. Patch the
+ // address this stub jumps to in the end.
+ FRAME_DROPPED_IN_DEBUG_SLOT_CALL,
+ // The top JS frame had been calling some C++ function. The return address
+ // gets patched automatically.
+ FRAME_DROPPED_IN_DIRECT_CALL,
+ FRAME_DROPPED_IN_RETURN_CALL,
+ CURRENTLY_SET_MODE
+ };
+
+ void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
+ FrameDropMode mode,
+ Object** restarter_frame_function_pointer);
+
+ // Initializes an artificial stack frame. The data it contains is used for:
+ // a. successful work of frame dropper code which eventually gets control,
+ // b. being compatible with regular stack structure for various stack
+ // iterators.
+ // Returns address of stack allocated pointer to restarted function,
+ // the value that is called 'restarter_frame_function_pointer'. The value
+ // at this address (possibly updated by GC) may be used later when preparing
+ // 'step in' operation.
+ static Object** SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
+ Handle<Code> code);
+
+ static const int kFrameDropperFrameSize;
+
+ // Architecture-specific constant.
+ static const bool kFrameDropperSupported;
+
+ /**
+ * Defines layout of a stack frame that supports padding. This is a regular
+ * internal frame that has a flexible stack structure. LiveEdit can shift
+ * its lower part up the stack, taking up the 'padding' space when additional
+ * stack memory is required.
+ * Such frame is expected immediately above the topmost JavaScript frame.
+ *
+ * Stack Layout:
+ * --- Top
+ * LiveEdit routine frames
+ * ---
+ * C frames of debug handler
+ * ---
+ * ...
+ * ---
+ * An internal frame that has n padding words:
+ * - any number of words as needed by code -- upper part of frame
+ * - padding size: a Smi storing n -- current size of padding
+ * - padding: n words filled with kPaddingValue in form of Smi
+ * - 3 context/type words of a regular InternalFrame
+ * - fp
+ * ---
+ * Topmost JavaScript frame
+ * ---
+ * ...
+ * --- Bottom
+ */
+ class FramePaddingLayout : public AllStatic {
+ public:
+ // Architecture-specific constant.
+ static const bool kIsSupported;
+
+ // A size of frame base including fp. Padding words starts right above
+ // the base.
+ static const int kFrameBaseSize = 4;
+
+ // A number of words that should be reserved on stack for the LiveEdit use.
+ // Normally equals 1. Stored on stack in form of Smi.
+ static const int kInitialSize;
+ // A value that padding words are filled with (in form of Smi). Going
+ // bottom-top, the first word not having this value is a counter word.
+ static const int kPaddingValue;
+ };
+
+ private:
+ explicit Debug(Isolate* isolate);
+ ~Debug();
+
+ static bool CompileDebuggerScript(int index);
+ void ClearOneShot();
+ void ActivateStepIn(StackFrame* frame);
+ void ClearStepIn();
+ void ActivateStepOut(StackFrame* frame);
+ void ClearStepNext();
+ // Returns whether the compile succeeded.
+ void RemoveDebugInfo(Handle<DebugInfo> debug_info);
+ void SetAfterBreakTarget(JavaScriptFrame* frame);
+ Handle<Object> CheckBreakPoints(Handle<Object> break_point);
+ bool CheckBreakPoint(Handle<Object> break_point_object);
+
+ // Global handle to debug context where all the debugger JavaScript code is
+ // loaded.
+ Handle<Context> debug_context_;
+
+ // Boolean state indicating whether any break points are set.
+ bool has_break_points_;
+
+ // Cache of all scripts in the heap.
+ ScriptCache* script_cache_;
+
+ // List of active debug info objects.
+ DebugInfoListNode* debug_info_list_;
+
+ bool disable_break_;
+ bool break_on_exception_;
+ bool break_on_uncaught_exception_;
+
+ // Per-thread data.
+ class ThreadLocal {
+ public:
+ // Counter for generating next break id.
+ int break_count_;
+
+ // Current break id.
+ int break_id_;
+
+ // Frame id for the frame of the current break.
+ StackFrame::Id break_frame_id_;
+
+ // Step action for last step performed.
+ StepAction last_step_action_;
+
+ // Source statement position from last step next action.
+ int last_statement_position_;
+
+ // Number of steps left to perform before debug event.
+ int step_count_;
+
+ // Frame pointer from last step next action.
+ Address last_fp_;
+
+ // Number of queued steps left to perform before debug event.
+ int queued_step_count_;
+
+ // Frame pointer for frame from which step in was performed.
+ Address step_into_fp_;
+
+ // Frame pointer for the frame where debugger should be called when current
+ // step out action is completed.
+ Address step_out_fp_;
+
+ // Storage location for jump when exiting debug break calls.
+ Address after_break_target_;
+
+ // Stores the way how LiveEdit has patched the stack. It is used when
+ // debugger returns control back to user script.
+ FrameDropMode frame_drop_mode_;
+
+ // Top debugger entry.
+ EnterDebugger* debugger_entry_;
+
+ // Pending interrupts scheduled while debugging.
+ int pending_interrupts_;
+
+ // When restarter frame is on stack, stores the address
+ // of the pointer to function being restarted. Otherwise (most of the time)
+ // stores NULL. This pointer is used with 'step in' implementation.
+ Object** restarter_frame_function_pointer_;
+ };
+
+ // Storage location for registers when handling debug break calls
+ JSCallerSavedBuffer registers_;
+ ThreadLocal thread_local_;
+ void ThreadInit();
+
+ // Code to call for handling debug break on return.
+ Code* debug_break_return_;
+
+ // Code to call for handling debug break in debug break slots.
+ Code* debug_break_slot_;
+
+ Isolate* isolate_;
+
+ friend class Isolate;
+
+ DISALLOW_COPY_AND_ASSIGN(Debug);
+};
+
+
+DECLARE_RUNTIME_FUNCTION(Object*, Debug_Break);
+
+
+// Message delivered to the message handler callback. This is either a debugger
+// event or the response to a command.
+class MessageImpl: public v8::Debug::Message {
+ public:
+ // Create a message object for a debug event.
+ static MessageImpl NewEvent(DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data);
+
+ // Create a message object for the response to a debug command.
+ static MessageImpl NewResponse(DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<String> response_json,
+ v8::Debug::ClientData* client_data);
+
+ // Implementation of interface v8::Debug::Message.
+ virtual bool IsEvent() const;
+ virtual bool IsResponse() const;
+ virtual DebugEvent GetEvent() const;
+ virtual bool WillStartRunning() const;
+ virtual v8::Handle<v8::Object> GetExecutionState() const;
+ virtual v8::Handle<v8::Object> GetEventData() const;
+ virtual v8::Handle<v8::String> GetJSON() const;
+ virtual v8::Handle<v8::Context> GetEventContext() const;
+ virtual v8::Debug::ClientData* GetClientData() const;
+
+ private:
+ MessageImpl(bool is_event,
+ DebugEvent event,
+ bool running,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<String> response_json,
+ v8::Debug::ClientData* client_data);
+
+ bool is_event_; // Does this message represent a debug event?
+ DebugEvent event_; // Debug event causing the break.
+ bool running_; // Will the VM start running after this event?
+ Handle<JSObject> exec_state_; // Current execution state.
+ Handle<JSObject> event_data_; // Data associated with the event.
+ Handle<String> response_json_; // Response JSON if message holds a response.
+ v8::Debug::ClientData* client_data_; // Client data passed with the request.
+};
+
+
+// Details of the debug event delivered to the debug event listener.
+class EventDetailsImpl : public v8::Debug::EventDetails {
+ public:
+ EventDetailsImpl(DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ Handle<Object> callback_data,
+ v8::Debug::ClientData* client_data);
+ virtual DebugEvent GetEvent() const;
+ virtual v8::Handle<v8::Object> GetExecutionState() const;
+ virtual v8::Handle<v8::Object> GetEventData() const;
+ virtual v8::Handle<v8::Context> GetEventContext() const;
+ virtual v8::Handle<v8::Value> GetCallbackData() const;
+ virtual v8::Debug::ClientData* GetClientData() const;
+ private:
+ DebugEvent event_; // Debug event causing the break.
+ Handle<JSObject> exec_state_; // Current execution state.
+ Handle<JSObject> event_data_; // Data associated with the event.
+ Handle<Object> callback_data_; // User data passed with the callback
+ // when it was registered.
+ v8::Debug::ClientData* client_data_; // Data passed to DebugBreakForCommand.
+};
+
+
+// Message send by user to v8 debugger or debugger output message.
+// In addition to command text it may contain a pointer to some user data
+// which are expected to be passed along with the command reponse to message
+// handler.
+class CommandMessage {
+ public:
+ static CommandMessage New(const Vector<uint16_t>& command,
+ v8::Debug::ClientData* data);
+ CommandMessage();
+ ~CommandMessage();
+
+ // Deletes user data and disposes of the text.
+ void Dispose();
+ Vector<uint16_t> text() const { return text_; }
+ v8::Debug::ClientData* client_data() const { return client_data_; }
+ private:
+ CommandMessage(const Vector<uint16_t>& text,
+ v8::Debug::ClientData* data);
+
+ Vector<uint16_t> text_;
+ v8::Debug::ClientData* client_data_;
+};
+
+// A Queue of CommandMessage objects. A thread-safe version is
+// LockingCommandMessageQueue, based on this class.
+class CommandMessageQueue BASE_EMBEDDED {
+ public:
+ explicit CommandMessageQueue(int size);
+ ~CommandMessageQueue();
+ bool IsEmpty() const { return start_ == end_; }
+ CommandMessage Get();
+ void Put(const CommandMessage& message);
+ void Clear() { start_ = end_ = 0; } // Queue is empty after Clear().
+ private:
+ // Doubles the size of the message queue, and copies the messages.
+ void Expand();
+
+ CommandMessage* messages_;
+ int start_;
+ int end_;
+ int size_; // The size of the queue buffer. Queue can hold size-1 messages.
+};
+
+
+class MessageDispatchHelperThread;
+
+
+// LockingCommandMessageQueue is a thread-safe circular buffer of CommandMessage
+// messages. The message data is not managed by LockingCommandMessageQueue.
+// Pointers to the data are passed in and out. Implemented by adding a
+// Mutex to CommandMessageQueue. Includes logging of all puts and gets.
+class LockingCommandMessageQueue BASE_EMBEDDED {
+ public:
+ LockingCommandMessageQueue(Logger* logger, int size);
+ ~LockingCommandMessageQueue();
+ bool IsEmpty() const;
+ CommandMessage Get();
+ void Put(const CommandMessage& message);
+ void Clear();
+ private:
+ Logger* logger_;
+ CommandMessageQueue queue_;
+ Mutex* lock_;
+ DISALLOW_COPY_AND_ASSIGN(LockingCommandMessageQueue);
+};
+
+
+class Debugger {
+ public:
+ ~Debugger();
+
+ void DebugRequest(const uint16_t* json_request, int length);
+
+ Handle<Object> MakeJSObject(Vector<const char> constructor_name,
+ int argc,
+ Handle<Object> argv[],
+ bool* caught_exception);
+ Handle<Object> MakeExecutionState(bool* caught_exception);
+ Handle<Object> MakeBreakEvent(Handle<Object> exec_state,
+ Handle<Object> break_points_hit,
+ bool* caught_exception);
+ Handle<Object> MakeExceptionEvent(Handle<Object> exec_state,
+ Handle<Object> exception,
+ bool uncaught,
+ bool* caught_exception);
+ Handle<Object> MakeNewFunctionEvent(Handle<Object> func,
+ bool* caught_exception);
+ Handle<Object> MakeCompileEvent(Handle<Script> script,
+ bool before,
+ bool* caught_exception);
+ Handle<Object> MakeScriptCollectedEvent(int id,
+ bool* caught_exception);
+ void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue);
+ void OnException(Handle<Object> exception, bool uncaught);
+ void OnBeforeCompile(Handle<Script> script);
+
+ enum AfterCompileFlags {
+ NO_AFTER_COMPILE_FLAGS,
+ SEND_WHEN_DEBUGGING
+ };
+ void OnAfterCompile(Handle<Script> script,
+ AfterCompileFlags after_compile_flags);
+ void OnScriptCollected(int id);
+ void ProcessDebugEvent(v8::DebugEvent event,
+ Handle<JSObject> event_data,
+ bool auto_continue);
+ void NotifyMessageHandler(v8::DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ bool auto_continue);
+ void SetEventListener(Handle<Object> callback, Handle<Object> data);
+ void SetMessageHandler(v8::Debug::MessageHandler2 handler);
+ void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
+ int period);
+ void SetDebugMessageDispatchHandler(
+ v8::Debug::DebugMessageDispatchHandler handler,
+ bool provide_locker);
+
+ // Invoke the message handler function.
+ void InvokeMessageHandler(MessageImpl message);
+
+ // Add a debugger command to the command queue.
+ void ProcessCommand(Vector<const uint16_t> command,
+ v8::Debug::ClientData* client_data = NULL);
+
+ // Check whether there are commands in the command queue.
+ bool HasCommands();
+
+ // Enqueue a debugger command to the command queue for event listeners.
+ void EnqueueDebugCommand(v8::Debug::ClientData* client_data = NULL);
+
+ Handle<Object> Call(Handle<JSFunction> fun,
+ Handle<Object> data,
+ bool* pending_exception);
+
+ // Start the debugger agent listening on the provided port.
+ bool StartAgent(const char* name, int port,
+ bool wait_for_connection = false);
+
+ // Stop the debugger agent.
+ void StopAgent();
+
+ // Blocks until the agent has started listening for connections
+ void WaitForAgent();
+
+ void CallMessageDispatchHandler();
+
+ Handle<Context> GetDebugContext();
+
+ // Unload the debugger if possible. Only called when no debugger is currently
+ // active.
+ void UnloadDebugger();
+ friend void ForceUnloadDebugger(); // In test-debug.cc
+
+ inline bool EventActive(v8::DebugEvent event) {
+ ScopedLock with(debugger_access_);
+
+ // Check whether the message handler was been cleared.
+ if (debugger_unload_pending_) {
+ if (isolate_->debug()->debugger_entry() == NULL) {
+ UnloadDebugger();
+ }
+ }
+
+ if (((event == v8::BeforeCompile) || (event == v8::AfterCompile)) &&
+ !FLAG_debug_compile_events) {
+ return false;
+
+ } else if ((event == v8::ScriptCollected) &&
+ !FLAG_debug_script_collected_events) {
+ return false;
+ }
+
+ // Currently argument event is not used.
+ return !compiling_natives_ && Debugger::IsDebuggerActive();
+ }
+
+ void set_compiling_natives(bool 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_live_edit_enabled(bool v) { live_edit_enabled_ = v; }
+ bool live_edit_enabled() const {
+ return FLAG_enable_liveedit && live_edit_enabled_ ;
+ }
+ 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();
+
+ private:
+ explicit Debugger(Isolate* isolate);
+
+ void CallEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data);
+ void CallCEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ v8::Debug::ClientData* client_data);
+ void CallJSEventCallback(v8::DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data);
+ void ListenersChanged();
+
+ Mutex* debugger_access_; // Mutex guarding debugger variables.
+ Handle<Object> event_listener_; // Global handle to listener.
+ Handle<Object> event_listener_data_;
+ bool compiling_natives_; // Are we compiling natives?
+ bool is_loading_debugger_; // Are we loading the debugger?
+ bool live_edit_enabled_; // Enable LiveEdit.
+ 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_;
+ Mutex* dispatch_handler_access_; // Mutex guarding dispatch handler.
+ v8::Debug::DebugMessageDispatchHandler debug_message_dispatch_handler_;
+ MessageDispatchHelperThread* message_dispatch_helper_thread_;
+ int host_dispatch_micros_;
+
+ DebuggerAgent* agent_;
+
+ static const int kQueueInitialSize = 4;
+ LockingCommandMessageQueue command_queue_;
+ Semaphore* command_received_; // Signaled for each command received.
+ LockingCommandMessageQueue event_command_queue_;
+
+ Isolate* isolate_;
+
+ friend class EnterDebugger;
+ friend class Isolate;
+
+ DISALLOW_COPY_AND_ASSIGN(Debugger);
+};
+
+
+// This class is used for entering the debugger. Create an instance in the stack
+// to enter the debugger. This will set the current break state, make sure the
+// debugger is loaded and switch to the debugger context. If the debugger for
+// some reason could not be entered FailedToEnter will return true.
+class EnterDebugger BASE_EMBEDDED {
+ public:
+ EnterDebugger();
+ ~EnterDebugger();
+
+ // Check whether the debugger could be entered.
+ inline bool FailedToEnter() { return load_failed_; }
+
+ // Check whether there are any JavaScript frames on the stack.
+ inline bool HasJavaScriptFrames() { return has_js_frames_; }
+
+ // Get the active context from before entering the debugger.
+ inline Handle<Context> GetContext() { return save_.context(); }
+
+ private:
+ Isolate* isolate_;
+ EnterDebugger* prev_; // Previous debugger entry if entered recursively.
+ JavaScriptFrameIterator it_;
+ const bool has_js_frames_; // Were there any JavaScript frames?
+ StackFrame::Id break_frame_id_; // Previous break frame id.
+ int break_id_; // Previous break id.
+ bool load_failed_; // Did the debugger fail to load?
+ SaveContext save_; // Saves previous context.
+};
+
+
+// Stack allocated class for disabling break.
+class DisableBreak BASE_EMBEDDED {
+ public:
+ explicit DisableBreak(bool disable_break) : isolate_(Isolate::Current()) {
+ prev_disable_break_ = isolate_->debug()->disable_break();
+ isolate_->debug()->set_disable_break(disable_break);
+ }
+ ~DisableBreak() {
+ ASSERT(Isolate::Current() == isolate_);
+ isolate_->debug()->set_disable_break(prev_disable_break_);
+ }
+
+ private:
+ Isolate* isolate_;
+ // The previous state of the disable break used to restore the value when this
+ // object is destructed.
+ bool prev_disable_break_;
+};
+
+
+// Debug_Address encapsulates the Address pointers used in generating debug
+// code.
+class Debug_Address {
+ public:
+ explicit Debug_Address(Debug::AddressId id) : id_(id) { }
+
+ static Debug_Address AfterBreakTarget() {
+ return Debug_Address(Debug::k_after_break_target_address);
+ }
+
+ static Debug_Address DebugBreakReturn() {
+ return Debug_Address(Debug::k_debug_break_return_address);
+ }
+
+ static Debug_Address RestarterFrameFunctionPointer() {
+ return Debug_Address(Debug::k_restarter_frame_function_pointer);
+ }
+
+ Address address(Isolate* isolate) const {
+ Debug* debug = isolate->debug();
+ switch (id_) {
+ case Debug::k_after_break_target_address:
+ return reinterpret_cast<Address>(debug->after_break_target_address());
+ case Debug::k_debug_break_return_address:
+ return reinterpret_cast<Address>(debug->debug_break_return_address());
+ case Debug::k_debug_break_slot_address:
+ return reinterpret_cast<Address>(debug->debug_break_slot_address());
+ case Debug::k_restarter_frame_function_pointer:
+ return reinterpret_cast<Address>(
+ debug->restarter_frame_function_pointer_address());
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+ }
+
+ private:
+ Debug::AddressId id_;
+};
+
+// The optional thread that Debug Agent may use to temporary call V8 to process
+// pending debug requests if debuggee is not running V8 at the moment.
+// Techincally it does not call V8 itself, rather it asks embedding program
+// to do this via v8::Debug::HostDispatchHandler
+class MessageDispatchHelperThread: public Thread {
+ public:
+ explicit MessageDispatchHelperThread(Isolate* isolate);
+ ~MessageDispatchHelperThread();
+
+ void Schedule();
+
+ private:
+ void Run();
+
+ Isolate* isolate_;
+ Semaphore* const sem_;
+ Mutex* const mutex_;
+ bool already_signalled_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageDispatchHelperThread);
+};
+
+
+} } // namespace v8::internal
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+#endif // V8_DEBUG_H_
diff --git a/chromium/v8/src/deoptimizer.cc b/chromium/v8/src/deoptimizer.cc
new file mode 100644
index 00000000000..dc9ffc51186
--- /dev/null
+++ b/chromium/v8/src/deoptimizer.cc
@@ -0,0 +1,3318 @@
+// Copyright 2013 the V8 project authors. 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 "accessors.h"
+#include "codegen.h"
+#include "deoptimizer.h"
+#include "disasm.h"
+#include "full-codegen.h"
+#include "global-handles.h"
+#include "macro-assembler.h"
+#include "prettyprinter.h"
+
+
+namespace v8 {
+namespace internal {
+
+static MemoryChunk* AllocateCodeChunk(MemoryAllocator* allocator) {
+ return allocator->AllocateChunk(Deoptimizer::GetMaxDeoptTableSize(),
+ OS::CommitPageSize(),
+#if defined(__native_client__)
+ // The Native Client port of V8 uses an interpreter,
+ // so code pages don't need PROT_EXEC.
+ NOT_EXECUTABLE,
+#else
+ EXECUTABLE,
+#endif
+ NULL);
+}
+
+
+DeoptimizerData::DeoptimizerData(MemoryAllocator* allocator)
+ : allocator_(allocator),
+ current_(NULL),
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ deoptimized_frame_info_(NULL),
+#endif
+ deoptimizing_code_list_(NULL) {
+ for (int i = 0; i < Deoptimizer::kBailoutTypesWithCodeEntry; ++i) {
+ deopt_entry_code_entries_[i] = -1;
+ deopt_entry_code_[i] = AllocateCodeChunk(allocator);
+ }
+}
+
+
+DeoptimizerData::~DeoptimizerData() {
+ for (int i = 0; i < Deoptimizer::kBailoutTypesWithCodeEntry; ++i) {
+ allocator_->Free(deopt_entry_code_[i]);
+ deopt_entry_code_[i] = NULL;
+ }
+
+ DeoptimizingCodeListNode* current = deoptimizing_code_list_;
+ while (current != NULL) {
+ DeoptimizingCodeListNode* prev = current;
+ current = current->next();
+ delete prev;
+ }
+ deoptimizing_code_list_ = NULL;
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void DeoptimizerData::Iterate(ObjectVisitor* v) {
+ if (deoptimized_frame_info_ != NULL) {
+ deoptimized_frame_info_->Iterate(v);
+ }
+}
+#endif
+
+
+Code* DeoptimizerData::FindDeoptimizingCode(Address addr) {
+ for (DeoptimizingCodeListNode* node = deoptimizing_code_list_;
+ node != NULL;
+ node = node->next()) {
+ if (node->code()->contains(addr)) return *node->code();
+ }
+ return NULL;
+}
+
+
+void DeoptimizerData::RemoveDeoptimizingCode(Code* code) {
+ for (DeoptimizingCodeListNode *prev = NULL, *cur = deoptimizing_code_list_;
+ cur != NULL;
+ prev = cur, cur = cur->next()) {
+ if (*cur->code() == code) {
+ if (prev == NULL) {
+ deoptimizing_code_list_ = cur->next();
+ } else {
+ prev->set_next(cur->next());
+ }
+ delete cur;
+ return;
+ }
+ }
+ // Deoptimizing code is removed through weak callback. Each object is expected
+ // to be removed once and only once.
+ UNREACHABLE();
+}
+
+
+// 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,
+ Address from,
+ int fp_to_sp_delta,
+ Isolate* isolate) {
+ Deoptimizer* deoptimizer = new Deoptimizer(isolate,
+ function,
+ type,
+ bailout_id,
+ from,
+ fp_to_sp_delta,
+ NULL);
+ ASSERT(isolate->deoptimizer_data()->current_ == NULL);
+ isolate->deoptimizer_data()->current_ = deoptimizer;
+ return deoptimizer;
+}
+
+
+// No larger than 2K on all platforms
+static const int kDeoptTableMaxEpilogueCodeSize = 2 * KB;
+
+
+size_t Deoptimizer::GetMaxDeoptTableSize() {
+ int entries_size =
+ Deoptimizer::kMaxNumberOfEntries * Deoptimizer::table_entry_size_;
+ int commit_page_size = static_cast<int>(OS::CommitPageSize());
+ int page_count = ((kDeoptTableMaxEpilogueCodeSize + entries_size - 1) /
+ commit_page_size) + 1;
+ return static_cast<size_t>(commit_page_size * page_count);
+}
+
+
+Deoptimizer* Deoptimizer::Grab(Isolate* isolate) {
+ Deoptimizer* result = isolate->deoptimizer_data()->current_;
+ ASSERT(result != NULL);
+ result->DeleteFrameDescriptions();
+ isolate->deoptimizer_data()->current_ = NULL;
+ 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 jsframe_index,
+ Isolate* isolate) {
+ ASSERT(frame->is_optimized());
+ ASSERT(isolate->deoptimizer_data()->deoptimized_frame_info_ == NULL);
+
+ // Get the function and code from the frame.
+ JSFunction* function = frame->function();
+ Code* code = frame->LookupCode();
+
+ // Locate the deoptimization point in the code. As we are at a call the
+ // return address must be at a place in the code with deoptimization support.
+ SafepointEntry safepoint_entry = code->GetSafepointEntry(frame->pc());
+ int deoptimization_index = safepoint_entry.deoptimization_index();
+ ASSERT(deoptimization_index != Safepoint::kNoDeoptimizationIndex);
+
+ // Always use the actual stack slots when calculating the fp to sp
+ // delta adding two for the function and context.
+ unsigned stack_slots = code->stack_slots();
+ unsigned fp_to_sp_delta = ((stack_slots + 2) * kPointerSize);
+
+ Deoptimizer* deoptimizer = new Deoptimizer(isolate,
+ function,
+ Deoptimizer::DEBUGGER,
+ deoptimization_index,
+ frame->pc(),
+ fp_to_sp_delta,
+ code);
+ Address tos = frame->fp() - fp_to_sp_delta;
+ deoptimizer->FillInputFrame(tos, frame);
+
+ // Calculate the output frames.
+ Deoptimizer::ComputeOutputFrames(deoptimizer);
+
+ // Create the GC safe output frame information and register it for GC
+ // handling.
+ 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;
+
+ int construct_offset = has_arguments_adaptor ? 2 : 1;
+ bool has_construct_stub =
+ frame_index >= construct_offset &&
+ deoptimizer->output_[frame_index - construct_offset]->GetFrameType() ==
+ StackFrame::CONSTRUCT;
+
+ DeoptimizedFrameInfo* info = new DeoptimizedFrameInfo(deoptimizer,
+ frame_index,
+ has_arguments_adaptor,
+ has_construct_stub);
+ isolate->deoptimizer_data()->deoptimized_frame_info_ = info;
+
+ // Get the "simulated" top and size for the requested frame.
+ 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(
+ parameters_top, parameters_size, expressions_top, expressions_size, info);
+
+ // Finished using the deoptimizer instance.
+ delete deoptimizer;
+
+ return info;
+}
+
+
+void Deoptimizer::DeleteDebuggerInspectableFrame(DeoptimizedFrameInfo* info,
+ Isolate* isolate) {
+ ASSERT(isolate->deoptimizer_data()->deoptimized_frame_info_ == info);
+ delete info;
+ isolate->deoptimizer_data()->deoptimized_frame_info_ = NULL;
+}
+#endif
+
+void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
+ int count,
+ BailoutType type) {
+ TableEntryGenerator generator(masm, type, count);
+ generator.Generate();
+}
+
+
+void Deoptimizer::VisitAllOptimizedFunctionsForContext(
+ Context* context, OptimizedFunctionVisitor* visitor) {
+ Isolate* isolate = context->GetIsolate();
+ Zone zone(isolate);
+ DisallowHeapAllocation no_allocation;
+
+ ASSERT(context->IsNativeContext());
+
+ visitor->EnterContext(context);
+
+ // Create a snapshot of the optimized functions list. This is needed because
+ // visitors might remove more than one link from the list at once.
+ ZoneList<JSFunction*> snapshot(1, &zone);
+ Object* element = context->OptimizedFunctionsListHead();
+ while (!element->IsUndefined()) {
+ JSFunction* element_function = JSFunction::cast(element);
+ snapshot.Add(element_function, &zone);
+ element = element_function->next_function_link();
+ }
+
+ // Run through the snapshot of optimized functions and visit them.
+ for (int i = 0; i < snapshot.length(); ++i) {
+ visitor->VisitFunction(snapshot.at(i));
+ }
+
+ visitor->LeaveContext(context);
+}
+
+
+void Deoptimizer::VisitAllOptimizedFunctions(
+ Isolate* isolate,
+ OptimizedFunctionVisitor* visitor) {
+ DisallowHeapAllocation no_allocation;
+
+ // Run through the list of all native contexts and deoptimize.
+ Object* context = isolate->heap()->native_contexts_list();
+ while (!context->IsUndefined()) {
+ VisitAllOptimizedFunctionsForContext(Context::cast(context), visitor);
+ context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
+ }
+}
+
+
+// Removes the functions selected by the given filter from the optimized
+// function list of the given context and adds their code to the list of
+// code objects to be deoptimized.
+static void SelectCodeToDeoptimize(Context* context,
+ OptimizedFunctionFilter* filter,
+ ZoneList<Code*>* codes,
+ Zone* zone,
+ Object* undefined) {
+ DisallowHeapAllocation no_allocation;
+ Object* current = context->get(Context::OPTIMIZED_FUNCTIONS_LIST);
+ Object* remainder_head = undefined;
+ Object* remainder_tail = undefined;
+
+ // TODO(titzer): rewrite to not modify unselected functions.
+ while (current != undefined) {
+ JSFunction* function = JSFunction::cast(current);
+ current = function->next_function_link();
+ if (filter->TakeFunction(function)) {
+ // Extract this function from the context's list and remember the code.
+ Code* code = function->code();
+ ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
+ if (code->marked_for_deoptimization()) {
+ ASSERT(codes->Contains(code));
+ } else {
+ code->set_marked_for_deoptimization(true);
+ codes->Add(code, zone);
+ }
+ SharedFunctionInfo* shared = function->shared();
+ // Replace the function's code with the shared code.
+ function->set_code(shared->code());
+ // Evict the code from the optimized code map.
+ shared->EvictFromOptimizedCodeMap(code, "deoptimized function");
+ // Remove the function from the optimized functions list.
+ function->set_next_function_link(undefined);
+
+ if (FLAG_trace_deopt) {
+ PrintF("[forced deoptimization: ");
+ function->PrintName();
+ PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
+ }
+ } else {
+ // Don't select this function; link it back into the list.
+ if (remainder_head == undefined) {
+ remainder_head = function;
+ } else {
+ JSFunction::cast(remainder_tail)->set_next_function_link(function);
+ }
+ remainder_tail = function;
+ }
+ }
+ if (remainder_tail != undefined) {
+ JSFunction::cast(remainder_tail)->set_next_function_link(undefined);
+ }
+ context->set(Context::OPTIMIZED_FUNCTIONS_LIST, remainder_head);
+}
+
+
+class DeoptimizeAllFilter : public OptimizedFunctionFilter {
+ public:
+ virtual bool TakeFunction(JSFunction* function) {
+ return true;
+ }
+};
+
+
+class DeoptimizeWithMatchingCodeFilter : public OptimizedFunctionFilter {
+ public:
+ explicit DeoptimizeWithMatchingCodeFilter(Code* code) : code_(code) {}
+ virtual bool TakeFunction(JSFunction* function) {
+ return function->code() == code_;
+ }
+ private:
+ Code* code_;
+};
+
+
+class DeoptimizeMarkedCodeFilter : public OptimizedFunctionFilter {
+ public:
+ virtual bool TakeFunction(JSFunction* function) {
+ return function->code()->marked_for_deoptimization();
+ }
+};
+
+
+void Deoptimizer::DeoptimizeAll(Isolate* isolate) {
+ DisallowHeapAllocation no_allocation;
+
+ if (FLAG_trace_deopt) {
+ PrintF("[deoptimize all contexts]\n");
+ }
+
+ DeoptimizeAllFilter filter;
+ DeoptimizeAllFunctionsWith(isolate, &filter);
+}
+
+
+void Deoptimizer::DeoptimizeGlobalObject(JSObject* object) {
+ DisallowHeapAllocation no_allocation;
+ DeoptimizeAllFilter filter;
+ if (object->IsJSGlobalProxy()) {
+ Object* proto = object->GetPrototype();
+ ASSERT(proto->IsJSGlobalObject());
+ DeoptimizeAllFunctionsForContext(
+ GlobalObject::cast(proto)->native_context(), &filter);
+ } else if (object->IsGlobalObject()) {
+ DeoptimizeAllFunctionsForContext(
+ GlobalObject::cast(object)->native_context(), &filter);
+ }
+}
+
+
+void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
+ Code* code = function->code();
+ if (code->kind() != Code::OPTIMIZED_FUNCTION) return;
+ DeoptimizeWithMatchingCodeFilter filter(code);
+ DeoptimizeAllFunctionsForContext(
+ function->context()->native_context(), &filter);
+}
+
+
+void Deoptimizer::DeoptimizeAllFunctionsForContext(
+ Context* context, OptimizedFunctionFilter* filter) {
+ ASSERT(context->IsNativeContext());
+ Isolate* isolate = context->GetIsolate();
+ Object* undefined = isolate->heap()->undefined_value();
+ Zone zone(isolate);
+ ZoneList<Code*> codes(4, &zone);
+ SelectCodeToDeoptimize(context, filter, &codes, &zone, undefined);
+ for (int i = 0; i < codes.length(); i++) {
+ DeoptimizeCode(isolate, codes.at(i));
+ }
+}
+
+
+void Deoptimizer::DeoptimizeAllFunctionsWith(Isolate* isolate,
+ OptimizedFunctionFilter* filter) {
+ DisallowHeapAllocation no_allocation;
+
+ // Run through the list of all native contexts and deoptimize.
+ Object* context = isolate->heap()->native_contexts_list();
+ while (!context->IsUndefined()) {
+ DeoptimizeAllFunctionsForContext(Context::cast(context), filter);
+ context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
+ }
+}
+
+
+void Deoptimizer::DeoptimizeCodeList(Isolate* isolate, ZoneList<Code*>* codes) {
+ if (codes->length() == 0) return; // Nothing to do.
+
+ // Mark the code; any functions refering to this code will be selected.
+ for (int i = 0; i < codes->length(); i++) {
+ ASSERT(!codes->at(i)->marked_for_deoptimization());
+ codes->at(i)->set_marked_for_deoptimization(true);
+ }
+
+ // For all contexts, remove optimized functions that refer to the selected
+ // code from the optimized function lists.
+ Object* undefined = isolate->heap()->undefined_value();
+ Zone zone(isolate);
+ Object* list = isolate->heap()->native_contexts_list();
+ DeoptimizeMarkedCodeFilter filter;
+ while (!list->IsUndefined()) {
+ Context* context = Context::cast(list);
+ // Note that selecting code unlinks the functions that refer to it.
+ SelectCodeToDeoptimize(context, &filter, codes, &zone, undefined);
+ list = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
+ }
+
+ // Now deoptimize all the code.
+ for (int i = 0; i < codes->length(); i++) {
+ DeoptimizeCode(isolate, codes->at(i));
+ }
+}
+
+
+void Deoptimizer::DeoptimizeCode(Isolate* isolate, Code* code) {
+ HandleScope scope(isolate);
+ DisallowHeapAllocation nha;
+
+ // Do platform-specific patching of the optimized code.
+ PatchCodeForDeoptimization(isolate, code);
+
+ // 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);
+}
+
+
+void Deoptimizer::HandleWeakDeoptimizedCode(v8::Isolate* isolate,
+ v8::Persistent<v8::Value>* obj,
+ void* parameter) {
+ DeoptimizingCodeListNode* node =
+ reinterpret_cast<DeoptimizingCodeListNode*>(parameter);
+ DeoptimizerData* data =
+ reinterpret_cast<Isolate*>(isolate)->deoptimizer_data();
+ data->RemoveDeoptimizingCode(*node->code());
+#ifdef DEBUG
+ for (DeoptimizingCodeListNode* current = data->deoptimizing_code_list_;
+ current != NULL;
+ current = current->next()) {
+ ASSERT(current != node);
+ }
+#endif
+}
+
+
+void Deoptimizer::ComputeOutputFrames(Deoptimizer* deoptimizer) {
+ deoptimizer->DoComputeOutputFrames();
+}
+
+
+bool Deoptimizer::TraceEnabledFor(BailoutType deopt_type,
+ StackFrame::Type frame_type) {
+ switch (deopt_type) {
+ case EAGER:
+ case SOFT:
+ case LAZY:
+ case DEBUGGER:
+ return (frame_type == StackFrame::STUB)
+ ? FLAG_trace_stub_failures
+ : FLAG_trace_deopt;
+ case OSR:
+ return FLAG_trace_osr;
+ }
+ UNREACHABLE();
+ return false;
+}
+
+
+const char* Deoptimizer::MessageFor(BailoutType type) {
+ switch (type) {
+ case EAGER: return "eager";
+ case SOFT: return "soft";
+ case LAZY: return "lazy";
+ case DEBUGGER: return "debugger";
+ case OSR: return "OSR";
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+Deoptimizer::Deoptimizer(Isolate* isolate,
+ JSFunction* function,
+ BailoutType type,
+ unsigned bailout_id,
+ Address from,
+ int fp_to_sp_delta,
+ Code* optimized_code)
+ : isolate_(isolate),
+ function_(function),
+ bailout_id_(bailout_id),
+ bailout_type_(type),
+ from_(from),
+ fp_to_sp_delta_(fp_to_sp_delta),
+ has_alignment_padding_(0),
+ input_(NULL),
+ output_count_(0),
+ jsframe_count_(0),
+ output_(NULL),
+ deferred_objects_tagged_values_(0),
+ deferred_objects_double_values_(0),
+ deferred_objects_(0),
+ deferred_heap_numbers_(0),
+ jsframe_functions_(0),
+ jsframe_has_adapted_arguments_(0),
+ materialized_values_(NULL),
+ materialized_objects_(NULL),
+ materialization_value_index_(0),
+ materialization_object_index_(0),
+ trace_(false) {
+ // For COMPILED_STUBs called from builtins, the function pointer is a SMI
+ // indicating an internal frame.
+ if (function->IsSmi()) {
+ function = NULL;
+ }
+ ASSERT(from != NULL);
+ if (function != NULL && function->IsOptimized()) {
+ function->shared()->increment_deopt_count();
+ if (bailout_type_ == Deoptimizer::SOFT) {
+ isolate->counters()->soft_deopts_executed()->Increment();
+ // Soft deopts shouldn't count against the overall re-optimization count
+ // that can eventually lead to disabling optimization for a function.
+ int opt_count = function->shared()->opt_count();
+ if (opt_count > 0) opt_count--;
+ function->shared()->set_opt_count(opt_count);
+ }
+ }
+ compiled_code_ = FindOptimizedCode(function, optimized_code);
+ StackFrame::Type frame_type = function == NULL
+ ? StackFrame::STUB
+ : StackFrame::JAVA_SCRIPT;
+ trace_ = TraceEnabledFor(type, frame_type);
+#ifdef DEBUG
+ CHECK(AllowHeapAllocation::IsAllowed());
+ disallow_heap_allocation_ = new DisallowHeapAllocation();
+#endif // DEBUG
+ unsigned size = ComputeInputFrameSize();
+ input_ = new(size) FrameDescription(size, function);
+ input_->SetFrameType(frame_type);
+}
+
+
+Code* Deoptimizer::FindOptimizedCode(JSFunction* function,
+ Code* optimized_code) {
+ switch (bailout_type_) {
+ case Deoptimizer::SOFT:
+ case Deoptimizer::EAGER:
+ case Deoptimizer::LAZY: {
+ Code* compiled_code =
+ isolate_->deoptimizer_data()->FindDeoptimizingCode(from_);
+ return (compiled_code == NULL)
+ ? static_cast<Code*>(isolate_->FindCodeObject(from_))
+ : compiled_code;
+ }
+ case Deoptimizer::OSR: {
+ // The function has already been optimized and we're transitioning
+ // from the unoptimized shared version to the optimized one in the
+ // function. The return address (from_) points to unoptimized code.
+ Code* compiled_code = function->code();
+ ASSERT(compiled_code->kind() == Code::OPTIMIZED_FUNCTION);
+ ASSERT(!compiled_code->contains(from_));
+ return compiled_code;
+ }
+ case Deoptimizer::DEBUGGER:
+ ASSERT(optimized_code->contains(from_));
+ return optimized_code;
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+void Deoptimizer::PrintFunctionName() {
+ if (function_->IsJSFunction()) {
+ function_->PrintName();
+ } else {
+ PrintF("%s", Code::Kind2String(compiled_code_->kind()));
+ }
+}
+
+
+Deoptimizer::~Deoptimizer() {
+ ASSERT(input_ == NULL && output_ == NULL);
+ ASSERT(disallow_heap_allocation_ == NULL);
+}
+
+
+void Deoptimizer::DeleteFrameDescriptions() {
+ delete input_;
+ for (int i = 0; i < output_count_; ++i) {
+ if (output_[i] != input_) delete output_[i];
+ }
+ delete[] output_;
+ input_ = NULL;
+ output_ = NULL;
+#ifdef DEBUG
+ CHECK(!AllowHeapAllocation::IsAllowed());
+ CHECK(disallow_heap_allocation_ != NULL);
+ delete disallow_heap_allocation_;
+ disallow_heap_allocation_ = NULL;
+#endif // DEBUG
+}
+
+
+Address Deoptimizer::GetDeoptimizationEntry(Isolate* isolate,
+ int id,
+ BailoutType type,
+ GetEntryMode mode) {
+ ASSERT(id >= 0);
+ if (id >= kMaxNumberOfEntries) return NULL;
+ if (mode == ENSURE_ENTRY_CODE) {
+ EnsureCodeForDeoptimizationEntry(isolate, type, id);
+ } else {
+ ASSERT(mode == CALCULATE_ENTRY_ADDRESS);
+ }
+ DeoptimizerData* data = isolate->deoptimizer_data();
+ ASSERT(type < kBailoutTypesWithCodeEntry);
+ MemoryChunk* base = data->deopt_entry_code_[type];
+ return base->area_start() + (id * table_entry_size_);
+}
+
+
+int Deoptimizer::GetDeoptimizationId(Isolate* isolate,
+ Address addr,
+ BailoutType type) {
+ DeoptimizerData* data = isolate->deoptimizer_data();
+ MemoryChunk* base = data->deopt_entry_code_[type];
+ Address start = base->area_start();
+ if (base == NULL ||
+ addr < start ||
+ addr >= start + (kMaxNumberOfEntries * table_entry_size_)) {
+ return kNotDeoptimizationEntry;
+ }
+ ASSERT_EQ(0,
+ static_cast<int>(addr - start) % table_entry_size_);
+ return static_cast<int>(addr - start) / table_entry_size_;
+}
+
+
+int Deoptimizer::GetOutputInfo(DeoptimizationOutputData* data,
+ BailoutId id,
+ SharedFunctionInfo* shared) {
+ // TODO(kasperl): For now, we do a simple linear search for the PC
+ // offset associated with the given node id. This should probably be
+ // changed to a binary search.
+ int length = data->DeoptPoints();
+ for (int i = 0; i < length; i++) {
+ if (data->AstId(i) == id) {
+ return data->PcAndState(i)->value();
+ }
+ }
+ PrintF("[couldn't find pc offset for node=%d]\n", id.ToInt());
+ PrintF("[method: %s]\n", *shared->DebugName()->ToCString());
+ // Print the source code if available.
+ HeapStringAllocator string_allocator;
+ StringStream stream(&string_allocator);
+ shared->SourceCodePrint(&stream, -1);
+ PrintF("[source:\n%s\n]", *stream.ToCString());
+
+ FATAL("unable to find pc offset during deoptimization");
+ return -1;
+}
+
+
+int Deoptimizer::GetDeoptimizedCodeCount(Isolate* isolate) {
+ int length = 0;
+ DeoptimizingCodeListNode* node =
+ isolate->deoptimizer_data()->deoptimizing_code_list_;
+ while (node != NULL) {
+ length++;
+ node = node->next();
+ }
+ return length;
+}
+
+
+// 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();
+ return;
+ }
+
+ // Print some helpful diagnostic information.
+ int64_t start = OS::Ticks();
+ if (FLAG_log_timer_events &&
+ compiled_code_->kind() == Code::OPTIMIZED_FUNCTION) {
+ LOG(isolate(), CodeDeoptEvent(compiled_code_));
+ }
+ if (trace_) {
+ PrintF("[deoptimizing (DEOPT %s): begin 0x%08" V8PRIxPTR " ",
+ MessageFor(bailout_type_),
+ reinterpret_cast<intptr_t>(function_));
+ PrintFunctionName();
+ PrintF(" @%d, FP to SP delta: %d]\n", bailout_id_, fp_to_sp_delta_);
+ if (bailout_type_ == EAGER || bailout_type_ == SOFT) {
+ compiled_code_->PrintDeoptLocation(bailout_id_);
+ }
+ }
+
+ // Determine basic deoptimization information. The optimized frame is
+ // described by the input data.
+ DeoptimizationInputData* input_data =
+ DeoptimizationInputData::cast(compiled_code_->deoptimization_data());
+ BailoutId node_id = input_data->AstId(bailout_id_);
+ ByteArray* translations = input_data->TranslationByteArray();
+ unsigned translation_index =
+ input_data->TranslationIndex(bailout_id_)->value();
+
+ // Do the input frame to output frame(s) translation.
+ TranslationIterator iterator(translations, translation_index);
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator.Next());
+ ASSERT(Translation::BEGIN == opcode);
+ USE(opcode);
+ // 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) {
+ output_[i] = NULL;
+ }
+ output_count_ = count;
+
+ // Translate each output frame.
+ for (int i = 0; i < count; ++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;
+ case Translation::CONSTRUCT_STUB_FRAME:
+ DoComputeConstructStubFrame(&iterator, i);
+ break;
+ case Translation::GETTER_STUB_FRAME:
+ DoComputeAccessorStubFrame(&iterator, i, false);
+ break;
+ case Translation::SETTER_STUB_FRAME:
+ DoComputeAccessorStubFrame(&iterator, i, true);
+ break;
+ case Translation::COMPILED_STUB_FRAME:
+ DoComputeCompiledStubFrame(&iterator, i);
+ break;
+ case Translation::BEGIN:
+ case Translation::REGISTER:
+ case Translation::INT32_REGISTER:
+ case Translation::UINT32_REGISTER:
+ case Translation::DOUBLE_REGISTER:
+ case Translation::STACK_SLOT:
+ case Translation::INT32_STACK_SLOT:
+ case Translation::UINT32_STACK_SLOT:
+ case Translation::DOUBLE_STACK_SLOT:
+ case Translation::LITERAL:
+ case Translation::ARGUMENTS_OBJECT:
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ // Print some helpful diagnostic information.
+ if (trace_) {
+ double ms = static_cast<double>(OS::Ticks() - start) / 1000;
+ int index = output_count_ - 1; // Index of the topmost frame.
+ JSFunction* function = output_[index]->GetFunction();
+ PrintF("[deoptimizing (%s): end 0x%08" V8PRIxPTR " ",
+ MessageFor(bailout_type_),
+ reinterpret_cast<intptr_t>(function));
+ PrintFunctionName();
+ PrintF(" @%d => node=%d, pc=0x%08" V8PRIxPTR ", state=%s, alignment=%s,"
+ " took %0.3f ms]\n",
+ bailout_id_,
+ node_id.ToInt(),
+ output_[index]->GetPc(),
+ FullCodeGenerator::State2String(
+ static_cast<FullCodeGenerator::State>(
+ output_[index]->GetState()->value())),
+ has_alignment_padding_ ? "with padding" : "no padding",
+ ms);
+ }
+}
+
+
+void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
+ int frame_index) {
+ BailoutId node_id = BailoutId(iterator->Next());
+ JSFunction* function;
+ if (frame_index != 0) {
+ function = JSFunction::cast(ComputeLiteral(iterator->Next()));
+ } else {
+ int closure_id = iterator->Next();
+ USE(closure_id);
+ ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
+ function = function_;
+ }
+ unsigned height = iterator->Next();
+ unsigned height_in_bytes = height * kPointerSize;
+ if (trace_) {
+ PrintF(" translating ");
+ function->PrintName();
+ PrintF(" => node=%d, height=%d\n", node_id.ToInt(), 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.
+ Register fp_reg = JavaScriptFrame::fp_register();
+ intptr_t top_address;
+ if (is_bottommost) {
+ // Determine whether the input frame contains alignment padding.
+ has_alignment_padding_ = HasAlignmentPadding(function) ? 1 : 0;
+ // 2 = context and function in the frame.
+ // 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(fp_reg.code()) - (2 * kPointerSize) -
+ height_in_bytes + has_alignment_padding_ * kPointerSize;
+ } 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 -= kPCOnStackSize;
+ input_offset -= kPCOnStackSize;
+ intptr_t value;
+ if (is_bottommost) {
+ value = input_->GetFrameSlot(input_offset);
+ } else {
+ value = output_[frame_index - 1]->GetPc();
+ }
+ output_frame->SetCallerPc(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; 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 -= kFPOnStackSize;
+ input_offset -= kFPOnStackSize;
+ if (is_bottommost) {
+ value = input_->GetFrameSlot(input_offset);
+ } else {
+ value = output_[frame_index - 1]->GetFp();
+ }
+ output_frame->SetCallerFp(output_offset, value);
+ intptr_t fp_value = top_address + output_offset;
+ ASSERT(!is_bottommost || (input_->GetRegister(fp_reg.code()) +
+ has_alignment_padding_ * kPointerSize) == fp_value);
+ output_frame->SetFp(fp_value);
+ if (is_topmost) output_frame->SetRegister(fp_reg.code(), fp_value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; caller's fp\n",
+ fp_value, output_offset, value);
+ }
+ ASSERT(!is_bottommost || !has_alignment_padding_ ||
+ (fp_value & kPointerSize) != 0);
+
+ // 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.
+ Register context_reg = JavaScriptFrame::context_register();
+ 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);
+ output_frame->SetContext(value);
+ if (is_topmost) output_frame->SetRegister(context_reg.code(), value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR "; 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<intptr_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 (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR "; 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);
+ intptr_t pc_value = reinterpret_cast<intptr_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 = builtins->builtin(Builtins::kNotifyDeoptimized);
+ if (bailout_type_ == LAZY) {
+ continuation = builtins->builtin(Builtins::kNotifyLazyDeoptimized);
+ } else if (bailout_type_ == SOFT) {
+ continuation = builtins->builtin(Builtins::kNotifySoftDeoptimized);
+ } else {
+ ASSERT(bailout_type_ == EAGER);
+ }
+ output_frame->SetContinuation(
+ reinterpret_cast<intptr_t>(continuation->entry()));
+ }
+}
+
+
+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 (trace_) {
+ PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes);
+ }
+
+ unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
+ 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;
+ for (int i = 0; i < parameter_count; ++i) {
+ output_offset -= kPointerSize;
+ DoTranslateCommand(iterator, frame_index, output_offset);
+ }
+
+ // Read caller's PC from the previous frame.
+ output_offset -= kPCOnStackSize;
+ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+ output_frame->SetCallerPc(output_offset, callers_pc);
+ if (trace_) {
+ 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 -= kFPOnStackSize;
+ intptr_t value = output_[frame_index - 1]->GetFp();
+ output_frame->SetCallerFp(output_offset, value);
+ intptr_t fp_value = top_address + output_offset;
+ output_frame->SetFp(fp_value);
+ if (trace_) {
+ 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;
+ intptr_t context = reinterpret_cast<intptr_t>(
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ output_frame->SetFrameSlot(output_offset, context);
+ if (trace_) {
+ 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;
+ value = reinterpret_cast<intptr_t>(function);
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ 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;
+ value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1));
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ 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::DoComputeConstructStubFrame(TranslationIterator* iterator,
+ int frame_index) {
+ Builtins* builtins = isolate_->builtins();
+ Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric);
+ JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
+ unsigned height = iterator->Next();
+ unsigned height_in_bytes = height * kPointerSize;
+ if (trace_) {
+ PrintF(" translating construct stub => height=%d\n", height_in_bytes);
+ }
+
+ unsigned fixed_frame_size = ConstructFrameConstants::kFrameSize;
+ 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::CONSTRUCT);
+
+ // Construct stub 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;
+ for (int i = 0; i < parameter_count; ++i) {
+ output_offset -= kPointerSize;
+ int deferred_object_index = deferred_objects_.length();
+ DoTranslateCommand(iterator, frame_index, output_offset);
+ // The allocated receiver of a construct stub frame is passed as the
+ // receiver parameter through the translation. It might be encoding
+ // a captured object, patch the slot address for a captured object.
+ if (i == 0 && deferred_objects_.length() > deferred_object_index) {
+ ASSERT(!deferred_objects_[deferred_object_index].is_arguments());
+ deferred_objects_[deferred_object_index].patch_slot_address(top_address);
+ }
+ }
+
+ // Read caller's PC from the previous frame.
+ output_offset -= kPCOnStackSize;
+ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+ output_frame->SetCallerPc(output_offset, callers_pc);
+ if (trace_) {
+ 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 -= kFPOnStackSize;
+ intptr_t value = output_[frame_index - 1]->GetFp();
+ output_frame->SetCallerFp(output_offset, value);
+ intptr_t fp_value = top_address + output_offset;
+ output_frame->SetFp(fp_value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; caller's fp\n",
+ fp_value, output_offset, value);
+ }
+
+ // The context can be gotten from the previous frame.
+ output_offset -= kPointerSize;
+ value = output_[frame_index - 1]->GetContext();
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; context\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ // A marker value is used in place of the function.
+ output_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::CONSTRUCT));
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; function (construct sentinel)\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ // The output frame reflects a JSConstructStubGeneric frame.
+ output_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(construct_stub);
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; code object\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ // Number of incoming arguments.
+ output_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1));
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; argc (%d)\n",
+ top_address + output_offset, output_offset, value, height - 1);
+ }
+
+ // Constructor function being invoked by the stub (only present on some
+ // architectures, indicated by kConstructorOffset).
+ if (ConstructFrameConstants::kConstructorOffset != kMinInt) {
+ output_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(function);
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; constructor function\n",
+ top_address + output_offset, output_offset, value);
+ }
+ }
+
+ // The newly allocated object was passed as receiver in the artificial
+ // constructor stub environment created by HEnvironment::CopyForInlining().
+ output_offset -= kPointerSize;
+ value = output_frame->GetFrameSlot(output_frame_size - kPointerSize);
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; allocated receiver\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ ASSERT(0 == output_offset);
+
+ intptr_t pc = reinterpret_cast<intptr_t>(
+ construct_stub->instruction_start() +
+ isolate_->heap()->construct_stub_deopt_pc_offset()->value());
+ output_frame->SetPc(pc);
+}
+
+
+void Deoptimizer::DoComputeAccessorStubFrame(TranslationIterator* iterator,
+ int frame_index,
+ bool is_setter_stub_frame) {
+ JSFunction* accessor = JSFunction::cast(ComputeLiteral(iterator->Next()));
+ // The receiver (and the implicit return value, if any) are expected in
+ // registers by the LoadIC/StoreIC, so they don't belong to the output stack
+ // frame. This means that we have to use a height of 0.
+ unsigned height = 0;
+ unsigned height_in_bytes = height * kPointerSize;
+ const char* kind = is_setter_stub_frame ? "setter" : "getter";
+ if (trace_) {
+ PrintF(" translating %s stub => height=%u\n", kind, height_in_bytes);
+ }
+
+ // We need 1 stack entry for the return address + 4 stack entries from
+ // StackFrame::INTERNAL (FP, context, frame type, code object, see
+ // MacroAssembler::EnterFrame). For a setter stub frame we need one additional
+ // entry for the implicit return value, see
+ // StoreStubCompiler::CompileStoreViaSetter.
+ unsigned fixed_frame_entries = (kPCOnStackSize / kPointerSize) +
+ (kFPOnStackSize / kPointerSize) + 3 +
+ (is_setter_stub_frame ? 1 : 0);
+ unsigned fixed_frame_size = fixed_frame_entries * kPointerSize;
+ 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, accessor);
+ output_frame->SetFrameType(StackFrame::INTERNAL);
+
+ // A frame for an accessor stub can not be the topmost or bottommost one.
+ 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 = output_[frame_index - 1]->GetTop() - output_frame_size;
+ output_frame->SetTop(top_address);
+
+ unsigned output_offset = output_frame_size;
+
+ // Read caller's PC from the previous frame.
+ output_offset -= kPCOnStackSize;
+ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
+ output_frame->SetCallerPc(output_offset, callers_pc);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 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 -= kFPOnStackSize;
+ intptr_t value = output_[frame_index - 1]->GetFp();
+ output_frame->SetCallerFp(output_offset, value);
+ intptr_t fp_value = top_address + output_offset;
+ output_frame->SetFp(fp_value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+ " ; caller's fp\n",
+ fp_value, output_offset, value);
+ }
+
+ // The context can be gotten from the previous frame.
+ output_offset -= kPointerSize;
+ value = output_[frame_index - 1]->GetContext();
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+ " ; context\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ // A marker value is used in place of the function.
+ output_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::INTERNAL));
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+ " ; function (%s sentinel)\n",
+ top_address + output_offset, output_offset, value, kind);
+ }
+
+ // Get Code object from accessor stub.
+ output_offset -= kPointerSize;
+ Builtins::Name name = is_setter_stub_frame ?
+ Builtins::kStoreIC_Setter_ForDeopt :
+ Builtins::kLoadIC_Getter_ForDeopt;
+ Code* accessor_stub = isolate_->builtins()->builtin(name);
+ value = reinterpret_cast<intptr_t>(accessor_stub);
+ output_frame->SetFrameSlot(output_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08" V8PRIxPTR
+ " ; code object\n",
+ top_address + output_offset, output_offset, value);
+ }
+
+ // Skip receiver.
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator->Next());
+ iterator->Skip(Translation::NumberOfOperandsFor(opcode));
+
+ if (is_setter_stub_frame) {
+ // The implicit return value was part of the artificial setter stub
+ // environment.
+ output_offset -= kPointerSize;
+ DoTranslateCommand(iterator, frame_index, output_offset);
+ }
+
+ ASSERT(0 == output_offset);
+
+ Smi* offset = is_setter_stub_frame ?
+ isolate_->heap()->setter_stub_deopt_pc_offset() :
+ isolate_->heap()->getter_stub_deopt_pc_offset();
+ intptr_t pc = reinterpret_cast<intptr_t>(
+ accessor_stub->instruction_start() + offset->value());
+ output_frame->SetPc(pc);
+}
+
+
+void Deoptimizer::DoComputeCompiledStubFrame(TranslationIterator* iterator,
+ int frame_index) {
+ //
+ // FROM TO
+ // | .... | | .... |
+ // +-------------------------+ +-------------------------+
+ // | JSFunction continuation | | JSFunction continuation |
+ // +-------------------------+ +-------------------------+
+ // | | saved frame (FP) | | saved frame (FP) |
+ // | +=========================+<-fpreg +=========================+<-fpreg
+ // | | JSFunction context | | JSFunction context |
+ // v +-------------------------+ +-------------------------|
+ // | COMPILED_STUB marker | | STUB_FAILURE marker |
+ // +-------------------------+ +-------------------------+
+ // | | | caller args.arguments_ |
+ // | ... | +-------------------------+
+ // | | | caller args.length_ |
+ // |-------------------------|<-spreg +-------------------------+
+ // | caller args pointer |
+ // +-------------------------+
+ // | caller stack param 1 |
+ // parameters in registers +-------------------------+
+ // and spilled to stack | .... |
+ // +-------------------------+
+ // | caller stack param n |
+ // +-------------------------+<-spreg
+ // reg = number of parameters
+ // reg = failure handler address
+ // reg = saved frame
+ // reg = JSFunction context
+ //
+
+ ASSERT(compiled_code_->is_crankshafted() &&
+ compiled_code_->kind() != Code::OPTIMIZED_FUNCTION);
+ int major_key = compiled_code_->major_key();
+ CodeStubInterfaceDescriptor* descriptor =
+ isolate_->code_stub_interface_descriptor(major_key);
+
+ // The output frame must have room for all pushed register parameters
+ // and the standard stack frame slots. Include space for an argument
+ // object to the callee and optionally the space to pass the argument
+ // object to the stub failure handler.
+ ASSERT(descriptor->register_param_count_ >= 0);
+ int height_in_bytes = kPointerSize * descriptor->register_param_count_ +
+ sizeof(Arguments) + kPointerSize;
+ int fixed_frame_size = StandardFrameConstants::kFixedFrameSize;
+ int input_frame_size = input_->GetFrameSize();
+ int output_frame_size = height_in_bytes + fixed_frame_size;
+ if (trace_) {
+ PrintF(" translating %s => StubFailureTrampolineStub, height=%d\n",
+ CodeStub::MajorName(static_cast<CodeStub::Major>(major_key), false),
+ height_in_bytes);
+ }
+
+ // The stub failure trampoline is a single frame.
+ FrameDescription* output_frame =
+ new(output_frame_size) FrameDescription(output_frame_size, NULL);
+ output_frame->SetFrameType(StackFrame::STUB_FAILURE_TRAMPOLINE);
+ ASSERT(frame_index == 0);
+ output_[frame_index] = output_frame;
+
+ // The top address for the output frame can be computed from the input
+ // frame pointer and the output frame's height. Subtract space for the
+ // context and function slots.
+ Register fp_reg = StubFailureTrampolineFrame::fp_register();
+ intptr_t top_address = input_->GetRegister(fp_reg.code()) -
+ (2 * kPointerSize) - height_in_bytes;
+ output_frame->SetTop(top_address);
+
+ // Read caller's PC (JSFunction continuation) from the input frame.
+ unsigned input_frame_offset = input_frame_size - kPCOnStackSize;
+ unsigned output_frame_offset = output_frame_size - kFPOnStackSize;
+ intptr_t value = input_->GetFrameSlot(input_frame_offset);
+ output_frame->SetCallerPc(output_frame_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; caller's pc\n",
+ top_address + output_frame_offset, output_frame_offset, value);
+ }
+
+ // Read caller's FP from the input frame, and set this frame's FP.
+ input_frame_offset -= kFPOnStackSize;
+ value = input_->GetFrameSlot(input_frame_offset);
+ output_frame_offset -= kFPOnStackSize;
+ output_frame->SetCallerFp(output_frame_offset, value);
+ intptr_t frame_ptr = input_->GetRegister(fp_reg.code());
+ output_frame->SetRegister(fp_reg.code(), frame_ptr);
+ output_frame->SetFp(frame_ptr);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; caller's fp\n",
+ top_address + output_frame_offset, output_frame_offset, value);
+ }
+
+ // The context can be gotten from the input frame.
+ Register context_reg = StubFailureTrampolineFrame::context_register();
+ input_frame_offset -= kPointerSize;
+ value = input_->GetFrameSlot(input_frame_offset);
+ output_frame->SetRegister(context_reg.code(), value);
+ output_frame_offset -= kPointerSize;
+ output_frame->SetFrameSlot(output_frame_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; context\n",
+ top_address + output_frame_offset, output_frame_offset, value);
+ }
+
+ // A marker value is used in place of the function.
+ output_frame_offset -= kPointerSize;
+ value = reinterpret_cast<intptr_t>(
+ Smi::FromInt(StackFrame::STUB_FAILURE_TRAMPOLINE));
+ output_frame->SetFrameSlot(output_frame_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; function (stub failure sentinel)\n",
+ top_address + output_frame_offset, output_frame_offset, value);
+ }
+
+ intptr_t caller_arg_count = 0;
+ bool arg_count_known = descriptor->stack_parameter_count_ == NULL;
+
+ // Build the Arguments object for the caller's parameters and a pointer to it.
+ output_frame_offset -= kPointerSize;
+ int args_arguments_offset = output_frame_offset;
+ intptr_t the_hole = reinterpret_cast<intptr_t>(
+ isolate_->heap()->the_hole_value());
+ if (arg_count_known) {
+ value = frame_ptr + StandardFrameConstants::kCallerSPOffset +
+ (caller_arg_count - 1) * kPointerSize;
+ } else {
+ value = the_hole;
+ }
+
+ output_frame->SetFrameSlot(args_arguments_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; args.arguments %s\n",
+ top_address + args_arguments_offset, args_arguments_offset, value,
+ arg_count_known ? "" : "(the hole)");
+ }
+
+ output_frame_offset -= kPointerSize;
+ int length_frame_offset = output_frame_offset;
+ value = arg_count_known ? caller_arg_count : the_hole;
+ output_frame->SetFrameSlot(length_frame_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; args.length %s\n",
+ top_address + length_frame_offset, length_frame_offset, value,
+ arg_count_known ? "" : "(the hole)");
+ }
+
+ output_frame_offset -= kPointerSize;
+ value = frame_ptr + StandardFrameConstants::kCallerSPOffset -
+ (output_frame_size - output_frame_offset) + kPointerSize;
+ output_frame->SetFrameSlot(output_frame_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; args*\n",
+ top_address + output_frame_offset, output_frame_offset, value);
+ }
+
+ // Copy the register parameters to the failure frame.
+ for (int i = 0; i < descriptor->register_param_count_; ++i) {
+ output_frame_offset -= kPointerSize;
+ DoTranslateCommand(iterator, 0, output_frame_offset);
+ }
+
+ if (!arg_count_known) {
+ DoTranslateCommand(iterator, 0, length_frame_offset,
+ TRANSLATED_VALUE_IS_NATIVE);
+ caller_arg_count = output_frame->GetFrameSlot(length_frame_offset);
+ value = frame_ptr + StandardFrameConstants::kCallerSPOffset +
+ (caller_arg_count - 1) * kPointerSize;
+ output_frame->SetFrameSlot(args_arguments_offset, value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
+ V8PRIxPTR " ; args.arguments\n",
+ top_address + args_arguments_offset, args_arguments_offset, value);
+ }
+ }
+
+ ASSERT(0 == output_frame_offset);
+
+ // Copy the double registers from the input into the output frame.
+ CopyDoubleRegisters(output_frame);
+
+ // Fill registers containing handler and number of parameters.
+ SetPlatformCompiledStubRegisters(output_frame, descriptor);
+
+ // Compute this frame's PC, state, and continuation.
+ Code* trampoline = NULL;
+ StubFunctionMode function_mode = descriptor->function_mode_;
+ StubFailureTrampolineStub(function_mode).FindCodeInCache(&trampoline,
+ isolate_);
+ ASSERT(trampoline != NULL);
+ output_frame->SetPc(reinterpret_cast<intptr_t>(
+ trampoline->instruction_start()));
+ output_frame->SetState(Smi::FromInt(FullCodeGenerator::NO_REGISTERS));
+ Code* notify_failure =
+ isolate_->builtins()->builtin(Builtins::kNotifyStubFailure);
+ output_frame->SetContinuation(
+ reinterpret_cast<intptr_t>(notify_failure->entry()));
+}
+
+
+Handle<Object> Deoptimizer::MaterializeNextHeapObject() {
+ int object_index = materialization_object_index_++;
+ ObjectMaterializationDescriptor desc = deferred_objects_[object_index];
+ const int length = desc.object_length();
+
+ if (desc.duplicate_object() >= 0) {
+ // Found a previously materialized object by de-duplication.
+ object_index = desc.duplicate_object();
+ materialized_objects_->Add(Handle<Object>());
+ } else if (desc.is_arguments() && ArgumentsObjectIsAdapted(object_index)) {
+ // Use the arguments adapter frame we just built to materialize the
+ // arguments object. FunctionGetArguments can't throw an exception.
+ Handle<JSFunction> function = ArgumentsObjectFunction(object_index);
+ Handle<JSObject> arguments = Handle<JSObject>::cast(
+ Accessors::FunctionGetArguments(function));
+ materialized_objects_->Add(arguments);
+ materialization_value_index_ += length;
+ } else if (desc.is_arguments()) {
+ // Construct an arguments object and copy the parameters to a newly
+ // allocated arguments object backing store.
+ Handle<JSFunction> function = ArgumentsObjectFunction(object_index);
+ Handle<JSObject> arguments =
+ isolate_->factory()->NewArgumentsObject(function, length);
+ Handle<FixedArray> array = isolate_->factory()->NewFixedArray(length);
+ ASSERT(array->length() == length);
+ arguments->set_elements(*array);
+ materialized_objects_->Add(arguments);
+ for (int i = 0; i < length; ++i) {
+ Handle<Object> value = MaterializeNextValue();
+ array->set(i, *value);
+ }
+ } else {
+ // Dispatch on the instance type of the object to be materialized.
+ Handle<Map> map = Handle<Map>::cast(MaterializeNextValue());
+ switch (map->instance_type()) {
+ case HEAP_NUMBER_TYPE: {
+ Handle<HeapNumber> number =
+ Handle<HeapNumber>::cast(MaterializeNextValue());
+ materialized_objects_->Add(number);
+ materialization_value_index_ += kDoubleSize / kPointerSize - 1;
+ break;
+ }
+ case JS_OBJECT_TYPE: {
+ Handle<JSObject> object =
+ isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED, false);
+ materialized_objects_->Add(object);
+ Handle<Object> properties = MaterializeNextValue();
+ Handle<Object> elements = MaterializeNextValue();
+ object->set_properties(FixedArray::cast(*properties));
+ object->set_elements(FixedArray::cast(*elements));
+ for (int i = 0; i < length - 3; ++i) {
+ Handle<Object> value = MaterializeNextValue();
+ object->FastPropertyAtPut(i, *value);
+ }
+ break;
+ }
+ default:
+ PrintF("[couldn't handle instance type %d]\n", map->instance_type());
+ UNREACHABLE();
+ }
+ }
+
+ return materialized_objects_->at(object_index);
+}
+
+
+Handle<Object> Deoptimizer::MaterializeNextValue() {
+ int value_index = materialization_value_index_++;
+ Handle<Object> value = materialized_values_->at(value_index);
+ if (*value == isolate_->heap()->arguments_marker()) {
+ value = MaterializeNextHeapObject();
+ }
+ return value;
+}
+
+
+void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) {
+ ASSERT_NE(DEBUGGER, bailout_type_);
+
+ // Walk all JavaScript output frames with the given frame iterator.
+ for (int frame_index = 0; frame_index < jsframe_count(); ++frame_index) {
+ if (frame_index != 0) it->Advance();
+ JavaScriptFrame* frame = it->frame();
+ jsframe_functions_.Add(handle(frame->function(), isolate_));
+ jsframe_has_adapted_arguments_.Add(frame->has_adapted_arguments());
+ }
+
+ // Handlify all tagged object values before triggering any allocation.
+ List<Handle<Object> > values(deferred_objects_tagged_values_.length());
+ for (int i = 0; i < deferred_objects_tagged_values_.length(); ++i) {
+ values.Add(Handle<Object>(deferred_objects_tagged_values_[i], isolate_));
+ }
+
+ // Play it safe and clear all unhandlified values before we continue.
+ deferred_objects_tagged_values_.Clear();
+
+ // Materialize all heap numbers before looking at arguments because when the
+ // output frames are used to materialize arguments objects later on they need
+ // to already contain valid heap numbers.
+ for (int i = 0; i < deferred_heap_numbers_.length(); i++) {
+ HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i];
+ Handle<Object> num = isolate_->factory()->NewNumber(d.value());
+ if (trace_) {
+ PrintF("Materialized a new heap number %p [%e] in slot %p\n",
+ reinterpret_cast<void*>(*num),
+ d.value(),
+ d.slot_address());
+ }
+ Memory::Object_at(d.slot_address()) = *num;
+ }
+
+ // Materialize all heap numbers required for arguments/captured objects.
+ for (int i = 0; i < values.length(); i++) {
+ if (!values.at(i)->IsTheHole()) continue;
+ double double_value = deferred_objects_double_values_[i];
+ Handle<Object> num = isolate_->factory()->NewNumber(double_value);
+ if (trace_) {
+ PrintF("Materialized a new heap number %p [%e] for object\n",
+ reinterpret_cast<void*>(*num), double_value);
+ }
+ values.Set(i, num);
+ }
+
+ // Materialize arguments/captured objects.
+ if (!deferred_objects_.is_empty()) {
+ List<Handle<Object> > materialized_objects(deferred_objects_.length());
+ materialized_objects_ = &materialized_objects;
+ materialized_values_ = &values;
+
+ while (materialization_object_index_ < deferred_objects_.length()) {
+ int object_index = materialization_object_index_;
+ ObjectMaterializationDescriptor descriptor =
+ deferred_objects_.at(object_index);
+
+ // Find a previously materialized object by de-duplication or
+ // materialize a new instance of the object if necessary. Store
+ // the materialized object into the frame slot.
+ Handle<Object> object = MaterializeNextHeapObject();
+ Memory::Object_at(descriptor.slot_address()) = *object;
+ if (trace_) {
+ if (descriptor.is_arguments()) {
+ PrintF("Materialized %sarguments object of length %d for %p: ",
+ ArgumentsObjectIsAdapted(object_index) ? "(adapted) " : "",
+ Handle<JSObject>::cast(object)->elements()->length(),
+ reinterpret_cast<void*>(descriptor.slot_address()));
+ } else {
+ PrintF("Materialized captured object of size %d for %p: ",
+ Handle<HeapObject>::cast(object)->Size(),
+ reinterpret_cast<void*>(descriptor.slot_address()));
+ }
+ object->ShortPrint();
+ PrintF("\n");
+ }
+ }
+
+ ASSERT(materialization_object_index_ == materialized_objects_->length());
+ ASSERT(materialization_value_index_ == materialized_values_->length());
+ }
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void Deoptimizer::MaterializeHeapNumbersForDebuggerInspectableFrame(
+ 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 (parameters_top <= slot && slot < parameters_bottom) {
+ Handle<Object> num = isolate_->factory()->NewNumber(d.value());
+
+ int index = (info->parameters_count() - 1) -
+ static_cast<int>(slot - parameters_top) / kPointerSize;
+
+ if (trace_) {
+ PrintF("Materializing a new heap number %p [%e] in slot %p"
+ "for parameter slot #%d\n",
+ reinterpret_cast<void*>(*num),
+ d.value(),
+ d.slot_address(),
+ index);
+ }
+
+ 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 (trace_) {
+ 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);
+ }
+ }
+}
+#endif
+
+
+static const char* TraceValueType(bool is_smi, bool is_native = false) {
+ if (is_native) {
+ return "native";
+ } else if (is_smi) {
+ return "smi";
+ }
+
+ return "heap number";
+}
+
+
+void Deoptimizer::DoTranslateObject(TranslationIterator* iterator,
+ int object_index,
+ int field_index) {
+ disasm::NameConverter converter;
+ Address object_slot = deferred_objects_[object_index].slot_address();
+
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator->Next());
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
+ case Translation::CONSTRUCT_STUB_FRAME:
+ case Translation::GETTER_STUB_FRAME:
+ case Translation::SETTER_STUB_FRAME:
+ case Translation::COMPILED_STUB_FRAME:
+ UNREACHABLE();
+ return;
+
+ case Translation::REGISTER: {
+ int input_reg = iterator->Next();
+ intptr_t input_value = input_->GetRegister(input_reg);
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ PrintF("0x%08" V8PRIxPTR " ; %s ", input_value,
+ converter.NameOfCPURegister(input_reg));
+ reinterpret_cast<Object*>(input_value)->ShortPrint();
+ PrintF("\n");
+ }
+ AddObjectTaggedValue(input_value);
+ return;
+ }
+
+ case Translation::INT32_REGISTER: {
+ int input_reg = iterator->Next();
+ intptr_t value = input_->GetRegister(input_reg);
+ bool is_smi = Smi::IsValid(value);
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ PrintF("%" V8PRIdPTR " ; %s (%s)\n", value,
+ converter.NameOfCPURegister(input_reg),
+ TraceValueType(is_smi));
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ AddObjectTaggedValue(tagged_value);
+ } else {
+ double double_value = static_cast<double>(static_cast<int32_t>(value));
+ AddObjectDoubleValue(double_value);
+ }
+ return;
+ }
+
+ case Translation::UINT32_REGISTER: {
+ int input_reg = iterator->Next();
+ uintptr_t value = static_cast<uintptr_t>(input_->GetRegister(input_reg));
+ bool is_smi = (value <= static_cast<uintptr_t>(Smi::kMaxValue));
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ PrintF("%" V8PRIdPTR " ; uint %s (%s)\n", value,
+ converter.NameOfCPURegister(input_reg),
+ TraceValueType(is_smi));
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ AddObjectTaggedValue(tagged_value);
+ } else {
+ double double_value = static_cast<double>(static_cast<uint32_t>(value));
+ AddObjectDoubleValue(double_value);
+ }
+ return;
+ }
+
+ case Translation::DOUBLE_REGISTER: {
+ int input_reg = iterator->Next();
+ double value = input_->GetDoubleRegister(input_reg);
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ PrintF("%e ; %s\n", value,
+ DoubleRegister::AllocationIndexToString(input_reg));
+ }
+ AddObjectDoubleValue(value);
+ return;
+ }
+
+ case Translation::STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
+ intptr_t input_value = input_->GetFrameSlot(input_offset);
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ PrintF("0x%08" V8PRIxPTR " ; [sp + %d] ", input_value, input_offset);
+ reinterpret_cast<Object*>(input_value)->ShortPrint();
+ PrintF("\n");
+ }
+ AddObjectTaggedValue(input_value);
+ return;
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
+ intptr_t value = input_->GetFrameSlot(input_offset);
+ bool is_smi = Smi::IsValid(value);
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ PrintF("%" V8PRIdPTR " ; [sp + %d] (%s)\n",
+ value, input_offset, TraceValueType(is_smi));
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ AddObjectTaggedValue(tagged_value);
+ } else {
+ double double_value = static_cast<double>(static_cast<int32_t>(value));
+ AddObjectDoubleValue(double_value);
+ }
+ return;
+ }
+
+ case Translation::UINT32_STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
+ uintptr_t value =
+ static_cast<uintptr_t>(input_->GetFrameSlot(input_offset));
+ bool is_smi = (value <= static_cast<uintptr_t>(Smi::kMaxValue));
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ PrintF("%" V8PRIdPTR " ; [sp + %d] (uint %s)\n",
+ value, input_offset, TraceValueType(is_smi));
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ AddObjectTaggedValue(tagged_value);
+ } else {
+ double double_value = static_cast<double>(static_cast<uint32_t>(value));
+ AddObjectDoubleValue(double_value);
+ }
+ return;
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
+ double value = input_->GetDoubleFrameSlot(input_offset);
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ PrintF("%e ; [sp + %d]\n", value, input_offset);
+ }
+ AddObjectDoubleValue(value);
+ return;
+ }
+
+ case Translation::LITERAL: {
+ Object* literal = ComputeLiteral(iterator->Next());
+ if (trace_) {
+ PrintF(" object @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ literal->ShortPrint();
+ PrintF(" ; literal\n");
+ }
+ intptr_t value = reinterpret_cast<intptr_t>(literal);
+ AddObjectTaggedValue(value);
+ return;
+ }
+
+ case Translation::DUPLICATED_OBJECT: {
+ int object_index = iterator->Next();
+ if (trace_) {
+ PrintF(" nested @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ isolate_->heap()->arguments_marker()->ShortPrint();
+ PrintF(" ; duplicate of object #%d\n", object_index);
+ }
+ // Use the materialization marker value as a sentinel and fill in
+ // the object after the deoptimized frame is built.
+ intptr_t value = reinterpret_cast<intptr_t>(
+ isolate_->heap()->arguments_marker());
+ AddObjectDuplication(0, object_index);
+ AddObjectTaggedValue(value);
+ return;
+ }
+
+ case Translation::ARGUMENTS_OBJECT:
+ case Translation::CAPTURED_OBJECT: {
+ int length = iterator->Next();
+ bool is_args = opcode == Translation::ARGUMENTS_OBJECT;
+ if (trace_) {
+ PrintF(" nested @0x%08" V8PRIxPTR ": [field #%d] <- ",
+ reinterpret_cast<intptr_t>(object_slot),
+ field_index);
+ isolate_->heap()->arguments_marker()->ShortPrint();
+ PrintF(" ; object (length = %d, is_args = %d)\n", length, is_args);
+ }
+ // Use the materialization marker value as a sentinel and fill in
+ // the object after the deoptimized frame is built.
+ intptr_t value = reinterpret_cast<intptr_t>(
+ isolate_->heap()->arguments_marker());
+ AddObjectStart(0, length, is_args);
+ AddObjectTaggedValue(value);
+ // We save the object values on the side and materialize the actual
+ // object after the deoptimized frame is built.
+ int object_index = deferred_objects_.length() - 1;
+ for (int i = 0; i < length; i++) {
+ DoTranslateObject(iterator, object_index, i);
+ }
+ return;
+ }
+ }
+}
+
+
+void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
+ int frame_index,
+ unsigned output_offset,
+ DeoptimizerTranslatedValueType value_type) {
+ disasm::NameConverter converter;
+ // A GC-safe temporary placeholder that we can put in the output frame.
+ const intptr_t kPlaceholder = reinterpret_cast<intptr_t>(Smi::FromInt(0));
+ bool is_native = value_type == TRANSLATED_VALUE_IS_NATIVE;
+
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator->Next());
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
+ case Translation::CONSTRUCT_STUB_FRAME:
+ case Translation::GETTER_STUB_FRAME:
+ case Translation::SETTER_STUB_FRAME:
+ case Translation::COMPILED_STUB_FRAME:
+ UNREACHABLE();
+ return;
+
+ case Translation::REGISTER: {
+ int input_reg = iterator->Next();
+ intptr_t input_value = input_->GetRegister(input_reg);
+ if (trace_) {
+ PrintF(
+ " 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;
+ }
+
+ case Translation::INT32_REGISTER: {
+ int input_reg = iterator->Next();
+ intptr_t value = input_->GetRegister(input_reg);
+ bool is_smi = (value_type == TRANSLATED_VALUE_IS_TAGGED) &&
+ Smi::IsValid(value);
+ if (trace_) {
+ PrintF(
+ " 0x%08" V8PRIxPTR ": [top + %d] <- %" V8PRIdPTR " ; %s (%s)\n",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset,
+ value,
+ converter.NameOfCPURegister(input_reg),
+ TraceValueType(is_smi, is_native));
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
+ } else if (value_type == TRANSLATED_VALUE_IS_NATIVE) {
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ } else {
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ ASSERT(value_type == TRANSLATED_VALUE_IS_TAGGED);
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
+ static_cast<double>(static_cast<int32_t>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ }
+ return;
+ }
+
+ case Translation::UINT32_REGISTER: {
+ int input_reg = iterator->Next();
+ uintptr_t value = static_cast<uintptr_t>(input_->GetRegister(input_reg));
+ bool is_smi = (value_type == TRANSLATED_VALUE_IS_TAGGED) &&
+ (value <= static_cast<uintptr_t>(Smi::kMaxValue));
+ if (trace_) {
+ PrintF(
+ " 0x%08" V8PRIxPTR ": [top + %d] <- %" V8PRIuPTR
+ " ; uint %s (%s)\n",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset,
+ value,
+ converter.NameOfCPURegister(input_reg),
+ TraceValueType(is_smi, is_native));
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
+ } else if (value_type == TRANSLATED_VALUE_IS_NATIVE) {
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ } else {
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ ASSERT(value_type == TRANSLATED_VALUE_IS_TAGGED);
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
+ static_cast<double>(static_cast<uint32_t>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ }
+ return;
+ }
+
+ case Translation::DOUBLE_REGISTER: {
+ int input_reg = iterator->Next();
+ double value = input_->GetDoubleRegister(input_reg);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; %s\n",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset,
+ value,
+ DoubleRegister::AllocationIndexToString(input_reg));
+ }
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset, value);
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ return;
+ }
+
+ case Translation::STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
+ intptr_t input_value = input_->GetFrameSlot(input_offset);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": ",
+ output_[frame_index]->GetTop() + output_offset);
+ PrintF("[top + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d] ",
+ output_offset,
+ input_value,
+ input_offset);
+ reinterpret_cast<Object*>(input_value)->ShortPrint();
+ PrintF("\n");
+ }
+ output_[frame_index]->SetFrameSlot(output_offset, input_value);
+ return;
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
+ intptr_t value = input_->GetFrameSlot(input_offset);
+ bool is_smi = (value_type == TRANSLATED_VALUE_IS_TAGGED) &&
+ Smi::IsValid(value);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": ",
+ output_[frame_index]->GetTop() + output_offset);
+ PrintF("[top + %d] <- %" V8PRIdPTR " ; [sp + %d] (%s)\n",
+ output_offset,
+ value,
+ input_offset,
+ TraceValueType(is_smi, is_native));
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
+ } else if (value_type == TRANSLATED_VALUE_IS_NATIVE) {
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ } else {
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ ASSERT(value_type == TRANSLATED_VALUE_IS_TAGGED);
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
+ static_cast<double>(static_cast<int32_t>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ }
+ return;
+ }
+
+ case Translation::UINT32_STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
+ uintptr_t value =
+ static_cast<uintptr_t>(input_->GetFrameSlot(input_offset));
+ bool is_smi = (value_type == TRANSLATED_VALUE_IS_TAGGED) &&
+ (value <= static_cast<uintptr_t>(Smi::kMaxValue));
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": ",
+ output_[frame_index]->GetTop() + output_offset);
+ PrintF("[top + %d] <- %" V8PRIuPTR " ; [sp + %d] (uint32 %s)\n",
+ output_offset,
+ value,
+ input_offset,
+ TraceValueType(is_smi, is_native));
+ }
+ if (is_smi) {
+ intptr_t tagged_value =
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, tagged_value);
+ } else if (value_type == TRANSLATED_VALUE_IS_NATIVE) {
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ } else {
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ ASSERT(value_type == TRANSLATED_VALUE_IS_TAGGED);
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset,
+ static_cast<double>(static_cast<uint32_t>(value)));
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ }
+ return;
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ int input_slot_index = iterator->Next();
+ unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index);
+ double value = input_->GetDoubleFrameSlot(input_offset);
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; [sp + %d]\n",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset,
+ value,
+ input_offset);
+ }
+ // We save the untagged value on the side and store a GC-safe
+ // temporary placeholder in the frame.
+ AddDoubleValue(output_[frame_index]->GetTop() + output_offset, value);
+ output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder);
+ return;
+ }
+
+ case Translation::LITERAL: {
+ Object* literal = ComputeLiteral(iterator->Next());
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset);
+ literal->ShortPrint();
+ PrintF(" ; literal\n");
+ }
+ intptr_t value = reinterpret_cast<intptr_t>(literal);
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ return;
+ }
+
+ case Translation::DUPLICATED_OBJECT: {
+ int object_index = iterator->Next();
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset);
+ isolate_->heap()->arguments_marker()->ShortPrint();
+ PrintF(" ; duplicate of object #%d\n", object_index);
+ }
+ // Use the materialization marker value as a sentinel and fill in
+ // the object after the deoptimized frame is built.
+ intptr_t value = reinterpret_cast<intptr_t>(
+ isolate_->heap()->arguments_marker());
+ AddObjectDuplication(output_[frame_index]->GetTop() + output_offset,
+ object_index);
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ return;
+ }
+
+ case Translation::ARGUMENTS_OBJECT:
+ case Translation::CAPTURED_OBJECT: {
+ int length = iterator->Next();
+ bool is_args = opcode == Translation::ARGUMENTS_OBJECT;
+ if (trace_) {
+ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ",
+ output_[frame_index]->GetTop() + output_offset,
+ output_offset);
+ isolate_->heap()->arguments_marker()->ShortPrint();
+ PrintF(" ; object (length = %d, is_args = %d)\n", length, is_args);
+ }
+ // Use the materialization marker value as a sentinel and fill in
+ // the object after the deoptimized frame is built.
+ intptr_t value = reinterpret_cast<intptr_t>(
+ isolate_->heap()->arguments_marker());
+ AddObjectStart(output_[frame_index]->GetTop() + output_offset,
+ length, is_args);
+ output_[frame_index]->SetFrameSlot(output_offset, value);
+ // We save the object values on the side and materialize the actual
+ // object after the deoptimized frame is built.
+ int object_index = deferred_objects_.length() - 1;
+ for (int i = 0; i < length; i++) {
+ DoTranslateObject(iterator, object_index, i);
+ }
+ return;
+ }
+ }
+}
+
+
+bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
+ int* input_offset) {
+ disasm::NameConverter converter;
+ FrameDescription* output = output_[0];
+
+ // The input values are all part of the unoptimized frame so they
+ // are all tagged pointers.
+ uintptr_t input_value = input_->GetFrameSlot(*input_offset);
+ Object* input_object = reinterpret_cast<Object*>(input_value);
+
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator->Next());
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
+ case Translation::CONSTRUCT_STUB_FRAME:
+ case Translation::GETTER_STUB_FRAME:
+ case Translation::SETTER_STUB_FRAME:
+ case Translation::COMPILED_STUB_FRAME:
+ UNREACHABLE(); // Malformed input.
+ return false;
+
+ case Translation::REGISTER: {
+ int output_reg = iterator->Next();
+ if (FLAG_trace_osr) {
+ PrintF(" %s <- 0x%08" V8PRIxPTR " ; [sp + %d]\n",
+ converter.NameOfCPURegister(output_reg),
+ input_value,
+ *input_offset);
+ }
+ output->SetRegister(output_reg, input_value);
+ break;
+ }
+
+ case Translation::INT32_REGISTER: {
+ int32_t int32_value = 0;
+ if (!input_object->ToInt32(&int32_value)) return false;
+
+ int output_reg = iterator->Next();
+ if (FLAG_trace_osr) {
+ PrintF(" %s <- %d (int32) ; [sp + %d]\n",
+ converter.NameOfCPURegister(output_reg),
+ int32_value,
+ *input_offset);
+ }
+ output->SetRegister(output_reg, int32_value);
+ break;
+ }
+
+ case Translation::UINT32_REGISTER: {
+ uint32_t uint32_value = 0;
+ if (!input_object->ToUint32(&uint32_value)) return false;
+
+ int output_reg = iterator->Next();
+ if (FLAG_trace_osr) {
+ PrintF(" %s <- %u (uint32) ; [sp + %d]\n",
+ converter.NameOfCPURegister(output_reg),
+ uint32_value,
+ *input_offset);
+ }
+ output->SetRegister(output_reg, static_cast<int32_t>(uint32_value));
+ }
+
+
+ case Translation::DOUBLE_REGISTER: {
+ // Abort OSR if we don't have a number.
+ if (!input_object->IsNumber()) return false;
+
+ int output_reg = iterator->Next();
+ double double_value = input_object->Number();
+ if (FLAG_trace_osr) {
+ PrintF(" %s <- %g (double) ; [sp + %d]\n",
+ DoubleRegister::AllocationIndexToString(output_reg),
+ double_value,
+ *input_offset);
+ }
+ output->SetDoubleRegister(output_reg, double_value);
+ break;
+ }
+
+ case Translation::STACK_SLOT: {
+ int output_index = iterator->Next();
+ unsigned output_offset =
+ output->GetOffsetFromSlotIndex(output_index);
+ if (FLAG_trace_osr) {
+ 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;
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ int32_t int32_value = 0;
+ if (!input_object->ToInt32(&int32_value)) return false;
+
+ int output_index = iterator->Next();
+ unsigned output_offset =
+ output->GetOffsetFromSlotIndex(output_index);
+ if (FLAG_trace_osr) {
+ PrintF(" [sp + %d] <- %d (int32) ; [sp + %d]\n",
+ output_offset,
+ int32_value,
+ *input_offset);
+ }
+ output->SetFrameSlot(output_offset, int32_value);
+ break;
+ }
+
+ case Translation::UINT32_STACK_SLOT: {
+ uint32_t uint32_value = 0;
+ if (!input_object->ToUint32(&uint32_value)) return false;
+
+ int output_index = iterator->Next();
+ unsigned output_offset =
+ output->GetOffsetFromSlotIndex(output_index);
+ if (FLAG_trace_osr) {
+ PrintF(" [sp + %d] <- %u (uint32) ; [sp + %d]\n",
+ output_offset,
+ uint32_value,
+ *input_offset);
+ }
+ output->SetFrameSlot(output_offset, static_cast<int32_t>(uint32_value));
+ break;
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ static const int kLowerOffset = 0 * kPointerSize;
+ static const int kUpperOffset = 1 * kPointerSize;
+
+ // Abort OSR if we don't have a number.
+ if (!input_object->IsNumber()) return false;
+
+ int output_index = iterator->Next();
+ unsigned output_offset =
+ 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);
+ int32_t upper = static_cast<int32_t>(int_value >> kBitsPerInt);
+ if (FLAG_trace_osr) {
+ PrintF(" [sp + %d] <- 0x%08x (upper bits of %g) ; [sp + %d]\n",
+ output_offset + kUpperOffset,
+ upper,
+ double_value,
+ *input_offset);
+ PrintF(" [sp + %d] <- 0x%08x (lower bits of %g) ; [sp + %d]\n",
+ output_offset + kLowerOffset,
+ lower,
+ double_value,
+ *input_offset);
+ }
+ output->SetFrameSlot(output_offset + kLowerOffset, lower);
+ output->SetFrameSlot(output_offset + kUpperOffset, upper);
+ break;
+ }
+
+ case Translation::LITERAL: {
+ // Just ignore non-materialized literals.
+ iterator->Next();
+ break;
+ }
+
+ case Translation::DUPLICATED_OBJECT:
+ case Translation::ARGUMENTS_OBJECT:
+ case Translation::CAPTURED_OBJECT: {
+ // Optimized code assumes that the argument object has not been
+ // materialized and so bypasses it when doing arguments access.
+ // We should have bailed out before starting the frame
+ // translation.
+ UNREACHABLE();
+ return false;
+ }
+ }
+
+ *input_offset -= kPointerSize;
+ return true;
+}
+
+
+void Deoptimizer::PatchInterruptCode(Code* unoptimized_code,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ // Iterate over the back edge table and patch every interrupt
+ // call to an unconditional call to the replacement code.
+ int loop_nesting_level = unoptimized_code->allow_osr_at_loop_nesting_level();
+
+ for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized_code);
+ !back_edges.Done();
+ back_edges.Next()) {
+ if (static_cast<int>(back_edges.loop_depth()) == loop_nesting_level) {
+ PatchInterruptCodeAt(unoptimized_code,
+ back_edges.pc(),
+ interrupt_code,
+ replacement_code);
+ }
+ }
+
+ unoptimized_code->set_back_edges_patched_for_osr(true);
+#ifdef DEBUG
+ Deoptimizer::VerifyInterruptCode(
+ unoptimized_code, interrupt_code, replacement_code, loop_nesting_level);
+#endif // DEBUG
+}
+
+
+void Deoptimizer::RevertInterruptCode(Code* unoptimized_code,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ // Iterate over the back edge table and revert the patched interrupt calls.
+ ASSERT(unoptimized_code->back_edges_patched_for_osr());
+ int loop_nesting_level = unoptimized_code->allow_osr_at_loop_nesting_level();
+
+ for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized_code);
+ !back_edges.Done();
+ back_edges.Next()) {
+ if (static_cast<int>(back_edges.loop_depth()) <= loop_nesting_level) {
+ RevertInterruptCodeAt(unoptimized_code,
+ back_edges.pc(),
+ interrupt_code,
+ replacement_code);
+ }
+ }
+
+ unoptimized_code->set_back_edges_patched_for_osr(false);
+ unoptimized_code->set_allow_osr_at_loop_nesting_level(0);
+#ifdef DEBUG
+ // Assert that none of the back edges are patched anymore.
+ Deoptimizer::VerifyInterruptCode(
+ unoptimized_code, interrupt_code, replacement_code, -1);
+#endif // DEBUG
+}
+
+
+#ifdef DEBUG
+void Deoptimizer::VerifyInterruptCode(Code* unoptimized_code,
+ Code* interrupt_code,
+ Code* replacement_code,
+ int loop_nesting_level) {
+ for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized_code);
+ !back_edges.Done();
+ back_edges.Next()) {
+ uint32_t loop_depth = back_edges.loop_depth();
+ CHECK_LE(static_cast<int>(loop_depth), Code::kMaxLoopNestingMarker);
+ // Assert that all back edges for shallower loops (and only those)
+ // have already been patched.
+ CHECK_EQ((static_cast<int>(loop_depth) <= loop_nesting_level),
+ InterruptCodeIsPatched(unoptimized_code,
+ back_edges.pc(),
+ interrupt_code,
+ replacement_code));
+ }
+}
+#endif // DEBUG
+
+
+unsigned Deoptimizer::ComputeInputFrameSize() const {
+ unsigned fixed_size = ComputeFixedSize(function_);
+ // The fp-to-sp delta already takes the context and the function
+ // into account so we have to avoid double counting them (-2).
+ unsigned result = fixed_size + fp_to_sp_delta_ - (2 * kPointerSize);
+#ifdef DEBUG
+ if (bailout_type_ == OSR) {
+ // TODO(kasperl): It would be nice if we could verify that the
+ // size matches with the stack height we can compute based on the
+ // environment at the OSR entry. The code for that his built into
+ // the DoComputeOsrOutputFrame function for now.
+ } else if (compiled_code_->kind() == Code::OPTIMIZED_FUNCTION) {
+ unsigned stack_slots = compiled_code_->stack_slots();
+ unsigned outgoing_size = ComputeOutgoingArgumentSize();
+ ASSERT(result == fixed_size + (stack_slots * kPointerSize) + outgoing_size);
+ }
+#endif
+ return result;
+}
+
+
+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.
+ return ComputeIncomingArgumentSize(function) +
+ StandardFrameConstants::kFixedFrameSize;
+}
+
+
+unsigned Deoptimizer::ComputeIncomingArgumentSize(JSFunction* function) const {
+ // The incoming arguments is the values for formal parameters and
+ // the receiver. Every slot contains a pointer.
+ if (function->IsSmi()) {
+ ASSERT(Smi::cast(function) == Smi::FromInt(StackFrame::STUB));
+ return 0;
+ }
+ unsigned arguments = function->shared()->formal_parameter_count() + 1;
+ return arguments * kPointerSize;
+}
+
+
+unsigned Deoptimizer::ComputeOutgoingArgumentSize() const {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ compiled_code_->deoptimization_data());
+ unsigned height = data->ArgumentsStackHeight(bailout_id_)->value();
+ return height * kPointerSize;
+}
+
+
+Object* Deoptimizer::ComputeLiteral(int index) const {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ compiled_code_->deoptimization_data());
+ FixedArray* literals = data->LiteralArray();
+ return literals->get(index);
+}
+
+
+void Deoptimizer::AddObjectStart(intptr_t slot, int length, bool is_args) {
+ ObjectMaterializationDescriptor object_desc(
+ reinterpret_cast<Address>(slot), jsframe_count_, length, -1, is_args);
+ deferred_objects_.Add(object_desc);
+}
+
+
+void Deoptimizer::AddObjectDuplication(intptr_t slot, int object_index) {
+ ObjectMaterializationDescriptor object_desc(
+ reinterpret_cast<Address>(slot), jsframe_count_, -1, object_index, false);
+ deferred_objects_.Add(object_desc);
+}
+
+
+void Deoptimizer::AddObjectTaggedValue(intptr_t value) {
+ deferred_objects_tagged_values_.Add(reinterpret_cast<Object*>(value));
+ deferred_objects_double_values_.Add(isolate()->heap()->nan_value()->value());
+}
+
+
+void Deoptimizer::AddObjectDoubleValue(double value) {
+ deferred_objects_tagged_values_.Add(isolate()->heap()->the_hole_value());
+ deferred_objects_double_values_.Add(value);
+}
+
+
+void Deoptimizer::AddDoubleValue(intptr_t slot_address, double value) {
+ HeapNumberMaterializationDescriptor value_desc(
+ reinterpret_cast<Address>(slot_address), value);
+ deferred_heap_numbers_.Add(value_desc);
+}
+
+
+void Deoptimizer::EnsureCodeForDeoptimizationEntry(Isolate* isolate,
+ BailoutType type,
+ int max_entry_id) {
+ // 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
+ // isn't meant to be serialized at all.
+ ASSERT(type == EAGER || type == SOFT || type == LAZY);
+ DeoptimizerData* data = isolate->deoptimizer_data();
+ int entry_count = data->deopt_entry_code_entries_[type];
+ if (max_entry_id < entry_count) return;
+ entry_count = Max(entry_count, Deoptimizer::kMinNumberOfEntries);
+ while (max_entry_id >= entry_count) entry_count *= 2;
+ ASSERT(entry_count <= Deoptimizer::kMaxNumberOfEntries);
+
+ MacroAssembler masm(isolate, NULL, 16 * KB);
+ masm.set_emit_debug_code(false);
+ GenerateDeoptimizationEntries(&masm, entry_count, type);
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ MemoryChunk* chunk = data->deopt_entry_code_[type];
+ ASSERT(static_cast<int>(Deoptimizer::GetMaxDeoptTableSize()) >=
+ desc.instr_size);
+ chunk->CommitArea(desc.instr_size);
+ CopyBytes(chunk->area_start(), desc.buffer,
+ static_cast<size_t>(desc.instr_size));
+ CPU::FlushICache(chunk->area_start(), desc.instr_size);
+
+ data->deopt_entry_code_entries_[type] = entry_count;
+}
+
+
+FrameDescription::FrameDescription(uint32_t frame_size,
+ JSFunction* function)
+ : frame_size_(frame_size),
+ function_(function),
+ top_(kZapUint32),
+ pc_(kZapUint32),
+ fp_(kZapUint32),
+ context_(kZapUint32) {
+ // Zap all the registers.
+ for (int r = 0; r < Register::kNumRegisters; r++) {
+ SetRegister(r, kZapUint32);
+ }
+
+ // Zap all the slots.
+ for (unsigned o = 0; o < frame_size; o += kPointerSize) {
+ SetFrameSlot(o, kZapUint32);
+ }
+}
+
+
+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() - ComputeFixedSize();
+ return base - ((slot_index + 1) * kPointerSize);
+ } else {
+ // Incoming parameter.
+ int arg_size = (ComputeParametersCount() + 1) * kPointerSize;
+ unsigned base = GetFrameSize() - arg_size;
+ return base - ((slot_index + 1) * kPointerSize);
+ }
+}
+
+
+int FrameDescription::ComputeParametersCount() {
+ 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();
+ }
+ case StackFrame::STUB:
+ return -1; // Minus receiver.
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+}
+
+
+Object* FrameDescription::GetParameter(int index) {
+ ASSERT(index >= 0);
+ ASSERT(index < ComputeParametersCount());
+ // The slot indexes for incoming arguments are negative.
+ unsigned offset = GetOffsetFromSlotIndex(index - ComputeParametersCount());
+ return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset));
+}
+
+
+unsigned FrameDescription::GetExpressionCount() {
+ ASSERT_EQ(StackFrame::JAVA_SCRIPT, type_);
+ unsigned size = GetFrameSize() - ComputeFixedSize();
+ return size / kPointerSize;
+}
+
+
+Object* FrameDescription::GetExpression(int index) {
+ ASSERT_EQ(StackFrame::JAVA_SCRIPT, type_);
+ unsigned offset = GetOffsetFromSlotIndex(index);
+ return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset));
+}
+
+
+void TranslationBuffer::Add(int32_t value, Zone* zone) {
+ // Encode the sign bit in the least significant bit.
+ bool is_negative = (value < 0);
+ uint32_t bits = ((is_negative ? -value : value) << 1) |
+ static_cast<int32_t>(is_negative);
+ // Encode the individual bytes using the least significant bit of
+ // each byte to indicate whether or not more bytes follow.
+ do {
+ uint32_t next = bits >> 7;
+ contents_.Add(((bits << 1) & 0xFF) | (next != 0), zone);
+ bits = next;
+ } while (bits != 0);
+}
+
+
+int32_t TranslationIterator::Next() {
+ // Run through the bytes until we reach one with a least significant
+ // bit of zero (marks the end).
+ uint32_t bits = 0;
+ for (int i = 0; true; i += 7) {
+ ASSERT(HasNext());
+ uint8_t next = buffer_->get(index_++);
+ bits |= (next >> 1) << i;
+ if ((next & 1) == 0) break;
+ }
+ // The bits encode the sign in the least significant bit.
+ bool is_negative = (bits & 1) == 1;
+ int32_t result = bits >> 1;
+ return is_negative ? -result : result;
+}
+
+
+Handle<ByteArray> TranslationBuffer::CreateByteArray(Factory* factory) {
+ int length = contents_.length();
+ Handle<ByteArray> result = factory->NewByteArray(length, TENURED);
+ OS::MemCopy(
+ result->GetDataStartAddress(), contents_.ToVector().start(), length);
+ return result;
+}
+
+
+void Translation::BeginConstructStubFrame(int literal_id, unsigned height) {
+ buffer_->Add(CONSTRUCT_STUB_FRAME, zone());
+ buffer_->Add(literal_id, zone());
+ buffer_->Add(height, zone());
+}
+
+
+void Translation::BeginGetterStubFrame(int literal_id) {
+ buffer_->Add(GETTER_STUB_FRAME, zone());
+ buffer_->Add(literal_id, zone());
+}
+
+
+void Translation::BeginSetterStubFrame(int literal_id) {
+ buffer_->Add(SETTER_STUB_FRAME, zone());
+ buffer_->Add(literal_id, zone());
+}
+
+
+void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) {
+ buffer_->Add(ARGUMENTS_ADAPTOR_FRAME, zone());
+ buffer_->Add(literal_id, zone());
+ buffer_->Add(height, zone());
+}
+
+
+void Translation::BeginJSFrame(BailoutId node_id,
+ int literal_id,
+ unsigned height) {
+ buffer_->Add(JS_FRAME, zone());
+ buffer_->Add(node_id.ToInt(), zone());
+ buffer_->Add(literal_id, zone());
+ buffer_->Add(height, zone());
+}
+
+
+void Translation::BeginCompiledStubFrame() {
+ buffer_->Add(COMPILED_STUB_FRAME, zone());
+}
+
+
+void Translation::BeginArgumentsObject(int args_length) {
+ buffer_->Add(ARGUMENTS_OBJECT, zone());
+ buffer_->Add(args_length, zone());
+}
+
+
+void Translation::BeginCapturedObject(int length) {
+ buffer_->Add(CAPTURED_OBJECT, zone());
+ buffer_->Add(length, zone());
+}
+
+
+void Translation::DuplicateObject(int object_index) {
+ buffer_->Add(DUPLICATED_OBJECT, zone());
+ buffer_->Add(object_index, zone());
+}
+
+
+void Translation::StoreRegister(Register reg) {
+ buffer_->Add(REGISTER, zone());
+ buffer_->Add(reg.code(), zone());
+}
+
+
+void Translation::StoreInt32Register(Register reg) {
+ buffer_->Add(INT32_REGISTER, zone());
+ buffer_->Add(reg.code(), zone());
+}
+
+
+void Translation::StoreUint32Register(Register reg) {
+ buffer_->Add(UINT32_REGISTER, zone());
+ buffer_->Add(reg.code(), zone());
+}
+
+
+void Translation::StoreDoubleRegister(DoubleRegister reg) {
+ buffer_->Add(DOUBLE_REGISTER, zone());
+ buffer_->Add(DoubleRegister::ToAllocationIndex(reg), zone());
+}
+
+
+void Translation::StoreStackSlot(int index) {
+ buffer_->Add(STACK_SLOT, zone());
+ buffer_->Add(index, zone());
+}
+
+
+void Translation::StoreInt32StackSlot(int index) {
+ buffer_->Add(INT32_STACK_SLOT, zone());
+ buffer_->Add(index, zone());
+}
+
+
+void Translation::StoreUint32StackSlot(int index) {
+ buffer_->Add(UINT32_STACK_SLOT, zone());
+ buffer_->Add(index, zone());
+}
+
+
+void Translation::StoreDoubleStackSlot(int index) {
+ buffer_->Add(DOUBLE_STACK_SLOT, zone());
+ buffer_->Add(index, zone());
+}
+
+
+void Translation::StoreLiteral(int literal_id) {
+ buffer_->Add(LITERAL, zone());
+ buffer_->Add(literal_id, zone());
+}
+
+
+void Translation::StoreArgumentsObject(bool args_known,
+ int args_index,
+ int args_length) {
+ buffer_->Add(ARGUMENTS_OBJECT, zone());
+ buffer_->Add(args_known, zone());
+ buffer_->Add(args_index, zone());
+ buffer_->Add(args_length, zone());
+}
+
+
+int Translation::NumberOfOperandsFor(Opcode opcode) {
+ switch (opcode) {
+ case GETTER_STUB_FRAME:
+ case SETTER_STUB_FRAME:
+ case DUPLICATED_OBJECT:
+ case ARGUMENTS_OBJECT:
+ case CAPTURED_OBJECT:
+ case REGISTER:
+ case INT32_REGISTER:
+ case UINT32_REGISTER:
+ case DOUBLE_REGISTER:
+ case STACK_SLOT:
+ case INT32_STACK_SLOT:
+ case UINT32_STACK_SLOT:
+ case DOUBLE_STACK_SLOT:
+ case LITERAL:
+ case COMPILED_STUB_FRAME:
+ return 1;
+ case BEGIN:
+ case ARGUMENTS_ADAPTOR_FRAME:
+ case CONSTRUCT_STUB_FRAME:
+ return 2;
+ case JS_FRAME:
+ return 3;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+#if defined(OBJECT_PRINT) || defined(ENABLE_DISASSEMBLER)
+
+const char* Translation::StringFor(Opcode opcode) {
+ switch (opcode) {
+ case BEGIN:
+ return "BEGIN";
+ case JS_FRAME:
+ return "JS_FRAME";
+ case ARGUMENTS_ADAPTOR_FRAME:
+ return "ARGUMENTS_ADAPTOR_FRAME";
+ case CONSTRUCT_STUB_FRAME:
+ return "CONSTRUCT_STUB_FRAME";
+ case GETTER_STUB_FRAME:
+ return "GETTER_STUB_FRAME";
+ case SETTER_STUB_FRAME:
+ return "SETTER_STUB_FRAME";
+ case COMPILED_STUB_FRAME:
+ return "COMPILED_STUB_FRAME";
+ case REGISTER:
+ return "REGISTER";
+ case INT32_REGISTER:
+ return "INT32_REGISTER";
+ case UINT32_REGISTER:
+ return "UINT32_REGISTER";
+ case DOUBLE_REGISTER:
+ return "DOUBLE_REGISTER";
+ case STACK_SLOT:
+ return "STACK_SLOT";
+ case INT32_STACK_SLOT:
+ return "INT32_STACK_SLOT";
+ case UINT32_STACK_SLOT:
+ return "UINT32_STACK_SLOT";
+ case DOUBLE_STACK_SLOT:
+ return "DOUBLE_STACK_SLOT";
+ case LITERAL:
+ return "LITERAL";
+ case DUPLICATED_OBJECT:
+ return "DUPLICATED_OBJECT";
+ case ARGUMENTS_OBJECT:
+ return "ARGUMENTS_OBJECT";
+ case CAPTURED_OBJECT:
+ return "CAPTURED_OBJECT";
+ }
+ UNREACHABLE();
+ return "";
+}
+
+#endif
+
+
+DeoptimizingCodeListNode::DeoptimizingCodeListNode(Code* code): next_(NULL) {
+ GlobalHandles* global_handles = code->GetIsolate()->global_handles();
+ // Globalize the code object and make it weak.
+ code_ = Handle<Code>::cast(global_handles->Create(code));
+ global_handles->MakeWeak(reinterpret_cast<Object**>(code_.location()),
+ this,
+ Deoptimizer::HandleWeakDeoptimizedCode);
+}
+
+
+DeoptimizingCodeListNode::~DeoptimizingCodeListNode() {
+ GlobalHandles* global_handles = code_->GetIsolate()->global_handles();
+ global_handles->Destroy(reinterpret_cast<Object**>(code_.location()));
+}
+
+
+// We can't intermix stack decoding and allocations because
+// deoptimization infrastracture is not GC safe.
+// Thus we build a temporary structure in malloced space.
+SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
+ DeoptimizationInputData* data,
+ JavaScriptFrame* frame) {
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator->Next());
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
+ case Translation::CONSTRUCT_STUB_FRAME:
+ case Translation::GETTER_STUB_FRAME:
+ case Translation::SETTER_STUB_FRAME:
+ // Peeled off before getting here.
+ break;
+
+ case Translation::DUPLICATED_OBJECT:
+ case Translation::ARGUMENTS_OBJECT:
+ case Translation::CAPTURED_OBJECT:
+ // This can be only emitted for local slots not for argument slots.
+ break;
+
+ case Translation::REGISTER:
+ case Translation::INT32_REGISTER:
+ case Translation::UINT32_REGISTER:
+ case Translation::DOUBLE_REGISTER:
+ // We are at safepoint which corresponds to call. All registers are
+ // saved by caller so there would be no live registers at this
+ // point. Thus these translation commands should not be used.
+ break;
+
+ case Translation::STACK_SLOT: {
+ int slot_index = iterator->Next();
+ Address slot_addr = SlotAddress(frame, slot_index);
+ return SlotRef(slot_addr, SlotRef::TAGGED);
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ int slot_index = iterator->Next();
+ Address slot_addr = SlotAddress(frame, slot_index);
+ return SlotRef(slot_addr, SlotRef::INT32);
+ }
+
+ case Translation::UINT32_STACK_SLOT: {
+ int slot_index = iterator->Next();
+ Address slot_addr = SlotAddress(frame, slot_index);
+ return SlotRef(slot_addr, SlotRef::UINT32);
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ int slot_index = iterator->Next();
+ Address slot_addr = SlotAddress(frame, slot_index);
+ return SlotRef(slot_addr, SlotRef::DOUBLE);
+ }
+
+ case Translation::LITERAL: {
+ int literal_index = iterator->Next();
+ return SlotRef(data->GetIsolate(),
+ data->LiteralArray()->get(literal_index));
+ }
+
+ case Translation::COMPILED_STUB_FRAME:
+ UNREACHABLE();
+ break;
+ }
+
+ UNREACHABLE();
+ return SlotRef();
+}
+
+
+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) {
+ DisallowHeapAllocation no_gc;
+ int deopt_index = Safepoint::kNoDeoptimizationIndex;
+ DeoptimizationInputData* data =
+ static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index);
+ TranslationIterator it(data->TranslationByteArray(),
+ data->TranslationIndex(deopt_index)->value());
+ Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
+ ASSERT(opcode == Translation::BEGIN);
+ 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());
+ 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. 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;
+ }
+ 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,
+ bool has_arguments_adaptor,
+ bool has_construct_stub) {
+ FrameDescription* output_frame = deoptimizer->output_[frame_index];
+ function_ = output_frame->GetFunction();
+ has_construct_stub_ = has_construct_stub;
+ 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(deoptimizer->isolate()->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(i));
+ }
+}
+
+
+DeoptimizedFrameInfo::~DeoptimizedFrameInfo() {
+ delete[] expression_stack_;
+ delete[] parameters_;
+}
+
+
+void DeoptimizedFrameInfo::Iterate(ObjectVisitor* v) {
+ v->VisitPointer(BitCast<Object**>(&function_));
+ v->VisitPointers(parameters_, parameters_ + parameters_count_);
+ v->VisitPointers(expression_stack_, expression_stack_ + expression_count_);
+}
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/deoptimizer.h b/chromium/v8/src/deoptimizer.h
new file mode 100644
index 00000000000..b6e4667a20d
--- /dev/null
+++ b/chromium/v8/src/deoptimizer.h
@@ -0,0 +1,1012 @@
+// Copyright 2012 the V8 project authors. 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_DEOPTIMIZER_H_
+#define V8_DEOPTIMIZER_H_
+
+#include "v8.h"
+
+#include "allocation.h"
+#include "macro-assembler.h"
+#include "zone-inl.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+static inline double read_double_value(Address p) {
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ return Memory::double_at(p);
+#else // V8_HOST_CAN_READ_UNALIGNED
+ // Prevent gcc from using load-double (mips ldc1) on (possibly)
+ // non-64-bit aligned address.
+ union conversion {
+ double d;
+ uint32_t u[2];
+ } c;
+ c.u[0] = *reinterpret_cast<uint32_t*>(p);
+ c.u[1] = *reinterpret_cast<uint32_t*>(p + 4);
+ return c.d;
+#endif // V8_HOST_CAN_READ_UNALIGNED
+}
+
+
+class FrameDescription;
+class TranslationIterator;
+class DeoptimizingCodeListNode;
+class DeoptimizedFrameInfo;
+
+class HeapNumberMaterializationDescriptor BASE_EMBEDDED {
+ public:
+ HeapNumberMaterializationDescriptor(Address slot_address, double val)
+ : slot_address_(slot_address), val_(val) { }
+
+ Address slot_address() const { return slot_address_; }
+ double value() const { return val_; }
+
+ private:
+ Address slot_address_;
+ double val_;
+};
+
+
+class ObjectMaterializationDescriptor BASE_EMBEDDED {
+ public:
+ ObjectMaterializationDescriptor(
+ Address slot_address, int frame, int length, int duplicate, bool is_args)
+ : slot_address_(slot_address),
+ jsframe_index_(frame),
+ object_length_(length),
+ duplicate_object_(duplicate),
+ is_arguments_(is_args) { }
+
+ Address slot_address() const { return slot_address_; }
+ int jsframe_index() const { return jsframe_index_; }
+ int object_length() const { return object_length_; }
+ int duplicate_object() const { return duplicate_object_; }
+ bool is_arguments() const { return is_arguments_; }
+
+ // Only used for allocated receivers in DoComputeConstructStubFrame.
+ void patch_slot_address(intptr_t slot) {
+ slot_address_ = reinterpret_cast<Address>(slot);
+ }
+
+ private:
+ Address slot_address_;
+ int jsframe_index_;
+ int object_length_;
+ int duplicate_object_;
+ bool is_arguments_;
+};
+
+
+class OptimizedFunctionVisitor BASE_EMBEDDED {
+ public:
+ virtual ~OptimizedFunctionVisitor() {}
+
+ // Function which is called before iteration of any optimized functions
+ // from given native context.
+ virtual void EnterContext(Context* context) = 0;
+
+ virtual void VisitFunction(JSFunction* function) = 0;
+
+ // Function which is called after iteration of all optimized functions
+ // from given native context.
+ virtual void LeaveContext(Context* context) = 0;
+};
+
+
+class OptimizedFunctionFilter BASE_EMBEDDED {
+ public:
+ virtual ~OptimizedFunctionFilter() {}
+
+ virtual bool TakeFunction(JSFunction* function) = 0;
+};
+
+
+class Deoptimizer;
+
+
+class Deoptimizer : public Malloced {
+ public:
+ enum BailoutType {
+ EAGER,
+ LAZY,
+ SOFT,
+ OSR,
+ // This last bailout type is not really a bailout, but used by the
+ // debugger to deoptimize stack frames to allow inspection.
+ DEBUGGER
+ };
+
+ static const int kBailoutTypesWithCodeEntry = SOFT + 1;
+
+ struct JumpTableEntry {
+ inline JumpTableEntry(Address entry,
+ Deoptimizer::BailoutType type,
+ bool frame)
+ : label(),
+ address(entry),
+ bailout_type(type),
+ needs_frame(frame) { }
+ Label label;
+ Address address;
+ Deoptimizer::BailoutType bailout_type;
+ bool needs_frame;
+ };
+
+ static bool TraceEnabledFor(BailoutType deopt_type,
+ StackFrame::Type frame_type);
+ static const char* MessageFor(BailoutType type);
+
+ int output_count() const { return output_count_; }
+
+ Code::Kind compiled_code_kind() const { return compiled_code_->kind(); }
+
+ // 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,
+ Address from,
+ int fp_to_sp_delta,
+ Isolate* isolate);
+ static Deoptimizer* Grab(Isolate* isolate);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // 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 jsframe_index,
+ Isolate* isolate);
+ static void DeleteDebuggerInspectableFrame(DeoptimizedFrameInfo* info,
+ Isolate* isolate);
+#endif
+
+ // Makes sure that there is enough room in the relocation
+ // information of a code object to perform lazy deoptimization
+ // patching. If there is not enough room a new relocation
+ // information object is allocated and comments are added until it
+ // is big enough.
+ static void EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code);
+
+ // Deoptimize the function now. Its current optimized code will never be run
+ // again and any activations of the optimized code will get deoptimized when
+ // execution returns.
+ static void DeoptimizeFunction(JSFunction* function);
+
+ // Iterate over all the functions which share the same code object
+ // and make them use unoptimized version.
+ static void ReplaceCodeForRelatedFunctions(JSFunction* function, Code* code);
+
+ // Deoptimize all functions in the heap.
+ static void DeoptimizeAll(Isolate* isolate);
+
+ static void DeoptimizeGlobalObject(JSObject* object);
+
+ static void DeoptimizeAllFunctionsWith(Isolate* isolate,
+ OptimizedFunctionFilter* filter);
+
+ static void DeoptimizeCodeList(Isolate* isolate, ZoneList<Code*>* codes);
+
+ static void DeoptimizeAllFunctionsForContext(
+ Context* context, OptimizedFunctionFilter* filter);
+
+ static void VisitAllOptimizedFunctionsForContext(
+ Context* context, OptimizedFunctionVisitor* visitor);
+
+ static void VisitAllOptimizedFunctions(Isolate* isolate,
+ OptimizedFunctionVisitor* visitor);
+
+ // The size in bytes of the code required at a lazy deopt patch site.
+ static int patch_size();
+
+ // Patch all interrupts with allowed loop depth in the unoptimized code to
+ // unconditionally call replacement_code.
+ static void PatchInterruptCode(Code* unoptimized_code,
+ Code* interrupt_code,
+ Code* replacement_code);
+
+ // Patch the interrupt at the instruction before pc_after in
+ // the unoptimized code to unconditionally call replacement_code.
+ static void PatchInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code);
+
+ // Change all patched interrupts patched in the unoptimized code
+ // back to normal interrupts.
+ static void RevertInterruptCode(Code* unoptimized_code,
+ Code* interrupt_code,
+ Code* replacement_code);
+
+ // Change patched interrupt in the unoptimized code
+ // back to a normal interrupt.
+ static void RevertInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code);
+
+#ifdef DEBUG
+ static bool InterruptCodeIsPatched(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code);
+
+ // Verify that all back edges of a certain loop depth are patched.
+ static void VerifyInterruptCode(Code* unoptimized_code,
+ Code* interrupt_code,
+ Code* replacement_code,
+ int loop_nesting_level);
+#endif // DEBUG
+
+ ~Deoptimizer();
+
+ void MaterializeHeapObjects(JavaScriptFrameIterator* it);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ void MaterializeHeapNumbersForDebuggerInspectableFrame(
+ Address parameters_top,
+ uint32_t parameters_size,
+ Address expressions_top,
+ uint32_t expressions_size,
+ DeoptimizedFrameInfo* info);
+#endif
+
+ static void ComputeOutputFrames(Deoptimizer* deoptimizer);
+
+
+ enum GetEntryMode {
+ CALCULATE_ENTRY_ADDRESS,
+ ENSURE_ENTRY_CODE
+ };
+
+
+ static Address GetDeoptimizationEntry(
+ Isolate* isolate,
+ int id,
+ BailoutType type,
+ GetEntryMode mode = ENSURE_ENTRY_CODE);
+ static int GetDeoptimizationId(Isolate* isolate,
+ Address addr,
+ BailoutType type);
+ static int GetOutputInfo(DeoptimizationOutputData* data,
+ BailoutId node_id,
+ SharedFunctionInfo* shared);
+
+ // Code generation support.
+ static int input_offset() { return OFFSET_OF(Deoptimizer, input_); }
+ static int output_count_offset() {
+ return OFFSET_OF(Deoptimizer, output_count_);
+ }
+ static int output_offset() { return OFFSET_OF(Deoptimizer, output_); }
+
+ static int has_alignment_padding_offset() {
+ return OFFSET_OF(Deoptimizer, has_alignment_padding_);
+ }
+
+ static int GetDeoptimizedCodeCount(Isolate* isolate);
+
+ static const int kNotDeoptimizationEntry = -1;
+
+ // Generators for the deoptimization entry code.
+ class EntryGenerator BASE_EMBEDDED {
+ public:
+ EntryGenerator(MacroAssembler* masm, BailoutType type)
+ : masm_(masm), type_(type) { }
+ virtual ~EntryGenerator() { }
+
+ void Generate();
+
+ protected:
+ MacroAssembler* masm() const { return masm_; }
+ BailoutType type() const { return type_; }
+ Isolate* isolate() const { return masm_->isolate(); }
+
+ virtual void GeneratePrologue() { }
+
+ private:
+ MacroAssembler* masm_;
+ Deoptimizer::BailoutType type_;
+ };
+
+ class TableEntryGenerator : public EntryGenerator {
+ public:
+ TableEntryGenerator(MacroAssembler* masm, BailoutType type, int count)
+ : EntryGenerator(masm, type), count_(count) { }
+
+ protected:
+ virtual void GeneratePrologue();
+
+ private:
+ int count() const { return count_; }
+
+ int count_;
+ };
+
+ int ConvertJSFrameIndexToFrameIndex(int jsframe_index);
+
+ static size_t GetMaxDeoptTableSize();
+
+ static void EnsureCodeForDeoptimizationEntry(Isolate* isolate,
+ BailoutType type,
+ int max_entry_id);
+
+ Isolate* isolate() const { return isolate_; }
+
+ private:
+ static const int kMinNumberOfEntries = 64;
+ static const int kMaxNumberOfEntries = 16384;
+
+ Deoptimizer(Isolate* isolate,
+ JSFunction* function,
+ BailoutType type,
+ unsigned bailout_id,
+ Address from,
+ int fp_to_sp_delta,
+ Code* optimized_code);
+ Code* FindOptimizedCode(JSFunction* function, Code* optimized_code);
+ void PrintFunctionName();
+ void DeleteFrameDescriptions();
+
+ void DoComputeOutputFrames();
+ void DoComputeOsrOutputFrame();
+ void DoComputeJSFrame(TranslationIterator* iterator, int frame_index);
+ void DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
+ int frame_index);
+ void DoComputeConstructStubFrame(TranslationIterator* iterator,
+ int frame_index);
+ void DoComputeAccessorStubFrame(TranslationIterator* iterator,
+ int frame_index,
+ bool is_setter_stub_frame);
+ void DoComputeCompiledStubFrame(TranslationIterator* iterator,
+ int frame_index);
+
+ void DoTranslateObject(TranslationIterator* iterator,
+ int object_index,
+ int field_index);
+
+ enum DeoptimizerTranslatedValueType {
+ TRANSLATED_VALUE_IS_NATIVE,
+ TRANSLATED_VALUE_IS_TAGGED
+ };
+
+ void DoTranslateCommand(TranslationIterator* iterator,
+ int frame_index,
+ unsigned output_offset,
+ DeoptimizerTranslatedValueType value_type = TRANSLATED_VALUE_IS_TAGGED);
+
+ // Translate a command for OSR. Updates the input offset to be used for
+ // the next command. Returns false if translation of the command failed
+ // (e.g., a number conversion failed) and may or may not have updated the
+ // input offset.
+ bool DoOsrTranslateCommand(TranslationIterator* iterator,
+ int* input_offset);
+
+ unsigned ComputeInputFrameSize() const;
+ unsigned ComputeFixedSize(JSFunction* function) const;
+
+ unsigned ComputeIncomingArgumentSize(JSFunction* function) const;
+ unsigned ComputeOutgoingArgumentSize() const;
+
+ Object* ComputeLiteral(int index) const;
+
+ void AddObjectStart(intptr_t slot_address, int argc, bool is_arguments);
+ void AddObjectDuplication(intptr_t slot, int object_index);
+ void AddObjectTaggedValue(intptr_t value);
+ void AddObjectDoubleValue(double value);
+ void AddDoubleValue(intptr_t slot_address, double value);
+
+ bool ArgumentsObjectIsAdapted(int object_index) {
+ ObjectMaterializationDescriptor desc = deferred_objects_.at(object_index);
+ int reverse_jsframe_index = jsframe_count_ - desc.jsframe_index() - 1;
+ return jsframe_has_adapted_arguments_[reverse_jsframe_index];
+ }
+
+ Handle<JSFunction> ArgumentsObjectFunction(int object_index) {
+ ObjectMaterializationDescriptor desc = deferred_objects_.at(object_index);
+ int reverse_jsframe_index = jsframe_count_ - desc.jsframe_index() - 1;
+ return jsframe_functions_[reverse_jsframe_index];
+ }
+
+ // Helper function for heap object materialization.
+ Handle<Object> MaterializeNextHeapObject();
+ Handle<Object> MaterializeNextValue();
+
+ static void GenerateDeoptimizationEntries(
+ MacroAssembler* masm, int count, BailoutType type);
+
+ // Weak handle callback for deoptimizing code objects.
+ static void HandleWeakDeoptimizedCode(v8::Isolate* isolate,
+ v8::Persistent<v8::Value>* obj,
+ void* data);
+
+ // Deoptimize the given code and add to appropriate deoptimization lists.
+ static void DeoptimizeCode(Isolate* isolate, Code* code);
+
+ // Patch the given code so that it will deoptimize itself.
+ static void PatchCodeForDeoptimization(Isolate* isolate, Code* code);
+
+ // Fill the input from from a JavaScript frame. This is used when
+ // the debugger needs to inspect an optimized frame. For normal
+ // deoptimizations the input frame is filled in generated code.
+ void FillInputFrame(Address tos, JavaScriptFrame* frame);
+
+ // Fill the given output frame's registers to contain the failure handler
+ // address and the number of parameters for a stub failure trampoline.
+ void SetPlatformCompiledStubRegisters(FrameDescription* output_frame,
+ CodeStubInterfaceDescriptor* desc);
+
+ // Fill the given output frame's double registers with the original values
+ // from the input frame's double registers.
+ void CopyDoubleRegisters(FrameDescription* output_frame);
+
+ // Determines whether the input frame contains alignment padding by looking
+ // at the dynamic alignment state slot inside the frame.
+ bool HasAlignmentPadding(JSFunction* function);
+
+ Isolate* isolate_;
+ JSFunction* function_;
+ Code* compiled_code_;
+ unsigned bailout_id_;
+ BailoutType bailout_type_;
+ Address from_;
+ int fp_to_sp_delta_;
+ int has_alignment_padding_;
+
+ // Input frame description.
+ FrameDescription* input_;
+ // Number of output frames.
+ int output_count_;
+ // Number of output js frames.
+ int jsframe_count_;
+ // Array of output frame descriptions.
+ FrameDescription** output_;
+
+ // Deferred values to be materialized.
+ List<Object*> deferred_objects_tagged_values_;
+ List<double> deferred_objects_double_values_;
+ List<ObjectMaterializationDescriptor> deferred_objects_;
+ List<HeapNumberMaterializationDescriptor> deferred_heap_numbers_;
+
+ // Output frame information. Only used during heap object materialization.
+ List<Handle<JSFunction> > jsframe_functions_;
+ List<bool> jsframe_has_adapted_arguments_;
+
+ // Materialized objects. Only used during heap object materialization.
+ List<Handle<Object> >* materialized_values_;
+ List<Handle<Object> >* materialized_objects_;
+ int materialization_value_index_;
+ int materialization_object_index_;
+
+#ifdef DEBUG
+ DisallowHeapAllocation* disallow_heap_allocation_;
+#endif // DEBUG
+
+ bool trace_;
+
+ static const int table_entry_size_;
+
+ friend class FrameDescription;
+ friend class DeoptimizingCodeListNode;
+ friend class DeoptimizedFrameInfo;
+};
+
+
+class FrameDescription {
+ public:
+ FrameDescription(uint32_t frame_size,
+ JSFunction* function);
+
+ void* operator new(size_t size, uint32_t frame_size) {
+ // Subtracts kPointerSize, as the member frame_content_ already supplies
+ // the first element of the area to store the frame.
+ return malloc(size + frame_size - kPointerSize);
+ }
+
+ void operator delete(void* pointer, uint32_t frame_size) {
+ free(pointer);
+ }
+
+ void operator delete(void* description) {
+ free(description);
+ }
+
+ uint32_t GetFrameSize() const {
+ ASSERT(static_cast<uint32_t>(frame_size_) == frame_size_);
+ return static_cast<uint32_t>(frame_size_);
+ }
+
+ JSFunction* GetFunction() const { return function_; }
+
+ unsigned GetOffsetFromSlotIndex(int slot_index);
+
+ intptr_t GetFrameSlot(unsigned offset) {
+ return *GetFrameSlotPointer(offset);
+ }
+
+ double GetDoubleFrameSlot(unsigned offset) {
+ intptr_t* ptr = GetFrameSlotPointer(offset);
+ return read_double_value(reinterpret_cast<Address>(ptr));
+ }
+
+ void SetFrameSlot(unsigned offset, intptr_t value) {
+ *GetFrameSlotPointer(offset) = value;
+ }
+
+ void SetCallerPc(unsigned offset, intptr_t value);
+
+ void SetCallerFp(unsigned offset, intptr_t value);
+
+ intptr_t GetRegister(unsigned n) const {
+ ASSERT(n < ARRAY_SIZE(registers_));
+ return registers_[n];
+ }
+
+ double GetDoubleRegister(unsigned n) const {
+ ASSERT(n < ARRAY_SIZE(double_registers_));
+ return double_registers_[n];
+ }
+
+ void SetRegister(unsigned n, intptr_t value) {
+ ASSERT(n < ARRAY_SIZE(registers_));
+ registers_[n] = value;
+ }
+
+ void SetDoubleRegister(unsigned n, double value) {
+ ASSERT(n < ARRAY_SIZE(double_registers_));
+ double_registers_[n] = value;
+ }
+
+ intptr_t GetTop() const { return top_; }
+ void SetTop(intptr_t top) { top_ = top; }
+
+ intptr_t GetPc() const { return pc_; }
+ void SetPc(intptr_t pc) { pc_ = pc; }
+
+ intptr_t GetFp() const { return fp_; }
+ void SetFp(intptr_t fp) { fp_ = fp; }
+
+ intptr_t GetContext() const { return context_; }
+ void SetContext(intptr_t context) { context_ = context; }
+
+ Smi* GetState() const { return state_; }
+ void SetState(Smi* state) { state_ = state; }
+
+ void SetContinuation(intptr_t pc) { continuation_ = pc; }
+
+ 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(int index);
+
+ // Get the expression stack height for a unoptimized frame.
+ unsigned GetExpressionCount();
+
+ // Get the expression stack value for an unoptimized frame.
+ Object* GetExpression(int index);
+
+ static int registers_offset() {
+ return OFFSET_OF(FrameDescription, registers_);
+ }
+
+ static int double_registers_offset() {
+ return OFFSET_OF(FrameDescription, double_registers_);
+ }
+
+ static int frame_size_offset() {
+ return OFFSET_OF(FrameDescription, frame_size_);
+ }
+
+ static int pc_offset() {
+ return OFFSET_OF(FrameDescription, pc_);
+ }
+
+ static int state_offset() {
+ return OFFSET_OF(FrameDescription, state_);
+ }
+
+ static int continuation_offset() {
+ return OFFSET_OF(FrameDescription, continuation_);
+ }
+
+ static int frame_content_offset() {
+ return OFFSET_OF(FrameDescription, frame_content_);
+ }
+
+ private:
+ static const uint32_t kZapUint32 = 0xbeeddead;
+
+ // Frame_size_ must hold a uint32_t value. It is only a uintptr_t to
+ // keep the variable-size array frame_content_ of type intptr_t at
+ // the end of the structure aligned.
+ uintptr_t frame_size_; // Number of bytes.
+ JSFunction* function_;
+ intptr_t registers_[Register::kNumRegisters];
+ double double_registers_[DoubleRegister::kMaxNumRegisters];
+ intptr_t top_;
+ intptr_t pc_;
+ intptr_t fp_;
+ intptr_t context_;
+ StackFrame::Type type_;
+ Smi* state_;
+
+ // Continuation is the PC where the execution continues after
+ // deoptimizing.
+ intptr_t continuation_;
+
+ // This must be at the end of the object as the object is allocated larger
+ // than it's definition indicate to extend this array.
+ intptr_t frame_content_[1];
+
+ intptr_t* GetFrameSlotPointer(unsigned offset) {
+ ASSERT(offset < frame_size_);
+ return reinterpret_cast<intptr_t*>(
+ reinterpret_cast<Address>(this) + frame_content_offset() + offset);
+ }
+
+ int ComputeFixedSize();
+};
+
+
+class DeoptimizerData {
+ public:
+ explicit DeoptimizerData(MemoryAllocator* allocator);
+ ~DeoptimizerData();
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ void Iterate(ObjectVisitor* v);
+#endif
+
+ Code* FindDeoptimizingCode(Address addr);
+ void RemoveDeoptimizingCode(Code* code);
+
+ private:
+ MemoryAllocator* allocator_;
+ int deopt_entry_code_entries_[Deoptimizer::kBailoutTypesWithCodeEntry];
+ MemoryChunk* deopt_entry_code_[Deoptimizer::kBailoutTypesWithCodeEntry];
+ Deoptimizer* current_;
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ DeoptimizedFrameInfo* deoptimized_frame_info_;
+#endif
+
+ // List of deoptimized code which still have references from active stack
+ // frames. These code objects are needed by the deoptimizer when deoptimizing
+ // a frame for which the code object for the function function has been
+ // changed from the code present when deoptimizing was done.
+ DeoptimizingCodeListNode* deoptimizing_code_list_;
+
+ friend class Deoptimizer;
+
+ DISALLOW_COPY_AND_ASSIGN(DeoptimizerData);
+};
+
+
+class TranslationBuffer BASE_EMBEDDED {
+ public:
+ explicit TranslationBuffer(Zone* zone) : contents_(256, zone) { }
+
+ int CurrentIndex() const { return contents_.length(); }
+ void Add(int32_t value, Zone* zone);
+
+ Handle<ByteArray> CreateByteArray(Factory* factory);
+
+ private:
+ ZoneList<uint8_t> contents_;
+};
+
+
+class TranslationIterator BASE_EMBEDDED {
+ public:
+ TranslationIterator(ByteArray* buffer, int index)
+ : buffer_(buffer), index_(index) {
+ ASSERT(index >= 0 && index < buffer->length());
+ }
+
+ int32_t Next();
+
+ bool HasNext() const { return index_ < buffer_->length(); }
+
+ void Skip(int n) {
+ for (int i = 0; i < n; i++) Next();
+ }
+
+ private:
+ ByteArray* buffer_;
+ int index_;
+};
+
+
+class Translation BASE_EMBEDDED {
+ public:
+ enum Opcode {
+ BEGIN,
+ JS_FRAME,
+ CONSTRUCT_STUB_FRAME,
+ GETTER_STUB_FRAME,
+ SETTER_STUB_FRAME,
+ ARGUMENTS_ADAPTOR_FRAME,
+ COMPILED_STUB_FRAME,
+ DUPLICATED_OBJECT,
+ ARGUMENTS_OBJECT,
+ CAPTURED_OBJECT,
+ REGISTER,
+ INT32_REGISTER,
+ UINT32_REGISTER,
+ DOUBLE_REGISTER,
+ STACK_SLOT,
+ INT32_STACK_SLOT,
+ UINT32_STACK_SLOT,
+ DOUBLE_STACK_SLOT,
+ LITERAL
+ };
+
+ Translation(TranslationBuffer* buffer, int frame_count, int jsframe_count,
+ Zone* zone)
+ : buffer_(buffer),
+ index_(buffer->CurrentIndex()),
+ zone_(zone) {
+ buffer_->Add(BEGIN, zone);
+ buffer_->Add(frame_count, zone);
+ buffer_->Add(jsframe_count, zone);
+ }
+
+ int index() const { return index_; }
+
+ // Commands.
+ void BeginJSFrame(BailoutId node_id, int literal_id, unsigned height);
+ void BeginCompiledStubFrame();
+ void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
+ void BeginConstructStubFrame(int literal_id, unsigned height);
+ void BeginGetterStubFrame(int literal_id);
+ void BeginSetterStubFrame(int literal_id);
+ void BeginArgumentsObject(int args_length);
+ void BeginCapturedObject(int length);
+ void DuplicateObject(int object_index);
+ void StoreRegister(Register reg);
+ void StoreInt32Register(Register reg);
+ void StoreUint32Register(Register reg);
+ void StoreDoubleRegister(DoubleRegister reg);
+ void StoreStackSlot(int index);
+ void StoreInt32StackSlot(int index);
+ void StoreUint32StackSlot(int index);
+ void StoreDoubleStackSlot(int index);
+ void StoreLiteral(int literal_id);
+ void StoreArgumentsObject(bool args_known, int args_index, int args_length);
+
+ Zone* zone() const { return zone_; }
+
+ static int NumberOfOperandsFor(Opcode opcode);
+
+#if defined(OBJECT_PRINT) || defined(ENABLE_DISASSEMBLER)
+ static const char* StringFor(Opcode opcode);
+#endif
+
+ // A literal id which refers to the JSFunction itself.
+ static const int kSelfLiteralId = -239;
+
+ private:
+ TranslationBuffer* buffer_;
+ int index_;
+ Zone* zone_;
+};
+
+
+// Linked list holding deoptimizing code objects. The deoptimizing code objects
+// are kept as weak handles until they are no longer activated on the stack.
+class DeoptimizingCodeListNode : public Malloced {
+ public:
+ explicit DeoptimizingCodeListNode(Code* code);
+ ~DeoptimizingCodeListNode();
+
+ DeoptimizingCodeListNode* next() const { return next_; }
+ void set_next(DeoptimizingCodeListNode* next) { next_ = next; }
+ Handle<Code> code() const { return code_; }
+
+ private:
+ // Global (weak) handle to the deoptimizing code object.
+ Handle<Code> code_;
+
+ // Next pointer for linked list.
+ DeoptimizingCodeListNode* next_;
+};
+
+
+class SlotRef BASE_EMBEDDED {
+ public:
+ enum SlotRepresentation {
+ UNKNOWN,
+ TAGGED,
+ INT32,
+ UINT32,
+ DOUBLE,
+ LITERAL
+ };
+
+ SlotRef()
+ : addr_(NULL), representation_(UNKNOWN) { }
+
+ SlotRef(Address addr, SlotRepresentation representation)
+ : addr_(addr), representation_(representation) { }
+
+ SlotRef(Isolate* isolate, Object* literal)
+ : literal_(literal, isolate), representation_(LITERAL) { }
+
+ Handle<Object> GetValue(Isolate* isolate) {
+ switch (representation_) {
+ case TAGGED:
+ return Handle<Object>(Memory::Object_at(addr_), isolate);
+
+ case INT32: {
+ int value = Memory::int32_at(addr_);
+ if (Smi::IsValid(value)) {
+ return Handle<Object>(Smi::FromInt(value), isolate);
+ } else {
+ return isolate->factory()->NewNumberFromInt(value);
+ }
+ }
+
+ case UINT32: {
+ uint32_t value = Memory::uint32_at(addr_);
+ if (value <= static_cast<uint32_t>(Smi::kMaxValue)) {
+ return Handle<Object>(Smi::FromInt(static_cast<int>(value)), isolate);
+ } else {
+ return isolate->factory()->NewNumber(static_cast<double>(value));
+ }
+ }
+
+ case DOUBLE: {
+ double value = read_double_value(addr_);
+ return isolate->factory()->NewNumber(value);
+ }
+
+ case LITERAL:
+ return literal_;
+
+ default:
+ UNREACHABLE();
+ return Handle<Object>::null();
+ }
+ }
+
+ static Vector<SlotRef> ComputeSlotMappingForArguments(
+ JavaScriptFrame* frame,
+ int inlined_frame_index,
+ int formal_parameter_count);
+
+ private:
+ Address addr_;
+ Handle<Object> literal_;
+ SlotRepresentation representation_;
+
+ static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
+ if (slot_index >= 0) {
+ const int offset = JavaScriptFrameConstants::kLocal0Offset;
+ return frame->fp() + offset - (slot_index * kPointerSize);
+ } else {
+ const int offset = JavaScriptFrameConstants::kLastParameterOffset;
+ return frame->fp() + offset - ((slot_index + 1) * kPointerSize);
+ }
+ }
+
+ static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
+ DeoptimizationInputData* data,
+ JavaScriptFrame* frame);
+
+ static void ComputeSlotsForArguments(
+ Vector<SlotRef>* args_slots,
+ TranslationIterator* iterator,
+ DeoptimizationInputData* data,
+ JavaScriptFrame* frame);
+};
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+// Class used to represent an unoptimized frame when the debugger
+// 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,
+ bool has_arguments_adaptor,
+ bool has_construct_stub);
+ virtual ~DeoptimizedFrameInfo();
+
+ // GC support.
+ void Iterate(ObjectVisitor* v);
+
+ // Return the number of incoming arguments.
+ int parameters_count() { return parameters_count_; }
+
+ // Return the height of the expression stack.
+ int expression_count() { return expression_count_; }
+
+ // Get the frame function.
+ JSFunction* GetFunction() {
+ return function_;
+ }
+
+ // Check if this frame is preceded by construct stub frame. The bottom-most
+ // inlined frame might still be called by an uninlined construct stub.
+ bool HasConstructStub() {
+ return has_construct_stub_;
+ }
+
+ // Get an incoming argument.
+ Object* GetParameter(int index) {
+ ASSERT(0 <= index && index < parameters_count());
+ return parameters_[index];
+ }
+
+ // Get an expression from the expression stack.
+ Object* GetExpression(int index) {
+ ASSERT(0 <= index && index < expression_count());
+ return expression_stack_[index];
+ }
+
+ int GetSourcePosition() {
+ return source_position_;
+ }
+
+ private:
+ // Set an incoming argument.
+ void SetParameter(int index, Object* obj) {
+ ASSERT(0 <= index && index < parameters_count());
+ parameters_[index] = obj;
+ }
+
+ // Set an expression on the expression stack.
+ void SetExpression(int index, Object* obj) {
+ ASSERT(0 <= index && index < expression_count());
+ expression_stack_[index] = obj;
+ }
+
+ JSFunction* function_;
+ bool has_construct_stub_;
+ int parameters_count_;
+ int expression_count_;
+ Object** parameters_;
+ Object** expression_stack_;
+ int source_position_;
+
+ friend class Deoptimizer;
+};
+#endif
+
+} } // namespace v8::internal
+
+#endif // V8_DEOPTIMIZER_H_
diff --git a/chromium/v8/src/disasm.h b/chromium/v8/src/disasm.h
new file mode 100644
index 00000000000..f7f2d412028
--- /dev/null
+++ b/chromium/v8/src/disasm.h
@@ -0,0 +1,80 @@
+// Copyright 2007-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_DISASM_H_
+#define V8_DISASM_H_
+
+namespace disasm {
+
+typedef unsigned char byte;
+
+// Interface and default implementation for converting addresses and
+// register-numbers to text. The default implementation is machine
+// specific.
+class NameConverter {
+ public:
+ virtual ~NameConverter() {}
+ virtual const char* NameOfCPURegister(int reg) const;
+ virtual const char* NameOfByteCPURegister(int reg) const;
+ virtual const char* NameOfXMMRegister(int reg) const;
+ virtual const char* NameOfAddress(byte* addr) const;
+ virtual const char* NameOfConstant(byte* addr) const;
+ virtual const char* NameInCode(byte* addr) const;
+
+ protected:
+ v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
+};
+
+
+// A generic Disassembler interface
+class Disassembler {
+ public:
+ // Caller deallocates converter.
+ explicit Disassembler(const NameConverter& converter);
+
+ virtual ~Disassembler();
+
+ // Writes one disassembled instruction into 'buffer' (0-terminated).
+ // Returns the length of the disassembled machine instruction in bytes.
+ int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
+
+ // Returns -1 if instruction does not mark the beginning of a constant pool,
+ // or the number of entries in the constant pool beginning here.
+ int ConstantPoolSizeAt(byte* instruction);
+
+ // Write disassembly into specified file 'f' using specified NameConverter
+ // (see constructor).
+ static void Disassemble(FILE* f, byte* begin, byte* end);
+ private:
+ const NameConverter& converter_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Disassembler);
+};
+
+} // namespace disasm
+
+#endif // V8_DISASM_H_
diff --git a/chromium/v8/src/disassembler.cc b/chromium/v8/src/disassembler.cc
new file mode 100644
index 00000000000..fa8ae1ffc8f
--- /dev/null
+++ b/chromium/v8/src/disassembler.cc
@@ -0,0 +1,369 @@
+// 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 "code-stubs.h"
+#include "codegen.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "disasm.h"
+#include "disassembler.h"
+#include "macro-assembler.h"
+#include "serialize.h"
+#include "string-stream.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef ENABLE_DISASSEMBLER
+
+void Disassembler::Dump(FILE* f, byte* begin, byte* end) {
+ for (byte* pc = begin; pc < end; pc++) {
+ if (f == NULL) {
+ PrintF("%" V8PRIxPTR " %4" V8PRIdPTR " %02x\n",
+ reinterpret_cast<intptr_t>(pc),
+ pc - begin,
+ *pc);
+ } else {
+ PrintF(f, "%" V8PRIxPTR " %4" V8PRIdPTR " %02x\n",
+ reinterpret_cast<uintptr_t>(pc), pc - begin, *pc);
+ }
+ }
+}
+
+
+class V8NameConverter: public disasm::NameConverter {
+ public:
+ explicit V8NameConverter(Code* code) : code_(code) {}
+ virtual const char* NameOfAddress(byte* pc) const;
+ virtual const char* NameInCode(byte* addr) const;
+ Code* code() const { return code_; }
+ private:
+ Code* code_;
+
+ EmbeddedVector<char, 128> v8_buffer_;
+};
+
+
+const char* V8NameConverter::NameOfAddress(byte* pc) const {
+ const char* name = Isolate::Current()->builtins()->Lookup(pc);
+ if (name != NULL) {
+ OS::SNPrintF(v8_buffer_, "%s (%p)", name, pc);
+ return v8_buffer_.start();
+ }
+
+ if (code_ != NULL) {
+ int offs = static_cast<int>(pc - code_->instruction_start());
+ // print as code offset, if it seems reasonable
+ if (0 <= offs && offs < code_->instruction_size()) {
+ OS::SNPrintF(v8_buffer_, "%d (%p)", offs, pc);
+ return v8_buffer_.start();
+ }
+ }
+
+ return disasm::NameConverter::NameOfAddress(pc);
+}
+
+
+const char* V8NameConverter::NameInCode(byte* addr) const {
+ // The V8NameConverter is used for well known code, so we can "safely"
+ // dereference pointers in generated code.
+ return (code_ != NULL) ? reinterpret_cast<const char*>(addr) : "";
+}
+
+
+static void DumpBuffer(FILE* f, StringBuilder* out) {
+ if (f == NULL) {
+ PrintF("%s\n", out->Finalize());
+ } else {
+ PrintF(f, "%s\n", out->Finalize());
+ }
+ out->Reset();
+}
+
+
+static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength;
+static const int kRelocInfoPosition = 57;
+
+static int DecodeIt(Isolate* isolate,
+ FILE* f,
+ const V8NameConverter& converter,
+ byte* begin,
+ byte* end) {
+ SealHandleScope shs(isolate);
+ DisallowHeapAllocation no_alloc;
+ ExternalReferenceEncoder ref_encoder;
+ Heap* heap = HEAP;
+
+ v8::internal::EmbeddedVector<char, 128> decode_buffer;
+ v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
+ StringBuilder out(out_buffer.start(), out_buffer.length());
+ byte* pc = begin;
+ disasm::Disassembler d(converter);
+ RelocIterator* it = NULL;
+ if (converter.code() != NULL) {
+ it = new RelocIterator(converter.code());
+ } else {
+ // No relocation information when printing code stubs.
+ }
+ int constants = -1; // no constants being decoded at the start
+
+ while (pc < end) {
+ // First decode instruction so that we know its length.
+ byte* prev_pc = pc;
+ if (constants > 0) {
+ OS::SNPrintF(decode_buffer,
+ "%08x constant",
+ *reinterpret_cast<int32_t*>(pc));
+ constants--;
+ pc += 4;
+ } else {
+ int num_const = d.ConstantPoolSizeAt(pc);
+ if (num_const >= 0) {
+ OS::SNPrintF(decode_buffer,
+ "%08x constant pool begin",
+ *reinterpret_cast<int32_t*>(pc));
+ constants = num_const;
+ pc += 4;
+ } else if (it != NULL && !it->done() && it->rinfo()->pc() == pc &&
+ it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
+ // raw pointer embedded in code stream, e.g., jump table
+ byte* ptr = *reinterpret_cast<byte**>(pc);
+ OS::SNPrintF(decode_buffer,
+ "%08" V8PRIxPTR " jump table entry %4" V8PRIdPTR,
+ ptr,
+ ptr - begin);
+ pc += 4;
+ } else {
+ decode_buffer[0] = '\0';
+ pc += d.InstructionDecode(decode_buffer, pc);
+ }
+ }
+
+ // Collect RelocInfo for this instruction (prev_pc .. pc-1)
+ List<const char*> comments(4);
+ List<byte*> pcs(1);
+ List<RelocInfo::Mode> rmodes(1);
+ List<intptr_t> datas(1);
+ if (it != NULL) {
+ while (!it->done() && it->rinfo()->pc() < pc) {
+ if (RelocInfo::IsComment(it->rinfo()->rmode())) {
+ // For comments just collect the text.
+ comments.Add(reinterpret_cast<const char*>(it->rinfo()->data()));
+ } else {
+ // For other reloc info collect all data.
+ pcs.Add(it->rinfo()->pc());
+ rmodes.Add(it->rinfo()->rmode());
+ datas.Add(it->rinfo()->data());
+ }
+ it->next();
+ }
+ }
+
+ // Comments.
+ for (int i = 0; i < comments.length(); i++) {
+ out.AddFormatted(" %s", comments[i]);
+ DumpBuffer(f, &out);
+ }
+
+ // Instruction address and instruction offset.
+ out.AddFormatted("%p %4d ", prev_pc, prev_pc - begin);
+
+ // Instruction.
+ out.AddFormatted("%s", decode_buffer.start());
+
+ // 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], NULL);
+
+ // Indent the printing of the reloc info.
+ if (i == 0) {
+ // The first reloc info is printed after the disassembled instruction.
+ out.AddPadding(' ', kRelocInfoPosition - out.position());
+ } else {
+ // Additional reloc infos are printed on separate lines.
+ DumpBuffer(f, &out);
+ out.AddPadding(' ', kRelocInfoPosition);
+ }
+
+ RelocInfo::Mode rmode = relocinfo.rmode();
+ if (RelocInfo::IsPosition(rmode)) {
+ if (RelocInfo::IsStatementPosition(rmode)) {
+ out.AddFormatted(" ;; debug: statement %d", relocinfo.data());
+ } else {
+ out.AddFormatted(" ;; debug: position %d", relocinfo.data());
+ }
+ } else if (rmode == RelocInfo::EMBEDDED_OBJECT) {
+ HeapStringAllocator allocator;
+ StringStream accumulator(&allocator);
+ relocinfo.target_object()->ShortPrint(&accumulator);
+ SmartArrayPointer<const char> obj_name = accumulator.ToCString();
+ out.AddFormatted(" ;; object: %s", *obj_name);
+ } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
+ const char* reference_name =
+ ref_encoder.NameOfAddress(*relocinfo.target_reference_address());
+ out.AddFormatted(" ;; external reference (%s)", reference_name);
+ } else if (RelocInfo::IsCodeTarget(rmode)) {
+ out.AddFormatted(" ;; code:");
+ if (rmode == RelocInfo::CONSTRUCT_CALL) {
+ out.AddFormatted(" constructor,");
+ }
+ Code* code = Code::GetCodeFromTargetAddress(relocinfo.target_address());
+ Code::Kind kind = code->kind();
+ if (code->is_inline_cache_stub()) {
+ if (rmode == RelocInfo::CODE_TARGET_CONTEXT) {
+ out.AddFormatted(" contextual,");
+ }
+ InlineCacheState ic_state = code->ic_state();
+ out.AddFormatted(" %s, %s", Code::Kind2String(kind),
+ Code::ICState2String(ic_state));
+ if (ic_state == MONOMORPHIC) {
+ Code::StubType type = code->type();
+ out.AddFormatted(", %s", Code::StubType2String(type));
+ }
+ if (kind == Code::CALL_IC || kind == Code::KEYED_CALL_IC) {
+ out.AddFormatted(", argc = %d", code->arguments_count());
+ }
+ } else if (kind == Code::STUB) {
+ // Reverse lookup required as the minor key cannot be retrieved
+ // from the code object.
+ Object* obj = heap->code_stubs()->SlowReverseLookup(code);
+ if (obj != heap->undefined_value()) {
+ ASSERT(obj->IsSmi());
+ // Get the STUB key and extract major and minor key.
+ uint32_t key = Smi::cast(obj)->value();
+ uint32_t minor_key = CodeStub::MinorKeyFromKey(key);
+ CodeStub::Major major_key = CodeStub::GetMajorKey(code);
+ ASSERT(major_key == CodeStub::MajorKeyFromKey(key));
+ out.AddFormatted(" %s, %s, ",
+ Code::Kind2String(kind),
+ CodeStub::MajorName(major_key, false));
+ switch (major_key) {
+ case CodeStub::CallFunction: {
+ int argc =
+ CallFunctionStub::ExtractArgcFromMinorKey(minor_key);
+ out.AddFormatted("argc = %d", argc);
+ break;
+ }
+ default:
+ out.AddFormatted("minor: %d", minor_key);
+ }
+ }
+ } else {
+ out.AddFormatted(" %s", Code::Kind2String(kind));
+ }
+ if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
+ out.AddFormatted(" (id = %d)", static_cast<int>(relocinfo.data()));
+ }
+ } else if (RelocInfo::IsRuntimeEntry(rmode) &&
+ isolate->deoptimizer_data() != NULL) {
+ // A runtime entry reloinfo might be a deoptimization bailout.
+ Address addr = relocinfo.target_address();
+ int id = Deoptimizer::GetDeoptimizationId(isolate,
+ addr,
+ Deoptimizer::EAGER);
+ if (id == Deoptimizer::kNotDeoptimizationEntry) {
+ id = Deoptimizer::GetDeoptimizationId(isolate,
+ addr,
+ Deoptimizer::LAZY);
+ if (id == Deoptimizer::kNotDeoptimizationEntry) {
+ id = Deoptimizer::GetDeoptimizationId(isolate,
+ addr,
+ Deoptimizer::SOFT);
+ if (id == Deoptimizer::kNotDeoptimizationEntry) {
+ out.AddFormatted(" ;; %s", RelocInfo::RelocModeName(rmode));
+ } else {
+ out.AddFormatted(" ;; soft deoptimization bailout %d", id);
+ }
+ } else {
+ out.AddFormatted(" ;; lazy deoptimization bailout %d", id);
+ }
+ } else {
+ out.AddFormatted(" ;; deoptimization bailout %d", id);
+ }
+ } else {
+ out.AddFormatted(" ;; %s", RelocInfo::RelocModeName(rmode));
+ }
+ }
+ DumpBuffer(f, &out);
+ }
+
+ // Emit comments following the last instruction (if any).
+ if (it != NULL) {
+ for ( ; !it->done(); it->next()) {
+ if (RelocInfo::IsComment(it->rinfo()->rmode())) {
+ out.AddFormatted(" %s",
+ reinterpret_cast<const char*>(it->rinfo()->data()));
+ DumpBuffer(f, &out);
+ }
+ }
+ }
+
+ delete it;
+ return static_cast<int>(pc - begin);
+}
+
+
+int Disassembler::Decode(Isolate* isolate, FILE* f, byte* begin, byte* end) {
+ V8NameConverter defaultConverter(NULL);
+ return DecodeIt(isolate, f, defaultConverter, begin, end);
+}
+
+
+// Called by Code::CodePrint.
+void Disassembler::Decode(FILE* f, Code* code) {
+ Isolate* isolate = code->GetIsolate();
+ int decode_size = code->is_crankshafted()
+ ? static_cast<int>(code->safepoint_table_offset())
+ : code->instruction_size();
+ // If there might be a back edge table, stop before reaching it.
+ if (code->kind() == Code::FUNCTION) {
+ decode_size =
+ Min(decode_size, static_cast<int>(code->back_edge_table_offset()));
+ }
+
+ byte* begin = code->instruction_start();
+ byte* end = begin + decode_size;
+ V8NameConverter v8NameConverter(code);
+ DecodeIt(isolate, f, v8NameConverter, begin, end);
+}
+
+#else // ENABLE_DISASSEMBLER
+
+void Disassembler::Dump(FILE* f, byte* begin, byte* end) {}
+int Disassembler::Decode(Isolate* isolate, FILE* f, byte* begin, byte* end) {
+ return 0;
+}
+
+
+void Disassembler::Decode(FILE* f, Code* code) {}
+
+#endif // ENABLE_DISASSEMBLER
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/disassembler.h b/chromium/v8/src/disassembler.h
new file mode 100644
index 00000000000..87891500367
--- /dev/null
+++ b/chromium/v8/src/disassembler.h
@@ -0,0 +1,58 @@
+// 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_DISASSEMBLER_H_
+#define V8_DISASSEMBLER_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+class Disassembler : public AllStatic {
+ public:
+ // Print the bytes in the interval [begin, end) into f.
+ static void Dump(FILE* f, byte* begin, byte* end);
+
+ // Decode instructions in the the interval [begin, end) and print the
+ // code into f. Returns the number of bytes disassembled or 1 if no
+ // instruction could be decoded.
+ static int Decode(Isolate* isolate, FILE* f, byte* begin, byte* end);
+
+ // Decode instructions in code.
+ static void Decode(FILE* f, Code* code);
+ private:
+ // Decode instruction at pc and print disassembled instruction into f.
+ // Returns the instruction length in bytes, or 1 if the instruction could
+ // not be decoded. The number of characters written is written into
+ // the out parameter char_count.
+ static int Decode(FILE* f, byte* pc, int* char_count);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_DISASSEMBLER_H_
diff --git a/chromium/v8/src/diy-fp.cc b/chromium/v8/src/diy-fp.cc
new file mode 100644
index 00000000000..49138777088
--- /dev/null
+++ b/chromium/v8/src/diy-fp.cc
@@ -0,0 +1,59 @@
+// 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 "../include/v8stdint.h"
+#include "globals.h"
+#include "checks.h"
+#include "diy-fp.h"
+
+namespace v8 {
+namespace internal {
+
+void DiyFp::Multiply(const DiyFp& other) {
+ // Simply "emulates" a 128 bit multiplication.
+ // However: the resulting number only contains 64 bits. The least
+ // significant 64 bits are only used for rounding the most significant 64
+ // bits.
+ const uint64_t kM32 = 0xFFFFFFFFu;
+ uint64_t a = f_ >> 32;
+ uint64_t b = f_ & kM32;
+ uint64_t c = other.f_ >> 32;
+ uint64_t d = other.f_ & kM32;
+ uint64_t ac = a * c;
+ uint64_t bc = b * c;
+ uint64_t ad = a * d;
+ uint64_t bd = b * d;
+ uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32);
+ // By adding 1U << 31 to tmp we round the final result.
+ // Halfway cases will be round up.
+ tmp += 1U << 31;
+ uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
+ e_ += other.e_ + 64;
+ f_ = result_f;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/diy-fp.h b/chromium/v8/src/diy-fp.h
new file mode 100644
index 00000000000..26ff1a20bfc
--- /dev/null
+++ b/chromium/v8/src/diy-fp.h
@@ -0,0 +1,117 @@
+// 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_DIY_FP_H_
+#define V8_DIY_FP_H_
+
+namespace v8 {
+namespace internal {
+
+// This "Do It Yourself Floating Point" class implements a floating-point number
+// with a uint64 significand and an int exponent. Normalized DiyFp numbers will
+// have the most significant bit of the significand set.
+// Multiplication and Subtraction do not normalize their results.
+// DiyFp are not designed to contain special doubles (NaN and Infinity).
+class DiyFp {
+ public:
+ static const int kSignificandSize = 64;
+
+ DiyFp() : f_(0), e_(0) {}
+ DiyFp(uint64_t f, int e) : f_(f), e_(e) {}
+
+ // this = this - other.
+ // The exponents of both numbers must be the same and the significand of this
+ // must be bigger than the significand of other.
+ // The result will not be normalized.
+ void Subtract(const DiyFp& other) {
+ ASSERT(e_ == other.e_);
+ ASSERT(f_ >= other.f_);
+ f_ -= other.f_;
+ }
+
+ // Returns a - b.
+ // The exponents of both numbers must be the same and this must be bigger
+ // than other. The result will not be normalized.
+ static DiyFp Minus(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Subtract(b);
+ return result;
+ }
+
+
+ // this = this * other.
+ void Multiply(const DiyFp& other);
+
+ // returns a * b;
+ static DiyFp Times(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Multiply(b);
+ return result;
+ }
+
+ void Normalize() {
+ ASSERT(f_ != 0);
+ uint64_t f = f_;
+ int e = e_;
+
+ // This method is mainly called for normalizing boundaries. In general
+ // boundaries need to be shifted by 10 bits. We thus optimize for this case.
+ const uint64_t k10MSBits = static_cast<uint64_t>(0x3FF) << 54;
+ while ((f & k10MSBits) == 0) {
+ f <<= 10;
+ e -= 10;
+ }
+ while ((f & kUint64MSB) == 0) {
+ f <<= 1;
+ e--;
+ }
+ f_ = f;
+ e_ = e;
+ }
+
+ static DiyFp Normalize(const DiyFp& a) {
+ DiyFp result = a;
+ result.Normalize();
+ return result;
+ }
+
+ uint64_t f() const { return f_; }
+ int e() const { return e_; }
+
+ void set_f(uint64_t new_value) { f_ = new_value; }
+ void set_e(int new_value) { e_ = new_value; }
+
+ private:
+ static const uint64_t kUint64MSB = static_cast<uint64_t>(1) << 63;
+
+ uint64_t f_;
+ int e_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_DIY_FP_H_
diff --git a/chromium/v8/src/double.h b/chromium/v8/src/double.h
new file mode 100644
index 00000000000..fcf6906af73
--- /dev/null
+++ b/chromium/v8/src/double.h
@@ -0,0 +1,232 @@
+// 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_DOUBLE_H_
+#define V8_DOUBLE_H_
+
+#include "diy-fp.h"
+
+namespace v8 {
+namespace internal {
+
+// We assume that doubles and uint64_t have the same endianness.
+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 {
+ public:
+ static const uint64_t kSignMask = V8_2PART_UINT64_C(0x80000000, 00000000);
+ static const uint64_t kExponentMask = V8_2PART_UINT64_C(0x7FF00000, 00000000);
+ static const uint64_t kSignificandMask =
+ V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF);
+ static const uint64_t kHiddenBit = V8_2PART_UINT64_C(0x00100000, 00000000);
+ static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
+ static const int kSignificandSize = 53;
+
+ Double() : d64_(0) {}
+ explicit Double(double d) : d64_(double_to_uint64(d)) {}
+ explicit Double(uint64_t d64) : d64_(d64) {}
+ explicit Double(DiyFp diy_fp)
+ : d64_(DiyFpToUint64(diy_fp)) {}
+
+ // The value encoded by this Double must be greater or equal to +0.0.
+ // It must not be special (infinity, or NaN).
+ DiyFp AsDiyFp() const {
+ ASSERT(Sign() > 0);
+ ASSERT(!IsSpecial());
+ return DiyFp(Significand(), Exponent());
+ }
+
+ // The value encoded by this Double must be strictly greater than 0.
+ DiyFp AsNormalizedDiyFp() const {
+ ASSERT(value() > 0.0);
+ uint64_t f = Significand();
+ int e = Exponent();
+
+ // The current double could be a denormal.
+ while ((f & kHiddenBit) == 0) {
+ f <<= 1;
+ e--;
+ }
+ // Do the final shifts in one go.
+ f <<= DiyFp::kSignificandSize - kSignificandSize;
+ e -= DiyFp::kSignificandSize - kSignificandSize;
+ return DiyFp(f, e);
+ }
+
+ // Returns the double's bit as uint64.
+ uint64_t AsUint64() const {
+ return d64_;
+ }
+
+ // Returns the next greater double. Returns +infinity on input +infinity.
+ double NextDouble() const {
+ if (d64_ == kInfinity) return Double(kInfinity).value();
+ if (Sign() < 0 && Significand() == 0) {
+ // -0.0
+ return 0.0;
+ }
+ if (Sign() < 0) {
+ return Double(d64_ - 1).value();
+ } else {
+ return Double(d64_ + 1).value();
+ }
+ }
+
+ int Exponent() const {
+ if (IsDenormal()) return kDenormalExponent;
+
+ uint64_t d64 = AsUint64();
+ int biased_e =
+ static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize);
+ return biased_e - kExponentBias;
+ }
+
+ uint64_t Significand() const {
+ uint64_t d64 = AsUint64();
+ uint64_t significand = d64 & kSignificandMask;
+ if (!IsDenormal()) {
+ return significand + kHiddenBit;
+ } else {
+ return significand;
+ }
+ }
+
+ // Returns true if the double is a denormal.
+ bool IsDenormal() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == 0;
+ }
+
+ // We consider denormals not to be special.
+ // Hence only Infinity and NaN are special.
+ bool IsSpecial() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == kExponentMask;
+ }
+
+ bool IsInfinite() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) == 0);
+ }
+
+ int Sign() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kSignMask) == 0? 1: -1;
+ }
+
+ // Precondition: the value encoded by this Double must be greater or equal
+ // than +0.0.
+ DiyFp UpperBoundary() const {
+ ASSERT(Sign() > 0);
+ return DiyFp(Significand() * 2 + 1, Exponent() - 1);
+ }
+
+ // Returns the two boundaries of this.
+ // The bigger boundary (m_plus) is normalized. The lower boundary has the same
+ // exponent as m_plus.
+ // Precondition: the value encoded by this Double must be greater than 0.
+ void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
+ ASSERT(value() > 0.0);
+ DiyFp v = this->AsDiyFp();
+ bool significand_is_zero = (v.f() == kHiddenBit);
+ DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
+ DiyFp m_minus;
+ if (significand_is_zero && v.e() != kDenormalExponent) {
+ // The boundary is closer. Think of v = 1000e10 and v- = 9999e9.
+ // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
+ // at a distance of 1e8.
+ // The only exception is for the smallest normal: the largest denormal is
+ // at the same distance as its successor.
+ // Note: denormals have the same exponent as the smallest normals.
+ m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
+ } else {
+ m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
+ }
+ m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
+ m_minus.set_e(m_plus.e());
+ *out_m_plus = m_plus;
+ *out_m_minus = m_minus;
+ }
+
+ double value() const { return uint64_to_double(d64_); }
+
+ // Returns the significand size for a given order of magnitude.
+ // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
+ // This function returns the number of significant binary digits v will have
+ // once its encoded into a double. In almost all cases this is equal to
+ // kSignificandSize. The only exception are denormals. They start with leading
+ // zeroes and their effective significand-size is hence smaller.
+ static int SignificandSizeForOrderOfMagnitude(int order) {
+ if (order >= (kDenormalExponent + kSignificandSize)) {
+ return kSignificandSize;
+ }
+ if (order <= kDenormalExponent) return 0;
+ return order - kDenormalExponent;
+ }
+
+ private:
+ static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
+ static const int kDenormalExponent = -kExponentBias + 1;
+ static const int kMaxExponent = 0x7FF - kExponentBias;
+ static const uint64_t kInfinity = V8_2PART_UINT64_C(0x7FF00000, 00000000);
+
+ const uint64_t d64_;
+
+ static uint64_t DiyFpToUint64(DiyFp diy_fp) {
+ uint64_t significand = diy_fp.f();
+ int exponent = diy_fp.e();
+ while (significand > kHiddenBit + kSignificandMask) {
+ significand >>= 1;
+ exponent++;
+ }
+ if (exponent >= kMaxExponent) {
+ return kInfinity;
+ }
+ if (exponent < kDenormalExponent) {
+ return 0;
+ }
+ while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) {
+ significand <<= 1;
+ exponent--;
+ }
+ uint64_t biased_exponent;
+ if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) {
+ biased_exponent = 0;
+ } else {
+ biased_exponent = static_cast<uint64_t>(exponent + kExponentBias);
+ }
+ return (significand & kSignificandMask) |
+ (biased_exponent << kPhysicalSignificandSize);
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_DOUBLE_H_
diff --git a/chromium/v8/src/dtoa.cc b/chromium/v8/src/dtoa.cc
new file mode 100644
index 00000000000..bda67205c76
--- /dev/null
+++ b/chromium/v8/src/dtoa.cc
@@ -0,0 +1,106 @@
+// 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 <cmath>
+
+#include "../include/v8stdint.h"
+#include "checks.h"
+#include "utils.h"
+
+#include "dtoa.h"
+
+#include "bignum-dtoa.h"
+#include "double.h"
+#include "fast-dtoa.h"
+#include "fixed-dtoa.h"
+
+namespace v8 {
+namespace internal {
+
+static BignumDtoaMode DtoaToBignumDtoaMode(DtoaMode dtoa_mode) {
+ switch (dtoa_mode) {
+ case DTOA_SHORTEST: return BIGNUM_DTOA_SHORTEST;
+ case DTOA_FIXED: return BIGNUM_DTOA_FIXED;
+ case DTOA_PRECISION: return BIGNUM_DTOA_PRECISION;
+ default:
+ UNREACHABLE();
+ return BIGNUM_DTOA_SHORTEST; // To silence compiler.
+ }
+}
+
+
+void DoubleToAscii(double v, DtoaMode mode, int requested_digits,
+ Vector<char> buffer, int* sign, int* length, int* point) {
+ ASSERT(!Double(v).IsSpecial());
+ ASSERT(mode == DTOA_SHORTEST || requested_digits >= 0);
+
+ if (Double(v).Sign() < 0) {
+ *sign = 1;
+ v = -v;
+ } else {
+ *sign = 0;
+ }
+
+ if (v == 0) {
+ buffer[0] = '0';
+ buffer[1] = '\0';
+ *length = 1;
+ *point = 1;
+ return;
+ }
+
+ if (mode == DTOA_PRECISION && requested_digits == 0) {
+ buffer[0] = '\0';
+ *length = 0;
+ return;
+ }
+
+ bool fast_worked;
+ switch (mode) {
+ case DTOA_SHORTEST:
+ fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, length, point);
+ break;
+ case DTOA_FIXED:
+ fast_worked = FastFixedDtoa(v, requested_digits, buffer, length, point);
+ break;
+ case DTOA_PRECISION:
+ fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
+ buffer, length, point);
+ break;
+ default:
+ UNREACHABLE();
+ fast_worked = false;
+ }
+ if (fast_worked) return;
+
+ // If the fast dtoa didn't succeed use the slower bignum version.
+ BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
+ BignumDtoa(v, bignum_mode, requested_digits, buffer, length, point);
+ buffer[*length] = '\0';
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/dtoa.h b/chromium/v8/src/dtoa.h
new file mode 100644
index 00000000000..948a0791913
--- /dev/null
+++ b/chromium/v8/src/dtoa.h
@@ -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.
+
+#ifndef V8_DTOA_H_
+#define V8_DTOA_H_
+
+namespace v8 {
+namespace internal {
+
+enum DtoaMode {
+ // Return the shortest correct representation.
+ // For example the output of 0.299999999999999988897 is (the less accurate but
+ // correct) 0.3.
+ DTOA_SHORTEST,
+ // Return a fixed number of digits after the decimal point.
+ // For instance fixed(0.1, 4) becomes 0.1000
+ // If the input number is big, the output will be big.
+ DTOA_FIXED,
+ // Return a fixed number of digits, no matter what the exponent is.
+ DTOA_PRECISION
+};
+
+// 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.
+const int kBase10MaximalLength = 17;
+
+// Converts the given double 'v' to ASCII.
+// The result should be interpreted as buffer * 10^(point-length).
+//
+// The output depends on the given mode:
+// - SHORTEST: produce the least amount of digits for which the internal
+// identity requirement is still satisfied. If the digits are printed
+// (together with the correct exponent) then reading this number will give
+// 'v' again. The buffer will choose the representation that is closest to
+// 'v'. If there are two at the same distance, than the one farther away
+// from 0 is chosen (halfway cases - ending with 5 - are rounded up).
+// In this mode the 'requested_digits' parameter is ignored.
+// - FIXED: produces digits necessary to print a given number with
+// 'requested_digits' digits after the decimal point. The produced digits
+// might be too short in which case the caller has to fill the gaps with '0's.
+// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
+// Halfway cases are rounded towards +/-Infinity (away from 0). The call
+// toFixed(0.15, 2) thus returns buffer="2", point=0.
+// The returned buffer may contain digits that would be truncated from the
+// shortest representation of the input.
+// - PRECISION: produces 'requested_digits' where the first digit is not '0'.
+// Even though the length of produced digits usually equals
+// 'requested_digits', the function is allowed to return fewer digits, in
+// which case the caller has to fill the missing digits with '0's.
+// Halfway cases are again rounded away from 0.
+// 'DoubleToAscii' expects the given buffer to be big enough to hold all digits
+// and a terminating null-character. In SHORTEST-mode it expects a buffer of
+// at least kBase10MaximalLength + 1. Otherwise, the size of the output is
+// limited to requested_digits digits plus the null terminator.
+void DoubleToAscii(double v, DtoaMode mode, int requested_digits,
+ Vector<char> buffer, int* sign, int* length, int* point);
+
+} } // namespace v8::internal
+
+#endif // V8_DTOA_H_
diff --git a/chromium/v8/src/effects.h b/chromium/v8/src/effects.h
new file mode 100644
index 00000000000..8e823634710
--- /dev/null
+++ b/chromium/v8/src/effects.h
@@ -0,0 +1,361 @@
+// Copyright 2013 the V8 project authors. 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_EFFECTS_H_
+#define V8_EFFECTS_H_
+
+#include "v8.h"
+
+#include "types.h"
+
+namespace v8 {
+namespace internal {
+
+
+// A simple struct to represent (write) effects. A write is represented as a
+// modification of type bounds (e.g. of a variable).
+//
+// An effect can either be definite, if the write is known to have taken place,
+// or 'possible', if it was optional. The difference is relevant when composing
+// effects.
+//
+// There are two ways to compose effects: sequentially (they happen one after
+// the other) or alternatively (either one or the other happens). A definite
+// effect cancels out any previous effect upon sequencing. A possible effect
+// merges into a previous effect, i.e., type bounds are merged. Alternative
+// composition always merges bounds. It yields a possible effect if at least
+// one was only possible.
+struct Effect {
+ enum Modality { POSSIBLE, DEFINITE };
+
+ Modality modality;
+ Bounds bounds;
+
+ Effect() {}
+ Effect(Bounds b, Modality m = DEFINITE) : modality(m), bounds(b) {}
+
+ // The unknown effect.
+ static Effect Unknown(Isolate* isolate) {
+ return Effect(Bounds::Unbounded(isolate), POSSIBLE);
+ }
+
+ static Effect Forget(Isolate* isolate) {
+ return Effect(Bounds::Unbounded(isolate), DEFINITE);
+ }
+
+ // Sequential composition, as in 'e1; e2'.
+ static Effect Seq(Effect e1, Effect e2, Isolate* isolate) {
+ if (e2.modality == DEFINITE) return e2;
+ return Effect(Bounds::Either(e1.bounds, e2.bounds, isolate), e1.modality);
+ }
+
+ // Alternative composition, as in 'cond ? e1 : e2'.
+ static Effect Alt(Effect e1, Effect e2, Isolate* isolate) {
+ return Effect(
+ Bounds::Either(e1.bounds, e2.bounds, isolate),
+ e1.modality == POSSIBLE ? POSSIBLE : e2.modality);
+ }
+};
+
+
+// Classes encapsulating sets of effects on variables.
+//
+// Effects maps variables to effects and supports sequential and alternative
+// composition.
+//
+// NestedEffects is an incremental representation that supports persistence
+// through functional extension. It represents the map as an adjoin of a list
+// of maps, whose tail can be shared.
+//
+// Both classes provide similar interfaces, implemented in parts through the
+// EffectsMixin below (using sandwich style, to work around the style guide's
+// MI restriction).
+//
+// We also (ab)use Effects/NestedEffects as a representation for abstract
+// store typings. In that case, only definite effects are of interest.
+
+template<class Var, class Base, class Effects>
+class EffectsMixin: public Base {
+ public:
+ explicit EffectsMixin(Zone* zone) : Base(zone) {}
+
+ Effect Lookup(Var var) {
+ Locator locator;
+ return this->Find(var, &locator)
+ ? locator.value() : Effect::Unknown(Base::isolate());
+ }
+
+ Bounds LookupBounds(Var var) {
+ Effect effect = Lookup(var);
+ return effect.modality == Effect::DEFINITE
+ ? effect.bounds : Bounds::Unbounded(Base::isolate());
+ }
+
+ // Sequential composition.
+ void Seq(Var var, Effect effect) {
+ Locator locator;
+ if (!this->Insert(var, &locator)) {
+ effect = Effect::Seq(locator.value(), effect, Base::isolate());
+ }
+ locator.set_value(effect);
+ }
+
+ void Seq(Effects that) {
+ SeqMerger<EffectsMixin> merge = { *this };
+ that.ForEach(&merge);
+ }
+
+ // Alternative composition.
+ void Alt(Var var, Effect effect) {
+ Locator locator;
+ if (!this->Insert(var, &locator)) {
+ effect = Effect::Alt(locator.value(), effect, Base::isolate());
+ }
+ locator.set_value(effect);
+ }
+
+ void Alt(Effects that) {
+ AltWeakener<EffectsMixin> weaken = { *this, that };
+ this->ForEach(&weaken);
+ AltMerger<EffectsMixin> merge = { *this };
+ that.ForEach(&merge);
+ }
+
+ // Invalidation.
+ void Forget() {
+ Overrider override = {
+ Effect::Forget(Base::isolate()), Effects(Base::zone()) };
+ this->ForEach(&override);
+ Seq(override.effects);
+ }
+
+ protected:
+ typedef typename Base::Locator Locator;
+
+ template<class Self>
+ struct SeqMerger {
+ void Call(Var var, Effect effect) { self.Seq(var, effect); }
+ Self self;
+ };
+
+ template<class Self>
+ struct AltMerger {
+ void Call(Var var, Effect effect) { self.Alt(var, effect); }
+ Self self;
+ };
+
+ template<class Self>
+ struct AltWeakener {
+ void Call(Var var, Effect effect) {
+ if (effect.modality == Effect::DEFINITE && !other.Contains(var)) {
+ effect.modality = Effect::POSSIBLE;
+ Locator locator;
+ self.Insert(var, &locator);
+ locator.set_value(effect);
+ }
+ }
+ Self self;
+ Effects other;
+ };
+
+ struct Overrider {
+ void Call(Var var, Effect effect) { effects.Seq(var, new_effect); }
+ Effect new_effect;
+ Effects effects;
+ };
+};
+
+
+template<class Var, Var kNoVar> class Effects;
+template<class Var, Var kNoVar> class NestedEffectsBase;
+
+template<class Var, Var kNoVar>
+class EffectsBase {
+ public:
+ explicit EffectsBase(Zone* zone) : map_(new(zone) Mapping(zone)) {}
+
+ bool IsEmpty() { return map_->is_empty(); }
+
+ protected:
+ friend class NestedEffectsBase<Var, kNoVar>;
+ friend class
+ EffectsMixin<Var, NestedEffectsBase<Var, kNoVar>, Effects<Var, kNoVar> >;
+
+ Zone* zone() { return map_->allocator().zone(); }
+ Isolate* isolate() { return zone()->isolate(); }
+
+ struct SplayTreeConfig {
+ typedef Var Key;
+ typedef Effect Value;
+ static const Var kNoKey = kNoVar;
+ static Effect NoValue() { return Effect(); }
+ static int Compare(int x, int y) { return y - x; }
+ };
+ typedef ZoneSplayTree<SplayTreeConfig> Mapping;
+ typedef typename Mapping::Locator Locator;
+
+ bool Contains(Var var) {
+ ASSERT(var != kNoVar);
+ return map_->Contains(var);
+ }
+ bool Find(Var var, Locator* locator) {
+ ASSERT(var != kNoVar);
+ return map_->Find(var, locator);
+ }
+ bool Insert(Var var, Locator* locator) {
+ ASSERT(var != kNoVar);
+ return map_->Insert(var, locator);
+ }
+
+ template<class Callback>
+ void ForEach(Callback* callback) {
+ return map_->ForEach(callback);
+ }
+
+ private:
+ Mapping* map_;
+};
+
+template<class Var, Var kNoVar>
+const Var EffectsBase<Var, kNoVar>::SplayTreeConfig::kNoKey;
+
+template<class Var, Var kNoVar>
+class Effects: public
+ EffectsMixin<Var, EffectsBase<Var, kNoVar>, Effects<Var, kNoVar> > {
+ public:
+ explicit Effects(Zone* zone)
+ : EffectsMixin<Var, EffectsBase<Var, kNoVar>, Effects<Var, kNoVar> >(zone)
+ {}
+};
+
+
+template<class Var, Var kNoVar>
+class NestedEffectsBase {
+ public:
+ explicit NestedEffectsBase(Zone* zone) : node_(new(zone) Node(zone)) {}
+
+ template<class Callback>
+ void ForEach(Callback* callback) {
+ if (node_->previous) NestedEffectsBase(node_->previous).ForEach(callback);
+ node_->effects.ForEach(callback);
+ }
+
+ Effects<Var, kNoVar> Top() { return node_->effects; }
+
+ bool IsEmpty() {
+ for (Node* node = node_; node != NULL; node = node->previous) {
+ if (!node->effects.IsEmpty()) return false;
+ }
+ return true;
+ }
+
+ protected:
+ typedef typename EffectsBase<Var, kNoVar>::Locator Locator;
+
+ Zone* zone() { return node_->zone; }
+ Isolate* isolate() { return zone()->isolate(); }
+
+ void push() { node_ = new(node_->zone) Node(node_->zone, node_); }
+ void pop() { node_ = node_->previous; }
+ bool is_empty() { return node_ == NULL; }
+
+ bool Contains(Var var) {
+ ASSERT(var != kNoVar);
+ for (Node* node = node_; node != NULL; node = node->previous) {
+ if (node->effects.Contains(var)) return true;
+ }
+ return false;
+ }
+
+ bool Find(Var var, Locator* locator) {
+ ASSERT(var != kNoVar);
+ for (Node* node = node_; node != NULL; node = node->previous) {
+ if (node->effects.Find(var, locator)) return true;
+ }
+ return false;
+ }
+
+ bool Insert(Var var, Locator* locator);
+
+ private:
+ struct Node: ZoneObject {
+ Zone* zone;
+ Effects<Var, kNoVar> effects;
+ Node* previous;
+ explicit Node(Zone* zone, Node* previous = NULL)
+ : zone(zone), effects(zone), previous(previous) {}
+ };
+
+ explicit NestedEffectsBase(Node* node) : node_(node) {}
+
+ Node* node_;
+};
+
+
+template<class Var, Var kNoVar>
+bool NestedEffectsBase<Var, kNoVar>::Insert(Var var, Locator* locator) {
+ ASSERT(var != kNoVar);
+ if (!node_->effects.Insert(var, locator)) return false;
+ Locator shadowed;
+ for (Node* node = node_->previous; node != NULL; node = node->previous) {
+ if (node->effects.Find(var, &shadowed)) {
+ // Initialize with shadowed entry.
+ locator->set_value(shadowed.value());
+ return false;
+ }
+ }
+ return true;
+}
+
+
+template<class Var, Var kNoVar>
+class NestedEffects: public
+ EffectsMixin<Var, NestedEffectsBase<Var, kNoVar>, Effects<Var, kNoVar> > {
+ public:
+ explicit NestedEffects(Zone* zone) :
+ EffectsMixin<Var, NestedEffectsBase<Var, kNoVar>, Effects<Var, kNoVar> >(
+ zone) {}
+
+ // Create an extension of the current effect set. The current set should not
+ // be modified while the extension is in use.
+ NestedEffects Push() {
+ NestedEffects result = *this;
+ result.push();
+ return result;
+ }
+
+ NestedEffects Pop() {
+ NestedEffects result = *this;
+ result.pop();
+ ASSERT(!this->is_empty());
+ return result;
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_EFFECTS_H_
diff --git a/chromium/v8/src/elements-kind.cc b/chromium/v8/src/elements-kind.cc
new file mode 100644
index 00000000000..213aa35c85d
--- /dev/null
+++ b/chromium/v8/src/elements-kind.cc
@@ -0,0 +1,140 @@
+// Copyright 2012 the V8 project authors. 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 "elements-kind.h"
+
+#include "api.h"
+#include "elements.h"
+#include "objects.h"
+
+namespace v8 {
+namespace internal {
+
+
+const char* ElementsKindToString(ElementsKind kind) {
+ ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
+ return accessor->name();
+}
+
+
+void PrintElementsKind(FILE* out, ElementsKind kind) {
+ PrintF(out, "%s", ElementsKindToString(kind));
+}
+
+
+ElementsKind GetInitialFastElementsKind() {
+ if (FLAG_packed_arrays) {
+ return FAST_SMI_ELEMENTS;
+ } else {
+ return FAST_HOLEY_SMI_ELEMENTS;
+ }
+}
+
+
+struct InitializeFastElementsKindSequence {
+ static void Construct(
+ ElementsKind** fast_elements_kind_sequence_ptr) {
+ ElementsKind* fast_elements_kind_sequence =
+ new ElementsKind[kFastElementsKindCount];
+ *fast_elements_kind_sequence_ptr = fast_elements_kind_sequence;
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == FIRST_FAST_ELEMENTS_KIND);
+ fast_elements_kind_sequence[0] = FAST_SMI_ELEMENTS;
+ fast_elements_kind_sequence[1] = FAST_HOLEY_SMI_ELEMENTS;
+ fast_elements_kind_sequence[2] = FAST_DOUBLE_ELEMENTS;
+ fast_elements_kind_sequence[3] = FAST_HOLEY_DOUBLE_ELEMENTS;
+ fast_elements_kind_sequence[4] = FAST_ELEMENTS;
+ fast_elements_kind_sequence[5] = FAST_HOLEY_ELEMENTS;
+ }
+};
+
+
+static LazyInstance<ElementsKind*,
+ InitializeFastElementsKindSequence>::type
+ fast_elements_kind_sequence = LAZY_INSTANCE_INITIALIZER;
+
+
+ElementsKind GetFastElementsKindFromSequenceIndex(int sequence_number) {
+ ASSERT(sequence_number >= 0 &&
+ sequence_number < kFastElementsKindCount);
+ return fast_elements_kind_sequence.Get()[sequence_number];
+}
+
+
+int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind) {
+ for (int i = 0; i < kFastElementsKindCount; ++i) {
+ if (fast_elements_kind_sequence.Get()[i] == elements_kind) {
+ return i;
+ }
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+
+ElementsKind GetNextMoreGeneralFastElementsKind(ElementsKind elements_kind,
+ bool allow_only_packed) {
+ ASSERT(IsFastElementsKind(elements_kind));
+ ASSERT(elements_kind != TERMINAL_FAST_ELEMENTS_KIND);
+ while (true) {
+ int index =
+ GetSequenceIndexFromFastElementsKind(elements_kind) + 1;
+ elements_kind = GetFastElementsKindFromSequenceIndex(index);
+ if (!IsFastHoleyElementsKind(elements_kind) || !allow_only_packed) {
+ return elements_kind;
+ }
+ }
+ UNREACHABLE();
+ return TERMINAL_FAST_ELEMENTS_KIND;
+}
+
+
+bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind,
+ ElementsKind to_kind) {
+ switch (from_kind) {
+ case FAST_SMI_ELEMENTS:
+ return to_kind != FAST_SMI_ELEMENTS;
+ case FAST_HOLEY_SMI_ELEMENTS:
+ return to_kind != FAST_SMI_ELEMENTS &&
+ to_kind != FAST_HOLEY_SMI_ELEMENTS;
+ case FAST_DOUBLE_ELEMENTS:
+ return to_kind != FAST_SMI_ELEMENTS &&
+ to_kind != FAST_HOLEY_SMI_ELEMENTS &&
+ to_kind != FAST_DOUBLE_ELEMENTS;
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ return to_kind == FAST_ELEMENTS ||
+ to_kind == FAST_HOLEY_ELEMENTS;
+ case FAST_ELEMENTS:
+ return to_kind == FAST_HOLEY_ELEMENTS;
+ case FAST_HOLEY_ELEMENTS:
+ return false;
+ default:
+ return false;
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/elements-kind.h b/chromium/v8/src/elements-kind.h
new file mode 100644
index 00000000000..da151924be2
--- /dev/null
+++ b/chromium/v8/src/elements-kind.h
@@ -0,0 +1,234 @@
+// Copyright 2012 the V8 project authors. 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_ELEMENTS_KIND_H_
+#define V8_ELEMENTS_KIND_H_
+
+#include "v8checks.h"
+
+namespace v8 {
+namespace internal {
+
+enum ElementsKind {
+ // 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_ELEMENTS,
+ FAST_HOLEY_SMI_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,
+ FAST_HOLEY_ELEMENTS,
+
+ // The "fast" kind for unwrapped, non-tagged double values.
+ FAST_DOUBLE_ELEMENTS,
+ FAST_HOLEY_DOUBLE_ELEMENTS,
+
+ // The "slow" kind.
+ DICTIONARY_ELEMENTS,
+ NON_STRICT_ARGUMENTS_ELEMENTS,
+ // The "fast" kind for external arrays
+ EXTERNAL_BYTE_ELEMENTS,
+ EXTERNAL_UNSIGNED_BYTE_ELEMENTS,
+ EXTERNAL_SHORT_ELEMENTS,
+ EXTERNAL_UNSIGNED_SHORT_ELEMENTS,
+ EXTERNAL_INT_ELEMENTS,
+ EXTERNAL_UNSIGNED_INT_ELEMENTS,
+ EXTERNAL_FLOAT_ELEMENTS,
+ EXTERNAL_DOUBLE_ELEMENTS,
+ EXTERNAL_PIXEL_ELEMENTS,
+
+ // Derived constants from ElementsKind
+ FIRST_ELEMENTS_KIND = FAST_SMI_ELEMENTS,
+ LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS,
+ FIRST_FAST_ELEMENTS_KIND = FAST_SMI_ELEMENTS,
+ LAST_FAST_ELEMENTS_KIND = FAST_HOLEY_DOUBLE_ELEMENTS,
+ FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS,
+ LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS,
+ TERMINAL_FAST_ELEMENTS_KIND = FAST_HOLEY_ELEMENTS
+};
+
+const int kElementsKindCount = LAST_ELEMENTS_KIND - FIRST_ELEMENTS_KIND + 1;
+const int kFastElementsKindCount = LAST_FAST_ELEMENTS_KIND -
+ FIRST_FAST_ELEMENTS_KIND + 1;
+
+const char* ElementsKindToString(ElementsKind kind);
+void PrintElementsKind(FILE* out, ElementsKind kind);
+
+ElementsKind GetInitialFastElementsKind();
+
+ElementsKind GetFastElementsKindFromSequenceIndex(int sequence_index);
+
+int GetSequenceIndexFromFastElementsKind(ElementsKind elements_kind);
+
+
+inline bool IsDictionaryElementsKind(ElementsKind kind) {
+ return kind == DICTIONARY_ELEMENTS;
+}
+
+
+inline bool IsExternalArrayElementsKind(ElementsKind kind) {
+ return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
+ kind <= LAST_EXTERNAL_ARRAY_ELEMENTS_KIND;
+}
+
+
+inline bool IsFastElementsKind(ElementsKind kind) {
+ ASSERT(FIRST_FAST_ELEMENTS_KIND == 0);
+ return kind <= FAST_HOLEY_DOUBLE_ELEMENTS;
+}
+
+
+inline bool IsFastDoubleElementsKind(ElementsKind kind) {
+ return kind == FAST_DOUBLE_ELEMENTS ||
+ kind == FAST_HOLEY_DOUBLE_ELEMENTS;
+}
+
+
+inline bool IsExternalFloatOrDoubleElementsKind(ElementsKind kind) {
+ return kind == EXTERNAL_DOUBLE_ELEMENTS ||
+ kind == EXTERNAL_FLOAT_ELEMENTS;
+}
+
+
+inline bool IsDoubleOrFloatElementsKind(ElementsKind kind) {
+ return IsFastDoubleElementsKind(kind) ||
+ IsExternalFloatOrDoubleElementsKind(kind);
+}
+
+
+inline bool IsFastSmiOrObjectElementsKind(ElementsKind kind) {
+ return kind == FAST_SMI_ELEMENTS ||
+ kind == FAST_HOLEY_SMI_ELEMENTS ||
+ kind == FAST_ELEMENTS ||
+ kind == FAST_HOLEY_ELEMENTS;
+}
+
+
+inline bool IsFastSmiElementsKind(ElementsKind kind) {
+ return kind == FAST_SMI_ELEMENTS ||
+ kind == FAST_HOLEY_SMI_ELEMENTS;
+}
+
+
+inline bool IsFastObjectElementsKind(ElementsKind kind) {
+ return kind == FAST_ELEMENTS ||
+ kind == FAST_HOLEY_ELEMENTS;
+}
+
+
+inline bool IsFastHoleyElementsKind(ElementsKind kind) {
+ return kind == FAST_HOLEY_SMI_ELEMENTS ||
+ kind == FAST_HOLEY_DOUBLE_ELEMENTS ||
+ kind == FAST_HOLEY_ELEMENTS;
+}
+
+
+inline bool IsHoleyElementsKind(ElementsKind kind) {
+ return IsFastHoleyElementsKind(kind) ||
+ kind == DICTIONARY_ELEMENTS;
+}
+
+
+inline bool IsFastPackedElementsKind(ElementsKind kind) {
+ return kind == FAST_SMI_ELEMENTS ||
+ kind == FAST_DOUBLE_ELEMENTS ||
+ kind == FAST_ELEMENTS;
+}
+
+
+inline ElementsKind GetPackedElementsKind(ElementsKind holey_kind) {
+ if (holey_kind == FAST_HOLEY_SMI_ELEMENTS) {
+ return FAST_SMI_ELEMENTS;
+ }
+ if (holey_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
+ return FAST_DOUBLE_ELEMENTS;
+ }
+ if (holey_kind == FAST_HOLEY_ELEMENTS) {
+ return FAST_ELEMENTS;
+ }
+ return holey_kind;
+}
+
+
+inline ElementsKind GetHoleyElementsKind(ElementsKind packed_kind) {
+ if (packed_kind == FAST_SMI_ELEMENTS) {
+ return FAST_HOLEY_SMI_ELEMENTS;
+ }
+ if (packed_kind == FAST_DOUBLE_ELEMENTS) {
+ return FAST_HOLEY_DOUBLE_ELEMENTS;
+ }
+ if (packed_kind == FAST_ELEMENTS) {
+ return FAST_HOLEY_ELEMENTS;
+ }
+ return packed_kind;
+}
+
+
+inline ElementsKind FastSmiToObjectElementsKind(ElementsKind from_kind) {
+ ASSERT(IsFastSmiElementsKind(from_kind));
+ return (from_kind == FAST_SMI_ELEMENTS)
+ ? FAST_ELEMENTS
+ : FAST_HOLEY_ELEMENTS;
+}
+
+
+inline bool IsSimpleMapChangeTransition(ElementsKind from_kind,
+ ElementsKind to_kind) {
+ return (GetHoleyElementsKind(from_kind) == to_kind) ||
+ (IsFastSmiElementsKind(from_kind) &&
+ IsFastObjectElementsKind(to_kind));
+}
+
+
+bool IsMoreGeneralElementsKindTransition(ElementsKind from_kind,
+ ElementsKind to_kind);
+
+
+inline bool IsTransitionableFastElementsKind(ElementsKind from_kind) {
+ return IsFastElementsKind(from_kind) &&
+ from_kind != TERMINAL_FAST_ELEMENTS_KIND;
+}
+
+
+ElementsKind GetNextMoreGeneralFastElementsKind(ElementsKind elements_kind,
+ bool allow_only_packed);
+
+
+inline bool CanTransitionToMoreGeneralFastElementsKind(
+ ElementsKind elements_kind,
+ bool allow_only_packed) {
+ return IsFastElementsKind(elements_kind) &&
+ (elements_kind != TERMINAL_FAST_ELEMENTS_KIND &&
+ (!allow_only_packed || elements_kind != FAST_ELEMENTS));
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_ELEMENTS_KIND_H_
diff --git a/chromium/v8/src/elements.cc b/chromium/v8/src/elements.cc
new file mode 100644
index 00000000000..77abf4e42b8
--- /dev/null
+++ b/chromium/v8/src/elements.cc
@@ -0,0 +1,2073 @@
+// Copyright 2012 the V8 project authors. 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 "arguments.h"
+#include "objects.h"
+#include "elements.h"
+#include "utils.h"
+#include "v8conversions.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)
+// - FastSmiOrObjectElementsAccessor
+// - FastPackedSmiElementsAccessor
+// - FastHoleySmiElementsAccessor
+// - FastPackedObjectElementsAccessor
+// - FastHoleyObjectElementsAccessor
+// - FastDoubleElementsAccessor
+// - FastPackedDoubleElementsAccessor
+// - FastHoleyDoubleElementsAccessor
+// - ExternalElementsAccessor (abstract)
+// - ExternalByteElementsAccessor
+// - ExternalUnsignedByteElementsAccessor
+// - ExternalShortElementsAccessor
+// - ExternalUnsignedShortElementsAccessor
+// - ExternalIntElementsAccessor
+// - ExternalUnsignedIntElementsAccessor
+// - ExternalFloatElementsAccessor
+// - ExternalDoubleElementsAccessor
+// - PixelElementsAccessor
+// - DictionaryElementsAccessor
+// - NonStrictArgumentsElementsAccessor
+
+
+namespace v8 {
+namespace internal {
+
+
+static const int kPackedSizeNotKnown = -1;
+
+
+// First argument in list is the accessor class, the second argument is the
+// accessor ElementsKind, and the third is the backing store class. 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(FastPackedSmiElementsAccessor, FAST_SMI_ELEMENTS, FixedArray) \
+ V(FastHoleySmiElementsAccessor, FAST_HOLEY_SMI_ELEMENTS, \
+ FixedArray) \
+ V(FastPackedObjectElementsAccessor, FAST_ELEMENTS, FixedArray) \
+ V(FastHoleyObjectElementsAccessor, FAST_HOLEY_ELEMENTS, FixedArray) \
+ V(FastPackedDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS, \
+ FixedDoubleArray) \
+ V(FastHoleyDoubleElementsAccessor, FAST_HOLEY_DOUBLE_ELEMENTS, \
+ FixedDoubleArray) \
+ V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS, \
+ SeededNumberDictionary) \
+ V(NonStrictArgumentsElementsAccessor, NON_STRICT_ARGUMENTS_ELEMENTS, \
+ FixedArray) \
+ V(ExternalByteElementsAccessor, EXTERNAL_BYTE_ELEMENTS, \
+ ExternalByteArray) \
+ V(ExternalUnsignedByteElementsAccessor, \
+ EXTERNAL_UNSIGNED_BYTE_ELEMENTS, ExternalUnsignedByteArray) \
+ V(ExternalShortElementsAccessor, EXTERNAL_SHORT_ELEMENTS, \
+ ExternalShortArray) \
+ V(ExternalUnsignedShortElementsAccessor, \
+ EXTERNAL_UNSIGNED_SHORT_ELEMENTS, ExternalUnsignedShortArray) \
+ V(ExternalIntElementsAccessor, EXTERNAL_INT_ELEMENTS, \
+ ExternalIntArray) \
+ V(ExternalUnsignedIntElementsAccessor, \
+ EXTERNAL_UNSIGNED_INT_ELEMENTS, ExternalUnsignedIntArray) \
+ V(ExternalFloatElementsAccessor, \
+ EXTERNAL_FLOAT_ELEMENTS, ExternalFloatArray) \
+ V(ExternalDoubleElementsAccessor, \
+ EXTERNAL_DOUBLE_ELEMENTS, ExternalDoubleArray) \
+ V(PixelElementsAccessor, EXTERNAL_PIXEL_ELEMENTS, ExternalPixelArray)
+
+
+template<ElementsKind Kind> class ElementsKindTraits {
+ public:
+ typedef FixedArrayBase BackingStore;
+};
+
+#define ELEMENTS_TRAITS(Class, KindParam, Store) \
+template<> class ElementsKindTraits<KindParam> { \
+ public: \
+ static const ElementsKind Kind = KindParam; \
+ typedef Store BackingStore; \
+};
+ELEMENTS_LIST(ELEMENTS_TRAITS)
+#undef ELEMENTS_TRAITS
+
+
+ElementsAccessor** ElementsAccessor::elements_accessors_;
+
+
+static bool HasKey(FixedArray* array, Object* key) {
+ int len0 = array->length();
+ for (int i = 0; i < len0; i++) {
+ Object* element = array->get(i);
+ if (element->IsSmi() && element == key) return true;
+ if (element->IsString() &&
+ key->IsString() && String::cast(element)->Equals(String::cast(key))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+static Failure* ThrowArrayLengthRangeError(Heap* heap) {
+ HandleScope scope(heap->isolate());
+ return heap->isolate()->Throw(
+ *heap->isolate()->factory()->NewRangeError("invalid_array_length",
+ HandleVector<Object>(NULL, 0)));
+}
+
+
+static void CopyObjectToObjectElements(FixedArrayBase* from_base,
+ ElementsKind from_kind,
+ uint32_t from_start,
+ FixedArrayBase* to_base,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int raw_copy_size) {
+ ASSERT(to_base->map() != HEAP->fixed_cow_array_map());
+ DisallowHeapAllocation no_allocation;
+ int copy_size = raw_copy_size;
+ if (raw_copy_size < 0) {
+ ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd ||
+ raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
+ copy_size = Min(from_base->length() - from_start,
+ to_base->length() - to_start);
+ if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
+ int start = to_start + copy_size;
+ int length = to_base->length() - start;
+ if (length > 0) {
+ Heap* heap = from_base->GetHeap();
+ MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
+ heap->the_hole_value(), length);
+ }
+ }
+ }
+ ASSERT((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_base->length());
+ if (copy_size == 0) return;
+ FixedArray* from = FixedArray::cast(from_base);
+ FixedArray* to = FixedArray::cast(to_base);
+ ASSERT(IsFastSmiOrObjectElementsKind(from_kind));
+ ASSERT(IsFastSmiOrObjectElementsKind(to_kind));
+ Address to_address = to->address() + FixedArray::kHeaderSize;
+ Address from_address = from->address() + FixedArray::kHeaderSize;
+ CopyWords(reinterpret_cast<Object**>(to_address) + to_start,
+ reinterpret_cast<Object**>(from_address) + from_start,
+ static_cast<size_t>(copy_size));
+ if (IsFastObjectElementsKind(from_kind) &&
+ IsFastObjectElementsKind(to_kind)) {
+ Heap* heap = from->GetHeap();
+ if (!heap->InNewSpace(to)) {
+ heap->RecordWrites(to->address(),
+ to->OffsetOfElementAt(to_start),
+ copy_size);
+ }
+ heap->incremental_marking()->RecordWrites(to);
+ }
+}
+
+
+static void CopyDictionaryToObjectElements(FixedArrayBase* from_base,
+ uint32_t from_start,
+ FixedArrayBase* to_base,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int raw_copy_size) {
+ SeededNumberDictionary* from = SeededNumberDictionary::cast(from_base);
+ DisallowHeapAllocation no_allocation;
+ int copy_size = raw_copy_size;
+ Heap* heap = from->GetHeap();
+ if (raw_copy_size < 0) {
+ ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd ||
+ raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
+ copy_size = from->max_number_key() + 1 - from_start;
+ if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
+ int start = to_start + copy_size;
+ int length = to_base->length() - start;
+ if (length > 0) {
+ Heap* heap = from->GetHeap();
+ MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
+ heap->the_hole_value(), length);
+ }
+ }
+ }
+ ASSERT(to_base != from_base);
+ ASSERT(IsFastSmiOrObjectElementsKind(to_kind));
+ if (copy_size == 0) return;
+ FixedArray* to = FixedArray::cast(to_base);
+ uint32_t to_length = to->length();
+ if (to_start + copy_size > to_length) {
+ copy_size = to_length - to_start;
+ }
+ for (int i = 0; i < copy_size; i++) {
+ int entry = from->FindEntry(i + from_start);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ Object* value = from->ValueAt(entry);
+ ASSERT(!value->IsTheHole());
+ to->set(i + to_start, value, SKIP_WRITE_BARRIER);
+ } else {
+ to->set_the_hole(i + to_start);
+ }
+ }
+ if (IsFastObjectElementsKind(to_kind)) {
+ if (!heap->InNewSpace(to)) {
+ heap->RecordWrites(to->address(),
+ to->OffsetOfElementAt(to_start),
+ copy_size);
+ }
+ heap->incremental_marking()->RecordWrites(to);
+ }
+}
+
+
+MUST_USE_RESULT static MaybeObject* CopyDoubleToObjectElements(
+ FixedArrayBase* from_base,
+ uint32_t from_start,
+ FixedArrayBase* to_base,
+ ElementsKind to_kind,
+ uint32_t to_start,
+ int raw_copy_size) {
+ ASSERT(IsFastSmiOrObjectElementsKind(to_kind));
+ int copy_size = raw_copy_size;
+ if (raw_copy_size < 0) {
+ ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd ||
+ raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
+ copy_size = Min(from_base->length() - from_start,
+ to_base->length() - to_start);
+ if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
+ // Also initialize the area that will be copied over since HeapNumber
+ // allocation below can cause an incremental marking step, requiring all
+ // existing heap objects to be propertly initialized.
+ int start = to_start;
+ int length = to_base->length() - start;
+ if (length > 0) {
+ Heap* heap = from_base->GetHeap();
+ MemsetPointer(FixedArray::cast(to_base)->data_start() + start,
+ heap->the_hole_value(), length);
+ }
+ }
+ }
+ ASSERT((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_base->length());
+ if (copy_size == 0) return from_base;
+ FixedDoubleArray* from = FixedDoubleArray::cast(from_base);
+ FixedArray* to = FixedArray::cast(to_base);
+ for (int i = 0; i < copy_size; ++i) {
+ if (IsFastSmiElementsKind(to_kind)) {
+ UNIMPLEMENTED();
+ return Failure::Exception();
+ } else {
+ MaybeObject* maybe_value = from->get(i + from_start);
+ Object* value;
+ ASSERT(IsFastObjectElementsKind(to_kind));
+ // Because Double -> Object elements transitions allocate HeapObjects
+ // iteratively, the allocate must succeed within a single GC cycle,
+ // otherwise the retry after the GC will also fail. In order to ensure
+ // that no GC is triggered, allocate HeapNumbers from old space if they
+ // can't be taken from new space.
+ if (!maybe_value->ToObject(&value)) {
+ ASSERT(maybe_value->IsRetryAfterGC() || maybe_value->IsOutOfMemory());
+ Heap* heap = from->GetHeap();
+ MaybeObject* maybe_value_object =
+ heap->AllocateHeapNumber(from->get_scalar(i + from_start),
+ TENURED);
+ if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
+ }
+ to->set(i + to_start, value, UPDATE_WRITE_BARRIER);
+ }
+ }
+ return to;
+}
+
+
+static void CopyDoubleToDoubleElements(FixedArrayBase* from_base,
+ uint32_t from_start,
+ FixedArrayBase* to_base,
+ uint32_t to_start,
+ int raw_copy_size) {
+ int copy_size = raw_copy_size;
+ if (raw_copy_size < 0) {
+ ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd ||
+ raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
+ copy_size = Min(from_base->length() - from_start,
+ to_base->length() - to_start);
+ if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
+ for (int i = to_start + copy_size; i < to_base->length(); ++i) {
+ FixedDoubleArray::cast(to_base)->set_the_hole(i);
+ }
+ }
+ }
+ ASSERT((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_base->length());
+ if (copy_size == 0) return;
+ FixedDoubleArray* from = FixedDoubleArray::cast(from_base);
+ FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
+ Address to_address = to->address() + FixedDoubleArray::kHeaderSize;
+ Address from_address = from->address() + FixedDoubleArray::kHeaderSize;
+ to_address += kDoubleSize * to_start;
+ from_address += kDoubleSize * from_start;
+ int words_per_double = (kDoubleSize / kPointerSize);
+ CopyWords(reinterpret_cast<Object**>(to_address),
+ reinterpret_cast<Object**>(from_address),
+ static_cast<size_t>(words_per_double * copy_size));
+}
+
+
+static void CopySmiToDoubleElements(FixedArrayBase* from_base,
+ uint32_t from_start,
+ FixedArrayBase* to_base,
+ uint32_t to_start,
+ int raw_copy_size) {
+ int copy_size = raw_copy_size;
+ if (raw_copy_size < 0) {
+ ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd ||
+ raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
+ copy_size = from_base->length() - from_start;
+ if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
+ for (int i = to_start + copy_size; i < to_base->length(); ++i) {
+ FixedDoubleArray::cast(to_base)->set_the_hole(i);
+ }
+ }
+ }
+ ASSERT((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_base->length());
+ if (copy_size == 0) return;
+ FixedArray* from = FixedArray::cast(from_base);
+ FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
+ Object* the_hole = from->GetHeap()->the_hole_value();
+ for (uint32_t from_end = from_start + static_cast<uint32_t>(copy_size);
+ from_start < from_end; from_start++, to_start++) {
+ Object* hole_or_smi = from->get(from_start);
+ if (hole_or_smi == the_hole) {
+ to->set_the_hole(to_start);
+ } else {
+ to->set(to_start, Smi::cast(hole_or_smi)->value());
+ }
+ }
+}
+
+
+static void CopyPackedSmiToDoubleElements(FixedArrayBase* from_base,
+ uint32_t from_start,
+ FixedArrayBase* to_base,
+ uint32_t to_start,
+ int packed_size,
+ int raw_copy_size) {
+ int copy_size = raw_copy_size;
+ uint32_t to_end;
+ if (raw_copy_size < 0) {
+ ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd ||
+ raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
+ copy_size = packed_size - from_start;
+ if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
+ to_end = to_base->length();
+ for (uint32_t i = to_start + copy_size; i < to_end; ++i) {
+ FixedDoubleArray::cast(to_base)->set_the_hole(i);
+ }
+ } else {
+ to_end = to_start + static_cast<uint32_t>(copy_size);
+ }
+ } else {
+ to_end = to_start + static_cast<uint32_t>(copy_size);
+ }
+ ASSERT(static_cast<int>(to_end) <= to_base->length());
+ ASSERT(packed_size >= 0 && packed_size <= copy_size);
+ ASSERT((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_base->length());
+ if (copy_size == 0) return;
+ FixedArray* from = FixedArray::cast(from_base);
+ FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
+ for (uint32_t from_end = from_start + static_cast<uint32_t>(packed_size);
+ from_start < from_end; from_start++, to_start++) {
+ Object* smi = from->get(from_start);
+ ASSERT(!smi->IsTheHole());
+ to->set(to_start, Smi::cast(smi)->value());
+ }
+}
+
+
+static void CopyObjectToDoubleElements(FixedArrayBase* from_base,
+ uint32_t from_start,
+ FixedArrayBase* to_base,
+ uint32_t to_start,
+ int raw_copy_size) {
+ int copy_size = raw_copy_size;
+ if (raw_copy_size < 0) {
+ ASSERT(raw_copy_size == ElementsAccessor::kCopyToEnd ||
+ raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
+ copy_size = from_base->length() - from_start;
+ if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
+ for (int i = to_start + copy_size; i < to_base->length(); ++i) {
+ FixedDoubleArray::cast(to_base)->set_the_hole(i);
+ }
+ }
+ }
+ ASSERT((copy_size + static_cast<int>(to_start)) <= to_base->length() &&
+ (copy_size + static_cast<int>(from_start)) <= from_base->length());
+ if (copy_size == 0) return;
+ FixedArray* from = FixedArray::cast(from_base);
+ FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
+ Object* the_hole = from->GetHeap()->the_hole_value();
+ for (uint32_t from_end = from_start + copy_size;
+ from_start < from_end; from_start++, to_start++) {
+ Object* hole_or_object = from->get(from_start);
+ if (hole_or_object == the_hole) {
+ to->set_the_hole(to_start);
+ } else {
+ to->set(to_start, hole_or_object->Number());
+ }
+ }
+}
+
+
+static void CopyDictionaryToDoubleElements(FixedArrayBase* from_base,
+ uint32_t from_start,
+ FixedArrayBase* to_base,
+ uint32_t to_start,
+ int raw_copy_size) {
+ SeededNumberDictionary* from = SeededNumberDictionary::cast(from_base);
+ int copy_size = raw_copy_size;
+ if (copy_size < 0) {
+ ASSERT(copy_size == ElementsAccessor::kCopyToEnd ||
+ copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole);
+ copy_size = from->max_number_key() + 1 - from_start;
+ if (raw_copy_size == ElementsAccessor::kCopyToEndAndInitializeToHole) {
+ for (int i = to_start + copy_size; i < to_base->length(); ++i) {
+ FixedDoubleArray::cast(to_base)->set_the_hole(i);
+ }
+ }
+ }
+ if (copy_size == 0) return;
+ FixedDoubleArray* to = FixedDoubleArray::cast(to_base);
+ uint32_t to_length = to->length();
+ if (to_start + copy_size > to_length) {
+ copy_size = to_length - to_start;
+ }
+ for (int i = 0; i < copy_size; i++) {
+ int entry = from->FindEntry(i + from_start);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ to->set(i + to_start, from->ValueAt(entry)->Number());
+ } else {
+ to->set_the_hole(i + to_start);
+ }
+ }
+}
+
+
+static void TraceTopFrame(Isolate* isolate) {
+ StackFrameIterator it(isolate);
+ if (it.done()) {
+ PrintF("unknown location (no JavaScript frames present)");
+ return;
+ }
+ StackFrame* raw_frame = it.frame();
+ if (raw_frame->is_internal()) {
+ Isolate* isolate = Isolate::Current();
+ Code* apply_builtin = isolate->builtins()->builtin(
+ Builtins::kFunctionApply);
+ if (raw_frame->unchecked_code() == apply_builtin) {
+ PrintF("apply from ");
+ it.Advance();
+ raw_frame = it.frame();
+ }
+ }
+ JavaScriptFrame::PrintTop(isolate, stdout, false, true);
+}
+
+
+void CheckArrayAbuse(JSObject* obj, const char* op, uint32_t key,
+ bool allow_appending) {
+ Object* raw_length = NULL;
+ const char* elements_type = "array";
+ if (obj->IsJSArray()) {
+ JSArray* array = JSArray::cast(obj);
+ raw_length = array->length();
+ } else {
+ raw_length = Smi::FromInt(obj->elements()->length());
+ elements_type = "object";
+ }
+
+ if (raw_length->IsNumber()) {
+ double n = raw_length->Number();
+ if (FastI2D(FastD2UI(n)) == n) {
+ int32_t int32_length = DoubleToInt32(n);
+ uint32_t compare_length = static_cast<uint32_t>(int32_length);
+ if (allow_appending) compare_length++;
+ if (key >= compare_length) {
+ PrintF("[OOB %s %s (%s length = %d, element accessed = %d) in ",
+ elements_type, op, elements_type,
+ static_cast<int>(int32_length),
+ static_cast<int>(key));
+ TraceTopFrame(obj->GetIsolate());
+ PrintF("]\n");
+ }
+ } else {
+ PrintF("[%s elements length not integer value in ", elements_type);
+ TraceTopFrame(obj->GetIsolate());
+ PrintF("]\n");
+ }
+ } else {
+ PrintF("[%s elements length not a number in ", elements_type);
+ TraceTopFrame(obj->GetIsolate());
+ PrintF("]\n");
+ }
+}
+
+
+// Base class for element handler implementations. Contains the
+// the common logic for objects with different ElementsKinds.
+// Subclasses must specialize method for which the element
+// implementation differs from the base class implementation.
+//
+// This class is intended to be used in the following way:
+//
+// class SomeElementsAccessor :
+// public ElementsAccessorBase<SomeElementsAccessor,
+// BackingStoreClass> {
+// ...
+// }
+//
+// This is an example of the Curiously Recurring Template Pattern (see
+// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). We use
+// CRTP to guarantee aggressive compile time optimizations (i.e. inlining and
+// specialization of SomeElementsAccessor methods).
+template <typename ElementsAccessorSubclass,
+ typename ElementsTraitsParam>
+class ElementsAccessorBase : public ElementsAccessor {
+ protected:
+ explicit ElementsAccessorBase(const char* name)
+ : ElementsAccessor(name) { }
+
+ typedef ElementsTraitsParam ElementsTraits;
+ typedef typename ElementsTraitsParam::BackingStore BackingStore;
+
+ virtual ElementsKind kind() const { return ElementsTraits::Kind; }
+
+ static void ValidateContents(JSObject* holder, int length) {
+ }
+
+ static void ValidateImpl(JSObject* holder) {
+ FixedArrayBase* fixed_array_base = holder->elements();
+ // When objects are first allocated, its elements are Failures.
+ if (fixed_array_base->IsFailure()) return;
+ if (!fixed_array_base->IsHeapObject()) return;
+ Map* map = fixed_array_base->map();
+ // Arrays that have been shifted in place can't be verified.
+ Heap* heap = holder->GetHeap();
+ if (map == heap->one_pointer_filler_map() ||
+ map == heap->two_pointer_filler_map() ||
+ map == heap->free_space_map()) {
+ return;
+ }
+ int length = 0;
+ if (holder->IsJSArray()) {
+ Object* length_obj = JSArray::cast(holder)->length();
+ if (length_obj->IsSmi()) {
+ length = Smi::cast(length_obj)->value();
+ }
+ } else {
+ length = fixed_array_base->length();
+ }
+ ElementsAccessorSubclass::ValidateContents(holder, length);
+ }
+
+ virtual void Validate(JSObject* holder) {
+ ElementsAccessorSubclass::ValidateImpl(holder);
+ }
+
+ static bool HasElementImpl(Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ return ElementsAccessorSubclass::GetAttributesImpl(
+ receiver, holder, key, backing_store) != ABSENT;
+ }
+
+ virtual bool HasElement(Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (backing_store == NULL) {
+ backing_store = holder->elements();
+ }
+ return ElementsAccessorSubclass::HasElementImpl(
+ receiver, holder, key, backing_store);
+ }
+
+ MUST_USE_RESULT virtual MaybeObject* Get(Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (backing_store == NULL) {
+ backing_store = holder->elements();
+ }
+
+ if (!IsExternalArrayElementsKind(ElementsTraits::Kind) &&
+ FLAG_trace_js_array_abuse) {
+ CheckArrayAbuse(holder, "elements read", key);
+ }
+
+ if (IsExternalArrayElementsKind(ElementsTraits::Kind) &&
+ FLAG_trace_external_array_abuse) {
+ CheckArrayAbuse(holder, "external elements read", key);
+ }
+
+ return ElementsAccessorSubclass::GetImpl(
+ receiver, holder, key, backing_store);
+ }
+
+ MUST_USE_RESULT static MaybeObject* GetImpl(Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ return (key < ElementsAccessorSubclass::GetCapacityImpl(backing_store))
+ ? BackingStore::cast(backing_store)->get(key)
+ : backing_store->GetHeap()->the_hole_value();
+ }
+
+ MUST_USE_RESULT virtual PropertyAttributes GetAttributes(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (backing_store == NULL) {
+ backing_store = holder->elements();
+ }
+ return ElementsAccessorSubclass::GetAttributesImpl(
+ receiver, holder, key, backing_store);
+ }
+
+ MUST_USE_RESULT static PropertyAttributes GetAttributesImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (key >= ElementsAccessorSubclass::GetCapacityImpl(backing_store)) {
+ return ABSENT;
+ }
+ return BackingStore::cast(backing_store)->is_the_hole(key) ? ABSENT : NONE;
+ }
+
+ MUST_USE_RESULT virtual PropertyType GetType(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (backing_store == NULL) {
+ backing_store = holder->elements();
+ }
+ return ElementsAccessorSubclass::GetTypeImpl(
+ receiver, holder, key, backing_store);
+ }
+
+ MUST_USE_RESULT static PropertyType GetTypeImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (key >= ElementsAccessorSubclass::GetCapacityImpl(backing_store)) {
+ return NONEXISTENT;
+ }
+ return BackingStore::cast(backing_store)->is_the_hole(key)
+ ? NONEXISTENT : FIELD;
+ }
+
+ MUST_USE_RESULT virtual AccessorPair* GetAccessorPair(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (backing_store == NULL) {
+ backing_store = holder->elements();
+ }
+ return ElementsAccessorSubclass::GetAccessorPairImpl(
+ receiver, holder, key, backing_store);
+ }
+
+ MUST_USE_RESULT static AccessorPair* GetAccessorPairImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ return NULL;
+ }
+
+ MUST_USE_RESULT virtual MaybeObject* SetLength(JSArray* array,
+ Object* length) {
+ return ElementsAccessorSubclass::SetLengthImpl(
+ array, length, array->elements());
+ }
+
+ MUST_USE_RESULT static MaybeObject* SetLengthImpl(
+ JSObject* obj,
+ Object* length,
+ FixedArrayBase* backing_store);
+
+ MUST_USE_RESULT virtual MaybeObject* SetCapacityAndLength(
+ JSArray* array,
+ int capacity,
+ int length) {
+ return ElementsAccessorSubclass::SetFastElementsCapacityAndLength(
+ array,
+ capacity,
+ length);
+ }
+
+ MUST_USE_RESULT static MaybeObject* SetFastElementsCapacityAndLength(
+ JSObject* obj,
+ int capacity,
+ int length) {
+ UNIMPLEMENTED();
+ return obj;
+ }
+
+ MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* obj,
+ uint32_t key,
+ JSReceiver::DeleteMode mode) = 0;
+
+ MUST_USE_RESULT static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind from_kind,
+ uint32_t to_start,
+ int packed_size,
+ int copy_size) {
+ UNREACHABLE();
+ return NULL;
+ }
+
+ MUST_USE_RESULT virtual MaybeObject* CopyElements(JSObject* from_holder,
+ uint32_t from_start,
+ ElementsKind from_kind,
+ FixedArrayBase* to,
+ uint32_t to_start,
+ int copy_size,
+ FixedArrayBase* from) {
+ int packed_size = kPackedSizeNotKnown;
+ if (from == NULL) {
+ from = from_holder->elements();
+ }
+
+ if (from_holder) {
+ bool is_packed = IsFastPackedElementsKind(from_kind) &&
+ from_holder->IsJSArray();
+ if (is_packed) {
+ packed_size = Smi::cast(JSArray::cast(from_holder)->length())->value();
+ if (copy_size >= 0 && packed_size > copy_size) {
+ packed_size = copy_size;
+ }
+ }
+ }
+ return ElementsAccessorSubclass::CopyElementsImpl(
+ from, from_start, to, from_kind, to_start, packed_size, copy_size);
+ }
+
+ MUST_USE_RESULT virtual MaybeObject* AddElementsToFixedArray(
+ Object* receiver,
+ JSObject* holder,
+ FixedArray* to,
+ FixedArrayBase* from) {
+ int len0 = to->length();
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ for (int i = 0; i < len0; i++) {
+ ASSERT(!to->get(i)->IsTheHole());
+ }
+ }
+#endif
+ if (from == NULL) {
+ from = holder->elements();
+ }
+
+ // Optimize if 'other' is empty.
+ // We cannot optimize if 'this' is empty, as other may have holes.
+ uint32_t len1 = ElementsAccessorSubclass::GetCapacityImpl(from);
+ if (len1 == 0) return to;
+
+ // Compute how many elements are not in other.
+ uint32_t extra = 0;
+ for (uint32_t y = 0; y < len1; y++) {
+ uint32_t key = ElementsAccessorSubclass::GetKeyForIndexImpl(from, y);
+ if (ElementsAccessorSubclass::HasElementImpl(
+ receiver, holder, key, from)) {
+ MaybeObject* maybe_value =
+ ElementsAccessorSubclass::GetImpl(receiver, holder, key, from);
+ Object* value;
+ if (!maybe_value->To(&value)) return maybe_value;
+ ASSERT(!value->IsTheHole());
+ if (!HasKey(to, value)) {
+ extra++;
+ }
+ }
+ }
+
+ if (extra == 0) return to;
+
+ // Allocate the result
+ FixedArray* result;
+ MaybeObject* maybe_obj = from->GetHeap()->AllocateFixedArray(len0 + extra);
+ if (!maybe_obj->To(&result)) return maybe_obj;
+
+ // Fill in the content
+ {
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
+ for (int i = 0; i < len0; i++) {
+ Object* e = to->get(i);
+ ASSERT(e->IsString() || e->IsNumber());
+ result->set(i, e, mode);
+ }
+ }
+ // Fill in the extra values.
+ uint32_t index = 0;
+ for (uint32_t y = 0; y < len1; y++) {
+ uint32_t key =
+ ElementsAccessorSubclass::GetKeyForIndexImpl(from, y);
+ if (ElementsAccessorSubclass::HasElementImpl(
+ receiver, holder, key, from)) {
+ MaybeObject* maybe_value =
+ ElementsAccessorSubclass::GetImpl(receiver, holder, key, from);
+ Object* value;
+ if (!maybe_value->To(&value)) return maybe_value;
+ if (!value->IsTheHole() && !HasKey(to, value)) {
+ result->set(len0 + index, value);
+ index++;
+ }
+ }
+ }
+ ASSERT(extra == index);
+ return result;
+ }
+
+ protected:
+ static uint32_t GetCapacityImpl(FixedArrayBase* backing_store) {
+ return backing_store->length();
+ }
+
+ virtual uint32_t GetCapacity(FixedArrayBase* backing_store) {
+ return ElementsAccessorSubclass::GetCapacityImpl(backing_store);
+ }
+
+ static uint32_t GetKeyForIndexImpl(FixedArrayBase* backing_store,
+ uint32_t index) {
+ return index;
+ }
+
+ virtual uint32_t GetKeyForIndex(FixedArrayBase* backing_store,
+ uint32_t index) {
+ return ElementsAccessorSubclass::GetKeyForIndexImpl(backing_store, index);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ElementsAccessorBase);
+};
+
+
+// Super class for all fast element arrays.
+template<typename FastElementsAccessorSubclass,
+ typename KindTraits,
+ int ElementSize>
+class FastElementsAccessor
+ : public ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits> {
+ public:
+ explicit FastElementsAccessor(const char* name)
+ : ElementsAccessorBase<FastElementsAccessorSubclass,
+ KindTraits>(name) {}
+ protected:
+ friend class ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits>;
+ friend class NonStrictArgumentsElementsAccessor;
+
+ typedef typename KindTraits::BackingStore 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(FixedArrayBase* backing_store,
+ JSArray* array,
+ Object* length_object,
+ uint32_t length) {
+ uint32_t old_capacity = backing_store->length();
+ Object* old_length = array->length();
+ bool same_or_smaller_size = old_length->IsSmi() &&
+ static_cast<uint32_t>(Smi::cast(old_length)->value()) >= length;
+ ElementsKind kind = array->GetElementsKind();
+
+ if (!same_or_smaller_size && IsFastElementsKind(kind) &&
+ !IsFastHoleyElementsKind(kind)) {
+ kind = GetHoleyElementsKind(kind);
+ MaybeObject* maybe_obj = array->TransitionElementsKind(kind);
+ if (maybe_obj->IsFailure()) return maybe_obj;
+ }
+
+ // Check whether the backing store should be shrunk.
+ if (length <= old_capacity) {
+ if (array->HasFastSmiOrObjectElements()) {
+ 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 = FastD2IChecked(array->length()->Number());
+ for (int i = length; i < old_length; i++) {
+ BackingStore::cast(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;
+ array->ValidateElements();
+ return length_object;
+ }
+
+ // Request conversion to slow elements.
+ return array->GetHeap()->undefined_value();
+ }
+
+ static MaybeObject* DeleteCommon(JSObject* obj,
+ uint32_t key,
+ JSReceiver::DeleteMode mode) {
+ ASSERT(obj->HasFastSmiOrObjectElements() ||
+ obj->HasFastDoubleElements() ||
+ obj->HasFastArgumentsElements());
+ Heap* heap = obj->GetHeap();
+ Object* elements = obj->elements();
+ if (elements == heap->empty_fixed_array()) {
+ return heap->true_value();
+ }
+ typename KindTraits::BackingStore* backing_store =
+ KindTraits::BackingStore::cast(elements);
+ bool is_non_strict_arguments_elements_map =
+ backing_store->map() == heap->non_strict_arguments_elements_map();
+ if (is_non_strict_arguments_elements_map) {
+ backing_store = KindTraits::BackingStore::cast(
+ FixedArray::cast(backing_store)->get(1));
+ }
+ uint32_t length = static_cast<uint32_t>(
+ obj->IsJSArray()
+ ? Smi::cast(JSArray::cast(obj)->length())->value()
+ : backing_store->length());
+ if (key < length) {
+ if (!is_non_strict_arguments_elements_map) {
+ ElementsKind kind = KindTraits::Kind;
+ if (IsFastPackedElementsKind(kind)) {
+ MaybeObject* transitioned =
+ obj->TransitionElementsKind(GetHoleyElementsKind(kind));
+ if (transitioned->IsFailure()) return transitioned;
+ }
+ if (IsFastSmiOrObjectElementsKind(KindTraits::Kind)) {
+ Object* writable;
+ MaybeObject* maybe = obj->EnsureWritableFastElements();
+ if (!maybe->ToObject(&writable)) return maybe;
+ backing_store = KindTraits::BackingStore::cast(writable);
+ }
+ }
+ backing_store->set_the_hole(key);
+ // If an old space backing store is larger than a certain size and
+ // has too few used values, normalize it.
+ // To avoid doing the check on every delete we require at least
+ // one adjacent hole to the value being deleted.
+ const int kMinLengthForSparsenessCheck = 64;
+ if (backing_store->length() >= kMinLengthForSparsenessCheck &&
+ !heap->InNewSpace(backing_store) &&
+ ((key > 0 && backing_store->is_the_hole(key - 1)) ||
+ (key + 1 < length && backing_store->is_the_hole(key + 1)))) {
+ int num_used = 0;
+ for (int i = 0; i < backing_store->length(); ++i) {
+ if (!backing_store->is_the_hole(i)) ++num_used;
+ // Bail out early if more than 1/4 is used.
+ if (4 * num_used > backing_store->length()) break;
+ }
+ if (4 * num_used <= backing_store->length()) {
+ MaybeObject* result = obj->NormalizeElements();
+ if (result->IsFailure()) return result;
+ }
+ }
+ }
+ return heap->true_value();
+ }
+
+ virtual MaybeObject* Delete(JSObject* obj,
+ uint32_t key,
+ JSReceiver::DeleteMode mode) {
+ return DeleteCommon(obj, key, mode);
+ }
+
+ static bool HasElementImpl(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ if (key >= static_cast<uint32_t>(backing_store->length())) {
+ return false;
+ }
+ return !BackingStore::cast(backing_store)->is_the_hole(key);
+ }
+
+ static void ValidateContents(JSObject* holder, int length) {
+#if DEBUG
+ FixedArrayBase* elements = holder->elements();
+ Heap* heap = elements->GetHeap();
+ Map* map = elements->map();
+ ASSERT((IsFastSmiOrObjectElementsKind(KindTraits::Kind) &&
+ (map == heap->fixed_array_map() ||
+ map == heap->fixed_cow_array_map())) ||
+ (IsFastDoubleElementsKind(KindTraits::Kind) ==
+ ((map == heap->fixed_array_map() && length == 0) ||
+ map == heap->fixed_double_array_map())));
+ for (int i = 0; i < length; i++) {
+ typename KindTraits::BackingStore* backing_store =
+ KindTraits::BackingStore::cast(elements);
+ ASSERT((!IsFastSmiElementsKind(KindTraits::Kind) ||
+ static_cast<Object*>(backing_store->get(i))->IsSmi()) ||
+ (IsFastHoleyElementsKind(KindTraits::Kind) ==
+ backing_store->is_the_hole(i)));
+ }
+#endif
+ }
+};
+
+
+static inline ElementsKind ElementsKindForArray(FixedArrayBase* array) {
+ switch (array->map()->instance_type()) {
+ case FIXED_ARRAY_TYPE:
+ if (array->IsDictionary()) {
+ return DICTIONARY_ELEMENTS;
+ } else {
+ return FAST_HOLEY_ELEMENTS;
+ }
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ return FAST_HOLEY_DOUBLE_ELEMENTS;
+ case EXTERNAL_BYTE_ARRAY_TYPE:
+ return EXTERNAL_BYTE_ELEMENTS;
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ return EXTERNAL_UNSIGNED_BYTE_ELEMENTS;
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ return EXTERNAL_SHORT_ELEMENTS;
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ return EXTERNAL_UNSIGNED_SHORT_ELEMENTS;
+ case EXTERNAL_INT_ARRAY_TYPE:
+ return EXTERNAL_INT_ELEMENTS;
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ return EXTERNAL_UNSIGNED_INT_ELEMENTS;
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ return EXTERNAL_FLOAT_ELEMENTS;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ return EXTERNAL_DOUBLE_ELEMENTS;
+ case EXTERNAL_PIXEL_ARRAY_TYPE:
+ return EXTERNAL_PIXEL_ELEMENTS;
+ default:
+ UNREACHABLE();
+ }
+ return FAST_HOLEY_ELEMENTS;
+}
+
+
+template<typename FastElementsAccessorSubclass,
+ typename KindTraits>
+class FastSmiOrObjectElementsAccessor
+ : public FastElementsAccessor<FastElementsAccessorSubclass,
+ KindTraits,
+ kPointerSize> {
+ public:
+ explicit FastSmiOrObjectElementsAccessor(const char* name)
+ : FastElementsAccessor<FastElementsAccessorSubclass,
+ KindTraits,
+ kPointerSize>(name) {}
+
+ static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind from_kind,
+ uint32_t to_start,
+ int packed_size,
+ int copy_size) {
+ ElementsKind to_kind = KindTraits::Kind;
+ switch (from_kind) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ CopyObjectToObjectElements(
+ from, from_kind, from_start, to, to_kind, to_start, copy_size);
+ return to->GetHeap()->undefined_value();
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ return CopyDoubleToObjectElements(
+ from, from_start, to, to_kind, to_start, copy_size);
+ case DICTIONARY_ELEMENTS:
+ CopyDictionaryToObjectElements(
+ from, from_start, to, to_kind, to_start, copy_size);
+ return to->GetHeap()->undefined_value();
+ case NON_STRICT_ARGUMENTS_ELEMENTS: {
+ // TODO(verwaest): This is a temporary hack to support extending
+ // NON_STRICT_ARGUMENTS_ELEMENTS in SetFastElementsCapacityAndLength.
+ // This case should be UNREACHABLE().
+ FixedArray* parameter_map = FixedArray::cast(from);
+ FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1));
+ ElementsKind from_kind = ElementsKindForArray(arguments);
+ return CopyElementsImpl(arguments, from_start, to, from_kind,
+ to_start, packed_size, copy_size);
+ }
+ 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();
+ }
+ return NULL;
+ }
+
+
+ static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
+ uint32_t capacity,
+ uint32_t length) {
+ JSObject::SetFastElementsCapacitySmiMode set_capacity_mode =
+ obj->HasFastSmiElements()
+ ? JSObject::kAllowSmiElements
+ : JSObject::kDontAllowSmiElements;
+ return obj->SetFastElementsCapacityAndLength(capacity,
+ length,
+ set_capacity_mode);
+ }
+};
+
+
+class FastPackedSmiElementsAccessor
+ : public FastSmiOrObjectElementsAccessor<
+ FastPackedSmiElementsAccessor,
+ ElementsKindTraits<FAST_SMI_ELEMENTS> > {
+ public:
+ explicit FastPackedSmiElementsAccessor(const char* name)
+ : FastSmiOrObjectElementsAccessor<
+ FastPackedSmiElementsAccessor,
+ ElementsKindTraits<FAST_SMI_ELEMENTS> >(name) {}
+};
+
+
+class FastHoleySmiElementsAccessor
+ : public FastSmiOrObjectElementsAccessor<
+ FastHoleySmiElementsAccessor,
+ ElementsKindTraits<FAST_HOLEY_SMI_ELEMENTS> > {
+ public:
+ explicit FastHoleySmiElementsAccessor(const char* name)
+ : FastSmiOrObjectElementsAccessor<
+ FastHoleySmiElementsAccessor,
+ ElementsKindTraits<FAST_HOLEY_SMI_ELEMENTS> >(name) {}
+};
+
+
+class FastPackedObjectElementsAccessor
+ : public FastSmiOrObjectElementsAccessor<
+ FastPackedObjectElementsAccessor,
+ ElementsKindTraits<FAST_ELEMENTS> > {
+ public:
+ explicit FastPackedObjectElementsAccessor(const char* name)
+ : FastSmiOrObjectElementsAccessor<
+ FastPackedObjectElementsAccessor,
+ ElementsKindTraits<FAST_ELEMENTS> >(name) {}
+};
+
+
+class FastHoleyObjectElementsAccessor
+ : public FastSmiOrObjectElementsAccessor<
+ FastHoleyObjectElementsAccessor,
+ ElementsKindTraits<FAST_HOLEY_ELEMENTS> > {
+ public:
+ explicit FastHoleyObjectElementsAccessor(const char* name)
+ : FastSmiOrObjectElementsAccessor<
+ FastHoleyObjectElementsAccessor,
+ ElementsKindTraits<FAST_HOLEY_ELEMENTS> >(name) {}
+};
+
+
+template<typename FastElementsAccessorSubclass,
+ typename KindTraits>
+class FastDoubleElementsAccessor
+ : public FastElementsAccessor<FastElementsAccessorSubclass,
+ KindTraits,
+ kDoubleSize> {
+ public:
+ explicit FastDoubleElementsAccessor(const char* name)
+ : FastElementsAccessor<FastElementsAccessorSubclass,
+ KindTraits,
+ kDoubleSize>(name) {}
+
+ static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
+ uint32_t capacity,
+ uint32_t length) {
+ return obj->SetFastDoubleElementsCapacityAndLength(capacity,
+ length);
+ }
+
+ protected:
+ static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind from_kind,
+ uint32_t to_start,
+ int packed_size,
+ int copy_size) {
+ switch (from_kind) {
+ case FAST_SMI_ELEMENTS:
+ CopyPackedSmiToDoubleElements(
+ from, from_start, to, to_start, packed_size, copy_size);
+ break;
+ case FAST_HOLEY_SMI_ELEMENTS:
+ CopySmiToDoubleElements(from, from_start, to, to_start, copy_size);
+ break;
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ CopyDoubleToDoubleElements(from, from_start, to, to_start, copy_size);
+ break;
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ CopyObjectToDoubleElements(from, from_start, to, to_start, copy_size);
+ break;
+ case DICTIONARY_ELEMENTS:
+ CopyDictionaryToDoubleElements(
+ from, from_start, to, to_start, copy_size);
+ break;
+ 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();
+ }
+ return to->GetHeap()->undefined_value();
+ }
+};
+
+
+class FastPackedDoubleElementsAccessor
+ : public FastDoubleElementsAccessor<
+ FastPackedDoubleElementsAccessor,
+ ElementsKindTraits<FAST_DOUBLE_ELEMENTS> > {
+ public:
+ friend class ElementsAccessorBase<FastPackedDoubleElementsAccessor,
+ ElementsKindTraits<FAST_DOUBLE_ELEMENTS> >;
+ explicit FastPackedDoubleElementsAccessor(const char* name)
+ : FastDoubleElementsAccessor<
+ FastPackedDoubleElementsAccessor,
+ ElementsKindTraits<FAST_DOUBLE_ELEMENTS> >(name) {}
+};
+
+
+class FastHoleyDoubleElementsAccessor
+ : public FastDoubleElementsAccessor<
+ FastHoleyDoubleElementsAccessor,
+ ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> > {
+ public:
+ friend class ElementsAccessorBase<
+ FastHoleyDoubleElementsAccessor,
+ ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> >;
+ explicit FastHoleyDoubleElementsAccessor(const char* name)
+ : FastDoubleElementsAccessor<
+ FastHoleyDoubleElementsAccessor,
+ ElementsKindTraits<FAST_HOLEY_DOUBLE_ELEMENTS> >(name) {}
+};
+
+
+// Super class for all external element arrays.
+template<typename ExternalElementsAccessorSubclass,
+ ElementsKind Kind>
+class ExternalElementsAccessor
+ : public ElementsAccessorBase<ExternalElementsAccessorSubclass,
+ ElementsKindTraits<Kind> > {
+ public:
+ explicit ExternalElementsAccessor(const char* name)
+ : ElementsAccessorBase<ExternalElementsAccessorSubclass,
+ ElementsKindTraits<Kind> >(name) {}
+
+ protected:
+ typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore;
+
+ friend class ElementsAccessorBase<ExternalElementsAccessorSubclass,
+ ElementsKindTraits<Kind> >;
+
+ MUST_USE_RESULT static MaybeObject* GetImpl(Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ return
+ key < ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store)
+ ? BackingStore::cast(backing_store)->get(key)
+ : backing_store->GetHeap()->undefined_value();
+ }
+
+ MUST_USE_RESULT static PropertyAttributes GetAttributesImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ return
+ key < ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store)
+ ? NONE : ABSENT;
+ }
+
+ MUST_USE_RESULT static PropertyType GetTypeImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ return
+ key < ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store)
+ ? FIELD : NONEXISTENT;
+ }
+
+ MUST_USE_RESULT static MaybeObject* SetLengthImpl(
+ JSObject* obj,
+ Object* length,
+ FixedArrayBase* backing_store) {
+ // External arrays do not support changing their length.
+ UNREACHABLE();
+ return obj;
+ }
+
+ MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* obj,
+ uint32_t key,
+ JSReceiver::DeleteMode mode) {
+ // External arrays always ignore deletes.
+ return obj->GetHeap()->true_value();
+ }
+
+ static bool HasElementImpl(Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ uint32_t capacity =
+ ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store);
+ return key < capacity;
+ }
+};
+
+
+class ExternalByteElementsAccessor
+ : public ExternalElementsAccessor<ExternalByteElementsAccessor,
+ EXTERNAL_BYTE_ELEMENTS> {
+ public:
+ explicit ExternalByteElementsAccessor(const char* name)
+ : ExternalElementsAccessor<ExternalByteElementsAccessor,
+ EXTERNAL_BYTE_ELEMENTS>(name) {}
+};
+
+
+class ExternalUnsignedByteElementsAccessor
+ : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
+ EXTERNAL_UNSIGNED_BYTE_ELEMENTS> {
+ public:
+ explicit ExternalUnsignedByteElementsAccessor(const char* name)
+ : ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
+ EXTERNAL_UNSIGNED_BYTE_ELEMENTS>(name) {}
+};
+
+
+class ExternalShortElementsAccessor
+ : public ExternalElementsAccessor<ExternalShortElementsAccessor,
+ EXTERNAL_SHORT_ELEMENTS> {
+ public:
+ explicit ExternalShortElementsAccessor(const char* name)
+ : ExternalElementsAccessor<ExternalShortElementsAccessor,
+ EXTERNAL_SHORT_ELEMENTS>(name) {}
+};
+
+
+class ExternalUnsignedShortElementsAccessor
+ : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
+ EXTERNAL_UNSIGNED_SHORT_ELEMENTS> {
+ public:
+ explicit ExternalUnsignedShortElementsAccessor(const char* name)
+ : ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
+ EXTERNAL_UNSIGNED_SHORT_ELEMENTS>(name) {}
+};
+
+
+class ExternalIntElementsAccessor
+ : public ExternalElementsAccessor<ExternalIntElementsAccessor,
+ EXTERNAL_INT_ELEMENTS> {
+ public:
+ explicit ExternalIntElementsAccessor(const char* name)
+ : ExternalElementsAccessor<ExternalIntElementsAccessor,
+ EXTERNAL_INT_ELEMENTS>(name) {}
+};
+
+
+class ExternalUnsignedIntElementsAccessor
+ : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
+ EXTERNAL_UNSIGNED_INT_ELEMENTS> {
+ public:
+ explicit ExternalUnsignedIntElementsAccessor(const char* name)
+ : ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
+ EXTERNAL_UNSIGNED_INT_ELEMENTS>(name) {}
+};
+
+
+class ExternalFloatElementsAccessor
+ : public ExternalElementsAccessor<ExternalFloatElementsAccessor,
+ EXTERNAL_FLOAT_ELEMENTS> {
+ public:
+ explicit ExternalFloatElementsAccessor(const char* name)
+ : ExternalElementsAccessor<ExternalFloatElementsAccessor,
+ EXTERNAL_FLOAT_ELEMENTS>(name) {}
+};
+
+
+class ExternalDoubleElementsAccessor
+ : public ExternalElementsAccessor<ExternalDoubleElementsAccessor,
+ EXTERNAL_DOUBLE_ELEMENTS> {
+ public:
+ explicit ExternalDoubleElementsAccessor(const char* name)
+ : ExternalElementsAccessor<ExternalDoubleElementsAccessor,
+ EXTERNAL_DOUBLE_ELEMENTS>(name) {}
+};
+
+
+class PixelElementsAccessor
+ : public ExternalElementsAccessor<PixelElementsAccessor,
+ EXTERNAL_PIXEL_ELEMENTS> {
+ public:
+ explicit PixelElementsAccessor(const char* name)
+ : ExternalElementsAccessor<PixelElementsAccessor,
+ EXTERNAL_PIXEL_ELEMENTS>(name) {}
+};
+
+
+class DictionaryElementsAccessor
+ : public ElementsAccessorBase<DictionaryElementsAccessor,
+ ElementsKindTraits<DICTIONARY_ELEMENTS> > {
+ public:
+ explicit DictionaryElementsAccessor(const char* name)
+ : ElementsAccessorBase<DictionaryElementsAccessor,
+ ElementsKindTraits<DICTIONARY_ELEMENTS> >(name) {}
+
+ // Adjusts the length of the dictionary backing store and returns the new
+ // length according to ES5 section 15.4.5.2 behavior.
+ MUST_USE_RESULT static MaybeObject* SetLengthWithoutNormalize(
+ FixedArrayBase* store,
+ JSArray* array,
+ Object* length_object,
+ uint32_t length) {
+ SeededNumberDictionary* dict = SeededNumberDictionary::cast(store);
+ Heap* heap = array->GetHeap();
+ int capacity = dict->Capacity();
+ 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.
+ 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;
+ }
+ }
+
+ if (new_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 {
+ // 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;
+ }
+
+ MUST_USE_RESULT static MaybeObject* DeleteCommon(
+ JSObject* obj,
+ uint32_t key,
+ JSReceiver::DeleteMode mode) {
+ Isolate* isolate = obj->GetIsolate();
+ Heap* heap = isolate->heap();
+ FixedArray* backing_store = FixedArray::cast(obj->elements());
+ bool is_arguments =
+ (obj->GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS);
+ if (is_arguments) {
+ backing_store = FixedArray::cast(backing_store->get(1));
+ }
+ SeededNumberDictionary* dictionary =
+ SeededNumberDictionary::cast(backing_store);
+ int entry = dictionary->FindEntry(key);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ Object* result = dictionary->DeleteProperty(entry, mode);
+ if (result == heap->false_value()) {
+ if (mode == JSObject::STRICT_DELETION) {
+ // Deleting a non-configurable property in strict mode.
+ HandleScope scope(isolate);
+ Handle<Object> holder(obj, isolate);
+ Handle<Object> name = isolate->factory()->NewNumberFromUint(key);
+ Handle<Object> args[2] = { name, holder };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("strict_delete_property",
+ HandleVector(args, 2));
+ return isolate->Throw(*error);
+ }
+ return heap->false_value();
+ }
+ MaybeObject* maybe_elements = dictionary->Shrink(key);
+ FixedArray* new_elements = NULL;
+ if (!maybe_elements->To(&new_elements)) {
+ return maybe_elements;
+ }
+ if (is_arguments) {
+ FixedArray::cast(obj->elements())->set(1, new_elements);
+ } else {
+ obj->set_elements(new_elements);
+ }
+ }
+ return heap->true_value();
+ }
+
+ MUST_USE_RESULT static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind from_kind,
+ uint32_t to_start,
+ int packed_size,
+ int copy_size) {
+ UNREACHABLE();
+ return NULL;
+ }
+
+
+ protected:
+ friend class ElementsAccessorBase<DictionaryElementsAccessor,
+ ElementsKindTraits<DICTIONARY_ELEMENTS> >;
+
+ MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* obj,
+ uint32_t key,
+ JSReceiver::DeleteMode mode) {
+ return DeleteCommon(obj, key, mode);
+ }
+
+ MUST_USE_RESULT static MaybeObject* GetImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* store) {
+ SeededNumberDictionary* backing_store = SeededNumberDictionary::cast(store);
+ int entry = backing_store->FindEntry(key);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ Object* element = backing_store->ValueAt(entry);
+ PropertyDetails details = backing_store->DetailsAt(entry);
+ if (details.type() == CALLBACKS) {
+ return obj->GetElementWithCallback(receiver,
+ element,
+ key,
+ obj);
+ } else {
+ return element;
+ }
+ }
+ return obj->GetHeap()->the_hole_value();
+ }
+
+ MUST_USE_RESULT static PropertyAttributes GetAttributesImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ SeededNumberDictionary* dictionary =
+ SeededNumberDictionary::cast(backing_store);
+ int entry = dictionary->FindEntry(key);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ return dictionary->DetailsAt(entry).attributes();
+ }
+ return ABSENT;
+ }
+
+ MUST_USE_RESULT static PropertyType GetTypeImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* store) {
+ SeededNumberDictionary* backing_store = SeededNumberDictionary::cast(store);
+ int entry = backing_store->FindEntry(key);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ return backing_store->DetailsAt(entry).type();
+ }
+ return NONEXISTENT;
+ }
+
+ MUST_USE_RESULT static AccessorPair* GetAccessorPairImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* store) {
+ SeededNumberDictionary* backing_store = SeededNumberDictionary::cast(store);
+ int entry = backing_store->FindEntry(key);
+ if (entry != SeededNumberDictionary::kNotFound &&
+ backing_store->DetailsAt(entry).type() == CALLBACKS &&
+ backing_store->ValueAt(entry)->IsAccessorPair()) {
+ return AccessorPair::cast(backing_store->ValueAt(entry));
+ }
+ return NULL;
+ }
+
+ static bool HasElementImpl(Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ return SeededNumberDictionary::cast(backing_store)->FindEntry(key) !=
+ SeededNumberDictionary::kNotFound;
+ }
+
+ static uint32_t GetKeyForIndexImpl(FixedArrayBase* store,
+ uint32_t index) {
+ SeededNumberDictionary* dict = SeededNumberDictionary::cast(store);
+ Object* key = dict->KeyAt(index);
+ return Smi::cast(key)->value();
+ }
+};
+
+
+class NonStrictArgumentsElementsAccessor : public ElementsAccessorBase<
+ NonStrictArgumentsElementsAccessor,
+ ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> > {
+ public:
+ explicit NonStrictArgumentsElementsAccessor(const char* name)
+ : ElementsAccessorBase<
+ NonStrictArgumentsElementsAccessor,
+ ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> >(name) {}
+ protected:
+ friend class ElementsAccessorBase<
+ NonStrictArgumentsElementsAccessor,
+ ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> >;
+
+ MUST_USE_RESULT static MaybeObject* GetImpl(Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* parameters) {
+ FixedArray* parameter_map = FixedArray::cast(parameters);
+ Object* probe = GetParameterMapArg(obj, parameter_map, key);
+ if (!probe->IsTheHole()) {
+ Context* context = Context::cast(parameter_map->get(0));
+ int context_index = Smi::cast(probe)->value();
+ ASSERT(!context->get(context_index)->IsTheHole());
+ return context->get(context_index);
+ } else {
+ // Object is not mapped, defer to the arguments.
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ MaybeObject* maybe_result = ElementsAccessor::ForArray(arguments)->Get(
+ receiver, obj, key, arguments);
+ Object* result;
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ // Elements of the arguments object in slow mode might be slow aliases.
+ if (result->IsAliasedArgumentsEntry()) {
+ AliasedArgumentsEntry* entry = AliasedArgumentsEntry::cast(result);
+ Context* context = Context::cast(parameter_map->get(0));
+ int context_index = entry->aliased_context_slot();
+ ASSERT(!context->get(context_index)->IsTheHole());
+ return context->get(context_index);
+ } else {
+ return result;
+ }
+ }
+ }
+
+ MUST_USE_RESULT static PropertyAttributes GetAttributesImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* backing_store) {
+ FixedArray* parameter_map = FixedArray::cast(backing_store);
+ Object* probe = GetParameterMapArg(obj, parameter_map, key);
+ if (!probe->IsTheHole()) {
+ return NONE;
+ } else {
+ // If not aliased, check the arguments.
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ return ElementsAccessor::ForArray(arguments)->GetAttributes(
+ receiver, obj, key, arguments);
+ }
+ }
+
+ MUST_USE_RESULT static PropertyType GetTypeImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* parameters) {
+ FixedArray* parameter_map = FixedArray::cast(parameters);
+ Object* probe = GetParameterMapArg(obj, parameter_map, key);
+ if (!probe->IsTheHole()) {
+ return FIELD;
+ } else {
+ // If not aliased, check the arguments.
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ return ElementsAccessor::ForArray(arguments)->GetType(
+ receiver, obj, key, arguments);
+ }
+ }
+
+ MUST_USE_RESULT static AccessorPair* GetAccessorPairImpl(
+ Object* receiver,
+ JSObject* obj,
+ uint32_t key,
+ FixedArrayBase* parameters) {
+ FixedArray* parameter_map = FixedArray::cast(parameters);
+ Object* probe = GetParameterMapArg(obj, parameter_map, key);
+ if (!probe->IsTheHole()) {
+ return NULL;
+ } else {
+ // If not aliased, check the arguments.
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ return ElementsAccessor::ForArray(arguments)->GetAccessorPair(
+ receiver, obj, key, arguments);
+ }
+ }
+
+ MUST_USE_RESULT static MaybeObject* SetLengthImpl(
+ JSObject* obj,
+ Object* length,
+ FixedArrayBase* parameter_map) {
+ // TODO(mstarzinger): This was never implemented but will be used once we
+ // correctly implement [[DefineOwnProperty]] on arrays.
+ UNIMPLEMENTED();
+ return obj;
+ }
+
+ MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* obj,
+ uint32_t key,
+ JSReceiver::DeleteMode mode) {
+ FixedArray* parameter_map = FixedArray::cast(obj->elements());
+ Object* probe = GetParameterMapArg(obj, parameter_map, key);
+ if (!probe->IsTheHole()) {
+ // TODO(kmillikin): We could check if this was the last aliased
+ // parameter, and revert to normal elements in that case. That
+ // would enable GC of the context.
+ parameter_map->set_the_hole(key + 2);
+ } else {
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ if (arguments->IsDictionary()) {
+ return DictionaryElementsAccessor::DeleteCommon(obj, key, mode);
+ } else {
+ // It's difficult to access the version of DeleteCommon that is declared
+ // in the templatized super class, call the concrete implementation in
+ // the class for the most generalized ElementsKind subclass.
+ return FastHoleyObjectElementsAccessor::DeleteCommon(obj, key, mode);
+ }
+ }
+ return obj->GetHeap()->true_value();
+ }
+
+ MUST_USE_RESULT static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+ uint32_t from_start,
+ FixedArrayBase* to,
+ ElementsKind from_kind,
+ uint32_t to_start,
+ int packed_size,
+ int copy_size) {
+ UNREACHABLE();
+ return NULL;
+ }
+
+ static uint32_t GetCapacityImpl(FixedArrayBase* backing_store) {
+ FixedArray* parameter_map = FixedArray::cast(backing_store);
+ FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1));
+ return Max(static_cast<uint32_t>(parameter_map->length() - 2),
+ ForArray(arguments)->GetCapacity(arguments));
+ }
+
+ static uint32_t GetKeyForIndexImpl(FixedArrayBase* dict,
+ uint32_t index) {
+ return index;
+ }
+
+ static bool HasElementImpl(Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* parameters) {
+ FixedArray* parameter_map = FixedArray::cast(parameters);
+ Object* probe = GetParameterMapArg(holder, parameter_map, key);
+ if (!probe->IsTheHole()) {
+ return true;
+ } else {
+ FixedArrayBase* arguments =
+ FixedArrayBase::cast(FixedArray::cast(parameter_map)->get(1));
+ ElementsAccessor* accessor = ElementsAccessor::ForArray(arguments);
+ return !accessor->Get(receiver, holder, key, arguments)->IsTheHole();
+ }
+ }
+
+ private:
+ static Object* GetParameterMapArg(JSObject* holder,
+ FixedArray* parameter_map,
+ uint32_t key) {
+ uint32_t length = holder->IsJSArray()
+ ? Smi::cast(JSArray::cast(holder)->length())->value()
+ : parameter_map->length();
+ return key < (length - 2)
+ ? parameter_map->get(key + 2)
+ : parameter_map->GetHeap()->the_hole_value();
+ }
+};
+
+
+ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) {
+ return elements_accessors_[ElementsKindForArray(array)];
+}
+
+
+void ElementsAccessor::InitializeOncePerProcess() {
+ static ElementsAccessor* accessor_array[] = {
+#define ACCESSOR_ARRAY(Class, Kind, Store) new Class(#Kind),
+ ELEMENTS_LIST(ACCESSOR_ARRAY)
+#undef ACCESSOR_ARRAY
+ };
+
+ STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) ==
+ kElementsKindCount);
+
+ elements_accessors_ = accessor_array;
+}
+
+
+void ElementsAccessor::TearDown() {
+#define ACCESSOR_DELETE(Class, Kind, Store) delete elements_accessors_[Kind];
+ ELEMENTS_LIST(ACCESSOR_DELETE)
+#undef ACCESSOR_DELETE
+ elements_accessors_ = NULL;
+}
+
+
+template <typename ElementsAccessorSubclass, typename ElementsKindTraits>
+MUST_USE_RESULT MaybeObject* ElementsAccessorBase<ElementsAccessorSubclass,
+ ElementsKindTraits>::
+ SetLengthImpl(JSObject* obj,
+ Object* length,
+ FixedArrayBase* backing_store) {
+ 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);
+ { MaybeObject* result = array->SetContent(new_backing_store);
+ if (result->IsFailure()) return result;
+ }
+ return array;
+}
+
+
+MUST_USE_RESULT MaybeObject* ArrayConstructInitializeElements(
+ JSArray* array, Arguments* args) {
+ Heap* heap = array->GetIsolate()->heap();
+
+ // Optimize the case where there is one argument and the argument is a
+ // small smi.
+ if (args->length() == 1) {
+ Object* obj = (*args)[0];
+ if (obj->IsSmi()) {
+ int len = Smi::cast(obj)->value();
+ if (len > 0 && len < JSObject::kInitialMaxFastElementArray) {
+ ElementsKind elements_kind = array->GetElementsKind();
+ MaybeObject* maybe_array = array->Initialize(len, len);
+ if (maybe_array->IsFailure()) return maybe_array;
+
+ if (!IsFastHoleyElementsKind(elements_kind)) {
+ elements_kind = GetHoleyElementsKind(elements_kind);
+ maybe_array = array->TransitionElementsKind(elements_kind);
+ if (maybe_array->IsFailure()) return maybe_array;
+ }
+
+ return array;
+ } else if (len == 0) {
+ return array->Initialize(JSArray::kPreallocatedArrayElements);
+ }
+ }
+
+ // Take the argument as the length.
+ MaybeObject* maybe_obj = array->Initialize(0);
+ if (!maybe_obj->To(&obj)) return maybe_obj;
+
+ return array->SetElementsLength((*args)[0]);
+ }
+
+ // Optimize the case where there are no parameters passed.
+ if (args->length() == 0) {
+ return array->Initialize(JSArray::kPreallocatedArrayElements);
+ }
+
+ // Set length and elements on the array.
+ int number_of_elements = args->length();
+ MaybeObject* maybe_object =
+ array->EnsureCanContainElements(args, 0, 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 (IsFastDoubleElementsKind(elements_kind)) {
+ maybe_elms = heap->AllocateUninitializedFixedDoubleArray(
+ number_of_elements);
+ } else {
+ maybe_elms = heap->AllocateFixedArrayWithHoles(number_of_elements);
+ }
+ FixedArrayBase* elms;
+ if (!maybe_elms->To(&elms)) return maybe_elms;
+
+ // Fill in the content
+ switch (array->GetElementsKind()) {
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_SMI_ELEMENTS: {
+ FixedArray* smi_elms = FixedArray::cast(elms);
+ for (int index = 0; index < number_of_elements; index++) {
+ smi_elms->set(index, (*args)[index], SKIP_WRITE_BARRIER);
+ }
+ break;
+ }
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_ELEMENTS: {
+ DisallowHeapAllocation 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], mode);
+ }
+ break;
+ }
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ 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]->Number());
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ array->set_elements(elms);
+ array->set_length(Smi::FromInt(number_of_elements));
+ return array;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/elements.h b/chromium/v8/src/elements.h
new file mode 100644
index 00000000000..6353aaecf5c
--- /dev/null
+++ b/chromium/v8/src/elements.h
@@ -0,0 +1,208 @@
+// Copyright 2012 the V8 project authors. 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_ELEMENTS_H_
+#define V8_ELEMENTS_H_
+
+#include "elements-kind.h"
+#include "objects.h"
+#include "heap.h"
+#include "isolate.h"
+
+namespace v8 {
+namespace internal {
+
+// Abstract base class for handles that can operate on objects with differing
+// ElementsKinds.
+class ElementsAccessor {
+ public:
+ explicit ElementsAccessor(const char* name) : name_(name) { }
+ virtual ~ElementsAccessor() { }
+
+ virtual ElementsKind kind() const = 0;
+ const char* name() const { return name_; }
+
+ // Checks the elements of an object for consistency, asserting when a problem
+ // is found.
+ virtual void Validate(JSObject* obj) = 0;
+
+ // Returns true if a holder contains an element with the specified key
+ // without iterating up the prototype chain. The caller can optionally pass
+ // in the backing store to use for the check, which must be compatible with
+ // the ElementsKind of the ElementsAccessor. If backing_store is NULL, the
+ // holder->elements() is used as the backing store.
+ virtual bool HasElement(Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store = NULL) = 0;
+
+ // Returns the element with the specified key or undefined if there is no such
+ // element. This method doesn't iterate up the prototype chain. The caller
+ // can optionally pass in the backing store to use for the check, which must
+ // be compatible with the ElementsKind of the ElementsAccessor. If
+ // backing_store is NULL, the holder->elements() is used as the backing store.
+ MUST_USE_RESULT virtual MaybeObject* Get(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store = NULL) = 0;
+
+ // Returns an element's attributes, or ABSENT if there is no such
+ // element. This method doesn't iterate up the prototype chain. The caller
+ // can optionally pass in the backing store to use for the check, which must
+ // be compatible with the ElementsKind of the ElementsAccessor. If
+ // backing_store is NULL, the holder->elements() is used as the backing store.
+ MUST_USE_RESULT virtual PropertyAttributes GetAttributes(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store = NULL) = 0;
+
+ // Returns an element's type, or NONEXISTENT if there is no such
+ // element. This method doesn't iterate up the prototype chain. The caller
+ // can optionally pass in the backing store to use for the check, which must
+ // be compatible with the ElementsKind of the ElementsAccessor. If
+ // backing_store is NULL, the holder->elements() is used as the backing store.
+ MUST_USE_RESULT virtual PropertyType GetType(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store = NULL) = 0;
+
+ // Returns an element's accessors, or NULL if the element does not exist or
+ // is plain. This method doesn't iterate up the prototype chain. The caller
+ // can optionally pass in the backing store to use for the check, which must
+ // be compatible with the ElementsKind of the ElementsAccessor. If
+ // backing_store is NULL, the holder->elements() is used as the backing store.
+ MUST_USE_RESULT virtual AccessorPair* GetAccessorPair(
+ Object* receiver,
+ JSObject* holder,
+ uint32_t key,
+ FixedArrayBase* backing_store = NULL) = 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.
+ MUST_USE_RESULT virtual MaybeObject* SetLength(JSArray* 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.
+ MUST_USE_RESULT virtual MaybeObject* SetCapacityAndLength(JSArray* array,
+ int capacity,
+ int length) = 0;
+
+ // Deletes an element in an object, returning a new elements backing store.
+ MUST_USE_RESULT virtual MaybeObject* Delete(JSObject* holder,
+ uint32_t key,
+ JSReceiver::DeleteMode mode) = 0;
+
+ // If kCopyToEnd is specified as the copy_size to CopyElements, it copies all
+ // of elements from source after source_start to the destination array.
+ static const int kCopyToEnd = -1;
+ // If kCopyToEndAndInitializeToHole is specified as the copy_size to
+ // CopyElements, it copies all of elements from source after source_start to
+ // destination array, padding any remaining uninitialized elements in the
+ // destination array with the hole.
+ static const int kCopyToEndAndInitializeToHole = -2;
+
+ // Copy elements from one backing store to another. Typically, callers specify
+ // the source JSObject or JSArray in source_holder. If the holder's backing
+ // store is available, it can be passed in source and source_holder is
+ // ignored.
+ MUST_USE_RESULT virtual MaybeObject* CopyElements(
+ JSObject* source_holder,
+ uint32_t source_start,
+ ElementsKind source_kind,
+ FixedArrayBase* destination,
+ uint32_t destination_start,
+ int copy_size,
+ FixedArrayBase* source = NULL) = 0;
+
+ MUST_USE_RESULT MaybeObject* CopyElements(JSObject* from_holder,
+ FixedArrayBase* to,
+ ElementsKind from_kind,
+ FixedArrayBase* from = NULL) {
+ return CopyElements(from_holder, 0, from_kind, to, 0,
+ kCopyToEndAndInitializeToHole, from);
+ }
+
+ MUST_USE_RESULT virtual MaybeObject* AddElementsToFixedArray(
+ Object* receiver,
+ JSObject* holder,
+ FixedArray* to,
+ FixedArrayBase* from = NULL) = 0;
+
+ // Returns a shared ElementsAccessor for the specified ElementsKind.
+ static ElementsAccessor* ForKind(ElementsKind elements_kind) {
+ ASSERT(elements_kind < kElementsKindCount);
+ return elements_accessors_[elements_kind];
+ }
+
+ static ElementsAccessor* ForArray(FixedArrayBase* array);
+
+ static void InitializeOncePerProcess();
+ static void TearDown();
+
+ protected:
+ friend class NonStrictArgumentsElementsAccessor;
+
+ virtual uint32_t GetCapacity(FixedArrayBase* backing_store) = 0;
+
+ // Element handlers distinguish between indexes and keys when they manipulate
+ // elements. Indexes refer to elements in terms of their location in the
+ // underlying storage's backing store representation, and are between 0 and
+ // GetCapacity. Keys refer to elements in terms of the value that would be
+ // specified in JavaScript to access the element. In most implementations,
+ // keys are equivalent to indexes, and GetKeyForIndex returns the same value
+ // it is passed. In the NumberDictionary ElementsAccessor, GetKeyForIndex maps
+ // the index to a key using the KeyAt method on the NumberDictionary.
+ virtual uint32_t GetKeyForIndex(FixedArrayBase* backing_store,
+ uint32_t index) = 0;
+
+ private:
+ static ElementsAccessor** elements_accessors_;
+ const char* name_;
+
+ DISALLOW_COPY_AND_ASSIGN(ElementsAccessor);
+};
+
+void CheckArrayAbuse(JSObject* obj, const char* op, uint32_t key,
+ bool allow_appending = false);
+
+MUST_USE_RESULT MaybeObject* ArrayConstructInitializeElements(
+ JSArray* array, Arguments* args);
+
+} } // namespace v8::internal
+
+#endif // V8_ELEMENTS_H_
diff --git a/chromium/v8/src/execution.cc b/chromium/v8/src/execution.cc
new file mode 100644
index 00000000000..ecfa1db1ed8
--- /dev/null
+++ b/chromium/v8/src/execution.cc
@@ -0,0 +1,934 @@
+// Copyright 2012 the V8 project authors. 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 "api.h"
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "isolate-inl.h"
+#include "runtime-profiler.h"
+#include "simulator.h"
+#include "v8threads.h"
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+StackGuard::StackGuard()
+ : isolate_(NULL) {
+}
+
+
+void StackGuard::set_interrupt_limits(const ExecutionAccess& lock) {
+ ASSERT(isolate_ != NULL);
+ // Ignore attempts to interrupt when interrupts are postponed.
+ if (should_postpone_interrupts(lock)) return;
+ thread_local_.jslimit_ = kInterruptLimit;
+ thread_local_.climit_ = kInterruptLimit;
+ isolate_->heap()->SetStackLimits();
+}
+
+
+void StackGuard::reset_limits(const ExecutionAccess& lock) {
+ ASSERT(isolate_ != NULL);
+ thread_local_.jslimit_ = thread_local_.real_jslimit_;
+ thread_local_.climit_ = thread_local_.real_climit_;
+ isolate_->heap()->SetStackLimits();
+}
+
+
+static Handle<Object> Invoke(bool is_construct,
+ Handle<JSFunction> function,
+ Handle<Object> receiver,
+ int argc,
+ Handle<Object> args[],
+ bool* has_pending_exception) {
+ Isolate* isolate = function->GetIsolate();
+
+ // Entering JavaScript.
+ VMState<JS> state(isolate);
+
+ // 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 = 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
+ // directly to a global object.
+ if (receiver->IsGlobalObject()) {
+ Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
+ receiver = Handle<JSObject>(global->global_receiver());
+ }
+
+ // Make sure that the global object of the context we're about to
+ // make the current one is indeed a global object.
+ ASSERT(function->context()->global_object()->IsGlobalObject());
+
+ {
+ // Save and restore context around invocation and block the
+ // allocation of handles without explicit handle scopes.
+ SaveContext save(isolate);
+ SealHandleScope shs(isolate);
+ JSEntryFunction stub_entry = FUNCTION_CAST<JSEntryFunction>(code->entry());
+
+ // Call the function through the right JS entry stub.
+ 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 VERIFY_HEAP
+ value->Verify();
+#endif
+
+ // Update the pending exception flag and return the value.
+ *has_pending_exception = value->IsException();
+ ASSERT(*has_pending_exception == isolate->has_pending_exception());
+ if (*has_pending_exception) {
+ isolate->ReportPendingMessages();
+ if (isolate->pending_exception()->IsOutOfMemory()) {
+ if (!isolate->ignore_out_of_memory()) {
+ V8::FatalProcessOutOfMemory("JS", true);
+ }
+ }
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Reset stepping state when script exits with uncaught exception.
+ if (isolate->debugger()->IsDebuggerActive()) {
+ isolate->debug()->ClearStepping();
+ }
+#endif // ENABLE_DEBUGGER_SUPPORT
+ return Handle<Object>();
+ } else {
+ isolate->clear_pending_message();
+ }
+
+ return Handle<Object>(value->ToObjectUnchecked(), isolate);
+}
+
+
+Handle<Object> Execution::Call(Handle<Object> callable,
+ Handle<Object> receiver,
+ int argc,
+ 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;
+ }
+ Handle<JSFunction> func = Handle<JSFunction>::cast(callable);
+
+ // In non-strict mode, convert receiver.
+ if (convert_receiver && !receiver->IsJSReceiver() &&
+ !func->shared()->native() && func->shared()->is_classic_mode()) {
+ if (receiver->IsUndefined() || receiver->IsNull()) {
+ Object* global = func->context()->global_object()->global_receiver();
+ // Under some circumstances, 'global' can be the JSBuiltinsObject
+ // In that case, don't rewrite. (FWIW, the same holds for
+ // GetIsolate()->global_object()->global_receiver().)
+ if (!global->IsJSBuiltinsObject()) {
+ receiver = Handle<Object>(global, func->GetIsolate());
+ }
+ } else {
+ receiver = ToObject(receiver, pending_exception);
+ }
+ if (*pending_exception) return callable;
+ }
+
+ return Invoke(false, func, receiver, argc, argv, pending_exception);
+}
+
+
+Handle<Object> Execution::New(Handle<JSFunction> func,
+ int argc,
+ Handle<Object> argv[],
+ bool* pending_exception) {
+ return Invoke(true, func, func->GetIsolate()->global_object(), argc, argv,
+ pending_exception);
+}
+
+
+Handle<Object> Execution::TryCall(Handle<JSFunction> func,
+ Handle<Object> receiver,
+ int argc,
+ 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
+ // creating message objects during stack overflow we shouldn't
+ // capture messages.
+ v8::TryCatch catcher;
+ catcher.SetVerbose(false);
+ catcher.SetCaptureMessage(false);
+ *caught_exception = false;
+
+ // Get isolate now, because handle might be persistent
+ // and get destroyed in the next call.
+ Isolate* isolate = func->GetIsolate();
+ Handle<Object> result = Invoke(false, func, receiver, argc, args,
+ caught_exception);
+
+ if (*caught_exception) {
+ ASSERT(catcher.HasCaught());
+ ASSERT(isolate->has_pending_exception());
+ ASSERT(isolate->external_caught_exception());
+ if (isolate->is_out_of_memory() && !isolate->ignore_out_of_memory()) {
+ V8::FatalProcessOutOfMemory("OOM during Execution::TryCall");
+ }
+ if (isolate->pending_exception() ==
+ isolate->heap()->termination_exception()) {
+ result = isolate->factory()->termination_exception();
+ } else {
+ result = v8::Utils::OpenHandle(*catcher.Exception());
+ }
+ isolate->OptionalRescheduleException(true);
+ }
+
+ ASSERT(!isolate->has_pending_exception());
+ ASSERT(!isolate->external_caught_exception());
+ return result;
+}
+
+
+Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) {
+ ASSERT(!object->IsJSFunction());
+ Isolate* isolate = Isolate::Current();
+ Factory* factory = isolate->factory();
+
+ // If you return a function from here, it will be called when an
+ // attempt is made to call the given object as a function.
+
+ // If object is a function proxy, get its handler. Iterate if necessary.
+ Object* fun = *object;
+ while (fun->IsJSFunctionProxy()) {
+ fun = JSFunctionProxy::cast(fun)->call_trap();
+ }
+ if (fun->IsJSFunction()) return Handle<Object>(fun, isolate);
+
+ // Objects created through the API can have an instance-call handler
+ // that should be used when calling the object as a function.
+ if (fun->IsHeapObject() &&
+ HeapObject::cast(fun)->map()->has_instance_call_handler()) {
+ return Handle<JSFunction>(
+ isolate->native_context()->call_as_function_delegate());
+ }
+
+ return factory->undefined_value();
+}
+
+
+Handle<Object> Execution::TryGetFunctionDelegate(Handle<Object> object,
+ bool* has_pending_exception) {
+ ASSERT(!object->IsJSFunction());
+ Isolate* isolate = Isolate::Current();
+
+ // If object is a function proxy, get its handler. Iterate if necessary.
+ Object* fun = *object;
+ while (fun->IsJSFunctionProxy()) {
+ fun = JSFunctionProxy::cast(fun)->call_trap();
+ }
+ if (fun->IsJSFunction()) return Handle<Object>(fun, isolate);
+
+ // Objects created through the API can have an instance-call handler
+ // that should be used when calling the object as a function.
+ if (fun->IsHeapObject() &&
+ HeapObject::cast(fun)->map()->has_instance_call_handler()) {
+ return Handle<JSFunction>(
+ isolate->native_context()->call_as_function_delegate());
+ }
+
+ // If the Object doesn't have an instance-call handler we should
+ // throw a non-callable exception.
+ i::Handle<i::Object> error_obj = isolate->factory()->NewTypeError(
+ "called_non_callable", i::HandleVector<i::Object>(&object, 1));
+ isolate->Throw(*error_obj);
+ *has_pending_exception = true;
+
+ return isolate->factory()->undefined_value();
+}
+
+
+Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
+ ASSERT(!object->IsJSFunction());
+ Isolate* isolate = Isolate::Current();
+
+ // If you return a function from here, it will be called when an
+ // attempt is made to call the given object as a constructor.
+
+ // If object is a function proxies, get its handler. Iterate if necessary.
+ Object* fun = *object;
+ while (fun->IsJSFunctionProxy()) {
+ fun = JSFunctionProxy::cast(fun)->call_trap();
+ }
+ if (fun->IsJSFunction()) return Handle<Object>(fun, isolate);
+
+ // Objects created through the API can have an instance-call handler
+ // that should be used when calling the object as a function.
+ if (fun->IsHeapObject() &&
+ HeapObject::cast(fun)->map()->has_instance_call_handler()) {
+ return Handle<JSFunction>(
+ isolate->native_context()->call_as_constructor_delegate());
+ }
+
+ return isolate->factory()->undefined_value();
+}
+
+
+Handle<Object> Execution::TryGetConstructorDelegate(
+ Handle<Object> object,
+ bool* has_pending_exception) {
+ ASSERT(!object->IsJSFunction());
+ Isolate* isolate = Isolate::Current();
+
+ // If you return a function from here, it will be called when an
+ // attempt is made to call the given object as a constructor.
+
+ // If object is a function proxies, get its handler. Iterate if necessary.
+ Object* fun = *object;
+ while (fun->IsJSFunctionProxy()) {
+ fun = JSFunctionProxy::cast(fun)->call_trap();
+ }
+ if (fun->IsJSFunction()) return Handle<Object>(fun, isolate);
+
+ // Objects created through the API can have an instance-call handler
+ // that should be used when calling the object as a function.
+ if (fun->IsHeapObject() &&
+ HeapObject::cast(fun)->map()->has_instance_call_handler()) {
+ return Handle<JSFunction>(
+ isolate->native_context()->call_as_constructor_delegate());
+ }
+
+ // If the Object doesn't have an instance-call handler we should
+ // throw a non-callable exception.
+ i::Handle<i::Object> error_obj = isolate->factory()->NewTypeError(
+ "called_non_callable", i::HandleVector<i::Object>(&object, 1));
+ isolate->Throw(*error_obj);
+ *has_pending_exception = true;
+
+ return isolate->factory()->undefined_value();
+}
+
+
+bool StackGuard::IsStackOverflow() {
+ ExecutionAccess access(isolate_);
+ return (thread_local_.jslimit_ != kInterruptLimit &&
+ thread_local_.climit_ != kInterruptLimit);
+}
+
+
+void StackGuard::EnableInterrupts() {
+ ExecutionAccess access(isolate_);
+ if (has_pending_interrupts(access)) {
+ set_interrupt_limits(access);
+ }
+}
+
+
+void StackGuard::SetStackLimit(uintptr_t limit) {
+ ExecutionAccess access(isolate_);
+ // 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_) {
+ thread_local_.jslimit_ = jslimit;
+ }
+ if (thread_local_.climit_ == thread_local_.real_climit_) {
+ thread_local_.climit_ = limit;
+ }
+ thread_local_.real_climit_ = limit;
+ thread_local_.real_jslimit_ = jslimit;
+}
+
+
+void StackGuard::DisableInterrupts() {
+ ExecutionAccess access(isolate_);
+ reset_limits(access);
+}
+
+
+bool StackGuard::ShouldPostponeInterrupts() {
+ ExecutionAccess access(isolate_);
+ return should_postpone_interrupts(access);
+}
+
+
+bool StackGuard::IsInterrupted() {
+ ExecutionAccess access(isolate_);
+ return (thread_local_.interrupt_flags_ & INTERRUPT) != 0;
+}
+
+
+void StackGuard::Interrupt() {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ |= INTERRUPT;
+ set_interrupt_limits(access);
+}
+
+
+bool StackGuard::IsPreempted() {
+ ExecutionAccess access(isolate_);
+ return thread_local_.interrupt_flags_ & PREEMPT;
+}
+
+
+void StackGuard::Preempt() {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ |= PREEMPT;
+ set_interrupt_limits(access);
+}
+
+
+bool StackGuard::IsTerminateExecution() {
+ ExecutionAccess access(isolate_);
+ return (thread_local_.interrupt_flags_ & TERMINATE) != 0;
+}
+
+
+void StackGuard::CancelTerminateExecution() {
+ ExecutionAccess access(isolate_);
+ Continue(TERMINATE);
+ isolate_->CancelTerminateExecution();
+}
+
+
+void StackGuard::TerminateExecution() {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ |= TERMINATE;
+ set_interrupt_limits(access);
+}
+
+
+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();
+ }
+}
+
+
+bool StackGuard::IsFullDeopt() {
+ ExecutionAccess access(isolate_);
+ return (thread_local_.interrupt_flags_ & FULL_DEOPT) != 0;
+}
+
+
+void StackGuard::FullDeopt() {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ |= FULL_DEOPT;
+ set_interrupt_limits(access);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+bool StackGuard::IsDebugBreak() {
+ ExecutionAccess access(isolate_);
+ return thread_local_.interrupt_flags_ & DEBUGBREAK;
+}
+
+
+void StackGuard::DebugBreak() {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ |= DEBUGBREAK;
+ set_interrupt_limits(access);
+}
+
+
+bool StackGuard::IsDebugCommand() {
+ ExecutionAccess access(isolate_);
+ return thread_local_.interrupt_flags_ & DEBUGCOMMAND;
+}
+
+
+void StackGuard::DebugCommand() {
+ if (FLAG_debugger_auto_break) {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ |= DEBUGCOMMAND;
+ set_interrupt_limits(access);
+ }
+}
+#endif
+
+void StackGuard::Continue(InterruptFlag after_what) {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ &= ~static_cast<int>(after_what);
+ if (!should_postpone_interrupts(access) && !has_pending_interrupts(access)) {
+ reset_limits(access);
+ }
+}
+
+
+char* StackGuard::ArchiveStackGuard(char* to) {
+ ExecutionAccess access(isolate_);
+ OS::MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
+ ThreadLocal blank;
+
+ // Set the stack limits using the old thread_local_.
+ // TODO(isolates): This was the old semantics of constructing a ThreadLocal
+ // (as the ctor called SetStackLimits, which looked at the
+ // current thread_local_ from StackGuard)-- but is this
+ // really what was intended?
+ isolate_->heap()->SetStackLimits();
+ thread_local_ = blank;
+
+ return to + sizeof(ThreadLocal);
+}
+
+
+char* StackGuard::RestoreStackGuard(char* from) {
+ ExecutionAccess access(isolate_);
+ OS::MemCopy(
+ reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
+ isolate_->heap()->SetStackLimits();
+ return from + sizeof(ThreadLocal);
+}
+
+
+void StackGuard::FreeThreadResources() {
+ Isolate::PerIsolateThreadData* per_thread =
+ isolate_->FindOrAllocatePerThreadDataForThisThread();
+ per_thread->set_stack_limit(thread_local_.real_climit_);
+}
+
+
+void StackGuard::ThreadLocal::Clear() {
+ real_jslimit_ = kIllegalLimit;
+ jslimit_ = kIllegalLimit;
+ real_climit_ = kIllegalLimit;
+ climit_ = kIllegalLimit;
+ nesting_ = 0;
+ postpone_interrupts_nesting_ = 0;
+ interrupt_flags_ = 0;
+}
+
+
+bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) {
+ bool should_set_stack_limits = false;
+ if (real_climit_ == kIllegalLimit) {
+ // Takes the address of the limit variable in order to find out where
+ // the top of stack is right now.
+ const uintptr_t kLimitSize = FLAG_stack_size * KB;
+ uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize;
+ ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize);
+ real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
+ jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
+ real_climit_ = limit;
+ climit_ = limit;
+ should_set_stack_limits = true;
+ }
+ nesting_ = 0;
+ postpone_interrupts_nesting_ = 0;
+ interrupt_flags_ = 0;
+ return should_set_stack_limits;
+}
+
+
+void StackGuard::ClearThread(const ExecutionAccess& lock) {
+ thread_local_.Clear();
+ isolate_->heap()->SetStackLimits();
+}
+
+
+void StackGuard::InitThread(const ExecutionAccess& lock) {
+ if (thread_local_.Initialize(isolate_)) isolate_->heap()->SetStackLimits();
+ Isolate::PerIsolateThreadData* per_thread =
+ isolate_->FindOrAllocatePerThreadDataForThisThread();
+ uintptr_t stored_limit = per_thread->stack_limit();
+ // You should hold the ExecutionAccess lock when you call this.
+ if (stored_limit != 0) {
+ SetStackLimit(stored_limit);
+ }
+}
+
+
+// --- C a l l s t o n a t i v e s ---
+
+#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)
+
+
+Handle<Object> Execution::ToNumber(Handle<Object> obj, bool* exc) {
+ RETURN_NATIVE_CALL(to_number, { obj }, exc);
+}
+
+
+Handle<Object> Execution::ToString(Handle<Object> obj, bool* exc) {
+ RETURN_NATIVE_CALL(to_string, { obj }, exc);
+}
+
+
+Handle<Object> Execution::ToDetailString(Handle<Object> obj, bool* 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, { obj }, exc);
+}
+
+
+Handle<Object> Execution::ToInteger(Handle<Object> obj, bool* exc) {
+ RETURN_NATIVE_CALL(to_integer, { obj }, exc);
+}
+
+
+Handle<Object> Execution::ToUint32(Handle<Object> obj, bool* exc) {
+ RETURN_NATIVE_CALL(to_uint32, { obj }, exc);
+}
+
+
+Handle<Object> Execution::ToInt32(Handle<Object> obj, bool* exc) {
+ RETURN_NATIVE_CALL(to_int32, { obj }, exc);
+}
+
+
+Handle<Object> Execution::NewDate(double time, bool* exc) {
+ Isolate* isolate = Isolate::Current();
+ Handle<Object> time_obj = isolate->factory()->NewNumber(time);
+ RETURN_NATIVE_CALL(create_date, { time_obj }, exc);
+}
+
+
+#undef RETURN_NATIVE_CALL
+
+
+Handle<JSRegExp> Execution::NewJSRegExp(Handle<String> pattern,
+ Handle<String> flags,
+ bool* exc) {
+ Handle<JSFunction> function = Handle<JSFunction>(
+ pattern->GetIsolate()->native_context()->regexp_function());
+ Handle<Object> re_obj = RegExpImpl::CreateRegExpLiteral(
+ function, pattern, flags, exc);
+ if (*exc) return Handle<JSRegExp>();
+ return Handle<JSRegExp>::cast(re_obj);
+}
+
+
+Handle<Object> Execution::CharAt(Handle<String> string, uint32_t index) {
+ Isolate* isolate = string->GetIsolate();
+ Factory* factory = isolate->factory();
+
+ int int_index = static_cast<int>(index);
+ if (int_index < 0 || int_index >= string->length()) {
+ return factory->undefined_value();
+ }
+
+ Handle<Object> char_at = GetProperty(
+ isolate, isolate->js_builtins_object(), factory->char_at_string());
+ if (!char_at->IsJSFunction()) {
+ return factory->undefined_value();
+ }
+
+ bool caught_exception;
+ Handle<Object> index_object = factory->NewNumberFromInt(int_index);
+ Handle<Object> index_arg[] = { index_object };
+ Handle<Object> result = TryCall(Handle<JSFunction>::cast(char_at),
+ string,
+ ARRAY_SIZE(index_arg),
+ index_arg,
+ &caught_exception);
+ if (caught_exception) {
+ return factory->undefined_value();
+ }
+ return result;
+}
+
+
+Handle<JSFunction> Execution::InstantiateFunction(
+ 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();
+ Object* elm =
+ isolate->native_context()->function_cache()->
+ GetElementNoExceptionThrown(serial_number);
+ if (elm->IsJSFunction()) return Handle<JSFunction>(JSFunction::cast(elm));
+ // The function has not yet been instantiated in this context; do it.
+ 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);
+}
+
+
+Handle<JSObject> Execution::InstantiateObject(Handle<ObjectTemplateInfo> data,
+ bool* exc) {
+ Isolate* isolate = data->GetIsolate();
+ if (data->property_list()->IsUndefined() &&
+ !data->constructor()->IsUndefined()) {
+ // Initialization to make gcc happy.
+ Object* result = NULL;
+ {
+ HandleScope scope(isolate);
+ Handle<FunctionTemplateInfo> cons_template =
+ Handle<FunctionTemplateInfo>(
+ FunctionTemplateInfo::cast(data->constructor()));
+ Handle<JSFunction> cons = InstantiateFunction(cons_template, exc);
+ if (*exc) return Handle<JSObject>::null();
+ Handle<Object> value = New(cons, 0, NULL, exc);
+ if (*exc) return Handle<JSObject>::null();
+ result = *value;
+ }
+ ASSERT(!*exc);
+ return Handle<JSObject>(JSObject::cast(result));
+ } else {
+ 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);
+ }
+}
+
+
+void Execution::ConfigureInstance(Handle<Object> instance,
+ Handle<Object> instance_template,
+ bool* exc) {
+ Isolate* isolate = Isolate::Current();
+ Handle<Object> args[] = { instance, instance_template };
+ Execution::Call(isolate->configure_instance_fun(),
+ isolate->js_builtins_object(),
+ ARRAY_SIZE(args),
+ args,
+ exc);
+}
+
+
+Handle<String> Execution::GetStackTraceLine(Handle<Object> recv,
+ Handle<JSFunction> fun,
+ Handle<Object> pos,
+ Handle<Object> is_global) {
+ Isolate* isolate = fun->GetIsolate();
+ 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_string();
+ }
+
+ return Handle<String>::cast(result);
+}
+
+
+static Object* RuntimePreempt() {
+ Isolate* isolate = Isolate::Current();
+
+ // Clear the preempt request flag.
+ isolate->stack_guard()->Continue(PREEMPT);
+
+ ContextSwitcher::PreemptionReceived();
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (isolate->debug()->InDebugger()) {
+ // If currently in the debugger don't do any actual preemption but record
+ // that preemption occoured while in the debugger.
+ isolate->debug()->PreemptionWhileInDebugger();
+ } else {
+ // Perform preemption.
+ v8::Unlocker unlocker(reinterpret_cast<v8::Isolate*>(isolate));
+ Thread::YieldCPU();
+ }
+#else
+ { // NOLINT
+ // Perform preemption.
+ v8::Unlocker unlocker(reinterpret_cast<v8::Isolate*>(isolate));
+ Thread::YieldCPU();
+ }
+#endif
+
+ return isolate->heap()->undefined_value();
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+Object* Execution::DebugBreakHelper() {
+ Isolate* isolate = Isolate::Current();
+
+ // Just continue if breaks are disabled.
+ if (isolate->debug()->disable_break()) {
+ return isolate->heap()->undefined_value();
+ }
+
+ // Ignore debug break during bootstrapping.
+ if (isolate->bootstrapper()->IsActive()) {
+ return isolate->heap()->undefined_value();
+ }
+
+ // Ignore debug break if debugger is not active.
+ if (!isolate->debugger()->IsDebuggerActive()) {
+ return isolate->heap()->undefined_value();
+ }
+
+ StackLimitCheck check(isolate);
+ if (check.HasOverflowed()) {
+ return isolate->heap()->undefined_value();
+ }
+
+ {
+ JavaScriptFrameIterator it(isolate);
+ ASSERT(!it.done());
+ Object* fun = it.frame()->function();
+ if (fun && fun->IsJSFunction()) {
+ // Don't stop in builtin functions.
+ if (JSFunction::cast(fun)->IsBuiltin()) {
+ return isolate->heap()->undefined_value();
+ }
+ GlobalObject* global = JSFunction::cast(fun)->context()->global_object();
+ // Don't stop in debugger functions.
+ if (isolate->debug()->IsDebugGlobal(global)) {
+ return isolate->heap()->undefined_value();
+ }
+ }
+ }
+
+ // Collect the break state before clearing the flags.
+ bool debug_command_only =
+ isolate->stack_guard()->IsDebugCommand() &&
+ !isolate->stack_guard()->IsDebugBreak();
+
+ // Clear the debug break request flag.
+ isolate->stack_guard()->Continue(DEBUGBREAK);
+
+ ProcessDebugMessages(debug_command_only);
+
+ // Return to continue execution.
+ return isolate->heap()->undefined_value();
+}
+
+
+void Execution::ProcessDebugMessages(bool debug_command_only) {
+ Isolate* isolate = Isolate::Current();
+ // Clear the debug command request flag.
+ isolate->stack_guard()->Continue(DEBUGCOMMAND);
+
+ StackLimitCheck check(isolate);
+ if (check.HasOverflowed()) {
+ return;
+ }
+
+ HandleScope scope(isolate);
+ // Enter the debugger. Just continue if we fail to enter the debugger.
+ EnterDebugger debugger;
+ if (debugger.FailedToEnter()) {
+ return;
+ }
+
+ // Notify the debug event listeners. Indicate auto continue if the break was
+ // a debug command break.
+ isolate->debugger()->OnDebugBreak(isolate->factory()->undefined_value(),
+ debug_command_only);
+}
+
+
+#endif
+
+MaybeObject* Execution::HandleStackGuardInterrupt(Isolate* isolate) {
+ StackGuard* stack_guard = isolate->stack_guard();
+ if (stack_guard->ShouldPostponeInterrupts()) {
+ return isolate->heap()->undefined_value();
+ }
+
+ if (stack_guard->IsGCRequest()) {
+ isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags,
+ "StackGuard GC request");
+ stack_guard->Continue(GC_REQUEST);
+ }
+
+ isolate->counters()->stack_interrupts()->Increment();
+ isolate->counters()->runtime_profiler_ticks()->Increment();
+ isolate->runtime_profiler()->OptimizeNow();
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (stack_guard->IsDebugBreak() || stack_guard->IsDebugCommand()) {
+ DebugBreakHelper();
+ }
+#endif
+ if (stack_guard->IsPreempted()) RuntimePreempt();
+ if (stack_guard->IsTerminateExecution()) {
+ stack_guard->Continue(TERMINATE);
+ return isolate->TerminateExecution();
+ }
+ if (stack_guard->IsInterrupted()) {
+ stack_guard->Continue(INTERRUPT);
+ return isolate->StackOverflow();
+ }
+ if (stack_guard->IsFullDeopt()) {
+ stack_guard->Continue(FULL_DEOPT);
+ Deoptimizer::DeoptimizeAll(isolate);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/execution.h b/chromium/v8/src/execution.h
new file mode 100644
index 00000000000..c6bf63d72f2
--- /dev/null
+++ b/chromium/v8/src/execution.h
@@ -0,0 +1,308 @@
+// 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_EXECUTION_H_
+#define V8_EXECUTION_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Flag used to set the interrupt causes.
+enum InterruptFlag {
+ INTERRUPT = 1 << 0,
+ DEBUGBREAK = 1 << 1,
+ DEBUGCOMMAND = 1 << 2,
+ PREEMPT = 1 << 3,
+ TERMINATE = 1 << 4,
+ GC_REQUEST = 1 << 5,
+ FULL_DEOPT = 1 << 6
+};
+
+
+class Isolate;
+
+
+class Execution : public AllStatic {
+ public:
+ // Call a function, the caller supplies a receiver and an array
+ // of arguments. Arguments are Object* type. After function returns,
+ // pointers in 'args' might be invalid.
+ //
+ // *pending_exception tells whether the invoke resulted in
+ // a pending exception.
+ //
+ // When convert_receiver is set, and the receiver is not an object,
+ // and the function called is not in strict mode, receiver is converted to
+ // an object.
+ //
+ static Handle<Object> Call(Handle<Object> callable,
+ Handle<Object> receiver,
+ int argc,
+ Handle<Object> argv[],
+ bool* pending_exception,
+ bool convert_receiver = false);
+
+ // Construct object from function, the caller supplies an array of
+ // arguments. Arguments are Object* type. After function returns,
+ // pointers in 'args' might be invalid.
+ //
+ // *pending_exception tells whether the invoke resulted in
+ // a pending exception.
+ //
+ static Handle<Object> New(Handle<JSFunction> func,
+ int argc,
+ Handle<Object> argv[],
+ bool* pending_exception);
+
+ // Call a function, just like Call(), but make sure to silently catch
+ // any thrown exceptions. The return value is either the result of
+ // calling the function (if caught exception is false) or the exception
+ // that occurred (if caught exception is true).
+ static Handle<Object> TryCall(Handle<JSFunction> func,
+ Handle<Object> receiver,
+ int argc,
+ Handle<Object> argv[],
+ bool* caught_exception);
+
+ // ECMA-262 9.3
+ static Handle<Object> ToNumber(Handle<Object> obj, bool* exc);
+
+ // ECMA-262 9.4
+ static Handle<Object> ToInteger(Handle<Object> obj, bool* exc);
+
+ // ECMA-262 9.5
+ static Handle<Object> ToInt32(Handle<Object> obj, bool* exc);
+
+ // ECMA-262 9.6
+ static Handle<Object> ToUint32(Handle<Object> obj, bool* exc);
+
+ // ECMA-262 9.8
+ static Handle<Object> ToString(Handle<Object> obj, bool* exc);
+
+ // ECMA-262 9.8
+ static Handle<Object> ToDetailString(Handle<Object> obj, bool* exc);
+
+ // ECMA-262 9.9
+ static Handle<Object> ToObject(Handle<Object> obj, bool* exc);
+
+ // Create a new date object from 'time'.
+ static Handle<Object> NewDate(double time, bool* exc);
+
+ // Create a new regular expression object from 'pattern' and 'flags'.
+ static Handle<JSRegExp> NewJSRegExp(Handle<String> pattern,
+ Handle<String> flags,
+ bool* exc);
+
+ // Used to implement [] notation on strings (calls JS code)
+ static Handle<Object> CharAt(Handle<String> str, uint32_t index);
+
+ static Handle<Object> GetFunctionFor();
+ static Handle<JSFunction> InstantiateFunction(
+ Handle<FunctionTemplateInfo> data, bool* exc);
+ static Handle<JSObject> InstantiateObject(Handle<ObjectTemplateInfo> data,
+ bool* exc);
+ static void ConfigureInstance(Handle<Object> instance,
+ Handle<Object> data,
+ bool* exc);
+ static Handle<String> GetStackTraceLine(Handle<Object> recv,
+ Handle<JSFunction> fun,
+ Handle<Object> pos,
+ Handle<Object> is_global);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ static Object* DebugBreakHelper();
+ static void ProcessDebugMessages(bool debug_command_only);
+#endif
+
+ // If the stack guard is triggered, but it is not an actual
+ // stack overflow, then handle the interruption accordingly.
+ MUST_USE_RESULT static MaybeObject* HandleStackGuardInterrupt(
+ Isolate* isolate);
+
+ // Get a function delegate (or undefined) for the given non-function
+ // object. Used for support calling objects as functions.
+ static Handle<Object> GetFunctionDelegate(Handle<Object> object);
+ static Handle<Object> TryGetFunctionDelegate(Handle<Object> object,
+ bool* has_pending_exception);
+
+ // Get a function delegate (or undefined) for the given non-function
+ // object. Used for support calling objects as constructors.
+ static Handle<Object> GetConstructorDelegate(Handle<Object> object);
+ static Handle<Object> TryGetConstructorDelegate(Handle<Object> object,
+ bool* has_pending_exception);
+};
+
+
+class ExecutionAccess;
+
+
+// StackGuard contains the handling of the limits that are used to limit the
+// number of nested invocations of JavaScript and the stack size used in each
+// invocation.
+class StackGuard {
+ public:
+ // Pass the address beyond which the stack should not grow. The stack
+ // is assumed to grow downwards.
+ void SetStackLimit(uintptr_t limit);
+
+ // Threading support.
+ char* ArchiveStackGuard(char* to);
+ char* RestoreStackGuard(char* from);
+ static int ArchiveSpacePerThread() { return sizeof(ThreadLocal); }
+ void FreeThreadResources();
+ // Sets up the default stack guard for this thread if it has not
+ // already been set up.
+ void InitThread(const ExecutionAccess& lock);
+ // Clears the stack guard for this thread so it does not look as if
+ // it has been set up.
+ void ClearThread(const ExecutionAccess& lock);
+
+ bool IsStackOverflow();
+ bool IsPreempted();
+ void Preempt();
+ bool IsInterrupted();
+ void Interrupt();
+ bool IsTerminateExecution();
+ void TerminateExecution();
+ void CancelTerminateExecution();
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ bool IsDebugBreak();
+ void DebugBreak();
+ bool IsDebugCommand();
+ void DebugCommand();
+#endif
+ bool IsGCRequest();
+ void RequestGC();
+ bool IsFullDeopt();
+ void FullDeopt();
+ void Continue(InterruptFlag after_what);
+
+ // This provides an asynchronous read of the stack limits for the current
+ // thread. There are no locks protecting this, but it is assumed that you
+ // have the global V8 lock if you are using multiple V8 threads.
+ uintptr_t climit() {
+ return thread_local_.climit_;
+ }
+ uintptr_t real_climit() {
+ return thread_local_.real_climit_;
+ }
+ uintptr_t jslimit() {
+ return thread_local_.jslimit_;
+ }
+ uintptr_t real_jslimit() {
+ return thread_local_.real_jslimit_;
+ }
+ Address address_of_jslimit() {
+ return reinterpret_cast<Address>(&thread_local_.jslimit_);
+ }
+ Address address_of_real_jslimit() {
+ return reinterpret_cast<Address>(&thread_local_.real_jslimit_);
+ }
+ bool ShouldPostponeInterrupts();
+
+ private:
+ StackGuard();
+
+ // You should hold the ExecutionAccess lock when calling this method.
+ bool has_pending_interrupts(const ExecutionAccess& lock) {
+ // Sanity check: We shouldn't be asking about pending interrupts
+ // unless we're not postponing them anymore.
+ ASSERT(!should_postpone_interrupts(lock));
+ return thread_local_.interrupt_flags_ != 0;
+ }
+
+ // You should hold the ExecutionAccess lock when calling this method.
+ bool should_postpone_interrupts(const ExecutionAccess& lock) {
+ return thread_local_.postpone_interrupts_nesting_ > 0;
+ }
+
+ // You should hold the ExecutionAccess lock when calling this method.
+ inline void set_interrupt_limits(const ExecutionAccess& lock);
+
+ // Reset limits to actual values. For example after handling interrupt.
+ // You should hold the ExecutionAccess lock when calling this method.
+ inline void reset_limits(const ExecutionAccess& lock);
+
+ // Enable or disable interrupts.
+ void EnableInterrupts();
+ void DisableInterrupts();
+
+#if V8_TARGET_ARCH_X64
+ static const uintptr_t kInterruptLimit = V8_UINT64_C(0xfffffffffffffffe);
+ static const uintptr_t kIllegalLimit = V8_UINT64_C(0xfffffffffffffff8);
+#else
+ static const uintptr_t kInterruptLimit = 0xfffffffe;
+ static const uintptr_t kIllegalLimit = 0xfffffff8;
+#endif
+
+ class ThreadLocal {
+ public:
+ ThreadLocal() { Clear(); }
+ // You should hold the ExecutionAccess lock when you call Initialize or
+ // Clear.
+ void Clear();
+
+ // Returns true if the heap's stack limits should be set, false if not.
+ bool Initialize(Isolate* isolate);
+
+ // The stack limit is split into a JavaScript and a C++ stack limit. These
+ // two are the same except when running on a simulator where the C++ and
+ // JavaScript stacks are separate. Each of the two stack limits have two
+ // values. The one eith the real_ prefix is the actual stack limit
+ // set for the VM. The one without the real_ prefix has the same value as
+ // the actual stack limit except when there is an interruption (e.g. debug
+ // break or preemption) in which case it is lowered to make stack checks
+ // fail. Both the generated code and the runtime system check against the
+ // one without the real_ prefix.
+ uintptr_t real_jslimit_; // Actual JavaScript stack limit set for the VM.
+ uintptr_t jslimit_;
+ uintptr_t real_climit_; // Actual C++ stack limit set for the VM.
+ uintptr_t climit_;
+
+ int nesting_;
+ int postpone_interrupts_nesting_;
+ int interrupt_flags_;
+ };
+
+ // TODO(isolates): Technically this could be calculated directly from a
+ // pointer to StackGuard.
+ Isolate* isolate_;
+ ThreadLocal thread_local_;
+
+ friend class Isolate;
+ friend class StackLimitCheck;
+ friend class PostponeInterruptsScope;
+
+ DISALLOW_COPY_AND_ASSIGN(StackGuard);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_EXECUTION_H_
diff --git a/chromium/v8/src/extensions/externalize-string-extension.cc b/chromium/v8/src/extensions/externalize-string-extension.cc
new file mode 100644
index 00000000000..a3630fb9f5b
--- /dev/null
+++ b/chromium/v8/src/extensions/externalize-string-extension.cc
@@ -0,0 +1,145 @@
+// 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.
+
+#include "externalize-string-extension.h"
+
+namespace v8 {
+namespace internal {
+
+template <typename Char, typename Base>
+class SimpleStringResource : public Base {
+ public:
+ // Takes ownership of |data|.
+ SimpleStringResource(Char* data, size_t length)
+ : data_(data),
+ length_(length) {}
+
+ virtual ~SimpleStringResource() { delete[] data_; }
+
+ virtual const Char* data() const { return data_; }
+
+ virtual size_t length() const { return length_; }
+
+ private:
+ Char* const data_;
+ const size_t length_;
+};
+
+
+typedef SimpleStringResource<char, v8::String::ExternalAsciiStringResource>
+ SimpleAsciiStringResource;
+typedef SimpleStringResource<uc16, v8::String::ExternalStringResource>
+ SimpleTwoByteStringResource;
+
+
+const char* const ExternalizeStringExtension::kSource =
+ "native function externalizeString();"
+ "native function isAsciiString();";
+
+
+v8::Handle<v8::FunctionTemplate> ExternalizeStringExtension::GetNativeFunction(
+ v8::Handle<v8::String> str) {
+ if (strcmp(*v8::String::Utf8Value(str), "externalizeString") == 0) {
+ return v8::FunctionTemplate::New(ExternalizeStringExtension::Externalize);
+ } else {
+ ASSERT(strcmp(*v8::String::Utf8Value(str), "isAsciiString") == 0);
+ return v8::FunctionTemplate::New(ExternalizeStringExtension::IsAscii);
+ }
+}
+
+
+void ExternalizeStringExtension::Externalize(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() < 1 || !args[0]->IsString()) {
+ v8::ThrowException(v8::String::New(
+ "First parameter to externalizeString() must be a string."));
+ return;
+ }
+ bool force_two_byte = false;
+ if (args.Length() >= 2) {
+ if (args[1]->IsBoolean()) {
+ force_two_byte = args[1]->BooleanValue();
+ } else {
+ v8::ThrowException(v8::String::New(
+ "Second parameter to externalizeString() must be a boolean."));
+ return;
+ }
+ }
+ bool result = false;
+ Handle<String> string = Utils::OpenHandle(*args[0].As<v8::String>());
+ if (string->IsExternalString()) {
+ v8::ThrowException(v8::String::New(
+ "externalizeString() can't externalize twice."));
+ return;
+ }
+ if (string->IsOneByteRepresentation() && !force_two_byte) {
+ uint8_t* data = new uint8_t[string->length()];
+ String::WriteToFlat(*string, data, 0, string->length());
+ SimpleAsciiStringResource* resource = new SimpleAsciiStringResource(
+ reinterpret_cast<char*>(data), string->length());
+ result = string->MakeExternal(resource);
+ if (result && !string->IsInternalizedString()) {
+ HEAP->external_string_table()->AddString(*string);
+ }
+ if (!result) delete resource;
+ } else {
+ uc16* data = new uc16[string->length()];
+ String::WriteToFlat(*string, data, 0, string->length());
+ SimpleTwoByteStringResource* resource = new SimpleTwoByteStringResource(
+ data, string->length());
+ result = string->MakeExternal(resource);
+ if (result && !string->IsInternalizedString()) {
+ HEAP->external_string_table()->AddString(*string);
+ }
+ if (!result) delete resource;
+ }
+ if (!result) {
+ v8::ThrowException(v8::String::New("externalizeString() failed."));
+ return;
+ }
+}
+
+
+void ExternalizeStringExtension::IsAscii(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 1 || !args[0]->IsString()) {
+ v8::ThrowException(v8::String::New(
+ "isAsciiString() requires a single string argument."));
+ return;
+ }
+ bool is_one_byte =
+ Utils::OpenHandle(*args[0].As<v8::String>())->IsOneByteRepresentation();
+ args.GetReturnValue().Set(is_one_byte);
+}
+
+
+void ExternalizeStringExtension::Register() {
+ static ExternalizeStringExtension externalize_extension;
+ static v8::DeclareExtension declaration(&externalize_extension);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/extensions/externalize-string-extension.h b/chromium/v8/src/extensions/externalize-string-extension.h
new file mode 100644
index 00000000000..ecbc1cf447c
--- /dev/null
+++ b/chromium/v8/src/extensions/externalize-string-extension.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_
+#define V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_
+
+#include "v8.h"
+
+namespace v8 {
+namespace internal {
+
+class ExternalizeStringExtension : public v8::Extension {
+ public:
+ ExternalizeStringExtension() : v8::Extension("v8/externalize", kSource) {}
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<v8::String> name);
+ static void Externalize(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void IsAscii(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Register();
+ private:
+ static const char* const kSource;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_
diff --git a/chromium/v8/src/extensions/gc-extension.cc b/chromium/v8/src/extensions/gc-extension.cc
new file mode 100644
index 00000000000..036b60cb231
--- /dev/null
+++ b/chromium/v8/src/extensions/gc-extension.cc
@@ -0,0 +1,63 @@
+// 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.
+
+#include "gc-extension.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+
+v8::Handle<v8::FunctionTemplate> GCExtension::GetNativeFunction(
+ v8::Handle<v8::String> str) {
+ return v8::FunctionTemplate::New(GCExtension::GC);
+}
+
+
+void GCExtension::GC(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args[0]->BooleanValue()) {
+ HEAP->CollectGarbage(NEW_SPACE, "gc extension");
+ } else {
+ HEAP->CollectAllGarbage(Heap::kNoGCFlags, "gc extension");
+ }
+}
+
+
+void GCExtension::Register() {
+ static char buffer[50];
+ Vector<char> temp_vector(buffer, sizeof(buffer));
+ if (FLAG_expose_gc_as != NULL && strlen(FLAG_expose_gc_as) != 0) {
+ OS::SNPrintF(temp_vector, "native function %s();", FLAG_expose_gc_as);
+ } else {
+ OS::SNPrintF(temp_vector, "native function gc();");
+ }
+
+ static GCExtension gc_extension(buffer);
+ static v8::DeclareExtension declaration(&gc_extension);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/extensions/gc-extension.h b/chromium/v8/src/extensions/gc-extension.h
new file mode 100644
index 00000000000..e412b92a4d9
--- /dev/null
+++ b/chromium/v8/src/extensions/gc-extension.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef V8_EXTENSIONS_GC_EXTENSION_H_
+#define V8_EXTENSIONS_GC_EXTENSION_H_
+
+#include "v8.h"
+
+namespace v8 {
+namespace internal {
+
+class GCExtension : public v8::Extension {
+ public:
+ explicit GCExtension(const char* source) : v8::Extension("v8/gc", source) {}
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<v8::String> name);
+ static void GC(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Register();
+};
+
+} } // namespace v8::internal
+
+#endif // V8_EXTENSIONS_GC_EXTENSION_H_
diff --git a/chromium/v8/src/extensions/i18n/break-iterator.cc b/chromium/v8/src/extensions/i18n/break-iterator.cc
new file mode 100644
index 00000000000..0681e264ab0
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/break-iterator.cc
@@ -0,0 +1,333 @@
+// Copyright 2013 the V8 project authors. 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 "break-iterator.h"
+
+#include <string.h>
+
+#include "i18n-utils.h"
+#include "unicode/brkiter.h"
+#include "unicode/locid.h"
+#include "unicode/rbbi.h"
+
+namespace v8_i18n {
+
+static v8::Handle<v8::Value> ThrowUnexpectedObjectError();
+static icu::UnicodeString* ResetAdoptedText(v8::Handle<v8::Object>,
+ v8::Handle<v8::Value>);
+static icu::BreakIterator* InitializeBreakIterator(v8::Handle<v8::String>,
+ v8::Handle<v8::Object>,
+ v8::Handle<v8::Object>);
+static icu::BreakIterator* CreateICUBreakIterator(const icu::Locale&,
+ v8::Handle<v8::Object>);
+static void SetResolvedSettings(const icu::Locale&,
+ icu::BreakIterator*,
+ v8::Handle<v8::Object>);
+
+icu::BreakIterator* BreakIterator::UnpackBreakIterator(
+ v8::Handle<v8::Object> obj) {
+ v8::HandleScope handle_scope;
+
+ // v8::ObjectTemplate doesn't have HasInstance method so we can't check
+ // if obj is an instance of BreakIterator class. We'll check for a property
+ // that has to be in the object. The same applies to other services, like
+ // Collator and DateTimeFormat.
+ if (obj->HasOwnProperty(v8::String::New("breakIterator"))) {
+ return static_cast<icu::BreakIterator*>(
+ obj->GetAlignedPointerFromInternalField(0));
+ }
+
+ return NULL;
+}
+
+void BreakIterator::DeleteBreakIterator(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param) {
+ // 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.
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object);
+ delete UnpackBreakIterator(handle);
+
+ delete static_cast<icu::UnicodeString*>(
+ handle->GetAlignedPointerFromInternalField(1));
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
+
+// 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.")));
+}
+
+
+// Deletes the old value and sets the adopted text in corresponding
+// JavaScript object.
+icu::UnicodeString* 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->GetAlignedPointerFromInternalField(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->SetAlignedPointerInInternalField(1, text);
+
+ // Return new unicode string pointer.
+ return text;
+}
+
+void BreakIterator::JSInternalBreakIteratorAdoptText(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New(
+ "Internal error. Iterator and text have to be specified.")));
+ return;
+ }
+
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ break_iterator->setText(*ResetAdoptedText(args[0]->ToObject(), args[1]));
+}
+
+void BreakIterator::JSInternalBreakIteratorFirst(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ args.GetReturnValue().Set(static_cast<int32_t>(break_iterator->first()));
+}
+
+void BreakIterator::JSInternalBreakIteratorNext(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ args.GetReturnValue().Set(static_cast<int32_t>(break_iterator->next()));
+}
+
+void BreakIterator::JSInternalBreakIteratorCurrent(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ args.GetReturnValue().Set(static_cast<int32_t>(break_iterator->current()));
+}
+
+void BreakIterator::JSInternalBreakIteratorBreakType(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ icu::BreakIterator* break_iterator = UnpackBreakIterator(args[0]->ToObject());
+ if (!break_iterator) {
+ ThrowUnexpectedObjectError();
+ return;
+ }
+
+ // 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.
+ v8::Handle<v8::String> result;
+ if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
+ result = v8::String::New("none");
+ } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) {
+ result = v8::String::New("number");
+ } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) {
+ result = v8::String::New("letter");
+ } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) {
+ result = v8::String::New("kana");
+ } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) {
+ result = v8::String::New("ideo");
+ } else {
+ result = v8::String::New("unknown");
+ }
+ args.GetReturnValue().Set(result);
+}
+
+void BreakIterator::JSCreateBreakIterator(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsObject() ||
+ !args[2]->IsObject()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, wrong parameters.")));
+ return;
+ }
+
+ v8::Isolate* isolate = args.GetIsolate();
+ v8::Local<v8::ObjectTemplate> break_iterator_template =
+ Utils::GetTemplate2(isolate);
+
+ // Create an empty object wrapper.
+ v8::Local<v8::Object> local_object = break_iterator_template->NewInstance();
+ // But the handle shouldn't be empty.
+ // That can happen if there was a stack overflow when creating the object.
+ if (local_object.IsEmpty()) {
+ args.GetReturnValue().Set(local_object);
+ return;
+ }
+
+ // Set break iterator as internal field of the resulting JS object.
+ icu::BreakIterator* break_iterator = InitializeBreakIterator(
+ args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject());
+
+ if (!break_iterator) {
+ v8::ThrowException(v8::Exception::Error(v8::String::New(
+ "Internal error. Couldn't create ICU break iterator.")));
+ return;
+ } else {
+ local_object->SetAlignedPointerInInternalField(0, break_iterator);
+ // Make sure that the pointer to adopted text is NULL.
+ local_object->SetAlignedPointerInInternalField(1, NULL);
+
+ v8::TryCatch try_catch;
+ local_object->Set(v8::String::New("breakIterator"),
+ v8::String::New("valid"));
+ if (try_catch.HasCaught()) {
+ v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Internal error, couldn't set property.")));
+ return;
+ }
+ }
+
+ v8::Persistent<v8::Object> wrapper(isolate, local_object);
+ // Make object handle weak so we can delete iterator once GC kicks in.
+ wrapper.MakeWeak<void>(NULL, &DeleteBreakIterator);
+ args.GetReturnValue().Set(wrapper);
+ wrapper.ClearAndLeak();
+}
+
+static icu::BreakIterator* InitializeBreakIterator(
+ v8::Handle<v8::String> locale,
+ v8::Handle<v8::Object> options,
+ v8::Handle<v8::Object> resolved) {
+ // Convert BCP47 into ICU locale format.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale icu_locale;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+ v8::String::AsciiValue bcp47_locale(locale);
+ if (bcp47_locale.length() != 0) {
+ uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &status);
+ if (U_FAILURE(status) || icu_length == 0) {
+ return NULL;
+ }
+ icu_locale = icu::Locale(icu_result);
+ }
+
+ icu::BreakIterator* break_iterator =
+ CreateICUBreakIterator(icu_locale, options);
+ if (!break_iterator) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ break_iterator = CreateICUBreakIterator(no_extension_locale, options);
+
+ // Set resolved settings (locale).
+ SetResolvedSettings(no_extension_locale, break_iterator, resolved);
+ } else {
+ SetResolvedSettings(icu_locale, break_iterator, resolved);
+ }
+
+ return break_iterator;
+}
+
+static icu::BreakIterator* CreateICUBreakIterator(
+ const icu::Locale& icu_locale, v8::Handle<v8::Object> options) {
+ UErrorCode status = U_ZERO_ERROR;
+ icu::BreakIterator* break_iterator = NULL;
+ icu::UnicodeString type;
+ if (!Utils::ExtractStringSetting(options, "type", &type)) {
+ // Type had to be in the options. This would be an internal error.
+ return NULL;
+ }
+
+ if (type == UNICODE_STRING_SIMPLE("character")) {
+ break_iterator =
+ icu::BreakIterator::createCharacterInstance(icu_locale, status);
+ } else if (type == UNICODE_STRING_SIMPLE("sentence")) {
+ break_iterator =
+ icu::BreakIterator::createSentenceInstance(icu_locale, status);
+ } else if (type == UNICODE_STRING_SIMPLE("line")) {
+ break_iterator =
+ icu::BreakIterator::createLineInstance(icu_locale, status);
+ } else {
+ // Defualt is word iterator.
+ break_iterator =
+ icu::BreakIterator::createWordInstance(icu_locale, status);
+ }
+
+ if (U_FAILURE(status)) {
+ delete break_iterator;
+ return NULL;
+ }
+
+ return break_iterator;
+}
+
+static void SetResolvedSettings(const icu::Locale& icu_locale,
+ icu::BreakIterator* date_format,
+ v8::Handle<v8::Object> resolved) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ resolved->Set(v8::String::New("locale"), v8::String::New(result));
+ } else {
+ // This would never happen, since we got the locale from ICU.
+ resolved->Set(v8::String::New("locale"), v8::String::New("und"));
+ }
+}
+
+} // namespace v8_i18n
diff --git a/chromium/v8/src/extensions/i18n/break-iterator.h b/chromium/v8/src/extensions/i18n/break-iterator.h
new file mode 100644
index 00000000000..c44c20fbc8a
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/break-iterator.h
@@ -0,0 +1,85 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_BREAK_ITERATOR_H_
+#define V8_EXTENSIONS_I18N_BREAK_ITERATOR_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace U_ICU_NAMESPACE {
+class BreakIterator;
+class UnicodeString;
+}
+
+namespace v8_i18n {
+
+class BreakIterator {
+ public:
+ static void JSCreateBreakIterator(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Helper methods for various bindings.
+
+ // Unpacks iterator object from corresponding JavaScript object.
+ static icu::BreakIterator* UnpackBreakIterator(v8::Handle<v8::Object> obj);
+
+ // Release memory we allocated for the BreakIterator once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteBreakIterator(v8::Isolate* isolate,
+ v8::Persistent<v8::Object>* object,
+ void* param);
+
+ // Assigns new text to the iterator.
+ static void JSInternalBreakIteratorAdoptText(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Moves iterator to the beginning of the string and returns new position.
+ static void JSInternalBreakIteratorFirst(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Moves iterator to the next position and returns it.
+ static void JSInternalBreakIteratorNext(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Returns current iterator's current position.
+ static void JSInternalBreakIteratorCurrent(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ // Returns type of the item from current position.
+ // This call is only valid for word break iterators. Others just return 0.
+ static void JSInternalBreakIteratorBreakType(
+ const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ private:
+ BreakIterator() {}
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_BREAK_ITERATOR_H_
diff --git a/chromium/v8/src/extensions/i18n/break-iterator.js b/chromium/v8/src/extensions/i18n/break-iterator.js
new file mode 100644
index 00000000000..eefd8c2ab1e
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/break-iterator.js
@@ -0,0 +1,197 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Initializes the given object so it's a valid BreakIterator instance.
+ * Useful for subclassing.
+ */
+function initializeBreakIterator(iterator, locales, options) {
+ native function NativeJSCreateBreakIterator();
+
+ if (iterator.hasOwnProperty('__initializedIntlObject')) {
+ throw new TypeError('Trying to re-initialize v8BreakIterator object.');
+ }
+
+ if (options === undefined) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'breakiterator');
+
+ var internalOptions = {};
+
+ defineWEProperty(internalOptions, 'type', getOption(
+ 'type', 'string', ['character', 'word', 'sentence', 'line'], 'word'));
+
+ var locale = resolveLocale('breakiterator', locales, options);
+ var resolved = Object.defineProperties({}, {
+ requestedLocale: {value: locale.locale, writable: true},
+ type: {value: internalOptions.type, writable: true},
+ locale: {writable: true}
+ });
+
+ var internalIterator = NativeJSCreateBreakIterator(locale.locale,
+ internalOptions,
+ resolved);
+
+ Object.defineProperty(iterator, 'iterator', {value: internalIterator});
+ Object.defineProperty(iterator, 'resolved', {value: resolved});
+ Object.defineProperty(iterator, '__initializedIntlObject',
+ {value: 'breakiterator'});
+
+ return iterator;
+}
+
+
+/**
+ * Constructs Intl.v8BreakIterator object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%SetProperty(Intl, 'v8BreakIterator', function() {
+ var locales = arguments[0];
+ var options = arguments[1];
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.v8BreakIterator(locales, options);
+ }
+
+ return initializeBreakIterator(toObject(this), locales, options);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+
+
+/**
+ * BreakIterator resolvedOptions method.
+ */
+%SetProperty(Intl.v8BreakIterator.prototype, 'resolvedOptions', function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject !== 'breakiterator') {
+ throw new TypeError('resolvedOptions method called on a non-object or ' +
+ 'on a object that is not Intl.v8BreakIterator.');
+ }
+
+ var segmenter = this;
+ var locale = getOptimalLanguageTag(segmenter.resolved.requestedLocale,
+ segmenter.resolved.locale);
+
+ return {
+ locale: locale,
+ type: segmenter.resolved.type
+ };
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.v8BreakIterator.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.v8BreakIterator.prototype.resolvedOptions);
+%SetNativeFlag(Intl.v8BreakIterator.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%SetProperty(Intl.v8BreakIterator, 'supportedLocalesOf', function(locales) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ return supportedLocalesOf('breakiterator', locales, arguments[1]);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.v8BreakIterator.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.v8BreakIterator.supportedLocalesOf);
+%SetNativeFlag(Intl.v8BreakIterator.supportedLocalesOf);
+
+
+/**
+ * Adopts text to segment using the iterator. Old text, if present,
+ * gets discarded.
+ */
+function adoptText(iterator, text) {
+ native function NativeJSBreakIteratorAdoptText();
+ NativeJSBreakIteratorAdoptText(iterator.iterator, String(text));
+}
+
+
+/**
+ * Returns index of the first break in the string and moves current pointer.
+ */
+function first(iterator) {
+ native function NativeJSBreakIteratorFirst();
+ return NativeJSBreakIteratorFirst(iterator.iterator);
+}
+
+
+/**
+ * Returns the index of the next break and moves the pointer.
+ */
+function next(iterator) {
+ native function NativeJSBreakIteratorNext();
+ return NativeJSBreakIteratorNext(iterator.iterator);
+}
+
+
+/**
+ * Returns index of the current break.
+ */
+function current(iterator) {
+ native function NativeJSBreakIteratorCurrent();
+ return NativeJSBreakIteratorCurrent(iterator.iterator);
+}
+
+
+/**
+ * Returns type of the current break.
+ */
+function breakType(iterator) {
+ native function NativeJSBreakIteratorBreakType();
+ return NativeJSBreakIteratorBreakType(iterator.iterator);
+}
+
+
+addBoundMethod(Intl.v8BreakIterator, 'adoptText', adoptText, 1);
+addBoundMethod(Intl.v8BreakIterator, 'first', first, 0);
+addBoundMethod(Intl.v8BreakIterator, 'next', next, 0);
+addBoundMethod(Intl.v8BreakIterator, 'current', current, 0);
+addBoundMethod(Intl.v8BreakIterator, 'breakType', breakType, 0);
diff --git a/chromium/v8/src/extensions/i18n/collator.js b/chromium/v8/src/extensions/i18n/collator.js
new file mode 100644
index 00000000000..d8d247b36f4
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/collator.js
@@ -0,0 +1,209 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Initializes the given object so it's a valid Collator instance.
+ * Useful for subclassing.
+ */
+function initializeCollator(collator, locales, options) {
+ if (collator.hasOwnProperty('__initializedIntlObject')) {
+ throw new TypeError('Trying to re-initialize Collator object.');
+ }
+
+ if (options === undefined) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'collator');
+
+ var internalOptions = {};
+
+ defineWEProperty(internalOptions, 'usage', getOption(
+ 'usage', 'string', ['sort', 'search'], 'sort'));
+
+ var sensitivity = getOption('sensitivity', 'string',
+ ['base', 'accent', 'case', 'variant']);
+ if (sensitivity === undefined && internalOptions.usage === 'sort') {
+ sensitivity = 'variant';
+ }
+ defineWEProperty(internalOptions, 'sensitivity', sensitivity);
+
+ defineWEProperty(internalOptions, 'ignorePunctuation', getOption(
+ 'ignorePunctuation', 'boolean', undefined, false));
+
+ var locale = resolveLocale('collator', locales, options);
+
+ // ICU can't take kb, kc... parameters through localeID, so we need to pass
+ // them as options.
+ // One exception is -co- which has to be part of the extension, but only for
+ // usage: sort, and its value can't be 'standard' or 'search'.
+ var extensionMap = parseExtension(locale.extension);
+ setOptions(
+ options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions);
+
+ var collation = 'default';
+ var extension = '';
+ if (extensionMap.hasOwnProperty('co') && internalOptions.usage === 'sort') {
+ if (ALLOWED_CO_VALUES.indexOf(extensionMap.co) !== -1) {
+ extension = '-u-co-' + extensionMap.co;
+ // ICU can't tell us what the collation is, so save user's input.
+ collation = extensionMap.co;
+ }
+ } else if (internalOptions.usage === 'search') {
+ extension = '-u-co-search';
+ }
+ defineWEProperty(internalOptions, 'collation', collation);
+
+ var requestedLocale = locale.locale + extension;
+
+ // We define all properties C++ code may produce, to prevent security
+ // problems. If malicious user decides to redefine Object.prototype.locale
+ // we can't just use plain x.locale = 'us' or in C++ Set("locale", "us").
+ // Object.defineProperties will either succeed defining or throw an error.
+ var resolved = Object.defineProperties({}, {
+ caseFirst: {writable: true},
+ collation: {value: internalOptions.collation, writable: true},
+ ignorePunctuation: {writable: true},
+ locale: {writable: true},
+ numeric: {writable: true},
+ requestedLocale: {value: requestedLocale, writable: true},
+ sensitivity: {writable: true},
+ strength: {writable: true},
+ usage: {value: internalOptions.usage, writable: true}
+ });
+
+ var internalCollator = %CreateCollator(requestedLocale,
+ internalOptions,
+ resolved);
+
+ // Writable, configurable and enumerable are set to false by default.
+ Object.defineProperty(collator, 'collator', {value: internalCollator});
+ Object.defineProperty(collator, '__initializedIntlObject',
+ {value: 'collator'});
+ Object.defineProperty(collator, 'resolved', {value: resolved});
+
+ return collator;
+}
+
+
+/**
+ * Constructs Intl.Collator object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%SetProperty(Intl, 'Collator', function() {
+ var locales = arguments[0];
+ var options = arguments[1];
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.Collator(locales, options);
+ }
+
+ return initializeCollator(toObject(this), locales, options);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+
+
+/**
+ * Collator resolvedOptions method.
+ */
+%SetProperty(Intl.Collator.prototype, 'resolvedOptions', function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject !== 'collator') {
+ throw new TypeError('resolvedOptions method called on a non-object ' +
+ 'or on a object that is not Intl.Collator.');
+ }
+
+ var coll = this;
+ var locale = getOptimalLanguageTag(coll.resolved.requestedLocale,
+ coll.resolved.locale);
+
+ return {
+ locale: locale,
+ usage: coll.resolved.usage,
+ sensitivity: coll.resolved.sensitivity,
+ ignorePunctuation: coll.resolved.ignorePunctuation,
+ numeric: coll.resolved.numeric,
+ caseFirst: coll.resolved.caseFirst,
+ collation: coll.resolved.collation
+ };
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.Collator.prototype.resolvedOptions, 'resolvedOptions');
+%FunctionRemovePrototype(Intl.Collator.prototype.resolvedOptions);
+%SetNativeFlag(Intl.Collator.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%SetProperty(Intl.Collator, 'supportedLocalesOf', function(locales) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ return supportedLocalesOf('collator', locales, arguments[1]);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.Collator.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.Collator.supportedLocalesOf);
+%SetNativeFlag(Intl.Collator.supportedLocalesOf);
+
+
+/**
+ * When the compare method is called with two arguments x and y, it returns a
+ * Number other than NaN that represents the result of a locale-sensitive
+ * String comparison of x with y.
+ * The result is intended to order String values in the sort order specified
+ * by the effective locale and collation options computed during construction
+ * of this Collator object, and will be negative, zero, or positive, depending
+ * on whether x comes before y in the sort order, the Strings are equal under
+ * the sort order, or x comes after y in the sort order, respectively.
+ */
+function compare(collator, x, y) {
+ return %InternalCompare(collator.collator, String(x), String(y));
+};
+
+
+addBoundMethod(Intl.Collator, 'compare', compare, 2);
diff --git a/chromium/v8/src/extensions/i18n/date-format.js b/chromium/v8/src/extensions/i18n/date-format.js
new file mode 100644
index 00000000000..b1d28e535cd
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/date-format.js
@@ -0,0 +1,474 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Returns a string that matches LDML representation of the options object.
+ */
+function toLDMLString(options) {
+ var getOption = getGetOption(options, 'dateformat');
+
+ var ldmlString = '';
+
+ var option = getOption('weekday', 'string', ['narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(
+ option, {narrow: 'EEEEE', short: 'EEE', long: 'EEEE'});
+
+ option = getOption('era', 'string', ['narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(
+ option, {narrow: 'GGGGG', short: 'GGG', long: 'GGGG'});
+
+ option = getOption('year', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'yy', 'numeric': 'y'});
+
+ option = getOption('month', 'string',
+ ['2-digit', 'numeric', 'narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M',
+ 'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'});
+
+ option = getOption('day', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(
+ option, {'2-digit': 'dd', 'numeric': 'd'});
+
+ var hr12 = getOption('hour12', 'boolean');
+ option = getOption('hour', 'string', ['2-digit', 'numeric']);
+ if (hr12 === undefined) {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'});
+ } else if (hr12 === true) {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'});
+ } else {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'});
+ }
+
+ option = getOption('minute', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'});
+
+ option = getOption('second', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'});
+
+ option = getOption('timeZoneName', 'string', ['short', 'long']);
+ ldmlString += appendToLDMLString(option, {short: 'v', long: 'vv'});
+
+ return ldmlString;
+}
+
+
+/**
+ * Returns either LDML equivalent of the current option or empty string.
+ */
+function appendToLDMLString(option, pairs) {
+ if (option !== undefined) {
+ return pairs[option];
+ } else {
+ return '';
+ }
+}
+
+
+/**
+ * Returns object that matches LDML representation of the date.
+ */
+function fromLDMLString(ldmlString) {
+ // First remove '' quoted text, so we lose 'Uhr' strings.
+ ldmlString = ldmlString.replace(QUOTED_STRING_RE, '');
+
+ var options = {};
+ var match = ldmlString.match(/E{3,5}/g);
+ options = appendToDateTimeObject(
+ options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'});
+
+ match = ldmlString.match(/G{3,5}/g);
+ options = appendToDateTimeObject(
+ options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'});
+
+ match = ldmlString.match(/y{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'year', match, {y: 'numeric', yy: '2-digit'});
+
+ match = ldmlString.match(/M{1,5}/g);
+ options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit',
+ M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'});
+
+ // Sometimes we get L instead of M for month - standalone name.
+ match = ldmlString.match(/L{1,5}/g);
+ options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit',
+ L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'});
+
+ match = ldmlString.match(/d{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'day', match, {d: 'numeric', dd: '2-digit'});
+
+ match = ldmlString.match(/h{1,2}/g);
+ if (match !== null) {
+ options['hour12'] = true;
+ }
+ options = appendToDateTimeObject(
+ options, 'hour', match, {h: 'numeric', hh: '2-digit'});
+
+ match = ldmlString.match(/H{1,2}/g);
+ if (match !== null) {
+ options['hour12'] = false;
+ }
+ options = appendToDateTimeObject(
+ options, 'hour', match, {H: 'numeric', HH: '2-digit'});
+
+ match = ldmlString.match(/m{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'minute', match, {m: 'numeric', mm: '2-digit'});
+
+ match = ldmlString.match(/s{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'second', match, {s: 'numeric', ss: '2-digit'});
+
+ match = ldmlString.match(/v{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'timeZoneName', match, {v: 'short', vv: 'long'});
+
+ return options;
+}
+
+
+function appendToDateTimeObject(options, option, match, pairs) {
+ if (match === null) {
+ if (!options.hasOwnProperty(option)) {
+ defineWEProperty(options, option, undefined);
+ }
+ return options;
+ }
+
+ var property = match[0];
+ defineWEProperty(options, option, pairs[property]);
+
+ return options;
+}
+
+
+/**
+ * Returns options with at least default values in it.
+ */
+function toDateTimeOptions(options, required, defaults) {
+ if (options === undefined) {
+ options = null;
+ } else {
+ options = toObject(options);
+ }
+
+ options = Object.apply(this, [options]);
+
+ var needsDefault = true;
+ if ((required === 'date' || required === 'any') &&
+ (options.weekday !== undefined || options.year !== undefined ||
+ options.month !== undefined || options.day !== undefined)) {
+ needsDefault = false;
+ }
+
+ if ((required === 'time' || required === 'any') &&
+ (options.hour !== undefined || options.minute !== undefined ||
+ options.second !== undefined)) {
+ needsDefault = false;
+ }
+
+ if (needsDefault && (defaults === 'date' || defaults === 'all')) {
+ Object.defineProperty(options, 'year', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ Object.defineProperty(options, 'month', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ Object.defineProperty(options, 'day', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ }
+
+ if (needsDefault && (defaults === 'time' || defaults === 'all')) {
+ Object.defineProperty(options, 'hour', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ Object.defineProperty(options, 'minute', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ Object.defineProperty(options, 'second', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ }
+
+ return options;
+}
+
+
+/**
+ * Initializes the given object so it's a valid DateTimeFormat instance.
+ * Useful for subclassing.
+ */
+function initializeDateTimeFormat(dateFormat, locales, options) {
+
+ if (dateFormat.hasOwnProperty('__initializedIntlObject')) {
+ throw new TypeError('Trying to re-initialize DateTimeFormat object.');
+ }
+
+ if (options === undefined) {
+ options = {};
+ }
+
+ var locale = resolveLocale('dateformat', locales, options);
+
+ options = toDateTimeOptions(options, 'any', 'date');
+
+ var getOption = getGetOption(options, 'dateformat');
+
+ // We implement only best fit algorithm, but still need to check
+ // if the formatMatcher values are in range.
+ var matcher = getOption('formatMatcher', 'string',
+ ['basic', 'best fit'], 'best fit');
+
+ // Build LDML string for the skeleton that we pass to the formatter.
+ var ldmlString = toLDMLString(options);
+
+ // Filter out supported extension keys so we know what to put in resolved
+ // section later on.
+ // We need to pass calendar and number system to the method.
+ var tz = canonicalizeTimeZoneID(options.timeZone);
+
+ // ICU prefers options to be passed using -u- extension key/values, so
+ // we need to build that.
+ var internalOptions = {};
+ var extensionMap = parseExtension(locale.extension);
+ var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP,
+ getOption, internalOptions);
+
+ var requestedLocale = locale.locale + extension;
+ var resolved = Object.defineProperties({}, {
+ calendar: {writable: true},
+ day: {writable: true},
+ era: {writable: true},
+ hour12: {writable: true},
+ hour: {writable: true},
+ locale: {writable: true},
+ minute: {writable: true},
+ month: {writable: true},
+ numberingSystem: {writable: true},
+ pattern: {writable: true},
+ requestedLocale: {value: requestedLocale, writable: true},
+ second: {writable: true},
+ timeZone: {writable: true},
+ timeZoneName: {writable: true},
+ tz: {value: tz, writable: true},
+ weekday: {writable: true},
+ year: {writable: true}
+ });
+
+ var formatter = %CreateDateTimeFormat(
+ requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved);
+
+ if (tz !== undefined && tz !== resolved.timeZone) {
+ throw new RangeError('Unsupported time zone specified ' + tz);
+ }
+
+ Object.defineProperty(dateFormat, 'formatter', {value: formatter});
+ Object.defineProperty(dateFormat, 'resolved', {value: resolved});
+ Object.defineProperty(dateFormat, '__initializedIntlObject',
+ {value: 'dateformat'});
+
+ return dateFormat;
+}
+
+
+/**
+ * Constructs Intl.DateTimeFormat object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%SetProperty(Intl, 'DateTimeFormat', function() {
+ var locales = arguments[0];
+ var options = arguments[1];
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.DateTimeFormat(locales, options);
+ }
+
+ return initializeDateTimeFormat(toObject(this), locales, options);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+
+
+/**
+ * DateTimeFormat resolvedOptions method.
+ */
+%SetProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject !== 'dateformat') {
+ throw new TypeError('resolvedOptions method called on a non-object or ' +
+ 'on a object that is not Intl.DateTimeFormat.');
+ }
+
+ var format = this;
+ var fromPattern = fromLDMLString(format.resolved.pattern);
+ var userCalendar = ICU_CALENDAR_MAP[format.resolved.calendar];
+ if (userCalendar === undefined) {
+ // Use ICU name if we don't have a match. It shouldn't happen, but
+ // it would be too strict to throw for this.
+ userCalendar = format.resolved.calendar;
+ }
+
+ var locale = getOptimalLanguageTag(format.resolved.requestedLocale,
+ format.resolved.locale);
+
+ var result = {
+ locale: locale,
+ numberingSystem: format.resolved.numberingSystem,
+ calendar: userCalendar,
+ timeZone: format.resolved.timeZone
+ };
+
+ addWECPropertyIfDefined(result, 'timeZoneName', fromPattern.timeZoneName);
+ addWECPropertyIfDefined(result, 'era', fromPattern.era);
+ addWECPropertyIfDefined(result, 'year', fromPattern.year);
+ addWECPropertyIfDefined(result, 'month', fromPattern.month);
+ addWECPropertyIfDefined(result, 'day', fromPattern.day);
+ addWECPropertyIfDefined(result, 'weekday', fromPattern.weekday);
+ addWECPropertyIfDefined(result, 'hour12', fromPattern.hour12);
+ addWECPropertyIfDefined(result, 'hour', fromPattern.hour);
+ addWECPropertyIfDefined(result, 'minute', fromPattern.minute);
+ addWECPropertyIfDefined(result, 'second', fromPattern.second);
+
+ return result;
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.DateTimeFormat.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.DateTimeFormat.prototype.resolvedOptions);
+%SetNativeFlag(Intl.DateTimeFormat.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%SetProperty(Intl.DateTimeFormat, 'supportedLocalesOf', function(locales) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ return supportedLocalesOf('dateformat', locales, arguments[1]);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.DateTimeFormat.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.DateTimeFormat.supportedLocalesOf);
+%SetNativeFlag(Intl.DateTimeFormat.supportedLocalesOf);
+
+
+/**
+ * Returns a String value representing the result of calling ToNumber(date)
+ * according to the effective locale and the formatting options of this
+ * DateTimeFormat.
+ */
+function formatDate(formatter, dateValue) {
+ var dateMs;
+ if (dateValue === undefined) {
+ dateMs = Date.now();
+ } else {
+ dateMs = Number(dateValue);
+ }
+
+ if (!isFinite(dateMs)) {
+ throw new RangeError('Provided date is not in valid range.');
+ }
+
+ return %InternalDateFormat(formatter.formatter, new Date(dateMs));
+}
+
+
+/**
+ * Returns a Date object representing the result of calling ToString(value)
+ * according to the effective locale and the formatting options of this
+ * DateTimeFormat.
+ * Returns undefined if date string cannot be parsed.
+ */
+function parseDate(formatter, value) {
+ return %InternalDateParse(formatter.formatter, String(value));
+}
+
+
+// 0 because date is optional argument.
+addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0);
+addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1);
+
+
+/**
+ * Returns canonical Area/Location name, or throws an exception if the zone
+ * name is invalid IANA name.
+ */
+function canonicalizeTimeZoneID(tzID) {
+ // Skip undefined zones.
+ if (tzID === undefined) {
+ return tzID;
+ }
+
+ // Special case handling (UTC, GMT).
+ var upperID = tzID.toUpperCase();
+ if (upperID === 'UTC' || upperID === 'GMT' ||
+ upperID === 'ETC/UTC' || upperID === 'ETC/GMT') {
+ return 'UTC';
+ }
+
+ // We expect only _ and / beside ASCII letters.
+ // All inputs should conform to Area/Location from now on.
+ var match = TIMEZONE_NAME_CHECK_RE.exec(tzID);
+ if (match === null) {
+ throw new RangeError('Expected Area/Location for time zone, got ' + tzID);
+ }
+
+ var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]);
+ var i = 3;
+ while (match[i] !== undefined && i < match.length) {
+ result = result + '_' + toTitleCaseWord(match[i]);
+ i++;
+ }
+
+ return result;
+}
diff --git a/chromium/v8/src/extensions/i18n/footer.js b/chromium/v8/src/extensions/i18n/footer.js
new file mode 100644
index 00000000000..adaa6334624
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/footer.js
@@ -0,0 +1,40 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+// Fix RegExp global state so we don't fail WebKit layout test:
+// fast/js/regexp-caching.html
+// It seems that 'g' or test() operations leave state changed.
+var CLEANUP_RE = new RegExp('');
+CLEANUP_RE.test('');
+
+return Intl;
+}())});
diff --git a/chromium/v8/src/extensions/i18n/globals.js b/chromium/v8/src/extensions/i18n/globals.js
new file mode 100644
index 00000000000..68fabe777f5
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/globals.js
@@ -0,0 +1,168 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+
+/**
+ * List of available services.
+ */
+var AVAILABLE_SERVICES = ['collator',
+ 'numberformat',
+ 'dateformat',
+ 'breakiterator'];
+
+/**
+ * Caches available locales for each service.
+ */
+var AVAILABLE_LOCALES = {
+ 'collator': undefined,
+ 'numberformat': undefined,
+ 'dateformat': undefined,
+ 'breakiterator': undefined
+};
+
+/**
+ * Caches default ICU locale.
+ */
+var DEFAULT_ICU_LOCALE = undefined;
+
+/**
+ * Unicode extension regular expression.
+ */
+var UNICODE_EXTENSION_RE = new RegExp('-u(-[a-z0-9]{2,8})+', 'g');
+
+/**
+ * Matches any Unicode extension.
+ */
+var ANY_EXTENSION_RE = new RegExp('-[a-z0-9]{1}-.*', 'g');
+
+/**
+ * Replace quoted text (single quote, anything but the quote and quote again).
+ */
+var QUOTED_STRING_RE = new RegExp("'[^']+'", 'g');
+
+/**
+ * Matches valid service name.
+ */
+var SERVICE_RE =
+ new RegExp('^(collator|numberformat|dateformat|breakiterator)$');
+
+/**
+ * Validates a language tag against bcp47 spec.
+ * Actual value is assigned on first run.
+ */
+var LANGUAGE_TAG_RE = undefined;
+
+/**
+ * Helps find duplicate variants in the language tag.
+ */
+var LANGUAGE_VARIANT_RE = undefined;
+
+/**
+ * Helps find duplicate singletons in the language tag.
+ */
+var LANGUAGE_SINGLETON_RE = undefined;
+
+/**
+ * Matches valid IANA time zone names.
+ */
+var TIMEZONE_NAME_CHECK_RE =
+ new RegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$');
+
+/**
+ * Maps ICU calendar names into LDML type.
+ */
+var ICU_CALENDAR_MAP = {
+ 'gregorian': 'gregory',
+ 'japanese': 'japanese',
+ 'buddhist': 'buddhist',
+ 'roc': 'roc',
+ 'persian': 'persian',
+ 'islamic-civil': 'islamicc',
+ 'islamic': 'islamic',
+ 'hebrew': 'hebrew',
+ 'chinese': 'chinese',
+ 'indian': 'indian',
+ 'coptic': 'coptic',
+ 'ethiopic': 'ethiopic',
+ 'ethiopic-amete-alem': 'ethioaa'
+};
+
+/**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a collator.
+ */
+var COLLATOR_KEY_MAP = {
+ 'kn': {'property': 'numeric', 'type': 'boolean'},
+ 'kf': {'property': 'caseFirst', 'type': 'string',
+ 'values': ['false', 'lower', 'upper']}
+};
+
+/**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a number format.
+ */
+var NUMBER_FORMAT_KEY_MAP = {
+ 'nu': {'property': undefined, 'type': 'string'}
+};
+
+/**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a date/time format.
+ */
+var DATETIME_FORMAT_KEY_MAP = {
+ 'ca': {'property': undefined, 'type': 'string'},
+ 'nu': {'property': undefined, 'type': 'string'}
+};
+
+/**
+ * Allowed -u-co- values. List taken from:
+ * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml
+ */
+var ALLOWED_CO_VALUES = [
+ 'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic',
+ 'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin'
+];
+
+/**
+ * Object attributes (configurable, writable, enumerable).
+ * To combine attributes, OR them.
+ * Values/names are copied from v8/include/v8.h:PropertyAttribute
+ */
+var ATTRIBUTES = {
+ 'NONE': 0,
+ 'READ_ONLY': 1,
+ 'DONT_ENUM': 2,
+ 'DONT_DELETE': 4
+};
+
+/**
+ * Error message for when function object is created with new and it's not
+ * a constructor.
+ */
+var ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR =
+ 'Function object that\'s not a constructor was created with new';
diff --git a/chromium/v8/src/extensions/i18n/header.js b/chromium/v8/src/extensions/i18n/header.js
new file mode 100644
index 00000000000..b854ce5eada
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/header.js
@@ -0,0 +1,41 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Intl object is a single object that has some named properties,
+ * all of which are constructors.
+ */
+Object.defineProperty(this, "Intl", { enumerable: false, value: (function() {
+
+'use strict';
+
+var Intl = {};
diff --git a/chromium/v8/src/extensions/i18n/i18n-extension.cc b/chromium/v8/src/extensions/i18n/i18n-extension.cc
new file mode 100644
index 00000000000..e2cba8eb9ef
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/i18n-extension.cc
@@ -0,0 +1,77 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+#include "i18n-extension.h"
+
+#include "break-iterator.h"
+#include "natives.h"
+
+using v8::internal::I18NNatives;
+
+namespace v8_i18n {
+
+Extension::Extension()
+ : v8::Extension("v8/i18n",
+ reinterpret_cast<const char*>(
+ I18NNatives::GetScriptsSource().start()),
+ 0,
+ 0,
+ I18NNatives::GetScriptsSource().length()) {}
+
+v8::Handle<v8::FunctionTemplate> Extension::GetNativeFunction(
+ v8::Handle<v8::String> name) {
+ // Break iterator.
+ if (name->Equals(v8::String::New("NativeJSCreateBreakIterator"))) {
+ return v8::FunctionTemplate::New(BreakIterator::JSCreateBreakIterator);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorAdoptText"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorAdoptText);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorFirst"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorFirst);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorNext"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorNext);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorCurrent"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorCurrent);
+ } else if (name->Equals(v8::String::New("NativeJSBreakIteratorBreakType"))) {
+ return v8::FunctionTemplate::New(
+ BreakIterator::JSInternalBreakIteratorBreakType);
+ }
+
+ return v8::Handle<v8::FunctionTemplate>();
+}
+
+
+void Extension::Register() {
+ static Extension i18n_extension;
+ static v8::DeclareExtension extension_declaration(&i18n_extension);
+}
+
+} // namespace v8_i18n
diff --git a/chromium/v8/src/extensions/i18n/i18n-extension.h b/chromium/v8/src/extensions/i18n/i18n-extension.h
new file mode 100644
index 00000000000..050c336a67a
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/i18n-extension.h
@@ -0,0 +1,51 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_I18N_EXTENSION_H_
+#define V8_EXTENSIONS_I18N_I18N_EXTENSION_H_
+
+#include "v8.h"
+
+namespace v8_i18n {
+
+class Extension : public v8::Extension {
+ public:
+ Extension();
+
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<v8::String> name);
+
+ static void Register();
+
+ private:
+ static Extension* extension_;
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_I18N_EXTENSION_H_
diff --git a/chromium/v8/src/extensions/i18n/i18n-utils.cc b/chromium/v8/src/extensions/i18n/i18n-utils.cc
new file mode 100644
index 00000000000..8c87f0715b9
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/i18n-utils.cc
@@ -0,0 +1,177 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+#include "i18n-utils.h"
+
+#include <string.h>
+
+#include "unicode/unistr.h"
+
+namespace v8_i18n {
+
+// static
+void Utils::StrNCopy(char* dest, int length, const char* src) {
+ if (!dest || !src) return;
+
+ strncpy(dest, src, length);
+ dest[length - 1] = '\0';
+}
+
+
+// static
+bool Utils::V8StringToUnicodeString(const v8::Handle<v8::Value>& input,
+ icu::UnicodeString* output) {
+ v8::String::Utf8Value utf8_value(input);
+
+ if (*utf8_value == NULL) return false;
+
+ output->setTo(icu::UnicodeString::fromUTF8(*utf8_value));
+
+ return true;
+}
+
+
+// static
+bool Utils::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()) {
+ return V8StringToUnicodeString(value, result);
+ }
+ return false;
+}
+
+
+// static
+bool Utils::ExtractIntegerSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ int32_t* 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->IsNumber()) {
+ *result = static_cast<int32_t>(value->Int32Value());
+ return true;
+ }
+ return false;
+}
+
+
+// static
+bool Utils::ExtractBooleanSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ bool* 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->IsBoolean()) {
+ *result = static_cast<bool>(value->BooleanValue());
+ return true;
+ }
+ return false;
+}
+
+
+// static
+void Utils::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;
+}
+
+
+static v8::Local<v8::ObjectTemplate> ToLocal(i::Handle<i::Object> handle) {
+ return v8::Utils::ToLocal(i::Handle<i::ObjectTemplateInfo>::cast(handle));
+}
+
+
+template<int internal_fields, i::EternalHandles::SingletonHandle field>
+static v8::Local<v8::ObjectTemplate> GetEternal(v8::Isolate* external) {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(external);
+ if (isolate->eternal_handles()->Exists(field)) {
+ return ToLocal(isolate->eternal_handles()->GetSingleton(field));
+ }
+ v8::Local<v8::ObjectTemplate> raw_template(v8::ObjectTemplate::New());
+ raw_template->SetInternalFieldCount(internal_fields);
+ return ToLocal(
+ isolate->eternal_handles()->CreateSingleton(
+ isolate,
+ *v8::Utils::OpenHandle(*raw_template),
+ field));
+}
+
+
+// static
+v8::Local<v8::ObjectTemplate> Utils::GetTemplate(v8::Isolate* isolate) {
+ return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate);
+}
+
+
+// static
+v8::Local<v8::ObjectTemplate> Utils::GetTemplate2(v8::Isolate* isolate) {
+ return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate);
+}
+
+
+} // namespace v8_i18n
diff --git a/chromium/v8/src/extensions/i18n/i18n-utils.h b/chromium/v8/src/extensions/i18n/i18n-utils.h
new file mode 100644
index 00000000000..db5d1b6ac0d
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/i18n-utils.h
@@ -0,0 +1,91 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+#ifndef V8_EXTENSIONS_I18N_SRC_UTILS_H_
+#define V8_EXTENSIONS_I18N_SRC_UTILS_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace U_ICU_NAMESPACE {
+class UnicodeString;
+}
+
+namespace v8_i18n {
+
+class Utils {
+ 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);
+
+ // Converts v8::String into UnicodeString. Returns false if input
+ // can't be converted into utf8.
+ static bool V8StringToUnicodeString(const v8::Handle<v8::Value>& input,
+ icu::UnicodeString* output);
+
+ // 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);
+
+ // Extract a Integer setting named in |settings| and set it to |result|.
+ // Return true if it's specified. Otherwise, return false.
+ static bool ExtractIntegerSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ int32_t* result);
+
+ // Extract a Boolean setting named in |settings| and set it to |result|.
+ // Return true if it's specified. Otherwise, return false.
+ static bool ExtractBooleanSetting(const v8::Handle<v8::Object>& settings,
+ const char* setting,
+ bool* 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);
+
+ // Creates an ObjectTemplate with one internal field.
+ static v8::Local<v8::ObjectTemplate> GetTemplate(v8::Isolate* isolate);
+
+ // Creates an ObjectTemplate with two internal fields.
+ static v8::Local<v8::ObjectTemplate> GetTemplate2(v8::Isolate* isolate);
+
+ private:
+ Utils() {}
+};
+
+} // namespace v8_i18n
+
+#endif // V8_EXTENSIONS_I18N_UTILS_H_
diff --git a/chromium/v8/src/extensions/i18n/i18n-utils.js b/chromium/v8/src/extensions/i18n/i18n-utils.js
new file mode 100644
index 00000000000..545082ecbba
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/i18n-utils.js
@@ -0,0 +1,536 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Adds bound method to the prototype of the given object.
+ */
+function addBoundMethod(obj, methodName, implementation, length) {
+ function getter() {
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject === undefined) {
+ throw new TypeError('Method ' + methodName + ' called on a ' +
+ 'non-object or on a wrong type of object.');
+ }
+ var internalName = '__bound' + methodName + '__';
+ if (this[internalName] === undefined) {
+ var that = this;
+ var boundMethod;
+ if (length === undefined || length === 2) {
+ boundMethod = function(x, y) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+ return implementation(that, x, y);
+ }
+ } else if (length === 1) {
+ boundMethod = function(x) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+ return implementation(that, x);
+ }
+ } else {
+ boundMethod = function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+ // DateTimeFormat.format needs to be 0 arg method, but can stil
+ // receive optional dateValue param. If one was provided, pass it
+ // along.
+ if (arguments.length > 0) {
+ return implementation(that, arguments[0]);
+ } else {
+ return implementation(that);
+ }
+ }
+ }
+ %FunctionSetName(boundMethod, internalName);
+ %FunctionRemovePrototype(boundMethod);
+ %SetNativeFlag(boundMethod);
+ this[internalName] = boundMethod;
+ }
+ return this[internalName];
+ }
+
+ %FunctionSetName(getter, methodName);
+ %FunctionRemovePrototype(getter);
+ %SetNativeFlag(getter);
+
+ Object.defineProperty(obj.prototype, methodName, {
+ get: getter,
+ enumerable: false,
+ configurable: true
+ });
+}
+
+
+/**
+ * Returns an intersection of locales and service supported locales.
+ * Parameter locales is treated as a priority list.
+ */
+function supportedLocalesOf(service, locales, options) {
+ if (service.match(SERVICE_RE) === null) {
+ throw new Error('Internal error, wrong service type: ' + service);
+ }
+
+ // Provide defaults if matcher was not specified.
+ if (options === undefined) {
+ options = {};
+ } else {
+ options = toObject(options);
+ }
+
+ var matcher = options.localeMatcher;
+ if (matcher !== undefined) {
+ matcher = String(matcher);
+ if (matcher !== 'lookup' && matcher !== 'best fit') {
+ throw new RangeError('Illegal value for localeMatcher:' + matcher);
+ }
+ } else {
+ matcher = 'best fit';
+ }
+
+ var requestedLocales = initializeLocaleList(locales);
+
+ // Cache these, they don't ever change per service.
+ if (AVAILABLE_LOCALES[service] === undefined) {
+ AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
+ }
+
+ // Use either best fit or lookup algorithm to match locales.
+ if (matcher === 'best fit') {
+ return initializeLocaleList(bestFitSupportedLocalesOf(
+ requestedLocales, AVAILABLE_LOCALES[service]));
+ }
+
+ return initializeLocaleList(lookupSupportedLocalesOf(
+ requestedLocales, AVAILABLE_LOCALES[service]));
+}
+
+
+/**
+ * Returns the subset of the provided BCP 47 language priority list for which
+ * this service has a matching locale when using the BCP 47 Lookup algorithm.
+ * Locales appear in the same order in the returned list as in the input list.
+ */
+function lookupSupportedLocalesOf(requestedLocales, availableLocales) {
+ var matchedLocales = [];
+ for (var i = 0; i < requestedLocales.length; ++i) {
+ // Remove -u- extension.
+ var locale = requestedLocales[i].replace(UNICODE_EXTENSION_RE, '');
+ do {
+ if (availableLocales[locale] !== undefined) {
+ // Push requested locale not the resolved one.
+ matchedLocales.push(requestedLocales[i]);
+ break;
+ }
+ // Truncate locale if possible, if not break.
+ var pos = locale.lastIndexOf('-');
+ if (pos === -1) {
+ break;
+ }
+ locale = locale.substring(0, pos);
+ } while (true);
+ }
+
+ return matchedLocales;
+}
+
+
+/**
+ * Returns the subset of the provided BCP 47 language priority list for which
+ * this service has a matching locale when using the implementation
+ * dependent algorithm.
+ * Locales appear in the same order in the returned list as in the input list.
+ */
+function bestFitSupportedLocalesOf(requestedLocales, availableLocales) {
+ // TODO(cira): implement better best fit algorithm.
+ return lookupSupportedLocalesOf(requestedLocales, availableLocales);
+}
+
+
+/**
+ * Returns a getOption function that extracts property value for given
+ * options object. If property is missing it returns defaultValue. If value
+ * is out of range for that property it throws RangeError.
+ */
+function getGetOption(options, caller) {
+ if (options === undefined) {
+ throw new Error('Internal ' + caller + ' error. ' +
+ 'Default options are missing.');
+ }
+
+ var getOption = function getOption(property, type, values, defaultValue) {
+ if (options[property] !== undefined) {
+ var value = options[property];
+ switch (type) {
+ case 'boolean':
+ value = Boolean(value);
+ break;
+ case 'string':
+ value = String(value);
+ break;
+ case 'number':
+ value = Number(value);
+ break;
+ default:
+ throw new Error('Internal error. Wrong value type.');
+ }
+ if (values !== undefined && values.indexOf(value) === -1) {
+ throw new RangeError('Value ' + value + ' out of range for ' + caller +
+ ' options property ' + property);
+ }
+
+ return value;
+ }
+
+ return defaultValue;
+ }
+
+ return getOption;
+}
+
+
+/**
+ * Compares a BCP 47 language priority list requestedLocales against the locales
+ * in availableLocales and determines the best available language to meet the
+ * request. Two algorithms are available to match the locales: the Lookup
+ * algorithm described in RFC 4647 section 3.4, and an implementation dependent
+ * best-fit algorithm. Independent of the locale matching algorithm, options
+ * specified through Unicode locale extension sequences are negotiated
+ * separately, taking the caller's relevant extension keys and locale data as
+ * well as client-provided options into consideration. Returns an object with
+ * a locale property whose value is the language tag of the selected locale,
+ * and properties for each key in relevantExtensionKeys providing the selected
+ * value for that key.
+ */
+function resolveLocale(service, requestedLocales, options) {
+ requestedLocales = initializeLocaleList(requestedLocales);
+
+ var getOption = getGetOption(options, service);
+ var matcher = getOption('localeMatcher', 'string',
+ ['lookup', 'best fit'], 'best fit');
+ var resolved;
+ if (matcher === 'lookup') {
+ resolved = lookupMatcher(service, requestedLocales);
+ } else {
+ resolved = bestFitMatcher(service, requestedLocales);
+ }
+
+ return resolved;
+}
+
+
+/**
+ * Returns best matched supported locale and extension info using basic
+ * lookup algorithm.
+ */
+function lookupMatcher(service, requestedLocales) {
+ if (service.match(SERVICE_RE) === null) {
+ throw new Error('Internal error, wrong service type: ' + service);
+ }
+
+ // Cache these, they don't ever change per service.
+ if (AVAILABLE_LOCALES[service] === undefined) {
+ AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
+ }
+
+ for (var i = 0; i < requestedLocales.length; ++i) {
+ // Remove all extensions.
+ var locale = requestedLocales[i].replace(ANY_EXTENSION_RE, '');
+ do {
+ if (AVAILABLE_LOCALES[service][locale] !== undefined) {
+ // Return the resolved locale and extension.
+ var extensionMatch = requestedLocales[i].match(UNICODE_EXTENSION_RE);
+ var extension = (extensionMatch === null) ? '' : extensionMatch[0];
+ return {'locale': locale, 'extension': extension, 'position': i};
+ }
+ // Truncate locale if possible.
+ var pos = locale.lastIndexOf('-');
+ if (pos === -1) {
+ break;
+ }
+ locale = locale.substring(0, pos);
+ } while (true);
+ }
+
+ // Didn't find a match, return default.
+ if (DEFAULT_ICU_LOCALE === undefined) {
+ DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
+ }
+
+ return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1};
+}
+
+
+/**
+ * Returns best matched supported locale and extension info using
+ * implementation dependend algorithm.
+ */
+function bestFitMatcher(service, requestedLocales) {
+ // TODO(cira): implement better best fit algorithm.
+ return lookupMatcher(service, requestedLocales);
+}
+
+
+/**
+ * Parses Unicode extension into key - value map.
+ * Returns empty object if the extension string is invalid.
+ * We are not concerned with the validity of the values at this point.
+ */
+function parseExtension(extension) {
+ var extensionSplit = extension.split('-');
+
+ // Assume ['', 'u', ...] input, but don't throw.
+ if (extensionSplit.length <= 2 ||
+ (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) {
+ return {};
+ }
+
+ // Key is {2}alphanum, value is {3,8}alphanum.
+ // Some keys may not have explicit values (booleans).
+ var extensionMap = {};
+ var previousKey = undefined;
+ for (var i = 2; i < extensionSplit.length; ++i) {
+ var length = extensionSplit[i].length;
+ var element = extensionSplit[i];
+ if (length === 2) {
+ extensionMap[element] = undefined;
+ previousKey = element;
+ } else if (length >= 3 && length <=8 && previousKey !== undefined) {
+ extensionMap[previousKey] = element;
+ previousKey = undefined;
+ } else {
+ // There is a value that's too long, or that doesn't have a key.
+ return {};
+ }
+ }
+
+ return extensionMap;
+}
+
+
+/**
+ * Converts parameter to an Object if possible.
+ */
+function toObject(value) {
+ if (value === undefined || value === null) {
+ throw new TypeError('Value cannot be converted to an Object.');
+ }
+
+ return Object(value);
+}
+
+
+/**
+ * Populates internalOptions object with boolean key-value pairs
+ * from extensionMap and options.
+ * Returns filtered extension (number and date format constructors use
+ * Unicode extensions for passing parameters to ICU).
+ * It's used for extension-option pairs only, e.g. kn-normalization, but not
+ * for 'sensitivity' since it doesn't have extension equivalent.
+ * Extensions like nu and ca don't have options equivalent, so we place
+ * undefined in the map.property to denote that.
+ */
+function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
+ var extension = '';
+
+ var updateExtension = function updateExtension(key, value) {
+ return '-' + key + '-' + String(value);
+ }
+
+ var updateProperty = function updateProperty(property, type, value) {
+ if (type === 'boolean' && (typeof value === 'string')) {
+ value = (value === 'true') ? true : false;
+ }
+
+ if (property !== undefined) {
+ defineWEProperty(outOptions, property, value);
+ }
+ }
+
+ for (var key in keyValues) {
+ if (keyValues.hasOwnProperty(key)) {
+ var value = undefined;
+ var map = keyValues[key];
+ if (map.property !== undefined) {
+ // This may return true if user specifies numeric: 'false', since
+ // Boolean('nonempty') === true.
+ value = getOption(map.property, map.type, map.values);
+ }
+ if (value !== undefined) {
+ updateProperty(map.property, map.type, value);
+ extension += updateExtension(key, value);
+ continue;
+ }
+ // User options didn't have it, check Unicode extension.
+ // Here we want to convert strings 'true', 'false' into proper Boolean
+ // values (not a user error).
+ if (extensionMap.hasOwnProperty(key)) {
+ value = extensionMap[key];
+ if (value !== undefined) {
+ updateProperty(map.property, map.type, value);
+ extension += updateExtension(key, value);
+ } else if (map.type === 'boolean') {
+ // Boolean keys are allowed not to have values in Unicode extension.
+ // Those default to true.
+ updateProperty(map.property, map.type, true);
+ extension += updateExtension(key, true);
+ }
+ }
+ }
+ }
+
+ return extension === ''? '' : '-u' + extension;
+}
+
+
+/**
+ * Converts all OwnProperties into
+ * configurable: false, writable: false, enumerable: true.
+ */
+function freezeArray(array) {
+ array.forEach(function(element, index) {
+ Object.defineProperty(array, index, {value: element,
+ configurable: false,
+ writable: false,
+ enumerable: true});
+ });
+
+ Object.defineProperty(array, 'length', {value: array.length,
+ writable: false});
+
+ return array;
+}
+
+
+/**
+ * It's sometimes desireable to leave user requested locale instead of ICU
+ * supported one (zh-TW is equivalent to zh-Hant-TW, so we should keep shorter
+ * one, if that was what user requested).
+ * This function returns user specified tag if its maximized form matches ICU
+ * resolved locale. If not we return ICU result.
+ */
+function getOptimalLanguageTag(original, resolved) {
+ // Returns Array<Object>, where each object has maximized and base properties.
+ // Maximized: zh -> zh-Hans-CN
+ // Base: zh-CN-u-ca-gregory -> zh-CN
+ // Take care of grandfathered or simple cases.
+ if (original === resolved) {
+ return original;
+ }
+
+ var locales = %GetLanguageTagVariants([original, resolved]);
+ if (locales[0].maximized !== locales[1].maximized) {
+ return resolved;
+ }
+
+ // Preserve extensions of resolved locale, but swap base tags with original.
+ var resolvedBase = new RegExp('^' + locales[1].base);
+ return resolved.replace(resolvedBase, locales[0].base);
+}
+
+
+/**
+ * Returns an Object that contains all of supported locales for a given
+ * service.
+ * In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ
+ * that is supported. This is required by the spec.
+ */
+function getAvailableLocalesOf(service) {
+ var available = %AvailableLocalesOf(service);
+
+ for (var i in available) {
+ if (available.hasOwnProperty(i)) {
+ var parts = i.match(/^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/);
+ if (parts !== null) {
+ // Build xx-ZZ. We don't care about the actual value,
+ // as long it's not undefined.
+ available[parts[1] + '-' + parts[3]] = null;
+ }
+ }
+ }
+
+ return available;
+}
+
+
+/**
+ * Defines a property and sets writable and enumerable to true.
+ * Configurable is false by default.
+ */
+function defineWEProperty(object, property, value) {
+ Object.defineProperty(object, property,
+ {value: value, writable: true, enumerable: true});
+}
+
+
+/**
+ * Adds property to an object if the value is not undefined.
+ * Sets configurable descriptor to false.
+ */
+function addWEPropertyIfDefined(object, property, value) {
+ if (value !== undefined) {
+ defineWEProperty(object, property, value);
+ }
+}
+
+
+/**
+ * Defines a property and sets writable, enumerable and configurable to true.
+ */
+function defineWECProperty(object, property, value) {
+ Object.defineProperty(object, property,
+ {value: value,
+ writable: true,
+ enumerable: true,
+ configurable: true});
+}
+
+
+/**
+ * Adds property to an object if the value is not undefined.
+ * Sets all descriptors to true.
+ */
+function addWECPropertyIfDefined(object, property, value) {
+ if (value !== undefined) {
+ defineWECProperty(object, property, value);
+ }
+}
+
+
+/**
+ * Returns titlecased word, aMeRricA -> America.
+ */
+function toTitleCaseWord(word) {
+ return word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase();
+}
diff --git a/chromium/v8/src/extensions/i18n/locale.js b/chromium/v8/src/extensions/i18n/locale.js
new file mode 100644
index 00000000000..e4783277e64
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/locale.js
@@ -0,0 +1,190 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Canonicalizes the language tag, or throws in case the tag is invalid.
+ */
+function canonicalizeLanguageTag(localeID) {
+ // null is typeof 'object' so we have to do extra check.
+ if (typeof localeID !== 'string' && typeof localeID !== 'object' ||
+ localeID === null) {
+ throw new TypeError('Language ID should be string or object.');
+ }
+
+ var localeString = String(localeID);
+
+ if (isValidLanguageTag(localeString) === false) {
+ throw new RangeError('Invalid language tag: ' + localeString);
+ }
+
+ // This call will strip -kn but not -kn-true extensions.
+ // ICU bug filled - http://bugs.icu-project.org/trac/ticket/9265.
+ // TODO(cira): check if -u-kn-true-kc-true-kh-true still throws after
+ // upgrade to ICU 4.9.
+ var tag = %CanonicalizeLanguageTag(localeString);
+ if (tag === 'invalid-tag') {
+ throw new RangeError('Invalid language tag: ' + localeString);
+ }
+
+ return tag;
+}
+
+
+/**
+ * Returns an array where all locales are canonicalized and duplicates removed.
+ * Throws on locales that are not well formed BCP47 tags.
+ */
+function initializeLocaleList(locales) {
+ var seen = [];
+ if (locales === undefined) {
+ // Constructor is called without arguments.
+ seen = [];
+ } else {
+ // We allow single string localeID.
+ if (typeof locales === 'string') {
+ seen.push(canonicalizeLanguageTag(locales));
+ return freezeArray(seen);
+ }
+
+ var o = toObject(locales);
+ // Converts it to UInt32 (>>> is shr on 32bit integers).
+ var len = o.length >>> 0;
+
+ for (var k = 0; k < len; k++) {
+ if (k in o) {
+ var value = o[k];
+
+ var tag = canonicalizeLanguageTag(value);
+
+ if (seen.indexOf(tag) === -1) {
+ seen.push(tag);
+ }
+ }
+ }
+ }
+
+ return freezeArray(seen);
+}
+
+
+/**
+ * Validates the language tag. Section 2.2.9 of the bcp47 spec
+ * defines a valid tag.
+ *
+ * ICU is too permissible and lets invalid tags, like
+ * hant-cmn-cn, through.
+ *
+ * Returns false if the language tag is invalid.
+ */
+function isValidLanguageTag(locale) {
+ // Check if it's well-formed, including grandfadered tags.
+ if (LANGUAGE_TAG_RE.test(locale) === false) {
+ return false;
+ }
+
+ // Just return if it's a x- form. It's all private.
+ if (locale.indexOf('x-') === 0) {
+ return true;
+ }
+
+ // Check if there are any duplicate variants or singletons (extensions).
+
+ // Remove private use section.
+ locale = locale.split(/-x-/)[0];
+
+ // Skip language since it can match variant regex, so we start from 1.
+ // We are matching i-klingon here, but that's ok, since i-klingon-klingon
+ // is not valid and would fail LANGUAGE_TAG_RE test.
+ var variants = [];
+ var extensions = [];
+ var parts = locale.split(/-/);
+ for (var i = 1; i < parts.length; i++) {
+ var value = parts[i];
+ if (LANGUAGE_VARIANT_RE.test(value) === true && extensions.length === 0) {
+ if (variants.indexOf(value) === -1) {
+ variants.push(value);
+ } else {
+ return false;
+ }
+ }
+
+ if (LANGUAGE_SINGLETON_RE.test(value) === true) {
+ if (extensions.indexOf(value) === -1) {
+ extensions.push(value);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+/**
+ * Builds a regular expresion that validates the language tag
+ * against bcp47 spec.
+ * Uses http://tools.ietf.org/html/bcp47, section 2.1, ABNF.
+ * Runs on load and initializes the global REs.
+ */
+(function() {
+ var alpha = '[a-zA-Z]';
+ var digit = '[0-9]';
+ var alphanum = '(' + alpha + '|' + digit + ')';
+ var regular = '(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|' +
+ 'zh-min|zh-min-nan|zh-xiang)';
+ var irregular = '(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|' +
+ 'i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|' +
+ 'i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)';
+ var grandfathered = '(' + irregular + '|' + regular + ')';
+ var privateUse = '(x(-' + alphanum + '{1,8})+)';
+
+ var singleton = '(' + digit + '|[A-WY-Za-wy-z])';
+ LANGUAGE_SINGLETON_RE = new RegExp('^' + singleton + '$', 'i');
+
+ var extension = '(' + singleton + '(-' + alphanum + '{2,8})+)';
+
+ var variant = '(' + alphanum + '{5,8}|(' + digit + alphanum + '{3}))';
+ LANGUAGE_VARIANT_RE = new RegExp('^' + variant + '$', 'i');
+
+ var region = '(' + alpha + '{2}|' + digit + '{3})';
+ var script = '(' + alpha + '{4})';
+ var extLang = '(' + alpha + '{3}(-' + alpha + '{3}){0,2})';
+ var language = '(' + alpha + '{2,3}(-' + extLang + ')?|' + alpha + '{4}|' +
+ alpha + '{5,8})';
+ var langTag = language + '(-' + script + ')?(-' + region + ')?(-' +
+ variant + ')*(-' + extension + ')*(-' + privateUse + ')?';
+
+ var languageTag =
+ '^(' + langTag + '|' + privateUse + '|' + grandfathered + ')$';
+ LANGUAGE_TAG_RE = new RegExp(languageTag, 'i');
+})();
diff --git a/chromium/v8/src/extensions/i18n/number-format.js b/chromium/v8/src/extensions/i18n/number-format.js
new file mode 100644
index 00000000000..5722a5dc1f3
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/number-format.js
@@ -0,0 +1,289 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+/**
+ * Verifies that the input is a well-formed ISO 4217 currency code.
+ * Don't uppercase to test. It could convert invalid code into a valid one.
+ * For example \u00DFP (Eszett+P) becomes SSP.
+ */
+function isWellFormedCurrencyCode(currency) {
+ return typeof currency == "string" &&
+ currency.length == 3 &&
+ currency.match(/[^A-Za-z]/) == null;
+}
+
+
+/**
+ * Returns the valid digit count for a property, or throws RangeError on
+ * a value out of the range.
+ */
+function getNumberOption(options, property, min, max, fallback) {
+ var value = options[property];
+ if (value !== undefined) {
+ value = Number(value);
+ if (isNaN(value) || value < min || value > max) {
+ throw new RangeError(property + ' value is out of range.');
+ }
+ return Math.floor(value);
+ }
+
+ return fallback;
+}
+
+
+/**
+ * Initializes the given object so it's a valid NumberFormat instance.
+ * Useful for subclassing.
+ */
+function initializeNumberFormat(numberFormat, locales, options) {
+ if (numberFormat.hasOwnProperty('__initializedIntlObject')) {
+ throw new TypeError('Trying to re-initialize NumberFormat object.');
+ }
+
+ if (options === undefined) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'numberformat');
+
+ var locale = resolveLocale('numberformat', locales, options);
+
+ var internalOptions = {};
+ defineWEProperty(internalOptions, 'style', getOption(
+ 'style', 'string', ['decimal', 'percent', 'currency'], 'decimal'));
+
+ var currency = getOption('currency', 'string');
+ if (currency !== undefined && !isWellFormedCurrencyCode(currency)) {
+ throw new RangeError('Invalid currency code: ' + currency);
+ }
+
+ if (internalOptions.style === 'currency' && currency === undefined) {
+ throw new TypeError('Currency code is required with currency style.');
+ }
+
+ var currencyDisplay = getOption(
+ 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
+ if (internalOptions.style === 'currency') {
+ defineWEProperty(internalOptions, 'currency', currency.toUpperCase());
+ defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay);
+ }
+
+ // Digit ranges.
+ var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
+ defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
+
+ var mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0);
+ defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
+
+ var mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, 3);
+ defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
+
+ var mnsd = options['minimumSignificantDigits'];
+ var mxsd = options['maximumSignificantDigits'];
+ if (mnsd !== undefined || mxsd !== undefined) {
+ mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 0);
+ defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
+
+ mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21);
+ defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
+ }
+
+ // Grouping.
+ defineWEProperty(internalOptions, 'useGrouping', getOption(
+ 'useGrouping', 'boolean', undefined, true));
+
+ // ICU prefers options to be passed using -u- extension key/values for
+ // number format, so we need to build that.
+ var extensionMap = parseExtension(locale.extension);
+ var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP,
+ getOption, internalOptions);
+
+ var requestedLocale = locale.locale + extension;
+ var resolved = Object.defineProperties({}, {
+ currency: {writable: true},
+ currencyDisplay: {writable: true},
+ locale: {writable: true},
+ maximumFractionDigits: {writable: true},
+ minimumFractionDigits: {writable: true},
+ minimumIntegerDigits: {writable: true},
+ numberingSystem: {writable: true},
+ requestedLocale: {value: requestedLocale, writable: true},
+ style: {value: internalOptions.style, writable: true},
+ useGrouping: {writable: true}
+ });
+ if (internalOptions.hasOwnProperty('minimumSignificantDigits')) {
+ defineWEProperty(resolved, 'minimumSignificantDigits', undefined);
+ }
+ if (internalOptions.hasOwnProperty('maximumSignificantDigits')) {
+ defineWEProperty(resolved, 'maximumSignificantDigits', undefined);
+ }
+ var formatter = %CreateNumberFormat(requestedLocale,
+ internalOptions,
+ resolved);
+
+ // We can't get information about number or currency style from ICU, so we
+ // assume user request was fulfilled.
+ if (internalOptions.style === 'currency') {
+ Object.defineProperty(resolved, 'currencyDisplay', {value: currencyDisplay,
+ writable: true});
+ }
+
+ Object.defineProperty(numberFormat, 'formatter', {value: formatter});
+ Object.defineProperty(numberFormat, 'resolved', {value: resolved});
+ Object.defineProperty(numberFormat, '__initializedIntlObject',
+ {value: 'numberformat'});
+
+ return numberFormat;
+}
+
+
+/**
+ * Constructs Intl.NumberFormat object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%SetProperty(Intl, 'NumberFormat', function() {
+ var locales = arguments[0];
+ var options = arguments[1];
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.NumberFormat(locales, options);
+ }
+
+ return initializeNumberFormat(toObject(this), locales, options);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+
+
+/**
+ * NumberFormat resolvedOptions method.
+ */
+%SetProperty(Intl.NumberFormat.prototype, 'resolvedOptions', function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!this || typeof this !== 'object' ||
+ this.__initializedIntlObject !== 'numberformat') {
+ throw new TypeError('resolvedOptions method called on a non-object' +
+ ' or on a object that is not Intl.NumberFormat.');
+ }
+
+ var format = this;
+ var locale = getOptimalLanguageTag(format.resolved.requestedLocale,
+ format.resolved.locale);
+
+ var result = {
+ locale: locale,
+ numberingSystem: format.resolved.numberingSystem,
+ style: format.resolved.style,
+ useGrouping: format.resolved.useGrouping,
+ minimumIntegerDigits: format.resolved.minimumIntegerDigits,
+ minimumFractionDigits: format.resolved.minimumFractionDigits,
+ maximumFractionDigits: format.resolved.maximumFractionDigits,
+ };
+
+ if (result.style === 'currency') {
+ defineWECProperty(result, 'currency', format.resolved.currency);
+ defineWECProperty(result, 'currencyDisplay',
+ format.resolved.currencyDisplay);
+ }
+
+ if (format.resolved.hasOwnProperty('minimumSignificantDigits')) {
+ defineWECProperty(result, 'minimumSignificantDigits',
+ format.resolved.minimumSignificantDigits);
+ }
+
+ if (format.resolved.hasOwnProperty('maximumSignificantDigits')) {
+ defineWECProperty(result, 'maximumSignificantDigits',
+ format.resolved.maximumSignificantDigits);
+ }
+
+ return result;
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.NumberFormat.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.NumberFormat.prototype.resolvedOptions);
+%SetNativeFlag(Intl.NumberFormat.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%SetProperty(Intl.NumberFormat, 'supportedLocalesOf', function(locales) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ return supportedLocalesOf('numberformat', locales, arguments[1]);
+ },
+ ATTRIBUTES.DONT_ENUM
+);
+%FunctionSetName(Intl.NumberFormat.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.NumberFormat.supportedLocalesOf);
+%SetNativeFlag(Intl.NumberFormat.supportedLocalesOf);
+
+
+/**
+ * Returns a String value representing the result of calling ToNumber(value)
+ * according to the effective locale and the formatting options of this
+ * NumberFormat.
+ */
+function formatNumber(formatter, value) {
+ // Spec treats -0 and +0 as 0.
+ var number = Number(value);
+ if (number === -0) {
+ number = 0;
+ }
+
+ return %InternalNumberFormat(formatter.formatter, number);
+}
+
+
+/**
+ * Returns a Number that represents string value that was passed in.
+ */
+function parseNumber(formatter, value) {
+ return %InternalNumberParse(formatter.formatter, String(value));
+}
+
+
+addBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1);
+addBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1);
diff --git a/chromium/v8/src/extensions/i18n/overrides.js b/chromium/v8/src/extensions/i18n/overrides.js
new file mode 100644
index 00000000000..b2d60b3fc67
--- /dev/null
+++ b/chromium/v8/src/extensions/i18n/overrides.js
@@ -0,0 +1,220 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+// ECMAScript 402 API implementation is broken into separate files for
+// each service. The build system combines them together into one
+// Intl namespace.
+
+
+// Save references to Intl objects and methods we use, for added security.
+var savedObjects = {
+ 'collator': Intl.Collator,
+ 'numberformat': Intl.NumberFormat,
+ 'dateformatall': Intl.DateTimeFormat,
+ 'dateformatdate': Intl.DateTimeFormat,
+ 'dateformattime': Intl.DateTimeFormat
+};
+
+
+// Default (created with undefined locales and options parameters) collator,
+// number and date format instances. They'll be created as needed.
+var defaultObjects = {
+ 'collator': undefined,
+ 'numberformat': undefined,
+ 'dateformatall': undefined,
+ 'dateformatdate': undefined,
+ 'dateformattime': undefined,
+};
+
+
+/**
+ * Returns cached or newly created instance of a given service.
+ * We cache only default instances (where no locales or options are provided).
+ */
+function cachedOrNewService(service, locales, options, defaults) {
+ var useOptions = (defaults === undefined) ? options : defaults;
+ if (locales === undefined && options === undefined) {
+ if (defaultObjects[service] === undefined) {
+ defaultObjects[service] = new savedObjects[service](locales, useOptions);
+ }
+ return defaultObjects[service];
+ }
+ return new savedObjects[service](locales, useOptions);
+}
+
+
+/**
+ * Compares this and that, and returns less than 0, 0 or greater than 0 value.
+ * Overrides the built-in method.
+ */
+Object.defineProperty(String.prototype, 'localeCompare', {
+ value: function(that) {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (this === undefined || this === null) {
+ throw new TypeError('Method invoked on undefined or null value.');
+ }
+
+ var locales = arguments[1];
+ var options = arguments[2];
+ var collator = cachedOrNewService('collator', locales, options);
+ return compare(collator, this, that);
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(String.prototype.localeCompare, 'localeCompare');
+%FunctionRemovePrototype(String.prototype.localeCompare);
+%SetNativeFlag(String.prototype.localeCompare);
+
+
+/**
+ * Formats a Number object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used.
+ */
+Object.defineProperty(Number.prototype, 'toLocaleString', {
+ value: function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ if (!(this instanceof Number) && typeof(this) !== 'number') {
+ throw new TypeError('Method invoked on an object that is not Number.');
+ }
+
+ var locales = arguments[0];
+ var options = arguments[1];
+ var numberFormat = cachedOrNewService('numberformat', locales, options);
+ return formatNumber(numberFormat, this);
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(Number.prototype.toLocaleString, 'toLocaleString');
+%FunctionRemovePrototype(Number.prototype.toLocaleString);
+%SetNativeFlag(Number.prototype.toLocaleString);
+
+
+/**
+ * Returns actual formatted date or fails if date parameter is invalid.
+ */
+function toLocaleDateTime(date, locales, options, required, defaults, service) {
+ if (!(date instanceof Date)) {
+ throw new TypeError('Method invoked on an object that is not Date.');
+ }
+
+ if (isNaN(date)) {
+ return 'Invalid Date';
+ }
+
+ var internalOptions = toDateTimeOptions(options, required, defaults);
+
+ var dateFormat =
+ cachedOrNewService(service, locales, options, internalOptions);
+
+ return formatDate(dateFormat, date);
+}
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - both date and time are
+ * present in the output.
+ */
+Object.defineProperty(Date.prototype, 'toLocaleString', {
+ value: function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ var locales = arguments[0];
+ var options = arguments[1];
+ return toLocaleDateTime(
+ this, locales, options, 'any', 'all', 'dateformatall');
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(Date.prototype.toLocaleString, 'toLocaleString');
+%FunctionRemovePrototype(Date.prototype.toLocaleString);
+%SetNativeFlag(Date.prototype.toLocaleString);
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - only date is present
+ * in the output.
+ */
+Object.defineProperty(Date.prototype, 'toLocaleDateString', {
+ value: function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ var locales = arguments[0];
+ var options = arguments[1];
+ return toLocaleDateTime(
+ this, locales, options, 'date', 'date', 'dateformatdate');
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(Date.prototype.toLocaleDateString, 'toLocaleDateString');
+%FunctionRemovePrototype(Date.prototype.toLocaleDateString);
+%SetNativeFlag(Date.prototype.toLocaleDateString);
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - only time is present
+ * in the output.
+ */
+Object.defineProperty(Date.prototype, 'toLocaleTimeString', {
+ value: function() {
+ if (%_IsConstructCall()) {
+ throw new TypeError(ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR);
+ }
+
+ var locales = arguments[0];
+ var options = arguments[1];
+ return toLocaleDateTime(
+ this, locales, options, 'time', 'time', 'dateformattime');
+ },
+ writable: true,
+ configurable: true,
+ enumerable: false
+});
+%FunctionSetName(Date.prototype.toLocaleTimeString, 'toLocaleTimeString');
+%FunctionRemovePrototype(Date.prototype.toLocaleTimeString);
+%SetNativeFlag(Date.prototype.toLocaleTimeString);
diff --git a/chromium/v8/src/extensions/statistics-extension.cc b/chromium/v8/src/extensions/statistics-extension.cc
new file mode 100644
index 00000000000..32bc07de8bd
--- /dev/null
+++ b/chromium/v8/src/extensions/statistics-extension.cc
@@ -0,0 +1,159 @@
+// Copyright 2012 the V8 project authors. 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 "statistics-extension.h"
+
+namespace v8 {
+namespace internal {
+
+const char* const StatisticsExtension::kSource =
+ "native function getV8Statistics();";
+
+
+v8::Handle<v8::FunctionTemplate> StatisticsExtension::GetNativeFunction(
+ v8::Handle<v8::String> str) {
+ ASSERT(strcmp(*v8::String::Utf8Value(str), "getV8Statistics") == 0);
+ return v8::FunctionTemplate::New(StatisticsExtension::GetCounters);
+}
+
+
+static void AddCounter(v8::Local<v8::Object> object,
+ StatsCounter* counter,
+ const char* name) {
+ if (counter->Enabled()) {
+ object->Set(v8::String::New(name),
+ v8::Number::New(*counter->GetInternalPointer()));
+ }
+}
+
+static void AddNumber(v8::Local<v8::Object> object,
+ intptr_t value,
+ const char* name) {
+ object->Set(v8::String::New(name),
+ v8::Number::New(static_cast<double>(value)));
+}
+
+
+void StatisticsExtension::GetCounters(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = Isolate::Current();
+ Heap* heap = isolate->heap();
+
+ if (args.Length() > 0) { // GC if first argument evaluates to true.
+ if (args[0]->IsBoolean() && args[0]->ToBoolean()->Value()) {
+ heap->CollectAllGarbage(Heap::kNoGCFlags, "counters extension");
+ }
+ }
+
+ Counters* counters = isolate->counters();
+ v8::Local<v8::Object> result = v8::Object::New();
+
+#define ADD_COUNTER(name, caption) \
+ AddCounter(result, counters->name(), #name);
+
+ STATS_COUNTER_LIST_1(ADD_COUNTER)
+ STATS_COUNTER_LIST_2(ADD_COUNTER)
+#undef ADD_COUNTER
+#define ADD_COUNTER(name) \
+ AddCounter(result, counters->count_of_##name(), "count_of_" #name); \
+ AddCounter(result, counters->size_of_##name(), "size_of_" #name);
+
+ INSTANCE_TYPE_LIST(ADD_COUNTER)
+#undef ADD_COUNTER
+#define ADD_COUNTER(name) \
+ AddCounter(result, counters->count_of_CODE_TYPE_##name(), \
+ "count_of_CODE_TYPE_" #name); \
+ AddCounter(result, counters->size_of_CODE_TYPE_##name(), \
+ "size_of_CODE_TYPE_" #name);
+
+ CODE_KIND_LIST(ADD_COUNTER)
+#undef ADD_COUNTER
+#define ADD_COUNTER(name) \
+ AddCounter(result, counters->count_of_FIXED_ARRAY_##name(), \
+ "count_of_FIXED_ARRAY_" #name); \
+ AddCounter(result, counters->size_of_FIXED_ARRAY_##name(), \
+ "size_of_FIXED_ARRAY_" #name);
+
+ FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(ADD_COUNTER)
+#undef ADD_COUNTER
+
+ AddNumber(result, isolate->memory_allocator()->Size(),
+ "total_committed_bytes");
+ AddNumber(result, heap->new_space()->Size(),
+ "new_space_live_bytes");
+ AddNumber(result, heap->new_space()->Available(),
+ "new_space_available_bytes");
+ AddNumber(result, heap->new_space()->CommittedMemory(),
+ "new_space_commited_bytes");
+ AddNumber(result, heap->old_pointer_space()->Size(),
+ "old_pointer_space_live_bytes");
+ AddNumber(result, heap->old_pointer_space()->Available(),
+ "old_pointer_space_available_bytes");
+ AddNumber(result, heap->old_pointer_space()->CommittedMemory(),
+ "old_pointer_space_commited_bytes");
+ AddNumber(result, heap->old_data_space()->Size(),
+ "old_data_space_live_bytes");
+ AddNumber(result, heap->old_data_space()->Available(),
+ "old_data_space_available_bytes");
+ AddNumber(result, heap->old_data_space()->CommittedMemory(),
+ "old_data_space_commited_bytes");
+ AddNumber(result, heap->code_space()->Size(),
+ "code_space_live_bytes");
+ AddNumber(result, heap->code_space()->Available(),
+ "code_space_available_bytes");
+ AddNumber(result, heap->code_space()->CommittedMemory(),
+ "code_space_commited_bytes");
+ AddNumber(result, heap->cell_space()->Size(),
+ "cell_space_live_bytes");
+ AddNumber(result, heap->cell_space()->Available(),
+ "cell_space_available_bytes");
+ AddNumber(result, heap->cell_space()->CommittedMemory(),
+ "cell_space_commited_bytes");
+ AddNumber(result, heap->property_cell_space()->Size(),
+ "property_cell_space_live_bytes");
+ AddNumber(result, heap->property_cell_space()->Available(),
+ "property_cell_space_available_bytes");
+ AddNumber(result, heap->property_cell_space()->CommittedMemory(),
+ "property_cell_space_commited_bytes");
+ AddNumber(result, heap->lo_space()->Size(),
+ "lo_space_live_bytes");
+ AddNumber(result, heap->lo_space()->Available(),
+ "lo_space_available_bytes");
+ AddNumber(result, heap->lo_space()->CommittedMemory(),
+ "lo_space_commited_bytes");
+ AddNumber(result, heap->amount_of_external_allocated_memory(),
+ "amount_of_external_allocated_memory");
+ args.GetReturnValue().Set(result);
+}
+
+
+void StatisticsExtension::Register() {
+ static StatisticsExtension statistics_extension;
+ static v8::DeclareExtension declaration(&statistics_extension);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/extensions/statistics-extension.h b/chromium/v8/src/extensions/statistics-extension.h
new file mode 100644
index 00000000000..bfd9c4134e2
--- /dev/null
+++ b/chromium/v8/src/extensions/statistics-extension.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef V8_EXTENSIONS_STATISTICS_EXTENSION_H_
+#define V8_EXTENSIONS_STATISTICS_EXTENSION_H_
+
+#include "v8.h"
+
+namespace v8 {
+namespace internal {
+
+class StatisticsExtension : public v8::Extension {
+ public:
+ StatisticsExtension() : v8::Extension("v8/statistics", kSource) {}
+ virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
+ v8::Handle<v8::String> name);
+ static void GetCounters(const v8::FunctionCallbackInfo<v8::Value>& args);
+ static void Register();
+ private:
+ static const char* const kSource;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_EXTENSIONS_STATISTICS_EXTENSION_H_
diff --git a/chromium/v8/src/factory.cc b/chromium/v8/src/factory.cc
new file mode 100644
index 00000000000..9323c2f1f03
--- /dev/null
+++ b/chromium/v8/src/factory.cc
@@ -0,0 +1,1619 @@
+// Copyright 2013 the V8 project authors. 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 "api.h"
+#include "debug.h"
+#include "execution.h"
+#include "factory.h"
+#include "isolate-inl.h"
+#include "macro-assembler.h"
+#include "objects.h"
+#include "objects-visiting.h"
+#include "platform.h"
+#include "scopeinfo.h"
+
+namespace v8 {
+namespace internal {
+
+
+Handle<Box> Factory::NewBox(Handle<Object> value, PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateBox(*value, pretenure),
+ Box);
+}
+
+
+Handle<FixedArray> Factory::NewFixedArray(int size, PretenureFlag pretenure) {
+ ASSERT(0 <= size);
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateFixedArray(size, pretenure),
+ FixedArray);
+}
+
+
+Handle<FixedArray> Factory::NewFixedArrayWithHoles(int size,
+ PretenureFlag pretenure) {
+ ASSERT(0 <= size);
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateFixedArrayWithHoles(size, pretenure),
+ FixedArray);
+}
+
+
+Handle<FixedDoubleArray> Factory::NewFixedDoubleArray(int size,
+ PretenureFlag pretenure) {
+ ASSERT(0 <= size);
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateUninitializedFixedDoubleArray(size, pretenure),
+ FixedDoubleArray);
+}
+
+
+Handle<NameDictionary> Factory::NewNameDictionary(int at_least_space_for) {
+ ASSERT(0 <= at_least_space_for);
+ CALL_HEAP_FUNCTION(isolate(),
+ NameDictionary::Allocate(isolate()->heap(),
+ at_least_space_for),
+ NameDictionary);
+}
+
+
+Handle<SeededNumberDictionary> Factory::NewSeededNumberDictionary(
+ int at_least_space_for) {
+ ASSERT(0 <= at_least_space_for);
+ CALL_HEAP_FUNCTION(isolate(),
+ SeededNumberDictionary::Allocate(isolate()->heap(),
+ at_least_space_for),
+ SeededNumberDictionary);
+}
+
+
+Handle<UnseededNumberDictionary> Factory::NewUnseededNumberDictionary(
+ int at_least_space_for) {
+ ASSERT(0 <= at_least_space_for);
+ CALL_HEAP_FUNCTION(isolate(),
+ UnseededNumberDictionary::Allocate(isolate()->heap(),
+ at_least_space_for),
+ UnseededNumberDictionary);
+}
+
+
+Handle<ObjectHashSet> Factory::NewObjectHashSet(int at_least_space_for) {
+ ASSERT(0 <= at_least_space_for);
+ CALL_HEAP_FUNCTION(isolate(),
+ ObjectHashSet::Allocate(isolate()->heap(),
+ at_least_space_for),
+ ObjectHashSet);
+}
+
+
+Handle<ObjectHashTable> Factory::NewObjectHashTable(int at_least_space_for) {
+ ASSERT(0 <= at_least_space_for);
+ CALL_HEAP_FUNCTION(isolate(),
+ ObjectHashTable::Allocate(isolate()->heap(),
+ at_least_space_for),
+ ObjectHashTable);
+}
+
+
+Handle<DescriptorArray> Factory::NewDescriptorArray(int number_of_descriptors,
+ int slack) {
+ ASSERT(0 <= number_of_descriptors);
+ CALL_HEAP_FUNCTION(isolate(),
+ DescriptorArray::Allocate(number_of_descriptors, slack),
+ DescriptorArray);
+}
+
+
+Handle<DeoptimizationInputData> Factory::NewDeoptimizationInputData(
+ int deopt_entry_count,
+ PretenureFlag pretenure) {
+ ASSERT(deopt_entry_count > 0);
+ CALL_HEAP_FUNCTION(isolate(),
+ DeoptimizationInputData::Allocate(deopt_entry_count,
+ pretenure),
+ DeoptimizationInputData);
+}
+
+
+Handle<DeoptimizationOutputData> Factory::NewDeoptimizationOutputData(
+ int deopt_entry_count,
+ PretenureFlag pretenure) {
+ ASSERT(deopt_entry_count > 0);
+ CALL_HEAP_FUNCTION(isolate(),
+ DeoptimizationOutputData::Allocate(deopt_entry_count,
+ pretenure),
+ DeoptimizationOutputData);
+}
+
+
+Handle<AccessorPair> Factory::NewAccessorPair() {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateAccessorPair(),
+ AccessorPair);
+}
+
+
+Handle<TypeFeedbackInfo> Factory::NewTypeFeedbackInfo() {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateTypeFeedbackInfo(),
+ TypeFeedbackInfo);
+}
+
+
+// Internalized strings are created in the old generation (data space).
+Handle<String> Factory::InternalizeUtf8String(Vector<const char> string) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->InternalizeUtf8String(string),
+ String);
+}
+
+
+// Internalized strings are created in the old generation (data space).
+Handle<String> Factory::InternalizeString(Handle<String> string) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->InternalizeString(*string),
+ String);
+}
+
+
+Handle<String> Factory::InternalizeOneByteString(Vector<const uint8_t> string) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->InternalizeOneByteString(string),
+ String);
+}
+
+
+Handle<String> Factory::InternalizeOneByteString(
+ Handle<SeqOneByteString> string, int from, int length) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->InternalizeOneByteString(
+ string, from, length),
+ String);
+}
+
+
+Handle<String> Factory::InternalizeTwoByteString(Vector<const uc16> string) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->InternalizeTwoByteString(string),
+ String);
+}
+
+
+Handle<String> Factory::NewStringFromOneByte(Vector<const uint8_t> string,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateStringFromOneByte(string, pretenure),
+ String);
+}
+
+Handle<String> Factory::NewStringFromUtf8(Vector<const char> string,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateStringFromUtf8(string, pretenure),
+ String);
+}
+
+
+Handle<String> Factory::NewStringFromTwoByte(Vector<const uc16> string,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateStringFromTwoByte(string, pretenure),
+ String);
+}
+
+
+Handle<SeqOneByteString> Factory::NewRawOneByteString(int length,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateRawOneByteString(length, pretenure),
+ SeqOneByteString);
+}
+
+
+Handle<SeqTwoByteString> Factory::NewRawTwoByteString(int length,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateRawTwoByteString(length, pretenure),
+ SeqTwoByteString);
+}
+
+
+Handle<String> Factory::NewConsString(Handle<String> first,
+ Handle<String> second) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateConsString(*first, *second),
+ String);
+}
+
+
+template<typename SinkChar, typename StringType>
+Handle<String> ConcatStringContent(Handle<StringType> result,
+ Handle<String> first,
+ Handle<String> second) {
+ DisallowHeapAllocation pointer_stays_valid;
+ SinkChar* sink = result->GetChars();
+ String::WriteToFlat(*first, sink, 0, first->length());
+ String::WriteToFlat(*second, sink + first->length(), 0, second->length());
+ return result;
+}
+
+
+Handle<String> Factory::NewFlatConcatString(Handle<String> first,
+ Handle<String> second) {
+ int total_length = first->length() + second->length();
+ if (first->IsOneByteRepresentationUnderneath() &&
+ second->IsOneByteRepresentationUnderneath()) {
+ return ConcatStringContent<uint8_t>(
+ NewRawOneByteString(total_length), first, second);
+ } else {
+ return ConcatStringContent<uc16>(
+ NewRawTwoByteString(total_length), first, second);
+ }
+}
+
+
+Handle<String> Factory::NewSubString(Handle<String> str,
+ int begin,
+ int end) {
+ CALL_HEAP_FUNCTION(isolate(),
+ str->SubString(begin, end),
+ String);
+}
+
+
+Handle<String> Factory::NewProperSubString(Handle<String> str,
+ int begin,
+ int end) {
+ ASSERT(begin > 0 || end < str->length());
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateSubString(*str, begin, end),
+ String);
+}
+
+
+Handle<String> Factory::NewExternalStringFromAscii(
+ const ExternalAsciiString::Resource* resource) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateExternalStringFromAscii(resource),
+ String);
+}
+
+
+Handle<String> Factory::NewExternalStringFromTwoByte(
+ const ExternalTwoByteString::Resource* resource) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateExternalStringFromTwoByte(resource),
+ String);
+}
+
+
+Handle<Symbol> Factory::NewSymbol() {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateSymbol(),
+ Symbol);
+}
+
+
+Handle<Context> Factory::NewNativeContext() {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateNativeContext(),
+ Context);
+}
+
+
+Handle<Context> Factory::NewGlobalContext(Handle<JSFunction> function,
+ Handle<ScopeInfo> scope_info) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateGlobalContext(*function, *scope_info),
+ Context);
+}
+
+
+Handle<Context> Factory::NewModuleContext(Handle<ScopeInfo> scope_info) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateModuleContext(*scope_info),
+ Context);
+}
+
+
+Handle<Context> Factory::NewFunctionContext(int length,
+ Handle<JSFunction> function) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateFunctionContext(length, *function),
+ Context);
+}
+
+
+Handle<Context> Factory::NewCatchContext(Handle<JSFunction> function,
+ Handle<Context> previous,
+ Handle<String> name,
+ Handle<Object> thrown_object) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateCatchContext(*function,
+ *previous,
+ *name,
+ *thrown_object),
+ Context);
+}
+
+
+Handle<Context> Factory::NewWithContext(Handle<JSFunction> function,
+ Handle<Context> previous,
+ Handle<JSObject> extension) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateWithContext(*function, *previous, *extension),
+ Context);
+}
+
+
+Handle<Context> Factory::NewBlockContext(Handle<JSFunction> function,
+ Handle<Context> previous,
+ Handle<ScopeInfo> scope_info) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateBlockContext(*function,
+ *previous,
+ *scope_info),
+ Context);
+}
+
+
+Handle<Struct> Factory::NewStruct(InstanceType type) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateStruct(type),
+ Struct);
+}
+
+
+Handle<DeclaredAccessorDescriptor> Factory::NewDeclaredAccessorDescriptor() {
+ return Handle<DeclaredAccessorDescriptor>::cast(
+ NewStruct(DECLARED_ACCESSOR_DESCRIPTOR_TYPE));
+}
+
+
+Handle<DeclaredAccessorInfo> Factory::NewDeclaredAccessorInfo() {
+ Handle<DeclaredAccessorInfo> info =
+ Handle<DeclaredAccessorInfo>::cast(
+ NewStruct(DECLARED_ACCESSOR_INFO_TYPE));
+ info->set_flag(0); // Must clear the flag, it was initialized as undefined.
+ return info;
+}
+
+
+Handle<ExecutableAccessorInfo> Factory::NewExecutableAccessorInfo() {
+ Handle<ExecutableAccessorInfo> info =
+ Handle<ExecutableAccessorInfo>::cast(
+ NewStruct(EXECUTABLE_ACCESSOR_INFO_TYPE));
+ info->set_flag(0); // Must clear the flag, it was initialized as undefined.
+ return info;
+}
+
+
+Handle<Script> Factory::NewScript(Handle<String> source) {
+ // Generate id for this script.
+ Heap* heap = isolate()->heap();
+ int id = heap->last_script_id()->value() + 1;
+ if (!Smi::IsValid(id) || id < 0) id = 1;
+ heap->set_last_script_id(Smi::FromInt(id));
+
+ // Create and initialize script object.
+ Handle<Foreign> wrapper = NewForeign(0, TENURED);
+ Handle<Script> script = Handle<Script>::cast(NewStruct(SCRIPT_TYPE));
+ script->set_source(*source);
+ script->set_name(heap->undefined_value());
+ script->set_id(Smi::FromInt(id));
+ script->set_line_offset(Smi::FromInt(0));
+ script->set_column_offset(Smi::FromInt(0));
+ script->set_data(heap->undefined_value());
+ script->set_context_data(heap->undefined_value());
+ script->set_type(Smi::FromInt(Script::TYPE_NORMAL));
+ script->set_wrapper(*wrapper);
+ script->set_line_ends(heap->undefined_value());
+ script->set_eval_from_shared(heap->undefined_value());
+ script->set_eval_from_instructions_offset(Smi::FromInt(0));
+ script->set_flags(Smi::FromInt(0));
+
+ return script;
+}
+
+
+Handle<Foreign> Factory::NewForeign(Address addr, PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateForeign(addr, pretenure),
+ Foreign);
+}
+
+
+Handle<Foreign> Factory::NewForeign(const AccessorDescriptor* desc) {
+ return NewForeign((Address) desc, TENURED);
+}
+
+
+Handle<ByteArray> Factory::NewByteArray(int length, PretenureFlag pretenure) {
+ ASSERT(0 <= length);
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateByteArray(length, pretenure),
+ ByteArray);
+}
+
+
+Handle<ExternalArray> Factory::NewExternalArray(int length,
+ ExternalArrayType array_type,
+ void* external_pointer,
+ PretenureFlag pretenure) {
+ ASSERT(0 <= length);
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateExternalArray(length,
+ array_type,
+ external_pointer,
+ pretenure),
+ ExternalArray);
+}
+
+
+Handle<Cell> Factory::NewCell(Handle<Object> value) {
+ AllowDeferredHandleDereference convert_to_cell;
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateCell(*value),
+ Cell);
+}
+
+
+Handle<PropertyCell> Factory::NewPropertyCell(Handle<Object> value) {
+ AllowDeferredHandleDereference convert_to_cell;
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocatePropertyCell(*value),
+ PropertyCell);
+}
+
+
+Handle<AllocationSite> Factory::NewAllocationSite() {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateAllocationSite(),
+ AllocationSite);
+}
+
+
+Handle<Map> Factory::NewMap(InstanceType type,
+ int instance_size,
+ ElementsKind elements_kind) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateMap(type, instance_size, elements_kind),
+ Map);
+}
+
+
+Handle<JSObject> Factory::NewFunctionPrototype(Handle<JSFunction> function) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateFunctionPrototype(*function),
+ JSObject);
+}
+
+
+Handle<Map> Factory::CopyWithPreallocatedFieldDescriptors(Handle<Map> src) {
+ CALL_HEAP_FUNCTION(
+ isolate(), src->CopyWithPreallocatedFieldDescriptors(), Map);
+}
+
+
+Handle<Map> Factory::CopyMap(Handle<Map> src,
+ int extra_inobject_properties) {
+ Handle<Map> copy = CopyWithPreallocatedFieldDescriptors(src);
+ // Check that we do not overflow the instance size when adding the
+ // extra inobject properties.
+ int instance_size_delta = extra_inobject_properties * kPointerSize;
+ int max_instance_size_delta =
+ JSObject::kMaxInstanceSize - copy->instance_size();
+ if (instance_size_delta > max_instance_size_delta) {
+ // If the instance size overflows, we allocate as many properties
+ // as we can as inobject properties.
+ instance_size_delta = max_instance_size_delta;
+ extra_inobject_properties = max_instance_size_delta >> kPointerSizeLog2;
+ }
+ // Adjust the map with the extra inobject properties.
+ int inobject_properties =
+ copy->inobject_properties() + extra_inobject_properties;
+ copy->set_inobject_properties(inobject_properties);
+ copy->set_unused_property_fields(inobject_properties);
+ copy->set_instance_size(copy->instance_size() + instance_size_delta);
+ copy->set_visitor_id(StaticVisitorBase::GetVisitorId(*copy));
+ return copy;
+}
+
+
+Handle<Map> Factory::CopyMap(Handle<Map> src) {
+ CALL_HEAP_FUNCTION(isolate(), src->Copy(), Map);
+}
+
+
+Handle<Map> Factory::GetElementsTransitionMap(
+ Handle<JSObject> src,
+ ElementsKind elements_kind) {
+ Isolate* i = isolate();
+ CALL_HEAP_FUNCTION(i,
+ src->GetElementsTransitionMap(i, elements_kind),
+ Map);
+}
+
+
+Handle<FixedArray> Factory::CopyFixedArray(Handle<FixedArray> array) {
+ CALL_HEAP_FUNCTION(isolate(), array->Copy(), FixedArray);
+}
+
+
+Handle<FixedArray> Factory::CopySizeFixedArray(Handle<FixedArray> array,
+ int new_length) {
+ CALL_HEAP_FUNCTION(isolate(), array->CopySize(new_length), FixedArray);
+}
+
+
+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,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateFunction(*function_map,
+ *function_info,
+ isolate()->heap()->the_hole_value(),
+ pretenure),
+ JSFunction);
+}
+
+
+static Handle<Map> MapForNewFunction(Isolate *isolate,
+ Handle<SharedFunctionInfo> function_info) {
+ Context *context = isolate->context()->native_context();
+ int map_index = Context::FunctionMapIndex(function_info->language_mode(),
+ function_info->is_generator());
+ return Handle<Map>(Map::cast(context->get(map_index)));
+}
+
+
+Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
+ Handle<SharedFunctionInfo> function_info,
+ Handle<Context> context,
+ PretenureFlag pretenure) {
+ Handle<JSFunction> result = BaseNewFunctionFromSharedFunctionInfo(
+ function_info,
+ MapForNewFunction(isolate(), function_info),
+ pretenure);
+
+ if (function_info->ic_age() != isolate()->heap()->global_ic_age()) {
+ function_info->ResetForNewContext(isolate()->heap()->global_ic_age());
+ }
+
+ result->set_context(*context);
+
+ int index = function_info->SearchOptimizedCodeMap(context->native_context());
+ if (!function_info->bound() && index < 0) {
+ int number_of_literals = function_info->num_literals();
+ Handle<FixedArray> literals = NewFixedArray(number_of_literals, pretenure);
+ if (number_of_literals > 0) {
+ // Store the native context in the literals array prefix. This
+ // context will be used when creating object, regexp and array
+ // literals in this function.
+ literals->set(JSFunction::kLiteralNativeContextIndex,
+ context->native_context());
+ }
+ result->set_literals(*literals);
+ }
+
+ if (index > 0) {
+ // Caching of optimized code enabled and optimized code found.
+ function_info->InstallFromOptimizedCodeMap(*result, index);
+ return result;
+ }
+
+ if (V8::UseCrankshaft() &&
+ FLAG_always_opt &&
+ result->is_compiled() &&
+ !function_info->is_toplevel() &&
+ function_info->allows_lazy_compilation() &&
+ !function_info->optimization_disabled() &&
+ !isolate()->DebuggerHasBreakPoints()) {
+ result->MarkForLazyRecompilation();
+ }
+ return result;
+}
+
+
+Handle<Object> Factory::NewNumber(double value,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->NumberFromDouble(value, pretenure), Object);
+}
+
+
+Handle<Object> Factory::NewNumberFromInt(int32_t value,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->NumberFromInt32(value, pretenure), Object);
+}
+
+
+Handle<Object> Factory::NewNumberFromUint(uint32_t value,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->NumberFromUint32(value, pretenure), Object);
+}
+
+
+Handle<HeapNumber> Factory::NewHeapNumber(double value,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateHeapNumber(value, pretenure), HeapNumber);
+}
+
+
+Handle<JSObject> Factory::NewNeanderObject() {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSObjectFromMap(
+ isolate()->heap()->neander_map()),
+ JSObject);
+}
+
+
+Handle<Object> Factory::NewTypeError(const char* message,
+ Vector< Handle<Object> > args) {
+ return NewError("MakeTypeError", message, args);
+}
+
+
+Handle<Object> Factory::NewTypeError(Handle<String> message) {
+ return NewError("$TypeError", message);
+}
+
+
+Handle<Object> Factory::NewRangeError(const char* message,
+ Vector< Handle<Object> > args) {
+ return NewError("MakeRangeError", message, args);
+}
+
+
+Handle<Object> Factory::NewRangeError(Handle<String> message) {
+ return NewError("$RangeError", message);
+}
+
+
+Handle<Object> Factory::NewSyntaxError(const char* message,
+ Handle<JSArray> args) {
+ return NewError("MakeSyntaxError", message, args);
+}
+
+
+Handle<Object> Factory::NewSyntaxError(Handle<String> message) {
+ return NewError("$SyntaxError", message);
+}
+
+
+Handle<Object> Factory::NewReferenceError(const char* message,
+ Vector< Handle<Object> > args) {
+ return NewError("MakeReferenceError", message, args);
+}
+
+
+Handle<Object> Factory::NewReferenceError(Handle<String> message) {
+ return NewError("$ReferenceError", message);
+}
+
+
+Handle<Object> Factory::NewError(const char* maker,
+ const char* message,
+ Vector< Handle<Object> > args) {
+ // Instantiate a closeable HandleScope for EscapeFrom.
+ v8::HandleScope scope(reinterpret_cast<v8::Isolate*>(isolate()));
+ Handle<FixedArray> array = NewFixedArray(args.length());
+ for (int i = 0; i < args.length(); i++) {
+ array->set(i, *args[i]);
+ }
+ Handle<JSArray> object = NewJSArrayWithElements(array);
+ Handle<Object> result = NewError(maker, message, object);
+ return result.EscapeFrom(&scope);
+}
+
+
+Handle<Object> Factory::NewEvalError(const char* message,
+ Vector< Handle<Object> > args) {
+ return NewError("MakeEvalError", message, args);
+}
+
+
+Handle<Object> Factory::NewError(const char* message,
+ Vector< Handle<Object> > args) {
+ return NewError("MakeError", message, args);
+}
+
+
+Handle<String> Factory::EmergencyNewError(const char* message,
+ Handle<JSArray> args) {
+ const int kBufferSize = 1000;
+ char buffer[kBufferSize];
+ size_t space = kBufferSize;
+ char* p = &buffer[0];
+
+ Vector<char> v(buffer, kBufferSize);
+ OS::StrNCpy(v, message, space);
+ space -= Min(space, strlen(message));
+ p = &buffer[kBufferSize] - space;
+
+ for (unsigned i = 0; i < ARRAY_SIZE(args); i++) {
+ if (space > 0) {
+ *p++ = ' ';
+ space--;
+ if (space > 0) {
+ MaybeObject* maybe_arg = args->GetElement(i);
+ Handle<String> arg_str(reinterpret_cast<String*>(maybe_arg));
+ const char* arg = *arg_str->ToCString();
+ Vector<char> v2(p, static_cast<int>(space));
+ OS::StrNCpy(v2, arg, space);
+ space -= Min(space, strlen(arg));
+ p = &buffer[kBufferSize] - space;
+ }
+ }
+ }
+ if (space > 0) {
+ *p = '\0';
+ } else {
+ buffer[kBufferSize - 1] = '\0';
+ }
+ Handle<String> error_string = NewStringFromUtf8(CStrVector(buffer), TENURED);
+ return error_string;
+}
+
+
+Handle<Object> Factory::NewError(const char* maker,
+ const char* message,
+ Handle<JSArray> args) {
+ Handle<String> make_str = InternalizeUtf8String(maker);
+ Handle<Object> fun_obj(
+ isolate()->js_builtins_object()->GetPropertyNoExceptionThrown(*make_str),
+ isolate());
+ // If the builtins haven't been properly configured yet this error
+ // constructor may not have been defined. Bail out.
+ if (!fun_obj->IsJSFunction()) {
+ return EmergencyNewError(message, args);
+ }
+ Handle<JSFunction> fun = Handle<JSFunction>::cast(fun_obj);
+ Handle<Object> message_obj = InternalizeUtf8String(message);
+ Handle<Object> argv[] = { message_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(),
+ ARRAY_SIZE(argv),
+ argv,
+ &caught_exception);
+ return result;
+}
+
+
+Handle<Object> Factory::NewError(Handle<String> message) {
+ return NewError("$Error", message);
+}
+
+
+Handle<Object> Factory::NewError(const char* constructor,
+ Handle<String> message) {
+ Handle<String> constr = InternalizeUtf8String(constructor);
+ Handle<JSFunction> fun = Handle<JSFunction>(
+ JSFunction::cast(isolate()->js_builtins_object()->
+ GetPropertyNoExceptionThrown(*constr)));
+ 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(),
+ ARRAY_SIZE(argv),
+ argv,
+ &caught_exception);
+ return result;
+}
+
+
+Handle<JSFunction> Factory::NewFunction(Handle<String> name,
+ InstanceType type,
+ int instance_size,
+ Handle<Code> code,
+ bool force_initial_map) {
+ // Allocate the function
+ Handle<JSFunction> function = NewFunction(name, the_hole_value());
+
+ // 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);
+
+ if (force_initial_map ||
+ type != JS_OBJECT_TYPE ||
+ instance_size != JSObject::kHeaderSize) {
+ Handle<Map> initial_map = NewMap(type, instance_size);
+ Handle<JSObject> prototype = NewFunctionPrototype(function);
+ initial_map->set_prototype(*prototype);
+ function->set_initial_map(*initial_map);
+ initial_map->set_constructor(*function);
+ } else {
+ ASSERT(!function->has_initial_map());
+ ASSERT(!function->has_prototype());
+ }
+
+ return function;
+}
+
+
+Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name,
+ InstanceType type,
+ int instance_size,
+ Handle<JSObject> prototype,
+ Handle<Code> code,
+ bool force_initial_map) {
+ // Allocate the function.
+ Handle<JSFunction> function = NewFunction(name, prototype);
+
+ // 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);
+
+ if (force_initial_map ||
+ type != JS_OBJECT_TYPE ||
+ instance_size != JSObject::kHeaderSize) {
+ Handle<Map> initial_map = NewMap(type,
+ instance_size,
+ GetInitialFastElementsKind());
+ function->set_initial_map(*initial_map);
+ initial_map->set_constructor(*function);
+ }
+
+ JSFunction::SetPrototype(function, prototype);
+ return function;
+}
+
+
+Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name,
+ Handle<Code> code) {
+ Handle<JSFunction> function = NewFunctionWithoutPrototype(name,
+ CLASSIC_MODE);
+ function->shared()->set_code(*code);
+ function->set_code(*code);
+ ASSERT(!function->has_initial_map());
+ ASSERT(!function->has_prototype());
+ return function;
+}
+
+
+Handle<ScopeInfo> Factory::NewScopeInfo(int length) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateScopeInfo(length),
+ ScopeInfo);
+}
+
+
+Handle<JSObject> Factory::NewExternal(void* value) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateExternal(value),
+ JSObject);
+}
+
+
+Handle<Code> Factory::NewCode(const CodeDesc& desc,
+ Code::Flags flags,
+ Handle<Object> self_ref,
+ bool immovable,
+ bool crankshafted) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->CreateCode(
+ desc, flags, self_ref, immovable, crankshafted),
+ Code);
+}
+
+
+Handle<Code> Factory::CopyCode(Handle<Code> code) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->CopyCode(*code),
+ Code);
+}
+
+
+Handle<Code> Factory::CopyCode(Handle<Code> code, Vector<byte> reloc_info) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->CopyCode(*code, reloc_info),
+ Code);
+}
+
+
+Handle<String> Factory::InternalizedStringFromString(Handle<String> value) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->InternalizeString(*value), String);
+}
+
+
+Handle<JSObject> Factory::NewJSObject(Handle<JSFunction> constructor,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSObject(*constructor, pretenure), JSObject);
+}
+
+
+Handle<JSModule> Factory::NewJSModule(Handle<Context> context,
+ Handle<ScopeInfo> scope_info) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSModule(*context, *scope_info), JSModule);
+}
+
+
+Handle<GlobalObject> Factory::NewGlobalObject(
+ Handle<JSFunction> constructor) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateGlobalObject(*constructor),
+ GlobalObject);
+}
+
+
+
+Handle<JSObject> Factory::NewJSObjectFromMap(Handle<Map> map,
+ PretenureFlag pretenure,
+ bool alloc_props) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSObjectFromMap(*map, pretenure, alloc_props),
+ JSObject);
+}
+
+
+Handle<JSArray> Factory::NewJSArray(int capacity,
+ ElementsKind elements_kind,
+ PretenureFlag pretenure) {
+ if (capacity != 0) {
+ elements_kind = GetHoleyElementsKind(elements_kind);
+ }
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateJSArrayAndStorage(
+ elements_kind,
+ 0,
+ capacity,
+ INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE,
+ pretenure),
+ JSArray);
+}
+
+
+Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArrayBase> elements,
+ ElementsKind elements_kind,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSArrayWithElements(*elements,
+ elements_kind,
+ elements->length(),
+ 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,
+ uint32_t length,
+ EnsureElementsMode mode) {
+ CALL_HEAP_FUNCTION_VOID(
+ isolate(),
+ array->EnsureCanContainElements(*elements, length, mode));
+}
+
+
+Handle<JSArrayBuffer> Factory::NewJSArrayBuffer() {
+ Handle<JSFunction> array_buffer_fun(
+ isolate()->context()->native_context()->array_buffer_fun());
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSObject(*array_buffer_fun),
+ JSArrayBuffer);
+}
+
+
+Handle<JSDataView> Factory::NewJSDataView() {
+ Handle<JSFunction> data_view_fun(
+ isolate()->context()->native_context()->data_view_fun());
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSObject(*data_view_fun),
+ JSDataView);
+}
+
+
+static JSFunction* GetTypedArrayFun(ExternalArrayType type,
+ Isolate* isolate) {
+ Context* native_context = isolate->context()->native_context();
+ switch (type) {
+ case kExternalUnsignedByteArray:
+ return native_context->uint8_array_fun();
+
+ case kExternalByteArray:
+ return native_context->int8_array_fun();
+
+ case kExternalUnsignedShortArray:
+ return native_context->uint16_array_fun();
+
+ case kExternalShortArray:
+ return native_context->int16_array_fun();
+
+ case kExternalUnsignedIntArray:
+ return native_context->uint32_array_fun();
+
+ case kExternalIntArray:
+ return native_context->int32_array_fun();
+
+ case kExternalFloatArray:
+ return native_context->float_array_fun();
+
+ case kExternalDoubleArray:
+ return native_context->double_array_fun();
+
+ case kExternalPixelArray:
+ return native_context->uint8c_array_fun();
+
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+Handle<JSTypedArray> Factory::NewJSTypedArray(ExternalArrayType type) {
+ Handle<JSFunction> typed_array_fun_handle(GetTypedArrayFun(type, isolate()));
+
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSObject(*typed_array_fun_handle),
+ JSTypedArray);
+}
+
+
+Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
+ Handle<Object> prototype) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSProxy(*handler, *prototype),
+ JSProxy);
+}
+
+
+void Factory::BecomeJSObject(Handle<JSReceiver> object) {
+ CALL_HEAP_FUNCTION_VOID(
+ isolate(),
+ isolate()->heap()->ReinitializeJSReceiver(
+ *object, JS_OBJECT_TYPE, JSObject::kHeaderSize));
+}
+
+
+void Factory::BecomeJSFunction(Handle<JSReceiver> object) {
+ CALL_HEAP_FUNCTION_VOID(
+ isolate(),
+ isolate()->heap()->ReinitializeJSReceiver(
+ *object, JS_FUNCTION_TYPE, JSFunction::kSize));
+}
+
+
+void Factory::SetIdentityHash(Handle<JSObject> object, Smi* hash) {
+ CALL_HEAP_FUNCTION_VOID(
+ isolate(),
+ object->SetIdentityHash(hash, ALLOW_CREATION));
+}
+
+
+Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
+ Handle<String> name,
+ int number_of_literals,
+ bool is_generator,
+ Handle<Code> code,
+ Handle<ScopeInfo> scope_info) {
+ Handle<SharedFunctionInfo> shared = NewSharedFunctionInfo(name);
+ shared->set_code(*code);
+ shared->set_scope_info(*scope_info);
+ int literals_array_size = number_of_literals;
+ // If the function contains object, regexp or array literals,
+ // allocate extra space for a literals array prefix containing the
+ // context.
+ if (number_of_literals > 0) {
+ literals_array_size += JSFunction::kLiteralsPrefixSize;
+ }
+ shared->set_num_literals(literals_array_size);
+ if (is_generator) {
+ shared->set_instance_class_name(isolate()->heap()->Generator_string());
+ shared->DisableOptimization(kGenerator);
+ }
+ return shared;
+}
+
+
+Handle<JSMessageObject> Factory::NewJSMessageObject(
+ Handle<String> type,
+ Handle<JSArray> arguments,
+ int start_position,
+ int end_position,
+ Handle<Object> script,
+ Handle<Object> stack_trace,
+ Handle<Object> stack_frames) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateJSMessageObject(*type,
+ *arguments,
+ start_position,
+ end_position,
+ *script,
+ *stack_trace,
+ *stack_frames),
+ JSMessageObject);
+}
+
+
+Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(Handle<String> name) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->AllocateSharedFunctionInfo(*name),
+ SharedFunctionInfo);
+}
+
+
+Handle<String> Factory::NumberToString(Handle<Object> number) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->NumberToString(*number), String);
+}
+
+
+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,
+ Handle<Object> value) {
+ CALL_HEAP_FUNCTION(isolate(),
+ dictionary->AtNumberPut(key, *value),
+ SeededNumberDictionary);
+}
+
+
+Handle<UnseededNumberDictionary> Factory::DictionaryAtNumberPut(
+ Handle<UnseededNumberDictionary> dictionary,
+ uint32_t key,
+ Handle<Object> value) {
+ CALL_HEAP_FUNCTION(isolate(),
+ dictionary->AtNumberPut(key, *value),
+ UnseededNumberDictionary);
+}
+
+
+Handle<JSFunction> Factory::NewFunctionHelper(Handle<String> name,
+ Handle<Object> prototype) {
+ Handle<SharedFunctionInfo> function_share = NewSharedFunctionInfo(name);
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateFunction(*isolate()->function_map(),
+ *function_share,
+ *prototype),
+ JSFunction);
+}
+
+
+Handle<JSFunction> Factory::NewFunction(Handle<String> name,
+ Handle<Object> prototype) {
+ Handle<JSFunction> fun = NewFunctionHelper(name, prototype);
+ fun->set_context(isolate()->context()->native_context());
+ return fun;
+}
+
+
+Handle<JSFunction> Factory::NewFunctionWithoutPrototypeHelper(
+ Handle<String> name,
+ LanguageMode language_mode) {
+ Handle<SharedFunctionInfo> function_share = NewSharedFunctionInfo(name);
+ 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,
+ *function_share,
+ *the_hole_value()),
+ JSFunction);
+}
+
+
+Handle<JSFunction> Factory::NewFunctionWithoutPrototype(
+ Handle<String> name,
+ LanguageMode language_mode) {
+ Handle<JSFunction> fun =
+ NewFunctionWithoutPrototypeHelper(name, language_mode);
+ fun->set_context(isolate()->context()->native_context());
+ return fun;
+}
+
+
+Handle<Object> Factory::ToObject(Handle<Object> object) {
+ CALL_HEAP_FUNCTION(isolate(), object->ToObject(), Object);
+}
+
+
+Handle<Object> Factory::ToObject(Handle<Object> object,
+ Handle<Context> native_context) {
+ CALL_HEAP_FUNCTION(isolate(), object->ToObject(*native_context), Object);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+Handle<DebugInfo> Factory::NewDebugInfo(Handle<SharedFunctionInfo> shared) {
+ // Get the original code of the function.
+ Handle<Code> code(shared->code());
+
+ // Create a copy of the code before allocating the debug info object to avoid
+ // allocation while setting up the debug info object.
+ Handle<Code> original_code(*Factory::CopyCode(code));
+
+ // Allocate initial fixed array for active break points before allocating the
+ // debug info object to avoid allocation while setting up the debug info
+ // object.
+ Handle<FixedArray> break_points(
+ NewFixedArray(Debug::kEstimatedNofBreakPointsInFunction));
+
+ // Create and set up the debug info object. Debug info contains function, a
+ // copy of the original code, the executing code and initial fixed array for
+ // active break points.
+ Handle<DebugInfo> debug_info =
+ Handle<DebugInfo>::cast(NewStruct(DEBUG_INFO_TYPE));
+ debug_info->set_shared(*shared);
+ debug_info->set_original_code(*original_code);
+ debug_info->set_code(*code);
+ debug_info->set_break_points(*break_points);
+
+ // Link debug info to function.
+ shared->set_debug_info(*debug_info);
+
+ return debug_info;
+}
+#endif
+
+
+Handle<JSObject> Factory::NewArgumentsObject(Handle<Object> callee,
+ int length) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateArgumentsObject(*callee, length), JSObject);
+}
+
+
+Handle<JSFunction> Factory::CreateApiFunction(
+ Handle<FunctionTemplateInfo> obj, ApiInstanceType instance_type) {
+ Handle<Code> code = isolate()->builtins()->HandleApiCall();
+ Handle<Code> construct_stub = isolate()->builtins()->JSConstructStubApi();
+
+ int internal_field_count = 0;
+ if (!obj->instance_template()->IsUndefined()) {
+ Handle<ObjectTemplateInfo> instance_template =
+ Handle<ObjectTemplateInfo>(
+ ObjectTemplateInfo::cast(obj->instance_template()));
+ internal_field_count =
+ Smi::cast(instance_template->internal_field_count())->value();
+ }
+
+ // TODO(svenpanne) Kill ApiInstanceType and refactor things by generalizing
+ // JSObject::GetHeaderSize.
+ int instance_size = kPointerSize * internal_field_count;
+ InstanceType type;
+ switch (instance_type) {
+ case JavaScriptObject:
+ type = JS_OBJECT_TYPE;
+ instance_size += JSObject::kHeaderSize;
+ break;
+ case InnerGlobalObject:
+ type = JS_GLOBAL_OBJECT_TYPE;
+ instance_size += JSGlobalObject::kSize;
+ break;
+ case OuterGlobalObject:
+ type = JS_GLOBAL_PROXY_TYPE;
+ instance_size += JSGlobalProxy::kSize;
+ break;
+ default:
+ UNREACHABLE();
+ type = JS_OBJECT_TYPE; // Keep the compiler happy.
+ break;
+ }
+
+ Handle<JSFunction> result =
+ NewFunction(Factory::empty_string(),
+ type,
+ instance_size,
+ code,
+ true);
+
+ // Set length.
+ result->shared()->set_length(obj->length());
+
+ // Set class name.
+ Handle<Object> class_name = Handle<Object>(obj->class_name(), isolate());
+ if (class_name->IsString()) {
+ result->shared()->set_instance_class_name(*class_name);
+ result->shared()->set_name(*class_name);
+ }
+
+ Handle<Map> map = Handle<Map>(result->initial_map());
+
+ // Mark as undetectable if needed.
+ if (obj->undetectable()) {
+ map->set_is_undetectable();
+ }
+
+ // Mark as hidden for the __proto__ accessor if needed.
+ if (obj->hidden_prototype()) {
+ map->set_is_hidden_prototype();
+ }
+
+ // Mark as needs_access_check if needed.
+ if (obj->needs_access_check()) {
+ map->set_is_access_check_needed(true);
+ }
+
+ // Set interceptor information in the map.
+ if (!obj->named_property_handler()->IsUndefined()) {
+ map->set_has_named_interceptor();
+ }
+ if (!obj->indexed_property_handler()->IsUndefined()) {
+ map->set_has_indexed_interceptor();
+ }
+
+ // Set instance call-as-function information in the map.
+ if (!obj->instance_call_handler()->IsUndefined()) {
+ map->set_has_instance_call_handler();
+ }
+
+ result->shared()->set_function_data(*obj);
+ result->shared()->set_construct_stub(*construct_stub);
+ result->shared()->DontAdaptArguments();
+
+ // Recursively copy parent templates' accessors, 'data' may be modified.
+ int max_number_of_additional_properties = 0;
+ FunctionTemplateInfo* info = *obj;
+ while (true) {
+ Object* props = info->property_accessors();
+ if (!props->IsUndefined()) {
+ Handle<Object> props_handle(props, isolate());
+ NeanderArray props_array(props_handle);
+ max_number_of_additional_properties += props_array.length();
+ }
+ Object* parent = info->parent_template();
+ if (parent->IsUndefined()) break;
+ info = FunctionTemplateInfo::cast(parent);
+ }
+
+ Map::EnsureDescriptorSlack(map, max_number_of_additional_properties);
+
+ while (true) {
+ Handle<Object> props = Handle<Object>(obj->property_accessors(),
+ isolate());
+ if (!props->IsUndefined()) {
+ Map::AppendCallbackDescriptors(map, props);
+ }
+ Handle<Object> parent = Handle<Object>(obj->parent_template(), isolate());
+ if (parent->IsUndefined()) break;
+ obj = Handle<FunctionTemplateInfo>::cast(parent);
+ }
+
+ ASSERT(result->shared()->IsApiFunction());
+ return result;
+}
+
+
+Handle<MapCache> Factory::NewMapCache(int at_least_space_for) {
+ CALL_HEAP_FUNCTION(isolate(),
+ MapCache::Allocate(isolate()->heap(),
+ at_least_space_for),
+ MapCache);
+}
+
+
+MUST_USE_RESULT static MaybeObject* UpdateMapCacheWith(Context* context,
+ FixedArray* keys,
+ Map* map) {
+ Object* result;
+ { MaybeObject* maybe_result =
+ MapCache::cast(context->map_cache())->Put(keys, map);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ context->set_map_cache(MapCache::cast(result));
+ return result;
+}
+
+
+Handle<MapCache> Factory::AddToMapCache(Handle<Context> context,
+ Handle<FixedArray> keys,
+ Handle<Map> map) {
+ CALL_HEAP_FUNCTION(isolate(),
+ UpdateMapCacheWith(*context, *keys, *map), MapCache);
+}
+
+
+Handle<Map> Factory::ObjectLiteralMapFromCache(Handle<Context> context,
+ Handle<FixedArray> keys) {
+ if (context->map_cache()->IsUndefined()) {
+ // Allocate the new map cache for the native context.
+ Handle<MapCache> new_cache = NewMapCache(24);
+ context->set_map_cache(*new_cache);
+ }
+ // Check to see whether there is a matching element in the cache.
+ Handle<MapCache> cache =
+ Handle<MapCache>(MapCache::cast(context->map_cache()));
+ Handle<Object> result = Handle<Object>(cache->Lookup(*keys), isolate());
+ if (result->IsMap()) return Handle<Map>::cast(result);
+ // Create a new map and add it to the cache.
+ Handle<Map> map =
+ CopyMap(Handle<Map>(context->object_function()->initial_map()),
+ keys->length());
+ AddToMapCache(context, keys, map);
+ return Handle<Map>(map);
+}
+
+
+void Factory::SetRegExpAtomData(Handle<JSRegExp> regexp,
+ JSRegExp::Type type,
+ Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<Object> data) {
+ Handle<FixedArray> store = NewFixedArray(JSRegExp::kAtomDataSize);
+
+ store->set(JSRegExp::kTagIndex, Smi::FromInt(type));
+ store->set(JSRegExp::kSourceIndex, *source);
+ store->set(JSRegExp::kFlagsIndex, Smi::FromInt(flags.value()));
+ store->set(JSRegExp::kAtomPatternIndex, *data);
+ regexp->set_data(*store);
+}
+
+void Factory::SetRegExpIrregexpData(Handle<JSRegExp> regexp,
+ JSRegExp::Type type,
+ Handle<String> source,
+ JSRegExp::Flags flags,
+ int capture_count) {
+ Handle<FixedArray> store = NewFixedArray(JSRegExp::kIrregexpDataSize);
+ Smi* uninitialized = Smi::FromInt(JSRegExp::kUninitializedValue);
+ store->set(JSRegExp::kTagIndex, Smi::FromInt(type));
+ store->set(JSRegExp::kSourceIndex, *source);
+ store->set(JSRegExp::kFlagsIndex, Smi::FromInt(flags.value()));
+ store->set(JSRegExp::kIrregexpASCIICodeIndex, uninitialized);
+ store->set(JSRegExp::kIrregexpUC16CodeIndex, uninitialized);
+ store->set(JSRegExp::kIrregexpASCIICodeSavedIndex, uninitialized);
+ store->set(JSRegExp::kIrregexpUC16CodeSavedIndex, uninitialized);
+ store->set(JSRegExp::kIrregexpMaxRegisterCountIndex, Smi::FromInt(0));
+ store->set(JSRegExp::kIrregexpCaptureCountIndex,
+ Smi::FromInt(capture_count));
+ regexp->set_data(*store);
+}
+
+
+
+void Factory::ConfigureInstance(Handle<FunctionTemplateInfo> desc,
+ Handle<JSObject> instance,
+ bool* pending_exception) {
+ // Configure the instance by adding the properties specified by the
+ // instance template.
+ Handle<Object> instance_template(desc->instance_template(), isolate());
+ if (!instance_template->IsUndefined()) {
+ Execution::ConfigureInstance(instance,
+ instance_template,
+ pending_exception);
+ } else {
+ *pending_exception = false;
+ }
+}
+
+
+Handle<Object> Factory::GlobalConstantFor(Handle<String> name) {
+ Heap* h = isolate()->heap();
+ if (name->Equals(h->undefined_string())) return undefined_value();
+ if (name->Equals(h->nan_string())) return nan_value();
+ if (name->Equals(h->infinity_string())) return infinity_value();
+ return Handle<Object>::null();
+}
+
+
+Handle<Object> Factory::ToBoolean(bool value) {
+ return value ? true_value() : false_value();
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/factory.h b/chromium/v8/src/factory.h
new file mode 100644
index 00000000000..02c9a4d2eb4
--- /dev/null
+++ b/chromium/v8/src/factory.h
@@ -0,0 +1,652 @@
+// Copyright 2012 the V8 project authors. 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_FACTORY_H_
+#define V8_FACTORY_H_
+
+#include "globals.h"
+#include "handles.h"
+#include "heap.h"
+
+namespace v8 {
+namespace internal {
+
+// Interface for handle based allocation.
+
+class Factory {
+ public:
+ // Allocate a new boxed value.
+ Handle<Box> NewBox(
+ Handle<Object> value,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocate a new uninitialized fixed array.
+ Handle<FixedArray> NewFixedArray(
+ int size,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocate a new fixed array with non-existing entries (the hole).
+ Handle<FixedArray> NewFixedArrayWithHoles(
+ int size,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocate a new uninitialized fixed double array.
+ Handle<FixedDoubleArray> NewFixedDoubleArray(
+ int size,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ Handle<SeededNumberDictionary> NewSeededNumberDictionary(
+ int at_least_space_for);
+
+ Handle<UnseededNumberDictionary> NewUnseededNumberDictionary(
+ int at_least_space_for);
+
+ Handle<NameDictionary> NewNameDictionary(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,
+ int slack = 0);
+ Handle<DeoptimizationInputData> NewDeoptimizationInputData(
+ int deopt_entry_count,
+ PretenureFlag pretenure);
+ Handle<DeoptimizationOutputData> NewDeoptimizationOutputData(
+ int deopt_entry_count,
+ PretenureFlag pretenure);
+ // Allocates a pre-tenured empty AccessorPair.
+ Handle<AccessorPair> NewAccessorPair();
+
+ Handle<TypeFeedbackInfo> NewTypeFeedbackInfo();
+
+ Handle<String> InternalizeUtf8String(Vector<const char> str);
+ Handle<String> InternalizeUtf8String(const char* str) {
+ return InternalizeUtf8String(CStrVector(str));
+ }
+ Handle<String> InternalizeString(Handle<String> str);
+ Handle<String> InternalizeOneByteString(Vector<const uint8_t> str);
+ Handle<String> InternalizeOneByteString(Handle<SeqOneByteString>,
+ int from,
+ int length);
+ Handle<String> InternalizeTwoByteString(Vector<const uc16> str);
+
+
+ // String creation functions. Most of the string creation functions take
+ // a Heap::PretenureFlag argument to optionally request that they be
+ // allocated in the old generation. The pretenure flag defaults to
+ // DONT_TENURE.
+ //
+ // Creates a new String object. There are two String encodings: ASCII and
+ // two byte. One should choose between the three string factory functions
+ // based on the encoding of the string buffer that the string is
+ // initialized from.
+ // - ...FromAscii initializes the string from a buffer that is ASCII
+ // encoded (it does not check that the buffer is ASCII encoded) and
+ // the result will be ASCII encoded.
+ // - ...FromUtf8 initializes the string from a buffer that is UTF-8
+ // encoded. If the characters are all single-byte characters, the
+ // result will be ASCII encoded, otherwise it will converted to two
+ // byte.
+ // - ...FromTwoByte initializes the string from a buffer that is two
+ // byte encoded. If the characters are all single-byte characters,
+ // the result will be converted to ASCII, otherwise it will be left as
+ // two byte.
+ //
+ // ASCII strings are pretenured when used as keys in the SourceCodeCache.
+ Handle<String> NewStringFromOneByte(
+ Vector<const uint8_t> str,
+ PretenureFlag pretenure = NOT_TENURED);
+ // TODO(dcarney): remove this function.
+ inline Handle<String> NewStringFromAscii(
+ Vector<const char> str,
+ PretenureFlag pretenure = NOT_TENURED) {
+ return NewStringFromOneByte(Vector<const uint8_t>::cast(str), pretenure);
+ }
+
+ // UTF8 strings are pretenured when used for regexp literal patterns and
+ // flags in the parser.
+ Handle<String> NewStringFromUtf8(
+ Vector<const char> str,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ Handle<String> NewStringFromTwoByte(
+ Vector<const uc16> str,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocates and partially initializes an ASCII or TwoByte String. The
+ // characters of the string are uninitialized. Currently used in regexp code
+ // only, where they are pretenured.
+ Handle<SeqOneByteString> NewRawOneByteString(
+ int length,
+ PretenureFlag pretenure = NOT_TENURED);
+ Handle<SeqTwoByteString> NewRawTwoByteString(
+ int length,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Create a new cons string object which consists of a pair of strings.
+ Handle<String> NewConsString(Handle<String> first,
+ Handle<String> second);
+
+ // Create a new sequential string containing the concatenation of the inputs.
+ Handle<String> NewFlatConcatString(Handle<String> first,
+ Handle<String> second);
+
+ // Create a new string object which holds a substring of a string.
+ Handle<String> NewSubString(Handle<String> str,
+ int begin,
+ int end);
+
+ // Create a new string object which holds a proper substring of a string.
+ Handle<String> NewProperSubString(Handle<String> str,
+ int begin,
+ int end);
+
+ // Creates a new external String object. There are two String encodings
+ // in the system: ASCII and two byte. Unlike other String types, it does
+ // not make sense to have a UTF-8 factory function for external strings,
+ // because we cannot change the underlying buffer.
+ Handle<String> NewExternalStringFromAscii(
+ const ExternalAsciiString::Resource* resource);
+ Handle<String> NewExternalStringFromTwoByte(
+ const ExternalTwoByteString::Resource* resource);
+
+ // Create a symbol.
+ Handle<Symbol> NewSymbol();
+
+ // Create a global (but otherwise uninitialized) context.
+ Handle<Context> NewNativeContext();
+
+ // Create a global context.
+ Handle<Context> NewGlobalContext(Handle<JSFunction> function,
+ Handle<ScopeInfo> scope_info);
+
+ // Create a module context.
+ Handle<Context> NewModuleContext(Handle<ScopeInfo> scope_info);
+
+ // Create a function context.
+ Handle<Context> NewFunctionContext(int length, Handle<JSFunction> function);
+
+ // Create a catch context.
+ Handle<Context> NewCatchContext(Handle<JSFunction> function,
+ Handle<Context> previous,
+ Handle<String> name,
+ Handle<Object> thrown_object);
+
+ // Create a 'with' context.
+ Handle<Context> NewWithContext(Handle<JSFunction> function,
+ Handle<Context> previous,
+ Handle<JSObject> extension);
+
+ // Create a block context.
+ Handle<Context> NewBlockContext(Handle<JSFunction> function,
+ Handle<Context> previous,
+ Handle<ScopeInfo> scope_info);
+
+ // Return the internalized version of the passed in string.
+ Handle<String> InternalizedStringFromString(Handle<String> value);
+
+ // Allocate a new struct. The struct is pretenured (allocated directly in
+ // the old generation).
+ Handle<Struct> NewStruct(InstanceType type);
+
+ Handle<DeclaredAccessorDescriptor> NewDeclaredAccessorDescriptor();
+
+ Handle<DeclaredAccessorInfo> NewDeclaredAccessorInfo();
+
+ Handle<ExecutableAccessorInfo> NewExecutableAccessorInfo();
+
+ Handle<Script> NewScript(Handle<String> source);
+
+ // Foreign objects are pretenured when allocated by the bootstrapper.
+ Handle<Foreign> NewForeign(Address addr,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocate a new foreign object. The foreign is pretenured (allocated
+ // directly in the old generation).
+ Handle<Foreign> NewForeign(const AccessorDescriptor* foreign);
+
+ Handle<ByteArray> NewByteArray(int length,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ Handle<ExternalArray> NewExternalArray(
+ int length,
+ ExternalArrayType array_type,
+ void* external_pointer,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ Handle<Cell> NewCell(Handle<Object> value);
+
+ Handle<PropertyCell> NewPropertyCell(Handle<Object> value);
+
+ Handle<AllocationSite> NewAllocationSite();
+
+ Handle<Map> NewMap(
+ InstanceType type,
+ int instance_size,
+ ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND);
+
+ Handle<JSObject> NewFunctionPrototype(Handle<JSFunction> function);
+
+ Handle<Map> CopyWithPreallocatedFieldDescriptors(Handle<Map> map);
+
+ // Copy the map adding more inobject properties if possible without
+ // overflowing the instance size.
+ Handle<Map> CopyMap(Handle<Map> map, int extra_inobject_props);
+ Handle<Map> CopyMap(Handle<Map> map);
+
+ Handle<Map> GetElementsTransitionMap(Handle<JSObject> object,
+ ElementsKind elements_kind);
+
+ Handle<FixedArray> CopyFixedArray(Handle<FixedArray> array);
+
+ Handle<FixedArray> CopySizeFixedArray(Handle<FixedArray> array,
+ int new_length);
+
+ 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(int32_t value,
+ PretenureFlag pretenure = NOT_TENURED);
+ Handle<Object> NewNumberFromUint(uint32_t value,
+ PretenureFlag pretenure = NOT_TENURED);
+ inline Handle<Object> NewNumberFromSize(size_t value,
+ PretenureFlag pretenure = NOT_TENURED);
+ Handle<HeapNumber> NewHeapNumber(double value,
+ PretenureFlag pretenure = NOT_TENURED);
+
+
+ // These objects are used by the api to create env-independent data
+ // structures in the heap.
+ Handle<JSObject> NewNeanderObject();
+
+ Handle<JSObject> NewArgumentsObject(Handle<Object> callee, int length);
+
+ // JS objects are pretenured when allocated by the bootstrapper and
+ // runtime.
+ Handle<JSObject> NewJSObject(Handle<JSFunction> constructor,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Global objects are pretenured.
+ Handle<GlobalObject> NewGlobalObject(Handle<JSFunction> constructor);
+
+ // JS objects are pretenured when allocated by the bootstrapper and
+ // runtime.
+ Handle<JSObject> NewJSObjectFromMap(Handle<Map> map,
+ PretenureFlag pretenure = NOT_TENURED,
+ bool allocate_properties = true);
+
+ Handle<JSObject> NewJSObjectFromMapForDeoptimizer(
+ Handle<Map> map, PretenureFlag pretenure = NOT_TENURED);
+
+ // JS modules are pretenured.
+ Handle<JSModule> NewJSModule(Handle<Context> context,
+ Handle<ScopeInfo> scope_info);
+
+ // JS arrays are pretenured when allocated by the parser.
+ Handle<JSArray> NewJSArray(
+ int capacity,
+ ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ Handle<JSArray> NewJSArrayWithElements(
+ Handle<FixedArrayBase> elements,
+ ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND,
+ 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,
+ uint32_t length,
+ EnsureElementsMode mode);
+
+ Handle<JSArrayBuffer> NewJSArrayBuffer();
+
+ Handle<JSTypedArray> NewJSTypedArray(ExternalArrayType type);
+
+ Handle<JSDataView> NewJSDataView();
+
+ 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, Smi* hash);
+
+ Handle<JSFunction> NewFunction(Handle<String> name,
+ Handle<Object> prototype);
+
+ Handle<JSFunction> NewFunctionWithoutPrototype(
+ Handle<String> name,
+ LanguageMode language_mode);
+
+ Handle<JSFunction> NewFunction(Handle<Object> super, bool is_global);
+
+ Handle<JSFunction> BaseNewFunctionFromSharedFunctionInfo(
+ Handle<SharedFunctionInfo> function_info,
+ Handle<Map> function_map,
+ PretenureFlag pretenure);
+
+ Handle<JSFunction> NewFunctionFromSharedFunctionInfo(
+ Handle<SharedFunctionInfo> function_info,
+ Handle<Context> context,
+ PretenureFlag pretenure = TENURED);
+
+ Handle<ScopeInfo> NewScopeInfo(int length);
+
+ Handle<JSObject> NewExternal(void* value);
+
+ Handle<Code> NewCode(const CodeDesc& desc,
+ Code::Flags flags,
+ Handle<Object> self_reference,
+ bool immovable = false,
+ bool crankshafted = false);
+
+ Handle<Code> CopyCode(Handle<Code> code);
+
+ Handle<Code> CopyCode(Handle<Code> code, Vector<byte> reloc_info);
+
+ Handle<Object> ToObject(Handle<Object> object);
+ Handle<Object> ToObject(Handle<Object> object,
+ Handle<Context> native_context);
+
+ // Interface for creating error objects.
+
+ Handle<Object> NewError(const char* maker, const char* message,
+ Handle<JSArray> args);
+ Handle<String> EmergencyNewError(const char* message, Handle<JSArray> args);
+ Handle<Object> NewError(const char* maker, const char* message,
+ Vector< Handle<Object> > args);
+ Handle<Object> NewError(const char* message,
+ Vector< Handle<Object> > args);
+ Handle<Object> NewError(Handle<String> message);
+ Handle<Object> NewError(const char* constructor,
+ Handle<String> message);
+
+ Handle<Object> NewTypeError(const char* message,
+ Vector< Handle<Object> > args);
+ Handle<Object> NewTypeError(Handle<String> message);
+
+ Handle<Object> NewRangeError(const char* message,
+ Vector< Handle<Object> > args);
+ Handle<Object> NewRangeError(Handle<String> message);
+
+ Handle<Object> NewSyntaxError(const char* message, Handle<JSArray> args);
+ Handle<Object> NewSyntaxError(Handle<String> message);
+
+ Handle<Object> NewReferenceError(const char* message,
+ Vector< Handle<Object> > args);
+ Handle<Object> NewReferenceError(Handle<String> message);
+
+ Handle<Object> NewEvalError(const char* message,
+ Vector< Handle<Object> > args);
+
+
+ Handle<JSFunction> NewFunction(Handle<String> name,
+ InstanceType type,
+ int instance_size,
+ Handle<Code> code,
+ bool force_initial_map);
+
+ Handle<JSFunction> NewFunction(Handle<Map> function_map,
+ Handle<SharedFunctionInfo> shared, Handle<Object> prototype);
+
+
+ Handle<JSFunction> NewFunctionWithPrototype(Handle<String> name,
+ InstanceType type,
+ int instance_size,
+ Handle<JSObject> prototype,
+ Handle<Code> code,
+ bool force_initial_map);
+
+ Handle<JSFunction> NewFunctionWithoutPrototype(Handle<String> name,
+ Handle<Code> code);
+
+ Handle<String> NumberToString(Handle<Object> number);
+ Handle<String> Uint32ToString(uint32_t value);
+
+ enum ApiInstanceType {
+ JavaScriptObject,
+ InnerGlobalObject,
+ OuterGlobalObject
+ };
+
+ Handle<JSFunction> CreateApiFunction(
+ Handle<FunctionTemplateInfo> data,
+ ApiInstanceType type = JavaScriptObject);
+
+ Handle<JSFunction> InstallMembers(Handle<JSFunction> function);
+
+ // Installs interceptors on the instance. 'desc' is a function template,
+ // and instance is an object instance created by the function of this
+ // function template.
+ void ConfigureInstance(Handle<FunctionTemplateInfo> desc,
+ Handle<JSObject> instance,
+ bool* pending_exception);
+
+#define ROOT_ACCESSOR(type, name, camel_name) \
+ inline Handle<type> name() { \
+ return Handle<type>(BitCast<type**>( \
+ &isolate()->heap()->roots_[Heap::k##camel_name##RootIndex])); \
+ }
+ ROOT_LIST(ROOT_ACCESSOR)
+#undef ROOT_ACCESSOR_ACCESSOR
+
+#define STRING_ACCESSOR(name, str) \
+ inline Handle<String> name() { \
+ return Handle<String>(BitCast<String**>( \
+ &isolate()->heap()->roots_[Heap::k##name##RootIndex])); \
+ }
+ INTERNALIZED_STRING_LIST(STRING_ACCESSOR)
+#undef STRING_ACCESSOR
+
+ Handle<String> hidden_string() {
+ return Handle<String>(&isolate()->heap()->hidden_string_);
+ }
+
+ Handle<SharedFunctionInfo> NewSharedFunctionInfo(
+ Handle<String> name,
+ int number_of_literals,
+ bool is_generator,
+ Handle<Code> code,
+ Handle<ScopeInfo> scope_info);
+ Handle<SharedFunctionInfo> NewSharedFunctionInfo(Handle<String> name);
+
+ Handle<JSMessageObject> NewJSMessageObject(
+ Handle<String> type,
+ Handle<JSArray> arguments,
+ int start_position,
+ int end_position,
+ Handle<Object> script,
+ Handle<Object> stack_trace,
+ Handle<Object> stack_frames);
+
+ Handle<SeededNumberDictionary> DictionaryAtNumberPut(
+ Handle<SeededNumberDictionary>,
+ uint32_t key,
+ Handle<Object> value);
+
+ Handle<UnseededNumberDictionary> DictionaryAtNumberPut(
+ Handle<UnseededNumberDictionary>,
+ uint32_t key,
+ Handle<Object> value);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Handle<DebugInfo> NewDebugInfo(Handle<SharedFunctionInfo> shared);
+#endif
+
+ // Return a map using the map cache in the native context.
+ // The key the an ordered set of property names.
+ Handle<Map> ObjectLiteralMapFromCache(Handle<Context> context,
+ Handle<FixedArray> keys);
+
+ // Creates a new FixedArray that holds the data associated with the
+ // atom regexp and stores it in the regexp.
+ void SetRegExpAtomData(Handle<JSRegExp> regexp,
+ JSRegExp::Type type,
+ Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<Object> match_pattern);
+
+ // Creates a new FixedArray that holds the data associated with the
+ // irregexp regexp and stores it in the regexp.
+ void SetRegExpIrregexpData(Handle<JSRegExp> regexp,
+ JSRegExp::Type type,
+ Handle<String> source,
+ 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); }
+
+ Handle<JSFunction> NewFunctionHelper(Handle<String> name,
+ Handle<Object> prototype);
+
+ Handle<JSFunction> NewFunctionWithoutPrototypeHelper(
+ Handle<String> name,
+ LanguageMode language_mode);
+
+ // Create a new map cache.
+ Handle<MapCache> NewMapCache(int at_least_space_for);
+
+ // Update the map cache in the native context with (keys, map)
+ Handle<MapCache> AddToMapCache(Handle<Context> context,
+ Handle<FixedArray> keys,
+ Handle<Map> map);
+};
+
+
+Handle<Object> Factory::NewNumberFromSize(size_t value,
+ PretenureFlag pretenure) {
+ if (Smi::IsValid(static_cast<intptr_t>(value))) {
+ return Handle<Object>(Smi::FromIntptr(static_cast<intptr_t>(value)),
+ isolate());
+ } else {
+ return NewNumber(static_cast<double>(value), pretenure);
+ }
+}
+
+
+// Used to "safely" transition from pointer-based runtime code to Handle-based
+// runtime code. When a GC happens during the called Handle-based code, a
+// failure object is returned to the pointer-based code to cause it abort and
+// re-trigger a gc of it's own. Since this double-gc will cause the Handle-based
+// code to be called twice, it must be idempotent.
+class IdempotentPointerToHandleCodeTrampoline {
+ public:
+ explicit IdempotentPointerToHandleCodeTrampoline(Isolate* isolate)
+ : isolate_(isolate) {}
+
+ template<typename R>
+ MUST_USE_RESULT MaybeObject* Call(R (*function)()) {
+ int collections = isolate_->heap()->gc_count();
+ (*function)();
+ return (collections == isolate_->heap()->gc_count())
+ ? isolate_->heap()->true_value()
+ : reinterpret_cast<MaybeObject*>(Failure::RetryAfterGC());
+ }
+
+ template<typename R>
+ MUST_USE_RESULT MaybeObject* CallWithReturnValue(R (*function)()) {
+ int collections = isolate_->heap()->gc_count();
+ Object* result = (*function)();
+ return (collections == isolate_->heap()->gc_count())
+ ? result
+ : reinterpret_cast<MaybeObject*>(Failure::RetryAfterGC());
+ }
+
+ template<typename R, typename P1>
+ MUST_USE_RESULT MaybeObject* Call(R (*function)(P1), P1 p1) {
+ int collections = isolate_->heap()->gc_count();
+ (*function)(p1);
+ return (collections == isolate_->heap()->gc_count())
+ ? isolate_->heap()->true_value()
+ : reinterpret_cast<MaybeObject*>(Failure::RetryAfterGC());
+ }
+
+ template<typename R, typename P1>
+ MUST_USE_RESULT MaybeObject* CallWithReturnValue(
+ R (*function)(P1),
+ P1 p1) {
+ int collections = isolate_->heap()->gc_count();
+ Object* result = (*function)(p1);
+ return (collections == isolate_->heap()->gc_count())
+ ? result
+ : reinterpret_cast<MaybeObject*>(Failure::RetryAfterGC());
+ }
+
+ template<typename R, typename P1, typename P2>
+ MUST_USE_RESULT MaybeObject* Call(
+ R (*function)(P1, P2),
+ P1 p1,
+ P2 p2) {
+ int collections = isolate_->heap()->gc_count();
+ (*function)(p1, p2);
+ return (collections == isolate_->heap()->gc_count())
+ ? isolate_->heap()->true_value()
+ : reinterpret_cast<MaybeObject*>(Failure::RetryAfterGC());
+ }
+
+ template<typename R, typename P1, typename P2>
+ MUST_USE_RESULT MaybeObject* CallWithReturnValue(
+ R (*function)(P1, P2),
+ P1 p1,
+ P2 p2) {
+ int collections = isolate_->heap()->gc_count();
+ Object* result = (*function)(p1, p2);
+ return (collections == isolate_->heap()->gc_count())
+ ? result
+ : reinterpret_cast<MaybeObject*>(Failure::RetryAfterGC());
+ }
+
+ private:
+ Isolate* isolate_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_FACTORY_H_
diff --git a/chromium/v8/src/fast-dtoa.cc b/chromium/v8/src/fast-dtoa.cc
new file mode 100644
index 00000000000..e62bd01fbb5
--- /dev/null
+++ b/chromium/v8/src/fast-dtoa.cc
@@ -0,0 +1,738 @@
+// 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 "../include/v8stdint.h"
+#include "checks.h"
+#include "utils.h"
+
+#include "fast-dtoa.h"
+
+#include "cached-powers.h"
+#include "diy-fp.h"
+#include "double.h"
+
+namespace v8 {
+namespace internal {
+
+// The minimal and maximal target exponent define the range of w's binary
+// exponent, where 'w' is the result of multiplying the input by a cached power
+// of ten.
+//
+// A different range might be chosen on a different platform, to optimize digit
+// generation, but a smaller range requires more powers of ten to be cached.
+static const int kMinimalTargetExponent = -60;
+static const int kMaximalTargetExponent = -32;
+
+
+// Adjusts the last digit of the generated number, and screens out generated
+// solutions that may be inaccurate. A solution may be inaccurate if it is
+// outside the safe interval, or if we ctannot prove that it is closer to the
+// input than a neighboring representation of the same length.
+//
+// Input: * buffer containing the digits of too_high / 10^kappa
+// * the buffer's length
+// * distance_too_high_w == (too_high - w).f() * unit
+// * unsafe_interval == (too_high - too_low).f() * unit
+// * rest = (too_high - buffer * 10^kappa).f() * unit
+// * ten_kappa = 10^kappa * unit
+// * unit = the common multiplier
+// Output: returns true if the buffer is guaranteed to contain the closest
+// representable number to the input.
+// Modifies the generated digits in the buffer to approach (round towards) w.
+static bool RoundWeed(Vector<char> buffer,
+ int length,
+ uint64_t distance_too_high_w,
+ uint64_t unsafe_interval,
+ uint64_t rest,
+ uint64_t ten_kappa,
+ uint64_t unit) {
+ uint64_t small_distance = distance_too_high_w - unit;
+ uint64_t big_distance = distance_too_high_w + unit;
+ // Let w_low = too_high - big_distance, and
+ // w_high = too_high - small_distance.
+ // Note: w_low < w < w_high
+ //
+ // The real w (* unit) must lie somewhere inside the interval
+ // ]w_low; w_high[ (often written as "(w_low; w_high)")
+
+ // Basically the buffer currently contains a number in the unsafe interval
+ // ]too_low; too_high[ with too_low < w < too_high
+ //
+ // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // ^v 1 unit ^ ^ ^ ^
+ // boundary_high --------------------- . . . .
+ // ^v 1 unit . . . .
+ // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . .
+ // . . ^ . .
+ // . big_distance . . .
+ // . . . . rest
+ // small_distance . . . .
+ // v . . . .
+ // w_high - - - - - - - - - - - - - - - - - - . . . .
+ // ^v 1 unit . . . .
+ // w ---------------------------------------- . . . .
+ // ^v 1 unit v . . .
+ // w_low - - - - - - - - - - - - - - - - - - - - - . . .
+ // . . v
+ // buffer --------------------------------------------------+-------+--------
+ // . .
+ // safe_interval .
+ // v .
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
+ // ^v 1 unit .
+ // boundary_low ------------------------- unsafe_interval
+ // ^v 1 unit v
+ // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ //
+ //
+ // Note that the value of buffer could lie anywhere inside the range too_low
+ // to too_high.
+ //
+ // boundary_low, boundary_high and w are approximations of the real boundaries
+ // and v (the input number). They are guaranteed to be precise up to one unit.
+ // In fact the error is guaranteed to be strictly less than one unit.
+ //
+ // Anything that lies outside the unsafe interval is guaranteed not to round
+ // to v when read again.
+ // Anything that lies inside the safe interval is guaranteed to round to v
+ // when read again.
+ // If the number inside the buffer lies inside the unsafe interval but not
+ // inside the safe interval then we simply do not know and bail out (returning
+ // false).
+ //
+ // Similarly we have to take into account the imprecision of 'w' when finding
+ // the closest representation of 'w'. If we have two potential
+ // representations, and one is closer to both w_low and w_high, then we know
+ // it is closer to the actual value v.
+ //
+ // By generating the digits of too_high we got the largest (closest to
+ // too_high) buffer that is still in the unsafe interval. In the case where
+ // w_high < buffer < too_high we try to decrement the buffer.
+ // This way the buffer approaches (rounds towards) w.
+ // There are 3 conditions that stop the decrementation process:
+ // 1) the buffer is already below w_high
+ // 2) decrementing the buffer would make it leave the unsafe interval
+ // 3) decrementing the buffer would yield a number below w_high and farther
+ // away than the current number. In other words:
+ // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high
+ // Instead of using the buffer directly we use its distance to too_high.
+ // Conceptually rest ~= too_high - buffer
+ // We need to do the following tests in this order to avoid over- and
+ // underflows.
+ ASSERT(rest <= unsafe_interval);
+ while (rest < small_distance && // Negated condition 1
+ unsafe_interval - rest >= ten_kappa && // Negated condition 2
+ (rest + ten_kappa < small_distance || // buffer{-1} > w_high
+ small_distance - rest >= rest + ten_kappa - small_distance)) {
+ buffer[length - 1]--;
+ rest += ten_kappa;
+ }
+
+ // We have approached w+ as much as possible. We now test if approaching w-
+ // would require changing the buffer. If yes, then we have two possible
+ // representations close to w, but we cannot decide which one is closer.
+ if (rest < big_distance &&
+ unsafe_interval - rest >= ten_kappa &&
+ (rest + ten_kappa < big_distance ||
+ big_distance - rest > rest + ten_kappa - big_distance)) {
+ return false;
+ }
+
+ // Weeding test.
+ // The safe interval is [too_low + 2 ulp; too_high - 2 ulp]
+ // Since too_low = too_high - unsafe_interval this is equivalent to
+ // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp]
+ // Conceptually we have: rest ~= too_high - buffer
+ return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit);
+}
+
+
+// Rounds the buffer upwards if the result is closer to v by possibly adding
+// 1 to the buffer. If the precision of the calculation is not sufficient to
+// round correctly, return false.
+// The rounding might shift the whole buffer in which case the kappa is
+// adjusted. For example "99", kappa = 3 might become "10", kappa = 4.
+//
+// If 2*rest > ten_kappa then the buffer needs to be round up.
+// rest can have an error of +/- 1 unit. This function accounts for the
+// imprecision and returns false, if the rounding direction cannot be
+// unambiguously determined.
+//
+// Precondition: rest < ten_kappa.
+static bool RoundWeedCounted(Vector<char> buffer,
+ int length,
+ uint64_t rest,
+ uint64_t ten_kappa,
+ uint64_t unit,
+ int* kappa) {
+ ASSERT(rest < ten_kappa);
+ // The following tests are done in a specific order to avoid overflows. They
+ // will work correctly with any uint64 values of rest < ten_kappa and unit.
+ //
+ // If the unit is too big, then we don't know which way to round. For example
+ // a unit of 50 means that the real number lies within rest +/- 50. If
+ // 10^kappa == 40 then there is no way to tell which way to round.
+ if (unit >= ten_kappa) return false;
+ // Even if unit is just half the size of 10^kappa we are already completely
+ // lost. (And after the previous test we know that the expression will not
+ // over/underflow.)
+ if (ten_kappa - unit <= unit) return false;
+ // If 2 * (rest + unit) <= 10^kappa we can safely round down.
+ if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) {
+ return true;
+ }
+ // If 2 * (rest - unit) >= 10^kappa, then we can safely round up.
+ if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) {
+ // Increment the last digit recursively until we find a non '9' digit.
+ buffer[length - 1]++;
+ for (int i = length - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the
+ // exception of the first digit all digits are now '0'. Simply switch the
+ // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and
+ // the power (the kappa) is increased.
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*kappa) += 1;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+static const uint32_t kTen4 = 10000;
+static const uint32_t kTen5 = 100000;
+static const uint32_t kTen6 = 1000000;
+static const uint32_t kTen7 = 10000000;
+static const uint32_t kTen8 = 100000000;
+static const uint32_t kTen9 = 1000000000;
+
+// Returns the biggest power of ten that is less than or equal than the given
+// number. We furthermore receive the maximum number of bits 'number' has.
+// If number_bits == 0 then 0^-1 is returned
+// The number of bits must be <= 32.
+// Precondition: number < (1 << (number_bits + 1)).
+static void BiggestPowerTen(uint32_t number,
+ int number_bits,
+ uint32_t* power,
+ int* exponent) {
+ switch (number_bits) {
+ case 32:
+ case 31:
+ case 30:
+ if (kTen9 <= number) {
+ *power = kTen9;
+ *exponent = 9;
+ break;
+ } // else fallthrough
+ case 29:
+ case 28:
+ case 27:
+ if (kTen8 <= number) {
+ *power = kTen8;
+ *exponent = 8;
+ break;
+ } // else fallthrough
+ case 26:
+ case 25:
+ case 24:
+ if (kTen7 <= number) {
+ *power = kTen7;
+ *exponent = 7;
+ break;
+ } // else fallthrough
+ case 23:
+ case 22:
+ case 21:
+ case 20:
+ if (kTen6 <= number) {
+ *power = kTen6;
+ *exponent = 6;
+ break;
+ } // else fallthrough
+ case 19:
+ case 18:
+ case 17:
+ if (kTen5 <= number) {
+ *power = kTen5;
+ *exponent = 5;
+ break;
+ } // else fallthrough
+ case 16:
+ case 15:
+ case 14:
+ if (kTen4 <= number) {
+ *power = kTen4;
+ *exponent = 4;
+ break;
+ } // else fallthrough
+ case 13:
+ case 12:
+ case 11:
+ case 10:
+ if (1000 <= number) {
+ *power = 1000;
+ *exponent = 3;
+ break;
+ } // else fallthrough
+ case 9:
+ case 8:
+ case 7:
+ if (100 <= number) {
+ *power = 100;
+ *exponent = 2;
+ break;
+ } // else fallthrough
+ case 6:
+ case 5:
+ case 4:
+ if (10 <= number) {
+ *power = 10;
+ *exponent = 1;
+ break;
+ } // else fallthrough
+ case 3:
+ case 2:
+ case 1:
+ if (1 <= number) {
+ *power = 1;
+ *exponent = 0;
+ break;
+ } // else fallthrough
+ case 0:
+ *power = 0;
+ *exponent = -1;
+ break;
+ default:
+ // Following assignments are here to silence compiler warnings.
+ *power = 0;
+ *exponent = 0;
+ UNREACHABLE();
+ }
+}
+
+
+// Generates the digits of input number w.
+// w is a floating-point number (DiyFp), consisting of a significand and an
+// exponent. Its exponent is bounded by kMinimalTargetExponent and
+// kMaximalTargetExponent.
+// Hence -60 <= w.e() <= -32.
+//
+// Returns false if it fails, in which case the generated digits in the buffer
+// should not be used.
+// Preconditions:
+// * low, w and high are correct up to 1 ulp (unit in the last place). That
+// is, their error must be less than a unit of their last digits.
+// * low.e() == w.e() == high.e()
+// * low < w < high, and taking into account their error: low~ <= high~
+// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
+// Postconditions: returns false if procedure fails.
+// otherwise:
+// * buffer is not null-terminated, but len contains the number of digits.
+// * buffer contains the shortest possible decimal digit-sequence
+// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
+// correct values of low and high (without their error).
+// * if more than one decimal representation gives the minimal number of
+// decimal digits then the one closest to W (where W is the correct value
+// of w) is chosen.
+// Remark: this procedure takes into account the imprecision of its input
+// numbers. If the precision is not enough to guarantee all the postconditions
+// then false is returned. This usually happens rarely (~0.5%).
+//
+// Say, for the sake of example, that
+// w.e() == -48, and w.f() == 0x1234567890abcdef
+// w's value can be computed by w.f() * 2^w.e()
+// We can obtain w's integral digits by simply shifting w.f() by -w.e().
+// -> w's integral part is 0x1234
+// w's fractional part is therefore 0x567890abcdef.
+// Printing w's integral part is easy (simply print 0x1234 in decimal).
+// In order to print its fraction we repeatedly multiply the fraction by 10 and
+// get each digit. Example the first digit after the point would be computed by
+// (0x567890abcdef * 10) >> 48. -> 3
+// The whole thing becomes slightly more complicated because we want to stop
+// once we have enough digits. That is, once the digits inside the buffer
+// represent 'w' we can stop. Everything inside the interval low - high
+// represents w. However we have to pay attention to low, high and w's
+// imprecision.
+static bool DigitGen(DiyFp low,
+ DiyFp w,
+ DiyFp high,
+ Vector<char> buffer,
+ int* length,
+ int* kappa) {
+ ASSERT(low.e() == w.e() && w.e() == high.e());
+ ASSERT(low.f() + 1 <= high.f() - 1);
+ ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+ // low, w and high are imprecise, but by less than one ulp (unit in the last
+ // place).
+ // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that
+ // the new numbers are outside of the interval we want the final
+ // representation to lie in.
+ // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield
+ // numbers that are certain to lie in the interval. We will use this fact
+ // later on.
+ // We will now start by generating the digits within the uncertain
+ // interval. Later we will weed out representations that lie outside the safe
+ // interval and thus _might_ lie outside the correct interval.
+ uint64_t unit = 1;
+ DiyFp too_low = DiyFp(low.f() - unit, low.e());
+ DiyFp too_high = DiyFp(high.f() + unit, high.e());
+ // too_low and too_high are guaranteed to lie outside the interval we want the
+ // generated number in.
+ DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low);
+ // We now cut the input number into two parts: the integral digits and the
+ // fractionals. We will not write any decimal separator though, but adapt
+ // kappa instead.
+ // Reminder: we are currently computing the digits (stored inside the buffer)
+ // such that: too_low < buffer * 10^kappa < too_high
+ // We use too_high for the digit_generation and stop as soon as possible.
+ // If we stop early we effectively round down.
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
+ // Division by one is a shift.
+ uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e());
+ // Modulo by one is an and.
+ uint64_t fractionals = too_high.f() & (one.f() - 1);
+ uint32_t divisor;
+ int divisor_exponent;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
+ &divisor, &divisor_exponent);
+ *kappa = divisor_exponent + 1;
+ *length = 0;
+ // Loop invariant: buffer = too_high / 10^kappa (integer division)
+ // The invariant holds for the first iteration: kappa has been initialized
+ // with the divisor exponent + 1. And the divisor is the biggest power of ten
+ // that is smaller than integrals.
+ while (*kappa > 0) {
+ int digit = integrals / divisor;
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ integrals %= divisor;
+ (*kappa)--;
+ // Note that kappa now equals the exponent of the divisor and that the
+ // invariant thus holds again.
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
+ // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
+ // Reminder: unsafe_interval.e() == one.e()
+ if (rest < unsafe_interval.f()) {
+ // Rounding down (by not emitting the remaining digits) yields a number
+ // that lies within the unsafe interval.
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(),
+ unsafe_interval.f(), rest,
+ static_cast<uint64_t>(divisor) << -one.e(), unit);
+ }
+ divisor /= 10;
+ }
+
+ // The integrals have been generated. We are at the point of the decimal
+ // separator. In the following loop we simply multiply the remaining digits by
+ // 10 and divide by one. We just need to pay attention to multiply associated
+ // data (like the interval or 'unit'), too.
+ // Note that the multiplication by 10 does not overflow, because w.e >= -60
+ // and thus one.e >= -60.
+ ASSERT(one.e() >= -60);
+ ASSERT(fractionals < one.f());
+ ASSERT(V8_2PART_UINT64_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
+ while (true) {
+ fractionals *= 10;
+ unit *= 10;
+ unsafe_interval.set_f(unsafe_interval.f() * 10);
+ // Integer division by one.
+ int digit = static_cast<int>(fractionals >> -one.e());
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ fractionals &= one.f() - 1; // Modulo by one.
+ (*kappa)--;
+ if (fractionals < unsafe_interval.f()) {
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit,
+ unsafe_interval.f(), fractionals, one.f(), unit);
+ }
+ }
+}
+
+
+
+// Generates (at most) requested_digits of input number w.
+// w is a floating-point number (DiyFp), consisting of a significand and an
+// exponent. Its exponent is bounded by kMinimalTargetExponent and
+// kMaximalTargetExponent.
+// Hence -60 <= w.e() <= -32.
+//
+// Returns false if it fails, in which case the generated digits in the buffer
+// should not be used.
+// Preconditions:
+// * w is correct up to 1 ulp (unit in the last place). That
+// is, its error must be strictly less than a unit of its last digit.
+// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
+//
+// Postconditions: returns false if procedure fails.
+// otherwise:
+// * buffer is not null-terminated, but length contains the number of
+// digits.
+// * the representation in buffer is the most precise representation of
+// requested_digits digits.
+// * buffer contains at most requested_digits digits of w. If there are less
+// than requested_digits digits then some trailing '0's have been removed.
+// * kappa is such that
+// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2.
+//
+// Remark: This procedure takes into account the imprecision of its input
+// numbers. If the precision is not enough to guarantee all the postconditions
+// then false is returned. This usually happens rarely, but the failure-rate
+// increases with higher requested_digits.
+static bool DigitGenCounted(DiyFp w,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* kappa) {
+ ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+ ASSERT(kMinimalTargetExponent >= -60);
+ ASSERT(kMaximalTargetExponent <= -32);
+ // w is assumed to have an error less than 1 unit. Whenever w is scaled we
+ // also scale its error.
+ uint64_t w_error = 1;
+ // We cut the input number into two parts: the integral digits and the
+ // fractional digits. We don't emit any decimal separator, but adapt kappa
+ // instead. Example: instead of writing "1.2" we put "12" into the buffer and
+ // increase kappa by 1.
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
+ // Division by one is a shift.
+ uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e());
+ // Modulo by one is an and.
+ uint64_t fractionals = w.f() & (one.f() - 1);
+ uint32_t divisor;
+ int divisor_exponent;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
+ &divisor, &divisor_exponent);
+ *kappa = divisor_exponent + 1;
+ *length = 0;
+
+ // Loop invariant: buffer = w / 10^kappa (integer division)
+ // The invariant holds for the first iteration: kappa has been initialized
+ // with the divisor exponent + 1. And the divisor is the biggest power of ten
+ // that is smaller than 'integrals'.
+ while (*kappa > 0) {
+ int digit = integrals / divisor;
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ requested_digits--;
+ integrals %= divisor;
+ (*kappa)--;
+ // Note that kappa now equals the exponent of the divisor and that the
+ // invariant thus holds again.
+ if (requested_digits == 0) break;
+ divisor /= 10;
+ }
+
+ if (requested_digits == 0) {
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
+ return RoundWeedCounted(buffer, *length, rest,
+ static_cast<uint64_t>(divisor) << -one.e(), w_error,
+ kappa);
+ }
+
+ // The integrals have been generated. We are at the point of the decimal
+ // separator. In the following loop we simply multiply the remaining digits by
+ // 10 and divide by one. We just need to pay attention to multiply associated
+ // data (the 'unit'), too.
+ // Note that the multiplication by 10 does not overflow, because w.e >= -60
+ // and thus one.e >= -60.
+ ASSERT(one.e() >= -60);
+ ASSERT(fractionals < one.f());
+ ASSERT(V8_2PART_UINT64_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
+ while (requested_digits > 0 && fractionals > w_error) {
+ fractionals *= 10;
+ w_error *= 10;
+ // Integer division by one.
+ int digit = static_cast<int>(fractionals >> -one.e());
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ requested_digits--;
+ fractionals &= one.f() - 1; // Modulo by one.
+ (*kappa)--;
+ }
+ if (requested_digits != 0) return false;
+ return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error,
+ kappa);
+}
+
+
+// Provides a decimal representation of v.
+// Returns true if it succeeds, otherwise the result cannot be trusted.
+// There will be *length digits inside the buffer (not null-terminated).
+// If the function returns true then
+// v == (double) (buffer * 10^decimal_exponent).
+// The digits in the buffer are the shortest representation possible: no
+// 0.09999999999999999 instead of 0.1. The shorter representation will even be
+// chosen even if the longer one would be closer to v.
+// The last digit will be closest to the actual v. That is, even if several
+// digits might correctly yield 'v' when read again, the closest will be
+// computed.
+static bool Grisu3(double v,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_exponent) {
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ // boundary_minus and boundary_plus are the boundaries between v and its
+ // closest floating-point neighbors. Any number strictly between
+ // boundary_minus and boundary_plus will round to v when convert to a double.
+ // Grisu3 will never output representations that lie exactly on a boundary.
+ DiyFp boundary_minus, boundary_plus;
+ Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus);
+ ASSERT(boundary_plus.e() == w.e());
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent,
+ ten_mk_maximal_binary_exponent,
+ &ten_mk, &mk);
+ ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize) &&
+ (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize));
+ // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
+ // 64 bit significand and ten_mk is thus only precise up to 64 bits.
+
+ // The DiyFp::Times procedure rounds its result, and ten_mk is approximated
+ // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
+ // off by a small amount.
+ // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
+ // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
+ // (f-1) * 2^e < w*10^k < (f+1) * 2^e
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+ ASSERT(scaled_w.e() ==
+ boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize);
+ // In theory it would be possible to avoid some recomputations by computing
+ // the difference between w and boundary_minus/plus (a power of 2) and to
+ // compute scaled_boundary_minus/plus by subtracting/adding from
+ // scaled_w. However the code becomes much less readable and the speed
+ // enhancements are not terriffic.
+ DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk);
+ DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk);
+
+ // DigitGen will generate the digits of scaled_w. Therefore we have
+ // v == (double) (scaled_w * 10^-mk).
+ // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
+ // integer than it will be updated. For instance if scaled_w == 1.23 then
+ // the buffer will be filled with "123" und the decimal_exponent will be
+ // decreased by 2.
+ int kappa;
+ bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+
+// The "counted" version of grisu3 (see above) only generates requested_digits
+// number of digits. This version does not generate the shortest representation,
+// and with enough requested digits 0.1 will at some point print as 0.9999999...
+// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and
+// therefore the rounding strategy for halfway cases is irrelevant.
+static bool Grisu3Counted(double v,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_exponent) {
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent,
+ ten_mk_maximal_binary_exponent,
+ &ten_mk, &mk);
+ ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize) &&
+ (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize));
+ // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
+ // 64 bit significand and ten_mk is thus only precise up to 64 bits.
+
+ // The DiyFp::Times procedure rounds its result, and ten_mk is approximated
+ // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
+ // off by a small amount.
+ // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
+ // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
+ // (f-1) * 2^e < w*10^k < (f+1) * 2^e
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+
+ // We now have (double) (scaled_w * 10^-mk).
+ // DigitGen will generate the first requested_digits digits of scaled_w and
+ // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It
+ // will not always be exactly the same since DigitGenCounted only produces a
+ // limited number of digits.)
+ int kappa;
+ bool result = DigitGenCounted(scaled_w, requested_digits,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+
+bool FastDtoa(double v,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ ASSERT(v > 0);
+ ASSERT(!Double(v).IsSpecial());
+
+ bool result = false;
+ int decimal_exponent = 0;
+ switch (mode) {
+ case FAST_DTOA_SHORTEST:
+ result = Grisu3(v, buffer, length, &decimal_exponent);
+ break;
+ case FAST_DTOA_PRECISION:
+ result = Grisu3Counted(v, requested_digits,
+ buffer, length, &decimal_exponent);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (result) {
+ *decimal_point = *length + decimal_exponent;
+ buffer[*length] = '\0';
+ }
+ return result;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/fast-dtoa.h b/chromium/v8/src/fast-dtoa.h
new file mode 100644
index 00000000000..ef285579341
--- /dev/null
+++ b/chromium/v8/src/fast-dtoa.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_FAST_DTOA_H_
+#define V8_FAST_DTOA_H_
+
+namespace v8 {
+namespace internal {
+
+enum FastDtoaMode {
+ // Computes the shortest representation of the given input. The returned
+ // result will be the most accurate number of this length. Longer
+ // representations might be more accurate.
+ FAST_DTOA_SHORTEST,
+ // Computes a representation where the precision (number of digits) is
+ // given as input. The precision is independent of the decimal point.
+ FAST_DTOA_PRECISION
+};
+
+// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not
+// include the terminating '\0' character.
+const int kFastDtoaMaximalLength = 17;
+
+// Provides a decimal representation of v.
+// The result should be interpreted as buffer * 10^(point - length).
+//
+// Precondition:
+// * v must be a strictly positive finite double.
+//
+// Returns true if it succeeds, otherwise the result can not be trusted.
+// There will be *length digits inside the buffer followed by a null terminator.
+// If the function returns true and mode equals
+// - FAST_DTOA_SHORTEST, then
+// the parameter requested_digits is ignored.
+// The result satisfies
+// v == (double) (buffer * 10^(point - length)).
+// The digits in the buffer are the shortest representation possible. E.g.
+// if 0.099999999999 and 0.1 represent the same double then "1" is returned
+// with point = 0.
+// The last digit will be closest to the actual v. That is, even if several
+// digits might correctly yield 'v' when read again, the buffer will contain
+// the one closest to v.
+// - FAST_DTOA_PRECISION, then
+// the buffer contains requested_digits digits.
+// the difference v - (buffer * 10^(point-length)) is closest to zero for
+// all possible representations of requested_digits digits.
+// If there are two values that are equally close, then FastDtoa returns
+// false.
+// For both modes the buffer must be large enough to hold the result.
+bool FastDtoa(double d,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point);
+
+} } // namespace v8::internal
+
+#endif // V8_FAST_DTOA_H_
diff --git a/chromium/v8/src/fixed-dtoa.cc b/chromium/v8/src/fixed-dtoa.cc
new file mode 100644
index 00000000000..fd90eca901c
--- /dev/null
+++ b/chromium/v8/src/fixed-dtoa.cc
@@ -0,0 +1,407 @@
+// 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 <cmath>
+
+#include "../include/v8stdint.h"
+#include "checks.h"
+#include "utils.h"
+
+#include "double.h"
+#include "fixed-dtoa.h"
+
+namespace v8 {
+namespace internal {
+
+// Represents a 128bit type. This class should be replaced by a native type on
+// platforms that support 128bit integers.
+class UInt128 {
+ public:
+ UInt128() : high_bits_(0), low_bits_(0) { }
+ UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { }
+
+ void Multiply(uint32_t multiplicand) {
+ uint64_t accumulator;
+
+ accumulator = (low_bits_ & kMask32) * multiplicand;
+ uint32_t part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (low_bits_ >> 32) * multiplicand;
+ low_bits_ = (accumulator << 32) + part;
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
+ part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ >> 32) * multiplicand;
+ high_bits_ = (accumulator << 32) + part;
+ ASSERT((accumulator >> 32) == 0);
+ }
+
+ void Shift(int shift_amount) {
+ ASSERT(-64 <= shift_amount && shift_amount <= 64);
+ if (shift_amount == 0) {
+ return;
+ } else if (shift_amount == -64) {
+ high_bits_ = low_bits_;
+ low_bits_ = 0;
+ } else if (shift_amount == 64) {
+ low_bits_ = high_bits_;
+ high_bits_ = 0;
+ } else if (shift_amount <= 0) {
+ high_bits_ <<= -shift_amount;
+ high_bits_ += low_bits_ >> (64 + shift_amount);
+ low_bits_ <<= -shift_amount;
+ } else {
+ low_bits_ >>= shift_amount;
+ low_bits_ += high_bits_ << (64 - shift_amount);
+ high_bits_ >>= shift_amount;
+ }
+ }
+
+ // Modifies *this to *this MOD (2^power).
+ // Returns *this DIV (2^power).
+ int DivModPowerOf2(int power) {
+ if (power >= 64) {
+ int result = static_cast<int>(high_bits_ >> (power - 64));
+ high_bits_ -= static_cast<uint64_t>(result) << (power - 64);
+ return result;
+ } else {
+ uint64_t part_low = low_bits_ >> power;
+ uint64_t part_high = high_bits_ << (64 - power);
+ int result = static_cast<int>(part_low + part_high);
+ high_bits_ = 0;
+ low_bits_ -= part_low << power;
+ return result;
+ }
+ }
+
+ bool IsZero() const {
+ return high_bits_ == 0 && low_bits_ == 0;
+ }
+
+ int BitAt(int position) {
+ if (position >= 64) {
+ return static_cast<int>(high_bits_ >> (position - 64)) & 1;
+ } else {
+ return static_cast<int>(low_bits_ >> position) & 1;
+ }
+ }
+
+ private:
+ static const uint64_t kMask32 = 0xFFFFFFFF;
+ // Value == (high_bits_ << 64) + low_bits_
+ uint64_t high_bits_;
+ uint64_t low_bits_;
+};
+
+
+static const int kDoubleSignificandSize = 53; // Includes the hidden bit.
+
+
+static void FillDigits32FixedLength(uint32_t number, int requested_length,
+ Vector<char> buffer, int* length) {
+ for (int i = requested_length - 1; i >= 0; --i) {
+ buffer[(*length) + i] = '0' + number % 10;
+ number /= 10;
+ }
+ *length += requested_length;
+}
+
+
+static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) {
+ int number_length = 0;
+ // We fill the digits in reverse order and exchange them afterwards.
+ while (number != 0) {
+ int digit = number % 10;
+ number /= 10;
+ buffer[(*length) + number_length] = '0' + digit;
+ number_length++;
+ }
+ // Exchange the digits.
+ int i = *length;
+ int j = *length + number_length - 1;
+ while (i < j) {
+ char tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
+ i++;
+ j--;
+ }
+ *length += number_length;
+}
+
+
+static void FillDigits64FixedLength(uint64_t number, int requested_length,
+ Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ FillDigits32FixedLength(part0, 3, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+}
+
+
+static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ if (part0 != 0) {
+ FillDigits32(part0, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else if (part1 != 0) {
+ FillDigits32(part1, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else {
+ FillDigits32(part2, buffer, length);
+ }
+}
+
+
+static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) {
+ // An empty buffer represents 0.
+ if (*length == 0) {
+ buffer[0] = '1';
+ *decimal_point = 1;
+ *length = 1;
+ return;
+ }
+ // Round the last digit until we either have a digit that was not '9' or until
+ // we reached the first digit.
+ buffer[(*length) - 1]++;
+ for (int i = (*length) - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) {
+ return;
+ }
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ // If the first digit is now '0' + 10, we would need to set it to '0' and add
+ // a '1' in front. However we reach the first digit only if all following
+ // digits had been '9' before rounding up. Now all trailing digits are '0' and
+ // we simply switch the first digit to '1' and update the decimal-point
+ // (indicating that the point is now one digit to the right).
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+}
+
+
+// The given fractionals number represents a fixed-point number with binary
+// point at bit (-exponent).
+// Preconditions:
+// -128 <= exponent <= 0.
+// 0 <= fractionals * 2^exponent < 1
+// The buffer holds the result.
+// The function will round its result. During the rounding-process digits not
+// generated by this function might be updated, and the decimal-point variable
+// might be updated. If this function generates the digits 99 and the buffer
+// already contained "199" (thus yielding a buffer of "19999") then a
+// rounding-up will change the contents of the buffer to "20000".
+static void FillFractionals(uint64_t fractionals, int exponent,
+ int fractional_count, Vector<char> buffer,
+ int* length, int* decimal_point) {
+ ASSERT(-128 <= exponent && exponent <= 0);
+ // 'fractionals' is a fixed-point number, with binary point at bit
+ // (-exponent). Inside the function the non-converted remainder of fractionals
+ // is a fixed-point number, with binary point at bit 'point'.
+ if (-exponent <= 64) {
+ // One 64 bit number is sufficient.
+ ASSERT(fractionals >> 56 == 0);
+ int point = -exponent;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals == 0) break;
+ // Instead of multiplying by 10 we multiply by 5 and adjust the point
+ // location. This way the fractionals variable will not overflow.
+ // Invariant at the beginning of the loop: fractionals < 2^point.
+ // Initially we have: point <= 64 and fractionals < 2^56
+ // After each iteration the point is decremented by one.
+ // Note that 5^3 = 125 < 128 = 2^7.
+ // Therefore three iterations of this loop will not overflow fractionals
+ // (even without the subtraction at the end of the loop body). At this
+ // time point will satisfy point <= 61 and therefore fractionals < 2^point
+ // and any further multiplication of fractionals by 5 will not overflow.
+ fractionals *= 5;
+ point--;
+ int digit = static_cast<int>(fractionals >> point);
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ fractionals -= static_cast<uint64_t>(digit) << point;
+ }
+ // If the first bit after the point is set we have to round up.
+ if (((fractionals >> (point - 1)) & 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ } else { // We need 128 bits.
+ ASSERT(64 < -exponent && -exponent <= 128);
+ UInt128 fractionals128 = UInt128(fractionals, 0);
+ fractionals128.Shift(-exponent - 64);
+ int point = 128;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals128.IsZero()) break;
+ // As before: instead of multiplying by 10 we multiply by 5 and adjust the
+ // point location.
+ // This multiplication will not overflow for the same reasons as before.
+ fractionals128.Multiply(5);
+ point--;
+ int digit = fractionals128.DivModPowerOf2(point);
+ buffer[*length] = '0' + digit;
+ (*length)++;
+ }
+ if (fractionals128.BitAt(point - 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ }
+}
+
+
+// Removes leading and trailing zeros.
+// If leading zeros are removed then the decimal point position is adjusted.
+static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) {
+ while (*length > 0 && buffer[(*length) - 1] == '0') {
+ (*length)--;
+ }
+ int first_non_zero = 0;
+ while (first_non_zero < *length && buffer[first_non_zero] == '0') {
+ first_non_zero++;
+ }
+ if (first_non_zero != 0) {
+ for (int i = first_non_zero; i < *length; ++i) {
+ buffer[i - first_non_zero] = buffer[i];
+ }
+ *length -= first_non_zero;
+ *decimal_point -= first_non_zero;
+ }
+}
+
+
+bool FastFixedDtoa(double v,
+ int fractional_count,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ const uint32_t kMaxUInt32 = 0xFFFFFFFF;
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+ // v = significand * 2^exponent (with significand a 53bit integer).
+ // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we
+ // don't know how to compute the representation. 2^73 ~= 9.5*10^21.
+ // If necessary this limit could probably be increased, but we don't need
+ // more.
+ if (exponent > 20) return false;
+ if (fractional_count > 20) return false;
+ *length = 0;
+ // At most kDoubleSignificandSize bits of the significand are non-zero.
+ // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero
+ // bits: 0..11*..0xxx..53*..xx
+ if (exponent + kDoubleSignificandSize > 64) {
+ // The exponent must be > 11.
+ //
+ // We know that v = significand * 2^exponent.
+ // And the exponent > 11.
+ // We simplify the task by dividing v by 10^17.
+ // The quotient delivers the first digits, and the remainder fits into a 64
+ // bit number.
+ // Dividing by 10^17 is equivalent to dividing by 5^17*2^17.
+ const uint64_t kFive17 = V8_2PART_UINT64_C(0xB1, A2BC2EC5); // 5^17
+ uint64_t divisor = kFive17;
+ int divisor_power = 17;
+ uint64_t dividend = significand;
+ uint32_t quotient;
+ uint64_t remainder;
+ // Let v = f * 2^e with f == significand and e == exponent.
+ // Then need q (quotient) and r (remainder) as follows:
+ // v = q * 10^17 + r
+ // f * 2^e = q * 10^17 + r
+ // f * 2^e = q * 5^17 * 2^17 + r
+ // If e > 17 then
+ // f * 2^(e-17) = q * 5^17 + r/2^17
+ // else
+ // f = q * 5^17 * 2^(17-e) + r/2^e
+ if (exponent > divisor_power) {
+ // We only allow exponents of up to 20 and therefore (17 - e) <= 3
+ dividend <<= exponent - divisor_power;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << divisor_power;
+ } else {
+ divisor <<= divisor_power - exponent;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << exponent;
+ }
+ FillDigits32(quotient, buffer, length);
+ FillDigits64FixedLength(remainder, divisor_power, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent >= 0) {
+ // 0 <= exponent <= 11
+ significand <<= exponent;
+ FillDigits64(significand, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent > -kDoubleSignificandSize) {
+ // We have to cut the number.
+ uint64_t integrals = significand >> -exponent;
+ uint64_t fractionals = significand - (integrals << -exponent);
+ if (integrals > kMaxUInt32) {
+ FillDigits64(integrals, buffer, length);
+ } else {
+ FillDigits32(static_cast<uint32_t>(integrals), buffer, length);
+ }
+ *decimal_point = *length;
+ FillFractionals(fractionals, exponent, fractional_count,
+ buffer, length, decimal_point);
+ } else if (exponent < -128) {
+ // This configuration (with at most 20 digits) means that all digits must be
+ // 0.
+ ASSERT(fractional_count <= 20);
+ buffer[0] = '\0';
+ *length = 0;
+ *decimal_point = -fractional_count;
+ } else {
+ *decimal_point = 0;
+ FillFractionals(significand, exponent, fractional_count,
+ buffer, length, decimal_point);
+ }
+ TrimZeros(buffer, length, decimal_point);
+ buffer[*length] = '\0';
+ if ((*length) == 0) {
+ // The string is empty and the decimal_point thus has no importance. Mimick
+ // Gay's dtoa and and set it to -fractional_count.
+ *decimal_point = -fractional_count;
+ }
+ return true;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/fixed-dtoa.h b/chromium/v8/src/fixed-dtoa.h
new file mode 100644
index 00000000000..93f826fe841
--- /dev/null
+++ b/chromium/v8/src/fixed-dtoa.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef V8_FIXED_DTOA_H_
+#define V8_FIXED_DTOA_H_
+
+namespace v8 {
+namespace internal {
+
+// Produces digits necessary to print a given number with
+// 'fractional_count' digits after the decimal point.
+// The buffer must be big enough to hold the result plus one terminating null
+// character.
+//
+// The produced digits might be too short in which case the caller has to fill
+// the gaps with '0's.
+// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and
+// decimal_point = -2.
+// Halfway cases are rounded towards +/-Infinity (away from 0). The call
+// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0.
+// The returned buffer may contain digits that would be truncated from the
+// shortest representation of the input.
+//
+// This method only works for some parameters. If it can't handle the input it
+// returns false. The output is null-terminated when the function succeeds.
+bool FastFixedDtoa(double v, int fractional_count,
+ Vector<char> buffer, int* length, int* decimal_point);
+
+} } // namespace v8::internal
+
+#endif // V8_FIXED_DTOA_H_
diff --git a/chromium/v8/src/flag-definitions.h b/chromium/v8/src/flag-definitions.h
new file mode 100644
index 00000000000..c0ad4a8e17a
--- /dev/null
+++ b/chromium/v8/src/flag-definitions.h
@@ -0,0 +1,822 @@
+// Copyright 2012 the V8 project authors. 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 defines all of the flags. It is separated into different section,
+// for Debug, Release, Logging and Profiling, etc. To add a new flag, find the
+// correct section, and use one of the DEFINE_ macros, without a trailing ';'.
+//
+// This include does not have a guard, because it is a template-style include,
+// which can be included multiple times in different modes. It expects to have
+// a mode defined before it's included. The modes are FLAG_MODE_... below:
+
+// We want to declare the names of the variables for the header file. Normally
+// this will just be an extern declaration, but for a readonly flag we let the
+// compiler make better optimizations by giving it the value.
+#if defined(FLAG_MODE_DECLARE)
+#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
+ 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.
+#elif defined(FLAG_MODE_DEFINE)
+#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,
+// for MODE_META, so there is no impact on the flags interface.
+#elif defined(FLAG_MODE_DEFINE_DEFAULTS)
+#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.
+#elif defined(FLAG_MODE_META)
+#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
+#endif
+
+#ifdef FLAG_MODE_DECLARE
+// Structure used to hold a collection of arguments to the JavaScript code.
+#define JSARGUMENTS_INIT {{}}
+struct JSArguments {
+public:
+ inline int argc() const {
+ return static_cast<int>(storage_[0]);
+ }
+ inline const char** argv() const {
+ return reinterpret_cast<const char**>(storage_[1]);
+ }
+ inline const char*& operator[] (int idx) const {
+ return argv()[idx];
+ }
+ inline JSArguments& operator=(JSArguments args) {
+ set_argc(args.argc());
+ set_argv(args.argv());
+ return *this;
+ }
+ static JSArguments Create(int argc, const char** argv) {
+ JSArguments args;
+ args.set_argc(argc);
+ args.set_argv(argv);
+ return args;
+ }
+private:
+ void set_argc(int argc) {
+ storage_[0] = argc;
+ }
+ void set_argv(const char** argv) {
+ storage_[1] = reinterpret_cast<AtomicWord>(argv);
+ }
+public:
+ // Contains argc and argv. Unfortunately we have to store these two fields
+ // into a single one to avoid making the initialization macro (which would be
+ // "{ 0, NULL }") contain a coma.
+ AtomicWord storage_[2];
+};
+#endif
+
+#if (defined CAN_USE_VFP3_INSTRUCTIONS) || !(defined ARM_TEST)
+# define ENABLE_VFP3_DEFAULT true
+#else
+# define ENABLE_VFP3_DEFAULT false
+#endif
+#if (defined CAN_USE_ARMV7_INSTRUCTIONS) || !(defined ARM_TEST)
+# define ENABLE_ARMV7_DEFAULT true
+#else
+# define ENABLE_ARMV7_DEFAULT false
+#endif
+#if (defined CAN_USE_VFP32DREGS) || !(defined ARM_TEST)
+# define ENABLE_32DREGS_DEFAULT true
+#else
+# define ENABLE_32DREGS_DEFAULT false
+#endif
+
+#define DEFINE_bool(nam, def, cmt) FLAG(BOOL, bool, nam, def, cmt)
+#define DEFINE_int(nam, def, cmt) FLAG(INT, int, nam, def, cmt)
+#define DEFINE_float(nam, def, cmt) FLAG(FLOAT, double, nam, def, cmt)
+#define DEFINE_string(nam, def, cmt) FLAG(STRING, const char*, nam, def, cmt)
+#define DEFINE_args(nam, def, cmt) FLAG(ARGS, JSArguments, nam, def, cmt)
+
+//
+// Flags in all modes.
+//
+#define FLAG FLAG_FULL
+
+// Flags for language modes and experimental language features.
+DEFINE_bool(use_strict, false, "enforce strict mode")
+DEFINE_bool(es5_readonly, true,
+ "activate correct semantics for inheriting readonliness")
+DEFINE_bool(es52_globals, true,
+ "activate new semantics for global var declarations")
+
+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 (implies block scoping)")
+DEFINE_bool(harmony_symbols, false,
+ "enable harmony symbols (a.k.a. private names)")
+DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
+DEFINE_bool(harmony_collections, false,
+ "enable harmony collections (sets, maps, and weak maps)")
+DEFINE_bool(harmony_observation, false,
+ "enable harmony object observation (implies harmony collections")
+DEFINE_bool(harmony_typed_arrays, false,
+ "enable harmony typed arrays")
+DEFINE_bool(harmony_array_buffer, false,
+ "enable harmony array buffer")
+DEFINE_implication(harmony_typed_arrays, harmony_array_buffer)
+DEFINE_bool(harmony_generators, false, "enable harmony generators")
+DEFINE_bool(harmony_iteration, false, "enable harmony iteration (for-of)")
+DEFINE_bool(harmony_numeric_literals, false,
+ "enable harmony numeric literals (0o77, 0b11)")
+DEFINE_bool(harmony_strings, false, "enable harmony string")
+DEFINE_bool(harmony_arrays, false, "enable harmony arrays")
+DEFINE_bool(harmony, false, "enable all harmony features (except typeof)")
+DEFINE_implication(harmony, harmony_scoping)
+DEFINE_implication(harmony, harmony_modules)
+DEFINE_implication(harmony, harmony_symbols)
+DEFINE_implication(harmony, harmony_proxies)
+DEFINE_implication(harmony, harmony_collections)
+DEFINE_implication(harmony, harmony_observation)
+DEFINE_implication(harmony, harmony_generators)
+DEFINE_implication(harmony, harmony_iteration)
+DEFINE_implication(harmony, harmony_numeric_literals)
+DEFINE_implication(harmony, harmony_strings)
+DEFINE_implication(harmony, harmony_arrays)
+DEFINE_implication(harmony_modules, harmony_scoping)
+DEFINE_implication(harmony_observation, harmony_collections)
+// TODO[dslomov] add harmony => harmony_typed_arrays
+
+// Flags for experimental implementation features.
+DEFINE_bool(packed_arrays, true, "optimizes arrays that have no holes")
+DEFINE_bool(smi_only_arrays, true, "tracks arrays with only smi values")
+DEFINE_bool(compiled_keyed_stores, true, "use optimizing compiler to "
+ "generate keyed store stubs")
+DEFINE_bool(clever_optimizations,
+ true,
+ "Optimize object size, Array shift, DOM strings and string +")
+DEFINE_bool(pretenuring, true, "allocate objects in old space")
+// TODO(hpayer): We will remove this flag as soon as we have pretenuring
+// support for specific allocation sites.
+DEFINE_bool(pretenuring_call_new, false, "pretenure call new")
+DEFINE_bool(track_fields, true, "track fields with only smi values")
+DEFINE_bool(track_double_fields, true, "track fields with double values")
+DEFINE_bool(track_heap_object_fields, true, "track fields with heap values")
+DEFINE_bool(track_computed_fields, true, "track computed boilerplate fields")
+DEFINE_implication(track_double_fields, track_fields)
+DEFINE_implication(track_heap_object_fields, track_fields)
+DEFINE_implication(track_computed_fields, track_fields)
+DEFINE_bool(smi_binop, true, "support smi representation in binary operations")
+
+// Flags for data representation optimizations
+DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles")
+DEFINE_bool(string_slices, true, "use string slices")
+
+// Flags for Crankshaft.
+DEFINE_bool(crankshaft, true, "use crankshaft")
+DEFINE_string(hydrogen_filter, "*", "optimization filter")
+DEFINE_bool(use_range, true, "use hydrogen range analysis")
+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(use_escape_analysis, false, "use hydrogen escape analysis")
+DEFINE_bool(use_allocation_folding, true, "use allocation folding")
+DEFINE_int(max_inlining_levels, 5, "maximum number of inlining levels")
+DEFINE_int(max_inlined_source_size, 600,
+ "maximum source size in bytes considered for a single inlining")
+DEFINE_int(max_inlined_nodes, 196,
+ "maximum number of AST nodes considered for a single inlining")
+DEFINE_int(max_inlined_nodes_cumulative, 400,
+ "maximum cumulative number of AST nodes considered for inlining")
+DEFINE_bool(loop_invariant_code_motion, true, "loop invariant code motion")
+DEFINE_bool(fast_math, true, "faster (but maybe less accurate) math functions")
+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_hydrogen_stubs, false, "trace generated hydrogen for stubs")
+DEFINE_string(trace_hydrogen_file, NULL, "trace hydrogen to given file name")
+DEFINE_string(trace_phase, "HLZ", "trace generated IR for specified phases")
+DEFINE_bool(trace_inlining, false, "trace inlining decisions")
+DEFINE_bool(trace_alloc, false, "trace register allocator")
+DEFINE_bool(trace_all_uses, false, "trace all use positions")
+DEFINE_bool(trace_range, false, "trace range analysis")
+DEFINE_bool(trace_gvn, false, "trace global value numbering")
+DEFINE_bool(trace_representation, false, "trace representation types")
+DEFINE_bool(trace_escape_analysis, false, "trace hydrogen escape analysis")
+DEFINE_bool(trace_allocation_folding, false, "trace allocation folding")
+DEFINE_bool(trace_track_allocation_sites, false,
+ "trace the tracking of allocation sites")
+DEFINE_bool(trace_migration, false, "trace object migration")
+DEFINE_bool(trace_generalization, false, "trace map generalization")
+DEFINE_bool(stress_pointer_maps, false, "pointer map for every instruction")
+DEFINE_bool(stress_environments, false, "environment for every instruction")
+DEFINE_int(deopt_every_n_times,
+ 0,
+ "deoptimize every n times a deopt point is passed")
+DEFINE_int(deopt_every_n_garbage_collections,
+ 0,
+ "deoptimize every n garbage collections")
+DEFINE_bool(print_deopt_stress, false, "print number of possible deopt points")
+DEFINE_bool(trap_on_deopt, false, "put a break point before deoptimizing")
+DEFINE_bool(trap_on_stub_deopt, false,
+ "put a break point before deoptimizing a stub")
+DEFINE_bool(deoptimize_uncommon_cases, true, "deoptimize uncommon cases")
+DEFINE_bool(polymorphic_inlining, true, "polymorphic inlining")
+DEFINE_bool(use_osr, true, "use on-stack replacement")
+DEFINE_bool(array_bounds_checks_elimination, true,
+ "perform array bounds checks elimination")
+DEFINE_bool(array_bounds_checks_hoisting, false,
+ "perform array bounds checks hoisting")
+DEFINE_bool(array_index_dehoisting, true,
+ "perform array index dehoisting")
+DEFINE_bool(analyze_environment_liveness, true,
+ "analyze liveness of environment slots and zap dead values")
+DEFINE_bool(dead_code_elimination, true, "use dead code elimination")
+DEFINE_bool(fold_constants, true, "use constant folding")
+DEFINE_bool(trace_dead_code_elimination, false, "trace dead code elimination")
+DEFINE_bool(unreachable_code_elimination, false,
+ "eliminate unreachable code (hidden behind soft deopts)")
+DEFINE_bool(track_allocation_sites, true,
+ "Use allocation site info to reduce transitions")
+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_bool(lookup_sample_by_shared, true,
+ "when picking a function to optimize, watch for shared function "
+ "info, not JSFunction itself")
+DEFINE_bool(cache_optimized_code, true,
+ "cache optimized code for closures")
+DEFINE_bool(flush_optimized_code_cache, true,
+ "flushes the cache of optimized code for closures on every GC")
+DEFINE_bool(inline_construct, true, "inline constructor calls")
+DEFINE_bool(inline_arguments, true, "inline functions with arguments object")
+DEFINE_bool(inline_accessors, true, "inline JavaScript accessors")
+DEFINE_int(loop_weight, 1, "loop weight for representation inference")
+
+DEFINE_bool(optimize_for_in, true,
+ "optimize functions containing for-in loops")
+DEFINE_bool(opt_safe_uint32_operations, true,
+ "allow uint32 values on optimize frames if they are used only in "
+ "safe operations")
+
+DEFINE_bool(parallel_recompilation, true,
+ "optimizing hot functions asynchronously on a separate thread")
+DEFINE_bool(trace_parallel_recompilation, false, "track parallel recompilation")
+DEFINE_int(parallel_recompilation_queue_length, 8,
+ "the length of the parallel compilation queue")
+DEFINE_int(parallel_recompilation_delay, 0,
+ "artificial compilation delay in ms")
+DEFINE_bool(omit_map_checks_for_leaf_maps, true,
+ "do not emit check maps for constant values that have a leaf map, "
+ "deoptimize the optimized code if the layout of the maps changes.")
+
+// Experimental profiler changes.
+DEFINE_bool(experimental_profiler, true, "enable all profiler experiments")
+DEFINE_bool(watch_ic_patching, false, "profiler considers IC stability")
+DEFINE_int(frame_count, 1, "number of stack frames inspected by the profiler")
+DEFINE_bool(self_optimization, false,
+ "primitive functions trigger their own optimization")
+DEFINE_bool(direct_self_opt, false,
+ "call recompile stub directly when self-optimizing")
+DEFINE_bool(retry_self_opt, false, "re-try self-optimization if it failed")
+DEFINE_bool(interrupt_at_exit, false,
+ "insert an interrupt check at function exit")
+DEFINE_bool(weighted_back_edges, false,
+ "weight back edges by jump distance for interrupt triggering")
+ // 0x1700 fits in the immediate field of an ARM instruction.
+DEFINE_int(interrupt_budget, 0x1700,
+ "execution budget before interrupt is triggered")
+DEFINE_int(type_info_threshold, 25,
+ "percentage of ICs that must have type info to allow optimization")
+DEFINE_int(self_opt_count, 130, "call count before self-optimization")
+
+DEFINE_implication(experimental_profiler, watch_ic_patching)
+DEFINE_implication(experimental_profiler, self_optimization)
+// Not implying direct_self_opt here because it seems to be a bad idea.
+DEFINE_implication(experimental_profiler, retry_self_opt)
+DEFINE_implication(experimental_profiler, interrupt_at_exit)
+DEFINE_implication(experimental_profiler, weighted_back_edges)
+
+DEFINE_bool(trace_opt_verbose, false, "extra verbose compilation tracing")
+DEFINE_implication(trace_opt_verbose, trace_opt)
+
+// 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(enable_sse2, true,
+ "enable use of SSE2 instructions if available")
+DEFINE_bool(enable_sse3, true,
+ "enable use of SSE3 instructions if available")
+DEFINE_bool(enable_sse4_1, true,
+ "enable use of SSE4.1 instructions if available")
+DEFINE_bool(enable_cmov, true,
+ "enable use of CMOV instruction if available")
+DEFINE_bool(enable_rdtsc, true,
+ "enable use of RDTSC instruction if available")
+DEFINE_bool(enable_sahf, true,
+ "enable use of SAHF instruction if available (X64 only)")
+DEFINE_bool(enable_vfp3, ENABLE_VFP3_DEFAULT,
+ "enable use of VFP3 instructions if available")
+DEFINE_bool(enable_armv7, ENABLE_ARMV7_DEFAULT,
+ "enable use of ARMv7 instructions if available (ARM only)")
+DEFINE_bool(enable_neon, true,
+ "enable use of NEON instructions if available (ARM only)")
+DEFINE_bool(enable_sudiv, true,
+ "enable use of SDIV and UDIV instructions if available (ARM only)")
+DEFINE_bool(enable_movw_movt, false,
+ "enable loading 32-bit constant by means of movw/movt "
+ "instruction pairs (ARM only)")
+DEFINE_bool(enable_unaligned_accesses, true,
+ "enable unaligned accesses for ARMv7 (ARM only)")
+DEFINE_bool(enable_32dregs, ENABLE_32DREGS_DEFAULT,
+ "enable use of d16-d31 registers on ARM - this requires VFP3")
+DEFINE_bool(enable_vldr_imm, false,
+ "enable use of constant pools for double immediate (ARM only)")
+
+// bootstrapper.cc
+DEFINE_bool(enable_i18n, true, "enable i18n extension")
+DEFINE_string(expose_natives_as, NULL, "expose natives in global object")
+DEFINE_string(expose_debug_as, NULL, "expose debug in global object")
+DEFINE_bool(expose_gc, false, "expose gc extension")
+DEFINE_string(expose_gc_as,
+ NULL,
+ "expose gc extension under the specified name")
+DEFINE_implication(expose_gc_as, expose_gc)
+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
+DEFINE_bool(inline_new, true, "use fast inline allocation")
+
+// checks.cc
+DEFINE_bool(stack_trace_on_abort, true,
+ "print a stack trace if an assertion failure occurs")
+
+// codegen-ia32.cc / codegen-arm.cc
+DEFINE_bool(trace_codegen, false,
+ "print name of functions for which code is generated")
+DEFINE_bool(trace, false, "trace function calls")
+DEFINE_bool(mask_constants_with_cookie,
+ true,
+ "use random jit cookie to mask large constants")
+
+// codegen.cc
+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(always_opt, false, "always try to optimize functions")
+DEFINE_bool(always_osr, false, "always try to OSR functions")
+DEFINE_bool(prepare_always_opt, false, "prepare for turning on always opt")
+DEFINE_bool(trace_deopt, false, "trace optimize function deoptimization")
+DEFINE_bool(trace_stub_failures, false,
+ "trace deoptimization of generated code stubs")
+
+// compiler.cc
+DEFINE_int(min_preparse_length, 1024,
+ "minimum length for automatic enable preparsing")
+DEFINE_bool(always_full_compiler, false,
+ "try to use the dedicated run-once backend for all code")
+DEFINE_int(max_opt_count, 10,
+ "maximum number of optimization attempts before giving up.")
+
+// compilation-cache.cc
+DEFINE_bool(compilation_cache, true, "enable compilation cache")
+
+DEFINE_bool(cache_prototype_transitions, true, "cache prototype transitions")
+
+// debug.cc
+DEFINE_bool(trace_debug_json, false, "trace debugging JSON request/response")
+DEFINE_bool(trace_js_array_abuse, false,
+ "trace out-of-bounds accesses to JS arrays")
+DEFINE_bool(trace_external_array_abuse, false,
+ "trace out-of-bounds-accesses to external arrays")
+DEFINE_bool(trace_array_abuse, false,
+ "trace out-of-bounds accesses to all arrays")
+DEFINE_implication(trace_array_abuse, trace_js_array_abuse)
+DEFINE_implication(trace_array_abuse, trace_external_array_abuse)
+DEFINE_bool(debugger_auto_break, true,
+ "automatically set the debug break flag when debugger commands are "
+ "in the queue")
+DEFINE_bool(enable_liveedit, true, "enable liveedit experimental feature")
+DEFINE_bool(break_on_abort, true, "always cause a debug break before aborting")
+
+// execution.cc
+// Slightly less than 1MB on 64-bit, since Windows' default stack size for
+// the main execution thread is 1MB for both 32 and 64-bit.
+DEFINE_int(stack_size, kPointerSize * 123,
+ "default size of stack region v8 is allowed to use (in kBytes)")
+
+// frames.cc
+DEFINE_int(max_stack_trace_source_length, 300,
+ "maximum length of function source code printed in a stack trace.")
+
+// full-codegen.cc
+DEFINE_bool(always_inline_smi_code, false,
+ "always inline smi code in non-opt code")
+
+// heap.cc
+DEFINE_int(max_new_space_size, 0, "max size of the new generation (in kBytes)")
+DEFINE_int(max_old_space_size, 0, "max size of the old generation (in Mbytes)")
+DEFINE_int(max_executable_size, 0, "max size of executable memory (in Mbytes)")
+DEFINE_bool(gc_global, false, "always perform global GCs")
+DEFINE_int(gc_interval, -1, "garbage collect after <n> allocations")
+DEFINE_bool(trace_gc, false,
+ "print one trace line following each garbage collection")
+DEFINE_bool(trace_gc_nvp, false,
+ "print one detailed trace line in name=value format "
+ "after each garbage collection")
+DEFINE_bool(trace_gc_ignore_scavenger, false,
+ "do not print trace line after scavenger collection")
+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(trace_external_memory, false,
+ "print amount of external allocated memory after each time "
+ "it is adjusted.")
+DEFINE_bool(collect_maps, true,
+ "garbage collect maps from which no objects can be reached")
+DEFINE_bool(weak_embedded_maps_in_optimized_code, true,
+ "make maps embedded in optimized code weak")
+DEFINE_bool(flush_code, true,
+ "flush code that we expect not to use again (during full gc)")
+DEFINE_bool(flush_code_incrementally, true,
+ "flush code that we expect not to use again (incrementally)")
+DEFINE_bool(trace_code_flushing, false, "trace code flushing progress")
+DEFINE_bool(age_code, true,
+ "track un-executed functions to age code and flush only "
+ "old code (required for code flushing)")
+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")
+DEFINE_bool(track_gc_object_stats, false,
+ "track object counts and memory usage")
+DEFINE_bool(parallel_sweeping, true, "enable parallel sweeping")
+DEFINE_bool(concurrent_sweeping, false, "enable concurrent sweeping")
+DEFINE_int(sweeper_threads, 0,
+ "number of parallel and concurrent sweeping threads")
+DEFINE_bool(parallel_marking, false, "enable parallel marking")
+DEFINE_int(marking_threads, 0, "number of parallel marking threads")
+#ifdef VERIFY_HEAP
+DEFINE_bool(verify_heap, false, "verify heap pointers before and after GC")
+#endif
+
+// v8.cc
+DEFINE_bool(use_idle_notification, true,
+ "Use idle notification to reduce memory footprint.")
+// ic.cc
+DEFINE_bool(use_ic, true, "use inline caching")
+
+// macro-assembler-ia32.cc
+DEFINE_bool(native_code_counters, false,
+ "generate extra code for manipulating stats counters")
+
+// 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(incremental_code_compaction, true,
+ "Compact code space on full 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.")
+DEFINE_bool(use_marking_progress_bar, true,
+ "Use a progress bar to scan large objects in increments when "
+ "incremental marking is active.")
+DEFINE_int(random_seed, 0,
+ "Default seed for initializing random generator "
+ "(0, the default, means to use system random).")
+
+// objects.cc
+DEFINE_bool(use_verbose_printer, true, "allows verbose printing")
+
+// parser.cc
+DEFINE_bool(allow_natives_syntax, false, "allow natives syntax")
+DEFINE_bool(trace_parse, false, "trace parsing and preparsing")
+
+// 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 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)")
+
+// isolate.cc
+DEFINE_bool(abort_on_uncaught_exception, false,
+ "abort program (dump core) when an uncaught exception is thrown")
+DEFINE_bool(trace_exception, false,
+ "print stack trace when throwing exceptions")
+DEFINE_bool(preallocate_message_memory, false,
+ "preallocate some memory to build stack traces.")
+DEFINE_bool(randomize_hashes,
+ true,
+ "randomize hashes to avoid predictable hash collisions "
+ "(with snapshots this option cannot override the baked-in seed)")
+DEFINE_int(hash_seed,
+ 0,
+ "Fixed seed to use to hash property keys (0 means random)"
+ "(with snapshots this option cannot override the baked-in seed)")
+
+// v8.cc
+DEFINE_bool(preemption, false,
+ "activate a 100ms timer that switches between V8 threads")
+
+// Regexp
+DEFINE_bool(regexp_optimization, true, "generate optimized regexp code")
+
+// Testing flags test/cctest/test-{flags,api,serialization}.cc
+DEFINE_bool(testing_bool_flag, true, "testing_bool_flag")
+DEFINE_int(testing_int_flag, 13, "testing_int_flag")
+DEFINE_float(testing_float_flag, 2.5, "float-flag")
+DEFINE_string(testing_string_flag, "Hello, world!", "string-flag")
+DEFINE_int(testing_prng_seed, 42, "Seed used for threading test randomness")
+#ifdef WIN32
+DEFINE_string(testing_serialization_file, "C:\\Windows\\Temp\\serdes",
+ "file in which to testing_serialize heap")
+#else
+DEFINE_string(testing_serialization_file, "/tmp/serdes",
+ "file in which to serialize heap")
+#endif
+
+// mksnapshot.cc
+DEFINE_string(extra_code, NULL, "A filename with extra code to be included in"
+ " the snapshot (mksnapshot only)")
+
+//
+// Dev shell flags
+//
+
+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_INIT,
+ "Pass all remaining arguments to the script. Alias for \"--\".")
+
+#if defined(WEBOS__)
+DEFINE_bool(debug_compile_events, false, "Enable debugger compile events")
+DEFINE_bool(debug_script_collected_events, false,
+ "Enable debugger script collected events")
+#else
+DEFINE_bool(debug_compile_events, true, "Enable debugger compile events")
+DEFINE_bool(debug_script_collected_events, true,
+ "Enable debugger script collected events")
+#endif
+
+
+//
+// GDB JIT integration flags.
+//
+
+DEFINE_bool(gdbjit, false, "enable GDBJIT interface (disables compacting GC)")
+DEFINE_bool(gdbjit_full, false, "enable GDBJIT interface for all code objects")
+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
+//
+#undef FLAG
+#ifdef DEBUG
+#define FLAG FLAG_FULL
+#else
+#define FLAG FLAG_READONLY
+#endif
+
+// checks.cc
+DEFINE_bool(enable_slow_asserts, false,
+ "enable asserts that are slow to execute")
+
+// codegen-ia32.cc / codegen-arm.cc
+DEFINE_bool(print_source, false, "pretty print source code")
+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_string(stop_at, "", "function name where to insert a breakpoint")
+
+// compiler.cc
+DEFINE_bool(print_builtin_scopes, false, "print scopes for builtins")
+DEFINE_bool(print_scopes, false, "print scopes")
+
+// contexts.cc
+DEFINE_bool(trace_contexts, false, "trace contexts operations")
+
+// heap.cc
+DEFINE_bool(gc_greedy, false, "perform GC prior to some allocations")
+DEFINE_bool(gc_verbose, false, "print stuff during garbage collection")
+DEFINE_bool(heap_stats, false, "report heap statistics before and after GC")
+DEFINE_bool(code_stats, false, "report code statistics after GC")
+DEFINE_bool(verify_native_context_separation, false,
+ "verify that code holds on to at most one native context after GC")
+DEFINE_bool(print_handles, false, "report handles after GC")
+DEFINE_bool(print_global_handles, false, "report global handles after GC")
+
+// ic.cc
+DEFINE_bool(trace_ic, false, "trace inline cache state transitions")
+
+// interface.cc
+DEFINE_bool(print_interfaces, false, "print interfaces")
+DEFINE_bool(print_interface_details, false, "print interface inference details")
+DEFINE_int(print_interface_depth, 5, "depth for printing interfaces")
+
+// objects.cc
+DEFINE_bool(trace_normalization,
+ false,
+ "prints when objects are turned into dictionaries.")
+
+// runtime.cc
+DEFINE_bool(trace_lazy, false, "trace lazy compilation")
+
+// spaces.cc
+DEFINE_bool(collect_heap_spill_statistics, false,
+ "report heap spill statistics along with heap_stats "
+ "(requires heap_stats)")
+
+DEFINE_bool(trace_isolates, false, "trace isolate state changes")
+
+// Regexp
+DEFINE_bool(regexp_possessive_quantifier,
+ false,
+ "enable possessive quantifier syntax for testing")
+DEFINE_bool(trace_regexp_bytecodes, false, "trace regexp bytecode execution")
+DEFINE_bool(trace_regexp_assembler,
+ false,
+ "trace regexp macro assembler calls.")
+
+//
+// Logging and profiling flags
+//
+#undef FLAG
+#define FLAG FLAG_FULL
+
+// log.cc
+DEFINE_bool(log, false,
+ "Minimal logging (no API, code, GC, suspect, or handles samples).")
+DEFINE_bool(log_all, false, "Log all events to the log file.")
+DEFINE_bool(log_runtime, false, "Activate runtime system %Log call.")
+DEFINE_bool(log_api, false, "Log API events to the log file.")
+DEFINE_bool(log_code, false,
+ "Log code events to the log file without profiling.")
+DEFINE_bool(log_gc, false,
+ "Log heap samples on garbage collection for the hp2ps tool.")
+DEFINE_bool(log_handles, false, "Log global handle events.")
+DEFINE_bool(log_snapshot_positions, false,
+ "log positions of (de)serialized objects in the snapshot.")
+DEFINE_bool(log_suspect, false, "Log suspect operations.")
+DEFINE_bool(prof, false,
+ "Log statistical profiling information (implies --log-code).")
+DEFINE_bool(prof_lazy, false,
+ "Used with --prof, only does sampling and logging"
+ " when profiler is active.")
+DEFINE_bool(prof_browser_mode, true,
+ "Used with --prof, turns on browser-compatible mode for profiling.")
+DEFINE_bool(log_regexp, false, "Log regular expression execution.")
+DEFINE_string(logfile, "v8.log", "Specify the name of the log file.")
+DEFINE_bool(ll_prof, false, "Enable low-level linux profiler.")
+DEFINE_string(gc_fake_mmap, "/tmp/__v8_gc__",
+ "Specify the name of the file for fake gc mmap used in ll_prof")
+DEFINE_bool(log_internal_timer_events, false, "Time internal events.")
+DEFINE_bool(log_timer_events, false,
+ "Time events including external callbacks.")
+DEFINE_implication(log_timer_events, log_internal_timer_events)
+DEFINE_implication(log_internal_timer_events, prof)
+
+//
+// Disassembler only flags
+//
+#undef FLAG
+#ifdef ENABLE_DISASSEMBLER
+#define FLAG FLAG_FULL
+#else
+#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")
+DEFINE_bool(test_secondary_stub_cache,
+ false,
+ "test secondary stub cache by disabling the primary one")
+
+DEFINE_bool(test_primary_stub_cache,
+ false,
+ "test primary stub cache by disabling the secondary one")
+
+// codegen-ia32.cc / codegen-arm.cc
+DEFINE_bool(print_code, false, "print generated code")
+DEFINE_bool(print_opt_code, false, "print optimized code")
+DEFINE_bool(print_unopt_code, false, "print unoptimized code before "
+ "printing optimized code based on it")
+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
+#undef FLAG
+
+#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/chromium/v8/src/flags.cc b/chromium/v8/src/flags.cc
new file mode 100644
index 00000000000..855e20712c6
--- /dev/null
+++ b/chromium/v8/src/flags.cc
@@ -0,0 +1,557 @@
+// 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.
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "platform.h"
+#include "smart-pointers.h"
+#include "string-stream.h"
+
+#if V8_TARGET_ARCH_ARM
+#include "arm/assembler-arm-inl.h"
+#endif
+
+namespace v8 {
+namespace internal {
+
+// Define all of our flags.
+#define FLAG_MODE_DEFINE
+#include "flag-definitions.h"
+
+// Define all of our flags default values.
+#define FLAG_MODE_DEFINE_DEFAULTS
+#include "flag-definitions.h"
+
+namespace {
+
+// This structure represents a single entry in the flag system, with a pointer
+// to the actual flag, default value, comment, etc. This is designed to be POD
+// initialized as to avoid requiring static constructors.
+struct Flag {
+ enum FlagType { TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_ARGS };
+
+ FlagType type_; // What type of flag, bool, int, or string.
+ const char* name_; // Name of the flag, ex "my_flag".
+ void* valptr_; // Pointer to the global flag variable.
+ const void* defptr_; // Pointer to the default value.
+ const char* cmt_; // A comment about the flags purpose.
+ bool owns_ptr_; // Does the flag own its string value?
+
+ FlagType type() const { return type_; }
+
+ const char* name() const { return name_; }
+
+ const char* comment() const { return cmt_; }
+
+ bool* bool_variable() const {
+ ASSERT(type_ == TYPE_BOOL);
+ return reinterpret_cast<bool*>(valptr_);
+ }
+
+ int* int_variable() const {
+ ASSERT(type_ == TYPE_INT);
+ return reinterpret_cast<int*>(valptr_);
+ }
+
+ double* float_variable() const {
+ ASSERT(type_ == TYPE_FLOAT);
+ return reinterpret_cast<double*>(valptr_);
+ }
+
+ const char* string_value() const {
+ ASSERT(type_ == TYPE_STRING);
+ return *reinterpret_cast<const char**>(valptr_);
+ }
+
+ void set_string_value(const char* value, bool owns_ptr) {
+ ASSERT(type_ == TYPE_STRING);
+ const char** ptr = reinterpret_cast<const char**>(valptr_);
+ if (owns_ptr_ && *ptr != NULL) DeleteArray(*ptr);
+ *ptr = value;
+ owns_ptr_ = owns_ptr;
+ }
+
+ JSArguments* args_variable() const {
+ ASSERT(type_ == TYPE_ARGS);
+ return reinterpret_cast<JSArguments*>(valptr_);
+ }
+
+ bool bool_default() const {
+ ASSERT(type_ == TYPE_BOOL);
+ return *reinterpret_cast<const bool*>(defptr_);
+ }
+
+ int int_default() const {
+ ASSERT(type_ == TYPE_INT);
+ return *reinterpret_cast<const int*>(defptr_);
+ }
+
+ double float_default() const {
+ ASSERT(type_ == TYPE_FLOAT);
+ return *reinterpret_cast<const double*>(defptr_);
+ }
+
+ const char* string_default() const {
+ ASSERT(type_ == TYPE_STRING);
+ return *reinterpret_cast<const char* const *>(defptr_);
+ }
+
+ JSArguments args_default() const {
+ ASSERT(type_ == TYPE_ARGS);
+ return *reinterpret_cast<const JSArguments*>(defptr_);
+ }
+
+ // Compare this flag's current value against the default.
+ bool IsDefault() const {
+ switch (type_) {
+ case TYPE_BOOL:
+ return *bool_variable() == bool_default();
+ case TYPE_INT:
+ return *int_variable() == int_default();
+ case TYPE_FLOAT:
+ return *float_variable() == float_default();
+ case TYPE_STRING: {
+ const char* str1 = string_value();
+ const char* str2 = string_default();
+ if (str2 == NULL) return str1 == NULL;
+ if (str1 == NULL) return str2 == NULL;
+ return strcmp(str1, str2) == 0;
+ }
+ case TYPE_ARGS:
+ return args_variable()->argc() == 0;
+ }
+ UNREACHABLE();
+ return true;
+ }
+
+ // Set a flag back to it's default value.
+ void Reset() {
+ switch (type_) {
+ case TYPE_BOOL:
+ *bool_variable() = bool_default();
+ break;
+ case TYPE_INT:
+ *int_variable() = int_default();
+ break;
+ case TYPE_FLOAT:
+ *float_variable() = float_default();
+ break;
+ case TYPE_STRING:
+ set_string_value(string_default(), false);
+ break;
+ case TYPE_ARGS:
+ *args_variable() = args_default();
+ break;
+ }
+ }
+};
+
+Flag flags[] = {
+#define FLAG_MODE_META
+#include "flag-definitions.h"
+};
+
+const size_t num_flags = sizeof(flags) / sizeof(*flags);
+
+} // namespace
+
+
+static const char* Type2String(Flag::FlagType type) {
+ switch (type) {
+ case Flag::TYPE_BOOL: return "bool";
+ case Flag::TYPE_INT: return "int";
+ case Flag::TYPE_FLOAT: return "float";
+ case Flag::TYPE_STRING: return "string";
+ case Flag::TYPE_ARGS: return "arguments";
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+static SmartArrayPointer<const char> ToString(Flag* flag) {
+ HeapStringAllocator string_allocator;
+ StringStream buffer(&string_allocator);
+ switch (flag->type()) {
+ case Flag::TYPE_BOOL:
+ buffer.Add("%s", (*flag->bool_variable() ? "true" : "false"));
+ break;
+ case Flag::TYPE_INT:
+ buffer.Add("%d", *flag->int_variable());
+ break;
+ case Flag::TYPE_FLOAT:
+ buffer.Add("%f", FmtElm(*flag->float_variable()));
+ break;
+ case Flag::TYPE_STRING: {
+ const char* str = flag->string_value();
+ buffer.Add("%s", str ? str : "NULL");
+ break;
+ }
+ case Flag::TYPE_ARGS: {
+ JSArguments args = *flag->args_variable();
+ if (args.argc() > 0) {
+ buffer.Add("%s", args[0]);
+ for (int i = 1; i < args.argc(); i++) {
+ buffer.Add(" %s", args[i]);
+ }
+ }
+ break;
+ }
+ }
+ return buffer.ToCString();
+}
+
+
+// static
+List<const char*>* FlagList::argv() {
+ List<const char*>* args = new List<const char*>(8);
+ Flag* args_flag = NULL;
+ for (size_t i = 0; i < num_flags; ++i) {
+ Flag* f = &flags[i];
+ if (!f->IsDefault()) {
+ if (f->type() == Flag::TYPE_ARGS) {
+ ASSERT(args_flag == NULL);
+ args_flag = f; // Must be last in arguments.
+ continue;
+ }
+ HeapStringAllocator string_allocator;
+ StringStream buffer(&string_allocator);
+ if (f->type() != Flag::TYPE_BOOL || *(f->bool_variable())) {
+ buffer.Add("--%s", f->name());
+ } else {
+ buffer.Add("--no%s", f->name());
+ }
+ args->Add(buffer.ToCString().Detach());
+ if (f->type() != Flag::TYPE_BOOL) {
+ args->Add(ToString(f).Detach());
+ }
+ }
+ }
+ if (args_flag != NULL) {
+ HeapStringAllocator string_allocator;
+ StringStream buffer(&string_allocator);
+ buffer.Add("--%s", args_flag->name());
+ args->Add(buffer.ToCString().Detach());
+ JSArguments jsargs = *args_flag->args_variable();
+ for (int j = 0; j < jsargs.argc(); j++) {
+ args->Add(StrDup(jsargs[j]));
+ }
+ }
+ return args;
+}
+
+
+// Helper function to parse flags: Takes an argument arg and splits it into
+// a flag name and flag value (or NULL if they are missing). is_bool is set
+// if the arg started with "-no" or "--no". The buffer may be used to NUL-
+// terminate the name, it must be large enough to hold any possible name.
+static void SplitArgument(const char* arg,
+ char* buffer,
+ int buffer_size,
+ const char** name,
+ const char** value,
+ bool* is_bool) {
+ *name = NULL;
+ *value = NULL;
+ *is_bool = false;
+
+ if (arg != NULL && *arg == '-') {
+ // find the begin of the flag name
+ arg++; // remove 1st '-'
+ if (*arg == '-') {
+ arg++; // remove 2nd '-'
+ if (arg[0] == '\0') {
+ const char* kJSArgumentsFlagName = "js_arguments";
+ *name = kJSArgumentsFlagName;
+ return;
+ }
+ }
+ if (arg[0] == 'n' && arg[1] == 'o') {
+ arg += 2; // remove "no"
+ *is_bool = true;
+ }
+ *name = arg;
+
+ // find the end of the flag name
+ while (*arg != '\0' && *arg != '=')
+ arg++;
+
+ // get the value if any
+ if (*arg == '=') {
+ // make a copy so we can NUL-terminate flag name
+ size_t n = arg - *name;
+ CHECK(n < static_cast<size_t>(buffer_size)); // buffer is too small
+ OS::MemCopy(buffer, *name, n);
+ buffer[n] = '\0';
+ *name = buffer;
+ // get the value
+ *value = arg + 1;
+ }
+ }
+}
+
+
+inline char NormalizeChar(char ch) {
+ return ch == '_' ? '-' : ch;
+}
+
+
+static bool EqualNames(const char* a, const char* b) {
+ for (int i = 0; NormalizeChar(a[i]) == NormalizeChar(b[i]); i++) {
+ if (a[i] == '\0') {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+static Flag* FindFlag(const char* name) {
+ for (size_t i = 0; i < num_flags; ++i) {
+ if (EqualNames(name, flags[i].name()))
+ return &flags[i];
+ }
+ return NULL;
+}
+
+
+// static
+int FlagList::SetFlagsFromCommandLine(int* argc,
+ char** argv,
+ bool remove_flags) {
+ int return_code = 0;
+ // parse arguments
+ for (int i = 1; i < *argc;) {
+ int j = i; // j > 0
+ const char* arg = argv[i++];
+
+ // split arg into flag components
+ char buffer[1*KB];
+ const char* name;
+ const char* value;
+ bool is_bool;
+ SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);
+
+ if (name != NULL) {
+ // lookup the flag
+ Flag* flag = FindFlag(name);
+ if (flag == NULL) {
+ if (remove_flags) {
+ // We don't recognize this flag but since we're removing
+ // the flags we recognize we assume that the remaining flags
+ // will be processed somewhere else so this flag might make
+ // sense there.
+ continue;
+ } else {
+ PrintF(stderr, "Error: unrecognized flag %s\n"
+ "Try --help for options\n", arg);
+ return_code = j;
+ break;
+ }
+ }
+
+ // if we still need a flag value, use the next argument if available
+ if (flag->type() != Flag::TYPE_BOOL &&
+ flag->type() != Flag::TYPE_ARGS &&
+ value == NULL) {
+ if (i < *argc) {
+ value = argv[i++];
+ } else {
+ PrintF(stderr, "Error: missing value for flag %s of type %s\n"
+ "Try --help for options\n",
+ arg, Type2String(flag->type()));
+ return_code = j;
+ break;
+ }
+ }
+
+ // set the flag
+ char* endp = const_cast<char*>(""); // *endp is only read
+ switch (flag->type()) {
+ case Flag::TYPE_BOOL:
+ *flag->bool_variable() = !is_bool;
+ break;
+ case Flag::TYPE_INT:
+ *flag->int_variable() = strtol(value, &endp, 10); // NOLINT
+ break;
+ case Flag::TYPE_FLOAT:
+ *flag->float_variable() = strtod(value, &endp);
+ break;
+ case Flag::TYPE_STRING:
+ flag->set_string_value(value ? StrDup(value) : NULL, true);
+ break;
+ case Flag::TYPE_ARGS: {
+ int start_pos = (value == NULL) ? i : i - 1;
+ int js_argc = *argc - start_pos;
+ const char** js_argv = NewArray<const char*>(js_argc);
+ if (value != NULL) {
+ js_argv[0] = StrDup(value);
+ }
+ for (int k = i; k < *argc; k++) {
+ js_argv[k - start_pos] = StrDup(argv[k]);
+ }
+ *flag->args_variable() = JSArguments::Create(js_argc, js_argv);
+ i = *argc; // Consume all arguments
+ break;
+ }
+ }
+
+ // handle errors
+ if ((flag->type() == Flag::TYPE_BOOL && value != NULL) ||
+ (flag->type() != Flag::TYPE_BOOL && is_bool) ||
+ *endp != '\0') {
+ PrintF(stderr, "Error: illegal value for flag %s of type %s\n"
+ "Try --help for options\n",
+ arg, Type2String(flag->type()));
+ return_code = j;
+ break;
+ }
+
+ // remove the flag & value from the command
+ if (remove_flags) {
+ while (j < i) {
+ argv[j++] = NULL;
+ }
+ }
+ }
+ }
+
+ // shrink the argument list
+ if (remove_flags) {
+ int j = 1;
+ for (int i = 1; i < *argc; i++) {
+ if (argv[i] != NULL)
+ argv[j++] = argv[i];
+ }
+ *argc = j;
+ }
+
+ if (FLAG_help) {
+ PrintHelp();
+ exit(0);
+ }
+ // parsed all flags successfully
+ return return_code;
+}
+
+
+static char* SkipWhiteSpace(char* p) {
+ while (*p != '\0' && isspace(*p) != 0) p++;
+ return p;
+}
+
+
+static char* SkipBlackSpace(char* p) {
+ while (*p != '\0' && isspace(*p) == 0) p++;
+ return p;
+}
+
+
+// static
+int FlagList::SetFlagsFromString(const char* str, int len) {
+ // make a 0-terminated copy of str
+ ScopedVector<char> copy0(len + 1);
+ OS::MemCopy(copy0.start(), str, len);
+ copy0[len] = '\0';
+
+ // strip leading white space
+ char* copy = SkipWhiteSpace(copy0.start());
+
+ // count the number of 'arguments'
+ int argc = 1; // be compatible with SetFlagsFromCommandLine()
+ for (char* p = copy; *p != '\0'; argc++) {
+ p = SkipBlackSpace(p);
+ p = SkipWhiteSpace(p);
+ }
+
+ // allocate argument array
+ ScopedVector<char*> argv(argc);
+
+ // split the flags string into arguments
+ argc = 1; // be compatible with SetFlagsFromCommandLine()
+ for (char* p = copy; *p != '\0'; argc++) {
+ argv[argc] = p;
+ p = SkipBlackSpace(p);
+ if (*p != '\0') *p++ = '\0'; // 0-terminate argument
+ p = SkipWhiteSpace(p);
+ }
+
+ // set the flags
+ int result = SetFlagsFromCommandLine(&argc, argv.start(), false);
+
+ return result;
+}
+
+
+// static
+void FlagList::ResetAllFlags() {
+ for (size_t i = 0; i < num_flags; ++i) {
+ flags[i].Reset();
+ }
+}
+
+
+// static
+void FlagList::PrintHelp() {
+#if V8_TARGET_ARCH_ARM
+ CpuFeatures::PrintTarget();
+ CpuFeatures::Probe();
+ CpuFeatures::PrintFeatures();
+#endif // V8_TARGET_ARCH_ARM
+
+ printf("Usage:\n");
+ printf(" shell [options] -e string\n");
+ printf(" execute string in V8\n");
+ printf(" shell [options] file1 file2 ... filek\n");
+ printf(" run JavaScript scripts in file1, file2, ..., filek\n");
+ printf(" shell [options]\n");
+ printf(" shell [options] --shell [file1 file2 ... filek]\n");
+ printf(" run an interactive JavaScript shell\n");
+ printf(" d8 [options] file1 file2 ... filek\n");
+ printf(" d8 [options]\n");
+ printf(" d8 [options] --shell [file1 file2 ... filek]\n");
+ printf(" run the new debugging shell\n\n");
+ printf("Options:\n");
+ for (size_t i = 0; i < num_flags; ++i) {
+ Flag* f = &flags[i];
+ SmartArrayPointer<const char> value = ToString(f);
+ printf(" --%s (%s)\n type: %s default: %s\n",
+ f->name(), f->comment(), Type2String(f->type()), *value);
+ }
+}
+
+
+void FlagList::EnforceFlagImplications() {
+#define FLAG_MODE_DEFINE_IMPLICATIONS
+#include "flag-definitions.h"
+#undef FLAG_MODE_DEFINE_IMPLICATIONS
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/flags.h b/chromium/v8/src/flags.h
new file mode 100644
index 00000000000..f0b239b6f20
--- /dev/null
+++ b/chromium/v8/src/flags.h
@@ -0,0 +1,82 @@
+// 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_FLAGS_H_
+#define V8_FLAGS_H_
+
+namespace v8 {
+namespace internal {
+
+// Declare all of our flags.
+#define FLAG_MODE_DECLARE
+#include "flag-definitions.h"
+
+// The global list of all flags.
+class FlagList {
+ public:
+ // The list of all flags with a value different from the default
+ // and their values. The format of the list is like the format of the
+ // argv array passed to the main function, e.g.
+ // ("--prof", "--log-file", "v8.prof", "--nolazy").
+ //
+ // The caller is responsible for disposing the list, as well
+ // as every element of it.
+ static List<const char*>* argv();
+
+ // Set the flag values by parsing the command line. If remove_flags is
+ // set, the flags and associated values are removed from (argc,
+ // argv). Returns 0 if no error occurred. Otherwise, returns the argv
+ // index > 0 for the argument where an error occurred. In that case,
+ // (argc, argv) will remain unchanged independent of the remove_flags
+ // value, and no assumptions about flag settings should be made.
+ //
+ // The following syntax for flags is accepted (both '-' and '--' are ok):
+ //
+ // --flag (bool flags only)
+ // --noflag (bool flags only)
+ // --flag=value (non-bool flags only, no spaces around '=')
+ // --flag value (non-bool flags only)
+ // -- (equivalent to --js_arguments, captures all remaining args)
+ static int SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags);
+
+ // Set the flag values by parsing the string str. Splits string into argc
+ // substrings argv[], each of which consisting of non-white-space chars,
+ // and then calls SetFlagsFromCommandLine() and returns its result.
+ static int SetFlagsFromString(const char* str, int len);
+
+ // Reset all flags to their default value.
+ static void ResetAllFlags();
+
+ // 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
+
+#endif // V8_FLAGS_H_
diff --git a/chromium/v8/src/frames-inl.h b/chromium/v8/src/frames-inl.h
new file mode 100644
index 00000000000..2b15bfffab7
--- /dev/null
+++ b/chromium/v8/src/frames-inl.h
@@ -0,0 +1,346 @@
+// Copyright 2012 the V8 project authors. 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_FRAMES_INL_H_
+#define V8_FRAMES_INL_H_
+
+#include "frames.h"
+#include "isolate.h"
+#include "v8memory.h"
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/frames-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/frames-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/frames-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/frames-mips.h"
+#else
+#error Unsupported target architecture.
+#endif
+
+namespace v8 {
+namespace internal {
+
+
+inline Address StackHandler::address() const {
+ return reinterpret_cast<Address>(const_cast<StackHandler*>(this));
+}
+
+
+inline StackHandler* StackHandler::next() const {
+ const int offset = StackHandlerConstants::kNextOffset;
+ return FromAddress(Memory::Address_at(address() + offset));
+}
+
+
+inline bool StackHandler::includes(Address address) const {
+ Address start = this->address();
+ Address end = start + StackHandlerConstants::kSize;
+ return start <= address && address <= end;
+}
+
+
+inline void StackHandler::Iterate(ObjectVisitor* v, Code* holder) const {
+ v->VisitPointer(context_address());
+ v->VisitPointer(code_address());
+}
+
+
+inline StackHandler* StackHandler::FromAddress(Address address) {
+ return reinterpret_cast<StackHandler*>(address);
+}
+
+
+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 KindField::decode(Memory::unsigned_at(address() + offset));
+}
+
+
+inline unsigned StackHandler::index() const {
+ const int offset = StackHandlerConstants::kStateOffset;
+ return IndexField::decode(Memory::unsigned_at(address() + offset));
+}
+
+
+inline Object** StackHandler::context_address() const {
+ const int offset = StackHandlerConstants::kContextOffset;
+ return reinterpret_cast<Object**>(address() + offset);
+}
+
+
+inline Object** StackHandler::code_address() const {
+ const int offset = StackHandlerConstants::kCodeOffset;
+ return reinterpret_cast<Object**>(address() + offset);
+}
+
+
+inline StackFrame::StackFrame(StackFrameIteratorBase* iterator)
+ : iterator_(iterator), isolate_(iterator_->isolate()) {
+}
+
+
+inline StackHandler* StackFrame::top_handler() const {
+ return iterator_->handler();
+}
+
+
+inline Code* StackFrame::LookupCode() const {
+ return GetContainingCode(isolate(), pc());
+}
+
+
+inline Code* StackFrame::GetContainingCode(Isolate* isolate, Address pc) {
+ return isolate->inner_pointer_to_code_cache()->GetCacheEntry(pc)->code;
+}
+
+
+inline Address* StackFrame::ResolveReturnAddressLocation(Address* pc_address) {
+ if (return_address_location_resolver_ == NULL) {
+ return pc_address;
+ } else {
+ return reinterpret_cast<Address*>(
+ return_address_location_resolver_(
+ reinterpret_cast<uintptr_t>(pc_address)));
+ }
+}
+
+
+inline EntryFrame::EntryFrame(StackFrameIteratorBase* iterator)
+ : StackFrame(iterator) {
+}
+
+
+inline EntryConstructFrame::EntryConstructFrame(
+ StackFrameIteratorBase* iterator)
+ : EntryFrame(iterator) {
+}
+
+
+inline ExitFrame::ExitFrame(StackFrameIteratorBase* iterator)
+ : StackFrame(iterator) {
+}
+
+
+inline StandardFrame::StandardFrame(StackFrameIteratorBase* iterator)
+ : StackFrame(iterator) {
+}
+
+
+inline Object* StandardFrame::GetExpression(int index) const {
+ return Memory::Object_at(GetExpressionAddress(index));
+}
+
+
+inline void StandardFrame::SetExpression(int index, Object* value) {
+ Memory::Object_at(GetExpressionAddress(index)) = value;
+}
+
+
+inline Object* StandardFrame::context() const {
+ const int offset = StandardFrameConstants::kContextOffset;
+ return Memory::Object_at(fp() + offset);
+}
+
+
+inline Address StandardFrame::caller_fp() const {
+ return Memory::Address_at(fp() + StandardFrameConstants::kCallerFPOffset);
+}
+
+
+inline Address StandardFrame::caller_pc() const {
+ return Memory::Address_at(ComputePCAddress(fp()));
+}
+
+
+inline Address StandardFrame::ComputePCAddress(Address fp) {
+ return fp + StandardFrameConstants::kCallerPCOffset;
+}
+
+
+inline bool StandardFrame::IsArgumentsAdaptorFrame(Address fp) {
+ Object* marker =
+ Memory::Object_at(fp + StandardFrameConstants::kContextOffset);
+ return marker == Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR);
+}
+
+
+inline bool StandardFrame::IsConstructFrame(Address fp) {
+ Object* marker =
+ Memory::Object_at(fp + StandardFrameConstants::kMarkerOffset);
+ return marker == Smi::FromInt(StackFrame::CONSTRUCT);
+}
+
+
+inline JavaScriptFrame::JavaScriptFrame(StackFrameIteratorBase* iterator)
+ : StandardFrame(iterator) {
+}
+
+
+Address JavaScriptFrame::GetParameterSlot(int index) const {
+ int param_count = ComputeParametersCount();
+ ASSERT(-1 <= index && index < param_count);
+ int parameter_offset = (param_count - index - 1) * kPointerSize;
+ return caller_sp() + parameter_offset;
+}
+
+
+Object* JavaScriptFrame::GetParameter(int index) const {
+ return Memory::Object_at(GetParameterSlot(index));
+}
+
+
+inline Address JavaScriptFrame::GetOperandSlot(int index) const {
+ Address base = fp() + JavaScriptFrameConstants::kLocal0Offset;
+ ASSERT(IsAddressAligned(base, kPointerSize));
+ ASSERT_EQ(type(), JAVA_SCRIPT);
+ ASSERT_LT(index, ComputeOperandsCount());
+ ASSERT_LE(0, index);
+ // Operand stack grows down.
+ return base - index * kPointerSize;
+}
+
+
+inline Object* JavaScriptFrame::GetOperand(int index) const {
+ return Memory::Object_at(GetOperandSlot(index));
+}
+
+
+inline int JavaScriptFrame::ComputeOperandsCount() const {
+ Address base = fp() + JavaScriptFrameConstants::kLocal0Offset;
+ // Base points to low address of first operand and stack grows down, so add
+ // kPointerSize to get the actual stack size.
+ intptr_t stack_size_in_bytes = (base + kPointerSize) - sp();
+ ASSERT(IsAligned(stack_size_in_bytes, kPointerSize));
+ ASSERT(type() == JAVA_SCRIPT);
+ ASSERT(stack_size_in_bytes >= 0);
+ return static_cast<int>(stack_size_in_bytes >> kPointerSizeLog2);
+}
+
+
+inline Object* JavaScriptFrame::receiver() const {
+ return GetParameter(-1);
+}
+
+
+inline void JavaScriptFrame::set_receiver(Object* value) {
+ Memory::Object_at(GetParameterSlot(-1)) = value;
+}
+
+
+inline bool JavaScriptFrame::has_adapted_arguments() const {
+ return IsArgumentsAdaptorFrame(caller_fp());
+}
+
+
+inline JSFunction* JavaScriptFrame::function() const {
+ return JSFunction::cast(function_slot_object());
+}
+
+
+inline StubFrame::StubFrame(StackFrameIteratorBase* iterator)
+ : StandardFrame(iterator) {
+}
+
+
+inline OptimizedFrame::OptimizedFrame(StackFrameIteratorBase* iterator)
+ : JavaScriptFrame(iterator) {
+}
+
+
+inline ArgumentsAdaptorFrame::ArgumentsAdaptorFrame(
+ StackFrameIteratorBase* iterator) : JavaScriptFrame(iterator) {
+}
+
+
+inline InternalFrame::InternalFrame(StackFrameIteratorBase* iterator)
+ : StandardFrame(iterator) {
+}
+
+
+inline StubFailureTrampolineFrame::StubFailureTrampolineFrame(
+ StackFrameIteratorBase* iterator) : StandardFrame(iterator) {
+}
+
+
+inline ConstructFrame::ConstructFrame(StackFrameIteratorBase* iterator)
+ : InternalFrame(iterator) {
+}
+
+
+inline JavaScriptFrameIterator::JavaScriptFrameIterator(
+ Isolate* isolate)
+ : iterator_(isolate) {
+ if (!done()) Advance();
+}
+
+
+inline JavaScriptFrameIterator::JavaScriptFrameIterator(
+ Isolate* isolate, ThreadLocalTop* top)
+ : iterator_(isolate, top) {
+ if (!done()) Advance();
+}
+
+
+inline JavaScriptFrame* JavaScriptFrameIterator::frame() const {
+ // TODO(1233797): The frame hierarchy needs to change. It's
+ // problematic that we can't use the safe-cast operator to cast to
+ // the JavaScript frame type, because we may encounter arguments
+ // adaptor frames.
+ StackFrame* frame = iterator_.frame();
+ ASSERT(frame->is_java_script() || frame->is_arguments_adaptor());
+ return static_cast<JavaScriptFrame*>(frame);
+}
+
+
+inline StackFrame* SafeStackFrameIterator::frame() const {
+ ASSERT(!done());
+ ASSERT(frame_->is_java_script() || frame_->is_exit());
+ return frame_;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_FRAMES_INL_H_
diff --git a/chromium/v8/src/frames.cc b/chromium/v8/src/frames.cc
new file mode 100644
index 00000000000..c17a9d5f82e
--- /dev/null
+++ b/chromium/v8/src/frames.cc
@@ -0,0 +1,1637 @@
+// Copyright 2012 the V8 project authors. 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 "ast.h"
+#include "deoptimizer.h"
+#include "frames-inl.h"
+#include "full-codegen.h"
+#include "lazy-instance.h"
+#include "mark-compact.h"
+#include "safepoint-table.h"
+#include "scopeinfo.h"
+#include "string-stream.h"
+#include "vm-state-inl.h"
+
+#include "allocation-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+ReturnAddressLocationResolver
+ StackFrame::return_address_location_resolver_ = NULL;
+
+
+// Iterator that supports traversing the stack handlers of a
+// particular frame. Needs to know the top of the handler chain.
+class StackHandlerIterator BASE_EMBEDDED {
+ public:
+ StackHandlerIterator(const StackFrame* frame, StackHandler* handler)
+ : limit_(frame->fp()), handler_(handler) {
+ // Make sure the handler has already been unwound to this frame.
+ ASSERT(frame->sp() <= handler->address());
+ }
+
+ StackHandler* handler() const { return handler_; }
+
+ bool done() {
+ return handler_ == NULL || handler_->address() > limit_;
+ }
+ void Advance() {
+ ASSERT(!done());
+ handler_ = handler_->next();
+ }
+
+ private:
+ const Address limit_;
+ StackHandler* handler_;
+};
+
+
+// -------------------------------------------------------------------------
+
+
+#define INITIALIZE_SINGLETON(type, field) field##_(this),
+StackFrameIteratorBase::StackFrameIteratorBase(Isolate* isolate,
+ bool can_access_heap_objects)
+ : isolate_(isolate),
+ STACK_FRAME_TYPE_LIST(INITIALIZE_SINGLETON)
+ frame_(NULL), handler_(NULL),
+ can_access_heap_objects_(can_access_heap_objects) {
+}
+#undef INITIALIZE_SINGLETON
+
+
+StackFrameIterator::StackFrameIterator(Isolate* isolate)
+ : StackFrameIteratorBase(isolate, true) {
+ Reset(isolate->thread_local_top());
+}
+
+
+StackFrameIterator::StackFrameIterator(Isolate* isolate, ThreadLocalTop* t)
+ : StackFrameIteratorBase(isolate, true) {
+ Reset(t);
+}
+
+
+void StackFrameIterator::Advance() {
+ ASSERT(!done());
+ // Compute the state of the calling frame before restoring
+ // callee-saved registers and unwinding handlers. This allows the
+ // frame code that computes the caller state to access the top
+ // handler and the value of any callee-saved register if needed.
+ StackFrame::State state;
+ StackFrame::Type type = frame_->GetCallerState(&state);
+
+ // Unwind handlers corresponding to the current frame.
+ StackHandlerIterator it(frame_, handler_);
+ while (!it.done()) it.Advance();
+ handler_ = it.handler();
+
+ // Advance to the calling frame.
+ frame_ = SingletonFor(type, &state);
+
+ // When we're done iterating over the stack frames, the handler
+ // chain must have been completely unwound.
+ ASSERT(!done() || handler_ == NULL);
+}
+
+
+void StackFrameIterator::Reset(ThreadLocalTop* top) {
+ StackFrame::State state;
+ StackFrame::Type type = ExitFrame::GetStateForFramePointer(
+ Isolate::c_entry_fp(top), &state);
+ handler_ = StackHandler::FromAddress(Isolate::handler(top));
+ if (SingletonFor(type) == NULL) return;
+ frame_ = SingletonFor(type, &state);
+}
+
+
+StackFrame* StackFrameIteratorBase::SingletonFor(StackFrame::Type type,
+ StackFrame::State* state) {
+ if (type == StackFrame::NONE) return NULL;
+ StackFrame* result = SingletonFor(type);
+ ASSERT(result != NULL);
+ result->state_ = *state;
+ return result;
+}
+
+
+StackFrame* StackFrameIteratorBase::SingletonFor(StackFrame::Type type) {
+#define FRAME_TYPE_CASE(type, field) \
+ case StackFrame::type: result = &field##_; break;
+
+ StackFrame* result = NULL;
+ switch (type) {
+ case StackFrame::NONE: return NULL;
+ STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE)
+ default: break;
+ }
+ return result;
+
+#undef FRAME_TYPE_CASE
+}
+
+
+// -------------------------------------------------------------------------
+
+
+JavaScriptFrameIterator::JavaScriptFrameIterator(
+ Isolate* isolate, StackFrame::Id id)
+ : iterator_(isolate) {
+ while (!done()) {
+ Advance();
+ if (frame()->id() == id) return;
+ }
+}
+
+
+void JavaScriptFrameIterator::Advance() {
+ do {
+ iterator_.Advance();
+ } while (!iterator_.done() && !iterator_.frame()->is_java_script());
+}
+
+
+void JavaScriptFrameIterator::AdvanceToArgumentsFrame() {
+ if (!frame()->has_adapted_arguments()) return;
+ iterator_.Advance();
+ ASSERT(iterator_.frame()->is_arguments_adaptor());
+}
+
+
+// -------------------------------------------------------------------------
+
+
+StackTraceFrameIterator::StackTraceFrameIterator(Isolate* isolate)
+ : JavaScriptFrameIterator(isolate) {
+ if (!done() && !IsValidFrame()) Advance();
+}
+
+
+void StackTraceFrameIterator::Advance() {
+ while (true) {
+ JavaScriptFrameIterator::Advance();
+ if (done()) return;
+ if (IsValidFrame()) return;
+ }
+}
+
+
+bool StackTraceFrameIterator::IsValidFrame() {
+ if (!frame()->function()->IsJSFunction()) return false;
+ Object* script = frame()->function()->shared()->script();
+ // Don't show functions from native scripts to user.
+ return (script->IsScript() &&
+ Script::TYPE_NATIVE != Script::cast(script)->type()->value());
+}
+
+
+// -------------------------------------------------------------------------
+
+
+SafeStackFrameIterator::SafeStackFrameIterator(
+ Isolate* isolate,
+ Address fp, Address sp, Address js_entry_sp)
+ : StackFrameIteratorBase(isolate, false),
+ low_bound_(sp),
+ high_bound_(js_entry_sp),
+ top_frame_type_(StackFrame::NONE),
+ external_callback_scope_(isolate->external_callback_scope()) {
+ StackFrame::State state;
+ StackFrame::Type type;
+ ThreadLocalTop* top = isolate->thread_local_top();
+ if (IsValidTop(top)) {
+ type = ExitFrame::GetStateForFramePointer(Isolate::c_entry_fp(top), &state);
+ top_frame_type_ = type;
+ } else if (IsValidStackAddress(fp)) {
+ ASSERT(fp != NULL);
+ state.fp = fp;
+ state.sp = sp;
+ state.pc_address = StackFrame::ResolveReturnAddressLocation(
+ reinterpret_cast<Address*>(StandardFrame::ComputePCAddress(fp)));
+ // StackFrame::ComputeType will read both kContextOffset and kMarkerOffset,
+ // we check only that kMarkerOffset is within the stack bounds and do
+ // compile time check that kContextOffset slot is pushed on the stack before
+ // kMarkerOffset.
+ STATIC_ASSERT(StandardFrameConstants::kMarkerOffset <
+ StandardFrameConstants::kContextOffset);
+ Address frame_marker = fp + StandardFrameConstants::kMarkerOffset;
+ if (IsValidStackAddress(frame_marker)) {
+ type = StackFrame::ComputeType(this, &state);
+ top_frame_type_ = type;
+ } else {
+ // Mark the frame as JAVA_SCRIPT if we cannot determine its type.
+ // The frame anyways will be skipped.
+ type = StackFrame::JAVA_SCRIPT;
+ // Top frame is incomplete so we cannot reliably determine its type.
+ top_frame_type_ = StackFrame::NONE;
+ }
+ } else {
+ return;
+ }
+ if (SingletonFor(type) == NULL) return;
+ frame_ = SingletonFor(type, &state);
+ if (frame_ == NULL) return;
+
+ Advance();
+
+ if (frame_ != NULL && !frame_->is_exit() &&
+ external_callback_scope_ != NULL &&
+ external_callback_scope_->scope_address() < frame_->fp()) {
+ // Skip top ExternalCallbackScope if we already advanced to a JS frame
+ // under it. Sampler will anyways take this top external callback.
+ external_callback_scope_ = external_callback_scope_->previous();
+ }
+}
+
+
+bool SafeStackFrameIterator::IsValidTop(ThreadLocalTop* top) const {
+ Address c_entry_fp = Isolate::c_entry_fp(top);
+ if (!IsValidExitFrame(c_entry_fp)) return false;
+ // There should be at least one JS_ENTRY stack handler.
+ Address handler = Isolate::handler(top);
+ if (handler == NULL) return false;
+ // Check that there are no js frames on top of the native frames.
+ return c_entry_fp < handler;
+}
+
+
+void SafeStackFrameIterator::AdvanceOneFrame() {
+ ASSERT(!done());
+ StackFrame* last_frame = frame_;
+ Address last_sp = last_frame->sp(), last_fp = last_frame->fp();
+ // Before advancing to the next stack frame, perform pointer validity tests.
+ if (!IsValidFrame(last_frame) || !IsValidCaller(last_frame)) {
+ frame_ = NULL;
+ return;
+ }
+
+ // Advance to the previous frame.
+ StackFrame::State state;
+ StackFrame::Type type = frame_->GetCallerState(&state);
+ frame_ = SingletonFor(type, &state);
+ if (frame_ == NULL) return;
+
+ // Check that we have actually moved to the previous frame in the stack.
+ if (frame_->sp() < last_sp || frame_->fp() < last_fp) {
+ frame_ = NULL;
+ }
+}
+
+
+bool SafeStackFrameIterator::IsValidFrame(StackFrame* frame) const {
+ return IsValidStackAddress(frame->sp()) && IsValidStackAddress(frame->fp());
+}
+
+
+bool SafeStackFrameIterator::IsValidCaller(StackFrame* frame) {
+ StackFrame::State state;
+ if (frame->is_entry() || frame->is_entry_construct()) {
+ // See EntryFrame::GetCallerState. It computes the caller FP address
+ // and calls ExitFrame::GetStateForFramePointer on it. We need to be
+ // sure that caller FP address is valid.
+ Address caller_fp = Memory::Address_at(
+ frame->fp() + EntryFrameConstants::kCallerFPOffset);
+ if (!IsValidExitFrame(caller_fp)) return false;
+ } else if (frame->is_arguments_adaptor()) {
+ // See ArgumentsAdaptorFrame::GetCallerStackPointer. It assumes that
+ // the number of arguments is stored on stack as Smi. We need to check
+ // that it really an Smi.
+ Object* number_of_args = reinterpret_cast<ArgumentsAdaptorFrame*>(frame)->
+ GetExpression(0);
+ if (!number_of_args->IsSmi()) {
+ return false;
+ }
+ }
+ frame->ComputeCallerState(&state);
+ return IsValidStackAddress(state.sp) && IsValidStackAddress(state.fp) &&
+ SingletonFor(frame->GetCallerState(&state)) != NULL;
+}
+
+
+bool SafeStackFrameIterator::IsValidExitFrame(Address fp) const {
+ if (!IsValidStackAddress(fp)) return false;
+ Address sp = ExitFrame::ComputeStackPointer(fp);
+ if (!IsValidStackAddress(sp)) return false;
+ StackFrame::State state;
+ ExitFrame::FillState(fp, sp, &state);
+ if (!IsValidStackAddress(reinterpret_cast<Address>(state.pc_address))) {
+ return false;
+ }
+ return *state.pc_address != NULL;
+}
+
+
+void SafeStackFrameIterator::Advance() {
+ while (true) {
+ AdvanceOneFrame();
+ if (done()) return;
+ if (frame_->is_java_script()) return;
+ if (frame_->is_exit() && external_callback_scope_) {
+ // Some of the EXIT frames may have ExternalCallbackScope allocated on
+ // top of them. In that case the scope corresponds to the first EXIT
+ // frame beneath it. There may be other EXIT frames on top of the
+ // ExternalCallbackScope, just skip them as we cannot collect any useful
+ // information about them.
+ if (external_callback_scope_->scope_address() < frame_->fp()) {
+ Address* callback_address =
+ external_callback_scope_->callback_address();
+ if (*callback_address != NULL) {
+ frame_->state_.pc_address = callback_address;
+ }
+ external_callback_scope_ = external_callback_scope_->previous();
+ ASSERT(external_callback_scope_ == NULL ||
+ external_callback_scope_->scope_address() > frame_->fp());
+ return;
+ }
+ }
+ }
+}
+
+
+// -------------------------------------------------------------------------
+
+
+Code* StackFrame::GetSafepointData(Isolate* isolate,
+ Address inner_pointer,
+ SafepointEntry* safepoint_entry,
+ unsigned* stack_slots) {
+ InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry =
+ isolate->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer);
+ if (!entry->safepoint_entry.is_valid()) {
+ entry->safepoint_entry = entry->code->GetSafepointEntry(inner_pointer);
+ ASSERT(entry->safepoint_entry.is_valid());
+ } else {
+ ASSERT(entry->safepoint_entry.Equals(
+ entry->code->GetSafepointEntry(inner_pointer)));
+ }
+
+ // Fill in the results and return the code.
+ Code* code = entry->code;
+ *safepoint_entry = entry->safepoint_entry;
+ *stack_slots = code->stack_slots();
+ return code;
+}
+
+
+bool StackFrame::HasHandler() const {
+ StackHandlerIterator it(this, top_handler());
+ return !it.done();
+}
+
+
+#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(GcSafeCodeContains(holder, pc));
+ unsigned pc_offset = static_cast<unsigned>(pc - holder->instruction_start());
+ Object* code = holder;
+ v->VisitPointer(&code);
+ if (code != holder) {
+ holder = reinterpret_cast<Code*>(code);
+ pc = holder->instruction_start() + pc_offset;
+ *pc_address = pc;
+ }
+}
+
+
+void StackFrame::SetReturnAddressLocationResolver(
+ ReturnAddressLocationResolver resolver) {
+ ASSERT(return_address_location_resolver_ == NULL);
+ return_address_location_resolver_ = resolver;
+}
+
+
+StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
+ State* state) {
+ ASSERT(state->fp != NULL);
+ if (StandardFrame::IsArgumentsAdaptorFrame(state->fp)) {
+ return ARGUMENTS_ADAPTOR;
+ }
+ // The marker and function offsets overlap. If the marker isn't a
+ // smi then the frame is a JavaScript frame -- and the marker is
+ // really the function.
+ const int offset = StandardFrameConstants::kMarkerOffset;
+ Object* marker = Memory::Object_at(state->fp + offset);
+ if (!marker->IsSmi()) {
+ // If we're using a "safe" stack iterator, we treat optimized
+ // frames as normal JavaScript frames to avoid having to look
+ // into the heap to determine the state. This is safe as long
+ // as nobody tries to GC...
+ if (!iterator->can_access_heap_objects_) return JAVA_SCRIPT;
+ Code::Kind kind = GetContainingCode(iterator->isolate(),
+ *(state->pc_address))->kind();
+ ASSERT(kind == Code::FUNCTION || kind == Code::OPTIMIZED_FUNCTION);
+ return (kind == Code::OPTIMIZED_FUNCTION) ? OPTIMIZED : JAVA_SCRIPT;
+ }
+ return static_cast<StackFrame::Type>(Smi::cast(marker)->value());
+}
+
+
+#ifdef DEBUG
+bool StackFrame::can_access_heap_objects() const {
+ return iterator_->can_access_heap_objects_;
+}
+#endif
+
+
+StackFrame::Type StackFrame::GetCallerState(State* state) const {
+ ComputeCallerState(state);
+ return ComputeType(iterator_, state);
+}
+
+
+Address StackFrame::UnpaddedFP() const {
+#if V8_TARGET_ARCH_IA32
+ if (!is_optimized()) return fp();
+ int32_t alignment_state = Memory::int32_at(
+ fp() + JavaScriptFrameConstants::kDynamicAlignmentStateOffset);
+
+ return (alignment_state == kAlignmentPaddingPushed) ?
+ (fp() + kPointerSize) : fp();
+#else
+ return fp();
+#endif
+}
+
+
+Code* EntryFrame::unchecked_code() const {
+ return HEAP->js_entry_code();
+}
+
+
+void EntryFrame::ComputeCallerState(State* state) const {
+ GetCallerState(state);
+}
+
+
+void EntryFrame::SetCallerFp(Address caller_fp) {
+ const int offset = EntryFrameConstants::kCallerFPOffset;
+ Memory::Address_at(this->fp() + offset) = caller_fp;
+}
+
+
+StackFrame::Type EntryFrame::GetCallerState(State* state) const {
+ const int offset = EntryFrameConstants::kCallerFPOffset;
+ Address fp = Memory::Address_at(this->fp() + offset);
+ return ExitFrame::GetStateForFramePointer(fp, state);
+}
+
+
+Code* EntryConstructFrame::unchecked_code() const {
+ return HEAP->js_construct_entry_code();
+}
+
+
+Object*& ExitFrame::code_slot() const {
+ const int offset = ExitFrameConstants::kCodeOffset;
+ return Memory::Object_at(fp() + offset);
+}
+
+
+Code* ExitFrame::unchecked_code() const {
+ return reinterpret_cast<Code*>(code_slot());
+}
+
+
+void ExitFrame::ComputeCallerState(State* state) const {
+ // Set up the caller state.
+ state->sp = caller_sp();
+ state->fp = Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset);
+ state->pc_address = ResolveReturnAddressLocation(
+ reinterpret_cast<Address*>(fp() + ExitFrameConstants::kCallerPCOffset));
+}
+
+
+void ExitFrame::SetCallerFp(Address caller_fp) {
+ Memory::Address_at(fp() + ExitFrameConstants::kCallerFPOffset) = caller_fp;
+}
+
+
+void ExitFrame::Iterate(ObjectVisitor* v) const {
+ // The arguments are traversed as part of the expression stack of
+ // the calling frame.
+ IteratePc(v, pc_address(), LookupCode());
+ v->VisitPointer(&code_slot());
+}
+
+
+Address ExitFrame::GetCallerStackPointer() const {
+ return fp() + ExitFrameConstants::kCallerSPDisplacement;
+}
+
+
+StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
+ if (fp == 0) return NONE;
+ Address sp = ComputeStackPointer(fp);
+ FillState(fp, sp, state);
+ ASSERT(*state->pc_address != NULL);
+ return EXIT;
+}
+
+
+Address ExitFrame::ComputeStackPointer(Address fp) {
+ return Memory::Address_at(fp + ExitFrameConstants::kSPOffset);
+}
+
+
+void ExitFrame::FillState(Address fp, Address sp, State* state) {
+ state->sp = sp;
+ state->fp = fp;
+ state->pc_address = ResolveReturnAddressLocation(
+ reinterpret_cast<Address*>(sp - 1 * kPCOnStackSize));
+}
+
+
+Address StandardFrame::GetExpressionAddress(int n) const {
+ const int offset = StandardFrameConstants::kExpressionsOffset;
+ return fp() + offset - n * kPointerSize;
+}
+
+
+Object* StandardFrame::GetExpression(Address fp, int index) {
+ return Memory::Object_at(GetExpressionAddress(fp, index));
+}
+
+
+Address StandardFrame::GetExpressionAddress(Address fp, int n) {
+ const int offset = StandardFrameConstants::kExpressionsOffset;
+ return fp + offset - n * kPointerSize;
+}
+
+
+int StandardFrame::ComputeExpressionsCount() const {
+ const int offset =
+ StandardFrameConstants::kExpressionsOffset + kPointerSize;
+ Address base = fp() + offset;
+ Address limit = sp();
+ ASSERT(base >= limit); // stack grows downwards
+ // Include register-allocated locals in number of expressions.
+ return static_cast<int>((base - limit) / kPointerSize);
+}
+
+
+void StandardFrame::ComputeCallerState(State* state) const {
+ state->sp = caller_sp();
+ state->fp = caller_fp();
+ state->pc_address = ResolveReturnAddressLocation(
+ reinterpret_cast<Address*>(ComputePCAddress(fp())));
+}
+
+
+void StandardFrame::SetCallerFp(Address caller_fp) {
+ Memory::Address_at(fp() + StandardFrameConstants::kCallerFPOffset) =
+ caller_fp;
+}
+
+
+bool StandardFrame::IsExpressionInsideHandler(int n) const {
+ Address address = GetExpressionAddress(n);
+ for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
+ if (it.handler()->includes(address)) return true;
+ }
+ return false;
+}
+
+
+void StandardFrame::IterateCompiledFrame(ObjectVisitor* v) const {
+ // Make sure that we're not doing "safe" stack frame iteration. We cannot
+ // possibly find pointers in optimized frames in that state.
+ ASSERT(can_access_heap_objects());
+
+ // Compute the safepoint information.
+ unsigned stack_slots = 0;
+ SafepointEntry safepoint_entry;
+ Code* code = StackFrame::GetSafepointData(
+ isolate(), pc(), &safepoint_entry, &stack_slots);
+ unsigned slot_space = stack_slots * kPointerSize;
+
+ // Visit the outgoing parameters.
+ Object** parameters_base = &Memory::Object_at(sp());
+ Object** parameters_limit = &Memory::Object_at(
+ fp() + JavaScriptFrameConstants::kFunctionOffset - slot_space);
+
+ // Visit the parameters that may be on top of the saved registers.
+ if (safepoint_entry.argument_count() > 0) {
+ v->VisitPointers(parameters_base,
+ parameters_base + safepoint_entry.argument_count());
+ parameters_base += safepoint_entry.argument_count();
+ }
+
+ // Skip saved double registers.
+ if (safepoint_entry.has_doubles()) {
+ // Number of doubles not known at snapshot time.
+ ASSERT(!Serializer::enabled());
+ parameters_base += DoubleRegister::NumAllocatableRegisters() *
+ kDoubleSize / kPointerSize;
+ }
+
+ // Visit the registers that contain pointers if any.
+ if (safepoint_entry.HasRegisters()) {
+ for (int i = kNumSafepointRegisters - 1; i >=0; i--) {
+ if (safepoint_entry.HasRegisterAt(i)) {
+ int reg_stack_index = MacroAssembler::SafepointRegisterStackIndex(i);
+ v->VisitPointer(parameters_base + reg_stack_index);
+ }
+ }
+ // Skip the words containing the register values.
+ parameters_base += kNumSafepointRegisters;
+ }
+
+ // We're done dealing with the register bits.
+ uint8_t* safepoint_bits = safepoint_entry.bits();
+ safepoint_bits += kNumSafepointRegisters >> kBitsPerByteLog2;
+
+ // Visit the rest of the parameters.
+ v->VisitPointers(parameters_base, parameters_limit);
+
+ // Visit pointer spill slots and locals.
+ for (unsigned index = 0; index < stack_slots; index++) {
+ int byte_index = index >> kBitsPerByteLog2;
+ int bit_index = index & (kBitsPerByte - 1);
+ if ((safepoint_bits[byte_index] & (1U << bit_index)) != 0) {
+ v->VisitPointer(parameters_limit + index);
+ }
+ }
+
+ // Visit the return address in the callee and incoming arguments.
+ IteratePc(v, pc_address(), code);
+
+ // Visit the context in stub frame and JavaScript frame.
+ // Visit the function in JavaScript frame.
+ Object** fixed_base = &Memory::Object_at(
+ fp() + StandardFrameConstants::kMarkerOffset);
+ Object** fixed_limit = &Memory::Object_at(fp());
+ v->VisitPointers(fixed_base, fixed_limit);
+}
+
+
+void StubFrame::Iterate(ObjectVisitor* v) const {
+ IterateCompiledFrame(v);
+}
+
+
+Code* StubFrame::unchecked_code() const {
+ return static_cast<Code*>(isolate()->FindCodeObject(pc()));
+}
+
+
+Address StubFrame::GetCallerStackPointer() const {
+ return fp() + ExitFrameConstants::kCallerSPDisplacement;
+}
+
+
+int StubFrame::GetNumberOfIncomingArguments() const {
+ return 0;
+}
+
+
+void OptimizedFrame::Iterate(ObjectVisitor* v) const {
+#ifdef DEBUG
+ // Make sure that optimized frames do not contain any stack handlers.
+ StackHandlerIterator it(this, top_handler());
+ ASSERT(it.done());
+#endif
+
+ IterateCompiledFrame(v);
+}
+
+
+void JavaScriptFrame::SetParameterValue(int index, Object* value) const {
+ Memory::Object_at(GetParameterSlot(index)) = value;
+}
+
+
+bool JavaScriptFrame::IsConstructor() const {
+ Address fp = caller_fp();
+ if (has_adapted_arguments()) {
+ // Skip the arguments adaptor frame and look at the real caller.
+ fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset);
+ }
+ return IsConstructFrame(fp);
+}
+
+
+int JavaScriptFrame::GetArgumentsLength() const {
+ // If there is an arguments adaptor frame get the arguments length from it.
+ if (has_adapted_arguments()) {
+ return Smi::cast(GetExpression(caller_fp(), 0))->value();
+ } else {
+ return GetNumberOfIncomingArguments();
+ }
+}
+
+
+Code* JavaScriptFrame::unchecked_code() const {
+ return function()->code();
+}
+
+
+int JavaScriptFrame::GetNumberOfIncomingArguments() const {
+ ASSERT(can_access_heap_objects() &&
+ isolate()->heap()->gc_state() == Heap::NOT_IN_GC);
+
+ return function()->shared()->formal_parameter_count();
+}
+
+
+Address JavaScriptFrame::GetCallerStackPointer() const {
+ return fp() + StandardFrameConstants::kCallerSPOffset;
+}
+
+
+void JavaScriptFrame::GetFunctions(List<JSFunction*>* functions) {
+ ASSERT(functions->length() == 0);
+ functions->Add(function());
+}
+
+
+void JavaScriptFrame::Summarize(List<FrameSummary>* functions) {
+ ASSERT(functions->length() == 0);
+ Code* code_pointer = LookupCode();
+ int offset = static_cast<int>(pc() - code_pointer->address());
+ FrameSummary summary(receiver(),
+ function(),
+ code_pointer,
+ offset,
+ IsConstructor());
+ functions->Add(summary);
+}
+
+
+void JavaScriptFrame::PrintTop(Isolate* isolate,
+ FILE* file,
+ bool print_args,
+ bool print_line_number) {
+ // constructor calls
+ HandleScope scope(isolate);
+ DisallowHeapAllocation no_allocation;
+ JavaScriptFrameIterator it(isolate);
+ while (!it.done()) {
+ if (it.frame()->is_java_script()) {
+ JavaScriptFrame* frame = it.frame();
+ if (frame->IsConstructor()) PrintF(file, "new ");
+ // function name
+ JSFunction* fun = frame->function();
+ 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()->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>");
+ }
+ }
+
+ 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 JavaScriptFrame::SaveOperandStack(FixedArray* store,
+ int* stack_handler_index) const {
+ int operands_count = store->length();
+ ASSERT_LE(operands_count, ComputeOperandsCount());
+
+ // Visit the stack in LIFO order, saving operands and stack handlers into the
+ // array. The saved stack handlers store a link to the next stack handler,
+ // which will allow RestoreOperandStack to rewind the handlers.
+ StackHandlerIterator it(this, top_handler());
+ int i = operands_count - 1;
+ *stack_handler_index = -1;
+ for (; !it.done(); it.Advance()) {
+ StackHandler* handler = it.handler();
+ // Save operands pushed after the handler was pushed.
+ for (; GetOperandSlot(i) < handler->address(); i--) {
+ store->set(i, GetOperand(i));
+ }
+ ASSERT_GE(i + 1, StackHandlerConstants::kSlotCount);
+ ASSERT_EQ(handler->address(), GetOperandSlot(i));
+ int next_stack_handler_index = i + 1 - StackHandlerConstants::kSlotCount;
+ handler->Unwind(isolate(), store, next_stack_handler_index,
+ *stack_handler_index);
+ *stack_handler_index = next_stack_handler_index;
+ i -= StackHandlerConstants::kSlotCount;
+ }
+
+ // Save any remaining operands.
+ for (; i >= 0; i--) {
+ store->set(i, GetOperand(i));
+ }
+}
+
+
+void JavaScriptFrame::RestoreOperandStack(FixedArray* store,
+ int stack_handler_index) {
+ int operands_count = store->length();
+ ASSERT_LE(operands_count, ComputeOperandsCount());
+ int i = 0;
+ while (i <= stack_handler_index) {
+ if (i < stack_handler_index) {
+ // An operand.
+ ASSERT_EQ(GetOperand(i), isolate()->heap()->the_hole_value());
+ Memory::Object_at(GetOperandSlot(i)) = store->get(i);
+ i++;
+ } else {
+ // A stack handler.
+ ASSERT_EQ(i, stack_handler_index);
+ // The FixedArray store grows up. The stack grows down. So the operand
+ // slot for i actually points to the bottom of the top word in the
+ // handler. The base of the StackHandler* is the address of the bottom
+ // word, which will be the last slot that is in the handler.
+ int handler_slot_index = i + StackHandlerConstants::kSlotCount - 1;
+ StackHandler *handler =
+ StackHandler::FromAddress(GetOperandSlot(handler_slot_index));
+ stack_handler_index = handler->Rewind(isolate(), store, i, fp());
+ i += StackHandlerConstants::kSlotCount;
+ }
+ }
+
+ for (; i < operands_count; i++) {
+ ASSERT_EQ(GetOperand(i), isolate()->heap()->the_hole_value());
+ Memory::Object_at(GetOperandSlot(i)) = store->get(i);
+ }
+}
+
+
+void FrameSummary::Print() {
+ PrintF("receiver: ");
+ receiver_->ShortPrint();
+ PrintF("\nfunction: ");
+ function_->shared()->DebugName()->ShortPrint();
+ PrintF("\ncode: ");
+ code_->ShortPrint();
+ if (code_->kind() == Code::FUNCTION) PrintF(" NON-OPT");
+ if (code_->kind() == Code::OPTIMIZED_FUNCTION) PrintF(" OPT");
+ PrintF("\npc: %d\n", offset_);
+}
+
+
+JSFunction* OptimizedFrame::LiteralAt(FixedArray* literal_array,
+ int literal_id) {
+ if (literal_id == Translation::kSelfLiteralId) {
+ return function();
+ }
+
+ return JSFunction::cast(literal_array->get(literal_id));
+}
+
+
+void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
+ ASSERT(frames->length() == 0);
+ ASSERT(is_optimized());
+
+ int deopt_index = Safepoint::kNoDeoptimizationIndex;
+ DeoptimizationInputData* data = GetDeoptimizationData(&deopt_index);
+ FixedArray* literal_array = data->LiteralArray();
+
+ // BUG(3243555): Since we don't have a lazy-deopt registered at
+ // throw-statements, we can't use the translation at the call-site of
+ // throw. An entry with no deoptimization index indicates a call-site
+ // without a lazy-deopt. As a consequence we are not allowed to inline
+ // functions containing throw.
+ if (deopt_index == Safepoint::kNoDeoptimizationIndex) {
+ JavaScriptFrame::Summarize(frames);
+ return;
+ }
+
+ TranslationIterator it(data->TranslationByteArray(),
+ data->TranslationIndex(deopt_index)->value());
+ Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
+ ASSERT(opcode == Translation::BEGIN);
+ 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.
+ bool is_constructor = IsConstructor();
+ int i = jsframe_count;
+ while (i > 0) {
+ opcode = static_cast<Translation::Opcode>(it.Next());
+ if (opcode == Translation::JS_FRAME) {
+ i--;
+ BailoutId ast_id = BailoutId(it.Next());
+ JSFunction* function = LiteralAt(literal_array, it.Next());
+ it.Next(); // Skip height.
+
+ // The translation commands are ordered and the receiver is always
+ // at the first position. Since we are always at a call when we need
+ // to construct a stack trace, the receiver is always in a stack slot.
+ opcode = static_cast<Translation::Opcode>(it.Next());
+ ASSERT(opcode == Translation::STACK_SLOT ||
+ opcode == Translation::LITERAL);
+ int index = it.Next();
+
+ // Get the correct receiver in the optimized frame.
+ Object* receiver = NULL;
+ if (opcode == Translation::LITERAL) {
+ receiver = data->LiteralArray()->get(index);
+ } else {
+ // Positive index means the value is spilled to the locals
+ // area. Negative means it is stored in the incoming parameter
+ // area.
+ if (index >= 0) {
+ receiver = GetExpression(index);
+ } else {
+ // Index -1 overlaps with last parameter, -n with the first parameter,
+ // (-n - 1) with the receiver with n being the number of parameters
+ // of the outermost, optimized frame.
+ int parameter_count = ComputeParametersCount();
+ int parameter_index = index + parameter_count;
+ receiver = (parameter_index == -1)
+ ? this->receiver()
+ : this->GetParameter(parameter_index);
+ }
+ }
+
+ Code* code = function->shared()->code();
+ DeoptimizationOutputData* output_data =
+ DeoptimizationOutputData::cast(code->deoptimization_data());
+ unsigned entry = Deoptimizer::GetOutputInfo(output_data,
+ ast_id,
+ function->shared());
+ unsigned pc_offset =
+ FullCodeGenerator::PcField::decode(entry) + Code::kHeaderSize;
+ ASSERT(pc_offset > 0);
+
+ FrameSummary summary(receiver, function, code, pc_offset, is_constructor);
+ frames->Add(summary);
+ is_constructor = false;
+ } else if (opcode == Translation::CONSTRUCT_STUB_FRAME) {
+ // The next encountered JS_FRAME will be marked as a constructor call.
+ it.Skip(Translation::NumberOfOperandsFor(opcode));
+ ASSERT(!is_constructor);
+ is_constructor = true;
+ } else {
+ // Skip over operands to advance to the next opcode.
+ it.Skip(Translation::NumberOfOperandsFor(opcode));
+ }
+ }
+ ASSERT(!is_constructor);
+}
+
+
+DeoptimizationInputData* OptimizedFrame::GetDeoptimizationData(
+ int* deopt_index) {
+ ASSERT(is_optimized());
+
+ JSFunction* opt_function = function();
+ Code* code = opt_function->code();
+
+ // The code object may have been replaced by lazy deoptimization. Fall
+ // back to a slow search in this case to find the original optimized
+ // code object.
+ if (!code->contains(pc())) {
+ code = isolate()->inner_pointer_to_code_cache()->
+ GcSafeFindCodeForInnerPointer(pc());
+ }
+ ASSERT(code != NULL);
+ ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
+
+ SafepointEntry safepoint_entry = code->GetSafepointEntry(pc());
+ *deopt_index = safepoint_entry.deoptimization_index();
+ ASSERT(*deopt_index != Safepoint::kNoDeoptimizationIndex);
+
+ return DeoptimizationInputData::cast(code->deoptimization_data());
+}
+
+
+int OptimizedFrame::GetInlineCount() {
+ ASSERT(is_optimized());
+
+ int deopt_index = Safepoint::kNoDeoptimizationIndex;
+ DeoptimizationInputData* data = GetDeoptimizationData(&deopt_index);
+
+ TranslationIterator it(data->TranslationByteArray(),
+ data->TranslationIndex(deopt_index)->value());
+ Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
+ ASSERT(opcode == Translation::BEGIN);
+ USE(opcode);
+ it.Next(); // Drop frame count.
+ int jsframe_count = it.Next();
+ return jsframe_count;
+}
+
+
+void OptimizedFrame::GetFunctions(List<JSFunction*>* functions) {
+ ASSERT(functions->length() == 0);
+ ASSERT(is_optimized());
+
+ int deopt_index = Safepoint::kNoDeoptimizationIndex;
+ DeoptimizationInputData* data = GetDeoptimizationData(&deopt_index);
+ FixedArray* literal_array = data->LiteralArray();
+
+ TranslationIterator it(data->TranslationByteArray(),
+ data->TranslationIndex(deopt_index)->value());
+ Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
+ ASSERT(opcode == Translation::BEGIN);
+ 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 (jsframe_count > 0) {
+ opcode = static_cast<Translation::Opcode>(it.Next());
+ if (opcode == Translation::JS_FRAME) {
+ jsframe_count--;
+ it.Next(); // Skip ast id.
+ JSFunction* function = LiteralAt(literal_array, it.Next());
+ it.Next(); // Skip height.
+ functions->Add(function);
+ } else {
+ // Skip over operands to advance to the next opcode.
+ it.Skip(Translation::NumberOfOperandsFor(opcode));
+ }
+ }
+}
+
+
+int ArgumentsAdaptorFrame::GetNumberOfIncomingArguments() const {
+ return Smi::cast(GetExpression(0))->value();
+}
+
+
+Address ArgumentsAdaptorFrame::GetCallerStackPointer() const {
+ return fp() + StandardFrameConstants::kCallerSPOffset;
+}
+
+
+Address InternalFrame::GetCallerStackPointer() const {
+ // Internal frames have no arguments. The stack pointer of the
+ // caller is at a fixed offset from the frame pointer.
+ return fp() + StandardFrameConstants::kCallerSPOffset;
+}
+
+
+Code* ArgumentsAdaptorFrame::unchecked_code() const {
+ return isolate()->builtins()->builtin(
+ Builtins::kArgumentsAdaptorTrampoline);
+}
+
+
+Code* InternalFrame::unchecked_code() const {
+ const int offset = InternalFrameConstants::kCodeOffset;
+ Object* code = Memory::Object_at(fp() + offset);
+ ASSERT(code != NULL);
+ return reinterpret_cast<Code*>(code);
+}
+
+
+void StackFrame::PrintIndex(StringStream* accumulator,
+ PrintMode mode,
+ int index) {
+ accumulator->Add((mode == OVERVIEW) ? "%5d: " : "[%d]: ", index);
+}
+
+
+void JavaScriptFrame::Print(StringStream* accumulator,
+ PrintMode mode,
+ int index) const {
+ HandleScope scope(isolate());
+ Object* receiver = this->receiver();
+ JSFunction* function = this->function();
+
+ accumulator->PrintSecurityTokenIfChanged(function);
+ PrintIndex(accumulator, mode, index);
+ Code* code = NULL;
+ if (IsConstructor()) accumulator->Add("new ");
+ accumulator->PrintFunction(function, receiver, &code);
+
+ // 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(isolate()));
+
+ Handle<SharedFunctionInfo> shared(function->shared());
+ scope_info = Handle<ScopeInfo>(shared->scope_info());
+ Object* script_obj = shared->script();
+ if (script_obj->IsScript()) {
+ Handle<Script> script(Script::cast(script_obj));
+ accumulator->Add(" [");
+ accumulator->PrintName(script->name());
+
+ Address pc = this->pc();
+ if (code != NULL && code->kind() == Code::FUNCTION &&
+ pc >= code->instruction_start() && pc < code->instruction_end()) {
+ int source_pos = code->SourcePosition(pc);
+ int line = GetScriptLineNumberSafe(script, source_pos) + 1;
+ accumulator->Add(":%d", line);
+ } else {
+ int function_start_pos = shared->start_position();
+ int line = GetScriptLineNumberSafe(script, function_start_pos) + 1;
+ accumulator->Add(":~%d", line);
+ }
+
+ accumulator->Add("] ");
+ }
+
+ accumulator->Add("(this=%o", receiver);
+
+ // Print the parameters.
+ int parameters_count = ComputeParametersCount();
+ for (int i = 0; i < parameters_count; i++) {
+ accumulator->Add(",");
+ // 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 < scope_info->ParameterCount()) {
+ accumulator->PrintName(scope_info->ParameterName(i));
+ accumulator->Add("=");
+ }
+ accumulator->Add("%o", GetParameter(i));
+ }
+
+ accumulator->Add(")");
+ if (mode == OVERVIEW) {
+ accumulator->Add("\n");
+ return;
+ }
+ if (is_optimized()) {
+ accumulator->Add(" {\n// optimized frame\n}\n");
+ return;
+ }
+ accumulator->Add(" {\n");
+
+ // Compute the number of locals and expression stack elements.
+ int stack_locals_count = scope_info->StackLocalCount();
+ int heap_locals_count = scope_info->ContextLocalCount();
+ int expressions_count = ComputeExpressionsCount();
+
+ // Print stack-allocated local variables.
+ if (stack_locals_count > 0) {
+ accumulator->Add(" // stack-allocated locals\n");
+ }
+ for (int i = 0; i < stack_locals_count; i++) {
+ accumulator->Add(" var ");
+ accumulator->PrintName(scope_info->StackLocalName(i));
+ accumulator->Add(" = ");
+ if (i < expressions_count) {
+ accumulator->Add("%o", GetExpression(i));
+ } else {
+ accumulator->Add("// no expression found - inconsistent frame?");
+ }
+ accumulator->Add("\n");
+ }
+
+ // Try to get hold of the context of this frame.
+ Context* context = NULL;
+ if (this->context() != NULL && this->context()->IsContext()) {
+ context = Context::cast(this->context());
+ }
+
+ // Print heap-allocated local variables.
+ if (heap_locals_count > 0) {
+ accumulator->Add(" // heap-allocated locals\n");
+ }
+ for (int i = 0; i < heap_locals_count; i++) {
+ accumulator->Add(" var ");
+ accumulator->PrintName(scope_info->ContextLocalName(i));
+ accumulator->Add(" = ");
+ if (context != NULL) {
+ if (i < context->length()) {
+ accumulator->Add("%o", context->get(Context::MIN_CONTEXT_SLOTS + i));
+ } else {
+ accumulator->Add(
+ "// warning: missing context slot - inconsistent frame?");
+ }
+ } else {
+ accumulator->Add("// warning: no context found - inconsistent frame?");
+ }
+ accumulator->Add("\n");
+ }
+
+ // Print the expression stack.
+ int expressions_start = stack_locals_count;
+ if (expressions_start < expressions_count) {
+ accumulator->Add(" // expression stack (top to bottom)\n");
+ }
+ for (int i = expressions_count - 1; i >= expressions_start; i--) {
+ if (IsExpressionInsideHandler(i)) continue;
+ accumulator->Add(" [%02d] : %o\n", i, GetExpression(i));
+ }
+
+ // Print details about the function.
+ if (FLAG_max_stack_trace_source_length != 0 && code != NULL) {
+ SharedFunctionInfo* shared = function->shared();
+ accumulator->Add("--------- s o u r c e c o d e ---------\n");
+ shared->SourceCodePrint(accumulator, FLAG_max_stack_trace_source_length);
+ accumulator->Add("\n-----------------------------------------\n");
+ }
+
+ accumulator->Add("}\n\n");
+}
+
+
+void ArgumentsAdaptorFrame::Print(StringStream* accumulator,
+ PrintMode mode,
+ int index) const {
+ int actual = ComputeParametersCount();
+ int expected = -1;
+ JSFunction* function = this->function();
+ expected = function->shared()->formal_parameter_count();
+
+ PrintIndex(accumulator, mode, index);
+ accumulator->Add("arguments adaptor frame: %d->%d", actual, expected);
+ if (mode == OVERVIEW) {
+ accumulator->Add("\n");
+ return;
+ }
+ accumulator->Add(" {\n");
+
+ // Print actual arguments.
+ if (actual > 0) accumulator->Add(" // actual arguments\n");
+ for (int i = 0; i < actual; i++) {
+ accumulator->Add(" [%02d] : %o", i, GetParameter(i));
+ if (expected != -1 && i >= expected) {
+ accumulator->Add(" // not passed to callee");
+ }
+ accumulator->Add("\n");
+ }
+
+ accumulator->Add("}\n\n");
+}
+
+
+void EntryFrame::Iterate(ObjectVisitor* v) const {
+ StackHandlerIterator it(this, top_handler());
+ ASSERT(!it.done());
+ StackHandler* handler = it.handler();
+ ASSERT(handler->is_js_entry());
+ handler->Iterate(v, LookupCode());
+#ifdef DEBUG
+ // Make sure that the entry frame does not contain more than one
+ // stack handler.
+ it.Advance();
+ ASSERT(it.done());
+#endif
+ IteratePc(v, pc_address(), LookupCode());
+}
+
+
+void StandardFrame::IterateExpressions(ObjectVisitor* v) const {
+ const int offset = StandardFrameConstants::kContextOffset;
+ Object** base = &Memory::Object_at(sp());
+ Object** limit = &Memory::Object_at(fp() + offset) + 1;
+ for (StackHandlerIterator it(this, top_handler()); !it.done(); it.Advance()) {
+ StackHandler* handler = it.handler();
+ // Traverse pointers down to - but not including - the next
+ // handler in the handler chain. Update the base to skip the
+ // handler and allow the handler to traverse its own pointers.
+ const Address address = handler->address();
+ v->VisitPointers(base, reinterpret_cast<Object**>(address));
+ base = reinterpret_cast<Object**>(address + StackHandlerConstants::kSize);
+ // Traverse the pointers in the handler itself.
+ handler->Iterate(v, LookupCode());
+ }
+ v->VisitPointers(base, limit);
+}
+
+
+void JavaScriptFrame::Iterate(ObjectVisitor* v) const {
+ IterateExpressions(v);
+ IteratePc(v, pc_address(), LookupCode());
+}
+
+
+void InternalFrame::Iterate(ObjectVisitor* v) const {
+ // Internal frames only have object pointers on the expression stack
+ // as they never have any arguments.
+ IterateExpressions(v);
+ IteratePc(v, pc_address(), LookupCode());
+}
+
+
+void StubFailureTrampolineFrame::Iterate(ObjectVisitor* v) const {
+ Object** base = &Memory::Object_at(sp());
+ Object** limit = &Memory::Object_at(fp() +
+ kFirstRegisterParameterFrameOffset);
+ v->VisitPointers(base, limit);
+ base = &Memory::Object_at(fp() + StandardFrameConstants::kMarkerOffset);
+ const int offset = StandardFrameConstants::kContextOffset;
+ limit = &Memory::Object_at(fp() + offset) + 1;
+ v->VisitPointers(base, limit);
+ IteratePc(v, pc_address(), LookupCode());
+}
+
+
+Address StubFailureTrampolineFrame::GetCallerStackPointer() const {
+ return fp() + StandardFrameConstants::kCallerSPOffset;
+}
+
+
+Code* StubFailureTrampolineFrame::unchecked_code() const {
+ Code* trampoline;
+ StubFailureTrampolineStub(NOT_JS_FUNCTION_STUB_MODE).
+ FindCodeInCache(&trampoline, isolate());
+ if (trampoline->contains(pc())) {
+ return trampoline;
+ }
+
+ StubFailureTrampolineStub(JS_FUNCTION_STUB_MODE).
+ FindCodeInCache(&trampoline, isolate());
+ if (trampoline->contains(pc())) {
+ return trampoline;
+ }
+
+ UNREACHABLE();
+ return NULL;
+}
+
+
+// -------------------------------------------------------------------------
+
+
+JavaScriptFrame* StackFrameLocator::FindJavaScriptFrame(int n) {
+ ASSERT(n >= 0);
+ for (int i = 0; i <= n; i++) {
+ while (!iterator_.frame()->is_java_script()) iterator_.Advance();
+ if (i == n) return JavaScriptFrame::cast(iterator_.frame());
+ iterator_.Advance();
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+// -------------------------------------------------------------------------
+
+
+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 && GcSafeCodeContains(code, inner_pointer));
+ return code;
+}
+
+
+Code* InnerPointerToCodeCache::GcSafeFindCodeForInnerPointer(
+ Address inner_pointer) {
+ Heap* heap = isolate_->heap();
+ // Check if the inner pointer points into a large object chunk.
+ LargePage* large_page = heap->lo_space()->FindPage(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) {
+ if (addr == top && addr != limit) {
+ addr = limit;
+ continue;
+ }
+
+ 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;
+ }
+}
+
+
+InnerPointerToCodeCache::InnerPointerToCodeCacheEntry*
+ InnerPointerToCodeCache::GetCacheEntry(Address inner_pointer) {
+ isolate_->counters()->pc_to_code()->Increment();
+ ASSERT(IsPowerOf2(kInnerPointerToCodeCacheSize));
+ uint32_t hash = ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(inner_pointer)),
+ v8::internal::kZeroHashSeed);
+ 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 == GcSafeFindCodeForInnerPointer(inner_pointer));
+ } else {
+ // Because this code may be interrupted by a profiling signal that
+ // 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 = GcSafeFindCodeForInnerPointer(inner_pointer);
+ entry->safepoint_entry.Reset();
+ entry->inner_pointer = inner_pointer;
+ }
+ return entry;
+}
+
+
+// -------------------------------------------------------------------------
+
+
+void StackHandler::Unwind(Isolate* isolate,
+ FixedArray* array,
+ int offset,
+ int previous_handler_offset) const {
+ STATIC_ASSERT(StackHandlerConstants::kSlotCount >= 5);
+ ASSERT_LE(0, offset);
+ ASSERT_GE(array->length(), offset + StackHandlerConstants::kSlotCount);
+ // Unwinding a stack handler into an array chains it in the opposite
+ // direction, re-using the "next" slot as a "previous" link, so that stack
+ // handlers can be later re-wound in the correct order. Decode the "state"
+ // slot into "index" and "kind" and store them separately, using the fp slot.
+ array->set(offset, Smi::FromInt(previous_handler_offset)); // next
+ array->set(offset + 1, *code_address()); // code
+ array->set(offset + 2, Smi::FromInt(static_cast<int>(index()))); // state
+ array->set(offset + 3, *context_address()); // context
+ array->set(offset + 4, Smi::FromInt(static_cast<int>(kind()))); // fp
+
+ *isolate->handler_address() = next()->address();
+}
+
+
+int StackHandler::Rewind(Isolate* isolate,
+ FixedArray* array,
+ int offset,
+ Address fp) {
+ STATIC_ASSERT(StackHandlerConstants::kSlotCount >= 5);
+ ASSERT_LE(0, offset);
+ ASSERT_GE(array->length(), offset + StackHandlerConstants::kSlotCount);
+ Smi* prev_handler_offset = Smi::cast(array->get(offset));
+ Code* code = Code::cast(array->get(offset + 1));
+ Smi* smi_index = Smi::cast(array->get(offset + 2));
+ Object* context = array->get(offset + 3);
+ Smi* smi_kind = Smi::cast(array->get(offset + 4));
+
+ unsigned state = KindField::encode(static_cast<Kind>(smi_kind->value())) |
+ IndexField::encode(static_cast<unsigned>(smi_index->value()));
+
+ Memory::Address_at(address() + StackHandlerConstants::kNextOffset) =
+ *isolate->handler_address();
+ Memory::Object_at(address() + StackHandlerConstants::kCodeOffset) = code;
+ Memory::uintptr_at(address() + StackHandlerConstants::kStateOffset) = state;
+ Memory::Object_at(address() + StackHandlerConstants::kContextOffset) =
+ context;
+ SetFp(address() + StackHandlerConstants::kFPOffset, fp);
+
+ *isolate->handler_address() = address();
+
+ return prev_handler_offset->value();
+}
+
+
+// -------------------------------------------------------------------------
+
+int NumRegs(RegList reglist) {
+ return CompilerIntrinsics::CountSetBits(reglist);
+}
+
+
+struct JSCallerSavedCodeData {
+ int reg_code[kNumJSCallerSaved];
+};
+
+JSCallerSavedCodeData caller_saved_code_data;
+
+void SetUpJSCallerSavedCodeData() {
+ int i = 0;
+ for (int r = 0; r < kNumRegs; r++)
+ if ((kJSCallerSaved & (1 << r)) != 0)
+ caller_saved_code_data.reg_code[i++] = r;
+
+ ASSERT(i == kNumJSCallerSaved);
+}
+
+
+int JSCallerSavedCode(int n) {
+ ASSERT(0 <= n && n < kNumJSCallerSaved);
+ return caller_saved_code_data.reg_code[n];
+}
+
+
+#define DEFINE_WRAPPER(type, field) \
+class field##_Wrapper : public ZoneObject { \
+ public: /* NOLINT */ \
+ field##_Wrapper(const field& original) : frame_(original) { \
+ } \
+ field frame_; \
+};
+STACK_FRAME_TYPE_LIST(DEFINE_WRAPPER)
+#undef DEFINE_WRAPPER
+
+static StackFrame* AllocateFrameCopy(StackFrame* frame, Zone* zone) {
+#define FRAME_TYPE_CASE(type, field) \
+ case StackFrame::type: { \
+ field##_Wrapper* wrapper = \
+ new(zone) field##_Wrapper(*(reinterpret_cast<field*>(frame))); \
+ return &wrapper->frame_; \
+ }
+
+ switch (frame->type()) {
+ STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE)
+ default: UNREACHABLE();
+ }
+#undef FRAME_TYPE_CASE
+ return NULL;
+}
+
+
+Vector<StackFrame*> CreateStackMap(Isolate* isolate, Zone* zone) {
+ ZoneList<StackFrame*> list(10, zone);
+ for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
+ StackFrame* frame = AllocateFrameCopy(it.frame(), zone);
+ list.Add(frame, zone);
+ }
+ return list.ToVector();
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/frames.h b/chromium/v8/src/frames.h
new file mode 100644
index 00000000000..2bbbd98ac07
--- /dev/null
+++ b/chromium/v8/src/frames.h
@@ -0,0 +1,931 @@
+// Copyright 2012 the V8 project authors. 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_FRAMES_H_
+#define V8_FRAMES_H_
+
+#include "allocation.h"
+#include "handles.h"
+#include "safepoint-table.h"
+
+namespace v8 {
+namespace internal {
+
+typedef uint32_t RegList;
+
+// Get the number of registers in a given register list.
+int NumRegs(RegList list);
+
+void SetUpJSCallerSavedCodeData();
+
+// Return the code of the n-th saved register available to JavaScript.
+int JSCallerSavedCode(int n);
+
+
+// Forward declarations.
+class ExternalCallbackScope;
+class StackFrameIteratorBase;
+class ThreadLocalTop;
+class Isolate;
+
+class InnerPointerToCodeCache {
+ public:
+ struct InnerPointerToCodeCacheEntry {
+ Address inner_pointer;
+ Code* code;
+ SafepointEntry safepoint_entry;
+ };
+
+ explicit InnerPointerToCodeCache(Isolate* isolate) : isolate_(isolate) {
+ Flush();
+ }
+
+ Code* GcSafeFindCodeForInnerPointer(Address inner_pointer);
+ Code* GcSafeCastToCode(HeapObject* object, Address inner_pointer);
+
+ void Flush() {
+ memset(&cache_[0], 0, sizeof(cache_));
+ }
+
+ InnerPointerToCodeCacheEntry* GetCacheEntry(Address inner_pointer);
+
+ private:
+ InnerPointerToCodeCacheEntry* cache(int index) { return &cache_[index]; }
+
+ Isolate* isolate_;
+
+ static const int kInnerPointerToCodeCacheSize = 1024;
+ InnerPointerToCodeCacheEntry cache_[kInnerPointerToCodeCacheSize];
+
+ DISALLOW_COPY_AND_ASSIGN(InnerPointerToCodeCache);
+};
+
+
+class StackHandlerConstants : public AllStatic {
+ public:
+ 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 = kFPOffset + kFPOnStackSize;
+ static const int kSlotCount = kSize >> kPointerSizeLog2;
+};
+
+
+class StackHandler BASE_EMBEDDED {
+ public:
+ 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;
+
+ // Get the next stack handler in the chain.
+ inline StackHandler* next() const;
+
+ // Tells whether the given address is inside this handler.
+ inline bool includes(Address address) const;
+
+ // Garbage collection support.
+ inline void Iterate(ObjectVisitor* v, Code* holder) const;
+
+ // Conversion support.
+ static inline StackHandler* FromAddress(Address address);
+
+ // Testers
+ inline bool is_js_entry() const;
+ inline bool is_catch() const;
+ inline bool is_finally() const;
+
+ // Generator support to preserve stack handlers.
+ void Unwind(Isolate* isolate, FixedArray* array, int offset,
+ int previous_handler_offset) const;
+ int Rewind(Isolate* isolate, FixedArray* array, int offset, Address fp);
+
+ private:
+ // Accessors.
+ inline Kind kind() const;
+ inline unsigned index() const;
+
+ inline Object** context_address() const;
+ inline Object** code_address() const;
+ inline void SetFp(Address slot, Address fp);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler);
+};
+
+
+#define STACK_FRAME_TYPE_LIST(V) \
+ V(ENTRY, EntryFrame) \
+ V(ENTRY_CONSTRUCT, EntryConstructFrame) \
+ V(EXIT, ExitFrame) \
+ V(JAVA_SCRIPT, JavaScriptFrame) \
+ V(OPTIMIZED, OptimizedFrame) \
+ V(STUB, StubFrame) \
+ V(STUB_FAILURE_TRAMPOLINE, StubFailureTrampolineFrame) \
+ V(INTERNAL, InternalFrame) \
+ V(CONSTRUCT, ConstructFrame) \
+ V(ARGUMENTS_ADAPTOR, ArgumentsAdaptorFrame)
+
+
+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 = kPCOnStackSize + kFPOnStackSize +
+ 2 * kPointerSize;
+ static const int kExpressionsOffset = -3 * kPointerSize;
+ static const int kMarkerOffset = -2 * kPointerSize;
+ static const int kContextOffset = -1 * kPointerSize;
+ static const int kCallerFPOffset = 0 * kPointerSize;
+ static const int kCallerPCOffset = +1 * kFPOnStackSize;
+ static const int kCallerSPOffset = kCallerPCOffset + 1 * kPCOnStackSize;
+};
+
+
+// Abstract base class for all stack frames.
+class StackFrame BASE_EMBEDDED {
+ public:
+#define DECLARE_TYPE(type, ignore) type,
+ enum Type {
+ NONE = 0,
+ STACK_FRAME_TYPE_LIST(DECLARE_TYPE)
+ 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
+
+ // Opaque data type for identifying stack frames. Used extensively
+ // by the debugger.
+ // ID_MIN_VALUE and ID_MAX_VALUE are specified to ensure that enumeration type
+ // has correct value range (see Issue 830 for more details).
+ enum Id {
+ ID_MIN_VALUE = kMinInt,
+ ID_MAX_VALUE = kMaxInt,
+ NO_ID = 0
+ };
+
+ // Used to mark the outermost JS entry frame.
+ enum JsFrameMarker {
+ INNER_JSENTRY_FRAME = 0,
+ OUTERMOST_JSENTRY_FRAME = 1
+ };
+
+ struct State {
+ State() : sp(NULL), fp(NULL), pc_address(NULL) { }
+ Address sp;
+ Address fp;
+ Address* pc_address;
+ };
+
+ // Copy constructor; it breaks the connection to host iterator
+ // (as an iterator usually lives on stack).
+ StackFrame(const StackFrame& original) {
+ this->state_ = original.state_;
+ this->iterator_ = NULL;
+ this->isolate_ = original.isolate_;
+ }
+
+ // Type testers.
+ bool is_entry() const { return type() == ENTRY; }
+ bool is_entry_construct() const { return type() == ENTRY_CONSTRUCT; }
+ bool is_exit() const { return type() == EXIT; }
+ bool is_optimized() const { return type() == OPTIMIZED; }
+ bool is_arguments_adaptor() const { return type() == ARGUMENTS_ADAPTOR; }
+ bool is_internal() const { return type() == INTERNAL; }
+ bool is_stub_failure_trampoline() const {
+ return type() == STUB_FAILURE_TRAMPOLINE;
+ }
+ bool is_construct() const { return type() == CONSTRUCT; }
+ virtual bool is_standard() const { return false; }
+
+ bool is_java_script() const {
+ Type type = this->type();
+ return (type == JAVA_SCRIPT) || (type == OPTIMIZED);
+ }
+
+ // Accessors.
+ Address sp() const { return state_.sp; }
+ Address fp() const { return state_.fp; }
+ Address caller_sp() const { return GetCallerStackPointer(); }
+
+ // If this frame is optimized and was dynamically aligned return its old
+ // unaligned frame pointer. When the frame is deoptimized its FP will shift
+ // up one word and become unaligned.
+ Address UnpaddedFP() const;
+
+ Address pc() const { return *pc_address(); }
+ void set_pc(Address pc) { *pc_address() = pc; }
+
+ virtual void SetCallerFp(Address caller_fp) = 0;
+
+ // Manually changes value of fp in this object.
+ void UpdateFp(Address fp) { state_.fp = fp; }
+
+ Address* pc_address() const { return state_.pc_address; }
+
+ // Get the id of this stack frame.
+ Id id() const { return static_cast<Id>(OffsetFrom(caller_sp())); }
+
+ // Checks if this frame includes any stack handlers.
+ bool HasHandler() const;
+
+ // Get the type of this frame.
+ virtual Type type() const = 0;
+
+ // Get the code associated with this frame.
+ // This method could be called during marking phase of GC.
+ virtual Code* unchecked_code() const = 0;
+
+ // Get the code associated with this frame.
+ inline Code* LookupCode() const;
+
+ // Get the code object that contains the given pc.
+ static inline Code* GetContainingCode(Isolate* isolate, Address pc);
+
+ // Get the code object containing the given pc and fill in the
+ // safepoint entry and the number of stack slots. The pc must be at
+ // a safepoint.
+ static Code* GetSafepointData(Isolate* isolate,
+ Address pc,
+ SafepointEntry* safepoint_entry,
+ unsigned* stack_slots);
+
+ virtual void Iterate(ObjectVisitor* v) const = 0;
+ static void IteratePc(ObjectVisitor* v, Address* pc_address, Code* holder);
+
+ // Sets a callback function for return-address rewriting profilers
+ // to resolve the location of a return address to the location of the
+ // profiler's stashed return address.
+ static void SetReturnAddressLocationResolver(
+ ReturnAddressLocationResolver resolver);
+
+ // Resolves pc_address through the resolution address function if one is set.
+ static inline Address* ResolveReturnAddressLocation(Address* pc_address);
+
+
+ // Printing support.
+ enum PrintMode { OVERVIEW, DETAILS };
+ virtual void Print(StringStream* accumulator,
+ PrintMode mode,
+ int index) const { }
+
+ Isolate* isolate() const { return isolate_; }
+
+ protected:
+ inline explicit StackFrame(StackFrameIteratorBase* iterator);
+ virtual ~StackFrame() { }
+
+ // Compute the stack pointer for the calling frame.
+ virtual Address GetCallerStackPointer() const = 0;
+
+ // Printing support.
+ static void PrintIndex(StringStream* accumulator,
+ PrintMode mode,
+ int index);
+
+ // Get the top handler from the current stack iterator.
+ inline StackHandler* top_handler() const;
+
+ // Compute the stack frame type for the given state.
+ static Type ComputeType(const StackFrameIteratorBase* iterator, State* state);
+
+#ifdef DEBUG
+ bool can_access_heap_objects() const;
+#endif
+
+ private:
+ const StackFrameIteratorBase* iterator_;
+ Isolate* isolate_;
+ State state_;
+
+ static ReturnAddressLocationResolver return_address_location_resolver_;
+
+ // Fill in the state of the calling frame.
+ virtual void ComputeCallerState(State* state) const = 0;
+
+ // Get the type and the state of the calling frame.
+ virtual Type GetCallerState(State* state) const;
+
+ static const intptr_t kIsolateTag = 1;
+
+ friend class StackFrameIterator;
+ friend class StackFrameIteratorBase;
+ friend class StackHandlerIterator;
+ friend class SafeStackFrameIterator;
+
+ private:
+ void operator=(const StackFrame& original);
+};
+
+
+// Entry frames are used to enter JavaScript execution from C.
+class EntryFrame: public StackFrame {
+ public:
+ virtual Type type() const { return ENTRY; }
+
+ virtual Code* unchecked_code() const;
+
+ // Garbage collection support.
+ virtual void Iterate(ObjectVisitor* v) const;
+
+ static EntryFrame* cast(StackFrame* frame) {
+ ASSERT(frame->is_entry());
+ return static_cast<EntryFrame*>(frame);
+ }
+ virtual void SetCallerFp(Address caller_fp);
+
+ protected:
+ inline explicit EntryFrame(StackFrameIteratorBase* iterator);
+
+ // The caller stack pointer for entry frames is always zero. The
+ // real information about the caller frame is available through the
+ // link to the top exit frame.
+ virtual Address GetCallerStackPointer() const { return 0; }
+
+ private:
+ virtual void ComputeCallerState(State* state) const;
+ virtual Type GetCallerState(State* state) const;
+
+ friend class StackFrameIteratorBase;
+};
+
+
+class EntryConstructFrame: public EntryFrame {
+ public:
+ virtual Type type() const { return ENTRY_CONSTRUCT; }
+
+ virtual Code* unchecked_code() const;
+
+ static EntryConstructFrame* cast(StackFrame* frame) {
+ ASSERT(frame->is_entry_construct());
+ return static_cast<EntryConstructFrame*>(frame);
+ }
+
+ protected:
+ inline explicit EntryConstructFrame(StackFrameIteratorBase* iterator);
+
+ private:
+ friend class StackFrameIteratorBase;
+};
+
+
+// Exit frames are used to exit JavaScript execution and go to C.
+class ExitFrame: public StackFrame {
+ public:
+ virtual Type type() const { return EXIT; }
+
+ virtual Code* unchecked_code() const;
+
+ Object*& code_slot() const;
+
+ // Garbage collection support.
+ virtual void Iterate(ObjectVisitor* v) const;
+
+ virtual void SetCallerFp(Address caller_fp);
+
+ static ExitFrame* cast(StackFrame* frame) {
+ ASSERT(frame->is_exit());
+ return static_cast<ExitFrame*>(frame);
+ }
+
+ // Compute the state and type of an exit frame given a frame
+ // pointer. Used when constructing the first stack frame seen by an
+ // iterator and the frames following entry frames.
+ static Type GetStateForFramePointer(Address fp, State* state);
+ static Address ComputeStackPointer(Address fp);
+ static void FillState(Address fp, Address sp, State* state);
+
+ protected:
+ inline explicit ExitFrame(StackFrameIteratorBase* iterator);
+
+ virtual Address GetCallerStackPointer() const;
+
+ private:
+ virtual void ComputeCallerState(State* state) const;
+
+ friend class StackFrameIteratorBase;
+};
+
+
+class StandardFrame: public StackFrame {
+ public:
+ // Testers.
+ virtual bool is_standard() const { return true; }
+
+ // Accessors.
+ inline Object* context() const;
+
+ // Access the expressions in the stack frame including locals.
+ inline Object* GetExpression(int index) const;
+ inline void SetExpression(int index, Object* value);
+ int ComputeExpressionsCount() const;
+ static Object* GetExpression(Address fp, int index);
+
+ virtual void SetCallerFp(Address caller_fp);
+
+ static StandardFrame* cast(StackFrame* frame) {
+ ASSERT(frame->is_standard());
+ return static_cast<StandardFrame*>(frame);
+ }
+
+ protected:
+ inline explicit StandardFrame(StackFrameIteratorBase* iterator);
+
+ virtual void ComputeCallerState(State* state) const;
+
+ // Accessors.
+ inline Address caller_fp() const;
+ inline Address caller_pc() const;
+
+ // Computes the address of the PC field in the standard frame given
+ // by the provided frame pointer.
+ static inline Address ComputePCAddress(Address fp);
+
+ // Iterate over expression stack including stack handlers, locals,
+ // and parts of the fixed part including context and code fields.
+ void IterateExpressions(ObjectVisitor* v) const;
+
+ // Returns the address of the n'th expression stack element.
+ Address GetExpressionAddress(int n) const;
+ static Address GetExpressionAddress(Address fp, int n);
+
+ // Determines if the n'th expression stack element is in a stack
+ // handler or not. Requires traversing all handlers in this frame.
+ bool IsExpressionInsideHandler(int n) const;
+
+ // Determines if the standard frame for the given frame pointer is
+ // an arguments adaptor frame.
+ static inline bool IsArgumentsAdaptorFrame(Address fp);
+
+ // Determines if the standard frame for the given frame pointer is a
+ // construct frame.
+ static inline bool IsConstructFrame(Address fp);
+
+ // Used by OptimizedFrames and StubFrames.
+ void IterateCompiledFrame(ObjectVisitor* v) const;
+
+ private:
+ friend class StackFrame;
+ friend class SafeStackFrameIterator;
+};
+
+
+class FrameSummary BASE_EMBEDDED {
+ public:
+ FrameSummary(Object* receiver,
+ JSFunction* function,
+ Code* code,
+ int offset,
+ bool is_constructor)
+ : receiver_(receiver, function->GetIsolate()),
+ function_(function),
+ code_(code),
+ offset_(offset),
+ is_constructor_(is_constructor) { }
+ Handle<Object> receiver() { return receiver_; }
+ Handle<JSFunction> function() { return function_; }
+ Handle<Code> code() { return code_; }
+ Address pc() { return code_->address() + offset_; }
+ int offset() { return offset_; }
+ bool is_constructor() { return is_constructor_; }
+
+ void Print();
+
+ private:
+ Handle<Object> receiver_;
+ Handle<JSFunction> function_;
+ Handle<Code> code_;
+ int offset_;
+ bool is_constructor_;
+};
+
+
+class JavaScriptFrame: public StandardFrame {
+ public:
+ virtual Type type() const { return JAVA_SCRIPT; }
+
+ // Accessors.
+ inline JSFunction* function() const;
+ inline Object* receiver() const;
+ inline void set_receiver(Object* value);
+
+ // Access the parameters.
+ inline Address GetParameterSlot(int index) const;
+ inline Object* GetParameter(int index) const;
+ inline int ComputeParametersCount() const {
+ return GetNumberOfIncomingArguments();
+ }
+
+ // Access the operand stack.
+ inline Address GetOperandSlot(int index) const;
+ inline Object* GetOperand(int index) const;
+ inline int ComputeOperandsCount() const;
+
+ // Generator support to preserve operand stack and stack handlers.
+ void SaveOperandStack(FixedArray* store, int* stack_handler_index) const;
+ void RestoreOperandStack(FixedArray* store, int stack_handler_index);
+
+ // Debugger access.
+ void SetParameterValue(int index, Object* value) const;
+
+ // Check if this frame is a constructor frame invoked through 'new'.
+ bool IsConstructor() const;
+
+ // Check if this frame has "adapted" arguments in the sense that the
+ // actual passed arguments are available in an arguments adaptor
+ // frame below it on the stack.
+ inline bool has_adapted_arguments() const;
+ int GetArgumentsLength() const;
+
+ // Garbage collection support.
+ virtual void Iterate(ObjectVisitor* v) const;
+
+ // Printing support.
+ virtual void Print(StringStream* accumulator,
+ PrintMode mode,
+ int index) const;
+
+ // Determine the code for the frame.
+ virtual Code* unchecked_code() const;
+
+ // Returns the levels of inlining for this frame.
+ virtual int GetInlineCount() { return 1; }
+
+ // Return a list with JSFunctions of this frame.
+ virtual void GetFunctions(List<JSFunction*>* functions);
+
+ // Build a list with summaries for this frame including all inlined frames.
+ virtual void Summarize(List<FrameSummary>* frames);
+
+ // Architecture-specific register description.
+ static Register fp_register();
+ static Register context_register();
+
+ static JavaScriptFrame* cast(StackFrame* frame) {
+ ASSERT(frame->is_java_script());
+ return static_cast<JavaScriptFrame*>(frame);
+ }
+
+ static void PrintTop(Isolate* isolate,
+ FILE* file,
+ bool print_args,
+ bool print_line_number);
+
+ protected:
+ inline explicit JavaScriptFrame(StackFrameIteratorBase* iterator);
+
+ virtual Address GetCallerStackPointer() const;
+
+ virtual int GetNumberOfIncomingArguments() const;
+
+ // Garbage collection support. Iterates over incoming arguments,
+ // receiver, and any callee-saved registers.
+ void IterateArguments(ObjectVisitor* v) const;
+
+ private:
+ inline Object* function_slot_object() const;
+
+ friend class StackFrameIteratorBase;
+};
+
+
+class StubFrame : public StandardFrame {
+ public:
+ virtual Type type() const { return STUB; }
+
+ // GC support.
+ virtual void Iterate(ObjectVisitor* v) const;
+
+ // Determine the code for the frame.
+ virtual Code* unchecked_code() const;
+
+ protected:
+ inline explicit StubFrame(StackFrameIteratorBase* iterator);
+
+ virtual Address GetCallerStackPointer() const;
+
+ virtual int GetNumberOfIncomingArguments() const;
+
+ friend class StackFrameIteratorBase;
+};
+
+
+class OptimizedFrame : public JavaScriptFrame {
+ public:
+ virtual Type type() const { return OPTIMIZED; }
+
+ // GC support.
+ virtual void Iterate(ObjectVisitor* v) const;
+
+ virtual int GetInlineCount();
+
+ // Return a list with JSFunctions of this frame.
+ // The functions are ordered bottom-to-top (i.e. functions.last()
+ // is the top-most activation)
+ virtual void GetFunctions(List<JSFunction*>* functions);
+
+ virtual void Summarize(List<FrameSummary>* frames);
+
+ DeoptimizationInputData* GetDeoptimizationData(int* deopt_index);
+
+ protected:
+ inline explicit OptimizedFrame(StackFrameIteratorBase* iterator);
+
+ private:
+ JSFunction* LiteralAt(FixedArray* literal_array, int literal_id);
+
+ friend class StackFrameIteratorBase;
+};
+
+
+// Arguments adaptor frames are automatically inserted below
+// JavaScript frames when the actual number of parameters does not
+// match the formal number of parameters.
+class ArgumentsAdaptorFrame: public JavaScriptFrame {
+ public:
+ virtual Type type() const { return ARGUMENTS_ADAPTOR; }
+
+ // Determine the code for the frame.
+ virtual Code* unchecked_code() const;
+
+ static ArgumentsAdaptorFrame* cast(StackFrame* frame) {
+ ASSERT(frame->is_arguments_adaptor());
+ return static_cast<ArgumentsAdaptorFrame*>(frame);
+ }
+
+ // Printing support.
+ virtual void Print(StringStream* accumulator,
+ PrintMode mode,
+ int index) const;
+
+ protected:
+ inline explicit ArgumentsAdaptorFrame(StackFrameIteratorBase* iterator);
+
+ virtual int GetNumberOfIncomingArguments() const;
+
+ virtual Address GetCallerStackPointer() const;
+
+ private:
+ friend class StackFrameIteratorBase;
+};
+
+
+class InternalFrame: public StandardFrame {
+ public:
+ virtual Type type() const { return INTERNAL; }
+
+ // Garbage collection support.
+ virtual void Iterate(ObjectVisitor* v) const;
+
+ // Determine the code for the frame.
+ virtual Code* unchecked_code() const;
+
+ static InternalFrame* cast(StackFrame* frame) {
+ ASSERT(frame->is_internal());
+ return static_cast<InternalFrame*>(frame);
+ }
+
+ protected:
+ inline explicit InternalFrame(StackFrameIteratorBase* iterator);
+
+ virtual Address GetCallerStackPointer() const;
+
+ private:
+ friend class StackFrameIteratorBase;
+};
+
+
+class StubFailureTrampolineFrame: public StandardFrame {
+ public:
+ // sizeof(Arguments) - sizeof(Arguments*) is 3 * kPointerSize), but the
+ // presubmit script complains about using sizeof() on a type.
+ static const int kFirstRegisterParameterFrameOffset =
+ StandardFrameConstants::kMarkerOffset - 3 * kPointerSize;
+
+ static const int kCallerStackParameterCountFrameOffset =
+ StandardFrameConstants::kMarkerOffset - 2 * kPointerSize;
+
+ virtual Type type() const { return STUB_FAILURE_TRAMPOLINE; }
+
+ // Get the code associated with this frame.
+ // This method could be called during marking phase of GC.
+ virtual Code* unchecked_code() const;
+
+ virtual void Iterate(ObjectVisitor* v) const;
+
+ // Architecture-specific register description.
+ static Register fp_register();
+ static Register context_register();
+
+ protected:
+ inline explicit StubFailureTrampolineFrame(
+ StackFrameIteratorBase* iterator);
+
+ virtual Address GetCallerStackPointer() const;
+
+ private:
+ friend class StackFrameIteratorBase;
+};
+
+
+// Construct frames are special trampoline frames introduced to handle
+// function invocations through 'new'.
+class ConstructFrame: public InternalFrame {
+ public:
+ virtual Type type() const { return CONSTRUCT; }
+
+ static ConstructFrame* cast(StackFrame* frame) {
+ ASSERT(frame->is_construct());
+ return static_cast<ConstructFrame*>(frame);
+ }
+
+ protected:
+ inline explicit ConstructFrame(StackFrameIteratorBase* iterator);
+
+ private:
+ friend class StackFrameIteratorBase;
+};
+
+
+class StackFrameIteratorBase BASE_EMBEDDED {
+ public:
+ Isolate* isolate() const { return isolate_; }
+
+ bool done() const { return frame_ == NULL; }
+
+ protected:
+ // An iterator that iterates over a given thread's stack.
+ StackFrameIteratorBase(Isolate* isolate, bool can_access_heap_objects);
+
+ Isolate* isolate_;
+#define DECLARE_SINGLETON(ignore, type) type type##_;
+ STACK_FRAME_TYPE_LIST(DECLARE_SINGLETON)
+#undef DECLARE_SINGLETON
+ StackFrame* frame_;
+ StackHandler* handler_;
+ const bool can_access_heap_objects_;
+
+ StackHandler* handler() const {
+ ASSERT(!done());
+ return handler_;
+ }
+
+ // Get the type-specific frame singleton in a given state.
+ StackFrame* SingletonFor(StackFrame::Type type, StackFrame::State* state);
+ // A helper function, can return a NULL pointer.
+ StackFrame* SingletonFor(StackFrame::Type type);
+
+ private:
+ friend class StackFrame;
+ DISALLOW_COPY_AND_ASSIGN(StackFrameIteratorBase);
+};
+
+
+class StackFrameIterator: public StackFrameIteratorBase {
+ public:
+ // An iterator that iterates over the isolate's current thread's stack,
+ explicit StackFrameIterator(Isolate* isolate);
+ // An iterator that iterates over a given thread's stack.
+ StackFrameIterator(Isolate* isolate, ThreadLocalTop* t);
+
+ StackFrame* frame() const {
+ ASSERT(!done());
+ return frame_;
+ }
+ void Advance();
+
+ private:
+ // Go back to the first frame.
+ void Reset(ThreadLocalTop* top);
+
+ DISALLOW_COPY_AND_ASSIGN(StackFrameIterator);
+};
+
+
+// Iterator that supports iterating through all JavaScript frames.
+class JavaScriptFrameIterator BASE_EMBEDDED {
+ public:
+ inline explicit JavaScriptFrameIterator(Isolate* isolate);
+ inline JavaScriptFrameIterator(Isolate* isolate, ThreadLocalTop* top);
+ // Skip frames until the frame with the given id is reached.
+ JavaScriptFrameIterator(Isolate* isolate, StackFrame::Id id);
+
+ inline JavaScriptFrame* frame() const;
+
+ bool done() const { return iterator_.done(); }
+ void Advance();
+
+ // Advance to the frame holding the arguments for the current
+ // frame. This only affects the current frame if it has adapted
+ // arguments.
+ void AdvanceToArgumentsFrame();
+
+ private:
+ StackFrameIterator iterator_;
+};
+
+
+// NOTE: The stack trace frame iterator is an iterator that only
+// traverse proper JavaScript frames; that is JavaScript frames that
+// have proper JavaScript functions. This excludes the problematic
+// functions in runtime.js.
+class StackTraceFrameIterator: public JavaScriptFrameIterator {
+ public:
+ explicit StackTraceFrameIterator(Isolate* isolate);
+ void Advance();
+
+ private:
+ bool IsValidFrame();
+};
+
+
+class SafeStackFrameIterator: public StackFrameIteratorBase {
+ public:
+ SafeStackFrameIterator(Isolate* isolate,
+ Address fp, Address sp,
+ Address js_entry_sp);
+
+ inline StackFrame* frame() const;
+ void Advance();
+
+ StackFrame::Type top_frame_type() const { return top_frame_type_; }
+
+ private:
+ void AdvanceOneFrame();
+
+ bool IsValidStackAddress(Address addr) const {
+ return low_bound_ <= addr && addr <= high_bound_;
+ }
+ bool IsValidFrame(StackFrame* frame) const;
+ bool IsValidCaller(StackFrame* frame);
+ bool IsValidExitFrame(Address fp) const;
+ bool IsValidTop(ThreadLocalTop* top) const;
+
+ const Address low_bound_;
+ const Address high_bound_;
+ StackFrame::Type top_frame_type_;
+ ExternalCallbackScope* external_callback_scope_;
+};
+
+
+class StackFrameLocator BASE_EMBEDDED {
+ public:
+ explicit StackFrameLocator(Isolate* isolate) : iterator_(isolate) {}
+
+ // Find the nth JavaScript frame on the stack. The caller must
+ // guarantee that such a frame exists.
+ JavaScriptFrame* FindJavaScriptFrame(int n);
+
+ private:
+ StackFrameIterator iterator_;
+};
+
+
+// Reads all frames on the current stack and copies them into the current
+// zone memory.
+Vector<StackFrame*> CreateStackMap(Isolate* isolate, Zone* zone);
+
+} } // namespace v8::internal
+
+#endif // V8_FRAMES_H_
diff --git a/chromium/v8/src/full-codegen.cc b/chromium/v8/src/full-codegen.cc
new file mode 100644
index 00000000000..f5539e8b187
--- /dev/null
+++ b/chromium/v8/src/full-codegen.cc
@@ -0,0 +1,1625 @@
+// Copyright 2012 the V8 project authors. 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 "codegen.h"
+#include "compiler.h"
+#include "debug.h"
+#include "full-codegen.h"
+#include "liveedit.h"
+#include "macro-assembler.h"
+#include "prettyprinter.h"
+#include "scopes.h"
+#include "scopeinfo.h"
+#include "snapshot.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+void BreakableStatementChecker::Check(Statement* stmt) {
+ Visit(stmt);
+}
+
+
+void BreakableStatementChecker::Check(Expression* expr) {
+ Visit(expr);
+}
+
+
+void BreakableStatementChecker::VisitVariableDeclaration(
+ VariableDeclaration* decl) {
+}
+
+void BreakableStatementChecker::VisitFunctionDeclaration(
+ FunctionDeclaration* decl) {
+}
+
+void BreakableStatementChecker::VisitModuleDeclaration(
+ ModuleDeclaration* decl) {
+}
+
+void BreakableStatementChecker::VisitImportDeclaration(
+ ImportDeclaration* decl) {
+}
+
+void BreakableStatementChecker::VisitExportDeclaration(
+ ExportDeclaration* decl) {
+}
+
+
+void BreakableStatementChecker::VisitModuleLiteral(ModuleLiteral* module) {
+}
+
+
+void BreakableStatementChecker::VisitModuleVariable(ModuleVariable* module) {
+}
+
+
+void BreakableStatementChecker::VisitModulePath(ModulePath* module) {
+}
+
+
+void BreakableStatementChecker::VisitModuleUrl(ModuleUrl* module) {
+}
+
+
+void BreakableStatementChecker::VisitModuleStatement(ModuleStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitBlock(Block* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitExpressionStatement(
+ ExpressionStatement* stmt) {
+ // Check if expression is breakable.
+ Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitEmptyStatement(EmptyStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitIfStatement(IfStatement* stmt) {
+ // If the condition is breakable the if statement is breakable.
+ Visit(stmt->condition());
+}
+
+
+void BreakableStatementChecker::VisitContinueStatement(
+ ContinueStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitBreakStatement(BreakStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitReturnStatement(ReturnStatement* stmt) {
+ // Return is breakable if the expression is.
+ Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitWithStatement(WithStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitSwitchStatement(SwitchStatement* stmt) {
+ // Switch statements breakable if the tag expression is.
+ Visit(stmt->tag());
+}
+
+
+void BreakableStatementChecker::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ // Mark do while as breakable to avoid adding a break slot in front of it.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitWhileStatement(WhileStatement* stmt) {
+ // Mark while statements breakable if the condition expression is.
+ Visit(stmt->cond());
+}
+
+
+void BreakableStatementChecker::VisitForStatement(ForStatement* stmt) {
+ // Mark for statements breakable if the condition expression is.
+ if (stmt->cond() != NULL) {
+ Visit(stmt->cond());
+ }
+}
+
+
+void BreakableStatementChecker::VisitForInStatement(ForInStatement* stmt) {
+ // Mark for in statements breakable if the enumerable expression is.
+ Visit(stmt->enumerable());
+}
+
+
+void BreakableStatementChecker::VisitForOfStatement(ForOfStatement* stmt) {
+ // For-of is breakable because of the next() call.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitTryCatchStatement(
+ TryCatchStatement* stmt) {
+ // Mark try catch as breakable to avoid adding a break slot in front of it.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitTryFinallyStatement(
+ TryFinallyStatement* stmt) {
+ // Mark try finally as breakable to avoid adding a break slot in front of it.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitDebuggerStatement(
+ DebuggerStatement* stmt) {
+ // The debugger statement is breakable.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitFunctionLiteral(FunctionLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitConditional(Conditional* expr) {
+}
+
+
+void BreakableStatementChecker::VisitVariableProxy(VariableProxy* expr) {
+}
+
+
+void BreakableStatementChecker::VisitLiteral(Literal* expr) {
+}
+
+
+void BreakableStatementChecker::VisitRegExpLiteral(RegExpLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitObjectLiteral(ObjectLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitArrayLiteral(ArrayLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitAssignment(Assignment* expr) {
+ // If assigning to a property (including a global property) the assignment is
+ // breakable.
+ VariableProxy* proxy = expr->target()->AsVariableProxy();
+ Property* prop = expr->target()->AsProperty();
+ if (prop != NULL || (proxy != NULL && proxy->var()->IsUnallocated())) {
+ is_breakable_ = true;
+ return;
+ }
+
+ // Otherwise the assignment is breakable if the assigned value is.
+ Visit(expr->value());
+}
+
+
+void BreakableStatementChecker::VisitYield(Yield* expr) {
+ // Yield is breakable if the expression is.
+ Visit(expr->expression());
+}
+
+
+void BreakableStatementChecker::VisitThrow(Throw* expr) {
+ // Throw is breakable if the expression is.
+ Visit(expr->exception());
+}
+
+
+void BreakableStatementChecker::VisitProperty(Property* expr) {
+ // Property load is breakable.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCall(Call* expr) {
+ // Function calls both through IC and call stub are breakable.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCallNew(CallNew* expr) {
+ // Function calls through new are breakable.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCallRuntime(CallRuntime* expr) {
+}
+
+
+void BreakableStatementChecker::VisitUnaryOperation(UnaryOperation* expr) {
+ Visit(expr->expression());
+}
+
+
+void BreakableStatementChecker::VisitCountOperation(CountOperation* expr) {
+ Visit(expr->expression());
+}
+
+
+void BreakableStatementChecker::VisitBinaryOperation(BinaryOperation* expr) {
+ Visit(expr->left());
+ if (expr->op() != Token::AND &&
+ expr->op() != Token::OR) {
+ Visit(expr->right());
+ }
+}
+
+
+void BreakableStatementChecker::VisitCompareOperation(CompareOperation* expr) {
+ Visit(expr->left());
+ Visit(expr->right());
+}
+
+
+void BreakableStatementChecker::VisitThisFunction(ThisFunction* expr) {
+}
+
+
+#define __ ACCESS_MASM(masm())
+
+bool FullCodeGenerator::MakeCode(CompilationInfo* info) {
+ Isolate* isolate = info->isolate();
+ Handle<Script> script = info->script();
+ if (!script->IsUndefined() && !script->source()->IsUndefined()) {
+ int len = String::cast(script->source())->length();
+ isolate->counters()->total_full_codegen_source_size()->Increment(len);
+ }
+ CodeGenerator::MakeCodePrologue(info, "full");
+ const int kInitialBufferSize = 4 * KB;
+ MacroAssembler masm(info->isolate(), NULL, kInitialBufferSize);
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ masm.positions_recorder()->StartGDBJITLineInfoRecording();
+#endif
+ LOG_CODE_EVENT(isolate,
+ CodeStartLinePosInfoRecordEvent(masm.positions_recorder()));
+
+ FullCodeGenerator cgen(&masm, info);
+ cgen.Generate();
+ if (cgen.HasStackOverflow()) {
+ ASSERT(!isolate->has_pending_exception());
+ return false;
+ }
+ unsigned table_offset = cgen.EmitBackEdgeTable();
+
+ Code::Flags flags = Code::ComputeFlags(Code::FUNCTION);
+ Handle<Code> code = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
+ code->set_optimizable(info->IsOptimizable() &&
+ !info->function()->flags()->Contains(kDontOptimize) &&
+ info->function()->scope()->AllowsLazyCompilation());
+ cgen.PopulateDeoptimizationData(code);
+ cgen.PopulateTypeFeedbackInfo(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_profiler_ticks(0);
+ code->set_back_edge_table_offset(table_offset);
+ code->set_back_edges_patched_for_osr(false);
+ CodeGenerator::PrintCode(code, info);
+ info->SetCode(code); // May be an empty handle.
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ if (FLAG_gdbjit && !code.is_null()) {
+ GDBJITLineInfo* lineinfo =
+ masm.positions_recorder()->DetachGDBJITLineInfo();
+
+ GDBJIT(RegisterDetailedLineInfo(*code, lineinfo));
+ }
+#endif
+ if (!code.is_null()) {
+ void* line_info =
+ masm.positions_recorder()->DetachJITHandlerData();
+ LOG_CODE_EVENT(isolate, CodeEndLinePosInfoRecordEvent(*code, line_info));
+ }
+ return !code.is_null();
+}
+
+
+unsigned FullCodeGenerator::EmitBackEdgeTable() {
+ // The back edge table consists of a length (in number of entries)
+ // field, and then a sequence of entries. Each entry is a pair of AST id
+ // and code-relative pc offset.
+ masm()->Align(kIntSize);
+ unsigned offset = masm()->pc_offset();
+ unsigned length = back_edges_.length();
+ __ dd(length);
+ for (unsigned i = 0; i < length; ++i) {
+ __ dd(back_edges_[i].id.ToInt());
+ __ dd(back_edges_[i].pc);
+ __ dd(back_edges_[i].loop_depth);
+ }
+ return offset;
+}
+
+
+void FullCodeGenerator::PopulateDeoptimizationData(Handle<Code> code) {
+ // Fill in the deoptimization information.
+ ASSERT(info_->HasDeoptimizationSupport() || bailout_entries_.is_empty());
+ if (!info_->HasDeoptimizationSupport()) return;
+ int length = bailout_entries_.length();
+ Handle<DeoptimizationOutputData> data = isolate()->factory()->
+ NewDeoptimizationOutputData(length, TENURED);
+ for (int i = 0; i < length; i++) {
+ data->SetAstId(i, bailout_entries_[i].id);
+ data->SetPcAndState(i, Smi::FromInt(bailout_entries_[i].pc_and_state));
+ }
+ code->set_deoptimization_data(*data);
+}
+
+
+void FullCodeGenerator::PopulateTypeFeedbackInfo(Handle<Code> code) {
+ Handle<TypeFeedbackInfo> info = isolate()->factory()->NewTypeFeedbackInfo();
+ info->set_ic_total_count(ic_total_count_);
+ ASSERT(!isolate()->heap()->InNewSpace(*info));
+ code->set_type_feedback_info(*info);
+}
+
+
+void FullCodeGenerator::Initialize() {
+ // The generation of debug code must match between the snapshot code and the
+ // code that is generated later. This is assumed by the debugger when it is
+ // calculating PC offsets after generating a debug version of code. Therefore
+ // we disable the production of debug code in the full compiler if we are
+ // either generating a snapshot or we booted from a snapshot.
+ generate_debug_code_ = FLAG_debug_code &&
+ !Serializer::enabled() &&
+ !Snapshot::HaveASnapshotToStartFrom();
+ masm_->set_emit_debug_code(generate_debug_code_);
+ masm_->set_predictable_code_size(true);
+ InitializeAstVisitor();
+}
+
+
+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, type_feedback_cells_[i].ast_id);
+ cache->SetCell(i, *type_feedback_cells_[i].cell);
+ }
+ TypeFeedbackInfo::cast(code->type_feedback_info())->set_type_feedback_cells(
+ *cache);
+}
+
+
+
+void FullCodeGenerator::PrepareForBailout(Expression* node, State state) {
+ PrepareForBailoutForId(node->id(), state);
+}
+
+
+void FullCodeGenerator::RecordJSReturnSite(Call* call) {
+ // We record the offset of the function return so we can rebuild the frame
+ // if the function was inlined, i.e., this is the return address in the
+ // inlined function's frame.
+ //
+ // The state is ignored. We defensively set it to TOS_REG, which is the
+ // real state of the unoptimized code at the return site.
+ PrepareForBailoutForId(call->ReturnId(), TOS_REG);
+#ifdef DEBUG
+ // In debug builds, mark the return so we can verify that this function
+ // was called.
+ ASSERT(!call->return_is_recorded_);
+ call->return_is_recorded_ = true;
+#endif
+}
+
+
+void FullCodeGenerator::PrepareForBailoutForId(BailoutId 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 (!info_->HasDeoptimizationSupport()) return;
+ unsigned pc_and_state =
+ StateField::encode(state) | PcField::encode(masm_->pc_offset());
+ ASSERT(Smi::IsValid(pc_and_state));
+ BailoutEntry entry = { id, pc_and_state };
+ ASSERT(!prepared_bailout_ids_.Contains(id.ToInt()));
+ prepared_bailout_ids_.Add(id.ToInt(), zone());
+ bailout_entries_.Add(entry, zone());
+}
+
+
+void FullCodeGenerator::RecordTypeFeedbackCell(
+ TypeFeedbackId id, Handle<Cell> cell) {
+ TypeFeedbackCellEntry entry = { id, cell };
+ type_feedback_cells_.Add(entry, zone());
+}
+
+
+void FullCodeGenerator::RecordBackEdge(BailoutId ast_id) {
+ // The pc offset does not need to be encoded and packed together with a state.
+ ASSERT(masm_->pc_offset() > 0);
+ ASSERT(loop_depth() > 0);
+ uint8_t depth = Min(loop_depth(), Code::kMaxLoopNestingMarker);
+ BackEdgeEntry entry =
+ { ast_id, static_cast<unsigned>(masm_->pc_offset()), depth };
+ back_edges_.Add(entry, zone());
+}
+
+
+bool FullCodeGenerator::ShouldInlineSmiCase(Token::Value op) {
+ // Inline smi case inside loops, but not division and modulo which
+ // are too complicated and take up too much space.
+ if (op == Token::DIV ||op == Token::MOD) return false;
+ if (FLAG_always_inline_smi_code) return true;
+ return loop_depth_ > 0;
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Register reg) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(Register reg) const {
+ __ Move(result_register(), reg);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Register reg) const {
+ __ Push(reg);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Register reg) const {
+ // For simplicity we always test the accumulator register.
+ __ Move(result_register(), reg);
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::PlugTOS() const {
+ __ Drop(1);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::PlugTOS() const {
+ __ Pop(result_register());
+}
+
+
+void FullCodeGenerator::StackValueContext::PlugTOS() const {
+}
+
+
+void FullCodeGenerator::TestContext::PlugTOS() const {
+ // For simplicity we always test the accumulator register.
+ __ Pop(result_register());
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::PrepareTest(
+ Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const {
+ // In an effect context, the true and the false case branch to the
+ // same label.
+ *if_true = *if_false = *fall_through = materialize_true;
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::PrepareTest(
+ Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const {
+ *if_true = *fall_through = materialize_true;
+ *if_false = materialize_false;
+}
+
+
+void FullCodeGenerator::StackValueContext::PrepareTest(
+ Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const {
+ *if_true = *fall_through = materialize_true;
+ *if_false = materialize_false;
+}
+
+
+void FullCodeGenerator::TestContext::PrepareTest(
+ Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const {
+ *if_true = true_label_;
+ *if_false = false_label_;
+ *fall_through = fall_through_;
+}
+
+
+void FullCodeGenerator::DoTest(const TestContext* context) {
+ DoTest(context->condition(),
+ context->true_label(),
+ context->false_label(),
+ context->fall_through());
+}
+
+
+void FullCodeGenerator::AllocateModules(ZoneList<Declaration*>* declarations) {
+ ASSERT(scope_->is_global_scope());
+
+ for (int i = 0; i < declarations->length(); i++) {
+ ModuleDeclaration* declaration = declarations->at(i)->AsModuleDeclaration();
+ if (declaration != NULL) {
+ ModuleLiteral* module = declaration->module()->AsModuleLiteral();
+ if (module != NULL) {
+ Comment cmnt(masm_, "[ Link nested modules");
+ Scope* scope = module->body()->scope();
+ Interface* interface = scope->interface();
+ ASSERT(interface->IsModule() && interface->IsFrozen());
+
+ interface->Allocate(scope->module_var()->index());
+
+ // Set up module context.
+ ASSERT(scope->interface()->Index() >= 0);
+ __ Push(Smi::FromInt(scope->interface()->Index()));
+ __ Push(scope->GetScopeInfo());
+ __ CallRuntime(Runtime::kPushModuleContext, 2);
+ StoreToFrameField(StandardFrameConstants::kContextOffset,
+ context_register());
+
+ AllocateModules(scope->declarations());
+
+ // Pop module context.
+ LoadContextField(context_register(), Context::PREVIOUS_INDEX);
+ // Update local stack frame context field.
+ StoreToFrameField(StandardFrameConstants::kContextOffset,
+ context_register());
+ }
+ }
+ }
+}
+
+
+// Modules have their own local scope, represented by their own context.
+// Module instance objects have an accessor for every export that forwards
+// access to the respective slot from the module's context. (Exports that are
+// modules themselves, however, are simple data properties.)
+//
+// All modules have a _hosting_ scope/context, which (currently) is the
+// (innermost) enclosing global scope. To deal with recursion, nested modules
+// are hosted by the same scope as global ones.
+//
+// For every (global or nested) module literal, the hosting context has an
+// internal slot that points directly to the respective module context. This
+// enables quick access to (statically resolved) module members by 2-dimensional
+// access through the hosting context. For example,
+//
+// module A {
+// let x;
+// module B { let y; }
+// }
+// module C { let z; }
+//
+// allocates contexts as follows:
+//
+// [header| .A | .B | .C | A | C ] (global)
+// | | |
+// | | +-- [header| z ] (module)
+// | |
+// | +------- [header| y ] (module)
+// |
+// +------------ [header| x | B ] (module)
+//
+// Here, .A, .B, .C are the internal slots pointing to the hosted module
+// contexts, whereas A, B, C hold the actual instance objects (note that every
+// module context also points to the respective instance object through its
+// extension slot in the header).
+//
+// To deal with arbitrary recursion and aliases between modules,
+// they are created and initialized in several stages. Each stage applies to
+// all modules in the hosting global scope, including nested ones.
+//
+// 1. Allocate: for each module _literal_, allocate the module contexts and
+// respective instance object and wire them up. This happens in the
+// PushModuleContext runtime function, as generated by AllocateModules
+// (invoked by VisitDeclarations in the hosting scope).
+//
+// 2. Bind: for each module _declaration_ (i.e. literals as well as aliases),
+// assign the respective instance object to respective local variables. This
+// happens in VisitModuleDeclaration, and uses the instance objects created
+// in the previous stage.
+// For each module _literal_, this phase also constructs a module descriptor
+// for the next stage. This happens in VisitModuleLiteral.
+//
+// 3. Populate: invoke the DeclareModules runtime function to populate each
+// _instance_ object with accessors for it exports. This is generated by
+// DeclareModules (invoked by VisitDeclarations in the hosting scope again),
+// and uses the descriptors generated in the previous stage.
+//
+// 4. Initialize: execute the module bodies (and other code) in sequence. This
+// happens by the separate statements generated for module bodies. To reenter
+// the module scopes properly, the parser inserted ModuleStatements.
+
+void FullCodeGenerator::VisitDeclarations(
+ ZoneList<Declaration*>* declarations) {
+ Handle<FixedArray> saved_modules = modules_;
+ int saved_module_index = module_index_;
+ ZoneList<Handle<Object> >* saved_globals = globals_;
+ ZoneList<Handle<Object> > inner_globals(10, zone());
+ globals_ = &inner_globals;
+
+ if (scope_->num_modules() != 0) {
+ // This is a scope hosting modules. Allocate a descriptor array to pass
+ // to the runtime for initialization.
+ Comment cmnt(masm_, "[ Allocate modules");
+ ASSERT(scope_->is_global_scope());
+ modules_ =
+ isolate()->factory()->NewFixedArray(scope_->num_modules(), TENURED);
+ module_index_ = 0;
+
+ // Generate code for allocating all modules, including nested ones.
+ // The allocated contexts are stored in internal variables in this scope.
+ AllocateModules(declarations);
+ }
+
+ AstVisitor::VisitDeclarations(declarations);
+
+ if (scope_->num_modules() != 0) {
+ // Initialize modules from descriptor array.
+ ASSERT(module_index_ == modules_->length());
+ DeclareModules(modules_);
+ modules_ = saved_modules;
+ module_index_ = saved_module_index;
+ }
+
+ if (!globals_->is_empty()) {
+ // Invoke the platform-dependent code generator to do the actual
+ // declaration of the global functions and variables.
+ Handle<FixedArray> array =
+ isolate()->factory()->NewFixedArray(globals_->length(), TENURED);
+ for (int i = 0; i < globals_->length(); ++i)
+ array->set(i, *globals_->at(i));
+ DeclareGlobals(array);
+ }
+
+ globals_ = saved_globals;
+}
+
+
+void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) {
+ Block* block = module->body();
+ Scope* saved_scope = scope();
+ scope_ = block->scope();
+ Interface* interface = scope_->interface();
+
+ Comment cmnt(masm_, "[ ModuleLiteral");
+ SetStatementPosition(block);
+
+ ASSERT(!modules_.is_null());
+ ASSERT(module_index_ < modules_->length());
+ int index = module_index_++;
+
+ // Set up module context.
+ ASSERT(interface->Index() >= 0);
+ __ Push(Smi::FromInt(interface->Index()));
+ __ Push(Smi::FromInt(0));
+ __ CallRuntime(Runtime::kPushModuleContext, 2);
+ StoreToFrameField(StandardFrameConstants::kContextOffset, context_register());
+
+ {
+ Comment cmnt(masm_, "[ Declarations");
+ VisitDeclarations(scope_->declarations());
+ }
+
+ // Populate the module description.
+ Handle<ModuleInfo> description =
+ ModuleInfo::Create(isolate(), interface, scope_);
+ modules_->set(index, *description);
+
+ scope_ = saved_scope;
+ // Pop module context.
+ LoadContextField(context_register(), Context::PREVIOUS_INDEX);
+ // Update local stack frame context field.
+ StoreToFrameField(StandardFrameConstants::kContextOffset, context_register());
+}
+
+
+void FullCodeGenerator::VisitModuleVariable(ModuleVariable* module) {
+ // Nothing to do.
+ // The instance object is resolved statically through the module's interface.
+}
+
+
+void FullCodeGenerator::VisitModulePath(ModulePath* module) {
+ // Nothing to do.
+ // The instance object is resolved statically through the module's interface.
+}
+
+
+void FullCodeGenerator::VisitModuleUrl(ModuleUrl* module) {
+ // TODO(rossberg): dummy allocation for now.
+ Scope* scope = module->body()->scope();
+ Interface* interface = scope_->interface();
+
+ ASSERT(interface->IsModule() && interface->IsFrozen());
+ ASSERT(!modules_.is_null());
+ ASSERT(module_index_ < modules_->length());
+ interface->Allocate(scope->module_var()->index());
+ int index = module_index_++;
+
+ Handle<ModuleInfo> description =
+ ModuleInfo::Create(isolate(), interface, scope_);
+ modules_->set(index, *description);
+}
+
+
+int FullCodeGenerator::DeclareGlobalsFlags() {
+ ASSERT(DeclareGlobalsLanguageMode::is_valid(language_mode()));
+ return DeclareGlobalsEvalFlag::encode(is_eval()) |
+ DeclareGlobalsNativeFlag::encode(is_native()) |
+ DeclareGlobalsLanguageMode::encode(language_mode());
+}
+
+
+void FullCodeGenerator::SetFunctionPosition(FunctionLiteral* fun) {
+ CodeGenerator::RecordPositions(masm_, fun->start_position());
+}
+
+
+void FullCodeGenerator::SetReturnPosition(FunctionLiteral* fun) {
+ CodeGenerator::RecordPositions(masm_, fun->end_position() - 1);
+}
+
+
+void FullCodeGenerator::SetStatementPosition(Statement* stmt) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (!isolate()->debugger()->IsDebuggerActive()) {
+ CodeGenerator::RecordPositions(masm_, stmt->statement_pos());
+ } else {
+ // Check if the statement will be breakable without adding a debug break
+ // slot.
+ BreakableStatementChecker checker;
+ checker.Check(stmt);
+ // Record the statement position right here if the statement is not
+ // breakable. For breakable statements the actual recording of the
+ // position will be postponed to the breakable code (typically an IC).
+ bool position_recorded = CodeGenerator::RecordPositions(
+ masm_, stmt->statement_pos(), !checker.is_breakable());
+ // If the position recording did record a new position generate a debug
+ // break slot to make the statement breakable.
+ if (position_recorded) {
+ Debug::GenerateSlot(masm_);
+ }
+ }
+#else
+ CodeGenerator::RecordPositions(masm_, stmt->statement_pos());
+#endif
+}
+
+
+void FullCodeGenerator::SetExpressionPosition(Expression* expr, int pos) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (!isolate()->debugger()->IsDebuggerActive()) {
+ CodeGenerator::RecordPositions(masm_, pos);
+ } else {
+ // Check if the expression will be breakable without adding a debug break
+ // slot.
+ BreakableStatementChecker checker;
+ checker.Check(expr);
+ // Record a statement position right here if the expression is not
+ // breakable. For breakable expressions the actual recording of the
+ // position will be postponed to the breakable code (typically an IC).
+ // NOTE this will record a statement position for something which might
+ // not be a statement. As stepping in the debugger will only stop at
+ // statement positions this is used for e.g. the condition expression of
+ // a do while loop.
+ bool position_recorded = CodeGenerator::RecordPositions(
+ masm_, pos, !checker.is_breakable());
+ // If the position recording did record a new position generate a debug
+ // break slot to make the statement breakable.
+ if (position_recorded) {
+ Debug::GenerateSlot(masm_);
+ }
+ }
+#else
+ CodeGenerator::RecordPositions(masm_, pos);
+#endif
+}
+
+
+void FullCodeGenerator::SetStatementPosition(int pos) {
+ CodeGenerator::RecordPositions(masm_, pos);
+}
+
+
+void FullCodeGenerator::SetSourcePosition(int pos) {
+ if (pos != RelocInfo::kNoPosition) {
+ masm_->positions_recorder()->RecordPosition(pos);
+ }
+}
+
+
+// Lookup table for code generators for special runtime calls which are
+// generated inline.
+#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
+ &FullCodeGenerator::Emit##Name,
+
+const FullCodeGenerator::InlineFunctionGenerator
+ FullCodeGenerator::kInlineFunctionGenerators[] = {
+ INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
+ INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
+ };
+#undef INLINE_FUNCTION_GENERATOR_ADDRESS
+
+
+FullCodeGenerator::InlineFunctionGenerator
+ FullCodeGenerator::FindInlineFunctionGenerator(Runtime::FunctionId id) {
+ int lookup_index =
+ static_cast<int>(id) - static_cast<int>(Runtime::kFirstInlineFunction);
+ ASSERT(lookup_index >= 0);
+ ASSERT(static_cast<size_t>(lookup_index) <
+ ARRAY_SIZE(kInlineFunctionGenerators));
+ return kInlineFunctionGenerators[lookup_index];
+}
+
+
+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))(expr);
+}
+
+
+void FullCodeGenerator::EmitGeneratorNext(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ EmitGeneratorResume(args->at(0), args->at(1), JSGeneratorObject::NEXT);
+}
+
+
+void FullCodeGenerator::EmitGeneratorThrow(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ EmitGeneratorResume(args->at(0), args->at(1), JSGeneratorObject::THROW);
+}
+
+
+void FullCodeGenerator::EmitDebugBreakInOptimizedCode(CallRuntime* expr) {
+ context()->Plug(handle(Smi::FromInt(0), isolate()));
+}
+
+
+void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
+ switch (expr->op()) {
+ case Token::COMMA:
+ return VisitComma(expr);
+ case Token::OR:
+ case Token::AND:
+ return VisitLogicalExpression(expr);
+ default:
+ return VisitArithmeticExpression(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());
+ VisitInDuplicateContext(expr->right());
+}
+
+
+void FullCodeGenerator::VisitLogicalExpression(BinaryOperation* expr) {
+ bool is_logical_and = expr->op() == Token::AND;
+ Comment cmnt(masm_, is_logical_and ? "[ Logical AND" : "[ Logical OR");
+ Expression* left = expr->left();
+ Expression* right = expr->right();
+ BailoutId right_id = expr->RightId();
+ Label done;
+
+ if (context()->IsTest()) {
+ Label eval_right;
+ const TestContext* test = TestContext::cast(context());
+ if (is_logical_and) {
+ VisitForControl(left, &eval_right, test->false_label(), &eval_right);
+ } else {
+ VisitForControl(left, test->true_label(), &eval_right, &eval_right);
+ }
+ PrepareForBailoutForId(right_id, NO_REGISTERS);
+ __ bind(&eval_right);
+
+ } else if (context()->IsAccumulatorValue()) {
+ VisitForAccumulatorValue(left);
+ // We want the value in the accumulator for the test, and on the stack in
+ // case we need it.
+ __ Push(result_register());
+ Label discard, restore;
+ if (is_logical_and) {
+ DoTest(left, &discard, &restore, &restore);
+ } else {
+ DoTest(left, &restore, &discard, &restore);
+ }
+ __ bind(&restore);
+ __ Pop(result_register());
+ __ jmp(&done);
+ __ bind(&discard);
+ __ Drop(1);
+ PrepareForBailoutForId(right_id, NO_REGISTERS);
+
+ } else if (context()->IsStackValue()) {
+ VisitForAccumulatorValue(left);
+ // We want the value in the accumulator for the test, and on the stack in
+ // case we need it.
+ __ Push(result_register());
+ Label discard;
+ if (is_logical_and) {
+ DoTest(left, &discard, &done, &discard);
+ } else {
+ DoTest(left, &done, &discard, &discard);
+ }
+ __ bind(&discard);
+ __ Drop(1);
+ PrepareForBailoutForId(right_id, NO_REGISTERS);
+
+ } else {
+ ASSERT(context()->IsEffect());
+ Label eval_right;
+ if (is_logical_and) {
+ VisitForControl(left, &eval_right, &done, &eval_right);
+ } else {
+ VisitForControl(left, &done, &eval_right, &eval_right);
+ }
+ PrepareForBailoutForId(right_id, NO_REGISTERS);
+ __ bind(&eval_right);
+ }
+
+ VisitInDuplicateContext(right);
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
+ Token::Value op = expr->op();
+ Comment cmnt(masm_, "[ ArithmeticExpression");
+ Expression* left = expr->left();
+ Expression* right = expr->right();
+ OverwriteMode mode =
+ left->ResultOverwriteAllowed()
+ ? OVERWRITE_LEFT
+ : (right->ResultOverwriteAllowed() ? OVERWRITE_RIGHT : NO_OVERWRITE);
+
+ VisitForStackValue(left);
+ VisitForAccumulatorValue(right);
+
+ SetSourcePosition(expr->position());
+ if (ShouldInlineSmiCase(op)) {
+ EmitInlineSmiBinaryOp(expr, op, mode, left, right);
+ } else {
+ EmitBinaryOp(expr, op, mode);
+ }
+}
+
+
+void FullCodeGenerator::VisitBlock(Block* stmt) {
+ Comment cmnt(masm_, "[ Block");
+ NestedBlock nested_block(this, stmt);
+ SetStatementPosition(stmt);
+
+ Scope* saved_scope = scope();
+ // Push a block context when entering a block with block scoped variables.
+ if (stmt->scope() != NULL) {
+ scope_ = stmt->scope();
+ ASSERT(!scope_->is_module_scope());
+ { Comment cmnt(masm_, "[ Extend block context");
+ Handle<ScopeInfo> scope_info = scope_->GetScopeInfo();
+ int heap_slots = scope_info->ContextLength() - Context::MIN_CONTEXT_SLOTS;
+ __ Push(scope_info);
+ PushFunctionArgumentForContextAllocation();
+ 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());
+ }
+ { Comment cmnt(masm_, "[ Declarations");
+ VisitDeclarations(scope_->declarations());
+ }
+ }
+
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ VisitStatements(stmt->statements());
+ scope_ = saved_scope;
+ __ bind(nested_block.break_label());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+
+ // Pop block context if necessary.
+ if (stmt->scope() != NULL) {
+ LoadContextField(context_register(), Context::PREVIOUS_INDEX);
+ // Update local stack frame context field.
+ StoreToFrameField(StandardFrameConstants::kContextOffset,
+ context_register());
+ }
+}
+
+
+void FullCodeGenerator::VisitModuleStatement(ModuleStatement* stmt) {
+ Comment cmnt(masm_, "[ Module context");
+
+ __ Push(Smi::FromInt(stmt->proxy()->interface()->Index()));
+ __ Push(Smi::FromInt(0));
+ __ CallRuntime(Runtime::kPushModuleContext, 2);
+ StoreToFrameField(
+ StandardFrameConstants::kContextOffset, context_register());
+
+ Scope* saved_scope = scope_;
+ scope_ = stmt->body()->scope();
+ VisitStatements(stmt->body()->statements());
+ scope_ = saved_scope;
+ LoadContextField(context_register(), Context::PREVIOUS_INDEX);
+ // Update local stack frame context field.
+ StoreToFrameField(StandardFrameConstants::kContextOffset,
+ context_register());
+}
+
+
+void FullCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
+ Comment cmnt(masm_, "[ ExpressionStatement");
+ SetStatementPosition(stmt);
+ VisitForEffect(stmt->expression());
+}
+
+
+void FullCodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
+ Comment cmnt(masm_, "[ EmptyStatement");
+ SetStatementPosition(stmt);
+}
+
+
+void FullCodeGenerator::VisitIfStatement(IfStatement* stmt) {
+ Comment cmnt(masm_, "[ IfStatement");
+ SetStatementPosition(stmt);
+ Label then_part, else_part, done;
+
+ if (stmt->HasElseStatement()) {
+ VisitForControl(stmt->condition(), &then_part, &else_part, &then_part);
+ PrepareForBailoutForId(stmt->ThenId(), NO_REGISTERS);
+ __ bind(&then_part);
+ Visit(stmt->then_statement());
+ __ jmp(&done);
+
+ PrepareForBailoutForId(stmt->ElseId(), NO_REGISTERS);
+ __ bind(&else_part);
+ Visit(stmt->else_statement());
+ } else {
+ VisitForControl(stmt->condition(), &then_part, &done, &then_part);
+ PrepareForBailoutForId(stmt->ThenId(), NO_REGISTERS);
+ __ bind(&then_part);
+ Visit(stmt->then_statement());
+
+ PrepareForBailoutForId(stmt->ElseId(), NO_REGISTERS);
+ }
+ __ bind(&done);
+ PrepareForBailoutForId(stmt->IfId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
+ Comment cmnt(masm_, "[ ContinueStatement");
+ SetStatementPosition(stmt);
+ NestedStatement* current = nesting_stack_;
+ int stack_depth = 0;
+ int context_length = 0;
+ // When continuing, we clobber the unpredictable value in the accumulator
+ // with one that's safe for GC. If we hit an exit from the try block of
+ // try...finally on our way out, we will unconditionally preserve the
+ // accumulator on the stack.
+ ClearAccumulator();
+ while (!current->IsContinueTarget(stmt->target())) {
+ current = current->Exit(&stack_depth, &context_length);
+ }
+ __ Drop(stack_depth);
+ if (context_length > 0) {
+ while (context_length > 0) {
+ LoadContextField(context_register(), Context::PREVIOUS_INDEX);
+ --context_length;
+ }
+ StoreToFrameField(StandardFrameConstants::kContextOffset,
+ context_register());
+ }
+
+ __ jmp(current->AsIteration()->continue_label());
+}
+
+
+void FullCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
+ Comment cmnt(masm_, "[ BreakStatement");
+ SetStatementPosition(stmt);
+ NestedStatement* current = nesting_stack_;
+ int stack_depth = 0;
+ int context_length = 0;
+ // When breaking, we clobber the unpredictable value in the accumulator
+ // with one that's safe for GC. If we hit an exit from the try block of
+ // try...finally on our way out, we will unconditionally preserve the
+ // accumulator on the stack.
+ ClearAccumulator();
+ while (!current->IsBreakTarget(stmt->target())) {
+ current = current->Exit(&stack_depth, &context_length);
+ }
+ __ Drop(stack_depth);
+ if (context_length > 0) {
+ while (context_length > 0) {
+ LoadContextField(context_register(), Context::PREVIOUS_INDEX);
+ --context_length;
+ }
+ StoreToFrameField(StandardFrameConstants::kContextOffset,
+ context_register());
+ }
+
+ __ jmp(current->AsBreakable()->break_label());
+}
+
+
+void FullCodeGenerator::EmitUnwindBeforeReturn() {
+ NestedStatement* current = nesting_stack_;
+ int stack_depth = 0;
+ int context_length = 0;
+ while (current != NULL) {
+ current = current->Exit(&stack_depth, &context_length);
+ }
+ __ Drop(stack_depth);
+}
+
+
+void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
+ Comment cmnt(masm_, "[ ReturnStatement");
+ SetStatementPosition(stmt);
+ Expression* expr = stmt->expression();
+ VisitForAccumulatorValue(expr);
+ EmitUnwindBeforeReturn();
+ EmitReturnSequence();
+}
+
+
+void FullCodeGenerator::VisitWithStatement(WithStatement* stmt) {
+ Comment cmnt(masm_, "[ WithStatement");
+ SetStatementPosition(stmt);
+
+ VisitForStackValue(stmt->expression());
+ PushFunctionArgumentForContextAllocation();
+ __ CallRuntime(Runtime::kPushWithContext, 2);
+ StoreToFrameField(StandardFrameConstants::kContextOffset, context_register());
+
+ Scope* saved_scope = scope();
+ scope_ = stmt->scope();
+ { WithOrCatch body(this);
+ Visit(stmt->statement());
+ }
+ scope_ = saved_scope;
+
+ // Pop context.
+ LoadContextField(context_register(), Context::PREVIOUS_INDEX);
+ // Update local stack frame context field.
+ StoreToFrameField(StandardFrameConstants::kContextOffset, context_register());
+}
+
+
+void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ Comment cmnt(masm_, "[ DoWhileStatement");
+ SetStatementPosition(stmt);
+ Label body, book_keeping;
+
+ Iteration loop_statement(this, stmt);
+ increment_loop_depth();
+
+ __ bind(&body);
+ Visit(stmt->body());
+
+ // Record the position of the do while condition and make sure it is
+ // possible to break on the condition.
+ __ bind(loop_statement.continue_label());
+ PrepareForBailoutForId(stmt->ContinueId(), NO_REGISTERS);
+ SetExpressionPosition(stmt->cond(), stmt->condition_position());
+ VisitForControl(stmt->cond(),
+ &book_keeping,
+ loop_statement.break_label(),
+ &book_keeping);
+
+ // Check stack before looping.
+ PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS);
+ __ bind(&book_keeping);
+ EmitBackEdgeBookkeeping(stmt, &body);
+ __ jmp(&body);
+
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(loop_statement.break_label());
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
+ Comment cmnt(masm_, "[ WhileStatement");
+ Label test, body;
+
+ Iteration loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // Emit the test at the bottom of the loop.
+ __ jmp(&test);
+
+ PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
+ __ bind(&body);
+ Visit(stmt->body());
+
+ // Emit the statement position here as this is where the while
+ // statement code starts.
+ __ bind(loop_statement.continue_label());
+ SetStatementPosition(stmt);
+
+ // Check stack before looping.
+ EmitBackEdgeBookkeeping(stmt, &body);
+
+ __ bind(&test);
+ VisitForControl(stmt->cond(),
+ &body,
+ loop_statement.break_label(),
+ loop_statement.break_label());
+
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(loop_statement.break_label());
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::VisitForStatement(ForStatement* stmt) {
+ Comment cmnt(masm_, "[ ForStatement");
+ Label test, body;
+
+ Iteration loop_statement(this, stmt);
+
+ // Set statement position for a break slot before entering the for-body.
+ SetStatementPosition(stmt);
+
+ if (stmt->init() != NULL) {
+ Visit(stmt->init());
+ }
+
+ increment_loop_depth();
+ // Emit the test at the bottom of the loop (even if empty).
+ __ jmp(&test);
+
+ PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
+ __ bind(&body);
+ Visit(stmt->body());
+
+ PrepareForBailoutForId(stmt->ContinueId(), NO_REGISTERS);
+ __ bind(loop_statement.continue_label());
+ if (stmt->next() != NULL) {
+ Visit(stmt->next());
+ }
+
+ // Emit the statement position here as this is where the for
+ // statement code starts.
+ SetStatementPosition(stmt);
+
+ // Check stack before looping.
+ EmitBackEdgeBookkeeping(stmt, &body);
+
+ __ bind(&test);
+ if (stmt->cond() != NULL) {
+ VisitForControl(stmt->cond(),
+ &body,
+ loop_statement.break_label(),
+ loop_statement.break_label());
+ } else {
+ __ jmp(&body);
+ }
+
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(loop_statement.break_label());
+ decrement_loop_depth();
+}
+
+
+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, 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());
+ __ Push(result_register());
+ PushFunctionArgumentForContextAllocation();
+ __ CallRuntime(Runtime::kPushCatchContext, 3);
+ StoreToFrameField(StandardFrameConstants::kContextOffset,
+ context_register());
+ }
+
+ Scope* saved_scope = scope();
+ scope_ = stmt->scope();
+ ASSERT(scope_->declarations()->is_empty());
+ { 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(&exit);
+
+ // Try block code. Sets up the exception handler chain.
+ __ bind(&try_entry);
+ __ PushTryHandler(StackHandler::CATCH, stmt->index());
+ { TryCatch try_body(this);
+ Visit(stmt->try_block());
+ }
+ __ PopTryHandler();
+ __ bind(&exit);
+}
+
+
+void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ Comment cmnt(masm_, "[ TryFinallyStatement");
+ SetStatementPosition(stmt);
+ // Try finally is compiled by setting up a try-handler on the stack while
+ // executing the try body, and removing it again afterwards.
+ //
+ // 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.
+ // 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.
+ // 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
+ // rethrowing the exception.
+ //
+ // The finally block must assume a return address on top of the stack
+ // (or in the link register on ARM chips) and a value (return value or
+ // 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 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);
+ EnterFinallyBlock();
+ { Finally finally_body(this);
+ Visit(stmt->finally_block());
+ }
+ ExitFinallyBlock(); // Return to the calling code.
+
+ // Set up try handler.
+ __ bind(&try_entry);
+ __ PushTryHandler(StackHandler::FINALLY, stmt->index());
+ { TryFinally try_body(this, &finally_entry);
+ Visit(stmt->try_block());
+ }
+ __ PopTryHandler();
+ // Execute the finally block on the way out. Clobber the unpredictable
+ // 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);
+}
+
+
+void FullCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Comment cmnt(masm_, "[ DebuggerStatement");
+ SetStatementPosition(stmt);
+
+ __ DebugBreak();
+ // Ignore the return value.
+#endif
+}
+
+
+void FullCodeGenerator::VisitConditional(Conditional* expr) {
+ Comment cmnt(masm_, "[ Conditional");
+ Label true_case, false_case, done;
+ VisitForControl(expr->condition(), &true_case, &false_case, &true_case);
+
+ PrepareForBailoutForId(expr->ThenId(), NO_REGISTERS);
+ __ bind(&true_case);
+ SetExpressionPosition(expr->then_expression(),
+ expr->then_expression_position());
+ if (context()->IsTest()) {
+ const TestContext* for_test = TestContext::cast(context());
+ VisitForControl(expr->then_expression(),
+ for_test->true_label(),
+ for_test->false_label(),
+ NULL);
+ } else {
+ VisitInDuplicateContext(expr->then_expression());
+ __ jmp(&done);
+ }
+
+ PrepareForBailoutForId(expr->ElseId(), NO_REGISTERS);
+ __ bind(&false_case);
+ SetExpressionPosition(expr->else_expression(),
+ expr->else_expression_position());
+ VisitInDuplicateContext(expr->else_expression());
+ // If control flow falls through Visit, merge it with true case here.
+ if (!context()->IsTest()) {
+ __ bind(&done);
+ }
+}
+
+
+void FullCodeGenerator::VisitLiteral(Literal* expr) {
+ Comment cmnt(masm_, "[ Literal");
+ context()->Plug(expr->value());
+}
+
+
+void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
+ Comment cmnt(masm_, "[ FunctionLiteral");
+
+ // Build the function boilerplate and instantiate it.
+ Handle<SharedFunctionInfo> function_info =
+ Compiler::BuildFunctionInfo(expr, script());
+ if (function_info.is_null()) {
+ SetStackOverflow();
+ return;
+ }
+ EmitNewClosure(function_info, expr->pretenure());
+}
+
+
+void FullCodeGenerator::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* expr) {
+ Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
+ EmitNewClosure(expr->shared_function_info(), false);
+}
+
+
+void FullCodeGenerator::VisitThrow(Throw* expr) {
+ Comment cmnt(masm_, "[ Throw");
+ VisitForStackValue(expr->exception());
+ __ CallRuntime(Runtime::kThrow, 1);
+ // Never returns here.
+}
+
+
+FullCodeGenerator::NestedStatement* FullCodeGenerator::TryCatch::Exit(
+ int* stack_depth,
+ int* context_length) {
+ // The macros used here must preserve the result register.
+ __ Drop(*stack_depth);
+ __ PopTryHandler();
+ *stack_depth = 0;
+ return previous_;
+}
+
+
+bool FullCodeGenerator::TryLiteralCompare(CompareOperation* expr) {
+ Expression* sub_expr;
+ Handle<String> check;
+ if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) {
+ EmitLiteralCompareTypeof(expr, sub_expr, check);
+ return true;
+ }
+
+ if (expr->IsLiteralCompareUndefined(&sub_expr, isolate())) {
+ EmitLiteralCompareNil(expr, sub_expr, kUndefinedValue);
+ return true;
+ }
+
+ if (expr->IsLiteralCompareNull(&sub_expr)) {
+ EmitLiteralCompareNil(expr, sub_expr, kNullValue);
+ return true;
+ }
+
+ return false;
+}
+
+
+#undef __
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/full-codegen.h b/chromium/v8/src/full-codegen.h
new file mode 100644
index 00000000000..af63aedfbff
--- /dev/null
+++ b/chromium/v8/src/full-codegen.h
@@ -0,0 +1,945 @@
+// Copyright 2012 the V8 project authors. 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_FULL_CODEGEN_H_
+#define V8_FULL_CODEGEN_H_
+
+#include "v8.h"
+
+#include "allocation.h"
+#include "assert-scope.h"
+#include "ast.h"
+#include "code-stubs.h"
+#include "codegen.h"
+#include "compiler.h"
+#include "data-flow.h"
+#include "globals.h"
+#include "objects.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class JumpPatchSite;
+
+// AST node visitor which can tell whether a given statement will be breakable
+// when the code is compiled by the full compiler in the debugger. This means
+// that there will be an IC (load/store/call) in the code generated for the
+// debugger to piggybag on.
+class BreakableStatementChecker: public AstVisitor {
+ public:
+ BreakableStatementChecker() : is_breakable_(false) {
+ InitializeAstVisitor();
+ }
+
+ void Check(Statement* stmt);
+ void Check(Expression* stmt);
+
+ bool is_breakable() { return is_breakable_; }
+
+ private:
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ bool is_breakable_;
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+ DISALLOW_COPY_AND_ASSIGN(BreakableStatementChecker);
+};
+
+
+// -----------------------------------------------------------------------------
+// Full code generator.
+
+class FullCodeGenerator: public AstVisitor {
+ public:
+ enum State {
+ NO_REGISTERS,
+ TOS_REG
+ };
+
+ FullCodeGenerator(MacroAssembler* masm, CompilationInfo* info)
+ : masm_(masm),
+ info_(info),
+ scope_(info->scope()),
+ nesting_stack_(NULL),
+ loop_depth_(0),
+ globals_(NULL),
+ context_(NULL),
+ bailout_entries_(info->HasDeoptimizationSupport()
+ ? info->function()->ast_node_count() : 0,
+ info->zone()),
+ back_edges_(2, info->zone()),
+ type_feedback_cells_(info->HasDeoptimizationSupport()
+ ? info->function()->ast_node_count() : 0,
+ info->zone()),
+ ic_total_count_(0),
+ zone_(info->zone()) {
+ Initialize();
+ }
+
+ void Initialize();
+
+ static bool MakeCode(CompilationInfo* info);
+
+ // Encode state and pc-offset as a BitField<type, start, size>.
+ // Only use 30 bits because we encode the result as a smi.
+ class StateField : public BitField<State, 0, 1> { };
+ class PcField : public BitField<unsigned, 1, 30-1> { };
+
+ static const char* State2String(State state) {
+ switch (state) {
+ case NO_REGISTERS: return "NO_REGISTERS";
+ case TOS_REG: return "TOS_REG";
+ }
+ UNREACHABLE();
+ return NULL;
+ }
+
+ Zone* zone() const { return zone_; }
+
+ static const int kMaxBackEdgeWeight = 127;
+
+ // Platform-specific code size multiplier.
+#if V8_TARGET_ARCH_IA32
+ static const int kCodeSizeMultiplier = 100;
+#elif V8_TARGET_ARCH_X64
+ static const int kCodeSizeMultiplier = 162;
+#elif V8_TARGET_ARCH_ARM
+ static const int kCodeSizeMultiplier = 142;
+#elif V8_TARGET_ARCH_MIPS
+ static const int kCodeSizeMultiplier = 142;
+#else
+#error Unsupported target architecture.
+#endif
+
+ class BackEdgeTableIterator {
+ public:
+ explicit BackEdgeTableIterator(Code* unoptimized) {
+ ASSERT(unoptimized->kind() == Code::FUNCTION);
+ instruction_start_ = unoptimized->instruction_start();
+ cursor_ = instruction_start_ + unoptimized->back_edge_table_offset();
+ ASSERT(cursor_ < instruction_start_ + unoptimized->instruction_size());
+ table_length_ = Memory::uint32_at(cursor_);
+ cursor_ += kTableLengthSize;
+ end_ = cursor_ + table_length_ * kEntrySize;
+ }
+
+ bool Done() { return cursor_ >= end_; }
+
+ void Next() {
+ ASSERT(!Done());
+ cursor_ += kEntrySize;
+ }
+
+ BailoutId ast_id() {
+ ASSERT(!Done());
+ return BailoutId(static_cast<int>(
+ Memory::uint32_at(cursor_ + kAstIdOffset)));
+ }
+
+ uint32_t loop_depth() {
+ ASSERT(!Done());
+ return Memory::uint32_at(cursor_ + kLoopDepthOffset);
+ }
+
+ uint32_t pc_offset() {
+ ASSERT(!Done());
+ return Memory::uint32_at(cursor_ + kPcOffsetOffset);
+ }
+
+ Address pc() {
+ ASSERT(!Done());
+ return instruction_start_ + pc_offset();
+ }
+
+ uint32_t table_length() { return table_length_; }
+
+ private:
+ static const int kTableLengthSize = kIntSize;
+ static const int kAstIdOffset = 0 * kIntSize;
+ static const int kPcOffsetOffset = 1 * kIntSize;
+ static const int kLoopDepthOffset = 2 * kIntSize;
+ static const int kEntrySize = 3 * kIntSize;
+
+ Address cursor_;
+ Address end_;
+ Address instruction_start_;
+ uint32_t table_length_;
+ DisallowHeapAllocation no_gc_while_iterating_over_raw_addresses_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackEdgeTableIterator);
+ };
+
+
+ private:
+ class Breakable;
+ class Iteration;
+
+ class TestContext;
+
+ class NestedStatement BASE_EMBEDDED {
+ public:
+ explicit NestedStatement(FullCodeGenerator* codegen) : codegen_(codegen) {
+ // Link into codegen's nesting stack.
+ previous_ = codegen->nesting_stack_;
+ codegen->nesting_stack_ = this;
+ }
+ virtual ~NestedStatement() {
+ // Unlink from codegen's nesting stack.
+ ASSERT_EQ(this, codegen_->nesting_stack_);
+ codegen_->nesting_stack_ = previous_;
+ }
+
+ virtual Breakable* AsBreakable() { return NULL; }
+ virtual Iteration* AsIteration() { return NULL; }
+
+ virtual bool IsContinueTarget(Statement* target) { return false; }
+ virtual bool IsBreakTarget(Statement* target) { return false; }
+
+ // Notify the statement that we are exiting it via break, continue, or
+ // return and give it a chance to generate cleanup code. Return the
+ // next outer statement in the nesting stack. We accumulate in
+ // *stack_depth the amount to drop the stack and in *context_length the
+ // number of context chain links to unwind as we traverse the nesting
+ // stack from an exit to its target.
+ virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
+ return previous_;
+ }
+
+ protected:
+ MacroAssembler* masm() { return codegen_->masm(); }
+
+ FullCodeGenerator* codegen_;
+ NestedStatement* previous_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NestedStatement);
+ };
+
+ // A breakable statement such as a block.
+ class Breakable : public NestedStatement {
+ public:
+ Breakable(FullCodeGenerator* codegen, BreakableStatement* statement)
+ : NestedStatement(codegen), statement_(statement) {
+ }
+ virtual ~Breakable() {}
+
+ virtual Breakable* AsBreakable() { return this; }
+ virtual bool IsBreakTarget(Statement* target) {
+ return statement() == target;
+ }
+
+ BreakableStatement* statement() { return statement_; }
+ Label* break_label() { return &break_label_; }
+
+ private:
+ BreakableStatement* statement_;
+ Label break_label_;
+ };
+
+ // An iteration statement such as a while, for, or do loop.
+ class Iteration : public Breakable {
+ public:
+ Iteration(FullCodeGenerator* codegen, IterationStatement* statement)
+ : Breakable(codegen, statement) {
+ }
+ virtual ~Iteration() {}
+
+ virtual Iteration* AsIteration() { return this; }
+ virtual bool IsContinueTarget(Statement* target) {
+ return statement() == target;
+ }
+
+ Label* continue_label() { return &continue_label_; }
+
+ private:
+ Label continue_label_;
+ };
+
+ // A nested block statement.
+ class NestedBlock : public Breakable {
+ public:
+ NestedBlock(FullCodeGenerator* codegen, Block* block)
+ : Breakable(codegen, block) {
+ }
+ virtual ~NestedBlock() {}
+
+ virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
+ if (statement()->AsBlock()->scope() != NULL) {
+ ++(*context_length);
+ }
+ return previous_;
+ };
+ };
+
+ // The try block of a try/catch statement.
+ class TryCatch : public NestedStatement {
+ public:
+ explicit TryCatch(FullCodeGenerator* codegen) : NestedStatement(codegen) {
+ }
+ virtual ~TryCatch() {}
+
+ virtual NestedStatement* Exit(int* stack_depth, int* context_length);
+ };
+
+ // The try block of a try/finally statement.
+ class TryFinally : public NestedStatement {
+ public:
+ TryFinally(FullCodeGenerator* codegen, Label* finally_entry)
+ : NestedStatement(codegen), finally_entry_(finally_entry) {
+ }
+ virtual ~TryFinally() {}
+
+ virtual NestedStatement* Exit(int* stack_depth, int* context_length);
+
+ private:
+ Label* finally_entry_;
+ };
+
+ // The finally block of a try/finally statement.
+ class Finally : public NestedStatement {
+ public:
+ static const int kElementCount = 5;
+
+ explicit Finally(FullCodeGenerator* codegen) : NestedStatement(codegen) { }
+ virtual ~Finally() {}
+
+ virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
+ *stack_depth += kElementCount;
+ return previous_;
+ }
+ };
+
+ // The body of a for/in loop.
+ class ForIn : public Iteration {
+ public:
+ static const int kElementCount = 5;
+
+ ForIn(FullCodeGenerator* codegen, ForInStatement* statement)
+ : Iteration(codegen, statement) {
+ }
+ virtual ~ForIn() {}
+
+ virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
+ *stack_depth += kElementCount;
+ return previous_;
+ }
+ };
+
+
+ // The body of a with or catch.
+ class WithOrCatch : public NestedStatement {
+ public:
+ explicit WithOrCatch(FullCodeGenerator* codegen)
+ : NestedStatement(codegen) {
+ }
+ virtual ~WithOrCatch() {}
+
+ virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
+ ++(*context_length);
+ return previous_;
+ }
+ };
+
+ // Type of a member function that generates inline code for a native function.
+ typedef void (FullCodeGenerator::*InlineFunctionGenerator)(CallRuntime* expr);
+
+ static const InlineFunctionGenerator kInlineFunctionGenerators[];
+
+ // A platform-specific utility to overwrite the accumulator register
+ // with a GC-safe value.
+ void ClearAccumulator();
+
+ // Determine whether or not to inline the smi case for the given
+ // operation.
+ bool ShouldInlineSmiCase(Token::Value op);
+
+ // Helper function to convert a pure value into a test context. The value
+ // is expected on the stack or the accumulator, depending on the platform.
+ // See the platform-specific implementation for details.
+ void DoTest(Expression* condition,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through);
+ void DoTest(const TestContext* context);
+
+ // Helper function to split control flow and avoid a branch to the
+ // fall-through label if it is set up.
+#if V8_TARGET_ARCH_MIPS
+ void Split(Condition cc,
+ Register lhs,
+ const Operand& rhs,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through);
+#else // All non-mips arch.
+ void Split(Condition cc,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through);
+#endif // V8_TARGET_ARCH_MIPS
+
+ // Load the value of a known (PARAMETER, LOCAL, or CONTEXT) variable into
+ // a register. Emits a context chain walk if if necessary (so does
+ // SetVar) so avoid calling both on the same variable.
+ void GetVar(Register destination, Variable* var);
+
+ // Assign to a known (PARAMETER, LOCAL, or CONTEXT) variable. If it's in
+ // the context, the write barrier will be emitted and source, scratch0,
+ // scratch1 will be clobbered. Emits a context chain walk if if necessary
+ // (so does GetVar) so avoid calling both on the same variable.
+ void SetVar(Variable* var,
+ Register source,
+ Register scratch0,
+ Register scratch1);
+
+ // An operand used to read/write a stack-allocated (PARAMETER or LOCAL)
+ // variable. Writing does not need the write barrier.
+ MemOperand StackOperand(Variable* var);
+
+ // An operand used to read/write a known (PARAMETER, LOCAL, or CONTEXT)
+ // variable. May emit code to traverse the context chain, loading the
+ // found context into the scratch register. Writing to this operand will
+ // need the write barrier if location is CONTEXT.
+ MemOperand VarOperand(Variable* var, Register scratch);
+
+ void VisitForEffect(Expression* expr) {
+ EffectContext context(this);
+ Visit(expr);
+ PrepareForBailout(expr, NO_REGISTERS);
+ }
+
+ void VisitForAccumulatorValue(Expression* expr) {
+ AccumulatorValueContext context(this);
+ Visit(expr);
+ PrepareForBailout(expr, TOS_REG);
+ }
+
+ void VisitForStackValue(Expression* expr) {
+ StackValueContext context(this);
+ Visit(expr);
+ PrepareForBailout(expr, NO_REGISTERS);
+ }
+
+ void VisitForControl(Expression* expr,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ TestContext context(this, expr, if_true, if_false, fall_through);
+ 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 DeclareModules(Handle<FixedArray> descriptions);
+ void DeclareGlobals(Handle<FixedArray> pairs);
+ int DeclareGlobalsFlags();
+
+ // Generate code to allocate all (including nested) modules and contexts.
+ // Because of recursive linking and the presence of module alias declarations,
+ // this has to be a separate pass _before_ populating or executing any module.
+ void AllocateModules(ZoneList<Declaration*>* declarations);
+
+ // Generate code to create an iterator result object. The "value" property is
+ // set to a value popped from the stack, and "done" is set according to the
+ // argument. The result object is left in the result register.
+ void EmitCreateIteratorResult(bool done);
+
+ // 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);
+
+ // Platform-specific code for comparing the type of a value with
+ // a given literal string.
+ void EmitLiteralCompareTypeof(Expression* expr,
+ 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(BailoutId 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(TypeFeedbackId id, Handle<Cell> cell);
+
+ // Record a call's return site offset, used to rebuild the frame if the
+ // called function was inlined at the site.
+ void RecordJSReturnSite(Call* call);
+
+ // Prepare for bailout before a test (or compare) and branch. If
+ // should_normalize, then the following comparison will not handle the
+ // 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(Expression* expr,
+ bool should_normalize,
+ Label* if_true,
+ Label* if_false);
+
+ // If enabled, emit debug code for checking that the current context is
+ // neither a with nor a catch context.
+ void EmitDebugCheckDeclarationContext(Variable* variable);
+
+ // This is meant to be called at loop back edges, |back_edge_target| is
+ // the jump target of the back edge and is used to approximate the amount
+ // of code inside the loop.
+ void EmitBackEdgeBookkeeping(IterationStatement* stmt,
+ Label* back_edge_target);
+ // Record the OSR AST id corresponding to a back edge in the code.
+ void RecordBackEdge(BailoutId osr_ast_id);
+ // Emit a table of back edge ids, pcs and loop depths into the code stream.
+ // Return the offset of the start of the table.
+ unsigned EmitBackEdgeTable();
+
+ void EmitProfilingCounterDecrement(int delta);
+ void EmitProfilingCounterReset();
+
+ // Emit code to pop values from the stack associated with nested statements
+ // like try/catch, try/finally, etc, running the finallies and unwinding the
+ // handlers as needed.
+ void EmitUnwindBeforeReturn();
+
+ // Platform-specific return sequence
+ void EmitReturnSequence();
+
+ // Platform-specific code sequences for calls
+ void EmitCallWithStub(Call* expr, CallFunctionFlags flags);
+ void EmitCallWithIC(Call* expr, Handle<Object> name, RelocInfo::Mode mode);
+ void EmitKeyedCallWithIC(Call* expr, Expression* key);
+
+ // Platform-specific code for inline runtime calls.
+ InlineFunctionGenerator FindInlineFunctionGenerator(Runtime::FunctionId id);
+
+ void EmitInlineRuntimeCall(CallRuntime* expr);
+
+#define EMIT_INLINE_RUNTIME_CALL(name, x, y) \
+ 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
+
+ void EmitSeqStringSetCharCheck(Register string,
+ Register index,
+ Register value,
+ uint32_t encoding_mask);
+
+ // Platform-specific code for resuming generators.
+ void EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode);
+
+ // Platform-specific code for loading variables.
+ void EmitLoadGlobalCheckExtensions(Variable* var,
+ TypeofState typeof_state,
+ Label* slow);
+ MemOperand ContextSlotOperandCheckExtensions(Variable* var, Label* slow);
+ void EmitDynamicLookupFastCase(Variable* var,
+ TypeofState typeof_state,
+ Label* slow,
+ Label* done);
+ void EmitVariableLoad(VariableProxy* proxy);
+
+ void EmitAccessor(Expression* expression);
+
+ // Expects the arguments and the function already pushed.
+ void EmitResolvePossiblyDirectEval(int arg_count);
+
+ // Platform-specific support for allocating a new closure based on
+ // the given function info.
+ void EmitNewClosure(Handle<SharedFunctionInfo> info, bool pretenure);
+
+ // Platform-specific support for compiling assignments.
+
+ // Load a value from a named property.
+ // The receiver is left on the stack by the IC.
+ void EmitNamedPropertyLoad(Property* expr);
+
+ // Load a value from a keyed property.
+ // The receiver and the key is left on the stack by the IC.
+ void EmitKeyedPropertyLoad(Property* expr);
+
+ // Apply the compound assignment operator. Expects the left operand on top
+ // of the stack and the right one in the accumulator.
+ void EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode);
+
+ // Helper functions for generating inlined smi code for certain
+ // binary operations.
+ void EmitInlineSmiBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode,
+ Expression* left,
+ Expression* right);
+
+ // Assign to the given expression as if via '='. The right-hand-side value
+ // is expected in the accumulator.
+ void EmitAssignment(Expression* expr);
+
+ // Complete a variable assignment. The right-hand-side value is expected
+ // in the accumulator.
+ void EmitVariableAssignment(Variable* var,
+ Token::Value op);
+
+ // Complete a named property assignment. The receiver is expected on top
+ // of the stack and the right-hand-side value in the accumulator.
+ void EmitNamedPropertyAssignment(Assignment* expr);
+
+ // Complete a keyed property assignment. The receiver and key are
+ // expected on top of the stack and the right-hand-side value in the
+ // accumulator.
+ void EmitKeyedPropertyAssignment(Assignment* expr);
+
+ void CallIC(Handle<Code> code,
+ RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
+ TypeFeedbackId id = TypeFeedbackId::None());
+
+ void SetFunctionPosition(FunctionLiteral* fun);
+ void SetReturnPosition(FunctionLiteral* fun);
+ void SetStatementPosition(Statement* stmt);
+ void SetExpressionPosition(Expression* expr, int pos);
+ void SetStatementPosition(int pos);
+ void SetSourcePosition(int pos);
+
+ // Non-local control flow support.
+ void EnterFinallyBlock();
+ void ExitFinallyBlock();
+
+ // Loop nesting counter.
+ int loop_depth() { return loop_depth_; }
+ void increment_loop_depth() { loop_depth_++; }
+ void decrement_loop_depth() {
+ ASSERT(loop_depth_ > 0);
+ loop_depth_--;
+ }
+
+ MacroAssembler* masm() { return masm_; }
+
+ class ExpressionContext;
+ const ExpressionContext* context() { return context_; }
+ void set_new_context(const ExpressionContext* context) { context_ = context; }
+
+ Handle<Script> script() { return info_->script(); }
+ bool is_eval() { return info_->is_eval(); }
+ bool is_native() { return info_->is_native(); }
+ 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_; }
+
+ static Register result_register();
+ static Register context_register();
+
+ // Set fields in the stack frame. Offsets are the frame pointer relative
+ // offsets defined in, e.g., StandardFrameConstants.
+ void StoreToFrameField(int frame_offset, Register value);
+
+ // Load a value from the current context. Indices are defined as an enum
+ // in v8::internal::Context.
+ void LoadContextField(Register dst, int context_index);
+
+ // Push the function argument for the runtime functions PushWithContext
+ // and PushCatchContext.
+ void PushFunctionArgumentForContextAllocation();
+
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ void VisitComma(BinaryOperation* expr);
+ void VisitLogicalExpression(BinaryOperation* expr);
+ void VisitArithmeticExpression(BinaryOperation* expr);
+
+ void VisitForTypeofValue(Expression* expr);
+
+ void Generate();
+ void PopulateDeoptimizationData(Handle<Code> code);
+ void PopulateTypeFeedbackInfo(Handle<Code> code);
+ void PopulateTypeFeedbackCells(Handle<Code> code);
+
+ Handle<FixedArray> handler_table() { return handler_table_; }
+
+ struct BailoutEntry {
+ BailoutId id;
+ unsigned pc_and_state;
+ };
+
+ struct BackEdgeEntry {
+ BailoutId id;
+ unsigned pc;
+ uint32_t loop_depth;
+ };
+
+ struct TypeFeedbackCellEntry {
+ TypeFeedbackId ast_id;
+ Handle<Cell> cell;
+ };
+
+
+ class ExpressionContext BASE_EMBEDDED {
+ public:
+ explicit ExpressionContext(FullCodeGenerator* codegen)
+ : masm_(codegen->masm()), old_(codegen->context()), codegen_(codegen) {
+ codegen->set_new_context(this);
+ }
+
+ virtual ~ExpressionContext() {
+ codegen_->set_new_context(old_);
+ }
+
+ Isolate* isolate() const { return codegen_->isolate(); }
+
+ // Convert constant control flow (true or false) to the result expected for
+ // this expression context.
+ virtual void Plug(bool flag) const = 0;
+
+ // Emit code to convert a pure value (in a register, known variable
+ // location, as a literal, or on top of the stack) into the result
+ // expected according to this expression context.
+ virtual void Plug(Register reg) const = 0;
+ virtual void Plug(Variable* var) const = 0;
+ virtual void Plug(Handle<Object> lit) const = 0;
+ virtual void Plug(Heap::RootListIndex index) const = 0;
+ virtual void PlugTOS() const = 0;
+
+ // Emit code to convert pure control flow to a pair of unbound labels into
+ // the result expected according to this expression context. The
+ // implementation will bind both labels unless it's a TestContext, which
+ // won't bind them at this point.
+ virtual void Plug(Label* materialize_true,
+ Label* materialize_false) const = 0;
+
+ // Emit code to discard count elements from the top of stack, then convert
+ // a pure value into the result expected according to this expression
+ // context.
+ virtual void DropAndPlug(int count, Register reg) const = 0;
+
+ // Set up branch labels for a test expression. The three Label** parameters
+ // are output parameters.
+ virtual void PrepareTest(Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const = 0;
+
+ // 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).
+ virtual bool IsAccumulatorValue() const { return false; }
+ virtual bool IsStackValue() const { return false; }
+
+ // Returns true if we are branching on the value rather than materializing
+ // it. Only used for asserts.
+ virtual bool IsTest() const { return false; }
+
+ protected:
+ FullCodeGenerator* codegen() const { return codegen_; }
+ MacroAssembler* masm() const { return masm_; }
+ MacroAssembler* masm_;
+
+ private:
+ const ExpressionContext* old_;
+ FullCodeGenerator* codegen_;
+ };
+
+ class AccumulatorValueContext : public ExpressionContext {
+ public:
+ explicit AccumulatorValueContext(FullCodeGenerator* codegen)
+ : ExpressionContext(codegen) { }
+
+ virtual void Plug(bool flag) const;
+ virtual void Plug(Register reg) const;
+ virtual void Plug(Label* materialize_true, Label* materialize_false) const;
+ virtual void Plug(Variable* var) const;
+ virtual void Plug(Handle<Object> lit) const;
+ virtual void Plug(Heap::RootListIndex) const;
+ virtual void PlugTOS() const;
+ virtual void DropAndPlug(int count, Register reg) const;
+ virtual void PrepareTest(Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const;
+ virtual bool IsAccumulatorValue() const { return true; }
+ };
+
+ class StackValueContext : public ExpressionContext {
+ public:
+ explicit StackValueContext(FullCodeGenerator* codegen)
+ : ExpressionContext(codegen) { }
+
+ virtual void Plug(bool flag) const;
+ virtual void Plug(Register reg) const;
+ virtual void Plug(Label* materialize_true, Label* materialize_false) const;
+ virtual void Plug(Variable* var) const;
+ virtual void Plug(Handle<Object> lit) const;
+ virtual void Plug(Heap::RootListIndex) const;
+ virtual void PlugTOS() const;
+ virtual void DropAndPlug(int count, Register reg) const;
+ virtual void PrepareTest(Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const;
+ virtual bool IsStackValue() const { return true; }
+ };
+
+ class TestContext : public ExpressionContext {
+ public:
+ TestContext(FullCodeGenerator* codegen,
+ Expression* condition,
+ Label* true_label,
+ Label* false_label,
+ Label* fall_through)
+ : ExpressionContext(codegen),
+ condition_(condition),
+ true_label_(true_label),
+ false_label_(false_label),
+ fall_through_(fall_through) { }
+
+ static const TestContext* cast(const ExpressionContext* context) {
+ ASSERT(context->IsTest());
+ return reinterpret_cast<const TestContext*>(context);
+ }
+
+ Expression* condition() const { return condition_; }
+ Label* true_label() const { return true_label_; }
+ Label* false_label() const { return false_label_; }
+ Label* fall_through() const { return fall_through_; }
+
+ virtual void Plug(bool flag) const;
+ virtual void Plug(Register reg) const;
+ virtual void Plug(Label* materialize_true, Label* materialize_false) const;
+ virtual void Plug(Variable* var) const;
+ virtual void Plug(Handle<Object> lit) const;
+ virtual void Plug(Heap::RootListIndex) const;
+ virtual void PlugTOS() const;
+ virtual void DropAndPlug(int count, Register reg) const;
+ virtual void PrepareTest(Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const;
+ virtual bool IsTest() const { return true; }
+
+ private:
+ Expression* condition_;
+ Label* true_label_;
+ Label* false_label_;
+ Label* fall_through_;
+ };
+
+ class EffectContext : public ExpressionContext {
+ public:
+ explicit EffectContext(FullCodeGenerator* codegen)
+ : ExpressionContext(codegen) { }
+
+ virtual void Plug(bool flag) const;
+ virtual void Plug(Register reg) const;
+ virtual void Plug(Label* materialize_true, Label* materialize_false) const;
+ virtual void Plug(Variable* var) const;
+ virtual void Plug(Handle<Object> lit) const;
+ virtual void Plug(Heap::RootListIndex) const;
+ virtual void PlugTOS() const;
+ virtual void DropAndPlug(int count, Register reg) const;
+ virtual void PrepareTest(Label* materialize_true,
+ Label* materialize_false,
+ Label** if_true,
+ Label** if_false,
+ Label** fall_through) const;
+ virtual bool IsEffect() const { return true; }
+ };
+
+ MacroAssembler* masm_;
+ CompilationInfo* info_;
+ Scope* scope_;
+ Label return_label_;
+ NestedStatement* nesting_stack_;
+ int loop_depth_;
+ ZoneList<Handle<Object> >* globals_;
+ Handle<FixedArray> modules_;
+ int module_index_;
+ const ExpressionContext* context_;
+ ZoneList<BailoutEntry> bailout_entries_;
+ GrowableBitVector prepared_bailout_ids_;
+ ZoneList<BackEdgeEntry> back_edges_;
+ ZoneList<TypeFeedbackCellEntry> type_feedback_cells_;
+ int ic_total_count_;
+ Handle<FixedArray> handler_table_;
+ Handle<Cell> profiling_counter_;
+ bool generate_debug_code_;
+ Zone* zone_;
+
+ friend class NestedStatement;
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+ DISALLOW_COPY_AND_ASSIGN(FullCodeGenerator);
+};
+
+
+// A map from property names to getter/setter pairs allocated in the zone.
+class AccessorTable: public TemplateHashMap<Literal,
+ ObjectLiteral::Accessors,
+ ZoneAllocationPolicy> {
+ public:
+ explicit AccessorTable(Zone* zone) :
+ TemplateHashMap<Literal, ObjectLiteral::Accessors,
+ ZoneAllocationPolicy>(Literal::Match,
+ ZoneAllocationPolicy(zone)),
+ zone_(zone) { }
+
+ Iterator lookup(Literal* literal) {
+ Iterator it = find(literal, true, ZoneAllocationPolicy(zone_));
+ if (it->second == NULL) it->second = new(zone_) ObjectLiteral::Accessors();
+ return it;
+ }
+
+ private:
+ Zone* zone_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_FULL_CODEGEN_H_
diff --git a/chromium/v8/src/func-name-inferrer.cc b/chromium/v8/src/func-name-inferrer.cc
new file mode 100644
index 00000000000..84d3bf06b87
--- /dev/null
+++ b/chromium/v8/src/func-name-inferrer.cc
@@ -0,0 +1,107 @@
+// 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 "ast.h"
+#include "func-name-inferrer.h"
+#include "list-inl.h"
+
+namespace v8 {
+namespace internal {
+
+FuncNameInferrer::FuncNameInferrer(Isolate* isolate, Zone* zone)
+ : isolate_(isolate),
+ entries_stack_(10, zone),
+ names_stack_(5, zone),
+ funcs_to_infer_(4, zone),
+ zone_(zone) {
+}
+
+
+void FuncNameInferrer::PushEnclosingName(Handle<String> name) {
+ // Enclosing name is a name of a constructor function. To check
+ // that it is really a constructor, we check that it is not empty
+ // and starts with a capital letter.
+ if (name->length() > 0 && Runtime::IsUpperCaseChar(
+ isolate()->runtime_state(), name->Get(0))) {
+ names_stack_.Add(Name(name, kEnclosingConstructorName), zone());
+ }
+}
+
+
+void FuncNameInferrer::PushLiteralName(Handle<String> name) {
+ if (IsOpen() && !isolate()->heap()->prototype_string()->Equals(*name)) {
+ names_stack_.Add(Name(name, kLiteralName), zone());
+ }
+}
+
+
+void FuncNameInferrer::PushVariableName(Handle<String> name) {
+ if (IsOpen() && !isolate()->heap()->result_string()->Equals(*name)) {
+ names_stack_.Add(Name(name, kVariableName), zone());
+ }
+}
+
+
+Handle<String> FuncNameInferrer::MakeNameFromStack() {
+ return MakeNameFromStackHelper(0, isolate()->factory()->empty_string());
+}
+
+
+Handle<String> FuncNameInferrer::MakeNameFromStackHelper(int pos,
+ Handle<String> prev) {
+ if (pos >= names_stack_.length()) return prev;
+ if (pos < names_stack_.length() - 1 &&
+ names_stack_.at(pos).type == kVariableName &&
+ names_stack_.at(pos + 1).type == kVariableName) {
+ // Skip consecutive variable declarations.
+ return MakeNameFromStackHelper(pos + 1, prev);
+ } else {
+ if (prev->length() > 0) {
+ Factory* factory = isolate()->factory();
+ Handle<String> curr = factory->NewConsString(
+ factory->dot_string(), names_stack_.at(pos).name);
+ return MakeNameFromStackHelper(pos + 1,
+ factory->NewConsString(prev, curr));
+ } else {
+ return MakeNameFromStackHelper(pos + 1, names_stack_.at(pos).name);
+ }
+ }
+}
+
+
+void FuncNameInferrer::InferFunctionsNames() {
+ Handle<String> func_name = MakeNameFromStack();
+ for (int i = 0; i < funcs_to_infer_.length(); ++i) {
+ funcs_to_infer_[i]->set_inferred_name(func_name);
+ }
+ funcs_to_infer_.Rewind(0);
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/func-name-inferrer.h b/chromium/v8/src/func-name-inferrer.h
new file mode 100644
index 00000000000..f57e7786045
--- /dev/null
+++ b/chromium/v8/src/func-name-inferrer.h
@@ -0,0 +1,131 @@
+// Copyright 2006-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.
+
+#ifndef V8_FUNC_NAME_INFERRER_H_
+#define V8_FUNC_NAME_INFERRER_H_
+
+namespace v8 {
+namespace internal {
+
+class Isolate;
+
+// FuncNameInferrer is a stateful class that is used to perform name
+// inference for anonymous functions during static analysis of source code.
+// Inference is performed in cases when an anonymous function is assigned
+// to a variable or a property (see test-func-name-inference.cc for examples.)
+//
+// The basic idea is that during parsing of LHSs of certain expressions
+// (assignments, declarations, object literals) we collect name strings,
+// and during parsing of the RHS, a function literal can be collected. After
+// parsing the RHS we can infer a name for function literals that do not have
+// a name.
+class FuncNameInferrer : public ZoneObject {
+ public:
+ FuncNameInferrer(Isolate* isolate, Zone* zone);
+
+ // Returns whether we have entered name collection state.
+ bool IsOpen() const { return !entries_stack_.is_empty(); }
+
+ // Pushes an enclosing the name of enclosing function onto names stack.
+ void PushEnclosingName(Handle<String> name);
+
+ // Enters name collection state.
+ void Enter() {
+ entries_stack_.Add(names_stack_.length(), zone());
+ }
+
+ // Pushes an encountered name onto names stack when in collection state.
+ void PushLiteralName(Handle<String> name);
+
+ void PushVariableName(Handle<String> name);
+
+ // Adds a function to infer name for.
+ void AddFunction(FunctionLiteral* func_to_infer) {
+ if (IsOpen()) {
+ funcs_to_infer_.Add(func_to_infer, zone());
+ }
+ }
+
+ void RemoveLastFunction() {
+ if (IsOpen() && !funcs_to_infer_.is_empty()) {
+ funcs_to_infer_.RemoveLast();
+ }
+ }
+
+ // Infers a function name and leaves names collection state.
+ void Infer() {
+ ASSERT(IsOpen());
+ if (!funcs_to_infer_.is_empty()) {
+ InferFunctionsNames();
+ }
+ }
+
+ // Leaves names collection state.
+ void Leave() {
+ ASSERT(IsOpen());
+ names_stack_.Rewind(entries_stack_.RemoveLast());
+ if (entries_stack_.is_empty())
+ funcs_to_infer_.Clear();
+ }
+
+ private:
+ enum NameType {
+ kEnclosingConstructorName,
+ kLiteralName,
+ kVariableName
+ };
+ struct Name {
+ Name(Handle<String> name, NameType type) : name(name), type(type) { }
+ Handle<String> name;
+ NameType type;
+ };
+
+ Isolate* isolate() { return isolate_; }
+ Zone* zone() const { return zone_; }
+
+ // Constructs a full name in dotted notation from gathered names.
+ Handle<String> MakeNameFromStack();
+
+ // A helper function for MakeNameFromStack.
+ Handle<String> MakeNameFromStackHelper(int pos, Handle<String> prev);
+
+ // Performs name inferring for added functions.
+ void InferFunctionsNames();
+
+ Isolate* isolate_;
+ ZoneList<int> entries_stack_;
+ ZoneList<Name> names_stack_;
+ ZoneList<FunctionLiteral*> funcs_to_infer_;
+ Zone* zone_;
+
+ DISALLOW_COPY_AND_ASSIGN(FuncNameInferrer);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_FUNC_NAME_INFERRER_H_
diff --git a/chromium/v8/src/gdb-jit.cc b/chromium/v8/src/gdb-jit.cc
new file mode 100644
index 00000000000..74db807fb35
--- /dev/null
+++ b/chromium/v8/src/gdb-jit.cc
@@ -0,0 +1,2199 @@
+// 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.
+
+#ifdef ENABLE_GDB_JIT_INTERFACE
+#include "v8.h"
+#include "gdb-jit.h"
+
+#include "bootstrapper.h"
+#include "compiler.h"
+#include "frames.h"
+#include "frames-inl.h"
+#include "global-handles.h"
+#include "messages.h"
+#include "natives.h"
+#include "platform.h"
+#include "scopes.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef __APPLE__
+#define __MACH_O
+class MachO;
+class MachOSection;
+typedef MachO DebugObject;
+typedef MachOSection DebugSection;
+#else
+#define __ELF
+class ELF;
+class ELFSection;
+typedef ELF DebugObject;
+typedef ELFSection DebugSection;
+#endif
+
+class Writer BASE_EMBEDDED {
+ public:
+ explicit Writer(DebugObject* debug_object)
+ : debug_object_(debug_object),
+ position_(0),
+ capacity_(1024),
+ buffer_(reinterpret_cast<byte*>(malloc(capacity_))) {
+ }
+
+ ~Writer() {
+ free(buffer_);
+ }
+
+ uintptr_t position() const {
+ return position_;
+ }
+
+ template<typename T>
+ class Slot {
+ public:
+ Slot(Writer* w, uintptr_t offset) : w_(w), offset_(offset) { }
+
+ T* operator-> () {
+ return w_->RawSlotAt<T>(offset_);
+ }
+
+ void set(const T& value) {
+ *w_->RawSlotAt<T>(offset_) = value;
+ }
+
+ Slot<T> at(int i) {
+ return Slot<T>(w_, offset_ + sizeof(T) * i);
+ }
+
+ private:
+ Writer* w_;
+ uintptr_t offset_;
+ };
+
+ template<typename T>
+ void Write(const T& val) {
+ Ensure(position_ + sizeof(T));
+ *RawSlotAt<T>(position_) = val;
+ position_ += sizeof(T);
+ }
+
+ template<typename T>
+ Slot<T> SlotAt(uintptr_t offset) {
+ Ensure(offset + sizeof(T));
+ return Slot<T>(this, offset);
+ }
+
+ template<typename T>
+ Slot<T> CreateSlotHere() {
+ return CreateSlotsHere<T>(1);
+ }
+
+ template<typename T>
+ Slot<T> CreateSlotsHere(uint32_t count) {
+ uintptr_t slot_position = position_;
+ position_ += sizeof(T) * count;
+ Ensure(position_);
+ return SlotAt<T>(slot_position);
+ }
+
+ void Ensure(uintptr_t pos) {
+ if (capacity_ < pos) {
+ while (capacity_ < pos) capacity_ *= 2;
+ buffer_ = reinterpret_cast<byte*>(realloc(buffer_, capacity_));
+ }
+ }
+
+ DebugObject* debug_object() { return debug_object_; }
+
+ byte* buffer() { return buffer_; }
+
+ void Align(uintptr_t align) {
+ uintptr_t delta = position_ % align;
+ if (delta == 0) return;
+ uintptr_t padding = align - delta;
+ Ensure(position_ += padding);
+ ASSERT((position_ % align) == 0);
+ }
+
+ void WriteULEB128(uintptr_t value) {
+ do {
+ uint8_t byte = value & 0x7F;
+ value >>= 7;
+ if (value != 0) byte |= 0x80;
+ Write<uint8_t>(byte);
+ } while (value != 0);
+ }
+
+ void WriteSLEB128(intptr_t value) {
+ bool more = true;
+ while (more) {
+ int8_t byte = value & 0x7F;
+ bool byte_sign = byte & 0x40;
+ value >>= 7;
+
+ if ((value == 0 && !byte_sign) || (value == -1 && byte_sign)) {
+ more = false;
+ } else {
+ byte |= 0x80;
+ }
+
+ Write<int8_t>(byte);
+ }
+ }
+
+ void WriteString(const char* str) {
+ do {
+ Write<char>(*str);
+ } while (*str++);
+ }
+
+ private:
+ template<typename T> friend class Slot;
+
+ template<typename T>
+ T* RawSlotAt(uintptr_t offset) {
+ ASSERT(offset < capacity_ && offset + sizeof(T) <= capacity_);
+ return reinterpret_cast<T*>(&buffer_[offset]);
+ }
+
+ DebugObject* debug_object_;
+ uintptr_t position_;
+ uintptr_t capacity_;
+ byte* buffer_;
+};
+
+class ELFStringTable;
+
+template<typename THeader>
+class DebugSectionBase : public ZoneObject {
+ public:
+ virtual ~DebugSectionBase() { }
+
+ virtual void WriteBody(Writer::Slot<THeader> header, Writer* writer) {
+ uintptr_t start = writer->position();
+ if (WriteBodyInternal(writer)) {
+ uintptr_t end = writer->position();
+ header->offset = start;
+#if defined(__MACH_O)
+ header->addr = 0;
+#endif
+ header->size = end - start;
+ }
+ }
+
+ virtual bool WriteBodyInternal(Writer* writer) {
+ return false;
+ }
+
+ typedef THeader Header;
+};
+
+
+struct MachOSectionHeader {
+ char sectname[16];
+ char segname[16];
+#if V8_TARGET_ARCH_IA32
+ uint32_t addr;
+ uint32_t size;
+#else
+ uint64_t addr;
+ uint64_t size;
+#endif
+ uint32_t offset;
+ uint32_t align;
+ uint32_t reloff;
+ uint32_t nreloc;
+ uint32_t flags;
+ uint32_t reserved1;
+ uint32_t reserved2;
+};
+
+
+class MachOSection : public DebugSectionBase<MachOSectionHeader> {
+ public:
+ enum Type {
+ S_REGULAR = 0x0u,
+ S_ATTR_COALESCED = 0xbu,
+ S_ATTR_SOME_INSTRUCTIONS = 0x400u,
+ S_ATTR_DEBUG = 0x02000000u,
+ S_ATTR_PURE_INSTRUCTIONS = 0x80000000u
+ };
+
+ MachOSection(const char* name,
+ const char* segment,
+ uintptr_t align,
+ uint32_t flags)
+ : name_(name),
+ segment_(segment),
+ align_(align),
+ flags_(flags) {
+ ASSERT(IsPowerOf2(align));
+ if (align_ != 0) {
+ align_ = WhichPowerOf2(align_);
+ }
+ }
+
+ virtual ~MachOSection() { }
+
+ virtual void PopulateHeader(Writer::Slot<Header> header) {
+ header->addr = 0;
+ header->size = 0;
+ header->offset = 0;
+ header->align = align_;
+ header->reloff = 0;
+ header->nreloc = 0;
+ header->flags = flags_;
+ header->reserved1 = 0;
+ header->reserved2 = 0;
+ memset(header->sectname, 0, sizeof(header->sectname));
+ memset(header->segname, 0, sizeof(header->segname));
+ ASSERT(strlen(name_) < sizeof(header->sectname));
+ ASSERT(strlen(segment_) < sizeof(header->segname));
+ strncpy(header->sectname, name_, sizeof(header->sectname));
+ strncpy(header->segname, segment_, sizeof(header->segname));
+ }
+
+ private:
+ const char* name_;
+ const char* segment_;
+ uintptr_t align_;
+ uint32_t flags_;
+};
+
+
+struct ELFSectionHeader {
+ uint32_t name;
+ uint32_t type;
+ uintptr_t flags;
+ uintptr_t address;
+ uintptr_t offset;
+ uintptr_t size;
+ uint32_t link;
+ uint32_t info;
+ uintptr_t alignment;
+ uintptr_t entry_size;
+};
+
+
+#if defined(__ELF)
+class ELFSection : public DebugSectionBase<ELFSectionHeader> {
+ public:
+ enum Type {
+ TYPE_NULL = 0,
+ TYPE_PROGBITS = 1,
+ TYPE_SYMTAB = 2,
+ TYPE_STRTAB = 3,
+ TYPE_RELA = 4,
+ TYPE_HASH = 5,
+ TYPE_DYNAMIC = 6,
+ TYPE_NOTE = 7,
+ TYPE_NOBITS = 8,
+ TYPE_REL = 9,
+ TYPE_SHLIB = 10,
+ TYPE_DYNSYM = 11,
+ TYPE_LOPROC = 0x70000000,
+ TYPE_X86_64_UNWIND = 0x70000001,
+ TYPE_HIPROC = 0x7fffffff,
+ TYPE_LOUSER = 0x80000000,
+ TYPE_HIUSER = 0xffffffff
+ };
+
+ enum Flags {
+ FLAG_WRITE = 1,
+ FLAG_ALLOC = 2,
+ FLAG_EXEC = 4
+ };
+
+ enum SpecialIndexes {
+ INDEX_ABSOLUTE = 0xfff1
+ };
+
+ ELFSection(const char* name, Type type, uintptr_t align)
+ : name_(name), type_(type), align_(align) { }
+
+ virtual ~ELFSection() { }
+
+ void PopulateHeader(Writer::Slot<Header> header, ELFStringTable* strtab);
+
+ virtual void WriteBody(Writer::Slot<Header> header, Writer* w) {
+ uintptr_t start = w->position();
+ if (WriteBodyInternal(w)) {
+ uintptr_t end = w->position();
+ header->offset = start;
+ header->size = end - start;
+ }
+ }
+
+ virtual bool WriteBodyInternal(Writer* w) {
+ return false;
+ }
+
+ uint16_t index() const { return index_; }
+ void set_index(uint16_t index) { index_ = index; }
+
+ protected:
+ virtual void PopulateHeader(Writer::Slot<Header> header) {
+ header->flags = 0;
+ header->address = 0;
+ header->offset = 0;
+ header->size = 0;
+ header->link = 0;
+ header->info = 0;
+ header->entry_size = 0;
+ }
+
+ private:
+ const char* name_;
+ Type type_;
+ uintptr_t align_;
+ uint16_t index_;
+};
+#endif // defined(__ELF)
+
+
+#if defined(__MACH_O)
+class MachOTextSection : public MachOSection {
+ public:
+ MachOTextSection(uintptr_t align,
+ uintptr_t addr,
+ uintptr_t size)
+ : MachOSection("__text",
+ "__TEXT",
+ align,
+ MachOSection::S_REGULAR |
+ MachOSection::S_ATTR_SOME_INSTRUCTIONS |
+ MachOSection::S_ATTR_PURE_INSTRUCTIONS),
+ addr_(addr),
+ size_(size) { }
+
+ protected:
+ virtual void PopulateHeader(Writer::Slot<Header> header) {
+ MachOSection::PopulateHeader(header);
+ header->addr = addr_;
+ header->size = size_;
+ }
+
+ private:
+ uintptr_t addr_;
+ uintptr_t size_;
+};
+#endif // defined(__MACH_O)
+
+
+#if defined(__ELF)
+class FullHeaderELFSection : public ELFSection {
+ public:
+ FullHeaderELFSection(const char* name,
+ Type type,
+ uintptr_t align,
+ uintptr_t addr,
+ uintptr_t offset,
+ uintptr_t size,
+ uintptr_t flags)
+ : ELFSection(name, type, align),
+ addr_(addr),
+ offset_(offset),
+ size_(size),
+ flags_(flags) { }
+
+ protected:
+ virtual void PopulateHeader(Writer::Slot<Header> header) {
+ ELFSection::PopulateHeader(header);
+ header->address = addr_;
+ header->offset = offset_;
+ header->size = size_;
+ header->flags = flags_;
+ }
+
+ private:
+ uintptr_t addr_;
+ uintptr_t offset_;
+ uintptr_t size_;
+ uintptr_t flags_;
+};
+
+
+class ELFStringTable : public ELFSection {
+ public:
+ explicit ELFStringTable(const char* name)
+ : ELFSection(name, TYPE_STRTAB, 1), writer_(NULL), offset_(0), size_(0) {
+ }
+
+ uintptr_t Add(const char* str) {
+ if (*str == '\0') return 0;
+
+ uintptr_t offset = size_;
+ WriteString(str);
+ return offset;
+ }
+
+ void AttachWriter(Writer* w) {
+ writer_ = w;
+ offset_ = writer_->position();
+
+ // First entry in the string table should be an empty string.
+ WriteString("");
+ }
+
+ void DetachWriter() {
+ writer_ = NULL;
+ }
+
+ virtual void WriteBody(Writer::Slot<Header> header, Writer* w) {
+ ASSERT(writer_ == NULL);
+ header->offset = offset_;
+ header->size = size_;
+ }
+
+ private:
+ void WriteString(const char* str) {
+ uintptr_t written = 0;
+ do {
+ writer_->Write(*str);
+ written++;
+ } while (*str++);
+ size_ += written;
+ }
+
+ Writer* writer_;
+
+ uintptr_t offset_;
+ uintptr_t size_;
+};
+
+
+void ELFSection::PopulateHeader(Writer::Slot<ELFSection::Header> header,
+ ELFStringTable* strtab) {
+ header->name = strtab->Add(name_);
+ header->type = type_;
+ header->alignment = align_;
+ PopulateHeader(header);
+}
+#endif // defined(__ELF)
+
+
+#if defined(__MACH_O)
+class MachO BASE_EMBEDDED {
+ public:
+ explicit MachO(Zone* zone) : zone_(zone), sections_(6, zone) { }
+
+ uint32_t AddSection(MachOSection* section) {
+ sections_.Add(section, zone_);
+ return sections_.length() - 1;
+ }
+
+ void Write(Writer* w, uintptr_t code_start, uintptr_t code_size) {
+ Writer::Slot<MachOHeader> header = WriteHeader(w);
+ uintptr_t load_command_start = w->position();
+ Writer::Slot<MachOSegmentCommand> cmd = WriteSegmentCommand(w,
+ code_start,
+ code_size);
+ WriteSections(w, cmd, header, load_command_start);
+ }
+
+ private:
+ struct MachOHeader {
+ uint32_t magic;
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ uint32_t filetype;
+ uint32_t ncmds;
+ uint32_t sizeofcmds;
+ uint32_t flags;
+#if V8_TARGET_ARCH_X64
+ uint32_t reserved;
+#endif
+ };
+
+ struct MachOSegmentCommand {
+ uint32_t cmd;
+ uint32_t cmdsize;
+ char segname[16];
+#if V8_TARGET_ARCH_IA32
+ uint32_t vmaddr;
+ uint32_t vmsize;
+ uint32_t fileoff;
+ uint32_t filesize;
+#else
+ uint64_t vmaddr;
+ uint64_t vmsize;
+ uint64_t fileoff;
+ uint64_t filesize;
+#endif
+ uint32_t maxprot;
+ uint32_t initprot;
+ uint32_t nsects;
+ uint32_t flags;
+ };
+
+ enum MachOLoadCommandCmd {
+ LC_SEGMENT_32 = 0x00000001u,
+ LC_SEGMENT_64 = 0x00000019u
+ };
+
+
+ Writer::Slot<MachOHeader> WriteHeader(Writer* w) {
+ ASSERT(w->position() == 0);
+ Writer::Slot<MachOHeader> header = w->CreateSlotHere<MachOHeader>();
+#if V8_TARGET_ARCH_IA32
+ header->magic = 0xFEEDFACEu;
+ header->cputype = 7; // i386
+ header->cpusubtype = 3; // CPU_SUBTYPE_I386_ALL
+#elif V8_TARGET_ARCH_X64
+ header->magic = 0xFEEDFACFu;
+ header->cputype = 7 | 0x01000000; // i386 | 64-bit ABI
+ header->cpusubtype = 3; // CPU_SUBTYPE_I386_ALL
+ header->reserved = 0;
+#else
+#error Unsupported target architecture.
+#endif
+ header->filetype = 0x1; // MH_OBJECT
+ header->ncmds = 1;
+ header->sizeofcmds = 0;
+ header->flags = 0;
+ return header;
+ }
+
+
+ Writer::Slot<MachOSegmentCommand> WriteSegmentCommand(Writer* w,
+ uintptr_t code_start,
+ uintptr_t code_size) {
+ Writer::Slot<MachOSegmentCommand> cmd =
+ w->CreateSlotHere<MachOSegmentCommand>();
+#if V8_TARGET_ARCH_IA32
+ cmd->cmd = LC_SEGMENT_32;
+#else
+ cmd->cmd = LC_SEGMENT_64;
+#endif
+ cmd->vmaddr = code_start;
+ cmd->vmsize = code_size;
+ cmd->fileoff = 0;
+ cmd->filesize = 0;
+ cmd->maxprot = 7;
+ cmd->initprot = 7;
+ cmd->flags = 0;
+ cmd->nsects = sections_.length();
+ memset(cmd->segname, 0, 16);
+ cmd->cmdsize = sizeof(MachOSegmentCommand) + sizeof(MachOSection::Header) *
+ cmd->nsects;
+ return cmd;
+ }
+
+
+ void WriteSections(Writer* w,
+ Writer::Slot<MachOSegmentCommand> cmd,
+ Writer::Slot<MachOHeader> header,
+ uintptr_t load_command_start) {
+ Writer::Slot<MachOSection::Header> headers =
+ w->CreateSlotsHere<MachOSection::Header>(sections_.length());
+ cmd->fileoff = w->position();
+ header->sizeofcmds = w->position() - load_command_start;
+ for (int section = 0; section < sections_.length(); ++section) {
+ sections_[section]->PopulateHeader(headers.at(section));
+ sections_[section]->WriteBody(headers.at(section), w);
+ }
+ cmd->filesize = w->position() - (uintptr_t)cmd->fileoff;
+ }
+
+ Zone* zone_;
+ ZoneList<MachOSection*> sections_;
+};
+#endif // defined(__MACH_O)
+
+
+#if defined(__ELF)
+class ELF BASE_EMBEDDED {
+ public:
+ explicit ELF(Zone* zone) : zone_(zone), sections_(6, zone) {
+ sections_.Add(new(zone) ELFSection("", ELFSection::TYPE_NULL, 0), zone);
+ sections_.Add(new(zone) ELFStringTable(".shstrtab"), zone);
+ }
+
+ void Write(Writer* w) {
+ WriteHeader(w);
+ WriteSectionTable(w);
+ WriteSections(w);
+ }
+
+ ELFSection* SectionAt(uint32_t index) {
+ return sections_[index];
+ }
+
+ uint32_t AddSection(ELFSection* section) {
+ sections_.Add(section, zone_);
+ section->set_index(sections_.length() - 1);
+ return sections_.length() - 1;
+ }
+
+ private:
+ struct ELFHeader {
+ uint8_t ident[16];
+ uint16_t type;
+ uint16_t machine;
+ uint32_t version;
+ uintptr_t entry;
+ uintptr_t pht_offset;
+ uintptr_t sht_offset;
+ uint32_t flags;
+ uint16_t header_size;
+ uint16_t pht_entry_size;
+ uint16_t pht_entry_num;
+ uint16_t sht_entry_size;
+ uint16_t sht_entry_num;
+ uint16_t sht_strtab_index;
+ };
+
+
+ void WriteHeader(Writer* w) {
+ ASSERT(w->position() == 0);
+ Writer::Slot<ELFHeader> header = w->CreateSlotHere<ELFHeader>();
+#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM
+ const uint8_t ident[16] =
+ { 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#elif V8_TARGET_ARCH_X64
+ const uint8_t ident[16] =
+ { 0x7f, 'E', 'L', 'F', 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+#else
+#error Unsupported target architecture.
+#endif
+ OS::MemCopy(header->ident, ident, 16);
+ header->type = 1;
+#if V8_TARGET_ARCH_IA32
+ header->machine = 3;
+#elif V8_TARGET_ARCH_X64
+ // Processor identification value for x64 is 62 as defined in
+ // System V ABI, AMD64 Supplement
+ // http://www.x86-64.org/documentation/abi.pdf
+ header->machine = 62;
+#elif V8_TARGET_ARCH_ARM
+ // Set to EM_ARM, defined as 40, in "ARM ELF File Format" at
+ // infocenter.arm.com/help/topic/com.arm.doc.dui0101a/DUI0101A_Elf.pdf
+ header->machine = 40;
+#else
+#error Unsupported target architecture.
+#endif
+ header->version = 1;
+ header->entry = 0;
+ header->pht_offset = 0;
+ header->sht_offset = sizeof(ELFHeader); // Section table follows header.
+ header->flags = 0;
+ header->header_size = sizeof(ELFHeader);
+ header->pht_entry_size = 0;
+ header->pht_entry_num = 0;
+ header->sht_entry_size = sizeof(ELFSection::Header);
+ header->sht_entry_num = sections_.length();
+ header->sht_strtab_index = 1;
+ }
+
+ void WriteSectionTable(Writer* w) {
+ // Section headers table immediately follows file header.
+ ASSERT(w->position() == sizeof(ELFHeader));
+
+ Writer::Slot<ELFSection::Header> headers =
+ w->CreateSlotsHere<ELFSection::Header>(sections_.length());
+
+ // String table for section table is the first section.
+ ELFStringTable* strtab = static_cast<ELFStringTable*>(SectionAt(1));
+ strtab->AttachWriter(w);
+ for (int i = 0, length = sections_.length();
+ i < length;
+ i++) {
+ sections_[i]->PopulateHeader(headers.at(i), strtab);
+ }
+ strtab->DetachWriter();
+ }
+
+ int SectionHeaderPosition(uint32_t section_index) {
+ return sizeof(ELFHeader) + sizeof(ELFSection::Header) * section_index;
+ }
+
+ void WriteSections(Writer* w) {
+ Writer::Slot<ELFSection::Header> headers =
+ w->SlotAt<ELFSection::Header>(sizeof(ELFHeader));
+
+ for (int i = 0, length = sections_.length();
+ i < length;
+ i++) {
+ sections_[i]->WriteBody(headers.at(i), w);
+ }
+ }
+
+ Zone* zone_;
+ ZoneList<ELFSection*> sections_;
+};
+
+
+class ELFSymbol BASE_EMBEDDED {
+ public:
+ enum Type {
+ TYPE_NOTYPE = 0,
+ TYPE_OBJECT = 1,
+ TYPE_FUNC = 2,
+ TYPE_SECTION = 3,
+ TYPE_FILE = 4,
+ TYPE_LOPROC = 13,
+ TYPE_HIPROC = 15
+ };
+
+ enum Binding {
+ BIND_LOCAL = 0,
+ BIND_GLOBAL = 1,
+ BIND_WEAK = 2,
+ BIND_LOPROC = 13,
+ BIND_HIPROC = 15
+ };
+
+ ELFSymbol(const char* name,
+ uintptr_t value,
+ uintptr_t size,
+ Binding binding,
+ Type type,
+ uint16_t section)
+ : name(name),
+ value(value),
+ size(size),
+ info((binding << 4) | type),
+ other(0),
+ section(section) {
+ }
+
+ Binding binding() const {
+ return static_cast<Binding>(info >> 4);
+ }
+#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM
+ struct SerializedLayout {
+ SerializedLayout(uint32_t name,
+ uintptr_t value,
+ uintptr_t size,
+ Binding binding,
+ Type type,
+ uint16_t section)
+ : name(name),
+ value(value),
+ size(size),
+ info((binding << 4) | type),
+ other(0),
+ section(section) {
+ }
+
+ uint32_t name;
+ uintptr_t value;
+ uintptr_t size;
+ uint8_t info;
+ uint8_t other;
+ uint16_t section;
+ };
+#elif V8_TARGET_ARCH_X64
+ struct SerializedLayout {
+ SerializedLayout(uint32_t name,
+ uintptr_t value,
+ uintptr_t size,
+ Binding binding,
+ Type type,
+ uint16_t section)
+ : name(name),
+ info((binding << 4) | type),
+ other(0),
+ section(section),
+ value(value),
+ size(size) {
+ }
+
+ uint32_t name;
+ uint8_t info;
+ uint8_t other;
+ uint16_t section;
+ uintptr_t value;
+ uintptr_t size;
+ };
+#endif
+
+ void Write(Writer::Slot<SerializedLayout> s, ELFStringTable* t) {
+ // Convert symbol names from strings to indexes in the string table.
+ s->name = t->Add(name);
+ s->value = value;
+ s->size = size;
+ s->info = info;
+ s->other = other;
+ s->section = section;
+ }
+
+ private:
+ const char* name;
+ uintptr_t value;
+ uintptr_t size;
+ uint8_t info;
+ uint8_t other;
+ uint16_t section;
+};
+
+
+class ELFSymbolTable : public ELFSection {
+ public:
+ ELFSymbolTable(const char* name, Zone* zone)
+ : ELFSection(name, TYPE_SYMTAB, sizeof(uintptr_t)),
+ locals_(1, zone),
+ globals_(1, zone) {
+ }
+
+ virtual void WriteBody(Writer::Slot<Header> header, Writer* w) {
+ w->Align(header->alignment);
+ int total_symbols = locals_.length() + globals_.length() + 1;
+ header->offset = w->position();
+
+ Writer::Slot<ELFSymbol::SerializedLayout> symbols =
+ w->CreateSlotsHere<ELFSymbol::SerializedLayout>(total_symbols);
+
+ header->size = w->position() - header->offset;
+
+ // String table for this symbol table should follow it in the section table.
+ ELFStringTable* strtab =
+ static_cast<ELFStringTable*>(w->debug_object()->SectionAt(index() + 1));
+ strtab->AttachWriter(w);
+ symbols.at(0).set(ELFSymbol::SerializedLayout(0,
+ 0,
+ 0,
+ ELFSymbol::BIND_LOCAL,
+ ELFSymbol::TYPE_NOTYPE,
+ 0));
+ WriteSymbolsList(&locals_, symbols.at(1), strtab);
+ WriteSymbolsList(&globals_, symbols.at(locals_.length() + 1), strtab);
+ strtab->DetachWriter();
+ }
+
+ void Add(const ELFSymbol& symbol, Zone* zone) {
+ if (symbol.binding() == ELFSymbol::BIND_LOCAL) {
+ locals_.Add(symbol, zone);
+ } else {
+ globals_.Add(symbol, zone);
+ }
+ }
+
+ protected:
+ virtual void PopulateHeader(Writer::Slot<Header> header) {
+ ELFSection::PopulateHeader(header);
+ // We are assuming that string table will follow symbol table.
+ header->link = index() + 1;
+ header->info = locals_.length() + 1;
+ header->entry_size = sizeof(ELFSymbol::SerializedLayout);
+ }
+
+ private:
+ void WriteSymbolsList(const ZoneList<ELFSymbol>* src,
+ Writer::Slot<ELFSymbol::SerializedLayout> dst,
+ ELFStringTable* strtab) {
+ for (int i = 0, len = src->length();
+ i < len;
+ i++) {
+ src->at(i).Write(dst.at(i), strtab);
+ }
+ }
+
+ ZoneList<ELFSymbol> locals_;
+ ZoneList<ELFSymbol> globals_;
+};
+#endif // defined(__ELF)
+
+
+class CodeDescription BASE_EMBEDDED {
+ public:
+#if V8_TARGET_ARCH_X64
+ enum StackState {
+ POST_RBP_PUSH,
+ POST_RBP_SET,
+ POST_RBP_POP,
+ STACK_STATE_MAX
+ };
+#endif
+
+ CodeDescription(const char* name,
+ Code* code,
+ Handle<Script> script,
+ GDBJITLineInfo* lineinfo,
+ GDBJITInterface::CodeTag tag,
+ CompilationInfo* info)
+ : name_(name),
+ code_(code),
+ script_(script),
+ lineinfo_(lineinfo),
+ tag_(tag),
+ info_(info) {
+ }
+
+ const char* name() const {
+ return name_;
+ }
+
+ GDBJITLineInfo* lineinfo() const {
+ return lineinfo_;
+ }
+
+ GDBJITInterface::CodeTag tag() const {
+ return tag_;
+ }
+
+ CompilationInfo* info() const {
+ return info_;
+ }
+
+ bool IsInfoAvailable() const {
+ return info_ != NULL;
+ }
+
+ uintptr_t CodeStart() const {
+ return reinterpret_cast<uintptr_t>(code_->instruction_start());
+ }
+
+ uintptr_t CodeEnd() const {
+ return reinterpret_cast<uintptr_t>(code_->instruction_end());
+ }
+
+ uintptr_t CodeSize() const {
+ return CodeEnd() - CodeStart();
+ }
+
+ bool IsLineInfoAvailable() {
+ return !script_.is_null() &&
+ script_->source()->IsString() &&
+ script_->HasValidSource() &&
+ script_->name()->IsString() &&
+ lineinfo_ != NULL;
+ }
+
+#if V8_TARGET_ARCH_X64
+ uintptr_t GetStackStateStartAddress(StackState state) const {
+ ASSERT(state < STACK_STATE_MAX);
+ return stack_state_start_addresses_[state];
+ }
+
+ void SetStackStateStartAddress(StackState state, uintptr_t addr) {
+ ASSERT(state < STACK_STATE_MAX);
+ stack_state_start_addresses_[state] = addr;
+ }
+#endif
+
+ SmartArrayPointer<char> GetFilename() {
+ return String::cast(script_->name())->ToCString();
+ }
+
+ int GetScriptLineNumber(int pos) {
+ return GetScriptLineNumberSafe(script_, pos) + 1;
+ }
+
+
+ private:
+ const char* name_;
+ Code* code_;
+ Handle<Script> script_;
+ GDBJITLineInfo* lineinfo_;
+ GDBJITInterface::CodeTag tag_;
+ CompilationInfo* info_;
+#if V8_TARGET_ARCH_X64
+ uintptr_t stack_state_start_addresses_[STACK_STATE_MAX];
+#endif
+};
+
+#if defined(__ELF)
+static void CreateSymbolsTable(CodeDescription* desc,
+ Zone* zone,
+ ELF* elf,
+ int text_section_index) {
+ ELFSymbolTable* symtab = new(zone) ELFSymbolTable(".symtab", zone);
+ ELFStringTable* strtab = new(zone) ELFStringTable(".strtab");
+
+ // Symbol table should be followed by the linked string table.
+ elf->AddSection(symtab);
+ elf->AddSection(strtab);
+
+ symtab->Add(ELFSymbol("V8 Code",
+ 0,
+ 0,
+ ELFSymbol::BIND_LOCAL,
+ ELFSymbol::TYPE_FILE,
+ ELFSection::INDEX_ABSOLUTE),
+ zone);
+
+ symtab->Add(ELFSymbol(desc->name(),
+ 0,
+ desc->CodeSize(),
+ ELFSymbol::BIND_GLOBAL,
+ ELFSymbol::TYPE_FUNC,
+ text_section_index),
+ zone);
+}
+#endif // defined(__ELF)
+
+
+class DebugInfoSection : public DebugSection {
+ public:
+ explicit DebugInfoSection(CodeDescription* desc)
+#if defined(__ELF)
+ : ELFSection(".debug_info", TYPE_PROGBITS, 1),
+#else
+ : MachOSection("__debug_info",
+ "__DWARF",
+ 1,
+ MachOSection::S_REGULAR | MachOSection::S_ATTR_DEBUG),
+#endif
+ desc_(desc) { }
+
+ // DWARF2 standard
+ enum DWARF2LocationOp {
+ DW_OP_reg0 = 0x50,
+ DW_OP_reg1 = 0x51,
+ DW_OP_reg2 = 0x52,
+ DW_OP_reg3 = 0x53,
+ DW_OP_reg4 = 0x54,
+ DW_OP_reg5 = 0x55,
+ DW_OP_reg6 = 0x56,
+ DW_OP_reg7 = 0x57,
+ DW_OP_fbreg = 0x91 // 1 param: SLEB128 offset
+ };
+
+ enum DWARF2Encoding {
+ DW_ATE_ADDRESS = 0x1,
+ DW_ATE_SIGNED = 0x5
+ };
+
+ bool WriteBodyInternal(Writer* w) {
+ uintptr_t cu_start = w->position();
+ Writer::Slot<uint32_t> size = w->CreateSlotHere<uint32_t>();
+ uintptr_t start = w->position();
+ w->Write<uint16_t>(2); // DWARF version.
+ w->Write<uint32_t>(0); // Abbreviation table offset.
+ w->Write<uint8_t>(sizeof(intptr_t));
+
+ w->WriteULEB128(1); // Abbreviation code.
+ w->WriteString(*desc_->GetFilename());
+ w->Write<intptr_t>(desc_->CodeStart());
+ w->Write<intptr_t>(desc_->CodeStart() + desc_->CodeSize());
+ w->Write<uint32_t>(0);
+
+ uint32_t ty_offset = static_cast<uint32_t>(w->position() - cu_start);
+ w->WriteULEB128(3);
+ w->Write<uint8_t>(kPointerSize);
+ w->WriteString("v8value");
+
+ if (desc_->IsInfoAvailable()) {
+ Scope* scope = desc_->info()->scope();
+ w->WriteULEB128(2);
+ w->WriteString(desc_->name());
+ w->Write<intptr_t>(desc_->CodeStart());
+ w->Write<intptr_t>(desc_->CodeStart() + desc_->CodeSize());
+ Writer::Slot<uint32_t> fb_block_size = w->CreateSlotHere<uint32_t>();
+ uintptr_t fb_block_start = w->position();
+#if V8_TARGET_ARCH_IA32
+ w->Write<uint8_t>(DW_OP_reg5); // The frame pointer's here on ia32
+#elif V8_TARGET_ARCH_X64
+ w->Write<uint8_t>(DW_OP_reg6); // and here on x64.
+#elif V8_TARGET_ARCH_ARM
+ UNIMPLEMENTED();
+#elif V8_TARGET_ARCH_MIPS
+ UNIMPLEMENTED();
+#else
+#error Unsupported target architecture.
+#endif
+ fb_block_size.set(static_cast<uint32_t>(w->position() - fb_block_start));
+
+ int params = scope->num_parameters();
+ int slots = scope->num_stack_slots();
+ int context_slots = scope->ContextLocalCount();
+ // The real slot ID is internal_slots + context_slot_id.
+ int internal_slots = Context::MIN_CONTEXT_SLOTS;
+ int locals = scope->StackLocalCount();
+ int current_abbreviation = 4;
+
+ for (int param = 0; param < params; ++param) {
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteString(
+ *scope->parameter(param)->name()->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();
+ w->Write<uint8_t>(DW_OP_fbreg);
+ w->WriteSLEB128(
+ JavaScriptFrameConstants::kLastParameterOffset +
+ kPointerSize * (params - param - 1));
+ block_size.set(static_cast<uint32_t>(w->position() - block_start));
+ }
+
+ EmbeddedVector<char, 256> buffer;
+ StringBuilder builder(buffer.start(), buffer.length());
+
+ for (int slot = 0; slot < slots; ++slot) {
+ w->WriteULEB128(current_abbreviation++);
+ builder.Reset();
+ builder.AddFormatted("slot%d", slot);
+ w->WriteString(builder.Finalize());
+ }
+
+ // See contexts.h for more information.
+ ASSERT(Context::MIN_CONTEXT_SLOTS == 4);
+ ASSERT(Context::CLOSURE_INDEX == 0);
+ ASSERT(Context::PREVIOUS_INDEX == 1);
+ ASSERT(Context::EXTENSION_INDEX == 2);
+ ASSERT(Context::GLOBAL_OBJECT_INDEX == 3);
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteString(".closure");
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteString(".previous");
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteString(".extension");
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteString(".global");
+
+ for (int context_slot = 0;
+ context_slot < context_slots;
+ ++context_slot) {
+ w->WriteULEB128(current_abbreviation++);
+ builder.Reset();
+ builder.AddFormatted("context_slot%d", context_slot + internal_slots);
+ w->WriteString(builder.Finalize());
+ }
+
+ ZoneList<Variable*> stack_locals(locals, scope->zone());
+ ZoneList<Variable*> context_locals(context_slots, scope->zone());
+ scope->CollectStackAndContextLocals(&stack_locals, &context_locals);
+ for (int local = 0; local < locals; ++local) {
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteString(
+ *stack_locals[local]->name()->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();
+ w->Write<uint8_t>(DW_OP_fbreg);
+ w->WriteSLEB128(
+ JavaScriptFrameConstants::kLocal0Offset -
+ kPointerSize * local);
+ block_size.set(static_cast<uint32_t>(w->position() - block_start));
+ }
+
+ {
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteString("__function");
+ w->Write<uint32_t>(ty_offset);
+ Writer::Slot<uint32_t> block_size = w->CreateSlotHere<uint32_t>();
+ uintptr_t block_start = w->position();
+ w->Write<uint8_t>(DW_OP_fbreg);
+ w->WriteSLEB128(JavaScriptFrameConstants::kFunctionOffset);
+ block_size.set(static_cast<uint32_t>(w->position() - block_start));
+ }
+
+ {
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteString("__context");
+ w->Write<uint32_t>(ty_offset);
+ Writer::Slot<uint32_t> block_size = w->CreateSlotHere<uint32_t>();
+ uintptr_t block_start = w->position();
+ w->Write<uint8_t>(DW_OP_fbreg);
+ w->WriteSLEB128(StandardFrameConstants::kContextOffset);
+ block_size.set(static_cast<uint32_t>(w->position() - block_start));
+ }
+
+ w->WriteULEB128(0); // Terminate the sub program.
+ }
+
+ w->WriteULEB128(0); // Terminate the compile unit.
+ size.set(static_cast<uint32_t>(w->position() - start));
+ return true;
+ }
+
+ private:
+ CodeDescription* desc_;
+};
+
+
+class DebugAbbrevSection : public DebugSection {
+ public:
+ explicit DebugAbbrevSection(CodeDescription* desc)
+#ifdef __ELF
+ : ELFSection(".debug_abbrev", TYPE_PROGBITS, 1),
+#else
+ : MachOSection("__debug_abbrev",
+ "__DWARF",
+ 1,
+ MachOSection::S_REGULAR | MachOSection::S_ATTR_DEBUG),
+#endif
+ desc_(desc) { }
+
+ // DWARF2 standard, figure 14.
+ enum DWARF2Tags {
+ DW_TAG_FORMAL_PARAMETER = 0x05,
+ DW_TAG_POINTER_TYPE = 0xf,
+ DW_TAG_COMPILE_UNIT = 0x11,
+ DW_TAG_STRUCTURE_TYPE = 0x13,
+ DW_TAG_BASE_TYPE = 0x24,
+ DW_TAG_SUBPROGRAM = 0x2e,
+ DW_TAG_VARIABLE = 0x34
+ };
+
+ // DWARF2 standard, figure 16.
+ enum DWARF2ChildrenDetermination {
+ DW_CHILDREN_NO = 0,
+ DW_CHILDREN_YES = 1
+ };
+
+ // DWARF standard, figure 17.
+ enum DWARF2Attribute {
+ DW_AT_LOCATION = 0x2,
+ DW_AT_NAME = 0x3,
+ DW_AT_BYTE_SIZE = 0xb,
+ DW_AT_STMT_LIST = 0x10,
+ DW_AT_LOW_PC = 0x11,
+ DW_AT_HIGH_PC = 0x12,
+ DW_AT_ENCODING = 0x3e,
+ DW_AT_FRAME_BASE = 0x40,
+ DW_AT_TYPE = 0x49
+ };
+
+ // DWARF2 standard, figure 19.
+ enum DWARF2AttributeForm {
+ DW_FORM_ADDR = 0x1,
+ DW_FORM_BLOCK4 = 0x4,
+ DW_FORM_STRING = 0x8,
+ DW_FORM_DATA4 = 0x6,
+ DW_FORM_BLOCK = 0x9,
+ DW_FORM_DATA1 = 0xb,
+ DW_FORM_FLAG = 0xc,
+ DW_FORM_REF4 = 0x13
+ };
+
+ void WriteVariableAbbreviation(Writer* w,
+ int abbreviation_code,
+ bool has_value,
+ bool is_parameter) {
+ w->WriteULEB128(abbreviation_code);
+ w->WriteULEB128(is_parameter ? DW_TAG_FORMAL_PARAMETER : DW_TAG_VARIABLE);
+ w->Write<uint8_t>(DW_CHILDREN_NO);
+ w->WriteULEB128(DW_AT_NAME);
+ w->WriteULEB128(DW_FORM_STRING);
+ if (has_value) {
+ w->WriteULEB128(DW_AT_TYPE);
+ w->WriteULEB128(DW_FORM_REF4);
+ w->WriteULEB128(DW_AT_LOCATION);
+ w->WriteULEB128(DW_FORM_BLOCK4);
+ }
+ w->WriteULEB128(0);
+ w->WriteULEB128(0);
+ }
+
+ bool WriteBodyInternal(Writer* w) {
+ int current_abbreviation = 1;
+ bool extra_info = desc_->IsInfoAvailable();
+ ASSERT(desc_->IsLineInfoAvailable());
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteULEB128(DW_TAG_COMPILE_UNIT);
+ w->Write<uint8_t>(extra_info ? DW_CHILDREN_YES : DW_CHILDREN_NO);
+ w->WriteULEB128(DW_AT_NAME);
+ w->WriteULEB128(DW_FORM_STRING);
+ w->WriteULEB128(DW_AT_LOW_PC);
+ w->WriteULEB128(DW_FORM_ADDR);
+ w->WriteULEB128(DW_AT_HIGH_PC);
+ w->WriteULEB128(DW_FORM_ADDR);
+ w->WriteULEB128(DW_AT_STMT_LIST);
+ w->WriteULEB128(DW_FORM_DATA4);
+ w->WriteULEB128(0);
+ w->WriteULEB128(0);
+
+ if (extra_info) {
+ Scope* scope = desc_->info()->scope();
+ int params = scope->num_parameters();
+ int slots = scope->num_stack_slots();
+ int context_slots = scope->ContextLocalCount();
+ // The real slot ID is internal_slots + context_slot_id.
+ int internal_slots = Context::MIN_CONTEXT_SLOTS;
+ int locals = scope->StackLocalCount();
+ // Total children is params + slots + context_slots + internal_slots +
+ // locals + 2 (__function and __context).
+
+ // The extra duplication below seems to be necessary to keep
+ // gdb from getting upset on OSX.
+ w->WriteULEB128(current_abbreviation++); // Abbreviation code.
+ w->WriteULEB128(DW_TAG_SUBPROGRAM);
+ w->Write<uint8_t>(DW_CHILDREN_YES);
+ w->WriteULEB128(DW_AT_NAME);
+ w->WriteULEB128(DW_FORM_STRING);
+ w->WriteULEB128(DW_AT_LOW_PC);
+ w->WriteULEB128(DW_FORM_ADDR);
+ w->WriteULEB128(DW_AT_HIGH_PC);
+ w->WriteULEB128(DW_FORM_ADDR);
+ w->WriteULEB128(DW_AT_FRAME_BASE);
+ w->WriteULEB128(DW_FORM_BLOCK4);
+ w->WriteULEB128(0);
+ w->WriteULEB128(0);
+
+ w->WriteULEB128(current_abbreviation++);
+ w->WriteULEB128(DW_TAG_STRUCTURE_TYPE);
+ w->Write<uint8_t>(DW_CHILDREN_NO);
+ w->WriteULEB128(DW_AT_BYTE_SIZE);
+ w->WriteULEB128(DW_FORM_DATA1);
+ w->WriteULEB128(DW_AT_NAME);
+ w->WriteULEB128(DW_FORM_STRING);
+ w->WriteULEB128(0);
+ w->WriteULEB128(0);
+
+ for (int param = 0; param < params; ++param) {
+ WriteVariableAbbreviation(w, current_abbreviation++, true, true);
+ }
+
+ for (int slot = 0; slot < slots; ++slot) {
+ WriteVariableAbbreviation(w, current_abbreviation++, false, false);
+ }
+
+ for (int internal_slot = 0;
+ internal_slot < internal_slots;
+ ++internal_slot) {
+ WriteVariableAbbreviation(w, current_abbreviation++, false, false);
+ }
+
+ for (int context_slot = 0;
+ context_slot < context_slots;
+ ++context_slot) {
+ WriteVariableAbbreviation(w, current_abbreviation++, false, false);
+ }
+
+ for (int local = 0; local < locals; ++local) {
+ WriteVariableAbbreviation(w, current_abbreviation++, true, false);
+ }
+
+ // The function.
+ WriteVariableAbbreviation(w, current_abbreviation++, true, false);
+
+ // The context.
+ WriteVariableAbbreviation(w, current_abbreviation++, true, false);
+
+ w->WriteULEB128(0); // Terminate the sibling list.
+ }
+
+ w->WriteULEB128(0); // Terminate the table.
+ return true;
+ }
+
+ private:
+ CodeDescription* desc_;
+};
+
+
+class DebugLineSection : public DebugSection {
+ public:
+ explicit DebugLineSection(CodeDescription* desc)
+#ifdef __ELF
+ : ELFSection(".debug_line", TYPE_PROGBITS, 1),
+#else
+ : MachOSection("__debug_line",
+ "__DWARF",
+ 1,
+ MachOSection::S_REGULAR | MachOSection::S_ATTR_DEBUG),
+#endif
+ desc_(desc) { }
+
+ // DWARF2 standard, figure 34.
+ enum DWARF2Opcodes {
+ DW_LNS_COPY = 1,
+ DW_LNS_ADVANCE_PC = 2,
+ DW_LNS_ADVANCE_LINE = 3,
+ DW_LNS_SET_FILE = 4,
+ DW_LNS_SET_COLUMN = 5,
+ DW_LNS_NEGATE_STMT = 6
+ };
+
+ // DWARF2 standard, figure 35.
+ enum DWARF2ExtendedOpcode {
+ DW_LNE_END_SEQUENCE = 1,
+ DW_LNE_SET_ADDRESS = 2,
+ DW_LNE_DEFINE_FILE = 3
+ };
+
+ bool WriteBodyInternal(Writer* w) {
+ // Write prologue.
+ Writer::Slot<uint32_t> total_length = w->CreateSlotHere<uint32_t>();
+ uintptr_t start = w->position();
+
+ // Used for special opcodes
+ const int8_t line_base = 1;
+ const uint8_t line_range = 7;
+ const int8_t max_line_incr = (line_base + line_range - 1);
+ const uint8_t opcode_base = DW_LNS_NEGATE_STMT + 1;
+
+ w->Write<uint16_t>(2); // Field version.
+ Writer::Slot<uint32_t> prologue_length = w->CreateSlotHere<uint32_t>();
+ uintptr_t prologue_start = w->position();
+ w->Write<uint8_t>(1); // Field minimum_instruction_length.
+ w->Write<uint8_t>(1); // Field default_is_stmt.
+ w->Write<int8_t>(line_base); // Field line_base.
+ w->Write<uint8_t>(line_range); // Field line_range.
+ w->Write<uint8_t>(opcode_base); // Field opcode_base.
+ w->Write<uint8_t>(0); // DW_LNS_COPY operands count.
+ w->Write<uint8_t>(1); // DW_LNS_ADVANCE_PC operands count.
+ w->Write<uint8_t>(1); // DW_LNS_ADVANCE_LINE operands count.
+ w->Write<uint8_t>(1); // DW_LNS_SET_FILE operands count.
+ w->Write<uint8_t>(1); // DW_LNS_SET_COLUMN operands count.
+ w->Write<uint8_t>(0); // DW_LNS_NEGATE_STMT operands count.
+ w->Write<uint8_t>(0); // Empty include_directories sequence.
+ w->WriteString(*desc_->GetFilename()); // File name.
+ w->WriteULEB128(0); // Current directory.
+ w->WriteULEB128(0); // Unknown modification time.
+ w->WriteULEB128(0); // Unknown file size.
+ w->Write<uint8_t>(0);
+ prologue_length.set(static_cast<uint32_t>(w->position() - prologue_start));
+
+ WriteExtendedOpcode(w, DW_LNE_SET_ADDRESS, sizeof(intptr_t));
+ w->Write<intptr_t>(desc_->CodeStart());
+ w->Write<uint8_t>(DW_LNS_COPY);
+
+ intptr_t pc = 0;
+ intptr_t line = 1;
+ bool is_statement = true;
+
+ List<GDBJITLineInfo::PCInfo>* pc_info = desc_->lineinfo()->pc_info();
+ pc_info->Sort(&ComparePCInfo);
+
+ int pc_info_length = pc_info->length();
+ for (int i = 0; i < pc_info_length; i++) {
+ GDBJITLineInfo::PCInfo* info = &pc_info->at(i);
+ ASSERT(info->pc_ >= pc);
+
+ // Reduce bloating in the debug line table by removing duplicate line
+ // entries (per DWARF2 standard).
+ intptr_t new_line = desc_->GetScriptLineNumber(info->pos_);
+ if (new_line == line) {
+ continue;
+ }
+
+ // Mark statement boundaries. For a better debugging experience, mark
+ // the last pc address in the function as a statement (e.g. "}"), so that
+ // a user can see the result of the last line executed in the function,
+ // should control reach the end.
+ if ((i+1) == pc_info_length) {
+ if (!is_statement) {
+ w->Write<uint8_t>(DW_LNS_NEGATE_STMT);
+ }
+ } else if (is_statement != info->is_statement_) {
+ w->Write<uint8_t>(DW_LNS_NEGATE_STMT);
+ is_statement = !is_statement;
+ }
+
+ // Generate special opcodes, if possible. This results in more compact
+ // debug line tables. See the DWARF 2.0 standard to learn more about
+ // special opcodes.
+ uintptr_t pc_diff = info->pc_ - pc;
+ intptr_t line_diff = new_line - line;
+
+ // Compute special opcode (see DWARF 2.0 standard)
+ intptr_t special_opcode = (line_diff - line_base) +
+ (line_range * pc_diff) + opcode_base;
+
+ // If special_opcode is less than or equal to 255, it can be used as a
+ // special opcode. If line_diff is larger than the max line increment
+ // allowed for a special opcode, or if line_diff is less than the minimum
+ // line that can be added to the line register (i.e. line_base), then
+ // special_opcode can't be used.
+ if ((special_opcode >= opcode_base) && (special_opcode <= 255) &&
+ (line_diff <= max_line_incr) && (line_diff >= line_base)) {
+ w->Write<uint8_t>(special_opcode);
+ } else {
+ w->Write<uint8_t>(DW_LNS_ADVANCE_PC);
+ w->WriteSLEB128(pc_diff);
+ w->Write<uint8_t>(DW_LNS_ADVANCE_LINE);
+ w->WriteSLEB128(line_diff);
+ w->Write<uint8_t>(DW_LNS_COPY);
+ }
+
+ // Increment the pc and line operands.
+ pc += pc_diff;
+ line += line_diff;
+ }
+ // Advance the pc to the end of the routine, since the end sequence opcode
+ // requires this.
+ w->Write<uint8_t>(DW_LNS_ADVANCE_PC);
+ w->WriteSLEB128(desc_->CodeSize() - pc);
+ WriteExtendedOpcode(w, DW_LNE_END_SEQUENCE, 0);
+ total_length.set(static_cast<uint32_t>(w->position() - start));
+ return true;
+ }
+
+ private:
+ void WriteExtendedOpcode(Writer* w,
+ DWARF2ExtendedOpcode op,
+ size_t operands_size) {
+ w->Write<uint8_t>(0);
+ w->WriteULEB128(operands_size + 1);
+ w->Write<uint8_t>(op);
+ }
+
+ static int ComparePCInfo(const GDBJITLineInfo::PCInfo* a,
+ const GDBJITLineInfo::PCInfo* b) {
+ if (a->pc_ == b->pc_) {
+ if (a->is_statement_ != b->is_statement_) {
+ return b->is_statement_ ? +1 : -1;
+ }
+ return 0;
+ } else if (a->pc_ > b->pc_) {
+ return +1;
+ } else {
+ return -1;
+ }
+ }
+
+ CodeDescription* desc_;
+};
+
+
+#if V8_TARGET_ARCH_X64
+
+class UnwindInfoSection : public DebugSection {
+ public:
+ explicit UnwindInfoSection(CodeDescription* desc);
+ virtual bool WriteBodyInternal(Writer* w);
+
+ 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 WriteLength(Writer* w,
+ Writer::Slot<uint32_t>* length_slot,
+ int initial_position);
+
+ private:
+ CodeDescription* desc_;
+
+ // DWARF3 Specification, Table 7.23
+ enum CFIInstructions {
+ DW_CFA_ADVANCE_LOC = 0x40,
+ DW_CFA_OFFSET = 0x80,
+ DW_CFA_RESTORE = 0xC0,
+ DW_CFA_NOP = 0x00,
+ DW_CFA_SET_LOC = 0x01,
+ DW_CFA_ADVANCE_LOC1 = 0x02,
+ DW_CFA_ADVANCE_LOC2 = 0x03,
+ DW_CFA_ADVANCE_LOC4 = 0x04,
+ DW_CFA_OFFSET_EXTENDED = 0x05,
+ DW_CFA_RESTORE_EXTENDED = 0x06,
+ DW_CFA_UNDEFINED = 0x07,
+ DW_CFA_SAME_VALUE = 0x08,
+ DW_CFA_REGISTER = 0x09,
+ DW_CFA_REMEMBER_STATE = 0x0A,
+ DW_CFA_RESTORE_STATE = 0x0B,
+ DW_CFA_DEF_CFA = 0x0C,
+ DW_CFA_DEF_CFA_REGISTER = 0x0D,
+ DW_CFA_DEF_CFA_OFFSET = 0x0E,
+
+ DW_CFA_DEF_CFA_EXPRESSION = 0x0F,
+ DW_CFA_EXPRESSION = 0x10,
+ DW_CFA_OFFSET_EXTENDED_SF = 0x11,
+ DW_CFA_DEF_CFA_SF = 0x12,
+ DW_CFA_DEF_CFA_OFFSET_SF = 0x13,
+ DW_CFA_VAL_OFFSET = 0x14,
+ DW_CFA_VAL_OFFSET_SF = 0x15,
+ DW_CFA_VAL_EXPRESSION = 0x16
+ };
+
+ // System V ABI, AMD64 Supplement, Version 0.99.5, Figure 3.36
+ enum RegisterMapping {
+ // Only the relevant ones have been added to reduce clutter.
+ AMD64_RBP = 6,
+ AMD64_RSP = 7,
+ AMD64_RA = 16
+ };
+
+ enum CFIConstants {
+ CIE_ID = 0,
+ CIE_VERSION = 1,
+ CODE_ALIGN_FACTOR = 1,
+ DATA_ALIGN_FACTOR = 1,
+ RETURN_ADDRESS_REGISTER = AMD64_RA
+ };
+};
+
+
+void UnwindInfoSection::WriteLength(Writer* w,
+ Writer::Slot<uint32_t>* length_slot,
+ int initial_position) {
+ uint32_t align = (w->position() - initial_position) % kPointerSize;
+
+ if (align != 0) {
+ for (uint32_t i = 0; i < (kPointerSize - align); i++) {
+ w->Write<uint8_t>(DW_CFA_NOP);
+ }
+ }
+
+ ASSERT((w->position() - initial_position) % kPointerSize == 0);
+ length_slot->set(w->position() - initial_position);
+}
+
+
+UnwindInfoSection::UnwindInfoSection(CodeDescription* desc)
+#ifdef __ELF
+ : ELFSection(".eh_frame", TYPE_X86_64_UNWIND, 1),
+#else
+ : MachOSection("__eh_frame", "__TEXT", sizeof(uintptr_t),
+ MachOSection::S_REGULAR),
+#endif
+ desc_(desc) { }
+
+int UnwindInfoSection::WriteCIE(Writer* w) {
+ Writer::Slot<uint32_t> cie_length_slot = w->CreateSlotHere<uint32_t>();
+ uint32_t cie_position = w->position();
+
+ // Write out the CIE header. Currently no 'common instructions' are
+ // emitted onto the CIE; every FDE has its own set of instructions.
+
+ w->Write<uint32_t>(CIE_ID);
+ w->Write<uint8_t>(CIE_VERSION);
+ w->Write<uint8_t>(0); // Null augmentation string.
+ w->WriteSLEB128(CODE_ALIGN_FACTOR);
+ w->WriteSLEB128(DATA_ALIGN_FACTOR);
+ w->Write<uint8_t>(RETURN_ADDRESS_REGISTER);
+
+ WriteLength(w, &cie_length_slot, cie_position);
+
+ return 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();
+ w->Write<int32_t>(fde_position - cie_position + 4);
+
+ w->Write<uintptr_t>(desc_->CodeStart());
+ w->Write<uintptr_t>(desc_->CodeSize());
+
+ WriteFDEStateOnEntry(w);
+ WriteFDEStateAfterRBPPush(w);
+ WriteFDEStateAfterRBPSet(w);
+ WriteFDEStateAfterRBPPop(w);
+
+ WriteLength(w, &fde_length_slot, fde_position);
+}
+
+
+void UnwindInfoSection::WriteFDEStateOnEntry(Writer* w) {
+ // The first state, just after the control has been transferred to the the
+ // function.
+
+ // RBP for this function will be the value of RSP after pushing the RBP
+ // for the previous function. The previous RBP has not been pushed yet.
+ w->Write<uint8_t>(DW_CFA_DEF_CFA_SF);
+ w->WriteULEB128(AMD64_RSP);
+ w->WriteSLEB128(-kPointerSize);
+
+ // The RA is stored at location CFA + kCallerPCOffset. This is an invariant,
+ // and hence omitted from the next states.
+ w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED);
+ w->WriteULEB128(AMD64_RA);
+ w->WriteSLEB128(StandardFrameConstants::kCallerPCOffset);
+
+ // The RBP of the previous function is still in RBP.
+ w->Write<uint8_t>(DW_CFA_SAME_VALUE);
+ w->WriteULEB128(AMD64_RBP);
+
+ // Last location described by this entry.
+ w->Write<uint8_t>(DW_CFA_SET_LOC);
+ w->Write<uint64_t>(
+ desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_PUSH));
+}
+
+
+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
+ // offset from the previous rule (from -8) to 0.
+ w->Write<uint8_t>(DW_CFA_DEF_CFA_OFFSET);
+ w->WriteULEB128(0);
+
+ // The previous RBP is stored at CFA + kCallerFPOffset. This is an invariant
+ // in this and the next state, and hence omitted in the next state.
+ w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED);
+ w->WriteULEB128(AMD64_RBP);
+ w->WriteSLEB128(StandardFrameConstants::kCallerFPOffset);
+
+ // Last location described by this entry.
+ w->Write<uint8_t>(DW_CFA_SET_LOC);
+ w->Write<uint64_t>(
+ desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_SET));
+}
+
+
+void UnwindInfoSection::WriteFDEStateAfterRBPSet(Writer* w) {
+ // The third state, after the RBP has been set.
+
+ // The CFA can now directly be set to RBP.
+ w->Write<uint8_t>(DW_CFA_DEF_CFA);
+ w->WriteULEB128(AMD64_RBP);
+ w->WriteULEB128(0);
+
+ // Last location described by this entry.
+ w->Write<uint8_t>(DW_CFA_SET_LOC);
+ w->Write<uint64_t>(
+ desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_POP));
+}
+
+
+void UnwindInfoSection::WriteFDEStateAfterRBPPop(Writer* w) {
+ // The fourth (final) state. The RBP has been popped (just before issuing a
+ // return).
+
+ // The CFA can is now calculated in the same way as in the first state.
+ w->Write<uint8_t>(DW_CFA_DEF_CFA_SF);
+ w->WriteULEB128(AMD64_RSP);
+ w->WriteSLEB128(-kPointerSize);
+
+ // The RBP
+ w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED);
+ w->WriteULEB128(AMD64_RBP);
+ w->WriteSLEB128(StandardFrameConstants::kCallerFPOffset);
+
+ // Last location described by this entry.
+ w->Write<uint8_t>(DW_CFA_SET_LOC);
+ w->Write<uint64_t>(desc_->CodeEnd());
+}
+
+
+bool UnwindInfoSection::WriteBodyInternal(Writer* w) {
+ uint32_t cie_position = WriteCIE(w);
+ WriteFDE(w, cie_position);
+ return true;
+}
+
+
+#endif // V8_TARGET_ARCH_X64
+
+static void CreateDWARFSections(CodeDescription* desc,
+ Zone* zone,
+ DebugObject* obj) {
+ if (desc->IsLineInfoAvailable()) {
+ obj->AddSection(new(zone) DebugInfoSection(desc));
+ obj->AddSection(new(zone) DebugAbbrevSection(desc));
+ obj->AddSection(new(zone) DebugLineSection(desc));
+ }
+#if V8_TARGET_ARCH_X64
+ obj->AddSection(new(zone) UnwindInfoSection(desc));
+#endif
+}
+
+
+// -------------------------------------------------------------------
+// Binary GDB JIT Interface as described in
+// http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
+extern "C" {
+ typedef enum {
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+ } JITAction;
+
+ struct JITCodeEntry {
+ JITCodeEntry* next_;
+ JITCodeEntry* prev_;
+ Address symfile_addr_;
+ uint64_t symfile_size_;
+ };
+
+ struct JITDescriptor {
+ uint32_t version_;
+ uint32_t action_flag_;
+ JITCodeEntry* relevant_entry_;
+ JITCodeEntry* first_entry_;
+ };
+
+ // GDB will place breakpoint into this function.
+ // To prevent GCC from inlining or removing it we place noinline attribute
+ // and inline assembler statement inside.
+ void __attribute__((noinline)) __jit_debug_register_code() {
+ __asm__("");
+ }
+
+ // GDB will inspect contents of this descriptor.
+ // Static initialization is necessary to prevent GDB from seeing
+ // uninitialized descriptor.
+ JITDescriptor __jit_debug_descriptor = { 1, 0, 0, 0 };
+
+#ifdef OBJECT_PRINT
+ void __gdb_print_v8_object(MaybeObject* object) {
+ object->Print();
+ PrintF(stdout, "\n");
+ }
+#endif
+}
+
+
+static JITCodeEntry* CreateCodeEntry(Address symfile_addr,
+ uintptr_t symfile_size) {
+ JITCodeEntry* entry = static_cast<JITCodeEntry*>(
+ malloc(sizeof(JITCodeEntry) + symfile_size));
+
+ entry->symfile_addr_ = reinterpret_cast<Address>(entry + 1);
+ entry->symfile_size_ = symfile_size;
+ OS::MemCopy(entry->symfile_addr_, symfile_addr, symfile_size);
+
+ entry->prev_ = entry->next_ = NULL;
+
+ return entry;
+}
+
+
+static void DestroyCodeEntry(JITCodeEntry* entry) {
+ free(entry);
+}
+
+
+static void RegisterCodeEntry(JITCodeEntry* entry,
+ bool dump_if_enabled,
+ const char* name_hint) {
+#if defined(DEBUG) && !defined(WIN32)
+ static int file_num = 0;
+ if (FLAG_gdbjit_dump && dump_if_enabled) {
+ static const int kMaxFileNameSize = 64;
+ static const char* kElfFilePrefix = "/tmp/elfdump";
+ static const char* kObjFileExt = ".o";
+ char file_name[64];
+
+ OS::SNPrintF(Vector<char>(file_name, kMaxFileNameSize),
+ "%s%s%d%s",
+ kElfFilePrefix,
+ (name_hint != NULL) ? name_hint : "",
+ file_num++,
+ kObjFileExt);
+ WriteBytes(file_name, entry->symfile_addr_, entry->symfile_size_);
+ }
+#endif
+
+ entry->next_ = __jit_debug_descriptor.first_entry_;
+ if (entry->next_ != NULL) entry->next_->prev_ = entry;
+ __jit_debug_descriptor.first_entry_ =
+ __jit_debug_descriptor.relevant_entry_ = entry;
+
+ __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
+ __jit_debug_register_code();
+}
+
+
+static void UnregisterCodeEntry(JITCodeEntry* entry) {
+ if (entry->prev_ != NULL) {
+ entry->prev_->next_ = entry->next_;
+ } else {
+ __jit_debug_descriptor.first_entry_ = entry->next_;
+ }
+
+ if (entry->next_ != NULL) {
+ entry->next_->prev_ = entry->prev_;
+ }
+
+ __jit_debug_descriptor.relevant_entry_ = entry;
+ __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
+ __jit_debug_register_code();
+}
+
+
+static JITCodeEntry* CreateELFObject(CodeDescription* desc, Isolate* isolate) {
+#ifdef __MACH_O
+ Zone zone(isolate);
+ MachO mach_o(&zone);
+ Writer w(&mach_o);
+
+ mach_o.AddSection(new(&zone) MachOTextSection(kCodeAlignment,
+ desc->CodeStart(),
+ desc->CodeSize()));
+
+ CreateDWARFSections(desc, &zone, &mach_o);
+
+ mach_o.Write(&w, desc->CodeStart(), desc->CodeSize());
+#else
+ Zone zone(isolate);
+ ELF elf(&zone);
+ Writer w(&elf);
+
+ int text_section_index = elf.AddSection(
+ new(&zone) FullHeaderELFSection(
+ ".text",
+ ELFSection::TYPE_NOBITS,
+ kCodeAlignment,
+ desc->CodeStart(),
+ 0,
+ desc->CodeSize(),
+ ELFSection::FLAG_ALLOC | ELFSection::FLAG_EXEC));
+
+ CreateSymbolsTable(desc, &zone, &elf, text_section_index);
+
+ CreateDWARFSections(desc, &zone, &elf);
+
+ elf.Write(&w);
+#endif
+
+ return CreateCodeEntry(w.buffer(), w.position());
+}
+
+
+static bool SameCodeObjects(void* key1, void* key2) {
+ return key1 == key2;
+}
+
+
+static HashMap* GetEntries() {
+ static HashMap* entries = NULL;
+ if (entries == NULL) {
+ entries = new HashMap(&SameCodeObjects);
+ }
+ return entries;
+}
+
+
+static uint32_t HashForCodeObject(Code* code) {
+ static const uintptr_t kGoldenRatio = 2654435761u;
+ uintptr_t hash = reinterpret_cast<uintptr_t>(code->address());
+ return static_cast<uint32_t>((hash >> kCodeAlignmentBits) * kGoldenRatio);
+}
+
+
+static const intptr_t kLineInfoTag = 0x1;
+
+
+static bool IsLineInfoTagged(void* ptr) {
+ return 0 != (reinterpret_cast<intptr_t>(ptr) & kLineInfoTag);
+}
+
+
+static void* TagLineInfo(GDBJITLineInfo* ptr) {
+ return reinterpret_cast<void*>(
+ reinterpret_cast<intptr_t>(ptr) | kLineInfoTag);
+}
+
+
+static GDBJITLineInfo* UntagLineInfo(void* ptr) {
+ return reinterpret_cast<GDBJITLineInfo*>(
+ reinterpret_cast<intptr_t>(ptr) & ~kLineInfoTag);
+}
+
+
+void GDBJITInterface::AddCode(Handle<Name> name,
+ Handle<Script> script,
+ Handle<Code> code,
+ CompilationInfo* info) {
+ if (!FLAG_gdbjit) return;
+
+ // Force initialization of line_ends array.
+ GetScriptLineNumber(script, 0);
+
+ if (!name.is_null() && name->IsString()) {
+ SmartArrayPointer<char> name_cstring =
+ Handle<String>::cast(name)->ToCString(DISALLOW_NULLS);
+ AddCode(*name_cstring, *code, GDBJITInterface::FUNCTION, *script, info);
+ } else {
+ AddCode("", *code, GDBJITInterface::FUNCTION, *script, info);
+ }
+}
+
+
+static void AddUnwindInfo(CodeDescription* desc) {
+#if V8_TARGET_ARCH_X64
+ if (desc->tag() == GDBJITInterface::FUNCTION) {
+ // To avoid propagating unwinding information through
+ // compilation pipeline we use an approximation.
+ // For most use cases this should not affect usability.
+ static const int kFramePointerPushOffset = 1;
+ static const int kFramePointerSetOffset = 4;
+ static const int kFramePointerPopOffset = -3;
+
+ uintptr_t frame_pointer_push_address =
+ desc->CodeStart() + kFramePointerPushOffset;
+
+ uintptr_t frame_pointer_set_address =
+ desc->CodeStart() + kFramePointerSetOffset;
+
+ uintptr_t frame_pointer_pop_address =
+ desc->CodeEnd() + kFramePointerPopOffset;
+
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_PUSH,
+ frame_pointer_push_address);
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_SET,
+ frame_pointer_set_address);
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_POP,
+ frame_pointer_pop_address);
+ } else {
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_PUSH,
+ desc->CodeStart());
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_SET,
+ desc->CodeStart());
+ desc->SetStackStateStartAddress(CodeDescription::POST_RBP_POP,
+ desc->CodeEnd());
+ }
+#endif // V8_TARGET_ARCH_X64
+}
+
+
+static LazyMutex mutex = LAZY_MUTEX_INITIALIZER;
+
+
+void GDBJITInterface::AddCode(const char* name,
+ Code* code,
+ GDBJITInterface::CodeTag tag,
+ Script* script,
+ CompilationInfo* info) {
+ if (!FLAG_gdbjit) return;
+
+ ScopedLock lock(mutex.Pointer());
+ DisallowHeapAllocation no_gc;
+
+ HashMap::Entry* e = GetEntries()->Lookup(code, HashForCodeObject(code), true);
+ if (e->value != NULL && !IsLineInfoTagged(e->value)) return;
+
+ GDBJITLineInfo* lineinfo = UntagLineInfo(e->value);
+ CodeDescription code_desc(name,
+ code,
+ script != NULL ? Handle<Script>(script)
+ : Handle<Script>(),
+ lineinfo,
+ tag,
+ info);
+
+ if (!FLAG_gdbjit_full && !code_desc.IsLineInfoAvailable()) {
+ delete lineinfo;
+ GetEntries()->Remove(code, HashForCodeObject(code));
+ return;
+ }
+
+ AddUnwindInfo(&code_desc);
+ Isolate* isolate = code->GetIsolate();
+ JITCodeEntry* entry = CreateELFObject(&code_desc, isolate);
+ ASSERT(!IsLineInfoTagged(entry));
+
+ delete lineinfo;
+ e->value = entry;
+
+ const char* name_hint = NULL;
+ bool should_dump = false;
+ if (FLAG_gdbjit_dump) {
+ if (strlen(FLAG_gdbjit_dump_filter) == 0) {
+ name_hint = name;
+ should_dump = true;
+ } else if (name != NULL) {
+ name_hint = strstr(name, FLAG_gdbjit_dump_filter);
+ should_dump = (name_hint != NULL);
+ }
+ }
+ RegisterCodeEntry(entry, should_dump, name_hint);
+}
+
+
+void GDBJITInterface::AddCode(GDBJITInterface::CodeTag tag,
+ const char* name,
+ Code* code) {
+ if (!FLAG_gdbjit) return;
+
+ EmbeddedVector<char, 256> buffer;
+ StringBuilder builder(buffer.start(), buffer.length());
+
+ builder.AddString(Tag2String(tag));
+ if ((name != NULL) && (*name != '\0')) {
+ builder.AddString(": ");
+ builder.AddString(name);
+ } else {
+ builder.AddFormatted(": code object %p", static_cast<void*>(code));
+ }
+
+ AddCode(builder.Finalize(), code, tag, NULL, NULL);
+}
+
+
+void GDBJITInterface::AddCode(GDBJITInterface::CodeTag tag,
+ Name* name,
+ Code* code) {
+ if (!FLAG_gdbjit) return;
+ if (name != NULL && name->IsString()) {
+ AddCode(tag, *String::cast(name)->ToCString(DISALLOW_NULLS), code);
+ } else {
+ AddCode(tag, "", code);
+ }
+}
+
+
+void GDBJITInterface::AddCode(GDBJITInterface::CodeTag tag, Code* code) {
+ if (!FLAG_gdbjit) return;
+
+ AddCode(tag, "", code);
+}
+
+
+void GDBJITInterface::RemoveCode(Code* code) {
+ if (!FLAG_gdbjit) return;
+
+ ScopedLock lock(mutex.Pointer());
+ HashMap::Entry* e = GetEntries()->Lookup(code,
+ HashForCodeObject(code),
+ false);
+ if (e == NULL) return;
+
+ if (IsLineInfoTagged(e->value)) {
+ delete UntagLineInfo(e->value);
+ } else {
+ JITCodeEntry* entry = static_cast<JITCodeEntry*>(e->value);
+ UnregisterCodeEntry(entry);
+ DestroyCodeEntry(entry);
+ }
+ e->value = NULL;
+ GetEntries()->Remove(code, HashForCodeObject(code));
+}
+
+
+void GDBJITInterface::RemoveCodeRange(Address start, Address end) {
+ HashMap* entries = GetEntries();
+ Zone zone(Isolate::Current());
+ ZoneList<Code*> dead_codes(1, &zone);
+
+ for (HashMap::Entry* e = entries->Start(); e != NULL; e = entries->Next(e)) {
+ Code* code = reinterpret_cast<Code*>(e->key);
+ if (code->address() >= start && code->address() < end) {
+ dead_codes.Add(code, &zone);
+ }
+ }
+
+ for (int i = 0; i < dead_codes.length(); i++) {
+ RemoveCode(dead_codes.at(i));
+ }
+}
+
+
+void GDBJITInterface::RegisterDetailedLineInfo(Code* code,
+ GDBJITLineInfo* line_info) {
+ ScopedLock lock(mutex.Pointer());
+ ASSERT(!IsLineInfoTagged(line_info));
+ HashMap::Entry* e = GetEntries()->Lookup(code, HashForCodeObject(code), true);
+ ASSERT(e->value == NULL);
+ e->value = TagLineInfo(line_info);
+}
+
+
+} } // namespace v8::internal
+#endif
diff --git a/chromium/v8/src/gdb-jit.h b/chromium/v8/src/gdb-jit.h
new file mode 100644
index 00000000000..a34d3d3012e
--- /dev/null
+++ b/chromium/v8/src/gdb-jit.h
@@ -0,0 +1,146 @@
+// 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.
+
+#ifndef V8_GDB_JIT_H_
+#define V8_GDB_JIT_H_
+
+#include "allocation.h"
+
+//
+// Basic implementation of GDB JIT Interface client.
+// GBD JIT Interface is supported in GDB 7.0 and above.
+// Currently on x64 and ia32 architectures and Linux OS are supported.
+//
+
+#ifdef ENABLE_GDB_JIT_INTERFACE
+#include "v8.h"
+#include "factory.h"
+
+namespace v8 {
+namespace internal {
+
+class CompilationInfo;
+
+#define CODE_TAGS_LIST(V) \
+ V(LOAD_IC) \
+ V(KEYED_LOAD_IC) \
+ V(STORE_IC) \
+ V(KEYED_STORE_IC) \
+ V(CALL_IC) \
+ V(CALL_INITIALIZE) \
+ V(CALL_PRE_MONOMORPHIC) \
+ V(CALL_NORMAL) \
+ V(CALL_MEGAMORPHIC) \
+ V(CALL_MISS) \
+ V(STUB) \
+ V(BUILTIN) \
+ V(SCRIPT) \
+ V(EVAL) \
+ V(FUNCTION)
+
+class GDBJITLineInfo : public Malloced {
+ public:
+ GDBJITLineInfo()
+ : pc_info_(10) { }
+
+ void SetPosition(intptr_t pc, int pos, bool is_statement) {
+ AddPCInfo(PCInfo(pc, pos, is_statement));
+ }
+
+ struct PCInfo {
+ PCInfo(intptr_t pc, int pos, bool is_statement)
+ : pc_(pc), pos_(pos), is_statement_(is_statement) { }
+
+ intptr_t pc_;
+ int pos_;
+ bool is_statement_;
+ };
+
+ List<PCInfo>* pc_info() {
+ return &pc_info_;
+ }
+
+ private:
+ void AddPCInfo(const PCInfo& pc_info) {
+ pc_info_.Add(pc_info);
+ }
+
+ List<PCInfo> pc_info_;
+};
+
+
+class GDBJITInterface: public AllStatic {
+ public:
+ enum CodeTag {
+#define V(x) x,
+ CODE_TAGS_LIST(V)
+#undef V
+ TAG_COUNT
+ };
+
+ static const char* Tag2String(CodeTag tag) {
+ switch (tag) {
+#define V(x) case x: return #x;
+ CODE_TAGS_LIST(V)
+#undef V
+ default:
+ return NULL;
+ }
+ }
+
+ static void AddCode(const char* name,
+ Code* code,
+ CodeTag tag,
+ Script* script,
+ CompilationInfo* info);
+
+ static void AddCode(Handle<Name> name,
+ Handle<Script> script,
+ Handle<Code> code,
+ CompilationInfo* info);
+
+ static void AddCode(CodeTag tag, Name* name, Code* code);
+
+ static void AddCode(CodeTag tag, const char* name, Code* code);
+
+ static void AddCode(CodeTag tag, Code* code);
+
+ static void RemoveCode(Code* code);
+
+ static void RemoveCodeRange(Address start, Address end);
+
+ static void RegisterDetailedLineInfo(Code* code, GDBJITLineInfo* line_info);
+};
+
+#define GDBJIT(action) GDBJITInterface::action
+
+} } // namespace v8::internal
+#else
+#define GDBJIT(action) ((void) 0)
+#endif
+
+#endif
diff --git a/chromium/v8/src/generator.js b/chromium/v8/src/generator.js
new file mode 100644
index 00000000000..3c8ea6f3194
--- /dev/null
+++ b/chromium/v8/src/generator.js
@@ -0,0 +1,92 @@
+// Copyright 2013 the V8 project authors. 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.
+
+"use strict";
+
+// This file relies on the fact that the following declarations have been made
+// in runtime.js:
+// var $Function = global.Function;
+
+// ----------------------------------------------------------------------------
+
+
+// Generator functions and objects are specified by ES6, sections 15.19.3 and
+// 15.19.4.
+
+function GeneratorObjectNext(value) {
+ if (!IS_GENERATOR(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['[Generator].prototype.next', this]);
+ }
+
+ return %_GeneratorNext(this, value);
+}
+
+function GeneratorObjectThrow(exn) {
+ if (!IS_GENERATOR(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['[Generator].prototype.throw', this]);
+ }
+
+ return %_GeneratorThrow(this, exn);
+}
+
+function GeneratorFunctionPrototypeConstructor(x) {
+ if (%_IsConstructCall()) {
+ throw MakeTypeError('not_constructor', ['GeneratorFunctionPrototype']);
+ }
+}
+
+function GeneratorFunctionConstructor(arg1) { // length == 1
+ var source = NewFunctionString(arguments, 'function*');
+ var global_receiver = %GlobalReceiver(global);
+ // Compile the string in the constructor and not a helper so that errors
+ // appear to come from here.
+ var f = %_CallFunction(global_receiver, %CompileString(source, true));
+ %FunctionMarkNameShouldPrintAsAnonymous(f);
+ return f;
+}
+
+
+function SetUpGenerators() {
+ %CheckIsBootstrapping();
+ var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;
+ InstallFunctions(GeneratorObjectPrototype,
+ DONT_ENUM | DONT_DELETE | READ_ONLY,
+ ["next", GeneratorObjectNext,
+ "throw", GeneratorObjectThrow]);
+ %SetProperty(GeneratorObjectPrototype, "constructor",
+ GeneratorFunctionPrototype, DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetPrototype(GeneratorFunctionPrototype, $Function.prototype);
+ %SetCode(GeneratorFunctionPrototype, GeneratorFunctionPrototypeConstructor);
+ %SetProperty(GeneratorFunctionPrototype, "constructor",
+ GeneratorFunction, DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetPrototype(GeneratorFunction, $Function);
+ %SetCode(GeneratorFunction, GeneratorFunctionConstructor);
+}
+
+SetUpGenerators();
diff --git a/chromium/v8/src/global-handles.cc b/chromium/v8/src/global-handles.cc
new file mode 100644
index 00000000000..5df9dd4c6c7
--- /dev/null
+++ b/chromium/v8/src/global-handles.cc
@@ -0,0 +1,1086 @@
+// 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.
+
+#include "v8.h"
+
+#include "api.h"
+#include "global-handles.h"
+
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+ObjectGroup::~ObjectGroup() {
+ if (info != NULL) info->Dispose();
+ delete[] objects;
+}
+
+
+ImplicitRefGroup::~ImplicitRefGroup() {
+ delete[] children;
+}
+
+
+class GlobalHandles::Node {
+ public:
+ // State transition diagram:
+ // FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE }
+ enum State {
+ FREE = 0,
+ NORMAL, // Normal global handle.
+ WEAK, // Flagged as weak but not yet finalized.
+ PENDING, // Has been recognized as only reachable by weak handles.
+ NEAR_DEATH // Callback has informed the handle is near death.
+ };
+
+ // Maps handle location (slot) to the containing node.
+ static Node* FromLocation(Object** location) {
+ ASSERT(OFFSET_OF(Node, object_) == 0);
+ return reinterpret_cast<Node*>(location);
+ }
+
+ Node() {
+ ASSERT(OFFSET_OF(Node, class_id_) == Internals::kNodeClassIdOffset);
+ ASSERT(OFFSET_OF(Node, flags_) == Internals::kNodeFlagsOffset);
+ STATIC_ASSERT(static_cast<int>(NodeState::kMask) ==
+ Internals::kNodeStateMask);
+ STATIC_ASSERT(WEAK == Internals::kNodeStateIsWeakValue);
+ STATIC_ASSERT(PENDING == Internals::kNodeStateIsPendingValue);
+ STATIC_ASSERT(NEAR_DEATH == Internals::kNodeStateIsNearDeathValue);
+ STATIC_ASSERT(static_cast<int>(IsIndependent::kShift) ==
+ Internals::kNodeIsIndependentShift);
+ STATIC_ASSERT(static_cast<int>(IsPartiallyDependent::kShift) ==
+ Internals::kNodeIsPartiallyDependentShift);
+ }
+
+#ifdef ENABLE_EXTRA_CHECKS
+ ~Node() {
+ // TODO(1428): if it's a weak handle we should have invoked its callback.
+ // Zap the values for eager trapping.
+ object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
+ class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
+ index_ = 0;
+ set_independent(false);
+ set_partially_dependent(false);
+ set_in_new_space_list(false);
+ parameter_or_next_free_.next_free = NULL;
+ weak_reference_callback_ = NULL;
+ }
+#endif
+
+ void Initialize(int index, Node** first_free) {
+ index_ = static_cast<uint8_t>(index);
+ ASSERT(static_cast<int>(index_) == index);
+ set_state(FREE);
+ set_in_new_space_list(false);
+ parameter_or_next_free_.next_free = *first_free;
+ *first_free = this;
+ }
+
+ void Acquire(Object* object) {
+ ASSERT(state() == FREE);
+ object_ = object;
+ class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
+ set_independent(false);
+ set_partially_dependent(false);
+ set_state(NORMAL);
+ parameter_or_next_free_.parameter = NULL;
+ weak_reference_callback_ = NULL;
+ IncreaseBlockUses();
+ }
+
+ void Release() {
+ ASSERT(state() != FREE);
+ set_state(FREE);
+#ifdef ENABLE_EXTRA_CHECKS
+ // Zap the values for eager trapping.
+ object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
+ class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
+ set_independent(false);
+ set_partially_dependent(false);
+ weak_reference_callback_ = NULL;
+#endif
+ DecreaseBlockUses();
+ }
+
+ // Object slot accessors.
+ Object* object() const { return object_; }
+ Object** location() { return &object_; }
+ Handle<Object> handle() { return Handle<Object>(location()); }
+
+ // Wrapper class ID accessors.
+ bool has_wrapper_class_id() const {
+ return class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId;
+ }
+
+ uint16_t wrapper_class_id() const { return class_id_; }
+
+ // State and flag accessors.
+
+ State state() const {
+ return NodeState::decode(flags_);
+ }
+ void set_state(State state) {
+ flags_ = NodeState::update(flags_, state);
+ }
+
+ bool is_independent() {
+ return IsIndependent::decode(flags_);
+ }
+ void set_independent(bool v) {
+ flags_ = IsIndependent::update(flags_, v);
+ }
+
+ bool is_partially_dependent() {
+ return IsPartiallyDependent::decode(flags_);
+ }
+ void set_partially_dependent(bool v) {
+ flags_ = IsPartiallyDependent::update(flags_, v);
+ }
+
+ bool is_in_new_space_list() {
+ return IsInNewSpaceList::decode(flags_);
+ }
+ void set_in_new_space_list(bool v) {
+ flags_ = IsInNewSpaceList::update(flags_, v);
+ }
+
+ bool IsNearDeath() const {
+ // Check for PENDING to ensure correct answer when processing callbacks.
+ return state() == PENDING || state() == NEAR_DEATH;
+ }
+
+ bool IsWeak() const { return state() == WEAK; }
+
+ bool IsRetainer() const { return state() != FREE; }
+
+ bool IsStrongRetainer() const { return state() == NORMAL; }
+
+ bool IsWeakRetainer() const {
+ return state() == WEAK || state() == PENDING || state() == NEAR_DEATH;
+ }
+
+ void MarkPending() {
+ ASSERT(state() == WEAK);
+ set_state(PENDING);
+ }
+
+ // Independent flag accessors.
+ void MarkIndependent() {
+ ASSERT(state() != FREE);
+ set_independent(true);
+ }
+
+ void MarkPartiallyDependent() {
+ ASSERT(state() != FREE);
+ if (GetGlobalHandles()->isolate()->heap()->InNewSpace(object_)) {
+ set_partially_dependent(true);
+ }
+ }
+ void clear_partially_dependent() { set_partially_dependent(false); }
+
+ // Callback accessor.
+ // TODO(svenpanne) Re-enable or nuke later.
+ // WeakReferenceCallback callback() { return callback_; }
+
+ // Callback parameter accessors.
+ void set_parameter(void* parameter) {
+ ASSERT(state() != FREE);
+ parameter_or_next_free_.parameter = parameter;
+ }
+ void* parameter() const {
+ ASSERT(state() != FREE);
+ return parameter_or_next_free_.parameter;
+ }
+
+ // Accessors for next free node in the free list.
+ Node* next_free() {
+ ASSERT(state() == FREE);
+ return parameter_or_next_free_.next_free;
+ }
+ void set_next_free(Node* value) {
+ ASSERT(state() == FREE);
+ parameter_or_next_free_.next_free = value;
+ }
+
+ void MakeWeak(void* parameter,
+ RevivableCallback weak_reference_callback) {
+ ASSERT(state() != FREE);
+ set_state(WEAK);
+ set_parameter(parameter);
+ weak_reference_callback_ = weak_reference_callback;
+ }
+
+ void ClearWeakness() {
+ ASSERT(state() != FREE);
+ set_state(NORMAL);
+ set_parameter(NULL);
+ }
+
+ bool PostGarbageCollectionProcessing(Isolate* isolate) {
+ if (state() != Node::PENDING) return false;
+ if (weak_reference_callback_ == NULL) {
+ Release();
+ return false;
+ }
+ void* par = parameter();
+ set_state(NEAR_DEATH);
+ set_parameter(NULL);
+
+ Object** object = location();
+ {
+ // Check that we are not passing a finalized external string to
+ // the callback.
+ ASSERT(!object_->IsExternalAsciiString() ||
+ ExternalAsciiString::cast(object_)->resource() != NULL);
+ ASSERT(!object_->IsExternalTwoByteString() ||
+ ExternalTwoByteString::cast(object_)->resource() != NULL);
+ // Leaving V8.
+ VMState<EXTERNAL> state(isolate);
+ HandleScope handle_scope(isolate);
+ weak_reference_callback_(reinterpret_cast<v8::Isolate*>(isolate),
+ reinterpret_cast<Persistent<Value>*>(&object),
+ par);
+ }
+ // 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;
+ }
+
+ private:
+ inline NodeBlock* FindBlock();
+ inline GlobalHandles* GetGlobalHandles();
+ inline void IncreaseBlockUses();
+ inline void DecreaseBlockUses();
+
+ // Storage for object pointer.
+ // Placed first to avoid offset computation.
+ Object* object_;
+
+ // Next word stores class_id, index, state, and independent.
+ // Note: the most aligned fields should go first.
+
+ // Wrapper class ID.
+ uint16_t class_id_;
+
+ // Index in the containing handle block.
+ uint8_t index_;
+
+ // This stores three flags (independent, partially_dependent and
+ // in_new_space_list) and a State.
+ class NodeState: public BitField<State, 0, 4> {};
+ class IsIndependent: public BitField<bool, 4, 1> {};
+ class IsPartiallyDependent: public BitField<bool, 5, 1> {};
+ class IsInNewSpaceList: public BitField<bool, 6, 1> {};
+
+ uint8_t flags_;
+
+ // Handle specific callback - might be a weak reference in disguise.
+ RevivableCallback weak_reference_callback_;
+
+ // Provided data for callback. In FREE state, this is used for
+ // the free list link.
+ union {
+ void* parameter;
+ Node* next_free;
+ } parameter_or_next_free_;
+
+ DISALLOW_COPY_AND_ASSIGN(Node);
+};
+
+
+class GlobalHandles::NodeBlock {
+ public:
+ static const int kSize = 256;
+
+ explicit NodeBlock(GlobalHandles* global_handles, NodeBlock* next)
+ : next_(next),
+ used_nodes_(0),
+ next_used_(NULL),
+ prev_used_(NULL),
+ global_handles_(global_handles) {}
+
+ void PutNodesOnFreeList(Node** first_free) {
+ for (int i = kSize - 1; i >= 0; --i) {
+ nodes_[i].Initialize(i, first_free);
+ }
+ }
+
+ Node* node_at(int index) {
+ ASSERT(0 <= index && index < kSize);
+ return &nodes_[index];
+ }
+
+ void IncreaseUses() {
+ ASSERT(used_nodes_ < kSize);
+ if (used_nodes_++ == 0) {
+ NodeBlock* old_first = global_handles_->first_used_block_;
+ global_handles_->first_used_block_ = this;
+ next_used_ = old_first;
+ prev_used_ = NULL;
+ if (old_first == NULL) return;
+ old_first->prev_used_ = this;
+ }
+ }
+
+ void DecreaseUses() {
+ ASSERT(used_nodes_ > 0);
+ if (--used_nodes_ == 0) {
+ if (next_used_ != NULL) next_used_->prev_used_ = prev_used_;
+ if (prev_used_ != NULL) prev_used_->next_used_ = next_used_;
+ if (this == global_handles_->first_used_block_) {
+ global_handles_->first_used_block_ = next_used_;
+ }
+ }
+ }
+
+ GlobalHandles* global_handles() { return global_handles_; }
+
+ // Next block in the list of all blocks.
+ NodeBlock* next() const { return next_; }
+
+ // Next/previous block in the list of blocks with used nodes.
+ NodeBlock* next_used() const { return next_used_; }
+ NodeBlock* prev_used() const { return prev_used_; }
+
+ private:
+ Node nodes_[kSize];
+ NodeBlock* const next_;
+ int used_nodes_;
+ NodeBlock* next_used_;
+ NodeBlock* prev_used_;
+ GlobalHandles* global_handles_;
+};
+
+
+GlobalHandles* GlobalHandles::Node::GetGlobalHandles() {
+ return FindBlock()->global_handles();
+}
+
+
+GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() {
+ intptr_t ptr = reinterpret_cast<intptr_t>(this);
+ ptr = ptr - index_ * sizeof(Node);
+ NodeBlock* block = reinterpret_cast<NodeBlock*>(ptr);
+ ASSERT(block->node_at(index_) == this);
+ return block;
+}
+
+
+void GlobalHandles::Node::IncreaseBlockUses() {
+ NodeBlock* node_block = FindBlock();
+ node_block->IncreaseUses();
+ GlobalHandles* global_handles = node_block->global_handles();
+ global_handles->isolate()->counters()->global_handles()->Increment();
+ global_handles->number_of_global_handles_++;
+}
+
+
+void GlobalHandles::Node::DecreaseBlockUses() {
+ NodeBlock* node_block = FindBlock();
+ GlobalHandles* global_handles = node_block->global_handles();
+ parameter_or_next_free_.next_free = global_handles->first_free_;
+ global_handles->first_free_ = this;
+ node_block->DecreaseUses();
+ global_handles->isolate()->counters()->global_handles()->Decrement();
+ global_handles->number_of_global_handles_--;
+}
+
+
+class GlobalHandles::NodeIterator {
+ public:
+ explicit NodeIterator(GlobalHandles* global_handles)
+ : block_(global_handles->first_used_block_),
+ index_(0) {}
+
+ bool done() const { return block_ == NULL; }
+
+ Node* node() const {
+ ASSERT(!done());
+ return block_->node_at(index_);
+ }
+
+ void Advance() {
+ ASSERT(!done());
+ if (++index_ < NodeBlock::kSize) return;
+ index_ = 0;
+ block_ = block_->next_used();
+ }
+
+ private:
+ NodeBlock* block_;
+ int index_;
+
+ DISALLOW_COPY_AND_ASSIGN(NodeIterator);
+};
+
+
+GlobalHandles::GlobalHandles(Isolate* isolate)
+ : isolate_(isolate),
+ number_of_global_handles_(0),
+ first_block_(NULL),
+ first_used_block_(NULL),
+ first_free_(NULL),
+ post_gc_processing_count_(0),
+ object_group_connections_(kObjectGroupConnectionsCapacity) {}
+
+
+GlobalHandles::~GlobalHandles() {
+ NodeBlock* block = first_block_;
+ while (block != NULL) {
+ NodeBlock* tmp = block->next();
+ delete block;
+ block = tmp;
+ }
+ first_block_ = NULL;
+}
+
+
+Handle<Object> GlobalHandles::Create(Object* value) {
+ if (first_free_ == NULL) {
+ first_block_ = new NodeBlock(this, first_block_);
+ first_block_->PutNodesOnFreeList(&first_free_);
+ }
+ ASSERT(first_free_ != NULL);
+ // Take the first node in the free list.
+ Node* result = first_free_;
+ first_free_ = result->next_free();
+ result->Acquire(value);
+ if (isolate_->heap()->InNewSpace(value) &&
+ !result->is_in_new_space_list()) {
+ new_space_nodes_.Add(result);
+ result->set_in_new_space_list(true);
+ }
+ return result->handle();
+}
+
+
+void GlobalHandles::Destroy(Object** location) {
+ if (location != NULL) Node::FromLocation(location)->Release();
+}
+
+
+void GlobalHandles::MakeWeak(Object** location,
+ void* parameter,
+ RevivableCallback weak_reference_callback) {
+ ASSERT(weak_reference_callback != NULL);
+ Node::FromLocation(location)->MakeWeak(parameter, weak_reference_callback);
+}
+
+
+void GlobalHandles::ClearWeakness(Object** location) {
+ Node::FromLocation(location)->ClearWeakness();
+}
+
+
+void GlobalHandles::MarkIndependent(Object** location) {
+ Node::FromLocation(location)->MarkIndependent();
+}
+
+
+void GlobalHandles::MarkPartiallyDependent(Object** location) {
+ Node::FromLocation(location)->MarkPartiallyDependent();
+}
+
+
+bool GlobalHandles::IsIndependent(Object** location) {
+ return Node::FromLocation(location)->is_independent();
+}
+
+
+bool GlobalHandles::IsNearDeath(Object** location) {
+ return Node::FromLocation(location)->IsNearDeath();
+}
+
+
+bool GlobalHandles::IsWeak(Object** location) {
+ return Node::FromLocation(location)->IsWeak();
+}
+
+
+void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ if (it.node()->IsWeakRetainer()) v->VisitPointer(it.node()->location());
+ }
+}
+
+
+void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ if (it.node()->IsWeak() && f(it.node()->location())) {
+ it.node()->MarkPending();
+ }
+ }
+}
+
+
+void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) {
+ for (int i = 0; i < new_space_nodes_.length(); ++i) {
+ Node* node = new_space_nodes_[i];
+ if (node->IsStrongRetainer() ||
+ (node->IsWeakRetainer() && !node->is_independent() &&
+ !node->is_partially_dependent())) {
+ v->VisitPointer(node->location());
+ }
+ }
+}
+
+
+void GlobalHandles::IdentifyNewSpaceWeakIndependentHandles(
+ WeakSlotCallbackWithHeap f) {
+ for (int i = 0; i < new_space_nodes_.length(); ++i) {
+ Node* node = new_space_nodes_[i];
+ ASSERT(node->is_in_new_space_list());
+ if ((node->is_independent() || node->is_partially_dependent()) &&
+ node->IsWeak() && f(isolate_->heap(), node->location())) {
+ node->MarkPending();
+ }
+ }
+}
+
+
+void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
+ for (int i = 0; i < new_space_nodes_.length(); ++i) {
+ Node* node = new_space_nodes_[i];
+ ASSERT(node->is_in_new_space_list());
+ if ((node->is_independent() || node->is_partially_dependent()) &&
+ node->IsWeakRetainer()) {
+ v->VisitPointer(node->location());
+ }
+ }
+}
+
+
+bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v,
+ WeakSlotCallbackWithHeap can_skip) {
+ ComputeObjectGroupsAndImplicitReferences();
+ int last = 0;
+ bool any_group_was_visited = false;
+ for (int i = 0; i < object_groups_.length(); i++) {
+ ObjectGroup* entry = object_groups_.at(i);
+ ASSERT(entry != NULL);
+
+ Object*** objects = entry->objects;
+ bool group_should_be_visited = false;
+ for (size_t j = 0; j < entry->length; j++) {
+ Object* object = *objects[j];
+ if (object->IsHeapObject()) {
+ if (!can_skip(isolate_->heap(), &object)) {
+ group_should_be_visited = true;
+ break;
+ }
+ }
+ }
+
+ if (!group_should_be_visited) {
+ object_groups_[last++] = entry;
+ continue;
+ }
+
+ // An object in the group requires visiting, so iterate over all
+ // objects in the group.
+ for (size_t j = 0; j < entry->length; ++j) {
+ Object* object = *objects[j];
+ if (object->IsHeapObject()) {
+ v->VisitPointer(&object);
+ any_group_was_visited = true;
+ }
+ }
+
+ // Once the entire group has been iterated over, set the object
+ // group to NULL so it won't be processed again.
+ delete entry;
+ object_groups_.at(i) = NULL;
+ }
+ object_groups_.Rewind(last);
+ return any_group_was_visited;
+}
+
+
+bool GlobalHandles::PostGarbageCollectionProcessing(
+ GarbageCollector collector, GCTracer* tracer) {
+ // Process weak global handle callbacks. This must be done after the
+ // GC is completely done, because the callbacks may invoke arbitrary
+ // API functions.
+ ASSERT(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
+ const int initial_post_gc_processing_count = ++post_gc_processing_count_;
+ bool next_gc_likely_to_collect_more = false;
+ if (collector == SCAVENGER) {
+ for (int i = 0; i < new_space_nodes_.length(); ++i) {
+ Node* node = new_space_nodes_[i];
+ ASSERT(node->is_in_new_space_list());
+ if (!node->IsRetainer()) {
+ // Free nodes do not have weak callbacks. Do not use them to compute
+ // the next_gc_likely_to_collect_more.
+ continue;
+ }
+ // Skip dependent handles. Their weak callbacks might expect to be
+ // called between two global garbage collection callbacks which
+ // are not called for minor collections.
+ if (!node->is_independent() && !node->is_partially_dependent()) {
+ continue;
+ }
+ node->clear_partially_dependent();
+ if (node->PostGarbageCollectionProcessing(isolate_)) {
+ if (initial_post_gc_processing_count != post_gc_processing_count_) {
+ // Weak callback triggered another GC and another round of
+ // PostGarbageCollection processing. The current node might
+ // have been deleted in that round, so we need to bail out (or
+ // restart the processing).
+ return next_gc_likely_to_collect_more;
+ }
+ }
+ if (!node->IsRetainer()) {
+ next_gc_likely_to_collect_more = true;
+ }
+ }
+ } else {
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ if (!it.node()->IsRetainer()) {
+ // Free nodes do not have weak callbacks. Do not use them to compute
+ // the next_gc_likely_to_collect_more.
+ continue;
+ }
+ it.node()->clear_partially_dependent();
+ if (it.node()->PostGarbageCollectionProcessing(isolate_)) {
+ if (initial_post_gc_processing_count != post_gc_processing_count_) {
+ // See the comment above.
+ return next_gc_likely_to_collect_more;
+ }
+ }
+ if (!it.node()->IsRetainer()) {
+ next_gc_likely_to_collect_more = true;
+ }
+ }
+ }
+ // Update the list of new space nodes.
+ int last = 0;
+ for (int i = 0; i < new_space_nodes_.length(); ++i) {
+ Node* node = new_space_nodes_[i];
+ ASSERT(node->is_in_new_space_list());
+ if (node->IsRetainer()) {
+ if (isolate_->heap()->InNewSpace(node->object())) {
+ new_space_nodes_[last++] = node;
+ tracer->increment_nodes_copied_in_new_space();
+ } else {
+ node->set_in_new_space_list(false);
+ tracer->increment_nodes_promoted();
+ }
+ } else {
+ node->set_in_new_space_list(false);
+ tracer->increment_nodes_died_in_new_space();
+ }
+ }
+ new_space_nodes_.Rewind(last);
+ return next_gc_likely_to_collect_more;
+}
+
+
+void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ if (it.node()->IsStrongRetainer()) {
+ v->VisitPointer(it.node()->location());
+ }
+ }
+}
+
+
+void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ if (it.node()->IsRetainer()) {
+ v->VisitPointer(it.node()->location());
+ }
+ }
+}
+
+
+void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ if (it.node()->IsRetainer() && it.node()->has_wrapper_class_id()) {
+ v->VisitEmbedderReference(it.node()->location(),
+ it.node()->wrapper_class_id());
+ }
+ }
+}
+
+
+void GlobalHandles::IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v) {
+ for (int i = 0; i < new_space_nodes_.length(); ++i) {
+ Node* node = new_space_nodes_[i];
+ if (node->IsRetainer() && node->has_wrapper_class_id()) {
+ v->VisitEmbedderReference(node->location(),
+ node->wrapper_class_id());
+ }
+ }
+}
+
+
+int GlobalHandles::NumberOfWeakHandles() {
+ int count = 0;
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ if (it.node()->IsWeakRetainer()) {
+ count++;
+ }
+ }
+ return count;
+}
+
+
+int GlobalHandles::NumberOfGlobalObjectWeakHandles() {
+ int count = 0;
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ if (it.node()->IsWeakRetainer() &&
+ it.node()->object()->IsJSGlobalObject()) {
+ count++;
+ }
+ }
+ return count;
+}
+
+
+void GlobalHandles::RecordStats(HeapStats* stats) {
+ *stats->global_handle_count = 0;
+ *stats->weak_global_handle_count = 0;
+ *stats->pending_global_handle_count = 0;
+ *stats->near_death_global_handle_count = 0;
+ *stats->free_global_handle_count = 0;
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ *stats->global_handle_count += 1;
+ if (it.node()->state() == Node::WEAK) {
+ *stats->weak_global_handle_count += 1;
+ } else if (it.node()->state() == Node::PENDING) {
+ *stats->pending_global_handle_count += 1;
+ } else if (it.node()->state() == Node::NEAR_DEATH) {
+ *stats->near_death_global_handle_count += 1;
+ } else if (it.node()->state() == Node::FREE) {
+ *stats->free_global_handle_count += 1;
+ }
+ }
+}
+
+#ifdef DEBUG
+
+void GlobalHandles::PrintStats() {
+ int total = 0;
+ int weak = 0;
+ int pending = 0;
+ int near_death = 0;
+ int destroyed = 0;
+
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ total++;
+ if (it.node()->state() == Node::WEAK) weak++;
+ if (it.node()->state() == Node::PENDING) pending++;
+ if (it.node()->state() == Node::NEAR_DEATH) near_death++;
+ if (it.node()->state() == Node::FREE) destroyed++;
+ }
+
+ PrintF("Global Handle Statistics:\n");
+ PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total);
+ PrintF(" # weak = %d\n", weak);
+ PrintF(" # pending = %d\n", pending);
+ PrintF(" # near_death = %d\n", near_death);
+ PrintF(" # free = %d\n", destroyed);
+ PrintF(" # total = %d\n", total);
+}
+
+
+void GlobalHandles::Print() {
+ PrintF("Global handles:\n");
+ for (NodeIterator it(this); !it.done(); it.Advance()) {
+ PrintF(" handle %p to %p%s\n",
+ reinterpret_cast<void*>(it.node()->location()),
+ reinterpret_cast<void*>(it.node()->object()),
+ it.node()->IsWeak() ? " (weak)" : "");
+ }
+}
+
+#endif
+
+
+
+void GlobalHandles::AddObjectGroup(Object*** handles,
+ size_t length,
+ v8::RetainedObjectInfo* info) {
+#ifdef DEBUG
+ for (size_t i = 0; i < length; ++i) {
+ ASSERT(!Node::FromLocation(handles[i])->is_independent());
+ }
+#endif
+ if (length == 0) {
+ if (info != NULL) info->Dispose();
+ return;
+ }
+ ObjectGroup* group = new ObjectGroup(length);
+ for (size_t i = 0; i < length; ++i)
+ group->objects[i] = handles[i];
+ group->info = info;
+ object_groups_.Add(group);
+}
+
+
+void GlobalHandles::SetObjectGroupId(Object** handle,
+ UniqueId id) {
+ object_group_connections_.Add(ObjectGroupConnection(id, handle));
+}
+
+
+void GlobalHandles::SetRetainedObjectInfo(UniqueId id,
+ RetainedObjectInfo* info) {
+ retainer_infos_.Add(ObjectGroupRetainerInfo(id, info));
+}
+
+
+void GlobalHandles::AddImplicitReferences(HeapObject** parent,
+ Object*** children,
+ size_t length) {
+#ifdef DEBUG
+ ASSERT(!Node::FromLocation(BitCast<Object**>(parent))->is_independent());
+ for (size_t i = 0; i < length; ++i) {
+ ASSERT(!Node::FromLocation(children[i])->is_independent());
+ }
+#endif
+ if (length == 0) return;
+ ImplicitRefGroup* group = new ImplicitRefGroup(parent, length);
+ for (size_t i = 0; i < length; ++i)
+ group->children[i] = children[i];
+ implicit_ref_groups_.Add(group);
+}
+
+
+void GlobalHandles::SetReferenceFromGroup(UniqueId id, Object** child) {
+ ASSERT(!Node::FromLocation(child)->is_independent());
+ implicit_ref_connections_.Add(ObjectGroupConnection(id, child));
+}
+
+
+void GlobalHandles::SetReference(HeapObject** parent, Object** child) {
+ ASSERT(!Node::FromLocation(child)->is_independent());
+ ImplicitRefGroup* group = new ImplicitRefGroup(parent, 1);
+ group->children[0] = child;
+ implicit_ref_groups_.Add(group);
+}
+
+
+void GlobalHandles::RemoveObjectGroups() {
+ for (int i = 0; i < object_groups_.length(); i++)
+ delete object_groups_.at(i);
+ object_groups_.Clear();
+ for (int i = 0; i < retainer_infos_.length(); ++i)
+ retainer_infos_[i].info->Dispose();
+ retainer_infos_.Clear();
+ object_group_connections_.Clear();
+ object_group_connections_.Initialize(kObjectGroupConnectionsCapacity);
+}
+
+
+void GlobalHandles::RemoveImplicitRefGroups() {
+ for (int i = 0; i < implicit_ref_groups_.length(); i++) {
+ delete implicit_ref_groups_.at(i);
+ }
+ implicit_ref_groups_.Clear();
+ implicit_ref_connections_.Clear();
+}
+
+
+void GlobalHandles::TearDown() {
+ // TODO(1428): invoke weak callbacks.
+}
+
+
+void GlobalHandles::ComputeObjectGroupsAndImplicitReferences() {
+ if (object_group_connections_.length() == 0) {
+ for (int i = 0; i < retainer_infos_.length(); ++i)
+ retainer_infos_[i].info->Dispose();
+ retainer_infos_.Clear();
+ implicit_ref_connections_.Clear();
+ return;
+ }
+
+ object_group_connections_.Sort();
+ retainer_infos_.Sort();
+ implicit_ref_connections_.Sort();
+
+ int info_index = 0; // For iterating retainer_infos_.
+ UniqueId current_group_id(0);
+ int current_group_start = 0;
+
+ int current_implicit_refs_start = 0;
+ int current_implicit_refs_end = 0;
+ for (int i = 0; i <= object_group_connections_.length(); ++i) {
+ if (i == 0)
+ current_group_id = object_group_connections_[i].id;
+ if (i == object_group_connections_.length() ||
+ current_group_id != object_group_connections_[i].id) {
+ // Group detected: objects in indices [current_group_start, i[.
+
+ // Find out which implicit references are related to this group. (We want
+ // to ignore object groups which only have 1 object, but that object is
+ // needed as a representative object for the implicit refrerence group.)
+ while (current_implicit_refs_start < implicit_ref_connections_.length() &&
+ implicit_ref_connections_[current_implicit_refs_start].id <
+ current_group_id)
+ ++current_implicit_refs_start;
+ current_implicit_refs_end = current_implicit_refs_start;
+ while (current_implicit_refs_end < implicit_ref_connections_.length() &&
+ implicit_ref_connections_[current_implicit_refs_end].id ==
+ current_group_id)
+ ++current_implicit_refs_end;
+
+ if (current_implicit_refs_end > current_implicit_refs_start) {
+ // Find a representative object for the implicit references.
+ HeapObject** representative = NULL;
+ for (int j = current_group_start; j < i; ++j) {
+ Object** object = object_group_connections_[j].object;
+ if ((*object)->IsHeapObject()) {
+ representative = reinterpret_cast<HeapObject**>(object);
+ break;
+ }
+ }
+ if (representative) {
+ ImplicitRefGroup* group = new ImplicitRefGroup(
+ representative,
+ current_implicit_refs_end - current_implicit_refs_start);
+ for (int j = current_implicit_refs_start;
+ j < current_implicit_refs_end;
+ ++j) {
+ group->children[j - current_implicit_refs_start] =
+ implicit_ref_connections_[j].object;
+ }
+ implicit_ref_groups_.Add(group);
+ }
+ current_implicit_refs_start = current_implicit_refs_end;
+ }
+
+ // Find a RetainedObjectInfo for the group.
+ RetainedObjectInfo* info = NULL;
+ while (info_index < retainer_infos_.length() &&
+ retainer_infos_[info_index].id < current_group_id) {
+ retainer_infos_[info_index].info->Dispose();
+ ++info_index;
+ }
+ if (info_index < retainer_infos_.length() &&
+ retainer_infos_[info_index].id == current_group_id) {
+ // This object group has an associated ObjectGroupRetainerInfo.
+ info = retainer_infos_[info_index].info;
+ ++info_index;
+ }
+
+ // Ignore groups which only contain one object.
+ if (i > current_group_start + 1) {
+ ObjectGroup* group = new ObjectGroup(i - current_group_start);
+ for (int j = current_group_start; j < i; ++j) {
+ group->objects[j - current_group_start] =
+ object_group_connections_[j].object;
+ }
+ group->info = info;
+ object_groups_.Add(group);
+ } else if (info) {
+ info->Dispose();
+ }
+
+ if (i < object_group_connections_.length()) {
+ current_group_id = object_group_connections_[i].id;
+ current_group_start = i;
+ }
+ }
+ }
+ object_group_connections_.Clear();
+ object_group_connections_.Initialize(kObjectGroupConnectionsCapacity);
+ retainer_infos_.Clear();
+ implicit_ref_connections_.Clear();
+}
+
+
+EternalHandles::EternalHandles() : size_(0) {
+ STATIC_ASSERT(v8::kUninitializedEternalIndex == kInvalidIndex);
+ for (unsigned i = 0; i < ARRAY_SIZE(singleton_handles_); i++) {
+ singleton_handles_[i] = kInvalidIndex;
+ }
+}
+
+
+EternalHandles::~EternalHandles() {
+ for (int i = 0; i < blocks_.length(); i++) delete[] blocks_[i];
+}
+
+
+void EternalHandles::IterateAllRoots(ObjectVisitor* visitor) {
+ int limit = size_;
+ for (int i = 0; i < blocks_.length(); i++) {
+ ASSERT(limit > 0);
+ Object** block = blocks_[i];
+ visitor->VisitPointers(block, block + Min(limit, kSize));
+ limit -= kSize;
+ }
+}
+
+
+void EternalHandles::IterateNewSpaceRoots(ObjectVisitor* visitor) {
+ for (int i = 0; i < new_space_indices_.length(); i++) {
+ visitor->VisitPointer(GetLocation(new_space_indices_[i]));
+ }
+}
+
+
+void EternalHandles::PostGarbageCollectionProcessing(Heap* heap) {
+ int last = 0;
+ for (int i = 0; i < new_space_indices_.length(); i++) {
+ int index = new_space_indices_[i];
+ if (heap->InNewSpace(*GetLocation(index))) {
+ new_space_indices_[last++] = index;
+ }
+ }
+ new_space_indices_.Rewind(last);
+}
+
+
+int EternalHandles::Create(Isolate* isolate, Object* object) {
+ if (object == NULL) return kInvalidIndex;
+ ASSERT_NE(isolate->heap()->the_hole_value(), object);
+ int block = size_ >> kShift;
+ int offset = size_ & kMask;
+ // need to resize
+ if (offset == 0) {
+ Object** next_block = new Object*[kSize];
+ Object* the_hole = isolate->heap()->the_hole_value();
+ MemsetPointer(next_block, the_hole, kSize);
+ blocks_.Add(next_block);
+ }
+ ASSERT_EQ(isolate->heap()->the_hole_value(), blocks_[block][offset]);
+ blocks_[block][offset] = object;
+ if (isolate->heap()->InNewSpace(object)) {
+ new_space_indices_.Add(size_);
+ }
+ return size_++;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/global-handles.h b/chromium/v8/src/global-handles.h
new file mode 100644
index 00000000000..5a4ad13e2f5
--- /dev/null
+++ b/chromium/v8/src/global-handles.h
@@ -0,0 +1,407 @@
+// 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_GLOBAL_HANDLES_H_
+#define V8_GLOBAL_HANDLES_H_
+
+#include "../include/v8.h"
+#include "../include/v8-profiler.h"
+
+#include "handles.h"
+#include "list.h"
+#include "v8utils.h"
+
+namespace v8 {
+namespace internal {
+
+class GCTracer;
+class HeapStats;
+class ObjectVisitor;
+
+// Structure for tracking global handles.
+// A single list keeps all the allocated global handles.
+// Destroyed handles stay in the list but is added to the free list.
+// At GC the destroyed global handles are removed from the free list
+// and deallocated.
+
+// Data structures for tracking object groups and implicit references.
+
+// An object group is treated like a single JS object: if one of object in
+// the group is alive, all objects in the same group are considered alive.
+// An object group is used to simulate object relationship in a DOM tree.
+
+// An implicit references group consists of two parts: a parent object and a
+// list of children objects. If the parent is alive, all the children are alive
+// too.
+
+struct ObjectGroup {
+ explicit ObjectGroup(size_t length)
+ : info(NULL), length(length) {
+ ASSERT(length > 0);
+ objects = new Object**[length];
+ }
+ ~ObjectGroup();
+
+ v8::RetainedObjectInfo* info;
+ Object*** objects;
+ size_t length;
+};
+
+
+struct ImplicitRefGroup {
+ ImplicitRefGroup(HeapObject** parent, size_t length)
+ : parent(parent), length(length) {
+ ASSERT(length > 0);
+ children = new Object**[length];
+ }
+ ~ImplicitRefGroup();
+
+ HeapObject** parent;
+ Object*** children;
+ size_t length;
+};
+
+
+// For internal bookkeeping.
+struct ObjectGroupConnection {
+ ObjectGroupConnection(UniqueId id, Object** object)
+ : id(id), object(object) {}
+
+ bool operator==(const ObjectGroupConnection& other) const {
+ return id == other.id;
+ }
+
+ bool operator<(const ObjectGroupConnection& other) const {
+ return id < other.id;
+ }
+
+ UniqueId id;
+ Object** object;
+};
+
+
+struct ObjectGroupRetainerInfo {
+ ObjectGroupRetainerInfo(UniqueId id, RetainedObjectInfo* info)
+ : id(id), info(info) {}
+
+ bool operator==(const ObjectGroupRetainerInfo& other) const {
+ return id == other.id;
+ }
+
+ bool operator<(const ObjectGroupRetainerInfo& other) const {
+ return id < other.id;
+ }
+
+ UniqueId id;
+ RetainedObjectInfo* info;
+};
+
+
+class GlobalHandles {
+ public:
+ ~GlobalHandles();
+
+ // Creates a new global handle that is alive until Destroy is called.
+ Handle<Object> Create(Object* value);
+
+ // Destroy a global handle.
+ static void Destroy(Object** location);
+
+ typedef WeakReferenceCallbacks<v8::Value, void>::Revivable RevivableCallback;
+
+ // Make the global handle weak and set the callback parameter for the
+ // handle. When the garbage collector recognizes that only weak global
+ // handles point to an object the handles are cleared and the callback
+ // function is invoked (for each handle) with the handle and corresponding
+ // parameter as arguments. Note: cleared means set to Smi::FromInt(0). The
+ // reason is that Smi::FromInt(0) does not change during garage collection.
+ static void MakeWeak(Object** location,
+ void* parameter,
+ RevivableCallback weak_reference_callback);
+
+ void RecordStats(HeapStats* stats);
+
+ // Returns the current number of weak handles.
+ int NumberOfWeakHandles();
+
+ // Returns the current number of weak handles to global objects.
+ // These handles are also included in NumberOfWeakHandles().
+ int NumberOfGlobalObjectWeakHandles();
+
+ // Returns the current number of handles to global objects.
+ int global_handles_count() const {
+ return number_of_global_handles_;
+ }
+
+ // Clear the weakness of a global handle.
+ static void ClearWeakness(Object** location);
+
+ // Clear the weakness of a global handle.
+ static void MarkIndependent(Object** location);
+
+ // Mark the reference to this object externaly unreachable.
+ static void MarkPartiallyDependent(Object** location);
+
+ static bool IsIndependent(Object** location);
+
+ // Tells whether global handle is near death.
+ static bool IsNearDeath(Object** location);
+
+ // Tells whether global handle is weak.
+ static bool IsWeak(Object** location);
+
+ // Process pending weak handles.
+ // Returns true if next major GC is likely to collect more garbage.
+ bool PostGarbageCollectionProcessing(GarbageCollector collector,
+ GCTracer* tracer);
+
+ // Iterates over all strong handles.
+ void IterateStrongRoots(ObjectVisitor* v);
+
+ // Iterates over all handles.
+ void IterateAllRoots(ObjectVisitor* v);
+
+ // Iterates over all handles that have embedder-assigned class ID.
+ void IterateAllRootsWithClassIds(ObjectVisitor* v);
+
+ // Iterates over all handles in the new space that have embedder-assigned
+ // class ID.
+ void IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v);
+
+ // Iterates over all weak roots in heap.
+ void IterateWeakRoots(ObjectVisitor* v);
+
+ // Find all weak handles satisfying the callback predicate, mark
+ // them as pending.
+ void IdentifyWeakHandles(WeakSlotCallback f);
+
+ // NOTE: Three ...NewSpace... functions below are used during
+ // scavenge collections and iterate over sets of handles that are
+ // guaranteed to contain all handles holding new space objects (but
+ // may also include old space objects).
+
+ // Iterates over strong and dependent handles. See the node above.
+ void IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v);
+
+ // Finds weak independent or partially independent handles satisfying
+ // the callback predicate and marks them as pending. See the note above.
+ void IdentifyNewSpaceWeakIndependentHandles(WeakSlotCallbackWithHeap f);
+
+ // Iterates over weak independent or partially independent handles.
+ // See the note above.
+ void IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v);
+
+ // Iterate over objects in object groups that have at least one object
+ // which requires visiting. The callback has to return true if objects
+ // can be skipped and false otherwise.
+ bool IterateObjectGroups(ObjectVisitor* v, WeakSlotCallbackWithHeap can_skip);
+
+ // Add an object group.
+ // Should be only used in GC callback function before a collection.
+ // All groups are destroyed after a garbage collection.
+ void AddObjectGroup(Object*** handles,
+ size_t length,
+ v8::RetainedObjectInfo* info);
+
+ // Associates handle with the object group represented by id.
+ // Should be only used in GC callback function before a collection.
+ // All groups are destroyed after a garbage collection.
+ void SetObjectGroupId(Object** handle, UniqueId id);
+
+ // Set RetainedObjectInfo for an object group. Should not be called more than
+ // once for a group. Should not be called for a group which contains no
+ // handles.
+ void SetRetainedObjectInfo(UniqueId id, RetainedObjectInfo* info);
+
+ // Add an implicit references' group.
+ // Should be only used in GC callback function before a collection.
+ // All groups are destroyed after a mark-compact collection.
+ void AddImplicitReferences(HeapObject** parent,
+ Object*** children,
+ size_t length);
+
+ // Adds an implicit reference from a group to an object. Should be only used
+ // in GC callback function before a collection. All implicit references are
+ // destroyed after a mark-compact collection.
+ void SetReferenceFromGroup(UniqueId id, Object** child);
+
+ // Adds an implicit reference from a parent object to a child object. Should
+ // be only used in GC callback function before a collection. All implicit
+ // references are destroyed after a mark-compact collection.
+ void SetReference(HeapObject** parent, Object** child);
+
+ List<ObjectGroup*>* object_groups() {
+ ComputeObjectGroupsAndImplicitReferences();
+ return &object_groups_;
+ }
+
+ List<ImplicitRefGroup*>* implicit_ref_groups() {
+ ComputeObjectGroupsAndImplicitReferences();
+ return &implicit_ref_groups_;
+ }
+
+ // Remove bags, this should only happen after GC.
+ void RemoveObjectGroups();
+ void RemoveImplicitRefGroups();
+
+ // Tear down the global handle structure.
+ void TearDown();
+
+ Isolate* isolate() { return isolate_; }
+
+#ifdef DEBUG
+ void PrintStats();
+ void Print();
+#endif
+
+ private:
+ explicit GlobalHandles(Isolate* isolate);
+
+ // Migrates data from the internal representation (object_group_connections_,
+ // retainer_infos_ and implicit_ref_connections_) to the public and more
+ // efficient representation (object_groups_ and implicit_ref_groups_).
+ void ComputeObjectGroupsAndImplicitReferences();
+
+ // v8::internal::List is inefficient even for small number of elements, if we
+ // don't assign any initial capacity.
+ static const int kObjectGroupConnectionsCapacity = 20;
+
+ // Internal node structures.
+ class Node;
+ class NodeBlock;
+ class NodeIterator;
+
+ Isolate* isolate_;
+
+ // Field always containing the number of handles to global objects.
+ int number_of_global_handles_;
+
+ // List of all allocated node blocks.
+ NodeBlock* first_block_;
+
+ // List of node blocks with used nodes.
+ NodeBlock* first_used_block_;
+
+ // Free list of nodes.
+ Node* first_free_;
+
+ // Contains all nodes holding new space objects. Note: when the list
+ // is accessed, some of the objects may have been promoted already.
+ List<Node*> new_space_nodes_;
+
+ int post_gc_processing_count_;
+
+ // Object groups and implicit references, public and more efficient
+ // representation.
+ List<ObjectGroup*> object_groups_;
+ List<ImplicitRefGroup*> implicit_ref_groups_;
+
+ // Object groups and implicit references, temporary representation while
+ // constructing the groups.
+ List<ObjectGroupConnection> object_group_connections_;
+ List<ObjectGroupRetainerInfo> retainer_infos_;
+ List<ObjectGroupConnection> implicit_ref_connections_;
+
+ friend class Isolate;
+
+ DISALLOW_COPY_AND_ASSIGN(GlobalHandles);
+};
+
+
+class EternalHandles {
+ public:
+ enum SingletonHandle {
+ I18N_TEMPLATE_ONE,
+ I18N_TEMPLATE_TWO,
+
+ NUMBER_OF_SINGLETON_HANDLES
+ };
+
+ EternalHandles();
+ ~EternalHandles();
+
+ int NumberOfHandles() { return size_; }
+
+ // Create an EternalHandle, returning the index.
+ int Create(Isolate* isolate, Object* object);
+
+ // Grab the handle for an existing EternalHandle.
+ inline Handle<Object> Get(int index) {
+ return Handle<Object>(GetLocation(index));
+ }
+
+ // Grab the handle for an existing SingletonHandle.
+ inline Handle<Object> GetSingleton(SingletonHandle singleton) {
+ ASSERT(Exists(singleton));
+ return Get(singleton_handles_[singleton]);
+ }
+
+ // Checks whether a SingletonHandle has been assigned.
+ inline bool Exists(SingletonHandle singleton) {
+ return singleton_handles_[singleton] != kInvalidIndex;
+ }
+
+ // Assign a SingletonHandle to an empty slot and returns the handle.
+ Handle<Object> CreateSingleton(Isolate* isolate,
+ Object* object,
+ SingletonHandle singleton) {
+ ASSERT(singleton_handles_[singleton] == kInvalidIndex);
+ singleton_handles_[singleton] = Create(isolate, object);
+ return Get(singleton_handles_[singleton]);
+ }
+
+ // Iterates over all handles.
+ void IterateAllRoots(ObjectVisitor* visitor);
+ // Iterates over all handles which might be in new space.
+ void IterateNewSpaceRoots(ObjectVisitor* visitor);
+ // Rebuilds new space list.
+ void PostGarbageCollectionProcessing(Heap* heap);
+
+ private:
+ static const int kInvalidIndex = -1;
+ static const int kShift = 8;
+ static const int kSize = 1 << kShift;
+ static const int kMask = 0xff;
+
+ // Gets the slot for an index
+ inline Object** GetLocation(int index) {
+ ASSERT(index >= 0 && index < size_);
+ return &blocks_[index >> kShift][index & kMask];
+ }
+
+ int size_;
+ List<Object**> blocks_;
+ List<int> new_space_indices_;
+ int singleton_handles_[NUMBER_OF_SINGLETON_HANDLES];
+
+ DISALLOW_COPY_AND_ASSIGN(EternalHandles);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_GLOBAL_HANDLES_H_
diff --git a/chromium/v8/src/globals.h b/chromium/v8/src/globals.h
new file mode 100644
index 00000000000..26fd53114c6
--- /dev/null
+++ b/chromium/v8/src/globals.h
@@ -0,0 +1,452 @@
+// Copyright 2012 the V8 project authors. 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_GLOBALS_H_
+#define V8_GLOBALS_H_
+
+// Define V8_INFINITY
+#define V8_INFINITY INFINITY
+
+// GCC specific stuff
+#ifdef __GNUC__
+
+#define __GNUC_VERSION_FOR_INFTY__ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100)
+
+// Unfortunately, the INFINITY macro cannot be used with the '-pedantic'
+// warning flag and certain versions of GCC due to a bug:
+// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11931
+// For now, we use the more involved template-based version from <limits>, but
+// only when compiling with GCC versions affected by the bug (2.96.x - 4.0.x)
+// __GNUC_PREREQ is not defined in GCC for Mac OS X, so we define our own macro
+#if __GNUC_VERSION_FOR_INFTY__ >= 29600 && __GNUC_VERSION_FOR_INFTY__ < 40100
+#include <limits>
+#undef V8_INFINITY
+#define V8_INFINITY std::numeric_limits<double>::infinity()
+#endif
+#undef __GNUC_VERSION_FOR_INFTY__
+
+#endif // __GNUC__
+
+#ifdef _MSC_VER
+#undef V8_INFINITY
+#define V8_INFINITY HUGE_VAL
+#endif
+
+
+#include "../include/v8stdint.h"
+
+namespace v8 {
+namespace internal {
+
+// Processor architecture detection. For more info on what's defined, see:
+// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+// http://www.agner.org/optimize/calling_conventions.pdf
+// or with gcc, run: "echo | gcc -E -dM -"
+#if defined(_M_X64) || defined(__x86_64__)
+#if defined(__native_client__)
+// For Native Client builds of V8, use V8_TARGET_ARCH_ARM, so that V8
+// generates ARM machine code, together with a portable ARM simulator
+// compiled for the host architecture in question.
+//
+// Since Native Client is ILP-32 on all architectures we use
+// V8_HOST_ARCH_IA32 on both 32- and 64-bit x86.
+#define V8_HOST_ARCH_IA32 1
+#define V8_HOST_ARCH_32_BIT 1
+#define V8_HOST_CAN_READ_UNALIGNED 1
+#else
+#define V8_HOST_ARCH_X64 1
+#define V8_HOST_ARCH_64_BIT 1
+#define V8_HOST_CAN_READ_UNALIGNED 1
+#endif // __native_client__
+#elif defined(_M_IX86) || defined(__i386__)
+#define V8_HOST_ARCH_IA32 1
+#define V8_HOST_ARCH_32_BIT 1
+#define V8_HOST_CAN_READ_UNALIGNED 1
+#elif defined(__ARMEL__)
+#define V8_HOST_ARCH_ARM 1
+#define V8_HOST_ARCH_32_BIT 1
+#elif defined(__MIPSEL__)
+#define V8_HOST_ARCH_MIPS 1
+#define V8_HOST_ARCH_32_BIT 1
+#else
+#error Host architecture was not detected as supported by v8
+#endif
+
+#if defined(__ARM_ARCH_7A__) || \
+ defined(__ARM_ARCH_7R__) || \
+ defined(__ARM_ARCH_7__)
+# define CAN_USE_ARMV7_INSTRUCTIONS 1
+# ifndef CAN_USE_VFP3_INSTRUCTIONS
+# define CAN_USE_VFP3_INSTRUCTIONS
+# endif
+#endif
+
+
+// Target architecture detection. This may be set externally. If not, detect
+// in the same way as the host architecture, that is, target the native
+// environment as presented by the compiler.
+#if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && \
+ !V8_TARGET_ARCH_ARM && !V8_TARGET_ARCH_MIPS
+#if defined(_M_X64) || defined(__x86_64__)
+#define V8_TARGET_ARCH_X64 1
+#elif defined(_M_IX86) || defined(__i386__)
+#define V8_TARGET_ARCH_IA32 1
+#elif defined(__ARMEL__)
+#define V8_TARGET_ARCH_ARM 1
+#elif defined(__MIPSEL__)
+#define V8_TARGET_ARCH_MIPS 1
+#else
+#error Target architecture was not detected as supported by v8
+#endif
+#endif
+
+// Check for supported combinations of host and target architectures.
+#if V8_TARGET_ARCH_IA32 && !V8_HOST_ARCH_IA32
+#error Target architecture ia32 is only supported on ia32 host
+#endif
+#if V8_TARGET_ARCH_X64 && !V8_HOST_ARCH_X64
+#error Target architecture x64 is only supported on x64 host
+#endif
+#if (V8_TARGET_ARCH_ARM && !(V8_HOST_ARCH_IA32 || V8_HOST_ARCH_ARM))
+#error Target architecture arm is only supported on arm and ia32 host
+#endif
+#if (V8_TARGET_ARCH_MIPS && !(V8_HOST_ARCH_IA32 || V8_HOST_ARCH_MIPS))
+#error Target architecture mips is only supported on mips and ia32 host
+#endif
+
+// Determine whether we are running in a simulated environment.
+// Setting USE_SIMULATOR explicitly from the build script will force
+// the use of a simulated environment.
+#if !defined(USE_SIMULATOR)
+#if (V8_TARGET_ARCH_ARM && !V8_HOST_ARCH_ARM)
+#define USE_SIMULATOR 1
+#endif
+#if (V8_TARGET_ARCH_MIPS && !V8_HOST_ARCH_MIPS)
+#define USE_SIMULATOR 1
+#endif
+#endif
+
+// Determine architecture endiannes (we only support little-endian).
+#if V8_TARGET_ARCH_IA32
+#define V8_TARGET_LITTLE_ENDIAN 1
+#elif V8_TARGET_ARCH_X64
+#define V8_TARGET_LITTLE_ENDIAN 1
+#elif V8_TARGET_ARCH_ARM
+#define V8_TARGET_LITTLE_ENDIAN 1
+#elif V8_TARGET_ARCH_MIPS
+#define V8_TARGET_LITTLE_ENDIAN 1
+#else
+#error Unknown target architecture endiannes
+#endif
+
+// Support for alternative bool type. This is only enabled if the code is
+// compiled with USE_MYBOOL defined. This catches some nasty type bugs.
+// For instance, 'bool b = "false";' results in b == true! This is a hidden
+// source of bugs.
+// However, redefining the bool type does have some negative impact on some
+// platforms. It gives rise to compiler warnings (i.e. with
+// MSVC) in the API header files when mixing code that uses the standard
+// bool with code that uses the redefined version.
+// This does not actually belong in the platform code, but needs to be
+// defined here because the platform code uses bool, and platform.h is
+// include very early in the main include file.
+
+#ifdef USE_MYBOOL
+typedef unsigned int __my_bool__;
+#define bool __my_bool__ // use 'indirection' to avoid name clashes
+#endif
+
+typedef uint8_t byte;
+typedef byte* Address;
+
+// Define our own macros for writing 64-bit constants. This is less fragile
+// than defining __STDC_CONSTANT_MACROS before including <stdint.h>, and it
+// works on compilers that don't have it (like MSVC).
+#if V8_HOST_ARCH_64_BIT
+#if defined(_MSC_VER)
+#define V8_UINT64_C(x) (x ## UI64)
+#define V8_INT64_C(x) (x ## I64)
+#define V8_INTPTR_C(x) (x ## I64)
+#define V8_PTR_PREFIX "ll"
+#elif defined(__MINGW64__)
+#define V8_UINT64_C(x) (x ## ULL)
+#define V8_INT64_C(x) (x ## LL)
+#define V8_INTPTR_C(x) (x ## LL)
+#define V8_PTR_PREFIX "I64"
+#else
+#define V8_UINT64_C(x) (x ## UL)
+#define V8_INT64_C(x) (x ## L)
+#define V8_INTPTR_C(x) (x ## L)
+#define V8_PTR_PREFIX "l"
+#endif
+#else // V8_HOST_ARCH_64_BIT
+#define V8_INTPTR_C(x) (x)
+#define V8_PTR_PREFIX ""
+#endif // V8_HOST_ARCH_64_BIT
+
+// The following macro works on both 32 and 64-bit platforms.
+// Usage: instead of writing 0x1234567890123456
+// write V8_2PART_UINT64_C(0x12345678,90123456);
+#define V8_2PART_UINT64_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
+
+#define V8PRIxPTR V8_PTR_PREFIX "x"
+#define V8PRIdPTR V8_PTR_PREFIX "d"
+#define V8PRIuPTR V8_PTR_PREFIX "u"
+
+// Fix for Mac OS X defining uintptr_t as "unsigned long":
+#if defined(__APPLE__) && defined(__MACH__)
+#undef V8PRIxPTR
+#define V8PRIxPTR "lx"
+#endif
+
+#if (defined(__APPLE__) && defined(__MACH__)) || \
+ defined(__FreeBSD__) || defined(__OpenBSD__)
+#define USING_BSD_ABI
+#endif
+
+// -----------------------------------------------------------------------------
+// Constants
+
+const int KB = 1024;
+const int MB = KB * KB;
+const int GB = KB * KB * KB;
+const int kMaxInt = 0x7FFFFFFF;
+const int kMinInt = -kMaxInt - 1;
+
+const uint32_t kMaxUInt32 = 0xFFFFFFFFu;
+
+const int kCharSize = sizeof(char); // NOLINT
+const int kShortSize = sizeof(short); // NOLINT
+const int kIntSize = sizeof(int); // NOLINT
+const int kDoubleSize = sizeof(double); // NOLINT
+const int kIntptrSize = sizeof(intptr_t); // NOLINT
+const int kPointerSize = sizeof(void*); // NOLINT
+const int kRegisterSize = kPointerSize;
+const int kPCOnStackSize = kRegisterSize;
+const int kFPOnStackSize = kRegisterSize;
+
+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);
+const uintptr_t kUintptrAllBitsSet = V8_UINT64_C(0xFFFFFFFFFFFFFFFF);
+#else
+const int kPointerSizeLog2 = 2;
+const intptr_t kIntptrSignBit = 0x80000000;
+const uintptr_t kUintptrAllBitsSet = 0xFFFFFFFFu;
+#endif
+
+const int kBitsPerByte = 8;
+const int kBitsPerByteLog2 = 3;
+const int kBitsPerPointer = kPointerSize * kBitsPerByte;
+const int kBitsPerInt = kIntSize * kBitsPerByte;
+
+// IEEE 754 single precision floating point number bit layout.
+const uint32_t kBinary32SignMask = 0x80000000u;
+const uint32_t kBinary32ExponentMask = 0x7f800000u;
+const uint32_t kBinary32MantissaMask = 0x007fffffu;
+const int kBinary32ExponentBias = 127;
+const int kBinary32MaxExponent = 0xFE;
+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;
+
+// Latin1/UTF-16 constants
+// Code-point values in Unicode 4.0 are 21 bits wide.
+// Code units in UTF-16 are 16 bits wide.
+typedef uint16_t uc16;
+typedef int32_t uc32;
+const int kOneByteSize = kCharSize;
+const int kUC16Size = sizeof(uc16); // NOLINT
+
+
+// The expression OFFSET_OF(type, field) computes the byte-offset
+// of the specified field relative to the containing type. This
+// corresponds to 'offsetof' (in stddef.h), except that it doesn't
+// use 0 or NULL, which causes a problem with the compiler warnings
+// we have enabled (which is also why 'offsetof' doesn't seem to work).
+// Here we simply use the non-zero value 4, which seems to work.
+#define OFFSET_OF(type, field) \
+ (reinterpret_cast<intptr_t>(&(reinterpret_cast<type*>(4)->field)) - 4)
+
+
+// The expression ARRAY_SIZE(a) is a compile-time constant of type
+// size_t which represents the number of elements of the given
+// array. You should only use ARRAY_SIZE on statically allocated
+// arrays.
+#define ARRAY_SIZE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+
+// The USE(x) template is used to silence C++ compiler warnings
+// issued for (yet) unused variables (typically parameters).
+template <typename T>
+inline void USE(T) { }
+
+
+// FUNCTION_ADDR(f) gets the address of a C function f.
+#define FUNCTION_ADDR(f) \
+ (reinterpret_cast<v8::internal::Address>(reinterpret_cast<intptr_t>(f)))
+
+
+// FUNCTION_CAST<F>(addr) casts an address into a function
+// of type F. Used to invoke generated code from within C.
+template <typename F>
+F FUNCTION_CAST(Address addr) {
+ return reinterpret_cast<F>(reinterpret_cast<intptr_t>(addr));
+}
+
+
+#if __cplusplus >= 201103L
+#define DISALLOW_BY_DELETE = delete
+#else
+#define DISALLOW_BY_DELETE
+#endif
+
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) DISALLOW_BY_DELETE; \
+ void operator=(const TypeName&) DISALLOW_BY_DELETE
+
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName() DISALLOW_BY_DELETE; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+
+// Define used for helping GCC to make better inlining. Don't bother for debug
+// builds. On GCC 3.4.5 using __attribute__((always_inline)) causes compilation
+// errors in debug build.
+#if defined(__GNUC__) && !defined(DEBUG)
+#if (__GNUC__ >= 4)
+#define INLINE(header) inline header __attribute__((always_inline))
+#define NO_INLINE(header) header __attribute__((noinline))
+#else
+#define INLINE(header) inline __attribute__((always_inline)) header
+#define NO_INLINE(header) __attribute__((noinline)) header
+#endif
+#elif defined(_MSC_VER) && !defined(DEBUG)
+#define INLINE(header) __forceinline header
+#define NO_INLINE(header) header
+#else
+#define INLINE(header) inline header
+#define NO_INLINE(header) header
+#endif
+
+
+#if defined(__GNUC__) && __GNUC__ >= 4
+#define MUST_USE_RESULT __attribute__ ((warn_unused_result))
+#else
+#define MUST_USE_RESULT
+#endif
+
+
+// Define DISABLE_ASAN macros.
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+#define DISABLE_ASAN __attribute__((no_address_safety_analysis))
+#endif
+#endif
+
+
+#ifndef DISABLE_ASAN
+#define DISABLE_ASAN
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Forward declarations for frequently used classes
+// (sorted alphabetically)
+
+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
+};
+
+
+// A simple Maybe type, that can be passed by value.
+template<class T>
+struct Maybe {
+ Maybe() : has_value(false) {}
+ explicit Maybe(T t) : has_value(true), value(t) {}
+ Maybe(bool has, T t) : has_value(has), value(t) {}
+
+ bool has_value;
+ T value;
+};
+
+
+// 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/chromium/v8/src/handles-inl.h b/chromium/v8/src/handles-inl.h
new file mode 100644
index 00000000000..4f4490b75bf
--- /dev/null
+++ b/chromium/v8/src/handles-inl.h
@@ -0,0 +1,212 @@
+// 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_HANDLES_INL_H_
+#define V8_HANDLES_INL_H_
+
+#include "api.h"
+#include "apiutils.h"
+#include "handles.h"
+#include "isolate.h"
+
+namespace v8 {
+namespace internal {
+
+template<typename T>
+Handle<T>::Handle(T* obj) {
+ ASSERT(!obj->IsFailure());
+ location_ = HandleScope::CreateHandle(obj->GetIsolate(), obj);
+}
+
+
+template<typename T>
+Handle<T>::Handle(T* obj, Isolate* isolate) {
+ ASSERT(!obj->IsFailure());
+ location_ = HandleScope::CreateHandle(isolate, obj);
+}
+
+
+template <typename T>
+inline bool Handle<T>::is_identical_to(const Handle<T> other) const {
+ ASSERT(location_ == NULL || !(*location_)->IsFailure());
+ if (location_ == other.location_) return true;
+ if (location_ == NULL || other.location_ == NULL) return false;
+ // Dereferencing deferred handles to check object equality is safe.
+ SLOW_ASSERT(IsDereferenceAllowed(NO_DEFERRED_CHECK) &&
+ other.IsDereferenceAllowed(NO_DEFERRED_CHECK));
+ return *location_ == *other.location_;
+}
+
+
+template <typename T>
+inline T* Handle<T>::operator*() const {
+ ASSERT(location_ != NULL && !(*location_)->IsFailure());
+ SLOW_ASSERT(IsDereferenceAllowed(INCLUDE_DEFERRED_CHECK));
+ return *BitCast<T**>(location_);
+}
+
+template <typename T>
+inline T** Handle<T>::location() const {
+ ASSERT(location_ == NULL || !(*location_)->IsFailure());
+ SLOW_ASSERT(location_ == NULL ||
+ IsDereferenceAllowed(INCLUDE_DEFERRED_CHECK));
+ return location_;
+}
+
+#ifdef DEBUG
+template <typename T>
+bool Handle<T>::IsDereferenceAllowed(DereferenceCheckMode mode) const {
+ ASSERT(location_ != NULL);
+ Object* object = *BitCast<T**>(location_);
+ if (object->IsSmi()) return true;
+ HeapObject* heap_object = HeapObject::cast(object);
+ Isolate* isolate = heap_object->GetIsolate();
+ Object** handle = reinterpret_cast<Object**>(location_);
+ Object** roots_array_start = isolate->heap()->roots_array_start();
+ if (roots_array_start <= handle &&
+ handle < roots_array_start + Heap::kStrongRootListLength) {
+ return true;
+ }
+ if (!AllowHandleDereference::IsAllowed()) return false;
+ if (mode == INCLUDE_DEFERRED_CHECK &&
+ !AllowDeferredHandleDereference::IsAllowed()) {
+ // Accessing maps and internalized strings is safe.
+ if (heap_object->IsMap()) return true;
+ if (heap_object->IsInternalizedString()) return true;
+ return !isolate->IsDeferredHandle(handle);
+ }
+ return true;
+}
+#endif
+
+
+
+HandleScope::HandleScope(Isolate* isolate) {
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate->handle_scope_data();
+ isolate_ = isolate;
+ prev_next_ = current->next;
+ prev_limit_ = current->limit;
+ current->level++;
+}
+
+
+HandleScope::~HandleScope() {
+ CloseScope(isolate_, prev_next_, prev_limit_);
+}
+
+
+void HandleScope::CloseScope(Isolate* isolate,
+ Object** prev_next,
+ Object** prev_limit) {
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate->handle_scope_data();
+
+ current->next = prev_next;
+ current->level--;
+ if (current->limit != prev_limit) {
+ current->limit = prev_limit;
+ DeleteExtensions(isolate);
+ }
+
+#ifdef ENABLE_EXTRA_CHECKS
+ ZapRange(prev_next, prev_limit);
+#endif
+}
+
+
+template <typename T>
+Handle<T> HandleScope::CloseAndEscape(Handle<T> handle_value) {
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate_->handle_scope_data();
+
+ T* value = *handle_value;
+ // Throw away all handles in the current scope.
+ CloseScope(isolate_, prev_next_, prev_limit_);
+ // Allocate one handle in the parent scope.
+ ASSERT(current->level > 0);
+ Handle<T> result(CreateHandle<T>(isolate_, value));
+ // Reinitialize the current scope (so that it's ready
+ // to be used or closed again).
+ prev_next_ = current->next;
+ prev_limit_ = current->limit;
+ current->level++;
+ return result;
+}
+
+
+template <typename T>
+T** HandleScope::CreateHandle(Isolate* isolate, T* value) {
+ ASSERT(AllowHandleAllocation::IsAllowed());
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate->handle_scope_data();
+
+ internal::Object** cur = current->next;
+ if (cur == current->limit) cur = Extend(isolate);
+ // Update the current next field, set the value in the created
+ // handle, and return the result.
+ ASSERT(cur < current->limit);
+ current->next = cur + 1;
+
+ T** result = reinterpret_cast<T**>(cur);
+ *result = value;
+ return result;
+}
+
+
+#ifdef DEBUG
+inline SealHandleScope::SealHandleScope(Isolate* isolate) : isolate_(isolate) {
+ // Make sure the current thread is allowed to create handles to begin with.
+ CHECK(AllowHandleAllocation::IsAllowed());
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate_->handle_scope_data();
+ // Shrink the current handle scope to make it impossible to do
+ // handle allocations without an explicit handle scope.
+ limit_ = current->limit;
+ current->limit = current->next;
+ level_ = current->level;
+ current->level = 0;
+}
+
+
+inline SealHandleScope::~SealHandleScope() {
+ // Restore state in current handle scope to re-enable handle
+ // allocations.
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate_->handle_scope_data();
+ ASSERT_EQ(0, current->level);
+ current->level = level_;
+ ASSERT_EQ(current->next, current->limit);
+ current->limit = limit_;
+}
+
+#endif
+
+} } // namespace v8::internal
+
+#endif // V8_HANDLES_INL_H_
diff --git a/chromium/v8/src/handles.cc b/chromium/v8/src/handles.cc
new file mode 100644
index 00000000000..48114d91a71
--- /dev/null
+++ b/chromium/v8/src/handles.cc
@@ -0,0 +1,918 @@
+// Copyright 2012 the V8 project authors. 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 "accessors.h"
+#include "api.h"
+#include "arguments.h"
+#include "bootstrapper.h"
+#include "compiler.h"
+#include "debug.h"
+#include "execution.h"
+#include "global-handles.h"
+#include "natives.h"
+#include "runtime.h"
+#include "string-search.h"
+#include "stub-cache.h"
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+int HandleScope::NumberOfHandles(Isolate* isolate) {
+ HandleScopeImplementer* impl = isolate->handle_scope_implementer();
+ int n = impl->blocks()->length();
+ if (n == 0) return 0;
+ return ((n - 1) * kHandleBlockSize) + static_cast<int>(
+ (isolate->handle_scope_data()->next - impl->blocks()->last()));
+}
+
+
+Object** HandleScope::Extend(Isolate* isolate) {
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate->handle_scope_data();
+
+ Object** result = current->next;
+
+ ASSERT(result == current->limit);
+ // Make sure there's at least one scope on the stack and that the
+ // top of the scope stack isn't a barrier.
+ if (current->level == 0) {
+ Utils::ReportApiFailure("v8::HandleScope::CreateHandle()",
+ "Cannot create a handle without a HandleScope");
+ return NULL;
+ }
+ HandleScopeImplementer* impl = isolate->handle_scope_implementer();
+ // If there's more room in the last block, we use that. This is used
+ // for fast creation of scopes after scope barriers.
+ if (!impl->blocks()->is_empty()) {
+ Object** limit = &impl->blocks()->last()[kHandleBlockSize];
+ if (current->limit != limit) {
+ current->limit = limit;
+ ASSERT(limit - current->next < kHandleBlockSize);
+ }
+ }
+
+ // If we still haven't found a slot for the handle, we extend the
+ // current handle scope by allocating a new handle block.
+ if (result == current->limit) {
+ // If there's a spare block, use it for growing the current scope.
+ result = impl->GetSpareOrNewBlock();
+ // Add the extension to the global list of blocks, but count the
+ // extension as part of the current scope.
+ impl->blocks()->Add(result);
+ current->limit = &result[kHandleBlockSize];
+ }
+
+ return result;
+}
+
+
+void HandleScope::DeleteExtensions(Isolate* isolate) {
+ v8::ImplementationUtilities::HandleScopeData* current =
+ isolate->handle_scope_data();
+ isolate->handle_scope_implementer()->DeleteExtensions(current->limit);
+}
+
+
+#ifdef ENABLE_EXTRA_CHECKS
+void HandleScope::ZapRange(Object** start, Object** end) {
+ ASSERT(end - start <= kHandleBlockSize);
+ for (Object** p = start; p != end; p++) {
+ *reinterpret_cast<Address*>(p) = v8::internal::kHandleZapValue;
+ }
+}
+#endif
+
+
+Address HandleScope::current_level_address(Isolate* isolate) {
+ return reinterpret_cast<Address>(&isolate->handle_scope_data()->level);
+}
+
+
+Address HandleScope::current_next_address(Isolate* isolate) {
+ return reinterpret_cast<Address>(&isolate->handle_scope_data()->next);
+}
+
+
+Address HandleScope::current_limit_address(Isolate* isolate) {
+ return reinterpret_cast<Address>(&isolate->handle_scope_data()->limit);
+}
+
+
+Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray> content,
+ Handle<JSArray> array) {
+ CALL_HEAP_FUNCTION(content->GetIsolate(),
+ content->AddKeysFromJSArray(*array), FixedArray);
+}
+
+
+Handle<FixedArray> UnionOfKeys(Handle<FixedArray> first,
+ Handle<FixedArray> second) {
+ CALL_HEAP_FUNCTION(first->GetIsolate(),
+ first->UnionOfKeys(*second), FixedArray);
+}
+
+
+Handle<JSGlobalProxy> ReinitializeJSGlobalProxy(
+ Handle<JSFunction> constructor,
+ Handle<JSGlobalProxy> global) {
+ CALL_HEAP_FUNCTION(
+ constructor->GetIsolate(),
+ constructor->GetHeap()->ReinitializeJSGlobalProxy(*constructor, *global),
+ JSGlobalProxy);
+}
+
+
+void SetExpectedNofProperties(Handle<JSFunction> func, int nof) {
+ // If objects constructed from this function exist then changing
+ // 'estimated_nof_properties' is dangerous since the previous value might
+ // have been compiled into the fast construct stub. More over, the inobject
+ // slack tracking logic might have adjusted the previous value, so even
+ // passing the same value is risky.
+ if (func->shared()->live_objects_may_exist()) return;
+
+ func->shared()->set_expected_nof_properties(nof);
+ if (func->has_initial_map()) {
+ Handle<Map> new_initial_map =
+ func->GetIsolate()->factory()->CopyMap(
+ Handle<Map>(func->initial_map()));
+ new_initial_map->set_unused_property_fields(nof);
+ func->set_initial_map(*new_initial_map);
+ }
+}
+
+
+static int ExpectedNofPropertiesFromEstimate(int estimate) {
+ // If no properties are added in the constructor, they are more likely
+ // to be added later.
+ if (estimate == 0) estimate = 2;
+
+ // We do not shrink objects that go into a snapshot (yet), so we adjust
+ // the estimate conservatively.
+ if (Serializer::enabled()) return estimate + 2;
+
+ // Inobject slack tracking will reclaim redundant inobject space later,
+ // so we can afford to adjust the estimate generously.
+ if (FLAG_clever_optimizations) {
+ return estimate + 8;
+ } else {
+ return estimate + 3;
+ }
+}
+
+
+void SetExpectedNofPropertiesFromEstimate(Handle<SharedFunctionInfo> shared,
+ int estimate) {
+ // See the comment in SetExpectedNofProperties.
+ if (shared->live_objects_may_exist()) return;
+
+ shared->set_expected_nof_properties(
+ ExpectedNofPropertiesFromEstimate(estimate));
+}
+
+
+void FlattenString(Handle<String> string) {
+ CALL_HEAP_FUNCTION_VOID(string->GetIsolate(), string->TryFlatten());
+}
+
+
+Handle<String> FlattenGetString(Handle<String> string) {
+ CALL_HEAP_FUNCTION(string->GetIsolate(), string->TryFlatten(), String);
+}
+
+
+Handle<Object> SetPrototype(Handle<JSFunction> function,
+ Handle<Object> prototype) {
+ ASSERT(function->should_have_prototype());
+ CALL_HEAP_FUNCTION(function->GetIsolate(),
+ Accessors::FunctionSetPrototype(*function,
+ *prototype,
+ NULL),
+ Object);
+}
+
+
+Handle<Object> SetProperty(Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode) {
+ CALL_HEAP_FUNCTION(
+ isolate,
+ Runtime::SetObjectProperty(
+ isolate, object, key, value, attributes, strict_mode),
+ Object);
+}
+
+
+Handle<Object> ForceSetProperty(Handle<JSObject> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attributes) {
+ Isolate* isolate = object->GetIsolate();
+ CALL_HEAP_FUNCTION(
+ isolate,
+ Runtime::ForceSetObjectProperty(
+ isolate, object, key, value, attributes),
+ Object);
+}
+
+
+Handle<Object> DeleteProperty(Handle<JSObject> object, Handle<Object> key) {
+ Isolate* isolate = object->GetIsolate();
+ CALL_HEAP_FUNCTION(isolate,
+ Runtime::DeleteObjectProperty(
+ isolate, object, key, JSReceiver::NORMAL_DELETION),
+ Object);
+}
+
+
+Handle<Object> ForceDeleteProperty(Handle<JSObject> object,
+ Handle<Object> key) {
+ Isolate* isolate = object->GetIsolate();
+ CALL_HEAP_FUNCTION(isolate,
+ Runtime::DeleteObjectProperty(
+ isolate, object, key, JSReceiver::FORCE_DELETION),
+ Object);
+}
+
+
+Handle<Object> HasProperty(Handle<JSReceiver> obj, Handle<Object> key) {
+ Isolate* isolate = obj->GetIsolate();
+ CALL_HEAP_FUNCTION(isolate,
+ Runtime::HasObjectProperty(isolate, obj, key), Object);
+}
+
+
+Handle<Object> GetProperty(Handle<JSReceiver> obj,
+ const char* name) {
+ Isolate* isolate = obj->GetIsolate();
+ Handle<String> str = isolate->factory()->InternalizeUtf8String(name);
+ CALL_HEAP_FUNCTION(isolate, obj->GetProperty(*str), Object);
+}
+
+
+Handle<Object> GetProperty(Isolate* isolate,
+ Handle<Object> obj,
+ Handle<Object> key) {
+ CALL_HEAP_FUNCTION(isolate,
+ Runtime::GetObjectProperty(isolate, obj, key), Object);
+}
+
+
+Handle<Object> LookupSingleCharacterStringFromCode(Isolate* isolate,
+ uint32_t index) {
+ CALL_HEAP_FUNCTION(
+ isolate,
+ isolate->heap()->LookupSingleCharacterStringFromCode(index), Object);
+}
+
+
+Handle<String> SubString(Handle<String> str,
+ int start,
+ int end,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(str->GetIsolate(),
+ str->SubString(start, end, pretenure), String);
+}
+
+
+Handle<JSObject> Copy(Handle<JSObject> obj) {
+ Isolate* isolate = obj->GetIsolate();
+ CALL_HEAP_FUNCTION(isolate,
+ isolate->heap()->CopyJSObject(*obj), JSObject);
+}
+
+
+Handle<JSObject> DeepCopy(Handle<JSObject> obj) {
+ Isolate* isolate = obj->GetIsolate();
+ CALL_HEAP_FUNCTION(isolate,
+ obj->DeepCopy(isolate),
+ JSObject);
+}
+
+
+Handle<Object> SetAccessor(Handle<JSObject> obj, Handle<AccessorInfo> info) {
+ CALL_HEAP_FUNCTION(obj->GetIsolate(), obj->DefineAccessor(*info), Object);
+}
+
+
+// Wrappers for scripts are kept alive and cached in weak global
+// handles referred from foreign objects held by the scripts as long as
+// they are used. When they are not used anymore, the garbage
+// collector will call the weak callback on the global handle
+// associated with the wrapper and get rid of both the wrapper and the
+// handle.
+static void ClearWrapperCache(v8::Isolate* v8_isolate,
+ Persistent<v8::Value>* handle,
+ void*) {
+ Handle<Object> cache = Utils::OpenPersistent(handle);
+ JSValue* wrapper = JSValue::cast(*cache);
+ Foreign* foreign = Script::cast(wrapper->value())->wrapper();
+ ASSERT(foreign->foreign_address() ==
+ reinterpret_cast<Address>(cache.location()));
+ foreign->set_foreign_address(0);
+ Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
+ isolate->global_handles()->Destroy(cache.location());
+ isolate->counters()->script_wrappers()->Decrement();
+}
+
+
+Handle<JSValue> GetScriptWrapper(Handle<Script> script) {
+ if (script->wrapper()->foreign_address() != NULL) {
+ // Return the script wrapper directly from the cache.
+ return Handle<JSValue>(
+ reinterpret_cast<JSValue**>(script->wrapper()->foreign_address()));
+ }
+ Isolate* isolate = script->GetIsolate();
+ // Construct a new script wrapper.
+ isolate->counters()->script_wrappers()->Increment();
+ Handle<JSFunction> constructor = isolate->script_function();
+ Handle<JSValue> result =
+ Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
+
+ // The allocation might have triggered a GC, which could have called this
+ // function recursively, and a wrapper has already been created and cached.
+ // In that case, simply return the cached wrapper.
+ if (script->wrapper()->foreign_address() != NULL) {
+ return Handle<JSValue>(
+ reinterpret_cast<JSValue**>(script->wrapper()->foreign_address()));
+ }
+
+ result->set_value(*script);
+
+ // Create a new weak global handle and use it to cache the wrapper
+ // for future use. The cache will automatically be cleared by the
+ // garbage collector when it is not used anymore.
+ Handle<Object> handle = isolate->global_handles()->Create(*result);
+ isolate->global_handles()->MakeWeak(handle.location(),
+ NULL,
+ &ClearWrapperCache);
+ script->wrapper()->set_foreign_address(
+ reinterpret_cast<Address>(handle.location()));
+ return result;
+}
+
+
+// Init line_ends array with code positions of line ends inside script
+// source.
+void InitScriptLineEnds(Handle<Script> script) {
+ if (!script->line_ends()->IsUndefined()) return;
+
+ Isolate* isolate = script->GetIsolate();
+
+ if (!script->source()->IsString()) {
+ ASSERT(script->source()->IsUndefined());
+ Handle<FixedArray> empty = isolate->factory()->NewFixedArray(0);
+ script->set_line_ends(*empty);
+ ASSERT(script->line_ends()->IsFixedArray());
+ return;
+ }
+
+ Handle<String> src(String::cast(script->source()), isolate);
+
+ Handle<FixedArray> array = CalculateLineEnds(src, true);
+
+ if (*array != isolate->heap()->empty_fixed_array()) {
+ array->set_map(isolate->heap()->fixed_cow_array_map());
+ }
+
+ script->set_line_ends(*array);
+ ASSERT(script->line_ends()->IsFixedArray());
+}
+
+
+template <typename SourceChar>
+static void CalculateLineEnds(Isolate* isolate,
+ List<int>* line_ends,
+ Vector<const SourceChar> src,
+ bool with_last_line) {
+ const int src_len = src.length();
+ StringSearch<uint8_t, SourceChar> search(isolate, STATIC_ASCII_VECTOR("\n"));
+
+ // Find and record line ends.
+ int position = 0;
+ while (position != -1 && position < src_len) {
+ position = search.Search(src, position);
+ if (position != -1) {
+ line_ends->Add(position);
+ position++;
+ } else if (with_last_line) {
+ // Even if the last line misses a line end, it is counted.
+ line_ends->Add(src_len);
+ return;
+ }
+ }
+}
+
+
+Handle<FixedArray> CalculateLineEnds(Handle<String> src,
+ bool with_last_line) {
+ src = FlattenGetString(src);
+ // Rough estimate of line count based on a roughly estimated average
+ // length of (unpacked) code.
+ int line_count_estimate = src->length() >> 4;
+ List<int> line_ends(line_count_estimate);
+ Isolate* isolate = src->GetIsolate();
+ {
+ DisallowHeapAllocation no_allocation; // ensure vectors stay valid.
+ // Dispatch on type of strings.
+ String::FlatContent content = src->GetFlatContent();
+ ASSERT(content.IsFlat());
+ if (content.IsAscii()) {
+ CalculateLineEnds(isolate,
+ &line_ends,
+ content.ToOneByteVector(),
+ with_last_line);
+ } else {
+ CalculateLineEnds(isolate,
+ &line_ends,
+ content.ToUC16Vector(),
+ with_last_line);
+ }
+ }
+ int line_count = line_ends.length();
+ Handle<FixedArray> array = isolate->factory()->NewFixedArray(line_count);
+ for (int i = 0; i < line_count; i++) {
+ array->set(i, Smi::FromInt(line_ends[i]));
+ }
+ return array;
+}
+
+
+// Convert code position into line number.
+int GetScriptLineNumber(Handle<Script> script, int code_pos) {
+ InitScriptLineEnds(script);
+ DisallowHeapAllocation no_allocation;
+ FixedArray* line_ends_array = FixedArray::cast(script->line_ends());
+ const int line_ends_len = line_ends_array->length();
+
+ if (!line_ends_len) return -1;
+
+ if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) {
+ return script->line_offset()->value();
+ }
+
+ int left = 0;
+ int right = line_ends_len;
+ while (int half = (right - left) / 2) {
+ if ((Smi::cast(line_ends_array->get(left + half)))->value() > code_pos) {
+ right -= half;
+ } else {
+ left += half;
+ }
+ }
+ 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;
+
+ DisallowHeapAllocation 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) {
+ DisallowHeapAllocation no_allocation;
+ if (!script->line_ends()->IsUndefined()) {
+ return GetScriptLineNumber(script, code_pos);
+ }
+ // Slow mode: we do not have line_ends. We have to iterate through source.
+ if (!script->source()->IsString()) {
+ return -1;
+ }
+ String* source = String::cast(script->source());
+ int line = 0;
+ int len = source->length();
+ for (int pos = 0; pos < len; pos++) {
+ if (pos == code_pos) {
+ break;
+ }
+ if (source->Get(pos) == '\n') {
+ line++;
+ }
+ }
+ return line;
+}
+
+
+// Compute the property keys from the interceptor.
+// TODO(rossberg): support symbols in API, and filter here if needed.
+v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSReceiver> receiver,
+ Handle<JSObject> object) {
+ Isolate* isolate = receiver->GetIsolate();
+ Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor());
+ PropertyCallbackArguments
+ args(isolate, interceptor->data(), *receiver, *object);
+ v8::Handle<v8::Array> result;
+ if (!interceptor->enumerator()->IsUndefined()) {
+ v8::NamedPropertyEnumerator enum_fun =
+ v8::ToCData<v8::NamedPropertyEnumerator>(interceptor->enumerator());
+ LOG(isolate, ApiObjectAccess("interceptor-named-enum", *object));
+ result = args.Call(enum_fun);
+ }
+#if ENABLE_EXTRA_CHECKS
+ CHECK(result.IsEmpty() || v8::Utils::OpenHandle(*result)->IsJSObject());
+#endif
+ return v8::Local<v8::Array>::New(reinterpret_cast<v8::Isolate*>(isolate),
+ result);
+}
+
+
+// Compute the element keys from the interceptor.
+v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSReceiver> receiver,
+ Handle<JSObject> object) {
+ Isolate* isolate = receiver->GetIsolate();
+ Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor());
+ PropertyCallbackArguments
+ args(isolate, interceptor->data(), *receiver, *object);
+ v8::Handle<v8::Array> result;
+ if (!interceptor->enumerator()->IsUndefined()) {
+ v8::IndexedPropertyEnumerator enum_fun =
+ v8::ToCData<v8::IndexedPropertyEnumerator>(interceptor->enumerator());
+ LOG(isolate, ApiObjectAccess("interceptor-indexed-enum", *object));
+ result = args.Call(enum_fun);
+#if ENABLE_EXTRA_CHECKS
+ CHECK(result.IsEmpty() || v8::Utils::OpenHandle(*result)->IsJSObject());
+#endif
+ }
+ return v8::Local<v8::Array>::New(reinterpret_cast<v8::Isolate*>(isolate),
+ result);
+}
+
+
+Handle<Object> GetScriptNameOrSourceURL(Handle<Script> script) {
+ Isolate* isolate = script->GetIsolate();
+ Handle<String> name_or_source_url_key =
+ isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("nameOrSourceURL"));
+ Handle<JSValue> script_wrapper = GetScriptWrapper(script);
+ Handle<Object> property = GetProperty(isolate,
+ script_wrapper,
+ name_or_source_url_key);
+ ASSERT(property->IsJSFunction());
+ Handle<JSFunction> method = Handle<JSFunction>::cast(property);
+ bool caught_exception;
+ Handle<Object> result = Execution::TryCall(method, script_wrapper, 0,
+ NULL, &caught_exception);
+ if (caught_exception) {
+ result = isolate->factory()->undefined_value();
+ }
+ return result;
+}
+
+
+static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
+ int len = array->length();
+ for (int i = 0; i < len; i++) {
+ Object* e = array->get(i);
+ if (!(e->IsString() || e->IsNumber())) return false;
+ }
+ return true;
+}
+
+
+Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSReceiver> object,
+ KeyCollectionType type,
+ bool* threw) {
+ USE(ContainsOnlyValidKeys);
+ Isolate* isolate = object->GetIsolate();
+ Handle<FixedArray> content = isolate->factory()->empty_fixed_array();
+ Handle<JSObject> arguments_boilerplate = Handle<JSObject>(
+ isolate->context()->native_context()->arguments_boilerplate(),
+ isolate);
+ Handle<JSFunction> arguments_function = Handle<JSFunction>(
+ JSFunction::cast(arguments_boilerplate->map()->constructor()),
+ isolate);
+
+ // Only collect keys if access is permitted.
+ for (Handle<Object> p = object;
+ *p != isolate->heap()->null_value();
+ p = Handle<Object>(p->GetPrototype(isolate), 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.
+ if (current->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(*current,
+ isolate->heap()->undefined_value(),
+ v8::ACCESS_KEYS)) {
+ isolate->ReportFailedAccessCheck(*current, v8::ACCESS_KEYS);
+ if (isolate->has_scheduled_exception()) {
+ isolate->PromoteScheduledException();
+ *threw = true;
+ }
+ break;
+ }
+
+ // Compute the element keys.
+ Handle<FixedArray> element_keys =
+ isolate->factory()->NewFixedArray(current->NumberOfEnumElements());
+ current->GetEnumElementKeys(*element_keys);
+ content = UnionOfKeys(content, element_keys);
+ ASSERT(ContainsOnlyValidKeys(content));
+
+ // Add the element keys from the interceptor.
+ if (current->HasIndexedInterceptor()) {
+ v8::Handle<v8::Array> result =
+ GetKeysForIndexedInterceptor(object, current);
+ if (!result.IsEmpty())
+ content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
+ ASSERT(ContainsOnlyValidKeys(content));
+ }
+
+ // We can cache the computed property keys if access checks are
+ // not needed and no interceptors are involved.
+ //
+ // We do not use the cache if the object has elements and
+ // therefore it does not make sense to cache the property names
+ // for arguments objects. Arguments objects will always have
+ // elements.
+ // Wrapped strings have elements, but don't have an elements
+ // array or dictionary. So the fast inline test for whether to
+ // use the cache says yes, so we should not create a cache.
+ bool cache_enum_keys =
+ ((current->map()->constructor() != *arguments_function) &&
+ !current->IsJSValue() &&
+ !current->IsAccessCheckNeeded() &&
+ !current->HasNamedInterceptor() &&
+ !current->HasIndexedInterceptor());
+ // Compute the property keys and cache them if possible.
+ content =
+ UnionOfKeys(content, GetEnumPropertyKeys(current, cache_enum_keys));
+ ASSERT(ContainsOnlyValidKeys(content));
+
+ // Add the property keys from the interceptor.
+ if (current->HasNamedInterceptor()) {
+ v8::Handle<v8::Array> result =
+ GetKeysForNamedInterceptor(object, current);
+ if (!result.IsEmpty())
+ content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
+ ASSERT(ContainsOnlyValidKeys(content));
+ }
+
+ // If we only want local properties we bail out after the first
+ // iteration.
+ if (type == LOCAL_ONLY)
+ break;
+ }
+ return content;
+}
+
+
+Handle<JSArray> GetKeysFor(Handle<JSReceiver> object, bool* threw) {
+ Isolate* isolate = object->GetIsolate();
+ isolate->counters()->for_in()->Increment();
+ Handle<FixedArray> elements =
+ GetKeysInFixedArrayFor(object, INCLUDE_PROTOS, threw);
+ return isolate->factory()->NewJSArrayWithElements(elements);
+}
+
+
+Handle<FixedArray> ReduceFixedArrayTo(Handle<FixedArray> array, int length) {
+ ASSERT(array->length() >= length);
+ if (array->length() == length) return array;
+
+ Handle<FixedArray> new_array =
+ array->GetIsolate()->factory()->NewFixedArray(length);
+ for (int i = 0; i < length; ++i) new_array->set(i, array->get(i));
+ return new_array;
+}
+
+
+Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
+ bool cache_result) {
+ Isolate* isolate = object->GetIsolate();
+ if (object->HasFastProperties()) {
+ if (object->map()->instance_descriptors()->HasEnumCache()) {
+ int own_property_count = object->map()->EnumLength();
+ // If we have an enum cache, but the enum length of the given map is set
+ // to kInvalidEnumCache, this means that the map itself has never used the
+ // present enum cache. The first step to using the cache is to set the
+ // enum length of the map by counting the number of own descriptors that
+ // are not DONT_ENUM or SYMBOLIC.
+ if (own_property_count == Map::kInvalidEnumCache) {
+ own_property_count = object->map()->NumberOfDescribedProperties(
+ OWN_DESCRIPTORS, DONT_SHOW);
+
+ if (cache_result) object->map()->SetEnumLength(own_property_count);
+ }
+
+ DescriptorArray* desc = object->map()->instance_descriptors();
+ Handle<FixedArray> keys(desc->GetEnumCache(), isolate);
+
+ // In case the number of properties required in the enum are actually
+ // present, we can reuse the enum cache. Otherwise, this means that the
+ // enum cache was generated for a previous (smaller) version of the
+ // Descriptor Array. In that case we regenerate the enum cache.
+ if (own_property_count <= keys->length()) {
+ isolate->counters()->enum_cache_hits()->Increment();
+ return ReduceFixedArrayTo(keys, own_property_count);
+ }
+ }
+
+ Handle<Map> map(object->map());
+
+ if (map->instance_descriptors()->IsEmpty()) {
+ isolate->counters()->enum_cache_hits()->Increment();
+ if (cache_result) map->SetEnumLength(0);
+ return isolate->factory()->empty_fixed_array();
+ }
+
+ isolate->counters()->enum_cache_misses()->Increment();
+ int num_enum = map->NumberOfDescribedProperties(ALL_DESCRIPTORS, DONT_SHOW);
+
+ Handle<FixedArray> storage = isolate->factory()->NewFixedArray(num_enum);
+ Handle<FixedArray> indices = isolate->factory()->NewFixedArray(num_enum);
+
+ Handle<DescriptorArray> descs =
+ Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate);
+
+ int real_size = map->NumberOfOwnDescriptors();
+ int enum_size = 0;
+ int index = 0;
+
+ for (int i = 0; i < descs->number_of_descriptors(); i++) {
+ PropertyDetails details = descs->GetDetails(i);
+ Object* key = descs->GetKey(i);
+ if (!(details.IsDontEnum() || key->IsSymbol())) {
+ if (i < real_size) ++enum_size;
+ storage->set(index, key);
+ if (!indices.is_null()) {
+ if (details.type() != FIELD) {
+ indices = Handle<FixedArray>();
+ } else {
+ int field_index = descs->GetFieldIndex(i);
+ if (field_index >= map->inobject_properties()) {
+ field_index = -(field_index - map->inobject_properties() + 1);
+ }
+ indices->set(index, Smi::FromInt(field_index));
+ }
+ }
+ index++;
+ }
+ }
+ ASSERT(index == storage->length());
+
+ Handle<FixedArray> bridge_storage =
+ isolate->factory()->NewFixedArray(
+ DescriptorArray::kEnumCacheBridgeLength);
+ DescriptorArray* desc = object->map()->instance_descriptors();
+ desc->SetEnumCache(*bridge_storage,
+ *storage,
+ indices.is_null() ? Object::cast(Smi::FromInt(0))
+ : Object::cast(*indices));
+ if (cache_result) {
+ object->map()->SetEnumLength(enum_size);
+ }
+
+ return ReduceFixedArrayTo(storage, enum_size);
+ } else {
+ Handle<NameDictionary> dictionary(object->property_dictionary());
+
+ int length = dictionary->NumberOfElements();
+ if (length == 0) {
+ return Handle<FixedArray>(isolate->heap()->empty_fixed_array());
+ }
+
+ // The enumeration array is generated by allocating an array big enough to
+ // hold all properties that have been seen, whether they are are deleted or
+ // not. Subsequently all visible properties are added to the array. If some
+ // properties were not visible, the array is trimmed so it only contains
+ // visible properties. This improves over adding elements and sorting by
+ // index by having linear complexity rather than n*log(n).
+
+ // By comparing the monotonous NextEnumerationIndex to the NumberOfElements,
+ // we can predict the number of holes in the final array. If there will be
+ // more than 50% holes, regenerate the enumeration indices to reduce the
+ // number of holes to a minimum. This avoids allocating a large array if
+ // many properties were added but subsequently deleted.
+ int next_enumeration = dictionary->NextEnumerationIndex();
+ if (!object->IsGlobalObject() && next_enumeration > (length * 3) / 2) {
+ NameDictionary::DoGenerateNewEnumerationIndices(dictionary);
+ next_enumeration = dictionary->NextEnumerationIndex();
+ }
+
+ Handle<FixedArray> storage =
+ isolate->factory()->NewFixedArray(next_enumeration);
+
+ storage = Handle<FixedArray>(dictionary->CopyEnumKeysTo(*storage));
+ ASSERT(storage->length() == object->NumberOfLocalProperties(DONT_SHOW));
+ return storage;
+ }
+}
+
+
+Handle<ObjectHashSet> ObjectHashSetAdd(Handle<ObjectHashSet> table,
+ Handle<Object> key) {
+ CALL_HEAP_FUNCTION(table->GetIsolate(),
+ table->Add(*key),
+ ObjectHashSet);
+}
+
+
+Handle<ObjectHashSet> ObjectHashSetRemove(Handle<ObjectHashSet> table,
+ Handle<Object> key) {
+ CALL_HEAP_FUNCTION(table->GetIsolate(),
+ table->Remove(*key),
+ ObjectHashSet);
+}
+
+
+Handle<ObjectHashTable> PutIntoObjectHashTable(Handle<ObjectHashTable> table,
+ Handle<Object> key,
+ Handle<Object> value) {
+ CALL_HEAP_FUNCTION(table->GetIsolate(),
+ table->Put(*key, *value),
+ ObjectHashTable);
+}
+
+
+DeferredHandleScope::DeferredHandleScope(Isolate* isolate)
+ : impl_(isolate->handle_scope_implementer()) {
+ impl_->BeginDeferredScope();
+ v8::ImplementationUtilities::HandleScopeData* data =
+ impl_->isolate()->handle_scope_data();
+ Object** new_next = impl_->GetSpareOrNewBlock();
+ Object** new_limit = &new_next[kHandleBlockSize];
+ ASSERT(data->limit == &impl_->blocks()->last()[kHandleBlockSize]);
+ impl_->blocks()->Add(new_next);
+
+#ifdef DEBUG
+ prev_level_ = data->level;
+#endif
+ data->level++;
+ prev_limit_ = data->limit;
+ prev_next_ = data->next;
+ data->next = new_next;
+ data->limit = new_limit;
+}
+
+
+DeferredHandleScope::~DeferredHandleScope() {
+ impl_->isolate()->handle_scope_data()->level--;
+ ASSERT(handles_detached_);
+ ASSERT(impl_->isolate()->handle_scope_data()->level == prev_level_);
+}
+
+
+DeferredHandles* DeferredHandleScope::Detach() {
+ DeferredHandles* deferred = impl_->Detach(prev_limit_);
+ v8::ImplementationUtilities::HandleScopeData* data =
+ impl_->isolate()->handle_scope_data();
+ data->next = prev_next_;
+ data->limit = prev_limit_;
+#ifdef DEBUG
+ handles_detached_ = true;
+#endif
+ return deferred;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/handles.h b/chromium/v8/src/handles.h
new file mode 100644
index 00000000000..90db7d12121
--- /dev/null
+++ b/chromium/v8/src/handles.h
@@ -0,0 +1,358 @@
+// 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_HANDLES_H_
+#define V8_HANDLES_H_
+
+#include "allocation.h"
+#include "apiutils.h"
+#include "objects.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// A Handle provides a reference to an object that survives relocation by
+// the garbage collector.
+// Handles are only valid within a HandleScope.
+// When a handle is created for an object a cell is allocated in the heap.
+
+template<typename T>
+class Handle {
+ public:
+ INLINE(explicit Handle(T** location)) { location_ = location; }
+ INLINE(explicit Handle(T* obj));
+ INLINE(Handle(T* obj, Isolate* isolate));
+
+ INLINE(Handle()) : location_(NULL) {}
+
+ // Constructor for handling automatic up casting.
+ // Ex. Handle<JSFunction> can be passed when Handle<Object> is expected.
+ template <class S> Handle(Handle<S> handle) {
+#ifdef DEBUG
+ T* a = NULL;
+ S* b = NULL;
+ a = b; // Fake assignment to enforce type checks.
+ USE(a);
+#endif
+ location_ = reinterpret_cast<T**>(handle.location_);
+ }
+
+ INLINE(T* operator->() const) { return operator*(); }
+
+ // Check if this handle refers to the exact same object as the other handle.
+ INLINE(bool is_identical_to(const Handle<T> other) const);
+
+ // Provides the C++ dereference operator.
+ INLINE(T* operator*() const);
+
+ // Returns the address to where the raw pointer is stored.
+ INLINE(T** location() const);
+
+ template <class S> static Handle<T> cast(Handle<S> that) {
+ T::cast(*reinterpret_cast<T**>(that.location_));
+ return Handle<T>(reinterpret_cast<T**>(that.location_));
+ }
+
+ static Handle<T> null() { return Handle<T>(); }
+ bool is_null() const { return location_ == NULL; }
+
+ // Closes the given scope, but lets this handle escape. See
+ // implementation in api.h.
+ inline Handle<T> EscapeFrom(v8::HandleScope* scope);
+
+#ifdef DEBUG
+ enum DereferenceCheckMode { INCLUDE_DEFERRED_CHECK, NO_DEFERRED_CHECK };
+
+ bool IsDereferenceAllowed(DereferenceCheckMode mode) const;
+#endif // DEBUG
+
+ private:
+ T** location_;
+
+ // Handles of different classes are allowed to access each other's location_.
+ template<class S> friend class Handle;
+};
+
+
+// Convenience wrapper.
+template<class T>
+inline Handle<T> handle(T* t, Isolate* isolate) {
+ return Handle<T>(t, isolate);
+}
+
+
+// Convenience wrapper.
+template<class T>
+inline Handle<T> handle(T* t) {
+ return Handle<T>(t, t->GetIsolate());
+}
+
+
+class DeferredHandles;
+class HandleScopeImplementer;
+
+
+// A stack-allocated class that governs a number of local handles.
+// After a handle scope has been created, all local handles will be
+// allocated within that handle scope until either the handle scope is
+// deleted or another handle scope is created. If there is already a
+// handle scope and a new one is created, all allocations will take
+// place in the new handle scope until it is deleted. After that,
+// new handles will again be allocated in the original handle scope.
+//
+// After the handle scope of a local handle has been deleted the
+// garbage collector will no longer track the object stored in the
+// handle and may deallocate it. The behavior of accessing a handle
+// for which the handle scope has been deleted is undefined.
+class HandleScope {
+ public:
+ explicit inline HandleScope(Isolate* isolate);
+
+ inline ~HandleScope();
+
+ // Counts the number of allocated handles.
+ static int NumberOfHandles(Isolate* isolate);
+
+ // Creates a new handle with the given value.
+ template <typename T>
+ static inline T** CreateHandle(Isolate* isolate, T* value);
+
+ // Deallocates any extensions used by the current scope.
+ static void DeleteExtensions(Isolate* isolate);
+
+ static Address current_next_address(Isolate* isolate);
+ static Address current_limit_address(Isolate* isolate);
+ static Address current_level_address(Isolate* isolate);
+
+ // Closes the HandleScope (invalidating all handles
+ // created in the scope of the HandleScope) and returns
+ // a Handle backed by the parent scope holding the
+ // value of the argument handle.
+ template <typename T>
+ Handle<T> CloseAndEscape(Handle<T> handle_value);
+
+ Isolate* isolate() { return isolate_; }
+
+ private:
+ // Prevent heap allocation or illegal handle scopes.
+ HandleScope(const HandleScope&);
+ void operator=(const HandleScope&);
+ void* operator new(size_t size);
+ void operator delete(void* size_t);
+
+ Isolate* isolate_;
+ Object** prev_next_;
+ Object** prev_limit_;
+
+ // Close the handle scope resetting limits to a previous state.
+ static inline void CloseScope(Isolate* isolate,
+ Object** prev_next,
+ Object** prev_limit);
+
+ // Extend the handle scope making room for more handles.
+ static internal::Object** Extend(Isolate* isolate);
+
+#ifdef ENABLE_EXTRA_CHECKS
+ // Zaps the handles in the half-open interval [start, end).
+ static void ZapRange(Object** start, Object** end);
+#endif
+
+ friend class v8::HandleScope;
+ friend class v8::internal::DeferredHandles;
+ friend class v8::internal::HandleScopeImplementer;
+ friend class v8::internal::Isolate;
+};
+
+
+class DeferredHandles;
+
+
+class DeferredHandleScope {
+ public:
+ explicit DeferredHandleScope(Isolate* isolate);
+ // The DeferredHandles object returned stores the Handles created
+ // since the creation of this DeferredHandleScope. The Handles are
+ // alive as long as the DeferredHandles object is alive.
+ DeferredHandles* Detach();
+ ~DeferredHandleScope();
+
+ private:
+ Object** prev_limit_;
+ Object** prev_next_;
+ HandleScopeImplementer* impl_;
+
+#ifdef DEBUG
+ bool handles_detached_;
+ int prev_level_;
+#endif
+
+ friend class HandleScopeImplementer;
+};
+
+
+// ----------------------------------------------------------------------------
+// Handle operations.
+// They might invoke garbage collection. The result is an handle to
+// an object of expected type, or the handle is an error if running out
+// of space or encountering an internal error.
+
+// Flattens a string.
+void FlattenString(Handle<String> str);
+
+// Flattens a string and returns the underlying external or sequential
+// string.
+Handle<String> FlattenGetString(Handle<String> str);
+
+Handle<Object> SetProperty(Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode);
+
+Handle<Object> ForceSetProperty(Handle<JSObject> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attributes);
+
+Handle<Object> DeleteProperty(Handle<JSObject> object, Handle<Object> key);
+
+Handle<Object> ForceDeleteProperty(Handle<JSObject> object, Handle<Object> key);
+
+Handle<Object> HasProperty(Handle<JSReceiver> obj, Handle<Object> key);
+
+Handle<Object> GetProperty(Handle<JSReceiver> obj, const char* name);
+
+Handle<Object> GetProperty(Isolate* isolate,
+ Handle<Object> obj,
+ Handle<Object> key);
+
+Handle<Object> LookupSingleCharacterStringFromCode(Isolate* isolate,
+ uint32_t index);
+
+Handle<JSObject> Copy(Handle<JSObject> obj);
+
+Handle<JSObject> DeepCopy(Handle<JSObject> obj);
+
+Handle<Object> SetAccessor(Handle<JSObject> obj, Handle<AccessorInfo> info);
+
+Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray>,
+ Handle<JSArray> array);
+
+// Get the JS object corresponding to the given script; create it
+// if none exists.
+Handle<JSValue> GetScriptWrapper(Handle<Script> script);
+
+// Script line number computations. Note that the line number is zero-based.
+void InitScriptLineEnds(Handle<Script> script);
+// For string calculates an array of line end positions. If the string
+// does not end with a new line character, this character may optionally be
+// imagined.
+Handle<FixedArray> CalculateLineEnds(Handle<String> string,
+ bool with_imaginary_last_new_line);
+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);
+Handle<Object> GetScriptNameOrSourceURL(Handle<Script> script);
+
+// Computes the enumerable keys from interceptors. Used for debug mirrors and
+// by GetKeysInFixedArrayFor below.
+v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSReceiver> receiver,
+ Handle<JSObject> object);
+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<JSReceiver> object,
+ KeyCollectionType type,
+ bool* threw);
+Handle<JSArray> GetKeysFor(Handle<JSReceiver> object, bool* threw);
+Handle<FixedArray> ReduceFixedArrayTo(Handle<FixedArray> array, int length);
+Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
+ bool cache_result);
+
+// Computes the union of keys and return the result.
+// Used for implementing "for (n in object) { }"
+Handle<FixedArray> UnionOfKeys(Handle<FixedArray> first,
+ Handle<FixedArray> second);
+
+Handle<String> SubString(Handle<String> str,
+ int start,
+ int end,
+ PretenureFlag pretenure = NOT_TENURED);
+
+// Sets the expected number of properties for the function's instances.
+void SetExpectedNofProperties(Handle<JSFunction> func, int nof);
+
+// Sets the expected number of properties based on estimate from compiler.
+void SetExpectedNofPropertiesFromEstimate(Handle<SharedFunctionInfo> shared,
+ int estimate);
+
+
+Handle<JSGlobalProxy> ReinitializeJSGlobalProxy(
+ Handle<JSFunction> constructor,
+ Handle<JSGlobalProxy> global);
+
+Handle<Object> SetPrototype(Handle<JSFunction> function,
+ Handle<Object> prototype);
+
+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<Object> key,
+ Handle<Object> value);
+
+
+// Seal off the current HandleScope so that new handles can only be created
+// if a new HandleScope is entered.
+class SealHandleScope BASE_EMBEDDED {
+ public:
+#ifndef DEBUG
+ explicit SealHandleScope(Isolate* isolate) {}
+ ~SealHandleScope() {}
+#else
+ explicit inline SealHandleScope(Isolate* isolate);
+ inline ~SealHandleScope();
+ private:
+ Isolate* isolate_;
+ Object** limit_;
+ int level_;
+#endif
+};
+
+} } // namespace v8::internal
+
+#endif // V8_HANDLES_H_
diff --git a/chromium/v8/src/harmony-array.js b/chromium/v8/src/harmony-array.js
new file mode 100644
index 00000000000..e440299ff61
--- /dev/null
+++ b/chromium/v8/src/harmony-array.js
@@ -0,0 +1,124 @@
+// Copyright 2013 the V8 project authors. 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.
+
+'use strict';
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js:
+// var $Array = global.Array;
+
+// -------------------------------------------------------------------
+
+// ES6 draft 07-15-13, section 15.4.3.23
+function ArrayFind(predicate /* thisArg */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.find"]);
+ }
+
+ var array = ToObject(this);
+ var length = ToInteger(array.length);
+
+ if (!IS_SPEC_FUNCTION(predicate)) {
+ throw MakeTypeError('called_non_callable', [predicate]);
+ }
+
+ var thisArg;
+ if (%_ArgumentsLength() > 1) {
+ thisArg = %_Arguments(1);
+ }
+
+ if (IS_NULL_OR_UNDEFINED(thisArg)) {
+ thisArg = %GetDefaultReceiver(predicate) || thisArg;
+ } else if (!IS_SPEC_OBJECT(thisArg) && %IsClassicModeFunction(predicate)) {
+ thisArg = ToObject(thisArg);
+ }
+
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ if (%_CallFunction(thisArg, element, i, array, predicate)) {
+ return element;
+ }
+ }
+ }
+
+ return;
+}
+
+
+// ES6 draft 07-15-13, section 15.4.3.24
+function ArrayFindIndex(predicate /* thisArg */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.findIndex"]);
+ }
+
+ var array = ToObject(this);
+ var length = ToInteger(array.length);
+
+ if (!IS_SPEC_FUNCTION(predicate)) {
+ throw MakeTypeError('called_non_callable', [predicate]);
+ }
+
+ var thisArg;
+ if (%_ArgumentsLength() > 1) {
+ thisArg = %_Arguments(1);
+ }
+
+ if (IS_NULL_OR_UNDEFINED(thisArg)) {
+ thisArg = %GetDefaultReceiver(predicate) || thisArg;
+ } else if (!IS_SPEC_OBJECT(thisArg) && %IsClassicModeFunction(predicate)) {
+ thisArg = ToObject(thisArg);
+ }
+
+ for (var i = 0; i < length; i++) {
+ if (i in array) {
+ var element = array[i];
+ if (%_CallFunction(thisArg, element, i, array, predicate)) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+// -------------------------------------------------------------------
+
+function HarmonyArrayExtendArrayPrototype() {
+ %CheckIsBootstrapping();
+
+ // Set up the non-enumerable functions on the Array prototype object.
+ InstallFunctions($Array.prototype, DONT_ENUM, $Array(
+ "find", ArrayFind,
+ "findIndex", ArrayFindIndex
+ ));
+}
+
+HarmonyArrayExtendArrayPrototype(); \ No newline at end of file
diff --git a/chromium/v8/src/harmony-string.js b/chromium/v8/src/harmony-string.js
new file mode 100644
index 00000000000..a5c6f4e2ecd
--- /dev/null
+++ b/chromium/v8/src/harmony-string.js
@@ -0,0 +1,154 @@
+// Copyright 2013 the V8 project authors. 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.
+
+'use strict';
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js:
+// var $String = global.String;
+// var $Array = global.Array;
+
+// -------------------------------------------------------------------
+
+// ES6 draft 07-15-13, section 15.5.3.21
+function StringRepeat(count) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.repeat"]);
+ }
+
+ var s = TO_STRING_INLINE(this);
+ var n = ToInteger(count);
+ if (n < 0 || !NUMBER_IS_FINITE(n)) {
+ throw MakeRangeError("invalid_count_value", []);
+ }
+
+ var elements = new InternalArray(n);
+ for (var i = 0; i < n; i++) {
+ elements[i] = s;
+ }
+
+ return %StringBuilderConcat(elements, n, "");
+}
+
+
+// ES6 draft 07-15-13, section 15.5.3.22
+function StringStartsWith(searchString /* position */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.startsWith"]);
+ }
+
+ var s = TO_STRING_INLINE(this);
+ var ss = TO_STRING_INLINE(searchString);
+ var pos = 0;
+ if (%_ArgumentsLength() > 1) {
+ pos = %_Arguments(1); // position
+ pos = ToInteger(pos);
+ }
+
+ var s_len = s.length;
+ var start = MathMin(MathMax(pos, 0), s_len);
+ var ss_len = ss.length;
+ if (ss_len + start > s_len) {
+ return false;
+ }
+
+ return %StringIndexOf(s, ss, start) === start;
+}
+
+
+// ES6 draft 07-15-13, section 15.5.3.23
+function StringEndsWith(searchString /* position */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.endsWith"]);
+ }
+
+ var s = TO_STRING_INLINE(this);
+ var ss = TO_STRING_INLINE(searchString);
+ var s_len = s.length;
+ var pos = s_len;
+ if (%_ArgumentsLength() > 1) {
+ var arg = %_Arguments(1); // position
+ if (!IS_UNDEFINED(arg)) {
+ pos = ToInteger(arg);
+ }
+ }
+
+ var end = MathMin(MathMax(pos, 0), s_len);
+ var ss_len = ss.length;
+ var start = end - ss_len;
+ if (start < 0) {
+ return false;
+ }
+
+ return %StringLastIndexOf(s, ss, start) === start;
+}
+
+
+// ES6 draft 07-15-13, section 15.5.3.24
+function StringContains(searchString /* position */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.contains"]);
+ }
+
+ var s = TO_STRING_INLINE(this);
+ var ss = TO_STRING_INLINE(searchString);
+ var pos = 0;
+ if (%_ArgumentsLength() > 1) {
+ pos = %_Arguments(1); // position
+ pos = ToInteger(pos);
+ }
+
+ var s_len = s.length;
+ var start = MathMin(MathMax(pos, 0), s_len);
+ var ss_len = ss.length;
+ if (ss_len + start > s_len) {
+ return false;
+ }
+
+ return %StringIndexOf(s, ss, start) !== -1;
+}
+
+
+// -------------------------------------------------------------------
+
+function ExtendStringPrototype() {
+ %CheckIsBootstrapping();
+
+ // Set up the non-enumerable functions on the String prototype object.
+ InstallFunctions($String.prototype, DONT_ENUM, $Array(
+ "repeat", StringRepeat,
+ "startsWith", StringStartsWith,
+ "endsWith", StringEndsWith,
+ "contains", StringContains
+ ));
+}
+
+ExtendStringPrototype(); \ No newline at end of file
diff --git a/chromium/v8/src/hashmap.h b/chromium/v8/src/hashmap.h
new file mode 100644
index 00000000000..11f6ace7d83
--- /dev/null
+++ b/chromium/v8/src/hashmap.h
@@ -0,0 +1,364 @@
+// Copyright 2012 the V8 project authors. 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_HASHMAP_H_
+#define V8_HASHMAP_H_
+
+#include "allocation.h"
+#include "checks.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+template<class AllocationPolicy>
+class TemplateHashMapImpl {
+ public:
+ typedef bool (*MatchFun) (void* key1, void* key2);
+
+ // The default capacity. This is used by the call sites which want
+ // to pass in a non-default AllocationPolicy but want to use the
+ // default value of capacity specified by the implementation.
+ static const uint32_t kDefaultHashMapCapacity = 8;
+
+ // initial_capacity is the size of the initial hash map;
+ // it must be a power of 2 (and thus must not be 0).
+ TemplateHashMapImpl(MatchFun match,
+ uint32_t capacity = kDefaultHashMapCapacity,
+ AllocationPolicy allocator = AllocationPolicy());
+
+ ~TemplateHashMapImpl();
+
+ // HashMap entries are (key, value, hash) triplets.
+ // Some clients may not need to use the value slot
+ // (e.g. implementers of sets, where the key is the value).
+ struct Entry {
+ void* key;
+ void* value;
+ uint32_t hash; // The full hash value for key
+ int order; // If you never remove entries this is the insertion order.
+ };
+
+ // If an entry with matching key is found, Lookup()
+ // returns that entry. If no matching entry is found,
+ // but insert is set, a new entry is inserted with
+ // corresponding key, key hash, and NULL value.
+ // Otherwise, NULL is returned.
+ Entry* Lookup(void* key, uint32_t hash, bool insert,
+ AllocationPolicy allocator = AllocationPolicy());
+
+ // Removes the entry with matching key.
+ // It returns the value of the deleted entry
+ // or null if there is no value for such key.
+ void* Remove(void* key, uint32_t hash);
+
+ // Empties the hash map (occupancy() == 0).
+ void Clear();
+
+ // The number of (non-empty) entries in the table.
+ uint32_t occupancy() const { return occupancy_; }
+
+ // The capacity of the table. The implementation
+ // makes sure that occupancy is at most 80% of
+ // the table capacity.
+ uint32_t capacity() const { return capacity_; }
+
+ // Iteration
+ //
+ // for (Entry* p = map.Start(); p != NULL; p = map.Next(p)) {
+ // ...
+ // }
+ //
+ // If entries are inserted during iteration, the effect of
+ // calling Next() is undefined.
+ Entry* Start() const;
+ Entry* Next(Entry* p) const;
+
+ private:
+ MatchFun match_;
+ Entry* map_;
+ uint32_t capacity_;
+ uint32_t occupancy_;
+
+ Entry* map_end() const { return map_ + capacity_; }
+ Entry* Probe(void* key, uint32_t hash);
+ void Initialize(uint32_t capacity, AllocationPolicy allocator);
+ void Resize(AllocationPolicy allocator);
+};
+
+typedef TemplateHashMapImpl<FreeStoreAllocationPolicy> HashMap;
+
+template<class AllocationPolicy>
+TemplateHashMapImpl<AllocationPolicy>::TemplateHashMapImpl(
+ MatchFun match, uint32_t initial_capacity, AllocationPolicy allocator) {
+ match_ = match;
+ Initialize(initial_capacity, allocator);
+}
+
+
+template<class AllocationPolicy>
+TemplateHashMapImpl<AllocationPolicy>::~TemplateHashMapImpl() {
+ AllocationPolicy::Delete(map_);
+}
+
+
+template<class AllocationPolicy>
+typename TemplateHashMapImpl<AllocationPolicy>::Entry*
+TemplateHashMapImpl<AllocationPolicy>::Lookup(
+ void* key, uint32_t hash, bool insert, AllocationPolicy allocator) {
+ // Find a matching entry.
+ Entry* p = Probe(key, hash);
+ if (p->key != NULL) {
+ return p;
+ }
+
+ // No entry found; insert one if necessary.
+ if (insert) {
+ p->key = key;
+ p->value = NULL;
+ p->hash = hash;
+ p->order = occupancy_;
+ occupancy_++;
+
+ // Grow the map if we reached >= 80% occupancy.
+ if (occupancy_ + occupancy_/4 >= capacity_) {
+ Resize(allocator);
+ p = Probe(key, hash);
+ }
+
+ return p;
+ }
+
+ // No entry found and none inserted.
+ return NULL;
+}
+
+
+template<class AllocationPolicy>
+void* TemplateHashMapImpl<AllocationPolicy>::Remove(void* key, uint32_t hash) {
+ // Lookup the entry for the key to remove.
+ Entry* p = Probe(key, hash);
+ if (p->key == NULL) {
+ // Key not found nothing to remove.
+ return NULL;
+ }
+
+ void* value = p->value;
+ // To remove an entry we need to ensure that it does not create an empty
+ // entry that will cause the search for another entry to stop too soon. If all
+ // the entries between the entry to remove and the next empty slot have their
+ // initial position inside this interval, clearing the entry to remove will
+ // not break the search. If, while searching for the next empty entry, an
+ // entry is encountered which does not have its initial position between the
+ // entry to remove and the position looked at, then this entry can be moved to
+ // the place of the entry to remove without breaking the search for it. The
+ // entry made vacant by this move is now the entry to remove and the process
+ // starts over.
+ // Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
+
+ // This guarantees loop termination as there is at least one empty entry so
+ // eventually the removed entry will have an empty entry after it.
+ ASSERT(occupancy_ < capacity_);
+
+ // p is the candidate entry to clear. q is used to scan forwards.
+ Entry* q = p; // Start at the entry to remove.
+ while (true) {
+ // Move q to the next entry.
+ q = q + 1;
+ if (q == map_end()) {
+ q = map_;
+ }
+
+ // All entries between p and q have their initial position between p and q
+ // and the entry p can be cleared without breaking the search for these
+ // entries.
+ if (q->key == NULL) {
+ break;
+ }
+
+ // Find the initial position for the entry at position q.
+ Entry* r = map_ + (q->hash & (capacity_ - 1));
+
+ // If the entry at position q has its initial position outside the range
+ // between p and q it can be moved forward to position p and will still be
+ // found. There is now a new candidate entry for clearing.
+ if ((q > p && (r <= p || r > q)) ||
+ (q < p && (r <= p && r > q))) {
+ *p = *q;
+ p = q;
+ }
+ }
+
+ // Clear the entry which is allowed to en emptied.
+ p->key = NULL;
+ occupancy_--;
+ return value;
+}
+
+
+template<class AllocationPolicy>
+void TemplateHashMapImpl<AllocationPolicy>::Clear() {
+ // Mark all entries as empty.
+ const Entry* end = map_end();
+ for (Entry* p = map_; p < end; p++) {
+ p->key = NULL;
+ }
+ occupancy_ = 0;
+}
+
+
+template<class AllocationPolicy>
+typename TemplateHashMapImpl<AllocationPolicy>::Entry*
+ TemplateHashMapImpl<AllocationPolicy>::Start() const {
+ return Next(map_ - 1);
+}
+
+
+template<class AllocationPolicy>
+typename TemplateHashMapImpl<AllocationPolicy>::Entry*
+ TemplateHashMapImpl<AllocationPolicy>::Next(Entry* p) const {
+ const Entry* end = map_end();
+ ASSERT(map_ - 1 <= p && p < end);
+ for (p++; p < end; p++) {
+ if (p->key != NULL) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+
+template<class AllocationPolicy>
+typename TemplateHashMapImpl<AllocationPolicy>::Entry*
+ TemplateHashMapImpl<AllocationPolicy>::Probe(void* key, uint32_t hash) {
+ ASSERT(key != NULL);
+
+ ASSERT(IsPowerOf2(capacity_));
+ Entry* p = map_ + (hash & (capacity_ - 1));
+ const Entry* end = map_end();
+ ASSERT(map_ <= p && p < end);
+
+ ASSERT(occupancy_ < capacity_); // Guarantees loop termination.
+ while (p->key != NULL && (hash != p->hash || !match_(key, p->key))) {
+ p++;
+ if (p >= end) {
+ p = map_;
+ }
+ }
+
+ return p;
+}
+
+
+template<class AllocationPolicy>
+void TemplateHashMapImpl<AllocationPolicy>::Initialize(
+ uint32_t capacity, AllocationPolicy allocator) {
+ ASSERT(IsPowerOf2(capacity));
+ map_ = reinterpret_cast<Entry*>(allocator.New(capacity * sizeof(Entry)));
+ if (map_ == NULL) {
+ v8::internal::FatalProcessOutOfMemory("HashMap::Initialize");
+ return;
+ }
+ capacity_ = capacity;
+ Clear();
+}
+
+
+template<class AllocationPolicy>
+void TemplateHashMapImpl<AllocationPolicy>::Resize(AllocationPolicy allocator) {
+ Entry* map = map_;
+ uint32_t n = occupancy_;
+
+ // Allocate larger map.
+ Initialize(capacity_ * 2, allocator);
+
+ // Rehash all current entries.
+ for (Entry* p = map; n > 0; p++) {
+ if (p->key != NULL) {
+ Entry* entry = Lookup(p->key, p->hash, true, allocator);
+ entry->value = p->value;
+ entry->order = p->order;
+ n--;
+ }
+ }
+
+ // Delete old map.
+ AllocationPolicy::Delete(map);
+}
+
+
+// A hash map for pointer keys and values with an STL-like interface.
+template<class Key, class Value, class AllocationPolicy>
+class TemplateHashMap: private TemplateHashMapImpl<AllocationPolicy> {
+ public:
+ STATIC_ASSERT(sizeof(Key*) == sizeof(void*)); // NOLINT
+ STATIC_ASSERT(sizeof(Value*) == sizeof(void*)); // NOLINT
+ struct value_type {
+ Key* first;
+ Value* second;
+ };
+
+ class Iterator {
+ public:
+ Iterator& operator++() {
+ entry_ = map_->Next(entry_);
+ return *this;
+ }
+
+ value_type* operator->() { return reinterpret_cast<value_type*>(entry_); }
+ bool operator!=(const Iterator& other) { return entry_ != other.entry_; }
+
+ private:
+ Iterator(const TemplateHashMapImpl<AllocationPolicy>* map,
+ typename TemplateHashMapImpl<AllocationPolicy>::Entry* entry) :
+ map_(map), entry_(entry) { }
+
+ const TemplateHashMapImpl<AllocationPolicy>* map_;
+ typename TemplateHashMapImpl<AllocationPolicy>::Entry* entry_;
+
+ friend class TemplateHashMap;
+ };
+
+ TemplateHashMap(
+ typename TemplateHashMapImpl<AllocationPolicy>::MatchFun match,
+ AllocationPolicy allocator = AllocationPolicy())
+ : TemplateHashMapImpl<AllocationPolicy>(
+ match,
+ TemplateHashMapImpl<AllocationPolicy>::kDefaultHashMapCapacity,
+ allocator) { }
+
+ Iterator begin() const { return Iterator(this, this->Start()); }
+ Iterator end() const { return Iterator(this, NULL); }
+ Iterator find(Key* key, bool insert = false,
+ AllocationPolicy allocator = AllocationPolicy()) {
+ return Iterator(this, this->Lookup(key, key->Hash(), insert, allocator));
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_HASHMAP_H_
diff --git a/chromium/v8/src/heap-inl.h b/chromium/v8/src/heap-inl.h
new file mode 100644
index 00000000000..3c1d4d274ba
--- /dev/null
+++ b/chromium/v8/src/heap-inl.h
@@ -0,0 +1,855 @@
+// Copyright 2012 the V8 project authors. 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_HEAP_INL_H_
+#define V8_HEAP_INL_H_
+
+#include "heap.h"
+#include "isolate.h"
+#include "list-inl.h"
+#include "objects.h"
+#include "platform.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()->area_end());
+ 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.
+#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();
+}
+
+
+MaybeObject* Heap::AllocateStringFromUtf8(Vector<const char> str,
+ PretenureFlag pretenure) {
+ // Check for ASCII first since this is the common case.
+ const char* start = str.start();
+ int length = str.length();
+ int non_ascii_start = String::NonAsciiStart(start, length);
+ if (non_ascii_start >= length) {
+ // If the string is ASCII, we do not need to convert the characters
+ // since UTF8 is backwards compatible with ASCII.
+ return AllocateStringFromOneByte(str, pretenure);
+ }
+ // Non-ASCII and we need to decode.
+ return AllocateStringFromUtf8Slow(str, non_ascii_start, pretenure);
+}
+
+
+template<>
+bool inline Heap::IsOneByte(Vector<const char> str, int chars) {
+ // TODO(dcarney): incorporate Latin-1 check when Latin-1 is supported?
+ // ASCII only check.
+ return chars == str.length();
+}
+
+
+template<>
+bool inline Heap::IsOneByte(String* str, int chars) {
+ return str->IsOneByteRepresentation();
+}
+
+
+MaybeObject* Heap::AllocateInternalizedStringFromUtf8(
+ Vector<const char> str, int chars, uint32_t hash_field) {
+ if (IsOneByte(str, chars)) {
+ return AllocateOneByteInternalizedString(
+ Vector<const uint8_t>::cast(str), hash_field);
+ }
+ return AllocateInternalizedStringImpl<false>(str, chars, hash_field);
+}
+
+
+template<typename T>
+MaybeObject* Heap::AllocateInternalizedStringImpl(
+ T t, int chars, uint32_t hash_field) {
+ if (IsOneByte(t, chars)) {
+ return AllocateInternalizedStringImpl<true>(t, chars, hash_field);
+ }
+ return AllocateInternalizedStringImpl<false>(t, chars, hash_field);
+}
+
+
+MaybeObject* Heap::AllocateOneByteInternalizedString(Vector<const uint8_t> str,
+ uint32_t hash_field) {
+ if (str.length() > SeqOneByteString::kMaxLength) {
+ return Failure::OutOfMemoryException(0x2);
+ }
+ // Compute map and object size.
+ Map* map = ascii_internalized_string_map();
+ int size = SeqOneByteString::SizeFor(str.length());
+
+ // Allocate string.
+ Object* result;
+ { MaybeObject* maybe_result = (size > Page::kMaxNonCodeHeapObjectSize)
+ ? lo_space_->AllocateRaw(size, NOT_EXECUTABLE)
+ : old_data_space_->AllocateRaw(size);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // 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());
+ answer->set_hash_field(hash_field);
+
+ ASSERT_EQ(size, answer->Size());
+
+ // Fill in the characters.
+ OS::MemCopy(answer->address() + SeqOneByteString::kHeaderSize,
+ str.start(), str.length());
+
+ return answer;
+}
+
+
+MaybeObject* Heap::AllocateTwoByteInternalizedString(Vector<const uc16> str,
+ uint32_t hash_field) {
+ if (str.length() > SeqTwoByteString::kMaxLength) {
+ return Failure::OutOfMemoryException(0x3);
+ }
+ // Compute map and object size.
+ Map* map = internalized_string_map();
+ int size = SeqTwoByteString::SizeFor(str.length());
+
+ // Allocate string.
+ Object* result;
+ { MaybeObject* maybe_result = (size > Page::kMaxNonCodeHeapObjectSize)
+ ? 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);
+ // Set length and hash fields of the allocated string.
+ String* answer = String::cast(result);
+ answer->set_length(str.length());
+ answer->set_hash_field(hash_field);
+
+ ASSERT_EQ(size, answer->Size());
+
+ // Fill in the characters.
+ OS::MemCopy(answer->address() + SeqTwoByteString::kHeaderSize,
+ str.start(), str.length() * kUC16Size);
+
+ return answer;
+}
+
+MaybeObject* Heap::CopyFixedArray(FixedArray* src) {
+ return CopyFixedArrayWithMap(src, src->map());
+}
+
+
+MaybeObject* Heap::CopyFixedDoubleArray(FixedDoubleArray* src) {
+ return CopyFixedDoubleArrayWithMap(src, src->map());
+}
+
+
+MaybeObject* Heap::AllocateRaw(int size_in_bytes,
+ AllocationSpace space,
+ AllocationSpace retry_space) {
+ ASSERT(AllowHandleAllocation::IsAllowed() && gc_state_ == NOT_IN_GC);
+ ASSERT(space != NEW_SPACE ||
+ retry_space == OLD_POINTER_SPACE ||
+ retry_space == OLD_DATA_SPACE ||
+ retry_space == LO_SPACE);
+#ifdef DEBUG
+ if (FLAG_gc_interval >= 0 &&
+ !disallow_allocation_failure_ &&
+ Heap::allocation_timeout_-- <= 0) {
+ return Failure::RetryAfterGC(space);
+ }
+ isolate_->counters()->objs_since_last_full()->Increment();
+ isolate_->counters()->objs_since_last_young()->Increment();
+#endif
+ MaybeObject* result;
+ if (NEW_SPACE == space) {
+ result = new_space_.AllocateRaw(size_in_bytes);
+ if (always_allocate() && result->IsFailure()) {
+ space = retry_space;
+ } else {
+ return result;
+ }
+ }
+
+ if (OLD_POINTER_SPACE == space) {
+ result = old_pointer_space_->AllocateRaw(size_in_bytes);
+ } else if (OLD_DATA_SPACE == space) {
+ result = old_data_space_->AllocateRaw(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, NOT_EXECUTABLE);
+ } else if (CELL_SPACE == space) {
+ result = cell_space_->AllocateRaw(size_in_bytes);
+ } else if (PROPERTY_CELL_SPACE == space) {
+ result = property_cell_space_->AllocateRaw(size_in_bytes);
+ } else {
+ ASSERT(MAP_SPACE == space);
+ result = map_space_->AllocateRaw(size_in_bytes);
+ }
+ if (result->IsFailure()) old_gen_exhausted_ = true;
+ return result;
+}
+
+
+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), pretenure);
+}
+
+
+MaybeObject* Heap::NumberFromUint32(
+ uint32_t value, PretenureFlag pretenure) {
+ if (static_cast<int32_t>(value) >= 0 &&
+ Smi::IsValid(static_cast<int32_t>(value))) {
+ return Smi::FromInt(static_cast<int32_t>(value));
+ }
+ // Bypass NumberFromDouble to avoid various redundant checks.
+ return AllocateHeapNumber(FastUI2D(value), pretenure);
+}
+
+
+void Heap::FinalizeExternalString(String* string) {
+ ASSERT(string->IsExternalString());
+ v8::String::ExternalStringResourceBase** resource_addr =
+ reinterpret_cast<v8::String::ExternalStringResourceBase**>(
+ reinterpret_cast<byte*>(string) +
+ ExternalString::kResourceOffset -
+ kHeapObjectTag);
+
+ // Dispose of the C++ object if it has not already been disposed.
+ if (*resource_addr != NULL) {
+ (*resource_addr)->Dispose();
+ *resource_addr = NULL;
+ }
+}
+
+
+MaybeObject* Heap::AllocateRawMap() {
+#ifdef DEBUG
+ isolate_->counters()->objs_since_last_full()->Increment();
+ isolate_->counters()->objs_since_last_young()->Increment();
+#endif
+ MaybeObject* result = map_space_->AllocateRaw(Map::kSize);
+ if (result->IsFailure()) old_gen_exhausted_ = true;
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateRawCell() {
+#ifdef DEBUG
+ isolate_->counters()->objs_since_last_full()->Increment();
+ isolate_->counters()->objs_since_last_young()->Increment();
+#endif
+ MaybeObject* result = cell_space_->AllocateRaw(Cell::kSize);
+ if (result->IsFailure()) old_gen_exhausted_ = true;
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateRawPropertyCell() {
+#ifdef DEBUG
+ isolate_->counters()->objs_since_last_full()->Increment();
+ isolate_->counters()->objs_since_last_young()->Increment();
+#endif
+ MaybeObject* result =
+ property_cell_space_->AllocateRaw(PropertyCell::kSize);
+ if (result->IsFailure()) old_gen_exhausted_ = true;
+ return result;
+}
+
+
+bool Heap::InNewSpace(Object* object) {
+ bool result = new_space_.Contains(object);
+ ASSERT(!result || // Either not in new space
+ gc_state_ != NOT_IN_GC || // ... or in the middle of GC
+ InToSpace(object)); // ... or in to-space (where we allocate).
+ return result;
+}
+
+
+bool Heap::InNewSpace(Address address) {
+ return new_space_.Contains(address);
+}
+
+
+bool Heap::InFromSpace(Object* object) {
+ return new_space_.FromSpaceContains(object);
+}
+
+
+bool Heap::InToSpace(Object* object) {
+ return new_space_.ToSpaceContains(object);
+}
+
+
+bool Heap::InOldPointerSpace(Address address) {
+ return old_pointer_space_->Contains(address);
+}
+
+
+bool Heap::InOldPointerSpace(Object* object) {
+ return InOldPointerSpace(reinterpret_cast<Address>(object));
+}
+
+
+bool Heap::InOldDataSpace(Address address) {
+ return old_data_space_->Contains(address);
+}
+
+
+bool Heap::InOldDataSpace(Object* object) {
+ return InOldDataSpace(reinterpret_cast<Address>(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.
+ 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 (!InNewSpace(address)) store_buffer_.Mark(address + offset);
+}
+
+
+void Heap::RecordWrites(Address address, int start, int len) {
+ if (!InNewSpace(address)) {
+ for (int i = 0; i < len; i++) {
+ store_buffer_.Mark(address + start + i * kPointerSize);
+ }
+ }
+}
+
+
+OldSpace* Heap::TargetSpace(HeapObject* object) {
+ InstanceType type = object->map()->instance_type();
+ AllocationSpace space = TargetSpaceId(type);
+ return (space == OLD_POINTER_SPACE)
+ ? old_pointer_space_
+ : old_data_space_;
+}
+
+
+AllocationSpace Heap::TargetSpaceId(InstanceType type) {
+ // Heap numbers and sequential strings are promoted to old data space, all
+ // other object types are promoted to old pointer space. We do not use
+ // object->IsHeapNumber() and object->IsSeqString() because we already
+ // know that object has the heap object tag.
+
+ // These objects are never allocated in new space.
+ ASSERT(type != MAP_TYPE);
+ ASSERT(type != CODE_TYPE);
+ ASSERT(type != ODDBALL_TYPE);
+ ASSERT(type != CELL_TYPE);
+ ASSERT(type != PROPERTY_CELL_TYPE);
+
+ if (type <= LAST_NAME_TYPE) {
+ if (type == SYMBOL_TYPE) return OLD_POINTER_SPACE;
+ ASSERT(type < FIRST_NONSTRING_TYPE);
+ // There are four string representations: sequential strings, external
+ // strings, cons strings, and sliced strings.
+ // Only the latter two contain non-map-word pointers to heap objects.
+ return ((type & kIsIndirectStringMask) == kIsIndirectStringTag)
+ ? OLD_POINTER_SPACE
+ : OLD_DATA_SPACE;
+ } else {
+ return (type <= LAST_DATA_TYPE) ? OLD_DATA_SPACE : OLD_POINTER_SPACE;
+ }
+}
+
+
+void Heap::CopyBlock(Address dst, Address src, int byte_size) {
+ CopyWords(reinterpret_cast<Object**>(dst),
+ reinterpret_cast<Object**>(src),
+ static_cast<size_t>(byte_size / kPointerSize));
+}
+
+
+void Heap::MoveBlock(Address dst, Address src, int byte_size) {
+ ASSERT(IsAligned(byte_size, kPointerSize));
+
+ int size_in_words = byte_size / kPointerSize;
+
+ if ((dst < src) || (dst >= (src + byte_size))) {
+ Object** src_slot = reinterpret_cast<Object**>(src);
+ Object** dst_slot = reinterpret_cast<Object**>(dst);
+ Object** end_slot = src_slot + size_in_words;
+
+ while (src_slot != end_slot) {
+ *dst_slot++ = *src_slot++;
+ }
+ } else {
+ OS::MemMove(dst, src, static_cast<size_t>(byte_size));
+ }
+}
+
+
+void Heap::ScavengePointer(HeapObject** p) {
+ ScavengeObject(p, *p);
+}
+
+
+void Heap::ScavengeObject(HeapObject** p, HeapObject* object) {
+ ASSERT(HEAP->InFromSpace(object));
+
+ // We use the first word (where the map pointer usually is) of a heap
+ // object to record the forwarding pointer. A forwarding pointer can
+ // point to an old space, the code space, or the to space of the new
+ // generation.
+ MapWord first_word = object->map_word();
+
+ // If the first word is a forwarding address, the object has already been
+ // copied.
+ if (first_word.IsForwardingAddress()) {
+ HeapObject* dest = first_word.ToForwardingAddress();
+ ASSERT(HEAP->InFromSpace(*p));
+ *p = dest;
+ return;
+ }
+
+ // Call the slow part of scavenge object.
+ return ScavengeObjectSlow(p, object);
+}
+
+
+MaybeObject* Heap::AllocateEmptyJSArrayWithAllocationSite(
+ ElementsKind elements_kind,
+ Handle<AllocationSite> allocation_site) {
+ return AllocateJSArrayAndStorageWithAllocationSite(elements_kind, 0, 0,
+ allocation_site, DONT_INITIALIZE_ARRAY_ELEMENTS);
+}
+
+
+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);
+}
+
+
+MaybeObject* Heap::PrepareForCompare(String* str) {
+ // Always flatten small strings and force flattening of long strings
+ // after we have accumulated a certain amount we failed to flatten.
+ static const int kMaxAlwaysFlattenLength = 32;
+ static const int kFlattenLongThreshold = 16*KB;
+
+ const int length = str->length();
+ MaybeObject* obj = str->TryFlatten();
+ if (length <= kMaxAlwaysFlattenLength ||
+ unflattened_strings_length_ >= kFlattenLongThreshold) {
+ return obj;
+ }
+ if (obj->IsFailure()) {
+ unflattened_strings_length_ += length;
+ }
+ return str;
+}
+
+
+intptr_t Heap::AdjustAmountOfExternalAllocatedMemory(
+ intptr_t change_in_bytes) {
+ ASSERT(HasBeenSetUp());
+ intptr_t amount = amount_of_external_allocated_memory_ + change_in_bytes;
+ if (change_in_bytes > 0) {
+ // Avoid overflow.
+ if (amount > amount_of_external_allocated_memory_) {
+ amount_of_external_allocated_memory_ = amount;
+ } else {
+ // Give up and reset the counters in case of an overflow.
+ amount_of_external_allocated_memory_ = 0;
+ amount_of_external_allocated_memory_at_last_global_gc_ = 0;
+ }
+ intptr_t amount_since_last_global_gc = PromotedExternalMemorySize();
+ if (amount_since_last_global_gc > external_allocation_limit_) {
+ CollectAllGarbage(kNoGCFlags, "external memory allocation limit reached");
+ }
+ } else {
+ // Avoid underflow.
+ if (amount >= 0) {
+ amount_of_external_allocated_memory_ = amount;
+ } else {
+ // Give up and reset the counters in case of an underflow.
+ amount_of_external_allocated_memory_ = 0;
+ amount_of_external_allocated_memory_at_last_global_gc_ = 0;
+ }
+ }
+ if (FLAG_trace_external_memory) {
+ PrintPID("%8.0f ms: ", isolate()->time_millis_since_init());
+ PrintF("Adjust amount of external memory: delta=%6" V8_PTR_PREFIX "d KB, "
+ "amount=%6" V8_PTR_PREFIX "d KB, since_gc=%6" V8_PTR_PREFIX "d KB, "
+ "isolate=0x%08" V8PRIxPTR ".\n",
+ change_in_bytes / KB,
+ amount_of_external_allocated_memory_ / KB,
+ PromotedExternalMemorySize() / KB,
+ reinterpret_cast<intptr_t>(isolate()));
+ }
+ ASSERT(amount_of_external_allocated_memory_ >= 0);
+ return amount_of_external_allocated_memory_;
+}
+
+
+Isolate* Heap::isolate() {
+ return reinterpret_cast<Isolate*>(reinterpret_cast<intptr_t>(this) -
+ reinterpret_cast<size_t>(reinterpret_cast<Isolate*>(4)->heap()) + 4);
+}
+
+
+#ifdef DEBUG
+#define GC_GREEDY_CHECK() \
+ if (FLAG_gc_greedy) HEAP->GarbageCollectionGreedyCheck()
+#else
+#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.
+
+// Warning: Do not use the identifiers __object__, __maybe_object__ or
+// __scope__ in a call to this macro.
+
+#define CALL_AND_RETRY(ISOLATE, FUNCTION_CALL, RETURN_VALUE, RETURN_EMPTY, OOM)\
+ do { \
+ GC_GREEDY_CHECK(); \
+ MaybeObject* __maybe_object__ = FUNCTION_CALL; \
+ Object* __object__ = NULL; \
+ if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE; \
+ if (__maybe_object__->IsOutOfMemory()) { \
+ OOM; \
+ } \
+ if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY; \
+ ISOLATE->heap()->CollectGarbage(Failure::cast(__maybe_object__)-> \
+ allocation_space(), \
+ "allocation failure"); \
+ __maybe_object__ = FUNCTION_CALL; \
+ if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE; \
+ if (__maybe_object__->IsOutOfMemory()) { \
+ OOM; \
+ } \
+ if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY; \
+ ISOLATE->counters()->gc_last_resort_from_handles()->Increment(); \
+ ISOLATE->heap()->CollectAllAvailableGarbage("last resort gc"); \
+ { \
+ AlwaysAllocateScope __scope__; \
+ __maybe_object__ = FUNCTION_CALL; \
+ } \
+ if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE; \
+ if (__maybe_object__->IsOutOfMemory()) { \
+ OOM; \
+ } \
+ if (__maybe_object__->IsRetryAfterGC()) { \
+ /* TODO(1181417): Fix this. */ \
+ v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY_LAST", true); \
+ } \
+ RETURN_EMPTY; \
+ } while (false)
+
+#define CALL_AND_RETRY_OR_DIE( \
+ ISOLATE, FUNCTION_CALL, RETURN_VALUE, RETURN_EMPTY) \
+ CALL_AND_RETRY( \
+ ISOLATE, \
+ FUNCTION_CALL, \
+ RETURN_VALUE, \
+ RETURN_EMPTY, \
+ v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY", true))
+
+#define CALL_HEAP_FUNCTION(ISOLATE, FUNCTION_CALL, TYPE) \
+ CALL_AND_RETRY_OR_DIE(ISOLATE, \
+ FUNCTION_CALL, \
+ return Handle<TYPE>(TYPE::cast(__object__), ISOLATE), \
+ return Handle<TYPE>()) \
+
+
+#define CALL_HEAP_FUNCTION_VOID(ISOLATE, FUNCTION_CALL) \
+ CALL_AND_RETRY_OR_DIE(ISOLATE, FUNCTION_CALL, return, return)
+
+
+#define CALL_HEAP_FUNCTION_PASS_EXCEPTION(ISOLATE, FUNCTION_CALL) \
+ CALL_AND_RETRY(ISOLATE, \
+ FUNCTION_CALL, \
+ return __object__, \
+ return __maybe_object__, \
+ return __maybe_object__)
+
+
+void ExternalStringTable::AddString(String* string) {
+ ASSERT(string->IsExternalString());
+ if (heap_->InNewSpace(string)) {
+ new_space_strings_.Add(string);
+ } else {
+ old_space_strings_.Add(string);
+ }
+}
+
+
+void ExternalStringTable::Iterate(ObjectVisitor* v) {
+ if (!new_space_strings_.is_empty()) {
+ Object** start = &new_space_strings_[0];
+ v->VisitPointers(start, start + new_space_strings_.length());
+ }
+ if (!old_space_strings_.is_empty()) {
+ Object** start = &old_space_strings_[0];
+ v->VisitPointers(start, start + old_space_strings_.length());
+ }
+}
+
+
+// Verify() is inline to avoid ifdef-s around its calls in release
+// mode.
+void ExternalStringTable::Verify() {
+#ifdef DEBUG
+ for (int i = 0; i < new_space_strings_.length(); ++i) {
+ Object* obj = Object::cast(new_space_strings_[i]);
+ // TODO(yangguo): check that the object is indeed an external string.
+ ASSERT(heap_->InNewSpace(obj));
+ ASSERT(obj != HEAP->the_hole_value());
+ }
+ for (int i = 0; i < old_space_strings_.length(); ++i) {
+ Object* obj = Object::cast(old_space_strings_[i]);
+ // TODO(yangguo): check that the object is indeed an external string.
+ ASSERT(!heap_->InNewSpace(obj));
+ ASSERT(obj != HEAP->the_hole_value());
+ }
+#endif
+}
+
+
+void ExternalStringTable::AddOldString(String* string) {
+ ASSERT(string->IsExternalString());
+ ASSERT(!heap_->InNewSpace(string));
+ old_space_strings_.Add(string);
+}
+
+
+void ExternalStringTable::ShrinkNewStrings(int position) {
+ new_space_strings_.Rewind(position);
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ Verify();
+ }
+#endif
+}
+
+
+void Heap::ClearInstanceofCache() {
+ set_instanceof_cache_function(the_hole_value());
+}
+
+
+Object* Heap::ToBoolean(bool condition) {
+ return condition ? true_value() : false_value();
+}
+
+
+void Heap::CompletelyClearInstanceofCache() {
+ set_instanceof_cache_map(the_hole_value());
+ set_instanceof_cache_function(the_hole_value());
+}
+
+
+MaybeObject* TranscendentalCache::Get(Type type, double input) {
+ SubCache* cache = caches_[type];
+ if (cache == NULL) {
+ caches_[type] = cache = new SubCache(type);
+ }
+ return cache->Get(input);
+}
+
+
+Address TranscendentalCache::cache_array_address() {
+ return reinterpret_cast<Address>(caches_);
+}
+
+
+double TranscendentalCache::SubCache::Calculate(double input) {
+ switch (type_) {
+ case ACOS:
+ return acos(input);
+ case ASIN:
+ return asin(input);
+ case ATAN:
+ return atan(input);
+ case COS:
+ return fast_cos(input);
+ case EXP:
+ return exp(input);
+ case LOG:
+ return fast_log(input);
+ case SIN:
+ return fast_sin(input);
+ case TAN:
+ return fast_tan(input);
+ default:
+ return 0.0; // Never happens.
+ }
+}
+
+
+MaybeObject* TranscendentalCache::SubCache::Get(double input) {
+ Converter c;
+ c.dbl = input;
+ int hash = Hash(c);
+ Element e = elements_[hash];
+ if (e.in[0] == c.integers[0] &&
+ e.in[1] == c.integers[1]) {
+ ASSERT(e.output != NULL);
+ isolate_->counters()->transcendental_cache_hit()->Increment();
+ return e.output;
+ }
+ double answer = Calculate(input);
+ isolate_->counters()->transcendental_cache_miss()->Increment();
+ Object* heap_number;
+ { MaybeObject* maybe_heap_number =
+ isolate_->heap()->AllocateHeapNumber(answer);
+ if (!maybe_heap_number->ToObject(&heap_number)) return maybe_heap_number;
+ }
+ elements_[hash].in[0] = c.integers[0];
+ elements_[hash].in[1] = c.integers[1];
+ elements_[hash].output = heap_number;
+ return heap_number;
+}
+
+
+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);
+}
+
+
+#ifdef VERIFY_HEAP
+NoWeakEmbeddedMapsVerificationScope::NoWeakEmbeddedMapsVerificationScope() {
+ HEAP->no_weak_embedded_maps_verification_scope_depth_++;
+}
+
+
+NoWeakEmbeddedMapsVerificationScope::~NoWeakEmbeddedMapsVerificationScope() {
+ HEAP->no_weak_embedded_maps_verification_scope_depth_--;
+}
+#endif
+
+
+void VerifyPointersVisitor::VisitPointers(Object** start, Object** end) {
+ for (Object** current = start; current < end; current++) {
+ if ((*current)->IsHeapObject()) {
+ HeapObject* object = HeapObject::cast(*current);
+ CHECK(HEAP->Contains(object));
+ CHECK(object->map()->IsMap());
+ }
+ }
+}
+
+
+double GCTracer::SizeOfHeapObjects() {
+ return (static_cast<double>(HEAP->SizeOfObjects())) / MB;
+}
+
+
+DisallowAllocationFailure::DisallowAllocationFailure() {
+#ifdef DEBUG
+ old_state_ = HEAP->disallow_allocation_failure_;
+ HEAP->disallow_allocation_failure_ = true;
+#endif
+}
+
+
+DisallowAllocationFailure::~DisallowAllocationFailure() {
+#ifdef DEBUG
+ HEAP->disallow_allocation_failure_ = old_state_;
+#endif
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_HEAP_INL_H_
diff --git a/chromium/v8/src/heap-profiler.cc b/chromium/v8/src/heap-profiler.cc
new file mode 100644
index 00000000000..e66af3364d8
--- /dev/null
+++ b/chromium/v8/src/heap-profiler.cc
@@ -0,0 +1,145 @@
+// Copyright 2009-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.
+
+#include "v8.h"
+
+#include "heap-profiler.h"
+#include "heap-snapshot-generator-inl.h"
+
+namespace v8 {
+namespace internal {
+
+HeapProfiler::HeapProfiler(Heap* heap)
+ : snapshots_(new HeapSnapshotsCollection(heap)),
+ next_snapshot_uid_(1) {
+}
+
+
+HeapProfiler::~HeapProfiler() {
+ delete snapshots_;
+}
+
+
+void HeapProfiler::DeleteAllSnapshots() {
+ Heap* the_heap = heap();
+ delete snapshots_;
+ snapshots_ = new HeapSnapshotsCollection(the_heap);
+}
+
+
+void HeapProfiler::DefineWrapperClass(
+ uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) {
+ ASSERT(class_id != v8::HeapProfiler::kPersistentHandleNoClassId);
+ if (wrapper_callbacks_.length() <= class_id) {
+ wrapper_callbacks_.AddBlock(
+ NULL, class_id - wrapper_callbacks_.length() + 1);
+ }
+ wrapper_callbacks_[class_id] = callback;
+}
+
+
+v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback(
+ uint16_t class_id, Object** wrapper) {
+ if (wrapper_callbacks_.length() <= class_id) return NULL;
+ return wrapper_callbacks_[class_id](
+ class_id, Utils::ToLocal(Handle<Object>(wrapper)));
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshot(
+ const char* name,
+ v8::ActivityControl* control,
+ v8::HeapProfiler::ObjectNameResolver* resolver) {
+ HeapSnapshot* result = snapshots_->NewSnapshot(name, next_snapshot_uid_++);
+ {
+ HeapSnapshotGenerator generator(result, control, resolver, heap());
+ if (!generator.GenerateSnapshot()) {
+ delete result;
+ result = NULL;
+ }
+ }
+ snapshots_->SnapshotGenerationFinished(result);
+ return result;
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshot(
+ String* name,
+ v8::ActivityControl* control,
+ v8::HeapProfiler::ObjectNameResolver* resolver) {
+ return TakeSnapshot(snapshots_->names()->GetName(name), control, resolver);
+}
+
+
+void HeapProfiler::StartHeapObjectsTracking() {
+ snapshots_->StartHeapObjectsTracking();
+}
+
+
+SnapshotObjectId HeapProfiler::PushHeapObjectsStats(OutputStream* stream) {
+ return snapshots_->PushHeapObjectsStats(stream);
+}
+
+
+void HeapProfiler::StopHeapObjectsTracking() {
+ snapshots_->StopHeapObjectsTracking();
+}
+
+
+size_t HeapProfiler::GetMemorySizeUsedByProfiler() {
+ return snapshots_->GetUsedMemorySize();
+}
+
+
+int HeapProfiler::GetSnapshotsCount() {
+ return snapshots_->snapshots()->length();
+}
+
+
+HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
+ return snapshots_->snapshots()->at(index);
+}
+
+
+SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) {
+ if (!obj->IsHeapObject())
+ return v8::HeapProfiler::kUnknownObjectId;
+ return snapshots_->FindObjectId(HeapObject::cast(*obj)->address());
+}
+
+
+void HeapProfiler::ObjectMoveEvent(Address from, Address to) {
+ snapshots_->ObjectMoveEvent(from, to);
+}
+
+void HeapProfiler::SetRetainedObjectInfo(UniqueId id,
+ RetainedObjectInfo* info) {
+ // TODO(yurus, marja): Don't route this information through GlobalHandles.
+ heap()->isolate()->global_handles()->SetRetainedObjectInfo(id, info);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/heap-profiler.h b/chromium/v8/src/heap-profiler.h
new file mode 100644
index 00000000000..5ae60fa9234
--- /dev/null
+++ b/chromium/v8/src/heap-profiler.h
@@ -0,0 +1,95 @@
+// Copyright 2009-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.
+
+#ifndef V8_HEAP_PROFILER_H_
+#define V8_HEAP_PROFILER_H_
+
+#include "heap-snapshot-generator-inl.h"
+#include "isolate.h"
+
+namespace v8 {
+namespace internal {
+
+class HeapSnapshot;
+class HeapSnapshotsCollection;
+
+#define HEAP_PROFILE(heap, call) \
+ do { \
+ v8::internal::HeapProfiler* profiler = heap->isolate()->heap_profiler(); \
+ if (profiler != NULL && profiler->is_profiling()) { \
+ profiler->call; \
+ } \
+ } while (false)
+
+class HeapProfiler {
+ public:
+ explicit HeapProfiler(Heap* heap);
+ ~HeapProfiler();
+
+ size_t GetMemorySizeUsedByProfiler();
+
+ HeapSnapshot* TakeSnapshot(
+ const char* name,
+ v8::ActivityControl* control,
+ v8::HeapProfiler::ObjectNameResolver* resolver);
+ HeapSnapshot* TakeSnapshot(
+ String* name,
+ v8::ActivityControl* control,
+ v8::HeapProfiler::ObjectNameResolver* resolver);
+
+ void StartHeapObjectsTracking();
+ void StopHeapObjectsTracking();
+ SnapshotObjectId PushHeapObjectsStats(OutputStream* stream);
+ int GetSnapshotsCount();
+ HeapSnapshot* GetSnapshot(int index);
+ SnapshotObjectId GetSnapshotObjectId(Handle<Object> obj);
+ void DeleteAllSnapshots();
+
+ void ObjectMoveEvent(Address from, Address to);
+
+ void DefineWrapperClass(
+ uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback);
+
+ v8::RetainedObjectInfo* ExecuteWrapperClassCallback(uint16_t class_id,
+ Object** wrapper);
+ INLINE(bool is_profiling()) {
+ return snapshots_->is_tracking_objects();
+ }
+
+ void SetRetainedObjectInfo(UniqueId id, RetainedObjectInfo* info);
+
+ private:
+ Heap* heap() const { return snapshots_->heap(); }
+
+ HeapSnapshotsCollection* snapshots_;
+ unsigned next_snapshot_uid_;
+ List<v8::HeapProfiler::WrapperInfoCallback> wrapper_callbacks_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_HEAP_PROFILER_H_
diff --git a/chromium/v8/src/heap-snapshot-generator-inl.h b/chromium/v8/src/heap-snapshot-generator-inl.h
new file mode 100644
index 00000000000..1a878c6df17
--- /dev/null
+++ b/chromium/v8/src/heap-snapshot-generator-inl.h
@@ -0,0 +1,88 @@
+// Copyright 2013 the V8 project authors. 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_HEAP_SNAPSHOT_GENERATOR_INL_H_
+#define V8_HEAP_SNAPSHOT_GENERATOR_INL_H_
+
+#include "heap-snapshot-generator.h"
+
+namespace v8 {
+namespace internal {
+
+
+HeapEntry* HeapGraphEdge::from() const {
+ return &snapshot()->entries()[from_index_];
+}
+
+
+HeapSnapshot* HeapGraphEdge::snapshot() const {
+ return to_entry_->snapshot();
+}
+
+
+int HeapEntry::index() const {
+ return static_cast<int>(this - &snapshot_->entries().first());
+}
+
+
+int HeapEntry::set_children_index(int index) {
+ children_index_ = index;
+ int next_index = index + children_count_;
+ children_count_ = 0;
+ return next_index;
+}
+
+
+HeapGraphEdge** HeapEntry::children_arr() {
+ ASSERT(children_index_ >= 0);
+ return &snapshot_->children()[children_index_];
+}
+
+
+SnapshotObjectId 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);
+}
+
+} } // namespace v8::internal
+
+#endif // V8_HEAP_SNAPSHOT_GENERATOR_INL_H_
+
diff --git a/chromium/v8/src/heap-snapshot-generator.cc b/chromium/v8/src/heap-snapshot-generator.cc
new file mode 100644
index 00000000000..1c8a7b3dc4d
--- /dev/null
+++ b/chromium/v8/src/heap-snapshot-generator.cc
@@ -0,0 +1,2712 @@
+// Copyright 2013 the V8 project authors. 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 "heap-snapshot-generator-inl.h"
+
+#include "heap-profiler.h"
+#include "debug.h"
+#include "types.h"
+
+namespace v8 {
+namespace internal {
+
+
+HeapGraphEdge::HeapGraphEdge(Type type, const char* name, int from, int to)
+ : type_(type),
+ from_index_(from),
+ to_index_(to),
+ name_(name) {
+ ASSERT(type == kContextVariable
+ || type == kProperty
+ || type == kInternal
+ || type == kShortcut);
+}
+
+
+HeapGraphEdge::HeapGraphEdge(Type type, int index, int from, int to)
+ : type_(type),
+ from_index_(from),
+ to_index_(to),
+ index_(index) {
+ ASSERT(type == kElement || type == kHidden || type == kWeak);
+}
+
+
+void HeapGraphEdge::ReplaceToIndexWithEntry(HeapSnapshot* snapshot) {
+ to_entry_ = &snapshot->entries()[to_index_];
+}
+
+
+const int HeapEntry::kNoEntry = -1;
+
+HeapEntry::HeapEntry(HeapSnapshot* snapshot,
+ Type type,
+ const char* name,
+ SnapshotObjectId id,
+ int self_size)
+ : type_(type),
+ children_count_(0),
+ children_index_(-1),
+ self_size_(self_size),
+ id_(id),
+ snapshot_(snapshot),
+ name_(name) { }
+
+
+void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
+ const char* name,
+ HeapEntry* entry) {
+ HeapGraphEdge edge(type, name, this->index(), entry->index());
+ snapshot_->edges().Add(edge);
+ ++children_count_;
+}
+
+
+void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
+ int index,
+ HeapEntry* entry) {
+ HeapGraphEdge edge(type, index, this->index(), entry->index());
+ snapshot_->edges().Add(edge);
+ ++children_count_;
+}
+
+
+Handle<HeapObject> HeapEntry::GetHeapObject() {
+ return snapshot_->collection()->FindHeapObjectById(id());
+}
+
+
+void HeapEntry::Print(
+ const char* prefix, const char* edge_name, int max_depth, int indent) {
+ STATIC_CHECK(sizeof(unsigned) == sizeof(id()));
+ OS::Print("%6d @%6u %*c %s%s: ",
+ self_size(), id(), indent, ' ', prefix, edge_name);
+ if (type() != kString) {
+ OS::Print("%s %.40s\n", TypeAsString(), name_);
+ } else {
+ OS::Print("\"");
+ const char* c = name_;
+ while (*c && (c - name_) <= 40) {
+ if (*c != '\n')
+ OS::Print("%c", *c);
+ else
+ OS::Print("\\n");
+ ++c;
+ }
+ OS::Print("\"\n");
+ }
+ if (--max_depth == 0) return;
+ Vector<HeapGraphEdge*> ch = children();
+ for (int i = 0; i < ch.length(); ++i) {
+ HeapGraphEdge& edge = *ch[i];
+ const char* edge_prefix = "";
+ EmbeddedVector<char, 64> index;
+ const char* edge_name = index.start();
+ switch (edge.type()) {
+ case HeapGraphEdge::kContextVariable:
+ edge_prefix = "#";
+ edge_name = edge.name();
+ break;
+ case HeapGraphEdge::kElement:
+ OS::SNPrintF(index, "%d", edge.index());
+ break;
+ case HeapGraphEdge::kInternal:
+ edge_prefix = "$";
+ edge_name = edge.name();
+ break;
+ case HeapGraphEdge::kProperty:
+ edge_name = edge.name();
+ break;
+ case HeapGraphEdge::kHidden:
+ edge_prefix = "$";
+ OS::SNPrintF(index, "%d", edge.index());
+ break;
+ case HeapGraphEdge::kShortcut:
+ edge_prefix = "^";
+ edge_name = edge.name();
+ break;
+ case HeapGraphEdge::kWeak:
+ edge_prefix = "w";
+ OS::SNPrintF(index, "%d", edge.index());
+ break;
+ default:
+ OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
+ }
+ edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
+ }
+}
+
+
+const char* HeapEntry::TypeAsString() {
+ switch (type()) {
+ case kHidden: return "/hidden/";
+ case kObject: return "/object/";
+ case kClosure: return "/closure/";
+ case kString: return "/string/";
+ case kCode: return "/code/";
+ case kArray: return "/array/";
+ case kRegExp: return "/regexp/";
+ case kHeapNumber: return "/number/";
+ case kNative: return "/native/";
+ case kSynthetic: return "/synthetic/";
+ default: return "???";
+ }
+}
+
+
+// It is very important to keep objects that form a heap snapshot
+// as small as possible.
+namespace { // Avoid littering the global namespace.
+
+template <size_t ptr_size> struct SnapshotSizeConstants;
+
+template <> struct SnapshotSizeConstants<4> {
+ static const int kExpectedHeapGraphEdgeSize = 12;
+ static const int kExpectedHeapEntrySize = 24;
+};
+
+template <> struct SnapshotSizeConstants<8> {
+ static const int kExpectedHeapGraphEdgeSize = 24;
+ static const int kExpectedHeapEntrySize = 32;
+};
+
+} // namespace
+
+HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
+ const char* title,
+ unsigned uid)
+ : collection_(collection),
+ title_(title),
+ uid_(uid),
+ root_index_(HeapEntry::kNoEntry),
+ gc_roots_index_(HeapEntry::kNoEntry),
+ natives_root_index_(HeapEntry::kNoEntry),
+ max_snapshot_js_object_id_(0) {
+ STATIC_CHECK(
+ sizeof(HeapGraphEdge) ==
+ SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize);
+ STATIC_CHECK(
+ sizeof(HeapEntry) ==
+ SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize);
+ for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) {
+ gc_subroot_indexes_[i] = HeapEntry::kNoEntry;
+ }
+}
+
+
+void HeapSnapshot::Delete() {
+ collection_->RemoveSnapshot(this);
+ delete this;
+}
+
+
+void HeapSnapshot::RememberLastJSObjectId() {
+ max_snapshot_js_object_id_ = collection_->last_assigned_id();
+}
+
+
+HeapEntry* HeapSnapshot::AddRootEntry() {
+ ASSERT(root_index_ == HeapEntry::kNoEntry);
+ ASSERT(entries_.is_empty()); // Root entry must be the first one.
+ HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
+ "",
+ HeapObjectsMap::kInternalRootObjectId,
+ 0);
+ root_index_ = entry->index();
+ ASSERT(root_index_ == 0);
+ return entry;
+}
+
+
+HeapEntry* HeapSnapshot::AddGcRootsEntry() {
+ ASSERT(gc_roots_index_ == HeapEntry::kNoEntry);
+ HeapEntry* entry = AddEntry(HeapEntry::kSynthetic,
+ "(GC roots)",
+ HeapObjectsMap::kGcRootsObjectId,
+ 0);
+ gc_roots_index_ = entry->index();
+ return entry;
+}
+
+
+HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag) {
+ ASSERT(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry);
+ ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
+ HeapEntry* entry = AddEntry(
+ HeapEntry::kSynthetic,
+ VisitorSynchronization::kTagNames[tag],
+ HeapObjectsMap::GetNthGcSubrootId(tag),
+ 0);
+ gc_subroot_indexes_[tag] = entry->index();
+ return entry;
+}
+
+
+HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
+ const char* name,
+ SnapshotObjectId id,
+ int size) {
+ HeapEntry entry(this, type, name, id, size);
+ entries_.Add(entry);
+ return &entries_.last();
+}
+
+
+void HeapSnapshot::FillChildren() {
+ ASSERT(children().is_empty());
+ children().Allocate(edges().length());
+ int children_index = 0;
+ for (int i = 0; i < entries().length(); ++i) {
+ HeapEntry* entry = &entries()[i];
+ children_index = entry->set_children_index(children_index);
+ }
+ ASSERT(edges().length() == children_index);
+ for (int i = 0; i < edges().length(); ++i) {
+ HeapGraphEdge* edge = &edges()[i];
+ edge->ReplaceToIndexWithEntry(this);
+ edge->from()->add_child(edge);
+ }
+}
+
+
+class FindEntryById {
+ public:
+ explicit FindEntryById(SnapshotObjectId id) : id_(id) { }
+ int operator()(HeapEntry* const* entry) {
+ if ((*entry)->id() == id_) return 0;
+ return (*entry)->id() < id_ ? -1 : 1;
+ }
+ private:
+ SnapshotObjectId id_;
+};
+
+
+HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
+ List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
+ // Perform a binary search by id.
+ int index = SortedListBSearch(*entries_by_id, FindEntryById(id));
+ if (index == -1)
+ return NULL;
+ return entries_by_id->at(index);
+}
+
+
+template<class T>
+static int SortByIds(const T* entry1_ptr,
+ const T* entry2_ptr) {
+ if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
+ return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
+}
+
+
+List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
+ if (sorted_entries_.is_empty()) {
+ sorted_entries_.Allocate(entries_.length());
+ for (int i = 0; i < entries_.length(); ++i) {
+ sorted_entries_[i] = &entries_[i];
+ }
+ sorted_entries_.Sort(SortByIds);
+ }
+ return &sorted_entries_;
+}
+
+
+void HeapSnapshot::Print(int max_depth) {
+ root()->Print("", "", max_depth, 0);
+}
+
+
+template<typename T, class P>
+static size_t GetMemoryUsedByList(const List<T, P>& list) {
+ return list.length() * sizeof(T) + sizeof(list);
+}
+
+
+size_t HeapSnapshot::RawSnapshotSize() const {
+ return
+ sizeof(*this) +
+ GetMemoryUsedByList(entries_) +
+ GetMemoryUsedByList(edges_) +
+ GetMemoryUsedByList(children_) +
+ GetMemoryUsedByList(sorted_entries_);
+}
+
+
+// We split IDs on evens for embedder objects (see
+// HeapObjectsMap::GenerateId) and odds for native objects.
+const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
+const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
+ HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
+const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
+ HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
+const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
+ HeapObjectsMap::kGcRootsFirstSubrootId +
+ VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
+
+
+static bool AddressesMatch(void* key1, void* key2) {
+ return key1 == key2;
+}
+
+
+HeapObjectsMap::HeapObjectsMap(Heap* heap)
+ : next_id_(kFirstAvailableObjectId),
+ entries_map_(AddressesMatch),
+ heap_(heap) {
+ // This dummy element solves a problem with entries_map_.
+ // When we do lookup in HashMap we see no difference between two cases:
+ // it has an entry with NULL as the value or it has created
+ // a new entry on the fly with NULL as the default value.
+ // With such dummy element we have a guaranty that all entries_map_ entries
+ // will have the value field grater than 0.
+ // This fact is using in MoveObject method.
+ entries_.Add(EntryInfo(0, NULL, 0));
+}
+
+
+void HeapObjectsMap::SnapshotGenerationFinished() {
+ RemoveDeadEntries();
+}
+
+
+void HeapObjectsMap::MoveObject(Address from, Address to) {
+ ASSERT(to != NULL);
+ ASSERT(from != NULL);
+ if (from == to) return;
+ void* from_value = entries_map_.Remove(from, ComputePointerHash(from));
+ if (from_value == NULL) {
+ // It may occur that some untracked object moves to an address X and there
+ // is a tracked object at that address. In this case we should remove the
+ // entry as we know that the object has died.
+ void* to_value = entries_map_.Remove(to, ComputePointerHash(to));
+ if (to_value != NULL) {
+ int to_entry_info_index =
+ static_cast<int>(reinterpret_cast<intptr_t>(to_value));
+ entries_.at(to_entry_info_index).addr = NULL;
+ }
+ } else {
+ HashMap::Entry* to_entry = entries_map_.Lookup(to, ComputePointerHash(to),
+ true);
+ if (to_entry->value != NULL) {
+ // We found the existing entry with to address for an old object.
+ // Without this operation we will have two EntryInfo's with the same
+ // value in addr field. It is bad because later at RemoveDeadEntries
+ // one of this entry will be removed with the corresponding entries_map_
+ // entry.
+ int to_entry_info_index =
+ static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value));
+ entries_.at(to_entry_info_index).addr = NULL;
+ }
+ int from_entry_info_index =
+ static_cast<int>(reinterpret_cast<intptr_t>(from_value));
+ entries_.at(from_entry_info_index).addr = to;
+ to_entry->value = from_value;
+ }
+}
+
+
+SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
+ HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr),
+ false);
+ if (entry == NULL) return 0;
+ int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
+ EntryInfo& entry_info = entries_.at(entry_index);
+ ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
+ return entry_info.id;
+}
+
+
+SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
+ unsigned int size) {
+ ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
+ HashMap::Entry* entry = entries_map_.Lookup(addr, ComputePointerHash(addr),
+ true);
+ if (entry->value != NULL) {
+ int entry_index =
+ static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
+ EntryInfo& entry_info = entries_.at(entry_index);
+ entry_info.accessed = true;
+ entry_info.size = size;
+ return entry_info.id;
+ }
+ entry->value = reinterpret_cast<void*>(entries_.length());
+ SnapshotObjectId id = next_id_;
+ next_id_ += kObjectIdStep;
+ entries_.Add(EntryInfo(id, addr, size));
+ ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
+ return id;
+}
+
+
+void HeapObjectsMap::StopHeapObjectsTracking() {
+ time_intervals_.Clear();
+}
+
+
+void HeapObjectsMap::UpdateHeapObjectsMap() {
+ HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
+ "HeapSnapshotsCollection::UpdateHeapObjectsMap");
+ HeapIterator iterator(heap_);
+ for (HeapObject* obj = iterator.next();
+ obj != NULL;
+ obj = iterator.next()) {
+ FindOrAddEntry(obj->address(), obj->Size());
+ }
+ RemoveDeadEntries();
+}
+
+
+SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) {
+ UpdateHeapObjectsMap();
+ time_intervals_.Add(TimeInterval(next_id_));
+ int prefered_chunk_size = stream->GetChunkSize();
+ List<v8::HeapStatsUpdate> stats_buffer;
+ ASSERT(!entries_.is_empty());
+ EntryInfo* entry_info = &entries_.first();
+ EntryInfo* end_entry_info = &entries_.last() + 1;
+ for (int time_interval_index = 0;
+ time_interval_index < time_intervals_.length();
+ ++time_interval_index) {
+ TimeInterval& time_interval = time_intervals_[time_interval_index];
+ SnapshotObjectId time_interval_id = time_interval.id;
+ uint32_t entries_size = 0;
+ EntryInfo* start_entry_info = entry_info;
+ while (entry_info < end_entry_info && entry_info->id < time_interval_id) {
+ entries_size += entry_info->size;
+ ++entry_info;
+ }
+ uint32_t entries_count =
+ static_cast<uint32_t>(entry_info - start_entry_info);
+ if (time_interval.count != entries_count ||
+ time_interval.size != entries_size) {
+ stats_buffer.Add(v8::HeapStatsUpdate(
+ time_interval_index,
+ time_interval.count = entries_count,
+ time_interval.size = entries_size));
+ if (stats_buffer.length() >= prefered_chunk_size) {
+ OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
+ &stats_buffer.first(), stats_buffer.length());
+ if (result == OutputStream::kAbort) return last_assigned_id();
+ stats_buffer.Clear();
+ }
+ }
+ }
+ ASSERT(entry_info == end_entry_info);
+ if (!stats_buffer.is_empty()) {
+ OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
+ &stats_buffer.first(), stats_buffer.length());
+ if (result == OutputStream::kAbort) return last_assigned_id();
+ }
+ stream->EndOfStream();
+ return last_assigned_id();
+}
+
+
+void HeapObjectsMap::RemoveDeadEntries() {
+ ASSERT(entries_.length() > 0 &&
+ entries_.at(0).id == 0 &&
+ entries_.at(0).addr == NULL);
+ int first_free_entry = 1;
+ for (int i = 1; i < entries_.length(); ++i) {
+ EntryInfo& entry_info = entries_.at(i);
+ if (entry_info.accessed) {
+ if (first_free_entry != i) {
+ entries_.at(first_free_entry) = entry_info;
+ }
+ entries_.at(first_free_entry).accessed = false;
+ HashMap::Entry* entry = entries_map_.Lookup(
+ entry_info.addr, ComputePointerHash(entry_info.addr), false);
+ ASSERT(entry);
+ entry->value = reinterpret_cast<void*>(first_free_entry);
+ ++first_free_entry;
+ } else {
+ if (entry_info.addr) {
+ entries_map_.Remove(entry_info.addr,
+ ComputePointerHash(entry_info.addr));
+ }
+ }
+ }
+ entries_.Rewind(first_free_entry);
+ ASSERT(static_cast<uint32_t>(entries_.length()) - 1 ==
+ entries_map_.occupancy());
+}
+
+
+SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
+ SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash());
+ const char* label = info->GetLabel();
+ id ^= StringHasher::HashSequentialString(label,
+ static_cast<int>(strlen(label)),
+ HEAP->HashSeed());
+ intptr_t element_count = info->GetElementCount();
+ if (element_count != -1)
+ id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count),
+ v8::internal::kZeroHashSeed);
+ return id << 1;
+}
+
+
+size_t HeapObjectsMap::GetUsedMemorySize() const {
+ return
+ sizeof(*this) +
+ sizeof(HashMap::Entry) * entries_map_.capacity() +
+ GetMemoryUsedByList(entries_) +
+ GetMemoryUsedByList(time_intervals_);
+}
+
+
+HeapSnapshotsCollection::HeapSnapshotsCollection(Heap* heap)
+ : is_tracking_objects_(false),
+ ids_(heap) {
+}
+
+
+static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
+ delete *snapshot_ptr;
+}
+
+
+HeapSnapshotsCollection::~HeapSnapshotsCollection() {
+ snapshots_.Iterate(DeleteHeapSnapshot);
+}
+
+
+HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
+ unsigned uid) {
+ is_tracking_objects_ = true; // Start watching for heap objects moves.
+ return new HeapSnapshot(this, name, uid);
+}
+
+
+void HeapSnapshotsCollection::SnapshotGenerationFinished(
+ HeapSnapshot* snapshot) {
+ ids_.SnapshotGenerationFinished();
+ if (snapshot != NULL) {
+ snapshots_.Add(snapshot);
+ }
+}
+
+
+void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) {
+ snapshots_.RemoveElement(snapshot);
+}
+
+
+Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(
+ SnapshotObjectId id) {
+ // First perform a full GC in order to avoid dead objects.
+ HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
+ "HeapSnapshotsCollection::FindHeapObjectById");
+ DisallowHeapAllocation no_allocation;
+ HeapObject* object = NULL;
+ HeapIterator iterator(heap(), HeapIterator::kFilterUnreachable);
+ // Make sure that object with the given id is still reachable.
+ for (HeapObject* obj = iterator.next();
+ obj != NULL;
+ obj = iterator.next()) {
+ if (ids_.FindEntry(obj->address()) == id) {
+ ASSERT(object == NULL);
+ object = obj;
+ // Can't break -- kFilterUnreachable requires full heap traversal.
+ }
+ }
+ return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>();
+}
+
+
+size_t HeapSnapshotsCollection::GetUsedMemorySize() const {
+ size_t size = sizeof(*this);
+ size += names_.GetUsedMemorySize();
+ size += ids_.GetUsedMemorySize();
+ size += GetMemoryUsedByList(snapshots_);
+ for (int i = 0; i < snapshots_.length(); ++i) {
+ size += snapshots_[i]->RawSnapshotSize();
+ }
+ return size;
+}
+
+
+HeapEntriesMap::HeapEntriesMap()
+ : entries_(HeapThingsMatch) {
+}
+
+
+int HeapEntriesMap::Map(HeapThing thing) {
+ HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false);
+ if (cache_entry == NULL) return HeapEntry::kNoEntry;
+ return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
+}
+
+
+void HeapEntriesMap::Pair(HeapThing thing, int entry) {
+ HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
+ ASSERT(cache_entry->value == NULL);
+ cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(entry));
+}
+
+
+HeapObjectsSet::HeapObjectsSet()
+ : entries_(HeapEntriesMap::HeapThingsMatch) {
+}
+
+
+void HeapObjectsSet::Clear() {
+ entries_.Clear();
+}
+
+
+bool HeapObjectsSet::Contains(Object* obj) {
+ if (!obj->IsHeapObject()) return false;
+ HeapObject* object = HeapObject::cast(obj);
+ return entries_.Lookup(object, HeapEntriesMap::Hash(object), false) != NULL;
+}
+
+
+void HeapObjectsSet::Insert(Object* obj) {
+ if (!obj->IsHeapObject()) return;
+ HeapObject* object = HeapObject::cast(obj);
+ entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
+}
+
+
+const char* HeapObjectsSet::GetTag(Object* obj) {
+ HeapObject* object = HeapObject::cast(obj);
+ HashMap::Entry* cache_entry =
+ entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
+ return cache_entry != NULL
+ ? reinterpret_cast<const char*>(cache_entry->value)
+ : NULL;
+}
+
+
+void HeapObjectsSet::SetTag(Object* obj, const char* tag) {
+ if (!obj->IsHeapObject()) return;
+ HeapObject* object = HeapObject::cast(obj);
+ HashMap::Entry* cache_entry =
+ entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
+ cache_entry->value = const_cast<char*>(tag);
+}
+
+
+HeapObject* const V8HeapExplorer::kInternalRootObject =
+ reinterpret_cast<HeapObject*>(
+ static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
+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(
+ HeapSnapshot* snapshot,
+ SnapshottingProgressReportingInterface* progress,
+ v8::HeapProfiler::ObjectNameResolver* resolver)
+ : heap_(Isolate::Current()->heap()),
+ snapshot_(snapshot),
+ collection_(snapshot_->collection()),
+ progress_(progress),
+ filler_(NULL),
+ global_object_name_resolver_(resolver) {
+}
+
+
+V8HeapExplorer::~V8HeapExplorer() {
+}
+
+
+HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
+ return AddEntry(reinterpret_cast<HeapObject*>(ptr));
+}
+
+
+HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) {
+ if (object == kInternalRootObject) {
+ snapshot_->AddRootEntry();
+ return snapshot_->root();
+ } else if (object == kGcRootsObject) {
+ HeapEntry* entry = snapshot_->AddGcRootsEntry();
+ return entry;
+ } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) {
+ HeapEntry* entry = snapshot_->AddGcSubrootEntry(GetGcSubrootOrder(object));
+ return entry;
+ } else if (object->IsJSFunction()) {
+ JSFunction* func = JSFunction::cast(object);
+ SharedFunctionInfo* shared = func->shared();
+ const char* name = shared->bound() ? "native_bind" :
+ collection_->names()->GetName(String::cast(shared->name()));
+ return AddEntry(object, HeapEntry::kClosure, name);
+ } else if (object->IsJSRegExp()) {
+ JSRegExp* re = JSRegExp::cast(object);
+ return AddEntry(object,
+ HeapEntry::kRegExp,
+ collection_->names()->GetName(re->Pattern()));
+ } else if (object->IsJSObject()) {
+ const char* name = collection_->names()->GetName(
+ GetConstructorName(JSObject::cast(object)));
+ if (object->IsJSGlobalObject()) {
+ const char* tag = objects_tags_.GetTag(object);
+ if (tag != NULL) {
+ name = collection_->names()->GetFormatted("%s / %s", name, tag);
+ }
+ }
+ return AddEntry(object, HeapEntry::kObject, name);
+ } else if (object->IsString()) {
+ return AddEntry(object,
+ HeapEntry::kString,
+ collection_->names()->GetName(String::cast(object)));
+ } else if (object->IsCode()) {
+ return AddEntry(object, HeapEntry::kCode, "");
+ } else if (object->IsSharedFunctionInfo()) {
+ String* name = String::cast(SharedFunctionInfo::cast(object)->name());
+ return AddEntry(object,
+ HeapEntry::kCode,
+ collection_->names()->GetName(name));
+ } else if (object->IsScript()) {
+ Object* name = Script::cast(object)->name();
+ return AddEntry(object,
+ HeapEntry::kCode,
+ name->IsString()
+ ? collection_->names()->GetName(String::cast(name))
+ : "");
+ } else if (object->IsNativeContext()) {
+ return AddEntry(object, HeapEntry::kHidden, "system / NativeContext");
+ } else if (object->IsContext()) {
+ return AddEntry(object, HeapEntry::kObject, "system / Context");
+ } else if (object->IsFixedArray() ||
+ object->IsFixedDoubleArray() ||
+ object->IsByteArray() ||
+ object->IsExternalArray()) {
+ return AddEntry(object, HeapEntry::kArray, "");
+ } else if (object->IsHeapNumber()) {
+ return AddEntry(object, HeapEntry::kHeapNumber, "number");
+ }
+ return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object));
+}
+
+
+HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
+ HeapEntry::Type type,
+ const char* name) {
+ int object_size = object->Size();
+ SnapshotObjectId object_id =
+ collection_->GetObjectId(object->address(), object_size);
+ return snapshot_->AddEntry(type, name, object_id, object_size);
+}
+
+
+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);
+}
+
+
+const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
+ switch (object->map()->instance_type()) {
+ case MAP_TYPE:
+ switch (Map::cast(object)->instance_type()) {
+#define MAKE_STRING_MAP_CASE(instance_type, size, name, Name) \
+ case instance_type: return "system / Map (" #Name ")";
+ STRING_TYPE_LIST(MAKE_STRING_MAP_CASE)
+#undef MAKE_STRING_MAP_CASE
+ default: return "system / Map";
+ }
+ case CELL_TYPE: return "system / Cell";
+ case PROPERTY_CELL_TYPE: return "system / PropertyCell";
+ case FOREIGN_TYPE: return "system / Foreign";
+ case ODDBALL_TYPE: return "system / Oddball";
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE: return "system / "#Name;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ default: return "system";
+ }
+}
+
+
+int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) {
+ int objects_count = 0;
+ for (HeapObject* obj = iterator->next();
+ obj != NULL;
+ obj = iterator->next()) {
+ objects_count++;
+ }
+ return objects_count;
+}
+
+
+class IndexedReferencesExtractor : public ObjectVisitor {
+ public:
+ IndexedReferencesExtractor(V8HeapExplorer* generator,
+ HeapObject* parent_obj,
+ int parent)
+ : generator_(generator),
+ parent_obj_(parent_obj),
+ parent_(parent),
+ next_index_(0) {
+ }
+ void VisitCodeEntry(Address entry_address) {
+ Code* code = Code::cast(Code::GetObjectFromEntryAddress(entry_address));
+ generator_->SetInternalReference(parent_obj_, parent_, "code", code);
+ generator_->TagObject(code, "(code)");
+ }
+ void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) {
+ if (CheckVisitedAndUnmark(p)) continue;
+ generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p);
+ }
+ }
+ static void MarkVisitedField(HeapObject* obj, int offset) {
+ if (offset < 0) return;
+ Address field = obj->address() + offset;
+ ASSERT(!Memory::Object_at(field)->IsFailure());
+ ASSERT(Memory::Object_at(field)->IsHeapObject());
+ *field |= kFailureTag;
+ }
+
+ private:
+ bool CheckVisitedAndUnmark(Object** field) {
+ if ((*field)->IsFailure()) {
+ intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
+ *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
+ ASSERT((*field)->IsHeapObject());
+ return true;
+ }
+ return false;
+ }
+ V8HeapExplorer* generator_;
+ HeapObject* parent_obj_;
+ int parent_;
+ int next_index_;
+};
+
+
+void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
+ HeapEntry* heap_entry = GetEntry(obj);
+ if (heap_entry == NULL) return; // No interest in this object.
+ int entry = heap_entry->index();
+
+ if (obj->IsJSGlobalProxy()) {
+ ExtractJSGlobalProxyReferences(entry, JSGlobalProxy::cast(obj));
+ } else if (obj->IsJSObject()) {
+ ExtractJSObjectReferences(entry, JSObject::cast(obj));
+ } else if (obj->IsString()) {
+ ExtractStringReferences(entry, String::cast(obj));
+ } else if (obj->IsContext()) {
+ ExtractContextReferences(entry, Context::cast(obj));
+ } else if (obj->IsMap()) {
+ ExtractMapReferences(entry, Map::cast(obj));
+ } else if (obj->IsSharedFunctionInfo()) {
+ ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj));
+ } else if (obj->IsScript()) {
+ ExtractScriptReferences(entry, Script::cast(obj));
+ } else if (obj->IsAccessorPair()) {
+ ExtractAccessorPairReferences(entry, AccessorPair::cast(obj));
+ } else if (obj->IsCodeCache()) {
+ ExtractCodeCacheReferences(entry, CodeCache::cast(obj));
+ } else if (obj->IsCode()) {
+ ExtractCodeReferences(entry, Code::cast(obj));
+ } else if (obj->IsCell()) {
+ ExtractCellReferences(entry, Cell::cast(obj));
+ } else if (obj->IsPropertyCell()) {
+ ExtractPropertyCellReferences(entry, PropertyCell::cast(obj));
+ } else if (obj->IsAllocationSite()) {
+ ExtractAllocationSiteReferences(entry, AllocationSite::cast(obj));
+ }
+ SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
+
+ // Extract unvisited fields as hidden references and restore tags
+ // of visited fields.
+ IndexedReferencesExtractor refs_extractor(this, obj, entry);
+ obj->Iterate(&refs_extractor);
+}
+
+
+void V8HeapExplorer::ExtractJSGlobalProxyReferences(
+ int entry, JSGlobalProxy* proxy) {
+ SetInternalReference(proxy, entry,
+ "native_context", proxy->native_context(),
+ JSGlobalProxy::kNativeContextOffset);
+}
+
+
+void V8HeapExplorer::ExtractJSObjectReferences(
+ int entry, JSObject* js_obj) {
+ HeapObject* obj = js_obj;
+ ExtractClosureReferences(js_obj, entry);
+ ExtractPropertyReferences(js_obj, entry);
+ ExtractElementReferences(js_obj, entry);
+ ExtractInternalReferences(js_obj, entry);
+ SetPropertyReference(
+ obj, entry, heap_->proto_string(), js_obj->GetPrototype());
+ if (obj->IsJSFunction()) {
+ JSFunction* js_fun = JSFunction::cast(js_obj);
+ Object* proto_or_map = js_fun->prototype_or_initial_map();
+ if (!proto_or_map->IsTheHole()) {
+ if (!proto_or_map->IsMap()) {
+ SetPropertyReference(
+ obj, entry,
+ heap_->prototype_string(), proto_or_map,
+ NULL,
+ JSFunction::kPrototypeOrInitialMapOffset);
+ } else {
+ SetPropertyReference(
+ obj, entry,
+ heap_->prototype_string(), js_fun->prototype());
+ SetInternalReference(
+ obj, entry, "initial_map", proto_or_map,
+ JSFunction::kPrototypeOrInitialMapOffset);
+ }
+ }
+ SharedFunctionInfo* shared_info = js_fun->shared();
+ // JSFunction has either bindings or literals and never both.
+ bool bound = shared_info->bound();
+ TagObject(js_fun->literals_or_bindings(),
+ bound ? "(function bindings)" : "(function literals)");
+ SetInternalReference(js_fun, entry,
+ bound ? "bindings" : "literals",
+ js_fun->literals_or_bindings(),
+ JSFunction::kLiteralsOffset);
+ TagObject(shared_info, "(shared function info)");
+ SetInternalReference(js_fun, entry,
+ "shared", shared_info,
+ JSFunction::kSharedFunctionInfoOffset);
+ TagObject(js_fun->context(), "(context)");
+ SetInternalReference(js_fun, entry,
+ "context", js_fun->context(),
+ JSFunction::kContextOffset);
+ for (int i = JSFunction::kNonWeakFieldsEndOffset;
+ i < JSFunction::kSize;
+ i += kPointerSize) {
+ SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i);
+ }
+ } else if (obj->IsGlobalObject()) {
+ GlobalObject* global_obj = GlobalObject::cast(obj);
+ SetInternalReference(global_obj, entry,
+ "builtins", global_obj->builtins(),
+ GlobalObject::kBuiltinsOffset);
+ SetInternalReference(global_obj, entry,
+ "native_context", global_obj->native_context(),
+ GlobalObject::kNativeContextOffset);
+ SetInternalReference(global_obj, entry,
+ "global_receiver", global_obj->global_receiver(),
+ GlobalObject::kGlobalReceiverOffset);
+ }
+ TagObject(js_obj->properties(), "(object properties)");
+ SetInternalReference(obj, entry,
+ "properties", js_obj->properties(),
+ JSObject::kPropertiesOffset);
+ TagObject(js_obj->elements(), "(object elements)");
+ SetInternalReference(obj, entry,
+ "elements", js_obj->elements(),
+ JSObject::kElementsOffset);
+}
+
+
+void V8HeapExplorer::ExtractStringReferences(int entry, String* string) {
+ if (string->IsConsString()) {
+ ConsString* cs = ConsString::cast(string);
+ SetInternalReference(cs, entry, "first", cs->first(),
+ ConsString::kFirstOffset);
+ SetInternalReference(cs, entry, "second", cs->second(),
+ ConsString::kSecondOffset);
+ } else if (string->IsSlicedString()) {
+ SlicedString* ss = SlicedString::cast(string);
+ SetInternalReference(ss, entry, "parent", ss->parent(),
+ SlicedString::kParentOffset);
+ }
+}
+
+
+void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
+ if (context == context->declaration_context()) {
+ 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;
+ SetContextReference(context, entry, local_name, context->get(idx),
+ Context::OffsetOfElementAt(idx));
+ }
+ if (scope_info->HasFunctionName()) {
+ String* name = scope_info->FunctionName();
+ VariableMode mode;
+ int idx = scope_info->FunctionContextSlotIndex(name, &mode);
+ if (idx >= 0) {
+ SetContextReference(context, entry, name, context->get(idx),
+ Context::OffsetOfElementAt(idx));
+ }
+ }
+ }
+
+#define EXTRACT_CONTEXT_FIELD(index, type, name) \
+ SetInternalReference(context, entry, #name, context->get(Context::index), \
+ FixedArray::OffsetOfElementAt(Context::index));
+ EXTRACT_CONTEXT_FIELD(CLOSURE_INDEX, JSFunction, closure);
+ EXTRACT_CONTEXT_FIELD(PREVIOUS_INDEX, Context, previous);
+ EXTRACT_CONTEXT_FIELD(EXTENSION_INDEX, Object, extension);
+ EXTRACT_CONTEXT_FIELD(GLOBAL_OBJECT_INDEX, GlobalObject, global);
+ if (context->IsNativeContext()) {
+ TagObject(context->jsfunction_result_caches(),
+ "(context func. result caches)");
+ TagObject(context->normalized_map_cache(), "(context norm. map cache)");
+ TagObject(context->runtime_context(), "(runtime context)");
+ TagObject(context->embedder_data(), "(context data)");
+ NATIVE_CONTEXT_FIELDS(EXTRACT_CONTEXT_FIELD);
+#undef EXTRACT_CONTEXT_FIELD
+ for (int i = Context::FIRST_WEAK_SLOT;
+ i < Context::NATIVE_CONTEXT_SLOTS;
+ ++i) {
+ SetWeakReference(context, entry, i, context->get(i),
+ FixedArray::OffsetOfElementAt(i));
+ }
+ }
+}
+
+
+void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
+ if (map->HasTransitionArray()) {
+ TransitionArray* transitions = map->transitions();
+ int transitions_entry = GetEntry(transitions)->index();
+ Object* back_pointer = transitions->back_pointer_storage();
+ TagObject(back_pointer, "(back pointer)");
+ SetInternalReference(transitions, transitions_entry,
+ "back_pointer", back_pointer);
+ TagObject(transitions, "(transition array)");
+ SetInternalReference(map, entry,
+ "transitions", transitions,
+ Map::kTransitionsOrBackPointerOffset);
+ } else {
+ Object* back_pointer = map->GetBackPointer();
+ TagObject(back_pointer, "(back pointer)");
+ SetInternalReference(map, entry,
+ "back_pointer", back_pointer,
+ Map::kTransitionsOrBackPointerOffset);
+ }
+ DescriptorArray* descriptors = map->instance_descriptors();
+ TagObject(descriptors, "(map descriptors)");
+ SetInternalReference(map, entry,
+ "descriptors", descriptors,
+ Map::kDescriptorsOffset);
+
+ SetInternalReference(map, entry,
+ "code_cache", map->code_cache(),
+ Map::kCodeCacheOffset);
+ SetInternalReference(map, entry,
+ "prototype", map->prototype(), Map::kPrototypeOffset);
+ SetInternalReference(map, entry,
+ "constructor", map->constructor(),
+ Map::kConstructorOffset);
+ TagObject(map->dependent_code(), "(dependent code)");
+ SetInternalReference(map, entry,
+ "dependent_code", map->dependent_code(),
+ Map::kDependentCodeOffset);
+}
+
+
+void V8HeapExplorer::ExtractSharedFunctionInfoReferences(
+ int entry, SharedFunctionInfo* shared) {
+ HeapObject* obj = shared;
+ SetInternalReference(obj, entry,
+ "name", shared->name(),
+ SharedFunctionInfo::kNameOffset);
+ TagObject(shared->code(), "(code)");
+ SetInternalReference(obj, entry,
+ "code", shared->code(),
+ SharedFunctionInfo::kCodeOffset);
+ TagObject(shared->scope_info(), "(function scope info)");
+ SetInternalReference(obj, entry,
+ "scope_info", shared->scope_info(),
+ SharedFunctionInfo::kScopeInfoOffset);
+ SetInternalReference(obj, entry,
+ "instance_class_name", shared->instance_class_name(),
+ SharedFunctionInfo::kInstanceClassNameOffset);
+ SetInternalReference(obj, entry,
+ "script", shared->script(),
+ SharedFunctionInfo::kScriptOffset);
+ TagObject(shared->construct_stub(), "(code)");
+ SetInternalReference(obj, entry,
+ "construct_stub", shared->construct_stub(),
+ SharedFunctionInfo::kConstructStubOffset);
+ SetInternalReference(obj, entry,
+ "function_data", shared->function_data(),
+ SharedFunctionInfo::kFunctionDataOffset);
+ SetInternalReference(obj, entry,
+ "debug_info", shared->debug_info(),
+ SharedFunctionInfo::kDebugInfoOffset);
+ SetInternalReference(obj, entry,
+ "inferred_name", shared->inferred_name(),
+ SharedFunctionInfo::kInferredNameOffset);
+ SetWeakReference(obj, entry,
+ 1, shared->initial_map(),
+ SharedFunctionInfo::kInitialMapOffset);
+}
+
+
+void V8HeapExplorer::ExtractScriptReferences(int entry, Script* script) {
+ HeapObject* obj = script;
+ SetInternalReference(obj, entry,
+ "source", script->source(),
+ Script::kSourceOffset);
+ SetInternalReference(obj, entry,
+ "name", script->name(),
+ Script::kNameOffset);
+ SetInternalReference(obj, entry,
+ "data", script->data(),
+ Script::kDataOffset);
+ SetInternalReference(obj, entry,
+ "context_data", script->context_data(),
+ Script::kContextOffset);
+ TagObject(script->line_ends(), "(script line ends)");
+ SetInternalReference(obj, entry,
+ "line_ends", script->line_ends(),
+ Script::kLineEndsOffset);
+}
+
+
+void V8HeapExplorer::ExtractAccessorPairReferences(
+ int entry, AccessorPair* accessors) {
+ SetInternalReference(accessors, entry, "getter", accessors->getter(),
+ AccessorPair::kGetterOffset);
+ SetInternalReference(accessors, entry, "setter", accessors->setter(),
+ AccessorPair::kSetterOffset);
+}
+
+
+void V8HeapExplorer::ExtractCodeCacheReferences(
+ int entry, CodeCache* code_cache) {
+ TagObject(code_cache->default_cache(), "(default code cache)");
+ SetInternalReference(code_cache, entry,
+ "default_cache", code_cache->default_cache(),
+ CodeCache::kDefaultCacheOffset);
+ TagObject(code_cache->normal_type_cache(), "(code type cache)");
+ SetInternalReference(code_cache, entry,
+ "type_cache", code_cache->normal_type_cache(),
+ CodeCache::kNormalTypeCacheOffset);
+}
+
+
+void V8HeapExplorer::ExtractCodeReferences(int entry, Code* code) {
+ TagObject(code->relocation_info(), "(code relocation info)");
+ SetInternalReference(code, entry,
+ "relocation_info", code->relocation_info(),
+ Code::kRelocationInfoOffset);
+ SetInternalReference(code, entry,
+ "handler_table", code->handler_table(),
+ Code::kHandlerTableOffset);
+ TagObject(code->deoptimization_data(), "(code deopt data)");
+ SetInternalReference(code, entry,
+ "deoptimization_data", code->deoptimization_data(),
+ Code::kDeoptimizationDataOffset);
+ if (code->kind() == Code::FUNCTION) {
+ SetInternalReference(code, entry,
+ "type_feedback_info", code->type_feedback_info(),
+ Code::kTypeFeedbackInfoOffset);
+ }
+ SetInternalReference(code, entry,
+ "gc_metadata", code->gc_metadata(),
+ Code::kGCMetadataOffset);
+}
+
+
+void V8HeapExplorer::ExtractCellReferences(int entry, Cell* cell) {
+ SetInternalReference(cell, entry, "value", cell->value(), Cell::kValueOffset);
+}
+
+
+void V8HeapExplorer::ExtractPropertyCellReferences(int entry,
+ PropertyCell* cell) {
+ ExtractCellReferences(entry, cell);
+ SetInternalReference(cell, entry, "type", cell->type(),
+ PropertyCell::kTypeOffset);
+ SetInternalReference(cell, entry, "dependent_code", cell->dependent_code(),
+ PropertyCell::kDependentCodeOffset);
+}
+
+
+void V8HeapExplorer::ExtractAllocationSiteReferences(int entry,
+ AllocationSite* site) {
+ SetInternalReference(site, entry, "transition_info", site->transition_info(),
+ AllocationSite::kTransitionInfoOffset);
+}
+
+
+void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, int entry) {
+ if (!js_obj->IsJSFunction()) return;
+
+ JSFunction* func = JSFunction::cast(js_obj);
+ if (func->shared()->bound()) {
+ FixedArray* bindings = func->function_bindings();
+ SetNativeBindReference(js_obj, entry, "bound_this",
+ bindings->get(JSFunction::kBoundThisIndex));
+ SetNativeBindReference(js_obj, entry, "bound_function",
+ bindings->get(JSFunction::kBoundFunctionIndex));
+ for (int i = JSFunction::kBoundArgumentsStartIndex;
+ i < bindings->length(); i++) {
+ const char* reference_name = collection_->names()->GetFormatted(
+ "bound_argument_%d",
+ i - JSFunction::kBoundArgumentsStartIndex);
+ SetNativeBindReference(js_obj, entry, reference_name,
+ bindings->get(i));
+ }
+ }
+}
+
+
+void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
+ if (js_obj->HasFastProperties()) {
+ DescriptorArray* descs = js_obj->map()->instance_descriptors();
+ int real_size = js_obj->map()->NumberOfOwnDescriptors();
+ for (int i = 0; i < real_size; i++) {
+ switch (descs->GetType(i)) {
+ case FIELD: {
+ int index = descs->GetFieldIndex(i);
+
+ Name* k = descs->GetKey(i);
+ if (index < js_obj->map()->inobject_properties()) {
+ Object* value = js_obj->InObjectPropertyAt(index);
+ if (k != heap_->hidden_string()) {
+ SetPropertyReference(
+ js_obj, entry,
+ k, value,
+ NULL,
+ js_obj->GetInObjectPropertyOffset(index));
+ } else {
+ TagObject(value, "(hidden properties)");
+ SetInternalReference(
+ js_obj, entry,
+ "hidden_properties", value,
+ js_obj->GetInObjectPropertyOffset(index));
+ }
+ } else {
+ Object* value = js_obj->RawFastPropertyAt(index);
+ if (k != heap_->hidden_string()) {
+ SetPropertyReference(js_obj, entry, k, value);
+ } else {
+ TagObject(value, "(hidden properties)");
+ SetInternalReference(js_obj, entry, "hidden_properties", value);
+ }
+ }
+ break;
+ }
+ case CONSTANT:
+ SetPropertyReference(
+ js_obj, entry,
+ descs->GetKey(i), descs->GetConstant(i));
+ break;
+ case CALLBACKS:
+ ExtractAccessorPairProperty(
+ js_obj, entry,
+ descs->GetKey(i), descs->GetValue(i));
+ 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
+ break;
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else {
+ NameDictionary* dictionary = js_obj->property_dictionary();
+ int length = dictionary->Capacity();
+ for (int i = 0; i < length; ++i) {
+ Object* k = dictionary->KeyAt(i);
+ if (dictionary->IsKey(k)) {
+ Object* target = dictionary->ValueAt(i);
+ // We assume that global objects can only have slow properties.
+ Object* value = target->IsPropertyCell()
+ ? PropertyCell::cast(target)->value()
+ : target;
+ if (k == heap_->hidden_string()) {
+ TagObject(value, "(hidden properties)");
+ SetInternalReference(js_obj, entry, "hidden_properties", value);
+ continue;
+ }
+ if (ExtractAccessorPairProperty(js_obj, entry, k, value)) continue;
+ SetPropertyReference(js_obj, entry, String::cast(k), value);
+ }
+ }
+ }
+}
+
+
+bool V8HeapExplorer::ExtractAccessorPairProperty(
+ JSObject* js_obj, int entry, Object* key, Object* callback_obj) {
+ if (!callback_obj->IsAccessorPair()) return false;
+ AccessorPair* accessors = AccessorPair::cast(callback_obj);
+ Object* getter = accessors->getter();
+ if (!getter->IsOddball()) {
+ SetPropertyReference(js_obj, entry, String::cast(key), getter, "get %s");
+ }
+ Object* setter = accessors->setter();
+ if (!setter->IsOddball()) {
+ SetPropertyReference(js_obj, entry, String::cast(key), setter, "set %s");
+ }
+ return true;
+}
+
+
+void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, int entry) {
+ if (js_obj->HasFastObjectElements()) {
+ FixedArray* elements = FixedArray::cast(js_obj->elements());
+ int length = js_obj->IsJSArray() ?
+ Smi::cast(JSArray::cast(js_obj)->length())->value() :
+ elements->length();
+ for (int i = 0; i < length; ++i) {
+ if (!elements->get(i)->IsTheHole()) {
+ SetElementReference(js_obj, entry, i, elements->get(i));
+ }
+ }
+ } else if (js_obj->HasDictionaryElements()) {
+ SeededNumberDictionary* dictionary = js_obj->element_dictionary();
+ int length = dictionary->Capacity();
+ for (int i = 0; i < length; ++i) {
+ Object* k = dictionary->KeyAt(i);
+ if (dictionary->IsKey(k)) {
+ ASSERT(k->IsNumber());
+ uint32_t index = static_cast<uint32_t>(k->Number());
+ SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
+ }
+ }
+ }
+}
+
+
+void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, int entry) {
+ int length = js_obj->GetInternalFieldCount();
+ for (int i = 0; i < length; ++i) {
+ Object* o = js_obj->GetInternalField(i);
+ SetInternalReference(
+ js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i));
+ }
+}
+
+
+String* V8HeapExplorer::GetConstructorName(JSObject* object) {
+ Heap* heap = object->GetHeap();
+ if (object->IsJSFunction()) return heap->closure_string();
+ String* constructor_name = object->constructor_name();
+ if (constructor_name == heap->Object_string()) {
+ // 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(heap->isolate());
+ object->LocalLookupRealNamedProperty(heap->constructor_string(), &result);
+ if (!result.IsFound()) return object->constructor_name();
+
+ constructor_prop = result.GetLazyValue();
+ if (constructor_prop->IsJSFunction()) {
+ Object* maybe_name =
+ JSFunction::cast(constructor_prop)->shared()->name();
+ if (maybe_name->IsString()) {
+ String* name = String::cast(maybe_name);
+ if (name->length() > 0) return name;
+ }
+ }
+ }
+ return object->constructor_name();
+}
+
+
+HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
+ if (!obj->IsHeapObject()) return NULL;
+ return filler_->FindOrAddEntry(obj, this);
+}
+
+
+class RootsReferencesExtractor : public ObjectVisitor {
+ private:
+ struct IndexTag {
+ IndexTag(int index, VisitorSynchronization::SyncTag tag)
+ : index(index), tag(tag) { }
+ int index;
+ VisitorSynchronization::SyncTag tag;
+ };
+
+ public:
+ RootsReferencesExtractor()
+ : collecting_all_references_(false),
+ previous_reference_count_(0) {
+ }
+
+ void VisitPointers(Object** start, Object** end) {
+ 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:
+ 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) {
+ HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
+
+ filler_ = filler;
+ bool interrupted = false;
+
+ // Heap iteration with filtering must be finished in any case.
+ for (HeapObject* obj = iterator.next();
+ obj != NULL;
+ obj = iterator.next(), progress_->ProgressStep()) {
+ if (!interrupted) {
+ ExtractReferences(obj);
+ if (!progress_->ProgressReport(false)) interrupted = true;
+ }
+ }
+ if (interrupted) {
+ filler_ = NULL;
+ return false;
+ }
+
+ SetRootGcRootsReference();
+ RootsReferencesExtractor extractor;
+ heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG);
+ extractor.SetCollectingAllReferences();
+ heap_->IterateRoots(&extractor, VISIT_ALL);
+ extractor.FillReferences(this);
+ filler_ = NULL;
+ return progress_->ProgressReport(true);
+}
+
+
+bool V8HeapExplorer::IsEssentialObject(Object* object) {
+ return object->IsHeapObject()
+ && !object->IsOddball()
+ && object != heap_->empty_byte_array()
+ && object != heap_->empty_fixed_array()
+ && object != heap_->empty_descriptor_array()
+ && object != heap_->fixed_array_map()
+ && object != heap_->cell_map()
+ && object != heap_->global_property_cell_map()
+ && object != heap_->shared_function_info_map()
+ && object != heap_->free_space_map()
+ && object != heap_->one_pointer_filler_map()
+ && object != heap_->two_pointer_filler_map();
+}
+
+
+void V8HeapExplorer::SetContextReference(HeapObject* parent_obj,
+ int parent_entry,
+ String* reference_name,
+ Object* child_obj,
+ int field_offset) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry != NULL) {
+ filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
+ parent_entry,
+ collection_->names()->GetName(reference_name),
+ child_entry);
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
+ }
+}
+
+
+void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj,
+ int parent_entry,
+ const char* reference_name,
+ Object* child_obj) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry != NULL) {
+ filler_->SetNamedReference(HeapGraphEdge::kShortcut,
+ parent_entry,
+ reference_name,
+ child_entry);
+ }
+}
+
+
+void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
+ int parent_entry,
+ int index,
+ Object* child_obj) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry != NULL) {
+ filler_->SetIndexedReference(HeapGraphEdge::kElement,
+ parent_entry,
+ index,
+ child_entry);
+ }
+}
+
+
+void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
+ int parent_entry,
+ const char* reference_name,
+ Object* child_obj,
+ int field_offset) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry == NULL) return;
+ if (IsEssentialObject(child_obj)) {
+ filler_->SetNamedReference(HeapGraphEdge::kInternal,
+ parent_entry,
+ reference_name,
+ child_entry);
+ }
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
+}
+
+
+void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
+ int parent_entry,
+ int index,
+ Object* child_obj,
+ int field_offset) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry == NULL) return;
+ if (IsEssentialObject(child_obj)) {
+ filler_->SetNamedReference(HeapGraphEdge::kInternal,
+ parent_entry,
+ collection_->names()->GetName(index),
+ child_entry);
+ }
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
+}
+
+
+void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
+ int parent_entry,
+ int index,
+ Object* child_obj) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry != NULL && IsEssentialObject(child_obj)) {
+ filler_->SetIndexedReference(HeapGraphEdge::kHidden,
+ parent_entry,
+ index,
+ child_entry);
+ }
+}
+
+
+void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
+ int parent_entry,
+ int index,
+ Object* child_obj,
+ int field_offset) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry == NULL) return;
+ if (IsEssentialObject(child_obj)) {
+ filler_->SetIndexedReference(HeapGraphEdge::kWeak,
+ parent_entry,
+ index,
+ child_entry);
+ }
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
+}
+
+
+void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
+ int parent_entry,
+ Name* reference_name,
+ Object* child_obj,
+ const char* name_format_string,
+ int field_offset) {
+ ASSERT(parent_entry == GetEntry(parent_obj)->index());
+ HeapEntry* child_entry = GetEntry(child_obj);
+ if (child_entry != NULL) {
+ HeapGraphEdge::Type type =
+ reference_name->IsSymbol() || String::cast(reference_name)->length() > 0
+ ? HeapGraphEdge::kProperty : HeapGraphEdge::kInternal;
+ const char* name = name_format_string != NULL && reference_name->IsString()
+ ? collection_->names()->GetFormatted(
+ name_format_string,
+ *String::cast(reference_name)->ToCString(
+ DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)) :
+ collection_->names()->GetName(reference_name);
+
+ filler_->SetNamedReference(type,
+ parent_entry,
+ name,
+ child_entry);
+ IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
+ }
+}
+
+
+void V8HeapExplorer::SetRootGcRootsReference() {
+ filler_->SetIndexedAutoIndexReference(
+ HeapGraphEdge::kElement,
+ snapshot_->root()->index(),
+ snapshot_->gc_roots());
+}
+
+
+void V8HeapExplorer::SetUserGlobalReference(Object* child_obj) {
+ HeapEntry* child_entry = GetEntry(child_obj);
+ ASSERT(child_entry != NULL);
+ filler_->SetNamedAutoIndexReference(
+ HeapGraphEdge::kShortcut,
+ snapshot_->root()->index(),
+ child_entry);
+}
+
+
+void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) {
+ filler_->SetIndexedAutoIndexReference(
+ HeapGraphEdge::kElement,
+ snapshot_->gc_roots()->index(),
+ 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) {
+ const char* name = GetStrongGcSubrootName(child_obj);
+ if (name != NULL) {
+ filler_->SetNamedReference(
+ HeapGraphEdge::kInternal,
+ snapshot_->gc_subroot(tag)->index(),
+ name,
+ child_entry);
+ } else {
+ filler_->SetIndexedAutoIndexReference(
+ is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement,
+ snapshot_->gc_subroot(tag)->index(),
+ child_entry);
+ }
+
+ // Add a shortcut to JS global object reference at snapshot root.
+ if (child_obj->IsNativeContext()) {
+ Context* context = Context::cast(child_obj);
+ GlobalObject* global = context->global_object();
+ if (global->IsJSGlobalObject()) {
+ bool is_debug_object = false;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ is_debug_object = heap_->isolate()->debug()->IsDebugGlobal(global);
+#endif
+ if (!is_debug_object && !user_roots_.Contains(global)) {
+ user_roots_.Insert(global);
+ SetUserGlobalReference(global);
+ }
+ }
+ }
+ }
+}
+
+
+const char* V8HeapExplorer::GetStrongGcSubrootName(Object* object) {
+ if (strong_gc_subroot_names_.is_empty()) {
+#define NAME_ENTRY(name) strong_gc_subroot_names_.SetTag(heap_->name(), #name);
+#define ROOT_NAME(type, name, camel_name) NAME_ENTRY(name)
+ STRONG_ROOT_LIST(ROOT_NAME)
+#undef ROOT_NAME
+#define STRUCT_MAP_NAME(NAME, Name, name) NAME_ENTRY(name##_map)
+ STRUCT_LIST(STRUCT_MAP_NAME)
+#undef STRUCT_MAP_NAME
+#define STRING_NAME(name, str) NAME_ENTRY(name)
+ INTERNALIZED_STRING_LIST(STRING_NAME)
+#undef STRING_NAME
+#undef NAME_ENTRY
+ CHECK(!strong_gc_subroot_names_.is_empty());
+ }
+ return strong_gc_subroot_names_.GetTag(object);
+}
+
+
+void V8HeapExplorer::TagObject(Object* obj, const char* tag) {
+ if (IsEssentialObject(obj)) {
+ HeapEntry* entry = GetEntry(obj);
+ if (entry->name()[0] == '\0') {
+ entry->set_name(tag);
+ }
+ }
+}
+
+
+class GlobalObjectsEnumerator : public ObjectVisitor {
+ public:
+ virtual void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) {
+ if ((*p)->IsNativeContext()) {
+ Context* context = Context::cast(*p);
+ JSObject* proxy = context->global_proxy();
+ if (proxy->IsJSGlobalProxy()) {
+ Object* global = proxy->map()->prototype();
+ if (global->IsJSGlobalObject()) {
+ objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global)));
+ }
+ }
+ }
+ }
+ }
+ int count() { return objects_.length(); }
+ Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
+
+ private:
+ List<Handle<JSGlobalObject> > objects_;
+};
+
+
+// Modifies heap. Must not be run during heap traversal.
+void V8HeapExplorer::TagGlobalObjects() {
+ Isolate* isolate = Isolate::Current();
+ HandleScope scope(isolate);
+ GlobalObjectsEnumerator enumerator;
+ isolate->global_handles()->IterateAllRoots(&enumerator);
+ const char** urls = NewArray<const char*>(enumerator.count());
+ for (int i = 0, l = enumerator.count(); i < l; ++i) {
+ if (global_object_name_resolver_) {
+ HandleScope scope(isolate);
+ Handle<JSGlobalObject> global_obj = enumerator.at(i);
+ urls[i] = global_object_name_resolver_->GetName(
+ Utils::ToLocal(Handle<JSObject>::cast(global_obj)));
+ } else {
+ urls[i] = NULL;
+ }
+ }
+
+ DisallowHeapAllocation no_allocation;
+ for (int i = 0, l = enumerator.count(); i < l; ++i) {
+ objects_tags_.SetTag(*enumerator.at(i), urls[i]);
+ }
+
+ DeleteArray(urls);
+}
+
+
+class GlobalHandlesExtractor : public ObjectVisitor {
+ public:
+ explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
+ : explorer_(explorer) {}
+ virtual ~GlobalHandlesExtractor() {}
+ virtual void VisitPointers(Object** start, Object** end) {
+ UNREACHABLE();
+ }
+ virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
+ explorer_->VisitSubtreeWrapper(p, class_id);
+ }
+ private:
+ NativeObjectsExplorer* explorer_;
+};
+
+
+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);
+ private:
+ HeapSnapshot* snapshot_;
+ HeapSnapshotsCollection* collection_;
+ HeapEntry::Type entries_type_;
+};
+
+
+HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(HeapThing ptr) {
+ v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
+ intptr_t elements = info->GetElementCount();
+ intptr_t size = info->GetSizeInBytes();
+ const char* name = elements != -1
+ ? collection_->names()->GetFormatted(
+ "%s / %" V8_PTR_PREFIX "d entries", info->GetLabel(), elements)
+ : collection_->names()->GetCopy(info->GetLabel());
+ return snapshot_->AddEntry(
+ entries_type_,
+ name,
+ HeapObjectsMap::GenerateId(info),
+ size != -1 ? static_cast<int>(size) : 0);
+}
+
+
+NativeObjectsExplorer::NativeObjectsExplorer(
+ HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
+ : snapshot_(snapshot),
+ collection_(snapshot_->collection()),
+ 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);
+}
+
+
+NativeObjectsExplorer::~NativeObjectsExplorer() {
+ 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);
+ info->Dispose();
+ List<HeapObject*>* objects =
+ reinterpret_cast<List<HeapObject*>* >(p->value);
+ delete objects;
+ }
+ for (HashMap::Entry* p = native_groups_.Start();
+ p != NULL;
+ p = native_groups_.Next(p)) {
+ v8::RetainedObjectInfo* info =
+ reinterpret_cast<v8::RetainedObjectInfo*>(p->value);
+ info->Dispose();
+ }
+ delete synthetic_entries_allocator_;
+ delete native_entries_allocator_;
+}
+
+
+int NativeObjectsExplorer::EstimateObjectsCount() {
+ FillRetainedObjects();
+ return objects_by_info_.occupancy();
+}
+
+
+void NativeObjectsExplorer::FillRetainedObjects() {
+ if (embedder_queried_) return;
+ Isolate* isolate = Isolate::Current();
+ const GCType major_gc_type = kGCTypeMarkSweepCompact;
+ // Record objects that are joined into ObjectGroups.
+ isolate->heap()->CallGCPrologueCallbacks(
+ major_gc_type, kGCCallbackFlagConstructRetainedObjectInfos);
+ List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
+ for (int i = 0; i < groups->length(); ++i) {
+ ObjectGroup* group = groups->at(i);
+ if (group->info == NULL) continue;
+ List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info);
+ for (size_t j = 0; j < group->length; ++j) {
+ HeapObject* obj = HeapObject::cast(*group->objects[j]);
+ list->Add(obj);
+ in_groups_.Insert(obj);
+ }
+ group->info = NULL; // Acquire info object ownership.
+ }
+ isolate->global_handles()->RemoveObjectGroups();
+ isolate->heap()->CallGCEpilogueCallbacks(major_gc_type);
+ // Record objects that are not in ObjectGroups, but have class ID.
+ GlobalHandlesExtractor extractor(this);
+ isolate->global_handles()->IterateAllRootsWithClassIds(&extractor);
+ 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;
+ int parent_entry =
+ filler_->FindOrAddEntry(parent, native_entries_allocator_)->index();
+ ASSERT(parent_entry != HeapEntry::kNoEntry);
+ 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_entry,
+ "native",
+ child_entry);
+ }
+ }
+ isolate->global_handles()->RemoveImplicitRefGroups();
+}
+
+List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
+ v8::RetainedObjectInfo* info) {
+ HashMap::Entry* entry =
+ objects_by_info_.Lookup(info, InfoHash(info), true);
+ if (entry->value != NULL) {
+ info->Dispose();
+ } else {
+ entry->value = new List<HeapObject*>(4);
+ }
+ return reinterpret_cast<List<HeapObject*>* >(entry->value);
+}
+
+
+bool NativeObjectsExplorer::IterateAndExtractReferences(
+ SnapshotFillerInterface* filler) {
+ filler_ = filler;
+ FillRetainedObjects();
+ 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();
+ }
+ 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 = StringHasher::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, native_entries_allocator_);
+ ASSERT(child_entry != NULL);
+ NativeGroupRetainedObjectInfo* group_info =
+ FindOrAddGroupInfo(info->GetGroupLabel());
+ HeapEntry* group_entry =
+ filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_);
+ filler_->SetNamedAutoIndexReference(
+ HeapGraphEdge::kInternal,
+ group_entry->index(),
+ child_entry);
+}
+
+
+void NativeObjectsExplorer::SetWrapperNativeReferences(
+ HeapObject* wrapper, v8::RetainedObjectInfo* info) {
+ HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
+ ASSERT(wrapper_entry != NULL);
+ HeapEntry* info_entry =
+ filler_->FindOrAddEntry(info, native_entries_allocator_);
+ ASSERT(info_entry != NULL);
+ filler_->SetNamedReference(HeapGraphEdge::kInternal,
+ wrapper_entry->index(),
+ "native",
+ info_entry);
+ filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
+ info_entry->index(),
+ wrapper_entry);
+}
+
+
+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,
+ snapshot_->root()->index(),
+ group_entry);
+ }
+}
+
+
+void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
+ if (in_groups_.Contains(*p)) return;
+ Isolate* isolate = Isolate::Current();
+ v8::RetainedObjectInfo* info =
+ isolate->heap_profiler()->ExecuteWrapperClassCallback(class_id, p);
+ if (info == NULL) return;
+ GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
+}
+
+
+class SnapshotFiller : public SnapshotFillerInterface {
+ public:
+ explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
+ : snapshot_(snapshot),
+ collection_(snapshot->collection()),
+ entries_(entries) { }
+ HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+ HeapEntry* entry = allocator->AllocateEntry(ptr);
+ entries_->Pair(ptr, entry->index());
+ return entry;
+ }
+ HeapEntry* FindEntry(HeapThing ptr) {
+ int index = entries_->Map(ptr);
+ return index != HeapEntry::kNoEntry ? &snapshot_->entries()[index] : NULL;
+ }
+ HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+ HeapEntry* entry = FindEntry(ptr);
+ return entry != NULL ? entry : AddEntry(ptr, allocator);
+ }
+ void SetIndexedReference(HeapGraphEdge::Type type,
+ int parent,
+ int index,
+ HeapEntry* child_entry) {
+ HeapEntry* parent_entry = &snapshot_->entries()[parent];
+ parent_entry->SetIndexedReference(type, index, child_entry);
+ }
+ void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent,
+ HeapEntry* child_entry) {
+ HeapEntry* parent_entry = &snapshot_->entries()[parent];
+ int index = parent_entry->children_count() + 1;
+ parent_entry->SetIndexedReference(type, index, child_entry);
+ }
+ void SetNamedReference(HeapGraphEdge::Type type,
+ int parent,
+ const char* reference_name,
+ HeapEntry* child_entry) {
+ HeapEntry* parent_entry = &snapshot_->entries()[parent];
+ parent_entry->SetNamedReference(type, reference_name, child_entry);
+ }
+ void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent,
+ HeapEntry* child_entry) {
+ HeapEntry* parent_entry = &snapshot_->entries()[parent];
+ int index = parent_entry->children_count() + 1;
+ parent_entry->SetNamedReference(
+ type,
+ collection_->names()->GetName(index),
+ child_entry);
+ }
+
+ private:
+ HeapSnapshot* snapshot_;
+ HeapSnapshotsCollection* collection_;
+ HeapEntriesMap* entries_;
+};
+
+
+HeapSnapshotGenerator::HeapSnapshotGenerator(
+ HeapSnapshot* snapshot,
+ v8::ActivityControl* control,
+ v8::HeapProfiler::ObjectNameResolver* resolver,
+ Heap* heap)
+ : snapshot_(snapshot),
+ control_(control),
+ v8_heap_explorer_(snapshot_, this, resolver),
+ dom_explorer_(snapshot_, this),
+ heap_(heap) {
+}
+
+
+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 VERIFY_HEAP
+ Heap* debug_heap = Isolate::Current()->heap();
+ CHECK(!debug_heap->old_data_space()->was_swept_conservatively());
+ CHECK(!debug_heap->old_pointer_space()->was_swept_conservatively());
+ CHECK(!debug_heap->code_space()->was_swept_conservatively());
+ CHECK(!debug_heap->cell_space()->was_swept_conservatively());
+ CHECK(!debug_heap->property_cell_space()->
+ was_swept_conservatively());
+ CHECK(!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.
+ DisallowHeapAllocation no_alloc;
+
+#ifdef VERIFY_HEAP
+ debug_heap->Verify();
+#endif
+
+ SetProgressTotal(1); // 1 pass.
+
+#ifdef VERIFY_HEAP
+ debug_heap->Verify();
+#endif
+
+ if (!FillReferences()) return false;
+
+ snapshot_->FillChildren();
+ snapshot_->RememberLastJSObjectId();
+
+ progress_counter_ = progress_total_;
+ if (!ProgressReport(true)) return false;
+ return true;
+}
+
+
+void HeapSnapshotGenerator::ProgressStep() {
+ ++progress_counter_;
+}
+
+
+bool HeapSnapshotGenerator::ProgressReport(bool force) {
+ const int kProgressReportGranularity = 10000;
+ if (control_ != NULL
+ && (force || progress_counter_ % kProgressReportGranularity == 0)) {
+ return
+ control_->ReportProgressValue(progress_counter_, progress_total_) ==
+ v8::ActivityControl::kContinue;
+ }
+ return true;
+}
+
+
+void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
+ if (control_ == NULL) return;
+ HeapIterator iterator(heap_, HeapIterator::kFilterUnreachable);
+ progress_total_ = iterations_count * (
+ v8_heap_explorer_.EstimateObjectsCount(&iterator) +
+ dom_explorer_.EstimateObjectsCount());
+ progress_counter_ = 0;
+}
+
+
+bool HeapSnapshotGenerator::FillReferences() {
+ SnapshotFiller filler(snapshot_, &entries_);
+ v8_heap_explorer_.AddRootEntries(&filler);
+ return v8_heap_explorer_.IterateAndExtractReferences(&filler)
+ && dom_explorer_.IterateAndExtractReferences(&filler);
+}
+
+
+template<int bytes> struct MaxDecimalDigitsIn;
+template<> struct MaxDecimalDigitsIn<4> {
+ static const int kSigned = 11;
+ static const int kUnsigned = 10;
+};
+template<> struct MaxDecimalDigitsIn<8> {
+ static const int kSigned = 20;
+ static const int kUnsigned = 20;
+};
+
+
+class OutputStreamWriter {
+ public:
+ explicit OutputStreamWriter(v8::OutputStream* stream)
+ : stream_(stream),
+ chunk_size_(stream->GetChunkSize()),
+ chunk_(chunk_size_),
+ chunk_pos_(0),
+ aborted_(false) {
+ ASSERT(chunk_size_ > 0);
+ }
+ bool aborted() { return aborted_; }
+ void AddCharacter(char c) {
+ ASSERT(c != '\0');
+ ASSERT(chunk_pos_ < chunk_size_);
+ chunk_[chunk_pos_++] = c;
+ MaybeWriteChunk();
+ }
+ void AddString(const char* s) {
+ AddSubstring(s, StrLength(s));
+ }
+ void AddSubstring(const char* s, int n) {
+ if (n <= 0) return;
+ ASSERT(static_cast<size_t>(n) <= strlen(s));
+ const char* s_end = s + n;
+ while (s < s_end) {
+ int s_chunk_size = Min(
+ chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
+ ASSERT(s_chunk_size > 0);
+ OS::MemCopy(chunk_.start() + chunk_pos_, s, s_chunk_size);
+ s += s_chunk_size;
+ chunk_pos_ += s_chunk_size;
+ MaybeWriteChunk();
+ }
+ }
+ void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
+ void Finalize() {
+ if (aborted_) return;
+ ASSERT(chunk_pos_ < chunk_size_);
+ if (chunk_pos_ != 0) {
+ WriteChunk();
+ }
+ stream_->EndOfStream();
+ }
+
+ private:
+ template<typename T>
+ void AddNumberImpl(T n, const char* format) {
+ // Buffer for the longest value plus trailing \0
+ static const int kMaxNumberSize =
+ MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
+ if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
+ int result = OS::SNPrintF(
+ chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
+ ASSERT(result != -1);
+ chunk_pos_ += result;
+ MaybeWriteChunk();
+ } else {
+ EmbeddedVector<char, kMaxNumberSize> buffer;
+ int result = OS::SNPrintF(buffer, format, n);
+ USE(result);
+ ASSERT(result != -1);
+ AddString(buffer.start());
+ }
+ }
+ void MaybeWriteChunk() {
+ ASSERT(chunk_pos_ <= chunk_size_);
+ if (chunk_pos_ == chunk_size_) {
+ WriteChunk();
+ }
+ }
+ void WriteChunk() {
+ if (aborted_) return;
+ if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
+ v8::OutputStream::kAbort) aborted_ = true;
+ chunk_pos_ = 0;
+ }
+
+ v8::OutputStream* stream_;
+ int chunk_size_;
+ ScopedVector<char> chunk_;
+ int chunk_pos_;
+ bool aborted_;
+};
+
+
+// type, name|index, to_node.
+const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
+// type, name, id, self_size, children_index.
+const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 5;
+
+void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
+ ASSERT(writer_ == NULL);
+ writer_ = new OutputStreamWriter(stream);
+ SerializeImpl();
+ delete writer_;
+ writer_ = NULL;
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeImpl() {
+ ASSERT(0 == snapshot_->root()->index());
+ writer_->AddCharacter('{');
+ writer_->AddString("\"snapshot\":{");
+ SerializeSnapshot();
+ if (writer_->aborted()) return;
+ writer_->AddString("},\n");
+ writer_->AddString("\"nodes\":[");
+ SerializeNodes();
+ if (writer_->aborted()) return;
+ writer_->AddString("],\n");
+ writer_->AddString("\"edges\":[");
+ SerializeEdges();
+ if (writer_->aborted()) return;
+ writer_->AddString("],\n");
+ writer_->AddString("\"strings\":[");
+ SerializeStrings();
+ if (writer_->aborted()) return;
+ writer_->AddCharacter(']');
+ writer_->AddCharacter('}');
+ writer_->Finalize();
+}
+
+
+int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
+ HashMap::Entry* cache_entry = strings_.Lookup(
+ const_cast<char*>(s), ObjectHash(s), true);
+ if (cache_entry->value == NULL) {
+ cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
+ }
+ return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
+}
+
+
+static int utoa(unsigned value, const Vector<char>& buffer, int buffer_pos) {
+ int number_of_digits = 0;
+ unsigned t = value;
+ do {
+ ++number_of_digits;
+ } while (t /= 10);
+
+ buffer_pos += number_of_digits;
+ int result = buffer_pos;
+ do {
+ int last_digit = value % 10;
+ buffer[--buffer_pos] = '0' + last_digit;
+ value /= 10;
+ } while (value);
+ return result;
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
+ bool first_edge) {
+ // The buffer needs space for 3 unsigned ints, 3 commas, \n and \0
+ static const int kBufferSize =
+ MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 3 + 3 + 2; // NOLINT
+ EmbeddedVector<char, kBufferSize> buffer;
+ int edge_name_or_index = edge->type() == HeapGraphEdge::kElement
+ || edge->type() == HeapGraphEdge::kHidden
+ || edge->type() == HeapGraphEdge::kWeak
+ ? edge->index() : GetStringId(edge->name());
+ int buffer_pos = 0;
+ if (!first_edge) {
+ buffer[buffer_pos++] = ',';
+ }
+ buffer_pos = utoa(edge->type(), buffer, buffer_pos);
+ buffer[buffer_pos++] = ',';
+ buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos);
+ buffer[buffer_pos++] = ',';
+ buffer_pos = utoa(entry_index(edge->to()), buffer, buffer_pos);
+ buffer[buffer_pos++] = '\n';
+ buffer[buffer_pos++] = '\0';
+ writer_->AddString(buffer.start());
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeEdges() {
+ List<HeapGraphEdge*>& edges = snapshot_->children();
+ for (int i = 0; i < edges.length(); ++i) {
+ ASSERT(i == 0 ||
+ edges[i - 1]->from()->index() <= edges[i]->from()->index());
+ SerializeEdge(edges[i], i == 0);
+ if (writer_->aborted()) return;
+ }
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
+ // The buffer needs space for 5 unsigned ints, 5 commas, \n and \0
+ static const int kBufferSize =
+ 5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
+ + 5 + 1 + 1;
+ EmbeddedVector<char, kBufferSize> buffer;
+ int buffer_pos = 0;
+ if (entry_index(entry) != 0) {
+ buffer[buffer_pos++] = ',';
+ }
+ buffer_pos = utoa(entry->type(), buffer, buffer_pos);
+ buffer[buffer_pos++] = ',';
+ buffer_pos = utoa(GetStringId(entry->name()), buffer, buffer_pos);
+ buffer[buffer_pos++] = ',';
+ buffer_pos = utoa(entry->id(), buffer, buffer_pos);
+ buffer[buffer_pos++] = ',';
+ buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
+ buffer[buffer_pos++] = ',';
+ buffer_pos = utoa(entry->children_count(), buffer, buffer_pos);
+ buffer[buffer_pos++] = '\n';
+ buffer[buffer_pos++] = '\0';
+ writer_->AddString(buffer.start());
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeNodes() {
+ List<HeapEntry>& entries = snapshot_->entries();
+ for (int i = 0; i < entries.length(); ++i) {
+ SerializeNode(&entries[i]);
+ if (writer_->aborted()) return;
+ }
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeSnapshot() {
+ writer_->AddString("\"title\":\"");
+ writer_->AddString(snapshot_->title());
+ writer_->AddString("\"");
+ writer_->AddString(",\"uid\":");
+ writer_->AddNumber(snapshot_->uid());
+ writer_->AddString(",\"meta\":");
+ // The object describing node serialization layout.
+ // We use a set of macros to improve readability.
+#define JSON_A(s) "[" s "]"
+#define JSON_O(s) "{" s "}"
+#define JSON_S(s) "\"" s "\""
+ writer_->AddString(JSON_O(
+ JSON_S("node_fields") ":" JSON_A(
+ JSON_S("type") ","
+ JSON_S("name") ","
+ JSON_S("id") ","
+ JSON_S("self_size") ","
+ JSON_S("edge_count")) ","
+ JSON_S("node_types") ":" JSON_A(
+ JSON_A(
+ JSON_S("hidden") ","
+ JSON_S("array") ","
+ JSON_S("string") ","
+ JSON_S("object") ","
+ JSON_S("code") ","
+ JSON_S("closure") ","
+ JSON_S("regexp") ","
+ JSON_S("number") ","
+ JSON_S("native") ","
+ JSON_S("synthetic")) ","
+ JSON_S("string") ","
+ JSON_S("number") ","
+ JSON_S("number") ","
+ JSON_S("number") ","
+ JSON_S("number") ","
+ JSON_S("number")) ","
+ JSON_S("edge_fields") ":" JSON_A(
+ JSON_S("type") ","
+ JSON_S("name_or_index") ","
+ JSON_S("to_node")) ","
+ JSON_S("edge_types") ":" JSON_A(
+ JSON_A(
+ JSON_S("context") ","
+ JSON_S("element") ","
+ JSON_S("property") ","
+ JSON_S("internal") ","
+ JSON_S("hidden") ","
+ JSON_S("shortcut") ","
+ JSON_S("weak")) ","
+ JSON_S("string_or_number") ","
+ JSON_S("node"))));
+#undef JSON_S
+#undef JSON_O
+#undef JSON_A
+ writer_->AddString(",\"node_count\":");
+ writer_->AddNumber(snapshot_->entries().length());
+ writer_->AddString(",\"edge_count\":");
+ writer_->AddNumber(snapshot_->edges().length());
+}
+
+
+static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
+ static const char hex_chars[] = "0123456789ABCDEF";
+ w->AddString("\\u");
+ w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
+ w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
+ w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
+ w->AddCharacter(hex_chars[u & 0xf]);
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
+ writer_->AddCharacter('\n');
+ writer_->AddCharacter('\"');
+ for ( ; *s != '\0'; ++s) {
+ switch (*s) {
+ case '\b':
+ writer_->AddString("\\b");
+ continue;
+ case '\f':
+ writer_->AddString("\\f");
+ continue;
+ case '\n':
+ writer_->AddString("\\n");
+ continue;
+ case '\r':
+ writer_->AddString("\\r");
+ continue;
+ case '\t':
+ writer_->AddString("\\t");
+ continue;
+ case '\"':
+ case '\\':
+ writer_->AddCharacter('\\');
+ writer_->AddCharacter(*s);
+ continue;
+ default:
+ if (*s > 31 && *s < 128) {
+ writer_->AddCharacter(*s);
+ } else if (*s <= 31) {
+ // Special character with no dedicated literal.
+ WriteUChar(writer_, *s);
+ } else {
+ // Convert UTF-8 into \u UTF-16 literal.
+ unsigned length = 1, cursor = 0;
+ for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
+ unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
+ if (c != unibrow::Utf8::kBadChar) {
+ WriteUChar(writer_, c);
+ ASSERT(cursor != 0);
+ s += cursor - 1;
+ } else {
+ writer_->AddCharacter('?');
+ }
+ }
+ }
+ }
+ writer_->AddCharacter('\"');
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeStrings() {
+ List<HashMap::Entry*> sorted_strings;
+ SortHashMap(&strings_, &sorted_strings);
+ writer_->AddString("\"<dummy>\"");
+ for (int i = 0; i < sorted_strings.length(); ++i) {
+ writer_->AddCharacter(',');
+ SerializeString(
+ reinterpret_cast<const unsigned char*>(sorted_strings[i]->key));
+ if (writer_->aborted()) return;
+ }
+}
+
+
+template<typename T>
+inline static int SortUsingEntryValue(const T* x, const T* y) {
+ uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value);
+ uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value);
+ if (x_uint > y_uint) {
+ return 1;
+ } else if (x_uint == y_uint) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+
+void HeapSnapshotJSONSerializer::SortHashMap(
+ HashMap* map, List<HashMap::Entry*>* sorted_entries) {
+ for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p))
+ sorted_entries->Add(p);
+ sorted_entries->Sort(SortUsingEntryValue);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/heap-snapshot-generator.h b/chromium/v8/src/heap-snapshot-generator.h
new file mode 100644
index 00000000000..cea995820f7
--- /dev/null
+++ b/chromium/v8/src/heap-snapshot-generator.h
@@ -0,0 +1,677 @@
+// Copyright 2013 the V8 project authors. 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_HEAP_SNAPSHOT_GENERATOR_H_
+#define V8_HEAP_SNAPSHOT_GENERATOR_H_
+
+#include "profile-generator-inl.h"
+
+namespace v8 {
+namespace internal {
+
+class HeapEntry;
+class HeapSnapshot;
+
+class HeapGraphEdge BASE_EMBEDDED {
+ public:
+ enum Type {
+ kContextVariable = v8::HeapGraphEdge::kContextVariable,
+ kElement = v8::HeapGraphEdge::kElement,
+ kProperty = v8::HeapGraphEdge::kProperty,
+ kInternal = v8::HeapGraphEdge::kInternal,
+ kHidden = v8::HeapGraphEdge::kHidden,
+ kShortcut = v8::HeapGraphEdge::kShortcut,
+ kWeak = v8::HeapGraphEdge::kWeak
+ };
+
+ HeapGraphEdge() { }
+ HeapGraphEdge(Type type, const char* name, int from, int to);
+ HeapGraphEdge(Type type, int index, int from, int to);
+ void ReplaceToIndexWithEntry(HeapSnapshot* snapshot);
+
+ Type type() const { return static_cast<Type>(type_); }
+ int index() const {
+ ASSERT(type_ == kElement || type_ == kHidden || type_ == kWeak);
+ return index_;
+ }
+ const char* name() const {
+ ASSERT(type_ == kContextVariable
+ || type_ == kProperty
+ || type_ == kInternal
+ || type_ == kShortcut);
+ return name_;
+ }
+ INLINE(HeapEntry* from() const);
+ HeapEntry* to() const { return to_entry_; }
+
+ private:
+ INLINE(HeapSnapshot* snapshot() const);
+
+ unsigned type_ : 3;
+ int from_index_ : 29;
+ union {
+ // During entries population |to_index_| is used for storing the index,
+ // afterwards it is replaced with a pointer to the entry.
+ int to_index_;
+ HeapEntry* to_entry_;
+ };
+ union {
+ int index_;
+ const char* name_;
+ };
+};
+
+
+// HeapEntry instances represent an entity from the heap (or a special
+// virtual node, e.g. root).
+class HeapEntry BASE_EMBEDDED {
+ public:
+ enum Type {
+ kHidden = v8::HeapGraphNode::kHidden,
+ kArray = v8::HeapGraphNode::kArray,
+ kString = v8::HeapGraphNode::kString,
+ kObject = v8::HeapGraphNode::kObject,
+ kCode = v8::HeapGraphNode::kCode,
+ kClosure = v8::HeapGraphNode::kClosure,
+ kRegExp = v8::HeapGraphNode::kRegExp,
+ kHeapNumber = v8::HeapGraphNode::kHeapNumber,
+ kNative = v8::HeapGraphNode::kNative,
+ kSynthetic = v8::HeapGraphNode::kSynthetic
+ };
+ static const int kNoEntry;
+
+ HeapEntry() { }
+ HeapEntry(HeapSnapshot* snapshot,
+ Type type,
+ const char* name,
+ SnapshotObjectId id,
+ int self_size);
+
+ HeapSnapshot* snapshot() { return snapshot_; }
+ Type type() { return static_cast<Type>(type_); }
+ const char* name() { return name_; }
+ void set_name(const char* name) { name_ = name; }
+ inline SnapshotObjectId id() { return id_; }
+ int self_size() { return self_size_; }
+ INLINE(int index() const);
+ int children_count() const { return children_count_; }
+ INLINE(int set_children_index(int index));
+ void add_child(HeapGraphEdge* edge) {
+ children_arr()[children_count_++] = edge;
+ }
+ Vector<HeapGraphEdge*> children() {
+ return Vector<HeapGraphEdge*>(children_arr(), children_count_); }
+
+ void SetIndexedReference(
+ HeapGraphEdge::Type type, int index, HeapEntry* entry);
+ void SetNamedReference(
+ HeapGraphEdge::Type type, const char* name, HeapEntry* entry);
+
+ void Print(
+ const char* prefix, const char* edge_name, int max_depth, int indent);
+
+ Handle<HeapObject> GetHeapObject();
+
+ private:
+ INLINE(HeapGraphEdge** children_arr());
+ const char* TypeAsString();
+
+ unsigned type_: 4;
+ int children_count_: 28;
+ int children_index_;
+ int self_size_;
+ SnapshotObjectId id_;
+ HeapSnapshot* snapshot_;
+ const char* name_;
+};
+
+
+class HeapSnapshotsCollection;
+
+// HeapSnapshot represents a single heap snapshot. It is stored in
+// HeapSnapshotsCollection, which is also a factory for
+// HeapSnapshots. All HeapSnapshots share strings copied from JS heap
+// to be able to return them even if they were collected.
+// HeapSnapshotGenerator fills in a HeapSnapshot.
+class HeapSnapshot {
+ public:
+ HeapSnapshot(HeapSnapshotsCollection* collection,
+ const char* title,
+ unsigned uid);
+ void Delete();
+
+ HeapSnapshotsCollection* collection() { return collection_; }
+ const char* title() { return title_; }
+ unsigned uid() { return uid_; }
+ size_t RawSnapshotSize() const;
+ HeapEntry* root() { return &entries_[root_index_]; }
+ HeapEntry* gc_roots() { return &entries_[gc_roots_index_]; }
+ HeapEntry* natives_root() { return &entries_[natives_root_index_]; }
+ HeapEntry* gc_subroot(int index) {
+ return &entries_[gc_subroot_indexes_[index]];
+ }
+ List<HeapEntry>& entries() { return entries_; }
+ List<HeapGraphEdge>& edges() { return edges_; }
+ List<HeapGraphEdge*>& children() { return children_; }
+ void RememberLastJSObjectId();
+ SnapshotObjectId max_snapshot_js_object_id() const {
+ return max_snapshot_js_object_id_;
+ }
+
+ HeapEntry* AddEntry(HeapEntry::Type type,
+ const char* name,
+ SnapshotObjectId id,
+ int size);
+ HeapEntry* AddRootEntry();
+ HeapEntry* AddGcRootsEntry();
+ HeapEntry* AddGcSubrootEntry(int tag);
+ HeapEntry* AddNativesRootEntry();
+ HeapEntry* GetEntryById(SnapshotObjectId id);
+ List<HeapEntry*>* GetSortedEntriesList();
+ void FillChildren();
+
+ void Print(int max_depth);
+ void PrintEntriesSize();
+
+ private:
+ HeapSnapshotsCollection* collection_;
+ const char* title_;
+ unsigned uid_;
+ int root_index_;
+ int gc_roots_index_;
+ int natives_root_index_;
+ int gc_subroot_indexes_[VisitorSynchronization::kNumberOfSyncTags];
+ List<HeapEntry> entries_;
+ List<HeapGraphEdge> edges_;
+ List<HeapGraphEdge*> children_;
+ List<HeapEntry*> sorted_entries_;
+ SnapshotObjectId max_snapshot_js_object_id_;
+
+ friend class HeapSnapshotTester;
+
+ DISALLOW_COPY_AND_ASSIGN(HeapSnapshot);
+};
+
+
+class HeapObjectsMap {
+ public:
+ explicit HeapObjectsMap(Heap* heap);
+
+ Heap* heap() const { return heap_; }
+
+ void SnapshotGenerationFinished();
+ SnapshotObjectId FindEntry(Address addr);
+ SnapshotObjectId FindOrAddEntry(Address addr, unsigned int size);
+ void MoveObject(Address from, Address to);
+ SnapshotObjectId last_assigned_id() const {
+ return next_id_ - kObjectIdStep;
+ }
+
+ void StopHeapObjectsTracking();
+ SnapshotObjectId PushHeapObjectsStats(OutputStream* stream);
+ size_t GetUsedMemorySize() const;
+
+ static SnapshotObjectId GenerateId(v8::RetainedObjectInfo* info);
+ static inline SnapshotObjectId GetNthGcSubrootId(int delta);
+
+ static const int kObjectIdStep = 2;
+ static const SnapshotObjectId kInternalRootObjectId;
+ static const SnapshotObjectId kGcRootsObjectId;
+ static const SnapshotObjectId kNativesRootObjectId;
+ static const SnapshotObjectId kGcRootsFirstSubrootId;
+ static const SnapshotObjectId kFirstAvailableObjectId;
+
+ private:
+ struct EntryInfo {
+ EntryInfo(SnapshotObjectId id, Address addr, unsigned int size)
+ : id(id), addr(addr), size(size), accessed(true) { }
+ EntryInfo(SnapshotObjectId id, Address addr, unsigned int size, bool accessed)
+ : id(id), addr(addr), size(size), accessed(accessed) { }
+ SnapshotObjectId id;
+ Address addr;
+ unsigned int size;
+ bool accessed;
+ };
+ struct TimeInterval {
+ explicit TimeInterval(SnapshotObjectId id) : id(id), size(0), count(0) { }
+ SnapshotObjectId id;
+ uint32_t size;
+ uint32_t count;
+ };
+
+ void UpdateHeapObjectsMap();
+ void RemoveDeadEntries();
+
+ SnapshotObjectId next_id_;
+ HashMap entries_map_;
+ List<EntryInfo> entries_;
+ List<TimeInterval> time_intervals_;
+ Heap* heap_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeapObjectsMap);
+};
+
+
+class HeapSnapshotsCollection {
+ public:
+ explicit HeapSnapshotsCollection(Heap* heap);
+ ~HeapSnapshotsCollection();
+
+ Heap* heap() const { return ids_.heap(); }
+
+ bool is_tracking_objects() { return is_tracking_objects_; }
+ SnapshotObjectId PushHeapObjectsStats(OutputStream* stream) {
+ return ids_.PushHeapObjectsStats(stream);
+ }
+ void StartHeapObjectsTracking() { is_tracking_objects_ = true; }
+ void StopHeapObjectsTracking() { ids_.StopHeapObjectsTracking(); }
+
+ HeapSnapshot* NewSnapshot(const char* name, unsigned uid);
+ void SnapshotGenerationFinished(HeapSnapshot* snapshot);
+ List<HeapSnapshot*>* snapshots() { return &snapshots_; }
+ void RemoveSnapshot(HeapSnapshot* snapshot);
+
+ StringsStorage* names() { return &names_; }
+
+ SnapshotObjectId FindObjectId(Address object_addr) {
+ return ids_.FindEntry(object_addr);
+ }
+ SnapshotObjectId GetObjectId(Address object_addr, int object_size) {
+ return ids_.FindOrAddEntry(object_addr, object_size);
+ }
+ Handle<HeapObject> FindHeapObjectById(SnapshotObjectId id);
+ void ObjectMoveEvent(Address from, Address to) { ids_.MoveObject(from, to); }
+ SnapshotObjectId last_assigned_id() const {
+ return ids_.last_assigned_id();
+ }
+ size_t GetUsedMemorySize() const;
+
+ private:
+ bool is_tracking_objects_; // Whether tracking object moves is needed.
+ List<HeapSnapshot*> snapshots_;
+ StringsStorage names_;
+ // Mapping from HeapObject addresses to objects' uids.
+ HeapObjectsMap ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeapSnapshotsCollection);
+};
+
+
+// A typedef for referencing anything that can be snapshotted living
+// in any kind of heap memory.
+typedef void* HeapThing;
+
+
+// An interface that creates HeapEntries by HeapThings.
+class HeapEntriesAllocator {
+ public:
+ virtual ~HeapEntriesAllocator() { }
+ virtual HeapEntry* AllocateEntry(HeapThing ptr) = 0;
+};
+
+
+// The HeapEntriesMap instance is used to track a mapping between
+// real heap objects and their representations in heap snapshots.
+class HeapEntriesMap {
+ public:
+ HeapEntriesMap();
+
+ int Map(HeapThing thing);
+ void Pair(HeapThing thing, int entry);
+
+ private:
+ static uint32_t Hash(HeapThing thing) {
+ return ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(thing)),
+ v8::internal::kZeroHashSeed);
+ }
+ static bool HeapThingsMatch(HeapThing key1, HeapThing key2) {
+ return key1 == key2;
+ }
+
+ HashMap entries_;
+
+ friend class HeapObjectsSet;
+
+ DISALLOW_COPY_AND_ASSIGN(HeapEntriesMap);
+};
+
+
+class HeapObjectsSet {
+ public:
+ HeapObjectsSet();
+ void Clear();
+ bool Contains(Object* object);
+ void Insert(Object* obj);
+ const char* GetTag(Object* obj);
+ void SetTag(Object* obj, const char* tag);
+ bool is_empty() const { return entries_.occupancy() == 0; }
+
+ private:
+ HashMap entries_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeapObjectsSet);
+};
+
+
+// An interface used to populate a snapshot with nodes and edges.
+class SnapshotFillerInterface {
+ public:
+ virtual ~SnapshotFillerInterface() { }
+ virtual HeapEntry* AddEntry(HeapThing ptr,
+ HeapEntriesAllocator* allocator) = 0;
+ virtual HeapEntry* FindEntry(HeapThing ptr) = 0;
+ virtual HeapEntry* FindOrAddEntry(HeapThing ptr,
+ HeapEntriesAllocator* allocator) = 0;
+ virtual void SetIndexedReference(HeapGraphEdge::Type type,
+ int parent_entry,
+ int index,
+ HeapEntry* child_entry) = 0;
+ virtual void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent_entry,
+ HeapEntry* child_entry) = 0;
+ virtual void SetNamedReference(HeapGraphEdge::Type type,
+ int parent_entry,
+ const char* reference_name,
+ HeapEntry* child_entry) = 0;
+ virtual void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
+ int parent_entry,
+ HeapEntry* child_entry) = 0;
+};
+
+
+class SnapshottingProgressReportingInterface {
+ public:
+ virtual ~SnapshottingProgressReportingInterface() { }
+ virtual void ProgressStep() = 0;
+ virtual bool ProgressReport(bool force) = 0;
+};
+
+
+// An implementation of V8 heap graph extractor.
+class V8HeapExplorer : public HeapEntriesAllocator {
+ public:
+ V8HeapExplorer(HeapSnapshot* snapshot,
+ SnapshottingProgressReportingInterface* progress,
+ v8::HeapProfiler::ObjectNameResolver* resolver);
+ virtual ~V8HeapExplorer();
+ virtual HeapEntry* AllocateEntry(HeapThing ptr);
+ void AddRootEntries(SnapshotFillerInterface* filler);
+ int EstimateObjectsCount(HeapIterator* iterator);
+ bool IterateAndExtractReferences(SnapshotFillerInterface* filler);
+ void TagGlobalObjects();
+
+ static String* GetConstructorName(JSObject* object);
+
+ static HeapObject* const kInternalRootObject;
+
+ private:
+ HeapEntry* AddEntry(HeapObject* object);
+ HeapEntry* AddEntry(HeapObject* object,
+ HeapEntry::Type type,
+ const char* name);
+ const char* GetSystemEntryName(HeapObject* object);
+
+ void ExtractReferences(HeapObject* obj);
+ void ExtractJSGlobalProxyReferences(int entry, JSGlobalProxy* proxy);
+ void ExtractJSObjectReferences(int entry, JSObject* js_obj);
+ void ExtractStringReferences(int entry, String* obj);
+ void ExtractContextReferences(int entry, Context* context);
+ void ExtractMapReferences(int entry, Map* map);
+ void ExtractSharedFunctionInfoReferences(int entry,
+ SharedFunctionInfo* shared);
+ void ExtractScriptReferences(int entry, Script* script);
+ void ExtractAccessorPairReferences(int entry, AccessorPair* accessors);
+ void ExtractCodeCacheReferences(int entry, CodeCache* code_cache);
+ void ExtractCodeReferences(int entry, Code* code);
+ void ExtractCellReferences(int entry, Cell* cell);
+ void ExtractPropertyCellReferences(int entry, PropertyCell* cell);
+ void ExtractAllocationSiteReferences(int entry, AllocationSite* site);
+ void ExtractClosureReferences(JSObject* js_obj, int entry);
+ void ExtractPropertyReferences(JSObject* js_obj, int entry);
+ bool ExtractAccessorPairProperty(JSObject* js_obj, int entry,
+ Object* key, Object* callback_obj);
+ void ExtractElementReferences(JSObject* js_obj, int entry);
+ void ExtractInternalReferences(JSObject* js_obj, int entry);
+ bool IsEssentialObject(Object* object);
+ void SetContextReference(HeapObject* parent_obj,
+ int parent,
+ String* reference_name,
+ Object* child,
+ int field_offset);
+ void SetNativeBindReference(HeapObject* parent_obj,
+ int parent,
+ const char* reference_name,
+ Object* child);
+ void SetElementReference(HeapObject* parent_obj,
+ int parent,
+ int index,
+ Object* child);
+ void SetInternalReference(HeapObject* parent_obj,
+ int parent,
+ const char* reference_name,
+ Object* child,
+ int field_offset = -1);
+ void SetInternalReference(HeapObject* parent_obj,
+ int parent,
+ int index,
+ Object* child,
+ int field_offset = -1);
+ void SetHiddenReference(HeapObject* parent_obj,
+ int parent,
+ int index,
+ Object* child);
+ void SetWeakReference(HeapObject* parent_obj,
+ int parent,
+ int index,
+ Object* child_obj,
+ int field_offset);
+ void SetPropertyReference(HeapObject* parent_obj,
+ int parent,
+ Name* reference_name,
+ Object* child,
+ const char* name_format_string = NULL,
+ int field_offset = -1);
+ void SetUserGlobalReference(Object* user_global);
+ void SetRootGcRootsReference();
+ void SetGcRootsReference(VisitorSynchronization::SyncTag tag);
+ void SetGcSubrootReference(
+ VisitorSynchronization::SyncTag tag, bool is_weak, Object* child);
+ const char* GetStrongGcSubrootName(Object* object);
+ 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_;
+ SnapshottingProgressReportingInterface* progress_;
+ SnapshotFillerInterface* filler_;
+ HeapObjectsSet objects_tags_;
+ HeapObjectsSet strong_gc_subroot_names_;
+ HeapObjectsSet user_roots_;
+ v8::HeapProfiler::ObjectNameResolver* global_object_name_resolver_;
+
+ 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:
+ NativeObjectsExplorer(HeapSnapshot* snapshot,
+ SnapshottingProgressReportingInterface* progress);
+ virtual ~NativeObjectsExplorer();
+ 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 SetRootNativeRootsReference();
+ void SetWrapperNativeReferences(HeapObject* wrapper,
+ v8::RetainedObjectInfo* info);
+ void VisitSubtreeWrapper(Object** p, uint16_t class_id);
+
+ static uint32_t InfoHash(v8::RetainedObjectInfo* info) {
+ return ComputeIntegerHash(static_cast<uint32_t>(info->GetHash()),
+ v8::internal::kZeroHashSeed);
+ }
+ static bool RetainedInfosMatch(void* key1, void* key2) {
+ return key1 == key2 ||
+ (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_;
+ SnapshottingProgressReportingInterface* progress_;
+ bool embedder_queried_;
+ 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_;
+
+ static HeapThing const kNativesRootObject;
+
+ friend class GlobalHandlesExtractor;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeObjectsExplorer);
+};
+
+
+class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
+ public:
+ HeapSnapshotGenerator(HeapSnapshot* snapshot,
+ v8::ActivityControl* control,
+ v8::HeapProfiler::ObjectNameResolver* resolver,
+ Heap* heap);
+ bool GenerateSnapshot();
+
+ private:
+ bool FillReferences();
+ void ProgressStep();
+ bool ProgressReport(bool force = false);
+ void SetProgressTotal(int iterations_count);
+
+ HeapSnapshot* snapshot_;
+ v8::ActivityControl* control_;
+ V8HeapExplorer v8_heap_explorer_;
+ NativeObjectsExplorer dom_explorer_;
+ // Mapping from HeapThing pointers to HeapEntry* pointers.
+ HeapEntriesMap entries_;
+ // Used during snapshot generation.
+ int progress_counter_;
+ int progress_total_;
+ Heap* heap_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator);
+};
+
+class OutputStreamWriter;
+
+class HeapSnapshotJSONSerializer {
+ public:
+ explicit HeapSnapshotJSONSerializer(HeapSnapshot* snapshot)
+ : snapshot_(snapshot),
+ strings_(ObjectsMatch),
+ next_node_id_(1),
+ next_string_id_(1),
+ writer_(NULL) {
+ }
+ void Serialize(v8::OutputStream* stream);
+
+ private:
+ INLINE(static bool ObjectsMatch(void* key1, void* key2)) {
+ return key1 == key2;
+ }
+
+ INLINE(static uint32_t ObjectHash(const void* key)) {
+ return ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key)),
+ v8::internal::kZeroHashSeed);
+ }
+
+ int GetStringId(const char* s);
+ int entry_index(HeapEntry* e) { return e->index() * kNodeFieldsCount; }
+ void SerializeEdge(HeapGraphEdge* edge, bool first_edge);
+ void SerializeEdges();
+ void SerializeImpl();
+ void SerializeNode(HeapEntry* entry);
+ void SerializeNodes();
+ void SerializeSnapshot();
+ void SerializeString(const unsigned char* s);
+ void SerializeStrings();
+ void SortHashMap(HashMap* map, List<HashMap::Entry*>* sorted_entries);
+
+ static const int kEdgeFieldsCount;
+ static const int kNodeFieldsCount;
+
+ HeapSnapshot* snapshot_;
+ HashMap strings_;
+ int next_node_id_;
+ int next_string_id_;
+ OutputStreamWriter* writer_;
+
+ friend class HeapSnapshotJSONSerializerEnumerator;
+ friend class HeapSnapshotJSONSerializerIterator;
+
+ DISALLOW_COPY_AND_ASSIGN(HeapSnapshotJSONSerializer);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HEAP_SNAPSHOT_GENERATOR_H_
+
diff --git a/chromium/v8/src/heap.cc b/chromium/v8/src/heap.cc
new file mode 100644
index 00000000000..9d8a6fad995
--- /dev/null
+++ b/chromium/v8/src/heap.cc
@@ -0,0 +1,8054 @@
+// Copyright 2012 the V8 project authors. 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 "accessors.h"
+#include "api.h"
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "compilation-cache.h"
+#include "cpu-profiler.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "global-handles.h"
+#include "heap-profiler.h"
+#include "incremental-marking.h"
+#include "mark-compact.h"
+#include "natives.h"
+#include "objects-visiting.h"
+#include "objects-visiting-inl.h"
+#include "once.h"
+#include "runtime-profiler.h"
+#include "scopeinfo.h"
+#include "snapshot.h"
+#include "store-buffer.h"
+#include "v8threads.h"
+#include "v8utils.h"
+#include "vm-state-inl.h"
+#if V8_TARGET_ARCH_ARM && !V8_INTERPRETED_REGEXP
+#include "regexp-macro-assembler.h"
+#include "arm/regexp-macro-assembler-arm.h"
+#endif
+#if V8_TARGET_ARCH_MIPS && !V8_INTERPRETED_REGEXP
+#include "regexp-macro-assembler.h"
+#include "mips/regexp-macro-assembler-mips.h"
+#endif
+
+namespace v8 {
+namespace internal {
+
+
+Heap::Heap()
+ : isolate_(NULL),
+// semispace_size_ should be a power of 2 and old_generation_size_ should be
+// a multiple of Page::kPageSize.
+#if V8_TARGET_ARCH_X64
+#define LUMP_OF_MEMORY (2 * MB)
+ code_range_size_(512*MB),
+#else
+#define LUMP_OF_MEMORY MB
+ code_range_size_(0),
+#endif
+#if defined(ANDROID) || V8_TARGET_ARCH_MIPS
+ reserved_semispace_size_(4 * Max(LUMP_OF_MEMORY, Page::kPageSize)),
+ max_semispace_size_(4 * Max(LUMP_OF_MEMORY, Page::kPageSize)),
+ initial_semispace_size_(Page::kPageSize),
+ max_old_generation_size_(192*MB),
+ max_executable_size_(max_old_generation_size_),
+#else
+ 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_(256l * LUMP_OF_MEMORY),
+#endif
+
+// 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
+// generation can be aligned to its size.
+ survived_since_last_expansion_(0),
+ sweep_generation_(0),
+ always_allocate_scope_depth_(0),
+ linear_allocation_scope_depth_(0),
+ contexts_disposed_(0),
+ global_ic_age_(0),
+ flush_monomorphic_ics_(false),
+ scan_on_scavenge_pages_(0),
+ new_space_(this),
+ old_pointer_space_(NULL),
+ old_data_space_(NULL),
+ code_space_(NULL),
+ map_space_(NULL),
+ cell_space_(NULL),
+ property_cell_space_(NULL),
+ lo_space_(NULL),
+ gc_state_(NOT_IN_GC),
+ gc_post_processing_depth_(0),
+ ms_count_(0),
+ gc_count_(0),
+ remembered_unmapped_pages_index_(0),
+ unflattened_strings_length_(0),
+#ifdef DEBUG
+ allocation_timeout_(0),
+ disallow_allocation_failure_(false),
+#endif // DEBUG
+ new_space_high_promotion_mode_active_(false),
+ old_generation_allocation_limit_(kMinimumOldGenerationAllocationLimit),
+ 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_string_(NULL),
+ global_gc_prologue_callback_(NULL),
+ global_gc_epilogue_callback_(NULL),
+ gc_safe_size_of_old_object_(NULL),
+ total_regexp_code_generated_(0),
+ tracer_(NULL),
+ young_survivors_after_last_gc_(0),
+ high_survival_rate_period_length_(0),
+ low_survival_rate_period_length_(0),
+ survival_rate_(0),
+ previous_survival_rate_trend_(Heap::STABLE),
+ survival_rate_trend_(Heap::STABLE),
+ max_gc_pause_(0.0),
+ total_gc_time_ms_(0.0),
+ max_alive_after_gc_(0),
+ min_in_mutator_(kMaxInt),
+ alive_after_last_gc_(0),
+ last_gc_end_timestamp_(0.0),
+ marking_time_(0.0),
+ sweeping_time_(0.0),
+ 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),
+ mark_sweeps_since_idle_round_started_(0),
+ gc_count_at_last_idle_gc_(0),
+ scavenges_since_last_idle_round_(kIdleScavengeThreshold),
+ gcs_since_last_deopt_(0),
+#ifdef VERIFY_HEAP
+ no_weak_embedded_maps_verification_scope_depth_(0),
+#endif
+ promotion_queue_(this),
+ configured_(false),
+ chunks_queued_for_free_(NULL),
+ relocation_mutex_(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.
+#if defined(V8_MAX_SEMISPACE_SIZE)
+ max_semispace_size_ = reserved_semispace_size_ = V8_MAX_SEMISPACE_SIZE;
+#endif
+
+ intptr_t max_virtual = OS::MaxVirtualMemory();
+
+ if (max_virtual > 0) {
+ if (code_range_size_ > 0) {
+ // Reserve no more than 1/8 of the memory for the code range.
+ code_range_size_ = Min(code_range_size_, max_virtual >> 3);
+ }
+ }
+
+ memset(roots_, 0, sizeof(roots_[0]) * kRootListLength);
+ native_contexts_list_ = NULL;
+ array_buffers_list_ = Smi::FromInt(0);
+ allocation_sites_list_ = Smi::FromInt(0);
+ mark_compact_collector_.heap_ = this;
+ external_string_table_.heap_ = this;
+ // Put a dummy entry in the remembered pages so we can find the list the
+ // minidump even if there are no real unmapped pages.
+ RememberUnmappedPage(NULL, false);
+
+ ClearObjectStats(true);
+}
+
+
+intptr_t Heap::Capacity() {
+ if (!HasBeenSetUp()) return 0;
+
+ return new_space_.Capacity() +
+ old_pointer_space_->Capacity() +
+ old_data_space_->Capacity() +
+ code_space_->Capacity() +
+ map_space_->Capacity() +
+ cell_space_->Capacity() +
+ property_cell_space_->Capacity();
+}
+
+
+intptr_t Heap::CommittedMemory() {
+ if (!HasBeenSetUp()) return 0;
+
+ return new_space_.CommittedMemory() +
+ old_pointer_space_->CommittedMemory() +
+ old_data_space_->CommittedMemory() +
+ code_space_->CommittedMemory() +
+ map_space_->CommittedMemory() +
+ cell_space_->CommittedMemory() +
+ property_cell_space_->CommittedMemory() +
+ lo_space_->Size();
+}
+
+
+size_t Heap::CommittedPhysicalMemory() {
+ if (!HasBeenSetUp()) return 0;
+
+ return new_space_.CommittedPhysicalMemory() +
+ old_pointer_space_->CommittedPhysicalMemory() +
+ old_data_space_->CommittedPhysicalMemory() +
+ code_space_->CommittedPhysicalMemory() +
+ map_space_->CommittedPhysicalMemory() +
+ cell_space_->CommittedPhysicalMemory() +
+ property_cell_space_->CommittedPhysicalMemory() +
+ lo_space_->CommittedPhysicalMemory();
+}
+
+
+intptr_t Heap::CommittedMemoryExecutable() {
+ if (!HasBeenSetUp()) return 0;
+
+ return isolate()->memory_allocator()->SizeExecutable();
+}
+
+
+intptr_t Heap::Available() {
+ if (!HasBeenSetUp()) return 0;
+
+ return new_space_.Available() +
+ old_pointer_space_->Available() +
+ old_data_space_->Available() +
+ code_space_->Available() +
+ map_space_->Available() +
+ cell_space_->Available() +
+ property_cell_space_->Available();
+}
+
+
+bool Heap::HasBeenSetUp() {
+ return old_pointer_space_ != NULL &&
+ old_data_space_ != NULL &&
+ code_space_ != NULL &&
+ map_space_ != NULL &&
+ cell_space_ != NULL &&
+ property_cell_space_ != NULL &&
+ lo_space_ != NULL;
+}
+
+
+int Heap::GcSafeSizeOfOldObject(HeapObject* object) {
+ if (IntrusiveMarking::IsMarked(object)) {
+ return IntrusiveMarking::SizeOfMarkedObject(object);
+ }
+ return object->SizeFromMap(object->map());
+}
+
+
+GarbageCollector Heap::SelectGarbageCollector(AllocationSpace space,
+ const char** reason) {
+ // Is global GC requested?
+ if (space != NEW_SPACE) {
+ isolate_->counters()->gc_compactor_caused_by_request()->Increment();
+ *reason = "GC in old space requested";
+ return MARK_COMPACTOR;
+ }
+
+ if (FLAG_gc_global || (FLAG_stress_compaction && (gc_count_ & 1) != 0)) {
+ *reason = "GC in old space forced by flags";
+ return MARK_COMPACTOR;
+ }
+
+ // Is enough data promoted to justify a global GC?
+ if (OldGenerationAllocationLimitReached()) {
+ isolate_->counters()->gc_compactor_caused_by_promoted_data()->Increment();
+ *reason = "promotion limit reached";
+ return MARK_COMPACTOR;
+ }
+
+ // Have allocation in OLD and LO failed?
+ if (old_gen_exhausted_) {
+ isolate_->counters()->
+ gc_compactor_caused_by_oldspace_exhaustion()->Increment();
+ *reason = "old generations exhausted";
+ return MARK_COMPACTOR;
+ }
+
+ // Is there enough space left in OLD to guarantee that a scavenge can
+ // succeed?
+ //
+ // Note that MemoryAllocator->MaxAvailable() undercounts the memory available
+ // for object promotion. It counts only the bytes that the memory
+ // allocator has not yet allocated from the OS and assigned to any space,
+ // and does not count available bytes already in the old space or code
+ // space. Undercounting is safe---we may get an unrequested full GC when
+ // a scavenge would have succeeded.
+ 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;
+}
+
+
+// TODO(1238405): Combine the infrastructure for --heap-stats and
+// --log-gc to avoid the complicated preprocessor and flag testing.
+void Heap::ReportStatisticsBeforeGC() {
+ // Heap::ReportHeapStatistics will also log NewSpace statistics when
+ // compiled --log-gc is set. The following logic is used to avoid
+ // double logging.
+#ifdef DEBUG
+ if (FLAG_heap_stats || FLAG_log_gc) new_space_.CollectStatistics();
+ if (FLAG_heap_stats) {
+ ReportHeapStatistics("Before GC");
+ } else if (FLAG_log_gc) {
+ new_space_.ReportStatistics();
+ }
+ if (FLAG_heap_stats || FLAG_log_gc) new_space_.ClearHistograms();
+#else
+ if (FLAG_log_gc) {
+ new_space_.CollectStatistics();
+ new_space_.ReportStatistics();
+ new_space_.ClearHistograms();
+ }
+#endif // DEBUG
+}
+
+
+void Heap::PrintShortHeapStatistics() {
+ if (!FLAG_trace_gc_verbose) return;
+ PrintPID("Memory allocator, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB\n",
+ isolate_->memory_allocator()->Size() / KB,
+ isolate_->memory_allocator()->Available() / KB);
+ PrintPID("New space, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ new_space_.Size() / KB,
+ new_space_.Available() / KB,
+ new_space_.CommittedMemory() / KB);
+ PrintPID("Old pointers, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ old_pointer_space_->SizeOfObjects() / KB,
+ old_pointer_space_->Available() / KB,
+ old_pointer_space_->CommittedMemory() / KB);
+ PrintPID("Old data space, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ old_data_space_->SizeOfObjects() / KB,
+ old_data_space_->Available() / KB,
+ old_data_space_->CommittedMemory() / KB);
+ PrintPID("Code space, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ code_space_->SizeOfObjects() / KB,
+ code_space_->Available() / KB,
+ code_space_->CommittedMemory() / KB);
+ PrintPID("Map space, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ map_space_->SizeOfObjects() / KB,
+ map_space_->Available() / KB,
+ map_space_->CommittedMemory() / KB);
+ PrintPID("Cell space, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ cell_space_->SizeOfObjects() / KB,
+ cell_space_->Available() / KB,
+ cell_space_->CommittedMemory() / KB);
+ PrintPID("PropertyCell space, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ property_cell_space_->SizeOfObjects() / KB,
+ property_cell_space_->Available() / KB,
+ property_cell_space_->CommittedMemory() / KB);
+ PrintPID("Large object space, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ lo_space_->SizeOfObjects() / KB,
+ lo_space_->Available() / KB,
+ lo_space_->CommittedMemory() / KB);
+ PrintPID("All spaces, used: %6" V8_PTR_PREFIX "d KB"
+ ", available: %6" V8_PTR_PREFIX "d KB"
+ ", committed: %6" V8_PTR_PREFIX "d KB\n",
+ this->SizeOfObjects() / KB,
+ this->Available() / KB,
+ this->CommittedMemory() / KB);
+ PrintPID("External memory reported: %6" V8_PTR_PREFIX "d KB\n",
+ amount_of_external_allocated_memory_ / KB);
+ PrintPID("Total time spent in GC : %.1f ms\n", total_gc_time_ms_);
+}
+
+
+// TODO(1238405): Combine the infrastructure for --heap-stats and
+// --log-gc to avoid the complicated preprocessor and flag testing.
+void Heap::ReportStatisticsAfterGC() {
+ // Similar to the before GC, we use some complicated logic to ensure that
+ // NewSpace statistics are logged exactly once when --log-gc is turned on.
+#if defined(DEBUG)
+ if (FLAG_heap_stats) {
+ new_space_.CollectStatistics();
+ ReportHeapStatistics("After GC");
+ } else if (FLAG_log_gc) {
+ new_space_.ReportStatistics();
+ }
+#else
+ if (FLAG_log_gc) new_space_.ReportStatistics();
+#endif // DEBUG
+}
+
+
+void Heap::GarbageCollectionPrologue() {
+ { AllowHeapAllocation for_the_first_part_of_prologue;
+ isolate_->transcendental_cache()->Clear();
+ ClearJSFunctionResultCaches();
+ gc_count_++;
+ unflattened_strings_length_ = 0;
+
+ if (FLAG_flush_code && FLAG_flush_code_incrementally) {
+ mark_compact_collector()->EnableCodeFlushing(true);
+ }
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ Verify();
+ }
+#endif
+ }
+
+#ifdef DEBUG
+ ASSERT(!AllowHeapAllocation::IsAllowed() && gc_state_ == NOT_IN_GC);
+
+ if (FLAG_gc_verbose) Print();
+
+ ReportStatisticsBeforeGC();
+#endif // DEBUG
+
+ store_buffer()->GCPrologue();
+}
+
+
+intptr_t Heap::SizeOfObjects() {
+ intptr_t total = 0;
+ AllSpaces spaces(this);
+ for (Space* space = spaces.next(); space != NULL; space = spaces.next()) {
+ total += space->SizeOfObjects();
+ }
+ return total;
+}
+
+
+void Heap::RepairFreeListsAfterBoot() {
+ PagedSpaces spaces(this);
+ for (PagedSpace* space = spaces.next();
+ space != NULL;
+ space = spaces.next()) {
+ space->RepairFreeListsAfterBoot();
+ }
+}
+
+
+void Heap::GarbageCollectionEpilogue() {
+ store_buffer()->GCEpilogue();
+
+ // In release mode, we only zap the from space under heap verification.
+ if (Heap::ShouldZapGarbage()) {
+ ZapFromSpace();
+ }
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ Verify();
+ }
+#endif
+
+ AllowHeapAllocation for_the_rest_of_the_epilogue;
+
+#ifdef DEBUG
+ if (FLAG_print_global_handles) isolate_->global_handles()->Print();
+ if (FLAG_print_handles) PrintHandles();
+ if (FLAG_gc_verbose) Print();
+ if (FLAG_code_stats) ReportCodeStatistics("After GC");
+#endif
+ if (FLAG_deopt_every_n_garbage_collections > 0) {
+ if (++gcs_since_last_deopt_ == FLAG_deopt_every_n_garbage_collections) {
+ Deoptimizer::DeoptimizeAll(isolate());
+ gcs_since_last_deopt_ = 0;
+ }
+ }
+
+ isolate_->counters()->alive_after_last_gc()->Set(
+ static_cast<int>(SizeOfObjects()));
+
+ isolate_->counters()->string_table_capacity()->Set(
+ string_table()->Capacity());
+ isolate_->counters()->number_of_symbols()->Set(
+ string_table()->NumberOfElements());
+
+ if (CommittedMemory() > 0) {
+ isolate_->counters()->external_fragmentation_total()->AddSample(
+ static_cast<int>(100 - (SizeOfObjects() * 100.0) / CommittedMemory()));
+
+ isolate_->counters()->heap_fraction_map_space()->AddSample(
+ static_cast<int>(
+ (map_space()->CommittedMemory() * 100.0) / CommittedMemory()));
+ isolate_->counters()->heap_fraction_cell_space()->AddSample(
+ static_cast<int>(
+ (cell_space()->CommittedMemory() * 100.0) / CommittedMemory()));
+ isolate_->counters()->heap_fraction_property_cell_space()->
+ AddSample(static_cast<int>(
+ (property_cell_space()->CommittedMemory() * 100.0) /
+ CommittedMemory()));
+
+ isolate_->counters()->heap_sample_total_committed()->AddSample(
+ static_cast<int>(CommittedMemory() / KB));
+ isolate_->counters()->heap_sample_total_used()->AddSample(
+ static_cast<int>(SizeOfObjects() / KB));
+ isolate_->counters()->heap_sample_map_space_committed()->AddSample(
+ static_cast<int>(map_space()->CommittedMemory() / KB));
+ isolate_->counters()->heap_sample_cell_space_committed()->AddSample(
+ static_cast<int>(cell_space()->CommittedMemory() / KB));
+ isolate_->counters()->
+ heap_sample_property_cell_space_committed()->
+ AddSample(static_cast<int>(
+ property_cell_space()->CommittedMemory() / KB));
+ }
+
+#define UPDATE_COUNTERS_FOR_SPACE(space) \
+ isolate_->counters()->space##_bytes_available()->Set( \
+ static_cast<int>(space()->Available())); \
+ isolate_->counters()->space##_bytes_committed()->Set( \
+ static_cast<int>(space()->CommittedMemory())); \
+ isolate_->counters()->space##_bytes_used()->Set( \
+ static_cast<int>(space()->SizeOfObjects()));
+#define UPDATE_FRAGMENTATION_FOR_SPACE(space) \
+ if (space()->CommittedMemory() > 0) { \
+ isolate_->counters()->external_fragmentation_##space()->AddSample( \
+ static_cast<int>(100 - \
+ (space()->SizeOfObjects() * 100.0) / space()->CommittedMemory())); \
+ }
+#define UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE(space) \
+ UPDATE_COUNTERS_FOR_SPACE(space) \
+ UPDATE_FRAGMENTATION_FOR_SPACE(space)
+
+ UPDATE_COUNTERS_FOR_SPACE(new_space)
+ UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE(old_pointer_space)
+ UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE(old_data_space)
+ UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE(code_space)
+ UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE(map_space)
+ UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE(cell_space)
+ UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE(property_cell_space)
+ UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE(lo_space)
+#undef UPDATE_COUNTERS_FOR_SPACE
+#undef UPDATE_FRAGMENTATION_FOR_SPACE
+#undef UPDATE_COUNTERS_AND_FRAGMENTATION_FOR_SPACE
+
+#if defined(DEBUG)
+ ReportStatisticsAfterGC();
+#endif // DEBUG
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ isolate_->debug()->AfterGarbageCollection();
+#endif // ENABLE_DEBUGGER_SUPPORT
+}
+
+
+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_.SetFlags(flags);
+ CollectGarbage(OLD_POINTER_SPACE, gc_reason);
+ mark_compact_collector_.SetFlags(kNoGCFlags);
+}
+
+
+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.
+ // 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
+ // has been invoked, we rerun major GC to release objects which become
+ // garbage.
+ // 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;
+ const int kMinNumberOfAttempts = 2;
+ for (int attempt = 0; attempt < kMaxNumberOfAttempts; attempt++) {
+ if (!CollectGarbage(OLD_POINTER_SPACE, MARK_COMPACTOR, gc_reason, NULL) &&
+ attempt + 1 >= kMinNumberOfAttempts) {
+ break;
+ }
+ }
+ mark_compact_collector()->SetFlags(kNoGCFlags);
+ new_space_.Shrink();
+ UncommitFromSpace();
+ incremental_marking()->UncommitMarkingDeque();
+}
+
+
+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<GC> state(isolate_);
+
+#ifdef DEBUG
+ // Reset the allocation timeout to the GC interval, but make sure to
+ // allow at least a few allocations after a collection. The reason
+ // for this is that we have a lot of allocation sequences and we
+ // assume that a garbage collection will allow the subsequent
+ // allocation attempts to go through.
+ 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()->abort_incremental_marking() &&
+ !incremental_marking()->IsStopped() &&
+ !incremental_marking()->should_hurry() &&
+ FLAG_incremental_marking_steps) {
+ // Make progress in incremental marking.
+ const intptr_t kStepSizeWhenDelayedByScavenge = 1 * MB;
+ incremental_marking()->Step(kStepSizeWhenDelayedByScavenge,
+ IncrementalMarking::NO_GC_VIA_STACK_GUARD);
+ if (!incremental_marking()->IsComplete()) {
+ 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, gc_reason, collector_reason);
+ ASSERT(AllowHeapAllocation::IsAllowed());
+ DisallowHeapAllocation no_allocation_during_gc;
+ GarbageCollectionPrologue();
+ // The GC count was incremented in the prologue. Tell the tracer about
+ // it.
+ tracer.set_gc_count(gc_count_);
+
+ // Tell the tracer which collector we've selected.
+ tracer.set_collector(collector);
+
+ {
+ HistogramTimerScope histogram_timer_scope(
+ (collector == SCAVENGER) ? isolate_->counters()->gc_scavenger()
+ : isolate_->counters()->gc_compactor());
+ next_gc_likely_to_collect_more =
+ PerformGarbageCollection(collector, &tracer);
+ }
+
+ GarbageCollectionEpilogue();
+ }
+
+ // Start incremental marking for the next cycle. The heap snapshot
+ // generator needs incremental marking to stay off after it aborted.
+ if (!mark_compact_collector()->abort_incremental_marking() &&
+ incremental_marking()->IsStopped() &&
+ incremental_marking()->WorthActivating() &&
+ NextGCIsLikelyToBeFull()) {
+ incremental_marking()->Start();
+ }
+
+ return next_gc_likely_to_collect_more;
+}
+
+
+int Heap::NotifyContextDisposed() {
+ if (FLAG_parallel_recompilation) {
+ // Flush the queued recompilation tasks.
+ isolate()->optimizing_compiler_thread()->Flush();
+ }
+ flush_monomorphic_ics_ = true;
+ return ++contexts_disposed_;
+}
+
+
+void Heap::PerformScavenge() {
+ GCTracer tracer(this, NULL, NULL);
+ if (incremental_marking()->IsStopped()) {
+ PerformGarbageCollection(SCAVENGER, &tracer);
+ } else {
+ PerformGarbageCollection(MARK_COMPACTOR, &tracer);
+ }
+}
+
+
+void Heap::MoveElements(FixedArray* array,
+ int dst_index,
+ int src_index,
+ int len) {
+ if (len == 0) return;
+
+ ASSERT(array->map() != HEAP->fixed_cow_array_map());
+ Object** dst_objects = array->data_start() + dst_index;
+ OS::MemMove(dst_objects,
+ array->data_start() + src_index,
+ len * kPointerSize);
+ if (!InNewSpace(array)) {
+ for (int i = 0; i < len; i++) {
+ // TODO(hpayer): check store buffer for entries
+ if (InNewSpace(dst_objects[i])) {
+ RecordWrite(array->address(), array->OffsetOfElementAt(dst_index + i));
+ }
+ }
+ }
+ incremental_marking()->RecordWrites(array);
+}
+
+
+#ifdef VERIFY_HEAP
+// Helper class for verifying the string table.
+class StringTableVerifier : public ObjectVisitor {
+ public:
+ void VisitPointers(Object** start, Object** end) {
+ // Visit all HeapObject pointers in [start, end).
+ for (Object** p = start; p < end; p++) {
+ if ((*p)->IsHeapObject()) {
+ // Check that the string is actually internalized.
+ CHECK((*p)->IsTheHole() || (*p)->IsUndefined() ||
+ (*p)->IsInternalizedString());
+ }
+ }
+ }
+};
+
+
+static void VerifyStringTable() {
+ StringTableVerifier verifier;
+ HEAP->string_table()->IterateElements(&verifier);
+}
+#endif // VERIFY_HEAP
+
+
+static bool AbortIncrementalMarkingAndCollectGarbage(
+ Heap* heap,
+ AllocationSpace space,
+ const char* gc_reason = NULL) {
+ heap->mark_compact_collector()->SetFlags(Heap::kAbortIncrementalMarkingMask);
+ bool result = heap->CollectGarbage(space, gc_reason);
+ heap->mark_compact_collector()->SetFlags(Heap::kNoGCFlags);
+ return result;
+}
+
+
+void Heap::ReserveSpace(
+ int *sizes,
+ Address *locations_out) {
+ bool gc_performed = true;
+ int counter = 0;
+ static const int kThreshold = 20;
+ while (gc_performed && counter++ < kThreshold) {
+ gc_performed = false;
+ ASSERT(NEW_SPACE == FIRST_PAGED_SPACE - 1);
+ for (int space = NEW_SPACE; space <= LAST_PAGED_SPACE; space++) {
+ if (sizes[space] != 0) {
+ MaybeObject* allocation;
+ if (space == NEW_SPACE) {
+ allocation = new_space()->AllocateRaw(sizes[space]);
+ } else {
+ allocation = paged_space(space)->AllocateRaw(sizes[space]);
+ }
+ FreeListNode* node;
+ if (!allocation->To<FreeListNode>(&node)) {
+ if (space == NEW_SPACE) {
+ Heap::CollectGarbage(NEW_SPACE,
+ "failed to reserve space in the new space");
+ } else {
+ AbortIncrementalMarkingAndCollectGarbage(
+ this,
+ static_cast<AllocationSpace>(space),
+ "failed to reserve space in paged space");
+ }
+ gc_performed = true;
+ break;
+ } else {
+ // Mark with a free list node, in case we have a GC before
+ // deserializing.
+ node->set_size(this, sizes[space]);
+ locations_out[space] = node->address();
+ }
+ }
+ }
+ }
+
+ if (gc_performed) {
+ // Failed to reserve the space after several attempts.
+ V8::FatalProcessOutOfMemory("Heap::ReserveSpace");
+ }
+}
+
+
+void Heap::EnsureFromSpaceIsCommitted() {
+ if (new_space_.CommitFromSpaceIfNeeded()) return;
+
+ // Committing memory to from space failed.
+ // Memory is exhausted and we will die.
+ V8::FatalProcessOutOfMemory("Committing semi space failed.");
+}
+
+
+void Heap::ClearJSFunctionResultCaches() {
+ if (isolate_->bootstrapper()->IsActive()) return;
+
+ Object* context = native_contexts_list_;
+ while (!context->IsUndefined()) {
+ // 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);
+ }
+}
+
+
+void Heap::ClearNormalizedMapCaches() {
+ if (isolate_->bootstrapper()->IsActive() &&
+ !incremental_marking()->IsMarking()) {
+ return;
+ }
+
+ Object* context = native_contexts_list_;
+ while (!context->IsUndefined()) {
+ // 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);
+ }
+}
+
+
+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 > 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) {
+ set_survival_rate_trend(DECREASING);
+ } else if (survival_rate_diff < -kYoungSurvivalRateAllowedDeviation) {
+ set_survival_rate_trend(INCREASING);
+ } else {
+ set_survival_rate_trend(STABLE);
+ }
+
+ survival_rate_ = survival_rate;
+}
+
+bool Heap::PerformGarbageCollection(GarbageCollector collector,
+ GCTracer* tracer) {
+ bool next_gc_likely_to_collect_more = false;
+
+ if (collector != SCAVENGER) {
+ PROFILE(isolate_, CodeMovingGCEvent());
+ }
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ VerifyStringTable();
+ }
+#endif
+
+ GCType gc_type =
+ collector == MARK_COMPACTOR ? kGCTypeMarkSweepCompact : kGCTypeScavenge;
+
+ {
+ GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+ VMState<EXTERNAL> state(isolate_);
+ HandleScope handle_scope(isolate_);
+ CallGCPrologueCallbacks(gc_type, kNoGCCallbackFlags);
+ }
+
+ EnsureFromSpaceIsCommitted();
+
+ 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);
+ sweep_generation_++;
+
+ UpdateSurvivalRateTrend(start_new_space_size);
+
+ size_of_old_gen_at_last_old_space_gc_ = PromotedSpaceSizeOfObjects();
+
+ old_generation_allocation_limit_ =
+ OldGenerationAllocationLimit(size_of_old_gen_at_last_old_space_gc_);
+
+ old_gen_exhausted_ = false;
+ } else {
+ tracer_ = tracer;
+ Scavenge();
+ tracer_ = NULL;
+
+ 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.
+ SetNewSpaceHighPromotionModeActive(true);
+ if (FLAG_trace_gc) {
+ PrintPID("Limited new space size due to high promotion rate: %d MB\n",
+ new_space_.InitialCapacity() / MB);
+ }
+ // Support for global pre-tenuring uses the high promotion mode as a
+ // heuristic indicator of whether to pretenure or not, we trigger
+ // deoptimization here to take advantage of pre-tenuring as soon as
+ // possible.
+ if (FLAG_pretenuring) {
+ isolate_->stack_guard()->FullDeopt();
+ }
+ } 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.
+ SetNewSpaceHighPromotionModeActive(false);
+ if (FLAG_trace_gc) {
+ PrintPID("Unlimited new space size due to low promotion rate: %d MB\n",
+ new_space_.MaximumCapacity() / MB);
+ }
+ // Trigger deoptimization here to turn off pre-tenuring as soon as
+ // possible.
+ if (FLAG_pretenuring) {
+ isolate_->stack_guard()->FullDeopt();
+ }
+ }
+
+ if (new_space_high_promotion_mode_active_ &&
+ new_space_.Capacity() > new_space_.InitialCapacity()) {
+ new_space_.Shrink();
+ }
+
+ isolate_->counters()->objs_since_last_young()->Set(0);
+
+ // Callbacks that fire after this point might trigger nested GCs and
+ // restart incremental marking, the assertion can't be moved down.
+ ASSERT(collector == SCAVENGER || incremental_marking()->IsStopped());
+
+ gc_post_processing_depth_++;
+ { AllowHeapAllocation allow_allocation;
+ GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+ next_gc_likely_to_collect_more =
+ isolate_->global_handles()->PostGarbageCollectionProcessing(
+ collector, tracer);
+ }
+ gc_post_processing_depth_--;
+
+ isolate_->eternal_handles()->PostGarbageCollectionProcessing(this);
+
+ // Update relocatables.
+ Relocatable::PostGarbageCollectionProcessing();
+
+ if (collector == MARK_COMPACTOR) {
+ // Register the amount of external allocated memory.
+ amount_of_external_allocated_memory_at_last_global_gc_ =
+ amount_of_external_allocated_memory_;
+ }
+
+ {
+ GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+ VMState<EXTERNAL> state(isolate_);
+ HandleScope handle_scope(isolate_);
+ CallGCEpilogueCallbacks(gc_type);
+ }
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ VerifyStringTable();
+ }
+#endif
+
+ return next_gc_likely_to_collect_more;
+}
+
+
+void Heap::CallGCPrologueCallbacks(GCType gc_type, GCCallbackFlags flags) {
+ if (gc_type == kGCTypeMarkSweepCompact && global_gc_prologue_callback_) {
+ global_gc_prologue_callback_();
+ }
+ for (int i = 0; i < gc_prologue_callbacks_.length(); ++i) {
+ if (gc_type & gc_prologue_callbacks_[i].gc_type) {
+ gc_prologue_callbacks_[i].callback(gc_type, flags);
+ }
+ }
+}
+
+
+void Heap::CallGCEpilogueCallbacks(GCType gc_type) {
+ 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, kNoGCCallbackFlags);
+ }
+ }
+ if (gc_type == kGCTypeMarkSweepCompact && global_gc_epilogue_callback_) {
+ global_gc_epilogue_callback_();
+ }
+}
+
+
+void Heap::MarkCompact(GCTracer* tracer) {
+ gc_state_ = MARK_COMPACT;
+ LOG(isolate_, ResourceEvent("markcompact", "begin"));
+
+ mark_compact_collector_.Prepare(tracer);
+
+ ms_count_++;
+ tracer->set_full_gc_count(ms_count_);
+
+ MarkCompactPrologue();
+
+ mark_compact_collector_.CollectGarbage();
+
+ LOG(isolate_, ResourceEvent("markcompact", "end"));
+
+ gc_state_ = NOT_IN_GC;
+
+ isolate_->counters()->objs_since_last_full()->Set(0);
+
+ contexts_disposed_ = 0;
+
+ flush_monomorphic_ics_ = false;
+}
+
+
+void Heap::MarkCompactPrologue() {
+ // At any old GC clear the keyed lookup cache to enable collection of unused
+ // maps.
+ isolate_->keyed_lookup_cache()->Clear();
+ isolate_->context_slot_cache()->Clear();
+ isolate_->descriptor_lookup_cache()->Clear();
+ RegExpResultsCache::Clear(string_split_cache());
+ RegExpResultsCache::Clear(regexp_multiple_cache());
+
+ isolate_->compilation_cache()->MarkCompactPrologue();
+
+ CompletelyClearInstanceofCache();
+
+ FlushNumberStringCache();
+ if (FLAG_cleanup_code_caches_at_gc) {
+ polymorphic_code_cache()->set_cache(undefined_value());
+ }
+
+ ClearNormalizedMapCaches();
+}
+
+
+// Helper class for copying HeapObjects
+class ScavengeVisitor: public ObjectVisitor {
+ public:
+ explicit ScavengeVisitor(Heap* heap) : heap_(heap) {}
+
+ void VisitPointer(Object** p) { ScavengePointer(p); }
+
+ void VisitPointers(Object** start, Object** end) {
+ // Copy all HeapObject pointers in [start, end)
+ for (Object** p = start; p < end; p++) ScavengePointer(p);
+ }
+
+ private:
+ void ScavengePointer(Object** p) {
+ Object* object = *p;
+ if (!heap_->InNewSpace(object)) return;
+ Heap::ScavengeObject(reinterpret_cast<HeapObject**>(p),
+ reinterpret_cast<HeapObject*>(object));
+ }
+
+ Heap* heap_;
+};
+
+
+#ifdef VERIFY_HEAP
+// Visitor class to verify pointers in code or data space do not point into
+// new space.
+class VerifyNonPointerSpacePointersVisitor: public ObjectVisitor {
+ public:
+ void VisitPointers(Object** start, Object**end) {
+ for (Object** current = start; current < end; current++) {
+ if ((*current)->IsHeapObject()) {
+ CHECK(!HEAP->InNewSpace(HeapObject::cast(*current)));
+ }
+ }
+ }
+};
+
+
+static void VerifyNonPointerSpacePointers() {
+ // Verify that there are no pointers to new space in spaces where we
+ // do not expect them.
+ VerifyNonPointerSpacePointersVisitor v;
+ HeapObjectIterator code_it(HEAP->code_space());
+ for (HeapObject* object = code_it.Next();
+ object != NULL; object = code_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 // VERIFY_HEAP
+
+
+void Heap::CheckNewSpaceExpansionCriteria() {
+ if (new_space_.Capacity() < new_space_.MaximumCapacity() &&
+ 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;
+ }
+}
+
+
+static bool IsUnscavengedHeapObject(Heap* heap, Object** p) {
+ return heap->InNewSpace(*p) &&
+ !HeapObject::cast(*p)->map_word().IsForwardingAddress();
+}
+
+
+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 / 2);
+ } 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();
+ }
+}
+
+
+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->area_end()));
+
+ int entries_count =
+ static_cast<int>(head_end - head_start) / kEntrySizeInWords;
+
+ 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;
+}
+
+
+class ScavengeWeakObjectRetainer : public WeakObjectRetainer {
+ public:
+ explicit ScavengeWeakObjectRetainer(Heap* heap) : heap_(heap) { }
+
+ virtual Object* RetainAs(Object* object) {
+ if (!heap_->InFromSpace(object)) {
+ return object;
+ }
+
+ MapWord map_word = HeapObject::cast(object)->map_word();
+ if (map_word.IsForwardingAddress()) {
+ return map_word.ToForwardingAddress();
+ }
+ return NULL;
+ }
+
+ private:
+ Heap* heap_;
+};
+
+
+void Heap::Scavenge() {
+ RelocationLock relocation_lock(this);
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) VerifyNonPointerSpacePointers();
+#endif
+
+ gc_state_ = SCAVENGE;
+
+ // Implements Cheney's copying algorithm
+ LOG(isolate_, ResourceEvent("scavenge", "begin"));
+
+ // Clear descriptor cache.
+ isolate_->descriptor_lookup_cache()->Clear();
+
+ // Used for updating survived_since_last_expansion_ at function end.
+ intptr_t survived_watermark = PromotedSpaceSizeOfObjects();
+
+ CheckNewSpaceExpansionCriteria();
+
+ SelectScavengingVisitorsTable();
+
+ incremental_marking()->PrepareForScavenge();
+
+ paged_space(OLD_DATA_SPACE)->EnsureSweeperProgress(new_space_.Size());
+ paged_space(OLD_POINTER_SPACE)->EnsureSweeperProgress(new_space_.Size());
+
+ // Flip the semispaces. After flipping, to space is empty, from space has
+ // live objects.
+ new_space_.Flip();
+ new_space_.ResetAllocationInfo();
+
+ // We need to sweep newly copied objects which can be either in the
+ // to space or promoted to the old generation. For to-space
+ // objects, we treat the bottom of the to space as a queue. Newly
+ // copied and unswept objects lie between a 'front' mark and the
+ // allocation pointer.
+ //
+ // Promoted objects can go into various old-generation spaces, and
+ // can be allocated internally in the spaces (from the free list).
+ // We treat the top of the to space as a queue of addresses of
+ // promoted objects. The addresses of newly promoted and unswept
+ // objects lie between a 'front' mark and a 'rear' mark that is
+ // updated as a side effect of promoting an object.
+ //
+ // There is guaranteed to be enough room at the top of the to space
+ // 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_.ToSpaceStart();
+ promotion_queue_.Initialize();
+
+#ifdef DEBUG
+ store_buffer()->Clean();
+#endif
+
+ ScavengeVisitor scavenge_visitor(this);
+ // Copy roots.
+ IterateRoots(&scavenge_visitor, VISIT_ALL_IN_SCAVENGE);
+
+ // Copy objects reachable from the old generation.
+ {
+ StoreBufferRebuildScope scope(this,
+ store_buffer(),
+ &ScavengeStoreBufferCallback);
+ store_buffer()->IteratePointersToNewSpace(&ScavengeObject);
+ }
+
+ // Copy objects reachable from simple cells by scavenging cell values
+ // directly.
+ HeapObjectIterator cell_iterator(cell_space_);
+ for (HeapObject* heap_object = cell_iterator.Next();
+ heap_object != NULL;
+ heap_object = cell_iterator.Next()) {
+ if (heap_object->IsCell()) {
+ Cell* cell = Cell::cast(heap_object);
+ Address value_address = cell->ValueAddress();
+ scavenge_visitor.VisitPointer(reinterpret_cast<Object**>(value_address));
+ }
+ }
+
+ // Copy objects reachable from global property cells by scavenging global
+ // property cell values directly.
+ HeapObjectIterator js_global_property_cell_iterator(property_cell_space_);
+ for (HeapObject* heap_object = js_global_property_cell_iterator.Next();
+ heap_object != NULL;
+ heap_object = js_global_property_cell_iterator.Next()) {
+ if (heap_object->IsPropertyCell()) {
+ PropertyCell* cell = PropertyCell::cast(heap_object);
+ Address value_address = cell->ValueAddress();
+ scavenge_visitor.VisitPointer(reinterpret_cast<Object**>(value_address));
+ Address type_address = cell->TypeAddress();
+ scavenge_visitor.VisitPointer(reinterpret_cast<Object**>(type_address));
+ }
+ }
+
+ // Copy objects reachable from the code flushing candidates list.
+ MarkCompactCollector* collector = mark_compact_collector();
+ if (collector->is_code_flushing_enabled()) {
+ collector->code_flusher()->IteratePointersToFromSpace(&scavenge_visitor);
+ }
+
+ // Scavenge object reachable from the native contexts list directly.
+ scavenge_visitor.VisitPointer(BitCast<Object**>(&native_contexts_list_));
+
+ new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
+
+ while (isolate()->global_handles()->IterateObjectGroups(
+ &scavenge_visitor, &IsUnscavengedHeapObject)) {
+ new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
+ }
+ isolate()->global_handles()->RemoveObjectGroups();
+ isolate()->global_handles()->RemoveImplicitRefGroups();
+
+ isolate_->global_handles()->IdentifyNewSpaceWeakIndependentHandles(
+ &IsUnscavengedHeapObject);
+ isolate_->global_handles()->IterateNewSpaceWeakIndependentRoots(
+ &scavenge_visitor);
+ new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
+
+ UpdateNewSpaceReferencesInExternalStringTable(
+ &UpdateNewSpaceReferenceInExternalStringTableEntry);
+
+ promotion_queue_.Destroy();
+
+ if (!FLAG_watch_ic_patching) {
+ isolate()->runtime_profiler()->UpdateSamplesAfterScavenge();
+ }
+ incremental_marking()->UpdateMarkingDequeAfterScavenge();
+
+ ScavengeWeakObjectRetainer weak_object_retainer(this);
+ ProcessWeakReferences(&weak_object_retainer);
+
+ ASSERT(new_space_front == new_space_.top());
+
+ // 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>(
+ (PromotedSpaceSizeOfObjects() - survived_watermark) + new_space_.Size()));
+
+ LOG(isolate_, ResourceEvent("scavenge", "end"));
+
+ gc_state_ = NOT_IN_GC;
+
+ scavenges_since_last_idle_round_++;
+}
+
+
+String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap,
+ Object** p) {
+ MapWord first_word = HeapObject::cast(*p)->map_word();
+
+ if (!first_word.IsForwardingAddress()) {
+ // Unreachable external string can be finalized.
+ heap->FinalizeExternalString(String::cast(*p));
+ return NULL;
+ }
+
+ // String is still reachable.
+ return String::cast(first_word.ToForwardingAddress());
+}
+
+
+void Heap::UpdateNewSpaceReferencesInExternalStringTable(
+ ExternalStringTableUpdaterCallback updater_func) {
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ external_string_table_.Verify();
+ }
+#endif
+
+ if (external_string_table_.new_space_strings_.is_empty()) return;
+
+ Object** start = &external_string_table_.new_space_strings_[0];
+ Object** end = start + external_string_table_.new_space_strings_.length();
+ Object** last = start;
+
+ for (Object** p = start; p < end; ++p) {
+ ASSERT(InFromSpace(*p));
+ String* target = updater_func(this, p);
+
+ if (target == NULL) continue;
+
+ ASSERT(target->IsExternalString());
+
+ if (InNewSpace(target)) {
+ // String is still in new space. Update the table entry.
+ *last = target;
+ ++last;
+ } else {
+ // String got promoted. Move it to the old string list.
+ external_string_table_.AddOldString(target);
+ }
+ }
+
+ ASSERT(last <= end);
+ external_string_table_.ShrinkNewStrings(static_cast<int>(last - start));
+}
+
+
+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);
+}
+
+
+template <class T>
+struct WeakListVisitor;
+
+
+template <class T>
+static Object* VisitWeakList(Heap* heap,
+ Object* list,
+ WeakObjectRetainer* retainer,
+ bool record_slots) {
+ Object* undefined = heap->undefined_value();
+ Object* head = undefined;
+ T* tail = NULL;
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+ while (list != undefined) {
+ // Check whether to keep the candidate in the list.
+ T* candidate = reinterpret_cast<T*>(list);
+ Object* retained = retainer->RetainAs(list);
+ if (retained != NULL) {
+ if (head == undefined) {
+ // First element in the list.
+ head = retained;
+ } else {
+ // Subsequent elements in the list.
+ ASSERT(tail != NULL);
+ WeakListVisitor<T>::SetWeakNext(tail, retained);
+ if (record_slots) {
+ Object** next_slot =
+ HeapObject::RawField(tail, WeakListVisitor<T>::WeakNextOffset());
+ collector->RecordSlot(next_slot, next_slot, retained);
+ }
+ }
+ // Retained object is new tail.
+ ASSERT(!retained->IsUndefined());
+ candidate = reinterpret_cast<T*>(retained);
+ tail = candidate;
+
+
+ // tail is a live object, visit it.
+ WeakListVisitor<T>::VisitLiveObject(
+ heap, tail, retainer, record_slots);
+ } else {
+ WeakListVisitor<T>::VisitPhantomObject(heap, candidate);
+ }
+
+ // Move to next element in the list.
+ list = WeakListVisitor<T>::WeakNext(candidate);
+ }
+
+ // Terminate the list if there is one or more elements.
+ if (tail != NULL) {
+ WeakListVisitor<T>::SetWeakNext(tail, undefined);
+ }
+ return head;
+}
+
+
+template<>
+struct WeakListVisitor<JSFunction> {
+ static void SetWeakNext(JSFunction* function, Object* next) {
+ function->set_next_function_link(next);
+ }
+
+ static Object* WeakNext(JSFunction* function) {
+ return function->next_function_link();
+ }
+
+ static int WeakNextOffset() {
+ return JSFunction::kNextFunctionLinkOffset;
+ }
+
+ static void VisitLiveObject(Heap*, JSFunction*,
+ WeakObjectRetainer*, bool) {
+ }
+
+ static void VisitPhantomObject(Heap*, JSFunction*) {
+ }
+};
+
+
+template<>
+struct WeakListVisitor<Context> {
+ static void SetWeakNext(Context* context, Object* next) {
+ context->set(Context::NEXT_CONTEXT_LINK,
+ next,
+ UPDATE_WRITE_BARRIER);
+ }
+
+ static Object* WeakNext(Context* context) {
+ return context->get(Context::NEXT_CONTEXT_LINK);
+ }
+
+ static void VisitLiveObject(Heap* heap,
+ Context* context,
+ WeakObjectRetainer* retainer,
+ bool record_slots) {
+ // Process the weak list of optimized functions for the context.
+ Object* function_list_head =
+ VisitWeakList<JSFunction>(
+ heap,
+ context->get(Context::OPTIMIZED_FUNCTIONS_LIST),
+ retainer,
+ record_slots);
+ context->set(Context::OPTIMIZED_FUNCTIONS_LIST,
+ function_list_head,
+ UPDATE_WRITE_BARRIER);
+ if (record_slots) {
+ Object** optimized_functions =
+ HeapObject::RawField(
+ context, FixedArray::SizeFor(Context::OPTIMIZED_FUNCTIONS_LIST));
+ heap->mark_compact_collector()->RecordSlot(
+ optimized_functions, optimized_functions, function_list_head);
+ }
+ }
+
+ static void VisitPhantomObject(Heap*, Context*) {
+ }
+
+ static int WeakNextOffset() {
+ return FixedArray::SizeFor(Context::NEXT_CONTEXT_LINK);
+ }
+};
+
+
+void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) {
+ // We don't record weak slots during marking or scavenges.
+ // Instead we do it once when we complete mark-compact cycle.
+ // Note that write barrier has no effect if we are already in the middle of
+ // compacting mark-sweep cycle and we have to record slots manually.
+ bool record_slots =
+ gc_state() == MARK_COMPACT &&
+ mark_compact_collector()->is_compacting();
+ ProcessArrayBuffers(retainer, record_slots);
+ ProcessNativeContexts(retainer, record_slots);
+ ProcessAllocationSites(retainer, record_slots);
+}
+
+void Heap::ProcessNativeContexts(WeakObjectRetainer* retainer,
+ bool record_slots) {
+ Object* head =
+ VisitWeakList<Context>(
+ this, native_contexts_list(), retainer, record_slots);
+ // Update the head of the list of contexts.
+ native_contexts_list_ = head;
+}
+
+
+template<>
+struct WeakListVisitor<JSArrayBufferView> {
+ static void SetWeakNext(JSArrayBufferView* obj, Object* next) {
+ obj->set_weak_next(next);
+ }
+
+ static Object* WeakNext(JSArrayBufferView* obj) {
+ return obj->weak_next();
+ }
+
+ static void VisitLiveObject(Heap*,
+ JSArrayBufferView* obj,
+ WeakObjectRetainer* retainer,
+ bool record_slots) {}
+
+ static void VisitPhantomObject(Heap*, JSArrayBufferView*) {}
+
+ static int WeakNextOffset() {
+ return JSArrayBufferView::kWeakNextOffset;
+ }
+};
+
+
+template<>
+struct WeakListVisitor<JSArrayBuffer> {
+ static void SetWeakNext(JSArrayBuffer* obj, Object* next) {
+ obj->set_weak_next(next);
+ }
+
+ static Object* WeakNext(JSArrayBuffer* obj) {
+ return obj->weak_next();
+ }
+
+ static void VisitLiveObject(Heap* heap,
+ JSArrayBuffer* array_buffer,
+ WeakObjectRetainer* retainer,
+ bool record_slots) {
+ Object* typed_array_obj =
+ VisitWeakList<JSArrayBufferView>(
+ heap,
+ array_buffer->weak_first_view(),
+ retainer, record_slots);
+ array_buffer->set_weak_first_view(typed_array_obj);
+ if (typed_array_obj != heap->undefined_value() && record_slots) {
+ Object** slot = HeapObject::RawField(
+ array_buffer, JSArrayBuffer::kWeakFirstViewOffset);
+ heap->mark_compact_collector()->RecordSlot(slot, slot, typed_array_obj);
+ }
+ }
+
+ static void VisitPhantomObject(Heap* heap, JSArrayBuffer* phantom) {
+ Runtime::FreeArrayBuffer(heap->isolate(), phantom);
+ }
+
+ static int WeakNextOffset() {
+ return JSArrayBuffer::kWeakNextOffset;
+ }
+};
+
+
+void Heap::ProcessArrayBuffers(WeakObjectRetainer* retainer,
+ bool record_slots) {
+ Object* array_buffer_obj =
+ VisitWeakList<JSArrayBuffer>(this,
+ array_buffers_list(),
+ retainer, record_slots);
+ set_array_buffers_list(array_buffer_obj);
+}
+
+
+void Heap::TearDownArrayBuffers() {
+ Object* undefined = undefined_value();
+ for (Object* o = array_buffers_list(); o != undefined;) {
+ JSArrayBuffer* buffer = JSArrayBuffer::cast(o);
+ Runtime::FreeArrayBuffer(isolate(), buffer);
+ o = buffer->weak_next();
+ }
+ array_buffers_list_ = undefined;
+}
+
+
+template<>
+struct WeakListVisitor<AllocationSite> {
+ static void SetWeakNext(AllocationSite* obj, Object* next) {
+ obj->set_weak_next(next);
+ }
+
+ static Object* WeakNext(AllocationSite* obj) {
+ return obj->weak_next();
+ }
+
+ static void VisitLiveObject(Heap* heap,
+ AllocationSite* array_buffer,
+ WeakObjectRetainer* retainer,
+ bool record_slots) {}
+
+ static void VisitPhantomObject(Heap* heap, AllocationSite* phantom) {}
+
+ static int WeakNextOffset() {
+ return AllocationSite::kWeakNextOffset;
+ }
+};
+
+
+void Heap::ProcessAllocationSites(WeakObjectRetainer* retainer,
+ bool record_slots) {
+ Object* allocation_site_obj =
+ VisitWeakList<AllocationSite>(this,
+ allocation_sites_list(),
+ retainer, record_slots);
+ set_allocation_sites_list(allocation_site_obj);
+}
+
+
+void Heap::VisitExternalResources(v8::ExternalResourceVisitor* visitor) {
+ DisallowHeapAllocation no_allocation;
+
+ // Both the external string table and the string table may contain
+ // external strings, but neither lists them exhaustively, nor is the
+ // intersection set empty. Therefore we iterate over the external string
+ // table first, ignoring internalized strings, and then over the
+ // internalized string table.
+
+ class ExternalStringTableVisitorAdapter : public ObjectVisitor {
+ public:
+ explicit ExternalStringTableVisitorAdapter(
+ v8::ExternalResourceVisitor* visitor) : visitor_(visitor) {}
+ virtual void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) {
+ // Visit non-internalized external strings,
+ // since internalized strings are listed in the string table.
+ if (!(*p)->IsInternalizedString()) {
+ ASSERT((*p)->IsExternalString());
+ visitor_->VisitExternalString(Utils::ToLocal(
+ Handle<String>(String::cast(*p))));
+ }
+ }
+ }
+ private:
+ v8::ExternalResourceVisitor* visitor_;
+ } external_string_table_visitor(visitor);
+
+ external_string_table_.Iterate(&external_string_table_visitor);
+
+ class StringTableVisitorAdapter : public ObjectVisitor {
+ public:
+ explicit StringTableVisitorAdapter(
+ v8::ExternalResourceVisitor* visitor) : visitor_(visitor) {}
+ virtual void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) {
+ if ((*p)->IsExternalString()) {
+ ASSERT((*p)->IsInternalizedString());
+ visitor_->VisitExternalString(Utils::ToLocal(
+ Handle<String>(String::cast(*p))));
+ }
+ }
+ }
+ private:
+ v8::ExternalResourceVisitor* visitor_;
+ } string_table_visitor(visitor);
+
+ string_table()->IterateElements(&string_table_visitor);
+}
+
+
+class NewSpaceScavenger : public StaticNewSpaceVisitor<NewSpaceScavenger> {
+ public:
+ static inline void VisitPointer(Heap* heap, Object** p) {
+ Object* object = *p;
+ if (!heap->InNewSpace(object)) return;
+ Heap::ScavengeObject(reinterpret_cast<HeapObject**>(p),
+ reinterpret_cast<HeapObject*>(object));
+ }
+};
+
+
+Address Heap::DoScavenge(ObjectVisitor* scavenge_visitor,
+ Address new_space_front) {
+ do {
+ 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()) {
+ 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()->area_start();
+ }
+ }
+
+ // Promote and process all the to-be-promoted objects.
+ {
+ 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());
+
+ return new_space_front;
+}
+
+
+STATIC_ASSERT((FixedDoubleArray::kHeaderSize & kDoubleAlignmentMask) == 0);
+
+
+INLINE(static HeapObject* EnsureDoubleAligned(Heap* heap,
+ HeapObject* object,
+ int size));
+
+static HeapObject* EnsureDoubleAligned(Heap* heap,
+ HeapObject* object,
+ int size) {
+ if ((OffsetFrom(object->address()) & kDoubleAlignmentMask) != 0) {
+ heap->CreateFillerObjectAt(object->address(), kPointerSize);
+ return HeapObject::FromAddress(object->address() + kPointerSize);
+ } else {
+ heap->CreateFillerObjectAt(object->address() + size - kPointerSize,
+ kPointerSize);
+ return object;
+ }
+}
+
+
+enum LoggingAndProfiling {
+ LOGGING_AND_PROFILING_ENABLED,
+ LOGGING_AND_PROFILING_DISABLED
+};
+
+
+enum MarksHandling { TRANSFER_MARKS, IGNORE_MARKS };
+
+
+template<MarksHandling marks_handling,
+ LoggingAndProfiling logging_and_profiling_mode>
+class ScavengingVisitor : public StaticVisitorBase {
+ public:
+ static void Initialize() {
+ table_.Register(kVisitSeqOneByteString, &EvacuateSeqOneByteString);
+ table_.Register(kVisitSeqTwoByteString, &EvacuateSeqTwoByteString);
+ table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate);
+ table_.Register(kVisitByteArray, &EvacuateByteArray);
+ table_.Register(kVisitFixedArray, &EvacuateFixedArray);
+ table_.Register(kVisitFixedDoubleArray, &EvacuateFixedDoubleArray);
+
+ table_.Register(kVisitNativeContext,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ template VisitSpecialized<Context::kSize>);
+
+ table_.Register(kVisitConsString,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ template VisitSpecialized<ConsString::kSize>);
+
+ table_.Register(kVisitSlicedString,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ template VisitSpecialized<SlicedString::kSize>);
+
+ table_.Register(kVisitSymbol,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ template VisitSpecialized<Symbol::kSize>);
+
+ table_.Register(kVisitSharedFunctionInfo,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ template VisitSpecialized<SharedFunctionInfo::kSize>);
+
+ table_.Register(kVisitJSWeakMap,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ Visit);
+
+ table_.Register(kVisitJSWeakSet,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ Visit);
+
+ table_.Register(kVisitJSArrayBuffer,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ Visit);
+
+ table_.Register(kVisitJSTypedArray,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ Visit);
+
+ table_.Register(kVisitJSDataView,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ Visit);
+
+ table_.Register(kVisitJSRegExp,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ Visit);
+
+ 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,
+ kVisitDataObjectGeneric>();
+
+ table_.RegisterSpecializations<ObjectEvacuationStrategy<POINTER_OBJECT>,
+ kVisitJSObject,
+ kVisitJSObjectGeneric>();
+
+ table_.RegisterSpecializations<ObjectEvacuationStrategy<POINTER_OBJECT>,
+ kVisitStruct,
+ kVisitStructGeneric>();
+ }
+
+ static VisitorDispatchTable<ScavengingCallback>* GetTable() {
+ return &table_;
+ }
+
+ private:
+ enum ObjectContents { DATA_OBJECT, POINTER_OBJECT };
+
+ static void RecordCopiedObject(Heap* heap, HeapObject* obj) {
+ bool should_record = false;
+#ifdef DEBUG
+ should_record = FLAG_heap_stats;
+#endif
+ should_record = should_record || FLAG_log_gc;
+ if (should_record) {
+ if (heap->new_space()->Contains(obj)) {
+ heap->new_space()->RecordAllocation(obj);
+ } else {
+ heap->new_space()->RecordPromotion(obj);
+ }
+ }
+ }
+
+ // 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 void MigrateObject(Heap* heap,
+ HeapObject* source,
+ HeapObject* target,
+ int size)) {
+ // Copy the content of source to target.
+ heap->CopyBlock(target->address(), source->address(), size);
+
+ // Set the forwarding address.
+ source->set_map_word(MapWord::FromForwardingAddress(target));
+
+ if (logging_and_profiling_mode == LOGGING_AND_PROFILING_ENABLED) {
+ // Update NewSpace stats if necessary.
+ RecordCopiedObject(heap, target);
+ HEAP_PROFILE(heap, ObjectMoveEvent(source->address(), target->address()));
+ Isolate* isolate = heap->isolate();
+ if (isolate->logger()->is_logging_code_events() ||
+ isolate->cpu_profiler()->is_profiling()) {
+ if (target->IsSharedFunctionInfo()) {
+ PROFILE(isolate, SharedFunctionInfoMoveEvent(
+ source->address(), target->address()));
+ }
+ }
+ }
+
+ if (marks_handling == TRANSFER_MARKS) {
+ if (Marking::TransferColor(source, target)) {
+ MemoryChunk::IncrementLiveBytesFromGC(target->address(), size);
+ }
+ }
+ }
+
+
+ template<ObjectContents object_contents, int alignment>
+ static inline void EvacuateObject(Map* map,
+ HeapObject** slot,
+ HeapObject* object,
+ int object_size) {
+ SLOW_ASSERT(object_size <= Page::kMaxNonCodeHeapObjectSize);
+ SLOW_ASSERT(object->Size() == object_size);
+
+ int allocation_size = object_size;
+ if (alignment != kObjectAlignment) {
+ ASSERT(alignment == kDoubleAlignment);
+ allocation_size += kPointerSize;
+ }
+
+ Heap* heap = map->GetHeap();
+ if (heap->ShouldBePromoted(object->address(), object_size)) {
+ MaybeObject* maybe_result;
+
+ if (object_contents == DATA_OBJECT) {
+ maybe_result = heap->old_data_space()->AllocateRaw(allocation_size);
+ } else {
+ maybe_result =
+ heap->old_pointer_space()->AllocateRaw(allocation_size);
+ }
+
+ Object* result = NULL; // Initialization to please compiler.
+ if (maybe_result->ToObject(&result)) {
+ HeapObject* target = HeapObject::cast(result);
+
+ if (alignment != kObjectAlignment) {
+ target = EnsureDoubleAligned(heap, target, allocation_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) {
+ if (map->instance_type() == JS_FUNCTION_TYPE) {
+ heap->promotion_queue()->insert(
+ target, JSFunction::kNonWeakFieldsEndOffset);
+ } else {
+ heap->promotion_queue()->insert(target, object_size);
+ }
+ }
+
+ heap->tracer()->increment_promoted_objects_size(object_size);
+ return;
+ }
+ }
+ MaybeObject* allocation = heap->new_space()->AllocateRaw(allocation_size);
+ heap->promotion_queue()->SetNewLimit(heap->new_space()->top());
+ Object* result = allocation->ToObjectUnchecked();
+ HeapObject* target = HeapObject::cast(result);
+
+ if (alignment != kObjectAlignment) {
+ target = EnsureDoubleAligned(heap, target, allocation_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);
+ 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) {
+ int object_size = FixedArray::BodyDescriptor::SizeOf(map, object);
+ EvacuateObject<POINTER_OBJECT, kObjectAlignment>(
+ map, slot, object, object_size);
+ }
+
+
+ static inline void EvacuateFixedDoubleArray(Map* map,
+ HeapObject** slot,
+ HeapObject* object) {
+ int length = reinterpret_cast<FixedDoubleArray*>(object)->length();
+ int object_size = FixedDoubleArray::SizeFor(length);
+ EvacuateObject<DATA_OBJECT, kDoubleAlignment>(
+ map, slot, object, object_size);
+ }
+
+
+ static inline void EvacuateByteArray(Map* map,
+ HeapObject** slot,
+ HeapObject* object) {
+ int object_size = reinterpret_cast<ByteArray*>(object)->ByteArraySize();
+ EvacuateObject<DATA_OBJECT, kObjectAlignment>(
+ map, slot, object, object_size);
+ }
+
+
+ static inline void EvacuateSeqOneByteString(Map* map,
+ HeapObject** slot,
+ HeapObject* object) {
+ int object_size = SeqOneByteString::cast(object)->
+ SeqOneByteStringSize(map->instance_type());
+ EvacuateObject<DATA_OBJECT, kObjectAlignment>(
+ map, slot, object, object_size);
+ }
+
+
+ static inline void EvacuateSeqTwoByteString(Map* map,
+ HeapObject** slot,
+ HeapObject* object) {
+ int object_size = SeqTwoByteString::cast(object)->
+ SeqTwoByteStringSize(map->instance_type());
+ EvacuateObject<DATA_OBJECT, kObjectAlignment>(
+ map, slot, object, object_size);
+ }
+
+
+ static inline bool IsShortcutCandidate(int type) {
+ return ((type & kShortcutTypeMask) == kShortcutTypeTag);
+ }
+
+ static inline void EvacuateShortcutCandidate(Map* map,
+ HeapObject** slot,
+ HeapObject* object) {
+ ASSERT(IsShortcutCandidate(map->instance_type()));
+
+ 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 (!heap->InNewSpace(first)) {
+ object->set_map_word(MapWord::FromForwardingAddress(first));
+ return;
+ }
+
+ MapWord first_word = first->map_word();
+ if (first_word.IsForwardingAddress()) {
+ HeapObject* target = first_word.ToForwardingAddress();
+
+ *slot = target;
+ object->set_map_word(MapWord::FromForwardingAddress(target));
+ return;
+ }
+
+ heap->DoScavengeObject(first->map(), slot, first);
+ object->set_map_word(MapWord::FromForwardingAddress(*slot));
+ return;
+ }
+
+ int object_size = ConsString::kSize;
+ EvacuateObject<POINTER_OBJECT, kObjectAlignment>(
+ map, slot, object, object_size);
+ }
+
+ template<ObjectContents object_contents>
+ class ObjectEvacuationStrategy {
+ public:
+ template<int object_size>
+ static inline void VisitSpecialized(Map* map,
+ HeapObject** slot,
+ HeapObject* object) {
+ EvacuateObject<object_contents, kObjectAlignment>(
+ map, slot, object, object_size);
+ }
+
+ static inline void Visit(Map* map,
+ HeapObject** slot,
+ HeapObject* object) {
+ int object_size = map->instance_size();
+ EvacuateObject<object_contents, kObjectAlignment>(
+ map, slot, object, object_size);
+ }
+ };
+
+ static VisitorDispatchTable<ScavengingCallback> table_;
+};
+
+
+template<MarksHandling marks_handling,
+ LoggingAndProfiling logging_and_profiling_mode>
+VisitorDispatchTable<ScavengingCallback>
+ ScavengingVisitor<marks_handling, logging_and_profiling_mode>::table_;
+
+
+static void InitializeScavengingVisitorsTables() {
+ 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::SelectScavengingVisitorsTable() {
+ bool logging_and_profiling =
+ isolate()->logger()->is_logging() ||
+ isolate()->cpu_profiler()->is_profiling() ||
+ (isolate()->heap_profiler() != NULL &&
+ 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());
+ }
+
+ 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) {
+ SLOW_ASSERT(HEAP->InFromSpace(object));
+ MapWord first_word = object->map_word();
+ SLOW_ASSERT(!first_word.IsForwardingAddress());
+ Map* map = first_word.ToMap();
+ map->GetHeap()->DoScavengeObject(map, p, object);
+}
+
+
+MaybeObject* Heap::AllocatePartialMap(InstanceType instance_type,
+ int instance_size) {
+ Object* result;
+ MaybeObject* maybe_result = AllocateRawMap();
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+
+ // Map::cast cannot be used due to uninitialized map field.
+ reinterpret_cast<Map*>(result)->set_map(raw_unchecked_meta_map());
+ reinterpret_cast<Map*>(result)->set_instance_type(instance_type);
+ reinterpret_cast<Map*>(result)->set_instance_size(instance_size);
+ reinterpret_cast<Map*>(result)->set_visitor_id(
+ StaticVisitorBase::GetVisitorId(instance_type, instance_size));
+ reinterpret_cast<Map*>(result)->set_inobject_properties(0);
+ reinterpret_cast<Map*>(result)->set_pre_allocated_property_fields(0);
+ reinterpret_cast<Map*>(result)->set_unused_property_fields(0);
+ reinterpret_cast<Map*>(result)->set_bit_field(0);
+ reinterpret_cast<Map*>(result)->set_bit_field2(0);
+ int bit_field3 = Map::EnumLengthBits::encode(Map::kInvalidEnumCache) |
+ Map::OwnsDescriptors::encode(true);
+ reinterpret_cast<Map*>(result)->set_bit_field3(bit_field3);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateMap(InstanceType instance_type,
+ int instance_size,
+ ElementsKind elements_kind) {
+ Object* result;
+ MaybeObject* maybe_result = AllocateRawMap();
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ Map* map = reinterpret_cast<Map*>(result);
+ 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(), 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->set_code_cache(empty_fixed_array(), SKIP_WRITE_BARRIER);
+ map->set_dependent_code(DependentCode::cast(empty_fixed_array()),
+ SKIP_WRITE_BARRIER);
+ map->init_back_pointer(undefined_value());
+ map->set_unused_property_fields(0);
+ map->set_instance_descriptors(empty_descriptor_array());
+ map->set_bit_field(0);
+ map->set_bit_field2(1 << Map::kIsExtensible);
+ int bit_field3 = Map::EnumLengthBits::encode(Map::kInvalidEnumCache) |
+ Map::OwnsDescriptors::encode(true);
+ map->set_bit_field3(bit_field3);
+ map->set_elements_kind(elements_kind);
+
+ return map;
+}
+
+
+MaybeObject* Heap::AllocateCodeCache() {
+ CodeCache* code_cache;
+ { MaybeObject* maybe_code_cache = AllocateStruct(CODE_CACHE_TYPE);
+ if (!maybe_code_cache->To(&code_cache)) return maybe_code_cache;
+ }
+ 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;
+}
+
+
+MaybeObject* Heap::AllocatePolymorphicCodeCache() {
+ return AllocateStruct(POLYMORPHIC_CODE_CACHE_TYPE);
+}
+
+
+MaybeObject* Heap::AllocateAccessorPair() {
+ AccessorPair* accessors;
+ { MaybeObject* maybe_accessors = AllocateStruct(ACCESSOR_PAIR_TYPE);
+ if (!maybe_accessors->To(&accessors)) return maybe_accessors;
+ }
+ accessors->set_getter(the_hole_value(), SKIP_WRITE_BARRIER);
+ accessors->set_setter(the_hole_value(), SKIP_WRITE_BARRIER);
+ return accessors;
+}
+
+
+MaybeObject* Heap::AllocateTypeFeedbackInfo() {
+ TypeFeedbackInfo* info;
+ { MaybeObject* maybe_info = AllocateStruct(TYPE_FEEDBACK_INFO_TYPE);
+ if (!maybe_info->To(&info)) return maybe_info;
+ }
+ info->initialize_storage();
+ info->set_type_feedback_cells(TypeFeedbackCells::cast(empty_fixed_array()),
+ SKIP_WRITE_BARRIER);
+ return info;
+}
+
+
+MaybeObject* Heap::AllocateAliasedArgumentsEntry(int aliased_context_slot) {
+ AliasedArgumentsEntry* entry;
+ { MaybeObject* maybe_entry = AllocateStruct(ALIASED_ARGUMENTS_ENTRY_TYPE);
+ if (!maybe_entry->To(&entry)) return maybe_entry;
+ }
+ entry->set_aliased_context_slot(aliased_context_slot);
+ return entry;
+}
+
+
+const Heap::StringTypeTable Heap::string_type_table[] = {
+#define STRING_TYPE_ELEMENT(type, size, name, camel_name) \
+ {type, size, k##camel_name##MapRootIndex},
+ STRING_TYPE_LIST(STRING_TYPE_ELEMENT)
+#undef STRING_TYPE_ELEMENT
+};
+
+
+const Heap::ConstantStringTable Heap::constant_string_table[] = {
+#define CONSTANT_STRING_ELEMENT(name, contents) \
+ {contents, k##name##RootIndex},
+ INTERNALIZED_STRING_LIST(CONSTANT_STRING_ELEMENT)
+#undef CONSTANT_STRING_ELEMENT
+};
+
+
+const Heap::StructTable Heap::struct_table[] = {
+#define STRUCT_TABLE_ELEMENT(NAME, Name, name) \
+ { NAME##_TYPE, Name::kSize, k##Name##MapRootIndex },
+ STRUCT_LIST(STRUCT_TABLE_ELEMENT)
+#undef STRUCT_TABLE_ELEMENT
+};
+
+
+bool Heap::CreateInitialMaps() {
+ Object* obj;
+ { MaybeObject* maybe_obj = AllocatePartialMap(MAP_TYPE, Map::kSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ // Map::cast cannot be used due to uninitialized map field.
+ Map* new_meta_map = reinterpret_cast<Map*>(obj);
+ set_meta_map(new_meta_map);
+ new_meta_map->set_map(new_meta_map);
+
+ { MaybeObject* maybe_obj =
+ AllocatePartialMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_fixed_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocatePartialMap(ODDBALL_TYPE, Oddball::kSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_oddball_map(Map::cast(obj));
+
+ // Allocate the empty array.
+ { MaybeObject* maybe_obj = AllocateEmptyFixedArray();
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_fixed_array(FixedArray::cast(obj));
+
+ { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_POINTER_SPACE);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ 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;
+ }
+ set_empty_descriptor_array(DescriptorArray::cast(obj));
+
+ // Fix the instance_descriptors for the existing maps.
+ meta_map()->set_code_cache(empty_fixed_array());
+ meta_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
+ meta_map()->init_back_pointer(undefined_value());
+ meta_map()->set_instance_descriptors(empty_descriptor_array());
+
+ fixed_array_map()->set_code_cache(empty_fixed_array());
+ fixed_array_map()->set_dependent_code(
+ DependentCode::cast(empty_fixed_array()));
+ fixed_array_map()->init_back_pointer(undefined_value());
+ fixed_array_map()->set_instance_descriptors(empty_descriptor_array());
+
+ oddball_map()->set_code_cache(empty_fixed_array());
+ oddball_map()->set_dependent_code(DependentCode::cast(empty_fixed_array()));
+ oddball_map()->init_back_pointer(undefined_value());
+ oddball_map()->set_instance_descriptors(empty_descriptor_array());
+
+ // Fix prototype object for existing maps.
+ meta_map()->set_prototype(null_value());
+ meta_map()->set_constructor(null_value());
+
+ fixed_array_map()->set_prototype(null_value());
+ fixed_array_map()->set_constructor(null_value());
+
+ oddball_map()->set_prototype(null_value());
+ oddball_map()->set_constructor(null_value());
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_fixed_cow_array_map(Map::cast(obj));
+ ASSERT(fixed_array_map() != fixed_cow_array_map());
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_scope_info_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(HEAP_NUMBER_TYPE, HeapNumber::kSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_heap_number_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(SYMBOL_TYPE, Symbol::kSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_symbol_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(FOREIGN_TYPE, Foreign::kSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_foreign_map(Map::cast(obj));
+
+ for (unsigned i = 0; i < ARRAY_SIZE(string_type_table); i++) {
+ const StringTypeTable& entry = string_type_table[i];
+ { MaybeObject* maybe_obj = AllocateMap(entry.type, entry.size);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ roots_[entry.index] = Map::cast(obj);
+ }
+
+ { MaybeObject* maybe_obj = AllocateMap(STRING_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_undetectable_string_map(Map::cast(obj));
+ Map::cast(obj)->set_is_undetectable();
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(ASCII_STRING_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_undetectable_ascii_string_map(Map::cast(obj));
+ Map::cast(obj)->set_is_undetectable();
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_DOUBLE_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_fixed_double_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(BYTE_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ 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;
+ }
+ set_empty_byte_array(ByteArray::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(EXTERNAL_PIXEL_ARRAY_TYPE, ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_pixel_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_BYTE_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_byte_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_unsigned_byte_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_SHORT_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_short_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_unsigned_short_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_INT_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_int_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_unsigned_int_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_FLOAT_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_float_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_non_strict_arguments_elements_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_DOUBLE_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_double_array_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateEmptyExternalArray(kExternalByteArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_byte_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateEmptyExternalArray(kExternalUnsignedByteArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_unsigned_byte_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateEmptyExternalArray(kExternalShortArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_short_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateEmptyExternalArray(
+ kExternalUnsignedShortArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_unsigned_short_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateEmptyExternalArray(kExternalIntArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_int_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateEmptyExternalArray(kExternalUnsignedIntArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_unsigned_int_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateEmptyExternalArray(kExternalFloatArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_float_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateEmptyExternalArray(kExternalDoubleArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_double_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateEmptyExternalArray(kExternalPixelArray);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_empty_external_pixel_array(ExternalArray::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(CODE_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_code_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(CELL_TYPE, Cell::kSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_cell_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(PROPERTY_CELL_TYPE,
+ PropertyCell::kSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_global_property_cell_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(FILLER_TYPE, kPointerSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_one_pointer_filler_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(FILLER_TYPE, 2 * kPointerSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_two_pointer_filler_map(Map::cast(obj));
+
+ for (unsigned i = 0; i < ARRAY_SIZE(struct_table); i++) {
+ const StructTable& entry = struct_table[i];
+ { MaybeObject* maybe_obj = AllocateMap(entry.type, entry.size);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ roots_[entry.index] = Map::cast(obj);
+ }
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_hash_table_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_function_context_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_catch_context_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_with_context_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_block_context_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_module_context_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_global_context_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj =
+ AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ Map* native_context_map = Map::cast(obj);
+ native_context_map->set_dictionary_map(true);
+ native_context_map->set_visitor_id(StaticVisitorBase::kVisitNativeContext);
+ set_native_context_map(native_context_map);
+
+ { MaybeObject* maybe_obj = AllocateMap(SHARED_FUNCTION_INFO_TYPE,
+ SharedFunctionInfo::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_shared_function_info_map(Map::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateMap(JS_MESSAGE_OBJECT_TYPE,
+ JSMessageObject::kSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_message_object_map(Map::cast(obj));
+
+ Map* external_map;
+ { MaybeObject* maybe_obj =
+ AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize + kPointerSize);
+ if (!maybe_obj->To(&external_map)) return false;
+ }
+ external_map->set_is_extensible(false);
+ set_external_map(external_map);
+
+ ASSERT(!InNewSpace(empty_fixed_array()));
+ return true;
+}
+
+
+MaybeObject* Heap::AllocateHeapNumber(double value, PretenureFlag pretenure) {
+ // Statically ensure that it is safe to allocate heap numbers in paged
+ // spaces.
+ STATIC_ASSERT(HeapNumber::kSize <= Page::kNonCodeObjectAreaSize);
+ AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateRaw(HeapNumber::kSize, space, OLD_DATA_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ HeapObject::cast(result)->set_map_no_write_barrier(heap_number_map());
+ HeapNumber::cast(result)->set_value(value);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateHeapNumber(double value) {
+ // Use general version, if we're forced to always allocate.
+ if (always_allocate()) return AllocateHeapNumber(value, TENURED);
+
+ // This version of AllocateHeapNumber is optimized for
+ // allocation in new space.
+ STATIC_ASSERT(HeapNumber::kSize <= Page::kMaxNonCodeHeapObjectSize);
+ Object* result;
+ { MaybeObject* maybe_result = new_space_.AllocateRaw(HeapNumber::kSize);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ HeapObject::cast(result)->set_map_no_write_barrier(heap_number_map());
+ HeapNumber::cast(result)->set_value(value);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateCell(Object* value) {
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRawCell();
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ HeapObject::cast(result)->set_map_no_write_barrier(cell_map());
+ Cell::cast(result)->set_value(value);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocatePropertyCell(Object* value) {
+ Object* result;
+ MaybeObject* maybe_result = AllocateRawPropertyCell();
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+
+ HeapObject::cast(result)->set_map_no_write_barrier(
+ global_property_cell_map());
+ PropertyCell* cell = PropertyCell::cast(result);
+ cell->set_dependent_code(DependentCode::cast(empty_fixed_array()),
+ SKIP_WRITE_BARRIER);
+ cell->set_value(value);
+ cell->set_type(Type::None());
+ maybe_result = cell->SetValueInferType(value);
+ if (maybe_result->IsFailure()) return maybe_result;
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateBox(Object* value, PretenureFlag pretenure) {
+ Box* result;
+ MaybeObject* maybe_result = AllocateStruct(BOX_TYPE);
+ if (!maybe_result->To(&result)) return maybe_result;
+ result->set_value(value);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateAllocationSite() {
+ Object* result;
+ MaybeObject* maybe_result = Allocate(allocation_site_map(),
+ OLD_POINTER_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ AllocationSite* site = AllocationSite::cast(result);
+ site->Initialize();
+
+ // Link the site
+ site->set_weak_next(allocation_sites_list());
+ set_allocation_sites_list(site);
+ return result;
+}
+
+
+MaybeObject* Heap::CreateOddball(const char* to_string,
+ Object* to_number,
+ byte kind) {
+ Object* result;
+ { 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);
+}
+
+
+bool Heap::CreateApiObjects() {
+ Object* obj;
+
+ { MaybeObject* maybe_obj = AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ // 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(TERMINAL_FAST_ELEMENTS_KIND);
+ set_neander_map(new_neander_map);
+
+ { MaybeObject* maybe_obj = AllocateJSObjectFromMap(neander_map());
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ Object* elements;
+ { MaybeObject* maybe_elements = AllocateFixedArray(2);
+ if (!maybe_elements->ToObject(&elements)) return false;
+ }
+ FixedArray::cast(elements)->set(0, Smi::FromInt(0));
+ JSObject::cast(obj)->set_elements(FixedArray::cast(elements));
+ set_message_listeners(JSObject::cast(obj));
+
+ return true;
+}
+
+
+void Heap::CreateJSEntryStub() {
+ JSEntryStub stub;
+ set_js_entry_code(*stub.GetCode(isolate()));
+}
+
+
+void Heap::CreateJSConstructEntryStub() {
+ JSConstructEntryStub stub;
+ set_js_construct_entry_code(*stub.GetCode(isolate()));
+}
+
+
+void Heap::CreateFixedStubs() {
+ // Here we create roots for fixed stubs. They are needed at GC
+ // for cooking and uncooking (check out frames.cc).
+ // The eliminates the need for doing dictionary lookup in the
+ // stub cache for these stubs.
+ HandleScope scope(isolate());
+ // gcc-4.4 has problem generating correct code of following snippet:
+ // { JSEntryStub stub;
+ // js_entry_code_ = *stub.GetCode();
+ // }
+ // { JSConstructEntryStub stub;
+ // js_construct_entry_code_ = *stub.GetCode();
+ // }
+ // 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(isolate());
+}
+
+
+bool Heap::CreateInitialObjects() {
+ Object* obj;
+
+ // The -0 value must be set before NumberFromDouble works.
+ { MaybeObject* maybe_obj = AllocateHeapNumber(-0.0, TENURED);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_minus_zero_value(HeapNumber::cast(obj));
+ ASSERT(std::signbit(minus_zero_value()->Number()) != 0);
+
+ { MaybeObject* maybe_obj = AllocateHeapNumber(OS::nan_value(), TENURED);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_nan_value(HeapNumber::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateHeapNumber(V8_INFINITY, TENURED);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ 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 string table, so lets make that Smi zero.
+ set_the_hole_value(reinterpret_cast<Oddball*>(Smi::FromInt(0)));
+
+ // Allocate initial string table.
+ { MaybeObject* maybe_obj =
+ StringTable::Allocate(this, kInitialStringTableSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ // Don't use set_string_table() due to asserts.
+ roots_[kStringTableRootIndex] = obj;
+
+ // Finish initializing oddballs after creating the string table.
+ { MaybeObject* maybe_obj =
+ undefined_value()->Initialize("undefined",
+ nan_value(),
+ Oddball::kUndefined);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+
+ // Initialize the null_value.
+ { MaybeObject* maybe_obj =
+ null_value()->Initialize("null", Smi::FromInt(0), Oddball::kNull);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+
+ { MaybeObject* maybe_obj = CreateOddball("true",
+ Smi::FromInt(1),
+ Oddball::kTrue);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ 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(Oddball::cast(obj));
+
+ { MaybeObject* maybe_obj = CreateOddball("hole",
+ Smi::FromInt(-1),
+ Oddball::kTheHole);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_the_hole_value(Oddball::cast(obj));
+
+ { MaybeObject* maybe_obj = CreateOddball("uninitialized",
+ Smi::FromInt(-1),
+ Oddball::kUninitialized);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_uninitialized_value(Oddball::cast(obj));
+
+ { MaybeObject* maybe_obj = CreateOddball("arguments_marker",
+ Smi::FromInt(-4),
+ Oddball::kArgumentMarker);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_arguments_marker(Oddball::cast(obj));
+
+ { MaybeObject* maybe_obj = CreateOddball("no_interceptor_result_sentinel",
+ Smi::FromInt(-2),
+ Oddball::kOther);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_no_interceptor_result_sentinel(obj);
+
+ { MaybeObject* maybe_obj = CreateOddball("termination_exception",
+ Smi::FromInt(-3),
+ Oddball::kOther);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_termination_exception(obj);
+
+ for (unsigned i = 0; i < ARRAY_SIZE(constant_string_table); i++) {
+ { MaybeObject* maybe_obj =
+ InternalizeUtf8String(constant_string_table[i].contents);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ roots_[constant_string_table[i].index] = String::cast(obj);
+ }
+
+ // Allocate the hidden string which is used to identify the hidden properties
+ // in JSObjects. The hash code has a special value so that it will not match
+ // the empty string when searching for the property. It cannot be part of the
+ // loop above because it needs to be allocated manually with the special
+ // hash code in place. The hash code for the hidden_string is zero to ensure
+ // that it will always be at the first entry in property descriptors.
+ { MaybeObject* maybe_obj = AllocateOneByteInternalizedString(
+ OneByteVector("", 0), String::kEmptyStringHash);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ hidden_string_ = String::cast(obj);
+
+ // Allocate the code_stubs dictionary. The initial size is set to avoid
+ // expanding the dictionary during bootstrapping.
+ { MaybeObject* maybe_obj = UnseededNumberDictionary::Allocate(this, 128);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ 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(this, 64);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_non_monomorphic_cache(UnseededNumberDictionary::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocatePolymorphicCodeCache();
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_polymorphic_code_cache(PolymorphicCodeCache::cast(obj));
+
+ set_instanceof_cache_function(Smi::FromInt(0));
+ set_instanceof_cache_map(Smi::FromInt(0));
+ set_instanceof_cache_answer(Smi::FromInt(0));
+
+ CreateFixedStubs();
+
+ // Allocate the dictionary of intrinsic function names.
+ { MaybeObject* maybe_obj =
+ NameDictionary::Allocate(this, Runtime::kNumFunctions);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ { MaybeObject* maybe_obj = Runtime::InitializeIntrinsicFunctionNames(this,
+ obj);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_intrinsic_function_names(NameDictionary::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateInitialNumberStringCache();
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_number_string_cache(FixedArray::cast(obj));
+
+ // Allocate cache for single character one byte strings.
+ { MaybeObject* maybe_obj =
+ AllocateFixedArray(String::kMaxOneByteCharCode + 1, TENURED);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_single_character_string_cache(FixedArray::cast(obj));
+
+ // Allocate cache for string split.
+ { MaybeObject* maybe_obj = AllocateFixedArray(
+ RegExpResultsCache::kRegExpResultsCacheSize, TENURED);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_string_split_cache(FixedArray::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateFixedArray(
+ RegExpResultsCache::kRegExpResultsCacheSize, TENURED);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_regexp_multiple_cache(FixedArray::cast(obj));
+
+ // Allocate cache for external strings pointing to native source code.
+ { MaybeObject* maybe_obj = AllocateFixedArray(Natives::GetBuiltinsCount());
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_natives_source_cache(FixedArray::cast(obj));
+
+ // Allocate object to hold object observation state.
+ { MaybeObject* maybe_obj = AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ { MaybeObject* maybe_obj = AllocateJSObjectFromMap(Map::cast(obj));
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_observation_state(JSObject::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateSymbol();
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_frozen_symbol(Symbol::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateSymbol();
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_elements_transition_symbol(Symbol::cast(obj));
+
+ { MaybeObject* maybe_obj = SeededNumberDictionary::Allocate(this, 0, TENURED);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ SeededNumberDictionary::cast(obj)->set_requires_slow_elements();
+ set_empty_slow_element_dictionary(SeededNumberDictionary::cast(obj));
+
+ { MaybeObject* maybe_obj = AllocateSymbol();
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_observed_symbol(Symbol::cast(obj));
+
+ // Handling of script id generation is in Factory::NewScript.
+ set_last_script_id(Smi::FromInt(v8::Script::kNoScriptId));
+
+ // Initialize keyed lookup cache.
+ isolate_->keyed_lookup_cache()->Clear();
+
+ // Initialize context slot cache.
+ isolate_->context_slot_cache()->Clear();
+
+ // Initialize descriptor cache.
+ isolate_->descriptor_lookup_cache()->Clear();
+
+ // Initialize compilation cache.
+ isolate_->compilation_cache()->Clear();
+
+ return true;
+}
+
+
+bool Heap::RootCanBeWrittenAfterInitialization(Heap::RootListIndex root_index) {
+ RootListIndex writable_roots[] = {
+ kStoreBufferTopRootIndex,
+ kStackLimitRootIndex,
+ kNumberStringCacheRootIndex,
+ kInstanceofCacheFunctionRootIndex,
+ kInstanceofCacheMapRootIndex,
+ kInstanceofCacheAnswerRootIndex,
+ kCodeStubsRootIndex,
+ kNonMonomorphicCacheRootIndex,
+ kPolymorphicCodeCacheRootIndex,
+ kLastScriptIdRootIndex,
+ kEmptyScriptRootIndex,
+ kRealStackLimitRootIndex,
+ kArgumentsAdaptorDeoptPCOffsetRootIndex,
+ kConstructStubDeoptPCOffsetRootIndex,
+ kGetterStubDeoptPCOffsetRootIndex,
+ kSetterStubDeoptPCOffsetRootIndex,
+ kStringTableRootIndex,
+ };
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(writable_roots); i++) {
+ if (root_index == writable_roots[i])
+ return true;
+ }
+ return false;
+}
+
+
+bool Heap::RootCanBeTreatedAsConstant(RootListIndex root_index) {
+ return !RootCanBeWrittenAfterInitialization(root_index) &&
+ !InNewSpace(roots_array_start()[root_index]);
+}
+
+
+Object* RegExpResultsCache::Lookup(Heap* heap,
+ String* key_string,
+ Object* key_pattern,
+ ResultsCacheType type) {
+ FixedArray* cache;
+ if (!key_string->IsInternalizedString()) return Smi::FromInt(0);
+ if (type == STRING_SPLIT_SUBSTRINGS) {
+ ASSERT(key_pattern->IsString());
+ if (!key_pattern->IsInternalizedString()) return Smi::FromInt(0);
+ cache = heap->string_split_cache();
+ } else {
+ ASSERT(type == REGEXP_MULTIPLE_INDICES);
+ ASSERT(key_pattern->IsFixedArray());
+ cache = heap->regexp_multiple_cache();
+ }
+
+ uint32_t hash = key_string->Hash();
+ uint32_t index = ((hash & (kRegExpResultsCacheSize - 1)) &
+ ~(kArrayEntriesPerCacheEntry - 1));
+ if (cache->get(index + kStringOffset) == key_string &&
+ cache->get(index + kPatternOffset) == key_pattern) {
+ return cache->get(index + kArrayOffset);
+ }
+ index =
+ ((index + kArrayEntriesPerCacheEntry) & (kRegExpResultsCacheSize - 1));
+ if (cache->get(index + kStringOffset) == key_string &&
+ cache->get(index + kPatternOffset) == key_pattern) {
+ return cache->get(index + kArrayOffset);
+ }
+ return Smi::FromInt(0);
+}
+
+
+void RegExpResultsCache::Enter(Heap* heap,
+ String* key_string,
+ Object* key_pattern,
+ FixedArray* value_array,
+ ResultsCacheType type) {
+ FixedArray* cache;
+ if (!key_string->IsInternalizedString()) return;
+ if (type == STRING_SPLIT_SUBSTRINGS) {
+ ASSERT(key_pattern->IsString());
+ if (!key_pattern->IsInternalizedString()) return;
+ cache = heap->string_split_cache();
+ } else {
+ ASSERT(type == REGEXP_MULTIPLE_INDICES);
+ ASSERT(key_pattern->IsFixedArray());
+ cache = heap->regexp_multiple_cache();
+ }
+
+ uint32_t hash = key_string->Hash();
+ uint32_t index = ((hash & (kRegExpResultsCacheSize - 1)) &
+ ~(kArrayEntriesPerCacheEntry - 1));
+ if (cache->get(index + kStringOffset) == Smi::FromInt(0)) {
+ cache->set(index + kStringOffset, key_string);
+ cache->set(index + kPatternOffset, key_pattern);
+ cache->set(index + kArrayOffset, value_array);
+ } else {
+ uint32_t index2 =
+ ((index + kArrayEntriesPerCacheEntry) & (kRegExpResultsCacheSize - 1));
+ if (cache->get(index2 + kStringOffset) == Smi::FromInt(0)) {
+ cache->set(index2 + kStringOffset, key_string);
+ cache->set(index2 + kPatternOffset, key_pattern);
+ cache->set(index2 + kArrayOffset, value_array);
+ } else {
+ cache->set(index2 + kStringOffset, Smi::FromInt(0));
+ cache->set(index2 + kPatternOffset, Smi::FromInt(0));
+ cache->set(index2 + kArrayOffset, Smi::FromInt(0));
+ cache->set(index + kStringOffset, key_string);
+ cache->set(index + kPatternOffset, key_pattern);
+ cache->set(index + kArrayOffset, value_array);
+ }
+ }
+ // If the array is a reasonably short list of substrings, convert it into a
+ // list of internalized strings.
+ if (type == STRING_SPLIT_SUBSTRINGS && value_array->length() < 100) {
+ for (int i = 0; i < value_array->length(); i++) {
+ String* str = String::cast(value_array->get(i));
+ Object* internalized_str;
+ MaybeObject* maybe_string = heap->InternalizeString(str);
+ if (maybe_string->ToObject(&internalized_str)) {
+ value_array->set(i, internalized_str);
+ }
+ }
+ }
+ // Convert backing store to a copy-on-write array.
+ value_array->set_map_no_write_barrier(heap->fixed_cow_array_map());
+}
+
+
+void RegExpResultsCache::Clear(FixedArray* cache) {
+ for (int i = 0; i < kRegExpResultsCacheSize; i++) {
+ cache->set(i, Smi::FromInt(0));
+ }
+}
+
+
+MaybeObject* Heap::AllocateInitialNumberStringCache() {
+ MaybeObject* maybe_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() || FLAG_extra_code != NULL);
+ 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();
+ for (int i = 0; i < len; i++) {
+ number_string_cache()->set_undefined(this, i);
+ }
+}
+
+
+static inline int double_get_hash(double d) {
+ DoubleRepresentation rep(d);
+ return static_cast<int>(rep.bits) ^ static_cast<int>(rep.bits >> 32);
+}
+
+
+static inline int smi_get_hash(Smi* smi) {
+ return smi->value();
+}
+
+
+Object* Heap::GetNumberStringCache(Object* number) {
+ int hash;
+ int mask = (number_string_cache()->length() >> 1) - 1;
+ if (number->IsSmi()) {
+ hash = smi_get_hash(Smi::cast(number)) & mask;
+ } else {
+ hash = double_get_hash(number->Number()) & mask;
+ }
+ Object* key = number_string_cache()->get(hash * 2);
+ if (key == number) {
+ return String::cast(number_string_cache()->get(hash * 2 + 1));
+ } else if (key->IsHeapNumber() &&
+ number->IsHeapNumber() &&
+ key->Number() == number->Number()) {
+ return String::cast(number_string_cache()->get(hash * 2 + 1));
+ }
+ return undefined_value();
+}
+
+
+void Heap::SetNumberStringCache(Object* number, String* string) {
+ int hash;
+ int mask = (number_string_cache()->length() >> 1) - 1;
+ if (number->IsSmi()) {
+ hash = smi_get_hash(Smi::cast(number)) & mask;
+ } else {
+ hash = double_get_hash(number->Number()) & mask;
+ }
+ 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);
+}
+
+
+MaybeObject* Heap::NumberToString(Object* number,
+ bool check_number_string_cache,
+ PretenureFlag pretenure) {
+ isolate_->counters()->number_to_string_runtime()->Increment();
+ if (check_number_string_cache) {
+ Object* cached = GetNumberStringCache(number);
+ if (cached != undefined_value()) {
+ return cached;
+ }
+ }
+
+ char arr[100];
+ Vector<char> buffer(arr, ARRAY_SIZE(arr));
+ const char* str;
+ if (number->IsSmi()) {
+ int num = Smi::cast(number)->value();
+ str = IntToCString(num, buffer);
+ } else {
+ double num = HeapNumber::cast(number)->value();
+ str = DoubleToCString(num, buffer);
+ }
+
+ Object* js_string;
+ MaybeObject* maybe_js_string =
+ AllocateStringFromOneByte(CStrVector(str), pretenure);
+ if (maybe_js_string->ToObject(&js_string)) {
+ SetNumberStringCache(number, String::cast(js_string));
+ }
+ return maybe_js_string;
+}
+
+
+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)]);
+}
+
+
+Heap::RootListIndex Heap::RootIndexForExternalArrayType(
+ ExternalArrayType array_type) {
+ switch (array_type) {
+ case kExternalByteArray:
+ return kExternalByteArrayMapRootIndex;
+ case kExternalUnsignedByteArray:
+ return kExternalUnsignedByteArrayMapRootIndex;
+ case kExternalShortArray:
+ return kExternalShortArrayMapRootIndex;
+ case kExternalUnsignedShortArray:
+ return kExternalUnsignedShortArrayMapRootIndex;
+ case kExternalIntArray:
+ return kExternalIntArrayMapRootIndex;
+ case kExternalUnsignedIntArray:
+ return kExternalUnsignedIntArrayMapRootIndex;
+ case kExternalFloatArray:
+ return kExternalFloatArrayMapRootIndex;
+ case kExternalDoubleArray:
+ return kExternalDoubleArrayMapRootIndex;
+ case kExternalPixelArray:
+ return kExternalPixelArrayMapRootIndex;
+ default:
+ UNREACHABLE();
+ return kUndefinedValueRootIndex;
+ }
+}
+
+Heap::RootListIndex Heap::RootIndexForEmptyExternalArray(
+ ElementsKind elementsKind) {
+ switch (elementsKind) {
+ case EXTERNAL_BYTE_ELEMENTS:
+ return kEmptyExternalByteArrayRootIndex;
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ return kEmptyExternalUnsignedByteArrayRootIndex;
+ case EXTERNAL_SHORT_ELEMENTS:
+ return kEmptyExternalShortArrayRootIndex;
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ return kEmptyExternalUnsignedShortArrayRootIndex;
+ case EXTERNAL_INT_ELEMENTS:
+ return kEmptyExternalIntArrayRootIndex;
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ return kEmptyExternalUnsignedIntArrayRootIndex;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ return kEmptyExternalFloatArrayRootIndex;
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ return kEmptyExternalDoubleArrayRootIndex;
+ case EXTERNAL_PIXEL_ELEMENTS:
+ return kEmptyExternalPixelArrayRootIndex;
+ default:
+ UNREACHABLE();
+ return kUndefinedValueRootIndex;
+ }
+}
+
+
+ExternalArray* Heap::EmptyExternalArrayForMap(Map* map) {
+ return ExternalArray::cast(
+ roots_[RootIndexForEmptyExternalArray(map->elements_kind())]);
+}
+
+
+
+
+MaybeObject* Heap::NumberFromDouble(double value, PretenureFlag pretenure) {
+ // We need to distinguish the minus zero value and this cannot be
+ // done after conversion to int. Doing this by comparing bit
+ // patterns is faster than using fpclassify() et al.
+ static const DoubleRepresentation minus_zero(-0.0);
+
+ DoubleRepresentation rep(value);
+ if (rep.bits == minus_zero.bits) {
+ return AllocateHeapNumber(-0.0, pretenure);
+ }
+
+ int int_value = FastD2I(value);
+ if (value == int_value && Smi::IsValid(int_value)) {
+ return Smi::FromInt(int_value);
+ }
+
+ // Materialize the value in the heap.
+ return AllocateHeapNumber(value, pretenure);
+}
+
+
+MaybeObject* Heap::AllocateForeign(Address address, PretenureFlag pretenure) {
+ // Statically ensure that it is safe to allocate foreigns in paged spaces.
+ STATIC_ASSERT(Foreign::kSize <= Page::kMaxNonCodeHeapObjectSize);
+ AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+ Foreign* result;
+ MaybeObject* maybe_result = Allocate(foreign_map(), space);
+ if (!maybe_result->To(&result)) return maybe_result;
+ result->set_foreign_address(address);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateSharedFunctionInfo(Object* name) {
+ SharedFunctionInfo* share;
+ MaybeObject* maybe = Allocate(shared_function_info_map(), OLD_POINTER_SPACE);
+ if (!maybe->To<SharedFunctionInfo>(&share)) return maybe;
+
+ // Set pointer fields.
+ share->set_name(name);
+ Code* illegal = isolate_->builtins()->builtin(Builtins::kIllegal);
+ share->set_code(illegal);
+ share->set_optimized_code_map(Smi::FromInt(0));
+ share->set_scope_info(ScopeInfo::Empty(isolate_));
+ Code* construct_stub =
+ isolate_->builtins()->builtin(Builtins::kJSConstructStubGeneric);
+ share->set_construct_stub(construct_stub);
+ share->set_instance_class_name(Object_string());
+ 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_ast_node_count(0);
+ share->set_counters(0);
+
+ // Set integer fields (smi or int, depending on the architecture).
+ share->set_length(0);
+ share->set_formal_parameter_count(0);
+ share->set_expected_nof_properties(0);
+ share->set_num_literals(0);
+ share->set_start_position_and_type(0);
+ share->set_end_position(0);
+ share->set_function_token_position(0);
+ // All compiler hints default to false or 0.
+ share->set_compiler_hints(0);
+ share->set_opt_count(0);
+
+ return share;
+}
+
+
+MaybeObject* Heap::AllocateJSMessageObject(String* type,
+ JSArray* arguments,
+ int start_position,
+ int end_position,
+ Object* script,
+ Object* stack_trace,
+ Object* stack_frames) {
+ Object* result;
+ { MaybeObject* maybe_result = Allocate(message_object_map(), NEW_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ JSMessageObject* message = JSMessageObject::cast(result);
+ message->set_properties(Heap::empty_fixed_array(), SKIP_WRITE_BARRIER);
+ message->initialize_elements();
+ message->set_elements(Heap::empty_fixed_array(), SKIP_WRITE_BARRIER);
+ message->set_type(type);
+ message->set_arguments(arguments);
+ message->set_start_position(start_position);
+ message->set_end_position(end_position);
+ message->set_script(script);
+ message->set_stack_trace(stack_trace);
+ message->set_stack_frames(stack_frames);
+ return result;
+}
+
+
+
+// Returns true for a character in a range. Both limits are inclusive.
+static inline bool Between(uint32_t character, uint32_t from, uint32_t to) {
+ // This makes uses of the the unsigned wraparound.
+ return character - from <= to - from;
+}
+
+
+MUST_USE_RESULT static inline MaybeObject* MakeOrFindTwoCharacterString(
+ Heap* heap,
+ uint16_t c1,
+ uint16_t c2) {
+ String* result;
+ // Numeric strings have a different hash algorithm not known by
+ // LookupTwoCharsStringIfExists, so we skip this step for such strings.
+ if ((!Between(c1, '0', '9') || !Between(c2, '0', '9')) &&
+ heap->string_table()->LookupTwoCharsStringIfExists(c1, c2, &result)) {
+ return result;
+ // Now we know the length is 2, we might as well make use of that fact
+ // when building the new string.
+ } else if (static_cast<unsigned>(c1 | c2) <= String::kMaxOneByteCharCodeU) {
+ // We can do this.
+ ASSERT(IsPowerOf2(String::kMaxOneByteCharCodeU + 1)); // because of this.
+ Object* result;
+ { MaybeObject* maybe_result = heap->AllocateRawOneByteString(2);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ uint8_t* dest = SeqOneByteString::cast(result)->GetChars();
+ dest[0] = static_cast<uint8_t>(c1);
+ dest[1] = static_cast<uint8_t>(c2);
+ return result;
+ } else {
+ Object* result;
+ { MaybeObject* maybe_result = heap->AllocateRawTwoByteString(2);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ uc16* dest = SeqTwoByteString::cast(result)->GetChars();
+ dest[0] = c1;
+ dest[1] = c2;
+ return result;
+ }
+}
+
+
+MaybeObject* Heap::AllocateConsString(String* first, String* second) {
+ int first_length = first->length();
+ if (first_length == 0) {
+ return second;
+ }
+
+ int second_length = second->length();
+ if (second_length == 0) {
+ return first;
+ }
+
+ int length = first_length + second_length;
+
+ // Optimization for 2-byte strings often used as keys in a decompression
+ // dictionary. Check whether we already have the string in the string
+ // table to prevent creation of many unneccesary strings.
+ if (length == 2) {
+ uint16_t c1 = first->Get(0);
+ uint16_t c2 = second->Get(0);
+ return MakeOrFindTwoCharacterString(this, c1, c2);
+ }
+
+ bool first_is_one_byte = first->IsOneByteRepresentation();
+ bool second_is_one_byte = second->IsOneByteRepresentation();
+ bool is_one_byte = first_is_one_byte && second_is_one_byte;
+ // Make sure that an out of memory exception is thrown if the length
+ // of the new cons string is too large.
+ if (length > String::kMaxLength || length < 0) {
+ isolate()->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x4);
+ }
+
+ bool is_one_byte_data_in_two_byte_string = false;
+ if (!is_one_byte) {
+ // 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.
+ is_one_byte_data_in_two_byte_string =
+ first->HasOnlyOneByteChars() && second->HasOnlyOneByteChars();
+ if (is_one_byte_data_in_two_byte_string) {
+ isolate_->counters()->string_add_runtime_ext_to_ascii()->Increment();
+ }
+ }
+
+ // If the resulting string is small make a flat string.
+ if (length < ConsString::kMinLength) {
+ // Note that neither of the two inputs can be a slice because:
+ STATIC_ASSERT(ConsString::kMinLength <= SlicedString::kMinLength);
+ ASSERT(first->IsFlat());
+ ASSERT(second->IsFlat());
+ if (is_one_byte) {
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRawOneByteString(length);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Copy the characters into the new object.
+ uint8_t* dest = SeqOneByteString::cast(result)->GetChars();
+ // Copy first part.
+ const uint8_t* src;
+ if (first->IsExternalString()) {
+ src = ExternalAsciiString::cast(first)->GetChars();
+ } else {
+ src = SeqOneByteString::cast(first)->GetChars();
+ }
+ for (int i = 0; i < first_length; i++) *dest++ = src[i];
+ // Copy second part.
+ if (second->IsExternalString()) {
+ src = ExternalAsciiString::cast(second)->GetChars();
+ } else {
+ src = SeqOneByteString::cast(second)->GetChars();
+ }
+ for (int i = 0; i < second_length; i++) *dest++ = src[i];
+ return result;
+ } else {
+ if (is_one_byte_data_in_two_byte_string) {
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRawOneByteString(length);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Copy the characters into the new object.
+ uint8_t* dest = SeqOneByteString::cast(result)->GetChars();
+ String::WriteToFlat(first, dest, 0, first_length);
+ String::WriteToFlat(second, dest + first_length, 0, second_length);
+ isolate_->counters()->string_add_runtime_ext_to_ascii()->Increment();
+ return result;
+ }
+
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRawTwoByteString(length);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Copy the characters into the new object.
+ uc16* dest = SeqTwoByteString::cast(result)->GetChars();
+ String::WriteToFlat(first, dest, 0, first_length);
+ String::WriteToFlat(second, dest + first_length, 0, second_length);
+ return result;
+ }
+ }
+
+ Map* map = (is_one_byte || is_one_byte_data_in_two_byte_string) ?
+ cons_ascii_string_map() : cons_string_map();
+
+ Object* result;
+ { MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ DisallowHeapAllocation no_gc;
+ ConsString* cons_string = ConsString::cast(result);
+ WriteBarrierMode mode = cons_string->GetWriteBarrierMode(no_gc);
+ cons_string->set_length(length);
+ cons_string->set_hash_field(String::kEmptyHashField);
+ cons_string->set_first(first, mode);
+ cons_string->set_second(second, mode);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateSubString(String* buffer,
+ int start,
+ int end,
+ PretenureFlag pretenure) {
+ int length = end - start;
+ if (length <= 0) {
+ return empty_string();
+ } else if (length == 1) {
+ return LookupSingleCharacterStringFromCode(buffer->Get(start));
+ } else if (length == 2) {
+ // Optimization for 2-byte strings often used as keys in a decompression
+ // dictionary. Check whether we already have the string in the string
+ // table to prevent creation of many unnecessary strings.
+ uint16_t c1 = buffer->Get(start);
+ uint16_t c2 = buffer->Get(start + 1);
+ return MakeOrFindTwoCharacterString(this, c1, c2);
+ }
+
+ // Make an attempt to flatten the buffer to reduce access time.
+ buffer = buffer->TryFlattenGetString();
+
+ if (!FLAG_string_slices ||
+ !buffer->IsFlat() ||
+ length < SlicedString::kMinLength ||
+ pretenure == TENURED) {
+ Object* result;
+ // 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_one_byte = buffer->IsOneByteRepresentation();
+ { MaybeObject* maybe_result = is_one_byte
+ ? AllocateRawOneByteString(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 (is_one_byte) {
+ ASSERT(string_result->IsOneByteRepresentation());
+ uint8_t* dest = SeqOneByteString::cast(string_result)->GetChars();
+ String::WriteToFlat(buffer, dest, start, end);
+ } else {
+ ASSERT(string_result->IsTwoByteRepresentation());
+ uc16* dest = SeqTwoByteString::cast(string_result)->GetChars();
+ String::WriteToFlat(buffer, dest, start, end);
+ }
+ return result;
+ }
+
+ ASSERT(buffer->IsFlat());
+#if VERIFY_HEAP
+ 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->IsOneByteRepresentation()
+ ? sliced_ascii_string_map()
+ : sliced_string_map();
+ MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ DisallowHeapAllocation no_gc;
+ SlicedString* sliced_string = SlicedString::cast(result);
+ sliced_string->set_length(length);
+ sliced_string->set_hash_field(String::kEmptyHashField);
+ if (buffer->IsConsString()) {
+ ConsString* cons = ConsString::cast(buffer);
+ ASSERT(cons->second()->length() == 0);
+ sliced_string->set_parent(cons->first());
+ sliced_string->set_offset(start);
+ } else if (buffer->IsSlicedString()) {
+ // Prevent nesting sliced strings.
+ SlicedString* parent_slice = SlicedString::cast(buffer);
+ sliced_string->set_parent(parent_slice->parent());
+ sliced_string->set_offset(start + parent_slice->offset());
+ } else {
+ sliced_string->set_parent(buffer);
+ sliced_string->set_offset(start);
+ }
+ ASSERT(sliced_string->parent()->IsSeqString() ||
+ sliced_string->parent()->IsExternalString());
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateExternalStringFromAscii(
+ const ExternalAsciiString::Resource* resource) {
+ size_t length = resource->length();
+ if (length > static_cast<size_t>(String::kMaxLength)) {
+ isolate()->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x5);
+ }
+
+ Map* map = external_ascii_string_map();
+ Object* result;
+ { MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ ExternalAsciiString* external_string = ExternalAsciiString::cast(result);
+ external_string->set_length(static_cast<int>(length));
+ external_string->set_hash_field(String::kEmptyHashField);
+ external_string->set_resource(resource);
+
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateExternalStringFromTwoByte(
+ const ExternalTwoByteString::Resource* resource) {
+ size_t length = resource->length();
+ if (length > static_cast<size_t>(String::kMaxLength)) {
+ isolate()->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x6);
+ }
+
+ // For small strings we check whether the resource contains only
+ // one byte characters. If yes, we use a different string map.
+ static const size_t kOneByteCheckLengthLimit = 32;
+ bool is_one_byte = length <= kOneByteCheckLengthLimit &&
+ String::IsOneByte(resource->data(), static_cast<int>(length));
+ Map* map = is_one_byte ?
+ external_string_with_one_byte_data_map() : external_string_map();
+ Object* result;
+ { MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ ExternalTwoByteString* external_string = ExternalTwoByteString::cast(result);
+ external_string->set_length(static_cast<int>(length));
+ external_string->set_hash_field(String::kEmptyHashField);
+ external_string->set_resource(resource);
+
+ return result;
+}
+
+
+MaybeObject* Heap::LookupSingleCharacterStringFromCode(uint16_t code) {
+ if (code <= String::kMaxOneByteCharCode) {
+ Object* value = single_character_string_cache()->get(code);
+ if (value != undefined_value()) return value;
+
+ uint8_t buffer[1];
+ buffer[0] = static_cast<uint8_t>(code);
+ Object* result;
+ MaybeObject* maybe_result =
+ InternalizeOneByteString(Vector<const uint8_t>(buffer, 1));
+
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ single_character_string_cache()->set(code, result);
+ return result;
+ }
+
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRawTwoByteString(1);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ String* answer = String::cast(result);
+ answer->Set(0, code);
+ return answer;
+}
+
+
+MaybeObject* Heap::AllocateByteArray(int length, PretenureFlag pretenure) {
+ if (length < 0 || length > ByteArray::kMaxLength) {
+ return Failure::OutOfMemoryException(0x7);
+ }
+ if (pretenure == NOT_TENURED) {
+ return AllocateByteArray(length);
+ }
+ int size = ByteArray::SizeFor(length);
+ Object* result;
+ { MaybeObject* maybe_result = (size <= Page::kMaxNonCodeHeapObjectSize)
+ ? old_data_space_->AllocateRaw(size)
+ : lo_space_->AllocateRaw(size, NOT_EXECUTABLE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ reinterpret_cast<ByteArray*>(result)->set_map_no_write_barrier(
+ byte_array_map());
+ reinterpret_cast<ByteArray*>(result)->set_length(length);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateByteArray(int length) {
+ if (length < 0 || length > ByteArray::kMaxLength) {
+ return Failure::OutOfMemoryException(0x8);
+ }
+ int size = ByteArray::SizeFor(length);
+ AllocationSpace space =
+ (size > Page::kMaxNonCodeHeapObjectSize) ? LO_SPACE : NEW_SPACE;
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRaw(size, space, OLD_DATA_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ reinterpret_cast<ByteArray*>(result)->set_map_no_write_barrier(
+ byte_array_map());
+ reinterpret_cast<ByteArray*>(result)->set_length(length);
+ return result;
+}
+
+
+void Heap::CreateFillerObjectAt(Address addr, int size) {
+ if (size == 0) return;
+ HeapObject* filler = HeapObject::FromAddress(addr);
+ if (size == kPointerSize) {
+ filler->set_map_no_write_barrier(one_pointer_filler_map());
+ } else if (size == 2 * kPointerSize) {
+ filler->set_map_no_write_barrier(two_pointer_filler_map());
+ } else {
+ filler->set_map_no_write_barrier(free_space_map());
+ FreeSpace::cast(filler)->set_size(size);
+ }
+}
+
+
+MaybeObject* Heap::AllocateExternalArray(int length,
+ ExternalArrayType array_type,
+ void* external_pointer,
+ PretenureFlag pretenure) {
+ AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRaw(ExternalArray::kAlignedSize,
+ space,
+ OLD_DATA_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ 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(
+ external_pointer);
+
+ return result;
+}
+
+
+MaybeObject* Heap::CreateCode(const CodeDesc& desc,
+ Code::Flags flags,
+ Handle<Object> self_reference,
+ bool immovable,
+ bool crankshafted) {
+ // Allocate ByteArray before the Code object, so that we do not risk
+ // leaving uninitialized Code object (and breaking the heap).
+ 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);
+ int obj_size = Code::SizeFor(body_size);
+ ASSERT(IsAligned(static_cast<intptr_t>(obj_size), kCodeAlignment));
+ MaybeObject* maybe_result;
+ // Large code objects and code objects which should stay at a fixed address
+ // are allocated in large object space.
+ HeapObject* result;
+ bool force_lo_space = obj_size > code_space()->AreaSize();
+ if (force_lo_space) {
+ maybe_result = lo_space_->AllocateRaw(obj_size, EXECUTABLE);
+ } else {
+ maybe_result = code_space_->AllocateRaw(obj_size);
+ }
+ if (!maybe_result->To<HeapObject>(&result)) return maybe_result;
+
+ if (immovable && !force_lo_space &&
+ // Objects on the first page of each space are never moved.
+ !code_space_->FirstPage()->Contains(result->address())) {
+ // Discard the first code allocation, which was on a page where it could be
+ // moved.
+ CreateFillerObjectAt(result->address(), obj_size);
+ maybe_result = lo_space_->AllocateRaw(obj_size, EXECUTABLE);
+ if (!maybe_result->To<HeapObject>(&result)) return maybe_result;
+ }
+
+ // Initialize the object
+ 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(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_is_crankshafted(crankshafted);
+ code->set_deoptimization_data(empty_fixed_array(), SKIP_WRITE_BARRIER);
+ code->InitializeTypeFeedbackInfoNoWriteBarrier(undefined_value());
+ code->set_handler_table(empty_fixed_array(), SKIP_WRITE_BARRIER);
+ code->set_gc_metadata(Smi::FromInt(0));
+ code->set_ic_age(global_ic_age_);
+ code->set_prologue_offset(kPrologueOffsetNotSet);
+ if (code->kind() == Code::OPTIMIZED_FUNCTION) {
+ code->set_marked_for_deoptimization(false);
+ }
+ // Allow self references to created code object by patching the handle to
+ // point to the newly allocated Code object.
+ if (!self_reference.is_null()) {
+ *(self_reference.location()) = code;
+ }
+ // Migrate generated code.
+ // The generated code can contain Object** values (typically from handles)
+ // that are dereferenced during the copy to point directly to the actual heap
+ // objects. These pointers can include references to the code object itself,
+ // through the self_reference parameter.
+ code->CopyFrom(desc);
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ code->Verify();
+ }
+#endif
+ return code;
+}
+
+
+MaybeObject* Heap::CopyCode(Code* code) {
+ // Allocate an object the same size as the code object.
+ int obj_size = code->Size();
+ MaybeObject* maybe_result;
+ if (obj_size > code_space()->AreaSize()) {
+ maybe_result = lo_space_->AllocateRaw(obj_size, EXECUTABLE);
+ } else {
+ maybe_result = code_space_->AllocateRaw(obj_size);
+ }
+
+ Object* result;
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+
+ // Copy code object.
+ Address old_addr = code->address();
+ Address new_addr = reinterpret_cast<HeapObject*>(result)->address();
+ CopyBlock(new_addr, old_addr, obj_size);
+ // Relocate the copy.
+ Code* new_code = Code::cast(result);
+ ASSERT(!isolate_->code_range()->exists() ||
+ isolate_->code_range()->contains(code->address()));
+ new_code->Relocate(new_addr - old_addr);
+ return new_code;
+}
+
+
+MaybeObject* Heap::CopyCode(Code* code, Vector<byte> reloc_info) {
+ // Allocate ByteArray before the Code object, so that we do not risk
+ // leaving uninitialized Code object (and breaking the heap).
+ Object* reloc_info_array;
+ { MaybeObject* maybe_reloc_info_array =
+ AllocateByteArray(reloc_info.length(), TENURED);
+ if (!maybe_reloc_info_array->ToObject(&reloc_info_array)) {
+ return maybe_reloc_info_array;
+ }
+ }
+
+ int new_body_size = RoundUp(code->instruction_size(), kObjectAlignment);
+
+ int new_obj_size = Code::SizeFor(new_body_size);
+
+ Address old_addr = code->address();
+
+ size_t relocation_offset =
+ static_cast<size_t>(code->instruction_end() - old_addr);
+
+ MaybeObject* maybe_result;
+ if (new_obj_size > code_space()->AreaSize()) {
+ maybe_result = lo_space_->AllocateRaw(new_obj_size, EXECUTABLE);
+ } else {
+ maybe_result = code_space_->AllocateRaw(new_obj_size);
+ }
+
+ Object* result;
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+
+ // Copy code object.
+ Address new_addr = reinterpret_cast<HeapObject*>(result)->address();
+
+ // Copy header and instructions.
+ CopyBytes(new_addr, old_addr, relocation_offset);
+
+ Code* new_code = Code::cast(result);
+ new_code->set_relocation_info(ByteArray::cast(reloc_info_array));
+
+ // Copy patched rinfo.
+ CopyBytes(new_code->relocation_start(),
+ reloc_info.start(),
+ static_cast<size_t>(reloc_info.length()));
+
+ // Relocate the copy.
+ ASSERT(!isolate_->code_range()->exists() ||
+ isolate_->code_range()->contains(code->address()));
+ new_code->Relocate(new_addr - old_addr);
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ code->Verify();
+ }
+#endif
+ return new_code;
+}
+
+
+MaybeObject* Heap::AllocateWithAllocationSite(Map* map, AllocationSpace space,
+ Handle<AllocationSite> allocation_site) {
+ ASSERT(gc_state_ == NOT_IN_GC);
+ ASSERT(map->instance_type() != MAP_TYPE);
+ // If allocation failures are disallowed, we may allocate in a different
+ // space when new space is full and the object is not a large object.
+ AllocationSpace retry_space =
+ (space != NEW_SPACE) ? space : TargetSpaceId(map->instance_type());
+ int size = map->instance_size() + AllocationMemento::kSize;
+ Object* result;
+ MaybeObject* maybe_result = AllocateRaw(size, space, retry_space);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ // No need for write barrier since object is white and map is in old space.
+ HeapObject::cast(result)->set_map_no_write_barrier(map);
+ AllocationMemento* alloc_memento = reinterpret_cast<AllocationMemento*>(
+ reinterpret_cast<Address>(result) + map->instance_size());
+ alloc_memento->set_map_no_write_barrier(allocation_memento_map());
+ alloc_memento->set_allocation_site(*allocation_site, SKIP_WRITE_BARRIER);
+ return result;
+}
+
+
+MaybeObject* Heap::Allocate(Map* map, AllocationSpace space) {
+ ASSERT(gc_state_ == NOT_IN_GC);
+ ASSERT(map->instance_type() != MAP_TYPE);
+ // If allocation failures are disallowed, we may allocate in a different
+ // space when new space is full and the object is not a large object.
+ AllocationSpace retry_space =
+ (space != NEW_SPACE) ? space : TargetSpaceId(map->instance_type());
+ int size = map->instance_size();
+ Object* result;
+ MaybeObject* maybe_result = AllocateRaw(size, space, retry_space);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ // 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;
+}
+
+
+void Heap::InitializeFunction(JSFunction* function,
+ SharedFunctionInfo* shared,
+ Object* prototype) {
+ ASSERT(!prototype->IsMap());
+ function->initialize_properties();
+ function->initialize_elements();
+ function->set_shared(shared);
+ function->set_code(shared->code());
+ function->set_prototype_or_initial_map(prototype);
+ function->set_context(undefined_value());
+ function->set_literals_or_bindings(empty_fixed_array());
+ function->set_next_function_link(undefined_value());
+}
+
+
+MaybeObject* Heap::AllocateFunctionPrototype(JSFunction* function) {
+ // Make sure to use globals from the function's context, since the function
+ // can be from a different context.
+ Context* native_context = function->context()->native_context();
+ Map* new_map;
+ if (function->shared()->is_generator()) {
+ // Generator prototypes can share maps since they don't have "constructor"
+ // properties.
+ new_map = native_context->generator_object_prototype_map();
+ } else {
+ // Each function prototype gets a fresh map to avoid unwanted sharing of
+ // maps between prototypes of different constructors.
+ JSFunction* object_function = native_context->object_function();
+ ASSERT(object_function->has_initial_map());
+ MaybeObject* maybe_map = object_function->initial_map()->Copy();
+ if (!maybe_map->To(&new_map)) return maybe_map;
+ }
+
+ Object* prototype;
+ MaybeObject* maybe_prototype = AllocateJSObjectFromMap(new_map);
+ if (!maybe_prototype->ToObject(&prototype)) return maybe_prototype;
+
+ if (!function->shared()->is_generator()) {
+ MaybeObject* maybe_failure =
+ JSObject::cast(prototype)->SetLocalPropertyIgnoreAttributes(
+ constructor_string(), function, DONT_ENUM);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ }
+
+ return prototype;
+}
+
+
+MaybeObject* Heap::AllocateFunction(Map* function_map,
+ SharedFunctionInfo* shared,
+ Object* prototype,
+ PretenureFlag pretenure) {
+ AllocationSpace space =
+ (pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
+ Object* result;
+ { MaybeObject* maybe_result = Allocate(function_map, space);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ InitializeFunction(JSFunction::cast(result), shared, prototype);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateArgumentsObject(Object* callee, int length) {
+ // To get fast allocation and map sharing for arguments objects we
+ // allocate them based on an arguments boilerplate.
+
+ JSObject* boilerplate;
+ int arguments_object_size;
+ bool strict_mode_callee = callee->IsJSFunction() &&
+ !JSFunction::cast(callee)->shared()->is_classic_mode();
+ if (strict_mode_callee) {
+ boilerplate =
+ isolate()->context()->native_context()->
+ strict_mode_arguments_boilerplate();
+ arguments_object_size = kArgumentsObjectSizeStrict;
+ } else {
+ boilerplate =
+ isolate()->context()->native_context()->arguments_boilerplate();
+ arguments_object_size = kArgumentsObjectSize;
+ }
+
+ // This calls Copy directly rather than using Heap::AllocateRaw so we
+ // duplicate the check here.
+ ASSERT(AllowHeapAllocation::IsAllowed() && gc_state_ == NOT_IN_GC);
+
+ // Check that the size of the boilerplate matches our
+ // expectations. The ArgumentsAccessStub::GenerateNewObject relies
+ // on the size being a known constant.
+ ASSERT(arguments_object_size == boilerplate->map()->instance_size());
+
+ // Do the allocation.
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateRaw(arguments_object_size, NEW_SPACE, OLD_POINTER_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // Copy the content. The arguments boilerplate doesn't have any
+ // fields that point to new space so it's safe to skip the write
+ // barrier here.
+ CopyBlock(HeapObject::cast(result)->address(),
+ boilerplate->address(),
+ JSObject::kHeaderSize);
+
+ // Set the length property.
+ JSObject::cast(result)->InObjectPropertyAtPut(kArgumentsLengthIndex,
+ Smi::FromInt(length),
+ SKIP_WRITE_BARRIER);
+ // Set the callee property for non-strict mode arguments object only.
+ if (!strict_mode_callee) {
+ JSObject::cast(result)->InObjectPropertyAtPut(kArgumentsCalleeIndex,
+ callee);
+ }
+
+ // Check the state of the object
+ ASSERT(JSObject::cast(result)->HasFastProperties());
+ ASSERT(JSObject::cast(result)->HasFastObjectElements());
+
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateInitialMap(JSFunction* fun) {
+ ASSERT(!fun->has_initial_map());
+
+ // First create a new map with the size and number of in-object properties
+ // suggested by the function.
+ InstanceType instance_type;
+ int instance_size;
+ int in_object_properties;
+ if (fun->shared()->is_generator()) {
+ instance_type = JS_GENERATOR_OBJECT_TYPE;
+ instance_size = JSGeneratorObject::kSize;
+ in_object_properties = 0;
+ } else {
+ instance_type = JS_OBJECT_TYPE;
+ instance_size = fun->shared()->CalculateInstanceSize();
+ in_object_properties = fun->shared()->CalculateInObjectProperties();
+ }
+ Map* map;
+ MaybeObject* maybe_map = AllocateMap(instance_type, instance_size);
+ if (!maybe_map->To(&map)) return maybe_map;
+
+ // Fetch or allocate prototype.
+ Object* prototype;
+ if (fun->has_instance_prototype()) {
+ prototype = fun->instance_prototype();
+ } else {
+ MaybeObject* maybe_prototype = AllocateFunctionPrototype(fun);
+ if (!maybe_prototype->To(&prototype)) return maybe_prototype;
+ }
+ map->set_inobject_properties(in_object_properties);
+ map->set_unused_property_fields(in_object_properties);
+ map->set_prototype(prototype);
+ ASSERT(map->has_fast_object_elements());
+
+ if (!fun->shared()->is_generator()) {
+ fun->shared()->StartInobjectSlackTracking(map);
+ }
+
+ return map;
+}
+
+
+void Heap::InitializeJSObjectFromMap(JSObject* obj,
+ FixedArray* properties,
+ Map* map) {
+ obj->set_properties(properties);
+ obj->initialize_elements();
+ // 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 (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()) {
+ // We might want to shrink the object later.
+ ASSERT(obj->GetInternalFieldCount() == 0);
+ filler = Heap::one_pointer_filler_map();
+ } else {
+ filler = Heap::undefined_value();
+ }
+ obj->InitializeBody(map, Heap::undefined_value(), filler);
+}
+
+
+MaybeObject* Heap::AllocateJSObjectFromMap(
+ Map* map, PretenureFlag pretenure, bool allocate_properties) {
+ // JSFunctions should be allocated using AllocateFunction to be
+ // properly initialized.
+ ASSERT(map->instance_type() != JS_FUNCTION_TYPE);
+
+ // Both types of global objects should be allocated using
+ // AllocateGlobalObject to be properly initialized.
+ ASSERT(map->instance_type() != JS_GLOBAL_OBJECT_TYPE);
+ ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE);
+
+ // Allocate the backing storage for the properties.
+ FixedArray* properties;
+ if (allocate_properties) {
+ int prop_size = map->InitialPropertiesLength();
+ ASSERT(prop_size >= 0);
+ { MaybeObject* maybe_properties = AllocateFixedArray(prop_size, pretenure);
+ if (!maybe_properties->To(&properties)) return maybe_properties;
+ }
+ } else {
+ properties = empty_fixed_array();
+ }
+
+ // Allocate the JSObject.
+ AllocationSpace space =
+ (pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
+ if (map->instance_size() > Page::kMaxNonCodeHeapObjectSize) space = LO_SPACE;
+ Object* obj;
+ MaybeObject* maybe_obj = Allocate(map, space);
+ if (!maybe_obj->To(&obj)) return maybe_obj;
+
+ // Initialize the JSObject.
+ InitializeJSObjectFromMap(JSObject::cast(obj), properties, map);
+ ASSERT(JSObject::cast(obj)->HasFastElements() ||
+ JSObject::cast(obj)->HasExternalArrayElements());
+ return obj;
+}
+
+
+MaybeObject* Heap::AllocateJSObjectFromMapWithAllocationSite(
+ Map* map, Handle<AllocationSite> allocation_site) {
+ // JSFunctions should be allocated using AllocateFunction to be
+ // properly initialized.
+ ASSERT(map->instance_type() != JS_FUNCTION_TYPE);
+
+ // Both types of global objects should be allocated using
+ // AllocateGlobalObject to be properly initialized.
+ ASSERT(map->instance_type() != JS_GLOBAL_OBJECT_TYPE);
+ ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE);
+
+ // Allocate the backing storage for the properties.
+ int prop_size = map->InitialPropertiesLength();
+ ASSERT(prop_size >= 0);
+ FixedArray* properties;
+ { MaybeObject* maybe_properties = AllocateFixedArray(prop_size);
+ if (!maybe_properties->To(&properties)) return maybe_properties;
+ }
+
+ // Allocate the JSObject.
+ AllocationSpace space = NEW_SPACE;
+ if (map->instance_size() > Page::kMaxNonCodeHeapObjectSize) space = LO_SPACE;
+ Object* obj;
+ MaybeObject* maybe_obj =
+ AllocateWithAllocationSite(map, space, allocation_site);
+ if (!maybe_obj->To(&obj)) return maybe_obj;
+
+ // Initialize the JSObject.
+ InitializeJSObjectFromMap(JSObject::cast(obj), properties, map);
+ ASSERT(JSObject::cast(obj)->HasFastElements());
+ return obj;
+}
+
+
+MaybeObject* Heap::AllocateJSObject(JSFunction* constructor,
+ PretenureFlag pretenure) {
+ // Allocate the initial map if absent.
+ if (!constructor->has_initial_map()) {
+ Object* initial_map;
+ { MaybeObject* maybe_initial_map = AllocateInitialMap(constructor);
+ if (!maybe_initial_map->ToObject(&initial_map)) return maybe_initial_map;
+ }
+ constructor->set_initial_map(Map::cast(initial_map));
+ Map::cast(initial_map)->set_constructor(constructor);
+ }
+ // Allocate the object based on the constructors initial map.
+ MaybeObject* result = AllocateJSObjectFromMap(
+ constructor->initial_map(), pretenure);
+#ifdef DEBUG
+ // Make sure result is NOT a global object if valid.
+ Object* non_failure;
+ ASSERT(!result->ToObject(&non_failure) || !non_failure->IsGlobalObject());
+#endif
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateJSObjectWithAllocationSite(JSFunction* constructor,
+ Handle<AllocationSite> allocation_site) {
+ // Allocate the initial map if absent.
+ if (!constructor->has_initial_map()) {
+ Object* initial_map;
+ { MaybeObject* maybe_initial_map = AllocateInitialMap(constructor);
+ if (!maybe_initial_map->ToObject(&initial_map)) return maybe_initial_map;
+ }
+ constructor->set_initial_map(Map::cast(initial_map));
+ Map::cast(initial_map)->set_constructor(constructor);
+ }
+ // Allocate the object based on the constructors initial map, or the payload
+ // advice
+ Map* initial_map = constructor->initial_map();
+
+ Smi* smi = Smi::cast(allocation_site->transition_info());
+ ElementsKind to_kind = static_cast<ElementsKind>(smi->value());
+ AllocationSiteMode mode = TRACK_ALLOCATION_SITE;
+ if (to_kind != initial_map->elements_kind()) {
+ MaybeObject* maybe_new_map = initial_map->AsElementsKind(to_kind);
+ if (!maybe_new_map->To(&initial_map)) return maybe_new_map;
+ // Possibly alter the mode, since we found an updated elements kind
+ // in the type info cell.
+ mode = AllocationSite::GetMode(to_kind);
+ }
+
+ MaybeObject* result;
+ if (mode == TRACK_ALLOCATION_SITE) {
+ result = AllocateJSObjectFromMapWithAllocationSite(initial_map,
+ allocation_site);
+ } else {
+ result = AllocateJSObjectFromMap(initial_map, NOT_TENURED);
+ }
+#ifdef DEBUG
+ // Make sure result is NOT a global object if valid.
+ Object* non_failure;
+ ASSERT(!result->ToObject(&non_failure) || !non_failure->IsGlobalObject());
+#endif
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateJSGeneratorObject(JSFunction *function) {
+ ASSERT(function->shared()->is_generator());
+ Map *map;
+ if (function->has_initial_map()) {
+ map = function->initial_map();
+ } else {
+ // Allocate the initial map if absent.
+ MaybeObject* maybe_map = AllocateInitialMap(function);
+ if (!maybe_map->To(&map)) return maybe_map;
+ function->set_initial_map(map);
+ map->set_constructor(function);
+ }
+ ASSERT(map->instance_type() == JS_GENERATOR_OBJECT_TYPE);
+ return AllocateJSObjectFromMap(map);
+}
+
+
+MaybeObject* Heap::AllocateJSModule(Context* context, ScopeInfo* scope_info) {
+ // Allocate a fresh map. Modules do not have a prototype.
+ Map* map;
+ MaybeObject* maybe_map = AllocateMap(JS_MODULE_TYPE, JSModule::kSize);
+ if (!maybe_map->To(&map)) return maybe_map;
+ // Allocate the object based on the map.
+ JSModule* module;
+ MaybeObject* maybe_module = AllocateJSObjectFromMap(map, TENURED);
+ if (!maybe_module->To(&module)) return maybe_module;
+ module->set_context(context);
+ module->set_scope_info(scope_info);
+ return module;
+}
+
+
+MaybeObject* Heap::AllocateJSArrayAndStorage(
+ ElementsKind elements_kind,
+ int length,
+ int capacity,
+ ArrayStorageAllocationMode mode,
+ PretenureFlag pretenure) {
+ MaybeObject* maybe_array = AllocateJSArray(elements_kind, pretenure);
+ JSArray* array;
+ if (!maybe_array->To(&array)) return maybe_array;
+
+ // TODO(mvstanton): this body of code is duplicate with AllocateJSArrayStorage
+ // for performance reasons.
+ ASSERT(capacity >= length);
+
+ if (capacity == 0) {
+ array->set_length(Smi::FromInt(0));
+ array->set_elements(empty_fixed_array());
+ return array;
+ }
+
+ FixedArrayBase* elms;
+ MaybeObject* maybe_elms = NULL;
+ if (IsFastDoubleElementsKind(elements_kind)) {
+ if (mode == DONT_INITIALIZE_ARRAY_ELEMENTS) {
+ maybe_elms = AllocateUninitializedFixedDoubleArray(capacity);
+ } else {
+ ASSERT(mode == INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
+ maybe_elms = AllocateFixedDoubleArrayWithHoles(capacity);
+ }
+ } else {
+ ASSERT(IsFastSmiOrObjectElementsKind(elements_kind));
+ 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::AllocateJSArrayAndStorageWithAllocationSite(
+ ElementsKind elements_kind,
+ int length,
+ int capacity,
+ Handle<AllocationSite> allocation_site,
+ ArrayStorageAllocationMode mode) {
+ MaybeObject* maybe_array = AllocateJSArrayWithAllocationSite(elements_kind,
+ allocation_site);
+ JSArray* array;
+ if (!maybe_array->To(&array)) return maybe_array;
+ return AllocateJSArrayStorage(array, length, capacity, mode);
+}
+
+
+MaybeObject* Heap::AllocateJSArrayStorage(
+ JSArray* array,
+ int length,
+ int capacity,
+ ArrayStorageAllocationMode mode) {
+ ASSERT(capacity >= length);
+
+ if (capacity == 0) {
+ array->set_length(Smi::FromInt(0));
+ array->set_elements(empty_fixed_array());
+ return array;
+ }
+
+ FixedArrayBase* elms;
+ MaybeObject* maybe_elms = NULL;
+ ElementsKind elements_kind = array->GetElementsKind();
+ if (IsFastDoubleElementsKind(elements_kind)) {
+ if (mode == DONT_INITIALIZE_ARRAY_ELEMENTS) {
+ maybe_elms = AllocateUninitializedFixedDoubleArray(capacity);
+ } else {
+ ASSERT(mode == INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
+ maybe_elms = AllocateFixedDoubleArrayWithHoles(capacity);
+ }
+ } else {
+ ASSERT(IsFastSmiOrObjectElementsKind(elements_kind));
+ 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,
+ int length,
+ 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(length));
+ array->ValidateElements();
+ return array;
+}
+
+
+MaybeObject* Heap::AllocateJSProxy(Object* handler, Object* prototype) {
+ // Allocate map.
+ // TODO(rossberg): Once we optimize proxies, think about a scheme to share
+ // maps. Will probably depend on the identity of the handler object, too.
+ Map* map;
+ MaybeObject* maybe_map_obj = AllocateMap(JS_PROXY_TYPE, JSProxy::kSize);
+ if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
+ map->set_prototype(prototype);
+
+ // Allocate the proxy object.
+ JSProxy* result;
+ MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+ 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;
+}
+
+
+MaybeObject* Heap::AllocateJSFunctionProxy(Object* handler,
+ Object* call_trap,
+ Object* construct_trap,
+ Object* prototype) {
+ // Allocate map.
+ // TODO(rossberg): Once we optimize proxies, think about a scheme to share
+ // maps. Will probably depend on the identity of the handler object, too.
+ Map* map;
+ MaybeObject* maybe_map_obj =
+ AllocateMap(JS_FUNCTION_PROXY_TYPE, JSFunctionProxy::kSize);
+ if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
+ map->set_prototype(prototype);
+
+ // Allocate the proxy object.
+ JSFunctionProxy* result;
+ MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+ 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;
+}
+
+
+MaybeObject* Heap::AllocateGlobalObject(JSFunction* constructor) {
+ ASSERT(constructor->has_initial_map());
+ Map* map = constructor->initial_map();
+ ASSERT(map->is_dictionary_map());
+
+ // Make sure no field properties are described in the initial map.
+ // This guarantees us that normalizing the properties does not
+ // require us to change property values to PropertyCells.
+ ASSERT(map->NextFreePropertyIndex() == 0);
+
+ // Make sure we don't have a ton of pre-allocated slots in the
+ // global objects. They will be unused once we normalize the object.
+ ASSERT(map->unused_property_fields() == 0);
+ ASSERT(map->inobject_properties() == 0);
+
+ // Initial size of the backing store to avoid resize of the storage during
+ // bootstrapping. The size differs between the JS global object ad the
+ // builtins object.
+ int initial_size = map->instance_type() == JS_GLOBAL_OBJECT_TYPE ? 64 : 512;
+
+ // Allocate a dictionary object for backing storage.
+ NameDictionary* dictionary;
+ MaybeObject* maybe_dictionary =
+ NameDictionary::Allocate(
+ this,
+ map->NumberOfOwnDescriptors() * 2 + initial_size);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+
+ // The global object might be created from an object template with accessors.
+ // Fill these accessors into the dictionary.
+ DescriptorArray* descs = map->instance_descriptors();
+ for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
+ PropertyDetails details = descs->GetDetails(i);
+ ASSERT(details.type() == CALLBACKS); // Only accessors are expected.
+ PropertyDetails d = PropertyDetails(details.attributes(), CALLBACKS, i + 1);
+ Object* value = descs->GetCallbacksObject(i);
+ MaybeObject* maybe_value = AllocatePropertyCell(value);
+ if (!maybe_value->ToObject(&value)) return maybe_value;
+
+ MaybeObject* maybe_added = dictionary->Add(descs->GetKey(i), value, d);
+ if (!maybe_added->To(&dictionary)) return maybe_added;
+ }
+
+ // Allocate the global object and initialize it with the backing store.
+ JSObject* global;
+ MaybeObject* maybe_global = Allocate(map, OLD_POINTER_SPACE);
+ if (!maybe_global->To(&global)) return maybe_global;
+
+ InitializeJSObjectFromMap(global, dictionary, map);
+
+ // Create a new map for the global object.
+ Map* new_map;
+ MaybeObject* maybe_map = map->CopyDropDescriptors();
+ if (!maybe_map->To(&new_map)) return maybe_map;
+ new_map->set_dictionary_map(true);
+
+ // Set up the global object as a normalized object.
+ global->set_map(new_map);
+ global->set_properties(dictionary);
+
+ // Make sure result is a global object with properties in dictionary.
+ ASSERT(global->IsGlobalObject());
+ ASSERT(!global->HasFastProperties());
+ return global;
+}
+
+
+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.
+ 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()) {
+ { MaybeObject* maybe_clone =
+ AllocateRaw(object_size, NEW_SPACE, OLD_POINTER_SPACE);
+ if (!maybe_clone->ToObject(&clone)) return maybe_clone;
+ }
+ Address clone_address = HeapObject::cast(clone)->address();
+ CopyBlock(clone_address,
+ source->address(),
+ object_size);
+ // Update write barrier for all fields that lie beyond the header.
+ RecordWrites(clone_address,
+ 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;
+ }
+ 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(),
+ source->address(),
+ 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.
+ if (elements->length() > 0) {
+ Object* elem;
+ { MaybeObject* maybe_elem;
+ if (elements->map() == fixed_cow_array_map()) {
+ maybe_elem = FixedArray::cast(elements);
+ } else if (source->HasFastDoubleElements()) {
+ maybe_elem = CopyFixedDoubleArray(FixedDoubleArray::cast(elements));
+ } else {
+ maybe_elem = CopyFixedArray(FixedArray::cast(elements));
+ }
+ if (!maybe_elem->ToObject(&elem)) return maybe_elem;
+ }
+ JSObject::cast(clone)->set_elements(FixedArrayBase::cast(elem), wb_mode);
+ }
+ // Update properties if necessary.
+ if (properties->length() > 0) {
+ Object* prop;
+ { MaybeObject* maybe_prop = CopyFixedArray(properties);
+ if (!maybe_prop->ToObject(&prop)) return maybe_prop;
+ }
+ JSObject::cast(clone)->set_properties(FixedArray::cast(prop), wb_mode);
+ }
+ // Return the new clone.
+ return clone;
+}
+
+
+MaybeObject* Heap::CopyJSObjectWithAllocationSite(
+ JSObject* source,
+ AllocationSite* site) {
+ // Never used to copy functions. If functions need to be copied we
+ // have to be careful to clear the literals array.
+ SLOW_ASSERT(!source->IsJSFunction());
+
+ // Make the clone.
+ Map* map = source->map();
+ int object_size = map->instance_size();
+ Object* clone;
+
+ ASSERT(map->CanTrackAllocationSite());
+ ASSERT(map->instance_type() == JS_ARRAY_TYPE);
+ 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.
+ int adjusted_object_size = object_size;
+ if (always_allocate()) {
+ // We'll only track origin if we are certain to allocate in new space
+ const int kMinFreeNewSpaceAfterGC = InitialSemiSpaceSize() * 3/4;
+ if ((object_size + AllocationMemento::kSize) < kMinFreeNewSpaceAfterGC) {
+ adjusted_object_size += AllocationMemento::kSize;
+ }
+
+ { MaybeObject* maybe_clone =
+ AllocateRaw(adjusted_object_size, NEW_SPACE, OLD_POINTER_SPACE);
+ if (!maybe_clone->ToObject(&clone)) return maybe_clone;
+ }
+ Address clone_address = HeapObject::cast(clone)->address();
+ CopyBlock(clone_address,
+ source->address(),
+ object_size);
+ // Update write barrier for all fields that lie beyond the header.
+ int write_barrier_offset = adjusted_object_size > object_size
+ ? JSArray::kSize + AllocationMemento::kSize
+ : JSObject::kHeaderSize;
+ if (((object_size - write_barrier_offset) / kPointerSize) > 0) {
+ RecordWrites(clone_address,
+ write_barrier_offset,
+ (object_size - write_barrier_offset) / kPointerSize);
+ }
+
+ // Track allocation site information, if we failed to allocate it inline.
+ if (InNewSpace(clone) &&
+ adjusted_object_size == object_size) {
+ MaybeObject* maybe_alloc_memento =
+ AllocateStruct(ALLOCATION_MEMENTO_TYPE);
+ AllocationMemento* alloc_memento;
+ if (maybe_alloc_memento->To(&alloc_memento)) {
+ alloc_memento->set_map_no_write_barrier(allocation_memento_map());
+ alloc_memento->set_allocation_site(site, SKIP_WRITE_BARRIER);
+ }
+ }
+ } else {
+ wb_mode = SKIP_WRITE_BARRIER;
+ adjusted_object_size += AllocationMemento::kSize;
+
+ { MaybeObject* maybe_clone = new_space_.AllocateRaw(adjusted_object_size);
+ if (!maybe_clone->ToObject(&clone)) return maybe_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(),
+ source->address(),
+ object_size);
+ }
+
+ if (adjusted_object_size > object_size) {
+ AllocationMemento* alloc_memento = reinterpret_cast<AllocationMemento*>(
+ reinterpret_cast<Address>(clone) + object_size);
+ alloc_memento->set_map_no_write_barrier(allocation_memento_map());
+ alloc_memento->set_allocation_site(site, SKIP_WRITE_BARRIER);
+ }
+
+ SLOW_ASSERT(
+ JSObject::cast(clone)->GetElementsKind() == source->GetElementsKind());
+ FixedArrayBase* elements = FixedArrayBase::cast(source->elements());
+ FixedArray* properties = FixedArray::cast(source->properties());
+ // Update elements if necessary.
+ if (elements->length() > 0) {
+ Object* elem;
+ { MaybeObject* maybe_elem;
+ if (elements->map() == fixed_cow_array_map()) {
+ maybe_elem = FixedArray::cast(elements);
+ } else if (source->HasFastDoubleElements()) {
+ maybe_elem = CopyFixedDoubleArray(FixedDoubleArray::cast(elements));
+ } else {
+ maybe_elem = CopyFixedArray(FixedArray::cast(elements));
+ }
+ if (!maybe_elem->ToObject(&elem)) return maybe_elem;
+ }
+ JSObject::cast(clone)->set_elements(FixedArrayBase::cast(elem), wb_mode);
+ }
+ // Update properties if necessary.
+ if (properties->length() > 0) {
+ Object* prop;
+ { MaybeObject* maybe_prop = CopyFixedArray(properties);
+ if (!maybe_prop->ToObject(&prop)) return maybe_prop;
+ }
+ JSObject::cast(clone)->set_properties(FixedArray::cast(prop), wb_mode);
+ }
+ // Return the new clone.
+ return clone;
+}
+
+
+MaybeObject* Heap::ReinitializeJSReceiver(
+ JSReceiver* object, InstanceType type, int size) {
+ ASSERT(type >= FIRST_JS_OBJECT_TYPE);
+
+ // Allocate fresh map.
+ // TODO(rossberg): Once we optimize proxies, cache these maps.
+ Map* map;
+ 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();
+ ASSERT(size_difference >= 0);
+
+ map->set_prototype(object->map()->prototype());
+
+ // Allocate the backing storage for the properties.
+ int prop_size = map->unused_property_fields() - map->inobject_properties();
+ Object* 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 =
+ InternalizeOneByteString(STATIC_ASCII_VECTOR("<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(jsobj, FixedArray::cast(properties), map);
+
+ // Functions require some minimal initialization.
+ if (type == JS_FUNCTION_TYPE) {
+ map->set_function_with_prototype(true);
+ InitializeFunction(JSFunction::cast(object), shared, the_hole_value());
+ JSFunction::cast(object)->set_context(
+ isolate()->context()->native_context());
+ }
+
+ // Put in filler if the new object is smaller than the old.
+ if (size_difference > 0) {
+ CreateFillerObjectAt(
+ object->address() + map->instance_size(), size_difference);
+ }
+
+ return object;
+}
+
+
+MaybeObject* Heap::ReinitializeJSGlobalProxy(JSFunction* constructor,
+ JSGlobalProxy* object) {
+ ASSERT(constructor->has_initial_map());
+ Map* map = constructor->initial_map();
+
+ // Check that the already allocated object has the same size and type as
+ // objects allocated using the constructor.
+ ASSERT(map->instance_size() == object->map()->instance_size());
+ ASSERT(map->instance_type() == object->map()->instance_type());
+
+ // 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;
+ }
+
+ // Reset the map for the object.
+ object->set_map(constructor->initial_map());
+
+ // Reinitialize the object from the constructor map.
+ InitializeJSObjectFromMap(object, FixedArray::cast(properties), map);
+ return object;
+}
+
+
+MaybeObject* Heap::AllocateStringFromOneByte(Vector<const uint8_t> string,
+ PretenureFlag pretenure) {
+ int length = string.length();
+ if (length == 1) {
+ return Heap::LookupSingleCharacterStringFromCode(string[0]);
+ }
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateRawOneByteString(string.length(), pretenure);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // Copy the characters into the new object.
+ CopyChars(SeqOneByteString::cast(result)->GetChars(),
+ string.start(),
+ length);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateStringFromUtf8Slow(Vector<const char> string,
+ int non_ascii_start,
+ PretenureFlag pretenure) {
+ // Continue counting the number of characters in the UTF-8 string, starting
+ // from the first non-ascii character or word.
+ Access<UnicodeCache::Utf8Decoder>
+ decoder(isolate_->unicode_cache()->utf8_decoder());
+ decoder->Reset(string.start() + non_ascii_start,
+ string.length() - non_ascii_start);
+ int utf16_length = decoder->Utf16Length();
+ ASSERT(utf16_length > 0);
+ // Allocate string.
+ Object* result;
+ {
+ int chars = non_ascii_start + utf16_length;
+ MaybeObject* maybe_result = AllocateRawTwoByteString(chars, pretenure);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Convert and copy the characters into the new object.
+ SeqTwoByteString* twobyte = SeqTwoByteString::cast(result);
+ // Copy ascii portion.
+ uint16_t* data = twobyte->GetChars();
+ if (non_ascii_start != 0) {
+ const char* ascii_data = string.start();
+ for (int i = 0; i < non_ascii_start; i++) {
+ *data++ = *ascii_data++;
+ }
+ }
+ // Now write the remainder.
+ decoder->WriteUtf16(data, utf16_length);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateStringFromTwoByte(Vector<const uc16> string,
+ PretenureFlag pretenure) {
+ // Check if the string is an ASCII string.
+ Object* result;
+ int length = string.length();
+ const uc16* start = string.start();
+
+ if (String::IsOneByte(start, length)) {
+ MaybeObject* maybe_result = AllocateRawOneByteString(length, pretenure);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ CopyChars(SeqOneByteString::cast(result)->GetChars(), start, length);
+ } else { // It's not a one byte string.
+ MaybeObject* maybe_result = AllocateRawTwoByteString(length, pretenure);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ CopyChars(SeqTwoByteString::cast(result)->GetChars(), start, length);
+ }
+ return result;
+}
+
+
+Map* Heap::InternalizedStringMapForString(String* string) {
+ // If the string is in new space it cannot be used as internalized.
+ if (InNewSpace(string)) return NULL;
+
+ // Find the corresponding internalized string map for strings.
+ switch (string->map()->instance_type()) {
+ case STRING_TYPE: return internalized_string_map();
+ case ASCII_STRING_TYPE: return ascii_internalized_string_map();
+ case CONS_STRING_TYPE: return cons_internalized_string_map();
+ case CONS_ASCII_STRING_TYPE: return cons_ascii_internalized_string_map();
+ case EXTERNAL_STRING_TYPE: return external_internalized_string_map();
+ case EXTERNAL_ASCII_STRING_TYPE:
+ return external_ascii_internalized_string_map();
+ case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
+ return external_internalized_string_with_one_byte_data_map();
+ case SHORT_EXTERNAL_STRING_TYPE:
+ return short_external_internalized_string_map();
+ case SHORT_EXTERNAL_ASCII_STRING_TYPE:
+ return short_external_ascii_internalized_string_map();
+ case SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
+ return short_external_internalized_string_with_one_byte_data_map();
+ default: return NULL; // No match found.
+ }
+}
+
+
+static inline void WriteOneByteData(Vector<const char> vector,
+ uint8_t* chars,
+ int len) {
+ // Only works for ascii.
+ ASSERT(vector.length() == len);
+ OS::MemCopy(chars, vector.start(), len);
+}
+
+static inline void WriteTwoByteData(Vector<const char> vector,
+ uint16_t* chars,
+ int len) {
+ const uint8_t* stream = reinterpret_cast<const uint8_t*>(vector.start());
+ unsigned stream_length = vector.length();
+ while (stream_length != 0) {
+ unsigned consumed = 0;
+ uint32_t c = unibrow::Utf8::ValueOf(stream, stream_length, &consumed);
+ ASSERT(c != unibrow::Utf8::kBadChar);
+ ASSERT(consumed <= stream_length);
+ stream_length -= consumed;
+ stream += consumed;
+ if (c > unibrow::Utf16::kMaxNonSurrogateCharCode) {
+ len -= 2;
+ if (len < 0) break;
+ *chars++ = unibrow::Utf16::LeadSurrogate(c);
+ *chars++ = unibrow::Utf16::TrailSurrogate(c);
+ } else {
+ len -= 1;
+ if (len < 0) break;
+ *chars++ = c;
+ }
+ }
+ ASSERT(stream_length == 0);
+ ASSERT(len == 0);
+}
+
+
+static inline void WriteOneByteData(String* s, uint8_t* chars, int len) {
+ ASSERT(s->length() == len);
+ String::WriteToFlat(s, chars, 0, len);
+}
+
+
+static inline void WriteTwoByteData(String* s, uint16_t* chars, int len) {
+ ASSERT(s->length() == len);
+ String::WriteToFlat(s, chars, 0, len);
+}
+
+
+template<bool is_one_byte, typename T>
+MaybeObject* Heap::AllocateInternalizedStringImpl(
+ T t, int chars, uint32_t hash_field) {
+ ASSERT(chars >= 0);
+ // Compute map and object size.
+ int size;
+ Map* map;
+
+ if (is_one_byte) {
+ if (chars > SeqOneByteString::kMaxLength) {
+ return Failure::OutOfMemoryException(0x9);
+ }
+ map = ascii_internalized_string_map();
+ size = SeqOneByteString::SizeFor(chars);
+ } else {
+ if (chars > SeqTwoByteString::kMaxLength) {
+ return Failure::OutOfMemoryException(0xa);
+ }
+ map = internalized_string_map();
+ size = SeqTwoByteString::SizeFor(chars);
+ }
+
+ // Allocate string.
+ Object* result;
+ { MaybeObject* maybe_result = (size > Page::kMaxNonCodeHeapObjectSize)
+ ? 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_no_write_barrier(map);
+ // Set length and hash fields of the allocated string.
+ String* answer = String::cast(result);
+ answer->set_length(chars);
+ answer->set_hash_field(hash_field);
+
+ ASSERT_EQ(size, answer->Size());
+
+ if (is_one_byte) {
+ WriteOneByteData(t, SeqOneByteString::cast(answer)->GetChars(), chars);
+ } else {
+ WriteTwoByteData(t, SeqTwoByteString::cast(answer)->GetChars(), chars);
+ }
+ return answer;
+}
+
+
+// Need explicit instantiations.
+template
+MaybeObject* Heap::AllocateInternalizedStringImpl<true>(String*, int, uint32_t);
+template
+MaybeObject* Heap::AllocateInternalizedStringImpl<false>(
+ String*, int, uint32_t);
+template
+MaybeObject* Heap::AllocateInternalizedStringImpl<false>(
+ Vector<const char>, int, uint32_t);
+
+
+MaybeObject* Heap::AllocateRawOneByteString(int length,
+ PretenureFlag pretenure) {
+ if (length < 0 || length > SeqOneByteString::kMaxLength) {
+ return Failure::OutOfMemoryException(0xb);
+ }
+ int size = SeqOneByteString::SizeFor(length);
+ ASSERT(size <= SeqOneByteString::kMaxSize);
+ AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+ AllocationSpace retry_space = OLD_DATA_SPACE;
+
+ if (size > Page::kMaxNonCodeHeapObjectSize) {
+ // Allocate in large object space, retry space will be ignored.
+ space = LO_SPACE;
+ }
+
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRaw(size, space, retry_space);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // Partially initialize the object.
+ 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());
+
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateRawTwoByteString(int length,
+ PretenureFlag pretenure) {
+ if (length < 0 || length > SeqTwoByteString::kMaxLength) {
+ return Failure::OutOfMemoryException(0xc);
+ }
+ int size = SeqTwoByteString::SizeFor(length);
+ ASSERT(size <= SeqTwoByteString::kMaxSize);
+ AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+ AllocationSpace retry_space = OLD_DATA_SPACE;
+
+ if (size > Page::kMaxNonCodeHeapObjectSize) {
+ // Allocate in large object space, retry space will be ignored.
+ space = LO_SPACE;
+ }
+
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRaw(size, space, retry_space);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // Partially initialize the object.
+ 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());
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateJSArray(
+ ElementsKind elements_kind,
+ PretenureFlag pretenure) {
+ Context* native_context = isolate()->context()->native_context();
+ JSFunction* array_function = native_context->array_function();
+ Map* map = array_function->initial_map();
+ Map* transition_map = isolate()->get_initial_js_array_map(elements_kind);
+ if (transition_map != NULL) map = transition_map;
+ return AllocateJSObjectFromMap(map, pretenure);
+}
+
+
+MaybeObject* Heap::AllocateJSArrayWithAllocationSite(
+ ElementsKind elements_kind,
+ Handle<AllocationSite> allocation_site) {
+ Context* native_context = isolate()->context()->native_context();
+ JSFunction* array_function = native_context->array_function();
+ Map* map = array_function->initial_map();
+ Object* maybe_map_array = native_context->js_array_maps();
+ if (!maybe_map_array->IsUndefined()) {
+ Object* maybe_transitioned_map =
+ FixedArray::cast(maybe_map_array)->get(elements_kind);
+ if (!maybe_transitioned_map->IsUndefined()) {
+ map = Map::cast(maybe_transitioned_map);
+ }
+ }
+ return AllocateJSObjectFromMapWithAllocationSite(map, allocation_site);
+}
+
+
+MaybeObject* Heap::AllocateEmptyFixedArray() {
+ int size = FixedArray::SizeFor(0);
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateRaw(size, OLD_DATA_SPACE, OLD_DATA_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Initialize the object.
+ reinterpret_cast<FixedArray*>(result)->set_map_no_write_barrier(
+ fixed_array_map());
+ reinterpret_cast<FixedArray*>(result)->set_length(0);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateEmptyExternalArray(ExternalArrayType array_type) {
+ return AllocateExternalArray(0, array_type, NULL, TENURED);
+}
+
+
+MaybeObject* Heap::AllocateRawFixedArray(int length) {
+ if (length < 0 || length > FixedArray::kMaxLength) {
+ return Failure::OutOfMemoryException(0xd);
+ }
+ ASSERT(length > 0);
+ // Use the general function if we're forced to always allocate.
+ if (always_allocate()) return AllocateFixedArray(length, TENURED);
+ // Allocate the raw data for a fixed array.
+ int size = FixedArray::SizeFor(length);
+ return size <= Page::kMaxNonCodeHeapObjectSize
+ ? new_space_.AllocateRaw(size)
+ : lo_space_->AllocateRaw(size, NOT_EXECUTABLE);
+}
+
+
+MaybeObject* Heap::CopyFixedArrayWithMap(FixedArray* src, Map* map) {
+ int len = src->length();
+ Object* obj;
+ { MaybeObject* maybe_obj = AllocateRawFixedArray(len);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ if (InNewSpace(obj)) {
+ HeapObject* dst = HeapObject::cast(obj);
+ 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_no_write_barrier(map);
+ FixedArray* result = FixedArray::cast(obj);
+ result->set_length(len);
+
+ // Copy the content
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
+ for (int i = 0; i < len; i++) result->set(i, src->get(i), mode);
+ return result;
+}
+
+
+MaybeObject* Heap::CopyFixedDoubleArrayWithMap(FixedDoubleArray* src,
+ Map* map) {
+ int len = src->length();
+ Object* obj;
+ { MaybeObject* maybe_obj = AllocateRawFixedDoubleArray(len, NOT_TENURED);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ HeapObject* dst = HeapObject::cast(obj);
+ dst->set_map_no_write_barrier(map);
+ CopyBlock(
+ dst->address() + FixedDoubleArray::kLengthOffset,
+ src->address() + FixedDoubleArray::kLengthOffset,
+ FixedDoubleArray::SizeFor(len) - FixedDoubleArray::kLengthOffset);
+ return obj;
+}
+
+
+MaybeObject* Heap::AllocateFixedArray(int length) {
+ ASSERT(length >= 0);
+ if (length == 0) return empty_fixed_array();
+ Object* result;
+ { MaybeObject* maybe_result = AllocateRawFixedArray(length);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Initialize header.
+ FixedArray* array = reinterpret_cast<FixedArray*>(result);
+ array->set_map_no_write_barrier(fixed_array_map());
+ array->set_length(length);
+ // Initialize body.
+ ASSERT(!InNewSpace(undefined_value()));
+ MemsetPointer(array->data_start(), undefined_value(), length);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateRawFixedArray(int length, PretenureFlag pretenure) {
+ if (length < 0 || length > FixedArray::kMaxLength) {
+ return Failure::OutOfMemoryException(0xe);
+ }
+ int size = FixedArray::SizeFor(length);
+ AllocationSpace space =
+ (pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
+ AllocationSpace retry_space = OLD_POINTER_SPACE;
+
+ if (size > Page::kMaxNonCodeHeapObjectSize) {
+ // Allocate in large object space, retry space will be ignored.
+ space = LO_SPACE;
+ }
+
+ return AllocateRaw(size, space, retry_space);
+}
+
+
+MUST_USE_RESULT static MaybeObject* AllocateFixedArrayWithFiller(
+ Heap* heap,
+ int length,
+ PretenureFlag pretenure,
+ Object* filler) {
+ ASSERT(length >= 0);
+ ASSERT(heap->empty_fixed_array()->IsFixedArray());
+ if (length == 0) return heap->empty_fixed_array();
+
+ ASSERT(!heap->InNewSpace(filler));
+ Object* result;
+ { MaybeObject* maybe_result = heap->AllocateRawFixedArray(length, pretenure);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ 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);
+ return array;
+}
+
+
+MaybeObject* Heap::AllocateFixedArray(int length, PretenureFlag pretenure) {
+ return AllocateFixedArrayWithFiller(this,
+ length,
+ pretenure,
+ undefined_value());
+}
+
+
+MaybeObject* Heap::AllocateFixedArrayWithHoles(int length,
+ PretenureFlag pretenure) {
+ return AllocateFixedArrayWithFiller(this,
+ length,
+ pretenure,
+ the_hole_value());
+}
+
+
+MaybeObject* Heap::AllocateUninitializedFixedArray(int length) {
+ if (length == 0) return empty_fixed_array();
+
+ Object* obj;
+ { MaybeObject* maybe_obj = AllocateRawFixedArray(length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ reinterpret_cast<FixedArray*>(obj)->set_map_no_write_barrier(
+ fixed_array_map());
+ FixedArray::cast(obj)->set_length(length);
+ return obj;
+}
+
+
+MaybeObject* Heap::AllocateEmptyFixedDoubleArray() {
+ int size = FixedDoubleArray::SizeFor(0);
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateRaw(size, OLD_DATA_SPACE, OLD_DATA_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Initialize the object.
+ reinterpret_cast<FixedDoubleArray*>(result)->set_map_no_write_barrier(
+ fixed_double_array_map());
+ reinterpret_cast<FixedDoubleArray*>(result)->set_length(0);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateUninitializedFixedDoubleArray(
+ int length,
+ PretenureFlag pretenure) {
+ if (length == 0) return empty_fixed_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);
+
+ 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_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);
+ }
+
+ elements->set_map_no_write_barrier(fixed_double_array_map());
+ elements->set_length(length);
+ return elements;
+}
+
+
+MaybeObject* Heap::AllocateRawFixedDoubleArray(int length,
+ PretenureFlag pretenure) {
+ if (length < 0 || length > FixedDoubleArray::kMaxLength) {
+ return Failure::OutOfMemoryException(0xf);
+ }
+ int size = FixedDoubleArray::SizeFor(length);
+ AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
+ AllocationSpace retry_space = OLD_DATA_SPACE;
+
+#ifndef V8_HOST_ARCH_64_BIT
+ size += kPointerSize;
+#endif
+
+ if (size > Page::kMaxNonCodeHeapObjectSize) {
+ // Allocate in large object space, retry space will be ignored.
+ space = LO_SPACE;
+ }
+
+ HeapObject* object;
+ { MaybeObject* maybe_object = AllocateRaw(size, space, retry_space);
+ if (!maybe_object->To<HeapObject>(&object)) return maybe_object;
+ }
+
+ return EnsureDoubleAligned(this, object, size);
+}
+
+
+MaybeObject* Heap::AllocateHashTable(int length, PretenureFlag pretenure) {
+ Object* result;
+ { MaybeObject* maybe_result = AllocateFixedArray(length, pretenure);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ reinterpret_cast<HeapObject*>(result)->set_map_no_write_barrier(
+ hash_table_map());
+ ASSERT(result->IsHashTable());
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateSymbol() {
+ // Statically ensure that it is safe to allocate symbols in paged spaces.
+ STATIC_ASSERT(Symbol::kSize <= Page::kNonCodeObjectAreaSize);
+
+ Object* result;
+ MaybeObject* maybe =
+ AllocateRaw(Symbol::kSize, OLD_POINTER_SPACE, OLD_POINTER_SPACE);
+ if (!maybe->ToObject(&result)) return maybe;
+
+ HeapObject::cast(result)->set_map_no_write_barrier(symbol_map());
+
+ // Generate a random hash value.
+ int hash;
+ int attempts = 0;
+ do {
+ hash = V8::RandomPrivate(isolate()) & Name::kHashBitMask;
+ attempts++;
+ } while (hash == 0 && attempts < 30);
+ if (hash == 0) hash = 1; // never return 0
+
+ Symbol::cast(result)->set_hash_field(
+ Name::kIsNotArrayIndexMask | (hash << Name::kHashShift));
+ Symbol::cast(result)->set_name(undefined_value());
+
+ ASSERT(result->IsSymbol());
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateNativeContext() {
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateFixedArray(Context::NATIVE_CONTEXT_SLOTS);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Context* context = reinterpret_cast<Context*>(result);
+ context->set_map_no_write_barrier(native_context_map());
+ context->set_js_array_maps(undefined_value());
+ ASSERT(context->IsNativeContext());
+ ASSERT(result->IsContext());
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateGlobalContext(JSFunction* function,
+ ScopeInfo* scope_info) {
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateFixedArray(scope_info->ContextLength(), TENURED);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Context* context = reinterpret_cast<Context*>(result);
+ context->set_map_no_write_barrier(global_context_map());
+ context->set_closure(function);
+ context->set_previous(function->context());
+ context->set_extension(scope_info);
+ context->set_global_object(function->context()->global_object());
+ ASSERT(context->IsGlobalContext());
+ ASSERT(result->IsContext());
+ return context;
+}
+
+
+MaybeObject* Heap::AllocateModuleContext(ScopeInfo* scope_info) {
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateFixedArray(scope_info->ContextLength(), TENURED);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Context* context = reinterpret_cast<Context*>(result);
+ context->set_map_no_write_barrier(module_context_map());
+ // Instance link will be set later.
+ context->set_extension(Smi::FromInt(0));
+ return context;
+}
+
+
+MaybeObject* Heap::AllocateFunctionContext(int length, JSFunction* function) {
+ ASSERT(length >= Context::MIN_CONTEXT_SLOTS);
+ Object* result;
+ { MaybeObject* maybe_result = AllocateFixedArray(length);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Context* context = reinterpret_cast<Context*>(result);
+ context->set_map_no_write_barrier(function_context_map());
+ context->set_closure(function);
+ context->set_previous(function->context());
+ context->set_extension(Smi::FromInt(0));
+ context->set_global_object(function->context()->global_object());
+ return context;
+}
+
+
+MaybeObject* Heap::AllocateCatchContext(JSFunction* function,
+ Context* previous,
+ String* name,
+ Object* thrown_object) {
+ STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == Context::THROWN_OBJECT_INDEX);
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateFixedArray(Context::MIN_CONTEXT_SLOTS + 1);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Context* context = reinterpret_cast<Context*>(result);
+ context->set_map_no_write_barrier(catch_context_map());
+ context->set_closure(function);
+ context->set_previous(previous);
+ context->set_extension(name);
+ context->set_global_object(previous->global_object());
+ context->set(Context::THROWN_OBJECT_INDEX, thrown_object);
+ return context;
+}
+
+
+MaybeObject* Heap::AllocateWithContext(JSFunction* function,
+ Context* previous,
+ JSReceiver* extension) {
+ Object* result;
+ { MaybeObject* maybe_result = AllocateFixedArray(Context::MIN_CONTEXT_SLOTS);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Context* context = reinterpret_cast<Context*>(result);
+ context->set_map_no_write_barrier(with_context_map());
+ context->set_closure(function);
+ context->set_previous(previous);
+ context->set_extension(extension);
+ context->set_global_object(previous->global_object());
+ return context;
+}
+
+
+MaybeObject* Heap::AllocateBlockContext(JSFunction* function,
+ Context* previous,
+ ScopeInfo* scope_info) {
+ Object* result;
+ { MaybeObject* maybe_result =
+ AllocateFixedArrayWithHoles(scope_info->ContextLength());
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Context* context = reinterpret_cast<Context*>(result);
+ context->set_map_no_write_barrier(block_context_map());
+ context->set_closure(function);
+ context->set_previous(previous);
+ context->set_extension(scope_info);
+ context->set_global_object(previous->global_object());
+ return context;
+}
+
+
+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;
+}
+
+
+MaybeObject* Heap::AllocateExternal(void* value) {
+ Foreign* foreign;
+ { MaybeObject* maybe_result = AllocateForeign(static_cast<Address>(value));
+ if (!maybe_result->To(&foreign)) return maybe_result;
+ }
+ JSObject* external;
+ { MaybeObject* maybe_result = AllocateJSObjectFromMap(external_map());
+ if (!maybe_result->To(&external)) return maybe_result;
+ }
+ external->SetInternalField(0, foreign);
+ return external;
+}
+
+
+MaybeObject* Heap::AllocateStruct(InstanceType type) {
+ Map* map;
+ switch (type) {
+#define MAKE_CASE(NAME, Name, name) \
+ case NAME##_TYPE: map = name##_map(); break;
+STRUCT_LIST(MAKE_CASE)
+#undef MAKE_CASE
+ default:
+ UNREACHABLE();
+ return Failure::InternalError();
+ }
+ int size = map->instance_size();
+ AllocationSpace space =
+ (size > Page::kMaxNonCodeHeapObjectSize) ? LO_SPACE : OLD_POINTER_SPACE;
+ Object* result;
+ { MaybeObject* maybe_result = Allocate(map, space);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Struct::cast(result)->InitializeBody(size);
+ return result;
+}
+
+
+bool Heap::IsHeapIterable() {
+ return (!old_pointer_space()->was_swept_conservatively() &&
+ !old_data_space()->was_swept_conservatively());
+}
+
+
+void Heap::EnsureHeapIsIterable() {
+ ASSERT(AllowHeapAllocation::IsAllowed());
+ if (!IsHeapIterable()) {
+ CollectAllGarbage(kMakeHeapIterableMask, "Heap::EnsureHeapIsIterable");
+ }
+ ASSERT(IsHeapIterable());
+}
+
+
+void Heap::AdvanceIdleIncrementalMarking(intptr_t step_size) {
+ incremental_marking()->Step(step_size,
+ IncrementalMarking::NO_GC_VIA_STACK_GUARD);
+
+ 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");
+ mark_sweeps_since_idle_round_started_++;
+ gc_count_at_last_idle_gc_ = gc_count_;
+ if (uncommit) {
+ new_space_.Shrink();
+ UncommitFromSpace();
+ }
+ }
+}
+
+
+bool Heap::IdleNotification(int hint) {
+ // Hints greater than this value indicate that
+ // the embedder is requesting a lot of GC work.
+ const int kMaxHint = 1000;
+ const int kMinHintForIncrementalMarking = 10;
+ // Minimal hint that allows to do full GC.
+ const int kMinHintForFullGC = 100;
+ intptr_t size_factor = Min(Max(hint, 20), kMaxHint) / 4;
+ // The size factor is in range [5..250]. The numbers here are chosen from
+ // experiments. If you changes them, make sure to test with
+ // chrome/performance_ui_tests --gtest_filter="GeneralMixMemoryTest.*
+ intptr_t step_size =
+ size_factor * IncrementalMarking::kAllocatedThreshold;
+
+ if (contexts_disposed_ > 0) {
+ if (hint >= kMaxHint) {
+ // The embedder is requesting a lot of GC work after context disposal,
+ // we age inline caches so that they don't keep objects from
+ // the old context alive.
+ AgeInlineCaches();
+ }
+ int mark_sweep_time = Min(TimeMarkSweepWouldTakeInMs(), 1000);
+ if (hint >= mark_sweep_time && !FLAG_expose_gc &&
+ incremental_marking()->IsStopped()) {
+ HistogramTimerScope scope(isolate_->counters()->gc_context());
+ CollectAllGarbage(kReduceMemoryFootprintMask,
+ "idle notification: contexts disposed");
+ } else {
+ AdvanceIdleIncrementalMarking(step_size);
+ contexts_disposed_ = 0;
+ }
+ // After context disposal there is likely a lot of garbage remaining, reset
+ // the idle notification counters in order to trigger more incremental GCs
+ // on subsequent idle notifications.
+ StartIdleRound();
+ return false;
+ }
+
+ if (!FLAG_incremental_marking || FLAG_expose_gc || Serializer::enabled()) {
+ return IdleGlobalGC();
+ }
+
+ // 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.
+
+ if (incremental_marking()->IsStopped()) {
+ if (!mark_compact_collector()->AreSweeperThreadsActivated() &&
+ !IsSweepingComplete() &&
+ !AdvanceSweepers(static_cast<int>(step_size))) {
+ return false;
+ }
+ }
+
+ if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
+ if (EnoughGarbageSinceLastIdleRound()) {
+ StartIdleRound();
+ } else {
+ return true;
+ }
+ }
+
+ int remaining_mark_sweeps = kMaxMarkSweepsInIdleRound -
+ mark_sweeps_since_idle_round_started_;
+
+ if (incremental_marking()->IsStopped()) {
+ // If there are no more than two GCs left in this idle round and we are
+ // allowed to do a full GC, then make those GCs full in order to compact
+ // the code space.
+ // TODO(ulan): Once we enable code compaction for incremental marking,
+ // we can get rid of this special case and always start incremental marking.
+ if (remaining_mark_sweeps <= 2 && hint >= kMinHintForFullGC) {
+ CollectAllGarbage(kReduceMemoryFootprintMask,
+ "idle notification: finalize idle round");
+ mark_sweeps_since_idle_round_started_++;
+ } else if (hint > kMinHintForIncrementalMarking) {
+ incremental_marking()->Start();
+ }
+ }
+ if (!incremental_marking()->IsStopped() &&
+ hint > kMinHintForIncrementalMarking) {
+ AdvanceIdleIncrementalMarking(step_size);
+ }
+
+ if (mark_sweeps_since_idle_round_started_ >= kMaxMarkSweepsInIdleRound) {
+ FinishIdleRound();
+ return true;
+ }
+
+ return false;
+}
+
+
+bool Heap::IdleGlobalGC() {
+ static const int kIdlesBeforeScavenge = 4;
+ static const int kIdlesBeforeMarkSweep = 7;
+ static const int kIdlesBeforeMarkCompact = 8;
+ static const int kMaxIdleCount = kIdlesBeforeMarkCompact + 1;
+ static const unsigned int kGCsBetweenCleanup = 4;
+
+ if (!last_idle_notification_gc_count_init_) {
+ last_idle_notification_gc_count_ = gc_count_;
+ last_idle_notification_gc_count_init_ = true;
+ }
+
+ bool uncommit = true;
+ bool finished = false;
+
+ // Reset the number of idle notifications received when a number of
+ // GCs have taken place. This allows another round of cleanup based
+ // on idle notifications if enough work has been carried out to
+ // provoke a number of garbage collections.
+ if (gc_count_ - last_idle_notification_gc_count_ < kGCsBetweenCleanup) {
+ number_idle_notifications_ =
+ Min(number_idle_notifications_ + 1, kMaxIdleCount);
+ } else {
+ number_idle_notifications_ = 0;
+ last_idle_notification_gc_count_ = gc_count_;
+ }
+
+ if (number_idle_notifications_ == kIdlesBeforeScavenge) {
+ CollectGarbage(NEW_SPACE, "idle notification");
+ new_space_.Shrink();
+ last_idle_notification_gc_count_ = gc_count_;
+ } else if (number_idle_notifications_ == kIdlesBeforeMarkSweep) {
+ // Before doing the mark-sweep collections we clear the
+ // compilation cache to avoid hanging on to source code and
+ // generated code for cached functions.
+ isolate_->compilation_cache()->Clear();
+
+ CollectAllGarbage(kReduceMemoryFootprintMask, "idle notification");
+ new_space_.Shrink();
+ last_idle_notification_gc_count_ = gc_count_;
+
+ } else if (number_idle_notifications_ == kIdlesBeforeMarkCompact) {
+ CollectAllGarbage(kReduceMemoryFootprintMask, "idle notification");
+ new_space_.Shrink();
+ last_idle_notification_gc_count_ = gc_count_;
+ number_idle_notifications_ = 0;
+ finished = true;
+ } else if (number_idle_notifications_ > kIdlesBeforeMarkCompact) {
+ // If we have received more than kIdlesBeforeMarkCompact idle
+ // notifications we do not perform any cleanup because we don't
+ // expect to gain much by doing so.
+ finished = true;
+ }
+
+ if (uncommit) UncommitFromSpace();
+
+ return finished;
+}
+
+
+#ifdef DEBUG
+
+void Heap::Print() {
+ if (!HasBeenSetUp()) return;
+ isolate()->PrintStack(stdout);
+ AllSpaces spaces(this);
+ for (Space* space = spaces.next(); space != NULL; space = spaces.next()) {
+ space->Print();
+ }
+}
+
+
+void Heap::ReportCodeStatistics(const char* title) {
+ PrintF(">>>>>> Code Stats (%s) >>>>>>\n", title);
+ PagedSpace::ResetCodeStatistics();
+ // We do not look for code in new space, map space, or old space. If code
+ // somehow ends up in those spaces, we would miss it here.
+ code_space_->CollectCodeStatistics();
+ lo_space_->CollectCodeStatistics();
+ PagedSpace::ReportCodeStatistics();
+}
+
+
+// This function expects that NewSpace's allocated objects histogram is
+// populated (via a call to CollectStatistics or else as a side effect of a
+// just-completed scavenge collection).
+void Heap::ReportHeapStatistics(const char* title) {
+ USE(title);
+ PrintF(">>>>>> =============== %s (%d) =============== >>>>>>\n",
+ title, gc_count_);
+ PrintF("old_generation_allocation_limit_ %" V8_PTR_PREFIX "d\n",
+ old_generation_allocation_limit_);
+
+ PrintF("\n");
+ PrintF("Number of handles : %d\n", HandleScope::NumberOfHandles(isolate_));
+ isolate_->global_handles()->PrintStats();
+ PrintF("\n");
+
+ PrintF("Heap statistics : ");
+ isolate_->memory_allocator()->ReportStatistics();
+ PrintF("To space : ");
+ new_space_.ReportStatistics();
+ PrintF("Old pointer space : ");
+ old_pointer_space_->ReportStatistics();
+ PrintF("Old data space : ");
+ old_data_space_->ReportStatistics();
+ PrintF("Code space : ");
+ code_space_->ReportStatistics();
+ PrintF("Map space : ");
+ map_space_->ReportStatistics();
+ PrintF("Cell space : ");
+ cell_space_->ReportStatistics();
+ PrintF("PropertyCell space : ");
+ property_cell_space_->ReportStatistics();
+ PrintF("Large object space : ");
+ lo_space_->ReportStatistics();
+ PrintF(">>>>>> ========================================= >>>>>>\n");
+}
+
+#endif // DEBUG
+
+bool Heap::Contains(HeapObject* value) {
+ return Contains(value->address());
+}
+
+
+bool Heap::Contains(Address addr) {
+ if (OS::IsOutsideAllocatedSpace(addr)) return false;
+ return HasBeenSetUp() &&
+ (new_space_.ToSpaceContains(addr) ||
+ old_pointer_space_->Contains(addr) ||
+ old_data_space_->Contains(addr) ||
+ code_space_->Contains(addr) ||
+ map_space_->Contains(addr) ||
+ cell_space_->Contains(addr) ||
+ property_cell_space_->Contains(addr) ||
+ lo_space_->SlowContains(addr));
+}
+
+
+bool Heap::InSpace(HeapObject* value, AllocationSpace space) {
+ return InSpace(value->address(), space);
+}
+
+
+bool Heap::InSpace(Address addr, AllocationSpace space) {
+ if (OS::IsOutsideAllocatedSpace(addr)) return false;
+ if (!HasBeenSetUp()) return false;
+
+ switch (space) {
+ case NEW_SPACE:
+ return new_space_.ToSpaceContains(addr);
+ case OLD_POINTER_SPACE:
+ return old_pointer_space_->Contains(addr);
+ case OLD_DATA_SPACE:
+ return old_data_space_->Contains(addr);
+ case CODE_SPACE:
+ return code_space_->Contains(addr);
+ case MAP_SPACE:
+ return map_space_->Contains(addr);
+ case CELL_SPACE:
+ return cell_space_->Contains(addr);
+ case PROPERTY_CELL_SPACE:
+ return property_cell_space_->Contains(addr);
+ case LO_SPACE:
+ return lo_space_->SlowContains(addr);
+ }
+
+ return false;
+}
+
+
+#ifdef VERIFY_HEAP
+void Heap::Verify() {
+ CHECK(HasBeenSetUp());
+
+ store_buffer()->Verify();
+
+ VerifyPointersVisitor visitor;
+ IterateRoots(&visitor, VISIT_ONLY_STRONG);
+
+ new_space_.Verify();
+
+ old_pointer_space_->Verify(&visitor);
+ map_space_->Verify(&visitor);
+
+ VerifyPointersVisitor no_dirty_regions_visitor;
+ old_data_space_->Verify(&no_dirty_regions_visitor);
+ code_space_->Verify(&no_dirty_regions_visitor);
+ cell_space_->Verify(&no_dirty_regions_visitor);
+ property_cell_space_->Verify(&no_dirty_regions_visitor);
+
+ lo_space_->Verify();
+}
+#endif
+
+
+MaybeObject* Heap::InternalizeUtf8String(Vector<const char> string) {
+ Object* result = NULL;
+ Object* new_table;
+ { MaybeObject* maybe_new_table =
+ string_table()->LookupUtf8String(string, &result);
+ if (!maybe_new_table->ToObject(&new_table)) return maybe_new_table;
+ }
+ // Can't use set_string_table because StringTable::cast knows that
+ // StringTable is a singleton and checks for identity.
+ roots_[kStringTableRootIndex] = new_table;
+ ASSERT(result != NULL);
+ return result;
+}
+
+
+MaybeObject* Heap::InternalizeOneByteString(Vector<const uint8_t> string) {
+ Object* result = NULL;
+ Object* new_table;
+ { MaybeObject* maybe_new_table =
+ string_table()->LookupOneByteString(string, &result);
+ if (!maybe_new_table->ToObject(&new_table)) return maybe_new_table;
+ }
+ // Can't use set_string_table because StringTable::cast knows that
+ // StringTable is a singleton and checks for identity.
+ roots_[kStringTableRootIndex] = new_table;
+ ASSERT(result != NULL);
+ return result;
+}
+
+
+MaybeObject* Heap::InternalizeOneByteString(Handle<SeqOneByteString> string,
+ int from,
+ int length) {
+ Object* result = NULL;
+ Object* new_table;
+ { MaybeObject* maybe_new_table =
+ string_table()->LookupSubStringOneByteString(string,
+ from,
+ length,
+ &result);
+ if (!maybe_new_table->ToObject(&new_table)) return maybe_new_table;
+ }
+ // Can't use set_string_table because StringTable::cast knows that
+ // StringTable is a singleton and checks for identity.
+ roots_[kStringTableRootIndex] = new_table;
+ ASSERT(result != NULL);
+ return result;
+}
+
+
+MaybeObject* Heap::InternalizeTwoByteString(Vector<const uc16> string) {
+ Object* result = NULL;
+ Object* new_table;
+ { MaybeObject* maybe_new_table =
+ string_table()->LookupTwoByteString(string, &result);
+ if (!maybe_new_table->ToObject(&new_table)) return maybe_new_table;
+ }
+ // Can't use set_string_table because StringTable::cast knows that
+ // StringTable is a singleton and checks for identity.
+ roots_[kStringTableRootIndex] = new_table;
+ ASSERT(result != NULL);
+ return result;
+}
+
+
+MaybeObject* Heap::InternalizeString(String* string) {
+ if (string->IsInternalizedString()) return string;
+ Object* result = NULL;
+ Object* new_table;
+ { MaybeObject* maybe_new_table =
+ string_table()->LookupString(string, &result);
+ if (!maybe_new_table->ToObject(&new_table)) return maybe_new_table;
+ }
+ // Can't use set_string_table because StringTable::cast knows that
+ // StringTable is a singleton and checks for identity.
+ roots_[kStringTableRootIndex] = new_table;
+ ASSERT(result != NULL);
+ return result;
+}
+
+
+bool Heap::InternalizeStringIfExists(String* string, String** result) {
+ if (string->IsInternalizedString()) {
+ *result = string;
+ return true;
+ }
+ return string_table()->LookupStringIfExists(string, result);
+}
+
+
+void Heap::ZapFromSpace() {
+ NewSpacePageIterator it(new_space_.FromSpaceStart(),
+ new_space_.FromSpaceEnd());
+ while (it.has_next()) {
+ NewSpacePage* page = it.next();
+ for (Address cursor = page->area_start(), limit = page->area_end();
+ cursor < limit;
+ cursor += kPointerSize) {
+ Memory::Address_at(cursor) = kFromSpaceZapValue;
+ }
+ }
+}
+
+
+void Heap::IterateAndMarkPointersToFromSpace(Address start,
+ Address end,
+ ObjectSlotCallback callback) {
+ Address slot_address = start;
+
+ // 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);
+ 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;
+ }
+}
+
+
+#ifdef DEBUG
+typedef bool (*CheckStoreBufferFilter)(Object** addr);
+
+
+bool IsAMapPointerAddress(Object** addr) {
+ uintptr_t a = reinterpret_cast<uintptr_t>(addr);
+ int mod = a % Map::kSize;
+ return mod >= Map::kPointerFieldsBeginOffset &&
+ mod < Map::kPointerFieldsEndOffset;
+}
+
+
+bool EverythingsAPointer(Object** addr) {
+ return 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();
+ }
+ }
+}
+
+
+// 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);
+
+ store_buffer()->SortUniq();
+
+ while (pages.has_next()) {
+ Page* page = pages.next();
+ Object** current = reinterpret_cast<Object**>(page->area_start());
+
+ Address end = page->area_end();
+
+ Object*** store_buffer_position = store_buffer()->Start();
+ Object*** store_buffer_top = store_buffer()->Top();
+
+ Object** limit = reinterpret_cast<Object**>(end);
+ CheckStoreBuffer(this,
+ current,
+ limit,
+ &store_buffer_position,
+ store_buffer_top,
+ &EverythingsAPointer,
+ space->top(),
+ space->limit());
+ }
+}
+
+
+void Heap::MapSpaceCheckStoreBuffer() {
+ MapSpace* space = map_space();
+ PageIterator pages(space);
+
+ store_buffer()->SortUniq();
+
+ while (pages.has_next()) {
+ Page* page = pages.next();
+ Object** current = reinterpret_cast<Object**>(page->area_start());
+
+ Address end = page->area_end();
+
+ Object*** store_buffer_position = store_buffer()->Start();
+ Object*** store_buffer_top = store_buffer()->Top();
+
+ Object** limit = reinterpret_cast<Object**>(end);
+ CheckStoreBuffer(this,
+ current,
+ limit,
+ &store_buffer_position,
+ store_buffer_top,
+ &IsAMapPointerAddress,
+ space->top(),
+ space->limit());
+ }
+}
+
+
+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);
+ }
+ }
+}
+#endif
+
+
+void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) {
+ IterateStrongRoots(v, mode);
+ IterateWeakRoots(v, mode);
+}
+
+
+void Heap::IterateWeakRoots(ObjectVisitor* v, VisitMode mode) {
+ v->VisitPointer(reinterpret_cast<Object**>(&roots_[kStringTableRootIndex]));
+ v->Synchronize(VisitorSynchronization::kStringTable);
+ 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(VisitorSynchronization::kExternalStringsTable);
+}
+
+
+void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
+ v->VisitPointers(&roots_[0], &roots_[kStrongRootListLength]);
+ v->Synchronize(VisitorSynchronization::kStrongRootList);
+
+ v->VisitPointer(BitCast<Object**>(&hidden_string_));
+ v->Synchronize(VisitorSynchronization::kInternalizedString);
+
+ isolate_->bootstrapper()->Iterate(v);
+ v->Synchronize(VisitorSynchronization::kBootstrapper);
+ isolate_->Iterate(v);
+ v->Synchronize(VisitorSynchronization::kTop);
+ Relocatable::Iterate(v);
+ v->Synchronize(VisitorSynchronization::kRelocatable);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ isolate_->debug()->Iterate(v);
+ if (isolate_->deoptimizer_data() != NULL) {
+ isolate_->deoptimizer_data()->Iterate(v);
+ }
+#endif
+ v->Synchronize(VisitorSynchronization::kDebug);
+ isolate_->compilation_cache()->Iterate(v);
+ v->Synchronize(VisitorSynchronization::kCompilationCache);
+
+ // Iterate over local handles in handle scopes.
+ isolate_->handle_scope_implementer()->Iterate(v);
+ isolate_->IterateDeferredHandles(v);
+ 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) {
+ isolate_->builtins()->IterateBuiltins(v);
+ }
+ v->Synchronize(VisitorSynchronization::kBuiltins);
+
+ // Iterate over global handles.
+ switch (mode) {
+ case VISIT_ONLY_STRONG:
+ isolate_->global_handles()->IterateStrongRoots(v);
+ break;
+ case VISIT_ALL_IN_SCAVENGE:
+ isolate_->global_handles()->IterateNewSpaceStrongAndDependentRoots(v);
+ break;
+ case VISIT_ALL_IN_SWEEP_NEWSPACE:
+ case VISIT_ALL:
+ isolate_->global_handles()->IterateAllRoots(v);
+ break;
+ }
+ v->Synchronize(VisitorSynchronization::kGlobalHandles);
+
+ // Iterate over eternal handles.
+ if (mode == VISIT_ALL_IN_SCAVENGE) {
+ isolate_->eternal_handles()->IterateNewSpaceRoots(v);
+ } else {
+ isolate_->eternal_handles()->IterateAllRoots(v);
+ }
+ v->Synchronize(VisitorSynchronization::kEternalHandles);
+
+ // Iterate over pointers being held by inactive threads.
+ isolate_->thread_manager()->Iterate(v);
+ v->Synchronize(VisitorSynchronization::kThreadManager);
+
+ // Iterate over the pointers the Serialization/Deserialization code is
+ // holding.
+ // During garbage collection this keeps the partial snapshot cache alive.
+ // During deserialization of the startup snapshot this creates the partial
+ // snapshot cache and deserializes the objects it refers to. During
+ // serialization this does nothing, since the partial snapshot cache is
+ // empty. However the next thing we do is create the partial snapshot,
+ // filling up the partial snapshot cache with objects it needs as we go.
+ SerializerDeserializer::Iterate(v);
+ // We don't do a v->Synchronize call here, because in debug mode that will
+ // output a flag to the snapshot. However at this point the serializer and
+ // deserializer are deliberately a little unsynchronized (see above) so the
+ // checking of the sync flag in the snapshot would fail.
+}
+
+
+// TODO(1236194): Since the heap size is configurable on the command line
+// 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,
+ intptr_t max_old_gen_size,
+ intptr_t max_executable_size) {
+ if (HasBeenSetUp()) return false;
+
+ if (FLAG_stress_compaction) {
+ // This will cause more frequent GCs when stressing.
+ max_semispace_size_ = Page::kPageSize;
+ }
+
+ if (max_semispace_size > 0) {
+ if (max_semispace_size < Page::kPageSize) {
+ max_semispace_size = Page::kPageSize;
+ if (FLAG_trace_gc) {
+ PrintPID("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
+ // of memory for each semispace because code in the snapshot has
+ // write-barrier code that relies on the size and alignment of new
+ // space. We therefore cannot use a larger 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) {
+ PrintPID("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
+ // max semispace size.
+ reserved_semispace_size_ = max_semispace_size_;
+ }
+
+ if (max_old_gen_size > 0) max_old_generation_size_ = max_old_gen_size;
+ if (max_executable_size > 0) {
+ max_executable_size_ = RoundUp(max_executable_size, Page::kPageSize);
+ }
+
+ // The max executable size must be less than or equal to the max old
+ // generation size.
+ if (max_executable_size_ > max_old_generation_size_) {
+ max_executable_size_ = max_old_generation_size_;
+ }
+
+ // The new space size must be a power of two to support single-bit testing
+ // for containment.
+ max_semispace_size_ = RoundUpToPowerOf2(max_semispace_size_);
+ reserved_semispace_size_ = RoundUpToPowerOf2(reserved_semispace_size_);
+ initial_semispace_size_ = Min(initial_semispace_size_, max_semispace_size_);
+
+ // The external allocation limit should be below 256 MB on all architectures
+ // to avoid unnecessary low memory notifications, as that is the threshold
+ // for some embedders.
+ external_allocation_limit_ = 12 * max_semispace_size_;
+ ASSERT(external_allocation_limit_ <= 256 * MB);
+
+ // 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;
+}
+
+
+bool Heap::ConfigureHeapDefault() {
+ 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);
+}
+
+
+void Heap::RecordStats(HeapStats* stats, bool take_snapshot) {
+ *stats->start_marker = HeapStats::kStartMarker;
+ *stats->end_marker = HeapStats::kEndMarker;
+ *stats->new_space_size = new_space_.SizeAsInt();
+ *stats->new_space_capacity = static_cast<int>(new_space_.Capacity());
+ *stats->old_pointer_space_size = old_pointer_space_->SizeOfObjects();
+ *stats->old_pointer_space_capacity = old_pointer_space_->Capacity();
+ *stats->old_data_space_size = old_data_space_->SizeOfObjects();
+ *stats->old_data_space_capacity = old_data_space_->Capacity();
+ *stats->code_space_size = code_space_->SizeOfObjects();
+ *stats->code_space_capacity = code_space_->Capacity();
+ *stats->map_space_size = map_space_->SizeOfObjects();
+ *stats->map_space_capacity = map_space_->Capacity();
+ *stats->cell_space_size = cell_space_->SizeOfObjects();
+ *stats->cell_space_capacity = cell_space_->Capacity();
+ *stats->property_cell_space_size = property_cell_space_->SizeOfObjects();
+ *stats->property_cell_space_capacity = property_cell_space_->Capacity();
+ *stats->lo_space_size = lo_space_->Size();
+ isolate_->global_handles()->RecordStats(stats);
+ *stats->memory_allocator_size = isolate()->memory_allocator()->Size();
+ *stats->memory_allocator_capacity =
+ isolate()->memory_allocator()->Size() +
+ isolate()->memory_allocator()->Available();
+ *stats->os_error = OS::GetLastError();
+ isolate()->memory_allocator()->Available();
+ if (take_snapshot) {
+ HeapIterator iterator(this);
+ for (HeapObject* obj = iterator.next();
+ obj != NULL;
+ obj = iterator.next()) {
+ InstanceType type = obj->map()->instance_type();
+ ASSERT(0 <= type && type <= LAST_TYPE);
+ stats->objects_per_type[type]++;
+ stats->size_per_type[type] += obj->Size();
+ }
+ }
+}
+
+
+intptr_t Heap::PromotedSpaceSizeOfObjects() {
+ return old_pointer_space_->SizeOfObjects()
+ + old_data_space_->SizeOfObjects()
+ + code_space_->SizeOfObjects()
+ + map_space_->SizeOfObjects()
+ + cell_space_->SizeOfObjects()
+ + property_cell_space_->SizeOfObjects()
+ + lo_space_->SizeOfObjects();
+}
+
+
+intptr_t Heap::PromotedExternalMemorySize() {
+ if (amount_of_external_allocated_memory_
+ <= amount_of_external_allocated_memory_at_last_global_gc_) return 0;
+ return amount_of_external_allocated_memory_
+ - amount_of_external_allocated_memory_at_last_global_gc_;
+}
+
+
+V8_DECLARE_ONCE(initialize_gc_once);
+
+static void InitializeGCOnce() {
+ InitializeScavengingVisitorsTables();
+ NewSpaceScavenger::Initialize();
+ MarkCompactCollector::Initialize();
+}
+
+
+bool Heap::SetUp() {
+#ifdef DEBUG
+ allocation_timeout_ = FLAG_gc_interval;
+#endif
+
+ // Initialize heap spaces and initial maps and objects. Whenever something
+ // 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 (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.
+ if (!configured_) {
+ if (!ConfigureHeapDefault()) return false;
+ }
+
+ CallOnce(&initialize_gc_once, &InitializeGCOnce);
+
+ MarkMapPointersAsEncoded(false);
+
+ // Set up memory allocator.
+ if (!isolate_->memory_allocator()->SetUp(MaxReserved(), MaxExecutableSize()))
+ return false;
+
+ // Set up new space.
+ if (!new_space_.SetUp(reserved_semispace_size_, max_semispace_size_)) {
+ return false;
+ }
+
+ // Initialize old pointer space.
+ old_pointer_space_ =
+ new OldSpace(this,
+ max_old_generation_size_,
+ OLD_POINTER_SPACE,
+ NOT_EXECUTABLE);
+ if (old_pointer_space_ == NULL) return false;
+ if (!old_pointer_space_->SetUp()) return false;
+
+ // Initialize old data space.
+ old_data_space_ =
+ new OldSpace(this,
+ max_old_generation_size_,
+ OLD_DATA_SPACE,
+ NOT_EXECUTABLE);
+ if (old_data_space_ == NULL) 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_)) {
+ return false;
+ }
+ }
+
+ code_space_ =
+ new OldSpace(this, max_old_generation_size_, CODE_SPACE, EXECUTABLE);
+ if (code_space_ == NULL) return false;
+ if (!code_space_->SetUp()) return false;
+
+ // Initialize map space.
+ map_space_ = new MapSpace(this, max_old_generation_size_, MAP_SPACE);
+ if (map_space_ == NULL) return false;
+ if (!map_space_->SetUp()) return false;
+
+ // Initialize simple cell space.
+ cell_space_ = new CellSpace(this, max_old_generation_size_, CELL_SPACE);
+ if (cell_space_ == NULL) return false;
+ if (!cell_space_->SetUp()) return false;
+
+ // Initialize global property cell space.
+ property_cell_space_ = new PropertyCellSpace(this, max_old_generation_size_,
+ PROPERTY_CELL_SPACE);
+ if (property_cell_space_ == NULL) return false;
+ if (!property_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, max_old_generation_size_, LO_SPACE);
+ if (lo_space_ == NULL) return false;
+ if (!lo_space_->SetUp()) return false;
+
+ // 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) {
+ set_hash_seed(
+ Smi::FromInt(V8::RandomPrivate(isolate()) & 0x3fffffff));
+ } else {
+ set_hash_seed(Smi::FromInt(FLAG_hash_seed));
+ }
+ }
+
+ LOG(isolate_, IntPtrTEvent("heap-capacity", Capacity()));
+ LOG(isolate_, IntPtrTEvent("heap-available", Available()));
+
+ store_buffer()->SetUp();
+
+ if (FLAG_parallel_recompilation) relocation_mutex_ = OS::CreateMutex();
+#ifdef DEBUG
+ relocation_mutex_locked_by_optimizer_thread_ = false;
+#endif // DEBUG
+
+ return true;
+}
+
+
+bool Heap::CreateHeapObjects() {
+ // Create initial maps.
+ if (!CreateInitialMaps()) return false;
+ if (!CreateApiObjects()) return false;
+
+ // Create initial objects
+ if (!CreateInitialObjects()) return false;
+
+ native_contexts_list_ = undefined_value();
+ array_buffers_list_ = undefined_value();
+ allocation_sites_list_ = undefined_value();
+ return true;
+}
+
+
+void Heap::SetStackLimits() {
+ ASSERT(isolate_ != NULL);
+ ASSERT(isolate_ == isolate());
+ // On 64 bit machines, pointers are generally out of range of Smis. We write
+ // something that looks like an out of range Smi to the GC.
+
+ // Set up the special root array entries containing the stack limits.
+ // These are actually addresses, but the tag makes the GC ignore it.
+ roots_[kStackLimitRootIndex] =
+ reinterpret_cast<Object*>(
+ (isolate_->stack_guard()->jslimit() & ~kSmiTagMask) | kSmiTag);
+ roots_[kRealStackLimitRootIndex] =
+ reinterpret_cast<Object*>(
+ (isolate_->stack_guard()->real_jslimit() & ~kSmiTagMask) | kSmiTag);
+}
+
+
+void Heap::TearDown() {
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ Verify();
+ }
+#endif
+
+ if (FLAG_print_cumulative_gc_stat) {
+ PrintF("\n");
+ PrintF("gc_count=%d ", gc_count_);
+ PrintF("mark_sweep_count=%d ", ms_count_);
+ PrintF("max_gc_pause=%.1f ", get_max_gc_pause());
+ PrintF("total_gc_time=%.1f ", total_gc_time_ms_);
+ PrintF("min_in_mutator=%.1f ", get_min_in_mutator());
+ PrintF("max_alive_after_gc=%" V8_PTR_PREFIX "d ",
+ get_max_alive_after_gc());
+ PrintF("total_marking_time=%.1f ", marking_time());
+ PrintF("total_sweeping_time=%.1f ", sweeping_time());
+ PrintF("\n\n");
+ }
+
+ TearDownArrayBuffers();
+
+ isolate_->global_handles()->TearDown();
+
+ external_string_table_.TearDown();
+
+ mark_compact_collector()->TearDown();
+
+ new_space_.TearDown();
+
+ if (old_pointer_space_ != NULL) {
+ old_pointer_space_->TearDown();
+ delete old_pointer_space_;
+ old_pointer_space_ = NULL;
+ }
+
+ if (old_data_space_ != NULL) {
+ old_data_space_->TearDown();
+ delete old_data_space_;
+ old_data_space_ = NULL;
+ }
+
+ if (code_space_ != NULL) {
+ code_space_->TearDown();
+ delete code_space_;
+ code_space_ = NULL;
+ }
+
+ if (map_space_ != NULL) {
+ map_space_->TearDown();
+ delete map_space_;
+ map_space_ = NULL;
+ }
+
+ if (cell_space_ != NULL) {
+ cell_space_->TearDown();
+ delete cell_space_;
+ cell_space_ = NULL;
+ }
+
+ if (property_cell_space_ != NULL) {
+ property_cell_space_->TearDown();
+ delete property_cell_space_;
+ property_cell_space_ = NULL;
+ }
+
+ if (lo_space_ != NULL) {
+ lo_space_->TearDown();
+ delete lo_space_;
+ lo_space_ = NULL;
+ }
+
+ store_buffer()->TearDown();
+ incremental_marking()->TearDown();
+
+ isolate_->memory_allocator()->TearDown();
+
+ delete relocation_mutex_;
+}
+
+
+void Heap::AddGCPrologueCallback(GCPrologueCallback callback, GCType gc_type) {
+ ASSERT(callback != NULL);
+ GCPrologueCallbackPair pair(callback, gc_type);
+ ASSERT(!gc_prologue_callbacks_.Contains(pair));
+ return gc_prologue_callbacks_.Add(pair);
+}
+
+
+void Heap::RemoveGCPrologueCallback(GCPrologueCallback callback) {
+ ASSERT(callback != NULL);
+ for (int i = 0; i < gc_prologue_callbacks_.length(); ++i) {
+ if (gc_prologue_callbacks_[i].callback == callback) {
+ gc_prologue_callbacks_.Remove(i);
+ return;
+ }
+ }
+ UNREACHABLE();
+}
+
+
+void Heap::AddGCEpilogueCallback(GCEpilogueCallback callback, GCType gc_type) {
+ ASSERT(callback != NULL);
+ GCEpilogueCallbackPair pair(callback, gc_type);
+ ASSERT(!gc_epilogue_callbacks_.Contains(pair));
+ return gc_epilogue_callbacks_.Add(pair);
+}
+
+
+void Heap::RemoveGCEpilogueCallback(GCEpilogueCallback callback) {
+ ASSERT(callback != NULL);
+ for (int i = 0; i < gc_epilogue_callbacks_.length(); ++i) {
+ if (gc_epilogue_callbacks_[i].callback == callback) {
+ gc_epilogue_callbacks_.Remove(i);
+ return;
+ }
+ }
+ UNREACHABLE();
+}
+
+
+#ifdef DEBUG
+
+class PrintHandleVisitor: public ObjectVisitor {
+ public:
+ void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++)
+ PrintF(" handle %p to %p\n",
+ reinterpret_cast<void*>(p),
+ reinterpret_cast<void*>(*p));
+ }
+};
+
+
+void Heap::PrintHandles() {
+ PrintF("Handles:\n");
+ PrintHandleVisitor v;
+ isolate_->handle_scope_implementer()->Iterate(&v);
+}
+
+#endif
+
+
+Space* AllSpaces::next() {
+ switch (counter_++) {
+ case NEW_SPACE:
+ return heap_->new_space();
+ case OLD_POINTER_SPACE:
+ return heap_->old_pointer_space();
+ case OLD_DATA_SPACE:
+ return heap_->old_data_space();
+ case CODE_SPACE:
+ return heap_->code_space();
+ case MAP_SPACE:
+ return heap_->map_space();
+ case CELL_SPACE:
+ return heap_->cell_space();
+ case PROPERTY_CELL_SPACE:
+ return heap_->property_cell_space();
+ case LO_SPACE:
+ return heap_->lo_space();
+ default:
+ return NULL;
+ }
+}
+
+
+PagedSpace* PagedSpaces::next() {
+ switch (counter_++) {
+ case OLD_POINTER_SPACE:
+ return heap_->old_pointer_space();
+ case OLD_DATA_SPACE:
+ return heap_->old_data_space();
+ case CODE_SPACE:
+ return heap_->code_space();
+ case MAP_SPACE:
+ return heap_->map_space();
+ case CELL_SPACE:
+ return heap_->cell_space();
+ case PROPERTY_CELL_SPACE:
+ return heap_->property_cell_space();
+ default:
+ return NULL;
+ }
+}
+
+
+
+OldSpace* OldSpaces::next() {
+ switch (counter_++) {
+ case OLD_POINTER_SPACE:
+ return heap_->old_pointer_space();
+ case OLD_DATA_SPACE:
+ return heap_->old_data_space();
+ case CODE_SPACE:
+ return heap_->code_space();
+ default:
+ return NULL;
+ }
+}
+
+
+SpaceIterator::SpaceIterator(Heap* heap)
+ : heap_(heap),
+ current_space_(FIRST_SPACE),
+ iterator_(NULL),
+ size_func_(NULL) {
+}
+
+
+SpaceIterator::SpaceIterator(Heap* heap, HeapObjectCallback size_func)
+ : heap_(heap),
+ current_space_(FIRST_SPACE),
+ iterator_(NULL),
+ size_func_(size_func) {
+}
+
+
+SpaceIterator::~SpaceIterator() {
+ // Delete active iterator if any.
+ delete iterator_;
+}
+
+
+bool SpaceIterator::has_next() {
+ // Iterate until no more spaces.
+ return current_space_ != LAST_SPACE;
+}
+
+
+ObjectIterator* SpaceIterator::next() {
+ if (iterator_ != NULL) {
+ delete iterator_;
+ iterator_ = NULL;
+ // Move to the next space
+ current_space_++;
+ if (current_space_ > LAST_SPACE) {
+ return NULL;
+ }
+ }
+
+ // Return iterator for the new current space.
+ return CreateIterator();
+}
+
+
+// Create an iterator for the space to iterate.
+ObjectIterator* SpaceIterator::CreateIterator() {
+ ASSERT(iterator_ == NULL);
+
+ switch (current_space_) {
+ case NEW_SPACE:
+ iterator_ = new SemiSpaceIterator(heap_->new_space(), size_func_);
+ break;
+ case OLD_POINTER_SPACE:
+ iterator_ =
+ new HeapObjectIterator(heap_->old_pointer_space(), size_func_);
+ break;
+ case OLD_DATA_SPACE:
+ iterator_ = new HeapObjectIterator(heap_->old_data_space(), size_func_);
+ break;
+ case CODE_SPACE:
+ iterator_ = new HeapObjectIterator(heap_->code_space(), size_func_);
+ break;
+ case MAP_SPACE:
+ iterator_ = new HeapObjectIterator(heap_->map_space(), size_func_);
+ break;
+ case CELL_SPACE:
+ iterator_ = new HeapObjectIterator(heap_->cell_space(), size_func_);
+ break;
+ case PROPERTY_CELL_SPACE:
+ iterator_ = new HeapObjectIterator(heap_->property_cell_space(),
+ size_func_);
+ break;
+ case LO_SPACE:
+ iterator_ = new LargeObjectIterator(heap_->lo_space(), size_func_);
+ break;
+ }
+
+ // Return the newly allocated iterator;
+ ASSERT(iterator_ != NULL);
+ return iterator_;
+}
+
+
+class HeapObjectsFilter {
+ public:
+ virtual ~HeapObjectsFilter() {}
+ virtual bool SkipObject(HeapObject* object) = 0;
+};
+
+
+class UnreachableObjectsFilter : public HeapObjectsFilter {
+ public:
+ UnreachableObjectsFilter() {
+ MarkReachableObjects();
+ }
+
+ ~UnreachableObjectsFilter() {
+ Isolate::Current()->heap()->mark_compact_collector()->ClearMarkbits();
+ }
+
+ bool SkipObject(HeapObject* object) {
+ MarkBit mark_bit = Marking::MarkBitFrom(object);
+ return !mark_bit.Get();
+ }
+
+ private:
+ class MarkingVisitor : public ObjectVisitor {
+ public:
+ 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);
+ MarkBit mark_bit = Marking::MarkBitFrom(obj);
+ if (!mark_bit.Get()) {
+ mark_bit.Set();
+ marking_stack_.Add(obj);
+ }
+ }
+ }
+
+ void TransitiveClosure() {
+ while (!marking_stack_.is_empty()) {
+ HeapObject* obj = marking_stack_.RemoveLast();
+ obj->Iterate(this);
+ }
+ }
+
+ private:
+ List<HeapObject*> marking_stack_;
+ };
+
+ void MarkReachableObjects() {
+ Heap* heap = Isolate::Current()->heap();
+ MarkingVisitor visitor;
+ heap->IterateRoots(&visitor, VISIT_ALL);
+ visitor.TransitiveClosure();
+ }
+
+ DisallowHeapAllocation no_allocation_;
+};
+
+
+HeapIterator::HeapIterator(Heap* heap)
+ : heap_(heap),
+ filtering_(HeapIterator::kNoFiltering),
+ filter_(NULL) {
+ Init();
+}
+
+
+HeapIterator::HeapIterator(Heap* heap,
+ HeapIterator::HeapObjectsFiltering filtering)
+ : heap_(heap),
+ filtering_(filtering),
+ filter_(NULL) {
+ Init();
+}
+
+
+HeapIterator::~HeapIterator() {
+ Shutdown();
+}
+
+
+void HeapIterator::Init() {
+ // Start the iteration.
+ space_iterator_ = new SpaceIterator(heap_);
+ switch (filtering_) {
+ case kFilterUnreachable:
+ filter_ = new UnreachableObjectsFilter;
+ break;
+ default:
+ break;
+ }
+ object_iterator_ = space_iterator_->next();
+}
+
+
+void HeapIterator::Shutdown() {
+#ifdef DEBUG
+ // Assert that in filtering mode we have iterated through all
+ // objects. Otherwise, heap will be left in an inconsistent state.
+ if (filtering_ != kNoFiltering) {
+ ASSERT(object_iterator_ == NULL);
+ }
+#endif
+ // Make sure the last iterator is deallocated.
+ delete space_iterator_;
+ space_iterator_ = NULL;
+ object_iterator_ = NULL;
+ delete filter_;
+ filter_ = NULL;
+}
+
+
+HeapObject* HeapIterator::next() {
+ if (filter_ == NULL) return NextObject();
+
+ HeapObject* obj = NextObject();
+ while (obj != NULL && filter_->SkipObject(obj)) obj = NextObject();
+ return obj;
+}
+
+
+HeapObject* HeapIterator::NextObject() {
+ // No iterator means we are done.
+ if (object_iterator_ == NULL) return NULL;
+
+ if (HeapObject* obj = object_iterator_->next_object()) {
+ // If the current iterator has more objects we are fine.
+ return obj;
+ } else {
+ // Go though the spaces looking for one that has objects.
+ while (space_iterator_->has_next()) {
+ object_iterator_ = space_iterator_->next();
+ if (HeapObject* obj = object_iterator_->next_object()) {
+ return obj;
+ }
+ }
+ }
+ // Done with the last space.
+ object_iterator_ = NULL;
+ return NULL;
+}
+
+
+void HeapIterator::reset() {
+ // Restart the iterator.
+ Shutdown();
+ Init();
+}
+
+
+#ifdef DEBUG
+
+Object* const PathTracer::kAnyGlobalObject = NULL;
+
+class PathTracer::MarkVisitor: public ObjectVisitor {
+ public:
+ explicit MarkVisitor(PathTracer* tracer) : tracer_(tracer) {}
+ void VisitPointers(Object** start, Object** end) {
+ // Scan all HeapObject pointers in [start, end)
+ for (Object** p = start; !tracer_->found() && (p < end); p++) {
+ if ((*p)->IsHeapObject())
+ tracer_->MarkRecursively(p, this);
+ }
+ }
+
+ private:
+ PathTracer* tracer_;
+};
+
+
+class PathTracer::UnmarkVisitor: public ObjectVisitor {
+ public:
+ explicit UnmarkVisitor(PathTracer* tracer) : tracer_(tracer) {}
+ void VisitPointers(Object** start, Object** end) {
+ // Scan all HeapObject pointers in [start, end)
+ for (Object** p = start; p < end; p++) {
+ if ((*p)->IsHeapObject())
+ tracer_->UnmarkRecursively(p, this);
+ }
+ }
+
+ private:
+ PathTracer* tracer_;
+};
+
+
+void PathTracer::VisitPointers(Object** start, Object** end) {
+ bool done = ((what_to_find_ == FIND_FIRST) && found_target_);
+ // Visit all HeapObject pointers in [start, end)
+ for (Object** p = start; !done && (p < end); p++) {
+ if ((*p)->IsHeapObject()) {
+ TracePathFrom(p);
+ done = ((what_to_find_ == FIND_FIRST) && found_target_);
+ }
+ }
+}
+
+
+void PathTracer::Reset() {
+ found_target_ = false;
+ object_stack_.Clear();
+}
+
+
+void PathTracer::TracePathFrom(Object** root) {
+ ASSERT((search_target_ == kAnyGlobalObject) ||
+ search_target_->IsHeapObject());
+ found_target_in_trace_ = false;
+ Reset();
+
+ MarkVisitor mark_visitor(this);
+ MarkRecursively(root, &mark_visitor);
+
+ UnmarkVisitor unmark_visitor(this);
+ UnmarkRecursively(root, &unmark_visitor);
+
+ ProcessResults();
+}
+
+
+static bool SafeIsNativeContext(HeapObject* obj) {
+ return obj->map() == obj->GetHeap()->raw_unchecked_native_context_map();
+}
+
+
+void PathTracer::MarkRecursively(Object** p, MarkVisitor* mark_visitor) {
+ if (!(*p)->IsHeapObject()) return;
+
+ HeapObject* obj = HeapObject::cast(*p);
+
+ Object* map = obj->map();
+
+ if (!map->IsHeapObject()) return; // visited before
+
+ if (found_target_in_trace_) return; // stop if target found
+ object_stack_.Add(obj);
+ if (((search_target_ == kAnyGlobalObject) && obj->IsJSGlobalObject()) ||
+ (obj == search_target_)) {
+ found_target_in_trace_ = true;
+ found_target_ = true;
+ return;
+ }
+
+ bool is_native_context = SafeIsNativeContext(obj);
+
+ // not visited yet
+ Map* map_p = reinterpret_cast<Map*>(HeapObject::cast(map));
+
+ Address map_addr = map_p->address();
+
+ obj->set_map_no_write_barrier(reinterpret_cast<Map*>(map_addr + kMarkTag));
+
+ // Scan the object body.
+ if (is_native_context && (visit_mode_ == VISIT_ONLY_STRONG)) {
+ // This is specialized to scan Context's properly.
+ Object** start = reinterpret_cast<Object**>(obj->address() +
+ Context::kHeaderSize);
+ Object** end = reinterpret_cast<Object**>(obj->address() +
+ Context::kHeaderSize + Context::FIRST_WEAK_SLOT * kPointerSize);
+ mark_visitor->VisitPointers(start, end);
+ } else {
+ obj->IterateBody(map_p->instance_type(),
+ obj->SizeFromMap(map_p),
+ mark_visitor);
+ }
+
+ // Scan the map after the body because the body is a lot more interesting
+ // when doing leak detection.
+ MarkRecursively(&map, mark_visitor);
+
+ if (!found_target_in_trace_) // don't pop if found the target
+ object_stack_.RemoveLast();
+}
+
+
+void PathTracer::UnmarkRecursively(Object** p, UnmarkVisitor* unmark_visitor) {
+ if (!(*p)->IsHeapObject()) return;
+
+ HeapObject* obj = HeapObject::cast(*p);
+
+ Object* map = obj->map();
+
+ if (map->IsHeapObject()) return; // unmarked already
+
+ Address map_addr = reinterpret_cast<Address>(map);
+
+ map_addr -= kMarkTag;
+
+ ASSERT_TAG_ALIGNED(map_addr);
+
+ HeapObject* map_p = HeapObject::FromAddress(map_addr);
+
+ obj->set_map_no_write_barrier(reinterpret_cast<Map*>(map_p));
+
+ UnmarkRecursively(reinterpret_cast<Object**>(&map_p), unmark_visitor);
+
+ obj->IterateBody(Map::cast(map_p)->instance_type(),
+ obj->SizeFromMap(Map::cast(map_p)),
+ unmark_visitor);
+}
+
+
+void PathTracer::ProcessResults() {
+ if (found_target_) {
+ PrintF("=====================================\n");
+ PrintF("==== Path to object ====\n");
+ PrintF("=====================================\n\n");
+
+ ASSERT(!object_stack_.is_empty());
+ for (int i = 0; i < object_stack_.length(); i++) {
+ if (i > 0) PrintF("\n |\n |\n V\n\n");
+ Object* obj = object_stack_[i];
+ obj->Print();
+ }
+ PrintF("=====================================\n");
+ }
+}
+
+
+// Triggers a depth-first traversal of reachable objects from one
+// given root object and finds a path to a specific heap object and
+// prints it.
+void Heap::TracePathToObjectFrom(Object* target, Object* root) {
+ PathTracer tracer(target, PathTracer::FIND_ALL, VISIT_ALL);
+ tracer.VisitPointer(&root);
+}
+
+
+// Triggers a depth-first traversal of reachable objects from roots
+// and finds a path to a specific heap object and prints it.
+void Heap::TracePathToObject(Object* target) {
+ PathTracer tracer(target, PathTracer::FIND_ALL, VISIT_ALL);
+ IterateRoots(&tracer, VISIT_ONLY_STRONG);
+}
+
+
+// Triggers a depth-first traversal of reachable objects from roots
+// and finds a path to any global object and prints it. Useful for
+// determining the source for leaks of global objects.
+void Heap::TracePathToGlobal() {
+ PathTracer tracer(PathTracer::kAnyGlobalObject,
+ PathTracer::FIND_ALL,
+ VISIT_ALL);
+ IterateRoots(&tracer, VISIT_ONLY_STRONG);
+}
+#endif
+
+
+static intptr_t CountTotalHolesSize(Heap* heap) {
+ intptr_t holes_size = 0;
+ OldSpaces spaces(heap);
+ for (OldSpace* space = spaces.next();
+ space != NULL;
+ space = spaces.next()) {
+ holes_size += space->Waste() + space->Available();
+ }
+ return holes_size;
+}
+
+
+GCTracer::GCTracer(Heap* heap,
+ const char* gc_reason,
+ const char* collector_reason)
+ : start_time_(0.0),
+ start_object_size_(0),
+ start_memory_size_(0),
+ gc_count_(0),
+ full_gc_count_(0),
+ allocated_since_last_gc_(0),
+ spent_in_mutator_(0),
+ promoted_objects_size_(0),
+ nodes_died_in_new_space_(0),
+ nodes_copied_in_new_space_(0),
+ nodes_promoted_(0),
+ 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_object_size_ = heap_->SizeOfObjects();
+ start_memory_size_ = heap_->isolate()->memory_allocator()->Size();
+
+ for (int i = 0; i < Scope::kNumberOfScopes; i++) {
+ scopes_[i] = 0;
+ }
+
+ in_free_list_or_wasted_before_gc_ = CountTotalHolesSize(heap);
+
+ allocated_since_last_gc_ =
+ heap_->SizeOfObjects() - heap_->alive_after_last_gc_;
+
+ 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();
+}
+
+
+GCTracer::~GCTracer() {
+ // Printf ONE line iff flag is set.
+ if (!FLAG_trace_gc && !FLAG_print_cumulative_gc_stat) return;
+
+ bool first_gc = (heap_->last_gc_end_timestamp_ == 0);
+
+ heap_->alive_after_last_gc_ = heap_->SizeOfObjects();
+ heap_->last_gc_end_timestamp_ = OS::TimeCurrentMillis();
+
+ double time = heap_->last_gc_end_timestamp_ - start_time_;
+
+ // Update cumulative GC statistics if required.
+ if (FLAG_print_cumulative_gc_stat) {
+ heap_->total_gc_time_ms_ += time;
+ heap_->max_gc_pause_ = Max(heap_->max_gc_pause_, time);
+ heap_->max_alive_after_gc_ = Max(heap_->max_alive_after_gc_,
+ heap_->alive_after_last_gc_);
+ if (!first_gc) {
+ heap_->min_in_mutator_ = Min(heap_->min_in_mutator_,
+ spent_in_mutator_);
+ }
+ } else if (FLAG_trace_gc_verbose) {
+ heap_->total_gc_time_ms_ += time;
+ }
+
+ if (collector_ == SCAVENGER && FLAG_trace_gc_ignore_scavenger) return;
+
+ heap_->AddMarkingTime(scopes_[Scope::MC_MARK]);
+
+ if (FLAG_print_cumulative_gc_stat && !FLAG_trace_gc) return;
+ PrintPID("%8.0f ms: ", heap_->isolate()->time_millis_since_init());
+
+ if (!FLAG_trace_gc_nvp) {
+ int external_time = static_cast<int>(scopes_[Scope::EXTERNAL]);
+
+ 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_object_size_) / MB,
+ static_cast<double>(start_memory_size_) / MB,
+ SizeOfHeapObjects(),
+ end_memory_size_mb);
+
+ if (external_time > 0) PrintF("%d / ", external_time);
+ PrintF("%.1f ms", time);
+ if (steps_count_ > 0) {
+ if (collector_ == SCAVENGER) {
+ PrintF(" (+ %.1f ms in %d steps since last GC)",
+ steps_took_since_last_gc_,
+ steps_count_since_last_gc_);
+ } else {
+ PrintF(" (+ %.1f ms in %d steps since start of marking, "
+ "biggest step %.1f ms)",
+ 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=%.1f ", time);
+ PrintF("mutator=%.1f ", spent_in_mutator_);
+ PrintF("gc=");
+ switch (collector_) {
+ case SCAVENGER:
+ PrintF("s");
+ break;
+ case MARK_COMPACTOR:
+ PrintF("ms");
+ break;
+ default:
+ UNREACHABLE();
+ }
+ PrintF(" ");
+
+ PrintF("external=%.1f ", scopes_[Scope::EXTERNAL]);
+ PrintF("mark=%.1f ", scopes_[Scope::MC_MARK]);
+ PrintF("sweep=%.1f ", scopes_[Scope::MC_SWEEP]);
+ PrintF("sweepns=%.1f ", scopes_[Scope::MC_SWEEP_NEWSPACE]);
+ PrintF("evacuate=%.1f ", scopes_[Scope::MC_EVACUATE_PAGES]);
+ PrintF("new_new=%.1f ", scopes_[Scope::MC_UPDATE_NEW_TO_NEW_POINTERS]);
+ PrintF("root_new=%.1f ", scopes_[Scope::MC_UPDATE_ROOT_TO_NEW_POINTERS]);
+ PrintF("old_new=%.1f ", scopes_[Scope::MC_UPDATE_OLD_TO_NEW_POINTERS]);
+ PrintF("compaction_ptrs=%.1f ",
+ scopes_[Scope::MC_UPDATE_POINTERS_TO_EVACUATED]);
+ PrintF("intracompaction_ptrs=%.1f ",
+ scopes_[Scope::MC_UPDATE_POINTERS_BETWEEN_EVACUATED]);
+ PrintF("misc_compaction=%.1f ", scopes_[Scope::MC_UPDATE_MISC_POINTERS]);
+ PrintF("weakcollection_process=%.1f ",
+ scopes_[Scope::MC_WEAKCOLLECTION_PROCESS]);
+ PrintF("weakcollection_clear=%.1f ",
+ scopes_[Scope::MC_WEAKCOLLECTION_CLEAR]);
+
+ 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_);
+ PrintF("holes_size_after=%" V8_PTR_PREFIX "d ", CountTotalHolesSize(heap_));
+
+ PrintF("allocated=%" V8_PTR_PREFIX "d ", allocated_since_last_gc_);
+ PrintF("promoted=%" V8_PTR_PREFIX "d ", promoted_objects_size_);
+ PrintF("nodes_died_in_new=%d ", nodes_died_in_new_space_);
+ PrintF("nodes_copied_in_new=%d ", nodes_copied_in_new_space_);
+ PrintF("nodes_promoted=%d ", nodes_promoted_);
+
+ if (collector_ == SCAVENGER) {
+ PrintF("stepscount=%d ", steps_count_since_last_gc_);
+ PrintF("stepstook=%.1f ", steps_took_since_last_gc_);
+ } else {
+ PrintF("stepscount=%d ", steps_count_);
+ PrintF("stepstook=%.1f ", steps_took_);
+ PrintF("longeststep=%.1f ", longest_step_);
+ }
+
+ PrintF("\n");
+ }
+
+ heap_->PrintShortHeapStatistics();
+}
+
+
+const char* GCTracer::CollectorString() {
+ switch (collector_) {
+ case SCAVENGER:
+ return "Scavenge";
+ case MARK_COMPACTOR:
+ return "Mark-sweep";
+ }
+ return "Unknown GC";
+}
+
+
+int KeyedLookupCache::Hash(Map* map, Name* name) {
+ // Uses only lower 32 bits if pointers are larger.
+ uintptr_t addr_hash =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map)) >> kMapHashShift;
+ return static_cast<uint32_t>((addr_hash ^ name->Hash()) & kCapacityMask);
+}
+
+
+int KeyedLookupCache::Lookup(Map* map, Name* name) {
+ 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;
+}
+
+
+void KeyedLookupCache::Update(Map* map, Name* name, int field_offset) {
+ if (!name->IsUniqueName()) {
+ String* internalized_string;
+ if (!HEAP->InternalizeStringIfExists(
+ String::cast(name), &internalized_string)) {
+ return;
+ }
+ name = internalized_string;
+ }
+ // This cache is cleared only between mark compact passes, so we expect the
+ // cache to only contain old space names.
+ ASSERT(!HEAP->InNewSpace(name));
+
+ int index = (Hash(map, name) & 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 = name;
+ 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 = name;
+ field_offsets_[index] = field_offset;
+}
+
+
+void KeyedLookupCache::Clear() {
+ for (int index = 0; index < kLength; index++) keys_[index].map = NULL;
+}
+
+
+void DescriptorLookupCache::Clear() {
+ for (int index = 0; index < kLength; index++) keys_[index].source = NULL;
+}
+
+
+#ifdef DEBUG
+void Heap::GarbageCollectionGreedyCheck() {
+ ASSERT(FLAG_gc_greedy);
+ if (isolate_->bootstrapper()->IsActive()) return;
+ if (disallow_allocation_failure()) return;
+ CollectGarbage(NEW_SPACE);
+}
+#endif
+
+
+TranscendentalCache::SubCache::SubCache(Type t)
+ : type_(t),
+ isolate_(Isolate::Current()) {
+ uint32_t in0 = 0xffffffffu; // Bit-pattern for a NaN that isn't
+ uint32_t in1 = 0xffffffffu; // generated by the FPU.
+ for (int i = 0; i < kCacheSize; i++) {
+ elements_[i].in[0] = in0;
+ elements_[i].in[1] = in1;
+ elements_[i].output = NULL;
+ }
+}
+
+
+void TranscendentalCache::Clear() {
+ for (int i = 0; i < kNumberOfCaches; i++) {
+ if (caches_[i] != NULL) {
+ delete caches_[i];
+ caches_[i] = NULL;
+ }
+ }
+}
+
+
+void ExternalStringTable::CleanUp() {
+ int last = 0;
+ for (int i = 0; i < new_space_strings_.length(); ++i) {
+ if (new_space_strings_[i] == heap_->the_hole_value()) {
+ continue;
+ }
+ if (heap_->InNewSpace(new_space_strings_[i])) {
+ new_space_strings_[last++] = new_space_strings_[i];
+ } else {
+ old_space_strings_.Add(new_space_strings_[i]);
+ }
+ }
+ new_space_strings_.Rewind(last);
+ new_space_strings_.Trim();
+
+ last = 0;
+ for (int i = 0; i < old_space_strings_.length(); ++i) {
+ if (old_space_strings_[i] == heap_->the_hole_value()) {
+ continue;
+ }
+ ASSERT(!heap_->InNewSpace(old_space_strings_[i]));
+ old_space_strings_[last++] = old_space_strings_[i];
+ }
+ old_space_strings_.Rewind(last);
+ old_space_strings_.Trim();
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ Verify();
+ }
+#endif
+}
+
+
+void ExternalStringTable::TearDown() {
+ new_space_strings_.Free();
+ old_space_strings_.Free();
+}
+
+
+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.
+ Address chunk_end = chunk->address() + chunk->size();
+ MemoryChunk* inner = MemoryChunk::FromAddress(
+ chunk->address() + Page::kPageSize);
+ MemoryChunk* inner_last = MemoryChunk::FromAddress(chunk_end - 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.
+ Address area_end = Min(inner->address() + Page::kPageSize, chunk_end);
+ // Guard against overflow.
+ if (area_end < inner->address()) area_end = chunk_end;
+ inner->SetArea(inner->address(), area_end);
+ 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;
+}
+
+
+void Heap::RememberUnmappedPage(Address page, bool compacted) {
+ uintptr_t p = reinterpret_cast<uintptr_t>(page);
+ // Tag the page pointer to make it findable in the dump file.
+ if (compacted) {
+ p ^= 0xc1ead & (Page::kPageSize - 1); // Cleared.
+ } else {
+ p ^= 0x1d1ed & (Page::kPageSize - 1); // I died.
+ }
+ remembered_unmapped_pages_[remembered_unmapped_pages_index_] =
+ reinterpret_cast<Address>(p);
+ remembered_unmapped_pages_index_++;
+ remembered_unmapped_pages_index_ %= kRememberedUnmappedPages;
+}
+
+
+void Heap::ClearObjectStats(bool clear_last_time_stats) {
+ memset(object_counts_, 0, sizeof(object_counts_));
+ memset(object_sizes_, 0, sizeof(object_sizes_));
+ if (clear_last_time_stats) {
+ memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_));
+ memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_));
+ }
+}
+
+
+static LazyMutex checkpoint_object_stats_mutex = LAZY_MUTEX_INITIALIZER;
+
+
+void Heap::CheckpointObjectStats() {
+ ScopedLock lock(checkpoint_object_stats_mutex.Pointer());
+ Counters* counters = isolate()->counters();
+#define ADJUST_LAST_TIME_OBJECT_COUNT(name) \
+ counters->count_of_##name()->Increment( \
+ static_cast<int>(object_counts_[name])); \
+ counters->count_of_##name()->Decrement( \
+ static_cast<int>(object_counts_last_time_[name])); \
+ counters->size_of_##name()->Increment( \
+ static_cast<int>(object_sizes_[name])); \
+ counters->size_of_##name()->Decrement( \
+ static_cast<int>(object_sizes_last_time_[name]));
+ INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
+#undef ADJUST_LAST_TIME_OBJECT_COUNT
+ int index;
+#define ADJUST_LAST_TIME_OBJECT_COUNT(name) \
+ index = FIRST_CODE_KIND_SUB_TYPE + Code::name; \
+ counters->count_of_CODE_TYPE_##name()->Increment( \
+ static_cast<int>(object_counts_[index])); \
+ counters->count_of_CODE_TYPE_##name()->Decrement( \
+ static_cast<int>(object_counts_last_time_[index])); \
+ counters->size_of_CODE_TYPE_##name()->Increment( \
+ static_cast<int>(object_sizes_[index])); \
+ counters->size_of_CODE_TYPE_##name()->Decrement( \
+ static_cast<int>(object_sizes_last_time_[index]));
+ CODE_KIND_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
+#undef ADJUST_LAST_TIME_OBJECT_COUNT
+#define ADJUST_LAST_TIME_OBJECT_COUNT(name) \
+ index = FIRST_FIXED_ARRAY_SUB_TYPE + name; \
+ counters->count_of_FIXED_ARRAY_##name()->Increment( \
+ static_cast<int>(object_counts_[index])); \
+ counters->count_of_FIXED_ARRAY_##name()->Decrement( \
+ static_cast<int>(object_counts_last_time_[index])); \
+ counters->size_of_FIXED_ARRAY_##name()->Increment( \
+ static_cast<int>(object_sizes_[index])); \
+ counters->size_of_FIXED_ARRAY_##name()->Decrement( \
+ static_cast<int>(object_sizes_last_time_[index]));
+ FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT)
+#undef ADJUST_LAST_TIME_OBJECT_COUNT
+
+ OS::MemCopy(object_counts_last_time_, object_counts_, sizeof(object_counts_));
+ OS::MemCopy(object_sizes_last_time_, object_sizes_, sizeof(object_sizes_));
+ ClearObjectStats();
+}
+
+
+Heap::RelocationLock::RelocationLock(Heap* heap) : heap_(heap) {
+ if (FLAG_parallel_recompilation) {
+ heap_->relocation_mutex_->Lock();
+#ifdef DEBUG
+ heap_->relocation_mutex_locked_by_optimizer_thread_ =
+ heap_->isolate()->optimizing_compiler_thread()->IsOptimizerThread();
+#endif // DEBUG
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/heap.h b/chromium/v8/src/heap.h
new file mode 100644
index 00000000000..78c0e5b26b4
--- /dev/null
+++ b/chromium/v8/src/heap.h
@@ -0,0 +1,3049 @@
+// Copyright 2012 the V8 project authors. 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_HEAP_H_
+#define V8_HEAP_H_
+
+#include <cmath>
+
+#include "allocation.h"
+#include "assert-scope.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 {
+
+// Defines all the roots in Heap.
+#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(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(Oddball, uninitialized_value, UninitializedValue) \
+ V(Map, cell_map, CellMap) \
+ V(Map, global_property_cell_map, GlobalPropertyCellMap) \
+ V(Map, shared_function_info_map, SharedFunctionInfoMap) \
+ V(Map, meta_map, MetaMap) \
+ V(Map, heap_number_map, HeapNumberMap) \
+ V(Map, native_context_map, NativeContextMap) \
+ V(Map, fixed_array_map, FixedArrayMap) \
+ 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, hash_table_map, HashTableMap) \
+ V(FixedArray, empty_fixed_array, EmptyFixedArray) \
+ V(ByteArray, empty_byte_array, EmptyByteArray) \
+ V(DescriptorArray, empty_descriptor_array, EmptyDescriptorArray) \
+ V(Smi, stack_limit, StackLimit) \
+ 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) \
+ V(Object, instanceof_cache_answer, InstanceofCacheAnswer) \
+ V(FixedArray, single_character_string_cache, SingleCharacterStringCache) \
+ V(FixedArray, string_split_cache, StringSplitCache) \
+ V(FixedArray, regexp_multiple_cache, RegExpMultipleCache) \
+ V(Object, termination_exception, TerminationException) \
+ V(Smi, hash_seed, HashSeed) \
+ V(Map, symbol_map, SymbolMap) \
+ V(Map, string_map, StringMap) \
+ V(Map, ascii_string_map, AsciiStringMap) \
+ 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, external_string_map, ExternalStringMap) \
+ V(Map, \
+ external_string_with_one_byte_data_map, \
+ ExternalStringWithOneByteDataMap) \
+ V(Map, external_ascii_string_map, ExternalAsciiStringMap) \
+ V(Map, short_external_string_map, ShortExternalStringMap) \
+ V(Map, \
+ short_external_string_with_one_byte_data_map, \
+ ShortExternalStringWithOneByteDataMap) \
+ V(Map, internalized_string_map, InternalizedStringMap) \
+ V(Map, ascii_internalized_string_map, AsciiInternalizedStringMap) \
+ V(Map, cons_internalized_string_map, ConsInternalizedStringMap) \
+ V(Map, cons_ascii_internalized_string_map, ConsAsciiInternalizedStringMap) \
+ V(Map, \
+ external_internalized_string_map, \
+ ExternalInternalizedStringMap) \
+ V(Map, \
+ external_internalized_string_with_one_byte_data_map, \
+ ExternalInternalizedStringWithOneByteDataMap) \
+ V(Map, \
+ external_ascii_internalized_string_map, \
+ ExternalAsciiInternalizedStringMap) \
+ V(Map, \
+ short_external_internalized_string_map, \
+ ShortExternalInternalizedStringMap) \
+ V(Map, \
+ short_external_internalized_string_with_one_byte_data_map, \
+ ShortExternalInternalizedStringWithOneByteDataMap) \
+ V(Map, \
+ short_external_ascii_internalized_string_map, \
+ ShortExternalAsciiInternalizedStringMap) \
+ V(Map, short_external_ascii_string_map, ShortExternalAsciiStringMap) \
+ V(Map, undetectable_string_map, UndetectableStringMap) \
+ V(Map, undetectable_ascii_string_map, UndetectableAsciiStringMap) \
+ V(Map, external_byte_array_map, ExternalByteArrayMap) \
+ V(Map, external_unsigned_byte_array_map, ExternalUnsignedByteArrayMap) \
+ V(Map, external_short_array_map, ExternalShortArrayMap) \
+ V(Map, external_unsigned_short_array_map, ExternalUnsignedShortArrayMap) \
+ V(Map, external_int_array_map, ExternalIntArrayMap) \
+ V(Map, external_unsigned_int_array_map, ExternalUnsignedIntArrayMap) \
+ V(Map, external_float_array_map, ExternalFloatArrayMap) \
+ V(Map, external_double_array_map, ExternalDoubleArrayMap) \
+ V(Map, external_pixel_array_map, ExternalPixelArrayMap) \
+ V(ExternalArray, empty_external_byte_array, \
+ EmptyExternalByteArray) \
+ V(ExternalArray, empty_external_unsigned_byte_array, \
+ EmptyExternalUnsignedByteArray) \
+ V(ExternalArray, empty_external_short_array, EmptyExternalShortArray) \
+ V(ExternalArray, empty_external_unsigned_short_array, \
+ EmptyExternalUnsignedShortArray) \
+ V(ExternalArray, empty_external_int_array, EmptyExternalIntArray) \
+ V(ExternalArray, empty_external_unsigned_int_array, \
+ EmptyExternalUnsignedIntArray) \
+ V(ExternalArray, empty_external_float_array, EmptyExternalFloatArray) \
+ V(ExternalArray, empty_external_double_array, EmptyExternalDoubleArray) \
+ V(ExternalArray, empty_external_pixel_array, \
+ EmptyExternalPixelArray) \
+ V(Map, non_strict_arguments_elements_map, NonStrictArgumentsElementsMap) \
+ V(Map, function_context_map, FunctionContextMap) \
+ V(Map, catch_context_map, CatchContextMap) \
+ V(Map, with_context_map, WithContextMap) \
+ V(Map, block_context_map, BlockContextMap) \
+ V(Map, module_context_map, ModuleContextMap) \
+ V(Map, global_context_map, GlobalContextMap) \
+ V(Map, oddball_map, OddballMap) \
+ V(Map, message_object_map, JSMessageObjectMap) \
+ V(Map, foreign_map, ForeignMap) \
+ 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(UnseededNumberDictionary, code_stubs, CodeStubs) \
+ V(UnseededNumberDictionary, non_monomorphic_cache, NonMonomorphicCache) \
+ V(PolymorphicCodeCache, polymorphic_code_cache, PolymorphicCodeCache) \
+ V(Code, js_entry_code, JsEntryCode) \
+ V(Code, js_construct_entry_code, JsConstructEntryCode) \
+ V(FixedArray, natives_source_cache, NativesSourceCache) \
+ V(Smi, last_script_id, LastScriptId) \
+ V(Script, empty_script, EmptyScript) \
+ V(Smi, real_stack_limit, RealStackLimit) \
+ V(NameDictionary, intrinsic_function_names, IntrinsicFunctionNames) \
+ V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset) \
+ V(Smi, construct_stub_deopt_pc_offset, ConstructStubDeoptPCOffset) \
+ V(Smi, getter_stub_deopt_pc_offset, GetterStubDeoptPCOffset) \
+ V(Smi, setter_stub_deopt_pc_offset, SetterStubDeoptPCOffset) \
+ V(JSObject, observation_state, ObservationState) \
+ V(Map, external_map, ExternalMap) \
+ V(Symbol, frozen_symbol, FrozenSymbol) \
+ V(Symbol, elements_transition_symbol, ElementsTransitionSymbol) \
+ V(SeededNumberDictionary, empty_slow_element_dictionary, \
+ EmptySlowElementDictionary) \
+ V(Symbol, observed_symbol, ObservedSymbol)
+
+#define ROOT_LIST(V) \
+ STRONG_ROOT_LIST(V) \
+ V(StringTable, string_table, StringTable)
+
+#define INTERNALIZED_STRING_LIST(V) \
+ V(Array_string, "Array") \
+ V(Object_string, "Object") \
+ V(proto_string, "__proto__") \
+ V(StringImpl_string, "StringImpl") \
+ V(arguments_string, "arguments") \
+ V(Arguments_string, "Arguments") \
+ V(call_string, "call") \
+ V(apply_string, "apply") \
+ V(caller_string, "caller") \
+ V(boolean_string, "boolean") \
+ V(Boolean_string, "Boolean") \
+ V(callee_string, "callee") \
+ V(constructor_string, "constructor") \
+ V(code_string, ".code") \
+ V(result_string, ".result") \
+ V(dot_for_string, ".for.") \
+ V(catch_var_string, ".catch-var") \
+ V(empty_string, "") \
+ V(eval_string, "eval") \
+ V(function_string, "function") \
+ V(length_string, "length") \
+ V(module_string, "module") \
+ V(name_string, "name") \
+ V(native_string, "native") \
+ V(null_string, "null") \
+ V(number_string, "number") \
+ V(Number_string, "Number") \
+ V(nan_string, "NaN") \
+ V(RegExp_string, "RegExp") \
+ V(source_string, "source") \
+ V(global_string, "global") \
+ V(ignore_case_string, "ignoreCase") \
+ V(multiline_string, "multiline") \
+ V(input_string, "input") \
+ V(index_string, "index") \
+ V(last_index_string, "lastIndex") \
+ V(object_string, "object") \
+ V(payload_string, "payload") \
+ V(literals_string, "literals") \
+ V(prototype_string, "prototype") \
+ V(string_string, "string") \
+ V(String_string, "String") \
+ V(unknown_field_string, "unknownField") \
+ V(symbol_string, "symbol") \
+ V(Symbol_string, "Symbol") \
+ V(Date_string, "Date") \
+ V(this_string, "this") \
+ V(to_string_string, "toString") \
+ V(char_at_string, "CharAt") \
+ V(undefined_string, "undefined") \
+ V(value_of_string, "valueOf") \
+ V(stack_string, "stack") \
+ V(toJSON_string, "toJSON") \
+ V(InitializeVarGlobal_string, "InitializeVarGlobal") \
+ V(InitializeConstGlobal_string, "InitializeConstGlobal") \
+ V(KeyedLoadElementMonomorphic_string, \
+ "KeyedLoadElementMonomorphic") \
+ V(KeyedStoreElementMonomorphic_string, \
+ "KeyedStoreElementMonomorphic") \
+ V(stack_overflow_string, "kStackOverflowBoilerplate") \
+ V(illegal_access_string, "illegal access") \
+ V(out_of_memory_string, "out-of-memory") \
+ V(illegal_execution_state_string, "illegal execution state") \
+ V(get_string, "get") \
+ V(set_string, "set") \
+ V(map_field_string, "%map") \
+ V(elements_field_string, "%elements") \
+ V(length_field_string, "%length") \
+ V(cell_value_string, "%cell_value") \
+ V(function_class_string, "Function") \
+ V(properties_field_symbol, "%properties") \
+ V(payload_field_symbol, "%payload") \
+ V(illegal_argument_string, "illegal argument") \
+ V(MakeReferenceError_string, "MakeReferenceError") \
+ V(MakeSyntaxError_string, "MakeSyntaxError") \
+ V(MakeTypeError_string, "MakeTypeError") \
+ V(invalid_lhs_in_assignment_string, "invalid_lhs_in_assignment") \
+ V(invalid_lhs_in_for_in_string, "invalid_lhs_in_for_in") \
+ V(invalid_lhs_in_postfix_op_string, "invalid_lhs_in_postfix_op") \
+ V(invalid_lhs_in_prefix_op_string, "invalid_lhs_in_prefix_op") \
+ V(illegal_return_string, "illegal_return") \
+ V(illegal_break_string, "illegal_break") \
+ V(illegal_continue_string, "illegal_continue") \
+ V(unknown_label_string, "unknown_label") \
+ V(redeclaration_string, "redeclaration") \
+ V(failure_string, "<failure>") \
+ V(space_string, " ") \
+ V(exec_string, "exec") \
+ V(zero_string, "0") \
+ V(global_eval_string, "GlobalEval") \
+ V(identity_hash_string, "v8::IdentityHash") \
+ V(closure_string, "(closure)") \
+ V(use_strict_string, "use strict") \
+ V(dot_string, ".") \
+ V(anonymous_function_string, "(anonymous function)") \
+ V(compare_ic_string, "==") \
+ V(strict_compare_ic_string, "===") \
+ V(infinity_string, "Infinity") \
+ V(minus_infinity_string, "-Infinity") \
+ V(hidden_stack_trace_string, "v8::hidden_stack_trace") \
+ V(query_colon_string, "(?:)") \
+ V(Generator_string, "Generator") \
+ V(throw_string, "throw") \
+ V(done_string, "done") \
+ V(value_string, "value") \
+ V(next_string, "next")
+
+// Forward declarations.
+class GCTracer;
+class HeapStats;
+class Isolate;
+class WeakObjectRetainer;
+
+
+typedef String* (*ExternalStringTableUpdaterCallback)(Heap* heap,
+ Object** pointer);
+
+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_;
+};
+
+
+
+// A queue of objects promoted during scavenge. Each object is accompanied
+// by it's size to avoid dereferencing a map pointer for scanning.
+class PromotionQueue {
+ public:
+ 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;
+ }
+
+ ASSERT(GetHeadPage() == Page::FromAllocationTop(limit));
+ limit_ = reinterpret_cast<intptr_t*>(limit);
+
+ if (limit_ <= rear_) {
+ return;
+ }
+
+ RelocateQueueHead();
+ }
+
+ 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()->area_end());
+ }
+ *target = reinterpret_cast<HeapObject*>(*(--front_));
+ *size = static_cast<int>(*(--front_));
+ // Assert no underflow.
+ SemiSpace::AssertValidRange(reinterpret_cast<Address>(rear_),
+ reinterpret_cast<Address>(front_));
+ }
+
+ private:
+ // 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.
+class ExternalStringTable {
+ public:
+ // Registers an external string.
+ inline void AddString(String* string);
+
+ inline void Iterate(ObjectVisitor* v);
+
+ // Restores internal invariant and gets rid of collected strings.
+ // Must be called after each Iterate() that modified the strings.
+ void CleanUp();
+
+ // Destroys all allocated memory.
+ void TearDown();
+
+ private:
+ ExternalStringTable() { }
+
+ friend class Heap;
+
+ inline void Verify();
+
+ inline void AddOldString(String* string);
+
+ // Notifies the table that only a prefix of the new list is valid.
+ inline void ShrinkNewStrings(int position);
+
+ // To speed up scavenge collections new space string are kept
+ // separate from old space strings.
+ List<Object*> new_space_strings_;
+ List<Object*> old_space_strings_;
+
+ Heap* heap_;
+
+ DISALLOW_COPY_AND_ASSIGN(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
+ // set up already.
+ bool ConfigureHeap(int max_semispace_size,
+ intptr_t max_old_gen_size,
+ intptr_t max_executable_size);
+ bool ConfigureHeapDefault();
+
+ // Prepares the heap, setting up memory areas that are needed in the isolate
+ // without actually creating any objects.
+ bool SetUp();
+
+ // Bootstraps the object heap with the core set of objects required to run.
+ // Returns whether it succeeded.
+ bool CreateHeapObjects();
+
+ // Destroys all memory allocated by the heap.
+ void TearDown();
+
+ // Set the stack limit in the roots_ array. Some architectures generate
+ // code that looks here, because it is faster than loading from the static
+ // jslimit_/real_jslimit_ variable in the StackGuard.
+ void SetStackLimits();
+
+ // 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
+ // semi space. The young generation consists of two semi spaces and
+ // we reserve twice the amount needed for those in order to ensure
+ // that new space can be aligned to its size.
+ intptr_t MaxReserved() {
+ return 4 * reserved_semispace_size_ + max_old_generation_size_;
+ }
+ int MaxSemiSpaceSize() { return max_semispace_size_; }
+ int ReservedSemiSpaceSize() { return reserved_semispace_size_; }
+ int InitialSemiSpaceSize() { return initial_semispace_size_; }
+ intptr_t MaxOldGenerationSize() { return max_old_generation_size_; }
+ intptr_t MaxExecutableSize() { return max_executable_size_; }
+ int MaxRegularSpaceAllocationSize() { return InitialSemiSpaceSize() * 3/4; }
+
+ // Returns the capacity of the heap in bytes w/o growing. Heap grows when
+ // more spaces are needed until it reaches the limit.
+ intptr_t Capacity();
+
+ // Returns the amount of memory currently committed for the heap.
+ intptr_t CommittedMemory();
+
+ // Returns the amount of executable memory currently committed for the heap.
+ intptr_t CommittedMemoryExecutable();
+
+ // Returns the amount of phyical memory currently committed for the heap.
+ size_t CommittedPhysicalMemory();
+
+ // Returns the available bytes in space w/o growing.
+ // Heap doesn't guarantee that it can allocate an object that requires
+ // all available bytes. Check MaxHeapObjectSize() instead.
+ intptr_t Available();
+
+ // Returns of size of all objects residing in the heap.
+ intptr_t SizeOfObjects();
+
+ // Return the starting address and a mask for the new space. And-masking an
+ // address with the mask will result in the start address of the new space
+ // for all addresses in either semispace.
+ Address NewSpaceStart() { return new_space_.start(); }
+ uintptr_t NewSpaceMask() { return new_space_.mask(); }
+ Address NewSpaceTop() { return new_space_.top(); }
+
+ NewSpace* new_space() { return &new_space_; }
+ OldSpace* old_pointer_space() { return old_pointer_space_; }
+ OldSpace* old_data_space() { return old_data_space_; }
+ OldSpace* code_space() { return code_space_; }
+ MapSpace* map_space() { return map_space_; }
+ CellSpace* cell_space() { return cell_space_; }
+ PropertyCellSpace* property_cell_space() {
+ return property_cell_space_;
+ }
+ LargeObjectSpace* lo_space() { return lo_space_; }
+ PagedSpace* paged_space(int idx) {
+ switch (idx) {
+ case OLD_POINTER_SPACE:
+ return old_pointer_space();
+ case OLD_DATA_SPACE:
+ return old_data_space();
+ case MAP_SPACE:
+ return map_space();
+ case CELL_SPACE:
+ return cell_space();
+ case PROPERTY_CELL_SPACE:
+ return property_cell_space();
+ case CODE_SPACE:
+ return code_space();
+ case NEW_SPACE:
+ case LO_SPACE:
+ UNREACHABLE();
+ }
+ return NULL;
+ }
+
+ bool always_allocate() { return always_allocate_scope_depth_ != 0; }
+ Address always_allocate_scope_depth_address() {
+ return reinterpret_cast<Address>(&always_allocate_scope_depth_);
+ }
+ bool linear_allocation() {
+ return linear_allocation_scope_depth_ != 0;
+ }
+
+ Address* NewSpaceAllocationTopAddress() {
+ return new_space_.allocation_top_address();
+ }
+ Address* NewSpaceAllocationLimitAddress() {
+ return new_space_.allocation_limit_address();
+ }
+
+ Address* OldPointerSpaceAllocationTopAddress() {
+ return old_pointer_space_->allocation_top_address();
+ }
+ Address* OldPointerSpaceAllocationLimitAddress() {
+ return old_pointer_space_->allocation_limit_address();
+ }
+
+ Address* OldDataSpaceAllocationTopAddress() {
+ return old_data_space_->allocation_top_address();
+ }
+ Address* OldDataSpaceAllocationLimitAddress() {
+ return old_data_space_->allocation_limit_address();
+ }
+
+ // Uncommit unused semi space.
+ bool UncommitFromSpace() { return new_space_.UncommitFromSpace(); }
+
+ // Allocates and initializes a new JavaScript object based on a
+ // constructor.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateJSObject(
+ JSFunction* constructor,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ MUST_USE_RESULT MaybeObject* AllocateJSObjectWithAllocationSite(
+ JSFunction* constructor,
+ Handle<AllocationSite> allocation_site);
+
+ MUST_USE_RESULT MaybeObject* AllocateJSGeneratorObject(
+ JSFunction* function);
+
+ MUST_USE_RESULT MaybeObject* AllocateJSModule(Context* context,
+ ScopeInfo* scope_info);
+
+ // 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);
+ }
+
+ inline MUST_USE_RESULT MaybeObject* AllocateEmptyJSArrayWithAllocationSite(
+ ElementsKind elements_kind,
+ Handle<AllocationSite> allocation_site);
+
+ // 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);
+
+ MUST_USE_RESULT MaybeObject* AllocateJSArrayAndStorageWithAllocationSite(
+ ElementsKind elements_kind,
+ int length,
+ int capacity,
+ Handle<AllocationSite> allocation_site,
+ ArrayStorageAllocationMode mode = DONT_INITIALIZE_ARRAY_ELEMENTS);
+
+ MUST_USE_RESULT MaybeObject* AllocateJSArrayStorage(
+ JSArray* array,
+ int length,
+ int capacity,
+ ArrayStorageAllocationMode mode = DONT_INITIALIZE_ARRAY_ELEMENTS);
+
+ // Allocate a JSArray with no elements
+ MUST_USE_RESULT MaybeObject* AllocateJSArrayWithElements(
+ FixedArrayBase* array_base,
+ ElementsKind elements_kind,
+ int length,
+ 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.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateGlobalObject(JSFunction* constructor);
+
+ // Returns a deep copy of the JavaScript object.
+ // Properties and elements are copied too.
+ // Returns failure if allocation failed.
+ MUST_USE_RESULT MaybeObject* CopyJSObject(JSObject* source);
+
+ MUST_USE_RESULT MaybeObject* CopyJSObjectWithAllocationSite(
+ JSObject* source, AllocationSite* site);
+
+ // Allocates the function prototype.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateFunctionPrototype(JSFunction* function);
+
+ // Allocates a JS ArrayBuffer object.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateJSArrayBuffer();
+
+ // Allocates a Harmony proxy or function proxy.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateJSProxy(Object* handler,
+ Object* prototype);
+
+ MUST_USE_RESULT MaybeObject* AllocateJSFunctionProxy(Object* handler,
+ Object* call_trap,
+ Object* construct_trap,
+ Object* prototype);
+
+ // Reinitialize a JSReceiver into an (empty) JS object of respective type and
+ // 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);
+
+ // Reinitialize an JSGlobalProxy based on a constructor. The object
+ // must have the same size as objects allocated using the
+ // constructor. The object is reinitialized and behaves as an
+ // object that has been freshly allocated using the constructor.
+ MUST_USE_RESULT MaybeObject* ReinitializeJSGlobalProxy(
+ JSFunction* constructor, JSGlobalProxy* global);
+
+ // Allocates and initializes a new JavaScript object based on a map.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateJSObjectFromMap(
+ Map* map, PretenureFlag pretenure = NOT_TENURED, bool alloc_props = true);
+
+ MUST_USE_RESULT MaybeObject* AllocateJSObjectFromMapWithAllocationSite(
+ Map* map, Handle<AllocationSite> allocation_site);
+
+ // Allocates a heap object based on the map.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this function does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* Allocate(Map* map, AllocationSpace space);
+
+ MUST_USE_RESULT MaybeObject* AllocateWithAllocationSite(Map* map,
+ AllocationSpace space, Handle<AllocationSite> allocation_site);
+
+ // Allocates a JS Map in the 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,
+ ElementsKind elements_kind = TERMINAL_FAST_ELEMENTS_KIND);
+
+ // Allocates a partial map for bootstrapping.
+ MUST_USE_RESULT MaybeObject* AllocatePartialMap(InstanceType instance_type,
+ int instance_size);
+
+ // Allocate a map for the specified function
+ MUST_USE_RESULT MaybeObject* AllocateInitialMap(JSFunction* fun);
+
+ // Allocates an empty code cache.
+ MUST_USE_RESULT MaybeObject* AllocateCodeCache();
+
+ // Allocates a serialized scope info.
+ MUST_USE_RESULT MaybeObject* AllocateScopeInfo(int length);
+
+ // Allocates an External object for v8's external API.
+ MUST_USE_RESULT MaybeObject* AllocateExternal(void* value);
+
+ // Allocates an empty PolymorphicCodeCache.
+ MUST_USE_RESULT MaybeObject* AllocatePolymorphicCodeCache();
+
+ // Allocates a pre-tenured empty AccessorPair.
+ MUST_USE_RESULT MaybeObject* AllocateAccessorPair();
+
+ // Allocates an empty TypeFeedbackInfo.
+ MUST_USE_RESULT MaybeObject* AllocateTypeFeedbackInfo();
+
+ // Allocates an AliasedArgumentsEntry.
+ MUST_USE_RESULT MaybeObject* AllocateAliasedArgumentsEntry(int slot);
+
+ // Clear the Instanceof cache (used when a prototype changes).
+ inline void ClearInstanceofCache();
+
+ // For use during bootup.
+ void RepairFreeListsAfterBoot();
+
+ // Allocates and fully initializes a String. There are two String
+ // encodings: ASCII and two byte. One should choose between the three string
+ // allocation functions based on the encoding of the string buffer used to
+ // initialized the string.
+ // - ...FromAscii initializes the string from a buffer that is ASCII
+ // encoded (it does not check that the buffer is ASCII encoded) and the
+ // result will be ASCII encoded.
+ // - ...FromUTF8 initializes the string from a buffer that is UTF-8
+ // encoded. If the characters are all single-byte characters, the
+ // result will be ASCII encoded, otherwise it will converted to two
+ // byte.
+ // - ...FromTwoByte initializes the string from a buffer that is two-byte
+ // encoded. If the characters are all single-byte characters, the
+ // result will be converted to ASCII, otherwise it will be left as
+ // two-byte.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateStringFromOneByte(
+ Vector<const uint8_t> str,
+ PretenureFlag pretenure = NOT_TENURED);
+ // TODO(dcarney): remove this function.
+ MUST_USE_RESULT inline MaybeObject* AllocateStringFromOneByte(
+ Vector<const char> str,
+ PretenureFlag pretenure = NOT_TENURED) {
+ return AllocateStringFromOneByte(Vector<const uint8_t>::cast(str),
+ pretenure);
+ }
+ MUST_USE_RESULT inline MaybeObject* AllocateStringFromUtf8(
+ Vector<const char> str,
+ PretenureFlag pretenure = NOT_TENURED);
+ MUST_USE_RESULT MaybeObject* AllocateStringFromUtf8Slow(
+ Vector<const char> str,
+ int non_ascii_start,
+ PretenureFlag pretenure = NOT_TENURED);
+ MUST_USE_RESULT MaybeObject* AllocateStringFromTwoByte(
+ Vector<const uc16> str,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocates an internalized string in old space based on the character
+ // stream. Returns Failure::RetryAfterGC(requested_bytes, space) if the
+ // allocation failed.
+ // Please note this function does not perform a garbage collection.
+ MUST_USE_RESULT inline MaybeObject* AllocateInternalizedStringFromUtf8(
+ Vector<const char> str,
+ int chars,
+ uint32_t hash_field);
+
+ MUST_USE_RESULT inline MaybeObject* AllocateOneByteInternalizedString(
+ Vector<const uint8_t> str,
+ uint32_t hash_field);
+
+ MUST_USE_RESULT inline MaybeObject* AllocateTwoByteInternalizedString(
+ Vector<const uc16> str,
+ uint32_t hash_field);
+
+ template<typename T>
+ static inline bool IsOneByte(T t, int chars);
+
+ template<typename T>
+ MUST_USE_RESULT inline MaybeObject* AllocateInternalizedStringImpl(
+ T t, int chars, uint32_t hash_field);
+
+ template<bool is_one_byte, typename T>
+ MUST_USE_RESULT MaybeObject* AllocateInternalizedStringImpl(
+ T t, int chars, uint32_t hash_field);
+
+ // Allocates and partially initializes a String. There are two String
+ // encodings: ASCII and two byte. These functions allocate a string of the
+ // given length and set its map and length fields. The characters of the
+ // string are uninitialized.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateRawOneByteString(
+ int length,
+ PretenureFlag pretenure = NOT_TENURED);
+ MUST_USE_RESULT MaybeObject* AllocateRawTwoByteString(
+ int length,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Computes a single character string where the character has code.
+ // 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(
+ uint16_t code);
+
+ // Allocate a byte array of the specified length
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateByteArray(int length,
+ PretenureFlag pretenure);
+
+ // Allocate a non-tenured byte array of the specified length
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateByteArray(int length);
+
+ // Allocates an external array of the specified length and type.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateExternalArray(
+ int length,
+ ExternalArrayType array_type,
+ void* external_pointer,
+ PretenureFlag pretenure);
+
+ // Allocate a symbol in old space.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateSymbol();
+
+ // Allocate a tenured simple cell.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateCell(Object* value);
+
+ // Allocate a tenured JS global property cell.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocatePropertyCell(Object* value);
+
+ // Allocate Box.
+ MUST_USE_RESULT MaybeObject* AllocateBox(Object* value,
+ PretenureFlag pretenure);
+
+ // Allocate a tenured AllocationSite. It's payload is null
+ MUST_USE_RESULT MaybeObject* AllocateAllocationSite();
+
+ // Allocates a fixed array initialized with undefined values
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateFixedArray(int length,
+ PretenureFlag pretenure);
+ // Allocates a fixed array initialized with undefined values
+ MUST_USE_RESULT MaybeObject* AllocateFixedArray(int length);
+
+ // Allocates an uninitialized fixed array. It must be filled by the caller.
+ //
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateUninitializedFixedArray(int length);
+
+ // Move len elements within a given array from src_index index to dst_index
+ // index.
+ void MoveElements(FixedArray* array, int dst_index, int src_index, int len);
+
+ // Make a copy of src and return it. Returns
+ // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed.
+ MUST_USE_RESULT inline MaybeObject* CopyFixedArray(FixedArray* src);
+
+ // Make a copy of src, set the map, and return the copy. Returns
+ // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed.
+ MUST_USE_RESULT MaybeObject* CopyFixedArrayWithMap(FixedArray* src, Map* map);
+
+ // Make a copy of src and return it. Returns
+ // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed.
+ MUST_USE_RESULT inline MaybeObject* CopyFixedDoubleArray(
+ FixedDoubleArray* src);
+
+ // Make a copy of src, set the map, and return the copy. Returns
+ // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed.
+ MUST_USE_RESULT MaybeObject* CopyFixedDoubleArrayWithMap(
+ FixedDoubleArray* src, Map* map);
+
+ // Allocates a fixed array initialized with the 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* AllocateFixedArrayWithHoles(
+ int length,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ MUST_USE_RESULT MaybeObject* AllocateRawFixedDoubleArray(
+ int length,
+ PretenureFlag pretenure);
+
+ // Allocates a fixed double array with uninitialized values. Returns
+ // Failure::RetryAfterGC(requested_bytes, space) if the allocation failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateUninitializedFixedDoubleArray(
+ 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(
+ int length, PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocate a native (but otherwise uninitialized) context.
+ MUST_USE_RESULT MaybeObject* AllocateNativeContext();
+
+ // Allocate a global context.
+ MUST_USE_RESULT MaybeObject* AllocateGlobalContext(JSFunction* function,
+ ScopeInfo* scope_info);
+
+ // Allocate a module context.
+ MUST_USE_RESULT MaybeObject* AllocateModuleContext(ScopeInfo* scope_info);
+
+ // Allocate a function context.
+ MUST_USE_RESULT MaybeObject* AllocateFunctionContext(int length,
+ JSFunction* function);
+
+ // Allocate a catch context.
+ MUST_USE_RESULT MaybeObject* AllocateCatchContext(JSFunction* function,
+ Context* previous,
+ String* name,
+ Object* thrown_object);
+ // Allocate a 'with' context.
+ MUST_USE_RESULT MaybeObject* AllocateWithContext(JSFunction* function,
+ Context* previous,
+ JSReceiver* extension);
+
+ // Allocate a block context.
+ MUST_USE_RESULT MaybeObject* AllocateBlockContext(JSFunction* function,
+ Context* previous,
+ ScopeInfo* info);
+
+ // Allocates a new utility object in the old generation.
+ MUST_USE_RESULT MaybeObject* AllocateStruct(InstanceType type);
+
+ // Allocates a function initialized with a shared part.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateFunction(
+ Map* function_map,
+ SharedFunctionInfo* shared,
+ Object* prototype,
+ PretenureFlag pretenure = TENURED);
+
+ // Arguments object size.
+ static const int kArgumentsObjectSize =
+ JSObject::kHeaderSize + 2 * kPointerSize;
+ // Strict mode arguments has no callee so it is smaller.
+ static const int kArgumentsObjectSizeStrict =
+ JSObject::kHeaderSize + 1 * kPointerSize;
+ // Indicies for direct access into argument objects.
+ static const int kArgumentsLengthIndex = 0;
+ // callee is only valid in non-strict mode.
+ static const int kArgumentsCalleeIndex = 1;
+
+ // Allocates an arguments object - optionally with an elements array.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateArgumentsObject(
+ Object* callee, int length);
+
+ // Same as NewNumberFromDouble, but may return a preallocated/immutable
+ // number object (e.g., minus_zero_value_, nan_value_)
+ MUST_USE_RESULT MaybeObject* NumberFromDouble(
+ double value, PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocated a HeapNumber from value.
+ MUST_USE_RESULT MaybeObject* AllocateHeapNumber(
+ double value,
+ PretenureFlag pretenure);
+ // pretenure = NOT_TENURED
+ MUST_USE_RESULT MaybeObject* AllocateHeapNumber(double value);
+
+ // 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* 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, PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocates a new foreign object.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateForeign(
+ Address address, PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocates a new SharedFunctionInfo object.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateSharedFunctionInfo(Object* name);
+
+ // Allocates a new JSMessageObject object.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note that this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateJSMessageObject(
+ String* type,
+ JSArray* arguments,
+ int start_position,
+ int end_position,
+ Object* script,
+ Object* stack_trace,
+ Object* stack_frames);
+
+ // Allocates a new cons string object.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateConsString(String* first,
+ String* second);
+
+ // Allocates a new sub string object which is a substring of an underlying
+ // string buffer stretching from the index start (inclusive) to the index
+ // end (exclusive).
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateSubString(
+ String* buffer,
+ int start,
+ int end,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Allocate a new external string object, which is backed by a string
+ // resource that resides outside the V8 heap.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateExternalStringFromAscii(
+ const ExternalAsciiString::Resource* resource);
+ MUST_USE_RESULT MaybeObject* AllocateExternalStringFromTwoByte(
+ const ExternalTwoByteString::Resource* resource);
+
+ // Finalizes an external string by deleting the associated external
+ // data and clearing the resource pointer.
+ inline void FinalizeExternalString(String* string);
+
+ // Allocates an uninitialized object. The memory is non-executable if the
+ // hardware and OS allow.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this function does not perform a garbage collection.
+ MUST_USE_RESULT inline MaybeObject* AllocateRaw(int size_in_bytes,
+ AllocationSpace space,
+ AllocationSpace retry_space);
+
+ // Initialize a filler object to keep the ability to iterate over the heap
+ // when shortening objects.
+ void CreateFillerObjectAt(Address addr, int size);
+
+ // Makes a new native code object
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed. On success, the pointer to the Code object is stored in the
+ // self_reference. This allows generated code to reference its own Code
+ // object by containing this pointer.
+ // Please note this function does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* CreateCode(const CodeDesc& desc,
+ Code::Flags flags,
+ Handle<Object> self_reference,
+ bool immovable = false,
+ bool crankshafted = false);
+
+ MUST_USE_RESULT MaybeObject* CopyCode(Code* code);
+
+ // Copy the code and scope info part of the code object, but insert
+ // the provided data as the relocation information.
+ MUST_USE_RESULT MaybeObject* CopyCode(Code* code, Vector<byte> reloc_info);
+
+ // Finds the internalized copy for string in the string table.
+ // If not found, a new string is added to the table and returned.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if allocation
+ // failed.
+ // Please note this function does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* InternalizeUtf8String(Vector<const char> str);
+ MUST_USE_RESULT MaybeObject* InternalizeUtf8String(const char* str) {
+ return InternalizeUtf8String(CStrVector(str));
+ }
+ MUST_USE_RESULT MaybeObject* InternalizeOneByteString(
+ Vector<const uint8_t> str);
+ MUST_USE_RESULT MaybeObject* InternalizeTwoByteString(Vector<const uc16> str);
+ MUST_USE_RESULT MaybeObject* InternalizeString(String* str);
+ MUST_USE_RESULT MaybeObject* InternalizeOneByteString(
+ Handle<SeqOneByteString> string, int from, int length);
+
+ bool InternalizeStringIfExists(String* str, String** result);
+ bool InternalizeTwoCharsStringIfExists(String* str, String** result);
+
+ // Compute the matching internalized string map for a string if possible.
+ // NULL is returned if string is in new space or not flattened.
+ Map* InternalizedStringMapForString(String* str);
+
+ // Tries to flatten a string before compare operation.
+ //
+ // Returns a failure in case it was decided that flattening was
+ // necessary and failed. Note, if flattening is not necessary the
+ // string might stay non-flat even when not a failure is returned.
+ //
+ // Please note this function does not perform a garbage collection.
+ MUST_USE_RESULT inline MaybeObject* PrepareForCompare(String* str);
+
+ // Converts the given boolean condition to JavaScript boolean value.
+ inline Object* ToBoolean(bool condition);
+
+ // Code that should be run before and after each GC. Includes some
+ // reporting/verification activities when compiled with DEBUG set.
+ void GarbageCollectionPrologue();
+ void GarbageCollectionEpilogue();
+
+ // Performs garbage collection operation.
+ // Returns whether there is a chance that another major GC could
+ // collect more garbage.
+ 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,
+ const char* gc_reason = NULL);
+
+ static const int kNoGCFlags = 0;
+ static const int kSweepPreciselyMask = 1;
+ static const int kReduceMemoryFootprintMask = 2;
+ static const int kAbortIncrementalMarkingMask = 4;
+
+ // Making the heap iterable requires us to sweep precisely and abort any
+ // incremental marking as well.
+ static const int kMakeHeapIterableMask =
+ kSweepPreciselyMask | kAbortIncrementalMarkingMask;
+
+ // 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(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();
+
+ // Utility to invoke the scavenger. This is needed in test code to
+ // 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
+ // Utility used with flag gc-greedy.
+ void GarbageCollectionGreedyCheck();
+#endif
+
+ void AddGCPrologueCallback(
+ GCPrologueCallback callback, GCType gc_type_filter);
+ void RemoveGCPrologueCallback(GCPrologueCallback callback);
+
+ void AddGCEpilogueCallback(
+ GCEpilogueCallback callback, GCType gc_type_filter);
+ void RemoveGCEpilogueCallback(GCEpilogueCallback callback);
+
+ void SetGlobalGCPrologueCallback(GCCallback callback) {
+ ASSERT((callback == NULL) ^ (global_gc_prologue_callback_ == NULL));
+ global_gc_prologue_callback_ = callback;
+ }
+ void SetGlobalGCEpilogueCallback(GCCallback callback) {
+ ASSERT((callback == NULL) ^ (global_gc_epilogue_callback_ == NULL));
+ global_gc_epilogue_callback_ = callback;
+ }
+
+ // 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]); \
+ } \
+ type* raw_unchecked_##name() { \
+ return reinterpret_cast<type*>(roots_[k##camel_name##RootIndex]); \
+ }
+ ROOT_LIST(ROOT_ACCESSOR)
+#undef ROOT_ACCESSOR
+
+// Utility type maps
+#define STRUCT_MAP_ACCESSOR(NAME, Name, name) \
+ Map* name##_map() { \
+ return Map::cast(roots_[k##Name##MapRootIndex]); \
+ }
+ STRUCT_LIST(STRUCT_MAP_ACCESSOR)
+#undef STRUCT_MAP_ACCESSOR
+
+#define STRING_ACCESSOR(name, str) String* name() { \
+ return String::cast(roots_[k##name##RootIndex]); \
+ }
+ INTERNALIZED_STRING_LIST(STRING_ACCESSOR)
+#undef STRING_ACCESSOR
+
+ // The hidden_string is special because it is the empty string, but does
+ // not match the empty string.
+ String* hidden_string() { return hidden_string_; }
+
+ void set_native_contexts_list(Object* object) {
+ native_contexts_list_ = object;
+ }
+ Object* native_contexts_list() { return native_contexts_list_; }
+
+ void set_array_buffers_list(Object* object) {
+ array_buffers_list_ = object;
+ }
+ Object* array_buffers_list() { return array_buffers_list_; }
+
+ void set_allocation_sites_list(Object* object) {
+ allocation_sites_list_ = object;
+ }
+ Object* allocation_sites_list() { return allocation_sites_list_; }
+ Object** allocation_sites_list_address() { return &allocation_sites_list_; }
+
+ // Number of mark-sweeps.
+ unsigned 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.
+ void IterateStrongRoots(ObjectVisitor* v, VisitMode mode);
+ // Iterates over all the other roots in the heap.
+ void IterateWeakRoots(ObjectVisitor* v, VisitMode mode);
+
+ // Iterate pointers to from semispace of new space found in memory interval
+ // from start to end.
+ void IterateAndMarkPointersToFromSpace(Address start,
+ Address end,
+ ObjectSlotCallback callback);
+
+ // Returns whether the object resides in new space.
+ inline bool InNewSpace(Object* object);
+ inline bool InNewSpace(Address address);
+ inline bool InNewSpacePage(Address address);
+ inline bool InFromSpace(Object* object);
+ inline bool InToSpace(Object* object);
+
+ // Returns whether the object resides in old pointer space.
+ inline bool InOldPointerSpace(Address address);
+ inline bool InOldPointerSpace(Object* object);
+
+ // Returns whether the object resides in old data space.
+ inline bool InOldDataSpace(Address address);
+ inline bool InOldDataSpace(Object* object);
+
+ // Checks whether an address/object in the heap (including auxiliary
+ // area and unused area).
+ bool Contains(Address addr);
+ bool Contains(HeapObject* value);
+
+ // Checks whether an address/object in a space.
+ // Currently used by tests, serialization and heap verification only.
+ bool InSpace(Address addr, AllocationSpace space);
+ bool InSpace(HeapObject* value, AllocationSpace space);
+
+ // Finds out which space an object should get promoted to based on its type.
+ inline OldSpace* TargetSpace(HeapObject* object);
+ static inline AllocationSpace TargetSpaceId(InstanceType type);
+
+ // Sets the stub_cache_ (only used when expanding the dictionary).
+ void public_set_code_stubs(UnseededNumberDictionary* value) {
+ roots_[kCodeStubsRootIndex] = value;
+ }
+
+ // Support for computing object sizes for old objects during GCs. Returns
+ // a function that is guaranteed to be safe for computing object sizes in
+ // the current GC phase.
+ HeapObjectCallback GcSafeSizeOfOldObjectFunction() {
+ return gc_safe_size_of_old_object_;
+ }
+
+ // Sets the non_monomorphic_cache_ (only used when expanding the dictionary).
+ void public_set_non_monomorphic_cache(UnseededNumberDictionary* value) {
+ roots_[kNonMonomorphicCacheRootIndex] = value;
+ }
+
+ void public_set_empty_script(Script* script) {
+ roots_[kEmptyScriptRootIndex] = script;
+ }
+
+ void public_set_store_buffer_top(Address* top) {
+ roots_[kStoreBufferTopRootIndex] = reinterpret_cast<Smi*>(top);
+ }
+
+ // Generated code can embed this address to get access to the roots.
+ Object** roots_array_start() { return roots_; }
+
+ Address* store_buffer_top_address() {
+ return reinterpret_cast<Address*>(&roots_[kStoreBufferTopRootIndex]);
+ }
+
+ // Get address of native contexts list for serialization support.
+ Object** native_contexts_list_address() {
+ return &native_contexts_list_;
+ }
+
+#ifdef VERIFY_HEAP
+ // Verify the heap is in its normal state before or after a GC.
+ void Verify();
+
+
+ bool weak_embedded_maps_verification_enabled() {
+ return no_weak_embedded_maps_verification_scope_depth_ == 0;
+ }
+#endif
+
+#ifdef DEBUG
+ void Print();
+ void PrintHandles();
+
+ void OldPointerSpaceCheckStoreBuffer();
+ void MapSpaceCheckStoreBuffer();
+ void LargeObjectSpaceCheckStoreBuffer();
+
+ // Report heap statistics.
+ void ReportHeapStatistics(const char* title);
+ void ReportCodeStatistics(const char* title);
+#endif
+
+ // Zapping is needed for verify heap, and always done in debug builds.
+ static inline bool ShouldZapGarbage() {
+#ifdef DEBUG
+ return true;
+#else
+#ifdef VERIFY_HEAP
+ return FLAG_verify_heap;
+#else
+ return false;
+#endif
+#endif
+ }
+
+ // Fill in bogus values in from space
+ void ZapFromSpace();
+
+ // Print short heap statistics.
+ void PrintShortHeapStatistics();
+
+ // Makes a new internalized string object
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this function does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* CreateInternalizedString(
+ const char* str, int length, int hash);
+ MUST_USE_RESULT MaybeObject* CreateInternalizedString(String* str);
+
+ // Write barrier support for address[offset] = o.
+ INLINE(void RecordWrite(Address address, int offset));
+
+ // Write barrier support for address[start : start + len[ = o.
+ INLINE(void RecordWrites(Address address, int start, int len));
+
+ enum HeapState { NOT_IN_GC, SCAVENGE, MARK_COMPACT };
+ inline HeapState gc_state() { return gc_state_; }
+
+ inline bool IsInGCPostProcessing() { return gc_post_processing_depth_ > 0; }
+
+#ifdef DEBUG
+ bool disallow_allocation_failure() {
+ return disallow_allocation_failure_;
+ }
+
+ void TracePathToObjectFrom(Object* target, Object* root);
+ void TracePathToObject(Object* target);
+ void TracePathToGlobal();
+#endif
+
+ // Callback function passed to Heap::Iterate etc. Copies an object if
+ // necessary, the object might be promoted to an old space. The caller must
+ // ensure the precondition that the object is (a) a heap object and (b) in
+ // the heap's from space.
+ static inline void ScavengePointer(HeapObject** p);
+ static inline void ScavengeObject(HeapObject** p, HeapObject* object);
+
+ // Commits from space if it is uncommitted.
+ void EnsureFromSpaceIsCommitted();
+
+ // Support for partial snapshots. After calling this we have a linear
+ // space to write objects in each space.
+ void ReserveSpace(int *sizes, Address* addresses);
+
+ //
+ // Support for the API.
+ //
+
+ bool CreateApiObjects();
+
+ // Attempt to find the number in a small cache. If we finds it, return
+ // the string representation of the number. Otherwise return undefined.
+ Object* GetNumberStringCache(Object* number);
+
+ // Update the cache with a new number-string pair.
+ void SetNumberStringCache(Object* number, String* str);
+
+ // Adjusts the amount of registered external memory.
+ // Returns the adjusted value.
+ inline intptr_t AdjustAmountOfExternalAllocatedMemory(
+ intptr_t change_in_bytes);
+
+ // Allocate uninitialized fixed array.
+ MUST_USE_RESULT MaybeObject* AllocateRawFixedArray(int length);
+ MUST_USE_RESULT MaybeObject* AllocateRawFixedArray(int length,
+ PretenureFlag pretenure);
+
+ // This is only needed for testing high promotion mode.
+ void SetNewSpaceHighPromotionModeActive(bool mode) {
+ new_space_high_promotion_mode_active_ = mode;
+ }
+
+ // Returns the allocation mode (pre-tenuring) based on observed promotion
+ // rates of previous collections.
+ inline PretenureFlag GetPretenureMode() {
+ return FLAG_pretenuring && new_space_high_promotion_mode_active_
+ ? TENURED : NOT_TENURED;
+ }
+
+ inline Address* NewSpaceHighPromotionModeActiveAddress() {
+ return reinterpret_cast<Address*>(&new_space_high_promotion_mode_active_);
+ }
+
+ inline intptr_t PromotedTotalSize() {
+ return PromotedSpaceSizeOfObjects() + PromotedExternalMemorySize();
+ }
+
+ inline intptr_t OldGenerationSpaceAvailable() {
+ return old_generation_allocation_limit_ - PromotedTotalSize();
+ }
+
+ inline intptr_t OldGenerationCapacityAvailable() {
+ return max_old_generation_size_ - PromotedTotalSize();
+ }
+
+ static const intptr_t kMinimumOldGenerationAllocationLimit =
+ 8 * (Page::kPageSize > MB ? Page::kPageSize : MB);
+
+ intptr_t OldGenerationAllocationLimit(intptr_t old_gen_size) {
+ const int divisor = FLAG_stress_compaction ? 10 :
+ new_space_high_promotion_mode_active_ ? 1 : 3;
+ intptr_t limit =
+ Max(old_gen_size + old_gen_size / divisor,
+ kMinimumOldGenerationAllocationLimit);
+ limit += new_space_.Capacity();
+ // TODO(hpayer): Can be removed when when pretenuring is supported for all
+ // allocation sites.
+ if (IsHighSurvivalRate() && IsStableOrIncreasingSurvivalTrend()) {
+ limit *= 2;
+ }
+ 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 {
+#define ROOT_INDEX_DECLARATION(type, name, camel_name) k##camel_name##RootIndex,
+ STRONG_ROOT_LIST(ROOT_INDEX_DECLARATION)
+#undef ROOT_INDEX_DECLARATION
+
+#define STRING_INDEX_DECLARATION(name, str) k##name##RootIndex,
+ INTERNALIZED_STRING_LIST(STRING_INDEX_DECLARATION)
+#undef STRING_DECLARATION
+
+ // Utility type maps
+#define DECLARE_STRUCT_MAP(NAME, Name, name) k##Name##MapRootIndex,
+ STRUCT_LIST(DECLARE_STRUCT_MAP)
+#undef DECLARE_STRUCT_MAP
+
+ kStringTableRootIndex,
+ kStrongRootListLength = kStringTableRootIndex,
+ kRootListLength
+ };
+
+ STATIC_CHECK(kUndefinedValueRootIndex == Internals::kUndefinedValueRootIndex);
+ STATIC_CHECK(kNullValueRootIndex == Internals::kNullValueRootIndex);
+ STATIC_CHECK(kTrueValueRootIndex == Internals::kTrueValueRootIndex);
+ STATIC_CHECK(kFalseValueRootIndex == Internals::kFalseValueRootIndex);
+ STATIC_CHECK(kempty_stringRootIndex == Internals::kEmptyStringRootIndex);
+
+ // Generated code can embed direct references to non-writable roots if
+ // they are in new space.
+ static bool RootCanBeWrittenAfterInitialization(RootListIndex root_index);
+ // Generated code can treat direct references to this root as constant.
+ bool RootCanBeTreatedAsConstant(RootListIndex root_index);
+
+ MUST_USE_RESULT MaybeObject* NumberToString(
+ Object* number, bool check_number_string_cache = true,
+ PretenureFlag pretenure = NOT_TENURED);
+ MUST_USE_RESULT MaybeObject* Uint32ToString(
+ uint32_t value, bool check_number_string_cache = true);
+
+ Map* MapForExternalArrayType(ExternalArrayType array_type);
+ RootListIndex RootIndexForExternalArrayType(
+ ExternalArrayType array_type);
+
+ RootListIndex RootIndexForEmptyExternalArray(ElementsKind kind);
+ ExternalArray* EmptyExternalArrayForMap(Map* map);
+
+ void RecordStats(HeapStats* stats, bool take_snapshot = false);
+
+ // Copy block of memory from src to dst. Size of block should be aligned
+ // by pointer size.
+ static inline void CopyBlock(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);
+
+ // 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;
+
+ if (FLAG_stress_compaction && (gc_count_ & 1) != 0) return true;
+
+ intptr_t adjusted_allocation_limit =
+ old_generation_allocation_limit_ - new_space_.Capacity();
+
+ if (PromotedTotalSize() >= 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,
+ // we try to promote this object.
+ inline bool ShouldBePromoted(Address old_address, int object_size);
+
+ void ClearJSFunctionResultCaches();
+
+ void ClearNormalizedMapCaches();
+
+ GCTracer* tracer() { return tracer_; }
+
+ // Returns the size of objects residing in non new spaces.
+ intptr_t PromotedSpaceSizeOfObjects();
+
+ double total_regexp_code_generated() { return total_regexp_code_generated_; }
+ void IncreaseTotalRegexpCodeGenerated(int size) {
+ total_regexp_code_generated_ += size;
+ }
+
+ // Returns maximum GC pause.
+ double get_max_gc_pause() { return max_gc_pause_; }
+
+ // Returns maximum size of objects alive after GC.
+ intptr_t get_max_alive_after_gc() { return max_alive_after_gc_; }
+
+ // Returns minimal interval between two subsequent collections.
+ double get_min_in_mutator() { return min_in_mutator_; }
+
+ // TODO(hpayer): remove, should be handled by GCTracer
+ void AddMarkingTime(double marking_time) {
+ marking_time_ += marking_time;
+ }
+
+ double marking_time() const {
+ return marking_time_;
+ }
+
+ // TODO(hpayer): remove, should be handled by GCTracer
+ void AddSweepingTime(double sweeping_time) {
+ sweeping_time_ += sweeping_time;
+ }
+
+ double sweeping_time() const {
+ return sweeping_time_;
+ }
+
+ MarkCompactCollector* mark_compact_collector() {
+ return &mark_compact_collector_;
+ }
+
+ StoreBuffer* store_buffer() {
+ return &store_buffer_;
+ }
+
+ Marking* marking() {
+ return &marking_;
+ }
+
+ IncrementalMarking* incremental_marking() {
+ return &incremental_marking_;
+ }
+
+ bool IsSweepingComplete() {
+ return !mark_compact_collector()->IsConcurrentSweepingInProgress() &&
+ old_data_space()->IsLazySweepingComplete() &&
+ old_pointer_space()->IsLazySweepingComplete();
+ }
+
+ bool AdvanceSweepers(int step_size) {
+ ASSERT(!FLAG_parallel_sweeping && !FLAG_concurrent_sweeping);
+ bool sweeping_complete = old_data_space()->AdvanceSweeper(step_size);
+ sweeping_complete &= old_pointer_space()->AdvanceSweeper(step_size);
+ return sweeping_complete;
+ }
+
+ bool EnsureSweepersProgressed(int step_size) {
+ bool sweeping_complete = old_data_space()->EnsureSweeperProgress(step_size);
+ sweeping_complete &= old_pointer_space()->EnsureSweeperProgress(step_size);
+ return sweeping_complete;
+ }
+
+ ExternalStringTable* external_string_table() {
+ return &external_string_table_;
+ }
+
+ // Returns the current sweep generation.
+ int sweep_generation() {
+ return sweep_generation_;
+ }
+
+ inline Isolate* isolate();
+
+ void CallGCPrologueCallbacks(GCType gc_type, GCCallbackFlags flags);
+ void CallGCEpilogueCallbacks(GCType gc_type);
+
+ 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();
+
+ int gc_count() const { return gc_count_; }
+
+ // 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;
+
+ 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));
+ }
+
+ void SetConstructStubDeoptPCOffset(int pc_offset) {
+ ASSERT(construct_stub_deopt_pc_offset() == Smi::FromInt(0));
+ set_construct_stub_deopt_pc_offset(Smi::FromInt(pc_offset));
+ }
+
+ void SetGetterStubDeoptPCOffset(int pc_offset) {
+ ASSERT(getter_stub_deopt_pc_offset() == Smi::FromInt(0));
+ set_getter_stub_deopt_pc_offset(Smi::FromInt(pc_offset));
+ }
+
+ void SetSetterStubDeoptPCOffset(int pc_offset) {
+ ASSERT(setter_stub_deopt_pc_offset() == Smi::FromInt(0));
+ set_setter_stub_deopt_pc_offset(Smi::FromInt(pc_offset));
+ }
+
+ // For post mortem debugging.
+ void RememberUnmappedPage(Address page, bool compacted);
+
+ // Global inline caching age: it is incremented on some GCs after context
+ // disposal. We use it to flush inline caches.
+ int global_ic_age() {
+ return global_ic_age_;
+ }
+
+ void AgeInlineCaches() {
+ global_ic_age_ = (global_ic_age_ + 1) & SharedFunctionInfo::ICAgeBits::kMax;
+ }
+
+ bool flush_monomorphic_ics() { return flush_monomorphic_ics_; }
+
+ intptr_t amount_of_external_allocated_memory() {
+ return amount_of_external_allocated_memory_;
+ }
+
+ // ObjectStats are kept in two arrays, counts and sizes. Related stats are
+ // stored in a contiguous linear buffer. Stats groups are stored one after
+ // another.
+ enum {
+ FIRST_CODE_KIND_SUB_TYPE = LAST_TYPE + 1,
+ FIRST_FIXED_ARRAY_SUB_TYPE =
+ FIRST_CODE_KIND_SUB_TYPE + Code::NUMBER_OF_KINDS,
+ OBJECT_STATS_COUNT =
+ FIRST_FIXED_ARRAY_SUB_TYPE + LAST_FIXED_ARRAY_SUB_TYPE + 1
+ };
+
+ void RecordObjectStats(InstanceType type, int sub_type, size_t size) {
+ ASSERT(type <= LAST_TYPE);
+ if (sub_type < 0) {
+ object_counts_[type]++;
+ object_sizes_[type] += size;
+ } else {
+ if (type == CODE_TYPE) {
+ ASSERT(sub_type < Code::NUMBER_OF_KINDS);
+ object_counts_[FIRST_CODE_KIND_SUB_TYPE + sub_type]++;
+ object_sizes_[FIRST_CODE_KIND_SUB_TYPE + sub_type] += size;
+ } else if (type == FIXED_ARRAY_TYPE) {
+ ASSERT(sub_type <= LAST_FIXED_ARRAY_SUB_TYPE);
+ object_counts_[FIRST_FIXED_ARRAY_SUB_TYPE + sub_type]++;
+ object_sizes_[FIRST_FIXED_ARRAY_SUB_TYPE + sub_type] += size;
+ }
+ }
+ }
+
+ void CheckpointObjectStats();
+
+ // We don't use a ScopedLock here since we want to lock the heap
+ // only when FLAG_parallel_recompilation is true.
+ class RelocationLock {
+ public:
+ explicit RelocationLock(Heap* heap);
+
+ ~RelocationLock() {
+ if (FLAG_parallel_recompilation) {
+#ifdef DEBUG
+ heap_->relocation_mutex_locked_by_optimizer_thread_ = false;
+#endif // DEBUG
+ heap_->relocation_mutex_->Unlock();
+ }
+ }
+
+#ifdef DEBUG
+ static bool IsLockedByOptimizerThread(Heap* heap) {
+ return heap->relocation_mutex_locked_by_optimizer_thread_;
+ }
+#endif // DEBUG
+
+ private:
+ Heap* heap_;
+ };
+
+ private:
+ Heap();
+
+ // This can be calculated directly from a pointer to the heap; however, it is
+ // more expedient to get at the isolate directly from within Heap methods.
+ Isolate* isolate_;
+
+ Object* roots_[kRootListLength];
+
+ 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_;
+
+ // For keeping track of how much data has survived
+ // scavenge since last new space expansion.
+ int survived_since_last_expansion_;
+
+ // For keeping track on when to flush RegExp code.
+ int sweep_generation_;
+
+ int always_allocate_scope_depth_;
+ int linear_allocation_scope_depth_;
+
+ // For keeping track of context disposals.
+ int contexts_disposed_;
+
+ int global_ic_age_;
+
+ bool flush_monomorphic_ics_;
+
+ int scan_on_scavenge_pages_;
+
+ NewSpace new_space_;
+ OldSpace* old_pointer_space_;
+ OldSpace* old_data_space_;
+ OldSpace* code_space_;
+ MapSpace* map_space_;
+ CellSpace* cell_space_;
+ PropertyCellSpace* property_cell_space_;
+ LargeObjectSpace* lo_space_;
+ HeapState gc_state_;
+ int gc_post_processing_depth_;
+
+ // Returns the amount of external memory registered since last global gc.
+ intptr_t PromotedExternalMemorySize();
+
+ unsigned int ms_count_; // how many mark-sweep collections happened
+ unsigned int gc_count_; // how many gc happened
+
+ // For post mortem debugging.
+ static const int kRememberedUnmappedPages = 128;
+ int remembered_unmapped_pages_index_;
+ Address remembered_unmapped_pages_[kRememberedUnmappedPages];
+
+ // Total length of the strings we failed to flatten since the last GC.
+ int unflattened_strings_length_;
+
+#define ROOT_ACCESSOR(type, name, camel_name) \
+ 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)
+#undef ROOT_ACCESSOR
+
+#ifdef DEBUG
+ // If the --gc-interval flag is set to a positive value, this
+ // variable holds the value indicating the number of allocations
+ // remain until the next failure and garbage collection.
+ int allocation_timeout_;
+
+ // Do we expect to be able to handle allocation failure at this
+ // time?
+ bool disallow_allocation_failure_;
+#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.
+ // TODO(hpayer): change to bool if no longer accessed from generated code
+ intptr_t 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, before expanding a paged space in the old
+ // generation and on every allocation in large object space.
+ intptr_t old_generation_allocation_limit_;
+
+ // 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_;
+
+ // The amount of external memory registered through the API kept alive
+ // by global handles
+ intptr_t amount_of_external_allocated_memory_;
+
+ // Caches the amount of external memory registered at the last global gc.
+ intptr_t amount_of_external_allocated_memory_at_last_global_gc_;
+
+ // Indicates that an allocation has failed in the old generation since the
+ // last GC.
+ bool old_gen_exhausted_;
+
+ // Weak list heads, threaded through the objects.
+ Object* native_contexts_list_;
+ Object* array_buffers_list_;
+ Object* allocation_sites_list_;
+
+ StoreBufferRebuilder store_buffer_rebuilder_;
+
+ struct StringTypeTable {
+ InstanceType type;
+ int size;
+ RootListIndex index;
+ };
+
+ struct ConstantStringTable {
+ const char* contents;
+ RootListIndex index;
+ };
+
+ struct StructTable {
+ InstanceType type;
+ int size;
+ RootListIndex index;
+ };
+
+ static const StringTypeTable string_type_table[];
+ static const ConstantStringTable constant_string_table[];
+ static const StructTable struct_table[];
+
+ // The special hidden string which is an empty string, but does not match
+ // any string when looked up in properties.
+ String* hidden_string_;
+
+ // GC callback function, called before and after mark-compact GC.
+ // Allocations in the callback function are disallowed.
+ struct GCPrologueCallbackPair {
+ GCPrologueCallbackPair(GCPrologueCallback callback, GCType gc_type)
+ : callback(callback), gc_type(gc_type) {
+ }
+ bool operator==(const GCPrologueCallbackPair& pair) const {
+ return pair.callback == callback;
+ }
+ GCPrologueCallback callback;
+ GCType gc_type;
+ };
+ List<GCPrologueCallbackPair> gc_prologue_callbacks_;
+
+ struct GCEpilogueCallbackPair {
+ GCEpilogueCallbackPair(GCEpilogueCallback callback, GCType gc_type)
+ : callback(callback), gc_type(gc_type) {
+ }
+ bool operator==(const GCEpilogueCallbackPair& pair) const {
+ return pair.callback == callback;
+ }
+ GCEpilogueCallback callback;
+ GCType gc_type;
+ };
+ List<GCEpilogueCallbackPair> gc_epilogue_callbacks_;
+
+ GCCallback global_gc_prologue_callback_;
+ GCCallback global_gc_epilogue_callback_;
+
+ // Support for computing object sizes during GC.
+ HeapObjectCallback gc_safe_size_of_old_object_;
+ static int GcSafeSizeOfOldObject(HeapObject* object);
+
+ // Update the GC state. Called from the mark-compact collector.
+ void MarkMapPointersAsEncoded(bool encoded) {
+ ASSERT(!encoded);
+ gc_safe_size_of_old_object_ = &GcSafeSizeOfOldObject;
+ }
+
+ // Checks whether a global GC is necessary
+ GarbageCollector SelectGarbageCollector(AllocationSpace space,
+ const char** reason);
+
+ // Performs garbage collection
+ // Returns whether there is a chance another major GC could
+ // collect more garbage.
+ bool PerformGarbageCollection(GarbageCollector collector,
+ GCTracer* tracer);
+
+ 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
+ // (since both AllocateRaw and AllocateRawMap are inlined).
+ MUST_USE_RESULT inline MaybeObject* AllocateRawMap();
+
+ // Allocate an uninitialized object in the simple cell space.
+ MUST_USE_RESULT inline MaybeObject* AllocateRawCell();
+
+ // Allocate an uninitialized object in the global property cell space.
+ MUST_USE_RESULT inline MaybeObject* AllocateRawPropertyCell();
+
+ // Initializes a JSObject based on its map.
+ void InitializeJSObjectFromMap(JSObject* obj,
+ FixedArray* properties,
+ Map* map);
+
+ bool CreateInitialMaps();
+ bool CreateInitialObjects();
+
+ // These five Create*EntryStub functions are here and forced to not be inlined
+ // because of a gcc-4.4 bug that assigns wrong vtable entries.
+ NO_INLINE(void CreateJSEntryStub());
+ NO_INLINE(void CreateJSConstructEntryStub());
+
+ void CreateFixedStubs();
+
+ MUST_USE_RESULT MaybeObject* CreateOddball(const char* to_string,
+ Object* to_number,
+ byte kind);
+
+ // Allocate a JSArray with no elements
+ MUST_USE_RESULT MaybeObject* AllocateJSArray(
+ ElementsKind elements_kind,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ MUST_USE_RESULT MaybeObject* AllocateJSArrayWithAllocationSite(
+ ElementsKind elements_kind,
+ Handle<AllocationSite> allocation_site);
+
+ // Allocate empty fixed array.
+ MUST_USE_RESULT MaybeObject* AllocateEmptyFixedArray();
+
+ // Allocate empty external array of given type.
+ MUST_USE_RESULT MaybeObject* AllocateEmptyExternalArray(
+ ExternalArrayType array_type);
+
+ // Allocate empty fixed double array.
+ MUST_USE_RESULT MaybeObject* AllocateEmptyFixedDoubleArray();
+
+ // Performs a minor collection in new generation.
+ void Scavenge();
+
+ static String* UpdateNewSpaceReferenceInExternalStringTableEntry(
+ Heap* 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();
+
+ void ProcessNativeContexts(WeakObjectRetainer* retainer, bool record_slots);
+ void ProcessArrayBuffers(WeakObjectRetainer* retainer, bool record_slots);
+ void ProcessAllocationSites(WeakObjectRetainer* retainer, bool record_slots);
+
+ // Called on heap tear-down.
+ void TearDownArrayBuffers();
+
+ // Record statistics before and after garbage collection.
+ void ReportStatisticsBeforeGC();
+ void ReportStatisticsAfterGC();
+
+ // Slow part of scavenge object.
+ static void ScavengeObjectSlow(HeapObject** p, HeapObject* object);
+
+ // Initializes a function with a shared part and prototype.
+ // 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.
+ inline void InitializeFunction(
+ JSFunction* function,
+ SharedFunctionInfo* shared,
+ Object* prototype);
+
+ // Total RegExp code ever generated
+ double total_regexp_code_generated_;
+
+ GCTracer* tracer_;
+
+ // 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();
+
+ void UpdateSurvivalRateTrend(int start_new_space_size);
+
+ enum SurvivalRateTrend { INCREASING, STABLE, DECREASING, FLUCTUATING };
+
+ 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_;
+
+ void set_survival_rate_trend(SurvivalRateTrend survival_rate_trend) {
+ ASSERT(survival_rate_trend != FLUCTUATING);
+ previous_survival_rate_trend_ = survival_rate_trend_;
+ survival_rate_trend_ = survival_rate_trend;
+ }
+
+ SurvivalRateTrend survival_rate_trend() {
+ if (survival_rate_trend_ == STABLE) {
+ return STABLE;
+ } else if (previous_survival_rate_trend_ == STABLE) {
+ return survival_rate_trend_;
+ } else if (survival_rate_trend_ != previous_survival_rate_trend_) {
+ return FLUCTUATING;
+ } else {
+ return survival_rate_trend_;
+ }
+ }
+
+ bool IsStableOrIncreasingSurvivalTrend() {
+ switch (survival_rate_trend()) {
+ case STABLE:
+ case INCREASING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool IsStableOrDecreasingSurvivalTrend() {
+ switch (survival_rate_trend()) {
+ case STABLE:
+ case DECREASING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool IsIncreasingSurvivalTrend() {
+ return survival_rate_trend() == INCREASING;
+ }
+
+ bool IsHighSurvivalRate() {
+ 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;
+ }
+
+ void FinishIdleRound() {
+ mark_sweeps_since_idle_round_started_ = kMaxMarkSweepsInIdleRound;
+ scavenges_since_last_idle_round_ = 0;
+ }
+
+ bool EnoughGarbageSinceLastIdleRound() {
+ return (scavenges_since_last_idle_round_ >= kIdleScavengeThreshold);
+ }
+
+ // Estimates how many milliseconds a Mark-Sweep would take to complete.
+ // In idle notification handler we assume that this function will return:
+ // - a number less than 10 for small heaps, which are less than 8Mb.
+ // - a number greater than 10 for large heaps, which are greater than 32Mb.
+ int TimeMarkSweepWouldTakeInMs() {
+ // Rough estimate of how many megabytes of heap can be processed in 1 ms.
+ static const int kMbPerMs = 2;
+
+ int heap_size_mb = static_cast<int>(SizeOfObjects() / MB);
+ return heap_size_mb / kMbPerMs;
+ }
+
+ // Returns true if no more GC work is left.
+ bool IdleGlobalGC();
+
+ void AdvanceIdleIncrementalMarking(intptr_t step_size);
+
+ void ClearObjectStats(bool clear_last_time_stats = false);
+
+ static const int kInitialStringTableSize = 2048;
+ static const int kInitialEvalCacheSize = 64;
+ static const int kInitialNumberStringCacheSize = 256;
+
+ // Object counts and used memory by InstanceType
+ size_t object_counts_[OBJECT_STATS_COUNT];
+ size_t object_counts_last_time_[OBJECT_STATS_COUNT];
+ size_t object_sizes_[OBJECT_STATS_COUNT];
+ size_t object_sizes_last_time_[OBJECT_STATS_COUNT];
+
+ // Maximum GC pause.
+ double max_gc_pause_;
+
+ // Total time spent in GC.
+ double total_gc_time_ms_;
+
+ // Maximum size of objects alive after GC.
+ intptr_t max_alive_after_gc_;
+
+ // Minimal interval between two subsequent collections.
+ double min_in_mutator_;
+
+ // Size of objects alive after last GC.
+ intptr_t alive_after_last_gc_;
+
+ double last_gc_end_timestamp_;
+
+ // Cumulative GC time spent in marking
+ double marking_time_;
+
+ // Cumulative GC time spent in sweeping
+ double sweeping_time_;
+
+ MarkCompactCollector mark_compact_collector_;
+
+ 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_;
+
+ int mark_sweeps_since_idle_round_started_;
+ unsigned int gc_count_at_last_idle_gc_;
+ int scavenges_since_last_idle_round_;
+
+ // If the --deopt_every_n_garbage_collections flag is set to a positive value,
+ // this variable holds the number of garbage collections since the last
+ // deoptimization triggered by garbage collection.
+ int gcs_since_last_deopt_;
+
+#ifdef VERIFY_HEAP
+ int no_weak_embedded_maps_verification_scope_depth_;
+#endif
+
+ 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 set up.
+ bool configured_;
+
+ ExternalStringTable external_string_table_;
+
+ VisitorDispatchTable<ScavengingCallback> scavenging_visitors_table_;
+
+ MemoryChunk* chunks_queued_for_free_;
+
+ Mutex* relocation_mutex_;
+#ifdef DEBUG
+ bool relocation_mutex_locked_by_optimizer_thread_;
+#endif // DEBUG;
+
+ friend class Factory;
+ friend class GCTracer;
+ friend class DisallowAllocationFailure;
+ friend class AlwaysAllocateScope;
+ friend class Page;
+ friend class Isolate;
+ friend class MarkCompactCollector;
+ friend class MarkCompactMarkingVisitor;
+ friend class MapCompact;
+#ifdef VERIFY_HEAP
+ friend class NoWeakEmbeddedMapsVerificationScope;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(Heap);
+};
+
+
+class HeapStats {
+ public:
+ static const int kStartMarker = 0xDECADE00;
+ static const int kEndMarker = 0xDECADE01;
+
+ int* start_marker; // 0
+ int* new_space_size; // 1
+ int* new_space_capacity; // 2
+ intptr_t* old_pointer_space_size; // 3
+ intptr_t* old_pointer_space_capacity; // 4
+ intptr_t* old_data_space_size; // 5
+ intptr_t* old_data_space_capacity; // 6
+ intptr_t* code_space_size; // 7
+ intptr_t* code_space_capacity; // 8
+ intptr_t* map_space_size; // 9
+ intptr_t* map_space_capacity; // 10
+ intptr_t* cell_space_size; // 11
+ intptr_t* cell_space_capacity; // 12
+ intptr_t* lo_space_size; // 13
+ int* global_handle_count; // 14
+ int* weak_global_handle_count; // 15
+ int* pending_global_handle_count; // 16
+ int* near_death_global_handle_count; // 17
+ int* free_global_handle_count; // 18
+ intptr_t* memory_allocator_size; // 19
+ intptr_t* memory_allocator_capacity; // 20
+ int* objects_per_type; // 21
+ int* size_per_type; // 22
+ int* os_error; // 23
+ int* end_marker; // 24
+ intptr_t* property_cell_space_size; // 25
+ intptr_t* property_cell_space_capacity; // 26
+};
+
+
+class DisallowAllocationFailure {
+ public:
+ inline DisallowAllocationFailure();
+ inline ~DisallowAllocationFailure();
+
+#ifdef DEBUG
+ private:
+ bool old_state_;
+#endif
+};
+
+
+class AlwaysAllocateScope {
+ public:
+ inline AlwaysAllocateScope();
+ inline ~AlwaysAllocateScope();
+
+ private:
+ // Implicitly disable artificial allocation failures.
+ DisallowAllocationFailure disallow_allocation_failure_;
+};
+
+#ifdef VERIFY_HEAP
+class NoWeakEmbeddedMapsVerificationScope {
+ public:
+ inline NoWeakEmbeddedMapsVerificationScope();
+ inline ~NoWeakEmbeddedMapsVerificationScope();
+};
+#endif
+
+
+// Visitor class to verify interior pointers in spaces that do not contain
+// or care about intergenerational references. All heap object pointers have to
+// point into the heap to a location that has a map pointer at its first word.
+// Caveat: Heap::Contains is an approximation because it can return true for
+// objects in a heap space but above the allocation pointer.
+class VerifyPointersVisitor: public ObjectVisitor {
+ public:
+ inline void VisitPointers(Object** start, Object** end);
+};
+
+
+// Space iterator for iterating over all spaces of the heap. Returns each space
+// in turn, and null when it is done.
+class AllSpaces BASE_EMBEDDED {
+ public:
+ explicit AllSpaces(Heap* heap) : heap_(heap), counter_(FIRST_SPACE) {}
+ Space* next();
+ private:
+ Heap* heap_;
+ int counter_;
+};
+
+
+// Space iterator for iterating over all old spaces of the heap: Old pointer
+// space, old data space and code space. Returns each space in turn, and null
+// when it is done.
+class OldSpaces BASE_EMBEDDED {
+ public:
+ explicit OldSpaces(Heap* heap) : heap_(heap), counter_(OLD_POINTER_SPACE) {}
+ OldSpace* next();
+ private:
+ Heap* heap_;
+ int counter_;
+};
+
+
+// Space iterator for iterating over all the paged spaces of the heap: Map
+// space, old pointer space, old data space, code space and cell space. Returns
+// each space in turn, and null when it is done.
+class PagedSpaces BASE_EMBEDDED {
+ public:
+ explicit PagedSpaces(Heap* heap) : heap_(heap), counter_(OLD_POINTER_SPACE) {}
+ PagedSpace* next();
+ private:
+ Heap* heap_;
+ int counter_;
+};
+
+
+// Space iterator for iterating over all spaces of the heap.
+// For each space an object iterator is provided. The deallocation of the
+// returned object iterators is handled by the space iterator.
+class SpaceIterator : public Malloced {
+ public:
+ explicit SpaceIterator(Heap* heap);
+ SpaceIterator(Heap* heap, HeapObjectCallback size_func);
+ virtual ~SpaceIterator();
+
+ bool has_next();
+ ObjectIterator* next();
+
+ private:
+ ObjectIterator* CreateIterator();
+
+ Heap* heap_;
+ int current_space_; // from enum AllocationSpace.
+ ObjectIterator* iterator_; // object iterator for the current space.
+ HeapObjectCallback size_func_;
+};
+
+
+// A HeapIterator provides iteration over the whole heap. It
+// aggregates the specific iterators for the different spaces as
+// these can only iterate over one space only.
+//
+// HeapIterator can skip free list nodes (that is, de-allocated heap
+// objects that still remain in the heap). As implementation of free
+// nodes filtering uses GC marks, it can't be used during MS/MC GC
+// phases. Also, it is forbidden to interrupt iteration in this mode,
+// as this will leave heap objects marked (and thus, unusable).
+class HeapObjectsFilter;
+
+class HeapIterator BASE_EMBEDDED {
+ public:
+ enum HeapObjectsFiltering {
+ kNoFiltering,
+ kFilterUnreachable
+ };
+
+ explicit HeapIterator(Heap* heap);
+ HeapIterator(Heap* heap, HeapObjectsFiltering filtering);
+ ~HeapIterator();
+
+ HeapObject* next();
+ void reset();
+
+ private:
+ // Perform the initialization.
+ void Init();
+ // Perform all necessary shutdown (destruction) work.
+ void Shutdown();
+ HeapObject* NextObject();
+
+ Heap* heap_;
+ HeapObjectsFiltering filtering_;
+ HeapObjectsFilter* filter_;
+ // Space iterator for iterating all the spaces.
+ SpaceIterator* space_iterator_;
+ // Object iterator for the space currently being iterated.
+ ObjectIterator* object_iterator_;
+};
+
+
+// Cache for mapping (map, property name) into field offset.
+// Cleared at startup and prior to mark sweep collection.
+class KeyedLookupCache {
+ public:
+ // Lookup field offset for (map, name). If absent, -1 is returned.
+ int Lookup(Map* map, Name* name);
+
+ // Update an element in the cache.
+ void Update(Map* map, Name* name, int field_offset);
+
+ // Clear the cache.
+ void Clear();
+
+ static const int kLength = 256;
+ static const int kCapacityMask = kLength - 1;
+ 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) {
+ keys_[i].map = NULL;
+ keys_[i].name = NULL;
+ field_offsets_[i] = kNotFound;
+ }
+ }
+
+ static inline int Hash(Map* map, Name* name);
+
+ // Get the address of the keys and field_offsets arrays. Used in
+ // generated code to perform cache lookups.
+ Address keys_address() {
+ return reinterpret_cast<Address>(&keys_);
+ }
+
+ Address field_offsets_address() {
+ return reinterpret_cast<Address>(&field_offsets_);
+ }
+
+ struct Key {
+ Map* map;
+ Name* name;
+ };
+
+ Key keys_[kLength];
+ int field_offsets_[kLength];
+
+ friend class ExternalReference;
+ friend class Isolate;
+ DISALLOW_COPY_AND_ASSIGN(KeyedLookupCache);
+};
+
+
+// Cache for mapping (map, property name) into descriptor index.
+// The cache contains both positive and negative results.
+// Descriptor index equals kNotFound means the property is absent.
+// Cleared at startup and prior to any gc.
+class DescriptorLookupCache {
+ public:
+ // Lookup descriptor index for (map, name).
+ // If absent, kAbsent is returned.
+ int Lookup(Map* source, Name* name) {
+ if (!name->IsUniqueName()) return kAbsent;
+ int index = Hash(source, name);
+ Key& key = keys_[index];
+ if ((key.source == source) && (key.name == name)) return results_[index];
+ return kAbsent;
+ }
+
+ // Update an element in the cache.
+ void Update(Map* source, Name* name, int result) {
+ ASSERT(result != kAbsent);
+ if (name->IsUniqueName()) {
+ int index = Hash(source, name);
+ Key& key = keys_[index];
+ key.source = source;
+ key.name = name;
+ results_[index] = result;
+ }
+ }
+
+ // Clear the cache.
+ void Clear();
+
+ static const int kAbsent = -2;
+
+ private:
+ DescriptorLookupCache() {
+ for (int i = 0; i < kLength; ++i) {
+ keys_[i].source = NULL;
+ keys_[i].name = NULL;
+ results_[i] = kAbsent;
+ }
+ }
+
+ static int Hash(Object* source, Name* name) {
+ // Uses only lower 32 bits if pointers are larger.
+ uint32_t source_hash =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(source))
+ >> kPointerSizeLog2;
+ uint32_t name_hash =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name))
+ >> kPointerSizeLog2;
+ return (source_hash ^ name_hash) % kLength;
+ }
+
+ static const int kLength = 64;
+ struct Key {
+ Map* source;
+ Name* name;
+ };
+
+ Key keys_[kLength];
+ int results_[kLength];
+
+ friend class Isolate;
+ DISALLOW_COPY_AND_ASSIGN(DescriptorLookupCache);
+};
+
+
+// GCTracer collects and prints ONE line after each garbage collector
+// invocation IFF --trace_gc is used.
+
+class GCTracer BASE_EMBEDDED {
+ public:
+ class Scope BASE_EMBEDDED {
+ public:
+ enum ScopeId {
+ EXTERNAL,
+ MC_MARK,
+ MC_SWEEP,
+ MC_SWEEP_NEWSPACE,
+ 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_WEAKCOLLECTION_PROCESS,
+ MC_WEAKCOLLECTION_CLEAR,
+ MC_FLUSH_CODE,
+ kNumberOfScopes
+ };
+
+ Scope(GCTracer* tracer, ScopeId scope)
+ : tracer_(tracer),
+ scope_(scope) {
+ start_time_ = OS::TimeCurrentMillis();
+ }
+
+ ~Scope() {
+ ASSERT(scope_ < kNumberOfScopes); // scope_ is unsigned.
+ tracer_->scopes_[scope_] += OS::TimeCurrentMillis() - start_time_;
+ }
+
+ private:
+ GCTracer* tracer_;
+ ScopeId scope_;
+ double start_time_;
+ };
+
+ explicit GCTracer(Heap* heap,
+ const char* gc_reason,
+ const char* collector_reason);
+ ~GCTracer();
+
+ // Sets the collector.
+ void set_collector(GarbageCollector collector) { collector_ = collector; }
+
+ // Sets the GC count.
+ void set_gc_count(unsigned int count) { gc_count_ = count; }
+
+ // Sets the full GC count.
+ void set_full_gc_count(int count) { full_gc_count_ = count; }
+
+ void increment_promoted_objects_size(int object_size) {
+ promoted_objects_size_ += object_size;
+ }
+
+ void increment_nodes_died_in_new_space() {
+ nodes_died_in_new_space_++;
+ }
+
+ void increment_nodes_copied_in_new_space() {
+ nodes_copied_in_new_space_++;
+ }
+
+ void increment_nodes_promoted() {
+ nodes_promoted_++;
+ }
+
+ private:
+ // Returns a string matching the collector.
+ const char* CollectorString();
+
+ // Returns size of object in heap (in 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_;
+
+ // Type of collector.
+ GarbageCollector collector_;
+
+ // 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_;
+
+ // Amounts of time spent in different scopes during GC.
+ double scopes_[Scope::kNumberOfScopes];
+
+ // Total amount of space either wasted or contained in one of free lists
+ // before the current GC.
+ intptr_t in_free_list_or_wasted_before_gc_;
+
+ // Difference between space used in the heap at the beginning of the current
+ // collection and the end of the previous collection.
+ intptr_t allocated_since_last_gc_;
+
+ // Amount of time spent in mutator that is time elapsed between end of the
+ // previous collection and the beginning of the current one.
+ double spent_in_mutator_;
+
+ // Size of objects promoted during the current collection.
+ intptr_t promoted_objects_size_;
+
+ // Number of died nodes in the new space.
+ int nodes_died_in_new_space_;
+
+ // Number of copied nodes to the new space.
+ int nodes_copied_in_new_space_;
+
+ // Number of promoted nodes to the old space.
+ int nodes_promoted_;
+
+ // 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_;
+};
+
+
+class RegExpResultsCache {
+ public:
+ enum ResultsCacheType { REGEXP_MULTIPLE_INDICES, STRING_SPLIT_SUBSTRINGS };
+
+ // Attempt to retrieve a cached result. On failure, 0 is returned as a Smi.
+ // On success, the returned result is guaranteed to be a COW-array.
+ static Object* Lookup(Heap* heap,
+ String* key_string,
+ Object* key_pattern,
+ ResultsCacheType type);
+ // Attempt to add value_array to the cache specified by type. On success,
+ // value_array is turned into a COW-array.
+ static void Enter(Heap* heap,
+ String* key_string,
+ Object* key_pattern,
+ FixedArray* value_array,
+ ResultsCacheType type);
+ static void Clear(FixedArray* cache);
+ static const int kRegExpResultsCacheSize = 0x100;
+
+ private:
+ static const int kArrayEntriesPerCacheEntry = 4;
+ static const int kStringOffset = 0;
+ static const int kPatternOffset = 1;
+ static const int kArrayOffset = 2;
+};
+
+
+class TranscendentalCache {
+ public:
+ enum Type {ACOS, ASIN, ATAN, COS, EXP, LOG, SIN, TAN, kNumberOfCaches};
+ static const int kTranscendentalTypeBits = 3;
+ STATIC_ASSERT((1 << kTranscendentalTypeBits) >= kNumberOfCaches);
+
+ // Returns a heap number with f(input), where f is a math function specified
+ // by the 'type' argument.
+ MUST_USE_RESULT inline MaybeObject* Get(Type type, double input);
+
+ // The cache contains raw Object pointers. This method disposes of
+ // them before a garbage collection.
+ void Clear();
+
+ private:
+ class SubCache {
+ static const int kCacheSize = 512;
+
+ explicit SubCache(Type t);
+
+ MUST_USE_RESULT inline MaybeObject* Get(double input);
+
+ inline double Calculate(double input);
+
+ struct Element {
+ uint32_t in[2];
+ Object* output;
+ };
+
+ union Converter {
+ double dbl;
+ uint32_t integers[2];
+ };
+
+ inline static int Hash(const Converter& c) {
+ uint32_t hash = (c.integers[0] ^ c.integers[1]);
+ hash ^= static_cast<int32_t>(hash) >> 16;
+ hash ^= static_cast<int32_t>(hash) >> 8;
+ return (hash & (kCacheSize - 1));
+ }
+
+ Element elements_[kCacheSize];
+ Type type_;
+ Isolate* isolate_;
+
+ // Allow access to the caches_ array as an ExternalReference.
+ friend class ExternalReference;
+ // Inline implementation of the cache.
+ friend class TranscendentalCacheStub;
+ // For evaluating value.
+ friend class TranscendentalCache;
+
+ DISALLOW_COPY_AND_ASSIGN(SubCache);
+ };
+
+ TranscendentalCache() {
+ for (int i = 0; i < kNumberOfCaches; ++i) caches_[i] = NULL;
+ }
+
+ ~TranscendentalCache() {
+ for (int i = 0; i < kNumberOfCaches; ++i) delete caches_[i];
+ }
+
+ // Used to create an external reference.
+ inline Address cache_array_address();
+
+ // Instantiation
+ friend class Isolate;
+ // Inline implementation of the caching.
+ friend class TranscendentalCacheStub;
+ // Allow access to the caches_ array as an ExternalReference.
+ friend class ExternalReference;
+
+ SubCache* caches_[kNumberOfCaches];
+ DISALLOW_COPY_AND_ASSIGN(TranscendentalCache);
+};
+
+
+// Abstract base class for checking whether a weak object should be retained.
+class WeakObjectRetainer {
+ public:
+ virtual ~WeakObjectRetainer() {}
+
+ // Return whether this object should be retained. If NULL is returned the
+ // object has no references. Otherwise the address of the retained object
+ // should be returned as in some GC situations the object has been moved.
+ virtual Object* RetainAs(Object* object) = 0;
+};
+
+
+// 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);
+};
+
+
+#ifdef DEBUG
+// 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
+// object to the search target object.
+class PathTracer : public ObjectVisitor {
+ public:
+ enum WhatToFind {
+ FIND_ALL, // Will find all matches.
+ FIND_FIRST // Will stop the search after first match.
+ };
+
+ // For the WhatToFind arg, if FIND_FIRST is specified, tracing will stop
+ // after the first match. If FIND_ALL is specified, then tracing will be
+ // done for all matches.
+ PathTracer(Object* search_target,
+ WhatToFind what_to_find,
+ VisitMode visit_mode)
+ : search_target_(search_target),
+ found_target_(false),
+ found_target_in_trace_(false),
+ what_to_find_(what_to_find),
+ visit_mode_(visit_mode),
+ object_stack_(20),
+ no_allocation() {}
+
+ virtual void VisitPointers(Object** start, Object** end);
+
+ void Reset();
+ void TracePathFrom(Object** root);
+
+ bool found() const { return found_target_; }
+
+ static Object* const kAnyGlobalObject;
+
+ protected:
+ class MarkVisitor;
+ class UnmarkVisitor;
+
+ void MarkRecursively(Object** p, MarkVisitor* mark_visitor);
+ void UnmarkRecursively(Object** p, UnmarkVisitor* unmark_visitor);
+ virtual void ProcessResults();
+
+ // Tags 0, 1, and 3 are used. Use 2 for marking visited HeapObject.
+ static const int kMarkTag = 2;
+
+ Object* search_target_;
+ bool found_target_;
+ bool found_target_in_trace_;
+ WhatToFind what_to_find_;
+ VisitMode visit_mode_;
+ List<Object*> object_stack_;
+
+ DisallowHeapAllocation no_allocation; // i.e. no gc allowed.
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PathTracer);
+};
+#endif // DEBUG
+
+} } // namespace v8::internal
+
+#endif // V8_HEAP_H_
diff --git a/chromium/v8/src/hydrogen-bce.cc b/chromium/v8/src/hydrogen-bce.cc
new file mode 100644
index 00000000000..7c81ec145cb
--- /dev/null
+++ b/chromium/v8/src/hydrogen-bce.cc
@@ -0,0 +1,394 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-bce.h"
+
+namespace v8 {
+namespace internal {
+
+// We try to "factor up" HBoundsCheck instructions towards the root of the
+// dominator tree.
+// For now we handle checks where the index is like "exp + int32value".
+// If in the dominator tree we check "exp + v1" and later (dominated)
+// "exp + v2", if v2 <= v1 we can safely remove the second check, and if
+// v2 > v1 we can use v2 in the 1st check and again remove the second.
+// To do so we keep a dictionary of all checks where the key if the pair
+// "exp, length".
+// The class BoundsCheckKey represents this key.
+class BoundsCheckKey : public ZoneObject {
+ public:
+ HValue* IndexBase() const { return index_base_; }
+ HValue* Length() const { return length_; }
+
+ uint32_t Hash() {
+ return static_cast<uint32_t>(index_base_->Hashcode() ^ length_->Hashcode());
+ }
+
+ static BoundsCheckKey* Create(Zone* zone,
+ HBoundsCheck* check,
+ int32_t* offset) {
+ if (!check->index()->representation().IsSmiOrInteger32()) return NULL;
+
+ HValue* index_base = NULL;
+ HConstant* constant = NULL;
+ bool is_sub = false;
+
+ if (check->index()->IsAdd()) {
+ HAdd* index = HAdd::cast(check->index());
+ if (index->left()->IsConstant()) {
+ constant = HConstant::cast(index->left());
+ index_base = index->right();
+ } else if (index->right()->IsConstant()) {
+ constant = HConstant::cast(index->right());
+ index_base = index->left();
+ }
+ } else if (check->index()->IsSub()) {
+ HSub* index = HSub::cast(check->index());
+ is_sub = true;
+ if (index->left()->IsConstant()) {
+ constant = HConstant::cast(index->left());
+ index_base = index->right();
+ } else if (index->right()->IsConstant()) {
+ constant = HConstant::cast(index->right());
+ index_base = index->left();
+ }
+ }
+
+ if (constant != NULL && constant->HasInteger32Value()) {
+ *offset = is_sub ? - constant->Integer32Value()
+ : constant->Integer32Value();
+ } else {
+ *offset = 0;
+ index_base = check->index();
+ }
+
+ return new(zone) BoundsCheckKey(index_base, check->length());
+ }
+
+ private:
+ BoundsCheckKey(HValue* index_base, HValue* length)
+ : index_base_(index_base),
+ length_(length) { }
+
+ HValue* index_base_;
+ HValue* length_;
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckKey);
+};
+
+
+// Data about each HBoundsCheck that can be eliminated or moved.
+// It is the "value" in the dictionary indexed by "base-index, length"
+// (the key is BoundsCheckKey).
+// We scan the code with a dominator tree traversal.
+// Traversing the dominator tree we keep a stack (implemented as a singly
+// linked list) of "data" for each basic block that contains a relevant check
+// with the same key (the dictionary holds the head of the list).
+// We also keep all the "data" created for a given basic block in a list, and
+// use it to "clean up" the dictionary when backtracking in the dominator tree
+// traversal.
+// Doing this each dictionary entry always directly points to the check that
+// is dominating the code being examined now.
+// We also track the current "offset" of the index expression and use it to
+// decide if any check is already "covered" (so it can be removed) or not.
+class BoundsCheckBbData: public ZoneObject {
+ public:
+ BoundsCheckKey* Key() const { return key_; }
+ int32_t LowerOffset() const { return lower_offset_; }
+ int32_t UpperOffset() const { return upper_offset_; }
+ HBasicBlock* BasicBlock() const { return basic_block_; }
+ HBoundsCheck* LowerCheck() const { return lower_check_; }
+ HBoundsCheck* UpperCheck() const { return upper_check_; }
+ BoundsCheckBbData* NextInBasicBlock() const { return next_in_bb_; }
+ BoundsCheckBbData* FatherInDominatorTree() const { return father_in_dt_; }
+
+ bool OffsetIsCovered(int32_t offset) const {
+ return offset >= LowerOffset() && offset <= UpperOffset();
+ }
+
+ bool HasSingleCheck() { return lower_check_ == upper_check_; }
+
+ // The goal of this method is to modify either upper_offset_ or
+ // lower_offset_ so that also new_offset is covered (the covered
+ // range grows).
+ //
+ // The precondition is that new_check follows UpperCheck() and
+ // LowerCheck() in the same basic block, and that new_offset is not
+ // covered (otherwise we could simply remove new_check).
+ //
+ // If HasSingleCheck() is true then new_check is added as "second check"
+ // (either upper or lower; note that HasSingleCheck() becomes false).
+ // Otherwise one of the current checks is modified so that it also covers
+ // new_offset, and new_check is removed.
+ //
+ // If the check cannot be modified because the context is unknown it
+ // returns false, otherwise it returns true.
+ bool CoverCheck(HBoundsCheck* new_check,
+ int32_t new_offset) {
+ ASSERT(new_check->index()->representation().IsSmiOrInteger32());
+ bool keep_new_check = false;
+
+ if (new_offset > upper_offset_) {
+ upper_offset_ = new_offset;
+ if (HasSingleCheck()) {
+ keep_new_check = true;
+ upper_check_ = new_check;
+ } else {
+ bool result = BuildOffsetAdd(upper_check_,
+ &added_upper_index_,
+ &added_upper_offset_,
+ Key()->IndexBase(),
+ new_check->index()->representation(),
+ new_offset);
+ if (!result) return false;
+ upper_check_->ReplaceAllUsesWith(upper_check_->index());
+ upper_check_->SetOperandAt(0, added_upper_index_);
+ }
+ } else if (new_offset < lower_offset_) {
+ lower_offset_ = new_offset;
+ if (HasSingleCheck()) {
+ keep_new_check = true;
+ lower_check_ = new_check;
+ } else {
+ bool result = BuildOffsetAdd(lower_check_,
+ &added_lower_index_,
+ &added_lower_offset_,
+ Key()->IndexBase(),
+ new_check->index()->representation(),
+ new_offset);
+ if (!result) return false;
+ lower_check_->ReplaceAllUsesWith(lower_check_->index());
+ lower_check_->SetOperandAt(0, added_lower_index_);
+ }
+ } else {
+ ASSERT(false);
+ }
+
+ if (!keep_new_check) {
+ new_check->block()->graph()->isolate()->counters()->
+ bounds_checks_eliminated()->Increment();
+ new_check->DeleteAndReplaceWith(new_check->ActualValue());
+ }
+
+ return true;
+ }
+
+ void RemoveZeroOperations() {
+ RemoveZeroAdd(&added_lower_index_, &added_lower_offset_);
+ RemoveZeroAdd(&added_upper_index_, &added_upper_offset_);
+ }
+
+ BoundsCheckBbData(BoundsCheckKey* key,
+ int32_t lower_offset,
+ int32_t upper_offset,
+ HBasicBlock* bb,
+ HBoundsCheck* lower_check,
+ HBoundsCheck* upper_check,
+ BoundsCheckBbData* next_in_bb,
+ BoundsCheckBbData* father_in_dt)
+ : key_(key),
+ lower_offset_(lower_offset),
+ upper_offset_(upper_offset),
+ basic_block_(bb),
+ lower_check_(lower_check),
+ upper_check_(upper_check),
+ added_lower_index_(NULL),
+ added_lower_offset_(NULL),
+ added_upper_index_(NULL),
+ added_upper_offset_(NULL),
+ next_in_bb_(next_in_bb),
+ father_in_dt_(father_in_dt) { }
+
+ private:
+ BoundsCheckKey* key_;
+ int32_t lower_offset_;
+ int32_t upper_offset_;
+ HBasicBlock* basic_block_;
+ HBoundsCheck* lower_check_;
+ HBoundsCheck* upper_check_;
+ HInstruction* added_lower_index_;
+ HConstant* added_lower_offset_;
+ HInstruction* added_upper_index_;
+ HConstant* added_upper_offset_;
+ BoundsCheckBbData* next_in_bb_;
+ BoundsCheckBbData* father_in_dt_;
+
+ // Given an existing add instruction and a bounds check it tries to
+ // find the current context (either of the add or of the check index).
+ HValue* IndexContext(HInstruction* add, HBoundsCheck* check) {
+ if (add != NULL && add->IsAdd()) {
+ return HAdd::cast(add)->context();
+ }
+ if (check->index()->IsBinaryOperation()) {
+ return HBinaryOperation::cast(check->index())->context();
+ }
+ return NULL;
+ }
+
+ // This function returns false if it cannot build the add because the
+ // current context cannot be determined.
+ bool BuildOffsetAdd(HBoundsCheck* check,
+ HInstruction** add,
+ HConstant** constant,
+ HValue* original_value,
+ Representation representation,
+ int32_t new_offset) {
+ HValue* index_context = IndexContext(*add, check);
+ if (index_context == NULL) return false;
+
+ Zone* zone = BasicBlock()->zone();
+ HConstant* new_constant = HConstant::New(zone, index_context,
+ new_offset, representation);
+ if (*add == NULL) {
+ new_constant->InsertBefore(check);
+ (*add) = HAdd::New(zone, index_context, original_value, new_constant);
+ (*add)->AssumeRepresentation(representation);
+ (*add)->InsertBefore(check);
+ } else {
+ new_constant->InsertBefore(*add);
+ (*constant)->DeleteAndReplaceWith(new_constant);
+ }
+ *constant = new_constant;
+ return true;
+ }
+
+ void RemoveZeroAdd(HInstruction** add, HConstant** constant) {
+ if (*add != NULL && (*add)->IsAdd() && (*constant)->Integer32Value() == 0) {
+ (*add)->DeleteAndReplaceWith(HAdd::cast(*add)->left());
+ (*constant)->DeleteAndReplaceWith(NULL);
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckBbData);
+};
+
+
+static bool BoundsCheckKeyMatch(void* key1, void* key2) {
+ BoundsCheckKey* k1 = static_cast<BoundsCheckKey*>(key1);
+ BoundsCheckKey* k2 = static_cast<BoundsCheckKey*>(key2);
+ return k1->IndexBase() == k2->IndexBase() && k1->Length() == k2->Length();
+}
+
+
+BoundsCheckTable::BoundsCheckTable(Zone* zone)
+ : ZoneHashMap(BoundsCheckKeyMatch, ZoneHashMap::kDefaultHashMapCapacity,
+ ZoneAllocationPolicy(zone)) { }
+
+
+BoundsCheckBbData** BoundsCheckTable::LookupOrInsert(BoundsCheckKey* key,
+ Zone* zone) {
+ return reinterpret_cast<BoundsCheckBbData**>(
+ &(Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value));
+}
+
+
+void BoundsCheckTable::Insert(BoundsCheckKey* key,
+ BoundsCheckBbData* data,
+ Zone* zone) {
+ Lookup(key, key->Hash(), true, ZoneAllocationPolicy(zone))->value = data;
+}
+
+
+void BoundsCheckTable::Delete(BoundsCheckKey* key) {
+ Remove(key, key->Hash());
+}
+
+
+// Eliminates checks in bb and recursively in the dominated blocks.
+// Also replace the results of check instructions with the original value, if
+// the result is used. This is safe now, since we don't do code motion after
+// this point. It enables better register allocation since the value produced
+// by check instructions is really a copy of the original value.
+void HBoundsCheckEliminationPhase::EliminateRedundantBoundsChecks(
+ HBasicBlock* bb) {
+ BoundsCheckBbData* bb_data_list = NULL;
+
+ for (HInstructionIterator it(bb); !it.Done(); it.Advance()) {
+ HInstruction* i = it.Current();
+ if (!i->IsBoundsCheck()) continue;
+
+ HBoundsCheck* check = HBoundsCheck::cast(i);
+ int32_t offset;
+ BoundsCheckKey* key =
+ BoundsCheckKey::Create(zone(), check, &offset);
+ if (key == NULL) continue;
+ BoundsCheckBbData** data_p = table_.LookupOrInsert(key, zone());
+ BoundsCheckBbData* data = *data_p;
+ if (data == NULL) {
+ bb_data_list = new(zone()) BoundsCheckBbData(key,
+ offset,
+ offset,
+ bb,
+ check,
+ check,
+ bb_data_list,
+ NULL);
+ *data_p = bb_data_list;
+ } else if (data->OffsetIsCovered(offset)) {
+ bb->graph()->isolate()->counters()->
+ bounds_checks_eliminated()->Increment();
+ check->DeleteAndReplaceWith(check->ActualValue());
+ } else if (data->BasicBlock() != bb ||
+ !data->CoverCheck(check, offset)) {
+ // If the check is in the current BB we try to modify it by calling
+ // "CoverCheck", but if also that fails we record the current offsets
+ // in a new data instance because from now on they are covered.
+ int32_t new_lower_offset = offset < data->LowerOffset()
+ ? offset
+ : data->LowerOffset();
+ int32_t new_upper_offset = offset > data->UpperOffset()
+ ? offset
+ : data->UpperOffset();
+ bb_data_list = new(zone()) BoundsCheckBbData(key,
+ new_lower_offset,
+ new_upper_offset,
+ bb,
+ data->LowerCheck(),
+ data->UpperCheck(),
+ bb_data_list,
+ data);
+ table_.Insert(key, bb_data_list, zone());
+ }
+ }
+
+ for (int i = 0; i < bb->dominated_blocks()->length(); ++i) {
+ EliminateRedundantBoundsChecks(bb->dominated_blocks()->at(i));
+ }
+
+ for (BoundsCheckBbData* data = bb_data_list;
+ data != NULL;
+ data = data->NextInBasicBlock()) {
+ data->RemoveZeroOperations();
+ if (data->FatherInDominatorTree()) {
+ table_.Insert(data->Key(), data->FatherInDominatorTree(), zone());
+ } else {
+ table_.Delete(data->Key());
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-bce.h b/chromium/v8/src/hydrogen-bce.h
new file mode 100644
index 00000000000..d91997bda01
--- /dev/null
+++ b/chromium/v8/src/hydrogen-bce.h
@@ -0,0 +1,72 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_BCE_H_
+#define V8_HYDROGEN_BCE_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class BoundsCheckBbData;
+class BoundsCheckKey;
+class BoundsCheckTable : private ZoneHashMap {
+ public:
+ explicit BoundsCheckTable(Zone* zone);
+
+ INLINE(BoundsCheckBbData** LookupOrInsert(BoundsCheckKey* key, Zone* zone));
+ INLINE(void Insert(BoundsCheckKey* key, BoundsCheckBbData* data, Zone* zone));
+ INLINE(void Delete(BoundsCheckKey* key));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BoundsCheckTable);
+};
+
+
+class HBoundsCheckEliminationPhase : public HPhase {
+ public:
+ explicit HBoundsCheckEliminationPhase(HGraph* graph)
+ : HPhase("H_Bounds checks elimination", graph), table_(zone()) { }
+
+ void Run() {
+ EliminateRedundantBoundsChecks(graph()->entry_block());
+ }
+
+ private:
+ void EliminateRedundantBoundsChecks(HBasicBlock* bb);
+
+ BoundsCheckTable table_;
+
+ DISALLOW_COPY_AND_ASSIGN(HBoundsCheckEliminationPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_BCE_H_
diff --git a/chromium/v8/src/hydrogen-bch.cc b/chromium/v8/src/hydrogen-bch.cc
new file mode 100644
index 00000000000..137d6295477
--- /dev/null
+++ b/chromium/v8/src/hydrogen-bch.cc
@@ -0,0 +1,410 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-bch.h"
+
+namespace v8 {
+namespace internal {
+
+/*
+ * This class is a table with one element for eack basic block.
+ *
+ * It is used to check if, inside one loop, all execution paths contain
+ * a bounds check for a particular [index, length] combination.
+ * The reason is that if there is a path that stays in the loop without
+ * executing a check then the check cannot be hoisted out of the loop (it
+ * would likely fail and cause a deopt for no good reason).
+ * We also check is there are paths that exit the loop early, and if yes we
+ * perform the hoisting only if graph()->use_optimistic_licm() is true.
+ * The reason is that such paths are realtively common and harmless (like in
+ * a "search" method that scans an array until an element is found), but in
+ * some cases they could cause a deopt if we hoist the check so this is a
+ * situation we need to detect.
+ */
+class InductionVariableBlocksTable BASE_EMBEDDED {
+ public:
+ class Element {
+ public:
+ static const int kNoBlock = -1;
+
+ HBasicBlock* block() { return block_; }
+ void set_block(HBasicBlock* block) { block_ = block; }
+ bool is_start() { return is_start_; }
+ bool is_proper_exit() { return is_proper_exit_; }
+ bool is_in_loop() { return is_in_loop_; }
+ bool has_check() { return has_check_; }
+ void set_has_check() { has_check_ = true; }
+ InductionVariableLimitUpdate* additional_limit() {
+ return &additional_limit_;
+ }
+
+ /*
+ * Initializes the table element for a given loop (identified by its
+ * induction variable).
+ */
+ void InitializeLoop(InductionVariableData* data) {
+ ASSERT(data->limit() != NULL);
+ HLoopInformation* loop = data->phi()->block()->current_loop();
+ is_start_ = (block() == loop->loop_header());
+ is_proper_exit_ = (block() == data->induction_exit_target());
+ is_in_loop_ = loop->IsNestedInThisLoop(block()->current_loop());
+ has_check_ = false;
+ }
+
+ // Utility methods to iterate over dominated blocks.
+ void ResetCurrentDominatedBlock() { current_dominated_block_ = kNoBlock; }
+ HBasicBlock* CurrentDominatedBlock() {
+ ASSERT(current_dominated_block_ != kNoBlock);
+ return current_dominated_block_ < block()->dominated_blocks()->length() ?
+ block()->dominated_blocks()->at(current_dominated_block_) : NULL;
+ }
+ HBasicBlock* NextDominatedBlock() {
+ current_dominated_block_++;
+ return CurrentDominatedBlock();
+ }
+
+ Element()
+ : block_(NULL), is_start_(false), is_proper_exit_(false),
+ has_check_(false), additional_limit_(),
+ current_dominated_block_(kNoBlock) {}
+
+ private:
+ HBasicBlock* block_;
+ bool is_start_;
+ bool is_proper_exit_;
+ bool is_in_loop_;
+ bool has_check_;
+ InductionVariableLimitUpdate additional_limit_;
+ int current_dominated_block_;
+ };
+
+ HGraph* graph() { return graph_; }
+ HBasicBlock* loop_header() { return loop_header_; }
+ Element* at(int index) { return &(elements_.at(index)); }
+ Element* at(HBasicBlock* block) { return at(block->block_id()); }
+
+ void AddCheckAt(HBasicBlock* block) {
+ at(block->block_id())->set_has_check();
+ }
+
+ /*
+ * Initializes the table for a given loop (identified by its induction
+ * variable).
+ */
+ void InitializeLoop(InductionVariableData* data) {
+ for (int i = 0; i < graph()->blocks()->length(); i++) {
+ at(i)->InitializeLoop(data);
+ }
+ loop_header_ = data->phi()->block()->current_loop()->loop_header();
+ }
+
+
+ enum Hoistability {
+ HOISTABLE,
+ OPTIMISTICALLY_HOISTABLE,
+ NOT_HOISTABLE
+ };
+
+ /*
+ * This method checks if it is appropriate to hoist the bounds checks on an
+ * induction variable out of the loop.
+ * The problem is that in the loop code graph there could be execution paths
+ * where the check is not performed, but hoisting the check has the same
+ * semantics as performing it at every loop iteration, which could cause
+ * unnecessary check failures (which would mean unnecessary deoptimizations).
+ * The method returns OK if there are no paths that perform an iteration
+ * (loop back to the header) without meeting a check, or UNSAFE is set if
+ * early exit paths are found.
+ */
+ Hoistability CheckHoistability() {
+ for (int i = 0; i < elements_.length(); i++) {
+ at(i)->ResetCurrentDominatedBlock();
+ }
+ bool unsafe = false;
+
+ HBasicBlock* current = loop_header();
+ while (current != NULL) {
+ HBasicBlock* next;
+
+ if (at(current)->has_check() || !at(current)->is_in_loop()) {
+ // We found a check or we reached a dominated block out of the loop,
+ // therefore this block is safe and we can backtrack.
+ next = NULL;
+ } else {
+ for (int i = 0; i < current->end()->SuccessorCount(); i ++) {
+ Element* successor = at(current->end()->SuccessorAt(i));
+
+ if (!successor->is_in_loop()) {
+ if (!successor->is_proper_exit()) {
+ // We found a path that exits the loop early, and is not the exit
+ // related to the induction limit, therefore hoisting checks is
+ // an optimistic assumption.
+ unsafe = true;
+ }
+ }
+
+ if (successor->is_start()) {
+ // We found a path that does one loop iteration without meeting any
+ // check, therefore hoisting checks would be likely to cause
+ // unnecessary deopts.
+ return NOT_HOISTABLE;
+ }
+ }
+
+ next = at(current)->NextDominatedBlock();
+ }
+
+ // If we have no next block we need to backtrack the tree traversal.
+ while (next == NULL) {
+ current = current->dominator();
+ if (current != NULL) {
+ next = at(current)->NextDominatedBlock();
+ } else {
+ // We reached the root: next stays NULL.
+ next = NULL;
+ break;
+ }
+ }
+
+ current = next;
+ }
+
+ return unsafe ? OPTIMISTICALLY_HOISTABLE : HOISTABLE;
+ }
+
+ explicit InductionVariableBlocksTable(HGraph* graph)
+ : graph_(graph), loop_header_(NULL),
+ elements_(graph->blocks()->length(), graph->zone()) {
+ for (int i = 0; i < graph->blocks()->length(); i++) {
+ Element element;
+ element.set_block(graph->blocks()->at(i));
+ elements_.Add(element, graph->zone());
+ ASSERT(at(i)->block()->block_id() == i);
+ }
+ }
+
+ // Tries to hoist a check out of its induction loop.
+ void ProcessRelatedChecks(
+ InductionVariableData::InductionVariableCheck* check,
+ InductionVariableData* data) {
+ HValue* length = check->check()->length();
+ check->set_processed();
+ HBasicBlock* header =
+ data->phi()->block()->current_loop()->loop_header();
+ HBasicBlock* pre_header = header->predecessors()->at(0);
+ // Check that the limit is defined in the loop preheader.
+ if (!data->limit()->IsInteger32Constant()) {
+ HBasicBlock* limit_block = data->limit()->block();
+ if (limit_block != pre_header &&
+ !limit_block->Dominates(pre_header)) {
+ return;
+ }
+ }
+ // Check that the length and limit have compatible representations.
+ if (!(data->limit()->representation().Equals(
+ length->representation()) ||
+ data->limit()->IsInteger32Constant())) {
+ return;
+ }
+ // Check that the length is defined in the loop preheader.
+ if (check->check()->length()->block() != pre_header &&
+ !check->check()->length()->block()->Dominates(pre_header)) {
+ return;
+ }
+
+ // Add checks to the table.
+ for (InductionVariableData::InductionVariableCheck* current_check = check;
+ current_check != NULL;
+ current_check = current_check->next()) {
+ if (current_check->check()->length() != length) continue;
+
+ AddCheckAt(current_check->check()->block());
+ current_check->set_processed();
+ }
+
+ // Check that we will not cause unwanted deoptimizations.
+ Hoistability hoistability = CheckHoistability();
+ if (hoistability == NOT_HOISTABLE ||
+ (hoistability == OPTIMISTICALLY_HOISTABLE &&
+ !graph()->use_optimistic_licm())) {
+ return;
+ }
+
+ // We will do the hoisting, but we must see if the limit is "limit" or if
+ // all checks are done on constants: if all check are done against the same
+ // constant limit we will use that instead of the induction limit.
+ bool has_upper_constant_limit = true;
+ InductionVariableData::InductionVariableCheck* current_check = check;
+ int32_t upper_constant_limit =
+ current_check != NULL && current_check->HasUpperLimit() ?
+ current_check->upper_limit() : 0;
+ while (current_check != NULL) {
+ if (check->HasUpperLimit()) {
+ if (check->upper_limit() != upper_constant_limit) {
+ has_upper_constant_limit = false;
+ }
+ } else {
+ has_upper_constant_limit = false;
+ }
+
+ current_check->check()->block()->graph()->isolate()->counters()->
+ bounds_checks_eliminated()->Increment();
+ current_check->check()->set_skip_check();
+ current_check = current_check->next();
+ }
+
+ // Choose the appropriate limit.
+ Zone* zone = graph()->zone();
+ HValue* context = graph()->GetInvalidContext();
+ HValue* limit = data->limit();
+ if (has_upper_constant_limit) {
+ HConstant* new_limit = HConstant::New(zone, context,
+ upper_constant_limit);
+ new_limit->InsertBefore(pre_header->end());
+ limit = new_limit;
+ }
+
+ // If necessary, redefine the limit in the preheader.
+ if (limit->IsInteger32Constant() &&
+ limit->block() != pre_header &&
+ !limit->block()->Dominates(pre_header)) {
+ HConstant* new_limit = HConstant::New(zone, context,
+ limit->GetInteger32Constant());
+ new_limit->InsertBefore(pre_header->end());
+ limit = new_limit;
+ }
+
+ // Do the hoisting.
+ HBoundsCheck* hoisted_check = HBoundsCheck::New(
+ zone, context, limit, check->check()->length());
+ hoisted_check->InsertBefore(pre_header->end());
+ hoisted_check->set_allow_equality(true);
+ hoisted_check->block()->graph()->isolate()->counters()->
+ bounds_checks_hoisted()->Increment();
+ }
+
+ void CollectInductionVariableData(HBasicBlock* bb) {
+ bool additional_limit = false;
+
+ for (int i = 0; i < bb->phis()->length(); i++) {
+ HPhi* phi = bb->phis()->at(i);
+ phi->DetectInductionVariable();
+ }
+
+ additional_limit = InductionVariableData::ComputeInductionVariableLimit(
+ bb, at(bb)->additional_limit());
+
+ if (additional_limit) {
+ at(bb)->additional_limit()->updated_variable->
+ UpdateAdditionalLimit(at(bb)->additional_limit());
+ }
+
+ for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
+ if (!i->IsBoundsCheck()) continue;
+ HBoundsCheck* check = HBoundsCheck::cast(i);
+ InductionVariableData::BitwiseDecompositionResult decomposition;
+ InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
+ if (!decomposition.base->IsPhi()) continue;
+ HPhi* phi = HPhi::cast(decomposition.base);
+
+ if (!phi->IsInductionVariable()) continue;
+ InductionVariableData* data = phi->induction_variable_data();
+
+ // For now ignore loops decrementing the index.
+ if (data->increment() <= 0) continue;
+ if (!data->LowerLimitIsNonNegativeConstant()) continue;
+
+ // TODO(mmassi): skip OSR values for check->length().
+ if (check->length() == data->limit() ||
+ check->length() == data->additional_upper_limit()) {
+ check->block()->graph()->isolate()->counters()->
+ bounds_checks_eliminated()->Increment();
+ check->set_skip_check();
+ continue;
+ }
+
+ if (!phi->IsLimitedInductionVariable()) continue;
+
+ int32_t limit = data->ComputeUpperLimit(decomposition.and_mask,
+ decomposition.or_mask);
+ phi->induction_variable_data()->AddCheck(check, limit);
+ }
+
+ for (int i = 0; i < bb->dominated_blocks()->length(); i++) {
+ CollectInductionVariableData(bb->dominated_blocks()->at(i));
+ }
+
+ if (additional_limit) {
+ at(bb->block_id())->additional_limit()->updated_variable->
+ UpdateAdditionalLimit(at(bb->block_id())->additional_limit());
+ }
+ }
+
+ void EliminateRedundantBoundsChecks(HBasicBlock* bb) {
+ for (int i = 0; i < bb->phis()->length(); i++) {
+ HPhi* phi = bb->phis()->at(i);
+ if (!phi->IsLimitedInductionVariable()) continue;
+
+ InductionVariableData* induction_data = phi->induction_variable_data();
+ InductionVariableData::ChecksRelatedToLength* current_length_group =
+ induction_data->checks();
+ while (current_length_group != NULL) {
+ current_length_group->CloseCurrentBlock();
+ InductionVariableData::InductionVariableCheck* current_base_check =
+ current_length_group->checks();
+ InitializeLoop(induction_data);
+
+ while (current_base_check != NULL) {
+ ProcessRelatedChecks(current_base_check, induction_data);
+ while (current_base_check != NULL &&
+ current_base_check->processed()) {
+ current_base_check = current_base_check->next();
+ }
+ }
+
+ current_length_group = current_length_group->next();
+ }
+ }
+ }
+
+ private:
+ HGraph* graph_;
+ HBasicBlock* loop_header_;
+ ZoneList<Element> elements_;
+};
+
+
+void HBoundsCheckHoistingPhase::HoistRedundantBoundsChecks() {
+ InductionVariableBlocksTable table(graph());
+ table.CollectInductionVariableData(graph()->entry_block());
+ for (int i = 0; i < graph()->blocks()->length(); i++) {
+ table.EliminateRedundantBoundsChecks(graph()->blocks()->at(i));
+ }
+}
+
+} } // namespace v8::internal
+
diff --git a/chromium/v8/src/hydrogen-bch.h b/chromium/v8/src/hydrogen-bch.h
new file mode 100644
index 00000000000..a22dacdd422
--- /dev/null
+++ b/chromium/v8/src/hydrogen-bch.h
@@ -0,0 +1,55 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_BCH_H_
+#define V8_HYDROGEN_BCH_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HBoundsCheckHoistingPhase : public HPhase {
+ public:
+ explicit HBoundsCheckHoistingPhase(HGraph* graph)
+ : HPhase("H_Bounds checks hoisting", graph) { }
+
+ void Run() {
+ HoistRedundantBoundsChecks();
+ }
+
+ private:
+ void HoistRedundantBoundsChecks();
+
+ DISALLOW_COPY_AND_ASSIGN(HBoundsCheckHoistingPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_BCE_H_
diff --git a/chromium/v8/src/hydrogen-canonicalize.cc b/chromium/v8/src/hydrogen-canonicalize.cc
new file mode 100644
index 00000000000..643234392d0
--- /dev/null
+++ b/chromium/v8/src/hydrogen-canonicalize.cc
@@ -0,0 +1,66 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-canonicalize.h"
+
+namespace v8 {
+namespace internal {
+
+void HCanonicalizePhase::Run() {
+ const ZoneList<HBasicBlock*>* blocks(graph()->blocks());
+ // Before removing no-op instructions, save their semantic value.
+ // We must be careful not to set the flag unnecessarily, because GVN
+ // cannot identify two instructions when their flag value differs.
+ for (int i = 0; i < blocks->length(); ++i) {
+ for (HInstructionIterator it(blocks->at(i)); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ if (instr->IsArithmeticBinaryOperation()) {
+ if (instr->representation().IsInteger32()) {
+ if (instr->HasAtLeastOneUseWithFlagAndNoneWithout(
+ HInstruction::kTruncatingToInt32)) {
+ instr->SetFlag(HInstruction::kAllUsesTruncatingToInt32);
+ }
+ } else if (instr->representation().IsSmi()) {
+ if (instr->HasAtLeastOneUseWithFlagAndNoneWithout(
+ HInstruction::kTruncatingToSmi)) {
+ instr->SetFlag(HInstruction::kAllUsesTruncatingToSmi);
+ }
+ }
+ }
+ }
+ }
+ // Perform actual Canonicalization pass.
+ for (int i = 0; i < blocks->length(); ++i) {
+ for (HInstructionIterator it(blocks->at(i)); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ HValue* value = instr->Canonicalize();
+ if (value != instr) instr->DeleteAndReplaceWith(value);
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-canonicalize.h b/chromium/v8/src/hydrogen-canonicalize.h
new file mode 100644
index 00000000000..d2b289bc212
--- /dev/null
+++ b/chromium/v8/src/hydrogen-canonicalize.h
@@ -0,0 +1,51 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_CANONICALIZE_H_
+#define V8_HYDROGEN_CANONICALIZE_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HCanonicalizePhase : public HPhase {
+ public:
+ explicit HCanonicalizePhase(HGraph* graph)
+ : HPhase("H_Canonicalize", graph) { }
+
+ void Run();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HCanonicalizePhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_CANONICALIZE_H_
diff --git a/chromium/v8/src/hydrogen-dce.cc b/chromium/v8/src/hydrogen-dce.cc
new file mode 100644
index 00000000000..0e7253d5a48
--- /dev/null
+++ b/chromium/v8/src/hydrogen-dce.cc
@@ -0,0 +1,127 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-dce.h"
+#include "v8.h"
+
+namespace v8 {
+namespace internal {
+
+bool HDeadCodeEliminationPhase::MarkLive(HValue* ref, HValue* instr) {
+ if (instr->CheckFlag(HValue::kIsLive)) return false;
+ instr->SetFlag(HValue::kIsLive);
+
+ if (FLAG_trace_dead_code_elimination) {
+ HeapStringAllocator allocator;
+ StringStream stream(&allocator);
+ if (ref != NULL) {
+ ref->PrintTo(&stream);
+ } else {
+ stream.Add("root ");
+ }
+ stream.Add(" -> ");
+ instr->PrintTo(&stream);
+ PrintF("[MarkLive %s]\n", *stream.ToCString());
+ }
+
+ return true;
+}
+
+
+void HDeadCodeEliminationPhase::MarkLiveInstructions() {
+ ZoneList<HValue*> worklist(graph()->blocks()->length(), zone());
+
+ // Mark initial root instructions for dead code elimination.
+ for (int i = 0; i < graph()->blocks()->length(); ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ if (instr->CannotBeEliminated() && MarkLive(NULL, instr)) {
+ worklist.Add(instr, zone());
+ }
+ }
+ for (int j = 0; j < block->phis()->length(); j++) {
+ HPhi* phi = block->phis()->at(j);
+ if (phi->CannotBeEliminated() && MarkLive(NULL, phi)) {
+ worklist.Add(phi, zone());
+ }
+ }
+ }
+
+ // Transitively mark all inputs of live instructions live.
+ while (!worklist.is_empty()) {
+ HValue* instr = worklist.RemoveLast();
+ for (int i = 0; i < instr->OperandCount(); ++i) {
+ if (MarkLive(instr, instr->OperandAt(i))) {
+ worklist.Add(instr->OperandAt(i), zone());
+ }
+ }
+ }
+}
+
+
+void HDeadCodeEliminationPhase::RemoveDeadInstructions() {
+ ZoneList<HPhi*> worklist(graph()->blocks()->length(), zone());
+
+ // Remove any instruction not marked kIsLive.
+ for (int i = 0; i < graph()->blocks()->length(); ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ if (!instr->CheckFlag(HValue::kIsLive)) {
+ // Instruction has not been marked live; assume it is dead and remove.
+ // TODO(titzer): we don't remove constants because some special ones
+ // might be used by later phases and are assumed to be in the graph
+ if (!instr->IsConstant()) instr->DeleteAndReplaceWith(NULL);
+ } else {
+ // Clear the liveness flag to leave the graph clean for the next DCE.
+ instr->ClearFlag(HValue::kIsLive);
+ }
+ }
+ // Collect phis that are dead and remove them in the next pass.
+ for (int j = 0; j < block->phis()->length(); j++) {
+ HPhi* phi = block->phis()->at(j);
+ if (!phi->CheckFlag(HValue::kIsLive)) {
+ worklist.Add(phi, zone());
+ } else {
+ phi->ClearFlag(HValue::kIsLive);
+ }
+ }
+ }
+
+ // Process phis separately to avoid simultaneously mutating the phi list.
+ while (!worklist.is_empty()) {
+ HPhi* phi = worklist.RemoveLast();
+ HBasicBlock* block = phi->block();
+ phi->DeleteAndReplaceWith(NULL);
+ if (phi->HasMergedIndex()) {
+ block->RecordDeletedPhi(phi->merged_index());
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-dce.h b/chromium/v8/src/hydrogen-dce.h
new file mode 100644
index 00000000000..19749f279a2
--- /dev/null
+++ b/chromium/v8/src/hydrogen-dce.h
@@ -0,0 +1,56 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_DCE_H_
+#define V8_HYDROGEN_DCE_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HDeadCodeEliminationPhase : public HPhase {
+ public:
+ explicit HDeadCodeEliminationPhase(HGraph* graph)
+ : HPhase("H_Dead code elimination", graph) { }
+
+ void Run() {
+ MarkLiveInstructions();
+ RemoveDeadInstructions();
+ }
+
+ private:
+ bool MarkLive(HValue* ref, HValue* instr);
+ void MarkLiveInstructions();
+ void RemoveDeadInstructions();
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_DCE_H_
diff --git a/chromium/v8/src/hydrogen-dehoist.cc b/chromium/v8/src/hydrogen-dehoist.cc
new file mode 100644
index 00000000000..67e67189983
--- /dev/null
+++ b/chromium/v8/src/hydrogen-dehoist.cc
@@ -0,0 +1,80 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-dehoist.h"
+
+namespace v8 {
+namespace internal {
+
+static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) {
+ HValue* index = array_operation->GetKey()->ActualValue();
+ if (!index->representation().IsSmiOrInteger32()) return;
+ if (!index->IsAdd() && !index->IsSub()) return;
+
+ HConstant* constant;
+ HValue* subexpression;
+ HBinaryOperation* binary_operation = HBinaryOperation::cast(index);
+ if (binary_operation->left()->IsConstant() && index->IsAdd()) {
+ subexpression = binary_operation->right();
+ constant = HConstant::cast(binary_operation->left());
+ } else if (binary_operation->right()->IsConstant()) {
+ subexpression = binary_operation->left();
+ constant = HConstant::cast(binary_operation->right());
+ } else {
+ return;
+ }
+
+ if (!constant->HasInteger32Value()) return;
+ int32_t sign = binary_operation->IsSub() ? -1 : 1;
+ int32_t value = constant->Integer32Value() * sign;
+ // We limit offset values to 30 bits because we want to avoid the risk of
+ // overflows when the offset is added to the object header size.
+ if (value >= 1 << 30 || value < 0) return;
+ array_operation->SetKey(subexpression);
+ if (binary_operation->HasNoUses()) {
+ binary_operation->DeleteAndReplaceWith(NULL);
+ }
+ array_operation->SetIndexOffset(static_cast<uint32_t>(value));
+ array_operation->SetDehoisted(true);
+}
+
+
+void HDehoistIndexComputationsPhase::Run() {
+ const ZoneList<HBasicBlock*>* blocks(graph()->blocks());
+ for (int i = 0; i < blocks->length(); ++i) {
+ for (HInstructionIterator it(blocks->at(i)); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ if (instr->IsLoadKeyed()) {
+ DehoistArrayIndex(HLoadKeyed::cast(instr));
+ } else if (instr->IsStoreKeyed()) {
+ DehoistArrayIndex(HStoreKeyed::cast(instr));
+ }
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-dehoist.h b/chromium/v8/src/hydrogen-dehoist.h
new file mode 100644
index 00000000000..140dc6e0e22
--- /dev/null
+++ b/chromium/v8/src/hydrogen-dehoist.h
@@ -0,0 +1,51 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_DEHOIST_H_
+#define V8_HYDROGEN_DEHOIST_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HDehoistIndexComputationsPhase : public HPhase {
+ public:
+ explicit HDehoistIndexComputationsPhase(HGraph* graph)
+ : HPhase("H_Dehoist index computations", graph) { }
+
+ void Run();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HDehoistIndexComputationsPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_DEHOIST_H_
diff --git a/chromium/v8/src/hydrogen-deoptimizing-mark.cc b/chromium/v8/src/hydrogen-deoptimizing-mark.cc
new file mode 100644
index 00000000000..626848e012f
--- /dev/null
+++ b/chromium/v8/src/hydrogen-deoptimizing-mark.cc
@@ -0,0 +1,126 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-deoptimizing-mark.h"
+
+namespace v8 {
+namespace internal {
+
+void HPropagateDeoptimizingMarkPhase::MarkAsDeoptimizing() {
+ HBasicBlock* block = graph()->entry_block();
+ ZoneList<HBasicBlock*> stack(graph()->blocks()->length(), zone());
+ while (block != NULL) {
+ const ZoneList<HBasicBlock*>* dominated_blocks(block->dominated_blocks());
+ if (!dominated_blocks->is_empty()) {
+ if (block->IsDeoptimizing()) {
+ for (int i = 0; i < dominated_blocks->length(); ++i) {
+ dominated_blocks->at(i)->MarkAsDeoptimizing();
+ }
+ }
+ for (int i = 1; i < dominated_blocks->length(); ++i) {
+ stack.Add(dominated_blocks->at(i), zone());
+ }
+ block = dominated_blocks->at(0);
+ } else if (!stack.is_empty()) {
+ // Pop next block from stack.
+ block = stack.RemoveLast();
+ } else {
+ // All blocks processed.
+ block = NULL;
+ }
+ }
+}
+
+
+void HPropagateDeoptimizingMarkPhase::NullifyUnreachableInstructions() {
+ if (!FLAG_unreachable_code_elimination) return;
+ for (int i = 0; i < graph()->blocks()->length(); ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ bool nullify = false;
+ const ZoneList<HBasicBlock*>* predecessors = block->predecessors();
+ int predecessors_length = predecessors->length();
+ bool all_predecessors_deoptimizing = (predecessors_length > 0);
+ for (int j = 0; j < predecessors_length; ++j) {
+ if (!predecessors->at(j)->IsDeoptimizing()) {
+ all_predecessors_deoptimizing = false;
+ break;
+ }
+ }
+ if (all_predecessors_deoptimizing) nullify = true;
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ // Leave the basic structure of the graph intact.
+ if (instr->IsBlockEntry()) continue;
+ if (instr->IsControlInstruction()) continue;
+ if (instr->IsSimulate()) continue;
+ if (instr->IsEnterInlined()) continue;
+ if (instr->IsLeaveInlined()) continue;
+ if (nullify) {
+ HInstruction* last_dummy = NULL;
+ for (int j = 0; j < instr->OperandCount(); ++j) {
+ HValue* operand = instr->OperandAt(j);
+ // Insert an HDummyUse for each operand, unless the operand
+ // is an HDummyUse itself. If it's even from the same block,
+ // remember it as a potential replacement for the instruction.
+ if (operand->IsDummyUse()) {
+ if (operand->block() == instr->block() &&
+ last_dummy == NULL) {
+ last_dummy = HInstruction::cast(operand);
+ }
+ continue;
+ }
+ if (operand->IsControlInstruction()) {
+ // Inserting a dummy use for a value that's not defined anywhere
+ // will fail. Some instructions define fake inputs on such
+ // values as control flow dependencies.
+ continue;
+ }
+ HDummyUse* dummy = new(graph()->zone()) HDummyUse(operand);
+ dummy->InsertBefore(instr);
+ last_dummy = dummy;
+ }
+ if (last_dummy == NULL) last_dummy = graph()->GetConstant1();
+ instr->DeleteAndReplaceWith(last_dummy);
+ continue;
+ }
+ if (instr->IsDeoptimize()) {
+ ASSERT(block->IsDeoptimizing());
+ nullify = true;
+ }
+ }
+ }
+}
+
+
+void HPropagateDeoptimizingMarkPhase::Run() {
+ // Skip this phase if there is nothing to be done anyway.
+ if (!graph()->has_soft_deoptimize()) return;
+ MarkAsDeoptimizing();
+ NullifyUnreachableInstructions();
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-deoptimizing-mark.h b/chromium/v8/src/hydrogen-deoptimizing-mark.h
new file mode 100644
index 00000000000..7d6e6e4bda3
--- /dev/null
+++ b/chromium/v8/src/hydrogen-deoptimizing-mark.h
@@ -0,0 +1,56 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_DEOPTIMIZING_MARK_H_
+#define V8_HYDROGEN_DEOPTIMIZING_MARK_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Mark all blocks that are dominated by an unconditional soft deoptimize to
+// prevent code motion across those blocks.
+class HPropagateDeoptimizingMarkPhase : public HPhase {
+ public:
+ explicit HPropagateDeoptimizingMarkPhase(HGraph* graph)
+ : HPhase("H_Propagate deoptimizing mark", graph) { }
+
+ void Run();
+
+ private:
+ void MarkAsDeoptimizing();
+ void NullifyUnreachableInstructions();
+
+ DISALLOW_COPY_AND_ASSIGN(HPropagateDeoptimizingMarkPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_DEOPTIMIZING_MARK_H_
diff --git a/chromium/v8/src/hydrogen-environment-liveness.cc b/chromium/v8/src/hydrogen-environment-liveness.cc
new file mode 100644
index 00000000000..9efa47bd34d
--- /dev/null
+++ b/chromium/v8/src/hydrogen-environment-liveness.cc
@@ -0,0 +1,248 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-environment-liveness.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+HEnvironmentLivenessAnalysisPhase::HEnvironmentLivenessAnalysisPhase(
+ HGraph* graph)
+ : HPhase("H_Environment liveness analysis", graph),
+ block_count_(graph->blocks()->length()),
+ maximum_environment_size_(graph->maximum_environment_size()),
+ live_at_block_start_(block_count_, zone()),
+ first_simulate_(block_count_, zone()),
+ first_simulate_invalid_for_index_(block_count_, zone()),
+ markers_(maximum_environment_size_, zone()),
+ collect_markers_(true),
+ last_simulate_(NULL),
+ went_live_since_last_simulate_(maximum_environment_size_, zone()) {
+ ASSERT(maximum_environment_size_ > 0);
+ for (int i = 0; i < block_count_; ++i) {
+ live_at_block_start_.Add(
+ new(zone()) BitVector(maximum_environment_size_, zone()), zone());
+ first_simulate_.Add(NULL, zone());
+ first_simulate_invalid_for_index_.Add(
+ new(zone()) BitVector(maximum_environment_size_, zone()), zone());
+ }
+}
+
+
+void HEnvironmentLivenessAnalysisPhase::ZapEnvironmentSlot(
+ int index, HSimulate* simulate) {
+ int operand_index = simulate->ToOperandIndex(index);
+ if (operand_index == -1) {
+ simulate->AddAssignedValue(index, graph()->GetConstantUndefined());
+ } else {
+ simulate->SetOperandAt(operand_index, graph()->GetConstantUndefined());
+ }
+}
+
+
+void HEnvironmentLivenessAnalysisPhase::ZapEnvironmentSlotsInSuccessors(
+ HBasicBlock* block, BitVector* live) {
+ // When a value is live in successor A but dead in B, we must
+ // explicitly zap it in B.
+ for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
+ HBasicBlock* successor = it.Current();
+ int successor_id = successor->block_id();
+ BitVector* live_in_successor = live_at_block_start_[successor_id];
+ if (live_in_successor->Equals(*live)) continue;
+ for (int i = 0; i < live->length(); ++i) {
+ if (!live->Contains(i)) continue;
+ if (live_in_successor->Contains(i)) continue;
+ if (first_simulate_invalid_for_index_.at(successor_id)->Contains(i)) {
+ continue;
+ }
+ HSimulate* simulate = first_simulate_.at(successor_id);
+ if (simulate == NULL) continue;
+ ASSERT(simulate->closure().is_identical_to(
+ block->last_environment()->closure()));
+ ZapEnvironmentSlot(i, simulate);
+ }
+ }
+}
+
+
+void HEnvironmentLivenessAnalysisPhase::ZapEnvironmentSlotsForInstruction(
+ HEnvironmentMarker* marker) {
+ if (!marker->CheckFlag(HValue::kEndsLiveRange)) return;
+ HSimulate* simulate = marker->next_simulate();
+ if (simulate != NULL) {
+ ASSERT(simulate->closure().is_identical_to(marker->closure()));
+ ZapEnvironmentSlot(marker->index(), simulate);
+ }
+}
+
+
+void HEnvironmentLivenessAnalysisPhase::UpdateLivenessAtBlockEnd(
+ HBasicBlock* block,
+ BitVector* live) {
+ // Liveness at the end of each block: union of liveness in successors.
+ live->Clear();
+ for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
+ live->Union(*live_at_block_start_[it.Current()->block_id()]);
+ }
+}
+
+
+void HEnvironmentLivenessAnalysisPhase::UpdateLivenessAtInstruction(
+ HInstruction* instr,
+ BitVector* live) {
+ switch (instr->opcode()) {
+ case HValue::kEnvironmentMarker: {
+ HEnvironmentMarker* marker = HEnvironmentMarker::cast(instr);
+ int index = marker->index();
+ if (!live->Contains(index)) {
+ marker->SetFlag(HValue::kEndsLiveRange);
+ } else {
+ marker->ClearFlag(HValue::kEndsLiveRange);
+ }
+ if (!went_live_since_last_simulate_.Contains(index)) {
+ marker->set_next_simulate(last_simulate_);
+ }
+ if (marker->kind() == HEnvironmentMarker::LOOKUP) {
+ live->Add(index);
+ } else {
+ ASSERT(marker->kind() == HEnvironmentMarker::BIND);
+ live->Remove(index);
+ went_live_since_last_simulate_.Add(index);
+ }
+ if (collect_markers_) {
+ // Populate |markers_| list during the first pass.
+ markers_.Add(marker, zone());
+ }
+ break;
+ }
+ case HValue::kLeaveInlined:
+ // No environment values are live at the end of an inlined section.
+ live->Clear();
+ last_simulate_ = NULL;
+
+ // The following ASSERTs guard the assumption used in case
+ // kEnterInlined below:
+ ASSERT(instr->next()->IsSimulate());
+ ASSERT(instr->next()->next()->IsGoto());
+
+ break;
+ case HValue::kEnterInlined: {
+ // Those environment values are live that are live at any return
+ // target block. Here we make use of the fact that the end of an
+ // inline sequence always looks like this: HLeaveInlined, HSimulate,
+ // HGoto (to return_target block), with no environment lookups in
+ // between (see ASSERTs above).
+ HEnterInlined* enter = HEnterInlined::cast(instr);
+ live->Clear();
+ for (int i = 0; i < enter->return_targets()->length(); ++i) {
+ int return_id = enter->return_targets()->at(i)->block_id();
+ // When an AbnormalExit is involved, it can happen that the return
+ // target block doesn't actually exist.
+ if (return_id < live_at_block_start_.length()) {
+ live->Union(*live_at_block_start_[return_id]);
+ }
+ }
+ last_simulate_ = NULL;
+ break;
+ }
+ case HValue::kSimulate:
+ last_simulate_ = HSimulate::cast(instr);
+ went_live_since_last_simulate_.Clear();
+ break;
+ default:
+ break;
+ }
+}
+
+
+void HEnvironmentLivenessAnalysisPhase::Run() {
+ ASSERT(maximum_environment_size_ > 0);
+
+ // Main iteration. Compute liveness of environment slots, and store it
+ // for each block until it doesn't change any more. For efficiency, visit
+ // blocks in reverse order and walk backwards through each block. We
+ // need several iterations to propagate liveness through nested loops.
+ BitVector live(maximum_environment_size_, zone());
+ BitVector worklist(block_count_, zone());
+ for (int i = 0; i < block_count_; ++i) {
+ worklist.Add(i);
+ }
+ while (!worklist.IsEmpty()) {
+ for (int block_id = block_count_ - 1; block_id >= 0; --block_id) {
+ if (!worklist.Contains(block_id)) {
+ continue;
+ }
+ worklist.Remove(block_id);
+ last_simulate_ = NULL;
+
+ HBasicBlock* block = graph()->blocks()->at(block_id);
+ UpdateLivenessAtBlockEnd(block, &live);
+
+ for (HInstruction* instr = block->last(); instr != NULL;
+ instr = instr->previous()) {
+ UpdateLivenessAtInstruction(instr, &live);
+ }
+
+ // Reached the start of the block, do necessary bookkeeping:
+ // store computed information for this block and add predecessors
+ // to the work list as necessary.
+ first_simulate_.Set(block_id, last_simulate_);
+ first_simulate_invalid_for_index_[block_id]->CopyFrom(
+ went_live_since_last_simulate_);
+ if (live_at_block_start_[block_id]->UnionIsChanged(live)) {
+ for (int i = 0; i < block->predecessors()->length(); ++i) {
+ worklist.Add(block->predecessors()->at(i)->block_id());
+ }
+ if (block->IsInlineReturnTarget()) {
+ worklist.Add(block->inlined_entry_block()->block_id());
+ }
+ }
+ }
+ // Only collect bind/lookup instructions during the first pass.
+ collect_markers_ = false;
+ }
+
+ // Analysis finished. Zap dead environment slots.
+ for (int i = 0; i < markers_.length(); ++i) {
+ ZapEnvironmentSlotsForInstruction(markers_[i]);
+ }
+ for (int block_id = block_count_ - 1; block_id >= 0; --block_id) {
+ HBasicBlock* block = graph()->blocks()->at(block_id);
+ UpdateLivenessAtBlockEnd(block, &live);
+ ZapEnvironmentSlotsInSuccessors(block, &live);
+ }
+
+ // Finally, remove the HEnvironment{Bind,Lookup} markers.
+ for (int i = 0; i < markers_.length(); ++i) {
+ markers_[i]->DeleteAndReplaceWith(NULL);
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-environment-liveness.h b/chromium/v8/src/hydrogen-environment-liveness.h
new file mode 100644
index 00000000000..248ec5ce5d7
--- /dev/null
+++ b/chromium/v8/src/hydrogen-environment-liveness.h
@@ -0,0 +1,88 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_ENVIRONMENT_LIVENESS_H_
+#define V8_HYDROGEN_ENVIRONMENT_LIVENESS_H_
+
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Trims live ranges of environment slots by doing explicit liveness analysis.
+// Values in the environment are kept alive by every subsequent LInstruction
+// that is assigned an LEnvironment, which creates register pressure and
+// unnecessary spill slot moves. Therefore it is beneficial to trim the
+// live ranges of environment slots by zapping them with a constant after
+// the last lookup that refers to them.
+// Slots are identified by their index and only affected if whitelisted in
+// HOptimizedGraphBuilder::IsEligibleForEnvironmentLivenessAnalysis().
+class HEnvironmentLivenessAnalysisPhase : public HPhase {
+ public:
+ explicit HEnvironmentLivenessAnalysisPhase(HGraph* graph);
+
+ void Run();
+
+ private:
+ void ZapEnvironmentSlot(int index, HSimulate* simulate);
+ void ZapEnvironmentSlotsInSuccessors(HBasicBlock* block, BitVector* live);
+ void ZapEnvironmentSlotsForInstruction(HEnvironmentMarker* marker);
+ void UpdateLivenessAtBlockEnd(HBasicBlock* block, BitVector* live);
+ void UpdateLivenessAtInstruction(HInstruction* instr, BitVector* live);
+
+ int block_count_;
+
+ // Largest number of local variables in any environment in the graph
+ // (including inlined environments).
+ int maximum_environment_size_;
+
+ // Per-block data. All these lists are indexed by block_id.
+ ZoneList<BitVector*> live_at_block_start_;
+ ZoneList<HSimulate*> first_simulate_;
+ ZoneList<BitVector*> first_simulate_invalid_for_index_;
+
+ // List of all HEnvironmentMarker instructions for quick iteration/deletion.
+ // It is populated during the first pass over the graph, controlled by
+ // |collect_markers_|.
+ ZoneList<HEnvironmentMarker*> markers_;
+ bool collect_markers_;
+
+ // Keeps track of the last simulate seen, as well as the environment slots
+ // for which a new live range has started since (so they must not be zapped
+ // in that simulate when the end of another live range of theirs is found).
+ HSimulate* last_simulate_;
+ BitVector went_live_since_last_simulate_;
+
+ DISALLOW_COPY_AND_ASSIGN(HEnvironmentLivenessAnalysisPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif /* V8_HYDROGEN_ENVIRONMENT_LIVENESS_H_ */
diff --git a/chromium/v8/src/hydrogen-escape-analysis.cc b/chromium/v8/src/hydrogen-escape-analysis.cc
new file mode 100644
index 00000000000..0359678ef95
--- /dev/null
+++ b/chromium/v8/src/hydrogen-escape-analysis.cc
@@ -0,0 +1,296 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-escape-analysis.h"
+
+namespace v8 {
+namespace internal {
+
+
+void HEscapeAnalysisPhase::CollectIfNoEscapingUses(HInstruction* instr) {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+ if (use->HasEscapingOperandAt(it.index())) {
+ if (FLAG_trace_escape_analysis) {
+ PrintF("#%d (%s) escapes through #%d (%s) @%d\n", instr->id(),
+ instr->Mnemonic(), use->id(), use->Mnemonic(), it.index());
+ }
+ return;
+ }
+ }
+ if (FLAG_trace_escape_analysis) {
+ PrintF("#%d (%s) is being captured\n", instr->id(), instr->Mnemonic());
+ }
+ captured_.Add(instr, zone());
+}
+
+
+void HEscapeAnalysisPhase::CollectCapturedValues() {
+ int block_count = graph()->blocks()->length();
+ for (int i = 0; i < block_count; ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ if (instr->IsAllocate()) {
+ CollectIfNoEscapingUses(instr);
+ }
+ }
+ }
+}
+
+
+HCapturedObject* HEscapeAnalysisPhase::NewState(HInstruction* previous) {
+ Zone* zone = graph()->zone();
+ HCapturedObject* state = new(zone) HCapturedObject(number_of_values_, zone);
+ state->InsertAfter(previous);
+ return state;
+}
+
+
+// Create a new state for replacing HAllocate instructions.
+HCapturedObject* HEscapeAnalysisPhase::NewStateForAllocation(
+ HInstruction* previous) {
+ HConstant* undefined = graph()->GetConstantUndefined();
+ HCapturedObject* state = NewState(previous);
+ for (int index = 0; index < number_of_values_; index++) {
+ state->SetOperandAt(index, undefined);
+ }
+ return state;
+}
+
+
+// Create a new state full of phis for loop header entries.
+HCapturedObject* HEscapeAnalysisPhase::NewStateForLoopHeader(
+ HInstruction* previous, HCapturedObject* old_state) {
+ HBasicBlock* block = previous->block();
+ HCapturedObject* state = NewState(previous);
+ for (int index = 0; index < number_of_values_; index++) {
+ HValue* operand = old_state->OperandAt(index);
+ HPhi* phi = NewPhiAndInsert(block, operand, index);
+ state->SetOperandAt(index, phi);
+ }
+ return state;
+}
+
+
+// Create a new state by copying an existing one.
+HCapturedObject* HEscapeAnalysisPhase::NewStateCopy(
+ HInstruction* previous, HCapturedObject* old_state) {
+ HCapturedObject* state = NewState(previous);
+ for (int index = 0; index < number_of_values_; index++) {
+ HValue* operand = old_state->OperandAt(index);
+ state->SetOperandAt(index, operand);
+ }
+ return state;
+}
+
+
+// Insert a newly created phi into the given block and fill all incoming
+// edges with the given value.
+HPhi* HEscapeAnalysisPhase::NewPhiAndInsert(
+ HBasicBlock* block, HValue* incoming_value, int index) {
+ Zone* zone = graph()->zone();
+ HPhi* phi = new(zone) HPhi(HPhi::kInvalidMergedIndex, zone);
+ for (int i = 0; i < block->predecessors()->length(); i++) {
+ phi->AddInput(incoming_value);
+ }
+ block->AddPhi(phi);
+ return phi;
+}
+
+
+// Performs a forward data-flow analysis of all loads and stores on the
+// given captured allocation. This uses a reverse post-order iteration
+// over affected basic blocks. All non-escaping instructions are handled
+// and replaced during the analysis.
+void HEscapeAnalysisPhase::AnalyzeDataFlow(HInstruction* allocate) {
+ HBasicBlock* allocate_block = allocate->block();
+ block_states_.AddBlock(NULL, graph()->blocks()->length(), zone());
+
+ // Iterate all blocks starting with the allocation block, since the
+ // allocation cannot dominate blocks that come before.
+ int start = allocate_block->block_id();
+ for (int i = start; i < graph()->blocks()->length(); i++) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ HCapturedObject* state = StateAt(block);
+
+ // Skip blocks that are not dominated by the captured allocation.
+ if (!allocate_block->Dominates(block) && allocate_block != block) continue;
+ if (FLAG_trace_escape_analysis) {
+ PrintF("Analyzing data-flow in B%d\n", block->block_id());
+ }
+
+ // Go through all instructions of the current block.
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ switch (instr->opcode()) {
+ case HValue::kAllocate: {
+ if (instr != allocate) continue;
+ state = NewStateForAllocation(allocate);
+ break;
+ }
+ case HValue::kLoadNamedField: {
+ HLoadNamedField* load = HLoadNamedField::cast(instr);
+ int index = load->access().offset() / kPointerSize;
+ if (load->object() != allocate) continue;
+ ASSERT(load->access().IsInobject());
+ HValue* replacement = state->OperandAt(index);
+ load->DeleteAndReplaceWith(replacement);
+ if (FLAG_trace_escape_analysis) {
+ PrintF("Replacing load #%d with #%d (%s)\n", instr->id(),
+ replacement->id(), replacement->Mnemonic());
+ }
+ break;
+ }
+ case HValue::kStoreNamedField: {
+ HStoreNamedField* store = HStoreNamedField::cast(instr);
+ int index = store->access().offset() / kPointerSize;
+ if (store->object() != allocate) continue;
+ ASSERT(store->access().IsInobject());
+ state = NewStateCopy(store, state);
+ state->SetOperandAt(index, store->value());
+ if (store->has_transition()) {
+ state->SetOperandAt(0, store->transition());
+ }
+ store->DeleteAndReplaceWith(NULL);
+ if (FLAG_trace_escape_analysis) {
+ PrintF("Replacing store #%d%s\n", instr->id(),
+ store->has_transition() ? " (with transition)" : "");
+ }
+ break;
+ }
+ case HValue::kSimulate: {
+ HSimulate* simulate = HSimulate::cast(instr);
+ // TODO(mstarzinger): This doesn't track deltas for values on the
+ // operand stack yet. Find a repro test case and fix this.
+ for (int i = 0; i < simulate->OperandCount(); i++) {
+ if (simulate->OperandAt(i) != allocate) continue;
+ simulate->SetOperandAt(i, state);
+ }
+ break;
+ }
+ case HValue::kArgumentsObject:
+ case HValue::kCapturedObject: {
+ for (int i = 0; i < instr->OperandCount(); i++) {
+ if (instr->OperandAt(i) != allocate) continue;
+ instr->SetOperandAt(i, state);
+ }
+ break;
+ }
+ case HValue::kCheckHeapObject: {
+ HCheckHeapObject* check = HCheckHeapObject::cast(instr);
+ if (check->value() != allocate) continue;
+ check->DeleteAndReplaceWith(NULL);
+ break;
+ }
+ case HValue::kCheckMaps: {
+ HCheckMaps* mapcheck = HCheckMaps::cast(instr);
+ if (mapcheck->value() != allocate) continue;
+ // TODO(mstarzinger): This approach breaks if the tracked map value
+ // is not a HConstant. Find a repro test case and fix this.
+ for (HUseIterator it(mapcheck->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->IsLoadNamedField()) continue;
+ HLoadNamedField* load = HLoadNamedField::cast(it.value());
+ ASSERT(load->typecheck() == mapcheck);
+ load->ClearTypeCheck();
+ }
+ ASSERT(mapcheck->HasNoUses());
+
+ mapcheck->DeleteAndReplaceWith(NULL);
+ break;
+ }
+ default:
+ // Nothing to see here, move along ...
+ break;
+ }
+ }
+
+ // Propagate the block state forward to all successor blocks.
+ for (int i = 0; i < block->end()->SuccessorCount(); i++) {
+ HBasicBlock* succ = block->end()->SuccessorAt(i);
+ if (!allocate_block->Dominates(succ)) continue;
+ if (succ->predecessors()->length() == 1) {
+ // Case 1: This is the only predecessor, just reuse state.
+ SetStateAt(succ, state);
+ } else if (StateAt(succ) == NULL && succ->IsLoopHeader()) {
+ // Case 2: This is a state that enters a loop header, be
+ // pessimistic about loop headers, add phis for all values.
+ SetStateAt(succ, NewStateForLoopHeader(succ->first(), state));
+ } else if (StateAt(succ) == NULL) {
+ // Case 3: This is the first state propagated forward to the
+ // successor, leave a copy of the current state.
+ SetStateAt(succ, NewStateCopy(succ->first(), state));
+ } else {
+ // Case 4: This is a state that needs merging with previously
+ // propagated states, potentially introducing new phis lazily or
+ // adding values to existing phis.
+ HCapturedObject* succ_state = StateAt(succ);
+ for (int index = 0; index < number_of_values_; index++) {
+ HValue* operand = state->OperandAt(index);
+ HValue* succ_operand = succ_state->OperandAt(index);
+ if (succ_operand->IsPhi() && succ_operand->block() == succ) {
+ // Phi already exists, add operand.
+ HPhi* phi = HPhi::cast(succ_operand);
+ phi->SetOperandAt(succ->PredecessorIndexOf(block), operand);
+ } else if (succ_operand != operand) {
+ // Phi does not exist, introduce one.
+ HPhi* phi = NewPhiAndInsert(succ, succ_operand, index);
+ phi->SetOperandAt(succ->PredecessorIndexOf(block), operand);
+ succ_state->SetOperandAt(index, phi);
+ }
+ }
+ }
+ }
+ }
+
+ // All uses have been handled.
+ ASSERT(allocate->HasNoUses());
+ allocate->DeleteAndReplaceWith(NULL);
+}
+
+
+void HEscapeAnalysisPhase::PerformScalarReplacement() {
+ for (int i = 0; i < captured_.length(); i++) {
+ HAllocate* allocate = HAllocate::cast(captured_.at(i));
+
+ // Compute number of scalar values and start with clean slate.
+ if (!allocate->size()->IsInteger32Constant()) continue;
+ int size_in_bytes = allocate->size()->GetInteger32Constant();
+ number_of_values_ = size_in_bytes / kPointerSize;
+ block_states_.Clear();
+
+ // Perform actual analysis steps.
+ AnalyzeDataFlow(allocate);
+
+ cumulative_values_ += number_of_values_;
+ ASSERT(allocate->HasNoUses());
+ ASSERT(!allocate->IsLinked());
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-escape-analysis.h b/chromium/v8/src/hydrogen-escape-analysis.h
new file mode 100644
index 00000000000..123da214e34
--- /dev/null
+++ b/chromium/v8/src/hydrogen-escape-analysis.h
@@ -0,0 +1,88 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_ESCAPE_ANALYSIS_H_
+#define V8_HYDROGEN_ESCAPE_ANALYSIS_H_
+
+#include "allocation.h"
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HEscapeAnalysisPhase : public HPhase {
+ public:
+ explicit HEscapeAnalysisPhase(HGraph* graph)
+ : HPhase("H_Escape analysis", graph),
+ captured_(0, zone()),
+ number_of_values_(0),
+ cumulative_values_(0),
+ block_states_(graph->blocks()->length(), zone()) { }
+
+ void Run() {
+ CollectCapturedValues();
+ PerformScalarReplacement();
+ }
+
+ private:
+ void CollectCapturedValues();
+ void CollectIfNoEscapingUses(HInstruction* instr);
+ void PerformScalarReplacement();
+ void AnalyzeDataFlow(HInstruction* instr);
+
+ HCapturedObject* NewState(HInstruction* prev);
+ HCapturedObject* NewStateForAllocation(HInstruction* prev);
+ HCapturedObject* NewStateForLoopHeader(HInstruction* prev, HCapturedObject*);
+ HCapturedObject* NewStateCopy(HInstruction* prev, HCapturedObject* state);
+
+ HPhi* NewPhiAndInsert(HBasicBlock* block, HValue* incoming_value, int index);
+
+ HCapturedObject* StateAt(HBasicBlock* block) {
+ return block_states_.at(block->block_id());
+ }
+
+ void SetStateAt(HBasicBlock* block, HCapturedObject* state) {
+ block_states_.Set(block->block_id(), state);
+ }
+
+ // List of allocations captured during collection phase.
+ ZoneList<HInstruction*> captured_;
+
+ // Number of scalar values tracked during scalar replacement phase.
+ int number_of_values_;
+ int cumulative_values_;
+
+ // Map of block IDs to the data-flow state at block entry during the
+ // scalar replacement phase.
+ ZoneList<HCapturedObject*> block_states_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_ESCAPE_ANALYSIS_H_
diff --git a/chromium/v8/src/hydrogen-gvn.cc b/chromium/v8/src/hydrogen-gvn.cc
new file mode 100644
index 00000000000..9a02a1dcf4b
--- /dev/null
+++ b/chromium/v8/src/hydrogen-gvn.cc
@@ -0,0 +1,855 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen.h"
+#include "hydrogen-gvn.h"
+#include "v8.h"
+
+namespace v8 {
+namespace internal {
+
+class HValueMap: public ZoneObject {
+ public:
+ explicit HValueMap(Zone* zone)
+ : array_size_(0),
+ lists_size_(0),
+ count_(0),
+ present_flags_(0),
+ array_(NULL),
+ lists_(NULL),
+ free_list_head_(kNil) {
+ ResizeLists(kInitialSize, zone);
+ Resize(kInitialSize, zone);
+ }
+
+ void Kill(GVNFlagSet flags);
+
+ void Add(HValue* value, Zone* zone) {
+ present_flags_.Add(value->gvn_flags());
+ Insert(value, zone);
+ }
+
+ HValue* Lookup(HValue* value) const;
+
+ HValueMap* Copy(Zone* zone) const {
+ return new(zone) HValueMap(zone, this);
+ }
+
+ bool IsEmpty() const { return count_ == 0; }
+
+ private:
+ // A linked list of HValue* values. Stored in arrays.
+ struct HValueMapListElement {
+ HValue* value;
+ int next; // Index in the array of the next list element.
+ };
+ static const int kNil = -1; // The end of a linked list
+
+ // Must be a power of 2.
+ static const int kInitialSize = 16;
+
+ HValueMap(Zone* zone, const HValueMap* other);
+
+ void Resize(int new_size, Zone* zone);
+ void ResizeLists(int new_size, Zone* zone);
+ void Insert(HValue* value, Zone* zone);
+ uint32_t Bound(uint32_t value) const { return value & (array_size_ - 1); }
+
+ int array_size_;
+ int lists_size_;
+ int count_; // The number of values stored 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.
+ int free_list_head_; // Unused elements in lists_ are on the free list.
+};
+
+
+class HSideEffectMap BASE_EMBEDDED {
+ public:
+ HSideEffectMap();
+ explicit HSideEffectMap(HSideEffectMap* other);
+ HSideEffectMap& operator= (const HSideEffectMap& other);
+
+ void Kill(GVNFlagSet flags);
+
+ void Store(GVNFlagSet flags, HInstruction* instr);
+
+ bool IsEmpty() const { return count_ == 0; }
+
+ inline HInstruction* operator[](int i) const {
+ ASSERT(0 <= i);
+ ASSERT(i < kNumberOfTrackedSideEffects);
+ return data_[i];
+ }
+ inline HInstruction* at(int i) const { return operator[](i); }
+
+ private:
+ int count_;
+ HInstruction* data_[kNumberOfTrackedSideEffects];
+};
+
+
+void TraceGVN(const char* msg, ...) {
+ va_list arguments;
+ va_start(arguments, msg);
+ OS::VPrint(msg, arguments);
+ va_end(arguments);
+}
+
+
+// Wrap TraceGVN in macros to avoid the expense of evaluating its arguments when
+// --trace-gvn is off.
+#define TRACE_GVN_1(msg, a1) \
+ if (FLAG_trace_gvn) { \
+ TraceGVN(msg, a1); \
+ }
+
+#define TRACE_GVN_2(msg, a1, a2) \
+ if (FLAG_trace_gvn) { \
+ TraceGVN(msg, a1, a2); \
+ }
+
+#define TRACE_GVN_3(msg, a1, a2, a3) \
+ if (FLAG_trace_gvn) { \
+ TraceGVN(msg, a1, a2, a3); \
+ }
+
+#define TRACE_GVN_4(msg, a1, a2, a3, a4) \
+ if (FLAG_trace_gvn) { \
+ TraceGVN(msg, a1, a2, a3, a4); \
+ }
+
+#define TRACE_GVN_5(msg, a1, a2, a3, a4, a5) \
+ if (FLAG_trace_gvn) { \
+ TraceGVN(msg, a1, a2, a3, a4, a5); \
+ }
+
+
+HValueMap::HValueMap(Zone* zone, const HValueMap* other)
+ : array_size_(other->array_size_),
+ lists_size_(other->lists_size_),
+ count_(other->count_),
+ present_flags_(other->present_flags_),
+ array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
+ lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
+ free_list_head_(other->free_list_head_) {
+ OS::MemCopy(
+ array_, other->array_, array_size_ * sizeof(HValueMapListElement));
+ OS::MemCopy(
+ lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
+}
+
+
+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) {
+ // Clear list of collisions first, so we know if it becomes empty.
+ int kept = kNil; // List of kept elements.
+ int next;
+ for (int current = array_[i].next; current != kNil; current = next) {
+ next = lists_[current].next;
+ HValue* value = lists_[current].value;
+ if (value->gvn_flags().ContainsAnyOf(depends_flags)) {
+ // Drop it.
+ count_--;
+ lists_[current].next = free_list_head_;
+ free_list_head_ = current;
+ } else {
+ // Keep it.
+ lists_[current].next = kept;
+ kept = current;
+ present_flags_.Add(value->gvn_flags());
+ }
+ }
+ array_[i].next = kept;
+
+ // Now possibly drop directly indexed element.
+ value = array_[i].value;
+ if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it.
+ count_--;
+ int head = array_[i].next;
+ if (head == kNil) {
+ array_[i].value = NULL;
+ } else {
+ array_[i].value = lists_[head].value;
+ array_[i].next = lists_[head].next;
+ lists_[head].next = free_list_head_;
+ free_list_head_ = head;
+ }
+ } else {
+ present_flags_.Add(value->gvn_flags()); // Keep it.
+ }
+ }
+ }
+}
+
+
+HValue* HValueMap::Lookup(HValue* value) const {
+ uint32_t hash = static_cast<uint32_t>(value->Hashcode());
+ uint32_t pos = Bound(hash);
+ if (array_[pos].value != NULL) {
+ if (array_[pos].value->Equals(value)) return array_[pos].value;
+ int next = array_[pos].next;
+ while (next != kNil) {
+ if (lists_[next].value->Equals(value)) return lists_[next].value;
+ next = lists_[next].next;
+ }
+ }
+ return NULL;
+}
+
+
+void HValueMap::Resize(int new_size, Zone* zone) {
+ ASSERT(new_size > count_);
+ // Hashing the values into the new array has no more collisions than in the
+ // old hash map, so we can use the existing lists_ array, if we are careful.
+
+ // Make sure we have at least one free element.
+ if (free_list_head_ == kNil) {
+ ResizeLists(lists_size_ << 1, zone);
+ }
+
+ HValueMapListElement* new_array =
+ zone->NewArray<HValueMapListElement>(new_size);
+ memset(new_array, 0, sizeof(HValueMapListElement) * new_size);
+
+ HValueMapListElement* old_array = array_;
+ int old_size = array_size_;
+
+ int old_count = count_;
+ count_ = 0;
+ // Do not modify present_flags_. It is currently correct.
+ array_size_ = new_size;
+ array_ = new_array;
+
+ if (old_array != NULL) {
+ // Iterate over all the elements in lists, rehashing them.
+ for (int i = 0; i < old_size; ++i) {
+ if (old_array[i].value != NULL) {
+ int current = old_array[i].next;
+ while (current != kNil) {
+ Insert(lists_[current].value, zone);
+ int next = lists_[current].next;
+ lists_[current].next = free_list_head_;
+ free_list_head_ = current;
+ current = next;
+ }
+ // Rehash the directly stored value.
+ Insert(old_array[i].value, zone);
+ }
+ }
+ }
+ USE(old_count);
+ ASSERT(count_ == old_count);
+}
+
+
+void HValueMap::ResizeLists(int new_size, Zone* zone) {
+ ASSERT(new_size > lists_size_);
+
+ HValueMapListElement* new_lists =
+ zone->NewArray<HValueMapListElement>(new_size);
+ memset(new_lists, 0, sizeof(HValueMapListElement) * new_size);
+
+ HValueMapListElement* old_lists = lists_;
+ int old_size = lists_size_;
+
+ lists_size_ = new_size;
+ lists_ = new_lists;
+
+ if (old_lists != NULL) {
+ OS::MemCopy(lists_, old_lists, old_size * sizeof(HValueMapListElement));
+ }
+ for (int i = old_size; i < lists_size_; ++i) {
+ lists_[i].next = free_list_head_;
+ free_list_head_ = i;
+ }
+}
+
+
+void HValueMap::Insert(HValue* value, Zone* zone) {
+ ASSERT(value != NULL);
+ // Resizing when half of the hashtable is filled up.
+ if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone);
+ ASSERT(count_ < array_size_);
+ count_++;
+ uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode()));
+ if (array_[pos].value == NULL) {
+ array_[pos].value = value;
+ array_[pos].next = kNil;
+ } else {
+ if (free_list_head_ == kNil) {
+ ResizeLists(lists_size_ << 1, zone);
+ }
+ int new_element_pos = free_list_head_;
+ ASSERT(new_element_pos != kNil);
+ free_list_head_ = lists_[free_list_head_].next;
+ lists_[new_element_pos].value = value;
+ lists_[new_element_pos].next = array_[pos].next;
+ ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL);
+ array_[pos].next = new_element_pos;
+ }
+}
+
+
+HSideEffectMap::HSideEffectMap() : count_(0) {
+ memset(data_, 0, kNumberOfTrackedSideEffects * kPointerSize);
+}
+
+
+HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) {
+ *this = *other; // Calls operator=.
+}
+
+
+HSideEffectMap& HSideEffectMap::operator= (const HSideEffectMap& other) {
+ if (this != &other) {
+ OS::MemCopy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize);
+ }
+ return *this;
+}
+
+
+void HSideEffectMap::Kill(GVNFlagSet flags) {
+ for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
+ GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
+ if (flags.Contains(changes_flag)) {
+ if (data_[i] != NULL) count_--;
+ data_[i] = NULL;
+ }
+ }
+}
+
+
+void HSideEffectMap::Store(GVNFlagSet flags, HInstruction* instr) {
+ for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
+ GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
+ if (flags.Contains(changes_flag)) {
+ if (data_[i] == NULL) count_++;
+ data_[i] = instr;
+ }
+ }
+}
+
+
+HGlobalValueNumberingPhase::HGlobalValueNumberingPhase(HGraph* graph)
+ : HPhase("H_Global value numbering", graph),
+ removed_side_effects_(false),
+ block_side_effects_(graph->blocks()->length(), zone()),
+ loop_side_effects_(graph->blocks()->length(), zone()),
+ visited_on_paths_(graph->blocks()->length(), zone()) {
+ ASSERT(!AllowHandleAllocation::IsAllowed());
+ block_side_effects_.AddBlock(GVNFlagSet(), graph->blocks()->length(),
+ zone());
+ loop_side_effects_.AddBlock(GVNFlagSet(), graph->blocks()->length(),
+ zone());
+ }
+
+void HGlobalValueNumberingPhase::Analyze() {
+ removed_side_effects_ = false;
+ ComputeBlockSideEffects();
+ if (FLAG_loop_invariant_code_motion) {
+ LoopInvariantCodeMotion();
+ }
+ AnalyzeGraph();
+}
+
+
+void HGlobalValueNumberingPhase::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);
+ int id = block->block_id();
+ GVNFlagSet side_effects;
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ side_effects.Add(instr->ChangesFlags());
+ if (instr->IsDeoptimize()) {
+ block_side_effects_[id].RemoveAll();
+ side_effects.RemoveAll();
+ break;
+ }
+ }
+ block_side_effects_[id].Add(side_effects);
+
+ // Loop headers are part of their loop.
+ if (block->IsLoopHeader()) {
+ 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].Add(block->IsLoopHeader()
+ ? loop_side_effects_[id]
+ : side_effects);
+ }
+ }
+}
+
+
+SmartArrayPointer<char> GetGVNFlagsString(GVNFlagSet flags) {
+ char underlying_buffer[kLastFlag * 128];
+ Vector<char> buffer(underlying_buffer, sizeof(underlying_buffer));
+#if DEBUG
+ int offset = 0;
+ const char* separator = "";
+ const char* comma = ", ";
+ buffer[0] = 0;
+ uint32_t set_depends_on = 0;
+ uint32_t set_changes = 0;
+ for (int bit = 0; bit < kLastFlag; ++bit) {
+ if ((flags.ToIntegral() & (1 << bit)) != 0) {
+ if (bit % 2 == 0) {
+ set_changes++;
+ } else {
+ set_depends_on++;
+ }
+ }
+ }
+ bool positive_changes = set_changes < (kLastFlag / 2);
+ bool positive_depends_on = set_depends_on < (kLastFlag / 2);
+ if (set_changes > 0) {
+ if (positive_changes) {
+ offset += OS::SNPrintF(buffer + offset, "changes [");
+ } else {
+ offset += OS::SNPrintF(buffer + offset, "changes all except [");
+ }
+ for (int bit = 0; bit < kLastFlag; ++bit) {
+ if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_changes) {
+ switch (static_cast<GVNFlag>(bit)) {
+#define DECLARE_FLAG(type) \
+ case kChanges##type: \
+ offset += OS::SNPrintF(buffer + offset, separator); \
+ offset += OS::SNPrintF(buffer + offset, #type); \
+ separator = comma; \
+ break;
+GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
+GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
+#undef DECLARE_FLAG
+ default:
+ break;
+ }
+ }
+ }
+ offset += OS::SNPrintF(buffer + offset, "]");
+ }
+ if (set_depends_on > 0) {
+ separator = "";
+ if (set_changes > 0) {
+ offset += OS::SNPrintF(buffer + offset, ", ");
+ }
+ if (positive_depends_on) {
+ offset += OS::SNPrintF(buffer + offset, "depends on [");
+ } else {
+ offset += OS::SNPrintF(buffer + offset, "depends on all except [");
+ }
+ for (int bit = 0; bit < kLastFlag; ++bit) {
+ if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_depends_on) {
+ switch (static_cast<GVNFlag>(bit)) {
+#define DECLARE_FLAG(type) \
+ case kDependsOn##type: \
+ offset += OS::SNPrintF(buffer + offset, separator); \
+ offset += OS::SNPrintF(buffer + offset, #type); \
+ separator = comma; \
+ break;
+GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
+GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
+#undef DECLARE_FLAG
+ default:
+ break;
+ }
+ }
+ }
+ offset += OS::SNPrintF(buffer + offset, "]");
+ }
+#else
+ OS::SNPrintF(buffer, "0x%08X", flags.ToIntegral());
+#endif
+ size_t string_len = strlen(underlying_buffer) + 1;
+ ASSERT(string_len <= sizeof(underlying_buffer));
+ char* result = new char[strlen(underlying_buffer) + 1];
+ OS::MemCopy(result, underlying_buffer, string_len);
+ return SmartArrayPointer<char>(result);
+}
+
+
+void HGlobalValueNumberingPhase::LoopInvariantCodeMotion() {
+ TRACE_GVN_1("Using optimistic loop invariant code motion: %s\n",
+ graph()->use_optimistic_licm() ? "yes" : "no");
+ for (int i = graph()->blocks()->length() - 1; i >= 0; --i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ if (block->IsLoopHeader()) {
+ GVNFlagSet side_effects = loop_side_effects_[block->block_id()];
+ TRACE_GVN_2("Try loop invariant motion for block B%d %s\n",
+ block->block_id(),
+ *GetGVNFlagsString(side_effects));
+
+ GVNFlagSet accumulated_first_time_depends;
+ GVNFlagSet accumulated_first_time_changes;
+ 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,
+ &accumulated_first_time_depends,
+ &accumulated_first_time_changes);
+ }
+ }
+ }
+}
+
+
+void HGlobalValueNumberingPhase::ProcessLoopBlock(
+ HBasicBlock* block,
+ HBasicBlock* loop_header,
+ GVNFlagSet loop_kills,
+ GVNFlagSet* first_time_depends,
+ GVNFlagSet* first_time_changes) {
+ HBasicBlock* pre_header = loop_header->predecessors()->at(0);
+ GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
+ TRACE_GVN_2("Loop invariant motion for B%d %s\n",
+ block->block_id(),
+ *GetGVNFlagsString(depends_flags));
+ HInstruction* instr = block->first();
+ while (instr != NULL) {
+ HInstruction* next = instr->next();
+ bool hoisted = false;
+ if (instr->CheckFlag(HValue::kUseGVN)) {
+ TRACE_GVN_4("Checking instruction %d (%s) %s. Loop %s\n",
+ instr->id(),
+ instr->Mnemonic(),
+ *GetGVNFlagsString(instr->gvn_flags()),
+ *GetGVNFlagsString(loop_kills));
+ bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags);
+ if (can_hoist && !graph()->use_optimistic_licm()) {
+ can_hoist = block->IsLoopSuccessorDominator();
+ }
+
+ 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)) {
+ TRACE_GVN_1("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.
+ GVNFlagSet previous_depends = *first_time_depends;
+ GVNFlagSet previous_changes = *first_time_changes;
+ first_time_depends->Add(instr->DependsOnFlags());
+ first_time_changes->Add(instr->ChangesFlags());
+ if (!(previous_depends == *first_time_depends)) {
+ TRACE_GVN_1("Updated first-time accumulated %s\n",
+ *GetGVNFlagsString(*first_time_depends));
+ }
+ if (!(previous_changes == *first_time_changes)) {
+ TRACE_GVN_1("Updated first-time accumulated %s\n",
+ *GetGVNFlagsString(*first_time_changes));
+ }
+ }
+ instr = next;
+ }
+}
+
+
+bool HGlobalValueNumberingPhase::AllowCodeMotion() {
+ return info()->IsStub() || info()->opt_count() + 1 < FLAG_max_opt_count;
+}
+
+
+bool HGlobalValueNumberingPhase::ShouldMove(HInstruction* instr,
+ HBasicBlock* loop_header) {
+ // If we've disabled code motion or we're in a block that unconditionally
+ // deoptimizes, don't move any instructions.
+ return AllowCodeMotion() && !instr->block()->IsDeoptimizing();
+}
+
+
+GVNFlagSet
+HGlobalValueNumberingPhase::CollectSideEffectsOnPathsToDominatedBlock(
+ HBasicBlock* dominator, HBasicBlock* dominated) {
+ 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_.Contains(block->block_id())) {
+ visited_on_paths_.Add(block->block_id());
+ side_effects.Add(block_side_effects_[block->block_id()]);
+ if (block->IsLoopHeader()) {
+ side_effects.Add(loop_side_effects_[block->block_id()]);
+ }
+ side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock(
+ dominator, block));
+ }
+ }
+ return side_effects;
+}
+
+
+// Each instance of this class is like a "stack frame" for the recursive
+// traversal of the dominator tree done during GVN (the stack is handled
+// as a double linked list).
+// We reuse frames when possible so the list length is limited by the depth
+// of the dominator tree but this forces us to initialize each frame calling
+// an explicit "Initialize" method instead of a using constructor.
+class GvnBasicBlockState: public ZoneObject {
+ public:
+ static GvnBasicBlockState* CreateEntry(Zone* zone,
+ HBasicBlock* entry_block,
+ HValueMap* entry_map) {
+ return new(zone)
+ GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone);
+ }
+
+ HBasicBlock* block() { return block_; }
+ HValueMap* map() { return map_; }
+ HSideEffectMap* dominators() { return &dominators_; }
+
+ GvnBasicBlockState* next_in_dominator_tree_traversal(
+ Zone* zone,
+ HBasicBlock** dominator) {
+ // This assignment needs to happen before calling next_dominated() because
+ // that call can reuse "this" if we are at the last dominated block.
+ *dominator = block();
+ GvnBasicBlockState* result = next_dominated(zone);
+ if (result == NULL) {
+ GvnBasicBlockState* dominator_state = pop();
+ if (dominator_state != NULL) {
+ // This branch is guaranteed not to return NULL because pop() never
+ // returns a state where "is_done() == true".
+ *dominator = dominator_state->block();
+ result = dominator_state->next_dominated(zone);
+ } else {
+ // Unnecessary (we are returning NULL) but done for cleanness.
+ *dominator = NULL;
+ }
+ }
+ return result;
+ }
+
+ private:
+ void Initialize(HBasicBlock* block,
+ HValueMap* map,
+ HSideEffectMap* dominators,
+ bool copy_map,
+ Zone* zone) {
+ block_ = block;
+ map_ = copy_map ? map->Copy(zone) : map;
+ dominated_index_ = -1;
+ length_ = block->dominated_blocks()->length();
+ if (dominators != NULL) {
+ dominators_ = *dominators;
+ }
+ }
+ bool is_done() { return dominated_index_ >= length_; }
+
+ GvnBasicBlockState(GvnBasicBlockState* previous,
+ HBasicBlock* block,
+ HValueMap* map,
+ HSideEffectMap* dominators,
+ Zone* zone)
+ : previous_(previous), next_(NULL) {
+ Initialize(block, map, dominators, true, zone);
+ }
+
+ GvnBasicBlockState* next_dominated(Zone* zone) {
+ dominated_index_++;
+ if (dominated_index_ == length_ - 1) {
+ // No need to copy the map for the last child in the dominator tree.
+ Initialize(block_->dominated_blocks()->at(dominated_index_),
+ map(),
+ dominators(),
+ false,
+ zone);
+ return this;
+ } else if (dominated_index_ < length_) {
+ return push(zone, block_->dominated_blocks()->at(dominated_index_));
+ } else {
+ return NULL;
+ }
+ }
+
+ GvnBasicBlockState* push(Zone* zone, HBasicBlock* block) {
+ if (next_ == NULL) {
+ next_ =
+ new(zone) GvnBasicBlockState(this, block, map(), dominators(), zone);
+ } else {
+ next_->Initialize(block, map(), dominators(), true, zone);
+ }
+ return next_;
+ }
+ GvnBasicBlockState* pop() {
+ GvnBasicBlockState* result = previous_;
+ while (result != NULL && result->is_done()) {
+ TRACE_GVN_2("Backtracking from block B%d to block b%d\n",
+ block()->block_id(),
+ previous_->block()->block_id())
+ result = result->previous_;
+ }
+ return result;
+ }
+
+ GvnBasicBlockState* previous_;
+ GvnBasicBlockState* next_;
+ HBasicBlock* block_;
+ HValueMap* map_;
+ HSideEffectMap dominators_;
+ int dominated_index_;
+ int length_;
+};
+
+
+// This is a recursive traversal of the dominator tree but it has been turned
+// into a loop to avoid stack overflows.
+// The logical "stack frames" of the recursion are kept in a list of
+// GvnBasicBlockState instances.
+void HGlobalValueNumberingPhase::AnalyzeGraph() {
+ HBasicBlock* entry_block = graph()->entry_block();
+ HValueMap* entry_map = new(zone()) HValueMap(zone());
+ GvnBasicBlockState* current =
+ GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map);
+
+ while (current != NULL) {
+ HBasicBlock* block = current->block();
+ HValueMap* map = current->map();
+ HSideEffectMap* dominators = current->dominators();
+
+ TRACE_GVN_2("Analyzing block B%d%s\n",
+ block->block_id(),
+ block->IsLoopHeader() ? " (loop header)" : "");
+
+ // If this is a loop header kill everything killed by the loop.
+ if (block->IsLoopHeader()) {
+ map->Kill(loop_side_effects_[block->block_id()]);
+ dominators->Kill(loop_side_effects_[block->block_id()]);
+ }
+
+ // Go through all instructions of the current block.
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* instr = it.Current();
+ if (instr->CheckFlag(HValue::kTrackSideEffectDominators)) {
+ for (int i = 0; i < kNumberOfTrackedSideEffects; i++) {
+ HValue* other = dominators->at(i);
+ GVNFlag changes_flag = HValue::ChangesFlagFromInt(i);
+ GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i);
+ if (instr->DependsOnFlags().Contains(depends_on_flag) &&
+ (other != NULL)) {
+ TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n",
+ i,
+ instr->id(),
+ instr->Mnemonic(),
+ other->id(),
+ other->Mnemonic());
+ instr->HandleSideEffectDominator(changes_flag, other);
+ }
+ }
+ }
+ // Instruction was unlinked during graph traversal.
+ if (!instr->IsLinked()) continue;
+
+ GVNFlagSet flags = instr->ChangesFlags();
+ if (!flags.IsEmpty()) {
+ // Clear all instructions in the map that are affected by side effects.
+ // Store instruction as the dominating one for tracked side effects.
+ map->Kill(flags);
+ dominators->Store(flags, instr);
+ TRACE_GVN_2("Instruction %d %s\n", instr->id(),
+ *GetGVNFlagsString(flags));
+ }
+ if (instr->CheckFlag(HValue::kUseGVN)) {
+ ASSERT(!instr->HasObservableSideEffects());
+ HValue* other = map->Lookup(instr);
+ if (other != NULL) {
+ ASSERT(instr->Equals(other) && other->Equals(instr));
+ TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n",
+ instr->id(),
+ instr->Mnemonic(),
+ other->id(),
+ other->Mnemonic());
+ if (instr->HasSideEffects()) removed_side_effects_ = true;
+ instr->DeleteAndReplaceWith(other);
+ } else {
+ map->Add(instr, zone());
+ }
+ }
+ }
+
+ HBasicBlock* dominator_block;
+ GvnBasicBlockState* next =
+ current->next_in_dominator_tree_traversal(zone(),
+ &dominator_block);
+
+ if (next != NULL) {
+ HBasicBlock* dominated = next->block();
+ HValueMap* successor_map = next->map();
+ HSideEffectMap* successor_dominators = next->dominators();
+
+ // Kill everything killed on any path between this block and the
+ // dominated block. We don't have to traverse these paths if the
+ // value map and the dominators list is already empty. If the range
+ // of block ids (block_id, dominated_id) is empty there are no such
+ // paths.
+ if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) &&
+ dominator_block->block_id() + 1 < dominated->block_id()) {
+ visited_on_paths_.Clear();
+ GVNFlagSet side_effects_on_all_paths =
+ CollectSideEffectsOnPathsToDominatedBlock(dominator_block,
+ dominated);
+ successor_map->Kill(side_effects_on_all_paths);
+ successor_dominators->Kill(side_effects_on_all_paths);
+ }
+ }
+ current = next;
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-gvn.h b/chromium/v8/src/hydrogen-gvn.h
new file mode 100644
index 00000000000..fdbad99c6bb
--- /dev/null
+++ b/chromium/v8/src/hydrogen-gvn.h
@@ -0,0 +1,89 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_GVN_H_
+#define V8_HYDROGEN_GVN_H_
+
+#include "hydrogen.h"
+#include "hydrogen-instructions.h"
+#include "compiler.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+// Perform common subexpression elimination and loop-invariant code motion.
+class HGlobalValueNumberingPhase : public HPhase {
+ public:
+ explicit HGlobalValueNumberingPhase(HGraph* graph);
+
+ void Run() {
+ 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_) {
+ Analyze();
+ // TODO(danno): Turn this into a fixpoint iteration.
+ }
+ }
+
+ private:
+ void Analyze();
+ GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock(
+ HBasicBlock* dominator,
+ HBasicBlock* dominated);
+ void AnalyzeGraph();
+ void ComputeBlockSideEffects();
+ void LoopInvariantCodeMotion();
+ void ProcessLoopBlock(HBasicBlock* block,
+ HBasicBlock* before_loop,
+ GVNFlagSet loop_kills,
+ GVNFlagSet* accumulated_first_time_depends,
+ GVNFlagSet* accumulated_first_time_changes);
+ bool AllowCodeMotion();
+ bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
+
+ bool removed_side_effects_;
+
+ // A map of block IDs to their side effects.
+ ZoneList<GVNFlagSet> block_side_effects_;
+
+ // A map of loop header block IDs to their loop's side effects.
+ ZoneList<GVNFlagSet> loop_side_effects_;
+
+ // Used when collecting side effects on paths from dominator to
+ // dominated.
+ BitVector visited_on_paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(HGlobalValueNumberingPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_GVN_H_
diff --git a/chromium/v8/src/hydrogen-infer-representation.cc b/chromium/v8/src/hydrogen-infer-representation.cc
new file mode 100644
index 00000000000..95c341285cc
--- /dev/null
+++ b/chromium/v8/src/hydrogen-infer-representation.cc
@@ -0,0 +1,172 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-infer-representation.h"
+
+namespace v8 {
+namespace internal {
+
+void HInferRepresentationPhase::AddToWorklist(HValue* current) {
+ if (current->representation().IsTagged()) return;
+ if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return;
+ if (in_worklist_.Contains(current->id())) return;
+ worklist_.Add(current, zone());
+ in_worklist_.Add(current->id());
+}
+
+
+void HInferRepresentationPhase::Run() {
+ // (1) Initialize bit vectors and count real uses. Each phi gets a
+ // bit-vector of length <number of phis>.
+ const ZoneList<HPhi*>* phi_list = graph()->phi_list();
+ int phi_count = phi_list->length();
+ ZoneList<BitVector*> connected_phis(phi_count, zone());
+ for (int i = 0; i < phi_count; ++i) {
+ phi_list->at(i)->InitRealUses(i);
+ BitVector* connected_set = new(zone()) BitVector(phi_count, zone());
+ connected_set->Add(i);
+ connected_phis.Add(connected_set, zone());
+ }
+
+ // (2) Do a fixed point iteration to find the set of connected phis. A
+ // phi is connected to another phi if its value is used either directly or
+ // indirectly through a transitive closure of the def-use relation.
+ bool change = true;
+ while (change) {
+ change = false;
+ // We normally have far more "forward edges" than "backward edges",
+ // so we terminate faster when we walk backwards.
+ for (int i = phi_count - 1; i >= 0; --i) {
+ HPhi* phi = phi_list->at(i);
+ for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+ if (use->IsPhi()) {
+ int id = HPhi::cast(use)->phi_id();
+ if (connected_phis[i]->UnionIsChanged(*connected_phis[id]))
+ change = true;
+ }
+ }
+ }
+ }
+
+ // Set truncation flags for groups of connected phis. This is a conservative
+ // approximation; the flag will be properly re-computed after representations
+ // have been determined.
+ if (phi_count > 0) {
+ BitVector done(phi_count, zone());
+ for (int i = 0; i < phi_count; ++i) {
+ if (done.Contains(i)) continue;
+
+ // Check if all uses of all connected phis in this group are truncating.
+ bool all_uses_everywhere_truncating = true;
+ for (BitVector::Iterator it(connected_phis[i]);
+ !it.Done();
+ it.Advance()) {
+ int index = it.Current();
+ all_uses_everywhere_truncating &=
+ phi_list->at(index)->CheckFlag(HInstruction::kTruncatingToInt32);
+ done.Add(index);
+ }
+ if (all_uses_everywhere_truncating) {
+ continue; // Great, nothing to do.
+ }
+ // Clear truncation flag of this group of connected phis.
+ for (BitVector::Iterator it(connected_phis[i]);
+ !it.Done();
+ it.Advance()) {
+ int index = it.Current();
+ phi_list->at(index)->ClearFlag(HInstruction::kTruncatingToInt32);
+ }
+ }
+ }
+
+ // Simplify constant phi inputs where possible.
+ // This step uses kTruncatingToInt32 flags of phis.
+ for (int i = 0; i < phi_count; ++i) {
+ phi_list->at(i)->SimplifyConstantInputs();
+ }
+
+ // Use the phi reachability information from step 2 to
+ // sum up the non-phi use counts of all connected phis.
+ for (int i = 0; i < phi_count; ++i) {
+ HPhi* phi = phi_list->at(i);
+ for (BitVector::Iterator it(connected_phis[i]);
+ !it.Done();
+ it.Advance()) {
+ int index = it.Current();
+ HPhi* it_use = phi_list->at(index);
+ if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice.
+ }
+ }
+
+ // Initialize work list
+ for (int i = 0; i < graph()->blocks()->length(); ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ const ZoneList<HPhi*>* phis = block->phis();
+ for (int j = 0; j < phis->length(); ++j) {
+ AddToWorklist(phis->at(j));
+ }
+
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ AddToWorklist(current);
+ }
+ }
+
+ // Do a fixed point iteration, trying to improve representations
+ while (!worklist_.is_empty()) {
+ HValue* current = worklist_.RemoveLast();
+ in_worklist_.Remove(current->id());
+ current->InferRepresentation(this);
+ }
+
+ // Lastly: any instruction that we don't have representation information
+ // for defaults to Tagged.
+ for (int i = 0; i < graph()->blocks()->length(); ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ const ZoneList<HPhi*>* phis = block->phis();
+ for (int j = 0; j < phis->length(); ++j) {
+ HPhi* phi = phis->at(j);
+ if (phi->representation().IsNone()) {
+ phi->ChangeRepresentation(Representation::Tagged());
+ }
+ }
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->representation().IsNone() &&
+ current->CheckFlag(HInstruction::kFlexibleRepresentation)) {
+ if (current->CheckFlag(HInstruction::kCannotBeTagged)) {
+ current->ChangeRepresentation(Representation::Double());
+ } else {
+ current->ChangeRepresentation(Representation::Tagged());
+ }
+ }
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-infer-representation.h b/chromium/v8/src/hydrogen-infer-representation.h
new file mode 100644
index 00000000000..7c605696c4b
--- /dev/null
+++ b/chromium/v8/src/hydrogen-infer-representation.h
@@ -0,0 +1,57 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_INFER_REPRESENTATION_H_
+#define V8_HYDROGEN_INFER_REPRESENTATION_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HInferRepresentationPhase : public HPhase {
+ public:
+ explicit HInferRepresentationPhase(HGraph* graph)
+ : HPhase("H_Infer representations", graph),
+ worklist_(8, zone()),
+ in_worklist_(graph->GetMaximumValueID(), zone()) { }
+
+ void Run();
+ void AddToWorklist(HValue* current);
+
+ private:
+ ZoneList<HValue*> worklist_;
+ BitVector in_worklist_;
+
+ DISALLOW_COPY_AND_ASSIGN(HInferRepresentationPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_INFER_REPRESENTATION_H_
diff --git a/chromium/v8/src/hydrogen-infer-types.cc b/chromium/v8/src/hydrogen-infer-types.cc
new file mode 100644
index 00000000000..01c60847367
--- /dev/null
+++ b/chromium/v8/src/hydrogen-infer-types.cc
@@ -0,0 +1,77 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-infer-types.h"
+
+namespace v8 {
+namespace internal {
+
+void HInferTypesPhase::InferTypes(int from_inclusive, int to_inclusive) {
+ for (int i = from_inclusive; i <= to_inclusive; ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+
+ const ZoneList<HPhi*>* phis = block->phis();
+ for (int j = 0; j < phis->length(); j++) {
+ phis->at(j)->UpdateInferredType();
+ }
+
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ it.Current()->UpdateInferredType();
+ }
+
+ if (block->IsLoopHeader()) {
+ HBasicBlock* last_back_edge =
+ block->loop_information()->GetLastBackEdge();
+ InferTypes(i + 1, last_back_edge->block_id());
+ // Skip all blocks already processed by the recursive call.
+ i = last_back_edge->block_id();
+ // Update phis of the loop header now after the whole loop body is
+ // guaranteed to be processed.
+ for (int j = 0; j < block->phis()->length(); ++j) {
+ HPhi* phi = block->phis()->at(j);
+ worklist_.Add(phi, zone());
+ in_worklist_.Add(phi->id());
+ }
+ while (!worklist_.is_empty()) {
+ HValue* current = worklist_.RemoveLast();
+ in_worklist_.Remove(current->id());
+ if (current->UpdateInferredType()) {
+ for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+ if (!in_worklist_.Contains(use->id())) {
+ in_worklist_.Add(use->id());
+ worklist_.Add(use, zone());
+ }
+ }
+ }
+ }
+ ASSERT(in_worklist_.IsEmpty());
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-infer-types.h b/chromium/v8/src/hydrogen-infer-types.h
new file mode 100644
index 00000000000..cfcbf3549ba
--- /dev/null
+++ b/chromium/v8/src/hydrogen-infer-types.h
@@ -0,0 +1,59 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_INFER_TYPES_H_
+#define V8_HYDROGEN_INFER_TYPES_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HInferTypesPhase : public HPhase {
+ public:
+ explicit HInferTypesPhase(HGraph* graph)
+ : HPhase("H_Inferring types", graph), worklist_(8, zone()),
+ in_worklist_(graph->GetMaximumValueID(), zone()) { }
+
+ void Run() {
+ InferTypes(0, graph()->blocks()->length() - 1);
+ }
+
+ private:
+ void InferTypes(int from_inclusive, int to_inclusive);
+
+ ZoneList<HValue*> worklist_;
+ BitVector in_worklist_;
+
+ DISALLOW_COPY_AND_ASSIGN(HInferTypesPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_INFER_TYPES_H_
diff --git a/chromium/v8/src/hydrogen-instructions.cc b/chromium/v8/src/hydrogen-instructions.cc
new file mode 100644
index 00000000000..a4c54e761e7
--- /dev/null
+++ b/chromium/v8/src/hydrogen-instructions.cc
@@ -0,0 +1,4006 @@
+// Copyright 2012 the V8 project authors. 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 "double.h"
+#include "factory.h"
+#include "hydrogen-infer-representation.h"
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/lithium-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/lithium-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/lithium-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/lithium-mips.h"
+#else
+#error Unsupported target architecture.
+#endif
+
+namespace v8 {
+namespace internal {
+
+#define DEFINE_COMPILE(type) \
+ LInstruction* H##type::CompileToLithium(LChunkBuilder* builder) { \
+ return builder->Do##type(this); \
+ }
+HYDROGEN_CONCRETE_INSTRUCTION_LIST(DEFINE_COMPILE)
+#undef DEFINE_COMPILE
+
+
+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))];
+}
+
+
+Isolate* HValue::isolate() const {
+ ASSERT(block() != NULL);
+ return block()->isolate();
+}
+
+
+void HValue::AssumeRepresentation(Representation r) {
+ if (CheckFlag(kFlexibleRepresentation)) {
+ ChangeRepresentation(r);
+ // The representation of the value is dictated by type feedback and
+ // will not be changed later.
+ ClearFlag(kFlexibleRepresentation);
+ }
+}
+
+
+void HValue::InferRepresentation(HInferRepresentationPhase* h_infer) {
+ ASSERT(CheckFlag(kFlexibleRepresentation));
+ Representation new_rep = RepresentationFromInputs();
+ UpdateRepresentation(new_rep, h_infer, "inputs");
+ new_rep = RepresentationFromUses();
+ UpdateRepresentation(new_rep, h_infer, "uses");
+ if (representation().IsSmi() && HasNonSmiUse()) {
+ UpdateRepresentation(
+ Representation::Integer32(), h_infer, "use requirements");
+ }
+}
+
+
+Representation HValue::RepresentationFromUses() {
+ if (HasNoUses()) return Representation::None();
+
+ // Array of use counts for each representation.
+ int use_count[Representation::kNumRepresentations] = { 0 };
+
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+ Representation rep = use->observed_input_representation(it.index());
+ if (rep.IsNone()) continue;
+ if (FLAG_trace_representation) {
+ PrintF("#%d %s is used by #%d %s as %s%s\n",
+ id(), Mnemonic(), use->id(), use->Mnemonic(), rep.Mnemonic(),
+ (use->CheckFlag(kTruncatingToInt32) ? "-trunc" : ""));
+ }
+ use_count[rep.kind()] += use->LoopWeight();
+ }
+ if (IsPhi()) HPhi::cast(this)->AddIndirectUsesTo(&use_count[0]);
+ int tagged_count = use_count[Representation::kTagged];
+ int double_count = use_count[Representation::kDouble];
+ int int32_count = use_count[Representation::kInteger32];
+ int smi_count = use_count[Representation::kSmi];
+
+ if (tagged_count > 0) return Representation::Tagged();
+ if (double_count > 0) return Representation::Double();
+ if (int32_count > 0) return Representation::Integer32();
+ if (smi_count > 0) return Representation::Smi();
+
+ return Representation::None();
+}
+
+
+void HValue::UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ Representation r = representation();
+ if (new_rep.is_more_general_than(r)) {
+ if (CheckFlag(kCannotBeTagged) && new_rep.IsTagged()) return;
+ if (FLAG_trace_representation) {
+ PrintF("Changing #%d %s representation %s -> %s based on %s\n",
+ id(), Mnemonic(), r.Mnemonic(), new_rep.Mnemonic(), reason);
+ }
+ ChangeRepresentation(new_rep);
+ AddDependantsToWorklist(h_infer);
+ }
+}
+
+
+void HValue::AddDependantsToWorklist(HInferRepresentationPhase* h_infer) {
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ h_infer->AddToWorklist(it.value());
+ }
+ for (int i = 0; i < OperandCount(); ++i) {
+ h_infer->AddToWorklist(OperandAt(i));
+ }
+}
+
+
+static int32_t ConvertAndSetOverflow(Representation r,
+ int64_t result,
+ bool* overflow) {
+ if (r.IsSmi()) {
+ if (result > Smi::kMaxValue) {
+ *overflow = true;
+ return Smi::kMaxValue;
+ }
+ if (result < Smi::kMinValue) {
+ *overflow = true;
+ return Smi::kMinValue;
+ }
+ } else {
+ if (result > kMaxInt) {
+ *overflow = true;
+ return kMaxInt;
+ }
+ if (result < kMinInt) {
+ *overflow = true;
+ return kMinInt;
+ }
+ }
+ return static_cast<int32_t>(result);
+}
+
+
+static int32_t AddWithoutOverflow(Representation r,
+ int32_t a,
+ int32_t b,
+ bool* overflow) {
+ int64_t result = static_cast<int64_t>(a) + static_cast<int64_t>(b);
+ return ConvertAndSetOverflow(r, result, overflow);
+}
+
+
+static int32_t SubWithoutOverflow(Representation r,
+ int32_t a,
+ int32_t b,
+ bool* overflow) {
+ int64_t result = static_cast<int64_t>(a) - static_cast<int64_t>(b);
+ return ConvertAndSetOverflow(r, result, overflow);
+}
+
+
+static int32_t MulWithoutOverflow(const Representation& r,
+ int32_t a,
+ int32_t b,
+ bool* overflow) {
+ int64_t result = static_cast<int64_t>(a) * static_cast<int64_t>(b);
+ return ConvertAndSetOverflow(r, result, overflow);
+}
+
+
+int32_t Range::Mask() const {
+ if (lower_ == upper_) return lower_;
+ if (lower_ >= 0) {
+ int32_t res = 1;
+ while (res < upper_) {
+ res = (res << 1) | 1;
+ }
+ return res;
+ }
+ return 0xffffffff;
+}
+
+
+void Range::AddConstant(int32_t value) {
+ if (value == 0) return;
+ bool may_overflow = false; // Overflow is ignored here.
+ Representation r = Representation::Integer32();
+ lower_ = AddWithoutOverflow(r, lower_, value, &may_overflow);
+ upper_ = AddWithoutOverflow(r, upper_, value, &may_overflow);
+#ifdef DEBUG
+ Verify();
+#endif
+}
+
+
+void Range::Intersect(Range* other) {
+ upper_ = Min(upper_, other->upper_);
+ lower_ = Max(lower_, other->lower_);
+ bool b = CanBeMinusZero() && other->CanBeMinusZero();
+ set_can_be_minus_zero(b);
+}
+
+
+void Range::Union(Range* other) {
+ upper_ = Max(upper_, other->upper_);
+ lower_ = Min(lower_, other->lower_);
+ bool b = CanBeMinusZero() || other->CanBeMinusZero();
+ set_can_be_minus_zero(b);
+}
+
+
+void Range::CombinedMax(Range* other) {
+ upper_ = Max(upper_, other->upper_);
+ lower_ = Max(lower_, other->lower_);
+ set_can_be_minus_zero(CanBeMinusZero() || other->CanBeMinusZero());
+}
+
+
+void Range::CombinedMin(Range* other) {
+ upper_ = Min(upper_, other->upper_);
+ lower_ = Min(lower_, other->lower_);
+ set_can_be_minus_zero(CanBeMinusZero() || other->CanBeMinusZero());
+}
+
+
+void Range::Sar(int32_t value) {
+ int32_t bits = value & 0x1F;
+ lower_ = lower_ >> bits;
+ upper_ = upper_ >> bits;
+ set_can_be_minus_zero(false);
+}
+
+
+void Range::Shl(int32_t value) {
+ int32_t bits = value & 0x1F;
+ int old_lower = lower_;
+ int old_upper = upper_;
+ lower_ = lower_ << bits;
+ upper_ = upper_ << bits;
+ if (old_lower != lower_ >> bits || old_upper != upper_ >> bits) {
+ upper_ = kMaxInt;
+ lower_ = kMinInt;
+ }
+ set_can_be_minus_zero(false);
+}
+
+
+bool Range::AddAndCheckOverflow(const Representation& r, Range* other) {
+ bool may_overflow = false;
+ lower_ = AddWithoutOverflow(r, lower_, other->lower(), &may_overflow);
+ upper_ = AddWithoutOverflow(r, upper_, other->upper(), &may_overflow);
+ KeepOrder();
+#ifdef DEBUG
+ Verify();
+#endif
+ return may_overflow;
+}
+
+
+bool Range::SubAndCheckOverflow(const Representation& r, Range* other) {
+ bool may_overflow = false;
+ lower_ = SubWithoutOverflow(r, lower_, other->upper(), &may_overflow);
+ upper_ = SubWithoutOverflow(r, upper_, other->lower(), &may_overflow);
+ KeepOrder();
+#ifdef DEBUG
+ Verify();
+#endif
+ return may_overflow;
+}
+
+
+void Range::KeepOrder() {
+ if (lower_ > upper_) {
+ int32_t tmp = lower_;
+ lower_ = upper_;
+ upper_ = tmp;
+ }
+}
+
+
+#ifdef DEBUG
+void Range::Verify() const {
+ ASSERT(lower_ <= upper_);
+}
+#endif
+
+
+bool Range::MulAndCheckOverflow(const Representation& r, Range* other) {
+ bool may_overflow = false;
+ int v1 = MulWithoutOverflow(r, lower_, other->lower(), &may_overflow);
+ int v2 = MulWithoutOverflow(r, lower_, other->upper(), &may_overflow);
+ int v3 = MulWithoutOverflow(r, upper_, other->lower(), &may_overflow);
+ int v4 = MulWithoutOverflow(r, 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;
+}
+
+
+const char* HType::ToString() {
+ // Note: The c1visualizer syntax for locals allows only a sequence of the
+ // following characters: A-Za-z0-9_-|:
+ switch (type_) {
+ case kNone: return "none";
+ case kTagged: return "tagged";
+ case kTaggedPrimitive: return "primitive";
+ case kTaggedNumber: return "number";
+ case kSmi: return "smi";
+ case kHeapNumber: return "heap-number";
+ case kString: return "string";
+ case kBoolean: return "boolean";
+ case kNonPrimitive: return "non-primitive";
+ case kJSArray: return "array";
+ case kJSObject: return "object";
+ }
+ UNREACHABLE();
+ return "unreachable";
+}
+
+
+HType HType::TypeFromValue(Handle<Object> value) {
+ HType result = HType::Tagged();
+ if (value->IsSmi()) {
+ result = HType::Smi();
+ } else if (value->IsHeapNumber()) {
+ result = HType::HeapNumber();
+ } else if (value->IsString()) {
+ result = HType::String();
+ } else if (value->IsBoolean()) {
+ result = HType::Boolean();
+ } else if (value->IsJSObject()) {
+ result = HType::JSObject();
+ } else if (value->IsJSArray()) {
+ result = HType::JSArray();
+ }
+ return result;
+}
+
+
+bool HValue::IsDefinedAfter(HBasicBlock* other) const {
+ return block()->block_id() > other->block_id();
+}
+
+
+HUseListNode* HUseListNode::tail() {
+ // Skip and remove dead items in the use list.
+ while (tail_ != NULL && tail_->value()->CheckFlag(HValue::kIsDead)) {
+ tail_ = tail_->tail_;
+ }
+ return tail_;
+}
+
+
+bool HValue::CheckUsesForFlag(Flag f) const {
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ if (it.value()->IsSimulate()) continue;
+ if (!it.value()->CheckFlag(f)) return false;
+ }
+ return true;
+}
+
+
+bool HValue::HasAtLeastOneUseWithFlagAndNoneWithout(Flag f) const {
+ bool return_value = false;
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ if (it.value()->IsSimulate()) continue;
+ if (!it.value()->CheckFlag(f)) return false;
+ return_value = true;
+ }
+ return return_value;
+}
+
+
+HUseIterator::HUseIterator(HUseListNode* head) : next_(head) {
+ Advance();
+}
+
+
+void HUseIterator::Advance() {
+ current_ = next_;
+ if (current_ != NULL) {
+ next_ = current_->tail();
+ value_ = current_->value();
+ index_ = current_->index();
+ }
+}
+
+
+int HValue::UseCount() const {
+ int count = 0;
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) ++count;
+ return count;
+}
+
+
+HUseListNode* HValue::RemoveUse(HValue* value, int index) {
+ HUseListNode* previous = NULL;
+ HUseListNode* current = use_list_;
+ while (current != NULL) {
+ if (current->value() == value && current->index() == index) {
+ if (previous == NULL) {
+ use_list_ = current->tail();
+ } else {
+ previous->set_tail(current->tail());
+ }
+ break;
+ }
+
+ previous = current;
+ current = current->tail();
+ }
+
+#ifdef DEBUG
+ // Do not reuse use list nodes in debug mode, zap them.
+ if (current != NULL) {
+ HUseListNode* temp =
+ new(block()->zone())
+ HUseListNode(current->value(), current->index(), NULL);
+ current->Zap();
+ current = temp;
+ }
+#endif
+ return current;
+}
+
+
+bool HValue::Equals(HValue* other) {
+ if (other->opcode() != opcode()) return false;
+ if (!other->representation().Equals(representation())) return false;
+ if (!other->type_.Equals(type_)) return false;
+ if (other->flags() != flags()) return false;
+ if (OperandCount() != other->OperandCount()) return false;
+ for (int i = 0; i < OperandCount(); ++i) {
+ if (OperandAt(i)->id() != other->OperandAt(i)->id()) return false;
+ }
+ bool result = DataEquals(other);
+ ASSERT(!result || Hashcode() == other->Hashcode());
+ return result;
+}
+
+
+intptr_t HValue::Hashcode() {
+ intptr_t result = opcode();
+ int count = OperandCount();
+ for (int i = 0; i < count; ++i) {
+ result = result * 19 + OperandAt(i)->id() + (result >> 7);
+ }
+ return result;
+}
+
+
+const char* HValue::Mnemonic() const {
+ switch (opcode()) {
+#define MAKE_CASE(type) case k##type: return #type;
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(MAKE_CASE)
+#undef MAKE_CASE
+ case kPhi: return "Phi";
+ default: return "";
+ }
+}
+
+
+bool HValue::IsInteger32Constant() {
+ return IsConstant() && HConstant::cast(this)->HasInteger32Value();
+}
+
+
+int32_t HValue::GetInteger32Constant() {
+ return HConstant::cast(this)->Integer32Value();
+}
+
+
+bool HValue::EqualsInteger32Constant(int32_t value) {
+ return IsInteger32Constant() && GetInteger32Constant() == value;
+}
+
+
+void HValue::SetOperandAt(int index, HValue* value) {
+ RegisterUse(index, value);
+ InternalSetOperandAt(index, value);
+}
+
+
+void HValue::DeleteAndReplaceWith(HValue* other) {
+ // We replace all uses first, so Delete can assert that there are none.
+ if (other != NULL) ReplaceAllUsesWith(other);
+ Kill();
+ DeleteFromGraph();
+}
+
+
+void HValue::ReplaceAllUsesWith(HValue* other) {
+ while (use_list_ != NULL) {
+ HUseListNode* list_node = use_list_;
+ HValue* value = list_node->value();
+ ASSERT(!value->block()->IsStartBlock());
+ value->InternalSetOperandAt(list_node->index(), other);
+ use_list_ = list_node->tail();
+ list_node->set_tail(other->use_list_);
+ other->use_list_ = list_node;
+ }
+}
+
+
+void HValue::Kill() {
+ // Instead of going through the entire use list of each operand, we only
+ // check the first item in each use list and rely on the tail() method to
+ // skip dead items, removing them lazily next time we traverse the list.
+ SetFlag(kIsDead);
+ for (int i = 0; i < OperandCount(); ++i) {
+ HValue* operand = OperandAt(i);
+ if (operand == NULL) continue;
+ HUseListNode* first = operand->use_list_;
+ if (first != NULL && first->value()->CheckFlag(kIsDead)) {
+ operand->use_list_ = first->tail();
+ }
+ }
+}
+
+
+void HValue::SetBlock(HBasicBlock* block) {
+ ASSERT(block_ == NULL || block == NULL);
+ block_ = block;
+ if (id_ == kNoNumber && block != NULL) {
+ id_ = block->graph()->GetNextValueID(this);
+ }
+}
+
+
+void HValue::PrintTypeTo(StringStream* stream) {
+ if (!representation().IsTagged() || type().Equals(HType::Tagged())) return;
+ stream->Add(" type:%s", type().ToString());
+}
+
+
+void HValue::PrintRangeTo(StringStream* stream) {
+ if (range() == NULL || range()->IsMostGeneric()) return;
+ // Note: The c1visualizer syntax for locals allows only a sequence of the
+ // following characters: A-Za-z0-9_-|:
+ stream->Add(" range:%d_%d%s",
+ range()->lower(),
+ range()->upper(),
+ range()->CanBeMinusZero() ? "_m0" : "");
+}
+
+
+void HValue::PrintChangesTo(StringStream* stream) {
+ GVNFlagSet changes_flags = ChangesFlags();
+ if (changes_flags.IsEmpty()) return;
+ stream->Add(" changes[");
+ if (changes_flags == AllSideEffectsFlagSet()) {
+ stream->Add("*");
+ } else {
+ bool add_comma = false;
+#define PRINT_DO(type) \
+ if (changes_flags.Contains(kChanges##type)) { \
+ if (add_comma) stream->Add(","); \
+ add_comma = true; \
+ stream->Add(#type); \
+ }
+ GVN_TRACKED_FLAG_LIST(PRINT_DO);
+ GVN_UNTRACKED_FLAG_LIST(PRINT_DO);
+#undef PRINT_DO
+ }
+ stream->Add("]");
+}
+
+
+void HValue::PrintNameTo(StringStream* stream) {
+ stream->Add("%s%d", representation_.Mnemonic(), id());
+}
+
+
+bool HValue::HasMonomorphicJSObjectType() {
+ return !GetMonomorphicJSObjectMap().is_null();
+}
+
+
+bool HValue::UpdateInferredType() {
+ HType type = CalculateInferredType();
+ bool result = (!type.Equals(type_));
+ type_ = type;
+ return result;
+}
+
+
+void HValue::RegisterUse(int index, HValue* new_value) {
+ HValue* old_value = OperandAt(index);
+ if (old_value == new_value) return;
+
+ HUseListNode* removed = NULL;
+ if (old_value != NULL) {
+ removed = old_value->RemoveUse(this, index);
+ }
+
+ if (new_value != NULL) {
+ if (removed == NULL) {
+ new_value->use_list_ = new(new_value->block()->zone()) HUseListNode(
+ this, index, new_value->use_list_);
+ } else {
+ removed->set_tail(new_value->use_list_);
+ new_value->use_list_ = removed;
+ }
+ }
+}
+
+
+void HValue::AddNewRange(Range* r, Zone* zone) {
+ if (!HasRange()) ComputeInitialRange(zone);
+ if (!HasRange()) range_ = new(zone) Range();
+ ASSERT(HasRange());
+ r->StackUpon(range_);
+ range_ = r;
+}
+
+
+void HValue::RemoveLastAddedRange() {
+ ASSERT(HasRange());
+ ASSERT(range_->next() != NULL);
+ range_ = range_->next();
+}
+
+
+void HValue::ComputeInitialRange(Zone* zone) {
+ ASSERT(!HasRange());
+ range_ = InferRange(zone);
+ ASSERT(HasRange());
+}
+
+
+void HInstruction::PrintTo(StringStream* stream) {
+ PrintMnemonicTo(stream);
+ PrintDataTo(stream);
+ PrintRangeTo(stream);
+ PrintChangesTo(stream);
+ PrintTypeTo(stream);
+ if (CheckFlag(HValue::kHasNoObservableSideEffects)) {
+ stream->Add(" [noOSE]");
+ }
+}
+
+
+void HInstruction::PrintDataTo(StringStream *stream) {
+ for (int i = 0; i < OperandCount(); ++i) {
+ if (i > 0) stream->Add(" ");
+ OperandAt(i)->PrintNameTo(stream);
+ }
+}
+
+
+void HInstruction::PrintMnemonicTo(StringStream* stream) {
+ stream->Add("%s ", Mnemonic());
+}
+
+
+void HInstruction::Unlink() {
+ ASSERT(IsLinked());
+ ASSERT(!IsControlInstruction()); // Must never move control instructions.
+ ASSERT(!IsBlockEntry()); // Doesn't make sense to delete these.
+ ASSERT(previous_ != NULL);
+ previous_->next_ = next_;
+ if (next_ == NULL) {
+ ASSERT(block()->last() == this);
+ block()->set_last(previous_);
+ } else {
+ next_->previous_ = previous_;
+ }
+ clear_block();
+}
+
+
+void HInstruction::InsertBefore(HInstruction* next) {
+ ASSERT(!IsLinked());
+ ASSERT(!next->IsBlockEntry());
+ ASSERT(!IsControlInstruction());
+ ASSERT(!next->block()->IsStartBlock());
+ ASSERT(next->previous_ != NULL);
+ HInstruction* prev = next->previous();
+ prev->next_ = this;
+ next->previous_ = this;
+ next_ = next;
+ previous_ = prev;
+ SetBlock(next->block());
+}
+
+
+void HInstruction::InsertAfter(HInstruction* previous) {
+ ASSERT(!IsLinked());
+ ASSERT(!previous->IsControlInstruction());
+ ASSERT(!IsControlInstruction() || previous->next_ == NULL);
+ HBasicBlock* block = previous->block();
+ // Never insert anything except constants into the start block after finishing
+ // it.
+ if (block->IsStartBlock() && block->IsFinished() && !IsConstant()) {
+ ASSERT(block->end()->SecondSuccessor() == NULL);
+ InsertAfter(block->end()->FirstSuccessor()->first());
+ return;
+ }
+
+ // If we're inserting after an instruction with side-effects that is
+ // followed by a simulate instruction, we need to insert after the
+ // simulate instruction instead.
+ HInstruction* next = previous->next_;
+ if (previous->HasObservableSideEffects() && next != NULL) {
+ ASSERT(next->IsSimulate());
+ previous = next;
+ next = previous->next_;
+ }
+
+ previous_ = previous;
+ next_ = next;
+ SetBlock(block);
+ previous->next_ = this;
+ if (next != NULL) next->previous_ = this;
+ if (block->last() == previous) {
+ block->set_last(this);
+ }
+}
+
+
+#ifdef DEBUG
+void HInstruction::Verify() {
+ // Verify that input operands are defined before use.
+ HBasicBlock* cur_block = block();
+ for (int i = 0; i < OperandCount(); ++i) {
+ HValue* other_operand = OperandAt(i);
+ if (other_operand == NULL) continue;
+ HBasicBlock* other_block = other_operand->block();
+ if (cur_block == other_block) {
+ if (!other_operand->IsPhi()) {
+ HInstruction* cur = this->previous();
+ while (cur != NULL) {
+ if (cur == other_operand) break;
+ cur = cur->previous();
+ }
+ // Must reach other operand in the same block!
+ ASSERT(cur == other_operand);
+ }
+ } else {
+ // If the following assert fires, you may have forgotten an
+ // AddInstruction.
+ ASSERT(other_block->Dominates(cur_block));
+ }
+ }
+
+ // Verify that instructions that may have side-effects are followed
+ // by a simulate instruction.
+ if (HasObservableSideEffects() && !IsOsrEntry()) {
+ ASSERT(next()->IsSimulate());
+ }
+
+ // Verify that instructions that can be eliminated by GVN have overridden
+ // HValue::DataEquals. The default implementation is UNREACHABLE. We
+ // don't actually care whether DataEquals returns true or false here.
+ if (CheckFlag(kUseGVN)) DataEquals(this);
+
+ // Verify that all uses are in the graph.
+ for (HUseIterator use = uses(); !use.Done(); use.Advance()) {
+ if (use.value()->IsInstruction()) {
+ ASSERT(HInstruction::cast(use.value())->IsLinked());
+ }
+ }
+}
+#endif
+
+
+void HDummyUse::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+}
+
+
+void HEnvironmentMarker::PrintDataTo(StringStream* stream) {
+ stream->Add("%s var[%d]", kind() == BIND ? "bind" : "lookup", index());
+}
+
+
+void HUnaryCall::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ stream->Add(" ");
+ stream->Add("#%d", argument_count());
+}
+
+
+void HBinaryCall::PrintDataTo(StringStream* stream) {
+ first()->PrintNameTo(stream);
+ stream->Add(" ");
+ second()->PrintNameTo(stream);
+ stream->Add(" ");
+ stream->Add("#%d", argument_count());
+}
+
+
+void HBoundsCheck::ApplyIndexChange() {
+ if (skip_check()) return;
+
+ DecompositionResult decomposition;
+ bool index_is_decomposable = index()->TryDecompose(&decomposition);
+ if (index_is_decomposable) {
+ ASSERT(decomposition.base() == base());
+ if (decomposition.offset() == offset() &&
+ decomposition.scale() == scale()) return;
+ } else {
+ return;
+ }
+
+ ReplaceAllUsesWith(index());
+
+ HValue* current_index = decomposition.base();
+ int actual_offset = decomposition.offset() + offset();
+ int actual_scale = decomposition.scale() + scale();
+
+ Zone* zone = block()->graph()->zone();
+ HValue* context = block()->graph()->GetInvalidContext();
+ if (actual_offset != 0) {
+ HConstant* add_offset = HConstant::New(zone, context, actual_offset);
+ add_offset->InsertBefore(this);
+ HInstruction* add = HAdd::New(zone, context,
+ current_index, add_offset);
+ add->InsertBefore(this);
+ add->AssumeRepresentation(index()->representation());
+ add->ClearFlag(kCanOverflow);
+ current_index = add;
+ }
+
+ if (actual_scale != 0) {
+ HConstant* sar_scale = HConstant::New(zone, context, actual_scale);
+ sar_scale->InsertBefore(this);
+ HInstruction* sar = HSar::New(zone, context,
+ current_index, sar_scale);
+ sar->InsertBefore(this);
+ sar->AssumeRepresentation(index()->representation());
+ current_index = sar;
+ }
+
+ SetOperandAt(0, current_index);
+
+ base_ = NULL;
+ offset_ = 0;
+ scale_ = 0;
+}
+
+
+void HBoundsCheck::PrintDataTo(StringStream* stream) {
+ index()->PrintNameTo(stream);
+ stream->Add(" ");
+ length()->PrintNameTo(stream);
+ if (base() != NULL && (offset() != 0 || scale() != 0)) {
+ stream->Add(" base: ((");
+ if (base() != index()) {
+ index()->PrintNameTo(stream);
+ } else {
+ stream->Add("index");
+ }
+ stream->Add(" + %d) >> %d)", offset(), scale());
+ }
+ if (skip_check()) {
+ stream->Add(" [DISABLED]");
+ }
+}
+
+
+void HBoundsCheck::InferRepresentation(HInferRepresentationPhase* h_infer) {
+ ASSERT(CheckFlag(kFlexibleRepresentation));
+ HValue* actual_index = index()->ActualValue();
+ HValue* actual_length = length()->ActualValue();
+ Representation index_rep = actual_index->representation();
+ Representation length_rep = actual_length->representation();
+ if (index_rep.IsTagged() && actual_index->type().IsSmi()) {
+ index_rep = Representation::Smi();
+ }
+ if (length_rep.IsTagged() && actual_length->type().IsSmi()) {
+ length_rep = Representation::Smi();
+ }
+ Representation r = index_rep.generalize(length_rep);
+ if (r.is_more_general_than(Representation::Integer32())) {
+ r = Representation::Integer32();
+ }
+ UpdateRepresentation(r, h_infer, "boundscheck");
+}
+
+
+void HBoundsCheckBaseIndexInformation::PrintDataTo(StringStream* stream) {
+ stream->Add("base: ");
+ base_index()->PrintNameTo(stream);
+ stream->Add(", check: ");
+ base_index()->PrintNameTo(stream);
+}
+
+
+void HCallConstantFunction::PrintDataTo(StringStream* stream) {
+ if (IsApplyFunction()) {
+ stream->Add("optimized apply ");
+ } else {
+ stream->Add("%o ", function()->shared()->DebugName());
+ }
+ stream->Add("#%d", argument_count());
+}
+
+
+void HCallNamed::PrintDataTo(StringStream* stream) {
+ stream->Add("%o ", *name());
+ HUnaryCall::PrintDataTo(stream);
+}
+
+
+void HCallGlobal::PrintDataTo(StringStream* stream) {
+ stream->Add("%o ", *name());
+ HUnaryCall::PrintDataTo(stream);
+}
+
+
+void HCallKnownGlobal::PrintDataTo(StringStream* stream) {
+ stream->Add("%o ", target()->shared()->DebugName());
+ stream->Add("#%d", argument_count());
+}
+
+
+void HCallNewArray::PrintDataTo(StringStream* stream) {
+ stream->Add(ElementsKindToString(elements_kind()));
+ stream->Add(" ");
+ HBinaryCall::PrintDataTo(stream);
+}
+
+
+void HCallRuntime::PrintDataTo(StringStream* stream) {
+ stream->Add("%o ", *name());
+ stream->Add("#%d", argument_count());
+}
+
+
+void HClassOfTestAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("class_of_test(");
+ value()->PrintNameTo(stream);
+ stream->Add(", \"%o\")", *class_name());
+}
+
+
+void HWrapReceiver::PrintDataTo(StringStream* stream) {
+ receiver()->PrintNameTo(stream);
+ stream->Add(" ");
+ function()->PrintNameTo(stream);
+}
+
+
+void HAccessArgumentsAt::PrintDataTo(StringStream* stream) {
+ arguments()->PrintNameTo(stream);
+ stream->Add("[");
+ index()->PrintNameTo(stream);
+ stream->Add("], length ");
+ length()->PrintNameTo(stream);
+}
+
+
+void HControlInstruction::PrintDataTo(StringStream* stream) {
+ stream->Add(" goto (");
+ bool first_block = true;
+ for (HSuccessorIterator it(this); !it.Done(); it.Advance()) {
+ stream->Add(first_block ? "B%d" : ", B%d", it.Current()->block_id());
+ first_block = false;
+ }
+ stream->Add(")");
+}
+
+
+void HUnaryControlInstruction::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ HControlInstruction::PrintDataTo(stream);
+}
+
+
+void HReturn::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ stream->Add(" (pop ");
+ parameter_count()->PrintNameTo(stream);
+ stream->Add(" values)");
+}
+
+
+Representation HBranch::observed_input_representation(int index) {
+ static const ToBooleanStub::Types tagged_types(
+ ToBooleanStub::NULL_TYPE |
+ ToBooleanStub::SPEC_OBJECT |
+ ToBooleanStub::STRING |
+ ToBooleanStub::SYMBOL);
+ if (expected_input_types_.ContainsAnyOf(tagged_types)) {
+ return Representation::Tagged();
+ }
+ if (expected_input_types_.Contains(ToBooleanStub::UNDEFINED)) {
+ if (expected_input_types_.Contains(ToBooleanStub::HEAP_NUMBER)) {
+ return Representation::Double();
+ }
+ return Representation::Tagged();
+ }
+ if (expected_input_types_.Contains(ToBooleanStub::HEAP_NUMBER)) {
+ return Representation::Double();
+ }
+ if (expected_input_types_.Contains(ToBooleanStub::SMI)) {
+ return Representation::Smi();
+ }
+ return Representation::None();
+}
+
+
+void HCompareMap::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ stream->Add(" (%p)", *map());
+ HControlInstruction::PrintDataTo(stream);
+}
+
+
+const char* HUnaryMathOperation::OpName() const {
+ switch (op()) {
+ case kMathFloor: return "floor";
+ case kMathRound: return "round";
+ case kMathAbs: return "abs";
+ case kMathLog: return "log";
+ case kMathSin: return "sin";
+ case kMathCos: return "cos";
+ case kMathTan: return "tan";
+ case kMathExp: return "exp";
+ case kMathSqrt: return "sqrt";
+ case kMathPowHalf: return "pow-half";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+Range* HUnaryMathOperation::InferRange(Zone* zone) {
+ Representation r = representation();
+ if (r.IsSmiOrInteger32() && value()->HasRange()) {
+ if (op() == kMathAbs) {
+ int upper = value()->range()->upper();
+ int lower = value()->range()->lower();
+ bool spans_zero = value()->range()->CanBeZero();
+ // Math.abs(kMinInt) overflows its representation, on which the
+ // instruction deopts. Hence clamp it to kMaxInt.
+ int abs_upper = upper == kMinInt ? kMaxInt : abs(upper);
+ int abs_lower = lower == kMinInt ? kMaxInt : abs(lower);
+ Range* result =
+ new(zone) Range(spans_zero ? 0 : Min(abs_lower, abs_upper),
+ Max(abs_lower, abs_upper));
+ // In case of Smi representation, clamp Math.abs(Smi::kMinValue) to
+ // Smi::kMaxValue.
+ if (r.IsSmi()) result->ClampToSmi();
+ return result;
+ }
+ }
+ return HValue::InferRange(zone);
+}
+
+
+void HUnaryMathOperation::PrintDataTo(StringStream* stream) {
+ const char* name = OpName();
+ stream->Add("%s ", name);
+ value()->PrintNameTo(stream);
+}
+
+
+void HUnaryOperation::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+}
+
+
+void HHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ switch (from_) {
+ case FIRST_JS_RECEIVER_TYPE:
+ if (to_ == LAST_TYPE) stream->Add(" spec_object");
+ break;
+ case JS_REGEXP_TYPE:
+ if (to_ == JS_REGEXP_TYPE) stream->Add(" reg_exp");
+ break;
+ case JS_ARRAY_TYPE:
+ if (to_ == JS_ARRAY_TYPE) stream->Add(" array");
+ break;
+ case JS_FUNCTION_TYPE:
+ if (to_ == JS_FUNCTION_TYPE) stream->Add(" function");
+ break;
+ default:
+ break;
+ }
+}
+
+
+void HTypeofIsAndBranch::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ stream->Add(" == %o", *type_literal_);
+ HControlInstruction::PrintDataTo(stream);
+}
+
+
+void HCheckMapValue::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ stream->Add(" ");
+ map()->PrintNameTo(stream);
+}
+
+
+void HForInPrepareMap::PrintDataTo(StringStream* stream) {
+ enumerable()->PrintNameTo(stream);
+}
+
+
+void HForInCacheArray::PrintDataTo(StringStream* stream) {
+ enumerable()->PrintNameTo(stream);
+ stream->Add(" ");
+ map()->PrintNameTo(stream);
+ stream->Add("[%d]", idx_);
+}
+
+
+void HLoadFieldByIndex::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ stream->Add(" ");
+ index()->PrintNameTo(stream);
+}
+
+
+static bool MatchLeftIsOnes(HValue* l, HValue* r, HValue** negated) {
+ if (!l->EqualsInteger32Constant(~0)) return false;
+ *negated = r;
+ return true;
+}
+
+
+static bool MatchNegationViaXor(HValue* instr, HValue** negated) {
+ if (!instr->IsBitwise()) return false;
+ HBitwise* b = HBitwise::cast(instr);
+ return (b->op() == Token::BIT_XOR) &&
+ (MatchLeftIsOnes(b->left(), b->right(), negated) ||
+ MatchLeftIsOnes(b->right(), b->left(), negated));
+}
+
+
+static bool MatchDoubleNegation(HValue* instr, HValue** arg) {
+ HValue* negated;
+ return MatchNegationViaXor(instr, &negated) &&
+ MatchNegationViaXor(negated, arg);
+}
+
+
+HValue* HBitwise::Canonicalize() {
+ if (!representation().IsSmiOrInteger32()) 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()->EqualsInteger32Constant(nop_constant) &&
+ !right()->CheckFlag(kUint32)) {
+ return right();
+ }
+ if (right()->EqualsInteger32Constant(nop_constant) &&
+ !left()->CheckFlag(kUint32)) {
+ return left();
+ }
+ // Optimize double negation, a common pattern used for ToInt32(x).
+ HValue* arg;
+ if (MatchDoubleNegation(this, &arg) && !arg->CheckFlag(kUint32)) {
+ return arg;
+ }
+ return this;
+}
+
+
+static bool IsIdentityOperation(HValue* arg1, HValue* arg2, int32_t identity) {
+ return arg1->representation().IsSpecialization() &&
+ arg2->EqualsInteger32Constant(identity);
+}
+
+
+HValue* HAdd::Canonicalize() {
+ if (IsIdentityOperation(left(), right(), 0)) return left();
+ if (IsIdentityOperation(right(), left(), 0)) return right();
+ return this;
+}
+
+
+HValue* HSub::Canonicalize() {
+ if (IsIdentityOperation(left(), right(), 0)) return left();
+ return this;
+}
+
+
+HValue* HMul::Canonicalize() {
+ if (IsIdentityOperation(left(), right(), 1)) return left();
+ if (IsIdentityOperation(right(), left(), 1)) return right();
+ return this;
+}
+
+
+HValue* HMod::Canonicalize() {
+ return this;
+}
+
+
+HValue* HDiv::Canonicalize() {
+ return this;
+}
+
+
+HValue* HChange::Canonicalize() {
+ return (from().Equals(to())) ? value() : this;
+}
+
+
+HValue* HWrapReceiver::Canonicalize() {
+ if (HasNoUses()) return NULL;
+ if (receiver()->type().IsJSObject()) {
+ return receiver();
+ }
+ return this;
+}
+
+
+void HTypeof::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+}
+
+
+void HForceRepresentation::PrintDataTo(StringStream* stream) {
+ stream->Add("%s ", representation().Mnemonic());
+ value()->PrintNameTo(stream);
+}
+
+
+void HChange::PrintDataTo(StringStream* stream) {
+ HUnaryOperation::PrintDataTo(stream);
+ stream->Add(" %s to %s", from().Mnemonic(), to().Mnemonic());
+
+ if (CanTruncateToInt32()) stream->Add(" truncating-int32");
+ if (CheckFlag(kBailoutOnMinusZero)) stream->Add(" -0?");
+ if (CheckFlag(kAllowUndefinedAsNaN)) stream->Add(" allow-undefined-as-nan");
+}
+
+
+static HValue* SimplifiedDividendForMathFloorOfDiv(HValue* dividend) {
+ // A value with an integer representation does not need to be transformed.
+ if (dividend->representation().IsInteger32()) {
+ return dividend;
+ }
+ // A change from an integer32 can be replaced by the integer32 value.
+ if (dividend->IsChange() &&
+ HChange::cast(dividend)->from().IsInteger32()) {
+ return HChange::cast(dividend)->value();
+ }
+ return NULL;
+}
+
+
+HValue* HUnaryMathOperation::Canonicalize() {
+ if (op() == kMathRound || op() == kMathFloor) {
+ HValue* val = value();
+ if (val->IsChange()) val = HChange::cast(val)->value();
+
+ // If the input is smi or integer32 then we replace the instruction with its
+ // input.
+ if (val->representation().IsSmiOrInteger32()) {
+ if (!val->representation().Equals(representation())) {
+ HChange* result = new(block()->zone()) HChange(
+ val, representation(), false, false);
+ result->InsertBefore(this);
+ return result;
+ }
+ return val;
+ }
+ }
+
+ if (op() == kMathFloor) {
+ HValue* val = value();
+ if (val->IsChange()) val = HChange::cast(val)->value();
+ if (val->IsDiv() && (val->UseCount() == 1)) {
+ HDiv* hdiv = HDiv::cast(val);
+ HValue* left = hdiv->left();
+ HValue* right = hdiv->right();
+ // Try to simplify left and right values of the division.
+ HValue* new_left = SimplifiedDividendForMathFloorOfDiv(left);
+ if (new_left == NULL &&
+ hdiv->observed_input_representation(1).IsSmiOrInteger32()) {
+ new_left = new(block()->zone()) HChange(
+ left, Representation::Integer32(), false, false);
+ HChange::cast(new_left)->InsertBefore(this);
+ }
+ HValue* new_right =
+ LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(right);
+ if (new_right == NULL &&
+#if V8_TARGET_ARCH_ARM
+ CpuFeatures::IsSupported(SUDIV) &&
+#endif
+ hdiv->observed_input_representation(2).IsSmiOrInteger32()) {
+ new_right = new(block()->zone()) HChange(
+ right, Representation::Integer32(), false, false);
+ HChange::cast(new_right)->InsertBefore(this);
+ }
+
+ // Return if left or right are not optimizable.
+ if ((new_left == NULL) || (new_right == NULL)) return this;
+
+ // Insert the new values in the graph.
+ if (new_left->IsInstruction() &&
+ !HInstruction::cast(new_left)->IsLinked()) {
+ HInstruction::cast(new_left)->InsertBefore(this);
+ }
+ if (new_right->IsInstruction() &&
+ !HInstruction::cast(new_right)->IsLinked()) {
+ HInstruction::cast(new_right)->InsertBefore(this);
+ }
+ HMathFloorOfDiv* instr =
+ HMathFloorOfDiv::New(block()->zone(), context(), new_left, new_right);
+ // Replace this HMathFloor instruction by the new HMathFloorOfDiv.
+ instr->InsertBefore(this);
+ ReplaceAllUsesWith(instr);
+ Kill();
+ // We know the division had no other uses than this HMathFloor. Delete it.
+ // Dead code elimination will deal with |left| and |right| if
+ // appropriate.
+ hdiv->DeleteAndReplaceWith(NULL);
+
+ // Return NULL to remove this instruction from the graph.
+ return NULL;
+ }
+ }
+ return this;
+}
+
+
+HValue* HCheckInstanceType::Canonicalize() {
+ if (check_ == IS_STRING && value()->type().IsString()) {
+ return value();
+ }
+
+ if (check_ == IS_INTERNALIZED_STRING && value()->IsConstant()) {
+ if (HConstant::cast(value())->HasInternalizedStringValue()) {
+ return value();
+ }
+ }
+ return this;
+}
+
+
+void HCheckInstanceType::GetCheckInterval(InstanceType* first,
+ InstanceType* last) {
+ ASSERT(is_interval_check());
+ switch (check_) {
+ case IS_SPEC_OBJECT:
+ *first = FIRST_SPEC_OBJECT_TYPE;
+ *last = LAST_SPEC_OBJECT_TYPE;
+ return;
+ case IS_JS_ARRAY:
+ *first = *last = JS_ARRAY_TYPE;
+ return;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void HCheckInstanceType::GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag) {
+ ASSERT(!is_interval_check());
+ switch (check_) {
+ case IS_STRING:
+ *mask = kIsNotStringMask;
+ *tag = kStringTag;
+ return;
+ case IS_INTERNALIZED_STRING:
+ *mask = kIsNotInternalizedMask;
+ *tag = kInternalizedTag;
+ return;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void HCheckMaps::HandleSideEffectDominator(GVNFlag side_effect,
+ HValue* dominator) {
+ ASSERT(side_effect == kChangesMaps);
+ // TODO(mstarzinger): For now we specialize on HStoreNamedField, but once
+ // type information is rich enough we should generalize this to any HType
+ // for which the map is known.
+ if (HasNoUses() && dominator->IsStoreNamedField()) {
+ HStoreNamedField* store = HStoreNamedField::cast(dominator);
+ if (!store->has_transition() || store->object() != value()) return;
+ HConstant* transition = HConstant::cast(store->transition());
+ for (int i = 0; i < map_set()->length(); i++) {
+ if (transition->UniqueValueIdsMatch(map_unique_ids_.at(i))) {
+ DeleteAndReplaceWith(NULL);
+ return;
+ }
+ }
+ }
+}
+
+
+void HCheckMaps::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ stream->Add(" [%p", *map_set()->first());
+ for (int i = 1; i < map_set()->length(); ++i) {
+ stream->Add(",%p", *map_set()->at(i));
+ }
+ stream->Add("]%s", CanOmitMapChecks() ? "(omitted)" : "");
+}
+
+
+void HCheckFunction::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ stream->Add(" %p", *target());
+}
+
+
+HValue* HCheckFunction::Canonicalize() {
+ return (value()->IsConstant() &&
+ HConstant::cast(value())->UniqueValueIdsMatch(target_unique_id_))
+ ? NULL
+ : this;
+}
+
+
+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_INTERNALIZED_STRING: return "internalized_string";
+ }
+ 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));
+ HUnaryCall::PrintDataTo(stream);
+}
+
+
+void HInstanceOf::PrintDataTo(StringStream* stream) {
+ left()->PrintNameTo(stream);
+ stream->Add(" ");
+ right()->PrintNameTo(stream);
+ stream->Add(" ");
+ context()->PrintNameTo(stream);
+}
+
+
+Range* HValue::InferRange(Zone* zone) {
+ Range* result;
+ if (representation().IsSmi() || type().IsSmi()) {
+ result = new(zone) Range(Smi::kMinValue, Smi::kMaxValue);
+ result->set_can_be_minus_zero(false);
+ } else {
+ result = new(zone) Range();
+ result->set_can_be_minus_zero(!CheckFlag(kAllUsesTruncatingToInt32));
+ // TODO(jkummerow): The range cannot be minus zero when the upper type
+ // bound is Integer32.
+ }
+ return result;
+}
+
+
+Range* HChange::InferRange(Zone* zone) {
+ Range* input_range = value()->range();
+ if (from().IsInteger32() && !value()->CheckFlag(HInstruction::kUint32) &&
+ (to().IsSmi() ||
+ (to().IsTagged() &&
+ input_range != NULL &&
+ input_range->IsInSmiRange()))) {
+ set_type(HType::Smi());
+ ClearGVNFlag(kChangesNewSpacePromotion);
+ }
+ Range* result = (input_range != NULL)
+ ? input_range->Copy(zone)
+ : HValue::InferRange(zone);
+ result->set_can_be_minus_zero(!to().IsSmiOrInteger32() ||
+ !(CheckFlag(kAllUsesTruncatingToInt32) ||
+ CheckFlag(kAllUsesTruncatingToSmi)));
+ if (to().IsSmi()) result->ClampToSmi();
+ return result;
+}
+
+
+Range* HConstant::InferRange(Zone* zone) {
+ if (has_int32_value_) {
+ Range* result = new(zone) Range(int32_value_, int32_value_);
+ result->set_can_be_minus_zero(false);
+ return result;
+ }
+ return HValue::InferRange(zone);
+}
+
+
+Range* HPhi::InferRange(Zone* zone) {
+ Representation r = representation();
+ if (r.IsSmiOrInteger32()) {
+ if (block()->IsLoopHeader()) {
+ Range* range = r.IsSmi()
+ ? new(zone) Range(Smi::kMinValue, Smi::kMaxValue)
+ : new(zone) Range(kMinInt, kMaxInt);
+ return range;
+ } else {
+ Range* range = OperandAt(0)->range()->Copy(zone);
+ for (int i = 1; i < OperandCount(); ++i) {
+ range->Union(OperandAt(i)->range());
+ }
+ return range;
+ }
+ } else {
+ return HValue::InferRange(zone);
+ }
+}
+
+
+Range* HAdd::InferRange(Zone* zone) {
+ Representation r = representation();
+ if (r.IsSmiOrInteger32()) {
+ Range* a = left()->range();
+ Range* b = right()->range();
+ Range* res = a->Copy(zone);
+ if (!res->AddAndCheckOverflow(r, b) ||
+ (r.IsInteger32() && CheckFlag(kAllUsesTruncatingToInt32)) ||
+ (r.IsSmi() && CheckFlag(kAllUsesTruncatingToSmi))) {
+ ClearFlag(kCanOverflow);
+ }
+ res->set_can_be_minus_zero(!CheckFlag(kAllUsesTruncatingToSmi) &&
+ !CheckFlag(kAllUsesTruncatingToInt32) &&
+ a->CanBeMinusZero() && b->CanBeMinusZero());
+ return res;
+ } else {
+ return HValue::InferRange(zone);
+ }
+}
+
+
+Range* HSub::InferRange(Zone* zone) {
+ Representation r = representation();
+ if (r.IsSmiOrInteger32()) {
+ Range* a = left()->range();
+ Range* b = right()->range();
+ Range* res = a->Copy(zone);
+ if (!res->SubAndCheckOverflow(r, b) ||
+ (r.IsInteger32() && CheckFlag(kAllUsesTruncatingToInt32)) ||
+ (r.IsSmi() && CheckFlag(kAllUsesTruncatingToSmi))) {
+ ClearFlag(kCanOverflow);
+ }
+ res->set_can_be_minus_zero(!CheckFlag(kAllUsesTruncatingToSmi) &&
+ !CheckFlag(kAllUsesTruncatingToInt32) &&
+ a->CanBeMinusZero() && b->CanBeZero());
+ return res;
+ } else {
+ return HValue::InferRange(zone);
+ }
+}
+
+
+Range* HMul::InferRange(Zone* zone) {
+ Representation r = representation();
+ if (r.IsSmiOrInteger32()) {
+ Range* a = left()->range();
+ Range* b = right()->range();
+ Range* res = a->Copy(zone);
+ if (!res->MulAndCheckOverflow(r, b)) {
+ // Clearing the kCanOverflow flag when kAllUsesAreTruncatingToInt32
+ // would be wrong, because truncated integer multiplication is too
+ // precise and therefore not the same as converting to Double and back.
+ ClearFlag(kCanOverflow);
+ }
+ res->set_can_be_minus_zero(!CheckFlag(kAllUsesTruncatingToSmi) &&
+ !CheckFlag(kAllUsesTruncatingToInt32) &&
+ ((a->CanBeZero() && b->CanBeNegative()) ||
+ (a->CanBeNegative() && b->CanBeZero())));
+ return res;
+ } else {
+ return HValue::InferRange(zone);
+ }
+}
+
+
+Range* HDiv::InferRange(Zone* zone) {
+ if (representation().IsInteger32()) {
+ Range* a = left()->range();
+ Range* b = right()->range();
+ Range* result = new(zone) Range();
+ result->set_can_be_minus_zero(!CheckFlag(kAllUsesTruncatingToInt32) &&
+ (a->CanBeMinusZero() ||
+ (a->CanBeZero() && b->CanBeNegative())));
+ if (!a->Includes(kMinInt) || !b->Includes(-1)) {
+ ClearFlag(HValue::kCanOverflow);
+ }
+
+ if (!b->CanBeZero()) {
+ ClearFlag(HValue::kCanBeDivByZero);
+ }
+ return result;
+ } else {
+ return HValue::InferRange(zone);
+ }
+}
+
+
+Range* HMod::InferRange(Zone* zone) {
+ if (representation().IsInteger32()) {
+ Range* a = left()->range();
+ Range* b = right()->range();
+
+ // The magnitude of the modulus is bounded by the right operand. Note that
+ // apart for the cases involving kMinInt, the calculation below is the same
+ // as Max(Abs(b->lower()), Abs(b->upper())) - 1.
+ int32_t positive_bound = -(Min(NegAbs(b->lower()), NegAbs(b->upper())) + 1);
+
+ // The result of the modulo operation has the sign of its left operand.
+ bool left_can_be_negative = a->CanBeMinusZero() || a->CanBeNegative();
+ Range* result = new(zone) Range(left_can_be_negative ? -positive_bound : 0,
+ a->CanBePositive() ? positive_bound : 0);
+
+ result->set_can_be_minus_zero(!CheckFlag(kAllUsesTruncatingToInt32) &&
+ left_can_be_negative);
+
+ if (!a->Includes(kMinInt) || !b->Includes(-1)) {
+ ClearFlag(HValue::kCanOverflow);
+ }
+
+ if (!b->CanBeZero()) {
+ ClearFlag(HValue::kCanBeDivByZero);
+ }
+ return result;
+ } else {
+ return HValue::InferRange(zone);
+ }
+}
+
+
+InductionVariableData* InductionVariableData::ExaminePhi(HPhi* phi) {
+ if (phi->block()->loop_information() == NULL) return NULL;
+ if (phi->OperandCount() != 2) return NULL;
+ int32_t candidate_increment;
+
+ candidate_increment = ComputeIncrement(phi, phi->OperandAt(0));
+ if (candidate_increment != 0) {
+ return new(phi->block()->graph()->zone())
+ InductionVariableData(phi, phi->OperandAt(1), candidate_increment);
+ }
+
+ candidate_increment = ComputeIncrement(phi, phi->OperandAt(1));
+ if (candidate_increment != 0) {
+ return new(phi->block()->graph()->zone())
+ InductionVariableData(phi, phi->OperandAt(0), candidate_increment);
+ }
+
+ return NULL;
+}
+
+
+/*
+ * This function tries to match the following patterns (and all the relevant
+ * variants related to |, & and + being commutative):
+ * base | constant_or_mask
+ * base & constant_and_mask
+ * (base + constant_offset) & constant_and_mask
+ * (base - constant_offset) & constant_and_mask
+ */
+void InductionVariableData::DecomposeBitwise(
+ HValue* value,
+ BitwiseDecompositionResult* result) {
+ HValue* base = IgnoreOsrValue(value);
+ result->base = value;
+
+ if (!base->representation().IsInteger32()) return;
+
+ if (base->IsBitwise()) {
+ bool allow_offset = false;
+ int32_t mask = 0;
+
+ HBitwise* bitwise = HBitwise::cast(base);
+ if (bitwise->right()->IsInteger32Constant()) {
+ mask = bitwise->right()->GetInteger32Constant();
+ base = bitwise->left();
+ } else if (bitwise->left()->IsInteger32Constant()) {
+ mask = bitwise->left()->GetInteger32Constant();
+ base = bitwise->right();
+ } else {
+ return;
+ }
+ if (bitwise->op() == Token::BIT_AND) {
+ result->and_mask = mask;
+ allow_offset = true;
+ } else if (bitwise->op() == Token::BIT_OR) {
+ result->or_mask = mask;
+ } else {
+ return;
+ }
+
+ result->context = bitwise->context();
+
+ if (allow_offset) {
+ if (base->IsAdd()) {
+ HAdd* add = HAdd::cast(base);
+ if (add->right()->IsInteger32Constant()) {
+ base = add->left();
+ } else if (add->left()->IsInteger32Constant()) {
+ base = add->right();
+ }
+ } else if (base->IsSub()) {
+ HSub* sub = HSub::cast(base);
+ if (sub->right()->IsInteger32Constant()) {
+ base = sub->left();
+ }
+ }
+ }
+
+ result->base = base;
+ }
+}
+
+
+void InductionVariableData::AddCheck(HBoundsCheck* check,
+ int32_t upper_limit) {
+ ASSERT(limit_validity() != NULL);
+ if (limit_validity() != check->block() &&
+ !limit_validity()->Dominates(check->block())) return;
+ if (!phi()->block()->current_loop()->IsNestedInThisLoop(
+ check->block()->current_loop())) return;
+
+ ChecksRelatedToLength* length_checks = checks();
+ while (length_checks != NULL) {
+ if (length_checks->length() == check->length()) break;
+ length_checks = length_checks->next();
+ }
+ if (length_checks == NULL) {
+ length_checks = new(check->block()->zone())
+ ChecksRelatedToLength(check->length(), checks());
+ checks_ = length_checks;
+ }
+
+ length_checks->AddCheck(check, upper_limit);
+}
+
+
+void InductionVariableData::ChecksRelatedToLength::CloseCurrentBlock() {
+ if (checks() != NULL) {
+ InductionVariableCheck* c = checks();
+ HBasicBlock* current_block = c->check()->block();
+ while (c != NULL && c->check()->block() == current_block) {
+ c->set_upper_limit(current_upper_limit_);
+ c = c->next();
+ }
+ }
+}
+
+
+void InductionVariableData::ChecksRelatedToLength::UseNewIndexInCurrentBlock(
+ Token::Value token,
+ int32_t mask,
+ HValue* index_base,
+ HValue* context) {
+ ASSERT(first_check_in_block() != NULL);
+ HValue* previous_index = first_check_in_block()->index();
+ ASSERT(context != NULL);
+
+ Zone* zone = index_base->block()->graph()->zone();
+ set_added_constant(HConstant::New(zone, context, mask));
+ if (added_index() != NULL) {
+ added_constant()->InsertBefore(added_index());
+ } else {
+ added_constant()->InsertBefore(first_check_in_block());
+ }
+
+ if (added_index() == NULL) {
+ first_check_in_block()->ReplaceAllUsesWith(first_check_in_block()->index());
+ HInstruction* new_index = HBitwise::New(zone, context, token, index_base,
+ added_constant());
+ ASSERT(new_index->IsBitwise());
+ new_index->ClearAllSideEffects();
+ new_index->AssumeRepresentation(Representation::Integer32());
+ set_added_index(HBitwise::cast(new_index));
+ added_index()->InsertBefore(first_check_in_block());
+ }
+ ASSERT(added_index()->op() == token);
+
+ added_index()->SetOperandAt(1, index_base);
+ added_index()->SetOperandAt(2, added_constant());
+ first_check_in_block()->SetOperandAt(0, added_index());
+ if (previous_index->UseCount() == 0) {
+ previous_index->DeleteAndReplaceWith(NULL);
+ }
+}
+
+void InductionVariableData::ChecksRelatedToLength::AddCheck(
+ HBoundsCheck* check,
+ int32_t upper_limit) {
+ BitwiseDecompositionResult decomposition;
+ InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
+
+ if (first_check_in_block() == NULL ||
+ first_check_in_block()->block() != check->block()) {
+ CloseCurrentBlock();
+
+ first_check_in_block_ = check;
+ set_added_index(NULL);
+ set_added_constant(NULL);
+ current_and_mask_in_block_ = decomposition.and_mask;
+ current_or_mask_in_block_ = decomposition.or_mask;
+ current_upper_limit_ = upper_limit;
+
+ InductionVariableCheck* new_check = new(check->block()->graph()->zone())
+ InductionVariableCheck(check, checks_, upper_limit);
+ checks_ = new_check;
+ return;
+ }
+
+ if (upper_limit > current_upper_limit()) {
+ current_upper_limit_ = upper_limit;
+ }
+
+ if (decomposition.and_mask != 0 &&
+ current_or_mask_in_block() == 0) {
+ if (current_and_mask_in_block() == 0 ||
+ decomposition.and_mask > current_and_mask_in_block()) {
+ UseNewIndexInCurrentBlock(Token::BIT_AND,
+ decomposition.and_mask,
+ decomposition.base,
+ decomposition.context);
+ current_and_mask_in_block_ = decomposition.and_mask;
+ }
+ check->set_skip_check();
+ }
+ if (current_and_mask_in_block() == 0) {
+ if (decomposition.or_mask > current_or_mask_in_block()) {
+ UseNewIndexInCurrentBlock(Token::BIT_OR,
+ decomposition.or_mask,
+ decomposition.base,
+ decomposition.context);
+ current_or_mask_in_block_ = decomposition.or_mask;
+ }
+ check->set_skip_check();
+ }
+
+ if (!check->skip_check()) {
+ InductionVariableCheck* new_check = new(check->block()->graph()->zone())
+ InductionVariableCheck(check, checks_, upper_limit);
+ checks_ = new_check;
+ }
+}
+
+
+/*
+ * This method detects if phi is an induction variable, with phi_operand as
+ * its "incremented" value (the other operand would be the "base" value).
+ *
+ * It cheks is phi_operand has the form "phi + constant".
+ * If yes, the constant is the increment that the induction variable gets at
+ * every loop iteration.
+ * Otherwise it returns 0.
+ */
+int32_t InductionVariableData::ComputeIncrement(HPhi* phi,
+ HValue* phi_operand) {
+ if (!phi_operand->representation().IsInteger32()) return 0;
+
+ if (phi_operand->IsAdd()) {
+ HAdd* operation = HAdd::cast(phi_operand);
+ if (operation->left() == phi &&
+ operation->right()->IsInteger32Constant()) {
+ return operation->right()->GetInteger32Constant();
+ } else if (operation->right() == phi &&
+ operation->left()->IsInteger32Constant()) {
+ return operation->left()->GetInteger32Constant();
+ }
+ } else if (phi_operand->IsSub()) {
+ HSub* operation = HSub::cast(phi_operand);
+ if (operation->left() == phi &&
+ operation->right()->IsInteger32Constant()) {
+ return -operation->right()->GetInteger32Constant();
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Swaps the information in "update" with the one contained in "this".
+ * The swapping is important because this method is used while doing a
+ * dominator tree traversal, and "update" will retain the old data that
+ * will be restored while backtracking.
+ */
+void InductionVariableData::UpdateAdditionalLimit(
+ InductionVariableLimitUpdate* update) {
+ ASSERT(update->updated_variable == this);
+ if (update->limit_is_upper) {
+ swap(&additional_upper_limit_, &update->limit);
+ swap(&additional_upper_limit_is_included_, &update->limit_is_included);
+ } else {
+ swap(&additional_lower_limit_, &update->limit);
+ swap(&additional_lower_limit_is_included_, &update->limit_is_included);
+ }
+}
+
+
+int32_t InductionVariableData::ComputeUpperLimit(int32_t and_mask,
+ int32_t or_mask) {
+ // Should be Smi::kMaxValue but it must fit 32 bits; lower is safe anyway.
+ const int32_t MAX_LIMIT = 1 << 30;
+
+ int32_t result = MAX_LIMIT;
+
+ if (limit() != NULL &&
+ limit()->IsInteger32Constant()) {
+ int32_t limit_value = limit()->GetInteger32Constant();
+ if (!limit_included()) {
+ limit_value--;
+ }
+ if (limit_value < result) result = limit_value;
+ }
+
+ if (additional_upper_limit() != NULL &&
+ additional_upper_limit()->IsInteger32Constant()) {
+ int32_t limit_value = additional_upper_limit()->GetInteger32Constant();
+ if (!additional_upper_limit_is_included()) {
+ limit_value--;
+ }
+ if (limit_value < result) result = limit_value;
+ }
+
+ if (and_mask > 0 && and_mask < MAX_LIMIT) {
+ if (and_mask < result) result = and_mask;
+ return result;
+ }
+
+ // Add the effect of the or_mask.
+ result |= or_mask;
+
+ return result >= MAX_LIMIT ? kNoLimit : result;
+}
+
+
+HValue* InductionVariableData::IgnoreOsrValue(HValue* v) {
+ if (!v->IsPhi()) return v;
+ HPhi* phi = HPhi::cast(v);
+ if (phi->OperandCount() != 2) return v;
+ if (phi->OperandAt(0)->block()->is_osr_entry()) {
+ return phi->OperandAt(1);
+ } else if (phi->OperandAt(1)->block()->is_osr_entry()) {
+ return phi->OperandAt(0);
+ } else {
+ return v;
+ }
+}
+
+
+InductionVariableData* InductionVariableData::GetInductionVariableData(
+ HValue* v) {
+ v = IgnoreOsrValue(v);
+ if (v->IsPhi()) {
+ return HPhi::cast(v)->induction_variable_data();
+ }
+ return NULL;
+}
+
+
+/*
+ * Check if a conditional branch to "current_branch" with token "token" is
+ * the branch that keeps the induction loop running (and, conversely, will
+ * terminate it if the "other_branch" is taken).
+ *
+ * Three conditions must be met:
+ * - "current_branch" must be in the induction loop.
+ * - "other_branch" must be out of the induction loop.
+ * - "token" and the induction increment must be "compatible": the token should
+ * be a condition that keeps the execution inside the loop until the limit is
+ * reached.
+ */
+bool InductionVariableData::CheckIfBranchIsLoopGuard(
+ Token::Value token,
+ HBasicBlock* current_branch,
+ HBasicBlock* other_branch) {
+ if (!phi()->block()->current_loop()->IsNestedInThisLoop(
+ current_branch->current_loop())) {
+ return false;
+ }
+
+ if (phi()->block()->current_loop()->IsNestedInThisLoop(
+ other_branch->current_loop())) {
+ return false;
+ }
+
+ if (increment() > 0 && (token == Token::LT || token == Token::LTE)) {
+ return true;
+ }
+ if (increment() < 0 && (token == Token::GT || token == Token::GTE)) {
+ return true;
+ }
+ if (Token::IsInequalityOp(token) && (increment() == 1 || increment() == -1)) {
+ return true;
+ }
+
+ return false;
+}
+
+
+void InductionVariableData::ComputeLimitFromPredecessorBlock(
+ HBasicBlock* block,
+ LimitFromPredecessorBlock* result) {
+ if (block->predecessors()->length() != 1) return;
+ HBasicBlock* predecessor = block->predecessors()->at(0);
+ HInstruction* end = predecessor->last();
+
+ if (!end->IsCompareNumericAndBranch()) return;
+ HCompareNumericAndBranch* branch = HCompareNumericAndBranch::cast(end);
+
+ Token::Value token = branch->token();
+ if (!Token::IsArithmeticCompareOp(token)) return;
+
+ HBasicBlock* other_target;
+ if (block == branch->SuccessorAt(0)) {
+ other_target = branch->SuccessorAt(1);
+ } else {
+ other_target = branch->SuccessorAt(0);
+ token = Token::NegateCompareOp(token);
+ ASSERT(block == branch->SuccessorAt(1));
+ }
+
+ InductionVariableData* data;
+
+ data = GetInductionVariableData(branch->left());
+ HValue* limit = branch->right();
+ if (data == NULL) {
+ data = GetInductionVariableData(branch->right());
+ token = Token::ReverseCompareOp(token);
+ limit = branch->left();
+ }
+
+ if (data != NULL) {
+ result->variable = data;
+ result->token = token;
+ result->limit = limit;
+ result->other_target = other_target;
+ }
+}
+
+
+/*
+ * Compute the limit that is imposed on an induction variable when entering
+ * "block" (if any).
+ * If the limit is the "proper" induction limit (the one that makes the loop
+ * terminate when the induction variable reaches it) it is stored directly in
+ * the induction variable data.
+ * Otherwise the limit is written in "additional_limit" and the method
+ * returns true.
+ */
+bool InductionVariableData::ComputeInductionVariableLimit(
+ HBasicBlock* block,
+ InductionVariableLimitUpdate* additional_limit) {
+ LimitFromPredecessorBlock limit;
+ ComputeLimitFromPredecessorBlock(block, &limit);
+ if (!limit.LimitIsValid()) return false;
+
+ if (limit.variable->CheckIfBranchIsLoopGuard(limit.token,
+ block,
+ limit.other_target)) {
+ limit.variable->limit_ = limit.limit;
+ limit.variable->limit_included_ = limit.LimitIsIncluded();
+ limit.variable->limit_validity_ = block;
+ limit.variable->induction_exit_block_ = block->predecessors()->at(0);
+ limit.variable->induction_exit_target_ = limit.other_target;
+ return false;
+ } else {
+ additional_limit->updated_variable = limit.variable;
+ additional_limit->limit = limit.limit;
+ additional_limit->limit_is_upper = limit.LimitIsUpper();
+ additional_limit->limit_is_included = limit.LimitIsIncluded();
+ return true;
+ }
+}
+
+
+Range* HMathMinMax::InferRange(Zone* zone) {
+ if (representation().IsSmiOrInteger32()) {
+ Range* a = left()->range();
+ Range* b = right()->range();
+ Range* res = a->Copy(zone);
+ if (operation_ == kMathMax) {
+ res->CombinedMax(b);
+ } else {
+ ASSERT(operation_ == kMathMin);
+ res->CombinedMin(b);
+ }
+ return res;
+ } else {
+ return HValue::InferRange(zone);
+ }
+}
+
+
+void HPhi::PrintTo(StringStream* stream) {
+ stream->Add("[");
+ for (int i = 0; i < OperandCount(); ++i) {
+ HValue* value = OperandAt(i);
+ stream->Add(" ");
+ value->PrintNameTo(stream);
+ stream->Add(" ");
+ }
+ stream->Add(" uses:%d_%ds_%di_%dd_%dt",
+ UseCount(),
+ smi_non_phi_uses() + smi_indirect_uses(),
+ int32_non_phi_uses() + int32_indirect_uses(),
+ double_non_phi_uses() + double_indirect_uses(),
+ tagged_non_phi_uses() + tagged_indirect_uses());
+ PrintRangeTo(stream);
+ PrintTypeTo(stream);
+ stream->Add("]");
+}
+
+
+void HPhi::AddInput(HValue* value) {
+ inputs_.Add(NULL, value->block()->zone());
+ SetOperandAt(OperandCount() - 1, value);
+ // Mark phis that may have 'arguments' directly or indirectly as an operand.
+ if (!CheckFlag(kIsArguments) && value->CheckFlag(kIsArguments)) {
+ SetFlag(kIsArguments);
+ }
+}
+
+
+bool HPhi::HasRealUses() {
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->IsPhi()) return true;
+ }
+ return false;
+}
+
+
+HValue* HPhi::GetRedundantReplacement() {
+ HValue* candidate = NULL;
+ int count = OperandCount();
+ int position = 0;
+ while (position < count && candidate == NULL) {
+ HValue* current = OperandAt(position++);
+ if (current != this) candidate = current;
+ }
+ while (position < count) {
+ HValue* current = OperandAt(position++);
+ if (current != this && current != candidate) return NULL;
+ }
+ ASSERT(candidate != this);
+ return candidate;
+}
+
+
+void HPhi::DeleteFromGraph() {
+ ASSERT(block() != NULL);
+ block()->RemovePhi(this);
+ ASSERT(block() == NULL);
+}
+
+
+void HPhi::InitRealUses(int phi_id) {
+ // Initialize real uses.
+ phi_id_ = phi_id;
+ // Compute a conservative approximation of truncating uses before inferring
+ // representations. The proper, exact computation will be done later, when
+ // inserting representation changes.
+ SetFlag(kTruncatingToSmi);
+ SetFlag(kTruncatingToInt32);
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ if (!value->IsPhi()) {
+ Representation rep = value->observed_input_representation(it.index());
+ non_phi_uses_[rep.kind()] += value->LoopWeight();
+ if (FLAG_trace_representation) {
+ PrintF("#%d Phi is used by real #%d %s as %s\n",
+ id(), value->id(), value->Mnemonic(), rep.Mnemonic());
+ }
+ if (!value->IsSimulate()) {
+ if (!value->CheckFlag(kTruncatingToSmi)) {
+ ClearFlag(kTruncatingToSmi);
+ }
+ if (!value->CheckFlag(kTruncatingToInt32)) {
+ ClearFlag(kTruncatingToInt32);
+ }
+ }
+ }
+ }
+}
+
+
+void HPhi::AddNonPhiUsesFrom(HPhi* other) {
+ if (FLAG_trace_representation) {
+ PrintF("adding to #%d Phi uses of #%d Phi: s%d i%d d%d t%d\n",
+ id(), other->id(),
+ other->non_phi_uses_[Representation::kSmi],
+ other->non_phi_uses_[Representation::kInteger32],
+ other->non_phi_uses_[Representation::kDouble],
+ other->non_phi_uses_[Representation::kTagged]);
+ }
+
+ for (int i = 0; i < Representation::kNumRepresentations; i++) {
+ indirect_uses_[i] += other->non_phi_uses_[i];
+ }
+}
+
+
+void HPhi::AddIndirectUsesTo(int* dest) {
+ for (int i = 0; i < Representation::kNumRepresentations; i++) {
+ dest[i] += indirect_uses_[i];
+ }
+}
+
+
+void HSimulate::MergeWith(ZoneList<HSimulate*>* list) {
+ while (!list->is_empty()) {
+ HSimulate* from = list->RemoveLast();
+ ZoneList<HValue*>* from_values = &from->values_;
+ for (int i = 0; i < from_values->length(); ++i) {
+ if (from->HasAssignedIndexAt(i)) {
+ int index = from->GetAssignedIndexAt(i);
+ if (HasValueForIndex(index)) continue;
+ AddAssignedValue(index, from_values->at(i));
+ } else {
+ if (pop_count_ > 0) {
+ pop_count_--;
+ } else {
+ AddPushedValue(from_values->at(i));
+ }
+ }
+ }
+ pop_count_ += from->pop_count_;
+ from->DeleteAndReplaceWith(NULL);
+ }
+}
+
+
+void HSimulate::PrintDataTo(StringStream* stream) {
+ stream->Add("id=%d", ast_id().ToInt());
+ if (pop_count_ > 0) stream->Add(" pop %d", pop_count_);
+ if (values_.length() > 0) {
+ if (pop_count_ > 0) stream->Add(" /");
+ for (int i = values_.length() - 1; i >= 0; --i) {
+ if (HasAssignedIndexAt(i)) {
+ stream->Add(" var[%d] = ", GetAssignedIndexAt(i));
+ } else {
+ stream->Add(" push ");
+ }
+ values_[i]->PrintNameTo(stream);
+ if (i > 0) stream->Add(",");
+ }
+ }
+}
+
+
+void HEnterInlined::RegisterReturnTarget(HBasicBlock* return_target,
+ Zone* zone) {
+ ASSERT(return_target->IsInlineReturnTarget());
+ return_targets_.Add(return_target, zone);
+}
+
+
+void HEnterInlined::PrintDataTo(StringStream* stream) {
+ SmartArrayPointer<char> name = function()->debug_name()->ToCString();
+ stream->Add("%s, id=%d", *name, function()->id().ToInt());
+}
+
+
+static bool IsInteger32(double value) {
+ double roundtrip_value = static_cast<double>(static_cast<int32_t>(value));
+ return BitCast<int64_t>(roundtrip_value) == BitCast<int64_t>(value);
+}
+
+
+HConstant::HConstant(Handle<Object> handle, Representation r)
+ : HTemplateInstruction<0>(HType::TypeFromValue(handle)),
+ handle_(handle),
+ unique_id_(),
+ has_smi_value_(false),
+ has_int32_value_(false),
+ has_double_value_(false),
+ has_external_reference_value_(false),
+ is_internalized_string_(false),
+ is_not_in_new_space_(true),
+ is_cell_(false),
+ boolean_value_(handle->BooleanValue()) {
+ if (handle_->IsHeapObject()) {
+ Heap* heap = Handle<HeapObject>::cast(handle)->GetHeap();
+ is_not_in_new_space_ = !heap->InNewSpace(*handle);
+ }
+ if (handle_->IsNumber()) {
+ double n = handle_->Number();
+ has_int32_value_ = IsInteger32(n);
+ int32_value_ = DoubleToInt32(n);
+ has_smi_value_ = has_int32_value_ && Smi::IsValid(int32_value_);
+ double_value_ = n;
+ has_double_value_ = true;
+ } else {
+ is_internalized_string_ = handle_->IsInternalizedString();
+ }
+
+ is_cell_ = !handle_.is_null() &&
+ (handle_->IsCell() || handle_->IsPropertyCell());
+ Initialize(r);
+}
+
+
+HConstant::HConstant(Handle<Object> handle,
+ UniqueValueId unique_id,
+ Representation r,
+ HType type,
+ bool is_internalize_string,
+ bool is_not_in_new_space,
+ bool is_cell,
+ bool boolean_value)
+ : HTemplateInstruction<0>(type),
+ handle_(handle),
+ unique_id_(unique_id),
+ has_smi_value_(false),
+ has_int32_value_(false),
+ has_double_value_(false),
+ has_external_reference_value_(false),
+ is_internalized_string_(is_internalize_string),
+ is_not_in_new_space_(is_not_in_new_space),
+ is_cell_(is_cell),
+ boolean_value_(boolean_value) {
+ ASSERT(!handle.is_null());
+ ASSERT(!type.IsTaggedNumber());
+ Initialize(r);
+}
+
+
+HConstant::HConstant(int32_t integer_value,
+ Representation r,
+ bool is_not_in_new_space,
+ Handle<Object> optional_handle)
+ : handle_(optional_handle),
+ unique_id_(),
+ has_smi_value_(Smi::IsValid(integer_value)),
+ has_int32_value_(true),
+ has_double_value_(true),
+ has_external_reference_value_(false),
+ is_internalized_string_(false),
+ is_not_in_new_space_(is_not_in_new_space),
+ is_cell_(false),
+ boolean_value_(integer_value != 0),
+ int32_value_(integer_value),
+ double_value_(FastI2D(integer_value)) {
+ set_type(has_smi_value_ ? HType::Smi() : HType::TaggedNumber());
+ Initialize(r);
+}
+
+
+HConstant::HConstant(double double_value,
+ Representation r,
+ bool is_not_in_new_space,
+ Handle<Object> optional_handle)
+ : handle_(optional_handle),
+ unique_id_(),
+ has_int32_value_(IsInteger32(double_value)),
+ has_double_value_(true),
+ has_external_reference_value_(false),
+ is_internalized_string_(false),
+ is_not_in_new_space_(is_not_in_new_space),
+ is_cell_(false),
+ boolean_value_(double_value != 0 && !std::isnan(double_value)),
+ int32_value_(DoubleToInt32(double_value)),
+ double_value_(double_value) {
+ has_smi_value_ = has_int32_value_ && Smi::IsValid(int32_value_);
+ set_type(has_smi_value_ ? HType::Smi() : HType::TaggedNumber());
+ Initialize(r);
+}
+
+
+HConstant::HConstant(ExternalReference reference)
+ : HTemplateInstruction<0>(HType::None()),
+ has_smi_value_(false),
+ has_int32_value_(false),
+ has_double_value_(false),
+ has_external_reference_value_(true),
+ is_internalized_string_(false),
+ is_not_in_new_space_(true),
+ is_cell_(false),
+ boolean_value_(true),
+ external_reference_value_(reference) {
+ Initialize(Representation::External());
+}
+
+
+static void PrepareConstant(Handle<Object> object) {
+ if (!object->IsJSObject()) return;
+ Handle<JSObject> js_object = Handle<JSObject>::cast(object);
+ if (!js_object->map()->is_deprecated()) return;
+ JSObject::TryMigrateInstance(js_object);
+}
+
+
+void HConstant::Initialize(Representation r) {
+ if (r.IsNone()) {
+ if (has_smi_value_ && kSmiValueSize == 31) {
+ r = Representation::Smi();
+ } else if (has_int32_value_) {
+ r = Representation::Integer32();
+ } else if (has_double_value_) {
+ r = Representation::Double();
+ } else if (has_external_reference_value_) {
+ r = Representation::External();
+ } else {
+ PrepareConstant(handle_);
+ r = Representation::Tagged();
+ }
+ }
+ set_representation(r);
+ SetFlag(kUseGVN);
+}
+
+
+bool HConstant::EmitAtUses() {
+ ASSERT(IsLinked());
+ if (block()->graph()->has_osr()) {
+ return block()->graph()->IsStandardConstant(this);
+ }
+ if (IsCell()) return false;
+ if (representation().IsDouble()) return false;
+ return true;
+}
+
+
+HConstant* HConstant::CopyToRepresentation(Representation r, Zone* zone) const {
+ if (r.IsSmi() && !has_smi_value_) return NULL;
+ if (r.IsInteger32() && !has_int32_value_) return NULL;
+ if (r.IsDouble() && !has_double_value_) return NULL;
+ if (r.IsExternal() && !has_external_reference_value_) return NULL;
+ if (has_int32_value_) {
+ return new(zone) HConstant(int32_value_, r, is_not_in_new_space_, handle_);
+ }
+ if (has_double_value_) {
+ return new(zone) HConstant(double_value_, r, is_not_in_new_space_, handle_);
+ }
+ if (has_external_reference_value_) {
+ return new(zone) HConstant(external_reference_value_);
+ }
+ ASSERT(!handle_.is_null());
+ return new(zone) HConstant(handle_,
+ unique_id_,
+ r,
+ type_,
+ is_internalized_string_,
+ is_not_in_new_space_,
+ is_cell_,
+ boolean_value_);
+}
+
+
+Maybe<HConstant*> HConstant::CopyToTruncatedInt32(Zone* zone) {
+ HConstant* res = NULL;
+ if (has_int32_value_) {
+ res = new(zone) HConstant(int32_value_,
+ Representation::Integer32(),
+ is_not_in_new_space_,
+ handle_);
+ } else if (has_double_value_) {
+ res = new(zone) HConstant(DoubleToInt32(double_value_),
+ Representation::Integer32(),
+ is_not_in_new_space_,
+ handle_);
+ } else {
+ ASSERT(!HasNumberValue());
+ Maybe<HConstant*> number = CopyToTruncatedNumber(zone);
+ if (number.has_value) return number.value->CopyToTruncatedInt32(zone);
+ }
+ return Maybe<HConstant*>(res != NULL, res);
+}
+
+
+Maybe<HConstant*> HConstant::CopyToTruncatedNumber(Zone* zone) {
+ HConstant* res = NULL;
+ if (handle()->IsBoolean()) {
+ res = handle()->BooleanValue() ?
+ new(zone) HConstant(1) : new(zone) HConstant(0);
+ } else if (handle()->IsUndefined()) {
+ res = new(zone) HConstant(OS::nan_value());
+ } else if (handle()->IsNull()) {
+ res = new(zone) HConstant(0);
+ }
+ return Maybe<HConstant*>(res != NULL, res);
+}
+
+
+void HConstant::PrintDataTo(StringStream* stream) {
+ if (has_int32_value_) {
+ stream->Add("%d ", int32_value_);
+ } else if (has_double_value_) {
+ stream->Add("%f ", FmtElm(double_value_));
+ } else if (has_external_reference_value_) {
+ stream->Add("%p ", reinterpret_cast<void*>(
+ external_reference_value_.address()));
+ } else {
+ handle()->ShortPrint(stream);
+ }
+}
+
+
+void HBinaryOperation::PrintDataTo(StringStream* stream) {
+ left()->PrintNameTo(stream);
+ stream->Add(" ");
+ right()->PrintNameTo(stream);
+ if (CheckFlag(kCanOverflow)) stream->Add(" !");
+ if (CheckFlag(kBailoutOnMinusZero)) stream->Add(" -0?");
+}
+
+
+void HBinaryOperation::InferRepresentation(HInferRepresentationPhase* h_infer) {
+ ASSERT(CheckFlag(kFlexibleRepresentation));
+ Representation new_rep = RepresentationFromInputs();
+ UpdateRepresentation(new_rep, h_infer, "inputs");
+ if (observed_output_representation_.IsNone()) {
+ new_rep = RepresentationFromUses();
+ UpdateRepresentation(new_rep, h_infer, "uses");
+ } else {
+ new_rep = RepresentationFromOutput();
+ UpdateRepresentation(new_rep, h_infer, "output");
+ }
+
+ if (representation().IsSmi() && HasNonSmiUse()) {
+ UpdateRepresentation(
+ Representation::Integer32(), h_infer, "use requirements");
+ }
+}
+
+
+Representation HBinaryOperation::RepresentationFromInputs() {
+ // Determine the worst case of observed input representations and
+ // the currently assumed output representation.
+ Representation rep = representation();
+ for (int i = 1; i <= 2; ++i) {
+ rep = rep.generalize(observed_input_representation(i));
+ }
+ // If any of the actual input representation is more general than what we
+ // have so far but not Tagged, use that representation instead.
+ Representation left_rep = left()->representation();
+ Representation right_rep = right()->representation();
+ if (!left_rep.IsTagged()) rep = rep.generalize(left_rep);
+ if (!right_rep.IsTagged()) rep = rep.generalize(right_rep);
+
+ return rep;
+}
+
+
+bool HBinaryOperation::IgnoreObservedOutputRepresentation(
+ Representation current_rep) {
+ return ((current_rep.IsInteger32() && CheckUsesForFlag(kTruncatingToInt32)) ||
+ (current_rep.IsSmi() && CheckUsesForFlag(kTruncatingToSmi))) &&
+ // Mul in Integer32 mode would be too precise.
+ !this->IsMul();
+}
+
+
+Representation HBinaryOperation::RepresentationFromOutput() {
+ Representation rep = representation();
+ // Consider observed output representation, but ignore it if it's Double,
+ // this instruction is not a division, and all its uses are truncating
+ // to Integer32.
+ if (observed_output_representation_.is_more_general_than(rep) &&
+ !IgnoreObservedOutputRepresentation(rep)) {
+ return observed_output_representation_;
+ }
+ return Representation::None();
+}
+
+
+void HBinaryOperation::AssumeRepresentation(Representation r) {
+ set_observed_input_representation(1, r);
+ set_observed_input_representation(2, r);
+ HValue::AssumeRepresentation(r);
+}
+
+
+void HMathMinMax::InferRepresentation(HInferRepresentationPhase* h_infer) {
+ ASSERT(CheckFlag(kFlexibleRepresentation));
+ Representation new_rep = RepresentationFromInputs();
+ UpdateRepresentation(new_rep, h_infer, "inputs");
+ // Do not care about uses.
+}
+
+
+Range* HBitwise::InferRange(Zone* zone) {
+ if (op() == Token::BIT_XOR) {
+ if (left()->HasRange() && right()->HasRange()) {
+ // The maximum value has the high bit, and all bits below, set:
+ // (1 << high) - 1.
+ // If the range can be negative, the minimum int is a negative number with
+ // the high bit, and all bits below, unset:
+ // -(1 << high).
+ // If it cannot be negative, conservatively choose 0 as minimum int.
+ int64_t left_upper = left()->range()->upper();
+ int64_t left_lower = left()->range()->lower();
+ int64_t right_upper = right()->range()->upper();
+ int64_t right_lower = right()->range()->lower();
+
+ if (left_upper < 0) left_upper = ~left_upper;
+ if (left_lower < 0) left_lower = ~left_lower;
+ if (right_upper < 0) right_upper = ~right_upper;
+ if (right_lower < 0) right_lower = ~right_lower;
+
+ int high = MostSignificantBit(
+ static_cast<uint32_t>(
+ left_upper | left_lower | right_upper | right_lower));
+
+ int64_t limit = 1;
+ limit <<= high;
+ int32_t min = (left()->range()->CanBeNegative() ||
+ right()->range()->CanBeNegative())
+ ? static_cast<int32_t>(-limit) : 0;
+ return new(zone) Range(min, static_cast<int32_t>(limit - 1));
+ }
+ Range* result = HValue::InferRange(zone);
+ result->set_can_be_minus_zero(false);
+ return result;
+ }
+ const int32_t kDefaultMask = static_cast<int32_t>(0xffffffff);
+ int32_t left_mask = (left()->range() != NULL)
+ ? left()->range()->Mask()
+ : kDefaultMask;
+ int32_t right_mask = (right()->range() != NULL)
+ ? right()->range()->Mask()
+ : kDefaultMask;
+ int32_t result_mask = (op() == Token::BIT_AND)
+ ? left_mask & right_mask
+ : left_mask | right_mask;
+ if (result_mask >= 0) return new(zone) Range(0, result_mask);
+
+ Range* result = HValue::InferRange(zone);
+ result->set_can_be_minus_zero(false);
+ return result;
+}
+
+
+Range* HSar::InferRange(Zone* zone) {
+ if (right()->IsConstant()) {
+ HConstant* c = HConstant::cast(right());
+ if (c->HasInteger32Value()) {
+ Range* result = (left()->range() != NULL)
+ ? left()->range()->Copy(zone)
+ : new(zone) Range();
+ result->Sar(c->Integer32Value());
+ return result;
+ }
+ }
+ return HValue::InferRange(zone);
+}
+
+
+Range* HShr::InferRange(Zone* zone) {
+ if (right()->IsConstant()) {
+ HConstant* c = HConstant::cast(right());
+ if (c->HasInteger32Value()) {
+ int shift_count = c->Integer32Value() & 0x1f;
+ if (left()->range()->CanBeNegative()) {
+ // Only compute bounds if the result always fits into an int32.
+ return (shift_count >= 1)
+ ? new(zone) Range(0,
+ static_cast<uint32_t>(0xffffffff) >> shift_count)
+ : new(zone) Range();
+ } else {
+ // For positive inputs we can use the >> operator.
+ Range* result = (left()->range() != NULL)
+ ? left()->range()->Copy(zone)
+ : new(zone) Range();
+ result->Sar(c->Integer32Value());
+ return result;
+ }
+ }
+ }
+ return HValue::InferRange(zone);
+}
+
+
+Range* HShl::InferRange(Zone* zone) {
+ if (right()->IsConstant()) {
+ HConstant* c = HConstant::cast(right());
+ if (c->HasInteger32Value()) {
+ Range* result = (left()->range() != NULL)
+ ? left()->range()->Copy(zone)
+ : new(zone) Range();
+ result->Shl(c->Integer32Value());
+ return result;
+ }
+ }
+ return HValue::InferRange(zone);
+}
+
+
+Range* HLoadNamedField::InferRange(Zone* zone) {
+ if (access().IsStringLength()) {
+ return new(zone) Range(0, String::kMaxLength);
+ }
+ return HValue::InferRange(zone);
+}
+
+
+Range* HLoadKeyed::InferRange(Zone* zone) {
+ switch (elements_kind()) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ return new(zone) Range(0, 255);
+ case EXTERNAL_BYTE_ELEMENTS:
+ return new(zone) Range(-128, 127);
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ return new(zone) Range(0, 255);
+ case EXTERNAL_SHORT_ELEMENTS:
+ return new(zone) Range(-32768, 32767);
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ return new(zone) Range(0, 65535);
+ default:
+ return HValue::InferRange(zone);
+ }
+}
+
+
+void HCompareGeneric::PrintDataTo(StringStream* stream) {
+ stream->Add(Token::Name(token()));
+ stream->Add(" ");
+ HBinaryOperation::PrintDataTo(stream);
+}
+
+
+void HStringCompareAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add(Token::Name(token()));
+ stream->Add(" ");
+ HControlInstruction::PrintDataTo(stream);
+}
+
+
+void HCompareNumericAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add(Token::Name(token()));
+ stream->Add(" ");
+ left()->PrintNameTo(stream);
+ stream->Add(" ");
+ right()->PrintNameTo(stream);
+ HControlInstruction::PrintDataTo(stream);
+}
+
+
+void HCompareObjectEqAndBranch::PrintDataTo(StringStream* stream) {
+ left()->PrintNameTo(stream);
+ stream->Add(" ");
+ right()->PrintNameTo(stream);
+ HControlInstruction::PrintDataTo(stream);
+}
+
+
+void HCompareHoleAndBranch::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ HControlInstruction::PrintDataTo(stream);
+}
+
+
+void HCompareHoleAndBranch::InferRepresentation(
+ HInferRepresentationPhase* h_infer) {
+ ChangeRepresentation(object()->representation());
+}
+
+
+void HGoto::PrintDataTo(StringStream* stream) {
+ stream->Add("B%d", SuccessorAt(0)->block_id());
+}
+
+
+void HCompareNumericAndBranch::InferRepresentation(
+ HInferRepresentationPhase* h_infer) {
+ Representation left_rep = left()->representation();
+ Representation right_rep = right()->representation();
+ Representation observed_left = observed_input_representation(0);
+ Representation observed_right = observed_input_representation(1);
+
+ Representation rep = Representation::None();
+ rep = rep.generalize(observed_left);
+ rep = rep.generalize(observed_right);
+ if (rep.IsNone() || rep.IsSmiOrInteger32()) {
+ if (!left_rep.IsTagged()) rep = rep.generalize(left_rep);
+ if (!right_rep.IsTagged()) rep = rep.generalize(right_rep);
+ } else {
+ rep = Representation::Double();
+ }
+
+ if (rep.IsDouble()) {
+ // According to the ES5 spec (11.9.3, 11.8.5), Equality comparisons (==, ===
+ // and !=) have special handling of undefined, e.g. undefined == undefined
+ // is 'true'. Relational comparisons have a different semantic, first
+ // calling ToPrimitive() on their arguments. The standard Crankshaft
+ // tagged-to-double conversion to ensure the HCompareNumericAndBranch's
+ // inputs are doubles caused 'undefined' to be converted to NaN. That's
+ // compatible out-of-the box with ordered relational comparisons (<, >, <=,
+ // >=). However, for equality comparisons (and for 'in' and 'instanceof'),
+ // it is not consistent with the spec. For example, it would cause undefined
+ // == undefined (should be true) to be evaluated as NaN == NaN
+ // (false). Therefore, any comparisons other than ordered relational
+ // comparisons must cause a deopt when one of their arguments is undefined.
+ // See also v8:1434
+ if (Token::IsOrderedRelationalCompareOp(token_)) {
+ SetFlag(kAllowUndefinedAsNaN);
+ }
+ }
+ ChangeRepresentation(rep);
+}
+
+
+void HParameter::PrintDataTo(StringStream* stream) {
+ stream->Add("%u", index());
+}
+
+
+void HLoadNamedField::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ access_.PrintTo(stream);
+ if (HasTypeCheck()) {
+ stream->Add(" ");
+ typecheck()->PrintNameTo(stream);
+ }
+}
+
+
+HCheckMaps* HCheckMaps::New(Zone* zone,
+ HValue* context,
+ HValue* value,
+ Handle<Map> map,
+ CompilationInfo* info,
+ HValue* typecheck) {
+ HCheckMaps* check_map = new(zone) HCheckMaps(value, zone, typecheck);
+ check_map->Add(map, zone);
+ if (map->CanOmitMapChecks() &&
+ value->IsConstant() &&
+ HConstant::cast(value)->InstanceOf(map)) {
+ check_map->omit(info);
+ }
+ return check_map;
+}
+
+
+void HCheckMaps::FinalizeUniqueValueId() {
+ if (!map_unique_ids_.is_empty()) return;
+ Zone* zone = block()->zone();
+ map_unique_ids_.Initialize(map_set_.length(), zone);
+ for (int i = 0; i < map_set_.length(); i++) {
+ map_unique_ids_.Add(UniqueValueId(map_set_.at(i)), zone);
+ }
+}
+
+
+void HLoadNamedGeneric::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ stream->Add(".");
+ stream->Add(*String::cast(*name())->ToCString());
+}
+
+
+void HLoadKeyed::PrintDataTo(StringStream* stream) {
+ if (!is_external()) {
+ elements()->PrintNameTo(stream);
+ } else {
+ ASSERT(elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
+ elements_kind() <= LAST_EXTERNAL_ARRAY_ELEMENTS_KIND);
+ elements()->PrintNameTo(stream);
+ stream->Add(".");
+ stream->Add(ElementsKindToString(elements_kind()));
+ }
+
+ stream->Add("[");
+ key()->PrintNameTo(stream);
+ if (IsDehoisted()) {
+ stream->Add(" + %d]", index_offset());
+ } else {
+ stream->Add("]");
+ }
+
+ if (HasDependency()) {
+ stream->Add(" ");
+ dependency()->PrintNameTo(stream);
+ }
+
+ if (RequiresHoleCheck()) {
+ stream->Add(" check_hole");
+ }
+}
+
+
+bool HLoadKeyed::UsesMustHandleHole() const {
+ if (IsFastPackedElementsKind(elements_kind())) {
+ return false;
+ }
+
+ if (IsExternalArrayElementsKind(elements_kind())) {
+ return false;
+ }
+
+ if (hole_mode() == ALLOW_RETURN_HOLE) {
+ if (IsFastDoubleElementsKind(elements_kind())) {
+ return AllUsesCanTreatHoleAsNaN();
+ }
+ return true;
+ }
+
+ if (IsFastDoubleElementsKind(elements_kind())) {
+ return false;
+ }
+
+ // Holes are only returned as tagged values.
+ if (!representation().IsTagged()) {
+ return false;
+ }
+
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+ if (!use->IsChange()) return false;
+ }
+
+ return true;
+}
+
+
+bool HLoadKeyed::AllUsesCanTreatHoleAsNaN() const {
+ return IsFastDoubleElementsKind(elements_kind()) &&
+ CheckUsesForFlag(HValue::kAllowUndefinedAsNaN);
+}
+
+
+bool HLoadKeyed::RequiresHoleCheck() const {
+ if (IsFastPackedElementsKind(elements_kind())) {
+ return false;
+ }
+
+ if (IsExternalArrayElementsKind(elements_kind())) {
+ return false;
+ }
+
+ return !UsesMustHandleHole();
+}
+
+
+void HLoadKeyedGeneric::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ stream->Add("[");
+ key()->PrintNameTo(stream);
+ stream->Add("]");
+}
+
+
+HValue* HLoadKeyedGeneric::Canonicalize() {
+ // Recognize generic keyed loads that use property name generated
+ // by for-in statement as a key and rewrite them into fast property load
+ // by index.
+ if (key()->IsLoadKeyed()) {
+ HLoadKeyed* key_load = HLoadKeyed::cast(key());
+ if (key_load->elements()->IsForInCacheArray()) {
+ HForInCacheArray* names_cache =
+ HForInCacheArray::cast(key_load->elements());
+
+ if (names_cache->enumerable() == object()) {
+ HForInCacheArray* index_cache =
+ names_cache->index_cache();
+ HCheckMapValue* map_check =
+ HCheckMapValue::New(block()->graph()->zone(),
+ block()->graph()->GetInvalidContext(),
+ object(),
+ names_cache->map());
+ HInstruction* index = HLoadKeyed::New(
+ block()->graph()->zone(),
+ block()->graph()->GetInvalidContext(),
+ index_cache,
+ key_load->key(),
+ key_load->key(),
+ key_load->elements_kind());
+ map_check->InsertBefore(this);
+ index->InsertBefore(this);
+ HLoadFieldByIndex* load = new(block()->zone()) HLoadFieldByIndex(
+ object(), index);
+ load->InsertBefore(this);
+ return load;
+ }
+ }
+ }
+
+ return this;
+}
+
+
+void HStoreNamedGeneric::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ stream->Add(".");
+ ASSERT(name()->IsString());
+ stream->Add(*String::cast(*name())->ToCString());
+ stream->Add(" = ");
+ value()->PrintNameTo(stream);
+}
+
+
+void HStoreNamedField::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ access_.PrintTo(stream);
+ stream->Add(" = ");
+ value()->PrintNameTo(stream);
+ if (NeedsWriteBarrier()) {
+ stream->Add(" (write-barrier)");
+ }
+ if (has_transition()) {
+ stream->Add(" (transition map %p)", *transition_map());
+ }
+}
+
+
+void HStoreKeyed::PrintDataTo(StringStream* stream) {
+ if (!is_external()) {
+ elements()->PrintNameTo(stream);
+ } else {
+ elements()->PrintNameTo(stream);
+ stream->Add(".");
+ stream->Add(ElementsKindToString(elements_kind()));
+ ASSERT(elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
+ elements_kind() <= LAST_EXTERNAL_ARRAY_ELEMENTS_KIND);
+ }
+
+ stream->Add("[");
+ key()->PrintNameTo(stream);
+ if (IsDehoisted()) {
+ stream->Add(" + %d] = ", index_offset());
+ } else {
+ stream->Add("] = ");
+ }
+
+ value()->PrintNameTo(stream);
+}
+
+
+void HStoreKeyedGeneric::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ stream->Add("[");
+ key()->PrintNameTo(stream);
+ stream->Add("] = ");
+ value()->PrintNameTo(stream);
+}
+
+
+void HTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ ElementsKind from_kind = original_map()->elements_kind();
+ ElementsKind to_kind = transitioned_map()->elements_kind();
+ stream->Add(" %p [%s] -> %p [%s]",
+ *original_map(),
+ ElementsAccessor::ForKind(from_kind)->name(),
+ *transitioned_map(),
+ ElementsAccessor::ForKind(to_kind)->name());
+}
+
+
+void HLoadGlobalCell::PrintDataTo(StringStream* stream) {
+ stream->Add("[%p]", *cell());
+ if (!details_.IsDontDelete()) stream->Add(" (deleteable)");
+ if (details_.IsReadOnly()) stream->Add(" (read-only)");
+}
+
+
+bool HLoadGlobalCell::RequiresHoleCheck() const {
+ 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;
+}
+
+
+void HLoadGlobalGeneric::PrintDataTo(StringStream* stream) {
+ stream->Add("%o ", *name());
+}
+
+
+void HInnerAllocatedObject::PrintDataTo(StringStream* stream) {
+ base_object()->PrintNameTo(stream);
+ stream->Add(" offset %d", offset());
+}
+
+
+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)");
+}
+
+
+void HStoreGlobalGeneric::PrintDataTo(StringStream* stream) {
+ stream->Add("%o = ", *name());
+ value()->PrintNameTo(stream);
+}
+
+
+void HLoadContextSlot::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+ stream->Add("[%d]", slot_index());
+}
+
+
+void HStoreContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintNameTo(stream);
+ stream->Add("[%d] = ", slot_index());
+ value()->PrintNameTo(stream);
+}
+
+
+// Implementation of type inference and type conversions. Calculates
+// the inferred type of this instruction based on the input operands.
+
+HType HValue::CalculateInferredType() {
+ return type_;
+}
+
+
+HType HPhi::CalculateInferredType() {
+ if (OperandCount() == 0) return HType::Tagged();
+ HType result = OperandAt(0)->type();
+ for (int i = 1; i < OperandCount(); ++i) {
+ HType current = OperandAt(i)->type();
+ result = result.Combine(current);
+ }
+ return result;
+}
+
+
+HType HChange::CalculateInferredType() {
+ if (from().IsDouble() && to().IsTagged()) return HType::HeapNumber();
+ return type();
+}
+
+
+Representation HUnaryMathOperation::RepresentationFromInputs() {
+ Representation rep = representation();
+ // If any of the actual input representation is more general than what we
+ // have so far but not Tagged, use that representation instead.
+ Representation input_rep = value()->representation();
+ if (!input_rep.IsTagged()) {
+ rep = rep.generalize(input_rep);
+ }
+ return rep;
+}
+
+
+void HAllocate::HandleSideEffectDominator(GVNFlag side_effect,
+ HValue* dominator) {
+ ASSERT(side_effect == kChangesNewSpacePromotion);
+ if (!FLAG_use_allocation_folding) return;
+
+ // Try to fold allocations together with their dominating allocations.
+ if (!dominator->IsAllocate()) {
+ if (FLAG_trace_allocation_folding) {
+ PrintF("#%d (%s) cannot fold into #%d (%s)\n",
+ id(), Mnemonic(), dominator->id(), dominator->Mnemonic());
+ }
+ return;
+ }
+
+ HAllocate* dominator_allocate_instr = HAllocate::cast(dominator);
+ HValue* dominator_size = dominator_allocate_instr->size();
+ HValue* current_size = size();
+ // We can just fold allocations that are guaranteed in new space.
+ // TODO(hpayer): Add support for non-constant allocation in dominator.
+ if (!IsNewSpaceAllocation() || !current_size->IsInteger32Constant() ||
+ !dominator_allocate_instr->IsNewSpaceAllocation() ||
+ !dominator_size->IsInteger32Constant()) {
+ if (FLAG_trace_allocation_folding) {
+ PrintF("#%d (%s) cannot fold into #%d (%s)\n",
+ id(), Mnemonic(), dominator->id(), dominator->Mnemonic());
+ }
+ return;
+ }
+
+ // First update the size of the dominator allocate instruction.
+ int32_t dominator_size_constant =
+ HConstant::cast(dominator_size)->GetInteger32Constant();
+ int32_t current_size_constant =
+ HConstant::cast(current_size)->GetInteger32Constant();
+ int32_t new_dominator_size = dominator_size_constant + current_size_constant;
+
+ if (MustAllocateDoubleAligned()) {
+ if (!dominator_allocate_instr->MustAllocateDoubleAligned()) {
+ dominator_allocate_instr->MakeDoubleAligned();
+ }
+ if ((dominator_size_constant & kDoubleAlignmentMask) != 0) {
+ dominator_size_constant += kDoubleSize / 2;
+ new_dominator_size += kDoubleSize / 2;
+ }
+ }
+
+ if (new_dominator_size > Page::kMaxNonCodeHeapObjectSize) {
+ if (FLAG_trace_allocation_folding) {
+ PrintF("#%d (%s) cannot fold into #%d (%s) due to size: %d\n",
+ id(), Mnemonic(), dominator->id(), dominator->Mnemonic(),
+ new_dominator_size);
+ }
+ return;
+ }
+ HBasicBlock* block = dominator->block();
+ Zone* zone = block->zone();
+ HInstruction* new_dominator_size_constant =
+ HConstant::New(zone, context(), new_dominator_size);
+ new_dominator_size_constant->InsertBefore(dominator_allocate_instr);
+ dominator_allocate_instr->UpdateSize(new_dominator_size_constant);
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ dominator_allocate_instr->MakePrefillWithFiller();
+ }
+#endif
+
+ // After that replace the dominated allocate instruction.
+ HInstruction* dominated_allocate_instr =
+ HInnerAllocatedObject::New(zone,
+ context(),
+ dominator_allocate_instr,
+ dominator_size_constant,
+ type());
+ dominated_allocate_instr->InsertBefore(this);
+ DeleteAndReplaceWith(dominated_allocate_instr);
+ if (FLAG_trace_allocation_folding) {
+ PrintF("#%d (%s) folded into #%d (%s)\n",
+ id(), Mnemonic(), dominator->id(), dominator->Mnemonic());
+ }
+}
+
+
+void HAllocate::PrintDataTo(StringStream* stream) {
+ size()->PrintNameTo(stream);
+ stream->Add(" (");
+ if (IsNewSpaceAllocation()) stream->Add("N");
+ if (IsOldPointerSpaceAllocation()) stream->Add("P");
+ if (IsOldDataSpaceAllocation()) stream->Add("D");
+ if (MustAllocateDoubleAligned()) stream->Add("A");
+ if (MustPrefillWithFiller()) stream->Add("F");
+ stream->Add(")");
+}
+
+
+HValue* HUnaryMathOperation::EnsureAndPropagateNotMinusZero(
+ BitVector* visited) {
+ visited->Add(id());
+ if (representation().IsSmiOrInteger32() &&
+ !value()->representation().Equals(representation())) {
+ if (value()->range() == NULL || value()->range()->CanBeMinusZero()) {
+ SetFlag(kBailoutOnMinusZero);
+ }
+ }
+ if (RequiredInputRepresentation(0).IsSmiOrInteger32() &&
+ representation().Equals(RequiredInputRepresentation(0))) {
+ return value();
+ }
+ return NULL;
+}
+
+
+HValue* HChange::EnsureAndPropagateNotMinusZero(BitVector* visited) {
+ visited->Add(id());
+ if (from().IsSmiOrInteger32()) return NULL;
+ if (CanTruncateToInt32()) return NULL;
+ if (value()->range() == NULL || value()->range()->CanBeMinusZero()) {
+ SetFlag(kBailoutOnMinusZero);
+ }
+ ASSERT(!from().IsSmiOrInteger32() || !to().IsSmiOrInteger32());
+ return NULL;
+}
+
+
+HValue* HForceRepresentation::EnsureAndPropagateNotMinusZero(
+ BitVector* visited) {
+ visited->Add(id());
+ return value();
+}
+
+
+HValue* HMod::EnsureAndPropagateNotMinusZero(BitVector* visited) {
+ visited->Add(id());
+ if (range() == NULL || range()->CanBeMinusZero()) {
+ SetFlag(kBailoutOnMinusZero);
+ return left();
+ }
+ return NULL;
+}
+
+
+HValue* HDiv::EnsureAndPropagateNotMinusZero(BitVector* visited) {
+ visited->Add(id());
+ if (range() == NULL || range()->CanBeMinusZero()) {
+ SetFlag(kBailoutOnMinusZero);
+ }
+ return NULL;
+}
+
+
+HValue* HMathFloorOfDiv::EnsureAndPropagateNotMinusZero(BitVector* visited) {
+ visited->Add(id());
+ SetFlag(kBailoutOnMinusZero);
+ return NULL;
+}
+
+
+HValue* HMul::EnsureAndPropagateNotMinusZero(BitVector* visited) {
+ visited->Add(id());
+ if (range() == NULL || range()->CanBeMinusZero()) {
+ SetFlag(kBailoutOnMinusZero);
+ }
+ return NULL;
+}
+
+
+HValue* HSub::EnsureAndPropagateNotMinusZero(BitVector* visited) {
+ visited->Add(id());
+ // Propagate to the left argument. If the left argument cannot be -0, then
+ // the result of the add operation cannot be either.
+ if (range() == NULL || range()->CanBeMinusZero()) {
+ return left();
+ }
+ return NULL;
+}
+
+
+HValue* HAdd::EnsureAndPropagateNotMinusZero(BitVector* visited) {
+ visited->Add(id());
+ // Propagate to the left argument. If the left argument cannot be -0, then
+ // the result of the sub operation cannot be either.
+ if (range() == NULL || range()->CanBeMinusZero()) {
+ return left();
+ }
+ return NULL;
+}
+
+
+bool HStoreKeyed::NeedsCanonicalization() {
+ // If value is an integer or smi or comes from the result of a keyed load or
+ // constant then it is either be a non-hole value or in the case of a constant
+ // the hole is only being stored explicitly: no need for canonicalization.
+ //
+ // The exception to that is keyed loads from external float or double arrays:
+ // these can load arbitrary representation of NaN.
+
+ if (value()->IsConstant()) {
+ return false;
+ }
+
+ if (value()->IsLoadKeyed()) {
+ return IsExternalFloatOrDoubleElementsKind(
+ HLoadKeyed::cast(value())->elements_kind());
+ }
+
+ if (value()->IsChange()) {
+ if (HChange::cast(value())->from().IsSmiOrInteger32()) {
+ return false;
+ }
+ if (HChange::cast(value())->value()->type().IsSmi()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+#define H_CONSTANT_INT(val) \
+HConstant::New(zone, context, static_cast<int32_t>(val))
+#define H_CONSTANT_DOUBLE(val) \
+HConstant::New(zone, context, static_cast<double>(val))
+
+#define DEFINE_NEW_H_SIMPLE_ARITHMETIC_INSTR(HInstr, op) \
+HInstruction* HInstr::New( \
+ Zone* zone, HValue* context, HValue* left, HValue* right) { \
+ if (FLAG_fold_constants && 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_INT(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* HStringAdd::New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right,
+ StringAddFlags flags) {
+ if (FLAG_fold_constants && left->IsConstant() && right->IsConstant()) {
+ HConstant* c_right = HConstant::cast(right);
+ HConstant* c_left = HConstant::cast(left);
+ if (c_left->HasStringValue() && c_right->HasStringValue()) {
+ Handle<String> concat = zone->isolate()->factory()->NewFlatConcatString(
+ c_left->StringValue(), c_right->StringValue());
+ return HConstant::New(zone, context, concat);
+ }
+ }
+ return new(zone) HStringAdd(context, left, right, flags);
+}
+
+
+HInstruction* HStringCharFromCode::New(
+ Zone* zone, HValue* context, HValue* char_code) {
+ if (FLAG_fold_constants && char_code->IsConstant()) {
+ HConstant* c_code = HConstant::cast(char_code);
+ Isolate* isolate = Isolate::Current();
+ if (c_code->HasNumberValue()) {
+ if (std::isfinite(c_code->DoubleValue())) {
+ uint32_t code = c_code->NumberValueAsInteger32() & 0xffff;
+ return HConstant::New(zone, context,
+ LookupSingleCharacterStringFromCode(isolate, code));
+ }
+ return HConstant::New(zone, context, isolate->factory()->empty_string());
+ }
+ }
+ return new(zone) HStringCharFromCode(context, char_code);
+}
+
+
+HInstruction* HUnaryMathOperation::New(
+ Zone* zone, HValue* context, HValue* value, BuiltinFunctionId op) {
+ do {
+ if (!FLAG_fold_constants) break;
+ if (!value->IsConstant()) break;
+ HConstant* constant = HConstant::cast(value);
+ if (!constant->HasNumberValue()) break;
+ double d = constant->DoubleValue();
+ if (std::isnan(d)) { // NaN poisons everything.
+ return H_CONSTANT_DOUBLE(OS::nan_value());
+ }
+ if (std::isinf(d)) { // +Infinity and -Infinity.
+ switch (op) {
+ case kMathSin:
+ case kMathCos:
+ case kMathTan:
+ return H_CONSTANT_DOUBLE(OS::nan_value());
+ case kMathExp:
+ return H_CONSTANT_DOUBLE((d > 0.0) ? d : 0.0);
+ case kMathLog:
+ case kMathSqrt:
+ return H_CONSTANT_DOUBLE((d > 0.0) ? d : OS::nan_value());
+ case kMathPowHalf:
+ case kMathAbs:
+ return H_CONSTANT_DOUBLE((d > 0.0) ? d : -d);
+ case kMathRound:
+ case kMathFloor:
+ return H_CONSTANT_DOUBLE(d);
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ switch (op) {
+ case kMathSin:
+ return H_CONSTANT_DOUBLE(fast_sin(d));
+ case kMathCos:
+ return H_CONSTANT_DOUBLE(fast_cos(d));
+ case kMathTan:
+ return H_CONSTANT_DOUBLE(fast_tan(d));
+ case kMathExp:
+ return H_CONSTANT_DOUBLE(fast_exp(d));
+ case kMathLog:
+ return H_CONSTANT_DOUBLE(fast_log(d));
+ case kMathSqrt:
+ return H_CONSTANT_DOUBLE(fast_sqrt(d));
+ case kMathPowHalf:
+ return H_CONSTANT_DOUBLE(power_double_double(d, 0.5));
+ case kMathAbs:
+ return H_CONSTANT_DOUBLE((d >= 0.0) ? d + 0.0 : -d);
+ case kMathRound:
+ // -0.5 .. -0.0 round to -0.0.
+ if ((d >= -0.5 && Double(d).Sign() < 0)) return H_CONSTANT_DOUBLE(-0.0);
+ // Doubles are represented as Significant * 2 ^ Exponent. If the
+ // Exponent is not negative, the double value is already an integer.
+ if (Double(d).Exponent() >= 0) return H_CONSTANT_DOUBLE(d);
+ return H_CONSTANT_DOUBLE(floor(d + 0.5));
+ case kMathFloor:
+ return H_CONSTANT_DOUBLE(floor(d));
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } while (false);
+ return new(zone) HUnaryMathOperation(context, value, op);
+}
+
+
+HInstruction* HPower::New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right) {
+ if (FLAG_fold_constants && left->IsConstant() && right->IsConstant()) {
+ HConstant* c_left = HConstant::cast(left);
+ HConstant* c_right = HConstant::cast(right);
+ if (c_left->HasNumberValue() && c_right->HasNumberValue()) {
+ double result = power_helper(c_left->DoubleValue(),
+ c_right->DoubleValue());
+ return H_CONSTANT_DOUBLE(std::isnan(result) ? OS::nan_value() : result);
+ }
+ }
+ return new(zone) HPower(left, right);
+}
+
+
+HInstruction* HMathMinMax::New(
+ Zone* zone, HValue* context, HValue* left, HValue* right, Operation op) {
+ if (FLAG_fold_constants && left->IsConstant() && right->IsConstant()) {
+ HConstant* c_left = HConstant::cast(left);
+ HConstant* c_right = HConstant::cast(right);
+ if (c_left->HasNumberValue() && c_right->HasNumberValue()) {
+ double d_left = c_left->DoubleValue();
+ double d_right = c_right->DoubleValue();
+ if (op == kMathMin) {
+ if (d_left > d_right) return H_CONSTANT_DOUBLE(d_right);
+ if (d_left < d_right) return H_CONSTANT_DOUBLE(d_left);
+ if (d_left == d_right) {
+ // Handle +0 and -0.
+ return H_CONSTANT_DOUBLE((Double(d_left).Sign() == -1) ? d_left
+ : d_right);
+ }
+ } else {
+ if (d_left < d_right) return H_CONSTANT_DOUBLE(d_right);
+ if (d_left > d_right) return H_CONSTANT_DOUBLE(d_left);
+ if (d_left == d_right) {
+ // Handle +0 and -0.
+ return H_CONSTANT_DOUBLE((Double(d_left).Sign() == -1) ? d_right
+ : d_left);
+ }
+ }
+ // All comparisons failed, must be NaN.
+ return H_CONSTANT_DOUBLE(OS::nan_value());
+ }
+ }
+ return new(zone) HMathMinMax(context, left, right, op);
+}
+
+
+HInstruction* HMod::New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right,
+ Maybe<int> fixed_right_arg) {
+ if (FLAG_fold_constants && 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 (dividend == kMinInt && divisor == -1) {
+ return H_CONSTANT_DOUBLE(-0.0);
+ }
+ if (divisor != 0) {
+ int32_t res = dividend % divisor;
+ if ((res == 0) && (dividend < 0)) {
+ return H_CONSTANT_DOUBLE(-0.0);
+ }
+ return H_CONSTANT_INT(res);
+ }
+ }
+ }
+ return new(zone) HMod(context, left, right, fixed_right_arg);
+}
+
+
+HInstruction* HDiv::New(
+ Zone* zone, HValue* context, HValue* left, HValue* right) {
+ // If left and right are constant values, try to return a constant value.
+ if (FLAG_fold_constants && 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_INT(double_res);
+ }
+ return H_CONSTANT_DOUBLE(double_res);
+ } else {
+ int sign = Double(c_left->DoubleValue()).Sign() *
+ Double(c_right->DoubleValue()).Sign(); // Right could be -0.
+ return H_CONSTANT_DOUBLE(sign * V8_INFINITY);
+ }
+ }
+ }
+ return new(zone) HDiv(context, left, right);
+}
+
+
+HInstruction* HBitwise::New(
+ Zone* zone, HValue* context, Token::Value op, HValue* left, HValue* right) {
+ if (FLAG_fold_constants && 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_INT(result);
+ }
+ }
+ return new(zone) HBitwise(context, op, left, right);
+}
+
+
+#define DEFINE_NEW_H_BITWISE_INSTR(HInstr, result) \
+HInstruction* HInstr::New( \
+ Zone* zone, HValue* context, HValue* left, HValue* right) { \
+ if (FLAG_fold_constants && 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_INT(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::New(
+ Zone* zone, HValue* context, HValue* left, HValue* right) {
+ if (FLAG_fold_constants && 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<uint32_t>(left_val));
+ }
+ return H_CONSTANT_INT(static_cast<uint32_t>(left_val) >> right_val);
+ }
+ }
+ return new(zone) HShr(context, left, right);
+}
+
+
+#undef H_CONSTANT_INT
+#undef H_CONSTANT_DOUBLE
+
+
+void HBitwise::PrintDataTo(StringStream* stream) {
+ stream->Add(Token::Name(op_));
+ stream->Add(" ");
+ HBitwiseBinaryOperation::PrintDataTo(stream);
+}
+
+
+void HPhi::SimplifyConstantInputs() {
+ // Convert constant inputs to integers when all uses are truncating.
+ // This must happen before representation inference takes place.
+ if (!CheckUsesForFlag(kTruncatingToInt32)) return;
+ for (int i = 0; i < OperandCount(); ++i) {
+ if (!OperandAt(i)->IsConstant()) return;
+ }
+ HGraph* graph = block()->graph();
+ for (int i = 0; i < OperandCount(); ++i) {
+ HConstant* operand = HConstant::cast(OperandAt(i));
+ if (operand->HasInteger32Value()) {
+ continue;
+ } else if (operand->HasDoubleValue()) {
+ HConstant* integer_input =
+ HConstant::New(graph->zone(), graph->GetInvalidContext(),
+ DoubleToInt32(operand->DoubleValue()));
+ integer_input->InsertAfter(operand);
+ SetOperandAt(i, integer_input);
+ } else if (operand->HasBooleanValue()) {
+ SetOperandAt(i, operand->BooleanValue() ? graph->GetConstant1()
+ : graph->GetConstant0());
+ } else if (operand->ImmortalImmovable()) {
+ SetOperandAt(i, graph->GetConstant0());
+ }
+ }
+ // Overwrite observed input representations because they are likely Tagged.
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+ if (use->IsBinaryOperation()) {
+ HBinaryOperation::cast(use)->set_observed_input_representation(
+ it.index(), Representation::Smi());
+ }
+ }
+}
+
+
+void HPhi::InferRepresentation(HInferRepresentationPhase* h_infer) {
+ ASSERT(CheckFlag(kFlexibleRepresentation));
+ Representation new_rep = RepresentationFromInputs();
+ UpdateRepresentation(new_rep, h_infer, "inputs");
+ new_rep = RepresentationFromUses();
+ UpdateRepresentation(new_rep, h_infer, "uses");
+ new_rep = RepresentationFromUseRequirements();
+ UpdateRepresentation(new_rep, h_infer, "use requirements");
+}
+
+
+Representation HPhi::RepresentationFromInputs() {
+ Representation r = Representation::None();
+ for (int i = 0; i < OperandCount(); ++i) {
+ r = r.generalize(OperandAt(i)->KnownOptimalRepresentation());
+ }
+ return r;
+}
+
+
+// Returns a representation if all uses agree on the same representation.
+// Integer32 is also returned when some uses are Smi but others are Integer32.
+Representation HValue::RepresentationFromUseRequirements() {
+ Representation rep = Representation::None();
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ // We check for observed_input_representation elsewhere.
+ Representation use_rep =
+ it.value()->RequiredInputRepresentation(it.index());
+ if (rep.IsNone()) {
+ rep = use_rep;
+ continue;
+ }
+ if (use_rep.IsNone() || rep.Equals(use_rep)) continue;
+ if (rep.generalize(use_rep).IsInteger32()) {
+ rep = Representation::Integer32();
+ continue;
+ }
+ return Representation::None();
+ }
+ return rep;
+}
+
+
+bool HValue::HasNonSmiUse() {
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ // We check for observed_input_representation elsewhere.
+ Representation use_rep =
+ it.value()->RequiredInputRepresentation(it.index());
+ if (!use_rep.IsNone() &&
+ !use_rep.IsSmi() &&
+ !use_rep.IsTagged()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Node-specific verification code is only included in debug mode.
+#ifdef DEBUG
+
+void HPhi::Verify() {
+ ASSERT(OperandCount() == block()->predecessors()->length());
+ for (int i = 0; i < OperandCount(); ++i) {
+ HValue* value = OperandAt(i);
+ HBasicBlock* defining_block = value->block();
+ HBasicBlock* predecessor_block = block()->predecessors()->at(i);
+ ASSERT(defining_block == predecessor_block ||
+ defining_block->Dominates(predecessor_block));
+ }
+}
+
+
+void HSimulate::Verify() {
+ HInstruction::Verify();
+ ASSERT(HasAstId());
+}
+
+
+void HCheckHeapObject::Verify() {
+ HInstruction::Verify();
+ ASSERT(HasNoUses());
+}
+
+
+void HCheckFunction::Verify() {
+ HInstruction::Verify();
+ ASSERT(HasNoUses());
+}
+
+#endif
+
+
+HObjectAccess HObjectAccess::ForFixedArrayHeader(int offset) {
+ ASSERT(offset >= 0);
+ ASSERT(offset < FixedArray::kHeaderSize);
+ if (offset == FixedArray::kLengthOffset) return ForFixedArrayLength();
+ return HObjectAccess(kInobject, offset);
+}
+
+
+HObjectAccess HObjectAccess::ForJSObjectOffset(int offset,
+ Representation representation) {
+ ASSERT(offset >= 0);
+ Portion portion = kInobject;
+
+ if (offset == JSObject::kElementsOffset) {
+ portion = kElementsPointer;
+ } else if (offset == JSObject::kMapOffset) {
+ portion = kMaps;
+ }
+ return HObjectAccess(portion, offset, representation);
+}
+
+
+HObjectAccess HObjectAccess::ForJSArrayOffset(int offset) {
+ ASSERT(offset >= 0);
+ Portion portion = kInobject;
+
+ if (offset == JSObject::kElementsOffset) {
+ portion = kElementsPointer;
+ } else if (offset == JSArray::kLengthOffset) {
+ portion = kArrayLengths;
+ } else if (offset == JSObject::kMapOffset) {
+ portion = kMaps;
+ }
+ return HObjectAccess(portion, offset);
+}
+
+
+HObjectAccess HObjectAccess::ForBackingStoreOffset(int offset,
+ Representation representation) {
+ ASSERT(offset >= 0);
+ return HObjectAccess(kBackingStore, offset, representation);
+}
+
+
+HObjectAccess HObjectAccess::ForField(Handle<Map> map,
+ LookupResult *lookup, Handle<String> name) {
+ ASSERT(lookup->IsField() || lookup->IsTransitionToField(*map));
+ int index;
+ Representation representation;
+ if (lookup->IsField()) {
+ index = lookup->GetLocalFieldIndexFromMap(*map);
+ representation = lookup->representation();
+ } else {
+ Map* transition = lookup->GetTransitionMapFromMap(*map);
+ int descriptor = transition->LastAdded();
+ index = transition->instance_descriptors()->GetFieldIndex(descriptor) -
+ map->inobject_properties();
+ PropertyDetails details =
+ transition->instance_descriptors()->GetDetails(descriptor);
+ representation = details.representation();
+ }
+ if (index < 0) {
+ // Negative property indices are in-object properties, indexed
+ // from the end of the fixed part of the object.
+ int offset = (index * kPointerSize) + map->instance_size();
+ return HObjectAccess(kInobject, offset, representation);
+ } else {
+ // Non-negative property indices are in the properties array.
+ int offset = (index * kPointerSize) + FixedArray::kHeaderSize;
+ return HObjectAccess(kBackingStore, offset, representation, name);
+ }
+}
+
+
+HObjectAccess HObjectAccess::ForCellPayload(Isolate* isolate) {
+ return HObjectAccess(
+ kInobject, Cell::kValueOffset, Representation::Tagged(),
+ Handle<String>(isolate->heap()->cell_value_string()));
+}
+
+
+void HObjectAccess::SetGVNFlags(HValue *instr, bool is_store) {
+ // set the appropriate GVN flags for a given load or store instruction
+ if (is_store) {
+ // track dominating allocations in order to eliminate write barriers
+ instr->SetGVNFlag(kDependsOnNewSpacePromotion);
+ instr->SetFlag(HValue::kTrackSideEffectDominators);
+ } else {
+ // try to GVN loads, but don't hoist above map changes
+ instr->SetFlag(HValue::kUseGVN);
+ instr->SetGVNFlag(kDependsOnMaps);
+ }
+
+ switch (portion()) {
+ case kArrayLengths:
+ instr->SetGVNFlag(is_store
+ ? kChangesArrayLengths : kDependsOnArrayLengths);
+ break;
+ case kStringLengths:
+ instr->SetGVNFlag(is_store
+ ? kChangesStringLengths : kDependsOnStringLengths);
+ break;
+ case kInobject:
+ instr->SetGVNFlag(is_store
+ ? kChangesInobjectFields : kDependsOnInobjectFields);
+ break;
+ case kDouble:
+ instr->SetGVNFlag(is_store
+ ? kChangesDoubleFields : kDependsOnDoubleFields);
+ break;
+ case kBackingStore:
+ instr->SetGVNFlag(is_store
+ ? kChangesBackingStoreFields : kDependsOnBackingStoreFields);
+ break;
+ case kElementsPointer:
+ instr->SetGVNFlag(is_store
+ ? kChangesElementsPointer : kDependsOnElementsPointer);
+ break;
+ case kMaps:
+ instr->SetGVNFlag(is_store
+ ? kChangesMaps : kDependsOnMaps);
+ break;
+ case kExternalMemory:
+ instr->SetGVNFlag(is_store
+ ? kChangesExternalMemory : kDependsOnExternalMemory);
+ break;
+ }
+}
+
+
+void HObjectAccess::PrintTo(StringStream* stream) {
+ stream->Add(".");
+
+ switch (portion()) {
+ case kArrayLengths:
+ case kStringLengths:
+ stream->Add("%length");
+ break;
+ case kElementsPointer:
+ stream->Add("%elements");
+ break;
+ case kMaps:
+ stream->Add("%map");
+ break;
+ case kDouble: // fall through
+ case kInobject:
+ if (!name_.is_null()) stream->Add(*String::cast(*name_)->ToCString());
+ stream->Add("[in-object]");
+ break;
+ case kBackingStore:
+ if (!name_.is_null()) stream->Add(*String::cast(*name_)->ToCString());
+ stream->Add("[backing-store]");
+ break;
+ case kExternalMemory:
+ stream->Add("[external-memory]");
+ break;
+ }
+
+ stream->Add("@%d", offset());
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-instructions.h b/chromium/v8/src/hydrogen-instructions.h
new file mode 100644
index 00000000000..41f9d0d5ccd
--- /dev/null
+++ b/chromium/v8/src/hydrogen-instructions.h
@@ -0,0 +1,6797 @@
+// Copyright 2012 the V8 project authors. 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_HYDROGEN_INSTRUCTIONS_H_
+#define V8_HYDROGEN_INSTRUCTIONS_H_
+
+#include "v8.h"
+
+#include "allocation.h"
+#include "code-stubs.h"
+#include "data-flow.h"
+#include "deoptimizer.h"
+#include "small-pointer-list.h"
+#include "string-stream.h"
+#include "v8conversions.h"
+#include "v8utils.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class HBasicBlock;
+class HEnvironment;
+class HInferRepresentationPhase;
+class HInstruction;
+class HLoopInformation;
+class HValue;
+class LInstruction;
+class LChunkBuilder;
+
+
+#define HYDROGEN_ABSTRACT_INSTRUCTION_LIST(V) \
+ V(ArithmeticBinaryOperation) \
+ V(BinaryOperation) \
+ V(BitwiseBinaryOperation) \
+ V(ControlInstruction) \
+ V(Instruction) \
+
+
+#define HYDROGEN_CONCRETE_INSTRUCTION_LIST(V) \
+ V(AbnormalExit) \
+ V(AccessArgumentsAt) \
+ V(Add) \
+ V(Allocate) \
+ V(ApplyArguments) \
+ V(ArgumentsElements) \
+ V(ArgumentsLength) \
+ V(ArgumentsObject) \
+ V(Bitwise) \
+ V(BlockEntry) \
+ V(BoundsCheck) \
+ V(BoundsCheckBaseIndexInformation) \
+ V(Branch) \
+ V(CallConstantFunction) \
+ V(CallFunction) \
+ V(CallGlobal) \
+ V(CallKeyed) \
+ V(CallKnownGlobal) \
+ V(CallNamed) \
+ V(CallNew) \
+ V(CallNewArray) \
+ V(CallRuntime) \
+ V(CallStub) \
+ V(CapturedObject) \
+ V(Change) \
+ V(CheckFunction) \
+ V(CheckHeapObject) \
+ V(CheckInstanceType) \
+ V(CheckMaps) \
+ V(CheckMapValue) \
+ V(CheckSmi) \
+ V(ClampToUint8) \
+ V(ClassOfTestAndBranch) \
+ V(CompareNumericAndBranch) \
+ V(CompareHoleAndBranch) \
+ V(CompareGeneric) \
+ V(CompareObjectEqAndBranch) \
+ V(CompareMap) \
+ V(Constant) \
+ V(Context) \
+ V(DateField) \
+ V(DebugBreak) \
+ V(DeclareGlobals) \
+ V(Deoptimize) \
+ V(Div) \
+ V(DummyUse) \
+ V(ElementsKind) \
+ V(EnterInlined) \
+ V(EnvironmentMarker) \
+ V(ForceRepresentation) \
+ V(ForInCacheArray) \
+ V(ForInPrepareMap) \
+ V(FunctionLiteral) \
+ V(GetCachedArrayIndex) \
+ V(GlobalObject) \
+ V(GlobalReceiver) \
+ V(Goto) \
+ V(HasCachedArrayIndexAndBranch) \
+ V(HasInstanceTypeAndBranch) \
+ V(InnerAllocatedObject) \
+ V(InstanceOf) \
+ V(InstanceOfKnownGlobal) \
+ V(InstanceSize) \
+ V(InvokeFunction) \
+ V(IsConstructCallAndBranch) \
+ V(IsObjectAndBranch) \
+ V(IsNumberAndBranch) \
+ V(IsStringAndBranch) \
+ V(IsSmiAndBranch) \
+ V(IsUndetectableAndBranch) \
+ V(LeaveInlined) \
+ V(LoadContextSlot) \
+ V(LoadExternalArrayPointer) \
+ V(LoadFieldByIndex) \
+ V(LoadFunctionPrototype) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
+ V(LoadKeyed) \
+ V(LoadKeyedGeneric) \
+ V(LoadNamedField) \
+ V(LoadNamedGeneric) \
+ V(MapEnumLength) \
+ V(MathFloorOfDiv) \
+ V(MathMinMax) \
+ V(Mod) \
+ V(Mul) \
+ V(OsrEntry) \
+ V(OuterContext) \
+ V(Parameter) \
+ V(Power) \
+ V(PushArgument) \
+ V(Random) \
+ V(RegExpLiteral) \
+ V(Return) \
+ V(Ror) \
+ V(Sar) \
+ V(SeqStringSetChar) \
+ V(Shl) \
+ V(Shr) \
+ V(Simulate) \
+ V(StackCheck) \
+ V(StoreContextSlot) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
+ V(StoreKeyed) \
+ V(StoreKeyedGeneric) \
+ V(StoreNamedField) \
+ V(StoreNamedGeneric) \
+ V(StringAdd) \
+ V(StringCharCodeAt) \
+ V(StringCharFromCode) \
+ V(StringCompareAndBranch) \
+ V(Sub) \
+ V(ThisFunction) \
+ V(Throw) \
+ V(ToFastProperties) \
+ V(TransitionElementsKind) \
+ V(TrapAllocationMemento) \
+ V(Typeof) \
+ V(TypeofIsAndBranch) \
+ V(UnaryMathOperation) \
+ V(UnknownOSRValue) \
+ V(UseConst) \
+ V(ValueOf) \
+ V(WrapReceiver)
+
+#define GVN_TRACKED_FLAG_LIST(V) \
+ V(Maps) \
+ V(NewSpacePromotion)
+
+#define GVN_UNTRACKED_FLAG_LIST(V) \
+ V(ArrayElements) \
+ V(ArrayLengths) \
+ V(StringLengths) \
+ V(BackingStoreFields) \
+ V(Calls) \
+ V(ContextSlots) \
+ V(DoubleArrayElements) \
+ V(DoubleFields) \
+ V(ElementsKind) \
+ V(ElementsPointer) \
+ V(GlobalVars) \
+ V(InobjectFields) \
+ V(OsrEntries) \
+ V(ExternalMemory)
+
+
+#define DECLARE_ABSTRACT_INSTRUCTION(type) \
+ virtual bool Is##type() const { return true; } \
+ static H##type* cast(HValue* value) { \
+ ASSERT(value->Is##type()); \
+ return reinterpret_cast<H##type*>(value); \
+ }
+
+
+#define DECLARE_CONCRETE_INSTRUCTION(type) \
+ virtual LInstruction* CompileToLithium(LChunkBuilder* builder); \
+ static H##type* cast(HValue* value) { \
+ ASSERT(value->Is##type()); \
+ return reinterpret_cast<H##type*>(value); \
+ } \
+ virtual Opcode opcode() const { return HValue::k##type; }
+
+
+class Range: public ZoneObject {
+ public:
+ Range()
+ : lower_(kMinInt),
+ upper_(kMaxInt),
+ next_(NULL),
+ can_be_minus_zero_(false) { }
+
+ Range(int32_t lower, int32_t upper)
+ : lower_(lower),
+ upper_(upper),
+ next_(NULL),
+ can_be_minus_zero_(false) { }
+
+ int32_t upper() const { return upper_; }
+ int32_t lower() const { return lower_; }
+ Range* next() const { return next_; }
+ Range* CopyClearLower(Zone* zone) const {
+ return new(zone) Range(kMinInt, upper_);
+ }
+ Range* CopyClearUpper(Zone* zone) const {
+ return new(zone) Range(lower_, kMaxInt);
+ }
+ Range* Copy(Zone* zone) const {
+ Range* result = new(zone) Range(lower_, upper_);
+ result->set_can_be_minus_zero(CanBeMinusZero());
+ return result;
+ }
+ int32_t Mask() const;
+ void set_can_be_minus_zero(bool b) { can_be_minus_zero_ = b; }
+ bool CanBeMinusZero() const { return CanBeZero() && can_be_minus_zero_; }
+ bool CanBeZero() const { return upper_ >= 0 && lower_ <= 0; }
+ bool CanBeNegative() const { return lower_ < 0; }
+ bool CanBePositive() const { return upper_ > 0; }
+ bool Includes(int value) const { return lower_ <= value && upper_ >= value; }
+ bool IsMostGeneric() const {
+ return lower_ == kMinInt && upper_ == kMaxInt && CanBeMinusZero();
+ }
+ bool IsInSmiRange() const {
+ return lower_ >= Smi::kMinValue && upper_ <= Smi::kMaxValue;
+ }
+ void ClampToSmi() {
+ lower_ = Max(lower_, Smi::kMinValue);
+ upper_ = Min(upper_, Smi::kMaxValue);
+ }
+ void KeepOrder();
+#ifdef DEBUG
+ void Verify() const;
+#endif
+
+ void StackUpon(Range* other) {
+ Intersect(other);
+ next_ = other;
+ }
+
+ void Intersect(Range* other);
+ void Union(Range* other);
+ void CombinedMax(Range* other);
+ void CombinedMin(Range* other);
+
+ void AddConstant(int32_t value);
+ void Sar(int32_t value);
+ void Shl(int32_t value);
+ bool AddAndCheckOverflow(const Representation& r, Range* other);
+ bool SubAndCheckOverflow(const Representation& r, Range* other);
+ bool MulAndCheckOverflow(const Representation& r, Range* other);
+
+ private:
+ int32_t lower_;
+ int32_t upper_;
+ Range* next_;
+ bool can_be_minus_zero_;
+};
+
+
+class UniqueValueId {
+ public:
+ UniqueValueId() : raw_address_(NULL) { }
+
+ explicit UniqueValueId(Object* object) {
+ raw_address_ = reinterpret_cast<Address>(object);
+ ASSERT(IsInitialized());
+ }
+
+ explicit UniqueValueId(Handle<Object> handle) {
+ static const Address kEmptyHandleSentinel = reinterpret_cast<Address>(1);
+ if (handle.is_null()) {
+ raw_address_ = kEmptyHandleSentinel;
+ } else {
+ raw_address_ = reinterpret_cast<Address>(*handle);
+ ASSERT_NE(kEmptyHandleSentinel, raw_address_);
+ }
+ ASSERT(IsInitialized());
+ }
+
+ bool IsInitialized() const { return raw_address_ != NULL; }
+
+ bool operator==(const UniqueValueId& other) const {
+ ASSERT(IsInitialized() && other.IsInitialized());
+ return raw_address_ == other.raw_address_;
+ }
+
+ bool operator!=(const UniqueValueId& other) const {
+ ASSERT(IsInitialized() && other.IsInitialized());
+ return raw_address_ != other.raw_address_;
+ }
+
+ intptr_t Hashcode() const {
+ ASSERT(IsInitialized());
+ return reinterpret_cast<intptr_t>(raw_address_);
+ }
+
+ private:
+ Address raw_address_;
+};
+
+
+class HType {
+ public:
+ static HType None() { return HType(kNone); }
+ static HType Tagged() { return HType(kTagged); }
+ static HType TaggedPrimitive() { return HType(kTaggedPrimitive); }
+ static HType TaggedNumber() { return HType(kTaggedNumber); }
+ static HType Smi() { return HType(kSmi); }
+ static HType HeapNumber() { return HType(kHeapNumber); }
+ static HType String() { return HType(kString); }
+ static HType Boolean() { return HType(kBoolean); }
+ static HType NonPrimitive() { return HType(kNonPrimitive); }
+ static HType JSArray() { return HType(kJSArray); }
+ static HType JSObject() { return HType(kJSObject); }
+
+ // Return the weakest (least precise) common type.
+ HType Combine(HType other) {
+ return HType(static_cast<Type>(type_ & other.type_));
+ }
+
+ bool Equals(const HType& other) const {
+ return type_ == other.type_;
+ }
+
+ bool IsSubtypeOf(const HType& other) {
+ return Combine(other).Equals(other);
+ }
+
+ bool IsTagged() const {
+ return ((type_ & kTagged) == kTagged);
+ }
+
+ bool IsTaggedPrimitive() const {
+ return ((type_ & kTaggedPrimitive) == kTaggedPrimitive);
+ }
+
+ bool IsTaggedNumber() const {
+ return ((type_ & kTaggedNumber) == kTaggedNumber);
+ }
+
+ bool IsSmi() const {
+ return ((type_ & kSmi) == kSmi);
+ }
+
+ bool IsHeapNumber() const {
+ return ((type_ & kHeapNumber) == kHeapNumber);
+ }
+
+ bool IsString() const {
+ return ((type_ & kString) == kString);
+ }
+
+ bool IsNonString() const {
+ return IsTaggedPrimitive() || IsSmi() || IsHeapNumber() ||
+ IsBoolean() || IsJSArray();
+ }
+
+ bool IsBoolean() const {
+ return ((type_ & kBoolean) == kBoolean);
+ }
+
+ bool IsNonPrimitive() const {
+ return ((type_ & kNonPrimitive) == kNonPrimitive);
+ }
+
+ bool IsJSArray() const {
+ return ((type_ & kJSArray) == kJSArray);
+ }
+
+ bool IsJSObject() const {
+ return ((type_ & kJSObject) == kJSObject);
+ }
+
+ bool IsHeapObject() const {
+ return IsHeapNumber() || IsString() || IsBoolean() || IsNonPrimitive();
+ }
+
+ bool ToStringOrToNumberCanBeObserved(Representation representation) {
+ switch (type_) {
+ case kTaggedPrimitive: // fallthru
+ case kTaggedNumber: // fallthru
+ case kSmi: // fallthru
+ case kHeapNumber: // fallthru
+ case kString: // fallthru
+ case kBoolean:
+ return false;
+ case kJSArray: // fallthru
+ case kJSObject:
+ return true;
+ case kTagged:
+ break;
+ }
+ return !representation.IsSmiOrInteger32() && !representation.IsDouble();
+ }
+
+ static HType TypeFromValue(Handle<Object> value);
+
+ const char* ToString();
+
+ private:
+ enum Type {
+ kNone = 0x0, // 0000 0000 0000 0000
+ kTagged = 0x1, // 0000 0000 0000 0001
+ kTaggedPrimitive = 0x5, // 0000 0000 0000 0101
+ kTaggedNumber = 0xd, // 0000 0000 0000 1101
+ kSmi = 0x1d, // 0000 0000 0001 1101
+ kHeapNumber = 0x2d, // 0000 0000 0010 1101
+ kString = 0x45, // 0000 0000 0100 0101
+ kBoolean = 0x85, // 0000 0000 1000 0101
+ kNonPrimitive = 0x101, // 0000 0001 0000 0001
+ kJSObject = 0x301, // 0000 0011 0000 0001
+ kJSArray = 0x701 // 0000 0111 0000 0001
+ };
+
+ // Make sure type fits in int16.
+ STATIC_ASSERT(kJSArray < (1 << (2 * kBitsPerByte)));
+
+ explicit HType(Type t) : type_(t) { }
+
+ int16_t type_;
+};
+
+
+class HUseListNode: public ZoneObject {
+ public:
+ HUseListNode(HValue* value, int index, HUseListNode* tail)
+ : tail_(tail), value_(value), index_(index) {
+ }
+
+ HUseListNode* tail();
+ HValue* value() const { return value_; }
+ int index() const { return index_; }
+
+ void set_tail(HUseListNode* list) { tail_ = list; }
+
+#ifdef DEBUG
+ void Zap() {
+ tail_ = reinterpret_cast<HUseListNode*>(1);
+ value_ = NULL;
+ index_ = -1;
+ }
+#endif
+
+ private:
+ HUseListNode* tail_;
+ HValue* value_;
+ int index_;
+};
+
+
+// We reuse use list nodes behind the scenes as uses are added and deleted.
+// This class is the safe way to iterate uses while deleting them.
+class HUseIterator BASE_EMBEDDED {
+ public:
+ bool Done() { return current_ == NULL; }
+ void Advance();
+
+ HValue* value() {
+ ASSERT(!Done());
+ return value_;
+ }
+
+ int index() {
+ ASSERT(!Done());
+ return index_;
+ }
+
+ private:
+ explicit HUseIterator(HUseListNode* head);
+
+ HUseListNode* current_;
+ HUseListNode* next_;
+ HValue* value_;
+ int index_;
+
+ friend class HValue;
+};
+
+
+// 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. All tracked flags should appear before untracked ones.
+enum GVNFlag {
+ // Declare global value numbering flags.
+#define DECLARE_FLAG(type) kChanges##type, kDependsOn##type,
+ GVN_TRACKED_FLAG_LIST(DECLARE_FLAG)
+ GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG)
+#undef DECLARE_FLAG
+ kAfterLastFlag,
+ kLastFlag = kAfterLastFlag - 1,
+#define COUNT_FLAG(type) + 1
+ kNumberOfTrackedSideEffects = 0 GVN_TRACKED_FLAG_LIST(COUNT_FLAG)
+#undef COUNT_FLAG
+};
+
+
+class DecompositionResult BASE_EMBEDDED {
+ public:
+ DecompositionResult() : base_(NULL), offset_(0), scale_(0) {}
+
+ HValue* base() { return base_; }
+ int offset() { return offset_; }
+ int scale() { return scale_; }
+
+ bool Apply(HValue* other_base, int other_offset, int other_scale = 0) {
+ if (base_ == NULL) {
+ base_ = other_base;
+ offset_ = other_offset;
+ scale_ = other_scale;
+ return true;
+ } else {
+ if (scale_ == 0) {
+ base_ = other_base;
+ offset_ += other_offset;
+ scale_ = other_scale;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ void SwapValues(HValue** other_base, int* other_offset, int* other_scale) {
+ swap(&base_, other_base);
+ swap(&offset_, other_offset);
+ swap(&scale_, other_scale);
+ }
+
+ private:
+ template <class T> void swap(T* a, T* b) {
+ T c(*a);
+ *a = *b;
+ *b = c;
+ }
+
+ HValue* base_;
+ int offset_;
+ int scale_;
+};
+
+
+typedef EnumSet<GVNFlag> GVNFlagSet;
+
+
+class HValue: public ZoneObject {
+ public:
+ static const int kNoNumber = -1;
+
+ enum Flag {
+ kFlexibleRepresentation,
+ kCannotBeTagged,
+ // Participate in Global Value Numbering, i.e. elimination of
+ // unnecessary recomputations. If an instruction sets this flag, it must
+ // implement DataEquals(), which will be used to determine if other
+ // occurrences of the instruction are indeed the same.
+ kUseGVN,
+ // Track instructions that are dominating side effects. If an instruction
+ // sets this flag, it must implement HandleSideEffectDominator() and should
+ // indicate which side effects to track by setting GVN flags.
+ kTrackSideEffectDominators,
+ kCanOverflow,
+ kBailoutOnMinusZero,
+ kCanBeDivByZero,
+ kAllowUndefinedAsNaN,
+ kIsArguments,
+ kTruncatingToInt32,
+ kAllUsesTruncatingToInt32,
+ kTruncatingToSmi,
+ kAllUsesTruncatingToSmi,
+ // Set after an instruction is killed.
+ kIsDead,
+ // Instructions that are allowed to produce full range unsigned integer
+ // values are marked with kUint32 flag. If arithmetic shift or a load from
+ // EXTERNAL_UNSIGNED_INT_ELEMENTS array is not marked with this flag
+ // it will deoptimize if result does not fit into signed integer range.
+ // HGraph::ComputeSafeUint32Operations is responsible for setting this
+ // flag.
+ kUint32,
+ kHasNoObservableSideEffects,
+ // Indicates the instruction is live during dead code elimination.
+ kIsLive,
+
+ // HEnvironmentMarkers are deleted before dead code
+ // elimination takes place, so they can repurpose the kIsLive flag:
+ kEndsLiveRange = kIsLive,
+
+ // TODO(everyone): Don't forget to update this!
+ kLastFlag = kIsLive
+ };
+
+ STATIC_ASSERT(kLastFlag < kBitsPerInt);
+
+ static const int kChangesToDependsFlagsLeftShift = 1;
+
+ static GVNFlag ChangesFlagFromInt(int x) {
+ return static_cast<GVNFlag>(x * 2);
+ }
+ static GVNFlag DependsOnFlagFromInt(int x) {
+ return static_cast<GVNFlag>(x * 2 + 1);
+ }
+ static GVNFlagSet ConvertChangesToDependsFlags(GVNFlagSet flags) {
+ return GVNFlagSet(flags.ToIntegral() << kChangesToDependsFlagsLeftShift);
+ }
+
+ static HValue* cast(HValue* value) { return value; }
+
+ enum Opcode {
+ // Declare a unique enum value for each hydrogen instruction.
+ #define DECLARE_OPCODE(type) k##type,
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE)
+ kPhi
+ #undef DECLARE_OPCODE
+ };
+ virtual Opcode opcode() const = 0;
+
+ // Declare a non-virtual predicates for each concrete HInstruction or HValue.
+ #define DECLARE_PREDICATE(type) \
+ bool Is##type() const { return opcode() == k##type; }
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE)
+ #undef DECLARE_PREDICATE
+ bool IsPhi() const { return opcode() == kPhi; }
+
+ // Declare virtual predicates for abstract HInstruction or HValue
+ #define DECLARE_PREDICATE(type) \
+ virtual bool Is##type() const { return false; }
+ HYDROGEN_ABSTRACT_INSTRUCTION_LIST(DECLARE_PREDICATE)
+ #undef DECLARE_PREDICATE
+
+ HValue(HType type = HType::Tagged())
+ : block_(NULL),
+ id_(kNoNumber),
+ type_(type),
+ use_list_(NULL),
+ range_(NULL),
+ flags_(0) {}
+ virtual ~HValue() {}
+
+ HBasicBlock* block() const { return block_; }
+ void SetBlock(HBasicBlock* block);
+ int LoopWeight() const;
+
+ // Note: Never call this method for an unlinked value.
+ Isolate* isolate() const;
+
+ int id() const { return id_; }
+ void set_id(int id) { id_ = id; }
+
+ HUseIterator uses() const { return HUseIterator(use_list_); }
+
+ virtual bool EmitAtUses() { return false; }
+
+ Representation representation() const { return representation_; }
+ void ChangeRepresentation(Representation r) {
+ ASSERT(CheckFlag(kFlexibleRepresentation));
+ ASSERT(!CheckFlag(kCannotBeTagged) || !r.IsTagged());
+ RepresentationChanged(r);
+ representation_ = r;
+ if (r.IsTagged()) {
+ // Tagged is the bottom of the lattice, don't go any further.
+ ClearFlag(kFlexibleRepresentation);
+ }
+ }
+ virtual void AssumeRepresentation(Representation r);
+
+ virtual Representation KnownOptimalRepresentation() {
+ Representation r = representation();
+ if (r.IsTagged()) {
+ HType t = type();
+ if (t.IsSmi()) return Representation::Smi();
+ if (t.IsHeapNumber()) return Representation::Double();
+ if (t.IsHeapObject()) return r;
+ return Representation::None();
+ }
+ return r;
+ }
+
+ HType type() const { return type_; }
+ void set_type(HType new_type) {
+ ASSERT(new_type.IsSubtypeOf(type_));
+ type_ = new_type;
+ }
+
+ bool IsHeapObject() {
+ return representation_.IsHeapObject() || type_.IsHeapObject();
+ }
+
+ // An operation needs to override this function iff:
+ // 1) it can produce an int32 output.
+ // 2) the true value of its output can potentially be minus zero.
+ // The implementation must set a flag so that it bails out in the case where
+ // it would otherwise output what should be a minus zero as an int32 zero.
+ // If the operation also exists in a form that takes int32 and outputs int32
+ // then the operation should return its input value so that we can propagate
+ // back. There are three operations that need to propagate back to more than
+ // one input. They are phi and binary div and mul. They always return NULL
+ // and expect the caller to take care of things.
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited) {
+ visited->Add(id());
+ return NULL;
+ }
+
+ // There are HInstructions that do not really change a value, they
+ // only add pieces of information to it (like bounds checks, map checks,
+ // smi checks...).
+ // We call these instructions "informative definitions", or "iDef".
+ // One of the iDef operands is special because it is the value that is
+ // "transferred" to the output, we call it the "redefined operand".
+ // If an HValue is an iDef it must override RedefinedOperandIndex() so that
+ // it does not return kNoRedefinedOperand;
+ static const int kNoRedefinedOperand = -1;
+ virtual int RedefinedOperandIndex() { return kNoRedefinedOperand; }
+ bool IsInformativeDefinition() {
+ return RedefinedOperandIndex() != kNoRedefinedOperand;
+ }
+ HValue* RedefinedOperand() {
+ int index = RedefinedOperandIndex();
+ return index == kNoRedefinedOperand ? NULL : OperandAt(index);
+ }
+
+ // A purely informative definition is an idef that will not emit code and
+ // should therefore be removed from the graph in the RestoreActualValues
+ // phase (so that live ranges will be shorter).
+ virtual bool IsPurelyInformativeDefinition() { return false; }
+
+ // This method must always return the original HValue SSA definition
+ // (regardless of any iDef of this value).
+ HValue* ActualValue() {
+ int index = RedefinedOperandIndex();
+ return index == kNoRedefinedOperand ? this : OperandAt(index);
+ }
+
+ bool IsInteger32Constant();
+ int32_t GetInteger32Constant();
+ bool EqualsInteger32Constant(int32_t value);
+
+ bool IsDefinedAfter(HBasicBlock* other) const;
+
+ // Operands.
+ virtual int OperandCount() = 0;
+ virtual HValue* OperandAt(int index) const = 0;
+ void SetOperandAt(int index, HValue* value);
+
+ void DeleteAndReplaceWith(HValue* other);
+ void ReplaceAllUsesWith(HValue* other);
+ bool HasNoUses() const { return use_list_ == NULL; }
+ bool HasMultipleUses() const {
+ return use_list_ != NULL && use_list_->tail() != NULL;
+ }
+ int UseCount() const;
+
+ // Mark this HValue as dead and to be removed from other HValues' use lists.
+ void Kill();
+
+ int flags() const { return flags_; }
+ void SetFlag(Flag f) { flags_ |= (1 << f); }
+ void ClearFlag(Flag f) { flags_ &= ~(1 << f); }
+ bool CheckFlag(Flag f) const { return (flags_ & (1 << f)) != 0; }
+
+ // Returns true if the flag specified is set for all uses, false otherwise.
+ bool CheckUsesForFlag(Flag f) const;
+ // Returns true if the flag specified is set for all uses, and this set
+ // of uses is non-empty.
+ bool HasAtLeastOneUseWithFlagAndNoneWithout(Flag f) const;
+
+ 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 !CheckFlag(kHasNoObservableSideEffects) &&
+ gvn_flags_.ContainsAnyOf(AllObservableSideEffectsFlagSet());
+ }
+
+ 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_; }
+ // TODO(svenpanne) We should really use the null object pattern here.
+ bool HasRange() const { return range_ != NULL; }
+ bool CanBeNegative() const { return !HasRange() || range()->CanBeNegative(); }
+ bool CanBeZero() const { return !HasRange() || range()->CanBeZero(); }
+ bool RangeCanInclude(int value) const {
+ return !HasRange() || range()->Includes(value);
+ }
+ void AddNewRange(Range* r, Zone* zone);
+ void RemoveLastAddedRange();
+ void ComputeInitialRange(Zone* zone);
+
+ // Escape analysis helpers.
+ virtual bool HasEscapingOperandAt(int index) { return true; }
+
+ // Representation helpers.
+ virtual Representation observed_input_representation(int index) {
+ return Representation::None();
+ }
+ virtual Representation RequiredInputRepresentation(int index) = 0;
+ virtual void InferRepresentation(HInferRepresentationPhase* h_infer);
+
+ // This gives the instruction an opportunity to replace itself with an
+ // instruction that does the same in some better way. To replace an
+ // instruction with a new one, first add the new instruction to the graph,
+ // then return it. Return NULL to have the instruction deleted.
+ virtual HValue* Canonicalize() { return this; }
+
+ bool Equals(HValue* other);
+ virtual intptr_t Hashcode();
+
+ // Compute unique ids upfront that is safe wrt GC and parallel recompilation.
+ virtual void FinalizeUniqueValueId() { }
+
+ // Printing support.
+ virtual void PrintTo(StringStream* stream) = 0;
+ void PrintNameTo(StringStream* stream);
+ void PrintTypeTo(StringStream* stream);
+ void PrintRangeTo(StringStream* stream);
+ void PrintChangesTo(StringStream* stream);
+
+ const char* Mnemonic() const;
+
+ // Type information helpers.
+ bool HasMonomorphicJSObjectType();
+
+ // TODO(mstarzinger): For now instructions can override this function to
+ // specify statically known types, once HType can convey more information
+ // it should be based on the HType.
+ virtual Handle<Map> GetMonomorphicJSObjectMap() { return Handle<Map>(); }
+
+ // Updated the inferred type of this instruction and returns true if
+ // it has changed.
+ bool UpdateInferredType();
+
+ virtual HType CalculateInferredType();
+
+ // This function must be overridden for instructions which have the
+ // kTrackSideEffectDominators flag set, to track instructions that are
+ // dominating side effects.
+ virtual void HandleSideEffectDominator(GVNFlag side_effect,
+ HValue* dominator) {
+ UNREACHABLE();
+ }
+
+ // Check if this instruction has some reason that prevents elimination.
+ bool CannotBeEliminated() const {
+ return HasObservableSideEffects() || !IsDeletable();
+ }
+
+#ifdef DEBUG
+ virtual void Verify() = 0;
+#endif
+
+ virtual bool TryDecompose(DecompositionResult* decomposition) {
+ if (RedefinedOperand() != NULL) {
+ return RedefinedOperand()->TryDecompose(decomposition);
+ } else {
+ return false;
+ }
+ }
+
+ // Returns true conservatively if the program might be able to observe a
+ // ToString() operation on this value.
+ bool ToStringCanBeObserved() const {
+ return type().ToStringOrToNumberCanBeObserved(representation());
+ }
+
+ // Returns true conservatively if the program might be able to observe a
+ // ToNumber() operation on this value.
+ bool ToNumberCanBeObserved() const {
+ return type().ToStringOrToNumberCanBeObserved(representation());
+ }
+
+ protected:
+ // This function must be overridden for instructions with flag kUseGVN, to
+ // compare the non-Operand parts of the instruction.
+ virtual bool DataEquals(HValue* other) {
+ UNREACHABLE();
+ return false;
+ }
+
+ virtual Representation RepresentationFromInputs() {
+ return representation();
+ }
+ Representation RepresentationFromUses();
+ Representation RepresentationFromUseRequirements();
+ bool HasNonSmiUse();
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason);
+ void AddDependantsToWorklist(HInferRepresentationPhase* h_infer);
+
+ virtual void RepresentationChanged(Representation to) { }
+
+ virtual Range* InferRange(Zone* zone);
+ virtual void DeleteFromGraph() = 0;
+ virtual void InternalSetOperandAt(int index, HValue* value) = 0;
+ void clear_block() {
+ ASSERT(block_ != NULL);
+ block_ = NULL;
+ }
+
+ void set_representation(Representation r) {
+ ASSERT(representation_.IsNone() && !r.IsNone());
+ representation_ = r;
+ }
+
+ static GVNFlagSet AllDependsOnFlagSet() {
+ GVNFlagSet result;
+ // Create changes mask.
+#define ADD_FLAG(type) result.Add(kDependsOn##type);
+ GVN_TRACKED_FLAG_LIST(ADD_FLAG)
+ GVN_UNTRACKED_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_TRACKED_FLAG_LIST(ADD_FLAG)
+ GVN_UNTRACKED_FLAG_LIST(ADD_FLAG)
+#undef ADD_FLAG
+ return result;
+ }
+
+ // A flag mask to mark an instruction as having arbitrary side effects.
+ 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(kChangesNewSpacePromotion);
+ result.Remove(kChangesElementsKind);
+ result.Remove(kChangesElementsPointer);
+ result.Remove(kChangesMaps);
+ return result;
+ }
+
+ // Remove the matching use from the use list if present. Returns the
+ // removed list node or NULL.
+ HUseListNode* RemoveUse(HValue* value, int index);
+
+ void RegisterUse(int index, HValue* new_value);
+
+ HBasicBlock* block_;
+
+ // The id of this instruction in the hydrogen graph, assigned when first
+ // added to the graph. Reflects creation order.
+ int id_;
+
+ Representation representation_;
+ HType type_;
+ HUseListNode* use_list_;
+ Range* range_;
+ int flags_;
+ GVNFlagSet gvn_flags_;
+
+ private:
+ virtual bool IsDeletable() const { return false; }
+
+ DISALLOW_COPY_AND_ASSIGN(HValue);
+};
+
+
+#define DECLARE_INSTRUCTION_FACTORY_P0(I) \
+ static I* New(Zone* zone, HValue* context) { \
+ return new(zone) I(); \
+}
+
+#define DECLARE_INSTRUCTION_FACTORY_P1(I, P1) \
+ static I* New(Zone* zone, HValue* context, P1 p1) { \
+ return new(zone) I(p1); \
+ }
+
+#define DECLARE_INSTRUCTION_FACTORY_P2(I, P1, P2) \
+ static I* New(Zone* zone, HValue* context, P1 p1, P2 p2) { \
+ return new(zone) I(p1, p2); \
+ }
+
+#define DECLARE_INSTRUCTION_FACTORY_P3(I, P1, P2, P3) \
+ static I* New(Zone* zone, HValue* context, P1 p1, P2 p2, P3 p3) { \
+ return new(zone) I(p1, p2, p3); \
+ }
+
+#define DECLARE_INSTRUCTION_FACTORY_P4(I, P1, P2, P3, P4) \
+ static I* New(Zone* zone, \
+ HValue* context, \
+ P1 p1, \
+ P2 p2, \
+ P3 p3, \
+ P4 p4) { \
+ return new(zone) I(p1, p2, p3, p4); \
+ }
+
+#define DECLARE_INSTRUCTION_FACTORY_P5(I, P1, P2, P3, P4, P5) \
+ static I* New(Zone* zone, \
+ HValue* context, \
+ P1 p1, \
+ P2 p2, \
+ P3 p3, \
+ P4 p4, \
+ P5 p5) { \
+ return new(zone) I(p1, p2, p3, p4, p5); \
+ }
+
+
+class HInstruction: public HValue {
+ public:
+ HInstruction* next() const { return next_; }
+ HInstruction* previous() const { return previous_; }
+
+ virtual void PrintTo(StringStream* stream);
+ virtual void PrintDataTo(StringStream* stream);
+
+ bool IsLinked() const { return block() != NULL; }
+ void Unlink();
+ void InsertBefore(HInstruction* next);
+ void InsertAfter(HInstruction* previous);
+
+ // The position is a write-once variable.
+ int position() const { return position_; }
+ bool has_position() const { return position_ != RelocInfo::kNoPosition; }
+ void set_position(int position) {
+ ASSERT(!has_position());
+ ASSERT(position != RelocInfo::kNoPosition);
+ position_ = position;
+ }
+
+ bool CanTruncateToInt32() const { return CheckFlag(kTruncatingToInt32); }
+
+ virtual LInstruction* CompileToLithium(LChunkBuilder* builder) = 0;
+
+#ifdef DEBUG
+ virtual void Verify();
+#endif
+
+ virtual bool IsCall() { return false; }
+
+ DECLARE_ABSTRACT_INSTRUCTION(Instruction)
+
+ protected:
+ HInstruction(HType type = HType::Tagged())
+ : HValue(type),
+ next_(NULL),
+ previous_(NULL),
+ position_(RelocInfo::kNoPosition) {
+ SetGVNFlag(kDependsOnOsrEntries);
+ }
+
+ virtual void DeleteFromGraph() { Unlink(); }
+
+ private:
+ void InitializeAsFirst(HBasicBlock* block) {
+ ASSERT(!IsLinked());
+ SetBlock(block);
+ }
+
+ void PrintMnemonicTo(StringStream* stream);
+
+ HInstruction* next_;
+ HInstruction* previous_;
+ int position_;
+
+ friend class HBasicBlock;
+};
+
+
+template<int V>
+class HTemplateInstruction : public HInstruction {
+ public:
+ int OperandCount() { return V; }
+ HValue* OperandAt(int i) const { return inputs_[i]; }
+
+ protected:
+ HTemplateInstruction(HType type = HType::Tagged()) : HInstruction(type) {}
+
+ void InternalSetOperandAt(int i, HValue* value) { inputs_[i] = value; }
+
+ private:
+ EmbeddedContainer<HValue*, V> inputs_;
+};
+
+
+class HControlInstruction: public HInstruction {
+ public:
+ virtual HBasicBlock* SuccessorAt(int i) = 0;
+ virtual int SuccessorCount() = 0;
+ virtual void SetSuccessorAt(int i, HBasicBlock* block) = 0;
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ HBasicBlock* FirstSuccessor() {
+ return SuccessorCount() > 0 ? SuccessorAt(0) : NULL;
+ }
+ HBasicBlock* SecondSuccessor() {
+ return SuccessorCount() > 1 ? SuccessorAt(1) : NULL;
+ }
+
+ DECLARE_ABSTRACT_INSTRUCTION(ControlInstruction)
+};
+
+
+class HSuccessorIterator BASE_EMBEDDED {
+ public:
+ explicit HSuccessorIterator(HControlInstruction* instr)
+ : instr_(instr), current_(0) { }
+
+ bool Done() { return current_ >= instr_->SuccessorCount(); }
+ HBasicBlock* Current() { return instr_->SuccessorAt(current_); }
+ void Advance() { current_++; }
+
+ private:
+ HControlInstruction* instr_;
+ int current_;
+};
+
+
+template<int S, int V>
+class HTemplateControlInstruction: public HControlInstruction {
+ public:
+ int SuccessorCount() { return S; }
+ HBasicBlock* SuccessorAt(int i) { return successors_[i]; }
+ void SetSuccessorAt(int i, HBasicBlock* block) { successors_[i] = block; }
+
+ int OperandCount() { return V; }
+ HValue* OperandAt(int i) const { return inputs_[i]; }
+
+
+ protected:
+ void InternalSetOperandAt(int i, HValue* value) { inputs_[i] = value; }
+
+ private:
+ EmbeddedContainer<HBasicBlock*, S> successors_;
+ EmbeddedContainer<HValue*, V> inputs_;
+};
+
+
+class HBlockEntry: public HTemplateInstruction<0> {
+ public:
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(BlockEntry)
+};
+
+
+class HDummyUse: public HTemplateInstruction<1> {
+ public:
+ explicit HDummyUse(HValue* value)
+ : HTemplateInstruction<1>(HType::Smi()) {
+ SetOperandAt(0, value);
+ // Pretend to be a Smi so that the HChange instructions inserted
+ // before any use generate as little code as possible.
+ set_representation(Representation::Tagged());
+ }
+
+ HValue* value() { return OperandAt(0); }
+
+ virtual bool HasEscapingOperandAt(int index) { return false; }
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse);
+};
+
+
+class HDeoptimize: public HTemplateInstruction<0> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P2(HDeoptimize, const char*,
+ Deoptimizer::BailoutType);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ const char* reason() const { return reason_; }
+ Deoptimizer::BailoutType type() { return type_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Deoptimize)
+
+ private:
+ explicit HDeoptimize(const char* reason, Deoptimizer::BailoutType type)
+ : reason_(reason), type_(type) {}
+
+ const char* reason_;
+ Deoptimizer::BailoutType type_;
+};
+
+
+// Inserts an int3/stop break instruction for debugging purposes.
+class HDebugBreak: public HTemplateInstruction<0> {
+ public:
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(DebugBreak)
+};
+
+
+class HGoto: public HTemplateControlInstruction<1, 0> {
+ public:
+ explicit HGoto(HBasicBlock* target) {
+ SetSuccessorAt(0, target);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(Goto)
+};
+
+
+class HUnaryControlInstruction: public HTemplateControlInstruction<2, 1> {
+ public:
+ HUnaryControlInstruction(HValue* value,
+ HBasicBlock* true_target,
+ HBasicBlock* false_target) {
+ SetOperandAt(0, value);
+ SetSuccessorAt(0, true_target);
+ SetSuccessorAt(1, false_target);
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ HValue* value() { return OperandAt(0); }
+};
+
+
+class HBranch: public HUnaryControlInstruction {
+ public:
+ HBranch(HValue* value,
+ ToBooleanStub::Types expected_input_types = ToBooleanStub::Types(),
+ HBasicBlock* true_target = NULL,
+ HBasicBlock* false_target = NULL)
+ : HUnaryControlInstruction(value, true_target, false_target),
+ expected_input_types_(expected_input_types) {
+ SetFlag(kAllowUndefinedAsNaN);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+ virtual Representation observed_input_representation(int index);
+
+ ToBooleanStub::Types expected_input_types() const {
+ return expected_input_types_;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Branch)
+
+ private:
+ ToBooleanStub::Types expected_input_types_;
+};
+
+
+class HCompareMap: public HUnaryControlInstruction {
+ public:
+ HCompareMap(HValue* value,
+ Handle<Map> map,
+ HBasicBlock* true_target = NULL,
+ HBasicBlock* false_target = NULL)
+ : HUnaryControlInstruction(value, true_target, false_target),
+ map_(map) {
+ ASSERT(!map.is_null());
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> map() const { return map_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareMap)
+
+ private:
+ Handle<Map> map_;
+};
+
+
+class HContext: public HTemplateInstruction<0> {
+ public:
+ static HContext* New(Zone* zone) {
+ return new(zone) HContext();
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Context)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ HContext() {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HReturn: public HTemplateControlInstruction<0, 3> {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* value,
+ HValue* parameter_count) {
+ return new(zone) HReturn(value, context, parameter_count);
+ }
+
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* value) {
+ return new(zone) HReturn(value, context, 0);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ HValue* value() { return OperandAt(0); }
+ HValue* context() { return OperandAt(1); }
+ HValue* parameter_count() { return OperandAt(2); }
+
+ DECLARE_CONCRETE_INSTRUCTION(Return)
+
+ private:
+ HReturn(HValue* value, HValue* context, HValue* parameter_count) {
+ SetOperandAt(0, value);
+ SetOperandAt(1, context);
+ SetOperandAt(2, parameter_count);
+ }
+};
+
+
+class HAbnormalExit: public HTemplateControlInstruction<0, 0> {
+ public:
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(AbnormalExit)
+};
+
+
+class HUnaryOperation: public HTemplateInstruction<1> {
+ public:
+ HUnaryOperation(HValue* value, HType type = HType::Tagged())
+ : HTemplateInstruction<1>(type) {
+ SetOperandAt(0, value);
+ }
+
+ static HUnaryOperation* cast(HValue* value) {
+ return reinterpret_cast<HUnaryOperation*>(value);
+ }
+
+ HValue* value() const { return OperandAt(0); }
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class HThrow: public HTemplateInstruction<2> {
+ public:
+ static HThrow* New(Zone* zone,
+ HValue* context,
+ HValue* value) {
+ return new(zone) HThrow(context, value);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* value() { return OperandAt(1); }
+
+ DECLARE_CONCRETE_INSTRUCTION(Throw)
+
+ private:
+ HThrow(HValue* context, HValue* value) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, value);
+ SetAllSideEffects();
+ }
+};
+
+
+class HUseConst: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HUseConst, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(UseConst)
+
+ private:
+ explicit HUseConst(HValue* old_value) : HUnaryOperation(old_value) { }
+};
+
+
+class HForceRepresentation: public HTemplateInstruction<1> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P2(HForceRepresentation, HValue*, Representation);
+
+ HValue* value() { return OperandAt(0); }
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return representation(); // Same as the output representation.
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(ForceRepresentation)
+
+ private:
+ HForceRepresentation(HValue* value, Representation required_representation) {
+ SetOperandAt(0, value);
+ set_representation(required_representation);
+ }
+};
+
+
+class HChange: public HUnaryOperation {
+ public:
+ HChange(HValue* value,
+ Representation to,
+ bool is_truncating_to_smi,
+ bool is_truncating_to_int32)
+ : HUnaryOperation(value) {
+ ASSERT(!value->representation().IsNone());
+ ASSERT(!to.IsNone());
+ ASSERT(!value->representation().Equals(to));
+ set_representation(to);
+ SetFlag(kUseGVN);
+ if (is_truncating_to_smi) SetFlag(kTruncatingToSmi);
+ if (is_truncating_to_int32) SetFlag(kTruncatingToInt32);
+ if (value->representation().IsSmi() || value->type().IsSmi()) {
+ set_type(HType::Smi());
+ } else {
+ set_type(HType::TaggedNumber());
+ if (to.IsTagged()) SetGVNFlag(kChangesNewSpacePromotion);
+ }
+ }
+
+ bool can_convert_undefined_to_nan() {
+ return CheckUsesForFlag(kAllowUndefinedAsNaN);
+ }
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+ virtual HType CalculateInferredType();
+ virtual HValue* Canonicalize();
+
+ Representation from() const { return value()->representation(); }
+ Representation to() const { return representation(); }
+ bool deoptimize_on_minus_zero() const {
+ return CheckFlag(kBailoutOnMinusZero);
+ }
+ virtual Representation RequiredInputRepresentation(int index) {
+ return from();
+ }
+
+ virtual Range* InferRange(Zone* zone);
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(Change)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ virtual bool IsDeletable() const {
+ return !from().IsTagged() || value()->type().IsSmi();
+ }
+};
+
+
+class HClampToUint8: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HClampToUint8, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampToUint8)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HClampToUint8(HValue* value)
+ : HUnaryOperation(value) {
+ set_representation(Representation::Integer32());
+ SetFlag(kAllowUndefinedAsNaN);
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+enum RemovableSimulate {
+ REMOVABLE_SIMULATE,
+ FIXED_SIMULATE
+};
+
+
+class HSimulate: public HInstruction {
+ public:
+ HSimulate(BailoutId ast_id,
+ int pop_count,
+ Zone* zone,
+ RemovableSimulate removable)
+ : ast_id_(ast_id),
+ pop_count_(pop_count),
+ values_(2, zone),
+ assigned_indexes_(2, zone),
+ zone_(zone),
+ removable_(removable) {}
+ virtual ~HSimulate() {}
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ bool HasAstId() const { return !ast_id_.IsNone(); }
+ BailoutId ast_id() const { return ast_id_; }
+ void set_ast_id(BailoutId id) {
+ ASSERT(!HasAstId());
+ ast_id_ = id;
+ }
+
+ int pop_count() const { return pop_count_; }
+ const ZoneList<HValue*>* values() const { return &values_; }
+ int GetAssignedIndexAt(int index) const {
+ ASSERT(HasAssignedIndexAt(index));
+ return assigned_indexes_[index];
+ }
+ bool HasAssignedIndexAt(int index) const {
+ return assigned_indexes_[index] != kNoIndex;
+ }
+ void AddAssignedValue(int index, HValue* value) {
+ AddValue(index, value);
+ }
+ void AddPushedValue(HValue* value) {
+ AddValue(kNoIndex, value);
+ }
+ int ToOperandIndex(int environment_index) {
+ for (int i = 0; i < assigned_indexes_.length(); ++i) {
+ if (assigned_indexes_[i] == environment_index) return i;
+ }
+ return -1;
+ }
+ virtual int OperandCount() { return values_.length(); }
+ virtual HValue* OperandAt(int index) const { return values_[index]; }
+
+ virtual bool HasEscapingOperandAt(int index) { return false; }
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ void MergeWith(ZoneList<HSimulate*>* list);
+ bool is_candidate_for_removal() { return removable_ == REMOVABLE_SIMULATE; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Simulate)
+
+#ifdef DEBUG
+ virtual void Verify();
+ void set_closure(Handle<JSFunction> closure) { closure_ = closure; }
+ Handle<JSFunction> closure() const { return closure_; }
+#endif
+
+ protected:
+ virtual void InternalSetOperandAt(int index, HValue* value) {
+ values_[index] = value;
+ }
+
+ private:
+ static const int kNoIndex = -1;
+ void AddValue(int index, HValue* value) {
+ assigned_indexes_.Add(index, zone_);
+ // Resize the list of pushed values.
+ values_.Add(NULL, zone_);
+ // Set the operand through the base method in HValue to make sure that the
+ // use lists are correctly updated.
+ SetOperandAt(values_.length() - 1, value);
+ }
+ bool HasValueForIndex(int index) {
+ for (int i = 0; i < assigned_indexes_.length(); ++i) {
+ if (assigned_indexes_[i] == index) return true;
+ }
+ return false;
+ }
+ BailoutId ast_id_;
+ int pop_count_;
+ ZoneList<HValue*> values_;
+ ZoneList<int> assigned_indexes_;
+ Zone* zone_;
+ RemovableSimulate removable_;
+
+#ifdef DEBUG
+ Handle<JSFunction> closure_;
+#endif
+};
+
+
+class HEnvironmentMarker: public HTemplateInstruction<1> {
+ public:
+ enum Kind { BIND, LOOKUP };
+
+ HEnvironmentMarker(Kind kind, int index)
+ : kind_(kind), index_(index), next_simulate_(NULL) { }
+
+ Kind kind() { return kind_; }
+ int index() { return index_; }
+ HSimulate* next_simulate() { return next_simulate_; }
+ void set_next_simulate(HSimulate* simulate) {
+ next_simulate_ = simulate;
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+#ifdef DEBUG
+ void set_closure(Handle<JSFunction> closure) {
+ ASSERT(closure_.is_null());
+ ASSERT(!closure.is_null());
+ closure_ = closure;
+ }
+ Handle<JSFunction> closure() const { return closure_; }
+#endif
+
+ DECLARE_CONCRETE_INSTRUCTION(EnvironmentMarker);
+
+ private:
+ Kind kind_;
+ int index_;
+ HSimulate* next_simulate_;
+
+#ifdef DEBUG
+ Handle<JSFunction> closure_;
+#endif
+};
+
+
+class HStackCheck: public HTemplateInstruction<1> {
+ public:
+ enum Type {
+ kFunctionEntry,
+ kBackwardsBranch
+ };
+
+ DECLARE_INSTRUCTION_FACTORY_P2(HStackCheck, HValue*, Type);
+
+ HValue* context() { return OperandAt(0); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ void Eliminate() {
+ // The stack check eliminator might try to eliminate the same stack
+ // check instruction multiple times.
+ if (IsLinked()) {
+ DeleteAndReplaceWith(NULL);
+ }
+ }
+
+ bool is_function_entry() { return type_ == kFunctionEntry; }
+ bool is_backwards_branch() { return type_ == kBackwardsBranch; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StackCheck)
+
+ private:
+ HStackCheck(HValue* context, Type type) : type_(type) {
+ SetOperandAt(0, context);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+
+ Type type_;
+};
+
+
+enum InliningKind {
+ NORMAL_RETURN, // Normal function/method call and return.
+ DROP_EXTRA_ON_RETURN, // Drop an extra value from the environment on return.
+ CONSTRUCT_CALL_RETURN, // Either use allocated receiver or return value.
+ GETTER_CALL_RETURN, // Returning from a getter, need to restore context.
+ SETTER_CALL_RETURN // Use the RHS of the assignment as the return value.
+};
+
+
+class HArgumentsObject;
+
+
+class HEnterInlined: public HTemplateInstruction<0> {
+ public:
+ static HEnterInlined* New(Zone* zone,
+ HValue* context,
+ Handle<JSFunction> closure,
+ int arguments_count,
+ FunctionLiteral* function,
+ InliningKind inlining_kind,
+ Variable* arguments_var,
+ HArgumentsObject* arguments_object,
+ bool undefined_receiver) {
+ return new(zone) HEnterInlined(closure, arguments_count, function,
+ inlining_kind, arguments_var,
+ arguments_object, undefined_receiver, zone);
+ }
+
+ void RegisterReturnTarget(HBasicBlock* return_target, Zone* zone);
+ ZoneList<HBasicBlock*>* return_targets() { return &return_targets_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<JSFunction> closure() const { return closure_; }
+ int arguments_count() const { return arguments_count_; }
+ bool arguments_pushed() const { return arguments_pushed_; }
+ void set_arguments_pushed() { arguments_pushed_ = true; }
+ FunctionLiteral* function() const { return function_; }
+ InliningKind inlining_kind() const { return inlining_kind_; }
+ bool undefined_receiver() const { return undefined_receiver_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ Variable* arguments_var() { return arguments_var_; }
+ HArgumentsObject* arguments_object() { return arguments_object_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(EnterInlined)
+
+ private:
+ HEnterInlined(Handle<JSFunction> closure,
+ int arguments_count,
+ FunctionLiteral* function,
+ InliningKind inlining_kind,
+ Variable* arguments_var,
+ HArgumentsObject* arguments_object,
+ bool undefined_receiver,
+ Zone* zone)
+ : closure_(closure),
+ arguments_count_(arguments_count),
+ arguments_pushed_(false),
+ function_(function),
+ inlining_kind_(inlining_kind),
+ arguments_var_(arguments_var),
+ arguments_object_(arguments_object),
+ undefined_receiver_(undefined_receiver),
+ return_targets_(2, zone) {
+ }
+
+ Handle<JSFunction> closure_;
+ int arguments_count_;
+ bool arguments_pushed_;
+ FunctionLiteral* function_;
+ InliningKind inlining_kind_;
+ Variable* arguments_var_;
+ HArgumentsObject* arguments_object_;
+ bool undefined_receiver_;
+ ZoneList<HBasicBlock*> return_targets_;
+};
+
+
+class HLeaveInlined: public HTemplateInstruction<0> {
+ public:
+ HLeaveInlined() { }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LeaveInlined)
+};
+
+
+class HPushArgument: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HPushArgument, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* argument() { return OperandAt(0); }
+
+ DECLARE_CONCRETE_INSTRUCTION(PushArgument)
+
+ private:
+ explicit HPushArgument(HValue* value) : HUnaryOperation(value) {
+ set_representation(Representation::Tagged());
+ }
+};
+
+
+class HThisFunction: public HTemplateInstruction<0> {
+ public:
+ HThisFunction() {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ThisFunction)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HOuterContext: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HOuterContext, HValue*);
+
+ DECLARE_CONCRETE_INSTRUCTION(OuterContext);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HOuterContext(HValue* inner) : HUnaryOperation(inner) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HDeclareGlobals: public HUnaryOperation {
+ public:
+ HDeclareGlobals(HValue* context,
+ Handle<FixedArray> pairs,
+ int flags)
+ : HUnaryOperation(context),
+ pairs_(pairs),
+ flags_(flags) {
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ static HDeclareGlobals* New(Zone* zone,
+ HValue* context,
+ Handle<FixedArray> pairs,
+ int flags) {
+ return new(zone) HDeclareGlobals(context, pairs, flags);
+ }
+
+ HValue* context() { return OperandAt(0); }
+ Handle<FixedArray> pairs() const { return pairs_; }
+ int flags() const { return flags_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ private:
+ Handle<FixedArray> pairs_;
+ int flags_;
+};
+
+
+class HGlobalObject: public HUnaryOperation {
+ public:
+ explicit HGlobalObject(HValue* context) : HUnaryOperation(context) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ static HGlobalObject* New(Zone* zone, HValue* context) {
+ return new(zone) HGlobalObject(context);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalObject)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HGlobalReceiver: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HGlobalReceiver, HValue*);
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HGlobalReceiver(HValue* global_object)
+ : HUnaryOperation(global_object) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+template <int V>
+class HCall: public HTemplateInstruction<V> {
+ public:
+ // The argument count includes the receiver.
+ explicit HCall<V>(int argument_count) : argument_count_(argument_count) {
+ this->set_representation(Representation::Tagged());
+ this->SetAllSideEffects();
+ }
+
+ virtual HType CalculateInferredType() { return HType::Tagged(); }
+
+ virtual int argument_count() const { return argument_count_; }
+
+ virtual bool IsCall() { return true; }
+
+ private:
+ int argument_count_;
+};
+
+
+class HUnaryCall: public HCall<1> {
+ public:
+ HUnaryCall(HValue* value, int argument_count)
+ : HCall<1>(argument_count) {
+ SetOperandAt(0, value);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ HValue* value() { return OperandAt(0); }
+};
+
+
+class HBinaryCall: public HCall<2> {
+ public:
+ HBinaryCall(HValue* first, HValue* second, int argument_count)
+ : HCall<2>(argument_count) {
+ SetOperandAt(0, first);
+ SetOperandAt(1, second);
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* first() { return OperandAt(0); }
+ HValue* second() { return OperandAt(1); }
+};
+
+
+class HInvokeFunction: public HBinaryCall {
+ public:
+ HInvokeFunction(HValue* context, HValue* function, int argument_count)
+ : HBinaryCall(context, function, argument_count) {
+ }
+
+ static HInvokeFunction* New(Zone* zone,
+ HValue* context,
+ HValue* function,
+ int argument_count) {
+ return new(zone) HInvokeFunction(context, function, argument_count);
+ }
+
+ HInvokeFunction(HValue* context,
+ HValue* function,
+ Handle<JSFunction> known_function,
+ int argument_count)
+ : HBinaryCall(context, function, argument_count),
+ known_function_(known_function) {
+ formal_parameter_count_ = known_function.is_null()
+ ? 0 : known_function->shared()->formal_parameter_count();
+ }
+
+ static HInvokeFunction* New(Zone* zone,
+ HValue* context,
+ HValue* function,
+ Handle<JSFunction> known_function,
+ int argument_count) {
+ return new(zone) HInvokeFunction(context, function,
+ known_function, argument_count);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* context() { return first(); }
+ HValue* function() { return second(); }
+ Handle<JSFunction> known_function() { return known_function_; }
+ int formal_parameter_count() const { return formal_parameter_count_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction)
+
+ private:
+ Handle<JSFunction> known_function_;
+ int formal_parameter_count_;
+};
+
+
+class HCallConstantFunction: public HCall<0> {
+ public:
+ HCallConstantFunction(Handle<JSFunction> function, int argument_count)
+ : HCall<0>(argument_count),
+ function_(function),
+ formal_parameter_count_(function->shared()->formal_parameter_count()) {}
+
+ Handle<JSFunction> function() const { return function_; }
+ int formal_parameter_count() const { return formal_parameter_count_; }
+
+ bool IsApplyFunction() const {
+ return function_->code() ==
+ Isolate::Current()->builtins()->builtin(Builtins::kFunctionApply);
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction)
+
+ private:
+ Handle<JSFunction> function_;
+ int formal_parameter_count_;
+};
+
+
+class HCallKeyed: public HBinaryCall {
+ public:
+ HCallKeyed(HValue* context, HValue* key, int argument_count)
+ : HBinaryCall(context, key, argument_count) {
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* context() { return first(); }
+ HValue* key() { return second(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallKeyed)
+};
+
+
+class HCallNamed: public HUnaryCall {
+ public:
+ HCallNamed(HValue* context, Handle<String> name, int argument_count)
+ : HUnaryCall(context, argument_count), name_(name) {
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ HValue* context() { return value(); }
+ Handle<String> name() const { return name_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNamed)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ private:
+ Handle<String> name_;
+};
+
+
+class HCallFunction: public HBinaryCall {
+ public:
+ HCallFunction(HValue* context, HValue* function, int argument_count)
+ : HBinaryCall(context, function, argument_count) {
+ }
+
+ static HCallFunction* New(Zone* zone,
+ HValue* context,
+ HValue* function,
+ int argument_count) {
+ return new(zone) HCallFunction(context, function, argument_count);
+ }
+
+ HValue* context() { return first(); }
+ HValue* function() { return second(); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallFunction)
+};
+
+
+class HCallGlobal: public HUnaryCall {
+ public:
+ HCallGlobal(HValue* context, Handle<String> name, int argument_count)
+ : HUnaryCall(context, argument_count), name_(name) {
+ }
+
+ static HCallGlobal* New(Zone* zone,
+ HValue* context,
+ Handle<String> name,
+ int argument_count) {
+ return new(zone) HCallGlobal(context, name, argument_count);
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ HValue* context() { return value(); }
+ Handle<String> name() const { return name_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallGlobal)
+
+ private:
+ Handle<String> name_;
+};
+
+
+class HCallKnownGlobal: public HCall<0> {
+ public:
+ HCallKnownGlobal(Handle<JSFunction> target, int argument_count)
+ : HCall<0>(argument_count),
+ target_(target),
+ formal_parameter_count_(target->shared()->formal_parameter_count()) { }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<JSFunction> target() const { return target_; }
+ int formal_parameter_count() const { return formal_parameter_count_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal)
+
+ private:
+ Handle<JSFunction> target_;
+ int formal_parameter_count_;
+};
+
+
+class HCallNew: public HBinaryCall {
+ public:
+ HCallNew(HValue* context, HValue* constructor, int argument_count)
+ : HBinaryCall(context, constructor, argument_count) {
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* context() { return first(); }
+ HValue* constructor() { return second(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNew)
+};
+
+
+class HCallNewArray: public HCallNew {
+ public:
+ HCallNewArray(HValue* context, HValue* constructor, int argument_count,
+ Handle<Cell> type_cell, ElementsKind elements_kind)
+ : HCallNew(context, constructor, argument_count),
+ elements_kind_(elements_kind),
+ type_cell_(type_cell) {}
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Cell> property_cell() const {
+ return type_cell_;
+ }
+
+ ElementsKind elements_kind() const { return elements_kind_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNewArray)
+
+ private:
+ ElementsKind elements_kind_;
+ Handle<Cell> type_cell_;
+};
+
+
+class HCallRuntime: public HCall<1> {
+ public:
+ static HCallRuntime* New(Zone* zone,
+ HValue* context,
+ Handle<String> name,
+ const Runtime::Function* c_function,
+ int argument_count) {
+ return new(zone) HCallRuntime(context, name, c_function, argument_count);
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ HValue* context() { return OperandAt(0); }
+ const Runtime::Function* function() const { return c_function_; }
+ Handle<String> name() const { return name_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallRuntime)
+
+ private:
+ HCallRuntime(HValue* context,
+ Handle<String> name,
+ const Runtime::Function* c_function,
+ int argument_count)
+ : HCall<1>(argument_count), c_function_(c_function), name_(name) {
+ SetOperandAt(0, context);
+ }
+
+ const Runtime::Function* c_function_;
+ Handle<String> name_;
+};
+
+
+class HMapEnumLength: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HMapEnumLength, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(MapEnumLength)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HMapEnumLength(HValue* value)
+ : HUnaryOperation(value, HType::Smi()) {
+ set_representation(Representation::Smi());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kDependsOnMaps);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HElementsKind: public HUnaryOperation {
+ public:
+ explicit HElementsKind(HValue* value) : HUnaryOperation(value) {
+ set_representation(Representation::Integer32());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kDependsOnElementsKind);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ElementsKind)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HUnaryMathOperation: public HTemplateInstruction<2> {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* value,
+ BuiltinFunctionId op);
+
+ HValue* context() { return OperandAt(0); }
+ HValue* value() { return OperandAt(1); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ if (index == 0) {
+ return Representation::Tagged();
+ } else {
+ switch (op_) {
+ case kMathFloor:
+ case kMathRound:
+ case kMathSqrt:
+ case kMathPowHalf:
+ case kMathLog:
+ case kMathExp:
+ case kMathSin:
+ case kMathCos:
+ case kMathTan:
+ return Representation::Double();
+ case kMathAbs:
+ return representation();
+ default:
+ UNREACHABLE();
+ return Representation::None();
+ }
+ }
+ }
+
+ virtual Range* InferRange(Zone* zone);
+
+ virtual HValue* Canonicalize();
+ virtual Representation RepresentationFromInputs();
+
+ BuiltinFunctionId op() const { return op_; }
+ const char* OpName() const;
+
+ DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ HUnaryMathOperation* b = HUnaryMathOperation::cast(other);
+ return op_ == b->op();
+ }
+
+ private:
+ HUnaryMathOperation(HValue* context, HValue* value, BuiltinFunctionId op)
+ : HTemplateInstruction<2>(HType::TaggedNumber()), op_(op) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, value);
+ switch (op) {
+ case kMathFloor:
+ case kMathRound:
+ set_representation(Representation::Integer32());
+ break;
+ case kMathAbs:
+ // Not setting representation here: it is None intentionally.
+ SetFlag(kFlexibleRepresentation);
+ // TODO(svenpanne) This flag is actually only needed if representation()
+ // is tagged, and not when it is an unboxed double or unboxed integer.
+ SetGVNFlag(kChangesNewSpacePromotion);
+ break;
+ case kMathLog:
+ case kMathSin:
+ case kMathCos:
+ case kMathTan:
+ set_representation(Representation::Double());
+ // These operations use the TranscendentalCache, so they may allocate.
+ SetGVNFlag(kChangesNewSpacePromotion);
+ break;
+ case kMathExp:
+ case kMathSqrt:
+ case kMathPowHalf:
+ set_representation(Representation::Double());
+ break;
+ default:
+ UNREACHABLE();
+ }
+ SetFlag(kUseGVN);
+ SetFlag(kAllowUndefinedAsNaN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+
+ BuiltinFunctionId op_;
+};
+
+
+class HLoadExternalArrayPointer: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HLoadExternalArrayPointer, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual HType CalculateInferredType() {
+ return HType::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HLoadExternalArrayPointer(HValue* value)
+ : HUnaryOperation(value) {
+ set_representation(Representation::External());
+ // The result of this instruction is idempotent as long as its inputs don't
+ // change. The external array of a specialized array elements object cannot
+ // change once set, so it's no necessary to introduce any additional
+ // dependencies on top of the inputs.
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HCheckMaps: public HTemplateInstruction<2> {
+ public:
+ static HCheckMaps* New(Zone* zone, HValue* context, HValue* value,
+ Handle<Map> map, CompilationInfo* info,
+ HValue *typecheck = NULL);
+ static HCheckMaps* New(Zone* zone, HValue* context,
+ HValue* value, SmallMapList* maps,
+ HValue *typecheck = NULL) {
+ HCheckMaps* check_map = new(zone) HCheckMaps(value, zone, typecheck);
+ for (int i = 0; i < maps->length(); i++) {
+ check_map->Add(maps->at(i), zone);
+ }
+ check_map->map_set_.Sort();
+ return check_map;
+ }
+
+ bool CanOmitMapChecks() { return omit_; }
+
+ virtual bool HasEscapingOperandAt(int index) { return false; }
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+ virtual void HandleSideEffectDominator(GVNFlag side_effect,
+ HValue* dominator);
+ virtual void PrintDataTo(StringStream* stream);
+
+ HValue* value() { return OperandAt(0); }
+ SmallMapList* map_set() { return &map_set_; }
+
+ bool has_migration_target() {
+ return has_migration_target_;
+ }
+
+ virtual void FinalizeUniqueValueId();
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMaps)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ ASSERT_EQ(map_set_.length(), map_unique_ids_.length());
+ HCheckMaps* b = HCheckMaps::cast(other);
+ // Relies on the fact that map_set has been sorted before.
+ if (map_unique_ids_.length() != b->map_unique_ids_.length()) {
+ return false;
+ }
+ for (int i = 0; i < map_unique_ids_.length(); i++) {
+ if (map_unique_ids_.at(i) != b->map_unique_ids_.at(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ void Add(Handle<Map> map, Zone* zone) {
+ map_set_.Add(map, zone);
+ if (!has_migration_target_ && map->is_migration_target()) {
+ has_migration_target_ = true;
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+ }
+
+ // Clients should use one of the static New* methods above.
+ HCheckMaps(HValue* value, Zone *zone, HValue* typecheck)
+ : HTemplateInstruction<2>(value->type()),
+ omit_(false), has_migration_target_(false), map_unique_ids_(0, zone) {
+ SetOperandAt(0, value);
+ // Use the object value for the dependency if NULL is passed.
+ // TODO(titzer): do GVN flags already express this dependency?
+ SetOperandAt(1, typecheck != NULL ? typecheck : value);
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetFlag(kTrackSideEffectDominators);
+ SetGVNFlag(kDependsOnMaps);
+ SetGVNFlag(kDependsOnElementsKind);
+ }
+
+ void omit(CompilationInfo* info) {
+ omit_ = true;
+ for (int i = 0; i < map_set_.length(); i++) {
+ Handle<Map> map = map_set_.at(i);
+ map->AddDependentCompilationInfo(DependentCode::kPrototypeCheckGroup,
+ info);
+ }
+ }
+
+ bool omit_;
+ bool has_migration_target_;
+ SmallMapList map_set_;
+ ZoneList<UniqueValueId> map_unique_ids_;
+};
+
+
+class HCheckFunction: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P2(HCheckFunction, HValue*, Handle<JSFunction>);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual HValue* Canonicalize();
+
+#ifdef DEBUG
+ virtual void Verify();
+#endif
+
+ virtual void FinalizeUniqueValueId() {
+ target_unique_id_ = UniqueValueId(target_);
+ }
+
+ Handle<JSFunction> target() const { return target_; }
+ bool target_in_new_space() const { return target_in_new_space_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckFunction)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ HCheckFunction* b = HCheckFunction::cast(other);
+ return target_unique_id_ == b->target_unique_id_;
+ }
+
+ private:
+ HCheckFunction(HValue* value, Handle<JSFunction> function)
+ : HUnaryOperation(value, value->type()),
+ target_(function), target_unique_id_() {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ target_in_new_space_ = Isolate::Current()->heap()->InNewSpace(*function);
+ }
+
+ Handle<JSFunction> target_;
+ UniqueValueId target_unique_id_;
+ bool target_in_new_space_;
+};
+
+
+class HCheckInstanceType: public HUnaryOperation {
+ public:
+ static HCheckInstanceType* NewIsSpecObject(HValue* value, Zone* zone) {
+ return new(zone) HCheckInstanceType(value, IS_SPEC_OBJECT);
+ }
+ static HCheckInstanceType* NewIsJSArray(HValue* value, Zone* zone) {
+ return new(zone) HCheckInstanceType(value, IS_JS_ARRAY);
+ }
+ static HCheckInstanceType* NewIsString(HValue* value, Zone* zone) {
+ return new(zone) HCheckInstanceType(value, IS_STRING);
+ }
+ static HCheckInstanceType* NewIsInternalizedString(
+ HValue* value, Zone* zone) {
+ return new(zone) HCheckInstanceType(value, IS_INTERNALIZED_STRING);
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual HValue* Canonicalize();
+
+ bool is_interval_check() const { return check_ <= LAST_INTERVAL_CHECK; }
+ void GetCheckInterval(InstanceType* first, InstanceType* last);
+ void GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag);
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType)
+
+ protected:
+ // TODO(ager): It could be nice to allow the ommision of instance
+ // type checks if we have already performed an instance type check
+ // with a larger range.
+ virtual bool DataEquals(HValue* other) {
+ HCheckInstanceType* b = HCheckInstanceType::cast(other);
+ return check_ == b->check_;
+ }
+
+ private:
+ enum Check {
+ IS_SPEC_OBJECT,
+ IS_JS_ARRAY,
+ IS_STRING,
+ IS_INTERNALIZED_STRING,
+ LAST_INTERVAL_CHECK = IS_JS_ARRAY
+ };
+
+ const char* GetCheckName();
+
+ HCheckInstanceType(HValue* value, Check check)
+ : HUnaryOperation(value), check_(check) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ const Check check_;
+};
+
+
+class HCheckSmi: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HCheckSmi, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual HValue* Canonicalize() {
+ HType value_type = value()->type();
+ if (value_type.IsSmi()) {
+ return NULL;
+ }
+ return this;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckSmi)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HCheckSmi(HValue* value) : HUnaryOperation(value, HType::Smi()) {
+ set_representation(Representation::Smi());
+ SetFlag(kUseGVN);
+ }
+};
+
+
+class HIsNumberAndBranch: public HUnaryControlInstruction {
+ public:
+ explicit HIsNumberAndBranch(HValue* value)
+ : HUnaryControlInstruction(value, NULL, NULL) {
+ SetFlag(kFlexibleRepresentation);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsNumberAndBranch)
+};
+
+
+class HCheckHeapObject: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HCheckHeapObject, HValue*);
+
+ virtual bool HasEscapingOperandAt(int index) { return false; }
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+#ifdef DEBUG
+ virtual void Verify();
+#endif
+
+ virtual HValue* Canonicalize() {
+ return value()->type().IsHeapObject() ? NULL : this;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckHeapObject)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HCheckHeapObject(HValue* value)
+ : HUnaryOperation(value, HType::NonPrimitive()) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+};
+
+
+class InductionVariableData;
+
+
+struct InductionVariableLimitUpdate {
+ InductionVariableData* updated_variable;
+ HValue* limit;
+ bool limit_is_upper;
+ bool limit_is_included;
+
+ InductionVariableLimitUpdate()
+ : updated_variable(NULL), limit(NULL),
+ limit_is_upper(false), limit_is_included(false) {}
+};
+
+
+class HBoundsCheck;
+class HPhi;
+class HConstant;
+class HBitwise;
+
+
+class InductionVariableData : public ZoneObject {
+ public:
+ class InductionVariableCheck : public ZoneObject {
+ public:
+ HBoundsCheck* check() { return check_; }
+ InductionVariableCheck* next() { return next_; }
+ bool HasUpperLimit() { return upper_limit_ >= 0; }
+ int32_t upper_limit() {
+ ASSERT(HasUpperLimit());
+ return upper_limit_;
+ }
+ void set_upper_limit(int32_t upper_limit) {
+ upper_limit_ = upper_limit;
+ }
+
+ bool processed() { return processed_; }
+ void set_processed() { processed_ = true; }
+
+ InductionVariableCheck(HBoundsCheck* check,
+ InductionVariableCheck* next,
+ int32_t upper_limit = kNoLimit)
+ : check_(check), next_(next), upper_limit_(upper_limit),
+ processed_(false) {}
+
+ private:
+ HBoundsCheck* check_;
+ InductionVariableCheck* next_;
+ int32_t upper_limit_;
+ bool processed_;
+ };
+
+ class ChecksRelatedToLength : public ZoneObject {
+ public:
+ HValue* length() { return length_; }
+ ChecksRelatedToLength* next() { return next_; }
+ InductionVariableCheck* checks() { return checks_; }
+
+ void AddCheck(HBoundsCheck* check, int32_t upper_limit = kNoLimit);
+ void CloseCurrentBlock();
+
+ ChecksRelatedToLength(HValue* length, ChecksRelatedToLength* next)
+ : length_(length), next_(next), checks_(NULL),
+ first_check_in_block_(NULL),
+ added_index_(NULL),
+ added_constant_(NULL),
+ current_and_mask_in_block_(0),
+ current_or_mask_in_block_(0) {}
+
+ private:
+ void UseNewIndexInCurrentBlock(Token::Value token,
+ int32_t mask,
+ HValue* index_base,
+ HValue* context);
+
+ HBoundsCheck* first_check_in_block() { return first_check_in_block_; }
+ HBitwise* added_index() { return added_index_; }
+ void set_added_index(HBitwise* index) { added_index_ = index; }
+ HConstant* added_constant() { return added_constant_; }
+ void set_added_constant(HConstant* constant) { added_constant_ = constant; }
+ int32_t current_and_mask_in_block() { return current_and_mask_in_block_; }
+ int32_t current_or_mask_in_block() { return current_or_mask_in_block_; }
+ int32_t current_upper_limit() { return current_upper_limit_; }
+
+ HValue* length_;
+ ChecksRelatedToLength* next_;
+ InductionVariableCheck* checks_;
+
+ HBoundsCheck* first_check_in_block_;
+ HBitwise* added_index_;
+ HConstant* added_constant_;
+ int32_t current_and_mask_in_block_;
+ int32_t current_or_mask_in_block_;
+ int32_t current_upper_limit_;
+ };
+
+ struct LimitFromPredecessorBlock {
+ InductionVariableData* variable;
+ Token::Value token;
+ HValue* limit;
+ HBasicBlock* other_target;
+
+ bool LimitIsValid() { return token != Token::ILLEGAL; }
+
+ bool LimitIsIncluded() {
+ return Token::IsEqualityOp(token) ||
+ token == Token::GTE || token == Token::LTE;
+ }
+ bool LimitIsUpper() {
+ return token == Token::LTE || token == Token::LT || token == Token::NE;
+ }
+
+ LimitFromPredecessorBlock()
+ : variable(NULL),
+ token(Token::ILLEGAL),
+ limit(NULL),
+ other_target(NULL) {}
+ };
+
+ static const int32_t kNoLimit = -1;
+
+ static InductionVariableData* ExaminePhi(HPhi* phi);
+ static void ComputeLimitFromPredecessorBlock(
+ HBasicBlock* block,
+ LimitFromPredecessorBlock* result);
+ static bool ComputeInductionVariableLimit(
+ HBasicBlock* block,
+ InductionVariableLimitUpdate* additional_limit);
+
+ struct BitwiseDecompositionResult {
+ HValue* base;
+ int32_t and_mask;
+ int32_t or_mask;
+ HValue* context;
+
+ BitwiseDecompositionResult()
+ : base(NULL), and_mask(0), or_mask(0), context(NULL) {}
+ };
+ static void DecomposeBitwise(HValue* value,
+ BitwiseDecompositionResult* result);
+
+ void AddCheck(HBoundsCheck* check, int32_t upper_limit = kNoLimit);
+
+ bool CheckIfBranchIsLoopGuard(Token::Value token,
+ HBasicBlock* current_branch,
+ HBasicBlock* other_branch);
+
+ void UpdateAdditionalLimit(InductionVariableLimitUpdate* update);
+
+ HPhi* phi() { return phi_; }
+ HValue* base() { return base_; }
+ int32_t increment() { return increment_; }
+ HValue* limit() { return limit_; }
+ bool limit_included() { return limit_included_; }
+ HBasicBlock* limit_validity() { return limit_validity_; }
+ HBasicBlock* induction_exit_block() { return induction_exit_block_; }
+ HBasicBlock* induction_exit_target() { return induction_exit_target_; }
+ ChecksRelatedToLength* checks() { return checks_; }
+ HValue* additional_upper_limit() { return additional_upper_limit_; }
+ bool additional_upper_limit_is_included() {
+ return additional_upper_limit_is_included_;
+ }
+ HValue* additional_lower_limit() { return additional_lower_limit_; }
+ bool additional_lower_limit_is_included() {
+ return additional_lower_limit_is_included_;
+ }
+
+ bool LowerLimitIsNonNegativeConstant() {
+ if (base()->IsInteger32Constant() && base()->GetInteger32Constant() >= 0) {
+ return true;
+ }
+ if (additional_lower_limit() != NULL &&
+ additional_lower_limit()->IsInteger32Constant() &&
+ additional_lower_limit()->GetInteger32Constant() >= 0) {
+ // Ignoring the corner case of !additional_lower_limit_is_included()
+ // is safe, handling it adds unneeded complexity.
+ return true;
+ }
+ return false;
+ }
+
+ int32_t ComputeUpperLimit(int32_t and_mask, int32_t or_mask);
+
+ private:
+ template <class T> void swap(T* a, T* b) {
+ T c(*a);
+ *a = *b;
+ *b = c;
+ }
+
+ InductionVariableData(HPhi* phi, HValue* base, int32_t increment)
+ : phi_(phi), base_(IgnoreOsrValue(base)), increment_(increment),
+ limit_(NULL), limit_included_(false), limit_validity_(NULL),
+ induction_exit_block_(NULL), induction_exit_target_(NULL),
+ checks_(NULL),
+ additional_upper_limit_(NULL),
+ additional_upper_limit_is_included_(false),
+ additional_lower_limit_(NULL),
+ additional_lower_limit_is_included_(false) {}
+
+ static int32_t ComputeIncrement(HPhi* phi, HValue* phi_operand);
+
+ static HValue* IgnoreOsrValue(HValue* v);
+ static InductionVariableData* GetInductionVariableData(HValue* v);
+
+ HPhi* phi_;
+ HValue* base_;
+ int32_t increment_;
+ HValue* limit_;
+ bool limit_included_;
+ HBasicBlock* limit_validity_;
+ HBasicBlock* induction_exit_block_;
+ HBasicBlock* induction_exit_target_;
+ ChecksRelatedToLength* checks_;
+ HValue* additional_upper_limit_;
+ bool additional_upper_limit_is_included_;
+ HValue* additional_lower_limit_;
+ bool additional_lower_limit_is_included_;
+};
+
+
+class HPhi: public HValue {
+ public:
+ HPhi(int merged_index, Zone* zone)
+ : inputs_(2, zone),
+ merged_index_(merged_index),
+ phi_id_(-1),
+ induction_variable_data_(NULL) {
+ for (int i = 0; i < Representation::kNumRepresentations; i++) {
+ non_phi_uses_[i] = 0;
+ indirect_uses_[i] = 0;
+ }
+ ASSERT(merged_index >= 0 || merged_index == kInvalidMergedIndex);
+ SetFlag(kFlexibleRepresentation);
+ SetFlag(kAllowUndefinedAsNaN);
+ }
+
+ virtual Representation RepresentationFromInputs();
+
+ virtual Range* InferRange(Zone* zone);
+ virtual void InferRepresentation(HInferRepresentationPhase* h_infer);
+ virtual Representation RequiredInputRepresentation(int index) {
+ return representation();
+ }
+ virtual Representation KnownOptimalRepresentation() {
+ return representation();
+ }
+ virtual HType CalculateInferredType();
+ virtual int OperandCount() { return inputs_.length(); }
+ virtual HValue* OperandAt(int index) const { return inputs_[index]; }
+ HValue* GetRedundantReplacement();
+ void AddInput(HValue* value);
+ bool HasRealUses();
+
+ bool IsReceiver() const { return merged_index_ == 0; }
+ bool HasMergedIndex() const { return merged_index_ != kInvalidMergedIndex; }
+
+ int merged_index() const { return merged_index_; }
+
+ InductionVariableData* induction_variable_data() {
+ return induction_variable_data_;
+ }
+ bool IsInductionVariable() {
+ return induction_variable_data_ != NULL;
+ }
+ bool IsLimitedInductionVariable() {
+ return IsInductionVariable() &&
+ induction_variable_data_->limit() != NULL;
+ }
+ void DetectInductionVariable() {
+ ASSERT(induction_variable_data_ == NULL);
+ induction_variable_data_ = InductionVariableData::ExaminePhi(this);
+ }
+
+ virtual void PrintTo(StringStream* stream);
+
+#ifdef DEBUG
+ virtual void Verify();
+#endif
+
+ void InitRealUses(int id);
+ void AddNonPhiUsesFrom(HPhi* other);
+ void AddIndirectUsesTo(int* use_count);
+
+ int tagged_non_phi_uses() const {
+ return non_phi_uses_[Representation::kTagged];
+ }
+ int smi_non_phi_uses() const {
+ return non_phi_uses_[Representation::kSmi];
+ }
+ int int32_non_phi_uses() const {
+ return non_phi_uses_[Representation::kInteger32];
+ }
+ int double_non_phi_uses() const {
+ return non_phi_uses_[Representation::kDouble];
+ }
+ int tagged_indirect_uses() const {
+ return indirect_uses_[Representation::kTagged];
+ }
+ int smi_indirect_uses() const {
+ return indirect_uses_[Representation::kSmi];
+ }
+ int int32_indirect_uses() const {
+ return indirect_uses_[Representation::kInteger32];
+ }
+ int double_indirect_uses() const {
+ return indirect_uses_[Representation::kDouble];
+ }
+ int phi_id() { return phi_id_; }
+
+ static HPhi* cast(HValue* value) {
+ ASSERT(value->IsPhi());
+ return reinterpret_cast<HPhi*>(value);
+ }
+ virtual Opcode opcode() const { return HValue::kPhi; }
+
+ void SimplifyConstantInputs();
+
+ // Marker value representing an invalid merge index.
+ static const int kInvalidMergedIndex = -1;
+
+ protected:
+ virtual void DeleteFromGraph();
+ virtual void InternalSetOperandAt(int index, HValue* value) {
+ inputs_[index] = value;
+ }
+
+ private:
+ ZoneList<HValue*> inputs_;
+ int merged_index_;
+
+ int non_phi_uses_[Representation::kNumRepresentations];
+ int indirect_uses_[Representation::kNumRepresentations];
+ int phi_id_;
+ InductionVariableData* induction_variable_data_;
+
+ // TODO(titzer): we can't eliminate the receiver for generating backtraces
+ virtual bool IsDeletable() const { return !IsReceiver(); }
+};
+
+
+// Common base class for HArgumentsObject and HCapturedObject.
+class HDematerializedObject: public HTemplateInstruction<0> {
+ public:
+ HDematerializedObject(int count, Zone* zone) : values_(count, zone) {}
+
+ virtual int OperandCount() { return values_.length(); }
+ virtual HValue* OperandAt(int index) const { return values_[index]; }
+
+ virtual bool HasEscapingOperandAt(int index) { return false; }
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ protected:
+ virtual void InternalSetOperandAt(int index, HValue* value) {
+ values_[index] = value;
+ }
+
+ // List of values tracked by this marker.
+ ZoneList<HValue*> values_;
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HArgumentsObject: public HDematerializedObject {
+ public:
+ static HArgumentsObject* New(Zone* zone, HValue* context, int count) {
+ return new(zone) HArgumentsObject(count, zone);
+ }
+
+ // The values contain a list of all elements in the arguments object
+ // including the receiver object, which is skipped when materializing.
+ const ZoneList<HValue*>* arguments_values() const { return &values_; }
+ int arguments_count() const { return values_.length(); }
+
+ void AddArgument(HValue* argument, Zone* zone) {
+ values_.Add(NULL, zone); // Resize list.
+ SetOperandAt(values_.length() - 1, argument);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsObject)
+
+ private:
+ HArgumentsObject(int count, Zone* zone)
+ : HDematerializedObject(count, zone) {
+ set_representation(Representation::Tagged());
+ SetFlag(kIsArguments);
+ }
+};
+
+
+class HCapturedObject: public HDematerializedObject {
+ public:
+ HCapturedObject(int length, Zone* zone)
+ : HDematerializedObject(length, zone) {
+ set_representation(Representation::Tagged());
+ values_.AddBlock(NULL, length, zone); // Resize list.
+ }
+
+ // The values contain a list of all in-object properties inside the
+ // captured object and is index by field index. Properties in the
+ // properties or elements backing store are not tracked here.
+ const ZoneList<HValue*>* values() const { return &values_; }
+ int length() const { return values_.length(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(CapturedObject)
+};
+
+
+class HConstant: public HTemplateInstruction<0> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HConstant, int32_t);
+ DECLARE_INSTRUCTION_FACTORY_P2(HConstant, int32_t, Representation);
+ DECLARE_INSTRUCTION_FACTORY_P1(HConstant, double);
+ DECLARE_INSTRUCTION_FACTORY_P1(HConstant, Handle<Object>);
+ DECLARE_INSTRUCTION_FACTORY_P1(HConstant, ExternalReference);
+
+ Handle<Object> handle() {
+ if (handle_.is_null()) {
+ Factory* factory = Isolate::Current()->factory();
+ // Default arguments to is_not_in_new_space depend on this heap number
+ // to be tenured so that it's guaranteed not be be located in new space.
+ handle_ = factory->NewNumber(double_value_, TENURED);
+ }
+ AllowDeferredHandleDereference smi_check;
+ ASSERT(has_int32_value_ || !handle_->IsSmi());
+ return handle_;
+ }
+
+ bool InstanceOf(Handle<Map> map) {
+ Handle<Object> constant_object = handle();
+ return constant_object->IsJSObject() &&
+ Handle<JSObject>::cast(constant_object)->map() == *map;
+ }
+
+ bool IsSpecialDouble() const {
+ return has_double_value_ &&
+ (BitCast<int64_t>(double_value_) == BitCast<int64_t>(-0.0) ||
+ FixedDoubleArray::is_the_hole_nan(double_value_) ||
+ std::isnan(double_value_));
+ }
+
+ bool NotInNewSpace() const {
+ return is_not_in_new_space_;
+ }
+
+ bool ImmortalImmovable() const {
+ if (has_int32_value_) {
+ return false;
+ }
+ if (has_double_value_) {
+ if (IsSpecialDouble()) {
+ return true;
+ }
+ return false;
+ }
+ if (has_external_reference_value_) {
+ return false;
+ }
+
+ ASSERT(!handle_.is_null());
+ Heap* heap = isolate()->heap();
+ ASSERT(unique_id_ != UniqueValueId(heap->minus_zero_value()));
+ ASSERT(unique_id_ != UniqueValueId(heap->nan_value()));
+ return unique_id_ == UniqueValueId(heap->undefined_value()) ||
+ unique_id_ == UniqueValueId(heap->null_value()) ||
+ unique_id_ == UniqueValueId(heap->true_value()) ||
+ unique_id_ == UniqueValueId(heap->false_value()) ||
+ unique_id_ == UniqueValueId(heap->the_hole_value()) ||
+ unique_id_ == UniqueValueId(heap->empty_string());
+ }
+
+ bool IsCell() const {
+ return is_cell_;
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ virtual Representation KnownOptimalRepresentation() {
+ if (HasSmiValue() && kSmiValueSize == 31) return Representation::Smi();
+ if (HasInteger32Value()) return Representation::Integer32();
+ if (HasNumberValue()) return Representation::Double();
+ if (HasExternalReferenceValue()) return Representation::External();
+ return Representation::Tagged();
+ }
+
+ virtual bool EmitAtUses();
+ virtual void PrintDataTo(StringStream* stream);
+ bool IsInteger() { return handle()->IsSmi(); }
+ HConstant* CopyToRepresentation(Representation r, Zone* zone) const;
+ Maybe<HConstant*> CopyToTruncatedInt32(Zone* zone);
+ Maybe<HConstant*> CopyToTruncatedNumber(Zone* zone);
+ bool HasInteger32Value() const { return has_int32_value_; }
+ int32_t Integer32Value() const {
+ ASSERT(HasInteger32Value());
+ return int32_value_;
+ }
+ bool HasSmiValue() const { return has_smi_value_; }
+ bool HasDoubleValue() const { return has_double_value_; }
+ double DoubleValue() const {
+ ASSERT(HasDoubleValue());
+ return double_value_;
+ }
+ bool IsTheHole() const {
+ if (HasDoubleValue() && FixedDoubleArray::is_the_hole_nan(double_value_)) {
+ return true;
+ }
+ Heap* heap = isolate()->heap();
+ if (!handle_.is_null() && *handle_ == heap->the_hole_value()) {
+ return true;
+ }
+ return false;
+ }
+ bool HasNumberValue() const { return has_double_value_; }
+ int32_t NumberValueAsInteger32() const {
+ ASSERT(HasNumberValue());
+ // Irrespective of whether a numeric HConstant can be safely
+ // represented as an int32, we store the (in some cases lossy)
+ // representation of the number in int32_value_.
+ return int32_value_;
+ }
+ bool HasStringValue() const {
+ if (has_double_value_ || has_int32_value_) return false;
+ ASSERT(!handle_.is_null());
+ return type_.IsString();
+ }
+ Handle<String> StringValue() const {
+ ASSERT(HasStringValue());
+ return Handle<String>::cast(handle_);
+ }
+ bool HasInternalizedStringValue() const {
+ return HasStringValue() && is_internalized_string_;
+ }
+
+ bool HasExternalReferenceValue() const {
+ return has_external_reference_value_;
+ }
+ ExternalReference ExternalReferenceValue() const {
+ return external_reference_value_;
+ }
+
+ bool HasBooleanValue() const { return type_.IsBoolean(); }
+ bool BooleanValue() const { return boolean_value_; }
+
+ virtual intptr_t Hashcode() {
+ if (has_int32_value_) {
+ return static_cast<intptr_t>(int32_value_);
+ } else if (has_double_value_) {
+ return static_cast<intptr_t>(BitCast<int64_t>(double_value_));
+ } else if (has_external_reference_value_) {
+ return reinterpret_cast<intptr_t>(external_reference_value_.address());
+ } else {
+ ASSERT(!handle_.is_null());
+ return unique_id_.Hashcode();
+ }
+ }
+
+ virtual void FinalizeUniqueValueId() {
+ if (!has_double_value_ && !has_external_reference_value_) {
+ ASSERT(!handle_.is_null());
+ unique_id_ = UniqueValueId(handle_);
+ }
+ }
+
+ bool UniqueValueIdsMatch(UniqueValueId other) {
+ return !has_double_value_ && !has_external_reference_value_ &&
+ unique_id_ == other;
+ }
+
+#ifdef DEBUG
+ virtual void Verify() { }
+#endif
+
+ DECLARE_CONCRETE_INSTRUCTION(Constant)
+
+ protected:
+ virtual Range* InferRange(Zone* zone);
+
+ virtual bool DataEquals(HValue* other) {
+ HConstant* other_constant = HConstant::cast(other);
+ if (has_int32_value_) {
+ return other_constant->has_int32_value_ &&
+ int32_value_ == other_constant->int32_value_;
+ } else if (has_double_value_) {
+ return other_constant->has_double_value_ &&
+ BitCast<int64_t>(double_value_) ==
+ BitCast<int64_t>(other_constant->double_value_);
+ } else if (has_external_reference_value_) {
+ return other_constant->has_external_reference_value_ &&
+ external_reference_value_ ==
+ other_constant->external_reference_value_;
+ } else {
+ ASSERT(!handle_.is_null());
+ return !other_constant->handle_.is_null() &&
+ unique_id_ == other_constant->unique_id_;
+ }
+ }
+
+ private:
+ friend class HGraph;
+ HConstant(Handle<Object> handle, Representation r = Representation::None());
+ HConstant(int32_t value,
+ Representation r = Representation::None(),
+ bool is_not_in_new_space = true,
+ Handle<Object> optional_handle = Handle<Object>::null());
+ HConstant(double value,
+ Representation r = Representation::None(),
+ bool is_not_in_new_space = true,
+ Handle<Object> optional_handle = Handle<Object>::null());
+ HConstant(Handle<Object> handle,
+ UniqueValueId unique_id,
+ Representation r,
+ HType type,
+ bool is_internalized_string,
+ bool is_not_in_new_space,
+ bool is_cell,
+ bool boolean_value);
+ explicit HConstant(ExternalReference reference);
+
+ void Initialize(Representation r);
+
+ virtual bool IsDeletable() const { return true; }
+
+ // If this is a numerical constant, handle_ either points to to the
+ // HeapObject the constant originated from or is null. If the
+ // constant is non-numeric, handle_ always points to a valid
+ // constant HeapObject.
+ Handle<Object> handle_;
+ UniqueValueId unique_id_;
+
+ // We store the HConstant in the most specific form safely possible.
+ // The two flags, has_int32_value_ and has_double_value_ tell us if
+ // int32_value_ and double_value_ hold valid, safe representations
+ // of the constant. has_int32_value_ implies has_double_value_ but
+ // not the converse.
+ bool has_smi_value_ : 1;
+ bool has_int32_value_ : 1;
+ bool has_double_value_ : 1;
+ bool has_external_reference_value_ : 1;
+ bool is_internalized_string_ : 1; // TODO(yangguo): make this part of HType.
+ bool is_not_in_new_space_ : 1;
+ bool is_cell_ : 1;
+ bool boolean_value_ : 1;
+ int32_t int32_value_;
+ double double_value_;
+ ExternalReference external_reference_value_;
+};
+
+
+class HBinaryOperation: public HTemplateInstruction<3> {
+ public:
+ HBinaryOperation(HValue* context, HValue* left, HValue* right,
+ HType type = HType::Tagged())
+ : HTemplateInstruction<3>(type),
+ observed_output_representation_(Representation::None()) {
+ ASSERT(left != NULL && right != NULL);
+ SetOperandAt(0, context);
+ SetOperandAt(1, left);
+ SetOperandAt(2, right);
+ observed_input_representation_[0] = Representation::None();
+ observed_input_representation_[1] = Representation::None();
+ }
+
+ HValue* context() const { return OperandAt(0); }
+ HValue* left() const { return OperandAt(1); }
+ HValue* right() const { return OperandAt(2); }
+
+ // True if switching left and right operands likely generates better code.
+ bool AreOperandsBetterSwitched() {
+ if (!IsCommutative()) return false;
+
+ // Constant operands are better off on the right, they can be inlined in
+ // many situations on most platforms.
+ if (left()->IsConstant()) return true;
+ if (right()->IsConstant()) return false;
+
+ // Otherwise, if there is only one use of the right operand, it would be
+ // better off on the left for platforms that only have 2-arg arithmetic
+ // ops (e.g ia32, x64) that clobber the left operand.
+ return right()->UseCount() == 1;
+ }
+
+ HValue* BetterLeftOperand() {
+ return AreOperandsBetterSwitched() ? right() : left();
+ }
+
+ HValue* BetterRightOperand() {
+ return AreOperandsBetterSwitched() ? left() : right();
+ }
+
+ void set_observed_input_representation(int index, Representation rep) {
+ ASSERT(index >= 1 && index <= 2);
+ observed_input_representation_[index - 1] = rep;
+ }
+
+ virtual void initialize_output_representation(Representation observed) {
+ observed_output_representation_ = observed;
+ }
+
+ virtual Representation observed_input_representation(int index) {
+ if (index == 0) return Representation::Tagged();
+ return observed_input_representation_[index - 1];
+ }
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ Representation rep = !FLAG_smi_binop && new_rep.IsSmi()
+ ? Representation::Integer32() : new_rep;
+ HValue::UpdateRepresentation(rep, h_infer, reason);
+ }
+
+ virtual void InferRepresentation(HInferRepresentationPhase* h_infer);
+ virtual Representation RepresentationFromInputs();
+ Representation RepresentationFromOutput();
+ virtual void AssumeRepresentation(Representation r);
+
+ virtual bool IsCommutative() const { return false; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ if (index == 0) return Representation::Tagged();
+ return representation();
+ }
+
+ DECLARE_ABSTRACT_INSTRUCTION(BinaryOperation)
+
+ private:
+ bool IgnoreObservedOutputRepresentation(Representation current_rep);
+
+ Representation observed_input_representation_[2];
+ Representation observed_output_representation_;
+};
+
+
+class HWrapReceiver: public HTemplateInstruction<2> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P2(HWrapReceiver, HValue*, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* receiver() { return OperandAt(0); }
+ HValue* function() { return OperandAt(1); }
+
+ virtual HValue* Canonicalize();
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(WrapReceiver)
+
+ private:
+ HWrapReceiver(HValue* receiver, HValue* function) {
+ set_representation(Representation::Tagged());
+ SetOperandAt(0, receiver);
+ SetOperandAt(1, function);
+ }
+};
+
+
+class HApplyArguments: public HTemplateInstruction<4> {
+ public:
+ HApplyArguments(HValue* function,
+ HValue* receiver,
+ HValue* length,
+ HValue* elements) {
+ set_representation(Representation::Tagged());
+ SetOperandAt(0, function);
+ SetOperandAt(1, receiver);
+ SetOperandAt(2, length);
+ SetOperandAt(3, elements);
+ SetAllSideEffects();
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ // The length is untagged, all other inputs are tagged.
+ return (index == 2)
+ ? Representation::Integer32()
+ : Representation::Tagged();
+ }
+
+ HValue* function() { return OperandAt(0); }
+ HValue* receiver() { return OperandAt(1); }
+ HValue* length() { return OperandAt(2); }
+ HValue* elements() { return OperandAt(3); }
+
+ DECLARE_CONCRETE_INSTRUCTION(ApplyArguments)
+};
+
+
+class HArgumentsElements: public HTemplateInstruction<0> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HArgumentsElements, bool);
+
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ bool from_inlined() const { return from_inlined_; }
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HArgumentsElements(bool from_inlined) : from_inlined_(from_inlined) {
+ // The value produced by this instruction is a pointer into the stack
+ // that looks as if it was a smi because of alignment.
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+
+ bool from_inlined_;
+};
+
+
+class HArgumentsLength: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HArgumentsLength, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ explicit HArgumentsLength(HValue* value) : HUnaryOperation(value) {
+ set_representation(Representation::Integer32());
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HAccessArgumentsAt: public HTemplateInstruction<3> {
+ public:
+ HAccessArgumentsAt(HValue* arguments, HValue* length, HValue* index) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetOperandAt(0, arguments);
+ SetOperandAt(1, length);
+ SetOperandAt(2, index);
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ // The arguments elements is considered tagged.
+ return index == 0
+ ? Representation::Tagged()
+ : Representation::Integer32();
+ }
+
+ HValue* arguments() { return OperandAt(0); }
+ HValue* length() { return OperandAt(1); }
+ HValue* index() { return OperandAt(2); }
+
+ DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt)
+
+ virtual bool DataEquals(HValue* other) { return true; }
+};
+
+
+class HBoundsCheckBaseIndexInformation;
+
+
+class HBoundsCheck: public HTemplateInstruction<2> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P2(HBoundsCheck, HValue*, HValue*);
+
+ bool skip_check() const { return skip_check_; }
+ void set_skip_check() { skip_check_ = true; }
+
+ HValue* base() { return base_; }
+ int offset() { return offset_; }
+ int scale() { return scale_; }
+
+ void ApplyIndexChange();
+ bool DetectCompoundIndex() {
+ ASSERT(base() == NULL);
+
+ DecompositionResult decomposition;
+ if (index()->TryDecompose(&decomposition)) {
+ base_ = decomposition.base();
+ offset_ = decomposition.offset();
+ scale_ = decomposition.scale();
+ return true;
+ } else {
+ base_ = index();
+ offset_ = 0;
+ scale_ = 0;
+ return false;
+ }
+ }
+
+ virtual Representation RequiredInputRepresentation(int arg_index) {
+ return representation();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+ virtual void InferRepresentation(HInferRepresentationPhase* h_infer);
+
+ HValue* index() { return OperandAt(0); }
+ HValue* length() { return OperandAt(1); }
+ bool allow_equality() { return allow_equality_; }
+ void set_allow_equality(bool v) { allow_equality_ = v; }
+
+ virtual int RedefinedOperandIndex() { return 0; }
+ virtual bool IsPurelyInformativeDefinition() { return skip_check(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(BoundsCheck)
+
+ protected:
+ friend class HBoundsCheckBaseIndexInformation;
+
+ virtual bool DataEquals(HValue* other) { return true; }
+ bool skip_check_;
+ HValue* base_;
+ int offset_;
+ int scale_;
+ bool allow_equality_;
+
+ private:
+ // Normally HBoundsCheck should be created using the
+ // HGraphBuilder::AddBoundsCheck() helper.
+ // However when building stubs, where we know that the arguments are Int32,
+ // it makes sense to invoke this constructor directly.
+ HBoundsCheck(HValue* index, HValue* length)
+ : skip_check_(false),
+ base_(NULL), offset_(0), scale_(0),
+ allow_equality_(false) {
+ SetOperandAt(0, index);
+ SetOperandAt(1, length);
+ SetFlag(kFlexibleRepresentation);
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const {
+ return skip_check() && !FLAG_debug_code;
+ }
+};
+
+
+class HBoundsCheckBaseIndexInformation: public HTemplateInstruction<2> {
+ public:
+ explicit HBoundsCheckBaseIndexInformation(HBoundsCheck* check) {
+ DecompositionResult decomposition;
+ if (check->index()->TryDecompose(&decomposition)) {
+ SetOperandAt(0, decomposition.base());
+ SetOperandAt(1, check);
+ } else {
+ UNREACHABLE();
+ }
+ }
+
+ HValue* base_index() { return OperandAt(0); }
+ HBoundsCheck* bounds_check() { return HBoundsCheck::cast(OperandAt(1)); }
+
+ DECLARE_CONCRETE_INSTRUCTION(BoundsCheckBaseIndexInformation)
+
+ virtual Representation RequiredInputRepresentation(int arg_index) {
+ return representation();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual int RedefinedOperandIndex() { return 0; }
+ virtual bool IsPurelyInformativeDefinition() { return true; }
+};
+
+
+class HBitwiseBinaryOperation: public HBinaryOperation {
+ public:
+ HBitwiseBinaryOperation(HValue* context, HValue* left, HValue* right,
+ HType type = HType::Tagged())
+ : HBinaryOperation(context, left, right, type) {
+ SetFlag(kFlexibleRepresentation);
+ SetFlag(kTruncatingToInt32);
+ SetFlag(kAllowUndefinedAsNaN);
+ SetAllSideEffects();
+ }
+
+ virtual void RepresentationChanged(Representation to) {
+ if (!to.IsTagged()) {
+ ASSERT(to.IsSmiOrInteger32());
+ ClearAllSideEffects();
+ SetFlag(kUseGVN);
+ } else {
+ SetAllSideEffects();
+ ClearFlag(kUseGVN);
+ }
+ }
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ // We only generate either int32 or generic tagged bitwise operations.
+ if (new_rep.IsDouble()) new_rep = Representation::Integer32();
+ HBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason);
+ }
+
+ virtual Representation observed_input_representation(int index) {
+ Representation r = HBinaryOperation::observed_input_representation(index);
+ if (r.IsDouble()) return Representation::Integer32();
+ return r;
+ }
+
+ virtual void initialize_output_representation(Representation observed) {
+ if (observed.IsDouble()) observed = Representation::Integer32();
+ HBinaryOperation::initialize_output_representation(observed);
+ }
+
+ DECLARE_ABSTRACT_INSTRUCTION(BitwiseBinaryOperation)
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HMathFloorOfDiv: public HBinaryOperation {
+ public:
+ static HMathFloorOfDiv* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right) {
+ return new(zone) HMathFloorOfDiv(context, left, right);
+ }
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Integer32();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ HMathFloorOfDiv(HValue* context, HValue* left, HValue* right)
+ : HBinaryOperation(context, left, right) {
+ set_representation(Representation::Integer32());
+ SetFlag(kUseGVN);
+ SetFlag(kCanOverflow);
+ if (!right->IsConstant()) {
+ SetFlag(kCanBeDivByZero);
+ }
+ SetFlag(kAllowUndefinedAsNaN);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HArithmeticBinaryOperation: public HBinaryOperation {
+ public:
+ HArithmeticBinaryOperation(HValue* context, HValue* left, HValue* right)
+ : HBinaryOperation(context, left, right, HType::TaggedNumber()) {
+ SetAllSideEffects();
+ SetFlag(kFlexibleRepresentation);
+ SetFlag(kAllowUndefinedAsNaN);
+ }
+
+ virtual void RepresentationChanged(Representation to) {
+ if (to.IsTagged()) {
+ SetAllSideEffects();
+ ClearFlag(kUseGVN);
+ } else {
+ ClearAllSideEffects();
+ SetFlag(kUseGVN);
+ }
+ }
+
+ DECLARE_ABSTRACT_INSTRUCTION(ArithmeticBinaryOperation)
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HCompareGeneric: public HBinaryOperation {
+ public:
+ HCompareGeneric(HValue* context,
+ HValue* left,
+ HValue* right,
+ Token::Value token)
+ : HBinaryOperation(context, left, right, HType::Boolean()),
+ token_(token) {
+ ASSERT(Token::IsCompareOp(token));
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return index == 0
+ ? Representation::Tagged()
+ : representation();
+ }
+
+ Token::Value token() const { return token_; }
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareGeneric)
+
+ private:
+ Token::Value token_;
+};
+
+
+class HCompareNumericAndBranch: public HTemplateControlInstruction<2, 2> {
+ public:
+ HCompareNumericAndBranch(HValue* left, HValue* right, Token::Value token)
+ : token_(token) {
+ SetFlag(kFlexibleRepresentation);
+ ASSERT(Token::IsCompareOp(token));
+ SetOperandAt(0, left);
+ SetOperandAt(1, right);
+ }
+
+ HValue* left() { return OperandAt(0); }
+ HValue* right() { return OperandAt(1); }
+ Token::Value token() const { return token_; }
+
+ void set_observed_input_representation(Representation left,
+ Representation right) {
+ observed_input_representation_[0] = left;
+ observed_input_representation_[1] = right;
+ }
+
+ virtual void InferRepresentation(HInferRepresentationPhase* h_infer);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return representation();
+ }
+ virtual Representation observed_input_representation(int index) {
+ return observed_input_representation_[index];
+ }
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareNumericAndBranch)
+
+ private:
+ Representation observed_input_representation_[2];
+ Token::Value token_;
+};
+
+
+class HCompareHoleAndBranch: public HTemplateControlInstruction<2, 1> {
+ public:
+ // TODO(danno): make this private when the IfBuilder properly constructs
+ // control flow instructions.
+ explicit HCompareHoleAndBranch(HValue* object) {
+ SetFlag(kFlexibleRepresentation);
+ SetFlag(kAllowUndefinedAsNaN);
+ SetOperandAt(0, object);
+ }
+
+ DECLARE_INSTRUCTION_FACTORY_P1(HCompareHoleAndBranch, HValue*);
+
+ HValue* object() { return OperandAt(0); }
+
+ virtual void InferRepresentation(HInferRepresentationPhase* h_infer);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return representation();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareHoleAndBranch)
+};
+
+
+class HCompareObjectEqAndBranch: public HTemplateControlInstruction<2, 2> {
+ public:
+ // TODO(danno): make this private when the IfBuilder properly constructs
+ // control flow instructions.
+ HCompareObjectEqAndBranch(HValue* left,
+ HValue* right) {
+ SetOperandAt(0, left);
+ SetOperandAt(1, right);
+ }
+
+ DECLARE_INSTRUCTION_FACTORY_P2(HCompareObjectEqAndBranch, HValue*, HValue*);
+
+ HValue* left() { return OperandAt(0); }
+ HValue* right() { return OperandAt(1); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual Representation observed_input_representation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareObjectEqAndBranch)
+};
+
+
+class HIsObjectAndBranch: public HUnaryControlInstruction {
+ public:
+ explicit HIsObjectAndBranch(HValue* value)
+ : HUnaryControlInstruction(value, NULL, NULL) { }
+
+ 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:
+ explicit HIsSmiAndBranch(HValue* value)
+ : HUnaryControlInstruction(value, NULL, NULL) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+};
+
+
+class HIsUndetectableAndBranch: public HUnaryControlInstruction {
+ public:
+ explicit HIsUndetectableAndBranch(HValue* value)
+ : HUnaryControlInstruction(value, NULL, NULL) { }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch)
+};
+
+
+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) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch)
+};
+
+
+class HHasInstanceTypeAndBranch: public HUnaryControlInstruction {
+ public:
+ HHasInstanceTypeAndBranch(HValue* value, InstanceType type)
+ : HUnaryControlInstruction(value, NULL, NULL), from_(type), to_(type) { }
+ HHasInstanceTypeAndBranch(HValue* value, InstanceType from, InstanceType to)
+ : HUnaryControlInstruction(value, NULL, NULL), from_(from), to_(to) {
+ ASSERT(to == LAST_TYPE); // Others not implemented yet in backend.
+ }
+
+ InstanceType from() { return from_; }
+ InstanceType to() { return to_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(HasInstanceTypeAndBranch)
+
+ private:
+ InstanceType from_;
+ InstanceType to_; // Inclusive range, not all combinations work.
+};
+
+
+class HHasCachedArrayIndexAndBranch: public HUnaryControlInstruction {
+ public:
+ explicit HHasCachedArrayIndexAndBranch(HValue* value)
+ : HUnaryControlInstruction(value, NULL, NULL) { }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch)
+};
+
+
+class HGetCachedArrayIndex: public HUnaryOperation {
+ public:
+ explicit HGetCachedArrayIndex(HValue* value) : HUnaryOperation(value) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HClassOfTestAndBranch: public HUnaryControlInstruction {
+ public:
+ HClassOfTestAndBranch(HValue* value, Handle<String> class_name)
+ : HUnaryControlInstruction(value, NULL, NULL),
+ class_name_(class_name) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<String> class_name() const { return class_name_; }
+
+ private:
+ Handle<String> class_name_;
+};
+
+
+class HTypeofIsAndBranch: public HUnaryControlInstruction {
+ public:
+ HTypeofIsAndBranch(HValue* value, Handle<String> type_literal)
+ : HUnaryControlInstruction(value, NULL, NULL),
+ type_literal_(type_literal) { }
+
+ Handle<String> type_literal() { return type_literal_; }
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ private:
+ Handle<String> type_literal_;
+};
+
+
+class HInstanceOf: public HBinaryOperation {
+ public:
+ HInstanceOf(HValue* context, HValue* left, HValue* right)
+ : HBinaryOperation(context, left, right, HType::Boolean()) {
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOf)
+};
+
+
+class HInstanceOfKnownGlobal: public HTemplateInstruction<2> {
+ public:
+ HInstanceOfKnownGlobal(HValue* context,
+ HValue* left,
+ Handle<JSFunction> right)
+ : HTemplateInstruction<2>(HType::Boolean()), function_(right) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, left);
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* left() { return OperandAt(1); }
+ Handle<JSFunction> function() { return function_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal)
+
+ private:
+ Handle<JSFunction> function_;
+};
+
+
+// TODO(mstarzinger): This instruction should be modeled as a load of the map
+// field followed by a load of the instance size field once HLoadNamedField is
+// flexible enough to accommodate byte-field loads.
+class HInstanceSize: public HTemplateInstruction<1> {
+ public:
+ explicit HInstanceSize(HValue* object) {
+ SetOperandAt(0, object);
+ set_representation(Representation::Integer32());
+ }
+
+ HValue* object() { return OperandAt(0); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceSize)
+};
+
+
+class HPower: public HTemplateInstruction<2> {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right);
+
+ HValue* left() { return OperandAt(0); }
+ HValue* right() const { return OperandAt(1); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return index == 0
+ ? Representation::Double()
+ : Representation::None();
+ }
+ virtual Representation observed_input_representation(int index) {
+ return RequiredInputRepresentation(index);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Power)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ HPower(HValue* left, HValue* right) {
+ SetOperandAt(0, left);
+ SetOperandAt(1, right);
+ set_representation(Representation::Double());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+
+ virtual bool IsDeletable() const {
+ return !right()->representation().IsTagged();
+ }
+};
+
+
+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)
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HAdd: public HArithmeticBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right);
+
+ // Add is only commutative if two integer values are added and not if two
+ // tagged values are added (because it might be a String concatenation).
+ virtual bool IsCommutative() const {
+ return !representation().IsTagged();
+ }
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual HValue* Canonicalize();
+
+ virtual bool TryDecompose(DecompositionResult* decomposition) {
+ if (left()->IsInteger32Constant()) {
+ decomposition->Apply(right(), left()->GetInteger32Constant());
+ return true;
+ } else if (right()->IsInteger32Constant()) {
+ decomposition->Apply(left(), right()->GetInteger32Constant());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ virtual void RepresentationChanged(Representation to) {
+ if (to.IsTagged()) ClearFlag(kAllowUndefinedAsNaN);
+ HArithmeticBinaryOperation::RepresentationChanged(to);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Add)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ virtual Range* InferRange(Zone* zone);
+
+ private:
+ HAdd(HValue* context, HValue* left, HValue* right)
+ : HArithmeticBinaryOperation(context, left, right) {
+ SetFlag(kCanOverflow);
+ }
+};
+
+
+class HSub: public HArithmeticBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right);
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual HValue* Canonicalize();
+
+ virtual bool TryDecompose(DecompositionResult* decomposition) {
+ if (right()->IsInteger32Constant()) {
+ decomposition->Apply(left(), -right()->GetInteger32Constant());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Sub)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ virtual Range* InferRange(Zone* zone);
+
+ private:
+ HSub(HValue* context, HValue* left, HValue* right)
+ : HArithmeticBinaryOperation(context, left, right) {
+ SetFlag(kCanOverflow);
+ }
+};
+
+
+class HMul: public HArithmeticBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right);
+
+ static HInstruction* NewImul(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right) {
+ HMul* mul = new(zone) HMul(context, left, right);
+ // TODO(mstarzinger): Prevent bailout on minus zero for imul.
+ mul->AssumeRepresentation(Representation::Integer32());
+ mul->ClearFlag(HValue::kCanOverflow);
+ return mul;
+ }
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual HValue* Canonicalize();
+
+ // Only commutative if it is certain that not two objects are multiplicated.
+ virtual bool IsCommutative() const {
+ return !representation().IsTagged();
+ }
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ if (new_rep.IsSmi()) new_rep = Representation::Integer32();
+ HArithmeticBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Mul)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ virtual Range* InferRange(Zone* zone);
+
+ private:
+ HMul(HValue* context, HValue* left, HValue* right)
+ : HArithmeticBinaryOperation(context, left, right) {
+ SetFlag(kCanOverflow);
+ }
+};
+
+
+class HMod: public HArithmeticBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right,
+ Maybe<int> fixed_right_arg);
+
+ Maybe<int> fixed_right_arg() const { return fixed_right_arg_; }
+
+ bool HasPowerOf2Divisor() {
+ if (right()->IsConstant() &&
+ HConstant::cast(right())->HasInteger32Value()) {
+ int32_t value = HConstant::cast(right())->Integer32Value();
+ return value != 0 && (IsPowerOf2(value) || IsPowerOf2(-value));
+ }
+
+ return false;
+ }
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual HValue* Canonicalize();
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ if (new_rep.IsSmi()) new_rep = Representation::Integer32();
+ HArithmeticBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Mod)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ virtual Range* InferRange(Zone* zone);
+
+ private:
+ HMod(HValue* context,
+ HValue* left,
+ HValue* right,
+ Maybe<int> fixed_right_arg)
+ : HArithmeticBinaryOperation(context, left, right),
+ fixed_right_arg_(fixed_right_arg) {
+ SetFlag(kCanBeDivByZero);
+ SetFlag(kCanOverflow);
+ }
+
+ const Maybe<int> fixed_right_arg_;
+};
+
+
+class HDiv: public HArithmeticBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right);
+
+ bool HasPowerOf2Divisor() {
+ if (right()->IsInteger32Constant()) {
+ int32_t value = right()->GetInteger32Constant();
+ return value != 0 && (IsPowerOf2(value) || IsPowerOf2(-value));
+ }
+
+ return false;
+ }
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual HValue* Canonicalize();
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ if (new_rep.IsSmi()) new_rep = Representation::Integer32();
+ HArithmeticBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Div)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ virtual Range* InferRange(Zone* zone);
+
+ private:
+ HDiv(HValue* context, HValue* left, HValue* right)
+ : HArithmeticBinaryOperation(context, left, right) {
+ SetFlag(kCanBeDivByZero);
+ SetFlag(kCanOverflow);
+ }
+};
+
+
+class HMathMinMax: public HArithmeticBinaryOperation {
+ public:
+ enum Operation { kMathMin, kMathMax };
+
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right,
+ Operation op);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return index == 0 ? Representation::Tagged()
+ : representation();
+ }
+
+ virtual Representation observed_input_representation(int index) {
+ return RequiredInputRepresentation(index);
+ }
+
+ virtual void InferRepresentation(HInferRepresentationPhase* h_infer);
+
+ virtual Representation RepresentationFromInputs() {
+ Representation left_rep = left()->representation();
+ Representation right_rep = right()->representation();
+ Representation result = Representation::Smi();
+ result = result.generalize(left_rep);
+ result = result.generalize(right_rep);
+ if (result.IsTagged()) return Representation::Double();
+ return result;
+ }
+
+ virtual bool IsCommutative() const { return true; }
+
+ Operation operation() { return operation_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathMinMax)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ return other->IsMathMinMax() &&
+ HMathMinMax::cast(other)->operation_ == operation_;
+ }
+
+ virtual Range* InferRange(Zone* zone);
+
+ private:
+ HMathMinMax(HValue* context, HValue* left, HValue* right, Operation op)
+ : HArithmeticBinaryOperation(context, left, right),
+ operation_(op) { }
+
+ Operation operation_;
+};
+
+
+class HBitwise: public HBitwiseBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ Token::Value op,
+ HValue* left,
+ HValue* right);
+
+ Token::Value op() const { return op_; }
+
+ virtual bool IsCommutative() const { return true; }
+
+ virtual HValue* Canonicalize();
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(Bitwise)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ return op() == HBitwise::cast(other)->op();
+ }
+
+ virtual Range* InferRange(Zone* zone);
+
+ private:
+ HBitwise(HValue* context,
+ Token::Value op,
+ HValue* left,
+ HValue* right)
+ : HBitwiseBinaryOperation(context, left, right, HType::TaggedNumber()),
+ op_(op) {
+ ASSERT(op == Token::BIT_AND || op == Token::BIT_OR || op == Token::BIT_XOR);
+ // BIT_AND with a smi-range positive value will always unset the
+ // entire sign-extension of the smi-sign.
+ if (op == Token::BIT_AND &&
+ ((left->IsConstant() &&
+ left->representation().IsSmi() &&
+ HConstant::cast(left)->Integer32Value() >= 0) ||
+ (right->IsConstant() &&
+ right->representation().IsSmi() &&
+ HConstant::cast(right)->Integer32Value() >= 0))) {
+ SetFlag(kTruncatingToSmi);
+ // BIT_OR with a smi-range negative value will always set the entire
+ // sign-extension of the smi-sign.
+ } else if (op == Token::BIT_OR &&
+ ((left->IsConstant() &&
+ left->representation().IsSmi() &&
+ HConstant::cast(left)->Integer32Value() < 0) ||
+ (right->IsConstant() &&
+ right->representation().IsSmi() &&
+ HConstant::cast(right)->Integer32Value() < 0))) {
+ SetFlag(kTruncatingToSmi);
+ }
+ }
+
+ Token::Value op_;
+};
+
+
+class HShl: public HBitwiseBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right);
+
+ virtual Range* InferRange(Zone* zone);
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ if (new_rep.IsSmi() &&
+ !(right()->IsInteger32Constant() &&
+ right()->GetInteger32Constant() >= 0)) {
+ new_rep = Representation::Integer32();
+ }
+ HBitwiseBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Shl)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ HShl(HValue* context, HValue* left, HValue* right)
+ : HBitwiseBinaryOperation(context, left, right) { }
+};
+
+
+class HShr: public HBitwiseBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right);
+
+ virtual bool TryDecompose(DecompositionResult* decomposition) {
+ if (right()->IsInteger32Constant()) {
+ if (decomposition->Apply(left(), 0, right()->GetInteger32Constant())) {
+ // This is intended to look for HAdd and HSub, to handle compounds
+ // like ((base + offset) >> scale) with one single decomposition.
+ left()->TryDecompose(decomposition);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual Range* InferRange(Zone* zone);
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ if (new_rep.IsSmi()) new_rep = Representation::Integer32();
+ HBitwiseBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Shr)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ HShr(HValue* context, HValue* left, HValue* right)
+ : HBitwiseBinaryOperation(context, left, right) { }
+};
+
+
+class HSar: public HBitwiseBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right);
+
+ virtual bool TryDecompose(DecompositionResult* decomposition) {
+ if (right()->IsInteger32Constant()) {
+ if (decomposition->Apply(left(), 0, right()->GetInteger32Constant())) {
+ // This is intended to look for HAdd and HSub, to handle compounds
+ // like ((base + offset) >> scale) with one single decomposition.
+ left()->TryDecompose(decomposition);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual Range* InferRange(Zone* zone);
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ if (new_rep.IsSmi()) new_rep = Representation::Integer32();
+ HBitwiseBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Sar)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ HSar(HValue* context, HValue* left, HValue* right)
+ : HBitwiseBinaryOperation(context, left, right) { }
+};
+
+
+class HRor: public HBitwiseBinaryOperation {
+ public:
+ HRor(HValue* context, HValue* left, HValue* right)
+ : HBitwiseBinaryOperation(context, left, right) {
+ ChangeRepresentation(Representation::Integer32());
+ }
+
+ virtual void UpdateRepresentation(Representation new_rep,
+ HInferRepresentationPhase* h_infer,
+ const char* reason) {
+ if (new_rep.IsSmi()) new_rep = Representation::Integer32();
+ HBitwiseBinaryOperation::UpdateRepresentation(new_rep, h_infer, reason);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Ror)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+};
+
+
+class HOsrEntry: public HTemplateInstruction<0> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HOsrEntry, BailoutId);
+
+ BailoutId ast_id() const { return ast_id_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(OsrEntry)
+
+ private:
+ explicit HOsrEntry(BailoutId ast_id) : ast_id_(ast_id) {
+ SetGVNFlag(kChangesOsrEntries);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+
+ BailoutId ast_id_;
+};
+
+
+class HParameter: public HTemplateInstruction<0> {
+ public:
+ enum ParameterKind {
+ STACK_PARAMETER,
+ REGISTER_PARAMETER
+ };
+
+ DECLARE_INSTRUCTION_FACTORY_P1(HParameter, unsigned);
+ DECLARE_INSTRUCTION_FACTORY_P2(HParameter, unsigned, ParameterKind);
+ DECLARE_INSTRUCTION_FACTORY_P3(HParameter, unsigned, ParameterKind,
+ Representation);
+
+ unsigned index() const { return index_; }
+ ParameterKind kind() const { return kind_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Parameter)
+
+ private:
+ explicit HParameter(unsigned index,
+ ParameterKind kind = STACK_PARAMETER)
+ : index_(index),
+ kind_(kind) {
+ set_representation(Representation::Tagged());
+ }
+
+ explicit HParameter(unsigned index,
+ ParameterKind kind,
+ Representation r)
+ : index_(index),
+ kind_(kind) {
+ set_representation(r);
+ }
+
+ unsigned index_;
+ ParameterKind kind_;
+};
+
+
+class HCallStub: public HUnaryCall {
+ public:
+ HCallStub(HValue* context, CodeStub::Major major_key, int argument_count)
+ : HUnaryCall(context, argument_count),
+ major_key_(major_key),
+ transcendental_type_(TranscendentalCache::kNumberOfCaches) {
+ }
+
+ CodeStub::Major major_key() { return major_key_; }
+
+ HValue* context() { return value(); }
+
+ void set_transcendental_type(TranscendentalCache::Type transcendental_type) {
+ transcendental_type_ = transcendental_type;
+ }
+ TranscendentalCache::Type transcendental_type() {
+ return transcendental_type_;
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallStub)
+
+ private:
+ CodeStub::Major major_key_;
+ TranscendentalCache::Type transcendental_type_;
+};
+
+
+class HUnknownOSRValue: public HTemplateInstruction<0> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P0(HUnknownOSRValue)
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ void set_incoming_value(HPhi* value) {
+ incoming_value_ = value;
+ }
+
+ HPhi* incoming_value() {
+ return incoming_value_;
+ }
+
+ virtual Representation KnownOptimalRepresentation() {
+ if (incoming_value_ == NULL) return Representation::None();
+ return incoming_value_->KnownOptimalRepresentation();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue)
+
+ private:
+ HUnknownOSRValue()
+ : incoming_value_(NULL) {
+ set_representation(Representation::Tagged());
+ }
+
+ HPhi* incoming_value_;
+};
+
+
+class HLoadGlobalCell: public HTemplateInstruction<0> {
+ public:
+ HLoadGlobalCell(Handle<Cell> cell, PropertyDetails details)
+ : cell_(cell), details_(details), unique_id_() {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kDependsOnGlobalVars);
+ }
+
+ Handle<Cell> cell() const { return cell_; }
+ bool RequiresHoleCheck() const;
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual intptr_t Hashcode() {
+ return unique_id_.Hashcode();
+ }
+
+ virtual void FinalizeUniqueValueId() {
+ unique_id_ = UniqueValueId(cell_);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ HLoadGlobalCell* b = HLoadGlobalCell::cast(other);
+ return unique_id_ == b->unique_id_;
+ }
+
+ private:
+ virtual bool IsDeletable() const { return !RequiresHoleCheck(); }
+
+ Handle<Cell> cell_;
+ PropertyDetails details_;
+ UniqueValueId unique_id_;
+};
+
+
+class HLoadGlobalGeneric: public HTemplateInstruction<2> {
+ public:
+ HLoadGlobalGeneric(HValue* context,
+ HValue* global_object,
+ Handle<Object> name,
+ bool for_typeof)
+ : name_(name),
+ for_typeof_(for_typeof) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, global_object);
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* global_object() { return OperandAt(1); }
+ Handle<Object> name() const { return name_; }
+ bool for_typeof() const { return for_typeof_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric)
+
+ private:
+ Handle<Object> name_;
+ bool for_typeof_;
+};
+
+
+class HAllocate: public HTemplateInstruction<2> {
+ public:
+ static HAllocate* New(Zone* zone,
+ HValue* context,
+ HValue* size,
+ HType type,
+ PretenureFlag pretenure_flag,
+ InstanceType instance_type) {
+ return new(zone) HAllocate(context, size, type, pretenure_flag,
+ instance_type);
+ }
+
+ // Maximum instance size for which allocations will be inlined.
+ static const int kMaxInlineSize = 64 * kPointerSize;
+
+ HValue* context() { return OperandAt(0); }
+ HValue* size() { return OperandAt(1); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ if (index == 0) {
+ return Representation::Tagged();
+ } else {
+ return Representation::Integer32();
+ }
+ }
+
+ virtual Handle<Map> GetMonomorphicJSObjectMap() {
+ return known_initial_map_;
+ }
+
+ void set_known_initial_map(Handle<Map> known_initial_map) {
+ known_initial_map_ = known_initial_map;
+ }
+
+ bool IsNewSpaceAllocation() const {
+ return (flags_ & ALLOCATE_IN_NEW_SPACE) != 0;
+ }
+
+ bool IsOldDataSpaceAllocation() const {
+ return (flags_ & ALLOCATE_IN_OLD_DATA_SPACE) != 0;
+ }
+
+ bool IsOldPointerSpaceAllocation() const {
+ return (flags_ & ALLOCATE_IN_OLD_POINTER_SPACE) != 0;
+ }
+
+ bool MustAllocateDoubleAligned() const {
+ return (flags_ & ALLOCATE_DOUBLE_ALIGNED) != 0;
+ }
+
+ bool MustPrefillWithFiller() const {
+ return (flags_ & PREFILL_WITH_FILLER) != 0;
+ }
+
+ void MakePrefillWithFiller() {
+ flags_ = static_cast<HAllocate::Flags>(flags_ | PREFILL_WITH_FILLER);
+ }
+
+ void MakeDoubleAligned() {
+ flags_ = static_cast<HAllocate::Flags>(flags_ | ALLOCATE_DOUBLE_ALIGNED);
+ }
+
+ void UpdateSize(HValue* size) {
+ SetOperandAt(1, size);
+ }
+
+ virtual void HandleSideEffectDominator(GVNFlag side_effect,
+ HValue* dominator);
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(Allocate)
+
+ private:
+ enum Flags {
+ ALLOCATE_IN_NEW_SPACE = 1 << 0,
+ ALLOCATE_IN_OLD_DATA_SPACE = 1 << 1,
+ ALLOCATE_IN_OLD_POINTER_SPACE = 1 << 2,
+ ALLOCATE_DOUBLE_ALIGNED = 1 << 3,
+ PREFILL_WITH_FILLER = 1 << 4
+ };
+
+ HAllocate(HValue* context,
+ HValue* size,
+ HType type,
+ PretenureFlag pretenure_flag,
+ InstanceType instance_type)
+ : HTemplateInstruction<2>(type) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, size);
+ set_representation(Representation::Tagged());
+ SetFlag(kTrackSideEffectDominators);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ SetGVNFlag(kDependsOnNewSpacePromotion);
+ flags_ = pretenure_flag == TENURED
+ ? (Heap::TargetSpaceId(instance_type) == OLD_POINTER_SPACE
+ ? ALLOCATE_IN_OLD_POINTER_SPACE : ALLOCATE_IN_OLD_DATA_SPACE)
+ : ALLOCATE_IN_NEW_SPACE;
+ if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) {
+ flags_ = static_cast<HAllocate::Flags>(flags_ |
+ ALLOCATE_DOUBLE_ALIGNED);
+ }
+ }
+
+ Flags flags_;
+ Handle<Map> known_initial_map_;
+};
+
+
+class HInnerAllocatedObject: public HTemplateInstruction<1> {
+ public:
+ static HInnerAllocatedObject* New(Zone* zone,
+ HValue* context,
+ HValue* value,
+ int offset,
+ HType type = HType::Tagged()) {
+ return new(zone) HInnerAllocatedObject(value, offset, type);
+ }
+
+ HValue* base_object() { return OperandAt(0); }
+ int offset() { return offset_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(InnerAllocatedObject)
+
+ private:
+ HInnerAllocatedObject(HValue* value, int offset, HType type = HType::Tagged())
+ : HTemplateInstruction<1>(type), offset_(offset) {
+ ASSERT(value->IsAllocate());
+ SetOperandAt(0, value);
+ set_type(type);
+ set_representation(Representation::Tagged());
+ }
+
+ int offset_;
+};
+
+
+inline bool StoringValueNeedsWriteBarrier(HValue* value) {
+ return !value->type().IsBoolean()
+ && !value->type().IsSmi()
+ && !(value->IsConstant() && HConstant::cast(value)->ImmortalImmovable());
+}
+
+
+inline bool ReceiverObjectNeedsWriteBarrier(HValue* object,
+ HValue* new_space_dominator) {
+ if (object->IsInnerAllocatedObject()) {
+ return ReceiverObjectNeedsWriteBarrier(
+ HInnerAllocatedObject::cast(object)->base_object(),
+ new_space_dominator);
+ }
+ if (object->IsConstant() && HConstant::cast(object)->IsCell()) {
+ return false;
+ }
+ if (object->IsConstant() &&
+ HConstant::cast(object)->HasExternalReferenceValue()) {
+ // Stores to external references require no write barriers
+ return false;
+ }
+ if (object != new_space_dominator) return true;
+ if (object->IsAllocate()) {
+ return !HAllocate::cast(object)->IsNewSpaceAllocation();
+ }
+ return true;
+}
+
+
+class HStoreGlobalCell: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P3(HStoreGlobalCell, HValue*,
+ Handle<PropertyCell>, PropertyDetails);
+
+ Handle<PropertyCell> cell() const { return cell_; }
+ bool RequiresHoleCheck() {
+ return !details_.IsDontDelete() || details_.IsReadOnly();
+ }
+ bool NeedsWriteBarrier() {
+ return StoringValueNeedsWriteBarrier(value());
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell)
+
+ private:
+ HStoreGlobalCell(HValue* value,
+ Handle<PropertyCell> cell,
+ PropertyDetails details)
+ : HUnaryOperation(value),
+ cell_(cell),
+ details_(details) {
+ SetGVNFlag(kChangesGlobalVars);
+ }
+
+ Handle<PropertyCell> cell_;
+ PropertyDetails details_;
+};
+
+
+class HStoreGlobalGeneric: public HTemplateInstruction<3> {
+ public:
+ inline static HStoreGlobalGeneric* New(Zone* zone,
+ HValue* context,
+ HValue* global_object,
+ Handle<Object> name,
+ HValue* value,
+ StrictModeFlag strict_mode_flag) {
+ return new(zone) HStoreGlobalGeneric(context, global_object,
+ name, value, strict_mode_flag);
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* global_object() { return OperandAt(1); }
+ Handle<Object> name() const { return name_; }
+ HValue* value() { return OperandAt(2); }
+ StrictModeFlag strict_mode_flag() { return strict_mode_flag_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric)
+
+ private:
+ HStoreGlobalGeneric(HValue* context,
+ HValue* global_object,
+ Handle<Object> name,
+ HValue* value,
+ StrictModeFlag strict_mode_flag)
+ : name_(name),
+ strict_mode_flag_(strict_mode_flag) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, global_object);
+ SetOperandAt(2, value);
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ Handle<Object> name_;
+ StrictModeFlag strict_mode_flag_;
+};
+
+
+class HLoadContextSlot: public HUnaryOperation {
+ public:
+ 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);
+ SetGVNFlag(kDependsOnContextSlots);
+ }
+
+ int slot_index() const { return slot_index_; }
+ Mode mode() const { return mode_; }
+
+ bool DeoptimizesOnHole() {
+ return mode_ == kCheckDeoptimize;
+ }
+
+ bool RequiresHoleCheck() const {
+ return mode_ != kNoCheck;
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ HLoadContextSlot* b = HLoadContextSlot::cast(other);
+ return (slot_index() == b->slot_index());
+ }
+
+ private:
+ virtual bool IsDeletable() const { return !RequiresHoleCheck(); }
+
+ int slot_index_;
+ Mode mode_;
+};
+
+
+class HStoreContextSlot: public HTemplateInstruction<2> {
+ public:
+ 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
+ };
+
+ DECLARE_INSTRUCTION_FACTORY_P4(HStoreContextSlot, HValue*, int,
+ Mode, HValue*);
+
+ 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());
+ }
+
+ bool DeoptimizesOnHole() {
+ return mode_ == kCheckDeoptimize;
+ }
+
+ bool RequiresHoleCheck() {
+ return mode_ != kNoCheck;
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot)
+
+ private:
+ HStoreContextSlot(HValue* context, int slot_index, Mode mode, HValue* value)
+ : slot_index_(slot_index), mode_(mode) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, value);
+ SetGVNFlag(kChangesContextSlots);
+ }
+
+ int slot_index_;
+ Mode mode_;
+};
+
+
+// Represents an access to a portion of an object, such as the map pointer,
+// array elements pointer, etc, but not accesses to array elements themselves.
+class HObjectAccess {
+ public:
+ inline bool IsInobject() const {
+ return portion() != kBackingStore && portion() != kExternalMemory;
+ }
+
+ inline bool IsExternalMemory() const {
+ return portion() == kExternalMemory;
+ }
+
+ inline bool IsStringLength() const {
+ return portion() == kStringLengths;
+ }
+
+ inline int offset() const {
+ return OffsetField::decode(value_);
+ }
+
+ inline Representation representation() const {
+ return Representation::FromKind(RepresentationField::decode(value_));
+ }
+
+ inline Handle<String> name() const {
+ return name_;
+ }
+
+ inline HObjectAccess WithRepresentation(Representation representation) {
+ return HObjectAccess(portion(), offset(), representation, name());
+ }
+
+ static HObjectAccess ForHeapNumberValue() {
+ return HObjectAccess(
+ kDouble, HeapNumber::kValueOffset, Representation::Double());
+ }
+
+ static HObjectAccess ForElementsPointer() {
+ return HObjectAccess(kElementsPointer, JSObject::kElementsOffset);
+ }
+
+ static HObjectAccess ForArrayLength(ElementsKind elements_kind) {
+ return HObjectAccess(
+ kArrayLengths,
+ JSArray::kLengthOffset,
+ IsFastElementsKind(elements_kind) &&
+ FLAG_track_fields
+ ? Representation::Smi() : Representation::Tagged());
+ }
+
+ static HObjectAccess ForAllocationSiteTransitionInfo() {
+ return HObjectAccess(kInobject, AllocationSite::kTransitionInfoOffset);
+ }
+
+ static HObjectAccess ForAllocationSiteWeakNext() {
+ return HObjectAccess(kInobject, AllocationSite::kWeakNextOffset);
+ }
+
+ static HObjectAccess ForAllocationSiteList() {
+ return HObjectAccess(kExternalMemory, 0, Representation::Tagged());
+ }
+
+ static HObjectAccess ForFixedArrayLength() {
+ return HObjectAccess(
+ kArrayLengths,
+ FixedArray::kLengthOffset,
+ FLAG_track_fields ? Representation::Smi() : Representation::Tagged());
+ }
+
+ static HObjectAccess ForStringLength() {
+ STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
+ return HObjectAccess(
+ kStringLengths,
+ String::kLengthOffset,
+ FLAG_track_fields ? Representation::Smi() : Representation::Tagged());
+ }
+
+ static HObjectAccess ForPropertiesPointer() {
+ return HObjectAccess(kInobject, JSObject::kPropertiesOffset);
+ }
+
+ static HObjectAccess ForPrototypeOrInitialMap() {
+ return HObjectAccess(kInobject, JSFunction::kPrototypeOrInitialMapOffset);
+ }
+
+ static HObjectAccess ForMap() {
+ return HObjectAccess(kMaps, JSObject::kMapOffset);
+ }
+
+ static HObjectAccess ForPropertyCellValue() {
+ return HObjectAccess(kInobject, PropertyCell::kValueOffset);
+ }
+
+ static HObjectAccess ForCellValue() {
+ return HObjectAccess(kInobject, Cell::kValueOffset);
+ }
+
+ static HObjectAccess ForAllocationMementoSite() {
+ return HObjectAccess(kInobject, AllocationMemento::kAllocationSiteOffset);
+ }
+
+ static HObjectAccess ForCounter() {
+ return HObjectAccess(kExternalMemory, 0, Representation::Integer32());
+ }
+
+ // Create an access to an offset in a fixed array header.
+ static HObjectAccess ForFixedArrayHeader(int offset);
+
+ // Create an access to an in-object property in a JSObject.
+ static HObjectAccess ForJSObjectOffset(int offset,
+ Representation representation = Representation::Tagged());
+
+ // Create an access to an in-object property in a JSArray.
+ static HObjectAccess ForJSArrayOffset(int offset);
+
+ // Create an access to the backing store of an object.
+ static HObjectAccess ForBackingStoreOffset(int offset,
+ Representation representation = Representation::Tagged());
+
+ // Create an access to a resolved field (in-object or backing store).
+ static HObjectAccess ForField(Handle<Map> map,
+ LookupResult *lookup, Handle<String> name = Handle<String>::null());
+
+ // Create an access for the payload of a Cell or JSGlobalPropertyCell.
+ static HObjectAccess ForCellPayload(Isolate* isolate);
+
+ void PrintTo(StringStream* stream);
+
+ inline bool Equals(HObjectAccess that) const {
+ return value_ == that.value_; // portion and offset must match
+ }
+
+ protected:
+ void SetGVNFlags(HValue *instr, bool is_store);
+
+ private:
+ // internal use only; different parts of an object or array
+ enum Portion {
+ kMaps, // map of an object
+ kArrayLengths, // the length of an array
+ kStringLengths, // the length of a string
+ kElementsPointer, // elements pointer
+ kBackingStore, // some field in the backing store
+ kDouble, // some double field
+ kInobject, // some other in-object field
+ kExternalMemory // some field in external memory
+ };
+
+ HObjectAccess(Portion portion, int offset,
+ Representation representation = Representation::Tagged(),
+ Handle<String> name = Handle<String>::null())
+ : value_(PortionField::encode(portion) |
+ RepresentationField::encode(representation.kind()) |
+ OffsetField::encode(offset)),
+ name_(name) {
+ // assert that the fields decode correctly
+ ASSERT(this->offset() == offset);
+ ASSERT(this->portion() == portion);
+ ASSERT(RepresentationField::decode(value_) == representation.kind());
+ }
+
+ class PortionField : public BitField<Portion, 0, 3> {};
+ class RepresentationField : public BitField<Representation::Kind, 3, 3> {};
+ class OffsetField : public BitField<int, 6, 26> {};
+
+ uint32_t value_; // encodes portion, representation, and offset
+ Handle<String> name_;
+
+ friend class HLoadNamedField;
+ friend class HStoreNamedField;
+
+ inline Portion portion() const {
+ return PortionField::decode(value_);
+ }
+};
+
+
+class HLoadNamedField: public HTemplateInstruction<2> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P2(HLoadNamedField, HValue*, HObjectAccess);
+ DECLARE_INSTRUCTION_FACTORY_P3(HLoadNamedField, HValue*, HObjectAccess,
+ HValue*);
+
+ HValue* object() { return OperandAt(0); }
+ HValue* typecheck() {
+ ASSERT(HasTypeCheck());
+ return OperandAt(1);
+ }
+
+ bool HasTypeCheck() const { return OperandAt(0) != OperandAt(1); }
+ void ClearTypeCheck() { SetOperandAt(1, object()); }
+ HObjectAccess access() const { return access_; }
+ Representation field_representation() const {
+ return access_.representation();
+ }
+
+ virtual bool HasEscapingOperandAt(int index) { return false; }
+ virtual Representation RequiredInputRepresentation(int index) {
+ if (index == 0 && access().IsExternalMemory()) {
+ // object must be external in case of external memory access
+ return Representation::External();
+ }
+ return Representation::Tagged();
+ }
+ virtual Range* InferRange(Zone* zone);
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ HLoadNamedField* b = HLoadNamedField::cast(other);
+ return access_.Equals(b->access_);
+ }
+
+ private:
+ HLoadNamedField(HValue* object,
+ HObjectAccess access,
+ HValue* typecheck = NULL)
+ : access_(access) {
+ ASSERT(object != NULL);
+ SetOperandAt(0, object);
+ SetOperandAt(1, typecheck != NULL ? typecheck : object);
+
+ Representation representation = access.representation();
+ if (representation.IsSmi()) {
+ set_type(HType::Smi());
+ set_representation(representation);
+ } else if (representation.IsDouble() ||
+ representation.IsExternal() ||
+ representation.IsInteger32()) {
+ set_representation(representation);
+ } else if (FLAG_track_heap_object_fields &&
+ representation.IsHeapObject()) {
+ set_type(HType::NonPrimitive());
+ set_representation(Representation::Tagged());
+ } else {
+ set_representation(Representation::Tagged());
+ }
+ access.SetGVNFlags(this, false);
+ }
+
+ virtual bool IsDeletable() const { return true; }
+
+ HObjectAccess access_;
+};
+
+
+class HLoadNamedGeneric: public HTemplateInstruction<2> {
+ public:
+ HLoadNamedGeneric(HValue* context, HValue* object, Handle<Object> name)
+ : name_(name) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, object);
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* object() { return OperandAt(1); }
+ Handle<Object> name() const { return name_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric)
+
+ private:
+ Handle<Object> name_;
+};
+
+
+class HLoadFunctionPrototype: public HUnaryOperation {
+ public:
+ explicit HLoadFunctionPrototype(HValue* function)
+ : HUnaryOperation(function) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kDependsOnCalls);
+ }
+
+ HValue* function() { return OperandAt(0); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+};
+
+class ArrayInstructionInterface {
+ public:
+ virtual HValue* GetKey() = 0;
+ virtual void SetKey(HValue* key) = 0;
+ virtual void SetIndexOffset(uint32_t index_offset) = 0;
+ virtual bool IsDehoisted() = 0;
+ virtual void SetDehoisted(bool is_dehoisted) = 0;
+ virtual ~ArrayInstructionInterface() { };
+
+ static Representation KeyedAccessIndexRequirement(Representation r) {
+ return r.IsInteger32() || kSmiValueSize != 31
+ ? Representation::Integer32() : Representation::Smi();
+ }
+};
+
+
+enum LoadKeyedHoleMode {
+ NEVER_RETURN_HOLE,
+ ALLOW_RETURN_HOLE
+};
+
+
+class HLoadKeyed
+ : public HTemplateInstruction<3>, public ArrayInstructionInterface {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P4(HLoadKeyed, HValue*, HValue*, HValue*,
+ ElementsKind);
+ DECLARE_INSTRUCTION_FACTORY_P5(HLoadKeyed, HValue*, HValue*, HValue*,
+ ElementsKind, LoadKeyedHoleMode);
+
+ bool is_external() const {
+ return IsExternalArrayElementsKind(elements_kind());
+ }
+ HValue* elements() { return OperandAt(0); }
+ HValue* key() { return OperandAt(1); }
+ HValue* dependency() {
+ ASSERT(HasDependency());
+ return OperandAt(2);
+ }
+ bool HasDependency() const { return OperandAt(0) != OperandAt(2); }
+ uint32_t index_offset() { return IndexOffsetField::decode(bit_field_); }
+ void SetIndexOffset(uint32_t index_offset) {
+ bit_field_ = IndexOffsetField::update(bit_field_, index_offset);
+ }
+ HValue* GetKey() { return key(); }
+ void SetKey(HValue* key) { SetOperandAt(1, key); }
+ bool IsDehoisted() { return IsDehoistedField::decode(bit_field_); }
+ void SetDehoisted(bool is_dehoisted) {
+ bit_field_ = IsDehoistedField::update(bit_field_, is_dehoisted);
+ }
+ ElementsKind elements_kind() const {
+ return ElementsKindField::decode(bit_field_);
+ }
+ LoadKeyedHoleMode hole_mode() const {
+ return HoleModeField::decode(bit_field_);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ // kind_fast: tagged[int32] (none)
+ // kind_double: tagged[int32] (none)
+ // kind_external: external[int32] (none)
+ if (index == 0) {
+ return is_external() ? Representation::External()
+ : Representation::Tagged();
+ }
+ if (index == 1) {
+ return ArrayInstructionInterface::KeyedAccessIndexRequirement(
+ OperandAt(1)->representation());
+ }
+ return Representation::None();
+ }
+
+ virtual Representation observed_input_representation(int index) {
+ return RequiredInputRepresentation(index);
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ bool UsesMustHandleHole() const;
+ bool AllUsesCanTreatHoleAsNaN() const;
+ bool RequiresHoleCheck() const;
+
+ virtual Range* InferRange(Zone* zone);
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyed)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ if (!other->IsLoadKeyed()) return false;
+ HLoadKeyed* other_load = HLoadKeyed::cast(other);
+
+ if (IsDehoisted() && index_offset() != other_load->index_offset())
+ return false;
+ return elements_kind() == other_load->elements_kind();
+ }
+
+ private:
+ HLoadKeyed(HValue* obj,
+ HValue* key,
+ HValue* dependency,
+ ElementsKind elements_kind,
+ LoadKeyedHoleMode mode = NEVER_RETURN_HOLE)
+ : bit_field_(0) {
+ bit_field_ = ElementsKindField::encode(elements_kind) |
+ HoleModeField::encode(mode);
+
+ SetOperandAt(0, obj);
+ SetOperandAt(1, key);
+ SetOperandAt(2, dependency != NULL ? dependency : obj);
+
+ if (!is_external()) {
+ // I can detect the case between storing double (holey and fast) and
+ // smi/object by looking at elements_kind_.
+ ASSERT(IsFastSmiOrObjectElementsKind(elements_kind) ||
+ IsFastDoubleElementsKind(elements_kind));
+
+ if (IsFastSmiOrObjectElementsKind(elements_kind)) {
+ if (IsFastSmiElementsKind(elements_kind) &&
+ (!IsHoleyElementsKind(elements_kind) ||
+ mode == NEVER_RETURN_HOLE)) {
+ set_type(HType::Smi());
+ set_representation(Representation::Smi());
+ } else {
+ set_representation(Representation::Tagged());
+ }
+
+ SetGVNFlag(kDependsOnArrayElements);
+ } else {
+ set_representation(Representation::Double());
+ SetGVNFlag(kDependsOnDoubleArrayElements);
+ }
+ } else {
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS ||
+ elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ set_representation(Representation::Double());
+ } else {
+ set_representation(Representation::Integer32());
+ }
+
+ SetGVNFlag(kDependsOnExternalMemory);
+ // Native code could change the specialized array.
+ SetGVNFlag(kDependsOnCalls);
+ }
+
+ SetFlag(kUseGVN);
+ }
+
+ virtual bool IsDeletable() const {
+ return !RequiresHoleCheck();
+ }
+
+ // Establish some checks around our packed fields
+ enum LoadKeyedBits {
+ kBitsForElementsKind = 5,
+ kBitsForHoleMode = 1,
+ kBitsForIndexOffset = 25,
+ kBitsForIsDehoisted = 1,
+
+ kStartElementsKind = 0,
+ kStartHoleMode = kStartElementsKind + kBitsForElementsKind,
+ kStartIndexOffset = kStartHoleMode + kBitsForHoleMode,
+ kStartIsDehoisted = kStartIndexOffset + kBitsForIndexOffset
+ };
+
+ STATIC_ASSERT((kBitsForElementsKind + kBitsForIndexOffset +
+ kBitsForIsDehoisted) <= sizeof(uint32_t)*8);
+ STATIC_ASSERT(kElementsKindCount <= (1 << kBitsForElementsKind));
+ class ElementsKindField:
+ public BitField<ElementsKind, kStartElementsKind, kBitsForElementsKind>
+ {}; // NOLINT
+ class HoleModeField:
+ public BitField<LoadKeyedHoleMode, kStartHoleMode, kBitsForHoleMode>
+ {}; // NOLINT
+ class IndexOffsetField:
+ public BitField<uint32_t, kStartIndexOffset, kBitsForIndexOffset>
+ {}; // NOLINT
+ class IsDehoistedField:
+ public BitField<bool, kStartIsDehoisted, kBitsForIsDehoisted>
+ {}; // NOLINT
+ uint32_t bit_field_;
+};
+
+
+class HLoadKeyedGeneric: public HTemplateInstruction<3> {
+ public:
+ HLoadKeyedGeneric(HValue* context, HValue* obj, HValue* key) {
+ set_representation(Representation::Tagged());
+ SetOperandAt(0, obj);
+ SetOperandAt(1, key);
+ SetOperandAt(2, context);
+ SetAllSideEffects();
+ }
+
+ HValue* object() { return OperandAt(0); }
+ HValue* key() { return OperandAt(1); }
+ HValue* context() { return OperandAt(2); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ // tagged[tagged]
+ return Representation::Tagged();
+ }
+
+ virtual HValue* Canonicalize();
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric)
+};
+
+
+class HStoreNamedField: public HTemplateInstruction<3> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P3(HStoreNamedField, HValue*,
+ HObjectAccess, HValue*);
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedField)
+
+ virtual bool HasEscapingOperandAt(int index) { return index == 1; }
+ virtual Representation RequiredInputRepresentation(int index) {
+ if (index == 0 && access().IsExternalMemory()) {
+ // object must be external in case of external memory access
+ return Representation::External();
+ } else if (index == 1 &&
+ (field_representation().IsDouble() ||
+ field_representation().IsSmi() ||
+ field_representation().IsInteger32())) {
+ return field_representation();
+ }
+ return Representation::Tagged();
+ }
+ virtual void HandleSideEffectDominator(GVNFlag side_effect,
+ HValue* dominator) {
+ ASSERT(side_effect == kChangesNewSpacePromotion);
+ new_space_dominator_ = dominator;
+ }
+ virtual void PrintDataTo(StringStream* stream);
+
+ void SkipWriteBarrier() { write_barrier_mode_ = SKIP_WRITE_BARRIER; }
+ bool IsSkipWriteBarrier() const {
+ return write_barrier_mode_ == SKIP_WRITE_BARRIER;
+ }
+
+ HValue* object() const { return OperandAt(0); }
+ HValue* value() const { return OperandAt(1); }
+ HValue* transition() const { return OperandAt(2); }
+
+ HObjectAccess access() const { return access_; }
+ HValue* new_space_dominator() const { return new_space_dominator_; }
+ bool has_transition() const { return has_transition_; }
+
+ Handle<Map> transition_map() const {
+ if (has_transition()) {
+ return Handle<Map>::cast(HConstant::cast(transition())->handle());
+ } else {
+ return Handle<Map>();
+ }
+ }
+
+ void SetTransition(HConstant* map_constant, CompilationInfo* info) {
+ ASSERT(!has_transition()); // Only set once.
+ Handle<Map> map = Handle<Map>::cast(map_constant->handle());
+ if (map->CanBeDeprecated()) {
+ map->AddDependentCompilationInfo(DependentCode::kTransitionGroup, info);
+ }
+ SetOperandAt(2, map_constant);
+ has_transition_ = true;
+ }
+
+ bool NeedsWriteBarrier() {
+ ASSERT(!(FLAG_track_double_fields && field_representation().IsDouble()) ||
+ !has_transition());
+ if (IsSkipWriteBarrier()) return false;
+ if (field_representation().IsDouble()) return false;
+ if (field_representation().IsSmi()) return false;
+ if (field_representation().IsInteger32()) return false;
+ if (field_representation().IsExternal()) return false;
+ return StoringValueNeedsWriteBarrier(value()) &&
+ ReceiverObjectNeedsWriteBarrier(object(), new_space_dominator());
+ }
+
+ bool NeedsWriteBarrierForMap() {
+ if (IsSkipWriteBarrier()) return false;
+ return ReceiverObjectNeedsWriteBarrier(object(), new_space_dominator());
+ }
+
+ Representation field_representation() const {
+ return access_.representation();
+ }
+
+ private:
+ HStoreNamedField(HValue* obj,
+ HObjectAccess access,
+ HValue* val)
+ : access_(access),
+ new_space_dominator_(NULL),
+ write_barrier_mode_(UPDATE_WRITE_BARRIER),
+ has_transition_(false) {
+ SetOperandAt(0, obj);
+ SetOperandAt(1, val);
+ SetOperandAt(2, obj);
+ access.SetGVNFlags(this, true);
+ }
+
+ HObjectAccess access_;
+ HValue* new_space_dominator_;
+ WriteBarrierMode write_barrier_mode_ : 1;
+ bool has_transition_ : 1;
+};
+
+
+class HStoreNamedGeneric: public HTemplateInstruction<3> {
+ public:
+ HStoreNamedGeneric(HValue* context,
+ HValue* object,
+ Handle<String> name,
+ HValue* value,
+ StrictModeFlag strict_mode_flag)
+ : name_(name),
+ strict_mode_flag_(strict_mode_flag) {
+ SetOperandAt(0, object);
+ SetOperandAt(1, value);
+ SetOperandAt(2, context);
+ SetAllSideEffects();
+ }
+
+ HValue* object() { return OperandAt(0); }
+ HValue* value() { return OperandAt(1); }
+ HValue* context() { return OperandAt(2); }
+ Handle<String> name() { return name_; }
+ StrictModeFlag strict_mode_flag() { return strict_mode_flag_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric)
+
+ private:
+ Handle<String> name_;
+ StrictModeFlag strict_mode_flag_;
+};
+
+
+class HStoreKeyed
+ : public HTemplateInstruction<3>, public ArrayInstructionInterface {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P4(HStoreKeyed, HValue*, HValue*, HValue*,
+ ElementsKind);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ // kind_fast: tagged[int32] = tagged
+ // kind_double: tagged[int32] = double
+ // kind_smi : tagged[int32] = smi
+ // kind_external: external[int32] = (double | int32)
+ if (index == 0) {
+ return is_external() ? Representation::External()
+ : Representation::Tagged();
+ } else if (index == 1) {
+ return ArrayInstructionInterface::KeyedAccessIndexRequirement(
+ OperandAt(1)->representation());
+ }
+
+ ASSERT_EQ(index, 2);
+ if (IsDoubleOrFloatElementsKind(elements_kind())) {
+ return Representation::Double();
+ }
+
+ if (IsFastSmiElementsKind(elements_kind())) {
+ return Representation::Smi();
+ }
+
+ return is_external() ? Representation::Integer32()
+ : Representation::Tagged();
+ }
+
+ bool is_external() const {
+ return IsExternalArrayElementsKind(elements_kind());
+ }
+
+ virtual Representation observed_input_representation(int index) {
+ if (index < 2) return RequiredInputRepresentation(index);
+ if (IsUninitialized()) {
+ return Representation::None();
+ }
+ if (IsFastSmiElementsKind(elements_kind())) {
+ return Representation::Smi();
+ }
+ if (IsDoubleOrFloatElementsKind(elements_kind())) {
+ return Representation::Double();
+ }
+ if (is_external()) {
+ return Representation::Integer32();
+ }
+ // For fast object elements kinds, don't assume anything.
+ return Representation::None();
+ }
+
+ HValue* elements() { return OperandAt(0); }
+ HValue* key() { return OperandAt(1); }
+ HValue* value() { return OperandAt(2); }
+ bool value_is_smi() const {
+ return IsFastSmiElementsKind(elements_kind_);
+ }
+ ElementsKind elements_kind() const { return elements_kind_; }
+ uint32_t index_offset() { return index_offset_; }
+ void SetIndexOffset(uint32_t index_offset) { index_offset_ = index_offset; }
+ HValue* GetKey() { return key(); }
+ void SetKey(HValue* key) { SetOperandAt(1, key); }
+ bool IsDehoisted() { return is_dehoisted_; }
+ void SetDehoisted(bool is_dehoisted) { is_dehoisted_ = is_dehoisted; }
+ bool IsUninitialized() { return is_uninitialized_; }
+ void SetUninitialized(bool is_uninitialized) {
+ is_uninitialized_ = is_uninitialized;
+ }
+
+ bool IsConstantHoleStore() {
+ return value()->IsConstant() && HConstant::cast(value())->IsTheHole();
+ }
+
+ virtual void HandleSideEffectDominator(GVNFlag side_effect,
+ HValue* dominator) {
+ ASSERT(side_effect == kChangesNewSpacePromotion);
+ new_space_dominator_ = dominator;
+ }
+
+ HValue* new_space_dominator() const { return new_space_dominator_; }
+
+ bool NeedsWriteBarrier() {
+ if (value_is_smi()) {
+ return false;
+ } else {
+ return StoringValueNeedsWriteBarrier(value()) &&
+ ReceiverObjectNeedsWriteBarrier(elements(), new_space_dominator());
+ }
+ }
+
+ bool NeedsCanonicalization();
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyed)
+
+ private:
+ HStoreKeyed(HValue* obj, HValue* key, HValue* val,
+ ElementsKind elements_kind)
+ : elements_kind_(elements_kind),
+ index_offset_(0),
+ is_dehoisted_(false),
+ is_uninitialized_(false),
+ new_space_dominator_(NULL) {
+ SetOperandAt(0, obj);
+ SetOperandAt(1, key);
+ SetOperandAt(2, val);
+
+ if (IsFastObjectElementsKind(elements_kind)) {
+ SetFlag(kTrackSideEffectDominators);
+ SetGVNFlag(kDependsOnNewSpacePromotion);
+ }
+ if (is_external()) {
+ SetGVNFlag(kChangesExternalMemory);
+ SetFlag(kAllowUndefinedAsNaN);
+ } else if (IsFastDoubleElementsKind(elements_kind)) {
+ SetGVNFlag(kChangesDoubleArrayElements);
+ } else if (IsFastSmiElementsKind(elements_kind)) {
+ SetGVNFlag(kChangesArrayElements);
+ } else {
+ SetGVNFlag(kChangesArrayElements);
+ }
+
+ // EXTERNAL_{UNSIGNED_,}{BYTE,SHORT,INT}_ELEMENTS are truncating.
+ if (elements_kind >= EXTERNAL_BYTE_ELEMENTS &&
+ elements_kind <= EXTERNAL_UNSIGNED_INT_ELEMENTS) {
+ SetFlag(kTruncatingToInt32);
+ }
+ }
+
+ ElementsKind elements_kind_;
+ uint32_t index_offset_;
+ bool is_dehoisted_ : 1;
+ bool is_uninitialized_ : 1;
+ HValue* new_space_dominator_;
+};
+
+
+class HStoreKeyedGeneric: public HTemplateInstruction<4> {
+ public:
+ HStoreKeyedGeneric(HValue* context,
+ HValue* object,
+ HValue* key,
+ HValue* value,
+ StrictModeFlag strict_mode_flag)
+ : strict_mode_flag_(strict_mode_flag) {
+ SetOperandAt(0, object);
+ SetOperandAt(1, key);
+ SetOperandAt(2, value);
+ SetOperandAt(3, context);
+ SetAllSideEffects();
+ }
+
+ HValue* object() { return OperandAt(0); }
+ HValue* key() { return OperandAt(1); }
+ HValue* value() { return OperandAt(2); }
+ HValue* context() { return OperandAt(3); }
+ StrictModeFlag strict_mode_flag() { return strict_mode_flag_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ // tagged[tagged] = tagged
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric)
+
+ private:
+ StrictModeFlag strict_mode_flag_;
+};
+
+
+class HTransitionElementsKind: public HTemplateInstruction<2> {
+ public:
+ inline static HTransitionElementsKind* New(Zone* zone,
+ HValue* context,
+ HValue* object,
+ Handle<Map> original_map,
+ Handle<Map> transitioned_map) {
+ return new(zone) HTransitionElementsKind(context, object,
+ original_map, transitioned_map);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* object() { return OperandAt(0); }
+ HValue* context() { return OperandAt(1); }
+ Handle<Map> original_map() { return original_map_; }
+ Handle<Map> transitioned_map() { return transitioned_map_; }
+ ElementsKind from_kind() { return from_kind_; }
+ ElementsKind to_kind() { return to_kind_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual void FinalizeUniqueValueId() {
+ original_map_unique_id_ = UniqueValueId(original_map_);
+ transitioned_map_unique_id_ = UniqueValueId(transitioned_map_);
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ HTransitionElementsKind* instr = HTransitionElementsKind::cast(other);
+ return original_map_unique_id_ == instr->original_map_unique_id_ &&
+ transitioned_map_unique_id_ == instr->transitioned_map_unique_id_;
+ }
+
+ private:
+ HTransitionElementsKind(HValue* context,
+ HValue* object,
+ Handle<Map> original_map,
+ Handle<Map> transitioned_map)
+ : original_map_(original_map),
+ transitioned_map_(transitioned_map),
+ original_map_unique_id_(),
+ transitioned_map_unique_id_(),
+ from_kind_(original_map->elements_kind()),
+ to_kind_(transitioned_map->elements_kind()) {
+ SetOperandAt(0, object);
+ SetOperandAt(1, context);
+ SetFlag(kUseGVN);
+ SetGVNFlag(kChangesElementsKind);
+ if (original_map->has_fast_double_elements()) {
+ SetGVNFlag(kChangesElementsPointer);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+ if (transitioned_map->has_fast_double_elements()) {
+ SetGVNFlag(kChangesElementsPointer);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+ set_representation(Representation::Tagged());
+ }
+
+ Handle<Map> original_map_;
+ Handle<Map> transitioned_map_;
+ UniqueValueId original_map_unique_id_;
+ UniqueValueId transitioned_map_unique_id_;
+ ElementsKind from_kind_;
+ ElementsKind to_kind_;
+};
+
+
+class HStringAdd: public HBinaryOperation {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* left,
+ HValue* right,
+ StringAddFlags flags = STRING_ADD_CHECK_NONE);
+
+ StringAddFlags flags() const { return flags_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ HStringAdd(HValue* context, HValue* left, HValue* right, StringAddFlags flags)
+ : HBinaryOperation(context, left, right, HType::String()), flags_(flags) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kDependsOnMaps);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+
+ // No side-effects except possible allocation.
+ // NOTE: this instruction _does not_ call ToString() on its inputs.
+ virtual bool IsDeletable() const { return true; }
+
+ const StringAddFlags flags_;
+};
+
+
+class HStringCharCodeAt: public HTemplateInstruction<3> {
+ public:
+ static HStringCharCodeAt* New(Zone* zone,
+ HValue* context,
+ HValue* string,
+ HValue* index) {
+ return new(zone) HStringCharCodeAt(context, string, index);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ // The index is supposed to be Integer32.
+ return index == 2
+ ? Representation::Integer32()
+ : Representation::Tagged();
+ }
+
+ HValue* context() const { return OperandAt(0); }
+ HValue* string() const { return OperandAt(1); }
+ HValue* index() const { return OperandAt(2); }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ virtual Range* InferRange(Zone* zone) {
+ return new(zone) Range(0, String::kMaxUtf16CodeUnit);
+ }
+
+ private:
+ HStringCharCodeAt(HValue* context, HValue* string, HValue* index) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, string);
+ SetOperandAt(2, index);
+ set_representation(Representation::Integer32());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kDependsOnMaps);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+
+ // No side effects: runtime function assumes string + number inputs.
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HStringCharFromCode: public HTemplateInstruction<2> {
+ public:
+ static HInstruction* New(Zone* zone,
+ HValue* context,
+ HValue* char_code);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return index == 0
+ ? Representation::Tagged()
+ : Representation::Integer32();
+ }
+
+ HValue* context() const { return OperandAt(0); }
+ HValue* value() const { return OperandAt(1); }
+
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode)
+
+ private:
+ HStringCharFromCode(HValue* context, HValue* char_code)
+ : HTemplateInstruction<2>(HType::String()) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, char_code);
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+
+ virtual bool IsDeletable() const {
+ return !value()->ToNumberCanBeObserved();
+ }
+};
+
+
+template <int V>
+class HMaterializedLiteral: public HTemplateInstruction<V> {
+ public:
+ HMaterializedLiteral<V>(int index, int depth, AllocationSiteMode mode)
+ : literal_index_(index), depth_(depth), allocation_site_mode_(mode) {
+ this->set_representation(Representation::Tagged());
+ }
+
+ HMaterializedLiteral<V>(int index, int depth)
+ : literal_index_(index), depth_(depth),
+ allocation_site_mode_(DONT_TRACK_ALLOCATION_SITE) {
+ this->set_representation(Representation::Tagged());
+ }
+
+ int literal_index() const { return literal_index_; }
+ int depth() const { return depth_; }
+ AllocationSiteMode allocation_site_mode() const {
+ return allocation_site_mode_;
+ }
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+
+ int literal_index_;
+ int depth_;
+ AllocationSiteMode allocation_site_mode_;
+};
+
+
+class HRegExpLiteral: public HMaterializedLiteral<1> {
+ public:
+ HRegExpLiteral(HValue* context,
+ Handle<FixedArray> literals,
+ Handle<String> pattern,
+ Handle<String> flags,
+ int literal_index)
+ : HMaterializedLiteral<1>(literal_index, 0),
+ literals_(literals),
+ pattern_(pattern),
+ flags_(flags) {
+ SetOperandAt(0, context);
+ SetAllSideEffects();
+ set_type(HType::JSObject());
+ }
+
+ HValue* context() { return OperandAt(0); }
+ Handle<FixedArray> literals() { return literals_; }
+ Handle<String> pattern() { return pattern_; }
+ Handle<String> flags() { return flags_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral)
+
+ private:
+ Handle<FixedArray> literals_;
+ Handle<String> pattern_;
+ Handle<String> flags_;
+};
+
+
+class HFunctionLiteral: public HTemplateInstruction<1> {
+ public:
+ HFunctionLiteral(HValue* context,
+ Handle<SharedFunctionInfo> shared,
+ bool pretenure)
+ : HTemplateInstruction<1>(HType::JSObject()),
+ shared_info_(shared),
+ pretenure_(pretenure),
+ has_no_literals_(shared->num_literals() == 0),
+ is_generator_(shared->is_generator()),
+ language_mode_(shared->language_mode()) {
+ SetOperandAt(0, context);
+ set_representation(Representation::Tagged());
+ SetGVNFlag(kChangesNewSpacePromotion);
+ }
+
+ HValue* context() { return OperandAt(0); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral)
+
+ Handle<SharedFunctionInfo> shared_info() const { return shared_info_; }
+ bool pretenure() const { return pretenure_; }
+ bool has_no_literals() const { return has_no_literals_; }
+ bool is_generator() const { return is_generator_; }
+ LanguageMode language_mode() const { return language_mode_; }
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+
+ Handle<SharedFunctionInfo> shared_info_;
+ bool pretenure_ : 1;
+ bool has_no_literals_ : 1;
+ bool is_generator_ : 1;
+ LanguageMode language_mode_;
+};
+
+
+class HTypeof: public HTemplateInstruction<2> {
+ public:
+ explicit HTypeof(HValue* context, HValue* value) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, value);
+ set_representation(Representation::Tagged());
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* value() { return OperandAt(1); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Typeof)
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HTrapAllocationMemento : public HTemplateInstruction<1> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HTrapAllocationMemento, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* object() { return OperandAt(0); }
+
+ DECLARE_CONCRETE_INSTRUCTION(TrapAllocationMemento)
+
+ private:
+ explicit HTrapAllocationMemento(HValue* obj) {
+ SetOperandAt(0, obj);
+ }
+};
+
+
+class HToFastProperties: public HUnaryOperation {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P1(HToFastProperties, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ToFastProperties)
+
+ private:
+ explicit HToFastProperties(HValue* value) : HUnaryOperation(value) {
+ set_representation(Representation::Tagged());
+ SetGVNFlag(kChangesNewSpacePromotion);
+
+ // This instruction is not marked as kChangesMaps, but does
+ // change the map of the input operand. Use it only when creating
+ // object literals via a runtime call.
+ ASSERT(value->IsCallRuntime());
+#ifdef DEBUG
+ const Runtime::Function* function = HCallRuntime::cast(value)->function();
+ ASSERT(function->function_id == Runtime::kCreateObjectLiteral ||
+ function->function_id == Runtime::kCreateObjectLiteralShallow);
+#endif
+ }
+
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HValueOf: public HUnaryOperation {
+ public:
+ explicit HValueOf(HValue* value) : HUnaryOperation(value) {
+ set_representation(Representation::Tagged());
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf)
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+class HDateField: public HUnaryOperation {
+ public:
+ HDateField(HValue* date, Smi* index)
+ : HUnaryOperation(date), index_(index) {
+ set_representation(Representation::Tagged());
+ }
+
+ Smi* index() const { return index_; }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(DateField)
+
+ private:
+ Smi* index_;
+};
+
+
+class HSeqStringSetChar: public HTemplateInstruction<3> {
+ public:
+ HSeqStringSetChar(String::Encoding encoding,
+ HValue* string,
+ HValue* index,
+ HValue* value) : encoding_(encoding) {
+ SetOperandAt(0, string);
+ SetOperandAt(1, index);
+ SetOperandAt(2, value);
+ set_representation(Representation::Tagged());
+ }
+
+ String::Encoding encoding() { return encoding_; }
+ HValue* string() { return OperandAt(0); }
+ HValue* index() { return OperandAt(1); }
+ HValue* value() { return OperandAt(2); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return (index == 0) ? Representation::Tagged()
+ : Representation::Integer32();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar)
+
+ private:
+ String::Encoding encoding_;
+};
+
+
+class HCheckMapValue: public HTemplateInstruction<2> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P2(HCheckMapValue, HValue*, HValue*);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual HType CalculateInferredType() {
+ return HType::Tagged();
+ }
+
+ HValue* value() { return OperandAt(0); }
+ HValue* map() { return OperandAt(1); }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMapValue)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ return true;
+ }
+
+ private:
+ HCheckMapValue(HValue* value,
+ HValue* map) {
+ SetOperandAt(0, value);
+ SetOperandAt(1, map);
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetGVNFlag(kDependsOnMaps);
+ SetGVNFlag(kDependsOnElementsKind);
+ }
+};
+
+
+class HForInPrepareMap : public HTemplateInstruction<2> {
+ public:
+ static HForInPrepareMap* New(Zone* zone,
+ HValue* context,
+ HValue* object) {
+ return new(zone) HForInPrepareMap(context, object);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* context() { return OperandAt(0); }
+ HValue* enumerable() { return OperandAt(1); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual HType CalculateInferredType() {
+ return HType::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap);
+
+ private:
+ HForInPrepareMap(HValue* context,
+ HValue* object) {
+ SetOperandAt(0, context);
+ SetOperandAt(1, object);
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+};
+
+
+class HForInCacheArray : public HTemplateInstruction<2> {
+ public:
+ DECLARE_INSTRUCTION_FACTORY_P3(HForInCacheArray, HValue*, HValue*, int);
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* enumerable() { return OperandAt(0); }
+ HValue* map() { return OperandAt(1); }
+ int idx() { return idx_; }
+
+ HForInCacheArray* index_cache() {
+ return index_cache_;
+ }
+
+ void set_index_cache(HForInCacheArray* index_cache) {
+ index_cache_ = index_cache;
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual HType CalculateInferredType() {
+ return HType::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray);
+
+ private:
+ HForInCacheArray(HValue* enumerable,
+ HValue* keys,
+ int idx) : idx_(idx) {
+ SetOperandAt(0, enumerable);
+ SetOperandAt(1, keys);
+ set_representation(Representation::Tagged());
+ }
+
+ int idx_;
+ HForInCacheArray* index_cache_;
+};
+
+
+class HLoadFieldByIndex : public HTemplateInstruction<2> {
+ public:
+ HLoadFieldByIndex(HValue* object,
+ HValue* index) {
+ SetOperandAt(0, object);
+ SetOperandAt(1, index);
+ set_representation(Representation::Tagged());
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* object() { return OperandAt(0); }
+ HValue* index() { return OperandAt(1); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ virtual HType CalculateInferredType() {
+ return HType::Tagged();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex);
+
+ private:
+ virtual bool IsDeletable() const { return true; }
+};
+
+
+#undef DECLARE_INSTRUCTION
+#undef DECLARE_CONCRETE_INSTRUCTION
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_INSTRUCTIONS_H_
diff --git a/chromium/v8/src/hydrogen-mark-deoptimize.cc b/chromium/v8/src/hydrogen-mark-deoptimize.cc
new file mode 100644
index 00000000000..c0236e91cbb
--- /dev/null
+++ b/chromium/v8/src/hydrogen-mark-deoptimize.cc
@@ -0,0 +1,84 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-mark-deoptimize.h"
+
+namespace v8 {
+namespace internal {
+
+void HMarkDeoptimizeOnUndefinedPhase::Run() {
+ const ZoneList<HPhi*>* phi_list = graph()->phi_list();
+ for (int i = 0; i < phi_list->length(); i++) {
+ HPhi* phi = phi_list->at(i);
+ if (phi->CheckFlag(HValue::kAllowUndefinedAsNaN) &&
+ !phi->CheckUsesForFlag(HValue::kAllowUndefinedAsNaN)) {
+ ProcessPhi(phi);
+ }
+ }
+}
+
+
+void HMarkDeoptimizeOnUndefinedPhase::ProcessPhi(HPhi* phi) {
+ ASSERT(phi->CheckFlag(HValue::kAllowUndefinedAsNaN));
+ ASSERT(worklist_.is_empty());
+
+ // Push the phi onto the worklist
+ phi->ClearFlag(HValue::kAllowUndefinedAsNaN);
+ worklist_.Add(phi, zone());
+
+ // Process all phis that can reach this phi
+ while (!worklist_.is_empty()) {
+ phi = worklist_.RemoveLast();
+ for (int i = phi->OperandCount() - 1; i >= 0; --i) {
+ HValue* input = phi->OperandAt(i);
+ if (input->IsPhi() && input->CheckFlag(HValue::kAllowUndefinedAsNaN)) {
+ input->ClearFlag(HValue::kAllowUndefinedAsNaN);
+ worklist_.Add(HPhi::cast(input), zone());
+ }
+ }
+ }
+}
+
+
+void HComputeChangeUndefinedToNaN::Run() {
+ const ZoneList<HBasicBlock*>* blocks(graph()->blocks());
+ for (int i = 0; i < blocks->length(); ++i) {
+ const HBasicBlock* block(blocks->at(i));
+ for (HInstruction* current = block->first(); current != NULL; ) {
+ HInstruction* next = current->next();
+ if (current->IsChange()) {
+ if (HChange::cast(current)->can_convert_undefined_to_nan()) {
+ current->SetFlag(HValue::kAllowUndefinedAsNaN);
+ }
+ }
+ current = next;
+ }
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-mark-deoptimize.h b/chromium/v8/src/hydrogen-mark-deoptimize.h
new file mode 100644
index 00000000000..30f35b3dec5
--- /dev/null
+++ b/chromium/v8/src/hydrogen-mark-deoptimize.h
@@ -0,0 +1,75 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_MARK_DEOPTIMIZE_H_
+#define V8_HYDROGEN_MARK_DEOPTIMIZE_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Compute DeoptimizeOnUndefined flag for phis. Any phi that can reach a use
+// with DeoptimizeOnUndefined set must have DeoptimizeOnUndefined set.
+// Currently only HCompareNumericAndBranch, with double input representation,
+// has this flag set. The flag is used by HChange tagged->double, which must
+// deoptimize if one of its uses has this flag set.
+class HMarkDeoptimizeOnUndefinedPhase : public HPhase {
+ public:
+ explicit HMarkDeoptimizeOnUndefinedPhase(HGraph* graph)
+ : HPhase("H_Mark deoptimize on undefined", graph),
+ worklist_(16, zone()) {}
+
+ void Run();
+
+ private:
+ void ProcessPhi(HPhi* phi);
+
+ // Preallocated worklist used as an optimization so we don't have
+ // to allocate a new ZoneList for every ProcessPhi() invocation.
+ ZoneList<HPhi*> worklist_;
+
+ DISALLOW_COPY_AND_ASSIGN(HMarkDeoptimizeOnUndefinedPhase);
+};
+
+
+class HComputeChangeUndefinedToNaN : public HPhase {
+ public:
+ explicit HComputeChangeUndefinedToNaN(HGraph* graph)
+ : HPhase("H_Compute change undefined to nan", graph) {}
+
+ void Run();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HComputeChangeUndefinedToNaN);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_MARK_DEOPTIMIZE_H_
diff --git a/chromium/v8/src/hydrogen-minus-zero.cc b/chromium/v8/src/hydrogen-minus-zero.cc
new file mode 100644
index 00000000000..28ae6eba401
--- /dev/null
+++ b/chromium/v8/src/hydrogen-minus-zero.cc
@@ -0,0 +1,83 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-minus-zero.h"
+
+namespace v8 {
+namespace internal {
+
+void HComputeMinusZeroChecksPhase::Run() {
+ const ZoneList<HBasicBlock*>* blocks(graph()->blocks());
+ for (int i = 0; i < blocks->length(); ++i) {
+ for (HInstructionIterator it(blocks->at(i)); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->IsChange()) {
+ HChange* change = HChange::cast(current);
+ // Propagate flags for negative zero checks upwards from conversions
+ // int32-to-tagged and int32-to-double.
+ Representation from = change->value()->representation();
+ ASSERT(from.Equals(change->from()));
+ if (from.IsSmiOrInteger32()) {
+ ASSERT(change->to().IsTagged() ||
+ change->to().IsDouble() ||
+ change->to().IsSmiOrInteger32());
+ ASSERT(visited_.IsEmpty());
+ PropagateMinusZeroChecks(change->value());
+ visited_.Clear();
+ }
+ }
+ }
+ }
+}
+
+
+void HComputeMinusZeroChecksPhase::PropagateMinusZeroChecks(HValue* value) {
+ for (HValue* current = value;
+ current != NULL && !visited_.Contains(current->id());
+ current = current->EnsureAndPropagateNotMinusZero(&visited_)) {
+ // For phis, we must propagate the check to all of its inputs.
+ if (current->IsPhi()) {
+ visited_.Add(current->id());
+ HPhi* phi = HPhi::cast(current);
+ for (int i = 0; i < phi->OperandCount(); ++i) {
+ PropagateMinusZeroChecks(phi->OperandAt(i));
+ }
+ break;
+ }
+
+ // For multiplication, division, and Math.min/max(), we must propagate
+ // to the left and the right side.
+ if (current->IsMul() || current->IsDiv() || current->IsMathMinMax()) {
+ HBinaryOperation* operation = HBinaryOperation::cast(current);
+ operation->EnsureAndPropagateNotMinusZero(&visited_);
+ PropagateMinusZeroChecks(operation->left());
+ PropagateMinusZeroChecks(operation->right());
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-minus-zero.h b/chromium/v8/src/hydrogen-minus-zero.h
new file mode 100644
index 00000000000..d23ec1196b3
--- /dev/null
+++ b/chromium/v8/src/hydrogen-minus-zero.h
@@ -0,0 +1,56 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_MINUS_ZERO_H_
+#define V8_HYDROGEN_MINUS_ZERO_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HComputeMinusZeroChecksPhase : public HPhase {
+ public:
+ explicit HComputeMinusZeroChecksPhase(HGraph* graph)
+ : HPhase("H_Compute minus zero checks", graph),
+ visited_(graph->GetMaximumValueID(), zone()) { }
+
+ void Run();
+
+ private:
+ void PropagateMinusZeroChecks(HValue* value);
+
+ BitVector visited_;
+
+ DISALLOW_COPY_AND_ASSIGN(HComputeMinusZeroChecksPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_MINUS_ZERO_H_
diff --git a/chromium/v8/src/hydrogen-osr.cc b/chromium/v8/src/hydrogen-osr.cc
new file mode 100644
index 00000000000..73fa40a72cf
--- /dev/null
+++ b/chromium/v8/src/hydrogen-osr.cc
@@ -0,0 +1,125 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen.h"
+#include "hydrogen-osr.h"
+
+namespace v8 {
+namespace internal {
+
+// True iff. we are compiling for OSR and the statement is the entry.
+bool HOsrBuilder::HasOsrEntryAt(IterationStatement* statement) {
+ return statement->OsrEntryId() == builder_->current_info()->osr_ast_id();
+}
+
+
+// Build a new loop header block and set it as the current block.
+HBasicBlock *HOsrBuilder::BuildLoopEntry() {
+ HBasicBlock* loop_entry = builder_->CreateLoopHeaderBlock();
+ builder_->current_block()->Goto(loop_entry);
+ builder_->set_current_block(loop_entry);
+ return loop_entry;
+}
+
+
+HBasicBlock* HOsrBuilder::BuildPossibleOsrLoopEntry(
+ IterationStatement* statement) {
+ // Check if there is an OSR here first.
+ if (!HasOsrEntryAt(statement)) return BuildLoopEntry();
+
+ Zone* zone = builder_->zone();
+ HGraph* graph = builder_->graph();
+
+ // only one OSR point per compile is allowed.
+ ASSERT(graph->osr() == NULL);
+
+ // remember this builder as the one OSR builder in the graph.
+ graph->set_osr(this);
+
+ HBasicBlock* non_osr_entry = graph->CreateBasicBlock();
+ osr_entry_ = graph->CreateBasicBlock();
+ HValue* true_value = graph->GetConstantTrue();
+ HBranch* test = new(zone) HBranch(true_value, ToBooleanStub::Types(),
+ non_osr_entry, osr_entry_);
+ builder_->current_block()->Finish(test);
+
+ HBasicBlock* loop_predecessor = graph->CreateBasicBlock();
+ non_osr_entry->Goto(loop_predecessor);
+
+ builder_->set_current_block(osr_entry_);
+ osr_entry_->set_osr_entry();
+ BailoutId osr_entry_id = statement->OsrEntryId();
+
+ HEnvironment *environment = builder_->environment();
+ int first_expression_index = environment->first_expression_index();
+ int length = environment->length();
+ osr_values_ = new(zone) ZoneList<HUnknownOSRValue*>(length, zone);
+
+ for (int i = 0; i < first_expression_index; ++i) {
+ HUnknownOSRValue* osr_value = builder_->Add<HUnknownOSRValue>();
+ environment->Bind(i, osr_value);
+ osr_values_->Add(osr_value, zone);
+ }
+
+ if (first_expression_index != length) {
+ environment->Drop(length - first_expression_index);
+ for (int i = first_expression_index; i < length; ++i) {
+ HUnknownOSRValue* osr_value = builder_->Add<HUnknownOSRValue>();
+ environment->Push(osr_value);
+ osr_values_->Add(osr_value, zone);
+ }
+ }
+
+ builder_->Add<HSimulate>(osr_entry_id);
+ builder_->Add<HOsrEntry>(osr_entry_id);
+ HContext* context = builder_->Add<HContext>();
+ environment->BindContext(context);
+ builder_->current_block()->Goto(loop_predecessor);
+ loop_predecessor->SetJoinId(statement->EntryId());
+ builder_->set_current_block(loop_predecessor);
+
+ // Create the final loop entry
+ osr_loop_entry_ = BuildLoopEntry();
+ return osr_loop_entry_;
+}
+
+
+void HOsrBuilder::FinishGraph() {
+ // do nothing for now.
+}
+
+
+void HOsrBuilder::FinishOsrValues() {
+ const ZoneList<HPhi*>* phis = osr_loop_entry_->phis();
+ for (int j = 0; j < phis->length(); j++) {
+ HPhi* phi = phis->at(j);
+ ASSERT(phi->HasMergedIndex());
+ osr_values_->at(phi->merged_index())->set_incoming_value(phi);
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-osr.h b/chromium/v8/src/hydrogen-osr.h
new file mode 100644
index 00000000000..0c6b65d0d4e
--- /dev/null
+++ b/chromium/v8/src/hydrogen-osr.h
@@ -0,0 +1,70 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_OSR_H_
+#define V8_HYDROGEN_OSR_H_
+
+#include "hydrogen.h"
+#include "ast.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+// Responsible for building graph parts related to OSR and otherwise
+// setting up the graph to do an OSR compile.
+class HOsrBuilder : public ZoneObject {
+ public:
+ explicit HOsrBuilder(HOptimizedGraphBuilder* builder)
+ : builder_(builder),
+ osr_entry_(NULL),
+ osr_loop_entry_(NULL),
+ osr_values_(NULL) { }
+ // Creates the loop entry block for the given statement, setting up OSR
+ // entries as necessary, and sets the current block to the new block.
+ HBasicBlock* BuildPossibleOsrLoopEntry(IterationStatement* statement);
+
+ // Process the hydrogen graph after it has been completed, performing
+ // any OSR-specific cleanups or changes.
+ void FinishGraph();
+
+ // Process the OSR values and phis after initial graph optimization.
+ void FinishOsrValues();
+
+ private:
+ HBasicBlock* BuildLoopEntry();
+ bool HasOsrEntryAt(IterationStatement* statement);
+
+ HOptimizedGraphBuilder* builder_;
+ HBasicBlock* osr_entry_;
+ HBasicBlock* osr_loop_entry_;
+ ZoneList<HUnknownOSRValue*>* osr_values_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_OSR_H_
diff --git a/chromium/v8/src/hydrogen-range-analysis.cc b/chromium/v8/src/hydrogen-range-analysis.cc
new file mode 100644
index 00000000000..76fd5f35f28
--- /dev/null
+++ b/chromium/v8/src/hydrogen-range-analysis.cc
@@ -0,0 +1,200 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-range-analysis.h"
+
+namespace v8 {
+namespace internal {
+
+
+class Pending {
+ public:
+ Pending(HBasicBlock* block, int last_changed_range)
+ : block_(block), last_changed_range_(last_changed_range) {}
+
+ HBasicBlock* block() const { return block_; }
+ int last_changed_range() const { return last_changed_range_; }
+
+ private:
+ HBasicBlock* block_;
+ int last_changed_range_;
+};
+
+
+void HRangeAnalysisPhase::TraceRange(const char* msg, ...) {
+ if (FLAG_trace_range) {
+ va_list arguments;
+ va_start(arguments, msg);
+ OS::VPrint(msg, arguments);
+ va_end(arguments);
+ }
+}
+
+
+void HRangeAnalysisPhase::Run() {
+ HBasicBlock* block(graph()->entry_block());
+ ZoneList<Pending> stack(graph()->blocks()->length(), zone());
+ while (block != NULL) {
+ TraceRange("Analyzing block B%d\n", block->block_id());
+
+ // Infer range based on control flow.
+ if (block->predecessors()->length() == 1) {
+ HBasicBlock* pred = block->predecessors()->first();
+ if (pred->end()->IsCompareNumericAndBranch()) {
+ InferControlFlowRange(HCompareNumericAndBranch::cast(pred->end()),
+ block);
+ }
+ }
+
+ // Process phi instructions.
+ for (int i = 0; i < block->phis()->length(); ++i) {
+ HPhi* phi = block->phis()->at(i);
+ InferRange(phi);
+ }
+
+ // Go through all instructions of the current block.
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ InferRange(it.Current());
+ }
+
+ // Continue analysis in all dominated blocks.
+ const ZoneList<HBasicBlock*>* dominated_blocks(block->dominated_blocks());
+ if (!dominated_blocks->is_empty()) {
+ // Continue with first dominated block, and push the
+ // remaining blocks on the stack (in reverse order).
+ int last_changed_range = changed_ranges_.length();
+ for (int i = dominated_blocks->length() - 1; i > 0; --i) {
+ stack.Add(Pending(dominated_blocks->at(i), last_changed_range), zone());
+ }
+ block = dominated_blocks->at(0);
+ } else if (!stack.is_empty()) {
+ // Pop next pending block from stack.
+ Pending pending = stack.RemoveLast();
+ RollBackTo(pending.last_changed_range());
+ block = pending.block();
+ } else {
+ // All blocks done.
+ block = NULL;
+ }
+ }
+}
+
+
+void HRangeAnalysisPhase::InferControlFlowRange(HCompareNumericAndBranch* test,
+ HBasicBlock* dest) {
+ ASSERT((test->FirstSuccessor() == dest) == (test->SecondSuccessor() != dest));
+ if (test->representation().IsSmiOrInteger32()) {
+ Token::Value op = test->token();
+ if (test->SecondSuccessor() == dest) {
+ op = Token::NegateCompareOp(op);
+ }
+ Token::Value inverted_op = Token::ReverseCompareOp(op);
+ UpdateControlFlowRange(op, test->left(), test->right());
+ UpdateControlFlowRange(inverted_op, test->right(), test->left());
+ }
+}
+
+
+// We know that value [op] other. Use this information to update the range on
+// value.
+void HRangeAnalysisPhase::UpdateControlFlowRange(Token::Value op,
+ HValue* value,
+ HValue* other) {
+ Range temp_range;
+ Range* range = other->range() != NULL ? other->range() : &temp_range;
+ Range* new_range = NULL;
+
+ TraceRange("Control flow range infer %d %s %d\n",
+ value->id(),
+ Token::Name(op),
+ other->id());
+
+ if (op == Token::EQ || op == Token::EQ_STRICT) {
+ // The same range has to apply for value.
+ new_range = range->Copy(graph()->zone());
+ } else if (op == Token::LT || op == Token::LTE) {
+ new_range = range->CopyClearLower(graph()->zone());
+ if (op == Token::LT) {
+ new_range->AddConstant(-1);
+ }
+ } else if (op == Token::GT || op == Token::GTE) {
+ new_range = range->CopyClearUpper(graph()->zone());
+ if (op == Token::GT) {
+ new_range->AddConstant(1);
+ }
+ }
+
+ if (new_range != NULL && !new_range->IsMostGeneric()) {
+ AddRange(value, new_range);
+ }
+}
+
+
+void HRangeAnalysisPhase::InferRange(HValue* value) {
+ ASSERT(!value->HasRange());
+ if (!value->representation().IsNone()) {
+ value->ComputeInitialRange(graph()->zone());
+ Range* range = value->range();
+ TraceRange("Initial inferred range of %d (%s) set to [%d,%d]\n",
+ value->id(),
+ value->Mnemonic(),
+ range->lower(),
+ range->upper());
+ }
+}
+
+
+void HRangeAnalysisPhase::RollBackTo(int index) {
+ ASSERT(index <= changed_ranges_.length());
+ for (int i = index; i < changed_ranges_.length(); ++i) {
+ changed_ranges_[i]->RemoveLastAddedRange();
+ }
+ changed_ranges_.Rewind(index);
+}
+
+
+void HRangeAnalysisPhase::AddRange(HValue* value, Range* range) {
+ Range* original_range = value->range();
+ value->AddNewRange(range, graph()->zone());
+ changed_ranges_.Add(value, zone());
+ Range* new_range = value->range();
+ TraceRange("Updated range of %d set to [%d,%d]\n",
+ value->id(),
+ new_range->lower(),
+ new_range->upper());
+ if (original_range != NULL) {
+ TraceRange("Original range was [%d,%d]\n",
+ original_range->lower(),
+ original_range->upper());
+ }
+ TraceRange("New information was [%d,%d]\n",
+ range->lower(),
+ range->upper());
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-range-analysis.h b/chromium/v8/src/hydrogen-range-analysis.h
new file mode 100644
index 00000000000..a1e9737c5e0
--- /dev/null
+++ b/chromium/v8/src/hydrogen-range-analysis.h
@@ -0,0 +1,59 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_RANGE_ANALYSIS_H_
+#define V8_HYDROGEN_RANGE_ANALYSIS_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HRangeAnalysisPhase : public HPhase {
+ public:
+ explicit HRangeAnalysisPhase(HGraph* graph)
+ : HPhase("H_Range analysis", graph), changed_ranges_(16, zone()) { }
+
+ void Run();
+
+ private:
+ void TraceRange(const char* msg, ...);
+ void InferControlFlowRange(HCompareNumericAndBranch* test,
+ HBasicBlock* dest);
+ void UpdateControlFlowRange(Token::Value op, HValue* value, HValue* other);
+ void InferRange(HValue* value);
+ void RollBackTo(int index);
+ void AddRange(HValue* value, Range* range);
+
+ ZoneList<HValue*> changed_ranges_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_RANGE_ANALYSIS_H_
diff --git a/chromium/v8/src/hydrogen-redundant-phi.cc b/chromium/v8/src/hydrogen-redundant-phi.cc
new file mode 100644
index 00000000000..9c38200577d
--- /dev/null
+++ b/chromium/v8/src/hydrogen-redundant-phi.cc
@@ -0,0 +1,76 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-redundant-phi.h"
+
+namespace v8 {
+namespace internal {
+
+void HRedundantPhiEliminationPhase::Run() {
+ // We do a simple fixed point iteration without any work list, because
+ // machine-generated JavaScript can lead to a very dense Hydrogen graph with
+ // an enormous work list and will consequently result in OOM. Experiments
+ // showed that this simple algorithm is good enough, and even e.g. tracking
+ // the set or range of blocks to consider is not a real improvement.
+ bool need_another_iteration;
+ const ZoneList<HBasicBlock*>* blocks(graph()->blocks());
+ ZoneList<HPhi*> redundant_phis(blocks->length(), zone());
+ do {
+ need_another_iteration = false;
+ for (int i = 0; i < blocks->length(); ++i) {
+ HBasicBlock* block = blocks->at(i);
+ for (int j = 0; j < block->phis()->length(); j++) {
+ HPhi* phi = block->phis()->at(j);
+ HValue* replacement = phi->GetRedundantReplacement();
+ if (replacement != NULL) {
+ // Remember phi to avoid concurrent modification of the block's phis.
+ redundant_phis.Add(phi, zone());
+ for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ value->SetOperandAt(it.index(), replacement);
+ need_another_iteration |= value->IsPhi();
+ }
+ }
+ }
+ for (int i = 0; i < redundant_phis.length(); i++) {
+ block->RemovePhi(redundant_phis[i]);
+ }
+ redundant_phis.Clear();
+ }
+ } while (need_another_iteration);
+
+#if DEBUG
+ // Make sure that we *really* removed all redundant phis.
+ for (int i = 0; i < blocks->length(); ++i) {
+ for (int j = 0; j < blocks->at(i)->phis()->length(); j++) {
+ ASSERT(blocks->at(i)->phis()->at(j)->GetRedundantReplacement() == NULL);
+ }
+ }
+#endif
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-redundant-phi.h b/chromium/v8/src/hydrogen-redundant-phi.h
new file mode 100644
index 00000000000..6291fa5b787
--- /dev/null
+++ b/chromium/v8/src/hydrogen-redundant-phi.h
@@ -0,0 +1,53 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_REDUNDANT_PHI_H_
+#define V8_HYDROGEN_REDUNDANT_PHI_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Replace all phis consisting of a single non-loop operand plus any number of
+// loop operands by that single non-loop operand.
+class HRedundantPhiEliminationPhase : public HPhase {
+ public:
+ explicit HRedundantPhiEliminationPhase(HGraph* graph)
+ : HPhase("H_Redundant phi elimination", graph) { }
+
+ void Run();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HRedundantPhiEliminationPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_REDUNDANT_PHI_H_
diff --git a/chromium/v8/src/hydrogen-removable-simulates.cc b/chromium/v8/src/hydrogen-removable-simulates.cc
new file mode 100644
index 00000000000..f952832431c
--- /dev/null
+++ b/chromium/v8/src/hydrogen-removable-simulates.cc
@@ -0,0 +1,94 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-removable-simulates.h"
+
+namespace v8 {
+namespace internal {
+
+void HMergeRemovableSimulatesPhase::Run() {
+ ZoneList<HSimulate*> mergelist(2, zone());
+ for (int i = 0; i < graph()->blocks()->length(); ++i) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ // Make sure the merge list is empty at the start of a block.
+ ASSERT(mergelist.is_empty());
+ // Nasty heuristic: Never remove the first simulate in a block. This
+ // just so happens to have a beneficial effect on register allocation.
+ bool first = true;
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->IsLeaveInlined()) {
+ // Never fold simulates from inlined environments into simulates
+ // in the outer environment.
+ // (Before each HEnterInlined, there is a non-foldable HSimulate
+ // anyway, so we get the barrier in the other direction for free.)
+ // Simply remove all accumulated simulates without merging. This
+ // is safe because simulates after instructions with side effects
+ // are never added to the merge list.
+ while (!mergelist.is_empty()) {
+ mergelist.RemoveLast()->DeleteAndReplaceWith(NULL);
+ }
+ continue;
+ }
+ if (current->IsReturn()) {
+ // Drop mergeable simulates in the list. This is safe because
+ // simulates after instructions with side effects are never added
+ // to the merge list.
+ while (!mergelist.is_empty()) {
+ mergelist.RemoveLast()->DeleteAndReplaceWith(NULL);
+ }
+ continue;
+ }
+ // Skip the non-simulates and the first simulate.
+ if (!current->IsSimulate()) continue;
+ if (first) {
+ first = false;
+ continue;
+ }
+ HSimulate* current_simulate = HSimulate::cast(current);
+ if ((current_simulate->previous()->HasObservableSideEffects() &&
+ !current_simulate->next()->IsSimulate()) ||
+ !current_simulate->is_candidate_for_removal()) {
+ // This simulate is not suitable for folding.
+ // Fold the ones accumulated so far.
+ current_simulate->MergeWith(&mergelist);
+ continue;
+ } else {
+ // Accumulate this simulate for folding later on.
+ mergelist.Add(current_simulate, zone());
+ }
+ }
+
+ if (!mergelist.is_empty()) {
+ // Merge the accumulated simulates at the end of the block.
+ HSimulate* last = mergelist.RemoveLast();
+ last->MergeWith(&mergelist);
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-removable-simulates.h b/chromium/v8/src/hydrogen-removable-simulates.h
new file mode 100644
index 00000000000..f5bcd6ddfa8
--- /dev/null
+++ b/chromium/v8/src/hydrogen-removable-simulates.h
@@ -0,0 +1,51 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_REMOVABLE_SIMULATES_H_
+#define V8_HYDROGEN_REMOVABLE_SIMULATES_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HMergeRemovableSimulatesPhase : public HPhase {
+ public:
+ explicit HMergeRemovableSimulatesPhase(HGraph* graph)
+ : HPhase("H_Merge removable simulates", graph) { }
+
+ void Run();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HMergeRemovableSimulatesPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_REMOVABLE_SIMULATES_H_
diff --git a/chromium/v8/src/hydrogen-representation-changes.cc b/chromium/v8/src/hydrogen-representation-changes.cc
new file mode 100644
index 00000000000..862457db38c
--- /dev/null
+++ b/chromium/v8/src/hydrogen-representation-changes.cc
@@ -0,0 +1,178 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-representation-changes.h"
+
+namespace v8 {
+namespace internal {
+
+void HRepresentationChangesPhase::InsertRepresentationChangeForUse(
+ HValue* value, HValue* use_value, int use_index, Representation to) {
+ // Insert the representation change right before its use. For phi-uses we
+ // insert at the end of the corresponding predecessor.
+ HInstruction* next = NULL;
+ if (use_value->IsPhi()) {
+ next = use_value->block()->predecessors()->at(use_index)->end();
+ } else {
+ next = HInstruction::cast(use_value);
+ }
+ // For constants we try to make the representation change at compile
+ // time. When a representation change is not possible without loss of
+ // information we treat constants like normal instructions and insert the
+ // change instructions for them.
+ HInstruction* new_value = NULL;
+ bool is_truncating_to_smi = use_value->CheckFlag(HValue::kTruncatingToSmi);
+ bool is_truncating_to_int = use_value->CheckFlag(HValue::kTruncatingToInt32);
+ if (value->IsConstant()) {
+ HConstant* constant = HConstant::cast(value);
+ // Try to create a new copy of the constant with the new representation.
+ if (is_truncating_to_int && to.IsInteger32()) {
+ Maybe<HConstant*> res = constant->CopyToTruncatedInt32(graph()->zone());
+ if (res.has_value) new_value = res.value;
+ } else {
+ new_value = constant->CopyToRepresentation(to, graph()->zone());
+ }
+ }
+
+ if (new_value == NULL) {
+ new_value = new(graph()->zone()) HChange(
+ value, to, is_truncating_to_smi, is_truncating_to_int);
+ }
+
+ new_value->InsertBefore(next);
+ use_value->SetOperandAt(use_index, new_value);
+}
+
+
+void HRepresentationChangesPhase::InsertRepresentationChangesForValue(
+ HValue* value) {
+ Representation r = value->representation();
+ if (r.IsNone()) return;
+ if (value->HasNoUses()) return;
+
+ for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
+ HValue* use_value = it.value();
+ int use_index = it.index();
+ Representation req = use_value->RequiredInputRepresentation(use_index);
+ if (req.IsNone() || req.Equals(r)) continue;
+ InsertRepresentationChangeForUse(value, use_value, use_index, req);
+ }
+ if (value->HasNoUses()) {
+ ASSERT(value->IsConstant());
+ value->DeleteAndReplaceWith(NULL);
+ }
+
+ // The only purpose of a HForceRepresentation is to represent the value
+ // after the (possible) HChange instruction. We make it disappear.
+ if (value->IsForceRepresentation()) {
+ value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
+ }
+}
+
+
+void HRepresentationChangesPhase::Run() {
+ // Compute truncation flag for phis: Initially assume that all
+ // int32-phis allow truncation and iteratively remove the ones that
+ // are used in an operation that does not allow a truncating
+ // conversion.
+ ZoneList<HPhi*> worklist(8, zone());
+
+ const ZoneList<HPhi*>* phi_list(graph()->phi_list());
+ for (int i = 0; i < phi_list->length(); i++) {
+ HPhi* phi = phi_list->at(i);
+ if (phi->representation().IsInteger32()) {
+ phi->SetFlag(HValue::kTruncatingToInt32);
+ } else if (phi->representation().IsSmi()) {
+ phi->SetFlag(HValue::kTruncatingToSmi);
+ }
+ }
+
+ for (int i = 0; i < phi_list->length(); i++) {
+ HPhi* phi = phi_list->at(i);
+ for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
+ // If a Phi is used as a non-truncating int32 or as a double,
+ // clear its "truncating" flag.
+ HValue* use = it.value();
+ Representation input_representation =
+ use->RequiredInputRepresentation(it.index());
+ if ((phi->representation().IsInteger32() &&
+ !(input_representation.IsInteger32() &&
+ use->CheckFlag(HValue::kTruncatingToInt32))) ||
+ (phi->representation().IsSmi() &&
+ !(input_representation.IsSmi() &&
+ use->CheckFlag(HValue::kTruncatingToSmi)))) {
+ if (FLAG_trace_representation) {
+ PrintF("#%d Phi is not truncating because of #%d %s\n",
+ phi->id(), it.value()->id(), it.value()->Mnemonic());
+ }
+ phi->ClearFlag(HValue::kTruncatingToInt32);
+ phi->ClearFlag(HValue::kTruncatingToSmi);
+ worklist.Add(phi, zone());
+ break;
+ }
+ }
+ }
+
+ while (!worklist.is_empty()) {
+ HPhi* current = worklist.RemoveLast();
+ for (int i = 0; i < current->OperandCount(); ++i) {
+ HValue* input = current->OperandAt(i);
+ if (input->IsPhi() &&
+ ((input->representation().IsInteger32() &&
+ input->CheckFlag(HValue::kTruncatingToInt32)) ||
+ (input->representation().IsSmi() &&
+ input->CheckFlag(HValue::kTruncatingToSmi)))) {
+ if (FLAG_trace_representation) {
+ PrintF("#%d Phi is not truncating because of #%d %s\n",
+ input->id(), current->id(), current->Mnemonic());
+ }
+ input->ClearFlag(HValue::kTruncatingToInt32);
+ input->ClearFlag(HValue::kTruncatingToSmi);
+ worklist.Add(HPhi::cast(input), zone());
+ }
+ }
+ }
+
+ const ZoneList<HBasicBlock*>* blocks(graph()->blocks());
+ for (int i = 0; i < blocks->length(); ++i) {
+ // Process phi instructions first.
+ const HBasicBlock* block(blocks->at(i));
+ const ZoneList<HPhi*>* phis = block->phis();
+ for (int j = 0; j < phis->length(); j++) {
+ InsertRepresentationChangesForValue(phis->at(j));
+ }
+
+ // Process normal instructions.
+ for (HInstruction* current = block->first(); current != NULL; ) {
+ HInstruction* next = current->next();
+ InsertRepresentationChangesForValue(current);
+ current = next;
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-representation-changes.h b/chromium/v8/src/hydrogen-representation-changes.h
new file mode 100644
index 00000000000..77e899b60b3
--- /dev/null
+++ b/chromium/v8/src/hydrogen-representation-changes.h
@@ -0,0 +1,55 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_REPRESENTATION_CHANGES_H_
+#define V8_HYDROGEN_REPRESENTATION_CHANGES_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HRepresentationChangesPhase : public HPhase {
+ public:
+ explicit HRepresentationChangesPhase(HGraph* graph)
+ : HPhase("H_Representation changes", graph) { }
+
+ void Run();
+
+ private:
+ void InsertRepresentationChangeForUse(HValue* value,
+ HValue* use_value,
+ int use_index,
+ Representation to);
+ void InsertRepresentationChangesForValue(HValue* value);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_REPRESENTATION_CHANGES_H_
diff --git a/chromium/v8/src/hydrogen-sce.cc b/chromium/v8/src/hydrogen-sce.cc
new file mode 100644
index 00000000000..a6995f647af
--- /dev/null
+++ b/chromium/v8/src/hydrogen-sce.cc
@@ -0,0 +1,62 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-sce.h"
+#include "v8.h"
+
+namespace v8 {
+namespace internal {
+
+void HStackCheckEliminationPhase::Run() {
+ // For each loop block walk the dominator tree from the backwards branch to
+ // the loop header. If a call instruction is encountered the backwards branch
+ // is dominated by a call and the stack check in the backwards branch can be
+ // removed.
+ for (int i = 0; i < graph()->blocks()->length(); i++) {
+ HBasicBlock* block = graph()->blocks()->at(i);
+ if (block->IsLoopHeader()) {
+ HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
+ HBasicBlock* dominator = back_edge;
+ while (true) {
+ for (HInstructionIterator it(dominator); !it.Done(); it.Advance()) {
+ if (it.Current()->IsCall()) {
+ block->loop_information()->stack_check()->Eliminate();
+ break;
+ }
+ }
+
+ // Done when the loop header is processed.
+ if (dominator == block) break;
+
+ // Move up the dominator tree.
+ dominator = dominator->dominator();
+ }
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-sce.h b/chromium/v8/src/hydrogen-sce.h
new file mode 100644
index 00000000000..55e153e0ed5
--- /dev/null
+++ b/chromium/v8/src/hydrogen-sce.h
@@ -0,0 +1,48 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_SCE_H_
+#define V8_HYDROGEN_SCE_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+class HStackCheckEliminationPhase : public HPhase {
+ public:
+ explicit HStackCheckEliminationPhase(HGraph* graph)
+ : HPhase("H_Stack check elimination", graph) { }
+
+ void Run();
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_SCE_H_
diff --git a/chromium/v8/src/hydrogen-uint32-analysis.cc b/chromium/v8/src/hydrogen-uint32-analysis.cc
new file mode 100644
index 00000000000..835a198d4d8
--- /dev/null
+++ b/chromium/v8/src/hydrogen-uint32-analysis.cc
@@ -0,0 +1,227 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen-uint32-analysis.h"
+
+namespace v8 {
+namespace internal {
+
+
+bool HUint32AnalysisPhase::IsSafeUint32Use(HValue* val, HValue* use) {
+ // Operations that operate on bits are safe.
+ if (use->IsBitwise() || use->IsShl() || use->IsSar() || use->IsShr()) {
+ return true;
+ } else if (use->IsChange() || use->IsSimulate()) {
+ // Conversions and deoptimization have special support for unt32.
+ return true;
+ } else if (use->IsStoreKeyed()) {
+ HStoreKeyed* store = HStoreKeyed::cast(use);
+ if (store->is_external()) {
+ // Storing a value into an external integer array is a bit level
+ // operation.
+ if (store->value() == val) {
+ // Clamping or a conversion to double should have beed inserted.
+ ASSERT(store->elements_kind() != EXTERNAL_PIXEL_ELEMENTS);
+ ASSERT(store->elements_kind() != EXTERNAL_FLOAT_ELEMENTS);
+ ASSERT(store->elements_kind() != EXTERNAL_DOUBLE_ELEMENTS);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+// Iterate over all uses and verify that they are uint32 safe: either don't
+// distinguish between int32 and uint32 due to their bitwise nature or
+// have special support for uint32 values.
+// Encountered phis are optimistically treated as safe uint32 uses,
+// marked with kUint32 flag and collected in the phis_ list. A separate
+// pass will be performed later by UnmarkUnsafePhis to clear kUint32 from
+// phis that are not actually uint32-safe (it requires fix point iteration).
+bool HUint32AnalysisPhase::Uint32UsesAreSafe(HValue* uint32val) {
+ bool collect_phi_uses = false;
+ for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+
+ if (use->IsPhi()) {
+ if (!use->CheckFlag(HInstruction::kUint32)) {
+ // There is a phi use of this value from a phi that is not yet
+ // collected in phis_ array. Separate pass is required.
+ collect_phi_uses = true;
+ }
+
+ // Optimistically treat phis as uint32 safe.
+ continue;
+ }
+
+ if (!IsSafeUint32Use(uint32val, use)) {
+ return false;
+ }
+ }
+
+ if (collect_phi_uses) {
+ for (HUseIterator it(uint32val->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+
+ // There is a phi use of this value from a phi that is not yet
+ // collected in phis_ array. Separate pass is required.
+ if (use->IsPhi() && !use->CheckFlag(HInstruction::kUint32)) {
+ use->SetFlag(HInstruction::kUint32);
+ phis_.Add(HPhi::cast(use), zone());
+ }
+ }
+ }
+
+ return true;
+}
+
+
+// Check if all operands to the given phi are marked with kUint32 flag.
+bool HUint32AnalysisPhase::CheckPhiOperands(HPhi* phi) {
+ if (!phi->CheckFlag(HInstruction::kUint32)) {
+ // This phi is not uint32 safe. No need to check operands.
+ return false;
+ }
+
+ for (int j = 0; j < phi->OperandCount(); j++) {
+ HValue* operand = phi->OperandAt(j);
+ if (!operand->CheckFlag(HInstruction::kUint32)) {
+ // Lazily mark constants that fit into uint32 range with kUint32 flag.
+ if (operand->IsInteger32Constant() &&
+ operand->GetInteger32Constant() >= 0) {
+ operand->SetFlag(HInstruction::kUint32);
+ continue;
+ }
+
+ // This phi is not safe, some operands are not uint32 values.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+// Remove kUint32 flag from the phi itself and its operands. If any operand
+// was a phi marked with kUint32 place it into a worklist for
+// transitive clearing of kUint32 flag.
+void HUint32AnalysisPhase::UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist) {
+ phi->ClearFlag(HInstruction::kUint32);
+ for (int j = 0; j < phi->OperandCount(); j++) {
+ HValue* operand = phi->OperandAt(j);
+ if (operand->CheckFlag(HInstruction::kUint32)) {
+ operand->ClearFlag(HInstruction::kUint32);
+ if (operand->IsPhi()) {
+ worklist->Add(HPhi::cast(operand), zone());
+ }
+ }
+ }
+}
+
+
+void HUint32AnalysisPhase::UnmarkUnsafePhis() {
+ // No phis were collected. Nothing to do.
+ if (phis_.length() == 0) return;
+
+ // Worklist used to transitively clear kUint32 from phis that
+ // are used as arguments to other phis.
+ ZoneList<HPhi*> worklist(phis_.length(), zone());
+
+ // Phi can be used as a uint32 value if and only if
+ // all its operands are uint32 values and all its
+ // uses are uint32 safe.
+
+ // Iterate over collected phis and unmark those that
+ // are unsafe. When unmarking phi unmark its operands
+ // and add it to the worklist if it is a phi as well.
+ // Phis that are still marked as safe are shifted down
+ // so that all safe phis form a prefix of the phis_ array.
+ int phi_count = 0;
+ for (int i = 0; i < phis_.length(); i++) {
+ HPhi* phi = phis_[i];
+
+ if (CheckPhiOperands(phi) && Uint32UsesAreSafe(phi)) {
+ phis_[phi_count++] = phi;
+ } else {
+ UnmarkPhi(phi, &worklist);
+ }
+ }
+
+ // Now phis array contains only those phis that have safe
+ // non-phi uses. Start transitively clearing kUint32 flag
+ // from phi operands of discovered non-safe phis until
+ // only safe phis are left.
+ while (!worklist.is_empty()) {
+ while (!worklist.is_empty()) {
+ HPhi* phi = worklist.RemoveLast();
+ UnmarkPhi(phi, &worklist);
+ }
+
+ // Check if any operands to safe phis were unmarked
+ // turning a safe phi into unsafe. The same value
+ // can flow into several phis.
+ int new_phi_count = 0;
+ for (int i = 0; i < phi_count; i++) {
+ HPhi* phi = phis_[i];
+
+ if (CheckPhiOperands(phi)) {
+ phis_[new_phi_count++] = phi;
+ } else {
+ UnmarkPhi(phi, &worklist);
+ }
+ }
+ phi_count = new_phi_count;
+ }
+}
+
+
+void HUint32AnalysisPhase::Run() {
+ if (!graph()->has_uint32_instructions()) return;
+
+ ZoneList<HInstruction*>* uint32_instructions = graph()->uint32_instructions();
+ for (int i = 0; i < uint32_instructions->length(); ++i) {
+ // Analyze instruction and mark it with kUint32 if all
+ // its uses are uint32 safe.
+ HInstruction* current = uint32_instructions->at(i);
+ if (current->IsLinked() &&
+ current->representation().IsInteger32() &&
+ Uint32UsesAreSafe(current)) {
+ current->SetFlag(HInstruction::kUint32);
+ }
+ }
+
+ // Some phis might have been optimistically marked with kUint32 flag.
+ // Remove this flag from those phis that are unsafe and propagate
+ // this information transitively potentially clearing kUint32 flag
+ // from some non-phi operations that are used as operands to unsafe phis.
+ UnmarkUnsafePhis();
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen-uint32-analysis.h b/chromium/v8/src/hydrogen-uint32-analysis.h
new file mode 100644
index 00000000000..59739d1ccf1
--- /dev/null
+++ b/chromium/v8/src/hydrogen-uint32-analysis.h
@@ -0,0 +1,59 @@
+// Copyright 2013 the V8 project authors. 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_HYDROGEN_UINT32_ANALYSIS_H_
+#define V8_HYDROGEN_UINT32_ANALYSIS_H_
+
+#include "hydrogen.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Discover instructions that can be marked with kUint32 flag allowing
+// them to produce full range uint32 values.
+class HUint32AnalysisPhase : public HPhase {
+ public:
+ explicit HUint32AnalysisPhase(HGraph* graph)
+ : HPhase("H_Compute safe UInt32 operations", graph), phis_(4, zone()) { }
+
+ void Run();
+
+ private:
+ INLINE(bool IsSafeUint32Use(HValue* val, HValue* use));
+ INLINE(bool Uint32UsesAreSafe(HValue* uint32val));
+ INLINE(bool CheckPhiOperands(HPhi* phi));
+ INLINE(void UnmarkPhi(HPhi* phi, ZoneList<HPhi*>* worklist));
+ INLINE(void UnmarkUnsafePhis());
+
+ ZoneList<HPhi*> phis_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_UINT32_ANALYSIS_H_
diff --git a/chromium/v8/src/hydrogen.cc b/chromium/v8/src/hydrogen.cc
new file mode 100644
index 00000000000..ba1de7aa225
--- /dev/null
+++ b/chromium/v8/src/hydrogen.cc
@@ -0,0 +1,9852 @@
+// Copyright 2013 the V8 project authors. 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 "hydrogen.h"
+
+#include <algorithm>
+
+#include "v8.h"
+#include "codegen.h"
+#include "full-codegen.h"
+#include "hashmap.h"
+#include "hydrogen-bce.h"
+#include "hydrogen-bch.h"
+#include "hydrogen-canonicalize.h"
+#include "hydrogen-dce.h"
+#include "hydrogen-dehoist.h"
+#include "hydrogen-deoptimizing-mark.h"
+#include "hydrogen-environment-liveness.h"
+#include "hydrogen-escape-analysis.h"
+#include "hydrogen-infer-representation.h"
+#include "hydrogen-infer-types.h"
+#include "hydrogen-gvn.h"
+#include "hydrogen-mark-deoptimize.h"
+#include "hydrogen-minus-zero.h"
+#include "hydrogen-osr.h"
+#include "hydrogen-range-analysis.h"
+#include "hydrogen-redundant-phi.h"
+#include "hydrogen-removable-simulates.h"
+#include "hydrogen-representation-changes.h"
+#include "hydrogen-sce.h"
+#include "hydrogen-uint32-analysis.h"
+#include "lithium-allocator.h"
+#include "parser.h"
+#include "scopeinfo.h"
+#include "scopes.h"
+#include "stub-cache.h"
+#include "typing.h"
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/lithium-codegen-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/lithium-codegen-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/lithium-codegen-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/lithium-codegen-mips.h"
+#else
+#error Unsupported target architecture.
+#endif
+
+namespace v8 {
+namespace internal {
+
+HBasicBlock::HBasicBlock(HGraph* graph)
+ : block_id_(graph->GetNextBlockID()),
+ graph_(graph),
+ phis_(4, graph->zone()),
+ first_(NULL),
+ last_(NULL),
+ end_(NULL),
+ loop_information_(NULL),
+ predecessors_(2, graph->zone()),
+ dominator_(NULL),
+ dominated_blocks_(4, graph->zone()),
+ last_environment_(NULL),
+ argument_count_(-1),
+ first_instruction_index_(-1),
+ last_instruction_index_(-1),
+ deleted_phis_(4, graph->zone()),
+ parent_loop_header_(NULL),
+ inlined_entry_block_(NULL),
+ is_inline_return_target_(false),
+ is_deoptimizing_(false),
+ dominates_loop_successors_(false),
+ is_osr_entry_(false) { }
+
+
+Isolate* HBasicBlock::isolate() const {
+ return graph_->isolate();
+}
+
+
+void HBasicBlock::AttachLoopInformation() {
+ ASSERT(!IsLoopHeader());
+ loop_information_ = new(zone()) HLoopInformation(this, zone());
+}
+
+
+void HBasicBlock::DetachLoopInformation() {
+ ASSERT(IsLoopHeader());
+ loop_information_ = NULL;
+}
+
+
+void HBasicBlock::AddPhi(HPhi* phi) {
+ ASSERT(!IsStartBlock());
+ phis_.Add(phi, zone());
+ phi->SetBlock(this);
+}
+
+
+void HBasicBlock::RemovePhi(HPhi* phi) {
+ ASSERT(phi->block() == this);
+ ASSERT(phis_.Contains(phi));
+ phi->Kill();
+ phis_.RemoveElement(phi);
+ phi->SetBlock(NULL);
+}
+
+
+void HBasicBlock::AddInstruction(HInstruction* instr) {
+ ASSERT(!IsStartBlock() || !IsFinished());
+ ASSERT(!instr->IsLinked());
+ ASSERT(!IsFinished());
+
+ if (first_ == NULL) {
+ ASSERT(last_environment() != NULL);
+ ASSERT(!last_environment()->ast_id().IsNone());
+ HBlockEntry* entry = new(zone()) HBlockEntry();
+ entry->InitializeAsFirst(this);
+ first_ = last_ = entry;
+ }
+ instr->InsertAfter(last_);
+}
+
+
+HPhi* HBasicBlock::AddNewPhi(int merged_index) {
+ if (graph()->IsInsideNoSideEffectsScope()) {
+ merged_index = HPhi::kInvalidMergedIndex;
+ }
+ HPhi* phi = new(zone()) HPhi(merged_index, zone());
+ AddPhi(phi);
+ return phi;
+}
+
+
+HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id,
+ RemovableSimulate removable) {
+ ASSERT(HasEnvironment());
+ HEnvironment* environment = last_environment();
+ ASSERT(ast_id.IsNone() ||
+ ast_id == BailoutId::StubEntry() ||
+ environment->closure()->shared()->VerifyBailoutId(ast_id));
+
+ int push_count = environment->push_count();
+ int pop_count = environment->pop_count();
+
+ HSimulate* instr =
+ new(zone()) HSimulate(ast_id, pop_count, zone(), removable);
+#ifdef DEBUG
+ instr->set_closure(environment->closure());
+#endif
+ // Order of pushed values: newest (top of stack) first. This allows
+ // HSimulate::MergeWith() to easily append additional pushed values
+ // that are older (from further down the stack).
+ for (int i = 0; i < push_count; ++i) {
+ instr->AddPushedValue(environment->ExpressionStackAt(i));
+ }
+ for (GrowableBitVector::Iterator it(environment->assigned_variables(),
+ zone());
+ !it.Done();
+ it.Advance()) {
+ int index = it.Current();
+ instr->AddAssignedValue(index, environment->Lookup(index));
+ }
+ environment->ClearHistory();
+ return instr;
+}
+
+
+void HBasicBlock::Finish(HControlInstruction* end) {
+ ASSERT(!IsFinished());
+ AddInstruction(end);
+ end_ = end;
+ for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
+ it.Current()->RegisterPredecessor(this);
+ }
+}
+
+
+void HBasicBlock::Goto(HBasicBlock* block,
+ FunctionState* state,
+ bool add_simulate) {
+ bool drop_extra = state != NULL &&
+ state->inlining_kind() == DROP_EXTRA_ON_RETURN;
+
+ if (block->IsInlineReturnTarget()) {
+ AddInstruction(new(zone()) HLeaveInlined());
+ UpdateEnvironment(last_environment()->DiscardInlined(drop_extra));
+ }
+
+ if (add_simulate) AddNewSimulate(BailoutId::None());
+ HGoto* instr = new(zone()) HGoto(block);
+ Finish(instr);
+}
+
+
+void HBasicBlock::AddLeaveInlined(HValue* return_value,
+ FunctionState* state) {
+ HBasicBlock* target = state->function_return();
+ bool drop_extra = state->inlining_kind() == DROP_EXTRA_ON_RETURN;
+
+ ASSERT(target->IsInlineReturnTarget());
+ ASSERT(return_value != NULL);
+ AddInstruction(new(zone()) HLeaveInlined());
+ UpdateEnvironment(last_environment()->DiscardInlined(drop_extra));
+ last_environment()->Push(return_value);
+ AddNewSimulate(BailoutId::None());
+ HGoto* instr = new(zone()) HGoto(target);
+ Finish(instr);
+}
+
+
+void HBasicBlock::SetInitialEnvironment(HEnvironment* env) {
+ ASSERT(!HasEnvironment());
+ ASSERT(first() == NULL);
+ UpdateEnvironment(env);
+}
+
+
+void HBasicBlock::UpdateEnvironment(HEnvironment* env) {
+ last_environment_ = env;
+ graph()->update_maximum_environment_size(env->first_expression_index());
+}
+
+
+void HBasicBlock::SetJoinId(BailoutId ast_id) {
+ int length = predecessors_.length();
+ ASSERT(length > 0);
+ for (int i = 0; i < length; i++) {
+ HBasicBlock* predecessor = predecessors_[i];
+ ASSERT(predecessor->end()->IsGoto());
+ HSimulate* simulate = HSimulate::cast(predecessor->end()->previous());
+ ASSERT(i != 0 ||
+ (predecessor->last_environment()->closure().is_null() ||
+ predecessor->last_environment()->closure()->shared()
+ ->VerifyBailoutId(ast_id)));
+ simulate->set_ast_id(ast_id);
+ predecessor->last_environment()->set_ast_id(ast_id);
+ }
+}
+
+
+bool HBasicBlock::Dominates(HBasicBlock* other) const {
+ HBasicBlock* current = other->dominator();
+ while (current != NULL) {
+ if (current == this) return true;
+ current = current->dominator();
+ }
+ return false;
+}
+
+
+int HBasicBlock::LoopNestingDepth() const {
+ const HBasicBlock* current = this;
+ int result = (current->IsLoopHeader()) ? 1 : 0;
+ while (current->parent_loop_header() != NULL) {
+ current = current->parent_loop_header();
+ result++;
+ }
+ return result;
+}
+
+
+void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
+ ASSERT(IsLoopHeader());
+
+ SetJoinId(stmt->EntryId());
+ if (predecessors()->length() == 1) {
+ // This is a degenerated loop.
+ DetachLoopInformation();
+ return;
+ }
+
+ // Only the first entry into the loop is from outside the loop. All other
+ // entries must be back edges.
+ for (int i = 1; i < predecessors()->length(); ++i) {
+ loop_information()->RegisterBackEdge(predecessors()->at(i));
+ }
+}
+
+
+void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
+ if (HasPredecessor()) {
+ // Only loop header blocks can have a predecessor added after
+ // instructions have been added to the block (they have phis for all
+ // values in the environment, these phis may be eliminated later).
+ ASSERT(IsLoopHeader() || first_ == NULL);
+ HEnvironment* incoming_env = pred->last_environment();
+ if (IsLoopHeader()) {
+ ASSERT(phis()->length() == incoming_env->length());
+ for (int i = 0; i < phis_.length(); ++i) {
+ phis_[i]->AddInput(incoming_env->values()->at(i));
+ }
+ } else {
+ last_environment()->AddIncomingEdge(this, pred->last_environment());
+ }
+ } else if (!HasEnvironment() && !IsFinished()) {
+ ASSERT(!IsLoopHeader());
+ SetInitialEnvironment(pred->last_environment()->Copy());
+ }
+
+ predecessors_.Add(pred, zone());
+}
+
+
+void HBasicBlock::AddDominatedBlock(HBasicBlock* block) {
+ ASSERT(!dominated_blocks_.Contains(block));
+ // Keep the list of dominated blocks sorted such that if there is two
+ // succeeding block in this list, the predecessor is before the successor.
+ int index = 0;
+ while (index < dominated_blocks_.length() &&
+ dominated_blocks_[index]->block_id() < block->block_id()) {
+ ++index;
+ }
+ dominated_blocks_.InsertAt(index, block, zone());
+}
+
+
+void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
+ if (dominator_ == NULL) {
+ dominator_ = other;
+ other->AddDominatedBlock(this);
+ } else if (other->dominator() != NULL) {
+ HBasicBlock* first = dominator_;
+ HBasicBlock* second = other;
+
+ while (first != second) {
+ if (first->block_id() > second->block_id()) {
+ first = first->dominator();
+ } else {
+ second = second->dominator();
+ }
+ ASSERT(first != NULL && second != NULL);
+ }
+
+ if (dominator_ != first) {
+ ASSERT(dominator_->dominated_blocks_.Contains(this));
+ dominator_->dominated_blocks_.RemoveElement(this);
+ dominator_ = first;
+ first->AddDominatedBlock(this);
+ }
+ }
+}
+
+
+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;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+#ifdef DEBUG
+void HBasicBlock::Verify() {
+ // Check that every block is finished.
+ ASSERT(IsFinished());
+ ASSERT(block_id() >= 0);
+
+ // Check that the incoming edges are in edge split form.
+ if (predecessors_.length() > 1) {
+ for (int i = 0; i < predecessors_.length(); ++i) {
+ ASSERT(predecessors_[i]->end()->SecondSuccessor() == NULL);
+ }
+ }
+}
+#endif
+
+
+void HLoopInformation::RegisterBackEdge(HBasicBlock* block) {
+ this->back_edges_.Add(block, block->zone());
+ AddBlock(block);
+}
+
+
+HBasicBlock* HLoopInformation::GetLastBackEdge() const {
+ int max_id = -1;
+ HBasicBlock* result = NULL;
+ for (int i = 0; i < back_edges_.length(); ++i) {
+ HBasicBlock* cur = back_edges_[i];
+ if (cur->block_id() > max_id) {
+ max_id = cur->block_id();
+ result = cur;
+ }
+ }
+ return result;
+}
+
+
+void HLoopInformation::AddBlock(HBasicBlock* block) {
+ if (block == loop_header()) return;
+ if (block->parent_loop_header() == loop_header()) return;
+ if (block->parent_loop_header() != NULL) {
+ AddBlock(block->parent_loop_header());
+ } else {
+ block->set_parent_loop_header(loop_header());
+ blocks_.Add(block, block->zone());
+ for (int i = 0; i < block->predecessors()->length(); ++i) {
+ AddBlock(block->predecessors()->at(i));
+ }
+ }
+}
+
+
+#ifdef DEBUG
+
+// Checks reachability of the blocks in this graph and stores a bit in
+// the BitVector "reachable()" for every block that can be reached
+// from the start block of the graph. If "dont_visit" is non-null, the given
+// block is treated as if it would not be part of the graph. "visited_count()"
+// returns the number of reachable blocks.
+class ReachabilityAnalyzer BASE_EMBEDDED {
+ public:
+ ReachabilityAnalyzer(HBasicBlock* entry_block,
+ int block_count,
+ HBasicBlock* dont_visit)
+ : visited_count_(0),
+ stack_(16, entry_block->zone()),
+ reachable_(block_count, entry_block->zone()),
+ dont_visit_(dont_visit) {
+ PushBlock(entry_block);
+ Analyze();
+ }
+
+ int visited_count() const { return visited_count_; }
+ const BitVector* reachable() const { return &reachable_; }
+
+ private:
+ void PushBlock(HBasicBlock* block) {
+ if (block != NULL && block != dont_visit_ &&
+ !reachable_.Contains(block->block_id())) {
+ reachable_.Add(block->block_id());
+ stack_.Add(block, block->zone());
+ visited_count_++;
+ }
+ }
+
+ void Analyze() {
+ while (!stack_.is_empty()) {
+ HControlInstruction* end = stack_.RemoveLast()->end();
+ for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
+ PushBlock(it.Current());
+ }
+ }
+ }
+
+ int visited_count_;
+ ZoneList<HBasicBlock*> stack_;
+ BitVector reachable_;
+ HBasicBlock* dont_visit_;
+};
+
+
+void HGraph::Verify(bool do_full_verify) const {
+ Heap::RelocationLock relocation_lock(isolate()->heap());
+ AllowHandleDereference allow_deref;
+ AllowDeferredHandleDereference allow_deferred_deref;
+ for (int i = 0; i < blocks_.length(); i++) {
+ HBasicBlock* block = blocks_.at(i);
+
+ block->Verify();
+
+ // Check that every block contains at least one node and that only the last
+ // node is a control instruction.
+ HInstruction* current = block->first();
+ ASSERT(current != NULL && current->IsBlockEntry());
+ while (current != NULL) {
+ ASSERT((current->next() == NULL) == current->IsControlInstruction());
+ ASSERT(current->block() == block);
+ current->Verify();
+ current = current->next();
+ }
+
+ // Check that successors are correctly set.
+ HBasicBlock* first = block->end()->FirstSuccessor();
+ HBasicBlock* second = block->end()->SecondSuccessor();
+ ASSERT(second == NULL || first != NULL);
+
+ // Check that the predecessor array is correct.
+ if (first != NULL) {
+ ASSERT(first->predecessors()->Contains(block));
+ if (second != NULL) {
+ ASSERT(second->predecessors()->Contains(block));
+ }
+ }
+
+ // Check that phis have correct arguments.
+ for (int j = 0; j < block->phis()->length(); j++) {
+ HPhi* phi = block->phis()->at(j);
+ phi->Verify();
+ }
+
+ // Check that all join blocks have predecessors that end with an
+ // unconditional goto and agree on their environment node id.
+ if (block->predecessors()->length() >= 2) {
+ BailoutId id =
+ block->predecessors()->first()->last_environment()->ast_id();
+ for (int k = 0; k < block->predecessors()->length(); k++) {
+ HBasicBlock* predecessor = block->predecessors()->at(k);
+ ASSERT(predecessor->end()->IsGoto());
+ ASSERT(predecessor->last_environment()->ast_id() == id);
+ }
+ }
+ }
+
+ // Check special property of first block to have no predecessors.
+ ASSERT(blocks_.at(0)->predecessors()->is_empty());
+
+ 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 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()));
+ }
+ }
+ }
+}
+
+#endif
+
+
+HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer,
+ int32_t value) {
+ if (!pointer->is_set()) {
+ // Can't pass GetInvalidContext() to HConstant::New, because that will
+ // recursively call GetConstant
+ HConstant* constant = HConstant::New(zone(), NULL, value);
+ constant->InsertAfter(GetConstantUndefined());
+ pointer->set(constant);
+ }
+ return pointer->get();
+}
+
+
+HConstant* HGraph::GetConstant0() {
+ return GetConstant(&constant_0_, 0);
+}
+
+
+HConstant* HGraph::GetConstant1() {
+ return GetConstant(&constant_1_, 1);
+}
+
+
+HConstant* HGraph::GetConstantMinus1() {
+ return GetConstant(&constant_minus1_, -1);
+}
+
+
+#define DEFINE_GET_CONSTANT(Name, name, htype, boolean_value) \
+HConstant* HGraph::GetConstant##Name() { \
+ if (!constant_##name##_.is_set()) { \
+ HConstant* constant = new(zone()) HConstant( \
+ isolate()->factory()->name##_value(), \
+ UniqueValueId(isolate()->heap()->name##_value()), \
+ Representation::Tagged(), \
+ htype, \
+ false, \
+ true, \
+ false, \
+ boolean_value); \
+ constant->InsertAfter(GetConstantUndefined()); \
+ constant_##name##_.set(constant); \
+ } \
+ return constant_##name##_.get(); \
+}
+
+
+DEFINE_GET_CONSTANT(True, true, HType::Boolean(), true)
+DEFINE_GET_CONSTANT(False, false, HType::Boolean(), false)
+DEFINE_GET_CONSTANT(Hole, the_hole, HType::Tagged(), false)
+DEFINE_GET_CONSTANT(Null, null, HType::Tagged(), false)
+
+
+#undef DEFINE_GET_CONSTANT
+
+
+HConstant* HGraph::GetInvalidContext() {
+ return GetConstant(&constant_invalid_context_, 0xFFFFC0C7);
+}
+
+
+bool HGraph::IsStandardConstant(HConstant* constant) {
+ if (constant == GetConstantUndefined()) return true;
+ if (constant == GetConstant0()) return true;
+ if (constant == GetConstant1()) return true;
+ if (constant == GetConstantMinus1()) return true;
+ if (constant == GetConstantTrue()) return true;
+ if (constant == GetConstantFalse()) return true;
+ if (constant == GetConstantHole()) return true;
+ if (constant == GetConstantNull()) return true;
+ return false;
+}
+
+
+HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder, int position)
+ : builder_(builder),
+ position_(position),
+ finished_(false),
+ deopt_then_(false),
+ deopt_else_(false),
+ did_then_(false),
+ did_else_(false),
+ did_and_(false),
+ did_or_(false),
+ captured_(false),
+ needs_compare_(true),
+ split_edge_merge_block_(NULL),
+ merge_block_(NULL) {
+ HEnvironment* env = builder->environment();
+ first_true_block_ = builder->CreateBasicBlock(env->Copy());
+ last_true_block_ = NULL;
+ first_false_block_ = builder->CreateBasicBlock(env->Copy());
+}
+
+
+HGraphBuilder::IfBuilder::IfBuilder(
+ HGraphBuilder* builder,
+ HIfContinuation* continuation)
+ : builder_(builder),
+ position_(RelocInfo::kNoPosition),
+ finished_(false),
+ deopt_then_(false),
+ deopt_else_(false),
+ did_then_(false),
+ did_else_(false),
+ did_and_(false),
+ did_or_(false),
+ captured_(false),
+ needs_compare_(false),
+ first_true_block_(NULL),
+ first_false_block_(NULL),
+ split_edge_merge_block_(NULL),
+ merge_block_(NULL) {
+ continuation->Continue(&first_true_block_,
+ &first_false_block_,
+ &position_);
+}
+
+
+void HGraphBuilder::IfBuilder::AddCompare(HControlInstruction* compare) {
+ if (split_edge_merge_block_ != NULL) {
+ HEnvironment* env = first_false_block_->last_environment();
+ HBasicBlock* split_edge =
+ builder_->CreateBasicBlock(env->Copy());
+ if (did_or_) {
+ compare->SetSuccessorAt(0, split_edge);
+ compare->SetSuccessorAt(1, first_false_block_);
+ } else {
+ compare->SetSuccessorAt(0, first_true_block_);
+ compare->SetSuccessorAt(1, split_edge);
+ }
+ split_edge->GotoNoSimulate(split_edge_merge_block_);
+ } else {
+ compare->SetSuccessorAt(0, first_true_block_);
+ compare->SetSuccessorAt(1, first_false_block_);
+ }
+ builder_->current_block()->Finish(compare);
+ needs_compare_ = false;
+}
+
+
+void HGraphBuilder::IfBuilder::Or() {
+ ASSERT(!did_and_);
+ did_or_ = true;
+ HEnvironment* env = first_false_block_->last_environment();
+ if (split_edge_merge_block_ == NULL) {
+ split_edge_merge_block_ =
+ builder_->CreateBasicBlock(env->Copy());
+ first_true_block_->GotoNoSimulate(split_edge_merge_block_);
+ first_true_block_ = split_edge_merge_block_;
+ }
+ builder_->set_current_block(first_false_block_);
+ first_false_block_ = builder_->CreateBasicBlock(env->Copy());
+}
+
+
+void HGraphBuilder::IfBuilder::And() {
+ ASSERT(!did_or_);
+ did_and_ = true;
+ HEnvironment* env = first_false_block_->last_environment();
+ if (split_edge_merge_block_ == NULL) {
+ split_edge_merge_block_ = builder_->CreateBasicBlock(env->Copy());
+ first_false_block_->GotoNoSimulate(split_edge_merge_block_);
+ first_false_block_ = split_edge_merge_block_;
+ }
+ builder_->set_current_block(first_true_block_);
+ first_true_block_ = builder_->CreateBasicBlock(env->Copy());
+}
+
+
+void HGraphBuilder::IfBuilder::CaptureContinuation(
+ HIfContinuation* continuation) {
+ ASSERT(!finished_);
+ ASSERT(!captured_);
+ HBasicBlock* true_block = last_true_block_ == NULL
+ ? first_true_block_
+ : last_true_block_;
+ HBasicBlock* false_block = did_else_ && (first_false_block_ != NULL)
+ ? builder_->current_block()
+ : first_false_block_;
+ continuation->Capture(true_block, false_block, position_);
+ captured_ = true;
+ End();
+}
+
+
+void HGraphBuilder::IfBuilder::Then() {
+ ASSERT(!captured_);
+ ASSERT(!finished_);
+ did_then_ = true;
+ if (needs_compare_) {
+ // Handle if's without any expressions, they jump directly to the "else"
+ // branch. However, we must pretend that the "then" branch is reachable,
+ // so that the graph builder visits it and sees any live range extending
+ // constructs within it.
+ HConstant* constant_false = builder_->graph()->GetConstantFalse();
+ ToBooleanStub::Types boolean_type = ToBooleanStub::Types();
+ boolean_type.Add(ToBooleanStub::BOOLEAN);
+ HBranch* branch =
+ new(zone()) HBranch(constant_false, boolean_type, first_true_block_,
+ first_false_block_);
+ builder_->current_block()->Finish(branch);
+ }
+ builder_->set_current_block(first_true_block_);
+}
+
+
+void HGraphBuilder::IfBuilder::Else() {
+ ASSERT(did_then_);
+ ASSERT(!captured_);
+ ASSERT(!finished_);
+ last_true_block_ = builder_->current_block();
+ ASSERT(first_true_block_ == NULL || !last_true_block_->IsFinished());
+ builder_->set_current_block(first_false_block_);
+ did_else_ = true;
+}
+
+
+void HGraphBuilder::IfBuilder::Deopt(const char* reason) {
+ ASSERT(did_then_);
+ if (did_else_) {
+ deopt_else_ = true;
+ } else {
+ deopt_then_ = true;
+ }
+ builder_->Add<HDeoptimize>(reason, Deoptimizer::EAGER);
+}
+
+
+void HGraphBuilder::IfBuilder::Return(HValue* value) {
+ HBasicBlock* block = builder_->current_block();
+ HValue* parameter_count = builder_->graph()->GetConstantMinus1();
+ block->FinishExit(builder_->New<HReturn>(value, parameter_count));
+ builder_->set_current_block(NULL);
+ if (did_else_) {
+ first_false_block_ = NULL;
+ } else {
+ first_true_block_ = NULL;
+ }
+}
+
+
+void HGraphBuilder::IfBuilder::End() {
+ if (!captured_) {
+ ASSERT(did_then_);
+ if (!did_else_) {
+ last_true_block_ = builder_->current_block();
+ }
+ if (first_true_block_ == NULL) {
+ // Return on true. Nothing to do, just continue the false block.
+ } else if (first_false_block_ == NULL) {
+ // Deopt on false. Nothing to do except switching to the true block.
+ builder_->set_current_block(last_true_block_);
+ } else {
+ merge_block_ = builder_->graph()->CreateBasicBlock();
+ ASSERT(!finished_);
+ if (!did_else_) Else();
+ ASSERT(!last_true_block_->IsFinished());
+ HBasicBlock* last_false_block = builder_->current_block();
+ ASSERT(!last_false_block->IsFinished());
+ if (deopt_then_) {
+ last_false_block->GotoNoSimulate(merge_block_);
+ builder_->PadEnvironmentForContinuation(last_true_block_,
+ merge_block_);
+ last_true_block_->GotoNoSimulate(merge_block_);
+ } else {
+ last_true_block_->GotoNoSimulate(merge_block_);
+ if (deopt_else_) {
+ builder_->PadEnvironmentForContinuation(last_false_block,
+ merge_block_);
+ }
+ last_false_block->GotoNoSimulate(merge_block_);
+ }
+ builder_->set_current_block(merge_block_);
+ }
+ }
+ finished_ = true;
+}
+
+
+HGraphBuilder::LoopBuilder::LoopBuilder(HGraphBuilder* builder,
+ HValue* context,
+ LoopBuilder::Direction direction)
+ : builder_(builder),
+ context_(context),
+ direction_(direction),
+ finished_(false) {
+ header_block_ = builder->CreateLoopHeaderBlock();
+ body_block_ = NULL;
+ exit_block_ = NULL;
+}
+
+
+HValue* HGraphBuilder::LoopBuilder::BeginBody(
+ HValue* initial,
+ HValue* terminating,
+ Token::Value token) {
+ HEnvironment* env = builder_->environment();
+ phi_ = header_block_->AddNewPhi(env->values()->length());
+ phi_->AddInput(initial);
+ env->Push(initial);
+ builder_->current_block()->GotoNoSimulate(header_block_);
+
+ HEnvironment* body_env = env->Copy();
+ HEnvironment* exit_env = env->Copy();
+ body_block_ = builder_->CreateBasicBlock(body_env);
+ exit_block_ = builder_->CreateBasicBlock(exit_env);
+ // Remove the phi from the expression stack
+ body_env->Pop();
+
+ builder_->set_current_block(header_block_);
+ HCompareNumericAndBranch* compare =
+ new(zone()) HCompareNumericAndBranch(phi_, terminating, token);
+ compare->SetSuccessorAt(0, body_block_);
+ compare->SetSuccessorAt(1, exit_block_);
+ builder_->current_block()->Finish(compare);
+
+ builder_->set_current_block(body_block_);
+ if (direction_ == kPreIncrement || direction_ == kPreDecrement) {
+ HValue* one = builder_->graph()->GetConstant1();
+ if (direction_ == kPreIncrement) {
+ increment_ = HAdd::New(zone(), context_, phi_, one);
+ } else {
+ increment_ = HSub::New(zone(), context_, phi_, one);
+ }
+ increment_->ClearFlag(HValue::kCanOverflow);
+ builder_->AddInstruction(increment_);
+ return increment_;
+ } else {
+ return phi_;
+ }
+}
+
+
+void HGraphBuilder::LoopBuilder::EndBody() {
+ ASSERT(!finished_);
+
+ if (direction_ == kPostIncrement || direction_ == kPostDecrement) {
+ HValue* one = builder_->graph()->GetConstant1();
+ if (direction_ == kPostIncrement) {
+ increment_ = HAdd::New(zone(), context_, phi_, one);
+ } else {
+ increment_ = HSub::New(zone(), context_, phi_, one);
+ }
+ increment_->ClearFlag(HValue::kCanOverflow);
+ builder_->AddInstruction(increment_);
+ }
+
+ // Push the new increment value on the expression stack to merge into the phi.
+ builder_->environment()->Push(increment_);
+ HBasicBlock* last_block = builder_->current_block();
+ last_block->GotoNoSimulate(header_block_);
+ header_block_->loop_information()->RegisterBackEdge(last_block);
+
+ builder_->set_current_block(exit_block_);
+ // Pop the phi from the expression stack
+ builder_->environment()->Pop();
+ finished_ = true;
+}
+
+
+HGraph* HGraphBuilder::CreateGraph() {
+ graph_ = new(zone()) HGraph(info_);
+ if (FLAG_hydrogen_stats) isolate()->GetHStatistics()->Initialize(info_);
+ CompilationPhase phase("H_Block building", info_);
+ set_current_block(graph()->entry_block());
+ if (!BuildGraph()) return NULL;
+ graph()->FinalizeUniqueValueIds();
+ return graph_;
+}
+
+
+HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
+ ASSERT(current_block() != NULL);
+ current_block()->AddInstruction(instr);
+ if (graph()->IsInsideNoSideEffectsScope()) {
+ instr->SetFlag(HValue::kHasNoObservableSideEffects);
+ }
+ return instr;
+}
+
+
+void HGraphBuilder::AddIncrementCounter(StatsCounter* counter,
+ HValue* context) {
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ HValue* reference = Add<HConstant>(ExternalReference(counter));
+ HValue* old_value = Add<HLoadNamedField>(reference,
+ HObjectAccess::ForCounter());
+ HValue* new_value = Add<HAdd>(old_value, graph()->GetConstant1());
+ new_value->ClearFlag(HValue::kCanOverflow); // Ignore counter overflow
+ Add<HStoreNamedField>(reference, HObjectAccess::ForCounter(),
+ new_value);
+ }
+}
+
+
+void HGraphBuilder::AddSimulate(BailoutId id,
+ RemovableSimulate removable) {
+ ASSERT(current_block() != NULL);
+ ASSERT(!graph()->IsInsideNoSideEffectsScope());
+ current_block()->AddNewSimulate(id, removable);
+}
+
+
+HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) {
+ HBasicBlock* b = graph()->CreateBasicBlock();
+ b->SetInitialEnvironment(env);
+ return b;
+}
+
+
+HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
+ HBasicBlock* header = graph()->CreateBasicBlock();
+ HEnvironment* entry_env = environment()->CopyAsLoopHeader(header);
+ header->SetInitialEnvironment(entry_env);
+ header->AttachLoopInformation();
+ return header;
+}
+
+
+HValue* HGraphBuilder::BuildCheckHeapObject(HValue* obj) {
+ if (obj->type().IsHeapObject()) return obj;
+ return Add<HCheckHeapObject>(obj);
+}
+
+
+void HGraphBuilder::FinishExitWithHardDeoptimization(
+ const char* reason, HBasicBlock* continuation) {
+ PadEnvironmentForContinuation(current_block(), continuation);
+ Add<HDeoptimize>(reason, Deoptimizer::EAGER);
+ if (graph()->IsInsideNoSideEffectsScope()) {
+ current_block()->GotoNoSimulate(continuation);
+ } else {
+ current_block()->Goto(continuation);
+ }
+}
+
+
+void HGraphBuilder::PadEnvironmentForContinuation(
+ HBasicBlock* from,
+ HBasicBlock* continuation) {
+ if (continuation->last_environment() != NULL) {
+ // When merging from a deopt block to a continuation, resolve differences in
+ // environment by pushing constant 0 and popping extra values so that the
+ // environments match during the join. Push 0 since it has the most specific
+ // representation, and will not influence representation inference of the
+ // phi.
+ int continuation_env_length = continuation->last_environment()->length();
+ while (continuation_env_length != from->last_environment()->length()) {
+ if (continuation_env_length > from->last_environment()->length()) {
+ from->last_environment()->Push(graph()->GetConstant0());
+ } else {
+ from->last_environment()->Pop();
+ }
+ }
+ } else {
+ ASSERT(continuation->predecessors()->length() == 0);
+ }
+}
+
+
+HValue* HGraphBuilder::BuildCheckMap(HValue* obj, Handle<Map> map) {
+ return Add<HCheckMaps>(obj, map, top_info());
+}
+
+
+HValue* HGraphBuilder::BuildWrapReceiver(HValue* object, HValue* function) {
+ if (object->type().IsJSObject()) return object;
+ return Add<HWrapReceiver>(object, function);
+}
+
+
+HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object,
+ HValue* elements,
+ ElementsKind kind,
+ HValue* length,
+ HValue* key,
+ bool is_js_array) {
+ Zone* zone = this->zone();
+ IfBuilder length_checker(this);
+
+ Token::Value token = IsHoleyElementsKind(kind) ? Token::GTE : Token::EQ;
+ length_checker.If<HCompareNumericAndBranch>(key, length, token);
+
+ length_checker.Then();
+
+ HValue* current_capacity = AddLoadFixedArrayLength(elements);
+
+ IfBuilder capacity_checker(this);
+
+ capacity_checker.If<HCompareNumericAndBranch>(key, current_capacity,
+ Token::GTE);
+ capacity_checker.Then();
+
+ HValue* context = environment()->context();
+
+ HValue* max_gap = Add<HConstant>(static_cast<int32_t>(JSObject::kMaxGap));
+ HValue* max_capacity = Add<HAdd>(current_capacity, max_gap);
+ IfBuilder key_checker(this);
+ key_checker.If<HCompareNumericAndBranch>(key, max_capacity, Token::LT);
+ key_checker.Then();
+ key_checker.ElseDeopt("Key out of capacity range");
+ key_checker.End();
+
+ HValue* new_capacity = BuildNewElementsCapacity(key);
+ HValue* new_elements = BuildGrowElementsCapacity(object, elements,
+ kind, kind, length,
+ new_capacity);
+
+ environment()->Push(new_elements);
+ capacity_checker.Else();
+
+ environment()->Push(elements);
+ capacity_checker.End();
+
+ if (is_js_array) {
+ HValue* new_length = AddInstruction(
+ HAdd::New(zone, context, key, graph_->GetConstant1()));
+ new_length->ClearFlag(HValue::kCanOverflow);
+
+ Add<HStoreNamedField>(object, HObjectAccess::ForArrayLength(kind),
+ new_length);
+ }
+
+ length_checker.Else();
+ Add<HBoundsCheck>(key, length);
+
+ environment()->Push(elements);
+ length_checker.End();
+
+ return environment()->Pop();
+}
+
+
+HValue* HGraphBuilder::BuildCopyElementsOnWrite(HValue* object,
+ HValue* elements,
+ ElementsKind kind,
+ HValue* length) {
+ Factory* factory = isolate()->factory();
+
+ IfBuilder cow_checker(this);
+
+ cow_checker.If<HCompareMap>(elements, factory->fixed_cow_array_map());
+ cow_checker.Then();
+
+ HValue* capacity = AddLoadFixedArrayLength(elements);
+
+ HValue* new_elements = BuildGrowElementsCapacity(object, elements, kind,
+ kind, length, capacity);
+
+ environment()->Push(new_elements);
+
+ cow_checker.Else();
+
+ environment()->Push(elements);
+
+ cow_checker.End();
+
+ return environment()->Pop();
+}
+
+
+void HGraphBuilder::BuildTransitionElementsKind(HValue* object,
+ HValue* map,
+ ElementsKind from_kind,
+ ElementsKind to_kind,
+ bool is_jsarray) {
+ ASSERT(!IsFastHoleyElementsKind(from_kind) ||
+ IsFastHoleyElementsKind(to_kind));
+
+ if (AllocationSite::GetMode(from_kind, to_kind) == TRACK_ALLOCATION_SITE) {
+ Add<HTrapAllocationMemento>(object);
+ }
+
+ if (!IsSimpleMapChangeTransition(from_kind, to_kind)) {
+ HInstruction* elements = AddLoadElements(object, NULL);
+
+ HInstruction* empty_fixed_array = Add<HConstant>(
+ isolate()->factory()->empty_fixed_array());
+
+ IfBuilder if_builder(this);
+
+ if_builder.IfNot<HCompareObjectEqAndBranch>(elements, empty_fixed_array);
+
+ if_builder.Then();
+
+ HInstruction* elements_length = AddLoadFixedArrayLength(elements);
+
+ HInstruction* array_length = is_jsarray
+ ? Add<HLoadNamedField>(object, HObjectAccess::ForArrayLength(from_kind))
+ : elements_length;
+
+ BuildGrowElementsCapacity(object, elements, from_kind, to_kind,
+ array_length, elements_length);
+
+ if_builder.End();
+ }
+
+ Add<HStoreNamedField>(object, HObjectAccess::ForMap(), map);
+}
+
+
+HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
+ HValue* object,
+ HValue* key,
+ HValue* val,
+ HCheckMaps* mapcheck,
+ bool is_js_array,
+ ElementsKind elements_kind,
+ bool is_store,
+ LoadKeyedHoleMode load_mode,
+ KeyedAccessStoreMode store_mode) {
+ ASSERT(!IsExternalArrayElementsKind(elements_kind) || !is_js_array);
+ // No GVNFlag is necessary for ElementsKind if there is an explicit dependency
+ // on a HElementsTransition instruction. The flag can also be removed if the
+ // map to check has FAST_HOLEY_ELEMENTS, since there can be no further
+ // ElementsKind transitions. Finally, the dependency can be removed for stores
+ // for FAST_ELEMENTS, since a transition to HOLEY elements won't change the
+ // generated store code.
+ if ((elements_kind == FAST_HOLEY_ELEMENTS) ||
+ (elements_kind == FAST_ELEMENTS && is_store)) {
+ if (mapcheck != NULL) {
+ mapcheck->ClearGVNFlag(kDependsOnElementsKind);
+ }
+ }
+ bool fast_smi_only_elements = IsFastSmiElementsKind(elements_kind);
+ bool fast_elements = IsFastObjectElementsKind(elements_kind);
+ HValue* elements = AddLoadElements(object, mapcheck);
+ if (is_store && (fast_elements || fast_smi_only_elements) &&
+ store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
+ HCheckMaps* check_cow_map = Add<HCheckMaps>(
+ elements, isolate()->factory()->fixed_array_map(), top_info());
+ check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
+ }
+ HInstruction* length = NULL;
+ if (is_js_array) {
+ length = Add<HLoadNamedField>(object,
+ HObjectAccess::ForArrayLength(elements_kind), mapcheck);
+ } else {
+ length = AddLoadFixedArrayLength(elements);
+ }
+ length->set_type(HType::Smi());
+ HValue* checked_key = NULL;
+ if (IsExternalArrayElementsKind(elements_kind)) {
+ if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
+ NoObservableSideEffectsScope no_effects(this);
+ HLoadExternalArrayPointer* external_elements =
+ Add<HLoadExternalArrayPointer>(elements);
+ IfBuilder length_checker(this);
+ length_checker.If<HCompareNumericAndBranch>(key, length, Token::LT);
+ length_checker.Then();
+ IfBuilder negative_checker(this);
+ HValue* bounds_check = negative_checker.If<HCompareNumericAndBranch>(
+ key, graph()->GetConstant0(), Token::GTE);
+ negative_checker.Then();
+ HInstruction* result = AddExternalArrayElementAccess(
+ external_elements, key, val, bounds_check, elements_kind, is_store);
+ negative_checker.ElseDeopt("Negative key encountered");
+ length_checker.End();
+ return result;
+ } else {
+ ASSERT(store_mode == STANDARD_STORE);
+ checked_key = Add<HBoundsCheck>(key, length);
+ HLoadExternalArrayPointer* external_elements =
+ Add<HLoadExternalArrayPointer>(elements);
+ return AddExternalArrayElementAccess(
+ external_elements, checked_key, val,
+ mapcheck, elements_kind, is_store);
+ }
+ }
+ ASSERT(fast_smi_only_elements ||
+ fast_elements ||
+ IsFastDoubleElementsKind(elements_kind));
+
+ // In case val is stored into a fast smi array, assure that the value is a smi
+ // before manipulating the backing store. Otherwise the actual store may
+ // deopt, leaving the backing store in an invalid state.
+ if (is_store && IsFastSmiElementsKind(elements_kind) &&
+ !val->type().IsSmi()) {
+ val = Add<HForceRepresentation>(val, Representation::Smi());
+ }
+
+ if (IsGrowStoreMode(store_mode)) {
+ NoObservableSideEffectsScope no_effects(this);
+ elements = BuildCheckForCapacityGrow(object, elements, elements_kind,
+ length, key, is_js_array);
+ checked_key = key;
+ } else {
+ checked_key = Add<HBoundsCheck>(key, length);
+
+ if (is_store && (fast_elements || fast_smi_only_elements)) {
+ if (store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
+ NoObservableSideEffectsScope no_effects(this);
+
+ elements = BuildCopyElementsOnWrite(object, elements, elements_kind,
+ length);
+ } else {
+ HCheckMaps* check_cow_map = Add<HCheckMaps>(
+ elements, isolate()->factory()->fixed_array_map(),
+ top_info());
+ check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
+ }
+ }
+ }
+ return AddFastElementAccess(elements, checked_key, val, mapcheck,
+ elements_kind, is_store, load_mode, store_mode);
+}
+
+
+HValue* HGraphBuilder::BuildAllocateElements(ElementsKind kind,
+ HValue* capacity) {
+ int elements_size;
+ InstanceType instance_type;
+
+ if (IsFastDoubleElementsKind(kind)) {
+ elements_size = kDoubleSize;
+ instance_type = FIXED_DOUBLE_ARRAY_TYPE;
+ } else {
+ elements_size = kPointerSize;
+ instance_type = FIXED_ARRAY_TYPE;
+ }
+
+ HConstant* elements_size_value = Add<HConstant>(elements_size);
+ HValue* mul = Add<HMul>(capacity, elements_size_value);
+ mul->ClearFlag(HValue::kCanOverflow);
+
+ HConstant* header_size = Add<HConstant>(FixedArray::kHeaderSize);
+ HValue* total_size = Add<HAdd>(mul, header_size);
+ total_size->ClearFlag(HValue::kCanOverflow);
+
+ return Add<HAllocate>(total_size, HType::JSArray(),
+ isolate()->heap()->GetPretenureMode(), instance_type);
+}
+
+
+void HGraphBuilder::BuildInitializeElementsHeader(HValue* elements,
+ ElementsKind kind,
+ HValue* capacity) {
+ Factory* factory = isolate()->factory();
+ Handle<Map> map = IsFastDoubleElementsKind(kind)
+ ? factory->fixed_double_array_map()
+ : factory->fixed_array_map();
+
+ AddStoreMapConstant(elements, map);
+ Add<HStoreNamedField>(elements, HObjectAccess::ForFixedArrayLength(),
+ capacity);
+}
+
+
+HValue* HGraphBuilder::BuildAllocateElementsAndInitializeElementsHeader(
+ ElementsKind kind,
+ HValue* capacity) {
+ // The HForceRepresentation is to prevent possible deopt on int-smi
+ // conversion after allocation but before the new object fields are set.
+ capacity = Add<HForceRepresentation>(capacity, Representation::Smi());
+ HValue* new_elements = BuildAllocateElements(kind, capacity);
+ BuildInitializeElementsHeader(new_elements, kind, capacity);
+ return new_elements;
+}
+
+
+HInnerAllocatedObject* HGraphBuilder::BuildJSArrayHeader(HValue* array,
+ HValue* array_map,
+ AllocationSiteMode mode,
+ ElementsKind elements_kind,
+ HValue* allocation_site_payload,
+ HValue* length_field) {
+
+ Add<HStoreNamedField>(array, HObjectAccess::ForMap(), array_map);
+
+ HConstant* empty_fixed_array =
+ Add<HConstant>(isolate()->factory()->empty_fixed_array());
+
+ HObjectAccess access = HObjectAccess::ForPropertiesPointer();
+ Add<HStoreNamedField>(array, access, empty_fixed_array);
+ Add<HStoreNamedField>(array, HObjectAccess::ForArrayLength(elements_kind),
+ length_field);
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ BuildCreateAllocationMemento(array,
+ JSArray::kSize,
+ allocation_site_payload);
+ }
+
+ int elements_location = JSArray::kSize;
+ if (mode == TRACK_ALLOCATION_SITE) {
+ elements_location += AllocationMemento::kSize;
+ }
+
+ HValue* elements = Add<HInnerAllocatedObject>(array, elements_location);
+ Add<HStoreNamedField>(array, HObjectAccess::ForElementsPointer(), elements);
+ return static_cast<HInnerAllocatedObject*>(elements);
+}
+
+
+HInstruction* HGraphBuilder::AddExternalArrayElementAccess(
+ HValue* external_elements,
+ HValue* checked_key,
+ HValue* val,
+ HValue* dependency,
+ ElementsKind elements_kind,
+ bool is_store) {
+ if (is_store) {
+ ASSERT(val != NULL);
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS: {
+ val = Add<HClampToUint8>(val);
+ 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: {
+ break;
+ }
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ break;
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ return Add<HStoreKeyed>(external_elements, checked_key, val, elements_kind);
+ } else {
+ ASSERT(val == NULL);
+ HLoadKeyed* load = Add<HLoadKeyed>(external_elements,
+ checked_key,
+ dependency,
+ elements_kind);
+ if (FLAG_opt_safe_uint32_operations &&
+ elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) {
+ graph()->RecordUint32Instruction(load);
+ }
+ return load;
+ }
+}
+
+
+HInstruction* HGraphBuilder::AddFastElementAccess(
+ HValue* elements,
+ HValue* checked_key,
+ HValue* val,
+ HValue* load_dependency,
+ ElementsKind elements_kind,
+ bool is_store,
+ LoadKeyedHoleMode load_mode,
+ KeyedAccessStoreMode store_mode) {
+ if (is_store) {
+ ASSERT(val != NULL);
+ switch (elements_kind) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ return Add<HStoreKeyed>(elements, checked_key, val, elements_kind);
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+ }
+ // It's an element load (!is_store).
+ return Add<HLoadKeyed>(
+ elements, checked_key, load_dependency, elements_kind, load_mode);
+}
+
+
+HLoadNamedField* HGraphBuilder::AddLoadElements(HValue* object,
+ HValue* typecheck) {
+ return Add<HLoadNamedField>(object,
+ HObjectAccess::ForElementsPointer(),
+ typecheck);
+}
+
+
+HLoadNamedField* HGraphBuilder::AddLoadFixedArrayLength(HValue* object) {
+ return Add<HLoadNamedField>(object,
+ HObjectAccess::ForFixedArrayLength());
+}
+
+
+HValue* HGraphBuilder::BuildNewElementsCapacity(HValue* old_capacity) {
+ HValue* half_old_capacity = Add<HShr>(old_capacity, graph_->GetConstant1());
+
+ HValue* new_capacity = Add<HAdd>(half_old_capacity, old_capacity);
+ new_capacity->ClearFlag(HValue::kCanOverflow);
+
+ HValue* min_growth = Add<HConstant>(16);
+
+ new_capacity = Add<HAdd>(new_capacity, min_growth);
+ new_capacity->ClearFlag(HValue::kCanOverflow);
+
+ return new_capacity;
+}
+
+
+void HGraphBuilder::BuildNewSpaceArrayCheck(HValue* length, ElementsKind kind) {
+ Heap* heap = isolate()->heap();
+ int element_size = IsFastDoubleElementsKind(kind) ? kDoubleSize
+ : kPointerSize;
+ int max_size = heap->MaxRegularSpaceAllocationSize() / element_size;
+ max_size -= JSArray::kSize / element_size;
+ HConstant* max_size_constant = Add<HConstant>(max_size);
+ Add<HBoundsCheck>(length, max_size_constant);
+}
+
+
+HValue* HGraphBuilder::BuildGrowElementsCapacity(HValue* object,
+ HValue* elements,
+ ElementsKind kind,
+ ElementsKind new_kind,
+ HValue* length,
+ HValue* new_capacity) {
+ BuildNewSpaceArrayCheck(new_capacity, new_kind);
+
+ HValue* new_elements = BuildAllocateElementsAndInitializeElementsHeader(
+ new_kind, new_capacity);
+
+ BuildCopyElements(elements, kind,
+ new_elements, new_kind,
+ length, new_capacity);
+
+ Add<HStoreNamedField>(object, HObjectAccess::ForElementsPointer(),
+ new_elements);
+
+ return new_elements;
+}
+
+
+void HGraphBuilder::BuildFillElementsWithHole(HValue* elements,
+ ElementsKind elements_kind,
+ HValue* from,
+ HValue* to) {
+ // Fast elements kinds need to be initialized in case statements below cause
+ // a garbage collection.
+ Factory* factory = isolate()->factory();
+
+ double nan_double = FixedDoubleArray::hole_nan_as_double();
+ HValue* hole = IsFastSmiOrObjectElementsKind(elements_kind)
+ ? Add<HConstant>(factory->the_hole_value())
+ : Add<HConstant>(nan_double);
+
+ // Special loop unfolding case
+ static const int kLoopUnfoldLimit = 4;
+ bool unfold_loop = false;
+ int initial_capacity = JSArray::kPreallocatedArrayElements;
+ if (from->IsConstant() && to->IsConstant() &&
+ initial_capacity <= kLoopUnfoldLimit) {
+ HConstant* constant_from = HConstant::cast(from);
+ HConstant* constant_to = HConstant::cast(to);
+
+ if (constant_from->HasInteger32Value() &&
+ constant_from->Integer32Value() == 0 &&
+ constant_to->HasInteger32Value() &&
+ constant_to->Integer32Value() == initial_capacity) {
+ unfold_loop = true;
+ }
+ }
+
+ // Since we're about to store a hole value, the store instruction below must
+ // assume an elements kind that supports heap object values.
+ if (IsFastSmiOrObjectElementsKind(elements_kind)) {
+ elements_kind = FAST_HOLEY_ELEMENTS;
+ }
+
+ if (unfold_loop) {
+ for (int i = 0; i < initial_capacity; i++) {
+ HInstruction* key = Add<HConstant>(i);
+ Add<HStoreKeyed>(elements, key, hole, elements_kind);
+ }
+ } else {
+ LoopBuilder builder(this, context(), LoopBuilder::kPostIncrement);
+
+ HValue* key = builder.BeginBody(from, to, Token::LT);
+
+ Add<HStoreKeyed>(elements, key, hole, elements_kind);
+
+ builder.EndBody();
+ }
+}
+
+
+void HGraphBuilder::BuildCopyElements(HValue* from_elements,
+ ElementsKind from_elements_kind,
+ HValue* to_elements,
+ ElementsKind to_elements_kind,
+ HValue* length,
+ HValue* capacity) {
+ bool pre_fill_with_holes =
+ IsFastDoubleElementsKind(from_elements_kind) &&
+ IsFastObjectElementsKind(to_elements_kind);
+
+ if (pre_fill_with_holes) {
+ // If the copy might trigger a GC, make sure that the FixedArray is
+ // pre-initialized with holes to make sure that it's always in a consistent
+ // state.
+ BuildFillElementsWithHole(to_elements, to_elements_kind,
+ graph()->GetConstant0(), capacity);
+ }
+
+ LoopBuilder builder(this, context(), LoopBuilder::kPostIncrement);
+
+ HValue* key = builder.BeginBody(graph()->GetConstant0(), length, Token::LT);
+
+ HValue* element = Add<HLoadKeyed>(from_elements, key,
+ static_cast<HValue*>(NULL),
+ from_elements_kind,
+ ALLOW_RETURN_HOLE);
+
+ ElementsKind kind = (IsHoleyElementsKind(from_elements_kind) &&
+ IsFastSmiElementsKind(to_elements_kind))
+ ? FAST_HOLEY_ELEMENTS : to_elements_kind;
+
+ if (IsHoleyElementsKind(from_elements_kind) &&
+ from_elements_kind != to_elements_kind) {
+ IfBuilder if_hole(this);
+ if_hole.If<HCompareHoleAndBranch>(element);
+ if_hole.Then();
+ HConstant* hole_constant = IsFastDoubleElementsKind(to_elements_kind)
+ ? Add<HConstant>(FixedDoubleArray::hole_nan_as_double())
+ : graph()->GetConstantHole();
+ Add<HStoreKeyed>(to_elements, key, hole_constant, kind);
+ if_hole.Else();
+ HStoreKeyed* store = Add<HStoreKeyed>(to_elements, key, element, kind);
+ store->SetFlag(HValue::kAllowUndefinedAsNaN);
+ if_hole.End();
+ } else {
+ HStoreKeyed* store = Add<HStoreKeyed>(to_elements, key, element, kind);
+ store->SetFlag(HValue::kAllowUndefinedAsNaN);
+ }
+
+ builder.EndBody();
+
+ if (!pre_fill_with_holes && length != capacity) {
+ // Fill unused capacity with the hole.
+ BuildFillElementsWithHole(to_elements, to_elements_kind,
+ key, capacity);
+ }
+}
+
+
+HValue* HGraphBuilder::BuildCloneShallowArray(HValue* boilerplate,
+ HValue* allocation_site,
+ AllocationSiteMode mode,
+ ElementsKind kind,
+ int length) {
+ NoObservableSideEffectsScope no_effects(this);
+
+ // All sizes here are multiples of kPointerSize.
+ int size = JSArray::kSize;
+ if (mode == TRACK_ALLOCATION_SITE) {
+ size += AllocationMemento::kSize;
+ }
+ int elems_offset = size;
+ InstanceType instance_type = IsFastDoubleElementsKind(kind) ?
+ FIXED_DOUBLE_ARRAY_TYPE : FIXED_ARRAY_TYPE;
+ if (length > 0) {
+ size += IsFastDoubleElementsKind(kind)
+ ? FixedDoubleArray::SizeFor(length)
+ : FixedArray::SizeFor(length);
+ }
+
+ // Allocate both the JS array and the elements array in one big
+ // allocation. This avoids multiple limit checks.
+ HValue* size_in_bytes = Add<HConstant>(size);
+ HInstruction* object = Add<HAllocate>(size_in_bytes,
+ HType::JSObject(),
+ NOT_TENURED,
+ instance_type);
+
+ // Copy the JS array part.
+ for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
+ if ((i != JSArray::kElementsOffset) || (length == 0)) {
+ HObjectAccess access = HObjectAccess::ForJSArrayOffset(i);
+ Add<HStoreNamedField>(object, access,
+ Add<HLoadNamedField>(boilerplate, access));
+ }
+ }
+
+ // Create an allocation site info if requested.
+ if (mode == TRACK_ALLOCATION_SITE) {
+ BuildCreateAllocationMemento(object, JSArray::kSize, allocation_site);
+ }
+
+ if (length > 0) {
+ // Get hold of the elements array of the boilerplate and setup the
+ // elements pointer in the resulting object.
+ HValue* boilerplate_elements = AddLoadElements(boilerplate, NULL);
+ HValue* object_elements = Add<HInnerAllocatedObject>(object, elems_offset);
+ Add<HStoreNamedField>(object, HObjectAccess::ForElementsPointer(),
+ object_elements);
+
+ // Copy the elements array header.
+ for (int i = 0; i < FixedArrayBase::kHeaderSize; i += kPointerSize) {
+ HObjectAccess access = HObjectAccess::ForFixedArrayHeader(i);
+ Add<HStoreNamedField>(object_elements, access,
+ Add<HLoadNamedField>(boilerplate_elements, access));
+ }
+
+ // Copy the elements array contents.
+ // TODO(mstarzinger): Teach HGraphBuilder::BuildCopyElements to unfold
+ // copying loops with constant length up to a given boundary and use this
+ // helper here instead.
+ for (int i = 0; i < length; i++) {
+ HValue* key_constant = Add<HConstant>(i);
+ HInstruction* value = Add<HLoadKeyed>(boilerplate_elements, key_constant,
+ static_cast<HValue*>(NULL), kind);
+ Add<HStoreKeyed>(object_elements, key_constant, value, kind);
+ }
+ }
+
+ return object;
+}
+
+
+void HGraphBuilder::BuildCompareNil(
+ HValue* value,
+ Handle<Type> type,
+ int position,
+ HIfContinuation* continuation) {
+ IfBuilder if_nil(this, position);
+ bool needs_or = false;
+ if (type->Maybe(Type::Null())) {
+ if (needs_or) if_nil.Or();
+ if_nil.If<HCompareObjectEqAndBranch>(value, graph()->GetConstantNull());
+ needs_or = true;
+ }
+ if (type->Maybe(Type::Undefined())) {
+ if (needs_or) if_nil.Or();
+ if_nil.If<HCompareObjectEqAndBranch>(value,
+ graph()->GetConstantUndefined());
+ needs_or = true;
+ }
+ if (type->Maybe(Type::Undetectable())) {
+ if (needs_or) if_nil.Or();
+ if_nil.If<HIsUndetectableAndBranch>(value);
+ } else {
+ if_nil.Then();
+ if_nil.Else();
+ if (type->NumClasses() == 1) {
+ BuildCheckHeapObject(value);
+ // For ICs, the map checked below is a sentinel map that gets replaced by
+ // the monomorphic map when the code is used as a template to generate a
+ // new IC. For optimized functions, there is no sentinel map, the map
+ // emitted below is the actual monomorphic map.
+ BuildCheckMap(value, type->Classes().Current());
+ } else {
+ if_nil.Deopt("Too many undetectable types");
+ }
+ }
+
+ if_nil.CaptureContinuation(continuation);
+}
+
+
+HValue* HGraphBuilder::BuildCreateAllocationMemento(HValue* previous_object,
+ int previous_object_size,
+ HValue* alloc_site) {
+ ASSERT(alloc_site != NULL);
+ HInnerAllocatedObject* alloc_memento = Add<HInnerAllocatedObject>(
+ previous_object, previous_object_size);
+ Handle<Map> alloc_memento_map(
+ isolate()->heap()->allocation_memento_map());
+ AddStoreMapConstant(alloc_memento, alloc_memento_map);
+ HObjectAccess access = HObjectAccess::ForAllocationMementoSite();
+ Add<HStoreNamedField>(alloc_memento, access, alloc_site);
+ return alloc_memento;
+}
+
+
+HInstruction* HGraphBuilder::BuildGetNativeContext() {
+ // Get the global context, then the native context
+ HInstruction* global_object = Add<HGlobalObject>();
+ HObjectAccess access = HObjectAccess::ForJSObjectOffset(
+ GlobalObject::kNativeContextOffset);
+ return Add<HLoadNamedField>(global_object, access);
+}
+
+
+HInstruction* HGraphBuilder::BuildGetArrayFunction() {
+ HInstruction* native_context = BuildGetNativeContext();
+ HInstruction* index =
+ Add<HConstant>(static_cast<int32_t>(Context::ARRAY_FUNCTION_INDEX));
+ return Add<HLoadKeyed>(
+ native_context, index, static_cast<HValue*>(NULL), FAST_ELEMENTS);
+}
+
+
+HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder,
+ ElementsKind kind,
+ HValue* allocation_site_payload,
+ HValue* constructor_function,
+ AllocationSiteOverrideMode override_mode) :
+ builder_(builder),
+ kind_(kind),
+ allocation_site_payload_(allocation_site_payload),
+ constructor_function_(constructor_function) {
+ mode_ = override_mode == DISABLE_ALLOCATION_SITES
+ ? DONT_TRACK_ALLOCATION_SITE
+ : AllocationSite::GetMode(kind);
+}
+
+
+HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder,
+ ElementsKind kind,
+ HValue* constructor_function) :
+ builder_(builder),
+ kind_(kind),
+ mode_(DONT_TRACK_ALLOCATION_SITE),
+ allocation_site_payload_(NULL),
+ constructor_function_(constructor_function) {
+}
+
+
+HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode() {
+ if (kind_ == GetInitialFastElementsKind()) {
+ // No need for a context lookup if the kind_ matches the initial
+ // map, because we can just load the map in that case.
+ HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap();
+ return builder()->AddInstruction(
+ builder()->BuildLoadNamedField(constructor_function_, access, NULL));
+ }
+
+ HInstruction* native_context = builder()->BuildGetNativeContext();
+ HInstruction* index = builder()->Add<HConstant>(
+ static_cast<int32_t>(Context::JS_ARRAY_MAPS_INDEX));
+
+ HInstruction* map_array = builder()->Add<HLoadKeyed>(
+ native_context, index, static_cast<HValue*>(NULL), FAST_ELEMENTS);
+
+ HInstruction* kind_index = builder()->Add<HConstant>(kind_);
+
+ return builder()->Add<HLoadKeyed>(
+ map_array, kind_index, static_cast<HValue*>(NULL), FAST_ELEMENTS);
+}
+
+
+HValue* HGraphBuilder::JSArrayBuilder::EmitInternalMapCode() {
+ // Find the map near the constructor function
+ HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap();
+ return builder()->AddInstruction(
+ builder()->BuildLoadNamedField(constructor_function_, access, NULL));
+}
+
+
+HValue* HGraphBuilder::JSArrayBuilder::EstablishAllocationSize(
+ HValue* length_node) {
+ ASSERT(length_node != NULL);
+
+ int base_size = JSArray::kSize;
+ if (mode_ == TRACK_ALLOCATION_SITE) {
+ base_size += AllocationMemento::kSize;
+ }
+
+ STATIC_ASSERT(FixedDoubleArray::kHeaderSize == FixedArray::kHeaderSize);
+ base_size += FixedArray::kHeaderSize;
+
+ HInstruction* elements_size_value =
+ builder()->Add<HConstant>(elements_size());
+ HInstruction* mul = builder()->Add<HMul>(length_node, elements_size_value);
+ mul->ClearFlag(HValue::kCanOverflow);
+
+ HInstruction* base = builder()->Add<HConstant>(base_size);
+ HInstruction* total_size = builder()->Add<HAdd>(base, mul);
+ total_size->ClearFlag(HValue::kCanOverflow);
+ return total_size;
+}
+
+
+HValue* HGraphBuilder::JSArrayBuilder::EstablishEmptyArrayAllocationSize() {
+ int base_size = JSArray::kSize;
+ if (mode_ == TRACK_ALLOCATION_SITE) {
+ base_size += AllocationMemento::kSize;
+ }
+
+ base_size += IsFastDoubleElementsKind(kind_)
+ ? FixedDoubleArray::SizeFor(initial_capacity())
+ : FixedArray::SizeFor(initial_capacity());
+
+ return builder()->Add<HConstant>(base_size);
+}
+
+
+HValue* HGraphBuilder::JSArrayBuilder::AllocateEmptyArray() {
+ HValue* size_in_bytes = EstablishEmptyArrayAllocationSize();
+ HConstant* capacity = builder()->Add<HConstant>(initial_capacity());
+ return AllocateArray(size_in_bytes,
+ capacity,
+ builder()->graph()->GetConstant0(),
+ true);
+}
+
+
+HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* capacity,
+ HValue* length_field,
+ bool fill_with_hole) {
+ HValue* size_in_bytes = EstablishAllocationSize(capacity);
+ return AllocateArray(size_in_bytes, capacity, length_field, fill_with_hole);
+}
+
+
+HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes,
+ HValue* capacity,
+ HValue* length_field,
+ bool fill_with_hole) {
+ // These HForceRepresentations are because we store these as fields in the
+ // objects we construct, and an int32-to-smi HChange could deopt. Accept
+ // the deopt possibility now, before allocation occurs.
+ capacity = builder()->Add<HForceRepresentation>(capacity,
+ Representation::Smi());
+ length_field = builder()->Add<HForceRepresentation>(length_field,
+ Representation::Smi());
+ // Allocate (dealing with failure appropriately)
+ HAllocate* new_object = builder()->Add<HAllocate>(size_in_bytes,
+ HType::JSArray(), NOT_TENURED, JS_ARRAY_TYPE);
+
+ // Fill in the fields: map, properties, length
+ HValue* map;
+ if (allocation_site_payload_ == NULL) {
+ map = EmitInternalMapCode();
+ } else {
+ map = EmitMapCode();
+ }
+ elements_location_ = builder()->BuildJSArrayHeader(new_object,
+ map,
+ mode_,
+ kind_,
+ allocation_site_payload_,
+ length_field);
+
+ // Initialize the elements
+ builder()->BuildInitializeElementsHeader(elements_location_, kind_, capacity);
+
+ if (fill_with_hole) {
+ builder()->BuildFillElementsWithHole(elements_location_, kind_,
+ graph()->GetConstant0(), capacity);
+ }
+
+ return new_object;
+}
+
+
+HStoreNamedField* HGraphBuilder::AddStoreMapConstant(HValue *object,
+ Handle<Map> map) {
+ return Add<HStoreNamedField>(object, HObjectAccess::ForMap(),
+ Add<HConstant>(map));
+}
+
+
+HValue* HGraphBuilder::AddLoadJSBuiltin(Builtins::JavaScript builtin) {
+ HGlobalObject* global_object = Add<HGlobalObject>();
+ HObjectAccess access = HObjectAccess::ForJSObjectOffset(
+ GlobalObject::kBuiltinsOffset);
+ HValue* builtins = Add<HLoadNamedField>(global_object, access);
+ HObjectAccess function_access = HObjectAccess::ForJSObjectOffset(
+ JSBuiltinsObject::OffsetOfFunctionWithId(builtin));
+ return Add<HLoadNamedField>(builtins, function_access);
+}
+
+
+HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info)
+ : HGraphBuilder(info),
+ function_state_(NULL),
+ initial_function_state_(this, info, NORMAL_RETURN),
+ ast_context_(NULL),
+ break_scope_(NULL),
+ inlined_count_(0),
+ globals_(10, info->zone()),
+ inline_bailout_(false),
+ osr_(new(info->zone()) HOsrBuilder(this)) {
+ // This is not initialized in the initializer list because the
+ // constructor for the initial state relies on function_state_ == NULL
+ // to know it's the initial state.
+ function_state_= &initial_function_state_;
+ InitializeAstVisitor();
+}
+
+
+HBasicBlock* HOptimizedGraphBuilder::CreateJoin(HBasicBlock* first,
+ HBasicBlock* second,
+ BailoutId join_id) {
+ if (first == NULL) {
+ return second;
+ } else if (second == NULL) {
+ return first;
+ } else {
+ HBasicBlock* join_block = graph()->CreateBasicBlock();
+ first->Goto(join_block);
+ second->Goto(join_block);
+ join_block->SetJoinId(join_id);
+ return join_block;
+ }
+}
+
+
+HBasicBlock* HOptimizedGraphBuilder::JoinContinue(IterationStatement* statement,
+ HBasicBlock* exit_block,
+ HBasicBlock* continue_block) {
+ if (continue_block != NULL) {
+ if (exit_block != NULL) exit_block->Goto(continue_block);
+ continue_block->SetJoinId(statement->ContinueId());
+ return continue_block;
+ }
+ return exit_block;
+}
+
+
+HBasicBlock* HOptimizedGraphBuilder::CreateLoop(IterationStatement* statement,
+ HBasicBlock* loop_entry,
+ HBasicBlock* body_exit,
+ HBasicBlock* loop_successor,
+ HBasicBlock* break_block) {
+ if (body_exit != NULL) body_exit->Goto(loop_entry);
+ loop_entry->PostProcessLoopHeader(statement);
+ if (break_block != NULL) {
+ if (loop_successor != NULL) loop_successor->Goto(break_block);
+ break_block->SetJoinId(statement->ExitId());
+ return break_block;
+ }
+ return loop_successor;
+}
+
+
+void HBasicBlock::FinishExit(HControlInstruction* instruction) {
+ Finish(instruction);
+ ClearEnvironment();
+}
+
+
+HGraph::HGraph(CompilationInfo* info)
+ : isolate_(info->isolate()),
+ next_block_id_(0),
+ entry_block_(NULL),
+ blocks_(8, info->zone()),
+ values_(16, info->zone()),
+ phi_list_(NULL),
+ uint32_instructions_(NULL),
+ osr_(NULL),
+ info_(info),
+ zone_(info->zone()),
+ is_recursive_(false),
+ use_optimistic_licm_(false),
+ has_soft_deoptimize_(false),
+ depends_on_empty_array_proto_elements_(false),
+ type_change_checksum_(0),
+ maximum_environment_size_(0),
+ no_side_effects_scope_count_(0) {
+ if (info->IsStub()) {
+ HydrogenCodeStub* stub = info->code_stub();
+ CodeStubInterfaceDescriptor* descriptor =
+ stub->GetInterfaceDescriptor(isolate_);
+ start_environment_ =
+ new(zone_) HEnvironment(zone_, descriptor->environment_length());
+ } else {
+ start_environment_ =
+ new(zone_) HEnvironment(NULL, info->scope(), info->closure(), zone_);
+ }
+ start_environment_->set_ast_id(BailoutId::FunctionEntry());
+ entry_block_ = CreateBasicBlock();
+ entry_block_->SetInitialEnvironment(start_environment_);
+}
+
+
+HBasicBlock* HGraph::CreateBasicBlock() {
+ HBasicBlock* result = new(zone()) HBasicBlock(this);
+ blocks_.Add(result, zone());
+ return result;
+}
+
+
+void HGraph::FinalizeUniqueValueIds() {
+ DisallowHeapAllocation no_gc;
+ ASSERT(!isolate()->optimizing_compiler_thread()->IsOptimizerThread());
+ for (int i = 0; i < blocks()->length(); ++i) {
+ for (HInstructionIterator it(blocks()->at(i)); !it.Done(); it.Advance()) {
+ it.Current()->FinalizeUniqueValueId();
+ }
+ }
+}
+
+
+// Block ordering was implemented with two mutually recursive methods,
+// HGraph::Postorder and HGraph::PostorderLoopBlocks.
+// The recursion could lead to stack overflow so the algorithm has been
+// implemented iteratively.
+// At a high level the algorithm looks like this:
+//
+// Postorder(block, loop_header) : {
+// if (block has already been visited or is of another loop) return;
+// mark block as visited;
+// if (block is a loop header) {
+// VisitLoopMembers(block, loop_header);
+// VisitSuccessorsOfLoopHeader(block);
+// } else {
+// VisitSuccessors(block)
+// }
+// put block in result list;
+// }
+//
+// VisitLoopMembers(block, outer_loop_header) {
+// foreach (block b in block loop members) {
+// VisitSuccessorsOfLoopMember(b, outer_loop_header);
+// if (b is loop header) VisitLoopMembers(b);
+// }
+// }
+//
+// VisitSuccessorsOfLoopMember(block, outer_loop_header) {
+// foreach (block b in block successors) Postorder(b, outer_loop_header)
+// }
+//
+// VisitSuccessorsOfLoopHeader(block) {
+// foreach (block b in block successors) Postorder(b, block)
+// }
+//
+// VisitSuccessors(block, loop_header) {
+// foreach (block b in block successors) Postorder(b, loop_header)
+// }
+//
+// The ordering is started calling Postorder(entry, NULL).
+//
+// Each instance of PostorderProcessor represents the "stack frame" of the
+// recursion, and particularly keeps the state of the loop (iteration) of the
+// "Visit..." function it represents.
+// To recycle memory we keep all the frames in a double linked list but
+// this means that we cannot use constructors to initialize the frames.
+//
+class PostorderProcessor : public ZoneObject {
+ public:
+ // Back link (towards the stack bottom).
+ PostorderProcessor* parent() {return father_; }
+ // Forward link (towards the stack top).
+ PostorderProcessor* child() {return child_; }
+ HBasicBlock* block() { return block_; }
+ HLoopInformation* loop() { return loop_; }
+ HBasicBlock* loop_header() { return loop_header_; }
+
+ static PostorderProcessor* CreateEntryProcessor(Zone* zone,
+ HBasicBlock* block,
+ BitVector* visited) {
+ PostorderProcessor* result = new(zone) PostorderProcessor(NULL);
+ return result->SetupSuccessors(zone, block, NULL, visited);
+ }
+
+ PostorderProcessor* PerformStep(Zone* zone,
+ BitVector* visited,
+ ZoneList<HBasicBlock*>* order) {
+ PostorderProcessor* next =
+ PerformNonBacktrackingStep(zone, visited, order);
+ if (next != NULL) {
+ return next;
+ } else {
+ return Backtrack(zone, visited, order);
+ }
+ }
+
+ private:
+ explicit PostorderProcessor(PostorderProcessor* father)
+ : father_(father), child_(NULL), successor_iterator(NULL) { }
+
+ // Each enum value states the cycle whose state is kept by this instance.
+ enum LoopKind {
+ NONE,
+ SUCCESSORS,
+ SUCCESSORS_OF_LOOP_HEADER,
+ LOOP_MEMBERS,
+ SUCCESSORS_OF_LOOP_MEMBER
+ };
+
+ // Each "Setup..." method is like a constructor for a cycle state.
+ PostorderProcessor* SetupSuccessors(Zone* zone,
+ HBasicBlock* block,
+ HBasicBlock* loop_header,
+ BitVector* visited) {
+ if (block == NULL || visited->Contains(block->block_id()) ||
+ block->parent_loop_header() != loop_header) {
+ kind_ = NONE;
+ block_ = NULL;
+ loop_ = NULL;
+ loop_header_ = NULL;
+ return this;
+ } else {
+ block_ = block;
+ loop_ = NULL;
+ visited->Add(block->block_id());
+
+ if (block->IsLoopHeader()) {
+ kind_ = SUCCESSORS_OF_LOOP_HEADER;
+ loop_header_ = block;
+ InitializeSuccessors();
+ PostorderProcessor* result = Push(zone);
+ return result->SetupLoopMembers(zone, block, block->loop_information(),
+ loop_header);
+ } else {
+ ASSERT(block->IsFinished());
+ kind_ = SUCCESSORS;
+ loop_header_ = loop_header;
+ InitializeSuccessors();
+ return this;
+ }
+ }
+ }
+
+ PostorderProcessor* SetupLoopMembers(Zone* zone,
+ HBasicBlock* block,
+ HLoopInformation* loop,
+ HBasicBlock* loop_header) {
+ kind_ = LOOP_MEMBERS;
+ block_ = block;
+ loop_ = loop;
+ loop_header_ = loop_header;
+ InitializeLoopMembers();
+ return this;
+ }
+
+ PostorderProcessor* SetupSuccessorsOfLoopMember(
+ HBasicBlock* block,
+ HLoopInformation* loop,
+ HBasicBlock* loop_header) {
+ kind_ = SUCCESSORS_OF_LOOP_MEMBER;
+ block_ = block;
+ loop_ = loop;
+ loop_header_ = loop_header;
+ InitializeSuccessors();
+ return this;
+ }
+
+ // This method "allocates" a new stack frame.
+ PostorderProcessor* Push(Zone* zone) {
+ if (child_ == NULL) {
+ child_ = new(zone) PostorderProcessor(this);
+ }
+ return child_;
+ }
+
+ void ClosePostorder(ZoneList<HBasicBlock*>* order, Zone* zone) {
+ ASSERT(block_->end()->FirstSuccessor() == NULL ||
+ order->Contains(block_->end()->FirstSuccessor()) ||
+ block_->end()->FirstSuccessor()->IsLoopHeader());
+ ASSERT(block_->end()->SecondSuccessor() == NULL ||
+ order->Contains(block_->end()->SecondSuccessor()) ||
+ block_->end()->SecondSuccessor()->IsLoopHeader());
+ order->Add(block_, zone);
+ }
+
+ // This method is the basic block to walk up the stack.
+ PostorderProcessor* Pop(Zone* zone,
+ BitVector* visited,
+ ZoneList<HBasicBlock*>* order) {
+ switch (kind_) {
+ case SUCCESSORS:
+ case SUCCESSORS_OF_LOOP_HEADER:
+ ClosePostorder(order, zone);
+ return father_;
+ case LOOP_MEMBERS:
+ return father_;
+ case SUCCESSORS_OF_LOOP_MEMBER:
+ if (block()->IsLoopHeader() && block() != loop_->loop_header()) {
+ // In this case we need to perform a LOOP_MEMBERS cycle so we
+ // initialize it and return this instead of father.
+ return SetupLoopMembers(zone, block(),
+ block()->loop_information(), loop_header_);
+ } else {
+ return father_;
+ }
+ case NONE:
+ return father_;
+ }
+ UNREACHABLE();
+ return NULL;
+ }
+
+ // Walks up the stack.
+ PostorderProcessor* Backtrack(Zone* zone,
+ BitVector* visited,
+ ZoneList<HBasicBlock*>* order) {
+ PostorderProcessor* parent = Pop(zone, visited, order);
+ while (parent != NULL) {
+ PostorderProcessor* next =
+ parent->PerformNonBacktrackingStep(zone, visited, order);
+ if (next != NULL) {
+ return next;
+ } else {
+ parent = parent->Pop(zone, visited, order);
+ }
+ }
+ return NULL;
+ }
+
+ PostorderProcessor* PerformNonBacktrackingStep(
+ Zone* zone,
+ BitVector* visited,
+ ZoneList<HBasicBlock*>* order) {
+ HBasicBlock* next_block;
+ switch (kind_) {
+ case SUCCESSORS:
+ next_block = AdvanceSuccessors();
+ if (next_block != NULL) {
+ PostorderProcessor* result = Push(zone);
+ return result->SetupSuccessors(zone, next_block,
+ loop_header_, visited);
+ }
+ break;
+ case SUCCESSORS_OF_LOOP_HEADER:
+ next_block = AdvanceSuccessors();
+ if (next_block != NULL) {
+ PostorderProcessor* result = Push(zone);
+ return result->SetupSuccessors(zone, next_block,
+ block(), visited);
+ }
+ break;
+ case LOOP_MEMBERS:
+ next_block = AdvanceLoopMembers();
+ if (next_block != NULL) {
+ PostorderProcessor* result = Push(zone);
+ return result->SetupSuccessorsOfLoopMember(next_block,
+ loop_, loop_header_);
+ }
+ break;
+ case SUCCESSORS_OF_LOOP_MEMBER:
+ next_block = AdvanceSuccessors();
+ if (next_block != NULL) {
+ PostorderProcessor* result = Push(zone);
+ return result->SetupSuccessors(zone, next_block,
+ loop_header_, visited);
+ }
+ break;
+ case NONE:
+ return NULL;
+ }
+ return NULL;
+ }
+
+ // The following two methods implement a "foreach b in successors" cycle.
+ void InitializeSuccessors() {
+ loop_index = 0;
+ loop_length = 0;
+ successor_iterator = HSuccessorIterator(block_->end());
+ }
+
+ HBasicBlock* AdvanceSuccessors() {
+ if (!successor_iterator.Done()) {
+ HBasicBlock* result = successor_iterator.Current();
+ successor_iterator.Advance();
+ return result;
+ }
+ return NULL;
+ }
+
+ // The following two methods implement a "foreach b in loop members" cycle.
+ void InitializeLoopMembers() {
+ loop_index = 0;
+ loop_length = loop_->blocks()->length();
+ }
+
+ HBasicBlock* AdvanceLoopMembers() {
+ if (loop_index < loop_length) {
+ HBasicBlock* result = loop_->blocks()->at(loop_index);
+ loop_index++;
+ return result;
+ } else {
+ return NULL;
+ }
+ }
+
+ LoopKind kind_;
+ PostorderProcessor* father_;
+ PostorderProcessor* child_;
+ HLoopInformation* loop_;
+ HBasicBlock* block_;
+ HBasicBlock* loop_header_;
+ int loop_index;
+ int loop_length;
+ HSuccessorIterator successor_iterator;
+};
+
+
+void HGraph::OrderBlocks() {
+ CompilationPhase phase("H_Block ordering", info());
+ BitVector visited(blocks_.length(), zone());
+
+ ZoneList<HBasicBlock*> reverse_result(8, zone());
+ HBasicBlock* start = blocks_[0];
+ PostorderProcessor* postorder =
+ PostorderProcessor::CreateEntryProcessor(zone(), start, &visited);
+ while (postorder != NULL) {
+ postorder = postorder->PerformStep(zone(), &visited, &reverse_result);
+ }
+ blocks_.Rewind(0);
+ int index = 0;
+ for (int i = reverse_result.length() - 1; i >= 0; --i) {
+ HBasicBlock* b = reverse_result[i];
+ blocks_.Add(b, zone());
+ b->set_block_id(index++);
+ }
+}
+
+
+void HGraph::AssignDominators() {
+ HPhase phase("H_Assign dominators", this);
+ for (int i = 0; i < blocks_.length(); ++i) {
+ 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.
+ block->AssignCommonDominator(block->predecessors()->first());
+ block->AssignLoopSuccessorDominators();
+ } else {
+ for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
+ blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
+ }
+ }
+ }
+}
+
+
+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) {
+ HPhi* phi = blocks_[i]->phis()->at(j);
+ // We don't support phi uses of arguments for now.
+ if (phi->CheckFlag(HValue::kIsArguments)) return false;
+ }
+ }
+ return true;
+}
+
+
+bool HGraph::CheckConstPhiUses() {
+ int block_count = blocks_.length();
+ 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);
+ // Check for the hole value (from an uninitialized const).
+ for (int k = 0; k < phi->OperandCount(); k++) {
+ if (phi->OperandAt(k) == GetConstantHole()) return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+void HGraph::CollectPhis() {
+ int block_count = blocks_.length();
+ phi_list_ = new(zone()) ZoneList<HPhi*>(block_count, zone());
+ 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, zone());
+ }
+ }
+}
+
+
+// Implementation of utility class to encapsulate the translation state for
+// a (possibly inlined) function.
+FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
+ CompilationInfo* info,
+ InliningKind inlining_kind)
+ : owner_(owner),
+ compilation_info_(info),
+ call_context_(NULL),
+ inlining_kind_(inlining_kind),
+ function_return_(NULL),
+ test_context_(NULL),
+ entry_(NULL),
+ arguments_object_(NULL),
+ arguments_elements_(NULL),
+ outer_(owner->function_state()) {
+ if (outer_ != NULL) {
+ // State for an inline function.
+ if (owner->ast_context()->IsTest()) {
+ HBasicBlock* if_true = owner->graph()->CreateBasicBlock();
+ HBasicBlock* if_false = owner->graph()->CreateBasicBlock();
+ if_true->MarkAsInlineReturnTarget(owner->current_block());
+ if_false->MarkAsInlineReturnTarget(owner->current_block());
+ TestContext* outer_test_context = TestContext::cast(owner->ast_context());
+ Expression* cond = outer_test_context->condition();
+ // The AstContext constructor pushed on the context stack. This newed
+ // instance is the reason that AstContext can't be BASE_EMBEDDED.
+ test_context_ = new TestContext(owner, cond, if_true, if_false);
+ } else {
+ function_return_ = owner->graph()->CreateBasicBlock();
+ function_return()->MarkAsInlineReturnTarget(owner->current_block());
+ }
+ // Set this after possibly allocating a new TestContext above.
+ call_context_ = owner->ast_context();
+ }
+
+ // Push on the state stack.
+ owner->set_function_state(this);
+}
+
+
+FunctionState::~FunctionState() {
+ delete test_context_;
+ owner_->set_function_state(outer_);
+}
+
+
+// Implementation of utility classes to represent an expression's context in
+// the AST.
+AstContext::AstContext(HOptimizedGraphBuilder* owner, Expression::Context kind)
+ : owner_(owner),
+ kind_(kind),
+ outer_(owner->ast_context()),
+ for_typeof_(false) {
+ owner->set_ast_context(this); // Push.
+#ifdef DEBUG
+ ASSERT(owner->environment()->frame_type() == JS_FUNCTION);
+ original_length_ = owner->environment()->length();
+#endif
+}
+
+
+AstContext::~AstContext() {
+ owner_->set_ast_context(outer_); // Pop.
+}
+
+
+EffectContext::~EffectContext() {
+ ASSERT(owner()->HasStackOverflow() ||
+ owner()->current_block() == NULL ||
+ (owner()->environment()->length() == original_length_ &&
+ owner()->environment()->frame_type() == JS_FUNCTION));
+}
+
+
+ValueContext::~ValueContext() {
+ ASSERT(owner()->HasStackOverflow() ||
+ owner()->current_block() == NULL ||
+ (owner()->environment()->length() == original_length_ + 1 &&
+ owner()->environment()->frame_type() == JS_FUNCTION));
+}
+
+
+void EffectContext::ReturnValue(HValue* value) {
+ // The value is simply ignored.
+}
+
+
+void ValueContext::ReturnValue(HValue* value) {
+ // The value is tracked in the bailout environment, and communicated
+ // through the environment as the result of the expression.
+ if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
+ owner()->Bailout(kBadValueContextForArgumentsValue);
+ }
+ owner()->Push(value);
+}
+
+
+void TestContext::ReturnValue(HValue* value) {
+ BuildBranch(value);
+}
+
+
+void EffectContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
+ ASSERT(!instr->IsControlInstruction());
+ owner()->AddInstruction(instr);
+ if (instr->HasObservableSideEffects()) {
+ owner()->Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
+ }
+}
+
+
+void EffectContext::ReturnControl(HControlInstruction* instr,
+ BailoutId ast_id) {
+ 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);
+ HBasicBlock* join = owner()->CreateJoin(empty_true, empty_false, ast_id);
+ owner()->set_current_block(join);
+}
+
+
+void EffectContext::ReturnContinuation(HIfContinuation* continuation,
+ BailoutId ast_id) {
+ HBasicBlock* true_branch = NULL;
+ HBasicBlock* false_branch = NULL;
+ continuation->Continue(&true_branch, &false_branch, NULL);
+ if (!continuation->IsTrueReachable()) {
+ owner()->set_current_block(false_branch);
+ } else if (!continuation->IsFalseReachable()) {
+ owner()->set_current_block(true_branch);
+ } else {
+ HBasicBlock* join = owner()->CreateJoin(true_branch, false_branch, ast_id);
+ owner()->set_current_block(join);
+ }
+}
+
+
+void ValueContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
+ ASSERT(!instr->IsControlInstruction());
+ if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
+ return owner()->Bailout(kBadValueContextForArgumentsObjectValue);
+ }
+ owner()->AddInstruction(instr);
+ owner()->Push(instr);
+ if (instr->HasObservableSideEffects()) {
+ owner()->Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
+ }
+}
+
+
+void ValueContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
+ ASSERT(!instr->HasObservableSideEffects());
+ if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
+ return owner()->Bailout(kBadValueContextForArgumentsObjectValue);
+ }
+ HBasicBlock* materialize_false = owner()->graph()->CreateBasicBlock();
+ HBasicBlock* materialize_true = owner()->graph()->CreateBasicBlock();
+ instr->SetSuccessorAt(0, materialize_true);
+ instr->SetSuccessorAt(1, materialize_false);
+ owner()->current_block()->Finish(instr);
+ owner()->set_current_block(materialize_true);
+ owner()->Push(owner()->graph()->GetConstantTrue());
+ owner()->set_current_block(materialize_false);
+ owner()->Push(owner()->graph()->GetConstantFalse());
+ HBasicBlock* join =
+ owner()->CreateJoin(materialize_true, materialize_false, ast_id);
+ owner()->set_current_block(join);
+}
+
+
+void ValueContext::ReturnContinuation(HIfContinuation* continuation,
+ BailoutId ast_id) {
+ HBasicBlock* materialize_true = NULL;
+ HBasicBlock* materialize_false = NULL;
+ continuation->Continue(&materialize_true, &materialize_false, NULL);
+ if (continuation->IsTrueReachable()) {
+ owner()->set_current_block(materialize_true);
+ owner()->Push(owner()->graph()->GetConstantTrue());
+ owner()->set_current_block(materialize_true);
+ }
+ if (continuation->IsFalseReachable()) {
+ owner()->set_current_block(materialize_false);
+ owner()->Push(owner()->graph()->GetConstantFalse());
+ owner()->set_current_block(materialize_false);
+ }
+ if (continuation->TrueAndFalseReachable()) {
+ HBasicBlock* join =
+ owner()->CreateJoin(materialize_true, materialize_false, ast_id);
+ owner()->set_current_block(join);
+ }
+}
+
+
+void TestContext::ReturnInstruction(HInstruction* instr, BailoutId ast_id) {
+ ASSERT(!instr->IsControlInstruction());
+ HOptimizedGraphBuilder* builder = owner();
+ 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->HasObservableSideEffects()) {
+ builder->Push(instr);
+ builder->Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
+ builder->Pop();
+ }
+ BuildBranch(instr);
+}
+
+
+void TestContext::ReturnControl(HControlInstruction* instr, BailoutId ast_id) {
+ 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(), owner()->function_state());
+ empty_false->Goto(if_false(), owner()->function_state());
+ owner()->set_current_block(NULL);
+}
+
+
+void TestContext::ReturnContinuation(HIfContinuation* continuation,
+ BailoutId ast_id) {
+ HBasicBlock* true_branch = NULL;
+ HBasicBlock* false_branch = NULL;
+ continuation->Continue(&true_branch, &false_branch, NULL);
+ if (continuation->IsTrueReachable()) {
+ true_branch->Goto(if_true(), owner()->function_state());
+ }
+ if (continuation->IsFalseReachable()) {
+ false_branch->Goto(if_false(), owner()->function_state());
+ }
+ owner()->set_current_block(NULL);
+}
+
+
+void TestContext::BuildBranch(HValue* value) {
+ // We expect the graph to be in edge-split form: there is no edge that
+ // connects a branch node to a join node. We conservatively ensure that
+ // property by always adding an empty block on the outgoing edges of this
+ // branch.
+ HOptimizedGraphBuilder* builder = owner();
+ if (value != NULL && value->CheckFlag(HValue::kIsArguments)) {
+ builder->Bailout(kArgumentsObjectValueInATestContext);
+ }
+ if (value->IsConstant()) {
+ HConstant* constant_value = HConstant::cast(value);
+ if (constant_value->BooleanValue()) {
+ builder->current_block()->Goto(if_true(), builder->function_state());
+ } else {
+ builder->current_block()->Goto(if_false(), builder->function_state());
+ }
+ builder->set_current_block(NULL);
+ return;
+ }
+ HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
+ HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
+ ToBooleanStub::Types expected(condition()->to_boolean_types());
+ HBranch* test = new(zone()) HBranch(value, expected, empty_true, empty_false);
+ builder->current_block()->Finish(test);
+
+ empty_true->Goto(if_true(), builder->function_state());
+ empty_false->Goto(if_false(), builder->function_state());
+ builder->set_current_block(NULL);
+}
+
+
+// HOptimizedGraphBuilder infrastructure for bailing out and checking bailouts.
+#define CHECK_BAILOUT(call) \
+ do { \
+ call; \
+ if (HasStackOverflow()) return; \
+ } while (false)
+
+
+#define CHECK_ALIVE(call) \
+ do { \
+ call; \
+ if (HasStackOverflow() || current_block() == NULL) return; \
+ } while (false)
+
+
+#define CHECK_ALIVE_OR_RETURN(call, value) \
+ do { \
+ call; \
+ if (HasStackOverflow() || current_block() == NULL) return value; \
+ } while (false)
+
+
+void HOptimizedGraphBuilder::Bailout(BailoutReason reason) {
+ current_info()->set_bailout_reason(reason);
+ SetStackOverflow();
+}
+
+
+void HOptimizedGraphBuilder::VisitForEffect(Expression* expr) {
+ EffectContext for_effect(this);
+ Visit(expr);
+}
+
+
+void HOptimizedGraphBuilder::VisitForValue(Expression* expr,
+ ArgumentsAllowedFlag flag) {
+ ValueContext for_value(this, flag);
+ Visit(expr);
+}
+
+
+void HOptimizedGraphBuilder::VisitForTypeOf(Expression* expr) {
+ ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
+ for_value.set_for_typeof(true);
+ Visit(expr);
+}
+
+
+
+void HOptimizedGraphBuilder::VisitForControl(Expression* expr,
+ HBasicBlock* true_block,
+ HBasicBlock* false_block) {
+ TestContext for_test(this, expr, true_block, false_block);
+ Visit(expr);
+}
+
+
+void HOptimizedGraphBuilder::VisitArgument(Expression* expr) {
+ CHECK_ALIVE(VisitForValue(expr));
+ Push(Add<HPushArgument>(Pop()));
+}
+
+
+void HOptimizedGraphBuilder::VisitArgumentList(
+ ZoneList<Expression*>* arguments) {
+ for (int i = 0; i < arguments->length(); i++) {
+ CHECK_ALIVE(VisitArgument(arguments->at(i)));
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitExpressions(
+ ZoneList<Expression*>* exprs) {
+ for (int i = 0; i < exprs->length(); ++i) {
+ CHECK_ALIVE(VisitForValue(exprs->at(i)));
+ }
+}
+
+
+bool HOptimizedGraphBuilder::BuildGraph() {
+ if (current_info()->function()->is_generator()) {
+ Bailout(kFunctionIsAGenerator);
+ return false;
+ }
+ Scope* scope = current_info()->scope();
+ if (scope->HasIllegalRedeclaration()) {
+ Bailout(kFunctionWithIllegalRedeclaration);
+ return false;
+ }
+ if (scope->calls_eval()) {
+ Bailout(kFunctionCallsEval);
+ return false;
+ }
+ 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
+ // environment on graph entry, but it has now been mutated by the
+ // Hydrogen translation of the instructions in the start block. This
+ // environment uses values which have not been defined yet. These
+ // Hydrogen instructions will then be replayed by the Lithium
+ // translation, so they cannot have an environment effect. The edge to
+ // the body's entry block (along with some special logic for the start
+ // block in HInstruction::InsertAfter) seals the start block from
+ // getting unwanted instructions inserted.
+ //
+ // TODO(kmillikin): Fix this. Stop mutating the initial environment.
+ // Make the Hydrogen instructions in the initial block into Hydrogen
+ // values (but not instructions), present in the initial environment and
+ // not replayed by the Lithium translation.
+ HEnvironment* initial_env = environment()->CopyWithoutHistory();
+ HBasicBlock* body_entry = CreateBasicBlock(initial_env);
+ current_block()->Goto(body_entry);
+ body_entry->SetJoinId(BailoutId::FunctionEntry());
+ set_current_block(body_entry);
+
+ // Handle implicit declaration of the function name in named function
+ // expressions before other declarations.
+ if (scope->is_function_scope() && scope->function() != NULL) {
+ VisitVariableDeclaration(scope->function());
+ }
+ VisitDeclarations(scope->declarations());
+ Add<HSimulate>(BailoutId::Declarations());
+
+ HValue* context = environment()->context();
+ Add<HStackCheck>(context, HStackCheck::kFunctionEntry);
+
+ VisitStatements(current_info()->function()->body());
+ if (HasStackOverflow()) return false;
+
+ if (current_block() != NULL) {
+ Add<HReturn>(graph()->GetConstantUndefined());
+ set_current_block(NULL);
+ }
+
+ // If the checksum of the number of type info changes is the same as the
+ // last time this function was compiled, then this recompile is likely not
+ // due to missing/inadequate type feedback, but rather too aggressive
+ // optimization. Disable optimistic LICM in that case.
+ Handle<Code> unoptimized_code(current_info()->shared_info()->code());
+ ASSERT(unoptimized_code->kind() == Code::FUNCTION);
+ Handle<TypeFeedbackInfo> type_info(
+ TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info()));
+ int checksum = type_info->own_type_change_checksum();
+ int composite_checksum = graph()->update_type_change_checksum(checksum);
+ graph()->set_use_optimistic_licm(
+ !type_info->matches_inlined_type_change_checksum(composite_checksum));
+ type_info->set_inlined_type_change_checksum(composite_checksum);
+
+ // Perform any necessary OSR-specific cleanups or changes to the graph.
+ osr_->FinishGraph();
+
+ return true;
+}
+
+
+bool HGraph::Optimize(BailoutReason* bailout_reason) {
+ OrderBlocks();
+ AssignDominators();
+
+ // We need to create a HConstant "zero" now so that GVN will fold every
+ // zero-valued constant in the graph together.
+ // The constant is needed to make idef-based bounds check work: the pass
+ // evaluates relations with "zero" and that zero cannot be created after GVN.
+ GetConstant0();
+
+#ifdef DEBUG
+ // Do a full verify after building the graph and computing dominators.
+ Verify(true);
+#endif
+
+ if (FLAG_analyze_environment_liveness && maximum_environment_size() != 0) {
+ Run<HEnvironmentLivenessAnalysisPhase>();
+ }
+
+ Run<HPropagateDeoptimizingMarkPhase>();
+ if (!CheckConstPhiUses()) {
+ *bailout_reason = kUnsupportedPhiUseOfConstVariable;
+ return false;
+ }
+ Run<HRedundantPhiEliminationPhase>();
+ if (!CheckArgumentsPhiUses()) {
+ *bailout_reason = kUnsupportedPhiUseOfArguments;
+ return false;
+ }
+
+ // Remove dead code and phis
+ if (FLAG_dead_code_elimination) Run<HDeadCodeEliminationPhase>();
+
+ if (FLAG_use_escape_analysis) Run<HEscapeAnalysisPhase>();
+
+ CollectPhis();
+
+ if (has_osr()) osr()->FinishOsrValues();
+
+ Run<HInferRepresentationPhase>();
+
+ // Remove HSimulate instructions that have turned out not to be needed
+ // after all by folding them into the following HSimulate.
+ // This must happen after inferring representations.
+ Run<HMergeRemovableSimulatesPhase>();
+
+ Run<HMarkDeoptimizeOnUndefinedPhase>();
+ Run<HRepresentationChangesPhase>();
+
+ Run<HInferTypesPhase>();
+
+ // Must be performed before canonicalization to ensure that Canonicalize
+ // will not remove semantically meaningful ToInt32 operations e.g. BIT_OR with
+ // zero.
+ if (FLAG_opt_safe_uint32_operations) Run<HUint32AnalysisPhase>();
+
+ if (FLAG_use_canonicalizing) Run<HCanonicalizePhase>();
+
+ if (FLAG_use_gvn) Run<HGlobalValueNumberingPhase>();
+
+ if (FLAG_use_range) Run<HRangeAnalysisPhase>();
+
+ Run<HComputeChangeUndefinedToNaN>();
+ Run<HComputeMinusZeroChecksPhase>();
+
+ // Eliminate redundant stack checks on backwards branches.
+ Run<HStackCheckEliminationPhase>();
+
+ if (FLAG_array_bounds_checks_elimination) {
+ Run<HBoundsCheckEliminationPhase>();
+ }
+ if (FLAG_array_bounds_checks_hoisting) {
+ Run<HBoundsCheckHoistingPhase>();
+ }
+ if (FLAG_array_index_dehoisting) Run<HDehoistIndexComputationsPhase>();
+ if (FLAG_dead_code_elimination) Run<HDeadCodeEliminationPhase>();
+
+ RestoreActualValues();
+
+ return true;
+}
+
+
+void HGraph::RestoreActualValues() {
+ HPhase phase("H_Restore actual values", this);
+
+ for (int block_index = 0; block_index < blocks()->length(); block_index++) {
+ HBasicBlock* block = blocks()->at(block_index);
+
+#ifdef DEBUG
+ for (int i = 0; i < block->phis()->length(); i++) {
+ HPhi* phi = block->phis()->at(i);
+ ASSERT(phi->ActualValue() == phi);
+ }
+#endif
+
+ for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->ActualValue() != instruction) {
+ ASSERT(instruction->IsInformativeDefinition());
+ if (instruction->IsPurelyInformativeDefinition()) {
+ instruction->DeleteAndReplaceWith(instruction->RedefinedOperand());
+ } else {
+ instruction->ReplaceAllUsesWith(instruction->ActualValue());
+ }
+ }
+ }
+ }
+}
+
+
+void HGraphBuilder::PushAndAdd(HInstruction* instr) {
+ Push(instr);
+ AddInstruction(instr);
+}
+
+
+template <class Instruction>
+HInstruction* HOptimizedGraphBuilder::PreProcessCall(Instruction* call) {
+ int count = call->argument_count();
+ ZoneList<HValue*> arguments(count, zone());
+ for (int i = 0; i < count; ++i) {
+ arguments.Add(Pop(), zone());
+ }
+
+ while (!arguments.is_empty()) {
+ Add<HPushArgument>(arguments.RemoveLast());
+ }
+ return call;
+}
+
+
+void HOptimizedGraphBuilder::SetUpScope(Scope* scope) {
+ // First special is HContext.
+ HInstruction* context = Add<HContext>();
+ environment()->BindContext(context);
+
+ HConstant* undefined_constant = HConstant::cast(Add<HConstant>(
+ isolate()->factory()->undefined_value()));
+ graph()->set_undefined_constant(undefined_constant);
+
+ // Create an arguments object containing the initial parameters. Set the
+ // initial values of parameters including "this" having parameter index 0.
+ ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
+ HArgumentsObject* arguments_object =
+ New<HArgumentsObject>(environment()->parameter_count());
+ for (int i = 0; i < environment()->parameter_count(); ++i) {
+ HInstruction* parameter = Add<HParameter>(i);
+ arguments_object->AddArgument(parameter, zone());
+ environment()->Bind(i, parameter);
+ }
+ AddInstruction(arguments_object);
+ graph()->SetArgumentsObject(arguments_object);
+
+ // Initialize specials and locals to undefined.
+ for (int i = environment()->parameter_count() + 1;
+ i < environment()->length();
+ ++i) {
+ environment()->Bind(i, undefined_constant);
+ }
+
+ // Handle the arguments and arguments shadow variables specially (they do
+ // not have declarations).
+ if (scope->arguments() != NULL) {
+ if (!scope->arguments()->IsStackAllocated()) {
+ return Bailout(kContextAllocatedArguments);
+ }
+
+ environment()->Bind(scope->arguments(),
+ graph()->GetArgumentsObject());
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
+ for (int i = 0; i < statements->length(); i++) {
+ CHECK_ALIVE(Visit(statements->at(i)));
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitBlock(Block* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ if (stmt->scope() != NULL) {
+ return Bailout(kScopedBlock);
+ }
+ BreakAndContinueInfo break_info(stmt);
+ { BreakAndContinueScope push(&break_info, this);
+ CHECK_BAILOUT(VisitStatements(stmt->statements()));
+ }
+ HBasicBlock* break_block = break_info.break_block();
+ if (break_block != NULL) {
+ if (current_block() != NULL) current_block()->Goto(break_block);
+ break_block->SetJoinId(stmt->ExitId());
+ set_current_block(break_block);
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitExpressionStatement(
+ ExpressionStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ VisitForEffect(stmt->expression());
+}
+
+
+void HOptimizedGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+}
+
+
+void HOptimizedGraphBuilder::VisitIfStatement(IfStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ if (stmt->condition()->ToBooleanIsTrue()) {
+ Add<HSimulate>(stmt->ThenId());
+ Visit(stmt->then_statement());
+ } else if (stmt->condition()->ToBooleanIsFalse()) {
+ Add<HSimulate>(stmt->ElseId());
+ Visit(stmt->else_statement());
+ } else {
+ HBasicBlock* cond_true = graph()->CreateBasicBlock();
+ HBasicBlock* cond_false = graph()->CreateBasicBlock();
+ CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
+
+ if (cond_true->HasPredecessor()) {
+ cond_true->SetJoinId(stmt->ThenId());
+ set_current_block(cond_true);
+ CHECK_BAILOUT(Visit(stmt->then_statement()));
+ cond_true = current_block();
+ } else {
+ cond_true = NULL;
+ }
+
+ if (cond_false->HasPredecessor()) {
+ cond_false->SetJoinId(stmt->ElseId());
+ set_current_block(cond_false);
+ CHECK_BAILOUT(Visit(stmt->else_statement()));
+ cond_false = current_block();
+ } else {
+ cond_false = NULL;
+ }
+
+ HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->IfId());
+ set_current_block(join);
+ }
+}
+
+
+HBasicBlock* HOptimizedGraphBuilder::BreakAndContinueScope::Get(
+ BreakableStatement* stmt,
+ BreakType type,
+ int* drop_extra) {
+ *drop_extra = 0;
+ BreakAndContinueScope* current = this;
+ while (current != NULL && current->info()->target() != stmt) {
+ *drop_extra += current->info()->drop_extra();
+ current = current->next();
+ }
+ ASSERT(current != NULL); // Always found (unless stack is malformed).
+
+ if (type == BREAK) {
+ *drop_extra += current->info()->drop_extra();
+ }
+
+ HBasicBlock* block = NULL;
+ switch (type) {
+ case BREAK:
+ block = current->info()->break_block();
+ if (block == NULL) {
+ block = current->owner()->graph()->CreateBasicBlock();
+ current->info()->set_break_block(block);
+ }
+ break;
+
+ case CONTINUE:
+ block = current->info()->continue_block();
+ if (block == NULL) {
+ block = current->owner()->graph()->CreateBasicBlock();
+ current->info()->set_continue_block(block);
+ }
+ break;
+ }
+
+ return block;
+}
+
+
+void HOptimizedGraphBuilder::VisitContinueStatement(
+ ContinueStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ int drop_extra = 0;
+ HBasicBlock* continue_block = break_scope()->Get(
+ stmt->target(), BreakAndContinueScope::CONTINUE, &drop_extra);
+ Drop(drop_extra);
+ current_block()->Goto(continue_block);
+ set_current_block(NULL);
+}
+
+
+void HOptimizedGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ int drop_extra = 0;
+ HBasicBlock* break_block = break_scope()->Get(
+ stmt->target(), BreakAndContinueScope::BREAK, &drop_extra);
+ Drop(drop_extra);
+ current_block()->Goto(break_block);
+ set_current_block(NULL);
+}
+
+
+void HOptimizedGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ FunctionState* state = function_state();
+ AstContext* context = call_context();
+ if (context == NULL) {
+ // Not an inlined return, so an actual one.
+ CHECK_ALIVE(VisitForValue(stmt->expression()));
+ HValue* result = environment()->Pop();
+ Add<HReturn>(result);
+ } else if (state->inlining_kind() == CONSTRUCT_CALL_RETURN) {
+ // Return from an inlined construct call. In a test context the return value
+ // will always evaluate to true, in a value context the return value needs
+ // to be a JSObject.
+ if (context->IsTest()) {
+ TestContext* test = TestContext::cast(context);
+ CHECK_ALIVE(VisitForEffect(stmt->expression()));
+ current_block()->Goto(test->if_true(), state);
+ } else if (context->IsEffect()) {
+ CHECK_ALIVE(VisitForEffect(stmt->expression()));
+ current_block()->Goto(function_return(), state);
+ } else {
+ ASSERT(context->IsValue());
+ CHECK_ALIVE(VisitForValue(stmt->expression()));
+ HValue* return_value = Pop();
+ HValue* receiver = environment()->arguments_environment()->Lookup(0);
+ HHasInstanceTypeAndBranch* typecheck =
+ new(zone()) HHasInstanceTypeAndBranch(return_value,
+ FIRST_SPEC_OBJECT_TYPE,
+ LAST_SPEC_OBJECT_TYPE);
+ HBasicBlock* if_spec_object = graph()->CreateBasicBlock();
+ HBasicBlock* not_spec_object = graph()->CreateBasicBlock();
+ typecheck->SetSuccessorAt(0, if_spec_object);
+ typecheck->SetSuccessorAt(1, not_spec_object);
+ current_block()->Finish(typecheck);
+ if_spec_object->AddLeaveInlined(return_value, state);
+ not_spec_object->AddLeaveInlined(receiver, state);
+ }
+ } else if (state->inlining_kind() == SETTER_CALL_RETURN) {
+ // Return from an inlined setter call. The returned value is never used, the
+ // value of an assignment is always the value of the RHS of the assignment.
+ CHECK_ALIVE(VisitForEffect(stmt->expression()));
+ if (context->IsTest()) {
+ HValue* rhs = environment()->arguments_environment()->Lookup(1);
+ context->ReturnValue(rhs);
+ } else if (context->IsEffect()) {
+ current_block()->Goto(function_return(), state);
+ } else {
+ ASSERT(context->IsValue());
+ HValue* rhs = environment()->arguments_environment()->Lookup(1);
+ current_block()->AddLeaveInlined(rhs, state);
+ }
+ } else {
+ // Return from a normal inlined function. Visit the subexpression in the
+ // expression context of the call.
+ if (context->IsTest()) {
+ TestContext* test = TestContext::cast(context);
+ VisitForControl(stmt->expression(), test->if_true(), test->if_false());
+ } else if (context->IsEffect()) {
+ CHECK_ALIVE(VisitForEffect(stmt->expression()));
+ current_block()->Goto(function_return(), state);
+ } else {
+ ASSERT(context->IsValue());
+ CHECK_ALIVE(VisitForValue(stmt->expression()));
+ current_block()->AddLeaveInlined(Pop(), state);
+ }
+ }
+ set_current_block(NULL);
+}
+
+
+void HOptimizedGraphBuilder::VisitWithStatement(WithStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout(kWithStatement);
+}
+
+
+void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+
+ // We only optimize switch statements with smi-literal smi comparisons,
+ // with a bounded number of clauses.
+ const int kCaseClauseLimit = 128;
+ ZoneList<CaseClause*>* clauses = stmt->cases();
+ int clause_count = clauses->length();
+ if (clause_count > kCaseClauseLimit) {
+ return Bailout(kSwitchStatementTooManyClauses);
+ }
+
+ ASSERT(stmt->switch_type() != SwitchStatement::UNKNOWN_SWITCH);
+ if (stmt->switch_type() == SwitchStatement::GENERIC_SWITCH) {
+ return Bailout(kSwitchStatementMixedOrNonLiteralSwitchLabels);
+ }
+
+ HValue* context = environment()->context();
+
+ CHECK_ALIVE(VisitForValue(stmt->tag()));
+ Add<HSimulate>(stmt->EntryId());
+ HValue* tag_value = Pop();
+ HBasicBlock* first_test_block = current_block();
+
+ HUnaryControlInstruction* string_check = NULL;
+ HBasicBlock* not_string_block = NULL;
+
+ // Test switch's tag value if all clauses are string literals
+ if (stmt->switch_type() == SwitchStatement::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);
+ }
+
+ // 1. Build all the tests, with dangling true branches
+ BailoutId default_id = BailoutId::None();
+ for (int i = 0; i < clause_count; ++i) {
+ CaseClause* clause = clauses->at(i);
+ if (clause->is_default()) {
+ default_id = clause->EntryId();
+ continue;
+ }
+
+ // Generate a compare and branch.
+ CHECK_ALIVE(VisitForValue(clause->label()));
+ HValue* label_value = Pop();
+
+ HBasicBlock* next_test_block = graph()->CreateBasicBlock();
+ HBasicBlock* body_block = graph()->CreateBasicBlock();
+
+ HControlInstruction* compare;
+
+ if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) {
+ if (!clause->compare_type()->Is(Type::Smi())) {
+ Add<HDeoptimize>("Non-smi switch type", Deoptimizer::SOFT);
+ }
+
+ HCompareNumericAndBranch* compare_ =
+ new(zone()) HCompareNumericAndBranch(tag_value,
+ label_value,
+ Token::EQ_STRICT);
+ compare_->set_observed_input_representation(
+ Representation::Smi(), Representation::Smi());
+ 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);
+ }
+
+ // Save the current block to use for the default or to join with the
+ // exit.
+ HBasicBlock* last_block = current_block();
+
+ if (not_string_block != NULL) {
+ BailoutId join_id = !default_id.IsNone() ? default_id : stmt->ExitId();
+ last_block = CreateJoin(last_block, not_string_block, join_id);
+ }
+
+ // 2. 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) {
+ CaseClause* clause = clauses->at(i);
+
+ // Identify the block where normal (non-fall-through) control flow
+ // goes to.
+ HBasicBlock* normal_block = NULL;
+ if (clause->is_default()) {
+ if (last_block != NULL) {
+ normal_block = last_block;
+ last_block = NULL; // Cleared to indicate we've handled it.
+ }
+ } else {
+ normal_block = curr_test_block->end()->FirstSuccessor();
+ curr_test_block = curr_test_block->end()->SecondSuccessor();
+ }
+
+ // Identify a block to emit the body into.
+ if (normal_block == NULL) {
+ if (fall_through_block == NULL) {
+ // (a) Unreachable.
+ if (clause->is_default()) {
+ continue; // Might still be reachable clause bodies.
+ } else {
+ break;
+ }
+ } else {
+ // (b) Reachable only as fall through.
+ set_current_block(fall_through_block);
+ }
+ } else if (fall_through_block == NULL) {
+ // (c) Reachable only normally.
+ set_current_block(normal_block);
+ } else {
+ // (d) Reachable both ways.
+ HBasicBlock* join = CreateJoin(fall_through_block,
+ normal_block,
+ clause->EntryId());
+ set_current_block(join);
+ }
+
+ CHECK_BAILOUT(VisitStatements(clause->statements()));
+ fall_through_block = current_block();
+ }
+ }
+
+ // Create an up-to-3-way join. Use the break block if it exists since
+ // it's already a join block.
+ HBasicBlock* break_block = break_info.break_block();
+ if (break_block == NULL) {
+ set_current_block(CreateJoin(fall_through_block,
+ last_block,
+ stmt->ExitId()));
+ } else {
+ if (fall_through_block != NULL) fall_through_block->Goto(break_block);
+ if (last_block != NULL) last_block->Goto(break_block);
+ break_block->SetJoinId(stmt->ExitId());
+ set_current_block(break_block);
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitLoopBody(IterationStatement* stmt,
+ HBasicBlock* loop_entry,
+ BreakAndContinueInfo* break_info) {
+ BreakAndContinueScope push(break_info, this);
+ Add<HSimulate>(stmt->StackCheckId());
+ HValue* context = environment()->context();
+ HStackCheck* stack_check = HStackCheck::cast(Add<HStackCheck>(
+ context, HStackCheck::kBackwardsBranch));
+ ASSERT(loop_entry->IsLoopHeader());
+ loop_entry->loop_information()->set_stack_check(stack_check);
+ CHECK_BAILOUT(Visit(stmt->body()));
+}
+
+
+void HOptimizedGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ ASSERT(current_block() != NULL);
+ HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
+
+ BreakAndContinueInfo break_info(stmt);
+ CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
+ HBasicBlock* body_exit =
+ JoinContinue(stmt, current_block(), break_info.continue_block());
+ HBasicBlock* loop_successor = NULL;
+ if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
+ set_current_block(body_exit);
+ // The block for a true condition, the actual predecessor block of the
+ // back edge.
+ body_exit = graph()->CreateBasicBlock();
+ loop_successor = graph()->CreateBasicBlock();
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
+ if (body_exit->HasPredecessor()) {
+ body_exit->SetJoinId(stmt->BackEdgeId());
+ } else {
+ body_exit = NULL;
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
+ }
+ HBasicBlock* loop_exit = CreateLoop(stmt,
+ loop_entry,
+ body_exit,
+ loop_successor,
+ break_info.break_block());
+ set_current_block(loop_exit);
+}
+
+
+void HOptimizedGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ ASSERT(current_block() != NULL);
+ HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
+
+ // If the condition is constant true, do not generate a branch.
+ HBasicBlock* loop_successor = NULL;
+ if (!stmt->cond()->ToBooleanIsTrue()) {
+ HBasicBlock* body_entry = graph()->CreateBasicBlock();
+ loop_successor = graph()->CreateBasicBlock();
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
+ if (body_entry->HasPredecessor()) {
+ body_entry->SetJoinId(stmt->BodyId());
+ set_current_block(body_entry);
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
+ }
+
+ BreakAndContinueInfo break_info(stmt);
+ if (current_block() != NULL) {
+ CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
+ }
+ HBasicBlock* body_exit =
+ JoinContinue(stmt, current_block(), break_info.continue_block());
+ HBasicBlock* loop_exit = CreateLoop(stmt,
+ loop_entry,
+ body_exit,
+ loop_successor,
+ break_info.break_block());
+ set_current_block(loop_exit);
+}
+
+
+void HOptimizedGraphBuilder::VisitForStatement(ForStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ if (stmt->init() != NULL) {
+ CHECK_ALIVE(Visit(stmt->init()));
+ }
+ ASSERT(current_block() != NULL);
+ HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
+
+ HBasicBlock* loop_successor = NULL;
+ if (stmt->cond() != NULL) {
+ HBasicBlock* body_entry = graph()->CreateBasicBlock();
+ loop_successor = graph()->CreateBasicBlock();
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
+ if (body_entry->HasPredecessor()) {
+ body_entry->SetJoinId(stmt->BodyId());
+ set_current_block(body_entry);
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
+ }
+
+ BreakAndContinueInfo break_info(stmt);
+ if (current_block() != NULL) {
+ CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
+ }
+ HBasicBlock* body_exit =
+ JoinContinue(stmt, current_block(), break_info.continue_block());
+
+ if (stmt->next() != NULL && body_exit != NULL) {
+ set_current_block(body_exit);
+ CHECK_BAILOUT(Visit(stmt->next()));
+ body_exit = current_block();
+ }
+
+ HBasicBlock* loop_exit = CreateLoop(stmt,
+ loop_entry,
+ body_exit,
+ loop_successor,
+ break_info.break_block());
+ set_current_block(loop_exit);
+}
+
+
+void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+
+ if (!FLAG_optimize_for_in) {
+ return Bailout(kForInStatementOptimizationIsDisabled);
+ }
+
+ if (stmt->for_in_type() != ForInStatement::FAST_FOR_IN) {
+ return Bailout(kForInStatementIsNotFastCase);
+ }
+
+ if (!stmt->each()->IsVariableProxy() ||
+ !stmt->each()->AsVariableProxy()->var()->IsStackLocal()) {
+ return Bailout(kForInStatementWithNonLocalEachVariable);
+ }
+
+ Variable* each_var = stmt->each()->AsVariableProxy()->var();
+
+ CHECK_ALIVE(VisitForValue(stmt->enumerable()));
+ HValue* enumerable = Top(); // Leave enumerable at the top.
+
+ HInstruction* map = Add<HForInPrepareMap>(enumerable);
+ Add<HSimulate>(stmt->PrepareId());
+
+ HInstruction* array = Add<HForInCacheArray>(
+ enumerable, map, DescriptorArray::kEnumCacheBridgeCacheIndex);
+
+ HInstruction* enum_length = Add<HMapEnumLength>(map);
+
+ HInstruction* start_index = Add<HConstant>(0);
+
+ Push(map);
+ Push(array);
+ Push(enum_length);
+ Push(start_index);
+
+ HInstruction* index_cache = Add<HForInCacheArray>(
+ enumerable, map, DescriptorArray::kEnumCacheBridgeIndicesCacheIndex);
+ HForInCacheArray::cast(array)->set_index_cache(
+ HForInCacheArray::cast(index_cache));
+
+ HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
+
+ HValue* index = environment()->ExpressionStackAt(0);
+ HValue* limit = environment()->ExpressionStackAt(1);
+
+ // Check that we still have more keys.
+ HCompareNumericAndBranch* compare_index =
+ new(zone()) HCompareNumericAndBranch(index, limit, Token::LT);
+ compare_index->set_observed_input_representation(
+ Representation::Smi(), Representation::Smi());
+
+ HBasicBlock* loop_body = graph()->CreateBasicBlock();
+ HBasicBlock* loop_successor = graph()->CreateBasicBlock();
+
+ compare_index->SetSuccessorAt(0, loop_body);
+ compare_index->SetSuccessorAt(1, loop_successor);
+ current_block()->Finish(compare_index);
+
+ set_current_block(loop_successor);
+ Drop(5);
+
+ set_current_block(loop_body);
+
+ HValue* key = Add<HLoadKeyed>(
+ environment()->ExpressionStackAt(2), // Enum cache.
+ environment()->ExpressionStackAt(0), // Iteration index.
+ environment()->ExpressionStackAt(0),
+ FAST_ELEMENTS);
+
+ // Check if the expected map still matches that of the enumerable.
+ // If not just deoptimize.
+ Add<HCheckMapValue>(environment()->ExpressionStackAt(4),
+ environment()->ExpressionStackAt(3));
+
+ Bind(each_var, key);
+
+ BreakAndContinueInfo break_info(stmt, 5);
+ CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
+
+ HBasicBlock* body_exit =
+ JoinContinue(stmt, current_block(), break_info.continue_block());
+
+ if (body_exit != NULL) {
+ set_current_block(body_exit);
+
+ HValue* current_index = Pop();
+ HInstruction* new_index = New<HAdd>(current_index,
+ graph()->GetConstant1());
+ PushAndAdd(new_index);
+ body_exit = current_block();
+ }
+
+ HBasicBlock* loop_exit = CreateLoop(stmt,
+ loop_entry,
+ body_exit,
+ loop_successor,
+ break_info.break_block());
+
+ set_current_block(loop_exit);
+}
+
+
+void HOptimizedGraphBuilder::VisitForOfStatement(ForOfStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout(kForOfStatement);
+}
+
+
+void HOptimizedGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout(kTryCatchStatement);
+}
+
+
+void HOptimizedGraphBuilder::VisitTryFinallyStatement(
+ TryFinallyStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout(kTryFinallyStatement);
+}
+
+
+void HOptimizedGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout(kDebuggerStatement);
+}
+
+
+static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
+ Code* unoptimized_code, FunctionLiteral* expr) {
+ int start_position = expr->start_position();
+ for (RelocIterator it(unoptimized_code); !it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue;
+ Object* obj = rinfo->target_object();
+ if (obj->IsSharedFunctionInfo()) {
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
+ if (shared->start_position() == start_position) {
+ return Handle<SharedFunctionInfo>(shared);
+ }
+ }
+ }
+
+ return Handle<SharedFunctionInfo>();
+}
+
+
+void HOptimizedGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ Handle<SharedFunctionInfo> shared_info =
+ SearchSharedFunctionInfo(current_info()->shared_info()->code(), expr);
+ if (shared_info.is_null()) {
+ shared_info = Compiler::BuildFunctionInfo(expr, current_info()->script());
+ }
+ // We also have a stack overflow if the recursive compilation did.
+ if (HasStackOverflow()) return;
+ HValue* context = environment()->context();
+ HFunctionLiteral* instr =
+ new(zone()) HFunctionLiteral(context, shared_info, expr->pretenure());
+ return ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HOptimizedGraphBuilder::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout(kSharedFunctionInfoLiteral);
+}
+
+
+void HOptimizedGraphBuilder::VisitConditional(Conditional* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ HBasicBlock* cond_true = graph()->CreateBasicBlock();
+ HBasicBlock* cond_false = graph()->CreateBasicBlock();
+ CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
+
+ // Visit the true and false subexpressions in the same AST context as the
+ // whole expression.
+ if (cond_true->HasPredecessor()) {
+ cond_true->SetJoinId(expr->ThenId());
+ set_current_block(cond_true);
+ CHECK_BAILOUT(Visit(expr->then_expression()));
+ cond_true = current_block();
+ } else {
+ cond_true = NULL;
+ }
+
+ if (cond_false->HasPredecessor()) {
+ cond_false->SetJoinId(expr->ElseId());
+ set_current_block(cond_false);
+ CHECK_BAILOUT(Visit(expr->else_expression()));
+ cond_false = current_block();
+ } else {
+ cond_false = NULL;
+ }
+
+ if (!ast_context()->IsTest()) {
+ HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
+ set_current_block(join);
+ if (join != NULL && !ast_context()->IsEffect()) {
+ return ast_context()->ReturnValue(Pop());
+ }
+ }
+}
+
+
+HOptimizedGraphBuilder::GlobalPropertyAccess
+ HOptimizedGraphBuilder::LookupGlobalProperty(
+ Variable* var, LookupResult* lookup, bool is_store) {
+ if (var->is_this() || !current_info()->has_global_object()) {
+ return kUseGeneric;
+ }
+ Handle<GlobalObject> global(current_info()->global_object());
+ global->Lookup(*var->name(), lookup);
+ if (!lookup->IsNormal() ||
+ (is_store && lookup->IsReadOnly()) ||
+ lookup->holder() != *global) {
+ return kUseGeneric;
+ }
+
+ return kUseCell;
+}
+
+
+HValue* HOptimizedGraphBuilder::BuildContextChainWalk(Variable* var) {
+ ASSERT(var->IsContextSlot());
+ HValue* context = environment()->context();
+ int length = current_info()->scope()->ContextChainLength(var->scope());
+ while (length-- > 0) {
+ context = Add<HOuterContext>(context);
+ }
+ return context;
+}
+
+
+void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ Variable* variable = expr->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED: {
+ if (IsLexicalVariableMode(variable->mode())) {
+ // TODO(rossberg): should this be an ASSERT?
+ return Bailout(kReferenceToGlobalLexicalVariable);
+ }
+ // 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<HConstant>(constant_value);
+ return ast_context()->ReturnInstruction(instr, expr->id());
+ }
+
+ LookupResult lookup(isolate());
+ GlobalPropertyAccess type =
+ LookupGlobalProperty(variable, &lookup, false);
+
+ if (type == kUseCell &&
+ current_info()->global_object()->IsAccessCheckNeeded()) {
+ type = kUseGeneric;
+ }
+
+ if (type == kUseCell) {
+ Handle<GlobalObject> global(current_info()->global_object());
+ Handle<PropertyCell> cell(global->GetPropertyCell(&lookup));
+ if (cell->type()->IsConstant()) {
+ cell->AddDependentCompilationInfo(top_info());
+ Handle<Object> constant_object = cell->type()->AsConstant();
+ if (constant_object->IsConsString()) {
+ constant_object =
+ FlattenGetString(Handle<String>::cast(constant_object));
+ }
+ HConstant* constant = New<HConstant>(constant_object);
+ return ast_context()->ReturnInstruction(constant, expr->id());
+ } else {
+ HLoadGlobalCell* instr =
+ new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails());
+ return ast_context()->ReturnInstruction(instr, expr->id());
+ }
+ } else {
+ HValue* context = environment()->context();
+ HGlobalObject* global_object = new(zone()) HGlobalObject(context);
+ AddInstruction(global_object);
+ HLoadGlobalGeneric* instr =
+ new(zone()) HLoadGlobalGeneric(context,
+ global_object,
+ variable->name(),
+ ast_context()->is_for_typeof());
+ instr->set_position(expr->position());
+ return ast_context()->ReturnInstruction(instr, expr->id());
+ }
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL: {
+ HValue* value = LookupAndMakeLive(variable);
+ if (value == graph()->GetConstantHole()) {
+ ASSERT(IsDeclaredVariableMode(variable->mode()) &&
+ variable->mode() != VAR);
+ return Bailout(kReferenceToUninitializedVariable);
+ }
+ return ast_context()->ReturnValue(value);
+ }
+
+ case Variable::CONTEXT: {
+ HValue* context = BuildContextChainWalk(variable);
+ HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
+ return ast_context()->ReturnInstruction(instr, expr->id());
+ }
+
+ case Variable::LOOKUP:
+ return Bailout(kReferenceToAVariableWhichRequiresDynamicLookup);
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitLiteral(Literal* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ HConstant* instr = New<HConstant>(expr->value());
+ return ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HOptimizedGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ Handle<JSFunction> closure = function_state()->compilation_info()->closure();
+ Handle<FixedArray> literals(closure->literals());
+ HValue* context = environment()->context();
+
+ HRegExpLiteral* instr = new(zone()) HRegExpLiteral(context,
+ literals,
+ expr->pattern(),
+ expr->flags(),
+ expr->literal_index());
+ return ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+static void LookupInPrototypes(Handle<Map> map,
+ Handle<String> name,
+ LookupResult* lookup) {
+ while (map->prototype()->IsJSObject()) {
+ Handle<JSObject> holder(JSObject::cast(map->prototype()));
+ if (!holder->HasFastProperties()) break;
+ map = Handle<Map>(holder->map());
+ map->LookupDescriptor(*holder, *name, lookup);
+ if (lookup->IsFound()) return;
+ }
+ lookup->NotFound();
+}
+
+
+// Tries to find a JavaScript accessor of the given name in the prototype chain
+// starting at the given map. Return true iff there is one, including the
+// corresponding AccessorPair plus its holder (which could be null when the
+// accessor is found directly in the given map).
+static bool LookupAccessorPair(Handle<Map> map,
+ Handle<String> name,
+ Handle<AccessorPair>* accessors,
+ Handle<JSObject>* holder) {
+ Isolate* isolate = map->GetIsolate();
+ LookupResult lookup(isolate);
+
+ // Check for a JavaScript accessor directly in the map.
+ map->LookupDescriptor(NULL, *name, &lookup);
+ if (lookup.IsPropertyCallbacks()) {
+ Handle<Object> callback(lookup.GetValueFromMap(*map), isolate);
+ if (!callback->IsAccessorPair()) return false;
+ *accessors = Handle<AccessorPair>::cast(callback);
+ *holder = Handle<JSObject>();
+ return true;
+ }
+
+ // Everything else, e.g. a field, can't be an accessor call.
+ if (lookup.IsFound()) return false;
+
+ // Check for a JavaScript accessor somewhere in the proto chain.
+ LookupInPrototypes(map, name, &lookup);
+ if (lookup.IsPropertyCallbacks()) {
+ Handle<Object> callback(lookup.GetValue(), isolate);
+ if (!callback->IsAccessorPair()) return false;
+ *accessors = Handle<AccessorPair>::cast(callback);
+ *holder = Handle<JSObject>(lookup.holder());
+ return true;
+ }
+
+ // We haven't found a JavaScript accessor anywhere.
+ return false;
+}
+
+
+static bool LookupGetter(Handle<Map> map,
+ Handle<String> name,
+ Handle<JSFunction>* getter,
+ Handle<JSObject>* holder) {
+ Handle<AccessorPair> accessors;
+ if (LookupAccessorPair(map, name, &accessors, holder) &&
+ accessors->getter()->IsJSFunction()) {
+ *getter = Handle<JSFunction>(JSFunction::cast(accessors->getter()));
+ return true;
+ }
+ return false;
+}
+
+
+static bool LookupSetter(Handle<Map> map,
+ Handle<String> name,
+ Handle<JSFunction>* setter,
+ Handle<JSObject>* holder) {
+ Handle<AccessorPair> accessors;
+ if (LookupAccessorPair(map, name, &accessors, holder) &&
+ accessors->setter()->IsJSFunction()) {
+ *setter = Handle<JSFunction>(JSFunction::cast(accessors->setter()));
+ return true;
+ }
+ return false;
+}
+
+
+// Determines whether the given array or 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 IsFastLiteral(Handle<JSObject> boilerplate,
+ int max_depth,
+ int* max_properties,
+ int* data_size,
+ int* pointer_size) {
+ if (boilerplate->map()->is_deprecated()) {
+ Handle<Object> result = JSObject::TryMigrateInstance(boilerplate);
+ if (result->IsSmi()) return false;
+ }
+
+ ASSERT(max_depth >= 0 && *max_properties >= 0);
+ if (max_depth == 0) return false;
+
+ Isolate* isolate = boilerplate->GetIsolate();
+ Handle<FixedArrayBase> elements(boilerplate->elements());
+ if (elements->length() > 0 &&
+ elements->map() != isolate->heap()->fixed_cow_array_map()) {
+ if (boilerplate->HasFastDoubleElements()) {
+ *data_size += FixedDoubleArray::SizeFor(elements->length());
+ } else if (boilerplate->HasFastObjectElements()) {
+ Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
+ int length = elements->length();
+ for (int i = 0; i < length; i++) {
+ if ((*max_properties)-- == 0) return false;
+ Handle<Object> value(fast_elements->get(i), isolate);
+ if (value->IsJSObject()) {
+ Handle<JSObject> value_object = Handle<JSObject>::cast(value);
+ if (!IsFastLiteral(value_object,
+ max_depth - 1,
+ max_properties,
+ data_size,
+ pointer_size)) {
+ return false;
+ }
+ }
+ }
+ *pointer_size += FixedArray::SizeFor(length);
+ } else {
+ return false;
+ }
+ }
+
+ Handle<FixedArray> properties(boilerplate->properties());
+ if (properties->length() > 0) {
+ return false;
+ } else {
+ Handle<DescriptorArray> descriptors(
+ boilerplate->map()->instance_descriptors());
+ int limit = boilerplate->map()->NumberOfOwnDescriptors();
+ for (int i = 0; i < limit; i++) {
+ PropertyDetails details = descriptors->GetDetails(i);
+ if (details.type() != FIELD) continue;
+ Representation representation = details.representation();
+ int index = descriptors->GetFieldIndex(i);
+ if ((*max_properties)-- == 0) return false;
+ Handle<Object> value(boilerplate->InObjectPropertyAt(index), isolate);
+ if (value->IsJSObject()) {
+ Handle<JSObject> value_object = Handle<JSObject>::cast(value);
+ if (!IsFastLiteral(value_object,
+ max_depth - 1,
+ max_properties,
+ data_size,
+ pointer_size)) {
+ return false;
+ }
+ } else if (representation.IsDouble()) {
+ *data_size += HeapNumber::kSize;
+ }
+ }
+ }
+
+ *pointer_size += boilerplate->map()->instance_size();
+ return true;
+}
+
+
+void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ Handle<JSFunction> closure = function_state()->compilation_info()->closure();
+ HValue* context = environment()->context();
+ HInstruction* literal;
+
+ // Check whether to use fast or slow deep-copying for boilerplate.
+ int data_size = 0;
+ int pointer_size = 0;
+ int max_properties = kMaxFastLiteralProperties;
+ Handle<Object> original_boilerplate(closure->literals()->get(
+ expr->literal_index()), isolate());
+ if (original_boilerplate->IsJSObject() &&
+ IsFastLiteral(Handle<JSObject>::cast(original_boilerplate),
+ kMaxFastLiteralDepth,
+ &max_properties,
+ &data_size,
+ &pointer_size)) {
+ Handle<JSObject> original_boilerplate_object =
+ Handle<JSObject>::cast(original_boilerplate);
+ Handle<JSObject> boilerplate_object =
+ DeepCopy(original_boilerplate_object);
+
+ literal = BuildFastLiteral(context,
+ boilerplate_object,
+ original_boilerplate_object,
+ Handle<Object>::null(),
+ data_size,
+ pointer_size,
+ DONT_TRACK_ALLOCATION_SITE);
+ } else {
+ NoObservableSideEffectsScope no_effects(this);
+ Handle<FixedArray> closure_literals(closure->literals(), isolate());
+ Handle<FixedArray> constant_properties = expr->constant_properties();
+ int literal_index = expr->literal_index();
+ int flags = expr->fast_elements()
+ ? ObjectLiteral::kFastElements : ObjectLiteral::kNoFlags;
+ flags |= expr->has_function()
+ ? ObjectLiteral::kHasFunction : ObjectLiteral::kNoFlags;
+
+ Add<HPushArgument>(Add<HConstant>(closure_literals));
+ Add<HPushArgument>(Add<HConstant>(literal_index));
+ Add<HPushArgument>(Add<HConstant>(constant_properties));
+ Add<HPushArgument>(Add<HConstant>(flags));
+
+ Runtime::FunctionId function_id =
+ (expr->depth() > 1 || expr->may_store_doubles())
+ ? Runtime::kCreateObjectLiteral : Runtime::kCreateObjectLiteralShallow;
+ literal = Add<HCallRuntime>(isolate()->factory()->empty_string(),
+ Runtime::FunctionForId(function_id),
+ 4);
+ }
+
+ // The object is expected in the bailout environment during computation
+ // of the property values and is the value of the entire expression.
+ Push(literal);
+
+ expr->CalculateEmitStore(zone());
+
+ for (int i = 0; i < expr->properties()->length(); i++) {
+ ObjectLiteral::Property* property = expr->properties()->at(i);
+ if (property->IsCompileTimeValue()) continue;
+
+ Literal* key = property->key();
+ Expression* value = property->value();
+
+ switch (property->kind()) {
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+ ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
+ // Fall through.
+ case ObjectLiteral::Property::COMPUTED:
+ if (key->value()->IsInternalizedString()) {
+ if (property->emit_store()) {
+ CHECK_ALIVE(VisitForValue(value));
+ HValue* value = Pop();
+ Handle<Map> map = property->GetReceiverType();
+ Handle<String> name = property->key()->AsPropertyName();
+ HInstruction* store;
+ if (map.is_null()) {
+ // If we don't know the monomorphic type, do a generic store.
+ CHECK_ALIVE(store = BuildStoreNamedGeneric(literal, name, value));
+ } else {
+#if DEBUG
+ Handle<JSFunction> setter;
+ Handle<JSObject> holder;
+ ASSERT(!LookupSetter(map, name, &setter, &holder));
+#endif
+ CHECK_ALIVE(store = BuildStoreNamedMonomorphic(literal,
+ name,
+ value,
+ map));
+ }
+ AddInstruction(store);
+ if (store->HasObservableSideEffects()) {
+ Add<HSimulate>(key->id(), REMOVABLE_SIMULATE);
+ }
+ } else {
+ CHECK_ALIVE(VisitForEffect(value));
+ }
+ break;
+ }
+ // Fall through.
+ case ObjectLiteral::Property::PROTOTYPE:
+ case ObjectLiteral::Property::SETTER:
+ case ObjectLiteral::Property::GETTER:
+ return Bailout(kObjectLiteralWithComplexProperty);
+ default: UNREACHABLE();
+ }
+ }
+
+ if (expr->has_function()) {
+ // Return the result of the transformation to fast properties
+ // instead of the original since this operation changes the map
+ // of the object. This makes sure that the original object won't
+ // be used by other optimized code before it is transformed
+ // (e.g. because of code motion).
+ HToFastProperties* result = Add<HToFastProperties>(Pop());
+ return ast_context()->ReturnValue(result);
+ } else {
+ return ast_context()->ReturnValue(Pop());
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ ZoneList<Expression*>* subexprs = expr->values();
+ int length = subexprs->length();
+ HValue* context = environment()->context();
+ HInstruction* literal;
+
+ Handle<AllocationSite> site;
+ Handle<FixedArray> literals(environment()->closure()->literals(), isolate());
+ bool uninitialized = false;
+ Handle<Object> literals_cell(literals->get(expr->literal_index()),
+ isolate());
+ Handle<Object> raw_boilerplate;
+ if (literals_cell->IsUndefined()) {
+ uninitialized = true;
+ raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
+ isolate(), literals, expr->constant_elements());
+ if (raw_boilerplate.is_null()) {
+ return Bailout(kArrayBoilerplateCreationFailed);
+ }
+
+ site = isolate()->factory()->NewAllocationSite();
+ site->set_transition_info(*raw_boilerplate);
+ literals->set(expr->literal_index(), *site);
+
+ if (JSObject::cast(*raw_boilerplate)->elements()->map() ==
+ isolate()->heap()->fixed_cow_array_map()) {
+ isolate()->counters()->cow_arrays_created_runtime()->Increment();
+ }
+ } else {
+ ASSERT(literals_cell->IsAllocationSite());
+ site = Handle<AllocationSite>::cast(literals_cell);
+ raw_boilerplate = Handle<Object>(site->transition_info(), isolate());
+ }
+
+ ASSERT(!raw_boilerplate.is_null());
+ ASSERT(site->IsLiteralSite());
+
+ Handle<JSObject> original_boilerplate_object =
+ Handle<JSObject>::cast(raw_boilerplate);
+ ElementsKind boilerplate_elements_kind =
+ Handle<JSObject>::cast(original_boilerplate_object)->GetElementsKind();
+
+ // TODO(mvstanton): This heuristic is only a temporary solution. In the
+ // end, we want to quit creating allocation site info after a certain number
+ // of GCs for a call site.
+ AllocationSiteMode mode = AllocationSite::GetMode(
+ boilerplate_elements_kind);
+
+ // Check whether to use fast or slow deep-copying for boilerplate.
+ int data_size = 0;
+ int pointer_size = 0;
+ int max_properties = kMaxFastLiteralProperties;
+ HCheckMaps* type_check = NULL;
+ if (IsFastLiteral(original_boilerplate_object,
+ kMaxFastLiteralDepth,
+ &max_properties,
+ &data_size,
+ &pointer_size)) {
+ if (mode == TRACK_ALLOCATION_SITE) {
+ pointer_size += AllocationMemento::kSize;
+ }
+
+ Handle<JSObject> boilerplate_object = DeepCopy(original_boilerplate_object);
+ literal = BuildFastLiteral(context,
+ boilerplate_object,
+ original_boilerplate_object,
+ site,
+ data_size,
+ pointer_size,
+ mode);
+ } else {
+ NoObservableSideEffectsScope no_effects(this);
+ // Boilerplate already exists and constant elements are never accessed,
+ // pass an empty fixed array to the runtime function instead.
+ Handle<FixedArray> constants = isolate()->factory()->empty_fixed_array();
+ int literal_index = expr->literal_index();
+
+ Add<HPushArgument>(Add<HConstant>(literals));
+ Add<HPushArgument>(Add<HConstant>(literal_index));
+ Add<HPushArgument>(Add<HConstant>(constants));
+
+ Runtime::FunctionId function_id = (expr->depth() > 1)
+ ? Runtime::kCreateArrayLiteral : Runtime::kCreateArrayLiteralShallow;
+ literal = Add<HCallRuntime>(isolate()->factory()->empty_string(),
+ Runtime::FunctionForId(function_id),
+ 3);
+
+ // De-opt if elements kind changed from boilerplate_elements_kind.
+ Handle<Map> map = Handle<Map>(original_boilerplate_object->map(),
+ isolate());
+ type_check = Add<HCheckMaps>(literal, map, top_info());
+ }
+
+ // The array is expected in the bailout environment during computation
+ // of the property values and is the value of the entire expression.
+ Push(literal);
+ // The literal index is on the stack, too.
+ Push(Add<HConstant>(expr->literal_index()));
+
+ HInstruction* elements = NULL;
+
+ for (int i = 0; i < length; i++) {
+ Expression* subexpr = subexprs->at(i);
+ // If the subexpression is a literal or a simple materialized literal it
+ // is already set in the cloned array.
+ if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
+
+ CHECK_ALIVE(VisitForValue(subexpr));
+ HValue* value = Pop();
+ if (!Smi::IsValid(i)) return Bailout(kNonSmiKeyInArrayLiteral);
+
+ elements = AddLoadElements(literal, type_check);
+
+ HValue* key = Add<HConstant>(i);
+
+ switch (boilerplate_elements_kind) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS: {
+ HStoreKeyed* instr = Add<HStoreKeyed>(elements, key, value,
+ boilerplate_elements_kind);
+ instr->SetUninitialized(uninitialized);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ Add<HSimulate>(expr->GetIdForElement(i));
+ }
+
+ Drop(1); // array literal index
+ return ast_context()->ReturnValue(Pop());
+}
+
+
+// Sets the lookup result and returns true if the load/store can be inlined.
+static bool ComputeLoadStoreField(Handle<Map> type,
+ Handle<String> name,
+ LookupResult* lookup,
+ bool is_store) {
+ ASSERT(!is_store || !type->is_observed());
+ if (type->has_named_interceptor()) {
+ lookup->InterceptorResult(NULL);
+ return false;
+ }
+ // If we directly find a field, the access can be inlined.
+ type->LookupDescriptor(NULL, *name, lookup);
+ if (lookup->IsField()) return true;
+
+ // For a load, we are out of luck if there is no such field.
+ if (!is_store) return false;
+
+ // 2nd chance: A store into a non-existent field can still be inlined if we
+ // have a matching transition and some room left in the object.
+ type->LookupTransition(NULL, *name, lookup);
+ return lookup->IsTransitionToField(*type) &&
+ (type->unused_property_fields() > 0);
+}
+
+
+HCheckMaps* HOptimizedGraphBuilder::AddCheckMap(HValue* object,
+ Handle<Map> map) {
+ BuildCheckHeapObject(object);
+ return Add<HCheckMaps>(object, map, top_info());
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
+ HValue* object,
+ Handle<String> name,
+ HValue* value,
+ Handle<Map> map,
+ LookupResult* lookup) {
+ ASSERT(lookup->IsFound());
+ // If the property does not exist yet, we have to check that it wasn't made
+ // readonly or turned into a setter by some meanwhile modifications on the
+ // prototype chain.
+ if (!lookup->IsProperty() && map->prototype()->IsJSReceiver()) {
+ Object* proto = map->prototype();
+ // First check that the prototype chain isn't affected already.
+ LookupResult proto_result(isolate());
+ proto->Lookup(*name, &proto_result);
+ if (proto_result.IsProperty()) {
+ // If the inherited property could induce readonly-ness, bail out.
+ if (proto_result.IsReadOnly() || !proto_result.IsCacheable()) {
+ Bailout(kImproperObjectOnPrototypeChainForStore);
+ return NULL;
+ }
+ // We only need to check up to the preexisting property.
+ proto = proto_result.holder();
+ } else {
+ // Otherwise, find the top prototype.
+ while (proto->GetPrototype(isolate())->IsJSObject()) {
+ proto = proto->GetPrototype(isolate());
+ }
+ ASSERT(proto->GetPrototype(isolate())->IsNull());
+ }
+ ASSERT(proto->IsJSObject());
+ BuildCheckPrototypeMaps(
+ Handle<JSObject>(JSObject::cast(map->prototype())),
+ Handle<JSObject>(JSObject::cast(proto)));
+ }
+
+ HObjectAccess field_access = HObjectAccess::ForField(map, lookup, name);
+ bool transition_to_field = lookup->IsTransitionToField(*map);
+
+ HStoreNamedField *instr;
+ if (FLAG_track_double_fields && field_access.representation().IsDouble()) {
+ HObjectAccess heap_number_access =
+ field_access.WithRepresentation(Representation::Tagged());
+ if (transition_to_field) {
+ // The store requires a mutable HeapNumber to be allocated.
+ NoObservableSideEffectsScope no_side_effects(this);
+ HInstruction* heap_number_size = Add<HConstant>(HeapNumber::kSize);
+ HInstruction* heap_number = Add<HAllocate>(heap_number_size,
+ HType::HeapNumber(), isolate()->heap()->GetPretenureMode(),
+ HEAP_NUMBER_TYPE);
+ AddStoreMapConstant(heap_number, isolate()->factory()->heap_number_map());
+ Add<HStoreNamedField>(heap_number, HObjectAccess::ForHeapNumberValue(),
+ value);
+ instr = New<HStoreNamedField>(object, heap_number_access,
+ heap_number);
+ } else {
+ // Already holds a HeapNumber; load the box and write its value field.
+ HInstruction* heap_number = Add<HLoadNamedField>(object,
+ heap_number_access);
+ heap_number->set_type(HType::HeapNumber());
+ instr = New<HStoreNamedField>(heap_number,
+ HObjectAccess::ForHeapNumberValue(),
+ value);
+ }
+ } else {
+ // This is a normal store.
+ instr = New<HStoreNamedField>(object, field_access, value);
+ }
+
+ if (transition_to_field) {
+ Handle<Map> transition(lookup->GetTransitionMapFromMap(*map));
+ HConstant* transition_constant = Add<HConstant>(transition);
+ instr->SetTransition(transition_constant, top_info());
+ // TODO(fschneider): Record the new map type of the object in the IR to
+ // enable elimination of redundant checks after the transition store.
+ instr->SetGVNFlag(kChangesMaps);
+ }
+ return instr;
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildStoreNamedGeneric(
+ HValue* object,
+ Handle<String> name,
+ HValue* value) {
+ HValue* context = environment()->context();
+ return new(zone()) HStoreNamedGeneric(
+ context,
+ object,
+ name,
+ value,
+ function_strict_mode_flag());
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic(
+ HValue* object,
+ Handle<String> name,
+ HValue* value,
+ Handle<Map> map) {
+ // Handle a store to a known field.
+ LookupResult lookup(isolate());
+ if (ComputeLoadStoreField(map, name, &lookup, true)) {
+ AddCheckMap(object, map);
+ return BuildStoreNamedField(object, name, value, map, &lookup);
+ }
+
+ // No luck, do a generic store.
+ return BuildStoreNamedGeneric(object, name, value);
+}
+
+
+static bool CanLoadPropertyFromPrototype(Handle<Map> map,
+ Handle<Name> name,
+ LookupResult* lookup) {
+ if (map->has_named_interceptor()) return false;
+ if (map->is_dictionary_map()) return false;
+ map->LookupDescriptor(NULL, *name, lookup);
+ if (lookup->IsFound()) return false;
+ return true;
+}
+
+
+HInstruction* HOptimizedGraphBuilder::TryLoadPolymorphicAsMonomorphic(
+ Property* expr,
+ HValue* object,
+ SmallMapList* types,
+ Handle<String> name) {
+ // Use monomorphic load if property lookup results in the same field index
+ // for all maps. Requires special map check on the set of all handled maps.
+ if (types->length() > kMaxLoadPolymorphism) return NULL;
+
+ LookupResult lookup(isolate());
+ int count;
+ HObjectAccess access = HObjectAccess::ForMap(); // initial value unused.
+ for (count = 0; count < types->length(); ++count) {
+ Handle<Map> map = types->at(count);
+ if (!ComputeLoadStoreField(map, name, &lookup, false)) break;
+
+ HObjectAccess new_access = HObjectAccess::ForField(map, &lookup, name);
+
+ if (count == 0) {
+ // First time through the loop; set access and representation.
+ access = new_access;
+ } else if (!access.representation().IsCompatibleForLoad(
+ new_access.representation())) {
+ // Representations did not match.
+ break;
+ } else if (access.offset() != new_access.offset()) {
+ // Offsets did not match.
+ break;
+ } else if (access.IsInobject() != new_access.IsInobject()) {
+ // In-objectness did not match.
+ break;
+ }
+ access = access.WithRepresentation(
+ access.representation().generalize(new_access.representation()));
+ }
+
+ if (count == types->length()) {
+ // Everything matched; can use monomorphic load.
+ BuildCheckHeapObject(object);
+ HCheckMaps* type_check = Add<HCheckMaps>(object, types);
+ return BuildLoadNamedField(object, access, type_check);
+ }
+
+ if (count != 0) return NULL;
+
+ // Second chance: the property is on the prototype and all maps have the
+ // same prototype.
+ Handle<Map> map(types->at(0));
+ if (!CanLoadPropertyFromPrototype(map, name, &lookup)) return NULL;
+
+ Handle<Object> prototype(map->prototype(), isolate());
+ for (count = 1; count < types->length(); ++count) {
+ Handle<Map> test_map(types->at(count));
+ if (!CanLoadPropertyFromPrototype(test_map, name, &lookup)) return NULL;
+ if (test_map->prototype() != *prototype) return NULL;
+ }
+
+ LookupInPrototypes(map, name, &lookup);
+ if (!lookup.IsField()) return NULL;
+
+ BuildCheckHeapObject(object);
+ HCheckMaps* type_check = Add<HCheckMaps>(object, types);
+
+ Handle<JSObject> holder(lookup.holder());
+ Handle<Map> holder_map(holder->map());
+ BuildCheckPrototypeMaps(Handle<JSObject>::cast(prototype), holder);
+ HValue* holder_value = Add<HConstant>(holder);
+ return BuildLoadNamedField(holder_value,
+ HObjectAccess::ForField(holder_map, &lookup, name), type_check);
+}
+
+
+// Returns true if an instance of this map can never find a property with this
+// name in its prototype chain. This means all prototypes up to the top are
+// fast and don't have the name in them. It would be good if we could optimize
+// polymorphic loads where the property is sometimes found in the prototype
+// chain.
+static bool PrototypeChainCanNeverResolve(
+ Handle<Map> map, Handle<String> name) {
+ Isolate* isolate = map->GetIsolate();
+ Object* current = map->prototype();
+ while (current != isolate->heap()->null_value()) {
+ if (current->IsJSGlobalProxy() ||
+ current->IsGlobalObject() ||
+ !current->IsJSObject() ||
+ JSObject::cast(current)->map()->has_named_interceptor() ||
+ JSObject::cast(current)->IsAccessCheckNeeded() ||
+ !JSObject::cast(current)->HasFastProperties()) {
+ return false;
+ }
+
+ LookupResult lookup(isolate);
+ Map* map = JSObject::cast(current)->map();
+ map->LookupDescriptor(NULL, *name, &lookup);
+ if (lookup.IsFound()) return false;
+ if (!lookup.IsCacheable()) return false;
+ current = JSObject::cast(current)->GetPrototype();
+ }
+ return true;
+}
+
+
+void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
+ Property* expr,
+ HValue* object,
+ SmallMapList* types,
+ Handle<String> name) {
+ HInstruction* instr = TryLoadPolymorphicAsMonomorphic(
+ expr, object, types, name);
+ if (instr != NULL) {
+ instr->set_position(expr->position());
+ return ast_context()->ReturnInstruction(instr, expr->id());
+ }
+
+ // Something did not match; must use a polymorphic load.
+ int count = 0;
+ HBasicBlock* join = NULL;
+ for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
+ Handle<Map> map = types->at(i);
+ LookupResult lookup(isolate());
+ if (ComputeLoadStoreField(map, name, &lookup, false) ||
+ (lookup.IsCacheable() &&
+ !map->is_dictionary_map() &&
+ !map->has_named_interceptor() &&
+ (lookup.IsConstant() ||
+ (!lookup.IsFound() &&
+ PrototypeChainCanNeverResolve(map, name))))) {
+ if (count == 0) {
+ BuildCheckHeapObject(object);
+ join = graph()->CreateBasicBlock();
+ }
+ ++count;
+ HBasicBlock* if_true = graph()->CreateBasicBlock();
+ HBasicBlock* if_false = graph()->CreateBasicBlock();
+ HCompareMap* compare =
+ new(zone()) HCompareMap(object, map, if_true, if_false);
+ current_block()->Finish(compare);
+
+ set_current_block(if_true);
+
+ // TODO(verwaest): Merge logic with BuildLoadNamedMonomorphic.
+ if (lookup.IsField()) {
+ HObjectAccess access = HObjectAccess::ForField(map, &lookup, name);
+ HLoadNamedField* load = BuildLoadNamedField(object, access, compare);
+ load->set_position(expr->position());
+ AddInstruction(load);
+ if (!ast_context()->IsEffect()) Push(load);
+ } else if (lookup.IsConstant()) {
+ Handle<Object> constant(lookup.GetConstantFromMap(*map), isolate());
+ HConstant* hconstant = Add<HConstant>(constant);
+ if (!ast_context()->IsEffect()) Push(hconstant);
+ } else {
+ ASSERT(!lookup.IsFound());
+ if (map->prototype()->IsJSObject()) {
+ Handle<JSObject> prototype(JSObject::cast(map->prototype()));
+ Handle<JSObject> holder = prototype;
+ while (holder->map()->prototype()->IsJSObject()) {
+ holder = handle(JSObject::cast(holder->map()->prototype()));
+ }
+ BuildCheckPrototypeMaps(prototype, holder);
+ }
+ if (!ast_context()->IsEffect()) Push(graph()->GetConstantUndefined());
+ }
+
+ current_block()->Goto(join);
+ set_current_block(if_false);
+ }
+ }
+
+ // Finish up. Unconditionally deoptimize if we've handled all the maps we
+ // know about and do not want to handle ones we've never seen. Otherwise
+ // use a generic IC.
+ if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
+ FinishExitWithHardDeoptimization("Unknown map in polymorphic load", join);
+ } else {
+ HInstruction* load = BuildLoadNamedGeneric(object, name, expr);
+ load->set_position(expr->position());
+ AddInstruction(load);
+ if (!ast_context()->IsEffect()) Push(load);
+
+ if (join != NULL) {
+ current_block()->Goto(join);
+ } else {
+ Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
+ if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
+ return;
+ }
+ }
+
+ ASSERT(join != NULL);
+ join->SetJoinId(expr->id());
+ set_current_block(join);
+ if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
+}
+
+
+bool HOptimizedGraphBuilder::TryStorePolymorphicAsMonomorphic(
+ int position,
+ BailoutId assignment_id,
+ HValue* object,
+ HValue* store_value,
+ HValue* result_value,
+ SmallMapList* types,
+ Handle<String> name) {
+ // Use monomorphic store if property lookup results in the same field index
+ // for all maps. Requires special map check on the set of all handled maps.
+ if (types->length() > kMaxStorePolymorphism) return false;
+
+ // TODO(verwaest): Merge the checking logic with the code in
+ // TryLoadPolymorphicAsMonomorphic.
+ LookupResult lookup(isolate());
+ int count;
+ Representation representation = Representation::None();
+ HObjectAccess access = HObjectAccess::ForMap(); // initial value unused.
+ for (count = 0; count < types->length(); ++count) {
+ Handle<Map> map = types->at(count);
+ // Pass false to ignore transitions.
+ if (!ComputeLoadStoreField(map, name, &lookup, false)) break;
+ ASSERT(!map->is_observed());
+
+ HObjectAccess new_access = HObjectAccess::ForField(map, &lookup, name);
+ Representation new_representation = new_access.representation();
+
+ if (count == 0) {
+ // First time through the loop; set access and representation.
+ access = new_access;
+ representation = new_representation;
+ } else if (!representation.IsCompatibleForStore(new_representation)) {
+ // Representations did not match.
+ break;
+ } else if (access.offset() != new_access.offset()) {
+ // Offsets did not match.
+ break;
+ } else if (access.IsInobject() != new_access.IsInobject()) {
+ // In-objectness did not match.
+ break;
+ }
+ }
+
+ if (count != types->length()) return false;
+
+ // Everything matched; can use monomorphic store.
+ BuildCheckHeapObject(object);
+ Add<HCheckMaps>(object, types);
+ HInstruction* store;
+ CHECK_ALIVE_OR_RETURN(
+ store = BuildStoreNamedField(
+ object, name, store_value, types->at(count - 1), &lookup),
+ true);
+ if (!ast_context()->IsEffect()) Push(result_value);
+ store->set_position(position);
+ AddInstruction(store);
+ Add<HSimulate>(assignment_id);
+ if (!ast_context()->IsEffect()) Drop(1);
+ ast_context()->ReturnValue(result_value);
+ return true;
+}
+
+
+void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField(
+ int position,
+ BailoutId assignment_id,
+ HValue* object,
+ HValue* store_value,
+ HValue* result_value,
+ SmallMapList* types,
+ Handle<String> name) {
+ if (TryStorePolymorphicAsMonomorphic(
+ position, assignment_id, object,
+ store_value, result_value, types, name)) {
+ return;
+ }
+
+ // TODO(ager): We should recognize when the prototype chains for different
+ // maps are identical. In that case we can avoid repeatedly generating the
+ // same prototype map checks.
+ int count = 0;
+ HBasicBlock* join = NULL;
+ for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
+ Handle<Map> map = types->at(i);
+ LookupResult lookup(isolate());
+ if (ComputeLoadStoreField(map, name, &lookup, true)) {
+ if (count == 0) {
+ BuildCheckHeapObject(object);
+ join = graph()->CreateBasicBlock();
+ }
+ ++count;
+ HBasicBlock* if_true = graph()->CreateBasicBlock();
+ HBasicBlock* if_false = graph()->CreateBasicBlock();
+ HCompareMap* compare =
+ new(zone()) HCompareMap(object, map, if_true, if_false);
+ current_block()->Finish(compare);
+
+ set_current_block(if_true);
+ HInstruction* instr;
+ CHECK_ALIVE(instr = BuildStoreNamedField(
+ object, name, store_value, map, &lookup));
+ instr->set_position(position);
+ // Goto will add the HSimulate for the store.
+ AddInstruction(instr);
+ if (!ast_context()->IsEffect()) Push(result_value);
+ current_block()->Goto(join);
+
+ set_current_block(if_false);
+ }
+ }
+
+ // Finish up. Unconditionally deoptimize if we've handled all the maps we
+ // know about and do not want to handle ones we've never seen. Otherwise
+ // use a generic IC.
+ if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
+ FinishExitWithHardDeoptimization("Unknown map in polymorphic store", join);
+ } else {
+ HInstruction* instr = BuildStoreNamedGeneric(object, name, store_value);
+ instr->set_position(position);
+ AddInstruction(instr);
+
+ if (join != NULL) {
+ if (!ast_context()->IsEffect()) {
+ Push(result_value);
+ }
+ current_block()->Goto(join);
+ } else {
+ // 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->HasObservableSideEffects()) {
+ if (ast_context()->IsEffect()) {
+ Add<HSimulate>(assignment_id, REMOVABLE_SIMULATE);
+ } else {
+ Push(result_value);
+ Add<HSimulate>(assignment_id, REMOVABLE_SIMULATE);
+ Drop(1);
+ }
+ }
+ return ast_context()->ReturnValue(result_value);
+ }
+ }
+
+ ASSERT(join != NULL);
+ join->SetJoinId(assignment_id);
+ set_current_block(join);
+ if (!ast_context()->IsEffect()) {
+ ast_context()->ReturnValue(Pop());
+ }
+}
+
+
+void HOptimizedGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
+ Property* prop = expr->target()->AsProperty();
+ ASSERT(prop != NULL);
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+
+ if (prop->key()->IsPropertyName()) {
+ // Named store.
+ CHECK_ALIVE(VisitForValue(expr->value()));
+ HValue* value = environment()->ExpressionStackAt(0);
+ HValue* object = environment()->ExpressionStackAt(1);
+
+ if (expr->IsUninitialized()) {
+ Add<HDeoptimize>("Insufficient type feedback for property assignment",
+ Deoptimizer::SOFT);
+ }
+ return BuildStoreNamed(expr, expr->id(), expr->position(),
+ expr->AssignmentId(), prop, object, value, value);
+ } else {
+ // Keyed store.
+ CHECK_ALIVE(VisitForValue(prop->key()));
+ CHECK_ALIVE(VisitForValue(expr->value()));
+ HValue* value = environment()->ExpressionStackAt(0);
+ HValue* key = environment()->ExpressionStackAt(1);
+ HValue* object = environment()->ExpressionStackAt(2);
+ bool has_side_effects = false;
+ HandleKeyedElementAccess(object, key, value, expr, expr->AssignmentId(),
+ expr->position(),
+ true, // is_store
+ &has_side_effects);
+ Drop(3);
+ Push(value);
+ Add<HSimulate>(expr->AssignmentId(), REMOVABLE_SIMULATE);
+ return ast_context()->ReturnValue(Pop());
+ }
+}
+
+
+// Because not every expression has a position and there is not common
+// superclass of Assignment and CountOperation, we cannot just pass the
+// owning expression instead of position and ast_id separately.
+void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
+ Variable* var,
+ HValue* value,
+ int position,
+ BailoutId ast_id) {
+ LookupResult lookup(isolate());
+ GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
+ if (type == kUseCell) {
+ Handle<GlobalObject> global(current_info()->global_object());
+ Handle<PropertyCell> cell(global->GetPropertyCell(&lookup));
+ if (cell->type()->IsConstant()) {
+ IfBuilder builder(this);
+ HValue* constant = Add<HConstant>(cell->type()->AsConstant());
+ if (cell->type()->AsConstant()->IsNumber()) {
+ builder.If<HCompareNumericAndBranch>(value, constant, Token::EQ);
+ } else {
+ builder.If<HCompareObjectEqAndBranch>(value, constant);
+ }
+ builder.Then();
+ builder.Else();
+ Add<HDeoptimize>("Constant global variable assignment",
+ Deoptimizer::EAGER);
+ builder.End();
+ }
+ HInstruction* instr =
+ Add<HStoreGlobalCell>(value, cell, lookup.GetPropertyDetails());
+ instr->set_position(position);
+ if (instr->HasObservableSideEffects()) {
+ Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
+ }
+ } else {
+ HGlobalObject* global_object = Add<HGlobalObject>();
+ HStoreGlobalGeneric* instr =
+ Add<HStoreGlobalGeneric>(global_object, var->name(),
+ value, function_strict_mode_flag());
+ instr->set_position(position);
+ ASSERT(instr->HasObservableSideEffects());
+ Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
+ }
+}
+
+
+void HOptimizedGraphBuilder::BuildStoreNamed(Expression* expr,
+ BailoutId id,
+ int position,
+ BailoutId assignment_id,
+ Property* prop,
+ HValue* object,
+ HValue* store_value,
+ HValue* result_value) {
+ Literal* key = prop->key()->AsLiteral();
+ Handle<String> name = Handle<String>::cast(key->value());
+ ASSERT(!name.is_null());
+
+ HInstruction* instr = NULL;
+ SmallMapList* types = expr->GetReceiverTypes();
+ bool monomorphic = expr->IsMonomorphic();
+ Handle<Map> map;
+ if (monomorphic) {
+ map = types->first();
+ if (map->is_dictionary_map()) monomorphic = false;
+ }
+ if (monomorphic) {
+ Handle<JSFunction> setter;
+ Handle<JSObject> holder;
+ if (LookupSetter(map, name, &setter, &holder)) {
+ AddCheckConstantFunction(holder, object, map);
+ // Don't try to inline if the result_value is different from the
+ // store_value. That case isn't handled yet by the inlining.
+ if (result_value == store_value &&
+ FLAG_inline_accessors &&
+ TryInlineSetter(setter, id, assignment_id, store_value)) {
+ return;
+ }
+ Drop(2);
+ Add<HPushArgument>(object);
+ Add<HPushArgument>(store_value);
+ instr = new(zone()) HCallConstantFunction(setter, 2);
+ } else {
+ Drop(2);
+ CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object,
+ name,
+ store_value,
+ map));
+ }
+ } else if (types != NULL && types->length() > 1) {
+ Drop(2);
+ return HandlePolymorphicStoreNamedField(
+ position, id, object,
+ store_value, result_value, types, name);
+ } else {
+ Drop(2);
+ instr = BuildStoreNamedGeneric(object, name, store_value);
+ }
+
+ if (!ast_context()->IsEffect()) Push(result_value);
+ instr->set_position(position);
+ AddInstruction(instr);
+ if (instr->HasObservableSideEffects()) {
+ Add<HSimulate>(id, REMOVABLE_SIMULATE);
+ }
+ if (!ast_context()->IsEffect()) Drop(1);
+ return ast_context()->ReturnValue(result_value);
+}
+
+
+void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
+ Expression* target = expr->target();
+ VariableProxy* proxy = target->AsVariableProxy();
+ Property* prop = target->AsProperty();
+ ASSERT(proxy == NULL || prop == NULL);
+
+ // We have a second position recorded in the FullCodeGenerator to have
+ // type feedback for the binary operation.
+ BinaryOperation* operation = expr->binary_operation();
+
+ if (proxy != NULL) {
+ Variable* var = proxy->var();
+ if (var->mode() == LET) {
+ return Bailout(kUnsupportedLetCompoundAssignment);
+ }
+
+ CHECK_ALIVE(VisitForValue(operation));
+
+ switch (var->location()) {
+ case Variable::UNALLOCATED:
+ HandleGlobalVariableAssignment(var,
+ Top(),
+ expr->position(),
+ expr->AssignmentId());
+ break;
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ if (var->mode() == CONST) {
+ return Bailout(kUnsupportedConstCompoundAssignment);
+ }
+ BindIfLive(var, Top());
+ break;
+
+ case Variable::CONTEXT: {
+ // 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.
+ if (current_info()->scope()->arguments() != NULL) {
+ // Parameters will be allocated to context slots. We have no
+ // direct way to detect that the variable is a parameter so we do
+ // a linear search of the parameter variables.
+ int count = current_info()->scope()->num_parameters();
+ for (int i = 0; i < count; ++i) {
+ if (var == current_info()->scope()->parameter(i)) {
+ Bailout(kAssignmentToParameterFunctionUsesArgumentsObject);
+ }
+ }
+ }
+
+ 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 = Add<HStoreContextSlot>(
+ context, var->index(), mode, Top());
+ if (instr->HasObservableSideEffects()) {
+ Add<HSimulate>(expr->AssignmentId(), REMOVABLE_SIMULATE);
+ }
+ break;
+ }
+
+ case Variable::LOOKUP:
+ return Bailout(kCompoundAssignmentToLookupSlot);
+ }
+ return ast_context()->ReturnValue(Pop());
+
+ } else if (prop != NULL) {
+ if (prop->key()->IsPropertyName()) {
+ // Named property.
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ HValue* object = Top();
+
+ Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
+ Handle<Map> map;
+ HInstruction* load = NULL;
+ SmallMapList* types = prop->GetReceiverTypes();
+ bool monomorphic = prop->IsMonomorphic();
+ if (monomorphic) {
+ map = types->first();
+ // We can't generate code for a monomorphic dict mode load so
+ // just pretend it is not monomorphic.
+ if (map->is_dictionary_map()) monomorphic = false;
+ }
+ if (monomorphic) {
+ Handle<JSFunction> getter;
+ Handle<JSObject> holder;
+ if (LookupGetter(map, name, &getter, &holder)) {
+ load = BuildCallGetter(object, map, getter, holder);
+ } else {
+ load = BuildLoadNamedMonomorphic(object, name, prop, map);
+ }
+ } else if (types != NULL && types->length() > 1) {
+ load = TryLoadPolymorphicAsMonomorphic(prop, object, types, name);
+ }
+ if (load == NULL) load = BuildLoadNamedGeneric(object, name, prop);
+ PushAndAdd(load);
+ if (load->HasObservableSideEffects()) {
+ Add<HSimulate>(prop->LoadId(), REMOVABLE_SIMULATE);
+ }
+
+ CHECK_ALIVE(VisitForValue(expr->value()));
+ HValue* right = Pop();
+ HValue* left = Pop();
+
+ HInstruction* instr = BuildBinaryOperation(operation, left, right);
+ PushAndAdd(instr);
+ if (instr->HasObservableSideEffects()) {
+ Add<HSimulate>(operation->id(), REMOVABLE_SIMULATE);
+ }
+
+ return BuildStoreNamed(expr, expr->id(), expr->position(),
+ expr->AssignmentId(), prop, object, instr, instr);
+ } else {
+ // Keyed property.
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
+ HValue* obj = environment()->ExpressionStackAt(1);
+ HValue* key = environment()->ExpressionStackAt(0);
+
+ bool has_side_effects = false;
+ HValue* load = HandleKeyedElementAccess(
+ obj, key, NULL, prop, prop->LoadId(), RelocInfo::kNoPosition,
+ false, // is_store
+ &has_side_effects);
+ Push(load);
+ if (has_side_effects) Add<HSimulate>(prop->LoadId(), REMOVABLE_SIMULATE);
+
+ CHECK_ALIVE(VisitForValue(expr->value()));
+ HValue* right = Pop();
+ HValue* left = Pop();
+
+ HInstruction* instr = BuildBinaryOperation(operation, left, right);
+ PushAndAdd(instr);
+ if (instr->HasObservableSideEffects()) {
+ Add<HSimulate>(operation->id(), REMOVABLE_SIMULATE);
+ }
+
+ HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(),
+ RelocInfo::kNoPosition,
+ true, // is_store
+ &has_side_effects);
+
+ // Drop the simulated receiver, key, and value. Return the value.
+ Drop(3);
+ Push(instr);
+ ASSERT(has_side_effects); // Stores always have side effects.
+ Add<HSimulate>(expr->AssignmentId(), REMOVABLE_SIMULATE);
+ return ast_context()->ReturnValue(Pop());
+ }
+
+ } else {
+ return Bailout(kInvalidLhsInCompoundAssignment);
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitAssignment(Assignment* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ VariableProxy* proxy = expr->target()->AsVariableProxy();
+ Property* prop = expr->target()->AsProperty();
+ ASSERT(proxy == NULL || prop == NULL);
+
+ if (expr->is_compound()) {
+ HandleCompoundAssignment(expr);
+ return;
+ }
+
+ if (prop != NULL) {
+ HandlePropertyAssignment(expr);
+ } else if (proxy != NULL) {
+ Variable* var = proxy->var();
+
+ if (var->mode() == CONST) {
+ if (expr->op() != Token::INIT_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);
+ Add<HUseConst>(old_value);
+ }
+ } else if (var->mode() == CONST_HARMONY) {
+ if (expr->op() != Token::INIT_CONST_HARMONY) {
+ return Bailout(kNonInitializerAssignmentToConst);
+ }
+ }
+
+ if (proxy->IsArguments()) return Bailout(kAssignmentToArguments);
+
+ // Handle the assignment.
+ switch (var->location()) {
+ case Variable::UNALLOCATED:
+ CHECK_ALIVE(VisitForValue(expr->value()));
+ HandleGlobalVariableAssignment(var,
+ Top(),
+ expr->position(),
+ expr->AssignmentId());
+ return ast_context()->ReturnValue(Pop());
+
+ 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(kAssignmentToLetVariableBeforeInitialization);
+ }
+ }
+ // We do not allow the arguments object to occur in a context where it
+ // may escape, but assignments to stack-allocated locals are
+ // permitted.
+ CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
+ HValue* value = Pop();
+ BindIfLive(var, value);
+ return ast_context()->ReturnValue(value);
+ }
+
+ case Variable::CONTEXT: {
+ // 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.
+ if (current_info()->scope()->arguments() != NULL) {
+ // Parameters will rewrite to context slots. We have no direct way
+ // to detect that the variable is a parameter.
+ int count = current_info()->scope()->num_parameters();
+ for (int i = 0; i < count; ++i) {
+ if (var == current_info()->scope()->parameter(i)) {
+ return Bailout(kAssignmentToParameterInArgumentsObject);
+ }
+ }
+ }
+
+ 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 = Add<HStoreContextSlot>(
+ context, var->index(), mode, Top());
+ if (instr->HasObservableSideEffects()) {
+ Add<HSimulate>(expr->AssignmentId(), REMOVABLE_SIMULATE);
+ }
+ return ast_context()->ReturnValue(Pop());
+ }
+
+ case Variable::LOOKUP:
+ return Bailout(kAssignmentToLOOKUPVariable);
+ }
+ } else {
+ return Bailout(kInvalidLeftHandSideInAssignment);
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitYield(Yield* expr) {
+ // Generators are not optimized, so we should never get here.
+ UNREACHABLE();
+}
+
+
+void HOptimizedGraphBuilder::VisitThrow(Throw* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ // We don't optimize functions with invalid left-hand sides in
+ // assignments, count operations, or for-in. Consequently throw can
+ // currently only occur in an effect context.
+ ASSERT(ast_context()->IsEffect());
+ CHECK_ALIVE(VisitForValue(expr->exception()));
+
+ HValue* value = environment()->Pop();
+ HThrow* instr = Add<HThrow>(value);
+ instr->set_position(expr->position());
+ Add<HSimulate>(expr->id());
+ current_block()->FinishExit(new(zone()) HAbnormalExit);
+ set_current_block(NULL);
+}
+
+
+HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
+ HObjectAccess access,
+ HValue* typecheck) {
+ if (FLAG_track_double_fields && access.representation().IsDouble()) {
+ // load the heap number
+ HLoadNamedField* heap_number = Add<HLoadNamedField>(
+ object, access.WithRepresentation(Representation::Tagged()));
+ heap_number->set_type(HType::HeapNumber());
+ // load the double value from it
+ return New<HLoadNamedField>(heap_number,
+ HObjectAccess::ForHeapNumberValue(),
+ typecheck);
+ }
+ return New<HLoadNamedField>(object, access, typecheck);
+}
+
+
+HInstruction* HGraphBuilder::BuildLoadStringLength(HValue* object,
+ HValue* typecheck) {
+ if (FLAG_fold_constants && object->IsConstant()) {
+ HConstant* constant = HConstant::cast(object);
+ if (constant->HasStringValue()) {
+ return New<HConstant>(constant->StringValue()->length());
+ }
+ }
+ return BuildLoadNamedField(
+ object, HObjectAccess::ForStringLength(), typecheck);
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildLoadNamedGeneric(
+ HValue* object,
+ Handle<String> name,
+ Property* expr) {
+ if (expr->IsUninitialized()) {
+ Add<HDeoptimize>("Insufficient feedback for generic named load",
+ Deoptimizer::SOFT);
+ }
+ HValue* context = environment()->context();
+ return new(zone()) HLoadNamedGeneric(context, object, name);
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildCallGetter(
+ HValue* object,
+ Handle<Map> map,
+ Handle<JSFunction> getter,
+ Handle<JSObject> holder) {
+ AddCheckConstantFunction(holder, object, map);
+ Add<HPushArgument>(object);
+ return new(zone()) HCallConstantFunction(getter, 1);
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic(
+ HValue* object,
+ Handle<String> name,
+ Property* expr,
+ Handle<Map> map) {
+ // Handle a load from a known field.
+ ASSERT(!map->is_dictionary_map());
+
+ // Handle access to various length properties
+ if (name->Equals(isolate()->heap()->length_string())) {
+ if (map->instance_type() == JS_ARRAY_TYPE) {
+ HCheckMaps* type_check = AddCheckMap(object, map);
+ return New<HLoadNamedField>(object,
+ HObjectAccess::ForArrayLength(map->elements_kind()), type_check);
+ }
+ }
+
+ LookupResult lookup(isolate());
+ map->LookupDescriptor(NULL, *name, &lookup);
+ if (lookup.IsField()) {
+ HCheckMaps* type_check = AddCheckMap(object, map);
+ return BuildLoadNamedField(object,
+ HObjectAccess::ForField(map, &lookup, name), type_check);
+ }
+
+ // Handle a load of a constant known function.
+ if (lookup.IsConstant()) {
+ AddCheckMap(object, map);
+ Handle<Object> constant(lookup.GetConstantFromMap(*map), isolate());
+ return New<HConstant>(constant);
+ }
+
+ // Handle a load from a known field somewhere in the prototype chain.
+ LookupInPrototypes(map, name, &lookup);
+ if (lookup.IsField()) {
+ Handle<JSObject> prototype(JSObject::cast(map->prototype()));
+ Handle<JSObject> holder(lookup.holder());
+ Handle<Map> holder_map(holder->map());
+ HCheckMaps* type_check = AddCheckMap(object, map);
+ BuildCheckPrototypeMaps(prototype, holder);
+ HValue* holder_value = Add<HConstant>(holder);
+ return BuildLoadNamedField(holder_value,
+ HObjectAccess::ForField(holder_map, &lookup, name), type_check);
+ }
+
+ // Handle a load of a constant function somewhere in the prototype chain.
+ if (lookup.IsConstant()) {
+ Handle<JSObject> prototype(JSObject::cast(map->prototype()));
+ Handle<JSObject> holder(lookup.holder());
+ Handle<Map> holder_map(holder->map());
+ AddCheckMap(object, map);
+ BuildCheckPrototypeMaps(prototype, holder);
+ Handle<Object> constant(lookup.GetConstantFromMap(*holder_map), isolate());
+ return New<HConstant>(constant);
+ }
+
+ // No luck, do a generic load.
+ return BuildLoadNamedGeneric(object, name, expr);
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
+ HValue* key) {
+ HValue* context = environment()->context();
+ return new(zone()) HLoadKeyedGeneric(context, object, key);
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess(
+ HValue* object,
+ HValue* key,
+ HValue* val,
+ HValue* dependency,
+ Handle<Map> map,
+ bool is_store,
+ KeyedAccessStoreMode store_mode) {
+ HCheckMaps* mapcheck = Add<HCheckMaps>(object, map, top_info(), dependency);
+ if (dependency) {
+ mapcheck->ClearGVNFlag(kDependsOnElementsKind);
+ }
+
+ // Loads from a "stock" fast holey double arrays can elide the hole check.
+ LoadKeyedHoleMode load_mode = NEVER_RETURN_HOLE;
+ if (*map == isolate()->get_initial_js_array_map(FAST_HOLEY_DOUBLE_ELEMENTS) &&
+ isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
+ Handle<JSObject> prototype(JSObject::cast(map->prototype()), isolate());
+ Handle<JSObject> object_prototype = isolate()->initial_object_prototype();
+ BuildCheckPrototypeMaps(prototype, object_prototype);
+ load_mode = ALLOW_RETURN_HOLE;
+ graph()->MarkDependsOnEmptyArrayProtoElements();
+ }
+
+ return BuildUncheckedMonomorphicElementAccess(
+ object, key, val,
+ mapcheck, map->instance_type() == JS_ARRAY_TYPE,
+ map->elements_kind(), is_store, load_mode, store_mode);
+}
+
+
+HInstruction* HOptimizedGraphBuilder::TryBuildConsolidatedElementLoad(
+ HValue* object,
+ HValue* key,
+ HValue* val,
+ SmallMapList* maps) {
+ // For polymorphic loads of similar elements kinds (i.e. all tagged or all
+ // double), always use the "worst case" code without a transition. This is
+ // much faster than transitioning the elements to the worst case, trading a
+ // HTransitionElements for a HCheckMaps, and avoiding mutation of the array.
+ bool has_double_maps = false;
+ bool has_smi_or_object_maps = false;
+ bool has_js_array_access = false;
+ bool has_non_js_array_access = false;
+ Handle<Map> most_general_consolidated_map;
+ for (int i = 0; i < maps->length(); ++i) {
+ Handle<Map> map = maps->at(i);
+ // Don't allow mixing of JSArrays with JSObjects.
+ if (map->instance_type() == JS_ARRAY_TYPE) {
+ if (has_non_js_array_access) return NULL;
+ has_js_array_access = true;
+ } else if (has_js_array_access) {
+ return NULL;
+ } else {
+ has_non_js_array_access = true;
+ }
+ // Don't allow mixed, incompatible elements kinds.
+ if (map->has_fast_double_elements()) {
+ if (has_smi_or_object_maps) return NULL;
+ has_double_maps = true;
+ } else if (map->has_fast_smi_or_object_elements()) {
+ if (has_double_maps) return NULL;
+ has_smi_or_object_maps = true;
+ } else {
+ return NULL;
+ }
+ // Remember the most general elements kind, the code for its load will
+ // properly handle all of the more specific cases.
+ if ((i == 0) || IsMoreGeneralElementsKindTransition(
+ most_general_consolidated_map->elements_kind(),
+ map->elements_kind())) {
+ most_general_consolidated_map = map;
+ }
+ }
+ if (!has_double_maps && !has_smi_or_object_maps) return NULL;
+
+ HCheckMaps* check_maps = Add<HCheckMaps>(object, maps);
+ HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
+ object, key, val, check_maps,
+ most_general_consolidated_map->instance_type() == JS_ARRAY_TYPE,
+ most_general_consolidated_map->elements_kind(),
+ false, NEVER_RETURN_HOLE, STANDARD_STORE);
+ return instr;
+}
+
+
+HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
+ HValue* object,
+ HValue* key,
+ HValue* val,
+ Expression* prop,
+ BailoutId ast_id,
+ int position,
+ bool is_store,
+ KeyedAccessStoreMode store_mode,
+ bool* has_side_effects) {
+ *has_side_effects = false;
+ BuildCheckHeapObject(object);
+ SmallMapList* maps = prop->GetReceiverTypes();
+
+ if (!is_store) {
+ HInstruction* consolidated_load =
+ TryBuildConsolidatedElementLoad(object, key, val, maps);
+ if (consolidated_load != NULL) {
+ *has_side_effects |= consolidated_load->HasObservableSideEffects();
+ if (position != RelocInfo::kNoPosition) {
+ consolidated_load->set_position(position);
+ }
+ return consolidated_load;
+ }
+ }
+
+ // 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 (IsFastElementsKind(elements_kind) &&
+ elements_kind != GetInitialFastElementsKind()) {
+ 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);
+ }
+
+ MapHandleList untransitionable_maps(maps->length());
+ HTransitionElementsKind* transition = NULL;
+ for (int i = 0; i < maps->length(); ++i) {
+ Handle<Map> map = maps->at(i);
+ ASSERT(map->IsMap());
+ if (!transition_target.at(i).is_null()) {
+ ASSERT(Map::IsValidElementsTransition(
+ map->elements_kind(),
+ transition_target.at(i)->elements_kind()));
+ transition = Add<HTransitionElementsKind>(object, map,
+ transition_target.at(i));
+ } else {
+ untransitionable_maps.Add(map);
+ }
+ }
+
+ // If only one map is left after transitioning, handle this case
+ // monomorphically.
+ ASSERT(untransitionable_maps.length() >= 1);
+ if (untransitionable_maps.length() == 1) {
+ Handle<Map> untransitionable_map = untransitionable_maps[0];
+ HInstruction* instr = NULL;
+ if (untransitionable_map->has_slow_elements_kind()) {
+ instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
+ : BuildLoadKeyedGeneric(object, key));
+ } else {
+ instr = BuildMonomorphicElementAccess(
+ object, key, val, transition, untransitionable_map, is_store,
+ store_mode);
+ }
+ *has_side_effects |= instr->HasObservableSideEffects();
+ if (position != RelocInfo::kNoPosition) instr->set_position(position);
+ return is_store ? NULL : instr;
+ }
+
+ HInstruction* checkspec =
+ AddInstruction(HCheckInstanceType::NewIsSpecObject(object, zone()));
+ HBasicBlock* join = graph()->CreateBasicBlock();
+
+ HInstruction* elements = AddLoadElements(object, checkspec);
+
+ for (int i = 0; i < untransitionable_maps.length(); ++i) {
+ Handle<Map> map = untransitionable_maps[i];
+ ElementsKind elements_kind = map->elements_kind();
+ HBasicBlock* this_map = graph()->CreateBasicBlock();
+ HBasicBlock* other_map = graph()->CreateBasicBlock();
+ HCompareMap* mapcompare =
+ new(zone()) HCompareMap(object, map, this_map, other_map);
+ current_block()->Finish(mapcompare);
+
+ set_current_block(this_map);
+ HInstruction* checked_key = NULL;
+ HInstruction* access = NULL;
+ if (IsFastElementsKind(elements_kind)) {
+ if (is_store && !IsFastDoubleElementsKind(elements_kind)) {
+ Add<HCheckMaps>(
+ elements, isolate()->factory()->fixed_array_map(),
+ top_info(), mapcompare);
+ }
+ if (map->instance_type() == JS_ARRAY_TYPE) {
+ HInstruction* length = Add<HLoadNamedField>(
+ object, HObjectAccess::ForArrayLength(elements_kind), mapcompare);
+ checked_key = Add<HBoundsCheck>(key, length);
+ } else {
+ HInstruction* length = AddLoadFixedArrayLength(elements);
+ checked_key = Add<HBoundsCheck>(key, length);
+ }
+ access = AddFastElementAccess(
+ elements, checked_key, val, mapcompare,
+ elements_kind, is_store, NEVER_RETURN_HOLE, STANDARD_STORE);
+ } else if (IsDictionaryElementsKind(elements_kind)) {
+ if (is_store) {
+ access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
+ } else {
+ access = AddInstruction(BuildLoadKeyedGeneric(object, key));
+ }
+ } else {
+ ASSERT(IsExternalArrayElementsKind(elements_kind));
+ HInstruction* length = AddLoadFixedArrayLength(elements);
+ checked_key = Add<HBoundsCheck>(key, length);
+ HLoadExternalArrayPointer* external_elements =
+ Add<HLoadExternalArrayPointer>(elements);
+ access = AddExternalArrayElementAccess(
+ external_elements, checked_key, val,
+ mapcompare, elements_kind, is_store);
+ }
+ *has_side_effects |= access->HasObservableSideEffects();
+ // The caller will use has_side_effects and add a correct Simulate.
+ access->SetFlag(HValue::kHasNoObservableSideEffects);
+ if (position != RelocInfo::kNoPosition) access->set_position(position);
+ if (!is_store) {
+ Push(access);
+ }
+ NoObservableSideEffectsScope scope(this);
+ current_block()->GotoNoSimulate(join);
+ set_current_block(other_map);
+ }
+
+ // Deopt if none of the cases matched.
+ NoObservableSideEffectsScope scope(this);
+ FinishExitWithHardDeoptimization("Unknown type in polymorphic element access",
+ join);
+ set_current_block(join);
+ return is_store ? NULL : Pop();
+}
+
+
+HValue* HOptimizedGraphBuilder::HandleKeyedElementAccess(
+ HValue* obj,
+ HValue* key,
+ HValue* val,
+ Expression* expr,
+ BailoutId ast_id,
+ int position,
+ bool is_store,
+ bool* has_side_effects) {
+ ASSERT(!expr->IsPropertyName());
+ HInstruction* instr = NULL;
+ if (expr->IsMonomorphic()) {
+ Handle<Map> map = expr->GetMonomorphicReceiverType();
+ if (map->has_slow_elements_kind()) {
+ instr = is_store ? BuildStoreKeyedGeneric(obj, key, val)
+ : BuildLoadKeyedGeneric(obj, key);
+ AddInstruction(instr);
+ } else {
+ BuildCheckHeapObject(obj);
+ instr = BuildMonomorphicElementAccess(
+ obj, key, val, NULL, map, is_store, expr->GetStoreMode());
+ }
+ } else if (expr->GetReceiverTypes() != NULL &&
+ !expr->GetReceiverTypes()->is_empty()) {
+ return HandlePolymorphicElementAccess(
+ obj, key, val, expr, ast_id, position, is_store,
+ expr->GetStoreMode(), has_side_effects);
+ } else {
+ if (is_store) {
+ if (expr->IsAssignment() && expr->AsAssignment()->IsUninitialized()) {
+ Add<HDeoptimize>("Insufficient feedback for keyed store",
+ Deoptimizer::SOFT);
+ }
+ instr = BuildStoreKeyedGeneric(obj, key, val);
+ } else {
+ if (expr->AsProperty()->IsUninitialized()) {
+ Add<HDeoptimize>("Insufficient feedback for keyed load",
+ Deoptimizer::SOFT);
+ }
+ instr = BuildLoadKeyedGeneric(obj, key);
+ }
+ AddInstruction(instr);
+ }
+ if (position != RelocInfo::kNoPosition) instr->set_position(position);
+ *has_side_effects = instr->HasObservableSideEffects();
+ return instr;
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildStoreKeyedGeneric(
+ HValue* object,
+ HValue* key,
+ HValue* value) {
+ HValue* context = environment()->context();
+ return new(zone()) HStoreKeyedGeneric(
+ context,
+ object,
+ key,
+ value,
+ function_strict_mode_flag());
+}
+
+
+void HOptimizedGraphBuilder::EnsureArgumentsArePushedForAccess() {
+ // Outermost function already has arguments on the stack.
+ if (function_state()->outer() == NULL) return;
+
+ if (function_state()->arguments_pushed()) return;
+
+ // Push arguments when entering inlined function.
+ HEnterInlined* entry = function_state()->entry();
+ entry->set_arguments_pushed();
+
+ HArgumentsObject* arguments = entry->arguments_object();
+ const ZoneList<HValue*>* arguments_values = arguments->arguments_values();
+
+ HInstruction* insert_after = entry;
+ for (int i = 0; i < arguments_values->length(); i++) {
+ HValue* argument = arguments_values->at(i);
+ HInstruction* push_argument = New<HPushArgument>(argument);
+ push_argument->InsertAfter(insert_after);
+ insert_after = push_argument;
+ }
+
+ HArgumentsElements* arguments_elements = New<HArgumentsElements>(true);
+ arguments_elements->ClearFlag(HValue::kUseGVN);
+ arguments_elements->InsertAfter(insert_after);
+ function_state()->set_arguments_elements(arguments_elements);
+}
+
+
+bool HOptimizedGraphBuilder::TryArgumentsAccess(Property* expr) {
+ VariableProxy* proxy = expr->obj()->AsVariableProxy();
+ if (proxy == NULL) return false;
+ if (!proxy->var()->IsStackAllocated()) return false;
+ if (!environment()->Lookup(proxy->var())->CheckFlag(HValue::kIsArguments)) {
+ return false;
+ }
+
+ HInstruction* result = NULL;
+ if (expr->key()->IsPropertyName()) {
+ Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
+ if (!name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("length"))) return false;
+
+ if (function_state()->outer() == NULL) {
+ HInstruction* elements = Add<HArgumentsElements>(false);
+ result = New<HArgumentsLength>(elements);
+ } else {
+ // Number of arguments without receiver.
+ int argument_count = environment()->
+ arguments_environment()->parameter_count() - 1;
+ result = New<HConstant>(argument_count);
+ }
+ } else {
+ Push(graph()->GetArgumentsObject());
+ VisitForValue(expr->key());
+ if (HasStackOverflow() || current_block() == NULL) return true;
+ HValue* key = Pop();
+ Drop(1); // Arguments object.
+ if (function_state()->outer() == NULL) {
+ HInstruction* elements = Add<HArgumentsElements>(false);
+ HInstruction* length = Add<HArgumentsLength>(elements);
+ HInstruction* checked_key = Add<HBoundsCheck>(key, length);
+ result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
+ } else {
+ EnsureArgumentsArePushedForAccess();
+
+ // Number of arguments without receiver.
+ HInstruction* elements = function_state()->arguments_elements();
+ int argument_count = environment()->
+ arguments_environment()->parameter_count() - 1;
+ HInstruction* length = Add<HConstant>(argument_count);
+ HInstruction* checked_key = Add<HBoundsCheck>(key, length);
+ result = new(zone()) HAccessArgumentsAt(elements, length, checked_key);
+ }
+ }
+ ast_context()->ReturnInstruction(result, expr->id());
+ return true;
+}
+
+
+void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+
+ if (TryArgumentsAccess(expr)) return;
+
+ CHECK_ALIVE(VisitForValue(expr->obj()));
+
+ HInstruction* instr = NULL;
+ if (expr->IsStringLength()) {
+ HValue* string = Pop();
+ BuildCheckHeapObject(string);
+ HInstruction* checkstring =
+ AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
+ instr = BuildLoadStringLength(string, checkstring);
+ } else if (expr->IsStringAccess()) {
+ CHECK_ALIVE(VisitForValue(expr->key()));
+ HValue* index = Pop();
+ HValue* string = Pop();
+ HValue* context = environment()->context();
+ HInstruction* char_code =
+ BuildStringCharCodeAt(string, index);
+ AddInstruction(char_code);
+ instr = HStringCharFromCode::New(zone(), context, char_code);
+
+ } else if (expr->IsFunctionPrototype()) {
+ HValue* function = Pop();
+ BuildCheckHeapObject(function);
+ instr = new(zone()) HLoadFunctionPrototype(function);
+
+ } else if (expr->key()->IsPropertyName()) {
+ Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
+ SmallMapList* types = expr->GetReceiverTypes();
+ HValue* object = Top();
+
+ Handle<Map> map;
+ bool monomorphic = false;
+ if (expr->IsMonomorphic()) {
+ map = types->first();
+ monomorphic = !map->is_dictionary_map();
+ } else if (object->HasMonomorphicJSObjectType()) {
+ map = object->GetMonomorphicJSObjectMap();
+ monomorphic = !map->is_dictionary_map();
+ }
+ if (monomorphic) {
+ Handle<JSFunction> getter;
+ Handle<JSObject> holder;
+ if (LookupGetter(map, name, &getter, &holder)) {
+ AddCheckConstantFunction(holder, Top(), map);
+ if (FLAG_inline_accessors && TryInlineGetter(getter, expr)) return;
+ Add<HPushArgument>(Pop());
+ instr = new(zone()) HCallConstantFunction(getter, 1);
+ } else {
+ instr = BuildLoadNamedMonomorphic(Pop(), name, expr, map);
+ }
+ } else if (types != NULL && types->length() > 1) {
+ return HandlePolymorphicLoadNamedField(expr, Pop(), types, name);
+ } else {
+ instr = BuildLoadNamedGeneric(Pop(), name, expr);
+ }
+
+ } else {
+ CHECK_ALIVE(VisitForValue(expr->key()));
+
+ HValue* key = Pop();
+ HValue* obj = Pop();
+
+ bool has_side_effects = false;
+ HValue* load = HandleKeyedElementAccess(
+ obj, key, NULL, expr, expr->id(), expr->position(),
+ false, // is_store
+ &has_side_effects);
+ if (has_side_effects) {
+ if (ast_context()->IsEffect()) {
+ Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
+ } else {
+ Push(load);
+ Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
+ Drop(1);
+ }
+ }
+ return ast_context()->ReturnValue(load);
+ }
+ instr->set_position(expr->position());
+ return ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HGraphBuilder::BuildConstantMapCheck(Handle<JSObject> constant,
+ CompilationInfo* info) {
+ HConstant* constant_value = New<HConstant>(constant);
+
+ if (constant->map()->CanOmitMapChecks()) {
+ constant->map()->AddDependentCompilationInfo(
+ DependentCode::kPrototypeCheckGroup, info);
+ return;
+ }
+
+ AddInstruction(constant_value);
+ HCheckMaps* check =
+ Add<HCheckMaps>(constant_value, handle(constant->map()), info);
+ check->ClearGVNFlag(kDependsOnElementsKind);
+}
+
+
+void HGraphBuilder::BuildCheckPrototypeMaps(Handle<JSObject> prototype,
+ Handle<JSObject> holder) {
+ BuildConstantMapCheck(prototype, top_info());
+ while (!prototype.is_identical_to(holder)) {
+ prototype = handle(JSObject::cast(prototype->GetPrototype()));
+ BuildConstantMapCheck(prototype, top_info());
+ }
+}
+
+
+void HOptimizedGraphBuilder::AddCheckPrototypeMaps(Handle<JSObject> holder,
+ Handle<Map> receiver_map) {
+ if (!holder.is_null()) {
+ Handle<JSObject> prototype(JSObject::cast(receiver_map->prototype()));
+ BuildCheckPrototypeMaps(prototype, holder);
+ }
+}
+
+
+void HOptimizedGraphBuilder::AddCheckConstantFunction(
+ Handle<JSObject> holder,
+ HValue* receiver,
+ Handle<Map> receiver_map) {
+ // Constant functions have the nice property that the map will change if they
+ // are overwritten. Therefore it is enough to check the map of the holder and
+ // its prototypes.
+ AddCheckMap(receiver, receiver_map);
+ AddCheckPrototypeMaps(holder, receiver_map);
+}
+
+
+class FunctionSorter {
+ public:
+ FunctionSorter() : index_(0), ticks_(0), ast_length_(0), src_length_(0) { }
+ FunctionSorter(int index, int ticks, int ast_length, int src_length)
+ : index_(index),
+ ticks_(ticks),
+ ast_length_(ast_length),
+ src_length_(src_length) { }
+
+ int index() const { return index_; }
+ int ticks() const { return ticks_; }
+ int ast_length() const { return ast_length_; }
+ int src_length() const { return src_length_; }
+
+ private:
+ int index_;
+ int ticks_;
+ int ast_length_;
+ int src_length_;
+};
+
+
+inline bool operator<(const FunctionSorter& lhs, const FunctionSorter& rhs) {
+ int diff = lhs.ticks() - rhs.ticks();
+ if (diff != 0) return diff > 0;
+ diff = lhs.ast_length() - rhs.ast_length();
+ if (diff != 0) return diff < 0;
+ return lhs.src_length() < rhs.src_length();
+}
+
+
+bool HOptimizedGraphBuilder::TryCallPolymorphicAsMonomorphic(
+ Call* expr,
+ HValue* receiver,
+ SmallMapList* types,
+ Handle<String> name) {
+ if (types->length() > kMaxCallPolymorphism) return false;
+
+ Handle<Map> map(types->at(0));
+ LookupResult lookup(isolate());
+ if (!CanLoadPropertyFromPrototype(map, name, &lookup)) return false;
+
+ Handle<Object> prototype(map->prototype(), isolate());
+ for (int count = 1; count < types->length(); ++count) {
+ Handle<Map> test_map(types->at(count));
+ if (!CanLoadPropertyFromPrototype(test_map, name, &lookup)) return false;
+ if (test_map->prototype() != *prototype) return false;
+ }
+
+ if (!expr->ComputeTarget(map, name)) return false;
+
+ BuildCheckHeapObject(receiver);
+ Add<HCheckMaps>(receiver, types);
+ AddCheckPrototypeMaps(expr->holder(), map);
+ if (FLAG_trace_inlining) {
+ Handle<JSFunction> caller = current_info()->closure();
+ SmartArrayPointer<char> caller_name =
+ caller->shared()->DebugName()->ToCString();
+ PrintF("Trying to inline the polymorphic call to %s from %s\n",
+ *name->ToCString(), *caller_name);
+ }
+
+ if (!TryInlineCall(expr)) {
+ int argument_count = expr->arguments()->length() + 1; // Includes receiver.
+ HCallConstantFunction* call =
+ new(zone()) HCallConstantFunction(expr->target(), argument_count);
+ call->set_position(expr->position());
+ PreProcessCall(call);
+ AddInstruction(call);
+ if (!ast_context()->IsEffect()) Push(call);
+ Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
+ if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
+ }
+
+ return true;
+}
+
+
+void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
+ Call* expr,
+ HValue* receiver,
+ SmallMapList* types,
+ Handle<String> name) {
+ if (TryCallPolymorphicAsMonomorphic(expr, receiver, types, name)) return;
+
+ int argument_count = expr->arguments()->length() + 1; // Includes receiver.
+ HBasicBlock* join = NULL;
+ FunctionSorter order[kMaxCallPolymorphism];
+ int ordered_functions = 0;
+
+ Handle<Map> initial_string_map(
+ isolate()->native_context()->string_function()->initial_map());
+ Handle<Map> string_marker_map(
+ JSObject::cast(initial_string_map->prototype())->map());
+ Handle<Map> initial_number_map(
+ isolate()->native_context()->number_function()->initial_map());
+ Handle<Map> number_marker_map(
+ JSObject::cast(initial_number_map->prototype())->map());
+ Handle<Map> heap_number_map = isolate()->factory()->heap_number_map();
+
+ bool handle_smi = false;
+
+ for (int i = 0;
+ i < types->length() && ordered_functions < kMaxCallPolymorphism;
+ ++i) {
+ Handle<Map> map = types->at(i);
+ if (expr->ComputeTarget(map, name)) {
+ if (map.is_identical_to(number_marker_map)) handle_smi = true;
+ order[ordered_functions++] =
+ FunctionSorter(i,
+ expr->target()->shared()->profiler_ticks(),
+ InliningAstSize(expr->target()),
+ expr->target()->shared()->SourceSize());
+ }
+ }
+
+ std::sort(order, order + ordered_functions);
+
+ HBasicBlock* number_block = NULL;
+
+ for (int fn = 0; fn < ordered_functions; ++fn) {
+ int i = order[fn].index();
+ Handle<Map> map = types->at(i);
+ if (fn == 0) {
+ // Only needed once.
+ join = graph()->CreateBasicBlock();
+ if (handle_smi) {
+ HBasicBlock* empty_smi_block = graph()->CreateBasicBlock();
+ HBasicBlock* not_smi_block = graph()->CreateBasicBlock();
+ number_block = graph()->CreateBasicBlock();
+ HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(receiver);
+ smicheck->SetSuccessorAt(0, empty_smi_block);
+ smicheck->SetSuccessorAt(1, not_smi_block);
+ current_block()->Finish(smicheck);
+ empty_smi_block->Goto(number_block);
+ set_current_block(not_smi_block);
+ } else {
+ BuildCheckHeapObject(receiver);
+ }
+ }
+ HBasicBlock* if_true = graph()->CreateBasicBlock();
+ HBasicBlock* if_false = graph()->CreateBasicBlock();
+ HUnaryControlInstruction* compare;
+
+ if (handle_smi && map.is_identical_to(number_marker_map)) {
+ compare = new(zone()) HCompareMap(
+ receiver, heap_number_map, if_true, if_false);
+ map = initial_number_map;
+ expr->set_number_check(
+ Handle<JSObject>(JSObject::cast(map->prototype())));
+ } else if (map.is_identical_to(string_marker_map)) {
+ compare = new(zone()) HIsStringAndBranch(receiver);
+ compare->SetSuccessorAt(0, if_true);
+ compare->SetSuccessorAt(1, if_false);
+ map = initial_string_map;
+ expr->set_string_check(
+ Handle<JSObject>(JSObject::cast(map->prototype())));
+ } else {
+ compare = new(zone()) HCompareMap(receiver, map, if_true, if_false);
+ expr->set_map_check();
+ }
+
+ current_block()->Finish(compare);
+
+ if (expr->check_type() == NUMBER_CHECK) {
+ if_true->Goto(number_block);
+ if_true = number_block;
+ number_block->SetJoinId(expr->id());
+ }
+ set_current_block(if_true);
+
+ expr->ComputeTarget(map, name);
+ AddCheckPrototypeMaps(expr->holder(), map);
+ if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
+ Handle<JSFunction> caller = current_info()->closure();
+ SmartArrayPointer<char> caller_name =
+ caller->shared()->DebugName()->ToCString();
+ PrintF("Trying to inline the polymorphic call to %s from %s\n",
+ *name->ToCString(),
+ *caller_name);
+ }
+ if (FLAG_polymorphic_inlining && TryInlineCall(expr)) {
+ // Trying to inline will signal that we should bailout from the
+ // entire compilation by setting stack overflow on the visitor.
+ if (HasStackOverflow()) return;
+ } else {
+ HCallConstantFunction* call =
+ new(zone()) HCallConstantFunction(expr->target(), argument_count);
+ call->set_position(expr->position());
+ PreProcessCall(call);
+ AddInstruction(call);
+ if (!ast_context()->IsEffect()) Push(call);
+ }
+
+ if (current_block() != NULL) current_block()->Goto(join);
+ set_current_block(if_false);
+ }
+
+ // Finish up. Unconditionally deoptimize if we've handled all the maps we
+ // know about and do not want to handle ones we've never seen. Otherwise
+ // use a generic IC.
+ if (ordered_functions == types->length() && FLAG_deoptimize_uncommon_cases) {
+ // Because the deopt may be the only path in the polymorphic call, make sure
+ // that the environment stack matches the depth on deopt that it otherwise
+ // would have had after a successful call.
+ Drop(argument_count - (ast_context()->IsEffect() ? 0 : 1));
+ FinishExitWithHardDeoptimization("Unknown map in polymorphic call", join);
+ } else {
+ HValue* context = environment()->context();
+ HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
+ call->set_position(expr->position());
+ PreProcessCall(call);
+
+ if (join != NULL) {
+ AddInstruction(call);
+ if (!ast_context()->IsEffect()) Push(call);
+ current_block()->Goto(join);
+ } else {
+ return ast_context()->ReturnInstruction(call, expr->id());
+ }
+ }
+
+ // We assume that control flow is always live after an expression. So
+ // even without predecessors to the join block, we set it as the exit
+ // block and continue by adding instructions there.
+ ASSERT(join != NULL);
+ if (join->HasPredecessor()) {
+ set_current_block(join);
+ join->SetJoinId(expr->id());
+ if (!ast_context()->IsEffect()) return ast_context()->ReturnValue(Pop());
+ } else {
+ set_current_block(NULL);
+ }
+}
+
+
+void HOptimizedGraphBuilder::TraceInline(Handle<JSFunction> target,
+ Handle<JSFunction> caller,
+ const char* reason) {
+ if (FLAG_trace_inlining) {
+ SmartArrayPointer<char> target_name =
+ target->shared()->DebugName()->ToCString();
+ SmartArrayPointer<char> caller_name =
+ caller->shared()->DebugName()->ToCString();
+ if (reason == NULL) {
+ PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
+ } else {
+ PrintF("Did not inline %s called from %s (%s).\n",
+ *target_name, *caller_name, reason);
+ }
+ }
+}
+
+
+static const int kNotInlinable = 1000000000;
+
+
+int HOptimizedGraphBuilder::InliningAstSize(Handle<JSFunction> target) {
+ if (!FLAG_use_inlining) return kNotInlinable;
+
+ // Precondition: call is monomorphic and we have found a target with the
+ // appropriate arity.
+ Handle<JSFunction> caller = current_info()->closure();
+ Handle<SharedFunctionInfo> target_shared(target->shared());
+
+ // Do a quick check on source code length to avoid parsing large
+ // inlining candidates.
+ if (target_shared->SourceSize() >
+ Min(FLAG_max_inlined_source_size, kUnlimitedMaxInlinedSourceSize)) {
+ TraceInline(target, caller, "target text too big");
+ return kNotInlinable;
+ }
+
+ // Target must be inlineable.
+ if (!target->IsInlineable()) {
+ TraceInline(target, caller, "target not inlineable");
+ return kNotInlinable;
+ }
+ if (target_shared->dont_inline() || target_shared->dont_optimize()) {
+ TraceInline(target, caller, "target contains unsupported syntax [early]");
+ return kNotInlinable;
+ }
+
+ int nodes_added = target_shared->ast_node_count();
+ return nodes_added;
+}
+
+
+bool HOptimizedGraphBuilder::TryInline(CallKind call_kind,
+ Handle<JSFunction> target,
+ int arguments_count,
+ HValue* implicit_return_value,
+ BailoutId ast_id,
+ BailoutId return_id,
+ InliningKind inlining_kind) {
+ int nodes_added = InliningAstSize(target);
+ if (nodes_added == kNotInlinable) return false;
+
+ Handle<JSFunction> caller = current_info()->closure();
+
+ if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
+ TraceInline(target, caller, "target AST is too large [early]");
+ return false;
+ }
+
+#if !V8_TARGET_ARCH_IA32
+ // Target must be able to use caller's context.
+ CompilationInfo* outer_info = current_info();
+ if (target->context() != outer_info->closure()->context() ||
+ outer_info->scope()->contains_with() ||
+ outer_info->scope()->num_heap_slots() > 0) {
+ TraceInline(target, caller, "target requires context change");
+ return false;
+ }
+#endif
+
+
+ // Don't inline deeper than the maximum number of inlining levels.
+ HEnvironment* env = environment();
+ int current_level = 1;
+ while (env->outer() != NULL) {
+ if (current_level == FLAG_max_inlining_levels) {
+ TraceInline(target, caller, "inline depth limit reached");
+ return false;
+ }
+ if (env->outer()->frame_type() == JS_FUNCTION) {
+ current_level++;
+ }
+ env = env->outer();
+ }
+
+ // Don't inline recursive functions.
+ for (FunctionState* state = function_state();
+ state != NULL;
+ state = state->outer()) {
+ if (*state->compilation_info()->closure() == *target) {
+ TraceInline(target, caller, "target is recursive");
+ return false;
+ }
+ }
+
+ // We don't want to add more than a certain number of nodes from inlining.
+ if (inlined_count_ > Min(FLAG_max_inlined_nodes_cumulative,
+ kUnlimitedMaxInlinedNodesCumulative)) {
+ TraceInline(target, caller, "cumulative AST node limit reached");
+ return false;
+ }
+
+ // Parse and allocate variables.
+ CompilationInfo target_info(target, zone());
+ Handle<SharedFunctionInfo> target_shared(target->shared());
+ if (!Parser::Parse(&target_info) || !Scope::Analyze(&target_info)) {
+ if (target_info.isolate()->has_pending_exception()) {
+ // Parse or scope error, never optimize this function.
+ SetStackOverflow();
+ target_shared->DisableOptimization(kParseScopeError);
+ }
+ TraceInline(target, caller, "parse failure");
+ return false;
+ }
+
+ if (target_info.scope()->num_heap_slots() > 0) {
+ TraceInline(target, caller, "target has context-allocated variables");
+ return false;
+ }
+ FunctionLiteral* function = target_info.function();
+
+ // 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 (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) {
+ 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;
+ }
+
+ // If the function uses the arguments object check that inlining of functions
+ // with arguments object is enabled and the arguments-variable is
+ // stack allocated.
+ if (function->scope()->arguments() != NULL) {
+ if (!FLAG_inline_arguments) {
+ TraceInline(target, caller, "target uses arguments object");
+ return false;
+ }
+
+ if (!function->scope()->arguments()->IsStackAllocated()) {
+ TraceInline(target,
+ caller,
+ "target uses non-stackallocated arguments object");
+ return false;
+ }
+ }
+
+ // All declarations must be inlineable.
+ ZoneList<Declaration*>* decls = target_info.scope()->declarations();
+ int decl_count = decls->length();
+ for (int i = 0; i < decl_count; ++i) {
+ if (!decls->at(i)->IsInlineable()) {
+ TraceInline(target, caller, "target has non-trivial declaration");
+ return false;
+ }
+ }
+
+ // Generate the deoptimization data for the unoptimized version of
+ // the target function if we don't already have it.
+ if (!target_shared->has_deoptimization_support()) {
+ // Note that we compile here using the same AST that we will use for
+ // generating the optimized inline code.
+ target_info.EnableDeoptimizationSupport();
+ if (!FullCodeGenerator::MakeCode(&target_info)) {
+ TraceInline(target, caller, "could not generate deoptimization info");
+ return false;
+ }
+ if (target_shared->scope_info() == ScopeInfo::Empty(isolate())) {
+ // The scope info might not have been set if a lazily compiled
+ // function is inlined before being called for the first time.
+ Handle<ScopeInfo> target_scope_info =
+ ScopeInfo::Create(target_info.scope(), zone());
+ target_shared->set_scope_info(*target_scope_info);
+ }
+ target_shared->EnableDeoptimizationSupport(*target_info.code());
+ Compiler::RecordFunctionCompilation(Logger::FUNCTION_TAG,
+ &target_info,
+ target_shared);
+ }
+
+ // ----------------------------------------------------------------
+ // After this point, we've made a decision to inline this function (so
+ // TryInline should always return true).
+
+ // Type-check the inlined function.
+ ASSERT(target_shared->has_deoptimization_support());
+ AstTyper::Run(&target_info);
+
+ // Save the pending call context. Set up new one for the inlined function.
+ // The function state is new-allocated because we need to delete it
+ // in two different places.
+ FunctionState* target_state = new FunctionState(
+ this, &target_info, inlining_kind);
+
+ HConstant* undefined = graph()->GetConstantUndefined();
+ bool undefined_receiver = HEnvironment::UseUndefinedReceiver(
+ target, function, call_kind, inlining_kind);
+ HEnvironment* inner_env =
+ environment()->CopyForInlining(target,
+ arguments_count,
+ function,
+ undefined,
+ function_state()->inlining_kind(),
+ undefined_receiver);
+#if 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 = Add<HConstant>(Handle<Context>(target->context()));
+ inner_env->BindContext(context);
+#endif
+
+ Add<HSimulate>(return_id);
+ current_block()->UpdateEnvironment(inner_env);
+ HArgumentsObject* arguments_object = NULL;
+
+ // If the function uses arguments object create and bind one, also copy
+ // current arguments values to use them for materialization.
+ if (function->scope()->arguments() != NULL) {
+ ASSERT(function->scope()->arguments()->IsStackAllocated());
+ HEnvironment* arguments_env = inner_env->arguments_environment();
+ int arguments_count = arguments_env->parameter_count();
+ arguments_object = Add<HArgumentsObject>(arguments_count);
+ inner_env->Bind(function->scope()->arguments(), arguments_object);
+ for (int i = 0; i < arguments_count; i++) {
+ arguments_object->AddArgument(arguments_env->Lookup(i), zone());
+ }
+ }
+
+ HEnterInlined* enter_inlined =
+ Add<HEnterInlined>(target, arguments_count, function,
+ function_state()->inlining_kind(),
+ function->scope()->arguments(),
+ arguments_object, undefined_receiver);
+ function_state()->set_entry(enter_inlined);
+
+ VisitDeclarations(target_info.scope()->declarations());
+ VisitStatements(function->body());
+ if (HasStackOverflow()) {
+ // Bail out if the inline function did, as we cannot residualize a call
+ // instead.
+ TraceInline(target, caller, "inline graph construction failed");
+ target_shared->DisableOptimization(kInliningBailedOut);
+ inline_bailout_ = true;
+ delete target_state;
+ return true;
+ }
+
+ // Update inlined nodes count.
+ inlined_count_ += nodes_added;
+
+ Handle<Code> unoptimized_code(target_shared->code());
+ ASSERT(unoptimized_code->kind() == Code::FUNCTION);
+ Handle<TypeFeedbackInfo> type_info(
+ TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info()));
+ graph()->update_type_change_checksum(type_info->own_type_change_checksum());
+
+ TraceInline(target, caller, NULL);
+
+ if (current_block() != NULL) {
+ FunctionState* state = function_state();
+ if (state->inlining_kind() == CONSTRUCT_CALL_RETURN) {
+ // Falling off the end of an inlined construct call. In a test context the
+ // return value will always evaluate to true, in a value context the
+ // return value is the newly allocated receiver.
+ if (call_context()->IsTest()) {
+ current_block()->Goto(inlined_test_context()->if_true(), state);
+ } else if (call_context()->IsEffect()) {
+ current_block()->Goto(function_return(), state);
+ } else {
+ ASSERT(call_context()->IsValue());
+ current_block()->AddLeaveInlined(implicit_return_value, state);
+ }
+ } else if (state->inlining_kind() == SETTER_CALL_RETURN) {
+ // Falling off the end of an inlined setter call. The returned value is
+ // never used, the value of an assignment is always the value of the RHS
+ // of the assignment.
+ if (call_context()->IsTest()) {
+ inlined_test_context()->ReturnValue(implicit_return_value);
+ } else if (call_context()->IsEffect()) {
+ current_block()->Goto(function_return(), state);
+ } else {
+ ASSERT(call_context()->IsValue());
+ current_block()->AddLeaveInlined(implicit_return_value, state);
+ }
+ } else {
+ // Falling off the end of a normal inlined function. This basically means
+ // returning undefined.
+ if (call_context()->IsTest()) {
+ current_block()->Goto(inlined_test_context()->if_false(), state);
+ } else if (call_context()->IsEffect()) {
+ current_block()->Goto(function_return(), state);
+ } else {
+ ASSERT(call_context()->IsValue());
+ current_block()->AddLeaveInlined(undefined, state);
+ }
+ }
+ }
+
+ // Fix up the function exits.
+ if (inlined_test_context() != NULL) {
+ HBasicBlock* if_true = inlined_test_context()->if_true();
+ HBasicBlock* if_false = inlined_test_context()->if_false();
+
+ HEnterInlined* entry = function_state()->entry();
+
+ // 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()) {
+ entry->RegisterReturnTarget(if_true, zone());
+ if_true->SetJoinId(ast_id);
+ HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
+ if_true->Goto(true_target, function_state());
+ }
+ if (if_false->HasPredecessor()) {
+ entry->RegisterReturnTarget(if_false, zone());
+ if_false->SetJoinId(ast_id);
+ HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
+ if_false->Goto(false_target, function_state());
+ }
+ set_current_block(NULL);
+ return true;
+
+ } else if (function_return()->HasPredecessor()) {
+ function_state()->entry()->RegisterReturnTarget(function_return(), zone());
+ function_return()->SetJoinId(ast_id);
+ set_current_block(function_return());
+ } else {
+ set_current_block(NULL);
+ }
+ delete target_state;
+ return true;
+}
+
+
+bool HOptimizedGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) {
+ // The function call we are inlining is a method call if the call
+ // is a property call.
+ CallKind call_kind = (expr->expression()->AsProperty() == NULL)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+
+ return TryInline(call_kind,
+ expr->target(),
+ expr->arguments()->length(),
+ NULL,
+ expr->id(),
+ expr->ReturnId(),
+ drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN);
+}
+
+
+bool HOptimizedGraphBuilder::TryInlineConstruct(CallNew* expr,
+ HValue* implicit_return_value) {
+ return TryInline(CALL_AS_FUNCTION,
+ expr->target(),
+ expr->arguments()->length(),
+ implicit_return_value,
+ expr->id(),
+ expr->ReturnId(),
+ CONSTRUCT_CALL_RETURN);
+}
+
+
+bool HOptimizedGraphBuilder::TryInlineGetter(Handle<JSFunction> getter,
+ Property* prop) {
+ return TryInline(CALL_AS_METHOD,
+ getter,
+ 0,
+ NULL,
+ prop->id(),
+ prop->LoadId(),
+ GETTER_CALL_RETURN);
+}
+
+
+bool HOptimizedGraphBuilder::TryInlineSetter(Handle<JSFunction> setter,
+ BailoutId id,
+ BailoutId assignment_id,
+ HValue* implicit_return_value) {
+ return TryInline(CALL_AS_METHOD,
+ setter,
+ 1,
+ implicit_return_value,
+ id, assignment_id,
+ SETTER_CALL_RETURN);
+}
+
+
+bool HOptimizedGraphBuilder::TryInlineApply(Handle<JSFunction> function,
+ Call* expr,
+ int arguments_count) {
+ return TryInline(CALL_AS_METHOD,
+ function,
+ arguments_count,
+ NULL,
+ expr->id(),
+ expr->ReturnId(),
+ NORMAL_RETURN);
+}
+
+
+bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
+ bool drop_extra) {
+ if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
+ BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
+ switch (id) {
+ case kMathExp:
+ if (!FLAG_fast_math) break;
+ // Fall through if FLAG_fast_math.
+ case kMathRound:
+ case kMathFloor:
+ case kMathAbs:
+ case kMathSqrt:
+ case kMathLog:
+ case kMathSin:
+ case kMathCos:
+ case kMathTan:
+ if (expr->arguments()->length() == 1) {
+ HValue* argument = Pop();
+ HValue* context = environment()->context();
+ Drop(1); // Receiver.
+ HInstruction* op =
+ HUnaryMathOperation::New(zone(), 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;
+ case kMathImul:
+ if (expr->arguments()->length() == 2) {
+ HValue* right = Pop();
+ HValue* left = Pop();
+ Drop(1); // Receiver.
+ HValue* context = environment()->context();
+ HInstruction* op = HMul::NewImul(zone(), context, left, right);
+ 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 HOptimizedGraphBuilder::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;
+ BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
+ int argument_count = expr->arguments()->length() + 1; // Plus receiver.
+ switch (id) {
+ case kStringCharCodeAt:
+ case kStringCharAt:
+ if (argument_count == 2 && check_type == STRING_CHECK) {
+ HValue* index = Pop();
+ HValue* string = Pop();
+ HValue* context = environment()->context();
+ ASSERT(!expr->holder().is_null());
+ BuildCheckPrototypeMaps(Call::GetPrototypeForPrimitiveCheck(
+ STRING_CHECK, expr->holder()->GetIsolate()),
+ expr->holder());
+ HInstruction* char_code =
+ BuildStringCharCodeAt(string, index);
+ if (id == kStringCharCodeAt) {
+ ast_context()->ReturnInstruction(char_code, expr->id());
+ return true;
+ }
+ AddInstruction(char_code);
+ HInstruction* result =
+ HStringCharFromCode::New(zone(), context, char_code);
+ ast_context()->ReturnInstruction(result, expr->id());
+ return true;
+ }
+ break;
+ case kStringFromCharCode:
+ if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
+ HValue* argument = Pop();
+ HValue* context = environment()->context();
+ Drop(1); // Receiver.
+ HInstruction* result =
+ HStringCharFromCode::New(zone(), context, argument);
+ ast_context()->ReturnInstruction(result, expr->id());
+ return true;
+ }
+ break;
+ case kMathExp:
+ if (!FLAG_fast_math) break;
+ // Fall through if FLAG_fast_math.
+ case kMathRound:
+ case kMathFloor:
+ case kMathAbs:
+ case kMathSqrt:
+ case kMathLog:
+ case kMathSin:
+ case kMathCos:
+ case kMathTan:
+ if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
+ HValue* argument = Pop();
+ HValue* context = environment()->context();
+ Drop(1); // Receiver.
+ HInstruction* op =
+ HUnaryMathOperation::New(zone(), context, argument, id);
+ op->set_position(expr->position());
+ ast_context()->ReturnInstruction(op, expr->id());
+ return true;
+ }
+ break;
+ case kMathPow:
+ if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
+ HValue* right = Pop();
+ HValue* left = Pop();
+ Pop(); // Pop receiver.
+ HValue* context = environment()->context();
+ HInstruction* result = NULL;
+ // Use sqrt() if exponent is 0.5 or -0.5.
+ if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
+ double exponent = HConstant::cast(right)->DoubleValue();
+ if (exponent == 0.5) {
+ result =
+ HUnaryMathOperation::New(zone(), context, left, kMathPowHalf);
+ } else if (exponent == -0.5) {
+ HValue* one = graph()->GetConstant1();
+ HInstruction* sqrt =
+ HUnaryMathOperation::New(zone(), context, left, kMathPowHalf);
+ AddInstruction(sqrt);
+ // MathPowHalf doesn't have side effects so there's no need for
+ // an environment simulation here.
+ ASSERT(!sqrt->HasObservableSideEffects());
+ result = HDiv::New(zone(), context, one, sqrt);
+ } else if (exponent == 2.0) {
+ result = HMul::New(zone(), context, left, left);
+ }
+ } else if (right->EqualsInteger32Constant(2)) {
+ result = HMul::New(zone(), context, left, left);
+ }
+
+ if (result == NULL) {
+ result = HPower::New(zone(), context, left, right);
+ }
+ ast_context()->ReturnInstruction(result, expr->id());
+ return true;
+ }
+ break;
+ case kMathRandom:
+ if (argument_count == 1 && check_type == RECEIVER_MAP_CHECK) {
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
+ Drop(1); // Receiver.
+ HGlobalObject* global_object = Add<HGlobalObject>();
+ 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->holder(), receiver, receiver_map);
+ HValue* right = Pop();
+ HValue* left = Pop();
+ Drop(1); // Receiver.
+ HValue* context = environment()->context();
+ HMathMinMax::Operation op = (id == kMathMin) ? HMathMinMax::kMathMin
+ : HMathMinMax::kMathMax;
+ HInstruction* result =
+ HMathMinMax::New(zone(), context, left, right, op);
+ ast_context()->ReturnInstruction(result, expr->id());
+ return true;
+ }
+ break;
+ case kMathImul:
+ if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
+ HValue* right = Pop();
+ HValue* left = Pop();
+ Drop(1); // Receiver.
+ HValue* context = environment()->context();
+ HInstruction* result = HMul::NewImul(zone(), context, left, right);
+ ast_context()->ReturnInstruction(result, expr->id());
+ return true;
+ }
+ break;
+ default:
+ // Not yet supported for inlining.
+ break;
+ }
+ return false;
+}
+
+
+bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
+ Expression* callee = expr->expression();
+ Property* prop = callee->AsProperty();
+ ASSERT(prop != NULL);
+
+ if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) {
+ return false;
+ }
+ Handle<Map> function_map = expr->GetReceiverTypes()->first();
+ if (function_map->instance_type() != JS_FUNCTION_TYPE ||
+ !expr->target()->shared()->HasBuiltinFunctionId() ||
+ expr->target()->shared()->builtin_function_id() != kFunctionApply) {
+ return false;
+ }
+
+ if (current_info()->scope()->arguments() == NULL) return false;
+
+ ZoneList<Expression*>* args = expr->arguments();
+ if (args->length() != 2) return false;
+
+ VariableProxy* arg_two = args->at(1)->AsVariableProxy();
+ if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
+ HValue* arg_two_value = LookupAndMakeLive(arg_two->var());
+ if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
+
+ // Found pattern f.apply(receiver, arguments).
+ VisitForValue(prop->obj());
+ if (HasStackOverflow() || current_block() == NULL) return true;
+ HValue* function = Top();
+ AddCheckConstantFunction(expr->holder(), function, function_map);
+ Drop(1);
+
+ VisitForValue(args->at(0));
+ if (HasStackOverflow() || current_block() == NULL) return true;
+ HValue* receiver = Pop();
+
+ if (function_state()->outer() == NULL) {
+ HInstruction* elements = Add<HArgumentsElements>(false);
+ HInstruction* length = Add<HArgumentsLength>(elements);
+ HValue* wrapped_receiver = BuildWrapReceiver(receiver, function);
+ HInstruction* result =
+ new(zone()) HApplyArguments(function,
+ wrapped_receiver,
+ length,
+ elements);
+ result->set_position(expr->position());
+ ast_context()->ReturnInstruction(result, expr->id());
+ return true;
+ } else {
+ // We are inside inlined function and we know exactly what is inside
+ // arguments object. But we need to be able to materialize at deopt.
+ ASSERT_EQ(environment()->arguments_environment()->parameter_count(),
+ function_state()->entry()->arguments_object()->arguments_count());
+ HArgumentsObject* args = function_state()->entry()->arguments_object();
+ const ZoneList<HValue*>* arguments_values = args->arguments_values();
+ int arguments_count = arguments_values->length();
+ Push(BuildWrapReceiver(receiver, function));
+ for (int i = 1; i < arguments_count; i++) {
+ Push(arguments_values->at(i));
+ }
+
+ Handle<JSFunction> known_function;
+ if (function->IsConstant()) {
+ HConstant* constant_function = HConstant::cast(function);
+ known_function = Handle<JSFunction>::cast(constant_function->handle());
+ int args_count = arguments_count - 1; // Excluding receiver.
+ if (TryInlineApply(known_function, expr, args_count)) return true;
+ }
+
+ Drop(arguments_count - 1);
+ PushAndAdd(New<HPushArgument>(Pop()));
+ for (int i = 1; i < arguments_count; i++) {
+ PushAndAdd(New<HPushArgument>(arguments_values->at(i)));
+ }
+
+ HValue* context = environment()->context();
+ HInvokeFunction* call = new(zone()) HInvokeFunction(
+ context,
+ function,
+ known_function,
+ arguments_count);
+ Drop(arguments_count);
+ call->set_position(expr->position());
+ ast_context()->ReturnInstruction(call, expr->id());
+ return true;
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitCall(Call* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ Expression* callee = expr->expression();
+ int argument_count = expr->arguments()->length() + 1; // Plus receiver.
+ HInstruction* call = NULL;
+
+ Property* prop = callee->AsProperty();
+ if (prop != NULL) {
+ if (!prop->key()->IsPropertyName()) {
+ // Keyed function call.
+ CHECK_ALIVE(VisitArgument(prop->obj()));
+
+ CHECK_ALIVE(VisitForValue(prop->key()));
+ // Push receiver and key like the non-optimized code generator expects it.
+ HValue* key = Pop();
+ HValue* receiver = Pop();
+ Push(key);
+ Push(receiver);
+
+ CHECK_ALIVE(VisitArgumentList(expr->arguments()));
+
+ HValue* context = environment()->context();
+ call = new(zone()) HCallKeyed(context, key, argument_count);
+ call->set_position(expr->position());
+ Drop(argument_count + 1); // 1 is the key.
+ return ast_context()->ReturnInstruction(call, expr->id());
+ }
+
+ // Named function call.
+ if (TryCallApply(expr)) return;
+
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
+
+ Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
+ SmallMapList* types = expr->GetReceiverTypes();
+
+ bool monomorphic = expr->IsMonomorphic();
+ Handle<Map> receiver_map;
+ if (monomorphic) {
+ receiver_map = (types == NULL || types->is_empty())
+ ? Handle<Map>::null()
+ : types->first();
+ }
+
+ HValue* receiver =
+ environment()->ExpressionStackAt(expr->arguments()->length());
+ if (monomorphic) {
+ 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()) ||
+ 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
+ // when a primitive receiver check is required.
+ HValue* context = environment()->context();
+ call = PreProcessCall(
+ new(zone()) HCallNamed(context, name, argument_count));
+ } else {
+ AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
+
+ if (TryInlineCall(expr)) return;
+ call = PreProcessCall(
+ new(zone()) HCallConstantFunction(expr->target(),
+ argument_count));
+ }
+ } else if (types != NULL && types->length() > 1) {
+ ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
+ HandlePolymorphicCallNamed(expr, receiver, types, name);
+ return;
+
+ } else {
+ HValue* context = environment()->context();
+ call = PreProcessCall(
+ new(zone()) HCallNamed(context, name, argument_count));
+ }
+
+ } else {
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+ if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
+ return Bailout(kPossibleDirectCallToEval);
+ }
+
+ bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
+ if (global_call) {
+ Variable* var = proxy->var();
+ bool known_global_function = false;
+ // 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(isolate());
+ GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
+ if (type == kUseCell &&
+ !current_info()->global_object()->IsAccessCheckNeeded()) {
+ Handle<GlobalObject> global(current_info()->global_object());
+ known_global_function = expr->ComputeGlobalTarget(global, &lookup);
+ }
+ if (known_global_function) {
+ // Push the global object instead of the global receiver because
+ // code generated by the full code generator expects it.
+ HValue* context = environment()->context();
+ HGlobalObject* global_object = new(zone()) HGlobalObject(context);
+ PushAndAdd(global_object);
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
+
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ HValue* function = Pop();
+ Add<HCheckFunction>(function, expr->target());
+
+ // Replace the global object with the global receiver.
+ HGlobalReceiver* global_receiver = Add<HGlobalReceiver>(global_object);
+ // Index of the receiver from the top of the expression stack.
+ const int receiver_index = argument_count - 1;
+ ASSERT(environment()->ExpressionStackAt(receiver_index)->
+ 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 (TryInlineCall(expr)) return;
+
+ if (expr->target().is_identical_to(current_info()->closure())) {
+ graph()->MarkRecursive();
+ }
+
+ if (CallStubCompiler::HasCustomCallGenerator(expr->target())) {
+ // When the target has a custom call IC generator, use the IC,
+ // because it is likely to generate better code.
+ HValue* context = environment()->context();
+ call = PreProcessCall(
+ new(zone()) HCallNamed(context, var->name(), argument_count));
+ } else {
+ call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
+ argument_count));
+ }
+ } else {
+ HGlobalObject* receiver = Add<HGlobalObject>();
+ PushAndAdd(New<HPushArgument>(receiver));
+ CHECK_ALIVE(VisitArgumentList(expr->arguments()));
+
+ call = New<HCallGlobal>(var->name(), argument_count);
+ 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();
+ HGlobalObject* global = Add<HGlobalObject>();
+ HGlobalReceiver* receiver = New<HGlobalReceiver>(global);
+ PushAndAdd(receiver);
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
+ Add<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 (TryInlineCall(expr, true)) { // Drop function from environment.
+ return;
+ } else {
+ call = PreProcessCall(New<HInvokeFunction>(function, expr->target(),
+ argument_count));
+ Drop(1); // The function.
+ }
+
+ } else {
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ HValue* function = Top();
+ HGlobalObject* global_object = Add<HGlobalObject>();
+ HGlobalReceiver* receiver = Add<HGlobalReceiver>(global_object);
+ PushAndAdd(New<HPushArgument>(receiver));
+ CHECK_ALIVE(VisitArgumentList(expr->arguments()));
+
+ call = New<HCallFunction>(function, argument_count);
+ Drop(argument_count + 1);
+ }
+ }
+
+ call->set_position(expr->position());
+ return ast_context()->ReturnInstruction(call, expr->id());
+}
+
+
+// Checks whether allocation using the given constructor can be inlined.
+static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
+ return constructor->has_initial_map() &&
+ constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
+ constructor->initial_map()->instance_size() < HAllocate::kMaxInlineSize &&
+ constructor->initial_map()->InitialPropertiesLength() == 0;
+}
+
+
+void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ int argument_count = expr->arguments()->length() + 1; // Plus constructor.
+ HValue* context = environment()->context();
+ Factory* factory = isolate()->factory();
+
+ if (FLAG_inline_construct &&
+ expr->IsMonomorphic() &&
+ IsAllocationInlineable(expr->target())) {
+ // The constructor function is on the stack in the unoptimized code
+ // during evaluation of the arguments.
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ HValue* function = Top();
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
+ Handle<JSFunction> constructor = expr->target();
+ HValue* check = Add<HCheckFunction>(function, constructor);
+
+ // Force completion of inobject slack tracking before generating
+ // allocation code to finalize instance size.
+ if (constructor->shared()->IsInobjectSlackTrackingInProgress()) {
+ constructor->shared()->CompleteInobjectSlackTracking();
+ }
+
+ // Calculate instance size from initial map of constructor.
+ ASSERT(constructor->has_initial_map());
+ Handle<Map> initial_map(constructor->initial_map());
+ int instance_size = initial_map->instance_size();
+ ASSERT(initial_map->InitialPropertiesLength() == 0);
+
+ // Allocate an instance of the implicit receiver object.
+ HValue* size_in_bytes = Add<HConstant>(instance_size);
+ PretenureFlag pretenure_flag =
+ (FLAG_pretenuring_call_new &&
+ isolate()->heap()->GetPretenureMode() == TENURED)
+ ? TENURED : NOT_TENURED;
+ HAllocate* receiver =
+ Add<HAllocate>(size_in_bytes, HType::JSObject(), pretenure_flag,
+ JS_OBJECT_TYPE);
+ receiver->set_known_initial_map(initial_map);
+
+ // Load the initial map from the constructor.
+ HValue* constructor_value = Add<HConstant>(constructor);
+ HValue* initial_map_value =
+ Add<HLoadNamedField>(constructor_value, HObjectAccess::ForJSObjectOffset(
+ JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Initialize map and fields of the newly allocated object.
+ { NoObservableSideEffectsScope no_effects(this);
+ ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE);
+ Add<HStoreNamedField>(receiver,
+ HObjectAccess::ForJSObjectOffset(JSObject::kMapOffset),
+ initial_map_value);
+ HValue* empty_fixed_array = Add<HConstant>(factory->empty_fixed_array());
+ Add<HStoreNamedField>(receiver,
+ HObjectAccess::ForJSObjectOffset(JSObject::kPropertiesOffset),
+ empty_fixed_array);
+ Add<HStoreNamedField>(receiver,
+ HObjectAccess::ForJSObjectOffset(JSObject::kElementsOffset),
+ empty_fixed_array);
+ if (initial_map->inobject_properties() != 0) {
+ HConstant* undefined = graph()->GetConstantUndefined();
+ for (int i = 0; i < initial_map->inobject_properties(); i++) {
+ int property_offset = JSObject::kHeaderSize + i * kPointerSize;
+ Add<HStoreNamedField>(receiver,
+ HObjectAccess::ForJSObjectOffset(property_offset),
+ undefined);
+ }
+ }
+ }
+
+ // Replace the constructor function with a newly allocated receiver using
+ // the index of the receiver from the top of the expression stack.
+ const int receiver_index = argument_count - 1;
+ ASSERT(environment()->ExpressionStackAt(receiver_index) == function);
+ environment()->SetExpressionStackAt(receiver_index, receiver);
+
+ if (TryInlineConstruct(expr, receiver)) return;
+
+ // TODO(mstarzinger): For now we remove the previous HAllocate and all
+ // corresponding instructions and instead add HPushArgument for the
+ // arguments in case inlining failed. What we actually should do is for
+ // inlining to try to build a subgraph without mutating the parent graph.
+ HInstruction* instr = current_block()->last();
+ while (instr != initial_map_value) {
+ HInstruction* prev_instr = instr->previous();
+ instr->DeleteAndReplaceWith(NULL);
+ instr = prev_instr;
+ }
+ initial_map_value->DeleteAndReplaceWith(NULL);
+ receiver->DeleteAndReplaceWith(NULL);
+ check->DeleteAndReplaceWith(NULL);
+ environment()->SetExpressionStackAt(receiver_index, function);
+ HInstruction* call = PreProcessCall(
+ new(zone()) HCallNew(context, function, argument_count));
+ call->set_position(expr->position());
+ return ast_context()->ReturnInstruction(call, expr->id());
+ } else {
+ // The constructor function is both an operand to the instruction and an
+ // argument to the construct call.
+ Handle<JSFunction> array_function(
+ isolate()->global_context()->array_function(), isolate());
+ CHECK_ALIVE(VisitArgument(expr->expression()));
+ HValue* constructor = HPushArgument::cast(Top())->argument();
+ CHECK_ALIVE(VisitArgumentList(expr->arguments()));
+ HCallNew* call;
+ if (expr->target().is_identical_to(array_function)) {
+ Handle<Cell> cell = expr->allocation_info_cell();
+ Add<HCheckFunction>(constructor, array_function);
+ call = new(zone()) HCallNewArray(context, constructor, argument_count,
+ cell, expr->elements_kind());
+ } else {
+ call = new(zone()) HCallNew(context, constructor, argument_count);
+ }
+ Drop(argument_count);
+ call->set_position(expr->position());
+ return ast_context()->ReturnInstruction(call, expr->id());
+ }
+}
+
+
+// Support for generating inlined runtime functions.
+
+// Lookup table for generators for runtime calls that are generated inline.
+// Elements of the table are member pointers to functions of
+// HOptimizedGraphBuilder.
+#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \
+ &HOptimizedGraphBuilder::Generate##Name,
+
+const HOptimizedGraphBuilder::InlineFunctionGenerator
+ HOptimizedGraphBuilder::kInlineFunctionGenerators[] = {
+ INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
+ INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
+};
+#undef INLINE_FUNCTION_GENERATOR_ADDRESS
+
+
+void HOptimizedGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ if (expr->is_jsruntime()) {
+ return Bailout(kCallToAJavaScriptRuntimeFunction);
+ }
+
+ const Runtime::Function* function = expr->function();
+ ASSERT(function != NULL);
+ if (function->intrinsic_type == Runtime::INLINE) {
+ ASSERT(expr->name()->length() > 0);
+ ASSERT(expr->name()->Get(0) == '_');
+ // Call to an inline function.
+ int lookup_index = static_cast<int>(function->function_id) -
+ static_cast<int>(Runtime::kFirstInlineFunction);
+ ASSERT(lookup_index >= 0);
+ ASSERT(static_cast<size_t>(lookup_index) <
+ ARRAY_SIZE(kInlineFunctionGenerators));
+ InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index];
+
+ // Call the inline code generator using the pointer-to-member.
+ (this->*generator)(expr);
+ } else {
+ ASSERT(function->intrinsic_type == Runtime::RUNTIME);
+ CHECK_ALIVE(VisitArgumentList(expr->arguments()));
+
+ Handle<String> name = expr->name();
+ int argument_count = expr->arguments()->length();
+ HCallRuntime* call = New<HCallRuntime>(name, function,
+ argument_count);
+ Drop(argument_count);
+ return ast_context()->ReturnInstruction(call, expr->id());
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ switch (expr->op()) {
+ case Token::DELETE: return VisitDelete(expr);
+ case Token::VOID: return VisitVoid(expr);
+ case Token::TYPEOF: return VisitTypeof(expr);
+ case Token::NOT: return VisitNot(expr);
+ default: UNREACHABLE();
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitDelete(UnaryOperation* expr) {
+ Property* prop = expr->expression()->AsProperty();
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+ if (prop != NULL) {
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
+ HValue* key = Pop();
+ HValue* obj = Pop();
+ HValue* function = AddLoadJSBuiltin(Builtins::DELETE);
+ Add<HPushArgument>(obj);
+ Add<HPushArgument>(key);
+ Add<HPushArgument>(Add<HConstant>(function_strict_mode_flag()));
+ // TODO(olivf) InvokeFunction produces a check for the parameter count,
+ // even though we are certain to pass the correct number of arguments here.
+ HInstruction* instr = New<HInvokeFunction>(function, 3);
+ return ast_context()->ReturnInstruction(instr, expr->id());
+ } else if (proxy != NULL) {
+ Variable* var = proxy->var();
+ if (var->IsUnallocated()) {
+ Bailout(kDeleteWithGlobalVariable);
+ } else if (var->IsStackAllocated() || var->IsContextSlot()) {
+ // Result of deleting non-global variables is false. 'this' is not
+ // really a variable, though we implement it as one. The
+ // subexpression does not have side effects.
+ HValue* value = var->is_this()
+ ? graph()->GetConstantTrue()
+ : graph()->GetConstantFalse();
+ return ast_context()->ReturnValue(value);
+ } else {
+ Bailout(kDeleteWithNonGlobalVariable);
+ }
+ } else {
+ // Result of deleting non-property, non-variable reference is true.
+ // Evaluate the subexpression for side effects.
+ CHECK_ALIVE(VisitForEffect(expr->expression()));
+ return ast_context()->ReturnValue(graph()->GetConstantTrue());
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitVoid(UnaryOperation* expr) {
+ CHECK_ALIVE(VisitForEffect(expr->expression()));
+ return ast_context()->ReturnValue(graph()->GetConstantUndefined());
+}
+
+
+void HOptimizedGraphBuilder::VisitTypeof(UnaryOperation* expr) {
+ CHECK_ALIVE(VisitForTypeOf(expr->expression()));
+ HValue* value = Pop();
+ HValue* context = environment()->context();
+ HInstruction* instr = new(zone()) HTypeof(context, value);
+ return ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HOptimizedGraphBuilder::VisitNot(UnaryOperation* expr) {
+ if (ast_context()->IsTest()) {
+ TestContext* context = TestContext::cast(ast_context());
+ VisitForControl(expr->expression(),
+ context->if_false(),
+ context->if_true());
+ return;
+ }
+
+ if (ast_context()->IsEffect()) {
+ VisitForEffect(expr->expression());
+ return;
+ }
+
+ ASSERT(ast_context()->IsValue());
+ HBasicBlock* materialize_false = graph()->CreateBasicBlock();
+ HBasicBlock* materialize_true = graph()->CreateBasicBlock();
+ CHECK_BAILOUT(VisitForControl(expr->expression(),
+ materialize_false,
+ materialize_true));
+
+ if (materialize_false->HasPredecessor()) {
+ materialize_false->SetJoinId(expr->MaterializeFalseId());
+ set_current_block(materialize_false);
+ Push(graph()->GetConstantFalse());
+ } else {
+ materialize_false = NULL;
+ }
+
+ if (materialize_true->HasPredecessor()) {
+ materialize_true->SetJoinId(expr->MaterializeTrueId());
+ set_current_block(materialize_true);
+ Push(graph()->GetConstantTrue());
+ } else {
+ materialize_true = NULL;
+ }
+
+ HBasicBlock* join =
+ CreateJoin(materialize_false, materialize_true, expr->id());
+ set_current_block(join);
+ if (join != NULL) return ast_context()->ReturnValue(Pop());
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildIncrement(
+ bool returns_original_input,
+ CountOperation* expr) {
+ // The input to the count operation is on top of the expression stack.
+ TypeInfo info = expr->type();
+ Representation rep = Representation::FromType(info);
+ if (rep.IsNone() || rep.IsTagged()) {
+ rep = Representation::Smi();
+ }
+
+ if (returns_original_input) {
+ // We need an explicit HValue representing ToNumber(input). The
+ // actual HChange instruction we need is (sometimes) added in a later
+ // phase, so it is not available now to be used as an input to HAdd and
+ // as the return value.
+ HInstruction* number_input = Add<HForceRepresentation>(Pop(), rep);
+ if (!rep.IsDouble()) {
+ number_input->SetFlag(HInstruction::kFlexibleRepresentation);
+ number_input->SetFlag(HInstruction::kCannotBeTagged);
+ }
+ Push(number_input);
+ }
+
+ // The addition has no side effects, so we do not need
+ // to simulate the expression stack after this instruction.
+ // Any later failures deopt to the load of the input or earlier.
+ HConstant* delta = (expr->op() == Token::INC)
+ ? graph()->GetConstant1()
+ : graph()->GetConstantMinus1();
+ HInstruction* instr = Add<HAdd>(Top(), delta);
+ instr->SetFlag(HInstruction::kCannotBeTagged);
+ instr->ClearAllSideEffects();
+ return instr;
+}
+
+
+void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ Expression* target = expr->expression();
+ VariableProxy* proxy = target->AsVariableProxy();
+ Property* prop = target->AsProperty();
+ if (proxy == NULL && prop == NULL) {
+ return Bailout(kInvalidLhsInCountOperation);
+ }
+
+ // Match the full code generator stack by simulating an extra stack
+ // element for postfix operations in a non-effect context. The return
+ // value is ToNumber(input).
+ bool returns_original_input =
+ expr->is_postfix() && !ast_context()->IsEffect();
+ HValue* input = NULL; // ToNumber(original_input).
+ HValue* after = NULL; // The result after incrementing or decrementing.
+
+ if (proxy != NULL) {
+ Variable* var = proxy->var();
+ if (var->mode() == CONST) {
+ return Bailout(kUnsupportedCountOperationWithConst);
+ }
+ // Argument of the count operation is a variable, not a property.
+ ASSERT(prop == NULL);
+ CHECK_ALIVE(VisitForValue(target));
+
+ after = BuildIncrement(returns_original_input, expr);
+ input = returns_original_input ? Top() : Pop();
+ Push(after);
+
+ switch (var->location()) {
+ case Variable::UNALLOCATED:
+ HandleGlobalVariableAssignment(var,
+ after,
+ expr->position(),
+ expr->AssignmentId());
+ break;
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ BindIfLive(var, after);
+ break;
+
+ case Variable::CONTEXT: {
+ // 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.
+ if (current_info()->scope()->arguments() != NULL) {
+ // Parameters will rewrite to context slots. We have no direct
+ // way to detect that the variable is a parameter so we use a
+ // linear search of the parameter list.
+ int count = current_info()->scope()->num_parameters();
+ for (int i = 0; i < count; ++i) {
+ if (var == current_info()->scope()->parameter(i)) {
+ return Bailout(kAssignmentToParameterInArgumentsObject);
+ }
+ }
+ }
+
+ HValue* context = BuildContextChainWalk(var);
+ HStoreContextSlot::Mode mode = IsLexicalVariableMode(var->mode())
+ ? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck;
+ HStoreContextSlot* instr = Add<HStoreContextSlot>(context, var->index(),
+ mode, after);
+ if (instr->HasObservableSideEffects()) {
+ Add<HSimulate>(expr->AssignmentId(), REMOVABLE_SIMULATE);
+ }
+ break;
+ }
+
+ case Variable::LOOKUP:
+ return Bailout(kLookupVariableInCountOperation);
+ }
+
+ } else {
+ // Argument of the count operation is a property.
+ ASSERT(prop != NULL);
+
+ if (prop->key()->IsPropertyName()) {
+ // Named property.
+ if (returns_original_input) Push(graph()->GetConstantUndefined());
+
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ HValue* object = Top();
+
+ Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
+ Handle<Map> map;
+ HInstruction* load = NULL;
+ bool monomorphic = prop->IsMonomorphic();
+ SmallMapList* types = prop->GetReceiverTypes();
+ if (monomorphic) {
+ map = types->first();
+ if (map->is_dictionary_map()) monomorphic = false;
+ }
+ if (monomorphic) {
+ Handle<JSFunction> getter;
+ Handle<JSObject> holder;
+ if (LookupGetter(map, name, &getter, &holder)) {
+ load = BuildCallGetter(object, map, getter, holder);
+ } else {
+ load = BuildLoadNamedMonomorphic(object, name, prop, map);
+ }
+ } else if (types != NULL && types->length() > 1) {
+ load = TryLoadPolymorphicAsMonomorphic(prop, object, types, name);
+ }
+ if (load == NULL) load = BuildLoadNamedGeneric(object, name, prop);
+ PushAndAdd(load);
+ if (load->HasObservableSideEffects()) {
+ Add<HSimulate>(prop->LoadId(), REMOVABLE_SIMULATE);
+ }
+
+ after = BuildIncrement(returns_original_input, expr);
+ HValue* result = returns_original_input ? Pop() : after;
+
+ return BuildStoreNamed(expr, expr->id(), expr->position(),
+ expr->AssignmentId(), prop, object, after, result);
+ } else {
+ // Keyed property.
+ if (returns_original_input) Push(graph()->GetConstantUndefined());
+
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
+ HValue* obj = environment()->ExpressionStackAt(1);
+ HValue* key = environment()->ExpressionStackAt(0);
+
+ bool has_side_effects = false;
+ HValue* load = HandleKeyedElementAccess(
+ obj, key, NULL, prop, prop->LoadId(), RelocInfo::kNoPosition,
+ false, // is_store
+ &has_side_effects);
+ Push(load);
+ if (has_side_effects) Add<HSimulate>(prop->LoadId(), REMOVABLE_SIMULATE);
+
+ after = BuildIncrement(returns_original_input, expr);
+ input = environment()->ExpressionStackAt(0);
+
+ HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(),
+ RelocInfo::kNoPosition,
+ true, // is_store
+ &has_side_effects);
+
+ // Drop the key and the original value from the bailout environment.
+ // Overwrite the receiver with the result of the operation, and the
+ // placeholder with the original value if necessary.
+ Drop(2);
+ environment()->SetExpressionStackAt(0, after);
+ if (returns_original_input) environment()->SetExpressionStackAt(1, input);
+ ASSERT(has_side_effects); // Stores always have side effects.
+ Add<HSimulate>(expr->AssignmentId(), REMOVABLE_SIMULATE);
+ }
+ }
+
+ Drop(returns_original_input ? 2 : 1);
+ return ast_context()->ReturnValue(expr->is_postfix() ? input : after);
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildStringCharCodeAt(
+ HValue* string,
+ HValue* index) {
+ if (string->IsConstant() && index->IsConstant()) {
+ HConstant* c_string = HConstant::cast(string);
+ HConstant* c_index = HConstant::cast(index);
+ if (c_string->HasStringValue() && c_index->HasNumberValue()) {
+ int32_t i = c_index->NumberValueAsInteger32();
+ Handle<String> s = c_string->StringValue();
+ if (i < 0 || i >= s->length()) {
+ return New<HConstant>(OS::nan_value());
+ }
+ return New<HConstant>(s->Get(i));
+ }
+ }
+ BuildCheckHeapObject(string);
+ HValue* checkstring =
+ AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
+ HInstruction* length = BuildLoadStringLength(string, checkstring);
+ AddInstruction(length);
+ HInstruction* checked_index = Add<HBoundsCheck>(index, length);
+ return New<HStringCharCodeAt>(string, checked_index);
+}
+
+
+// Checks if the given shift amounts have form: (sa) and (32 - sa).
+static bool ShiftAmountsAllowReplaceByRotate(HValue* sa,
+ HValue* const32_minus_sa) {
+ if (!const32_minus_sa->IsSub()) return false;
+ HSub* sub = HSub::cast(const32_minus_sa);
+ if (sa != sub->right()) return false;
+ HValue* const32 = sub->left();
+ if (!const32->IsConstant() ||
+ HConstant::cast(const32)->Integer32Value() != 32) {
+ return false;
+ }
+ return (sub->right() == sa);
+}
+
+
+// Checks if the left and the right are shift instructions with the oposite
+// directions that can be replaced by one rotate right instruction or not.
+// Returns the operand and the shift amount for the rotate instruction in the
+// former case.
+bool HOptimizedGraphBuilder::MatchRotateRight(HValue* left,
+ HValue* right,
+ HValue** operand,
+ HValue** shift_amount) {
+ HShl* shl;
+ HShr* shr;
+ if (left->IsShl() && right->IsShr()) {
+ shl = HShl::cast(left);
+ shr = HShr::cast(right);
+ } else if (left->IsShr() && right->IsShl()) {
+ shl = HShl::cast(right);
+ shr = HShr::cast(left);
+ } else {
+ return false;
+ }
+ if (shl->left() != shr->left()) return false;
+
+ if (!ShiftAmountsAllowReplaceByRotate(shl->right(), shr->right()) &&
+ !ShiftAmountsAllowReplaceByRotate(shr->right(), shl->right())) {
+ return false;
+ }
+ *operand= shr->left();
+ *shift_amount = shr->right();
+ return true;
+}
+
+
+bool CanBeZero(HValue* right) {
+ if (right->IsConstant()) {
+ HConstant* right_const = HConstant::cast(right);
+ if (right_const->HasInteger32Value() &&
+ (right_const->Integer32Value() & 0x1f) != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+HValue* HGraphBuilder::TruncateToNumber(HValue* value, Handle<Type>* expected) {
+ if (value->IsConstant()) {
+ HConstant* constant = HConstant::cast(value);
+ Maybe<HConstant*> number = constant->CopyToTruncatedNumber(zone());
+ if (number.has_value) {
+ *expected = handle(Type::Number(), isolate());
+ return AddInstruction(number.value);
+ }
+ }
+
+ return value;
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation(
+ BinaryOperation* expr,
+ HValue* left,
+ HValue* right) {
+ HValue* context = environment()->context();
+ Handle<Type> left_type = expr->left()->bounds().lower;
+ Handle<Type> right_type = expr->right()->bounds().lower;
+ Handle<Type> result_type = expr->bounds().lower;
+ Maybe<int> fixed_right_arg = expr->fixed_right_arg();
+ Representation left_rep = Representation::FromType(left_type);
+ Representation right_rep = Representation::FromType(right_type);
+ Representation result_rep = Representation::FromType(result_type);
+
+ if (expr->op() != Token::ADD ||
+ (left->type().IsNonString() && right->type().IsNonString())) {
+ // For addition we can only truncate the arguments to number if we can
+ // prove that we will not end up in string concatenation mode.
+ left = TruncateToNumber(left, &left_type);
+ right = TruncateToNumber(right, &right_type);
+ }
+
+ if (left_type->Is(Type::None())) {
+ Add<HDeoptimize>("Insufficient type feedback for left side",
+ Deoptimizer::SOFT);
+ // TODO(rossberg): we should be able to get rid of non-continuous defaults.
+ left_type = handle(Type::Any(), isolate());
+ }
+ if (right_type->Is(Type::None())) {
+ Add<HDeoptimize>("Insufficient type feedback for right side",
+ Deoptimizer::SOFT);
+ right_type = handle(Type::Any(), isolate());
+ }
+ HInstruction* instr = NULL;
+ switch (expr->op()) {
+ case Token::ADD:
+ if (left_type->Is(Type::String()) && right_type->Is(Type::String())) {
+ BuildCheckHeapObject(left);
+ AddInstruction(HCheckInstanceType::NewIsString(left, zone()));
+ BuildCheckHeapObject(right);
+ AddInstruction(HCheckInstanceType::NewIsString(right, zone()));
+ instr = HStringAdd::New(zone(), context, left, right);
+ } else {
+ instr = HAdd::New(zone(), context, left, right);
+ }
+ break;
+ case Token::SUB:
+ instr = HSub::New(zone(), context, left, right);
+ break;
+ case Token::MUL:
+ instr = HMul::New(zone(), context, left, right);
+ break;
+ case Token::MOD:
+ instr = HMod::New(zone(), context, left, right, fixed_right_arg);
+ break;
+ case Token::DIV:
+ instr = HDiv::New(zone(), context, left, right);
+ break;
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ instr = NewUncasted<HBitwise>(expr->op(), left, right);
+ break;
+ case Token::BIT_OR: {
+ HValue* operand, *shift_amount;
+ if (left_type->Is(Type::Signed32()) &&
+ right_type->Is(Type::Signed32()) &&
+ MatchRotateRight(left, right, &operand, &shift_amount)) {
+ instr = new(zone()) HRor(context, operand, shift_amount);
+ } else {
+ instr = NewUncasted<HBitwise>(expr->op(), left, right);
+ }
+ break;
+ }
+ case Token::SAR:
+ instr = HSar::New(zone(), context, left, right);
+ break;
+ case Token::SHR:
+ instr = HShr::New(zone(), context, left, right);
+ if (FLAG_opt_safe_uint32_operations && instr->IsShr() &&
+ CanBeZero(right)) {
+ graph()->RecordUint32Instruction(instr);
+ }
+ break;
+ case Token::SHL:
+ instr = HShl::New(zone(), context, left, right);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (instr->IsBinaryOperation()) {
+ HBinaryOperation* binop = HBinaryOperation::cast(instr);
+ binop->set_observed_input_representation(1, left_rep);
+ binop->set_observed_input_representation(2, right_rep);
+ binop->initialize_output_representation(result_rep);
+ }
+ return instr;
+}
+
+
+// Check for the form (%_ClassOf(foo) === 'BarClass').
+static bool IsClassOfTest(CompareOperation* expr) {
+ if (expr->op() != Token::EQ_STRICT) return false;
+ CallRuntime* call = expr->left()->AsCallRuntime();
+ if (call == NULL) return false;
+ Literal* literal = expr->right()->AsLiteral();
+ if (literal == NULL) return false;
+ if (!literal->value()->IsString()) return false;
+ if (!call->name()->IsOneByteEqualTo(STATIC_ASCII_VECTOR("_ClassOf"))) {
+ return false;
+ }
+ ASSERT(call->arguments()->length() == 1);
+ return true;
+}
+
+
+void HOptimizedGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ switch (expr->op()) {
+ case Token::COMMA:
+ return VisitComma(expr);
+ case Token::OR:
+ case Token::AND:
+ return VisitLogicalExpression(expr);
+ default:
+ return VisitArithmeticExpression(expr);
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitComma(BinaryOperation* expr) {
+ CHECK_ALIVE(VisitForEffect(expr->left()));
+ // Visit the right subexpression in the same AST context as the entire
+ // expression.
+ Visit(expr->right());
+}
+
+
+void HOptimizedGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
+ bool is_logical_and = expr->op() == Token::AND;
+ if (ast_context()->IsTest()) {
+ TestContext* context = TestContext::cast(ast_context());
+ // Translate left subexpression.
+ HBasicBlock* eval_right = graph()->CreateBasicBlock();
+ if (is_logical_and) {
+ CHECK_BAILOUT(VisitForControl(expr->left(),
+ eval_right,
+ context->if_false()));
+ } else {
+ CHECK_BAILOUT(VisitForControl(expr->left(),
+ context->if_true(),
+ eval_right));
+ }
+
+ // Translate right subexpression by visiting it in the same AST
+ // context as the entire expression.
+ if (eval_right->HasPredecessor()) {
+ eval_right->SetJoinId(expr->RightId());
+ set_current_block(eval_right);
+ Visit(expr->right());
+ }
+
+ } else if (ast_context()->IsValue()) {
+ CHECK_ALIVE(VisitForValue(expr->left()));
+ ASSERT(current_block() != NULL);
+ HValue* left_value = Top();
+
+ if (left_value->IsConstant()) {
+ HConstant* left_constant = HConstant::cast(left_value);
+ if ((is_logical_and && left_constant->BooleanValue()) ||
+ (!is_logical_and && !left_constant->BooleanValue())) {
+ Drop(1); // left_value.
+ CHECK_ALIVE(VisitForValue(expr->right()));
+ }
+ return ast_context()->ReturnValue(Pop());
+ }
+
+ // We need an extra block to maintain edge-split form.
+ HBasicBlock* empty_block = graph()->CreateBasicBlock();
+ HBasicBlock* eval_right = graph()->CreateBasicBlock();
+ ToBooleanStub::Types expected(expr->left()->to_boolean_types());
+ HBranch* test = is_logical_and
+ ? new(zone()) HBranch(left_value, expected, eval_right, empty_block)
+ : new(zone()) HBranch(left_value, expected, empty_block, eval_right);
+ current_block()->Finish(test);
+
+ set_current_block(eval_right);
+ Drop(1); // Value of the left subexpression.
+ CHECK_BAILOUT(VisitForValue(expr->right()));
+
+ HBasicBlock* join_block =
+ CreateJoin(empty_block, current_block(), expr->id());
+ set_current_block(join_block);
+ return ast_context()->ReturnValue(Pop());
+
+ } else {
+ ASSERT(ast_context()->IsEffect());
+ // In an effect context, we don't need the value of the left subexpression,
+ // only its control flow and side effects. We need an extra block to
+ // maintain edge-split form.
+ HBasicBlock* empty_block = graph()->CreateBasicBlock();
+ HBasicBlock* right_block = graph()->CreateBasicBlock();
+ if (is_logical_and) {
+ CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
+ } else {
+ CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
+ }
+
+ // TODO(kmillikin): Find a way to fix this. It's ugly that there are
+ // actually two empty blocks (one here and one inserted by
+ // TestContext::BuildBranch, and that they both have an HSimulate though the
+ // second one is not a merge node, and that we really have no good AST ID to
+ // put on that first HSimulate.
+
+ if (empty_block->HasPredecessor()) {
+ empty_block->SetJoinId(expr->id());
+ } else {
+ empty_block = NULL;
+ }
+
+ if (right_block->HasPredecessor()) {
+ right_block->SetJoinId(expr->RightId());
+ set_current_block(right_block);
+ CHECK_BAILOUT(VisitForEffect(expr->right()));
+ right_block = current_block();
+ } else {
+ right_block = NULL;
+ }
+
+ HBasicBlock* join_block =
+ CreateJoin(empty_block, right_block, expr->id());
+ set_current_block(join_block);
+ // We did not materialize any value in the predecessor environments,
+ // so there is no need to handle it here.
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
+ CHECK_ALIVE(VisitForValue(expr->left()));
+ CHECK_ALIVE(VisitForValue(expr->right()));
+ HValue* right = Pop();
+ HValue* left = Pop();
+ HInstruction* instr = BuildBinaryOperation(expr, left, right);
+ instr->set_position(expr->position());
+ return ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HOptimizedGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
+ Expression* sub_expr,
+ Handle<String> check) {
+ CHECK_ALIVE(VisitForTypeOf(sub_expr));
+ HValue* value = Pop();
+ HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check);
+ instr->set_position(expr->position());
+ return ast_context()->ReturnControl(instr, expr->id());
+}
+
+
+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()));
+}
+
+
+void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+
+ // Check for a few fast cases. The AST visiting behavior must be in sync
+ // with the full codegen: We don't push both left and right values onto
+ // the expression stack when one side is a special-case literal.
+ Expression* sub_expr = NULL;
+ Handle<String> check;
+ if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) {
+ return HandleLiteralCompareTypeof(expr, sub_expr, check);
+ }
+ if (expr->IsLiteralCompareUndefined(&sub_expr, isolate())) {
+ return HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue);
+ }
+ if (expr->IsLiteralCompareNull(&sub_expr)) {
+ return HandleLiteralCompareNil(expr, sub_expr, kNullValue);
+ }
+
+ if (IsClassOfTest(expr)) {
+ CallRuntime* call = expr->left()->AsCallRuntime();
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ Literal* literal = expr->right()->AsLiteral();
+ Handle<String> rhs = Handle<String>::cast(literal->value());
+ HClassOfTestAndBranch* instr =
+ new(zone()) HClassOfTestAndBranch(value, rhs);
+ instr->set_position(expr->position());
+ return ast_context()->ReturnControl(instr, expr->id());
+ }
+
+ Handle<Type> left_type = expr->left()->bounds().lower;
+ Handle<Type> right_type = expr->right()->bounds().lower;
+ Handle<Type> combined_type = expr->combined_type();
+ Representation combined_rep = Representation::FromType(combined_type);
+ Representation left_rep = Representation::FromType(left_type);
+ Representation right_rep = Representation::FromType(right_type);
+
+ CHECK_ALIVE(VisitForValue(expr->left()));
+ CHECK_ALIVE(VisitForValue(expr->right()));
+
+ HValue* context = environment()->context();
+ HValue* right = Pop();
+ HValue* left = Pop();
+ Token::Value op = expr->op();
+
+ if (IsLiteralCompareBool(left, op, right)) {
+ HCompareObjectEqAndBranch* result =
+ New<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
+ // same.
+ Handle<JSFunction> target = Handle<JSFunction>::null();
+ VariableProxy* proxy = expr->right()->AsVariableProxy();
+ bool global_function = (proxy != NULL) && proxy->var()->IsUnallocated();
+ if (global_function &&
+ current_info()->has_global_object() &&
+ !current_info()->global_object()->IsAccessCheckNeeded()) {
+ Handle<String> name = proxy->name();
+ Handle<GlobalObject> global(current_info()->global_object());
+ LookupResult lookup(isolate());
+ global->Lookup(*name, &lookup);
+ if (lookup.IsNormal() && lookup.GetValue()->IsJSFunction()) {
+ Handle<JSFunction> candidate(JSFunction::cast(lookup.GetValue()));
+ // If the function is in new space we assume it's more likely to
+ // change and thus prefer the general IC code.
+ if (!isolate()->heap()->InNewSpace(*candidate)) {
+ target = candidate;
+ }
+ }
+ }
+
+ // If the target is not null we have found a known global function that is
+ // assumed to stay the same for this instanceof.
+ if (target.is_null()) {
+ HInstanceOf* result = new(zone()) HInstanceOf(context, left, right);
+ result->set_position(expr->position());
+ return ast_context()->ReturnInstruction(result, expr->id());
+ } else {
+ Add<HCheckFunction>(right, target);
+ HInstanceOfKnownGlobal* result =
+ new(zone()) HInstanceOfKnownGlobal(context, left, target);
+ result->set_position(expr->position());
+ return ast_context()->ReturnInstruction(result, expr->id());
+ }
+
+ // Code below assumes that we don't fall through.
+ UNREACHABLE();
+ } else if (op == Token::IN) {
+ HValue* function = AddLoadJSBuiltin(Builtins::IN);
+ Add<HPushArgument>(left);
+ Add<HPushArgument>(right);
+ // TODO(olivf) InvokeFunction produces a check for the parameter count,
+ // even though we are certain to pass the correct number of arguments here.
+ HInstruction* result = new(zone()) HInvokeFunction(context, function, 2);
+ result->set_position(expr->position());
+ return ast_context()->ReturnInstruction(result, expr->id());
+ }
+
+ // Cases handled below depend on collected type feedback. They should
+ // soft deoptimize when there is no type feedback.
+ if (combined_type->Is(Type::None())) {
+ Add<HDeoptimize>("insufficient type feedback for combined type",
+ Deoptimizer::SOFT);
+ combined_type = left_type = right_type = handle(Type::Any(), isolate());
+ }
+
+ if (combined_type->Is(Type::Receiver())) {
+ switch (op) {
+ case Token::EQ:
+ case Token::EQ_STRICT: {
+ // Can we get away with map check and not instance type check?
+ if (combined_type->IsClass()) {
+ Handle<Map> map = combined_type->AsClass();
+ AddCheckMap(left, map);
+ AddCheckMap(right, map);
+ HCompareObjectEqAndBranch* result =
+ New<HCompareObjectEqAndBranch>(left, right);
+ result->set_position(expr->position());
+ return ast_context()->ReturnControl(result, expr->id());
+ } else {
+ BuildCheckHeapObject(left);
+ AddInstruction(HCheckInstanceType::NewIsSpecObject(left, zone()));
+ BuildCheckHeapObject(right);
+ AddInstruction(HCheckInstanceType::NewIsSpecObject(right, zone()));
+ HCompareObjectEqAndBranch* result =
+ new(zone()) HCompareObjectEqAndBranch(left, right);
+ result->set_position(expr->position());
+ return ast_context()->ReturnControl(result, expr->id());
+ }
+ }
+ default:
+ return Bailout(kUnsupportedNonPrimitiveCompare);
+ }
+ } else if (combined_type->Is(Type::InternalizedString()) &&
+ Token::IsEqualityOp(op)) {
+ BuildCheckHeapObject(left);
+ AddInstruction(HCheckInstanceType::NewIsInternalizedString(left, zone()));
+ BuildCheckHeapObject(right);
+ AddInstruction(HCheckInstanceType::NewIsInternalizedString(right, zone()));
+ HCompareObjectEqAndBranch* result =
+ new(zone()) HCompareObjectEqAndBranch(left, right);
+ result->set_position(expr->position());
+ return ast_context()->ReturnControl(result, expr->id());
+ } else {
+ if (combined_rep.IsTagged() || combined_rep.IsNone()) {
+ HCompareGeneric* result =
+ new(zone()) HCompareGeneric(context, left, right, op);
+ result->set_observed_input_representation(1, left_rep);
+ result->set_observed_input_representation(2, right_rep);
+ result->set_position(expr->position());
+ return ast_context()->ReturnInstruction(result, expr->id());
+ } else {
+ HCompareNumericAndBranch* result =
+ new(zone()) HCompareNumericAndBranch(left, right, op);
+ result->set_observed_input_representation(left_rep, right_rep);
+ result->set_position(expr->position());
+ return ast_context()->ReturnControl(result, expr->id());
+ }
+ }
+}
+
+
+void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
+ Expression* sub_expr,
+ NilValue nil) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ ASSERT(expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT);
+ CHECK_ALIVE(VisitForValue(sub_expr));
+ HValue* value = Pop();
+ HIfContinuation continuation;
+ if (expr->op() == Token::EQ_STRICT) {
+ IfBuilder if_nil(this);
+ if_nil.If<HCompareObjectEqAndBranch>(
+ value, (nil == kNullValue) ? graph()->GetConstantNull()
+ : graph()->GetConstantUndefined());
+ if_nil.Then();
+ if_nil.Else();
+ if_nil.CaptureContinuation(&continuation);
+ return ast_context()->ReturnContinuation(&continuation, expr->id());
+ }
+ Handle<Type> type = expr->combined_type()->Is(Type::None())
+ ? handle(Type::Any(), isolate_) : expr->combined_type();
+ BuildCompareNil(value, type, expr->position(), &continuation);
+ return ast_context()->ReturnContinuation(&continuation, expr->id());
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildThisFunction() {
+ // If we share optimized code between different closures, the
+ // this-function is not a constant, except inside an inlined body.
+ if (function_state()->outer() != NULL) {
+ return New<HConstant>(
+ function_state()->compilation_info()->closure());
+ } else {
+ return new(zone()) HThisFunction;
+ }
+}
+
+
+HInstruction* HOptimizedGraphBuilder::BuildFastLiteral(
+ HValue* context,
+ Handle<JSObject> boilerplate_object,
+ Handle<JSObject> original_boilerplate_object,
+ Handle<Object> allocation_site,
+ int data_size,
+ int pointer_size,
+ AllocationSiteMode mode) {
+ NoObservableSideEffectsScope no_effects(this);
+
+ HInstruction* target = NULL;
+ HInstruction* data_target = NULL;
+
+ if (isolate()->heap()->GetPretenureMode() == TENURED) {
+ if (data_size != 0) {
+ HValue* size_in_bytes = Add<HConstant>(data_size);
+ data_target = Add<HAllocate>(size_in_bytes, HType::JSObject(), TENURED,
+ FIXED_DOUBLE_ARRAY_TYPE);
+ Handle<Map> free_space_map = isolate()->factory()->free_space_map();
+ AddStoreMapConstant(data_target, free_space_map);
+ HObjectAccess access =
+ HObjectAccess::ForJSObjectOffset(FreeSpace::kSizeOffset);
+ Add<HStoreNamedField>(data_target, access, size_in_bytes);
+ }
+ if (pointer_size != 0) {
+ HValue* size_in_bytes = Add<HConstant>(pointer_size);
+ target = Add<HAllocate>(size_in_bytes, HType::JSObject(), TENURED,
+ JS_OBJECT_TYPE);
+ }
+ } else {
+ InstanceType instance_type = boilerplate_object->map()->instance_type();
+ HValue* size_in_bytes = Add<HConstant>(data_size + pointer_size);
+ target = Add<HAllocate>(size_in_bytes, HType::JSObject(), NOT_TENURED,
+ instance_type);
+ }
+
+ int offset = 0;
+ int data_offset = 0;
+ BuildEmitDeepCopy(boilerplate_object, original_boilerplate_object,
+ allocation_site, target, &offset, data_target,
+ &data_offset, mode);
+ return target;
+}
+
+
+void HOptimizedGraphBuilder::BuildEmitDeepCopy(
+ Handle<JSObject> boilerplate_object,
+ Handle<JSObject> original_boilerplate_object,
+ Handle<Object> allocation_site_object,
+ HInstruction* target,
+ int* offset,
+ HInstruction* data_target,
+ int* data_offset,
+ AllocationSiteMode mode) {
+ bool create_allocation_site_info = mode == TRACK_ALLOCATION_SITE &&
+ boilerplate_object->map()->CanTrackAllocationSite();
+
+ // If using allocation sites, then the payload on the site should already
+ // be filled in as a valid (boilerplate) array.
+ ASSERT(!create_allocation_site_info ||
+ AllocationSite::cast(*allocation_site_object)->IsLiteralSite());
+
+ HInstruction* allocation_site = NULL;
+
+ if (create_allocation_site_info) {
+ allocation_site = Add<HConstant>(allocation_site_object);
+ }
+
+ // Only elements backing stores for non-COW arrays need to be copied.
+ Handle<FixedArrayBase> elements(boilerplate_object->elements());
+ Handle<FixedArrayBase> original_elements(
+ original_boilerplate_object->elements());
+ ElementsKind kind = boilerplate_object->map()->elements_kind();
+
+ int object_offset = *offset;
+ int object_size = boilerplate_object->map()->instance_size();
+ int elements_size = (elements->length() > 0 &&
+ elements->map() != isolate()->heap()->fixed_cow_array_map()) ?
+ elements->Size() : 0;
+ int elements_offset = 0;
+
+ if (data_target != NULL && boilerplate_object->HasFastDoubleElements()) {
+ elements_offset = *data_offset;
+ *data_offset += elements_size;
+ } else {
+ // Place elements right after this object.
+ elements_offset = *offset + object_size;
+ *offset += elements_size;
+ }
+ // Increase the offset so that subsequent objects end up right after this
+ // object (and it's elements if they are allocated in the same space).
+ *offset += object_size;
+
+ // Copy object elements if non-COW.
+ HValue* object_elements = BuildEmitObjectHeader(boilerplate_object, target,
+ data_target, object_offset, elements_offset, elements_size);
+ if (object_elements != NULL) {
+ BuildEmitElements(elements, original_elements, kind, object_elements,
+ target, offset, data_target, data_offset);
+ }
+
+ // Copy in-object properties.
+ if (boilerplate_object->map()->NumberOfFields() != 0) {
+ HValue* object_properties =
+ Add<HInnerAllocatedObject>(target, object_offset);
+ BuildEmitInObjectProperties(boilerplate_object, original_boilerplate_object,
+ object_properties, target, offset, data_target, data_offset);
+ }
+
+ // Create allocation site info.
+ if (mode == TRACK_ALLOCATION_SITE &&
+ boilerplate_object->map()->CanTrackAllocationSite()) {
+ elements_offset += AllocationMemento::kSize;
+ *offset += AllocationMemento::kSize;
+ BuildCreateAllocationMemento(target, JSArray::kSize, allocation_site);
+ }
+}
+
+
+HValue* HOptimizedGraphBuilder::BuildEmitObjectHeader(
+ Handle<JSObject> boilerplate_object,
+ HInstruction* target,
+ HInstruction* data_target,
+ int object_offset,
+ int elements_offset,
+ int elements_size) {
+ ASSERT(boilerplate_object->properties()->length() == 0);
+ HValue* result = NULL;
+
+ HValue* object_header = Add<HInnerAllocatedObject>(target, object_offset);
+ Handle<Map> boilerplate_object_map(boilerplate_object->map());
+ AddStoreMapConstant(object_header, boilerplate_object_map);
+
+ HInstruction* elements;
+ if (elements_size == 0) {
+ Handle<Object> elements_field =
+ Handle<Object>(boilerplate_object->elements(), isolate());
+ elements = Add<HConstant>(elements_field);
+ } else {
+ if (data_target != NULL && boilerplate_object->HasFastDoubleElements()) {
+ elements = Add<HInnerAllocatedObject>(data_target, elements_offset);
+ } else {
+ elements = Add<HInnerAllocatedObject>(target, elements_offset);
+ }
+ result = elements;
+ }
+ Add<HStoreNamedField>(object_header, HObjectAccess::ForElementsPointer(),
+ elements);
+
+ Handle<Object> properties_field =
+ Handle<Object>(boilerplate_object->properties(), isolate());
+ ASSERT(*properties_field == isolate()->heap()->empty_fixed_array());
+ HInstruction* properties = Add<HConstant>(properties_field);
+ HObjectAccess access = HObjectAccess::ForPropertiesPointer();
+ Add<HStoreNamedField>(object_header, access, properties);
+
+ if (boilerplate_object->IsJSArray()) {
+ Handle<JSArray> boilerplate_array =
+ Handle<JSArray>::cast(boilerplate_object);
+ Handle<Object> length_field =
+ Handle<Object>(boilerplate_array->length(), isolate());
+ HInstruction* length = Add<HConstant>(length_field);
+
+ ASSERT(boilerplate_array->length()->IsSmi());
+ Add<HStoreNamedField>(object_header, HObjectAccess::ForArrayLength(
+ boilerplate_array->GetElementsKind()), length);
+ }
+
+ return result;
+}
+
+
+void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
+ Handle<JSObject> boilerplate_object,
+ Handle<JSObject> original_boilerplate_object,
+ HValue* object_properties,
+ HInstruction* target,
+ int* offset,
+ HInstruction* data_target,
+ int* data_offset) {
+ Handle<DescriptorArray> descriptors(
+ boilerplate_object->map()->instance_descriptors());
+ int limit = boilerplate_object->map()->NumberOfOwnDescriptors();
+
+ int copied_fields = 0;
+ for (int i = 0; i < limit; i++) {
+ PropertyDetails details = descriptors->GetDetails(i);
+ if (details.type() != FIELD) continue;
+ copied_fields++;
+ int index = descriptors->GetFieldIndex(i);
+ int property_offset = boilerplate_object->GetInObjectPropertyOffset(index);
+ Handle<Name> name(descriptors->GetKey(i));
+ Handle<Object> value =
+ Handle<Object>(boilerplate_object->InObjectPropertyAt(index),
+ isolate());
+
+ // The access for the store depends on the type of the boilerplate.
+ HObjectAccess access = boilerplate_object->IsJSArray() ?
+ HObjectAccess::ForJSArrayOffset(property_offset) :
+ HObjectAccess::ForJSObjectOffset(property_offset);
+
+ if (value->IsJSObject()) {
+ Handle<JSObject> value_object = Handle<JSObject>::cast(value);
+ Handle<JSObject> original_value_object = Handle<JSObject>::cast(
+ Handle<Object>(original_boilerplate_object->InObjectPropertyAt(index),
+ isolate()));
+ HInstruction* value_instruction = Add<HInnerAllocatedObject>(target,
+ *offset);
+
+ Add<HStoreNamedField>(object_properties, access, value_instruction);
+ BuildEmitDeepCopy(value_object, original_value_object,
+ Handle<Object>::null(), target,
+ offset, data_target, data_offset,
+ DONT_TRACK_ALLOCATION_SITE);
+ } else {
+ Representation representation = details.representation();
+ HInstruction* value_instruction = Add<HConstant>(value);
+
+ if (representation.IsDouble()) {
+ // Allocate a HeapNumber box and store the value into it.
+ HInstruction* double_box;
+ if (data_target != NULL) {
+ double_box = Add<HInnerAllocatedObject>(data_target, *data_offset);
+ *data_offset += HeapNumber::kSize;
+ } else {
+ double_box = Add<HInnerAllocatedObject>(target, *offset);
+ *offset += HeapNumber::kSize;
+ }
+ AddStoreMapConstant(double_box,
+ isolate()->factory()->heap_number_map());
+ Add<HStoreNamedField>(double_box, HObjectAccess::ForHeapNumberValue(),
+ value_instruction);
+ value_instruction = double_box;
+ }
+
+ Add<HStoreNamedField>(object_properties, access, value_instruction);
+ }
+ }
+
+ int inobject_properties = boilerplate_object->map()->inobject_properties();
+ HInstruction* value_instruction =
+ Add<HConstant>(isolate()->factory()->one_pointer_filler_map());
+ for (int i = copied_fields; i < inobject_properties; i++) {
+ ASSERT(boilerplate_object->IsJSObject());
+ int property_offset = boilerplate_object->GetInObjectPropertyOffset(i);
+ HObjectAccess access = HObjectAccess::ForJSObjectOffset(property_offset);
+ Add<HStoreNamedField>(object_properties, access, value_instruction);
+ }
+}
+
+
+void HOptimizedGraphBuilder::BuildEmitElements(
+ Handle<FixedArrayBase> elements,
+ Handle<FixedArrayBase> original_elements,
+ ElementsKind kind,
+ HValue* object_elements,
+ HInstruction* target,
+ int* offset,
+ HInstruction* data_target,
+ int* data_offset) {
+ int elements_length = elements->length();
+ HValue* object_elements_length = Add<HConstant>(elements_length);
+
+ BuildInitializeElementsHeader(object_elements, kind, object_elements_length);
+
+ // Copy elements backing store content.
+ if (elements->IsFixedDoubleArray()) {
+ BuildEmitFixedDoubleArray(elements, kind, object_elements);
+ } else if (elements->IsFixedArray()) {
+ BuildEmitFixedArray(elements, original_elements, kind, object_elements,
+ target, offset, data_target, data_offset);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void HOptimizedGraphBuilder::BuildEmitFixedDoubleArray(
+ Handle<FixedArrayBase> elements,
+ ElementsKind kind,
+ HValue* object_elements) {
+ HInstruction* boilerplate_elements = Add<HConstant>(elements);
+ int elements_length = elements->length();
+ for (int i = 0; i < elements_length; i++) {
+ HValue* key_constant = Add<HConstant>(i);
+ HInstruction* value_instruction =
+ Add<HLoadKeyed>(boilerplate_elements, key_constant,
+ static_cast<HValue*>(NULL), kind,
+ ALLOW_RETURN_HOLE);
+ HInstruction* store = Add<HStoreKeyed>(object_elements, key_constant,
+ value_instruction, kind);
+ store->SetFlag(HValue::kAllowUndefinedAsNaN);
+ }
+}
+
+
+void HOptimizedGraphBuilder::BuildEmitFixedArray(
+ Handle<FixedArrayBase> elements,
+ Handle<FixedArrayBase> original_elements,
+ ElementsKind kind,
+ HValue* object_elements,
+ HInstruction* target,
+ int* offset,
+ HInstruction* data_target,
+ int* data_offset) {
+ HInstruction* boilerplate_elements = Add<HConstant>(elements);
+ int elements_length = elements->length();
+ Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
+ Handle<FixedArray> original_fast_elements =
+ Handle<FixedArray>::cast(original_elements);
+ for (int i = 0; i < elements_length; i++) {
+ Handle<Object> value(fast_elements->get(i), isolate());
+ HValue* key_constant = Add<HConstant>(i);
+ if (value->IsJSObject()) {
+ Handle<JSObject> value_object = Handle<JSObject>::cast(value);
+ Handle<JSObject> original_value_object = Handle<JSObject>::cast(
+ Handle<Object>(original_fast_elements->get(i), isolate()));
+ HInstruction* value_instruction = Add<HInnerAllocatedObject>(target,
+ *offset);
+ Add<HStoreKeyed>(object_elements, key_constant, value_instruction, kind);
+ BuildEmitDeepCopy(value_object, original_value_object,
+ Handle<Object>::null(), target,
+ offset, data_target, data_offset,
+ DONT_TRACK_ALLOCATION_SITE);
+ } else {
+ HInstruction* value_instruction =
+ Add<HLoadKeyed>(boilerplate_elements, key_constant,
+ static_cast<HValue*>(NULL), kind,
+ ALLOW_RETURN_HOLE);
+ Add<HStoreKeyed>(object_elements, key_constant, value_instruction, kind);
+ }
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitThisFunction(ThisFunction* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ HInstruction* instr = BuildThisFunction();
+ return ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HOptimizedGraphBuilder::VisitDeclarations(
+ ZoneList<Declaration*>* declarations) {
+ ASSERT(globals_.is_empty());
+ AstVisitor::VisitDeclarations(declarations);
+ if (!globals_.is_empty()) {
+ Handle<FixedArray> array =
+ isolate()->factory()->NewFixedArray(globals_.length(), TENURED);
+ for (int i = 0; i < globals_.length(); ++i) array->set(i, *globals_.at(i));
+ int flags = DeclareGlobalsEvalFlag::encode(current_info()->is_eval()) |
+ DeclareGlobalsNativeFlag::encode(current_info()->is_native()) |
+ DeclareGlobalsLanguageMode::encode(current_info()->language_mode());
+ Add<HDeclareGlobals>(array, flags);
+ globals_.Clear();
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitVariableDeclaration(
+ VariableDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ VariableMode mode = declaration->mode();
+ Variable* variable = proxy->var();
+ bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ globals_.Add(variable->name(), zone());
+ globals_.Add(variable->binding_needs_init()
+ ? isolate()->factory()->the_hole_value()
+ : isolate()->factory()->undefined_value(), zone());
+ return;
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ if (hole_init) {
+ HValue* value = graph()->GetConstantHole();
+ environment()->Bind(variable, value);
+ }
+ break;
+ case Variable::CONTEXT:
+ if (hole_init) {
+ HValue* value = graph()->GetConstantHole();
+ HValue* context = environment()->context();
+ HStoreContextSlot* store = Add<HStoreContextSlot>(
+ context, variable->index(), HStoreContextSlot::kNoCheck, value);
+ if (store->HasObservableSideEffects()) {
+ Add<HSimulate>(proxy->id(), REMOVABLE_SIMULATE);
+ }
+ }
+ break;
+ case Variable::LOOKUP:
+ return Bailout(kUnsupportedLookupSlotInDeclaration);
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitFunctionDeclaration(
+ FunctionDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED: {
+ globals_.Add(variable->name(), zone());
+ Handle<SharedFunctionInfo> function = Compiler::BuildFunctionInfo(
+ declaration->fun(), current_info()->script());
+ // Check for stack-overflow exception.
+ if (function.is_null()) return SetStackOverflow();
+ globals_.Add(function, zone());
+ return;
+ }
+ case Variable::PARAMETER:
+ case Variable::LOCAL: {
+ CHECK_ALIVE(VisitForValue(declaration->fun()));
+ HValue* value = Pop();
+ BindIfLive(variable, value);
+ break;
+ }
+ case Variable::CONTEXT: {
+ CHECK_ALIVE(VisitForValue(declaration->fun()));
+ HValue* value = Pop();
+ HValue* context = environment()->context();
+ HStoreContextSlot* store = Add<HStoreContextSlot>(
+ context, variable->index(), HStoreContextSlot::kNoCheck, value);
+ if (store->HasObservableSideEffects()) {
+ Add<HSimulate>(proxy->id(), REMOVABLE_SIMULATE);
+ }
+ break;
+ }
+ case Variable::LOOKUP:
+ return Bailout(kUnsupportedLookupSlotInDeclaration);
+ }
+}
+
+
+void HOptimizedGraphBuilder::VisitModuleDeclaration(
+ ModuleDeclaration* declaration) {
+ UNREACHABLE();
+}
+
+
+void HOptimizedGraphBuilder::VisitImportDeclaration(
+ ImportDeclaration* declaration) {
+ UNREACHABLE();
+}
+
+
+void HOptimizedGraphBuilder::VisitExportDeclaration(
+ ExportDeclaration* declaration) {
+ UNREACHABLE();
+}
+
+
+void HOptimizedGraphBuilder::VisitModuleLiteral(ModuleLiteral* module) {
+ UNREACHABLE();
+}
+
+
+void HOptimizedGraphBuilder::VisitModuleVariable(ModuleVariable* module) {
+ UNREACHABLE();
+}
+
+
+void HOptimizedGraphBuilder::VisitModulePath(ModulePath* module) {
+ UNREACHABLE();
+}
+
+
+void HOptimizedGraphBuilder::VisitModuleUrl(ModuleUrl* module) {
+ UNREACHABLE();
+}
+
+
+void HOptimizedGraphBuilder::VisitModuleStatement(ModuleStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+// Generators for inline runtime functions.
+// Support for types.
+void HOptimizedGraphBuilder::GenerateIsSmi(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HIsSmiAndBranch* result = new(zone()) HIsSmiAndBranch(value);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HHasInstanceTypeAndBranch* result =
+ new(zone()) HHasInstanceTypeAndBranch(value,
+ FIRST_SPEC_OBJECT_TYPE,
+ LAST_SPEC_OBJECT_TYPE);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateIsFunction(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HHasInstanceTypeAndBranch* result =
+ new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HHasCachedArrayIndexAndBranch* result =
+ new(zone()) HHasCachedArrayIndexAndBranch(value);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateIsArray(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HHasInstanceTypeAndBranch* result =
+ new(zone()) HHasInstanceTypeAndBranch(value, JS_ARRAY_TYPE);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HHasInstanceTypeAndBranch* result =
+ new(zone()) HHasInstanceTypeAndBranch(value, JS_REGEXP_TYPE);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateIsObject(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HIsObjectAndBranch* result = new(zone()) HIsObjectAndBranch(value);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
+ return Bailout(kInlinedRuntimeFunctionIsNonNegativeSmi);
+}
+
+
+void HOptimizedGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HIsUndetectableAndBranch* result =
+ new(zone()) HIsUndetectableAndBranch(value);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
+ CallRuntime* call) {
+ return Bailout(kInlinedRuntimeFunctionIsStringWrapperSafeForDefaultValueOf);
+}
+
+
+// Support for construct call checks.
+void HOptimizedGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 0);
+ if (function_state()->outer() != NULL) {
+ // We are generating graph for inlined function.
+ HValue* value = function_state()->inlining_kind() == CONSTRUCT_CALL_RETURN
+ ? graph()->GetConstantTrue()
+ : graph()->GetConstantFalse();
+ return ast_context()->ReturnValue(value);
+ } else {
+ return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
+ call->id());
+ }
+}
+
+
+// Support for arguments.length and arguments[?].
+void HOptimizedGraphBuilder::GenerateArgumentsLength(CallRuntime* call) {
+ // Our implementation of arguments (based on this stack frame or an
+ // adapter below it) does not work for inlined functions. This runtime
+ // function is blacklisted by AstNode::IsInlineable.
+ ASSERT(function_state()->outer() == NULL);
+ ASSERT(call->arguments()->length() == 0);
+ HInstruction* elements = Add<HArgumentsElements>(false);
+ HArgumentsLength* result = New<HArgumentsLength>(elements);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateArguments(CallRuntime* call) {
+ // Our implementation of arguments (based on this stack frame or an
+ // adapter below it) does not work for inlined functions. This runtime
+ // function is blacklisted by AstNode::IsInlineable.
+ ASSERT(function_state()->outer() == NULL);
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* index = Pop();
+ HInstruction* elements = Add<HArgumentsElements>(false);
+ HInstruction* length = Add<HArgumentsLength>(elements);
+ HInstruction* checked_index = Add<HBoundsCheck>(index, length);
+ HAccessArgumentsAt* result =
+ new(zone()) HAccessArgumentsAt(elements, length, checked_index);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Support for accessing the class and value fields of an object.
+void HOptimizedGraphBuilder::GenerateClassOf(CallRuntime* call) {
+ // The special form detected by IsClassOfTest is detected before we get here
+ // and does not cause a bailout.
+ return Bailout(kInlinedRuntimeFunctionClassOf);
+}
+
+
+void HOptimizedGraphBuilder::GenerateValueOf(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HValueOf* result = new(zone()) HValueOf(value);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateDateField(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 2);
+ ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
+ Smi* index = Smi::cast(*(call->arguments()->at(1)->AsLiteral()->value()));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* date = Pop();
+ HDateField* result = new(zone()) HDateField(date, index);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateOneByteSeqStringSetChar(
+ CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 3);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
+ HValue* value = Pop();
+ HValue* index = Pop();
+ HValue* string = Pop();
+ HSeqStringSetChar* result = new(zone()) HSeqStringSetChar(
+ String::ONE_BYTE_ENCODING, string, index, value);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateTwoByteSeqStringSetChar(
+ CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 3);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
+ HValue* value = Pop();
+ HValue* index = Pop();
+ HValue* string = Pop();
+ HSeqStringSetChar* result = new(zone()) HSeqStringSetChar(
+ String::TWO_BYTE_ENCODING, string, index, value);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
+ 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);
+ Add<HStoreNamedField>(object,
+ HObjectAccess::ForJSObjectOffset(JSValue::kValueOffset), value);
+ if_js_value->Goto(join);
+ join->SetJoinId(call->id());
+ set_current_block(join);
+ return ast_context()->ReturnValue(value);
+}
+
+
+// Fast support for charCodeAt(n).
+void HOptimizedGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 2);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
+ HValue* index = Pop();
+ HValue* string = Pop();
+ HInstruction* result = BuildStringCharCodeAt(string, index);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Fast support for string.charAt(n) and string[n].
+void HOptimizedGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* char_code = Pop();
+ HInstruction* result = New<HStringCharFromCode>(char_code);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Fast support for string.charAt(n) and string[n].
+void HOptimizedGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 2);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
+ HValue* index = Pop();
+ HValue* string = Pop();
+ HInstruction* char_code = BuildStringCharCodeAt(string, index);
+ AddInstruction(char_code);
+ HInstruction* result = New<HStringCharFromCode>(char_code);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Fast support for object equality testing.
+void HOptimizedGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 2);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
+ HValue* right = Pop();
+ HValue* left = Pop();
+ HCompareObjectEqAndBranch* result =
+ New<HCompareObjectEqAndBranch>(left, right);
+ return ast_context()->ReturnControl(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateLog(CallRuntime* call) {
+ // %_Log is ignored in optimized code.
+ return ast_context()->ReturnValue(graph()->GetConstantUndefined());
+}
+
+
+// Fast support for Math.random().
+void HOptimizedGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
+ HGlobalObject* global_object = Add<HGlobalObject>();
+ HRandom* result = new(zone()) HRandom(global_object);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Fast support for StringAdd.
+void HOptimizedGraphBuilder::GenerateStringAdd(CallRuntime* call) {
+ ASSERT_EQ(2, call->arguments()->length());
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
+ HValue* right = Pop();
+ HValue* left = Pop();
+ HValue* context = environment()->context();
+ HInstruction* result = HStringAdd::New(
+ zone(), context, left, right, STRING_ADD_CHECK_BOTH);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Fast support for SubString.
+void HOptimizedGraphBuilder::GenerateSubString(CallRuntime* call) {
+ ASSERT_EQ(3, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
+ Drop(3);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Fast support for StringCompare.
+void HOptimizedGraphBuilder::GenerateStringCompare(CallRuntime* call) {
+ ASSERT_EQ(2, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
+ Drop(2);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Support for direct calls from JavaScript to native RegExp code.
+void HOptimizedGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
+ ASSERT_EQ(4, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
+ Drop(4);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Construct a RegExp exec result with two in-object properties.
+void HOptimizedGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
+ ASSERT_EQ(3, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
+ Drop(3);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Support for fast native caches.
+void HOptimizedGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
+ return Bailout(kInlinedRuntimeFunctionGetFromCache);
+}
+
+
+// Fast support for number to string.
+void HOptimizedGraphBuilder::GenerateNumberToString(CallRuntime* call) {
+ ASSERT_EQ(1, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
+ Drop(1);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Fast call for custom callbacks.
+void HOptimizedGraphBuilder::GenerateCallFunction(CallRuntime* call) {
+ // 1 ~ The function to call is not itself an argument to the call.
+ int arg_count = call->arguments()->length() - 1;
+ ASSERT(arg_count >= 1); // There's always at least a receiver.
+
+ for (int i = 0; i < arg_count; ++i) {
+ CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
+ }
+ CHECK_ALIVE(VisitForValue(call->arguments()->last()));
+
+ HValue* function = Pop();
+
+ // 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 = Add<HInvokeFunction>(function, arg_count);
+ Drop(arg_count);
+ Push(invoke_result);
+ if_jsfunction->Goto(join);
+
+ set_current_block(if_nonfunction);
+ HInstruction* call_result = Add<HCallFunction>(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());
+}
+
+
+// Fast call to math functions.
+void HOptimizedGraphBuilder::GenerateMathPow(CallRuntime* call) {
+ ASSERT_EQ(2, call->arguments()->length());
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
+ HValue* right = Pop();
+ HValue* left = Pop();
+ HInstruction* result = HPower::New(zone(), context(), left, right);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateMathSin(CallRuntime* call) {
+ ASSERT_EQ(1, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
+ result->set_transcendental_type(TranscendentalCache::SIN);
+ Drop(1);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateMathCos(CallRuntime* call) {
+ ASSERT_EQ(1, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
+ result->set_transcendental_type(TranscendentalCache::COS);
+ Drop(1);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateMathTan(CallRuntime* call) {
+ ASSERT_EQ(1, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ 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 HOptimizedGraphBuilder::GenerateMathLog(CallRuntime* call) {
+ ASSERT_EQ(1, call->arguments()->length());
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->context();
+ HCallStub* result =
+ new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
+ result->set_transcendental_type(TranscendentalCache::LOG);
+ Drop(1);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HValue* context = environment()->context();
+ HInstruction* result =
+ HUnaryMathOperation::New(zone(), context, value, kMathSqrt);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+// Check whether two RegExps are equivalent
+void HOptimizedGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
+ return Bailout(kInlinedRuntimeFunctionIsRegExpEquivalent);
+}
+
+
+void HOptimizedGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
+ return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
+void HOptimizedGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
+ return Bailout(kInlinedRuntimeFunctionFastAsciiArrayJoin);
+}
+
+
+// Support for generators.
+void HOptimizedGraphBuilder::GenerateGeneratorNext(CallRuntime* call) {
+ return Bailout(kInlinedRuntimeFunctionGeneratorNext);
+}
+
+
+void HOptimizedGraphBuilder::GenerateGeneratorThrow(CallRuntime* call) {
+ return Bailout(kInlinedRuntimeFunctionGeneratorThrow);
+}
+
+
+void HOptimizedGraphBuilder::GenerateDebugBreakInOptimizedCode(
+ CallRuntime* call) {
+ AddInstruction(new(zone()) HDebugBreak());
+ return ast_context()->ReturnValue(graph()->GetConstant0());
+}
+
+
+#undef CHECK_BAILOUT
+#undef CHECK_ALIVE
+
+
+HEnvironment::HEnvironment(HEnvironment* outer,
+ Scope* scope,
+ Handle<JSFunction> closure,
+ Zone* zone)
+ : closure_(closure),
+ values_(0, zone),
+ frame_type_(JS_FUNCTION),
+ parameter_count_(0),
+ specials_count_(1),
+ local_count_(0),
+ outer_(outer),
+ entry_(NULL),
+ pop_count_(0),
+ push_count_(0),
+ ast_id_(BailoutId::None()),
+ zone_(zone) {
+ Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
+}
+
+
+HEnvironment::HEnvironment(Zone* zone, int parameter_count)
+ : values_(0, zone),
+ frame_type_(STUB),
+ parameter_count_(parameter_count),
+ specials_count_(1),
+ local_count_(0),
+ outer_(NULL),
+ entry_(NULL),
+ pop_count_(0),
+ push_count_(0),
+ ast_id_(BailoutId::None()),
+ zone_(zone) {
+ Initialize(parameter_count, 0, 0);
+}
+
+
+HEnvironment::HEnvironment(const HEnvironment* other, Zone* zone)
+ : values_(0, zone),
+ frame_type_(JS_FUNCTION),
+ parameter_count_(0),
+ specials_count_(0),
+ local_count_(0),
+ outer_(NULL),
+ entry_(NULL),
+ pop_count_(0),
+ push_count_(0),
+ ast_id_(other->ast_id()),
+ zone_(zone) {
+ Initialize(other);
+}
+
+
+HEnvironment::HEnvironment(HEnvironment* outer,
+ Handle<JSFunction> closure,
+ FrameType frame_type,
+ int arguments,
+ Zone* zone)
+ : closure_(closure),
+ values_(arguments, zone),
+ frame_type_(frame_type),
+ parameter_count_(arguments),
+ specials_count_(0),
+ local_count_(0),
+ outer_(outer),
+ entry_(NULL),
+ pop_count_(0),
+ push_count_(0),
+ ast_id_(BailoutId::None()),
+ zone_(zone) {
+}
+
+
+void HEnvironment::Initialize(int parameter_count,
+ int local_count,
+ int stack_height) {
+ parameter_count_ = parameter_count;
+ local_count_ = local_count;
+
+ // Avoid reallocating the temporaries' backing store on the first Push.
+ int total = parameter_count + specials_count_ + local_count + stack_height;
+ values_.Initialize(total + 4, zone());
+ for (int i = 0; i < total; ++i) values_.Add(NULL, zone());
+}
+
+
+void HEnvironment::Initialize(const HEnvironment* other) {
+ closure_ = other->closure();
+ values_.AddAll(other->values_, zone());
+ assigned_variables_.Union(other->assigned_variables_, zone());
+ frame_type_ = other->frame_type_;
+ parameter_count_ = other->parameter_count_;
+ local_count_ = other->local_count_;
+ if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
+ entry_ = other->entry_;
+ pop_count_ = other->pop_count_;
+ push_count_ = other->push_count_;
+ specials_count_ = other->specials_count_;
+ ast_id_ = other->ast_id_;
+}
+
+
+void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) {
+ ASSERT(!block->IsLoopHeader());
+ ASSERT(values_.length() == other->values_.length());
+
+ int length = values_.length();
+ for (int i = 0; i < length; ++i) {
+ HValue* value = values_[i];
+ if (value != NULL && value->IsPhi() && value->block() == block) {
+ // There is already a phi for the i'th value.
+ HPhi* phi = HPhi::cast(value);
+ // Assert index is correct and that we haven't missed an incoming edge.
+ ASSERT(phi->merged_index() == i || !phi->HasMergedIndex());
+ ASSERT(phi->OperandCount() == block->predecessors()->length());
+ phi->AddInput(other->values_[i]);
+ } else if (values_[i] != other->values_[i]) {
+ // There is a fresh value on the incoming edge, a phi is needed.
+ ASSERT(values_[i] != NULL && other->values_[i] != NULL);
+ HPhi* phi = block->AddNewPhi(i);
+ HValue* old_value = values_[i];
+ for (int j = 0; j < block->predecessors()->length(); j++) {
+ phi->AddInput(old_value);
+ }
+ phi->AddInput(other->values_[i]);
+ this->values_[i] = phi;
+ }
+ }
+}
+
+
+void HEnvironment::Bind(int index, HValue* value) {
+ ASSERT(value != NULL);
+ assigned_variables_.Add(index, zone());
+ values_[index] = value;
+}
+
+
+bool HEnvironment::HasExpressionAt(int index) const {
+ return index >= parameter_count_ + specials_count_ + local_count_;
+}
+
+
+bool HEnvironment::ExpressionStackIsEmpty() const {
+ ASSERT(length() >= first_expression_index());
+ return length() == first_expression_index();
+}
+
+
+void HEnvironment::SetExpressionStackAt(int index_from_top, HValue* value) {
+ int count = index_from_top + 1;
+ int index = values_.length() - count;
+ ASSERT(HasExpressionAt(index));
+ // The push count must include at least the element in question or else
+ // the new value will not be included in this environment's history.
+ if (push_count_ < count) {
+ // This is the same effect as popping then re-pushing 'count' elements.
+ pop_count_ += (count - push_count_);
+ push_count_ = count;
+ }
+ values_[index] = value;
+}
+
+
+void HEnvironment::Drop(int count) {
+ for (int i = 0; i < count; ++i) {
+ Pop();
+ }
+}
+
+
+HEnvironment* HEnvironment::Copy() const {
+ return new(zone()) HEnvironment(this, zone());
+}
+
+
+HEnvironment* HEnvironment::CopyWithoutHistory() const {
+ HEnvironment* result = Copy();
+ result->ClearHistory();
+ return result;
+}
+
+
+HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
+ HEnvironment* new_env = Copy();
+ for (int i = 0; i < values_.length(); ++i) {
+ HPhi* phi = loop_header->AddNewPhi(i);
+ phi->AddInput(values_[i]);
+ new_env->values_[i] = phi;
+ }
+ new_env->ClearHistory();
+ return new_env;
+}
+
+
+HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer,
+ Handle<JSFunction> target,
+ FrameType frame_type,
+ int arguments) const {
+ HEnvironment* new_env =
+ new(zone()) HEnvironment(outer, target, frame_type,
+ arguments + 1, zone());
+ for (int i = 0; i <= arguments; ++i) { // Include receiver.
+ new_env->Push(ExpressionStackAt(arguments - i));
+ }
+ new_env->ClearHistory();
+ return new_env;
+}
+
+
+HEnvironment* HEnvironment::CopyForInlining(
+ Handle<JSFunction> target,
+ int arguments,
+ FunctionLiteral* function,
+ HConstant* undefined,
+ InliningKind inlining_kind,
+ bool undefined_receiver) const {
+ ASSERT(frame_type() == JS_FUNCTION);
+
+ // Outer environment is a copy of this one without the arguments.
+ int arity = function->scope()->num_parameters();
+
+ HEnvironment* outer = Copy();
+ outer->Drop(arguments + 1); // Including receiver.
+ outer->ClearHistory();
+
+ if (inlining_kind == CONSTRUCT_CALL_RETURN) {
+ // Create artificial constructor stub environment. The receiver should
+ // actually be the constructor function, but we pass the newly allocated
+ // object instead, DoComputeConstructStubFrame() relies on that.
+ outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments);
+ } else if (inlining_kind == GETTER_CALL_RETURN) {
+ // We need an additional StackFrame::INTERNAL frame for restoring the
+ // correct context.
+ outer = CreateStubEnvironment(outer, target, JS_GETTER, arguments);
+ } else if (inlining_kind == SETTER_CALL_RETURN) {
+ // We need an additional StackFrame::INTERNAL frame for temporarily saving
+ // the argument of the setter, see StoreStubCompiler::CompileStoreViaSetter.
+ outer = CreateStubEnvironment(outer, target, JS_SETTER, arguments);
+ }
+
+ if (arity != arguments) {
+ // Create artificial arguments adaptation environment.
+ outer = CreateStubEnvironment(outer, target, ARGUMENTS_ADAPTOR, arguments);
+ }
+
+ HEnvironment* inner =
+ new(zone()) HEnvironment(outer, function->scope(), target, zone());
+ // Get the argument values from the original environment.
+ for (int i = 0; i <= arity; ++i) { // Include receiver.
+ 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 (undefined_receiver) {
+ inner->SetValueAt(0, undefined);
+ }
+ inner->SetValueAt(arity + 1, context());
+ for (int i = arity + 2; i < inner->length(); ++i) {
+ inner->SetValueAt(i, undefined);
+ }
+
+ inner->set_ast_id(BailoutId::FunctionEntry());
+ return inner;
+}
+
+
+void HEnvironment::PrintTo(StringStream* stream) {
+ for (int i = 0; i < length(); i++) {
+ if (i == 0) stream->Add("parameters\n");
+ 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\n");
+ }
+ HValue* val = values_.at(i);
+ stream->Add("%d: ", i);
+ if (val != NULL) {
+ val->PrintNameTo(stream);
+ } else {
+ stream->Add("NULL");
+ }
+ stream->Add("\n");
+ }
+ PrintF("\n");
+}
+
+
+void HEnvironment::PrintToStd() {
+ HeapStringAllocator string_allocator;
+ StringStream trace(&string_allocator);
+ PrintTo(&trace);
+ PrintF("%s", *trace.ToCString());
+}
+
+
+void HTracer::TraceCompilation(CompilationInfo* info) {
+ Tag tag(this, "compilation");
+ if (info->IsOptimizing()) {
+ Handle<String> name = info->function()->debug_name();
+ PrintStringProperty("name", *name->ToCString());
+ PrintStringProperty("method", *name->ToCString());
+ } else {
+ CodeStub::Major major_key = info->code_stub()->MajorKey();
+ PrintStringProperty("name", CodeStub::MajorName(major_key, false));
+ PrintStringProperty("method", "stub");
+ }
+ PrintLongProperty("date", static_cast<int64_t>(OS::TimeCurrentMillis()));
+}
+
+
+void HTracer::TraceLithium(const char* name, LChunk* chunk) {
+ ASSERT(!FLAG_parallel_recompilation);
+ AllowHandleDereference allow_deref;
+ AllowDeferredHandleDereference allow_deferred_deref;
+ Trace(name, chunk->graph(), chunk);
+}
+
+
+void HTracer::TraceHydrogen(const char* name, HGraph* graph) {
+ ASSERT(!FLAG_parallel_recompilation);
+ AllowHandleDereference allow_deref;
+ AllowDeferredHandleDereference allow_deferred_deref;
+ Trace(name, graph, NULL);
+}
+
+
+void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
+ Tag tag(this, "cfg");
+ PrintStringProperty("name", name);
+ const ZoneList<HBasicBlock*>* blocks = graph->blocks();
+ for (int i = 0; i < blocks->length(); i++) {
+ HBasicBlock* current = blocks->at(i);
+ Tag block_tag(this, "block");
+ PrintBlockProperty("name", current->block_id());
+ PrintIntProperty("from_bci", -1);
+ PrintIntProperty("to_bci", -1);
+
+ if (!current->predecessors()->is_empty()) {
+ PrintIndent();
+ trace_.Add("predecessors");
+ for (int j = 0; j < current->predecessors()->length(); ++j) {
+ trace_.Add(" \"B%d\"", current->predecessors()->at(j)->block_id());
+ }
+ trace_.Add("\n");
+ } else {
+ PrintEmptyProperty("predecessors");
+ }
+
+ if (current->end()->SuccessorCount() == 0) {
+ PrintEmptyProperty("successors");
+ } else {
+ PrintIndent();
+ trace_.Add("successors");
+ for (HSuccessorIterator it(current->end()); !it.Done(); it.Advance()) {
+ trace_.Add(" \"B%d\"", it.Current()->block_id());
+ }
+ trace_.Add("\n");
+ }
+
+ PrintEmptyProperty("xhandlers");
+ const char* flags = current->IsLoopSuccessorDominator()
+ ? "dom-loop-succ"
+ : "";
+ PrintStringProperty("flags", flags);
+
+ if (current->dominator() != NULL) {
+ PrintBlockProperty("dominator", current->dominator()->block_id());
+ }
+
+ PrintIntProperty("loop_depth", current->LoopNestingDepth());
+
+ if (chunk != NULL) {
+ int first_index = current->first_instruction_index();
+ int last_index = current->last_instruction_index();
+ PrintIntProperty(
+ "first_lir_id",
+ LifetimePosition::FromInstructionIndex(first_index).Value());
+ PrintIntProperty(
+ "last_lir_id",
+ LifetimePosition::FromInstructionIndex(last_index).Value());
+ }
+
+ {
+ Tag states_tag(this, "states");
+ Tag locals_tag(this, "locals");
+ int total = current->phis()->length();
+ PrintIntProperty("size", current->phis()->length());
+ PrintStringProperty("method", "None");
+ for (int j = 0; j < total; ++j) {
+ HPhi* phi = current->phis()->at(j);
+ PrintIndent();
+ trace_.Add("%d ", phi->merged_index());
+ phi->PrintNameTo(&trace_);
+ trace_.Add(" ");
+ phi->PrintTo(&trace_);
+ trace_.Add("\n");
+ }
+ }
+
+ {
+ Tag HIR_tag(this, "HIR");
+ for (HInstructionIterator it(current); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ int bci = 0;
+ int uses = instruction->UseCount();
+ PrintIndent();
+ trace_.Add("%d %d ", bci, uses);
+ instruction->PrintNameTo(&trace_);
+ trace_.Add(" ");
+ instruction->PrintTo(&trace_);
+ trace_.Add(" <|@\n");
+ }
+ }
+
+
+ if (chunk != NULL) {
+ Tag LIR_tag(this, "LIR");
+ int first_index = current->first_instruction_index();
+ int last_index = current->last_instruction_index();
+ if (first_index != -1 && last_index != -1) {
+ const ZoneList<LInstruction*>* instructions = chunk->instructions();
+ for (int i = first_index; i <= last_index; ++i) {
+ LInstruction* linstr = instructions->at(i);
+ if (linstr != NULL) {
+ PrintIndent();
+ trace_.Add("%d ",
+ LifetimePosition::FromInstructionIndex(i).Value());
+ linstr->PrintTo(&trace_);
+ trace_.Add(" <|@\n");
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
+ Tag tag(this, "intervals");
+ PrintStringProperty("name", name);
+
+ const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
+ for (int i = 0; i < fixed_d->length(); ++i) {
+ TraceLiveRange(fixed_d->at(i), "fixed", allocator->zone());
+ }
+
+ const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
+ for (int i = 0; i < fixed->length(); ++i) {
+ TraceLiveRange(fixed->at(i), "fixed", allocator->zone());
+ }
+
+ const ZoneList<LiveRange*>* live_ranges = allocator->live_ranges();
+ for (int i = 0; i < live_ranges->length(); ++i) {
+ TraceLiveRange(live_ranges->at(i), "object", allocator->zone());
+ }
+}
+
+
+void HTracer::TraceLiveRange(LiveRange* range, const char* type,
+ Zone* zone) {
+ if (range != NULL && !range->IsEmpty()) {
+ PrintIndent();
+ trace_.Add("%d %s", range->id(), type);
+ if (range->HasRegisterAssigned()) {
+ LOperand* op = range->CreateAssignedOperand(zone);
+ int assigned_reg = op->index();
+ if (op->IsDoubleRegister()) {
+ trace_.Add(" \"%s\"",
+ DoubleRegister::AllocationIndexToString(assigned_reg));
+ } else {
+ ASSERT(op->IsRegister());
+ trace_.Add(" \"%s\"", Register::AllocationIndexToString(assigned_reg));
+ }
+ } else if (range->IsSpilled()) {
+ LOperand* op = range->TopLevel()->GetSpillOperand();
+ if (op->IsDoubleStackSlot()) {
+ trace_.Add(" \"double_stack:%d\"", op->index());
+ } else {
+ ASSERT(op->IsStackSlot());
+ trace_.Add(" \"stack:%d\"", op->index());
+ }
+ }
+ int parent_index = -1;
+ if (range->IsChild()) {
+ parent_index = range->parent()->id();
+ } else {
+ parent_index = range->id();
+ }
+ LOperand* op = range->FirstHint();
+ int hint_index = -1;
+ 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())) {
+ trace_.Add(" [%d, %d[",
+ cur_interval->start().Value(),
+ cur_interval->end().Value());
+ cur_interval = cur_interval->next();
+ }
+
+ UsePosition* current_pos = range->first_pos();
+ while (current_pos != NULL) {
+ if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
+ trace_.Add(" %d M", current_pos->pos().Value());
+ }
+ current_pos = current_pos->next();
+ }
+
+ trace_.Add(" \"\"\n");
+ }
+}
+
+
+void HTracer::FlushToFile() {
+ AppendChars(filename_.start(), *trace_.ToCString(), trace_.length(), false);
+ trace_.Reset();
+}
+
+
+void HStatistics::Initialize(CompilationInfo* info) {
+ if (info->shared_info().is_null()) return;
+ source_size_ += info->shared_info()->SourceSize();
+}
+
+
+void HStatistics::Print() {
+ PrintF("Timing results:\n");
+ int64_t sum = 0;
+ for (int i = 0; i < timing_.length(); ++i) {
+ sum += timing_[i];
+ }
+
+ for (int i = 0; i < names_.length(); ++i) {
+ PrintF("%32s", names_[i]);
+ double ms = static_cast<double>(timing_[i]) / 1000;
+ double percent = static_cast<double>(timing_[i]) * 100 / sum;
+ PrintF(" %8.3f ms / %4.1f %% ", ms, percent);
+
+ unsigned size = sizes_[i];
+ double size_percent = static_cast<double>(size) * 100 / total_size_;
+ PrintF(" %9u bytes / %4.1f %%\n", size, size_percent);
+ }
+
+ PrintF("----------------------------------------"
+ "---------------------------------------\n");
+ int64_t total = create_graph_ + optimize_graph_ + generate_code_;
+ PrintF("%32s %8.3f ms / %4.1f %% \n",
+ "Create graph",
+ static_cast<double>(create_graph_) / 1000,
+ static_cast<double>(create_graph_) * 100 / total);
+ PrintF("%32s %8.3f ms / %4.1f %% \n",
+ "Optimize graph",
+ static_cast<double>(optimize_graph_) / 1000,
+ static_cast<double>(optimize_graph_) * 100 / total);
+ PrintF("%32s %8.3f ms / %4.1f %% \n",
+ "Generate and install code",
+ static_cast<double>(generate_code_) / 1000,
+ static_cast<double>(generate_code_) * 100 / total);
+ PrintF("----------------------------------------"
+ "---------------------------------------\n");
+ PrintF("%32s %8.3f ms (%.1f times slower than full code gen)\n",
+ "Total",
+ static_cast<double>(total) / 1000,
+ static_cast<double>(total) / full_code_gen_);
+
+ double source_size_in_kb = static_cast<double>(source_size_) / 1024;
+ double normalized_time = source_size_in_kb > 0
+ ? (static_cast<double>(total) / 1000) / source_size_in_kb
+ : 0;
+ double normalized_size_in_kb = source_size_in_kb > 0
+ ? total_size_ / 1024 / source_size_in_kb
+ : 0;
+ PrintF("%32s %8.3f ms %7.3f kB allocated\n",
+ "Average per kB source",
+ normalized_time, normalized_size_in_kb);
+}
+
+
+void HStatistics::SaveTiming(const char* name, int64_t ticks, unsigned size) {
+ total_size_ += size;
+ for (int i = 0; i < names_.length(); ++i) {
+ if (strcmp(names_[i], name) == 0) {
+ timing_[i] += ticks;
+ sizes_[i] += size;
+ return;
+ }
+ }
+ names_.Add(name);
+ timing_.Add(ticks);
+ sizes_.Add(size);
+}
+
+
+HPhase::~HPhase() {
+ if (ShouldProduceTraceOutput()) {
+ isolate()->GetHTracer()->TraceHydrogen(name(), graph_);
+ }
+
+#ifdef DEBUG
+ graph_->Verify(false); // No full verify.
+#endif
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/hydrogen.h b/chromium/v8/src/hydrogen.h
new file mode 100644
index 00000000000..004aa16a878
--- /dev/null
+++ b/chromium/v8/src/hydrogen.h
@@ -0,0 +1,2331 @@
+// Copyright 2012 the V8 project authors. 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_HYDROGEN_H_
+#define V8_HYDROGEN_H_
+
+#include "v8.h"
+
+#include "allocation.h"
+#include "ast.h"
+#include "compiler.h"
+#include "hydrogen-instructions.h"
+#include "zone.h"
+#include "scopes.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class BitVector;
+class FunctionState;
+class HEnvironment;
+class HGraph;
+class HLoopInformation;
+class HOsrBuilder;
+class HTracer;
+class LAllocator;
+class LChunk;
+class LiveRange;
+
+
+class HBasicBlock: public ZoneObject {
+ public:
+ explicit HBasicBlock(HGraph* graph);
+ virtual ~HBasicBlock() { }
+
+ // Simple accessors.
+ int block_id() const { return block_id_; }
+ void set_block_id(int id) { block_id_ = id; }
+ HGraph* graph() const { return graph_; }
+ Isolate* isolate() const;
+ const ZoneList<HPhi*>* phis() const { return &phis_; }
+ HInstruction* first() const { return first_; }
+ HInstruction* last() const { return last_; }
+ void set_last(HInstruction* instr) { last_ = instr; }
+ HControlInstruction* end() const { return end_; }
+ HLoopInformation* loop_information() const { return loop_information_; }
+ HLoopInformation* current_loop() const {
+ return IsLoopHeader() ? loop_information()
+ : (parent_loop_header() != NULL
+ ? parent_loop_header()->loop_information() : NULL);
+ }
+ const ZoneList<HBasicBlock*>* predecessors() const { return &predecessors_; }
+ bool HasPredecessor() const { return predecessors_.length() > 0; }
+ const ZoneList<HBasicBlock*>* dominated_blocks() const {
+ return &dominated_blocks_;
+ }
+ const ZoneList<int>* deleted_phis() const {
+ return &deleted_phis_;
+ }
+ void RecordDeletedPhi(int merge_index) {
+ deleted_phis_.Add(merge_index, zone());
+ }
+ HBasicBlock* dominator() const { return dominator_; }
+ HEnvironment* last_environment() const { return last_environment_; }
+ int argument_count() const { return argument_count_; }
+ void set_argument_count(int count) { argument_count_ = count; }
+ int first_instruction_index() const { return first_instruction_index_; }
+ void set_first_instruction_index(int index) {
+ first_instruction_index_ = index;
+ }
+ int last_instruction_index() const { return last_instruction_index_; }
+ void set_last_instruction_index(int index) {
+ last_instruction_index_ = index;
+ }
+ bool is_osr_entry() { return is_osr_entry_; }
+ void set_osr_entry() { is_osr_entry_ = true; }
+
+ void AttachLoopInformation();
+ void DetachLoopInformation();
+ bool IsLoopHeader() const { return loop_information() != NULL; }
+ bool IsStartBlock() const { return block_id() == 0; }
+ void PostProcessLoopHeader(IterationStatement* stmt);
+
+ bool IsFinished() const { return end_ != NULL; }
+ void AddPhi(HPhi* phi);
+ void RemovePhi(HPhi* phi);
+ void AddInstruction(HInstruction* instr);
+ bool Dominates(HBasicBlock* other) const;
+ int LoopNestingDepth() const;
+
+ void SetInitialEnvironment(HEnvironment* env);
+ void ClearEnvironment() {
+ ASSERT(IsFinished());
+ ASSERT(end()->SuccessorCount() == 0);
+ last_environment_ = NULL;
+ }
+ bool HasEnvironment() const { return last_environment_ != NULL; }
+ void UpdateEnvironment(HEnvironment* env);
+ HBasicBlock* parent_loop_header() const { return parent_loop_header_; }
+
+ void set_parent_loop_header(HBasicBlock* block) {
+ ASSERT(parent_loop_header_ == NULL);
+ parent_loop_header_ = block;
+ }
+
+ bool HasParentLoopHeader() const { return parent_loop_header_ != NULL; }
+
+ void SetJoinId(BailoutId ast_id);
+
+ void Finish(HControlInstruction* last);
+ void FinishExit(HControlInstruction* instruction);
+ void Goto(HBasicBlock* block,
+ FunctionState* state = NULL,
+ bool add_simulate = true);
+ void GotoNoSimulate(HBasicBlock* block) {
+ Goto(block, NULL, false);
+ }
+
+ int PredecessorIndexOf(HBasicBlock* predecessor) const;
+ HPhi* AddNewPhi(int merged_index);
+ HSimulate* AddNewSimulate(BailoutId ast_id,
+ RemovableSimulate removable = FIXED_SIMULATE) {
+ HSimulate* instr = CreateSimulate(ast_id, removable);
+ AddInstruction(instr);
+ return instr;
+ }
+ void AssignCommonDominator(HBasicBlock* other);
+ void AssignLoopSuccessorDominators();
+
+ // Add the inlined function exit sequence, adding an HLeaveInlined
+ // instruction and updating the bailout environment.
+ void AddLeaveInlined(HValue* return_value, FunctionState* state);
+
+ // If a target block is tagged as an inline function return, all
+ // predecessors should contain the inlined exit sequence:
+ //
+ // LeaveInlined
+ // Simulate (caller's environment)
+ // Goto (target block)
+ bool IsInlineReturnTarget() const { return is_inline_return_target_; }
+ void MarkAsInlineReturnTarget(HBasicBlock* inlined_entry_block) {
+ is_inline_return_target_ = true;
+ inlined_entry_block_ = inlined_entry_block;
+ }
+ HBasicBlock* inlined_entry_block() { return inlined_entry_block_; }
+
+ 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() const;
+
+#ifdef DEBUG
+ void Verify();
+#endif
+
+ private:
+ friend class HGraphBuilder;
+
+ void RegisterPredecessor(HBasicBlock* pred);
+ void AddDominatedBlock(HBasicBlock* block);
+
+ HSimulate* CreateSimulate(BailoutId ast_id, RemovableSimulate removable);
+
+ int block_id_;
+ HGraph* graph_;
+ ZoneList<HPhi*> phis_;
+ HInstruction* first_;
+ HInstruction* last_;
+ HControlInstruction* end_;
+ HLoopInformation* loop_information_;
+ ZoneList<HBasicBlock*> predecessors_;
+ HBasicBlock* dominator_;
+ ZoneList<HBasicBlock*> dominated_blocks_;
+ HEnvironment* last_environment_;
+ // Outgoing parameter count at block exit, set during lithium translation.
+ int argument_count_;
+ // Instruction indices into the lithium code stream.
+ int first_instruction_index_;
+ int last_instruction_index_;
+ ZoneList<int> deleted_phis_;
+ HBasicBlock* parent_loop_header_;
+ // For blocks marked as inline return target: the block with HEnterInlined.
+ HBasicBlock* inlined_entry_block_;
+ bool is_inline_return_target_ : 1;
+ bool is_deoptimizing_ : 1;
+ bool dominates_loop_successors_ : 1;
+ bool is_osr_entry_ : 1;
+};
+
+
+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_;
+};
+
+
+class HInstructionIterator BASE_EMBEDDED {
+ public:
+ explicit HInstructionIterator(HBasicBlock* block)
+ : instr_(block->first()) {
+ next_ = Done() ? NULL : instr_->next();
+ }
+
+ inline bool Done() const { return instr_ == NULL; }
+ inline HInstruction* Current() { return instr_; }
+ inline void Advance() {
+ instr_ = next_;
+ next_ = Done() ? NULL : instr_->next();
+ }
+
+ private:
+ HInstruction* instr_;
+ HInstruction* next_;
+};
+
+
+class HLoopInformation: public ZoneObject {
+ public:
+ HLoopInformation(HBasicBlock* loop_header, Zone* zone)
+ : back_edges_(4, zone),
+ loop_header_(loop_header),
+ blocks_(8, zone),
+ stack_check_(NULL) {
+ blocks_.Add(loop_header, zone);
+ }
+ virtual ~HLoopInformation() {}
+
+ const ZoneList<HBasicBlock*>* back_edges() const { return &back_edges_; }
+ const ZoneList<HBasicBlock*>* blocks() const { return &blocks_; }
+ HBasicBlock* loop_header() const { return loop_header_; }
+ HBasicBlock* GetLastBackEdge() const;
+ void RegisterBackEdge(HBasicBlock* block);
+
+ HStackCheck* stack_check() const { return stack_check_; }
+ void set_stack_check(HStackCheck* stack_check) {
+ stack_check_ = stack_check;
+ }
+
+ bool IsNestedInThisLoop(HLoopInformation* other) {
+ while (other != NULL) {
+ if (other == this) {
+ return true;
+ }
+ other = other->parent_loop();
+ }
+ return false;
+ }
+ HLoopInformation* parent_loop() {
+ HBasicBlock* parent_header = loop_header()->parent_loop_header();
+ return parent_header != NULL ? parent_header->loop_information() : NULL;
+ }
+
+ private:
+ void AddBlock(HBasicBlock* block);
+
+ ZoneList<HBasicBlock*> back_edges_;
+ HBasicBlock* loop_header_;
+ ZoneList<HBasicBlock*> blocks_;
+ HStackCheck* stack_check_;
+};
+
+
+class BoundsCheckTable;
+class InductionVariableBlocksTable;
+class HGraph: public ZoneObject {
+ public:
+ explicit HGraph(CompilationInfo* info);
+
+ Isolate* isolate() const { return isolate_; }
+ Zone* zone() const { return zone_; }
+ CompilationInfo* info() const { return info_; }
+
+ const ZoneList<HBasicBlock*>* blocks() const { return &blocks_; }
+ const ZoneList<HPhi*>* phi_list() const { return phi_list_; }
+ HBasicBlock* entry_block() const { return entry_block_; }
+ HEnvironment* start_environment() const { return start_environment_; }
+
+ void FinalizeUniqueValueIds();
+ bool ProcessArgumentsObject();
+ void OrderBlocks();
+ void AssignDominators();
+ void SetupInformativeDefinitions();
+ void RestoreActualValues();
+
+ // Returns false if there are phi-uses of the arguments-object
+ // which are not supported by the optimizing compiler.
+ bool CheckArgumentsPhiUses();
+
+ // Returns false if there are phi-uses of an uninitialized const
+ // which are not supported by the optimizing compiler.
+ bool CheckConstPhiUses();
+
+ void CollectPhis();
+
+ void set_undefined_constant(HConstant* constant) {
+ undefined_constant_.set(constant);
+ }
+ HConstant* GetConstantUndefined() const { return undefined_constant_.get(); }
+ HConstant* GetConstant0();
+ HConstant* GetConstant1();
+ HConstant* GetConstantMinus1();
+ HConstant* GetConstantTrue();
+ HConstant* GetConstantFalse();
+ HConstant* GetConstantHole();
+ HConstant* GetConstantNull();
+ HConstant* GetInvalidContext();
+
+ bool IsStandardConstant(HConstant* constant);
+
+ HBasicBlock* CreateBasicBlock();
+ HArgumentsObject* GetArgumentsObject() const {
+ return arguments_object_.get();
+ }
+
+ void SetArgumentsObject(HArgumentsObject* object) {
+ arguments_object_.set(object);
+ }
+
+ int GetMaximumValueID() const { return values_.length(); }
+ int GetNextBlockID() { return next_block_id_++; }
+ int GetNextValueID(HValue* value) {
+ values_.Add(value, zone());
+ return values_.length() - 1;
+ }
+ HValue* LookupValue(int id) const {
+ if (id >= 0 && id < values_.length()) return values_[id];
+ return NULL;
+ }
+
+ bool Optimize(BailoutReason* bailout_reason);
+
+#ifdef DEBUG
+ void Verify(bool do_full_verify) const;
+#endif
+
+ bool has_osr() {
+ return osr_ != NULL;
+ }
+
+ void set_osr(HOsrBuilder* osr) {
+ osr_ = osr;
+ }
+
+ HOsrBuilder* osr() {
+ return osr_;
+ }
+
+ int update_type_change_checksum(int delta) {
+ type_change_checksum_ += delta;
+ return type_change_checksum_;
+ }
+
+ void update_maximum_environment_size(int environment_size) {
+ if (environment_size > maximum_environment_size_) {
+ maximum_environment_size_ = environment_size;
+ }
+ }
+ int maximum_environment_size() { return maximum_environment_size_; }
+
+ bool use_optimistic_licm() {
+ return use_optimistic_licm_;
+ }
+
+ void set_use_optimistic_licm(bool value) {
+ use_optimistic_licm_ = value;
+ }
+
+ bool has_soft_deoptimize() {
+ return has_soft_deoptimize_;
+ }
+
+ void set_has_soft_deoptimize(bool value) {
+ has_soft_deoptimize_ = value;
+ }
+
+ void MarkRecursive() {
+ is_recursive_ = true;
+ }
+
+ bool is_recursive() const {
+ return is_recursive_;
+ }
+
+ void MarkDependsOnEmptyArrayProtoElements() {
+ // Add map dependency if not already added.
+ if (depends_on_empty_array_proto_elements_) return;
+ isolate()->initial_object_prototype()->map()->AddDependentCompilationInfo(
+ DependentCode::kElementsCantBeAddedGroup, info());
+ isolate()->initial_array_prototype()->map()->AddDependentCompilationInfo(
+ DependentCode::kElementsCantBeAddedGroup, info());
+ depends_on_empty_array_proto_elements_ = true;
+ }
+
+ bool depends_on_empty_array_proto_elements() {
+ return depends_on_empty_array_proto_elements_;
+ }
+
+ bool has_uint32_instructions() {
+ ASSERT(uint32_instructions_ == NULL || !uint32_instructions_->is_empty());
+ return uint32_instructions_ != NULL;
+ }
+
+ ZoneList<HInstruction*>* uint32_instructions() {
+ ASSERT(uint32_instructions_ == NULL || !uint32_instructions_->is_empty());
+ return uint32_instructions_;
+ }
+
+ void RecordUint32Instruction(HInstruction* instr) {
+ ASSERT(uint32_instructions_ == NULL || !uint32_instructions_->is_empty());
+ if (uint32_instructions_ == NULL) {
+ uint32_instructions_ = new(zone()) ZoneList<HInstruction*>(4, zone());
+ }
+ uint32_instructions_->Add(instr, zone());
+ }
+
+ void IncrementInNoSideEffectsScope() { no_side_effects_scope_count_++; }
+ void DecrementInNoSideEffectsScope() { no_side_effects_scope_count_--; }
+ bool IsInsideNoSideEffectsScope() { return no_side_effects_scope_count_ > 0; }
+
+ private:
+ HConstant* GetConstant(SetOncePointer<HConstant>* pointer,
+ int32_t integer_value);
+
+ template<class Phase>
+ void Run() {
+ Phase phase(this);
+ phase.Run();
+ }
+
+ void CheckForBackEdge(HBasicBlock* block, HBasicBlock* successor);
+ void SetupInformativeDefinitionsInBlock(HBasicBlock* block);
+ void SetupInformativeDefinitionsRecursively(HBasicBlock* block);
+ void EliminateRedundantBoundsChecksUsingInductionVariables();
+
+ Isolate* isolate_;
+ int next_block_id_;
+ HBasicBlock* entry_block_;
+ HEnvironment* start_environment_;
+ ZoneList<HBasicBlock*> blocks_;
+ ZoneList<HValue*> values_;
+ ZoneList<HPhi*>* phi_list_;
+ ZoneList<HInstruction*>* uint32_instructions_;
+ SetOncePointer<HConstant> undefined_constant_;
+ SetOncePointer<HConstant> constant_0_;
+ SetOncePointer<HConstant> constant_1_;
+ SetOncePointer<HConstant> constant_minus1_;
+ SetOncePointer<HConstant> constant_true_;
+ SetOncePointer<HConstant> constant_false_;
+ SetOncePointer<HConstant> constant_the_hole_;
+ SetOncePointer<HConstant> constant_null_;
+ SetOncePointer<HConstant> constant_invalid_context_;
+ SetOncePointer<HArgumentsObject> arguments_object_;
+
+ HOsrBuilder* osr_;
+
+ CompilationInfo* info_;
+ Zone* zone_;
+
+ bool is_recursive_;
+ bool use_optimistic_licm_;
+ bool has_soft_deoptimize_;
+ bool depends_on_empty_array_proto_elements_;
+ int type_change_checksum_;
+ int maximum_environment_size_;
+ int no_side_effects_scope_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(HGraph);
+};
+
+
+Zone* HBasicBlock::zone() const { return graph_->zone(); }
+
+
+// Type of stack frame an environment might refer to.
+enum FrameType {
+ JS_FUNCTION,
+ JS_CONSTRUCT,
+ JS_GETTER,
+ JS_SETTER,
+ ARGUMENTS_ADAPTOR,
+ STUB
+};
+
+
+class HEnvironment: public ZoneObject {
+ public:
+ HEnvironment(HEnvironment* outer,
+ Scope* scope,
+ Handle<JSFunction> closure,
+ Zone* zone);
+
+ HEnvironment(Zone* zone, int parameter_count);
+
+ HEnvironment* arguments_environment() {
+ return outer()->frame_type() == ARGUMENTS_ADAPTOR ? outer() : this;
+ }
+
+ // Simple accessors.
+ Handle<JSFunction> closure() const { return closure_; }
+ const ZoneList<HValue*>* values() const { return &values_; }
+ const GrowableBitVector* assigned_variables() const {
+ return &assigned_variables_;
+ }
+ FrameType frame_type() const { return frame_type_; }
+ int parameter_count() const { return parameter_count_; }
+ int specials_count() const { return specials_count_; }
+ int local_count() const { return local_count_; }
+ HEnvironment* outer() const { return outer_; }
+ int pop_count() const { return pop_count_; }
+ int push_count() const { return push_count_; }
+
+ BailoutId ast_id() const { return ast_id_; }
+ void set_ast_id(BailoutId id) { ast_id_ = id; }
+
+ HEnterInlined* entry() const { return entry_; }
+ void set_entry(HEnterInlined* entry) { entry_ = entry; }
+
+ int length() const { return values_.length(); }
+ bool is_special_index(int i) const {
+ return i >= parameter_count() && i < parameter_count() + specials_count();
+ }
+
+ int first_expression_index() const {
+ return parameter_count() + specials_count() + local_count();
+ }
+
+ int first_local_index() const {
+ return parameter_count() + specials_count();
+ }
+
+ void Bind(Variable* variable, HValue* value) {
+ Bind(IndexFor(variable), value);
+ }
+
+ void Bind(int index, HValue* value);
+
+ void BindContext(HValue* value) {
+ Bind(parameter_count(), value);
+ }
+
+ HValue* Lookup(Variable* variable) const {
+ return Lookup(IndexFor(variable));
+ }
+
+ HValue* Lookup(int index) const {
+ HValue* result = values_[index];
+ ASSERT(result != NULL);
+ return result;
+ }
+
+ HValue* context() const {
+ // Return first special.
+ return Lookup(parameter_count());
+ }
+
+ void Push(HValue* value) {
+ ASSERT(value != NULL);
+ ++push_count_;
+ values_.Add(value, zone());
+ }
+
+ HValue* Pop() {
+ ASSERT(!ExpressionStackIsEmpty());
+ if (push_count_ > 0) {
+ --push_count_;
+ } else {
+ ++pop_count_;
+ }
+ return values_.RemoveLast();
+ }
+
+ void Drop(int count);
+
+ HValue* Top() const { return ExpressionStackAt(0); }
+
+ bool ExpressionStackIsEmpty() const;
+
+ HValue* ExpressionStackAt(int index_from_top) const {
+ int index = length() - index_from_top - 1;
+ ASSERT(HasExpressionAt(index));
+ return values_[index];
+ }
+
+ void SetExpressionStackAt(int index_from_top, HValue* value);
+
+ HEnvironment* Copy() const;
+ HEnvironment* CopyWithoutHistory() const;
+ HEnvironment* CopyAsLoopHeader(HBasicBlock* block) const;
+
+ // Create an "inlined version" of this environment, where the original
+ // 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,
+ InliningKind inlining_kind,
+ bool undefined_receiver) const;
+
+ static bool UseUndefinedReceiver(Handle<JSFunction> closure,
+ FunctionLiteral* function,
+ CallKind call_kind,
+ InliningKind inlining_kind) {
+ return (closure->shared()->native() || !function->is_classic_mode()) &&
+ call_kind == CALL_AS_FUNCTION && inlining_kind != CONSTRUCT_CALL_RETURN;
+ }
+
+ HEnvironment* DiscardInlined(bool drop_extra) {
+ HEnvironment* outer = outer_;
+ while (outer->frame_type() != JS_FUNCTION) outer = outer->outer_;
+ if (drop_extra) outer->Drop(1);
+ return outer;
+ }
+
+ void AddIncomingEdge(HBasicBlock* block, HEnvironment* other);
+
+ void ClearHistory() {
+ pop_count_ = 0;
+ push_count_ = 0;
+ assigned_variables_.Clear();
+ }
+
+ void SetValueAt(int index, HValue* value) {
+ ASSERT(index < length());
+ values_[index] = value;
+ }
+
+ // Map a variable to an environment index. Parameter indices are shifted
+ // by 1 (receiver is parameter index -1 but environment index 0).
+ // Stack-allocated local indices are shifted by the number of parameters.
+ int IndexFor(Variable* variable) const {
+ ASSERT(variable->IsStackAllocated());
+ int shift = variable->IsParameter()
+ ? 1
+ : parameter_count_ + specials_count_;
+ return variable->index() + shift;
+ }
+
+ bool is_local_index(int i) const {
+ return i >= first_local_index() &&
+ i < first_expression_index();
+ }
+
+ void PrintTo(StringStream* stream);
+ void PrintToStd();
+
+ Zone* zone() const { return zone_; }
+
+ private:
+ HEnvironment(const HEnvironment* other, Zone* zone);
+
+ HEnvironment(HEnvironment* outer,
+ Handle<JSFunction> closure,
+ FrameType frame_type,
+ int arguments,
+ Zone* zone);
+
+ // Create an artificial stub environment (e.g. for argument adaptor or
+ // constructor stub).
+ HEnvironment* CreateStubEnvironment(HEnvironment* outer,
+ Handle<JSFunction> target,
+ FrameType frame_type,
+ int arguments) const;
+
+ // True if index is included in the expression stack part of the environment.
+ bool HasExpressionAt(int index) const;
+
+ void Initialize(int parameter_count, int local_count, int stack_height);
+ void Initialize(const HEnvironment* other);
+
+ Handle<JSFunction> closure_;
+ // Value array [parameters] [specials] [locals] [temporaries].
+ ZoneList<HValue*> values_;
+ GrowableBitVector assigned_variables_;
+ FrameType frame_type_;
+ int parameter_count_;
+ int specials_count_;
+ int local_count_;
+ HEnvironment* outer_;
+ HEnterInlined* entry_;
+ int pop_count_;
+ int push_count_;
+ BailoutId ast_id_;
+ Zone* zone_;
+};
+
+
+class HOptimizedGraphBuilder;
+
+enum ArgumentsAllowedFlag {
+ ARGUMENTS_NOT_ALLOWED,
+ ARGUMENTS_ALLOWED
+};
+
+
+class HIfContinuation;
+
+// This class is not BASE_EMBEDDED because our inlining implementation uses
+// new and delete.
+class AstContext {
+ public:
+ bool IsEffect() const { return kind_ == Expression::kEffect; }
+ bool IsValue() const { return kind_ == Expression::kValue; }
+ bool IsTest() const { return kind_ == Expression::kTest; }
+
+ // 'Fill' this context with a hydrogen value. The value is assumed to
+ // have already been inserted in the instruction stream (or not need to
+ // be, e.g., HPhi). Call this function in tail position in the Visit
+ // functions for expressions.
+ virtual void ReturnValue(HValue* value) = 0;
+
+ // Add a hydrogen instruction to the instruction stream (recording an
+ // environment simulation if necessary) and then fill this context with
+ // the instruction as value.
+ virtual void ReturnInstruction(HInstruction* instr, BailoutId ast_id) = 0;
+
+ // Finishes the current basic block and materialize a boolean for
+ // value context, nothing for effect, generate a branch for test context.
+ // Call this function in tail position in the Visit functions for
+ // expressions.
+ virtual void ReturnControl(HControlInstruction* instr, BailoutId ast_id) = 0;
+
+ // Finishes the current basic block and materialize a boolean for
+ // value context, nothing for effect, generate a branch for test context.
+ // Call this function in tail position in the Visit functions for
+ // expressions that use an IfBuilder.
+ virtual void ReturnContinuation(HIfContinuation* continuation,
+ BailoutId ast_id) = 0;
+
+ void set_for_typeof(bool for_typeof) { for_typeof_ = for_typeof; }
+ bool is_for_typeof() { return for_typeof_; }
+
+ protected:
+ AstContext(HOptimizedGraphBuilder* owner, Expression::Context kind);
+ virtual ~AstContext();
+
+ HOptimizedGraphBuilder* owner() const { return owner_; }
+
+ inline Zone* zone() const;
+
+ // We want to be able to assert, in a context-specific way, that the stack
+ // height makes sense when the context is filled.
+#ifdef DEBUG
+ int original_length_;
+#endif
+
+ private:
+ HOptimizedGraphBuilder* owner_;
+ Expression::Context kind_;
+ AstContext* outer_;
+ bool for_typeof_;
+};
+
+
+class EffectContext: public AstContext {
+ public:
+ explicit EffectContext(HOptimizedGraphBuilder* owner)
+ : AstContext(owner, Expression::kEffect) {
+ }
+ virtual ~EffectContext();
+
+ virtual void ReturnValue(HValue* value);
+ virtual void ReturnInstruction(HInstruction* instr, BailoutId ast_id);
+ virtual void ReturnControl(HControlInstruction* instr, BailoutId ast_id);
+ virtual void ReturnContinuation(HIfContinuation* continuation,
+ BailoutId ast_id);
+};
+
+
+class ValueContext: public AstContext {
+ public:
+ ValueContext(HOptimizedGraphBuilder* owner, ArgumentsAllowedFlag flag)
+ : AstContext(owner, Expression::kValue), flag_(flag) {
+ }
+ virtual ~ValueContext();
+
+ virtual void ReturnValue(HValue* value);
+ virtual void ReturnInstruction(HInstruction* instr, BailoutId ast_id);
+ virtual void ReturnControl(HControlInstruction* instr, BailoutId ast_id);
+ virtual void ReturnContinuation(HIfContinuation* continuation,
+ BailoutId ast_id);
+
+ bool arguments_allowed() { return flag_ == ARGUMENTS_ALLOWED; }
+
+ private:
+ ArgumentsAllowedFlag flag_;
+};
+
+
+class TestContext: public AstContext {
+ public:
+ TestContext(HOptimizedGraphBuilder* owner,
+ Expression* condition,
+ HBasicBlock* if_true,
+ HBasicBlock* if_false)
+ : AstContext(owner, Expression::kTest),
+ condition_(condition),
+ if_true_(if_true),
+ if_false_(if_false) {
+ }
+
+ virtual void ReturnValue(HValue* value);
+ virtual void ReturnInstruction(HInstruction* instr, BailoutId ast_id);
+ virtual void ReturnControl(HControlInstruction* instr, BailoutId ast_id);
+ virtual void ReturnContinuation(HIfContinuation* continuation,
+ BailoutId ast_id);
+
+ static TestContext* cast(AstContext* context) {
+ ASSERT(context->IsTest());
+ return reinterpret_cast<TestContext*>(context);
+ }
+
+ Expression* condition() const { return condition_; }
+ HBasicBlock* if_true() const { return if_true_; }
+ HBasicBlock* if_false() const { return if_false_; }
+
+ private:
+ // Build the shared core part of the translation unpacking a value into
+ // control flow.
+ void BuildBranch(HValue* value);
+
+ Expression* condition_;
+ HBasicBlock* if_true_;
+ HBasicBlock* if_false_;
+};
+
+
+class FunctionState {
+ public:
+ FunctionState(HOptimizedGraphBuilder* owner,
+ CompilationInfo* info,
+ InliningKind inlining_kind);
+ ~FunctionState();
+
+ CompilationInfo* compilation_info() { return compilation_info_; }
+ AstContext* call_context() { return call_context_; }
+ InliningKind inlining_kind() const { return inlining_kind_; }
+ HBasicBlock* function_return() { return function_return_; }
+ TestContext* test_context() { return test_context_; }
+ void ClearInlinedTestContext() {
+ delete test_context_;
+ test_context_ = NULL;
+ }
+
+ FunctionState* outer() { return outer_; }
+
+ HEnterInlined* entry() { return entry_; }
+ void set_entry(HEnterInlined* entry) { entry_ = entry; }
+
+ HArgumentsObject* arguments_object() { return arguments_object_; }
+ void set_arguments_object(HArgumentsObject* arguments_object) {
+ arguments_object_ = arguments_object;
+ }
+
+ HArgumentsElements* arguments_elements() { return arguments_elements_; }
+ void set_arguments_elements(HArgumentsElements* arguments_elements) {
+ arguments_elements_ = arguments_elements;
+ }
+
+ bool arguments_pushed() { return arguments_elements() != NULL; }
+
+ private:
+ HOptimizedGraphBuilder* owner_;
+
+ CompilationInfo* compilation_info_;
+
+ // During function inlining, expression context of the call being
+ // inlined. NULL when not inlining.
+ AstContext* call_context_;
+
+ // The kind of call which is currently being inlined.
+ InliningKind inlining_kind_;
+
+ // When inlining in an effect or 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
+ // local return point.
+ HBasicBlock* function_return_;
+
+ // When inlining a call in a test context, a context containing a pair of
+ // return blocks. NULL in all other cases.
+ TestContext* test_context_;
+
+ // When inlining HEnterInlined instruction corresponding to the function
+ // entry.
+ HEnterInlined* entry_;
+
+ HArgumentsObject* arguments_object_;
+ HArgumentsElements* arguments_elements_;
+
+ FunctionState* outer_;
+};
+
+
+class HIfContinuation {
+ public:
+ HIfContinuation() { continuation_captured_ = false; }
+ ~HIfContinuation() { ASSERT(!continuation_captured_); }
+
+ void Capture(HBasicBlock* true_branch,
+ HBasicBlock* false_branch,
+ int position) {
+ ASSERT(!continuation_captured_);
+ true_branch_ = true_branch;
+ false_branch_ = false_branch;
+ position_ = position;
+ continuation_captured_ = true;
+ }
+
+ void Continue(HBasicBlock** true_branch,
+ HBasicBlock** false_branch,
+ int* position) {
+ ASSERT(continuation_captured_);
+ *true_branch = true_branch_;
+ *false_branch = false_branch_;
+ if (position != NULL) *position = position_;
+ continuation_captured_ = false;
+ }
+
+ bool IsTrueReachable() { return true_branch_ != NULL; }
+ bool IsFalseReachable() { return false_branch_ != NULL; }
+ bool TrueAndFalseReachable() {
+ return IsTrueReachable() || IsFalseReachable();
+ }
+
+ bool continuation_captured_;
+ HBasicBlock* true_branch_;
+ HBasicBlock* false_branch_;
+ int position_;
+};
+
+
+class HGraphBuilder {
+ public:
+ explicit HGraphBuilder(CompilationInfo* info)
+ : info_(info),
+ graph_(NULL),
+ current_block_(NULL) {}
+ virtual ~HGraphBuilder() {}
+
+ HBasicBlock* current_block() const { return current_block_; }
+ void set_current_block(HBasicBlock* block) { current_block_ = block; }
+ HEnvironment* environment() const {
+ return current_block()->last_environment();
+ }
+ Zone* zone() const { return info_->zone(); }
+ HGraph* graph() const { return graph_; }
+ Isolate* isolate() const { return graph_->isolate(); }
+ CompilationInfo* top_info() { return info_; }
+
+ HGraph* CreateGraph();
+
+ // Bailout environment manipulation.
+ void Push(HValue* value) { environment()->Push(value); }
+ HValue* Pop() { return environment()->Pop(); }
+
+ virtual HValue* context() = 0;
+
+ // Adding instructions.
+ HInstruction* AddInstruction(HInstruction* instr);
+
+ template<class I>
+ HInstruction* NewUncasted() { return I::New(zone(), context()); }
+
+ template<class I>
+ I* New() { return I::cast(NewUncasted<I>()); }
+
+ template<class I>
+ HInstruction* AddUncasted() { return AddInstruction(NewUncasted<I>());}
+
+ template<class I>
+ I* Add() { return I::cast(AddUncasted<I>());}
+
+ template<class I, class P1>
+ HInstruction* NewUncasted(P1 p1) {
+ return I::New(zone(), context(), p1);
+ }
+
+ template<class I, class P1>
+ I* New(P1 p1) { return I::cast(NewUncasted<I>(p1)); }
+
+ template<class I, class P1>
+ HInstruction* AddUncasted(P1 p1) {
+ HInstruction* result = AddInstruction(NewUncasted<I>(p1));
+ // Specializations must have their parameters properly casted
+ // to avoid landing here.
+ ASSERT(!result->IsReturn() && !result->IsSimulate() &&
+ !result->IsDeoptimize());
+ return result;
+ }
+
+ template<class I, class P1>
+ I* Add(P1 p1) {
+ return I::cast(AddUncasted<I>(p1));
+ }
+
+ template<class I, class P1, class P2>
+ HInstruction* NewUncasted(P1 p1, P2 p2) {
+ return I::New(zone(), context(), p1, p2);
+ }
+
+ template<class I, class P1, class P2>
+ I* New(P1 p1, P2 p2) {
+ return I::cast(NewUncasted<I>(p1, p2));
+ }
+
+ template<class I, class P1, class P2>
+ HInstruction* AddUncasted(P1 p1, P2 p2) {
+ HInstruction* result = AddInstruction(NewUncasted<I>(p1, p2));
+ // Specializations must have their parameters properly casted
+ // to avoid landing here.
+ ASSERT(!result->IsSimulate());
+ return result;
+ }
+
+ template<class I, class P1, class P2>
+ I* Add(P1 p1, P2 p2) {
+ return static_cast<I*>(AddUncasted<I>(p1, p2));
+ }
+
+ template<class I, class P1, class P2, class P3>
+ HInstruction* NewUncasted(P1 p1, P2 p2, P3 p3) {
+ return I::New(zone(), context(), p1, p2, p3);
+ }
+
+ template<class I, class P1, class P2, class P3>
+ I* New(P1 p1, P2 p2, P3 p3) {
+ return I::cast(NewUncasted<I>(p1, p2, p3));
+ }
+
+ template<class I, class P1, class P2, class P3>
+ HInstruction* AddUncasted(P1 p1, P2 p2, P3 p3) {
+ return AddInstruction(NewUncasted<I>(p1, p2, p3));
+ }
+
+ template<class I, class P1, class P2, class P3>
+ I* Add(P1 p1, P2 p2, P3 p3) {
+ return I::cast(AddUncasted<I>(p1, p2, p3));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4>
+ HInstruction* NewUncasted(P1 p1, P2 p2, P3 p3, P4 p4) {
+ return I::New(zone(), context(), p1, p2, p3, p4);
+ }
+
+ template<class I, class P1, class P2, class P3, class P4>
+ I* New(P1 p1, P2 p2, P3 p3, P4 p4) {
+ return I::cast(NewUncasted<I>(p1, p2, p3, p4));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4>
+ HInstruction* AddUncasted(P1 p1, P2 p2, P3 p3, P4 p4) {
+ return AddInstruction(NewUncasted<I>(p1, p2, p3, p4));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4>
+ I* Add(P1 p1, P2 p2, P3 p3, P4 p4) {
+ return I::cast(AddUncasted<I>(p1, p2, p3, p4));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4, class P5>
+ HInstruction* NewUncasted(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
+ return I::New(zone(), context(), p1, p2, p3, p4, p5);
+ }
+
+ template<class I, class P1, class P2, class P3, class P4, class P5>
+ I* New(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
+ return I::cast(NewUncasted<I>(p1, p2, p3, p4, p5));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4, class P5>
+ HInstruction* AddUncasted(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
+ return AddInstruction(NewUncasted<I>(p1, p2, p3, p4, p5));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4, class P5>
+ I* Add(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
+ return I::cast(AddUncasted<I>(p1, p2, p3, p4, p5));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4, class P5, class P6>
+ HInstruction* NewUncasted(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) {
+ return I::New(zone(), context(), p1, p2, p3, p4, p5, p6);
+ }
+
+ template<class I, class P1, class P2, class P3, class P4, class P5, class P6>
+ I* New(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) {
+ return I::cast(NewUncasted<I>(p1, p2, p3, p4, p5, p6));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4, class P5, class P6>
+ HInstruction* AddUncasted(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) {
+ return AddInstruction(NewUncasted<I>(p1, p2, p3, p4, p5, p6));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4, class P5, class P6>
+ I* Add(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) {
+ return I::cast(AddInstruction(NewUncasted<I>(p1, p2, p3, p4, p5, p6)));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4,
+ class P5, class P6, class P7>
+ HInstruction* NewUncasted(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) {
+ return I::New(zone(), context(), p1, p2, p3, p4, p5, p6, p7);
+ }
+
+ template<class I, class P1, class P2, class P3, class P4,
+ class P5, class P6, class P7>
+ I* New(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) {
+ return I::cast(NewUncasted<I>(p1, p2, p3, p4, p5, p6, p7));
+ }
+
+ template<class I, class P1, class P2, class P3,
+ class P4, class P5, class P6, class P7>
+ HInstruction* AddUncasted(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) {
+ return AddInstruction(NewUncasted<I>(p1, p2, p3, p4, p5, p6, p7));
+ }
+
+ template<class I, class P1, class P2, class P3,
+ class P4, class P5, class P6, class P7>
+ I* Add(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7) {
+ return I::cast(AddInstruction(NewUncasted<I>(p1, p2, p3, p4,
+ p5, p6, p7)));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4,
+ class P5, class P6, class P7, class P8>
+ HInstruction* NewUncasted(P1 p1, P2 p2, P3 p3, P4 p4,
+ P5 p5, P6 p6, P7 p7, P8 p8) {
+ return I::New(zone(), context(), p1, p2, p3, p4, p5, p6, p7, p8);
+ }
+
+ template<class I, class P1, class P2, class P3, class P4,
+ class P5, class P6, class P7, class P8>
+ I* New(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) {
+ return I::cast(NewUncasted<I>(p1, p2, p3, p4, p5, p6, p7, p8));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4,
+ class P5, class P6, class P7, class P8>
+ HInstruction* AddUncasted(P1 p1, P2 p2, P3 p3, P4 p4,
+ P5 p5, P6 p6, P7 p7, P8 p8) {
+ return AddInstruction(NewUncasted<I>(p1, p2, p3, p4, p5, p6, p7, p8));
+ }
+
+ template<class I, class P1, class P2, class P3, class P4,
+ class P5, class P6, class P7, class P8>
+ I* Add(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8) {
+ return I::cast(
+ AddInstruction(NewUncasted<I>(p1, p2, p3, p4, p5, p6, p7, p8)));
+ }
+
+ void AddSimulate(BailoutId id, RemovableSimulate removable = FIXED_SIMULATE);
+
+ protected:
+ virtual bool BuildGraph() = 0;
+
+ HBasicBlock* CreateBasicBlock(HEnvironment* env);
+ HBasicBlock* CreateLoopHeaderBlock();
+
+ HValue* BuildCheckHeapObject(HValue* object);
+ HValue* BuildCheckMap(HValue* obj, Handle<Map> map);
+ HValue* BuildWrapReceiver(HValue* object, HValue* function);
+
+ // Building common constructs
+ HValue* BuildCheckForCapacityGrow(HValue* object,
+ HValue* elements,
+ ElementsKind kind,
+ HValue* length,
+ HValue* key,
+ bool is_js_array);
+
+ HValue* BuildCopyElementsOnWrite(HValue* object,
+ HValue* elements,
+ ElementsKind kind,
+ HValue* length);
+
+ void BuildTransitionElementsKind(HValue* object,
+ HValue* map,
+ ElementsKind from_kind,
+ ElementsKind to_kind,
+ bool is_jsarray);
+
+ HInstruction* BuildUncheckedMonomorphicElementAccess(
+ HValue* object,
+ HValue* key,
+ HValue* val,
+ HCheckMaps* mapcheck,
+ bool is_js_array,
+ ElementsKind elements_kind,
+ bool is_store,
+ LoadKeyedHoleMode load_mode,
+ KeyedAccessStoreMode store_mode);
+
+ HInstruction* AddExternalArrayElementAccess(
+ HValue* external_elements,
+ HValue* checked_key,
+ HValue* val,
+ HValue* dependency,
+ ElementsKind elements_kind,
+ bool is_store);
+
+ HInstruction* AddFastElementAccess(
+ HValue* elements,
+ HValue* checked_key,
+ HValue* val,
+ HValue* dependency,
+ ElementsKind elements_kind,
+ bool is_store,
+ LoadKeyedHoleMode load_mode,
+ KeyedAccessStoreMode store_mode);
+
+ HLoadNamedField* BuildLoadNamedField(
+ HValue* object,
+ HObjectAccess access,
+ HValue* typecheck);
+ HInstruction* BuildLoadStringLength(HValue* object, HValue* typecheck);
+ HStoreNamedField* AddStoreMapConstant(HValue *object, Handle<Map>);
+ HLoadNamedField* AddLoadElements(HValue *object, HValue *typecheck);
+ HLoadNamedField* AddLoadFixedArrayLength(HValue *object);
+
+ HValue* AddLoadJSBuiltin(Builtins::JavaScript builtin);
+
+ HValue* TruncateToNumber(HValue* value, Handle<Type>* expected);
+
+ void PushAndAdd(HInstruction* instr);
+
+ void FinishExitWithHardDeoptimization(const char* reason,
+ HBasicBlock* continuation);
+
+ void AddIncrementCounter(StatsCounter* counter,
+ HValue* context);
+
+ class IfBuilder {
+ public:
+ explicit IfBuilder(HGraphBuilder* builder,
+ int position = RelocInfo::kNoPosition);
+ IfBuilder(HGraphBuilder* builder,
+ HIfContinuation* continuation);
+
+ ~IfBuilder() {
+ if (!finished_) End();
+ }
+
+ template<class Condition>
+ HInstruction* If(HValue *p) {
+ HControlInstruction* compare = new(zone()) Condition(p);
+ AddCompare(compare);
+ return compare;
+ }
+
+ template<class Condition, class P2>
+ HInstruction* If(HValue* p1, P2 p2) {
+ HControlInstruction* compare = new(zone()) Condition(p1, p2);
+ AddCompare(compare);
+ return compare;
+ }
+
+ template<class Condition, class P2, class P3>
+ HInstruction* If(HValue* p1, P2 p2, P3 p3) {
+ HControlInstruction* compare = new(zone()) Condition(p1, p2, p3);
+ AddCompare(compare);
+ return compare;
+ }
+
+ template<class Condition, class P2>
+ HInstruction* IfNot(HValue* p1, P2 p2) {
+ HControlInstruction* compare = new(zone()) Condition(p1, p2);
+ AddCompare(compare);
+ HBasicBlock* block0 = compare->SuccessorAt(0);
+ HBasicBlock* block1 = compare->SuccessorAt(1);
+ compare->SetSuccessorAt(0, block1);
+ compare->SetSuccessorAt(1, block0);
+ return compare;
+ }
+
+ template<class Condition, class P2, class P3>
+ HInstruction* IfNot(HValue* p1, P2 p2, P3 p3) {
+ HControlInstruction* compare = new(zone()) Condition(p1, p2, p3);
+ AddCompare(compare);
+ HBasicBlock* block0 = compare->SuccessorAt(0);
+ HBasicBlock* block1 = compare->SuccessorAt(1);
+ compare->SetSuccessorAt(0, block1);
+ compare->SetSuccessorAt(1, block0);
+ return compare;
+ }
+
+ template<class Condition>
+ HInstruction* OrIf(HValue *p) {
+ Or();
+ return If<Condition>(p);
+ }
+
+ template<class Condition, class P2>
+ HInstruction* OrIf(HValue* p1, P2 p2) {
+ Or();
+ return If<Condition>(p1, p2);
+ }
+
+ template<class Condition, class P2, class P3>
+ HInstruction* OrIf(HValue* p1, P2 p2, P3 p3) {
+ Or();
+ return If<Condition>(p1, p2, p3);
+ }
+
+ template<class Condition>
+ HInstruction* AndIf(HValue *p) {
+ And();
+ return If<Condition>(p);
+ }
+
+ template<class Condition, class P2>
+ HInstruction* AndIf(HValue* p1, P2 p2) {
+ And();
+ return If<Condition>(p1, p2);
+ }
+
+ template<class Condition, class P2, class P3>
+ HInstruction* AndIf(HValue* p1, P2 p2, P3 p3) {
+ And();
+ return If<Condition>(p1, p2, p3);
+ }
+
+ void Or();
+ void And();
+
+ void CaptureContinuation(HIfContinuation* continuation);
+
+ void Then();
+ void Else();
+ void End();
+
+ void Deopt(const char* reason);
+ void ElseDeopt(const char* reason) {
+ Else();
+ Deopt(reason);
+ }
+
+ void Return(HValue* value);
+
+ private:
+ void AddCompare(HControlInstruction* compare);
+
+ Zone* zone() { return builder_->zone(); }
+
+ HGraphBuilder* builder_;
+ int position_;
+ bool finished_ : 1;
+ bool deopt_then_ : 1;
+ bool deopt_else_ : 1;
+ bool did_then_ : 1;
+ bool did_else_ : 1;
+ bool did_and_ : 1;
+ bool did_or_ : 1;
+ bool captured_ : 1;
+ bool needs_compare_ : 1;
+ HBasicBlock* first_true_block_;
+ HBasicBlock* last_true_block_;
+ HBasicBlock* first_false_block_;
+ HBasicBlock* split_edge_merge_block_;
+ HBasicBlock* merge_block_;
+ };
+
+ class LoopBuilder {
+ public:
+ enum Direction {
+ kPreIncrement,
+ kPostIncrement,
+ kPreDecrement,
+ kPostDecrement
+ };
+
+ LoopBuilder(HGraphBuilder* builder,
+ HValue* context,
+ Direction direction);
+ ~LoopBuilder() {
+ ASSERT(finished_);
+ }
+
+ HValue* BeginBody(
+ HValue* initial,
+ HValue* terminating,
+ Token::Value token);
+ void EndBody();
+
+ private:
+ Zone* zone() { return builder_->zone(); }
+
+ HGraphBuilder* builder_;
+ HValue* context_;
+ HInstruction* increment_;
+ HPhi* phi_;
+ HBasicBlock* header_block_;
+ HBasicBlock* body_block_;
+ HBasicBlock* exit_block_;
+ Direction direction_;
+ bool finished_;
+ };
+
+ HValue* BuildNewElementsCapacity(HValue* old_capacity);
+
+ void BuildNewSpaceArrayCheck(HValue* length,
+ ElementsKind kind);
+
+ class JSArrayBuilder {
+ public:
+ JSArrayBuilder(HGraphBuilder* builder,
+ ElementsKind kind,
+ HValue* allocation_site_payload,
+ HValue* constructor_function,
+ AllocationSiteOverrideMode override_mode);
+
+ JSArrayBuilder(HGraphBuilder* builder,
+ ElementsKind kind,
+ HValue* constructor_function);
+
+ HValue* AllocateEmptyArray();
+ HValue* AllocateArray(HValue* capacity, HValue* length_field,
+ bool fill_with_hole);
+ HValue* GetElementsLocation() { return elements_location_; }
+
+ private:
+ Zone* zone() const { return builder_->zone(); }
+ int elements_size() const {
+ return IsFastDoubleElementsKind(kind_) ? kDoubleSize : kPointerSize;
+ }
+ HGraphBuilder* builder() { return builder_; }
+ HGraph* graph() { return builder_->graph(); }
+ int initial_capacity() {
+ STATIC_ASSERT(JSArray::kPreallocatedArrayElements > 0);
+ return JSArray::kPreallocatedArrayElements;
+ }
+
+ HValue* EmitMapCode();
+ HValue* EmitInternalMapCode();
+ HValue* EstablishEmptyArrayAllocationSize();
+ HValue* EstablishAllocationSize(HValue* length_node);
+ HValue* AllocateArray(HValue* size_in_bytes, HValue* capacity,
+ HValue* length_field, bool fill_with_hole);
+
+ HGraphBuilder* builder_;
+ ElementsKind kind_;
+ AllocationSiteMode mode_;
+ HValue* allocation_site_payload_;
+ HValue* constructor_function_;
+ HInnerAllocatedObject* elements_location_;
+ };
+
+ HValue* BuildAllocateElements(ElementsKind kind,
+ HValue* capacity);
+
+ void BuildInitializeElementsHeader(HValue* elements,
+ ElementsKind kind,
+ HValue* capacity);
+
+ HValue* BuildAllocateElementsAndInitializeElementsHeader(ElementsKind kind,
+ HValue* capacity);
+
+ // array must have been allocated with enough room for
+ // 1) the JSArray, 2) a AllocationMemento if mode requires it,
+ // 3) a FixedArray or FixedDoubleArray.
+ // A pointer to the Fixed(Double)Array is returned.
+ HInnerAllocatedObject* BuildJSArrayHeader(HValue* array,
+ HValue* array_map,
+ AllocationSiteMode mode,
+ ElementsKind elements_kind,
+ HValue* allocation_site_payload,
+ HValue* length_field);
+
+ HValue* BuildGrowElementsCapacity(HValue* object,
+ HValue* elements,
+ ElementsKind kind,
+ ElementsKind new_kind,
+ HValue* length,
+ HValue* new_capacity);
+
+ void BuildFillElementsWithHole(HValue* elements,
+ ElementsKind elements_kind,
+ HValue* from,
+ HValue* to);
+
+ void BuildCopyElements(HValue* from_elements,
+ ElementsKind from_elements_kind,
+ HValue* to_elements,
+ ElementsKind to_elements_kind,
+ HValue* length,
+ HValue* capacity);
+
+ HValue* BuildCloneShallowArray(HValue* boilerplate,
+ HValue* allocation_site,
+ AllocationSiteMode mode,
+ ElementsKind kind,
+ int length);
+
+ void BuildCompareNil(
+ HValue* value,
+ Handle<Type> type,
+ int position,
+ HIfContinuation* continuation);
+
+ HValue* BuildCreateAllocationMemento(HValue* previous_object,
+ int previous_object_size,
+ HValue* payload);
+
+ void BuildConstantMapCheck(Handle<JSObject> constant, CompilationInfo* info);
+ void BuildCheckPrototypeMaps(Handle<JSObject> prototype,
+ Handle<JSObject> holder);
+
+ HInstruction* BuildGetNativeContext();
+ HInstruction* BuildGetArrayFunction();
+
+ private:
+ HGraphBuilder();
+
+ void PadEnvironmentForContinuation(HBasicBlock* from,
+ HBasicBlock* continuation);
+
+ CompilationInfo* info_;
+ HGraph* graph_;
+ HBasicBlock* current_block_;
+};
+
+
+template<>
+inline HInstruction* HGraphBuilder::AddUncasted<HDeoptimize>(
+ const char* reason, Deoptimizer::BailoutType type) {
+ if (type == Deoptimizer::SOFT) {
+ isolate()->counters()->soft_deopts_requested()->Increment();
+ if (FLAG_always_opt) return NULL;
+ }
+ if (current_block()->IsDeoptimizing()) return NULL;
+ HDeoptimize* instr = New<HDeoptimize>(reason, type);
+ AddInstruction(instr);
+ if (type == Deoptimizer::SOFT) {
+ isolate()->counters()->soft_deopts_inserted()->Increment();
+ graph()->set_has_soft_deoptimize(true);
+ }
+ current_block()->MarkAsDeoptimizing();
+ return instr;
+}
+
+
+template<>
+inline HDeoptimize* HGraphBuilder::Add<HDeoptimize>(
+ const char* reason, Deoptimizer::BailoutType type) {
+ return static_cast<HDeoptimize*>(AddUncasted<HDeoptimize>(reason, type));
+}
+
+
+template<>
+inline HInstruction* HGraphBuilder::AddUncasted<HSimulate>(
+ BailoutId id,
+ RemovableSimulate removable) {
+ HSimulate* instr = current_block()->CreateSimulate(id, removable);
+ AddInstruction(instr);
+ return instr;
+}
+
+
+template<>
+inline HInstruction* HGraphBuilder::NewUncasted<HLoadNamedField>(
+ HValue* object, HObjectAccess access) {
+ return NewUncasted<HLoadNamedField>(object, access,
+ static_cast<HValue*>(NULL));
+}
+
+
+template<>
+inline HInstruction* HGraphBuilder::AddUncasted<HLoadNamedField>(
+ HValue* object, HObjectAccess access) {
+ return AddUncasted<HLoadNamedField>(object, access,
+ static_cast<HValue*>(NULL));
+}
+
+
+template<>
+inline HInstruction* HGraphBuilder::AddUncasted<HSimulate>(BailoutId id) {
+ return AddUncasted<HSimulate>(id, FIXED_SIMULATE);
+}
+
+
+template<>
+inline HInstruction* HGraphBuilder::AddUncasted<HReturn>(HValue* value) {
+ int num_parameters = graph()->info()->num_parameters();
+ HValue* params = AddUncasted<HConstant>(num_parameters);
+ HReturn* return_instruction = New<HReturn>(value, params);
+ current_block()->FinishExit(return_instruction);
+ return return_instruction;
+}
+
+
+template<>
+inline HInstruction* HGraphBuilder::AddUncasted<HReturn>(HConstant* value) {
+ return AddUncasted<HReturn>(static_cast<HValue*>(value));
+}
+
+
+template<>
+inline HInstruction* HGraphBuilder::NewUncasted<HContext>() {
+ return HContext::New(zone());
+}
+
+
+class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
+ public:
+ // A class encapsulating (lazily-allocated) break and continue blocks for
+ // a breakable statement. Separated from BreakAndContinueScope so that it
+ // can have a separate lifetime.
+ class BreakAndContinueInfo BASE_EMBEDDED {
+ public:
+ explicit BreakAndContinueInfo(BreakableStatement* target,
+ int drop_extra = 0)
+ : target_(target),
+ break_block_(NULL),
+ continue_block_(NULL),
+ drop_extra_(drop_extra) {
+ }
+
+ BreakableStatement* target() { return target_; }
+ HBasicBlock* break_block() { return break_block_; }
+ void set_break_block(HBasicBlock* block) { break_block_ = block; }
+ HBasicBlock* continue_block() { return continue_block_; }
+ void set_continue_block(HBasicBlock* block) { continue_block_ = block; }
+ int drop_extra() { return drop_extra_; }
+
+ private:
+ BreakableStatement* target_;
+ HBasicBlock* break_block_;
+ HBasicBlock* continue_block_;
+ int drop_extra_;
+ };
+
+ // A helper class to maintain a stack of current BreakAndContinueInfo
+ // structures mirroring BreakableStatement nesting.
+ class BreakAndContinueScope BASE_EMBEDDED {
+ public:
+ BreakAndContinueScope(BreakAndContinueInfo* info,
+ HOptimizedGraphBuilder* owner)
+ : info_(info), owner_(owner), next_(owner->break_scope()) {
+ owner->set_break_scope(this);
+ }
+
+ ~BreakAndContinueScope() { owner_->set_break_scope(next_); }
+
+ BreakAndContinueInfo* info() { return info_; }
+ HOptimizedGraphBuilder* owner() { return owner_; }
+ BreakAndContinueScope* next() { return next_; }
+
+ // Search the break stack for a break or continue target.
+ enum BreakType { BREAK, CONTINUE };
+ HBasicBlock* Get(BreakableStatement* stmt, BreakType type, int* drop_extra);
+
+ private:
+ BreakAndContinueInfo* info_;
+ HOptimizedGraphBuilder* owner_;
+ BreakAndContinueScope* next_;
+ };
+
+ explicit HOptimizedGraphBuilder(CompilationInfo* info);
+
+ virtual bool BuildGraph();
+
+ // Simple accessors.
+ BreakAndContinueScope* break_scope() const { return break_scope_; }
+ void set_break_scope(BreakAndContinueScope* head) { break_scope_ = head; }
+
+ bool inline_bailout() { return inline_bailout_; }
+
+ HValue* context() { return environment()->context(); }
+
+ void Bailout(BailoutReason reason);
+
+ HBasicBlock* CreateJoin(HBasicBlock* first,
+ HBasicBlock* second,
+ BailoutId join_id);
+
+ FunctionState* function_state() const { return function_state_; }
+
+ void VisitDeclarations(ZoneList<Declaration*>* declarations);
+
+ void* operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+ }
+ void operator delete(void* pointer, Zone* zone) { }
+ void operator delete(void* pointer) { }
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+
+ private:
+ // Type of a member function that generates inline code for a native function.
+ typedef void (HOptimizedGraphBuilder::*InlineFunctionGenerator)
+ (CallRuntime* call);
+
+ // Forward declarations for inner scope classes.
+ class SubgraphScope;
+
+ static const InlineFunctionGenerator kInlineFunctionGenerators[];
+
+ static const int kMaxCallPolymorphism = 4;
+ static const int kMaxLoadPolymorphism = 4;
+ static const int kMaxStorePolymorphism = 4;
+
+ // Even in the 'unlimited' case we have to have some limit in order not to
+ // overflow the stack.
+ static const int kUnlimitedMaxInlinedSourceSize = 100000;
+ static const int kUnlimitedMaxInlinedNodes = 10000;
+ static const int kUnlimitedMaxInlinedNodesCumulative = 10000;
+
+ // Maximum depth and total number of elements and properties for literal
+ // graphs to be considered for fast deep-copying.
+ static const int kMaxFastLiteralDepth = 3;
+ static const int kMaxFastLiteralProperties = 8;
+
+ // Simple accessors.
+ void set_function_state(FunctionState* state) { function_state_ = state; }
+
+ AstContext* ast_context() const { return ast_context_; }
+ void set_ast_context(AstContext* context) { ast_context_ = context; }
+
+ // Accessors forwarded to the function state.
+ CompilationInfo* current_info() const {
+ return function_state()->compilation_info();
+ }
+ AstContext* call_context() const {
+ return function_state()->call_context();
+ }
+ HBasicBlock* function_return() const {
+ return function_state()->function_return();
+ }
+ TestContext* inlined_test_context() const {
+ return function_state()->test_context();
+ }
+ void ClearInlinedTestContext() {
+ function_state()->ClearInlinedTestContext();
+ }
+ StrictModeFlag function_strict_mode_flag() {
+ return function_state()->compilation_info()->is_classic_mode()
+ ? kNonStrictMode : kStrictMode;
+ }
+
+ // Generators for inline runtime functions.
+#define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \
+ void Generate##Name(CallRuntime* call);
+
+ INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION)
+ INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION)
+#undef INLINE_FUNCTION_GENERATOR_DECLARATION
+
+ void VisitDelete(UnaryOperation* expr);
+ void VisitVoid(UnaryOperation* expr);
+ void VisitTypeof(UnaryOperation* expr);
+ void VisitNot(UnaryOperation* expr);
+
+ void VisitComma(BinaryOperation* expr);
+ void VisitLogicalExpression(BinaryOperation* expr);
+ void VisitArithmeticExpression(BinaryOperation* expr);
+
+ bool PreProcessOsrEntry(IterationStatement* statement);
+ void VisitLoopBody(IterationStatement* stmt,
+ HBasicBlock* loop_entry,
+ BreakAndContinueInfo* break_info);
+
+ // Create a back edge in the flow graph. body_exit is the predecessor
+ // block and loop_entry is the successor block. loop_successor is the
+ // block where control flow exits the loop normally (e.g., via failure of
+ // the condition) and break_block is the block where control flow breaks
+ // from the loop. All blocks except loop_entry can be NULL. The return
+ // value is the new successor block which is the join of loop_successor
+ // and break_block, or NULL.
+ HBasicBlock* CreateLoop(IterationStatement* statement,
+ HBasicBlock* loop_entry,
+ HBasicBlock* body_exit,
+ HBasicBlock* loop_successor,
+ HBasicBlock* break_block);
+
+ HBasicBlock* JoinContinue(IterationStatement* statement,
+ HBasicBlock* exit_block,
+ HBasicBlock* continue_block);
+
+ HValue* Top() const { return environment()->Top(); }
+ void Drop(int n) { environment()->Drop(n); }
+ void Bind(Variable* var, HValue* value) { environment()->Bind(var, value); }
+ bool IsEligibleForEnvironmentLivenessAnalysis(Variable* var,
+ int index,
+ HValue* value,
+ HEnvironment* env) {
+ if (!FLAG_analyze_environment_liveness) return false;
+ // |this| and |arguments| are always live; zapping parameters isn't
+ // safe because function.arguments can inspect them at any time.
+ return !var->is_this() &&
+ !var->is_arguments() &&
+ !value->IsArgumentsObject() &&
+ env->is_local_index(index);
+ }
+ void BindIfLive(Variable* var, HValue* value) {
+ HEnvironment* env = environment();
+ int index = env->IndexFor(var);
+ env->Bind(index, value);
+ if (IsEligibleForEnvironmentLivenessAnalysis(var, index, value, env)) {
+ HEnvironmentMarker* bind =
+ new(zone()) HEnvironmentMarker(HEnvironmentMarker::BIND, index);
+ AddInstruction(bind);
+#ifdef DEBUG
+ bind->set_closure(env->closure());
+#endif
+ }
+ }
+ HValue* LookupAndMakeLive(Variable* var) {
+ HEnvironment* env = environment();
+ int index = env->IndexFor(var);
+ HValue* value = env->Lookup(index);
+ if (IsEligibleForEnvironmentLivenessAnalysis(var, index, value, env)) {
+ HEnvironmentMarker* lookup =
+ new(zone()) HEnvironmentMarker(HEnvironmentMarker::LOOKUP, index);
+ AddInstruction(lookup);
+#ifdef DEBUG
+ lookup->set_closure(env->closure());
+#endif
+ }
+ return value;
+ }
+
+ // The value of the arguments object is allowed in some but not most value
+ // contexts. (It's allowed in all effect contexts and disallowed in all
+ // test contexts.)
+ void VisitForValue(Expression* expr,
+ ArgumentsAllowedFlag flag = ARGUMENTS_NOT_ALLOWED);
+ void VisitForTypeOf(Expression* expr);
+ void VisitForEffect(Expression* expr);
+ void VisitForControl(Expression* expr,
+ HBasicBlock* true_block,
+ HBasicBlock* false_block);
+
+ // Visit an argument subexpression and emit a push to the outgoing arguments.
+ void VisitArgument(Expression* expr);
+
+ void VisitArgumentList(ZoneList<Expression*>* arguments);
+
+ // Visit a list of expressions from left to right, each in a value context.
+ void VisitExpressions(ZoneList<Expression*>* exprs);
+
+ // Remove the arguments from the bailout environment and emit instructions
+ // to push them as outgoing parameters.
+ template <class Instruction> HInstruction* PreProcessCall(Instruction* call);
+
+ void SetUpScope(Scope* scope);
+ virtual void VisitStatements(ZoneList<Statement*>* statements);
+
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ // Helpers for flow graph construction.
+ enum GlobalPropertyAccess {
+ kUseCell,
+ kUseGeneric
+ };
+ GlobalPropertyAccess LookupGlobalProperty(Variable* var,
+ LookupResult* lookup,
+ bool is_store);
+
+ void EnsureArgumentsArePushedForAccess();
+ bool TryArgumentsAccess(Property* expr);
+
+ // Try to optimize fun.apply(receiver, arguments) pattern.
+ bool TryCallApply(Call* expr);
+
+ int InliningAstSize(Handle<JSFunction> target);
+ bool TryInline(CallKind call_kind,
+ Handle<JSFunction> target,
+ int arguments_count,
+ HValue* implicit_return_value,
+ BailoutId ast_id,
+ BailoutId return_id,
+ InliningKind inlining_kind);
+
+ bool TryInlineCall(Call* expr, bool drop_extra = false);
+ bool TryInlineConstruct(CallNew* expr, HValue* implicit_return_value);
+ bool TryInlineGetter(Handle<JSFunction> getter, Property* prop);
+ bool TryInlineSetter(Handle<JSFunction> setter,
+ BailoutId id,
+ BailoutId assignment_id,
+ HValue* implicit_return_value);
+ bool TryInlineApply(Handle<JSFunction> function,
+ Call* expr,
+ int arguments_count);
+ 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
+ // non-NULL reason string.
+ void TraceInline(Handle<JSFunction> target,
+ Handle<JSFunction> caller,
+ const char* failure_reason);
+
+ void HandleGlobalVariableAssignment(Variable* var,
+ HValue* value,
+ int position,
+ BailoutId ast_id);
+
+ void HandlePropertyAssignment(Assignment* expr);
+ void HandleCompoundAssignment(Assignment* expr);
+ void HandlePolymorphicLoadNamedField(Property* expr,
+ HValue* object,
+ SmallMapList* types,
+ Handle<String> name);
+ HInstruction* TryLoadPolymorphicAsMonomorphic(Property* expr,
+ HValue* object,
+ SmallMapList* types,
+ Handle<String> name);
+ void HandlePolymorphicStoreNamedField(int position,
+ BailoutId assignment_id,
+ HValue* object,
+ HValue* value,
+ HValue* result,
+ SmallMapList* types,
+ Handle<String> name);
+ bool TryStorePolymorphicAsMonomorphic(int position,
+ BailoutId assignment_id,
+ HValue* object,
+ HValue* value,
+ HValue* result,
+ SmallMapList* types,
+ Handle<String> name);
+ void HandlePolymorphicCallNamed(Call* expr,
+ HValue* receiver,
+ SmallMapList* types,
+ Handle<String> name);
+ bool TryCallPolymorphicAsMonomorphic(Call* expr,
+ HValue* receiver,
+ SmallMapList* types,
+ Handle<String> name);
+ void HandleLiteralCompareTypeof(CompareOperation* expr,
+ Expression* sub_expr,
+ Handle<String> check);
+ void HandleLiteralCompareNil(CompareOperation* expr,
+ Expression* sub_expr,
+ NilValue nil);
+
+ HInstruction* BuildStringCharCodeAt(HValue* string,
+ HValue* index);
+ HInstruction* BuildBinaryOperation(BinaryOperation* expr,
+ HValue* left,
+ HValue* right);
+ HInstruction* BuildIncrement(bool returns_original_input,
+ CountOperation* expr);
+ HInstruction* BuildLoadKeyedGeneric(HValue* object,
+ HValue* key);
+
+ HInstruction* TryBuildConsolidatedElementLoad(HValue* object,
+ HValue* key,
+ HValue* val,
+ SmallMapList* maps);
+
+ HInstruction* BuildMonomorphicElementAccess(HValue* object,
+ HValue* key,
+ HValue* val,
+ HValue* dependency,
+ Handle<Map> map,
+ bool is_store,
+ KeyedAccessStoreMode store_mode);
+
+ HValue* HandlePolymorphicElementAccess(HValue* object,
+ HValue* key,
+ HValue* val,
+ Expression* prop,
+ BailoutId ast_id,
+ int position,
+ bool is_store,
+ KeyedAccessStoreMode store_mode,
+ bool* has_side_effects);
+
+ HValue* HandleKeyedElementAccess(HValue* obj,
+ HValue* key,
+ HValue* val,
+ Expression* expr,
+ BailoutId ast_id,
+ int position,
+ bool is_store,
+ bool* has_side_effects);
+
+ HInstruction* BuildLoadNamedGeneric(HValue* object,
+ Handle<String> name,
+ Property* expr);
+ HInstruction* BuildCallGetter(HValue* object,
+ Handle<Map> map,
+ Handle<JSFunction> getter,
+ Handle<JSObject> holder);
+ HInstruction* BuildLoadNamedMonomorphic(HValue* object,
+ Handle<String> name,
+ Property* expr,
+ Handle<Map> map);
+
+ HCheckMaps* AddCheckMap(HValue* object, Handle<Map> map);
+
+ void BuildStoreNamed(Expression* expression,
+ BailoutId id,
+ int position,
+ BailoutId assignment_id,
+ Property* prop,
+ HValue* object,
+ HValue* store_value,
+ HValue* result_value);
+
+ HInstruction* BuildStoreNamedField(HValue* object,
+ Handle<String> name,
+ HValue* value,
+ Handle<Map> map,
+ LookupResult* lookup);
+ HInstruction* BuildStoreNamedGeneric(HValue* object,
+ Handle<String> name,
+ HValue* value);
+ HInstruction* BuildStoreNamedMonomorphic(HValue* object,
+ Handle<String> name,
+ HValue* value,
+ Handle<Map> map);
+ HInstruction* BuildStoreKeyedGeneric(HValue* object,
+ HValue* key,
+ HValue* value);
+
+ HValue* BuildContextChainWalk(Variable* var);
+
+ HInstruction* BuildThisFunction();
+
+ HInstruction* BuildFastLiteral(HValue* context,
+ Handle<JSObject> boilerplate_object,
+ Handle<JSObject> original_boilerplate_object,
+ Handle<Object> allocation_site,
+ int data_size,
+ int pointer_size,
+ AllocationSiteMode mode);
+
+ void BuildEmitDeepCopy(Handle<JSObject> boilerplat_object,
+ Handle<JSObject> object,
+ Handle<Object> allocation_site,
+ HInstruction* target,
+ int* offset,
+ HInstruction* data_target,
+ int* data_offset,
+ AllocationSiteMode mode);
+
+ MUST_USE_RESULT HValue* BuildEmitObjectHeader(
+ Handle<JSObject> boilerplat_object,
+ HInstruction* target,
+ HInstruction* data_target,
+ int object_offset,
+ int elements_offset,
+ int elements_size);
+
+ void BuildEmitInObjectProperties(Handle<JSObject> boilerplate_object,
+ Handle<JSObject> original_boilerplate_object,
+ HValue* object_properties,
+ HInstruction* target,
+ int* offset,
+ HInstruction* data_target,
+ int* data_offset);
+
+ void BuildEmitElements(Handle<FixedArrayBase> elements,
+ Handle<FixedArrayBase> original_elements,
+ ElementsKind kind,
+ HValue* object_elements,
+ HInstruction* target,
+ int* offset,
+ HInstruction* data_target,
+ int* data_offset);
+
+ void BuildEmitFixedDoubleArray(Handle<FixedArrayBase> elements,
+ ElementsKind kind,
+ HValue* object_elements);
+
+ void BuildEmitFixedArray(Handle<FixedArrayBase> elements,
+ Handle<FixedArrayBase> original_elements,
+ ElementsKind kind,
+ HValue* object_elements,
+ HInstruction* target,
+ int* offset,
+ HInstruction* data_target,
+ int* data_offset);
+
+ void AddCheckPrototypeMaps(Handle<JSObject> holder,
+ Handle<Map> receiver_map);
+
+ void AddCheckConstantFunction(Handle<JSObject> holder,
+ HValue* receiver,
+ Handle<Map> receiver_map);
+
+ bool MatchRotateRight(HValue* left,
+ HValue* right,
+ HValue** operand,
+ HValue** shift_amount);
+
+ // The translation state of the currently-being-translated function.
+ FunctionState* function_state_;
+
+ // The base of the function state stack.
+ FunctionState initial_function_state_;
+
+ // Expression context of the currently visited subexpression. NULL when
+ // visiting statements.
+ AstContext* ast_context_;
+
+ // A stack of breakable statements entered.
+ BreakAndContinueScope* break_scope_;
+
+ int inlined_count_;
+ ZoneList<Handle<Object> > globals_;
+
+ bool inline_bailout_;
+
+ HOsrBuilder* osr_;
+
+ friend class FunctionState; // Pushes and pops the state stack.
+ friend class AstContext; // Pushes and pops the AST context stack.
+ friend class KeyedLoadFastElementStub;
+ friend class HOsrBuilder;
+
+ DISALLOW_COPY_AND_ASSIGN(HOptimizedGraphBuilder);
+};
+
+
+Zone* AstContext::zone() const { return owner_->zone(); }
+
+
+class HStatistics: public Malloced {
+ public:
+ HStatistics()
+ : timing_(5),
+ names_(5),
+ sizes_(5),
+ create_graph_(0),
+ optimize_graph_(0),
+ generate_code_(0),
+ total_size_(0),
+ full_code_gen_(0),
+ source_size_(0) { }
+
+ void Initialize(CompilationInfo* info);
+ void Print();
+ void SaveTiming(const char* name, int64_t ticks, unsigned size);
+
+ void IncrementFullCodeGen(int64_t full_code_gen) {
+ full_code_gen_ += full_code_gen;
+ }
+
+ void IncrementSubtotals(int64_t create_graph,
+ int64_t optimize_graph,
+ int64_t generate_code) {
+ create_graph_ += create_graph;
+ optimize_graph_ += optimize_graph;
+ generate_code_ += generate_code;
+ }
+
+ private:
+ List<int64_t> timing_;
+ List<const char*> names_;
+ List<unsigned> sizes_;
+ int64_t create_graph_;
+ int64_t optimize_graph_;
+ int64_t generate_code_;
+ unsigned total_size_;
+ int64_t full_code_gen_;
+ double source_size_;
+};
+
+
+class HPhase : public CompilationPhase {
+ public:
+ HPhase(const char* name, HGraph* graph)
+ : CompilationPhase(name, graph->info()),
+ graph_(graph) { }
+ ~HPhase();
+
+ protected:
+ HGraph* graph() const { return graph_; }
+
+ private:
+ HGraph* graph_;
+
+ DISALLOW_COPY_AND_ASSIGN(HPhase);
+};
+
+
+class HTracer: public Malloced {
+ public:
+ explicit HTracer(int isolate_id)
+ : trace_(&string_allocator_), indent_(0) {
+ if (FLAG_trace_hydrogen_file == NULL) {
+ OS::SNPrintF(filename_,
+ "hydrogen-%d-%d.cfg",
+ OS::GetCurrentProcessId(),
+ isolate_id);
+ } else {
+ OS::StrNCpy(filename_, FLAG_trace_hydrogen_file, filename_.length());
+ }
+ WriteChars(filename_.start(), "", 0, false);
+ }
+
+ void TraceCompilation(CompilationInfo* info);
+ void TraceHydrogen(const char* name, HGraph* graph);
+ void TraceLithium(const char* name, LChunk* chunk);
+ void TraceLiveRanges(const char* name, LAllocator* allocator);
+
+ private:
+ class Tag BASE_EMBEDDED {
+ public:
+ Tag(HTracer* tracer, const char* name) {
+ name_ = name;
+ tracer_ = tracer;
+ tracer->PrintIndent();
+ tracer->trace_.Add("begin_%s\n", name);
+ tracer->indent_++;
+ }
+
+ ~Tag() {
+ tracer_->indent_--;
+ tracer_->PrintIndent();
+ tracer_->trace_.Add("end_%s\n", name_);
+ ASSERT(tracer_->indent_ >= 0);
+ tracer_->FlushToFile();
+ }
+
+ private:
+ HTracer* tracer_;
+ const char* name_;
+ };
+
+ void TraceLiveRange(LiveRange* range, const char* type, Zone* zone);
+ void Trace(const char* name, HGraph* graph, LChunk* chunk);
+ void FlushToFile();
+
+ void PrintEmptyProperty(const char* name) {
+ PrintIndent();
+ trace_.Add("%s\n", name);
+ }
+
+ void PrintStringProperty(const char* name, const char* value) {
+ PrintIndent();
+ trace_.Add("%s \"%s\"\n", name, value);
+ }
+
+ void PrintLongProperty(const char* name, int64_t value) {
+ PrintIndent();
+ trace_.Add("%s %d000\n", name, static_cast<int>(value / 1000));
+ }
+
+ void PrintBlockProperty(const char* name, int block_id) {
+ PrintIndent();
+ trace_.Add("%s \"B%d\"\n", name, block_id);
+ }
+
+ void PrintIntProperty(const char* name, int value) {
+ PrintIndent();
+ trace_.Add("%s %d\n", name, value);
+ }
+
+ void PrintIndent() {
+ for (int i = 0; i < indent_; i++) {
+ trace_.Add(" ");
+ }
+ }
+
+ EmbeddedVector<char, 64> filename_;
+ HeapStringAllocator string_allocator_;
+ StringStream trace_;
+ int indent_;
+};
+
+
+class NoObservableSideEffectsScope {
+ public:
+ explicit NoObservableSideEffectsScope(HGraphBuilder* builder) :
+ builder_(builder) {
+ builder_->graph()->IncrementInNoSideEffectsScope();
+ }
+ ~NoObservableSideEffectsScope() {
+ builder_->graph()->DecrementInNoSideEffectsScope();
+ }
+
+ private:
+ HGraphBuilder* builder_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_HYDROGEN_H_
diff --git a/chromium/v8/src/i18n.cc b/chromium/v8/src/i18n.cc
new file mode 100644
index 00000000000..5cfe4c43b2f
--- /dev/null
+++ b/chromium/v8/src/i18n.cc
@@ -0,0 +1,938 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+#include "i18n.h"
+
+#include "unicode/calendar.h"
+#include "unicode/coll.h"
+#include "unicode/curramt.h"
+#include "unicode/dcfmtsym.h"
+#include "unicode/decimfmt.h"
+#include "unicode/dtfmtsym.h"
+#include "unicode/dtptngen.h"
+#include "unicode/locid.h"
+#include "unicode/numfmt.h"
+#include "unicode/numsys.h"
+#include "unicode/smpdtfmt.h"
+#include "unicode/timezone.h"
+#include "unicode/uchar.h"
+#include "unicode/ucol.h"
+#include "unicode/ucurr.h"
+#include "unicode/unum.h"
+#include "unicode/uversion.h"
+
+namespace v8 {
+namespace internal {
+
+namespace {
+
+bool ExtractStringSetting(Isolate* isolate,
+ Handle<JSObject> options,
+ const char* key,
+ icu::UnicodeString* setting) {
+ Handle<String> str = isolate->factory()->NewStringFromAscii(CStrVector(key));
+ MaybeObject* maybe_object = options->GetProperty(*str);
+ Object* object;
+ if (maybe_object->ToObject(&object) && object->IsString()) {
+ v8::String::Utf8Value utf8_string(
+ v8::Utils::ToLocal(Handle<String>(String::cast(object))));
+ *setting = icu::UnicodeString::fromUTF8(*utf8_string);
+ return true;
+ }
+ return false;
+}
+
+
+bool ExtractIntegerSetting(Isolate* isolate,
+ Handle<JSObject> options,
+ const char* key,
+ int32_t* value) {
+ Handle<String> str = isolate->factory()->NewStringFromAscii(CStrVector(key));
+ MaybeObject* maybe_object = options->GetProperty(*str);
+ Object* object;
+ if (maybe_object->ToObject(&object) && object->IsNumber()) {
+ object->ToInt32(value);
+ return true;
+ }
+ return false;
+}
+
+
+bool ExtractBooleanSetting(Isolate* isolate,
+ Handle<JSObject> options,
+ const char* key,
+ bool* value) {
+ Handle<String> str = isolate->factory()->NewStringFromAscii(CStrVector(key));
+ MaybeObject* maybe_object = options->GetProperty(*str);
+ Object* object;
+ if (maybe_object->ToObject(&object) && object->IsBoolean()) {
+ *value = object->BooleanValue();
+ return true;
+ }
+ return false;
+}
+
+
+icu::SimpleDateFormat* CreateICUDateFormat(
+ Isolate* isolate,
+ const icu::Locale& icu_locale,
+ Handle<JSObject> options) {
+ // Create time zone as specified by the user. We have to re-create time zone
+ // since calendar takes ownership.
+ icu::TimeZone* tz = NULL;
+ icu::UnicodeString timezone;
+ if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) {
+ tz = icu::TimeZone::createTimeZone(timezone);
+ } else {
+ tz = icu::TimeZone::createDefault();
+ }
+
+ // Create a calendar using locale, and apply time zone to it.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Calendar* calendar =
+ icu::Calendar::createInstance(tz, icu_locale, status);
+
+ // Make formatter from skeleton. Calendar and numbering system are added
+ // to the locale as Unicode extension (if they were specified at all).
+ icu::SimpleDateFormat* date_format = NULL;
+ icu::UnicodeString skeleton;
+ if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) {
+ icu::DateTimePatternGenerator* generator =
+ icu::DateTimePatternGenerator::createInstance(icu_locale, status);
+ icu::UnicodeString pattern;
+ if (U_SUCCESS(status)) {
+ pattern = generator->getBestPattern(skeleton, status);
+ delete generator;
+ }
+
+ date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
+ if (U_SUCCESS(status)) {
+ date_format->adoptCalendar(calendar);
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ delete calendar;
+ delete date_format;
+ date_format = NULL;
+ }
+
+ return date_format;
+}
+
+
+void SetResolvedDateSettings(Isolate* isolate,
+ const icu::Locale& icu_locale,
+ icu::SimpleDateFormat* date_format,
+ Handle<JSObject> resolved) {
+ UErrorCode status = U_ZERO_ERROR;
+ icu::UnicodeString pattern;
+ date_format->toPattern(pattern);
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("pattern")),
+ isolate->factory()->NewStringFromTwoByte(
+ Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
+ pattern.length())),
+ NONE,
+ kNonStrictMode);
+
+ // Set time zone and calendar.
+ const icu::Calendar* calendar = date_format->getCalendar();
+ const char* calendar_name = calendar->getType();
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("calendar")),
+ isolate->factory()->NewStringFromAscii(CStrVector(calendar_name)),
+ NONE,
+ kNonStrictMode);
+
+ const icu::TimeZone& tz = calendar->getTimeZone();
+ icu::UnicodeString time_zone;
+ tz.getID(time_zone);
+
+ icu::UnicodeString canonical_time_zone;
+ icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
+ if (U_SUCCESS(status)) {
+ if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("timeZone")),
+ isolate->factory()->NewStringFromAscii(CStrVector("UTC")),
+ NONE,
+ kNonStrictMode);
+ } else {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("timeZone")),
+ isolate->factory()->NewStringFromTwoByte(
+ Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(
+ canonical_time_zone.getBuffer()),
+ canonical_time_zone.length())),
+ NONE,
+ kNonStrictMode);
+ }
+ }
+
+ // Ugly hack. ICU doesn't expose numbering system in any way, so we have
+ // to assume that for given locale NumberingSystem constructor produces the
+ // same digits as NumberFormat/Calendar would.
+ status = U_ZERO_ERROR;
+ icu::NumberingSystem* numbering_system =
+ icu::NumberingSystem::createInstance(icu_locale, status);
+ if (U_SUCCESS(status)) {
+ const char* ns = numbering_system->getName();
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("numberingSystem")),
+ isolate->factory()->NewStringFromAscii(CStrVector(ns)),
+ NONE,
+ kNonStrictMode);
+ } else {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("numberingSystem")),
+ isolate->factory()->undefined_value(),
+ NONE,
+ kNonStrictMode);
+ }
+ delete numbering_system;
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("locale")),
+ isolate->factory()->NewStringFromAscii(CStrVector(result)),
+ NONE,
+ kNonStrictMode);
+ } else {
+ // This would never happen, since we got the locale from ICU.
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("locale")),
+ isolate->factory()->NewStringFromAscii(CStrVector("und")),
+ NONE,
+ kNonStrictMode);
+ }
+}
+
+
+template<int internal_fields, EternalHandles::SingletonHandle field>
+Handle<ObjectTemplateInfo> GetEternal(Isolate* isolate) {
+ if (isolate->eternal_handles()->Exists(field)) {
+ return Handle<ObjectTemplateInfo>::cast(
+ isolate->eternal_handles()->GetSingleton(field));
+ }
+ v8::Local<v8::ObjectTemplate> raw_template(v8::ObjectTemplate::New());
+ raw_template->SetInternalFieldCount(internal_fields);
+ return Handle<ObjectTemplateInfo>::cast(
+ isolate->eternal_handles()->CreateSingleton(
+ isolate,
+ *v8::Utils::OpenHandle(*raw_template),
+ field));
+}
+
+
+icu::DecimalFormat* CreateICUNumberFormat(
+ Isolate* isolate,
+ const icu::Locale& icu_locale,
+ Handle<JSObject> options) {
+ // Make formatter from options. Numbering system is added
+ // to the locale as Unicode extension (if it was specified at all).
+ UErrorCode status = U_ZERO_ERROR;
+ icu::DecimalFormat* number_format = NULL;
+ icu::UnicodeString style;
+ icu::UnicodeString currency;
+ if (ExtractStringSetting(isolate, options, "style", &style)) {
+ if (style == UNICODE_STRING_SIMPLE("currency")) {
+ icu::UnicodeString display;
+ ExtractStringSetting(isolate, options, "currency", &currency);
+ ExtractStringSetting(isolate, options, "currencyDisplay", &display);
+
+#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
+ icu::NumberFormat::EStyles format_style;
+ if (display == UNICODE_STRING_SIMPLE("code")) {
+ format_style = icu::NumberFormat::kIsoCurrencyStyle;
+ } else if (display == UNICODE_STRING_SIMPLE("name")) {
+ format_style = icu::NumberFormat::kPluralCurrencyStyle;
+ } else {
+ format_style = icu::NumberFormat::kCurrencyStyle;
+ }
+#else // ICU version is 4.8 or above (we ignore versions below 4.0).
+ UNumberFormatStyle format_style;
+ if (display == UNICODE_STRING_SIMPLE("code")) {
+ format_style = UNUM_CURRENCY_ISO;
+ } else if (display == UNICODE_STRING_SIMPLE("name")) {
+ format_style = UNUM_CURRENCY_PLURAL;
+ } else {
+ format_style = UNUM_CURRENCY;
+ }
+#endif
+
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, format_style, status));
+ } else if (style == UNICODE_STRING_SIMPLE("percent")) {
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createPercentInstance(icu_locale, status));
+ if (U_FAILURE(status)) {
+ delete number_format;
+ return NULL;
+ }
+ // Make sure 1.1% doesn't go into 2%.
+ number_format->setMinimumFractionDigits(1);
+ } else {
+ // Make a decimal instance by default.
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, status));
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ delete number_format;
+ return NULL;
+ }
+
+ // Set all options.
+ if (!currency.isEmpty()) {
+ number_format->setCurrency(currency.getBuffer(), status);
+ }
+
+ int32_t digits;
+ if (ExtractIntegerSetting(
+ isolate, options, "minimumIntegerDigits", &digits)) {
+ number_format->setMinimumIntegerDigits(digits);
+ }
+
+ if (ExtractIntegerSetting(
+ isolate, options, "minimumFractionDigits", &digits)) {
+ number_format->setMinimumFractionDigits(digits);
+ }
+
+ if (ExtractIntegerSetting(
+ isolate, options, "maximumFractionDigits", &digits)) {
+ number_format->setMaximumFractionDigits(digits);
+ }
+
+ bool significant_digits_used = false;
+ if (ExtractIntegerSetting(
+ isolate, options, "minimumSignificantDigits", &digits)) {
+ number_format->setMinimumSignificantDigits(digits);
+ significant_digits_used = true;
+ }
+
+ if (ExtractIntegerSetting(
+ isolate, options, "maximumSignificantDigits", &digits)) {
+ number_format->setMaximumSignificantDigits(digits);
+ significant_digits_used = true;
+ }
+
+ number_format->setSignificantDigitsUsed(significant_digits_used);
+
+ bool grouping;
+ if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) {
+ number_format->setGroupingUsed(grouping);
+ }
+
+ // Set rounding mode.
+ number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
+
+ return number_format;
+}
+
+
+void SetResolvedNumberSettings(Isolate* isolate,
+ const icu::Locale& icu_locale,
+ icu::DecimalFormat* number_format,
+ Handle<JSObject> resolved) {
+ icu::UnicodeString pattern;
+ number_format->toPattern(pattern);
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("pattern")),
+ isolate->factory()->NewStringFromTwoByte(
+ Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
+ pattern.length())),
+ NONE,
+ kNonStrictMode);
+
+ // Set resolved currency code in options.currency if not empty.
+ icu::UnicodeString currency(number_format->getCurrency());
+ if (!currency.isEmpty()) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("currency")),
+ isolate->factory()->NewStringFromTwoByte(
+ Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(currency.getBuffer()),
+ currency.length())),
+ NONE,
+ kNonStrictMode);
+ }
+
+ // Ugly hack. ICU doesn't expose numbering system in any way, so we have
+ // to assume that for given locale NumberingSystem constructor produces the
+ // same digits as NumberFormat/Calendar would.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::NumberingSystem* numbering_system =
+ icu::NumberingSystem::createInstance(icu_locale, status);
+ if (U_SUCCESS(status)) {
+ const char* ns = numbering_system->getName();
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("numberingSystem")),
+ isolate->factory()->NewStringFromAscii(CStrVector(ns)),
+ NONE,
+ kNonStrictMode);
+ } else {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("numberingSystem")),
+ isolate->factory()->undefined_value(),
+ NONE,
+ kNonStrictMode);
+ }
+ delete numbering_system;
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("useGrouping")),
+ isolate->factory()->ToBoolean(number_format->isGroupingUsed()),
+ NONE,
+ kNonStrictMode);
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("minimumIntegerDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMinimumIntegerDigits()),
+ NONE,
+ kNonStrictMode);
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("minimumFractionDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMinimumFractionDigits()),
+ NONE,
+ kNonStrictMode);
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("maximumFractionDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMaximumFractionDigits()),
+ NONE,
+ kNonStrictMode);
+
+ Handle<String> key = isolate->factory()->NewStringFromAscii(
+ CStrVector("minimumSignificantDigits"));
+ if (resolved->HasLocalProperty(*key)) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("minimumSignificantDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMinimumSignificantDigits()),
+ NONE,
+ kNonStrictMode);
+ }
+
+ key = isolate->factory()->NewStringFromAscii(
+ CStrVector("maximumSignificantDigits"));
+ if (resolved->HasLocalProperty(*key)) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("maximumSignificantDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMaximumSignificantDigits()),
+ NONE,
+ kNonStrictMode);
+ }
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("locale")),
+ isolate->factory()->NewStringFromAscii(CStrVector(result)),
+ NONE,
+ kNonStrictMode);
+ } else {
+ // This would never happen, since we got the locale from ICU.
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("locale")),
+ isolate->factory()->NewStringFromAscii(CStrVector("und")),
+ NONE,
+ kNonStrictMode);
+ }
+}
+
+
+icu::Collator* CreateICUCollator(
+ Isolate* isolate,
+ const icu::Locale& icu_locale,
+ Handle<JSObject> options) {
+ // Make collator from options.
+ icu::Collator* collator = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ collator = icu::Collator::createInstance(icu_locale, status);
+
+ if (U_FAILURE(status)) {
+ delete collator;
+ return NULL;
+ }
+
+ // Set flags first, and then override them with sensitivity if necessary.
+ bool numeric;
+ if (ExtractBooleanSetting(isolate, options, "numeric", &numeric)) {
+ collator->setAttribute(
+ UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF, status);
+ }
+
+ // Normalization is always on, by the spec. We are free to optimize
+ // if the strings are already normalized (but we don't have a way to tell
+ // that right now).
+ collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
+
+ icu::UnicodeString case_first;
+ if (ExtractStringSetting(isolate, options, "caseFirst", &case_first)) {
+ if (case_first == UNICODE_STRING_SIMPLE("upper")) {
+ collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status);
+ } else if (case_first == UNICODE_STRING_SIMPLE("lower")) {
+ collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status);
+ } else {
+ // Default (false/off).
+ collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
+ }
+ }
+
+ icu::UnicodeString sensitivity;
+ if (ExtractStringSetting(isolate, options, "sensitivity", &sensitivity)) {
+ if (sensitivity == UNICODE_STRING_SIMPLE("base")) {
+ collator->setStrength(icu::Collator::PRIMARY);
+ } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) {
+ collator->setStrength(icu::Collator::SECONDARY);
+ } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) {
+ collator->setStrength(icu::Collator::PRIMARY);
+ collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
+ } else {
+ // variant (default)
+ collator->setStrength(icu::Collator::TERTIARY);
+ }
+ }
+
+ bool ignore;
+ if (ExtractBooleanSetting(isolate, options, "ignorePunctuation", &ignore)) {
+ if (ignore) {
+ collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
+ }
+ }
+
+ return collator;
+}
+
+
+void SetResolvedCollatorSettings(Isolate* isolate,
+ const icu::Locale& icu_locale,
+ icu::Collator* collator,
+ Handle<JSObject> resolved) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("numeric")),
+ isolate->factory()->ToBoolean(
+ collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON),
+ NONE,
+ kNonStrictMode);
+
+ switch (collator->getAttribute(UCOL_CASE_FIRST, status)) {
+ case UCOL_LOWER_FIRST:
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")),
+ isolate->factory()->NewStringFromAscii(CStrVector("lower")),
+ NONE,
+ kNonStrictMode);
+ break;
+ case UCOL_UPPER_FIRST:
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")),
+ isolate->factory()->NewStringFromAscii(CStrVector("upper")),
+ NONE,
+ kNonStrictMode);
+ break;
+ default:
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")),
+ isolate->factory()->NewStringFromAscii(CStrVector("false")),
+ NONE,
+ kNonStrictMode);
+ }
+
+ switch (collator->getAttribute(UCOL_STRENGTH, status)) {
+ case UCOL_PRIMARY: {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("strength")),
+ isolate->factory()->NewStringFromAscii(CStrVector("primary")),
+ NONE,
+ kNonStrictMode);
+
+ // case level: true + s1 -> case, s1 -> base.
+ if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")),
+ isolate->factory()->NewStringFromAscii(CStrVector("case")),
+ NONE,
+ kNonStrictMode);
+ } else {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")),
+ isolate->factory()->NewStringFromAscii(CStrVector("base")),
+ NONE,
+ kNonStrictMode);
+ }
+ break;
+ }
+ case UCOL_SECONDARY:
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("strength")),
+ isolate->factory()->NewStringFromAscii(CStrVector("secondary")),
+ NONE,
+ kNonStrictMode);
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")),
+ isolate->factory()->NewStringFromAscii(CStrVector("accent")),
+ NONE,
+ kNonStrictMode);
+ break;
+ case UCOL_TERTIARY:
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("strength")),
+ isolate->factory()->NewStringFromAscii(CStrVector("tertiary")),
+ NONE,
+ kNonStrictMode);
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")),
+ isolate->factory()->NewStringFromAscii(CStrVector("variant")),
+ NONE,
+ kNonStrictMode);
+ break;
+ case UCOL_QUATERNARY:
+ // We shouldn't get quaternary and identical from ICU, but if we do
+ // put them into variant.
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("strength")),
+ isolate->factory()->NewStringFromAscii(CStrVector("quaternary")),
+ NONE,
+ kNonStrictMode);
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")),
+ isolate->factory()->NewStringFromAscii(CStrVector("variant")),
+ NONE,
+ kNonStrictMode);
+ break;
+ default:
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("strength")),
+ isolate->factory()->NewStringFromAscii(CStrVector("identical")),
+ NONE,
+ kNonStrictMode);
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")),
+ isolate->factory()->NewStringFromAscii(CStrVector("variant")),
+ NONE,
+ kNonStrictMode);
+ }
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("ignorePunctuation")),
+ isolate->factory()->ToBoolean(collator->getAttribute(
+ UCOL_ALTERNATE_HANDLING, status) == UCOL_SHIFTED),
+ NONE,
+ kNonStrictMode);
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("locale")),
+ isolate->factory()->NewStringFromAscii(CStrVector(result)),
+ NONE,
+ kNonStrictMode);
+ } else {
+ // This would never happen, since we got the locale from ICU.
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("locale")),
+ isolate->factory()->NewStringFromAscii(CStrVector("und")),
+ NONE,
+ kNonStrictMode);
+ }
+}
+
+} // namespace
+
+
+// static
+Handle<ObjectTemplateInfo> I18N::GetTemplate(Isolate* isolate) {
+ return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate);
+}
+
+
+// static
+Handle<ObjectTemplateInfo> I18N::GetTemplate2(Isolate* isolate) {
+ return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate);
+}
+
+
+// static
+icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat(
+ Isolate* isolate,
+ Handle<String> locale,
+ Handle<JSObject> options,
+ Handle<JSObject> resolved) {
+ // Convert BCP47 into ICU locale format.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale icu_locale;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+ v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
+ if (bcp47_locale.length() != 0) {
+ uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &status);
+ if (U_FAILURE(status) || icu_length == 0) {
+ return NULL;
+ }
+ icu_locale = icu::Locale(icu_result);
+ }
+
+ icu::SimpleDateFormat* date_format = CreateICUDateFormat(
+ isolate, icu_locale, options);
+ if (!date_format) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
+
+ // Set resolved settings (pattern, numbering system, calendar).
+ SetResolvedDateSettings(
+ isolate, no_extension_locale, date_format, resolved);
+ } else {
+ SetResolvedDateSettings(isolate, icu_locale, date_format, resolved);
+ }
+
+ return date_format;
+}
+
+
+icu::SimpleDateFormat* DateFormat::UnpackDateFormat(
+ Isolate* isolate,
+ Handle<JSObject> obj) {
+ Handle<String> key =
+ isolate->factory()->NewStringFromAscii(CStrVector("dateFormat"));
+ if (obj->HasLocalProperty(*key)) {
+ return reinterpret_cast<icu::SimpleDateFormat*>(
+ obj->GetInternalField(0));
+ }
+
+ return NULL;
+}
+
+
+void DateFormat::DeleteDateFormat(v8::Isolate* isolate,
+ Persistent<v8::Object>* object,
+ void* param) {
+ // First delete the hidden C++ object.
+ delete reinterpret_cast<icu::SimpleDateFormat*>(Handle<JSObject>::cast(
+ v8::Utils::OpenPersistent(object))->GetInternalField(0));
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
+
+icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
+ Isolate* isolate,
+ Handle<String> locale,
+ Handle<JSObject> options,
+ Handle<JSObject> resolved) {
+ // Convert BCP47 into ICU locale format.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale icu_locale;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+ v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
+ if (bcp47_locale.length() != 0) {
+ uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &status);
+ if (U_FAILURE(status) || icu_length == 0) {
+ return NULL;
+ }
+ icu_locale = icu::Locale(icu_result);
+ }
+
+ icu::DecimalFormat* number_format =
+ CreateICUNumberFormat(isolate, icu_locale, options);
+ if (!number_format) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ number_format = CreateICUNumberFormat(
+ isolate, no_extension_locale, options);
+
+ // Set resolved settings (pattern, numbering system).
+ SetResolvedNumberSettings(
+ isolate, no_extension_locale, number_format, resolved);
+ } else {
+ SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved);
+ }
+
+ return number_format;
+}
+
+
+icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
+ Isolate* isolate,
+ Handle<JSObject> obj) {
+ Handle<String> key =
+ isolate->factory()->NewStringFromAscii(CStrVector("numberFormat"));
+ if (obj->HasLocalProperty(*key)) {
+ return reinterpret_cast<icu::DecimalFormat*>(obj->GetInternalField(0));
+ }
+
+ return NULL;
+}
+
+
+void NumberFormat::DeleteNumberFormat(v8::Isolate* isolate,
+ Persistent<v8::Object>* object,
+ void* param) {
+ // First delete the hidden C++ object.
+ delete reinterpret_cast<icu::DecimalFormat*>(Handle<JSObject>::cast(
+ v8::Utils::OpenPersistent(object))->GetInternalField(0));
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
+
+icu::Collator* Collator::InitializeCollator(
+ Isolate* isolate,
+ Handle<String> locale,
+ Handle<JSObject> options,
+ Handle<JSObject> resolved) {
+ // Convert BCP47 into ICU locale format.
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale icu_locale;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+ v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
+ if (bcp47_locale.length() != 0) {
+ uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &status);
+ if (U_FAILURE(status) || icu_length == 0) {
+ return NULL;
+ }
+ icu_locale = icu::Locale(icu_result);
+ }
+
+ icu::Collator* collator = CreateICUCollator(isolate, icu_locale, options);
+ if (!collator) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ collator = CreateICUCollator(isolate, no_extension_locale, options);
+
+ // Set resolved settings (pattern, numbering system).
+ SetResolvedCollatorSettings(
+ isolate, no_extension_locale, collator, resolved);
+ } else {
+ SetResolvedCollatorSettings(isolate, icu_locale, collator, resolved);
+ }
+
+ return collator;
+}
+
+
+icu::Collator* Collator::UnpackCollator(Isolate* isolate,
+ Handle<JSObject> obj) {
+ Handle<String> key =
+ isolate->factory()->NewStringFromAscii(CStrVector("collator"));
+ if (obj->HasLocalProperty(*key)) {
+ return reinterpret_cast<icu::Collator*>(obj->GetInternalField(0));
+ }
+
+ return NULL;
+}
+
+
+void Collator::DeleteCollator(v8::Isolate* isolate,
+ Persistent<v8::Object>* object,
+ void* param) {
+ // First delete the hidden C++ object.
+ delete reinterpret_cast<icu::Collator*>(Handle<JSObject>::cast(
+ v8::Utils::OpenPersistent(object))->GetInternalField(0));
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/i18n.h b/chromium/v8/src/i18n.h
new file mode 100644
index 00000000000..5825ab6c6c7
--- /dev/null
+++ b/chromium/v8/src/i18n.h
@@ -0,0 +1,129 @@
+// Copyright 2013 the V8 project authors. 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.
+// limitations under the License.
+
+#ifndef V8_I18N_H_
+#define V8_I18N_H_
+
+#include "unicode/uversion.h"
+#include "v8.h"
+
+namespace U_ICU_NAMESPACE {
+class Collator;
+class DecimalFormat;
+class SimpleDateFormat;
+}
+
+namespace v8 {
+namespace internal {
+
+class I18N {
+ public:
+ // Creates an ObjectTemplate with one internal field.
+ static Handle<ObjectTemplateInfo> GetTemplate(Isolate* isolate);
+
+ // Creates an ObjectTemplate with two internal fields.
+ static Handle<ObjectTemplateInfo> GetTemplate2(Isolate* isolate);
+
+ private:
+ I18N();
+};
+
+
+class DateFormat {
+ public:
+ // Create a formatter for the specificied locale and options. Returns the
+ // resolved settings for the locale / options.
+ static icu::SimpleDateFormat* InitializeDateTimeFormat(
+ Isolate* isolate,
+ Handle<String> locale,
+ Handle<JSObject> options,
+ Handle<JSObject> resolved);
+
+ // Unpacks date format object from corresponding JavaScript object.
+ static icu::SimpleDateFormat* UnpackDateFormat(Isolate* isolate,
+ Handle<JSObject> obj);
+
+ // Release memory we allocated for the DateFormat once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteDateFormat(v8::Isolate* isolate,
+ Persistent<v8::Object>* object,
+ void* param);
+ private:
+ DateFormat();
+};
+
+
+class NumberFormat {
+ public:
+ // Create a formatter for the specificied locale and options. Returns the
+ // resolved settings for the locale / options.
+ static icu::DecimalFormat* InitializeNumberFormat(
+ Isolate* isolate,
+ Handle<String> locale,
+ Handle<JSObject> options,
+ Handle<JSObject> resolved);
+
+ // Unpacks number format object from corresponding JavaScript object.
+ static icu::DecimalFormat* UnpackNumberFormat(Isolate* isolate,
+ Handle<JSObject> obj);
+
+ // Release memory we allocated for the NumberFormat once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteNumberFormat(v8::Isolate* isolate,
+ Persistent<v8::Object>* object,
+ void* param);
+ private:
+ NumberFormat();
+};
+
+
+class Collator {
+ public:
+ // Create a collator for the specificied locale and options. Returns the
+ // resolved settings for the locale / options.
+ static icu::Collator* InitializeCollator(
+ Isolate* isolate,
+ Handle<String> locale,
+ Handle<JSObject> options,
+ Handle<JSObject> resolved);
+
+ // Unpacks collator object from corresponding JavaScript object.
+ static icu::Collator* UnpackCollator(Isolate* isolate, Handle<JSObject> obj);
+
+ // Release memory we allocated for the Collator once the JS object that holds
+ // the pointer gets garbage collected.
+ static void DeleteCollator(v8::Isolate* isolate,
+ Persistent<v8::Object>* object,
+ void* param);
+ private:
+ Collator();
+};
+
+} } // namespace v8::internal
+
+#endif // V8_I18N_H_
diff --git a/chromium/v8/src/ia32/assembler-ia32-inl.h b/chromium/v8/src/ia32/assembler-ia32-inl.h
new file mode 100644
index 00000000000..b6ef242a2c4
--- /dev/null
+++ b/chromium/v8/src/ia32/assembler-ia32-inl.h
@@ -0,0 +1,528 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// 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"
+
+namespace v8 {
+namespace internal {
+
+
+static const byte kCallOpcode = 0xE8;
+
+
+// The modes possibly affected by apply must be in kApplyMask.
+void RelocInfo::apply(intptr_t delta) {
+ if (IsRuntimeEntry(rmode_) || IsCodeTarget(rmode_)) {
+ int32_t* p = reinterpret_cast<int32_t*>(pc_);
+ *p -= delta; // Relocate entry.
+ CPU::FlushICache(p, sizeof(uint32_t));
+ } else if (rmode_ == CODE_AGE_SEQUENCE) {
+ if (*pc_ == kCallOpcode) {
+ int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
+ *p -= delta; // Relocate entry.
+ CPU::FlushICache(p, sizeof(uint32_t));
+ }
+ } else if (rmode_ == JS_RETURN && IsPatchedReturnSequence()) {
+ // Special handling of js_return when a break point is set (call
+ // instruction has been inserted).
+ int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
+ *p -= delta; // Relocate entry.
+ CPU::FlushICache(p, sizeof(uint32_t));
+ } else if (rmode_ == DEBUG_BREAK_SLOT && IsPatchedDebugBreakSlotSequence()) {
+ // Special handling of a debug break slot when a break point is set (call
+ // instruction has been inserted).
+ int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
+ *p -= delta; // Relocate entry.
+ CPU::FlushICache(p, sizeof(uint32_t));
+ } else if (IsInternalReference(rmode_)) {
+ // absolute code pointer inside code object moves with the code object.
+ int32_t* p = reinterpret_cast<int32_t*>(pc_);
+ *p += delta; // Relocate entry.
+ CPU::FlushICache(p, sizeof(uint32_t));
+ }
+}
+
+
+Address RelocInfo::target_address() {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
+ return Assembler::target_address_at(pc_);
+}
+
+
+Address RelocInfo::target_address_address() {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
+ || rmode_ == EMBEDDED_OBJECT
+ || rmode_ == EXTERNAL_REFERENCE);
+ return reinterpret_cast<Address>(pc_);
+}
+
+
+int RelocInfo::target_address_size() {
+ return Assembler::kSpecialTargetSize;
+}
+
+
+void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) {
+ Assembler::set_target_address_at(pc_, target);
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
+ 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));
+ }
+}
+
+
+Object* RelocInfo::target_object() {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ return Memory::Object_at(pc_);
+}
+
+
+Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ return Memory::Object_Handle_at(pc_);
+}
+
+
+Object** RelocInfo::target_object_address() {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ return &Memory::Object_at(pc_);
+}
+
+
+void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ ASSERT(!target->IsConsString());
+ 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));
+ }
+}
+
+
+Address* RelocInfo::target_reference_address() {
+ ASSERT(rmode_ == RelocInfo::EXTERNAL_REFERENCE);
+ return reinterpret_cast<Address*>(pc_);
+}
+
+
+Address RelocInfo::target_runtime_entry(Assembler* origin) {
+ ASSERT(IsRuntimeEntry(rmode_));
+ return reinterpret_cast<Address>(*reinterpret_cast<int32_t*>(pc_));
+}
+
+
+void RelocInfo::set_target_runtime_entry(Address target,
+ WriteBarrierMode mode) {
+ ASSERT(IsRuntimeEntry(rmode_));
+ if (target_address() != target) set_target_address(target, mode);
+}
+
+
+Handle<Cell> RelocInfo::target_cell_handle() {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ Address address = Memory::Address_at(pc_);
+ return Handle<Cell>(reinterpret_cast<Cell**>(address));
+}
+
+
+Cell* RelocInfo::target_cell() {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ return Cell::FromValueAddress(Memory::Address_at(pc_));
+}
+
+
+void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ Address address = cell->address() + Cell::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);
+ }
+}
+
+
+Code* RelocInfo::code_age_stub() {
+ ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
+ ASSERT(*pc_ == kCallOpcode);
+ return Code::GetCodeFromTargetAddress(
+ Assembler::target_address_at(pc_ + 1));
+}
+
+
+void RelocInfo::set_code_age_stub(Code* stub) {
+ ASSERT(*pc_ == kCallOpcode);
+ ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
+ Assembler::set_target_address_at(pc_ + 1, stub->instruction_start());
+}
+
+
+Address RelocInfo::call_address() {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ return Assembler::target_address_at(pc_ + 1);
+}
+
+
+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));
+ }
+}
+
+
+Object* RelocInfo::call_object() {
+ return *call_object_address();
+}
+
+
+void RelocInfo::set_call_object(Object* target) {
+ *call_object_address() = target;
+}
+
+
+Object** RelocInfo::call_object_address() {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ return reinterpret_cast<Object**>(pc_ + 1);
+}
+
+
+bool RelocInfo::IsPatchedReturnSequence() {
+ return *pc_ == kCallOpcode;
+}
+
+
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+ return !Assembler::IsNop(pc());
+}
+
+
+void RelocInfo::Visit(ObjectVisitor* visitor) {
+ RelocInfo::Mode mode = rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT) {
+ visitor->VisitEmbeddedPointer(this);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (RelocInfo::IsCodeTarget(mode)) {
+ visitor->VisitCodeTarget(this);
+ } else if (mode == RelocInfo::CELL) {
+ visitor->VisitCell(this);
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
+ visitor->VisitExternalReference(this);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (RelocInfo::IsCodeAgeSequence(mode)) {
+ visitor->VisitCodeAgeSequence(this);
+ #ifdef ENABLE_DEBUGGER_SUPPORT
+ // TODO(isolates): Get a cached isolate below.
+ } else if (((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence())) &&
+ Isolate::Current()->debug()->has_break_points()) {
+ visitor->VisitDebugTarget(this);
+#endif
+ } else if (IsRuntimeEntry(mode)) {
+ visitor->VisitRuntimeEntry(this);
+ }
+}
+
+
+template<typename StaticVisitor>
+void RelocInfo::Visit(Heap* heap) {
+ RelocInfo::Mode mode = rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT) {
+ StaticVisitor::VisitEmbeddedPointer(heap, this);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (RelocInfo::IsCodeTarget(mode)) {
+ StaticVisitor::VisitCodeTarget(heap, this);
+ } else if (mode == RelocInfo::CELL) {
+ StaticVisitor::VisitCell(heap, this);
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
+ StaticVisitor::VisitExternalReference(this);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (RelocInfo::IsCodeAgeSequence(mode)) {
+ StaticVisitor::VisitCodeAgeSequence(heap, this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ } else if (heap->isolate()->debug()->has_break_points() &&
+ ((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()))) {
+ StaticVisitor::VisitDebugTarget(heap, this);
+#endif
+ } else if (IsRuntimeEntry(mode)) {
+ StaticVisitor::VisitRuntimeEntry(this);
+ }
+}
+
+
+
+Immediate::Immediate(int x) {
+ x_ = x;
+ rmode_ = RelocInfo::NONE32;
+}
+
+
+Immediate::Immediate(const ExternalReference& ext) {
+ x_ = reinterpret_cast<int32_t>(ext.address());
+ rmode_ = RelocInfo::EXTERNAL_REFERENCE;
+}
+
+
+Immediate::Immediate(Label* internal_offset) {
+ x_ = reinterpret_cast<int32_t>(internal_offset);
+ rmode_ = RelocInfo::INTERNAL_REFERENCE;
+}
+
+
+Immediate::Immediate(Handle<Object> handle) {
+#ifdef DEBUG
+ Isolate* isolate = Isolate::Current();
+#endif
+ AllowDeferredHandleDereference using_raw_address;
+ // Verify all Objects referred by code are NOT in new space.
+ Object* obj = *handle;
+ ASSERT(!isolate->heap()->InNewSpace(obj));
+ if (obj->IsHeapObject()) {
+ x_ = reinterpret_cast<intptr_t>(handle.location());
+ rmode_ = RelocInfo::EMBEDDED_OBJECT;
+ } else {
+ // no relocation needed
+ x_ = reinterpret_cast<intptr_t>(obj);
+ rmode_ = RelocInfo::NONE32;
+ }
+}
+
+
+Immediate::Immediate(Smi* value) {
+ x_ = reinterpret_cast<intptr_t>(value);
+ rmode_ = RelocInfo::NONE32;
+}
+
+
+Immediate::Immediate(Address addr) {
+ x_ = reinterpret_cast<int32_t>(addr);
+ rmode_ = RelocInfo::NONE32;
+}
+
+
+void Assembler::emit(uint32_t x) {
+ *reinterpret_cast<uint32_t*>(pc_) = x;
+ pc_ += sizeof(uint32_t);
+}
+
+
+void Assembler::emit(Handle<Object> handle) {
+ AllowDeferredHandleDereference heap_object_check;
+ // Verify all Objects referred by code are NOT in new space.
+ Object* obj = *handle;
+ ASSERT(!isolate()->heap()->InNewSpace(obj));
+ if (obj->IsHeapObject()) {
+ emit(reinterpret_cast<intptr_t>(handle.location()),
+ RelocInfo::EMBEDDED_OBJECT);
+ } else {
+ // no relocation needed
+ emit(reinterpret_cast<intptr_t>(obj));
+ }
+}
+
+
+void Assembler::emit(uint32_t x, RelocInfo::Mode rmode, TypeFeedbackId id) {
+ if (rmode == RelocInfo::CODE_TARGET && !id.IsNone()) {
+ RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, id.ToInt());
+ } else if (!RelocInfo::IsNone(rmode)) {
+ RecordRelocInfo(rmode);
+ }
+ emit(x);
+}
+
+
+void Assembler::emit(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId id) {
+ AllowDeferredHandleDereference embedding_raw_address;
+ emit(reinterpret_cast<intptr_t>(code.location()), rmode, id);
+}
+
+
+void Assembler::emit(const Immediate& x) {
+ if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) {
+ Label* label = reinterpret_cast<Label*>(x.x_);
+ emit_code_relative_offset(label);
+ return;
+ }
+ if (!RelocInfo::IsNone(x.rmode_)) RecordRelocInfo(x.rmode_);
+ emit(x.x_);
+}
+
+
+void Assembler::emit_code_relative_offset(Label* label) {
+ if (label->is_bound()) {
+ int32_t pos;
+ pos = label->pos() + Code::kHeaderSize - kHeapObjectTag;
+ emit(pos);
+ } else {
+ emit_disp(label, Displacement::CODE_RELATIVE);
+ }
+}
+
+
+void Assembler::emit_w(const Immediate& x) {
+ ASSERT(RelocInfo::IsNone(x.rmode_));
+ uint16_t value = static_cast<uint16_t>(x.x_);
+ reinterpret_cast<uint16_t*>(pc_)[0] = value;
+ pc_ += sizeof(uint16_t);
+}
+
+
+Address Assembler::target_address_at(Address pc) {
+ return pc + sizeof(int32_t) + *reinterpret_cast<int32_t*>(pc);
+}
+
+
+void Assembler::set_target_address_at(Address pc, Address target) {
+ int32_t* p = reinterpret_cast<int32_t*>(pc);
+ *p = target - (pc + sizeof(int32_t));
+ CPU::FlushICache(p, sizeof(int32_t));
+}
+
+
+Address Assembler::target_address_from_return_address(Address pc) {
+ return pc - kCallTargetAddressOffset;
+}
+
+
+Displacement Assembler::disp_at(Label* L) {
+ return Displacement(long_at(L->pos()));
+}
+
+
+void Assembler::disp_at_put(Label* L, Displacement disp) {
+ long_at_put(L->pos(), disp.data());
+}
+
+
+void Assembler::emit_disp(Label* L, Displacement::Type type) {
+ Displacement disp(L, type);
+ L->link_to(pc_offset());
+ emit(static_cast<int>(disp.data()));
+}
+
+
+void Assembler::emit_near_disp(Label* L) {
+ byte disp = 0x00;
+ if (L->is_near_linked()) {
+ int offset = L->near_link_pos() - pc_offset();
+ ASSERT(is_int8(offset));
+ disp = static_cast<byte>(offset & 0xFF);
+ }
+ L->link_to(pc_offset(), Label::kNear);
+ *pc_++ = disp;
+}
+
+
+void Operand::set_modrm(int mod, Register rm) {
+ ASSERT((mod & -4) == 0);
+ buf_[0] = mod << 6 | rm.code();
+ len_ = 1;
+}
+
+
+void Operand::set_sib(ScaleFactor scale, Register index, Register base) {
+ ASSERT(len_ == 1);
+ ASSERT((scale & -4) == 0);
+ // Use SIB with no index register only for base esp.
+ ASSERT(!index.is(esp) || base.is(esp));
+ buf_[1] = scale << 6 | index.code() << 3 | base.code();
+ len_ = 2;
+}
+
+
+void Operand::set_disp8(int8_t disp) {
+ ASSERT(len_ == 1 || len_ == 2);
+ *reinterpret_cast<int8_t*>(&buf_[len_++]) = disp;
+}
+
+
+void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) {
+ ASSERT(len_ == 1 || len_ == 2);
+ int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]);
+ *p = disp;
+ len_ += sizeof(int32_t);
+ rmode_ = rmode;
+}
+
+Operand::Operand(Register reg) {
+ // reg
+ set_modrm(3, reg);
+}
+
+
+Operand::Operand(XMMRegister xmm_reg) {
+ Register reg = { xmm_reg.code() };
+ set_modrm(3, reg);
+}
+
+
+Operand::Operand(int32_t disp, RelocInfo::Mode rmode) {
+ // [disp/r]
+ set_modrm(0, ebp);
+ set_dispr(disp, rmode);
+}
+
+} } // namespace v8::internal
+
+#endif // V8_IA32_ASSEMBLER_IA32_INL_H_
diff --git a/chromium/v8/src/ia32/assembler-ia32.cc b/chromium/v8/src/ia32/assembler-ia32.cc
new file mode 100644
index 00000000000..7bea3730258
--- /dev/null
+++ b/chromium/v8/src/ia32/assembler-ia32.cc
@@ -0,0 +1,2717 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been modified
+// significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "disassembler.h"
+#include "macro-assembler.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+// -----------------------------------------------------------------------------
+// Implementation of CpuFeatures
+
+#ifdef DEBUG
+bool CpuFeatures::initialized_ = false;
+#endif
+uint64_t CpuFeatures::supported_ = 0;
+uint64_t CpuFeatures::found_by_runtime_probing_only_ = 0;
+
+
+ExternalReference ExternalReference::cpu_features() {
+ ASSERT(CpuFeatures::initialized_);
+ return ExternalReference(&CpuFeatures::supported_);
+}
+
+
+int IntelDoubleRegister::NumAllocatableRegisters() {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ return XMMRegister::kNumAllocatableRegisters;
+ } else {
+ return X87Register::kNumAllocatableRegisters;
+ }
+}
+
+
+int IntelDoubleRegister::NumRegisters() {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ return XMMRegister::kNumRegisters;
+ } else {
+ return X87Register::kNumRegisters;
+ }
+}
+
+
+const char* IntelDoubleRegister::AllocationIndexToString(int index) {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ return XMMRegister::AllocationIndexToString(index);
+ } else {
+ return X87Register::AllocationIndexToString(index);
+ }
+}
+
+
+// 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);
+#ifdef DEBUG
+ initialized_ = true;
+#endif
+ if (Serializer::enabled()) {
+ supported_ |= OS::CpuFeaturesImpliedByPlatform();
+ return; // No features if we might serialize.
+ }
+
+ const int kBufferSize = 4 * KB;
+ VirtualMemory* memory = new VirtualMemory(kBufferSize);
+ if (!memory->IsReserved()) {
+ delete memory;
+ return;
+ }
+ ASSERT(memory->size() >= static_cast<size_t>(kBufferSize));
+ if (!memory->Commit(memory->address(), kBufferSize, true/*executable*/)) {
+ delete memory;
+ return;
+ }
+
+ Assembler assm(NULL, memory->address(), kBufferSize);
+ Label cpuid, done;
+#define __ assm.
+ // Save old esp, since we are going to modify the stack.
+ __ push(ebp);
+ __ pushfd();
+ __ push(ecx);
+ __ push(ebx);
+ __ mov(ebp, esp);
+
+ // If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
+ __ pushfd();
+ __ pop(eax);
+ __ mov(edx, eax);
+ __ xor_(eax, 0x200000); // Flip bit 21.
+ __ push(eax);
+ __ popfd();
+ __ pushfd();
+ __ pop(eax);
+ __ xor_(eax, edx); // Different if CPUID is supported.
+ __ j(not_zero, &cpuid);
+
+ // CPUID not supported. Clear the supported features in edx:eax.
+ __ xor_(eax, eax);
+ __ xor_(edx, edx);
+ __ jmp(&done);
+
+ // Invoke CPUID with 1 in eax to get feature information in
+ // ecx:edx. Temporarily enable CPUID support because we know it's
+ // safe here.
+ __ bind(&cpuid);
+ __ mov(eax, 1);
+ supported_ = (1 << CPUID);
+ { CpuFeatureScope fscope(&assm, CPUID);
+ __ cpuid();
+ }
+ supported_ = 0;
+
+ // Move the result from ecx:edx to edx:eax and make sure to mark the
+ // CPUID feature as supported.
+ __ mov(eax, edx);
+ __ or_(eax, 1 << CPUID);
+ __ mov(edx, ecx);
+
+ // Done.
+ __ bind(&done);
+ __ mov(esp, ebp);
+ __ pop(ebx);
+ __ pop(ecx);
+ __ popfd();
+ __ pop(ebp);
+ __ ret(0);
+#undef __
+
+ typedef uint64_t (*F0)();
+ F0 probe = FUNCTION_CAST<F0>(reinterpret_cast<Address>(memory->address()));
+ uint64_t probed_features = probe();
+ uint64_t platform_features = OS::CpuFeaturesImpliedByPlatform();
+ supported_ = probed_features | platform_features;
+ found_by_runtime_probing_only_ = probed_features & ~platform_features;
+
+ delete memory;
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Displacement
+
+void Displacement::init(Label* L, Type type) {
+ ASSERT(!L->is_bound());
+ int next = 0;
+ if (L->is_linked()) {
+ next = L->pos();
+ ASSERT(next > 0); // Displacements must be at positions > 0
+ }
+ // Ensure that we _never_ overflow the next field.
+ ASSERT(NextField::is_valid(Assembler::kMaximalBufferSize));
+ data_ = NextField::encode(next) | TypeField::encode(type);
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of RelocInfo
+
+
+const int RelocInfo::kApplyMask =
+ RelocInfo::kCodeTargetMask | 1 << RelocInfo::RUNTIME_ENTRY |
+ 1 << RelocInfo::JS_RETURN | 1 << RelocInfo::INTERNAL_REFERENCE |
+ 1 << RelocInfo::DEBUG_BREAK_SLOT | 1 << RelocInfo::CODE_AGE_SEQUENCE;
+
+
+bool RelocInfo::IsCodedSpecially() {
+ // The deserializer needs to know whether a pointer is specially coded. Being
+ // specially coded on IA32 means that it is a relative address, as used by
+ // branch instructions. These are also the ones that need changing when a
+ // code object moves.
+ return (1 << rmode_) & kApplyMask;
+}
+
+
+void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
+ // Patch the code at the current address with the supplied instructions.
+ for (int i = 0; i < instruction_count; i++) {
+ *(pc_ + i) = *(instructions + i);
+ }
+
+ // Indicate that code has changed.
+ CPU::FlushICache(pc_, instruction_count);
+}
+
+
+// Patch the code at the current PC with a call to the target address.
+// Additional guard int3 instructions can be added if required.
+void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
+ // Call instruction takes up 5 bytes and int3 takes up one byte.
+ static const int kCallCodeSize = 5;
+ int code_size = kCallCodeSize + guard_bytes;
+
+ // Create a code patcher.
+ CodePatcher patcher(pc_, code_size);
+
+ // Add a label for checking the size of the code used for returning.
+#ifdef DEBUG
+ Label check_codesize;
+ patcher.masm()->bind(&check_codesize);
+#endif
+
+ // Patch the code.
+ patcher.masm()->call(target, RelocInfo::NONE32);
+
+ // Check that the size of the code generated is as expected.
+ ASSERT_EQ(kCallCodeSize,
+ patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
+
+ // Add the requested number of int3 instructions after the call.
+ ASSERT_GE(guard_bytes, 0);
+ for (int i = 0; i < guard_bytes; i++) {
+ patcher.masm()->int3();
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Operand
+
+Operand::Operand(Register base, int32_t disp, RelocInfo::Mode rmode) {
+ // [base + disp/r]
+ if (disp == 0 && RelocInfo::IsNone(rmode) && !base.is(ebp)) {
+ // [base]
+ set_modrm(0, base);
+ if (base.is(esp)) set_sib(times_1, esp, base);
+ } else if (is_int8(disp) && RelocInfo::IsNone(rmode)) {
+ // [base + disp8]
+ set_modrm(1, base);
+ if (base.is(esp)) set_sib(times_1, esp, base);
+ set_disp8(disp);
+ } else {
+ // [base + disp/r]
+ set_modrm(2, base);
+ if (base.is(esp)) set_sib(times_1, esp, base);
+ set_dispr(disp, rmode);
+ }
+}
+
+
+Operand::Operand(Register base,
+ Register index,
+ ScaleFactor scale,
+ int32_t disp,
+ RelocInfo::Mode rmode) {
+ ASSERT(!index.is(esp)); // illegal addressing mode
+ // [base + index*scale + disp/r]
+ if (disp == 0 && RelocInfo::IsNone(rmode) && !base.is(ebp)) {
+ // [base + index*scale]
+ set_modrm(0, esp);
+ set_sib(scale, index, base);
+ } else if (is_int8(disp) && RelocInfo::IsNone(rmode)) {
+ // [base + index*scale + disp8]
+ set_modrm(1, esp);
+ set_sib(scale, index, base);
+ set_disp8(disp);
+ } else {
+ // [base + index*scale + disp/r]
+ set_modrm(2, esp);
+ set_sib(scale, index, base);
+ set_dispr(disp, rmode);
+ }
+}
+
+
+Operand::Operand(Register index,
+ ScaleFactor scale,
+ int32_t disp,
+ RelocInfo::Mode rmode) {
+ ASSERT(!index.is(esp)); // illegal addressing mode
+ // [index*scale + disp/r]
+ set_modrm(0, esp);
+ set_sib(scale, index, ebp);
+ set_dispr(disp, rmode);
+}
+
+
+bool Operand::is_reg(Register reg) const {
+ return ((buf_[0] & 0xF8) == 0xC0) // addressing mode is register only.
+ && ((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.
+
+// Emit a single byte. Must always be inlined.
+#define EMIT(x) \
+ *pc_++ = (x)
+
+
+#ifdef GENERATED_CODE_COVERAGE
+static void InitCoverageLog();
+#endif
+
+Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
+ : AssemblerBase(isolate, buffer, buffer_size),
+ positions_recorder_(this) {
+ // Clear the buffer in debug mode unless it was provided by the
+ // caller in which case we can't be sure it's okay to overwrite
+ // existing code in it; see CodePatcher::CodePatcher(...).
+#ifdef DEBUG
+ if (own_buffer_) {
+ memset(buffer_, 0xCC, buffer_size_); // int3
+ }
+#endif
+
+ reloc_info_writer.Reposition(buffer_ + buffer_size_, pc_);
+
+#ifdef GENERATED_CODE_COVERAGE
+ InitCoverageLog();
+#endif
+}
+
+
+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.
+ // Set up code descriptor.
+ desc->buffer = buffer_;
+ desc->buffer_size = buffer_size_;
+ desc->instr_size = pc_offset();
+ desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
+ desc->origin = this;
+}
+
+
+void Assembler::Align(int m) {
+ ASSERT(IsPowerOf2(m));
+ 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;
+ }
+ }
+}
+
+
+void Assembler::CodeTargetAlign() {
+ Align(16); // Preferred alignment of jump targets on ia32.
+}
+
+
+void Assembler::cpuid() {
+ ASSERT(IsEnabled(CPUID));
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xA2);
+}
+
+
+void Assembler::pushad() {
+ EnsureSpace ensure_space(this);
+ EMIT(0x60);
+}
+
+
+void Assembler::popad() {
+ EnsureSpace ensure_space(this);
+ EMIT(0x61);
+}
+
+
+void Assembler::pushfd() {
+ EnsureSpace ensure_space(this);
+ EMIT(0x9C);
+}
+
+
+void Assembler::popfd() {
+ EnsureSpace ensure_space(this);
+ EMIT(0x9D);
+}
+
+
+void Assembler::push(const Immediate& x) {
+ EnsureSpace ensure_space(this);
+ if (x.is_int8()) {
+ EMIT(0x6a);
+ EMIT(x.x_);
+ } else {
+ EMIT(0x68);
+ emit(x);
+ }
+}
+
+
+void Assembler::push_imm32(int32_t imm32) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x68);
+ emit(imm32);
+}
+
+
+void Assembler::push(Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x50 | src.code());
+}
+
+
+void Assembler::push(const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xFF);
+ emit_operand(esi, src);
+}
+
+
+void Assembler::pop(Register dst) {
+ ASSERT(reloc_info_writer.last_pc() != NULL);
+ EnsureSpace ensure_space(this);
+ EMIT(0x58 | dst.code());
+}
+
+
+void Assembler::pop(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x8F);
+ emit_operand(eax, dst);
+}
+
+
+void Assembler::enter(const Immediate& size) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xC8);
+ emit_w(size);
+ EMIT(0);
+}
+
+
+void Assembler::leave() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xC9);
+}
+
+
+void Assembler::mov_b(Register dst, const Operand& src) {
+ CHECK(dst.is_byte_register());
+ EnsureSpace ensure_space(this);
+ EMIT(0x8A);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::mov_b(const Operand& dst, int8_t imm8) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xC6);
+ emit_operand(eax, dst);
+ EMIT(imm8);
+}
+
+
+void Assembler::mov_b(const Operand& dst, Register src) {
+ CHECK(src.is_byte_register());
+ EnsureSpace ensure_space(this);
+ EMIT(0x88);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::mov_w(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x8B);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::mov_w(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x89);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::mov(Register dst, int32_t imm32) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xB8 | dst.code());
+ emit(imm32);
+}
+
+
+void Assembler::mov(Register dst, const Immediate& x) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xB8 | dst.code());
+ emit(x);
+}
+
+
+void Assembler::mov(Register dst, Handle<Object> handle) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xB8 | dst.code());
+ emit(handle);
+}
+
+
+void Assembler::mov(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x8B);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::mov(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x89);
+ EMIT(0xC0 | src.code() << 3 | dst.code());
+}
+
+
+void Assembler::mov(const Operand& dst, const Immediate& x) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xC7);
+ emit_operand(eax, dst);
+ emit(x);
+}
+
+
+void Assembler::mov(const Operand& dst, Handle<Object> handle) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xC7);
+ emit_operand(eax, dst);
+ emit(handle);
+}
+
+
+void Assembler::mov(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x89);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::movsx_b(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xBE);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movsx_w(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xBF);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movzx_b(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xB6);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movzx_w(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xB7);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::cmov(Condition cc, Register dst, const Operand& src) {
+ ASSERT(IsEnabled(CMOV));
+ EnsureSpace ensure_space(this);
+ // Opcode: 0f 40 + cc /r.
+ EMIT(0x0F);
+ EMIT(0x40 + cc);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::cld() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xFC);
+}
+
+
+void Assembler::rep_movs() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3);
+ EMIT(0xA5);
+}
+
+
+void Assembler::rep_stos() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3);
+ EMIT(0xAB);
+}
+
+
+void Assembler::stos() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xAB);
+}
+
+
+void Assembler::xchg(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ if (src.is(eax) || dst.is(eax)) { // Single-byte encoding.
+ EMIT(0x90 | (src.is(eax) ? dst.code() : src.code()));
+ } else {
+ EMIT(0x87);
+ EMIT(0xC0 | src.code() << 3 | dst.code());
+ }
+}
+
+
+void Assembler::adc(Register dst, int32_t imm32) {
+ EnsureSpace ensure_space(this);
+ emit_arith(2, Operand(dst), Immediate(imm32));
+}
+
+
+void Assembler::adc(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x13);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::add(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x03);
+ emit_operand(dst, 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);
+ emit_arith(0, dst, x);
+}
+
+
+void Assembler::and_(Register dst, int32_t imm32) {
+ and_(dst, Immediate(imm32));
+}
+
+
+void Assembler::and_(Register dst, const Immediate& x) {
+ EnsureSpace ensure_space(this);
+ emit_arith(4, Operand(dst), x);
+}
+
+
+void Assembler::and_(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x23);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::and_(const Operand& dst, const Immediate& x) {
+ EnsureSpace ensure_space(this);
+ emit_arith(4, dst, x);
+}
+
+
+void Assembler::and_(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x21);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::cmpb(const Operand& op, int8_t imm8) {
+ EnsureSpace ensure_space(this);
+ if (op.is_reg(eax)) {
+ EMIT(0x3C);
+ } else {
+ EMIT(0x80);
+ emit_operand(edi, op); // edi == 7
+ }
+ EMIT(imm8);
+}
+
+
+void Assembler::cmpb(const Operand& op, Register reg) {
+ CHECK(reg.is_byte_register());
+ EnsureSpace ensure_space(this);
+ EMIT(0x38);
+ emit_operand(reg, op);
+}
+
+
+void Assembler::cmpb(Register reg, const Operand& op) {
+ CHECK(reg.is_byte_register());
+ EnsureSpace ensure_space(this);
+ EMIT(0x3A);
+ emit_operand(reg, op);
+}
+
+
+void Assembler::cmpw(const Operand& op, Immediate imm16) {
+ ASSERT(imm16.is_int16());
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x81);
+ emit_operand(edi, op);
+ emit_w(imm16);
+}
+
+
+void Assembler::cmp(Register reg, int32_t imm32) {
+ EnsureSpace ensure_space(this);
+ emit_arith(7, Operand(reg), Immediate(imm32));
+}
+
+
+void Assembler::cmp(Register reg, Handle<Object> handle) {
+ EnsureSpace ensure_space(this);
+ emit_arith(7, Operand(reg), Immediate(handle));
+}
+
+
+void Assembler::cmp(Register reg, const Operand& op) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x3B);
+ emit_operand(reg, op);
+}
+
+
+void Assembler::cmp(const Operand& op, const Immediate& imm) {
+ EnsureSpace ensure_space(this);
+ emit_arith(7, op, imm);
+}
+
+
+void Assembler::cmp(const Operand& op, Handle<Object> handle) {
+ EnsureSpace ensure_space(this);
+ emit_arith(7, op, Immediate(handle));
+}
+
+
+void Assembler::cmpb_al(const Operand& op) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x38); // CMP r/m8, r8
+ emit_operand(eax, op); // eax has same code as register al.
+}
+
+
+void Assembler::cmpw_ax(const Operand& op) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x39); // CMP r/m16, r16
+ emit_operand(eax, op); // eax has same code as register ax.
+}
+
+
+void Assembler::dec_b(Register dst) {
+ CHECK(dst.is_byte_register());
+ EnsureSpace ensure_space(this);
+ EMIT(0xFE);
+ EMIT(0xC8 | dst.code());
+}
+
+
+void Assembler::dec_b(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xFE);
+ emit_operand(ecx, dst);
+}
+
+
+void Assembler::dec(Register dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x48 | dst.code());
+}
+
+
+void Assembler::dec(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xFF);
+ emit_operand(ecx, dst);
+}
+
+
+void Assembler::cdq() {
+ EnsureSpace ensure_space(this);
+ EMIT(0x99);
+}
+
+
+void Assembler::idiv(Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF7);
+ EMIT(0xF8 | src.code());
+}
+
+
+void Assembler::imul(Register reg) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF7);
+ EMIT(0xE8 | reg.code());
+}
+
+
+void Assembler::imul(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xAF);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::imul(Register dst, Register src, int32_t imm32) {
+ EnsureSpace ensure_space(this);
+ if (is_int8(imm32)) {
+ EMIT(0x6B);
+ EMIT(0xC0 | dst.code() << 3 | src.code());
+ EMIT(imm32);
+ } else {
+ EMIT(0x69);
+ EMIT(0xC0 | dst.code() << 3 | src.code());
+ emit(imm32);
+ }
+}
+
+
+void Assembler::inc(Register dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x40 | dst.code());
+}
+
+
+void Assembler::inc(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xFF);
+ emit_operand(eax, dst);
+}
+
+
+void Assembler::lea(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x8D);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::mul(Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF7);
+ EMIT(0xE0 | src.code());
+}
+
+
+void Assembler::neg(Register dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF7);
+ EMIT(0xD8 | dst.code());
+}
+
+
+void Assembler::not_(Register dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF7);
+ EMIT(0xD0 | dst.code());
+}
+
+
+void Assembler::or_(Register dst, int32_t imm32) {
+ EnsureSpace ensure_space(this);
+ emit_arith(1, Operand(dst), Immediate(imm32));
+}
+
+
+void Assembler::or_(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0B);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::or_(const Operand& dst, const Immediate& x) {
+ EnsureSpace ensure_space(this);
+ emit_arith(1, dst, x);
+}
+
+
+void Assembler::or_(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x09);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::rcl(Register dst, uint8_t imm8) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint5(imm8)); // illegal shift count
+ if (imm8 == 1) {
+ EMIT(0xD1);
+ EMIT(0xD0 | dst.code());
+ } else {
+ EMIT(0xC1);
+ EMIT(0xD0 | dst.code());
+ EMIT(imm8);
+ }
+}
+
+
+void Assembler::rcr(Register dst, uint8_t imm8) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint5(imm8)); // illegal shift count
+ if (imm8 == 1) {
+ EMIT(0xD1);
+ EMIT(0xD8 | dst.code());
+ } else {
+ EMIT(0xC1);
+ EMIT(0xD8 | dst.code());
+ EMIT(imm8);
+ }
+}
+
+
+void Assembler::ror(Register dst, uint8_t imm8) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint5(imm8)); // illegal shift count
+ if (imm8 == 1) {
+ EMIT(0xD1);
+ EMIT(0xC8 | dst.code());
+ } else {
+ EMIT(0xC1);
+ EMIT(0xC8 | dst.code());
+ EMIT(imm8);
+ }
+}
+
+
+void Assembler::ror_cl(Register dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD3);
+ EMIT(0xC8 | dst.code());
+}
+
+
+void Assembler::sar(Register dst, uint8_t imm8) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint5(imm8)); // illegal shift count
+ if (imm8 == 1) {
+ EMIT(0xD1);
+ EMIT(0xF8 | dst.code());
+ } else {
+ EMIT(0xC1);
+ EMIT(0xF8 | dst.code());
+ EMIT(imm8);
+ }
+}
+
+
+void Assembler::sar_cl(Register dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD3);
+ EMIT(0xF8 | dst.code());
+}
+
+
+void Assembler::sbb(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x1B);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::shld(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xA5);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::shl(Register dst, uint8_t imm8) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint5(imm8)); // illegal shift count
+ if (imm8 == 1) {
+ EMIT(0xD1);
+ EMIT(0xE0 | dst.code());
+ } else {
+ EMIT(0xC1);
+ EMIT(0xE0 | dst.code());
+ EMIT(imm8);
+ }
+}
+
+
+void Assembler::shl_cl(Register dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD3);
+ EMIT(0xE0 | dst.code());
+}
+
+
+void Assembler::shrd(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xAD);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::shr(Register dst, uint8_t imm8) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint5(imm8)); // illegal shift count
+ if (imm8 == 1) {
+ EMIT(0xD1);
+ EMIT(0xE8 | dst.code());
+ } else {
+ EMIT(0xC1);
+ EMIT(0xE8 | dst.code());
+ EMIT(imm8);
+ }
+}
+
+
+void Assembler::shr_cl(Register dst) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD3);
+ EMIT(0xE8 | dst.code());
+}
+
+
+void Assembler::sub(const Operand& dst, const Immediate& x) {
+ EnsureSpace ensure_space(this);
+ emit_arith(5, dst, x);
+}
+
+
+void Assembler::sub(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x2B);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::sub(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x29);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::test(Register reg, const Immediate& imm) {
+ EnsureSpace ensure_space(this);
+ // Only use test against byte for registers that have a byte
+ // variant: eax, ebx, ecx, and edx.
+ if (RelocInfo::IsNone(imm.rmode_) &&
+ is_uint8(imm.x_) &&
+ reg.is_byte_register()) {
+ uint8_t imm8 = imm.x_;
+ if (reg.is(eax)) {
+ EMIT(0xA8);
+ EMIT(imm8);
+ } else {
+ emit_arith_b(0xF6, 0xC0, reg, imm8);
+ }
+ } else {
+ // This is not using emit_arith because test doesn't support
+ // sign-extension of 8-bit operands.
+ if (reg.is(eax)) {
+ EMIT(0xA9);
+ } else {
+ EMIT(0xF7);
+ EMIT(0xC0 | reg.code());
+ }
+ emit(imm);
+ }
+}
+
+
+void Assembler::test(Register reg, const Operand& op) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x85);
+ emit_operand(reg, op);
+}
+
+
+void Assembler::test_b(Register reg, const Operand& op) {
+ CHECK(reg.is_byte_register());
+ EnsureSpace ensure_space(this);
+ EMIT(0x84);
+ emit_operand(reg, op);
+}
+
+
+void Assembler::test(const Operand& op, const Immediate& imm) {
+ if (op.is_reg_only()) {
+ test(op.reg(), imm);
+ return;
+ }
+ EnsureSpace ensure_space(this);
+ EMIT(0xF7);
+ emit_operand(eax, op);
+ emit(imm);
+}
+
+
+void Assembler::test_b(const Operand& op, uint8_t imm8) {
+ if (op.is_reg_only() && !op.reg().is_byte_register()) {
+ test(op, Immediate(imm8));
+ return;
+ }
+ EnsureSpace ensure_space(this);
+ EMIT(0xF6);
+ emit_operand(eax, op);
+ EMIT(imm8);
+}
+
+
+void Assembler::xor_(Register dst, int32_t imm32) {
+ EnsureSpace ensure_space(this);
+ emit_arith(6, Operand(dst), Immediate(imm32));
+}
+
+
+void Assembler::xor_(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x33);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::xor_(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x31);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::xor_(const Operand& dst, const Immediate& x) {
+ EnsureSpace ensure_space(this);
+ emit_arith(6, dst, x);
+}
+
+
+void Assembler::bt(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xA3);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::bts(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0xAB);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::hlt() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF4);
+}
+
+
+void Assembler::int3() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xCC);
+}
+
+
+void Assembler::nop() {
+ EnsureSpace ensure_space(this);
+ EMIT(0x90);
+}
+
+
+void Assembler::rdtsc() {
+ ASSERT(IsEnabled(RDTSC));
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0x31);
+}
+
+
+void Assembler::ret(int imm16) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint16(imm16));
+ if (imm16 == 0) {
+ EMIT(0xC3);
+ } else {
+ EMIT(0xC2);
+ EMIT(imm16 & 0xFF);
+ EMIT((imm16 >> 8) & 0xFF);
+ }
+}
+
+
+// Labels refer to positions in the (to be) generated code.
+// There are bound, linked, and unused labels.
+//
+// Bound labels refer to known positions in the already
+// generated code. pos() is the position the label refers to.
+//
+// Linked labels refer to unknown positions in the code
+// to be generated; pos() is the position of the 32bit
+// Displacement of the last instruction using the label.
+
+
+void Assembler::print(Label* L) {
+ if (L->is_unused()) {
+ PrintF("unused label\n");
+ } else if (L->is_bound()) {
+ PrintF("bound label to %d\n", L->pos());
+ } else if (L->is_linked()) {
+ Label l = *L;
+ PrintF("unbound label");
+ while (l.is_linked()) {
+ Displacement disp = disp_at(&l);
+ PrintF("@ %d ", l.pos());
+ disp.print();
+ PrintF("\n");
+ disp.next(&l);
+ }
+ } else {
+ PrintF("label in inconsistent state (pos = %d)\n", L->pos_);
+ }
+}
+
+
+void Assembler::bind_to(Label* L, int pos) {
+ EnsureSpace ensure_space(this);
+ ASSERT(0 <= pos && pos <= pc_offset()); // must have a valid binding position
+ while (L->is_linked()) {
+ Displacement disp = disp_at(L);
+ int fixup_pos = L->pos();
+ if (disp.type() == Displacement::CODE_RELATIVE) {
+ // Relative to Code* heap object pointer.
+ long_at_put(fixup_pos, pos + Code::kHeaderSize - kHeapObjectTag);
+ } else {
+ if (disp.type() == Displacement::UNCONDITIONAL_JUMP) {
+ ASSERT(byte_at(fixup_pos - 1) == 0xE9); // jmp expected
+ }
+ // Relative address, relative to point after address.
+ int imm32 = pos - (fixup_pos + sizeof(int32_t));
+ long_at_put(fixup_pos, imm32);
+ }
+ disp.next(L);
+ }
+ while (L->is_near_linked()) {
+ int fixup_pos = L->near_link_pos();
+ int offset_to_next =
+ static_cast<int>(*reinterpret_cast<int8_t*>(addr_at(fixup_pos)));
+ ASSERT(offset_to_next <= 0);
+ // Relative address, relative to point after address.
+ int disp = pos - fixup_pos - sizeof(int8_t);
+ CHECK(0 <= disp && disp <= 127);
+ set_byte_at(fixup_pos, disp);
+ if (offset_to_next < 0) {
+ L->link_to(fixup_pos + offset_to_next, Label::kNear);
+ } else {
+ L->UnuseNear();
+ }
+ }
+ L->bind_to(pos);
+}
+
+
+void Assembler::bind(Label* L) {
+ EnsureSpace ensure_space(this);
+ ASSERT(!L->is_bound()); // label can only be bound once
+ bind_to(L, pc_offset());
+}
+
+
+void Assembler::call(Label* L) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ if (L->is_bound()) {
+ const int long_size = 5;
+ int offs = L->pos() - pc_offset();
+ ASSERT(offs <= 0);
+ // 1110 1000 #32-bit disp.
+ EMIT(0xE8);
+ emit(offs - long_size);
+ } else {
+ // 1110 1000 #32-bit disp.
+ EMIT(0xE8);
+ emit_disp(L, Displacement::OTHER);
+ }
+}
+
+
+void Assembler::call(byte* entry, RelocInfo::Mode rmode) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ ASSERT(!RelocInfo::IsCodeTarget(rmode));
+ EMIT(0xE8);
+ if (RelocInfo::IsRuntimeEntry(rmode)) {
+ emit(reinterpret_cast<uint32_t>(entry), rmode);
+ } else {
+ emit(entry - (pc_ + sizeof(int32_t)), rmode);
+ }
+}
+
+
+int Assembler::CallSize(const Operand& adr) {
+ // Call size is 1 (opcode) + adr.len_ (operand).
+ return 1 + adr.len_;
+}
+
+
+void Assembler::call(const Operand& adr) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ EMIT(0xFF);
+ emit_operand(edx, adr);
+}
+
+
+int Assembler::CallSize(Handle<Code> code, RelocInfo::Mode rmode) {
+ return 1 /* EMIT */ + sizeof(uint32_t) /* emit */;
+}
+
+
+void Assembler::call(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ EMIT(0xE8);
+ emit(code, rmode, ast_id);
+}
+
+
+void Assembler::jmp(Label* L, Label::Distance distance) {
+ EnsureSpace ensure_space(this);
+ if (L->is_bound()) {
+ const int short_size = 2;
+ const int long_size = 5;
+ int offs = L->pos() - pc_offset();
+ ASSERT(offs <= 0);
+ if (is_int8(offs - short_size)) {
+ // 1110 1011 #8-bit disp.
+ EMIT(0xEB);
+ EMIT((offs - short_size) & 0xFF);
+ } else {
+ // 1110 1001 #32-bit disp.
+ EMIT(0xE9);
+ emit(offs - long_size);
+ }
+ } else if (distance == Label::kNear) {
+ EMIT(0xEB);
+ emit_near_disp(L);
+ } else {
+ // 1110 1001 #32-bit disp.
+ EMIT(0xE9);
+ emit_disp(L, Displacement::UNCONDITIONAL_JUMP);
+ }
+}
+
+
+void Assembler::jmp(byte* entry, RelocInfo::Mode rmode) {
+ EnsureSpace ensure_space(this);
+ ASSERT(!RelocInfo::IsCodeTarget(rmode));
+ EMIT(0xE9);
+ if (RelocInfo::IsRuntimeEntry(rmode)) {
+ emit(reinterpret_cast<uint32_t>(entry), rmode);
+ } else {
+ emit(entry - (pc_ + sizeof(int32_t)), rmode);
+ }
+}
+
+
+void Assembler::jmp(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xFF);
+ emit_operand(esp, adr);
+}
+
+
+void Assembler::jmp(Handle<Code> code, RelocInfo::Mode rmode) {
+ EnsureSpace ensure_space(this);
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ EMIT(0xE9);
+ emit(code, rmode);
+}
+
+
+void Assembler::j(Condition cc, Label* L, Label::Distance distance) {
+ EnsureSpace ensure_space(this);
+ ASSERT(0 <= cc && static_cast<int>(cc) < 16);
+ if (L->is_bound()) {
+ const int short_size = 2;
+ const int long_size = 6;
+ int offs = L->pos() - pc_offset();
+ ASSERT(offs <= 0);
+ if (is_int8(offs - short_size)) {
+ // 0111 tttn #8-bit disp
+ EMIT(0x70 | cc);
+ EMIT((offs - short_size) & 0xFF);
+ } else {
+ // 0000 1111 1000 tttn #32-bit disp
+ EMIT(0x0F);
+ EMIT(0x80 | cc);
+ emit(offs - long_size);
+ }
+ } else if (distance == Label::kNear) {
+ EMIT(0x70 | cc);
+ emit_near_disp(L);
+ } else {
+ // 0000 1111 1000 tttn #32-bit disp
+ // Note: could eliminate cond. jumps to this jump if condition
+ // is the same however, seems to be rather unlikely case.
+ EMIT(0x0F);
+ EMIT(0x80 | cc);
+ emit_disp(L, Displacement::OTHER);
+ }
+}
+
+
+void Assembler::j(Condition cc, byte* entry, RelocInfo::Mode rmode) {
+ EnsureSpace ensure_space(this);
+ ASSERT((0 <= cc) && (static_cast<int>(cc) < 16));
+ // 0000 1111 1000 tttn #32-bit disp.
+ EMIT(0x0F);
+ EMIT(0x80 | cc);
+ if (RelocInfo::IsRuntimeEntry(rmode)) {
+ emit(reinterpret_cast<uint32_t>(entry), rmode);
+ } else {
+ emit(entry - (pc_ + sizeof(int32_t)), rmode);
+ }
+}
+
+
+void Assembler::j(Condition cc, Handle<Code> code) {
+ EnsureSpace ensure_space(this);
+ // 0000 1111 1000 tttn #32-bit disp
+ EMIT(0x0F);
+ EMIT(0x80 | cc);
+ emit(code, RelocInfo::CODE_TARGET);
+}
+
+
+// FPU instructions.
+
+void Assembler::fld(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xD9, 0xC0, i);
+}
+
+
+void Assembler::fstp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDD, 0xD8, i);
+}
+
+
+void Assembler::fld1() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xE8);
+}
+
+
+void Assembler::fldpi() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xEB);
+}
+
+
+void Assembler::fldz() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xEE);
+}
+
+
+void Assembler::fldln2() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xED);
+}
+
+
+void Assembler::fld_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ emit_operand(eax, adr);
+}
+
+
+void Assembler::fld_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDD);
+ emit_operand(eax, adr);
+}
+
+
+void Assembler::fstp_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ emit_operand(ebx, adr);
+}
+
+
+void Assembler::fstp_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDD);
+ emit_operand(ebx, adr);
+}
+
+
+void Assembler::fst_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDD);
+ emit_operand(edx, adr);
+}
+
+
+void Assembler::fild_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDB);
+ emit_operand(eax, adr);
+}
+
+
+void Assembler::fild_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDF);
+ emit_operand(ebp, adr);
+}
+
+
+void Assembler::fistp_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDB);
+ emit_operand(ebx, adr);
+}
+
+
+void Assembler::fisttp_s(const Operand& adr) {
+ ASSERT(IsEnabled(SSE3));
+ EnsureSpace ensure_space(this);
+ EMIT(0xDB);
+ emit_operand(ecx, adr);
+}
+
+
+void Assembler::fisttp_d(const Operand& adr) {
+ ASSERT(IsEnabled(SSE3));
+ EnsureSpace ensure_space(this);
+ EMIT(0xDD);
+ emit_operand(ecx, adr);
+}
+
+
+void Assembler::fist_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDB);
+ emit_operand(edx, adr);
+}
+
+
+void Assembler::fistp_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDF);
+ emit_operand(edi, adr);
+}
+
+
+void Assembler::fabs() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xE1);
+}
+
+
+void Assembler::fchs() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xE0);
+}
+
+
+void Assembler::fcos() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xFF);
+}
+
+
+void Assembler::fsin() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xFE);
+}
+
+
+void Assembler::fptan() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xF2);
+}
+
+
+void Assembler::fyl2x() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xF1);
+}
+
+
+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);
+}
+
+
+void Assembler::fsub(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDC, 0xE8, i);
+}
+
+
+void Assembler::fisub_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDA);
+ emit_operand(esp, adr);
+}
+
+
+void Assembler::fmul_i(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xD8, 0xC8, i);
+}
+
+
+void Assembler::fmul(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDC, 0xC8, i);
+}
+
+
+void Assembler::fdiv(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDC, 0xF8, i);
+}
+
+
+void Assembler::faddp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xC0, i);
+}
+
+
+void Assembler::fsubp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xE8, i);
+}
+
+
+void Assembler::fsubrp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xE0, i);
+}
+
+
+void Assembler::fmulp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xC8, i);
+}
+
+
+void Assembler::fdivp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xF8, i);
+}
+
+
+void Assembler::fprem() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xF8);
+}
+
+
+void Assembler::fprem1() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xF5);
+}
+
+
+void Assembler::fxch(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xD9, 0xC8, i);
+}
+
+
+void Assembler::fincstp() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xF7);
+}
+
+
+void Assembler::ffree(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDD, 0xC0, i);
+}
+
+
+void Assembler::ftst() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xE4);
+}
+
+
+void Assembler::fucomp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDD, 0xE8, i);
+}
+
+
+void Assembler::fucompp() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDA);
+ EMIT(0xE9);
+}
+
+
+void Assembler::fucomi(int i) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDB);
+ EMIT(0xE8 + i);
+}
+
+
+void Assembler::fucomip() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDF);
+ EMIT(0xE9);
+}
+
+
+void Assembler::fcompp() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDE);
+ EMIT(0xD9);
+}
+
+
+void Assembler::fnstsw_ax() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDF);
+ EMIT(0xE0);
+}
+
+
+void Assembler::fwait() {
+ EnsureSpace ensure_space(this);
+ EMIT(0x9B);
+}
+
+
+void Assembler::frndint() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xD9);
+ EMIT(0xFC);
+}
+
+
+void Assembler::fnclex() {
+ EnsureSpace ensure_space(this);
+ EMIT(0xDB);
+ EMIT(0xE2);
+}
+
+
+void Assembler::sahf() {
+ EnsureSpace ensure_space(this);
+ EMIT(0x9E);
+}
+
+
+void Assembler::setcc(Condition cc, Register reg) {
+ ASSERT(reg.is_byte_register());
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0x90 | cc);
+ EMIT(0xC0 | reg.code());
+}
+
+
+void Assembler::cvttss2si(Register dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3);
+ EMIT(0x0F);
+ EMIT(0x2C);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::cvttsd2si(Register dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x2C);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::cvtsd2si(Register dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x2D);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtsi2sd(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x2A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3);
+ EMIT(0x0F);
+ EMIT(0x5A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x5A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::addsd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x58);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::addsd(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x58);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x59);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::mulsd(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x59);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::subsd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x5C);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::divsd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x5E);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::xorpd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x57);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::xorps(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0x57);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x51);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::andpd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x54);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::orpd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x56);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x2E);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::ucomisd(XMMRegister dst, const Operand& src) {
+ ASSERT(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(IsEnabled(SSE4_1));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x3A);
+ EMIT(0x0B);
+ emit_sse_operand(dst, src);
+ // Mask precision exeption.
+ EMIT(static_cast<byte>(mode) | 0x8);
+}
+
+
+void Assembler::movmskpd(Register dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x50);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movmskps(Register dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0x50);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::pcmpeqd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x76);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cmpltsd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0xC2);
+ emit_sse_operand(dst, src);
+ EMIT(1); // LT == 1
+}
+
+
+void Assembler::movaps(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0x28);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movdqa(const Operand& dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x7F);
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movdqa(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x6F);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movdqu(const Operand& dst, XMMRegister src ) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3);
+ EMIT(0x0F);
+ EMIT(0x7F);
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movdqu(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3);
+ EMIT(0x0F);
+ EMIT(0x6F);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movntdqa(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE4_1));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x38);
+ EMIT(0x2A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movntdq(const Operand& dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xE7);
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::prefetch(const Operand& src, int level) {
+ ASSERT(is_uint2(level));
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0x18);
+ // Emit hint number in Reg position of RegR/M.
+ XMMRegister code = XMMRegister::from_code(level);
+ emit_sse_operand(code, src);
+}
+
+
+void Assembler::movdbl(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ movsd(dst, src);
+}
+
+
+void Assembler::movdbl(const Operand& dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ movsd(dst, src);
+}
+
+
+void Assembler::movsd(const Operand& dst, XMMRegister src ) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2); // double
+ EMIT(0x0F);
+ EMIT(0x11); // store
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movsd(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2); // double
+ EMIT(0x0F);
+ EMIT(0x10); // load
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movsd(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x10);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movss(const Operand& dst, XMMRegister src ) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3); // float
+ EMIT(0x0F);
+ EMIT(0x11); // store
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movss(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3); // float
+ EMIT(0x0F);
+ EMIT(0x10); // load
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movss(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0xF3);
+ EMIT(0x0F);
+ EMIT(0x10);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movd(XMMRegister dst, const Operand& src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x6E);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movd(const Operand& dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x7E);
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::extractps(Register dst, XMMRegister src, byte imm8) {
+ ASSERT(IsEnabled(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(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xDB);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::pxor(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xEF);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::por(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xEB);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::ptest(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE4_1));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x38);
+ EMIT(0x17);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::psllq(XMMRegister reg, int8_t shift) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x73);
+ emit_sse_operand(esi, reg); // esi == 6
+ EMIT(shift);
+}
+
+
+void Assembler::psllq(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xF3);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::psrlq(XMMRegister reg, int8_t shift) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x73);
+ emit_sse_operand(edx, reg); // edx == 2
+ EMIT(shift);
+}
+
+
+void Assembler::psrlq(XMMRegister dst, XMMRegister src) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xD3);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::pshufd(XMMRegister dst, XMMRegister src, uint8_t shuffle) {
+ ASSERT(IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x70);
+ emit_sse_operand(dst, src);
+ EMIT(shuffle);
+}
+
+
+void Assembler::pextrd(const Operand& dst, XMMRegister src, int8_t offset) {
+ ASSERT(IsEnabled(SSE4_1));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x3A);
+ EMIT(0x16);
+ emit_sse_operand(src, dst);
+ EMIT(offset);
+}
+
+
+void Assembler::pinsrd(XMMRegister dst, const Operand& src, int8_t offset) {
+ ASSERT(IsEnabled(SSE4_1));
+ EnsureSpace ensure_space(this);
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x3A);
+ EMIT(0x22);
+ emit_sse_operand(dst, src);
+ EMIT(offset);
+}
+
+
+void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) {
+ Register ireg = { reg.code() };
+ emit_operand(ireg, adr);
+}
+
+
+void Assembler::emit_sse_operand(XMMRegister dst, XMMRegister src) {
+ EMIT(0xC0 | dst.code() << 3 | src.code());
+}
+
+
+void Assembler::emit_sse_operand(Register dst, XMMRegister src) {
+ EMIT(0xC0 | dst.code() << 3 | src.code());
+}
+
+
+void Assembler::Print() {
+ Disassembler::Decode(isolate(), stdout, buffer_, pc_);
+}
+
+
+void Assembler::RecordJSReturn() {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::JS_RETURN);
+}
+
+
+void Assembler::RecordDebugBreakSlot() {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
+void Assembler::RecordComment(const char* msg, bool force) {
+ if (FLAG_code_comments || force) {
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg));
+ }
+}
+
+
+void Assembler::GrowBuffer() {
+ ASSERT(overflow());
+ if (!own_buffer_) FATAL("external code buffer is too small");
+
+ // Compute new buffer size.
+ CodeDesc desc; // the new buffer
+ if (buffer_size_ < 4*KB) {
+ desc.buffer_size = 4*KB;
+ } else {
+ desc.buffer_size = 2*buffer_size_;
+ }
+ // Some internal data structures overflow for very large buffers,
+ // they must ensure that kMaximalBufferSize is not too large.
+ if ((desc.buffer_size > kMaximalBufferSize) ||
+ (desc.buffer_size > isolate()->heap()->MaxOldGenerationSize())) {
+ V8::FatalProcessOutOfMemory("Assembler::GrowBuffer");
+ }
+
+ // 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());
+
+ // Clear the buffer in debug mode. Use 'int3' instructions to make
+ // sure to get into problems if we ever run uninitialized code.
+#ifdef DEBUG
+ memset(desc.buffer, 0xCC, desc.buffer_size);
+#endif
+
+ // Copy the data.
+ int pc_delta = desc.buffer - buffer_;
+ int rc_delta = (desc.buffer + desc.buffer_size) - (buffer_ + buffer_size_);
+ OS::MemMove(desc.buffer, buffer_, desc.instr_size);
+ OS::MemMove(rc_delta + reloc_info_writer.pos(),
+ reloc_info_writer.pos(), desc.reloc_size);
+
+ // Switch buffers.
+ if (isolate()->assembler_spare_buffer() == NULL &&
+ buffer_size_ == kMinimalBufferSize) {
+ isolate()->set_assembler_spare_buffer(buffer_);
+ } else {
+ DeleteArray(buffer_);
+ }
+ buffer_ = desc.buffer;
+ buffer_size_ = desc.buffer_size;
+ pc_ += pc_delta;
+ reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
+ reloc_info_writer.last_pc() + pc_delta);
+
+ // Relocate runtime entries.
+ for (RelocIterator it(desc); !it.done(); it.next()) {
+ RelocInfo::Mode rmode = it.rinfo()->rmode();
+ if (rmode == RelocInfo::INTERNAL_REFERENCE) {
+ int32_t* p = reinterpret_cast<int32_t*>(it.rinfo()->pc());
+ if (*p != 0) { // 0 means uninitialized.
+ *p += pc_delta;
+ }
+ }
+ }
+
+ ASSERT(!overflow());
+}
+
+
+void Assembler::emit_arith_b(int op1, int op2, Register dst, int imm8) {
+ ASSERT(is_uint8(op1) && is_uint8(op2)); // wrong opcode
+ ASSERT(is_uint8(imm8));
+ ASSERT((op1 & 0x01) == 0); // should be 8bit operation
+ EMIT(op1);
+ EMIT(op2 | dst.code());
+ EMIT(imm8);
+}
+
+
+void Assembler::emit_arith(int sel, Operand dst, const Immediate& x) {
+ ASSERT((0 <= sel) && (sel <= 7));
+ Register ireg = { sel };
+ if (x.is_int8()) {
+ EMIT(0x83); // using a sign-extended 8-bit immediate.
+ emit_operand(ireg, dst);
+ EMIT(x.x_ & 0xFF);
+ } else if (dst.is_reg(eax)) {
+ EMIT((sel << 3) | 0x05); // short form if the destination is eax.
+ emit(x);
+ } else {
+ EMIT(0x81); // using a literal 32-bit immediate.
+ emit_operand(ireg, dst);
+ emit(x);
+ }
+}
+
+
+void Assembler::emit_operand(Register reg, const Operand& adr) {
+ const unsigned length = adr.len_;
+ ASSERT(length > 0);
+
+ // Emit updated ModRM byte containing the given register.
+ pc_[0] = (adr.buf_[0] & ~0x38) | (reg.code() << 3);
+
+ // Emit the rest of the encoded operand.
+ for (unsigned i = 1; i < length; i++) pc_[i] = adr.buf_[i];
+ pc_ += length;
+
+ // Emit relocation information if necessary.
+ if (length >= sizeof(int32_t) && !RelocInfo::IsNone(adr.rmode_)) {
+ pc_ -= sizeof(int32_t); // pc_ must be *at* disp32
+ RecordRelocInfo(adr.rmode_);
+ pc_ += sizeof(int32_t);
+ }
+}
+
+
+void Assembler::emit_farith(int b1, int b2, int i) {
+ ASSERT(is_uint8(b1) && is_uint8(b2)); // wrong opcode
+ ASSERT(0 <= i && i < 8); // illegal stack offset
+ EMIT(b1);
+ EMIT(b2 + i);
+}
+
+
+void Assembler::db(uint8_t data) {
+ EnsureSpace ensure_space(this);
+ EMIT(data);
+}
+
+
+void Assembler::dd(uint32_t data) {
+ EnsureSpace ensure_space(this);
+ emit(data);
+}
+
+
+void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
+ ASSERT(!RelocInfo::IsNone(rmode));
+ // Don't record external references unless the heap will be serialized.
+ if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
+#ifdef DEBUG
+ if (!Serializer::enabled()) {
+ Serializer::TooLateToEnableNow();
+ }
+#endif
+ if (!Serializer::enabled() && !emit_debug_code()) {
+ return;
+ }
+ }
+ RelocInfo rinfo(pc_, rmode, data, NULL);
+ reloc_info_writer.Write(&rinfo);
+}
+
+
+#ifdef GENERATED_CODE_COVERAGE
+static FILE* coverage_log = NULL;
+
+
+static void InitCoverageLog() {
+ char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG");
+ if (file_name != NULL) {
+ coverage_log = fopen(file_name, "aw+");
+ }
+}
+
+
+void LogGeneratedCodeCoverage(const char* file_line) {
+ const char* return_address = (&file_line)[-1];
+ char* push_insn = const_cast<char*>(return_address - 12);
+ push_insn[0] = 0xeb; // Relative branch insn.
+ push_insn[1] = 13; // Skip over coverage insns.
+ if (coverage_log != NULL) {
+ fprintf(coverage_log, "%s\n", file_line);
+ fflush(coverage_log);
+ }
+}
+
+#endif
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/assembler-ia32.h b/chromium/v8/src/ia32/assembler-ia32.h
new file mode 100644
index 00000000000..8380897f6f1
--- /dev/null
+++ b/chromium/v8/src/ia32/assembler-ia32.h
@@ -0,0 +1,1239 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// 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.
+
+// A light-weight IA32 Assembler.
+
+#ifndef V8_IA32_ASSEMBLER_IA32_H_
+#define V8_IA32_ASSEMBLER_IA32_H_
+
+#include "isolate.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+// CPU Registers.
+//
+// 1) We would prefer to use an enum, but enum values are assignment-
+// compatible with int, which has caused code-generation bugs.
+//
+// 2) We would prefer to use a class instead of a struct but we don't like
+// the register initialization to depend on the particular initialization
+// order (which appears to be different on OS X, Linux, and Windows for the
+// installed versions of C++ we tried). Using a struct permits C-style
+// "initialization". Also, the Register objects cannot be const as this
+// forces initialization stubs in MSVC, making us dependent on initialization
+// order.
+//
+// 3) By not using an enum, we are possibly preventing the compiler from
+// doing certain constant folds, which may significantly reduce the
+// code generated for some assembly instructions (because they boil down
+// to a few constants). If this is a problem, we could change the code
+// such that we use an enum in optimized mode, and the struct in debug
+// mode. This way we get the compile-time error checking in debug mode
+// and best performance in optimized code.
+//
+struct Register {
+ static const int kMaxNumAllocatableRegisters = 6;
+ static int NumAllocatableRegisters() {
+ return kMaxNumAllocatableRegisters;
+ }
+ static const int kNumRegisters = 8;
+
+ static inline const char* AllocationIndexToString(int index);
+
+ static inline int ToAllocationIndex(Register reg);
+
+ static inline Register FromAllocationIndex(int index);
+
+ static Register from_code(int code) {
+ ASSERT(code >= 0);
+ ASSERT(code < kNumRegisters);
+ Register r = { code };
+ return r;
+ }
+ bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; }
+ bool is(Register reg) const { return code_ == reg.code_; }
+ // eax, ebx, ecx and edx are byte registers, the rest are not.
+ bool is_byte_register() const { return code_ <= 3; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ ASSERT(is_valid());
+ return 1 << code_;
+ }
+
+ // Unfortunately we can't make this private in a struct.
+ int code_;
+};
+
+const int kRegister_eax_Code = 0;
+const int kRegister_ecx_Code = 1;
+const int kRegister_edx_Code = 2;
+const int kRegister_ebx_Code = 3;
+const int kRegister_esp_Code = 4;
+const int kRegister_ebp_Code = 5;
+const int kRegister_esi_Code = 6;
+const int kRegister_edi_Code = 7;
+const int kRegister_no_reg_Code = -1;
+
+const Register eax = { kRegister_eax_Code };
+const Register ecx = { kRegister_ecx_Code };
+const Register edx = { kRegister_edx_Code };
+const Register ebx = { kRegister_ebx_Code };
+const Register esp = { kRegister_esp_Code };
+const Register ebp = { kRegister_ebp_Code };
+const Register esi = { kRegister_esi_Code };
+const Register edi = { kRegister_edi_Code };
+const Register no_reg = { kRegister_no_reg_Code };
+
+
+inline const char* Register::AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ // This is the mapping of allocation indices to registers.
+ const char* const kNames[] = { "eax", "ecx", "edx", "ebx", "esi", "edi" };
+ return kNames[index];
+}
+
+
+inline int Register::ToAllocationIndex(Register reg) {
+ ASSERT(reg.is_valid() && !reg.is(esp) && !reg.is(ebp));
+ return (reg.code() >= 6) ? reg.code() - 2 : reg.code();
+}
+
+
+inline Register Register::FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ return (index >= 4) ? from_code(index + 2) : from_code(index);
+}
+
+
+struct IntelDoubleRegister {
+ static const int kMaxNumRegisters = 8;
+ static const int kMaxNumAllocatableRegisters = 7;
+ static int NumAllocatableRegisters();
+ static int NumRegisters();
+ static const char* AllocationIndexToString(int index);
+
+ static int ToAllocationIndex(IntelDoubleRegister reg) {
+ ASSERT(reg.code() != 0);
+ return reg.code() - 1;
+ }
+
+ static IntelDoubleRegister FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < NumAllocatableRegisters());
+ return from_code(index + 1);
+ }
+
+ static IntelDoubleRegister from_code(int code) {
+ IntelDoubleRegister result = { code };
+ return result;
+ }
+
+ bool is_valid() const {
+ return 0 <= code_ && code_ < NumRegisters();
+ }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+
+ int code_;
+};
+
+
+const IntelDoubleRegister double_register_0 = { 0 };
+const IntelDoubleRegister double_register_1 = { 1 };
+const IntelDoubleRegister double_register_2 = { 2 };
+const IntelDoubleRegister double_register_3 = { 3 };
+const IntelDoubleRegister double_register_4 = { 4 };
+const IntelDoubleRegister double_register_5 = { 5 };
+const IntelDoubleRegister double_register_6 = { 6 };
+const IntelDoubleRegister double_register_7 = { 7 };
+
+
+struct XMMRegister : IntelDoubleRegister {
+ static const int kNumAllocatableRegisters = 7;
+ static const int kNumRegisters = 8;
+
+ static XMMRegister from_code(int code) {
+ STATIC_ASSERT(sizeof(XMMRegister) == sizeof(IntelDoubleRegister));
+ XMMRegister result;
+ result.code_ = code;
+ return result;
+ }
+
+ bool is(XMMRegister reg) const { return code_ == reg.code_; }
+
+ static XMMRegister FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < NumAllocatableRegisters());
+ return from_code(index + 1);
+ }
+
+ static const char* AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < kNumAllocatableRegisters);
+ const char* const names[] = {
+ "xmm1",
+ "xmm2",
+ "xmm3",
+ "xmm4",
+ "xmm5",
+ "xmm6",
+ "xmm7"
+ };
+ return names[index];
+ }
+};
+
+
+#define xmm0 (static_cast<const XMMRegister&>(double_register_0))
+#define xmm1 (static_cast<const XMMRegister&>(double_register_1))
+#define xmm2 (static_cast<const XMMRegister&>(double_register_2))
+#define xmm3 (static_cast<const XMMRegister&>(double_register_3))
+#define xmm4 (static_cast<const XMMRegister&>(double_register_4))
+#define xmm5 (static_cast<const XMMRegister&>(double_register_5))
+#define xmm6 (static_cast<const XMMRegister&>(double_register_6))
+#define xmm7 (static_cast<const XMMRegister&>(double_register_7))
+
+
+struct X87Register : IntelDoubleRegister {
+ static const int kNumAllocatableRegisters = 5;
+ static const int kNumRegisters = 5;
+
+ bool is(X87Register reg) const {
+ return code_ == reg.code_;
+ }
+
+ static const char* AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < kNumAllocatableRegisters);
+ const char* const names[] = {
+ "stX_0", "stX_1", "stX_2", "stX_3", "stX_4"
+ };
+ return names[index];
+ }
+
+ static X87Register FromAllocationIndex(int index) {
+ STATIC_ASSERT(sizeof(X87Register) == sizeof(IntelDoubleRegister));
+ ASSERT(index >= 0 && index < NumAllocatableRegisters());
+ X87Register result;
+ result.code_ = index;
+ return result;
+ }
+
+ static int ToAllocationIndex(X87Register reg) {
+ return reg.code_;
+ }
+};
+
+#define stX_0 static_cast<const X87Register&>(double_register_0)
+#define stX_1 static_cast<const X87Register&>(double_register_1)
+#define stX_2 static_cast<const X87Register&>(double_register_2)
+#define stX_3 static_cast<const X87Register&>(double_register_3)
+#define stX_4 static_cast<const X87Register&>(double_register_4)
+
+
+typedef IntelDoubleRegister DoubleRegister;
+
+
+enum Condition {
+ // any value < 0 is considered no_condition
+ no_condition = -1,
+
+ overflow = 0,
+ no_overflow = 1,
+ below = 2,
+ above_equal = 3,
+ equal = 4,
+ not_equal = 5,
+ below_equal = 6,
+ above = 7,
+ negative = 8,
+ positive = 9,
+ parity_even = 10,
+ parity_odd = 11,
+ less = 12,
+ greater_equal = 13,
+ less_equal = 14,
+ greater = 15,
+
+ // aliases
+ carry = below,
+ not_carry = above_equal,
+ zero = equal,
+ not_zero = not_equal,
+ sign = negative,
+ not_sign = positive
+};
+
+
+// Returns the equivalent of !cc.
+// Negation of the default no_condition (-1) results in a non-default
+// no_condition value (-2). As long as tests for no_condition check
+// for condition < 0, this will work as expected.
+inline Condition NegateCondition(Condition cc) {
+ return static_cast<Condition>(cc ^ 1);
+}
+
+
+// Corresponds to transposing the operands of a comparison.
+inline Condition ReverseCondition(Condition cc) {
+ switch (cc) {
+ case below:
+ return above;
+ case above:
+ return below;
+ case above_equal:
+ return below_equal;
+ case below_equal:
+ return above_equal;
+ case less:
+ return greater;
+ case greater:
+ return less;
+ case greater_equal:
+ return less_equal;
+ case less_equal:
+ return greater_equal;
+ default:
+ return cc;
+ };
+}
+
+
+// -----------------------------------------------------------------------------
+// Machine instruction Immediates
+
+class Immediate BASE_EMBEDDED {
+ public:
+ inline explicit Immediate(int x);
+ inline explicit Immediate(const ExternalReference& ext);
+ inline explicit Immediate(Handle<Object> handle);
+ inline explicit Immediate(Smi* value);
+ inline explicit Immediate(Address addr);
+
+ static Immediate CodeRelativeOffset(Label* label) {
+ return Immediate(label);
+ }
+
+ bool is_zero() const { return x_ == 0 && RelocInfo::IsNone(rmode_); }
+ bool is_int8() const {
+ return -128 <= x_ && x_ < 128 && RelocInfo::IsNone(rmode_);
+ }
+ bool is_int16() const {
+ return -32768 <= x_ && x_ < 32768 && RelocInfo::IsNone(rmode_);
+ }
+
+ private:
+ inline explicit Immediate(Label* value);
+
+ int x_;
+ RelocInfo::Mode rmode_;
+
+ friend class Assembler;
+ friend class MacroAssembler;
+};
+
+
+// -----------------------------------------------------------------------------
+// Machine instruction Operands
+
+enum ScaleFactor {
+ times_1 = 0,
+ times_2 = 1,
+ times_4 = 2,
+ times_8 = 3,
+ times_int_size = times_4,
+ times_half_pointer_size = times_2,
+ times_pointer_size = times_4,
+ times_twice_pointer_size = times_8
+};
+
+
+class Operand BASE_EMBEDDED {
+ public:
+ // XMM reg
+ INLINE(explicit Operand(XMMRegister xmm_reg));
+
+ // [disp/r]
+ INLINE(explicit Operand(int32_t disp, RelocInfo::Mode rmode));
+ // disp only must always be relocated
+
+ // [base + disp/r]
+ explicit Operand(Register base, int32_t disp,
+ RelocInfo::Mode rmode = RelocInfo::NONE32);
+
+ // [base + index*scale + disp/r]
+ explicit Operand(Register base,
+ Register index,
+ ScaleFactor scale,
+ int32_t disp,
+ RelocInfo::Mode rmode = RelocInfo::NONE32);
+
+ // [index*scale + disp/r]
+ explicit Operand(Register index,
+ ScaleFactor scale,
+ int32_t disp,
+ RelocInfo::Mode rmode = RelocInfo::NONE32);
+
+ static Operand StaticVariable(const ExternalReference& ext) {
+ return Operand(reinterpret_cast<int32_t>(ext.address()),
+ RelocInfo::EXTERNAL_REFERENCE);
+ }
+
+ static Operand StaticArray(Register index,
+ ScaleFactor scale,
+ const ExternalReference& arr) {
+ return Operand(index, scale, reinterpret_cast<int32_t>(arr.address()),
+ RelocInfo::EXTERNAL_REFERENCE);
+ }
+
+ static Operand ForCell(Handle<Cell> cell) {
+ AllowDeferredHandleDereference embedding_raw_address;
+ return Operand(reinterpret_cast<int32_t>(cell.location()),
+ RelocInfo::CELL);
+ }
+
+ // 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:
+ // 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.
+ inline void set_modrm(int mod, Register rm);
+
+ inline void set_sib(ScaleFactor scale, Register index, Register base);
+ 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;
+};
+
+
+// -----------------------------------------------------------------------------
+// A Displacement describes the 32bit immediate field of an instruction which
+// may be used together with a Label in order to refer to a yet unknown code
+// position. Displacements stored in the instruction stream are used to describe
+// the instruction and to chain a list of instructions using the same Label.
+// A Displacement contains 2 different fields:
+//
+// next field: position of next displacement in the chain (0 = end of list)
+// type field: instruction type
+//
+// A next value of null (0) indicates the end of a chain (note that there can
+// be no displacement at position zero, because there is always at least one
+// instruction byte before the displacement).
+//
+// Displacement _data field layout
+//
+// |31.....2|1......0|
+// [ next | type |
+
+class Displacement BASE_EMBEDDED {
+ public:
+ enum Type {
+ UNCONDITIONAL_JUMP,
+ CODE_RELATIVE,
+ OTHER
+ };
+
+ int data() const { return data_; }
+ Type type() const { return TypeField::decode(data_); }
+ void next(Label* L) const {
+ int n = NextField::decode(data_);
+ n > 0 ? L->link_to(n) : L->Unuse();
+ }
+ void link_to(Label* L) { init(L, type()); }
+
+ explicit Displacement(int data) { data_ = data; }
+
+ Displacement(Label* L, Type type) { init(L, type); }
+
+ void print() {
+ PrintF("%s (%x) ", (type() == UNCONDITIONAL_JUMP ? "jmp" : "[other]"),
+ NextField::decode(data_));
+ }
+
+ private:
+ int data_;
+
+ class TypeField: public BitField<Type, 0, 2> {};
+ class NextField: public BitField<int, 2, 32-2> {};
+
+ void init(Label* L, Type type);
+};
+
+
+
+// CpuFeatures keeps track of which features are supported by the target CPU.
+// Supported features must be enabled by a CpuFeatureScope before use.
+// Example:
+// if (assembler->IsSupported(SSE2)) {
+// CpuFeatureScope fscope(assembler, SSE2);
+// // Generate SSE2 floating point code.
+// } else {
+// // Generate standard x87 floating point code.
+// }
+class CpuFeatures : public AllStatic {
+ public:
+ // Detect features of the target CPU. Set safe defaults if the serializer
+ // is enabled (snapshots must be portable).
+ static void Probe();
+
+ // Check whether a feature is supported by the target CPU.
+ static bool IsSupported(CpuFeature f) {
+ ASSERT(initialized_);
+ if (f == SSE2 && !FLAG_enable_sse2) return false;
+ if (f == SSE3 && !FLAG_enable_sse3) return false;
+ if (f == SSE4_1 && !FLAG_enable_sse4_1) return false;
+ if (f == CMOV && !FLAG_enable_cmov) return false;
+ if (f == RDTSC && !FLAG_enable_rdtsc) return false;
+ return (supported_ & (static_cast<uint64_t>(1) << f)) != 0;
+ }
+
+ static bool IsFoundByRuntimeProbingOnly(CpuFeature f) {
+ ASSERT(initialized_);
+ return (found_by_runtime_probing_only_ &
+ (static_cast<uint64_t>(1) << f)) != 0;
+ }
+
+ static bool IsSafeForSnapshot(CpuFeature f) {
+ return (IsSupported(f) &&
+ (!Serializer::enabled() || !IsFoundByRuntimeProbingOnly(f)));
+ }
+
+ private:
+#ifdef DEBUG
+ static bool initialized_;
+#endif
+ static uint64_t supported_;
+ static uint64_t found_by_runtime_probing_only_;
+
+ friend class ExternalReference;
+ DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
+};
+
+
+class Assembler : public AssemblerBase {
+ private:
+ // We check before assembling an instruction that there is sufficient
+ // space to write an instruction and its relocation information.
+ // The relocation writer's position must be kGap bytes above the end of
+ // the generated instructions. This leaves enough space for the
+ // longest possible ia32 instruction, 15 bytes, and the longest possible
+ // relocation information encoding, RelocInfoWriter::kMaxLength == 16.
+ // (There is a 15 byte limit on ia32 instruction length that rules out some
+ // otherwise valid instructions.)
+ // This allows for a single, fast space check per instruction.
+ static const int kGap = 32;
+
+ public:
+ // Create an assembler. Instructions and relocation information are emitted
+ // into a buffer, with the instructions starting from the beginning and the
+ // relocation information starting from the end of the buffer. See CodeDesc
+ // for a detailed comment on the layout (globals.h).
+ //
+ // If the provided buffer is NULL, the assembler allocates and grows its own
+ // buffer, and buffer_size determines the initial buffer size. The buffer is
+ // owned by the assembler and deallocated upon destruction of the assembler.
+ //
+ // If the provided buffer is not NULL, the assembler uses the provided buffer
+ // for code generation and assumes its size to be buffer_size. If the buffer
+ // is too small, a fatal error occurs. No deallocation of the buffer is done
+ // upon destruction of the assembler.
+ // TODO(vitalyr): the assembler does not need an isolate.
+ Assembler(Isolate* isolate, void* buffer, int buffer_size);
+ virtual ~Assembler() { }
+
+ // GetCode emits any pending (non-emitted) code and fills the descriptor
+ // desc. GetCode() is idempotent; it returns the same result if no other
+ // Assembler functions are invoked in between GetCode() calls.
+ void GetCode(CodeDesc* desc);
+
+ // Read/Modify the code target in the branch/call instruction at pc.
+ inline static Address target_address_at(Address pc);
+ inline static void set_target_address_at(Address pc, Address target);
+
+ // Return the code target address at a call site from the return address
+ // of that call in the instruction stream.
+ inline static Address target_address_from_return_address(Address pc);
+
+ // This sets the branch destination (which is in the instruction on x86).
+ // This is for calls and branches within generated code.
+ inline static void deserialization_set_special_target_at(
+ Address instruction_payload, Address target) {
+ set_target_address_at(instruction_payload, target);
+ }
+
+ // This sets the branch destination (which is in the instruction on x86).
+ // This is for calls and branches to runtime code.
+ inline static void set_external_target_at(Address instruction_payload,
+ Address target) {
+ set_target_address_at(instruction_payload, target);
+ }
+
+ static const int kSpecialTargetSize = kPointerSize;
+
+ // Distance between the address of the code target in the call instruction
+ // and the return address
+ static const int kCallTargetAddressOffset = kPointerSize;
+ // Distance between start of patched return sequence and the emitted address
+ // to jump to.
+ static const int kPatchReturnSequenceAddressOffset = 1; // JMP imm32.
+
+ // Distance between start of patched debug break slot and the emitted address
+ // to jump to.
+ static const int kPatchDebugBreakSlotAddressOffset = 1; // JMP imm32.
+
+ static const int kCallInstructionLength = 5;
+ static const int kPatchDebugBreakSlotReturnOffset = kPointerSize;
+ static const int kJSReturnSequenceLength = 6;
+
+ // The debug break slot must be able to contain a call instruction.
+ static const int kDebugBreakSlotLength = kCallInstructionLength;
+
+ // One byte opcode for test al, 0xXX.
+ static const byte kTestAlByte = 0xA8;
+ // One byte opcode for nop.
+ static const byte kNopByte = 0x90;
+
+ // One byte opcode for a short unconditional jump.
+ static const byte kJmpShortOpcode = 0xEB;
+ // One byte prefix for a short conditional jump.
+ static const byte kJccShortPrefix = 0x70;
+ static const byte kJncShortOpcode = kJccShortPrefix | not_carry;
+ static const byte kJcShortOpcode = kJccShortPrefix | carry;
+ static const byte kJnzShortOpcode = kJccShortPrefix | not_zero;
+ static const byte kJzShortOpcode = kJccShortPrefix | zero;
+
+
+ // ---------------------------------------------------------------------------
+ // Code generation
+ //
+ // - function names correspond one-to-one to ia32 instruction mnemonics
+ // - unless specified otherwise, instructions operate on 32bit operands
+ // - instructions on 8bit (byte) operands/registers have a trailing '_b'
+ // - instructions on 16bit (word) operands/registers have a trailing '_w'
+ // - naming conflicts with C++ keywords are resolved via a trailing '_'
+
+ // NOTE ON INTERFACE: Currently, the interface is not very consistent
+ // in the sense that some operations (e.g. mov()) can be called in more
+ // the one way to generate the same instruction: The Register argument
+ // can in some cases be replaced with an Operand(Register) argument.
+ // This should be cleaned up and made more orthogonal. The questions
+ // is: should we always use Operands instead of Registers where an
+ // Operand is possible, or should we have a Register (overloaded) form
+ // instead? We must be careful to make sure that the selected instruction
+ // is obvious from the parameters to avoid hard-to-find code generation
+ // bugs.
+
+ // Insert the smallest number of nop instructions
+ // 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();
+
+ // Stack
+ void pushad();
+ void popad();
+
+ void pushfd();
+ void popfd();
+
+ void push(const Immediate& x);
+ void push_imm32(int32_t imm32);
+ void push(Register src);
+ void push(const Operand& src);
+
+ void pop(Register dst);
+ void pop(const Operand& dst);
+
+ void enter(const Immediate& size);
+ 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);
+
+ void mov_w(Register dst, const Operand& src);
+ void mov_w(const Operand& dst, Register src);
+
+ void mov(Register dst, int32_t imm32);
+ void mov(Register dst, const Immediate& x);
+ void mov(Register dst, Handle<Object> handle);
+ void mov(Register dst, const Operand& src);
+ void mov(Register dst, Register src);
+ void mov(const Operand& dst, const Immediate& x);
+ 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, Register src) {
+ cmov(cc, dst, Operand(src));
+ }
+ void cmov(Condition cc, Register dst, const Operand& src);
+
+ // Flag management.
+ void cld();
+
+ // Repetitive string instructions.
+ void rep_movs();
+ void rep_stos();
+ void stos();
+
+ // Exchange two registers
+ void xchg(Register dst, Register src);
+
+ // Arithmetics
+ 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& 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 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);
+
+ void dec_b(Register dst);
+ void dec_b(const Operand& dst);
+
+ void dec(Register dst);
+ void dec(const Operand& dst);
+
+ void cdq();
+
+ void idiv(Register src);
+
+ // 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.
+
+ void inc(Register dst);
+ void inc(const Operand& dst);
+
+ void lea(Register dst, const Operand& src);
+
+ // Unsigned multiply instruction.
+ void mul(Register src); // edx:eax = eax * reg.
+
+ void neg(Register dst);
+
+ 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);
+ void rcr(Register dst, uint8_t imm8);
+ void ror(Register dst, uint8_t imm8);
+ void ror_cl(Register dst);
+
+ void sar(Register dst, uint8_t imm8);
+ void sar_cl(Register dst);
+
+ 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 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& 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
+ void hlt();
+ void int3();
+ void nop();
+ void rdtsc();
+ void ret(int imm16);
+
+ // Label operations & relative jumps (PPUM Appendix D)
+ //
+ // Takes a branch opcode (cc) and a label (L) and generates
+ // either a backward branch or a forward branch and links it
+ // to the label fixup chain. Usage:
+ //
+ // Label L; // unbound label
+ // j(cc, &L); // forward branch to unbound label
+ // bind(&L); // bind label to the current pc
+ // j(cc, &L); // backward branch to bound label
+ // bind(&L); // illegal: a label may be bound only once
+ //
+ // Note: The same Label can be used for forward and backward branches
+ // but it may be bound only once.
+
+ void bind(Label* L); // binds an unbound label L to the current code position
+
+ // Calls
+ 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,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId id = TypeFeedbackId::None());
+
+ // Jumps
+ // 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);
+
+ // Conditional jumps
+ void j(Condition cc,
+ Label* L,
+ Label::Distance distance = Label::kFar);
+ void j(Condition cc, byte* entry, RelocInfo::Mode rmode);
+ void j(Condition cc, Handle<Code> code);
+
+ // Floating-point operations
+ void fld(int i);
+ void fstp(int i);
+
+ void fld1();
+ void fldz();
+ void fldpi();
+ void fldln2();
+
+ void fld_s(const Operand& adr);
+ void fld_d(const Operand& adr);
+
+ void fstp_s(const Operand& adr);
+ void fstp_d(const Operand& adr);
+ void fst_d(const Operand& adr);
+
+ void fild_s(const Operand& adr);
+ void fild_d(const Operand& adr);
+
+ void fist_s(const Operand& adr);
+
+ void fistp_s(const Operand& adr);
+ void fistp_d(const Operand& adr);
+
+ // The fisttp instructions require SSE3.
+ void fisttp_s(const Operand& adr);
+ void fisttp_d(const Operand& adr);
+
+ void fabs();
+ void fchs();
+ void fcos();
+ void fsin();
+ void fptan();
+ void fyl2x();
+ void f2xm1();
+ void fscale();
+ void fninit();
+
+ void fadd(int i);
+ void fsub(int i);
+ void fmul(int i);
+ void fmul_i(int i);
+ void fdiv(int i);
+
+ void fisub_s(const Operand& adr);
+
+ void faddp(int i = 1);
+ void fsubp(int i = 1);
+ void fsubrp(int i = 1);
+ void fmulp(int i = 1);
+ void fdivp(int i = 1);
+ void fprem();
+ void fprem1();
+
+ void fxch(int i = 1);
+ void fincstp();
+ void ffree(int i = 0);
+
+ void ftst();
+ void fucomp(int i);
+ void fucompp();
+ void fucomi(int i);
+ void fucomip();
+ void fcompp();
+ void fnstsw_ax();
+ void fwait();
+ void fnclex();
+
+ void frndint();
+
+ void sahf();
+ void setcc(Condition cc, Register reg);
+
+ void cpuid();
+
+ // SSE2 instructions
+ void cvttss2si(Register dst, const Operand& src);
+ void cvttsd2si(Register dst, const Operand& src);
+ void cvtsd2si(Register dst, XMMRegister 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);
+
+ void addsd(XMMRegister dst, XMMRegister src);
+ void addsd(XMMRegister dst, const Operand& src);
+ void subsd(XMMRegister dst, XMMRegister src);
+ void mulsd(XMMRegister dst, XMMRegister src);
+ void mulsd(XMMRegister dst, const Operand& src);
+ void divsd(XMMRegister dst, XMMRegister src);
+ void xorpd(XMMRegister dst, XMMRegister src);
+ void xorps(XMMRegister dst, XMMRegister src);
+ void sqrtsd(XMMRegister dst, XMMRegister src);
+
+ void andpd(XMMRegister dst, XMMRegister src);
+ void orpd(XMMRegister dst, XMMRegister src);
+
+ void ucomisd(XMMRegister dst, XMMRegister src);
+ void ucomisd(XMMRegister dst, const Operand& src);
+
+ enum RoundingMode {
+ kRoundToNearest = 0x0,
+ kRoundDown = 0x1,
+ kRoundUp = 0x2,
+ kRoundToZero = 0x3
+ };
+
+ void roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode);
+
+ void movmskpd(Register dst, XMMRegister src);
+ void movmskps(Register dst, XMMRegister src);
+
+ void cmpltsd(XMMRegister dst, XMMRegister src);
+ void pcmpeqd(XMMRegister dst, XMMRegister src);
+
+ void movaps(XMMRegister dst, XMMRegister src);
+
+ void movdqa(XMMRegister dst, const Operand& src);
+ void movdqa(const Operand& dst, XMMRegister src);
+ void movdqu(XMMRegister dst, const Operand& src);
+ void movdqu(const Operand& dst, XMMRegister src);
+ void movdq(bool aligned, XMMRegister dst, const Operand& src) {
+ if (aligned) {
+ movdqa(dst, src);
+ } else {
+ movdqu(dst, src);
+ }
+ }
+
+ // Use either movsd or movlpd.
+ 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(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& 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);
+ void por(XMMRegister dst, XMMRegister src);
+ void ptest(XMMRegister dst, XMMRegister src);
+
+ void psllq(XMMRegister reg, int8_t shift);
+ void psllq(XMMRegister dst, XMMRegister src);
+ void psrlq(XMMRegister reg, int8_t shift);
+ void psrlq(XMMRegister dst, XMMRegister src);
+ void pshufd(XMMRegister dst, XMMRegister src, uint8_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 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
+ // non-temporal
+ void prefetch(const Operand& src, int level);
+ // TODO(lrn): Need SFENCE for movnt?
+
+ // Debugging
+ void Print();
+
+ // Check the code size generated from label to here.
+ int SizeOfCodeGeneratedSince(Label* label) {
+ return pc_offset() - label->pos();
+ }
+
+ // Mark address of the ExitJSFrame code.
+ void RecordJSReturn();
+
+ // Mark address of a debug break slot.
+ void RecordDebugBreakSlot();
+
+ // Record a comment relocation entry that can be used by a disassembler.
+ // Use --code-comments to enable, or provide "force = true" flag to always
+ // write a comment.
+ void RecordComment(const char* msg, bool force = false);
+
+ // Writes a single byte or word of data in the code stream. Used for
+ // inline tables, e.g., jump-tables.
+ void db(uint8_t data);
+ void dd(uint32_t data);
+
+ // Check if there is less than kGap bytes available in the buffer.
+ // If this is the case, we need to grow the buffer before emitting
+ // an instruction or relocation information.
+ inline bool overflow() const { return pc_ >= reloc_info_writer.pos() - kGap; }
+
+ // 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);
+
+ PositionsRecorder* positions_recorder() { return &positions_recorder_; }
+
+ int relocation_writer_size() {
+ return (buffer_ + buffer_size_) - reloc_info_writer.pos();
+ }
+
+ // Avoid overflows for displacements etc.
+ static const int kMaximalBufferSize = 512*MB;
+
+ byte byte_at(int pos) { return buffer_[pos]; }
+ void set_byte_at(int pos, byte value) { buffer_[pos] = value; }
+
+ protected:
+ void movsd(XMMRegister dst, const Operand& src);
+ void movsd(const Operand& dst, XMMRegister src);
+
+ void emit_sse_operand(XMMRegister reg, const Operand& adr);
+ void emit_sse_operand(XMMRegister dst, XMMRegister src);
+ void emit_sse_operand(Register dst, XMMRegister src);
+
+ byte* addr_at(int pos) { return buffer_ + pos; }
+
+
+ private:
+ uint32_t long_at(int pos) {
+ return *reinterpret_cast<uint32_t*>(addr_at(pos));
+ }
+ void long_at_put(int pos, uint32_t x) {
+ *reinterpret_cast<uint32_t*>(addr_at(pos)) = x;
+ }
+
+ // code emission
+ void GrowBuffer();
+ inline void emit(uint32_t x);
+ inline void emit(Handle<Object> handle);
+ inline void emit(uint32_t x,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId id = TypeFeedbackId::None());
+ inline void emit(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId id = TypeFeedbackId::None());
+ inline void emit(const Immediate& x);
+ inline void emit_w(const Immediate& x);
+
+ // Emit the code-object-relative offset of the label's position
+ inline void emit_code_relative_offset(Label* label);
+
+ // instruction generation
+ void emit_arith_b(int op1, int op2, Register dst, int imm8);
+
+ // Emit a basic arithmetic instruction (i.e. first byte of the family is 0x81)
+ // with a given destination expression and an immediate operand. It attempts
+ // to use the shortest encoding possible.
+ // sel specifies the /n in the modrm byte (see the Intel PRM).
+ void emit_arith(int sel, Operand dst, const Immediate& x);
+
+ void emit_operand(Register reg, const Operand& adr);
+
+ void emit_farith(int b1, int b2, int i);
+
+ // labels
+ void print(Label* L);
+ void bind_to(Label* L, int pos);
+
+ // displacements
+ inline Displacement disp_at(Label* L);
+ inline void disp_at_put(Label* L, Displacement disp);
+ inline void emit_disp(Label* L, Displacement::Type type);
+ inline void emit_near_disp(Label* L);
+
+ // record reloc info for current pc_
+ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
+
+ friend class CodePatcher;
+ friend class EnsureSpace;
+
+ // code generation
+ RelocInfoWriter reloc_info_writer;
+
+ PositionsRecorder positions_recorder_;
+ friend class PositionsRecorder;
+};
+
+
+// Helper class that ensures that there is enough space for generating
+// instructions and relocation information. The constructor makes
+// sure that there is enough space and (in debug mode) the destructor
+// checks that we did not generate too much.
+class EnsureSpace BASE_EMBEDDED {
+ public:
+ explicit EnsureSpace(Assembler* assembler) : assembler_(assembler) {
+ if (assembler_->overflow()) assembler_->GrowBuffer();
+#ifdef DEBUG
+ space_before_ = assembler_->available_space();
+#endif
+ }
+
+#ifdef DEBUG
+ ~EnsureSpace() {
+ int bytes_generated = space_before_ - assembler_->available_space();
+ ASSERT(bytes_generated < assembler_->kGap);
+ }
+#endif
+
+ private:
+ Assembler* assembler_;
+#ifdef DEBUG
+ int space_before_;
+#endif
+};
+
+} } // namespace v8::internal
+
+#endif // V8_IA32_ASSEMBLER_IA32_H_
diff --git a/chromium/v8/src/ia32/builtins-ia32.cc b/chromium/v8/src/ia32/builtins-ia32.cc
new file mode 100644
index 00000000000..59124eab757
--- /dev/null
+++ b/chromium/v8/src/ia32/builtins-ia32.cc
@@ -0,0 +1,1363 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "codegen.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+
+namespace v8 {
+namespace internal {
+
+
+#define __ ACCESS_MASM(masm)
+
+
+void Builtins::Generate_Adaptor(MacroAssembler* masm,
+ CFunctionId id,
+ BuiltinExtraArguments extra_args) {
+ // ----------- S t a t e -------------
+ // -- eax : number of arguments excluding receiver
+ // -- edi : called function (only guaranteed when
+ // extra_args requires it)
+ // -- esi : context
+ // -- esp[0] : return address
+ // -- esp[4] : last argument
+ // -- ...
+ // -- esp[4 * argc] : first argument (argc == eax)
+ // -- esp[4 * (argc +1)] : receiver
+ // -----------------------------------
+
+ // Insert extra arguments.
+ int num_extra_args = 0;
+ if (extra_args == NEEDS_CALLED_FUNCTION) {
+ num_extra_args = 1;
+ Register scratch = ebx;
+ __ pop(scratch); // Save return address.
+ __ push(edi);
+ __ push(scratch); // Restore return address.
+ } else {
+ ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
+ }
+
+ // JumpToExternalReference expects eax to contain the number of arguments
+ // including the receiver and the extra arguments.
+ __ add(eax, Immediate(num_extra_args + 1));
+ __ JumpToExternalReference(ExternalReference(id, masm->isolate()));
+}
+
+
+static void GenerateTailCallToSharedCode(MacroAssembler* masm) {
+ __ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kCodeOffset));
+ __ lea(eax, FieldOperand(eax, Code::kHeaderSize));
+ __ jmp(eax);
+}
+
+
+void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) {
+ GenerateTailCallToSharedCode(masm);
+}
+
+
+void Builtins::Generate_InstallRecompiledCode(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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::kInstallRecompiledCode, 1);
+
+ // Restore call kind information.
+ __ pop(ecx);
+ // Restore receiver.
+ __ pop(edi);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ lea(eax, FieldOperand(eax, Code::kHeaderSize));
+ __ jmp(eax);
+}
+
+
+void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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::kParallelRecompile, 1);
+
+ // Restore call kind information.
+ __ pop(ecx);
+ // Restore receiver.
+ __ pop(edi);
+
+ // Tear down internal frame.
+ }
+
+ GenerateTailCallToSharedCode(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
+ // -----------------------------------
+
+ // Should never count constructions for api objects.
+ ASSERT(!is_api_function || !count_constructions);
+
+ // Enter a construct frame.
+ {
+ FrameScope scope(masm, StackFrame::CONSTRUCT);
+
+ // Store a smi-tagged arguments count on the stack.
+ __ SmiTag(eax);
+ __ push(eax);
+
+ // 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;
+#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);
+#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);
+
+ __ push(eax);
+ __ push(edi);
+
+ __ push(edi); // constructor
+ // The call will replace the stub, so the countdown is only done once.
+ __ CallRuntime(Runtime::kFinalizeInstanceSize, 1);
+
+ __ pop(edi);
+ __ pop(eax);
+
+ __ bind(&allocate);
+ }
+
+ // Now allocate the JSObject on the heap.
+ // edi: constructor
+ // eax: initial map
+ __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset));
+ __ shl(edi, kPointerSizeLog2);
+ __ Allocate(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,
+ kUnexpectedNumberOfPreAllocatedPropertyFields);
+ }
+ __ InitializeFieldsWithFiller(ecx, esi, edx);
+ __ mov(edx, factory->one_pointer_filler_map());
+ }
+ __ 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, kPropertyAllocationCountFailed);
+
+ // 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
+ __ Allocate(FixedArray::kHeaderSize,
+ times_pointer_size,
+ edx,
+ REGISTER_VALUE_IS_INT32,
+ 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);
+ }
+
+ // 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);
+
+ // 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, eax); // store result in ebx
+
+ // 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);
+
+ // 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);
+
+ // 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, 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);
+ }
+
+ // Store offset of return address for deoptimizer.
+ if (!is_api_function && !count_constructions) {
+ masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // 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 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);
+
+ // 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.
+
+ // Leave construct frame.
+ }
+
+ // Remove caller arguments from the stack and return.
+ STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
+ __ pop(ecx);
+ __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
+ __ push(ecx);
+ __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1);
+ __ ret(0);
+}
+
+
+void Builtins::Generate_JSConstructStubCountdown(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, true);
+}
+
+
+void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, false);
+}
+
+
+void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, true, false);
+}
+
+
+static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
+ bool is_construct) {
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Clear the context before we push it when entering the internal frame.
+ __ Set(esi, Immediate(0));
+
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load the previous frame pointer (ebx) to access C arguments
+ __ mov(ebx, Operand(ebp, 0));
+
+ // Get the function from the frame and setup the context.
+ __ mov(ecx, Operand(ebx, EntryFrameConstants::kFunctionArgOffset));
+ __ mov(esi, FieldOperand(ecx, JSFunction::kContextOffset));
+
+ // Push the function and the receiver onto the stack.
+ __ push(ecx);
+ __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset));
+
+ // Load the number of arguments and setup pointer to the arguments.
+ __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset));
+ __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset));
+
+ // 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) {
+ // No type feedback cell is available
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(), masm->isolate());
+ __ mov(ebx, Immediate(undefined_sentinel));
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ __ CallStub(&stub);
+ } 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.
+ }
+ __ ret(kPointerSize); // Remove receiver.
+}
+
+
+void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
+ Generate_JSEntryTrampolineHelper(masm, false);
+}
+
+
+void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
+ Generate_JSEntryTrampolineHelper(masm, true);
+}
+
+
+void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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);
+
+ // Restore call kind information.
+ __ pop(ecx);
+ // Restore receiver.
+ __ pop(edi);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ lea(eax, FieldOperand(eax, Code::kHeaderSize));
+ __ jmp(eax);
+}
+
+
+void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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);
+
+ // Restore call kind information.
+ __ pop(ecx);
+ // Restore receiver.
+ __ pop(edi);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ lea(eax, FieldOperand(eax, Code::kHeaderSize));
+ __ jmp(eax);
+}
+
+
+static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) {
+ // For now, we are relying on the fact that make_code_young doesn't do any
+ // garbage collection which allows us to save/restore the registers without
+ // worrying about which of them contain pointers. We also don't build an
+ // internal frame to make the code faster, since we shouldn't have to do stack
+ // crawls in MakeCodeYoung. This seems a bit fragile.
+
+ // Re-execute the code that was patched back to the young age when
+ // the stub returns.
+ __ sub(Operand(esp, 0), Immediate(5));
+ __ pushad();
+ __ mov(eax, Operand(esp, 8 * kPointerSize));
+ {
+ FrameScope scope(masm, StackFrame::MANUAL);
+ __ PrepareCallCFunction(1, ebx);
+ __ mov(Operand(esp, 0), eax);
+ __ CallCFunction(
+ ExternalReference::get_make_code_young_function(masm->isolate()), 1);
+ }
+ __ popad();
+ __ ret(0);
+}
+
+#define DEFINE_CODE_AGE_BUILTIN_GENERATOR(C) \
+void Builtins::Generate_Make##C##CodeYoungAgainEvenMarking( \
+ MacroAssembler* masm) { \
+ GenerateMakeCodeYoungAgainCommon(masm); \
+} \
+void Builtins::Generate_Make##C##CodeYoungAgainOddMarking( \
+ MacroAssembler* masm) { \
+ GenerateMakeCodeYoungAgainCommon(masm); \
+}
+CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR)
+#undef DEFINE_CODE_AGE_BUILTIN_GENERATOR
+
+
+void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Preserve registers across notification, this is important for compiled
+ // stubs that tail call the runtime on deopts passing their parameters in
+ // registers.
+ __ pushad();
+ __ CallRuntime(Runtime::kNotifyStubFailure, 0);
+ __ popad();
+ // Tear down internal frame.
+ }
+
+ __ pop(MemOperand(esp, 0)); // Ignore state offset
+ __ ret(0); // Return to IC Miss stub, continuation still on stack.
+}
+
+
+static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
+ Deoptimizer::BailoutType type) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Pass deoptimization type to the runtime system.
+ __ push(Immediate(Smi::FromInt(static_cast<int>(type))));
+ __ CallRuntime(Runtime::kNotifyDeoptimized, 1);
+
+ // Tear down internal frame.
+ }
+
+ // Get the full codegen state from the stack and untag it.
+ __ mov(ecx, Operand(esp, 1 * kPointerSize));
+ __ SmiUntag(ecx);
+
+ // Switch on the state.
+ Label not_no_registers, not_tos_eax;
+ __ cmp(ecx, FullCodeGenerator::NO_REGISTERS);
+ __ j(not_equal, &not_no_registers, Label::kNear);
+ __ ret(1 * kPointerSize); // Remove state.
+
+ __ bind(&not_no_registers);
+ __ mov(eax, Operand(esp, 2 * kPointerSize));
+ __ cmp(ecx, FullCodeGenerator::TOS_REG);
+ __ j(not_equal, &not_tos_eax, Label::kNear);
+ __ ret(2 * kPointerSize); // Remove state, eax.
+
+ __ bind(&not_tos_eax);
+ __ Abort(kNoCasesLeft);
+}
+
+
+void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
+}
+
+
+void Builtins::Generate_NotifySoftDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
+}
+
+
+void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
+}
+
+
+void Builtins::Generate_NotifyOSR(MacroAssembler* masm) {
+ // TODO(kasperl): Do we need to save/restore the XMM registers too?
+ // TODO(mvstanton): We should save these regs, do this in a future
+ // checkin.
+
+ // 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.
+ __ pushad();
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ CallRuntime(Runtime::kNotifyOSR, 0);
+ }
+ __ popad();
+ __ ret(0);
+}
+
+
+void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
+ Factory* factory = masm->isolate()->factory();
+
+ // 1. Make sure we have at least one argument.
+ { Label done;
+ __ test(eax, eax);
+ __ j(not_zero, &done);
+ __ pop(ebx);
+ __ push(Immediate(factory->undefined_value()));
+ __ push(ebx);
+ __ inc(eax);
+ __ bind(&done);
+ }
+
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ Label slow, non_function;
+ // 1 ~ return address.
+ __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
+ __ JumpIfSmi(edi, &non_function);
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &slow);
+
+
+ // 3a. Patch the first argument if necessary when calling a function.
+ Label shift_arguments;
+ __ Set(edx, Immediate(0)); // indicate regular JS_FUNCTION
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+ // Do not transform the receiver for strict mode functions.
+ __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ test_b(FieldOperand(ebx, SharedFunctionInfo::kStrictModeByteOffset),
+ 1 << SharedFunctionInfo::kStrictModeBitWithinByte);
+ __ j(not_equal, &shift_arguments);
+
+ // Do not transform the receiver for natives (shared already in ebx).
+ __ test_b(FieldOperand(ebx, SharedFunctionInfo::kNativeByteOffset),
+ 1 << SharedFunctionInfo::kNativeBitWithinByte);
+ __ j(not_equal, &shift_arguments);
+
+ // Compute the receiver in non-strict mode.
+ __ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument.
+
+ // Call ToObject on the receiver if it is not an object, or use the
+ // global object if it is null or undefined.
+ __ JumpIfSmi(ebx, &convert_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, &shift_arguments);
+
+ __ bind(&convert_to_object);
+
+ { // 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);
+ }
+
+ // Restore the function to edi.
+ __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
+ __ jmp(&patch_receiver);
+
+ // Use the global receiver object from the called function as the
+ // receiver.
+ __ bind(&use_global_receiver);
+ const int kGlobalIndex =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ __ mov(ebx, FieldOperand(esi, kGlobalIndex));
+ __ mov(ebx, FieldOperand(ebx, GlobalObject::kNativeContextOffset));
+ __ mov(ebx, FieldOperand(ebx, kGlobalIndex));
+ __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
+
+ __ bind(&patch_receiver);
+ __ mov(Operand(esp, eax, times_4, 0), ebx);
+
+ __ jmp(&shift_arguments);
+ }
+
+ // 3b. Check for function proxy.
+ __ bind(&slow);
+ __ Set(edx, Immediate(1)); // indicate function proxy
+ __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
+ __ j(equal, &shift_arguments);
+ __ bind(&non_function);
+ __ Set(edx, Immediate(2)); // 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.
+ __ mov(Operand(esp, eax, times_4, 0), edi);
+
+ // 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.
+ __ bind(&shift_arguments);
+ { Label loop;
+ __ mov(ecx, eax);
+ __ bind(&loop);
+ __ mov(ebx, Operand(esp, ecx, times_4, 0));
+ __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
+ __ dec(ecx);
+ __ j(not_sign, &loop); // While non-negative (to copy return address).
+ __ pop(ebx); // Discard copy of return address.
+ __ dec(eax); // One fewer argument (first argument is new receiver).
+ }
+
+ // 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, edx);
+ __ j(zero, &function);
+ __ Set(ebx, Immediate(0));
+ __ 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);
+ __ bind(&function);
+ }
+
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(ebx,
+ FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ mov(edx, FieldOperand(edi, JSFunction::kCodeEntryOffset));
+ __ SmiUntag(ebx);
+ __ SetCallKind(ecx, CALL_AS_METHOD);
+ __ cmp(eax, ebx);
+ __ j(not_equal,
+ masm->isolate()->builtins()->ArgumentsAdaptorTrampoline());
+
+ ParameterCount expected(0);
+ __ InvokeCode(edx, expected, expected, JUMP_FUNCTION, NullCallWrapper(),
+ CALL_AS_METHOD);
+}
+
+
+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));
+
+ // 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);
+
+ Factory* factory = masm->isolate()->factory();
+
+ // 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, eax);
+ __ jmp(&push_receiver);
+
+ // Use the current global receiver object as the receiver.
+ __ bind(&use_global_receiver);
+ const int kGlobalOffset =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ __ mov(ebx, FieldOperand(esi, kGlobalOffset));
+ __ mov(ebx, FieldOperand(ebx, GlobalObject::kNativeContextOffset));
+ __ mov(ebx, FieldOperand(ebx, kGlobalOffset));
+ __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
+
+ // Push the receiver.
+ __ bind(&push_receiver);
+ __ push(ebx);
+
+ // Copy all arguments from the array to the stack.
+ Label entry, loop;
+ __ mov(ecx, Operand(ebp, kIndexOffset));
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ mov(edx, Operand(ebp, 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(eax);
+
+ // Update the index on the stack and in register eax.
+ __ mov(ecx, Operand(ebp, kIndexOffset));
+ __ add(ecx, Immediate(1 << kSmiTagSize));
+ __ mov(Operand(ebp, kIndexOffset), ecx);
+
+ __ bind(&entry);
+ __ cmp(ecx, Operand(ebp, kLimitOffset));
+ __ j(not_equal, &loop);
+
+ // Invoke the function.
+ Label call_proxy;
+ __ mov(eax, ecx);
+ 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);
+
+ 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);
+
+ // Leave internal frame.
+ }
+ __ ret(3 * kPointerSize); // remove this, receiver, and arguments
+}
+
+
+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, kUnexpectedInitialMapForInternalArrayFunction);
+ __ CmpObjectType(ebx, MAP_TYPE, ecx);
+ __ Assert(equal, kUnexpectedInitialMapForInternalArrayFunction);
+ }
+
+ // Run the native code for the InternalArray function called as a normal
+ // function.
+ // tail call a stub
+ InternalArrayConstructorStub stub(masm->isolate());
+ __ TailCallStub(&stub);
+}
+
+
+void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : argc
+ // -- esp[0] : return address
+ // -- esp[4] : last argument
+ // -----------------------------------
+ Label generic_array_code;
+
+ // Get the Array function.
+ __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, edi);
+
+ if (FLAG_debug_code) {
+ // 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));
+ __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction);
+ __ CmpObjectType(ebx, MAP_TYPE, ecx);
+ __ Assert(equal, kUnexpectedInitialMapForArrayFunction);
+ }
+
+ // Run the native code for the Array function called as a normal function.
+ // tail call a stub
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(),
+ masm->isolate());
+ __ mov(ebx, Immediate(undefined_sentinel));
+ ArrayConstructorStub stub(masm->isolate());
+ __ TailCallStub(&stub);
+}
+
+
+void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : number of arguments
+ // -- edi : constructor function
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->string_ctor_calls(), 1);
+
+ if (FLAG_debug_code) {
+ __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, ecx);
+ __ cmp(edi, ecx);
+ __ Assert(equal, kUnexpectedStringFunction);
+ }
+
+ // Load the first argument into eax and get rid of the rest
+ // (including the receiver).
+ Label no_arguments;
+ __ test(eax, eax);
+ __ j(zero, &no_arguments);
+ __ mov(ebx, Operand(esp, eax, times_pointer_size, 0));
+ __ pop(ecx);
+ __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize));
+ __ push(ecx);
+ __ mov(eax, ebx);
+
+ // Lookup the argument in the number to string cache.
+ Label not_cached, argument_is_string;
+ NumberToStringStub::GenerateLookupNumberStringCache(
+ masm,
+ eax, // Input.
+ ebx, // Result.
+ ecx, // Scratch 1.
+ edx, // Scratch 2.
+ &not_cached);
+ __ IncrementCounter(counters->string_ctor_cached_number(), 1);
+ __ bind(&argument_is_string);
+ // ----------- S t a t e -------------
+ // -- ebx : argument converted to string
+ // -- edi : constructor function
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ // Allocate a JSValue and put the tagged pointer into eax.
+ Label gc_required;
+ __ Allocate(JSValue::kSize,
+ eax, // Result.
+ ecx, // New allocation top (we ignore it).
+ no_reg,
+ &gc_required,
+ TAG_OBJECT);
+
+ // Set the map.
+ __ LoadGlobalFunctionInitialMap(edi, ecx);
+ if (FLAG_debug_code) {
+ __ cmpb(FieldOperand(ecx, Map::kInstanceSizeOffset),
+ JSValue::kSize >> kPointerSizeLog2);
+ __ Assert(equal, kUnexpectedStringWrapperInstanceSize);
+ __ cmpb(FieldOperand(ecx, Map::kUnusedPropertyFieldsOffset), 0);
+ __ Assert(equal, kUnexpectedUnusedPropertiesOfStringWrapper);
+ }
+ __ mov(FieldOperand(eax, HeapObject::kMapOffset), ecx);
+
+ // Set properties and elements.
+ Factory* factory = masm->isolate()->factory();
+ __ Set(ecx, Immediate(factory->empty_fixed_array()));
+ __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
+ __ mov(FieldOperand(eax, JSObject::kElementsOffset), ecx);
+
+ // Set the value.
+ __ mov(FieldOperand(eax, JSValue::kValueOffset), ebx);
+
+ // Ensure the object is fully initialized.
+ STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
+
+ // We're done. Return.
+ __ ret(0);
+
+ // The argument was not found in the number to string cache. Check
+ // if it's a string already before calling the conversion builtin.
+ Label convert_argument;
+ __ bind(&not_cached);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(eax, &convert_argument);
+ Condition is_string = masm->IsObjectStringType(eax, ebx, ecx);
+ __ j(NegateCondition(is_string), &convert_argument);
+ __ mov(ebx, eax);
+ __ IncrementCounter(counters->string_ctor_string_value(), 1);
+ __ jmp(&argument_is_string);
+
+ // Invoke the conversion builtin and put the result into ebx.
+ __ bind(&convert_argument);
+ __ IncrementCounter(counters->string_ctor_conversions(), 1);
+ {
+ 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);
+
+ // Load the empty string into ebx, remove the receiver from the
+ // stack, and jump back to the case where the argument is a string.
+ __ bind(&no_arguments);
+ __ Set(ebx, Immediate(factory->empty_string()));
+ __ pop(ecx);
+ __ lea(esp, Operand(esp, kPointerSize));
+ __ push(ecx);
+ __ jmp(&argument_is_string);
+
+ // At this point the argument is already a string. Call runtime to
+ // create a string wrapper.
+ __ bind(&gc_required);
+ __ IncrementCounter(counters->string_ctor_gc_required(), 1);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(ebx);
+ __ CallRuntime(Runtime::kNewStringWrapper, 1);
+ }
+ __ ret(0);
+}
+
+
+static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
+ __ push(ebp);
+ __ mov(ebp, esp);
+
+ // Store the arguments adaptor context sentinel.
+ __ push(Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Push the function on the stack.
+ __ push(edi);
+
+ // Preserve the number of arguments on the stack. Must preserve eax,
+ // ebx and ecx because these registers are used when copying the
+ // arguments and the receiver.
+ STATIC_ASSERT(kSmiTagSize == 1);
+ __ lea(edi, Operand(eax, eax, times_1, kSmiTag));
+ __ push(edi);
+}
+
+
+static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
+ // Retrieve the number of arguments from the stack.
+ __ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ // Leave the frame.
+ __ leave();
+
+ // Remove caller arguments from the stack.
+ STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
+ __ pop(ecx);
+ __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver
+ __ push(ecx);
+}
+
+
+void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : actual number of arguments
+ // -- ebx : expected number of arguments
+ // -- ecx : call kind information
+ // -- edx : code entry to call
+ // -----------------------------------
+
+ Label invoke, dont_adapt_arguments;
+ __ IncrementCounter(masm->isolate()->counters()->arguments_adaptors(), 1);
+
+ Label enough, too_few;
+ __ cmp(eax, ebx);
+ __ j(less, &too_few);
+ __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel);
+ __ j(equal, &dont_adapt_arguments);
+
+ { // Enough parameters: Actual >= expected.
+ __ bind(&enough);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Copy receiver and all expected arguments.
+ const int offset = StandardFrameConstants::kCallerSPOffset;
+ __ lea(eax, Operand(ebp, eax, times_4, offset));
+ __ mov(edi, -1); // account for receiver
+
+ Label copy;
+ __ bind(&copy);
+ __ inc(edi);
+ __ push(Operand(eax, 0));
+ __ sub(eax, Immediate(kPointerSize));
+ __ cmp(edi, ebx);
+ __ j(less, &copy);
+ __ jmp(&invoke);
+ }
+
+ { // Too few parameters: Actual < expected.
+ __ bind(&too_few);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Copy receiver and all actual arguments.
+ const int offset = StandardFrameConstants::kCallerSPOffset;
+ __ lea(edi, Operand(ebp, eax, times_4, offset));
+ // ebx = expected - actual.
+ __ sub(ebx, eax);
+ // eax = -actual - 1
+ __ neg(eax);
+ __ sub(eax, Immediate(1));
+
+ Label copy;
+ __ bind(&copy);
+ __ inc(eax);
+ __ push(Operand(edi, 0));
+ __ sub(edi, Immediate(kPointerSize));
+ __ test(eax, eax);
+ __ j(not_zero, &copy);
+
+ // Fill remaining expected arguments with undefined values.
+ Label fill;
+ __ bind(&fill);
+ __ inc(eax);
+ __ push(Immediate(masm->isolate()->factory()->undefined_value()));
+ __ cmp(eax, ebx);
+ __ j(less, &fill);
+ }
+
+ // Call the entry point.
+ __ bind(&invoke);
+ // Restore function pointer.
+ __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ call(edx);
+
+ // Store offset of return address for deoptimizer.
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
+
+ // Leave frame and return.
+ LeaveArgumentsAdaptorFrame(masm);
+ __ ret(0);
+
+ // -------------------------------------------
+ // Dont adapt arguments.
+ // -------------------------------------------
+ __ bind(&dont_adapt_arguments);
+ __ jmp(edx);
+}
+
+
+void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
+ __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+
+ // Pass the function to optimize as the argument to the on-stack
+ // replacement runtime function.
+ {
+ 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(eax, Immediate(Smi::FromInt(-1)));
+ __ j(not_equal, &skip, Label::kNear);
+ __ ret(0);
+
+ __ bind(&skip);
+ // Untag the AST id and push it on the stack.
+ __ SmiUntag(eax);
+ __ push(eax);
+
+ // Generate the code for doing the frame-to-frame translation using
+ // the deoptimizer infrastructure.
+ Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR);
+ generator.Generate();
+}
+
+
+#undef __
+}
+} // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/code-stubs-ia32.cc b/chromium/v8/src/ia32/code-stubs-ia32.cc
new file mode 100644
index 00000000000..12cc499a777
--- /dev/null
+++ b/chromium/v8/src/ia32/code-stubs-ia32.cc
@@ -0,0 +1,7723 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "bootstrapper.h"
+#include "code-stubs.h"
+#include "isolate.h"
+#include "jsregexp.h"
+#include "regexp-macro-assembler.h"
+#include "runtime.h"
+#include "stub-cache.h"
+#include "codegen.h"
+#include "runtime.h"
+
+namespace v8 {
+namespace internal {
+
+
+void ToNumberStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { eax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void FastCloneShallowArrayStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { eax, ebx, ecx };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kCreateArrayLiteralShallow)->entry;
+}
+
+
+void FastCloneShallowObjectStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { eax, ebx, ecx, edx };
+ descriptor->register_param_count_ = 4;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kCreateObjectLiteralShallow)->entry;
+}
+
+
+void CreateAllocationSiteStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { ebx };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedLoadFastElementStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { edx, ecx };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure);
+}
+
+
+void LoadFieldStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { edx };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedLoadFieldStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { edx };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedStoreFastElementStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { edx, ecx, eax };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure);
+}
+
+
+void TransitionElementsKindStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { eax, ebx };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry;
+}
+
+
+static void InitializeArrayConstructorDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor,
+ int constant_stack_parameter_count) {
+ // register state
+ // eax -- number of arguments
+ // edi -- function
+ // ebx -- type info cell with elements kind
+ static Register registers[] = { edi, ebx };
+ descriptor->register_param_count_ = 2;
+
+ if (constant_stack_parameter_count != 0) {
+ // stack param count needs (constructor pointer, and single argument)
+ descriptor->stack_parameter_count_ = &eax;
+ }
+ descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
+ descriptor->register_params_ = registers;
+ descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
+}
+
+
+static void InitializeInternalArrayConstructorDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor,
+ int constant_stack_parameter_count) {
+ // register state
+ // eax -- number of arguments
+ // edi -- constructor function
+ static Register registers[] = { edi };
+ descriptor->register_param_count_ = 1;
+
+ if (constant_stack_parameter_count != 0) {
+ // stack param count needs (constructor pointer, and single argument)
+ descriptor->stack_parameter_count_ = &eax;
+ }
+ descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
+ descriptor->register_params_ = registers;
+ descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
+}
+
+
+void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, 0);
+}
+
+
+void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, 1);
+}
+
+
+void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, -1);
+}
+
+
+void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0);
+}
+
+
+void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1);
+}
+
+
+void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1);
+}
+
+
+void CompareNilICStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { eax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(CompareNilIC_Miss);
+ descriptor->SetMissHandler(
+ ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate));
+}
+
+void ToBooleanStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { eax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(ToBooleanIC_Miss);
+ descriptor->SetMissHandler(
+ ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate));
+}
+
+
+void StoreGlobalStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { edx, ecx, eax };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(StoreIC_MissFromStubFailure);
+}
+
+
+void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { eax, ebx, ecx, edx };
+ descriptor->register_param_count_ = 4;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss);
+}
+
+
+#define __ ACCESS_MASM(masm)
+
+
+void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
+ // Update the static counter each time a new code stub is generated.
+ Isolate* isolate = masm->isolate();
+ isolate->counters()->code_stubs()->Increment();
+
+ CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate);
+ int param_count = descriptor->register_param_count_;
+ {
+ // Call the runtime system in a fresh internal frame.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ ASSERT(descriptor->register_param_count_ == 0 ||
+ eax.is(descriptor->register_params_[param_count - 1]));
+ // Push arguments
+ for (int i = 0; i < param_count; ++i) {
+ __ push(descriptor->register_params_[i]);
+ }
+ ExternalReference miss = descriptor->miss_handler();
+ __ CallExternalReference(miss, descriptor->register_param_count_);
+ }
+
+ __ ret(0);
+}
+
+
+void FastNewClosureStub::Generate(MacroAssembler* masm) {
+ // Create a new closure from the given function info in new
+ // space. Set the context to the current context in esi.
+ Counters* counters = masm->isolate()->counters();
+
+ Label gc;
+ __ Allocate(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT);
+
+ __ IncrementCounter(counters->fast_new_closure_total(), 1);
+
+ // Get the function info from the stack.
+ __ mov(edx, Operand(esp, 1 * kPointerSize));
+
+ int map_index = Context::FunctionMapIndex(language_mode_, is_generator_);
+
+ // Compute the function map in the current native context and set that
+ // as the map of the allocated object.
+ __ mov(ecx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ mov(ecx, FieldOperand(ecx, GlobalObject::kNativeContextOffset));
+ __ mov(ebx, Operand(ecx, Context::SlotOffset(map_index)));
+ __ mov(FieldOperand(eax, JSObject::kMapOffset), ebx);
+
+ // Initialize the rest of the function. We don't have to update the
+ // write barrier because the allocated object is in new space.
+ Factory* factory = masm->isolate()->factory();
+ __ mov(ebx, Immediate(factory->empty_fixed_array()));
+ __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ebx);
+ __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
+ __ mov(FieldOperand(eax, JSFunction::kPrototypeOrInitialMapOffset),
+ Immediate(factory->the_hole_value()));
+ __ mov(FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset), edx);
+ __ mov(FieldOperand(eax, JSFunction::kContextOffset), esi);
+ __ mov(FieldOperand(eax, JSFunction::kLiteralsOffset), ebx);
+
+ // Initialize the code pointer in the function to be the one
+ // found in the shared function info object.
+ // But first check if there is an optimized version for our context.
+ Label check_optimized;
+ Label install_unoptimized;
+ if (FLAG_cache_optimized_code) {
+ __ mov(ebx, FieldOperand(edx, SharedFunctionInfo::kOptimizedCodeMapOffset));
+ __ test(ebx, ebx);
+ __ j(not_zero, &check_optimized, Label::kNear);
+ }
+ __ bind(&install_unoptimized);
+ __ mov(FieldOperand(eax, JSFunction::kNextFunctionLinkOffset),
+ Immediate(factory->undefined_value()));
+ __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+ __ mov(FieldOperand(eax, JSFunction::kCodeEntryOffset), edx);
+
+ // Return and remove the on-stack parameter.
+ __ ret(1 * kPointerSize);
+
+ __ bind(&check_optimized);
+
+ __ IncrementCounter(counters->fast_new_closure_try_optimized(), 1);
+
+ // ecx holds native context, ebx points to fixed array of 3-element entries
+ // (native context, optimized code, literals).
+ // Map must never be empty, so check the first elements.
+ Label install_optimized;
+ // Speculatively move code object into edx.
+ __ mov(edx, FieldOperand(ebx, SharedFunctionInfo::kFirstCodeSlot));
+ __ cmp(ecx, FieldOperand(ebx, SharedFunctionInfo::kFirstContextSlot));
+ __ j(equal, &install_optimized);
+
+ // Iterate through the rest of map backwards. edx holds an index as a Smi.
+ Label loop;
+ Label restore;
+ __ mov(edx, FieldOperand(ebx, FixedArray::kLengthOffset));
+ __ bind(&loop);
+ // Do not double check first entry.
+ __ cmp(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kSecondEntryIndex)));
+ __ j(equal, &restore);
+ __ sub(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kEntryLength)));
+ __ cmp(ecx, CodeGenerator::FixedArrayElementOperand(ebx, edx, 0));
+ __ j(not_equal, &loop, Label::kNear);
+ // Hit: fetch the optimized code.
+ __ mov(edx, CodeGenerator::FixedArrayElementOperand(ebx, edx, 1));
+
+ __ bind(&install_optimized);
+ __ IncrementCounter(counters->fast_new_closure_install_optimized(), 1);
+
+ // TODO(fschneider): Idea: store proper code pointers in the optimized code
+ // map and either unmangle them on marking or do nothing as the whole map is
+ // discarded on major GC anyway.
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+ __ mov(FieldOperand(eax, JSFunction::kCodeEntryOffset), edx);
+
+ // Now link a function into a list of optimized functions.
+ __ mov(edx, ContextOperand(ecx, Context::OPTIMIZED_FUNCTIONS_LIST));
+
+ __ mov(FieldOperand(eax, JSFunction::kNextFunctionLinkOffset), edx);
+ // No need for write barrier as JSFunction (eax) is in the new space.
+
+ __ mov(ContextOperand(ecx, Context::OPTIMIZED_FUNCTIONS_LIST), eax);
+ // Store JSFunction (eax) into edx before issuing write barrier as
+ // it clobbers all the registers passed.
+ __ mov(edx, eax);
+ __ RecordWriteContextSlot(
+ ecx,
+ Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST),
+ edx,
+ ebx,
+ kDontSaveFPRegs);
+
+ // Return and remove the on-stack parameter.
+ __ ret(1 * kPointerSize);
+
+ __ bind(&restore);
+ // Restore SharedFunctionInfo into edx.
+ __ mov(edx, Operand(esp, 1 * kPointerSize));
+ __ jmp(&install_unoptimized);
+
+ // Create a new closure through the slower runtime call.
+ __ bind(&gc);
+ __ pop(ecx); // Temporarily remove return address.
+ __ pop(edx);
+ __ push(esi);
+ __ push(edx);
+ __ push(Immediate(factory->false_value()));
+ __ push(ecx); // Restore return address.
+ __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
+}
+
+
+void FastNewContextStub::Generate(MacroAssembler* masm) {
+ // Try to allocate the context in new space.
+ Label gc;
+ int length = slots_ + Context::MIN_CONTEXT_SLOTS;
+ __ Allocate((length * kPointerSize) + FixedArray::kHeaderSize,
+ eax, ebx, ecx, &gc, TAG_OBJECT);
+
+ // Get the function from the stack.
+ __ mov(ecx, Operand(esp, 1 * kPointerSize));
+
+ // 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)));
+
+ // 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);
+ __ mov(Operand(eax, Context::SlotOffset(Context::EXTENSION_INDEX)), ebx);
+
+ // Copy the global object from the previous context.
+ __ mov(ebx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ mov(Operand(eax, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)), ebx);
+
+ // Initialize the rest of the slots to undefined.
+ __ mov(ebx, factory->undefined_value());
+ for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
+ __ mov(Operand(eax, Context::SlotOffset(i)), ebx);
+ }
+
+ // Return and remove the on-stack parameter.
+ __ mov(esi, eax);
+ __ ret(1 * kPointerSize);
+
+ // Need to collect. Call into runtime system.
+ __ bind(&gc);
+ __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
+}
+
+
+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;
+ __ Allocate(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 native context we get a smi
+ // sentinel instead of a function. The block context should get the
+ // canonical empty function of the native context as its closure which
+ // we still have to look up.
+ Label after_sentinel;
+ __ JumpIfNotSmi(ecx, &after_sentinel, Label::kNear);
+ if (FLAG_debug_code) {
+ __ cmp(ecx, 0);
+ __ Assert(equal, kExpected0AsASmiSentinel);
+ }
+ __ mov(ecx, GlobalObjectOperand());
+ __ mov(ecx, FieldOperand(ecx, GlobalObject::kNativeContextOffset));
+ __ 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_OBJECT_INDEX));
+ __ mov(ContextOperand(eax, Context::GLOBAL_OBJECT_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);
+}
+
+
+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) {
+ CpuFeatureScope scope(masm, 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(masm->isolate())));
+ __ CallCFunction(
+ ExternalReference::store_buffer_overflow_function(masm->isolate()),
+ argument_count);
+ if (save_doubles_ == kSaveFPRegs) {
+ CpuFeatureScope scope(masm, 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);
+}
+
+
+class FloatingPointHelper : public AllStatic {
+ public:
+ enum ArgLocation {
+ ARGS_ON_STACK,
+ ARGS_IN_REGISTERS
+ };
+
+ // Code pattern for loading a floating point value. Input value must
+ // be either a smi or a heap number object (fp value). Requirements:
+ // operand in register number. Returns operand as floating point number
+ // on FPU stack.
+ static void LoadFloatOperand(MacroAssembler* masm, Register number);
+
+ // Code pattern for loading floating point values. Input values must
+ // be either smi or heap number objects (fp values). Requirements:
+ // operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax.
+ // Returns operands as floating point numbers on FPU stack.
+ static void LoadFloatOperands(MacroAssembler* masm,
+ Register scratch,
+ ArgLocation arg_location = ARGS_ON_STACK);
+
+ // Similar to LoadFloatOperand but assumes that both operands are smis.
+ // Expects operands in edx, eax.
+ static void LoadFloatSmis(MacroAssembler* masm, Register scratch);
+
+ // Test if operands are smi or number objects (fp). Requirements:
+ // operand_1 in eax, operand_2 in edx; falls through on float
+ // operands, jumps to the non_float label otherwise.
+ static void CheckFloatOperands(MacroAssembler* masm,
+ Label* non_float,
+ Register scratch);
+
+ // Takes the operands in edx and eax and loads them as integers in eax
+ // and ecx.
+ static void LoadUnknownsAsIntegers(MacroAssembler* masm,
+ bool use_sse3,
+ BinaryOpIC::TypeInfo left_type,
+ BinaryOpIC::TypeInfo right_type,
+ Label* operand_conversion_failure);
+
+ // Assumes that operands are smis or heap numbers and loads them
+ // into xmm0 and xmm1. Operands are in edx and eax.
+ // Leaves operands unchanged.
+ static void LoadSSE2Operands(MacroAssembler* masm);
+
+ // Test if operands are numbers (smi or HeapNumber objects), and load
+ // them into xmm0 and xmm1 if they are. Jump to label not_numbers if
+ // either operand is not a number. Operands are in edx and eax.
+ // Leaves operands unchanged.
+ static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers);
+
+ // Similar to LoadSSE2Operands but assumes that both operands are smis.
+ // Expects operands in edx, eax.
+ static void LoadSSE2Smis(MacroAssembler* masm, Register scratch);
+
+ // Checks that |operand| has an int32 value. If |int32_result| is different
+ // from |scratch|, it will contain that int32 value.
+ static void CheckSSE2OperandIsInt32(MacroAssembler* masm,
+ Label* non_int32,
+ XMMRegister operand,
+ Register int32_result,
+ Register scratch,
+ XMMRegister xmm_scratch);
+};
+
+
+void DoubleToIStub::Generate(MacroAssembler* masm) {
+ Register input_reg = this->source();
+ Register final_result_reg = this->destination();
+ ASSERT(is_truncating());
+
+ Label check_negative, process_64_bits, done, done_no_stash;
+
+ int double_offset = offset();
+
+ // Account for return address and saved regs if input is esp.
+ if (input_reg.is(esp)) double_offset += 3 * kPointerSize;
+
+ MemOperand mantissa_operand(MemOperand(input_reg, double_offset));
+ MemOperand exponent_operand(MemOperand(input_reg,
+ double_offset + kDoubleSize / 2));
+
+ Register scratch1;
+ {
+ Register scratch_candidates[3] = { ebx, edx, edi };
+ for (int i = 0; i < 3; i++) {
+ scratch1 = scratch_candidates[i];
+ if (!final_result_reg.is(scratch1) && !input_reg.is(scratch1)) break;
+ }
+ }
+ // Since we must use ecx for shifts below, use some other register (eax)
+ // to calculate the result if ecx is the requested return register.
+ Register result_reg = final_result_reg.is(ecx) ? eax : final_result_reg;
+ // Save ecx if it isn't the return register and therefore volatile, or if it
+ // is the return register, then save the temp register we use in its stead for
+ // the result.
+ Register save_reg = final_result_reg.is(ecx) ? eax : ecx;
+ __ push(scratch1);
+ __ push(save_reg);
+
+ bool stash_exponent_copy = !input_reg.is(esp);
+ __ mov(scratch1, mantissa_operand);
+ if (CpuFeatures::IsSupported(SSE3)) {
+ CpuFeatureScope scope(masm, SSE3);
+ // Load x87 register with heap number.
+ __ fld_d(mantissa_operand);
+ }
+ __ mov(ecx, exponent_operand);
+ if (stash_exponent_copy) __ push(ecx);
+
+ __ and_(ecx, HeapNumber::kExponentMask);
+ __ shr(ecx, HeapNumber::kExponentShift);
+ __ lea(result_reg, MemOperand(ecx, -HeapNumber::kExponentBias));
+ __ cmp(result_reg, Immediate(HeapNumber::kMantissaBits));
+ __ j(below, &process_64_bits);
+
+ // Result is entirely in lower 32-bits of mantissa
+ int delta = HeapNumber::kExponentBias + Double::kPhysicalSignificandSize;
+ if (CpuFeatures::IsSupported(SSE3)) {
+ __ fstp(0);
+ }
+ __ sub(ecx, Immediate(delta));
+ __ xor_(result_reg, result_reg);
+ __ cmp(ecx, Immediate(31));
+ __ j(above, &done);
+ __ shl_cl(scratch1);
+ __ jmp(&check_negative);
+
+ __ bind(&process_64_bits);
+ if (CpuFeatures::IsSupported(SSE3)) {
+ CpuFeatureScope scope(masm, SSE3);
+ if (stash_exponent_copy) {
+ // Already a copy of the exponent on the stack, overwrite it.
+ STATIC_ASSERT(kDoubleSize == 2 * kPointerSize);
+ __ sub(esp, Immediate(kDoubleSize / 2));
+ } else {
+ // Reserve space for 64 bit answer.
+ __ sub(esp, Immediate(kDoubleSize)); // Nolint.
+ }
+ // Do conversion, which cannot fail because we checked the exponent.
+ __ fisttp_d(Operand(esp, 0));
+ __ mov(result_reg, Operand(esp, 0)); // Load low word of answer as result
+ __ add(esp, Immediate(kDoubleSize));
+ __ jmp(&done_no_stash);
+ } else {
+ // Result must be extracted from shifted 32-bit mantissa
+ __ sub(ecx, Immediate(delta));
+ __ neg(ecx);
+ if (stash_exponent_copy) {
+ __ mov(result_reg, MemOperand(esp, 0));
+ } else {
+ __ mov(result_reg, exponent_operand);
+ }
+ __ and_(result_reg,
+ Immediate(static_cast<uint32_t>(Double::kSignificandMask >> 32)));
+ __ add(result_reg,
+ Immediate(static_cast<uint32_t>(Double::kHiddenBit >> 32)));
+ __ shrd(result_reg, scratch1);
+ __ shr_cl(result_reg);
+ __ test(ecx, Immediate(32));
+ if (CpuFeatures::IsSupported(CMOV)) {
+ CpuFeatureScope use_cmov(masm, CMOV);
+ __ cmov(not_equal, scratch1, result_reg);
+ } else {
+ Label skip_mov;
+ __ j(equal, &skip_mov, Label::kNear);
+ __ mov(scratch1, result_reg);
+ __ bind(&skip_mov);
+ }
+ }
+
+ // If the double was negative, negate the integer result.
+ __ bind(&check_negative);
+ __ mov(result_reg, scratch1);
+ __ neg(result_reg);
+ if (stash_exponent_copy) {
+ __ cmp(MemOperand(esp, 0), Immediate(0));
+ } else {
+ __ cmp(exponent_operand, Immediate(0));
+ }
+ if (CpuFeatures::IsSupported(CMOV)) {
+ CpuFeatureScope use_cmov(masm, CMOV);
+ __ cmov(greater, result_reg, scratch1);
+ } else {
+ Label skip_mov;
+ __ j(less_equal, &skip_mov, Label::kNear);
+ __ mov(result_reg, scratch1);
+ __ bind(&skip_mov);
+ }
+
+ // Restore registers
+ __ bind(&done);
+ if (stash_exponent_copy) {
+ __ add(esp, Immediate(kDoubleSize / 2));
+ }
+ __ bind(&done_no_stash);
+ if (!final_result_reg.is(result_reg)) {
+ ASSERT(final_result_reg.is(ecx));
+ __ mov(final_result_reg, result_reg);
+ }
+ __ pop(save_reg);
+ __ pop(scratch1);
+ __ ret(0);
+}
+
+
+// Uses SSE2 to convert the heap number in |source| to an integer. Jumps to
+// |conversion_failure| if the heap number did not contain an int32 value.
+// Result is in ecx. Trashes ebx, xmm0, and xmm1.
+static void ConvertHeapNumberToInt32(MacroAssembler* masm,
+ Register source,
+ Label* conversion_failure) {
+ __ movdbl(xmm0, FieldOperand(source, HeapNumber::kValueOffset));
+ FloatingPointHelper::CheckSSE2OperandIsInt32(
+ masm, conversion_failure, xmm0, ecx, ebx, xmm1);
+}
+
+
+void BinaryOpStub::Initialize() {
+ platform_specific_bit_ = CpuFeatures::IsSupported(SSE3);
+}
+
+
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ __ pop(ecx); // Save return address.
+ __ push(edx);
+ __ push(eax);
+ // Left and right arguments are now on top.
+ __ push(Immediate(Smi::FromInt(MinorKey())));
+
+ __ push(ecx); // Push return address.
+
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
+ masm->isolate()),
+ 3,
+ 1);
+}
+
+
+// Prepare for a type transition runtime call when the args are already on
+// the stack, under the return address.
+void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm) {
+ __ pop(ecx); // Save return address.
+ // Left and right arguments are already on top of the stack.
+ __ push(Immediate(Smi::FromInt(MinorKey())));
+
+ __ push(ecx); // Push return address.
+
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
+ masm->isolate()),
+ 3,
+ 1);
+}
+
+
+static void BinaryOpStub_GenerateRegisterArgsPop(MacroAssembler* masm) {
+ __ pop(ecx);
+ __ pop(eax);
+ __ pop(edx);
+ __ push(ecx);
+}
+
+
+static void BinaryOpStub_GenerateSmiCode(
+ MacroAssembler* masm,
+ Label* slow,
+ BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results,
+ Token::Value op) {
+ // 1. Move arguments into edx, eax except for DIV and MOD, which need the
+ // dividend in eax and edx free for the division. Use eax, ebx for those.
+ Comment load_comment(masm, "-- Load arguments");
+ Register left = edx;
+ Register right = eax;
+ if (op == Token::DIV || op == Token::MOD) {
+ left = eax;
+ right = ebx;
+ __ mov(ebx, eax);
+ __ mov(eax, edx);
+ }
+
+
+ // 2. Prepare the smi check of both operands by oring them together.
+ Comment smi_check_comment(masm, "-- Smi check arguments");
+ Label not_smis;
+ Register combined = ecx;
+ ASSERT(!left.is(combined) && !right.is(combined));
+ switch (op) {
+ case Token::BIT_OR:
+ // Perform the operation into eax and smi check the result. Preserve
+ // eax in case the result is not a smi.
+ ASSERT(!left.is(ecx) && !right.is(ecx));
+ __ mov(ecx, right);
+ __ or_(right, left); // Bitwise or is commutative.
+ combined = right;
+ break;
+
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ __ mov(combined, right);
+ __ or_(combined, left);
+ break;
+
+ case Token::SHL:
+ case Token::SAR:
+ case Token::SHR:
+ // Move the right operand into ecx for the shift operation, use eax
+ // for the smi check register.
+ ASSERT(!left.is(ecx) && !right.is(ecx));
+ __ mov(ecx, right);
+ __ or_(right, left);
+ combined = right;
+ break;
+
+ default:
+ break;
+ }
+
+ // 3. Perform the smi check of the operands.
+ STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case.
+ __ JumpIfNotSmi(combined, &not_smis);
+
+ // 4. Operands are both smis, perform the operation leaving the result in
+ // eax and check the result if necessary.
+ Comment perform_smi(masm, "-- Perform smi operation");
+ Label use_fp_on_smis;
+ switch (op) {
+ case Token::BIT_OR:
+ // Nothing to do.
+ break;
+
+ case Token::BIT_XOR:
+ ASSERT(right.is(eax));
+ __ xor_(right, left); // Bitwise xor is commutative.
+ break;
+
+ case Token::BIT_AND:
+ ASSERT(right.is(eax));
+ __ and_(right, left); // Bitwise and is commutative.
+ break;
+
+ case Token::SHL:
+ // Remove tags from operands (but keep sign).
+ __ SmiUntag(left);
+ __ SmiUntag(ecx);
+ // Perform the operation.
+ __ shl_cl(left);
+ // Check that the *signed* result fits in a smi.
+ __ cmp(left, 0xc0000000);
+ __ j(sign, &use_fp_on_smis);
+ // Tag the result and store it in register eax.
+ __ SmiTag(left);
+ __ mov(eax, left);
+ break;
+
+ case Token::SAR:
+ // Remove tags from operands (but keep sign).
+ __ SmiUntag(left);
+ __ SmiUntag(ecx);
+ // Perform the operation.
+ __ sar_cl(left);
+ // Tag the result and store it in register eax.
+ __ SmiTag(left);
+ __ mov(eax, left);
+ break;
+
+ case Token::SHR:
+ // Remove tags from operands (but keep sign).
+ __ SmiUntag(left);
+ __ SmiUntag(ecx);
+ // Perform the operation.
+ __ shr_cl(left);
+ // Check that the *unsigned* result fits in a smi.
+ // Neither of the two high-order bits can be set:
+ // - 0x80000000: high bit would be lost when smi tagging.
+ // - 0x40000000: this number would convert to negative when
+ // Smi tagging these two cases can only happen with shifts
+ // by 0 or 1 when handed a valid smi.
+ __ test(left, Immediate(0xc0000000));
+ __ j(not_zero, &use_fp_on_smis);
+ // Tag the result and store it in register eax.
+ __ SmiTag(left);
+ __ mov(eax, left);
+ break;
+
+ case Token::ADD:
+ ASSERT(right.is(eax));
+ __ add(right, left); // Addition is commutative.
+ __ j(overflow, &use_fp_on_smis);
+ break;
+
+ case Token::SUB:
+ __ sub(left, right);
+ __ j(overflow, &use_fp_on_smis);
+ __ mov(eax, left);
+ break;
+
+ case Token::MUL:
+ // If the smi tag is 0 we can just leave the tag on one operand.
+ STATIC_ASSERT(kSmiTag == 0); // Adjust code below if not the case.
+ // We can't revert the multiplication if the result is not a smi
+ // so save the right operand.
+ __ mov(ebx, right);
+ // Remove tag from one of the operands (but keep sign).
+ __ SmiUntag(right);
+ // Do multiplication.
+ __ 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);
+ break;
+
+ case Token::DIV:
+ // We can't revert the division if the result is not a smi so
+ // save the left operand.
+ __ mov(edi, left);
+ // Check for 0 divisor.
+ __ test(right, right);
+ __ j(zero, &use_fp_on_smis);
+ // Sign extend left into edx:eax.
+ ASSERT(left.is(eax));
+ __ cdq();
+ // Divide edx:eax by right.
+ __ idiv(right);
+ // Check for the corner case of dividing the most negative smi by
+ // -1. We cannot use the overflow flag, since it is not set by idiv
+ // instruction.
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ cmp(eax, 0x40000000);
+ __ j(equal, &use_fp_on_smis);
+ // Check for negative zero result. Use combined = left | right.
+ __ NegativeZeroTest(eax, combined, &use_fp_on_smis);
+ // Check that the remainder is zero.
+ __ test(edx, edx);
+ __ j(not_zero, &use_fp_on_smis);
+ // Tag the result and store it in register eax.
+ __ SmiTag(eax);
+ break;
+
+ case Token::MOD:
+ // Check for 0 divisor.
+ __ test(right, right);
+ __ j(zero, &not_smis);
+
+ // Sign extend left into edx:eax.
+ ASSERT(left.is(eax));
+ __ cdq();
+ // Divide edx:eax by right.
+ __ idiv(right);
+ // Check for negative zero result. Use combined = left | right.
+ __ NegativeZeroTest(edx, combined, slow);
+ // Move remainder to register eax.
+ __ mov(eax, edx);
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ // 5. Emit return of result in eax. Some operations have registers pushed.
+ switch (op) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ __ ret(0);
+ break;
+ case Token::MOD:
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ __ ret(2 * kPointerSize);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // 6. For some operations emit inline code to perform floating point
+ // operations on known smis (e.g., if the result of the operation
+ // overflowed the smi range).
+ if (allow_heapnumber_results == BinaryOpStub::NO_HEAPNUMBER_RESULTS) {
+ __ bind(&use_fp_on_smis);
+ switch (op) {
+ // Undo the effects of some operations, and some register moves.
+ case Token::SHL:
+ // The arguments are saved on the stack, and only used from there.
+ break;
+ case Token::ADD:
+ // Revert right = right + left.
+ __ sub(right, left);
+ break;
+ case Token::SUB:
+ // Revert left = left - right.
+ __ add(left, right);
+ break;
+ case Token::MUL:
+ // Right was clobbered but a copy is in ebx.
+ __ mov(right, ebx);
+ break;
+ case Token::DIV:
+ // Left was clobbered but a copy is in edi. Right is in ebx for
+ // division. They should be in eax, ebx for jump to not_smi.
+ __ mov(eax, edi);
+ break;
+ default:
+ // No other operators jump to use_fp_on_smis.
+ break;
+ }
+ __ jmp(&not_smis);
+ } else {
+ ASSERT(allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS);
+ switch (op) {
+ case Token::SHL:
+ case Token::SHR: {
+ Comment perform_float(masm, "-- Perform float operation on smis");
+ __ bind(&use_fp_on_smis);
+ // Result we want is in left == edx, so we can put the allocated heap
+ // number in eax.
+ __ AllocateHeapNumber(eax, ecx, ebx, slow);
+ // Store the result in the HeapNumber and return.
+ // It's OK to overwrite the arguments on the stack because we
+ // are about to return.
+ if (op == Token::SHR) {
+ __ mov(Operand(esp, 1 * kPointerSize), left);
+ __ mov(Operand(esp, 2 * kPointerSize), Immediate(0));
+ __ fild_d(Operand(esp, 1 * kPointerSize));
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ } else {
+ ASSERT_EQ(Token::SHL, op);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ cvtsi2sd(xmm0, left);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ mov(Operand(esp, 1 * kPointerSize), left);
+ __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ }
+ }
+ __ ret(2 * kPointerSize);
+ break;
+ }
+
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV: {
+ Comment perform_float(masm, "-- Perform float operation on smis");
+ __ bind(&use_fp_on_smis);
+ // Restore arguments to edx, eax.
+ switch (op) {
+ case Token::ADD:
+ // Revert right = right + left.
+ __ sub(right, left);
+ break;
+ case Token::SUB:
+ // Revert left = left - right.
+ __ add(left, right);
+ break;
+ case Token::MUL:
+ // Right was clobbered but a copy is in ebx.
+ __ mov(right, ebx);
+ break;
+ case Token::DIV:
+ // Left was clobbered but a copy is in edi. Right is in ebx for
+ // division.
+ __ mov(edx, edi);
+ __ mov(eax, right);
+ break;
+ default: UNREACHABLE();
+ break;
+ }
+ __ AllocateHeapNumber(ecx, ebx, no_reg, slow);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ FloatingPointHelper::LoadSSE2Smis(masm, ebx);
+ switch (op) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0);
+ } else { // SSE2 not available, use FPU.
+ FloatingPointHelper::LoadFloatSmis(masm, ebx);
+ switch (op) {
+ case Token::ADD: __ faddp(1); break;
+ case Token::SUB: __ fsubp(1); break;
+ case Token::MUL: __ fmulp(1); break;
+ case Token::DIV: __ fdivp(1); break;
+ default: UNREACHABLE();
+ }
+ __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset));
+ }
+ __ mov(eax, ecx);
+ __ ret(0);
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ // 7. Non-smi operands, fall out to the non-smi code with the operands in
+ // edx and eax.
+ Comment done_comment(masm, "-- Enter non-smi code");
+ __ bind(&not_smis);
+ switch (op) {
+ case Token::BIT_OR:
+ case Token::SHL:
+ case Token::SAR:
+ case Token::SHR:
+ // Right operand is saved in ecx and eax was destroyed by the smi
+ // check.
+ __ mov(eax, ecx);
+ break;
+
+ case Token::DIV:
+ case Token::MOD:
+ // Operands are in eax, ebx at this point.
+ __ mov(edx, eax);
+ __ mov(eax, ebx);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ Label right_arg_changed, call_runtime;
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ break;
+ case Token::MOD:
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ GenerateRegisterArgsPush(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (op_ == Token::MOD && encoded_right_arg_.has_value) {
+ // It is guaranteed that the value will fit into a Smi, because if it
+ // didn't, we wouldn't be here, see BinaryOp_Patch.
+ __ cmp(eax, Immediate(Smi::FromInt(fixed_right_arg_value())));
+ __ j(not_equal, &right_arg_changed);
+ }
+
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, NO_HEAPNUMBER_RESULTS, op_);
+ } else {
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
+ }
+
+ // Code falls through if the result is not returned as either a smi or heap
+ // number.
+ __ bind(&right_arg_changed);
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ GenerateTypeTransition(masm);
+ break;
+ case Token::MOD:
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ GenerateTypeTransitionWithSavedArgs(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ __ bind(&call_runtime);
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ break;
+ case Token::MOD:
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ BinaryOpStub_GenerateRegisterArgsPop(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(edx);
+ __ push(eax);
+ GenerateCallRuntime(masm);
+ }
+ __ ret(0);
+}
+
+
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime, Label::kNear);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
+ __ j(above_equal, &call_runtime, Label::kNear);
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime, Label::kNear);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
+ __ j(above_equal, &call_runtime, Label::kNear);
+
+ StringAddStub string_add_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
+static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
+ Label* alloc_failure,
+ OverwriteMode mode);
+
+
+// Input:
+// edx: left operand (tagged)
+// eax: right operand (tagged)
+// Output:
+// eax: result (tagged)
+void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(Max(left_type_, right_type_) == BinaryOpIC::INT32);
+
+ // Floating point case.
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD: {
+ Label not_floats, not_int32, right_arg_changed;
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ // It could be that only SMIs have been seen at either the left
+ // or the right operand. For precise type feedback, patch the IC
+ // again if this changes.
+ // In theory, we would need the same check in the non-SSE2 case,
+ // but since we don't support Crankshaft on such hardware we can
+ // afford not to care about precise type feedback.
+ if (left_type_ == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(edx, &not_int32);
+ }
+ if (right_type_ == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(eax, &not_int32);
+ }
+ FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
+ FloatingPointHelper::CheckSSE2OperandIsInt32(
+ masm, &not_int32, xmm0, ebx, ecx, xmm2);
+ FloatingPointHelper::CheckSSE2OperandIsInt32(
+ masm, &not_int32, xmm1, edi, ecx, xmm2);
+ if (op_ == Token::MOD) {
+ if (encoded_right_arg_.has_value) {
+ __ cmp(edi, Immediate(fixed_right_arg_value()));
+ __ j(not_equal, &right_arg_changed);
+ }
+ GenerateRegisterArgsPush(masm);
+ __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
+ } else {
+ switch (op_) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ // Check result type if it is currently Int32.
+ if (result_type_ <= BinaryOpIC::INT32) {
+ FloatingPointHelper::CheckSSE2OperandIsInt32(
+ masm, &not_int32, xmm0, ecx, ecx, xmm2);
+ }
+ BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ __ ret(0);
+ }
+ } else { // SSE2 not available, use FPU.
+ FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
+ FloatingPointHelper::LoadFloatOperands(
+ masm,
+ ecx,
+ FloatingPointHelper::ARGS_IN_REGISTERS);
+ if (op_ == Token::MOD) {
+ // The operands are now on the FPU stack, but we don't need them.
+ __ fstp(0);
+ __ fstp(0);
+ GenerateRegisterArgsPush(masm);
+ __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
+ } else {
+ switch (op_) {
+ case Token::ADD: __ faddp(1); break;
+ case Token::SUB: __ fsubp(1); break;
+ case Token::MUL: __ fmulp(1); break;
+ case Token::DIV: __ fdivp(1); break;
+ default: UNREACHABLE();
+ }
+ Label after_alloc_failure;
+ BinaryOpStub_GenerateHeapResultAllocation(
+ masm, &after_alloc_failure, mode_);
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ ret(0);
+ __ bind(&after_alloc_failure);
+ __ fstp(0); // Pop FPU stack before calling runtime.
+ __ jmp(&call_runtime);
+ }
+ }
+
+ __ bind(&not_floats);
+ __ bind(&not_int32);
+ __ bind(&right_arg_changed);
+ GenerateTypeTransition(masm);
+ break;
+ }
+
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR: {
+ GenerateRegisterArgsPush(masm);
+ Label not_floats;
+ Label not_int32;
+ Label non_smi_result;
+ bool use_sse3 = platform_specific_bit_;
+ FloatingPointHelper::LoadUnknownsAsIntegers(
+ masm, use_sse3, left_type_, right_type_, &not_floats);
+ switch (op_) {
+ 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;
+ default: UNREACHABLE();
+ }
+ if (op_ == Token::SHR) {
+ // Check if result is non-negative and fits in a smi.
+ __ test(eax, Immediate(0xc0000000));
+ __ j(not_zero, &call_runtime);
+ } else {
+ // Check if result fits in a smi.
+ __ cmp(eax, 0xc0000000);
+ __ j(negative, &non_smi_result, Label::kNear);
+ }
+ // Tag smi result and return.
+ __ SmiTag(eax);
+ __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
+
+ // All ops except SHR return a signed int32 that we load in
+ // a HeapNumber.
+ if (op_ != Token::SHR) {
+ __ bind(&non_smi_result);
+ // Allocate a heap number if needed.
+ __ mov(ebx, eax); // ebx: result
+ Label skip_allocation;
+ switch (mode_) {
+ case OVERWRITE_LEFT:
+ case OVERWRITE_RIGHT:
+ // If the operand was an object, we skip the
+ // allocation of a heap number.
+ __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
+ 1 * kPointerSize : 2 * kPointerSize));
+ __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
+ // Fall through!
+ case NO_OVERWRITE:
+ __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
+ __ bind(&skip_allocation);
+ break;
+ default: UNREACHABLE();
+ }
+ // Store the result in the HeapNumber and return.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ cvtsi2sd(xmm0, ebx);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ mov(Operand(esp, 1 * kPointerSize), ebx);
+ __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ }
+ __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
+ }
+
+ __ bind(&not_floats);
+ __ bind(&not_int32);
+ GenerateTypeTransitionWithSavedArgs(masm);
+ break;
+ }
+ default: UNREACHABLE(); break;
+ }
+
+ // If an allocation fails, or SHR hits a hard case, use the runtime system to
+ // get the correct result.
+ __ bind(&call_runtime);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ break;
+ case Token::MOD:
+ return; // Handled above.
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ BinaryOpStub_GenerateRegisterArgsPop(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(edx);
+ __ push(eax);
+ GenerateCallRuntime(masm);
+ }
+ __ ret(0);
+}
+
+
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
+ if (op_ == Token::ADD) {
+ // Handle string addition here, because it is the only operation
+ // that does not do a ToNumber conversion on the operands.
+ GenerateAddStrings(masm);
+ }
+
+ Factory* factory = masm->isolate()->factory();
+
+ // Convert odd ball arguments to numbers.
+ Label check, done;
+ __ cmp(edx, factory->undefined_value());
+ __ j(not_equal, &check, Label::kNear);
+ if (Token::IsBitOp(op_)) {
+ __ xor_(edx, edx);
+ } else {
+ __ mov(edx, Immediate(factory->nan_value()));
+ }
+ __ jmp(&done, Label::kNear);
+ __ bind(&check);
+ __ cmp(eax, factory->undefined_value());
+ __ j(not_equal, &done, Label::kNear);
+ if (Token::IsBitOp(op_)) {
+ __ xor_(eax, eax);
+ } else {
+ __ mov(eax, Immediate(factory->nan_value()));
+ }
+ __ bind(&done);
+
+ GenerateNumberStub(masm);
+}
+
+
+void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
+ Label call_runtime;
+
+ // Floating point case.
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV: {
+ Label not_floats;
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+
+ // It could be that only SMIs have been seen at either the left
+ // or the right operand. For precise type feedback, patch the IC
+ // again if this changes.
+ // In theory, we would need the same check in the non-SSE2 case,
+ // but since we don't support Crankshaft on such hardware we can
+ // afford not to care about precise type feedback.
+ if (left_type_ == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(edx, &not_floats);
+ }
+ if (right_type_ == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(eax, &not_floats);
+ }
+ FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
+ if (left_type_ == BinaryOpIC::INT32) {
+ FloatingPointHelper::CheckSSE2OperandIsInt32(
+ masm, &not_floats, xmm0, ecx, ecx, xmm2);
+ }
+ if (right_type_ == BinaryOpIC::INT32) {
+ FloatingPointHelper::CheckSSE2OperandIsInt32(
+ masm, &not_floats, xmm1, ecx, ecx, xmm2);
+ }
+
+ switch (op_) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ __ ret(0);
+ } else { // SSE2 not available, use FPU.
+ FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
+ FloatingPointHelper::LoadFloatOperands(
+ masm,
+ ecx,
+ FloatingPointHelper::ARGS_IN_REGISTERS);
+ switch (op_) {
+ case Token::ADD: __ faddp(1); break;
+ case Token::SUB: __ fsubp(1); break;
+ case Token::MUL: __ fmulp(1); break;
+ case Token::DIV: __ fdivp(1); break;
+ default: UNREACHABLE();
+ }
+ Label after_alloc_failure;
+ BinaryOpStub_GenerateHeapResultAllocation(
+ masm, &after_alloc_failure, mode_);
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ ret(0);
+ __ bind(&after_alloc_failure);
+ __ fstp(0); // Pop FPU stack before calling runtime.
+ __ jmp(&call_runtime);
+ }
+
+ __ bind(&not_floats);
+ GenerateTypeTransition(masm);
+ break;
+ }
+
+ case Token::MOD: {
+ // For MOD we go directly to runtime in the non-smi case.
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR: {
+ GenerateRegisterArgsPush(masm);
+ Label not_floats;
+ Label non_smi_result;
+ // We do not check the input arguments here, as any value is
+ // unconditionally truncated to an int32 anyway. To get the
+ // right optimized code, int32 type feedback is just right.
+ bool use_sse3 = platform_specific_bit_;
+ FloatingPointHelper::LoadUnknownsAsIntegers(
+ masm, use_sse3, left_type_, right_type_, &not_floats);
+ switch (op_) {
+ 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;
+ default: UNREACHABLE();
+ }
+ if (op_ == Token::SHR) {
+ // Check if result is non-negative and fits in a smi.
+ __ test(eax, Immediate(0xc0000000));
+ __ j(not_zero, &call_runtime);
+ } else {
+ // Check if result fits in a smi.
+ __ cmp(eax, 0xc0000000);
+ __ j(negative, &non_smi_result, Label::kNear);
+ }
+ // Tag smi result and return.
+ __ SmiTag(eax);
+ __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
+
+ // All ops except SHR return a signed int32 that we load in
+ // a HeapNumber.
+ if (op_ != Token::SHR) {
+ __ bind(&non_smi_result);
+ // Allocate a heap number if needed.
+ __ mov(ebx, eax); // ebx: result
+ Label skip_allocation;
+ switch (mode_) {
+ case OVERWRITE_LEFT:
+ case OVERWRITE_RIGHT:
+ // If the operand was an object, we skip the
+ // allocation of a heap number.
+ __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
+ 1 * kPointerSize : 2 * kPointerSize));
+ __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
+ // Fall through!
+ case NO_OVERWRITE:
+ __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
+ __ bind(&skip_allocation);
+ break;
+ default: UNREACHABLE();
+ }
+ // Store the result in the HeapNumber and return.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ cvtsi2sd(xmm0, ebx);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ mov(Operand(esp, 1 * kPointerSize), ebx);
+ __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ }
+ __ ret(2 * kPointerSize); // Drop two pushed arguments from the stack.
+ }
+
+ __ bind(&not_floats);
+ GenerateTypeTransitionWithSavedArgs(masm);
+ break;
+ }
+ default: UNREACHABLE(); break;
+ }
+
+ // If an allocation fails, or SHR or MOD hit a hard case,
+ // use the runtime system to get the correct result.
+ __ bind(&call_runtime);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ break;
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ BinaryOpStub_GenerateRegisterArgsPop(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(edx);
+ __ push(eax);
+ GenerateCallRuntime(masm);
+ }
+ __ ret(0);
+}
+
+
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+ Label call_runtime;
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->generic_binary_stub_calls(), 1);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ break;
+ case Token::MOD:
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ GenerateRegisterArgsPush(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
+
+ // Floating point case.
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV: {
+ Label not_floats;
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ FloatingPointHelper::LoadSSE2Operands(masm, &not_floats);
+
+ switch (op_) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ BinaryOpStub_GenerateHeapResultAllocation(masm, &call_runtime, mode_);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ __ ret(0);
+ } else { // SSE2 not available, use FPU.
+ FloatingPointHelper::CheckFloatOperands(masm, &not_floats, ebx);
+ FloatingPointHelper::LoadFloatOperands(
+ masm,
+ ecx,
+ FloatingPointHelper::ARGS_IN_REGISTERS);
+ switch (op_) {
+ case Token::ADD: __ faddp(1); break;
+ case Token::SUB: __ fsubp(1); break;
+ case Token::MUL: __ fmulp(1); break;
+ case Token::DIV: __ fdivp(1); break;
+ default: UNREACHABLE();
+ }
+ Label after_alloc_failure;
+ BinaryOpStub_GenerateHeapResultAllocation(
+ masm, &after_alloc_failure, mode_);
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ ret(0);
+ __ bind(&after_alloc_failure);
+ __ fstp(0); // Pop FPU stack before calling runtime.
+ __ jmp(&call_runtime);
+ }
+ __ bind(&not_floats);
+ break;
+ }
+ case Token::MOD: {
+ // For MOD we go directly to runtime in the non-smi case.
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR: {
+ Label non_smi_result;
+ bool use_sse3 = platform_specific_bit_;
+ FloatingPointHelper::LoadUnknownsAsIntegers(masm,
+ use_sse3,
+ BinaryOpIC::GENERIC,
+ BinaryOpIC::GENERIC,
+ &call_runtime);
+ switch (op_) {
+ 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;
+ default: UNREACHABLE();
+ }
+ if (op_ == Token::SHR) {
+ // Check if result is non-negative and fits in a smi.
+ __ test(eax, Immediate(0xc0000000));
+ __ j(not_zero, &call_runtime);
+ } else {
+ // Check if result fits in a smi.
+ __ cmp(eax, 0xc0000000);
+ __ j(negative, &non_smi_result, Label::kNear);
+ }
+ // Tag smi result and return.
+ __ SmiTag(eax);
+ __ ret(2 * kPointerSize); // Drop the arguments from the stack.
+
+ // All ops except SHR return a signed int32 that we load in
+ // a HeapNumber.
+ if (op_ != Token::SHR) {
+ __ bind(&non_smi_result);
+ // Allocate a heap number if needed.
+ __ mov(ebx, eax); // ebx: result
+ Label skip_allocation;
+ switch (mode_) {
+ case OVERWRITE_LEFT:
+ case OVERWRITE_RIGHT:
+ // If the operand was an object, we skip the
+ // allocation of a heap number.
+ __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
+ 1 * kPointerSize : 2 * kPointerSize));
+ __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
+ // Fall through!
+ case NO_OVERWRITE:
+ __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
+ __ bind(&skip_allocation);
+ break;
+ default: UNREACHABLE();
+ }
+ // Store the result in the HeapNumber and return.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ cvtsi2sd(xmm0, ebx);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ mov(Operand(esp, 1 * kPointerSize), ebx);
+ __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ }
+ __ ret(2 * kPointerSize);
+ }
+ break;
+ }
+ default: UNREACHABLE(); break;
+ }
+
+ // If all else fails, use the runtime system to get the correct
+ // result.
+ __ bind(&call_runtime);
+ switch (op_) {
+ case Token::ADD:
+ GenerateAddStrings(masm);
+ // Fall through.
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ break;
+ case Token::MOD:
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR:
+ BinaryOpStub_GenerateRegisterArgsPop(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(edx);
+ __ push(eax);
+ GenerateCallRuntime(masm);
+ }
+ __ ret(0);
+}
+
+
+void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
+ ASSERT(op_ == Token::ADD);
+ Label left_not_string, call_runtime;
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &left_not_string, Label::kNear);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
+ __ j(above_equal, &left_not_string, Label::kNear);
+
+ StringAddStub string_add_left_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_left_stub);
+
+ // Left operand is not a string, test right.
+ __ bind(&left_not_string);
+ __ JumpIfSmi(right, &call_runtime, Label::kNear);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
+ __ j(above_equal, &call_runtime, Label::kNear);
+
+ StringAddStub string_add_right_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_right_stub);
+
+ // Neither argument is a string.
+ __ bind(&call_runtime);
+}
+
+
+static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
+ Label* alloc_failure,
+ OverwriteMode mode) {
+ Label skip_allocation;
+ switch (mode) {
+ case OVERWRITE_LEFT: {
+ // If the argument in edx is already an object, we skip the
+ // allocation of a heap number.
+ __ JumpIfNotSmi(edx, &skip_allocation, Label::kNear);
+ // Allocate a heap number for the result. Keep eax and edx intact
+ // for the possible runtime call.
+ __ 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, ebx);
+ __ bind(&skip_allocation);
+ // Use object in edx as a result holder
+ __ mov(eax, edx);
+ break;
+ }
+ case OVERWRITE_RIGHT:
+ // If the argument in eax is already an object, we skip the
+ // allocation of a heap number.
+ __ JumpIfNotSmi(eax, &skip_allocation, Label::kNear);
+ // Fall through!
+ case NO_OVERWRITE:
+ // Allocate a heap number for the result. Keep eax and edx intact
+ // for the possible runtime call.
+ __ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
+ // Now eax can be overwritten losing one of the arguments as we are
+ // now done and will not need it any more.
+ __ mov(eax, ebx);
+ __ bind(&skip_allocation);
+ break;
+ default: UNREACHABLE();
+ }
+}
+
+
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ pop(ecx);
+ __ push(edx);
+ __ push(eax);
+ __ push(ecx);
+}
+
+
+void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
+ // TAGGED case:
+ // Input:
+ // esp[4]: tagged number input argument (should be number).
+ // esp[0]: return address.
+ // Output:
+ // eax: tagged double result.
+ // UNTAGGED case:
+ // Input::
+ // esp[0]: return address.
+ // xmm1: untagged double input argument
+ // Output:
+ // xmm1: untagged double result.
+
+ Label runtime_call;
+ Label runtime_call_clear_stack;
+ Label skip_cache;
+ const bool tagged = (argument_type_ == TAGGED);
+ if (tagged) {
+ // Test that eax is a number.
+ Label input_not_smi;
+ Label loaded;
+ __ mov(eax, Operand(esp, kPointerSize));
+ __ JumpIfNotSmi(eax, &input_not_smi, Label::kNear);
+ // Input is a smi. Untag and load it onto the FPU stack.
+ // Then load the low and high words of the double into ebx, edx.
+ STATIC_ASSERT(kSmiTagSize == 1);
+ __ sar(eax, 1);
+ __ sub(esp, Immediate(2 * kPointerSize));
+ __ mov(Operand(esp, 0), eax);
+ __ fild_s(Operand(esp, 0));
+ __ fst_d(Operand(esp, 0));
+ __ pop(edx);
+ __ pop(ebx);
+ __ jmp(&loaded, Label::kNear);
+ __ bind(&input_not_smi);
+ // Check if input is a HeapNumber.
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ Factory* factory = masm->isolate()->factory();
+ __ 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.
+ __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
+ __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
+
+ __ bind(&loaded);
+ } else { // UNTAGGED.
+ CpuFeatureScope scope(masm, SSE2);
+ if (CpuFeatures::IsSupported(SSE4_1)) {
+ CpuFeatureScope sse4_scope(masm, SSE4_1);
+ __ pextrd(edx, xmm1, 0x1); // copy xmm1[63..32] to edx.
+ } else {
+ __ pshufd(xmm0, xmm1, 0x1);
+ __ movd(edx, xmm0);
+ }
+ __ movd(ebx, xmm1);
+ }
+
+ // ST[0] or xmm1 == double value
+ // ebx = low 32 bits of double value
+ // edx = high 32 bits of double value
+ // Compute hash (the shifts are arithmetic):
+ // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
+ __ mov(ecx, ebx);
+ __ xor_(ecx, edx);
+ __ mov(eax, ecx);
+ __ sar(eax, 16);
+ __ xor_(ecx, eax);
+ __ mov(eax, ecx);
+ __ sar(eax, 8);
+ __ xor_(ecx, eax);
+ ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
+ __ and_(ecx,
+ Immediate(TranscendentalCache::SubCache::kCacheSize - 1));
+
+ // ST[0] or xmm1 == double value.
+ // ebx = low 32 bits of double value.
+ // edx = high 32 bits of double value.
+ // ecx = TranscendentalCache::hash(double value).
+ ExternalReference cache_array =
+ ExternalReference::transcendental_cache_array_address(masm->isolate());
+ __ mov(eax, Immediate(cache_array));
+ int cache_array_index =
+ type_ * sizeof(masm->isolate()->transcendental_cache()->caches_[0]);
+ __ 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, eax);
+ __ j(zero, &runtime_call_clear_stack);
+#ifdef DEBUG
+ // Check that the layout of cache elements match expectations.
+ { TranscendentalCache::SubCache::Element test_elem[2];
+ char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
+ char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
+ char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
+ char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
+ char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
+ CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
+ CHECK_EQ(0, elem_in0 - elem_start);
+ CHECK_EQ(kIntSize, elem_in1 - elem_start);
+ CHECK_EQ(2 * kIntSize, elem_out - elem_start);
+ }
+#endif
+ // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12].
+ __ lea(ecx, Operand(ecx, ecx, times_2, 0));
+ __ lea(ecx, Operand(eax, ecx, times_4, 0));
+ // Check if cache matches: Double value is stored in uint32_t[2] array.
+ Label cache_miss;
+ __ cmp(ebx, Operand(ecx, 0));
+ __ j(not_equal, &cache_miss, Label::kNear);
+ __ 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);
+ __ ret(kPointerSize);
+ } else { // UNTAGGED.
+ CpuFeatureScope scope(masm, SSE2);
+ __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
+ __ Ret();
+ }
+
+ __ 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.
+ if (tagged) {
+ __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
+ } else { // UNTAGGED.
+ CpuFeatureScope scope(masm, SSE2);
+ __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
+ __ sub(esp, Immediate(kDoubleSize));
+ __ movdbl(Operand(esp, 0), xmm1);
+ __ fld_d(Operand(esp, 0));
+ __ add(esp, Immediate(kDoubleSize));
+ }
+ GenerateOperation(masm, type_);
+ __ mov(Operand(ecx, 0), ebx);
+ __ mov(Operand(ecx, kIntSize), edx);
+ __ mov(Operand(ecx, 2 * kIntSize), eax);
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ if (tagged) {
+ __ ret(kPointerSize);
+ } else { // UNTAGGED.
+ CpuFeatureScope scope(masm, SSE2);
+ __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
+ __ Ret();
+
+ // Skip cache and return answer directly, only in untagged case.
+ __ bind(&skip_cache);
+ __ sub(esp, Immediate(kDoubleSize));
+ __ movdbl(Operand(esp, 0), xmm1);
+ __ fld_d(Operand(esp, 0));
+ GenerateOperation(masm, type_);
+ __ fstp_d(Operand(esp, 0));
+ __ movdbl(xmm1, Operand(esp, 0));
+ __ 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.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ // Allocate an unused object bigger than a HeapNumber.
+ __ push(Immediate(Smi::FromInt(2 * kDoubleSize)));
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
+ }
+ __ Ret();
+ }
+
+ // Call runtime, doing whatever allocation and cleanup is necessary.
+ if (tagged) {
+ __ bind(&runtime_call_clear_stack);
+ __ fstp(0);
+ __ bind(&runtime_call);
+ ExternalReference runtime =
+ ExternalReference(RuntimeFunction(), masm->isolate());
+ __ TailCallExternalReference(runtime, 1, 1);
+ } else { // UNTAGGED.
+ CpuFeatureScope scope(masm, SSE2);
+ __ bind(&runtime_call_clear_stack);
+ __ bind(&runtime_call);
+ __ AllocateHeapNumber(eax, edi, no_reg, &skip_cache);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm1);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(eax);
+ __ CallRuntime(RuntimeFunction(), 1);
+ }
+ __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
+ __ Ret();
+ }
+}
+
+
+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();
+ return Runtime::kAbort;
+ }
+}
+
+
+void TranscendentalCacheStub::GenerateOperation(
+ MacroAssembler* masm, TranscendentalCache::Type type) {
+ // Only free register is edi.
+ // 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 ||
+ 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.
+ Label in_range, done;
+ // 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_(edi, Immediate(0x7ff00000)); // Exponent only.
+ int supported_exponent_limit =
+ (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
+ __ cmp(edi, Immediate(supported_exponent_limit));
+ __ j(below, &in_range, Label::kNear);
+ // Check for infinity and NaN. Both return NaN for sin.
+ __ cmp(edi, Immediate(0x7ff00000));
+ Label non_nan_result;
+ __ j(not_equal, &non_nan_result, Label::kNear);
+ // Input is +/-Infinity or NaN. Result is NaN.
+ __ fstp(0);
+ // NaN is represented by 0x7ff8000000000000.
+ __ push(Immediate(0x7ff80000));
+ __ push(Immediate(0));
+ __ fld_d(Operand(esp, 0));
+ __ add(esp, Immediate(2 * kPointerSize));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&non_nan_result);
+
+ // Use fpmod to restrict argument to the range +/-2*PI.
+ __ mov(edi, eax); // Save eax before using fnstsw_ax.
+ __ fldpi();
+ __ fadd(0);
+ __ fld(1);
+ // FPU Stack: input, 2*pi, input.
+ {
+ Label no_exceptions;
+ __ fwait();
+ __ fnstsw_ax();
+ // Clear if Illegal Operand or Zero Division exceptions are set.
+ __ test(eax, Immediate(5));
+ __ j(zero, &no_exceptions, Label::kNear);
+ __ fnclex();
+ __ bind(&no_exceptions);
+ }
+
+ // Compute st(0) % st(1)
+ {
+ Label partial_remainder_loop;
+ __ bind(&partial_remainder_loop);
+ __ fprem1();
+ __ fwait();
+ __ fnstsw_ax();
+ __ test(eax, Immediate(0x400 /* C2 */));
+ // If C2 is set, computation only has partial result. Loop to
+ // continue computation.
+ __ j(not_zero, &partial_remainder_loop);
+ }
+ // FPU Stack: input, 2*pi, input % 2*pi
+ __ fstp(2);
+ __ fstp(0);
+ __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
+
+ // FPU Stack: input % 2*pi
+ __ bind(&in_range);
+ switch (type) {
+ case TranscendentalCache::SIN:
+ __ fsin();
+ break;
+ 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();
+ }
+ __ bind(&done);
+ } else {
+ ASSERT(type == TranscendentalCache::LOG);
+ __ fldln2();
+ __ fxch();
+ __ fyl2x();
+ }
+}
+
+
+// Input: edx, eax are the left and right objects of a bit op.
+// Output: eax, ecx are left and right integers for a bit op.
+// Warning: can clobber inputs even when it jumps to |conversion_failure|!
+void FloatingPointHelper::LoadUnknownsAsIntegers(
+ MacroAssembler* masm,
+ bool use_sse3,
+ BinaryOpIC::TypeInfo left_type,
+ BinaryOpIC::TypeInfo right_type,
+ Label* conversion_failure) {
+ // Check float operands.
+ Label arg1_is_object, check_undefined_arg1;
+ Label arg2_is_object, check_undefined_arg2;
+ Label load_arg2, done;
+
+ // Test if arg1 is a Smi.
+ if (left_type == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(edx, conversion_failure);
+ } else {
+ __ JumpIfNotSmi(edx, &arg1_is_object, Label::kNear);
+ }
+
+ __ SmiUntag(edx);
+ __ jmp(&load_arg2);
+
+ // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
+ __ bind(&check_undefined_arg1);
+ Factory* factory = masm->isolate()->factory();
+ __ cmp(edx, factory->undefined_value());
+ __ j(not_equal, conversion_failure);
+ __ mov(edx, Immediate(0));
+ __ jmp(&load_arg2);
+
+ __ bind(&arg1_is_object);
+ __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ cmp(ebx, factory->heap_number_map());
+ __ j(not_equal, &check_undefined_arg1);
+
+ // Get the untagged integer version of the edx heap number in ecx.
+ if (left_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ ConvertHeapNumberToInt32(masm, edx, conversion_failure);
+ } else {
+ DoubleToIStub stub(edx, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
+ true);
+ __ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+ }
+ __ mov(edx, ecx);
+
+ // Here edx has the untagged integer, eax has a Smi or a heap number.
+ __ bind(&load_arg2);
+
+ // Test if arg2 is a Smi.
+ if (right_type == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(eax, conversion_failure);
+ } else {
+ __ JumpIfNotSmi(eax, &arg2_is_object, Label::kNear);
+ }
+
+ __ SmiUntag(eax);
+ __ mov(ecx, eax);
+ __ jmp(&done);
+
+ // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
+ __ bind(&check_undefined_arg2);
+ __ cmp(eax, factory->undefined_value());
+ __ j(not_equal, conversion_failure);
+ __ mov(ecx, Immediate(0));
+ __ jmp(&done);
+
+ __ bind(&arg2_is_object);
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ cmp(ebx, factory->heap_number_map());
+ __ j(not_equal, &check_undefined_arg2);
+ // Get the untagged integer version of the eax heap number in ecx.
+
+ if (right_type == BinaryOpIC::INT32 && CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ ConvertHeapNumberToInt32(masm, eax, conversion_failure);
+ } else {
+ DoubleToIStub stub(eax, ecx, HeapNumber::kValueOffset - kHeapObjectTag,
+ true);
+ __ call(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+ }
+
+ __ bind(&done);
+ __ mov(eax, edx);
+}
+
+
+void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
+ Register number) {
+ Label load_smi, done;
+
+ __ JumpIfSmi(number, &load_smi, Label::kNear);
+ __ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&load_smi);
+ __ SmiUntag(number);
+ __ push(number);
+ __ fild_s(Operand(esp, 0));
+ __ pop(number);
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
+ Label load_smi_edx, load_eax, load_smi_eax, done;
+ // Load operand in edx into xmm0.
+ __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
+ __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
+
+ __ bind(&load_eax);
+ // Load operand in eax into xmm1.
+ __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
+ __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&load_smi_edx);
+ __ SmiUntag(edx); // Untag smi before converting to float.
+ __ 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, eax);
+ __ SmiTag(eax); // Retag smi for heap number overwriting test.
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
+ Label* not_numbers) {
+ Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
+ // Load operand in edx into xmm0, or branch to not_numbers.
+ __ JumpIfSmi(edx, &load_smi_edx, Label::kNear);
+ Factory* factory = masm->isolate()->factory();
+ __ cmp(FieldOperand(edx, HeapObject::kMapOffset), factory->heap_number_map());
+ __ j(not_equal, not_numbers); // Argument in edx is not a number.
+ __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
+ __ bind(&load_eax);
+ // Load operand in eax into xmm1, or branch to not_numbers.
+ __ JumpIfSmi(eax, &load_smi_eax, Label::kNear);
+ __ cmp(FieldOperand(eax, HeapObject::kMapOffset), factory->heap_number_map());
+ __ j(equal, &load_float_eax, Label::kNear);
+ __ jmp(not_numbers); // Argument in eax is not a number.
+ __ bind(&load_smi_edx);
+ __ SmiUntag(edx); // Untag smi before converting to float.
+ __ 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, eax);
+ __ SmiTag(eax); // Retag smi for heap number overwriting test.
+ __ jmp(&done, Label::kNear);
+ __ bind(&load_float_eax);
+ __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm,
+ Register scratch) {
+ const Register left = edx;
+ const Register right = eax;
+ __ mov(scratch, left);
+ ASSERT(!scratch.is(right)); // We're about to clobber scratch.
+ __ SmiUntag(scratch);
+ __ cvtsi2sd(xmm0, scratch);
+
+ __ mov(scratch, right);
+ __ SmiUntag(scratch);
+ __ cvtsi2sd(xmm1, scratch);
+}
+
+
+void FloatingPointHelper::CheckSSE2OperandIsInt32(MacroAssembler* masm,
+ Label* non_int32,
+ XMMRegister operand,
+ Register int32_result,
+ Register scratch,
+ XMMRegister xmm_scratch) {
+ __ cvttsd2si(int32_result, Operand(operand));
+ __ cvtsi2sd(xmm_scratch, int32_result);
+ __ pcmpeqd(xmm_scratch, operand);
+ __ movmskps(scratch, xmm_scratch);
+ // Two least significant bits should be both set.
+ __ not_(scratch);
+ __ test(scratch, Immediate(3));
+ __ j(not_zero, non_int32);
+}
+
+
+void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
+ Register scratch,
+ ArgLocation arg_location) {
+ Label load_smi_1, load_smi_2, done_load_1, done;
+ if (arg_location == ARGS_IN_REGISTERS) {
+ __ mov(scratch, edx);
+ } else {
+ __ mov(scratch, Operand(esp, 2 * kPointerSize));
+ }
+ __ JumpIfSmi(scratch, &load_smi_1, Label::kNear);
+ __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
+ __ bind(&done_load_1);
+
+ if (arg_location == ARGS_IN_REGISTERS) {
+ __ mov(scratch, eax);
+ } else {
+ __ mov(scratch, Operand(esp, 1 * kPointerSize));
+ }
+ __ JumpIfSmi(scratch, &load_smi_2, Label::kNear);
+ __ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&load_smi_1);
+ __ SmiUntag(scratch);
+ __ push(scratch);
+ __ fild_s(Operand(esp, 0));
+ __ pop(scratch);
+ __ jmp(&done_load_1);
+
+ __ bind(&load_smi_2);
+ __ SmiUntag(scratch);
+ __ push(scratch);
+ __ fild_s(Operand(esp, 0));
+ __ pop(scratch);
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm,
+ Register scratch) {
+ const Register left = edx;
+ const Register right = eax;
+ __ mov(scratch, left);
+ ASSERT(!scratch.is(right)); // We're about to clobber scratch.
+ __ SmiUntag(scratch);
+ __ push(scratch);
+ __ fild_s(Operand(esp, 0));
+
+ __ mov(scratch, right);
+ __ SmiUntag(scratch);
+ __ mov(Operand(esp, 0), scratch);
+ __ fild_s(Operand(esp, 0));
+ __ pop(scratch);
+}
+
+
+void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
+ Label* non_float,
+ Register scratch) {
+ Label test_other, done;
+ // Test if both operands are floats or smi -> scratch=k_is_float;
+ // Otherwise scratch = k_not_float.
+ __ JumpIfSmi(edx, &test_other, Label::kNear);
+ __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
+ Factory* factory = masm->isolate()->factory();
+ __ cmp(scratch, factory->heap_number_map());
+ __ j(not_equal, non_float); // argument in edx is not a number -> NaN
+
+ __ bind(&test_other);
+ __ JumpIfSmi(eax, &done, Label::kNear);
+ __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
+ __ cmp(scratch, factory->heap_number_map());
+ __ j(not_equal, non_float); // argument in eax is not a number -> NaN
+
+ // Fall-through: Both operands are numbers.
+ __ bind(&done);
+}
+
+
+void MathPowStub::Generate(MacroAssembler* masm) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ Factory* factory = masm->isolate()->factory();
+ 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));
+ }
+
+ 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);
+ }
+
+ // 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); // 2^(X-rnd(X)), rnd(X)
+ // FSCALE calculates st(0) * 2^st(1)
+ __ fscale(); // 2^X, rnd(X)
+ __ fstp(1); // 2^X
+ // 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);
+ }
+
+ // 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, while_true, while_false;
+ __ test(scratch, scratch);
+ __ j(positive, &no_neg, Label::kNear);
+ __ neg(scratch);
+ __ bind(&no_neg);
+
+ __ j(zero, &while_false, Label::kNear);
+ __ shr(scratch, 1);
+ // Above condition means CF==0 && ZF==0. This means that the
+ // bit that has been shifted out is 0 and the result is not 0.
+ __ j(above, &while_true, Label::kNear);
+ __ movsd(double_result, double_scratch);
+ __ j(zero, &while_false, Label::kNear);
+
+ __ bind(&while_true);
+ __ shr(scratch, 1);
+ __ mulsd(double_scratch, double_scratch);
+ __ j(above, &while_true, Label::kNear);
+ __ mulsd(double_result, double_scratch);
+ __ j(not_zero, &while_true);
+
+ __ bind(&while_false);
+ // 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);
+
+ // 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(&done);
+ __ IncrementCounter(counters->math_pow(), 1);
+ __ ret(0);
+ }
+}
+
+
+void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ if (kind() == Code::KEYED_LOAD_IC) {
+ __ cmp(ecx, Immediate(masm->isolate()->factory()->prototype_string()));
+ __ j(not_equal, &miss);
+ }
+
+ StubCompiler::GenerateLoadFunctionPrototype(masm, edx, eax, ebx, &miss);
+ __ bind(&miss);
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void StringLengthStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ if (kind() == Code::KEYED_LOAD_IC) {
+ __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
+ __ j(not_equal, &miss);
+ }
+
+ StubCompiler::GenerateLoadStringLength(masm, edx, eax, ebx, &miss,
+ support_wrapper_);
+ __ bind(&miss);
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void StoreArrayLengthStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ //
+ // 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;
+
+ Register receiver = edx;
+ Register value = eax;
+ Register scratch = ebx;
+
+ if (kind() == Code::KEYED_STORE_IC) {
+ __ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
+ __ j(not_equal, &miss);
+ }
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Check that the object is a JS array.
+ __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
+ __ j(not_equal, &miss);
+
+ // Check that elements are FixedArray.
+ // We rely on StoreIC_ArrayLength below to deal with all types of
+ // fast elements (including COW).
+ __ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
+ __ 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);
+
+ // Prepare tail call to StoreIC_ArrayLength.
+ __ pop(scratch);
+ __ push(receiver);
+ __ push(value);
+ __ push(scratch); // return address
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+
+ __ bind(&miss);
+
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+ // The key is in edx and the parameter count is in eax.
+
+ // The displacement is used for skipping the frame pointer on the
+ // stack. It is the offset of the last parameter (if any) relative
+ // to the frame pointer.
+ static const int kDisplacement = 1 * kPointerSize;
+
+ // Check that the key is a smi.
+ Label slow;
+ __ JumpIfNotSmi(edx, &slow, Label::kNear);
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor;
+ __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+ __ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
+ __ 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, eax);
+ __ j(above_equal, &slow, Label::kNear);
+
+ // Read the argument from the stack and return it.
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
+ __ lea(ebx, Operand(ebp, eax, times_2, 0));
+ __ neg(edx);
+ __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
+ __ ret(0);
+
+ // Arguments adaptor case: Check index against actual arguments
+ // limit found in the arguments adaptor frame. Use unsigned
+ // comparison to get negative check for free.
+ __ bind(&adaptor);
+ __ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ cmp(edx, ecx);
+ __ j(above_equal, &slow, Label::kNear);
+
+ // Read the argument from the stack and return it.
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0); // Shifting code depends on these.
+ __ lea(ebx, Operand(ebx, ecx, times_2, 0));
+ __ neg(edx);
+ __ mov(eax, Operand(ebx, edx, times_2, kDisplacement));
+ __ ret(0);
+
+ // Slow-case: Handle non-smi or out-of-bounds access to arguments
+ // by calling the runtime system.
+ __ bind(&slow);
+ __ pop(ebx); // Return address.
+ __ push(edx);
+ __ push(ebx);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
+ // esp[0] : return address
+ // esp[4] : number of parameters
+ // esp[8] : receiver displacement
+ // esp[12] : function
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label runtime;
+ __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+ __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
+ __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ j(not_equal, &runtime, Label::kNear);
+
+ // Patch the arguments.length and the parameters pointer.
+ __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ mov(Operand(esp, 1 * kPointerSize), ecx);
+ __ lea(edx, Operand(edx, ecx, times_2,
+ StandardFrameConstants::kCallerSPOffset));
+ __ mov(Operand(esp, 2 * kPointerSize), edx);
+
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
+ Isolate* isolate = masm->isolate();
+
+ // esp[0] : return address
+ // esp[4] : number of parameters (tagged)
+ // esp[8] : receiver displacement
+ // esp[12] : function
+
+ // ebx = parameter count (tagged)
+ __ mov(ebx, Operand(esp, 1 * kPointerSize));
+
+ // Check if the calling frame is an arguments adaptor frame.
+ // TODO(rossberg): Factor out some of the bits that are shared with the other
+ // Generate* functions.
+ Label runtime;
+ Label adaptor_frame, try_allocate;
+ __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+ __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
+ __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ j(equal, &adaptor_frame, Label::kNear);
+
+ // No adaptor, parameter count = argument count.
+ __ mov(ecx, ebx);
+ __ jmp(&try_allocate, Label::kNear);
+
+ // We have an adaptor frame. Patch the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ lea(edx, Operand(edx, ecx, times_2,
+ StandardFrameConstants::kCallerSPOffset));
+ __ mov(Operand(esp, 2 * kPointerSize), edx);
+
+ // ebx = parameter count (tagged)
+ // ecx = argument count (tagged)
+ // esp[4] = parameter count (tagged)
+ // esp[8] = address of receiver argument
+ // Compute the mapped parameter count = min(ebx, ecx) in ebx.
+ __ cmp(ebx, ecx);
+ __ j(less_equal, &try_allocate, Label::kNear);
+ __ mov(ebx, ecx);
+
+ __ bind(&try_allocate);
+
+ // Save mapped parameter count.
+ __ push(ebx);
+
+ // Compute the sizes of backing store, parameter map, and arguments object.
+ // 1. Parameter map, has 2 extra words containing context and backing store.
+ const int kParameterMapHeaderSize =
+ FixedArray::kHeaderSize + 2 * kPointerSize;
+ Label no_parameter_map;
+ __ test(ebx, ebx);
+ __ j(zero, &no_parameter_map, Label::kNear);
+ __ lea(ebx, Operand(ebx, times_2, kParameterMapHeaderSize));
+ __ bind(&no_parameter_map);
+
+ // 2. Backing store.
+ __ lea(ebx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize));
+
+ // 3. Arguments object.
+ __ add(ebx, Immediate(Heap::kArgumentsObjectSize));
+
+ // Do the allocation of all three objects in one go.
+ __ Allocate(ebx, eax, edx, edi, &runtime, TAG_OBJECT);
+
+ // eax = address of new object(s) (tagged)
+ // ecx = argument count (tagged)
+ // esp[0] = mapped parameter count (tagged)
+ // esp[8] = parameter count (tagged)
+ // esp[12] = address of receiver argument
+ // Get the arguments boilerplate from the current native context into edi.
+ Label has_mapped_parameters, copy;
+ __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
+ __ mov(ebx, Operand(esp, 0 * kPointerSize));
+ __ test(ebx, ebx);
+ __ j(not_zero, &has_mapped_parameters, Label::kNear);
+ __ mov(edi, Operand(edi,
+ Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX)));
+ __ jmp(&copy, Label::kNear);
+
+ __ bind(&has_mapped_parameters);
+ __ mov(edi, Operand(edi,
+ Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX)));
+ __ bind(&copy);
+
+ // eax = address of new object (tagged)
+ // ebx = mapped parameter count (tagged)
+ // ecx = argument count (tagged)
+ // edi = address of boilerplate object (tagged)
+ // esp[0] = mapped parameter count (tagged)
+ // esp[8] = parameter count (tagged)
+ // esp[12] = address of receiver argument
+ // Copy the JS object part.
+ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+ __ mov(edx, FieldOperand(edi, i));
+ __ mov(FieldOperand(eax, i), edx);
+ }
+
+ // Set up the callee in-object property.
+ STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
+ __ mov(edx, Operand(esp, 4 * kPointerSize));
+ __ mov(FieldOperand(eax, JSObject::kHeaderSize +
+ Heap::kArgumentsCalleeIndex * kPointerSize),
+ edx);
+
+ // Use the length (smi tagged) and set that as an in-object property too.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ __ mov(FieldOperand(eax, JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize),
+ ecx);
+
+ // 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));
+ __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
+
+ // eax = address of new object (tagged)
+ // ebx = mapped parameter count (tagged)
+ // ecx = argument count (tagged)
+ // edi = address of parameter map or backing store (tagged)
+ // esp[0] = mapped parameter count (tagged)
+ // esp[8] = parameter count (tagged)
+ // esp[12] = address of receiver argument
+ // Free a register.
+ __ push(eax);
+
+ // Initialize parameter map. If there are no mapped arguments, we're done.
+ Label skip_parameter_map;
+ __ test(ebx, ebx);
+ __ j(zero, &skip_parameter_map);
+
+ __ mov(FieldOperand(edi, FixedArray::kMapOffset),
+ Immediate(isolate->factory()->non_strict_arguments_elements_map()));
+ __ lea(eax, Operand(ebx, reinterpret_cast<intptr_t>(Smi::FromInt(2))));
+ __ mov(FieldOperand(edi, FixedArray::kLengthOffset), eax);
+ __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 0 * kPointerSize), esi);
+ __ lea(eax, Operand(edi, ebx, times_2, kParameterMapHeaderSize));
+ __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 1 * kPointerSize), eax);
+
+ // Copy the parameter slots and the holes in the arguments.
+ // We need to fill in mapped_parameter_count slots. They index the context,
+ // where parameters are stored in reverse order, at
+ // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
+ // The mapped parameter thus need to get indices
+ // MIN_CONTEXT_SLOTS+parameter_count-1 ..
+ // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
+ // We loop from right to left.
+ Label parameters_loop, parameters_test;
+ __ push(ecx);
+ __ mov(eax, Operand(esp, 2 * kPointerSize));
+ __ mov(ebx, Immediate(Smi::FromInt(Context::MIN_CONTEXT_SLOTS)));
+ __ add(ebx, Operand(esp, 4 * kPointerSize));
+ __ sub(ebx, eax);
+ __ mov(ecx, isolate->factory()->the_hole_value());
+ __ mov(edx, edi);
+ __ lea(edi, Operand(edi, eax, times_2, kParameterMapHeaderSize));
+ // eax = loop variable (tagged)
+ // ebx = mapping index (tagged)
+ // ecx = the hole value
+ // edx = address of parameter map (tagged)
+ // edi = address of backing store (tagged)
+ // esp[0] = argument count (tagged)
+ // esp[4] = address of new object (tagged)
+ // esp[8] = mapped parameter count (tagged)
+ // esp[16] = parameter count (tagged)
+ // esp[20] = address of receiver argument
+ __ jmp(&parameters_test, Label::kNear);
+
+ __ bind(&parameters_loop);
+ __ sub(eax, Immediate(Smi::FromInt(1)));
+ __ mov(FieldOperand(edx, eax, times_2, kParameterMapHeaderSize), ebx);
+ __ mov(FieldOperand(edi, eax, times_2, FixedArray::kHeaderSize), ecx);
+ __ add(ebx, Immediate(Smi::FromInt(1)));
+ __ bind(&parameters_test);
+ __ test(eax, eax);
+ __ j(not_zero, &parameters_loop, Label::kNear);
+ __ pop(ecx);
+
+ __ bind(&skip_parameter_map);
+
+ // ecx = argument count (tagged)
+ // edi = address of backing store (tagged)
+ // esp[0] = address of new object (tagged)
+ // esp[4] = mapped parameter count (tagged)
+ // esp[12] = parameter count (tagged)
+ // esp[16] = address of receiver argument
+ // Copy arguments header and remaining slots (if there are any).
+ __ mov(FieldOperand(edi, FixedArray::kMapOffset),
+ Immediate(isolate->factory()->fixed_array_map()));
+ __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
+
+ Label arguments_loop, arguments_test;
+ __ mov(ebx, Operand(esp, 1 * kPointerSize));
+ __ mov(edx, Operand(esp, 4 * kPointerSize));
+ __ sub(edx, ebx); // Is there a smarter way to do negative scaling?
+ __ sub(edx, ebx);
+ __ jmp(&arguments_test, Label::kNear);
+
+ __ bind(&arguments_loop);
+ __ sub(edx, Immediate(kPointerSize));
+ __ mov(eax, Operand(edx, 0));
+ __ mov(FieldOperand(edi, ebx, times_2, FixedArray::kHeaderSize), eax);
+ __ add(ebx, Immediate(Smi::FromInt(1)));
+
+ __ bind(&arguments_test);
+ __ cmp(ebx, ecx);
+ __ j(less, &arguments_loop, Label::kNear);
+
+ // Restore.
+ __ pop(eax); // Address of arguments object.
+ __ pop(ebx); // Parameter count.
+
+ // Return and remove the on-stack parameters.
+ __ ret(3 * kPointerSize);
+
+ // Do the runtime call to allocate the arguments object.
+ __ bind(&runtime);
+ __ pop(eax); // Remove saved parameter count.
+ __ mov(Operand(esp, 1 * kPointerSize), ecx); // Patch argument count.
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
+ Isolate* isolate = masm->isolate();
+
+ // esp[0] : return address
+ // esp[4] : number of parameters
+ // esp[8] : receiver displacement
+ // esp[12] : function
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor_frame, try_allocate, runtime;
+ __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+ __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
+ __ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ j(equal, &adaptor_frame, Label::kNear);
+
+ // Get the length from the frame.
+ __ mov(ecx, Operand(esp, 1 * kPointerSize));
+ __ jmp(&try_allocate, Label::kNear);
+
+ // Patch the arguments.length and the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ mov(Operand(esp, 1 * kPointerSize), ecx);
+ __ lea(edx, Operand(edx, ecx, times_2,
+ StandardFrameConstants::kCallerSPOffset));
+ __ mov(Operand(esp, 2 * kPointerSize), edx);
+
+ // Try the new space allocation. Start out with computing the size of
+ // the arguments object and the elements array.
+ Label add_arguments_object;
+ __ bind(&try_allocate);
+ __ test(ecx, ecx);
+ __ j(zero, &add_arguments_object, Label::kNear);
+ __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
+ __ bind(&add_arguments_object);
+ __ add(ecx, Immediate(Heap::kArgumentsObjectSizeStrict));
+
+ // Do the allocation of both objects in one go.
+ __ Allocate(ecx, eax, edx, ebx, &runtime, TAG_OBJECT);
+
+ // Get the arguments boilerplate from the current native context.
+ __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ mov(edi, FieldOperand(edi, GlobalObject::kNativeContextOffset));
+ const int offset =
+ Context::SlotOffset(Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX);
+ __ mov(edi, Operand(edi, offset));
+
+ // Copy the JS object part.
+ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+ __ mov(ebx, FieldOperand(edi, i));
+ __ mov(FieldOperand(eax, i), ebx);
+ }
+
+ // Get the length (smi tagged) and set that as an in-object property too.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ __ mov(ecx, Operand(esp, 1 * kPointerSize));
+ __ mov(FieldOperand(eax, JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize),
+ ecx);
+
+ // If there are no actual arguments, we're done.
+ Label done;
+ __ test(ecx, ecx);
+ __ j(zero, &done, Label::kNear);
+
+ // Get the parameters pointer from the stack.
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
+
+ // 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);
+ __ mov(FieldOperand(edi, FixedArray::kMapOffset),
+ Immediate(isolate->factory()->fixed_array_map()));
+
+ __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
+ // Untag the length for the loop below.
+ __ SmiUntag(ecx);
+
+ // Copy the fixed array slots.
+ Label loop;
+ __ bind(&loop);
+ __ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
+ __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
+ __ add(edi, Immediate(kPointerSize));
+ __ sub(edx, Immediate(kPointerSize));
+ __ dec(ecx);
+ __ j(not_zero, &loop);
+
+ // Return and remove the on-stack parameters.
+ __ bind(&done);
+ __ ret(3 * kPointerSize);
+
+ // Do the runtime call to allocate the arguments object.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
+}
+
+
+void RegExpExecStub::Generate(MacroAssembler* masm) {
+ // Just jump directly to runtime if native RegExp is not selected at compile
+ // time or if regexp entry in generated code is turned off runtime switch or
+ // at compilation.
+#ifdef V8_INTERPRETED_REGEXP
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+#else // V8_INTERPRETED_REGEXP
+
+ // Stack frame on entry.
+ // esp[0]: return address
+ // esp[4]: last_match_info (expected JSArray)
+ // esp[8]: previous index
+ // esp[12]: subject string
+ // esp[16]: JSRegExp object
+
+ static const int kLastMatchInfoOffset = 1 * kPointerSize;
+ static const int kPreviousIndexOffset = 2 * kPointerSize;
+ static const int kSubjectOffset = 3 * kPointerSize;
+ static const int kJSRegExpOffset = 4 * kPointerSize;
+
+ Label runtime;
+ Factory* factory = masm->isolate()->factory();
+
+ // Ensure that a RegExp stack is allocated.
+ ExternalReference address_of_regexp_stack_memory_address =
+ ExternalReference::address_of_regexp_stack_memory_address(
+ masm->isolate());
+ 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, ebx);
+ __ j(zero, &runtime);
+
+ // Check that the first argument is a JSRegExp object.
+ __ mov(eax, Operand(esp, kJSRegExpOffset));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(eax, &runtime);
+ __ CmpObjectType(eax, JS_REGEXP_TYPE, ecx);
+ __ j(not_equal, &runtime);
+
+ // Check that the RegExp has been compiled (data contains a fixed array).
+ __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
+ if (FLAG_debug_code) {
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ Check(not_zero, kUnexpectedTypeForRegExpDataFixedArrayExpected);
+ __ CmpObjectType(ecx, FIXED_ARRAY_TYPE, ebx);
+ __ Check(equal, kUnexpectedTypeForRegExpDataFixedArrayExpected);
+ }
+
+ // ecx: RegExp data (FixedArray)
+ // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
+ __ mov(ebx, FieldOperand(ecx, JSRegExp::kDataTagOffset));
+ __ cmp(ebx, Immediate(Smi::FromInt(JSRegExp::IRREGEXP)));
+ __ j(not_equal, &runtime);
+
+ // ecx: RegExp data (FixedArray)
+ // Check that the number of captures fit in the static offsets vector buffer.
+ __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
+ // Check (number_of_captures + 1) * 2 <= offsets vector size
+ // Or number_of_captures * 2 <= offsets vector size - 2
+ // Multiplying by 2 comes for free since edx is smi-tagged.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
+ __ cmp(edx, Isolate::kJSRegexpStaticOffsetsVectorSize - 2);
+ __ j(above, &runtime);
+
+ // Reset offset for possibly sliced string.
+ __ Set(edi, Immediate(0));
+ __ mov(eax, Operand(esp, kSubjectOffset));
+ __ JumpIfSmi(eax, &runtime);
+ __ mov(edx, eax); // Make a copy of the original subject string.
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
+
+ // eax: subject string
+ // edx: subject string
+ // ebx: subject string instance type
+ // ecx: RegExp data (FixedArray)
+ // Handle subject string according to its encoding and representation:
+ // (1) Sequential two byte? If yes, go to (9).
+ // (2) Sequential one byte? If yes, go to (6).
+ // (3) Anything but sequential or cons? If yes, go to (7).
+ // (4) Cons string. If the string is flat, replace subject with first string.
+ // Otherwise bailout.
+ // (5a) Is subject sequential two byte? If yes, go to (9).
+ // (5b) Is subject external? If yes, go to (8).
+ // (6) One byte sequential. Load regexp code for one byte.
+ // (E) Carry on.
+ /// [...]
+
+ // Deferred code at the end of the stub:
+ // (7) Not a long external string? If yes, go to (10).
+ // (8) External string. Make it, offset-wise, look like a sequential string.
+ // (8a) Is the external string one byte? If yes, go to (6).
+ // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
+ // (10) Short external string or not a string? If yes, bail out to runtime.
+ // (11) Sliced string. Replace subject with parent. Go to (5a).
+
+ Label seq_one_byte_string /* 6 */, seq_two_byte_string /* 9 */,
+ external_string /* 8 */, check_underlying /* 5a */,
+ not_seq_nor_cons /* 7 */, check_code /* E */,
+ not_long_external /* 10 */;
+
+ // (1) Sequential two byte? If yes, go to (9).
+ __ and_(ebx, kIsNotStringMask |
+ kStringRepresentationMask |
+ kStringEncodingMask |
+ kShortExternalStringMask);
+ STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
+ __ j(zero, &seq_two_byte_string); // Go to (9).
+
+ // (2) Sequential one byte? If yes, go to (6).
+ // Any other sequential string must be one byte.
+ __ and_(ebx, Immediate(kIsNotStringMask |
+ kStringRepresentationMask |
+ kShortExternalStringMask));
+ __ j(zero, &seq_one_byte_string, Label::kNear); // Go to (6).
+
+ // (3) Anything but sequential or cons? If yes, go to (7).
+ // We check whether the subject string is a cons, since sequential strings
+ // have already been covered.
+ STATIC_ASSERT(kConsStringTag < kExternalStringTag);
+ STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
+ STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
+ STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
+ __ cmp(ebx, Immediate(kExternalStringTag));
+ __ j(greater_equal, &not_seq_nor_cons); // Go to (7).
+
+ // (4) Cons string. Check that it's flat.
+ // Replace subject with first string and reload instance type.
+ __ cmp(FieldOperand(eax, ConsString::kSecondOffset), factory->empty_string());
+ __ j(not_equal, &runtime);
+ __ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
+ __ bind(&check_underlying);
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ mov(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
+
+ // (5a) Is subject sequential two byte? If yes, go to (9).
+ __ test_b(ebx, kStringRepresentationMask | kStringEncodingMask);
+ STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
+ __ j(zero, &seq_two_byte_string); // Go to (9).
+ // (5b) Is subject external? If yes, go to (8).
+ __ test_b(ebx, kStringRepresentationMask);
+ // The underlying external string is never a short external string.
+ STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength);
+ STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength);
+ __ j(not_zero, &external_string); // Go to (8).
+
+ // eax: sequential subject string (or look-alike, external string)
+ // edx: original subject string
+ // ecx: RegExp data (FixedArray)
+ // (6) One byte sequential. Load regexp code for one byte.
+ __ bind(&seq_one_byte_string);
+ // Load previous index and check range before edx is overwritten. We have
+ // to use edx instead of eax here because it might have been only made to
+ // look like a sequential string when it actually is an external string.
+ __ mov(ebx, Operand(esp, kPreviousIndexOffset));
+ __ JumpIfNotSmi(ebx, &runtime);
+ __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
+ __ j(above_equal, &runtime);
+ __ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset));
+ __ Set(ecx, Immediate(1)); // Type is one byte.
+
+ // (E) Carry on. String handling is done.
+ __ bind(&check_code);
+ // edx: irregexp code
+ // Check that the irregexp code has been generated for the actual string
+ // encoding. If it has, the field contains a code object otherwise it contains
+ // a smi (code flushing support).
+ __ JumpIfSmi(edx, &runtime);
+
+ // eax: subject string
+ // ebx: previous index (smi)
+ // edx: code
+ // 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);
+
+ // Isolates: note we add an additional parameter here (isolate pointer).
+ static const int kRegExpExecuteArguments = 9;
+ __ EnterApiExitFrame(kRegExpExecuteArguments);
+
+ // Argument 9: Pass current isolate address.
+ __ mov(Operand(esp, 8 * kPointerSize),
+ Immediate(ExternalReference::isolate_address(masm->isolate())));
+
+ // Argument 8: Indicate that this is a direct call from JavaScript.
+ __ mov(Operand(esp, 7 * kPointerSize), Immediate(1));
+
+ // Argument 7: Start (high end) of backtracking stack memory area.
+ __ mov(esi, Operand::StaticVariable(address_of_regexp_stack_memory_address));
+ __ add(esi, Operand::StaticVariable(address_of_regexp_stack_memory_size));
+ __ mov(Operand(esp, 6 * kPointerSize), esi);
+
+ // Argument 6: Set the number of capture registers to zero to force global
+ // regexps to behave as non-global. This does not affect non-global regexps.
+ __ mov(Operand(esp, 5 * kPointerSize), Immediate(0));
+
+ // Argument 5: static offsets vector buffer.
+ __ mov(Operand(esp, 4 * kPointerSize),
+ Immediate(ExternalReference::address_of_static_offsets_vector(
+ masm->isolate())));
+
+ // Argument 2: Previous index.
+ __ SmiUntag(ebx);
+ __ mov(Operand(esp, 1 * kPointerSize), ebx);
+
+ // Argument 1: Original subject string.
+ // The original subject is in the previous stack frame. Therefore we have to
+ // use ebp, which points exactly to one pointer size below the previous esp.
+ // (Because creating a new stack frame pushes the previous ebp onto the stack
+ // and thereby moves up esp by one kPointerSize.)
+ __ mov(esi, Operand(ebp, kSubjectOffset + kPointerSize));
+ __ mov(Operand(esp, 0 * kPointerSize), esi);
+
+ // esi: original subject string
+ // eax: underlying subject string
+ // ebx: previous index
+ // 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, edi); // Calculate input end wrt offset.
+ __ SmiUntag(edi);
+ __ 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, ecx);
+ __ j(zero, &setup_two_byte, Label::kNear);
+ __ SmiUntag(esi);
+ __ lea(ecx, FieldOperand(eax, esi, times_1, SeqOneByteString::kHeaderSize));
+ __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
+ __ lea(ecx, FieldOperand(eax, ebx, times_1, SeqOneByteString::kHeaderSize));
+ __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
+ __ jmp(&setup_rest, Label::kNear);
+
+ __ bind(&setup_two_byte);
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1); // esi is smi (powered by 2).
+ __ lea(ecx, FieldOperand(eax, esi, times_1, SeqTwoByteString::kHeaderSize));
+ __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
+ __ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
+ __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
+
+ __ bind(&setup_rest);
+
+ // Locate the code entry and call it.
+ __ add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ __ call(edx);
+
+ // Drop arguments and come back to JS mode.
+ __ LeaveApiExitFrame();
+
+ // Check the result.
+ Label success;
+ __ cmp(eax, 1);
+ // We expect exactly one result since we force the called regexp to behave
+ // as non-global.
+ __ j(equal, &success);
+ Label failure;
+ __ cmp(eax, NativeRegExpMacroAssembler::FAILURE);
+ __ j(equal, &failure);
+ __ cmp(eax, NativeRegExpMacroAssembler::EXCEPTION);
+ // If not exception it can only be retry. Handle that in the runtime system.
+ __ j(not_equal, &runtime);
+ // Result must now be exception. If there is no pending exception already a
+ // 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.
+ ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
+ masm->isolate());
+ __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
+ __ mov(eax, Operand::StaticVariable(pending_exception));
+ __ cmp(edx, eax);
+ __ j(equal, &runtime);
+ // For exception, throw the exception again.
+
+ // Clear the pending exception variable.
+ __ mov(Operand::StaticVariable(pending_exception), edx);
+
+ // Special handling of termination exceptions which are uncatchable
+ // by javascript code.
+ __ cmp(eax, factory->termination_exception());
+ Label throw_termination_exception;
+ __ j(equal, &throw_termination_exception, Label::kNear);
+
+ // Handle normal exception by following handler chain.
+ __ Throw(eax);
+
+ __ bind(&throw_termination_exception);
+ __ ThrowUncatchable(eax);
+
+ __ bind(&failure);
+ // For failure to match, return null.
+ __ mov(eax, factory->null_value());
+ __ ret(4 * kPointerSize);
+
+ // Load RegExp data.
+ __ bind(&success);
+ __ mov(eax, Operand(esp, kJSRegExpOffset));
+ __ mov(ecx, FieldOperand(eax, JSRegExp::kDataOffset));
+ __ mov(edx, FieldOperand(ecx, JSRegExp::kIrregexpCaptureCountOffset));
+ // Calculate number of capture registers (number_of_captures + 1) * 2.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ __ 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.
+ // Check that the fourth object is a JSArray object.
+ __ mov(eax, Operand(esp, kLastMatchInfoOffset));
+ __ JumpIfSmi(eax, &runtime);
+ __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
+ __ j(not_equal, &runtime);
+ // Check that the JSArray is in fast case.
+ __ mov(ebx, FieldOperand(eax, JSArray::kElementsOffset));
+ __ mov(eax, FieldOperand(ebx, HeapObject::kMapOffset));
+ __ cmp(eax, factory->fixed_array_map());
+ __ j(not_equal, &runtime);
+ // Check that the last match info has space for the capture registers and the
+ // additional information.
+ __ mov(eax, FieldOperand(ebx, FixedArray::kLengthOffset));
+ __ SmiUntag(eax);
+ __ sub(eax, Immediate(RegExpImpl::kLastMatchOverhead));
+ __ cmp(edx, eax);
+ __ j(greater, &runtime);
+
+ // ebx: last_match_info backing store (FixedArray)
+ // edx: number of capture registers
+ // Store the capture count.
+ __ SmiTag(edx); // Number of capture registers to smi.
+ __ mov(FieldOperand(ebx, RegExpImpl::kLastCaptureCountOffset), edx);
+ __ SmiUntag(edx); // Number of capture registers back from smi.
+ // Store last subject and last input.
+ __ mov(eax, Operand(esp, kSubjectOffset));
+ __ mov(ecx, eax);
+ __ mov(FieldOperand(ebx, RegExpImpl::kLastSubjectOffset), eax);
+ __ RecordWriteField(ebx,
+ RegExpImpl::kLastSubjectOffset,
+ eax,
+ edi,
+ kDontSaveFPRegs);
+ __ mov(eax, ecx);
+ __ mov(FieldOperand(ebx, RegExpImpl::kLastInputOffset), eax);
+ __ RecordWriteField(ebx,
+ RegExpImpl::kLastInputOffset,
+ eax,
+ edi,
+ 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());
+ __ mov(ecx, Immediate(address_of_static_offsets_vector));
+
+ // ebx: last_match_info backing store (FixedArray)
+ // ecx: offsets vector
+ // edx: number of capture registers
+ Label next_capture, done;
+ // Capture register counter starts from number of capture registers and
+ // counts down until wraping after zero.
+ __ bind(&next_capture);
+ __ 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));
+ __ SmiTag(edi);
+ // Store the smi value in the last match info.
+ __ mov(FieldOperand(ebx,
+ edx,
+ times_pointer_size,
+ RegExpImpl::kFirstCaptureOffset),
+ edi);
+ __ jmp(&next_capture);
+ __ bind(&done);
+
+ // Return last match info.
+ __ mov(eax, Operand(esp, kLastMatchInfoOffset));
+ __ ret(4 * kPointerSize);
+
+ // Do the runtime call to execute the regexp.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+
+ // Deferred code for string handling.
+ // (7) Not a long external string? If yes, go to (10).
+ __ bind(&not_seq_nor_cons);
+ // Compare flags are still set from (3).
+ __ j(greater, &not_long_external, Label::kNear); // Go to (10).
+
+ // (8) External string. Short external strings have been ruled out.
+ __ bind(&external_string);
+ // Reload instance type.
+ __ 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, kExternalStringExpectedButNotFound);
+ }
+ __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
+ // Move the pointer so that offset-wise, it looks like a sequential string.
+ STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
+ __ sub(eax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ // (8a) Is the external string one byte? If yes, go to (6).
+ __ test_b(ebx, kStringEncodingMask);
+ __ j(not_zero, &seq_one_byte_string); // Goto (6).
+
+ // eax: sequential subject string (or look-alike, external string)
+ // edx: original subject string
+ // ecx: RegExp data (FixedArray)
+ // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
+ __ bind(&seq_two_byte_string);
+ // Load previous index and check range before edx is overwritten. We have
+ // to use edx instead of eax here because it might have been only made to
+ // look like a sequential string when it actually is an external string.
+ __ mov(ebx, Operand(esp, kPreviousIndexOffset));
+ __ JumpIfNotSmi(ebx, &runtime);
+ __ cmp(ebx, FieldOperand(edx, String::kLengthOffset));
+ __ j(above_equal, &runtime);
+ __ mov(edx, FieldOperand(ecx, JSRegExp::kDataUC16CodeOffset));
+ __ Set(ecx, Immediate(0)); // Type is two byte.
+ __ jmp(&check_code); // Go to (E).
+
+ // (10) Not a string or a short external string? If yes, bail out to runtime.
+ __ bind(&not_long_external);
+ // Catch non-string subject or short external string.
+ STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
+ __ test(ebx, Immediate(kIsNotStringMask | kShortExternalStringTag));
+ __ j(not_zero, &runtime);
+
+ // (11) Sliced string. Replace subject with parent. Go to (5a).
+ // Load offset into edi and replace subject string with parent.
+ __ mov(edi, FieldOperand(eax, SlicedString::kOffsetOffset));
+ __ mov(eax, FieldOperand(eax, SlicedString::kParentOffset));
+ __ jmp(&check_underlying); // Go to (5a).
+#endif // V8_INTERPRETED_REGEXP
+}
+
+
+void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
+ const int kMaxInlineLength = 100;
+ Label slowcase;
+ Label done;
+ __ mov(ebx, Operand(esp, kPointerSize * 3));
+ __ JumpIfNotSmi(ebx, &slowcase);
+ __ cmp(ebx, Immediate(Smi::FromInt(kMaxInlineLength)));
+ __ j(above, &slowcase);
+ // Smi-tagging is equivalent to multiplying by 2.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ // Allocate RegExpResult followed by FixedArray with size in ebx.
+ // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
+ // Elements: [Map][Length][..elements..]
+ __ Allocate(JSRegExpResult::kSize + FixedArray::kHeaderSize,
+ times_pointer_size,
+ ebx, // In: Number of elements as a smi
+ REGISTER_VALUE_IS_SMI,
+ eax, // Out: Start of allocation (tagged).
+ ecx, // Out: End of allocation.
+ edx, // Scratch register
+ &slowcase,
+ TAG_OBJECT);
+ // eax: Start of allocated area, object-tagged.
+
+ // Set JSArray map to global.regexp_result_map().
+ // Set empty properties FixedArray.
+ // Set elements to point to FixedArray allocated right after the JSArray.
+ // Interleave operations for better latency.
+ __ mov(edx, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
+ Factory* factory = masm->isolate()->factory();
+ __ mov(ecx, Immediate(factory->empty_fixed_array()));
+ __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
+ __ mov(edx, FieldOperand(edx, GlobalObject::kNativeContextOffset));
+ __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
+ __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
+ __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
+ __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
+
+ // Set input, index and length fields from arguments.
+ __ mov(ecx, Operand(esp, kPointerSize * 1));
+ __ mov(FieldOperand(eax, JSRegExpResult::kInputOffset), ecx);
+ __ mov(ecx, Operand(esp, kPointerSize * 2));
+ __ mov(FieldOperand(eax, JSRegExpResult::kIndexOffset), ecx);
+ __ mov(ecx, Operand(esp, kPointerSize * 3));
+ __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
+
+ // Fill out the elements FixedArray.
+ // eax: JSArray.
+ // ebx: FixedArray.
+ // ecx: Number of elements in array, as smi.
+
+ // Set map.
+ __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(factory->fixed_array_map()));
+ // Set length.
+ __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
+ // Fill contents of fixed-array with undefined.
+ __ SmiUntag(ecx);
+ __ mov(edx, Immediate(factory->undefined_value()));
+ __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
+ // Fill fixed array elements with undefined.
+ // eax: JSArray.
+ // ecx: Number of elements to fill.
+ // ebx: Start of elements in FixedArray.
+ // edx: undefined.
+ Label loop;
+ __ test(ecx, ecx);
+ __ bind(&loop);
+ __ j(less_equal, &done, Label::kNear); // Jump if ecx is negative or zero.
+ __ sub(ecx, Immediate(1));
+ __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
+ __ jmp(&loop);
+
+ __ bind(&done);
+ __ ret(3 * kPointerSize);
+
+ __ bind(&slowcase);
+ __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
+}
+
+
+void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* not_found) {
+ // Use of registers. Register result is used as a temporary.
+ Register number_string_cache = result;
+ Register mask = scratch1;
+ Register scratch = scratch2;
+
+ // Load the number string cache.
+ __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
+ // 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(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
+ // doubles is the xor of the upper and lower words. See
+ // Heap::GetNumberStringCache.
+ Label smi_hash_calculated;
+ Label load_result_from_cache;
+ Label not_smi;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(object, &not_smi, Label::kNear);
+ __ mov(scratch, object);
+ __ SmiUntag(scratch);
+ __ jmp(&smi_hash_calculated, Label::kNear);
+ __ bind(&not_smi);
+ __ cmp(FieldOperand(object, HeapObject::kMapOffset),
+ masm->isolate()->factory()->heap_number_map());
+ __ j(not_equal, not_found);
+ STATIC_ASSERT(8 == kDoubleSize);
+ __ 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, mask);
+ Register index = scratch;
+ Register probe = mask;
+ __ mov(probe,
+ FieldOperand(number_string_cache,
+ index,
+ times_twice_pointer_size,
+ FixedArray::kHeaderSize));
+ __ JumpIfSmi(probe, not_found);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope fscope(masm, SSE2);
+ __ movdbl(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
+ __ movdbl(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
+ __ ucomisd(xmm0, xmm1);
+ } else {
+ __ fld_d(FieldOperand(object, HeapNumber::kValueOffset));
+ __ fld_d(FieldOperand(probe, HeapNumber::kValueOffset));
+ __ FCmp();
+ }
+ __ j(parity_even, not_found); // Bail out if NaN is involved.
+ __ j(not_equal, not_found); // The cache did not contain this value.
+ __ jmp(&load_result_from_cache, Label::kNear);
+
+ __ bind(&smi_hash_calculated);
+ // Object is smi and hash is now in scratch. Calculate cache index.
+ __ and_(scratch, mask);
+ // Check if the entry is the smi we are looking for.
+ __ cmp(object,
+ FieldOperand(number_string_cache,
+ index,
+ times_twice_pointer_size,
+ FixedArray::kHeaderSize));
+ __ j(not_equal, not_found);
+
+ // Get the result from the cache.
+ __ bind(&load_result_from_cache);
+ __ mov(result,
+ FieldOperand(number_string_cache,
+ index,
+ times_twice_pointer_size,
+ FixedArray::kHeaderSize + kPointerSize));
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->number_to_string_native(), 1);
+}
+
+
+void NumberToStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ __ mov(ebx, Operand(esp, kPointerSize));
+
+ // Generate code to lookup number in the number string cache.
+ GenerateLookupNumberStringCache(masm, ebx, eax, ecx, edx, &runtime);
+ __ ret(1 * kPointerSize);
+
+ __ bind(&runtime);
+ // Handle number to string in the runtime system if not found in the cache.
+ __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
+}
+
+
+static int NegativeComparisonResult(Condition cc) {
+ ASSERT(cc != equal);
+ ASSERT((cc == less) || (cc == less_equal)
+ || (cc == greater) || (cc == greater_equal));
+ return (cc == greater || cc == greater_equal) ? LESS : GREATER;
+}
+
+
+static void CheckInputType(MacroAssembler* masm,
+ Register input,
+ CompareIC::State expected,
+ Label* fail) {
+ Label ok;
+ if (expected == CompareIC::SMI) {
+ __ JumpIfNotSmi(input, fail);
+ } else if (expected == CompareIC::NUMBER) {
+ __ JumpIfSmi(input, &ok);
+ __ cmp(FieldOperand(input, HeapObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->heap_number_map()));
+ __ j(not_equal, fail);
+ }
+ // We could be strict about internalized/non-internalized here, but as long as
+ // hydrogen doesn't care, the stub doesn't have to care either.
+ __ bind(&ok);
+}
+
+
+static void BranchIfNotInternalizedString(MacroAssembler* masm,
+ Label* label,
+ Register object,
+ Register scratch) {
+ __ JumpIfSmi(object, label);
+ __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset));
+ __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ test(scratch, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
+ __ j(not_zero, label);
+}
+
+
+void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
+ Label check_unequal_objects;
+ Condition cc = GetCondition();
+
+ Label miss;
+ CheckInputType(masm, edx, left_, &miss);
+ CheckInputType(masm, eax, right_, &miss);
+
+ // Compare two smis.
+ Label non_smi, smi_done;
+ __ mov(ecx, edx);
+ __ or_(ecx, eax);
+ __ JumpIfNotSmi(ecx, &non_smi, Label::kNear);
+ __ 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);
+ __ mov(eax, edx);
+ __ ret(0);
+ __ bind(&non_smi);
+
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+ // it is certain that at least one operand isn't a smi.
+
+ // Identical objects can be compared fast, but there are some tricky cases
+ // for NaN and undefined.
+ Label generic_heap_number_comparison;
+ {
+ Label not_identical;
+ __ cmp(eax, edx);
+ __ j(not_equal, &not_identical);
+
+ if (cc != equal) {
+ // Check for undefined. undefined OP undefined is false even though
+ // undefined == undefined.
+ Label check_for_nan;
+ __ cmp(edx, masm->isolate()->factory()->undefined_value());
+ __ j(not_equal, &check_for_nan, Label::kNear);
+ __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
+ __ ret(0);
+ __ bind(&check_for_nan);
+ }
+
+ // Test for NaN. Compare heap numbers in a general way,
+ // to hanlde NaNs correctly.
+ __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->heap_number_map()));
+ __ j(equal, &generic_heap_number_comparison, Label::kNear);
+ if (cc != equal) {
+ // Call runtime on identical JSObjects. Otherwise return equal.
+ __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
+ __ j(above_equal, &not_identical);
+ }
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+
+
+ __ bind(&not_identical);
+ }
+
+ // Strict equality can quickly decide whether objects are equal.
+ // Non-strict object equality is slower, so it is handled later in the stub.
+ if (cc == equal && strict()) {
+ Label slow; // Fallthrough label.
+ Label not_smis;
+ // If we're doing a strict equality comparison, we don't have to do
+ // type conversion, so we generate code to do fast comparison for objects
+ // and oddballs. Non-smi numbers and strings still go through the usual
+ // slow-case code.
+ // If either is a Smi (we know that not both are), then they can only
+ // be equal if the other is a HeapNumber. If so, use the slow case.
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT_EQ(0, Smi::FromInt(0));
+ __ mov(ecx, Immediate(kSmiTagMask));
+ __ 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(ecx, Immediate(0x01));
+ __ mov(ebx, edx);
+ __ 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.
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->heap_number_map()));
+ // If heap number, handle it in the slow case.
+ __ j(equal, &slow, Label::kNear);
+ // Return non-equal (ebx is not zero)
+ __ mov(eax, ebx);
+ __ ret(0);
+
+ __ bind(&not_smis);
+ // If either operand is a JSObject or an oddball value, then they are not
+ // equal since their pointers are different
+ // There is no test for undetectability in strict equality.
+
+ // Get the type of the first operand.
+ // If the first object is a JS object, we have done pointer comparison.
+ Label first_non_object;
+ STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
+ __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
+ __ j(below, &first_non_object, Label::kNear);
+
+ // Return non-zero (eax is not zero)
+ Label return_not_equal;
+ STATIC_ASSERT(kHeapObjectTag != 0);
+ __ bind(&return_not_equal);
+ __ ret(0);
+
+ __ bind(&first_non_object);
+ // Check for oddballs: true, false, null, undefined.
+ __ CmpInstanceType(ecx, ODDBALL_TYPE);
+ __ j(equal, &return_not_equal);
+
+ __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ecx);
+ __ j(above_equal, &return_not_equal);
+
+ // Check for oddballs: true, false, null, undefined.
+ __ CmpInstanceType(ecx, ODDBALL_TYPE);
+ __ j(equal, &return_not_equal);
+
+ // Fall through to the general case.
+ __ bind(&slow);
+ }
+
+ // Generate the number comparison code.
+ Label non_number_comparison;
+ Label unordered;
+ __ bind(&generic_heap_number_comparison);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ CpuFeatureScope use_cmov(masm, CMOV);
+
+ FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
+ __ ucomisd(xmm0, xmm1);
+
+ // Don't base result on EFLAGS when a NaN is involved.
+ __ j(parity_even, &unordered, Label::kNear);
+ // Return a result of -1, 0, or 1, based on EFLAGS.
+ __ mov(eax, 0); // equal
+ __ mov(ecx, Immediate(Smi::FromInt(1)));
+ __ cmov(above, eax, ecx);
+ __ mov(ecx, Immediate(Smi::FromInt(-1)));
+ __ cmov(below, eax, ecx);
+ __ ret(0);
+ } else {
+ FloatingPointHelper::CheckFloatOperands(
+ masm, &non_number_comparison, ebx);
+ FloatingPointHelper::LoadFloatOperand(masm, eax);
+ FloatingPointHelper::LoadFloatOperand(masm, edx);
+ __ FCmp();
+
+ // Don't base result on EFLAGS when a NaN is involved.
+ __ j(parity_even, &unordered, Label::kNear);
+
+ Label below_label, above_label;
+ // Return a result of -1, 0, or 1, based on EFLAGS.
+ __ j(below, &below_label, Label::kNear);
+ __ j(above, &above_label, Label::kNear);
+
+ __ Set(eax, Immediate(0));
+ __ ret(0);
+
+ __ bind(&below_label);
+ __ mov(eax, Immediate(Smi::FromInt(-1)));
+ __ ret(0);
+
+ __ bind(&above_label);
+ __ mov(eax, Immediate(Smi::FromInt(1)));
+ __ ret(0);
+ }
+
+ // If one of the numbers was NaN, then the result is always false.
+ // The cc is never not-equal.
+ __ bind(&unordered);
+ ASSERT(cc != not_equal);
+ if (cc == less || cc == less_equal) {
+ __ mov(eax, Immediate(Smi::FromInt(1)));
+ } else {
+ __ mov(eax, Immediate(Smi::FromInt(-1)));
+ }
+ __ ret(0);
+
+ // The number comparison code did not provide a valid result.
+ __ bind(&non_number_comparison);
+
+ // Fast negative check for internalized-to-internalized equality.
+ Label check_for_strings;
+ if (cc == equal) {
+ BranchIfNotInternalizedString(masm, &check_for_strings, eax, ecx);
+ BranchIfNotInternalizedString(masm, &check_for_strings, edx, ecx);
+
+ // We've already checked for object identity, so if both operands
+ // are internalized they aren't equal. Register eax already holds a
+ // non-zero value, which indicates not equal, so just return.
+ __ ret(0);
+ }
+
+ __ bind(&check_for_strings);
+
+ __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx,
+ &check_unequal_objects);
+
+ // Inline comparison of ASCII strings.
+ if (cc == equal) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
+ edx,
+ eax,
+ ecx,
+ ebx);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ edx,
+ eax,
+ ecx,
+ ebx,
+ edi);
+ }
+#ifdef DEBUG
+ __ Abort(kUnexpectedFallThroughFromStringComparison);
+#endif
+
+ __ bind(&check_unequal_objects);
+ if (cc == equal && !strict()) {
+ // Non-strict equality. Objects are unequal if
+ // they are both JSObjects and not undetectable,
+ // and their pointers are different.
+ Label not_both_objects;
+ Label return_unequal;
+ // At most one is a smi, so we can test for smi by adding the two.
+ // A smi plus a heap object has the low bit set, a heap object plus
+ // a heap object has the low bit clear.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagMask == 1);
+ __ lea(ecx, Operand(eax, edx, times_1, 0));
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ j(not_zero, &not_both_objects, Label::kNear);
+ __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
+ __ j(below, &not_both_objects, Label::kNear);
+ __ CmpObjectType(edx, FIRST_SPEC_OBJECT_TYPE, ebx);
+ __ j(below, &not_both_objects, Label::kNear);
+ // We do not bail out after this point. Both are JSObjects, and
+ // they are equal if and only if both are undetectable.
+ // The and of the undetectable flags is 1 if and only if they are equal.
+ __ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ __ j(zero, &return_unequal, Label::kNear);
+ __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ __ j(zero, &return_unequal, Label::kNear);
+ // The objects are both undetectable, so they both compare as the value
+ // undefined, and are equal.
+ __ Set(eax, Immediate(EQUAL));
+ __ bind(&return_unequal);
+ // Return non-equal by returning the non-zero object pointer in eax,
+ // or return equal if we fell through to here.
+ __ ret(0); // rax, rdx were pushed
+ __ bind(&not_both_objects);
+ }
+
+ // Push arguments below the return address.
+ __ pop(ecx);
+ __ push(edx);
+ __ push(eax);
+
+ // Figure out which native to call and setup the arguments.
+ Builtins::JavaScript builtin;
+ if (cc == equal) {
+ builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
+ } else {
+ builtin = Builtins::COMPARE;
+ __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc))));
+ }
+
+ // Restore return address on the stack.
+ __ push(ecx);
+
+ // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ InvokeBuiltin(builtin, JUMP_FUNCTION);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void StackCheckStub::Generate(MacroAssembler* masm) {
+ __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
+}
+
+
+void InterruptStub::Generate(MacroAssembler* masm) {
+ __ TailCallRuntime(Runtime::kInterrupt, 0, 1);
+}
+
+
+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, miss, megamorphic, not_array_function;
+
+ // Load the cache state into ecx.
+ __ mov(ecx, FieldOperand(ebx, Cell::kValueOffset));
+
+ // A monomorphic cache hit or an already megamorphic state: invoke the
+ // function without changing the state.
+ __ cmp(ecx, edi);
+ __ j(equal, &done);
+ __ cmp(ecx, Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
+ __ j(equal, &done);
+
+ // If we came here, we need to see if we are the array function.
+ // If we didn't have a matching function, and we didn't find the megamorph
+ // sentinel, then we have in the cell either some other function or an
+ // AllocationSite. Do a map check on the object in ecx.
+ Handle<Map> allocation_site_map(
+ masm->isolate()->heap()->allocation_site_map(),
+ masm->isolate());
+ __ cmp(FieldOperand(ecx, 0), Immediate(allocation_site_map));
+ __ j(not_equal, &miss);
+
+ // Load the global or builtins object from the current context
+ __ LoadGlobalContext(ecx);
+ // Make sure the function is the Array() function
+ __ cmp(edi, Operand(ecx,
+ Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
+ __ j(not_equal, &megamorphic);
+ __ jmp(&done);
+
+ __ bind(&miss);
+
+ // A monomorphic miss (i.e, here the cache is not uninitialized) goes
+ // megamorphic.
+ __ cmp(ecx, Immediate(TypeFeedbackCells::UninitializedSentinel(isolate)));
+ __ j(equal, &initialize);
+ // MegamorphicSentinel is an immortal immovable object (undefined) so no
+ // write-barrier is needed.
+ __ bind(&megamorphic);
+ __ mov(FieldOperand(ebx, Cell::kValueOffset),
+ Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
+ __ jmp(&done, Label::kNear);
+
+ // An uninitialized cache is patched with the function or sentinel to
+ // indicate the ElementsKind if function is the Array constructor.
+ __ bind(&initialize);
+ __ LoadGlobalContext(ecx);
+ // Make sure the function is the Array() function
+ __ cmp(edi, Operand(ecx,
+ Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
+ __ j(not_equal, &not_array_function);
+
+ // The target function is the Array constructor,
+ // Create an AllocationSite if we don't already have it, store it in the cell
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ __ push(eax);
+ __ push(edi);
+ __ push(ebx);
+
+ CreateAllocationSiteStub create_stub;
+ __ CallStub(&create_stub);
+
+ __ pop(ebx);
+ __ pop(edi);
+ __ pop(eax);
+ }
+ __ jmp(&done);
+
+ __ bind(&not_array_function);
+ __ mov(FieldOperand(ebx, Cell::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 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, isolate->factory()->the_hole_value());
+ __ j(not_equal, &receiver_ok, Label::kNear);
+ // Patch the receiver on the stack with the global receiver object.
+ __ mov(ecx, GlobalObjectOperand());
+ __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
+ __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ecx);
+ __ bind(&receiver_ok);
+ }
+
+ // 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, isolate->factory()->the_hole_value());
+ __ j(equal, &call_as_function);
+ __ InvokeFunction(edi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_METHOD);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(edi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
+
+ // 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, Cell::kValueOffset),
+ Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
+ }
+ // Check for function proxy.
+ __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
+ __ j(not_equal, &non_function);
+ __ pop(ecx);
+ __ push(edi); // put proxy as additional argument under return address
+ __ push(ecx);
+ __ Set(eax, Immediate(argc_ + 1));
+ __ Set(ebx, Immediate(0));
+ __ SetCallKind(ecx, CALL_AS_FUNCTION);
+ __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
+ {
+ Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
+ __ jmp(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);
+ __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
+ __ Set(eax, Immediate(argc_));
+ __ Set(ebx, Immediate(0));
+ __ SetCallKind(ecx, CALL_AS_METHOD);
+ __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
+ 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.
+ Register jmp_reg = ecx;
+ __ mov(jmp_reg, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(jmp_reg, FieldOperand(jmp_reg,
+ SharedFunctionInfo::kConstructStubOffset));
+ __ lea(jmp_reg, FieldOperand(jmp_reg, Code::kHeaderSize));
+ __ jmp(jmp_reg);
+
+ // 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(Isolate* isolate) {
+ CEntryStub::GenerateAheadOfTime(isolate);
+ StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ StubFailureTrampolineStub::GenerateAheadOfTime(isolate);
+ // It is important that the store buffer overflow stubs are generated first.
+ RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
+ CreateAllocationSiteStub::GenerateAheadOfTime(isolate);
+}
+
+
+void CodeStub::GenerateFPStubs(Isolate* isolate) {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CEntryStub save_doubles(1, kSaveFPRegs);
+ // Stubs might already be in the snapshot, detect that and don't regenerate,
+ // which would lead to code stub initialization state being messed up.
+ Code* save_doubles_code;
+ if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) {
+ save_doubles_code = *(save_doubles.GetCode(isolate));
+ }
+ save_doubles_code->set_is_pregenerated(true);
+ isolate->set_fp_stubs_generated(true);
+ }
+}
+
+
+void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
+ CEntryStub stub(1, kDontSaveFPRegs);
+ Handle<Code> code = stub.GetCode(isolate);
+ code->set_is_pregenerated(true);
+}
+
+
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ __ mov(scratch, value);
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ and_(scratch, 0xf);
+ __ cmp(scratch, 0xf);
+ __ j(equal, oom_label);
+}
+
+
+void CEntryStub::GenerateCore(MacroAssembler* masm,
+ Label* throw_normal_exception,
+ Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
+ bool do_gc,
+ bool always_allocate_scope) {
+ // eax: result parameter for PerformGC, if any
+ // ebx: pointer to C function (C callee-saved)
+ // ebp: frame pointer (restored after C call)
+ // esp: stack pointer (restored after C call)
+ // edi: number of arguments including receiver (C callee-saved)
+ // esi: pointer to the first argument (C callee-saved)
+
+ // Result returned in eax, or eax+edx if result_size_ is 2.
+
+ // Check stack alignment.
+ if (FLAG_debug_code) {
+ __ CheckStackAlignment();
+ }
+
+ if (do_gc) {
+ // Pass failure code returned from last attempt as first argument to
+ // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
+ // stack alignment is known to be correct. This function takes one argument
+ // which is passed on the stack, and we know that the stack has been
+ // prepared to pass at least one argument.
+ __ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
+ __ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
+ }
+
+ ExternalReference scope_depth =
+ ExternalReference::heap_always_allocate_scope_depth(masm->isolate());
+ if (always_allocate_scope) {
+ __ inc(Operand::StaticVariable(scope_depth));
+ }
+
+ // Call C function.
+ __ mov(Operand(esp, 0 * kPointerSize), edi); // argc.
+ __ mov(Operand(esp, 1 * kPointerSize), esi); // argv.
+ __ mov(Operand(esp, 2 * kPointerSize),
+ Immediate(ExternalReference::isolate_address(masm->isolate())));
+ __ call(ebx);
+ // Result is in eax or edx:eax - do not destroy these registers!
+
+ if (always_allocate_scope) {
+ __ dec(Operand::StaticVariable(scope_depth));
+ }
+
+ // Runtime functions should not return 'the hole'. Allowing it to escape may
+ // lead to crashes in the IC code later.
+ if (FLAG_debug_code) {
+ Label okay;
+ __ cmp(eax, masm->isolate()->factory()->the_hole_value());
+ __ j(not_equal, &okay, Label::kNear);
+ __ int3();
+ __ bind(&okay);
+ }
+
+ // Check for failure result.
+ Label failure_returned;
+ STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
+ __ lea(ecx, Operand(eax, 1));
+ // Lower 2 bits of ecx are 0 iff eax has failure tag.
+ __ test(ecx, Immediate(kFailureTagMask));
+ __ j(zero, &failure_returned);
+
+ ExternalReference pending_exception_address(
+ Isolate::kPendingExceptionAddress, masm->isolate());
+
+ // Check that there is no pending exception, otherwise we
+ // should have returned some failure value.
+ if (FLAG_debug_code) {
+ __ push(edx);
+ __ 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.
+ __ j(equal, &okay, Label::kNear);
+ __ int3();
+ __ bind(&okay);
+ __ pop(edx);
+ }
+
+ // Exit the JavaScript to C++ exit frame.
+ __ LeaveExitFrame(save_doubles_ == kSaveFPRegs);
+ __ ret(0);
+
+ // Handling of failure.
+ __ bind(&failure_returned);
+
+ Label retry;
+ // If the returned exception is RETRY_AFTER_GC continue at retry label
+ STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
+ __ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
+ __ j(zero, &retry, Label::kNear);
+
+ // Special handling of out of memory exceptions.
+ JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
+
+ // Retrieve the pending exception.
+ __ mov(eax, Operand::StaticVariable(pending_exception_address));
+
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, eax, ecx, throw_out_of_memory_exception);
+
+ // Clear the pending exception.
+ __ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
+ __ mov(Operand::StaticVariable(pending_exception_address), edx);
+
+ // Special handling of termination exceptions which are uncatchable
+ // by javascript code.
+ __ cmp(eax, masm->isolate()->factory()->termination_exception());
+ __ j(equal, throw_termination_exception);
+
+ // Handle normal exception.
+ __ jmp(throw_normal_exception);
+
+ // Retry.
+ __ bind(&retry);
+}
+
+
+void CEntryStub::Generate(MacroAssembler* masm) {
+ // eax: number of arguments including receiver
+ // ebx: pointer to C function (C callee-saved)
+ // ebp: frame pointer (restored after C call)
+ // esp: stack pointer (restored after C call)
+ // esi: current context (C callee-saved)
+ // edi: JS function of the caller (C callee-saved)
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // NOTE: Invocations of builtins may return failure objects instead
+ // of a proper result. The builtin entry handles this by performing
+ // a garbage collection and retrying the builtin (twice).
+
+ // Enter the exit frame that transitions from JavaScript to C++.
+ __ EnterExitFrame(save_doubles_ == kSaveFPRegs);
+
+ // eax: result parameter for PerformGC, if any (setup below)
+ // ebx: pointer to builtin function (C callee-saved)
+ // ebp: frame pointer (restored after C call)
+ // esp: stack pointer (restored after C call)
+ // edi: number of arguments including receiver (C callee-saved)
+ // esi: argv pointer (C callee-saved)
+
+ Label throw_normal_exception;
+ Label throw_termination_exception;
+ Label throw_out_of_memory_exception;
+
+ // Call into the runtime system.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ false,
+ false);
+
+ // Do space-specific GC and retry runtime call.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ false);
+
+ // Do full GC and retry runtime call one final time.
+ Failure* failure = Failure::InternalError();
+ __ mov(eax, Immediate(reinterpret_cast<int32_t>(failure)));
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ true);
+
+ __ bind(&throw_out_of_memory_exception);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ 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);
+ Label already_have_failure;
+ JumpIfOOM(masm, eax, ecx, &already_have_failure);
+ __ mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException(0x1)));
+ __ bind(&already_have_failure);
+ __ mov(Operand::StaticVariable(pending_exception), eax);
+ // Fall through to the next label.
+
+ __ bind(&throw_termination_exception);
+ __ ThrowUncatchable(eax);
+
+ __ bind(&throw_normal_exception);
+ __ Throw(eax);
+}
+
+
+void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
+ Label invoke, handler_entry, exit;
+ Label not_outermost_js, not_outermost_js_2;
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Set up frame.
+ __ push(ebp);
+ __ mov(ebp, esp);
+
+ // Push marker in two places.
+ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
+ __ push(Immediate(Smi::FromInt(marker))); // context slot
+ __ push(Immediate(Smi::FromInt(marker))); // function slot
+ // Save callee-saved registers (C calling conventions).
+ __ push(edi);
+ __ push(esi);
+ __ push(ebx);
+
+ // Save copies of the top frame descriptor on the stack.
+ ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, masm->isolate());
+ __ push(Operand::StaticVariable(c_entry_fp));
+
+ // If this is the outermost JS call, set js_entry_sp value.
+ ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress,
+ masm->isolate());
+ __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0));
+ __ j(not_equal, &not_outermost_js, Label::kNear);
+ __ mov(Operand::StaticVariable(js_entry_sp), ebp);
+ __ push(Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
+ __ jmp(&invoke, Label::kNear);
+ __ bind(&not_outermost_js);
+ __ push(Immediate(Smi::FromInt(StackFrame::INNER_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.
+ 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. There's only one
+ // handler block in this code object, so its index is 0.
+ __ bind(&invoke);
+ __ PushTryHandler(StackHandler::JS_ENTRY, 0);
+
+ // Clear any pending exceptions.
+ __ 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.
+ if (is_construct) {
+ ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
+ masm->isolate());
+ __ mov(edx, Immediate(construct_entry));
+ } else {
+ ExternalReference entry(Builtins::kJSEntryTrampoline,
+ masm->isolate());
+ __ mov(edx, Immediate(entry));
+ }
+ __ mov(edx, Operand(edx, 0)); // deref address
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+ __ call(edx);
+
+ // Unlink this frame from the handler chain.
+ __ PopTryHandler();
+
+ __ bind(&exit);
+ // Check if the current stack frame is marked as the outermost JS frame.
+ __ pop(ebx);
+ __ 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);
+
+ // Restore the top frame descriptor from the stack.
+ __ pop(Operand::StaticVariable(ExternalReference(
+ Isolate::kCEntryFPAddress,
+ masm->isolate())));
+
+ // Restore callee-saved registers (C calling conventions).
+ __ pop(ebx);
+ __ pop(esi);
+ __ pop(edi);
+ __ add(esp, Immediate(2 * kPointerSize)); // remove markers
+
+ // Restore frame pointer and return.
+ __ pop(ebp);
+ __ ret(0);
+}
+
+
+// Generate stub code for instanceof.
+// This code can patch a call site inlined cache of the instance of check,
+// which looks like this.
+//
+// 81 ff XX XX XX XX cmp edi, <the hole, patched to a map>
+// 75 0a jne <some near label>
+// b8 XX XX XX XX mov eax, <the hole, patched to either true or false>
+//
+// If call site patching is requested the stack will have the delta from the
+// return address to the cmp instruction just below the return address. This
+// also means that call site patching can only take place with arguments in
+// registers. TOS looks like this when call site patching is requested
+//
+// esp[0] : return address
+// esp[4] : delta from return address to cmp instruction
+//
+void InstanceofStub::Generate(MacroAssembler* masm) {
+ // Call site inlining and patching implies arguments in registers.
+ ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
+
+ // Fixed register usage throughout the stub.
+ Register object = eax; // Object (lhs).
+ Register map = ebx; // Map of the object.
+ Register function = edx; // Function (rhs).
+ Register prototype = edi; // Prototype of the function.
+ Register scratch = ecx;
+
+ // Constants describing the call site code to patch.
+ static const int kDeltaToCmpImmediate = 2;
+ static const int kDeltaToMov = 8;
+ static const int kDeltaToMovImmediate = 9;
+ 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);
+
+ ASSERT_EQ(object.code(), InstanceofStub::left().code());
+ ASSERT_EQ(function.code(), InstanceofStub::right().code());
+
+ // Get the object and function - they are always both needed.
+ Label slow, not_js_object;
+ if (!HasArgsInRegisters()) {
+ __ mov(object, Operand(esp, 2 * kPointerSize));
+ __ mov(function, Operand(esp, 1 * kPointerSize));
+ }
+
+ // Check that the left hand is a JS object.
+ __ JumpIfSmi(object, &not_js_object);
+ __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
+
+ // If there is a call site cache don't look in the global cache, but do the
+ // real lookup and update the call site cache.
+ if (!HasCallSiteInlineCheck()) {
+ // Look up the function and the map in the instanceof cache.
+ Label miss;
+ __ CompareRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
+ __ j(not_equal, &miss, Label::kNear);
+ __ CompareRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex);
+ __ j(not_equal, &miss, Label::kNear);
+ __ LoadRoot(eax, Heap::kInstanceofCacheAnswerRootIndex);
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+ __ bind(&miss);
+ }
+
+ // Get the prototype of the function.
+ __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
+
+ // Check that the function prototype is a JS object.
+ __ JumpIfSmi(prototype, &slow);
+ __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
+
+ // Update the global instanceof or call site inlined cache with the current
+ // map and function. The cached answer will be set when it is known below.
+ if (!HasCallSiteInlineCheck()) {
+ __ StoreRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex);
+ __ StoreRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex);
+ } else {
+ // The constants for the code patching are based on no push instructions
+ // at the call site.
+ ASSERT(HasArgsInRegisters());
+ // Get return address and delta to inlined map check.
+ __ mov(scratch, Operand(esp, 0 * kPointerSize));
+ __ sub(scratch, Operand(esp, 1 * kPointerSize));
+ if (FLAG_debug_code) {
+ __ cmpb(Operand(scratch, 0), kCmpEdiOperandByte1);
+ __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCmp1);
+ __ cmpb(Operand(scratch, 1), kCmpEdiOperandByte2);
+ __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCmp2);
+ }
+ __ mov(scratch, Operand(scratch, kDeltaToCmpImmediate));
+ __ mov(Operand(scratch, 0), map);
+ }
+
+ // Loop through the prototype chain of the object looking for the function
+ // prototype.
+ __ mov(scratch, FieldOperand(map, Map::kPrototypeOffset));
+ Label loop, is_instance, is_not_instance;
+ __ bind(&loop);
+ __ cmp(scratch, prototype);
+ __ j(equal, &is_instance, Label::kNear);
+ Factory* factory = masm->isolate()->factory();
+ __ 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));
+ __ jmp(&loop);
+
+ __ bind(&is_instance);
+ if (!HasCallSiteInlineCheck()) {
+ __ mov(eax, Immediate(0));
+ __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ // Get return address and delta to inlined map check.
+ __ mov(eax, factory->true_value());
+ __ mov(scratch, Operand(esp, 0 * kPointerSize));
+ __ sub(scratch, Operand(esp, 1 * kPointerSize));
+ if (FLAG_debug_code) {
+ __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
+ __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
+ }
+ __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
+ if (!ReturnTrueFalseObject()) {
+ __ Set(eax, Immediate(0));
+ }
+ }
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+
+ __ bind(&is_not_instance);
+ if (!HasCallSiteInlineCheck()) {
+ __ mov(eax, Immediate(Smi::FromInt(1)));
+ __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ // Get return address and delta to inlined map check.
+ __ mov(eax, factory->false_value());
+ __ mov(scratch, Operand(esp, 0 * kPointerSize));
+ __ sub(scratch, Operand(esp, 1 * kPointerSize));
+ if (FLAG_debug_code) {
+ __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
+ __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
+ }
+ __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
+ if (!ReturnTrueFalseObject()) {
+ __ Set(eax, Immediate(Smi::FromInt(1)));
+ }
+ }
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+
+ Label object_not_null, object_not_null_or_smi;
+ __ bind(&not_js_object);
+ // Before null, smi and string value checks, check that the rhs is a function
+ // as for a non-function rhs an exception needs to be thrown.
+ __ JumpIfSmi(function, &slow, Label::kNear);
+ __ CmpObjectType(function, JS_FUNCTION_TYPE, scratch);
+ __ j(not_equal, &slow, Label::kNear);
+
+ // Null is not instance of anything.
+ __ cmp(object, factory->null_value());
+ __ j(not_equal, &object_not_null, Label::kNear);
+ __ Set(eax, Immediate(Smi::FromInt(1)));
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+
+ __ bind(&object_not_null);
+ // Smi values is not instance of anything.
+ __ JumpIfNotSmi(object, &object_not_null_or_smi, Label::kNear);
+ __ Set(eax, Immediate(Smi::FromInt(1)));
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+
+ __ bind(&object_not_null_or_smi);
+ // String values is not instance of anything.
+ Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
+ __ j(NegateCondition(is_string), &slow, Label::kNear);
+ __ Set(eax, Immediate(Smi::FromInt(1)));
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+
+ // Slow-case: Go through the JavaScript implementation.
+ __ bind(&slow);
+ if (!ReturnTrueFalseObject()) {
+ // Tail call the builtin which returns 0 or 1.
+ if (HasArgsInRegisters()) {
+ // Push arguments below return address.
+ __ pop(scratch);
+ __ push(object);
+ __ push(function);
+ __ push(scratch);
+ }
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
+ } else {
+ // Call the builtin and convert 0/1 to true/false.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(object);
+ __ push(function);
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
+ }
+ Label true_value, done;
+ __ test(eax, eax);
+ __ j(zero, &true_value, Label::kNear);
+ __ mov(eax, factory->false_value());
+ __ jmp(&done, Label::kNear);
+ __ bind(&true_value);
+ __ mov(eax, factory->true_value());
+ __ bind(&done);
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
+ }
+}
+
+
+Register InstanceofStub::left() { return eax; }
+
+
+Register InstanceofStub::right() { return edx; }
+
+
+// -------------------------------------------------------------------------
+// StringCharCodeAtGenerator
+
+void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
+ // If the receiver is a smi trigger the non-string case.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(object_, receiver_not_string_);
+
+ // Fetch the instance type of the receiver into result register.
+ __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
+ __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
+ // If the receiver is not a string trigger the non-string case.
+ __ test(result_, Immediate(kIsNotStringMask));
+ __ j(not_zero, receiver_not_string_);
+
+ // If the index is non-smi trigger the non-smi case.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(index_, &index_not_smi_);
+ __ bind(&got_smi_index_);
+
+ // Check for index out of range.
+ __ cmp(index_, FieldOperand(object_, String::kLengthOffset));
+ __ j(above_equal, index_out_of_range_);
+
+ __ SmiUntag(index_);
+
+ Factory* factory = masm->isolate()->factory();
+ StringCharLoadGenerator::Generate(
+ masm, factory, object_, index_, result_, &call_runtime_);
+
+ __ SmiTag(result_);
+ __ bind(&exit_);
+}
+
+
+void StringCharCodeAtGenerator::GenerateSlow(
+ MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase);
+
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_,
+ masm->isolate()->factory()->heap_number_map(),
+ index_not_number_,
+ DONT_DO_SMI_CHECK);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_); // Consumed by runtime conversion function.
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+ if (!index_.is(eax)) {
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+ __ mov(index_, eax);
+ }
+ __ pop(object_);
+ // Reload the instance type.
+ __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset));
+ __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
+ call_helper.AfterCall(masm);
+ // If index is still not a smi, it must be out of range.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(index_, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ jmp(&got_smi_index_);
+
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // is too complex (e.g., when the string needs to be flattened).
+ __ bind(&call_runtime_);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ SmiTag(index_);
+ __ push(index_);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+ if (!result_.is(eax)) {
+ __ mov(result_, eax);
+ }
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase);
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharFromCodeGenerator
+
+void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiShiftSize == 0);
+ ASSERT(IsPowerOf2(String::kMaxOneByteCharCode + 1));
+ __ test(code_,
+ Immediate(kSmiTagMask |
+ ((~String::kMaxOneByteCharCode) << kSmiTagSize)));
+ __ j(not_zero, &slow_case_);
+
+ Factory* factory = masm->isolate()->factory();
+ __ Set(result_, Immediate(factory->single_character_string_cache()));
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiShiftSize == 0);
+ // At this point code register contains smi tagged ASCII char code.
+ __ mov(result_, FieldOperand(result_,
+ code_, times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(result_, factory->undefined_value());
+ __ j(equal, &slow_case_);
+ __ bind(&exit_);
+}
+
+
+void StringCharFromCodeGenerator::GenerateSlow(
+ MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ __ Abort(kUnexpectedFallthroughToCharFromCodeSlowCase);
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ if (!result_.is(eax)) {
+ __ mov(result_, eax);
+ }
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase);
+}
+
+
+void StringAddStub::Generate(MacroAssembler* masm) {
+ Label call_runtime, call_builtin;
+ Builtins::JavaScript builtin_id = Builtins::ADD;
+
+ // Load the two arguments.
+ __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
+ __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
+
+ // Make sure that both arguments are strings if not known in advance.
+ // Otherwise, at least one of the arguments is definitely a string,
+ // and we convert the one that is not known to be a string.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
+ ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT);
+ ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT);
+ __ JumpIfSmi(eax, &call_runtime);
+ __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
+ __ j(above_equal, &call_runtime);
+
+ // First argument is a a string, test second.
+ __ JumpIfSmi(edx, &call_runtime);
+ __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
+ __ j(above_equal, &call_runtime);
+ } else if ((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
+ ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == 0);
+ GenerateConvertArgument(masm, 2 * kPointerSize, eax, ebx, ecx, edi,
+ &call_builtin);
+ builtin_id = Builtins::STRING_ADD_RIGHT;
+ } else if ((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
+ ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == 0);
+ GenerateConvertArgument(masm, 1 * kPointerSize, edx, ebx, ecx, edi,
+ &call_builtin);
+ builtin_id = Builtins::STRING_ADD_LEFT;
+ }
+
+ // Both arguments are strings.
+ // eax: first string
+ // edx: second string
+ // Check if either of the strings are empty. In that case return the other.
+ Label second_not_zero_length, both_not_zero_length;
+ __ mov(ecx, FieldOperand(edx, String::kLengthOffset));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ 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();
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+ __ bind(&second_not_zero_length);
+ __ mov(ebx, FieldOperand(eax, String::kLengthOffset));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ 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);
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+
+ // Both strings are non-empty.
+ // eax: first string
+ // ebx: length of first string as a smi
+ // ecx: length of second string as a smi
+ // edx: second string
+ // 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, ecx);
+ STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
+ // Handle exceptionally long strings in the runtime system.
+ __ j(overflow, &call_runtime);
+ // Use the string table when adding two one character strings, as it
+ // helps later optimizations to return an internalized string here.
+ __ 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, &call_runtime);
+
+ // Get the two characters forming the new string.
+ __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
+ __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
+
+ // Try to lookup two character string in string table. If it is not found
+ // just allocate a new one.
+ Label make_two_character_string, make_two_character_string_no_reload;
+ StringHelper::GenerateTwoCharacterStringTableProbe(
+ masm, ebx, ecx, eax, edx, edi,
+ &make_two_character_string_no_reload, &make_two_character_string);
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+
+ // Allocate a two character string.
+ __ bind(&make_two_character_string);
+ // Reload the arguments.
+ __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument.
+ __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument.
+ // Get the two characters forming the new string.
+ __ movzx_b(ebx, FieldOperand(eax, SeqOneByteString::kHeaderSize));
+ __ movzx_b(ecx, FieldOperand(edx, SeqOneByteString::kHeaderSize));
+ __ bind(&make_two_character_string_no_reload);
+ __ IncrementCounter(counters->string_add_make_two_char(), 1);
+ __ AllocateAsciiString(eax, 2, edi, edx, &call_runtime);
+ // Pack both characters in ebx.
+ __ shl(ecx, kBitsPerByte);
+ __ or_(ebx, ecx);
+ // Set the characters in the new string.
+ __ mov_w(FieldOperand(eax, SeqOneByteString::kHeaderSize), ebx);
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&longer_than_two);
+ // Check if resulting string will be flat.
+ __ 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.
+ 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, edi);
+ STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
+ STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
+ __ test(ecx, Immediate(kStringEncodingMask));
+ __ j(zero, &non_ascii);
+ __ bind(&ascii_data);
+ // Allocate an ASCII cons string.
+ __ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime);
+ __ bind(&allocated);
+ // Fill the fields of the cons string.
+ __ AssertSmi(ebx);
+ __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
+ __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+
+ Label skip_write_barrier, after_writing;
+ ExternalReference high_promotion_mode = ExternalReference::
+ new_space_high_promotion_mode_active_address(masm->isolate());
+ __ test(Operand::StaticVariable(high_promotion_mode), Immediate(1));
+ __ j(zero, &skip_write_barrier);
+
+ __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
+ __ RecordWriteField(ecx,
+ ConsString::kFirstOffset,
+ eax,
+ ebx,
+ kDontSaveFPRegs);
+ __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
+ __ RecordWriteField(ecx,
+ ConsString::kSecondOffset,
+ edx,
+ ebx,
+ kDontSaveFPRegs);
+ __ jmp(&after_writing);
+
+ __ bind(&skip_write_barrier);
+ __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax);
+ __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx);
+
+ __ bind(&after_writing);
+
+ __ mov(eax, ecx);
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+ __ bind(&non_ascii);
+ // At least one of the strings is two-byte. Check whether it happens
+ // to contain only one byte characters.
+ // ecx: first instance type AND second instance type.
+ // edi: second instance type.
+ __ test(ecx, Immediate(kOneByteDataHintMask));
+ __ j(not_zero, &ascii_data);
+ __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+ __ xor_(edi, ecx);
+ STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
+ __ and_(edi, kOneByteStringTag | kOneByteDataHintTag);
+ __ cmp(edi, kOneByteStringTag | kOneByteDataHintTag);
+ __ j(equal, &ascii_data);
+ // Allocate a two byte cons string.
+ __ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
+ __ jmp(&allocated);
+
+ // 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));
+ // 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(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
+ __ jmp(&first_prepared, Label::kNear);
+ __ bind(&first_is_sequential);
+ __ add(eax, Immediate(SeqOneByteString::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(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
+ __ jmp(&second_prepared, Label::kNear);
+ __ bind(&second_is_sequential);
+ __ add(edx, Immediate(SeqOneByteString::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);
+
+ // Both strings are ASCII strings.
+ // ebx: length of resulting flat string as a smi
+ __ SmiUntag(ebx);
+ __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
+ // eax: result string
+ __ mov(ecx, eax);
+ // Locate first character of result.
+ __ add(ecx, Immediate(SeqOneByteString::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);
+ __ 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'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);
+ __ pop(edx);
+ // eax: result string
+ // ecx: next character of result
+ // edx: first char of second argument
+ // edi: length of second argument
+ StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+
+ // Handle creating a flat two byte result.
+ // eax: first string - known to be two byte
+ // ebx: length of resulting flat string as a smi
+ // edx: second string
+ __ bind(&non_ascii_string_add_flat_result);
+ // Both strings are two byte strings.
+ __ SmiUntag(ebx);
+ __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
+ // eax: result string
+ __ mov(ecx, eax);
+ // Locate first character of result.
+ __ 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);
+ __ 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'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);
+ __ pop(edx);
+ // eax: result string
+ // ecx: next character of result
+ // edx: first char of second argument
+ // edi: length of second argument
+ StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
+ __ 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(&call_runtime);
+ if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
+ GenerateRegisterArgsPop(masm, ecx);
+ // Build a frame
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ __ CallRuntime(Runtime::kStringAdd, 2);
+ }
+ __ ret(0);
+ } else {
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
+ }
+
+ if (call_builtin.is_linked()) {
+ __ bind(&call_builtin);
+ if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
+ GenerateRegisterArgsPop(masm, ecx);
+ // Build a frame
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ __ InvokeBuiltin(builtin_id, CALL_FUNCTION);
+ }
+ __ ret(0);
+ } else {
+ __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
+ }
+ }
+}
+
+
+void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ push(eax);
+ __ push(edx);
+}
+
+
+void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm,
+ Register temp) {
+ __ pop(temp);
+ __ pop(edx);
+ __ pop(eax);
+ __ push(temp);
+}
+
+
+void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* slow) {
+ // First check if the argument is already a string.
+ Label not_string, done;
+ __ JumpIfSmi(arg, &not_string);
+ __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
+ __ j(below, &done);
+
+ // Check the number to string cache.
+ Label not_cached;
+ __ bind(&not_string);
+ // Puts the cached result into scratch1.
+ NumberToStringStub::GenerateLookupNumberStringCache(masm,
+ arg,
+ scratch1,
+ scratch2,
+ scratch3,
+ &not_cached);
+ __ mov(arg, scratch1);
+ __ mov(Operand(esp, stack_offset), arg);
+ __ jmp(&done);
+
+ // Check if the argument is a safe string wrapper.
+ __ bind(&not_cached);
+ __ JumpIfSmi(arg, slow);
+ __ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
+ __ j(not_equal, slow);
+ __ test_b(FieldOperand(scratch1, Map::kBitField2Offset),
+ 1 << Map::kStringWrapperSafeForDefaultValueOf);
+ __ j(zero, slow);
+ __ mov(arg, FieldOperand(arg, JSValue::kValueOffset));
+ __ mov(Operand(esp, stack_offset), arg);
+
+ __ bind(&done);
+}
+
+
+void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii) {
+ Label loop;
+ __ bind(&loop);
+ // This loop just copies one character at a time, as it is only used for very
+ // short strings.
+ if (ascii) {
+ __ mov_b(scratch, Operand(src, 0));
+ __ mov_b(Operand(dest, 0), scratch);
+ __ add(src, Immediate(1));
+ __ add(dest, Immediate(1));
+ } else {
+ __ mov_w(scratch, Operand(src, 0));
+ __ mov_w(Operand(dest, 0), scratch);
+ __ add(src, Immediate(2));
+ __ add(dest, Immediate(2));
+ }
+ __ sub(count, Immediate(1));
+ __ j(not_zero, &loop);
+}
+
+
+void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii) {
+ // Copy characters using rep movs of doublewords.
+ // The destination is aligned on a 4 byte boundary because we are
+ // copying to the beginning of a newly allocated string.
+ ASSERT(dest.is(edi)); // rep movs destination
+ ASSERT(src.is(esi)); // rep movs source
+ ASSERT(count.is(ecx)); // rep movs count
+ ASSERT(!scratch.is(dest));
+ ASSERT(!scratch.is(src));
+ ASSERT(!scratch.is(count));
+
+ // Nothing to do for zero characters.
+ Label done;
+ __ test(count, count);
+ __ j(zero, &done);
+
+ // Make count the number of bytes to copy.
+ if (!ascii) {
+ __ shl(count, 1);
+ }
+
+ // Don't enter the rep movs if there are less than 4 bytes to copy.
+ Label last_bytes;
+ __ test(count, Immediate(~3));
+ __ j(zero, &last_bytes, Label::kNear);
+
+ // Copy from edi to esi using rep movs instruction.
+ __ mov(scratch, count);
+ __ sar(count, 2); // Number of doublewords to copy.
+ __ cld();
+ __ rep_movs();
+
+ // Find number of bytes left.
+ __ mov(count, scratch);
+ __ and_(count, 3);
+
+ // Check if there are more bytes to copy.
+ __ bind(&last_bytes);
+ __ test(count, count);
+ __ j(zero, &done);
+
+ // Copy remaining characters.
+ Label loop;
+ __ bind(&loop);
+ __ mov_b(scratch, Operand(src, 0));
+ __ mov_b(Operand(dest, 0), scratch);
+ __ add(src, Immediate(1));
+ __ add(dest, Immediate(1));
+ __ sub(count, Immediate(1));
+ __ j(not_zero, &loop);
+
+ __ bind(&done);
+}
+
+
+void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_probed,
+ Label* not_found) {
+ // Register scratch3 is the general scratch register in this function.
+ Register scratch = scratch3;
+
+ // Make sure that both characters are not digits as such strings has a
+ // different hash algorithm. Don't try to look for these in the string table.
+ Label not_array_index;
+ __ mov(scratch, c1);
+ __ 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(scratch, Immediate(static_cast<int>('0')));
+ __ cmp(scratch, Immediate(static_cast<int>('9' - '0')));
+ __ j(below_equal, not_probed);
+
+ __ bind(&not_array_index);
+ // Calculate the two character string hash.
+ Register hash = scratch1;
+ GenerateHashInit(masm, hash, c1, scratch);
+ GenerateHashAddCharacter(masm, hash, c2, scratch);
+ GenerateHashGetHash(masm, hash, scratch);
+
+ // Collect the two characters in a register.
+ Register chars = c1;
+ __ shl(c2, kBitsPerByte);
+ __ 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 string table.
+ Register string_table = c2;
+ __ LoadRoot(string_table, Heap::kStringTableRootIndex);
+
+ // Calculate capacity mask from the string table capacity.
+ Register mask = scratch2;
+ __ mov(mask, FieldOperand(string_table, StringTable::kCapacityOffset));
+ __ SmiUntag(mask);
+ __ sub(mask, Immediate(1));
+
+ // Registers
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string
+ // string_table: string table
+ // mask: capacity mask
+ // scratch: -
+
+ // Perform a number of probes in the string table.
+ static const int kProbes = 4;
+ Label found_in_string_table;
+ Label next_probe[kProbes], next_probe_pop_mask[kProbes];
+ Register candidate = scratch; // Scratch register contains candidate.
+ for (int i = 0; i < kProbes; i++) {
+ // Calculate entry in string table.
+ __ mov(scratch, hash);
+ if (i > 0) {
+ __ add(scratch, Immediate(StringTable::GetProbeOffset(i)));
+ }
+ __ and_(scratch, mask);
+
+ // Load the entry from the string table.
+ STATIC_ASSERT(StringTable::kEntrySize == 1);
+ __ mov(candidate,
+ FieldOperand(string_table,
+ scratch,
+ times_pointer_size,
+ StringTable::kElementsStartOffset));
+
+ // If entry is undefined no string with this hash can be found.
+ Factory* factory = masm->isolate()->factory();
+ __ cmp(candidate, factory->undefined_value());
+ __ j(equal, not_found);
+ __ cmp(candidate, factory->the_hole_value());
+ __ j(equal, &next_probe[i]);
+
+ // If length is not 2 the string is not a candidate.
+ __ cmp(FieldOperand(candidate, String::kLengthOffset),
+ Immediate(Smi::FromInt(2)));
+ __ j(not_equal, &next_probe[i]);
+
+ // As we are out of registers save the mask on the stack and use that
+ // register as a temporary.
+ __ push(mask);
+ Register temp = mask;
+
+ // Check that the candidate is a non-external ASCII string.
+ __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset));
+ __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(
+ temp, temp, &next_probe_pop_mask[i]);
+
+ // Check if the two characters match.
+ __ mov(temp, FieldOperand(candidate, SeqOneByteString::kHeaderSize));
+ __ and_(temp, 0x0000ffff);
+ __ cmp(chars, temp);
+ __ j(equal, &found_in_string_table);
+ __ bind(&next_probe_pop_mask[i]);
+ __ pop(mask);
+ __ bind(&next_probe[i]);
+ }
+
+ // No matching 2 character string found by probing.
+ __ jmp(not_found);
+
+ // Scratch register contains result when we fall through to here.
+ Register result = candidate;
+ __ bind(&found_in_string_table);
+ __ pop(mask); // Pop saved mask from the stack.
+ if (!result.is(eax)) {
+ __ mov(eax, result);
+ }
+}
+
+
+void StringHelper::GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
+ // hash = (seed + character) + ((seed + character) << 10);
+ if (Serializer::enabled()) {
+ __ LoadRoot(scratch, Heap::kHashSeedRootIndex);
+ __ SmiUntag(scratch);
+ __ add(scratch, character);
+ __ mov(hash, scratch);
+ __ shl(scratch, 10);
+ __ add(hash, scratch);
+ } else {
+ int32_t seed = masm->isolate()->heap()->HashSeed();
+ __ lea(scratch, Operand(character, seed));
+ __ shl(scratch, 10);
+ __ lea(hash, Operand(scratch, character, times_1, seed));
+ }
+ // hash ^= hash >> 6;
+ __ mov(scratch, hash);
+ __ shr(scratch, 6);
+ __ xor_(hash, scratch);
+}
+
+
+void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
+ // hash += character;
+ __ add(hash, character);
+ // hash += hash << 10;
+ __ mov(scratch, hash);
+ __ shl(scratch, 10);
+ __ add(hash, scratch);
+ // hash ^= hash >> 6;
+ __ mov(scratch, hash);
+ __ shr(scratch, 6);
+ __ xor_(hash, scratch);
+}
+
+
+void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch) {
+ // hash += hash << 3;
+ __ mov(scratch, hash);
+ __ shl(scratch, 3);
+ __ add(hash, scratch);
+ // hash ^= hash >> 11;
+ __ mov(scratch, hash);
+ __ shr(scratch, 11);
+ __ xor_(hash, scratch);
+ // hash += hash << 15;
+ __ mov(scratch, hash);
+ __ shl(scratch, 15);
+ __ add(hash, scratch);
+
+ __ and_(hash, String::kHashBitMask);
+
+ // if (hash == 0) hash = 27;
+ Label hash_not_zero;
+ __ j(not_zero, &hash_not_zero, Label::kNear);
+ __ mov(hash, Immediate(StringHasher::kZeroHash));
+ __ bind(&hash_not_zero);
+}
+
+
+void SubStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ // Stack frame on entry.
+ // esp[0]: return address
+ // esp[4]: to
+ // esp[8]: from
+ // esp[12]: string
+
+ // Make sure first argument is a string.
+ __ mov(eax, Operand(esp, 3 * kPointerSize));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(eax, &runtime);
+ Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
+ __ j(NegateCondition(is_string), &runtime);
+
+ // eax: string
+ // ebx: instance type
+
+ // Calculate length of sub string using the smi values.
+ __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
+ __ JumpIfNotSmi(ecx, &runtime);
+ __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
+ __ JumpIfNotSmi(edx, &runtime);
+ __ sub(ecx, edx);
+ __ cmp(ecx, FieldOperand(eax, String::kLengthOffset));
+ Label not_original_string;
+ // Shorter than original string's length: an actual substring.
+ __ j(below, &not_original_string, Label::kNear);
+ // Longer than original string's length or negative: unsafe arguments.
+ __ j(above, &runtime);
+ // Return original string.
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(3 * kPointerSize);
+ __ bind(&not_original_string);
+
+ Label single_char;
+ __ cmp(ecx, Immediate(Smi::FromInt(1)));
+ __ j(equal, &single_char);
+
+ // 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));
+ __ 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;
+ // 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);
+ // 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 & kOneByteStringTag) != 0);
+ STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
+ __ test(ebx, Immediate(kStringEncodingMask));
+ __ j(zero, &two_byte_slice, Label::kNear);
+ __ AllocateAsciiSlicedString(eax, ebx, no_reg, &runtime);
+ __ jmp(&set_slice_header, Label::kNear);
+ __ bind(&two_byte_slice);
+ __ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime);
+ __ bind(&set_slice_header);
+ __ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx);
+ __ mov(FieldOperand(eax, SlicedString::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+ __ mov(FieldOperand(eax, SlicedString::kParentOffset), edi);
+ __ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx);
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(3 * kPointerSize);
+
+ __ bind(&copy_routine);
+ }
+
+ // 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 == SeqOneByteString::kHeaderSize);
+ __ sub(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+
+ __ bind(&sequential_string);
+ // Stash away (adjusted) index and (underlying) string.
+ __ push(edx);
+ __ push(edi);
+ __ SmiUntag(ecx);
+ STATIC_ASSERT((kOneByteStringTag & 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(edi, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ // Load string argument and locate character of sub string start.
+ __ pop(esi);
+ __ pop(ebx);
+ __ SmiUntag(ebx);
+ __ lea(esi, FieldOperand(esi, ebx, times_1, SeqOneByteString::kHeaderSize));
+
+ // eax: result string
+ // ecx: result length
+ // edx: original value of esi
+ // edi: first character of result
+ // esi: character of sub string start
+ StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
+ __ mov(esi, edx); // Restore esi.
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(3 * kPointerSize);
+
+ __ 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(edi,
+ Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // Load string argument and locate character of sub string start.
+ __ 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);
+ __ lea(esi, FieldOperand(esi, ebx, times_1, SeqTwoByteString::kHeaderSize));
+
+ // eax: result string
+ // ecx: result length
+ // edx: original value of esi
+ // edi: first character of result
+ // esi: character of sub string start
+ StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
+ __ mov(esi, edx); // Restore esi.
+ __ 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);
+
+ __ bind(&single_char);
+ // eax: string
+ // ebx: instance type
+ // ecx: sub string length (smi)
+ // edx: from index (smi)
+ StringCharAtGenerator generator(
+ eax, edx, ecx, eax, &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm);
+ __ ret(3 * kPointerSize);
+ generator.SkipSlow(masm, &runtime);
+}
+
+
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label strings_not_equal, check_zero_length;
+ __ mov(length, FieldOperand(left, String::kLengthOffset));
+ __ cmp(length, FieldOperand(right, String::kLengthOffset));
+ __ j(equal, &check_zero_length, Label::kNear);
+ __ bind(&strings_not_equal);
+ __ Set(eax, Immediate(Smi::FromInt(NOT_EQUAL)));
+ __ ret(0);
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ test(length, length);
+ __ j(not_zero, &compare_chars, Label::kNear);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+
+ // Compare characters.
+ __ bind(&compare_chars);
+ GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
+ &strings_not_equal, Label::kNear);
+
+ // Characters are equal.
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+}
+
+
+void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3) {
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->string_compare_native(), 1);
+
+ // Find minimum length.
+ Label left_shorter;
+ __ mov(scratch1, FieldOperand(left, String::kLengthOffset));
+ __ mov(scratch3, scratch1);
+ __ sub(scratch3, FieldOperand(right, String::kLengthOffset));
+
+ Register length_delta = scratch3;
+
+ __ j(less_equal, &left_shorter, Label::kNear);
+ // Right string is shorter. Change scratch1 to be length of right string.
+ __ 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, min_length);
+ __ j(zero, &compare_lengths, Label::kNear);
+
+ // Compare characters.
+ Label result_not_equal;
+ GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
+ &result_not_equal, Label::kNear);
+
+ // Compare lengths - strings up to min-length are equal.
+ __ bind(&compare_lengths);
+ __ test(length_delta, length_delta);
+ Label length_not_equal;
+ __ j(not_zero, &length_not_equal, Label::kNear);
+
+ // Result is EQUAL.
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+
+ Label result_greater;
+ Label result_less;
+ __ bind(&length_not_equal);
+ __ j(greater, &result_greater, Label::kNear);
+ __ jmp(&result_less, Label::kNear);
+ __ bind(&result_not_equal);
+ __ j(above, &result_greater, Label::kNear);
+ __ bind(&result_less);
+
+ // Result is LESS.
+ __ Set(eax, Immediate(Smi::FromInt(LESS)));
+ __ ret(0);
+
+ // Result is GREATER.
+ __ bind(&result_greater);
+ __ Set(eax, Immediate(Smi::FromInt(GREATER)));
+ __ ret(0);
+}
+
+
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance chars_not_equal_near) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiUntag(length);
+ __ lea(left,
+ FieldOperand(left, length, times_1, SeqOneByteString::kHeaderSize));
+ __ lea(right,
+ FieldOperand(right, length, times_1, SeqOneByteString::kHeaderSize));
+ __ neg(length);
+ Register index = length; // index = -length;
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ 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);
+ __ inc(index);
+ __ j(not_zero, &loop);
+}
+
+
+void StringCompareStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ // Stack frame on entry.
+ // esp[0]: return address
+ // esp[4]: right string
+ // esp[8]: left string
+
+ __ mov(edx, Operand(esp, 2 * kPointerSize)); // left
+ __ mov(eax, Operand(esp, 1 * kPointerSize)); // right
+
+ Label not_same;
+ __ cmp(edx, eax);
+ __ j(not_equal, &not_same, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ IncrementCounter(masm->isolate()->counters()->string_compare_native(), 1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&not_same);
+
+ // Check that both objects are sequential ASCII strings.
+ __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
+
+ // Compare flat ASCII strings.
+ // Drop arguments from the stack.
+ __ pop(ecx);
+ __ add(esp, Immediate(2 * kPointerSize));
+ __ push(ecx);
+ GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
+
+ // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+}
+
+
+void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SMI);
+ Label miss;
+ __ 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, edx);
+ } else {
+ Label done;
+ __ sub(edx, eax);
+ __ j(no_overflow, &done, Label::kNear);
+ // Correct sign of result in case of overflow.
+ __ not_(edx);
+ __ bind(&done);
+ __ mov(eax, edx);
+ }
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateNumbers(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::NUMBER);
+
+ Label generic_stub;
+ Label unordered, maybe_undefined1, maybe_undefined2;
+ Label miss;
+
+ if (left_ == CompareIC::SMI) {
+ __ JumpIfNotSmi(edx, &miss);
+ }
+ if (right_ == CompareIC::SMI) {
+ __ JumpIfNotSmi(eax, &miss);
+ }
+
+ // Inlining the double comparison and falling back to the general compare
+ // stub if NaN is involved or SSE2 or CMOV is unsupported.
+ if (CpuFeatures::IsSupported(SSE2) && CpuFeatures::IsSupported(CMOV)) {
+ CpuFeatureScope scope1(masm, SSE2);
+ CpuFeatureScope scope2(masm, CMOV);
+
+ // Load left and right operand.
+ Label done, left, left_smi, right_smi;
+ __ JumpIfSmi(eax, &right_smi, Label::kNear);
+ __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
+ masm->isolate()->factory()->heap_number_map());
+ __ j(not_equal, &maybe_undefined1, Label::kNear);
+ __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
+ __ jmp(&left, Label::kNear);
+ __ bind(&right_smi);
+ __ mov(ecx, eax); // Can't clobber eax because we can still jump away.
+ __ SmiUntag(ecx);
+ __ cvtsi2sd(xmm1, ecx);
+
+ __ bind(&left);
+ __ JumpIfSmi(edx, &left_smi, Label::kNear);
+ __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
+ masm->isolate()->factory()->heap_number_map());
+ __ j(not_equal, &maybe_undefined2, Label::kNear);
+ __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
+ __ jmp(&done);
+ __ bind(&left_smi);
+ __ mov(ecx, edx); // Can't clobber edx because we can still jump away.
+ __ SmiUntag(ecx);
+ __ cvtsi2sd(xmm0, ecx);
+
+ __ bind(&done);
+ // Compare operands.
+ __ ucomisd(xmm0, xmm1);
+
+ // Don't base result on EFLAGS when a NaN is involved.
+ __ j(parity_even, &unordered, Label::kNear);
+
+ // Return a result of -1, 0, or 1, based on EFLAGS.
+ // Performing mov, because xor would destroy the flag register.
+ __ mov(eax, 0); // equal
+ __ mov(ecx, Immediate(Smi::FromInt(1)));
+ __ cmov(above, eax, ecx);
+ __ mov(ecx, Immediate(Smi::FromInt(-1)));
+ __ cmov(below, eax, ecx);
+ __ ret(0);
+ } else {
+ __ mov(ecx, edx);
+ __ and_(ecx, eax);
+ __ JumpIfSmi(ecx, &generic_stub, Label::kNear);
+
+ __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
+ masm->isolate()->factory()->heap_number_map());
+ __ j(not_equal, &maybe_undefined1, Label::kNear);
+ __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
+ masm->isolate()->factory()->heap_number_map());
+ __ j(not_equal, &maybe_undefined2, Label::kNear);
+ }
+
+ __ bind(&unordered);
+ __ bind(&generic_stub);
+ ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC,
+ CompareIC::GENERIC);
+ __ jmp(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+
+ __ bind(&maybe_undefined1);
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ __ cmp(eax, Immediate(masm->isolate()->factory()->undefined_value()));
+ __ j(not_equal, &miss);
+ __ JumpIfSmi(edx, &unordered);
+ __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx);
+ __ j(not_equal, &maybe_undefined2, Label::kNear);
+ __ jmp(&unordered);
+ }
+
+ __ bind(&maybe_undefined2);
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ __ cmp(edx, Immediate(masm->isolate()->factory()->undefined_value()));
+ __ j(equal, &unordered);
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::INTERNALIZED_STRING);
+ ASSERT(GetCondition() == equal);
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+ Register tmp1 = ecx;
+ Register tmp2 = ebx;
+
+ // Check that both operands are heap objects.
+ Label miss;
+ __ mov(tmp1, left);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ and_(tmp1, right);
+ __ JumpIfSmi(tmp1, &miss, Label::kNear);
+
+ // Check that both operands are internalized strings.
+ __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ or_(tmp1, tmp2);
+ __ test(tmp1, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
+ __ j(not_zero, &miss, Label::kNear);
+
+ // Internalized strings are compared by identity.
+ Label done;
+ __ cmp(left, right);
+ // Make sure eax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(eax));
+ __ j(not_equal, &done, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ bind(&done);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::UNIQUE_NAME);
+ ASSERT(GetCondition() == equal);
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+ Register tmp1 = ecx;
+ Register tmp2 = ebx;
+
+ // Check that both operands are heap objects.
+ Label miss;
+ __ mov(tmp1, left);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ and_(tmp1, right);
+ __ JumpIfSmi(tmp1, &miss, Label::kNear);
+
+ // Check that both operands are unique names. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+
+ __ JumpIfNotUniqueName(tmp1, &miss, Label::kNear);
+ __ JumpIfNotUniqueName(tmp2, &miss, Label::kNear);
+
+ // Unique names are compared by identity.
+ Label done;
+ __ cmp(left, right);
+ // Make sure eax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(eax));
+ __ j(not_equal, &done, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ bind(&done);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRING);
+ Label miss;
+
+ bool equality = Token::IsEqualityOp(op_);
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+ Register tmp1 = ecx;
+ Register tmp2 = ebx;
+ Register tmp3 = edi;
+
+ // Check that both operands are heap objects.
+ __ mov(tmp1, left);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ and_(tmp1, right);
+ __ JumpIfSmi(tmp1, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ __ mov(tmp3, tmp1);
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ or_(tmp3, tmp2);
+ __ test(tmp3, Immediate(kIsNotStringMask));
+ __ j(not_zero, &miss);
+
+ // Fast check for identical strings.
+ Label not_same;
+ __ cmp(left, right);
+ __ j(not_equal, &not_same, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+
+ // Handle not identical strings.
+ __ bind(&not_same);
+
+ // Check that both strings are internalized. If they are, we're done
+ // because we already know they are not identical. But in the case of
+ // non-equality compare, we still need to determine the order. We
+ // also know they are both strings.
+ if (equality) {
+ Label do_compare;
+ STATIC_ASSERT(kInternalizedTag == 0);
+ __ or_(tmp1, tmp2);
+ __ test(tmp1, Immediate(kIsNotInternalizedMask));
+ __ j(not_zero, &do_compare, Label::kNear);
+ // Make sure eax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(eax));
+ __ ret(0);
+ __ bind(&do_compare);
+ }
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ if (equality) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(
+ masm, left, right, tmp1, tmp2, tmp3);
+ }
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ pop(tmp1); // Return address.
+ __ push(left);
+ __ push(right);
+ __ push(tmp1);
+ if (equality) {
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ } else {
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::OBJECT);
+ Label miss;
+ __ mov(ecx, edx);
+ __ and_(ecx, eax);
+ __ JumpIfSmi(ecx, &miss, Label::kNear);
+
+ __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx);
+ __ j(not_equal, &miss, Label::kNear);
+ __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
+ __ j(not_equal, &miss, Label::kNear);
+
+ ASSERT(GetCondition() == equal);
+ __ sub(eax, edx);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) {
+ Label miss;
+ __ mov(ecx, edx);
+ __ and_(ecx, eax);
+ __ JumpIfSmi(ecx, &miss, Label::kNear);
+
+ __ 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);
+
+ __ sub(eax, edx);
+ __ ret(0);
+
+ __ 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(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(edi);
+}
+
+
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// 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 unique name and receiver must be a heap object.
+void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ Handle<Name> name,
+ Register r0) {
+ ASSERT(name->IsUniqueName());
+
+ // 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
+ // (their names are the hole value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = r0;
+ // Capacity is smi 2^n.
+ __ mov(index, FieldOperand(properties, kCapacityOffset));
+ __ dec(index);
+ __ and_(index,
+ Immediate(Smi::FromInt(name->Hash() +
+ NameDictionary::GetProbeOffset(i))));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
+ Register entity_name = r0;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ cmp(entity_name, masm->isolate()->factory()->undefined_value());
+ __ j(equal, done);
+
+ // Stop if found the property.
+ __ cmp(entity_name, Handle<Name>(name));
+ __ j(equal, miss);
+
+ Label good;
+ // Check for the hole and skip.
+ __ cmp(entity_name, masm->isolate()->factory()->the_hole_value());
+ __ j(equal, &good, Label::kNear);
+
+ // Check if the entry name is not a unique name.
+ __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
+ __ JumpIfNotUniqueName(FieldOperand(entity_name, Map::kInstanceTypeOffset),
+ miss);
+ __ bind(&good);
+ }
+
+ NameDictionaryLookupStub stub(properties, r0, r0, NEGATIVE_LOOKUP);
+ __ push(Immediate(Handle<Object>(name)));
+ __ push(Immediate(name->Hash()));
+ __ CallStub(&stub);
+ __ test(r0, r0);
+ __ j(not_zero, miss);
+ __ jmp(done);
+}
+
+
+// Probe the name dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found leaving the
+// index into the dictionary in |r0|. Jump to the |miss| label
+// otherwise.
+void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1) {
+ ASSERT(!elements.is(r0));
+ ASSERT(!elements.is(r1));
+ ASSERT(!name.is(r0));
+ ASSERT(!name.is(r1));
+
+ __ AssertName(name);
+
+ __ mov(r1, FieldOperand(elements, kCapacityOffset));
+ __ shr(r1, kSmiTagSize); // convert smi to int
+ __ dec(r1);
+
+ // Generate an unrolled loop that performs a few probes before
+ // giving up. Measurements done on Gmail indicate that 2 probes
+ // cover ~93% of loads from dictionaries.
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
+ __ shr(r0, Name::kHashShift);
+ if (i > 0) {
+ __ add(r0, Immediate(NameDictionary::GetProbeOffset(i)));
+ }
+ __ and_(r0, r1);
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
+
+ // Check if the key is identical to the name.
+ __ cmp(name, Operand(elements,
+ r0,
+ times_4,
+ kElementsStartOffset - kHeapObjectTag));
+ __ j(equal, done);
+ }
+
+ NameDictionaryLookupStub stub(elements, r1, r0, POSITIVE_LOOKUP);
+ __ push(name);
+ __ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
+ __ shr(r0, Name::kHashShift);
+ __ push(r0);
+ __ CallStub(&stub);
+
+ __ test(r1, r1);
+ __ j(zero, miss);
+ __ jmp(done);
+}
+
+
+void NameDictionaryLookupStub::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.
+ // esp[2 * kPointerSize]: key.
+ // Registers:
+ // dictionary_: NameDictionary to probe.
+ // result_: used as scratch.
+ // index_: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ Register scratch = result_;
+
+ __ mov(scratch, FieldOperand(dictionary_, kCapacityOffset));
+ __ dec(scratch);
+ __ SmiUntag(scratch);
+ __ push(scratch);
+
+ // 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
+ // (their names are the null value).
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ mov(scratch, Operand(esp, 2 * kPointerSize));
+ if (i > 0) {
+ __ add(scratch, Immediate(NameDictionary::GetProbeOffset(i)));
+ }
+ __ and_(scratch, Operand(esp, 0));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
+
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ mov(scratch, Operand(dictionary_,
+ index_,
+ times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ cmp(scratch, masm->isolate()->factory()->undefined_value());
+ __ j(equal, &not_in_dictionary);
+
+ // Stop if found the property.
+ __ cmp(scratch, Operand(esp, 3 * kPointerSize));
+ __ j(equal, &in_dictionary);
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // If we hit a key that is not a unique name during negative
+ // lookup we have to bailout as this key might be equal to the
+ // key we are looking for.
+
+ // Check if the entry name is not a unique name.
+ __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
+ __ JumpIfNotUniqueName(FieldOperand(scratch, Map::kInstanceTypeOffset),
+ &maybe_in_dictionary);
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ mov(result_, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+ }
+
+ __ bind(&in_dictionary);
+ __ mov(result_, Immediate(1));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&not_in_dictionary);
+ __ mov(result_, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+}
+
+
+struct AheadOfTimeWriteBarrierStubList {
+ Register object, value, address;
+ RememberedSetAction action;
+};
+
+
+#define REG(Name) { kRegister_ ## Name ## _Code }
+
+static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
+ // Used in RegExpExecStub.
+ { REG(ebx), REG(eax), REG(edi), EMIT_REMEMBERED_SET },
+ // Used in CompileArrayPushCall.
+ { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
+ { REG(ebx), REG(edi), REG(edx), OMIT_REMEMBERED_SET },
+ // Used in CompileStoreGlobal and CallFunctionStub.
+ { REG(ebx), REG(ecx), REG(edx), OMIT_REMEMBERED_SET },
+ // Used in StoreStubCompiler::CompileStoreField and
+ // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
+ { REG(edx), REG(ecx), REG(ebx), EMIT_REMEMBERED_SET },
+ // GenerateStoreField calls the stub with two different permutations of
+ // registers. This is the second.
+ { REG(ebx), REG(ecx), REG(edx), EMIT_REMEMBERED_SET },
+ // StoreIC::GenerateNormal via GenerateDictionaryStore
+ { REG(ebx), REG(edi), REG(edx), EMIT_REMEMBERED_SET },
+ // KeyedStoreIC::GenerateGeneric.
+ { REG(ebx), REG(edx), REG(ecx), EMIT_REMEMBERED_SET},
+ // KeyedStoreStubCompiler::GenerateStoreFastElement.
+ { REG(edi), REG(ebx), REG(ecx), EMIT_REMEMBERED_SET},
+ { REG(edx), REG(edi), REG(ebx), EMIT_REMEMBERED_SET},
+ // ElementsTransitionGenerator::GenerateMapChangeElementTransition
+ // and ElementsTransitionGenerator::GenerateSmiToDouble
+ // and ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(edx), REG(ebx), REG(edi), EMIT_REMEMBERED_SET},
+ { REG(edx), REG(ebx), REG(edi), OMIT_REMEMBERED_SET},
+ // ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(eax), REG(edx), REG(esi), EMIT_REMEMBERED_SET},
+ { REG(edx), REG(eax), REG(edi), EMIT_REMEMBERED_SET},
+ // StoreArrayLiteralElementStub::Generate
+ { REG(ebx), REG(eax), REG(ecx), EMIT_REMEMBERED_SET},
+ // FastNewClosureStub and StringAddStub::Generate
+ { REG(ecx), REG(edx), REG(ebx), EMIT_REMEMBERED_SET},
+ // StringAddStub::Generate
+ { REG(ecx), REG(eax), REG(ebx), EMIT_REMEMBERED_SET},
+ // Null termination.
+ { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
+};
+
+#undef REG
+
+bool RecordWriteStub::IsPregenerated() {
+ for (const 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(
+ Isolate* isolate) {
+ StoreBufferOverflowStub stub(kDontSaveFPRegs);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
+ StoreBufferOverflowStub stub2(kSaveFPRegs);
+ stub2.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+void RecordWriteStub::GenerateFixedRegStubsAheadOfTime(Isolate* isolate) {
+ for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
+ !entry->object.is(no_reg);
+ entry++) {
+ RecordWriteStub stub(entry->object,
+ entry->value,
+ entry->address,
+ entry->action,
+ kDontSaveFPRegs);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+bool CodeStub::CanUseFPRegisters() {
+ return CpuFeatures::IsSupported(SSE2);
+}
+
+
+// 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());
+ __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot.
+ __ mov(Operand(esp, 2 * kPointerSize),
+ Immediate(ExternalReference::isolate_address(masm->isolate())));
+
+ 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;
+
+ __ mov(regs_.scratch0(), Immediate(~Page::kPageAlignmentMask));
+ __ and_(regs_.scratch0(), regs_.object());
+ __ mov(regs_.scratch1(),
+ Operand(regs_.scratch0(),
+ MemoryChunk::kWriteBarrierCounterOffset));
+ __ sub(regs_.scratch1(), Immediate(1));
+ __ mov(Operand(regs_.scratch0(),
+ MemoryChunk::kWriteBarrierCounterOffset),
+ regs_.scratch1());
+ __ j(negative, &need_incremental);
+
+ // 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
+ // -- ecx : element index as smi
+ // -- esp[0] : return address
+ // -- esp[4] : array literal index in function
+ // -- esp[8] : array literal
+ // clobbers ebx, edx, edi
+ // -----------------------------------
+
+ Label element_done;
+ Label double_elements;
+ Label smi_element;
+ Label slow_elements;
+ Label slow_elements_from_double;
+ Label fast_elements;
+
+ // Get array literal index, array literal and its map.
+ __ mov(edx, Operand(esp, 1 * kPointerSize));
+ __ mov(ebx, Operand(esp, 2 * kPointerSize));
+ __ mov(edi, FieldOperand(ebx, JSObject::kMapOffset));
+
+ __ CheckFastElements(edi, &double_elements);
+
+ // Check for FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS elements
+ __ JumpIfSmi(eax, &smi_element);
+ __ CheckFastSmiElements(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_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);
+}
+
+
+void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
+ CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs);
+ __ call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+ int parameter_count_offset =
+ StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
+ __ mov(ebx, MemOperand(ebp, parameter_count_offset));
+ masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE);
+ __ pop(ecx);
+ int additional_offset = function_mode_ == JS_FUNCTION_STUB_MODE
+ ? kPointerSize
+ : 0;
+ __ lea(esp, MemOperand(esp, ebx, times_pointer_size, additional_offset));
+ __ jmp(ecx); // Return to IC Miss stub, continuation still on stack.
+}
+
+
+void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
+ if (masm->isolate()->function_entry_hook() != NULL) {
+ // It's always safe to call the entry hook stub, as the hook itself
+ // is not allowed to call back to V8.
+ AllowStubCallsScope allow_stub_calls(masm, true);
+
+ ProfileEntryHookStub stub;
+ masm->CallStub(&stub);
+ }
+}
+
+
+void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
+ // Save volatile registers.
+ const int kNumSavedRegisters = 3;
+ __ push(eax);
+ __ push(ecx);
+ __ push(edx);
+
+ // Calculate and push the original stack pointer.
+ __ lea(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
+ __ push(eax);
+
+ // Retrieve our return address and use it to calculate the calling
+ // function's address.
+ __ mov(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize));
+ __ sub(eax, Immediate(Assembler::kCallInstructionLength));
+ __ push(eax);
+
+ // Call the entry hook.
+ ASSERT(masm->isolate()->function_entry_hook() != NULL);
+ __ call(FUNCTION_ADDR(masm->isolate()->function_entry_hook()),
+ RelocInfo::RUNTIME_ENTRY);
+ __ add(esp, Immediate(2 * kPointerSize));
+
+ // Restore ecx.
+ __ pop(edx);
+ __ pop(ecx);
+ __ pop(eax);
+
+ __ ret(0);
+}
+
+
+template<class T>
+static void CreateArrayDispatch(MacroAssembler* masm) {
+ int last_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= last_index; ++i) {
+ Label next;
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ __ cmp(edx, kind);
+ __ j(not_equal, &next);
+ T stub(kind);
+ __ TailCallStub(&stub);
+ __ bind(&next);
+ }
+
+ // If we reached this point there is a problem.
+ __ Abort(kUnexpectedElementsKindInArrayConstructor);
+}
+
+
+static void CreateArrayDispatchOneArgument(MacroAssembler* masm) {
+ // ebx - type info cell
+ // edx - kind
+ // eax - number of arguments
+ // edi - constructor?
+ // esp[0] - return address
+ // esp[4] - last argument
+ ASSERT(FAST_SMI_ELEMENTS == 0);
+ ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ ASSERT(FAST_ELEMENTS == 2);
+ ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ ASSERT(FAST_DOUBLE_ELEMENTS == 4);
+ ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5);
+
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(),
+ masm->isolate());
+
+ // is the low bit set? If so, we are holey and that is good.
+ __ test_b(edx, 1);
+ Label normal_sequence;
+ __ j(not_zero, &normal_sequence);
+
+ // look at the first argument
+ __ mov(ecx, Operand(esp, kPointerSize));
+ __ test(ecx, ecx);
+ __ j(zero, &normal_sequence);
+
+ // We are going to create a holey array, but our kind is non-holey.
+ // Fix kind and retry (only if we have an allocation site in the cell).
+ __ inc(edx);
+ __ cmp(ebx, Immediate(undefined_sentinel));
+ __ j(equal, &normal_sequence);
+ __ mov(ecx, FieldOperand(ebx, Cell::kValueOffset));
+ Handle<Map> allocation_site_map(
+ masm->isolate()->heap()->allocation_site_map(),
+ masm->isolate());
+ __ cmp(FieldOperand(ecx, 0), Immediate(allocation_site_map));
+ __ j(not_equal, &normal_sequence);
+
+ // Save the resulting elements kind in type info
+ __ SmiTag(edx);
+ __ mov(FieldOperand(ecx, AllocationSite::kTransitionInfoOffset), edx);
+ __ SmiUntag(edx);
+
+ __ bind(&normal_sequence);
+ int last_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= last_index; ++i) {
+ Label next;
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ __ cmp(edx, kind);
+ __ j(not_equal, &next);
+ ArraySingleArgumentConstructorStub stub(kind);
+ __ TailCallStub(&stub);
+ __ bind(&next);
+ }
+
+ // If we reached this point there is a problem.
+ __ Abort(kUnexpectedElementsKindInArrayConstructor);
+}
+
+
+template<class T>
+static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) {
+ int to_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= to_index; ++i) {
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ T stub(kind);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ if (AllocationSite::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) {
+ T stub1(kind, CONTEXT_CHECK_REQUIRED, DISABLE_ALLOCATION_SITES);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ }
+ }
+}
+
+
+void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) {
+ ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>(
+ isolate);
+ ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
+ isolate);
+ ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>(
+ isolate);
+}
+
+
+void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(
+ Isolate* isolate) {
+ ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS };
+ for (int i = 0; i < 2; i++) {
+ // For internal arrays we only need a few things
+ InternalArrayNoArgumentConstructorStub stubh1(kinds[i]);
+ stubh1.GetCode(isolate)->set_is_pregenerated(true);
+ InternalArraySingleArgumentConstructorStub stubh2(kinds[i]);
+ stubh2.GetCode(isolate)->set_is_pregenerated(true);
+ InternalArrayNArgumentsConstructorStub stubh3(kinds[i]);
+ stubh3.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+void ArrayConstructorStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : argc (only if argument_count_ == ANY)
+ // -- ebx : type info cell
+ // -- edi : constructor
+ // -- esp[0] : return address
+ // -- esp[4] : last argument
+ // -----------------------------------
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(),
+ masm->isolate());
+
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the global and natives
+ // builtin Array functions which always have maps.
+
+ // Initial map for the builtin Array function should be a map.
+ __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction);
+ __ CmpObjectType(ecx, MAP_TYPE, ecx);
+ __ Assert(equal, kUnexpectedInitialMapForArrayFunction);
+
+ // We should either have undefined in ebx or a valid cell
+ Label okay_here;
+ Handle<Map> cell_map = masm->isolate()->factory()->cell_map();
+ __ cmp(ebx, Immediate(undefined_sentinel));
+ __ j(equal, &okay_here);
+ __ cmp(FieldOperand(ebx, 0), Immediate(cell_map));
+ __ Assert(equal, kExpectedPropertyCellInRegisterEbx);
+ __ bind(&okay_here);
+ }
+
+ Label no_info, switch_ready;
+ // Get the elements kind and case on that.
+ __ cmp(ebx, Immediate(undefined_sentinel));
+ __ j(equal, &no_info);
+ __ mov(edx, FieldOperand(ebx, Cell::kValueOffset));
+
+ // The type cell may have undefined in its value.
+ __ cmp(edx, Immediate(undefined_sentinel));
+ __ j(equal, &no_info);
+
+ // The type cell has either an AllocationSite or a JSFunction
+ __ cmp(FieldOperand(edx, 0), Immediate(Handle<Map>(
+ masm->isolate()->heap()->allocation_site_map())));
+ __ j(not_equal, &no_info);
+
+ __ mov(edx, FieldOperand(edx, AllocationSite::kTransitionInfoOffset));
+ __ SmiUntag(edx);
+ __ jmp(&switch_ready);
+ __ bind(&no_info);
+ __ mov(edx, Immediate(GetInitialFastElementsKind()));
+ __ bind(&switch_ready);
+
+ if (argument_count_ == ANY) {
+ Label not_zero_case, not_one_case;
+ __ test(eax, eax);
+ __ j(not_zero, &not_zero_case);
+ CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
+
+ __ bind(&not_zero_case);
+ __ cmp(eax, 1);
+ __ j(greater, &not_one_case);
+ CreateArrayDispatchOneArgument(masm);
+
+ __ bind(&not_one_case);
+ CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
+ } else if (argument_count_ == NONE) {
+ CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
+ } else if (argument_count_ == ONE) {
+ CreateArrayDispatchOneArgument(masm);
+ } else if (argument_count_ == MORE_THAN_ONE) {
+ CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void InternalArrayConstructorStub::GenerateCase(
+ MacroAssembler* masm, ElementsKind kind) {
+ Label not_zero_case, not_one_case;
+ Label normal_sequence;
+
+ __ test(eax, eax);
+ __ j(not_zero, &not_zero_case);
+ InternalArrayNoArgumentConstructorStub stub0(kind);
+ __ TailCallStub(&stub0);
+
+ __ bind(&not_zero_case);
+ __ cmp(eax, 1);
+ __ j(greater, &not_one_case);
+
+ if (IsFastPackedElementsKind(kind)) {
+ // We might need to create a holey array
+ // look at the first argument
+ __ mov(ecx, Operand(esp, kPointerSize));
+ __ test(ecx, ecx);
+ __ j(zero, &normal_sequence);
+
+ InternalArraySingleArgumentConstructorStub
+ stub1_holey(GetHoleyElementsKind(kind));
+ __ TailCallStub(&stub1_holey);
+ }
+
+ __ bind(&normal_sequence);
+ InternalArraySingleArgumentConstructorStub stub1(kind);
+ __ TailCallStub(&stub1);
+
+ __ bind(&not_one_case);
+ InternalArrayNArgumentsConstructorStub stubN(kind);
+ __ TailCallStub(&stubN);
+}
+
+
+void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : argc
+ // -- ebx : type info cell
+ // -- edi : constructor
+ // -- esp[0] : return address
+ // -- esp[4] : last argument
+ // -----------------------------------
+
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the global and natives
+ // builtin Array functions which always have maps.
+
+ // Initial map for the builtin Array function should be a map.
+ __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction);
+ __ CmpObjectType(ecx, MAP_TYPE, ecx);
+ __ Assert(equal, kUnexpectedInitialMapForArrayFunction);
+ }
+
+ // Figure out the right elements kind
+ __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Load the map's "bit field 2" into |result|. We only need the first byte,
+ // but the following masking takes care of that anyway.
+ __ mov(ecx, FieldOperand(ecx, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ and_(ecx, Map::kElementsKindMask);
+ __ shr(ecx, Map::kElementsKindShift);
+
+ if (FLAG_debug_code) {
+ Label done;
+ __ cmp(ecx, Immediate(FAST_ELEMENTS));
+ __ j(equal, &done);
+ __ cmp(ecx, Immediate(FAST_HOLEY_ELEMENTS));
+ __ Assert(equal,
+ kInvalidElementsKindForInternalArrayOrInternalPackedArray);
+ __ bind(&done);
+ }
+
+ Label fast_elements_case;
+ __ cmp(ecx, Immediate(FAST_ELEMENTS));
+ __ j(equal, &fast_elements_case);
+ GenerateCase(masm, FAST_HOLEY_ELEMENTS);
+
+ __ bind(&fast_elements_case);
+ GenerateCase(masm, FAST_ELEMENTS);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/code-stubs-ia32.h b/chromium/v8/src/ia32/code-stubs-ia32.h
new file mode 100644
index 00000000000..e80acc6ccfc
--- /dev/null
+++ b/chromium/v8/src/ia32/code-stubs-ia32.h
@@ -0,0 +1,568 @@
+// 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_IA32_CODE_STUBS_IA32_H_
+#define V8_IA32_CODE_STUBS_IA32_H_
+
+#include "macro-assembler.h"
+#include "code-stubs.h"
+#include "ic-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+void ArrayNativeCode(MacroAssembler* masm,
+ bool construct_call,
+ Label* call_generic_code);
+
+// Compute a transcendental math function natively, or call the
+// TranscendentalCache runtime function.
+class TranscendentalCacheStub: public PlatformCodeStub {
+ public:
+ enum ArgumentType {
+ TAGGED = 0,
+ UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
+ };
+
+ TranscendentalCacheStub(TranscendentalCache::Type type,
+ ArgumentType argument_type)
+ : type_(type), argument_type_(argument_type) {}
+ void Generate(MacroAssembler* masm);
+ static void GenerateOperation(MacroAssembler* masm,
+ TranscendentalCache::Type type);
+ private:
+ TranscendentalCache::Type type_;
+ ArgumentType argument_type_;
+
+ Major MajorKey() { return TranscendentalCache; }
+ int MinorKey() { return type_ | argument_type_; }
+ Runtime::FunctionId RuntimeFunction();
+};
+
+
+class StoreBufferOverflowStub: public PlatformCodeStub {
+ public:
+ explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
+ : save_doubles_(save_fp) {
+ ASSERT(CpuFeatures::IsSafeForSnapshot(SSE2) || save_fp == kDontSaveFPRegs);
+ }
+
+ void Generate(MacroAssembler* masm);
+
+ virtual bool IsPregenerated() { return true; }
+ static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ private:
+ SaveFPRegsMode save_doubles_;
+
+ Major MajorKey() { return StoreBufferOverflow; }
+ int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
+};
+
+
+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 GenerateCopyCharactersREP adds too much
+ // overhead. Copying of overlapping regions is not supported.
+ static void GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii);
+
+ // Generate code for copying characters using the rep movs instruction.
+ // Copies ecx characters from esi to edi. Copying of overlapping regions is
+ // not supported.
+ static void GenerateCopyCharactersREP(MacroAssembler* masm,
+ Register dest, // Must be edi.
+ Register src, // Must be esi.
+ Register count, // Must be ecx.
+ Register scratch, // Neither of above.
+ bool ascii);
+
+ // Probe the string table for a two character string. If the string
+ // requires non-standard hashing a jump to the label not_probed is
+ // performed and registers c1 and c2 are preserved. In all other
+ // cases they are clobbered. 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 string table. If the
+ // string is found the code falls through with the string in
+ // register eax.
+ static void GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_probed,
+ Label* not_found);
+
+ // Generate string hash.
+ static void GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ static void GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ static void GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
+};
+
+
+class StringAddStub: public PlatformCodeStub {
+ public:
+ explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
+
+ private:
+ Major MajorKey() { return StringAdd; }
+ int MinorKey() { return flags_; }
+
+ void Generate(MacroAssembler* masm);
+
+ void GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* slow);
+
+ void GenerateRegisterArgsPush(MacroAssembler* masm);
+ void GenerateRegisterArgsPop(MacroAssembler* masm, Register temp);
+
+ const StringAddFlags flags_;
+};
+
+
+class SubStringStub: public PlatformCodeStub {
+ public:
+ SubStringStub() {}
+
+ private:
+ Major MajorKey() { return SubString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class StringCompareStub: public PlatformCodeStub {
+ public:
+ StringCompareStub() { }
+
+ // Compares two flat ASCII strings and returns result in eax.
+ static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3);
+
+ // Compares two flat ASCII strings for equality and returns result
+ // in eax.
+ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2);
+
+ private:
+ virtual Major MajorKey() { return StringCompare; }
+ virtual int MinorKey() { return 0; }
+ virtual void Generate(MacroAssembler* masm);
+
+ static void GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance chars_not_equal_near = Label::kFar);
+};
+
+
+class NumberToStringStub: public PlatformCodeStub {
+ public:
+ NumberToStringStub() { }
+
+ // Generate code to do a lookup in the number string cache. If the number in
+ // the register object is found in the cache the generated code falls through
+ // with the result in the result register. The object and the result register
+ // can be the same. If the number is not found in the cache the code jumps to
+ // the label not_found with only the content of register object unchanged.
+ static void GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* not_found);
+
+ private:
+ Major MajorKey() { return NumberToString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class NameDictionaryLookupStub: public PlatformCodeStub {
+ public:
+ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
+
+ NameDictionaryLookupStub(Register dictionary,
+ Register result,
+ Register index,
+ LookupMode mode)
+ : dictionary_(dictionary), result_(result), index_(index), mode_(mode) { }
+
+ void Generate(MacroAssembler* masm);
+
+ static void GenerateNegativeLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ Handle<Name> name,
+ Register r0);
+
+ static void GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1);
+
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ private:
+ static const int kInlinedProbes = 4;
+ static const int kTotalProbes = 20;
+
+ static const int kCapacityOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kCapacityIndex * kPointerSize;
+
+ static const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+
+ Major MajorKey() { return NameDictionaryLookup; }
+
+ int MinorKey() {
+ return DictionaryBits::encode(dictionary_.code()) |
+ ResultBits::encode(result_.code()) |
+ IndexBits::encode(index_.code()) |
+ LookupModeBits::encode(mode_);
+ }
+
+ class DictionaryBits: public BitField<int, 0, 3> {};
+ class ResultBits: public BitField<int, 3, 3> {};
+ class IndexBits: public BitField<int, 6, 3> {};
+ class LookupModeBits: public BitField<LookupMode, 9, 1> {};
+
+ Register dictionary_;
+ Register result_;
+ Register index_;
+ LookupMode mode_;
+};
+
+
+class RecordWriteStub: public PlatformCodeStub {
+ 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.
+ ASSERT(CpuFeatures::IsSafeForSnapshot(SSE2) || fp_mode == kDontSaveFPRegs);
+ }
+
+ enum Mode {
+ STORE_BUFFER_ONLY,
+ INCREMENTAL,
+ INCREMENTAL_COMPACTION
+ };
+
+ virtual bool IsPregenerated();
+ static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
+ 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) {
+ CpuFeatureScope scope(masm, 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) {
+ CpuFeatureScope scope(masm, 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::NumAllocatableRegisters(); 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/chromium/v8/src/ia32/codegen-ia32.cc b/chromium/v8/src/ia32/codegen-ia32.cc
new file mode 100644
index 00000000000..28b0f4ad82f
--- /dev/null
+++ b/chromium/v8/src/ia32/codegen-ia32.cc
@@ -0,0 +1,1181 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "codegen.h"
+#include "heap.h"
+#include "macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+
+// -------------------------------------------------------------------------
+// Platform-specific RuntimeCallHelper functions.
+
+void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ masm->EnterFrame(StackFrame::INTERNAL);
+ ASSERT(!masm->has_frame());
+ masm->set_has_frame(true);
+}
+
+
+void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ masm->LeaveFrame(StackFrame::INTERNAL);
+ ASSERT(masm->has_frame());
+ masm->set_has_frame(false);
+}
+
+
+#define __ masm.
+
+
+UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type) {
+ size_t actual_size;
+ // Allocate buffer in executable space.
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB,
+ &actual_size,
+ true));
+ if (buffer == NULL) {
+ // Fallback to library function if function cannot be created.
+ switch (type) {
+ case TranscendentalCache::SIN: return &sin;
+ case TranscendentalCache::COS: return &cos;
+ case TranscendentalCache::TAN: return &tan;
+ case TranscendentalCache::LOG: return &log;
+ default: UNIMPLEMENTED();
+ }
+ }
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+ // esp[1 * kPointerSize]: raw double input
+ // esp[0 * kPointerSize]: return address
+ // Move double input into registers.
+
+ __ push(ebx);
+ __ push(edx);
+ __ push(edi);
+ __ fld_d(Operand(esp, 4 * kPointerSize));
+ __ mov(ebx, Operand(esp, 4 * kPointerSize));
+ __ mov(edx, Operand(esp, 5 * kPointerSize));
+ TranscendentalCacheStub::GenerateOperation(&masm, type);
+ // The return value is expected to be on ST(0) of the FPU stack.
+ __ pop(edi);
+ __ pop(edx);
+ __ pop(ebx);
+ __ Ret();
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+ return FUNCTION_CAST<UnaryMathFunction>(buffer);
+}
+
+
+UnaryMathFunction CreateExpFunction() {
+ if (!CpuFeatures::IsSupported(SSE2)) return &exp;
+ if (!FLAG_fast_math) return &exp;
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true));
+ if (buffer == NULL) return &exp;
+ ExternalReference::InitializeMathExpData();
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+ // esp[1 * kPointerSize]: raw double input
+ // esp[0 * kPointerSize]: return address
+ {
+ CpuFeatureScope use_sse2(&masm, SSE2);
+ XMMRegister input = xmm1;
+ XMMRegister result = xmm2;
+ __ movdbl(input, Operand(esp, 1 * kPointerSize));
+ __ push(eax);
+ __ push(ebx);
+
+ MathExpGenerator::EmitMathExp(&masm, input, result, xmm0, eax, ebx);
+
+ __ pop(ebx);
+ __ pop(eax);
+ __ movdbl(Operand(esp, 1 * kPointerSize), result);
+ __ fld_d(Operand(esp, 1 * kPointerSize));
+ __ Ret();
+ }
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+ return FUNCTION_CAST<UnaryMathFunction>(buffer);
+}
+
+
+UnaryMathFunction CreateSqrtFunction() {
+ size_t actual_size;
+ // Allocate buffer in executable space.
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB,
+ &actual_size,
+ true));
+ // If SSE2 is not available, we can use libc's implementation to ensure
+ // consistency since code by fullcodegen's calls into runtime in that case.
+ if (buffer == NULL || !CpuFeatures::IsSupported(SSE2)) return &sqrt;
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+ // esp[1 * kPointerSize]: raw double input
+ // esp[0 * kPointerSize]: return address
+ // Move double input into registers.
+ {
+ CpuFeatureScope use_sse2(&masm, SSE2);
+ __ movdbl(xmm0, Operand(esp, 1 * kPointerSize));
+ __ sqrtsd(xmm0, xmm0);
+ __ movdbl(Operand(esp, 1 * kPointerSize), xmm0);
+ // Load result into floating point register as return value.
+ __ fld_d(Operand(esp, 1 * kPointerSize));
+ __ Ret();
+ }
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+ return FUNCTION_CAST<UnaryMathFunction>(buffer);
+}
+
+
+// Helper functions for CreateMemMoveFunction.
+#undef __
+#define __ ACCESS_MASM(masm)
+
+// Keep around global pointers to these objects so that Valgrind won't complain.
+static size_t* medium_handlers = NULL;
+static size_t* small_handlers = NULL;
+
+
+enum Direction { FORWARD, BACKWARD };
+enum Alignment { MOVE_ALIGNED, MOVE_UNALIGNED };
+
+// Expects registers:
+// esi - source, aligned if alignment == ALIGNED
+// edi - destination, always aligned
+// ecx - count (copy size in bytes)
+// edx - loop count (number of 64 byte chunks)
+void MemMoveEmitMainLoop(MacroAssembler* masm,
+ Label* move_last_15,
+ Direction direction,
+ Alignment alignment) {
+ Register src = esi;
+ Register dst = edi;
+ Register count = ecx;
+ Register loop_count = edx;
+ Label loop, move_last_31, move_last_63;
+ __ cmp(loop_count, 0);
+ __ j(equal, &move_last_63);
+ __ bind(&loop);
+ // Main loop. Copy in 64 byte chunks.
+ if (direction == BACKWARD) __ sub(src, Immediate(0x40));
+ __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0x00));
+ __ movdq(alignment == MOVE_ALIGNED, xmm1, Operand(src, 0x10));
+ __ movdq(alignment == MOVE_ALIGNED, xmm2, Operand(src, 0x20));
+ __ movdq(alignment == MOVE_ALIGNED, xmm3, Operand(src, 0x30));
+ if (direction == FORWARD) __ add(src, Immediate(0x40));
+ if (direction == BACKWARD) __ sub(dst, Immediate(0x40));
+ __ movdqa(Operand(dst, 0x00), xmm0);
+ __ movdqa(Operand(dst, 0x10), xmm1);
+ __ movdqa(Operand(dst, 0x20), xmm2);
+ __ movdqa(Operand(dst, 0x30), xmm3);
+ if (direction == FORWARD) __ add(dst, Immediate(0x40));
+ __ dec(loop_count);
+ __ j(not_zero, &loop);
+ // At most 63 bytes left to copy.
+ __ bind(&move_last_63);
+ __ test(count, Immediate(0x20));
+ __ j(zero, &move_last_31);
+ if (direction == BACKWARD) __ sub(src, Immediate(0x20));
+ __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0x00));
+ __ movdq(alignment == MOVE_ALIGNED, xmm1, Operand(src, 0x10));
+ if (direction == FORWARD) __ add(src, Immediate(0x20));
+ if (direction == BACKWARD) __ sub(dst, Immediate(0x20));
+ __ movdqa(Operand(dst, 0x00), xmm0);
+ __ movdqa(Operand(dst, 0x10), xmm1);
+ if (direction == FORWARD) __ add(dst, Immediate(0x20));
+ // At most 31 bytes left to copy.
+ __ bind(&move_last_31);
+ __ test(count, Immediate(0x10));
+ __ j(zero, move_last_15);
+ if (direction == BACKWARD) __ sub(src, Immediate(0x10));
+ __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0));
+ if (direction == FORWARD) __ add(src, Immediate(0x10));
+ if (direction == BACKWARD) __ sub(dst, Immediate(0x10));
+ __ movdqa(Operand(dst, 0), xmm0);
+ if (direction == FORWARD) __ add(dst, Immediate(0x10));
+}
+
+
+void MemMoveEmitPopAndReturn(MacroAssembler* masm) {
+ __ pop(esi);
+ __ pop(edi);
+ __ ret(0);
+}
+
+
+#undef __
+#define __ masm.
+
+
+OS::MemMoveFunction CreateMemMoveFunction() {
+ size_t actual_size;
+ // Allocate buffer in executable space.
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true));
+ if (buffer == NULL) return NULL;
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+
+ // Generated code is put into a fixed, unmovable buffer, and not into
+ // the V8 heap. We can't, and don't, refer to any relocatable addresses
+ // (e.g. the JavaScript nan-object).
+
+ // 32-bit C declaration function calls pass arguments on stack.
+
+ // Stack layout:
+ // esp[12]: Third argument, size.
+ // esp[8]: Second argument, source pointer.
+ // esp[4]: First argument, destination pointer.
+ // esp[0]: return address
+
+ const int kDestinationOffset = 1 * kPointerSize;
+ const int kSourceOffset = 2 * kPointerSize;
+ const int kSizeOffset = 3 * kPointerSize;
+
+ // When copying up to this many bytes, use special "small" handlers.
+ const size_t kSmallCopySize = 8;
+ // When copying up to this many bytes, use special "medium" handlers.
+ const size_t kMediumCopySize = 63;
+ // When non-overlapping region of src and dst is less than this,
+ // use a more careful implementation (slightly slower).
+ const size_t kMinMoveDistance = 16;
+ // Note that these values are dictated by the implementation below,
+ // do not just change them and hope things will work!
+
+ int stack_offset = 0; // Update if we change the stack height.
+
+ Label backward, backward_much_overlap;
+ Label forward_much_overlap, small_size, medium_size, pop_and_return;
+ __ push(edi);
+ __ push(esi);
+ stack_offset += 2 * kPointerSize;
+ Register dst = edi;
+ Register src = esi;
+ Register count = ecx;
+ Register loop_count = edx;
+ __ mov(dst, Operand(esp, stack_offset + kDestinationOffset));
+ __ mov(src, Operand(esp, stack_offset + kSourceOffset));
+ __ mov(count, Operand(esp, stack_offset + kSizeOffset));
+
+ __ cmp(dst, src);
+ __ j(equal, &pop_and_return);
+
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope sse2_scope(&masm, SSE2);
+ __ prefetch(Operand(src, 0), 1);
+ __ cmp(count, kSmallCopySize);
+ __ j(below_equal, &small_size);
+ __ cmp(count, kMediumCopySize);
+ __ j(below_equal, &medium_size);
+ __ cmp(dst, src);
+ __ j(above, &backward);
+
+ {
+ // |dst| is a lower address than |src|. Copy front-to-back.
+ Label unaligned_source, move_last_15, skip_last_move;
+ __ mov(eax, src);
+ __ sub(eax, dst);
+ __ cmp(eax, kMinMoveDistance);
+ __ j(below, &forward_much_overlap);
+ // Copy first 16 bytes.
+ __ movdqu(xmm0, Operand(src, 0));
+ __ movdqu(Operand(dst, 0), xmm0);
+ // Determine distance to alignment: 16 - (dst & 0xF).
+ __ mov(edx, dst);
+ __ and_(edx, 0xF);
+ __ neg(edx);
+ __ add(edx, Immediate(16));
+ __ add(dst, edx);
+ __ add(src, edx);
+ __ sub(count, edx);
+ // dst is now aligned. Main copy loop.
+ __ mov(loop_count, count);
+ __ shr(loop_count, 6);
+ // Check if src is also aligned.
+ __ test(src, Immediate(0xF));
+ __ j(not_zero, &unaligned_source);
+ // Copy loop for aligned source and destination.
+ MemMoveEmitMainLoop(&masm, &move_last_15, FORWARD, MOVE_ALIGNED);
+ // At most 15 bytes to copy. Copy 16 bytes at end of string.
+ __ bind(&move_last_15);
+ __ and_(count, 0xF);
+ __ j(zero, &skip_last_move, Label::kNear);
+ __ movdqu(xmm0, Operand(src, count, times_1, -0x10));
+ __ movdqu(Operand(dst, count, times_1, -0x10), xmm0);
+ __ bind(&skip_last_move);
+ MemMoveEmitPopAndReturn(&masm);
+
+ // Copy loop for unaligned source and aligned destination.
+ __ bind(&unaligned_source);
+ MemMoveEmitMainLoop(&masm, &move_last_15, FORWARD, MOVE_UNALIGNED);
+ __ jmp(&move_last_15);
+
+ // Less than kMinMoveDistance offset between dst and src.
+ Label loop_until_aligned, last_15_much_overlap;
+ __ bind(&loop_until_aligned);
+ __ mov_b(eax, Operand(src, 0));
+ __ inc(src);
+ __ mov_b(Operand(dst, 0), eax);
+ __ inc(dst);
+ __ dec(count);
+ __ bind(&forward_much_overlap); // Entry point into this block.
+ __ test(dst, Immediate(0xF));
+ __ j(not_zero, &loop_until_aligned);
+ // dst is now aligned, src can't be. Main copy loop.
+ __ mov(loop_count, count);
+ __ shr(loop_count, 6);
+ MemMoveEmitMainLoop(&masm, &last_15_much_overlap,
+ FORWARD, MOVE_UNALIGNED);
+ __ bind(&last_15_much_overlap);
+ __ and_(count, 0xF);
+ __ j(zero, &pop_and_return);
+ __ cmp(count, kSmallCopySize);
+ __ j(below_equal, &small_size);
+ __ jmp(&medium_size);
+ }
+
+ {
+ // |dst| is a higher address than |src|. Copy backwards.
+ Label unaligned_source, move_first_15, skip_last_move;
+ __ bind(&backward);
+ // |dst| and |src| always point to the end of what's left to copy.
+ __ add(dst, count);
+ __ add(src, count);
+ __ mov(eax, dst);
+ __ sub(eax, src);
+ __ cmp(eax, kMinMoveDistance);
+ __ j(below, &backward_much_overlap);
+ // Copy last 16 bytes.
+ __ movdqu(xmm0, Operand(src, -0x10));
+ __ movdqu(Operand(dst, -0x10), xmm0);
+ // Find distance to alignment: dst & 0xF
+ __ mov(edx, dst);
+ __ and_(edx, 0xF);
+ __ sub(dst, edx);
+ __ sub(src, edx);
+ __ sub(count, edx);
+ // dst is now aligned. Main copy loop.
+ __ mov(loop_count, count);
+ __ shr(loop_count, 6);
+ // Check if src is also aligned.
+ __ test(src, Immediate(0xF));
+ __ j(not_zero, &unaligned_source);
+ // Copy loop for aligned source and destination.
+ MemMoveEmitMainLoop(&masm, &move_first_15, BACKWARD, MOVE_ALIGNED);
+ // At most 15 bytes to copy. Copy 16 bytes at beginning of string.
+ __ bind(&move_first_15);
+ __ and_(count, 0xF);
+ __ j(zero, &skip_last_move, Label::kNear);
+ __ sub(src, count);
+ __ sub(dst, count);
+ __ movdqu(xmm0, Operand(src, 0));
+ __ movdqu(Operand(dst, 0), xmm0);
+ __ bind(&skip_last_move);
+ MemMoveEmitPopAndReturn(&masm);
+
+ // Copy loop for unaligned source and aligned destination.
+ __ bind(&unaligned_source);
+ MemMoveEmitMainLoop(&masm, &move_first_15, BACKWARD, MOVE_UNALIGNED);
+ __ jmp(&move_first_15);
+
+ // Less than kMinMoveDistance offset between dst and src.
+ Label loop_until_aligned, first_15_much_overlap;
+ __ bind(&loop_until_aligned);
+ __ dec(src);
+ __ dec(dst);
+ __ mov_b(eax, Operand(src, 0));
+ __ mov_b(Operand(dst, 0), eax);
+ __ dec(count);
+ __ bind(&backward_much_overlap); // Entry point into this block.
+ __ test(dst, Immediate(0xF));
+ __ j(not_zero, &loop_until_aligned);
+ // dst is now aligned, src can't be. Main copy loop.
+ __ mov(loop_count, count);
+ __ shr(loop_count, 6);
+ MemMoveEmitMainLoop(&masm, &first_15_much_overlap,
+ BACKWARD, MOVE_UNALIGNED);
+ __ bind(&first_15_much_overlap);
+ __ and_(count, 0xF);
+ __ j(zero, &pop_and_return);
+ // Small/medium handlers expect dst/src to point to the beginning.
+ __ sub(dst, count);
+ __ sub(src, count);
+ __ cmp(count, kSmallCopySize);
+ __ j(below_equal, &small_size);
+ __ jmp(&medium_size);
+ }
+ {
+ // Special handlers for 9 <= copy_size < 64. No assumptions about
+ // alignment or move distance, so all reads must be unaligned and
+ // must happen before any writes.
+ Label f9_16, f17_32, f33_48, f49_63;
+
+ __ bind(&f9_16);
+ __ movdbl(xmm0, Operand(src, 0));
+ __ movdbl(xmm1, Operand(src, count, times_1, -8));
+ __ movdbl(Operand(dst, 0), xmm0);
+ __ movdbl(Operand(dst, count, times_1, -8), xmm1);
+ MemMoveEmitPopAndReturn(&masm);
+
+ __ bind(&f17_32);
+ __ movdqu(xmm0, Operand(src, 0));
+ __ movdqu(xmm1, Operand(src, count, times_1, -0x10));
+ __ movdqu(Operand(dst, 0x00), xmm0);
+ __ movdqu(Operand(dst, count, times_1, -0x10), xmm1);
+ MemMoveEmitPopAndReturn(&masm);
+
+ __ bind(&f33_48);
+ __ movdqu(xmm0, Operand(src, 0x00));
+ __ movdqu(xmm1, Operand(src, 0x10));
+ __ movdqu(xmm2, Operand(src, count, times_1, -0x10));
+ __ movdqu(Operand(dst, 0x00), xmm0);
+ __ movdqu(Operand(dst, 0x10), xmm1);
+ __ movdqu(Operand(dst, count, times_1, -0x10), xmm2);
+ MemMoveEmitPopAndReturn(&masm);
+
+ __ bind(&f49_63);
+ __ movdqu(xmm0, Operand(src, 0x00));
+ __ movdqu(xmm1, Operand(src, 0x10));
+ __ movdqu(xmm2, Operand(src, 0x20));
+ __ movdqu(xmm3, Operand(src, count, times_1, -0x10));
+ __ movdqu(Operand(dst, 0x00), xmm0);
+ __ movdqu(Operand(dst, 0x10), xmm1);
+ __ movdqu(Operand(dst, 0x20), xmm2);
+ __ movdqu(Operand(dst, count, times_1, -0x10), xmm3);
+ MemMoveEmitPopAndReturn(&masm);
+
+ medium_handlers = new size_t[4];
+ medium_handlers[0] = reinterpret_cast<intptr_t>(buffer) + f9_16.pos();
+ medium_handlers[1] = reinterpret_cast<intptr_t>(buffer) + f17_32.pos();
+ medium_handlers[2] = reinterpret_cast<intptr_t>(buffer) + f33_48.pos();
+ medium_handlers[3] = reinterpret_cast<intptr_t>(buffer) + f49_63.pos();
+
+ __ bind(&medium_size); // Entry point into this block.
+ __ mov(eax, count);
+ __ dec(eax);
+ __ shr(eax, 4);
+ if (FLAG_debug_code) {
+ Label ok;
+ __ cmp(eax, 3);
+ __ j(below_equal, &ok);
+ __ int3();
+ __ bind(&ok);
+ }
+ __ mov(eax, Operand(eax, times_4,
+ reinterpret_cast<intptr_t>(medium_handlers)));
+ __ jmp(eax);
+ }
+ {
+ // Specialized copiers for copy_size <= 8 bytes.
+ Label f0, f1, f2, f3, f4, f5_8;
+ __ bind(&f0);
+ MemMoveEmitPopAndReturn(&masm);
+
+ __ bind(&f1);
+ __ mov_b(eax, Operand(src, 0));
+ __ mov_b(Operand(dst, 0), eax);
+ MemMoveEmitPopAndReturn(&masm);
+
+ __ bind(&f2);
+ __ mov_w(eax, Operand(src, 0));
+ __ mov_w(Operand(dst, 0), eax);
+ MemMoveEmitPopAndReturn(&masm);
+
+ __ bind(&f3);
+ __ mov_w(eax, Operand(src, 0));
+ __ mov_b(edx, Operand(src, 2));
+ __ mov_w(Operand(dst, 0), eax);
+ __ mov_b(Operand(dst, 2), edx);
+ MemMoveEmitPopAndReturn(&masm);
+
+ __ bind(&f4);
+ __ mov(eax, Operand(src, 0));
+ __ mov(Operand(dst, 0), eax);
+ MemMoveEmitPopAndReturn(&masm);
+
+ __ bind(&f5_8);
+ __ mov(eax, Operand(src, 0));
+ __ mov(edx, Operand(src, count, times_1, -4));
+ __ mov(Operand(dst, 0), eax);
+ __ mov(Operand(dst, count, times_1, -4), edx);
+ MemMoveEmitPopAndReturn(&masm);
+
+ small_handlers = new size_t[9];
+ small_handlers[0] = reinterpret_cast<intptr_t>(buffer) + f0.pos();
+ small_handlers[1] = reinterpret_cast<intptr_t>(buffer) + f1.pos();
+ small_handlers[2] = reinterpret_cast<intptr_t>(buffer) + f2.pos();
+ small_handlers[3] = reinterpret_cast<intptr_t>(buffer) + f3.pos();
+ small_handlers[4] = reinterpret_cast<intptr_t>(buffer) + f4.pos();
+ small_handlers[5] = reinterpret_cast<intptr_t>(buffer) + f5_8.pos();
+ small_handlers[6] = reinterpret_cast<intptr_t>(buffer) + f5_8.pos();
+ small_handlers[7] = reinterpret_cast<intptr_t>(buffer) + f5_8.pos();
+ small_handlers[8] = reinterpret_cast<intptr_t>(buffer) + f5_8.pos();
+
+ __ bind(&small_size); // Entry point into this block.
+ if (FLAG_debug_code) {
+ Label ok;
+ __ cmp(count, 8);
+ __ j(below_equal, &ok);
+ __ int3();
+ __ bind(&ok);
+ }
+ __ mov(eax, Operand(count, times_4,
+ reinterpret_cast<intptr_t>(small_handlers)));
+ __ jmp(eax);
+ }
+ } else {
+ // No SSE2.
+ Label forward;
+ __ cmp(count, 0);
+ __ j(equal, &pop_and_return);
+ __ cmp(dst, src);
+ __ j(above, &backward);
+ __ jmp(&forward);
+ {
+ // Simple forward copier.
+ Label forward_loop_1byte, forward_loop_4byte;
+ __ bind(&forward_loop_4byte);
+ __ mov(eax, Operand(src, 0));
+ __ sub(count, Immediate(4));
+ __ add(src, Immediate(4));
+ __ mov(Operand(dst, 0), eax);
+ __ add(dst, Immediate(4));
+ __ bind(&forward); // Entry point.
+ __ cmp(count, 3);
+ __ j(above, &forward_loop_4byte);
+ __ bind(&forward_loop_1byte);
+ __ cmp(count, 0);
+ __ j(below_equal, &pop_and_return);
+ __ mov_b(eax, Operand(src, 0));
+ __ dec(count);
+ __ inc(src);
+ __ mov_b(Operand(dst, 0), eax);
+ __ inc(dst);
+ __ jmp(&forward_loop_1byte);
+ }
+ {
+ // Simple backward copier.
+ Label backward_loop_1byte, backward_loop_4byte, entry_shortcut;
+ __ bind(&backward);
+ __ add(src, count);
+ __ add(dst, count);
+ __ cmp(count, 3);
+ __ j(below_equal, &entry_shortcut);
+
+ __ bind(&backward_loop_4byte);
+ __ sub(src, Immediate(4));
+ __ sub(count, Immediate(4));
+ __ mov(eax, Operand(src, 0));
+ __ sub(dst, Immediate(4));
+ __ mov(Operand(dst, 0), eax);
+ __ cmp(count, 3);
+ __ j(above, &backward_loop_4byte);
+ __ bind(&backward_loop_1byte);
+ __ cmp(count, 0);
+ __ j(below_equal, &pop_and_return);
+ __ bind(&entry_shortcut);
+ __ dec(src);
+ __ dec(count);
+ __ mov_b(eax, Operand(src, 0));
+ __ dec(dst);
+ __ mov_b(Operand(dst, 0), eax);
+ __ jmp(&backward_loop_1byte);
+ }
+ }
+
+ __ bind(&pop_and_return);
+ MemMoveEmitPopAndReturn(&masm);
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+ // TODO(jkummerow): It would be nice to register this code creation event
+ // with the PROFILE / GDBJIT system.
+ return FUNCTION_CAST<OS::MemMoveFunction>(buffer);
+}
+
+
+#undef __
+
+// -------------------------------------------------------------------------
+// Code generators
+
+#define __ ACCESS_MASM(masm)
+
+
+void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
+ MacroAssembler* masm, AllocationSiteMode mode,
+ Label* allocation_memento_found) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ebx : target map
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ if (mode == TRACK_ALLOCATION_SITE) {
+ ASSERT(allocation_memento_found != NULL);
+ __ TestJSArrayForAllocationMemento(edx, edi);
+ __ j(equal, allocation_memento_found);
+ }
+
+ // Set transitioned map.
+ __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
+ __ RecordWriteField(edx,
+ HeapObject::kMapOffset,
+ ebx,
+ edi,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+}
+
+
+void ElementsTransitionGenerator::GenerateSmiToDouble(
+ MacroAssembler* masm, AllocationSiteMode mode, 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, only_change_map;
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ __ TestJSArrayForAllocationMemento(edx, edi);
+ __ j(equal, fail);
+ }
+
+ // Check for empty arrays, which only require a map transition and no changes
+ // to the backing store.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
+ __ j(equal, &only_change_map);
+
+ __ push(eax);
+ __ push(ebx);
+
+ __ mov(edi, FieldOperand(edi, FixedArray::kLengthOffset));
+
+ // Allocate new FixedDoubleArray.
+ // edx: receiver
+ // edi: length of source FixedArray (smi-tagged)
+ AllocationFlags flags =
+ static_cast<AllocationFlags>(TAG_OBJECT | DOUBLE_ALIGNMENT);
+ __ Allocate(FixedDoubleArray::kHeaderSize, times_8, edi,
+ REGISTER_VALUE_IS_SMI, eax, ebx, no_reg, &gc_required, flags);
+
+ // 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)) {
+ CpuFeatureScope use_sse2(masm, 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)) {
+ CpuFeatureScope fscope(masm, 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, kObjectFoundInSmiOnlyArray);
+ }
+
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, 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);
+
+ // Restore esi.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+
+ __ bind(&only_change_map);
+ // eax: value
+ // ebx: target map
+ // Set transitioned map.
+ __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
+ __ RecordWriteField(edx,
+ HeapObject::kMapOffset,
+ ebx,
+ edi,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+}
+
+
+void ElementsTransitionGenerator::GenerateDoubleToObject(
+ MacroAssembler* masm, AllocationSiteMode mode, 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, only_change_map, success;
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ __ TestJSArrayForAllocationMemento(edx, edi);
+ __ j(equal, fail);
+ }
+
+ // Check for empty arrays, which only require a map transition and no changes
+ // to the backing store.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
+ __ j(equal, &only_change_map);
+
+ __ push(eax);
+ __ push(edx);
+ __ push(ebx);
+
+ __ mov(ebx, FieldOperand(edi, FixedDoubleArray::kLengthOffset));
+
+ // Allocate new FixedArray.
+ // ebx: length of source FixedDoubleArray (smi-tagged)
+ __ lea(edi, Operand(ebx, times_2, FixedArray::kHeaderSize));
+ __ Allocate(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);
+
+ // ebx: target map
+ // edx: receiver
+ // Set transitioned map.
+ __ bind(&only_change_map);
+ __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
+ __ RecordWriteField(edx,
+ HeapObject::kMapOffset,
+ ebx,
+ edi,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ __ jmp(&success);
+
+ // 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)) {
+ CpuFeatureScope fscope(masm, 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,
+ OMIT_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));
+
+ __ bind(&success);
+}
+
+
+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, kExternalStringExpectedButNotFound);
+ }
+ // 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 & kOneByteStringTag) != 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,
+ SeqOneByteString::kHeaderSize));
+ __ bind(&done);
+}
+
+
+static Operand ExpConstant(int index) {
+ return Operand::StaticVariable(ExternalReference::math_exp_constants(index));
+}
+
+
+void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
+ XMMRegister input,
+ XMMRegister result,
+ XMMRegister double_scratch,
+ Register temp1,
+ Register temp2) {
+ ASSERT(!input.is(double_scratch));
+ ASSERT(!input.is(result));
+ ASSERT(!result.is(double_scratch));
+ ASSERT(!temp1.is(temp2));
+ ASSERT(ExternalReference::math_exp_constants(0).address() != NULL);
+
+ Label done;
+
+ __ movdbl(double_scratch, ExpConstant(0));
+ __ xorpd(result, result);
+ __ ucomisd(double_scratch, input);
+ __ j(above_equal, &done);
+ __ ucomisd(input, ExpConstant(1));
+ __ movdbl(result, ExpConstant(2));
+ __ j(above_equal, &done);
+ __ movdbl(double_scratch, ExpConstant(3));
+ __ movdbl(result, ExpConstant(4));
+ __ mulsd(double_scratch, input);
+ __ addsd(double_scratch, result);
+ __ movd(temp2, double_scratch);
+ __ subsd(double_scratch, result);
+ __ movdbl(result, ExpConstant(6));
+ __ mulsd(double_scratch, ExpConstant(5));
+ __ subsd(double_scratch, input);
+ __ subsd(result, double_scratch);
+ __ movsd(input, double_scratch);
+ __ mulsd(input, double_scratch);
+ __ mulsd(result, input);
+ __ mov(temp1, temp2);
+ __ mulsd(result, ExpConstant(7));
+ __ subsd(result, double_scratch);
+ __ add(temp1, Immediate(0x1ff800));
+ __ addsd(result, ExpConstant(8));
+ __ and_(temp2, Immediate(0x7ff));
+ __ shr(temp1, 11);
+ __ shl(temp1, 20);
+ __ movd(input, temp1);
+ __ pshufd(input, input, static_cast<uint8_t>(0xe1)); // Order: 11 10 00 01
+ __ movdbl(double_scratch, Operand::StaticArray(
+ temp2, times_8, ExternalReference::math_exp_log_table()));
+ __ por(input, double_scratch);
+ __ mulsd(result, input);
+ __ bind(&done);
+}
+
+#undef __
+
+static const int kNoCodeAgeSequenceLength = 5;
+
+static byte* GetNoCodeAgeSequence(uint32_t* length) {
+ static bool initialized = false;
+ static byte sequence[kNoCodeAgeSequenceLength];
+ *length = kNoCodeAgeSequenceLength;
+ if (!initialized) {
+ // The sequence of instructions that is patched out for aging code is the
+ // following boilerplate stack-building prologue that is found both in
+ // FUNCTION and OPTIMIZED_FUNCTION code:
+ CodePatcher patcher(sequence, kNoCodeAgeSequenceLength);
+ patcher.masm()->push(ebp);
+ patcher.masm()->mov(ebp, esp);
+ patcher.masm()->push(esi);
+ patcher.masm()->push(edi);
+ initialized = true;
+ }
+ return sequence;
+}
+
+
+bool Code::IsYoungSequence(byte* sequence) {
+ uint32_t young_length;
+ byte* young_sequence = GetNoCodeAgeSequence(&young_length);
+ bool result = (!memcmp(sequence, young_sequence, young_length));
+ ASSERT(result || *sequence == kCallOpcode);
+ return result;
+}
+
+
+void Code::GetCodeAgeAndParity(byte* sequence, Age* age,
+ MarkingParity* parity) {
+ if (IsYoungSequence(sequence)) {
+ *age = kNoAge;
+ *parity = NO_MARKING_PARITY;
+ } else {
+ sequence++; // Skip the kCallOpcode byte
+ Address target_address = sequence + *reinterpret_cast<int*>(sequence) +
+ Assembler::kCallTargetAddressOffset;
+ Code* stub = GetCodeFromTargetAddress(target_address);
+ GetCodeAgeAndParity(stub, age, parity);
+ }
+}
+
+
+void Code::PatchPlatformCodeAge(byte* sequence,
+ Code::Age age,
+ MarkingParity parity) {
+ uint32_t young_length;
+ byte* young_sequence = GetNoCodeAgeSequence(&young_length);
+ if (age == kNoAge) {
+ CopyBytes(sequence, young_sequence, young_length);
+ CPU::FlushICache(sequence, young_length);
+ } else {
+ Code* stub = GetCodeAgeStub(age, parity);
+ CodePatcher patcher(sequence, young_length);
+ patcher.masm()->call(stub->instruction_start(), RelocInfo::NONE32);
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/codegen-ia32.h b/chromium/v8/src/ia32/codegen-ia32.h
new file mode 100644
index 00000000000..6db381e47e5
--- /dev/null
+++ b/chromium/v8/src/ia32/codegen-ia32.h
@@ -0,0 +1,107 @@
+// 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_IA32_CODEGEN_IA32_H_
+#define V8_IA32_CODEGEN_IA32_H_
+
+#include "ast.h"
+#include "ic-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations
+class CompilationInfo;
+
+// -------------------------------------------------------------------------
+// CodeGenerator
+
+class CodeGenerator {
+ public:
+ // Printing of AST, etc. as requested by flags.
+ static void MakeCodePrologue(CompilationInfo* info, const char* kind);
+
+ // Allocate and install the code.
+ static Handle<Code> MakeCodeEpilogue(MacroAssembler* masm,
+ Code::Flags flags,
+ CompilationInfo* info);
+
+ // Print the code after compiling it.
+ static void PrintCode(Handle<Code> code, CompilationInfo* info);
+
+ static bool ShouldGenerateLog(Expression* type);
+
+ static bool RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here = false);
+
+
+ static Operand FixedArrayElementOperand(Register array,
+ Register index_as_smi,
+ int additional_offset = 0) {
+ int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize;
+ return FieldOperand(array, index_as_smi, times_half_pointer_size, offset);
+ }
+
+ 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,
+ Factory* factory,
+ Register string,
+ Register index,
+ Register result,
+ Label* call_runtime);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator);
+};
+
+
+class MathExpGenerator : public AllStatic {
+ public:
+ static void EmitMathExp(MacroAssembler* masm,
+ XMMRegister input,
+ XMMRegister result,
+ XMMRegister double_scratch,
+ Register temp1,
+ Register temp2);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MathExpGenerator);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_IA32_CODEGEN_IA32_H_
diff --git a/chromium/v8/src/ia32/cpu-ia32.cc b/chromium/v8/src/ia32/cpu-ia32.cc
new file mode 100644
index 00000000000..77ff169b52b
--- /dev/null
+++ b/chromium/v8/src/ia32/cpu-ia32.cc
@@ -0,0 +1,91 @@
+// 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.
+
+// CPU specific code for ia32 independent of OS goes here.
+
+#ifdef __GNUC__
+#include "third_party/valgrind/valgrind.h"
+#endif
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "cpu.h"
+#include "macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+void CPU::SetUp() {
+ CpuFeatures::Probe();
+}
+
+
+bool CPU::SupportsCrankshaft() {
+ return CpuFeatures::IsSupported(SSE2);
+}
+
+
+void CPU::FlushICache(void* start, size_t size) {
+ // No need to flush the instruction cache on Intel. On Intel instruction
+ // cache flushing is only necessary when multiple cores running the same
+ // code simultaneously. V8 (and JavaScript) is single threaded and when code
+ // is patched on an intel CPU the core performing the patching will have its
+ // own instruction cache updated automatically.
+
+ // If flushing of the instruction cache becomes necessary Windows has the
+ // API function FlushInstructionCache.
+
+ // By default, valgrind only checks the stack for writes that might need to
+ // invalidate already cached translated code. This leads to random
+ // instability when code patches or moves are sometimes unnoticed. One
+ // solution is to run valgrind with --smc-check=all, but this comes at a big
+ // performance cost. We can notify valgrind to invalidate its cache.
+#ifdef VALGRIND_DISCARD_TRANSLATIONS
+ unsigned res = VALGRIND_DISCARD_TRANSLATIONS(start, size);
+ USE(res);
+#endif
+}
+
+
+void CPU::DebugBreak() {
+#ifdef _MSC_VER
+ // To avoid Visual Studio runtime support the following code can be used
+ // instead
+ // __asm { int 3 }
+ __debugbreak();
+#elif defined(__native_client__)
+ asm("hlt");
+#else
+ asm("int $3");
+#endif
+}
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/debug-ia32.cc b/chromium/v8/src/ia32/debug-ia32.cc
new file mode 100644
index 00000000000..fd703dcc0c7
--- /dev/null
+++ b/chromium/v8/src/ia32/debug-ia32.cc
@@ -0,0 +1,372 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "codegen.h"
+#include "debug.h"
+
+
+namespace v8 {
+namespace internal {
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+bool BreakLocationIterator::IsDebugBreakAtReturn() {
+ return Debug::IsDebugBreakAtReturn(rinfo());
+}
+
+
+// Patch the JS frame exit code with a debug break call. See
+// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-ia32.cc
+// for the precise return instructions sequence.
+void BreakLocationIterator::SetDebugBreakAtReturn() {
+ ASSERT(Assembler::kJSReturnSequenceLength >=
+ Assembler::kCallInstructionLength);
+ Isolate* isolate = Isolate::Current();
+ rinfo()->PatchCodeWithCall(isolate->debug()->debug_break_return()->entry(),
+ Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
+}
+
+
+// Restore the JS frame exit code.
+void BreakLocationIterator::ClearDebugBreakAtReturn() {
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kJSReturnSequenceLength);
+}
+
+
+// A debug break in the frame exit code is identified by the JS frame exit code
+// having been patched with a call instruction.
+bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
+ return rinfo->IsPatchedReturnSequence();
+}
+
+
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return rinfo()->IsPatchedDebugBreakSlotSequence();
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ Isolate* isolate = Isolate::Current();
+ rinfo()->PatchCodeWithCall(
+ isolate->debug()->debug_break_slot()->entry(),
+ Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
+}
+
+
+// All debug break stubs support padding for LiveEdit.
+const bool Debug::FramePaddingLayout::kIsSupported = true;
+
+
+#define __ ACCESS_MASM(masm)
+
+static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
+ RegList object_regs,
+ RegList non_object_regs,
+ bool convert_call_to_jmp) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ for (int i = 0; i < Debug::FramePaddingLayout::kInitialSize; i++) {
+ __ push(Immediate(Smi::FromInt(
+ Debug::FramePaddingLayout::kPaddingValue)));
+ }
+ __ push(Immediate(Smi::FromInt(Debug::FramePaddingLayout::kInitialSize)));
+
+ // 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, kUnableToEncodeValueAsSmi);
+ }
+ __ SmiTag(reg);
+ __ push(reg);
+ }
+ }
+
+#ifdef DEBUG
+ __ 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);
+
+ // Automatically find register that could be used after register restore.
+ // We need one register for padding skip instructions.
+ Register unused_reg = { -1 };
+
+ // 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));
+ }
+ bool taken = reg.code() == esi.code();
+ if ((object_regs & (1 << r)) != 0) {
+ __ pop(reg);
+ taken = true;
+ }
+ if ((non_object_regs & (1 << r)) != 0) {
+ __ pop(reg);
+ __ SmiUntag(reg);
+ taken = true;
+ }
+ if (!taken) {
+ unused_reg = reg;
+ }
+ }
+
+ ASSERT(unused_reg.code() != -1);
+
+ // Read current padding counter and skip corresponding number of words.
+ __ pop(unused_reg);
+ // We divide stored value by 2 (untagging) and multiply it by word's size.
+ STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
+ __ lea(esp, Operand(esp, unused_reg, times_half_pointer_size, 0));
+
+ // 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(esp, Immediate(kPointerSize));
+ }
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate());
+ __ jmp(Operand::StaticVariable(after_break_target));
+}
+
+
+void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
+ // Register state for IC load call (from ic-ia32.cc).
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edx : receiver
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, ecx.bit() | edx.bit(), 0, false);
+}
+
+
+void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
+ // Register state for IC store call (from ic-ia32.cc).
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(
+ masm, eax.bit() | ecx.bit() | edx.bit(), 0, false);
+}
+
+
+void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
+ // Register state for keyed IC load call (from ic-ia32.cc).
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, ecx.bit() | edx.bit(), 0, false);
+}
+
+
+void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
+ // Register state for keyed IC load call (from ic-ia32.cc).
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(
+ masm, eax.bit() | ecx.bit() | edx.bit(), 0, false);
+}
+
+
+void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
+ // Register state for CompareNil IC
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, eax.bit(), 0, false);
+}
+
+
+void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
+ // Register state for keyed IC call call (from ic-ia32.cc)
+ // ----------- S t a t e -------------
+ // -- ecx: name
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, ecx.bit(), 0, false);
+}
+
+
+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 -------------
+ // -- eax: number of arguments (not smi)
+ // -- edi: constructor function
+ // -----------------------------------
+ // The number of arguments in eax is not smi encoded.
+ Generate_DebugBreakCallHelper(masm, edi.bit(), eax.bit(), false);
+}
+
+
+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 -------------
+ // -- eax: number of arguments (not smi)
+ // -- ebx: cache cell for call target
+ // -- edi: constructor function
+ // -----------------------------------
+ // The number of arguments in eax is not smi encoded.
+ Generate_DebugBreakCallHelper(masm, ebx.bit() | edi.bit(), eax.bit(), false);
+}
+
+
+void Debug::GenerateSlot(MacroAssembler* masm) {
+ // Generate enough nop's to make space for a call instruction.
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ RecordDebugBreakSlot();
+ __ Nop(Assembler::kDebugBreakSlotLength);
+ ASSERT_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
+ // In the places where a debug break slot is inserted no registers can contain
+ // object pointers.
+ Generate_DebugBreakCallHelper(masm, 0, 0, true);
+}
+
+
+void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ masm->ret(0);
+}
+
+
+void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference(Debug_Address::RestarterFrameFunctionPointer(),
+ masm->isolate());
+ __ mov(Operand::StaticVariable(restarter_frame_function_slot), Immediate(0));
+
+ // We do not know our frame height, but set esp based on ebp.
+ __ lea(esp, Operand(ebp, -1 * kPointerSize));
+
+ __ pop(edi); // Function.
+ __ pop(ebp);
+
+ // Load context from the function.
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+
+ // Re-run JSFunction, edi is function, esi is context.
+ __ jmp(edx);
+}
+
+const bool Debug::kFrameDropperSupported = true;
+
+#undef __
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/deoptimizer-ia32.cc b/chromium/v8/src/ia32/deoptimizer-ia32.cc
new file mode 100644
index 00000000000..a9bd8c50b72
--- /dev/null
+++ b/chromium/v8/src/ia32/deoptimizer-ia32.cc
@@ -0,0 +1,727 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "codegen.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+#include "safepoint-table.h"
+
+namespace v8 {
+namespace internal {
+
+const int Deoptimizer::table_entry_size_ = 10;
+
+
+int Deoptimizer::patch_size() {
+ return Assembler::kCallInstructionLength;
+}
+
+
+void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) {
+ Isolate* isolate = code->GetIsolate();
+ HandleScope scope(isolate);
+
+ // Compute the size of relocation information needed for the code
+ // patching in Deoptimizer::DeoptimizeFunction.
+ int min_reloc_size = 0;
+ int prev_pc_offset = 0;
+ DeoptimizationInputData* deopt_data =
+ DeoptimizationInputData::cast(code->deoptimization_data());
+ for (int i = 0; i < deopt_data->DeoptCount(); i++) {
+ int pc_offset = deopt_data->Pc(i)->value();
+ if (pc_offset == -1) continue;
+ ASSERT_GE(pc_offset, prev_pc_offset);
+ int pc_delta = pc_offset - prev_pc_offset;
+ // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes
+ // if encodable with small pc delta encoding and up to 6 bytes
+ // otherwise.
+ if (pc_delta <= RelocInfo::kMaxSmallPCDelta) {
+ min_reloc_size += 2;
+ } else {
+ min_reloc_size += 6;
+ }
+ prev_pc_offset = pc_offset;
+ }
+
+ // If the relocation information is not big enough we create a new
+ // relocation info object that is padded with comments to make it
+ // big enough for lazy doptimization.
+ int reloc_length = code->relocation_info()->length();
+ if (min_reloc_size > reloc_length) {
+ int comment_reloc_size = RelocInfo::kMinRelocCommentSize;
+ // Padding needed.
+ int min_padding = min_reloc_size - reloc_length;
+ // Number of comments needed to take up at least that much space.
+ int additional_comments =
+ (min_padding + comment_reloc_size - 1) / comment_reloc_size;
+ // Actual padding size.
+ int padding = additional_comments * comment_reloc_size;
+ // Allocate new relocation info and copy old relocation to the end
+ // of the new relocation info array because relocation info is
+ // written and read backwards.
+ Factory* factory = isolate->factory();
+ Handle<ByteArray> new_reloc =
+ factory->NewByteArray(reloc_length + padding, TENURED);
+ OS::MemCopy(new_reloc->GetDataStartAddress() + padding,
+ code->relocation_info()->GetDataStartAddress(),
+ reloc_length);
+ // Create a relocation writer to write the comments in the padding
+ // space. Use position 0 for everything to ensure short encoding.
+ RelocInfoWriter reloc_info_writer(
+ new_reloc->GetDataStartAddress() + padding, 0);
+ intptr_t comment_string
+ = reinterpret_cast<intptr_t>(RelocInfo::kFillerCommentString);
+ 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();
+#endif
+ reloc_info_writer.Write(&rinfo);
+ ASSERT(RelocInfo::kMinRelocCommentSize ==
+ pos_before - reloc_info_writer.pos());
+ }
+ // Replace relocation information on the code object.
+ code->set_relocation_info(*new_reloc);
+ }
+}
+
+
+void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
+ Address code_start_address = code->instruction_start();
+ // We will overwrite the code's relocation info in-place. Relocation info
+ // is written backward. The relocation info is the payload of a byte
+ // array. Later on we will slide this to the start of the byte array and
+ // create a filler object in the remaining space.
+ ByteArray* reloc_info = code->relocation_info();
+ Address reloc_end_address = reloc_info->address() + reloc_info->Size();
+ RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address);
+
+ // For each LLazyBailout instruction insert a call to the corresponding
+ // deoptimization entry.
+
+ // Since the call is a relative encoding, write new
+ // reloc info. We do not need any of the existing reloc info because the
+ // existing code will not be used again (we zap it in debug builds).
+ //
+ // Emit call to lazy deoptimization at all lazy deopt points.
+ 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;
+ // Patch lazy deoptimization entry.
+ Address call_address = code_start_address + deopt_data->Pc(i)->value();
+ CodePatcher patcher(call_address, patch_size());
+ Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
+ patcher.masm()->call(deopt_entry, RelocInfo::NONE32);
+ // 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),
+ NULL);
+ reloc_info_writer.Write(&rinfo);
+ ASSERT_GE(reloc_info_writer.pos(),
+ reloc_info->address() + ByteArray::kHeaderSize);
+ 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
+ }
+
+ // Move the relocation info to the beginning of the byte array.
+ int new_reloc_size = reloc_end_address - reloc_info_writer.pos();
+ OS::MemMove(
+ code->relocation_start(), reloc_info_writer.pos(), new_reloc_size);
+
+ // The relocation info is in place, update the size.
+ reloc_info->set_length(new_reloc_size);
+
+ // Handle the junk part after the new relocation info. We will create
+ // a non-live object in the extra space at the end of the former reloc info.
+ Address junk_address = reloc_info->address() + reloc_info->Size();
+ ASSERT(junk_address <= reloc_end_address);
+ isolate->heap()->CreateFillerObjectAt(junk_address,
+ reloc_end_address - junk_address);
+}
+
+
+static const byte kJnsInstruction = 0x79;
+static const byte kJnsOffset = 0x11;
+static const byte kCallInstruction = 0xe8;
+static const byte kNopByteOne = 0x66;
+static const byte kNopByteTwo = 0x90;
+
+// The back edge bookkeeping code matches the pattern:
+//
+// sub <profiling_counter>, <delta>
+// jns ok
+// call <interrupt stub>
+// ok:
+//
+// The patched back edge looks like this:
+//
+// sub <profiling_counter>, <delta> ;; Not changed
+// nop
+// nop
+// call <on-stack replacment>
+// ok:
+
+void Deoptimizer::PatchInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ ASSERT(!InterruptCodeIsPatched(unoptimized_code,
+ pc_after,
+ interrupt_code,
+ replacement_code));
+ // Turn the jump into nops.
+ Address call_target_address = pc_after - kIntSize;
+ *(call_target_address - 3) = kNopByteOne;
+ *(call_target_address - 2) = kNopByteTwo;
+ // Replace the call address.
+ 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::RevertInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ ASSERT(InterruptCodeIsPatched(unoptimized_code,
+ pc_after,
+ interrupt_code,
+ replacement_code));
+ // Restore the original jump.
+ Address call_target_address = pc_after - kIntSize;
+ *(call_target_address - 3) = kJnsInstruction;
+ *(call_target_address - 2) = kJnsOffset;
+ // Restore the original call address.
+ Assembler::set_target_address_at(call_target_address,
+ interrupt_code->entry());
+
+ interrupt_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
+ unoptimized_code, call_target_address, interrupt_code);
+}
+
+
+#ifdef DEBUG
+bool Deoptimizer::InterruptCodeIsPatched(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ Address call_target_address = pc_after - kIntSize;
+ ASSERT_EQ(kCallInstruction, *(call_target_address - 1));
+ if (*(call_target_address - 3) == kNopByteOne) {
+ ASSERT_EQ(replacement_code->entry(),
+ Assembler::target_address_at(call_target_address));
+ ASSERT_EQ(kNopByteTwo, *(call_target_address - 2));
+ return true;
+ } else {
+ ASSERT_EQ(interrupt_code->entry(),
+ Assembler::target_address_at(call_target_address));
+ ASSERT_EQ(kJnsInstruction, *(call_target_address - 3));
+ ASSERT_EQ(kJnsOffset, *(call_target_address - 2));
+ return false;
+ }
+}
+#endif // DEBUG
+
+
+static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) {
+ ByteArray* translations = data->TranslationByteArray();
+ int length = data->DeoptCount();
+ for (int i = 0; i < length; i++) {
+ if (data->AstId(i) == 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() {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ compiled_code_->deoptimization_data());
+ unsigned ast_id = data->OsrAstId()->value();
+ // TODO(kasperl): This should not be the bailout_id_. It should be
+ // the ast id. Confusing.
+ ASSERT(bailout_id_ == ast_id);
+
+ int bailout_id = LookupBailoutId(data, BailoutId(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.Next(); // Drop JS frames 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);
+ int closure_id = iterator.Next();
+ USE(closure_id);
+ ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
+ 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 = compiled_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_));
+ PrintFunctionName();
+ PrintF(" => node=%u, frame=%d->%d, ebp:esp=0x%08x:0x%08x]\n",
+ ast_id,
+ input_frame_size,
+ output_frame_size,
+ input_->GetRegister(ebp.code()),
+ input_->GetRegister(esp.code()));
+ }
+
+ // 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;
+ }
+
+ // All OSR stack frames are dynamically aligned to an 8-byte boundary.
+ int frame_pointer = input_->GetRegister(ebp.code());
+ if ((frame_pointer & kPointerSize) != 0) {
+ frame_pointer -= kPointerSize;
+ has_alignment_padding_ = 1;
+ }
+
+ int32_t alignment_state = (has_alignment_padding_ == 1) ?
+ kAlignmentPaddingPushed :
+ kNoAlignmentPadding;
+ if (FLAG_trace_osr) {
+ PrintF(" [sp + %d] <- 0x%08x ; (alignment state)\n",
+ output_offset,
+ alignment_state);
+ }
+ output_[0]->SetFrameSlot(output_offset, alignment_state);
+ 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(ebp.code(), frame_pointer);
+ output_[0]->SetRegister(esi.code(), input_->GetRegister(esi.code()));
+
+ unsigned pc_offset = data->OsrPcOffset()->value();
+ uint32_t pc = reinterpret_cast<uint32_t>(
+ compiled_code_->entry() + pc_offset);
+ output_[0]->SetPc(pc);
+ }
+ Code* continuation =
+ function_->GetIsolate()->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_));
+ PrintFunctionName();
+ PrintF(" => pc=0x%0x]\n", output_[0]->GetPc());
+ }
+}
+
+
+void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
+ // 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 ebp and esp are set to the correct values though.
+
+ for (int i = 0; i < Register::kNumRegisters; i++) {
+ input_->SetRegister(i, i * 4);
+ }
+ input_->SetRegister(esp.code(), reinterpret_cast<intptr_t>(frame->sp()));
+ input_->SetRegister(ebp.code(), reinterpret_cast<intptr_t>(frame->fp()));
+ for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); 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));
+ }
+}
+
+
+void Deoptimizer::SetPlatformCompiledStubRegisters(
+ FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
+ intptr_t handler =
+ reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
+ int params = descriptor->register_param_count_;
+ if (descriptor->stack_parameter_count_ != NULL) {
+ params++;
+ }
+ output_frame->SetRegister(eax.code(), params);
+ output_frame->SetRegister(ebx.code(), handler);
+}
+
+
+void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
+ for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
+ double double_value = input_->GetDoubleRegister(i);
+ output_frame->SetDoubleRegister(i, double_value);
+ }
+}
+
+
+bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
+ int parameter_count = function->shared()->formal_parameter_count() + 1;
+ unsigned input_frame_size = input_->GetFrameSize();
+ unsigned alignment_state_offset =
+ input_frame_size - parameter_count * kPointerSize -
+ StandardFrameConstants::kFixedFrameSize -
+ kPointerSize;
+ ASSERT(JavaScriptFrameConstants::kDynamicAlignmentStateOffset ==
+ JavaScriptFrameConstants::kLocal0Offset);
+ int32_t alignment_state = input_->GetFrameSlot(alignment_state_offset);
+ return (alignment_state == kAlignmentPaddingPushed);
+}
+
+
+#define __ masm()->
+
+void Deoptimizer::EntryGenerator::Generate() {
+ GeneratePrologue();
+
+ // Save all general purpose registers before messing with them.
+ const int kNumberOfRegisters = Register::kNumRegisters;
+
+ const int kDoubleRegsSize = kDoubleSize *
+ XMMRegister::kNumAllocatableRegisters;
+ __ sub(esp, Immediate(kDoubleRegsSize));
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
+ XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
+ int offset = i * kDoubleSize;
+ __ movdbl(Operand(esp, offset), xmm_reg);
+ }
+ }
+
+ __ pushad();
+
+ const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
+ kDoubleRegsSize;
+
+ // Get the bailout id from the stack.
+ __ mov(ebx, Operand(esp, kSavedRegistersAreaSize));
+
+ // Get the address of the location in the code object
+ // and compute the fp-to-sp delta in register edx.
+ __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
+ __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize));
+
+ __ sub(edx, ebp);
+ __ neg(edx);
+
+ // Allocate a new deoptimizer object.
+ __ PrepareCallCFunction(6, eax);
+ __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ mov(Operand(esp, 0 * kPointerSize), eax); // Function.
+ __ mov(Operand(esp, 1 * kPointerSize), Immediate(type())); // Bailout type.
+ __ mov(Operand(esp, 2 * kPointerSize), ebx); // Bailout id.
+ __ mov(Operand(esp, 3 * kPointerSize), ecx); // Code address or 0.
+ __ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta.
+ __ mov(Operand(esp, 5 * kPointerSize),
+ Immediate(ExternalReference::isolate_address(isolate())));
+ {
+ AllowExternalCallThatCantCauseGC scope(masm());
+ __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
+ }
+
+ // Preserve deoptimizer object in register eax and get the input
+ // frame descriptor pointer.
+ __ mov(ebx, Operand(eax, Deoptimizer::input_offset()));
+
+ // Fill in the input registers.
+ for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
+ int offset = (i * kPointerSize) + FrameDescription::registers_offset();
+ __ pop(Operand(ebx, offset));
+ }
+
+ int double_regs_offset = FrameDescription::double_registers_offset();
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ // Fill in the double input registers.
+ for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
+ int dst_offset = i * kDoubleSize + double_regs_offset;
+ int src_offset = i * kDoubleSize;
+ __ movdbl(xmm0, Operand(esp, src_offset));
+ __ movdbl(Operand(ebx, dst_offset), xmm0);
+ }
+ }
+
+ // Clear FPU all exceptions.
+ // TODO(ulan): Find out why the TOP register is not zero here in some cases,
+ // and check that the generated code never deoptimizes with unbalanced stack.
+ __ fnclex();
+
+ // Remove the bailout id, return address and the double registers.
+ __ 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, esp);
+
+ // Unwind the stack down to - but not including - the unwinding
+ // limit and copy the contents of the activation frame to the input
+ // frame description.
+ __ lea(edx, Operand(ebx, FrameDescription::frame_content_offset()));
+ Label pop_loop_header;
+ __ jmp(&pop_loop_header);
+ Label pop_loop;
+ __ bind(&pop_loop);
+ __ pop(Operand(edx, 0));
+ __ add(edx, Immediate(sizeof(uint32_t)));
+ __ bind(&pop_loop_header);
+ __ cmp(ecx, esp);
+ __ j(not_equal, &pop_loop);
+
+ // Compute the output frame in the deoptimizer.
+ __ push(eax);
+ __ PrepareCallCFunction(1, ebx);
+ __ mov(Operand(esp, 0 * kPointerSize), eax);
+ {
+ AllowExternalCallThatCantCauseGC scope(masm());
+ __ CallCFunction(
+ ExternalReference::compute_output_frames_function(isolate()), 1);
+ }
+ __ pop(eax);
+
+ if (type() != OSR) {
+ // If frame was dynamically aligned, pop padding.
+ Label no_padding;
+ __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()),
+ Immediate(0));
+ __ j(equal, &no_padding);
+ __ pop(ecx);
+ if (FLAG_debug_code) {
+ __ cmp(ecx, Immediate(kAlignmentZapValue));
+ __ Assert(equal, kAlignmentMarkerExpected);
+ }
+ __ bind(&no_padding);
+ } else {
+ // If frame needs dynamic alignment push padding.
+ Label no_padding;
+ __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()),
+ Immediate(0));
+ __ j(equal, &no_padding);
+ __ push(Immediate(kAlignmentZapValue));
+ __ bind(&no_padding);
+ }
+
+ // Replace the current frame with the output frames.
+ Label outer_push_loop, inner_push_loop,
+ outer_loop_header, inner_loop_header;
+ // Outer loop state: eax = current FrameDescription**, edx = one past the
+ // last FrameDescription**.
+ __ mov(edx, Operand(eax, Deoptimizer::output_count_offset()));
+ __ mov(eax, Operand(eax, Deoptimizer::output_offset()));
+ __ lea(edx, Operand(eax, edx, times_4, 0));
+ __ jmp(&outer_loop_header);
+ __ bind(&outer_push_loop);
+ // Inner loop state: ebx = current FrameDescription*, ecx = loop index.
+ __ mov(ebx, Operand(eax, 0));
+ __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
+ __ jmp(&inner_loop_header);
+ __ bind(&inner_push_loop);
+ __ sub(ecx, Immediate(sizeof(uint32_t)));
+ __ push(Operand(ebx, ecx, times_1, FrameDescription::frame_content_offset()));
+ __ bind(&inner_loop_header);
+ __ test(ecx, ecx);
+ __ j(not_zero, &inner_push_loop);
+ __ add(eax, Immediate(kPointerSize));
+ __ bind(&outer_loop_header);
+ __ cmp(eax, edx);
+ __ j(below, &outer_push_loop);
+
+ // In case of OSR or a failed STUB, we have to restore the XMM registers.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
+ XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
+ int src_offset = i * kDoubleSize + double_regs_offset;
+ __ movdbl(xmm_reg, Operand(ebx, src_offset));
+ }
+ }
+
+ // Push state, pc, and continuation from the last output frame.
+ if (type() != OSR) {
+ __ push(Operand(ebx, FrameDescription::state_offset()));
+ }
+ __ push(Operand(ebx, FrameDescription::pc_offset()));
+ __ push(Operand(ebx, FrameDescription::continuation_offset()));
+
+
+ // Push the registers from the last output frame.
+ for (int i = 0; i < kNumberOfRegisters; i++) {
+ int offset = (i * kPointerSize) + FrameDescription::registers_offset();
+ __ push(Operand(ebx, offset));
+ }
+
+ // Restore the registers from the stack.
+ __ popad();
+
+ // Return to the continuation point.
+ __ ret(0);
+}
+
+
+void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
+ // Create a sequence of deoptimization entries.
+ Label done;
+ for (int i = 0; i < count(); i++) {
+ int start = masm()->pc_offset();
+ USE(start);
+ __ push_imm32(i);
+ __ jmp(&done);
+ ASSERT(masm()->pc_offset() - start == table_entry_size_);
+ }
+ __ bind(&done);
+}
+
+
+void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
+ SetFrameSlot(offset, value);
+}
+
+
+void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
+ SetFrameSlot(offset, value);
+}
+
+
+#undef __
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/disasm-ia32.cc b/chromium/v8/src/ia32/disasm-ia32.cc
new file mode 100644
index 00000000000..c43f11c00e9
--- /dev/null
+++ b/chromium/v8/src/ia32/disasm-ia32.cc
@@ -0,0 +1,1727 @@
+// 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 <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "disasm.h"
+
+namespace disasm {
+
+enum OperandOrder {
+ UNSET_OP_ORDER = 0,
+ REG_OPER_OP_ORDER,
+ OPER_REG_OP_ORDER
+};
+
+
+//------------------------------------------------------------------
+// Tables
+//------------------------------------------------------------------
+struct ByteMnemonic {
+ int b; // -1 terminates, otherwise must be in range (0..255)
+ const char* mnem;
+ OperandOrder op_order_;
+};
+
+
+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},
+ {0x1B, "sbb", REG_OPER_OP_ORDER},
+ {0x21, "and", OPER_REG_OP_ORDER},
+ {0x23, "and", REG_OPER_OP_ORDER},
+ {0x29, "sub", OPER_REG_OP_ORDER},
+ {0x2A, "subb", REG_OPER_OP_ORDER},
+ {0x2B, "sub", REG_OPER_OP_ORDER},
+ {0x31, "xor", OPER_REG_OP_ORDER},
+ {0x33, "xor", REG_OPER_OP_ORDER},
+ {0x38, "cmpb", OPER_REG_OP_ORDER},
+ {0x3A, "cmpb", REG_OPER_OP_ORDER},
+ {0x3B, "cmp", REG_OPER_OP_ORDER},
+ {0x84, "test_b", REG_OPER_OP_ORDER},
+ {0x85, "test", REG_OPER_OP_ORDER},
+ {0x87, "xchg", REG_OPER_OP_ORDER},
+ {0x8A, "mov_b", REG_OPER_OP_ORDER},
+ {0x8B, "mov", REG_OPER_OP_ORDER},
+ {0x8D, "lea", REG_OPER_OP_ORDER},
+ {-1, "", UNSET_OP_ORDER}
+};
+
+
+static const ByteMnemonic zero_operands_instr[] = {
+ {0xC3, "ret", UNSET_OP_ORDER},
+ {0xC9, "leave", UNSET_OP_ORDER},
+ {0x90, "nop", UNSET_OP_ORDER},
+ {0xF4, "hlt", UNSET_OP_ORDER},
+ {0xCC, "int3", UNSET_OP_ORDER},
+ {0x60, "pushad", UNSET_OP_ORDER},
+ {0x61, "popad", UNSET_OP_ORDER},
+ {0x9C, "pushfd", UNSET_OP_ORDER},
+ {0x9D, "popfd", UNSET_OP_ORDER},
+ {0x9E, "sahf", UNSET_OP_ORDER},
+ {0x99, "cdq", UNSET_OP_ORDER},
+ {0x9B, "fwait", UNSET_OP_ORDER},
+ {0xFC, "cld", UNSET_OP_ORDER},
+ {0xAB, "stos", UNSET_OP_ORDER},
+ {-1, "", UNSET_OP_ORDER}
+};
+
+
+static const ByteMnemonic call_jump_instr[] = {
+ {0xE8, "call", UNSET_OP_ORDER},
+ {0xE9, "jmp", UNSET_OP_ORDER},
+ {-1, "", UNSET_OP_ORDER}
+};
+
+
+static const ByteMnemonic short_immediate_instr[] = {
+ {0x05, "add", UNSET_OP_ORDER},
+ {0x0D, "or", UNSET_OP_ORDER},
+ {0x15, "adc", UNSET_OP_ORDER},
+ {0x25, "and", UNSET_OP_ORDER},
+ {0x2D, "sub", UNSET_OP_ORDER},
+ {0x35, "xor", UNSET_OP_ORDER},
+ {0x3D, "cmp", UNSET_OP_ORDER},
+ {-1, "", UNSET_OP_ORDER}
+};
+
+
+// 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",
+ /*8*/ "js", "jns", "jpe", "jpo",
+ /*12*/ "jl", "jnl", "jng", "jg"
+};
+
+
+static const char* const set_conditional_mnem[] = {
+ /*0*/ "seto", "setno", "setc", "setnc",
+ /*4*/ "setz", "setnz", "setna", "seta",
+ /*8*/ "sets", "setns", "setpe", "setpo",
+ /*12*/ "setl", "setnl", "setng", "setg"
+};
+
+
+static const char* const conditional_move_mnem[] = {
+ /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc",
+ /*4*/ "cmovz", "cmovnz", "cmovna", "cmova",
+ /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo",
+ /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg"
+};
+
+
+enum InstructionType {
+ NO_INSTR,
+ ZERO_OPERANDS_INSTR,
+ TWO_OPERANDS_INSTR,
+ JUMP_CONDITIONAL_SHORT_INSTR,
+ REGISTER_INSTR,
+ MOVE_REG_INSTR,
+ CALL_JUMP_INSTR,
+ SHORT_IMMEDIATE_INSTR,
+ BYTE_IMMEDIATE_INSTR
+};
+
+
+struct InstructionDesc {
+ const char* mnem;
+ InstructionType type;
+ OperandOrder op_order_;
+};
+
+
+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];
+ void Clear();
+ void Init();
+ void CopyTable(const ByteMnemonic bm[], InstructionType type);
+ void SetTableRange(InstructionType type,
+ byte start,
+ byte end,
+ const char* mnem);
+ void AddJumpConditionalShort();
+};
+
+
+InstructionTable::InstructionTable() {
+ Clear();
+ Init();
+}
+
+
+void InstructionTable::Clear() {
+ for (int i = 0; i < 256; i++) {
+ instructions_[i].mnem = "";
+ instructions_[i].type = NO_INSTR;
+ instructions_[i].op_order_ = UNSET_OP_ORDER;
+ }
+}
+
+
+void InstructionTable::Init() {
+ CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
+ 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");
+ SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push");
+ SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop");
+ SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop.
+ SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov");
+}
+
+
+void InstructionTable::CopyTable(const ByteMnemonic bm[],
+ InstructionType type) {
+ for (int i = 0; bm[i].b >= 0; i++) {
+ InstructionDesc* id = &instructions_[bm[i].b];
+ id->mnem = bm[i].mnem;
+ id->op_order_ = bm[i].op_order_;
+ ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
+ id->type = type;
+ }
+}
+
+
+void InstructionTable::SetTableRange(InstructionType type,
+ byte start,
+ byte end,
+ const char* mnem) {
+ for (byte b = start; b <= end; b++) {
+ InstructionDesc* id = &instructions_[b];
+ ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
+ id->mnem = mnem;
+ id->type = type;
+ }
+}
+
+
+void InstructionTable::AddJumpConditionalShort() {
+ for (byte b = 0x70; b <= 0x7F; b++) {
+ InstructionDesc* id = &instructions_[b];
+ ASSERT_EQ(NO_INSTR, id->type); // Information not already entered.
+ id->mnem = jump_conditional_mnem[b & 0x0F];
+ id->type = JUMP_CONDITIONAL_SHORT_INSTR;
+ }
+}
+
+
+// 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';
+ }
+
+ virtual ~DisassemblerIA32() {}
+
+ // Writes one disassembled instruction into 'buffer' (0-terminated).
+ // Returns the length of the disassembled machine instruction in bytes.
+ int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
+
+ 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,
+ edx = 2,
+ ebx = 3,
+ esp = 4,
+ ebp = 5,
+ esi = 6,
+ edi = 7
+ };
+
+
+ enum ShiftOpcodeExtension {
+ kROL = 0,
+ kROR = 1,
+ kRCL = 2,
+ kRCR = 3,
+ kSHL = 4,
+ KSHR = 5,
+ kSAR = 7
+ };
+
+
+ const char* NameOfCPURegister(int reg) const {
+ return converter_.NameOfCPURegister(reg);
+ }
+
+
+ const char* NameOfByteCPURegister(int reg) const {
+ return converter_.NameOfByteCPURegister(reg);
+ }
+
+
+ const char* NameOfXMMRegister(int reg) const {
+ return converter_.NameOfXMMRegister(reg);
+ }
+
+
+ const char* NameOfAddress(byte* addr) const {
+ return converter_.NameOfAddress(addr);
+ }
+
+
+ // Disassembler helper functions.
+ static void get_modrm(byte data, int* mod, int* regop, int* rm) {
+ *mod = (data >> 6) & 3;
+ *regop = (data & 0x38) >> 3;
+ *rm = data & 7;
+ }
+
+
+ static void get_sib(byte data, int* scale, int* index, int* base) {
+ *scale = (data >> 6) & 3;
+ *index = (data >> 3) & 7;
+ *base = data & 7;
+ }
+
+ typedef const char* (DisassemblerIA32::*RegisterNameMapping)(int reg) const;
+
+ int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name);
+ int PrintRightOperand(byte* modrmp);
+ int PrintRightByteOperand(byte* modrmp);
+ int PrintRightXMMOperand(byte* modrmp);
+ int PrintOperands(const char* mnem, OperandOrder op_order, byte* data);
+ int PrintImmediateOp(byte* data);
+ int F7Instruction(byte* data);
+ int D1D3C1Instruction(byte* data);
+ int JumpShort(byte* data);
+ int JumpConditional(byte* data, const char* comment);
+ int JumpConditionalShort(byte* data, const char* comment);
+ int SetCC(byte* data);
+ int CMov(byte* data);
+ int FPUInstruction(byte* data);
+ int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
+ int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
+ void AppendToBuffer(const char* format, ...);
+
+
+ void UnimplementedInstruction() {
+ if (abort_on_unimplemented_) {
+ UNIMPLEMENTED();
+ } else {
+ AppendToBuffer("'Unimplemented Instruction'");
+ }
+ }
+};
+
+
+void DisassemblerIA32::AppendToBuffer(const char* format, ...) {
+ v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
+ va_list args;
+ va_start(args, format);
+ int result = v8::internal::OS::VSNPrintF(buf, format, args);
+ va_end(args);
+ tmp_buffer_pos_ += result;
+}
+
+int DisassemblerIA32::PrintRightOperandHelper(
+ byte* modrmp,
+ RegisterNameMapping direct_register_name) {
+ int mod, regop, rm;
+ get_modrm(*modrmp, &mod, &regop, &rm);
+ RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
+ &DisassemblerIA32::NameOfCPURegister;
+ switch (mod) {
+ case 0:
+ if (rm == ebp) {
+ int32_t disp = *reinterpret_cast<int32_t*>(modrmp+1);
+ AppendToBuffer("[0x%x]", disp);
+ return 5;
+ } else if (rm == esp) {
+ byte sib = *(modrmp + 1);
+ int scale, index, base;
+ get_sib(sib, &scale, &index, &base);
+ if (index == esp && base == esp && scale == 0 /*times_1*/) {
+ AppendToBuffer("[%s]", (this->*register_name)(rm));
+ return 2;
+ } else if (base == ebp) {
+ int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
+ AppendToBuffer("[%s*%d+0x%x]",
+ (this->*register_name)(index),
+ 1 << scale,
+ disp);
+ return 6;
+ } else if (index != esp && base != ebp) {
+ // [base+index*scale]
+ AppendToBuffer("[%s+%s*%d]",
+ (this->*register_name)(base),
+ (this->*register_name)(index),
+ 1 << scale);
+ return 2;
+ } else {
+ UnimplementedInstruction();
+ return 1;
+ }
+ } else {
+ AppendToBuffer("[%s]", (this->*register_name)(rm));
+ return 1;
+ }
+ break;
+ case 1: // fall through
+ case 2:
+ if (rm == esp) {
+ byte sib = *(modrmp + 1);
+ int scale, index, base;
+ get_sib(sib, &scale, &index, &base);
+ int disp =
+ mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 2) : *(modrmp + 2);
+ if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) {
+ AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
+ } else {
+ AppendToBuffer("[%s+%s*%d+0x%x]",
+ (this->*register_name)(base),
+ (this->*register_name)(index),
+ 1 << scale,
+ disp);
+ }
+ return mod == 2 ? 6 : 3;
+ } else {
+ // No sib.
+ int disp =
+ mod == 2 ? *reinterpret_cast<int32_t*>(modrmp + 1) : *(modrmp + 1);
+ AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
+ return mod == 2 ? 5 : 2;
+ }
+ break;
+ case 3:
+ AppendToBuffer("%s", (this->*register_name)(rm));
+ return 1;
+ default:
+ UnimplementedInstruction();
+ return 1;
+ }
+ UNREACHABLE();
+}
+
+
+int DisassemblerIA32::PrintRightOperand(byte* modrmp) {
+ return PrintRightOperandHelper(modrmp, &DisassemblerIA32::NameOfCPURegister);
+}
+
+
+int DisassemblerIA32::PrintRightByteOperand(byte* modrmp) {
+ return PrintRightOperandHelper(modrmp,
+ &DisassemblerIA32::NameOfByteCPURegister);
+}
+
+
+int DisassemblerIA32::PrintRightXMMOperand(byte* modrmp) {
+ return PrintRightOperandHelper(modrmp,
+ &DisassemblerIA32::NameOfXMMRegister);
+}
+
+
+// Returns number of bytes used including the current *data.
+// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
+int DisassemblerIA32::PrintOperands(const char* mnem,
+ OperandOrder op_order,
+ byte* data) {
+ byte modrm = *data;
+ int mod, regop, rm;
+ get_modrm(modrm, &mod, &regop, &rm);
+ int advance = 0;
+ switch (op_order) {
+ case REG_OPER_OP_ORDER: {
+ AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
+ advance = PrintRightOperand(data);
+ break;
+ }
+ case OPER_REG_OP_ORDER: {
+ AppendToBuffer("%s ", mnem);
+ advance = PrintRightOperand(data);
+ AppendToBuffer(",%s", NameOfCPURegister(regop));
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ return advance;
+}
+
+
+// Returns number of bytes used by machine instruction, including *data byte.
+// Writes immediate instructions to 'tmp_buffer_'.
+int DisassemblerIA32::PrintImmediateOp(byte* data) {
+ bool sign_extension_bit = (*data & 0x02) != 0;
+ byte modrm = *(data+1);
+ int mod, regop, rm;
+ get_modrm(modrm, &mod, &regop, &rm);
+ const char* mnem = "Imm???";
+ switch (regop) {
+ case 0: mnem = "add"; break;
+ case 1: mnem = "or"; break;
+ case 2: mnem = "adc"; break;
+ case 4: mnem = "and"; break;
+ case 5: mnem = "sub"; break;
+ case 6: mnem = "xor"; break;
+ case 7: mnem = "cmp"; break;
+ default: UnimplementedInstruction();
+ }
+ AppendToBuffer("%s ", mnem);
+ int count = PrintRightOperand(data+1);
+ if (sign_extension_bit) {
+ AppendToBuffer(",0x%x", *(data + 1 + count));
+ return 1 + count + 1 /*int8*/;
+ } else {
+ AppendToBuffer(",0x%x", *reinterpret_cast<int32_t*>(data + 1 + count));
+ return 1 + count + 4 /*int32_t*/;
+ }
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerIA32::F7Instruction(byte* data) {
+ ASSERT_EQ(0xF7, *data);
+ byte modrm = *(data+1);
+ int mod, regop, rm;
+ get_modrm(modrm, &mod, &regop, &rm);
+ if (mod == 3 && regop != 0) {
+ const char* mnem = NULL;
+ switch (regop) {
+ case 2: mnem = "not"; break;
+ case 3: mnem = "neg"; break;
+ case 4: mnem = "mul"; break;
+ case 5: mnem = "imul"; break;
+ case 7: mnem = "idiv"; break;
+ default: UnimplementedInstruction();
+ }
+ AppendToBuffer("%s %s", mnem, NameOfCPURegister(rm));
+ return 2;
+ } else if (mod == 3 && regop == eax) {
+ int32_t imm = *reinterpret_cast<int32_t*>(data+2);
+ AppendToBuffer("test %s,0x%x", NameOfCPURegister(rm), imm);
+ return 6;
+ } else if (regop == eax) {
+ AppendToBuffer("test ");
+ int count = PrintRightOperand(data+1);
+ int32_t imm = *reinterpret_cast<int32_t*>(data+1+count);
+ AppendToBuffer(",0x%x", imm);
+ return 1+count+4 /*int32_t*/;
+ } else {
+ UnimplementedInstruction();
+ return 2;
+ }
+}
+
+
+int DisassemblerIA32::D1D3C1Instruction(byte* data) {
+ byte op = *data;
+ ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1);
+ byte modrm = *(data+1);
+ int mod, regop, rm;
+ get_modrm(modrm, &mod, &regop, &rm);
+ int imm8 = -1;
+ int num_bytes = 2;
+ if (mod == 3) {
+ const char* mnem = NULL;
+ switch (regop) {
+ case kROL: mnem = "rol"; break;
+ case kROR: mnem = "ror"; break;
+ case kRCL: mnem = "rcl"; break;
+ case kRCR: mnem = "rcr"; break;
+ case kSHL: mnem = "shl"; break;
+ case KSHR: mnem = "shr"; break;
+ case kSAR: mnem = "sar"; break;
+ default: UnimplementedInstruction();
+ }
+ if (op == 0xD1) {
+ imm8 = 1;
+ } else if (op == 0xC1) {
+ imm8 = *(data+2);
+ num_bytes = 3;
+ } else if (op == 0xD3) {
+ // Shift/rotate by cl.
+ }
+ ASSERT_NE(NULL, mnem);
+ AppendToBuffer("%s %s,", mnem, NameOfCPURegister(rm));
+ if (imm8 > 0) {
+ AppendToBuffer("%d", imm8);
+ } else {
+ AppendToBuffer("cl");
+ }
+ } else {
+ UnimplementedInstruction();
+ }
+ return num_bytes;
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerIA32::JumpShort(byte* data) {
+ ASSERT_EQ(0xEB, *data);
+ byte b = *(data+1);
+ byte* dest = data + static_cast<int8_t>(b) + 2;
+ AppendToBuffer("jmp %s", NameOfAddress(dest));
+ return 2;
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerIA32::JumpConditional(byte* data, const char* comment) {
+ ASSERT_EQ(0x0F, *data);
+ byte cond = *(data+1) & 0x0F;
+ byte* dest = data + *reinterpret_cast<int32_t*>(data+2) + 6;
+ const char* mnem = jump_conditional_mnem[cond];
+ AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
+ if (comment != NULL) {
+ AppendToBuffer(", %s", comment);
+ }
+ return 6; // includes 0x0F
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerIA32::JumpConditionalShort(byte* data, const char* comment) {
+ byte cond = *data & 0x0F;
+ byte b = *(data+1);
+ byte* dest = data + static_cast<int8_t>(b) + 2;
+ const char* mnem = jump_conditional_mnem[cond];
+ AppendToBuffer("%s %s", mnem, NameOfAddress(dest));
+ if (comment != NULL) {
+ AppendToBuffer(", %s", comment);
+ }
+ return 2;
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerIA32::SetCC(byte* data) {
+ ASSERT_EQ(0x0F, *data);
+ byte cond = *(data+1) & 0x0F;
+ const char* mnem = set_conditional_mnem[cond];
+ AppendToBuffer("%s ", mnem);
+ PrintRightByteOperand(data+2);
+ return 3; // Includes 0x0F.
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerIA32::CMov(byte* data) {
+ ASSERT_EQ(0x0F, *data);
+ byte cond = *(data + 1) & 0x0F;
+ const char* mnem = conditional_move_mnem[cond];
+ int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2);
+ return 2 + op_size; // includes 0x0F
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerIA32::FPUInstruction(byte* data) {
+ byte escape_opcode = *data;
+ ASSERT_EQ(0xD8, escape_opcode & 0xF8);
+ byte modrm_byte = *(data+1);
+
+ if (modrm_byte >= 0xC0) {
+ return RegisterFPUInstruction(escape_opcode, modrm_byte);
+ } else {
+ return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
+ }
+}
+
+int DisassemblerIA32::MemoryFPUInstruction(int escape_opcode,
+ int modrm_byte,
+ byte* modrm_start) {
+ const char* mnem = "?";
+ int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
+ switch (escape_opcode) {
+ case 0xD9: switch (regop) {
+ case 0: mnem = "fld_s"; break;
+ case 3: mnem = "fstp_s"; break;
+ case 7: mnem = "fstcw"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDB: switch (regop) {
+ case 0: mnem = "fild_s"; break;
+ case 1: mnem = "fisttp_s"; break;
+ case 2: mnem = "fist_s"; break;
+ case 3: mnem = "fistp_s"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDD: switch (regop) {
+ case 0: mnem = "fld_d"; break;
+ case 1: mnem = "fisttp_d"; break;
+ case 2: mnem = "fst_d"; break;
+ case 3: mnem = "fstp_d"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDF: switch (regop) {
+ case 5: mnem = "fild_d"; break;
+ case 7: mnem = "fistp_d"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ default: UnimplementedInstruction();
+ }
+ AppendToBuffer("%s ", mnem);
+ int count = PrintRightOperand(modrm_start);
+ return count + 1;
+}
+
+int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode,
+ byte modrm_byte) {
+ bool has_register = false; // Is the FPU register encoded in modrm_byte?
+ const char* mnem = "?";
+
+ switch (escape_opcode) {
+ case 0xD8:
+ UnimplementedInstruction();
+ break;
+
+ case 0xD9:
+ switch (modrm_byte & 0xF8) {
+ case 0xC0:
+ mnem = "fld";
+ has_register = true;
+ break;
+ case 0xC8:
+ mnem = "fxch";
+ has_register = true;
+ break;
+ default:
+ switch (modrm_byte) {
+ case 0xE0: mnem = "fchs"; break;
+ case 0xE1: mnem = "fabs"; 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 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();
+ }
+ }
+ break;
+
+ case 0xDA:
+ if (modrm_byte == 0xE9) {
+ mnem = "fucompp";
+ } else {
+ UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDB:
+ if ((modrm_byte & 0xF8) == 0xE8) {
+ mnem = "fucomi";
+ has_register = true;
+ } else if (modrm_byte == 0xE2) {
+ mnem = "fclex";
+ } else if (modrm_byte == 0xE3) {
+ mnem = "fninit";
+ } else {
+ UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDC:
+ has_register = true;
+ switch (modrm_byte & 0xF8) {
+ case 0xC0: mnem = "fadd"; break;
+ case 0xE8: mnem = "fsub"; break;
+ case 0xC8: mnem = "fmul"; break;
+ case 0xF8: mnem = "fdiv"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDD:
+ has_register = true;
+ switch (modrm_byte & 0xF8) {
+ case 0xC0: mnem = "ffree"; break;
+ case 0xD8: mnem = "fstp"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDE:
+ if (modrm_byte == 0xD9) {
+ mnem = "fcompp";
+ } else {
+ has_register = true;
+ switch (modrm_byte & 0xF8) {
+ case 0xC0: mnem = "faddp"; break;
+ case 0xE8: mnem = "fsubp"; break;
+ case 0xC8: mnem = "fmulp"; break;
+ case 0xF8: mnem = "fdivp"; break;
+ default: UnimplementedInstruction();
+ }
+ }
+ break;
+
+ case 0xDF:
+ if (modrm_byte == 0xE0) {
+ mnem = "fnstsw_ax";
+ } else if ((modrm_byte & 0xF8) == 0xE8) {
+ mnem = "fucomip";
+ has_register = true;
+ }
+ break;
+
+ default: UnimplementedInstruction();
+ }
+
+ if (has_register) {
+ AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
+ } else {
+ AppendToBuffer("%s", mnem);
+ }
+ return 2;
+}
+
+
+// Mnemonics for instructions 0xF0 byte.
+// Returns NULL if the instruction is not handled here.
+static const char* F0Mnem(byte f0byte) {
+ switch (f0byte) {
+ case 0x18: return "prefetch";
+ case 0xA2: return "cpuid";
+ case 0x31: return "rdtsc";
+ case 0xBE: return "movsx_b";
+ case 0xBF: return "movsx_w";
+ case 0xB6: return "movzx_b";
+ case 0xB7: return "movzx_w";
+ case 0xAF: return "imul";
+ case 0xA5: return "shld";
+ case 0xAD: return "shrd";
+ case 0xAC: return "shrd"; // 3-operand version.
+ case 0xAB: return "bts";
+ default: return NULL;
+ }
+}
+
+
+// Disassembled instruction '*instr' and writes it into 'out_buffer'.
+int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
+ byte* instr) {
+ tmp_buffer_pos_ = 0; // starting to write as position 0
+ byte* data = instr;
+ // Check for hints.
+ const char* branch_hint = NULL;
+ // We use these two prefixes only with branch prediction
+ if (*data == 0x3E /*ds*/) {
+ branch_hint = "predicted taken";
+ data++;
+ } else if (*data == 0x2E /*cs*/) {
+ branch_hint = "predicted not taken";
+ data++;
+ }
+ bool processed = true; // Will be set to false if the current instruction
+ // is not in 'instructions' table.
+ const InstructionDesc& idesc = instruction_table_->Get(*data);
+ switch (idesc.type) {
+ case ZERO_OPERANDS_INSTR:
+ AppendToBuffer(idesc.mnem);
+ data++;
+ break;
+
+ case TWO_OPERANDS_INSTR:
+ data++;
+ data += PrintOperands(idesc.mnem, idesc.op_order_, data);
+ break;
+
+ case JUMP_CONDITIONAL_SHORT_INSTR:
+ data += JumpConditionalShort(data, branch_hint);
+ break;
+
+ case REGISTER_INSTR:
+ AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
+ data++;
+ break;
+
+ case MOVE_REG_INSTR: {
+ byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
+ AppendToBuffer("mov %s,%s",
+ NameOfCPURegister(*data & 0x07),
+ NameOfAddress(addr));
+ data += 5;
+ break;
+ }
+
+ case CALL_JUMP_INSTR: {
+ byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
+ AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
+ data += 5;
+ break;
+ }
+
+ case SHORT_IMMEDIATE_INSTR: {
+ byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
+ AppendToBuffer("%s eax, %s", idesc.mnem, NameOfAddress(addr));
+ data += 5;
+ break;
+ }
+
+ case BYTE_IMMEDIATE_INSTR: {
+ AppendToBuffer("%s al, 0x%x", idesc.mnem, data[1]);
+ data += 2;
+ break;
+ }
+
+ case NO_INSTR:
+ processed = false;
+ break;
+
+ default:
+ UNIMPLEMENTED(); // This type is not implemented.
+ }
+ //----------------------------
+ if (!processed) {
+ switch (*data) {
+ case 0xC2:
+ AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data+1));
+ data += 3;
+ break;
+
+ case 0x69: // fall through
+ case 0x6B:
+ { int mod, regop, rm;
+ get_modrm(*(data+1), &mod, &regop, &rm);
+ int32_t imm =
+ *data == 0x6B ? *(data+2) : *reinterpret_cast<int32_t*>(data+2);
+ AppendToBuffer("imul %s,%s,0x%x",
+ NameOfCPURegister(regop),
+ NameOfCPURegister(rm),
+ imm);
+ data += 2 + (*data == 0x6B ? 1 : 4);
+ }
+ break;
+
+ case 0xF6:
+ { data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (regop == eax) {
+ AppendToBuffer("test_b ");
+ data += PrintRightByteOperand(data);
+ int32_t imm = *data;
+ AppendToBuffer(",0x%x", imm);
+ data++;
+ } else {
+ UnimplementedInstruction();
+ }
+ }
+ break;
+
+ case 0x81: // fall through
+ case 0x83: // 0x81 with sign extension bit set
+ data += PrintImmediateOp(data);
+ break;
+
+ case 0x0F:
+ { byte f0byte = data[1];
+ const char* f0mnem = F0Mnem(f0byte);
+ if (f0byte == 0x18) {
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ 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;
+ } else if (f0byte == 0x28) {
+ data += 2;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movaps %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (f0byte == 0x57) {
+ data += 2;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("xorps %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (f0byte == 0x50) {
+ data += 2;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movmskps %s,%s",
+ NameOfCPURegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if ((f0byte & 0xF0) == 0x80) {
+ data += JumpConditional(data, branch_hint);
+ } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
+ f0byte == 0xB7 || f0byte == 0xAF) {
+ data += 2;
+ data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data);
+ } else if ((f0byte & 0xF0) == 0x90) {
+ data += SetCC(data);
+ } else if ((f0byte & 0xF0) == 0x40) {
+ data += CMov(data);
+ } else {
+ data += 2;
+ if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) {
+ // shrd, shld, bts
+ AppendToBuffer("%s ", f0mnem);
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ data += PrintRightOperand(data);
+ if (f0byte == 0xAB) {
+ AppendToBuffer(",%s", NameOfCPURegister(regop));
+ } else {
+ AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
+ }
+ } else {
+ UnimplementedInstruction();
+ }
+ }
+ }
+ break;
+
+ case 0x8F:
+ { data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (regop == eax) {
+ AppendToBuffer("pop ");
+ data += PrintRightOperand(data);
+ }
+ }
+ break;
+
+ case 0xFF:
+ { data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ const char* mnem = NULL;
+ switch (regop) {
+ case esi: mnem = "push"; break;
+ case eax: mnem = "inc"; break;
+ case ecx: mnem = "dec"; break;
+ case edx: mnem = "call"; break;
+ case esp: mnem = "jmp"; break;
+ default: mnem = "???";
+ }
+ AppendToBuffer("%s ", mnem);
+ data += PrintRightOperand(data);
+ }
+ break;
+
+ case 0xC7: // imm32, fall through
+ case 0xC6: // imm8
+ { bool is_byte = *data == 0xC6;
+ data++;
+ if (is_byte) {
+ AppendToBuffer("%s ", "mov_b");
+ data += PrintRightByteOperand(data);
+ int32_t imm = *data;
+ AppendToBuffer(",0x%x", imm);
+ data++;
+ } else {
+ AppendToBuffer("%s ", "mov");
+ data += PrintRightOperand(data);
+ int32_t imm = *reinterpret_cast<int32_t*>(data);
+ AppendToBuffer(",0x%x", imm);
+ data += 4;
+ }
+ }
+ break;
+
+ case 0x80:
+ { data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ const char* mnem = NULL;
+ switch (regop) {
+ case 5: mnem = "subb"; break;
+ case 7: mnem = "cmpb"; break;
+ default: UnimplementedInstruction();
+ }
+ AppendToBuffer("%s ", mnem);
+ data += PrintRightByteOperand(data);
+ int32_t imm = *data;
+ AppendToBuffer(",0x%x", imm);
+ data++;
+ }
+ break;
+
+ case 0x88: // 8bit, fall through
+ case 0x89: // 32bit
+ { bool is_byte = *data == 0x88;
+ int mod, regop, rm;
+ data++;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (is_byte) {
+ AppendToBuffer("%s ", "mov_b");
+ data += PrintRightByteOperand(data);
+ AppendToBuffer(",%s", NameOfByteCPURegister(regop));
+ } else {
+ AppendToBuffer("%s ", "mov");
+ data += PrintRightOperand(data);
+ AppendToBuffer(",%s", NameOfCPURegister(regop));
+ }
+ }
+ break;
+
+ case 0x66: // prefix
+ 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) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("mov_w ");
+ data += PrintRightOperand(data);
+ AppendToBuffer(",%s", NameOfCPURegister(regop));
+ } else if (*data == 0x0F) {
+ data++;
+ if (*data == 0x38) {
+ data++;
+ if (*data == 0x17) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("ptest %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0x2A) {
+ // movntdqa
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movntdqa %s,", NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (*data == 0x3A) {
+ data++;
+ if (*data == 0x0B) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ int8_t imm8 = static_cast<int8_t>(data[1]);
+ AppendToBuffer("roundsd %s,%s,%d",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm),
+ static_cast<int>(imm8));
+ data += 2;
+ } else if (*data == 0x16) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ int8_t imm8 = static_cast<int8_t>(data[1]);
+ AppendToBuffer("pextrd %s,%s,%d",
+ NameOfCPURegister(regop),
+ 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;
+ get_modrm(*data, &mod, &regop, &rm);
+ int8_t imm8 = static_cast<int8_t>(data[1]);
+ AppendToBuffer("pinsrd %s,%s,%d",
+ NameOfXMMRegister(regop),
+ NameOfCPURegister(rm),
+ static_cast<int>(imm8));
+ data += 2;
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (*data == 0x2E || *data == 0x2F) {
+ const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd";
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (mod == 0x3) {
+ AppendToBuffer("%s %s,%s", mnem,
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else {
+ AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
+ }
+ } else if (*data == 0x50) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movmskpd %s,%s",
+ NameOfCPURegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0x54) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("andpd %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0x56) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("orpd %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0x57) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("xorpd %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0x6E) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
+ } else if (*data == 0x6F) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop));
+ data += PrintRightXMMOperand(data);
+ } else if (*data == 0x70) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ int8_t imm8 = static_cast<int8_t>(data[1]);
+ AppendToBuffer("pshufd %s,%s,%d",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm),
+ static_cast<int>(imm8));
+ data += 2;
+ } else if (*data == 0x76) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("pcmpeqd %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0x90) {
+ data++;
+ AppendToBuffer("nop"); // 2 byte nop.
+ } else if (*data == 0xF3) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("psllq %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0x73) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ int8_t imm8 = static_cast<int8_t>(data[1]);
+ ASSERT(regop == esi || regop == edx);
+ AppendToBuffer("%s %s,%d",
+ (regop == esi) ? "psllq" : "psrlq",
+ NameOfXMMRegister(rm),
+ static_cast<int>(imm8));
+ data += 2;
+ } else if (*data == 0xD3) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("psrlq %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0x7F) {
+ AppendToBuffer("movdqa ");
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ data += PrintRightXMMOperand(data);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else if (*data == 0x7E) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movd ");
+ data += PrintRightOperand(data);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else if (*data == 0xDB) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("pand %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0xE7) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (mod == 3) {
+ AppendToBuffer("movntdq ");
+ data += PrintRightOperand(data);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (*data == 0xEF) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("pxor %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else if (*data == 0xEB) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("por %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else {
+ UnimplementedInstruction();
+ }
+ } else {
+ UnimplementedInstruction();
+ }
+ break;
+
+ case 0xFE:
+ { data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (regop == ecx) {
+ AppendToBuffer("dec_b ");
+ data += PrintRightOperand(data);
+ } else {
+ UnimplementedInstruction();
+ }
+ }
+ break;
+
+ case 0x68:
+ AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data+1));
+ data += 5;
+ break;
+
+ case 0x6A:
+ AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
+ data += 2;
+ break;
+
+ case 0xA8:
+ AppendToBuffer("test al,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;
+ break;
+
+ case 0xD1: // fall through
+ case 0xD3: // fall through
+ case 0xC1:
+ data += D1D3C1Instruction(data);
+ break;
+
+ case 0xD9: // fall through
+ case 0xDA: // fall through
+ case 0xDB: // fall through
+ case 0xDC: // fall through
+ case 0xDD: // fall through
+ case 0xDE: // fall through
+ case 0xDF:
+ data += FPUInstruction(data);
+ break;
+
+ case 0xEB:
+ data += JumpShort(data);
+ break;
+
+ case 0xF2:
+ if (*(data+1) == 0x0F) {
+ byte b2 = *(data+2);
+ if (b2 == 0x11) {
+ AppendToBuffer("movsd ");
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ data += PrintRightXMMOperand(data);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else if (b2 == 0x10) {
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movsd %s,", NameOfXMMRegister(regop));
+ data += PrintRightXMMOperand(data);
+ } else if (b2 == 0x5A) {
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("cvtsd2ss %s,", NameOfXMMRegister(regop));
+ data += PrintRightXMMOperand(data);
+ } else {
+ const char* mnem = "?";
+ switch (b2) {
+ case 0x2A: mnem = "cvtsi2sd"; break;
+ case 0x2C: mnem = "cvttsd2si"; break;
+ case 0x2D: mnem = "cvtsd2si"; break;
+ case 0x51: mnem = "sqrtsd"; break;
+ case 0x58: mnem = "addsd"; break;
+ case 0x59: mnem = "mulsd"; break;
+ case 0x5C: mnem = "subsd"; break;
+ case 0x5E: mnem = "divsd"; break;
+ }
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (b2 == 0x2A) {
+ AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
+ } else if (b2 == 0x2C || b2 == 0x2D) {
+ AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
+ data += PrintRightXMMOperand(data);
+ } else if (b2 == 0xC2) {
+ // Intel manual 2A, Table 3-18.
+ const char* const pseudo_op[] = {
+ "cmpeqsd",
+ "cmpltsd",
+ "cmplesd",
+ "cmpunordsd",
+ "cmpneqsd",
+ "cmpnltsd",
+ "cmpnlesd",
+ "cmpordsd"
+ };
+ AppendToBuffer("%s %s,%s",
+ pseudo_op[data[1]],
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data += 2;
+ } else {
+ AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
+ data += PrintRightXMMOperand(data);
+ }
+ }
+ } else {
+ UnimplementedInstruction();
+ }
+ break;
+
+ case 0xF3:
+ if (*(data+1) == 0x0F) {
+ byte b2 = *(data+2);
+ if (b2 == 0x11) {
+ AppendToBuffer("movss ");
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ data += PrintRightXMMOperand(data);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else if (b2 == 0x10) {
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movss %s,", NameOfXMMRegister(regop));
+ data += PrintRightXMMOperand(data);
+ } else if (b2 == 0x2C) {
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("cvttss2si %s,", NameOfCPURegister(regop));
+ data += PrintRightXMMOperand(data);
+ } else if (b2 == 0x5A) {
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
+ data += PrintRightXMMOperand(data);
+ } else if (b2 == 0x6F) {
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop));
+ data += PrintRightXMMOperand(data);
+ } else if (b2 == 0x7F) {
+ AppendToBuffer("movdqu ");
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ data += PrintRightXMMOperand(data);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (*(data+1) == 0xA5) {
+ data += 2;
+ AppendToBuffer("rep_movs");
+ } else if (*(data+1) == 0xAB) {
+ data += 2;
+ AppendToBuffer("rep_stos");
+ } else {
+ UnimplementedInstruction();
+ }
+ break;
+
+ case 0xF7:
+ data += F7Instruction(data);
+ break;
+
+ default:
+ UnimplementedInstruction();
+ }
+ }
+
+ if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
+ tmp_buffer_[tmp_buffer_pos_] = '\0';
+ }
+
+ int instr_len = data - instr;
+ if (instr_len == 0) {
+ printf("%02x", *data);
+ }
+ ASSERT(instr_len > 0); // Ensure progress.
+
+ int outp = 0;
+ // Instruction bytes.
+ for (byte* bp = instr; bp < data; bp++) {
+ outp += v8::internal::OS::SNPrintF(out_buffer + outp,
+ "%02x",
+ *bp);
+ }
+ for (int i = 6 - instr_len; i >= 0; i--) {
+ outp += v8::internal::OS::SNPrintF(out_buffer + outp,
+ " ");
+ }
+
+ outp += v8::internal::OS::SNPrintF(out_buffer + outp,
+ " %s",
+ tmp_buffer_.start());
+ return instr_len;
+} // NOLINT (function is too long)
+
+
+//------------------------------------------------------------------------------
+
+
+static const char* cpu_regs[8] = {
+ "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
+};
+
+
+static const char* byte_cpu_regs[8] = {
+ "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"
+};
+
+
+static const char* xmm_regs[8] = {
+ "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
+};
+
+
+const char* NameConverter::NameOfAddress(byte* addr) const {
+ v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
+ return tmp_buffer_.start();
+}
+
+
+const char* NameConverter::NameOfConstant(byte* addr) const {
+ return NameOfAddress(addr);
+}
+
+
+const char* NameConverter::NameOfCPURegister(int reg) const {
+ if (0 <= reg && reg < 8) return cpu_regs[reg];
+ return "noreg";
+}
+
+
+const char* NameConverter::NameOfByteCPURegister(int reg) const {
+ if (0 <= reg && reg < 8) return byte_cpu_regs[reg];
+ return "noreg";
+}
+
+
+const char* NameConverter::NameOfXMMRegister(int reg) const {
+ if (0 <= reg && reg < 8) return xmm_regs[reg];
+ return "noxmmreg";
+}
+
+
+const char* NameConverter::NameInCode(byte* addr) const {
+ // IA32 does not embed debug strings at the moment.
+ UNREACHABLE();
+ return "";
+}
+
+
+//------------------------------------------------------------------------------
+
+Disassembler::Disassembler(const NameConverter& converter)
+ : converter_(converter) {}
+
+
+Disassembler::~Disassembler() {}
+
+
+int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
+ byte* instruction) {
+ DisassemblerIA32 d(converter_, false /*do not crash if unimplemented*/);
+ return d.InstructionDecode(buffer, instruction);
+}
+
+
+// The IA-32 assembler does not currently use constant pools.
+int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }
+
+
+/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
+ NameConverter converter;
+ Disassembler d(converter);
+ for (byte* pc = begin; pc < end;) {
+ v8::internal::EmbeddedVector<char, 128> buffer;
+ buffer[0] = '\0';
+ byte* prev_pc = pc;
+ pc += d.InstructionDecode(buffer, pc);
+ fprintf(f, "%p", prev_pc);
+ fprintf(f, " ");
+
+ for (byte* bp = prev_pc; bp < pc; bp++) {
+ fprintf(f, "%02x", *bp);
+ }
+ for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
+ fprintf(f, " ");
+ }
+ fprintf(f, " %s\n", buffer.start());
+ }
+}
+
+
+} // namespace disasm
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/frames-ia32.cc b/chromium/v8/src/ia32/frames-ia32.cc
new file mode 100644
index 00000000000..55708117689
--- /dev/null
+++ b/chromium/v8/src/ia32/frames-ia32.cc
@@ -0,0 +1,51 @@
+// 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.
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "assembler.h"
+#include "assembler-ia32.h"
+#include "assembler-ia32-inl.h"
+#include "frames.h"
+
+namespace v8 {
+namespace internal {
+
+
+Register JavaScriptFrame::fp_register() { return ebp; }
+Register JavaScriptFrame::context_register() { return esi; }
+
+
+Register StubFailureTrampolineFrame::fp_register() { return ebp; }
+Register StubFailureTrampolineFrame::context_register() { return esi; }
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/frames-ia32.h b/chromium/v8/src/ia32/frames-ia32.h
new file mode 100644
index 00000000000..86061251018
--- /dev/null
+++ b/chromium/v8/src/ia32/frames-ia32.h
@@ -0,0 +1,146 @@
+// Copyright 2012 the V8 project authors. 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_IA32_FRAMES_IA32_H_
+#define V8_IA32_FRAMES_IA32_H_
+
+namespace v8 {
+namespace internal {
+
+
+// Register lists
+// Note that the bit values must match those used in actual instruction encoding
+const int kNumRegs = 8;
+
+
+// Caller-saved registers
+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
+
+const int kNumJSCallerSaved = 5;
+
+typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
+
+
+// Number of registers for which space is reserved in safepoints.
+const int kNumSafepointRegisters = 8;
+
+const int kNoAlignmentPadding = 0;
+const int kAlignmentPaddingPushed = 2;
+const int kAlignmentZapValue = 0x12345678; // Not heap object tagged.
+
+// ----------------------------------------------------
+
+
+class EntryFrameConstants : public AllStatic {
+ public:
+ static const int kCallerFPOffset = -6 * kPointerSize;
+
+ static const int kFunctionArgOffset = +3 * kPointerSize;
+ static const int kReceiverArgOffset = +4 * kPointerSize;
+ static const int kArgcOffset = +5 * kPointerSize;
+ static const int kArgvOffset = +6 * kPointerSize;
+};
+
+
+class ExitFrameConstants : public AllStatic {
+ public:
+ static const int kCodeOffset = -2 * kPointerSize;
+ static const int kSPOffset = -1 * kPointerSize;
+
+ static const int kCallerFPOffset = 0 * kPointerSize;
+ static const int kCallerPCOffset = +1 * kPointerSize;
+
+ // FP-relative displacement of the caller's SP. It points just
+ // below the saved PC.
+ static const int kCallerSPDisplacement = +2 * kPointerSize;
+};
+
+
+class JavaScriptFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
+ static const int kLastParameterOffset = +2 * kPointerSize;
+ static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
+
+ // Caller SP-relative.
+ static const int kParam0Offset = -2 * kPointerSize;
+ static const int kReceiverOffset = -1 * kPointerSize;
+
+ static const int kDynamicAlignmentStateOffset = kLocal0Offset;
+};
+
+
+class ArgumentsAdaptorFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
+};
+
+
+class ConstructFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kImplicitReceiverOffset = -5 * kPointerSize;
+ static const int kConstructorOffset = kMinInt;
+ static const int kLengthOffset = -4 * kPointerSize;
+ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
+
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + 3 * kPointerSize;
+};
+
+
+class InternalFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
+};
+
+
+inline Object* JavaScriptFrame::function_slot_object() const {
+ const int offset = JavaScriptFrameConstants::kFunctionOffset;
+ return Memory::Object_at(fp() + offset);
+}
+
+
+inline void StackHandler::SetFp(Address slot, Address fp) {
+ Memory::Address_at(slot) = fp;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_IA32_FRAMES_IA32_H_
diff --git a/chromium/v8/src/ia32/full-codegen-ia32.cc b/chromium/v8/src/ia32/full-codegen-ia32.cc
new file mode 100644
index 00000000000..f08a269e85b
--- /dev/null
+++ b/chromium/v8/src/ia32/full-codegen-ia32.cc
@@ -0,0 +1,4903 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "code-stubs.h"
+#include "codegen.h"
+#include "compiler.h"
+#include "debug.h"
+#include "full-codegen.h"
+#include "isolate-inl.h"
+#include "parser.h"
+#include "scopes.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm_)
+
+
+class JumpPatchSite BASE_EMBEDDED {
+ public:
+ explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) {
+#ifdef DEBUG
+ info_emitted_ = false;
+#endif
+ }
+
+ ~JumpPatchSite() {
+ ASSERT(patch_site_.is_bound() == info_emitted_);
+ }
+
+ void EmitJumpIfNotSmi(Register reg,
+ Label* target,
+ Label::Distance distance = Label::kFar) {
+ __ test(reg, Immediate(kSmiTagMask));
+ EmitJump(not_carry, target, distance); // Always taken before patched.
+ }
+
+ void EmitJumpIfSmi(Register reg,
+ Label* target,
+ Label::Distance distance = Label::kFar) {
+ __ test(reg, Immediate(kSmiTagMask));
+ EmitJump(carry, target, distance); // Never taken before patched.
+ }
+
+ void EmitPatchInfo() {
+ if (patch_site_.is_bound()) {
+ int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site_);
+ ASSERT(is_int8(delta_to_patch_site));
+ __ test(eax, Immediate(delta_to_patch_site));
+#ifdef DEBUG
+ info_emitted_ = true;
+#endif
+ } else {
+ __ nop(); // Signals no inlined code.
+ }
+ }
+
+ private:
+ // jc will be patched with jz, jnc will become jnz.
+ void EmitJump(Condition cc, Label* target, Label::Distance distance) {
+ ASSERT(!patch_site_.is_bound() && !info_emitted_);
+ ASSERT(cc == carry || cc == not_carry);
+ __ bind(&patch_site_);
+ __ j(cc, target, distance);
+ }
+
+ MacroAssembler* masm_;
+ Label patch_site_;
+#ifdef DEBUG
+ bool info_emitted_;
+#endif
+};
+
+
+// Generate code for a JS function. On entry to the function the receiver
+// and arguments have been pushed on the stack left to right, with the
+// return address on top of them. The actual argument count matches the
+// formal parameter count expected by the function.
+//
+// The live registers are:
+// o ecx: CallKind
+// 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)
+//
+// The function builds a JS frame. Please see JavaScriptFrameConstants in
+// frames-ia32.h for its layout.
+void FullCodeGenerator::Generate() {
+ CompilationInfo* info = info_;
+ handler_table_ =
+ isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
+ profiling_counter_ = isolate()->factory()->NewCell(
+ Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate()));
+ SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info->function()->name()->IsUtf8EqualTo(CStrVector(FLAG_stop_at))) {
+ __ int3();
+ }
+#endif
+
+ // 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_classic_mode() || info->is_native()) {
+ Label ok;
+ __ 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);
+
+ info->set_prologue_offset(masm_->pc_offset());
+ __ push(ebp); // Caller's frame pointer.
+ __ mov(ebp, esp);
+ __ push(esi); // Callee's context.
+ __ push(edi); // Callee's JS Function.
+ info->AddNoFrameRange(0, masm_->pc_offset());
+
+ { Comment cmnt(masm_, "[ Allocate locals");
+ int locals_count = info->scope()->num_stack_slots();
+ // Generators allocate locals, if any, in context slots.
+ ASSERT(!info->function()->is_generator() || locals_count == 0);
+ if (locals_count == 1) {
+ __ push(Immediate(isolate()->factory()->undefined_value()));
+ } else if (locals_count > 1) {
+ __ mov(eax, Immediate(isolate()->factory()->undefined_value()));
+ for (int i = 0; i < locals_count; i++) {
+ __ push(eax);
+ }
+ }
+ }
+
+ bool function_in_register = true;
+
+ // Possibly allocate a local context.
+ int heap_slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ if (heap_slots > 0) {
+ Comment cmnt(masm_, "[ Allocate context");
+ // Argument to NewContext is the function, which is still in edi.
+ __ push(edi);
+ if (FLAG_harmony_scoping && info->scope()->is_global_scope()) {
+ __ Push(info->scope()->GetScopeInfo());
+ __ CallRuntime(Runtime::kNewGlobalContext, 2);
+ } else if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kNewFunctionContext, 1);
+ }
+ function_in_register = false;
+ // Context is returned in both eax and esi. It replaces the context
+ // passed to us. It's saved in the stack and kept live in esi.
+ __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
+
+ // Copy parameters into context if necessary.
+ int num_parameters = info->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.
+ __ mov(eax, Operand(ebp, parameter_offset));
+ // Store it in the context.
+ int context_offset = Context::SlotOffset(var->index());
+ __ mov(Operand(esi, context_offset), eax);
+ // Update the write barrier. This clobbers eax and ebx.
+ __ RecordWriteContextSlot(esi,
+ context_offset,
+ eax,
+ ebx,
+ kDontSaveFPRegs);
+ }
+ }
+ }
+
+ Variable* arguments = scope()->arguments();
+ if (arguments != NULL) {
+ // Function uses arguments object.
+ Comment cmnt(masm_, "[ Allocate arguments object");
+ if (function_in_register) {
+ __ push(edi);
+ } else {
+ __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ }
+ // Receiver is just before the parameters on the caller's stack.
+ int num_parameters = info->scope()->num_parameters();
+ int offset = num_parameters * kPointerSize;
+ __ lea(edx,
+ Operand(ebp, StandardFrameConstants::kCallerSPOffset + offset));
+ __ push(edx);
+ __ push(Immediate(Smi::FromInt(num_parameters)));
+ // 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_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, eax, ebx, edx);
+ }
+
+ if (FLAG_trace) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+
+ // Visit the declarations and body unless there is an illegal
+ // redeclaration.
+ if (scope()->HasIllegalRedeclaration()) {
+ Comment cmnt(masm_, "[ Declarations");
+ scope()->VisitIllegalRedeclaration(this);
+
+ } else {
+ PrepareForBailoutForId(BailoutId::FunctionEntry(), NO_REGISTERS);
+ { Comment cmnt(masm_, "[ Declarations");
+ // For named function expressions, declare the function name as a
+ // constant.
+ if (scope()->is_function_scope() && scope()->function() != NULL) {
+ VariableDeclaration* function = scope()->function();
+ ASSERT(function->proxy()->var()->mode() == CONST ||
+ function->proxy()->var()->mode() == CONST_HARMONY);
+ ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED);
+ VisitVariableDeclaration(function);
+ }
+ VisitDeclarations(scope()->declarations());
+ }
+
+ { Comment cmnt(masm_, "[ Stack check");
+ PrepareForBailoutForId(BailoutId::Declarations(), NO_REGISTERS);
+ Label ok;
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ cmp(esp, Operand::StaticVariable(stack_limit));
+ __ j(above_equal, &ok, Label::kNear);
+ StackCheckStub stub;
+ __ CallStub(&stub);
+ __ bind(&ok);
+ }
+
+ { Comment cmnt(masm_, "[ Body");
+ ASSERT(loop_depth() == 0);
+ VisitStatements(function()->body());
+ ASSERT(loop_depth() == 0);
+ }
+ }
+
+ // Always emit a 'return undefined' in case control fell off the end of
+ // the body.
+ { Comment cmnt(masm_, "[ return <undefined>;");
+ __ mov(eax, isolate()->factory()->undefined_value());
+ EmitReturnSequence();
+ }
+}
+
+
+void FullCodeGenerator::ClearAccumulator() {
+ __ Set(eax, Immediate(Smi::FromInt(0)));
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) {
+ __ mov(ebx, Immediate(profiling_counter_));
+ __ sub(FieldOperand(ebx, Cell::kValueOffset),
+ Immediate(Smi::FromInt(delta)));
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterReset() {
+ int reset_value = FLAG_interrupt_budget;
+ if (info_->ShouldSelfOptimize() && !FLAG_retry_self_opt) {
+ // Self-optimization is a one-off thing: if it fails, don't try again.
+ reset_value = Smi::kMaxValue;
+ }
+ __ mov(ebx, Immediate(profiling_counter_));
+ __ mov(FieldOperand(ebx, Cell::kValueOffset),
+ Immediate(Smi::FromInt(reset_value)));
+}
+
+
+void FullCodeGenerator::EmitBackEdgeBookkeeping(IterationStatement* stmt,
+ Label* back_edge_target) {
+ Comment cmnt(masm_, "[ Back edge bookkeeping");
+ Label ok;
+
+ int weight = 1;
+ if (FLAG_weighted_back_edges) {
+ ASSERT(back_edge_target->is_bound());
+ int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target);
+ weight = Min(kMaxBackEdgeWeight,
+ Max(1, distance / kCodeSizeMultiplier));
+ }
+ EmitProfilingCounterDecrement(weight);
+ __ j(positive, &ok, Label::kNear);
+ InterruptStub 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.
+ RecordBackEdge(stmt->OsrEntryId());
+
+ EmitProfilingCounterReset();
+
+ __ bind(&ok);
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ // Record a mapping of the OSR id to this PC. This is used if the OSR
+ // entry becomes the target of a bailout. We don't expect it to be, but
+ // we want it to work if it is.
+ PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::EmitReturnSequence() {
+ Comment cmnt(masm_, "[ Return sequence");
+ if (return_label_.is_bound()) {
+ __ jmp(&return_label_);
+ } else {
+ // Common return label
+ __ bind(&return_label_);
+ if (FLAG_trace) {
+ __ push(eax);
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+ if (FLAG_interrupt_at_exit || FLAG_self_optimization) {
+ // Pretend that the exit is a backwards jump to the entry.
+ int weight = 1;
+ if (info_->ShouldSelfOptimize()) {
+ weight = FLAG_interrupt_budget / FLAG_self_opt_count;
+ } else if (FLAG_weighted_back_edges) {
+ int distance = masm_->pc_offset();
+ weight = Min(kMaxBackEdgeWeight,
+ Max(1, distance / kCodeSizeMultiplier));
+ }
+ EmitProfilingCounterDecrement(weight);
+ Label ok;
+ __ j(positive, &ok, Label::kNear);
+ __ push(eax);
+ if (info_->ShouldSelfOptimize() && FLAG_direct_self_opt) {
+ __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ CallRuntime(Runtime::kOptimizeFunctionOnNextCall, 1);
+ } else {
+ InterruptStub stub;
+ __ CallStub(&stub);
+ }
+ __ pop(eax);
+ EmitProfilingCounterReset();
+ __ bind(&ok);
+ }
+#ifdef DEBUG
+ // Add a label for checking the size of the code used for returning.
+ Label check_exit_codesize;
+ masm_->bind(&check_exit_codesize);
+#endif
+ SetSourcePosition(function()->end_position() - 1);
+ __ RecordJSReturn();
+ // Do not use the leave instruction here because it is too short to
+ // patch with the code required by the debugger.
+ __ mov(esp, ebp);
+ int no_frame_start = masm_->pc_offset();
+ __ pop(ebp);
+
+ int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize;
+ __ Ret(arguments_bytes, ecx);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Check that the size of the code used for returning is large enough
+ // for the debugger's requirements.
+ ASSERT(Assembler::kJSReturnSequenceLength <=
+ masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
+#endif
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ codegen()->GetVar(result_register(), var);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ MemOperand operand = codegen()->VarOperand(var, result_register());
+ // Memory operands can be pushed directly.
+ __ push(operand);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Variable* var) const {
+ // For simplicity we always test the accumulator register.
+ codegen()->GetVar(result_register(), var);
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const {
+ UNREACHABLE(); // Not used on IA32.
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Heap::RootListIndex index) const {
+ UNREACHABLE(); // Not used on IA32.
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(
+ Heap::RootListIndex index) const {
+ UNREACHABLE(); // Not used on IA32.
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const {
+ UNREACHABLE(); // Not used on IA32.
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Handle<Object> lit) const {
+ if (lit->IsSmi()) {
+ __ SafeSet(result_register(), Immediate(lit));
+ } else {
+ __ Set(result_register(), Immediate(lit));
+ }
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const {
+ if (lit->IsSmi()) {
+ __ SafePush(Immediate(lit));
+ } else {
+ __ push(Immediate(lit));
+ }
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ ASSERT(!lit->IsUndetectableObject()); // There are no undetectable literals.
+ if (lit->IsUndefined() || lit->IsNull() || lit->IsFalse()) {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ } else if (lit->IsTrue() || lit->IsJSObject()) {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ } else if (lit->IsString()) {
+ if (String::cast(*lit)->length() == 0) {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ }
+ } else if (lit->IsSmi()) {
+ if (Smi::cast(*lit)->value() == 0) {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ }
+ } else {
+ // For simplicity we always test the accumulator register.
+ __ mov(result_register(), lit);
+ codegen()->DoTest(this);
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ __ Drop(count);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::DropAndPlug(
+ int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ __ Drop(count);
+ __ Move(result_register(), reg);
+}
+
+
+void FullCodeGenerator::StackValueContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ if (count > 1) __ Drop(count - 1);
+ __ mov(Operand(esp, 0), reg);
+}
+
+
+void FullCodeGenerator::TestContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ // For simplicity we always test the accumulator register.
+ __ Drop(count);
+ __ Move(result_register(), reg);
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Label* materialize_true,
+ Label* materialize_false) const {
+ ASSERT(materialize_true == materialize_false);
+ __ bind(materialize_true);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Label* materialize_true,
+ Label* materialize_false) const {
+ Label done;
+ __ bind(materialize_true);
+ __ mov(result_register(), isolate()->factory()->true_value());
+ __ jmp(&done, Label::kNear);
+ __ bind(materialize_false);
+ __ mov(result_register(), isolate()->factory()->false_value());
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(
+ Label* materialize_true,
+ Label* materialize_false) const {
+ Label done;
+ __ bind(materialize_true);
+ __ push(Immediate(isolate()->factory()->true_value()));
+ __ jmp(&done, Label::kNear);
+ __ bind(materialize_false);
+ __ push(Immediate(isolate()->factory()->false_value()));
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Label* materialize_true,
+ Label* materialize_false) const {
+ ASSERT(materialize_true == true_label_);
+ ASSERT(materialize_false == false_label_);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(bool flag) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const {
+ Handle<Object> value = flag
+ ? isolate()->factory()->true_value()
+ : isolate()->factory()->false_value();
+ __ mov(result_register(), value);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(bool flag) const {
+ Handle<Object> value = flag
+ ? isolate()->factory()->true_value()
+ : isolate()->factory()->false_value();
+ __ push(Immediate(value));
+}
+
+
+void FullCodeGenerator::TestContext::Plug(bool flag) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ if (flag) {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ } else {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ }
+}
+
+
+void FullCodeGenerator::DoTest(Expression* condition,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ Handle<Code> ic = ToBooleanStub::GetUninitialized(isolate());
+ CallIC(ic, RelocInfo::CODE_TARGET, condition->test_id());
+ __ test(result_register(), result_register());
+ // The stub returns nonzero for true.
+ Split(not_zero, if_true, if_false, fall_through);
+}
+
+
+void FullCodeGenerator::Split(Condition cc,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ if (if_false == fall_through) {
+ __ j(cc, if_true);
+ } else if (if_true == fall_through) {
+ __ j(NegateCondition(cc), if_false);
+ } else {
+ __ j(cc, if_true);
+ __ jmp(if_false);
+ }
+}
+
+
+MemOperand FullCodeGenerator::StackOperand(Variable* var) {
+ ASSERT(var->IsStackAllocated());
+ // Offset is negative because higher indexes are at lower addresses.
+ int offset = -var->index() * kPointerSize;
+ // Adjust by a (parameter or local) base offset.
+ if (var->IsParameter()) {
+ offset += (info_->scope()->num_parameters() + 1) * kPointerSize;
+ } else {
+ offset += JavaScriptFrameConstants::kLocal0Offset;
+ }
+ return Operand(ebp, offset);
+}
+
+
+MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ if (var->IsContextSlot()) {
+ int context_chain_length = scope()->ContextChainLength(var->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return ContextOperand(scratch, var->index());
+ } else {
+ return StackOperand(var);
+ }
+}
+
+
+void FullCodeGenerator::GetVar(Register dest, Variable* var) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ MemOperand location = VarOperand(var, dest);
+ __ mov(dest, location);
+}
+
+
+void FullCodeGenerator::SetVar(Variable* var,
+ Register src,
+ Register scratch0,
+ Register scratch1) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ ASSERT(!scratch0.is(src));
+ ASSERT(!scratch0.is(scratch1));
+ 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));
+ __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs);
+ }
+}
+
+
+void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
+ bool should_normalize,
+ Label* if_true,
+ Label* if_false) {
+ // Only prepare for bailouts before splits if we're in a test
+ // context. Otherwise, we let the Visit function deal with the
+ // preparation to avoid preparing with the same AST id twice.
+ if (!context()->IsTest() || !info_->IsOptimizable()) return;
+
+ Label skip;
+ if (should_normalize) __ jmp(&skip, Label::kNear);
+ PrepareForBailout(expr, TOS_REG);
+ if (should_normalize) {
+ __ cmp(eax, isolate()->factory()->true_value());
+ Split(equal, if_true, if_false, NULL);
+ __ bind(&skip);
+ }
+}
+
+
+void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
+ // The variable in the declaration always resides in the current context.
+ ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
+ if (generate_debug_code_) {
+ // Check that we're not inside a with or catch context.
+ __ mov(ebx, FieldOperand(esi, HeapObject::kMapOffset));
+ __ cmp(ebx, isolate()->factory()->with_context_map());
+ __ Check(not_equal, kDeclarationInWithContext);
+ __ cmp(ebx, isolate()->factory()->catch_context_map());
+ __ Check(not_equal, kDeclarationInCatchContext);
+ }
+}
+
+
+void FullCodeGenerator::VisitVariableDeclaration(
+ VariableDeclaration* declaration) {
+ // 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.
+ VariableProxy* proxy = declaration->proxy();
+ VariableMode mode = declaration->mode();
+ Variable* variable = proxy->var();
+ bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ globals_->Add(variable->name(), zone());
+ globals_->Add(variable->binding_needs_init()
+ ? isolate()->factory()->the_hole_value()
+ : isolate()->factory()->undefined_value(), zone());
+ break;
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ if (hole_init) {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ __ mov(StackOperand(variable),
+ Immediate(isolate()->factory()->the_hole_value()));
+ }
+ break;
+
+ case Variable::CONTEXT:
+ if (hole_init) {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ __ mov(ContextOperand(esi, variable->index()),
+ Immediate(isolate()->factory()->the_hole_value()));
+ // No write barrier since the hole value is in old space.
+ PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
+ }
+ break;
+
+ case Variable::LOOKUP: {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ __ push(esi);
+ __ push(Immediate(variable->name()));
+ // VariableDeclaration nodes are always introduced in one of four modes.
+ ASSERT(IsDeclaredVariableMode(mode));
+ PropertyAttributes attr =
+ IsImmutableVariableMode(mode) ? 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.
+ if (hole_init) {
+ __ push(Immediate(isolate()->factory()->the_hole_value()));
+ } else {
+ __ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
+ }
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitFunctionDeclaration(
+ FunctionDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED: {
+ globals_->Add(variable->name(), zone());
+ Handle<SharedFunctionInfo> function =
+ Compiler::BuildFunctionInfo(declaration->fun(), script());
+ // Check for stack-overflow exception.
+ if (function.is_null()) return SetStackOverflow();
+ globals_->Add(function, zone());
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ VisitForAccumulatorValue(declaration->fun());
+ __ mov(StackOperand(variable), result_register());
+ break;
+ }
+
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ VisitForAccumulatorValue(declaration->fun());
+ __ mov(ContextOperand(esi, variable->index()), result_register());
+ // 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);
+ break;
+ }
+
+ case Variable::LOOKUP: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ __ push(esi);
+ __ push(Immediate(variable->name()));
+ __ push(Immediate(Smi::FromInt(NONE)));
+ VisitForStackValue(declaration->fun());
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
+ Variable* variable = declaration->proxy()->var();
+ ASSERT(variable->location() == Variable::CONTEXT);
+ ASSERT(variable->interface()->IsFrozen());
+
+ Comment cmnt(masm_, "[ ModuleDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+
+ // Load instance object.
+ __ LoadContext(eax, scope_->ContextChainLength(scope_->GlobalScope()));
+ __ mov(eax, ContextOperand(eax, variable->interface()->Index()));
+ __ mov(eax, ContextOperand(eax, Context::EXTENSION_INDEX));
+
+ // Assign it.
+ __ mov(ContextOperand(esi, variable->index()), eax);
+ // We know that we have written a module, which is not a smi.
+ __ RecordWriteContextSlot(esi,
+ Context::SlotOffset(variable->index()),
+ eax,
+ ecx,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS);
+
+ // Traverse into body.
+ Visit(declaration->module());
+}
+
+
+void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ // TODO(rossberg)
+ break;
+
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, "[ ImportDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ // TODO(rossberg)
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ case Variable::LOOKUP:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
+ // TODO(rossberg)
+}
+
+
+void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
+ // Call the runtime to declare the globals.
+ __ push(esi); // The context is the first argument.
+ __ Push(pairs);
+ __ Push(Smi::FromInt(DeclareGlobalsFlags()));
+ __ CallRuntime(Runtime::kDeclareGlobals, 3);
+ // Return value is ignored.
+}
+
+
+void FullCodeGenerator::DeclareModules(Handle<FixedArray> descriptions) {
+ // Call the runtime to declare the modules.
+ __ Push(descriptions);
+ __ CallRuntime(Runtime::kDeclareModules, 1);
+ // Return value is ignored.
+}
+
+
+void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
+ Comment cmnt(masm_, "[ SwitchStatement");
+ Breakable nested_statement(this, stmt);
+ SetStatementPosition(stmt);
+
+ // Keep the switch value on the stack until a case matches.
+ VisitForStackValue(stmt->tag());
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+
+ ZoneList<CaseClause*>* clauses = stmt->cases();
+ CaseClause* default_clause = NULL; // Can occur anywhere in the list.
+
+ Label next_test; // Recycled for each test.
+ // Compile all the tests with branches to their bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ CaseClause* clause = clauses->at(i);
+ clause->body_target()->Unuse();
+
+ // The default is not a test, but remember it as final fall through.
+ if (clause->is_default()) {
+ default_clause = clause;
+ continue;
+ }
+
+ Comment cmnt(masm_, "[ Case comparison");
+ __ bind(&next_test);
+ next_test.Unuse();
+
+ // Compile the label expression.
+ VisitForAccumulatorValue(clause->label());
+
+ // Perform the comparison as if via '==='.
+ __ mov(edx, Operand(esp, 0)); // Switch value.
+ bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ mov(ecx, edx);
+ __ or_(ecx, eax);
+ patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear);
+
+ __ cmp(edx, eax);
+ __ j(not_equal, &next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ __ jmp(clause->body_target());
+ __ bind(&slow_case);
+ }
+
+ // Record position before stub call for type feedback.
+ SetSourcePosition(clause->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), Token::EQ_STRICT);
+ CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
+ patch_site.EmitPatchInfo();
+ __ test(eax, eax);
+ __ j(not_equal, &next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ __ jmp(clause->body_target());
+ }
+
+ // Discard the test value and jump to the default if present, otherwise to
+ // the end of the statement.
+ __ bind(&next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ if (default_clause == NULL) {
+ __ jmp(nested_statement.break_label());
+ } else {
+ __ jmp(default_clause->body_target());
+ }
+
+ // Compile all the case bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ Comment cmnt(masm_, "[ Case body");
+ CaseClause* clause = clauses->at(i);
+ __ bind(clause->body_target());
+ PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
+ VisitStatements(clause->statements());
+ }
+
+ __ bind(nested_statement.break_label());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
+ Comment cmnt(masm_, "[ ForInStatement");
+ SetStatementPosition(stmt);
+
+ Label loop, exit;
+ ForIn loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // Get the object to enumerate over. If the object is null or undefined, skip
+ // over the loop. See ECMA-262 version 5, section 12.6.4.
+ VisitForAccumulatorValue(stmt->enumerable());
+ __ cmp(eax, isolate()->factory()->undefined_value());
+ __ j(equal, &exit);
+ __ cmp(eax, isolate()->factory()->null_value());
+ __ j(equal, &exit);
+
+ PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
+
+ // Convert the object to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(eax, &convert, Label::kNear);
+ __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
+ __ j(above_equal, &done_convert, Label::kNear);
+ __ bind(&convert);
+ __ push(eax);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ bind(&done_convert);
+ __ push(eax);
+
+ // Check for proxies.
+ Label call_runtime, use_cache, fixed_array;
+ 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.
+ __ CheckEnumCache(&call_runtime);
+
+ __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
+ __ jmp(&use_cache, Label::kNear);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(eax);
+ __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
+ __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
+ isolate()->factory()->meta_map());
+ __ j(not_equal, &fixed_array);
+
+
+ // We got a map in register eax. Get the enumeration cache from it.
+ Label no_descriptors;
+ __ bind(&use_cache);
+
+ __ EnumLength(edx, eax);
+ __ cmp(edx, Immediate(Smi::FromInt(0)));
+ __ j(equal, &no_descriptors);
+
+ __ LoadInstanceDescriptors(eax, ecx);
+ __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheOffset));
+ __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset));
+
+ // Set up the four remaining stack slots.
+ __ push(eax); // Map.
+ __ push(ecx); // Enumeration cache.
+ __ push(edx); // Number of valid entries for the map in the enum cache.
+ __ push(Immediate(Smi::FromInt(0))); // Initial index.
+ __ jmp(&loop);
+
+ __ bind(&no_descriptors);
+ __ add(esp, Immediate(kPointerSize));
+ __ jmp(&exit);
+
+ // We got a fixed array in register eax. Iterate through that.
+ Label non_proxy;
+ __ bind(&fixed_array);
+
+ Handle<Cell> cell = isolate()->factory()->NewCell(
+ Handle<Object>(Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker),
+ isolate()));
+ RecordTypeFeedbackCell(stmt->ForInFeedbackId(), cell);
+ __ LoadHeapObject(ebx, cell);
+ __ mov(FieldOperand(ebx, Cell::kValueOffset),
+ Immediate(Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker)));
+
+ __ 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.
+
+ // Generate code for doing the condition check.
+ PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
+ __ bind(&loop);
+ __ mov(eax, Operand(esp, 0 * kPointerSize)); // Get the current index.
+ __ cmp(eax, Operand(esp, 1 * kPointerSize)); // Compare to the array length.
+ __ j(above_equal, loop_statement.break_label());
+
+ // Get the current entry of the array into register ebx.
+ __ mov(ebx, Operand(esp, 2 * kPointerSize));
+ __ mov(ebx, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize));
+
+ // 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 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, eax);
+ __ j(equal, loop_statement.continue_label());
+ __ mov(ebx, eax);
+
+ // Update the 'each' property or variable from the possibly filtered
+ // entry in register ebx.
+ __ bind(&update_each);
+ __ mov(result_register(), ebx);
+ // Perform the assignment as if via '='.
+ { EffectContext context(this);
+ EmitAssignment(stmt->each());
+ }
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Generate code for going to the next element by incrementing the
+ // index (smi) stored on top of the stack.
+ __ bind(loop_statement.continue_label());
+ __ add(Operand(esp, 0 * kPointerSize), Immediate(Smi::FromInt(1)));
+
+ EmitBackEdgeBookkeeping(stmt, &loop);
+ __ jmp(&loop);
+
+ // Remove the pointers stored on the stack.
+ __ bind(loop_statement.break_label());
+ __ add(esp, Immediate(5 * kPointerSize));
+
+ // Exit and decrement the loop depth.
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(&exit);
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
+ Comment cmnt(masm_, "[ ForOfStatement");
+ SetStatementPosition(stmt);
+
+ Iteration loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // var iterator = iterable[@@iterator]()
+ VisitForAccumulatorValue(stmt->assign_iterator());
+
+ // As with for-in, skip the loop if the iterator is null or undefined.
+ __ CompareRoot(eax, Heap::kUndefinedValueRootIndex);
+ __ j(equal, loop_statement.break_label());
+ __ CompareRoot(eax, Heap::kNullValueRootIndex);
+ __ j(equal, loop_statement.break_label());
+
+ // Convert the iterator to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(eax, &convert);
+ __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx);
+ __ j(above_equal, &done_convert);
+ __ bind(&convert);
+ __ push(eax);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ bind(&done_convert);
+
+ // Loop entry.
+ __ bind(loop_statement.continue_label());
+
+ // result = iterator.next()
+ VisitForEffect(stmt->next_result());
+
+ // if (result.done) break;
+ Label result_not_done;
+ VisitForControl(stmt->result_done(),
+ loop_statement.break_label(),
+ &result_not_done,
+ &result_not_done);
+ __ bind(&result_not_done);
+
+ // each = result.value
+ VisitForEffect(stmt->assign_each());
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Check stack before looping.
+ PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS);
+ EmitBackEdgeBookkeeping(stmt, loop_statement.continue_label());
+ __ jmp(loop_statement.continue_label());
+
+ // Exit and decrement the loop depth.
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(loop_statement.break_label());
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
+ bool pretenure) {
+ // Use the fast case closure allocation code that allocates in new
+ // space for nested functions that don't need literals cloning. If
+ // we're running with the --always-opt or the --prepare-always-opt
+ // flag, we need to use the runtime function so that the new function
+ // we are creating here gets a chance to have its code optimized and
+ // doesn't just get a copy of the existing unoptimized code.
+ if (!FLAG_always_opt &&
+ !FLAG_prepare_always_opt &&
+ !pretenure &&
+ scope()->is_function_scope() &&
+ info->num_literals() == 0) {
+ FastNewClosureStub stub(info->language_mode(), info->is_generator());
+ __ push(Immediate(info));
+ __ CallStub(&stub);
+ } else {
+ __ push(esi);
+ __ push(Immediate(info));
+ __ push(Immediate(pretenure
+ ? isolate()->factory()->true_value()
+ : isolate()->factory()->false_value()));
+ __ CallRuntime(Runtime::kNewClosure, 3);
+ }
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
+ Comment cmnt(masm_, "[ VariableProxy");
+ EmitVariableLoad(expr);
+}
+
+
+void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
+ TypeofState typeof_state,
+ Label* slow) {
+ Register context = esi;
+ Register temp = edx;
+
+ Scope* s = scope();
+ while (s != NULL) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_non_strict_eval()) {
+ // Check that extension is NULL.
+ __ cmp(ContextOperand(context, Context::EXTENSION_INDEX),
+ Immediate(0));
+ __ j(not_equal, slow);
+ }
+ // Load next context in chain.
+ __ mov(temp, ContextOperand(context, Context::PREVIOUS_INDEX));
+ // Walk the rest of the chain without clobbering esi.
+ context = temp;
+ }
+ // 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_non_strict_eval() || s->is_eval_scope()) break;
+ s = s->outer_scope();
+ }
+
+ if (s != NULL && s->is_eval_scope()) {
+ // Loop up the context chain. There is no frame effect so it is
+ // safe to use raw labels here.
+ Label next, fast;
+ if (!context.is(temp)) {
+ __ mov(temp, context);
+ }
+ __ bind(&next);
+ // Terminate at native context.
+ __ cmp(FieldOperand(temp, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->native_context_map()));
+ __ j(equal, &fast, Label::kNear);
+ // Check that extension is NULL.
+ __ cmp(ContextOperand(temp, Context::EXTENSION_INDEX), Immediate(0));
+ __ j(not_equal, slow);
+ // Load next context in chain.
+ __ mov(temp, ContextOperand(temp, Context::PREVIOUS_INDEX));
+ __ jmp(&next);
+ __ bind(&fast);
+ }
+
+ // All extension objects were empty and it is safe to use a global
+ // load IC call.
+ __ mov(edx, GlobalObjectOperand());
+ __ mov(ecx, var->name());
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+ ? RelocInfo::CODE_TARGET
+ : RelocInfo::CODE_TARGET_CONTEXT;
+ CallIC(ic, mode);
+}
+
+
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var,
+ Label* slow) {
+ ASSERT(var->IsContextSlot());
+ Register context = esi;
+ Register temp = ebx;
+
+ for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_non_strict_eval()) {
+ // Check that extension is NULL.
+ __ cmp(ContextOperand(context, Context::EXTENSION_INDEX),
+ Immediate(0));
+ __ j(not_equal, slow);
+ }
+ __ mov(temp, ContextOperand(context, Context::PREVIOUS_INDEX));
+ // Walk the rest of the chain without clobbering esi.
+ context = temp;
+ }
+ }
+ // Check that last extension is NULL.
+ __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
+ __ j(not_equal, slow);
+
+ // This function is used only for loads, not stores, so it's safe to
+ // return an esi-based operand (the write barrier cannot be allowed to
+ // destroy the esi register).
+ return ContextOperand(context, var->index());
+}
+
+
+void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var,
+ TypeofState typeof_state,
+ Label* slow,
+ Label* done) {
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // 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() == DYNAMIC_GLOBAL) {
+ EmitLoadGlobalCheckExtensions(var, typeof_state, slow);
+ __ jmp(done);
+ } else if (var->mode() == DYNAMIC_LOCAL) {
+ Variable* local = var->local_if_not_shadowed();
+ __ mov(eax, ContextSlotOperandCheckExtensions(local, slow));
+ if (local->mode() == LET ||
+ local->mode() == CONST ||
+ local->mode() == CONST_HARMONY) {
+ __ cmp(eax, isolate()->factory()->the_hole_value());
+ __ j(not_equal, done);
+ if (local->mode() == CONST) {
+ __ mov(eax, isolate()->factory()->undefined_value());
+ } else { // LET || CONST_HARMONY
+ __ push(Immediate(var->name()));
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ }
+ }
+ __ jmp(done);
+ }
+}
+
+
+void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
+ // Record position before possible IC call.
+ SetSourcePosition(proxy->position());
+ Variable* var = proxy->var();
+
+ // Three cases: global variables, lookup variables, and all other types of
+ // variables.
+ switch (var->location()) {
+ case Variable::UNALLOCATED: {
+ Comment cmnt(masm_, "Global variable");
+ // Use inline caching. Variable name is passed in ecx and the global
+ // object in eax.
+ __ mov(edx, GlobalObjectOperand());
+ __ mov(ecx, var->name());
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ context()->Plug(eax);
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, var->IsContextSlot()
+ ? "Context variable"
+ : "Stack variable");
+ 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;
+ }
+ }
+ context()->Plug(var);
+ break;
+ }
+
+ case Variable::LOOKUP: {
+ Label done, slow;
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done);
+ __ bind(&slow);
+ Comment cmnt(masm_, "Lookup variable");
+ __ push(esi); // Context.
+ __ push(Immediate(var->name()));
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ bind(&done);
+ context()->Plug(eax);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
+ Comment cmnt(masm_, "[ RegExpLiteral");
+ Label materialized;
+ // Registers will be used as follows:
+ // edi = JS function.
+ // ecx = literals array.
+ // ebx = regexp literal.
+ // eax = regexp literal clone.
+ __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ mov(ecx, FieldOperand(edi, JSFunction::kLiteralsOffset));
+ int literal_offset =
+ FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
+ __ mov(ebx, FieldOperand(ecx, literal_offset));
+ __ cmp(ebx, isolate()->factory()->undefined_value());
+ __ j(not_equal, &materialized, Label::kNear);
+
+ // Create regexp literal using runtime function
+ // Result will be in eax.
+ __ push(ecx);
+ __ push(Immediate(Smi::FromInt(expr->literal_index())));
+ __ push(Immediate(expr->pattern()));
+ __ push(Immediate(expr->flags()));
+ __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
+ __ mov(ebx, eax);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+ __ Allocate(size, eax, ecx, edx, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ push(ebx);
+ __ push(Immediate(Smi::FromInt(size)));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ pop(ebx);
+
+ __ 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) {
+ __ mov(edx, FieldOperand(ebx, i));
+ __ mov(ecx, FieldOperand(ebx, i + kPointerSize));
+ __ mov(FieldOperand(eax, i), edx);
+ __ mov(FieldOperand(eax, i + kPointerSize), ecx);
+ }
+ if ((size % (2 * kPointerSize)) != 0) {
+ __ mov(edx, FieldOperand(ebx, size - kPointerSize));
+ __ mov(FieldOperand(eax, size - kPointerSize), edx);
+ }
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitAccessor(Expression* expression) {
+ if (expression == NULL) {
+ __ push(Immediate(isolate()->factory()->null_value()));
+ } else {
+ VisitForStackValue(expression);
+ }
+}
+
+
+void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
+ Comment cmnt(masm_, "[ ObjectLiteral");
+ Handle<FixedArray> constant_properties = expr->constant_properties();
+ int flags = expr->fast_elements()
+ ? ObjectLiteral::kFastElements
+ : ObjectLiteral::kNoFlags;
+ flags |= expr->has_function()
+ ? ObjectLiteral::kHasFunction
+ : ObjectLiteral::kNoFlags;
+ int properties_count = constant_properties->length() / 2;
+ if ((FLAG_track_double_fields && expr->may_store_doubles()) ||
+ expr->depth() > 1) {
+ __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(edi, JSFunction::kLiteralsOffset));
+ __ push(Immediate(Smi::FromInt(expr->literal_index())));
+ __ push(Immediate(constant_properties));
+ __ push(Immediate(Smi::FromInt(flags)));
+ __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
+ } else if (Serializer::enabled() || flags != ObjectLiteral::kFastElements ||
+ properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) {
+ __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(edi, JSFunction::kLiteralsOffset));
+ __ push(Immediate(Smi::FromInt(expr->literal_index())));
+ __ push(Immediate(constant_properties));
+ __ push(Immediate(Smi::FromInt(flags)));
+ __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
+ } else {
+ __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ mov(eax, FieldOperand(edi, JSFunction::kLiteralsOffset));
+ __ mov(ebx, Immediate(Smi::FromInt(expr->literal_index())));
+ __ mov(ecx, Immediate(constant_properties));
+ __ mov(edx, Immediate(Smi::FromInt(flags)));
+ FastCloneShallowObjectStub stub(properties_count);
+ __ CallStub(&stub);
+ }
+
+ // If result_saved is true the result is on top of the stack. If
+ // result_saved is false the result is in eax.
+ bool result_saved = false;
+
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ expr->CalculateEmitStore(zone());
+
+ AccessorTable accessor_table(zone());
+ for (int i = 0; i < expr->properties()->length(); i++) {
+ ObjectLiteral::Property* property = expr->properties()->at(i);
+ if (property->IsCompileTimeValue()) continue;
+
+ Literal* key = property->key();
+ Expression* value = property->value();
+ if (!result_saved) {
+ __ push(eax); // Save result on the stack
+ result_saved = true;
+ }
+ switch (property->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ UNREACHABLE();
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+ ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
+ // Fall through.
+ case ObjectLiteral::Property::COMPUTED:
+ if (key->value()->IsInternalizedString()) {
+ if (property->emit_store()) {
+ VisitForAccumulatorValue(value);
+ __ mov(ecx, Immediate(key->value()));
+ __ mov(edx, Operand(esp, 0));
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, key->LiteralFeedbackId());
+ PrepareForBailoutForId(key->id(), NO_REGISTERS);
+ } else {
+ VisitForEffect(value);
+ }
+ break;
+ }
+ __ push(Operand(esp, 0)); // Duplicate receiver.
+ VisitForStackValue(key);
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ push(Immediate(Smi::FromInt(NONE))); // PropertyAttributes
+ __ CallRuntime(Runtime::kSetProperty, 4);
+ } else {
+ __ Drop(3);
+ }
+ break;
+ case ObjectLiteral::Property::PROTOTYPE:
+ __ push(Operand(esp, 0)); // Duplicate receiver.
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ CallRuntime(Runtime::kSetPrototype, 2);
+ } else {
+ __ Drop(2);
+ }
+ break;
+ case ObjectLiteral::Property::GETTER:
+ accessor_table.lookup(key)->second->getter = value;
+ break;
+ case ObjectLiteral::Property::SETTER:
+ accessor_table.lookup(key)->second->setter = value;
+ break;
+ }
+ }
+
+ // Emit code to define accessors, using only a single call to the runtime for
+ // each pair of corresponding getters and setters.
+ for (AccessorTable::Iterator it = accessor_table.begin();
+ it != accessor_table.end();
+ ++it) {
+ __ push(Operand(esp, 0)); // Duplicate receiver.
+ VisitForStackValue(it->first);
+ EmitAccessor(it->second->getter);
+ EmitAccessor(it->second->setter);
+ __ push(Immediate(Smi::FromInt(NONE)));
+ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ }
+
+ if (expr->has_function()) {
+ ASSERT(result_saved);
+ __ push(Operand(esp, 0));
+ __ CallRuntime(Runtime::kToFastProperties, 1);
+ }
+
+ if (result_saved) {
+ context()->PlugTOS();
+ } else {
+ context()->Plug(eax);
+ }
+}
+
+
+void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
+ Comment cmnt(masm_, "[ ArrayLiteral");
+
+ 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 =
+ IsFastObjectElementsKind(constant_elements_kind);
+ Handle<FixedArrayBase> constant_elements_values(
+ FixedArrayBase::cast(constant_elements->get(1)));
+
+ 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);
+ __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ mov(eax, FieldOperand(ebx, JSFunction::kLiteralsOffset));
+ __ mov(ebx, Immediate(Smi::FromInt(expr->literal_index())));
+ __ mov(ecx, Immediate(constant_elements));
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS,
+ DONT_TRACK_ALLOCATION_SITE,
+ length);
+ __ CallStub(&stub);
+ } else if (expr->depth() > 1) {
+ __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
+ __ push(Immediate(Smi::FromInt(expr->literal_index())));
+ __ push(Immediate(constant_elements));
+ __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
+ } else if (Serializer::enabled() ||
+ length > FastCloneShallowArrayStub::kMaximumClonedLength) {
+ __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
+ __ push(Immediate(Smi::FromInt(expr->literal_index())));
+ __ push(Immediate(constant_elements));
+ __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
+ } else {
+ ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) ||
+ FLAG_smi_only_arrays);
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
+ AllocationSiteMode allocation_site_mode = FLAG_track_allocation_sites
+ ? TRACK_ALLOCATION_SITE : DONT_TRACK_ALLOCATION_SITE;
+
+ // If the elements are already FAST_*_ELEMENTS, the boilerplate cannot
+ // change, so it's possible to specialize the stub in advance.
+ if (has_constant_fast_elements) {
+ mode = FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
+ }
+
+ __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ __ mov(eax, FieldOperand(ebx, JSFunction::kLiteralsOffset));
+ __ mov(ebx, Immediate(Smi::FromInt(expr->literal_index())));
+ __ mov(ecx, Immediate(constant_elements));
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
+ __ CallStub(&stub);
+ }
+
+ bool result_saved = false; // Is the result saved to the stack?
+
+ // Emit code to evaluate all the non-constant subexpressions and to store
+ // them into the newly cloned array.
+ for (int i = 0; i < length; i++) {
+ Expression* subexpr = subexprs->at(i);
+ // If the subexpression is a literal or a simple materialized literal it
+ // is already set in the cloned array.
+ if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
+
+ if (!result_saved) {
+ __ push(eax); // array literal.
+ __ push(Immediate(Smi::FromInt(expr->literal_index())));
+ result_saved = true;
+ }
+ VisitForAccumulatorValue(subexpr);
+
+ if (IsFastObjectElementsKind(constant_elements_kind)) {
+ // 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, kPointerSize)); // 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(ecx, Immediate(Smi::FromInt(i)));
+ StoreArrayLiteralElementStub stub;
+ __ CallStub(&stub);
+ }
+
+ PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
+ }
+
+ if (result_saved) {
+ __ add(esp, Immediate(kPointerSize)); // literal index
+ context()->PlugTOS();
+ } else {
+ context()->Plug(eax);
+ }
+}
+
+
+void FullCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // on the left-hand side.
+ if (!expr->target()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->target());
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* property = expr->target()->AsProperty();
+ if (property != NULL) {
+ assign_type = (property->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ // Evaluate LHS expression.
+ switch (assign_type) {
+ case VARIABLE:
+ // Nothing to do here.
+ break;
+ case NAMED_PROPERTY:
+ if (expr->is_compound()) {
+ // We need the receiver both on the stack and in edx.
+ VisitForStackValue(property->obj());
+ __ mov(edx, Operand(esp, 0));
+ } else {
+ VisitForStackValue(property->obj());
+ }
+ break;
+ case KEYED_PROPERTY: {
+ if (expr->is_compound()) {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ __ mov(edx, Operand(esp, kPointerSize)); // Object.
+ __ mov(ecx, Operand(esp, 0)); // Key.
+ } else {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ }
+ break;
+ }
+ }
+
+ // For compound assignments we need another deoptimization point after the
+ // variable/property load.
+ if (expr->is_compound()) {
+ AccumulatorValueContext result_context(this);
+ { AccumulatorValueContext left_operand_context(this);
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableLoad(expr->target()->AsVariableProxy());
+ PrepareForBailout(expr->target(), TOS_REG);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
+ }
+ }
+
+ Token::Value op = expr->binary_op();
+ __ push(eax); // Left operand goes on the stack.
+ VisitForAccumulatorValue(expr->value());
+
+ OverwriteMode mode = expr->value()->ResultOverwriteAllowed()
+ ? OVERWRITE_RIGHT
+ : NO_OVERWRITE;
+ SetSourcePosition(expr->position() + 1);
+ if (ShouldInlineSmiCase(op)) {
+ EmitInlineSmiBinaryOp(expr->binary_operation(),
+ op,
+ mode,
+ expr->target(),
+ expr->value());
+ } else {
+ EmitBinaryOp(expr->binary_operation(), op, mode);
+ }
+
+ // Deoptimization point in case the binary operation may have side effects.
+ PrepareForBailout(expr->binary_operation(), TOS_REG);
+ } else {
+ VisitForAccumulatorValue(expr->value());
+ }
+
+ // Record source position before possible IC call.
+ SetSourcePosition(expr->position());
+
+ // Store the value.
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ expr->op());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(eax);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyAssignment(expr);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyAssignment(expr);
+ break;
+ }
+}
+
+
+void FullCodeGenerator::VisitYield(Yield* expr) {
+ Comment cmnt(masm_, "[ Yield");
+ // Evaluate yielded value first; the initial iterator definition depends on
+ // this. It stays on the stack while we update the iterator.
+ VisitForStackValue(expr->expression());
+
+ switch (expr->yield_kind()) {
+ case Yield::SUSPEND:
+ // Pop value from top-of-stack slot; box result into result register.
+ EmitCreateIteratorResult(false);
+ __ push(result_register());
+ // Fall through.
+ case Yield::INITIAL: {
+ Label suspend, continuation, post_runtime, resume;
+
+ __ jmp(&suspend);
+
+ __ bind(&continuation);
+ __ jmp(&resume);
+
+ __ bind(&suspend);
+ VisitForAccumulatorValue(expr->generator_object());
+ ASSERT(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
+ __ mov(FieldOperand(eax, JSGeneratorObject::kContinuationOffset),
+ Immediate(Smi::FromInt(continuation.pos())));
+ __ mov(FieldOperand(eax, JSGeneratorObject::kContextOffset), esi);
+ __ mov(ecx, esi);
+ __ RecordWriteField(eax, JSGeneratorObject::kContextOffset, ecx, edx,
+ kDontSaveFPRegs);
+ __ lea(ebx, Operand(ebp, StandardFrameConstants::kExpressionsOffset));
+ __ cmp(esp, ebx);
+ __ j(equal, &post_runtime);
+ __ push(eax); // generator object
+ __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+ __ mov(context_register(),
+ Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ bind(&post_runtime);
+ __ pop(result_register());
+ EmitReturnSequence();
+
+ __ bind(&resume);
+ context()->Plug(result_register());
+ break;
+ }
+
+ case Yield::FINAL: {
+ VisitForAccumulatorValue(expr->generator_object());
+ __ mov(FieldOperand(result_register(),
+ JSGeneratorObject::kContinuationOffset),
+ Immediate(Smi::FromInt(JSGeneratorObject::kGeneratorClosed)));
+ // Pop value from top-of-stack slot, box result into result register.
+ EmitCreateIteratorResult(true);
+ EmitUnwindBeforeReturn();
+ EmitReturnSequence();
+ break;
+ }
+
+ case Yield::DELEGATING: {
+ VisitForStackValue(expr->generator_object());
+
+ // Initial stack layout is as follows:
+ // [sp + 1 * kPointerSize] iter
+ // [sp + 0 * kPointerSize] g
+
+ Label l_catch, l_try, l_suspend, l_continuation, l_resume;
+ Label l_next, l_call, l_loop;
+ // Initial send value is undefined.
+ __ mov(eax, isolate()->factory()->undefined_value());
+ __ jmp(&l_next);
+
+ // catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; }
+ __ bind(&l_catch);
+ handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
+ __ mov(ecx, isolate()->factory()->throw_string()); // "throw"
+ __ push(ecx); // "throw"
+ __ push(Operand(esp, 2 * kPointerSize)); // iter
+ __ push(eax); // exception
+ __ jmp(&l_call);
+
+ // try { received = %yield result }
+ // Shuffle the received result above a try handler and yield it without
+ // re-boxing.
+ __ bind(&l_try);
+ __ pop(eax); // result
+ __ PushTryHandler(StackHandler::CATCH, expr->index());
+ const int handler_size = StackHandlerConstants::kSize;
+ __ push(eax); // result
+ __ jmp(&l_suspend);
+ __ bind(&l_continuation);
+ __ jmp(&l_resume);
+ __ bind(&l_suspend);
+ const int generator_object_depth = kPointerSize + handler_size;
+ __ mov(eax, Operand(esp, generator_object_depth));
+ __ push(eax); // g
+ ASSERT(l_continuation.pos() > 0 && Smi::IsValid(l_continuation.pos()));
+ __ mov(FieldOperand(eax, JSGeneratorObject::kContinuationOffset),
+ Immediate(Smi::FromInt(l_continuation.pos())));
+ __ mov(FieldOperand(eax, JSGeneratorObject::kContextOffset), esi);
+ __ mov(ecx, esi);
+ __ RecordWriteField(eax, JSGeneratorObject::kContextOffset, ecx, edx,
+ kDontSaveFPRegs);
+ __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+ __ mov(context_register(),
+ Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ pop(eax); // result
+ EmitReturnSequence();
+ __ bind(&l_resume); // received in eax
+ __ PopTryHandler();
+
+ // receiver = iter; f = iter.next; arg = received;
+ __ bind(&l_next);
+ __ mov(ecx, isolate()->factory()->next_string()); // "next"
+ __ push(ecx);
+ __ push(Operand(esp, 2 * kPointerSize)); // iter
+ __ push(eax); // received
+
+ // result = receiver[f](arg);
+ __ bind(&l_call);
+ Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(1);
+ CallIC(ic);
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ Drop(1); // The key is still on the stack; drop it.
+
+ // if (!result.done) goto l_try;
+ __ bind(&l_loop);
+ __ push(eax); // save result
+ __ mov(edx, eax); // result
+ __ mov(ecx, isolate()->factory()->done_string()); // "done"
+ Handle<Code> done_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(done_ic); // result.done in eax
+ Handle<Code> bool_ic = ToBooleanStub::GetUninitialized(isolate());
+ CallIC(bool_ic);
+ __ test(eax, eax);
+ __ j(zero, &l_try);
+
+ // result.value
+ __ pop(edx); // result
+ __ mov(ecx, isolate()->factory()->value_string()); // "value"
+ Handle<Code> value_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(value_ic); // result.value in eax
+ context()->DropAndPlug(2, eax); // drop iter and g
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode) {
+ // The value stays in eax, and is ultimately read by the resumed generator, as
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. ebx
+ // will hold the generator object until the activation has been resumed.
+ VisitForStackValue(generator);
+ VisitForAccumulatorValue(value);
+ __ pop(ebx);
+
+ // Check generator state.
+ Label wrong_state, done;
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ __ cmp(FieldOperand(ebx, JSGeneratorObject::kContinuationOffset),
+ Immediate(Smi::FromInt(0)));
+ __ j(less_equal, &wrong_state);
+
+ // Load suspended function and context.
+ __ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset));
+ __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset));
+
+ // Push receiver.
+ __ push(FieldOperand(ebx, JSGeneratorObject::kReceiverOffset));
+
+ // Push holes for arguments to generator function.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(edx,
+ FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ mov(ecx, isolate()->factory()->the_hole_value());
+ Label push_argument_holes, push_frame;
+ __ bind(&push_argument_holes);
+ __ sub(edx, Immediate(Smi::FromInt(1)));
+ __ j(carry, &push_frame);
+ __ push(ecx);
+ __ jmp(&push_argument_holes);
+
+ // Enter a new JavaScript frame, and initialize its slots as they were when
+ // the generator was suspended.
+ Label resume_frame;
+ __ bind(&push_frame);
+ __ call(&resume_frame);
+ __ jmp(&done);
+ __ bind(&resume_frame);
+ __ push(ebp); // Caller's frame pointer.
+ __ mov(ebp, esp);
+ __ push(esi); // Callee's context.
+ __ push(edi); // Callee's JS Function.
+
+ // Load the operand stack size.
+ __ mov(edx, FieldOperand(ebx, JSGeneratorObject::kOperandStackOffset));
+ __ mov(edx, FieldOperand(edx, FixedArray::kLengthOffset));
+ __ SmiUntag(edx);
+
+ // If we are sending a value and there is no operand stack, we can jump back
+ // in directly.
+ if (resume_mode == JSGeneratorObject::NEXT) {
+ Label slow_resume;
+ __ cmp(edx, Immediate(0));
+ __ j(not_zero, &slow_resume);
+ __ mov(edx, FieldOperand(edi, JSFunction::kCodeEntryOffset));
+ __ mov(ecx, FieldOperand(ebx, JSGeneratorObject::kContinuationOffset));
+ __ SmiUntag(ecx);
+ __ add(edx, ecx);
+ __ mov(FieldOperand(ebx, JSGeneratorObject::kContinuationOffset),
+ Immediate(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting)));
+ __ jmp(edx);
+ __ bind(&slow_resume);
+ }
+
+ // Otherwise, we push holes for the operand stack and call the runtime to fix
+ // up the stack and the handlers.
+ Label push_operand_holes, call_resume;
+ __ bind(&push_operand_holes);
+ __ sub(edx, Immediate(1));
+ __ j(carry, &call_resume);
+ __ push(ecx);
+ __ jmp(&push_operand_holes);
+ __ bind(&call_resume);
+ __ push(ebx);
+ __ push(result_register());
+ __ Push(Smi::FromInt(resume_mode));
+ __ CallRuntime(Runtime::kResumeJSGeneratorObject, 3);
+ // Not reached: the runtime call returns elsewhere.
+ __ Abort(kGeneratorFailedToResume);
+
+ // Throw error if we attempt to operate on a running generator.
+ __ bind(&wrong_state);
+ __ push(ebx);
+ __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
+
+ __ bind(&done);
+ context()->Plug(result_register());
+}
+
+
+void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
+ Label gc_required;
+ Label allocated;
+
+ Handle<Map> map(isolate()->native_context()->generator_result_map());
+
+ __ Allocate(map->instance_size(), eax, ecx, edx, &gc_required, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&gc_required);
+ __ Push(Smi::FromInt(map->instance_size()));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ mov(context_register(),
+ Operand(ebp, StandardFrameConstants::kContextOffset));
+
+ __ bind(&allocated);
+ __ mov(ebx, map);
+ __ pop(ecx);
+ __ mov(edx, isolate()->factory()->ToBoolean(done));
+ ASSERT_EQ(map->instance_size(), 5 * kPointerSize);
+ __ mov(FieldOperand(eax, HeapObject::kMapOffset), ebx);
+ __ mov(FieldOperand(eax, JSObject::kPropertiesOffset),
+ isolate()->factory()->empty_fixed_array());
+ __ mov(FieldOperand(eax, JSObject::kElementsOffset),
+ isolate()->factory()->empty_fixed_array());
+ __ mov(FieldOperand(eax, JSGeneratorObject::kResultValuePropertyOffset), ecx);
+ __ mov(FieldOperand(eax, JSGeneratorObject::kResultDonePropertyOffset), edx);
+
+ // Only the value field needs a write barrier, as the other values are in the
+ // root set.
+ __ RecordWriteField(eax, JSGeneratorObject::kResultValuePropertyOffset,
+ ecx, edx, kDontSaveFPRegs);
+}
+
+
+void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ Literal* key = prop->key()->AsLiteral();
+ ASSERT(!key->value()->IsSmi());
+ __ mov(ecx, Immediate(key->value()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
+}
+
+
+void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
+}
+
+
+void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode,
+ Expression* left,
+ Expression* right) {
+ // Do combined smi check of the operands. Left operand is on the
+ // stack. Right operand is in eax.
+ Label smi_case, done, stub_call;
+ __ pop(edx);
+ __ mov(ecx, eax);
+ __ or_(eax, edx);
+ JumpPatchSite patch_site(masm_);
+ patch_site.EmitJumpIfSmi(eax, &smi_case, Label::kNear);
+
+ __ bind(&stub_call);
+ __ mov(eax, ecx);
+ BinaryOpStub stub(op, mode);
+ CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET,
+ expr->BinaryOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ __ jmp(&done, Label::kNear);
+
+ // Smi case.
+ __ bind(&smi_case);
+ __ mov(eax, edx); // Copy left operand in case of a stub call.
+
+ switch (op) {
+ case Token::SAR:
+ __ SmiUntag(eax);
+ __ SmiUntag(ecx);
+ __ sar_cl(eax); // No checks of result necessary
+ __ SmiTag(eax);
+ break;
+ case Token::SHL: {
+ Label result_ok;
+ __ SmiUntag(eax);
+ __ SmiUntag(ecx);
+ __ shl_cl(eax);
+ // Check that the *signed* result fits in a smi.
+ __ cmp(eax, 0xc0000000);
+ __ j(positive, &result_ok);
+ __ SmiTag(ecx);
+ __ jmp(&stub_call);
+ __ bind(&result_ok);
+ __ SmiTag(eax);
+ break;
+ }
+ case Token::SHR: {
+ Label result_ok;
+ __ SmiUntag(eax);
+ __ SmiUntag(ecx);
+ __ shr_cl(eax);
+ __ test(eax, Immediate(0xc0000000));
+ __ j(zero, &result_ok);
+ __ SmiTag(ecx);
+ __ jmp(&stub_call);
+ __ bind(&result_ok);
+ __ SmiTag(eax);
+ break;
+ }
+ case Token::ADD:
+ __ add(eax, ecx);
+ __ j(overflow, &stub_call);
+ break;
+ case Token::SUB:
+ __ sub(eax, ecx);
+ __ j(overflow, &stub_call);
+ break;
+ case Token::MUL: {
+ __ SmiUntag(eax);
+ __ imul(eax, ecx);
+ __ j(overflow, &stub_call);
+ __ test(eax, eax);
+ __ j(not_zero, &done, Label::kNear);
+ __ mov(ebx, edx);
+ __ or_(ebx, ecx);
+ __ j(negative, &stub_call);
+ 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;
+ default:
+ UNREACHABLE();
+ }
+
+ __ bind(&done);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode) {
+ __ pop(edx);
+ BinaryOpStub stub(op, mode);
+ JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code.
+ CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET,
+ expr->BinaryOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitAssignment(Expression* expr) {
+ // Invalid left-hand sides are rewritten by the parser to have a 'throw
+ // ReferenceError' on the left-hand side.
+ if (!expr->IsValidLeftHandSide()) {
+ VisitForEffect(expr);
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->AsProperty();
+ if (prop != NULL) {
+ assign_type = (prop->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ switch (assign_type) {
+ case VARIABLE: {
+ Variable* var = expr->AsVariableProxy()->var();
+ EffectContext context(this);
+ EmitVariableAssignment(var, Token::ASSIGN);
+ break;
+ }
+ case NAMED_PROPERTY: {
+ __ push(eax); // Preserve value.
+ VisitForAccumulatorValue(prop->obj());
+ __ mov(edx, eax);
+ __ pop(eax); // Restore value.
+ __ mov(ecx, prop->key()->AsLiteral()->value());
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic);
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ push(eax); // Preserve value.
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ __ mov(ecx, eax);
+ __ pop(edx); // Receiver.
+ __ pop(eax); // Restore value.
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic);
+ break;
+ }
+ }
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitVariableAssignment(Variable* var,
+ Token::Value op) {
+ if (var->IsUnallocated()) {
+ // Global var, const, or let.
+ __ mov(ecx, var->name());
+ __ mov(edx, GlobalObjectOperand());
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+
+ } else if (op == Token::INIT_CONST) {
+ // Const initializers need a write barrier.
+ ASSERT(!var->IsParameter()); // No const parameters.
+ if (var->IsStackLocal()) {
+ Label skip;
+ __ mov(edx, StackOperand(var));
+ __ cmp(edx, isolate()->factory()->the_hole_value());
+ __ j(not_equal, &skip);
+ __ mov(StackOperand(var), eax);
+ __ bind(&skip);
+ } else {
+ ASSERT(var->IsContextSlot() || var->IsLookupSlot());
+ // Like var declarations, const declarations are hoisted to function
+ // scope. However, unlike var initializers, const initializers are
+ // able to drill a hole to that function context, even from inside a
+ // 'with' context. We thus bypass the normal static scope lookup for
+ // var->IsContextSlot().
+ __ push(eax);
+ __ push(esi);
+ __ push(Immediate(var->name()));
+ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
+ }
+
+ } 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(language_mode())));
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ } else {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ Label assign;
+ MemOperand location = VarOperand(var, ecx);
+ __ mov(edx, location);
+ __ cmp(edx, isolate()->factory()->the_hole_value());
+ __ j(not_equal, &assign, Label::kNear);
+ __ push(Immediate(var->name()));
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ __ bind(&assign);
+ __ mov(location, eax);
+ if (var->IsContextSlot()) {
+ __ mov(edx, eax);
+ int offset = Context::SlotOffset(var->index());
+ __ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs);
+ }
+ }
+
+ } 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 (generate_debug_code_ && op == Token::INIT_LET) {
+ // Check for an uninitialized let binding.
+ __ mov(edx, location);
+ __ cmp(edx, isolate()->factory()->the_hole_value());
+ __ Check(equal, kLetBindingReInitialization);
+ }
+ // Perform the assignment.
+ __ mov(location, eax);
+ if (var->IsContextSlot()) {
+ __ mov(edx, eax);
+ 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(language_mode())));
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ }
+ }
+ // Non-initializing assignments to consts are ignored.
+}
+
+
+void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
+ // Assignment to a property, using a named store IC.
+ // eax : value
+ // esp[0] : receiver
+
+ Property* prop = expr->target()->AsProperty();
+ ASSERT(prop != NULL);
+ ASSERT(prop->key()->AsLiteral() != NULL);
+
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ __ mov(ecx, prop->key()->AsLiteral()->value());
+ __ pop(edx);
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
+
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
+ // Assignment to a property, using a keyed store IC.
+ // eax : value
+ // esp[0] : key
+ // esp[kPointerSize] : receiver
+
+ __ pop(ecx); // Key.
+ __ pop(edx);
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
+
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::VisitProperty(Property* expr) {
+ Comment cmnt(masm_, "[ Property");
+ Expression* key = expr->key();
+
+ if (key->IsPropertyName()) {
+ VisitForAccumulatorValue(expr->obj());
+ __ mov(edx, result_register());
+ EmitNamedPropertyLoad(expr);
+ PrepareForBailoutForId(expr->LoadId(), TOS_REG);
+ context()->Plug(eax);
+ } else {
+ VisitForStackValue(expr->obj());
+ VisitForAccumulatorValue(expr->key());
+ __ pop(edx); // Object.
+ __ mov(ecx, result_register()); // Key.
+ EmitKeyedPropertyLoad(expr);
+ context()->Plug(eax);
+ }
+}
+
+
+void FullCodeGenerator::CallIC(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id) {
+ ic_total_count_++;
+ __ call(code, rmode, ast_id);
+}
+
+
+
+
+void FullCodeGenerator::EmitCallWithIC(Call* expr,
+ Handle<Object> name,
+ RelocInfo::Mode mode) {
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ __ Set(ecx, Immediate(name));
+ }
+ // Record source position of the IC call.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ CallIC(ic, mode, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
+ Expression* key) {
+ // Load the key.
+ VisitForAccumulatorValue(key);
+
+ // Swap the name of the function and the receiver on the stack to follow
+ // the calling convention for call ICs.
+ __ pop(ecx);
+ __ push(eax);
+ __ push(ecx);
+
+ // Load the arguments.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position of the IC call.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
+ __ mov(ecx, Operand(esp, (arg_count + 1) * kPointerSize)); // Key.
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, eax); // Drop the key still on the stack.
+}
+
+
+void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
+ // Code common for calls using the call stub.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+
+ // Record call targets in unoptimized code.
+ flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<Cell> cell = isolate()->factory()->NewCell(uninitialized);
+ RecordTypeFeedbackCell(expr->CallFeedbackId(), cell);
+ __ mov(ebx, cell);
+
+ CallFunctionStub stub(arg_count, flags);
+ __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
+ __ CallStub(&stub, expr->CallFeedbackId());
+
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, eax);
+}
+
+
+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));
+ } else {
+ __ push(Immediate(isolate()->factory()->undefined_value()));
+ }
+
+ // 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 start position of the scope the calls resides in.
+ __ push(Immediate(Smi::FromInt(scope()->start_position())));
+
+ // Do the runtime call.
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
+}
+
+
+void FullCodeGenerator::VisitCall(Call* expr) {
+#ifdef DEBUG
+ // We want to verify that RecordJSReturnSite gets called on all paths
+ // through this function. Avoid early returns.
+ expr->return_is_recorded_ = false;
+#endif
+
+ Comment cmnt(masm_, "[ Call");
+ Expression* callee = expr->expression();
+ VariableProxy* proxy = callee->AsVariableProxy();
+ Property* property = callee->AsProperty();
+
+ if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
+ // In a call to eval, we first call %ResolvePossiblyDirectEval to
+ // resolve the function we need to call and the receiver of the call.
+ // Then we call the resolved function using the given arguments.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope pos_scope(masm()->positions_recorder());
+ VisitForStackValue(callee);
+ // Reserved receiver slot.
+ __ push(Immediate(isolate()->factory()->undefined_value()));
+ // Push the arguments.
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Push a copy of the function (found below the arguments) and
+ // resolve eval.
+ __ push(Operand(esp, (arg_count + 1) * kPointerSize));
+ 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.
+ __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
+ __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
+ }
+ // 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));
+ context()->DropAndPlug(1, eax);
+
+ } else if (proxy != NULL && proxy->var()->IsUnallocated()) {
+ // Push global object as receiver for the call IC.
+ __ push(GlobalObjectOperand());
+ EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
+
+ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
+ // Call to a lookup slot (dynamically introduced variable).
+ Label slow, done;
+ { PreservePositionScope scope(masm()->positions_recorder());
+ // Generate code for loading from variables potentially shadowed by
+ // eval-introduced variables.
+ EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done);
+ }
+ __ bind(&slow);
+ // Call the runtime to find the function to call (returned in eax) and
+ // the object holding it (returned in edx).
+ __ push(context_register());
+ __ push(Immediate(proxy->name()));
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ push(eax); // Function.
+ __ push(edx); // Receiver.
+
+ // If fast case code has been generated, emit code to push the function
+ // and receiver and have the slow path jump around this code.
+ if (done.is_linked()) {
+ Label call;
+ __ jmp(&call, Label::kNear);
+ __ bind(&done);
+ // Push function.
+ __ push(eax);
+ // The receiver is implicitly the global receiver. Indicate this by
+ // passing the hole to the call function stub.
+ __ push(Immediate(isolate()->factory()->the_hole_value()));
+ __ bind(&call);
+ }
+
+ // The receiver is either the global receiver or an object found by
+ // LoadContextSlot. That object could be the hole if the receiver is
+ // implicitly the global object.
+ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
+
+ } else if (property != NULL) {
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(property->obj());
+ }
+ if (property->key()->IsPropertyName()) {
+ EmitCallWithIC(expr,
+ property->key()->AsLiteral()->value(),
+ RelocInfo::CODE_TARGET);
+ } else {
+ EmitKeyedCallWithIC(expr, property->key());
+ }
+
+ } else {
+ // Call to an arbitrary expression not handled specially above.
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(callee);
+ }
+ // Load global receiver object.
+ __ mov(ebx, GlobalObjectOperand());
+ __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
+ // Emit function call.
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ }
+
+#ifdef DEBUG
+ // RecordJSReturnSite should have been called.
+ ASSERT(expr->return_is_recorded_);
+#endif
+}
+
+
+void FullCodeGenerator::VisitCallNew(CallNew* expr) {
+ Comment cmnt(masm_, "[ CallNew");
+ // According to ECMA-262, section 11.2.2, page 44, the function
+ // expression in new calls must be evaluated before the
+ // arguments.
+
+ // Push constructor on the stack. If it's not a function it's used as
+ // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
+ // ignored.
+ VisitForStackValue(expr->expression());
+
+ // Push the arguments ("left-to-right") on the stack.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Call the construct call builtin that handles allocation and
+ // constructor invocation.
+ SetSourcePosition(expr->position());
+
+ // Load function and argument count into edi and eax.
+ __ Set(eax, Immediate(arg_count));
+ __ mov(edi, Operand(esp, arg_count * kPointerSize));
+
+ // Record call targets in unoptimized code.
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<Cell> cell = isolate()->factory()->NewCell(uninitialized);
+ RecordTypeFeedbackCell(expr->CallNewFeedbackId(), cell);
+ __ mov(ebx, cell);
+
+ CallConstructStub stub(RECORD_CALL_TARGET);
+ __ call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL);
+ PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ test(eax, Immediate(kSmiTagMask));
+ Split(zero, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ test(eax, Immediate(kSmiTagMask | 0x80000000));
+ Split(zero, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsObject(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(eax, if_false);
+ __ cmp(eax, isolate()->factory()->null_value());
+ __ j(equal, if_true);
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ // Undetectable objects behave like undefined when tested with typeof.
+ __ movzx_b(ecx, FieldOperand(ebx, Map::kBitFieldOffset));
+ __ test(ecx, Immediate(1 << Map::kIsUndetectable));
+ __ j(not_zero, if_false);
+ __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceTypeOffset));
+ __ cmp(ecx, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ j(below, if_false);
+ __ cmp(ecx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ 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(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(eax, if_false);
+ __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ebx);
+ 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(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(eax, if_false);
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset));
+ __ test(ebx, Immediate(1 << Map::kIsUndetectable));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(not_zero, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
+ CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ AssertNotSmi(eax);
+
+ // Check whether this map has already been checked to be safe for default
+ // valueOf.
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ test_b(FieldOperand(ebx, Map::kBitField2Offset),
+ 1 << Map::kStringWrapperSafeForDefaultValueOf);
+ __ j(not_zero, if_true);
+
+ // Check for fast case object. Return false for slow case objects.
+ __ mov(ecx, FieldOperand(eax, JSObject::kPropertiesOffset));
+ __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
+ __ cmp(ecx, isolate()->factory()->hash_table_map());
+ __ j(equal, if_false);
+
+ // Look for valueOf string in the descriptor array, and indicate false if
+ // found. Since we omit an enumeration index check, if it is added via a
+ // transition that shares its descriptor array, this is a false positive.
+ Label entry, loop, done;
+
+ // Skip loop if no descriptors are valid.
+ __ NumberOfOwnDescriptors(ecx, ebx);
+ __ cmp(ecx, 0);
+ __ j(equal, &done);
+
+ __ LoadInstanceDescriptors(ebx, ebx);
+ // ebx: descriptor array.
+ // ecx: valid entries in the descriptor array.
+ // Calculate the end of the descriptor array.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kPointerSize == 4);
+ __ imul(ecx, ecx, DescriptorArray::kDescriptorSize);
+ __ lea(ecx, Operand(ebx, ecx, times_2, DescriptorArray::kFirstOffset));
+ // Calculate location of the first key name.
+ __ add(ebx, Immediate(DescriptorArray::kFirstOffset));
+ // Loop through all the keys in the descriptor array. If one of these is the
+ // internalized string "valueOf" the result is false.
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ mov(edx, FieldOperand(ebx, 0));
+ __ cmp(edx, isolate()->factory()->value_of_string());
+ __ j(equal, if_false);
+ __ add(ebx, Immediate(DescriptorArray::kDescriptorSize * kPointerSize));
+ __ bind(&entry);
+ __ cmp(ebx, ecx);
+ __ j(not_equal, &loop);
+
+ __ bind(&done);
+
+ // Reload map as register ebx was used as temporary above.
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+
+ // If a valueOf property is not found on the object check that its
+ // prototype is the un-modified String prototype. If not result is false.
+ __ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
+ __ JumpIfSmi(ecx, if_false);
+ __ mov(ecx, FieldOperand(ecx, HeapObject::kMapOffset));
+ __ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ mov(edx,
+ FieldOperand(edx, GlobalObject::kNativeContextOffset));
+ __ cmp(ecx,
+ ContextOperand(edx,
+ Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
+ __ j(not_equal, if_false);
+ // Set the bit in the map to indicate that it has been checked safe for
+ // default valueOf and set true result.
+ __ or_(FieldOperand(ebx, Map::kBitField2Offset),
+ Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ jmp(if_true);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(eax, if_false);
+ __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(eax, if_false);
+ __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(eax, if_false);
+ __ CmpObjectType(eax, JS_REGEXP_TYPE, ebx);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+
+void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+
+ 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);
+
+ // Get the frame pointer for the calling frame.
+ __ mov(eax, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+
+ // Skip the arguments adaptor frame if it exists.
+ Label check_frame_marker;
+ __ cmp(Operand(eax, StandardFrameConstants::kContextOffset),
+ Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ j(not_equal, &check_frame_marker);
+ __ mov(eax, Operand(eax, StandardFrameConstants::kCallerFPOffset));
+
+ // Check the marker in the calling frame.
+ __ bind(&check_frame_marker);
+ __ cmp(Operand(eax, StandardFrameConstants::kMarkerOffset),
+ Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ // Load the two objects into registers and perform the comparison.
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ 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);
+
+ __ pop(ebx);
+ __ 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(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ // ArgumentsAccessStub expects the key in edx and the formal
+ // parameter count in eax.
+ VisitForAccumulatorValue(args->at(0));
+ __ mov(edx, eax);
+ __ Set(eax, Immediate(Smi::FromInt(info_->scope()->num_parameters())));
+ ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
+ __ CallStub(&stub);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+
+ Label exit;
+ // Get the number of formal parameters.
+ __ Set(eax, Immediate(Smi::FromInt(info_->scope()->num_parameters())));
+
+ // Check if the calling frame is an arguments adaptor frame.
+ __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+ __ cmp(Operand(ebx, StandardFrameConstants::kContextOffset),
+ Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ j(not_equal, &exit);
+
+ // Arguments adaptor case: Read the arguments length from the
+ // adaptor frame.
+ __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ __ bind(&exit);
+ __ AssertSmi(eax);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitClassOf(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ Label done, null, function, non_function_constructor;
+
+ VisitForAccumulatorValue(args->at(0));
+
+ // If the object is a smi, we return null.
+ __ JumpIfSmi(eax, &null);
+
+ // 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);
+ 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);
+
+ // eax now contains the constructor function. Grab the
+ // instance class name from there.
+ __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset));
+ __ jmp(&done);
+
+ // Functions have class 'Function'.
+ __ bind(&function);
+ __ mov(eax, isolate()->factory()->function_class_string());
+ __ jmp(&done);
+
+ // Objects with a non-function constructor have class 'Object'.
+ __ bind(&non_function_constructor);
+ __ mov(eax, isolate()->factory()->Object_string());
+ __ jmp(&done);
+
+ // Non-JS objects have class null.
+ __ bind(&null);
+ __ mov(eax, isolate()->factory()->null_value());
+
+ // All done.
+ __ bind(&done);
+
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitLog(CallRuntime* expr) {
+ // Conditionally generate a log call.
+ // Args:
+ // 0 (literal string): The type of logging (corresponds to the flags).
+ // This is used to determine whether or not to generate the log call.
+ // 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);
+ }
+ // Finally, we're expected to leave a value on the top of the stack.
+ __ mov(eax, isolate()->factory()->undefined_value());
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+
+ Label slow_allocate_heapnumber;
+ Label heapnumber_allocated;
+
+ __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ // Allocate a heap number.
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ mov(edi, eax);
+
+ __ bind(&heapnumber_allocated);
+
+ __ PrepareCallCFunction(1, ebx);
+ __ mov(eax, ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX));
+ __ mov(eax, FieldOperand(eax, GlobalObject::kNativeContextOffset));
+ __ 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)).
+ // This is implemented on both SSE2 and FPU.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope fscope(masm(), SSE2);
+ __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
+ __ movd(xmm1, ebx);
+ __ movd(xmm0, eax);
+ __ cvtss2sd(xmm1, xmm1);
+ __ xorps(xmm0, xmm1);
+ __ subsd(xmm0, xmm1);
+ __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
+ } else {
+ // 0x4130000000000000 is 1.0 x 2^20 as a double.
+ __ mov(FieldOperand(edi, HeapNumber::kExponentOffset),
+ Immediate(0x41300000));
+ __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax);
+ __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
+ __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0));
+ __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
+ __ fsubp(1);
+ __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset));
+ }
+ __ mov(eax, edi);
+ context()->Plug(eax);
+}
+
+
+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);
+ context()->Plug(eax);
+}
+
+
+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);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitValueOf(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label done;
+ // If the object is a smi return the object.
+ __ JumpIfSmi(eax, &done, Label::kNear);
+ // If the object is not a value type, return the object.
+ __ CmpObjectType(eax, JS_VALUE_TYPE, ebx);
+ __ j(not_equal, &done, Label::kNear);
+ __ mov(eax, FieldOperand(eax, JSValue::kValueOffset));
+
+ __ bind(&done);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ ASSERT_NE(NULL, args->at(1)->AsLiteral());
+ Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->value()));
+
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label runtime, done, not_date_object;
+ Register object = eax;
+ Register result = eax;
+ Register scratch = ecx;
+
+ __ JumpIfSmi(object, &not_date_object);
+ __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+ __ j(not_equal, &not_date_object);
+
+ if (index->value() == 0) {
+ __ mov(result, FieldOperand(object, JSDate::kValueOffset));
+ __ jmp(&done);
+ } else {
+ if (index->value() < JSDate::kFirstUncachedField) {
+ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+ __ mov(scratch, Operand::StaticVariable(stamp));
+ __ cmp(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
+ __ j(not_equal, &runtime, Label::kNear);
+ __ mov(result, FieldOperand(object, JSDate::kValueOffset +
+ kPointerSize * index->value()));
+ __ jmp(&done);
+ }
+ __ bind(&runtime);
+ __ PrepareCallCFunction(2, scratch);
+ __ mov(Operand(esp, 0), object);
+ __ mov(Operand(esp, 1 * kPointerSize), Immediate(index));
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ jmp(&done);
+ }
+
+ __ bind(&not_date_object);
+ __ CallRuntime(Runtime::kThrowNotDateError, 0);
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+void FullCodeGenerator::EmitSeqStringSetCharCheck(Register string,
+ Register index,
+ Register value,
+ uint32_t encoding_mask) {
+ __ test(index, Immediate(kSmiTagMask));
+ __ Check(zero, kNonSmiIndex);
+ __ test(value, Immediate(kSmiTagMask));
+ __ Check(zero, kNonSmiValue);
+
+ __ cmp(index, FieldOperand(string, String::kLengthOffset));
+ __ Check(less, kIndexIsTooLarge);
+
+ __ cmp(index, Immediate(Smi::FromInt(0)));
+ __ Check(greater_equal, kIndexIsNegative);
+
+ __ push(value);
+ __ mov(value, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzx_b(value, FieldOperand(value, Map::kInstanceTypeOffset));
+
+ __ and_(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
+ __ cmp(value, Immediate(encoding_mask));
+ __ Check(equal, kUnexpectedStringType);
+ __ pop(value);
+}
+
+
+void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(3, args->length());
+
+ Register string = eax;
+ Register index = ebx;
+ Register value = ecx;
+
+ VisitForStackValue(args->at(1)); // index
+ VisitForStackValue(args->at(2)); // value
+ __ pop(value);
+ __ pop(index);
+ VisitForAccumulatorValue(args->at(0)); // string
+
+
+ if (FLAG_debug_code) {
+ static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
+ EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
+ }
+
+ __ SmiUntag(value);
+ __ SmiUntag(index);
+ __ mov_b(FieldOperand(string, index, times_1, SeqOneByteString::kHeaderSize),
+ value);
+ context()->Plug(string);
+}
+
+
+void FullCodeGenerator::EmitTwoByteSeqStringSetChar(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(3, args->length());
+
+ Register string = eax;
+ Register index = ebx;
+ Register value = ecx;
+
+ VisitForStackValue(args->at(1)); // index
+ VisitForStackValue(args->at(2)); // value
+ __ pop(value);
+ __ pop(index);
+ VisitForAccumulatorValue(args->at(0)); // string
+
+ if (FLAG_debug_code) {
+ static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
+ EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
+ }
+
+ __ SmiUntag(value);
+ // No need to untag a smi for two-byte addressing.
+ __ mov_w(FieldOperand(string, index, times_1, SeqTwoByteString::kHeaderSize),
+ value);
+ context()->Plug(string);
+}
+
+
+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::ON_STACK);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kMath_pow, 2);
+ }
+ context()->Plug(eax);
+}
+
+
+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.
+
+ Label done;
+ // If the object is a smi, return the value.
+ __ JumpIfSmi(ebx, &done, Label::kNear);
+
+ // If the object is not a value type, return the value.
+ __ CmpObjectType(ebx, JS_VALUE_TYPE, ecx);
+ __ j(not_equal, &done, Label::kNear);
+
+ // 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);
+ __ RecordWriteField(ebx, JSValue::kValueOffset, edx, ecx, kDontSaveFPRegs);
+
+ __ bind(&done);
+ context()->Plug(eax);
+}
+
+
+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));
+
+ NumberToStringStub stub;
+ __ CallStub(&stub);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label done;
+ StringCharFromCodeGenerator generator(eax, ebx);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(ebx);
+}
+
+
+void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ Register object = ebx;
+ Register index = eax;
+ Register result = edx;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharCodeAtGenerator generator(object,
+ index,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ Set(result, Immediate(isolate()->factory()->nan_value()));
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Move the undefined value into the result register, which will
+ // trigger conversion.
+ __ Set(result, Immediate(isolate()->factory()->undefined_value()));
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ Register object = ebx;
+ Register index = eax;
+ Register scratch = edx;
+ Register result = eax;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharAtGenerator generator(object,
+ index,
+ scratch,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // the empty string.
+ __ Set(result, Immediate(isolate()->factory()->empty_string()));
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ Set(result, Immediate(Smi::FromInt(0)));
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(STRING_ADD_CHECK_BOTH);
+ __ CallStub(&stub);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringCompareStub stub;
+ __ CallStub(&stub);
+ context()->Plug(eax);
+}
+
+
+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);
+ context()->Plug(eax);
+}
+
+
+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);
+ context()->Plug(eax);
+}
+
+
+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);
+ context()->Plug(eax);
+}
+
+
+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);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() >= 2);
+
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; ++i) {
+ VisitForStackValue(args->at(i));
+ }
+ VisitForAccumulatorValue(args->last()); // Function.
+
+ Label runtime, done;
+ // Check for non-function argument (including proxy).
+ __ JumpIfSmi(eax, &runtime);
+ __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
+ __ j(not_equal, &runtime);
+
+ // 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));
+ __ jmp(&done);
+
+ __ bind(&runtime);
+ __ push(eax);
+ __ CallRuntime(Runtime::kCall, args->length());
+ __ bind(&done);
+
+ context()->Plug(eax);
+}
+
+
+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);
+ context()->Plug(eax);
+}
+
+
+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()->value()))->value();
+
+ Handle<FixedArray> jsfunction_result_caches(
+ isolate()->native_context()->jsfunction_result_caches());
+ if (jsfunction_result_caches->length() <= cache_id) {
+ __ Abort(kAttemptToUseUndefinedCache);
+ __ mov(eax, isolate()->factory()->undefined_value());
+ context()->Plug(eax);
+ return;
+ }
+
+ VisitForAccumulatorValue(args->at(1));
+
+ Register key = eax;
+ Register cache = ebx;
+ Register tmp = ecx;
+ __ mov(cache, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
+ __ mov(cache,
+ FieldOperand(cache, GlobalObject::kNativeContextOffset));
+ __ mov(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+ __ mov(cache,
+ FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
+
+ Label done, not_found;
+ // tmp now holds finger offset as a smi.
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
+ __ cmp(key, CodeGenerator::FixedArrayElementOperand(cache, tmp));
+ __ j(not_equal, &not_found);
+
+ __ mov(eax, CodeGenerator::FixedArrayElementOperand(cache, tmp, 1));
+ __ jmp(&done);
+
+ __ bind(&not_found);
+ // Call runtime to perform the lookup.
+ __ push(cache);
+ __ push(key);
+ __ CallRuntime(Runtime::kGetFromCache, 2);
+
+ __ bind(&done);
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ Register right = eax;
+ Register left = ebx;
+ Register tmp = ecx;
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+ __ pop(left);
+
+ Label done, fail, ok;
+ __ cmp(left, right);
+ __ j(equal, &ok);
+ // Fail if either is a non-HeapObject.
+ __ mov(tmp, left);
+ __ and_(tmp, right);
+ __ JumpIfSmi(tmp, &fail);
+ __ mov(tmp, FieldOperand(left, HeapObject::kMapOffset));
+ __ CmpInstanceType(tmp, JS_REGEXP_TYPE);
+ __ j(not_equal, &fail);
+ __ cmp(tmp, FieldOperand(right, HeapObject::kMapOffset));
+ __ j(not_equal, &fail);
+ __ mov(tmp, FieldOperand(left, JSRegExp::kDataOffset));
+ __ cmp(tmp, FieldOperand(right, JSRegExp::kDataOffset));
+ __ j(equal, &ok);
+ __ bind(&fail);
+ __ mov(eax, Immediate(isolate()->factory()->false_value()));
+ __ jmp(&done);
+ __ bind(&ok);
+ __ mov(eax, Immediate(isolate()->factory()->true_value()));
+ __ bind(&done);
+
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ __ AssertString(eax);
+
+ 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);
+
+ __ test(FieldOperand(eax, String::kHashFieldOffset),
+ Immediate(String::kContainsCachedArrayIndexMask));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(zero, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ VisitForAccumulatorValue(args->at(0));
+
+ __ AssertString(eax);
+
+ __ mov(eax, FieldOperand(eax, String::kHashFieldOffset));
+ __ IndexFromHash(eax, eax);
+
+ context()->Plug(eax);
+}
+
+
+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));
+ // Load this to eax (= array)
+ VisitForAccumulatorValue(args->at(0));
+ // All aliases of the same register have disjoint lifetimes.
+ Register array = eax;
+ Register elements = no_reg; // Will be eax.
+
+ Register index = edx;
+
+ Register string_length = ecx;
+
+ Register string = esi;
+
+ Register scratch = ebx;
+
+ Register array_length = edi;
+ Register result_pos = no_reg; // Will be edi.
+
+ // Separator operand is already pushed.
+ Operand separator_operand = Operand(esp, 2 * kPointerSize);
+ Operand result_operand = Operand(esp, 1 * kPointerSize);
+ Operand array_length_operand = Operand(esp, 0);
+ __ sub(esp, Immediate(2 * kPointerSize));
+ __ cld();
+ // Check that the array is a JSArray
+ __ JumpIfSmi(array, &bailout);
+ __ CmpObjectType(array, JS_ARRAY_TYPE, scratch);
+ __ j(not_equal, &bailout);
+
+ // Check that the array has fast elements.
+ __ CheckFastElements(scratch, &bailout);
+
+ // If the array has length zero, return the empty string.
+ __ mov(array_length, FieldOperand(array, JSArray::kLengthOffset));
+ __ SmiUntag(array_length);
+ __ j(not_zero, &non_trivial_array);
+ __ mov(result_operand, isolate()->factory()->empty_string());
+ __ jmp(&done);
+
+ // Save the array length.
+ __ bind(&non_trivial_array);
+ __ mov(array_length_operand, array_length);
+
+ // Save the FixedArray containing array's elements.
+ // End of array's live range.
+ elements = array;
+ __ mov(elements, FieldOperand(array, JSArray::kElementsOffset));
+ array = no_reg;
+
+
+ // Check that all array elements are sequential ASCII strings, and
+ // accumulate the sum of their lengths, as a smi-encoded value.
+ __ Set(index, Immediate(0));
+ __ Set(string_length, Immediate(0));
+ // Loop condition: while (index < length).
+ // Live loop registers: index, array_length, string,
+ // scratch, string_length, elements.
+ if (generate_debug_code_) {
+ __ cmp(index, array_length);
+ __ Assert(less, kNoEmptyArraysHereInEmitFastAsciiArrayJoin);
+ }
+ __ bind(&loop);
+ __ mov(string, FieldOperand(elements,
+ index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ JumpIfSmi(string, &bailout);
+ __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ __ and_(scratch, Immediate(
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
+ __ cmp(scratch, kStringTag | kOneByteStringTag | kSeqStringTag);
+ __ j(not_equal, &bailout);
+ __ add(string_length,
+ FieldOperand(string, SeqOneByteString::kLengthOffset));
+ __ j(overflow, &bailout);
+ __ add(index, Immediate(1));
+ __ cmp(index, array_length);
+ __ j(less, &loop);
+
+ // If array_length is 1, return elements[0], a string.
+ __ cmp(array_length, 1);
+ __ j(not_equal, &not_size_one_array);
+ __ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize));
+ __ mov(result_operand, scratch);
+ __ jmp(&done);
+
+ __ bind(&not_size_one_array);
+
+ // End of array_length live range.
+ result_pos = array_length;
+ array_length = no_reg;
+
+ // Live registers:
+ // string_length: Sum of string lengths, as a smi.
+ // elements: FixedArray of strings.
+
+ // Check that the separator is a flat ASCII string.
+ __ mov(string, separator_operand);
+ __ JumpIfSmi(string, &bailout);
+ __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ __ and_(scratch, Immediate(
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
+ __ cmp(scratch, kStringTag | kOneByteStringTag | kSeqStringTag);
+ __ j(not_equal, &bailout);
+
+ // Add (separator length times array_length) - separator length
+ // to string_length.
+ __ mov(scratch, separator_operand);
+ __ mov(scratch, FieldOperand(scratch, SeqOneByteString::kLengthOffset));
+ __ sub(string_length, scratch); // May be negative, temporarily.
+ __ imul(scratch, array_length_operand);
+ __ j(overflow, &bailout);
+ __ add(string_length, scratch);
+ __ j(overflow, &bailout);
+
+ __ shr(string_length, 1);
+ // Live registers and stack values:
+ // string_length
+ // elements
+ __ AllocateAsciiString(result_pos, string_length, scratch,
+ index, string, &bailout);
+ __ mov(result_operand, result_pos);
+ __ lea(result_pos, FieldOperand(result_pos, SeqOneByteString::kHeaderSize));
+
+
+ __ mov(string, separator_operand);
+ __ cmp(FieldOperand(string, SeqOneByteString::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ __ j(equal, &one_char_separator);
+ __ j(greater, &long_separator);
+
+
+ // Empty separator case
+ __ mov(index, Immediate(0));
+ __ jmp(&loop_1_condition);
+ // Loop condition: while (index < length).
+ __ bind(&loop_1);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+ // elements: the FixedArray of strings we are joining.
+
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+ __ add(index, Immediate(1));
+ __ bind(&loop_1_condition);
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_1); // End while (index < length).
+ __ jmp(&done);
+
+
+
+ // One-character separator case
+ __ bind(&one_char_separator);
+ // Replace separator with its ASCII character value.
+ __ mov_b(scratch, FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ mov_b(separator_operand, scratch);
+
+ __ Set(index, Immediate(0));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&loop_2_entry);
+ // Loop condition: while (index < length).
+ __ bind(&loop_2);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+
+ // Copy the separator character to the result.
+ __ mov_b(scratch, separator_operand);
+ __ mov_b(Operand(result_pos, 0), scratch);
+ __ inc(result_pos);
+
+ __ bind(&loop_2_entry);
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+ __ add(index, Immediate(1));
+
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_2); // End while (index < length).
+ __ jmp(&done);
+
+
+ // Long separator case (separator is more than one character).
+ __ bind(&long_separator);
+
+ __ Set(index, Immediate(0));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&loop_3_entry);
+ // Loop condition: while (index < length).
+ __ bind(&loop_3);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+
+ // Copy the separator to the result.
+ __ mov(string, separator_operand);
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+
+ __ bind(&loop_3_entry);
+ // Get string = array[index].
+ __ mov(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ mov(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ shr(string_length, 1);
+ __ lea(string,
+ FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ CopyBytes(string, result_pos, string_length, scratch);
+ __ add(index, Immediate(1));
+
+ __ cmp(index, array_length_operand);
+ __ j(less, &loop_3); // End while (index < length).
+ __ jmp(&done);
+
+
+ __ bind(&bailout);
+ __ mov(result_operand, isolate()->factory()->undefined_value());
+ __ bind(&done);
+ __ mov(eax, result_operand);
+ // Drop temp values from the stack, and restore context register.
+ __ add(esp, Immediate(3 * kPointerSize));
+
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
+ Handle<String> name = expr->name();
+ if (name->length() > 0 && name->Get(0) == '_') {
+ Comment cmnt(masm_, "[ InlineRuntimeCall");
+ EmitInlineRuntimeCall(expr);
+ return;
+ }
+
+ Comment cmnt(masm_, "[ CallRuntime");
+ ZoneList<Expression*>* args = expr->arguments();
+
+ if (expr->is_jsruntime()) {
+ // Prepare for calling JS runtime function.
+ __ mov(eax, GlobalObjectOperand());
+ __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset));
+ }
+
+ // Push the arguments ("left-to-right").
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ if (expr->is_jsruntime()) {
+ // Call the JS runtime function via a call IC.
+ __ Set(ecx, Immediate(expr->name()));
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ CallIC(ic, mode, expr->CallRuntimeFeedbackId());
+ // Restore context register.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ } else {
+ // Call the C runtime function.
+ __ CallRuntime(expr->function(), arg_count);
+ }
+ context()->Plug(eax);
+}
+
+
+void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
+ switch (expr->op()) {
+ case Token::DELETE: {
+ Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
+ Property* property = expr->expression()->AsProperty();
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+
+ if (property != NULL) {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE)
+ ? kNonStrictMode : kStrictMode;
+ __ push(Immediate(Smi::FromInt(strict_mode_flag)));
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
+ 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(language_mode() == CLASSIC_MODE || var->is_this());
+ if (var->IsUnallocated()) {
+ __ push(GlobalObjectOperand());
+ __ push(Immediate(var->name()));
+ __ push(Immediate(Smi::FromInt(kNonStrictMode)));
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
+ context()->Plug(eax);
+ } else if (var->IsStackAllocated() || var->IsContextSlot()) {
+ // Result of deleting non-global variables is false. 'this' is
+ // not really a variable, though we implement it as one. The
+ // subexpression does not have side effects.
+ context()->Plug(var->is_this());
+ } else {
+ // Non-global variable. Call the runtime to try to delete from the
+ // context where the variable was introduced.
+ __ push(context_register());
+ __ push(Immediate(var->name()));
+ __ CallRuntime(Runtime::kDeleteContextSlot, 2);
+ context()->Plug(eax);
+ }
+ } else {
+ // Result of deleting non-property, non-variable reference is true.
+ // The subexpression may have side effects.
+ VisitForEffect(expr->expression());
+ context()->Plug(true);
+ }
+ break;
+ }
+
+ case Token::VOID: {
+ Comment cmnt(masm_, "[ UnaryOperation (VOID)");
+ VisitForEffect(expr->expression());
+ context()->Plug(isolate()->factory()->undefined_value());
+ break;
+ }
+
+ case Token::NOT: {
+ Comment cmnt(masm_, "[ UnaryOperation (NOT)");
+ if (context()->IsEffect()) {
+ // 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 {
+ // 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;
+ }
+
+ case Token::TYPEOF: {
+ Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
+ { StackValueContext context(this);
+ VisitForTypeofValue(expr->expression());
+ }
+ __ CallRuntime(Runtime::kTypeof, 1);
+ context()->Plug(eax);
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
+ Comment cmnt(masm_, "[ CountOperation");
+ SetSourcePosition(expr->position());
+
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // as the left-hand side.
+ if (!expr->expression()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->expression());
+ return;
+ }
+
+ // Expression can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->expression()->AsProperty();
+ // In case of a property we use the uninitialized expression context
+ // of the key to detect a named property.
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate expression and get value.
+ if (assign_type == VARIABLE) {
+ ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
+ AccumulatorValueContext context(this);
+ EmitVariableLoad(expr->expression()->AsVariableProxy());
+ } else {
+ // Reserve space for result of postfix operation.
+ if (expr->is_postfix() && !context()->IsEffect()) {
+ __ push(Immediate(Smi::FromInt(0)));
+ }
+ if (assign_type == NAMED_PROPERTY) {
+ // Put the object both on the stack and in edx.
+ VisitForAccumulatorValue(prop->obj());
+ __ push(eax);
+ __ mov(edx, eax);
+ EmitNamedPropertyLoad(prop);
+ } else {
+ VisitForStackValue(prop->obj());
+ VisitForStackValue(prop->key());
+ __ mov(edx, Operand(esp, kPointerSize)); // Object.
+ __ mov(ecx, Operand(esp, 0)); // Key.
+ EmitKeyedPropertyLoad(prop);
+ }
+ }
+
+ // We need a second deoptimization point after loading the value
+ // in case evaluating the property load my have a side effect.
+ if (assign_type == VARIABLE) {
+ PrepareForBailout(expr->expression(), TOS_REG);
+ } else {
+ PrepareForBailoutForId(prop->LoadId(), TOS_REG);
+ }
+
+ // Call ToNumber only if operand is not a smi.
+ Label no_conversion;
+ if (ShouldInlineSmiCase(expr->op())) {
+ __ JumpIfSmi(eax, &no_conversion, Label::kNear);
+ }
+ ToNumberStub convert_stub;
+ __ CallStub(&convert_stub);
+ __ bind(&no_conversion);
+
+ // Save result for postfix expressions.
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ // Save the result on the stack. If we have a named or keyed property
+ // we store the result under the receiver that is currently on top
+ // of the stack.
+ switch (assign_type) {
+ case VARIABLE:
+ __ push(eax);
+ break;
+ case NAMED_PROPERTY:
+ __ mov(Operand(esp, kPointerSize), eax);
+ break;
+ case KEYED_PROPERTY:
+ __ mov(Operand(esp, 2 * kPointerSize), eax);
+ break;
+ }
+ }
+ }
+
+ // Inline smi case if we are in a loop.
+ Label done, stub_call;
+ JumpPatchSite patch_site(masm_);
+
+ if (ShouldInlineSmiCase(expr->op())) {
+ if (expr->op() == Token::INC) {
+ __ add(eax, Immediate(Smi::FromInt(1)));
+ } else {
+ __ sub(eax, Immediate(Smi::FromInt(1)));
+ }
+ __ j(overflow, &stub_call, Label::kNear);
+ // We could eliminate this smi check if we split the code at
+ // the first smi check before calling ToNumber.
+ patch_site.EmitJumpIfSmi(eax, &done, Label::kNear);
+
+ __ bind(&stub_call);
+ // Call stub. Undo operation first.
+ if (expr->op() == Token::INC) {
+ __ sub(eax, Immediate(Smi::FromInt(1)));
+ } else {
+ __ add(eax, Immediate(Smi::FromInt(1)));
+ }
+ }
+
+ // Record position before stub call.
+ SetSourcePosition(expr->position());
+
+ // Call stub for +1/-1.
+ __ mov(edx, eax);
+ __ mov(eax, Immediate(Smi::FromInt(1)));
+ BinaryOpStub stub(expr->binary_op(), NO_OVERWRITE);
+ CallIC(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ expr->CountBinOpFeedbackId());
+ patch_site.EmitPatchInfo();
+ __ bind(&done);
+
+ // Store the value returned in eax.
+ switch (assign_type) {
+ case VARIABLE:
+ if (expr->is_postfix()) {
+ // Perform the assignment as if via '='.
+ { EffectContext context(this);
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context.Plug(eax);
+ }
+ // For all contexts except EffectContext We have the result on
+ // top of the stack.
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ // Perform the assignment as if via '='.
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(eax);
+ }
+ break;
+ case NAMED_PROPERTY: {
+ __ mov(ecx, prop->key()->AsLiteral()->value());
+ __ pop(edx);
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(eax);
+ }
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ pop(ecx);
+ __ pop(edx);
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ // Result is on the stack
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(eax);
+ }
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
+ VariableProxy* proxy = expr->AsVariableProxy();
+ ASSERT(!context()->IsEffect());
+ ASSERT(!context()->IsTest());
+
+ if (proxy != NULL && proxy->var()->IsUnallocated()) {
+ Comment cmnt(masm_, "Global variable");
+ __ mov(edx, GlobalObjectOperand());
+ __ mov(ecx, Immediate(proxy->name()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ // Use a regular load, not a contextual load, to avoid a reference
+ // error.
+ CallIC(ic);
+ PrepareForBailout(expr, TOS_REG);
+ context()->Plug(eax);
+ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
+ Label done, slow;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done);
+
+ __ bind(&slow);
+ __ push(esi);
+ __ push(Immediate(proxy->name()));
+ __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ PrepareForBailout(expr, TOS_REG);
+ __ bind(&done);
+
+ context()->Plug(eax);
+ } else {
+ // This expression cannot throw a reference error at the top level.
+ VisitInDuplicateContext(expr);
+ }
+}
+
+
+void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
+ 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(sub_expr);
+ }
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+
+ if (check->Equals(isolate()->heap()->number_string())) {
+ __ JumpIfSmi(eax, if_true);
+ __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
+ isolate()->factory()->heap_number_map());
+ Split(equal, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->string_string())) {
+ __ JumpIfSmi(eax, if_false);
+ __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx);
+ __ j(above_equal, if_false);
+ // Check for undetectable objects => false.
+ __ test_b(FieldOperand(edx, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ Split(zero, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->symbol_string())) {
+ __ JumpIfSmi(eax, if_false);
+ __ CmpObjectType(eax, SYMBOL_TYPE, edx);
+ Split(equal, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->boolean_string())) {
+ __ cmp(eax, isolate()->factory()->true_value());
+ __ j(equal, if_true);
+ __ cmp(eax, isolate()->factory()->false_value());
+ Split(equal, if_true, if_false, fall_through);
+ } else if (FLAG_harmony_typeof &&
+ check->Equals(isolate()->heap()->null_string())) {
+ __ cmp(eax, isolate()->factory()->null_value());
+ Split(equal, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->undefined_string())) {
+ __ cmp(eax, isolate()->factory()->undefined_value());
+ __ j(equal, if_true);
+ __ JumpIfSmi(eax, if_false);
+ // Check for undetectable objects => true.
+ __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(edx, Map::kBitFieldOffset));
+ __ test(ecx, Immediate(1 << Map::kIsUndetectable));
+ Split(not_zero, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->function_string())) {
+ __ JumpIfSmi(eax, if_false);
+ 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_string())) {
+ __ JumpIfSmi(eax, if_false);
+ if (!FLAG_harmony_typeof) {
+ __ cmp(eax, isolate()->factory()->null_value());
+ __ j(equal, if_true);
+ }
+ __ CmpObjectType(eax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, edx);
+ __ j(below, if_false);
+ __ CmpInstanceType(edx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ j(above, if_false);
+ // Check for undetectable objects => false.
+ __ test_b(FieldOperand(edx, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ Split(zero, if_true, if_false, fall_through);
+ } else {
+ if (if_false != fall_through) __ jmp(if_false);
+ }
+ context()->Plug(if_true, if_false);
+}
+
+
+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;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ Token::Value op = expr->op();
+ VisitForStackValue(expr->left());
+ switch (op) {
+ case Token::IN:
+ VisitForStackValue(expr->right());
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ cmp(eax, isolate()->factory()->true_value());
+ Split(equal, if_true, if_false, fall_through);
+ break;
+
+ case Token::INSTANCEOF: {
+ VisitForStackValue(expr->right());
+ InstanceofStub stub(InstanceofStub::kNoFlags);
+ __ CallStub(&stub);
+ 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;
+ }
+
+ default: {
+ VisitForAccumulatorValue(expr->right());
+ Condition cc = CompareIC::ComputeCondition(op);
+ __ pop(edx);
+
+ bool inline_smi_code = ShouldInlineSmiCase(op);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ mov(ecx, edx);
+ __ or_(ecx, eax);
+ patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear);
+ __ cmp(edx, eax);
+ Split(cc, if_true, if_false, NULL);
+ __ bind(&slow_case);
+ }
+
+ // Record position and call the compare IC.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ test(eax, eax);
+ Split(cc, if_true, if_false, fall_through);
+ }
+ }
+
+ // Convert the result of the comparison into one expected for this
+ // expression's context.
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr,
+ Expression* sub_expr,
+ NilValue nil) {
+ 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);
+
+ VisitForAccumulatorValue(sub_expr);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+
+ Handle<Object> nil_value = nil == kNullValue
+ ? isolate()->factory()->null_value()
+ : isolate()->factory()->undefined_value();
+ if (expr->op() == Token::EQ_STRICT) {
+ __ cmp(eax, nil_value);
+ Split(equal, if_true, if_false, fall_through);
+ } else {
+ Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(), nil);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ __ test(eax, eax);
+ Split(not_zero, if_true, if_false, fall_through);
+ }
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
+ __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ context()->Plug(eax);
+}
+
+
+Register FullCodeGenerator::result_register() {
+ return eax;
+}
+
+
+Register FullCodeGenerator::context_register() {
+ return esi;
+}
+
+
+void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
+ ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset);
+ __ mov(Operand(ebp, frame_offset), value);
+}
+
+
+void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
+ __ mov(dst, ContextOperand(esi, context_index));
+}
+
+
+void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
+ Scope* declaration_scope = scope()->DeclarationScope();
+ if (declaration_scope->is_global_scope() ||
+ declaration_scope->is_module_scope()) {
+ // Contexts nested in the native context have a canonical empty function
+ // as their closure, not the anonymous closure containing the global
+ // code. Pass a smi sentinel and let the runtime look up the empty
+ // function.
+ __ push(Immediate(Smi::FromInt(0)));
+ } else if (declaration_scope->is_eval_scope()) {
+ // Contexts nested inside eval code have the same closure as the context
+ // calling eval, not the anonymous closure containing the eval code.
+ // Fetch it from the context.
+ __ push(ContextOperand(esi, Context::CLOSURE_INDEX));
+ } else {
+ ASSERT(declaration_scope->is_function_scope());
+ __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// Non-local control flow support.
+
+void FullCodeGenerator::EnterFinallyBlock() {
+ // Cook return address on top of stack (smi encoded Code* delta)
+ ASSERT(!result_register().is(edx));
+ __ pop(edx);
+ __ sub(edx, Immediate(masm_->CodeObject()));
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ SmiTag(edx);
+ __ push(edx);
+
+ // Store result register while executing finally block.
+ __ push(result_register());
+
+ // Store pending message while executing finally block.
+ ExternalReference pending_message_obj =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ __ mov(edx, Operand::StaticVariable(pending_message_obj));
+ __ push(edx);
+
+ ExternalReference has_pending_message =
+ ExternalReference::address_of_has_pending_message(isolate());
+ __ mov(edx, Operand::StaticVariable(has_pending_message));
+ __ SmiTag(edx);
+ __ push(edx);
+
+ ExternalReference pending_message_script =
+ ExternalReference::address_of_pending_message_script(isolate());
+ __ mov(edx, Operand::StaticVariable(pending_message_script));
+ __ push(edx);
+}
+
+
+void FullCodeGenerator::ExitFinallyBlock() {
+ ASSERT(!result_register().is(edx));
+ // Restore pending message from stack.
+ __ pop(edx);
+ ExternalReference pending_message_script =
+ ExternalReference::address_of_pending_message_script(isolate());
+ __ mov(Operand::StaticVariable(pending_message_script), edx);
+
+ __ pop(edx);
+ __ SmiUntag(edx);
+ ExternalReference has_pending_message =
+ ExternalReference::address_of_has_pending_message(isolate());
+ __ mov(Operand::StaticVariable(has_pending_message), edx);
+
+ __ pop(edx);
+ ExternalReference pending_message_obj =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ __ mov(Operand::StaticVariable(pending_message_obj), edx);
+
+ // Restore result register from stack.
+ __ pop(result_register());
+
+ // Uncook return address.
+ __ pop(edx);
+ __ SmiUntag(edx);
+ __ add(edx, Immediate(masm_->CodeObject()));
+ __ jmp(edx);
+}
+
+
+#undef __
+
+#define __ ACCESS_MASM(masm())
+
+FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
+ int* stack_depth,
+ int* context_length) {
+ // The macros used here must preserve the result register.
+
+ // Because the handler block contains the context of the finally
+ // code, we can restore it directly from there for the finally code
+ // rather than iteratively unwinding contexts via their previous
+ // links.
+ __ Drop(*stack_depth); // Down to the handler block.
+ if (*context_length > 0) {
+ // Restore the context to its dedicated register and the stack.
+ __ mov(esi, Operand(esp, StackHandlerConstants::kContextOffset));
+ __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
+ }
+ __ PopTryHandler();
+ __ call(finally_entry_);
+
+ *stack_depth = 0;
+ *context_length = 0;
+ return previous_;
+}
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/ic-ia32.cc b/chromium/v8/src/ia32/ic-ia32.cc
new file mode 100644
index 00000000000..1e0f14e7687
--- /dev/null
+++ b/chromium/v8/src/ia32/ic-ia32.cc
@@ -0,0 +1,1667 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "codegen.h"
+#include "ic-inl.h"
+#include "runtime.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// Static IC stub generators.
+//
+
+#define __ ACCESS_MASM(masm)
+
+
+static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
+ Register type,
+ Label* global_object) {
+ // Register usage:
+ // type: holds the receiver instance type on entry.
+ __ cmp(type, JS_GLOBAL_OBJECT_TYPE);
+ __ j(equal, global_object);
+ __ cmp(type, JS_BUILTINS_OBJECT_TYPE);
+ __ j(equal, global_object);
+ __ cmp(type, JS_GLOBAL_PROXY_TYPE);
+ __ j(equal, global_object);
+}
+
+
+// Generated code falls through if the receiver is a regular non-global
+// JS object with slow properties and no interceptors.
+static void GenerateNameDictionaryReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register r0,
+ Register r1,
+ Label* miss) {
+ // Register usage:
+ // receiver: holds the receiver on entry and is unchanged.
+ // r0: used to hold receiver instance type.
+ // Holds the property dictionary on fall through.
+ // r1: used to hold receivers map.
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // Check that the receiver is a valid JS object.
+ __ mov(r1, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ movzx_b(r0, FieldOperand(r1, Map::kInstanceTypeOffset));
+ __ cmp(r0, FIRST_SPEC_OBJECT_TYPE);
+ __ j(below, miss);
+
+ // If this assert fails, we have to check upper bound too.
+ STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
+
+ GenerateGlobalInstanceTypeCheck(masm, r0, miss);
+
+ // Check for non-global object that requires access check.
+ __ test_b(FieldOperand(r1, Map::kBitFieldOffset),
+ (1 << Map::kIsAccessCheckNeeded) |
+ (1 << Map::kHasNamedInterceptor));
+ __ j(not_zero, miss);
+
+ __ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset));
+ __ CheckMap(r0, masm->isolate()->factory()->hash_table_map(), miss,
+ DONT_DO_SMI_CHECK);
+}
+
+
+// Helper function used to load a property from a dictionary backing
+// storage. This function may fail to load a property even though it is
+// in the dictionary, so code at miss_label must always call a backup
+// property load that is complete. This function is safe to call if
+// name is not internalized, and will jump to the miss_label in that
+// case. The generated code assumes that the receiver has slow
+// properties, is not a global object and does not have interceptors.
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss_label,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1,
+ Register result) {
+ // Register use:
+ //
+ // elements - holds the property dictionary on entry and is unchanged.
+ //
+ // name - holds the name of the property on entry and is unchanged.
+ //
+ // Scratch registers:
+ //
+ // r0 - used for the index into the property dictionary
+ //
+ // r1 - used to hold the capacity of the property dictionary.
+ //
+ // result - holds the result on exit.
+
+ Label done;
+
+ // Probe the dictionary.
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
+
+ // If probing finds an entry in the dictionary, r0 contains the
+ // index into the dictionary. Check that the value is a normal
+ // property.
+ __ bind(&done);
+ const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
+ Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize));
+ __ j(not_zero, miss_label);
+
+ // Get the value at the masked, scaled index.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ mov(result, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
+}
+
+
+// Helper function used to store a property to a dictionary backing
+// storage. This function may fail to store a property eventhough it
+// is in the dictionary, so code at miss_label must always call a
+// backup property store that is complete. This function is safe to
+// call if name is not internalized, and will jump to the miss_label in
+// that case. The generated code assumes that the receiver has slow
+// properties, is not a global object and does not have interceptors.
+static void GenerateDictionaryStore(MacroAssembler* masm,
+ Label* miss_label,
+ Register elements,
+ Register name,
+ Register value,
+ Register r0,
+ Register r1) {
+ // Register use:
+ //
+ // elements - holds the property dictionary on entry and is clobbered.
+ //
+ // name - holds the name of the property on entry and is unchanged.
+ //
+ // value - holds the value to store and is unchanged.
+ //
+ // r0 - used for index into the property dictionary and is clobbered.
+ //
+ // r1 - used to hold the capacity of the property dictionary and is clobbered.
+ Label done;
+
+
+ // Probe the dictionary.
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
+
+ // If probing finds an entry in the dictionary, r0 contains the
+ // index into the dictionary. Check that the value is a normal
+ // property that is not read only.
+ __ bind(&done);
+ const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ const int kTypeAndReadOnlyMask =
+ (PropertyDetails::TypeField::kMask |
+ PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
+ __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
+ Immediate(kTypeAndReadOnlyMask));
+ __ j(not_zero, miss_label);
+
+ // Store the value at the masked, scaled index.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ lea(r0, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
+ __ mov(Operand(r0, 0), value);
+
+ // Update write barrier. Make sure not to clobber the value.
+ __ mov(r1, value);
+ __ RecordWrite(elements, r0, r1, kDontSaveFPRegs);
+}
+
+
+// Checks the receiver for special cases (value type, slow case bits).
+// Falls through for regular JS object.
+static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register map,
+ int interceptor_bit,
+ Label* slow) {
+ // Register use:
+ // receiver - holds the receiver and is unchanged.
+ // Scratch registers:
+ // map - used to hold the map of the receiver.
+
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, slow);
+
+ // Get the map of the receiver.
+ __ mov(map, FieldOperand(receiver, HeapObject::kMapOffset));
+
+ // Check bit field.
+ __ test_b(FieldOperand(map, Map::kBitFieldOffset),
+ (1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit));
+ __ j(not_zero, slow);
+ // Check that the object is some kind of JS object EXCEPT JS Value type.
+ // In the case that the object is a value-wrapper object,
+ // we enter the runtime system to make sure that indexing
+ // into string objects works as intended.
+ ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
+
+ __ CmpInstanceType(map, JS_OBJECT_TYPE);
+ __ j(below, slow);
+}
+
+
+// Loads an indexed element from a fast case array.
+// If not_fast_array is NULL, doesn't perform the elements map check.
+static void GenerateFastArrayLoad(MacroAssembler* masm,
+ Register receiver,
+ Register key,
+ Register scratch,
+ Register result,
+ Label* not_fast_array,
+ Label* out_of_range) {
+ // Register use:
+ // receiver - holds the receiver and is unchanged.
+ // key - holds the key and is unchanged (must be a smi).
+ // Scratch registers:
+ // scratch - used to hold elements of the receiver and the loaded value.
+ // result - holds the result on exit if the load succeeds and
+ // we fall through.
+
+ __ mov(scratch, FieldOperand(receiver, JSObject::kElementsOffset));
+ if (not_fast_array != NULL) {
+ // Check that the object is in fast mode and writable.
+ __ CheckMap(scratch,
+ masm->isolate()->factory()->fixed_array_map(),
+ not_fast_array,
+ DONT_DO_SMI_CHECK);
+ } else {
+ __ AssertFastElements(scratch);
+ }
+ // Check that the key (index) is within bounds.
+ __ cmp(key, FieldOperand(scratch, FixedArray::kLengthOffset));
+ __ j(above_equal, out_of_range);
+ // Fast case: Do the load.
+ STATIC_ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0));
+ __ mov(scratch, FieldOperand(scratch, key, times_2, FixedArray::kHeaderSize));
+ __ cmp(scratch, Immediate(masm->isolate()->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);
+ if (!result.is(scratch)) {
+ __ mov(result, scratch);
+ }
+}
+
+
+// Checks whether a key is an array index string or a unique name.
+// Falls through if the key is a unique name.
+static void GenerateKeyNameCheck(MacroAssembler* masm,
+ Register key,
+ Register map,
+ Register hash,
+ Label* index_string,
+ Label* not_unique) {
+ // Register use:
+ // key - holds the key and is unchanged. Assumed to be non-smi.
+ // Scratch registers:
+ // map - used to hold the map of the key.
+ // hash - used to hold the hash of the key.
+ Label unique;
+ __ CmpObjectType(key, LAST_UNIQUE_NAME_TYPE, map);
+ __ j(above, not_unique);
+ STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
+ __ j(equal, &unique);
+
+ // Is the string an array index, with cached numeric value?
+ __ mov(hash, FieldOperand(key, Name::kHashFieldOffset));
+ __ test(hash, Immediate(Name::kContainsCachedArrayIndexMask));
+ __ j(zero, index_string);
+
+ // Is the string internalized? We already know it's a string so a single
+ // bit test is enough.
+ STATIC_ASSERT(kNotInternalizedTag != 0);
+ __ test_b(FieldOperand(map, Map::kInstanceTypeOffset),
+ kIsNotInternalizedMask);
+ __ j(not_zero, not_unique);
+
+ __ bind(&unique);
+}
+
+
+static Operand GenerateMappedArgumentsLookup(MacroAssembler* masm,
+ Register object,
+ Register key,
+ Register scratch1,
+ Register scratch2,
+ Label* unmapped_case,
+ Label* slow_case) {
+ Heap* heap = masm->isolate()->heap();
+ Factory* factory = masm->isolate()->factory();
+
+ // Check that the receiver is a JSObject. Because of the elements
+ // map check later, we do not need to check for interceptors or
+ // whether it requires access checks.
+ __ JumpIfSmi(object, slow_case);
+ // Check that the object is some kind of JSObject.
+ __ CmpObjectType(object, FIRST_JS_RECEIVER_TYPE, scratch1);
+ __ j(below, slow_case);
+
+ // Check that the key is a positive smi.
+ __ test(key, Immediate(0x80000001));
+ __ j(not_zero, slow_case);
+
+ // Load the elements into scratch1 and check its map.
+ Handle<Map> arguments_map(heap->non_strict_arguments_elements_map());
+ __ mov(scratch1, FieldOperand(object, JSObject::kElementsOffset));
+ __ CheckMap(scratch1, arguments_map, slow_case, DONT_DO_SMI_CHECK);
+
+ // 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(scratch2, Immediate(Smi::FromInt(2)));
+ __ cmp(key, scratch2);
+ __ j(above_equal, unmapped_case);
+
+ // Load element index and check whether it is the hole.
+ const int kHeaderSize = FixedArray::kHeaderSize + 2 * kPointerSize;
+ __ mov(scratch2, FieldOperand(scratch1,
+ key,
+ times_half_pointer_size,
+ kHeaderSize));
+ __ cmp(scratch2, factory->the_hole_value());
+ __ j(equal, unmapped_case);
+
+ // Load value from context and return it. We can reuse scratch1 because
+ // we do not jump to the unmapped lookup (which requires the parameter
+ // map in scratch1).
+ const int kContextOffset = FixedArray::kHeaderSize;
+ __ mov(scratch1, FieldOperand(scratch1, kContextOffset));
+ return FieldOperand(scratch1,
+ scratch2,
+ times_half_pointer_size,
+ Context::kHeaderSize);
+}
+
+
+static Operand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
+ Register key,
+ Register parameter_map,
+ Register scratch,
+ Label* slow_case) {
+ // Element is in arguments backing store, which is referenced by the
+ // second element of the parameter_map.
+ const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize;
+ Register backing_store = parameter_map;
+ __ mov(backing_store, FieldOperand(parameter_map, kBackingStoreOffset));
+ 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, scratch);
+ __ j(greater_equal, slow_case);
+ return FieldOperand(backing_store,
+ key,
+ times_half_pointer_size,
+ FixedArray::kHeaderSize);
+}
+
+
+void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label slow, check_name, index_smi, index_name, property_array_property;
+ Label probe_dictionary, check_number_dictionary;
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(ecx, &check_name);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, edx, eax, Map::kHasIndexedInterceptor, &slow);
+
+ // Check the receiver's map to see if it has fast elements.
+ __ CheckFastElements(eax, &check_number_dictionary);
+
+ GenerateFastArrayLoad(masm, edx, ecx, eax, eax, NULL, &slow);
+ Isolate* isolate = masm->isolate();
+ Counters* counters = isolate->counters();
+ __ IncrementCounter(counters->keyed_load_generic_smi(), 1);
+ __ ret(0);
+
+ __ bind(&check_number_dictionary);
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ __ mov(eax, FieldOperand(edx, JSObject::kElementsOffset));
+
+ // Check whether the elements is a number dictionary.
+ // edx: receiver
+ // ebx: untagged index
+ // ecx: key
+ // eax: elements
+ __ CheckMap(eax,
+ isolate->factory()->hash_table_map(),
+ &slow,
+ DONT_DO_SMI_CHECK);
+ Label slow_pop_receiver;
+ // Push receiver on the stack to free up a register for the dictionary
+ // probing.
+ __ push(edx);
+ __ LoadFromNumberDictionary(&slow_pop_receiver, eax, ecx, ebx, edx, edi, eax);
+ // Pop receiver before returning.
+ __ pop(edx);
+ __ ret(0);
+
+ __ bind(&slow_pop_receiver);
+ // Pop the receiver from the stack and jump to runtime.
+ __ pop(edx);
+
+ __ bind(&slow);
+ // Slow case: jump to runtime.
+ // edx: receiver
+ // ecx: key
+ __ IncrementCounter(counters->keyed_load_generic_slow(), 1);
+ GenerateRuntimeGetProperty(masm);
+
+ __ bind(&check_name);
+ GenerateKeyNameCheck(masm, ecx, eax, ebx, &index_name, &slow);
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, edx, eax, Map::kHasNamedInterceptor, &slow);
+
+ // If the receiver is a fast-case object, check the keyed lookup
+ // cache. Otherwise probe the dictionary.
+ __ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset));
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(isolate->factory()->hash_table_map()));
+ __ j(equal, &probe_dictionary);
+
+ // The receiver's map is still in eax, compute the keyed lookup cache hash
+ // based on 32 bits of the map pointer and the string hash.
+ if (FLAG_debug_code) {
+ __ cmp(eax, FieldOperand(edx, HeapObject::kMapOffset));
+ __ Check(equal, kMapIsNoLongerInEax);
+ }
+ __ mov(ebx, eax); // Keep the map around for later.
+ __ shr(eax, KeyedLookupCache::kMapHashShift);
+ __ mov(edi, FieldOperand(ecx, String::kHashFieldOffset));
+ __ shr(edi, String::kHashShift);
+ __ xor_(eax, edi);
+ __ and_(eax, KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask);
+
+ // Load the key (consisting of map and internalized string) 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());
+
+ for (int i = 0; i < kEntriesPerBucket - 1; i++) {
+ Label try_next_entry;
+ __ mov(edi, eax);
+ __ 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(ecx, Operand::StaticArray(edi, times_1, cache_keys));
+ __ j(equal, &hit_on_nth_entry[i]);
+ __ bind(&try_next_entry);
+ }
+
+ __ lea(edi, Operand(eax, 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(edi, Immediate(kPointerSize));
+ __ cmp(ecx, Operand::StaticArray(edi, times_1, cache_keys));
+ __ j(not_equal, &slow);
+
+ // Get field offset.
+ // edx : receiver
+ // ebx : receiver's map
+ // ecx : key
+ // eax : lookup cache index
+ ExternalReference cache_field_offsets =
+ ExternalReference::keyed_lookup_cache_field_offsets(masm->isolate());
+
+ // Hit on nth entry.
+ for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
+ __ bind(&hit_on_nth_entry[i]);
+ if (i != 0) {
+ __ add(eax, Immediate(i));
+ }
+ __ mov(edi,
+ Operand::StaticArray(eax, times_pointer_size, cache_field_offsets));
+ __ movzx_b(eax, FieldOperand(ebx, Map::kInObjectPropertiesOffset));
+ __ sub(edi, eax);
+ __ 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(eax, FieldOperand(ebx, Map::kInstanceSizeOffset));
+ __ add(eax, edi);
+ __ mov(eax, FieldOperand(edx, eax, times_pointer_size, 0));
+ __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1);
+ __ ret(0);
+
+ // Load property array property.
+ __ bind(&property_array_property);
+ __ mov(eax, FieldOperand(edx, JSObject::kPropertiesOffset));
+ __ mov(eax, FieldOperand(eax, edi, times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1);
+ __ ret(0);
+
+ // Do a quick inline probe of the receiver's dictionary, if it
+ // exists.
+ __ bind(&probe_dictionary);
+
+ __ mov(eax, FieldOperand(edx, JSObject::kMapOffset));
+ __ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset));
+ GenerateGlobalInstanceTypeCheck(masm, eax, &slow);
+
+ GenerateDictionaryLoad(masm, &slow, ebx, ecx, eax, edi, eax);
+ __ IncrementCounter(counters->keyed_load_generic_symbol(), 1);
+ __ ret(0);
+
+ __ bind(&index_name);
+ __ IndexFromHash(ebx, ecx);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
+}
+
+
+void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : key (index)
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ Register receiver = edx;
+ Register index = ecx;
+ Register scratch = ebx;
+ Register result = eax;
+
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &miss, // When index out of range.
+ STRING_INDEX_IS_ARRAY_INDEX);
+ char_at_generator.GenerateFast(masm);
+ __ ret(0);
+
+ StubRuntimeCallHelper call_helper;
+ char_at_generator.GenerateSlow(masm, call_helper);
+
+ __ bind(&miss);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label slow;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(edx, &slow);
+
+ // Check that the key is an array index, that is Uint32.
+ __ test(ecx, Immediate(kSmiTagMask | kSmiSignMask));
+ __ j(not_zero, &slow);
+
+ // Get the map of the receiver.
+ __ mov(eax, FieldOperand(edx, HeapObject::kMapOffset));
+
+ // Check that it has indexed interceptor and access checks
+ // are not enabled for this object.
+ __ movzx_b(eax, FieldOperand(eax, Map::kBitFieldOffset));
+ __ and_(eax, Immediate(kSlowCaseBitFieldMask));
+ __ cmp(eax, Immediate(1 << Map::kHasIndexedInterceptor));
+ __ j(not_zero, &slow);
+
+ // Everything is fine, call runtime.
+ __ pop(eax);
+ __ push(edx); // receiver
+ __ push(ecx); // key
+ __ push(eax); // return address
+
+ // Perform tail call to the entry.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kKeyedLoadPropertyWithInterceptor),
+ masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedLoadIC::GenerateNonStrictArguments(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label slow, notin;
+ Factory* factory = masm->isolate()->factory();
+ Operand mapped_location =
+ GenerateMappedArgumentsLookup(masm, edx, ecx, ebx, eax, &notin, &slow);
+ __ mov(eax, mapped_location);
+ __ Ret();
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in ebx.
+ Operand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, ecx, ebx, eax, &slow);
+ __ cmp(unmapped_location, factory->the_hole_value());
+ __ j(equal, &slow);
+ __ mov(eax, unmapped_location);
+ __ Ret();
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label slow, notin;
+ Operand mapped_location =
+ GenerateMappedArgumentsLookup(masm, edx, ecx, ebx, edi, &notin, &slow);
+ __ mov(mapped_location, eax);
+ __ lea(ecx, mapped_location);
+ __ mov(edx, eax);
+ __ RecordWrite(ebx, ecx, edx, kDontSaveFPRegs);
+ __ Ret();
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in ebx.
+ Operand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, ecx, ebx, edi, &slow);
+ __ mov(unmapped_location, eax);
+ __ lea(edi, unmapped_location);
+ __ mov(edx, eax);
+ __ RecordWrite(ebx, edi, edx, kDontSaveFPRegs);
+ __ Ret();
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+static void KeyedStoreGenerateGenericHelper(
+ MacroAssembler* masm,
+ Label* fast_object,
+ Label* fast_double,
+ Label* slow,
+ KeyedStoreCheckMap check_map,
+ KeyedStoreIncrementLength increment_length) {
+ Label transition_smi_elements;
+ Label finish_object_store, non_double_value, transition_double_elements;
+ Label fast_double_without_map_check;
+ // eax: value
+ // ecx: key (a smi)
+ // edx: receiver
+ // ebx: FixedArray receiver->elements
+ // edi: receiver map
+ // Fast case: Do the store, could either Object or double.
+ __ bind(fast_object);
+ if (check_map == kCheckMap) {
+ __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset));
+ __ cmp(edi, masm->isolate()->factory()->fixed_array_map());
+ __ j(not_equal, fast_double);
+ }
+ // Smi stores don't require further checks.
+ Label non_smi_value;
+ __ JumpIfNotSmi(eax, &non_smi_value);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ }
+ // 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);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ }
+ __ mov(CodeGenerator::FixedArrayElementOperand(ebx, ecx), eax);
+ // Update write barrier for the elements array address.
+ __ 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);
+ if (check_map == kCheckMap) {
+ // 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);
+ // If the value is a number, store it as a double in the FastDoubleElements
+ // array.
+ }
+ __ bind(&fast_double_without_map_check);
+ __ StoreNumberToDoubleElements(eax, ebx, ecx, edi, xmm0,
+ &transition_double_elements, false);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ }
+ __ 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_ELEMENTS -> FAST_DOUBLE_ELEMENTS
+ // and complete the store.
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS,
+ ebx,
+ edi,
+ slow);
+ AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, slow);
+ __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
+ __ jmp(&fast_double_without_map_check);
+
+ __ bind(&non_double_value);
+ // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_ELEMENTS,
+ ebx,
+ edi,
+ slow);
+ mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm, mode,
+ slow);
+ __ 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);
+ mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, slow);
+ __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
+ __ jmp(&finish_object_store);
+}
+
+
+void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label slow, fast_object, fast_object_grow;
+ Label fast_double, fast_double_grow;
+ Label array, extra, check_if_double_array;
+
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(edx, &slow);
+ // Get the map from the receiver.
+ __ mov(edi, FieldOperand(edx, 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.
+ __ test_b(FieldOperand(edi, Map::kBitFieldOffset),
+ 1 << Map::kIsAccessCheckNeeded);
+ __ j(not_zero, &slow);
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(ecx, &slow);
+ __ CmpInstanceType(edi, JS_ARRAY_TYPE);
+ __ j(equal, &array);
+ // Check that the object is some kind of JSObject.
+ __ CmpInstanceType(edi, FIRST_JS_OBJECT_TYPE);
+ __ j(below, &slow);
+
+ // Object case: Check key against length in the elements array.
+ // eax: value
+ // edx: JSObject
+ // ecx: key (a smi)
+ // 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);
+
+ // Slow case: call runtime.
+ __ bind(&slow);
+ 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);
+ // eax: value
+ // edx: receiver, a JSArray
+ // ecx: key, a smi.
+ // 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(ebx, FixedArray::kLengthOffset));
+ __ j(above_equal, &slow);
+ __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset));
+ __ cmp(edi, masm->isolate()->factory()->fixed_array_map());
+ __ j(not_equal, &check_if_double_array);
+ __ jmp(&fast_object_grow);
+
+ __ bind(&check_if_double_array);
+ __ cmp(edi, masm->isolate()->factory()->fixed_double_array_map());
+ __ j(not_equal, &slow);
+ __ jmp(&fast_double_grow);
+
+ // 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);
+ // eax: value
+ // edx: receiver, a JSArray
+ // ecx: key, a smi.
+ // edi: receiver map
+ __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
+
+ // 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);
+
+ KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double,
+ &slow, kCheckMap, kDontIncrementLength);
+ KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow,
+ &slow, kDontCheckMap, kIncrementLength);
+}
+
+
+// The generated code does not accept smi keys.
+// The generated code falls through if both probes miss.
+void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm,
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edx : receiver
+ // -----------------------------------
+ Label number, non_number, non_string, boolean, probe, miss;
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(kind,
+ MONOMORPHIC,
+ extra_state,
+ Code::NORMAL,
+ argc);
+ 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
+ // the corresponding JSValue for the cache and that is what we need
+ // to probe.
+ //
+ // Check for number.
+ __ JumpIfSmi(edx, &number);
+ __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ebx);
+ __ j(not_equal, &non_number);
+ __ bind(&number);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::NUMBER_FUNCTION_INDEX, edx);
+ __ jmp(&probe);
+
+ // Check for string.
+ __ bind(&non_number);
+ __ CmpInstanceType(ebx, FIRST_NONSTRING_TYPE);
+ __ j(above_equal, &non_string);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::STRING_FUNCTION_INDEX, edx);
+ __ jmp(&probe);
+
+ // Check for boolean.
+ __ bind(&non_string);
+ __ cmp(edx, isolate->factory()->true_value());
+ __ j(equal, &boolean);
+ __ cmp(edx, isolate->factory()->false_value());
+ __ j(not_equal, &miss);
+ __ bind(&boolean);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::BOOLEAN_FUNCTION_INDEX, edx);
+
+ // Probe the stub cache for the value object.
+ __ bind(&probe);
+ isolate->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx, no_reg);
+ __ bind(&miss);
+}
+
+
+static void GenerateFunctionTailCall(MacroAssembler* masm,
+ int argc,
+ Label* miss) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edi : function
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ // Check that the result is not a smi.
+ __ JumpIfSmi(edi, miss);
+
+ // Check that the value is a JavaScript function, fetching its map into eax.
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
+ __ j(not_equal, miss);
+
+ // Invoke the function.
+ ParameterCount actual(argc);
+ __ InvokeFunction(edi, actual, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
+}
+
+
+// The generated code falls through if the call should be handled by runtime.
+void CallICBase::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
+ // -----------------------------------
+ Label miss;
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ GenerateNameDictionaryReceiverCheck(masm, edx, eax, ebx, &miss);
+
+ // eax: elements
+ // Search the dictionary placing the result in edi.
+ GenerateDictionaryLoad(masm, &miss, eax, ecx, edi, ebx, edi);
+ GenerateFunctionTailCall(masm, argc, &miss);
+
+ __ bind(&miss);
+}
+
+
+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
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ Counters* counters = masm->isolate()->counters();
+ if (id == IC::kCallIC_Miss) {
+ __ IncrementCounter(counters->call_miss(), 1);
+ } else {
+ __ IncrementCounter(counters->keyed_call_miss(), 1);
+ }
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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);
+
+ // 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.
+ if (id == IC::kCallIC_Miss) {
+ Label invoke, global;
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // receiver
+ __ JumpIfSmi(edx, &invoke, Label::kNear);
+ __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
+ __ cmp(ebx, JS_GLOBAL_OBJECT_TYPE);
+ __ j(equal, &global, Label::kNear);
+ __ cmp(ebx, JS_BUILTINS_OBJECT_TYPE);
+ __ j(not_equal, &invoke, Label::kNear);
+
+ // Patch the receiver on the stack.
+ __ bind(&global);
+ __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
+ __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
+ __ bind(&invoke);
+ }
+
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount actual(argc);
+ __ InvokeFunction(edi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ call_kind);
+}
+
+
+void CallIC::GenerateMegamorphic(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_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
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+ CallICBase::GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC,
+ extra_state);
+
+ GenerateMiss(masm, argc, extra_state);
+}
+
+
+void KeyedCallIC::GenerateMegamorphic(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
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ Label do_call, slow_call, slow_load, slow_reload_receiver;
+ Label check_number_dictionary, check_name, lookup_monomorphic_cache;
+ Label index_smi, index_name;
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(ecx, &check_name);
+
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, edx, eax, Map::kHasIndexedInterceptor, &slow_call);
+
+ GenerateFastArrayLoad(
+ masm, edx, ecx, eax, edi, &check_number_dictionary, &slow_load);
+ Isolate* isolate = masm->isolate();
+ Counters* counters = isolate->counters();
+ __ IncrementCounter(counters->keyed_call_generic_smi_fast(), 1);
+
+ __ bind(&do_call);
+ // receiver in edx is not used after this point.
+ // ecx: key
+ // edi: function
+ GenerateFunctionTailCall(masm, argc, &slow_call);
+
+ __ bind(&check_number_dictionary);
+ // eax: elements
+ // ecx: smi key
+ // Check whether the elements is a number dictionary.
+ __ CheckMap(eax,
+ isolate->factory()->hash_table_map(),
+ &slow_load,
+ DONT_DO_SMI_CHECK);
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ // ebx: untagged index
+ // Receiver in edx will be clobbered, need to reload it on miss.
+ __ LoadFromNumberDictionary(
+ &slow_reload_receiver, eax, ecx, ebx, edx, edi, edi);
+ __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1);
+ __ jmp(&do_call);
+
+ __ bind(&slow_reload_receiver);
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ __ bind(&slow_load);
+ // This branch is taken when calling KeyedCallIC_Miss is neither required
+ // nor beneficial.
+ __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1);
+
+ {
+ 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);
+
+ __ bind(&check_name);
+ GenerateKeyNameCheck(masm, ecx, eax, ebx, &index_name, &slow_call);
+
+ // The key is known to be a unique name.
+ // If the receiver is a regular JS object with slow properties then do
+ // a quick inline probe of the receiver's dictionary.
+ // Otherwise do the monomorphic cache probe.
+ GenerateKeyedLoadReceiverCheck(
+ masm, edx, eax, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
+
+ __ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset));
+ __ CheckMap(ebx,
+ isolate->factory()->hash_table_map(),
+ &lookup_monomorphic_cache,
+ DONT_DO_SMI_CHECK);
+
+ GenerateDictionaryLoad(masm, &slow_load, ebx, ecx, eax, edi, edi);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1);
+ __ jmp(&do_call);
+
+ __ bind(&lookup_monomorphic_cache);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1);
+ CallICBase::GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC,
+ Code::kNoExtraICState);
+ // Fall through on miss.
+
+ __ bind(&slow_call);
+ // This branch is taken if:
+ // - the receiver requires boxing or access check,
+ // - the key is neither smi nor a unique name,
+ // - the value loaded is not a function,
+ // - there is hope that the runtime will create a monomorphic call stub
+ // that will get fetched next time.
+ __ IncrementCounter(counters->keyed_call_generic_slow(), 1);
+ GenerateMiss(masm, argc);
+
+ __ bind(&index_name);
+ __ IndexFromHash(ebx, ecx);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
+}
+
+
+void KeyedCallIC::GenerateNonStrictArguments(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
+ // -----------------------------------
+ Label slow, notin;
+ Factory* factory = masm->isolate()->factory();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+ Operand mapped_location =
+ GenerateMappedArgumentsLookup(masm, edx, ecx, ebx, eax, &notin, &slow);
+ __ mov(edi, mapped_location);
+ GenerateFunctionTailCall(masm, argc, &slow);
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in ebx.
+ Operand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, ecx, ebx, eax, &slow);
+ __ cmp(unmapped_location, factory->the_hole_value());
+ __ j(equal, &slow);
+ __ mov(edi, unmapped_location);
+ GenerateFunctionTailCall(masm, argc, &slow);
+ __ bind(&slow);
+ GenerateMiss(masm, argc);
+}
+
+
+void KeyedCallIC::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
+ // -----------------------------------
+
+ // Check if the name is really a name.
+ Label miss;
+ __ JumpIfSmi(ecx, &miss);
+ Condition cond = masm->IsObjectNameType(ecx, eax, eax);
+ __ j(NegateCondition(cond), &miss);
+ CallICBase::GenerateNormal(masm, argc);
+ __ bind(&miss);
+ GenerateMiss(masm, argc);
+}
+
+
+void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, Code::kNoExtraICState,
+ Code::NORMAL, Code::LOAD_IC);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, edx, ecx, ebx, eax);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
+}
+
+
+void LoadIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameDictionaryReceiverCheck(masm, edx, eax, ebx, &miss);
+
+ // eax: elements
+ // Search the dictionary placing the result in eax.
+ GenerateDictionaryLoad(masm, &miss, eax, ecx, edi, ebx, eax);
+ __ ret(0);
+
+ // Cache miss: Jump to runtime.
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void LoadIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ IncrementCounter(masm->isolate()->counters()->load_miss(), 1);
+
+ __ pop(ebx);
+ __ push(edx); // receiver
+ __ push(ecx); // name
+ __ push(ebx); // return address
+
+ // Perform tail call to the entry.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kLoadIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ pop(ebx);
+ __ push(edx); // receiver
+ __ push(ecx); // name
+ __ push(ebx); // return address
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ IncrementCounter(masm->isolate()->counters()->keyed_load_miss(), 1);
+
+ __ pop(ebx);
+ __ push(edx); // receiver
+ __ push(ecx); // name
+ __ push(ebx); // return address
+
+ // Perform tail call to the entry.
+ ExternalReference ref = miss_mode == MISS_FORCE_GENERIC
+ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ pop(ebx);
+ __ push(edx); // receiver
+ __ push(ecx); // name
+ __ push(ebx); // return address
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
+}
+
+
+void StoreIC::GenerateMegamorphic(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, strict_mode,
+ Code::NORMAL, Code::STORE_IC);
+ Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx,
+ no_reg);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
+}
+
+
+void StoreIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ecx);
+ __ push(eax);
+ __ push(ebx);
+
+ // Perform tail call to the entry.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ Label miss, restore_miss;
+
+ GenerateNameDictionaryReceiverCheck(masm, edx, ebx, edi, &miss);
+
+ // A lot of registers are needed for storing to slow case
+ // objects. Push and restore receiver but rely on
+ // GenerateDictionaryStore preserving the value and name.
+ __ push(edx);
+ GenerateDictionaryStore(masm, &restore_miss, ebx, ecx, eax, edx, edi);
+ __ Drop(1);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->store_normal_hit(), 1);
+ __ ret(0);
+
+ __ bind(&restore_miss);
+ __ pop(edx);
+
+ __ bind(&miss);
+ __ IncrementCounter(counters->store_normal_miss(), 1);
+ GenerateMiss(masm);
+}
+
+
+void StoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ecx);
+ __ push(eax);
+ __ push(Immediate(Smi::FromInt(NONE))); // PropertyAttributes
+ __ push(Immediate(Smi::FromInt(strict_mode)));
+ __ push(ebx); // return address
+
+ // Do tail-call to runtime routine.
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+}
+
+
+void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ecx);
+ __ push(eax);
+ __ push(Immediate(Smi::FromInt(NONE))); // PropertyAttributes
+ __ push(Immediate(Smi::FromInt(strict_mode))); // Strict mode.
+ __ push(ebx); // return address
+
+ // Do tail-call to runtime routine.
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ecx);
+ __ push(eax);
+ __ push(ebx);
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref = miss_mode == MISS_FORCE_GENERIC
+ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ecx);
+ __ push(eax);
+ __ push(ebx); // return address
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref(IC_Utility(kStoreIC_Slow), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ecx);
+ __ push(eax);
+ __ push(ebx); // return address
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+#undef __
+
+
+Condition CompareIC::ComputeCondition(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;
+ }
+}
+
+
+bool CompareIC::HasInlinedSmiCode(Address address) {
+ // The address of the instruction following the call.
+ Address test_instruction_address =
+ address + Assembler::kCallTargetAddressOffset;
+
+ // If the instruction following the call is not a test al, nothing
+ // was inlined.
+ return *test_instruction_address == Assembler::kTestAlByte;
+}
+
+
+void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
+ // The address of the instruction following the call.
+ Address test_instruction_address =
+ address + Assembler::kCallTargetAddressOffset;
+
+ // If the instruction following the call is not a test al, nothing
+ // was inlined.
+ if (*test_instruction_address != Assembler::kTestAlByte) {
+ ASSERT(*test_instruction_address == Assembler::kNopByte);
+ return;
+ }
+
+ Address delta_address = test_instruction_address + 1;
+ // The delta to the start of the map check instruction and the
+ // condition code uses at the patched jump.
+ int8_t delta = *reinterpret_cast<int8_t*>(delta_address);
+ if (FLAG_trace_ic) {
+ PrintF("[ patching ic at %p, test=%p, delta=%d\n",
+ address, test_instruction_address, delta);
+ }
+
+ // Patch with a short conditional jump. Enabling means switching from a short
+ // jump-if-carry/not-carry to jump-if-zero/not-zero, whereas disabling is the
+ // reverse operation of that.
+ Address jmp_address = test_instruction_address - delta;
+ ASSERT((check == ENABLE_INLINED_SMI_CHECK)
+ ? (*jmp_address == Assembler::kJncShortOpcode ||
+ *jmp_address == Assembler::kJcShortOpcode)
+ : (*jmp_address == Assembler::kJnzShortOpcode ||
+ *jmp_address == Assembler::kJzShortOpcode));
+ Condition cc = (check == ENABLE_INLINED_SMI_CHECK)
+ ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero)
+ : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry);
+ *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/lithium-codegen-ia32.cc b/chromium/v8/src/ia32/lithium-codegen-ia32.cc
new file mode 100644
index 00000000000..19c553bfa51
--- /dev/null
+++ b/chromium/v8/src/ia32/lithium-codegen-ia32.cc
@@ -0,0 +1,6461 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "ia32/lithium-codegen-ia32.h"
+#include "ic.h"
+#include "code-stubs.h"
+#include "deoptimizer.h"
+#include "stub-cache.h"
+#include "codegen.h"
+
+namespace v8 {
+namespace internal {
+
+
+static SaveFPRegsMode GetSaveFPRegsMode() {
+ // We don't need to save floating point regs when generating the snapshot
+ return CpuFeatures::IsSafeForSnapshot(SSE2) ? kSaveFPRegs : kDontSaveFPRegs;
+}
+
+
+// When invoking builtins, we need to record the safepoint in the middle of
+// the invoke instruction sequence generated by the macro assembler.
+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() {
+ LPhase phase("Z_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);
+
+ support_aligned_spilled_doubles_ = info()->IsOptimizing();
+
+ dynamic_frame_alignment_ = info()->IsOptimizing() &&
+ ((chunk()->num_double_slots() > 2 &&
+ !chunk()->graph()->is_recursive()) ||
+ !info()->osr_ast_id().IsNone());
+
+ return GeneratePrologue() &&
+ GenerateBody() &&
+ GenerateDeferredCode() &&
+ GenerateJumpTable() &&
+ GenerateSafepointTable();
+}
+
+
+void LCodeGen::FinishCode(Handle<Code> code) {
+ ASSERT(is_done());
+ code->set_stack_slots(GetStackSlotCount());
+ code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
+ if (FLAG_weak_embedded_maps_in_optimized_code) {
+ RegisterDependentCodeForEmbeddedMaps(code);
+ }
+ PopulateDeoptimizationData(code);
+ if (!info()->IsStub()) {
+ Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code);
+ }
+ info()->CommitDependencies(code);
+}
+
+
+void LCodeGen::Abort(BailoutReason reason) {
+ info()->set_bailout_reason(reason);
+ 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);
+ OS::MemCopy(copy.start(), builder.Finalize(), copy.length());
+ masm()->RecordComment(copy.start());
+}
+
+
+#ifdef _MSC_VER
+void LCodeGen::MakeSureStackPagesMapped(int offset) {
+ const int kPageSize = 4 * KB;
+ for (offset -= kPageSize; offset > 0; offset -= kPageSize) {
+ __ mov(Operand(esp, offset), eax);
+ }
+}
+#endif
+
+
+bool LCodeGen::GeneratePrologue() {
+ ASSERT(is_generating());
+
+ if (info()->IsOptimizing()) {
+ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info_->function()->name()->IsUtf8EqualTo(CStrVector(FLAG_stop_at))) {
+ __ int3();
+ }
+#endif
+
+ // 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_classic_mode() || info_->is_native()) {
+ Label ok;
+ __ test(ecx, Operand(ecx));
+ __ j(zero, &ok, Label::kNear);
+ // +1 for return address.
+ int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
+ __ mov(Operand(esp, receiver_offset),
+ Immediate(isolate()->factory()->undefined_value()));
+ __ bind(&ok);
+ }
+
+ if (support_aligned_spilled_doubles_ && dynamic_frame_alignment_) {
+ // Move state of dynamic frame alignment into edx.
+ __ mov(edx, Immediate(kNoAlignmentPadding));
+
+ Label do_not_pad, align_loop;
+ STATIC_ASSERT(kDoubleSize == 2 * kPointerSize);
+ // Align esp + 4 to a multiple of 2 * kPointerSize.
+ __ test(esp, Immediate(kPointerSize));
+ __ j(not_zero, &do_not_pad, Label::kNear);
+ __ push(Immediate(0));
+ __ mov(ebx, esp);
+ __ mov(edx, Immediate(kAlignmentPaddingPushed));
+ // 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(kAlignmentZapValue));
+ __ bind(&do_not_pad);
+ }
+ }
+
+ info()->set_prologue_offset(masm_->pc_offset());
+ if (NeedsEagerFrame()) {
+ ASSERT(!frame_is_built_);
+ frame_is_built_ = true;
+ __ push(ebp); // Caller's frame pointer.
+ __ mov(ebp, esp);
+ info()->AddNoFrameRange(0, masm_->pc_offset());
+ __ push(esi); // Callee's context.
+ if (info()->IsStub()) {
+ __ push(Immediate(Smi::FromInt(StackFrame::STUB)));
+ } else {
+ __ push(edi); // Callee's JS function.
+ }
+ }
+
+ if (info()->IsOptimizing() &&
+ dynamic_frame_alignment_ &&
+ FLAG_debug_code) {
+ __ test(esp, Immediate(kPointerSize));
+ __ Assert(zero, kFrameIsExpectedToBeAligned);
+ }
+
+ // Reserve space for the stack slots needed by the code.
+ int slots = GetStackSlotCount();
+ ASSERT(slots != 0 || !info()->IsOptimizing());
+ if (slots > 0) {
+ if (slots == 1) {
+ if (dynamic_frame_alignment_) {
+ __ push(edx);
+ } else {
+ __ push(Immediate(kNoAlignmentPadding));
+ }
+ } else {
+ if (FLAG_debug_code) {
+ __ sub(Operand(esp), Immediate(slots * kPointerSize));
+#ifdef _MSC_VER
+ MakeSureStackPagesMapped(slots * kPointerSize);
+#endif
+ __ push(eax);
+ __ mov(Operand(eax), Immediate(slots));
+ Label loop;
+ __ bind(&loop);
+ __ mov(MemOperand(esp, eax, times_4, 0),
+ Immediate(kSlotsZapValue));
+ __ dec(eax);
+ __ j(not_zero, &loop);
+ __ pop(eax);
+ } else {
+ __ sub(Operand(esp), Immediate(slots * kPointerSize));
+#ifdef _MSC_VER
+ MakeSureStackPagesMapped(slots * kPointerSize);
+#endif
+ }
+
+ if (support_aligned_spilled_doubles_) {
+ Comment(";;; Store dynamic frame alignment tag for spilled doubles");
+ // Store dynamic frame alignment state in the first local.
+ int offset = JavaScriptFrameConstants::kDynamicAlignmentStateOffset;
+ if (dynamic_frame_alignment_) {
+ __ mov(Operand(ebp, offset), edx);
+ } else {
+ __ mov(Operand(ebp, offset), Immediate(kNoAlignmentPadding));
+ }
+ }
+ }
+
+ if (info()->saves_caller_doubles() && CpuFeatures::IsSupported(SSE2)) {
+ Comment(";;; Save clobbered callee double registers");
+ CpuFeatureScope scope(masm(), SSE2);
+ int count = 0;
+ BitVector* doubles = chunk()->allocated_double_registers();
+ BitVector::Iterator save_iterator(doubles);
+ while (!save_iterator.Done()) {
+ __ movdbl(MemOperand(esp, count * kDoubleSize),
+ XMMRegister::FromAllocationIndex(save_iterator.Current()));
+ save_iterator.Advance();
+ count++;
+ }
+ }
+ }
+
+ // Possibly allocate a local context.
+ int heap_slots = info_->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ if (heap_slots > 0) {
+ Comment(";;; Allocate local context");
+ // Argument to NewContext is the function, which is still in edi.
+ __ push(edi);
+ if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kNewFunctionContext, 1);
+ }
+ RecordSafepoint(Safepoint::kNoLazyDeopt);
+ // Context is returned in both eax and esi. It replaces the context
+ // passed to us. It's saved in the stack and kept live in esi.
+ __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
+
+ // Copy parameters into context if necessary.
+ 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.
+ __ mov(eax, Operand(ebp, parameter_offset));
+ // Store it in the context.
+ int context_offset = Context::SlotOffset(var->index());
+ __ mov(Operand(esi, context_offset), eax);
+ // Update the write barrier. This clobbers eax and ebx.
+ __ RecordWriteContextSlot(esi,
+ context_offset,
+ eax,
+ ebx,
+ kDontSaveFPRegs);
+ }
+ }
+ Comment(";;; End allocate local context");
+ }
+
+ // Trace the call.
+ if (FLAG_trace && info()->IsOptimizing()) {
+ // We have not executed any compiled code yet, so esi still holds the
+ // incoming context.
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+ 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_);
+
+ // Don't emit code for basic blocks with a replacement.
+ if (instr->IsLabel()) {
+ emit_instructions = !LLabel::cast(instr)->HasReplacement();
+ }
+ if (!emit_instructions) continue;
+
+ if (FLAG_code_comments && instr->HasInterestingComment(this)) {
+ Comment(";;; <@%d,#%d> %s",
+ current_instruction_,
+ instr->hydrogen_value()->id(),
+ instr->Mnemonic());
+ }
+
+ if (!CpuFeatures::IsSupported(SSE2)) FlushX87StackIfNecessary(instr);
+
+ RecordAndUpdatePosition(instr->position());
+
+ instr->CompileToNative(this);
+
+ if (!CpuFeatures::IsSupported(SSE2)) {
+ if (FLAG_debug_code && FLAG_enable_slow_asserts) {
+ __ VerifyX87StackDepth(x87_stack_depth_);
+ }
+ }
+ }
+ EnsureSpaceForLazyDeopt();
+ return !is_aborted();
+}
+
+
+bool LCodeGen::GenerateJumpTable() {
+ Label needs_frame;
+ if (jump_table_.length() > 0) {
+ Comment(";;; -------------------- Jump table --------------------");
+ }
+ for (int i = 0; i < jump_table_.length(); i++) {
+ __ bind(&jump_table_[i].label);
+ Address entry = jump_table_[i].address;
+ Deoptimizer::BailoutType type = jump_table_[i].bailout_type;
+ int id = Deoptimizer::GetDeoptimizationId(isolate(), entry, type);
+ if (id == Deoptimizer::kNotDeoptimizationEntry) {
+ Comment(";;; jump table entry %d.", i);
+ } else {
+ Comment(";;; jump table entry %d: deoptimization bailout %d.", i, id);
+ }
+ if (jump_table_[i].needs_frame) {
+ __ push(Immediate(ExternalReference::ForDeoptEntry(entry)));
+ if (needs_frame.is_bound()) {
+ __ jmp(&needs_frame);
+ } else {
+ __ bind(&needs_frame);
+ __ push(MemOperand(ebp, StandardFrameConstants::kContextOffset));
+ // This variant of deopt can only be used with stubs. Since we don't
+ // have a function pointer to install in the stack frame that we're
+ // building, install a special marker there instead.
+ ASSERT(info()->IsStub());
+ __ push(Immediate(Smi::FromInt(StackFrame::STUB)));
+ // Push a PC inside the function so that the deopt code can find where
+ // the deopt comes from. It doesn't have to be the precise return
+ // address of a "calling" LAZY deopt, it only has to be somewhere
+ // inside the code body.
+ Label push_approx_pc;
+ __ call(&push_approx_pc);
+ __ bind(&push_approx_pc);
+ // Push the continuation which was stashed were the ebp should
+ // be. Replace it with the saved ebp.
+ __ push(MemOperand(esp, 3 * kPointerSize));
+ __ mov(MemOperand(esp, 4 * kPointerSize), ebp);
+ __ lea(ebp, MemOperand(esp, 4 * kPointerSize));
+ __ ret(0); // Call the continuation without clobbering registers.
+ }
+ } else {
+ __ call(entry, RelocInfo::RUNTIME_ENTRY);
+ }
+ }
+ 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];
+
+ int pos = instructions_->at(code->instruction_index())->position();
+ RecordAndUpdatePosition(pos);
+
+ Comment(";;; <@%d,#%d> "
+ "-------------------- Deferred %s --------------------",
+ code->instruction_index(),
+ code->instr()->hydrogen_value()->id(),
+ code->instr()->Mnemonic());
+ __ bind(code->entry());
+ if (NeedsDeferredFrame()) {
+ Comment(";;; Build frame");
+ ASSERT(!frame_is_built_);
+ ASSERT(info()->IsStub());
+ frame_is_built_ = true;
+ // Build the frame in such a way that esi isn't trashed.
+ __ push(ebp); // Caller's frame pointer.
+ __ push(Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ push(Immediate(Smi::FromInt(StackFrame::STUB)));
+ __ lea(ebp, Operand(esp, 2 * kPointerSize));
+ Comment(";;; Deferred code");
+ }
+ code->Generate();
+ if (NeedsDeferredFrame()) {
+ Comment(";;; Destroy frame");
+ ASSERT(frame_is_built_);
+ frame_is_built_ = false;
+ __ mov(esp, ebp);
+ __ pop(ebp);
+ }
+ __ 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::GenerateSafepointTable() {
+ ASSERT(is_done());
+ if (!info()->IsStub()) {
+ // For lazy deoptimization we need space to patch a call after every call.
+ // Ensure there is always space for such patching, even if the code ends
+ // in a call.
+ int target_offset = masm()->pc_offset() + Deoptimizer::patch_size();
+ while (masm()->pc_offset() < target_offset) {
+ masm()->nop();
+ }
+ }
+ safepoints_.Emit(masm(), GetStackSlotCount());
+ return !is_aborted();
+}
+
+
+Register LCodeGen::ToRegister(int index) const {
+ return Register::FromAllocationIndex(index);
+}
+
+
+X87Register LCodeGen::ToX87Register(int index) const {
+ return X87Register::FromAllocationIndex(index);
+}
+
+
+XMMRegister LCodeGen::ToDoubleRegister(int index) const {
+ return XMMRegister::FromAllocationIndex(index);
+}
+
+
+void LCodeGen::X87LoadForUsage(X87Register reg) {
+ ASSERT(X87StackContains(reg));
+ X87Fxch(reg);
+ x87_stack_depth_--;
+}
+
+
+void LCodeGen::X87Fxch(X87Register reg, int other_slot) {
+ ASSERT(X87StackContains(reg) && x87_stack_depth_ > other_slot);
+ int i = X87ArrayIndex(reg);
+ int st = x87_st2idx(i);
+ if (st != other_slot) {
+ int other_i = x87_st2idx(other_slot);
+ X87Register other = x87_stack_[other_i];
+ x87_stack_[other_i] = reg;
+ x87_stack_[i] = other;
+ if (st == 0) {
+ __ fxch(other_slot);
+ } else if (other_slot == 0) {
+ __ fxch(st);
+ } else {
+ __ fxch(st);
+ __ fxch(other_slot);
+ __ fxch(st);
+ }
+ }
+}
+
+
+int LCodeGen::x87_st2idx(int pos) {
+ return x87_stack_depth_ - pos - 1;
+}
+
+
+int LCodeGen::X87ArrayIndex(X87Register reg) {
+ for (int i = 0; i < x87_stack_depth_; i++) {
+ if (x87_stack_[i].is(reg)) return i;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+bool LCodeGen::X87StackContains(X87Register reg) {
+ for (int i = 0; i < x87_stack_depth_; i++) {
+ if (x87_stack_[i].is(reg)) return true;
+ }
+ return false;
+}
+
+
+void LCodeGen::X87Free(X87Register reg) {
+ ASSERT(X87StackContains(reg));
+ int i = X87ArrayIndex(reg);
+ int st = x87_st2idx(i);
+ if (st > 0) {
+ // keep track of how fstp(i) changes the order of elements
+ int tos_i = x87_st2idx(0);
+ x87_stack_[i] = x87_stack_[tos_i];
+ }
+ x87_stack_depth_--;
+ __ fstp(st);
+}
+
+
+void LCodeGen::X87Mov(X87Register dst, Operand src, X87OperandType opts) {
+ if (X87StackContains(dst)) {
+ X87Fxch(dst);
+ __ fstp(0);
+ } else {
+ ASSERT(x87_stack_depth_ < X87Register::kNumAllocatableRegisters);
+ x87_stack_[x87_stack_depth_] = dst;
+ x87_stack_depth_++;
+ }
+ X87Fld(src, opts);
+}
+
+
+void LCodeGen::X87Fld(Operand src, X87OperandType opts) {
+ if (opts == kX87DoubleOperand) {
+ __ fld_d(src);
+ } else if (opts == kX87FloatOperand) {
+ __ fld_s(src);
+ } else if (opts == kX87IntOperand) {
+ __ fild_s(src);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::X87Mov(Operand dst, X87Register src) {
+ X87Fxch(src);
+ __ fst_d(dst);
+}
+
+
+void LCodeGen::X87PrepareToWrite(X87Register reg) {
+ if (X87StackContains(reg)) {
+ X87Free(reg);
+ }
+ // Mark this register as the next register to write to
+ x87_stack_[x87_stack_depth_] = reg;
+}
+
+
+void LCodeGen::X87CommitWrite(X87Register reg) {
+ // Assert the reg is prepared to write, but not on the virtual stack yet
+ ASSERT(!X87StackContains(reg) && x87_stack_[x87_stack_depth_].is(reg) &&
+ x87_stack_depth_ < X87Register::kNumAllocatableRegisters);
+ x87_stack_depth_++;
+}
+
+
+void LCodeGen::X87PrepareBinaryOp(
+ X87Register left, X87Register right, X87Register result) {
+ // You need to use DefineSameAsFirst for x87 instructions
+ ASSERT(result.is(left));
+ X87Fxch(right, 1);
+ X87Fxch(left);
+}
+
+
+void LCodeGen::FlushX87StackIfNecessary(LInstruction* instr) {
+ if (x87_stack_depth_ > 0 && instr->ClobbersDoubleRegisters()) {
+ bool double_inputs = instr->HasDoubleRegisterInput();
+
+ // Flush stack from tos down, since FreeX87() will mess with tos
+ for (int i = x87_stack_depth_-1; i >= 0; i--) {
+ X87Register reg = x87_stack_[i];
+ // Skip registers which contain the inputs for the next instruction
+ // when flushing the stack
+ if (double_inputs && instr->IsDoubleInput(reg, this)) {
+ continue;
+ }
+ X87Free(reg);
+ if (i < x87_stack_depth_-1) i++;
+ }
+ }
+ if (instr->IsReturn()) {
+ while (x87_stack_depth_ > 0) {
+ __ fstp(0);
+ x87_stack_depth_--;
+ }
+ }
+}
+
+
+void LCodeGen::EmitFlushX87ForDeopt() {
+ for (int i = 0; i < x87_stack_depth_; i++) __ fstp(0);
+}
+
+
+Register LCodeGen::ToRegister(LOperand* op) const {
+ ASSERT(op->IsRegister());
+ return ToRegister(op->index());
+}
+
+
+X87Register LCodeGen::ToX87Register(LOperand* op) const {
+ ASSERT(op->IsDoubleRegister());
+ return ToX87Register(op->index());
+}
+
+
+XMMRegister LCodeGen::ToDoubleRegister(LOperand* op) const {
+ ASSERT(op->IsDoubleRegister());
+ return ToDoubleRegister(op->index());
+}
+
+
+int32_t LCodeGen::ToInteger32(LConstantOperand* op) const {
+ return ToRepresentation(op, Representation::Integer32());
+}
+
+
+int32_t LCodeGen::ToRepresentation(LConstantOperand* op,
+ const Representation& r) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ int32_t value = constant->Integer32Value();
+ if (r.IsInteger32()) return value;
+ ASSERT(r.IsSmiOrTagged());
+ return reinterpret_cast<int32_t>(Smi::FromInt(value));
+}
+
+
+Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(chunk_->LookupLiteralRepresentation(op).IsSmiOrTagged());
+ return constant->handle();
+}
+
+
+double LCodeGen::ToDouble(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(constant->HasDoubleValue());
+ return constant->DoubleValue();
+}
+
+
+ExternalReference LCodeGen::ToExternalReference(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(constant->HasExternalReferenceValue());
+ return constant->ExternalReferenceValue();
+}
+
+
+bool LCodeGen::IsInteger32(LConstantOperand* op) const {
+ return chunk_->LookupLiteralRepresentation(op).IsSmiOrInteger32();
+}
+
+
+bool LCodeGen::IsSmi(LConstantOperand* op) const {
+ return chunk_->LookupLiteralRepresentation(op).IsSmi();
+}
+
+
+Operand LCodeGen::ToOperand(LOperand* op) const {
+ if (op->IsRegister()) return Operand(ToRegister(op));
+ if (op->IsDoubleRegister()) return Operand(ToDoubleRegister(op));
+ ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot());
+ return Operand(ebp, StackSlotOffset(op->index()));
+}
+
+
+Operand LCodeGen::HighOperand(LOperand* op) {
+ ASSERT(op->IsDoubleStackSlot());
+ return Operand(ebp, StackSlotOffset(op->index()) + 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->translation_size();
+ // The output frame height does not include the parameters.
+ int height = translation_size - environment->parameter_count();
+
+ WriteTranslation(environment->outer(), translation);
+ bool has_closure_id = !info()->closure().is_null() &&
+ !info()->closure().is_identical_to(environment->closure());
+ int closure_id = has_closure_id
+ ? DefineDeoptimizationLiteral(environment->closure())
+ : Translation::kSelfLiteralId;
+ switch (environment->frame_type()) {
+ case JS_FUNCTION:
+ translation->BeginJSFrame(environment->ast_id(), closure_id, height);
+ break;
+ case JS_CONSTRUCT:
+ translation->BeginConstructStubFrame(closure_id, translation_size);
+ break;
+ case JS_GETTER:
+ ASSERT(translation_size == 1);
+ ASSERT(height == 0);
+ translation->BeginGetterStubFrame(closure_id);
+ break;
+ case JS_SETTER:
+ ASSERT(translation_size == 2);
+ ASSERT(height == 0);
+ translation->BeginSetterStubFrame(closure_id);
+ break;
+ case ARGUMENTS_ADAPTOR:
+ translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
+ break;
+ case STUB:
+ translation->BeginCompiledStubFrame();
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ int object_index = 0;
+ int dematerialized_index = 0;
+ for (int i = 0; i < translation_size; ++i) {
+ LOperand* value = environment->values()->at(i);
+ AddToTranslation(environment,
+ translation,
+ value,
+ environment->HasTaggedValueAt(i),
+ environment->HasUint32ValueAt(i),
+ &object_index,
+ &dematerialized_index);
+ }
+}
+
+
+void LCodeGen::AddToTranslation(LEnvironment* environment,
+ Translation* translation,
+ LOperand* op,
+ bool is_tagged,
+ bool is_uint32,
+ int* object_index_pointer,
+ int* dematerialized_index_pointer) {
+ if (op == LEnvironment::materialization_marker()) {
+ int object_index = (*object_index_pointer)++;
+ if (environment->ObjectIsDuplicateAt(object_index)) {
+ int dupe_of = environment->ObjectDuplicateOfAt(object_index);
+ translation->DuplicateObject(dupe_of);
+ return;
+ }
+ int object_length = environment->ObjectLengthAt(object_index);
+ if (environment->ObjectIsArgumentsAt(object_index)) {
+ translation->BeginArgumentsObject(object_length);
+ } else {
+ translation->BeginCapturedObject(object_length);
+ }
+ int dematerialized_index = *dematerialized_index_pointer;
+ int env_offset = environment->translation_size() + dematerialized_index;
+ *dematerialized_index_pointer += object_length;
+ for (int i = 0; i < object_length; ++i) {
+ LOperand* value = environment->values()->at(env_offset + i);
+ AddToTranslation(environment,
+ translation,
+ value,
+ environment->HasTaggedValueAt(env_offset + i),
+ environment->HasUint32ValueAt(env_offset + i),
+ object_index_pointer,
+ dematerialized_index_pointer);
+ }
+ return;
+ }
+
+ if (op->IsStackSlot()) {
+ if (is_tagged) {
+ translation->StoreStackSlot(op->index());
+ } else if (is_uint32) {
+ translation->StoreUint32StackSlot(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 if (is_uint32) {
+ translation->StoreUint32Register(reg);
+ } else {
+ translation->StoreInt32Register(reg);
+ }
+ } else if (op->IsDoubleRegister()) {
+ XMMRegister reg = ToDoubleRegister(op);
+ translation->StoreDoubleRegister(reg);
+ } else if (op->IsConstantOperand()) {
+ HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op));
+ int src_index = DefineDeoptimizationLiteral(constant->handle());
+ translation->StoreLiteral(src_index);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+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);
+
+ // Signal that we don't inline smi code before these stubs in the
+ // optimizing code generator.
+ if (code->kind() == Code::BINARY_OP_IC ||
+ code->kind() == Code::COMPARE_IC) {
+ __ nop();
+ }
+}
+
+
+void LCodeGen::CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr) {
+ CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT);
+}
+
+
+void LCodeGen::CallRuntime(const Runtime::Function* fun,
+ int argc,
+ LInstruction* instr) {
+ ASSERT(instr != NULL);
+ ASSERT(instr->HasPointerMap());
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+
+ __ CallRuntime(fun, argc);
+
+ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT);
+
+ ASSERT(info()->is_calling());
+}
+
+
+void LCodeGen::LoadContextFromDeferred(LOperand* context) {
+ if (context->IsRegister()) {
+ if (!ToRegister(context).is(esi)) {
+ __ mov(esi, ToRegister(context));
+ }
+ } else if (context->IsStackSlot()) {
+ __ mov(esi, ToOperand(context));
+ } else if (context->IsConstantOperand()) {
+ HConstant* constant =
+ chunk_->LookupConstant(LConstantOperand::cast(context));
+ __ LoadObject(esi, Handle<Object>::cast(constant->handle()));
+ } else {
+ UNREACHABLE();
+ }
+}
+
+void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr,
+ LOperand* context) {
+ LoadContextFromDeferred(context);
+
+ __ CallRuntimeSaveDoubles(id);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), argc, Safepoint::kNoLazyDeopt);
+
+ ASSERT(info()->is_calling());
+}
+
+
+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->frame_type() == JS_FUNCTION) {
+ ++jsframe_count;
+ }
+ }
+ Translation translation(&translations_, frame_count, jsframe_count, zone());
+ 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, zone());
+ }
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition cc,
+ LEnvironment* environment,
+ Deoptimizer::BailoutType bailout_type) {
+ RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt);
+ ASSERT(environment->HasBeenRegistered());
+ int id = environment->deoptimization_index();
+ ASSERT(info()->IsOptimizing() || info()->IsStub());
+ Address entry =
+ Deoptimizer::GetDeoptimizationEntry(isolate(), id, bailout_type);
+ if (entry == NULL) {
+ Abort(kBailoutWasNotPrepared);
+ return;
+ }
+
+ if (FLAG_deopt_every_n_times != 0 && !info()->IsStub()) {
+ ExternalReference count = ExternalReference::stress_deopt_count(isolate());
+ Label no_deopt;
+ __ pushfd();
+ __ push(eax);
+ __ mov(eax, Operand::StaticVariable(count));
+ __ sub(eax, Immediate(1));
+ __ j(not_zero, &no_deopt, Label::kNear);
+ if (FLAG_trap_on_deopt) __ int3();
+ __ mov(eax, Immediate(FLAG_deopt_every_n_times));
+ __ mov(Operand::StaticVariable(count), eax);
+ __ pop(eax);
+ __ popfd();
+ ASSERT(frame_is_built_);
+ __ call(entry, RelocInfo::RUNTIME_ENTRY);
+ __ bind(&no_deopt);
+ __ mov(Operand::StaticVariable(count), eax);
+ __ pop(eax);
+ __ popfd();
+ }
+
+ // Before Instructions which can deopt, we normally flush the x87 stack. But
+ // we can have inputs or outputs of the current instruction on the stack,
+ // thus we need to flush them here from the physical stack to leave it in a
+ // consistent state.
+ if (x87_stack_depth_ > 0) {
+ Label done;
+ if (cc != no_condition) __ j(NegateCondition(cc), &done, Label::kNear);
+ EmitFlushX87ForDeopt();
+ __ bind(&done);
+ }
+
+ if (info()->ShouldTrapOnDeopt()) {
+ Label done;
+ if (cc != no_condition) __ j(NegateCondition(cc), &done, Label::kNear);
+ __ int3();
+ __ bind(&done);
+ }
+
+ ASSERT(info()->IsStub() || frame_is_built_);
+ if (cc == no_condition && frame_is_built_) {
+ __ call(entry, RelocInfo::RUNTIME_ENTRY);
+ } else {
+ // We often have several deopts to the same entry, reuse the last
+ // jump entry if this is the case.
+ if (jump_table_.is_empty() ||
+ jump_table_.last().address != entry ||
+ jump_table_.last().needs_frame != !frame_is_built_ ||
+ jump_table_.last().bailout_type != bailout_type) {
+ Deoptimizer::JumpTableEntry table_entry(entry,
+ bailout_type,
+ !frame_is_built_);
+ jump_table_.Add(table_entry, zone());
+ }
+ if (cc == no_condition) {
+ __ jmp(&jump_table_.last().label);
+ } else {
+ __ j(cc, &jump_table_.last().label);
+ }
+ }
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition cc,
+ LEnvironment* environment) {
+ Deoptimizer::BailoutType bailout_type = info()->IsStub()
+ ? Deoptimizer::LAZY
+ : Deoptimizer::EAGER;
+ DeoptimizeIf(cc, environment, bailout_type);
+}
+
+
+void LCodeGen::RegisterDependentCodeForEmbeddedMaps(Handle<Code> code) {
+ ZoneList<Handle<Map> > maps(1, zone());
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) {
+ RelocInfo::Mode mode = it.rinfo()->rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT &&
+ it.rinfo()->target_object()->IsMap()) {
+ Handle<Map> map(Map::cast(it.rinfo()->target_object()));
+ if (map->CanTransition()) {
+ maps.Add(map, zone());
+ }
+ }
+ }
+#ifdef VERIFY_HEAP
+ // This disables verification of weak embedded maps after full GC.
+ // AddDependentCode can cause a GC, which would observe the state where
+ // this code is not yet in the depended code lists of the embedded maps.
+ NoWeakEmbeddedMapsVerificationScope disable_verification_of_embedded_maps;
+#endif
+ for (int i = 0; i < maps.length(); i++) {
+ maps.at(i)->AddDependentCode(DependentCode::kWeaklyEmbeddedGroup, code);
+ }
+}
+
+
+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(isolate()->factory());
+ data->SetTranslationByteArray(*translations);
+ data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_));
+
+ Handle<FixedArray> literals =
+ factory()->NewFixedArray(deoptimization_literals_.length(), TENURED);
+ { AllowDeferredHandleDereference copy_handles;
+ 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().ToInt()));
+ 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, 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, zone());
+ 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(kind == expected_safepoint_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(), zone());
+ } else if (pointer->IsRegister() && (kind & Safepoint::kWithRegisters)) {
+ safepoint.DefinePointerRegister(ToRegister(pointer), zone());
+ }
+ }
+}
+
+
+void LCodeGen::RecordSafepoint(LPointerMap* pointers,
+ Safepoint::DeoptMode mode) {
+ RecordSafepoint(pointers, Safepoint::kSimple, 0, mode);
+}
+
+
+void LCodeGen::RecordSafepoint(Safepoint::DeoptMode mode) {
+ LPointerMap empty_pointers(RelocInfo::kNoPosition, zone());
+ RecordSafepoint(&empty_pointers, mode);
+}
+
+
+void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers,
+ int arguments,
+ Safepoint::DeoptMode mode) {
+ RecordSafepoint(pointers, Safepoint::kWithRegisters, arguments, mode);
+}
+
+
+void LCodeGen::RecordPosition(int position) {
+ if (position == RelocInfo::kNoPosition) return;
+ masm()->positions_recorder()->RecordPosition(position);
+}
+
+
+void LCodeGen::RecordAndUpdatePosition(int position) {
+ if (position >= 0 && position != old_position_) {
+ masm()->positions_recorder()->RecordPosition(position);
+ old_position_ = position;
+ }
+}
+
+
+static const char* LabelType(LLabel* label) {
+ if (label->is_loop_header()) return " (loop header)";
+ if (label->is_osr_entry()) return " (OSR entry)";
+ return "";
+}
+
+
+void LCodeGen::DoLabel(LLabel* label) {
+ Comment(";;; <@%d,#%d> -------------------- B%d%s --------------------",
+ current_instruction_,
+ label->hydrogen_value()->id(),
+ label->block_id(),
+ LabelType(label));
+ __ 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->context()).is(esi));
+ ASSERT(ToRegister(instr->result()).is(eax));
+ switch (instr->hydrogen()->major_key()) {
+ case CodeStub::RegExpConstructResult: {
+ RegExpConstructResultStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::RegExpExec: {
+ RegExpExecStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::SubString: {
+ SubStringStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::NumberToString: {
+ NumberToStringStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::StringCompare: {
+ StringCompareStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::TranscendentalCache: {
+ TranscendentalCacheStub stub(instr->transcendental_type(),
+ TranscendentalCacheStub::TAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) {
+ // Record the address of the first unknown OSR value as the place to enter.
+ if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset();
+}
+
+
+void LCodeGen::DoModI(LModI* instr) {
+ HMod* hmod = instr->hydrogen();
+ HValue* left = hmod->left();
+ HValue* right = hmod->right();
+ if (hmod->HasPowerOf2Divisor()) {
+ // TODO(svenpanne) We should really do the strength reduction on the
+ // Hydrogen level.
+ Register left_reg = ToRegister(instr->left());
+ ASSERT(left_reg.is(ToRegister(instr->result())));
+
+ // Note: The code below even works when right contains kMinInt.
+ int32_t divisor = Abs(right->GetInteger32Constant());
+
+ Label left_is_not_negative, done;
+ if (left->CanBeNegative()) {
+ __ test(left_reg, Operand(left_reg));
+ __ j(not_sign, &left_is_not_negative, Label::kNear);
+ __ neg(left_reg);
+ __ and_(left_reg, divisor - 1);
+ __ neg(left_reg);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(zero, instr->environment());
+ }
+ __ jmp(&done, Label::kNear);
+ }
+
+ __ bind(&left_is_not_negative);
+ __ and_(left_reg, divisor - 1);
+ __ bind(&done);
+
+ } else if (hmod->fixed_right_arg().has_value) {
+ Register left_reg = ToRegister(instr->left());
+ ASSERT(left_reg.is(ToRegister(instr->result())));
+ Register right_reg = ToRegister(instr->right());
+
+ int32_t divisor = hmod->fixed_right_arg().value;
+ ASSERT(IsPowerOf2(divisor));
+
+ // Check if our assumption of a fixed right operand still holds.
+ __ cmp(right_reg, Immediate(divisor));
+ DeoptimizeIf(not_equal, instr->environment());
+
+ Label left_is_not_negative, done;
+ if (left->CanBeNegative()) {
+ __ test(left_reg, Operand(left_reg));
+ __ j(not_sign, &left_is_not_negative, Label::kNear);
+ __ neg(left_reg);
+ __ and_(left_reg, divisor - 1);
+ __ neg(left_reg);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(zero, instr->environment());
+ }
+ __ jmp(&done, Label::kNear);
+ }
+
+ __ bind(&left_is_not_negative);
+ __ and_(left_reg, divisor - 1);
+ __ bind(&done);
+
+ } else {
+ Register left_reg = ToRegister(instr->left());
+ ASSERT(left_reg.is(eax));
+ Register right_reg = ToRegister(instr->right());
+ ASSERT(!right_reg.is(eax));
+ ASSERT(!right_reg.is(edx));
+ Register result_reg = ToRegister(instr->result());
+ ASSERT(result_reg.is(edx));
+
+ Label done;
+ // Check for x % 0, idiv would signal a divide error. We have to
+ // deopt in this case because we can't return a NaN.
+ if (right->CanBeZero()) {
+ __ test(right_reg, Operand(right_reg));
+ DeoptimizeIf(zero, instr->environment());
+ }
+
+ // Check for kMinInt % -1, idiv would signal a divide error. We
+ // have to deopt if we care about -0, because we can't return that.
+ if (left->RangeCanInclude(kMinInt) && right->RangeCanInclude(-1)) {
+ Label no_overflow_possible;
+ __ cmp(left_reg, kMinInt);
+ __ j(not_equal, &no_overflow_possible, Label::kNear);
+ __ cmp(right_reg, -1);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(equal, instr->environment());
+ } else {
+ __ j(not_equal, &no_overflow_possible, Label::kNear);
+ __ Set(result_reg, Immediate(0));
+ __ jmp(&done, Label::kNear);
+ }
+ __ bind(&no_overflow_possible);
+ }
+
+ // Sign extend dividend in eax into edx:eax.
+ __ cdq();
+
+ // If we care about -0, test if the dividend is <0 and the result is 0.
+ if (left->CanBeNegative() &&
+ hmod->CanBeZero() &&
+ hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label positive_left;
+ __ test(left_reg, Operand(left_reg));
+ __ j(not_sign, &positive_left, Label::kNear);
+ __ idiv(right_reg);
+ __ test(result_reg, Operand(result_reg));
+ DeoptimizeIf(zero, instr->environment());
+ __ jmp(&done, Label::kNear);
+ __ bind(&positive_left);
+ }
+ __ idiv(right_reg);
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoDivI(LDivI* instr) {
+ if (!instr->is_flooring() && instr->hydrogen()->HasPowerOf2Divisor()) {
+ Register dividend = ToRegister(instr->left());
+ int32_t divisor = instr->hydrogen()->right()->GetInteger32Constant();
+ int32_t test_value = 0;
+ int32_t power = 0;
+
+ if (divisor > 0) {
+ test_value = divisor - 1;
+ power = WhichPowerOf2(divisor);
+ } else {
+ // Check for (0 / -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ test(dividend, Operand(dividend));
+ DeoptimizeIf(zero, instr->environment());
+ }
+ // Check for (kMinInt / -1).
+ if (divisor == -1 && instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ __ cmp(dividend, kMinInt);
+ DeoptimizeIf(zero, instr->environment());
+ }
+ test_value = - divisor - 1;
+ power = WhichPowerOf2(-divisor);
+ }
+
+ if (test_value != 0) {
+ if (instr->hydrogen()->CheckFlag(
+ HInstruction::kAllUsesTruncatingToInt32)) {
+ Label done, negative;
+ __ cmp(dividend, 0);
+ __ j(less, &negative, Label::kNear);
+ __ sar(dividend, power);
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&negative);
+ __ neg(dividend);
+ __ sar(dividend, power);
+ if (divisor > 0) __ neg(dividend);
+ __ bind(&done);
+ return; // Don't fall through to "__ neg" below.
+ } else {
+ // Deoptimize if remainder is not 0.
+ __ test(dividend, Immediate(test_value));
+ DeoptimizeIf(not_zero, instr->environment());
+ __ sar(dividend, power);
+ }
+ }
+
+ if (divisor < 0) __ neg(dividend);
+
+ return;
+ }
+
+ LOperand* right = instr->right();
+ ASSERT(ToRegister(instr->result()).is(eax));
+ ASSERT(ToRegister(instr->left()).is(eax));
+ ASSERT(!ToRegister(instr->right()).is(eax));
+ ASSERT(!ToRegister(instr->right()).is(edx));
+
+ Register left_reg = eax;
+
+ // Check for x / 0.
+ Register right_reg = ToRegister(right);
+ if (instr->hydrogen_value()->CheckFlag(HValue::kCanBeDivByZero)) {
+ __ test(right_reg, ToOperand(right));
+ DeoptimizeIf(zero, instr->environment());
+ }
+
+ // Check for (0 / -x) that will produce negative zero.
+ if (instr->hydrogen_value()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label left_not_zero;
+ __ test(left_reg, Operand(left_reg));
+ __ j(not_zero, &left_not_zero, Label::kNear);
+ __ test(right_reg, ToOperand(right));
+ DeoptimizeIf(sign, instr->environment());
+ __ bind(&left_not_zero);
+ }
+
+ // Check for (kMinInt / -1).
+ if (instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow)) {
+ Label left_not_min_int;
+ __ cmp(left_reg, kMinInt);
+ __ j(not_zero, &left_not_min_int, Label::kNear);
+ __ cmp(right_reg, -1);
+ DeoptimizeIf(zero, instr->environment());
+ __ bind(&left_not_min_int);
+ }
+
+ // Sign extend to edx.
+ __ cdq();
+ __ idiv(right_reg);
+
+ if (instr->is_flooring()) {
+ Label done;
+ __ test(edx, edx);
+ __ j(zero, &done, Label::kNear);
+ __ xor_(edx, right_reg);
+ __ sar(edx, 31);
+ __ add(eax, edx);
+ __ bind(&done);
+ } else if (!instr->hydrogen()->CheckFlag(
+ HInstruction::kAllUsesTruncatingToInt32)) {
+ // Deoptimize if remainder is not 0.
+ __ test(edx, Operand(edx));
+ DeoptimizeIf(not_zero, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
+ ASSERT(instr->right()->IsConstantOperand());
+
+ Register dividend = ToRegister(instr->left());
+ int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right()));
+ Register result = ToRegister(instr->result());
+
+ switch (divisor) {
+ case 0:
+ DeoptimizeIf(no_condition, instr->environment());
+ return;
+
+ case 1:
+ __ Move(result, dividend);
+ return;
+
+ case -1:
+ __ Move(result, dividend);
+ __ neg(result);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(zero, instr->environment());
+ }
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+ return;
+ }
+
+ uint32_t divisor_abs = abs(divisor);
+ if (IsPowerOf2(divisor_abs)) {
+ int32_t power = WhichPowerOf2(divisor_abs);
+ if (divisor < 0) {
+ // Input[dividend] is clobbered.
+ // The sequence is tedious because neg(dividend) might overflow.
+ __ mov(result, dividend);
+ __ sar(dividend, 31);
+ __ neg(result);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(zero, instr->environment());
+ }
+ __ shl(dividend, 32 - power);
+ __ sar(result, power);
+ __ not_(dividend);
+ // Clear result.sign if dividend.sign is set.
+ __ and_(result, dividend);
+ } else {
+ __ Move(result, dividend);
+ __ sar(result, power);
+ }
+ } else {
+ ASSERT(ToRegister(instr->left()).is(eax));
+ ASSERT(ToRegister(instr->result()).is(edx));
+ Register scratch = ToRegister(instr->temp());
+
+ // Find b which: 2^b < divisor_abs < 2^(b+1).
+ unsigned b = 31 - CompilerIntrinsics::CountLeadingZeros(divisor_abs);
+ unsigned shift = 32 + b; // Precision +1bit (effectively).
+ double multiplier_f =
+ static_cast<double>(static_cast<uint64_t>(1) << shift) / divisor_abs;
+ int64_t multiplier;
+ if (multiplier_f - floor(multiplier_f) < 0.5) {
+ multiplier = static_cast<int64_t>(floor(multiplier_f));
+ } else {
+ multiplier = static_cast<int64_t>(floor(multiplier_f)) + 1;
+ }
+ // The multiplier is a uint32.
+ ASSERT(multiplier > 0 &&
+ multiplier < (static_cast<int64_t>(1) << 32));
+ __ mov(scratch, dividend);
+ if (divisor < 0 &&
+ instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ test(dividend, dividend);
+ DeoptimizeIf(zero, instr->environment());
+ }
+ __ mov(edx, static_cast<int32_t>(multiplier));
+ __ imul(edx);
+ if (static_cast<int32_t>(multiplier) < 0) {
+ __ add(edx, scratch);
+ }
+ Register reg_lo = eax;
+ Register reg_byte_scratch = scratch;
+ if (!reg_byte_scratch.is_byte_register()) {
+ __ xchg(reg_lo, reg_byte_scratch);
+ reg_lo = scratch;
+ reg_byte_scratch = eax;
+ }
+ if (divisor < 0) {
+ __ xor_(reg_byte_scratch, reg_byte_scratch);
+ __ cmp(reg_lo, 0x40000000);
+ __ setcc(above, reg_byte_scratch);
+ __ neg(edx);
+ __ sub(edx, reg_byte_scratch);
+ } else {
+ __ xor_(reg_byte_scratch, reg_byte_scratch);
+ __ cmp(reg_lo, 0xC0000000);
+ __ setcc(above_equal, reg_byte_scratch);
+ __ add(edx, reg_byte_scratch);
+ }
+ __ sar(edx, shift - 32);
+ }
+}
+
+
+void LCodeGen::DoMulI(LMulI* instr) {
+ Register left = ToRegister(instr->left());
+ LOperand* right = instr->right();
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ mov(ToRegister(instr->temp()), left);
+ }
+
+ if (right->IsConstantOperand()) {
+ // Try strength reductions on the multiplication.
+ // All replacement instructions are at most as long as the imul
+ // and have better latency.
+ int constant = ToInteger32(LConstantOperand::cast(right));
+ if (constant == -1) {
+ __ neg(left);
+ } else if (constant == 0) {
+ __ xor_(left, Operand(left));
+ } else if (constant == 2) {
+ __ add(left, Operand(left));
+ } else if (!instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ // If we know that the multiplication can't overflow, it's safe to
+ // use instructions that don't set the overflow flag for the
+ // multiplication.
+ switch (constant) {
+ case 1:
+ // Do nothing.
+ break;
+ case 3:
+ __ lea(left, Operand(left, left, times_2, 0));
+ break;
+ case 4:
+ __ shl(left, 2);
+ break;
+ case 5:
+ __ lea(left, Operand(left, left, times_4, 0));
+ break;
+ case 8:
+ __ shl(left, 3);
+ break;
+ case 9:
+ __ lea(left, Operand(left, left, times_8, 0));
+ break;
+ case 16:
+ __ shl(left, 4);
+ break;
+ default:
+ __ imul(left, left, constant);
+ break;
+ }
+ } else {
+ __ imul(left, left, constant);
+ }
+ } else {
+ if (instr->hydrogen()->representation().IsSmi()) {
+ __ SmiUntag(left);
+ }
+ __ imul(left, ToOperand(right));
+ }
+
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Bail out if the result is supposed to be negative zero.
+ Label done;
+ __ test(left, Operand(left));
+ __ j(not_zero, &done, Label::kNear);
+ if (right->IsConstantOperand()) {
+ if (ToInteger32(LConstantOperand::cast(right)) < 0) {
+ DeoptimizeIf(no_condition, instr->environment());
+ } else if (ToInteger32(LConstantOperand::cast(right)) == 0) {
+ __ cmp(ToRegister(instr->temp()), Immediate(0));
+ DeoptimizeIf(less, instr->environment());
+ }
+ } else {
+ // Test the non-zero operand for negative sign.
+ __ or_(ToRegister(instr->temp()), ToOperand(right));
+ DeoptimizeIf(sign, instr->environment());
+ }
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoBitI(LBitI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ ASSERT(left->Equals(instr->result()));
+ ASSERT(left->IsRegister());
+
+ if (right->IsConstantOperand()) {
+ int32_t right_operand =
+ ToRepresentation(LConstantOperand::cast(right),
+ instr->hydrogen()->representation());
+ switch (instr->op()) {
+ case Token::BIT_AND:
+ __ and_(ToRegister(left), right_operand);
+ break;
+ case Token::BIT_OR:
+ __ or_(ToRegister(left), right_operand);
+ break;
+ case Token::BIT_XOR:
+ if (right_operand == int32_t(~0)) {
+ __ not_(ToRegister(left));
+ } else {
+ __ xor_(ToRegister(left), right_operand);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } else {
+ switch (instr->op()) {
+ case Token::BIT_AND:
+ __ and_(ToRegister(left), ToOperand(right));
+ break;
+ case Token::BIT_OR:
+ __ or_(ToRegister(left), ToOperand(right));
+ break;
+ case Token::BIT_XOR:
+ __ xor_(ToRegister(left), ToOperand(right));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoShiftI(LShiftI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ ASSERT(left->Equals(instr->result()));
+ ASSERT(left->IsRegister());
+ if (right->IsRegister()) {
+ ASSERT(ToRegister(right).is(ecx));
+
+ switch (instr->op()) {
+ case Token::ROR:
+ __ ror_cl(ToRegister(left));
+ if (instr->can_deopt()) {
+ __ test(ToRegister(left), Immediate(0x80000000));
+ DeoptimizeIf(not_zero, instr->environment());
+ }
+ break;
+ case Token::SAR:
+ __ sar_cl(ToRegister(left));
+ break;
+ case Token::SHR:
+ __ shr_cl(ToRegister(left));
+ if (instr->can_deopt()) {
+ __ test(ToRegister(left), Immediate(0x80000000));
+ DeoptimizeIf(not_zero, instr->environment());
+ }
+ break;
+ case Token::SHL:
+ __ shl_cl(ToRegister(left));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } else {
+ int value = ToInteger32(LConstantOperand::cast(right));
+ uint8_t shift_count = static_cast<uint8_t>(value & 0x1F);
+ switch (instr->op()) {
+ case Token::ROR:
+ if (shift_count == 0 && instr->can_deopt()) {
+ __ test(ToRegister(left), Immediate(0x80000000));
+ DeoptimizeIf(not_zero, instr->environment());
+ } else {
+ __ ror(ToRegister(left), shift_count);
+ }
+ break;
+ case Token::SAR:
+ if (shift_count != 0) {
+ __ sar(ToRegister(left), shift_count);
+ }
+ break;
+ case Token::SHR:
+ if (shift_count == 0 && instr->can_deopt()) {
+ __ test(ToRegister(left), Immediate(0x80000000));
+ DeoptimizeIf(not_zero, instr->environment());
+ } else {
+ __ shr(ToRegister(left), shift_count);
+ }
+ break;
+ case Token::SHL:
+ if (shift_count != 0) {
+ if (instr->hydrogen_value()->representation().IsSmi() &&
+ instr->can_deopt()) {
+ if (shift_count != 1) {
+ __ shl(ToRegister(left), shift_count - 1);
+ }
+ __ SmiTag(ToRegister(left));
+ DeoptimizeIf(overflow, instr->environment());
+ } else {
+ __ shl(ToRegister(left), shift_count);
+ }
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoSubI(LSubI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ ASSERT(left->Equals(instr->result()));
+
+ if (right->IsConstantOperand()) {
+ __ sub(ToOperand(left),
+ ToImmediate(right, instr->hydrogen()->representation()));
+ } else {
+ __ sub(ToRegister(left), ToOperand(right));
+ }
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoConstantI(LConstantI* instr) {
+ __ Set(ToRegister(instr->result()), Immediate(instr->value()));
+}
+
+
+void LCodeGen::DoConstantS(LConstantS* instr) {
+ __ Set(ToRegister(instr->result()), Immediate(instr->value()));
+}
+
+
+void LCodeGen::DoConstantD(LConstantD* instr) {
+ double v = instr->value();
+ uint64_t int_val = BitCast<uint64_t, double>(v);
+ int32_t lower = static_cast<int32_t>(int_val);
+ int32_t upper = static_cast<int32_t>(int_val >> (kBitsPerInt));
+
+ if (!CpuFeatures::IsSafeForSnapshot(SSE2)) {
+ __ push(Immediate(upper));
+ __ push(Immediate(lower));
+ X87Mov(ToX87Register(instr->result()), Operand(esp, 0));
+ __ add(Operand(esp), Immediate(kDoubleSize));
+ } else {
+ CpuFeatureScope scope1(masm(), SSE2);
+ ASSERT(instr->result()->IsDoubleRegister());
+ XMMRegister res = ToDoubleRegister(instr->result());
+ if (int_val == 0) {
+ __ xorps(res, res);
+ } else {
+ Register temp = ToRegister(instr->temp());
+ if (CpuFeatures::IsSupported(SSE4_1)) {
+ CpuFeatureScope scope2(masm(), SSE4_1);
+ if (lower != 0) {
+ __ Set(temp, Immediate(lower));
+ __ movd(res, Operand(temp));
+ __ Set(temp, Immediate(upper));
+ __ pinsrd(res, Operand(temp), 1);
+ } else {
+ __ xorps(res, res);
+ __ Set(temp, Immediate(upper));
+ __ pinsrd(res, Operand(temp), 1);
+ }
+ } else {
+ __ Set(temp, Immediate(upper));
+ __ movd(res, Operand(temp));
+ __ psllq(res, 32);
+ if (lower != 0) {
+ __ Set(temp, Immediate(lower));
+ __ movd(xmm0, Operand(temp));
+ __ por(res, xmm0);
+ }
+ }
+ }
+ }
+}
+
+
+void LCodeGen::DoConstantE(LConstantE* instr) {
+ __ lea(ToRegister(instr->result()), Operand::StaticVariable(instr->value()));
+}
+
+
+void LCodeGen::DoConstantT(LConstantT* instr) {
+ Register reg = ToRegister(instr->result());
+ Handle<Object> handle = instr->value();
+ AllowDeferredHandleDereference smi_check;
+ __ LoadObject(reg, handle);
+}
+
+
+void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
+ Register result = ToRegister(instr->result());
+ Register map = ToRegister(instr->value());
+ __ EnumLength(result, map);
+}
+
+
+void LCodeGen::DoElementsKind(LElementsKind* instr) {
+ Register result = ToRegister(instr->result());
+ Register input = ToRegister(instr->value());
+
+ // Load map into |result|.
+ __ mov(result, FieldOperand(input, HeapObject::kMapOffset));
+ // Load the map's "bit field 2" into |result|. We only need the first byte,
+ // but the following masking takes care of that anyway.
+ __ mov(result, FieldOperand(result, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ and_(result, Map::kElementsKindMask);
+ __ shr(result, Map::kElementsKindShift);
+}
+
+
+void LCodeGen::DoValueOf(LValueOf* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ Register map = ToRegister(instr->temp());
+ ASSERT(input.is(result));
+
+ Label done;
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ // If the object is a smi return the object.
+ __ JumpIfSmi(input, &done, Label::kNear);
+ }
+
+ // If the object is not a value type, return the object.
+ __ CmpObjectType(input, JS_VALUE_TYPE, map);
+ __ j(not_equal, &done, Label::kNear);
+ __ mov(result, FieldOperand(input, JSValue::kValueOffset));
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDateField(LDateField* instr) {
+ Register object = ToRegister(instr->date());
+ Register result = ToRegister(instr->result());
+ Register scratch = ToRegister(instr->temp());
+ Smi* index = instr->index();
+ Label runtime, done;
+ ASSERT(object.is(result));
+ ASSERT(object.is(eax));
+
+ __ test(object, Immediate(kSmiTagMask));
+ DeoptimizeIf(zero, instr->environment());
+ __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+ DeoptimizeIf(not_equal, instr->environment());
+
+ if (index->value() == 0) {
+ __ mov(result, FieldOperand(object, JSDate::kValueOffset));
+ } else {
+ if (index->value() < JSDate::kFirstUncachedField) {
+ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+ __ mov(scratch, Operand::StaticVariable(stamp));
+ __ cmp(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
+ __ j(not_equal, &runtime, Label::kNear);
+ __ mov(result, FieldOperand(object, JSDate::kValueOffset +
+ kPointerSize * index->value()));
+ __ jmp(&done);
+ }
+ __ bind(&runtime);
+ __ PrepareCallCFunction(2, scratch);
+ __ mov(Operand(esp, 0), object);
+ __ mov(Operand(esp, 1 * kPointerSize), Immediate(index));
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) {
+ Register string = ToRegister(instr->string());
+ Register index = ToRegister(instr->index());
+ Register value = ToRegister(instr->value());
+ String::Encoding encoding = instr->encoding();
+
+ if (FLAG_debug_code) {
+ __ push(value);
+ __ mov(value, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzx_b(value, FieldOperand(value, Map::kInstanceTypeOffset));
+
+ __ and_(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
+ static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
+ static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
+ __ cmp(value, Immediate(encoding == String::ONE_BYTE_ENCODING
+ ? one_byte_seq_type : two_byte_seq_type));
+ __ Check(equal, kUnexpectedStringType);
+ __ pop(value);
+ }
+
+ if (encoding == String::ONE_BYTE_ENCODING) {
+ __ mov_b(FieldOperand(string, index, times_1, SeqString::kHeaderSize),
+ value);
+ } else {
+ __ mov_w(FieldOperand(string, index, times_2, SeqString::kHeaderSize),
+ value);
+ }
+}
+
+
+void LCodeGen::DoThrow(LThrow* instr) {
+ __ push(ToOperand(instr->value()));
+ ASSERT(ToRegister(instr->context()).is(esi));
+ CallRuntime(Runtime::kThrow, 1, instr);
+
+ if (FLAG_debug_code) {
+ Comment("Unreachable code.");
+ __ int3();
+ }
+}
+
+
+void LCodeGen::DoAddI(LAddI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+
+ if (LAddI::UseLea(instr->hydrogen()) && !left->Equals(instr->result())) {
+ if (right->IsConstantOperand()) {
+ int32_t offset = ToRepresentation(LConstantOperand::cast(right),
+ instr->hydrogen()->representation());
+ __ lea(ToRegister(instr->result()), MemOperand(ToRegister(left), offset));
+ } else {
+ Operand address(ToRegister(left), ToRegister(right), times_1, 0);
+ __ lea(ToRegister(instr->result()), address);
+ }
+ } else {
+ if (right->IsConstantOperand()) {
+ __ add(ToOperand(left),
+ ToImmediate(right, instr->hydrogen()->representation()));
+ } else {
+ __ add(ToRegister(left), ToOperand(right));
+ }
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoMathMinMax(LMathMinMax* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ ASSERT(left->Equals(instr->result()));
+ HMathMinMax::Operation operation = instr->hydrogen()->operation();
+ if (instr->hydrogen()->representation().IsSmiOrInteger32()) {
+ Label return_left;
+ Condition condition = (operation == HMathMinMax::kMathMin)
+ ? less_equal
+ : greater_equal;
+ if (right->IsConstantOperand()) {
+ Operand left_op = ToOperand(left);
+ Immediate immediate = ToImmediate(LConstantOperand::cast(instr->right()),
+ instr->hydrogen()->representation());
+ __ cmp(left_op, immediate);
+ __ j(condition, &return_left, Label::kNear);
+ __ mov(left_op, immediate);
+ } else {
+ Register left_reg = ToRegister(left);
+ Operand right_op = ToOperand(right);
+ __ cmp(left_reg, right_op);
+ __ j(condition, &return_left, Label::kNear);
+ __ mov(left_reg, right_op);
+ }
+ __ bind(&return_left);
+ } else {
+ ASSERT(instr->hydrogen()->representation().IsDouble());
+ Label check_nan_left, check_zero, return_left, return_right;
+ Condition condition = (operation == HMathMinMax::kMathMin) ? below : above;
+ XMMRegister left_reg = ToDoubleRegister(left);
+ XMMRegister right_reg = ToDoubleRegister(right);
+ __ ucomisd(left_reg, right_reg);
+ __ j(parity_even, &check_nan_left, Label::kNear); // At least one NaN.
+ __ j(equal, &check_zero, Label::kNear); // left == right.
+ __ j(condition, &return_left, Label::kNear);
+ __ jmp(&return_right, Label::kNear);
+
+ __ bind(&check_zero);
+ XMMRegister xmm_scratch = xmm0;
+ __ xorps(xmm_scratch, xmm_scratch);
+ __ ucomisd(left_reg, xmm_scratch);
+ __ j(not_equal, &return_left, Label::kNear); // left == right != 0.
+ // At this point, both left and right are either 0 or -0.
+ if (operation == HMathMinMax::kMathMin) {
+ __ orpd(left_reg, right_reg);
+ } else {
+ // Since we operate on +0 and/or -0, addsd and andsd have the same effect.
+ __ addsd(left_reg, right_reg);
+ }
+ __ jmp(&return_left, Label::kNear);
+
+ __ bind(&check_nan_left);
+ __ ucomisd(left_reg, left_reg); // NaN check.
+ __ j(parity_even, &return_left, Label::kNear); // left == NaN.
+ __ bind(&return_right);
+ __ movsd(left_reg, right_reg);
+
+ __ bind(&return_left);
+ }
+}
+
+
+void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
+ if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister left = ToDoubleRegister(instr->left());
+ XMMRegister right = ToDoubleRegister(instr->right());
+ XMMRegister result = ToDoubleRegister(instr->result());
+ // Modulo uses a fixed result register.
+ ASSERT(instr->op() == Token::MOD || left.is(result));
+ switch (instr->op()) {
+ case Token::ADD:
+ __ addsd(left, right);
+ break;
+ case Token::SUB:
+ __ subsd(left, right);
+ break;
+ case Token::MUL:
+ __ mulsd(left, right);
+ break;
+ case Token::DIV:
+ __ divsd(left, right);
+ // Don't delete this mov. It may improve performance on some CPUs,
+ // when there is a mulsd depending on the result
+ __ movaps(left, left);
+ break;
+ case Token::MOD: {
+ // Pass two doubles as arguments on the stack.
+ __ PrepareCallCFunction(4, eax);
+ __ movdbl(Operand(esp, 0 * kDoubleSize), left);
+ __ movdbl(Operand(esp, 1 * kDoubleSize), right);
+ __ CallCFunction(
+ ExternalReference::double_fp_operation(Token::MOD, isolate()),
+ 4);
+
+ // 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, Operand(esp, 0));
+ __ add(Operand(esp), Immediate(kDoubleSize));
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } else {
+ X87Register left = ToX87Register(instr->left());
+ X87Register right = ToX87Register(instr->right());
+ X87Register result = ToX87Register(instr->result());
+ X87PrepareBinaryOp(left, right, result);
+ switch (instr->op()) {
+ case Token::MUL:
+ __ fmul_i(1);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->left()).is(edx));
+ ASSERT(ToRegister(instr->right()).is(eax));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ BinaryOpStub stub(instr->op(), NO_OVERWRITE);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ __ nop(); // Signals no inlined code.
+}
+
+
+int LCodeGen::GetNextEmittedBlock() const {
+ for (int i = current_block_ + 1; i < graph()->blocks()->length(); ++i) {
+ if (!chunk_->GetLabel(i)->HasReplacement()) return i;
+ }
+ return -1;
+}
+
+
+template<class InstrType>
+void LCodeGen::EmitBranch(InstrType instr, Condition cc) {
+ int left_block = instr->TrueDestination(chunk_);
+ int right_block = instr->FalseDestination(chunk_);
+
+ int next_block = GetNextEmittedBlock();
+
+ if (right_block == left_block || cc == no_condition) {
+ EmitGoto(left_block);
+ } else if (left_block == next_block) {
+ __ j(NegateCondition(cc), chunk_->GetAssemblyLabel(right_block));
+ } else if (right_block == next_block) {
+ __ j(cc, chunk_->GetAssemblyLabel(left_block));
+ } else {
+ __ j(cc, chunk_->GetAssemblyLabel(left_block));
+ __ jmp(chunk_->GetAssemblyLabel(right_block));
+ }
+}
+
+
+template<class InstrType>
+void LCodeGen::EmitFalseBranch(InstrType instr, Condition cc) {
+ int false_block = instr->FalseDestination(chunk_);
+ if (cc == no_condition) {
+ __ jmp(chunk_->GetAssemblyLabel(false_block));
+ } else {
+ __ j(cc, chunk_->GetAssemblyLabel(false_block));
+ }
+}
+
+
+void LCodeGen::DoIsNumberAndBranch(LIsNumberAndBranch* instr) {
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsSmiOrInteger32() || r.IsDouble()) {
+ EmitBranch(instr, no_condition);
+ } else {
+ ASSERT(r.IsTagged());
+ Register reg = ToRegister(instr->value());
+ HType type = instr->hydrogen()->value()->type();
+ if (type.IsTaggedNumber()) {
+ EmitBranch(instr, no_condition);
+ }
+ __ JumpIfSmi(reg, instr->TrueLabel(chunk_));
+ __ cmp(FieldOperand(reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ EmitBranch(instr, equal);
+ }
+}
+
+
+void LCodeGen::DoBranch(LBranch* instr) {
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsSmiOrInteger32()) {
+ ASSERT(!info()->IsStub());
+ Register reg = ToRegister(instr->value());
+ __ test(reg, Operand(reg));
+ EmitBranch(instr, not_zero);
+ } else if (r.IsDouble()) {
+ ASSERT(!info()->IsStub());
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister reg = ToDoubleRegister(instr->value());
+ __ xorps(xmm0, xmm0);
+ __ ucomisd(reg, xmm0);
+ EmitBranch(instr, not_equal);
+ } else {
+ ASSERT(r.IsTagged());
+ Register reg = ToRegister(instr->value());
+ HType type = instr->hydrogen()->value()->type();
+ if (type.IsBoolean()) {
+ ASSERT(!info()->IsStub());
+ __ cmp(reg, factory()->true_value());
+ EmitBranch(instr, equal);
+ } else if (type.IsSmi()) {
+ ASSERT(!info()->IsStub());
+ __ test(reg, Operand(reg));
+ EmitBranch(instr, not_equal);
+ } else if (type.IsJSArray()) {
+ ASSERT(!info()->IsStub());
+ EmitBranch(instr, no_condition);
+ } else if (type.IsHeapNumber()) {
+ ASSERT(!info()->IsStub());
+ CpuFeatureScope scope(masm(), SSE2);
+ __ xorps(xmm0, xmm0);
+ __ ucomisd(xmm0, FieldOperand(reg, HeapNumber::kValueOffset));
+ EmitBranch(instr, not_equal);
+ } else if (type.IsString()) {
+ ASSERT(!info()->IsStub());
+ __ cmp(FieldOperand(reg, String::kLengthOffset), Immediate(0));
+ EmitBranch(instr, not_equal);
+ } else {
+ ToBooleanStub::Types expected = instr->hydrogen()->expected_input_types();
+ if (expected.IsEmpty()) expected = ToBooleanStub::Types::Generic();
+
+ if (expected.Contains(ToBooleanStub::UNDEFINED)) {
+ // undefined -> false.
+ __ cmp(reg, factory()->undefined_value());
+ __ j(equal, instr->FalseLabel(chunk_));
+ }
+ if (expected.Contains(ToBooleanStub::BOOLEAN)) {
+ // true -> true.
+ __ cmp(reg, factory()->true_value());
+ __ j(equal, instr->TrueLabel(chunk_));
+ // false -> false.
+ __ cmp(reg, factory()->false_value());
+ __ j(equal, instr->FalseLabel(chunk_));
+ }
+ if (expected.Contains(ToBooleanStub::NULL_TYPE)) {
+ // 'null' -> false.
+ __ cmp(reg, factory()->null_value());
+ __ j(equal, instr->FalseLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::SMI)) {
+ // Smis: 0 -> false, all other -> true.
+ __ test(reg, Operand(reg));
+ __ j(equal, instr->FalseLabel(chunk_));
+ __ JumpIfSmi(reg, instr->TrueLabel(chunk_));
+ } else if (expected.NeedsMap()) {
+ // If we need a map later and have a Smi -> deopt.
+ __ test(reg, Immediate(kSmiTagMask));
+ DeoptimizeIf(zero, instr->environment());
+ }
+
+ Register map = no_reg; // Keep the compiler happy.
+ if (expected.NeedsMap()) {
+ map = ToRegister(instr->temp());
+ ASSERT(!map.is(reg));
+ __ mov(map, FieldOperand(reg, HeapObject::kMapOffset));
+
+ if (expected.CanBeUndetectable()) {
+ // Undetectable -> false.
+ __ test_b(FieldOperand(map, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ __ j(not_zero, instr->FalseLabel(chunk_));
+ }
+ }
+
+ if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) {
+ // spec object -> true.
+ __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
+ __ j(above_equal, instr->TrueLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::STRING)) {
+ // String value -> false iff empty.
+ Label not_string;
+ __ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
+ __ j(above_equal, &not_string, Label::kNear);
+ __ cmp(FieldOperand(reg, String::kLengthOffset), Immediate(0));
+ __ j(not_zero, instr->TrueLabel(chunk_));
+ __ jmp(instr->FalseLabel(chunk_));
+ __ bind(&not_string);
+ }
+
+ if (expected.Contains(ToBooleanStub::SYMBOL)) {
+ // Symbol value -> true.
+ __ CmpInstanceType(map, SYMBOL_TYPE);
+ __ j(equal, instr->TrueLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
+ // heap number -> false iff +0, -0, or NaN.
+ Label not_heap_number;
+ __ cmp(FieldOperand(reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ __ j(not_equal, &not_heap_number, Label::kNear);
+ if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ __ xorps(xmm0, xmm0);
+ __ ucomisd(xmm0, FieldOperand(reg, HeapNumber::kValueOffset));
+ } else {
+ __ fldz();
+ __ fld_d(FieldOperand(reg, HeapNumber::kValueOffset));
+ __ FCmp();
+ }
+ __ j(zero, instr->FalseLabel(chunk_));
+ __ jmp(instr->TrueLabel(chunk_));
+ __ bind(&not_heap_number);
+ }
+
+ if (!expected.IsGeneric()) {
+ // We've seen something for the first time -> deopt.
+ // This can only happen if we are not generic already.
+ DeoptimizeIf(no_condition, instr->environment());
+ }
+ }
+ }
+}
+
+
+void LCodeGen::EmitGoto(int block) {
+ if (!IsNextEmittedBlock(block)) {
+ __ jmp(chunk_->GetAssemblyLabel(LookupDestination(block)));
+ }
+}
+
+
+void LCodeGen::DoGoto(LGoto* instr) {
+ EmitGoto(instr->block_id());
+}
+
+
+Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) {
+ Condition cond = no_condition;
+ switch (op) {
+ case Token::EQ:
+ case Token::EQ_STRICT:
+ cond = equal;
+ break;
+ case Token::LT:
+ cond = is_unsigned ? below : less;
+ break;
+ case Token::GT:
+ cond = is_unsigned ? above : greater;
+ break;
+ case Token::LTE:
+ cond = is_unsigned ? below_equal : less_equal;
+ break;
+ case Token::GTE:
+ cond = is_unsigned ? above_equal : greater_equal;
+ break;
+ case Token::IN:
+ case Token::INSTANCEOF:
+ default:
+ UNREACHABLE();
+ }
+ return cond;
+}
+
+
+void LCodeGen::DoCompareNumericAndBranch(LCompareNumericAndBranch* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ Condition cc = TokenToCondition(instr->op(), instr->is_double());
+
+ 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) ?
+ instr->TrueDestination(chunk_) : instr->FalseDestination(chunk_);
+ EmitGoto(next_block);
+ } else {
+ if (instr->is_double()) {
+ CpuFeatureScope scope(masm(), SSE2);
+ // 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, instr->FalseLabel(chunk_));
+ } else {
+ if (right->IsConstantOperand()) {
+ __ cmp(ToOperand(left),
+ ToImmediate(right, instr->hydrogen()->representation()));
+ } else if (left->IsConstantOperand()) {
+ __ cmp(ToOperand(right),
+ ToImmediate(left, instr->hydrogen()->representation()));
+ // We transposed the operands. Reverse the condition.
+ cc = ReverseCondition(cc);
+ } else {
+ __ cmp(ToRegister(left), ToOperand(right));
+ }
+ }
+ EmitBranch(instr, cc);
+ }
+}
+
+
+void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) {
+ Register left = ToRegister(instr->left());
+
+ if (instr->right()->IsConstantOperand()) {
+ Handle<Object> right = ToHandle(LConstantOperand::cast(instr->right()));
+ __ CmpObject(left, right);
+ } else {
+ Operand right = ToOperand(instr->right());
+ __ cmp(left, right);
+ }
+ EmitBranch(instr, equal);
+}
+
+
+void LCodeGen::DoCmpHoleAndBranch(LCmpHoleAndBranch* instr) {
+ if (instr->hydrogen()->representation().IsTagged()) {
+ Register input_reg = ToRegister(instr->object());
+ __ cmp(input_reg, factory()->the_hole_value());
+ EmitBranch(instr, equal);
+ return;
+ }
+
+ bool use_sse2 = CpuFeatures::IsSupported(SSE2);
+ if (use_sse2) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister input_reg = ToDoubleRegister(instr->object());
+ __ ucomisd(input_reg, input_reg);
+ EmitFalseBranch(instr, parity_odd);
+ } else {
+ // Put the value to the top of stack
+ X87Register src = ToX87Register(instr->object());
+ X87LoadForUsage(src);
+ __ fld(0);
+ __ fld(0);
+ __ FCmp();
+ Label ok;
+ __ j(parity_even, &ok);
+ __ fstp(0);
+ EmitFalseBranch(instr, no_condition);
+ __ bind(&ok);
+ }
+
+
+ __ sub(esp, Immediate(kDoubleSize));
+ if (use_sse2) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister input_reg = ToDoubleRegister(instr->object());
+ __ movdbl(MemOperand(esp, 0), input_reg);
+ } else {
+ __ fstp_d(MemOperand(esp, 0));
+ }
+
+ __ add(esp, Immediate(kDoubleSize));
+ int offset = sizeof(kHoleNanUpper32);
+ __ cmp(MemOperand(esp, -offset), Immediate(kHoleNanUpper32));
+ EmitBranch(instr, equal);
+}
+
+
+Condition LCodeGen::EmitIsObject(Register input,
+ Register temp1,
+ Label* is_not_object,
+ Label* is_object) {
+ __ JumpIfSmi(input, is_not_object);
+
+ __ cmp(input, isolate()->factory()->null_value());
+ __ j(equal, is_object);
+
+ __ mov(temp1, FieldOperand(input, HeapObject::kMapOffset));
+ // Undetectable objects behave like undefined.
+ __ test_b(FieldOperand(temp1, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ __ j(not_zero, is_not_object);
+
+ __ movzx_b(temp1, FieldOperand(temp1, Map::kInstanceTypeOffset));
+ __ cmp(temp1, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ j(below, is_not_object);
+ __ cmp(temp1, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ return below_equal;
+}
+
+
+void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ Condition true_cond = EmitIsObject(
+ reg, temp, instr->FalseLabel(chunk_), instr->TrueLabel(chunk_));
+
+ EmitBranch(instr, true_cond);
+}
+
+
+Condition LCodeGen::EmitIsString(Register input,
+ Register temp1,
+ Label* is_not_string,
+ SmiCheck check_needed = INLINE_SMI_CHECK) {
+ if (check_needed == INLINE_SMI_CHECK) {
+ __ JumpIfSmi(input, is_not_string);
+ }
+
+ Condition cond = masm_->IsObjectStringType(input, temp1, temp1);
+
+ return cond;
+}
+
+
+void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+
+ Condition true_cond = EmitIsString(
+ reg, temp, instr->FalseLabel(chunk_), check_needed);
+
+ EmitBranch(instr, true_cond);
+}
+
+
+void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
+ Operand input = ToOperand(instr->value());
+
+ __ test(input, Immediate(kSmiTagMask));
+ EmitBranch(instr, zero);
+}
+
+
+void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(input, instr->FalseLabel(chunk_));
+ }
+ __ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
+ __ test_b(FieldOperand(temp, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ EmitBranch(instr, not_zero);
+}
+
+
+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();
+
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+
+ Condition condition = ComputeCompareCondition(op);
+ __ test(eax, Operand(eax));
+
+ EmitBranch(instr, condition);
+}
+
+
+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 equal;
+ if (to == LAST_TYPE) return above_equal;
+ if (from == FIRST_TYPE) return below_equal;
+ UNREACHABLE();
+ return equal;
+}
+
+
+void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ __ JumpIfSmi(input, instr->FalseLabel(chunk_));
+ }
+
+ __ CmpObjectType(input, TestType(instr->hydrogen()), temp);
+ EmitBranch(instr, BranchCondition(instr->hydrogen()));
+}
+
+
+void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+
+ __ AssertString(input);
+
+ __ mov(result, FieldOperand(input, String::kHashFieldOffset));
+ __ IndexFromHash(result, result);
+}
+
+
+void LCodeGen::DoHasCachedArrayIndexAndBranch(
+ LHasCachedArrayIndexAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+
+ __ test(FieldOperand(input, String::kHashFieldOffset),
+ Immediate(String::kContainsCachedArrayIndexMask));
+ EmitBranch(instr, equal);
+}
+
+
+// Branches to a label or falls through with the answer in the z flag. 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->IsOneByteEqualTo(STATIC_ASCII_VECTOR("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);
+ __ 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 {
+ // 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));
+ __ movzx_b(temp2, FieldOperand(temp, Map::kInstanceTypeOffset));
+ __ sub(Operand(temp2), Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ __ cmp(Operand(temp2), Immediate(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));
+ // Objects with a non-function constructor have class 'Object'.
+ __ CmpObjectType(temp, JS_FUNCTION_TYPE, temp2);
+ if (class_name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("Object"))) {
+ __ j(not_equal, is_true);
+ } else {
+ __ j(not_equal, is_false);
+ }
+
+ // temp now contains the constructor function. Grab the
+ // instance class name from there.
+ __ mov(temp, FieldOperand(temp, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(temp, FieldOperand(temp,
+ SharedFunctionInfo::kInstanceClassNameOffset));
+ // The class name we are testing against is internalized since it's a literal.
+ // The name in the constructor is internalized 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 internalized it is sufficient to use an
+ // identity comparison.
+ __ cmp(temp, class_name);
+ // End with the answer in the z flag.
+}
+
+
+void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+ Register temp2 = ToRegister(instr->temp2());
+
+ Handle<String> class_name = instr->hydrogen()->class_name();
+
+ EmitClassOfTest(instr->TrueLabel(chunk_), instr->FalseLabel(chunk_),
+ class_name, input, temp, temp2);
+
+ EmitBranch(instr, equal);
+}
+
+
+void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ __ cmp(FieldOperand(reg, HeapObject::kMapOffset), instr->map());
+ EmitBranch(instr, equal);
+}
+
+
+void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
+ // Object and function are in fixed registers defined by the stub.
+ ASSERT(ToRegister(instr->context()).is(esi));
+ InstanceofStub stub(InstanceofStub::kArgsInRegisters);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+
+ Label true_value, done;
+ __ test(eax, Operand(eax));
+ __ j(zero, &true_value, Label::kNear);
+ __ mov(ToRegister(instr->result()), factory()->false_value());
+ __ jmp(&done, Label::kNear);
+ __ bind(&true_value);
+ __ mov(ToRegister(instr->result()), 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(zone()) DeferredInstanceOfKnownGlobal(this, instr);
+
+ Label done, false_result;
+ Register object = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ // A Smi is not an 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 = ToRegister(instr->temp());
+ __ mov(map, FieldOperand(object, HeapObject::kMapOffset));
+ __ bind(deferred->map_check()); // Label for calculating code patching.
+ Handle<Cell> cache_cell = factory()->NewCell(factory()->the_hole_value());
+ __ cmp(map, Operand::ForCell(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);
+
+ // The inlined call site cache did not match. Check for null and string
+ // before calling the deferred code.
+ __ bind(&cache_miss);
+ // Null is not an instance of anything.
+ __ cmp(object, factory()->null_value());
+ __ j(equal, &false_result);
+
+ // String values are not instances of anything.
+ Condition is_string = masm_->IsObjectStringType(object, temp, temp);
+ __ j(is_string, &false_result);
+
+ // Go to the deferred code.
+ __ jmp(deferred->entry());
+
+ __ bind(&false_result);
+ __ mov(ToRegister(instr->result()), factory()->false_value());
+
+ // 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) {
+ PushSafepointRegistersScope scope(this);
+
+ 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);
+
+ // Get the temp register reserved by the instruction. This needs to be a
+ // register which is pushed last by PushSafepointRegisters as top of the
+ // stack is used to pass the offset to the location of the map check to
+ // the stub.
+ Register temp = ToRegister(instr->temp());
+ ASSERT(MacroAssembler::SafepointRegisterStackIndex(temp) == 0);
+ __ LoadHeapObject(InstanceofStub::right(), instr->function());
+ static const int kAdditionalDelta = 13;
+ int delta = masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
+ __ mov(temp, Immediate(delta));
+ __ StoreToSafepointRegisterSlot(temp, temp);
+ CallCodeGeneric(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ instr,
+ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
+ // Get the deoptimization index of the LLazyBailout-environment that
+ // corresponds to this instruction.
+ LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
+ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
+
+ // Put the result value into the eax slot and restore all registers.
+ __ StoreToSafepointRegisterSlot(eax, eax);
+}
+
+
+void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
+ Register object = ToRegister(instr->object());
+ Register result = ToRegister(instr->result());
+ __ mov(result, FieldOperand(object, HeapObject::kMapOffset));
+ __ movzx_b(result, FieldOperand(result, Map::kInstanceSizeOffset));
+}
+
+
+void LCodeGen::DoCmpT(LCmpT* instr) {
+ Token::Value op = instr->op();
+
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+
+ Condition condition = ComputeCompareCondition(op);
+ Label true_value, done;
+ __ test(eax, Operand(eax));
+ __ j(condition, &true_value, Label::kNear);
+ __ mov(ToRegister(instr->result()), factory()->false_value());
+ __ jmp(&done, Label::kNear);
+ __ bind(&true_value);
+ __ mov(ToRegister(instr->result()), factory()->true_value());
+ __ bind(&done);
+}
+
+
+void LCodeGen::EmitReturn(LReturn* instr, bool dynamic_frame_alignment) {
+ int extra_value_count = dynamic_frame_alignment ? 2 : 1;
+
+ if (instr->has_constant_parameter_count()) {
+ int parameter_count = ToInteger32(instr->constant_parameter_count());
+ if (dynamic_frame_alignment && FLAG_debug_code) {
+ __ cmp(Operand(esp,
+ (parameter_count + extra_value_count) * kPointerSize),
+ Immediate(kAlignmentZapValue));
+ __ Assert(equal, kExpectedAlignmentMarker);
+ }
+ __ Ret((parameter_count + extra_value_count) * kPointerSize, ecx);
+ } else {
+ Register reg = ToRegister(instr->parameter_count());
+ // The argument count parameter is a smi
+ __ SmiUntag(reg);
+ Register return_addr_reg = reg.is(ecx) ? ebx : ecx;
+ if (dynamic_frame_alignment && FLAG_debug_code) {
+ ASSERT(extra_value_count == 2);
+ __ cmp(Operand(esp, reg, times_pointer_size,
+ extra_value_count * kPointerSize),
+ Immediate(kAlignmentZapValue));
+ __ Assert(equal, kExpectedAlignmentMarker);
+ }
+
+ // emit code to restore stack based on instr->parameter_count()
+ __ pop(return_addr_reg); // save return address
+ if (dynamic_frame_alignment) {
+ __ inc(reg); // 1 more for alignment
+ }
+ __ shl(reg, kPointerSizeLog2);
+ __ add(esp, reg);
+ __ jmp(return_addr_reg);
+ }
+}
+
+
+void LCodeGen::DoReturn(LReturn* instr) {
+ if (FLAG_trace && info()->IsOptimizing()) {
+ // Preserve the return value on the stack and rely on the runtime call
+ // to return the value in the same register. We're leaving the code
+ // managed by the register allocator and tearing down the frame, it's
+ // safe to write to the context register.
+ __ push(eax);
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+ if (info()->saves_caller_doubles() && CpuFeatures::IsSupported(SSE2)) {
+ ASSERT(NeedsEagerFrame());
+ CpuFeatureScope scope(masm(), SSE2);
+ BitVector* doubles = chunk()->allocated_double_registers();
+ BitVector::Iterator save_iterator(doubles);
+ int count = 0;
+ while (!save_iterator.Done()) {
+ __ movdbl(XMMRegister::FromAllocationIndex(save_iterator.Current()),
+ MemOperand(esp, count * kDoubleSize));
+ save_iterator.Advance();
+ count++;
+ }
+ }
+ if (dynamic_frame_alignment_) {
+ // Fetch the state of the dynamic frame alignment.
+ __ mov(edx, Operand(ebp,
+ JavaScriptFrameConstants::kDynamicAlignmentStateOffset));
+ }
+ int no_frame_start = -1;
+ if (NeedsEagerFrame()) {
+ __ mov(esp, ebp);
+ __ pop(ebp);
+ no_frame_start = masm_->pc_offset();
+ }
+ if (dynamic_frame_alignment_) {
+ Label no_padding;
+ __ cmp(edx, Immediate(kNoAlignmentPadding));
+ __ j(equal, &no_padding);
+
+ EmitReturn(instr, true);
+ __ bind(&no_padding);
+ }
+
+ EmitReturn(instr, false);
+ if (no_frame_start != -1) {
+ info()->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
+}
+
+
+void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
+ Register result = ToRegister(instr->result());
+ __ mov(result, Operand::ForCell(instr->hydrogen()->cell()));
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ cmp(result, factory()->the_hole_value());
+ DeoptimizeIf(equal, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->global_object()).is(edx));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ __ mov(ecx, 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());
+ Handle<PropertyCell> 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()->RequiresHoleCheck()) {
+ __ cmp(Operand::ForCell(cell_handle), factory()->the_hole_value());
+ DeoptimizeIf(equal, instr->environment());
+ }
+
+ // Store the value.
+ __ mov(Operand::ForCell(cell_handle), value);
+ // Cells are always rescanned, so no write barrier here.
+}
+
+
+void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->global_object()).is(edx));
+ ASSERT(ToRegister(instr->value()).is(eax));
+
+ __ mov(ecx, 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());
+ __ 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());
+
+ 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()) {
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ Register temp = ToRegister(instr->temp());
+ int offset = Context::SlotOffset(instr->slot_index());
+ __ RecordWriteContextSlot(context,
+ offset,
+ value,
+ temp,
+ GetSaveFPRegsMode(),
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+
+ __ bind(&skip_assignment);
+}
+
+
+void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
+ HObjectAccess access = instr->hydrogen()->access();
+ int offset = access.offset();
+
+ if (access.IsExternalMemory()) {
+ Register result = ToRegister(instr->result());
+ if (instr->object()->IsConstantOperand()) {
+ ExternalReference external_reference = ToExternalReference(
+ LConstantOperand::cast(instr->object()));
+ __ mov(result, MemOperand::StaticVariable(external_reference));
+ } else {
+ __ mov(result, MemOperand(ToRegister(instr->object()), offset));
+ }
+ return;
+ }
+
+ Register object = ToRegister(instr->object());
+ if (FLAG_track_double_fields &&
+ instr->hydrogen()->representation().IsDouble()) {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister result = ToDoubleRegister(instr->result());
+ __ movdbl(result, FieldOperand(object, offset));
+ } else {
+ X87Mov(ToX87Register(instr->result()), FieldOperand(object, offset));
+ }
+ return;
+ }
+
+ Register result = ToRegister(instr->result());
+ if (access.IsInobject()) {
+ __ mov(result, FieldOperand(object, offset));
+ } else {
+ __ mov(result, FieldOperand(object, JSObject::kPropertiesOffset));
+ __ mov(result, FieldOperand(result, offset));
+ }
+}
+
+
+void LCodeGen::EmitPushTaggedOperand(LOperand* operand) {
+ ASSERT(!operand->IsDoubleRegister());
+ if (operand->IsConstantOperand()) {
+ Handle<Object> object = ToHandle(LConstantOperand::cast(operand));
+ AllowDeferredHandleDereference smi_check;
+ 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));
+ }
+}
+
+
+void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->object()).is(edx));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ __ mov(ecx, instr->name());
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
+ Register function = ToRegister(instr->function());
+ Register temp = ToRegister(instr->temp());
+ Register result = ToRegister(instr->result());
+
+ // Check that the function really is a function.
+ __ CmpObjectType(function, JS_FUNCTION_TYPE, result);
+ DeoptimizeIf(not_equal, instr->environment());
+
+ // Check whether the function has an instance prototype.
+ Label non_instance;
+ __ test_b(FieldOperand(result, Map::kBitFieldOffset),
+ 1 << Map::kHasNonInstancePrototype);
+ __ j(not_zero, &non_instance, Label::kNear);
+
+ // Get the prototype or initial map from the function.
+ __ mov(result,
+ FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Check that the function has a prototype or an initial map.
+ __ cmp(Operand(result), Immediate(factory()->the_hole_value()));
+ DeoptimizeIf(equal, instr->environment());
+
+ // If the function does not have an initial map, we're done.
+ Label done;
+ __ CmpObjectType(result, MAP_TYPE, temp);
+ __ j(not_equal, &done, Label::kNear);
+
+ // Get the prototype from the initial map.
+ __ mov(result, FieldOperand(result, Map::kPrototypeOffset));
+ __ jmp(&done, Label::kNear);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in the function's map.
+ __ bind(&non_instance);
+ __ mov(result, FieldOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoLoadExternalArrayPointer(
+ LLoadExternalArrayPointer* instr) {
+ Register result = ToRegister(instr->result());
+ Register input = ToRegister(instr->object());
+ __ mov(result, FieldOperand(input,
+ ExternalArray::kExternalPointerOffset));
+}
+
+
+void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
+ Register arguments = ToRegister(instr->arguments());
+ Register result = ToRegister(instr->result());
+ if (instr->length()->IsConstantOperand() &&
+ instr->index()->IsConstantOperand()) {
+ int const_index = ToInteger32(LConstantOperand::cast(instr->index()));
+ int const_length = ToInteger32(LConstantOperand::cast(instr->length()));
+ int index = (const_length - const_index) + 1;
+ __ mov(result, Operand(arguments, index * kPointerSize));
+ } else {
+ Register length = ToRegister(instr->length());
+ Operand index = ToOperand(instr->index());
+ // There are two words between the frame pointer and the last argument.
+ // Subtracting from length accounts for one of them add one more.
+ __ sub(length, index);
+ __ mov(result, Operand(arguments, length, times_4, kPointerSize));
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) {
+ ElementsKind elements_kind = instr->elements_kind();
+ LOperand* key = instr->key();
+ if (!key->IsConstantOperand() &&
+ ExternalArrayOpRequiresTemp(instr->hydrogen()->key()->representation(),
+ elements_kind)) {
+ __ SmiUntag(ToRegister(key));
+ }
+ Operand operand(BuildFastArrayOperand(
+ instr->elements(),
+ key,
+ instr->hydrogen()->key()->representation(),
+ elements_kind,
+ 0,
+ instr->additional_index()));
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister result(ToDoubleRegister(instr->result()));
+ __ movss(result, operand);
+ __ cvtss2sd(result, result);
+ } else {
+ X87Mov(ToX87Register(instr->result()), operand, kX87FloatOperand);
+ }
+ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ __ movdbl(ToDoubleRegister(instr->result()), operand);
+ } else {
+ X87Mov(ToX87Register(instr->result()), operand);
+ }
+ } else {
+ Register result(ToRegister(instr->result()));
+ switch (elements_kind) {
+ case EXTERNAL_BYTE_ELEMENTS:
+ __ movsx_b(result, operand);
+ break;
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ movzx_b(result, operand);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ __ movsx_w(result, operand);
+ break;
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ movzx_w(result, operand);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ __ mov(result, operand);
+ break;
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ mov(result, operand);
+ if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) {
+ __ test(result, Operand(result));
+ DeoptimizeIf(negative, instr->environment());
+ }
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr) {
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag +
+ sizeof(kHoleNanLower32);
+ Operand hole_check_operand = BuildFastArrayOperand(
+ instr->elements(), instr->key(),
+ instr->hydrogen()->key()->representation(),
+ FAST_DOUBLE_ELEMENTS,
+ offset,
+ instr->additional_index());
+ __ cmp(hole_check_operand, Immediate(kHoleNanUpper32));
+ DeoptimizeIf(equal, instr->environment());
+ }
+
+ Operand double_load_operand = BuildFastArrayOperand(
+ instr->elements(),
+ instr->key(),
+ instr->hydrogen()->key()->representation(),
+ FAST_DOUBLE_ELEMENTS,
+ FixedDoubleArray::kHeaderSize - kHeapObjectTag,
+ instr->additional_index());
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister result = ToDoubleRegister(instr->result());
+ __ movdbl(result, double_load_operand);
+ } else {
+ X87Mov(ToX87Register(instr->result()), double_load_operand);
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedFixedArray(LLoadKeyed* instr) {
+ Register result = ToRegister(instr->result());
+
+ // Load the result.
+ __ mov(result,
+ BuildFastArrayOperand(instr->elements(),
+ instr->key(),
+ instr->hydrogen()->key()->representation(),
+ FAST_ELEMENTS,
+ FixedArray::kHeaderSize - kHeapObjectTag,
+ instr->additional_index()));
+
+ // Check for the hole value.
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ if (IsFastSmiElementsKind(instr->hydrogen()->elements_kind())) {
+ __ test(result, Immediate(kSmiTagMask));
+ DeoptimizeIf(not_equal, instr->environment());
+ } else {
+ __ cmp(result, factory()->the_hole_value());
+ DeoptimizeIf(equal, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoLoadKeyed(LLoadKeyed* instr) {
+ if (instr->is_external()) {
+ DoLoadKeyedExternalArray(instr);
+ } else if (instr->hydrogen()->representation().IsDouble()) {
+ DoLoadKeyedFixedDoubleArray(instr);
+ } else {
+ DoLoadKeyedFixedArray(instr);
+ }
+}
+
+
+Operand LCodeGen::BuildFastArrayOperand(
+ LOperand* elements_pointer,
+ LOperand* key,
+ Representation key_representation,
+ ElementsKind elements_kind,
+ uint32_t offset,
+ uint32_t additional_index) {
+ Register elements_pointer_reg = ToRegister(elements_pointer);
+ int element_shift_size = ElementsKindToShiftSize(elements_kind);
+ int shift_size = element_shift_size;
+ if (key->IsConstantOperand()) {
+ int constant_value = ToInteger32(LConstantOperand::cast(key));
+ if (constant_value & 0xF0000000) {
+ Abort(kArrayIndexConstantValueTooBig);
+ }
+ return Operand(elements_pointer_reg,
+ ((constant_value + additional_index) << shift_size)
+ + offset);
+ } else {
+ // Take the tag bit into account while computing the shift size.
+ if (key_representation.IsSmi() && (shift_size >= 1)) {
+ shift_size -= kSmiTagSize;
+ }
+ ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size);
+ return Operand(elements_pointer_reg,
+ ToRegister(key),
+ scale_factor,
+ offset + (additional_index << element_shift_size));
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->object()).is(edx));
+ ASSERT(ToRegister(instr->key()).is(ecx));
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
+ Register result = ToRegister(instr->result());
+
+ if (instr->hydrogen()->from_inlined()) {
+ __ lea(result, Operand(esp, -2 * kPointerSize));
+ } else {
+ // Check for arguments adapter frame.
+ Label done, adapted;
+ __ mov(result, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+ __ mov(result, Operand(result, StandardFrameConstants::kContextOffset));
+ __ cmp(Operand(result),
+ Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ j(equal, &adapted, Label::kNear);
+
+ // No arguments adaptor frame.
+ __ mov(result, Operand(ebp));
+ __ jmp(&done, Label::kNear);
+
+ // Arguments adaptor frame present.
+ __ bind(&adapted);
+ __ mov(result, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+
+ // Result is the frame pointer for the frame if not adapted and for the real
+ // frame below the adaptor frame if adapted.
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
+ Operand elem = ToOperand(instr->elements());
+ Register result = ToRegister(instr->result());
+
+ Label done;
+
+ // If no arguments adaptor frame the number of arguments is fixed.
+ __ cmp(ebp, elem);
+ __ mov(result, Immediate(scope()->num_parameters()));
+ __ j(equal, &done, Label::kNear);
+
+ // Arguments adaptor frame present. Get argument length from there.
+ __ mov(result, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+ __ mov(result, Operand(result,
+ ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ SmiUntag(result);
+
+ // Argument length is in result register.
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) {
+ Register receiver = ToRegister(instr->receiver());
+ Register function = ToRegister(instr->function());
+ Register scratch = ToRegister(instr->temp());
+
+ // 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.
+ __ mov(scratch,
+ FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
+ __ test_b(FieldOperand(scratch, SharedFunctionInfo::kStrictModeByteOffset),
+ 1 << SharedFunctionInfo::kStrictModeBitWithinByte);
+ __ j(not_equal, &receiver_ok); // A near jump is not sufficient here!
+
+ // Do not transform the receiver to object for builtins.
+ __ test_b(FieldOperand(scratch, SharedFunctionInfo::kNativeByteOffset),
+ 1 << SharedFunctionInfo::kNativeBitWithinByte);
+ __ j(not_equal, &receiver_ok);
+
+ // Normal function. Replace undefined or null with global receiver.
+ __ cmp(receiver, factory()->null_value());
+ __ j(equal, &global_object, Label::kNear);
+ __ cmp(receiver, factory()->undefined_value());
+ __ j(equal, &global_object, Label::kNear);
+
+ // The receiver should be a JS object.
+ __ test(receiver, Immediate(kSmiTagMask));
+ DeoptimizeIf(equal, instr->environment());
+ __ CmpObjectType(receiver, FIRST_SPEC_OBJECT_TYPE, scratch);
+ DeoptimizeIf(below, instr->environment());
+ __ jmp(&receiver_ok, Label::kNear);
+
+ __ bind(&global_object);
+ // TODO(kmillikin): We have a hydrogen value for the global object. See
+ // if it's better to use it than to explicitly fetch it from the context
+ // here.
+ __ mov(receiver, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ mov(receiver, ContextOperand(receiver, Context::GLOBAL_OBJECT_INDEX));
+ __ mov(receiver,
+ FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
+ __ bind(&receiver_ok);
+}
+
+
+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());
+ ASSERT(receiver.is(eax)); // Used for parameter count.
+ ASSERT(function.is(edi)); // Required by InvokeFunction.
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ // Copy the arguments to this function possibly from the
+ // adaptor frame below it.
+ const uint32_t kArgumentsLimit = 1 * KB;
+ __ cmp(length, kArgumentsLimit);
+ DeoptimizeIf(above, instr->environment());
+
+ __ push(receiver);
+ __ mov(receiver, length);
+
+ // 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.
+ __ test(length, Operand(length));
+ __ j(zero, &invoke, Label::kNear);
+ __ bind(&loop);
+ __ push(Operand(elements, length, times_pointer_size, 1 * kPointerSize));
+ __ dec(length);
+ __ j(not_zero, &loop);
+
+ // Invoke the function.
+ __ bind(&invoke);
+ ASSERT(instr->HasPointerMap());
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+ SafepointGenerator safepoint_generator(
+ this, pointers, Safepoint::kLazyDeopt);
+ ParameterCount actual(eax);
+ __ InvokeFunction(function, actual, CALL_FUNCTION,
+ safepoint_generator, CALL_AS_METHOD);
+}
+
+
+void LCodeGen::DoDebugBreak(LDebugBreak* instr) {
+ __ int3();
+}
+
+
+void LCodeGen::DoPushArgument(LPushArgument* instr) {
+ LOperand* argument = instr->value();
+ EmitPushTaggedOperand(argument);
+}
+
+
+void LCodeGen::DoDrop(LDrop* instr) {
+ __ Drop(instr->count());
+}
+
+
+void LCodeGen::DoThisFunction(LThisFunction* instr) {
+ Register result = ToRegister(instr->result());
+ __ mov(result, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+}
+
+
+void LCodeGen::DoContext(LContext* instr) {
+ Register result = ToRegister(instr->result());
+ if (info()->IsOptimizing()) {
+ __ mov(result, Operand(ebp, StandardFrameConstants::kContextOffset));
+ } else {
+ // If there is no frame, the context must be in esi.
+ ASSERT(result.is(esi));
+ }
+}
+
+
+void LCodeGen::DoOuterContext(LOuterContext* instr) {
+ Register context = ToRegister(instr->context());
+ Register result = ToRegister(instr->result());
+ __ mov(result,
+ Operand(context, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+}
+
+
+void LCodeGen::DoDeclareGlobals(LDeclareGlobals* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ __ push(esi); // The context is the first argument.
+ __ push(Immediate(instr->hydrogen()->pairs()));
+ __ push(Immediate(Smi::FromInt(instr->hydrogen()->flags())));
+ CallRuntime(Runtime::kDeclareGlobals, 3, instr);
+}
+
+
+void LCodeGen::DoGlobalObject(LGlobalObject* instr) {
+ Register context = ToRegister(instr->context());
+ Register result = ToRegister(instr->result());
+ __ mov(result,
+ Operand(context, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+}
+
+
+void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
+ Register global = ToRegister(instr->global());
+ Register result = ToRegister(instr->result());
+ __ mov(result, FieldOperand(global, GlobalObject::kGlobalReceiverOffset));
+}
+
+
+void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
+ int formal_parameter_count,
+ int arity,
+ LInstruction* instr,
+ CallKind call_kind,
+ EDIState edi_state) {
+ bool dont_adapt_arguments =
+ formal_parameter_count == SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+ bool can_invoke_directly =
+ dont_adapt_arguments || formal_parameter_count == arity;
+
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+
+ if (can_invoke_directly) {
+ if (edi_state == EDI_UNINITIALIZED) {
+ __ LoadHeapObject(edi, function);
+ }
+
+ // Change context.
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+ // Set eax to arguments count if adaption is not needed. Assumes that eax
+ // is available to write to at this point.
+ if (dont_adapt_arguments) {
+ __ mov(eax, arity);
+ }
+
+ // Invoke function directly.
+ __ SetCallKind(ecx, call_kind);
+ if (function.is_identical_to(info()->closure())) {
+ __ CallSelf();
+ } else {
+ __ call(FieldOperand(edi, JSFunction::kCodeEntryOffset));
+ }
+ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT);
+ } else {
+ // We need to adapt arguments.
+ SafepointGenerator generator(
+ this, pointers, Safepoint::kLazyDeopt);
+ ParameterCount count(arity);
+ ParameterCount expected(formal_parameter_count);
+ __ InvokeFunction(
+ function, expected, count, CALL_FUNCTION, generator, call_kind);
+ }
+}
+
+
+void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
+ ASSERT(ToRegister(instr->result()).is(eax));
+ CallKnownFunction(instr->hydrogen()->function(),
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD,
+ EDI_UNINITIALIZED);
+}
+
+
+void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr) {
+ Register input_reg = ToRegister(instr->value());
+ __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ DeoptimizeIf(not_equal, instr->environment());
+
+ Label slow, allocated, done;
+ Register tmp = input_reg.is(eax) ? ecx : eax;
+ Register tmp2 = tmp.is(ecx) ? edx : input_reg.is(ecx) ? edx : ecx;
+
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this);
+
+ __ mov(tmp, FieldOperand(input_reg, HeapNumber::kExponentOffset));
+ // Check the sign of the argument. If the argument is positive, just
+ // return it. We do not need to patch the stack since |input| and
+ // |result| are the same register and |input| will be restored
+ // unchanged by popping safepoint registers.
+ __ test(tmp, Immediate(HeapNumber::kSignMask));
+ __ j(zero, &done);
+
+ __ AllocateHeapNumber(tmp, tmp2, no_reg, &slow);
+ __ jmp(&allocated, Label::kNear);
+
+ // Slow case: Call the runtime system to do the number allocation.
+ __ bind(&slow);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0,
+ instr, instr->context());
+ // Set the pointer to the new heap number in tmp.
+ if (!tmp.is(eax)) __ mov(tmp, eax);
+ // Restore input_reg after call to runtime.
+ __ LoadFromSafepointRegisterSlot(input_reg, input_reg);
+
+ __ bind(&allocated);
+ __ mov(tmp2, FieldOperand(input_reg, HeapNumber::kExponentOffset));
+ __ and_(tmp2, ~HeapNumber::kSignMask);
+ __ mov(FieldOperand(tmp, HeapNumber::kExponentOffset), tmp2);
+ __ mov(tmp2, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
+ __ mov(FieldOperand(tmp, HeapNumber::kMantissaOffset), tmp2);
+ __ StoreToSafepointRegisterSlot(input_reg, tmp);
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::EmitIntegerMathAbs(LMathAbs* instr) {
+ Register input_reg = ToRegister(instr->value());
+ __ test(input_reg, Operand(input_reg));
+ Label is_positive;
+ __ j(not_sign, &is_positive, Label::kNear);
+ __ neg(input_reg); // Sets flags.
+ DeoptimizeIf(negative, instr->environment());
+ __ bind(&is_positive);
+}
+
+
+void LCodeGen::DoMathAbs(LMathAbs* instr) {
+ // Class for deferred case.
+ class DeferredMathAbsTaggedHeapNumber: public LDeferredCode {
+ public:
+ DeferredMathAbsTaggedHeapNumber(LCodeGen* codegen, LMathAbs* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LMathAbs* instr_;
+ };
+
+ ASSERT(instr->value()->Equals(instr->result()));
+ Representation r = instr->hydrogen()->value()->representation();
+
+ CpuFeatureScope scope(masm(), SSE2);
+ if (r.IsDouble()) {
+ XMMRegister scratch = xmm0;
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ __ xorps(scratch, scratch);
+ __ subsd(scratch, input_reg);
+ __ pand(input_reg, scratch);
+ } else if (r.IsSmiOrInteger32()) {
+ EmitIntegerMathAbs(instr);
+ } else { // Tagged case.
+ DeferredMathAbsTaggedHeapNumber* deferred =
+ new(zone()) DeferredMathAbsTaggedHeapNumber(this, instr);
+ Register input_reg = ToRegister(instr->value());
+ // Smi check.
+ __ JumpIfNotSmi(input_reg, deferred->entry());
+ EmitIntegerMathAbs(instr);
+ __ bind(deferred->exit());
+ }
+}
+
+
+void LCodeGen::DoMathFloor(LMathFloor* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister xmm_scratch = xmm0;
+ Register output_reg = ToRegister(instr->result());
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+
+ if (CpuFeatures::IsSupported(SSE4_1)) {
+ CpuFeatureScope scope(masm(), SSE4_1);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Deoptimize on negative zero.
+ Label non_zero;
+ __ xorps(xmm_scratch, xmm_scratch); // Zero the register.
+ __ ucomisd(input_reg, xmm_scratch);
+ __ j(not_equal, &non_zero, Label::kNear);
+ __ movmskpd(output_reg, input_reg);
+ __ test(output_reg, Immediate(1));
+ DeoptimizeIf(not_zero, instr->environment());
+ __ bind(&non_zero);
+ }
+ __ roundsd(xmm_scratch, input_reg, Assembler::kRoundDown);
+ __ cvttsd2si(output_reg, Operand(xmm_scratch));
+ // Overflow is signalled with minint.
+ __ cmp(output_reg, 0x80000000u);
+ DeoptimizeIf(equal, instr->environment());
+ } else {
+ Label negative_sign, done;
+ // Deoptimize on unordered.
+ __ xorps(xmm_scratch, xmm_scratch); // Zero the register.
+ __ ucomisd(input_reg, xmm_scratch);
+ DeoptimizeIf(parity_even, instr->environment());
+ __ j(below, &negative_sign, Label::kNear);
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Check for negative zero.
+ Label positive_sign;
+ __ j(above, &positive_sign, Label::kNear);
+ __ movmskpd(output_reg, input_reg);
+ __ test(output_reg, Immediate(1));
+ DeoptimizeIf(not_zero, instr->environment());
+ __ Set(output_reg, Immediate(0));
+ __ jmp(&done, Label::kNear);
+ __ bind(&positive_sign);
+ }
+
+ // Use truncating instruction (OK because input is positive).
+ __ cvttsd2si(output_reg, Operand(input_reg));
+ // Overflow is signalled with minint.
+ __ cmp(output_reg, 0x80000000u);
+ DeoptimizeIf(equal, instr->environment());
+ __ jmp(&done, Label::kNear);
+
+ // Non-zero negative reaches here.
+ __ bind(&negative_sign);
+ // Truncate, then compare and compensate.
+ __ cvttsd2si(output_reg, Operand(input_reg));
+ __ cvtsi2sd(xmm_scratch, output_reg);
+ __ ucomisd(input_reg, xmm_scratch);
+ __ j(equal, &done, Label::kNear);
+ __ sub(output_reg, Immediate(1));
+ DeoptimizeIf(overflow, instr->environment());
+
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoMathRound(LMathRound* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ Register output_reg = ToRegister(instr->result());
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ XMMRegister xmm_scratch = xmm0;
+ XMMRegister input_temp = ToDoubleRegister(instr->temp());
+ ExternalReference one_half = ExternalReference::address_of_one_half();
+ ExternalReference minus_one_half =
+ ExternalReference::address_of_minus_one_half();
+
+ Label done, round_to_zero, below_one_half, do_not_compensate;
+ __ movdbl(xmm_scratch, Operand::StaticVariable(one_half));
+ __ ucomisd(xmm_scratch, input_reg);
+ __ j(above, &below_one_half);
+
+ // CVTTSD2SI rounds towards zero, since 0.5 <= x, we use floor(0.5 + x).
+ __ addsd(xmm_scratch, input_reg);
+ __ cvttsd2si(output_reg, Operand(xmm_scratch));
+ // Overflow is signalled with minint.
+ __ cmp(output_reg, 0x80000000u);
+ __ RecordComment("D2I conversion overflow");
+ DeoptimizeIf(equal, instr->environment());
+ __ jmp(&done);
+
+ __ bind(&below_one_half);
+ __ movdbl(xmm_scratch, Operand::StaticVariable(minus_one_half));
+ __ ucomisd(xmm_scratch, input_reg);
+ __ j(below_equal, &round_to_zero);
+
+ // CVTTSD2SI rounds towards zero, we use ceil(x - (-0.5)) and then
+ // compare and compensate.
+ __ movsd(input_temp, input_reg); // Do not alter input_reg.
+ __ subsd(input_temp, xmm_scratch);
+ __ cvttsd2si(output_reg, Operand(input_temp));
+ // Catch minint due to overflow, and to prevent overflow when compensating.
+ __ cmp(output_reg, 0x80000000u);
+ __ RecordComment("D2I conversion overflow");
+ DeoptimizeIf(equal, instr->environment());
+
+ __ cvtsi2sd(xmm_scratch, output_reg);
+ __ ucomisd(xmm_scratch, input_temp);
+ __ j(equal, &done);
+ __ sub(output_reg, Immediate(1));
+ // No overflow because we already ruled out minint.
+ __ jmp(&done);
+
+ __ bind(&round_to_zero);
+ // We return 0 for the input range [+0, 0.5[, or [-0.5, 0.5[ if
+ // we can ignore the difference between a result of -0 and +0.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // If the sign is positive, we return +0.
+ __ movmskpd(output_reg, input_reg);
+ __ test(output_reg, Immediate(1));
+ __ RecordComment("Minus zero");
+ DeoptimizeIf(not_zero, instr->environment());
+ }
+ __ Set(output_reg, Immediate(0));
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoMathSqrt(LMathSqrt* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ ASSERT(ToDoubleRegister(instr->result()).is(input_reg));
+ __ sqrtsd(input_reg, input_reg);
+}
+
+
+void LCodeGen::DoMathPowHalf(LMathPowHalf* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ 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) {
+ 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->right()->IsDoubleRegister() ||
+ ToDoubleRegister(instr->right()).is(xmm1));
+ ASSERT(!instr->right()->IsRegister() ||
+ ToRegister(instr->right()).is(eax));
+ ASSERT(ToDoubleRegister(instr->left()).is(xmm2));
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm3));
+
+ if (exponent_type.IsSmi()) {
+ MathPowStub stub(MathPowStub::TAGGED);
+ __ CallStub(&stub);
+ } else 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()) {
+ MathPowStub stub(MathPowStub::INTEGER);
+ __ CallStub(&stub);
+ } else {
+ ASSERT(exponent_type.IsDouble());
+ MathPowStub stub(MathPowStub::DOUBLE);
+ __ CallStub(&stub);
+ }
+}
+
+
+void LCodeGen::DoRandom(LRandom* instr) {
+ class DeferredDoRandom: public LDeferredCode {
+ public:
+ DeferredDoRandom(LCodeGen* codegen, LRandom* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredRandom(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LRandom* instr_;
+ };
+
+ DeferredDoRandom* deferred = new(zone()) DeferredDoRandom(this, instr);
+
+ CpuFeatureScope scope(masm(), SSE2);
+ // Having marked this instruction as a call we can use any
+ // registers.
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
+ ASSERT(ToRegister(instr->global_object()).is(eax));
+ // Assert that the register size is indeed the size of each seed.
+ static const int kSeedSize = sizeof(uint32_t);
+ STATIC_ASSERT(kPointerSize == kSeedSize);
+
+ __ mov(eax, FieldOperand(eax, GlobalObject::kNativeContextOffset));
+ static const int kRandomSeedOffset =
+ FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
+ __ mov(ebx, FieldOperand(eax, kRandomSeedOffset));
+ // ebx: FixedArray of the native context's random seeds
+
+ // Load state[0].
+ __ mov(ecx, FieldOperand(ebx, ByteArray::kHeaderSize));
+ // If state[0] == 0, call runtime to initialize seeds.
+ __ test(ecx, ecx);
+ __ j(zero, deferred->entry());
+ // Load state[1].
+ __ mov(eax, FieldOperand(ebx, ByteArray::kHeaderSize + kSeedSize));
+ // ecx: state[0]
+ // eax: state[1]
+
+ // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
+ __ movzx_w(edx, ecx);
+ __ imul(edx, edx, 18273);
+ __ shr(ecx, 16);
+ __ add(ecx, edx);
+ // Save state[0].
+ __ mov(FieldOperand(ebx, ByteArray::kHeaderSize), ecx);
+
+ // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
+ __ movzx_w(edx, eax);
+ __ imul(edx, edx, 36969);
+ __ shr(eax, 16);
+ __ add(eax, edx);
+ // Save state[1].
+ __ mov(FieldOperand(ebx, ByteArray::kHeaderSize + kSeedSize), eax);
+
+ // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF)
+ __ shl(ecx, 14);
+ __ and_(eax, Immediate(0x3FFFF));
+ __ add(eax, ecx);
+
+ __ bind(deferred->exit());
+ // 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);
+}
+
+
+void LCodeGen::DoDeferredRandom(LRandom* instr) {
+ __ PrepareCallCFunction(1, ebx);
+ __ mov(Operand(esp, 0), eax);
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
+ // Return value is in eax.
+}
+
+
+void LCodeGen::DoMathLog(LMathLog* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ ASSERT(instr->value()->Equals(instr->result()));
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ Label positive, done, zero;
+ __ xorps(xmm0, xmm0);
+ __ ucomisd(input_reg, xmm0);
+ __ j(above, &positive, Label::kNear);
+ __ j(equal, &zero, Label::kNear);
+ ExternalReference nan =
+ ExternalReference::address_of_canonical_non_hole_nan();
+ __ movdbl(input_reg, Operand::StaticVariable(nan));
+ __ jmp(&done, Label::kNear);
+ __ bind(&zero);
+ __ push(Immediate(0xFFF00000));
+ __ push(Immediate(0));
+ __ movdbl(input_reg, Operand(esp, 0));
+ __ add(Operand(esp), Immediate(kDoubleSize));
+ __ jmp(&done, Label::kNear);
+ __ bind(&positive);
+ __ fldln2();
+ __ sub(Operand(esp), Immediate(kDoubleSize));
+ __ movdbl(Operand(esp, 0), input_reg);
+ __ fld_d(Operand(esp, 0));
+ __ fyl2x();
+ __ fstp_d(Operand(esp, 0));
+ __ movdbl(input_reg, Operand(esp, 0));
+ __ add(Operand(esp), Immediate(kDoubleSize));
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoMathExp(LMathExp* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister input = ToDoubleRegister(instr->value());
+ XMMRegister result = ToDoubleRegister(instr->result());
+ Register temp1 = ToRegister(instr->temp1());
+ Register temp2 = ToRegister(instr->temp2());
+
+ MathExpGenerator::EmitMathExp(masm(), input, result, xmm0, temp1, temp2);
+}
+
+
+void LCodeGen::DoMathTan(LMathTan* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
+ // Set the context register to a GC-safe fake value. Clobbering it is
+ // OK because this instruction is marked as a call.
+ __ Set(esi, Immediate(0));
+ TranscendentalCacheStub stub(TranscendentalCache::TAN,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathCos(LMathCos* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
+ // Set the context register to a GC-safe fake value. Clobbering it is
+ // OK because this instruction is marked as a call.
+ __ Set(esi, Immediate(0));
+ TranscendentalCacheStub stub(TranscendentalCache::COS,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathSin(LMathSin* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
+ // Set the context register to a GC-safe fake value. Clobbering it is
+ // OK because this instruction is marked as a call.
+ __ Set(esi, Immediate(0));
+ TranscendentalCacheStub stub(TranscendentalCache::SIN,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->function()).is(edi));
+ ASSERT(instr->HasPointerMap());
+
+ Handle<JSFunction> known_function = instr->hydrogen()->known_function();
+ if (known_function.is_null()) {
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+ SafepointGenerator generator(
+ this, pointers, Safepoint::kLazyDeopt);
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(edi, count, CALL_FUNCTION, generator, CALL_AS_METHOD);
+ } else {
+ CallKnownFunction(known_function,
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD,
+ EDI_CONTAINS_TARGET);
+ }
+}
+
+
+void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->key()).is(ecx));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ int arity = instr->arity();
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arity);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoCallNamed(LCallNamed* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ int arity = instr->arity();
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
+ __ mov(ecx, instr->name());
+ CallCode(ic, mode, 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, NO_CALL_FUNCTION_FLAGS);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ int arity = instr->arity();
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
+ __ mov(ecx, instr->name());
+ CallCode(ic, mode, instr);
+}
+
+
+void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) {
+ ASSERT(ToRegister(instr->result()).is(eax));
+ CallKnownFunction(instr->hydrogen()->target(),
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_FUNCTION,
+ EDI_UNINITIALIZED);
+}
+
+
+void LCodeGen::DoCallNew(LCallNew* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->constructor()).is(edi));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ // No cell in ebx for construct type feedback in optimized code
+ Handle<Object> undefined_value(isolate()->factory()->undefined_value());
+ __ mov(ebx, Immediate(undefined_value));
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ __ Set(eax, Immediate(instr->arity()));
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+}
+
+
+void LCodeGen::DoCallNewArray(LCallNewArray* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->constructor()).is(edi));
+ ASSERT(ToRegister(instr->result()).is(eax));
+
+ __ Set(eax, Immediate(instr->arity()));
+ __ mov(ebx, instr->hydrogen()->property_cell());
+ ElementsKind kind = instr->hydrogen()->elements_kind();
+ AllocationSiteOverrideMode override_mode =
+ (AllocationSite::GetMode(kind) == TRACK_ALLOCATION_SITE)
+ ? DISABLE_ALLOCATION_SITES
+ : DONT_OVERRIDE;
+ ContextCheckMode context_mode = CONTEXT_CHECK_NOT_REQUIRED;
+
+ if (instr->arity() == 0) {
+ ArrayNoArgumentConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ } else if (instr->arity() == 1) {
+ Label done;
+ if (IsFastPackedElementsKind(kind)) {
+ Label packed_case;
+ // We might need a change here
+ // look at the first argument
+ __ mov(ecx, Operand(esp, 0));
+ __ test(ecx, ecx);
+ __ j(zero, &packed_case);
+
+ ElementsKind holey_kind = GetHoleyElementsKind(kind);
+ ArraySingleArgumentConstructorStub stub(holey_kind, context_mode,
+ override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ __ jmp(&done);
+ __ bind(&packed_case);
+ }
+
+ ArraySingleArgumentConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ __ bind(&done);
+ } else {
+ ArrayNArgumentsConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ }
+}
+
+
+void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
+ CallRuntime(instr->function(), instr->arity(), instr);
+}
+
+
+void LCodeGen::DoInnerAllocatedObject(LInnerAllocatedObject* instr) {
+ Register result = ToRegister(instr->result());
+ Register base = ToRegister(instr->base_object());
+ __ lea(result, Operand(base, instr->offset()));
+}
+
+
+void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
+ Representation representation = instr->representation();
+
+ HObjectAccess access = instr->hydrogen()->access();
+ int offset = access.offset();
+
+ if (access.IsExternalMemory()) {
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ MemOperand operand = instr->object()->IsConstantOperand()
+ ? MemOperand::StaticVariable(
+ ToExternalReference(LConstantOperand::cast(instr->object())))
+ : MemOperand(ToRegister(instr->object()), offset);
+ if (instr->value()->IsConstantOperand()) {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ __ mov(operand, Immediate(ToInteger32(operand_value)));
+ } else {
+ Register value = ToRegister(instr->value());
+ __ mov(operand, value);
+ }
+ return;
+ }
+
+ Register object = ToRegister(instr->object());
+ Handle<Map> transition = instr->transition();
+
+ if (FLAG_track_fields && representation.IsSmi()) {
+ if (instr->value()->IsConstantOperand()) {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ if (!IsSmi(operand_value)) {
+ DeoptimizeIf(no_condition, instr->environment());
+ }
+ }
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ if (instr->value()->IsConstantOperand()) {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ if (IsInteger32(operand_value)) {
+ DeoptimizeIf(no_condition, instr->environment());
+ }
+ } else {
+ if (!instr->hydrogen()->value()->type().IsHeapObject()) {
+ Register value = ToRegister(instr->value());
+ __ test(value, Immediate(kSmiTagMask));
+ DeoptimizeIf(zero, instr->environment());
+ }
+ }
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ ASSERT(transition.is_null());
+ ASSERT(access.IsInobject());
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister value = ToDoubleRegister(instr->value());
+ __ movdbl(FieldOperand(object, offset), value);
+ } else {
+ X87Register value = ToX87Register(instr->value());
+ X87Mov(FieldOperand(object, offset), value);
+ }
+ return;
+ }
+
+ if (!transition.is_null()) {
+ if (!instr->hydrogen()->NeedsWriteBarrierForMap()) {
+ __ mov(FieldOperand(object, HeapObject::kMapOffset), transition);
+ } else {
+ Register temp = ToRegister(instr->temp());
+ Register temp_map = ToRegister(instr->temp_map());
+ __ mov(temp_map, transition);
+ __ mov(FieldOperand(object, HeapObject::kMapOffset), temp_map);
+ // Update the write barrier for the map field.
+ __ RecordWriteField(object,
+ HeapObject::kMapOffset,
+ temp_map,
+ temp,
+ GetSaveFPRegsMode(),
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ }
+ }
+
+ // Do the store.
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+
+ Register write_register = object;
+ if (!access.IsInobject()) {
+ write_register = ToRegister(instr->temp());
+ __ mov(write_register, FieldOperand(object, JSObject::kPropertiesOffset));
+ }
+
+ if (instr->value()->IsConstantOperand()) {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ if (operand_value->IsRegister()) {
+ __ mov(FieldOperand(write_register, offset), ToRegister(operand_value));
+ } else {
+ Handle<Object> handle_value = ToHandle(operand_value);
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ __ mov(FieldOperand(write_register, offset), handle_value);
+ }
+ } else {
+ __ mov(FieldOperand(write_register, offset), ToRegister(instr->value()));
+ }
+
+ if (instr->hydrogen()->NeedsWriteBarrier()) {
+ Register value = ToRegister(instr->value());
+ Register temp = access.IsInobject() ? ToRegister(instr->temp()) : object;
+ // Update the write barrier for the object for in-object properties.
+ __ RecordWriteField(write_register,
+ offset,
+ value,
+ temp,
+ GetSaveFPRegsMode(),
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+}
+
+
+void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->object()).is(edx));
+ ASSERT(ToRegister(instr->value()).is(eax));
+
+ __ mov(ecx, 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::ApplyCheckIf(Condition cc, LBoundsCheck* check) {
+ if (FLAG_debug_code && check->hydrogen()->skip_check()) {
+ Label done;
+ __ j(NegateCondition(cc), &done, Label::kNear);
+ __ int3();
+ __ bind(&done);
+ } else {
+ DeoptimizeIf(cc, check->environment());
+ }
+}
+
+
+void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
+ if (instr->hydrogen()->skip_check() && !FLAG_debug_code) return;
+
+ if (instr->index()->IsConstantOperand()) {
+ Immediate immediate =
+ ToImmediate(LConstantOperand::cast(instr->index()),
+ instr->hydrogen()->length()->representation());
+ __ cmp(ToOperand(instr->length()), immediate);
+ Condition condition =
+ instr->hydrogen()->allow_equality() ? below : below_equal;
+ ApplyCheckIf(condition, instr);
+ } else {
+ __ cmp(ToRegister(instr->index()), ToOperand(instr->length()));
+ Condition condition =
+ instr->hydrogen()->allow_equality() ? above : above_equal;
+ ApplyCheckIf(condition, instr);
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) {
+ ElementsKind elements_kind = instr->elements_kind();
+ LOperand* key = instr->key();
+ if (!key->IsConstantOperand() &&
+ ExternalArrayOpRequiresTemp(instr->hydrogen()->key()->representation(),
+ elements_kind)) {
+ __ SmiUntag(ToRegister(key));
+ }
+ Operand operand(BuildFastArrayOperand(
+ instr->elements(),
+ key,
+ instr->hydrogen()->key()->representation(),
+ elements_kind,
+ 0,
+ instr->additional_index()));
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ __ cvtsd2ss(xmm0, ToDoubleRegister(instr->value()));
+ __ movss(operand, xmm0);
+ } else {
+ __ fld(0);
+ __ fstp_s(operand);
+ }
+ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ __ movdbl(operand, ToDoubleRegister(instr->value()));
+ } else {
+ X87Mov(operand, ToX87Register(instr->value()));
+ }
+ } else {
+ Register value = ToRegister(instr->value());
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ case EXTERNAL_BYTE_ELEMENTS:
+ __ mov_b(operand, value);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ mov_w(operand, value);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ mov(operand, value);
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr) {
+ ExternalReference canonical_nan_reference =
+ ExternalReference::address_of_canonical_non_hole_nan();
+ Operand double_store_operand = BuildFastArrayOperand(
+ instr->elements(),
+ instr->key(),
+ instr->hydrogen()->key()->representation(),
+ FAST_DOUBLE_ELEMENTS,
+ FixedDoubleArray::kHeaderSize - kHeapObjectTag,
+ instr->additional_index());
+
+ if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister value = ToDoubleRegister(instr->value());
+
+ if (instr->NeedsCanonicalization()) {
+ Label have_value;
+
+ __ ucomisd(value, value);
+ __ j(parity_odd, &have_value); // NaN.
+
+ __ movdbl(value, Operand::StaticVariable(canonical_nan_reference));
+ __ bind(&have_value);
+ }
+
+ __ movdbl(double_store_operand, value);
+ } else {
+ // Can't use SSE2 in the serializer
+ if (instr->hydrogen()->IsConstantHoleStore()) {
+ // This means we should store the (double) hole. No floating point
+ // registers required.
+ double nan_double = FixedDoubleArray::hole_nan_as_double();
+ uint64_t int_val = BitCast<uint64_t, double>(nan_double);
+ int32_t lower = static_cast<int32_t>(int_val);
+ int32_t upper = static_cast<int32_t>(int_val >> (kBitsPerInt));
+
+ __ mov(double_store_operand, Immediate(lower));
+ Operand double_store_operand2 = BuildFastArrayOperand(
+ instr->elements(),
+ instr->key(),
+ instr->hydrogen()->key()->representation(),
+ FAST_DOUBLE_ELEMENTS,
+ FixedDoubleArray::kHeaderSize - kHeapObjectTag + kPointerSize,
+ instr->additional_index());
+ __ mov(double_store_operand2, Immediate(upper));
+ } else {
+ Label no_special_nan_handling;
+ X87Register value = ToX87Register(instr->value());
+ X87Fxch(value);
+
+ if (instr->NeedsCanonicalization()) {
+ __ fld(0);
+ __ fld(0);
+ __ FCmp();
+
+ __ j(parity_odd, &no_special_nan_handling);
+ __ sub(esp, Immediate(kDoubleSize));
+ __ fst_d(MemOperand(esp, 0));
+ __ cmp(MemOperand(esp, sizeof(kHoleNanLower32)),
+ Immediate(kHoleNanUpper32));
+ __ add(esp, Immediate(kDoubleSize));
+ Label canonicalize;
+ __ j(not_equal, &canonicalize);
+ __ jmp(&no_special_nan_handling);
+ __ bind(&canonicalize);
+ __ fstp(0);
+ __ fld_d(Operand::StaticVariable(canonical_nan_reference));
+ }
+
+ __ bind(&no_special_nan_handling);
+ __ fst_d(double_store_operand);
+ }
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedFixedArray(LStoreKeyed* instr) {
+ Register elements = ToRegister(instr->elements());
+ Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
+
+ Operand operand = BuildFastArrayOperand(
+ instr->elements(),
+ instr->key(),
+ instr->hydrogen()->key()->representation(),
+ FAST_ELEMENTS,
+ FixedArray::kHeaderSize - kHeapObjectTag,
+ instr->additional_index());
+ if (instr->value()->IsRegister()) {
+ __ mov(operand, ToRegister(instr->value()));
+ } else {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ if (IsSmi(operand_value)) {
+ Immediate immediate = ToImmediate(operand_value, Representation::Smi());
+ __ mov(operand, immediate);
+ } else {
+ ASSERT(!IsInteger32(operand_value));
+ Handle<Object> handle_value = ToHandle(operand_value);
+ __ mov(operand, handle_value);
+ }
+ }
+
+ if (instr->hydrogen()->NeedsWriteBarrier()) {
+ ASSERT(instr->value()->IsRegister());
+ Register value = ToRegister(instr->value());
+ ASSERT(!instr->key()->IsConstantOperand());
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ // Compute address of modified element and store it into key register.
+ __ lea(key, operand);
+ __ RecordWrite(elements,
+ key,
+ value,
+ GetSaveFPRegsMode(),
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+}
+
+
+void LCodeGen::DoStoreKeyed(LStoreKeyed* instr) {
+ // By cases...external, fast-double, fast
+ if (instr->is_external()) {
+ DoStoreKeyedExternalArray(instr);
+ } else if (instr->hydrogen()->value()->representation().IsDouble()) {
+ DoStoreKeyedFixedDoubleArray(instr);
+ } else {
+ DoStoreKeyedFixedArray(instr);
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->object()).is(edx));
+ ASSERT(ToRegister(instr->key()).is(ecx));
+ ASSERT(ToRegister(instr->value()).is(eax));
+
+ 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::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
+ Register object = ToRegister(instr->object());
+ Register temp = ToRegister(instr->temp());
+ __ TestJSArrayForAllocationMemento(object, temp);
+ DeoptimizeIf(equal, instr->environment());
+}
+
+
+void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
+ Register object_reg = ToRegister(instr->object());
+
+ Handle<Map> from_map = instr->original_map();
+ Handle<Map> to_map = instr->transitioned_map();
+ ElementsKind from_kind = instr->from_kind();
+ ElementsKind to_kind = instr->to_kind();
+
+ Label not_applicable;
+ bool is_simple_map_transition =
+ IsSimpleMapChangeTransition(from_kind, to_kind);
+ Label::Distance branch_distance =
+ is_simple_map_transition ? Label::kNear : Label::kFar;
+ __ cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map);
+ __ j(not_equal, &not_applicable, branch_distance);
+ if (is_simple_map_transition) {
+ Register new_map_reg = ToRegister(instr->new_map_temp());
+ Handle<Map> map = instr->hydrogen()->transitioned_map();
+ __ mov(FieldOperand(object_reg, HeapObject::kMapOffset),
+ Immediate(map));
+ // Write barrier.
+ ASSERT_NE(instr->temp(), NULL);
+ __ RecordWriteForMap(object_reg, to_map, new_map_reg,
+ ToRegister(instr->temp()),
+ kDontSaveFPRegs);
+ } else {
+ PushSafepointRegistersScope scope(this);
+ if (!object_reg.is(eax)) {
+ __ push(object_reg);
+ }
+ LoadContextFromDeferred(instr->context());
+ if (!object_reg.is(eax)) {
+ __ pop(eax);
+ }
+ __ mov(ebx, to_map);
+ TransitionElementsKindStub stub(from_kind, to_kind);
+ __ CallStub(&stub);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
+ }
+ __ 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_;
+ };
+
+ DeferredStringCharCodeAt* deferred =
+ new(zone()) DeferredStringCharCodeAt(this, instr);
+
+ StringCharLoadGenerator::Generate(masm(),
+ factory(),
+ 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());
+
+ // 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.
+ __ Set(result, Immediate(0));
+
+ PushSafepointRegistersScope scope(this);
+ __ push(string);
+ // Push the index as a smi. This is safe because of the checks in
+ // DoStringCharCodeAt above.
+ STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
+ if (instr->index()->IsConstantOperand()) {
+ Immediate immediate = ToImmediate(LConstantOperand::cast(instr->index()),
+ Representation::Smi());
+ __ push(immediate);
+ } else {
+ Register index = ToRegister(instr->index());
+ __ SmiTag(index);
+ __ push(index);
+ }
+ CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2,
+ instr, instr->context());
+ __ AssertSmi(eax);
+ __ SmiUntag(eax);
+ __ StoreToSafepointRegisterSlot(result, eax);
+}
+
+
+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(zone()) DeferredStringCharFromCode(this, instr);
+
+ ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+ ASSERT(!char_code.is(result));
+
+ __ cmp(char_code, String::kMaxOneByteCharCode);
+ __ j(above, deferred->entry());
+ __ Set(result, Immediate(factory()->single_character_string_cache()));
+ __ mov(result, FieldOperand(result,
+ char_code, times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(result, factory()->undefined_value());
+ __ j(equal, deferred->entry());
+ __ 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.
+ __ Set(result, Immediate(0));
+
+ PushSafepointRegistersScope scope(this);
+ __ SmiTag(char_code);
+ __ push(char_code);
+ CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr, instr->context());
+ __ StoreToSafepointRegisterSlot(result, eax);
+}
+
+
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ EmitPushTaggedOperand(instr->left());
+ EmitPushTaggedOperand(instr->right());
+ StringAddStub stub(instr->hydrogen()->flags());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() || input->IsStackSlot());
+ LOperand* output = instr->result();
+ ASSERT(output->IsDoubleRegister());
+ __ cvtsi2sd(ToDoubleRegister(output), ToOperand(input));
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::DoInteger32ToSmi(LInteger32ToSmi* instr) {
+ Register input = ToRegister(instr->value());
+ __ SmiTag(input);
+ if (!instr->hydrogen()->value()->HasRange() ||
+ !instr->hydrogen()->value()->range()->IsInSmiRange()) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ LOperand* input = instr->value();
+ LOperand* output = instr->result();
+ LOperand* temp = instr->temp();
+
+ __ LoadUint32(ToDoubleRegister(output),
+ ToRegister(input),
+ ToDoubleRegister(temp));
+}
+
+
+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_, instr_->value(), SIGNED_INT32);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LNumberTagI* instr_;
+ };
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ Register reg = ToRegister(input);
+
+ DeferredNumberTagI* deferred = new(zone()) DeferredNumberTagI(this, instr);
+ __ SmiTag(reg);
+ __ j(overflow, deferred->entry());
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
+ class DeferredNumberTagU: public LDeferredCode {
+ public:
+ DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredNumberTagI(instr_, instr_->value(), UNSIGNED_INT32);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LNumberTagU* instr_;
+ };
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ Register reg = ToRegister(input);
+
+ DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr);
+ __ cmp(reg, Immediate(Smi::kMaxValue));
+ __ j(above, deferred->entry());
+ __ SmiTag(reg);
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredNumberTagI(LInstruction* instr,
+ LOperand* value,
+ IntegerSignedness signedness) {
+ Label slow;
+ Register reg = ToRegister(value);
+ Register tmp = reg.is(eax) ? ecx : eax;
+
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this);
+
+ Label done;
+
+ if (signedness == SIGNED_INT32) {
+ // 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.
+ __ SmiUntag(reg);
+ __ xor_(reg, 0x80000000);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope feature_scope(masm(), SSE2);
+ __ cvtsi2sd(xmm0, Operand(reg));
+ } else {
+ __ push(reg);
+ __ fild_s(Operand(esp, 0));
+ __ pop(reg);
+ }
+ } else {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope feature_scope(masm(), SSE2);
+ __ LoadUint32(xmm0, reg,
+ ToDoubleRegister(LNumberTagU::cast(instr)->temp()));
+ } else {
+ // There's no fild variant for unsigned values, so zero-extend to a 64-bit
+ // int manually.
+ __ push(Immediate(0));
+ __ push(reg);
+ __ fild_d(Operand(esp, 0));
+ __ pop(reg);
+ __ pop(reg);
+ }
+ }
+
+ if (FLAG_inline_new) {
+ __ AllocateHeapNumber(reg, tmp, no_reg, &slow);
+ __ jmp(&done, Label::kNear);
+ }
+
+ // 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(reg, Immediate(0));
+ // NumberTagI and NumberTagD use the context from the frame, rather than
+ // the environment's HContext or HInlinedContext value.
+ // They only call Runtime::kAllocateHeapNumber.
+ // The corresponding HChange instructions are added in a phase that does
+ // not have easy access to the local context.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
+ if (!reg.is(eax)) __ mov(reg, eax);
+
+ // Done. Put the value in xmm0 into the value of the allocated heap
+ // number.
+ __ bind(&done);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope feature_scope(masm(), SSE2);
+ __ movdbl(FieldOperand(reg, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ fstp_d(FieldOperand(reg, HeapNumber::kValueOffset));
+ }
+ __ StoreToSafepointRegisterSlot(reg, reg);
+}
+
+
+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_;
+ };
+
+ Register reg = ToRegister(instr->result());
+
+ bool use_sse2 = CpuFeatures::IsSupported(SSE2);
+ if (!use_sse2) {
+ // Put the value to the top of stack
+ X87Register src = ToX87Register(instr->value());
+ X87LoadForUsage(src);
+ }
+
+ DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr);
+ if (FLAG_inline_new) {
+ Register tmp = ToRegister(instr->temp());
+ __ AllocateHeapNumber(reg, tmp, no_reg, deferred->entry());
+ } else {
+ __ jmp(deferred->entry());
+ }
+ __ bind(deferred->exit());
+ if (use_sse2) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ __ movdbl(FieldOperand(reg, HeapNumber::kValueOffset), input_reg);
+ } else {
+ __ fstp_d(FieldOperand(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());
+ __ Set(reg, Immediate(0));
+
+ PushSafepointRegistersScope scope(this);
+ // NumberTagI and NumberTagD use the context from the frame, rather than
+ // the environment's HContext or HInlinedContext value.
+ // They only call Runtime::kAllocateHeapNumber.
+ // The corresponding HChange instructions are added in a phase that does
+ // not have easy access to the local context.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
+ __ StoreToSafepointRegisterSlot(reg, eax);
+}
+
+
+void LCodeGen::DoSmiTag(LSmiTag* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow));
+ __ SmiTag(ToRegister(input));
+}
+
+
+void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
+ LOperand* input = instr->value();
+ Register result = ToRegister(input);
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ if (instr->needs_check()) {
+ __ test(result, Immediate(kSmiTagMask));
+ DeoptimizeIf(not_zero, instr->environment());
+ } else {
+ __ AssertSmi(result);
+ }
+ __ SmiUntag(result);
+}
+
+
+void LCodeGen::EmitNumberUntagDNoSSE2(Register input_reg,
+ Register temp_reg,
+ X87Register res_reg,
+ bool can_convert_undefined_to_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode) {
+ Label load_smi, done;
+
+ X87PrepareToWrite(res_reg);
+ if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) {
+ // Smi check.
+ __ JumpIfSmi(input_reg, &load_smi, Label::kNear);
+
+ // Heap number map check.
+ __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ if (!can_convert_undefined_to_nan) {
+ DeoptimizeIf(not_equal, env);
+ } else {
+ Label heap_number, convert;
+ __ j(equal, &heap_number, Label::kNear);
+
+ // Convert undefined (or hole) to NaN.
+ __ cmp(input_reg, factory()->undefined_value());
+ DeoptimizeIf(not_equal, env);
+
+ __ bind(&convert);
+ ExternalReference nan =
+ ExternalReference::address_of_canonical_non_hole_nan();
+ __ fld_d(Operand::StaticVariable(nan));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&heap_number);
+ }
+ // Heap number to x87 conversion.
+ __ fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
+ if (deoptimize_on_minus_zero) {
+ __ fldz();
+ __ FCmp();
+ __ fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ j(not_zero, &done, Label::kNear);
+
+ // Use general purpose registers to check if we have -0.0
+ __ mov(temp_reg, FieldOperand(input_reg, HeapNumber::kExponentOffset));
+ __ test(temp_reg, Immediate(HeapNumber::kSignMask));
+ __ j(zero, &done, Label::kNear);
+
+ // Pop FPU stack before deoptimizing.
+ __ fstp(0);
+ DeoptimizeIf(not_zero, env);
+ }
+ __ jmp(&done, Label::kNear);
+ } else {
+ ASSERT(mode == NUMBER_CANDIDATE_IS_SMI);
+ }
+
+ __ bind(&load_smi);
+ __ SmiUntag(input_reg); // Untag smi before converting to float.
+ __ push(input_reg);
+ __ fild_s(Operand(esp, 0));
+ __ pop(input_reg);
+ __ SmiTag(input_reg); // Retag smi.
+ __ bind(&done);
+ X87CommitWrite(res_reg);
+}
+
+
+void LCodeGen::EmitNumberUntagD(Register input_reg,
+ Register temp_reg,
+ XMMRegister result_reg,
+ bool can_convert_undefined_to_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode) {
+ Label load_smi, done;
+
+ if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) {
+ // Smi check.
+ __ JumpIfSmi(input_reg, &load_smi, Label::kNear);
+
+ // Heap number map check.
+ __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ if (!can_convert_undefined_to_nan) {
+ DeoptimizeIf(not_equal, env);
+ } else {
+ Label heap_number, convert;
+ __ j(equal, &heap_number, Label::kNear);
+
+ // Convert undefined (and hole) to NaN.
+ __ cmp(input_reg, factory()->undefined_value());
+ DeoptimizeIf(not_equal, env);
+
+ __ bind(&convert);
+ ExternalReference nan =
+ ExternalReference::address_of_canonical_non_hole_nan();
+ __ movdbl(result_reg, Operand::StaticVariable(nan));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&heap_number);
+ }
+ // 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);
+ } else {
+ ASSERT(mode == NUMBER_CANDIDATE_IS_SMI);
+ }
+
+ // Smi to XMM conversion
+ __ bind(&load_smi);
+ __ SmiUntag(input_reg); // Untag smi before converting to float.
+ __ cvtsi2sd(result_reg, Operand(input_reg));
+ __ SmiTag(input_reg); // Retag smi.
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
+ Label done, heap_number;
+ Register input_reg = ToRegister(instr->value());
+
+ // Heap number map check.
+ __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+
+ if (instr->truncating()) {
+ __ j(equal, &heap_number, Label::kNear);
+ // Check for undefined. Undefined is converted to zero for truncating
+ // conversions.
+ __ cmp(input_reg, factory()->undefined_value());
+ __ RecordComment("Deferred TaggedToI: cannot truncate");
+ DeoptimizeIf(not_equal, instr->environment());
+ __ mov(input_reg, 0);
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&heap_number);
+ if (CpuFeatures::IsSupported(SSE3)) {
+ CpuFeatureScope scope(masm(), SSE3);
+ Label convert;
+ // Use more powerful conversion when sse3 is available.
+ // Load x87 register with heap number.
+ __ fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
+ // Get exponent alone and check for too-big exponent.
+ __ mov(input_reg, FieldOperand(input_reg, HeapNumber::kExponentOffset));
+ __ and_(input_reg, HeapNumber::kExponentMask);
+ const uint32_t kTooBigExponent =
+ (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
+ __ cmp(Operand(input_reg), Immediate(kTooBigExponent));
+ __ j(less, &convert, Label::kNear);
+ // Pop FPU stack before deoptimizing.
+ __ fstp(0);
+ __ RecordComment("Deferred TaggedToI: exponent too big");
+ DeoptimizeIf(no_condition, instr->environment());
+
+ // Reserve space for 64 bit answer.
+ __ bind(&convert);
+ __ sub(Operand(esp), Immediate(kDoubleSize));
+ // Do conversion, which cannot fail because we checked the exponent.
+ __ fisttp_d(Operand(esp, 0));
+ __ mov(input_reg, Operand(esp, 0)); // Low word of answer is the result.
+ __ add(Operand(esp), Immediate(kDoubleSize));
+ } else if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister xmm_temp = ToDoubleRegister(instr->temp());
+ __ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ cvttsd2si(input_reg, Operand(xmm0));
+ __ cmp(input_reg, 0x80000000u);
+ __ j(not_equal, &done);
+ // Check if the input was 0x8000000 (kMinInt).
+ // If no, then we got an overflow and we deoptimize.
+ ExternalReference min_int = ExternalReference::address_of_min_int();
+ __ movdbl(xmm_temp, Operand::StaticVariable(min_int));
+ __ ucomisd(xmm_temp, xmm0);
+ DeoptimizeIf(not_equal, instr->environment());
+ DeoptimizeIf(parity_even, instr->environment()); // NaN.
+ } else {
+ UNREACHABLE();
+ }
+ } else if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ // Deoptimize if we don't have a heap number.
+ __ RecordComment("Deferred TaggedToI: not a heap number");
+ DeoptimizeIf(not_equal, instr->environment());
+
+ XMMRegister xmm_temp = ToDoubleRegister(instr->temp());
+ __ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ cvttsd2si(input_reg, Operand(xmm0));
+ __ cvtsi2sd(xmm_temp, Operand(input_reg));
+ __ ucomisd(xmm0, xmm_temp);
+ __ RecordComment("Deferred TaggedToI: lost precision");
+ DeoptimizeIf(not_equal, instr->environment());
+ __ RecordComment("Deferred TaggedToI: NaN");
+ DeoptimizeIf(parity_even, instr->environment()); // NaN.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ test(input_reg, Operand(input_reg));
+ __ j(not_zero, &done);
+ __ movmskpd(input_reg, xmm0);
+ __ and_(input_reg, 1);
+ __ RecordComment("Deferred TaggedToI: minus zero");
+ DeoptimizeIf(not_zero, instr->environment());
+ }
+ } else {
+ UNREACHABLE();
+ }
+ __ 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->value();
+ ASSERT(input->IsRegister());
+ Register input_reg = ToRegister(input);
+ ASSERT(input_reg.is(ToRegister(instr->result())));
+
+ DeferredTaggedToI* deferred = new(zone()) DeferredTaggedToI(this, instr);
+
+ __ JumpIfNotSmi(input_reg, deferred->entry());
+ __ SmiUntag(input_reg);
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredTaggedToINoSSE2(LTaggedToINoSSE2* instr) {
+ Label done, heap_number;
+ Register result_reg = ToRegister(instr->result());
+ Register input_reg = ToRegister(instr->value());
+
+ // Heap number map check.
+ __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ if (instr->truncating()) {
+ __ j(equal, &heap_number, Label::kNear);
+ // Check for undefined. Undefined is converted to zero for truncating
+ // conversions.
+ __ cmp(input_reg, factory()->undefined_value());
+ __ RecordComment("Deferred TaggedToI: cannot truncate");
+ DeoptimizeIf(not_equal, instr->environment());
+ __ xor_(result_reg, result_reg);
+ __ jmp(&done, Label::kFar);
+ __ bind(&heap_number);
+ } else {
+ // Deoptimize if we don't have a heap number.
+ DeoptimizeIf(not_equal, instr->environment());
+ }
+
+ // Surprisingly, all of this crazy bit manipulation is considerably
+ // faster than using the built-in x86 CPU conversion functions (about 6x).
+ Label right_exponent, adjust_bias, zero_result;
+ Register scratch = ToRegister(instr->scratch());
+ Register scratch2 = ToRegister(instr->scratch2());
+ // Get exponent word.
+ __ mov(scratch, FieldOperand(input_reg, HeapNumber::kExponentOffset));
+ // Get exponent alone in scratch2.
+ __ mov(scratch2, scratch);
+ __ and_(scratch2, HeapNumber::kExponentMask);
+ __ shr(scratch2, HeapNumber::kExponentShift);
+ if (instr->truncating()) {
+ __ j(zero, &zero_result);
+ } else {
+ __ j(not_zero, &adjust_bias);
+ __ test(scratch, Immediate(HeapNumber::kMantissaMask));
+ DeoptimizeIf(not_zero, instr->environment());
+ __ cmp(FieldOperand(input_reg, HeapNumber::kMantissaOffset), Immediate(0));
+ DeoptimizeIf(not_equal, instr->environment());
+ __ bind(&adjust_bias);
+ }
+ __ sub(scratch2, Immediate(HeapNumber::kExponentBias));
+ if (!instr->truncating()) {
+ DeoptimizeIf(negative, instr->environment());
+ } else {
+ __ j(negative, &zero_result);
+ }
+
+ // Get the second half of the double. For some exponents we don't
+ // actually need this because the bits get shifted out again, but
+ // it's probably slower to test than just to do it.
+ Register scratch3 = ToRegister(instr->scratch3());
+ __ mov(scratch3, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
+ __ xor_(result_reg, result_reg);
+
+ const uint32_t non_int32_exponent = 31;
+ __ cmp(scratch2, Immediate(non_int32_exponent));
+ // If we have a match of the int32 exponent then skip some logic.
+ __ j(equal, &right_exponent, Label::kNear);
+ // If the number doesn't find in an int32, deopt.
+ DeoptimizeIf(greater, instr->environment());
+
+ // Exponent word in scratch, exponent in scratch2. We know that 0 <= exponent
+ // < 31.
+ __ mov(result_reg, Immediate(31));
+ __ sub(result_reg, scratch2);
+
+ __ bind(&right_exponent);
+
+ // Save off exponent for negative check later.
+ __ mov(scratch2, scratch);
+
+ // Here result_reg is the shift, scratch is the exponent word.
+ // Get the top bits of the mantissa.
+ __ and_(scratch, HeapNumber::kMantissaMask);
+ // Put back the implicit 1.
+ __ or_(scratch, 1 << HeapNumber::kExponentShift);
+ // Shift up the mantissa bits to take up the space the exponent used to
+ // take. We have kExponentShift + 1 significant bits int he low end of the
+ // word. Shift them to the top bits.
+ const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
+ __ shl(scratch, shift_distance);
+ if (!instr->truncating()) {
+ // If not truncating, a non-zero value in the bottom 22 bits means a
+ // non-integral value --> trigger a deopt.
+ __ test(scratch3, Immediate((1 << (32 - shift_distance)) - 1));
+ DeoptimizeIf(not_equal, instr->environment());
+ }
+ // Shift down 22 bits to get the most significant 10 bits or the low
+ // mantissa word.
+ __ shr(scratch3, 32 - shift_distance);
+ __ or_(scratch3, scratch);
+ if (!instr->truncating()) {
+ // If truncating, a non-zero value in the bits that will be shifted away
+ // when adjusting the exponent means rounding --> deopt.
+ __ mov(scratch, 0x1);
+ ASSERT(result_reg.is(ecx));
+ __ shl_cl(scratch);
+ __ dec(scratch);
+ __ test(scratch3, scratch);
+ DeoptimizeIf(not_equal, instr->environment());
+ }
+ // Move down according to the exponent.
+ ASSERT(result_reg.is(ecx));
+ __ shr_cl(scratch3);
+ // Now the unsigned 32-bit answer is in scratch3. We need to move it to
+ // result_reg and we may need to fix the sign.
+ Label negative_result;
+ __ xor_(result_reg, result_reg);
+ __ cmp(scratch2, result_reg);
+ __ j(less, &negative_result, Label::kNear);
+ __ cmp(scratch3, result_reg);
+ __ mov(result_reg, scratch3);
+ // If the result is > MAX_INT, result doesn't fit in signed 32-bit --> deopt.
+ DeoptimizeIf(less, instr->environment());
+ __ jmp(&done, Label::kNear);
+ __ bind(&zero_result);
+ __ xor_(result_reg, result_reg);
+ __ jmp(&done, Label::kNear);
+ __ bind(&negative_result);
+ __ sub(result_reg, scratch3);
+ if (!instr->truncating()) {
+ // -0.0 triggers a deopt.
+ DeoptimizeIf(zero, instr->environment());
+ }
+ // If the negative subtraction overflows into a positive number, there was an
+ // overflow --> deopt.
+ DeoptimizeIf(positive, instr->environment());
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoTaggedToINoSSE2(LTaggedToINoSSE2* instr) {
+ class DeferredTaggedToINoSSE2: public LDeferredCode {
+ public:
+ DeferredTaggedToINoSSE2(LCodeGen* codegen, LTaggedToINoSSE2* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredTaggedToINoSSE2(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LTaggedToINoSSE2* instr_;
+ };
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ Register input_reg = ToRegister(input);
+ ASSERT(input_reg.is(ToRegister(instr->result())));
+
+ DeferredTaggedToINoSSE2* deferred =
+ new(zone()) DeferredTaggedToINoSSE2(this, instr);
+
+ // Smi check.
+ __ JumpIfNotSmi(input_reg, deferred->entry());
+ __ SmiUntag(input_reg); // Untag smi.
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ LOperand* temp = instr->temp();
+ ASSERT(temp == NULL || temp->IsRegister());
+ LOperand* result = instr->result();
+ ASSERT(result->IsDoubleRegister());
+
+ Register input_reg = ToRegister(input);
+ bool deoptimize_on_minus_zero =
+ instr->hydrogen()->deoptimize_on_minus_zero();
+ Register temp_reg = deoptimize_on_minus_zero ? ToRegister(temp) : no_reg;
+
+ HValue* value = instr->hydrogen()->value();
+ NumberUntagDMode mode = value->representation().IsSmi()
+ ? NUMBER_CANDIDATE_IS_SMI : NUMBER_CANDIDATE_IS_ANY_TAGGED;
+
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister result_reg = ToDoubleRegister(result);
+ EmitNumberUntagD(input_reg,
+ temp_reg,
+ result_reg,
+ instr->hydrogen()->can_convert_undefined_to_nan(),
+ deoptimize_on_minus_zero,
+ instr->environment(),
+ mode);
+ } else {
+ EmitNumberUntagDNoSSE2(input_reg,
+ temp_reg,
+ ToX87Register(instr->result()),
+ instr->hydrogen()->can_convert_undefined_to_nan(),
+ deoptimize_on_minus_zero,
+ instr->environment(),
+ mode);
+ }
+}
+
+
+void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsDoubleRegister());
+ LOperand* result = instr->result();
+ ASSERT(result->IsRegister());
+ CpuFeatureScope scope(masm(), SSE2);
+
+ XMMRegister input_reg = ToDoubleRegister(input);
+ Register result_reg = ToRegister(result);
+
+ __ cvttsd2si(result_reg, Operand(input_reg));
+
+ if (instr->truncating()) {
+ // Performs a truncating conversion of a floating point number as used by
+ // the JS bitwise operations.
+ Label fast_case_succeeded;
+ __ cmp(result_reg, 0x80000000u);
+ __ j(not_equal, &fast_case_succeeded);
+ __ sub(esp, Immediate(kDoubleSize));
+ __ movdbl(MemOperand(esp, 0), input_reg);
+ DoubleToIStub stub(esp, result_reg, 0, true);
+ __ call(stub.GetCode(isolate()), RelocInfo::CODE_TARGET);
+ __ add(esp, Immediate(kDoubleSize));
+ __ bind(&fast_case_succeeded);
+ } else {
+ Label done;
+ __ cvtsi2sd(xmm0, Operand(result_reg));
+ __ ucomisd(xmm0, input_reg);
+ DeoptimizeIf(not_equal, instr->environment());
+ DeoptimizeIf(parity_even, instr->environment()); // NaN.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // The integer converted back is equal to the original. We
+ // only have to test if we got -0 as an input.
+ __ test(result_reg, Operand(result_reg));
+ __ j(not_zero, &done, Label::kNear);
+ __ movmskpd(result_reg, input_reg);
+ // Bit 0 contains the sign of the double in input_reg.
+ // If input was positive, we are ok and return 0, otherwise
+ // deoptimize.
+ __ and_(result_reg, 1);
+ DeoptimizeIf(not_zero, instr->environment());
+ }
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsDoubleRegister());
+ LOperand* result = instr->result();
+ ASSERT(result->IsRegister());
+ CpuFeatureScope scope(masm(), SSE2);
+
+ XMMRegister input_reg = ToDoubleRegister(input);
+ Register result_reg = ToRegister(result);
+
+ Label done;
+ __ cvttsd2si(result_reg, Operand(input_reg));
+ __ cvtsi2sd(xmm0, Operand(result_reg));
+ __ ucomisd(xmm0, input_reg);
+ DeoptimizeIf(not_equal, instr->environment());
+ DeoptimizeIf(parity_even, instr->environment()); // NaN.
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // The integer converted back is equal to the original. We
+ // only have to test if we got -0 as an input.
+ __ test(result_reg, Operand(result_reg));
+ __ j(not_zero, &done, Label::kNear);
+ __ movmskpd(result_reg, input_reg);
+ // Bit 0 contains the sign of the double in input_reg.
+ // If input was positive, we are ok and return 0, otherwise
+ // deoptimize.
+ __ and_(result_reg, 1);
+ DeoptimizeIf(not_zero, instr->environment());
+ __ bind(&done);
+ }
+ __ SmiTag(result_reg);
+ DeoptimizeIf(overflow, instr->environment());
+}
+
+
+void LCodeGen::DoCheckSmi(LCheckSmi* instr) {
+ LOperand* input = instr->value();
+ __ test(ToOperand(input), Immediate(kSmiTagMask));
+ DeoptimizeIf(not_zero, instr->environment());
+}
+
+
+void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ LOperand* input = instr->value();
+ __ test(ToOperand(input), Immediate(kSmiTagMask));
+ DeoptimizeIf(zero, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ __ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
+
+ if (instr->hydrogen()->is_interval_check()) {
+ InstanceType first;
+ InstanceType last;
+ instr->hydrogen()->GetCheckInterval(&first, &last);
+
+ __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
+ static_cast<int8_t>(first));
+
+ // If there is only one type in the interval check for equality.
+ if (first == last) {
+ DeoptimizeIf(not_equal, instr->environment());
+ } else {
+ DeoptimizeIf(below, instr->environment());
+ // Omit check for the last type.
+ if (last != LAST_TYPE) {
+ __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
+ static_cast<int8_t>(last));
+ DeoptimizeIf(above, instr->environment());
+ }
+ }
+ } else {
+ uint8_t mask;
+ uint8_t tag;
+ instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
+
+ if (IsPowerOf2(mask)) {
+ ASSERT(tag == 0 || IsPowerOf2(tag));
+ __ test_b(FieldOperand(temp, Map::kInstanceTypeOffset), mask);
+ DeoptimizeIf(tag == 0 ? not_zero : zero, instr->environment());
+ } else {
+ __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
+ __ and_(temp, mask);
+ __ cmp(temp, tag);
+ DeoptimizeIf(not_equal, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
+ Handle<JSFunction> target = instr->hydrogen()->target();
+ if (instr->hydrogen()->target_in_new_space()) {
+ Register reg = ToRegister(instr->value());
+ Handle<Cell> cell = isolate()->factory()->NewCell(target);
+ __ cmp(reg, Operand::ForCell(cell));
+ } else {
+ Operand operand = ToOperand(instr->value());
+ __ cmp(operand, target);
+ }
+ DeoptimizeIf(not_equal, instr->environment());
+}
+
+
+void LCodeGen::DoDeferredInstanceMigration(LCheckMaps* instr, Register object) {
+ {
+ PushSafepointRegistersScope scope(this);
+ __ push(object);
+ __ xor_(esi, esi);
+ __ CallRuntimeSaveDoubles(Runtime::kMigrateInstance);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 1, Safepoint::kNoLazyDeopt);
+
+ __ test(eax, Immediate(kSmiTagMask));
+ }
+ DeoptimizeIf(zero, instr->environment());
+}
+
+
+void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
+ class DeferredCheckMaps: public LDeferredCode {
+ public:
+ DeferredCheckMaps(LCodeGen* codegen, LCheckMaps* instr, Register object)
+ : LDeferredCode(codegen), instr_(instr), object_(object) {
+ SetExit(check_maps());
+ }
+ virtual void Generate() {
+ codegen()->DoDeferredInstanceMigration(instr_, object_);
+ }
+ Label* check_maps() { return &check_maps_; }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LCheckMaps* instr_;
+ Label check_maps_;
+ Register object_;
+ };
+
+ if (instr->hydrogen()->CanOmitMapChecks()) return;
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ Register reg = ToRegister(input);
+
+ SmallMapList* map_set = instr->hydrogen()->map_set();
+
+ DeferredCheckMaps* deferred = NULL;
+ if (instr->hydrogen()->has_migration_target()) {
+ deferred = new(zone()) DeferredCheckMaps(this, instr, reg);
+ __ bind(deferred->check_maps());
+ }
+
+ Label success;
+ for (int i = 0; i < map_set->length() - 1; i++) {
+ Handle<Map> map = map_set->at(i);
+ __ CompareMap(reg, map, &success);
+ __ j(equal, &success);
+ }
+
+ Handle<Map> map = map_set->last();
+ __ CompareMap(reg, map, &success);
+ if (instr->hydrogen()->has_migration_target()) {
+ __ j(not_equal, deferred->entry());
+ } else {
+ DeoptimizeIf(not_equal, instr->environment());
+ }
+
+ __ bind(&success);
+}
+
+
+void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+ XMMRegister value_reg = ToDoubleRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ __ ClampDoubleToUint8(value_reg, xmm0, result_reg);
+}
+
+
+void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
+ ASSERT(instr->unclamped()->Equals(instr->result()));
+ Register value_reg = ToRegister(instr->result());
+ __ ClampUint8(value_reg);
+}
+
+
+void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) {
+ CpuFeatureScope scope(masm(), SSE2);
+
+ ASSERT(instr->unclamped()->Equals(instr->result()));
+ Register input_reg = ToRegister(instr->unclamped());
+ Label is_smi, done, heap_number;
+
+ __ JumpIfSmi(input_reg, &is_smi);
+
+ // Check for heap number
+ __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ __ j(equal, &heap_number, Label::kNear);
+
+ // Check for undefined. Undefined is converted to zero for clamping
+ // conversions.
+ __ cmp(input_reg, factory()->undefined_value());
+ DeoptimizeIf(not_equal, instr->environment());
+ __ mov(input_reg, 0);
+ __ jmp(&done, Label::kNear);
+
+ // Heap number
+ __ bind(&heap_number);
+ __ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ ClampDoubleToUint8(xmm0, xmm1, input_reg);
+ __ jmp(&done, Label::kNear);
+
+ // smi
+ __ bind(&is_smi);
+ __ SmiUntag(input_reg);
+ __ ClampUint8(input_reg);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoClampTToUint8NoSSE2(LClampTToUint8NoSSE2* instr) {
+ Register input_reg = ToRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ Register scratch = ToRegister(instr->scratch());
+ Register scratch2 = ToRegister(instr->scratch2());
+ Register scratch3 = ToRegister(instr->scratch3());
+ Label is_smi, done, heap_number, valid_exponent,
+ largest_value, zero_result, maybe_nan_or_infinity;
+
+ __ JumpIfSmi(input_reg, &is_smi);
+
+ // Check for heap number
+ __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ __ j(equal, &heap_number, Label::kFar);
+
+ // Check for undefined. Undefined is converted to zero for clamping
+ // conversions.
+ __ cmp(input_reg, factory()->undefined_value());
+ DeoptimizeIf(not_equal, instr->environment());
+ __ jmp(&zero_result);
+
+ // Heap number
+ __ bind(&heap_number);
+
+ // Surprisingly, all of the hand-crafted bit-manipulations below are much
+ // faster than the x86 FPU built-in instruction, especially since "banker's
+ // rounding" would be additionally very expensive
+
+ // Get exponent word.
+ __ mov(scratch, FieldOperand(input_reg, HeapNumber::kExponentOffset));
+ __ mov(scratch3, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
+
+ // Test for negative values --> clamp to zero
+ __ test(scratch, scratch);
+ __ j(negative, &zero_result);
+
+ // Get exponent alone in scratch2.
+ __ mov(scratch2, scratch);
+ __ and_(scratch2, HeapNumber::kExponentMask);
+ __ shr(scratch2, HeapNumber::kExponentShift);
+ __ j(zero, &zero_result);
+ __ sub(scratch2, Immediate(HeapNumber::kExponentBias - 1));
+ __ j(negative, &zero_result);
+
+ const uint32_t non_int8_exponent = 7;
+ __ cmp(scratch2, Immediate(non_int8_exponent + 1));
+ // If the exponent is too big, check for special values.
+ __ j(greater, &maybe_nan_or_infinity, Label::kNear);
+
+ __ bind(&valid_exponent);
+ // Exponent word in scratch, exponent in scratch2. We know that 0 <= exponent
+ // < 7. The shift bias is the number of bits to shift the mantissa such that
+ // with an exponent of 7 such the that top-most one is in bit 30, allowing
+ // detection the rounding overflow of a 255.5 to 256 (bit 31 goes from 0 to
+ // 1).
+ int shift_bias = (30 - HeapNumber::kExponentShift) - 7 - 1;
+ __ lea(result_reg, MemOperand(scratch2, shift_bias));
+ // Here result_reg (ecx) is the shift, scratch is the exponent word. Get the
+ // top bits of the mantissa.
+ __ and_(scratch, HeapNumber::kMantissaMask);
+ // Put back the implicit 1 of the mantissa
+ __ or_(scratch, 1 << HeapNumber::kExponentShift);
+ // Shift up to round
+ __ shl_cl(scratch);
+ // Use "banker's rounding" to spec: If fractional part of number is 0.5, then
+ // use the bit in the "ones" place and add it to the "halves" place, which has
+ // the effect of rounding to even.
+ __ mov(scratch2, scratch);
+ const uint32_t one_half_bit_shift = 30 - sizeof(uint8_t) * 8;
+ const uint32_t one_bit_shift = one_half_bit_shift + 1;
+ __ and_(scratch2, Immediate((1 << one_bit_shift) - 1));
+ __ cmp(scratch2, Immediate(1 << one_half_bit_shift));
+ Label no_round;
+ __ j(less, &no_round);
+ Label round_up;
+ __ mov(scratch2, Immediate(1 << one_half_bit_shift));
+ __ j(greater, &round_up);
+ __ test(scratch3, scratch3);
+ __ j(not_zero, &round_up);
+ __ mov(scratch2, scratch);
+ __ and_(scratch2, Immediate(1 << one_bit_shift));
+ __ shr(scratch2, 1);
+ __ bind(&round_up);
+ __ add(scratch, scratch2);
+ __ j(overflow, &largest_value);
+ __ bind(&no_round);
+ __ shr(scratch, 23);
+ __ mov(result_reg, scratch);
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&maybe_nan_or_infinity);
+ // Check for NaN/Infinity, all other values map to 255
+ __ cmp(scratch2, Immediate(HeapNumber::kInfinityOrNanExponent + 1));
+ __ j(not_equal, &largest_value, Label::kNear);
+
+ // Check for NaN, which differs from Infinity in that at least one mantissa
+ // bit is set.
+ __ and_(scratch, HeapNumber::kMantissaMask);
+ __ or_(scratch, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
+ __ j(not_zero, &zero_result); // M!=0 --> NaN
+ // Infinity -> Fall through to map to 255.
+
+ __ bind(&largest_value);
+ __ mov(result_reg, Immediate(255));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&zero_result);
+ __ xor_(result_reg, result_reg);
+ __ jmp(&done);
+
+ // smi
+ __ bind(&is_smi);
+ if (!input_reg.is(result_reg)) {
+ __ mov(result_reg, input_reg);
+ }
+ __ SmiUntag(result_reg);
+ __ ClampUint8(result_reg);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoAllocate(LAllocate* instr) {
+ class DeferredAllocate: public LDeferredCode {
+ public:
+ DeferredAllocate(LCodeGen* codegen, LAllocate* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredAllocate(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LAllocate* instr_;
+ };
+
+ DeferredAllocate* deferred =
+ new(zone()) DeferredAllocate(this, instr);
+
+ Register result = ToRegister(instr->result());
+ Register temp = ToRegister(instr->temp());
+
+ // Allocate memory for the object.
+ AllocationFlags flags = TAG_OBJECT;
+ if (instr->hydrogen()->MustAllocateDoubleAligned()) {
+ flags = static_cast<AllocationFlags>(flags | DOUBLE_ALIGNMENT);
+ }
+ if (instr->hydrogen()->IsOldPointerSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation());
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ flags = static_cast<AllocationFlags>(flags | PRETENURE_OLD_POINTER_SPACE);
+ } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ flags = static_cast<AllocationFlags>(flags | PRETENURE_OLD_DATA_SPACE);
+ }
+
+ if (instr->size()->IsConstantOperand()) {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ Allocate(size, result, temp, no_reg, deferred->entry(), flags);
+ } else {
+ Register size = ToRegister(instr->size());
+ __ Allocate(size, result, temp, no_reg, deferred->entry(), flags);
+ }
+
+ __ bind(deferred->exit());
+
+ if (instr->hydrogen()->MustPrefillWithFiller()) {
+ if (instr->size()->IsConstantOperand()) {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ mov(temp, (size / kPointerSize) - 1);
+ } else {
+ temp = ToRegister(instr->size());
+ __ shr(temp, kPointerSizeLog2);
+ __ dec(temp);
+ }
+ Label loop;
+ __ bind(&loop);
+ __ mov(FieldOperand(result, temp, times_pointer_size, 0),
+ isolate()->factory()->one_pointer_filler_map());
+ __ dec(temp);
+ __ j(not_zero, &loop);
+ }
+}
+
+
+void LCodeGen::DoDeferredAllocate(LAllocate* instr) {
+ 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, Immediate(Smi::FromInt(0)));
+
+ PushSafepointRegistersScope scope(this);
+ if (instr->size()->IsRegister()) {
+ Register size = ToRegister(instr->size());
+ ASSERT(!size.is(result));
+ __ SmiTag(ToRegister(instr->size()));
+ __ push(size);
+ } else {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ push(Immediate(Smi::FromInt(size)));
+ }
+
+ if (instr->hydrogen()->IsOldPointerSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation());
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ CallRuntimeFromDeferred(
+ Runtime::kAllocateInOldPointerSpace, 1, instr, instr->context());
+ } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ CallRuntimeFromDeferred(
+ Runtime::kAllocateInOldDataSpace, 1, instr, instr->context());
+ } else {
+ CallRuntimeFromDeferred(
+ Runtime::kAllocateInNewSpace, 1, instr, instr->context());
+ }
+ __ StoreToSafepointRegisterSlot(result, eax);
+}
+
+
+void LCodeGen::DoToFastProperties(LToFastProperties* instr) {
+ ASSERT(ToRegister(instr->value()).is(eax));
+ __ push(eax);
+ CallRuntime(Runtime::kToFastProperties, 1, instr);
+}
+
+
+void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ Label materialized;
+ // Registers will be used as follows:
+ // ecx = literals array.
+ // ebx = regexp literal.
+ // eax = regexp literal clone.
+ // esi = context.
+ int literal_offset =
+ FixedArray::OffsetOfElementAt(instr->hydrogen()->literal_index());
+ __ LoadHeapObject(ecx, instr->hydrogen()->literals());
+ __ mov(ebx, FieldOperand(ecx, literal_offset));
+ __ cmp(ebx, factory()->undefined_value());
+ __ j(not_equal, &materialized, Label::kNear);
+
+ // Create regexp literal using runtime function
+ // Result will be in eax.
+ __ push(ecx);
+ __ push(Immediate(Smi::FromInt(instr->hydrogen()->literal_index())));
+ __ push(Immediate(instr->hydrogen()->pattern()));
+ __ push(Immediate(instr->hydrogen()->flags()));
+ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr);
+ __ mov(ebx, eax);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+ __ Allocate(size, eax, ecx, edx, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ push(ebx);
+ __ push(Immediate(Smi::FromInt(size)));
+ CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
+ __ pop(ebx);
+
+ __ 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) {
+ __ mov(edx, FieldOperand(ebx, i));
+ __ mov(ecx, FieldOperand(ebx, i + kPointerSize));
+ __ mov(FieldOperand(eax, i), edx);
+ __ mov(FieldOperand(eax, i + kPointerSize), ecx);
+ }
+ if ((size % (2 * kPointerSize)) != 0) {
+ __ mov(edx, FieldOperand(ebx, size - kPointerSize));
+ __ mov(FieldOperand(eax, size - kPointerSize), edx);
+ }
+}
+
+
+void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ // Use the fast case closure allocation code that allocates in new
+ // space for nested functions that don't need literals cloning.
+ bool pretenure = instr->hydrogen()->pretenure();
+ if (!pretenure && instr->hydrogen()->has_no_literals()) {
+ FastNewClosureStub stub(instr->hydrogen()->language_mode(),
+ instr->hydrogen()->is_generator());
+ __ push(Immediate(instr->hydrogen()->shared_info()));
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ } else {
+ __ push(esi);
+ __ push(Immediate(instr->hydrogen()->shared_info()));
+ __ push(Immediate(pretenure ? factory()->true_value()
+ : factory()->false_value()));
+ CallRuntime(Runtime::kNewClosure, 3, instr);
+ }
+}
+
+
+void LCodeGen::DoTypeof(LTypeof* instr) {
+ LOperand* input = instr->value();
+ EmitPushTaggedOperand(input);
+ CallRuntime(Runtime::kTypeof, 1, instr);
+}
+
+
+void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+
+ Condition final_branch_condition =
+ EmitTypeofIs(instr->TrueLabel(chunk_), instr->FalseLabel(chunk_),
+ input, instr->type_literal());
+ if (final_branch_condition != no_condition) {
+ EmitBranch(instr, final_branch_condition);
+ }
+}
+
+
+Condition LCodeGen::EmitTypeofIs(Label* true_label,
+ Label* false_label,
+ Register input,
+ Handle<String> type_name) {
+ Condition final_branch_condition = no_condition;
+ if (type_name->Equals(heap()->number_string())) {
+ __ JumpIfSmi(input, true_label);
+ __ cmp(FieldOperand(input, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ final_branch_condition = equal;
+
+ } else if (type_name->Equals(heap()->string_string())) {
+ __ JumpIfSmi(input, false_label);
+ __ CmpObjectType(input, FIRST_NONSTRING_TYPE, input);
+ __ j(above_equal, false_label);
+ __ test_b(FieldOperand(input, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ final_branch_condition = zero;
+
+ } else if (type_name->Equals(heap()->symbol_string())) {
+ __ JumpIfSmi(input, false_label);
+ __ CmpObjectType(input, SYMBOL_TYPE, input);
+ final_branch_condition = equal;
+
+ } else if (type_name->Equals(heap()->boolean_string())) {
+ __ cmp(input, factory()->true_value());
+ __ j(equal, true_label);
+ __ cmp(input, factory()->false_value());
+ final_branch_condition = equal;
+
+ } else if (FLAG_harmony_typeof && type_name->Equals(heap()->null_string())) {
+ __ cmp(input, factory()->null_value());
+ final_branch_condition = equal;
+
+ } else if (type_name->Equals(heap()->undefined_string())) {
+ __ cmp(input, factory()->undefined_value());
+ __ j(equal, true_label);
+ __ JumpIfSmi(input, false_label);
+ // Check for undetectable objects => true.
+ __ mov(input, FieldOperand(input, HeapObject::kMapOffset));
+ __ test_b(FieldOperand(input, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ final_branch_condition = not_zero;
+
+ } else if (type_name->Equals(heap()->function_string())) {
+ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
+ __ JumpIfSmi(input, false_label);
+ __ 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_string())) {
+ __ JumpIfSmi(input, false_label);
+ if (!FLAG_harmony_typeof) {
+ __ cmp(input, factory()->null_value());
+ __ j(equal, true_label);
+ }
+ __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
+ __ j(below, false_label);
+ __ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ j(above, false_label);
+ // Check for undetectable objects => false.
+ __ test_b(FieldOperand(input, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ final_branch_condition = zero;
+
+ } else {
+ __ jmp(false_label);
+ }
+ return final_branch_condition;
+}
+
+
+void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) {
+ Register temp = ToRegister(instr->temp());
+
+ EmitIsConstructCall(temp);
+ EmitBranch(instr, equal);
+}
+
+
+void LCodeGen::EmitIsConstructCall(Register temp) {
+ // Get the frame pointer for the calling frame.
+ __ mov(temp, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+
+ // Skip the arguments adaptor frame if it exists.
+ Label check_frame_marker;
+ __ cmp(Operand(temp, StandardFrameConstants::kContextOffset),
+ Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ j(not_equal, &check_frame_marker, Label::kNear);
+ __ mov(temp, Operand(temp, StandardFrameConstants::kCallerFPOffset));
+
+ // Check the marker in the calling frame.
+ __ bind(&check_frame_marker);
+ __ cmp(Operand(temp, StandardFrameConstants::kMarkerOffset),
+ Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
+}
+
+
+void LCodeGen::EnsureSpaceForLazyDeopt() {
+ if (!info()->IsStub()) {
+ // 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;
+ __ Nop(padding_size);
+ }
+ }
+ 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) {
+ Deoptimizer::BailoutType type = instr->hydrogen()->type();
+ // TODO(danno): Stubs expect all deopts to be lazy for historical reasons (the
+ // needed return address), even though the implementation of LAZY and EAGER is
+ // now identical. When LAZY is eventually completely folded into EAGER, remove
+ // the special case below.
+ if (info()->IsStub() && type == Deoptimizer::EAGER) {
+ type = Deoptimizer::LAZY;
+ }
+ Comment(";;; deoptimize: %s", instr->hydrogen()->reason());
+ DeoptimizeIf(no_condition, instr->environment(), type);
+}
+
+
+void LCodeGen::DoDummyUse(LDummyUse* instr) {
+ // Nothing to see here, move on!
+}
+
+
+void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) {
+ PushSafepointRegistersScope scope(this);
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ 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;
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ cmp(esp, Operand::StaticVariable(stack_limit));
+ __ j(above_equal, &done, Label::kNear);
+
+ ASSERT(instr->context()->IsRegister());
+ ASSERT(ToRegister(instr->context()).is(esi));
+ StackCheckStub stub;
+ CallCode(stub.GetCode(isolate()), 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(zone()) DeferredStackCheck(this, instr);
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ cmp(esp, Operand::StaticVariable(stack_limit));
+ __ j(below, deferred_stack_check->entry());
+ 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();
+
+ // 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);
+
+ // Normally we record the first unknown OSR value as the entrypoint to the OSR
+ // code, but if there were none, record the entrypoint here.
+ if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset();
+}
+
+
+void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
+ __ cmp(eax, isolate()->factory()->undefined_value());
+ DeoptimizeIf(equal, instr->environment());
+
+ __ cmp(eax, isolate()->factory()->null_value());
+ DeoptimizeIf(equal, instr->environment());
+
+ __ test(eax, Immediate(kSmiTagMask));
+ DeoptimizeIf(zero, instr->environment());
+
+ STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
+ __ CmpObjectType(eax, LAST_JS_PROXY_TYPE, ecx);
+ DeoptimizeIf(below_equal, instr->environment());
+
+ Label use_cache, call_runtime;
+ __ CheckEnumCache(&call_runtime);
+
+ __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
+ __ jmp(&use_cache, Label::kNear);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(eax);
+ CallRuntime(Runtime::kGetPropertyNamesFast, 1, instr);
+
+ __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
+ isolate()->factory()->meta_map());
+ DeoptimizeIf(not_equal, instr->environment());
+ __ bind(&use_cache);
+}
+
+
+void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
+ Register map = ToRegister(instr->map());
+ Register result = ToRegister(instr->result());
+ Label load_cache, done;
+ __ EnumLength(result, map);
+ __ cmp(result, Immediate(Smi::FromInt(0)));
+ __ j(not_equal, &load_cache);
+ __ mov(result, isolate()->factory()->empty_fixed_array());
+ __ jmp(&done);
+
+ __ bind(&load_cache);
+ __ LoadInstanceDescriptors(map, result);
+ __ mov(result,
+ FieldOperand(result, DescriptorArray::kEnumCacheOffset));
+ __ mov(result,
+ FieldOperand(result, FixedArray::SizeFor(instr->idx())));
+ __ bind(&done);
+ __ test(result, result);
+ DeoptimizeIf(equal, instr->environment());
+}
+
+
+void LCodeGen::DoCheckMapValue(LCheckMapValue* instr) {
+ Register object = ToRegister(instr->value());
+ __ cmp(ToRegister(instr->map()),
+ FieldOperand(object, HeapObject::kMapOffset));
+ DeoptimizeIf(not_equal, instr->environment());
+}
+
+
+void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
+ Register object = ToRegister(instr->object());
+ Register index = ToRegister(instr->index());
+
+ Label out_of_object, done;
+ __ cmp(index, Immediate(0));
+ __ j(less, &out_of_object);
+ __ mov(object, FieldOperand(object,
+ index,
+ times_half_pointer_size,
+ JSObject::kHeaderSize));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&out_of_object);
+ __ mov(object, FieldOperand(object, JSObject::kPropertiesOffset));
+ __ neg(index);
+ // Index is now equal to out of object property index plus 1.
+ __ mov(object, FieldOperand(object,
+ index,
+ times_half_pointer_size,
+ FixedArray::kHeaderSize - kPointerSize));
+ __ bind(&done);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/lithium-codegen-ia32.h b/chromium/v8/src/ia32/lithium-codegen-ia32.h
new file mode 100644
index 00000000000..aa8f6c248a3
--- /dev/null
+++ b/chromium/v8/src/ia32/lithium-codegen-ia32.h
@@ -0,0 +1,514 @@
+// Copyright 2012 the V8 project authors. 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_IA32_LITHIUM_CODEGEN_IA32_H_
+#define V8_IA32_LITHIUM_CODEGEN_IA32_H_
+
+#include "ia32/lithium-ia32.h"
+
+#include "checks.h"
+#include "deoptimizer.h"
+#include "ia32/lithium-gap-resolver-ia32.h"
+#include "safepoint-table.h"
+#include "scopes.h"
+#include "v8utils.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class LDeferredCode;
+class LGapNode;
+class SafepointGenerator;
+
+class LCodeGen BASE_EMBEDDED {
+ public:
+ LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info)
+ : zone_(info->zone()),
+ chunk_(static_cast<LPlatformChunk*>(chunk)),
+ masm_(assembler),
+ info_(info),
+ current_block_(-1),
+ current_instruction_(-1),
+ instructions_(chunk->instructions()),
+ deoptimizations_(4, info->zone()),
+ jump_table_(4, info->zone()),
+ deoptimization_literals_(8, info->zone()),
+ inlined_function_count_(0),
+ scope_(info->scope()),
+ status_(UNUSED),
+ translations_(info->zone()),
+ deferred_(8, info->zone()),
+ dynamic_frame_alignment_(false),
+ support_aligned_spilled_doubles_(false),
+ osr_pc_offset_(-1),
+ last_lazy_deopt_pc_(0),
+ frame_is_built_(false),
+ x87_stack_depth_(0),
+ safepoints_(info->zone()),
+ resolver_(this),
+ expected_safepoint_kind_(Safepoint::kSimple),
+ old_position_(RelocInfo::kNoPosition) {
+ 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(); }
+ Zone* zone() const { return zone_; }
+
+ int LookupDestination(int block_id) const {
+ return chunk()->LookupDestination(block_id);
+ }
+
+ bool IsNextEmittedBlock(int block_id) const {
+ return LookupDestination(block_id) == GetNextEmittedBlock();
+ }
+
+ bool NeedsEagerFrame() const {
+ return GetStackSlotCount() > 0 ||
+ info()->is_non_deferred_calling() ||
+ !info()->IsStub() ||
+ info()->requires_frame();
+ }
+ bool NeedsDeferredFrame() const {
+ return !NeedsEagerFrame() && info()->is_deferred_calling();
+ }
+
+ // Support for converting LOperands to assembler types.
+ Operand ToOperand(LOperand* op) const;
+ Register ToRegister(LOperand* op) const;
+ XMMRegister ToDoubleRegister(LOperand* op) const;
+ X87Register ToX87Register(LOperand* op) const;
+
+ bool IsInteger32(LConstantOperand* op) const;
+ bool IsSmi(LConstantOperand* op) const;
+ Immediate ToImmediate(LOperand* op, const Representation& r) const {
+ return Immediate(ToRepresentation(LConstantOperand::cast(op), r));
+ }
+ double ToDouble(LConstantOperand* op) const;
+
+ // Support for non-sse2 (x87) floating point stack handling.
+ // These functions maintain the mapping of physical stack registers to our
+ // virtual registers between instructions.
+ enum X87OperandType { kX87DoubleOperand, kX87FloatOperand, kX87IntOperand };
+
+ void X87Mov(X87Register reg, Operand src,
+ X87OperandType operand = kX87DoubleOperand);
+ void X87Mov(Operand src, X87Register reg);
+
+ void X87PrepareBinaryOp(
+ X87Register left, X87Register right, X87Register result);
+
+ void X87LoadForUsage(X87Register reg);
+ void X87PrepareToWrite(X87Register reg);
+ void X87CommitWrite(X87Register reg);
+
+ Handle<Object> ToHandle(LConstantOperand* op) const;
+
+ // The operand denoting the second word (the one with a higher address) of
+ // a double stack slot.
+ Operand HighOperand(LOperand* op);
+
+ // 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();
+
+ // Finish the code by setting stack height, safepoint, and bailout
+ // information on it.
+ void FinishCode(Handle<Code> code);
+
+ // Deferred code support.
+ void DoDeferredNumberTagD(LNumberTagD* instr);
+
+ enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 };
+ void DoDeferredNumberTagI(LInstruction* instr,
+ LOperand* value,
+ IntegerSignedness signedness);
+
+ void DoDeferredTaggedToI(LTaggedToI* instr);
+ void DoDeferredTaggedToINoSSE2(LTaggedToINoSSE2* instr);
+ void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
+ void DoDeferredStackCheck(LStackCheck* instr);
+ void DoDeferredRandom(LRandom* instr);
+ void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+ void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
+ void DoDeferredAllocate(LAllocate* instr);
+ void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check);
+ void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
+
+ // Parallel move support.
+ void DoParallelMove(LParallelMove* move);
+ void DoGap(LGap* instr);
+
+ // Emit frame translation commands for an environment.
+ void WriteTranslation(LEnvironment* environment, Translation* translation);
+
+ void EnsureRelocSpaceForDeoptimization();
+
+ // 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;
+ }
+
+ LPlatformChunk* chunk() const { return chunk_; }
+ Scope* scope() const { return scope_; }
+ HGraph* graph() const { return chunk()->graph(); }
+
+ int GetNextEmittedBlock() const;
+
+ 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(); }
+
+ void Abort(BailoutReason reason);
+ void FPRINTF_CHECKING Comment(const char* format, ...);
+
+ void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code, zone()); }
+
+ // Code generation passes. Returns true if code generation should
+ // continue.
+ bool GeneratePrologue();
+ bool GenerateBody();
+ bool GenerateDeferredCode();
+ bool GenerateJumpTable();
+ 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* fun,
+ int argc,
+ LInstruction* instr);
+
+ void CallRuntime(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr) {
+ const Runtime::Function* function = Runtime::FunctionForId(id);
+ CallRuntime(function, argc, instr);
+ }
+
+ void CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr,
+ LOperand* context);
+
+ void LoadContextFromDeferred(LOperand* context);
+
+ enum EDIState {
+ EDI_UNINITIALIZED,
+ EDI_CONTAINS_TARGET
+ };
+
+ // Generate a direct call to a known function. Expects the function
+ // to be in edi.
+ void CallKnownFunction(Handle<JSFunction> function,
+ int formal_parameter_count,
+ int arity,
+ LInstruction* instr,
+ CallKind call_kind,
+ EDIState edi_state);
+
+ void RecordSafepointWithLazyDeopt(LInstruction* instr,
+ SafepointMode safepoint_mode);
+
+ void RegisterEnvironmentForDeoptimization(LEnvironment* environment,
+ Safepoint::DeoptMode mode);
+ void DeoptimizeIf(Condition cc,
+ LEnvironment* environment,
+ Deoptimizer::BailoutType bailout_type);
+ void DeoptimizeIf(Condition cc, LEnvironment* environment);
+ void ApplyCheckIf(Condition cc, LBoundsCheck* check);
+
+ void AddToTranslation(LEnvironment* environment,
+ Translation* translation,
+ LOperand* op,
+ bool is_tagged,
+ bool is_uint32,
+ int* object_index_pointer,
+ int* dematerialized_index_pointer);
+ void RegisterDependentCodeForEmbeddedMaps(Handle<Code> code);
+ void PopulateDeoptimizationData(Handle<Code> code);
+ int DefineDeoptimizationLiteral(Handle<Object> literal);
+
+ void PopulateDeoptimizationLiteralsWithInlinedFunctions();
+
+ Register ToRegister(int index) const;
+ XMMRegister ToDoubleRegister(int index) const;
+ X87Register ToX87Register(int index) const;
+ int32_t ToRepresentation(LConstantOperand* op, const Representation& r) const;
+ int32_t ToInteger32(LConstantOperand* op) const;
+ ExternalReference ToExternalReference(LConstantOperand* op) const;
+
+ Operand BuildFastArrayOperand(LOperand* elements_pointer,
+ LOperand* key,
+ Representation key_representation,
+ ElementsKind elements_kind,
+ uint32_t offset,
+ uint32_t additional_index = 0);
+
+ void EmitIntegerMathAbs(LMathAbs* 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 RecordPosition(int position);
+
+ void RecordAndUpdatePosition(int position);
+
+ static Condition TokenToCondition(Token::Value op, bool is_unsigned);
+ void EmitGoto(int block);
+ template<class InstrType>
+ void EmitBranch(InstrType instr, Condition cc);
+ template<class InstrType>
+ void EmitFalseBranch(InstrType instr, Condition cc);
+ void EmitNumberUntagD(
+ Register input,
+ Register temp,
+ XMMRegister result,
+ bool allow_undefined_as_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED);
+
+ void EmitNumberUntagDNoSSE2(
+ Register input,
+ Register temp,
+ X87Register res_reg,
+ bool allow_undefined_as_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED);
+
+ // 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);
+
+ // 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,
+ 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,
+ SmiCheck check_needed);
+
+ // Emits optimized code for %_IsConstructCall().
+ // Caller should branch on equal condition.
+ void EmitIsConstructCall(Register temp);
+
+ // 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,
+ AllocationSiteMode mode);
+
+ void EnsureSpaceForLazyDeopt();
+ void DoLoadKeyedExternalArray(LLoadKeyed* instr);
+ void DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr);
+ void DoLoadKeyedFixedArray(LLoadKeyed* instr);
+ void DoStoreKeyedExternalArray(LStoreKeyed* instr);
+ void DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr);
+ void DoStoreKeyedFixedArray(LStoreKeyed* instr);
+
+ void EmitReturn(LReturn* instr, bool dynamic_frame_alignment);
+
+ // Emits code for pushing either a tagged constant, a (non-double)
+ // register, or a stack slot operand.
+ void EmitPushTaggedOperand(LOperand* operand);
+
+ void X87Fxch(X87Register reg, int other_slot = 0);
+ void X87Fld(Operand src, X87OperandType opts);
+ void X87Free(X87Register reg);
+
+ void FlushX87StackIfNecessary(LInstruction* instr);
+ void EmitFlushX87ForDeopt();
+ bool X87StackContains(X87Register reg);
+ int X87ArrayIndex(X87Register reg);
+ int x87_st2idx(int pos);
+
+#ifdef _MSC_VER
+ // On windows, you may not access the stack more than one page below
+ // the most recently mapped page. To make the allocated area randomly
+ // accessible, we write an arbitrary value to each page in range
+ // esp + offset - page_size .. esp in turn.
+ void MakeSureStackPagesMapped(int offset);
+#endif
+
+ Zone* zone_;
+ LPlatformChunk* const chunk_;
+ MacroAssembler* const masm_;
+ CompilationInfo* const info_;
+
+ int current_block_;
+ int current_instruction_;
+ const ZoneList<LInstruction*>* instructions_;
+ ZoneList<LEnvironment*> deoptimizations_;
+ ZoneList<Deoptimizer::JumpTableEntry> jump_table_;
+ ZoneList<Handle<Object> > deoptimization_literals_;
+ int inlined_function_count_;
+ Scope* const scope_;
+ Status status_;
+ TranslationBuffer translations_;
+ ZoneList<LDeferredCode*> deferred_;
+ bool dynamic_frame_alignment_;
+ bool support_aligned_spilled_doubles_;
+ int osr_pc_offset_;
+ int last_lazy_deopt_pc_;
+ bool frame_is_built_;
+ X87Register x87_stack_[X87Register::kNumAllocatableRegisters];
+ int x87_stack_depth_;
+
+ // 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_;
+
+ int old_position_;
+
+ class PushSafepointRegistersScope BASE_EMBEDDED {
+ public:
+ explicit PushSafepointRegistersScope(LCodeGen* codegen)
+ : codegen_(codegen) {
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kSimple);
+ codegen_->masm_->PushSafepointRegisters();
+ codegen_->expected_safepoint_kind_ = Safepoint::kWithRegisters;
+ ASSERT(codegen_->info()->is_calling());
+ }
+
+ ~PushSafepointRegistersScope() {
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kWithRegisters);
+ codegen_->masm_->PopSafepointRegisters();
+ 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
+
+#endif // V8_IA32_LITHIUM_CODEGEN_IA32_H_
diff --git a/chromium/v8/src/ia32/lithium-gap-resolver-ia32.cc b/chromium/v8/src/ia32/lithium-gap-resolver-ia32.cc
new file mode 100644
index 00000000000..b5bc18bdc96
--- /dev/null
+++ b/chromium/v8/src/ia32/lithium-gap-resolver-ia32.cc
@@ -0,0 +1,549 @@
+// 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "ia32/lithium-gap-resolver-ia32.h"
+#include "ia32/lithium-codegen-ia32.h"
+
+namespace v8 {
+namespace internal {
+
+LGapResolver::LGapResolver(LCodeGen* owner)
+ : cgen_(owner),
+ moves_(32, owner->zone()),
+ source_uses_(),
+ destination_uses_(),
+ spilled_register_(-1) {}
+
+
+void LGapResolver::Resolve(LParallelMove* parallel_move) {
+ ASSERT(HasBeenReset());
+ // 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()) {
+ PerformMove(i);
+ }
+ }
+
+ // 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);
+ }
+ }
+
+ Finish();
+ ASSERT(HasBeenReset());
+}
+
+
+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()) AddMove(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 use operand swaps to resolve cycles,
+ // which means that a call to PerformMove could change any source operand
+ // in the move graph.
+
+ ASSERT(!moves_[index].IsPending());
+ ASSERT(!moves_[index].IsRedundant());
+
+ // Clear this move's destination to indicate a pending move. The actual
+ // destination is saved on the side.
+ 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()) {
+ // Though PerformMove can change any source operand in the move graph,
+ // this call cannot create a blocking move via a swap (this loop does
+ // not miss any). Assume there is a non-blocking move with source A
+ // and this move is blocked on source B and there is a swap of A and
+ // B. Then A and B must be involved in the same cycle (or they would
+ // not be swapped). Since this move's destination is B and there is
+ // only a single incoming edge to an operand, this move must also be
+ // involved in the same cycle. In that case, the blocking move will
+ // be created but will be "pending" when we return from PerformMove.
+ PerformMove(i);
+ }
+ }
+
+ // We are about to resolve this move and don't need it marked as
+ // pending, so restore its destination.
+ moves_[index].set_destination(destination);
+
+ // This move's source may have changed due to swaps to resolve cycles and
+ // so it may now be the last move in the cycle. If so remove it.
+ if (moves_[index].source()->Equals(destination)) {
+ RemoveMove(index);
+ return;
+ }
+
+ // The move may be blocked on a (at most one) pending move, in which case
+ // we have a cycle. Search for such a blocking move and perform a swap to
+ // resolve it.
+ for (int i = 0; i < moves_.length(); ++i) {
+ LMoveOperands other_move = moves_[i];
+ if (other_move.Blocks(destination)) {
+ ASSERT(other_move.IsPending());
+ EmitSwap(index);
+ return;
+ }
+ }
+
+ // This move is not blocked.
+ EmitMove(index);
+}
+
+
+void LGapResolver::AddMove(LMoveOperands move) {
+ LOperand* source = move.source();
+ if (source->IsRegister()) ++source_uses_[source->index()];
+
+ LOperand* destination = move.destination();
+ if (destination->IsRegister()) ++destination_uses_[destination->index()];
+
+ moves_.Add(move, cgen_->zone());
+}
+
+
+void LGapResolver::RemoveMove(int index) {
+ LOperand* source = moves_[index].source();
+ if (source->IsRegister()) {
+ --source_uses_[source->index()];
+ ASSERT(source_uses_[source->index()] >= 0);
+ }
+
+ LOperand* destination = moves_[index].destination();
+ if (destination->IsRegister()) {
+ --destination_uses_[destination->index()];
+ ASSERT(destination_uses_[destination->index()] >= 0);
+ }
+
+ moves_[index].Eliminate();
+}
+
+
+int LGapResolver::CountSourceUses(LOperand* operand) {
+ int count = 0;
+ for (int i = 0; i < moves_.length(); ++i) {
+ if (!moves_[i].IsEliminated() && moves_[i].source()->Equals(operand)) {
+ ++count;
+ }
+ }
+ return count;
+}
+
+
+Register LGapResolver::GetFreeRegisterNot(Register reg) {
+ int skip_index = reg.is(no_reg) ? -1 : Register::ToAllocationIndex(reg);
+ for (int i = 0; i < Register::NumAllocatableRegisters(); ++i) {
+ if (source_uses_[i] == 0 && destination_uses_[i] > 0 && i != skip_index) {
+ return Register::FromAllocationIndex(i);
+ }
+ }
+ return no_reg;
+}
+
+
+bool LGapResolver::HasBeenReset() {
+ if (!moves_.is_empty()) return false;
+ if (spilled_register_ >= 0) return false;
+
+ for (int i = 0; i < Register::NumAllocatableRegisters(); ++i) {
+ if (source_uses_[i] != 0) return false;
+ if (destination_uses_[i] != 0) return false;
+ }
+ return true;
+}
+
+
+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::Finish() {
+ if (spilled_register_ >= 0) {
+ __ pop(Register::FromAllocationIndex(spilled_register_));
+ spilled_register_ = -1;
+ }
+ moves_.Rewind(0);
+}
+
+
+void LGapResolver::EnsureRestored(LOperand* operand) {
+ if (operand->IsRegister() && operand->index() == spilled_register_) {
+ __ pop(Register::FromAllocationIndex(spilled_register_));
+ spilled_register_ = -1;
+ }
+}
+
+
+Register LGapResolver::EnsureTempRegister() {
+ // 1. We may have already spilled to create a temp register.
+ if (spilled_register_ >= 0) {
+ return Register::FromAllocationIndex(spilled_register_);
+ }
+
+ // 2. We may have a free register that we can use without spilling.
+ Register free = GetFreeRegisterNot(no_reg);
+ if (!free.is(no_reg)) return free;
+
+ // 3. Prefer to spill a register that is not used in any remaining move
+ // because it will not need to be restored until the end.
+ for (int i = 0; i < Register::NumAllocatableRegisters(); ++i) {
+ if (source_uses_[i] == 0 && destination_uses_[i] == 0) {
+ Register scratch = Register::FromAllocationIndex(i);
+ __ push(scratch);
+ spilled_register_ = i;
+ return scratch;
+ }
+ }
+
+ // 4. Use an arbitrary register. Register 0 is as arbitrary as any other.
+ Register scratch = Register::FromAllocationIndex(0);
+ __ push(scratch);
+ spilled_register_ = 0;
+ return scratch;
+}
+
+
+void LGapResolver::EmitMove(int index) {
+ LOperand* source = moves_[index].source();
+ LOperand* destination = moves_[index].destination();
+ EnsureRestored(source);
+ EnsureRestored(destination);
+
+ // Dispatch on the source and destination operand kinds. Not all
+ // combinations are possible.
+ if (source->IsRegister()) {
+ ASSERT(destination->IsRegister() || destination->IsStackSlot());
+ Register src = cgen_->ToRegister(source);
+ Operand dst = cgen_->ToOperand(destination);
+ __ mov(dst, src);
+
+ } else if (source->IsStackSlot()) {
+ ASSERT(destination->IsRegister() || destination->IsStackSlot());
+ Operand src = cgen_->ToOperand(source);
+ if (destination->IsRegister()) {
+ Register dst = cgen_->ToRegister(destination);
+ __ mov(dst, src);
+ } else {
+ // Spill on demand to use a temporary register for memory-to-memory
+ // moves.
+ Register tmp = EnsureTempRegister();
+ Operand dst = cgen_->ToOperand(destination);
+ __ mov(tmp, src);
+ __ mov(dst, tmp);
+ }
+
+ } else if (source->IsConstantOperand()) {
+ LConstantOperand* constant_source = LConstantOperand::cast(source);
+ if (destination->IsRegister()) {
+ Register dst = cgen_->ToRegister(destination);
+ Representation r = cgen_->IsSmi(constant_source)
+ ? Representation::Smi() : Representation::Integer32();
+ if (cgen_->IsInteger32(constant_source)) {
+ __ Set(dst, cgen_->ToImmediate(constant_source, r));
+ } else {
+ __ LoadObject(dst, cgen_->ToHandle(constant_source));
+ }
+ } else if (destination->IsDoubleRegister()) {
+ double v = cgen_->ToDouble(constant_source);
+ uint64_t int_val = BitCast<uint64_t, double>(v);
+ int32_t lower = static_cast<int32_t>(int_val);
+ int32_t upper = static_cast<int32_t>(int_val >> kBitsPerInt);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(cgen_->masm(), SSE2);
+ XMMRegister dst = cgen_->ToDoubleRegister(destination);
+ if (int_val == 0) {
+ __ xorps(dst, dst);
+ } else {
+ __ push(Immediate(upper));
+ __ push(Immediate(lower));
+ __ movdbl(dst, Operand(esp, 0));
+ __ add(esp, Immediate(kDoubleSize));
+ }
+ } else {
+ __ push(Immediate(upper));
+ __ push(Immediate(lower));
+ X87Register dst = cgen_->ToX87Register(destination);
+ cgen_->X87Mov(dst, MemOperand(esp, 0));
+ __ add(esp, Immediate(kDoubleSize));
+ }
+ } else {
+ ASSERT(destination->IsStackSlot());
+ Operand dst = cgen_->ToOperand(destination);
+ Representation r = cgen_->IsSmi(constant_source)
+ ? Representation::Smi() : Representation::Integer32();
+ if (cgen_->IsInteger32(constant_source)) {
+ __ Set(dst, cgen_->ToImmediate(constant_source, r));
+ } else {
+ Register tmp = EnsureTempRegister();
+ __ LoadObject(tmp, cgen_->ToHandle(constant_source));
+ __ mov(dst, tmp);
+ }
+ }
+
+ } else if (source->IsDoubleRegister()) {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(cgen_->masm(), SSE2);
+ XMMRegister src = cgen_->ToDoubleRegister(source);
+ if (destination->IsDoubleRegister()) {
+ XMMRegister dst = cgen_->ToDoubleRegister(destination);
+ __ movaps(dst, src);
+ } else {
+ ASSERT(destination->IsDoubleStackSlot());
+ Operand dst = cgen_->ToOperand(destination);
+ __ movdbl(dst, src);
+ }
+ } else {
+ // load from the register onto the stack, store in destination, which must
+ // be a double stack slot in the non-SSE2 case.
+ ASSERT(destination->IsDoubleStackSlot());
+ Operand dst = cgen_->ToOperand(destination);
+ X87Register src = cgen_->ToX87Register(source);
+ cgen_->X87Mov(dst, src);
+ }
+ } else if (source->IsDoubleStackSlot()) {
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope scope(cgen_->masm(), SSE2);
+ ASSERT(destination->IsDoubleRegister() ||
+ destination->IsDoubleStackSlot());
+ Operand src = cgen_->ToOperand(source);
+ if (destination->IsDoubleRegister()) {
+ XMMRegister dst = cgen_->ToDoubleRegister(destination);
+ __ movdbl(dst, src);
+ } else {
+ // We rely on having xmm0 available as a fixed scratch register.
+ Operand dst = cgen_->ToOperand(destination);
+ __ movdbl(xmm0, src);
+ __ movdbl(dst, xmm0);
+ }
+ } else {
+ // load from the stack slot on top of the floating point stack, and then
+ // store in destination. If destination is a double register, then it
+ // represents the top of the stack and nothing needs to be done.
+ if (destination->IsDoubleStackSlot()) {
+ Register tmp = EnsureTempRegister();
+ Operand src0 = cgen_->ToOperand(source);
+ Operand src1 = cgen_->HighOperand(source);
+ Operand dst0 = cgen_->ToOperand(destination);
+ Operand dst1 = cgen_->HighOperand(destination);
+ __ mov(tmp, src0); // Then use tmp to copy source to destination.
+ __ mov(dst0, tmp);
+ __ mov(tmp, src1);
+ __ mov(dst1, tmp);
+ } else {
+ Operand src = cgen_->ToOperand(source);
+ X87Register dst = cgen_->ToX87Register(destination);
+ cgen_->X87Mov(dst, src);
+ }
+ }
+ } else {
+ UNREACHABLE();
+ }
+
+ RemoveMove(index);
+}
+
+
+void LGapResolver::EmitSwap(int index) {
+ LOperand* source = moves_[index].source();
+ LOperand* destination = moves_[index].destination();
+ EnsureRestored(source);
+ EnsureRestored(destination);
+
+ // Dispatch on the source and destination operand kinds. Not all
+ // combinations are possible.
+ if (source->IsRegister() && destination->IsRegister()) {
+ // Register-register.
+ Register src = cgen_->ToRegister(source);
+ Register dst = cgen_->ToRegister(destination);
+ __ xchg(dst, src);
+
+ } else if ((source->IsRegister() && destination->IsStackSlot()) ||
+ (source->IsStackSlot() && destination->IsRegister())) {
+ // Register-memory. Use a free register as a temp if possible. Do not
+ // spill on demand because the simple spill implementation cannot avoid
+ // spilling src at this point.
+ Register tmp = GetFreeRegisterNot(no_reg);
+ Register reg =
+ cgen_->ToRegister(source->IsRegister() ? source : destination);
+ Operand mem =
+ cgen_->ToOperand(source->IsRegister() ? destination : source);
+ if (tmp.is(no_reg)) {
+ __ xor_(reg, mem);
+ __ xor_(mem, reg);
+ __ xor_(reg, mem);
+ } else {
+ __ mov(tmp, mem);
+ __ mov(mem, reg);
+ __ mov(reg, tmp);
+ }
+
+ } else if (source->IsStackSlot() && destination->IsStackSlot()) {
+ // Memory-memory. Spill on demand to use a temporary. If there is a
+ // free register after that, use it as a second temporary.
+ Register tmp0 = EnsureTempRegister();
+ Register tmp1 = GetFreeRegisterNot(tmp0);
+ Operand src = cgen_->ToOperand(source);
+ Operand dst = cgen_->ToOperand(destination);
+ if (tmp1.is(no_reg)) {
+ // Only one temp register available to us.
+ __ mov(tmp0, dst);
+ __ xor_(tmp0, src);
+ __ xor_(src, tmp0);
+ __ xor_(tmp0, src);
+ __ mov(dst, tmp0);
+ } else {
+ __ mov(tmp0, dst);
+ __ mov(tmp1, src);
+ __ mov(dst, tmp1);
+ __ mov(src, tmp0);
+ }
+ } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
+ CpuFeatureScope scope(cgen_->masm(), SSE2);
+ // XMM register-register swap. We rely on having xmm0
+ // available as a fixed scratch register.
+ XMMRegister src = cgen_->ToDoubleRegister(source);
+ XMMRegister dst = cgen_->ToDoubleRegister(destination);
+ __ movaps(xmm0, src);
+ __ movaps(src, dst);
+ __ movaps(dst, xmm0);
+ } else if (source->IsDoubleRegister() || destination->IsDoubleRegister()) {
+ CpuFeatureScope scope(cgen_->masm(), SSE2);
+ // XMM register-memory swap. We rely on having xmm0
+ // available as a fixed scratch register.
+ ASSERT(source->IsDoubleStackSlot() || destination->IsDoubleStackSlot());
+ XMMRegister reg = cgen_->ToDoubleRegister(source->IsDoubleRegister()
+ ? source
+ : destination);
+ Operand other =
+ cgen_->ToOperand(source->IsDoubleRegister() ? destination : source);
+ __ movdbl(xmm0, other);
+ __ movdbl(other, reg);
+ __ movdbl(reg, Operand(xmm0));
+ } else if (source->IsDoubleStackSlot() && destination->IsDoubleStackSlot()) {
+ CpuFeatureScope scope(cgen_->masm(), SSE2);
+ // Double-width memory-to-memory. Spill on demand to use a general
+ // purpose temporary register and also rely on having xmm0 available as
+ // a fixed scratch register.
+ Register tmp = EnsureTempRegister();
+ Operand src0 = cgen_->ToOperand(source);
+ Operand src1 = cgen_->HighOperand(source);
+ Operand dst0 = cgen_->ToOperand(destination);
+ Operand dst1 = cgen_->HighOperand(destination);
+ __ movdbl(xmm0, dst0); // Save destination in xmm0.
+ __ mov(tmp, src0); // Then use tmp to copy source to destination.
+ __ mov(dst0, tmp);
+ __ mov(tmp, src1);
+ __ mov(dst1, tmp);
+ __ movdbl(src0, xmm0);
+
+ } else {
+ // No other combinations are possible.
+ UNREACHABLE();
+ }
+
+ // The swap of source and destination has executed a move from source to
+ // destination.
+ RemoveMove(index);
+
+ // Any unperformed (including pending) move with a source of either
+ // this move's source or destination needs to have their source
+ // changed to reflect the state of affairs after the swap.
+ for (int i = 0; i < moves_.length(); ++i) {
+ LMoveOperands other_move = moves_[i];
+ if (other_move.Blocks(source)) {
+ moves_[i].set_source(destination);
+ } else if (other_move.Blocks(destination)) {
+ moves_[i].set_source(source);
+ }
+ }
+
+ // In addition to swapping the actual uses as sources, we need to update
+ // the use counts.
+ if (source->IsRegister() && destination->IsRegister()) {
+ int temp = source_uses_[source->index()];
+ source_uses_[source->index()] = source_uses_[destination->index()];
+ source_uses_[destination->index()] = temp;
+ } else if (source->IsRegister()) {
+ // We don't have use counts for non-register operands like destination.
+ // Compute those counts now.
+ source_uses_[source->index()] = CountSourceUses(source);
+ } else if (destination->IsRegister()) {
+ source_uses_[destination->index()] = CountSourceUses(destination);
+ }
+}
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/lithium-gap-resolver-ia32.h b/chromium/v8/src/ia32/lithium-gap-resolver-ia32.h
new file mode 100644
index 00000000000..3a58f585c31
--- /dev/null
+++ b/chromium/v8/src/ia32/lithium-gap-resolver-ia32.h
@@ -0,0 +1,110 @@
+// 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_IA32_LITHIUM_GAP_RESOLVER_IA32_H_
+#define V8_IA32_LITHIUM_GAP_RESOLVER_IA32_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);
+
+ // Emit any code necessary at the end of a gap move.
+ void Finish();
+
+ // Add or delete a move from the move graph without emitting any code.
+ // Used to build up the graph and remove trivial moves.
+ void AddMove(LMoveOperands move);
+ void RemoveMove(int index);
+
+ // Report the count of uses of operand as a source in a not-yet-performed
+ // move. Used to rebuild use counts.
+ int CountSourceUses(LOperand* operand);
+
+ // Emit a move and remove it from the move graph.
+ void EmitMove(int index);
+
+ // Execute a move by emitting a swap of two operands. The move from
+ // source to destination is removed from the move graph.
+ void EmitSwap(int index);
+
+ // Ensure that the given operand is not spilled.
+ void EnsureRestored(LOperand* operand);
+
+ // Return a register that can be used as a temp register, spilling
+ // something if necessary.
+ Register EnsureTempRegister();
+
+ // Return a known free register different from the given one (which could
+ // be no_reg---returning any free register), or no_reg if there is no such
+ // register.
+ Register GetFreeRegisterNot(Register reg);
+
+ // Verify that the state is the initial one, ready to resolve a single
+ // parallel move.
+ bool HasBeenReset();
+
+ // Verify the move list before performing moves.
+ void Verify();
+
+ LCodeGen* cgen_;
+
+ // List of moves not yet resolved.
+ ZoneList<LMoveOperands> moves_;
+
+ // Source and destination use counts for the general purpose registers.
+ int source_uses_[Register::kMaxNumAllocatableRegisters];
+ int destination_uses_[Register::kMaxNumAllocatableRegisters];
+
+ // If we had to spill on demand, the currently spilled register's
+ // allocation index.
+ int spilled_register_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_IA32_LITHIUM_GAP_RESOLVER_IA32_H_
diff --git a/chromium/v8/src/ia32/lithium-ia32.cc b/chromium/v8/src/ia32/lithium-ia32.cc
new file mode 100644
index 00000000000..b3158685fcf
--- /dev/null
+++ b/chromium/v8/src/ia32/lithium-ia32.cc
@@ -0,0 +1,2742 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "lithium-allocator-inl.h"
+#include "ia32/lithium-ia32.h"
+#include "ia32/lithium-codegen-ia32.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
+
+
+#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
+
+
+bool LInstruction::HasDoubleRegisterResult() {
+ return HasResult() && result()->IsDoubleRegister();
+}
+
+
+bool LInstruction::HasDoubleRegisterInput() {
+ for (int i = 0; i < InputCount(); i++) {
+ LOperand* op = InputAt(i);
+ if (op != NULL && op->IsDoubleRegister()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool LInstruction::IsDoubleInput(X87Register reg, LCodeGen* cgen) {
+ for (int i = 0; i < InputCount(); i++) {
+ LOperand* op = InputAt(i);
+ if (op != NULL && op->IsDoubleRegister()) {
+ if (cgen->ToX87Register(op).is(reg)) return true;
+ }
+ }
+ return false;
+}
+
+
+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);
+ }
+}
+
+
+void LInstruction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ for (int i = 0; i < InputCount(); i++) {
+ if (i > 0) stream->Add(" ");
+ if (InputAt(i) == NULL) {
+ stream->Add("NULL");
+ } else {
+ InputAt(i)->PrintTo(stream);
+ }
+ }
+}
+
+
+void LInstruction::PrintOutputOperandTo(StringStream* stream) {
+ if (HasResult()) result()->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::ROR: return "ror-t";
+ case Token::SHL: return "sal-t";
+ case Token::SAR: return "sar-t";
+ case Token::SHR: return "shr-t";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+bool LGoto::HasInterestingComment(LCodeGen* gen) const {
+ return !gen->IsNextEmittedBlock(block_id());
+}
+
+
+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());
+ value()->PrintTo(stream);
+}
+
+
+void LCompareNumericAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if ");
+ left()->PrintTo(stream);
+ stream->Add(" %s ", Token::String(op()));
+ right()->PrintTo(stream);
+ stream->Add(" then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_object(");
+ value()->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(");
+ value()->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(");
+ value()->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(");
+ value()->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(");
+ left()->PrintTo(stream);
+ right()->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(");
+ value()->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(");
+ value()->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(");
+ value()->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 ");
+ value()->PrintTo(stream);
+ stream->Add(" == \"%s\" then B%d else B%d",
+ *hydrogen()->type_literal()->ToCString(),
+ true_block_id(), false_block_id());
+}
+
+
+void LInnerAllocatedObject::PrintDataTo(StringStream* stream) {
+ stream->Add(" = ");
+ base_object()->PrintTo(stream);
+ stream->Add(" + %d", offset());
+}
+
+
+void LCallConstantFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("#%d / ", arity());
+}
+
+
+void LLoadContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintTo(stream);
+ stream->Add("[%d]", slot_index());
+}
+
+
+void LStoreContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintTo(stream);
+ stream->Add("[%d] <- ", slot_index());
+ value()->PrintTo(stream);
+}
+
+
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ context()->PrintTo(stream);
+ stream->Add(" ");
+ function()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
+void LCallKeyed::PrintDataTo(StringStream* stream) {
+ stream->Add("[ecx] #%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("= ");
+ context()->PrintTo(stream);
+ stream->Add(" ");
+ constructor()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
+void LCallNewArray::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ context()->PrintTo(stream);
+ stream->Add(" ");
+ constructor()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+ ElementsKind kind = hydrogen()->elements_kind();
+ stream->Add(" (%s) ", ElementsKindToString(kind));
+}
+
+
+void LAccessArgumentsAt::PrintDataTo(StringStream* stream) {
+ arguments()->PrintTo(stream);
+
+ stream->Add(" length ");
+ length()->PrintTo(stream);
+
+ stream->Add(" index ");
+ index()->PrintTo(stream);
+}
+
+
+int LPlatformChunk::GetNextSpillIndex(bool is_double) {
+ // Skip a slot if for a double-width slot.
+ if (is_double) {
+ spill_slot_count_++;
+ spill_slot_count_ |= 1;
+ num_double_slots_++;
+ }
+ return spill_slot_count_++;
+}
+
+
+LOperand* LPlatformChunk::GetNextSpillSlot(bool is_double) {
+ int index = GetNextSpillIndex(is_double);
+ if (is_double) {
+ return LDoubleStackSlot::Create(index, zone());
+ } else {
+ return LStackSlot::Create(index, zone());
+ }
+}
+
+
+void LStoreNamedField::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ hydrogen()->access().PrintTo(stream);
+ 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 LLoadKeyed::PrintDataTo(StringStream* stream) {
+ elements()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ if (hydrogen()->IsDehoisted()) {
+ stream->Add(" + %d]", additional_index());
+ } else {
+ stream->Add("]");
+ }
+}
+
+
+void LStoreKeyed::PrintDataTo(StringStream* stream) {
+ elements()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ if (hydrogen()->IsDehoisted()) {
+ stream->Add(" + %d] <-", additional_index());
+ } else {
+ stream->Add("] <- ");
+ }
+
+ if (value() == NULL) {
+ ASSERT(hydrogen()->IsConstantHoleStore() &&
+ hydrogen()->value()->representation().IsDouble());
+ stream->Add("<the hole(nan)>");
+ } else {
+ 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());
+}
+
+
+LPlatformChunk* LChunkBuilder::Build() {
+ ASSERT(is_unused());
+ chunk_ = new(zone()) LPlatformChunk(info(), graph());
+ LPhase phase("L_Building chunk", chunk_);
+ status_ = BUILDING;
+
+ // Reserve the first spill slot for the state of dynamic alignment.
+ if (info()->IsOptimizing()) {
+ int alignment_state_index = chunk_->GetNextSpillIndex(false);
+ ASSERT_EQ(alignment_state_index, 0);
+ USE(alignment_state_index);
+ }
+
+ 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(BailoutReason reason) {
+ info()->set_bailout_reason(reason);
+ status_ = ABORTED;
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(Register reg) {
+ return new(zone()) LUnallocated(LUnallocated::FIXED_REGISTER,
+ Register::ToAllocationIndex(reg));
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(XMMRegister reg) {
+ return new(zone()) LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER,
+ XMMRegister::ToAllocationIndex(reg));
+}
+
+
+LOperand* LChunkBuilder::UseFixed(HValue* value, Register fixed_register) {
+ return Use(value, ToUnallocated(fixed_register));
+}
+
+
+LOperand* LChunkBuilder::UseFixedDouble(HValue* value, XMMRegister reg) {
+ return Use(value, ToUnallocated(reg));
+}
+
+
+LOperand* LChunkBuilder::UseRegister(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) {
+ return Use(value,
+ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER,
+ LUnallocated::USED_AT_START));
+}
+
+
+LOperand* LChunkBuilder::UseTempRegister(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::WRITABLE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::Use(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::NONE));
+}
+
+
+LOperand* LChunkBuilder::UseAtStart(HValue* value) {
+ return Use(value, new(zone()) 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::UseConstant(HValue* value) {
+ return chunk_->DefineConstantOperand(HConstant::cast(value));
+}
+
+
+LOperand* LChunkBuilder::UseAny(HValue* value) {
+ return value->IsConstant()
+ ? chunk_->DefineConstantOperand(HConstant::cast(value))
+ : Use(value, new(zone()) 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(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineAsSpilled(
+ LTemplateInstruction<1, I, T>* instr,
+ int 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(zone()) 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,
+ XMMRegister reg) {
+ return Define(instr, ToUnallocated(reg));
+}
+
+
+LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
+ HEnvironment* hydrogen_env = current_block_->last_environment();
+ int argument_index_accumulator = 0;
+ ZoneList<HValue*> objects_to_materialize(0, zone());
+ instr->set_environment(CreateEnvironment(hydrogen_env,
+ &argument_index_accumulator,
+ &objects_to_materialize));
+ return instr;
+}
+
+
+LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
+ HInstruction* hinstr,
+ CanDeoptimize can_deoptimize) {
+ info()->MarkAsNonDeferredCalling();
+
+#ifdef DEBUG
+ instr->VerifyCall();
+#endif
+ instr->MarkAsCall();
+ instr = AssignPointerMap(instr);
+
+ if (hinstr->HasObservableSideEffects()) {
+ ASSERT(hinstr->next()->IsSimulate());
+ HSimulate* sim = HSimulate::cast(hinstr->next());
+ ASSERT(instruction_pending_deoptimization_environment_ == NULL);
+ ASSERT(pending_deoptimization_ast_id_.IsNone());
+ instruction_pending_deoptimization_environment_ = instr;
+ pending_deoptimization_ast_id_ = 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::AssignPointerMap(LInstruction* instr) {
+ ASSERT(!instr->HasPointerMap());
+ instr->set_pointer_map(new(zone()) LPointerMap(position_, zone()));
+ return instr;
+}
+
+
+LUnallocated* LChunkBuilder::TempRegister() {
+ LUnallocated* operand =
+ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER);
+ int vreg = allocator_->GetVirtualRegister();
+ if (!allocator_->AllocationOk()) {
+ Abort(kOutOfVirtualRegistersWhileTryingToAllocateTempRegister);
+ vreg = 0;
+ }
+ operand->set_virtual_register(vreg);
+ return operand;
+}
+
+
+LOperand* LChunkBuilder::FixedTemp(Register reg) {
+ LUnallocated* operand = ToUnallocated(reg);
+ ASSERT(operand->HasFixedPolicy());
+ return operand;
+}
+
+
+LOperand* LChunkBuilder::FixedTemp(XMMRegister reg) {
+ LUnallocated* operand = ToUnallocated(reg);
+ ASSERT(operand->HasFixedPolicy());
+ return operand;
+}
+
+
+LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) {
+ return new(zone()) LLabel(instr->block());
+}
+
+
+LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
+ return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value())));
+}
+
+
+LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
+ return AssignEnvironment(new(zone()) LDeoptimize);
+}
+
+
+LInstruction* LChunkBuilder::DoShift(Token::Value op,
+ HBitwiseBinaryOperation* instr) {
+ if (instr->representation().IsTagged()) {
+ ASSERT(instr->left()->representation().IsSmiOrTagged());
+ ASSERT(instr->right()->representation().IsSmiOrTagged());
+
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* left = UseFixed(instr->left(), edx);
+ LOperand* right = UseFixed(instr->right(), eax);
+ LArithmeticT* result = new(zone()) LArithmeticT(op, context, left, right);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+ }
+
+ ASSERT(instr->representation().IsSmiOrInteger32());
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->left());
+
+ HValue* right_value = instr->right();
+ LOperand* right = NULL;
+ int constant_value = 0;
+ bool does_deopt = false;
+ if (right_value->IsConstant()) {
+ HConstant* constant = HConstant::cast(right_value);
+ right = chunk_->DefineConstantOperand(constant);
+ constant_value = constant->Integer32Value() & 0x1f;
+ // Left shifts can deoptimize if we shift by > 0 and the result cannot be
+ // truncated to smi.
+ if (instr->representation().IsSmi() && constant_value > 0) {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToSmi)) {
+ does_deopt = true;
+ break;
+ }
+ }
+ }
+ } else {
+ right = UseFixed(right_value, ecx);
+ }
+
+ // Shift operations can only deoptimize if we do a logical shift by 0 and
+ // the result cannot be truncated to int32.
+ if (op == Token::SHR && constant_value == 0) {
+ if (FLAG_opt_safe_uint32_operations) {
+ does_deopt = !instr->CheckFlag(HInstruction::kUint32);
+ } else {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
+ does_deopt = true;
+ break;
+ }
+ }
+ }
+ }
+
+ LInstruction* result =
+ DefineSameAsFirst(new(zone()) 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->BetterLeftOperand());
+ LOperand* right = UseRegisterAtStart(instr->BetterRightOperand());
+ LArithmeticD* result = new(zone()) LArithmeticD(op, left, right);
+ return DefineSameAsFirst(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* context = UseFixed(instr->context(), esi);
+ LOperand* left_operand = UseFixed(left, edx);
+ LOperand* right_operand = UseFixed(right, eax);
+ LArithmeticT* result =
+ new(zone()) LArithmeticT(op, context, left_operand, right_operand);
+ return MarkAsCall(DefineFixed(result, eax), 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);
+ if (phi->HasMergedIndex()) {
+ last_environment->SetValueAt(phi->merged_index(), phi);
+ }
+ }
+ for (int i = 0; i < block->deleted_phis()->length(); ++i) {
+ if (block->deleted_phis()->at(i) < last_environment->length()) {
+ 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 DEBUG
+ // Make sure that the lithium instruction has either no fixed register
+ // constraints in temps or the result OR no uses that are only used at
+ // start. If this invariant doesn't hold, the register allocator can decide
+ // to insert a split of a range immediately before the instruction due to an
+ // already allocated register needing to be used for the instruction's fixed
+ // register constraint. In this case, The register allocator won't see an
+ // interference between the split child and the use-at-start (it would if
+ // the it was just a plain use), so it is free to move the split child into
+ // the same register that is used for the use-at-start.
+ // See https://code.google.com/p/chromium/issues/detail?id=201590
+ if (!(instr->ClobbersRegisters() && instr->ClobbersDoubleRegisters())) {
+ int fixed = 0;
+ int used_at_start = 0;
+ for (UseIterator it(instr); !it.Done(); it.Advance()) {
+ LUnallocated* operand = LUnallocated::cast(it.Current());
+ if (operand->IsUsedAtStart()) ++used_at_start;
+ }
+ if (instr->Output() != NULL) {
+ if (LUnallocated::cast(instr->Output())->HasFixedPolicy()) ++fixed;
+ }
+ for (TempIterator it(instr); !it.Done(); it.Advance()) {
+ LUnallocated* operand = LUnallocated::cast(it.Current());
+ if (operand->HasFixedPolicy()) ++fixed;
+ }
+ ASSERT(fixed == 0 || used_at_start == 0);
+ }
+#endif
+
+ instr->set_position(position_);
+ 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,
+ ZoneList<HValue*>* objects_to_materialize) {
+ if (hydrogen_env == NULL) return NULL;
+
+ LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
+ argument_index_accumulator,
+ objects_to_materialize);
+ BailoutId ast_id = hydrogen_env->ast_id();
+ ASSERT(!ast_id.IsNone() ||
+ hydrogen_env->frame_type() != JS_FUNCTION);
+ int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
+ LEnvironment* result =
+ new(zone()) LEnvironment(hydrogen_env->closure(),
+ hydrogen_env->frame_type(),
+ ast_id,
+ hydrogen_env->parameter_count(),
+ argument_count_,
+ value_count,
+ outer,
+ hydrogen_env->entry(),
+ zone());
+ int argument_index = *argument_index_accumulator;
+ int object_index = objects_to_materialize->length();
+ for (int i = 0; i < hydrogen_env->length(); ++i) {
+ if (hydrogen_env->is_special_index(i)) continue;
+
+ LOperand* op;
+ HValue* value = hydrogen_env->values()->at(i);
+ if (value->IsArgumentsObject() || value->IsCapturedObject()) {
+ objects_to_materialize->Add(value, zone());
+ op = LEnvironment::materialization_marker();
+ } else if (value->IsPushArgument()) {
+ op = new(zone()) LArgument(argument_index++);
+ } else {
+ op = UseAny(value);
+ }
+ result->AddValue(op,
+ value->representation(),
+ value->CheckFlag(HInstruction::kUint32));
+ }
+
+ for (int i = object_index; i < objects_to_materialize->length(); ++i) {
+ HValue* object_to_materialize = objects_to_materialize->at(i);
+ int previously_materialized_object = -1;
+ for (int prev = 0; prev < i; ++prev) {
+ if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) {
+ previously_materialized_object = prev;
+ break;
+ }
+ }
+ int length = object_to_materialize->OperandCount();
+ bool is_arguments = object_to_materialize->IsArgumentsObject();
+ if (previously_materialized_object >= 0) {
+ result->AddDuplicateObject(previously_materialized_object);
+ continue;
+ } else {
+ result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
+ }
+ for (int i = is_arguments ? 1 : 0; i < length; ++i) {
+ LOperand* op;
+ HValue* value = object_to_materialize->OperandAt(i);
+ if (value->IsArgumentsObject() || value->IsCapturedObject()) {
+ objects_to_materialize->Add(value, zone());
+ op = LEnvironment::materialization_marker();
+ } else {
+ ASSERT(!value->IsPushArgument());
+ op = UseAny(value);
+ }
+ result->AddValue(op,
+ value->representation(),
+ value->CheckFlag(HInstruction::kUint32));
+ }
+ }
+
+ if (hydrogen_env->frame_type() == JS_FUNCTION) {
+ *argument_index_accumulator = argument_index;
+ }
+
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
+ return new(zone()) LGoto(instr->FirstSuccessor()->block_id());
+}
+
+
+LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
+ HValue* value = instr->value();
+ if (value->EmitAtUses()) {
+ ASSERT(value->IsConstant());
+ ASSERT(!value->representation().IsDouble());
+ HBasicBlock* successor = HConstant::cast(value)->BooleanValue()
+ ? instr->FirstSuccessor()
+ : instr->SecondSuccessor();
+ return new(zone()) LGoto(successor->block_id());
+ }
+
+ ToBooleanStub::Types expected = instr->expected_input_types();
+
+ // Tagged values that are not known smis or booleans require a
+ // deoptimization environment. If the instruction is generic no
+ // environment is needed since all cases are handled.
+ Representation rep = value->representation();
+ HType type = value->type();
+ if (!rep.IsTagged() || type.IsSmi() || type.IsBoolean()) {
+ return new(zone()) LBranch(UseRegister(value), NULL);
+ }
+
+ bool needs_temp = expected.NeedsMap() || expected.IsEmpty();
+ LOperand* temp = needs_temp ? TempRegister() : NULL;
+
+ // The Generic stub does not have a deopt, so we need no environment.
+ if (expected.IsGeneric()) {
+ return new(zone()) LBranch(UseRegister(value), temp);
+ }
+
+ // 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).
+ return AssignEnvironment(new(zone()) LBranch(UseRegister(value), temp));
+}
+
+
+LInstruction* LChunkBuilder::DoDebugBreak(HDebugBreak* instr) {
+ return new(zone()) LDebugBreak();
+}
+
+
+LInstruction* LChunkBuilder::DoCompareMap(HCompareMap* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return new(zone()) LCmpMapAndBranch(value);
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* length) {
+ info()->MarkAsRequiresFrame();
+ return DefineAsRegister(new(zone()) LArgumentsLength(Use(length->value())));
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
+ info()->MarkAsRequiresFrame();
+ return DefineAsRegister(new(zone()) LArgumentsElements);
+}
+
+
+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(zone()) LInstanceOf(context, left, right);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
+ HInstanceOfKnownGlobal* instr) {
+ LInstanceOfKnownGlobal* result =
+ new(zone()) LInstanceOfKnownGlobal(
+ UseFixed(instr->context(), esi),
+ UseFixed(instr->left(), InstanceofStub::left()),
+ FixedTemp(edi));
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceSize(HInstanceSize* instr) {
+ LOperand* object = UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new(zone()) LInstanceSize(object));
+}
+
+
+LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) {
+ LOperand* receiver = UseRegister(instr->receiver());
+ LOperand* function = UseRegisterAtStart(instr->function());
+ LOperand* temp = TempRegister();
+ LWrapReceiver* result =
+ new(zone()) LWrapReceiver(receiver, function, temp);
+ return AssignEnvironment(DefineSameAsFirst(result));
+}
+
+
+LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) {
+ LOperand* function = UseFixed(instr->function(), edi);
+ LOperand* receiver = UseFixed(instr->receiver(), eax);
+ LOperand* length = UseFixed(instr->length(), ebx);
+ LOperand* elements = UseFixed(instr->elements(), ecx);
+ LApplyArguments* result = new(zone()) LApplyArguments(function,
+ receiver,
+ length,
+ elements);
+ return MarkAsCall(DefineFixed(result, eax), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
+ ++argument_count_;
+ LOperand* argument = UseAny(instr->argument());
+ return new(zone()) LPushArgument(argument);
+}
+
+
+LInstruction* LChunkBuilder::DoInnerAllocatedObject(
+ HInnerAllocatedObject* inner_object) {
+ LOperand* base_object = UseRegisterAtStart(inner_object->base_object());
+ LInnerAllocatedObject* result =
+ new(zone()) LInnerAllocatedObject(base_object);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoThisFunction(HThisFunction* instr) {
+ return instr->HasNoUses()
+ ? NULL
+ : DefineAsRegister(new(zone()) LThisFunction);
+}
+
+
+LInstruction* LChunkBuilder::DoContext(HContext* instr) {
+ if (instr->HasNoUses()) return NULL;
+
+ if (info()->IsStub()) {
+ return DefineFixed(new(zone()) LContext, esi);
+ }
+
+ return DefineAsRegister(new(zone()) LContext);
+}
+
+
+LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LOuterContext(context));
+}
+
+
+LInstruction* LChunkBuilder::DoDeclareGlobals(HDeclareGlobals* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ return MarkAsCall(new(zone()) LDeclareGlobals(context), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LGlobalObject(context));
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalReceiver(HGlobalReceiver* instr) {
+ LOperand* global_object = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LGlobalReceiver(global_object));
+}
+
+
+LInstruction* LChunkBuilder::DoCallConstantFunction(
+ HCallConstantFunction* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallConstantFunction, eax), instr);
+}
+
+
+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(zone()) LInvokeFunction(context, function);
+ return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
+ switch (instr->op()) {
+ case kMathFloor: return DoMathFloor(instr);
+ case kMathRound: return DoMathRound(instr);
+ case kMathAbs: return DoMathAbs(instr);
+ case kMathLog: return DoMathLog(instr);
+ case kMathSin: return DoMathSin(instr);
+ case kMathCos: return DoMathCos(instr);
+ case kMathTan: return DoMathTan(instr);
+ case kMathExp: return DoMathExp(instr);
+ case kMathSqrt: return DoMathSqrt(instr);
+ case kMathPowHalf: return DoMathPowHalf(instr);
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMathFloor(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathFloor* result = new(zone()) LMathFloor(input);
+ return AssignEnvironment(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoMathRound(HUnaryMathOperation* instr) {
+ LOperand* context = UseAny(instr->context());
+ LOperand* input = UseRegister(instr->value());
+ LOperand* temp = FixedTemp(xmm4);
+ LMathRound* result = new(zone()) LMathRound(context, input, temp);
+ return AssignEnvironment(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoMathAbs(HUnaryMathOperation* instr) {
+ LOperand* context = UseAny(instr->context()); // Deferred use.
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathAbs* result = new(zone()) LMathAbs(context, input);
+ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoMathLog(HUnaryMathOperation* instr) {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->value()->representation().IsDouble());
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathLog* result = new(zone()) LMathLog(input);
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathSin(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), xmm1);
+ LMathSin* result = new(zone()) LMathSin(input);
+ return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathCos(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), xmm1);
+ LMathCos* result = new(zone()) LMathCos(input);
+ return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathTan(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), xmm1);
+ LMathTan* result = new(zone()) LMathTan(input);
+ return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathExp(HUnaryMathOperation* instr) {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->value()->representation().IsDouble());
+ LOperand* value = UseTempRegister(instr->value());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LMathExp* result = new(zone()) LMathExp(value, temp1, temp2);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathSqrt(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathSqrt* result = new(zone()) LMathSqrt(input);
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathPowHalf(HUnaryMathOperation* instr) {
+ LOperand* context = UseAny(instr->context());
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LOperand* temp = TempRegister();
+ LMathPowHalf* result = new(zone()) LMathPowHalf(context, input, temp);
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) {
+ ASSERT(instr->key()->representation().IsTagged());
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* key = UseFixed(instr->key(), ecx);
+ argument_count_ -= instr->argument_count();
+ LCallKeyed* result = new(zone()) LCallKeyed(context, key);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ argument_count_ -= instr->argument_count();
+ LCallNamed* result = new(zone()) LCallNamed(context);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallGlobal(HCallGlobal* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ argument_count_ -= instr->argument_count();
+ 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(zone()) LCallKnownGlobal, eax), instr);
+}
+
+
+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(zone()) LCallNew(context, constructor);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* constructor = UseFixed(instr->constructor(), edi);
+ argument_count_ -= instr->argument_count();
+ LCallNewArray* result = new(zone()) LCallNewArray(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(zone()) LCallFunction(context, function);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) {
+ argument_count_ -= instr->argument_count();
+ LOperand* context = UseFixed(instr->context(), esi);
+ return MarkAsCall(DefineFixed(new(zone()) LCallRuntime(context), eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoRor(HRor* instr) {
+ return DoShift(Token::ROR, 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().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ LOperand* right = UseOrConstantAtStart(instr->BetterRightOperand());
+ return DefineSameAsFirst(new(zone()) LBitI(left, right));
+ } else {
+ ASSERT(instr->representation().IsSmiOrTagged());
+ ASSERT(instr->left()->representation().IsSmiOrTagged());
+ ASSERT(instr->right()->representation().IsSmiOrTagged());
+
+ 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);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
+ if (instr->representation().IsDouble()) {
+ return DoArithmeticD(Token::DIV, instr);
+ } else if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+ LOperand* value = UseRegisterAtStart(instr->left());
+ LDivI* div =
+ new(zone()) LDivI(value, UseOrConstant(instr->right()), NULL);
+ return AssignEnvironment(DefineSameAsFirst(div));
+ }
+ // The temporary operand is necessary to ensure that right is not allocated
+ // into edx.
+ LOperand* temp = FixedTemp(edx);
+ LOperand* dividend = UseFixed(instr->left(), eax);
+ LOperand* divisor = UseRegister(instr->right());
+ LDivI* result = new(zone()) LDivI(dividend, divisor, temp);
+ return AssignEnvironment(DefineFixed(result, eax));
+ } else {
+ ASSERT(instr->representation().IsTagged());
+ return DoArithmeticT(Token::DIV, instr);
+ }
+}
+
+
+HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
+ if (divisor->IsConstant() &&
+ HConstant::cast(divisor)->HasInteger32Value()) {
+ HConstant* constant_val = HConstant::cast(divisor);
+ return constant_val->CopyToRepresentation(Representation::Integer32(),
+ divisor->block()->zone());
+ }
+ // A value with an integer representation does not need to be transformed.
+ if (divisor->representation().IsInteger32()) {
+ return divisor;
+ // A change from an integer32 can be replaced by the integer32 value.
+ } else if (divisor->IsChange() &&
+ HChange::cast(divisor)->from().IsInteger32()) {
+ return HChange::cast(divisor)->value();
+ }
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
+ HValue* right = instr->right();
+ if (!right->IsConstant()) {
+ ASSERT(right->representation().IsInteger32());
+ // The temporary operand is necessary to ensure that right is not allocated
+ // into edx.
+ LOperand* temp = FixedTemp(edx);
+ LOperand* dividend = UseFixed(instr->left(), eax);
+ LOperand* divisor = UseRegister(instr->right());
+ LDivI* flooring_div = new(zone()) LDivI(dividend, divisor, temp);
+ return AssignEnvironment(DefineFixed(flooring_div, eax));
+ }
+
+ ASSERT(right->IsConstant() && HConstant::cast(right)->HasInteger32Value());
+ LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right));
+ int32_t divisor_si = HConstant::cast(right)->Integer32Value();
+ if (divisor_si == 0) {
+ LOperand* dividend = UseRegister(instr->left());
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LMathFloorOfDiv(dividend, divisor, NULL)));
+ } else if (IsPowerOf2(abs(divisor_si))) {
+ // use dividend as temp if divisor < 0 && divisor != -1
+ LOperand* dividend = divisor_si < -1 ? UseTempRegister(instr->left()) :
+ UseRegisterAtStart(instr->left());
+ LInstruction* result = DefineAsRegister(
+ new(zone()) LMathFloorOfDiv(dividend, divisor, NULL));
+ return divisor_si < 0 ? AssignEnvironment(result) : result;
+ } else {
+ // needs edx:eax, plus a temp
+ LOperand* dividend = UseFixed(instr->left(), eax);
+ LOperand* temp = TempRegister();
+ LInstruction* result = DefineFixed(
+ new(zone()) LMathFloorOfDiv(dividend, divisor, temp), edx);
+ return divisor_si < 0 ? AssignEnvironment(result) : result;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMod(HMod* instr) {
+ HValue* left = instr->left();
+ HValue* right = instr->right();
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!right->CanBeZero());
+ LModI* mod = new(zone()) LModI(UseRegisterAtStart(left),
+ UseOrConstant(right),
+ NULL);
+ LInstruction* result = DefineSameAsFirst(mod);
+ return (left->CanBeNegative() &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero))
+ ? AssignEnvironment(result)
+ : result;
+ } else if (instr->fixed_right_arg().has_value) {
+ LModI* mod = new(zone()) LModI(UseRegister(left),
+ UseRegisterAtStart(right),
+ NULL);
+ return AssignEnvironment(DefineSameAsFirst(mod));
+ } else {
+ // The temporary operand is necessary to ensure that right is not
+ // allocated into edx.
+ LModI* mod = new(zone()) LModI(UseFixed(left, eax),
+ UseRegister(right),
+ FixedTemp(edx));
+ LInstruction* result = DefineFixed(mod, edx);
+ return (right->CanBeZero() ||
+ (left->RangeCanInclude(kMinInt) &&
+ right->RangeCanInclude(-1) &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)) ||
+ (left->CanBeNegative() &&
+ instr->CanBeZero() &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)))
+ ? AssignEnvironment(result)
+ : result;
+ }
+ } else if (instr->representation().IsSmiOrTagged()) {
+ 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.
+ LArithmeticD* mod = new(zone()) LArithmeticD(Token::MOD,
+ UseFixedDouble(left, xmm2),
+ UseFixedDouble(right, xmm1));
+ return MarkAsCall(DefineFixedDouble(mod, xmm1), instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMul(HMul* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ LOperand* right = UseOrConstant(instr->BetterRightOperand());
+ LOperand* temp = NULL;
+ if (instr->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ temp = TempRegister();
+ }
+ 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 {
+ ASSERT(instr->representation().IsTagged());
+ return DoArithmeticT(Token::MUL, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoSub(HSub* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ LSubI* sub = new(zone()) LSubI(left, right);
+ LInstruction* result = DefineSameAsFirst(sub);
+ if (instr->CheckFlag(HValue::kCanOverflow)) {
+ result = AssignEnvironment(result);
+ }
+ return result;
+ } else if (instr->representation().IsDouble()) {
+ return DoArithmeticD(Token::SUB, instr);
+ } else {
+ ASSERT(instr->representation().IsSmiOrTagged());
+ return DoArithmeticT(Token::SUB, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ // Check to see if it would be advantageous to use an lea instruction rather
+ // than an add. This is the case when no overflow check is needed and there
+ // are multiple uses of the add's inputs, so using a 3-register add will
+ // preserve all input values for later uses.
+ bool use_lea = LAddI::UseLea(instr);
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ HValue* right_candidate = instr->BetterRightOperand();
+ LOperand* right = use_lea
+ ? UseRegisterOrConstantAtStart(right_candidate)
+ : UseOrConstantAtStart(right_candidate);
+ LAddI* add = new(zone()) LAddI(left, right);
+ bool can_overflow = instr->CheckFlag(HValue::kCanOverflow);
+ LInstruction* result = use_lea
+ ? DefineAsRegister(add)
+ : DefineSameAsFirst(add);
+ if (can_overflow) {
+ result = AssignEnvironment(result);
+ }
+ return result;
+ } else if (instr->representation().IsDouble()) {
+ return DoArithmeticD(Token::ADD, instr);
+ } else {
+ ASSERT(instr->representation().IsSmiOrTagged());
+ return DoArithmeticT(Token::ADD, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) {
+ LOperand* left = NULL;
+ LOperand* right = NULL;
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ left = UseRegisterAtStart(instr->BetterLeftOperand());
+ right = UseOrConstantAtStart(instr->BetterRightOperand());
+ } else {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->left()->representation().IsDouble());
+ ASSERT(instr->right()->representation().IsDouble());
+ left = UseRegisterAtStart(instr->left());
+ right = UseRegisterAtStart(instr->right());
+ }
+ LMathMinMax* minmax = new(zone()) LMathMinMax(left, right);
+ return DefineSameAsFirst(minmax);
+}
+
+
+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(), xmm2);
+ LOperand* right = exponent_type.IsDouble() ?
+ UseFixedDouble(instr->right(), xmm1) :
+ UseFixed(instr->right(), eax);
+ 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().IsSmiOrTagged());
+ 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) {
+ ASSERT(instr->left()->representation().IsSmiOrTagged());
+ ASSERT(instr->right()->representation().IsSmiOrTagged());
+ LOperand* context = UseFixed(instr->context(), esi);
+ 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);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareNumericAndBranch(
+ HCompareNumericAndBranch* instr) {
+ Representation r = instr->representation();
+ if (r.IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(r));
+ ASSERT(instr->right()->representation().Equals(r));
+ LOperand* left = UseRegisterOrConstantAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ return new(zone()) LCompareNumericAndBranch(left, right);
+ } else {
+ ASSERT(r.IsDouble());
+ ASSERT(instr->left()->representation().IsDouble());
+ ASSERT(instr->right()->representation().IsDouble());
+ 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()) LCompareNumericAndBranch(left, right);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch(
+ HCompareObjectEqAndBranch* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ return new(zone()) LCmpObjectEqAndBranch(left, right);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareHoleAndBranch(
+ HCompareHoleAndBranch* instr) {
+ LOperand* object = UseRegisterAtStart(instr->object());
+ return new(zone()) LCmpHoleAndBranch(object);
+}
+
+
+LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsSmiOrTagged());
+ LOperand* temp = TempRegister();
+ return new(zone()) LIsObjectAndBranch(UseRegister(instr->value()), temp);
+}
+
+
+LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* temp = TempRegister();
+ return new(zone()) LIsStringAndBranch(UseRegister(instr->value()), temp);
+}
+
+
+LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LIsSmiAndBranch(Use(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
+ HIsUndetectableAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ 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(zone())
+ LStringCompareAndBranch(context, left, right);
+
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
+ HHasInstanceTypeAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LHasInstanceTypeAndBranch(
+ UseRegisterAtStart(instr->value()),
+ TempRegister());
+}
+
+
+LInstruction* LChunkBuilder::DoGetCachedArrayIndex(
+ HGetCachedArrayIndex* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+
+ return DefineAsRegister(new(zone()) LGetCachedArrayIndex(value));
+}
+
+
+LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch(
+ HHasCachedArrayIndexAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LHasCachedArrayIndexAndBranch(
+ UseRegisterAtStart(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoClassOfTestAndBranch(
+ HClassOfTestAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LClassOfTestAndBranch(UseRegister(instr->value()),
+ TempRegister(),
+ TempRegister());
+}
+
+
+LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
+ LOperand* map = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LMapEnumLength(map));
+}
+
+
+LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
+ LOperand* object = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LElementsKind(object));
+}
+
+
+LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
+ LOperand* object = UseRegister(instr->value());
+ LValueOf* result = new(zone()) LValueOf(object, TempRegister());
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+ LOperand* date = UseFixed(instr->value(), eax);
+ LDateField* result =
+ new(zone()) LDateField(date, FixedTemp(ecx), instr->index());
+ return MarkAsCall(DefineFixed(result, eax), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) {
+ LOperand* string = UseRegister(instr->string());
+ LOperand* index = UseRegister(instr->index());
+ ASSERT(ecx.is_byte_register());
+ LOperand* value = UseFixed(instr->value(), ecx);
+ LSeqStringSetChar* result =
+ new(zone()) LSeqStringSetChar(instr->encoding(), string, index, value);
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
+ return AssignEnvironment(new(zone()) LBoundsCheck(
+ UseRegisterOrConstantAtStart(instr->index()),
+ UseAtStart(instr->length())));
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheckBaseIndexInformation(
+ HBoundsCheckBaseIndexInformation* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+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* context = UseFixed(instr->context(), esi);
+ LOperand* value = UseFixed(instr->value(), eax);
+ return MarkAsCall(new(zone()) LThrow(context, 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.IsSmi()) {
+ if (to.IsTagged()) {
+ LOperand* value = UseRegister(instr->value());
+ return DefineSameAsFirst(new(zone()) LDummyUse(value));
+ }
+ from = Representation::Tagged();
+ }
+ // Only mark conversions that might need to allocate as calling rather than
+ // all changes. This makes simple, non-allocating conversion not have to force
+ // building a stack frame.
+ if (from.IsTagged()) {
+ if (to.IsDouble()) {
+ info()->MarkAsDeferredCalling();
+ LOperand* value = UseRegister(instr->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 if (to.IsSmi()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ if (val->type().IsSmi()) {
+ return DefineSameAsFirst(new(zone()) LDummyUse(value));
+ }
+ return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value)));
+ } else {
+ ASSERT(to.IsInteger32());
+ if (instr->value()->type().IsSmi()) {
+ LOperand* value = UseRegister(instr->value());
+ return DefineSameAsFirst(new(zone()) LSmiUntag(value, false));
+ } else {
+ bool truncating = instr->CanTruncateToInt32();
+ if (CpuFeatures::IsSafeForSnapshot(SSE2)) {
+ LOperand* value = UseRegister(instr->value());
+ LOperand* xmm_temp =
+ (truncating && CpuFeatures::IsSupported(SSE3))
+ ? NULL
+ : FixedTemp(xmm1);
+ LTaggedToI* res = new(zone()) LTaggedToI(value, xmm_temp);
+ return AssignEnvironment(DefineSameAsFirst(res));
+ } else {
+ LOperand* value = UseFixed(instr->value(), ecx);
+ LTaggedToINoSSE2* res =
+ new(zone()) LTaggedToINoSSE2(value, TempRegister(),
+ TempRegister(), TempRegister());
+ return AssignEnvironment(DefineFixed(res, ecx));
+ }
+ }
+ }
+ } else if (from.IsDouble()) {
+ if (to.IsTagged()) {
+ info()->MarkAsDeferredCalling();
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* temp = FLAG_inline_new ? TempRegister() : NULL;
+
+ // Make sure that temp and result_temp are different registers.
+ LUnallocated* result_temp = TempRegister();
+ LNumberTagD* result = new(zone()) LNumberTagD(value, temp);
+ return AssignPointerMap(Define(result, result_temp));
+ } else if (to.IsSmi()) {
+ LOperand* value = UseRegister(instr->value());
+ return AssignEnvironment(
+ DefineAsRegister(new(zone()) LDoubleToSmi(value)));
+ } else {
+ ASSERT(to.IsInteger32());
+ bool truncating = instr->CanTruncateToInt32();
+ bool needs_temp = truncating && !CpuFeatures::IsSupported(SSE3);
+ LOperand* value = needs_temp ?
+ UseTempRegister(instr->value()) : UseRegister(instr->value());
+ LOperand* temp = needs_temp ? TempRegister() : NULL;
+ return AssignEnvironment(
+ DefineAsRegister(new(zone()) LDoubleToI(value, temp)));
+ }
+ } else if (from.IsInteger32()) {
+ info()->MarkAsDeferredCalling();
+ if (to.IsTagged()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ if (val->HasRange() && val->range()->IsInSmiRange()) {
+ return DefineSameAsFirst(new(zone()) LSmiTag(value));
+ } else if (val->CheckFlag(HInstruction::kUint32)) {
+ LOperand* temp = CpuFeatures::IsSupported(SSE2) ? FixedTemp(xmm1)
+ : NULL;
+ LNumberTagU* result = new(zone()) LNumberTagU(value, temp);
+ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
+ } else {
+ LNumberTagI* result = new(zone()) LNumberTagI(value);
+ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
+ }
+ } else if (to.IsSmi()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ LInstruction* result =
+ DefineSameAsFirst(new(zone()) LInteger32ToSmi(value));
+ if (val->HasRange() && val->range()->IsInSmiRange()) {
+ return result;
+ }
+ return AssignEnvironment(result);
+ } else {
+ ASSERT(to.IsDouble());
+ if (instr->value()->CheckFlag(HInstruction::kUint32)) {
+ LOperand* temp = FixedTemp(xmm1);
+ return DefineAsRegister(
+ new(zone()) LUint32ToDouble(UseRegister(instr->value()), temp));
+ } else {
+ return DefineAsRegister(
+ new(zone()) LInteger32ToDouble(Use(instr->value())));
+ }
+ }
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckHeapObject(HCheckHeapObject* instr) {
+ LOperand* value = UseAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckNonSmi(value));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckSmi(value));
+}
+
+
+LInstruction* LChunkBuilder::DoIsNumberAndBranch(HIsNumberAndBranch* instr) {
+ return new(zone())
+ LIsNumberAndBranch(UseRegisterOrConstantAtStart(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* temp = TempRegister();
+ LCheckInstanceType* result = new(zone()) LCheckInstanceType(value, temp);
+ return AssignEnvironment(result);
+}
+
+
+LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) {
+ // 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 = instr->target_in_new_space()
+ ? UseRegisterAtStart(instr->value()) : UseAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckFunction(value));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMaps(HCheckMaps* instr) {
+ LOperand* value = NULL;
+ if (!instr->CanOmitMapChecks()) {
+ value = UseRegisterAtStart(instr->value());
+ if (instr->has_migration_target()) info()->MarkAsDeferredCalling();
+ }
+ LCheckMaps* result = new(zone()) LCheckMaps(value);
+ if (!instr->CanOmitMapChecks()) {
+ AssignEnvironment(result);
+ if (instr->has_migration_target()) return AssignPointerMap(result);
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ if (input_rep.IsDouble()) {
+ LOperand* reg = UseRegister(value);
+ return DefineFixed(new(zone()) LClampDToUint8(reg), eax);
+ } else if (input_rep.IsInteger32()) {
+ LOperand* reg = UseFixed(value, eax);
+ return DefineFixed(new(zone()) LClampIToUint8(reg), eax);
+ } else {
+ ASSERT(input_rep.IsSmiOrTagged());
+ if (CpuFeatures::IsSupported(SSE2)) {
+ 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(zone()) LClampTToUint8(reg, temp);
+ return AssignEnvironment(DefineFixed(result, eax));
+ } else {
+ LOperand* value = UseRegister(instr->value());
+ LClampTToUint8NoSSE2* res =
+ new(zone()) LClampTToUint8NoSSE2(value, TempRegister(),
+ TempRegister(), TempRegister());
+ return AssignEnvironment(DefineFixed(res, ecx));
+ }
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
+ LOperand* context = info()->IsStub()
+ ? UseFixed(instr->context(), esi)
+ : NULL;
+ LOperand* parameter_count = UseRegisterOrConstant(instr->parameter_count());
+ return new(zone()) LReturn(UseFixed(instr->value(), eax), context,
+ parameter_count);
+}
+
+
+LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
+ Representation r = instr->representation();
+ if (r.IsSmi()) {
+ return DefineAsRegister(new(zone()) LConstantS);
+ } else if (r.IsInteger32()) {
+ return DefineAsRegister(new(zone()) LConstantI);
+ } else if (r.IsDouble()) {
+ double value = instr->DoubleValue();
+ bool value_is_zero = BitCast<uint64_t, double>(value) == 0;
+ LOperand* temp = value_is_zero ? NULL : TempRegister();
+ return DefineAsRegister(new(zone()) LConstantD(temp));
+ } else if (r.IsExternal()) {
+ return DefineAsRegister(new(zone()) LConstantE);
+ } else if (r.IsTagged()) {
+ return DefineAsRegister(new(zone()) LConstantT);
+ } else {
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
+ LLoadGlobalCell* result = new(zone()) LLoadGlobalCell;
+ return instr->RequiresHoleCheck()
+ ? AssignEnvironment(DefineAsRegister(result))
+ : DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* global_object = UseFixed(instr->global_object(), edx);
+ LLoadGlobalGeneric* result =
+ new(zone()) LLoadGlobalGeneric(context, global_object);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) {
+ LStoreGlobalCell* result =
+ new(zone()) LStoreGlobalCell(UseRegister(instr->value()));
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* global_object = UseFixed(instr->global_object(), edx);
+ LOperand* value = UseFixed(instr->value(), eax);
+ LStoreGlobalGeneric* result =
+ new(zone()) LStoreGlobalGeneric(context, global_object, value);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ LInstruction* result =
+ DefineAsRegister(new(zone()) LLoadContextSlot(context));
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) {
+ LOperand* value;
+ LOperand* temp;
+ LOperand* context = UseRegister(instr->context());
+ if (instr->NeedsWriteBarrier()) {
+ value = UseTempRegister(instr->value());
+ temp = TempRegister();
+ } else {
+ value = UseRegister(instr->value());
+ temp = NULL;
+ }
+ LInstruction* result = new(zone()) LStoreContextSlot(context, value, temp);
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
+ LOperand* obj = (instr->access().IsExternalMemory() &&
+ instr->access().offset() == 0)
+ ? UseRegisterOrConstantAtStart(instr->object())
+ : UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new(zone()) LLoadNamedField(obj));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* object = UseFixed(instr->object(), edx);
+ LLoadNamedGeneric* result = new(zone()) LLoadNamedGeneric(context, object);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFunctionPrototype(
+ HLoadFunctionPrototype* instr) {
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LLoadFunctionPrototype(UseRegister(instr->function()),
+ TempRegister())));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadExternalArrayPointer(
+ HLoadExternalArrayPointer* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LLoadExternalArrayPointer(input));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyed(HLoadKeyed* instr) {
+ ASSERT(instr->key()->representation().IsSmiOrInteger32());
+ ElementsKind elements_kind = instr->elements_kind();
+ bool clobbers_key = ExternalArrayOpRequiresTemp(
+ instr->key()->representation(), elements_kind);
+ LOperand* key = clobbers_key
+ ? UseTempRegister(instr->key())
+ : UseRegisterOrConstantAtStart(instr->key());
+ LLoadKeyed* result = NULL;
+
+ if (!instr->is_external()) {
+ LOperand* obj = UseRegisterAtStart(instr->elements());
+ result = new(zone()) LLoadKeyed(obj, key);
+ } else {
+ ASSERT(
+ (instr->representation().IsInteger32() &&
+ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
+ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
+ (instr->representation().IsDouble() &&
+ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
+ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
+ LOperand* external_pointer = UseRegister(instr->elements());
+ result = new(zone()) LLoadKeyed(external_pointer, key);
+ }
+
+ DefineAsRegister(result);
+ bool can_deoptimize = instr->RequiresHoleCheck() ||
+ (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS);
+ // An unsigned int array load might overflow and cause a deopt, make sure it
+ // has an environment.
+ return can_deoptimize ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* object = UseFixed(instr->object(), edx);
+ LOperand* key = UseFixed(instr->key(), ecx);
+
+ LLoadKeyedGeneric* result =
+ new(zone()) LLoadKeyedGeneric(context, object, key);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LOperand* LChunkBuilder::GetStoreKeyedValueOperand(HStoreKeyed* instr) {
+ ElementsKind elements_kind = instr->elements_kind();
+
+ // Determine if we need a byte register in this case for the value.
+ bool val_is_fixed_register =
+ elements_kind == EXTERNAL_BYTE_ELEMENTS ||
+ elements_kind == EXTERNAL_UNSIGNED_BYTE_ELEMENTS ||
+ elements_kind == EXTERNAL_PIXEL_ELEMENTS;
+ if (val_is_fixed_register) {
+ return UseFixed(instr->value(), eax);
+ }
+
+ if (!CpuFeatures::IsSafeForSnapshot(SSE2) &&
+ IsDoubleOrFloatElementsKind(elements_kind)) {
+ return UseRegisterAtStart(instr->value());
+ }
+
+ return UseRegister(instr->value());
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) {
+ if (!instr->is_external()) {
+ ASSERT(instr->elements()->representation().IsTagged());
+ ASSERT(instr->key()->representation().IsInteger32() ||
+ instr->key()->representation().IsSmi());
+
+ if (instr->value()->representation().IsDouble()) {
+ LOperand* object = UseRegisterAtStart(instr->elements());
+ LOperand* val = NULL;
+ val = UseRegisterAtStart(instr->value());
+ LOperand* key = UseRegisterOrConstantAtStart(instr->key());
+ return new(zone()) LStoreKeyed(object, key, val);
+ } else {
+ ASSERT(instr->value()->representation().IsSmiOrTagged());
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
+
+ LOperand* obj = UseRegister(instr->elements());
+ LOperand* val;
+ LOperand* key;
+ if (needs_write_barrier) {
+ val = UseTempRegister(instr->value());
+ key = UseTempRegister(instr->key());
+ } else {
+ val = UseRegisterOrConstantAtStart(instr->value());
+ key = UseRegisterOrConstantAtStart(instr->key());
+ }
+ return new(zone()) LStoreKeyed(obj, key, val);
+ }
+ }
+
+ ElementsKind elements_kind = instr->elements_kind();
+ ASSERT(
+ (instr->value()->representation().IsInteger32() &&
+ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
+ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
+ (instr->value()->representation().IsDouble() &&
+ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
+ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
+ ASSERT(instr->elements()->representation().IsExternal());
+
+ LOperand* external_pointer = UseRegister(instr->elements());
+ LOperand* val = GetStoreKeyedValueOperand(instr);
+ bool clobbers_key = ExternalArrayOpRequiresTemp(
+ instr->key()->representation(), elements_kind);
+ LOperand* key = clobbers_key
+ ? UseTempRegister(instr->key())
+ : UseRegisterOrConstantAtStart(instr->key());
+ return new(zone()) LStoreKeyed(external_pointer,
+ key,
+ val);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* object = UseFixed(instr->object(), edx);
+ LOperand* key = UseFixed(instr->key(), ecx);
+ LOperand* value = UseFixed(instr->value(), eax);
+
+ ASSERT(instr->object()->representation().IsTagged());
+ ASSERT(instr->key()->representation().IsTagged());
+ ASSERT(instr->value()->representation().IsTagged());
+
+ LStoreKeyedGeneric* result =
+ new(zone()) LStoreKeyedGeneric(context, object, key, value);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTransitionElementsKind(
+ HTransitionElementsKind* instr) {
+ LOperand* object = UseRegister(instr->object());
+ if (IsSimpleMapChangeTransition(instr->from_kind(), instr->to_kind())) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* new_map_reg = TempRegister();
+ LOperand* temp_reg = TempRegister();
+ LTransitionElementsKind* result =
+ new(zone()) LTransitionElementsKind(object, NULL,
+ new_map_reg, temp_reg);
+ return result;
+ } else {
+ LOperand* context = UseRegister(instr->context());
+ LTransitionElementsKind* result =
+ new(zone()) LTransitionElementsKind(object, context, NULL, NULL);
+ return AssignPointerMap(result);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoTrapAllocationMemento(
+ HTrapAllocationMemento* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* temp = TempRegister();
+ LTrapAllocationMemento* result =
+ new(zone()) LTrapAllocationMemento(object, temp);
+ return AssignEnvironment(result);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
+ bool is_in_object = instr->access().IsInobject();
+ bool is_external_location = instr->access().IsExternalMemory() &&
+ instr->access().offset() == 0;
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
+ bool needs_write_barrier_for_map = instr->has_transition() &&
+ instr->NeedsWriteBarrierForMap();
+
+ LOperand* obj;
+ if (needs_write_barrier) {
+ obj = is_in_object
+ ? UseRegister(instr->object())
+ : UseTempRegister(instr->object());
+ } else if (is_external_location) {
+ ASSERT(!is_in_object);
+ ASSERT(!needs_write_barrier);
+ ASSERT(!needs_write_barrier_for_map);
+ obj = UseRegisterOrConstant(instr->object());
+ } else {
+ obj = needs_write_barrier_for_map
+ ? UseRegister(instr->object())
+ : UseRegisterAtStart(instr->object());
+ }
+
+ bool can_be_constant = instr->value()->IsConstant() &&
+ HConstant::cast(instr->value())->NotInNewSpace() &&
+ !(FLAG_track_double_fields && instr->field_representation().IsDouble());
+
+ LOperand* val;
+ if (needs_write_barrier) {
+ val = UseTempRegister(instr->value());
+ } else if (can_be_constant) {
+ val = UseRegisterOrConstant(instr->value());
+ } else if (FLAG_track_fields && instr->field_representation().IsSmi()) {
+ val = UseTempRegister(instr->value());
+ } else if (FLAG_track_double_fields &&
+ instr->field_representation().IsDouble()) {
+ val = UseRegisterAtStart(instr->value());
+ } else {
+ val = UseRegister(instr->value());
+ }
+
+ // We only need a scratch register if we have a write barrier or we
+ // have a store into the properties array (not in-object-property).
+ LOperand* temp = (!is_in_object || needs_write_barrier ||
+ needs_write_barrier_for_map) ? TempRegister() : NULL;
+
+ // We need a temporary register for write barrier of the map field.
+ LOperand* temp_map = needs_write_barrier_for_map ? TempRegister() : NULL;
+
+ LStoreNamedField* result =
+ new(zone()) LStoreNamedField(obj, val, temp, temp_map);
+ if (FLAG_track_heap_object_fields &&
+ instr->field_representation().IsHeapObject()) {
+ if (!instr->value()->type().IsHeapObject()) {
+ return AssignEnvironment(result);
+ }
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* object = UseFixed(instr->object(), edx);
+ LOperand* value = UseFixed(instr->value(), eax);
+
+ LStoreNamedGeneric* result =
+ new(zone()) LStoreNamedGeneric(context, object, value);
+ return MarkAsCall(result, instr);
+}
+
+
+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(zone()) LStringAdd(context, left, right);
+ return MarkAsCall(DefineFixed(string_add, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
+ LOperand* string = UseTempRegister(instr->string());
+ LOperand* index = UseTempRegister(instr->index());
+ LOperand* context = UseAny(instr->context());
+ LStringCharCodeAt* result =
+ new(zone()) LStringCharCodeAt(context, string, index);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+ LOperand* char_code = UseRegister(instr->value());
+ LOperand* context = UseAny(instr->context());
+ LStringCharFromCode* result =
+ new(zone()) LStringCharFromCode(context, char_code);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) {
+ info()->MarkAsDeferredCalling();
+ LOperand* context = UseAny(instr->context());
+ LOperand* size = instr->size()->IsConstant()
+ ? UseConstant(instr->size())
+ : UseTempRegister(instr->size());
+ LOperand* temp = TempRegister();
+ LAllocate* result = new(zone()) LAllocate(context, size, temp);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ return MarkAsCall(
+ DefineFixed(new(zone()) LRegExpLiteral(context), eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ return MarkAsCall(
+ DefineFixed(new(zone()) LFunctionLiteral(context), eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) {
+ ASSERT(argument_count_ == 0);
+ allocator_->MarkAsOsrEntry();
+ current_block_->last_environment()->set_ast_id(instr->ast_id());
+ return AssignEnvironment(new(zone()) LOsrEntry);
+}
+
+
+LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
+ LParameter* result = new(zone()) LParameter;
+ if (instr->kind() == HParameter::STACK_PARAMETER) {
+ int spill_index = chunk()->GetParameterStackSlot(instr->index());
+ return DefineAsSpilled(result, spill_index);
+ } else {
+ ASSERT(info()->IsStub());
+ CodeStubInterfaceDescriptor* descriptor =
+ info()->code_stub()->GetInterfaceDescriptor(info()->isolate());
+ int index = static_cast<int>(instr->index());
+ Register reg = DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index);
+ return DefineFixed(result, reg);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
+ int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
+ if (spill_index > LUnallocated::kMaxFixedSlotIndex) {
+ Abort(kTooManySpillSlotsNeededForOSR);
+ spill_index = 0;
+ }
+ 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(zone()) LCallStub(context);
+ return MarkAsCall(DefineFixed(result, eax), 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::DoCapturedObject(HCapturedObject* instr) {
+ // There are no real uses of a captured object.
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
+ info()->MarkAsRequiresFrame();
+ LOperand* args = UseRegister(instr->arguments());
+ LOperand* length;
+ LOperand* index;
+ if (instr->length()->IsConstant() && instr->index()->IsConstant()) {
+ length = UseRegisterOrConstant(instr->length());
+ index = UseOrConstant(instr->index());
+ } else {
+ length = UseTempRegister(instr->length());
+ index = Use(instr->index());
+ }
+ return DefineAsRegister(new(zone()) LAccessArgumentsAt(args, length, index));
+}
+
+
+LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) {
+ LOperand* object = UseFixed(instr->value(), eax);
+ LToFastProperties* result = new(zone()) LToFastProperties(object);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* value = UseAtStart(instr->value());
+ LTypeof* result = new(zone()) LTypeof(context, value);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTypeofIsAndBranch(HTypeofIsAndBranch* instr) {
+ return new(zone()) LTypeofIsAndBranch(UseTempRegister(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
+ HIsConstructCallAndBranch* instr) {
+ return new(zone()) 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 = instr->values()->length() - 1; i >= 0; --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_.IsNone()) {
+ ASSERT(pending_deoptimization_ast_id_ == instr->ast_id());
+ LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
+ LInstruction* result = AssignEnvironment(lazy_bailout);
+ // Store the lazy deopt environment with the instruction if needed. Right
+ // now it is only used for LInstanceOfKnownGlobal.
+ instruction_pending_deoptimization_environment_->
+ SetDeferredLazyDeoptimizationEnvironment(result->environment());
+ instruction_pending_deoptimization_environment_ = NULL;
+ pending_deoptimization_ast_id_ = BailoutId::None();
+ return result;
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) {
+ info()->MarkAsDeferredCalling();
+ if (instr->is_function_entry()) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ return MarkAsCall(new(zone()) LStackCheck(context), instr);
+ } else {
+ ASSERT(instr->is_backwards_branch());
+ LOperand* context = UseAny(instr->context());
+ return AssignEnvironment(
+ AssignPointerMap(new(zone()) LStackCheck(context)));
+ }
+}
+
+
+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->inlining_kind(),
+ instr->undefined_receiver());
+ // Only replay binding of arguments object if it wasn't removed from graph.
+ if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
+ inner->Bind(instr->arguments_var(), instr->arguments_object());
+ }
+ inner->set_entry(instr);
+ current_block_->UpdateEnvironment(inner);
+ chunk_->AddInlinedClosure(instr->closure());
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
+ LInstruction* pop = NULL;
+
+ HEnvironment* env = current_block_->last_environment();
+
+ if (env->entry()->arguments_pushed()) {
+ int argument_count = env->arguments_environment()->parameter_count();
+ pop = new(zone()) LDrop(argument_count);
+ argument_count_ -= argument_count;
+ }
+
+ HEnvironment* outer = current_block_->last_environment()->
+ DiscardInlined(false);
+ current_block_->UpdateEnvironment(outer);
+ return pop;
+}
+
+
+LInstruction* LChunkBuilder::DoForInPrepareMap(HForInPrepareMap* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* object = UseFixed(instr->enumerable(), eax);
+ LForInPrepareMap* result = new(zone()) LForInPrepareMap(context, object);
+ return MarkAsCall(DefineFixed(result, eax), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoForInCacheArray(HForInCacheArray* instr) {
+ LOperand* map = UseRegister(instr->map());
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LForInCacheArray(map)));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMapValue(HCheckMapValue* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* map = UseRegisterAtStart(instr->map());
+ return AssignEnvironment(new(zone()) LCheckMapValue(value, map));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* index = UseTempRegister(instr->index());
+ return DefineSameAsFirst(new(zone()) LLoadFieldByIndex(object, index));
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/lithium-ia32.h b/chromium/v8/src/ia32/lithium-ia32.h
new file mode 100644
index 00000000000..7ae87a08c8d
--- /dev/null
+++ b/chromium/v8/src/ia32/lithium-ia32.h
@@ -0,0 +1,2908 @@
+// Copyright 2012 the V8 project authors. 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_IA32_LITHIUM_IA32_H_
+#define V8_IA32_LITHIUM_IA32_H_
+
+#include "hydrogen.h"
+#include "lithium-allocator.h"
+#include "lithium.h"
+#include "safepoint-table.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class LCodeGen;
+
+#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
+ V(AccessArgumentsAt) \
+ V(AddI) \
+ V(Allocate) \
+ V(ApplyArguments) \
+ V(ArgumentsElements) \
+ V(ArgumentsLength) \
+ V(ArithmeticD) \
+ V(ArithmeticT) \
+ V(BitI) \
+ V(BoundsCheck) \
+ V(Branch) \
+ V(CallConstantFunction) \
+ V(CallFunction) \
+ V(CallGlobal) \
+ V(CallKeyed) \
+ V(CallKnownGlobal) \
+ V(CallNamed) \
+ V(CallNew) \
+ V(CallNewArray) \
+ V(CallRuntime) \
+ V(CallStub) \
+ V(CheckFunction) \
+ V(CheckInstanceType) \
+ V(CheckMaps) \
+ V(CheckMapValue) \
+ V(CheckNonSmi) \
+ V(CheckSmi) \
+ V(ClampDToUint8) \
+ V(ClampIToUint8) \
+ V(ClampTToUint8) \
+ V(ClampTToUint8NoSSE2) \
+ V(ClassOfTestAndBranch) \
+ V(CompareNumericAndBranch) \
+ V(CmpObjectEqAndBranch) \
+ V(CmpHoleAndBranch) \
+ V(CmpMapAndBranch) \
+ V(CmpT) \
+ V(ConstantD) \
+ V(ConstantE) \
+ V(ConstantI) \
+ V(ConstantS) \
+ V(ConstantT) \
+ V(Context) \
+ V(DateField) \
+ V(DebugBreak) \
+ V(DeclareGlobals) \
+ V(Deoptimize) \
+ V(DivI) \
+ V(DoubleToI) \
+ V(DoubleToSmi) \
+ V(Drop) \
+ V(DummyUse) \
+ V(ElementsKind) \
+ V(ForInCacheArray) \
+ V(ForInPrepareMap) \
+ V(FunctionLiteral) \
+ V(GetCachedArrayIndex) \
+ V(GlobalObject) \
+ V(GlobalReceiver) \
+ V(Goto) \
+ V(HasCachedArrayIndexAndBranch) \
+ V(HasInstanceTypeAndBranch) \
+ V(InnerAllocatedObject) \
+ V(InstanceOf) \
+ V(InstanceOfKnownGlobal) \
+ V(InstanceSize) \
+ V(InstructionGap) \
+ V(Integer32ToDouble) \
+ V(Integer32ToSmi) \
+ V(InvokeFunction) \
+ V(IsConstructCallAndBranch) \
+ V(IsObjectAndBranch) \
+ V(IsStringAndBranch) \
+ V(IsSmiAndBranch) \
+ V(IsNumberAndBranch) \
+ V(IsUndetectableAndBranch) \
+ V(Label) \
+ V(LazyBailout) \
+ V(LoadContextSlot) \
+ V(LoadExternalArrayPointer) \
+ V(LoadFieldByIndex) \
+ V(LoadFunctionPrototype) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
+ V(LoadKeyed) \
+ V(LoadKeyedGeneric) \
+ V(LoadNamedField) \
+ V(LoadNamedGeneric) \
+ V(MapEnumLength) \
+ V(MathAbs) \
+ V(MathCos) \
+ V(MathExp) \
+ V(MathFloor) \
+ V(MathFloorOfDiv) \
+ V(MathLog) \
+ V(MathMinMax) \
+ V(MathPowHalf) \
+ V(MathRound) \
+ V(MathSin) \
+ V(MathSqrt) \
+ V(MathTan) \
+ V(ModI) \
+ V(MulI) \
+ V(NumberTagD) \
+ V(NumberTagI) \
+ V(NumberTagU) \
+ V(NumberUntagD) \
+ V(OsrEntry) \
+ V(OuterContext) \
+ V(Parameter) \
+ V(Power) \
+ V(Random) \
+ V(PushArgument) \
+ V(RegExpLiteral) \
+ V(Return) \
+ V(SeqStringSetChar) \
+ V(ShiftI) \
+ V(SmiTag) \
+ V(SmiUntag) \
+ V(StackCheck) \
+ V(StoreContextSlot) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
+ V(StoreKeyed) \
+ V(StoreKeyedGeneric) \
+ V(StoreNamedField) \
+ V(StoreNamedGeneric) \
+ V(StringAdd) \
+ V(StringCharCodeAt) \
+ V(StringCharFromCode) \
+ V(StringCompareAndBranch) \
+ V(SubI) \
+ V(TaggedToI) \
+ V(TaggedToINoSSE2) \
+ V(ThisFunction) \
+ V(Throw) \
+ V(ToFastProperties) \
+ V(TransitionElementsKind) \
+ V(TrapAllocationMemento) \
+ V(Typeof) \
+ V(TypeofIsAndBranch) \
+ V(Uint32ToDouble) \
+ V(UnknownOSRValue) \
+ V(ValueOf) \
+ V(WrapReceiver)
+
+
+#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()
+ : environment_(NULL),
+ hydrogen_value_(NULL),
+ bit_field_(IsCallBits::encode(false)) {
+ set_position(RelocInfo::kNoPosition);
+ }
+
+ virtual ~LInstruction() { }
+
+ virtual void CompileToNative(LCodeGen* generator) = 0;
+ virtual const char* Mnemonic() const = 0;
+ virtual void PrintTo(StringStream* stream);
+ virtual void PrintDataTo(StringStream* stream);
+ virtual void PrintOutputOperandTo(StringStream* stream);
+
+ 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(); }
+
+ // The 31 bits PositionBits is used to store the int position value. And the
+ // position value may be RelocInfo::kNoPosition (-1). The accessor always
+ // +1/-1 so that the encoded value of position in bit_field_ is always >= 0
+ // and can fit into the 31 bits PositionBits.
+ void set_position(int pos) {
+ bit_field_ = PositionBits::update(bit_field_, pos + 1);
+ }
+ int position() { return PositionBits::decode(bit_field_) - 1; }
+
+ void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
+ HValue* hydrogen_value() const { return hydrogen_value_; }
+
+ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
+
+ void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
+ bool IsCall() const { return IsCallBits::decode(bit_field_); }
+
+ // Interface to the register allocator and iterators.
+ bool ClobbersTemps() const { return IsCall(); }
+ bool ClobbersRegisters() const { return IsCall(); }
+ virtual bool ClobbersDoubleRegisters() const {
+ return IsCall() ||
+ (!CpuFeatures::IsSupported(SSE2) &&
+ // We only have rudimentary X87Stack tracking, thus in general
+ // cannot handle deoptimization nor phi-nodes.
+ (HasEnvironment() || IsControl()));
+ }
+
+ virtual bool HasResult() const = 0;
+ virtual LOperand* result() const = 0;
+
+ bool HasDoubleRegisterResult();
+ bool HasDoubleRegisterInput();
+ bool IsDoubleInput(X87Register reg, LCodeGen* cgen);
+
+ LOperand* FirstInput() { return InputAt(0); }
+ LOperand* Output() { return HasResult() ? result() : NULL; }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return true; }
+
+#ifdef DEBUG
+ void VerifyCall();
+#endif
+
+ private:
+ // Iterator support.
+ friend class InputIterator;
+ virtual int InputCount() = 0;
+ virtual LOperand* InputAt(int i) = 0;
+
+ friend class TempIterator;
+ virtual int TempCount() = 0;
+ virtual LOperand* TempAt(int i) = 0;
+
+ class IsCallBits: public BitField<bool, 0, 1> {};
+ class PositionBits: public BitField<int, 1, 31> {};
+
+ LEnvironment* environment_;
+ SetOncePointer<LPointerMap> pointer_map_;
+ HValue* hydrogen_value_;
+ int bit_field_;
+};
+
+
+// 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 && result() != NULL; }
+ void set_result(LOperand* operand) { results_[0] = operand; }
+ LOperand* result() const { return results_[0]; }
+
+ protected:
+ EmbeddedContainer<LOperand*, R> results_;
+ EmbeddedContainer<LOperand*, I> inputs_;
+ EmbeddedContainer<LOperand*, T> temps_;
+
+ private:
+ // Iterator support.
+ virtual int InputCount() { return I; }
+ virtual LOperand* InputAt(int i) { return inputs_[i]; }
+
+ virtual int TempCount() { return T; }
+ virtual LOperand* TempAt(int i) { return temps_[i]; }
+};
+
+
+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;
+ }
+
+ // 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);
+ }
+
+ bool IsRedundant() const;
+
+ HBasicBlock* block() const { return block_; }
+
+ enum InnerPosition {
+ BEFORE,
+ START,
+ END,
+ AFTER,
+ FIRST_INNER_POSITION = BEFORE,
+ LAST_INNER_POSITION = AFTER
+ };
+
+ LParallelMove* GetOrCreateParallelMove(InnerPosition pos, Zone* zone) {
+ if (parallel_moves_[pos] == NULL) {
+ parallel_moves_[pos] = new(zone) LParallelMove(zone);
+ }
+ return parallel_moves_[pos];
+ }
+
+ LParallelMove* GetParallelMove(InnerPosition pos) {
+ return parallel_moves_[pos];
+ }
+
+ private:
+ LParallelMove* parallel_moves_[LAST_INNER_POSITION + 1];
+ HBasicBlock* block_;
+};
+
+
+class LInstructionGap: public LGap {
+ public:
+ explicit LInstructionGap(HBasicBlock* block) : LGap(block) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const {
+ return !IsRedundant();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap")
+};
+
+
+class LGoto: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LGoto(int block_id) : block_id_(block_id) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const;
+ 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:
+ DECLARE_CONCRETE_INSTRUCTION(LazyBailout, "lazy-bailout")
+};
+
+
+class LDummyUse: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDummyUse(LOperand* value) {
+ inputs_[0] = value;
+ }
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use")
+};
+
+
+class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
+ DECLARE_HYDROGEN_ACCESSOR(Deoptimize)
+};
+
+
+class LLabel: public LGap {
+ public:
+ explicit LLabel(HBasicBlock* block)
+ : LGap(block), replacement_(NULL) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ 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(); }
+ bool is_osr_entry() const { return block()->is_osr_entry(); }
+ 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:
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(Parameter, "parameter")
+};
+
+
+class LCallStub: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallStub(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallStub, "call-stub")
+ DECLARE_HYDROGEN_ACCESSOR(CallStub)
+
+ TranscendentalCache::Type transcendental_type() {
+ return hydrogen()->transcendental_type();
+ }
+};
+
+
+class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
+ public:
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value")
+};
+
+
+template<int I, int T>
+class LControlInstruction: public LTemplateInstruction<0, I, T> {
+ public:
+ LControlInstruction() : false_label_(NULL), true_label_(NULL) { }
+
+ virtual bool IsControl() const { return true; }
+
+ int SuccessorCount() { return hydrogen()->SuccessorCount(); }
+ HBasicBlock* SuccessorAt(int i) { return hydrogen()->SuccessorAt(i); }
+
+ int TrueDestination(LChunk* chunk) {
+ return chunk->LookupDestination(true_block_id());
+ }
+ int FalseDestination(LChunk* chunk) {
+ return chunk->LookupDestination(false_block_id());
+ }
+
+ Label* TrueLabel(LChunk* chunk) {
+ if (true_label_ == NULL) {
+ true_label_ = chunk->GetAssemblyLabel(TrueDestination(chunk));
+ }
+ return true_label_;
+ }
+ Label* FalseLabel(LChunk* chunk) {
+ if (false_label_ == NULL) {
+ false_label_ = chunk->GetAssemblyLabel(FalseDestination(chunk));
+ }
+ return false_label_;
+ }
+
+ protected:
+ int true_block_id() { return SuccessorAt(0)->block_id(); }
+ int false_block_id() { return SuccessorAt(1)->block_id(); }
+
+ private:
+ HControlInstruction* hydrogen() {
+ return HControlInstruction::cast(this->hydrogen_value());
+ }
+
+ Label* false_label_;
+ Label* true_label_;
+};
+
+
+class LWrapReceiver: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LWrapReceiver(LOperand* receiver,
+ LOperand* function,
+ LOperand* temp) {
+ inputs_[0] = receiver;
+ inputs_[1] = function;
+ temps_[0] = temp;
+ }
+
+ LOperand* receiver() { return inputs_[0]; }
+ LOperand* function() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver")
+};
+
+
+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;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+ LOperand* receiver() { return inputs_[1]; }
+ LOperand* length() { return inputs_[2]; }
+ LOperand* elements() { return inputs_[3]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments")
+};
+
+
+class LAccessArgumentsAt: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LAccessArgumentsAt(LOperand* arguments, LOperand* length, LOperand* index) {
+ inputs_[0] = arguments;
+ inputs_[1] = length;
+ inputs_[2] = index;
+ }
+
+ LOperand* arguments() { return inputs_[0]; }
+ LOperand* length() { return inputs_[1]; }
+ LOperand* index() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at")
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LArgumentsLength: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LArgumentsLength(LOperand* elements) {
+ inputs_[0] = elements;
+ }
+
+ LOperand* elements() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length")
+};
+
+
+class LArgumentsElements: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments-elements")
+ DECLARE_HYDROGEN_ACCESSOR(ArgumentsElements)
+};
+
+
+class LDebugBreak: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(DebugBreak, "break")
+};
+
+
+class LModI: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LModI(LOperand* left, LOperand* right, LOperand* temp) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mod)
+};
+
+
+class LDivI: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LDivI(LOperand* left, LOperand* right, LOperand* temp) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
+ DECLARE_HYDROGEN_ACCESSOR(Div)
+};
+
+
+class LMathFloorOfDiv: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LMathFloorOfDiv(LOperand* left,
+ LOperand* right,
+ LOperand* temp = NULL) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
+ DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
+};
+
+
+class LMulI: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LMulI(LOperand* left, LOperand* right, LOperand* temp) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mul)
+};
+
+
+class LCompareNumericAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCompareNumericAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareNumericAndBranch,
+ "compare-numeric-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareNumericAndBranch)
+
+ Token::Value op() const { return hydrogen()->token(); }
+ bool is_double() const {
+ return hydrogen()->representation().IsDouble();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LMathFloor: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathFloor(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloor, "math-floor")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathRound: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LMathRound(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(MathRound, "math-round")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathAbs: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LMathAbs(LOperand* context, LOperand* value) {
+ inputs_[1] = context;
+ inputs_[0] = value;
+ }
+
+ LOperand* context() { return inputs_[1]; }
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathAbs, "math-abs")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathLog: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathLog(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathLog, "math-log")
+};
+
+
+class LMathSin: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathSin(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathSin, "math-sin")
+};
+
+
+class LMathCos: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathCos(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathCos, "math-cos")
+};
+
+
+class LMathTan: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathTan(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathTan, "math-tan")
+};
+
+
+class LMathExp: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LMathExp(LOperand* value,
+ LOperand* temp1,
+ LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
+ ExternalReference::InitializeMathExpData();
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp1() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathExp, "math-exp")
+};
+
+
+class LMathSqrt: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathSqrt(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathSqrt, "math-sqrt")
+};
+
+
+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")
+};
+
+
+class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCmpObjectEqAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, "cmp-object-eq-and-branch")
+};
+
+
+class LCmpHoleAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LCmpHoleAndBranch(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpHoleAndBranch, "cmp-hole-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareHoleAndBranch)
+};
+
+
+class LIsObjectAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LIsObjectAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LIsNumberAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LIsNumberAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsNumberAndBranch, "is-number-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsNumberAndBranch)
+};
+
+
+class LIsStringAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LIsStringAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch, "is-smi-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsSmiAndBranch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LIsUndetectableAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch,
+ "is-undetectable-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsUndetectableAndBranch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LStringCompareAndBranch: public LControlInstruction<3, 0> {
+ public:
+ LStringCompareAndBranch(LOperand* context, LOperand* left, LOperand* right) {
+ inputs_[0] = context;
+ inputs_[1] = left;
+ inputs_[2] = right;
+ }
+
+ LOperand* left() { return inputs_[1]; }
+ LOperand* right() { return inputs_[2]; }
+
+ 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) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get-cached-array-index")
+ DECLARE_HYDROGEN_ACCESSOR(GetCachedArrayIndex)
+};
+
+
+class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LHasCachedArrayIndexAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch,
+ "has-cached-array-index-and-branch")
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LIsConstructCallAndBranch: public LControlInstruction<0, 1> {
+ public:
+ explicit LIsConstructCallAndBranch(LOperand* temp) {
+ temps_[0] = temp;
+ }
+
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch,
+ "is-construct-call-and-branch")
+};
+
+
+class LClassOfTestAndBranch: public LControlInstruction<1, 2> {
+ public:
+ LClassOfTestAndBranch(LOperand* value, LOperand* temp, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch,
+ "class-of-test-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(ClassOfTestAndBranch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LCmpT: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LCmpT(LOperand* context, LOperand* left, LOperand* right) {
+ inputs_[0] = context;
+ inputs_[1] = left;
+ inputs_[2] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
+ DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
+
+ Token::Value op() const { return hydrogen()->token(); }
+};
+
+
+class LInstanceOf: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LInstanceOf(LOperand* context, LOperand* left, LOperand* right) {
+ inputs_[0] = context;
+ inputs_[1] = left;
+ inputs_[2] = right;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
+};
+
+
+class LInstanceOfKnownGlobal: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LInstanceOfKnownGlobal(LOperand* context, LOperand* value, LOperand* temp) {
+ inputs_[0] = context;
+ inputs_[1] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
+ "instance-of-known-global")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+
+ Handle<JSFunction> function() const { return hydrogen()->function(); }
+ LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
+ return lazy_deopt_env_;
+ }
+ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) {
+ lazy_deopt_env_ = env;
+ }
+
+ private:
+ LEnvironment* lazy_deopt_env_;
+};
+
+
+class LInstanceSize: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInstanceSize(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceSize, "instance-size")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceSize)
+};
+
+
+class LBoundsCheck: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LBoundsCheck(LOperand* index, LOperand* length) {
+ inputs_[0] = index;
+ inputs_[1] = length;
+ }
+
+ LOperand* index() { return inputs_[0]; }
+ LOperand* length() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check")
+ DECLARE_HYDROGEN_ACCESSOR(BoundsCheck)
+};
+
+
+class LBitI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LBitI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i")
+ DECLARE_HYDROGEN_ACCESSOR(Bitwise)
+
+ Token::Value op() const { return hydrogen()->op(); }
+};
+
+
+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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ShiftI, "shift-i")
+
+ Token::Value op() const { return op_; }
+ bool can_deopt() const { return can_deopt_; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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 LConstantS: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ConstantS, "constant-s")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ Smi* value() const { return Smi::FromInt(hydrogen()->Integer32Value()); }
+};
+
+
+class LConstantD: public LTemplateInstruction<1, 0, 1> {
+ public:
+ explicit LConstantD(LOperand* temp) {
+ temps_[0] = temp;
+ }
+
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ConstantD, "constant-d")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ double value() const { return hydrogen()->DoubleValue(); }
+};
+
+
+class LConstantE: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ConstantE, "constant-e")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ ExternalReference value() const {
+ return hydrogen()->ExternalReferenceValue();
+ }
+};
+
+
+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, 1> {
+ public:
+ LBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Branch, "branch")
+ DECLARE_HYDROGEN_ACCESSOR(Branch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LCmpMapAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LCmpMapAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMap)
+
+ Handle<Map> map() const { return hydrogen()->map(); }
+};
+
+
+class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMapEnumLength(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length")
+};
+
+
+class LElementsKind: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LElementsKind(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+};
+
+
+class LDateField: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LDateField(LOperand* date, LOperand* temp, Smi* index)
+ : index_(index) {
+ inputs_[0] = date;
+ temps_[0] = temp;
+ }
+
+ LOperand* date() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DateField, "date-field")
+ DECLARE_HYDROGEN_ACCESSOR(DateField)
+
+ Smi* index() const { return index_; }
+
+ private:
+ Smi* index_;
+};
+
+
+class LSeqStringSetChar: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LSeqStringSetChar(String::Encoding encoding,
+ LOperand* string,
+ LOperand* index,
+ LOperand* value) : encoding_(encoding) {
+ inputs_[0] = string;
+ inputs_[1] = index;
+ inputs_[2] = value;
+ }
+
+ String::Encoding encoding() { return encoding_; }
+ LOperand* string() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar, "seq-string-set-char")
+ DECLARE_HYDROGEN_ACCESSOR(SeqStringSetChar)
+
+ private:
+ String::Encoding encoding_;
+};
+
+
+class LThrow: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LThrow(LOperand* context, LOperand* value) {
+ inputs_[0] = context;
+ inputs_[1] = value;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
+};
+
+
+class LAddI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LAddI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ static bool UseLea(HAdd* add) {
+ return !add->CheckFlag(HValue::kCanOverflow) &&
+ add->BetterLeftOperand()->UseCount() > 1;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i")
+ DECLARE_HYDROGEN_ACCESSOR(Add)
+};
+
+
+class LMathMinMax: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LMathMinMax(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathMinMax, "math-min-max")
+ DECLARE_HYDROGEN_ACCESSOR(MathMinMax)
+};
+
+
+class LPower: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LPower(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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, 3, 0> {
+ public:
+ LArithmeticT(Token::Value op,
+ LOperand* context,
+ LOperand* left,
+ LOperand* right)
+ : op_(op) {
+ inputs_[0] = context;
+ inputs_[1] = left;
+ inputs_[2] = right;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* left() { return inputs_[1]; }
+ LOperand* right() { return inputs_[2]; }
+
+ 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, 3, 0> {
+ public:
+ explicit LReturn(LOperand* value, LOperand* context,
+ LOperand* parameter_count) {
+ inputs_[0] = value;
+ inputs_[1] = context;
+ inputs_[2] = parameter_count;
+ }
+
+ bool has_constant_parameter_count() {
+ return parameter_count()->IsConstantOperand();
+ }
+ LConstantOperand* constant_parameter_count() {
+ ASSERT(has_constant_parameter_count());
+ return LConstantOperand::cast(parameter_count());
+ }
+ LOperand* parameter_count() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Return, "return")
+ DECLARE_HYDROGEN_ACCESSOR(Return)
+};
+
+
+class LLoadNamedField: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadNamedField(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ virtual bool ClobbersDoubleRegisters() const {
+ return !CpuFeatures::IsSupported(SSE2) &&
+ !hydrogen()->representation().IsDouble();
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedField)
+};
+
+
+class LLoadNamedGeneric: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadNamedGeneric(LOperand* context, LOperand* object) {
+ inputs_[0] = context;
+ inputs_[1] = object;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* object() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedGeneric)
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+};
+
+
+class LLoadFunctionPrototype: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LLoadFunctionPrototype(LOperand* function, LOperand* temp) {
+ inputs_[0] = function;
+ temps_[0] = temp;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load-function-prototype")
+ DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype)
+};
+
+
+class LLoadExternalArrayPointer: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadExternalArrayPointer(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer,
+ "load-external-array-pointer")
+};
+
+
+class LLoadKeyed: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadKeyed(LOperand* elements, LOperand* key) {
+ inputs_[0] = elements;
+ inputs_[1] = key;
+ }
+ LOperand* elements() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ ElementsKind elements_kind() const {
+ return hydrogen()->elements_kind();
+ }
+ bool is_external() const {
+ return hydrogen()->is_external();
+ }
+
+ virtual bool ClobbersDoubleRegisters() const {
+ return !CpuFeatures::IsSupported(SSE2) &&
+ !IsDoubleOrFloatElementsKind(hydrogen()->elements_kind());
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyed, "load-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(LoadKeyed)
+
+ virtual void PrintDataTo(StringStream* stream);
+ uint32_t additional_index() const { return hydrogen()->index_offset(); }
+ bool key_is_smi() {
+ return hydrogen()->key()->representation().IsTagged();
+ }
+};
+
+
+inline static bool ExternalArrayOpRequiresTemp(
+ Representation key_representation,
+ ElementsKind elements_kind) {
+ // Operations that require the key to be divided by two to be converted into
+ // an index cannot fold the scale operation into a load and need an extra
+ // temp register to do the work.
+ return key_representation.IsSmi() &&
+ (elements_kind == EXTERNAL_BYTE_ELEMENTS ||
+ elements_kind == EXTERNAL_UNSIGNED_BYTE_ELEMENTS ||
+ elements_kind == EXTERNAL_PIXEL_ELEMENTS);
+}
+
+
+class LLoadKeyedGeneric: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LLoadKeyedGeneric(LOperand* context, LOperand* obj, LOperand* key) {
+ inputs_[0] = context;
+ inputs_[1] = obj;
+ inputs_[2] = key;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* object() { return inputs_[1]; }
+ LOperand* key() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load-keyed-generic")
+};
+
+
+class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell)
+};
+
+
+class LLoadGlobalGeneric: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadGlobalGeneric(LOperand* context, LOperand* global_object) {
+ inputs_[0] = context;
+ inputs_[1] = global_object;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* global_object() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+ bool for_typeof() const { return hydrogen()->for_typeof(); }
+};
+
+
+class LStoreGlobalCell: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LStoreGlobalCell(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell)
+};
+
+
+class LStoreGlobalGeneric: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreGlobalGeneric(LOperand* context,
+ LOperand* global_object,
+ LOperand* value) {
+ inputs_[0] = context;
+ inputs_[1] = global_object;
+ inputs_[2] = value;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* global_object() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric)
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LLoadContextSlot: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadContextSlot(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot)
+
+ int slot_index() { return hydrogen()->slot_index(); }
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LStoreContextSlot: public LTemplateInstruction<0, 2, 1> {
+ public:
+ LStoreContextSlot(LOperand* context, LOperand* value, LOperand* temp) {
+ inputs_[0] = context;
+ inputs_[1] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument")
+};
+
+
+class LDrop: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LDrop(int count) : count_(count) { }
+
+ int count() const { return count_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Drop, "drop")
+
+ private:
+ int count_;
+};
+
+
+class LInnerAllocatedObject: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInnerAllocatedObject(LOperand* base_object) {
+ inputs_[0] = base_object;
+ }
+
+ LOperand* base_object() { return inputs_[0]; }
+ int offset() { return hydrogen()->offset(); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(InnerAllocatedObject, "sub-allocated-object")
+ DECLARE_HYDROGEN_ACCESSOR(InnerAllocatedObject)
+};
+
+
+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")
+ DECLARE_HYDROGEN_ACCESSOR(Context)
+};
+
+
+class LOuterContext: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LOuterContext(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context")
+};
+
+
+class LDeclareGlobals: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LDeclareGlobals(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals, "declare-globals")
+ DECLARE_HYDROGEN_ACCESSOR(DeclareGlobals)
+};
+
+
+class LGlobalObject: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LGlobalObject(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object")
+};
+
+
+class LGlobalReceiver: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LGlobalReceiver(LOperand* global_object) {
+ inputs_[0] = global_object;
+ }
+
+ LOperand* global() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver")
+};
+
+
+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, 2, 0> {
+ public:
+ LInvokeFunction(LOperand* context, LOperand* function) {
+ inputs_[0] = context;
+ inputs_[1] = function;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* function() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallKeyed: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LCallKeyed(LOperand* context, LOperand* key) {
+ inputs_[0] = context;
+ inputs_[1] = key;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+
+ 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, 1, 0> {
+ public:
+ explicit LCallNamed(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ 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, 2, 0> {
+ public:
+ explicit LCallFunction(LOperand* context, LOperand* function) {
+ inputs_[0] = context;
+ inputs_[1] = function;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* function() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
+ DECLARE_HYDROGEN_ACCESSOR(CallFunction)
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallGlobal: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallGlobal(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ 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);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNew: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LCallNew(LOperand* context, LOperand* constructor) {
+ inputs_[0] = context;
+ inputs_[1] = constructor;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* constructor() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new")
+ DECLARE_HYDROGEN_ACCESSOR(CallNew)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNewArray: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LCallNewArray(LOperand* context, LOperand* constructor) {
+ inputs_[0] = context;
+ inputs_[1] = constructor;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* constructor() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNewArray, "call-new-array")
+ DECLARE_HYDROGEN_ACCESSOR(CallNewArray)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallRuntime: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallRuntime(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double")
+};
+
+
+class LInteger32ToSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInteger32ToSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToSmi, "int32-to-smi")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+};
+
+
+class LUint32ToDouble: public LTemplateInstruction<1, 1, 1> {
+ public:
+ explicit LUint32ToDouble(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double")
+};
+
+
+class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberTagI(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i")
+};
+
+
+class LNumberTagU: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LNumberTagU(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u")
+};
+
+
+class LNumberTagD: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LNumberTagD(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+};
+
+
+// Sometimes truncating conversion from a tagged value to an int32.
+class LDoubleToI: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LDoubleToI(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+
+ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+};
+
+
+class LDoubleToSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDoubleToSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DoubleToSmi, "double-to-smi")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+};
+
+
+// Truncating conversion from a tagged value to an int32.
+class LTaggedToI: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LTaggedToI(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+
+ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+};
+
+
+// Truncating conversion from a tagged value to an int32.
+class LTaggedToINoSSE2: public LTemplateInstruction<1, 1, 3> {
+ public:
+ LTaggedToINoSSE2(LOperand* value,
+ LOperand* temp1,
+ LOperand* temp2,
+ LOperand* temp3) {
+ inputs_[0] = value;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
+ temps_[2] = temp3;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* scratch() { return temps_[0]; }
+ LOperand* scratch2() { return temps_[1]; }
+ LOperand* scratch3() { return temps_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TaggedToINoSSE2, "tagged-to-i-nosse2")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+
+ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+};
+
+
+class LSmiTag: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LSmiTag(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag")
+};
+
+
+class LNumberUntagD: public LTemplateInstruction<1, 1, 1> {
+ public:
+ explicit LNumberUntagD(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ virtual bool ClobbersDoubleRegisters() const { return false; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag")
+
+ bool needs_check() const { return needs_check_; }
+
+ private:
+ bool needs_check_;
+};
+
+
+class LStoreNamedField: public LTemplateInstruction<0, 2, 2> {
+ public:
+ LStoreNamedField(LOperand* obj,
+ LOperand* val,
+ LOperand* temp,
+ LOperand* temp_map) {
+ inputs_[0] = obj;
+ inputs_[1] = val;
+ temps_[0] = temp;
+ temps_[1] = temp_map;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp_map() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> transition() const { return hydrogen()->transition_map(); }
+ Representation representation() const {
+ return hydrogen()->field_representation();
+ }
+};
+
+
+class LStoreNamedGeneric: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreNamedGeneric(LOperand* context, LOperand* object, LOperand* value) {
+ inputs_[0] = context;
+ inputs_[1] = object;
+ inputs_[2] = value;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* object() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
+
+ virtual void PrintDataTo(StringStream* stream);
+ Handle<Object> name() const { return hydrogen()->name(); }
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreKeyed(LOperand* obj, LOperand* key, LOperand* val) {
+ inputs_[0] = obj;
+ inputs_[1] = key;
+ inputs_[2] = val;
+ }
+
+ bool is_external() const { return hydrogen()->is_external(); }
+ LOperand* elements() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+ ElementsKind elements_kind() const {
+ return hydrogen()->elements_kind();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyed, "store-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyed)
+
+ virtual void PrintDataTo(StringStream* stream);
+ uint32_t additional_index() const { return hydrogen()->index_offset(); }
+ bool NeedsCanonicalization() { return hydrogen()->NeedsCanonicalization(); }
+};
+
+
+class LStoreKeyedGeneric: public LTemplateInstruction<0, 4, 0> {
+ public:
+ LStoreKeyedGeneric(LOperand* context,
+ LOperand* object,
+ LOperand* key,
+ LOperand* value) {
+ inputs_[0] = context;
+ inputs_[1] = object;
+ inputs_[2] = key;
+ inputs_[3] = value;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* object() { return inputs_[1]; }
+ LOperand* key() { return inputs_[2]; }
+ LOperand* value() { return inputs_[3]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LTransitionElementsKind: public LTemplateInstruction<0, 2, 2> {
+ public:
+ LTransitionElementsKind(LOperand* object,
+ LOperand* context,
+ LOperand* new_map_temp,
+ LOperand* temp) {
+ inputs_[0] = object;
+ inputs_[1] = context;
+ temps_[0] = new_map_temp;
+ temps_[1] = temp;
+ }
+
+ LOperand* context() { return inputs_[1]; }
+ LOperand* object() { return inputs_[0]; }
+ LOperand* new_map_temp() { return temps_[0]; }
+ LOperand* temp() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind,
+ "transition-elements-kind")
+ DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> original_map() { return hydrogen()->original_map(); }
+ Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+ ElementsKind from_kind() { return hydrogen()->from_kind(); }
+ ElementsKind to_kind() { return hydrogen()->to_kind(); }
+};
+
+
+class LTrapAllocationMemento : public LTemplateInstruction<0, 1, 1> {
+ public:
+ LTrapAllocationMemento(LOperand* object,
+ LOperand* temp) {
+ inputs_[0] = object;
+ temps_[0] = temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TrapAllocationMemento,
+ "trap-allocation-memento")
+};
+
+
+class LStringAdd: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LStringAdd(LOperand* context, LOperand* left, LOperand* right) {
+ inputs_[0] = context;
+ inputs_[1] = left;
+ inputs_[2] = right;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* left() { return inputs_[1]; }
+ LOperand* right() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+};
+
+
+class LStringCharCodeAt: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LStringCharCodeAt(LOperand* context, LOperand* string, LOperand* index) {
+ inputs_[0] = context;
+ inputs_[1] = string;
+ inputs_[2] = index;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* string() { return inputs_[1]; }
+ LOperand* index() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt)
+};
+
+
+class LStringCharFromCode: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringCharFromCode(LOperand* context, LOperand* char_code) {
+ inputs_[0] = context;
+ inputs_[1] = char_code;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* char_code() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+};
+
+
+class LCheckFunction: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckFunction(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function")
+ DECLARE_HYDROGEN_ACCESSOR(CheckFunction)
+};
+
+
+class LCheckInstanceType: public LTemplateInstruction<0, 1, 1> {
+ public:
+ LCheckInstanceType(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type")
+ DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType)
+};
+
+
+class LCheckMaps: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckMaps(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMaps, "check-maps")
+ DECLARE_HYDROGEN_ACCESSOR(CheckMaps)
+};
+
+
+class LCheckSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCheckSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check-smi")
+};
+
+
+class LClampDToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampDToUint8(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ 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")
+};
+
+
+// Truncating conversion from a tagged value to an int32.
+class LClampTToUint8NoSSE2: public LTemplateInstruction<1, 1, 3> {
+ public:
+ LClampTToUint8NoSSE2(LOperand* unclamped,
+ LOperand* temp1,
+ LOperand* temp2,
+ LOperand* temp3) {
+ inputs_[0] = unclamped;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
+ temps_[2] = temp3;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+ LOperand* scratch() { return temps_[0]; }
+ LOperand* scratch2() { return temps_[1]; }
+ LOperand* scratch3() { return temps_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8NoSSE2,
+ "clamp-t-to-uint8-nosse2")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+};
+
+
+class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckNonSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check-non-smi")
+ DECLARE_HYDROGEN_ACCESSOR(CheckHeapObject)
+};
+
+
+class LAllocate: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LAllocate(LOperand* context, LOperand* size, LOperand* temp) {
+ inputs_[0] = context;
+ inputs_[1] = size;
+ temps_[0] = temp;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* size() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Allocate, "allocate")
+ DECLARE_HYDROGEN_ACCESSOR(Allocate)
+};
+
+
+class LRegExpLiteral: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LRegExpLiteral(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral, "regexp-literal")
+ DECLARE_HYDROGEN_ACCESSOR(RegExpLiteral)
+};
+
+
+class LFunctionLiteral: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LFunctionLiteral(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral, "function-literal")
+ DECLARE_HYDROGEN_ACCESSOR(FunctionLiteral)
+};
+
+
+class LToFastProperties: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LToFastProperties(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to-fast-properties")
+ DECLARE_HYDROGEN_ACCESSOR(ToFastProperties)
+};
+
+
+class LTypeof: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LTypeof(LOperand* context, LOperand* value) {
+ inputs_[0] = context;
+ inputs_[1] = value;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
+};
+
+
+class LTypeofIsAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LTypeofIsAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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 LOsrEntry: public LTemplateInstruction<0, 0, 0> {
+ public:
+ LOsrEntry() {}
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr-entry")
+};
+
+
+class LStackCheck: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LStackCheck(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StackCheck, "stack-check")
+ DECLARE_HYDROGEN_ACCESSOR(StackCheck)
+
+ Label* done_label() { return &done_label_; }
+
+ private:
+ Label done_label_;
+};
+
+
+class LForInPrepareMap: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LForInPrepareMap(LOperand* context, LOperand* object) {
+ inputs_[0] = context;
+ inputs_[1] = object;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* object() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap, "for-in-prepare-map")
+};
+
+
+class LForInCacheArray: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LForInCacheArray(LOperand* map) {
+ inputs_[0] = map;
+ }
+
+ LOperand* map() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray, "for-in-cache-array")
+
+ int idx() {
+ return HForInCacheArray::cast(this->hydrogen_value())->idx();
+ }
+};
+
+
+class LCheckMapValue: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LCheckMapValue(LOperand* value, LOperand* map) {
+ inputs_[0] = value;
+ inputs_[1] = map;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* map() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMapValue, "check-map-value")
+};
+
+
+class LLoadFieldByIndex: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadFieldByIndex(LOperand* object, LOperand* index) {
+ inputs_[0] = object;
+ inputs_[1] = index;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex, "load-field-by-index")
+};
+
+
+class LChunkBuilder;
+class LPlatformChunk: public LChunk {
+ public:
+ LPlatformChunk(CompilationInfo* info, HGraph* graph)
+ : LChunk(info, graph),
+ num_double_slots_(0) { }
+
+ int GetNextSpillIndex(bool is_double);
+ LOperand* GetNextSpillSlot(bool is_double);
+
+ int num_double_slots() const { return num_double_slots_; }
+
+ private:
+ int num_double_slots_;
+};
+
+
+class LChunkBuilder BASE_EMBEDDED {
+ public:
+ LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
+ : chunk_(NULL),
+ info_(info),
+ graph_(graph),
+ zone_(graph->zone()),
+ 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_(BailoutId::None()) { }
+
+ // Build the sequence for the graph.
+ LPlatformChunk* Build();
+
+ // Declare methods that deal with the individual node types.
+#define DECLARE_DO(type) LInstruction* Do##type(H##type* node);
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
+#undef DECLARE_DO
+
+ static HValue* SimplifiedDivisorForMathFloorOfDiv(HValue* val);
+
+ LInstruction* DoMathFloor(HUnaryMathOperation* instr);
+ LInstruction* DoMathRound(HUnaryMathOperation* instr);
+ LInstruction* DoMathAbs(HUnaryMathOperation* instr);
+ LInstruction* DoMathLog(HUnaryMathOperation* instr);
+ LInstruction* DoMathSin(HUnaryMathOperation* instr);
+ LInstruction* DoMathCos(HUnaryMathOperation* instr);
+ LInstruction* DoMathTan(HUnaryMathOperation* instr);
+ LInstruction* DoMathExp(HUnaryMathOperation* instr);
+ LInstruction* DoMathSqrt(HUnaryMathOperation* instr);
+ LInstruction* DoMathPowHalf(HUnaryMathOperation* instr);
+
+ private:
+ enum Status {
+ UNUSED,
+ BUILDING,
+ DONE,
+ ABORTED
+ };
+
+ LPlatformChunk* chunk() const { return chunk_; }
+ CompilationInfo* info() const { return info_; }
+ HGraph* graph() const { return graph_; }
+ Zone* zone() const { return zone_; }
+
+ 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(BailoutReason reason);
+
+ // Methods for getting operands for Use / Define / Temp.
+ LUnallocated* ToUnallocated(Register reg);
+ LUnallocated* ToUnallocated(XMMRegister reg);
+ LUnallocated* ToUnallocated(X87Register 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,
+ XMMRegister 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 a constant operand.
+ MUST_USE_RESULT LOperand* UseConstant(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(XMMRegister 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,
+ XMMRegister reg);
+ template<int I, int T>
+ LInstruction* DefineX87TOS(LTemplateInstruction<1, I, T>* instr);
+ // Assigns an environment to an instruction. An instruction which can
+ // deoptimize must have an environment.
+ LInstruction* AssignEnvironment(LInstruction* instr);
+ // Assigns a pointer map to an instruction. An instruction which can
+ // trigger a GC or a lazy deoptimization must have a pointer map.
+ LInstruction* AssignPointerMap(LInstruction* instr);
+
+ enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY };
+
+ // Marks a call for the register allocator. Assigns a pointer map to
+ // support GC and lazy deoptimization. Assigns an environment to support
+ // eager deoptimization if CAN_DEOPTIMIZE_EAGERLY.
+ LInstruction* MarkAsCall(
+ LInstruction* instr,
+ HInstruction* hinstr,
+ CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
+
+ LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
+ int* argument_index_accumulator,
+ ZoneList<HValue*>* objects_to_materialize);
+
+ void VisitInstruction(HInstruction* current);
+
+ void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
+ LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr);
+ LInstruction* DoArithmeticD(Token::Value op,
+ HArithmeticBinaryOperation* instr);
+ LInstruction* DoArithmeticT(Token::Value op,
+ HArithmeticBinaryOperation* instr);
+
+ LOperand* GetStoreKeyedValueOperand(HStoreKeyed* instr);
+
+ LPlatformChunk* chunk_;
+ CompilationInfo* info_;
+ HGraph* const graph_;
+ Zone* zone_;
+ Status status_;
+ HInstruction* current_instruction_;
+ HBasicBlock* current_block_;
+ HBasicBlock* next_block_;
+ int argument_count_;
+ LAllocator* allocator_;
+ int position_;
+ LInstruction* instruction_pending_deoptimization_environment_;
+ BailoutId pending_deoptimization_ast_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
+};
+
+#undef DECLARE_HYDROGEN_ACCESSOR
+#undef DECLARE_CONCRETE_INSTRUCTION
+
+} } // namespace v8::internal
+
+#endif // V8_IA32_LITHIUM_IA32_H_
diff --git a/chromium/v8/src/ia32/macro-assembler-ia32.cc b/chromium/v8/src/ia32/macro-assembler-ia32.cc
new file mode 100644
index 00000000000..67a7c0d2b49
--- /dev/null
+++ b/chromium/v8/src/ia32/macro-assembler-ia32.cc
@@ -0,0 +1,3235 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "cpu-profiler.h"
+#include "debug.h"
+#include "runtime.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+// -------------------------------------------------------------------------
+// MacroAssembler implementation.
+
+MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
+ : Assembler(arg_isolate, buffer, size),
+ generating_stub_(false),
+ allow_stub_calls_(true),
+ has_frame_(false) {
+ if (isolate() != NULL) {
+ code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
+ isolate());
+ }
+}
+
+
+void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) {
+ if (isolate()->heap()->RootCanBeTreatedAsConstant(index)) {
+ Handle<Object> value(&isolate()->heap()->roots_array_start()[index]);
+ mov(destination, value);
+ return;
+ }
+ ExternalReference roots_array_start =
+ ExternalReference::roots_array_start(isolate());
+ mov(destination, Immediate(index));
+ mov(destination, Operand::StaticArray(destination,
+ times_pointer_size,
+ roots_array_start));
+}
+
+
+void MacroAssembler::StoreRoot(Register source,
+ Register scratch,
+ Heap::RootListIndex index) {
+ ASSERT(Heap::RootCanBeWrittenAfterInitialization(index));
+ ExternalReference roots_array_start =
+ ExternalReference::roots_array_start(isolate());
+ mov(scratch, Immediate(index));
+ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start),
+ source);
+}
+
+
+void MacroAssembler::CompareRoot(Register with,
+ Register scratch,
+ Heap::RootListIndex index) {
+ ExternalReference roots_array_start =
+ ExternalReference::roots_array_start(isolate());
+ mov(scratch, Immediate(index));
+ cmp(with, Operand::StaticArray(scratch,
+ times_pointer_size,
+ roots_array_start));
+}
+
+
+void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) {
+ ASSERT(isolate()->heap()->RootCanBeTreatedAsConstant(index));
+ Handle<Object> value(&isolate()->heap()->roots_array_start()[index]);
+ cmp(with, value);
+}
+
+
+void MacroAssembler::CompareRoot(const Operand& with,
+ Heap::RootListIndex index) {
+ ASSERT(isolate()->heap()->RootCanBeTreatedAsConstant(index));
+ Handle<Object> value(&isolate()->heap()->roots_array_start()[index]);
+ cmp(with, value);
+}
+
+
+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);
+}
+
+
+void MacroAssembler::RememberedSetHelper(
+ Register object, // Only used for debug checks.
+ Register addr,
+ Register scratch,
+ SaveFPRegsMode save_fp,
+ MacroAssembler::RememberedSetFinalAction and_then) {
+ Label done;
+ if (emit_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);
+ }
+}
+
+
+void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg,
+ XMMRegister scratch_reg,
+ Register result_reg) {
+ Label done;
+ Label conv_failure;
+ pxor(scratch_reg, scratch_reg);
+ cvtsd2si(result_reg, input_reg);
+ test(result_reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ cmp(result_reg, Immediate(0x80000000));
+ j(equal, &conv_failure, Label::kNear);
+ mov(result_reg, Immediate(0));
+ setcc(above, result_reg);
+ sub(result_reg, Immediate(1));
+ and_(result_reg, Immediate(255));
+ jmp(&done, Label::kNear);
+ bind(&conv_failure);
+ Set(result_reg, Immediate(0));
+ ucomisd(input_reg, scratch_reg);
+ j(below, &done, Label::kNear);
+ Set(result_reg, Immediate(255));
+ bind(&done);
+}
+
+
+void MacroAssembler::ClampUint8(Register reg) {
+ Label done;
+ test(reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ setcc(negative, reg); // 1 if negative, 0 if positive.
+ dec_b(reg); // 0 if negative, 255 if positive.
+ bind(&done);
+}
+
+
+static double kUint32Bias =
+ static_cast<double>(static_cast<uint32_t>(0xFFFFFFFF)) + 1;
+
+
+void MacroAssembler::LoadUint32(XMMRegister dst,
+ Register src,
+ XMMRegister scratch) {
+ Label done;
+ cmp(src, Immediate(0));
+ movdbl(scratch,
+ Operand(reinterpret_cast<int32_t>(&kUint32Bias), RelocInfo::NONE32));
+ cvtsi2sd(dst, src);
+ j(not_sign, &done, Label::kNear);
+ addsd(dst, scratch);
+ bind(&done);
+}
+
+
+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::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.
+ Label done;
+
+ // Skip barrier if writing a smi.
+ if (smi_check == INLINE_SMI_CHECK) {
+ JumpIfSmi(value, &done, Label::kNear);
+ }
+
+ // 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;
+ test_b(dst, (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 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(dst, Immediate(BitCast<int32_t>(kZapValue)));
+ }
+}
+
+
+void MacroAssembler::RecordWriteForMap(
+ Register object,
+ Handle<Map> map,
+ Register scratch1,
+ Register scratch2,
+ SaveFPRegsMode save_fp) {
+ Label done;
+
+ Register address = scratch1;
+ Register value = scratch2;
+ if (emit_debug_code()) {
+ Label ok;
+ lea(address, FieldOperand(object, HeapObject::kMapOffset));
+ test_b(address, (1 << kPointerSizeLog2) - 1);
+ j(zero, &ok, Label::kNear);
+ int3();
+ bind(&ok);
+ }
+
+ ASSERT(!object.is(value));
+ ASSERT(!object.is(address));
+ ASSERT(!value.is(address));
+ AssertNotSmi(object);
+
+ if (!FLAG_incremental_marking) {
+ return;
+ }
+
+ // A single check of the map's pages interesting flag suffices, since it is
+ // only set during incremental collection, and then it's also guaranteed that
+ // the from object's page's interesting flag is also set. This optimization
+ // relies on the fact that maps can never be in new space.
+ ASSERT(!isolate()->heap()->InNewSpace(*map));
+ CheckPageFlagForMap(map,
+ MemoryChunk::kPointersToHereAreInterestingMask,
+ zero,
+ &done,
+ Label::kNear);
+
+ // Delay the initialization of |address| and |value| for the stub until it's
+ // known that the will be needed. Up until this point their values are not
+ // needed since they are embedded in the operands of instructions that need
+ // them.
+ lea(address, FieldOperand(object, HeapObject::kMapOffset));
+ mov(value, Immediate(map));
+ RecordWriteStub stub(object, value, address, OMIT_REMEMBERED_SET, save_fp);
+ CallStub(&stub);
+
+ 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(scratch1, Immediate(BitCast<int32_t>(kZapValue)));
+ mov(scratch2, Immediate(BitCast<int32_t>(kZapValue)));
+ }
+}
+
+
+void MacroAssembler::RecordWrite(Register object,
+ Register address,
+ Register value,
+ SaveFPRegsMode fp_mode,
+ RememberedSetAction remembered_set_action,
+ SmiCheck smi_check) {
+ ASSERT(!object.is(value));
+ ASSERT(!object.is(address));
+ ASSERT(!value.is(address));
+ AssertNotSmi(object);
+
+ if (remembered_set_action == OMIT_REMEMBERED_SET &&
+ !FLAG_incremental_marking) {
+ return;
+ }
+
+ if (emit_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;
+
+ 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 clobbered registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (emit_debug_code()) {
+ mov(address, Immediate(BitCast<int32_t>(kZapValue)));
+ mov(value, Immediate(BitCast<int32_t>(kZapValue)));
+ }
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void MacroAssembler::DebugBreak() {
+ Set(eax, Immediate(0));
+ mov(ebx, Immediate(ExternalReference(Runtime::kDebugBreak, isolate())));
+ CEntryStub ces(1);
+ call(ces.GetCode(isolate()), RelocInfo::DEBUG_BREAK);
+}
+#endif
+
+
+void MacroAssembler::Set(Register dst, const Immediate& x) {
+ if (x.is_zero()) {
+ xor_(dst, dst); // Shorter than mov.
+ } else {
+ mov(dst, x);
+ }
+}
+
+
+void MacroAssembler::Set(const Operand& dst, const Immediate& x) {
+ mov(dst, x);
+}
+
+
+bool MacroAssembler::IsUnsafeImmediate(const Immediate& x) {
+ static const int kMaxImmediateBits = 17;
+ if (!RelocInfo::IsNone(x.rmode_)) return false;
+ return !is_intn(x.x_, kMaxImmediateBits);
+}
+
+
+void MacroAssembler::SafeSet(Register dst, const Immediate& x) {
+ if (IsUnsafeImmediate(x) && jit_cookie() != 0) {
+ Set(dst, Immediate(x.x_ ^ jit_cookie()));
+ xor_(dst, jit_cookie());
+ } else {
+ Set(dst, x);
+ }
+}
+
+
+void MacroAssembler::SafePush(const Immediate& x) {
+ if (IsUnsafeImmediate(x) && jit_cookie() != 0) {
+ push(Immediate(x.x_ ^ jit_cookie()));
+ xor_(Operand(esp, 0), Immediate(jit_cookie()));
+ } else {
+ push(x);
+ }
+}
+
+
+void MacroAssembler::CmpObjectType(Register heap_object,
+ InstanceType type,
+ Register map) {
+ mov(map, FieldOperand(heap_object, HeapObject::kMapOffset));
+ CmpInstanceType(map, type);
+}
+
+
+void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
+ cmpb(FieldOperand(map, Map::kInstanceTypeOffset),
+ static_cast<int8_t>(type));
+}
+
+
+void MacroAssembler::CheckFastElements(Register map,
+ Label* fail,
+ Label::Distance distance) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ STATIC_ASSERT(FAST_ELEMENTS == 2);
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ cmpb(FieldOperand(map, Map::kBitField2Offset),
+ Map::kMaximumBitField2FastHoleyElementValue);
+ j(above, fail, distance);
+}
+
+
+void MacroAssembler::CheckFastObjectElements(Register map,
+ Label* fail,
+ Label::Distance distance) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ STATIC_ASSERT(FAST_ELEMENTS == 2);
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ cmpb(FieldOperand(map, Map::kBitField2Offset),
+ Map::kMaximumBitField2FastHoleySmiElementValue);
+ j(below_equal, fail, distance);
+ cmpb(FieldOperand(map, Map::kBitField2Offset),
+ Map::kMaximumBitField2FastHoleyElementValue);
+ j(above, fail, distance);
+}
+
+
+void MacroAssembler::CheckFastSmiElements(Register map,
+ Label* fail,
+ Label::Distance distance) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ cmpb(FieldOperand(map, Map::kBitField2Offset),
+ Map::kMaximumBitField2FastHoleySmiElementValue);
+ j(above, fail, distance);
+}
+
+
+void MacroAssembler::StoreNumberToDoubleElements(
+ Register maybe_number,
+ Register elements,
+ Register key,
+ Register scratch1,
+ XMMRegister scratch2,
+ Label* fail,
+ bool specialize_for_processor,
+ int elements_offset) {
+ 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) {
+ CpuFeatureScope use_sse2(this, SSE2);
+ movdbl(scratch2, FieldOperand(maybe_number, HeapNumber::kValueOffset));
+ bind(&have_double_value);
+ movdbl(FieldOperand(elements, key, times_4,
+ FixedDoubleArray::kHeaderSize - elements_offset),
+ scratch2);
+ } else {
+ fld_d(FieldOperand(maybe_number, HeapNumber::kValueOffset));
+ bind(&have_double_value);
+ fstp_d(FieldOperand(elements, key, times_4,
+ FixedDoubleArray::kHeaderSize - elements_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.
+ 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) {
+ CpuFeatureScope use_sse2(this, 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) {
+ CpuFeatureScope fscope(this, SSE2);
+ cvtsi2sd(scratch2, scratch1);
+ movdbl(FieldOperand(elements, key, times_4,
+ FixedDoubleArray::kHeaderSize - elements_offset),
+ scratch2);
+ } else {
+ push(scratch1);
+ fild_s(Operand(esp, 0));
+ pop(scratch1);
+ fstp_d(FieldOperand(elements, key, times_4,
+ FixedDoubleArray::kHeaderSize - elements_offset));
+ }
+ bind(&done);
+}
+
+
+void MacroAssembler::CompareMap(Register obj,
+ Handle<Map> map,
+ Label* early_success) {
+ cmp(FieldOperand(obj, HeapObject::kMapOffset), map);
+}
+
+
+void MacroAssembler::CheckMap(Register obj,
+ Handle<Map> map,
+ Label* fail,
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, fail);
+ }
+
+ Label success;
+ CompareMap(obj, map, &success);
+ j(not_equal, fail);
+ bind(&success);
+}
+
+
+void MacroAssembler::DispatchMap(Register obj,
+ Register unused,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type) {
+ Label fail;
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, &fail);
+ }
+ cmp(FieldOperand(obj, HeapObject::kMapOffset), Immediate(map));
+ j(equal, success);
+
+ bind(&fail);
+}
+
+
+Condition MacroAssembler::IsObjectStringType(Register heap_object,
+ Register map,
+ Register instance_type) {
+ mov(map, FieldOperand(heap_object, HeapObject::kMapOffset));
+ movzx_b(instance_type, FieldOperand(map, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ test(instance_type, Immediate(kIsNotStringMask));
+ return zero;
+}
+
+
+Condition MacroAssembler::IsObjectNameType(Register heap_object,
+ Register map,
+ Register instance_type) {
+ mov(map, FieldOperand(heap_object, HeapObject::kMapOffset));
+ movzx_b(instance_type, FieldOperand(map, Map::kInstanceTypeOffset));
+ cmpb(instance_type, static_cast<uint8_t>(LAST_NAME_TYPE));
+ return below_equal;
+}
+
+
+void MacroAssembler::IsObjectJSObjectType(Register heap_object,
+ Register map,
+ Register scratch,
+ Label* fail) {
+ mov(map, FieldOperand(heap_object, HeapObject::kMapOffset));
+ IsInstanceJSObjectType(map, scratch, fail);
+}
+
+
+void MacroAssembler::IsInstanceJSObjectType(Register map,
+ Register scratch,
+ Label* fail) {
+ movzx_b(scratch, FieldOperand(map, Map::kInstanceTypeOffset));
+ sub(scratch, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ cmp(scratch,
+ LAST_NONCALLABLE_SPEC_OBJECT_TYPE - FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ j(above, fail);
+}
+
+
+void MacroAssembler::FCmp() {
+ if (CpuFeatures::IsSupported(CMOV)) {
+ fucomip();
+ fstp(0);
+ } else {
+ fucompp();
+ push(eax);
+ fnstsw_ax();
+ sahf();
+ pop(eax);
+ }
+}
+
+
+void MacroAssembler::AssertNumber(Register object) {
+ if (emit_debug_code()) {
+ Label ok;
+ JumpIfSmi(object, &ok);
+ cmp(FieldOperand(object, HeapObject::kMapOffset),
+ isolate()->factory()->heap_number_map());
+ Check(equal, kOperandNotANumber);
+ bind(&ok);
+ }
+}
+
+
+void MacroAssembler::AssertSmi(Register object) {
+ if (emit_debug_code()) {
+ test(object, Immediate(kSmiTagMask));
+ Check(equal, kOperandIsNotASmi);
+ }
+}
+
+
+void MacroAssembler::AssertString(Register object) {
+ if (emit_debug_code()) {
+ test(object, Immediate(kSmiTagMask));
+ Check(not_equal, kOperandIsASmiAndNotAString);
+ push(object);
+ mov(object, FieldOperand(object, HeapObject::kMapOffset));
+ CmpInstanceType(object, FIRST_NONSTRING_TYPE);
+ pop(object);
+ Check(below, kOperandIsNotAString);
+ }
+}
+
+
+void MacroAssembler::AssertName(Register object) {
+ if (emit_debug_code()) {
+ test(object, Immediate(kSmiTagMask));
+ Check(not_equal, kOperandIsASmiAndNotAName);
+ push(object);
+ mov(object, FieldOperand(object, HeapObject::kMapOffset));
+ CmpInstanceType(object, LAST_NAME_TYPE);
+ pop(object);
+ Check(below_equal, kOperandIsNotAName);
+ }
+}
+
+
+void MacroAssembler::AssertNotSmi(Register object) {
+ if (emit_debug_code()) {
+ test(object, Immediate(kSmiTagMask));
+ Check(not_equal, kOperandIsASmi);
+ }
+}
+
+
+void MacroAssembler::EnterFrame(StackFrame::Type type) {
+ push(ebp);
+ mov(ebp, esp);
+ push(esi);
+ push(Immediate(Smi::FromInt(type)));
+ push(Immediate(CodeObject()));
+ if (emit_debug_code()) {
+ cmp(Operand(esp, 0), Immediate(isolate()->factory()->undefined_value()));
+ Check(not_equal, kCodeObjectNotProperlyPatched);
+ }
+}
+
+
+void MacroAssembler::LeaveFrame(StackFrame::Type type) {
+ if (emit_debug_code()) {
+ cmp(Operand(ebp, StandardFrameConstants::kMarkerOffset),
+ Immediate(Smi::FromInt(type)));
+ Check(equal, kStackFrameTypesMustMatch);
+ }
+ leave();
+}
+
+
+void MacroAssembler::EnterExitFramePrologue() {
+ // 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, esp);
+
+ // Reserve room for entry stack pointer and push the code object.
+ ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
+ push(Immediate(0)); // Saved entry sp, patched before call.
+ push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot.
+
+ // Save the frame pointer and the context in top.
+ ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress,
+ isolate());
+ ExternalReference context_address(Isolate::kContextAddress,
+ isolate());
+ mov(Operand::StaticVariable(c_entry_fp_address), ebp);
+ mov(Operand::StaticVariable(context_address), esi);
+}
+
+
+void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) {
+ // Optionally save all XMM registers.
+ if (save_doubles) {
+ CpuFeatureScope scope(this, SSE2);
+ int space = XMMRegister::kNumRegisters * kDoubleSize + argc * kPointerSize;
+ 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(esp, Immediate(argc * kPointerSize));
+ }
+
+ // Get the required frame alignment for the OS.
+ const int kFrameAlignment = OS::ActivationFrameAlignment();
+ if (kFrameAlignment > 0) {
+ ASSERT(IsPowerOf2(kFrameAlignment));
+ and_(esp, -kFrameAlignment);
+ }
+
+ // Patch the saved entry sp.
+ mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp);
+}
+
+
+void MacroAssembler::EnterExitFrame(bool save_doubles) {
+ EnterExitFramePrologue();
+
+ // Set up argc and argv in callee-saved registers.
+ int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
+ mov(edi, eax);
+ lea(esi, Operand(ebp, eax, times_4, offset));
+
+ // Reserve space for argc, argv and isolate.
+ EnterExitFrameEpilogue(3, save_doubles);
+}
+
+
+void MacroAssembler::EnterApiExitFrame(int argc) {
+ EnterExitFramePrologue();
+ EnterExitFrameEpilogue(argc, false);
+}
+
+
+void MacroAssembler::LeaveExitFrame(bool save_doubles) {
+ // Optionally restore all XMM registers.
+ if (save_doubles) {
+ CpuFeatureScope scope(this, SSE2);
+ const int offset = -2 * kPointerSize;
+ for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
+ XMMRegister reg = XMMRegister::from_code(i);
+ movdbl(reg, Operand(ebp, offset - ((i + 1) * kDoubleSize)));
+ }
+ }
+
+ // Get the return address from the stack and restore the frame pointer.
+ mov(ecx, Operand(ebp, 1 * kPointerSize));
+ mov(ebp, Operand(ebp, 0 * kPointerSize));
+
+ // Pop the arguments and the receiver from the caller stack.
+ lea(esp, Operand(esi, 1 * kPointerSize));
+
+ // Push the return address to get ready to return.
+ push(ecx);
+
+ LeaveExitFrameEpilogue();
+}
+
+
+void MacroAssembler::LeaveExitFrameEpilogue() {
+ // Restore current context from top and clear it in debug mode.
+ ExternalReference context_address(Isolate::kContextAddress, isolate());
+ mov(esi, Operand::StaticVariable(context_address));
+#ifdef DEBUG
+ mov(Operand::StaticVariable(context_address), Immediate(0));
+#endif
+
+ // Clear the top frame.
+ ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress,
+ isolate());
+ mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0));
+}
+
+
+void MacroAssembler::LeaveApiExitFrame() {
+ mov(esp, ebp);
+ pop(ebp);
+
+ LeaveExitFrameEpilogue();
+}
+
+
+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::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);
+ }
+ // 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);
+ 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);
+}
+
+
+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::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 stack pointer to the top of the top handler.
+ ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
+ mov(esp, Operand::StaticVariable(handler_address));
+ // 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.
+
+ // If the handler is a JS frame, restore the context to the frame.
+ // (kind == ENTRY) == (ebp == 0) == (esi == 0), so we could test either
+ // ebp or esi.
+ Label skip;
+ test(esi, esi);
+ j(zero, &skip, Label::kNear);
+ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
+ bind(&skip);
+
+ JumpToHandlerEntry();
+}
+
+
+void MacroAssembler::ThrowUncatchable(Register value) {
+ // Adjust this code if not the case.
+ STATIC_ASSERT(StackHandlerConstants::kSize == 5 * 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 eax.
+ if (!value.is(eax)) {
+ mov(eax, value);
+ }
+ // 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);
+
+ // 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);
+
+ JumpToHandlerEntry();
+}
+
+
+void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ Label* miss) {
+ Label same_contexts;
+
+ ASSERT(!holder_reg.is(scratch1));
+ ASSERT(!holder_reg.is(scratch2));
+ ASSERT(!scratch1.is(scratch2));
+
+ // Load current lexical context from the stack frame.
+ mov(scratch1, Operand(ebp, StandardFrameConstants::kContextOffset));
+
+ // When generating debug code, make sure the lexical context is set.
+ if (emit_debug_code()) {
+ cmp(scratch1, Immediate(0));
+ Check(not_equal, kWeShouldNotHaveAnEmptyLexicalContext);
+ }
+ // Load the native context of the current context.
+ int offset =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ mov(scratch1, FieldOperand(scratch1, offset));
+ mov(scratch1, FieldOperand(scratch1, GlobalObject::kNativeContextOffset));
+
+ // Check the context is a native context.
+ if (emit_debug_code()) {
+ // Read the first word and compare to native_context_map.
+ cmp(FieldOperand(scratch1, HeapObject::kMapOffset),
+ isolate()->factory()->native_context_map());
+ Check(equal, kJSGlobalObjectNativeContextShouldBeANativeContext);
+ }
+
+ // Check if both contexts are the same.
+ cmp(scratch1, FieldOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+ j(equal, &same_contexts);
+
+ // Compare security tokens, save holder_reg on the stack so we can use it
+ // as a temporary register.
+ //
+ // Check that the security token in the calling global object is
+ // compatible with the security token in the receiving global
+ // object.
+ mov(scratch2,
+ FieldOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+
+ // Check the context is a native context.
+ if (emit_debug_code()) {
+ cmp(scratch2, isolate()->factory()->null_value());
+ Check(not_equal, kJSGlobalProxyContextShouldNotBeNull);
+
+ // Read the first word and compare to native_context_map(),
+ cmp(FieldOperand(scratch2, HeapObject::kMapOffset),
+ isolate()->factory()->native_context_map());
+ Check(equal, kJSGlobalObjectNativeContextShouldBeANativeContext);
+ }
+
+ int token_offset = Context::kHeaderSize +
+ Context::SECURITY_TOKEN_INDEX * kPointerSize;
+ mov(scratch1, FieldOperand(scratch1, token_offset));
+ cmp(scratch1, FieldOperand(scratch2, token_offset));
+ j(not_equal, miss);
+
+ bind(&same_contexts);
+}
+
+
+// Compute the hash code from the untagged key. This must be kept in sync
+// with ComputeIntegerHash in utils.h.
+//
+// Note: r0 will contain hash code
+void MacroAssembler::GetNumberHash(Register r0, Register scratch) {
+ // Xor original key with a seed.
+ if (Serializer::enabled()) {
+ ExternalReference roots_array_start =
+ ExternalReference::roots_array_start(isolate());
+ mov(scratch, Immediate(Heap::kHashSeedRootIndex));
+ mov(scratch,
+ Operand::StaticArray(scratch, times_pointer_size, roots_array_start));
+ SmiUntag(scratch);
+ xor_(r0, scratch);
+ } else {
+ int32_t seed = isolate()->heap()->HashSeed();
+ xor_(r0, Immediate(seed));
+ }
+
+ // hash = ~hash + (hash << 15);
+ mov(scratch, r0);
+ not_(r0);
+ shl(scratch, 15);
+ add(r0, scratch);
+ // hash = hash ^ (hash >> 12);
+ mov(scratch, r0);
+ shr(scratch, 12);
+ 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, scratch);
+ // hash = hash * 2057;
+ imul(r0, r0, 2057);
+ // hash = hash ^ (hash >> 16);
+ mov(scratch, r0);
+ shr(scratch, 16);
+ xor_(r0, scratch);
+}
+
+
+
+void MacroAssembler::LoadFromNumberDictionary(Label* miss,
+ Register elements,
+ Register key,
+ Register r0,
+ Register r1,
+ Register r2,
+ Register result) {
+ // Register use:
+ //
+ // elements - holds the slow-case elements of the receiver and is unchanged.
+ //
+ // key - holds the smi key on entry and is unchanged.
+ //
+ // Scratch registers:
+ //
+ // r0 - holds the untagged key on entry and holds the hash once computed.
+ //
+ // r1 - used to hold the capacity mask of the dictionary
+ //
+ // r2 - used for the index into the dictionary.
+ //
+ // result - holds the result on exit if the load succeeds and we fall through.
+
+ Label done;
+
+ GetNumberHash(r0, r1);
+
+ // Compute capacity mask.
+ mov(r1, FieldOperand(elements, SeededNumberDictionary::kCapacityOffset));
+ shr(r1, kSmiTagSize); // convert smi to int
+ dec(r1);
+
+ // Generate an unrolled loop that performs a few probes before giving up.
+ const int kProbes = 4;
+ for (int i = 0; i < kProbes; i++) {
+ // Use r2 for index calculations and keep the hash intact in r0.
+ mov(r2, r0);
+ // Compute the masked index: (hash + i + i * i) & mask.
+ if (i > 0) {
+ add(r2, Immediate(SeededNumberDictionary::GetProbeOffset(i)));
+ }
+ and_(r2, r1);
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(SeededNumberDictionary::kEntrySize == 3);
+ lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3
+
+ // Check if the key matches.
+ cmp(key, FieldOperand(elements,
+ r2,
+ times_pointer_size,
+ SeededNumberDictionary::kElementsStartOffset));
+ if (i != (kProbes - 1)) {
+ j(equal, &done);
+ } else {
+ j(not_equal, miss);
+ }
+ }
+
+ bind(&done);
+ // Check that the value is a normal propety.
+ const int kDetailsOffset =
+ SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize;
+ ASSERT_EQ(NORMAL, 0);
+ test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset),
+ Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize));
+ j(not_zero, miss);
+
+ // Get the value at the masked, scaled index.
+ const int kValueOffset =
+ SeededNumberDictionary::kElementsStartOffset + kPointerSize;
+ mov(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset));
+}
+
+
+void MacroAssembler::LoadAllocationTopHelper(Register result,
+ Register scratch,
+ AllocationFlags flags) {
+ ExternalReference allocation_top =
+ AllocationUtils::GetAllocationTopReference(isolate(), flags);
+
+ // Just return if allocation top is already known.
+ if ((flags & RESULT_CONTAINS_TOP) != 0) {
+ // No use of scratch if allocation top is provided.
+ ASSERT(scratch.is(no_reg));
+#ifdef DEBUG
+ // Assert that result actually contains top on entry.
+ cmp(result, Operand::StaticVariable(allocation_top));
+ Check(equal, kUnexpectedAllocationTop);
+#endif
+ return;
+ }
+
+ // Move address of new object to result. Use scratch register if available.
+ if (scratch.is(no_reg)) {
+ mov(result, Operand::StaticVariable(allocation_top));
+ } else {
+ mov(scratch, Immediate(allocation_top));
+ mov(result, Operand(scratch, 0));
+ }
+}
+
+
+void MacroAssembler::UpdateAllocationTopHelper(Register result_end,
+ Register scratch,
+ AllocationFlags flags) {
+ if (emit_debug_code()) {
+ test(result_end, Immediate(kObjectAlignmentMask));
+ Check(zero, kUnalignedAllocationInNewSpace);
+ }
+
+ ExternalReference allocation_top =
+ AllocationUtils::GetAllocationTopReference(isolate(), flags);
+
+ // Update new top. Use scratch if available.
+ if (scratch.is(no_reg)) {
+ mov(Operand::StaticVariable(allocation_top), result_end);
+ } else {
+ mov(Operand(scratch, 0), result_end);
+ }
+}
+
+
+void MacroAssembler::Allocate(int object_size,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0);
+ ASSERT(object_size <= Page::kMaxNonCodeHeapObjectSize);
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ mov(result, Immediate(0x7091));
+ if (result_end.is_valid()) {
+ mov(result_end, Immediate(0x7191));
+ }
+ if (scratch.is_valid()) {
+ mov(scratch, Immediate(0x7291));
+ }
+ }
+ jmp(gc_required);
+ return;
+ }
+ ASSERT(!result.is(result_end));
+
+ // Load address of new object into result.
+ LoadAllocationTopHelper(result, scratch, flags);
+
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ if ((flags & DOUBLE_ALIGNMENT) != 0) {
+ ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0);
+ ASSERT(kPointerAlignment * 2 == kDoubleAlignment);
+ Label aligned;
+ test(result, Immediate(kDoubleAlignmentMask));
+ j(zero, &aligned, Label::kNear);
+ mov(Operand(result, 0),
+ Immediate(isolate()->factory()->one_pointer_filler_map()));
+ add(result, Immediate(kDoubleSize / 2));
+ bind(&aligned);
+ }
+
+ Register top_reg = result_end.is_valid() ? result_end : result;
+
+ // Calculate new top and bail out if space is exhausted.
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+
+ if (!top_reg.is(result)) {
+ mov(top_reg, result);
+ }
+ add(top_reg, Immediate(object_size));
+ j(carry, gc_required);
+ cmp(top_reg, Operand::StaticVariable(allocation_limit));
+ j(above, gc_required);
+
+ // Update allocation top.
+ UpdateAllocationTopHelper(top_reg, scratch, flags);
+
+ // Tag result if requested.
+ bool tag_result = (flags & TAG_OBJECT) != 0;
+ if (top_reg.is(result)) {
+ if (tag_result) {
+ sub(result, Immediate(object_size - kHeapObjectTag));
+ } else {
+ sub(result, Immediate(object_size));
+ }
+ } else if (tag_result) {
+ ASSERT(kHeapObjectTag == 1);
+ inc(result);
+ }
+}
+
+
+void MacroAssembler::Allocate(int header_size,
+ ScaleFactor element_size,
+ Register element_count,
+ RegisterValueType element_count_type,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT((flags & SIZE_IN_WORDS) == 0);
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ mov(result, Immediate(0x7091));
+ mov(result_end, Immediate(0x7191));
+ if (scratch.is_valid()) {
+ mov(scratch, Immediate(0x7291));
+ }
+ // Register element_count is not modified by the function.
+ }
+ jmp(gc_required);
+ return;
+ }
+ ASSERT(!result.is(result_end));
+
+ // Load address of new object into result.
+ LoadAllocationTopHelper(result, scratch, flags);
+
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ if ((flags & DOUBLE_ALIGNMENT) != 0) {
+ ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0);
+ ASSERT(kPointerAlignment * 2 == kDoubleAlignment);
+ Label aligned;
+ test(result, Immediate(kDoubleAlignmentMask));
+ j(zero, &aligned, Label::kNear);
+ mov(Operand(result, 0),
+ Immediate(isolate()->factory()->one_pointer_filler_map()));
+ add(result, Immediate(kDoubleSize / 2));
+ bind(&aligned);
+ }
+
+ // Calculate new top and bail out if space is exhausted.
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+
+ // We assume that element_count*element_size + header_size does not
+ // overflow.
+ if (element_count_type == REGISTER_VALUE_IS_SMI) {
+ STATIC_ASSERT(static_cast<ScaleFactor>(times_2 - 1) == times_1);
+ STATIC_ASSERT(static_cast<ScaleFactor>(times_4 - 1) == times_2);
+ STATIC_ASSERT(static_cast<ScaleFactor>(times_8 - 1) == times_4);
+ ASSERT(element_size >= times_2);
+ ASSERT(kSmiTagSize == 1);
+ element_size = static_cast<ScaleFactor>(element_size - 1);
+ } else {
+ ASSERT(element_count_type == REGISTER_VALUE_IS_INT32);
+ }
+ lea(result_end, Operand(element_count, element_size, header_size));
+ add(result_end, result);
+ j(carry, gc_required);
+ cmp(result_end, Operand::StaticVariable(allocation_limit));
+ j(above, gc_required);
+
+ if ((flags & TAG_OBJECT) != 0) {
+ ASSERT(kHeapObjectTag == 1);
+ inc(result);
+ }
+
+ // Update allocation top.
+ UpdateAllocationTopHelper(result_end, scratch, flags);
+}
+
+
+void MacroAssembler::Allocate(Register object_size,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0);
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ mov(result, Immediate(0x7091));
+ mov(result_end, Immediate(0x7191));
+ if (scratch.is_valid()) {
+ mov(scratch, Immediate(0x7291));
+ }
+ // object_size is left unchanged by this function.
+ }
+ jmp(gc_required);
+ return;
+ }
+ ASSERT(!result.is(result_end));
+
+ // Load address of new object into result.
+ LoadAllocationTopHelper(result, scratch, flags);
+
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ if ((flags & DOUBLE_ALIGNMENT) != 0) {
+ ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0);
+ ASSERT(kPointerAlignment * 2 == kDoubleAlignment);
+ Label aligned;
+ test(result, Immediate(kDoubleAlignmentMask));
+ j(zero, &aligned, Label::kNear);
+ mov(Operand(result, 0),
+ Immediate(isolate()->factory()->one_pointer_filler_map()));
+ add(result, Immediate(kDoubleSize / 2));
+ bind(&aligned);
+ }
+
+ // Calculate new top and bail out if space is exhausted.
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+
+ if (!object_size.is(result_end)) {
+ mov(result_end, object_size);
+ }
+ add(result_end, result);
+ j(carry, gc_required);
+ cmp(result_end, Operand::StaticVariable(allocation_limit));
+ j(above, gc_required);
+
+ // Tag result if requested.
+ if ((flags & TAG_OBJECT) != 0) {
+ ASSERT(kHeapObjectTag == 1);
+ inc(result);
+ }
+
+ // Update allocation top.
+ UpdateAllocationTopHelper(result_end, scratch, flags);
+}
+
+
+void MacroAssembler::UndoAllocationInNewSpace(Register object) {
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+
+ // Make sure the object has no tag before resetting top.
+ and_(object, Immediate(~kHeapObjectTagMask));
+#ifdef DEBUG
+ cmp(object, Operand::StaticVariable(new_space_allocation_top));
+ Check(below, kUndoAllocationOfNonAllocatedMemory);
+#endif
+ mov(Operand::StaticVariable(new_space_allocation_top), object);
+}
+
+
+void MacroAssembler::AllocateHeapNumber(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ // Set the map.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->heap_number_map()));
+}
+
+
+void MacroAssembler::AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ ASSERT(kShortSize == 2);
+ // scratch1 = length * 2 + kObjectAlignmentMask.
+ lea(scratch1, Operand(length, length, times_1, kObjectAlignmentMask));
+ and_(scratch1, Immediate(~kObjectAlignmentMask));
+
+ // Allocate two byte string in new space.
+ Allocate(SeqTwoByteString::kHeaderSize,
+ times_1,
+ scratch1,
+ REGISTER_VALUE_IS_INT32,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->string_map()));
+ mov(scratch1, length);
+ SmiTag(scratch1);
+ mov(FieldOperand(result, String::kLengthOffset), scratch1);
+ mov(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ mov(scratch1, length);
+ ASSERT(kCharSize == 1);
+ add(scratch1, Immediate(kObjectAlignmentMask));
+ and_(scratch1, Immediate(~kObjectAlignmentMask));
+
+ // Allocate ASCII string in new space.
+ Allocate(SeqOneByteString::kHeaderSize,
+ times_1,
+ scratch1,
+ REGISTER_VALUE_IS_INT32,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->ascii_string_map()));
+ mov(scratch1, length);
+ SmiTag(scratch1);
+ mov(FieldOperand(result, String::kLengthOffset), scratch1);
+ mov(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateAsciiString(Register result,
+ int length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ ASSERT(length > 0);
+
+ // Allocate ASCII string in new space.
+ Allocate(SeqOneByteString::SizeFor(length), result, scratch1, scratch2,
+ gc_required, TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->ascii_string_map()));
+ mov(FieldOperand(result, String::kLengthOffset),
+ Immediate(Smi::FromInt(length)));
+ mov(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateTwoByteConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ Allocate(ConsString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->cons_string_map()));
+}
+
+
+void MacroAssembler::AllocateAsciiConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Label allocate_new_space, install_map;
+ AllocationFlags flags = TAG_OBJECT;
+
+ ExternalReference high_promotion_mode = ExternalReference::
+ new_space_high_promotion_mode_active_address(isolate());
+
+ test(Operand::StaticVariable(high_promotion_mode), Immediate(1));
+ j(zero, &allocate_new_space);
+
+ Allocate(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ static_cast<AllocationFlags>(flags | PRETENURE_OLD_POINTER_SPACE));
+ jmp(&install_map);
+
+ bind(&allocate_new_space);
+ Allocate(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ flags);
+
+ bind(&install_map);
+ // Set the map. The other fields are left uninitialized.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->cons_ascii_string_map()));
+}
+
+
+void MacroAssembler::AllocateTwoByteSlicedString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->sliced_string_map()));
+}
+
+
+void MacroAssembler::AllocateAsciiSlicedString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ mov(FieldOperand(result, HeapObject::kMapOffset),
+ Immediate(isolate()->factory()->sliced_ascii_string_map()));
+}
+
+
+// Copy memory, byte-by-byte, from source to destination. Not optimized for
+// long or aligned copies. The contents of scratch and length are destroyed.
+// Source and destination are incremented by length.
+// Many variants of movsb, loop unrolling, word moves, and indexed operands
+// have been tried here already, and this is fastest.
+// A simpler loop is faster on small copies, but 30% slower on large ones.
+// The cld() instruction must have been emitted, to set the direction flag(),
+// before calling this function.
+void MacroAssembler::CopyBytes(Register source,
+ Register destination,
+ Register length,
+ Register scratch) {
+ Label loop, done, short_string, short_loop;
+ // Experimentation shows that the short string loop is faster if length < 10.
+ cmp(length, Immediate(10));
+ j(less_equal, &short_string);
+
+ ASSERT(source.is(esi));
+ ASSERT(destination.is(edi));
+ ASSERT(length.is(ecx));
+
+ // Because source is 4-byte aligned in our uses of this function,
+ // we keep source aligned for the rep_movs call by copying the odd bytes
+ // at the end of the ranges.
+ mov(scratch, Operand(source, length, times_1, -4));
+ mov(Operand(destination, length, times_1, -4), scratch);
+ mov(scratch, ecx);
+ shr(ecx, 2);
+ rep_movs();
+ and_(scratch, Immediate(0x3));
+ add(destination, scratch);
+ jmp(&done);
+
+ bind(&short_string);
+ test(length, length);
+ j(zero, &done);
+
+ bind(&short_loop);
+ mov_b(scratch, Operand(source, 0));
+ mov_b(Operand(destination, 0), scratch);
+ inc(source);
+ inc(destination);
+ dec(length);
+ j(not_zero, &short_loop);
+
+ bind(&done);
+}
+
+
+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, result);
+ j(not_zero, &ok);
+ test(op, op);
+ j(sign, then_label);
+ bind(&ok);
+}
+
+
+void MacroAssembler::NegativeZeroTest(Register result,
+ Register op1,
+ Register op2,
+ Register scratch,
+ Label* then_label) {
+ Label ok;
+ test(result, result);
+ j(not_zero, &ok);
+ mov(scratch, op1);
+ or_(scratch, op2);
+ j(sign, then_label);
+ bind(&ok);
+}
+
+
+void MacroAssembler::TryGetFunctionPrototype(Register function,
+ Register result,
+ Register scratch,
+ Label* miss,
+ bool miss_on_bound_function) {
+ // Check that the receiver isn't a smi.
+ JumpIfSmi(function, miss);
+
+ // Check that the function really is a 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));
+ test(scratch, Immediate(1 << Map::kHasNonInstancePrototype));
+ j(not_zero, &non_instance);
+
+ // Get the prototype or initial map from the function.
+ mov(result,
+ FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // 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(result, Immediate(isolate()->factory()->the_hole_value()));
+ j(equal, miss);
+
+ // If the function does not have an initial map, we're done.
+ Label done;
+ CmpObjectType(result, MAP_TYPE, scratch);
+ j(not_equal, &done);
+
+ // Get the prototype from the initial map.
+ mov(result, FieldOperand(result, Map::kPrototypeOffset));
+ jmp(&done);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in initial map.
+ bind(&non_instance);
+ mov(result, FieldOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ bind(&done);
+}
+
+
+void MacroAssembler::CallStub(CodeStub* stub, TypeFeedbackId ast_id) {
+ ASSERT(AllowThisStubCall(stub)); // Calls are not allowed in some stubs.
+ call(stub->GetCode(isolate()), RelocInfo::CODE_TARGET, ast_id);
+}
+
+
+void MacroAssembler::TailCallStub(CodeStub* stub) {
+ ASSERT(allow_stub_calls_ ||
+ stub->CompilingCallsToThisStubIsGCSafe(isolate()));
+ jmp(stub->GetCode(isolate()), RelocInfo::CODE_TARGET);
+}
+
+
+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(isolate());
+}
+
+
+void MacroAssembler::IllegalOperation(int num_arguments) {
+ if (num_arguments > 0) {
+ add(esp, Immediate(num_arguments * kPointerSize));
+ }
+ mov(eax, Immediate(isolate()->factory()->undefined_value()));
+}
+
+
+void MacroAssembler::IndexFromHash(Register hash, Register index) {
+ // The assert checks that the constants for the maximum number of digits
+ // for an array index cached in the hash field and the number of bits
+ // reserved for it does not conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
+ // the low kHashShift bits.
+ and_(hash, String::kArrayIndexValueMask);
+ STATIC_ASSERT(String::kHashShift >= kSmiTagSize && kSmiTag == 0);
+ if (String::kHashShift > kSmiTagSize) {
+ shr(hash, String::kHashShift - kSmiTagSize);
+ }
+ if (!index.is(hash)) {
+ mov(index, hash);
+ }
+}
+
+
+void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
+ CallRuntime(Runtime::FunctionForId(id), num_arguments);
+}
+
+
+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, CpuFeatures::IsSupported(SSE2) ? kSaveFPRegs
+ : kDontSaveFPRegs);
+ CallStub(&ces);
+}
+
+
+void MacroAssembler::CallRuntime(const Runtime::Function* f,
+ int num_arguments) {
+ // If the expected number of arguments of the runtime function is
+ // constant, we check that the actual number of arguments match the
+ // expectation.
+ if (f->nargs >= 0 && f->nargs != num_arguments) {
+ IllegalOperation(num_arguments);
+ return;
+ }
+
+ // 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);
+ CallStub(&ces);
+}
+
+
+void MacroAssembler::CallExternalReference(ExternalReference ref,
+ int num_arguments) {
+ mov(eax, Immediate(num_arguments));
+ mov(ebx, Immediate(ref));
+
+ CEntryStub stub(1);
+ CallStub(&stub);
+}
+
+
+void MacroAssembler::TailCallExternalReference(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));
+ JumpToExternalReference(ext);
+}
+
+
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(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.
+// If false, it is returned as a pointer to a preallocated by caller memory
+// region. Pointer to this region should be passed to a function as an
+// implicit first argument.
+#if defined(USING_BSD_ABI) || defined(__MINGW32__) || defined(__CYGWIN__)
+static const bool kReturnHandlesDirectly = true;
+#else
+static const bool kReturnHandlesDirectly = false;
+#endif
+
+
+Operand ApiParameterOperand(int index, bool returns_handle) {
+ int offset = (index +(kReturnHandlesDirectly || !returns_handle ? 0 : 1));
+ return Operand(esp, offset * kPointerSize);
+}
+
+
+void MacroAssembler::PrepareCallApiFunction(int argc, bool returns_handle) {
+ if (kReturnHandlesDirectly || !returns_handle) {
+ EnterApiExitFrame(argc);
+ // When handles are returned directly we don't have to allocate extra
+ // space for and pass an out parameter.
+ if (emit_debug_code()) {
+ mov(esi, Immediate(BitCast<int32_t>(kZapValue)));
+ }
+ } else {
+ // We allocate two additional slots: return value and pointer to it.
+ EnterApiExitFrame(argc + 2);
+
+ // The argument slots are filled as follows:
+ //
+ // n + 1: output slot
+ // n: arg n
+ // ...
+ // 1: arg1
+ // 0: pointer to the output slot
+
+ lea(esi, Operand(esp, (argc + 1) * kPointerSize));
+ mov(Operand(esp, 0 * kPointerSize), esi);
+ if (emit_debug_code()) {
+ mov(Operand(esi, 0), Immediate(0));
+ }
+ }
+}
+
+
+void MacroAssembler::CallApiFunctionAndReturn(Address function_address,
+ Address thunk_address,
+ Operand thunk_last_arg,
+ int stack_space,
+ bool returns_handle,
+ int return_value_offset) {
+ ExternalReference next_address =
+ ExternalReference::handle_scope_next_address(isolate());
+ ExternalReference limit_address =
+ ExternalReference::handle_scope_limit_address(isolate());
+ ExternalReference level_address =
+ ExternalReference::handle_scope_level_address(isolate());
+
+ // Allocate HandleScope in callee-save registers.
+ mov(ebx, Operand::StaticVariable(next_address));
+ mov(edi, Operand::StaticVariable(limit_address));
+ add(Operand::StaticVariable(level_address), Immediate(1));
+
+ if (FLAG_log_timer_events) {
+ FrameScope frame(this, StackFrame::MANUAL);
+ PushSafepointRegisters();
+ PrepareCallCFunction(1, eax);
+ mov(Operand(esp, 0),
+ Immediate(ExternalReference::isolate_address(isolate())));
+ CallCFunction(ExternalReference::log_enter_external_function(isolate()), 1);
+ PopSafepointRegisters();
+ }
+
+
+ Label profiler_disabled;
+ Label end_profiler_check;
+ bool* is_profiling_flag =
+ isolate()->cpu_profiler()->is_profiling_address();
+ STATIC_ASSERT(sizeof(*is_profiling_flag) == 1);
+ mov(eax, Immediate(reinterpret_cast<Address>(is_profiling_flag)));
+ cmpb(Operand(eax, 0), 0);
+ j(zero, &profiler_disabled);
+
+ // Additional parameter is the address of the actual getter function.
+ mov(thunk_last_arg, Immediate(function_address));
+ // Call the api function.
+ call(thunk_address, RelocInfo::RUNTIME_ENTRY);
+ jmp(&end_profiler_check);
+
+ bind(&profiler_disabled);
+ // Call the api function.
+ call(function_address, RelocInfo::RUNTIME_ENTRY);
+ bind(&end_profiler_check);
+
+ if (FLAG_log_timer_events) {
+ FrameScope frame(this, StackFrame::MANUAL);
+ PushSafepointRegisters();
+ PrepareCallCFunction(1, eax);
+ mov(Operand(esp, 0),
+ Immediate(ExternalReference::isolate_address(isolate())));
+ CallCFunction(ExternalReference::log_leave_external_function(isolate()), 1);
+ PopSafepointRegisters();
+ }
+
+ Label prologue;
+ if (returns_handle) {
+ if (!kReturnHandlesDirectly) {
+ // PrepareCallApiFunction saved pointer to the output slot into
+ // callee-save register esi.
+ mov(eax, Operand(esi, 0));
+ }
+ Label empty_handle;
+ // Check if the result handle holds 0.
+ test(eax, eax);
+ j(zero, &empty_handle);
+ // It was non-zero. Dereference to get the result value.
+ mov(eax, Operand(eax, 0));
+ jmp(&prologue);
+ bind(&empty_handle);
+ }
+ // Load the value from ReturnValue
+ mov(eax, Operand(ebp, return_value_offset * kPointerSize));
+
+ Label promote_scheduled_exception;
+ Label delete_allocated_handles;
+ Label leave_exit_frame;
+
+ bind(&prologue);
+ // No more valid handles (the result handle was the last one). Restore
+ // previous handle scope.
+ mov(Operand::StaticVariable(next_address), ebx);
+ sub(Operand::StaticVariable(level_address), Immediate(1));
+ Assert(above_equal, kInvalidHandleScopeLevel);
+ cmp(edi, Operand::StaticVariable(limit_address));
+ j(not_equal, &delete_allocated_handles);
+ bind(&leave_exit_frame);
+
+ // Check if the function scheduled an exception.
+ ExternalReference scheduled_exception_address =
+ ExternalReference::scheduled_exception_address(isolate());
+ cmp(Operand::StaticVariable(scheduled_exception_address),
+ Immediate(isolate()->factory()->the_hole_value()));
+ j(not_equal, &promote_scheduled_exception);
+
+#if ENABLE_EXTRA_CHECKS
+ // Check if the function returned a valid JavaScript value.
+ Label ok;
+ Register return_value = eax;
+ Register map = ecx;
+
+ JumpIfSmi(return_value, &ok, Label::kNear);
+ mov(map, FieldOperand(return_value, HeapObject::kMapOffset));
+
+ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
+ j(below, &ok, Label::kNear);
+
+ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
+ j(above_equal, &ok, Label::kNear);
+
+ cmp(map, isolate()->factory()->heap_number_map());
+ j(equal, &ok, Label::kNear);
+
+ cmp(return_value, isolate()->factory()->undefined_value());
+ j(equal, &ok, Label::kNear);
+
+ cmp(return_value, isolate()->factory()->true_value());
+ j(equal, &ok, Label::kNear);
+
+ cmp(return_value, isolate()->factory()->false_value());
+ j(equal, &ok, Label::kNear);
+
+ cmp(return_value, isolate()->factory()->null_value());
+ j(equal, &ok, Label::kNear);
+
+ Abort(kAPICallReturnedInvalidObject);
+
+ bind(&ok);
+#endif
+
+ LeaveApiExitFrame();
+ ret(stack_space * kPointerSize);
+
+ bind(&promote_scheduled_exception);
+ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
+
+ // HandleScope limit has changed. Delete allocated extensions.
+ ExternalReference delete_extensions =
+ ExternalReference::delete_handle_scope_extensions(isolate());
+ bind(&delete_allocated_handles);
+ mov(Operand::StaticVariable(limit_address), edi);
+ mov(edi, eax);
+ mov(Operand(esp, 0),
+ Immediate(ExternalReference::isolate_address(isolate())));
+ mov(eax, Immediate(delete_extensions));
+ call(eax);
+ mov(eax, edi);
+ jmp(&leave_exit_frame);
+}
+
+
+void MacroAssembler::JumpToExternalReference(const ExternalReference& ext) {
+ // Set the entry point and jump to the C entry runtime stub.
+ mov(ebx, Immediate(ext));
+ CEntryStub ces(1);
+ jmp(ces.GetCode(isolate()), RelocInfo::CODE_TARGET);
+}
+
+
+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
+ // follow the calling convention which requires the call type to be
+ // in ecx.
+ ASSERT(dst.is(ecx));
+ if (call_kind == CALL_AS_FUNCTION) {
+ // Set to some non-zero smi by updating the least significant
+ // byte.
+ mov_b(dst, 1 << kSmiTagSize);
+ } else {
+ // Set to smi zero by clearing the register.
+ xor_(dst, dst);
+ }
+}
+
+
+void MacroAssembler::InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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());
+ if (expected.immediate() == actual.immediate()) {
+ definitely_matches = true;
+ } else {
+ mov(eax, actual.immediate());
+ const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+ if (expected.immediate() == sentinel) {
+ // Don't worry about adapting arguments for builtins that
+ // don't want that done. Skip adaption code by making it look
+ // like we have a match between expected and actual number of
+ // arguments.
+ definitely_matches = true;
+ } else {
+ *definitely_mismatches = true;
+ mov(ebx, expected.immediate());
+ }
+ }
+ } else {
+ if (actual.is_immediate()) {
+ // Expected is in register, actual is immediate. This is the
+ // case when we invoke function values without going through the
+ // IC mechanism.
+ cmp(expected.reg(), actual.immediate());
+ j(equal, &invoke);
+ ASSERT(expected.reg().is(ebx));
+ mov(eax, actual.immediate());
+ } 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(), actual.reg());
+ j(equal, &invoke);
+ ASSERT(actual.reg().is(eax));
+ ASSERT(expected.reg().is(ebx));
+ }
+ }
+
+ if (!definitely_matches) {
+ Handle<Code> adaptor =
+ isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ if (!code_constant.is_null()) {
+ mov(edx, Immediate(code_constant));
+ add(edx, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ } else if (!code_operand.is_reg(edx)) {
+ mov(edx, code_operand);
+ }
+
+ if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET));
+ SetCallKind(ecx, call_kind);
+ call(adaptor, RelocInfo::CODE_TARGET);
+ call_wrapper.AfterCall();
+ if (!*definitely_mismatches) {
+ jmp(done, done_near);
+ }
+ } else {
+ SetCallKind(ecx, call_kind);
+ jmp(adaptor, RelocInfo::CODE_TARGET);
+ }
+ bind(&invoke);
+ }
+}
+
+
+void MacroAssembler::InvokeCode(const Operand& code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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 (!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);
+ }
+}
+
+
+void MacroAssembler::InvokeCode(Handle<Code> code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ RelocInfo::Mode rmode,
+ 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, 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);
+ }
+}
+
+
+void MacroAssembler::InvokeFunction(Register fun,
+ const ParameterCount& actual,
+ 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));
+ mov(ebx, FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+ SmiUntag(ebx);
+
+ ParameterCount expected(ebx);
+ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
+ expected, actual, flag, call_wrapper, call_kind);
+}
+
+
+void MacroAssembler::InvokeFunction(Handle<JSFunction> function,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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());
+
+ // Get the function and setup the context.
+ LoadHeapObject(edi, function);
+ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+ // 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) {
+ // 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
+ // parameter count to avoid emitting code to do the check.
+ ParameterCount expected(0);
+ GetBuiltinFunction(edi, id);
+ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
+ 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.
+ mov(target, Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ mov(target, FieldOperand(target, GlobalObject::kBuiltinsOffset));
+ mov(target, FieldOperand(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.
+ GetBuiltinFunction(edi, id);
+ // Load the code entry point from the function into the target register.
+ mov(target, FieldOperand(edi, JSFunction::kCodeEntryOffset));
+}
+
+
+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.
+ mov(dst, Operand(esi, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ for (int i = 1; i < context_chain_length; i++) {
+ mov(dst, Operand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ }
+ } else {
+ // Slot is in the current function context. Move it into the
+ // destination register in case we store into it (the write barrier
+ // cannot be allowed to destroy the context in esi).
+ mov(dst, esi);
+ }
+
+ // We should not have found a with context by walking the context chain
+ // (i.e., the static scope chain and runtime context chain do not agree).
+ // A variable occurring in such a scope should have slot type LOOKUP and
+ // not CONTEXT.
+ if (emit_debug_code()) {
+ cmp(FieldOperand(dst, HeapObject::kMapOffset),
+ isolate()->factory()->with_context_map());
+ Check(not_equal, kVariableResolvedToWithContext);
+ }
+}
+
+
+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_OBJECT_INDEX)));
+ mov(scratch, FieldOperand(scratch, GlobalObject::kNativeContextOffset));
+
+ // Check that the function's map is the same as the expected cached map.
+ mov(scratch, Operand(scratch,
+ Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX)));
+
+ size_t offset = expected_kind * kPointerSize +
+ FixedArrayBase::kHeaderSize;
+ cmp(map_in_out, FieldOperand(scratch, offset));
+ j(not_equal, no_map_match);
+
+ // Use the transitioned cached map.
+ offset = transitioned_kind * kPointerSize +
+ FixedArrayBase::kHeaderSize;
+ mov(map_in_out, FieldOperand(scratch, offset));
+}
+
+
+void MacroAssembler::LoadInitialArrayMap(
+ Register function_in, Register scratch,
+ Register map_out, bool can_have_holes) {
+ ASSERT(!function_in.is(map_out));
+ Label done;
+ mov(map_out, FieldOperand(function_in,
+ JSFunction::kPrototypeOrInitialMapOffset));
+ if (!FLAG_smi_only_arrays) {
+ ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
+ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ kind,
+ map_out,
+ scratch,
+ &done);
+ } else if (can_have_holes) {
+ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_HOLEY_SMI_ELEMENTS,
+ map_out,
+ scratch,
+ &done);
+ }
+ bind(&done);
+}
+
+
+void MacroAssembler::LoadGlobalContext(Register global_context) {
+ // Load the global or builtins object from the current context.
+ mov(global_context,
+ Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ // Load the native context from the global or builtins object.
+ mov(global_context,
+ FieldOperand(global_context, GlobalObject::kNativeContextOffset));
+}
+
+
+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_OBJECT_INDEX)));
+ // Load the native context from the global or builtins object.
+ mov(function,
+ FieldOperand(function, GlobalObject::kNativeContextOffset));
+ // Load the function from the native context.
+ mov(function, Operand(function, Context::SlotOffset(index)));
+}
+
+
+void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
+ Register map) {
+ // Load the initial map. The global functions all have initial maps.
+ mov(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+ if (emit_debug_code()) {
+ Label ok, fail;
+ CheckMap(map, isolate()->factory()->meta_map(), &fail, DO_SMI_CHECK);
+ jmp(&ok);
+ bind(&fail);
+ Abort(kGlobalFunctionsMustHaveInitialMap);
+ bind(&ok);
+ }
+}
+
+
+// Store the value in register src in the safepoint register stack
+// slot for register dst.
+void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) {
+ mov(SafepointRegisterSlot(dst), src);
+}
+
+
+void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Immediate src) {
+ mov(SafepointRegisterSlot(dst), src);
+}
+
+
+void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
+ mov(dst, SafepointRegisterSlot(src));
+}
+
+
+Operand MacroAssembler::SafepointRegisterSlot(Register reg) {
+ return Operand(esp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
+}
+
+
+int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
+ // The registers are pushed starting with the lowest encoding,
+ // which means that lowest encodings are furthest away from
+ // the stack pointer.
+ ASSERT(reg_code >= 0 && reg_code < kNumSafepointRegisters);
+ return kNumSafepointRegisters - reg_code - 1;
+}
+
+
+void MacroAssembler::LoadHeapObject(Register result,
+ Handle<HeapObject> object) {
+ AllowDeferredHandleDereference embedding_raw_address;
+ if (isolate()->heap()->InNewSpace(*object)) {
+ Handle<Cell> cell = isolate()->factory()->NewCell(object);
+ mov(result, Operand::ForCell(cell));
+ } else {
+ mov(result, object);
+ }
+}
+
+
+void MacroAssembler::CmpHeapObject(Register reg, Handle<HeapObject> object) {
+ AllowDeferredHandleDereference using_raw_address;
+ if (isolate()->heap()->InNewSpace(*object)) {
+ Handle<Cell> cell = isolate()->factory()->NewCell(object);
+ cmp(reg, Operand::ForCell(cell));
+ } else {
+ cmp(reg, object);
+ }
+}
+
+
+void MacroAssembler::PushHeapObject(Handle<HeapObject> object) {
+ AllowDeferredHandleDereference using_raw_address;
+ if (isolate()->heap()->InNewSpace(*object)) {
+ Handle<Cell> cell = isolate()->factory()->NewCell(object);
+ push(Operand::ForCell(cell));
+ } else {
+ Push(object);
+ }
+}
+
+
+void MacroAssembler::Ret() {
+ ret(0);
+}
+
+
+void MacroAssembler::Ret(int bytes_dropped, Register scratch) {
+ if (is_uint16(bytes_dropped)) {
+ ret(bytes_dropped);
+ } else {
+ pop(scratch);
+ add(esp, Immediate(bytes_dropped));
+ push(scratch);
+ ret(0);
+ }
+}
+
+
+void MacroAssembler::VerifyX87StackDepth(uint32_t depth) {
+ // Make sure the floating point stack is either empty or has depth items.
+ ASSERT(depth <= 7);
+
+ // The top-of-stack (tos) is 7 if there is one item pushed.
+ int tos = (8 - depth) % 8;
+ const int kTopMask = 0x3800;
+ push(eax);
+ fwait();
+ fnstsw_ax();
+ and_(eax, kTopMask);
+ shr(eax, 11);
+ cmp(eax, Immediate(tos));
+ Check(equal, kUnexpectedFPUStackDepthAfterInstruction);
+ fnclex();
+ pop(eax);
+}
+
+
+void MacroAssembler::Drop(int stack_elements) {
+ if (stack_elements > 0) {
+ add(esp, Immediate(stack_elements * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::Move(Register dst, Register src) {
+ if (!dst.is(src)) {
+ mov(dst, src);
+ }
+}
+
+
+void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value));
+ }
+}
+
+
+void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ Operand operand = Operand::StaticVariable(ExternalReference(counter));
+ if (value == 1) {
+ inc(operand);
+ } else {
+ add(operand, Immediate(value));
+ }
+ }
+}
+
+
+void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ Operand operand = Operand::StaticVariable(ExternalReference(counter));
+ if (value == 1) {
+ dec(operand);
+ } else {
+ sub(operand, Immediate(value));
+ }
+ }
+}
+
+
+void MacroAssembler::IncrementCounter(Condition cc,
+ StatsCounter* counter,
+ int value) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ Label skip;
+ j(NegateCondition(cc), &skip);
+ pushfd();
+ IncrementCounter(counter, value);
+ popfd();
+ bind(&skip);
+ }
+}
+
+
+void MacroAssembler::DecrementCounter(Condition cc,
+ StatsCounter* counter,
+ int value) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ Label skip;
+ j(NegateCondition(cc), &skip);
+ pushfd();
+ DecrementCounter(counter, value);
+ popfd();
+ bind(&skip);
+ }
+}
+
+
+void MacroAssembler::Assert(Condition cc, BailoutReason reason) {
+ if (emit_debug_code()) Check(cc, reason);
+}
+
+
+void MacroAssembler::AssertFastElements(Register elements) {
+ if (emit_debug_code()) {
+ Factory* factory = isolate()->factory();
+ Label ok;
+ cmp(FieldOperand(elements, HeapObject::kMapOffset),
+ Immediate(factory->fixed_array_map()));
+ j(equal, &ok);
+ cmp(FieldOperand(elements, HeapObject::kMapOffset),
+ Immediate(factory->fixed_double_array_map()));
+ j(equal, &ok);
+ cmp(FieldOperand(elements, HeapObject::kMapOffset),
+ Immediate(factory->fixed_cow_array_map()));
+ j(equal, &ok);
+ Abort(kJSObjectWithFastElementsMapHasSlowElements);
+ bind(&ok);
+ }
+}
+
+
+void MacroAssembler::Check(Condition cc, BailoutReason reason) {
+ Label L;
+ j(cc, &L);
+ Abort(reason);
+ // will not return here
+ bind(&L);
+}
+
+
+void MacroAssembler::CheckStackAlignment() {
+ int frame_alignment = OS::ActivationFrameAlignment();
+ int frame_alignment_mask = frame_alignment - 1;
+ if (frame_alignment > kPointerSize) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ Label alignment_as_expected;
+ test(esp, Immediate(frame_alignment_mask));
+ j(zero, &alignment_as_expected);
+ // Abort if stack is not aligned.
+ int3();
+ bind(&alignment_as_expected);
+ }
+}
+
+
+void MacroAssembler::Abort(BailoutReason reason) {
+ // We want to pass the msg string like a smi to avoid GC
+ // problems, however msg is not guaranteed to be aligned
+ // properly. Instead, we pass an aligned pointer that is
+ // a proper v8 smi, but also pass the alignment difference
+ // from the real pointer as a smi.
+ const char* msg = GetBailoutReason(reason);
+ intptr_t p1 = reinterpret_cast<intptr_t>(msg);
+ intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
+ ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
+#ifdef DEBUG
+ if (msg != NULL) {
+ RecordComment("Abort message: ");
+ RecordComment(msg);
+ }
+#endif
+
+ push(eax);
+ push(Immediate(p0));
+ push(Immediate(reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0))));
+ // 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();
+}
+
+
+void MacroAssembler::LoadInstanceDescriptors(Register map,
+ Register descriptors) {
+ mov(descriptors, FieldOperand(map, Map::kDescriptorsOffset));
+}
+
+
+void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
+ mov(dst, FieldOperand(map, Map::kBitField3Offset));
+ DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
+}
+
+
+void MacroAssembler::LoadPowerOf2(XMMRegister dst,
+ Register scratch,
+ int power) {
+ ASSERT(is_uintn(power + HeapNumber::kExponentBias,
+ HeapNumber::kExponentBits));
+ mov(scratch, Immediate(power + HeapNumber::kExponentBias));
+ movd(dst, scratch);
+ psllq(dst, HeapNumber::kMantissaBits);
+}
+
+
+void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(
+ Register instance_type,
+ Register scratch,
+ Label* failure) {
+ if (!scratch.is(instance_type)) {
+ mov(scratch, instance_type);
+ }
+ and_(scratch,
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
+ cmp(scratch, kStringTag | kSeqStringTag | kOneByteStringTag);
+ j(not_equal, failure);
+}
+
+
+void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register object1,
+ Register object2,
+ Register scratch1,
+ Register scratch2,
+ Label* failure) {
+ // Check that both objects are not smis.
+ STATIC_ASSERT(kSmiTag == 0);
+ mov(scratch1, object1);
+ and_(scratch1, object2);
+ JumpIfSmi(scratch1, failure);
+
+ // Load instance type for both strings.
+ mov(scratch1, FieldOperand(object1, HeapObject::kMapOffset));
+ mov(scratch2, FieldOperand(object2, HeapObject::kMapOffset));
+ movzx_b(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset));
+ movzx_b(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset));
+
+ // Check that both are flat ASCII strings.
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+ const int kFlatAsciiStringTag =
+ kStringTag | kOneByteStringTag | kSeqStringTag;
+ // Interleave bits from both instance types and compare them in one check.
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
+ and_(scratch1, kFlatAsciiStringMask);
+ and_(scratch2, kFlatAsciiStringMask);
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
+ cmp(scratch1, kFlatAsciiStringTag | (kFlatAsciiStringTag << 3));
+ j(not_equal, failure);
+}
+
+
+void MacroAssembler::JumpIfNotUniqueName(Operand operand,
+ Label* not_unique_name,
+ Label::Distance distance) {
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ Label succeed;
+ test(operand, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
+ j(zero, &succeed);
+ cmpb(operand, static_cast<uint8_t>(SYMBOL_TYPE));
+ j(not_equal, not_unique_name, distance);
+
+ bind(&succeed);
+}
+
+
+void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
+ int frame_alignment = OS::ActivationFrameAlignment();
+ if (frame_alignment != 0) {
+ // Make stack end at alignment and make room for num_arguments words
+ // and the original value of esp.
+ mov(scratch, esp);
+ sub(esp, Immediate((num_arguments + 1) * kPointerSize));
+ ASSERT(IsPowerOf2(frame_alignment));
+ and_(esp, -frame_alignment);
+ mov(Operand(esp, num_arguments * kPointerSize), scratch);
+ } else {
+ sub(esp, Immediate(num_arguments * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::CallCFunction(ExternalReference function,
+ int num_arguments) {
+ // Trashing eax is ok as it will be the return value.
+ 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(function);
+ if (OS::ActivationFrameAlignment() != 0) {
+ mov(esp, Operand(esp, num_arguments * kPointerSize));
+ } else {
+ 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),
+ masm_(NULL, address, size + Assembler::kGap) {
+ // Create a new macro assembler pointing to the address of the code to patch.
+ // The size is adjusted with kGap on order for the assembler to generate size
+ // bytes of instructions without failing with buffer size constraints.
+ ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
+}
+
+
+CodePatcher::~CodePatcher() {
+ // Indicate that code has changed.
+ CPU::FlushICache(address_, size_);
+
+ // Check that the code was patched as expected.
+ ASSERT(masm_.pc_ == address_ + size_);
+ 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 {
+ 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::CheckPageFlagForMap(
+ Handle<Map> map,
+ int mask,
+ Condition cc,
+ Label* condition_met,
+ Label::Distance condition_met_distance) {
+ ASSERT(cc == zero || cc == not_zero);
+ Page* page = Page::FromAddress(map->address());
+ ExternalReference reference(ExternalReference::page_flags(page));
+ // The inlined static address check of the page's flags relies
+ // on maps never being compacted.
+ ASSERT(!isolate()->heap()->mark_compact_collector()->
+ IsOnEvacuationCandidate(*map));
+ if (mask < (1 << kBitsPerByte)) {
+ test_b(Operand::StaticVariable(reference), static_cast<uint8_t>(mask));
+ } else {
+ test(Operand::StaticVariable(reference), Immediate(mask));
+ }
+ j(cc, condition_met, condition_met_distance);
+}
+
+
+void MacroAssembler::CheckMapDeprecated(Handle<Map> map,
+ Register scratch,
+ Label* if_deprecated) {
+ if (map->CanBeDeprecated()) {
+ mov(scratch, map);
+ mov(scratch, FieldOperand(scratch, Map::kBitField3Offset));
+ and_(scratch, Immediate(Smi::FromInt(Map::Deprecated::kMask)));
+ j(not_zero, if_deprecated);
+ }
+}
+
+
+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 (emit_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, isolate()->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(kOneByteStringTag == 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(SeqOneByteString::kMaxSize, SeqTwoByteString::kMaxSize);
+ ASSERT(SeqOneByteString::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 (emit_debug_code()) {
+ mov(length, Operand(bitmap_scratch, MemoryChunk::kLiveBytesOffset));
+ cmp(length, Operand(bitmap_scratch, MemoryChunk::kSizeOffset));
+ Check(less_equal, kLiveBytesCountOverflowChunkSize);
+ }
+
+ bind(&done);
+}
+
+
+void MacroAssembler::EnumLength(Register dst, Register map) {
+ STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
+ mov(dst, FieldOperand(map, Map::kBitField3Offset));
+ and_(dst, Immediate(Smi::FromInt(Map::EnumLengthBits::kMask)));
+}
+
+
+void MacroAssembler::CheckEnumCache(Label* call_runtime) {
+ Label next, start;
+ mov(ecx, eax);
+
+ // Check if the enum length field is properly initialized, indicating that
+ // there is an enum cache.
+ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
+
+ EnumLength(edx, ebx);
+ cmp(edx, Immediate(Smi::FromInt(Map::kInvalidEnumCache)));
+ j(equal, call_runtime);
+
+ jmp(&start);
+
+ bind(&next);
+ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
+
+ // For all objects but the receiver, check that the cache is empty.
+ EnumLength(edx, ebx);
+ cmp(edx, Immediate(Smi::FromInt(0)));
+ j(not_equal, call_runtime);
+
+ bind(&start);
+
+ // Check that there are no elements. Register rcx contains the current JS
+ // object we've reached through the prototype chain.
+ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset));
+ cmp(ecx, isolate()->factory()->empty_fixed_array());
+ j(not_equal, call_runtime);
+
+ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
+ cmp(ecx, isolate()->factory()->null_value());
+ j(not_equal, &next);
+}
+
+
+void MacroAssembler::TestJSArrayForAllocationMemento(
+ Register receiver_reg,
+ Register scratch_reg) {
+ Label no_memento_available;
+
+ ExternalReference new_space_start =
+ ExternalReference::new_space_start(isolate());
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+
+ lea(scratch_reg, Operand(receiver_reg,
+ JSArray::kSize + AllocationMemento::kSize - kHeapObjectTag));
+ cmp(scratch_reg, Immediate(new_space_start));
+ j(less, &no_memento_available);
+ cmp(scratch_reg, Operand::StaticVariable(new_space_allocation_top));
+ j(greater, &no_memento_available);
+ cmp(MemOperand(scratch_reg, -AllocationMemento::kSize),
+ Immediate(Handle<Map>(isolate()->heap()->allocation_memento_map())));
+ bind(&no_memento_available);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/macro-assembler-ia32.h b/chromium/v8/src/ia32/macro-assembler-ia32.h
new file mode 100644
index 00000000000..d537b0b2cbd
--- /dev/null
+++ b/chromium/v8/src/ia32/macro-assembler-ia32.h
@@ -0,0 +1,1069 @@
+// Copyright 2012 the V8 project authors. 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_IA32_MACRO_ASSEMBLER_IA32_H_
+#define V8_IA32_MACRO_ASSEMBLER_IA32_H_
+
+#include "assembler.h"
+#include "frames.h"
+#include "v8globals.h"
+
+namespace v8 {
+namespace internal {
+
+// Convenience for platform-independent signatures. We do not normally
+// 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 };
+
+
+enum RegisterValueType {
+ REGISTER_VALUE_IS_SMI,
+ REGISTER_VALUE_IS_INT32
+};
+
+
+bool AreAliased(Register r1, Register r2, Register r3, Register r4);
+
+
+// MacroAssembler implements a collection of frequently used macros.
+class MacroAssembler: public Assembler {
+ public:
+ // The isolate parameter can be NULL if the macro assembler should
+ // not use isolate-dependent functionality. In this case, it's the
+ // responsibility of the caller to never invoke such function on the
+ // macro assembler.
+ MacroAssembler(Isolate* isolate, void* buffer, int size);
+
+ // Operations on roots in the root-array.
+ void LoadRoot(Register destination, Heap::RootListIndex index);
+ void StoreRoot(Register source, Register scratch, Heap::RootListIndex index);
+ void CompareRoot(Register with, Register scratch, Heap::RootListIndex index);
+ // These methods can only be used with constant roots (i.e. non-writable
+ // and not in new space).
+ void CompareRoot(Register with, Heap::RootListIndex index);
+ void CompareRoot(const Operand& with, Heap::RootListIndex index);
+
+ // ---------------------------------------------------------------------------
+ // 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);
+
+ void CheckPageFlagForMap(
+ Handle<Map> map,
+ int mask,
+ Condition cc,
+ Label* condition_met,
+ Label::Distance condition_met_distance = Label::kFar);
+
+ void CheckMapDeprecated(Handle<Map> map,
+ Register scratch,
+ Label* if_deprecated);
+
+ // 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);
+ }
+
+ // 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 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);
+ }
+
+ // 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 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. 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,
+ SaveFPRegsMode save_fp,
+ RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
+ SmiCheck smi_check = INLINE_SMI_CHECK);
+
+ // For page containing |object| mark the region covering the object's map
+ // dirty. |object| is the object being stored into, |map| is the Map object
+ // that was stored.
+ void RecordWriteForMap(
+ Register object,
+ Handle<Map> map,
+ Register scratch1,
+ Register scratch2,
+ SaveFPRegsMode save_fp);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // ---------------------------------------------------------------------------
+ // Debugger Support
+
+ void DebugBreak();
+#endif
+
+ // 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
+ // esi.
+ void EnterExitFrame(bool save_doubles);
+
+ void EnterApiExitFrame(int argc);
+
+ // Leave the current exit frame. Expects the return value in
+ // register eax:edx (untouched) and the pointer to the first
+ // argument in register esi.
+ void LeaveExitFrame(bool save_doubles);
+
+ // Leave the current exit frame. Expects the return value in
+ // register eax (untouched).
+ void LeaveApiExitFrame();
+
+ // 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 native context if the map in register
+ // map_in_out is the cached Array map in the native 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,
+ bool can_have_holes);
+
+ void LoadGlobalContext(Register global_context);
+
+ // Load the global function with the given index.
+ void LoadGlobalFunction(int index, Register function);
+
+ // Load the initial map from the global function. The registers
+ // function and map can be the same.
+ void LoadGlobalFunctionInitialMap(Register function, Register map);
+
+ // Push and pop the registers that can hold pointers.
+ void PushSafepointRegisters() { pushad(); }
+ void PopSafepointRegisters() { popad(); }
+ // Store the value in register/immediate src in the safepoint
+ // register stack slot for register dst.
+ void StoreToSafepointRegisterSlot(Register dst, Register src);
+ void StoreToSafepointRegisterSlot(Register dst, Immediate src);
+ void LoadFromSafepointRegisterSlot(Register dst, Register src);
+
+ void LoadHeapObject(Register result, Handle<HeapObject> object);
+ void CmpHeapObject(Register reg, Handle<HeapObject> object);
+ void PushHeapObject(Handle<HeapObject> object);
+
+ void LoadObject(Register result, Handle<Object> object) {
+ AllowDeferredHandleDereference heap_object_check;
+ if (object->IsHeapObject()) {
+ LoadHeapObject(result, Handle<HeapObject>::cast(object));
+ } else {
+ Set(result, Immediate(object));
+ }
+ }
+
+ void CmpObject(Register reg, Handle<Object> object) {
+ AllowDeferredHandleDereference heap_object_check;
+ if (object->IsHeapObject()) {
+ CmpHeapObject(reg, Handle<HeapObject>::cast(object));
+ } else {
+ cmp(reg, Immediate(object));
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ // JavaScript invokes
+
+ // 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,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ void InvokeCode(Handle<Code> code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ RelocInfo::Mode rmode,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ // Invoke the JavaScript function in the given register. Changes the
+ // current context to the context in the function before invoking.
+ void InvokeFunction(Register function,
+ const ParameterCount& actual,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ void InvokeFunction(Handle<JSFunction> function,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ // Invoke specified builtin JavaScript function. Adds an entry to
+ // the unresolved list if the name does not resolve.
+ void InvokeBuiltin(Builtins::JavaScript id,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper = NullCallWrapper());
+
+ // Store the function for the given builtin in the target register.
+ void GetBuiltinFunction(Register target, Builtins::JavaScript id);
+
+ // Store the code object for the given builtin in the target register.
+ void GetBuiltinEntry(Register target, Builtins::JavaScript id);
+
+ // Expression support
+ void Set(Register dst, const Immediate& x);
+ void Set(const Operand& dst, const Immediate& x);
+
+ // Support for constant splitting.
+ bool IsUnsafeImmediate(const Immediate& x);
+ void SafeSet(Register dst, const Immediate& x);
+ void SafePush(const Immediate& x);
+
+ // Compare object type for heap object.
+ // Incoming register is heap_object and outgoing register is map.
+ void CmpObjectType(Register heap_object, InstanceType type, Register map);
+
+ // Compare instance type for map.
+ void CmpInstanceType(Register map, InstanceType type);
+
+ // Check if a map for a JSObject indicates that the object has fast elements.
+ // Jump to the specified label if it does not.
+ void CheckFastElements(Register map,
+ 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 CheckFastSmiElements(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,
+ int offset = 0);
+
+ // 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);
+
+ // 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);
+
+ // 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
+ // known to be a heap object)
+ void DispatchMap(Register obj,
+ Register unused,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type);
+
+ // Check if the object in register heap_object is a string. Afterwards the
+ // register map contains the object map and the register instance_type
+ // contains the instance_type. The registers map and instance_type can be the
+ // same in which case it contains the instance type afterwards. Either of the
+ // registers map and instance_type can be the same as heap_object.
+ Condition IsObjectStringType(Register heap_object,
+ Register map,
+ Register instance_type);
+
+ // Check if the object in register heap_object is a name. Afterwards the
+ // register map contains the object map and the register instance_type
+ // contains the instance_type. The registers map and instance_type can be the
+ // same in which case it contains the instance type afterwards. Either of the
+ // registers map and instance_type can be the same as heap_object.
+ Condition IsObjectNameType(Register heap_object,
+ Register map,
+ Register instance_type);
+
+ // Check if a heap object's type is in the JSObject range, not including
+ // JSFunction. The object's map will be loaded in the map register.
+ // Any or all of the three registers may be the same.
+ // The contents of the scratch register will always be overwritten.
+ void IsObjectJSObjectType(Register heap_object,
+ Register map,
+ Register scratch,
+ Label* fail);
+
+ // The contents of the scratch register will be overwritten.
+ void IsInstanceJSObjectType(Register map, Register scratch, Label* fail);
+
+ // FCmp is similar to integer cmp, but requires unsigned
+ // jcc instructions (je, ja, jae, jb, jbe, je, and jz).
+ void FCmp();
+
+ void ClampUint8(Register reg);
+
+ void ClampDoubleToUint8(XMMRegister input_reg,
+ XMMRegister scratch_reg,
+ Register result_reg);
+
+
+ // Smi tagging support.
+ void SmiTag(Register reg) {
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ add(reg, reg);
+ }
+ void SmiUntag(Register reg) {
+ sar(reg, kSmiTagSize);
+ }
+
+ // Modifies the register even if it does not contain a Smi!
+ void SmiUntag(Register reg, Label* is_smi) {
+ STATIC_ASSERT(kSmiTagSize == 1);
+ sar(reg, kSmiTagSize);
+ STATIC_ASSERT(kSmiTag == 0);
+ j(not_carry, is_smi);
+ }
+
+ void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch);
+
+ // Jump the register contains a smi.
+ inline void JumpIfSmi(Register value,
+ Label* smi_label,
+ Label::Distance distance = Label::kFar) {
+ test(value, Immediate(kSmiTagMask));
+ j(zero, smi_label, distance);
+ }
+ // Jump if the operand is a smi.
+ inline void JumpIfSmi(Operand value,
+ Label* smi_label,
+ Label::Distance distance = Label::kFar) {
+ test(value, Immediate(kSmiTagMask));
+ j(zero, smi_label, distance);
+ }
+ // Jump if register contain a non-smi.
+ inline void JumpIfNotSmi(Register value,
+ Label* not_smi_label,
+ Label::Distance distance = Label::kFar) {
+ test(value, Immediate(kSmiTagMask));
+ j(not_zero, not_smi_label, distance);
+ }
+
+ void LoadInstanceDescriptors(Register map, Register descriptors);
+ void EnumLength(Register dst, Register map);
+ void NumberOfOwnDescriptors(Register dst, Register map);
+
+ template<typename Field>
+ void DecodeField(Register reg) {
+ static const int shift = Field::kShift;
+ static const int mask = (Field::kMask >> Field::kShift) << kSmiTagSize;
+ sar(reg, shift);
+ and_(reg, Immediate(mask));
+ }
+ void LoadPowerOf2(XMMRegister dst, Register scratch, int power);
+
+ // Abort execution if argument is not a number, enabled via --debug-code.
+ void AssertNumber(Register object);
+
+ // Abort execution if argument is not a smi, enabled via --debug-code.
+ void AssertSmi(Register object);
+
+ // Abort execution if argument is a smi, enabled via --debug-code.
+ void AssertNotSmi(Register object);
+
+ // Abort execution if argument is not a string, enabled via --debug-code.
+ void AssertString(Register object);
+
+ // Abort execution if argument is not a name, enabled via --debug-code.
+ void AssertName(Register object);
+
+ // ---------------------------------------------------------------------------
+ // Exception handling
+
+ // 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();
+
+ // Throw to the top handler in the try hander chain.
+ void Throw(Register value);
+
+ // Throw past all JS frames to the top JS entry frame.
+ void ThrowUncatchable(Register value);
+
+ // ---------------------------------------------------------------------------
+ // Inline caching support
+
+ // Generate code for checking access rights - used for security checks
+ // on access to global objects across environments. The holder register
+ // is left untouched, but the scratch register is clobbered.
+ void CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ Label* miss);
+
+ void GetNumberHash(Register r0, Register scratch);
+
+ void LoadFromNumberDictionary(Label* miss,
+ Register elements,
+ Register key,
+ Register r0,
+ Register r1,
+ Register r2,
+ Register result);
+
+
+ // ---------------------------------------------------------------------------
+ // Allocation support
+
+ // Allocate an object in new space or old pointer space. If the given space
+ // is exhausted control continues at the gc_required label. The allocated
+ // object is returned in result and end of the new object is returned in
+ // result_end. The register scratch can be passed as no_reg in which case
+ // an additional object reference will be added to the reloc info. The
+ // returned pointers in result and result_end have not yet been tagged as
+ // heap objects. If result_contains_top_on_entry is true the content of
+ // result is known to be the allocation top on entry (could be result_end
+ // from a previous call). If result_contains_top_on_entry is true scratch
+ // should be no_reg as it is never used.
+ void Allocate(int object_size,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ void Allocate(int header_size,
+ ScaleFactor element_size,
+ Register element_count,
+ RegisterValueType element_count_type,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ void Allocate(Register object_size,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ // Undo allocation in new space. The object passed and objects allocated after
+ // it will no longer be allocated. Make sure that no pointers are left to the
+ // object(s) no longer allocated as they would be invalid when allocation is
+ // un-done.
+ void UndoAllocationInNewSpace(Register object);
+
+ // Allocate a heap number in new space with undefined value. The
+ // register scratch2 can be passed as no_reg; the others must be
+ // valid registers. Returns tagged pointer in result register, or
+ // jumps to gc_required if new space is full.
+ void AllocateHeapNumber(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // Allocate a sequential string. All the header fields of the string object
+ // are initialized.
+ void AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateAsciiString(Register result,
+ int length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // Allocate a raw cons string object. Only the map field of the result is
+ // initialized.
+ void AllocateTwoByteConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // Allocate a raw sliced string object. Only the map field of the result is
+ // initialized.
+ void AllocateTwoByteSlicedString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiSlicedString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // Copy memory, byte-by-byte, from source to destination. Not optimized for
+ // long or aligned copies.
+ // The contents of index and scratch are destroyed.
+ void CopyBytes(Register source,
+ Register destination,
+ 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);
+
+ // Check if result is zero and any of op1 and op2 are negative.
+ // Register scratch is destroyed, and it must be different from op2.
+ void NegativeZeroTest(Register result, Register op1, Register op2,
+ Register scratch, Label* then_label);
+
+ // Try to get function prototype of a function and puts the value in
+ // the result register. Checks that the function really is a
+ // function and jumps to the miss label if the fast checks fail. The
+ // function register will be untouched; the other registers may be
+ // clobbered.
+ void TryGetFunctionPrototype(Register function,
+ Register result,
+ Register scratch,
+ Label* miss,
+ bool miss_on_bound_function = false);
+
+ // Generates code for reporting that an illegal operation has
+ // occurred.
+ void IllegalOperation(int num_arguments);
+
+ // Picks out an array index from the hash field.
+ // Register use:
+ // hash - holds the index's hash. Clobbered.
+ // index - holds the overwritten index on exit.
+ void IndexFromHash(Register hash, Register index);
+
+ // ---------------------------------------------------------------------------
+ // Runtime calls
+
+ // Call a code stub. Generate the code if necessary.
+ void CallStub(CodeStub* stub, TypeFeedbackId ast_id = TypeFeedbackId::None());
+
+ // Tail call a code stub (jump). Generate the code if necessary.
+ void TailCallStub(CodeStub* stub);
+
+ // Return from a code stub after popping its arguments.
+ void StubReturn(int argc);
+
+ // Call a runtime routine.
+ void CallRuntime(const Runtime::Function* f, int num_arguments);
+ void CallRuntimeSaveDoubles(Runtime::FunctionId id);
+
+ // Convenience function: Same as above, but takes the fid instead.
+ void CallRuntime(Runtime::FunctionId id, int num_arguments);
+
+ // Convenience function: call an external reference.
+ void CallExternalReference(ExternalReference ref, int num_arguments);
+
+ // Tail call of a runtime routine (jump).
+ // Like JumpToExternalReference, but also takes care of passing the number
+ // of parameters.
+ void TailCallExternalReference(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);
+
+ // 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.
+ // Some compilers/platforms require the stack to be aligned when calling
+ // C++ code.
+ // Needs a scratch register to do some arithmetic. This register will be
+ // trashed.
+ void PrepareCallCFunction(int num_arguments, Register scratch);
+
+ // Calls a C function and cleans up the space for arguments allocated
+ // by PrepareCallCFunction. The called function is not allowed to trigger a
+ // garbage collection, since that might move the code and invalidate the
+ // return address (unless this is somehow accounted for by the called
+ // function).
+ void CallCFunction(ExternalReference function, int num_arguments);
+ void CallCFunction(Register function, int num_arguments);
+
+ // Prepares stack to put arguments (aligns and so on). Reserves
+ // space for return value if needed (assumes the return value is a handle).
+ // Arguments must be stored in ApiParameterOperand(0), ApiParameterOperand(1)
+ // etc. Saves context (esi). If space was reserved for return value then
+ // stores the pointer to the reserved slot into esi.
+ void PrepareCallApiFunction(int argc, bool returns_handle);
+
+ // 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,
+ Address thunk_address,
+ Operand thunk_last_arg,
+ int stack_space,
+ bool returns_handle,
+ int return_value_offset_from_ebp);
+
+ // Jump to a runtime routine.
+ void JumpToExternalReference(const ExternalReference& ext);
+
+ // ---------------------------------------------------------------------------
+ // Utilities
+
+ void Ret();
+
+ // Return and drop arguments from stack, where the number of arguments
+ // may be bigger than 2^16 - 1. Requires a scratch register.
+ void Ret(int bytes_dropped, Register scratch);
+
+ // Emit code to discard a non-negative number of pointer-sized elements
+ // from the stack, clobbering only the esp register.
+ void Drop(int element_count);
+
+ void Call(Label* target) { call(target); }
+ void Push(Register src) { push(src); }
+ void Pop(Register dst) { pop(dst); }
+
+ // Emit call to the code we are currently generating.
+ void CallSelf() {
+ Handle<Code> self(reinterpret_cast<Code**>(CodeObject().location()));
+ call(self, RelocInfo::CODE_TARGET);
+ }
+
+ // Move if the registers are not identical.
+ void Move(Register target, Register source);
+
+ // Push a handle value.
+ void Push(Handle<Object> handle) { push(Immediate(handle)); }
+ void Push(Smi* smi) { Push(Handle<Smi>(smi, isolate())); }
+
+ Handle<Object> CodeObject() {
+ ASSERT(!code_object_.is_null());
+ return code_object_;
+ }
+
+ // Insert code to verify that the x87 stack has the specified depth (0-7)
+ void VerifyX87StackDepth(uint32_t depth);
+
+ // ---------------------------------------------------------------------------
+ // StatsCounter support
+
+ void SetCounter(StatsCounter* counter, int value);
+ void IncrementCounter(StatsCounter* counter, int value);
+ void DecrementCounter(StatsCounter* counter, int value);
+ void IncrementCounter(Condition cc, StatsCounter* counter, int value);
+ void DecrementCounter(Condition cc, StatsCounter* counter, int value);
+
+
+ // ---------------------------------------------------------------------------
+ // Debugging
+
+ // Calls Abort(msg) if the condition cc is not satisfied.
+ // Use --debug_code to enable.
+ void Assert(Condition cc, BailoutReason reason);
+
+ void AssertFastElements(Register elements);
+
+ // Like Assert(), but always enabled.
+ void Check(Condition cc, BailoutReason reason);
+
+ // Print a message to stdout and abort execution.
+ void Abort(BailoutReason reason);
+
+ // Check that the stack is aligned.
+ void CheckStackAlignment();
+
+ // Verify restrictions about code generated in stubs.
+ void set_generating_stub(bool value) { generating_stub_ = value; }
+ 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
+ // label if not. If the instance type can be scratched specify same register
+ // for both instance type and scratch.
+ void JumpIfInstanceTypeIsNotSequentialAscii(Register instance_type,
+ Register scratch,
+ Label* on_not_flat_ascii_string);
+
+ // Checks if both objects are sequential ASCII strings, and jumps to label
+ // if either is not.
+ void JumpIfNotBothSequentialAsciiStrings(Register object1,
+ Register object2,
+ Register scratch1,
+ Register scratch2,
+ Label* on_not_flat_ascii_strings);
+
+ // Checks if the given register or operand is a unique name
+ void JumpIfNotUniqueName(Register reg, Label* not_unique_name,
+ Label::Distance distance = Label::kFar) {
+ JumpIfNotUniqueName(Operand(reg), not_unique_name, distance);
+ }
+
+ void JumpIfNotUniqueName(Operand operand, Label* not_unique_name,
+ Label::Distance distance = Label::kFar);
+
+ static int SafepointRegisterStackIndex(Register reg) {
+ return SafepointRegisterStackIndex(reg.code());
+ }
+
+ // Activation support.
+ void EnterFrame(StackFrame::Type type);
+ void LeaveFrame(StackFrame::Type type);
+
+ // Expects object in eax and returns map with validated enum cache
+ // in eax. Assumes that any other register can be used as a scratch.
+ void CheckEnumCache(Label* call_runtime);
+
+ // AllocationMemento support. Arrays may have an associated
+ // AllocationMemento object that can be checked for in order to pretransition
+ // to another type.
+ // On entry, receiver_reg should point to the array object.
+ // scratch_reg gets clobbered.
+ // If allocation info is present, conditional code is set to equal
+ void TestJSArrayForAllocationMemento(Register receiver_reg,
+ Register scratch_reg);
+
+ 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_;
+
+ // Helper functions for generating invokes.
+ void InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ Handle<Code> code_constant,
+ const Operand& code_operand,
+ Label* done,
+ bool* definitely_mismatches,
+ InvokeFlag flag,
+ Label::Distance done_distance,
+ const CallWrapper& call_wrapper = NullCallWrapper(),
+ CallKind call_kind = CALL_AS_METHOD);
+
+ void EnterExitFramePrologue();
+ void EnterExitFrameEpilogue(int argc, bool save_doubles);
+
+ void LeaveExitFrameEpilogue();
+
+ // Allocation support helpers.
+ void LoadAllocationTopHelper(Register result,
+ Register scratch,
+ AllocationFlags flags);
+
+ void UpdateAllocationTopHelper(Register result_end,
+ Register scratch,
+ AllocationFlags flags);
+
+ // Helper for PopHandleScope. Allowed to perform a GC and returns
+ // NULL if gc_allowed. Does not perform a GC if !gc_allowed, and
+ // possibly returns a failure object indicating an allocation failure.
+ MUST_USE_RESULT MaybeObject* PopHandleScopeHelper(Register saved,
+ 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);
+ static int SafepointRegisterStackIndex(int reg_code);
+
+ // Needs access to SafepointRegisterStackIndex for compiled frame
+ // traversal.
+ friend class StandardFrame;
+};
+
+
+// The code patcher is used to patch (typically) small parts of code e.g. for
+// debugging and other types of instrumentation. When using the code patcher
+// the exact number of bytes specified must be emitted. Is not legal to emit
+// relocation information. If any of these constraints are violated it causes
+// an assertion.
+class CodePatcher {
+ public:
+ CodePatcher(byte* address, int size);
+ virtual ~CodePatcher();
+
+ // Macro assembler to emit code.
+ MacroAssembler* masm() { return &masm_; }
+
+ private:
+ byte* address_; // The address of the code being patched.
+ int size_; // Number of bytes of the expected patch size.
+ MacroAssembler masm_; // Macro assembler used to generate the code.
+};
+
+
+// -----------------------------------------------------------------------------
+// Static helper functions.
+
+// Generate an Operand for loading a field from an object.
+inline Operand FieldOperand(Register object, int offset) {
+ return Operand(object, offset - kHeapObjectTag);
+}
+
+
+// Generate an Operand for loading an indexed field from an object.
+inline Operand FieldOperand(Register object,
+ Register index,
+ ScaleFactor scale,
+ int offset) {
+ return Operand(object, index, scale, offset - kHeapObjectTag);
+}
+
+
+inline Operand ContextOperand(Register context, int index) {
+ return Operand(context, Context::SlotOffset(index));
+}
+
+
+inline Operand GlobalObjectOperand() {
+ return ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX);
+}
+
+
+// Generates an Operand for saving parameters after PrepareCallApiFunction.
+Operand ApiParameterOperand(int index, bool returns_handle);
+
+
+#ifdef GENERATED_CODE_COVERAGE
+extern void LogGeneratedCodeCoverage(const char* file_line);
+#define CODE_COVERAGE_STRINGIFY(x) #x
+#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
+#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
+#define ACCESS_MASM(masm) { \
+ byte* ia32_coverage_function = \
+ reinterpret_cast<byte*>(FUNCTION_ADDR(LogGeneratedCodeCoverage)); \
+ masm->pushfd(); \
+ masm->pushad(); \
+ masm->push(Immediate(reinterpret_cast<int>(&__FILE_LINE__))); \
+ masm->call(ia32_coverage_function, RelocInfo::RUNTIME_ENTRY); \
+ masm->pop(eax); \
+ masm->popad(); \
+ masm->popfd(); \
+ } \
+ masm->
+#else
+#define ACCESS_MASM(masm) masm->
+#endif
+
+
+} } // namespace v8::internal
+
+#endif // V8_IA32_MACRO_ASSEMBLER_IA32_H_
diff --git a/chromium/v8/src/ia32/regexp-macro-assembler-ia32.cc b/chromium/v8/src/ia32/regexp-macro-assembler-ia32.cc
new file mode 100644
index 00000000000..dfcc8695675
--- /dev/null
+++ b/chromium/v8/src/ia32/regexp-macro-assembler-ia32.cc
@@ -0,0 +1,1332 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "cpu-profiler.h"
+#include "unicode.h"
+#include "log.h"
+#include "regexp-stack.h"
+#include "macro-assembler.h"
+#include "regexp-macro-assembler.h"
+#include "ia32/regexp-macro-assembler-ia32.h"
+
+namespace v8 {
+namespace internal {
+
+#ifndef V8_INTERPRETED_REGEXP
+/*
+ * This assembler uses the following register assignment convention
+ * - edx : Current character. Must be loaded using LoadCurrentCharacter
+ * before using any of the dispatch methods. Temporarily stores the
+ * index of capture start after a matching pass for a global regexp.
+ * - edi : Current position in input, as negative offset from end of string.
+ * Please notice that this is the byte offset, not the character offset!
+ * - esi : end of input (points to byte after last character in input).
+ * - ebp : Frame pointer. Used to access arguments, local variables and
+ * RegExp registers.
+ * - esp : Points to tip of C stack.
+ * - ecx : Points to tip of backtrack stack
+ *
+ * The registers eax and ebx are free to use for computations.
+ *
+ * Each call to a public method should retain this convention.
+ * The stack will have the following structure:
+ * - Isolate* isolate (address of the current isolate)
+ * - direct_call (if 1, direct call from JavaScript code, if 0
+ * call through the runtime system)
+ * - stack_area_base (high end of the memory area to use as
+ * backtracking stack)
+ * - capture array size (may fit multiple sets of matches)
+ * - int* capture_array (int[num_saved_registers_], for output).
+ * - end of input (address of end of string)
+ * - start of input (address of first character in string)
+ * - start index (character index of start)
+ * - String* input_string (location of a handle containing the string)
+ * --- frame alignment (if applicable) ---
+ * - return address
+ * ebp-> - old ebp
+ * - backup of caller esi
+ * - backup of caller edi
+ * - backup of caller ebx
+ * - success counter (only for global regexps to count matches).
+ * - Offset of location before start of input (effectively character
+ * position -1). Used to initialize capture registers to a non-position.
+ * - register 0 ebp[-4] (only positions must be stored in the first
+ * - register 1 ebp[-8] num_saved_registers_ registers)
+ * - ...
+ *
+ * The first num_saved_registers_ registers are initialized to point to
+ * "character -1" in the string (i.e., char_size() bytes before the first
+ * character of the string). The remaining registers starts out as garbage.
+ *
+ * The data up to the return address must be placed there by the calling
+ * code, by calling the code entry as cast to a function with the signature:
+ * int (*match)(String* input_string,
+ * int start_index,
+ * Address start,
+ * Address end,
+ * int* capture_output_array,
+ * bool at_start,
+ * byte* stack_area_base,
+ * bool direct_call)
+ */
+
+#define __ ACCESS_MASM(masm_)
+
+RegExpMacroAssemblerIA32::RegExpMacroAssemblerIA32(
+ Mode mode,
+ int registers_to_save,
+ Zone* zone)
+ : NativeRegExpMacroAssembler(zone),
+ masm_(new MacroAssembler(zone->isolate(), NULL, kRegExpCodeSize)),
+ mode_(mode),
+ num_registers_(registers_to_save),
+ num_saved_registers_(registers_to_save),
+ entry_label_(),
+ start_label_(),
+ success_label_(),
+ backtrack_label_(),
+ exit_label_() {
+ ASSERT_EQ(0, registers_to_save % 2);
+ __ jmp(&entry_label_); // We'll write the entry code later.
+ __ bind(&start_label_); // And then continue from here.
+}
+
+
+RegExpMacroAssemblerIA32::~RegExpMacroAssemblerIA32() {
+ delete masm_;
+ // Unuse labels in case we throw away the assembler without calling GetCode.
+ entry_label_.Unuse();
+ start_label_.Unuse();
+ success_label_.Unuse();
+ backtrack_label_.Unuse();
+ exit_label_.Unuse();
+ check_preempt_label_.Unuse();
+ stack_overflow_label_.Unuse();
+}
+
+
+int RegExpMacroAssemblerIA32::stack_limit_slack() {
+ return RegExpStack::kStackLimitSlack;
+}
+
+
+void RegExpMacroAssemblerIA32::AdvanceCurrentPosition(int by) {
+ if (by != 0) {
+ __ add(edi, Immediate(by * char_size()));
+ }
+}
+
+
+void RegExpMacroAssemblerIA32::AdvanceRegister(int reg, int by) {
+ ASSERT(reg >= 0);
+ ASSERT(reg < num_registers_);
+ if (by != 0) {
+ __ add(register_location(reg), Immediate(by));
+ }
+}
+
+
+void RegExpMacroAssemblerIA32::Backtrack() {
+ CheckPreemption();
+ // Pop Code* offset from backtrack stack, add Code* and jump to location.
+ Pop(ebx);
+ __ add(ebx, Immediate(masm_->CodeObject()));
+ __ jmp(ebx);
+}
+
+
+void RegExpMacroAssemblerIA32::Bind(Label* label) {
+ __ bind(label);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckCharacter(uint32_t c, Label* on_equal) {
+ __ cmp(current_character(), c);
+ BranchOrBacktrack(equal, on_equal);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckCharacterGT(uc16 limit, Label* on_greater) {
+ __ cmp(current_character(), limit);
+ BranchOrBacktrack(greater, on_greater);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckAtStart(Label* on_at_start) {
+ Label not_at_start;
+ // Did we start the match at the start of the string at all?
+ __ cmp(Operand(ebp, kStartIndex), Immediate(0));
+ BranchOrBacktrack(not_equal, &not_at_start);
+ // If we did, are we still at the start of the input?
+ __ lea(eax, Operand(esi, edi, times_1, 0));
+ __ cmp(eax, Operand(ebp, kInputStart));
+ BranchOrBacktrack(equal, on_at_start);
+ __ bind(&not_at_start);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckNotAtStart(Label* on_not_at_start) {
+ // Did we start the match at the start of the string at all?
+ __ cmp(Operand(ebp, kStartIndex), Immediate(0));
+ BranchOrBacktrack(not_equal, on_not_at_start);
+ // If we did, are we still at the start of the input?
+ __ lea(eax, Operand(esi, edi, times_1, 0));
+ __ cmp(eax, Operand(ebp, kInputStart));
+ BranchOrBacktrack(not_equal, on_not_at_start);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckCharacterLT(uc16 limit, Label* on_less) {
+ __ cmp(current_character(), limit);
+ BranchOrBacktrack(less, on_less);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckGreedyLoop(Label* on_equal) {
+ Label fallthrough;
+ __ cmp(edi, Operand(backtrack_stackpointer(), 0));
+ __ j(not_equal, &fallthrough);
+ __ add(backtrack_stackpointer(), Immediate(kPointerSize)); // Pop.
+ BranchOrBacktrack(no_condition, on_equal);
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
+ int start_reg,
+ Label* on_no_match) {
+ 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, 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
+ // the start of the capture.
+ BranchOrBacktrack(less, on_no_match);
+
+ // If length is zero, either the capture is empty or it is completely
+ // uncaptured. In either case succeed immediately.
+ __ j(equal, &fallthrough);
+
+ // Check that there are sufficient characters left in the input.
+ __ mov(eax, edi);
+ __ add(eax, ebx);
+ BranchOrBacktrack(greater, on_no_match);
+
+ if (mode_ == ASCII) {
+ Label success;
+ Label fail;
+ Label loop_increment;
+ // Save register contents to make the registers available below.
+ __ push(edi);
+ __ push(backtrack_stackpointer());
+ // After this, the eax, ecx, and edi registers are available.
+
+ __ 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);
+ __ movzx_b(eax, Operand(edi, 0));
+ __ cmpb_al(Operand(edx, 0));
+ __ j(equal, &loop_increment);
+
+ // Mismatch, try case-insensitive match (converting letters to lower-case).
+ __ or_(eax, 0x20); // Convert match character to lower-case.
+ __ lea(ecx, Operand(eax, -'a'));
+ __ cmp(ecx, static_cast<int32_t>('z' - 'a')); // Is eax a lowercase letter?
+ Label convert_capture;
+ __ j(below_equal, &convert_capture); // In range 'a'-'z'.
+ // Latin-1: Check for values in range [224,254] but not 247.
+ __ sub(ecx, Immediate(224 - 'a'));
+ __ cmp(ecx, Immediate(254 - 224));
+ __ j(above, &fail); // Weren't Latin-1 letters.
+ __ cmp(ecx, Immediate(247 - 224)); // Check for 247.
+ __ j(equal, &fail);
+ __ bind(&convert_capture);
+ // Also convert capture character.
+ __ movzx_b(ecx, Operand(edx, 0));
+ __ or_(ecx, 0x20);
+
+ __ cmp(eax, ecx);
+ __ j(not_equal, &fail);
+
+ __ bind(&loop_increment);
+ // Increment pointers into match and capture strings.
+ __ add(edx, Immediate(1));
+ __ add(edi, Immediate(1));
+ // Compare to end of match, and loop if not done.
+ __ cmp(edi, ebx);
+ __ j(below, &loop);
+ __ jmp(&success);
+
+ __ bind(&fail);
+ // Restore original values before failing.
+ __ pop(backtrack_stackpointer());
+ __ pop(edi);
+ BranchOrBacktrack(no_condition, on_no_match);
+
+ __ bind(&success);
+ // Restore original value before continuing.
+ __ pop(backtrack_stackpointer());
+ // Drop original value of character position.
+ __ add(esp, Immediate(kPointerSize));
+ // Compute new value of character position after the matched part.
+ __ sub(edi, esi);
+ } else {
+ ASSERT(mode_ == UC16);
+ // Save registers before calling C function.
+ __ push(esi);
+ __ push(edi);
+ __ push(backtrack_stackpointer());
+ __ push(ebx);
+
+ static const int argument_count = 4;
+ __ PrepareCallCFunction(argument_count, ecx);
+ // Put arguments into allocated stack area, last argument highest on stack.
+ // Parameters are
+ // Address byte_offset1 - Address captured substring's start.
+ // Address byte_offset2 - Address of current character position.
+ // size_t byte_length - length of capture in bytes(!)
+ // Isolate* isolate
+
+ // Set isolate.
+ __ mov(Operand(esp, 3 * kPointerSize),
+ Immediate(ExternalReference::isolate_address(isolate())));
+ // Set byte_length.
+ __ mov(Operand(esp, 2 * kPointerSize), ebx);
+ // Set byte_offset2.
+ // Found by adding negative string-end offset of current position (edi)
+ // to end of string.
+ __ 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, esi);
+ __ mov(Operand(esp, 0 * kPointerSize), edx);
+
+ {
+ AllowExternalCallThatCantCauseGC scope(masm_);
+ ExternalReference compare =
+ ExternalReference::re_case_insensitive_compare_uc16(isolate());
+ __ CallCFunction(compare, argument_count);
+ }
+ // Pop original values before reacting on result value.
+ __ pop(ebx);
+ __ pop(backtrack_stackpointer());
+ __ pop(edi);
+ __ pop(esi);
+
+ // Check if function returned non-zero for success or zero for failure.
+ __ or_(eax, eax);
+ BranchOrBacktrack(zero, on_no_match);
+ // On success, increment position by length of capture.
+ __ add(edi, ebx);
+ }
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckNotBackReference(
+ int start_reg,
+ Label* on_no_match) {
+ Label fallthrough;
+ Label success;
+ Label fail;
+
+ // Find length of back-referenced capture.
+ __ mov(edx, register_location(start_reg));
+ __ mov(eax, register_location(start_reg + 1));
+ __ 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)
+ __ j(equal, &fallthrough);
+
+ // Check that there are sufficient characters left in the input.
+ __ mov(ebx, edi);
+ __ add(ebx, eax);
+ BranchOrBacktrack(greater, on_no_match);
+
+ // Save register to make it available below.
+ __ push(backtrack_stackpointer());
+
+ // Compute pointers to match string and capture string
+ __ lea(ebx, Operand(esi, edi, times_1, 0)); // Start of match.
+ __ add(edx, esi); // Start of capture.
+ __ lea(ecx, Operand(eax, ebx, times_1, 0)); // End of match
+
+ Label loop;
+ __ bind(&loop);
+ if (mode_ == ASCII) {
+ __ movzx_b(eax, Operand(edx, 0));
+ __ cmpb_al(Operand(ebx, 0));
+ } else {
+ ASSERT(mode_ == UC16);
+ __ movzx_w(eax, Operand(edx, 0));
+ __ cmpw_ax(Operand(ebx, 0));
+ }
+ __ j(not_equal, &fail);
+ // Increment pointers into capture and match string.
+ __ add(edx, Immediate(char_size()));
+ __ add(ebx, Immediate(char_size()));
+ // Check if we have reached end of match area.
+ __ cmp(ebx, ecx);
+ __ j(below, &loop);
+ __ jmp(&success);
+
+ __ bind(&fail);
+ // Restore backtrack stackpointer.
+ __ pop(backtrack_stackpointer());
+ BranchOrBacktrack(no_condition, on_no_match);
+
+ __ bind(&success);
+ // Move current character position to position after match.
+ __ mov(edi, ecx);
+ __ sub(edi, esi);
+ // Restore backtrack stackpointer.
+ __ pop(backtrack_stackpointer());
+
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckNotCharacter(uint32_t c,
+ Label* on_not_equal) {
+ __ cmp(current_character(), c);
+ BranchOrBacktrack(not_equal, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_equal) {
+ if (c == 0) {
+ __ test(current_character(), Immediate(mask));
+ } else {
+ __ mov(eax, mask);
+ __ and_(eax, current_character());
+ __ cmp(eax, c);
+ }
+ BranchOrBacktrack(equal, on_equal);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckNotCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_not_equal) {
+ if (c == 0) {
+ __ test(current_character(), Immediate(mask));
+ } else {
+ __ mov(eax, mask);
+ __ and_(eax, current_character());
+ __ cmp(eax, c);
+ }
+ BranchOrBacktrack(not_equal, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckNotCharacterAfterMinusAnd(
+ uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal) {
+ ASSERT(minus < String::kMaxUtf16CodeUnit);
+ __ lea(eax, Operand(current_character(), -minus));
+ if (c == 0) {
+ __ test(eax, Immediate(mask));
+ } else {
+ __ and_(eax, mask);
+ __ cmp(eax, c);
+ }
+ BranchOrBacktrack(not_equal, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckCharacterInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_in_range) {
+ __ lea(eax, Operand(current_character(), -from));
+ __ cmp(eax, to - from);
+ BranchOrBacktrack(below_equal, on_in_range);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckCharacterNotInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_not_in_range) {
+ __ lea(eax, Operand(current_character(), -from));
+ __ cmp(eax, to - from);
+ BranchOrBacktrack(above, on_not_in_range);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckBitInTable(
+ Handle<ByteArray> table,
+ Label* on_bit_set) {
+ __ mov(eax, Immediate(table));
+ Register index = current_character();
+ if (mode_ != ASCII || kTableMask != String::kMaxOneByteCharCode) {
+ __ mov(ebx, kTableSize - 1);
+ __ and_(ebx, current_character());
+ index = ebx;
+ }
+ __ cmpb(FieldOperand(eax, index, times_1, ByteArray::kHeaderSize), 0);
+ BranchOrBacktrack(not_equal, on_bit_set);
+}
+
+
+bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match) {
+ // Range checks (c in min..max) are generally implemented by an unsigned
+ // (c - min) <= (max - min) check
+ switch (type) {
+ case 's':
+ // Match space-characters
+ if (mode_ == ASCII) {
+ // One byte space characters are '\t'..'\r', ' ' and \u00a0.
+ Label success;
+ __ cmp(current_character(), ' ');
+ __ j(equal, &success, Label::kNear);
+ // Check range 0x09..0x0d
+ __ lea(eax, Operand(current_character(), -'\t'));
+ __ cmp(eax, '\r' - '\t');
+ __ j(below_equal, &success, Label::kNear);
+ // \u00a0 (NBSP).
+ __ cmp(eax, 0x00a0 - '\t');
+ BranchOrBacktrack(not_equal, on_no_match);
+ __ bind(&success);
+ return true;
+ }
+ return false;
+ case 'S':
+ // The emitted code for generic character classes is good enough.
+ return false;
+ case 'd':
+ // Match ASCII digits ('0'..'9')
+ __ lea(eax, Operand(current_character(), -'0'));
+ __ cmp(eax, '9' - '0');
+ BranchOrBacktrack(above, on_no_match);
+ return true;
+ case 'D':
+ // Match non ASCII-digits
+ __ lea(eax, Operand(current_character(), -'0'));
+ __ cmp(eax, '9' - '0');
+ BranchOrBacktrack(below_equal, on_no_match);
+ return true;
+ case '.': {
+ // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
+ __ mov(eax, current_character());
+ __ xor_(eax, Immediate(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
+ __ 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(eax, Immediate(0x2028 - 0x0b));
+ __ cmp(eax, 0x2029 - 0x2028);
+ BranchOrBacktrack(below_equal, on_no_match);
+ }
+ return true;
+ }
+ case 'w': {
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ cmp(current_character(), Immediate('z'));
+ BranchOrBacktrack(above, on_no_match);
+ }
+ ASSERT_EQ(0, word_character_map[0]); // Character '\0' is not a word char.
+ ExternalReference word_map = ExternalReference::re_word_character_map();
+ __ test_b(current_character(),
+ Operand::StaticArray(current_character(), times_1, word_map));
+ BranchOrBacktrack(zero, on_no_match);
+ return true;
+ }
+ case 'W': {
+ Label done;
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ cmp(current_character(), Immediate('z'));
+ __ j(above, &done);
+ }
+ ASSERT_EQ(0, word_character_map[0]); // Character '\0' is not a word char.
+ ExternalReference word_map = ExternalReference::re_word_character_map();
+ __ test_b(current_character(),
+ Operand::StaticArray(current_character(), times_1, word_map));
+ BranchOrBacktrack(not_zero, on_no_match);
+ if (mode_ != ASCII) {
+ __ bind(&done);
+ }
+ return true;
+ }
+ // Non-standard classes (with no syntactic shorthand) used internally.
+ case '*':
+ // Match any character.
+ return true;
+ case 'n': {
+ // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 or 0x2029).
+ // The opposite of '.'.
+ __ mov(eax, current_character());
+ __ xor_(eax, Immediate(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
+ __ sub(eax, Immediate(0x0b));
+ __ cmp(eax, 0x0c - 0x0b);
+ if (mode_ == ASCII) {
+ BranchOrBacktrack(above, on_no_match);
+ } else {
+ Label done;
+ BranchOrBacktrack(below_equal, &done);
+ ASSERT_EQ(UC16, mode_);
+ // 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(eax, Immediate(0x2028 - 0x0b));
+ __ cmp(eax, 1);
+ BranchOrBacktrack(above, on_no_match);
+ __ bind(&done);
+ }
+ return true;
+ }
+ // No custom implementation (yet): s(UC16), S(UC16).
+ default:
+ return false;
+ }
+}
+
+
+void RegExpMacroAssemblerIA32::Fail() {
+ STATIC_ASSERT(FAILURE == 0); // Return value for failure is zero.
+ if (!global()) {
+ __ Set(eax, Immediate(FAILURE));
+ }
+ __ jmp(&exit_label_);
+}
+
+
+Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
+ Label return_eax;
+ // Finalize code - write the entry point code now we know how many
+ // registers we need.
+
+ // Entry code:
+ __ bind(&entry_label_);
+
+ // 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
+ // kBackup_ebx etc.
+ __ push(esi);
+ __ push(edi);
+ __ push(ebx); // Callee-save on MacOS.
+ __ push(Immediate(0)); // Number of successful matches in a global regexp.
+ __ push(Immediate(0)); // Make room for "input start - 1" constant.
+
+ // Check if we have space on the stack for registers.
+ Label stack_limit_hit;
+ Label stack_ok;
+
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ mov(ecx, esp);
+ __ sub(ecx, Operand::StaticVariable(stack_limit));
+ // Handle it if the stack pointer is already below the stack limit.
+ __ j(below_equal, &stack_limit_hit);
+ // Check if there is room for the variable number of registers above
+ // the stack limit.
+ __ cmp(ecx, num_registers_ * kPointerSize);
+ __ j(above_equal, &stack_ok);
+ // Exit with OutOfMemory exception. There is not enough space on the stack
+ // for our working registers.
+ __ mov(eax, EXCEPTION);
+ __ jmp(&return_eax);
+
+ __ bind(&stack_limit_hit);
+ CallCheckStackGuardState(ebx);
+ __ or_(eax, eax);
+ // If returned value is non-zero, we exit with the returned value as result.
+ __ j(not_zero, &return_eax);
+
+ __ bind(&stack_ok);
+ // Load start index for later use.
+ __ mov(ebx, Operand(ebp, kStartIndex));
+
+ // Allocate space on stack for registers.
+ __ 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, esi);
+
+ // Set eax to address of char before start of the string.
+ // (effectively string position -1).
+ __ neg(ebx);
+ if (mode_ == UC16) {
+ __ lea(eax, Operand(edi, ebx, times_2, -char_size()));
+ } else {
+ __ lea(eax, Operand(edi, ebx, times_1, -char_size()));
+ }
+ // Store this value in a local variable, for use when clearing
+ // position registers.
+ __ mov(Operand(ebp, kInputStartMinusOne), eax);
+
+#ifdef WIN32
+ // Ensure that we write to each stack page, in order. Skipping a page
+ // on Windows can cause segmentation faults. Assuming page size is 4k.
+ const int kPageSize = 4096;
+ const int kRegistersPerPage = kPageSize / kPointerSize;
+ for (int i = num_saved_registers_ + kRegistersPerPage - 1;
+ i < num_registers_;
+ i += kRegistersPerPage) {
+ __ mov(register_location(i), eax); // One write every page.
+ }
+#endif // WIN32
+
+ Label load_char_start_regexp, start_regexp;
+ // Load newline if index is at start, previous character otherwise.
+ __ cmp(Operand(ebp, kStartIndex), Immediate(0));
+ __ j(not_equal, &load_char_start_regexp, Label::kNear);
+ __ mov(current_character(), '\n');
+ __ jmp(&start_regexp, Label::kNear);
+
+ // Global regexp restarts matching here.
+ __ bind(&load_char_start_regexp);
+ // Load previous char as initial value of current character register.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&start_regexp);
+
+ // Initialize on-stack registers.
+ if (num_saved_registers_ > 0) { // Always is, if generated from a regexp.
+ // Fill saved registers with initial value = start offset - 1
+ // Fill in stack push order, to avoid accessing across an unwritten
+ // page (a problem on Windows).
+ if (num_saved_registers_ > 8) {
+ __ mov(ecx, kRegisterZero);
+ Label init_loop;
+ __ bind(&init_loop);
+ __ mov(Operand(ebp, ecx, times_1, 0), eax);
+ __ sub(ecx, Immediate(kPointerSize));
+ __ cmp(ecx, kRegisterZero - num_saved_registers_ * kPointerSize);
+ __ j(greater, &init_loop);
+ } else { // Unroll the loop.
+ for (int i = 0; i < num_saved_registers_; i++) {
+ __ mov(register_location(i), eax);
+ }
+ }
+ }
+
+ // Initialize backtrack stack pointer.
+ __ mov(backtrack_stackpointer(), Operand(ebp, kStackHighEnd));
+
+ __ jmp(&start_label_);
+
+ // Exit code:
+ if (success_label_.is_linked()) {
+ // Save captures when successful.
+ __ bind(&success_label_);
+ if (num_saved_registers_ > 0) {
+ // copy captures to output
+ __ mov(ebx, Operand(ebp, kRegisterOutput));
+ __ mov(ecx, Operand(ebp, kInputEnd));
+ __ mov(edx, Operand(ebp, kStartIndex));
+ __ sub(ecx, Operand(ebp, kInputStart));
+ if (mode_ == UC16) {
+ __ lea(ecx, Operand(ecx, edx, times_2, 0));
+ } else {
+ __ add(ecx, edx);
+ }
+ for (int i = 0; i < num_saved_registers_; i++) {
+ __ mov(eax, register_location(i));
+ if (i == 0 && global_with_zero_length_check()) {
+ // Keep capture start in edx for the zero-length check later.
+ __ mov(edx, eax);
+ }
+ // Convert to index from start of string, not end.
+ __ add(eax, ecx);
+ if (mode_ == UC16) {
+ __ sar(eax, 1); // Convert byte index to character index.
+ }
+ __ mov(Operand(ebx, i * kPointerSize), eax);
+ }
+ }
+
+ if (global()) {
+ // Restart matching if the regular expression is flagged as global.
+ // Increment success counter.
+ __ inc(Operand(ebp, kSuccessfulCaptures));
+ // Capture results have been stored, so the number of remaining global
+ // output registers is reduced by the number of stored captures.
+ __ mov(ecx, Operand(ebp, kNumOutputRegisters));
+ __ sub(ecx, Immediate(num_saved_registers_));
+ // Check whether we have enough room for another set of capture results.
+ __ cmp(ecx, Immediate(num_saved_registers_));
+ __ j(less, &exit_label_);
+
+ __ mov(Operand(ebp, kNumOutputRegisters), ecx);
+ // Advance the location for output.
+ __ add(Operand(ebp, kRegisterOutput),
+ Immediate(num_saved_registers_ * kPointerSize));
+
+ // Prepare eax to initialize registers with its value in the next run.
+ __ mov(eax, Operand(ebp, kInputStartMinusOne));
+
+ if (global_with_zero_length_check()) {
+ // Special case for zero-length matches.
+ // edx: capture start index
+ __ cmp(edi, edx);
+ // Not a zero-length match, restart.
+ __ j(not_equal, &load_char_start_regexp);
+ // edi (offset from the end) is zero if we already reached the end.
+ __ test(edi, edi);
+ __ j(zero, &exit_label_, Label::kNear);
+ // Advance current position after a zero-length match.
+ if (mode_ == UC16) {
+ __ add(edi, Immediate(2));
+ } else {
+ __ inc(edi);
+ }
+ }
+
+ __ jmp(&load_char_start_regexp);
+ } else {
+ __ mov(eax, Immediate(SUCCESS));
+ }
+ }
+
+ __ bind(&exit_label_);
+ if (global()) {
+ // Return the number of successful captures.
+ __ mov(eax, Operand(ebp, kSuccessfulCaptures));
+ }
+
+ __ bind(&return_eax);
+ // Skip esp past regexp registers.
+ __ lea(esp, Operand(ebp, kBackup_ebx));
+ // Restore callee-save registers.
+ __ pop(ebx);
+ __ pop(edi);
+ __ pop(esi);
+ // Exit function frame, restore previous one.
+ __ pop(ebp);
+ __ ret(0);
+
+ // Backtrack code (branch target for conditional backtracks).
+ if (backtrack_label_.is_linked()) {
+ __ bind(&backtrack_label_);
+ Backtrack();
+ }
+
+ Label exit_with_exception;
+
+ // Preempt-code
+ if (check_preempt_label_.is_linked()) {
+ SafeCallTarget(&check_preempt_label_);
+
+ __ push(backtrack_stackpointer());
+ __ push(edi);
+
+ CallCheckStackGuardState(ebx);
+ __ or_(eax, eax);
+ // If returning non-zero, we should end execution with the given
+ // result as return value.
+ __ j(not_zero, &return_eax);
+
+ __ pop(edi);
+ __ pop(backtrack_stackpointer());
+ // String might have moved: Reload esi from frame.
+ __ mov(esi, Operand(ebp, kInputEnd));
+ SafeReturn();
+ }
+
+ // Backtrack stack overflow code.
+ if (stack_overflow_label_.is_linked()) {
+ SafeCallTarget(&stack_overflow_label_);
+ // Reached if the backtrack-stack limit has been hit.
+
+ Label grow_failed;
+ // Save registers before calling C function
+ __ push(esi);
+ __ push(edi);
+
+ // Call GrowStack(backtrack_stackpointer())
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, ebx);
+ __ mov(Operand(esp, 2 * kPointerSize),
+ Immediate(ExternalReference::isolate_address(isolate())));
+ __ lea(eax, Operand(ebp, kStackHighEnd));
+ __ mov(Operand(esp, 1 * kPointerSize), eax);
+ __ mov(Operand(esp, 0 * kPointerSize), backtrack_stackpointer());
+ ExternalReference grow_stack =
+ ExternalReference::re_grow_stack(isolate());
+ __ 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, eax);
+ __ j(equal, &exit_with_exception);
+ // Otherwise use return value as new stack pointer.
+ __ mov(backtrack_stackpointer(), eax);
+ // Restore saved registers and continue.
+ __ pop(edi);
+ __ pop(esi);
+ SafeReturn();
+ }
+
+ if (exit_with_exception.is_linked()) {
+ // If any of the code above needed to exit with an exception.
+ __ bind(&exit_with_exception);
+ // Exit with Result EXCEPTION(-1) to signal thrown exception.
+ __ mov(eax, EXCEPTION);
+ __ jmp(&return_eax);
+ }
+
+ CodeDesc code_desc;
+ masm_->GetCode(&code_desc);
+ Handle<Code> code =
+ isolate()->factory()->NewCode(code_desc,
+ Code::ComputeFlags(Code::REGEXP),
+ masm_->CodeObject());
+ PROFILE(isolate(), RegExpCodeCreateEvent(*code, *source));
+ return Handle<HeapObject>::cast(code);
+}
+
+
+void RegExpMacroAssemblerIA32::GoTo(Label* to) {
+ BranchOrBacktrack(no_condition, to);
+}
+
+
+void RegExpMacroAssemblerIA32::IfRegisterGE(int reg,
+ int comparand,
+ Label* if_ge) {
+ __ cmp(register_location(reg), Immediate(comparand));
+ BranchOrBacktrack(greater_equal, if_ge);
+}
+
+
+void RegExpMacroAssemblerIA32::IfRegisterLT(int reg,
+ int comparand,
+ Label* if_lt) {
+ __ cmp(register_location(reg), Immediate(comparand));
+ BranchOrBacktrack(less, if_lt);
+}
+
+
+void RegExpMacroAssemblerIA32::IfRegisterEqPos(int reg,
+ Label* if_eq) {
+ __ cmp(edi, register_location(reg));
+ BranchOrBacktrack(equal, if_eq);
+}
+
+
+RegExpMacroAssembler::IrregexpImplementation
+ RegExpMacroAssemblerIA32::Implementation() {
+ return kIA32Implementation;
+}
+
+
+void RegExpMacroAssemblerIA32::LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds,
+ int characters) {
+ ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
+ ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
+ if (check_bounds) {
+ CheckPosition(cp_offset + characters - 1, on_end_of_input);
+ }
+ LoadCurrentCharacterUnchecked(cp_offset, characters);
+}
+
+
+void RegExpMacroAssemblerIA32::PopCurrentPosition() {
+ Pop(edi);
+}
+
+
+void RegExpMacroAssemblerIA32::PopRegister(int register_index) {
+ Pop(eax);
+ __ mov(register_location(register_index), eax);
+}
+
+
+void RegExpMacroAssemblerIA32::PushBacktrack(Label* label) {
+ Push(Immediate::CodeRelativeOffset(label));
+ CheckStackLimit();
+}
+
+
+void RegExpMacroAssemblerIA32::PushCurrentPosition() {
+ Push(edi);
+}
+
+
+void RegExpMacroAssemblerIA32::PushRegister(int register_index,
+ StackCheckFlag check_stack_limit) {
+ __ mov(eax, register_location(register_index));
+ Push(eax);
+ if (check_stack_limit) CheckStackLimit();
+}
+
+
+void RegExpMacroAssemblerIA32::ReadCurrentPositionFromRegister(int reg) {
+ __ mov(edi, register_location(reg));
+}
+
+
+void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) {
+ __ mov(backtrack_stackpointer(), register_location(reg));
+ __ add(backtrack_stackpointer(), Operand(ebp, kStackHighEnd));
+}
+
+void RegExpMacroAssemblerIA32::SetCurrentPositionFromEnd(int by) {
+ Label after_position;
+ __ cmp(edi, -by * char_size());
+ __ j(greater_equal, &after_position, Label::kNear);
+ __ mov(edi, -by * char_size());
+ // On RegExp code entry (where this operation is used), the character before
+ // the current position is expected to be already loaded.
+ // We have advanced the position, so it's safe to read backwards.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&after_position);
+}
+
+
+void RegExpMacroAssemblerIA32::SetRegister(int register_index, int to) {
+ ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
+ __ mov(register_location(register_index), Immediate(to));
+}
+
+
+bool RegExpMacroAssemblerIA32::Succeed() {
+ __ jmp(&success_label_);
+ return global();
+}
+
+
+void RegExpMacroAssemblerIA32::WriteCurrentPositionToRegister(int reg,
+ int cp_offset) {
+ if (cp_offset == 0) {
+ __ mov(register_location(reg), edi);
+ } else {
+ __ lea(eax, Operand(edi, cp_offset * char_size()));
+ __ mov(register_location(reg), eax);
+ }
+}
+
+
+void RegExpMacroAssemblerIA32::ClearRegisters(int reg_from, int reg_to) {
+ ASSERT(reg_from <= reg_to);
+ __ mov(eax, Operand(ebp, kInputStartMinusOne));
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ __ mov(register_location(reg), eax);
+ }
+}
+
+
+void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) {
+ __ mov(eax, backtrack_stackpointer());
+ __ sub(eax, Operand(ebp, kStackHighEnd));
+ __ mov(register_location(reg), eax);
+}
+
+
+// Private methods:
+
+void RegExpMacroAssemblerIA32::CallCheckStackGuardState(Register scratch) {
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, scratch);
+ // RegExp code frame pointer.
+ __ mov(Operand(esp, 2 * kPointerSize), ebp);
+ // Code* of self.
+ __ mov(Operand(esp, 1 * kPointerSize), Immediate(masm_->CodeObject()));
+ // Next address on the stack (will be address of return address).
+ __ lea(eax, Operand(esp, -kPointerSize));
+ __ mov(Operand(esp, 0 * kPointerSize), eax);
+ ExternalReference check_stack_guard =
+ ExternalReference::re_check_stack_guard_state(isolate());
+ __ CallCFunction(check_stack_guard, num_arguments);
+}
+
+
+// Helper function for reading a value out of a stack frame.
+template <typename T>
+static T& frame_entry(Address re_frame, int frame_offset) {
+ return reinterpret_cast<T&>(Memory::int32_at(re_frame + frame_offset));
+}
+
+
+int RegExpMacroAssemblerIA32::CheckStackGuardState(Address* return_address,
+ Code* re_code,
+ Address re_frame) {
+ Isolate* isolate = frame_entry<Isolate*>(re_frame, kIsolate);
+ ASSERT(isolate == Isolate::Current());
+ if (isolate->stack_guard()->IsStackOverflow()) {
+ isolate->StackOverflow();
+ return EXCEPTION;
+ }
+
+ // If not real stack overflow the stack guard was used to interrupt
+ // execution for another purpose.
+
+ // If this is a direct call from JavaScript retry the RegExp forcing the call
+ // through the runtime system. Currently the direct call cannot handle a GC.
+ if (frame_entry<int>(re_frame, kDirectCall) == 1) {
+ return RETRY;
+ }
+
+ // Prepare for possible GC.
+ HandleScope handles(isolate);
+ Handle<Code> code_handle(re_code);
+
+ Handle<String> subject(frame_entry<String*>(re_frame, kInputString));
+
+ // Current string.
+ bool is_ascii = subject->IsOneByteRepresentationUnderneath();
+
+ ASSERT(re_code->instruction_start() <= *return_address);
+ ASSERT(*return_address <=
+ re_code->instruction_start() + re_code->instruction_size());
+
+ MaybeObject* result = Execution::HandleStackGuardInterrupt(isolate);
+
+ if (*code_handle != re_code) { // Return address no longer valid
+ int delta = code_handle->address() - re_code->address();
+ // Overwrite the return address on the stack.
+ *return_address += delta;
+ }
+
+ if (result->IsException()) {
+ return EXCEPTION;
+ }
+
+ Handle<String> subject_tmp = subject;
+ int slice_offset = 0;
+
+ // Extract the underlying string and the slice offset.
+ if (StringShape(*subject_tmp).IsCons()) {
+ subject_tmp = Handle<String>(ConsString::cast(*subject_tmp)->first());
+ } else if (StringShape(*subject_tmp).IsSliced()) {
+ SlicedString* slice = SlicedString::cast(*subject_tmp);
+ subject_tmp = Handle<String>(slice->parent());
+ slice_offset = slice->offset();
+ }
+
+ // String might have changed.
+ if (subject_tmp->IsOneByteRepresentation() != is_ascii) {
+ // If we changed between an ASCII and an UC16 string, the specialized
+ // code cannot be used, and we need to restart regexp matching from
+ // scratch (including, potentially, compiling a new version of the code).
+ return RETRY;
+ }
+
+ // Otherwise, the content of the string might have moved. It must still
+ // be a sequential or external string with the same content.
+ // Update the start and end pointers in the stack frame to the current
+ // location (whether it has actually moved or not).
+ ASSERT(StringShape(*subject_tmp).IsSequential() ||
+ StringShape(*subject_tmp).IsExternal());
+
+ // The original start address of the characters to match.
+ const byte* start_address = frame_entry<const byte*>(re_frame, kInputStart);
+
+ // Find the current start address of the same character at the current string
+ // position.
+ int start_index = frame_entry<int>(re_frame, kStartIndex);
+ const byte* new_address = StringCharacterPosition(*subject_tmp,
+ start_index + slice_offset);
+
+ if (start_address != new_address) {
+ // If there is a difference, update the object pointer and start and end
+ // addresses in the RegExp stack frame to match the new value.
+ const byte* end_address = frame_entry<const byte* >(re_frame, kInputEnd);
+ int byte_length = static_cast<int>(end_address - start_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;
+}
+
+
+Operand RegExpMacroAssemblerIA32::register_location(int register_index) {
+ ASSERT(register_index < (1<<30));
+ if (num_registers_ <= register_index) {
+ num_registers_ = register_index + 1;
+ }
+ return Operand(ebp, kRegisterZero - register_index * kPointerSize);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckPosition(int cp_offset,
+ Label* on_outside_input) {
+ __ cmp(edi, -cp_offset * char_size());
+ BranchOrBacktrack(greater_equal, on_outside_input);
+}
+
+
+void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition,
+ Label* to) {
+ if (condition < 0) { // No condition
+ if (to == NULL) {
+ Backtrack();
+ return;
+ }
+ __ jmp(to);
+ return;
+ }
+ if (to == NULL) {
+ __ j(condition, &backtrack_label_);
+ return;
+ }
+ __ j(condition, to);
+}
+
+
+void RegExpMacroAssemblerIA32::SafeCall(Label* to) {
+ Label return_to;
+ __ push(Immediate::CodeRelativeOffset(&return_to));
+ __ jmp(to);
+ __ bind(&return_to);
+}
+
+
+void RegExpMacroAssemblerIA32::SafeReturn() {
+ __ pop(ebx);
+ __ add(ebx, Immediate(masm_->CodeObject()));
+ __ jmp(ebx);
+}
+
+
+void RegExpMacroAssemblerIA32::SafeCallTarget(Label* name) {
+ __ bind(name);
+}
+
+
+void RegExpMacroAssemblerIA32::Push(Register source) {
+ ASSERT(!source.is(backtrack_stackpointer()));
+ // Notice: This updates flags, unlike normal Push.
+ __ sub(backtrack_stackpointer(), Immediate(kPointerSize));
+ __ mov(Operand(backtrack_stackpointer(), 0), source);
+}
+
+
+void RegExpMacroAssemblerIA32::Push(Immediate value) {
+ // Notice: This updates flags, unlike normal Push.
+ __ sub(backtrack_stackpointer(), Immediate(kPointerSize));
+ __ mov(Operand(backtrack_stackpointer(), 0), value);
+}
+
+
+void RegExpMacroAssemblerIA32::Pop(Register target) {
+ ASSERT(!target.is(backtrack_stackpointer()));
+ __ mov(target, Operand(backtrack_stackpointer(), 0));
+ // Notice: This updates flags, unlike normal Pop.
+ __ add(backtrack_stackpointer(), Immediate(kPointerSize));
+}
+
+
+void RegExpMacroAssemblerIA32::CheckPreemption() {
+ // Check for preemption.
+ Label no_preempt;
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ cmp(esp, Operand::StaticVariable(stack_limit));
+ __ j(above, &no_preempt);
+
+ SafeCall(&check_preempt_label_);
+
+ __ bind(&no_preempt);
+}
+
+
+void RegExpMacroAssemblerIA32::CheckStackLimit() {
+ Label no_stack_overflow;
+ ExternalReference stack_limit =
+ ExternalReference::address_of_regexp_stack_limit(isolate());
+ __ cmp(backtrack_stackpointer(), Operand::StaticVariable(stack_limit));
+ __ j(above, &no_stack_overflow);
+
+ SafeCall(&stack_overflow_label_);
+
+ __ bind(&no_stack_overflow);
+}
+
+
+void RegExpMacroAssemblerIA32::LoadCurrentCharacterUnchecked(int cp_offset,
+ int characters) {
+ if (mode_ == ASCII) {
+ if (characters == 4) {
+ __ mov(current_character(), Operand(esi, edi, times_1, cp_offset));
+ } else if (characters == 2) {
+ __ movzx_w(current_character(), Operand(esi, edi, times_1, cp_offset));
+ } else {
+ ASSERT(characters == 1);
+ __ movzx_b(current_character(), Operand(esi, edi, times_1, cp_offset));
+ }
+ } else {
+ ASSERT(mode_ == UC16);
+ if (characters == 2) {
+ __ mov(current_character(),
+ Operand(esi, edi, times_1, cp_offset * sizeof(uc16)));
+ } else {
+ ASSERT(characters == 1);
+ __ movzx_w(current_character(),
+ Operand(esi, edi, times_1, cp_offset * sizeof(uc16)));
+ }
+ }
+}
+
+
+#undef __
+
+#endif // V8_INTERPRETED_REGEXP
+
+}} // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ia32/regexp-macro-assembler-ia32.h b/chromium/v8/src/ia32/regexp-macro-assembler-ia32.h
new file mode 100644
index 00000000000..39333360077
--- /dev/null
+++ b/chromium/v8/src/ia32/regexp-macro-assembler-ia32.h
@@ -0,0 +1,223 @@
+// Copyright 2012 the V8 project authors. 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_IA32_REGEXP_MACRO_ASSEMBLER_IA32_H_
+#define V8_IA32_REGEXP_MACRO_ASSEMBLER_IA32_H_
+
+#include "ia32/assembler-ia32.h"
+#include "ia32/assembler-ia32-inl.h"
+#include "macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+#ifndef V8_INTERPRETED_REGEXP
+class RegExpMacroAssemblerIA32: public NativeRegExpMacroAssembler {
+ public:
+ RegExpMacroAssemblerIA32(Mode mode, int registers_to_save, Zone* zone);
+ virtual ~RegExpMacroAssemblerIA32();
+ virtual int stack_limit_slack();
+ virtual void AdvanceCurrentPosition(int by);
+ virtual void AdvanceRegister(int reg, int by);
+ virtual void Backtrack();
+ virtual void Bind(Label* label);
+ virtual void CheckAtStart(Label* on_at_start);
+ virtual void CheckCharacter(uint32_t c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_equal);
+ virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
+ virtual void CheckCharacterLT(uc16 limit, Label* on_less);
+ // A "greedy loop" is a loop that is both greedy and with a simple
+ // body. It has a particularly simple implementation.
+ virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
+ virtual void CheckNotAtStart(Label* on_not_at_start);
+ virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
+ virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
+ Label* on_no_match);
+ virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_not_equal);
+ virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal);
+ virtual void CheckCharacterInRange(uc16 from,
+ uc16 to,
+ Label* on_in_range);
+ virtual void CheckCharacterNotInRange(uc16 from,
+ uc16 to,
+ Label* on_not_in_range);
+ virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
+
+ // Checks whether the given offset from the current position is before
+ // the end of the string.
+ virtual void CheckPosition(int cp_offset, Label* on_outside_input);
+ virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
+ virtual void Fail();
+ virtual Handle<HeapObject> GetCode(Handle<String> source);
+ virtual void GoTo(Label* label);
+ virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
+ virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
+ virtual void IfRegisterEqPos(int reg, Label* if_eq);
+ virtual IrregexpImplementation Implementation();
+ virtual void LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds = true,
+ int characters = 1);
+ virtual void PopCurrentPosition();
+ virtual void PopRegister(int register_index);
+ virtual void PushBacktrack(Label* label);
+ virtual void PushCurrentPosition();
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
+ virtual void ReadCurrentPositionFromRegister(int reg);
+ virtual void ReadStackPointerFromRegister(int reg);
+ virtual void SetCurrentPositionFromEnd(int by);
+ virtual void SetRegister(int register_index, int to);
+ virtual bool Succeed();
+ virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+ virtual void ClearRegisters(int reg_from, int reg_to);
+ virtual void WriteStackPointerToRegister(int reg);
+
+ // Called from RegExp if the stack-guard is triggered.
+ // If the code object is relocated, the return address is fixed before
+ // returning.
+ static int CheckStackGuardState(Address* return_address,
+ Code* re_code,
+ Address re_frame);
+
+ private:
+ // Offsets from ebp of function parameters and stored registers.
+ static const int kFramePointer = 0;
+ // Above the frame pointer - function parameters and return address.
+ static const int kReturn_eip = kFramePointer + kPointerSize;
+ static const int kFrameAlign = kReturn_eip + kPointerSize;
+ // Parameters.
+ static const int kInputString = kFrameAlign;
+ static const int kStartIndex = kInputString + kPointerSize;
+ static const int kInputStart = kStartIndex + kPointerSize;
+ static const int kInputEnd = kInputStart + kPointerSize;
+ static const int kRegisterOutput = kInputEnd + kPointerSize;
+ // For the case of global regular expression, we have room to store at least
+ // one set of capture results. For the case of non-global regexp, we ignore
+ // this value.
+ static const int kNumOutputRegisters = kRegisterOutput + kPointerSize;
+ static const int kStackHighEnd = kNumOutputRegisters + kPointerSize;
+ static const int kDirectCall = kStackHighEnd + kPointerSize;
+ static const int kIsolate = kDirectCall + kPointerSize;
+ // Below the frame pointer - local stack variables.
+ // When adding local variables remember to push space for them in
+ // the frame in GetCode.
+ static const int kBackup_esi = kFramePointer - kPointerSize;
+ static const int kBackup_edi = kBackup_esi - kPointerSize;
+ static const int kBackup_ebx = kBackup_edi - kPointerSize;
+ static const int kSuccessfulCaptures = kBackup_ebx - kPointerSize;
+ static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
+ // First register address. Following registers are below it on the stack.
+ static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
+
+ // Initial size of code buffer.
+ static const size_t kRegExpCodeSize = 1024;
+
+ // Load a number of characters at the given offset from the
+ // current position, into the current-character register.
+ void LoadCurrentCharacterUnchecked(int cp_offset, int character_count);
+
+ // Check whether preemption has been requested.
+ void CheckPreemption();
+
+ // Check whether we are exceeding the stack limit on the backtrack stack.
+ void CheckStackLimit();
+
+ // Generate a call to CheckStackGuardState.
+ void CallCheckStackGuardState(Register scratch);
+
+ // The ebp-relative location of a regexp register.
+ Operand register_location(int register_index);
+
+ // The register containing the current character after LoadCurrentCharacter.
+ inline Register current_character() { return edx; }
+
+ // The register containing the backtrack stack top. Provides a meaningful
+ // name to the register.
+ inline Register backtrack_stackpointer() { return ecx; }
+
+ // Byte size of chars in the string to match (decided by the Mode argument)
+ inline int char_size() { return static_cast<int>(mode_); }
+
+ // Equivalent to a conditional branch to the label, unless the label
+ // is NULL, in which case it is a conditional Backtrack.
+ void BranchOrBacktrack(Condition condition, Label* to);
+
+ // Call and return internally in the generated code in a way that
+ // is GC-safe (i.e., doesn't leave absolute code addresses on the stack)
+ inline void SafeCall(Label* to);
+ inline void SafeReturn();
+ inline void SafeCallTarget(Label* name);
+
+ // Pushes the value of a register on the backtrack stack. Decrements the
+ // stack pointer (ecx) by a word size and stores the register's value there.
+ inline void Push(Register source);
+
+ // Pushes a value on the backtrack stack. Decrements the stack pointer (ecx)
+ // by a word size and stores the value there.
+ inline void Push(Immediate value);
+
+ // Pops a value from the backtrack stack. Reads the word at the stack pointer
+ // (ecx) and increments it by a word size.
+ inline void Pop(Register target);
+
+ Isolate* isolate() const { return masm_->isolate(); }
+
+ MacroAssembler* masm_;
+
+ // Which mode to generate code for (ASCII or UC16).
+ Mode mode_;
+
+ // One greater than maximal register index actually used.
+ int num_registers_;
+
+ // Number of registers to output at the end (the saved registers
+ // are always 0..num_saved_registers_-1)
+ int num_saved_registers_;
+
+ // Labels used internally.
+ Label entry_label_;
+ Label start_label_;
+ Label success_label_;
+ Label backtrack_label_;
+ Label exit_label_;
+ Label check_preempt_label_;
+ Label stack_overflow_label_;
+};
+#endif // V8_INTERPRETED_REGEXP
+
+}} // namespace v8::internal
+
+#endif // V8_IA32_REGEXP_MACRO_ASSEMBLER_IA32_H_
diff --git a/chromium/v8/src/ia32/simulator-ia32.cc b/chromium/v8/src/ia32/simulator-ia32.cc
new file mode 100644
index 00000000000..ab8169375c0
--- /dev/null
+++ b/chromium/v8/src/ia32/simulator-ia32.cc
@@ -0,0 +1,30 @@
+// Copyright 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.
+
+
+// Since there is no simulator for the ia32 architecture this file is empty.
+
diff --git a/chromium/v8/src/ia32/simulator-ia32.h b/chromium/v8/src/ia32/simulator-ia32.h
new file mode 100644
index 00000000000..478d4ce5cb3
--- /dev/null
+++ b/chromium/v8/src/ia32/simulator-ia32.h
@@ -0,0 +1,74 @@
+// Copyright 2012 the V8 project authors. 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_IA32_SIMULATOR_IA32_H_
+#define V8_IA32_SIMULATOR_IA32_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+// Since there is no simulator for the ia32 architecture the only thing we can
+// do is to call the entry directly.
+#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
+ (entry(p0, p1, p2, p3, p4))
+
+
+typedef int (*regexp_matcher)(String*, int, const byte*,
+ const byte*, int*, int, Address, int, Isolate*);
+
+// Call the generated regexp code directly. The code at the entry address should
+// expect eight int/pointer sized arguments and return an int.
+#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \
+ (FUNCTION_CAST<regexp_matcher>(entry)(p0, p1, p2, p3, p4, p5, p6, p7, p8))
+
+
+#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
+ (reinterpret_cast<TryCatch*>(try_catch_address))
+
+// The stack limit beyond which we will throw stack overflow errors in
+// generated code. Because generated code on ia32 uses the C stack, we
+// just use the C stack limit.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+ static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+ uintptr_t c_limit) {
+ USE(isolate);
+ return c_limit;
+ }
+
+ static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
+ return try_catch_address;
+ }
+
+ static inline void UnregisterCTryCatch() { }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_IA32_SIMULATOR_IA32_H_
diff --git a/chromium/v8/src/ia32/stub-cache-ia32.cc b/chromium/v8/src/ia32/stub-cache-ia32.cc
new file mode 100644
index 00000000000..df7ad4467f9
--- /dev/null
+++ b/chromium/v8/src/ia32/stub-cache-ia32.cc
@@ -0,0 +1,3777 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_IA32
+
+#include "ic-inl.h"
+#include "codegen.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+static void ProbeTable(Isolate* isolate,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ StubCache::Table table,
+ Register name,
+ Register receiver,
+ // Number of the cache entry pointer-size scaled.
+ Register offset,
+ Register extra) {
+ ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
+ ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
+ ExternalReference map_offset(isolate->stub_cache()->map_reference(table));
+
+ Label miss;
+
+ // Multiply by 3 because there are 3 fields per entry (name, code, map).
+ __ lea(offset, Operand(offset, offset, times_2, 0));
+
+ if (extra.is_valid()) {
+ // Get the code entry from the cache.
+ __ mov(extra, Operand::StaticArray(offset, times_1, value_offset));
+
+ // Check that the key in the entry matches the name.
+ __ cmp(name, Operand::StaticArray(offset, times_1, key_offset));
+ __ j(not_equal, &miss);
+
+ // Check the map matches.
+ __ mov(offset, Operand::StaticArray(offset, times_1, map_offset));
+ __ cmp(offset, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ j(not_equal, &miss);
+
+ // Check that the flags match what we're looking for.
+ __ mov(offset, FieldOperand(extra, Code::kFlagsOffset));
+ __ and_(offset, ~Code::kFlagsNotUsedInLookup);
+ __ cmp(offset, flags);
+ __ j(not_equal, &miss);
+
+#ifdef DEBUG
+ if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
+ __ jmp(&miss);
+ } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
+ __ jmp(&miss);
+ }
+#endif
+
+ // Jump to the first instruction in the code stub.
+ __ add(extra, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ __ jmp(extra);
+
+ __ bind(&miss);
+ } else {
+ // Save the offset on the stack.
+ __ push(offset);
+
+ // Check that the key in the entry matches the name.
+ __ cmp(name, Operand::StaticArray(offset, times_1, key_offset));
+ __ j(not_equal, &miss);
+
+ // Check the map matches.
+ __ mov(offset, Operand::StaticArray(offset, times_1, map_offset));
+ __ cmp(offset, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ j(not_equal, &miss);
+
+ // Restore offset register.
+ __ mov(offset, Operand(esp, 0));
+
+ // Get the code entry from the cache.
+ __ mov(offset, Operand::StaticArray(offset, times_1, value_offset));
+
+ // Check that the flags match what we're looking for.
+ __ mov(offset, FieldOperand(offset, Code::kFlagsOffset));
+ __ and_(offset, ~Code::kFlagsNotUsedInLookup);
+ __ cmp(offset, flags);
+ __ j(not_equal, &miss);
+
+#ifdef DEBUG
+ if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
+ __ jmp(&miss);
+ } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
+ __ jmp(&miss);
+ }
+#endif
+
+ // Restore offset and re-load code entry from cache.
+ __ pop(offset);
+ __ mov(offset, Operand::StaticArray(offset, times_1, value_offset));
+
+ // Jump to the first instruction in the code stub.
+ __ add(offset, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ __ jmp(offset);
+
+ // Pop at miss.
+ __ bind(&miss);
+ __ pop(offset);
+ }
+}
+
+
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// 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 unique and receiver must be a heap object.
+static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ Handle<Name> name,
+ Register r0,
+ Register r1) {
+ ASSERT(name->IsUniqueName());
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->negative_lookups(), 1);
+ __ IncrementCounter(counters->negative_lookups_miss(), 1);
+
+ __ mov(r0, FieldOperand(receiver, HeapObject::kMapOffset));
+
+ const int kInterceptorOrAccessCheckNeededMask =
+ (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
+
+ // Bail out if the receiver has a named interceptor or requires access checks.
+ __ test_b(FieldOperand(r0, Map::kBitFieldOffset),
+ kInterceptorOrAccessCheckNeededMask);
+ __ j(not_zero, miss_label);
+
+ // Check that receiver is a JSObject.
+ __ CmpInstanceType(r0, FIRST_SPEC_OBJECT_TYPE);
+ __ j(below, miss_label);
+
+ // Load properties array.
+ Register properties = r0;
+ __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset));
+
+ // Check that the properties array is a dictionary.
+ __ cmp(FieldOperand(properties, HeapObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->hash_table_map()));
+ __ j(not_equal, miss_label);
+
+ Label done;
+ NameDictionaryLookupStub::GenerateNegativeLookup(masm,
+ miss_label,
+ &done,
+ properties,
+ name,
+ r1);
+ __ bind(&done);
+ __ DecrementCounter(counters->negative_lookups_miss(), 1);
+}
+
+
+void StubCache::GenerateProbe(MacroAssembler* masm,
+ Code::Flags flags,
+ Register receiver,
+ Register name,
+ Register scratch,
+ Register extra,
+ Register extra2,
+ Register extra3) {
+ Label miss;
+
+ // Assert that code is valid. The multiplying code relies on the entry size
+ // being 12.
+ ASSERT(sizeof(Entry) == 12);
+
+ // Assert the flags do not name a specific type.
+ ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
+
+ // 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));
+
+ // Assert scratch and extra registers are valid, and extra2/3 are unused.
+ ASSERT(!scratch.is(no_reg));
+ ASSERT(extra2.is(no_reg));
+ ASSERT(extra3.is(no_reg));
+
+ Register offset = scratch;
+ scratch = no_reg;
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Get the map of the receiver and compute the hash.
+ __ mov(offset, FieldOperand(name, Name::kHashFieldOffset));
+ __ add(offset, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ xor_(offset, flags);
+ // We mask out the last two bits because they are not part of the hash and
+ // they are always 01 for maps. Also in the two 'and' instructions below.
+ __ and_(offset, (kPrimaryTableSize - 1) << kHeapObjectTagSize);
+ // ProbeTable expects the offset to be pointer scaled, which it is, because
+ // the heap object tag size is 2 and the pointer size log 2 is also 2.
+ ASSERT(kHeapObjectTagSize == kPointerSizeLog2);
+
+ // Probe the primary table.
+ ProbeTable(isolate(), masm, flags, kPrimary, name, receiver, offset, extra);
+
+ // Primary miss: Compute hash for secondary probe.
+ __ mov(offset, FieldOperand(name, Name::kHashFieldOffset));
+ __ add(offset, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ xor_(offset, flags);
+ __ and_(offset, (kPrimaryTableSize - 1) << kHeapObjectTagSize);
+ __ sub(offset, name);
+ __ add(offset, Immediate(flags));
+ __ and_(offset, (kSecondaryTableSize - 1) << kHeapObjectTagSize);
+
+ // Probe the secondary table.
+ ProbeTable(
+ isolate(), masm, flags, kSecondary, name, receiver, offset, extra);
+
+ // Cache miss: Fall-through and let caller handle the miss by
+ // entering the runtime system.
+ __ bind(&miss);
+ __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
+}
+
+
+void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
+ int index,
+ Register prototype) {
+ __ LoadGlobalFunction(index, prototype);
+ __ LoadGlobalFunctionInitialMap(prototype, prototype);
+ // Load the prototype from the initial map.
+ __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
+}
+
+
+void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
+ MacroAssembler* masm,
+ int index,
+ Register prototype,
+ Label* miss) {
+ // Check we're still in the same context.
+ __ cmp(Operand(esi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)),
+ masm->isolate()->global_object());
+ __ j(not_equal, miss);
+ // Get the global function with the given index.
+ Handle<JSFunction> function(
+ JSFunction::cast(masm->isolate()->native_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.
+ __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
+}
+
+
+void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch,
+ Label* miss_label) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss_label);
+
+ // Check that the object is a JS array.
+ __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
+ __ j(not_equal, miss_label);
+
+ // Load length directly from the JS array.
+ __ mov(eax, FieldOperand(receiver, JSArray::kLengthOffset));
+ __ ret(0);
+}
+
+
+// Generate code to check if an object is a string. If the object is
+// a string, the map's instance type is left in the scratch register.
+static void GenerateStringCheck(MacroAssembler* masm,
+ Register receiver,
+ Register scratch,
+ Label* smi,
+ Label* non_string_object) {
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, smi);
+
+ // Check that the object is a string.
+ __ mov(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ test(scratch, Immediate(kNotStringTag));
+ __ j(not_zero, non_string_object);
+}
+
+
+void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss,
+ bool support_wrappers) {
+ Label check_wrapper;
+
+ // Check if the object is a string leaving the instance type in the
+ // scratch register.
+ GenerateStringCheck(masm, receiver, scratch1, miss,
+ support_wrappers ? &check_wrapper : miss);
+
+ // Load length from the string and convert to a smi.
+ __ mov(eax, FieldOperand(receiver, String::kLengthOffset));
+ __ ret(0);
+
+ if (support_wrappers) {
+ // Check if the object is a JSValue wrapper.
+ __ bind(&check_wrapper);
+ __ cmp(scratch1, JS_VALUE_TYPE);
+ __ j(not_equal, miss);
+
+ // Check if the wrapped value is a string and load the length
+ // directly if it is.
+ __ mov(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
+ GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
+ __ mov(eax, FieldOperand(scratch2, String::kLengthOffset));
+ __ ret(0);
+ }
+}
+
+
+void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label) {
+ __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
+ __ mov(eax, scratch1);
+ __ ret(0);
+}
+
+
+void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
+ Register dst,
+ Register src,
+ bool inobject,
+ int index,
+ Representation representation) {
+ ASSERT(!FLAG_track_double_fields || !representation.IsDouble());
+ int offset = index * kPointerSize;
+ if (!inobject) {
+ // Calculate the offset into the properties array.
+ offset = offset + FixedArray::kHeaderSize;
+ __ mov(dst, FieldOperand(src, JSObject::kPropertiesOffset));
+ src = dst;
+ }
+ __ mov(dst, FieldOperand(src, offset));
+}
+
+
+static void PushInterceptorArguments(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ Handle<JSObject> holder_obj) {
+ __ push(name);
+ Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor());
+ ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor));
+ Register scratch = name;
+ __ mov(scratch, Immediate(interceptor));
+ __ push(scratch);
+ __ push(receiver);
+ __ push(holder);
+ __ push(FieldOperand(scratch, InterceptorInfo::kDataOffset));
+ __ push(Immediate(reinterpret_cast<int>(masm->isolate())));
+}
+
+
+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),
+ masm->isolate()),
+ 6);
+}
+
+
+// Number of pointers to be reserved on stack for fast API call.
+static const int kFastApiCallArguments = FunctionCallbackArguments::kArgsLength;
+
+
+// Reserves space for the extra arguments to API function in the
+// caller's frame.
+//
+// These arguments are set by CheckPrototypes and GenerateFastApiCall.
+static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
+ // ----------- S t a t e -------------
+ // -- esp[0] : return address
+ // -- esp[4] : last argument in the internal frame of the caller
+ // -----------------------------------
+ __ pop(scratch);
+ for (int i = 0; i < kFastApiCallArguments; i++) {
+ __ push(Immediate(Smi::FromInt(0)));
+ }
+ __ push(scratch);
+}
+
+
+// Undoes the effects of ReserveSpaceForFastApiCall.
+static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
+ // ----------- S t a t e -------------
+ // -- esp[0] : return address.
+ // -- esp[4] : last fast api call extra argument.
+ // -- ...
+ // -- esp[kFastApiCallArguments * 4] : first fast api call extra argument.
+ // -- esp[kFastApiCallArguments * 4 + 4] : last argument in the internal
+ // frame.
+ // -----------------------------------
+ __ pop(scratch);
+ __ add(esp, Immediate(kPointerSize * kFastApiCallArguments));
+ __ push(scratch);
+}
+
+
+// Generates call to API function.
+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
+ // (last fast api call extra argument,
+ // set by CheckPrototypes)
+ // -- esp[8] : api function
+ // (first fast api call extra argument)
+ // -- esp[12] : api call data
+ // -- esp[16] : isolate
+ // -- esp[20] : ReturnValue default value
+ // -- esp[24] : ReturnValue
+ // -- esp[28] : last argument
+ // -- ...
+ // -- esp[(argc + 6) * 4] : first argument
+ // -- esp[(argc + 7) * 4] : receiver
+ // -----------------------------------
+ // Get the function and setup the context.
+ 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);
+ Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
+ Handle<Object> call_data(api_call_info->data(), masm->isolate());
+ 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(call_data));
+ }
+ __ mov(Operand(esp, 4 * kPointerSize),
+ Immediate(reinterpret_cast<int>(masm->isolate())));
+ __ mov(Operand(esp, 5 * kPointerSize),
+ masm->isolate()->factory()->undefined_value());
+ __ mov(Operand(esp, 6 * kPointerSize),
+ masm->isolate()->factory()->undefined_value());
+
+ // Prepare arguments.
+ STATIC_ASSERT(kFastApiCallArguments == 6);
+ __ lea(eax, Operand(esp, kFastApiCallArguments * kPointerSize));
+
+
+ // API function gets reference to the v8::Arguments. If CPU profiler
+ // is enabled wrapper function will be called and we need to pass
+ // address of the callback as additional parameter, always allocate
+ // space for it.
+ const int kApiArgc = 1 + 1;
+
+ // Allocate the v8::Arguments structure in the arguments' space since
+ // it's not controlled by GC.
+ const int kApiStackSpace = 4;
+
+ // Function address is a foreign pointer outside V8's heap.
+ Address function_address = v8::ToCData<Address>(api_call_info->callback());
+ bool returns_handle =
+ !CallbackTable::ReturnsVoid(masm->isolate(),
+ reinterpret_cast<void*>(function_address));
+ __ PrepareCallApiFunction(kApiArgc + kApiStackSpace, returns_handle);
+
+ // v8::Arguments::implicit_args_.
+ __ mov(ApiParameterOperand(2, returns_handle), eax);
+ __ add(eax, Immediate(argc * kPointerSize));
+ // v8::Arguments::values_.
+ __ mov(ApiParameterOperand(3, returns_handle), eax);
+ // v8::Arguments::length_.
+ __ Set(ApiParameterOperand(4, returns_handle), Immediate(argc));
+ // v8::Arguments::is_construct_call_.
+ __ Set(ApiParameterOperand(5, returns_handle), Immediate(0));
+
+ // v8::InvocationCallback's argument.
+ __ lea(eax, ApiParameterOperand(2, returns_handle));
+ __ mov(ApiParameterOperand(0, returns_handle), eax);
+
+ Address thunk_address = returns_handle
+ ? FUNCTION_ADDR(&InvokeInvocationCallback)
+ : FUNCTION_ADDR(&InvokeFunctionCallback);
+
+ __ CallApiFunctionAndReturn(function_address,
+ thunk_address,
+ ApiParameterOperand(1, returns_handle),
+ argc + kFastApiCallArguments + 1,
+ returns_handle,
+ kFastApiCallArguments + 1);
+}
+
+
+class CallInterceptorCompiler BASE_EMBEDDED {
+ public:
+ CallInterceptorCompiler(StubCompiler* stub_compiler,
+ const ParameterCount& arguments,
+ Register name,
+ Code::ExtraICState extra_state)
+ : stub_compiler_(stub_compiler),
+ arguments_(arguments),
+ name_(name),
+ extra_state_(extra_state) {}
+
+ void Compile(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> 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()) {
+ CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3,
+ holder, lookup, name, optimization, miss);
+ } else {
+ CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3,
+ name, holder, miss);
+ }
+ }
+
+ private:
+ void CompileCacheable(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<JSObject> interceptor_holder,
+ LookupResult* lookup,
+ Handle<Name> name,
+ const CallOptimization& optimization,
+ Label* miss_label) {
+ ASSERT(optimization.is_constant_call());
+ ASSERT(!lookup->holder()->IsGlobalObject());
+
+ 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, Handle<JSObject>(lookup->holder()));
+ }
+ can_do_fast_api_call =
+ depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth;
+ }
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->call_const_interceptor(), 1);
+
+ if (can_do_fast_api_call) {
+ __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1);
+ ReserveSpaceForFastApiCall(masm, scratch1);
+ }
+
+ // Check that the maps from receiver to interceptor's holder
+ // haven't changed and thus we can invoke interceptor.
+ 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);
+
+ // Invoke an interceptor and if it provides a value,
+ // branch to |regular_invoke|.
+ Label regular_invoke;
+ LoadWithInterceptor(masm, receiver, holder, interceptor_holder,
+ &regular_invoke);
+
+ // Interceptor returned nothing for this property. Try to use cached
+ // constant function.
+
+ // 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()) {
+ stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
+ 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
+ // safe to omit it here, as if present, it should be fetched
+ // by the previous CheckPrototypes.
+ ASSERT(depth2 == kInvalidProtoDepth);
+ }
+
+ // Invoke function.
+ if (can_do_fast_api_call) {
+ GenerateFastApiCall(masm, optimization, arguments_.immediate());
+ } else {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ Handle<JSFunction> function = optimization.constant_function();
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments_,
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+ }
+
+ // Deferred code for fast API call case---clean preallocated space.
+ if (can_do_fast_api_call) {
+ __ bind(&miss_cleanup);
+ FreeSpaceForFastApiCall(masm, scratch1);
+ __ jmp(miss_label);
+ }
+
+ // Invoke a regular function.
+ __ bind(&regular_invoke);
+ if (can_do_fast_api_call) {
+ FreeSpaceForFastApiCall(masm, scratch1);
+ }
+ }
+
+ void CompileRegular(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<Name> name,
+ Handle<JSObject> interceptor_holder,
+ Label* miss_label) {
+ Register holder =
+ stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3,
+ name, miss_label);
+
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ // Save the name_ register across the call.
+ __ push(name_);
+
+ PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder);
+
+ __ CallExternalReference(
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
+ masm->isolate()),
+ 6);
+
+ // Restore the name_ register.
+ __ pop(name_);
+
+ // Leave the internal frame.
+ }
+
+ void LoadWithInterceptor(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Handle<JSObject> holder_obj,
+ Label* interceptor_succeeded) {
+ {
+ 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);
+ }
+
+ StubCompiler* stub_compiler_;
+ const ParameterCount& arguments_;
+ Register name_;
+ Code::ExtraICState extra_state_;
+};
+
+
+void BaseStoreStubCompiler::GenerateRestoreName(MacroAssembler* masm,
+ Label* label,
+ Handle<Name> name) {
+ if (!label->is_unused()) {
+ __ bind(label);
+ __ mov(this->name(), Immediate(name));
+ }
+}
+
+
+// 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.
+static void GenerateCheckPropertyCell(MacroAssembler* masm,
+ Handle<GlobalObject> global,
+ Handle<Name> name,
+ Register scratch,
+ Label* miss) {
+ Handle<PropertyCell> 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(cell));
+ __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset),
+ Immediate(the_hole));
+ } else {
+ __ cmp(Operand::ForCell(cell), Immediate(the_hole));
+ }
+ __ j(not_equal, miss);
+}
+
+
+void BaseStoreStubCompiler::GenerateNegativeHolderLookup(
+ MacroAssembler* masm,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Handle<Name> name,
+ Label* miss) {
+ if (holder->IsJSGlobalObject()) {
+ GenerateCheckPropertyCell(
+ masm, Handle<GlobalObject>::cast(holder), name, scratch1(), miss);
+ } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) {
+ GenerateDictionaryNegativeLookup(
+ masm, miss, holder_reg, name, scratch1(), scratch2());
+ }
+}
+
+
+// Receiver_reg is preserved on jumps to miss_label, but may be destroyed if
+// store is successful.
+void BaseStoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ Handle<Name> name,
+ Register receiver_reg,
+ Register storage_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Register unused,
+ Label* miss_label,
+ Label* slow) {
+ int descriptor = transition->LastAdded();
+ DescriptorArray* descriptors = transition->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+ Representation representation = details.representation();
+ ASSERT(!representation.IsNone());
+
+ if (details.type() == CONSTANT) {
+ Handle<Object> constant(descriptors->GetValue(descriptor), masm->isolate());
+ __ CmpObject(value_reg, constant);
+ __ j(not_equal, miss_label);
+ } else if (FLAG_track_fields && representation.IsSmi()) {
+ __ JumpIfNotSmi(value_reg, miss_label);
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ __ JumpIfSmi(value_reg, miss_label);
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ Label do_store, heap_number;
+ __ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow);
+
+ __ JumpIfNotSmi(value_reg, &heap_number);
+ __ SmiUntag(value_reg);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ cvtsi2sd(xmm0, value_reg);
+ } else {
+ __ push(value_reg);
+ __ fild_s(Operand(esp, 0));
+ __ pop(value_reg);
+ }
+ __ SmiTag(value_reg);
+ __ jmp(&do_store);
+
+ __ bind(&heap_number);
+ __ CheckMap(value_reg, masm->isolate()->factory()->heap_number_map(),
+ miss_label, DONT_DO_SMI_CHECK);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ movdbl(xmm0, FieldOperand(value_reg, HeapNumber::kValueOffset));
+ } else {
+ __ fld_d(FieldOperand(value_reg, HeapNumber::kValueOffset));
+ }
+
+ __ bind(&do_store);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ movdbl(FieldOperand(storage_reg, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ fstp_d(FieldOperand(storage_reg, HeapNumber::kValueOffset));
+ }
+ }
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ // Perform map transition for the receiver if necessary.
+ if (details.type() == FIELD &&
+ 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(scratch1); // Return address.
+ __ push(receiver_reg);
+ __ push(Immediate(transition));
+ __ push(value_reg);
+ __ push(scratch1);
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
+ masm->isolate()),
+ 3,
+ 1);
+ return;
+ }
+
+ // Update the map of the object.
+ __ mov(scratch1, Immediate(transition));
+ __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch1);
+
+ // Update the write barrier for the map field.
+ __ RecordWriteField(receiver_reg,
+ HeapObject::kMapOffset,
+ scratch1,
+ scratch2,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+
+ if (details.type() == CONSTANT) {
+ ASSERT(value_reg.is(eax));
+ __ ret(0);
+ return;
+ }
+
+ int index = transition->instance_descriptors()->GetFieldIndex(
+ transition->LastAdded());
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ SmiCheck smi_check = representation.IsTagged()
+ ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
+ // TODO(verwaest): Share this code as a code stub.
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ __ mov(FieldOperand(receiver_reg, offset), storage_reg);
+ } else {
+ __ mov(FieldOperand(receiver_reg, offset), value_reg);
+ }
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ if (!FLAG_track_double_fields || !representation.IsDouble()) {
+ __ mov(storage_reg, value_reg);
+ }
+ __ RecordWriteField(receiver_reg,
+ offset,
+ storage_reg,
+ scratch1,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array (optimistically).
+ __ mov(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ __ mov(FieldOperand(scratch1, offset), storage_reg);
+ } else {
+ __ mov(FieldOperand(scratch1, offset), value_reg);
+ }
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ if (!FLAG_track_double_fields || !representation.IsDouble()) {
+ __ mov(storage_reg, value_reg);
+ }
+ __ RecordWriteField(scratch1,
+ offset,
+ storage_reg,
+ receiver_reg,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ }
+
+ // Return the value (register eax).
+ ASSERT(value_reg.is(eax));
+ __ ret(0);
+}
+
+
+// Both name_reg and receiver_reg are preserved on jumps to miss_label,
+// but may be destroyed if store is successful.
+void BaseStoreStubCompiler::GenerateStoreField(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Register receiver_reg,
+ Register name_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label) {
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ int index = lookup->GetFieldIndex().field_index();
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ Representation representation = lookup->representation();
+ ASSERT(!representation.IsNone());
+ if (FLAG_track_fields && representation.IsSmi()) {
+ __ JumpIfNotSmi(value_reg, miss_label);
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ __ JumpIfSmi(value_reg, miss_label);
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ // Load the double storage.
+ if (index < 0) {
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ mov(scratch1, FieldOperand(receiver_reg, offset));
+ } else {
+ __ mov(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ __ mov(scratch1, FieldOperand(scratch1, offset));
+ }
+
+ // Store the value into the storage.
+ Label do_store, heap_number;
+ __ JumpIfNotSmi(value_reg, &heap_number);
+ __ SmiUntag(value_reg);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ cvtsi2sd(xmm0, value_reg);
+ } else {
+ __ push(value_reg);
+ __ fild_s(Operand(esp, 0));
+ __ pop(value_reg);
+ }
+ __ SmiTag(value_reg);
+ __ jmp(&do_store);
+ __ bind(&heap_number);
+ __ CheckMap(value_reg, masm->isolate()->factory()->heap_number_map(),
+ miss_label, DONT_DO_SMI_CHECK);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ movdbl(xmm0, FieldOperand(value_reg, HeapNumber::kValueOffset));
+ } else {
+ __ fld_d(FieldOperand(value_reg, HeapNumber::kValueOffset));
+ }
+ __ bind(&do_store);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ __ movdbl(FieldOperand(scratch1, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ fstp_d(FieldOperand(scratch1, HeapNumber::kValueOffset));
+ }
+ // Return the value (register eax).
+ ASSERT(value_reg.is(eax));
+ __ ret(0);
+ return;
+ }
+
+ ASSERT(!FLAG_track_double_fields || !representation.IsDouble());
+ // TODO(verwaest): Share this code as a code stub.
+ SmiCheck smi_check = representation.IsTagged()
+ ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ mov(FieldOperand(receiver_reg, offset), value_reg);
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ // Pass the value being stored in the now unused name_reg.
+ __ mov(name_reg, value_reg);
+ __ RecordWriteField(receiver_reg,
+ offset,
+ name_reg,
+ scratch1,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array (optimistically).
+ __ mov(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ __ mov(FieldOperand(scratch1, offset), value_reg);
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ // Pass the value being stored in the now unused name_reg.
+ __ mov(name_reg, value_reg);
+ __ RecordWriteField(scratch1,
+ offset,
+ name_reg,
+ receiver_reg,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ }
+
+ // Return the value (register eax).
+ ASSERT(value_reg.is(eax));
+ __ ret(0);
+}
+
+
+// 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<Name> 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()));
+ }
+}
+
+
+void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) {
+ __ jmp(code, RelocInfo::CODE_TARGET);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ Handle<Name> name,
+ int save_at_depth,
+ Label* miss,
+ PrototypeCheckType check) {
+ // Make sure that the type feedback oracle harvests the receiver map.
+ // TODO(svenpanne) Remove this hack when all ICs are reworked.
+ __ mov(scratch1, Handle<Map>(object->map()));
+
+ Handle<JSObject> first = object;
+ // Make sure there's no overlap between holder and object registers.
+ ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
+ ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
+ && !scratch2.is(scratch1));
+
+ // Keep track of the current object in register reg.
+ Register reg = object_reg;
+ Handle<JSObject> current = object;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ __ mov(Operand(esp, kPointerSize), reg);
+ }
+
+ // 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.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());
+
+ Handle<JSObject> prototype(JSObject::cast(current->GetPrototype()));
+ if (!current->HasFastProperties() &&
+ !current->IsJSGlobalObject() &&
+ !current->IsJSGlobalProxy()) {
+ if (!name->IsUniqueName()) {
+ ASSERT(name->IsString());
+ name = factory()->InternalizeString(Handle<String>::cast(name));
+ }
+ ASSERT(current->property_dictionary()->FindEntry(*name) ==
+ NameDictionary::kNotFound);
+
+ GenerateDictionaryNegativeLookup(masm(), miss, reg, name,
+ scratch1, scratch2);
+
+ __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
+ reg = holder_reg; // From now on the object will be in holder_reg.
+ __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
+ } else {
+ bool in_new_space = heap()->InNewSpace(*prototype);
+ Handle<Map> current_map(current->map());
+ if (!current.is_identical_to(first) || check == CHECK_ALL_MAPS) {
+ __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK);
+ }
+
+ // 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, scratch2, miss);
+ }
+
+ if (in_new_space) {
+ // Save the map in scratch1 for later.
+ __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
+ }
+
+ 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);
+ }
+ }
+
+ if (save_at_depth == depth) {
+ __ mov(Operand(esp, kPointerSize), reg);
+ }
+
+ // Go to the next object in the prototype chain.
+ current = prototype;
+ }
+ ASSERT(current.is_identical_to(holder));
+
+ // Log the check depth.
+ LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
+
+ if (!holder.is_identical_to(first) || check == CHECK_ALL_MAPS) {
+ // Check the holder map.
+ __ CheckMap(reg, Handle<Map>(holder->map()), miss, DONT_DO_SMI_CHECK);
+ }
+
+ // Perform security check for access to the global object.
+ ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
+ if (holder->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, scratch2, 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.
+ GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss);
+
+ // Return the register containing the holder.
+ return reg;
+}
+
+
+void BaseLoadStubCompiler::HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) {
+ if (!miss->is_unused()) {
+ __ jmp(success);
+ __ bind(miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+ }
+}
+
+
+void BaseStoreStubCompiler::HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) {
+ if (!miss->is_unused()) {
+ __ jmp(success);
+ GenerateRestoreName(masm(), miss, name);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+ }
+}
+
+
+Register BaseLoadStubCompiler::CallbackHandlerFrontend(
+ Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* success,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label miss;
+
+ Register reg = HandlerFrontendHeader(object, object_reg, holder, name, &miss);
+
+ if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+ ASSERT(!reg.is(scratch2()));
+ ASSERT(!reg.is(scratch3()));
+ Register dictionary = scratch1();
+ bool must_preserve_dictionary_reg = reg.is(dictionary);
+
+ // Load the properties dictionary.
+ if (must_preserve_dictionary_reg) {
+ __ push(dictionary);
+ }
+ __ mov(dictionary, FieldOperand(reg, JSObject::kPropertiesOffset));
+
+ // Probe the dictionary.
+ Label probe_done, pop_and_miss;
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm(),
+ &pop_and_miss,
+ &probe_done,
+ dictionary,
+ this->name(),
+ scratch2(),
+ scratch3());
+ __ bind(&pop_and_miss);
+ if (must_preserve_dictionary_reg) {
+ __ pop(dictionary);
+ }
+ __ jmp(&miss);
+ __ bind(&probe_done);
+
+ // If probing finds an entry in the dictionary, scratch2 contains the
+ // index into the dictionary. Check that the value is the callback.
+ Register index = scratch2();
+ const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ mov(scratch3(),
+ Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag));
+ if (must_preserve_dictionary_reg) {
+ __ pop(dictionary);
+ }
+ __ cmp(scratch3(), callback);
+ __ j(not_equal, &miss);
+ }
+
+ HandlerFrontendFooter(name, success, &miss);
+ return reg;
+}
+
+
+void BaseLoadStubCompiler::NonexistentHandlerFrontend(
+ Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Label* success,
+ Handle<GlobalObject> global) {
+ Label miss;
+
+ HandlerFrontendHeader(object, receiver(), last, name, &miss);
+
+ // If the last object in the prototype chain is a global object,
+ // check that the global property cell is empty.
+ if (!global.is_null()) {
+ GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss);
+ }
+
+ HandlerFrontendFooter(name, success, &miss);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadField(Register reg,
+ Handle<JSObject> holder,
+ PropertyIndex field,
+ Representation representation) {
+ if (!reg.is(receiver())) __ mov(receiver(), reg);
+ if (kind() == Code::LOAD_IC) {
+ LoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode(isolate()));
+ } else {
+ KeyedLoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode(isolate()));
+ }
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadCallback(
+ Register reg,
+ Handle<ExecutableAccessorInfo> callback) {
+ // 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(), esp);
+ ASSERT(!scratch2().is(reg));
+ __ push(reg); // holder
+ // Push data from ExecutableAccessorInfo.
+ if (isolate()->heap()->InNewSpace(callback->data())) {
+ __ mov(scratch1(), Immediate(callback));
+ __ push(FieldOperand(scratch1(), ExecutableAccessorInfo::kDataOffset));
+ } else {
+ __ push(Immediate(Handle<Object>(callback->data(), isolate())));
+ }
+ __ push(Immediate(isolate()->factory()->undefined_value())); // ReturnValue
+ // ReturnValue default value
+ __ push(Immediate(isolate()->factory()->undefined_value()));
+ __ push(Immediate(reinterpret_cast<int>(isolate())));
+
+ // Save a pointer to where we pushed the arguments pointer. This will be
+ // passed as the const ExecutableAccessorInfo& to the C++ callback.
+ __ push(scratch2());
+
+ __ push(name()); // name
+ __ mov(ebx, esp); // esp points to reference to name (handler).
+
+ __ push(scratch3()); // Restore return address.
+
+ // array for v8::Arguments::values_, handler for name and pointer
+ // to the values (it considered as smi in GC).
+ const int kStackSpace = PropertyCallbackArguments::kArgsLength + 2;
+ // Allocate space for opional callback address parameter in case
+ // CPU profiler is active.
+ const int kApiArgc = 2 + 1;
+
+ Address getter_address = v8::ToCData<Address>(callback->getter());
+ bool returns_handle =
+ !CallbackTable::ReturnsVoid(isolate(),
+ reinterpret_cast<void*>(getter_address));
+ __ PrepareCallApiFunction(kApiArgc, returns_handle);
+ __ mov(ApiParameterOperand(0, returns_handle), ebx); // name.
+ __ add(ebx, Immediate(kPointerSize));
+ __ mov(ApiParameterOperand(1, returns_handle), 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.
+
+ Address thunk_address = returns_handle
+ ? FUNCTION_ADDR(&InvokeAccessorGetter)
+ : FUNCTION_ADDR(&InvokeAccessorGetterCallback);
+
+ __ CallApiFunctionAndReturn(getter_address,
+ thunk_address,
+ ApiParameterOperand(2, returns_handle),
+ kStackSpace,
+ returns_handle,
+ 6);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadConstant(Handle<Object> value) {
+ // Return the constant value.
+ __ LoadObject(eax, value);
+ __ ret(0);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadInterceptor(
+ Register holder_reg,
+ Handle<JSObject> object,
+ Handle<JSObject> interceptor_holder,
+ LookupResult* lookup,
+ Handle<Name> name) {
+ ASSERT(interceptor_holder->HasNamedInterceptor());
+ ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // So far the most popular follow ups for interceptor loads are FIELD
+ // and CALLBACKS, so inline only them, other cases may be added
+ // later.
+ bool compile_followup_inline = false;
+ if (lookup->IsFound() && lookup->IsCacheable()) {
+ if (lookup->IsField()) {
+ compile_followup_inline = true;
+ } else if (lookup->type() == CALLBACKS &&
+ lookup->GetCallbackObject()->IsExecutableAccessorInfo()) {
+ ExecutableAccessorInfo* callback =
+ ExecutableAccessorInfo::cast(lookup->GetCallbackObject());
+ compile_followup_inline = callback->getter() != NULL &&
+ callback->IsCompatibleReceiver(*object);
+ }
+ }
+
+ if (compile_followup_inline) {
+ // Compile the interceptor call, followed by inline code to load the
+ // property from further up the prototype chain if the call fails.
+ // Check that the maps haven't changed.
+ ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
+
+ // Preserve the receiver register explicitly whenever it is different from
+ // the holder and it is needed should the interceptor return without any
+ // result. The CALLBACKS case needs the receiver to be passed into C++ code,
+ // the FIELD case might cause a miss during the prototype check.
+ bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
+ bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
+ (lookup->type() == CALLBACKS || must_perfrom_prototype_check);
+
+ // Save necessary data before invoking an interceptor.
+ // Requires a frame to make GC aware of pushed pointers.
+ {
+ FrameScope frame_scope(masm(), StackFrame::INTERNAL);
+
+ if (must_preserve_receiver_reg) {
+ __ push(receiver());
+ }
+ __ push(holder_reg);
+ __ push(this->name());
+
+ // 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,
+ this->name(),
+ 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);
+
+ // Clobber registers when generating debug-code to provoke errors.
+ __ bind(&interceptor_failed);
+ if (FLAG_debug_code) {
+ __ mov(receiver(), Immediate(BitCast<int32_t>(kZapValue)));
+ __ mov(holder_reg, Immediate(BitCast<int32_t>(kZapValue)));
+ __ mov(this->name(), Immediate(BitCast<int32_t>(kZapValue)));
+ }
+
+ __ pop(this->name());
+ __ pop(holder_reg);
+ if (must_preserve_receiver_reg) {
+ __ pop(receiver());
+ }
+
+ // Leave the internal frame.
+ }
+
+ GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup);
+ } else { // !compile_followup_inline
+ // Call the runtime system to load the interceptor.
+ // Check that the maps haven't changed.
+ __ pop(scratch2()); // save old return address
+ PushInterceptorArguments(masm(), receiver(), holder_reg,
+ this->name(), interceptor_holder);
+ __ push(scratch2()); // restore old return address
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad),
+ isolate());
+ __ TailCallExternalReference(ref, 6, 1);
+ }
+}
+
+
+void CallStubCompiler::GenerateNameCheck(Handle<Name> name, Label* miss) {
+ if (kind_ == Code::KEYED_CALL_IC) {
+ __ cmp(ecx, Immediate(name));
+ __ j(not_equal, miss);
+ }
+}
+
+
+void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss) {
+ ASSERT(holder->IsGlobalObject());
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ // Get the receiver from the stack.
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+
+ // Check that the maps haven't changed.
+ __ JumpIfSmi(edx, miss);
+ CheckPrototypes(object, edx, holder, ebx, eax, edi, name, miss);
+}
+
+
+void CallStubCompiler::GenerateLoadFunctionFromCell(
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Label* miss) {
+ // Get the value from the cell.
+ if (Serializer::enabled()) {
+ __ mov(edi, Immediate(cell));
+ __ mov(edi, FieldOperand(edi, Cell::kValueOffset));
+ } else {
+ __ mov(edi, Operand::ForCell(cell));
+ }
+
+ // Check that the cell contains the same 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
+ // function can all use this call IC. Before we load through the
+ // function, we have to verify that it still is a function.
+ __ JumpIfSmi(edi, miss);
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
+ __ j(not_equal, miss);
+
+ // Check the shared function info. Make sure it hasn't changed.
+ __ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset),
+ Immediate(Handle<SharedFunctionInfo>(function->shared())));
+ } else {
+ __ cmp(edi, Immediate(function));
+ }
+ __ j(not_equal, miss);
+}
+
+
+void CallStubCompiler::GenerateMissBranch() {
+ Handle<Code> code =
+ isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
+ kind_,
+ extra_state_);
+ __ jmp(code, RelocInfo::CODE_TARGET);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ PropertyIndex index,
+ Handle<Name> name) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(edx, &miss);
+
+ // Do the right check and compute the holder register.
+ Register reg = CheckPrototypes(object, edx, holder, ebx, eax, edi,
+ name, &miss);
+
+ GenerateFastPropertyLoad(
+ masm(), edi, reg, index.is_inobject(holder),
+ index.translate(holder), Representation::Tagged());
+
+ // Check that the function really is a function.
+ __ JumpIfSmi(edi, &miss);
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
+ __ j(not_equal, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
+ __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
+ }
+
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(edi, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::FIELD, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayCodeCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ Label miss;
+
+ // Check that function is still array
+ const int argc = arguments().immediate();
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ // Get the receiver from the stack.
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(edx, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ Handle<AllocationSite> site = isolate()->factory()->NewAllocationSite();
+ site->set_transition_info(Smi::FromInt(GetInitialFastElementsKind()));
+ Handle<Cell> site_feedback_cell = isolate()->factory()->NewCell(site);
+ __ mov(eax, Immediate(argc));
+ __ mov(ebx, site_feedback_cell);
+ __ mov(edi, function);
+
+ ArrayConstructorStub stub(isolate());
+ __ TailCallStub(&stub);
+
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayPushCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ if (!object->IsJSArray() || !cell.is_null()) {
+ return Handle<Code>::null();
+ }
+
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(edx, &miss);
+
+ CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
+ name, &miss);
+
+ if (argc == 0) {
+ // Noop, return the length.
+ __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
+ __ ret((argc + 1) * kPointerSize);
+ } else {
+ Label call_builtin;
+
+ if (argc == 1) { // Otherwise fall through to call builtin.
+ Label attempt_to_grow_elements, with_write_barrier, check_double;
+
+ // Get the elements array of the object.
+ __ mov(edi, FieldOperand(edx, JSArray::kElementsOffset));
+
+ // Check that the elements are in fast mode and writable.
+ __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
+ Immediate(factory()->fixed_array_map()));
+ __ j(not_equal, &check_double);
+
+ // 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(eax, Immediate(Smi::FromInt(argc)));
+
+ // Get the elements' length into ecx.
+ __ mov(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
+
+ // Check if we could survive without allocation.
+ __ 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);
+
+ // Store the value.
+ __ mov(FieldOperand(edi,
+ eax,
+ times_half_pointer_size,
+ FixedArray::kHeaderSize - argc * kPointerSize),
+ ecx);
+
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&check_double);
+
+
+ // Check that the elements are in double mode.
+ __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
+ Immediate(factory()->fixed_double_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(eax, Immediate(Smi::FromInt(argc)));
+
+ // Get the elements' length into ecx.
+ __ mov(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
+
+ // Check if we could survive without allocation.
+ __ cmp(eax, ecx);
+ __ j(greater, &call_builtin);
+
+ __ mov(ecx, Operand(esp, argc * kPointerSize));
+ __ StoreNumberToDoubleElements(
+ ecx, edi, eax, ecx, xmm0, &call_builtin, true, argc * kDoubleSize);
+
+ // Save new length.
+ __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&with_write_barrier);
+
+ __ 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);
+ __ CheckFastSmiElements(ebx, &call_builtin);
+ __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
+ Immediate(factory()->heap_number_map()));
+ __ j(equal, &call_builtin);
+ // edi: elements array
+ // edx: receiver
+ // ebx: map
+ Label try_holey_map;
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_ELEMENTS,
+ ebx,
+ edi,
+ &try_holey_map);
+
+ ElementsTransitionGenerator::
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
+ // Restore edi.
+ __ mov(edi, FieldOperand(edx, JSArray::kElementsOffset));
+ __ jmp(&fast_object);
+
+ __ bind(&try_holey_map);
+ __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS,
+ FAST_HOLEY_ELEMENTS,
+ ebx,
+ edi,
+ &call_builtin);
+ ElementsTransitionGenerator::
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
+ // 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);
+
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&attempt_to_grow_elements);
+ if (!FLAG_inline_new) {
+ __ 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 =
+ ExternalReference::new_space_allocation_limit_address(isolate());
+
+ const int kAllocationDelta = 4;
+ // Load top.
+ __ mov(ecx, Operand::StaticVariable(new_space_allocation_top));
+
+ // Check if it's the end of elements.
+ __ lea(edx, FieldOperand(edi,
+ eax, times_half_pointer_size,
+ FixedArray::kHeaderSize - argc * kPointerSize));
+ __ cmp(edx, ecx);
+ __ j(not_equal, &call_builtin);
+ __ 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);
+
+ // Push the argument...
+ __ 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(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);
+
+ __ ret((argc + 1) * kPointerSize);
+ }
+
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(
+ ExternalReference(Builtins::c_ArrayPush, isolate()),
+ argc + 1,
+ 1);
+ }
+
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayPopCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ 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.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(edx, &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));
+
+ // 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 array's length into ecx and calculate new length.
+ __ mov(ecx, FieldOperand(edx, JSArray::kLengthOffset));
+ __ sub(ecx, Immediate(Smi::FromInt(1)));
+ __ j(negative, &return_undefined);
+
+ // Get the last element.
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(eax, FieldOperand(ebx,
+ ecx, times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(eax, Immediate(factory()->the_hole_value()));
+ __ j(equal, &call_builtin);
+
+ // Set the array's length.
+ __ mov(FieldOperand(edx, JSArray::kLengthOffset), ecx);
+
+ // Fill with the hole.
+ __ mov(FieldOperand(ebx,
+ ecx, times_half_pointer_size,
+ FixedArray::kHeaderSize),
+ Immediate(factory()->the_hole_value()));
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&return_undefined);
+ __ mov(eax, Immediate(factory()->undefined_value()));
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(
+ ExternalReference(Builtins::c_ArrayPop, isolate()),
+ argc + 1,
+ 1);
+
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- ecx : function name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ 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_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.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ eax,
+ &miss);
+ ASSERT(!object.is_identical_to(holder));
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ eax, holder, ebx, edx, edi, name, &miss);
+
+ Register receiver = ebx;
+ Register index = edi;
+ Register result = eax;
+ __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
+ if (argc > 0) {
+ __ mov(index, Operand(esp, (argc - 0) * kPointerSize));
+ } else {
+ __ Set(index, Immediate(factory()->undefined_value()));
+ }
+
+ 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;
+ generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ Set(eax, Immediate(factory()->nan_value()));
+ __ ret((argc + 1) * kPointerSize);
+ }
+
+ __ bind(&miss);
+ // Restore function name in ecx.
+ __ Set(ecx, Immediate(name));
+ __ bind(&name_miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringCharAtCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- ecx : function name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ 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_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.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ eax,
+ &miss);
+ ASSERT(!object.is_identical_to(holder));
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ eax, holder, ebx, edx, edi, name, &miss);
+
+ Register receiver = eax;
+ Register index = edi;
+ Register scratch = edx;
+ Register result = eax;
+ __ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
+ if (argc > 0) {
+ __ mov(index, Operand(esp, (argc - 0) * kPointerSize));
+ } else {
+ __ Set(index, Immediate(factory()->undefined_value()));
+ }
+
+ 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;
+ generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ Set(eax, Immediate(factory()->empty_string()));
+ __ ret((argc + 1) * kPointerSize);
+ }
+
+ __ bind(&miss);
+ // Restore function name in ecx.
+ __ Set(ecx, Immediate(name));
+ __ bind(&name_miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- ecx : function name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : 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 Handle<Code>::null();
+ }
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(edx, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the char code argument.
+ Register code = ebx;
+ __ mov(code, Operand(esp, 1 * kPointerSize));
+
+ // Check the code is a smi.
+ Label slow;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(code, &slow);
+
+ // Convert the smi code to uint16.
+ __ and_(code, Immediate(Smi::FromInt(0xffff)));
+
+ StringCharFromCodeGenerator generator(code, eax);
+ generator.GenerateFast(masm());
+ __ ret(2 * kPointerSize);
+
+ StubRuntimeCallHelper 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_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+
+ __ bind(&miss);
+ // ecx: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileMathFloorCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ if (!CpuFeatures::IsSupported(SSE2)) {
+ return Handle<Code>::null();
+ }
+
+ CpuFeatureScope use_sse2(masm(), SSE2);
+
+ 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 Handle<Code>::null();
+ }
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
+
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(edx, &miss);
+
+ CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the (only) argument into eax.
+ __ mov(eax, Operand(esp, 1 * kPointerSize));
+
+ // Check if the argument is a smi.
+ Label smi;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(eax, &smi);
+
+ // Check if the argument is a heap number and load its value into xmm0.
+ Label slow;
+ __ CheckMap(eax, factory()->heap_number_map(), &slow, DONT_DO_SMI_CHECK);
+ __ movdbl(xmm0, FieldOperand(eax, HeapNumber::kValueOffset));
+
+ // Check if the argument is strictly positive. Note this also
+ // discards NaN.
+ __ xorpd(xmm1, xmm1);
+ __ ucomisd(xmm0, xmm1);
+ __ j(below_equal, &slow);
+
+ // Do a truncating conversion.
+ __ cvttsd2si(eax, Operand(xmm0));
+
+ // Check if the result fits into a smi. Note this also checks for
+ // 0x80000000 which signals a failed conversion.
+ Label wont_fit_into_smi;
+ __ test(eax, Immediate(0xc0000000));
+ __ j(not_zero, &wont_fit_into_smi);
+
+ // Smi tag and return.
+ __ SmiTag(eax);
+ __ bind(&smi);
+ __ ret(2 * kPointerSize);
+
+ // Check if the argument is < 2^kMantissaBits.
+ Label already_round;
+ __ bind(&wont_fit_into_smi);
+ __ LoadPowerOf2(xmm1, ebx, HeapNumber::kMantissaBits);
+ __ ucomisd(xmm0, xmm1);
+ __ j(above_equal, &already_round);
+
+ // Save a copy of the argument.
+ __ movaps(xmm2, xmm0);
+
+ // Compute (argument + 2^kMantissaBits) - 2^kMantissaBits.
+ __ addsd(xmm0, xmm1);
+ __ subsd(xmm0, xmm1);
+
+ // Compare the argument and the tentative result to get the right mask:
+ // if xmm2 < xmm0:
+ // xmm2 = 1...1
+ // else:
+ // xmm2 = 0...0
+ __ cmpltsd(xmm2, xmm0);
+
+ // Subtract 1 if the argument was less than the tentative result.
+ __ LoadPowerOf2(xmm1, ebx, 0);
+ __ andpd(xmm1, xmm2);
+ __ subsd(xmm0, xmm1);
+
+ // Return a new heap number.
+ __ AllocateHeapNumber(eax, ebx, edx, &slow);
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ __ ret(2 * kPointerSize);
+
+ // Return the argument (when it's an already round heap number).
+ __ bind(&already_round);
+ __ mov(eax, Operand(esp, 1 * kPointerSize));
+ __ ret(2 * kPointerSize);
+
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ __ bind(&slow);
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+
+ __ bind(&miss);
+ // ecx: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileMathAbsCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : 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 Handle<Code>::null();
+ }
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
+
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(edx, &miss);
+
+ CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax, edi,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the (only) argument into eax.
+ __ mov(eax, Operand(esp, 1 * kPointerSize));
+
+ // Check if the argument is a smi.
+ Label not_smi;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(eax, &not_smi);
+
+ // Branchless abs implementation, refer to below:
+ // http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs
+ // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0
+ // otherwise.
+ __ mov(ebx, eax);
+ __ sar(ebx, kBitsPerInt - 1);
+
+ // Do bitwise not or do nothing depending on ebx.
+ __ xor_(eax, ebx);
+
+ // Add 1 or do nothing depending on ebx.
+ __ sub(eax, ebx);
+
+ // If the result is still negative, go to the slow case.
+ // This only happens for the most negative smi.
+ Label slow;
+ __ j(negative, &slow);
+
+ // Smi case done.
+ __ ret(2 * kPointerSize);
+
+ // Check if the argument is a heap number and load its exponent and
+ // sign into ebx.
+ __ bind(&not_smi);
+ __ CheckMap(eax, factory()->heap_number_map(), &slow, DONT_DO_SMI_CHECK);
+ __ mov(ebx, FieldOperand(eax, HeapNumber::kExponentOffset));
+
+ // Check the sign of the argument. If the argument is positive,
+ // just return it.
+ Label negative_sign;
+ __ test(ebx, Immediate(HeapNumber::kSignMask));
+ __ j(not_zero, &negative_sign);
+ __ ret(2 * kPointerSize);
+
+ // If the argument is negative, clear the sign, and return a new
+ // number.
+ __ bind(&negative_sign);
+ __ and_(ebx, ~HeapNumber::kSignMask);
+ __ mov(ecx, FieldOperand(eax, HeapNumber::kMantissaOffset));
+ __ AllocateHeapNumber(eax, edi, edx, &slow);
+ __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ebx);
+ __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx);
+ __ ret(2 * kPointerSize);
+
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ __ bind(&slow);
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+
+ __ bind(&miss);
+ // ecx: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileFastApiCall(
+ const CallOptimization& optimization,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> 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 Handle<Code>::null();
+ if (!cell.is_null()) return Handle<Code>::null();
+ if (!object->IsJSObject()) return Handle<Code>::null();
+ int depth = optimization.GetPrototypeDepthOfExpectedType(
+ 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.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(edx, &miss_before_stack_reserved);
+
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->call_const(), 1);
+ __ IncrementCounter(counters->call_const_fast_api(), 1);
+
+ // Allocate space for v8::Arguments implicit values. Must be initialized
+ // before calling any runtime function.
+ __ sub(esp, Immediate(kFastApiCallArguments * kPointerSize));
+
+ // Check that the maps haven't changed and find a Holder as a side effect.
+ 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, kFastApiCallArguments * kPointerSize));
+ __ mov(Operand(esp, 0 * kPointerSize), eax);
+
+ // esp[2 * kPointerSize] is uninitialized, esp[3 * kPointerSize] contains
+ // duplicate of return address and will be overwritten.
+ GenerateFastApiCall(masm(), optimization, argc);
+
+ __ bind(&miss);
+ __ add(esp, Immediate(kFastApiCallArguments * kPointerSize));
+
+ __ bind(&miss_before_stack_reserved);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Label* success) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ if (check != NUMBER_CHECK) {
+ __ JumpIfSmi(edx, &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);
+ switch (check) {
+ case RECEIVER_MAP_CHECK:
+ __ IncrementCounter(isolate()->counters()->call_const(), 1);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(Handle<JSObject>::cast(object), edx, holder, ebx, eax,
+ edi, name, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
+ __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
+ }
+ break;
+
+ case STRING_CHECK:
+ // Check that the object is a string.
+ __ 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(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ eax, holder, ebx, edx, edi, name, &miss);
+ break;
+
+ case SYMBOL_CHECK:
+ // Check that the object is a symbol.
+ __ CmpObjectType(edx, SYMBOL_TYPE, eax);
+ __ j(not_equal, &miss);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::SYMBOL_FUNCTION_INDEX, eax, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ eax, holder, ebx, edx, edi, name, &miss);
+ break;
+
+ case NUMBER_CHECK: {
+ Label fast;
+ // Check that the object is a smi or a heap number.
+ __ JumpIfSmi(edx, &fast);
+ __ CmpObjectType(edx, HEAP_NUMBER_TYPE, eax);
+ __ j(not_equal, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::NUMBER_FUNCTION_INDEX, eax, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ eax, holder, ebx, edx, edi, name, &miss);
+ break;
+ }
+ case BOOLEAN_CHECK: {
+ Label fast;
+ // Check that the object is a boolean.
+ __ cmp(edx, factory()->true_value());
+ __ j(equal, &fast);
+ __ cmp(edx, factory()->false_value());
+ __ j(not_equal, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::BOOLEAN_FUNCTION_INDEX, eax, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ eax, holder, ebx, edx, edi, name, &miss);
+ break;
+ }
+ }
+
+ __ jmp(success);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+}
+
+
+void CallStubCompiler::CompileHandlerBackend(Handle<JSFunction> function) {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallConstant(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Handle<JSFunction> function) {
+
+ if (HasCustomCallGenerator(function)) {
+ Handle<Code> code = CompileCustomCall(object, holder,
+ Handle<Cell>::null(),
+ function, Handle<String>::cast(name),
+ Code::CONSTANT);
+ // A null handle means bail out to the regular compiler code below.
+ if (!code.is_null()) return code;
+ }
+
+ Label success;
+
+ CompileHandlerFrontend(object, holder, name, check, &success);
+ __ bind(&success);
+ CompileHandlerBackend(function);
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ 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_state_);
+ compiler.Compile(masm(), object, holder, name, &lookup, edx, ebx, edi, eax,
+ &miss);
+
+ // Restore receiver.
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the function really is a function.
+ __ JumpIfSmi(eax, &miss);
+ __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
+ __ j(not_equal, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
+ __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
+ }
+
+ // Invoke the function.
+ __ mov(edi, eax);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(edi, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
+
+ // Handle load cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::INTERCEPTOR, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallGlobal(
+ Handle<JSObject> object,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ Handle<JSFunction> function,
+ Handle<Name> name) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ if (HasCustomCallGenerator(function)) {
+ Handle<Code> code = CompileCustomCall(
+ object, holder, cell, function, Handle<String>::cast(name),
+ Code::NORMAL);
+ // 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 (object->IsGlobalObject()) {
+ __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
+ __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
+ }
+
+ // 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);
+ ParameterCount expected(function->shared()->formal_parameter_count());
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ // 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);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::NORMAL, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreCallback(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+
+ __ pop(scratch1()); // remove the return address
+ __ push(receiver());
+ __ Push(callback);
+ __ Push(name);
+ __ push(value());
+ __ push(scratch1()); // restore return address
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_callback_property =
+ ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate());
+ __ TailCallExternalReference(store_callback_property, 4, 1);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void StoreStubCompiler::GenerateStoreViaSetter(
+ MacroAssembler* masm,
+ Handle<JSFunction> setter) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Save value register, so we can restore it later.
+ __ push(eax);
+
+ if (!setter.is_null()) {
+ // Call the JavaScript setter with receiver and value on the stack.
+ __ push(edx);
+ __ push(eax);
+ ParameterCount actual(1);
+ ParameterCount expected(setter);
+ __ InvokeFunction(setter, expected, actual,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+ } else {
+ // If we generate a global code snippet for deoptimization only, remember
+ // the place to continue after deoptimization.
+ masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // We have to return the passed value, not the return value of the setter.
+ __ pop(eax);
+
+ // Restore context register.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ }
+ __ ret(0);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> StoreStubCompiler::CompileStoreInterceptor(
+ Handle<JSObject> object,
+ Handle<Name> name) {
+ __ pop(scratch1()); // remove the return address
+ __ push(receiver());
+ __ push(this->name());
+ __ push(value());
+ __ push(Immediate(Smi::FromInt(strict_mode())));
+ __ push(scratch1()); // restore return address
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_ic_property =
+ ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate());
+ __ TailCallExternalReference(store_ic_property, 4, 1);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::INTERCEPTOR, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreGlobal(
+ Handle<GlobalObject> object,
+ Handle<PropertyCell> cell,
+ Handle<Name> name) {
+ Label miss;
+
+ // Check that the map of the global has not changed.
+ __ cmp(FieldOperand(receiver(), HeapObject::kMapOffset),
+ Immediate(Handle<Map>(object->map())));
+ __ j(not_equal, &miss);
+
+ // Compute the cell operand to use.
+ __ mov(scratch1(), Immediate(cell));
+ Operand cell_operand =
+ FieldOperand(scratch1(), PropertyCell::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.
+ __ cmp(cell_operand, factory()->the_hole_value());
+ __ j(equal, &miss);
+
+ // Store the value in the cell.
+ __ mov(cell_operand, value());
+ // No write barrier here, because cells are always rescanned.
+
+ // Return the value (register eax).
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->named_store_global_inline(), 1);
+ __ ret(0);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ __ IncrementCounter(counters->named_store_global_inline_miss(), 1);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, name);
+}
+
+
+Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic(
+ MapHandleList* receiver_maps,
+ CodeHandleList* handler_stubs,
+ MapHandleList* transitioned_maps) {
+ Label miss;
+ __ JumpIfSmi(receiver(), &miss, Label::kNear);
+ __ mov(scratch1(), FieldOperand(receiver(), HeapObject::kMapOffset));
+ for (int i = 0; i < receiver_maps->length(); ++i) {
+ __ cmp(scratch1(), 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(transition_map(), Immediate(transitioned_maps->at(i)));
+ __ jmp(handler_stubs->at(i), RelocInfo::CODE_TARGET);
+ __ bind(&next_map);
+ }
+ }
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(
+ kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC);
+}
+
+
+Handle<Code> LoadStubCompiler::CompileLoadNonexistent(
+ Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Handle<GlobalObject> global) {
+ Label success;
+
+ NonexistentHandlerFrontend(object, last, name, &success, global);
+
+ __ bind(&success);
+ // Return undefined if maps of the full prototype chain are still the
+ // same and no global property with this name contains a value.
+ __ mov(eax, isolate()->factory()->undefined_value());
+ __ ret(0);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::NONEXISTENT, name);
+}
+
+
+Register* LoadStubCompiler::registers() {
+ // receiver, name, scratch1, scratch2, scratch3, scratch4.
+ static Register registers[] = { edx, ecx, ebx, eax, edi, no_reg };
+ return registers;
+}
+
+
+Register* KeyedLoadStubCompiler::registers() {
+ // receiver, name, scratch1, scratch2, scratch3, scratch4.
+ static Register registers[] = { edx, ecx, ebx, eax, edi, no_reg };
+ return registers;
+}
+
+
+Register* StoreStubCompiler::registers() {
+ // receiver, name, value, scratch1, scratch2, scratch3.
+ static Register registers[] = { edx, ecx, eax, ebx, edi, no_reg };
+ return registers;
+}
+
+
+Register* KeyedStoreStubCompiler::registers() {
+ // receiver, name, value, scratch1, scratch2, scratch3.
+ static Register registers[] = { edx, ecx, eax, ebx, edi, no_reg };
+ return registers;
+}
+
+
+void KeyedLoadStubCompiler::GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) {
+ __ cmp(name_reg, Immediate(name));
+ __ j(not_equal, miss);
+}
+
+
+void KeyedStoreStubCompiler::GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) {
+ __ cmp(name_reg, Immediate(name));
+ __ j(not_equal, miss);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm,
+ Handle<JSFunction> getter) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ if (!getter.is_null()) {
+ // Call the JavaScript getter with the receiver on the stack.
+ __ push(edx);
+ ParameterCount actual(0);
+ ParameterCount expected(getter);
+ __ InvokeFunction(getter, expected, actual,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+ } else {
+ // If we generate a global code snippet for deoptimization only, remember
+ // the place to continue after deoptimization.
+ masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // Restore context register.
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ }
+ __ ret(0);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> LoadStubCompiler::CompileLoadGlobal(
+ Handle<JSObject> object,
+ Handle<GlobalObject> global,
+ Handle<PropertyCell> cell,
+ Handle<Name> name,
+ bool is_dont_delete) {
+ Label success, miss;
+
+ __ CheckMap(receiver(), Handle<Map>(object->map()), &miss, DO_SMI_CHECK);
+ HandlerFrontendHeader(
+ object, receiver(), Handle<JSObject>::cast(global), name, &miss);
+ // Get the value from the cell.
+ if (Serializer::enabled()) {
+ __ mov(eax, Immediate(cell));
+ __ mov(eax, FieldOperand(eax, PropertyCell::kValueOffset));
+ } else {
+ __ mov(eax, Operand::ForCell(cell));
+ }
+
+ // Check for deleted property if property can actually be deleted.
+ if (!is_dont_delete) {
+ __ cmp(eax, factory()->the_hole_value());
+ __ j(equal, &miss);
+ } else if (FLAG_debug_code) {
+ __ cmp(eax, factory()->the_hole_value());
+ __ Check(not_equal, kDontDeleteCellsCannotContainTheHole);
+ }
+
+ HandlerFrontendFooter(name, &success, &miss);
+ __ bind(&success);
+
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->named_load_global_stub(), 1);
+ // The code above already loads the result into the return register.
+ __ ret(0);
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, name);
+}
+
+
+Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC(
+ MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ Handle<Name> name,
+ Code::StubType type,
+ IcCheckType check) {
+ Label miss;
+
+ if (check == PROPERTY) {
+ GenerateNameCheck(name, this->name(), &miss);
+ }
+
+ __ JumpIfSmi(receiver(), &miss);
+ Register map_reg = scratch1();
+ __ mov(map_reg, FieldOperand(receiver(), HeapObject::kMapOffset));
+ int receiver_count = receiver_maps->length();
+ int number_of_handled_maps = 0;
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map = receiver_maps->at(current);
+ if (!map->is_deprecated()) {
+ number_of_handled_maps++;
+ __ cmp(map_reg, map);
+ __ j(equal, handlers->at(current));
+ }
+ }
+ ASSERT(number_of_handled_maps != 0);
+
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ InlineCacheState state =
+ number_of_handled_maps > 1 ? POLYMORPHIC : MONOMORPHIC;
+ return GetICCode(kind(), type, name, state);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void KeyedLoadStubCompiler::GenerateLoadDictionaryElement(
+ MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label slow, miss_force_generic;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+ __ JumpIfNotSmi(ecx, &miss_force_generic);
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ __ mov(eax, FieldOperand(edx, JSObject::kElementsOffset));
+
+ // Push receiver on the stack to free up a register for the dictionary
+ // probing.
+ __ push(edx);
+ __ LoadFromNumberDictionary(&slow, eax, ecx, ebx, edx, edi, eax);
+ // Pop receiver before returning.
+ __ pop(edx);
+ __ ret(0);
+
+ __ bind(&slow);
+ __ pop(edx);
+
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow);
+
+ __ bind(&miss_force_generic);
+ // ----------- S t a t e -------------
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedLoadIC_MissForceGeneric);
+}
+
+
+static void GenerateSmiKeyCheck(MacroAssembler* masm,
+ Register key,
+ Register scratch,
+ XMMRegister xmm_scratch0,
+ XMMRegister xmm_scratch1,
+ Label* fail) {
+ // Check that key is a smi and if SSE2 is available a heap number
+ // containing a smi and branch if the check fails.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatureScope use_sse2(masm, SSE2);
+ Label key_ok;
+ __ JumpIfSmi(key, &key_ok);
+ __ cmp(FieldOperand(key, HeapObject::kMapOffset),
+ Immediate(Handle<Map>(masm->isolate()->heap()->heap_number_map())));
+ __ j(not_equal, fail);
+ __ movdbl(xmm_scratch0, FieldOperand(key, HeapNumber::kValueOffset));
+ __ cvttsd2si(scratch, Operand(xmm_scratch0));
+ __ cvtsi2sd(xmm_scratch1, scratch);
+ __ ucomisd(xmm_scratch1, xmm_scratch0);
+ __ j(not_equal, fail);
+ __ j(parity_even, fail); // NaN.
+ // Check if the key fits in the smi range.
+ __ cmp(scratch, 0xc0000000);
+ __ j(sign, fail);
+ __ SmiTag(scratch);
+ __ mov(key, scratch);
+ __ bind(&key_ok);
+ } else {
+ __ JumpIfNotSmi(key, fail);
+ }
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreExternalArray(
+ MacroAssembler* masm,
+ ElementsKind elements_kind) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic, slow, check_heap_number;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, ecx, ebx, xmm0, xmm1, &miss_force_generic);
+
+ // Check that the index is in range.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ cmp(ecx, FieldOperand(edi, ExternalArray::kLengthOffset));
+ // Unsigned comparison catches both negative and too-large values.
+ __ j(above_equal, &slow);
+
+ // Handle both smis and HeapNumbers in the fast path. Go to the
+ // runtime for all other kinds of values.
+ // eax: value
+ // edx: receiver
+ // ecx: key
+ // edi: elements array
+ if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) {
+ __ JumpIfNotSmi(eax, &slow);
+ } else {
+ __ JumpIfNotSmi(eax, &check_heap_number);
+ }
+
+ // smi case
+ __ mov(ebx, eax); // Preserve the value in eax as the return value.
+ __ SmiUntag(ebx);
+ __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
+ // edi: base pointer of external storage
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ __ ClampUint8(ebx);
+ __ SmiUntag(ecx);
+ __ mov_b(Operand(edi, ecx, times_1, 0), ebx);
+ break;
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ SmiUntag(ecx);
+ __ mov_b(Operand(edi, ecx, times_1, 0), ebx);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ mov_w(Operand(edi, ecx, times_1, 0), ebx);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ mov(Operand(edi, ecx, times_2, 0), ebx);
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ // Need to perform int-to-float conversion.
+ __ push(ebx);
+ __ fild_s(Operand(esp, 0));
+ __ pop(ebx);
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ __ fstp_s(Operand(edi, ecx, times_2, 0));
+ } else { // elements_kind == EXTERNAL_DOUBLE_ELEMENTS.
+ __ fstp_d(Operand(edi, ecx, times_4, 0));
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ __ ret(0); // Return the original value.
+
+ // TODO(danno): handle heap number -> pixel array conversion
+ if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) {
+ __ bind(&check_heap_number);
+ // eax: value
+ // edx: receiver
+ // ecx: key
+ // edi: elements array
+ __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->heap_number_map()));
+ __ j(not_equal, &slow);
+
+ // The WebGL specification leaves the behavior of storing NaN and
+ // +/-Infinity into integer arrays basically undefined. For more
+ // reproducible behavior, convert these to zero.
+ __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
+ // edi: base pointer of external storage
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ fstp_s(Operand(edi, ecx, times_2, 0));
+ __ ret(0);
+ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ fstp_d(Operand(edi, ecx, times_4, 0));
+ __ ret(0);
+ } else {
+ // Perform float-to-int conversion with truncation (round-to-zero)
+ // behavior.
+
+ // For the moment we make the slow call to the runtime on
+ // processors that don't support SSE2. The code in IntegerConvert
+ // (code-stubs-ia32.cc) is roughly what is needed here though the
+ // conversion failure case does not need to be handled.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ if ((elements_kind == EXTERNAL_INT_ELEMENTS ||
+ elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) &&
+ CpuFeatures::IsSupported(SSE3)) {
+ CpuFeatureScope scope(masm, SSE3);
+ // fisttp stores values as signed integers. To represent the
+ // entire range of int and unsigned int arrays, store as a
+ // 64-bit int and discard the high 32 bits.
+ __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ sub(esp, Immediate(2 * kPointerSize));
+ __ fisttp_d(Operand(esp, 0));
+
+ // If conversion failed (NaN, infinity, or a number outside
+ // signed int64 range), the result is 0x8000000000000000, and
+ // we must handle this case in the runtime.
+ Label ok;
+ __ cmp(Operand(esp, kPointerSize), Immediate(0x80000000u));
+ __ j(not_equal, &ok);
+ __ cmp(Operand(esp, 0), Immediate(0));
+ __ j(not_equal, &ok);
+ __ add(esp, Immediate(2 * kPointerSize)); // Restore the stack.
+ __ jmp(&slow);
+
+ __ bind(&ok);
+ __ pop(ebx);
+ __ add(esp, Immediate(kPointerSize));
+ __ mov(Operand(edi, ecx, times_2, 0), ebx);
+ } else {
+ ASSERT(CpuFeatures::IsSupported(SSE2));
+ CpuFeatureScope scope(masm, SSE2);
+ __ cvttsd2si(ebx, FieldOperand(eax, HeapNumber::kValueOffset));
+ __ cmp(ebx, 0x80000000u);
+ __ j(equal, &slow);
+ // ebx: untagged integer value
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ __ ClampUint8(ebx);
+ // Fall through.
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ SmiUntag(ecx);
+ __ mov_b(Operand(edi, ecx, times_1, 0), ebx);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ mov_w(Operand(edi, ecx, times_1, 0), ebx);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ mov(Operand(edi, ecx, times_2, 0), ebx);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ __ ret(0); // Return original value.
+ }
+ }
+ }
+
+ // Slow case: call runtime.
+ __ bind(&slow);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->keyed_store_external_array_slow(), 1);
+
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastElement(
+ MacroAssembler* masm,
+ bool is_js_array,
+ ElementsKind elements_kind,
+ KeyedAccessStoreMode store_mode) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic, grow, slow, transition_elements_kind;
+ Label check_capacity, prepare_slow, finish_store, commit_backing_store;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, ecx, ebx, xmm0, xmm1, &miss_force_generic);
+
+ if (IsFastSmiElementsKind(elements_kind)) {
+ __ JumpIfNotSmi(eax, &transition_elements_kind);
+ }
+
+ // Get the elements array and make sure it is a fast element array, not 'cow'.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ if (is_js_array) {
+ // Check that the key is within bounds.
+ __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // smis.
+ if (IsGrowStoreMode(store_mode)) {
+ __ j(above_equal, &grow);
+ } else {
+ __ j(above_equal, &miss_force_generic);
+ }
+ } else {
+ // Check that the key is within bounds.
+ __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); // smis.
+ __ j(above_equal, &miss_force_generic);
+ }
+
+ __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->fixed_array_map()));
+ __ j(not_equal, &miss_force_generic);
+
+ __ bind(&finish_store);
+ if (IsFastSmiElementsKind(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(IsFastObjectElementsKind(elements_kind));
+ // 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(ebx, eax);
+ __ RecordWrite(edi, ecx, ebx, kDontSaveFPRegs);
+ }
+
+ // Done.
+ __ ret(0);
+
+ // Handle store cache miss, replacing the ic with the generic stub.
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+
+ // Handle transition to other elements kinds without using the generic stub.
+ __ bind(&transition_elements_kind);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Miss);
+
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ // Handle transition requiring the array to grow.
+ __ bind(&grow);
+
+ // Make sure the array is only growing by a single element, anything else
+ // must be handled by the runtime. Flags are already set by previous
+ // compare.
+ __ j(not_equal, &miss_force_generic);
+
+ // Check for the empty array, and preallocate a small backing store if
+ // possible.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
+ __ j(not_equal, &check_capacity);
+
+ int size = FixedArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ Allocate(size, edi, ebx, ecx, &prepare_slow, TAG_OBJECT);
+ // Restore the key, which is known to be the array length.
+
+ // eax: value
+ // ecx: key
+ // edx: receiver
+ // edi: elements
+ // Make sure that the backing store can hold additional elements.
+ __ mov(FieldOperand(edi, JSObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->fixed_array_map()));
+ __ mov(FieldOperand(edi, FixedArray::kLengthOffset),
+ Immediate(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
+ __ mov(ebx, Immediate(masm->isolate()->factory()->the_hole_value()));
+ for (int i = 1; i < JSArray::kPreallocatedArrayElements; ++i) {
+ __ mov(FieldOperand(edi, FixedArray::SizeFor(i)), ebx);
+ }
+
+ // Store the element at index zero.
+ __ mov(FieldOperand(edi, FixedArray::SizeFor(0)), eax);
+
+ // Install the new backing store in the JSArray.
+ __ mov(FieldOperand(edx, JSObject::kElementsOffset), edi);
+ __ RecordWriteField(edx, JSObject::kElementsOffset, edi, ebx,
+ kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+
+ // Increment the length of the array.
+ __ mov(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ __ ret(0);
+
+ __ bind(&check_capacity);
+ __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->fixed_cow_array_map()));
+ __ j(equal, &miss_force_generic);
+
+ // eax: value
+ // ecx: key
+ // edx: receiver
+ // edi: elements
+ // Make sure that the backing store can hold additional elements.
+ __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
+ __ j(above_equal, &slow);
+
+ // Grow the array and finish the store.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ __ jmp(&finish_store);
+
+ __ bind(&prepare_slow);
+ // Restore the key, which is known to be the array length.
+ __ mov(ecx, Immediate(0));
+
+ __ bind(&slow);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+ }
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
+ MacroAssembler* masm,
+ bool is_js_array,
+ KeyedAccessStoreMode store_mode) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic, transition_elements_kind, grow, slow;
+ Label check_capacity, prepare_slow, finish_store, commit_backing_store;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, ecx, ebx, xmm0, xmm1, &miss_force_generic);
+
+ // Get the elements array.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ AssertFastElements(edi);
+
+ if (is_js_array) {
+ // Check that the key is within bounds.
+ __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // smis.
+ if (IsGrowStoreMode(store_mode)) {
+ __ j(above_equal, &grow);
+ } else {
+ __ j(above_equal, &miss_force_generic);
+ }
+ } else {
+ // Check that the key is within bounds.
+ __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); // smis.
+ __ j(above_equal, &miss_force_generic);
+ }
+
+ __ bind(&finish_store);
+ __ StoreNumberToDoubleElements(eax, edi, ecx, edx, xmm0,
+ &transition_elements_kind, true);
+ __ ret(0);
+
+ // Handle store cache miss, replacing the ic with the generic stub.
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+
+ // Handle transition to other elements kinds without using the generic stub.
+ __ bind(&transition_elements_kind);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Miss);
+
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ // Handle transition requiring the array to grow.
+ __ bind(&grow);
+
+ // Make sure the array is only growing by a single element, anything else
+ // must be handled by the runtime. Flags are already set by previous
+ // compare.
+ __ j(not_equal, &miss_force_generic);
+
+ // Transition on values that can't be stored in a FixedDoubleArray.
+ Label value_is_smi;
+ __ JumpIfSmi(eax, &value_is_smi);
+ __ cmp(FieldOperand(eax, HeapObject::kMapOffset),
+ Immediate(Handle<Map>(masm->isolate()->heap()->heap_number_map())));
+ __ j(not_equal, &transition_elements_kind);
+ __ bind(&value_is_smi);
+
+ // Check for the empty array, and preallocate a small backing store if
+ // possible.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
+ __ j(not_equal, &check_capacity);
+
+ int size = FixedDoubleArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ Allocate(size, edi, ebx, ecx, &prepare_slow, TAG_OBJECT);
+
+ // Restore the key, which is known to be the array length.
+ __ mov(ecx, Immediate(0));
+
+ // eax: value
+ // ecx: key
+ // edx: receiver
+ // edi: elements
+ // Initialize the new FixedDoubleArray.
+ __ mov(FieldOperand(edi, JSObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->fixed_double_array_map()));
+ __ mov(FieldOperand(edi, FixedDoubleArray::kLengthOffset),
+ Immediate(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
+
+ __ StoreNumberToDoubleElements(eax, edi, ecx, ebx, xmm0,
+ &transition_elements_kind, true);
+
+ for (int i = 1; i < JSArray::kPreallocatedArrayElements; i++) {
+ int offset = FixedDoubleArray::OffsetOfElementAt(i);
+ __ mov(FieldOperand(edi, offset), Immediate(kHoleNanLower32));
+ __ mov(FieldOperand(edi, offset + kPointerSize),
+ Immediate(kHoleNanUpper32));
+ }
+
+ // Install the new backing store in the JSArray.
+ __ mov(FieldOperand(edx, JSObject::kElementsOffset), edi);
+ __ RecordWriteField(edx, JSObject::kElementsOffset, edi, ebx,
+ kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+
+ // Increment the length of the array.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ ret(0);
+
+ __ bind(&check_capacity);
+ // eax: value
+ // ecx: key
+ // edx: receiver
+ // edi: elements
+ // Make sure that the backing store can hold additional elements.
+ __ cmp(ecx, FieldOperand(edi, FixedDoubleArray::kLengthOffset));
+ __ j(above_equal, &slow);
+
+ // Grow the array and finish the store.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(Smi::FromInt(1)));
+ __ jmp(&finish_store);
+
+ __ bind(&prepare_slow);
+ // Restore the key, which is known to be the array length.
+ __ mov(ecx, Immediate(0));
+
+ __ bind(&slow);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+ }
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_IA32
diff --git a/chromium/v8/src/ic-inl.h b/chromium/v8/src/ic-inl.h
new file mode 100644
index 00000000000..ca02183dbdb
--- /dev/null
+++ b/chromium/v8/src/ic-inl.h
@@ -0,0 +1,145 @@
+// Copyright 2012 the V8 project authors. 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_IC_INL_H_
+#define V8_IC_INL_H_
+
+#include "ic.h"
+
+#include "compiler.h"
+#include "debug.h"
+#include "macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+
+Address IC::address() const {
+ // Get the address of the call.
+ Address result = Assembler::target_address_from_return_address(pc());
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ ASSERT(Isolate::Current() == isolate());
+ Debug* debug = isolate()->debug();
+ // First check if any break points are active if not just return the address
+ // of the call.
+ if (!debug->has_break_points()) return result;
+
+ // At least one break point is active perform additional test to ensure that
+ // break point locations are updated correctly.
+ if (debug->IsDebugBreak(Assembler::target_address_at(result))) {
+ // If the call site is a call to debug break then return the address in
+ // the original code instead of the address in the running code. This will
+ // cause the original code to be updated and keeps the breakpoint active in
+ // the running code.
+ return OriginalCodeAddress();
+ } else {
+ // No break point here just return the address of the call.
+ return result;
+ }
+#else
+ return result;
+#endif
+}
+
+
+Code* IC::GetTargetAtAddress(Address address) {
+ // Get the target address of the IC.
+ Address target = Assembler::target_address_at(address);
+ // Convert target address to the code object. Code::GetCodeFromTargetAddress
+ // is safe for use during GC where the map might be marked.
+ Code* result = Code::GetCodeFromTargetAddress(target);
+ ASSERT(result->is_inline_cache_stub());
+ return result;
+}
+
+
+void IC::SetTargetAtAddress(Address address, Code* target) {
+ ASSERT(target->is_inline_cache_stub() || target->is_compare_ic_stub());
+ Heap* heap = target->GetHeap();
+ Code* old_target = GetTargetAtAddress(address);
+#ifdef DEBUG
+ // STORE_IC and KEYED_STORE_IC use Code::extra_ic_state() to mark
+ // ICs as strict mode. The strict-ness of the IC must be preserved.
+ if (old_target->kind() == Code::STORE_IC ||
+ old_target->kind() == Code::KEYED_STORE_IC) {
+ ASSERT(Code::GetStrictMode(old_target->extra_ic_state()) ==
+ Code::GetStrictMode(target->extra_ic_state()));
+ }
+#endif
+ Assembler::set_target_address_at(address, target->instruction_start());
+ if (heap->gc_state() == Heap::MARK_COMPACT) {
+ heap->mark_compact_collector()->RecordCodeTargetPatch(address, target);
+ } else {
+ heap->incremental_marking()->RecordCodeTargetPatch(address, target);
+ }
+ PostPatching(address, target, old_target);
+}
+
+
+InlineCacheHolderFlag IC::GetCodeCacheForObject(Object* object,
+ JSObject* holder) {
+ if (object->IsJSObject()) {
+ return GetCodeCacheForObject(JSObject::cast(object), holder);
+ }
+ // If the object is a value, we use the prototype map for the cache.
+ ASSERT(object->IsString() || object->IsSymbol() ||
+ object->IsNumber() || object->IsBoolean());
+ return PROTOTYPE_MAP;
+}
+
+
+InlineCacheHolderFlag IC::GetCodeCacheForObject(JSObject* object,
+ JSObject* holder) {
+ // Fast-properties and global objects store stubs in their own maps.
+ // Slow properties objects use prototype's map (unless the property is its own
+ // when holder == object). It works because slow properties objects having
+ // the same prototype (or a prototype with the same map) and not having
+ // the property are interchangeable for such a stub.
+ if (holder != object &&
+ !object->HasFastProperties() &&
+ !object->IsJSGlobalProxy() &&
+ !object->IsJSGlobalObject()) {
+ return PROTOTYPE_MAP;
+ }
+ return OWN_MAP;
+}
+
+
+JSObject* IC::GetCodeCacheHolder(Isolate* isolate,
+ Object* object,
+ InlineCacheHolderFlag holder) {
+ Object* map_owner =
+ holder == OWN_MAP ? object : object->GetPrototype(isolate);
+ ASSERT(map_owner->IsJSObject());
+ return JSObject::cast(map_owner);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_IC_INL_H_
diff --git a/chromium/v8/src/ic.cc b/chromium/v8/src/ic.cc
new file mode 100644
index 00000000000..3c22580c2c7
--- /dev/null
+++ b/chromium/v8/src/ic.cc
@@ -0,0 +1,3115 @@
+// Copyright 2012 the V8 project authors. 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 "accessors.h"
+#include "api.h"
+#include "arguments.h"
+#include "codegen.h"
+#include "execution.h"
+#include "ic-inl.h"
+#include "runtime.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef DEBUG
+char IC::TransitionMarkFromState(IC::State state) {
+ switch (state) {
+ case UNINITIALIZED: return '0';
+ case PREMONOMORPHIC: return '.';
+ case MONOMORPHIC: return '1';
+ case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
+ case POLYMORPHIC: return 'P';
+ case MEGAMORPHIC: return 'N';
+ case GENERIC: return 'G';
+
+ // We never see the debugger states here, because the state is
+ // computed from the original code - not the patched code. Let
+ // these cases fall through to the unreachable code below.
+ case DEBUG_STUB: break;
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+
+const char* GetTransitionMarkModifier(KeyedAccessStoreMode mode) {
+ if (mode == STORE_NO_TRANSITION_HANDLE_COW) return ".COW";
+ if (mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
+ return ".IGNORE_OOB";
+ }
+ if (IsGrowStoreMode(mode)) return ".GROW";
+ return "";
+}
+
+
+void IC::TraceIC(const char* type,
+ Handle<Object> name,
+ State old_state,
+ Code* new_target) {
+ if (FLAG_trace_ic) {
+ Object* undef = new_target->GetHeap()->undefined_value();
+ State new_state = StateFrom(new_target, undef, undef);
+ PrintF("[%s in ", type);
+ Isolate* isolate = new_target->GetIsolate();
+ StackFrameIterator it(isolate);
+ while (it.frame()->fp() != this->fp()) it.Advance();
+ StackFrame* raw_frame = it.frame();
+ if (raw_frame->is_internal()) {
+ Code* apply_builtin = isolate->builtins()->builtin(
+ Builtins::kFunctionApply);
+ if (raw_frame->unchecked_code() == apply_builtin) {
+ PrintF("apply from ");
+ it.Advance();
+ raw_frame = it.frame();
+ }
+ }
+ JavaScriptFrame::PrintTop(isolate, stdout, false, true);
+ Code::ExtraICState state = new_target->extra_ic_state();
+ const char* modifier =
+ GetTransitionMarkModifier(Code::GetKeyedAccessStoreMode(state));
+ PrintF(" (%c->%c%s)",
+ TransitionMarkFromState(old_state),
+ TransitionMarkFromState(new_state),
+ modifier);
+ name->Print();
+ PrintF("]\n");
+ }
+}
+
+#define TRACE_GENERIC_IC(isolate, type, reason) \
+ do { \
+ if (FLAG_trace_ic) { \
+ PrintF("[%s patching generic stub in ", type); \
+ JavaScriptFrame::PrintTop(isolate, stdout, false, true); \
+ PrintF(" (%s)]\n", reason); \
+ } \
+ } while (false)
+
+#else
+#define TRACE_GENERIC_IC(isolate, 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) {
+ // To improve the performance of the (much used) IC code, we unfold a few
+ // levels of the stack frame iteration code. This yields a ~35% speedup when
+ // running DeltaBlue and a ~25% speedup of gbemu with the '--nouse-ic' flag.
+ const Address entry =
+ Isolate::c_entry_fp(isolate->thread_local_top());
+ Address* pc_address =
+ reinterpret_cast<Address*>(entry + ExitFrameConstants::kCallerPCOffset);
+ Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
+ // If there's another JavaScript frame on the stack or a
+ // StubFailureTrampoline, we need to look one frame further down the stack to
+ // find the frame pointer and the return address stack slot.
+ if (depth == EXTRA_CALL_FRAME) {
+ const int kCallerPCOffset = StandardFrameConstants::kCallerPCOffset;
+ pc_address = reinterpret_cast<Address*>(fp + kCallerPCOffset);
+ fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset);
+ }
+#ifdef DEBUG
+ StackFrameIterator it(isolate);
+ for (int i = 0; i < depth + 1; i++) it.Advance();
+ StackFrame* frame = it.frame();
+ ASSERT(fp == frame->fp() && pc_address == frame->pc_address());
+#endif
+ fp_ = fp;
+ pc_address_ = StackFrame::ResolveReturnAddressLocation(pc_address);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+Address IC::OriginalCodeAddress() const {
+ HandleScope scope(isolate());
+ // Compute the JavaScript frame for the frame pointer of this IC
+ // structure. We need this to be able to find the function
+ // corresponding to the frame.
+ StackFrameIterator it(isolate());
+ while (it.frame()->fp() != this->fp()) it.Advance();
+ JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
+ // Find the function on the stack and both the active code for the
+ // function and the original code.
+ JSFunction* function = frame->function();
+ Handle<SharedFunctionInfo> shared(function->shared(), isolate());
+ Code* code = shared->code();
+ ASSERT(Debug::HasDebugInfo(shared));
+ Code* original_code = Debug::GetDebugInfo(shared)->original_code();
+ ASSERT(original_code->IsCode());
+ // Get the address of the call site in the active code. This is the
+ // place where the call to DebugBreakXXX is and where the IC
+ // normally would be.
+ Address addr = Assembler::target_address_from_return_address(pc());
+ // Return the address in the original code. This is the place where
+ // the call which has been overwritten by the DebugBreakXXX resides
+ // and the place where the inline cache system should look.
+ intptr_t delta =
+ original_code->instruction_start() - code->instruction_start();
+ return addr + delta;
+}
+#endif
+
+
+static bool TryRemoveInvalidPrototypeDependentStub(Code* target,
+ Object* receiver,
+ Object* name) {
+ if (target->is_keyed_load_stub() ||
+ target->is_keyed_call_stub() ||
+ target->is_keyed_store_stub()) {
+ // Determine whether the failure is due to a name failure.
+ if (!name->IsName()) return false;
+ Name* stub_name = target->FindFirstName();
+ if (Name::cast(name) != stub_name) return false;
+ }
+
+ InlineCacheHolderFlag cache_holder =
+ Code::ExtractCacheHolderFromFlags(target->flags());
+
+ Isolate* isolate = target->GetIsolate();
+ if (cache_holder == OWN_MAP && !receiver->IsJSObject()) {
+ // The stub was generated for JSObject but called for non-JSObject.
+ // IC::GetCodeCacheHolder is not applicable.
+ return false;
+ } else if (cache_holder == PROTOTYPE_MAP &&
+ receiver->GetPrototype(isolate)->IsNull()) {
+ // IC::GetCodeCacheHolder is not applicable.
+ return false;
+ }
+ Map* map = IC::GetCodeCacheHolder(isolate, receiver, cache_holder)->map();
+
+ // Decide whether the inline cache failed because of changes to the
+ // receiver itself or changes to one of its prototypes.
+ //
+ // If there are changes to the receiver itself, the map of the
+ // receiver will have changed and the current target will not be in
+ // the receiver map's code cache. Therefore, if the current target
+ // is in the receiver map's code cache, the inline cache failed due
+ // to prototype check failure.
+ int index = map->IndexInCodeCache(name, target);
+ if (index >= 0) {
+ map->RemoveFromCodeCache(String::cast(name), target, index);
+ // For loads and stores, handlers are stored in addition to the ICs on the
+ // map. Remove those, too.
+ if ((target->is_load_stub() || target->is_keyed_load_stub() ||
+ target->is_store_stub() || target->is_keyed_store_stub()) &&
+ target->type() != Code::NORMAL) {
+ Code* handler = target->FindFirstCode();
+ index = map->IndexInCodeCache(name, handler);
+ if (index >= 0) {
+ map->RemoveFromCodeCache(String::cast(name), handler, index);
+ }
+ }
+ return true;
+ }
+
+ // The stub is not in the cache. We've ruled out all other kinds of failure
+ // except for proptotype chain changes, a deprecated map, a map that's
+ // different from the one that the stub expects, elements kind changes, or a
+ // constant global property that will become mutable. Threat all those
+ // situations as prototype failures (stay monomorphic if possible).
+
+ // If the IC is shared between multiple receivers (slow dictionary mode), then
+ // the map cannot be deprecated and the stub invalidated.
+ if (cache_holder == OWN_MAP) {
+ Map* old_map = target->FindFirstMap();
+ if (old_map == map) return true;
+ if (old_map != NULL) {
+ if (old_map->is_deprecated()) return true;
+ if (IsMoreGeneralElementsKindTransition(old_map->elements_kind(),
+ map->elements_kind())) {
+ return true;
+ }
+ }
+ }
+
+ if (receiver->IsGlobalObject()) {
+ if (!name->IsName()) return false;
+ Isolate* isolate = target->GetIsolate();
+ LookupResult lookup(isolate);
+ GlobalObject* global = GlobalObject::cast(receiver);
+ global->LocalLookupRealNamedProperty(Name::cast(name), &lookup);
+ if (!lookup.IsFound()) return false;
+ PropertyCell* cell = global->GetPropertyCell(&lookup);
+ return cell->type()->IsConstant();
+ }
+
+ return false;
+}
+
+
+IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
+ IC::State state = target->ic_state();
+
+ if (state != MONOMORPHIC || !name->IsString()) return state;
+ if (receiver->IsUndefined() || receiver->IsNull()) return state;
+
+ Code::Kind kind = target->kind();
+ // Remove the target from the code cache if it became invalid
+ // because of changes in the prototype chain to avoid hitting it
+ // again.
+ // Call stubs handle this later to allow extra IC state
+ // transitions.
+ if (kind != Code::CALL_IC && kind != Code::KEYED_CALL_IC &&
+ TryRemoveInvalidPrototypeDependentStub(target, receiver, name)) {
+ return MONOMORPHIC_PROTOTYPE_FAILURE;
+ }
+
+ // The builtins object is special. It only changes when JavaScript
+ // builtins are loaded lazily. It is important to keep inline
+ // caches for the builtins object monomorphic. Therefore, if we get
+ // an inline cache miss for the builtins object after lazily loading
+ // JavaScript builtins, we return uninitialized as the state to
+ // force the inline cache back to monomorphic state.
+ if (receiver->IsJSBuiltinsObject()) {
+ return UNINITIALIZED;
+ }
+
+ return MONOMORPHIC;
+}
+
+
+RelocInfo::Mode IC::ComputeMode() {
+ Address addr = address();
+ Code* code = Code::cast(isolate()->FindCodeObject(addr));
+ for (RelocIterator it(code, RelocInfo::kCodeTargetMask);
+ !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ if (info->pc() == addr) return info->rmode();
+ }
+ UNREACHABLE();
+ return RelocInfo::NONE32;
+}
+
+
+Failure* IC::TypeError(const char* type,
+ Handle<Object> object,
+ Handle<Object> key) {
+ HandleScope scope(isolate());
+ Handle<Object> args[2] = { key, object };
+ Handle<Object> error = isolate()->factory()->NewTypeError(
+ type, HandleVector(args, 2));
+ return isolate()->Throw(*error);
+}
+
+
+Failure* IC::ReferenceError(const char* type, Handle<String> name) {
+ HandleScope scope(isolate());
+ Handle<Object> error = isolate()->factory()->NewReferenceError(
+ type, HandleVector(&name, 1));
+ return isolate()->Throw(*error);
+}
+
+
+static int ComputeTypeInfoCountDelta(IC::State old_state, IC::State new_state) {
+ bool was_uninitialized =
+ old_state == UNINITIALIZED || old_state == PREMONOMORPHIC;
+ bool is_uninitialized =
+ new_state == UNINITIALIZED || new_state == PREMONOMORPHIC;
+ return (was_uninitialized && !is_uninitialized) ? 1 :
+ (!was_uninitialized && is_uninitialized) ? -1 : 0;
+}
+
+
+void IC::PostPatching(Address address, Code* target, Code* old_target) {
+ if (FLAG_type_info_threshold == 0 && !FLAG_watch_ic_patching) {
+ return;
+ }
+ Isolate* isolate = target->GetHeap()->isolate();
+ Code* host = isolate->
+ inner_pointer_to_code_cache()->GetCacheEntry(address)->code;
+ if (host->kind() != Code::FUNCTION) return;
+
+ if (FLAG_type_info_threshold > 0 &&
+ old_target->is_inline_cache_stub() &&
+ target->is_inline_cache_stub()) {
+ int delta = ComputeTypeInfoCountDelta(old_target->ic_state(),
+ target->ic_state());
+ // Not all Code objects have TypeFeedbackInfo.
+ if (host->type_feedback_info()->IsTypeFeedbackInfo() && delta != 0) {
+ TypeFeedbackInfo* info =
+ TypeFeedbackInfo::cast(host->type_feedback_info());
+ info->change_ic_with_type_info_count(delta);
+ }
+ }
+ if (host->type_feedback_info()->IsTypeFeedbackInfo()) {
+ TypeFeedbackInfo* info =
+ TypeFeedbackInfo::cast(host->type_feedback_info());
+ info->change_own_type_change_checksum();
+ }
+ if (FLAG_watch_ic_patching) {
+ host->set_profiler_ticks(0);
+ isolate->runtime_profiler()->NotifyICChanged();
+ }
+ // TODO(2029): When an optimized function is patched, it would
+ // be nice to propagate the corresponding type information to its
+ // unoptimized version for the benefit of later inlining.
+}
+
+
+void IC::Clear(Address address) {
+ Code* target = GetTargetAtAddress(address);
+
+ // Don't clear debug break inline cache as it will remove the break point.
+ if (target->is_debug_break()) return;
+
+ switch (target->kind()) {
+ case Code::LOAD_IC: return LoadIC::Clear(address, target);
+ case Code::KEYED_LOAD_IC: return KeyedLoadIC::Clear(address, target);
+ case Code::STORE_IC: return StoreIC::Clear(address, target);
+ case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target);
+ case Code::CALL_IC: return CallIC::Clear(address, target);
+ case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
+ case Code::COMPARE_IC: return CompareIC::Clear(address, target);
+ case Code::COMPARE_NIL_IC: return CompareNilIC::Clear(address, target);
+ case Code::BINARY_OP_IC:
+ case Code::TO_BOOLEAN_IC:
+ // Clearing these is tricky and does not
+ // make any performance difference.
+ return;
+ default: UNREACHABLE();
+ }
+}
+
+
+void CallICBase::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ bool contextual = CallICBase::Contextual::decode(target->extra_ic_state());
+ Code* code =
+ Isolate::Current()->stub_cache()->FindCallInitialize(
+ target->arguments_count(),
+ contextual ? RelocInfo::CODE_TARGET_CONTEXT : RelocInfo::CODE_TARGET,
+ target->kind());
+ SetTargetAtAddress(address, code);
+}
+
+
+void KeyedLoadIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ // Make sure to also clear the map used in inline fast cases. If we
+ // do not clear these maps, cached code can keep objects alive
+ // through the embedded maps.
+ SetTargetAtAddress(address, *initialize_stub());
+}
+
+
+void LoadIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ SetTargetAtAddress(address, *initialize_stub());
+}
+
+
+void StoreIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ SetTargetAtAddress(address,
+ (Code::GetStrictMode(target->extra_ic_state()) == kStrictMode)
+ ? *initialize_stub_strict()
+ : *initialize_stub());
+}
+
+
+void KeyedStoreIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ SetTargetAtAddress(address,
+ (Code::GetStrictMode(target->extra_ic_state()) == kStrictMode)
+ ? *initialize_stub_strict()
+ : *initialize_stub());
+}
+
+
+void CompareIC::Clear(Address address, Code* target) {
+ ASSERT(target->major_key() == CodeStub::CompareIC);
+ CompareIC::State handler_state;
+ Token::Value op;
+ ICCompareStub::DecodeMinorKey(target->stub_info(), NULL, NULL,
+ &handler_state, &op);
+ // Only clear CompareICs that can retain objects.
+ if (handler_state != KNOWN_OBJECT) return;
+ SetTargetAtAddress(address, GetRawUninitialized(op));
+ PatchInlinedSmiCode(address, DISABLE_INLINED_SMI_CHECK);
+}
+
+
+static bool HasInterceptorGetter(JSObject* object) {
+ return !object->GetNamedInterceptor()->getter()->IsUndefined();
+}
+
+
+static void LookupForRead(Handle<Object> object,
+ Handle<String> name,
+ LookupResult* lookup) {
+ // Skip all the objects with named interceptors, but
+ // without actual getter.
+ while (true) {
+ 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.
+ if (!lookup->IsInterceptor() || !lookup->IsCacheable()) {
+ return;
+ }
+
+ Handle<JSObject> holder(lookup->holder(), lookup->isolate());
+ if (HasInterceptorGetter(*holder)) {
+ return;
+ }
+
+ holder->LocalLookupRealNamedProperty(*name, lookup);
+ if (lookup->IsFound()) {
+ ASSERT(!lookup->IsInterceptor());
+ return;
+ }
+
+ Handle<Object> proto(holder->GetPrototype(), lookup->isolate());
+ if (proto->IsNull()) {
+ ASSERT(!lookup->IsFound());
+ return;
+ }
+
+ object = proto;
+ }
+}
+
+
+Handle<Object> CallICBase::TryCallAsFunction(Handle<Object> object) {
+ Handle<Object> delegate = Execution::GetFunctionDelegate(object);
+
+ 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 = target()->arguments_count();
+ StackFrameLocator locator(isolate());
+ JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
+ int index = frame->ComputeExpressionsCount() - (argc + 1);
+ frame->SetExpression(index, *object);
+ }
+
+ return delegate;
+}
+
+
+void CallICBase::ReceiverToObjectIfRequired(Handle<Object> callee,
+ Handle<Object> object) {
+ while (callee->IsJSFunctionProxy()) {
+ callee = Handle<Object>(JSFunctionProxy::cast(*callee)->call_trap(),
+ isolate());
+ }
+
+ if (callee->IsJSFunction()) {
+ Handle<JSFunction> function = Handle<JSFunction>::cast(callee);
+ if (!function->shared()->is_classic_mode() || function->IsBuiltin()) {
+ // Do not wrap receiver for strict mode functions or for builtins.
+ return;
+ }
+ }
+
+ // And only wrap string, number or boolean.
+ if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
+ // Change the receiver to the result of calling ToObject on it.
+ const int argc = this->target()->arguments_count();
+ StackFrameLocator locator(isolate());
+ JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
+ int index = frame->ComputeExpressionsCount() - (argc + 1);
+ frame->SetExpression(index, *isolate()->factory()->ToObject(object));
+ }
+}
+
+
+MaybeObject* CallICBase::LoadFunction(State state,
+ Code::ExtraICState extra_ic_state,
+ Handle<Object> object,
+ Handle<String> name) {
+ if (object->IsJSObject()) {
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (receiver->map()->is_deprecated()) {
+ JSObject::MigrateInstance(receiver);
+ }
+ }
+
+ // If the object is undefined or null it's illegal to try to get any
+ // of its properties; throw a TypeError in that case.
+ if (object->IsUndefined() || object->IsNull()) {
+ return TypeError("non_object_property_call", object, name);
+ }
+
+ // Check if the name is trivially convertible to an index and get
+ // the element if so.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ 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;
+
+ // Otherwise, it will fail in the lookup step.
+ }
+
+ // Lookup the property in the object.
+ LookupResult lookup(isolate());
+ LookupForRead(object, name, &lookup);
+
+ if (!lookup.IsFound()) {
+ // If the object does not have the requested property, check which
+ // exception we need to throw.
+ return IsUndeclaredGlobal(object)
+ ? ReferenceError("not_defined", name)
+ : TypeError("undefined_method", object, name);
+ }
+
+ // Lookup is valid: Update inline cache and stub cache.
+ if (FLAG_use_ic) {
+ UpdateCaches(&lookup, state, extra_ic_state, object, name);
+ }
+
+ // Get the property.
+ PropertyAttributes attr;
+ Handle<Object> result =
+ Object::GetProperty(object, object, &lookup, name, &attr);
+ RETURN_IF_EMPTY_HANDLE(isolate(), result);
+
+ if (lookup.IsInterceptor() && attr == ABSENT) {
+ // If the object does not have the requested property, check which
+ // exception we need to throw.
+ return IsUndeclaredGlobal(object)
+ ? ReferenceError("not_defined", name)
+ : TypeError("undefined_method", object, name);
+ }
+
+ ASSERT(!result->IsTheHole());
+
+ // 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, object);
+
+ 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.
+ debug->HandleStepIn(function, object, fp(), false);
+ }
+#endif
+ return *function;
+ }
+
+ // Try to find a suitable function delegate for the object at hand.
+ result = TryCallAsFunction(result);
+ if (result->IsJSFunction()) return *result;
+
+ return TypeError("property_not_function", object, name);
+}
+
+
+bool CallICBase::TryUpdateExtraICState(LookupResult* lookup,
+ Handle<Object> object,
+ Code::ExtraICState* extra_ic_state) {
+ ASSERT(kind_ == Code::CALL_IC);
+ if (!lookup->IsConstantFunction()) return false;
+ JSFunction* function = lookup->GetConstantFunction();
+ if (!function->shared()->HasBuiltinFunctionId()) return false;
+
+ // Fetch the arguments passed to the called function.
+ const int argc = target()->arguments_count();
+ Address entry = isolate()->c_entry_fp(isolate()->thread_local_top());
+ Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
+ Arguments args(argc + 1,
+ &Memory::Object_at(fp +
+ StandardFrameConstants::kCallerSPOffset +
+ argc * kPointerSize));
+ switch (function->shared()->builtin_function_id()) {
+ case kStringCharCodeAt:
+ case kStringCharAt:
+ if (object->IsString()) {
+ String* string = String::cast(*object);
+ // Check there's the right string value or wrapper in the receiver slot.
+ ASSERT(string == args[0] || string == JSValue::cast(args[0])->value());
+ // If we're in the default (fastest) state and the index is
+ // out of bounds, update the state to record this fact.
+ if (StringStubState::decode(*extra_ic_state) == DEFAULT_STRING_STUB &&
+ argc >= 1 && args[1]->IsNumber()) {
+ double index = DoubleToInteger(args.number_at(1));
+ if (index < 0 || index >= string->length()) {
+ *extra_ic_state =
+ StringStubState::update(*extra_ic_state,
+ STRING_INDEX_OUT_OF_BOUNDS);
+ return true;
+ }
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+ return false;
+}
+
+
+Handle<Code> CallICBase::ComputeMonomorphicStub(LookupResult* lookup,
+ State state,
+ Code::ExtraICState extra_state,
+ Handle<Object> object,
+ Handle<String> name) {
+ int argc = target()->arguments_count();
+ Handle<JSObject> holder(lookup->holder(), isolate());
+ switch (lookup->type()) {
+ case FIELD: {
+ PropertyIndex index = lookup->GetFieldIndex();
+ return isolate()->stub_cache()->ComputeCallField(
+ argc, kind_, extra_state, name, object, holder, index);
+ }
+ case CONSTANT: {
+ if (!lookup->IsConstantFunction()) return Handle<Code>::null();
+ // 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.
+ Handle<JSFunction> function(lookup->GetConstantFunction(), isolate());
+ return isolate()->stub_cache()->ComputeCallConstant(
+ argc, kind_, extra_state, name, object, holder, function);
+ }
+ case NORMAL: {
+ // 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 (holder->IsGlobalObject()) {
+ Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
+ Handle<PropertyCell> cell(
+ global->GetPropertyCell(lookup), isolate());
+ 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 (!holder.is_identical_to(receiver)) return Handle<Code>::null();
+ return isolate()->stub_cache()->ComputeCallNormal(
+ argc, kind_, extra_state);
+ }
+ break;
+ }
+ case INTERCEPTOR:
+ ASSERT(HasInterceptorGetter(*holder));
+ return isolate()->stub_cache()->ComputeCallInterceptor(
+ argc, kind_, extra_state, name, object, holder);
+ default:
+ return Handle<Code>::null();
+ }
+}
+
+
+void CallICBase::UpdateCaches(LookupResult* lookup,
+ State state,
+ Code::ExtraICState extra_ic_state,
+ Handle<Object> object,
+ Handle<String> name) {
+ // Bail out if we didn't find a result.
+ if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
+
+ // Compute the number of arguments.
+ int argc = target()->arguments_count();
+ 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.
+ 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)) {
+ code = ComputeMonomorphicStub(lookup, state, extra_ic_state,
+ object, name);
+ } else if (TryRemoveInvalidPrototypeDependentStub(target(),
+ *object,
+ *name)) {
+ state = MONOMORPHIC_PROTOTYPE_FAILURE;
+ code = ComputeMonomorphicStub(lookup, state, extra_ic_state,
+ object, name);
+ } else {
+ code = isolate()->stub_cache()->ComputeCallMegamorphic(
+ argc, kind_, extra_ic_state);
+ }
+ } else {
+ code = ComputeMonomorphicStub(lookup, state, extra_ic_state,
+ object, name);
+ }
+
+ // 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.
+ switch (state) {
+ case UNINITIALIZED:
+ case MONOMORPHIC_PROTOTYPE_FAILURE:
+ case PREMONOMORPHIC:
+ case MONOMORPHIC:
+ set_target(*code);
+ break;
+ case MEGAMORPHIC: {
+ // Cache code holding map should be consistent with
+ // GenerateMonomorphicCacheProbe. It is not the map which holds the stub.
+ Handle<JSObject> cache_object = object->IsJSObject()
+ ? Handle<JSObject>::cast(object)
+ : Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate())),
+ isolate());
+ // Update the stub cache.
+ UpdateMegamorphicCache(cache_object->map(), *name, *code);
+ break;
+ }
+ case DEBUG_STUB:
+ break;
+ case POLYMORPHIC:
+ case GENERIC:
+ UNREACHABLE();
+ break;
+ }
+
+ TRACE_IC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
+ name, state, target());
+}
+
+
+MaybeObject* KeyedCallIC::LoadFunction(State state,
+ Handle<Object> object,
+ Handle<Object> key) {
+ if (key->IsInternalizedString()) {
+ return CallICBase::LoadFunction(state,
+ Code::kNoExtraICState,
+ object,
+ Handle<String>::cast(key));
+ }
+
+ if (object->IsJSObject()) {
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (receiver->map()->is_deprecated()) {
+ JSObject::MigrateInstance(receiver);
+ }
+ }
+
+ if (object->IsUndefined() || object->IsNull()) {
+ return TypeError("non_object_property_call", object, key);
+ }
+
+ bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
+ ASSERT(!(use_ic && object->IsJSGlobalProxy()));
+
+ if (use_ic && state != MEGAMORPHIC) {
+ int argc = target()->arguments_count();
+ Handle<Code> stub = isolate()->stub_cache()->ComputeCallMegamorphic(
+ argc, Code::KEYED_CALL_IC, Code::kNoExtraICState);
+ if (object->IsJSObject()) {
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (receiver->elements()->map() ==
+ isolate()->heap()->non_strict_arguments_elements_map()) {
+ stub = isolate()->stub_cache()->ComputeCallArguments(argc);
+ }
+ }
+ ASSERT(!stub.is_null());
+ set_target(*stub);
+ TRACE_IC("KeyedCallIC", key, state, target());
+ }
+
+ Handle<Object> result = GetProperty(isolate(), object, key);
+ RETURN_IF_EMPTY_HANDLE(isolate(), 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, object);
+ if (result->IsJSFunction()) return *result;
+
+ result = TryCallAsFunction(result);
+ if (result->IsJSFunction()) return *result;
+
+ return TypeError("property_not_function", object, key);
+}
+
+
+MaybeObject* LoadIC::Load(State state,
+ Handle<Object> object,
+ Handle<String> name) {
+ // If the object is undefined or null it's illegal to try to get any
+ // of its properties; throw a TypeError in that case.
+ if (object->IsUndefined() || object->IsNull()) {
+ return TypeError("non_object_property_load", object, name);
+ }
+
+ if (FLAG_use_ic) {
+ // Use specialized code for getting the length of strings and
+ // string wrapper objects. The length property of string wrapper
+ // objects is read-only and therefore always returns the length of
+ // the underlying string value. See ECMA-262 15.5.5.1.
+ if ((object->IsString() || object->IsStringWrapper()) &&
+ name->Equals(isolate()->heap()->length_string())) {
+ Handle<Code> stub;
+ if (state == UNINITIALIZED) {
+ stub = pre_monomorphic_stub();
+ } else if (state == PREMONOMORPHIC) {
+ StringLengthStub string_length_stub(kind(), !object->IsString());
+ stub = string_length_stub.GetCode(isolate());
+ } else if (state == MONOMORPHIC && object->IsStringWrapper()) {
+ StringLengthStub string_length_stub(kind(), true);
+ stub = string_length_stub.GetCode(isolate());
+ } else if (state != MEGAMORPHIC) {
+ ASSERT(state != GENERIC);
+ stub = megamorphic_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.
+ Handle<Object> string = object->IsJSValue()
+ ? Handle<Object>(Handle<JSValue>::cast(object)->value(), isolate())
+ : object;
+ return Smi::FromInt(String::cast(*string)->length());
+ }
+
+ // Use specialized code for getting prototype of functions.
+ if (object->IsJSFunction() &&
+ name->Equals(isolate()->heap()->prototype_string()) &&
+ Handle<JSFunction>::cast(object)->should_have_prototype()) {
+ Handle<Code> stub;
+ if (state == UNINITIALIZED) {
+ stub = pre_monomorphic_stub();
+ } else if (state == PREMONOMORPHIC) {
+ FunctionPrototypeStub function_prototype_stub(kind());
+ stub = function_prototype_stub.GetCode(isolate());
+ } else if (state != MEGAMORPHIC) {
+ ASSERT(state != GENERIC);
+ stub = megamorphic_stub();
+ }
+ if (!stub.is_null()) {
+ set_target(*stub);
+#ifdef DEBUG
+ if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
+#endif
+ }
+ return *Accessors::FunctionGetPrototype(object);
+ }
+ }
+
+ // Check if the name is trivially convertible to an index and get
+ // the element or char if so.
+ uint32_t index;
+ if (kind() == Code::KEYED_LOAD_IC && name->AsArrayIndex(&index)) {
+ // Rewrite to the generic keyed load stub.
+ if (FLAG_use_ic) set_target(*generic_stub());
+ return Runtime::GetElementOrCharAtOrFail(isolate(), object, index);
+ }
+
+ if (object->IsJSObject()) {
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (receiver->map()->is_deprecated()) {
+ JSObject::MigrateInstance(receiver);
+ }
+ }
+
+ // Named lookup in the object.
+ LookupResult lookup(isolate());
+ LookupForRead(object, name, &lookup);
+
+ // If we did not find a property, check if we need to throw an exception.
+ if (!lookup.IsFound()) {
+ if (IsUndeclaredGlobal(object)) {
+ return ReferenceError("not_defined", name);
+ }
+ LOG(isolate(), SuspectReadEvent(*name, *object));
+ }
+
+ // Update inline cache and stub cache.
+ if (FLAG_use_ic) UpdateCaches(&lookup, state, object, name);
+
+ PropertyAttributes attr;
+ if (lookup.IsInterceptor() || lookup.IsHandler()) {
+ // Get the property.
+ 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 && IsUndeclaredGlobal(object)) {
+ return ReferenceError("not_defined", name);
+ }
+ return *result;
+ }
+
+ // Get the property.
+ return Object::GetPropertyOrFail(object, object, &lookup, name, &attr);
+}
+
+
+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).is_null() &&
+ receiver_maps->at(current).is_identical_to(new_receiver_map)) {
+ return false;
+ }
+ }
+ receiver_maps->Add(new_receiver_map);
+ return true;
+}
+
+
+bool IC::UpdatePolymorphicIC(State state,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Code> code,
+ StrictModeFlag strict_mode) {
+ if (code->type() == Code::NORMAL) return false;
+ if (target()->ic_state() == MONOMORPHIC &&
+ target()->type() == Code::NORMAL) {
+ return false;
+ }
+
+ MapHandleList receiver_maps;
+ CodeHandleList handlers;
+
+ int number_of_valid_maps;
+ int handler_to_overwrite = -1;
+ Handle<Map> new_receiver_map(receiver->map());
+ {
+ DisallowHeapAllocation no_gc;
+ target()->FindAllMaps(&receiver_maps);
+ int number_of_maps = receiver_maps.length();
+ number_of_valid_maps = number_of_maps;
+
+ for (int i = 0; i < number_of_maps; i++) {
+ Handle<Map> map = receiver_maps.at(i);
+ // Filter out deprecated maps to ensure its instances get migrated.
+ if (map->is_deprecated()) {
+ number_of_valid_maps--;
+ // If the receiver map is already in the polymorphic IC, this indicates
+ // there was a prototoype chain failure. In that case, just overwrite the
+ // handler.
+ } else if (map.is_identical_to(new_receiver_map)) {
+ number_of_valid_maps--;
+ handler_to_overwrite = i;
+ }
+ }
+
+ if (number_of_valid_maps >= 4) return false;
+
+ // Only allow 0 maps in case target() was reset to UNINITIALIZED by the GC.
+ // In that case, allow the IC to go back monomorphic.
+ if (number_of_maps == 0 && target()->ic_state() != UNINITIALIZED) {
+ return false;
+ }
+ target()->FindAllCode(&handlers, receiver_maps.length());
+ }
+
+ number_of_valid_maps++;
+ if (handler_to_overwrite >= 0) {
+ handlers.Set(handler_to_overwrite, code);
+ } else {
+ receiver_maps.Add(new_receiver_map);
+ handlers.Add(code);
+ }
+
+ Handle<Code> ic = ComputePolymorphicIC(
+ &receiver_maps, &handlers, number_of_valid_maps, name, strict_mode);
+ set_target(*ic);
+ return true;
+}
+
+
+Handle<Code> LoadIC::ComputePolymorphicIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name,
+ StrictModeFlag strict_mode) {
+ return isolate()->stub_cache()->ComputePolymorphicLoadIC(
+ receiver_maps, handlers, number_of_valid_maps, name);
+}
+
+
+Handle<Code> StoreIC::ComputePolymorphicIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name,
+ StrictModeFlag strict_mode) {
+ return isolate()->stub_cache()->ComputePolymorphicStoreIC(
+ receiver_maps, handlers, number_of_valid_maps, name, strict_mode);
+}
+
+
+void LoadIC::UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode) {
+ if (handler->type() == Code::NORMAL) return set_target(*handler);
+ Handle<Code> ic = isolate()->stub_cache()->ComputeMonomorphicLoadIC(
+ receiver, handler, name);
+ set_target(*ic);
+}
+
+
+void KeyedLoadIC::UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode) {
+ if (handler->type() == Code::NORMAL) return set_target(*handler);
+ Handle<Code> ic = isolate()->stub_cache()->ComputeMonomorphicKeyedLoadIC(
+ receiver, handler, name);
+ set_target(*ic);
+}
+
+
+void StoreIC::UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode) {
+ if (handler->type() == Code::NORMAL) return set_target(*handler);
+ Handle<Code> ic = isolate()->stub_cache()->ComputeMonomorphicStoreIC(
+ receiver, handler, name, strict_mode);
+ set_target(*ic);
+}
+
+
+void KeyedStoreIC::UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode) {
+ if (handler->type() == Code::NORMAL) return set_target(*handler);
+ Handle<Code> ic = isolate()->stub_cache()->ComputeMonomorphicKeyedStoreIC(
+ receiver, handler, name, strict_mode);
+ set_target(*ic);
+}
+
+
+void IC::CopyICToMegamorphicCache(Handle<String> name) {
+ MapHandleList receiver_maps;
+ CodeHandleList handlers;
+ {
+ DisallowHeapAllocation no_gc;
+ target()->FindAllMaps(&receiver_maps);
+ target()->FindAllCode(&handlers, receiver_maps.length());
+ }
+ for (int i = 0; i < receiver_maps.length(); i++) {
+ UpdateMegamorphicCache(*receiver_maps.at(i), *name, *handlers.at(i));
+ }
+}
+
+
+bool IC::IsTransitionedMapOfMonomorphicTarget(Map* receiver_map) {
+ DisallowHeapAllocation no_allocation;
+
+ Map* current_map = target()->FindFirstMap();
+ ElementsKind receiver_elements_kind = receiver_map->elements_kind();
+ bool more_general_transition =
+ IsMoreGeneralElementsKindTransition(
+ current_map->elements_kind(), receiver_elements_kind);
+ Map* transitioned_map = more_general_transition
+ ? current_map->LookupElementsTransitionMap(receiver_elements_kind)
+ : NULL;
+
+ return transitioned_map == receiver_map;
+}
+
+
+// Since GC may have been invoked, by the time PatchCache is called, |state| is
+// not necessarily equal to target()->state().
+void IC::PatchCache(State state,
+ StrictModeFlag strict_mode,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Code> code) {
+ switch (state) {
+ case UNINITIALIZED:
+ case PREMONOMORPHIC:
+ case MONOMORPHIC_PROTOTYPE_FAILURE:
+ UpdateMonomorphicIC(receiver, code, name, strict_mode);
+ break;
+ case MONOMORPHIC:
+ // Only move to megamorphic if the target changes.
+ if (target() != *code) {
+ if (target()->is_load_stub() || target()->is_store_stub()) {
+ bool is_same_handler = false;
+ {
+ DisallowHeapAllocation no_allocation;
+ Code* old_handler = target()->FindFirstCode();
+ is_same_handler = old_handler == *code;
+ }
+ if (is_same_handler
+ && IsTransitionedMapOfMonomorphicTarget(receiver->map())) {
+ UpdateMonomorphicIC(receiver, code, name, strict_mode);
+ break;
+ }
+ if (UpdatePolymorphicIC(state, receiver, name, code, strict_mode)) {
+ break;
+ }
+
+ if (target()->type() != Code::NORMAL) {
+ CopyICToMegamorphicCache(name);
+ }
+ }
+
+ UpdateMegamorphicCache(receiver->map(), *name, *code);
+ set_target((strict_mode == kStrictMode)
+ ? *megamorphic_stub_strict()
+ : *megamorphic_stub());
+ }
+ break;
+ case MEGAMORPHIC:
+ // Update the stub cache.
+ UpdateMegamorphicCache(receiver->map(), *name, *code);
+ break;
+ case POLYMORPHIC:
+ if (target()->is_load_stub() || target()->is_store_stub()) {
+ if (UpdatePolymorphicIC(state, receiver, name, code, strict_mode)) {
+ break;
+ }
+ CopyICToMegamorphicCache(name);
+ UpdateMegamorphicCache(receiver->map(), *name, *code);
+ set_target((strict_mode == kStrictMode)
+ ? *megamorphic_stub_strict()
+ : *megamorphic_stub());
+ } else {
+ // When trying to patch a polymorphic keyed load/store element stub
+ // with anything other than another polymorphic stub, go generic.
+ set_target((strict_mode == kStrictMode)
+ ? *generic_stub_strict()
+ : *generic_stub());
+ }
+ break;
+ case DEBUG_STUB:
+ break;
+ case GENERIC:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+static void GetReceiverMapsForStub(Handle<Code> stub,
+ MapHandleList* result) {
+ ASSERT(stub->is_inline_cache_stub());
+ switch (stub->ic_state()) {
+ case MONOMORPHIC: {
+ Map* map = stub->FindFirstMap();
+ if (map != NULL) {
+ result->Add(Handle<Map>(map));
+ }
+ break;
+ }
+ case POLYMORPHIC: {
+ DisallowHeapAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(*stub, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Handle<Object> object(info->target_object(), stub->GetIsolate());
+ if (object->IsString()) break;
+ ASSERT(object->IsMap());
+ AddOneReceiverMapIfMissing(result, Handle<Map>::cast(object));
+ }
+ break;
+ }
+ case MEGAMORPHIC:
+ break;
+ case UNINITIALIZED:
+ case PREMONOMORPHIC:
+ case MONOMORPHIC_PROTOTYPE_FAILURE:
+ case GENERIC:
+ case DEBUG_STUB:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void LoadIC::UpdateCaches(LookupResult* lookup,
+ State state,
+ Handle<Object> object,
+ Handle<String> name) {
+ // Bail out if the result is not cacheable.
+ if (!lookup->IsCacheable()) {
+ set_target(*generic_stub());
+ return;
+ }
+
+ // TODO(jkummerow): It would be nice to support non-JSObjects in
+ // UpdateCaches, then we wouldn't need to go generic here.
+ if (!object->IsJSObject()) {
+ set_target(*generic_stub());
+ return;
+ }
+
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ 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.
+ code = pre_monomorphic_stub();
+ } else {
+ code = ComputeLoadHandler(lookup, receiver, name);
+ if (code.is_null()) {
+ set_target(*generic_stub());
+ return;
+ }
+ }
+
+ PatchCache(state, kNonStrictMode, receiver, name, code);
+ TRACE_IC("LoadIC", name, state, target());
+}
+
+
+void IC::UpdateMegamorphicCache(Map* map, Name* name, Code* code) {
+ // Cache code holding map should be consistent with
+ // GenerateMonomorphicCacheProbe.
+ isolate()->stub_cache()->Set(name, map, code);
+}
+
+
+Handle<Code> LoadIC::ComputeLoadHandler(LookupResult* lookup,
+ Handle<JSObject> receiver,
+ Handle<String> name) {
+ if (!lookup->IsProperty()) {
+ // Nonexistent property. The result is undefined.
+ return isolate()->stub_cache()->ComputeLoadNonexistent(name, receiver);
+ }
+
+ // Compute monomorphic stub.
+ Handle<JSObject> holder(lookup->holder());
+ switch (lookup->type()) {
+ case FIELD:
+ return isolate()->stub_cache()->ComputeLoadField(
+ name, receiver, holder,
+ lookup->GetFieldIndex(), lookup->representation());
+ case CONSTANT: {
+ Handle<Object> constant(lookup->GetConstant(), isolate());
+ // TODO(2803): Don't compute a stub for cons strings because they cannot
+ // be embedded into code.
+ if (constant->IsConsString()) return Handle<Code>::null();
+ return isolate()->stub_cache()->ComputeLoadConstant(
+ name, receiver, holder, constant);
+ }
+ case NORMAL:
+ if (holder->IsGlobalObject()) {
+ Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder);
+ Handle<PropertyCell> cell(
+ global->GetPropertyCell(lookup), isolate());
+ return isolate()->stub_cache()->ComputeLoadGlobal(
+ name, receiver, global, cell, lookup->IsDontDelete());
+ }
+ // 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 (!holder.is_identical_to(receiver)) break;
+ return isolate()->stub_cache()->ComputeLoadNormal(name, receiver);
+ case CALLBACKS: {
+ Handle<Object> callback(lookup->GetCallbackObject(), isolate());
+ if (callback->IsExecutableAccessorInfo()) {
+ Handle<ExecutableAccessorInfo> info =
+ Handle<ExecutableAccessorInfo>::cast(callback);
+ if (v8::ToCData<Address>(info->getter()) == 0) break;
+ if (!info->IsCompatibleReceiver(*receiver)) break;
+ return isolate()->stub_cache()->ComputeLoadCallback(
+ name, receiver, holder, info);
+ } else if (callback->IsAccessorPair()) {
+ Handle<Object> getter(Handle<AccessorPair>::cast(callback)->getter(),
+ isolate());
+ if (!getter->IsJSFunction()) break;
+ if (holder->IsGlobalObject()) break;
+ if (!holder->HasFastProperties()) break;
+ return isolate()->stub_cache()->ComputeLoadViaGetter(
+ name, receiver, holder, Handle<JSFunction>::cast(getter));
+ } else if (receiver->IsJSArray() &&
+ name->Equals(isolate()->heap()->length_string())) {
+ PropertyIndex lengthIndex =
+ PropertyIndex::NewHeaderIndex(JSArray::kLengthOffset / kPointerSize);
+ return isolate()->stub_cache()->ComputeLoadField(
+ name, receiver, holder, lengthIndex, Representation::Tagged());
+ }
+ // TODO(dcarney): Handle correctly.
+ if (callback->IsDeclaredAccessorInfo()) break;
+ ASSERT(callback->IsForeign());
+ // No IC support for old-style native accessors.
+ break;
+ }
+ case INTERCEPTOR:
+ ASSERT(HasInterceptorGetter(*holder));
+ return isolate()->stub_cache()->ComputeLoadInterceptor(
+ name, receiver, holder);
+ default:
+ break;
+ }
+ return Handle<Code>::null();
+}
+
+
+static Handle<Object> TryConvertKey(Handle<Object> key, Isolate* isolate) {
+ // This helper implements a few common fast cases for converting
+ // non-smi keys of keyed loads/stores to a smi or a string.
+ if (key->IsHeapNumber()) {
+ double value = Handle<HeapNumber>::cast(key)->value();
+ if (std::isnan(value)) {
+ key = isolate->factory()->nan_string();
+ } else {
+ int int_value = FastD2I(value);
+ if (value == int_value && Smi::IsValid(int_value)) {
+ key = Handle<Smi>(Smi::FromInt(int_value), isolate);
+ }
+ }
+ } else if (key->IsUndefined()) {
+ key = isolate->factory()->undefined_string();
+ }
+ return key;
+}
+
+
+Handle<Code> KeyedLoadIC::LoadElementStub(Handle<JSObject> receiver) {
+ State ic_state = target()->ic_state();
+
+ // 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() != Code::NORMAL) {
+ TRACE_GENERIC_IC(isolate(), "KeyedIC", "non-NORMAL target type");
+ return generic_stub();
+ }
+
+ Handle<Map> receiver_map(receiver->map(), isolate());
+ MapHandleList target_receiver_maps;
+ if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) {
+ // Optimistically assume that ICs that haven't reached the MONOMORPHIC state
+ // yet will do so and stay there.
+ return isolate()->stub_cache()->ComputeKeyedLoadElement(receiver_map);
+ }
+
+ if (target() == *string_stub()) {
+ target_receiver_maps.Add(isolate()->factory()->string_map());
+ } else {
+ GetReceiverMapsForStub(Handle<Code>(target(), isolate()),
+ &target_receiver_maps);
+ if (target_receiver_maps.length() == 0) {
+ return isolate()->stub_cache()->ComputeKeyedLoadElement(receiver_map);
+ }
+ }
+
+ // The first time a receiver is seen that is a transitioned version of the
+ // previous monomorphic receiver type, assume the new ElementsKind is the
+ // monomorphic type. This benefits global arrays that only transition
+ // once, and all call sites accessing them are faster if they remain
+ // monomorphic. If this optimistic assumption is not true, the IC will
+ // miss again and it will become polymorphic and support both the
+ // untransitioned and transitioned maps.
+ if (ic_state == MONOMORPHIC &&
+ IsMoreGeneralElementsKindTransition(
+ target_receiver_maps.at(0)->elements_kind(),
+ receiver->GetElementsKind())) {
+ return isolate()->stub_cache()->ComputeKeyedLoadElement(receiver_map);
+ }
+
+ ASSERT(ic_state != GENERIC);
+
+ // Determine the list of receiver maps that this call site has seen,
+ // adding the map that was just encountered.
+ if (!AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map)) {
+ // If the miss wasn't due to an unseen map, a polymorphic stub
+ // won't help, use the generic stub.
+ TRACE_GENERIC_IC(isolate(), "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(isolate(), "KeyedIC", "max polymorph exceeded");
+ return generic_stub();
+ }
+
+ return isolate()->stub_cache()->ComputeLoadElementPolymorphic(
+ &target_receiver_maps);
+}
+
+
+MaybeObject* KeyedLoadIC::Load(State state,
+ Handle<Object> object,
+ Handle<Object> key,
+ ICMissMode miss_mode) {
+ // Check for values that can be converted into an internalized string directly
+ // or is representable as a smi.
+ key = TryConvertKey(key, isolate());
+
+ if (key->IsInternalizedString()) {
+ return LoadIC::Load(state, object, Handle<String>::cast(key));
+ }
+
+ bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
+ ASSERT(!(use_ic && object->IsJSGlobalProxy()));
+
+ if (use_ic) {
+ Handle<Code> stub = generic_stub();
+ if (miss_mode != MISS_FORCE_GENERIC) {
+ if (object->IsString() && key->IsNumber()) {
+ if (state == UNINITIALIZED) {
+ stub = string_stub();
+ }
+ } else if (object->IsJSObject()) {
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (receiver->map()->is_deprecated()) {
+ JSObject::MigrateInstance(receiver);
+ }
+
+ 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->ToSmi()->IsFailure() &&
+ (target() != *non_strict_arguments_stub())) {
+ stub = LoadElementStub(receiver);
+ }
+ }
+ } else {
+ TRACE_GENERIC_IC(isolate(), "KeyedLoadIC", "force generic");
+ }
+ ASSERT(!stub.is_null());
+ set_target(*stub);
+ TRACE_IC("KeyedLoadIC", key, state, target());
+ }
+
+
+ return Runtime::GetObjectPropertyOrFail(isolate(), object, key);
+}
+
+
+Handle<Code> KeyedLoadIC::ComputeLoadHandler(LookupResult* lookup,
+ Handle<JSObject> receiver,
+ Handle<String> name) {
+ // Bail out if we didn't find a result.
+ if (!lookup->IsProperty()) return Handle<Code>::null();
+
+ // Compute a monomorphic stub.
+ Handle<JSObject> holder(lookup->holder(), isolate());
+ switch (lookup->type()) {
+ case FIELD:
+ return isolate()->stub_cache()->ComputeKeyedLoadField(
+ name, receiver, holder,
+ lookup->GetFieldIndex(), lookup->representation());
+ case CONSTANT: {
+ Handle<Object> constant(lookup->GetConstant(), isolate());
+ // TODO(2803): Don't compute a stub for cons strings because they cannot
+ // be embedded into code.
+ if (constant->IsConsString()) return Handle<Code>::null();
+ return isolate()->stub_cache()->ComputeKeyedLoadConstant(
+ name, receiver, holder, constant);
+ }
+ case CALLBACKS: {
+ Handle<Object> callback_object(lookup->GetCallbackObject(), isolate());
+ // TODO(dcarney): Handle DeclaredAccessorInfo correctly.
+ if (!callback_object->IsExecutableAccessorInfo()) break;
+ Handle<ExecutableAccessorInfo> callback =
+ Handle<ExecutableAccessorInfo>::cast(callback_object);
+ if (v8::ToCData<Address>(callback->getter()) == 0) break;
+ if (!callback->IsCompatibleReceiver(*receiver)) break;
+ return isolate()->stub_cache()->ComputeKeyedLoadCallback(
+ name, receiver, holder, callback);
+ }
+ case INTERCEPTOR:
+ ASSERT(HasInterceptorGetter(lookup->holder()));
+ return isolate()->stub_cache()->ComputeKeyedLoadInterceptor(
+ name, receiver, holder);
+ default:
+ // Always rewrite to the generic case so that we do not
+ // repeatedly try to rewrite.
+ return generic_stub();
+ }
+ return Handle<Code>::null();
+}
+
+
+static bool LookupForWrite(Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value,
+ LookupResult* lookup,
+ IC::State* state) {
+ Handle<JSObject> holder = receiver;
+ receiver->Lookup(*name, lookup);
+ if (lookup->IsFound()) {
+ if (lookup->IsReadOnly() || !lookup->IsCacheable()) return false;
+
+ if (lookup->holder() == *receiver) {
+ if (lookup->IsInterceptor() &&
+ receiver->GetNamedInterceptor()->setter()->IsUndefined()) {
+ receiver->LocalLookupRealNamedProperty(*name, lookup);
+ return lookup->IsFound() &&
+ !lookup->IsReadOnly() &&
+ lookup->CanHoldValue(value) &&
+ lookup->IsCacheable();
+ }
+ return lookup->CanHoldValue(value);
+ }
+
+ if (lookup->IsPropertyCallbacks()) return true;
+
+ // Currently normal holders in the prototype chain are not supported. They
+ // would require a runtime positive lookup and verification that the details
+ // have not changed.
+ if (lookup->IsInterceptor() || lookup->IsNormal()) return false;
+ holder = Handle<JSObject>(lookup->holder(), lookup->isolate());
+ }
+
+ // While normally LookupTransition gets passed the receiver, in this case we
+ // pass the holder of the property that we overwrite. This keeps the holder in
+ // the LookupResult intact so we can later use it to generate a prototype
+ // chain check. This avoids a double lookup, but requires us to pass in the
+ // receiver when trying to fetch extra information from the transition.
+ receiver->map()->LookupTransition(*holder, *name, lookup);
+ if (!lookup->IsTransition()) return false;
+ PropertyDetails target_details =
+ lookup->GetTransitionDetails(receiver->map());
+ if (target_details.IsReadOnly()) return false;
+
+ // If the value that's being stored does not fit in the field that the
+ // instance would transition to, create a new transition that fits the value.
+ // This has to be done before generating the IC, since that IC will embed the
+ // transition target.
+ // Ensure the instance and its map were migrated before trying to update the
+ // transition target.
+ ASSERT(!receiver->map()->is_deprecated());
+ if (!value->FitsRepresentation(target_details.representation())) {
+ Handle<Map> target(lookup->GetTransitionMapFromMap(receiver->map()));
+ Map::GeneralizeRepresentation(
+ target, target->LastAdded(), value->OptimalRepresentation());
+ // Lookup the transition again since the transition tree may have changed
+ // entirely by the migration above.
+ receiver->map()->LookupTransition(*holder, *name, lookup);
+ if (!lookup->IsTransition()) return false;
+ *state = MONOMORPHIC_PROTOTYPE_FAILURE;
+ }
+ return true;
+}
+
+
+MaybeObject* StoreIC::Store(State state,
+ StrictModeFlag strict_mode,
+ Handle<Object> object,
+ Handle<String> name,
+ Handle<Object> value,
+ JSReceiver::StoreFromKeyed store_mode) {
+ // Handle proxies.
+ if (object->IsJSProxy()) {
+ return JSReceiver::SetPropertyOrFail(
+ Handle<JSReceiver>::cast(object), 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);
+ }
+
+ // The length property of string values is read-only. Throw in strict mode.
+ if (strict_mode == kStrictMode && object->IsString() &&
+ name->Equals(isolate()->heap()->length_string())) {
+ return TypeError("strict_read_only_property", object, name);
+ }
+
+ // Ignore other stores where the receiver is not a JSObject.
+ // TODO(1475): Must check prototype chains of object wrappers.
+ if (!object->IsJSObject()) return *value;
+
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+
+ if (receiver->map()->is_deprecated()) {
+ JSObject::MigrateInstance(receiver);
+ }
+
+ // Check if the given name is an array index.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ Handle<Object> result =
+ JSObject::SetElement(receiver, index, value, NONE, strict_mode);
+ RETURN_IF_EMPTY_HANDLE(isolate(), result);
+ return *value;
+ }
+
+ // Observed objects are always modified through the runtime.
+ if (FLAG_harmony_observation && receiver->map()->is_observed()) {
+ return JSReceiver::SetPropertyOrFail(
+ receiver, name, value, NONE, strict_mode, store_mode);
+ }
+
+ // Use specialized code for setting the length of arrays with fast
+ // properties. Slow properties might indicate redefinition of the length
+ // property. Note that when redefined using Object.freeze, it's possible
+ // to have fast properties but a read-only length.
+ if (FLAG_use_ic &&
+ receiver->IsJSArray() &&
+ name->Equals(isolate()->heap()->length_string()) &&
+ Handle<JSArray>::cast(receiver)->AllowsSetElementsLength() &&
+ receiver->HasFastProperties() &&
+ !receiver->map()->is_frozen()) {
+ Handle<Code> stub =
+ StoreArrayLengthStub(kind(), strict_mode).GetCode(isolate());
+ set_target(*stub);
+ TRACE_IC("StoreIC", name, state, *stub);
+ return JSReceiver::SetPropertyOrFail(
+ receiver, name, value, NONE, strict_mode, store_mode);
+ }
+
+ if (receiver->IsJSGlobalProxy()) {
+ if (FLAG_use_ic && kind() != Code::KEYED_STORE_IC) {
+ // Generate a generic stub that goes to the runtime when we see a global
+ // proxy as receiver.
+ Handle<Code> stub = (strict_mode == kStrictMode)
+ ? global_proxy_stub_strict()
+ : global_proxy_stub();
+ set_target(*stub);
+ TRACE_IC("StoreIC", name, state, *stub);
+ }
+ return JSReceiver::SetPropertyOrFail(
+ receiver, name, value, NONE, strict_mode, store_mode);
+ }
+
+ LookupResult lookup(isolate());
+ if (LookupForWrite(receiver, name, value, &lookup, &state)) {
+ if (FLAG_use_ic) {
+ UpdateCaches(&lookup, state, strict_mode, receiver, name, value);
+ }
+ } else if (strict_mode == kStrictMode &&
+ !(lookup.IsProperty() && lookup.IsReadOnly()) &&
+ IsUndeclaredGlobal(object)) {
+ // Strict mode doesn't allow setting non-existent global property.
+ return ReferenceError("not_defined", name);
+ } else if (FLAG_use_ic &&
+ (lookup.IsNormal() ||
+ (lookup.IsField() && lookup.CanHoldValue(value)))) {
+ Handle<Code> stub = strict_mode == kStrictMode
+ ? generic_stub_strict() : generic_stub();
+ set_target(*stub);
+ }
+
+ // Set the property.
+ return JSReceiver::SetPropertyOrFail(
+ receiver, name, value, NONE, strict_mode, store_mode);
+}
+
+
+void StoreIC::UpdateCaches(LookupResult* lookup,
+ State state,
+ StrictModeFlag strict_mode,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value) {
+ ASSERT(!receiver->IsJSGlobalProxy());
+ ASSERT(lookup->IsFound());
+
+ // These are not cacheable, so we never see such LookupResults here.
+ ASSERT(!lookup->IsHandler());
+
+ Handle<Code> code = ComputeStoreMonomorphic(
+ lookup, strict_mode, receiver, name, value);
+ if (code.is_null()) {
+ Handle<Code> stub = strict_mode == kStrictMode
+ ? generic_stub_strict() : generic_stub();
+ set_target(*stub);
+ return;
+ }
+
+ PatchCache(state, strict_mode, receiver, name, code);
+ TRACE_IC("StoreIC", name, state, target());
+}
+
+
+Handle<Code> StoreIC::ComputeStoreMonomorphic(LookupResult* lookup,
+ StrictModeFlag strict_mode,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value) {
+ Handle<JSObject> holder(lookup->holder());
+ switch (lookup->type()) {
+ case FIELD:
+ return isolate()->stub_cache()->ComputeStoreField(
+ name, receiver, lookup, strict_mode);
+ 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);
+ Handle<PropertyCell> cell(
+ global->GetPropertyCell(lookup), isolate());
+ return isolate()->stub_cache()->ComputeStoreGlobal(
+ name, global, cell, value, strict_mode);
+ }
+ ASSERT(holder.is_identical_to(receiver));
+ return isolate()->stub_cache()->ComputeStoreNormal(strict_mode);
+ case CALLBACKS: {
+ Handle<Object> callback(lookup->GetCallbackObject(), isolate());
+ if (callback->IsExecutableAccessorInfo()) {
+ Handle<ExecutableAccessorInfo> info =
+ Handle<ExecutableAccessorInfo>::cast(callback);
+ if (v8::ToCData<Address>(info->setter()) == 0) break;
+ if (!holder->HasFastProperties()) break;
+ if (!info->IsCompatibleReceiver(*receiver)) break;
+ return isolate()->stub_cache()->ComputeStoreCallback(
+ name, receiver, holder, info, strict_mode);
+ } else if (callback->IsAccessorPair()) {
+ Handle<Object> setter(
+ Handle<AccessorPair>::cast(callback)->setter(), isolate());
+ if (!setter->IsJSFunction()) break;
+ if (holder->IsGlobalObject()) break;
+ if (!holder->HasFastProperties()) break;
+ return isolate()->stub_cache()->ComputeStoreViaSetter(
+ name, receiver, holder, Handle<JSFunction>::cast(setter),
+ strict_mode);
+ }
+ // TODO(dcarney): Handle correctly.
+ if (callback->IsDeclaredAccessorInfo()) break;
+ ASSERT(callback->IsForeign());
+ // No IC support for old-style native accessors.
+ break;
+ }
+ case INTERCEPTOR:
+ ASSERT(!receiver->GetNamedInterceptor()->setter()->IsUndefined());
+ return isolate()->stub_cache()->ComputeStoreInterceptor(
+ name, receiver, strict_mode);
+ case CONSTANT:
+ break;
+ case TRANSITION: {
+ // Explicitly pass in the receiver map since LookupForWrite may have
+ // stored something else than the receiver in the holder.
+ Handle<Map> transition(
+ lookup->GetTransitionTarget(receiver->map()), isolate());
+ int descriptor = transition->LastAdded();
+
+ DescriptorArray* target_descriptors = transition->instance_descriptors();
+ PropertyDetails details = target_descriptors->GetDetails(descriptor);
+
+ if (details.type() == CALLBACKS || details.attributes() != NONE) break;
+
+ return isolate()->stub_cache()->ComputeStoreTransition(
+ name, receiver, lookup, transition, strict_mode);
+ }
+ case NONEXISTENT:
+ case HANDLER:
+ UNREACHABLE();
+ break;
+ }
+ return Handle<Code>::null();
+}
+
+
+Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> receiver,
+ KeyedAccessStoreMode store_mode,
+ StrictModeFlag strict_mode) {
+ // 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() != Code::NORMAL) {
+ TRACE_GENERIC_IC(isolate(), "KeyedIC", "non-NORMAL target type");
+ return strict_mode == kStrictMode ? generic_stub_strict() : generic_stub();
+ }
+
+ if (!FLAG_compiled_keyed_stores &&
+ (store_mode == STORE_NO_TRANSITION_HANDLE_COW ||
+ store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS)) {
+ // TODO(danno): We'll soon handle MONOMORPHIC ICs that also support
+ // copying COW arrays and silently ignoring some OOB stores into external
+ // arrays, but for now use the generic.
+ TRACE_GENERIC_IC(isolate(), "KeyedIC", "COW/OOB external array");
+ return strict_mode == kStrictMode
+ ? generic_stub_strict()
+ : generic_stub();
+ }
+
+ State ic_state = target()->ic_state();
+ Handle<Map> receiver_map(receiver->map(), isolate());
+ if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) {
+ // Optimistically assume that ICs that haven't reached the MONOMORPHIC state
+ // yet will do so and stay there.
+ Handle<Map> monomorphic_map = ComputeTransitionedMap(receiver, store_mode);
+ store_mode = GetNonTransitioningStoreMode(store_mode);
+ return isolate()->stub_cache()->ComputeKeyedStoreElement(
+ monomorphic_map, strict_mode, store_mode);
+ }
+
+ MapHandleList target_receiver_maps;
+ target()->FindAllMaps(&target_receiver_maps);
+ if (target_receiver_maps.length() == 0) {
+ // In the case that there is a non-map-specific IC is installed (e.g. keyed
+ // stores into properties in dictionary mode), then there will be not
+ // receiver maps in the target.
+ return strict_mode == kStrictMode
+ ? generic_stub_strict()
+ : generic_stub();
+ }
+
+ // There are several special cases where an IC that is MONOMORPHIC can still
+ // transition to a different GetNonTransitioningStoreMode IC that handles a
+ // superset of the original IC. Handle those here if the receiver map hasn't
+ // changed or it has transitioned to a more general kind.
+ KeyedAccessStoreMode old_store_mode =
+ Code::GetKeyedAccessStoreMode(target()->extra_ic_state());
+ Handle<Map> previous_receiver_map = target_receiver_maps.at(0);
+ if (ic_state == MONOMORPHIC) {
+ // If the "old" and "new" maps are in the same elements map family, stay
+ // MONOMORPHIC and use the map for the most generic ElementsKind.
+ Handle<Map> transitioned_receiver_map = receiver_map;
+ if (IsTransitionStoreMode(store_mode)) {
+ transitioned_receiver_map =
+ ComputeTransitionedMap(receiver, store_mode);
+ }
+ if (IsTransitionedMapOfMonomorphicTarget(*transitioned_receiver_map)) {
+ // Element family is the same, use the "worst" case map.
+ store_mode = GetNonTransitioningStoreMode(store_mode);
+ return isolate()->stub_cache()->ComputeKeyedStoreElement(
+ transitioned_receiver_map, strict_mode, store_mode);
+ } else if (*previous_receiver_map == receiver->map() &&
+ old_store_mode == STANDARD_STORE &&
+ (IsGrowStoreMode(store_mode) ||
+ store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS ||
+ store_mode == STORE_NO_TRANSITION_HANDLE_COW)) {
+ // A "normal" IC that handles stores can switch to a version that can
+ // grow at the end of the array, handle OOB accesses or copy COW arrays
+ // and still stay MONOMORPHIC.
+ return isolate()->stub_cache()->ComputeKeyedStoreElement(
+ receiver_map, strict_mode, store_mode);
+ }
+ }
+
+ ASSERT(ic_state != GENERIC);
+
+ bool map_added =
+ AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map);
+
+ if (IsTransitionStoreMode(store_mode)) {
+ Handle<Map> transitioned_receiver_map =
+ ComputeTransitionedMap(receiver, store_mode);
+ map_added |= AddOneReceiverMapIfMissing(&target_receiver_maps,
+ transitioned_receiver_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(isolate(), "KeyedIC", "same map added twice");
+ return strict_mode == kStrictMode ? generic_stub_strict() : 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(isolate(), "KeyedIC", "max polymorph exceeded");
+ return strict_mode == kStrictMode ? generic_stub_strict() : generic_stub();
+ }
+
+ // Make sure all polymorphic handlers have the same store mode, otherwise the
+ // generic stub must be used.
+ store_mode = GetNonTransitioningStoreMode(store_mode);
+ if (old_store_mode != STANDARD_STORE) {
+ if (store_mode == STANDARD_STORE) {
+ store_mode = old_store_mode;
+ } else if (store_mode != old_store_mode) {
+ TRACE_GENERIC_IC(isolate(), "KeyedIC", "store mode mismatch");
+ return strict_mode == kStrictMode
+ ? generic_stub_strict()
+ : generic_stub();
+ }
+ }
+
+ // If the store mode isn't the standard mode, make sure that all polymorphic
+ // receivers are either external arrays, or all "normal" arrays. Otherwise,
+ // use the generic stub.
+ if (store_mode != STANDARD_STORE) {
+ int external_arrays = 0;
+ for (int i = 0; i < target_receiver_maps.length(); ++i) {
+ if (target_receiver_maps[i]->has_external_array_elements()) {
+ external_arrays++;
+ }
+ }
+ if (external_arrays != 0 &&
+ external_arrays != target_receiver_maps.length()) {
+ TRACE_GENERIC_IC(isolate(), "KeyedIC",
+ "unsupported combination of external and normal arrays");
+ return strict_mode == kStrictMode
+ ? generic_stub_strict()
+ : generic_stub();
+ }
+ }
+
+ return isolate()->stub_cache()->ComputeStoreElementPolymorphic(
+ &target_receiver_maps, store_mode, strict_mode);
+}
+
+
+Handle<Map> KeyedStoreIC::ComputeTransitionedMap(
+ Handle<JSObject> receiver,
+ KeyedAccessStoreMode store_mode) {
+ switch (store_mode) {
+ case STORE_TRANSITION_SMI_TO_OBJECT:
+ case STORE_TRANSITION_DOUBLE_TO_OBJECT:
+ case STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT:
+ case STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT:
+ return JSObject::GetElementsTransitionMap(receiver, FAST_ELEMENTS);
+ case STORE_TRANSITION_SMI_TO_DOUBLE:
+ case STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE:
+ return JSObject::GetElementsTransitionMap(receiver, FAST_DOUBLE_ELEMENTS);
+ case STORE_TRANSITION_HOLEY_SMI_TO_OBJECT:
+ case STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT:
+ case STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT:
+ case STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT:
+ return JSObject::GetElementsTransitionMap(receiver,
+ FAST_HOLEY_ELEMENTS);
+ case STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE:
+ case STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE:
+ return JSObject::GetElementsTransitionMap(receiver,
+ FAST_HOLEY_DOUBLE_ELEMENTS);
+ case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
+ ASSERT(receiver->map()->has_external_array_elements());
+ // Fall through
+ case STORE_NO_TRANSITION_HANDLE_COW:
+ case STANDARD_STORE:
+ case STORE_AND_GROW_NO_TRANSITION:
+ return Handle<Map>(receiver->map(), isolate());
+ }
+ return Handle<Map>::null();
+}
+
+
+bool IsOutOfBoundsAccess(Handle<JSObject> receiver,
+ int index) {
+ if (receiver->IsJSArray()) {
+ return JSArray::cast(*receiver)->length()->IsSmi() &&
+ index >= Smi::cast(JSArray::cast(*receiver)->length())->value();
+ }
+ return index >= receiver->elements()->length();
+}
+
+
+KeyedAccessStoreMode KeyedStoreIC::GetStoreMode(Handle<JSObject> receiver,
+ Handle<Object> key,
+ Handle<Object> value) {
+ ASSERT(!key->ToSmi()->IsFailure());
+ Smi* smi_key = NULL;
+ key->ToSmi()->To(&smi_key);
+ int index = smi_key->value();
+ bool oob_access = IsOutOfBoundsAccess(receiver, index);
+ bool allow_growth = receiver->IsJSArray() && oob_access;
+ if (allow_growth) {
+ // Handle growing array in stub if necessary.
+ if (receiver->HasFastSmiElements()) {
+ if (value->IsHeapNumber()) {
+ if (receiver->HasFastHoleyElements()) {
+ return STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE;
+ } else {
+ return STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE;
+ }
+ }
+ if (value->IsHeapObject()) {
+ if (receiver->HasFastHoleyElements()) {
+ return STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT;
+ } else {
+ return STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT;
+ }
+ }
+ } else if (receiver->HasFastDoubleElements()) {
+ if (!value->IsSmi() && !value->IsHeapNumber()) {
+ if (receiver->HasFastHoleyElements()) {
+ return STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT;
+ } else {
+ return STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT;
+ }
+ }
+ }
+ return STORE_AND_GROW_NO_TRANSITION;
+ } else {
+ // Handle only in-bounds elements accesses.
+ if (receiver->HasFastSmiElements()) {
+ if (value->IsHeapNumber()) {
+ if (receiver->HasFastHoleyElements()) {
+ return STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE;
+ } else {
+ return STORE_TRANSITION_SMI_TO_DOUBLE;
+ }
+ } else if (value->IsHeapObject()) {
+ if (receiver->HasFastHoleyElements()) {
+ return STORE_TRANSITION_HOLEY_SMI_TO_OBJECT;
+ } else {
+ return STORE_TRANSITION_SMI_TO_OBJECT;
+ }
+ }
+ } else if (receiver->HasFastDoubleElements()) {
+ if (!value->IsSmi() && !value->IsHeapNumber()) {
+ if (receiver->HasFastHoleyElements()) {
+ return STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT;
+ } else {
+ return STORE_TRANSITION_DOUBLE_TO_OBJECT;
+ }
+ }
+ }
+ if (!FLAG_trace_external_array_abuse &&
+ receiver->map()->has_external_array_elements() && oob_access) {
+ return STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS;
+ }
+ Heap* heap = receiver->GetHeap();
+ if (receiver->elements()->map() == heap->fixed_cow_array_map()) {
+ return STORE_NO_TRANSITION_HANDLE_COW;
+ } else {
+ return STANDARD_STORE;
+ }
+ }
+}
+
+
+MaybeObject* KeyedStoreIC::Store(State state,
+ StrictModeFlag strict_mode,
+ Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ ICMissMode miss_mode) {
+ // Check for values that can be converted into an internalized string directly
+ // or is representable as a smi.
+ key = TryConvertKey(key, isolate());
+
+ if (key->IsInternalizedString()) {
+ return StoreIC::Store(state,
+ strict_mode,
+ object,
+ Handle<String>::cast(key),
+ value,
+ JSReceiver::MAY_BE_STORE_FROM_KEYED);
+ }
+
+ bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded() &&
+ !(FLAG_harmony_observation && object->IsJSObject() &&
+ JSObject::cast(*object)->map()->is_observed());
+ if (use_ic && !object->IsSmi()) {
+ // Don't use ICs for maps of the objects in Array's prototype chain. We
+ // expect to be able to trap element sets to objects with those maps in the
+ // runtime to enable optimization of element hole access.
+ Handle<HeapObject> heap_object = Handle<HeapObject>::cast(object);
+ if (heap_object->map()->IsMapInArrayPrototypeChain()) use_ic = false;
+ }
+ ASSERT(!(use_ic && object->IsJSGlobalProxy()));
+
+ if (use_ic) {
+ Handle<Code> stub = (strict_mode == kStrictMode)
+ ? generic_stub_strict()
+ : generic_stub();
+ if (miss_mode != MISS_FORCE_GENERIC) {
+ if (object->IsJSObject()) {
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (receiver->map()->is_deprecated()) {
+ JSObject::MigrateInstance(receiver);
+ }
+ bool key_is_smi_like = key->IsSmi() ||
+ (FLAG_compiled_keyed_stores && !key->ToSmi()->IsFailure());
+ if (receiver->elements()->map() ==
+ isolate()->heap()->non_strict_arguments_elements_map()) {
+ stub = non_strict_arguments_stub();
+ } else if (key_is_smi_like &&
+ (target() != *non_strict_arguments_stub())) {
+ KeyedAccessStoreMode store_mode = GetStoreMode(receiver, key, value);
+ stub = StoreElementStub(receiver, store_mode, strict_mode);
+ } else {
+ TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "key not a number");
+ }
+ } else {
+ TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "not an object");
+ }
+ } else {
+ TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "force generic");
+ }
+ ASSERT(!stub.is_null());
+ set_target(*stub);
+ TRACE_IC("KeyedStoreIC", key, state, target());
+ }
+
+ return Runtime::SetObjectPropertyOrFail(
+ isolate(), object , key, value, NONE, strict_mode);
+}
+
+
+Handle<Code> KeyedStoreIC::ComputeStoreMonomorphic(LookupResult* lookup,
+ StrictModeFlag strict_mode,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value) {
+ // 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
+ // current state.
+ switch (lookup->type()) {
+ case FIELD:
+ return isolate()->stub_cache()->ComputeKeyedStoreField(
+ name, receiver, lookup, strict_mode);
+ case TRANSITION: {
+ // Explicitly pass in the receiver map since LookupForWrite may have
+ // stored something else than the receiver in the holder.
+ Handle<Map> transition(
+ lookup->GetTransitionTarget(receiver->map()), isolate());
+ int descriptor = transition->LastAdded();
+
+ DescriptorArray* target_descriptors = transition->instance_descriptors();
+ PropertyDetails details = target_descriptors->GetDetails(descriptor);
+
+ if (details.type() != CALLBACKS && details.attributes() == NONE) {
+ return isolate()->stub_cache()->ComputeKeyedStoreTransition(
+ name, receiver, lookup, transition, strict_mode);
+ }
+ // fall through.
+ }
+ case NORMAL:
+ case CONSTANT:
+ case CALLBACKS:
+ case INTERCEPTOR:
+ // Always rewrite to the generic case so that we do not
+ // repeatedly try to rewrite.
+ return (strict_mode == kStrictMode)
+ ? generic_stub_strict()
+ : generic_stub();
+ case HANDLER:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ return Handle<Code>::null();
+}
+
+
+#undef TRACE_IC
+
+
+// ----------------------------------------------------------------------------
+// Static IC stub generators.
+//
+
+// Used from ic-<arch>.cc.
+RUNTIME_FUNCTION(MaybeObject*, CallIC_Miss) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CallIC ic(isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ MaybeObject* maybe_result = ic.LoadFunction(state,
+ extra_ic_state,
+ args.at<Object>(0),
+ args.at<String>(1));
+ JSFunction* raw_function;
+ 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 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.
+ 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) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ KeyedCallIC ic(isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ MaybeObject* maybe_result =
+ ic.LoadFunction(state, args.at<Object>(0), args.at<Object>(1));
+ // Result could be a function or a failure.
+ JSFunction* raw_function = NULL;
+ if (!maybe_result->To(&raw_function)) return maybe_result;
+
+ if (raw_function->is_compiled()) return raw_function;
+
+ Handle<JSFunction> function(raw_function, isolate);
+ JSFunction::CompileLazy(function, CLEAR_EXCEPTION);
+ return *function;
+}
+
+
+// Used from ic-<arch>.cc.
+RUNTIME_FUNCTION(MaybeObject*, LoadIC_Miss) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ LoadIC ic(IC::NO_EXTRA_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ return ic.Load(state, args.at<Object>(0), args.at<String>(1));
+}
+
+
+// Used from ic-<arch>.cc
+RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_Miss) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ KeyedLoadIC ic(IC::NO_EXTRA_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ return ic.Load(state, args.at<Object>(0), args.at<Object>(1), MISS);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissFromStubFailure) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ KeyedLoadIC ic(IC::EXTRA_CALL_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ return ic.Load(state, args.at<Object>(0), args.at<Object>(1), MISS);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissForceGeneric) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ KeyedLoadIC ic(IC::NO_EXTRA_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ return ic.Load(state,
+ args.at<Object>(0),
+ args.at<Object>(1),
+ MISS_FORCE_GENERIC);
+}
+
+
+// Used from ic-<arch>.cc.
+RUNTIME_FUNCTION(MaybeObject*, StoreIC_Miss) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ StoreIC ic(IC::NO_EXTRA_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ return ic.Store(state,
+ Code::GetStrictMode(extra_ic_state),
+ args.at<Object>(0),
+ args.at<String>(1),
+ args.at<Object>(2));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, StoreIC_MissFromStubFailure) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ StoreIC ic(IC::EXTRA_CALL_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ return ic.Store(state,
+ Code::GetStrictMode(extra_ic_state),
+ args.at<Object>(0),
+ args.at<String>(1),
+ args.at<Object>(2));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, StoreIC_ArrayLength) {
+ SealHandleScope shs(isolate);
+
+ ASSERT(args.length() == 2);
+ 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_string(), &debug_lookup);
+ ASSERT(debug_lookup.IsPropertyCallbacks() && !debug_lookup.IsReadOnly());
+#endif
+
+ Object* result;
+ MaybeObject* maybe_result = receiver->SetElementsLength(len);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ return len;
+}
+
+
+// Extend storage is called in a store inline cache when
+// it is necessary to extend the properties array of a
+// JSObject.
+RUNTIME_FUNCTION(MaybeObject*, SharedStoreIC_ExtendStorage) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+
+ // Convert the parameters
+ JSObject* object = JSObject::cast(args[0]);
+ Map* transition = Map::cast(args[1]);
+ Object* value = args[2];
+
+ // Check the object has run out out property space.
+ ASSERT(object->HasFastProperties());
+ ASSERT(object->map()->unused_property_fields() == 0);
+
+ // Expand the properties array.
+ FixedArray* old_storage = object->properties();
+ int new_unused = transition->unused_property_fields();
+ int new_size = old_storage->length() + new_unused + 1;
+ Object* result;
+ MaybeObject* maybe_result = old_storage->CopySize(new_size);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+
+ FixedArray* new_storage = FixedArray::cast(result);
+
+ Object* to_store = value;
+
+ if (FLAG_track_double_fields) {
+ DescriptorArray* descriptors = transition->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(transition->LastAdded());
+ if (details.representation().IsDouble()) {
+ MaybeObject* maybe_storage =
+ isolate->heap()->AllocateHeapNumber(value->Number());
+ if (!maybe_storage->To(&to_store)) return maybe_storage;
+ }
+ }
+
+ new_storage->set(old_storage->length(), to_store);
+
+ // Set the new property value and do the map transition.
+ object->set_properties(new_storage);
+ object->set_map(transition);
+
+ // Return the stored value.
+ return value;
+}
+
+
+// Used from ic-<arch>.cc.
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Miss) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic(IC::NO_EXTRA_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ return ic.Store(state,
+ Code::GetStrictMode(extra_ic_state),
+ args.at<Object>(0),
+ args.at<Object>(1),
+ args.at<Object>(2),
+ MISS);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissFromStubFailure) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic(IC::EXTRA_CALL_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ return ic.Store(state,
+ Code::GetStrictMode(extra_ic_state),
+ args.at<Object>(0),
+ args.at<Object>(1),
+ args.at<Object>(2),
+ MISS);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, StoreIC_Slow) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ StoreIC ic(IC::NO_EXTRA_FRAME, isolate);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ Handle<Object> object = args.at<Object>(0);
+ Handle<Object> key = args.at<Object>(1);
+ Handle<Object> value = args.at<Object>(2);
+ StrictModeFlag strict_mode = Code::GetStrictMode(extra_ic_state);
+ return Runtime::SetObjectProperty(isolate,
+ object,
+ key,
+ value,
+ NONE,
+ strict_mode);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Slow) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic(IC::NO_EXTRA_FRAME, isolate);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ Handle<Object> object = args.at<Object>(0);
+ Handle<Object> key = args.at<Object>(1);
+ Handle<Object> value = args.at<Object>(2);
+ StrictModeFlag strict_mode = Code::GetStrictMode(extra_ic_state);
+ return Runtime::SetObjectProperty(isolate,
+ object,
+ key,
+ value,
+ NONE,
+ strict_mode);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissForceGeneric) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic(IC::NO_EXTRA_FRAME, isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ return ic.Store(state,
+ Code::GetStrictMode(extra_ic_state),
+ args.at<Object>(0),
+ args.at<Object>(1),
+ args.at<Object>(2),
+ MISS_FORCE_GENERIC);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, ElementsTransitionAndStoreIC_Miss) {
+ SealHandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ KeyedStoreIC ic(IC::EXTRA_CALL_FRAME, isolate);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ Handle<Object> value = args.at<Object>(0);
+ Handle<Object> key = args.at<Object>(2);
+ Handle<Object> object = args.at<Object>(3);
+ StrictModeFlag strict_mode = Code::GetStrictMode(extra_ic_state);
+ return Runtime::SetObjectProperty(isolate,
+ object,
+ key,
+ value,
+ NONE,
+ strict_mode);
+}
+
+
+void BinaryOpIC::patch(Code* code) {
+ set_target(code);
+}
+
+
+const char* BinaryOpIC::GetName(TypeInfo type_info) {
+ switch (type_info) {
+ case UNINITIALIZED: return "Uninitialized";
+ case SMI: return "Smi";
+ case INT32: return "Int32";
+ case NUMBER: return "Number";
+ case ODDBALL: return "Oddball";
+ case STRING: return "String";
+ case GENERIC: return "Generic";
+ default: return "Invalid";
+ }
+}
+
+
+BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
+ switch (type_info) {
+ case UNINITIALIZED:
+ return ::v8::internal::UNINITIALIZED;
+ case SMI:
+ case INT32:
+ case NUMBER:
+ case ODDBALL:
+ case STRING:
+ return MONOMORPHIC;
+ case GENERIC:
+ return ::v8::internal::GENERIC;
+ }
+ UNREACHABLE();
+ return ::v8::internal::UNINITIALIZED;
+}
+
+
+Handle<Type> BinaryOpIC::TypeInfoToType(BinaryOpIC::TypeInfo binary_type,
+ Isolate* isolate) {
+ switch (binary_type) {
+ case UNINITIALIZED:
+ return handle(Type::None(), isolate);
+ case SMI:
+ return handle(Type::Smi(), isolate);
+ case INT32:
+ return handle(Type::Signed32(), isolate);
+ case NUMBER:
+ return handle(Type::Number(), isolate);
+ case ODDBALL:
+ return handle(Type::Optional(
+ handle(Type::Union(
+ handle(Type::Number(), isolate),
+ handle(Type::String(), isolate)), isolate)), isolate);
+ case STRING:
+ return handle(Type::String(), isolate);
+ case GENERIC:
+ return handle(Type::Any(), isolate);
+ }
+ UNREACHABLE();
+ return handle(Type::Any(), isolate);
+}
+
+
+void BinaryOpIC::StubInfoToType(int minor_key,
+ Handle<Type>* left,
+ Handle<Type>* right,
+ Handle<Type>* result,
+ Isolate* isolate) {
+ TypeInfo left_typeinfo, right_typeinfo, result_typeinfo;
+ BinaryOpStub::decode_types_from_minor_key(
+ minor_key, &left_typeinfo, &right_typeinfo, &result_typeinfo);
+ *left = TypeInfoToType(left_typeinfo, isolate);
+ *right = TypeInfoToType(right_typeinfo, isolate);
+ *result = TypeInfoToType(result_typeinfo, isolate);
+}
+
+
+static BinaryOpIC::TypeInfo TypeInfoFromValue(Handle<Object> value,
+ Token::Value op) {
+ v8::internal::TypeInfo type = v8::internal::TypeInfo::FromValue(value);
+ if (type.IsSmi()) return BinaryOpIC::SMI;
+ if (type.IsInteger32()) {
+ if (kSmiValueSize == 32) return BinaryOpIC::SMI;
+ return BinaryOpIC::INT32;
+ }
+ if (type.IsNumber()) return BinaryOpIC::NUMBER;
+ if (type.IsString()) return BinaryOpIC::STRING;
+ if (value->IsUndefined()) {
+ if (op == Token::BIT_AND ||
+ op == Token::BIT_OR ||
+ op == Token::BIT_XOR ||
+ op == Token::SAR ||
+ op == Token::SHL ||
+ op == Token::SHR) {
+ if (kSmiValueSize == 32) return BinaryOpIC::SMI;
+ return BinaryOpIC::INT32;
+ }
+ return BinaryOpIC::ODDBALL;
+ }
+ return BinaryOpIC::GENERIC;
+}
+
+
+static BinaryOpIC::TypeInfo InputState(BinaryOpIC::TypeInfo old_type,
+ Handle<Object> value,
+ Token::Value op) {
+ BinaryOpIC::TypeInfo new_type = TypeInfoFromValue(value, op);
+ if (old_type == BinaryOpIC::STRING) {
+ if (new_type == BinaryOpIC::STRING) return new_type;
+ return BinaryOpIC::GENERIC;
+ }
+ return Max(old_type, new_type);
+}
+
+
+#ifdef DEBUG
+static void TraceBinaryOp(BinaryOpIC::TypeInfo left,
+ BinaryOpIC::TypeInfo right,
+ Maybe<int32_t> fixed_right_arg,
+ BinaryOpIC::TypeInfo result) {
+ PrintF("%s*%s", BinaryOpIC::GetName(left), BinaryOpIC::GetName(right));
+ if (fixed_right_arg.has_value) PrintF("{%d}", fixed_right_arg.value);
+ PrintF("->%s", BinaryOpIC::GetName(result));
+}
+#endif
+
+
+RUNTIME_FUNCTION(MaybeObject*, BinaryOp_Patch) {
+ ASSERT(args.length() == 3);
+
+ HandleScope scope(isolate);
+ Handle<Object> left = args.at<Object>(0);
+ Handle<Object> right = args.at<Object>(1);
+ int key = args.smi_at(2);
+ Token::Value op = BinaryOpStub::decode_op_from_minor_key(key);
+
+ BinaryOpIC::TypeInfo previous_left, previous_right, previous_result;
+ BinaryOpStub::decode_types_from_minor_key(
+ key, &previous_left, &previous_right, &previous_result);
+
+ BinaryOpIC::TypeInfo new_left = InputState(previous_left, left, op);
+ BinaryOpIC::TypeInfo new_right = InputState(previous_right, right, op);
+ BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED;
+
+ // STRING is only used for ADD operations.
+ if ((new_left == BinaryOpIC::STRING || new_right == BinaryOpIC::STRING) &&
+ op != Token::ADD) {
+ new_left = new_right = BinaryOpIC::GENERIC;
+ }
+
+ BinaryOpIC::TypeInfo new_overall = Max(new_left, new_right);
+ BinaryOpIC::TypeInfo previous_overall = Max(previous_left, previous_right);
+
+ Maybe<int> previous_fixed_right_arg =
+ BinaryOpStub::decode_fixed_right_arg_from_minor_key(key);
+
+ int32_t value;
+ bool new_has_fixed_right_arg =
+ op == Token::MOD &&
+ right->ToInt32(&value) &&
+ BinaryOpStub::can_encode_arg_value(value) &&
+ (previous_overall == BinaryOpIC::UNINITIALIZED ||
+ (previous_fixed_right_arg.has_value &&
+ previous_fixed_right_arg.value == value));
+ Maybe<int32_t> new_fixed_right_arg(
+ new_has_fixed_right_arg, new_has_fixed_right_arg ? value : 1);
+
+ if (previous_fixed_right_arg.has_value == new_fixed_right_arg.has_value) {
+ if (new_overall == BinaryOpIC::SMI && previous_overall == BinaryOpIC::SMI) {
+ if (op == Token::DIV ||
+ op == Token::MUL ||
+ op == Token::SHR ||
+ kSmiValueSize == 32) {
+ // Arithmetic on two Smi inputs has yielded a heap number.
+ // That is the only way to get here from the Smi stub.
+ // With 32-bit Smis, all overflows give heap numbers, but with
+ // 31-bit Smis, most operations overflow to int32 results.
+ result_type = BinaryOpIC::NUMBER;
+ } else {
+ // Other operations on SMIs that overflow yield int32s.
+ result_type = BinaryOpIC::INT32;
+ }
+ }
+ if (new_overall == BinaryOpIC::INT32 &&
+ previous_overall == BinaryOpIC::INT32) {
+ if (new_left == previous_left && new_right == previous_right) {
+ result_type = BinaryOpIC::NUMBER;
+ }
+ }
+ }
+
+ BinaryOpStub stub(key, new_left, new_right, result_type, new_fixed_right_arg);
+ Handle<Code> code = stub.GetCode(isolate);
+ if (!code.is_null()) {
+#ifdef DEBUG
+ if (FLAG_trace_ic) {
+ PrintF("[BinaryOpIC in ");
+ JavaScriptFrame::PrintTop(isolate, stdout, false, true);
+ PrintF(" ");
+ TraceBinaryOp(previous_left, previous_right, previous_fixed_right_arg,
+ previous_result);
+ PrintF(" => ");
+ TraceBinaryOp(new_left, new_right, new_fixed_right_arg, result_type);
+ PrintF(" #%s @ %p]\n", Token::Name(op), static_cast<void*>(*code));
+ }
+#endif
+ BinaryOpIC ic(isolate);
+ ic.patch(*code);
+
+ // Activate inlined smi code.
+ if (previous_overall == BinaryOpIC::UNINITIALIZED) {
+ PatchInlinedSmiCode(ic.address(), ENABLE_INLINED_SMI_CHECK);
+ }
+ }
+
+ Handle<JSBuiltinsObject> builtins(isolate->js_builtins_object());
+ Object* builtin = NULL; // Initialization calms down the compiler.
+ switch (op) {
+ case Token::ADD:
+ builtin = builtins->javascript_builtin(Builtins::ADD);
+ break;
+ case Token::SUB:
+ builtin = builtins->javascript_builtin(Builtins::SUB);
+ break;
+ case Token::MUL:
+ builtin = builtins->javascript_builtin(Builtins::MUL);
+ break;
+ case Token::DIV:
+ builtin = builtins->javascript_builtin(Builtins::DIV);
+ break;
+ case Token::MOD:
+ builtin = builtins->javascript_builtin(Builtins::MOD);
+ break;
+ case Token::BIT_AND:
+ builtin = builtins->javascript_builtin(Builtins::BIT_AND);
+ break;
+ case Token::BIT_OR:
+ builtin = builtins->javascript_builtin(Builtins::BIT_OR);
+ break;
+ case Token::BIT_XOR:
+ builtin = builtins->javascript_builtin(Builtins::BIT_XOR);
+ break;
+ case Token::SHR:
+ builtin = builtins->javascript_builtin(Builtins::SHR);
+ break;
+ case Token::SAR:
+ builtin = builtins->javascript_builtin(Builtins::SAR);
+ break;
+ case Token::SHL:
+ builtin = builtins->javascript_builtin(Builtins::SHL);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ Handle<JSFunction> builtin_function(JSFunction::cast(builtin), isolate);
+
+ bool caught_exception;
+ Handle<Object> builtin_args[] = { right };
+ Handle<Object> result = Execution::Call(builtin_function,
+ left,
+ ARRAY_SIZE(builtin_args),
+ builtin_args,
+ &caught_exception);
+ if (caught_exception) {
+ return Failure::Exception();
+ }
+ return *result;
+}
+
+
+Code* CompareIC::GetRawUninitialized(Token::Value op) {
+ ICCompareStub stub(op, UNINITIALIZED, UNINITIALIZED, UNINITIALIZED);
+ Code* code = NULL;
+ CHECK(stub.FindCodeInCache(&code, Isolate::Current()));
+ return code;
+}
+
+
+Handle<Code> CompareIC::GetUninitialized(Isolate* isolate, Token::Value op) {
+ ICCompareStub stub(op, UNINITIALIZED, UNINITIALIZED, UNINITIALIZED);
+ return stub.GetCode(isolate);
+}
+
+
+const char* CompareIC::GetStateName(State state) {
+ switch (state) {
+ case UNINITIALIZED: return "UNINITIALIZED";
+ case SMI: return "SMI";
+ case NUMBER: return "NUMBER";
+ case INTERNALIZED_STRING: return "INTERNALIZED_STRING";
+ case STRING: return "STRING";
+ case UNIQUE_NAME: return "UNIQUE_NAME";
+ case OBJECT: return "OBJECT";
+ case KNOWN_OBJECT: return "KNOWN_OBJECT";
+ case GENERIC: return "GENERIC";
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+Handle<Type> CompareIC::StateToType(
+ Isolate* isolate,
+ CompareIC::State state,
+ Handle<Map> map) {
+ switch (state) {
+ case CompareIC::UNINITIALIZED:
+ return handle(Type::None(), isolate);
+ case CompareIC::SMI:
+ return handle(Type::Smi(), isolate);
+ case CompareIC::NUMBER:
+ return handle(Type::Number(), isolate);
+ case CompareIC::STRING:
+ return handle(Type::String(), isolate);
+ case CompareIC::INTERNALIZED_STRING:
+ return handle(Type::InternalizedString(), isolate);
+ case CompareIC::UNIQUE_NAME:
+ return handle(Type::UniqueName(), isolate);
+ case CompareIC::OBJECT:
+ return handle(Type::Receiver(), isolate);
+ case CompareIC::KNOWN_OBJECT:
+ return handle(
+ map.is_null() ? Type::Receiver() : Type::Class(map), isolate);
+ case CompareIC::GENERIC:
+ return handle(Type::Any(), isolate);
+ }
+ UNREACHABLE();
+ return Handle<Type>();
+}
+
+
+void CompareIC::StubInfoToType(int stub_minor_key,
+ Handle<Type>* left_type,
+ Handle<Type>* right_type,
+ Handle<Type>* overall_type,
+ Handle<Map> map,
+ Isolate* isolate) {
+ State left_state, right_state, handler_state;
+ ICCompareStub::DecodeMinorKey(stub_minor_key, &left_state, &right_state,
+ &handler_state, NULL);
+ *left_type = StateToType(isolate, left_state);
+ *right_type = StateToType(isolate, right_state);
+ *overall_type = StateToType(isolate, handler_state, map);
+}
+
+
+CompareIC::State CompareIC::NewInputState(State old_state,
+ Handle<Object> value) {
+ switch (old_state) {
+ case UNINITIALIZED:
+ if (value->IsSmi()) return SMI;
+ if (value->IsHeapNumber()) return NUMBER;
+ if (value->IsInternalizedString()) return INTERNALIZED_STRING;
+ if (value->IsString()) return STRING;
+ if (value->IsSymbol()) return UNIQUE_NAME;
+ if (value->IsJSObject()) return OBJECT;
+ break;
+ case SMI:
+ if (value->IsSmi()) return SMI;
+ if (value->IsHeapNumber()) return NUMBER;
+ break;
+ case NUMBER:
+ if (value->IsNumber()) return NUMBER;
+ break;
+ case INTERNALIZED_STRING:
+ if (value->IsInternalizedString()) return INTERNALIZED_STRING;
+ if (value->IsString()) return STRING;
+ if (value->IsSymbol()) return UNIQUE_NAME;
+ break;
+ case STRING:
+ if (value->IsString()) return STRING;
+ break;
+ case UNIQUE_NAME:
+ if (value->IsUniqueName()) return UNIQUE_NAME;
+ break;
+ case OBJECT:
+ if (value->IsJSObject()) return OBJECT;
+ break;
+ case GENERIC:
+ break;
+ case KNOWN_OBJECT:
+ UNREACHABLE();
+ break;
+ }
+ return GENERIC;
+}
+
+
+CompareIC::State CompareIC::TargetState(State old_state,
+ State old_left,
+ State old_right,
+ bool has_inlined_smi_code,
+ Handle<Object> x,
+ Handle<Object> y) {
+ switch (old_state) {
+ case UNINITIALIZED:
+ if (x->IsSmi() && y->IsSmi()) return SMI;
+ if (x->IsNumber() && y->IsNumber()) return NUMBER;
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ // Ordered comparisons treat undefined as NaN, so the
+ // NUMBER stub will do the right thing.
+ if ((x->IsNumber() && y->IsUndefined()) ||
+ (y->IsNumber() && x->IsUndefined())) {
+ return NUMBER;
+ }
+ }
+ if (x->IsInternalizedString() && y->IsInternalizedString()) {
+ // We compare internalized strings as plain ones if we need to determine
+ // the order in a non-equality compare.
+ return Token::IsEqualityOp(op_) ? INTERNALIZED_STRING : STRING;
+ }
+ if (x->IsString() && y->IsString()) return STRING;
+ if (!Token::IsEqualityOp(op_)) return GENERIC;
+ if (x->IsUniqueName() && y->IsUniqueName()) return UNIQUE_NAME;
+ if (x->IsJSObject() && y->IsJSObject()) {
+ if (Handle<JSObject>::cast(x)->map() ==
+ Handle<JSObject>::cast(y)->map()) {
+ return KNOWN_OBJECT;
+ } else {
+ return OBJECT;
+ }
+ }
+ return GENERIC;
+ case SMI:
+ return x->IsNumber() && y->IsNumber() ? NUMBER : GENERIC;
+ case INTERNALIZED_STRING:
+ ASSERT(Token::IsEqualityOp(op_));
+ if (x->IsString() && y->IsString()) return STRING;
+ if (x->IsUniqueName() && y->IsUniqueName()) return UNIQUE_NAME;
+ return GENERIC;
+ case NUMBER:
+ // If the failure was due to one side changing from smi to heap number,
+ // then keep the state (if other changed at the same time, we will get
+ // a second miss and then go to generic).
+ if (old_left == SMI && x->IsHeapNumber()) return NUMBER;
+ if (old_right == SMI && y->IsHeapNumber()) return NUMBER;
+ return GENERIC;
+ case KNOWN_OBJECT:
+ ASSERT(Token::IsEqualityOp(op_));
+ if (x->IsJSObject() && y->IsJSObject()) return OBJECT;
+ return GENERIC;
+ case STRING:
+ case UNIQUE_NAME:
+ case OBJECT:
+ case GENERIC:
+ return GENERIC;
+ }
+ UNREACHABLE();
+ return GENERIC; // Make the compiler happy.
+}
+
+
+void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) {
+ HandleScope scope(isolate());
+ State previous_left, previous_right, previous_state;
+ ICCompareStub::DecodeMinorKey(target()->stub_info(), &previous_left,
+ &previous_right, &previous_state, NULL);
+ State new_left = NewInputState(previous_left, x);
+ State new_right = NewInputState(previous_right, y);
+ State state = TargetState(previous_state, previous_left, previous_right,
+ HasInlinedSmiCode(address()), x, y);
+ ICCompareStub stub(op_, new_left, new_right, state);
+ if (state == KNOWN_OBJECT) {
+ stub.set_known_map(
+ Handle<Map>(Handle<JSObject>::cast(x)->map(), isolate()));
+ }
+ set_target(*stub.GetCode(isolate()));
+
+#ifdef DEBUG
+ if (FLAG_trace_ic) {
+ PrintF("[CompareIC in ");
+ JavaScriptFrame::PrintTop(isolate(), stdout, false, true);
+ PrintF(" ((%s+%s=%s)->(%s+%s=%s))#%s @ %p]\n",
+ GetStateName(previous_left),
+ GetStateName(previous_right),
+ GetStateName(previous_state),
+ GetStateName(new_left),
+ GetStateName(new_right),
+ GetStateName(state),
+ Token::Name(op_),
+ static_cast<void*>(*stub.GetCode(isolate())));
+ }
+#endif
+
+ // Activate inlined smi code.
+ if (previous_state == UNINITIALIZED) {
+ PatchInlinedSmiCode(address(), ENABLE_INLINED_SMI_CHECK);
+ }
+}
+
+
+// Used from ICCompareStub::GenerateMiss in code-stubs-<arch>.cc.
+RUNTIME_FUNCTION(Code*, CompareIC_Miss) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ CompareIC ic(isolate, static_cast<Token::Value>(args.smi_at(2)));
+ ic.UpdateCaches(args.at<Object>(0), args.at<Object>(1));
+ return ic.target();
+}
+
+
+void CompareNilIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ Code::ExtraICState state = target->extended_extra_ic_state();
+
+ CompareNilICStub stub(state, HydrogenCodeStub::UNINITIALIZED);
+ stub.ClearState();
+
+ Code* code = NULL;
+ CHECK(stub.FindCodeInCache(&code, target->GetIsolate()));
+
+ SetTargetAtAddress(address, code);
+}
+
+
+MaybeObject* CompareNilIC::DoCompareNilSlow(NilValue nil,
+ Handle<Object> object) {
+ if (object->IsNull() || object->IsUndefined()) {
+ return Smi::FromInt(true);
+ }
+ return Smi::FromInt(object->IsUndetectableObject());
+}
+
+
+MaybeObject* CompareNilIC::CompareNil(Handle<Object> object) {
+ Code::ExtraICState extra_ic_state = target()->extended_extra_ic_state();
+
+ CompareNilICStub stub(extra_ic_state);
+
+ // Extract the current supported types from the patched IC and calculate what
+ // types must be supported as a result of the miss.
+ bool already_monomorphic = stub.IsMonomorphic();
+
+ stub.UpdateStatus(object);
+
+ NilValue nil = stub.GetNilValue();
+
+ // Find or create the specialized stub to support the new set of types.
+ Handle<Code> code;
+ if (stub.IsMonomorphic()) {
+ Handle<Map> monomorphic_map(already_monomorphic
+ ? target()->FindFirstMap()
+ : HeapObject::cast(*object)->map());
+ code = isolate()->stub_cache()->ComputeCompareNil(monomorphic_map, stub);
+ } else {
+ code = stub.GetCode(isolate());
+ }
+ set_target(*code);
+ return DoCompareNilSlow(nil, object);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss) {
+ HandleScope scope(isolate);
+ Handle<Object> object = args.at<Object>(0);
+ CompareNilIC ic(isolate);
+ return ic.CompareNil(object);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Unreachable) {
+ UNREACHABLE();
+ CHECK(false);
+ return isolate->heap()->undefined_value();
+}
+
+
+MaybeObject* ToBooleanIC::ToBoolean(Handle<Object> object,
+ Code::ExtraICState extra_ic_state) {
+ ToBooleanStub stub(extra_ic_state);
+ bool to_boolean_value = stub.UpdateStatus(object);
+ Handle<Code> code = stub.GetCode(isolate());
+ set_target(*code);
+ return Smi::FromInt(to_boolean_value ? 1 : 0);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, ToBooleanIC_Miss) {
+ ASSERT(args.length() == 1);
+ HandleScope scope(isolate);
+ Handle<Object> object = args.at<Object>(0);
+ ToBooleanIC ic(isolate);
+ Code::ExtraICState ic_state = ic.target()->extended_extra_ic_state();
+ return ic.ToBoolean(object, ic_state);
+}
+
+
+static const Address IC_utilities[] = {
+#define ADDR(name) FUNCTION_ADDR(name),
+ IC_UTIL_LIST(ADDR)
+ NULL
+#undef ADDR
+};
+
+
+Address IC::AddressFromUtilityId(IC::UtilityId id) {
+ return IC_utilities[id];
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/ic.h b/chromium/v8/src/ic.h
new file mode 100644
index 00000000000..fcf0de58f1a
--- /dev/null
+++ b/chromium/v8/src/ic.h
@@ -0,0 +1,858 @@
+// Copyright 2012 the V8 project authors. 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_IC_H_
+#define V8_IC_H_
+
+#include "macro-assembler.h"
+#include "type-info.h"
+
+namespace v8 {
+namespace internal {
+
+
+// IC_UTIL_LIST defines all utility functions called from generated
+// inline caching code. The argument for the macro, ICU, is the function name.
+#define IC_UTIL_LIST(ICU) \
+ ICU(LoadIC_Miss) \
+ ICU(KeyedLoadIC_Miss) \
+ ICU(KeyedLoadIC_MissForceGeneric) \
+ ICU(CallIC_Miss) \
+ ICU(KeyedCallIC_Miss) \
+ ICU(StoreIC_Miss) \
+ ICU(StoreIC_ArrayLength) \
+ ICU(StoreIC_Slow) \
+ ICU(SharedStoreIC_ExtendStorage) \
+ ICU(KeyedStoreIC_Miss) \
+ ICU(KeyedStoreIC_MissForceGeneric) \
+ ICU(KeyedStoreIC_Slow) \
+ /* Utilities for IC stubs. */ \
+ ICU(StoreCallbackProperty) \
+ ICU(LoadPropertyWithInterceptorOnly) \
+ ICU(LoadPropertyWithInterceptorForLoad) \
+ ICU(LoadPropertyWithInterceptorForCall) \
+ ICU(KeyedLoadPropertyWithInterceptor) \
+ ICU(StoreInterceptorProperty) \
+ ICU(BinaryOp_Patch) \
+ ICU(CompareIC_Miss) \
+ ICU(CompareNilIC_Miss) \
+ ICU(Unreachable) \
+ ICU(ToBooleanIC_Miss)
+//
+// IC is the base class for LoadIC, StoreIC, CallIC, KeyedLoadIC,
+// and KeyedStoreIC.
+//
+class IC {
+ public:
+ // The ids for utility called from the generated code.
+ enum UtilityId {
+ #define CONST_NAME(name) k##name,
+ IC_UTIL_LIST(CONST_NAME)
+ #undef CONST_NAME
+ kUtilityCount
+ };
+
+ // Looks up the address of the named utility.
+ static Address AddressFromUtilityId(UtilityId id);
+
+ // Alias the inline cache state type to make the IC code more readable.
+ typedef InlineCacheState State;
+
+ // The IC code is either invoked with no extra frames on the stack
+ // or with a single extra frame for supporting calls.
+ enum FrameDepth {
+ NO_EXTRA_FRAME = 0,
+ EXTRA_CALL_FRAME = 1
+ };
+
+ // 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() const { return GetTargetAtAddress(address()); }
+ inline Address address() const;
+
+ // Compute the current IC state based on the target stub, receiver and name.
+ static State StateFrom(Code* target, Object* receiver, Object* name);
+
+ // Clear the inline cache to initial state.
+ static void Clear(Address address);
+
+ // Computes the reloc info for this IC. This is a fairly expensive
+ // operation as it has to search through the heap to find the code
+ // object that contains this IC site.
+ RelocInfo::Mode ComputeMode();
+
+ // Returns if this IC is for contextual (no explicit receiver)
+ // access to properties.
+ bool IsUndeclaredGlobal(Handle<Object> receiver) {
+ if (receiver->IsGlobalObject()) {
+ return SlowIsUndeclaredGlobal();
+ } else {
+ ASSERT(!SlowIsUndeclaredGlobal());
+ return false;
+ }
+ }
+
+ bool SlowIsUndeclaredGlobal() {
+ return ComputeMode() == RelocInfo::CODE_TARGET_CONTEXT;
+ }
+
+ // Determines which map must be used for keeping the code stub.
+ // These methods should not be called with undefined or null.
+ static inline InlineCacheHolderFlag GetCodeCacheForObject(Object* object,
+ JSObject* holder);
+ static inline InlineCacheHolderFlag GetCodeCacheForObject(JSObject* object,
+ JSObject* holder);
+ static inline JSObject* GetCodeCacheHolder(Isolate* isolate,
+ Object* object,
+ InlineCacheHolderFlag holder);
+
+ protected:
+ Address fp() const { return fp_; }
+ Address pc() const { return *pc_address_; }
+ Isolate* isolate() const { return isolate_; }
+
+#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() 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,
+ Code* new_target);
+#endif
+
+ Failure* TypeError(const char* type,
+ Handle<Object> object,
+ Handle<Object> key);
+ Failure* ReferenceError(const char* type, Handle<String> name);
+
+ // 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(Address address, Code* target, Code* old_target);
+
+ virtual void UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode) {
+ set_target(*handler);
+ }
+ bool UpdatePolymorphicIC(State state,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Code> code,
+ StrictModeFlag strict_mode);
+
+ virtual Handle<Code> ComputePolymorphicIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name,
+ StrictModeFlag strict_mode) {
+ UNREACHABLE();
+ return Handle<Code>::null();
+ };
+
+ void CopyICToMegamorphicCache(Handle<String> name);
+ bool IsTransitionedMapOfMonomorphicTarget(Map* receiver_map);
+ void PatchCache(State state,
+ StrictModeFlag strict_mode,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Code> code);
+ virtual void UpdateMegamorphicCache(Map* map, Name* name, Code* code);
+ virtual Handle<Code> megamorphic_stub() {
+ UNREACHABLE();
+ return Handle<Code>::null();
+ }
+ virtual Handle<Code> megamorphic_stub_strict() {
+ UNREACHABLE();
+ return Handle<Code>::null();
+ }
+ virtual Handle<Code> generic_stub() const {
+ UNREACHABLE();
+ return Handle<Code>::null();
+ }
+ virtual Handle<Code> generic_stub_strict() const {
+ UNREACHABLE();
+ return Handle<Code>::null();
+ }
+
+ private:
+ // Frame pointer for the frame that uses (calls) the IC.
+ Address fp_;
+
+ // All access to the program counter of an IC structure is indirect
+ // to make the code GC safe. This feature is crucial since
+ // GetProperty and SetProperty are called and they in turn might
+ // invoke the garbage collector.
+ Address* pc_address_;
+
+ Isolate* isolate_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(IC);
+};
+
+
+// An IC_Utility encapsulates IC::UtilityId. It exists mainly because you
+// cannot make forward declarations to an enum.
+class IC_Utility {
+ public:
+ explicit IC_Utility(IC::UtilityId id)
+ : address_(IC::AddressFromUtilityId(id)), id_(id) {}
+
+ Address address() const { return address_; }
+
+ IC::UtilityId id() const { return id_; }
+ private:
+ Address address_;
+ IC::UtilityId id_;
+};
+
+
+class CallICBase: public IC {
+ public:
+ class Contextual: public BitField<bool, 0, 1> {};
+ class StringStubState: public BitField<StringStubFeedback, 1, 1> {};
+
+ // 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:
+ 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);
+
+ // 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.
+ 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.
+ 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;
+};
+
+
+class CallIC: public CallICBase {
+ public:
+ explicit CallIC(Isolate* isolate) : CallICBase(Code::CALL_IC, isolate) {
+ ASSERT(target()->is_call_stub());
+ }
+
+ // Code generator routines.
+ static void GenerateInitialize(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_state) {
+ GenerateMiss(masm, argc, extra_state);
+ }
+
+ static void GenerateMiss(MacroAssembler* masm,
+ int argc,
+ 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) {
+ CallICBase::GenerateNormal(masm, argc);
+ GenerateMiss(masm, argc, Code::kNoExtraICState);
+ }
+};
+
+
+class KeyedCallIC: public CallICBase {
+ public:
+ explicit KeyedCallIC(Isolate* isolate)
+ : CallICBase(Code::KEYED_CALL_IC, isolate) {
+ ASSERT(target()->is_keyed_call_stub());
+ }
+
+ MUST_USE_RESULT MaybeObject* LoadFunction(State state,
+ Handle<Object> object,
+ Handle<Object> key);
+
+ // Code generator routines.
+ static void GenerateInitialize(MacroAssembler* masm, int argc) {
+ GenerateMiss(masm, 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);
+};
+
+
+class LoadIC: public IC {
+ public:
+ explicit LoadIC(FrameDepth depth, Isolate* isolate) : IC(depth, isolate) {
+ ASSERT(target()->is_load_stub() || target()->is_keyed_load_stub());
+ }
+
+ // Code generator routines.
+ static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); }
+ static void GeneratePreMonomorphic(MacroAssembler* masm) {
+ GenerateMiss(masm);
+ }
+ static void GenerateMiss(MacroAssembler* masm);
+ static void GenerateMegamorphic(MacroAssembler* masm);
+ static void GenerateNormal(MacroAssembler* masm);
+ static void GenerateRuntimeGetProperty(MacroAssembler* masm);
+
+ MUST_USE_RESULT MaybeObject* Load(State state,
+ Handle<Object> object,
+ Handle<String> name);
+
+ protected:
+ virtual Code::Kind kind() const { return Code::LOAD_IC; }
+
+ virtual Handle<Code> generic_stub() const {
+ return isolate()->builtins()->LoadIC_Slow();
+ }
+
+ virtual Handle<Code> megamorphic_stub() {
+ return isolate()->builtins()->LoadIC_Megamorphic();
+ }
+
+ // Update the inline cache and the global stub cache based on the
+ // lookup result.
+ void UpdateCaches(LookupResult* lookup,
+ State state,
+ Handle<Object> object,
+ Handle<String> name);
+
+ virtual void UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode);
+
+ virtual Handle<Code> ComputePolymorphicIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name,
+ StrictModeFlag strict_mode);
+
+ virtual Handle<Code> ComputeLoadHandler(LookupResult* lookup,
+ Handle<JSObject> receiver,
+ Handle<String> name);
+
+ private:
+ // Stub accessors.
+ static Handle<Code> initialize_stub() {
+ return Isolate::Current()->builtins()->LoadIC_Initialize();
+ }
+ virtual Handle<Code> pre_monomorphic_stub() {
+ return isolate()->builtins()->LoadIC_PreMonomorphic();
+ }
+
+ static void Clear(Address address, Code* target);
+
+ friend class IC;
+};
+
+
+enum ICMissMode {
+ MISS_FORCE_GENERIC,
+ MISS
+};
+
+
+class KeyedLoadIC: public LoadIC {
+ public:
+ explicit KeyedLoadIC(FrameDepth depth, Isolate* isolate)
+ : LoadIC(depth, isolate) {
+ ASSERT(target()->is_keyed_load_stub());
+ }
+
+ MUST_USE_RESULT MaybeObject* Load(State state,
+ Handle<Object> object,
+ Handle<Object> key,
+ ICMissMode force_generic);
+
+ // Code generator routines.
+ static void GenerateMiss(MacroAssembler* masm, ICMissMode force_generic);
+ static void GenerateRuntimeGetProperty(MacroAssembler* masm);
+ static void GenerateInitialize(MacroAssembler* masm) {
+ GenerateMiss(masm, MISS);
+ }
+ static void GeneratePreMonomorphic(MacroAssembler* masm) {
+ GenerateMiss(masm, MISS);
+ }
+ static void GenerateGeneric(MacroAssembler* masm);
+ static void GenerateString(MacroAssembler* masm);
+ static void GenerateIndexedInterceptor(MacroAssembler* masm);
+ static void GenerateNonStrictArguments(MacroAssembler* masm);
+
+ // Bit mask to be tested against bit field for the cases when
+ // generic stub should go into slow case.
+ // Access check is necessary explicitly since generic stub does not perform
+ // map checks.
+ static const int kSlowCaseBitFieldMask =
+ (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor);
+
+ protected:
+ virtual Code::Kind kind() const { return Code::KEYED_LOAD_IC; }
+
+ Handle<Code> LoadElementStub(Handle<JSObject> receiver);
+
+ virtual Handle<Code> megamorphic_stub() {
+ return isolate()->builtins()->KeyedLoadIC_Generic();
+ }
+ virtual Handle<Code> generic_stub() const {
+ return isolate()->builtins()->KeyedLoadIC_Generic();
+ }
+
+ // Update the inline cache.
+ virtual void UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode);
+ virtual Handle<Code> ComputeLoadHandler(LookupResult* lookup,
+ Handle<JSObject> receiver,
+ Handle<String> name);
+ virtual void UpdateMegamorphicCache(Map* map, Name* name, Code* code) { }
+
+ private:
+ // Stub accessors.
+ static Handle<Code> initialize_stub() {
+ return Isolate::Current()->builtins()->KeyedLoadIC_Initialize();
+ }
+ virtual Handle<Code> pre_monomorphic_stub() {
+ return isolate()->builtins()->KeyedLoadIC_PreMonomorphic();
+ }
+ Handle<Code> indexed_interceptor_stub() {
+ return isolate()->builtins()->KeyedLoadIC_IndexedInterceptor();
+ }
+ Handle<Code> non_strict_arguments_stub() {
+ return isolate()->builtins()->KeyedLoadIC_NonStrictArguments();
+ }
+ Handle<Code> string_stub() {
+ return isolate()->builtins()->KeyedLoadIC_String();
+ }
+
+ static void Clear(Address address, Code* target);
+
+ friend class IC;
+};
+
+
+class StoreIC: public IC {
+ public:
+ StoreIC(FrameDepth depth, Isolate* isolate) : IC(depth, isolate) {
+ ASSERT(target()->is_store_stub() || target()->is_keyed_store_stub());
+ }
+
+ // Code generators for stub routines. Only called once at startup.
+ static void GenerateSlow(MacroAssembler* masm);
+ static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); }
+ static void GenerateMiss(MacroAssembler* masm);
+ static void GenerateMegamorphic(MacroAssembler* masm,
+ StrictModeFlag strict_mode);
+ static void GenerateNormal(MacroAssembler* masm);
+ static void GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode);
+
+ MUST_USE_RESULT MaybeObject* Store(
+ State state,
+ StrictModeFlag strict_mode,
+ Handle<Object> object,
+ Handle<String> name,
+ Handle<Object> value,
+ JSReceiver::StoreFromKeyed store_mode =
+ JSReceiver::CERTAINLY_NOT_STORE_FROM_KEYED);
+
+ protected:
+ virtual Code::Kind kind() const { return Code::STORE_IC; }
+ virtual Handle<Code> megamorphic_stub() {
+ return isolate()->builtins()->StoreIC_Megamorphic();
+ }
+ // Stub accessors.
+ virtual Handle<Code> megamorphic_stub_strict() {
+ return isolate()->builtins()->StoreIC_Megamorphic_Strict();
+ }
+ virtual Handle<Code> generic_stub() const {
+ return isolate()->builtins()->StoreIC_Generic();
+ }
+ virtual Handle<Code> generic_stub_strict() const {
+ return isolate()->builtins()->StoreIC_Generic_Strict();
+ }
+ virtual Handle<Code> global_proxy_stub() {
+ return isolate()->builtins()->StoreIC_GlobalProxy();
+ }
+ virtual Handle<Code> global_proxy_stub_strict() {
+ return isolate()->builtins()->StoreIC_GlobalProxy_Strict();
+ }
+
+ virtual void UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode);
+
+ virtual Handle<Code> ComputePolymorphicIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name,
+ StrictModeFlag strict_mode);
+
+ // Update the inline cache and the global stub cache based on the
+ // lookup result.
+ void UpdateCaches(LookupResult* lookup,
+ State state,
+ StrictModeFlag strict_mode,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value);
+ // 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.
+ virtual Handle<Code> ComputeStoreMonomorphic(LookupResult* lookup,
+ StrictModeFlag strict_mode,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value);
+
+ private:
+ void set_target(Code* code) {
+ // Strict mode must be preserved across IC patching.
+ ASSERT(Code::GetStrictMode(code->extra_ic_state()) ==
+ Code::GetStrictMode(target()->extra_ic_state()));
+ IC::set_target(code);
+ }
+
+ static Handle<Code> initialize_stub() {
+ return Isolate::Current()->builtins()->StoreIC_Initialize();
+ }
+ static Handle<Code> initialize_stub_strict() {
+ return Isolate::Current()->builtins()->StoreIC_Initialize_Strict();
+ }
+ static void Clear(Address address, Code* target);
+
+ friend class IC;
+};
+
+
+enum KeyedStoreCheckMap {
+ kDontCheckMap,
+ kCheckMap
+};
+
+
+enum KeyedStoreIncrementLength {
+ kDontIncrementLength,
+ kIncrementLength
+};
+
+
+class KeyedStoreIC: public StoreIC {
+ public:
+ KeyedStoreIC(FrameDepth depth, Isolate* isolate)
+ : StoreIC(depth, isolate) {
+ ASSERT(target()->is_keyed_store_stub());
+ }
+
+ MUST_USE_RESULT MaybeObject* Store(State state,
+ StrictModeFlag strict_mode,
+ Handle<Object> object,
+ Handle<Object> name,
+ Handle<Object> value,
+ ICMissMode force_generic);
+
+ // Code generators for stub routines. Only called once at startup.
+ static void GenerateInitialize(MacroAssembler* masm) {
+ GenerateMiss(masm, MISS);
+ }
+ static void GenerateMiss(MacroAssembler* masm, ICMissMode force_generic);
+ static void GenerateSlow(MacroAssembler* masm);
+ static void GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode);
+ static void GenerateGeneric(MacroAssembler* masm, StrictModeFlag strict_mode);
+ static void GenerateNonStrictArguments(MacroAssembler* masm);
+
+ protected:
+ virtual Code::Kind kind() const { return Code::KEYED_STORE_IC; }
+
+ virtual Handle<Code> ComputeStoreMonomorphic(LookupResult* lookup,
+ StrictModeFlag strict_mode,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value);
+ virtual void UpdateMegamorphicCache(Map* map, Name* name, Code* code) { }
+
+ virtual Handle<Code> megamorphic_stub() {
+ return isolate()->builtins()->KeyedStoreIC_Generic();
+ }
+ virtual Handle<Code> megamorphic_stub_strict() {
+ return isolate()->builtins()->KeyedStoreIC_Generic_Strict();
+ }
+
+ Handle<Code> StoreElementStub(Handle<JSObject> receiver,
+ KeyedAccessStoreMode store_mode,
+ StrictModeFlag strict_mode);
+
+ virtual void UpdateMonomorphicIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<String> name,
+ StrictModeFlag strict_mode);
+
+ private:
+ void set_target(Code* code) {
+ // Strict mode must be preserved across IC patching.
+ ASSERT(Code::GetStrictMode(code->extra_ic_state()) ==
+ Code::GetStrictMode(target()->extra_ic_state()));
+ IC::set_target(code);
+ }
+
+ // Stub accessors.
+ static Handle<Code> initialize_stub() {
+ return Isolate::Current()->builtins()->KeyedStoreIC_Initialize();
+ }
+ static Handle<Code> initialize_stub_strict() {
+ return Isolate::Current()->builtins()->KeyedStoreIC_Initialize_Strict();
+ }
+ Handle<Code> generic_stub() const {
+ return isolate()->builtins()->KeyedStoreIC_Generic();
+ }
+ 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);
+
+ KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver,
+ Handle<Object> key,
+ Handle<Object> value);
+
+ Handle<Map> ComputeTransitionedMap(Handle<JSObject> receiver,
+ KeyedAccessStoreMode store_mode);
+
+ friend class IC;
+};
+
+
+// Type Recording BinaryOpIC, that records the types of the inputs and outputs.
+class BinaryOpIC: public IC {
+ public:
+ enum TypeInfo {
+ UNINITIALIZED,
+ SMI,
+ INT32,
+ NUMBER,
+ ODDBALL,
+ STRING, // Only used for addition operation.
+ GENERIC
+ };
+
+ static void StubInfoToType(int minor_key,
+ Handle<Type>* left,
+ Handle<Type>* right,
+ Handle<Type>* result,
+ Isolate* isolate);
+
+ explicit BinaryOpIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
+
+ void patch(Code* code);
+
+ static const char* GetName(TypeInfo type_info);
+
+ static State ToState(TypeInfo type_info);
+
+ private:
+ static Handle<Type> TypeInfoToType(TypeInfo binary_type, Isolate* isolate);
+};
+
+
+class CompareIC: public IC {
+ public:
+ // The type/state lattice is defined by the following inequations:
+ // UNINITIALIZED < ...
+ // ... < GENERIC
+ // SMI < NUMBER
+ // INTERNALIZED_STRING < STRING
+ // KNOWN_OBJECT < OBJECT
+ enum State {
+ UNINITIALIZED,
+ SMI,
+ NUMBER,
+ STRING,
+ INTERNALIZED_STRING,
+ UNIQUE_NAME, // Symbol or InternalizedString
+ OBJECT, // JSObject
+ KNOWN_OBJECT, // JSObject with specific map (faster check)
+ GENERIC
+ };
+
+ static State NewInputState(State old_state, Handle<Object> value);
+
+ static Handle<Type> StateToType(Isolate* isolate,
+ State state,
+ Handle<Map> map = Handle<Map>());
+
+ static void StubInfoToType(int stub_minor_key,
+ Handle<Type>* left_type,
+ Handle<Type>* right_type,
+ Handle<Type>* overall_type,
+ Handle<Map> map,
+ Isolate* isolate);
+
+ CompareIC(Isolate* isolate, Token::Value op)
+ : IC(EXTRA_CALL_FRAME, isolate), op_(op) { }
+
+ // Update the inline cache for the given operands.
+ void UpdateCaches(Handle<Object> x, Handle<Object> y);
+
+
+ // Factory method for getting an uninitialized compare stub.
+ static Handle<Code> GetUninitialized(Isolate* isolate, Token::Value op);
+
+ // Helper function for computing the condition for a compare operation.
+ static Condition ComputeCondition(Token::Value op);
+
+ static const char* GetStateName(State state);
+
+ private:
+ static bool HasInlinedSmiCode(Address address);
+
+ State TargetState(State old_state,
+ State old_left,
+ State old_right,
+ bool has_inlined_smi_code,
+ Handle<Object> x,
+ Handle<Object> y);
+
+ bool strict() const { return op_ == Token::EQ_STRICT; }
+ Condition GetCondition() const { return ComputeCondition(op_); }
+
+ static Code* GetRawUninitialized(Token::Value op);
+
+ static void Clear(Address address, Code* target);
+
+ Token::Value op_;
+
+ friend class IC;
+};
+
+
+class CompareNilIC: public IC {
+ public:
+ explicit CompareNilIC(Isolate* isolate) : IC(EXTRA_CALL_FRAME, isolate) {}
+
+ MUST_USE_RESULT MaybeObject* CompareNil(Handle<Object> object);
+
+ static Handle<Code> GetUninitialized();
+
+ static void Clear(Address address, Code* target);
+
+ static MUST_USE_RESULT MaybeObject* DoCompareNilSlow(NilValue nil,
+ Handle<Object> object);
+};
+
+
+class ToBooleanIC: public IC {
+ public:
+ explicit ToBooleanIC(Isolate* isolate) : IC(EXTRA_CALL_FRAME, isolate) { }
+
+ MaybeObject* ToBoolean(Handle<Object> object, Code::ExtraICState state);
+};
+
+
+// Helper for BinaryOpIC and CompareIC.
+enum InlinedSmiCheck { ENABLE_INLINED_SMI_CHECK, DISABLE_INLINED_SMI_CHECK };
+void PatchInlinedSmiCode(Address address, InlinedSmiCheck check);
+
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissFromStubFailure);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissFromStubFailure);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, UnaryOpIC_Miss);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreIC_MissFromStubFailure);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, ElementsTransitionAndStoreIC_Miss);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, ToBooleanIC_Miss);
+
+
+} } // namespace v8::internal
+
+#endif // V8_IC_H_
diff --git a/chromium/v8/src/icu_util.cc b/chromium/v8/src/icu_util.cc
new file mode 100644
index 00000000000..b9bd65edc69
--- /dev/null
+++ b/chromium/v8/src/icu_util.cc
@@ -0,0 +1,62 @@
+// Copyright 2013 the V8 project authors. 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 "icu_util.h"
+
+#if defined(_WIN32) && defined(V8_I18N_SUPPORT)
+#include <windows.h>
+
+#include "unicode/putil.h"
+#include "unicode/udata.h"
+
+#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
+#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll"
+#endif
+
+namespace v8 {
+
+namespace internal {
+
+bool InitializeICU() {
+#if defined(_WIN32) && defined(V8_I18N_SUPPORT)
+ // We expect to find the ICU data module alongside the current module.
+ HMODULE module = LoadLibraryA(ICU_UTIL_DATA_SHARED_MODULE_NAME);
+ if (!module) return false;
+
+ FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL);
+ if (!addr) return false;
+
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setCommonData(reinterpret_cast<void*>(addr), &err);
+ return err == U_ZERO_ERROR;
+#else
+ // Mac/Linux bundle the ICU data in.
+ return true;
+#endif
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/icu_util.h b/chromium/v8/src/icu_util.h
new file mode 100644
index 00000000000..478abce508c
--- /dev/null
+++ b/chromium/v8/src/icu_util.h
@@ -0,0 +1,42 @@
+// Copyright 2013 the V8 project authors. 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_ICU_UTIL_H_
+#define V8_ICU_UTIL_H_
+
+namespace v8 {
+
+namespace internal {
+
+// Call this function to load ICU's data tables for the current process. This
+// function should be called before ICU is used.
+bool InitializeICU();
+
+} } // namespace v8::internal
+
+#endif // V8_ICU_UTIL_H_
diff --git a/chromium/v8/src/incremental-marking-inl.h b/chromium/v8/src/incremental-marking-inl.h
new file mode 100644
index 00000000000..1c30383d521
--- /dev/null
+++ b/chromium/v8/src/incremental-marking-inl.h
@@ -0,0 +1,145 @@
+// Copyright 2012 the V8 project authors. 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) {
+ HeapObject* value_heap_obj = HeapObject::cast(value);
+ MarkBit value_bit = Marking::MarkBitFrom(value_heap_obj);
+ if (Marking::IsWhite(value_bit)) {
+ MarkBit obj_bit = Marking::MarkBitFrom(obj);
+ if (Marking::IsBlack(obj_bit)) {
+ MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
+ if (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR)) {
+ if (chunk->IsLeftOfProgressBar(slot)) {
+ WhiteToGreyAndPush(value_heap_obj, value_bit);
+ RestartIfNotMarking();
+ } else {
+ return false;
+ }
+ } else {
+ BlackToGreyAndUnshift(obj, obj_bit);
+ RestartIfNotMarking();
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ if (!is_compacting_) return false;
+ MarkBit obj_bit = Marking::MarkBitFrom(obj);
+ return Marking::IsBlack(obj_bit);
+}
+
+
+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)) {
+ MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
+ if (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR)) {
+ chunk->set_progress_bar(0);
+ }
+ 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_->PromotedSpaceSizeOfObjects()) {
+ // 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) {
+ PrintPID("Hurrying incremental marking because of lack of progress\n");
+ }
+ marking_speed_ = kMaxMarkingSpeed;
+ }
+ }
+
+ marking_deque_.UnshiftGrey(obj);
+}
+
+
+void IncrementalMarking::WhiteToGreyAndPush(HeapObject* obj, MarkBit mark_bit) {
+ Marking::WhiteToGrey(mark_bit);
+ marking_deque_.PushGrey(obj);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_INCREMENTAL_MARKING_INL_H_
diff --git a/chromium/v8/src/incremental-marking.cc b/chromium/v8/src/incremental-marking.cc
new file mode 100644
index 00000000000..df0f14a74ce
--- /dev/null
+++ b/chromium/v8/src/incremental-marking.cc
@@ -0,0 +1,1042 @@
+// Copyright 2012 the V8 project authors. 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 "objects-visiting.h"
+#include "objects-visiting-inl.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),
+ marking_speed_(0),
+ allocated_(0),
+ no_marking_scope_depth_(0),
+ unscanned_bytes_of_large_object_(0) {
+}
+
+
+void IncrementalMarking::TearDown() {
+ delete marking_deque_memory_;
+}
+
+
+void IncrementalMarking::RecordWriteSlow(HeapObject* obj,
+ Object** slot,
+ Object* value) {
+ if (BaseRecordWrite(obj, slot, value) && 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** slot,
+ Isolate* isolate) {
+ ASSERT(obj->IsHeapObject());
+ IncrementalMarking* marking = isolate->heap()->incremental_marking();
+ ASSERT(!marking->is_compacting_);
+
+ MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
+ int counter = chunk->write_barrier_counter();
+ if (counter < (MemoryChunk::kWriteBarrierCounterGranularity / 2)) {
+ marking->write_barriers_invoked_since_last_step_ +=
+ MemoryChunk::kWriteBarrierCounterGranularity -
+ chunk->write_barrier_counter();
+ chunk->set_write_barrier_counter(
+ MemoryChunk::kWriteBarrierCounterGranularity);
+ }
+
+ marking->RecordWrite(obj, slot, *slot);
+}
+
+
+void IncrementalMarking::RecordWriteForEvacuationFromCode(HeapObject* obj,
+ Object** slot,
+ Isolate* isolate) {
+ ASSERT(obj->IsHeapObject());
+ IncrementalMarking* marking = isolate->heap()->incremental_marking();
+ ASSERT(marking->is_compacting_);
+
+ MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
+ int counter = chunk->write_barrier_counter();
+ if (counter < (MemoryChunk::kWriteBarrierCounterGranularity / 2)) {
+ marking->write_barriers_invoked_since_last_step_ +=
+ MemoryChunk::kWriteBarrierCounterGranularity -
+ chunk->write_barrier_counter();
+ chunk->set_write_barrier_counter(
+ MemoryChunk::kWriteBarrierCounterGranularity);
+ }
+
+ 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)) {
+ 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));
+ }
+ }
+}
+
+
+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);
+ }
+}
+
+
+static inline void MarkBlackOrKeepGrey(HeapObject* heap_object,
+ MarkBit mark_bit,
+ int size) {
+ ASSERT(!Marking::IsImpossible(mark_bit));
+ if (mark_bit.Get()) return;
+ mark_bit.Set();
+ MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), size);
+ ASSERT(Marking::IsBlack(mark_bit));
+}
+
+
+static inline void MarkBlackOrKeepBlack(HeapObject* heap_object,
+ MarkBit mark_bit,
+ int size) {
+ ASSERT(!Marking::IsImpossible(mark_bit));
+ if (Marking::IsBlack(mark_bit)) return;
+ Marking::MarkBlack(mark_bit);
+ MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), size);
+ ASSERT(Marking::IsBlack(mark_bit));
+}
+
+
+class IncrementalMarkingMarkingVisitor
+ : public StaticMarkingVisitor<IncrementalMarkingMarkingVisitor> {
+ public:
+ static void Initialize() {
+ StaticMarkingVisitor<IncrementalMarkingMarkingVisitor>::Initialize();
+ table_.Register(kVisitFixedArray, &VisitFixedArrayIncremental);
+ table_.Register(kVisitNativeContext, &VisitNativeContextIncremental);
+ table_.Register(kVisitJSRegExp, &VisitJSRegExp);
+ }
+
+ static const int kProgressBarScanningChunk = 32 * 1024;
+
+ static void VisitFixedArrayIncremental(Map* map, HeapObject* object) {
+ MemoryChunk* chunk = MemoryChunk::FromAddress(object->address());
+ // TODO(mstarzinger): Move setting of the flag to the allocation site of
+ // the array. The visitor should just check the flag.
+ if (FLAG_use_marking_progress_bar &&
+ chunk->owner()->identity() == LO_SPACE) {
+ chunk->SetFlag(MemoryChunk::HAS_PROGRESS_BAR);
+ }
+ if (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR)) {
+ Heap* heap = map->GetHeap();
+ // When using a progress bar for large fixed arrays, scan only a chunk of
+ // the array and try to push it onto the marking deque again until it is
+ // fully scanned. Fall back to scanning it through to the end in case this
+ // fails because of a full deque.
+ int object_size = FixedArray::BodyDescriptor::SizeOf(map, object);
+ int start_offset = Max(FixedArray::BodyDescriptor::kStartOffset,
+ chunk->progress_bar());
+ int end_offset = Min(object_size,
+ start_offset + kProgressBarScanningChunk);
+ int already_scanned_offset = start_offset;
+ bool scan_until_end = false;
+ do {
+ VisitPointersWithAnchor(heap,
+ HeapObject::RawField(object, 0),
+ HeapObject::RawField(object, start_offset),
+ HeapObject::RawField(object, end_offset));
+ start_offset = end_offset;
+ end_offset = Min(object_size, end_offset + kProgressBarScanningChunk);
+ scan_until_end = heap->incremental_marking()->marking_deque()->IsFull();
+ } while (scan_until_end && start_offset < object_size);
+ chunk->set_progress_bar(start_offset);
+ if (start_offset < object_size) {
+ heap->incremental_marking()->marking_deque()->UnshiftGrey(object);
+ heap->incremental_marking()->NotifyIncompleteScanOfObject(
+ object_size - (start_offset - already_scanned_offset));
+ }
+ } else {
+ FixedArrayVisitor::Visit(map, object);
+ }
+ }
+
+ static void VisitNativeContextIncremental(Map* map, HeapObject* object) {
+ Context* context = Context::cast(object);
+
+ // We will mark cache black with a separate pass
+ // when we finish marking.
+ MarkObjectGreyDoNotEnqueue(context->normalized_map_cache());
+ VisitNativeContext(map, context);
+ }
+
+ static void VisitWeakCollection(Map* map, HeapObject* object) {
+ Heap* heap = map->GetHeap();
+ VisitPointers(heap,
+ HeapObject::RawField(object,
+ JSWeakCollection::kPropertiesOffset),
+ HeapObject::RawField(object, JSWeakCollection::kSize));
+ }
+
+ static void BeforeVisitingSharedFunctionInfo(HeapObject* object) {}
+
+ INLINE(static void VisitPointer(Heap* heap, Object** p)) {
+ Object* obj = *p;
+ if (obj->NonFailureIsHeapObject()) {
+ heap->mark_compact_collector()->RecordSlot(p, p, obj);
+ MarkObject(heap, obj);
+ }
+ }
+
+ INLINE(static void VisitPointers(Heap* heap, 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(heap, obj);
+ }
+ }
+ }
+
+ INLINE(static void VisitPointersWithAnchor(Heap* heap,
+ Object** anchor,
+ Object** start,
+ Object** end)) {
+ for (Object** p = start; p < end; p++) {
+ Object* obj = *p;
+ if (obj->NonFailureIsHeapObject()) {
+ heap->mark_compact_collector()->RecordSlot(anchor, p, obj);
+ MarkObject(heap, obj);
+ }
+ }
+ }
+
+ // Marks the object grey and pushes it on the marking stack.
+ INLINE(static void MarkObject(Heap* heap, Object* obj)) {
+ HeapObject* heap_object = HeapObject::cast(obj);
+ MarkBit mark_bit = Marking::MarkBitFrom(heap_object);
+ if (mark_bit.data_only()) {
+ MarkBlackOrKeepGrey(heap_object, mark_bit, heap_object->Size());
+ } else if (Marking::IsWhite(mark_bit)) {
+ heap->incremental_marking()->WhiteToGreyAndPush(heap_object, mark_bit);
+ }
+ }
+
+ // Marks the object black without pushing it on the marking stack.
+ // Returns true if object needed marking and false otherwise.
+ INLINE(static bool MarkObjectWithoutPush(Heap* heap, Object* obj)) {
+ HeapObject* heap_object = HeapObject::cast(obj);
+ MarkBit mark_bit = Marking::MarkBitFrom(heap_object);
+ if (Marking::IsWhite(mark_bit)) {
+ mark_bit.Set();
+ MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(),
+ heap_object->Size());
+ return true;
+ }
+ return false;
+ }
+};
+
+
+class IncrementalMarkingRootMarkingVisitor : public ObjectVisitor {
+ public:
+ explicit IncrementalMarkingRootMarkingVisitor(
+ IncrementalMarking* incremental_marking)
+ : 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()) {
+ MarkBlackOrKeepGrey(heap_object, mark_bit, heap_object->Size());
+ } else {
+ if (Marking::IsWhite(mark_bit)) {
+ incremental_marking_->WhiteToGreyAndPush(heap_object, mark_bit);
+ }
+ }
+ }
+
+ IncrementalMarking* incremental_marking_;
+};
+
+
+void IncrementalMarking::Initialize() {
+ IncrementalMarkingMarkingVisitor::Initialize();
+}
+
+
+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->owner()->identity() == PROPERTY_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_->property_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_->property_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
+ // Only start incremental marking in a safe state: 1) when expose GC is
+ // deactivated, 2) when incremental marking is turned on, 3) when we are
+ // currently not in a GC, and 4) when we are currently not serializing
+ // or deserializing the heap.
+ return !FLAG_expose_gc &&
+ FLAG_incremental_marking &&
+ FLAG_incremental_marking_steps &&
+ heap_->gc_state() == Heap::NOT_IN_GC &&
+ !Serializer::enabled() &&
+ heap_->isolate()->IsInitialized() &&
+ heap_->PromotedSpaceSizeOfObjects() > 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(CompactionFlag flag) {
+ if (FLAG_trace_incremental_marking) {
+ PrintF("[IncrementalMarking] Start\n");
+ }
+ ASSERT(FLAG_incremental_marking);
+ ASSERT(FLAG_incremental_marking_steps);
+ ASSERT(state_ == STOPPED);
+ ASSERT(heap_->gc_state() == Heap::NOT_IN_GC);
+ ASSERT(!Serializer::enabled());
+ ASSERT(heap_->isolate()->IsInitialized());
+
+ ResetStepCounters();
+
+ if (heap_->IsSweepingComplete()) {
+ StartMarking(flag);
+ } else {
+ if (FLAG_trace_incremental_marking) {
+ PrintF("[IncrementalMarking] Start sweeping.\n");
+ }
+ state_ = SWEEPING;
+ }
+
+ heap_->new_space()->LowerInlineAllocationLimit(kAllocatedThreshold);
+}
+
+
+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();
+
+ // Marking bits are cleared by the sweeper.
+#ifdef VERIFY_HEAP
+ 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(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);
+ MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
+ ASSERT(Marking::IsGrey(mark_bit) ||
+ (obj->IsFiller() && Marking::IsWhite(mark_bit)) ||
+ (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR) &&
+ Marking::IsBlack(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::VisitObject(Map* map, HeapObject* obj, int size) {
+ MarkBit map_mark_bit = Marking::MarkBitFrom(map);
+ if (Marking::IsWhite(map_mark_bit)) {
+ WhiteToGreyAndPush(map, map_mark_bit);
+ }
+
+ IncrementalMarkingMarkingVisitor::IterateBody(map, obj);
+
+ MarkBit mark_bit = Marking::MarkBitFrom(obj);
+#ifdef DEBUG
+ MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
+ SLOW_ASSERT(Marking::IsGrey(mark_bit) ||
+ (obj->IsFiller() && Marking::IsWhite(mark_bit)) ||
+ (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR) &&
+ Marking::IsBlack(mark_bit)));
+#endif
+ MarkBlackOrKeepBlack(obj, mark_bit, size);
+}
+
+
+void IncrementalMarking::ProcessMarkingDeque(intptr_t bytes_to_process) {
+ Map* filler_map = heap_->one_pointer_filler_map();
+ 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);
+ unscanned_bytes_of_large_object_ = 0;
+ VisitObject(map, obj, size);
+ bytes_to_process -= (size - unscanned_bytes_of_large_object_);
+ }
+}
+
+
+void IncrementalMarking::ProcessMarkingDeque() {
+ Map* filler_map = heap_->one_pointer_filler_map();
+ 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;
+
+ VisitObject(map, obj, obj->SizeFromMap(map));
+ }
+}
+
+
+void IncrementalMarking::Hurry() {
+ if (state() == MARKING) {
+ double start = 0.0;
+ if (FLAG_trace_incremental_marking || FLAG_print_cumulative_gc_stat) {
+ start = OS::TimeCurrentMillis();
+ if (FLAG_trace_incremental_marking) {
+ PrintF("[IncrementalMarking] Hurry\n");
+ }
+ }
+ // TODO(gc) hurry can mark objects it encounters black as mutator
+ // was stopped.
+ ProcessMarkingDeque();
+ state_ = COMPLETE;
+ if (FLAG_trace_incremental_marking || FLAG_print_cumulative_gc_stat) {
+ double end = OS::TimeCurrentMillis();
+ double delta = end - start;
+ heap_->AddMarkingTime(delta);
+ if (FLAG_trace_incremental_marking) {
+ PrintF("[IncrementalMarking] Complete (hurry), spent %d ms.\n",
+ static_cast<int>(delta));
+ }
+ }
+ }
+
+ 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_->native_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(CompletionAction action) {
+ 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 (action == GC_VIA_STACK_GUARD) {
+ heap_->isolate()->stack_guard()->RequestGC();
+ }
+}
+
+
+void IncrementalMarking::OldSpaceStep(intptr_t allocated) {
+ if (IsStopped() && WorthActivating() && heap_->NextGCIsLikelyToBeFull()) {
+ // TODO(hpayer): Let's play safe for now, but compaction should be
+ // in principle possible.
+ Start(PREVENT_COMPACTION);
+ } else {
+ Step(allocated * kFastMarking / kInitialMarkingSpeed, GC_VIA_STACK_GUARD);
+ }
+}
+
+
+void IncrementalMarking::Step(intptr_t allocated_bytes,
+ CompletionAction action) {
+ 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 &&
+ write_barriers_invoked_since_last_step_ <
+ kWriteBarriersInvokedThreshold) {
+ return;
+ }
+
+ if (state_ == MARKING && no_marking_scope_depth_ > 0) return;
+
+ // The marking speed is driven either by the allocation rate or by the rate
+ // at which we are having to check the color of objects in the write barrier.
+ // It is possible for a tight non-allocating loop to run a lot of write
+ // barriers before we get here and check them (marking can only take place on
+ // allocation), so to reduce the lumpiness we don't use the write barriers
+ // invoked since last step directly to determine the amount of work to do.
+ intptr_t bytes_to_process =
+ marking_speed_ * Max(allocated_, write_barriers_invoked_since_last_step_);
+ allocated_ = 0;
+ write_barriers_invoked_since_last_step_ = 0;
+
+ bytes_scanned_ += bytes_to_process;
+
+ double start = 0;
+
+ if (FLAG_trace_incremental_marking || FLAG_trace_gc ||
+ FLAG_print_cumulative_gc_stat) {
+ start = OS::TimeCurrentMillis();
+ }
+
+ if (state_ == SWEEPING) {
+ if (heap_->EnsureSweepersProgressed(static_cast<int>(bytes_to_process))) {
+ bytes_scanned_ = 0;
+ StartMarking(PREVENT_COMPACTION);
+ }
+ } else if (state_ == MARKING) {
+ ProcessMarkingDeque(bytes_to_process);
+ if (marking_deque_.IsEmpty()) MarkingComplete(action);
+ }
+
+ steps_count_++;
+ steps_count_since_last_gc_++;
+
+ bool speed_up = false;
+
+ if ((steps_count_ % kMarkingSpeedAccellerationInterval) == 0) {
+ if (FLAG_trace_gc) {
+ PrintPID("Speed up marking after %d steps\n",
+ static_cast<int>(kMarkingSpeedAccellerationInterval));
+ }
+ 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() * (marking_speed_ + 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) PrintPID("Speed up marking because of low space left\n");
+ speed_up = true;
+ }
+
+ bool size_of_old_space_multiplied_by_n_during_marking =
+ (heap_->PromotedTotalSize() >
+ (marking_speed_ + 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) {
+ PrintPID("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 = marking_speed_ * 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) {
+ PrintPID("Speed up marking because marker was not keeping up\n");
+ }
+ speed_up = true;
+ }
+
+ if (speed_up) {
+ if (state_ != MARKING) {
+ if (FLAG_trace_gc) {
+ PrintPID("Postponing speeding up marking until marking starts\n");
+ }
+ } else {
+ marking_speed_ += kMarkingSpeedAccelleration;
+ marking_speed_ = static_cast<int>(
+ Min(kMaxMarkingSpeed,
+ static_cast<intptr_t>(marking_speed_ * 1.3)));
+ if (FLAG_trace_gc) {
+ PrintPID("Marking speed increased to %d\n", marking_speed_);
+ }
+ }
+ }
+
+ if (FLAG_trace_incremental_marking || FLAG_trace_gc ||
+ FLAG_print_cumulative_gc_stat) {
+ double end = OS::TimeCurrentMillis();
+ double delta = (end - start);
+ longest_step_ = Max(longest_step_, delta);
+ steps_took_ += delta;
+ steps_took_since_last_gc_ += delta;
+ heap_->AddMarkingTime(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;
+ marking_speed_ = kInitialMarkingSpeed;
+ bytes_scanned_ = 0;
+ write_barriers_invoked_since_last_step_ = 0;
+}
+
+
+int64_t IncrementalMarking::SpaceLeftInOldSpace() {
+ return heap_->MaxOldGenerationSize() - heap_->PromotedSpaceSizeOfObjects();
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/incremental-marking.h b/chromium/v8/src/incremental-marking.h
new file mode 100644
index 00000000000..d47c300ef3f
--- /dev/null
+++ b/chromium/v8/src/incremental-marking.h
@@ -0,0 +1,288 @@
+// Copyright 2012 the V8 project authors. 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
+ };
+
+ enum CompletionAction {
+ GC_VIA_STACK_GUARD,
+ NO_GC_VIA_STACK_GUARD
+ };
+
+ explicit IncrementalMarking(Heap* heap);
+
+ static void Initialize();
+
+ 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();
+
+ enum CompactionFlag { ALLOW_COMPACTION, PREVENT_COMPACTION };
+
+ void Start(CompactionFlag flag = ALLOW_COMPACTION);
+
+ void Stop();
+
+ void PrepareForScavenge();
+
+ void UpdateMarkingDequeAfterScavenge();
+
+ void Hurry();
+
+ void Finalize();
+
+ void Abort();
+
+ void MarkingComplete(CompletionAction action);
+
+ // 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 or that many
+ // heavy (color-checking) write barriers have been invoked.
+ static const intptr_t kAllocatedThreshold = 65536;
+ static const intptr_t kWriteBarriersInvokedThreshold = 65536;
+ // Start off by marking this many times more memory than has been allocated.
+ static const intptr_t kInitialMarkingSpeed = 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 kMarkingSpeedAccellerationInterval = 1024;
+ // This is how much we increase the marking/allocating factor by.
+ static const intptr_t kMarkingSpeedAccelleration = 2;
+ static const intptr_t kMaxMarkingSpeed = 1000;
+
+ void OldSpaceStep(intptr_t allocated);
+
+ void Step(intptr_t allocated, CompletionAction action);
+
+ 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** slot,
+ Isolate* isolate);
+
+ static void RecordWriteForEvacuationFromCode(HeapObject* obj,
+ Object** slot,
+ Isolate* isolate);
+
+ // Record a slot for compaction. Returns false for objects that are
+ // guaranteed to be rescanned or not guaranteed to survive.
+ //
+ // No slots in white objects should be recorded, as some slots are typed and
+ // cannot be interpreted correctly if the underlying object does not survive
+ // the incremental cycle (stays white).
+ 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 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 (marking_speed_ < kFastMarking) {
+ if (FLAG_trace_gc) {
+ PrintPID("Increasing marking speed to %d "
+ "due to high promotion rate\n",
+ static_cast<int>(kFastMarking));
+ }
+ marking_speed_ = kFastMarking;
+ }
+ }
+ }
+
+ void EnterNoMarkingScope() {
+ no_marking_scope_depth_++;
+ }
+
+ void LeaveNoMarkingScope() {
+ no_marking_scope_depth_--;
+ }
+
+ void UncommitMarkingDeque();
+
+ void NotifyIncompleteScanOfObject(int unscanned_bytes) {
+ unscanned_bytes_of_large_object_ = unscanned_bytes;
+ }
+
+ private:
+ int64_t SpaceLeftInOldSpace();
+
+ void ResetStepCounters();
+
+ 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();
+
+ INLINE(void ProcessMarkingDeque());
+
+ INLINE(void ProcessMarkingDeque(intptr_t bytes_to_process));
+
+ INLINE(void VisitObject(Map* map, HeapObject* obj, int size));
+
+ 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 marking_speed_;
+ intptr_t bytes_scanned_;
+ intptr_t allocated_;
+ intptr_t write_barriers_invoked_since_last_step_;
+
+ int no_marking_scope_depth_;
+
+ int unscanned_bytes_of_large_object_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(IncrementalMarking);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_INCREMENTAL_MARKING_H_
diff --git a/chromium/v8/src/interface.cc b/chromium/v8/src/interface.cc
new file mode 100644
index 00000000000..603dfe9b863
--- /dev/null
+++ b/chromium/v8/src/interface.cc
@@ -0,0 +1,244 @@
+// Copyright 2012 the V8 project authors. 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 "interface.h"
+
+namespace v8 {
+namespace internal {
+
+static bool Match(void* key1, void* key2) {
+ String* name1 = *static_cast<String**>(key1);
+ String* name2 = *static_cast<String**>(key2);
+ ASSERT(name1->IsInternalizedString());
+ ASSERT(name2->IsInternalizedString());
+ return name1 == name2;
+}
+
+
+Interface* Interface::Lookup(Handle<String> name, Zone* zone) {
+ ASSERT(IsModule());
+ ZoneHashMap* map = Chase()->exports_;
+ if (map == NULL) return NULL;
+ ZoneAllocationPolicy allocator(zone);
+ ZoneHashMap::Entry* p = map->Lookup(name.location(), name->Hash(), false,
+ allocator);
+ if (p == NULL) return NULL;
+ ASSERT(*static_cast<String**>(p->key) == *name);
+ ASSERT(p->value != NULL);
+ return static_cast<Interface*>(p->value);
+}
+
+
+#ifdef DEBUG
+// Current nesting depth for debug output.
+class Nesting {
+ public:
+ Nesting() { current_ += 2; }
+ ~Nesting() { current_ -= 2; }
+ static int current() { return current_; }
+ private:
+ static int current_;
+};
+
+int Nesting::current_ = 0;
+#endif
+
+
+void Interface::DoAdd(
+ void* name, uint32_t hash, Interface* interface, Zone* zone, bool* ok) {
+ MakeModule(ok);
+ if (!*ok) return;
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details) {
+ PrintF("%*s# Adding...\n", Nesting::current(), "");
+ PrintF("%*sthis = ", Nesting::current(), "");
+ this->Print(Nesting::current());
+ PrintF("%*s%s : ", Nesting::current(), "",
+ (*static_cast<String**>(name))->ToAsciiArray());
+ interface->Print(Nesting::current());
+ }
+#endif
+
+ ZoneHashMap** map = &Chase()->exports_;
+ ZoneAllocationPolicy allocator(zone);
+
+ if (*map == NULL)
+ *map = new ZoneHashMap(Match, ZoneHashMap::kDefaultHashMapCapacity,
+ allocator);
+
+ ZoneHashMap::Entry* p = (*map)->Lookup(name, hash, !IsFrozen(), allocator);
+ if (p == NULL) {
+ // This didn't have name but was frozen already, that's an error.
+ *ok = false;
+ } else if (p->value == NULL) {
+ p->value = interface;
+ } else {
+#ifdef DEBUG
+ Nesting nested;
+#endif
+ static_cast<Interface*>(p->value)->Unify(interface, zone, ok);
+ }
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details) {
+ PrintF("%*sthis' = ", Nesting::current(), "");
+ this->Print(Nesting::current());
+ PrintF("%*s# Added.\n", Nesting::current(), "");
+ }
+#endif
+}
+
+
+void Interface::Unify(Interface* that, Zone* zone, bool* ok) {
+ if (this->forward_) return this->Chase()->Unify(that, zone, ok);
+ if (that->forward_) return this->Unify(that->Chase(), zone, ok);
+ ASSERT(this->forward_ == NULL);
+ ASSERT(that->forward_ == NULL);
+
+ *ok = true;
+ if (this == that) return;
+ if (this->IsValue()) {
+ that->MakeValue(ok);
+ if (*ok && this->IsConst()) that->MakeConst(ok);
+ return;
+ }
+ if (that->IsValue()) {
+ this->MakeValue(ok);
+ if (*ok && that->IsConst()) this->MakeConst(ok);
+ return;
+ }
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details) {
+ PrintF("%*s# Unifying...\n", Nesting::current(), "");
+ PrintF("%*sthis = ", Nesting::current(), "");
+ this->Print(Nesting::current());
+ PrintF("%*sthat = ", Nesting::current(), "");
+ that->Print(Nesting::current());
+ }
+#endif
+
+ // Merge the smaller interface into the larger, for performance.
+ if (this->exports_ != NULL && (that->exports_ == NULL ||
+ this->exports_->occupancy() >= that->exports_->occupancy())) {
+ this->DoUnify(that, ok, zone);
+ } else {
+ that->DoUnify(this, ok, zone);
+ }
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details) {
+ PrintF("%*sthis' = ", Nesting::current(), "");
+ this->Print(Nesting::current());
+ PrintF("%*sthat' = ", Nesting::current(), "");
+ that->Print(Nesting::current());
+ PrintF("%*s# Unified.\n", Nesting::current(), "");
+ }
+#endif
+}
+
+
+void Interface::DoUnify(Interface* that, bool* ok, Zone* zone) {
+ ASSERT(this->forward_ == NULL);
+ ASSERT(that->forward_ == NULL);
+ ASSERT(!this->IsValue());
+ ASSERT(!that->IsValue());
+ ASSERT(this->index_ == -1);
+ ASSERT(that->index_ == -1);
+ ASSERT(*ok);
+
+#ifdef DEBUG
+ Nesting nested;
+#endif
+
+ // Try to merge all members from that into this.
+ ZoneHashMap* map = that->exports_;
+ if (map != NULL) {
+ for (ZoneHashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) {
+ this->DoAdd(p->key, p->hash, static_cast<Interface*>(p->value), zone, ok);
+ if (!*ok) return;
+ }
+ }
+
+ // If the new interface is larger than that's, then there were members in
+ // 'this' which 'that' didn't have. If 'that' was frozen that is an error.
+ int this_size = this->exports_ == NULL ? 0 : this->exports_->occupancy();
+ int that_size = map == NULL ? 0 : map->occupancy();
+ if (that->IsFrozen() && this_size > that_size) {
+ *ok = false;
+ return;
+ }
+
+ // Merge interfaces.
+ this->flags_ |= that->flags_;
+ that->forward_ = this;
+}
+
+
+#ifdef DEBUG
+void Interface::Print(int n) {
+ int n0 = n > 0 ? n : 0;
+
+ if (FLAG_print_interface_details) {
+ PrintF("%p", static_cast<void*>(this));
+ for (Interface* link = this->forward_; link != NULL; link = link->forward_)
+ PrintF("->%p", static_cast<void*>(link));
+ PrintF(" ");
+ }
+
+ if (IsUnknown()) {
+ PrintF("unknown\n");
+ } else if (IsConst()) {
+ PrintF("const\n");
+ } else if (IsValue()) {
+ PrintF("value\n");
+ } else if (IsModule()) {
+ PrintF("module %d %s{", Index(), IsFrozen() ? "" : "(unresolved) ");
+ ZoneHashMap* map = Chase()->exports_;
+ if (map == NULL || map->occupancy() == 0) {
+ PrintF("}\n");
+ } else if (n < 0 || n0 >= 2 * FLAG_print_interface_depth) {
+ // Avoid infinite recursion on cyclic types.
+ PrintF("...}\n");
+ } else {
+ PrintF("\n");
+ for (ZoneHashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) {
+ String* name = *static_cast<String**>(p->key);
+ Interface* interface = static_cast<Interface*>(p->value);
+ PrintF("%*s%s : ", n0 + 2, "", name->ToAsciiArray());
+ interface->Print(n0 + 2);
+ }
+ PrintF("%*s}\n", n0, "");
+ }
+ }
+}
+#endif
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/interface.h b/chromium/v8/src/interface.h
new file mode 100644
index 00000000000..f824a9a8749
--- /dev/null
+++ b/chromium/v8/src/interface.h
@@ -0,0 +1,240 @@
+// Copyright 2012 the V8 project authors. 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_INTERFACE_H_
+#define V8_INTERFACE_H_
+
+#include "zone-inl.h" // For operator new.
+
+namespace v8 {
+namespace internal {
+
+
+// This class implements the following abstract grammar of interfaces
+// (i.e. module types):
+// interface ::= UNDETERMINED | VALUE | CONST | MODULE(exports)
+// exports ::= {name : interface, ...}
+// A frozen type is one that is fully determined. Unification does not
+// allow to turn non-const values into const, or adding additional exports to
+// frozen interfaces. Otherwise, unifying modules merges their exports.
+// Undetermined types are unification variables that can be unified freely.
+// There is a natural subsort lattice that reflects the increase of knowledge:
+//
+// undetermined
+// // | \\ .
+// value (frozen) module
+// // \\ / \ //
+// const fr.value fr.module
+// \\ /
+// fr.const
+//
+// where the bold lines are the only transitions allowed.
+
+class Interface : public ZoneObject {
+ public:
+ // ---------------------------------------------------------------------------
+ // Factory methods.
+
+ static Interface* NewUnknown(Zone* zone) {
+ return new(zone) Interface(NONE);
+ }
+
+ static Interface* NewValue() {
+ static Interface value_interface(VALUE + FROZEN); // Cached.
+ return &value_interface;
+ }
+
+ static Interface* NewConst() {
+ static Interface value_interface(VALUE + CONST + FROZEN); // Cached.
+ return &value_interface;
+ }
+
+ static Interface* NewModule(Zone* zone) {
+ return new(zone) Interface(MODULE);
+ }
+
+ // ---------------------------------------------------------------------------
+ // Mutators.
+
+ // Add a name to the list of exports. If it already exists, unify with
+ // interface, otherwise insert unless this is closed.
+ void Add(Handle<String> name, Interface* interface, Zone* zone, bool* ok) {
+ DoAdd(name.location(), name->Hash(), interface, zone, ok);
+ }
+
+ // Unify with another interface. If successful, both interface objects will
+ // represent the same type, and changes to one are reflected in the other.
+ void Unify(Interface* that, Zone* zone, bool* ok);
+
+ // Determine this interface to be a value interface.
+ void MakeValue(bool* ok) {
+ *ok = !IsModule();
+ if (*ok) Chase()->flags_ |= VALUE;
+ }
+
+ // Determine this interface to be an immutable interface.
+ void MakeConst(bool* ok) {
+ *ok = !IsModule() && (IsConst() || !IsFrozen());
+ if (*ok) Chase()->flags_ |= VALUE + CONST;
+ }
+
+ // Determine this interface to be a module interface.
+ void MakeModule(bool* ok) {
+ *ok = !IsValue();
+ if (*ok) Chase()->flags_ |= MODULE;
+ }
+
+ // Do not allow any further refinements, directly or through unification.
+ void Freeze(bool* ok) {
+ *ok = IsValue() || IsModule();
+ if (*ok) Chase()->flags_ |= FROZEN;
+ }
+
+ // Assign an index.
+ void Allocate(int index) {
+ ASSERT(IsModule() && IsFrozen() && Chase()->index_ == -1);
+ Chase()->index_ = index;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Accessors.
+
+ // Check whether this is still a fully undetermined type.
+ bool IsUnknown() { return Chase()->flags_ == NONE; }
+
+ // Check whether this is a value type.
+ bool IsValue() { return Chase()->flags_ & VALUE; }
+
+ // Check whether this is a constant type.
+ bool IsConst() { return Chase()->flags_ & CONST; }
+
+ // Check whether this is a module type.
+ bool IsModule() { return Chase()->flags_ & MODULE; }
+
+ // Check whether this is closed (i.e. fully determined).
+ bool IsFrozen() { return Chase()->flags_ & FROZEN; }
+
+ bool IsUnified(Interface* that) {
+ return Chase() == that->Chase()
+ || (this->IsValue() == that->IsValue() &&
+ this->IsConst() == that->IsConst());
+ }
+
+ int Length() {
+ ASSERT(IsModule() && IsFrozen());
+ ZoneHashMap* exports = Chase()->exports_;
+ return exports ? exports->occupancy() : 0;
+ }
+
+ // The context slot in the hosting global context pointing to this module.
+ int Index() {
+ ASSERT(IsModule() && IsFrozen());
+ return Chase()->index_;
+ }
+
+ // Look up an exported name. Returns NULL if not (yet) defined.
+ Interface* Lookup(Handle<String> name, Zone* zone);
+
+ // ---------------------------------------------------------------------------
+ // Iterators.
+
+ // Use like:
+ // for (auto it = interface->iterator(); !it.done(); it.Advance()) {
+ // ... it.name() ... it.interface() ...
+ // }
+ class Iterator {
+ public:
+ bool done() const { return entry_ == NULL; }
+ Handle<String> name() const {
+ ASSERT(!done());
+ return Handle<String>(*static_cast<String**>(entry_->key));
+ }
+ Interface* interface() const {
+ ASSERT(!done());
+ return static_cast<Interface*>(entry_->value);
+ }
+ void Advance() { entry_ = exports_->Next(entry_); }
+
+ private:
+ friend class Interface;
+ explicit Iterator(const ZoneHashMap* exports)
+ : exports_(exports), entry_(exports ? exports->Start() : NULL) {}
+
+ const ZoneHashMap* exports_;
+ ZoneHashMap::Entry* entry_;
+ };
+
+ Iterator iterator() const { return Iterator(this->exports_); }
+
+ // ---------------------------------------------------------------------------
+ // Debugging.
+#ifdef DEBUG
+ void Print(int n = 0); // n = indentation; n < 0 => don't print recursively
+#endif
+
+ // ---------------------------------------------------------------------------
+ // Implementation.
+ private:
+ enum Flags { // All flags are monotonic
+ NONE = 0,
+ VALUE = 1, // This type describes a value
+ CONST = 2, // This type describes a constant
+ MODULE = 4, // This type describes a module
+ FROZEN = 8 // This type is fully determined
+ };
+
+ int flags_;
+ Interface* forward_; // Unification link
+ ZoneHashMap* exports_; // Module exports and their types (allocated lazily)
+ int index_;
+
+ explicit Interface(int flags)
+ : flags_(flags),
+ forward_(NULL),
+ exports_(NULL),
+ index_(-1) {
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Creating %p\n", static_cast<void*>(this));
+#endif
+ }
+
+ Interface* Chase() {
+ Interface* result = this;
+ while (result->forward_ != NULL) result = result->forward_;
+ if (result != this) forward_ = result; // On-the-fly path compression.
+ return result;
+ }
+
+ void DoAdd(void* name, uint32_t hash, Interface* interface, Zone* zone,
+ bool* ok);
+ void DoUnify(Interface* that, bool* ok, Zone* zone);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_INTERFACE_H_
diff --git a/chromium/v8/src/interpreter-irregexp.cc b/chromium/v8/src/interpreter-irregexp.cc
new file mode 100644
index 00000000000..2fc9fd30257
--- /dev/null
+++ b/chromium/v8/src/interpreter-irregexp.cc
@@ -0,0 +1,647 @@
+// 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.
+
+// A simple interpreter for the Irregexp byte code.
+
+
+#include "v8.h"
+#include "unicode.h"
+#include "utils.h"
+#include "ast.h"
+#include "bytecodes-irregexp.h"
+#include "interpreter-irregexp.h"
+#include "jsregexp.h"
+#include "regexp-macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+
+typedef unibrow::Mapping<unibrow::Ecma262Canonicalize> Canonicalize;
+
+static bool BackRefMatchesNoCase(Canonicalize* interp_canonicalize,
+ int from,
+ int current,
+ int len,
+ Vector<const uc16> subject) {
+ for (int i = 0; i < len; i++) {
+ unibrow::uchar old_char = subject[from++];
+ unibrow::uchar new_char = subject[current++];
+ if (old_char == new_char) continue;
+ unibrow::uchar old_string[1] = { old_char };
+ unibrow::uchar new_string[1] = { new_char };
+ interp_canonicalize->get(old_char, '\0', old_string);
+ interp_canonicalize->get(new_char, '\0', new_string);
+ if (old_string[0] != new_string[0]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static bool BackRefMatchesNoCase(Canonicalize* interp_canonicalize,
+ int from,
+ int current,
+ int len,
+ Vector<const uint8_t> subject) {
+ for (int i = 0; i < len; i++) {
+ unsigned int old_char = subject[from++];
+ unsigned int new_char = subject[current++];
+ if (old_char == new_char) continue;
+ // Convert both characters to lower case.
+ old_char |= 0x20;
+ new_char |= 0x20;
+ if (old_char != new_char) return false;
+ // Not letters in the ASCII range and Latin-1 range.
+ if (!(old_char - 'a' <= 'z' - 'a') &&
+ !(old_char - 224 <= 254 - 224 && old_char != 247)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+#ifdef DEBUG
+static void TraceInterpreter(const byte* code_base,
+ const byte* pc,
+ int stack_depth,
+ int current_position,
+ uint32_t current_char,
+ int bytecode_length,
+ const char* bytecode_name) {
+ if (FLAG_trace_regexp_bytecodes) {
+ bool printable = (current_char < 127 && current_char >= 32);
+ const char* format =
+ printable ?
+ "pc = %02x, sp = %d, curpos = %d, curchar = %08x (%c), bc = %s" :
+ "pc = %02x, sp = %d, curpos = %d, curchar = %08x .%c., bc = %s";
+ PrintF(format,
+ pc - code_base,
+ stack_depth,
+ current_position,
+ current_char,
+ printable ? current_char : '.',
+ bytecode_name);
+ for (int i = 0; i < bytecode_length; i++) {
+ printf(", %02x", pc[i]);
+ }
+ printf(" ");
+ for (int i = 1; i < bytecode_length; i++) {
+ unsigned char b = pc[i];
+ if (b < 127 && b >= 32) {
+ printf("%c", b);
+ } else {
+ printf(".");
+ }
+ }
+ printf("\n");
+ }
+}
+
+
+#define BYTECODE(name) \
+ case BC_##name: \
+ TraceInterpreter(code_base, \
+ pc, \
+ static_cast<int>(backtrack_sp - backtrack_stack_base), \
+ current, \
+ current_char, \
+ BC_##name##_LENGTH, \
+ #name);
+#else
+#define BYTECODE(name) \
+ case BC_##name:
+#endif
+
+
+static int32_t Load32Aligned(const byte* pc) {
+ ASSERT((reinterpret_cast<intptr_t>(pc) & 3) == 0);
+ return *reinterpret_cast<const int32_t *>(pc);
+}
+
+
+static int32_t Load16Aligned(const byte* pc) {
+ ASSERT((reinterpret_cast<intptr_t>(pc) & 1) == 0);
+ return *reinterpret_cast<const uint16_t *>(pc);
+}
+
+
+// A simple abstraction over the backtracking stack used by the interpreter.
+// This backtracking stack does not grow automatically, but it ensures that the
+// the memory held by the stack is released or remembered in a cache if the
+// matching terminates.
+class BacktrackStack {
+ public:
+ explicit BacktrackStack(Isolate* isolate) : isolate_(isolate) {
+ if (isolate->irregexp_interpreter_backtrack_stack_cache() != NULL) {
+ // If the cache is not empty reuse the previously allocated stack.
+ data_ = isolate->irregexp_interpreter_backtrack_stack_cache();
+ isolate->set_irregexp_interpreter_backtrack_stack_cache(NULL);
+ } else {
+ // Cache was empty. Allocate a new backtrack stack.
+ data_ = NewArray<int>(kBacktrackStackSize);
+ }
+ }
+
+ ~BacktrackStack() {
+ if (isolate_->irregexp_interpreter_backtrack_stack_cache() == NULL) {
+ // The cache is empty. Keep this backtrack stack around.
+ isolate_->set_irregexp_interpreter_backtrack_stack_cache(data_);
+ } else {
+ // A backtrack stack was already cached, just release this one.
+ DeleteArray(data_);
+ }
+ }
+
+ int* data() const { return data_; }
+
+ int max_size() const { return kBacktrackStackSize; }
+
+ private:
+ static const int kBacktrackStackSize = 10000;
+
+ int* data_;
+ Isolate* isolate_;
+
+ DISALLOW_COPY_AND_ASSIGN(BacktrackStack);
+};
+
+
+template <typename 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
+ // the moment.
+ BacktrackStack backtrack_stack(isolate);
+ int* backtrack_stack_base = backtrack_stack.data();
+ int* backtrack_sp = backtrack_stack_base;
+ int backtrack_stack_space = backtrack_stack.max_size();
+#ifdef DEBUG
+ if (FLAG_trace_regexp_bytecodes) {
+ PrintF("\n\nStart bytecode interpreter\n\n");
+ }
+#endif
+ while (true) {
+ int32_t insn = Load32Aligned(pc);
+ switch (insn & BYTECODE_MASK) {
+ BYTECODE(BREAK)
+ UNREACHABLE();
+ return RegExpImpl::RE_FAILURE;
+ BYTECODE(PUSH_CP)
+ if (--backtrack_stack_space < 0) {
+ return RegExpImpl::RE_EXCEPTION;
+ }
+ *backtrack_sp++ = current;
+ pc += BC_PUSH_CP_LENGTH;
+ break;
+ BYTECODE(PUSH_BT)
+ if (--backtrack_stack_space < 0) {
+ return RegExpImpl::RE_EXCEPTION;
+ }
+ *backtrack_sp++ = Load32Aligned(pc + 4);
+ pc += BC_PUSH_BT_LENGTH;
+ break;
+ BYTECODE(PUSH_REGISTER)
+ if (--backtrack_stack_space < 0) {
+ return RegExpImpl::RE_EXCEPTION;
+ }
+ *backtrack_sp++ = registers[insn >> BYTECODE_SHIFT];
+ pc += BC_PUSH_REGISTER_LENGTH;
+ break;
+ BYTECODE(SET_REGISTER)
+ registers[insn >> BYTECODE_SHIFT] = Load32Aligned(pc + 4);
+ pc += BC_SET_REGISTER_LENGTH;
+ break;
+ BYTECODE(ADVANCE_REGISTER)
+ registers[insn >> BYTECODE_SHIFT] += Load32Aligned(pc + 4);
+ pc += BC_ADVANCE_REGISTER_LENGTH;
+ break;
+ BYTECODE(SET_REGISTER_TO_CP)
+ registers[insn >> BYTECODE_SHIFT] = current + Load32Aligned(pc + 4);
+ pc += BC_SET_REGISTER_TO_CP_LENGTH;
+ break;
+ BYTECODE(SET_CP_TO_REGISTER)
+ current = registers[insn >> BYTECODE_SHIFT];
+ pc += BC_SET_CP_TO_REGISTER_LENGTH;
+ break;
+ BYTECODE(SET_REGISTER_TO_SP)
+ registers[insn >> BYTECODE_SHIFT] =
+ static_cast<int>(backtrack_sp - backtrack_stack_base);
+ pc += BC_SET_REGISTER_TO_SP_LENGTH;
+ break;
+ BYTECODE(SET_SP_TO_REGISTER)
+ backtrack_sp = backtrack_stack_base + registers[insn >> BYTECODE_SHIFT];
+ backtrack_stack_space = backtrack_stack.max_size() -
+ static_cast<int>(backtrack_sp - backtrack_stack_base);
+ pc += BC_SET_SP_TO_REGISTER_LENGTH;
+ break;
+ BYTECODE(POP_CP)
+ backtrack_stack_space++;
+ --backtrack_sp;
+ current = *backtrack_sp;
+ pc += BC_POP_CP_LENGTH;
+ break;
+ BYTECODE(POP_BT)
+ backtrack_stack_space++;
+ --backtrack_sp;
+ pc = code_base + *backtrack_sp;
+ break;
+ BYTECODE(POP_REGISTER)
+ backtrack_stack_space++;
+ --backtrack_sp;
+ registers[insn >> BYTECODE_SHIFT] = *backtrack_sp;
+ pc += BC_POP_REGISTER_LENGTH;
+ break;
+ BYTECODE(FAIL)
+ return RegExpImpl::RE_FAILURE;
+ BYTECODE(SUCCEED)
+ return RegExpImpl::RE_SUCCESS;
+ BYTECODE(ADVANCE_CP)
+ current += insn >> BYTECODE_SHIFT;
+ pc += BC_ADVANCE_CP_LENGTH;
+ break;
+ BYTECODE(GOTO)
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ BYTECODE(ADVANCE_CP_AND_GOTO)
+ current += insn >> BYTECODE_SHIFT;
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ BYTECODE(CHECK_GREEDY)
+ if (current == backtrack_sp[-1]) {
+ backtrack_sp--;
+ backtrack_stack_space++;
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_GREEDY_LENGTH;
+ }
+ break;
+ BYTECODE(LOAD_CURRENT_CHAR) {
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ if (pos >= subject.length()) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ current_char = subject[pos];
+ pc += BC_LOAD_CURRENT_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(LOAD_CURRENT_CHAR_UNCHECKED) {
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ current_char = subject[pos];
+ pc += BC_LOAD_CURRENT_CHAR_UNCHECKED_LENGTH;
+ break;
+ }
+ BYTECODE(LOAD_2_CURRENT_CHARS) {
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ if (pos + 2 > subject.length()) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ Char next = subject[pos + 1];
+ current_char =
+ (subject[pos] | (next << (kBitsPerByte * sizeof(Char))));
+ pc += BC_LOAD_2_CURRENT_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(LOAD_2_CURRENT_CHARS_UNCHECKED) {
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ Char next = subject[pos + 1];
+ current_char = (subject[pos] | (next << (kBitsPerByte * sizeof(Char))));
+ pc += BC_LOAD_2_CURRENT_CHARS_UNCHECKED_LENGTH;
+ break;
+ }
+ BYTECODE(LOAD_4_CURRENT_CHARS) {
+ ASSERT(sizeof(Char) == 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ if (pos + 4 > subject.length()) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ Char next1 = subject[pos + 1];
+ Char next2 = subject[pos + 2];
+ Char next3 = subject[pos + 3];
+ current_char = (subject[pos] |
+ (next1 << 8) |
+ (next2 << 16) |
+ (next3 << 24));
+ pc += BC_LOAD_4_CURRENT_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(LOAD_4_CURRENT_CHARS_UNCHECKED) {
+ ASSERT(sizeof(Char) == 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
+ Char next1 = subject[pos + 1];
+ Char next2 = subject[pos + 2];
+ Char next3 = subject[pos + 3];
+ current_char = (subject[pos] |
+ (next1 << 8) |
+ (next2 << 16) |
+ (next3 << 24));
+ pc += BC_LOAD_4_CURRENT_CHARS_UNCHECKED_LENGTH;
+ break;
+ }
+ BYTECODE(CHECK_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c == current_char) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c == current_char) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_NOT_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c != current_char) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_NOT_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_NOT_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c != current_char) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_NOT_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(AND_CHECK_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c == (current_char & Load32Aligned(pc + 8))) {
+ pc = code_base + Load32Aligned(pc + 12);
+ } else {
+ pc += BC_AND_CHECK_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(AND_CHECK_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c == (current_char & Load32Aligned(pc + 4))) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_AND_CHECK_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(AND_CHECK_NOT_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c != (current_char & Load32Aligned(pc + 8))) {
+ pc = code_base + Load32Aligned(pc + 12);
+ } else {
+ pc += BC_AND_CHECK_NOT_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(AND_CHECK_NOT_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c != (current_char & Load32Aligned(pc + 4))) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_AND_CHECK_NOT_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(MINUS_AND_CHECK_NOT_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ uint32_t minus = Load16Aligned(pc + 4);
+ uint32_t mask = Load16Aligned(pc + 6);
+ if (c != ((current_char - minus) & mask)) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_MINUS_AND_CHECK_NOT_CHAR_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_CHAR_IN_RANGE) {
+ uint32_t from = Load16Aligned(pc + 4);
+ uint32_t to = Load16Aligned(pc + 6);
+ if (from <= current_char && current_char <= to) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_CHAR_IN_RANGE_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_CHAR_NOT_IN_RANGE) {
+ uint32_t from = Load16Aligned(pc + 4);
+ uint32_t to = Load16Aligned(pc + 6);
+ if (from > current_char || current_char > to) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_CHAR_NOT_IN_RANGE_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_BIT_IN_TABLE) {
+ int mask = RegExpMacroAssembler::kTableMask;
+ byte b = pc[8 + ((current_char & mask) >> kBitsPerByteLog2)];
+ int bit = (current_char & (kBitsPerByte - 1));
+ if ((b & (1 << bit)) != 0) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_BIT_IN_TABLE_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_LT) {
+ uint32_t limit = (insn >> BYTECODE_SHIFT);
+ if (current_char < limit) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_LT_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_GT) {
+ uint32_t limit = (insn >> BYTECODE_SHIFT);
+ if (current_char > limit) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_GT_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_REGISTER_LT)
+ if (registers[insn >> BYTECODE_SHIFT] < Load32Aligned(pc + 4)) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_REGISTER_LT_LENGTH;
+ }
+ break;
+ BYTECODE(CHECK_REGISTER_GE)
+ if (registers[insn >> BYTECODE_SHIFT] >= Load32Aligned(pc + 4)) {
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_REGISTER_GE_LENGTH;
+ }
+ break;
+ BYTECODE(CHECK_REGISTER_EQ_POS)
+ if (registers[insn >> BYTECODE_SHIFT] == current) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_REGISTER_EQ_POS_LENGTH;
+ }
+ break;
+ BYTECODE(CHECK_NOT_REGS_EQUAL)
+ if (registers[insn >> BYTECODE_SHIFT] ==
+ registers[Load32Aligned(pc + 4)]) {
+ pc += BC_CHECK_NOT_REGS_EQUAL_LENGTH;
+ } else {
+ pc = code_base + Load32Aligned(pc + 8);
+ }
+ break;
+ BYTECODE(CHECK_NOT_BACK_REF) {
+ int from = registers[insn >> BYTECODE_SHIFT];
+ int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
+ if (from < 0 || len <= 0) {
+ pc += BC_CHECK_NOT_BACK_REF_LENGTH;
+ break;
+ }
+ if (current + len > subject.length()) {
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ } else {
+ int i;
+ for (i = 0; i < len; i++) {
+ if (subject[from + i] != subject[current + i]) {
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ }
+ }
+ if (i < len) break;
+ current += len;
+ }
+ pc += BC_CHECK_NOT_BACK_REF_LENGTH;
+ break;
+ }
+ BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
+ int from = registers[insn >> BYTECODE_SHIFT];
+ int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
+ if (from < 0 || len <= 0) {
+ pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
+ break;
+ }
+ if (current + len > subject.length()) {
+ pc = code_base + Load32Aligned(pc + 4);
+ break;
+ } else {
+ if (BackRefMatchesNoCase(isolate->interp_canonicalize_mapping(),
+ from, current, len, subject)) {
+ current += len;
+ pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
+ } else {
+ pc = code_base + Load32Aligned(pc + 4);
+ }
+ }
+ break;
+ }
+ BYTECODE(CHECK_AT_START)
+ if (current == 0) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_AT_START_LENGTH;
+ }
+ break;
+ BYTECODE(CHECK_NOT_AT_START)
+ if (current == 0) {
+ pc += BC_CHECK_NOT_AT_START_LENGTH;
+ } else {
+ pc = code_base + Load32Aligned(pc + 4);
+ }
+ break;
+ BYTECODE(SET_CURRENT_POSITION_FROM_END) {
+ int by = static_cast<uint32_t>(insn) >> BYTECODE_SHIFT;
+ if (subject.length() - current > by) {
+ current = subject.length() - by;
+ current_char = subject[current - 1];
+ }
+ pc += BC_SET_CURRENT_POSITION_FROM_END_LENGTH;
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+RegExpImpl::IrregexpResult IrregexpInterpreter::Match(
+ Isolate* isolate,
+ Handle<ByteArray> code_array,
+ Handle<String> subject,
+ int* registers,
+ int start_position) {
+ ASSERT(subject->IsFlat());
+
+ DisallowHeapAllocation no_gc;
+ const byte* code_base = code_array->GetDataStartAddress();
+ uc16 previous_char = '\n';
+ String::FlatContent subject_content = subject->GetFlatContent();
+ if (subject_content.IsAscii()) {
+ Vector<const uint8_t> subject_vector = subject_content.ToOneByteVector();
+ if (start_position != 0) previous_char = subject_vector[start_position - 1];
+ return RawMatch(isolate,
+ code_base,
+ subject_vector,
+ registers,
+ start_position,
+ previous_char);
+ } else {
+ ASSERT(subject_content.IsTwoByte());
+ Vector<const uc16> subject_vector = subject_content.ToUC16Vector();
+ if (start_position != 0) previous_char = subject_vector[start_position - 1];
+ return RawMatch(isolate,
+ code_base,
+ subject_vector,
+ registers,
+ start_position,
+ previous_char);
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/interpreter-irregexp.h b/chromium/v8/src/interpreter-irregexp.h
new file mode 100644
index 00000000000..0f45d98207f
--- /dev/null
+++ b/chromium/v8/src/interpreter-irregexp.h
@@ -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.
+
+// A simple interpreter for the Irregexp byte code.
+
+#ifndef V8_INTERPRETER_IRREGEXP_H_
+#define V8_INTERPRETER_IRREGEXP_H_
+
+namespace v8 {
+namespace internal {
+
+
+class IrregexpInterpreter {
+ public:
+ static RegExpImpl::IrregexpResult Match(Isolate* isolate,
+ Handle<ByteArray> code,
+ Handle<String> subject,
+ int* captures,
+ int start_position);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_INTERPRETER_IRREGEXP_H_
diff --git a/chromium/v8/src/isolate-inl.h b/chromium/v8/src/isolate-inl.h
new file mode 100644
index 00000000000..9fb16fbe96d
--- /dev/null
+++ b/chromium/v8/src/isolate-inl.h
@@ -0,0 +1,73 @@
+// 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_ISOLATE_INL_H_
+#define V8_ISOLATE_INL_H_
+
+#include "isolate.h"
+
+#include "debug.h"
+
+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::IsDebuggerActive() {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (!NoBarrier_Load(&debugger_initialized_)) return false;
+ return debugger()->IsDebuggerActive();
+#else
+ return false;
+#endif
+}
+
+
+bool Isolate::DebuggerHasBreakPoints() {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ return debug()->has_break_points();
+#else
+ return false;
+#endif
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_ISOLATE_INL_H_
diff --git a/chromium/v8/src/isolate.cc b/chromium/v8/src/isolate.cc
new file mode 100644
index 00000000000..7b77d893f15
--- /dev/null
+++ b/chromium/v8/src/isolate.cc
@@ -0,0 +1,2534 @@
+// Copyright 2012 the V8 project authors. 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 "allocation-inl.h"
+#include "ast.h"
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "compilation-cache.h"
+#include "cpu-profiler.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "heap-profiler.h"
+#include "hydrogen.h"
+#include "isolate.h"
+#include "lithium-allocator.h"
+#include "log.h"
+#include "marking-thread.h"
+#include "messages.h"
+#include "platform.h"
+#include "regexp-stack.h"
+#include "runtime-profiler.h"
+#include "sampler.h"
+#include "scopeinfo.h"
+#include "serialize.h"
+#include "simulator.h"
+#include "spaces.h"
+#include "stub-cache.h"
+#include "sweeper-thread.h"
+#include "version.h"
+#include "vm-state-inl.h"
+
+
+namespace v8 {
+namespace internal {
+
+Atomic32 ThreadId::highest_thread_id_ = 0;
+
+int ThreadId::AllocateThreadId() {
+ int new_id = NoBarrier_AtomicIncrement(&highest_thread_id_, 1);
+ return new_id;
+}
+
+
+int ThreadId::GetCurrentThreadId() {
+ int thread_id = Thread::GetThreadLocalInt(Isolate::thread_id_key_);
+ if (thread_id == 0) {
+ thread_id = AllocateThreadId();
+ Thread::SetThreadLocalInt(Isolate::thread_id_key_, thread_id);
+ }
+ return thread_id;
+}
+
+
+ThreadLocalTop::ThreadLocalTop() {
+ InitializeInternal();
+ // This flag may be set using v8::V8::IgnoreOutOfMemoryException()
+ // before an isolate is initialized. The initialize methods below do
+ // not touch it to preserve its value.
+ ignore_out_of_memory_ = false;
+}
+
+
+void ThreadLocalTop::InitializeInternal() {
+ c_entry_fp_ = 0;
+ handler_ = 0;
+#ifdef USE_SIMULATOR
+ simulator_ = NULL;
+#endif
+ js_entry_sp_ = NULL;
+ external_callback_scope_ = NULL;
+ current_vm_state_ = EXTERNAL;
+ try_catch_handler_address_ = NULL;
+ context_ = NULL;
+ thread_id_ = ThreadId::Invalid();
+ external_caught_exception_ = false;
+ 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;
+ rethrowing_message_ = false;
+ pending_message_obj_ = NULL;
+ pending_message_script_ = NULL;
+ scheduled_exception_ = NULL;
+}
+
+
+void ThreadLocalTop::Initialize() {
+ InitializeInternal();
+#ifdef USE_SIMULATOR
+#if V8_TARGET_ARCH_ARM
+ simulator_ = Simulator::current(isolate_);
+#elif V8_TARGET_ARCH_MIPS
+ simulator_ = Simulator::current(isolate_);
+#endif
+#endif
+ thread_id_ = ThreadId::Current();
+}
+
+
+v8::TryCatch* ThreadLocalTop::TryCatchHandler() {
+ return TRY_CATCH_FROM_ADDRESS(try_catch_handler_address());
+}
+
+
+int SystemThreadManager::NumberOfParallelSystemThreads(
+ ParallelSystemComponent type) {
+ int number_of_threads = Min(OS::NumberOfCores(), kMaxThreads);
+ ASSERT(number_of_threads > 0);
+ if (number_of_threads == 1) {
+ return 0;
+ }
+ if (type == PARALLEL_SWEEPING) {
+ return number_of_threads;
+ } else if (type == CONCURRENT_SWEEPING) {
+ return number_of_threads - 1;
+ } else if (type == PARALLEL_MARKING) {
+ return number_of_threads;
+ }
+ return 1;
+}
+
+
+// Create a dummy thread that will wait forever on a semaphore. The only
+// purpose for this thread is to have some stack area to save essential data
+// into for use by a stacks only core dump (aka minidump).
+class PreallocatedMemoryThread: public Thread {
+ public:
+ char* data() {
+ if (data_ready_semaphore_ != NULL) {
+ // Initial access is guarded until the data has been published.
+ data_ready_semaphore_->Wait();
+ delete data_ready_semaphore_;
+ data_ready_semaphore_ = NULL;
+ }
+ return data_;
+ }
+
+ unsigned length() {
+ if (data_ready_semaphore_ != NULL) {
+ // Initial access is guarded until the data has been published.
+ data_ready_semaphore_->Wait();
+ delete data_ready_semaphore_;
+ data_ready_semaphore_ = NULL;
+ }
+ return length_;
+ }
+
+ // Stop the PreallocatedMemoryThread and release its resources.
+ void StopThread() {
+ keep_running_ = false;
+ wait_for_ever_semaphore_->Signal();
+
+ // Wait for the thread to terminate.
+ Join();
+
+ if (data_ready_semaphore_ != NULL) {
+ delete data_ready_semaphore_;
+ data_ready_semaphore_ = NULL;
+ }
+
+ delete wait_for_ever_semaphore_;
+ wait_for_ever_semaphore_ = NULL;
+ }
+
+ protected:
+ // When the thread starts running it will allocate a fixed number of bytes
+ // on the stack and publish the location of this memory for others to use.
+ void Run() {
+ EmbeddedVector<char, 15 * 1024> local_buffer;
+
+ // Initialize the buffer with a known good value.
+ OS::StrNCpy(local_buffer, "Trace data was not generated.\n",
+ local_buffer.length());
+
+ // Publish the local buffer and signal its availability.
+ data_ = local_buffer.start();
+ length_ = local_buffer.length();
+ data_ready_semaphore_->Signal();
+
+ while (keep_running_) {
+ // This thread will wait here until the end of time.
+ wait_for_ever_semaphore_->Wait();
+ }
+
+ // Make sure we access the buffer after the wait to remove all possibility
+ // of it being optimized away.
+ OS::StrNCpy(local_buffer, "PreallocatedMemoryThread shutting down.\n",
+ local_buffer.length());
+ }
+
+
+ private:
+ PreallocatedMemoryThread()
+ : Thread("v8:PreallocMem"),
+ keep_running_(true),
+ wait_for_ever_semaphore_(OS::CreateSemaphore(0)),
+ data_ready_semaphore_(OS::CreateSemaphore(0)),
+ data_(NULL),
+ length_(0) {
+ }
+
+ // Used to make sure that the thread keeps looping even for spurious wakeups.
+ bool keep_running_;
+
+ // This semaphore is used by the PreallocatedMemoryThread to wait for ever.
+ Semaphore* wait_for_ever_semaphore_;
+ // Semaphore to signal that the data has been initialized.
+ Semaphore* data_ready_semaphore_;
+
+ // Location and size of the preallocated memory block.
+ char* data_;
+ unsigned length_;
+
+ friend class Isolate;
+
+ DISALLOW_COPY_AND_ASSIGN(PreallocatedMemoryThread);
+};
+
+
+void Isolate::PreallocatedMemoryThreadStart() {
+ if (preallocated_memory_thread_ != NULL) return;
+ preallocated_memory_thread_ = new PreallocatedMemoryThread();
+ preallocated_memory_thread_->Start();
+}
+
+
+void Isolate::PreallocatedMemoryThreadStop() {
+ if (preallocated_memory_thread_ == NULL) return;
+ preallocated_memory_thread_->StopThread();
+ // Done with the thread entirely.
+ delete preallocated_memory_thread_;
+ preallocated_memory_thread_ = NULL;
+}
+
+
+void Isolate::PreallocatedStorageInit(size_t size) {
+ ASSERT(free_list_.next_ == &free_list_);
+ ASSERT(free_list_.previous_ == &free_list_);
+ PreallocatedStorage* free_chunk =
+ reinterpret_cast<PreallocatedStorage*>(new char[size]);
+ free_list_.next_ = free_list_.previous_ = free_chunk;
+ free_chunk->next_ = free_chunk->previous_ = &free_list_;
+ free_chunk->size_ = size - sizeof(PreallocatedStorage);
+ preallocated_storage_preallocated_ = true;
+}
+
+
+void* Isolate::PreallocatedStorageNew(size_t size) {
+ if (!preallocated_storage_preallocated_) {
+ return FreeStoreAllocationPolicy().New(size);
+ }
+ ASSERT(free_list_.next_ != &free_list_);
+ ASSERT(free_list_.previous_ != &free_list_);
+
+ size = (size + kPointerSize - 1) & ~(kPointerSize - 1);
+ // Search for exact fit.
+ for (PreallocatedStorage* storage = free_list_.next_;
+ storage != &free_list_;
+ storage = storage->next_) {
+ if (storage->size_ == size) {
+ storage->Unlink();
+ storage->LinkTo(&in_use_list_);
+ return reinterpret_cast<void*>(storage + 1);
+ }
+ }
+ // Search for first fit.
+ for (PreallocatedStorage* storage = free_list_.next_;
+ storage != &free_list_;
+ storage = storage->next_) {
+ if (storage->size_ >= size + sizeof(PreallocatedStorage)) {
+ storage->Unlink();
+ storage->LinkTo(&in_use_list_);
+ PreallocatedStorage* left_over =
+ reinterpret_cast<PreallocatedStorage*>(
+ reinterpret_cast<char*>(storage + 1) + size);
+ left_over->size_ = storage->size_ - size - sizeof(PreallocatedStorage);
+ ASSERT(size + left_over->size_ + sizeof(PreallocatedStorage) ==
+ storage->size_);
+ storage->size_ = size;
+ left_over->LinkTo(&free_list_);
+ return reinterpret_cast<void*>(storage + 1);
+ }
+ }
+ // Allocation failure.
+ ASSERT(false);
+ return NULL;
+}
+
+
+// We don't attempt to coalesce.
+void Isolate::PreallocatedStorageDelete(void* p) {
+ if (p == NULL) {
+ return;
+ }
+ if (!preallocated_storage_preallocated_) {
+ FreeStoreAllocationPolicy::Delete(p);
+ return;
+ }
+ PreallocatedStorage* storage = reinterpret_cast<PreallocatedStorage*>(p) - 1;
+ ASSERT(storage->next_->previous_ == storage);
+ ASSERT(storage->previous_->next_ == storage);
+ storage->Unlink();
+ storage->LinkTo(&free_list_);
+}
+
+Isolate* Isolate::default_isolate_ = NULL;
+Thread::LocalStorageKey Isolate::isolate_key_;
+Thread::LocalStorageKey Isolate::thread_id_key_;
+Thread::LocalStorageKey Isolate::per_isolate_thread_data_key_;
+#ifdef DEBUG
+Thread::LocalStorageKey PerThreadAssertScopeBase::thread_local_key;
+#endif // DEBUG
+Mutex* Isolate::process_wide_mutex_ = OS::CreateMutex();
+Isolate::ThreadDataTable* Isolate::thread_data_table_ = NULL;
+Atomic32 Isolate::isolate_counter_ = 0;
+
+Isolate::PerIsolateThreadData* Isolate::AllocatePerIsolateThreadData(
+ ThreadId thread_id) {
+ ASSERT(!thread_id.Equals(ThreadId::Invalid()));
+ PerIsolateThreadData* per_thread = new PerIsolateThreadData(this, thread_id);
+ {
+ ScopedLock lock(process_wide_mutex_);
+ ASSERT(thread_data_table_->Lookup(this, thread_id) == NULL);
+ thread_data_table_->Insert(per_thread);
+ ASSERT(thread_data_table_->Lookup(this, thread_id) == per_thread);
+ }
+ return per_thread;
+}
+
+
+Isolate::PerIsolateThreadData*
+ Isolate::FindOrAllocatePerThreadDataForThisThread() {
+ ThreadId thread_id = ThreadId::Current();
+ PerIsolateThreadData* per_thread = NULL;
+ {
+ ScopedLock lock(process_wide_mutex_);
+ per_thread = thread_data_table_->Lookup(this, thread_id);
+ if (per_thread == NULL) {
+ per_thread = AllocatePerIsolateThreadData(thread_id);
+ }
+ }
+ return per_thread;
+}
+
+
+Isolate::PerIsolateThreadData* Isolate::FindPerThreadDataForThisThread() {
+ ThreadId thread_id = ThreadId::Current();
+ return FindPerThreadDataForThread(thread_id);
+}
+
+
+Isolate::PerIsolateThreadData* Isolate::FindPerThreadDataForThread(
+ ThreadId thread_id) {
+ PerIsolateThreadData* per_thread = NULL;
+ {
+ ScopedLock lock(process_wide_mutex_);
+ per_thread = thread_data_table_->Lookup(this, thread_id);
+ }
+ return per_thread;
+}
+
+
+void Isolate::EnsureDefaultIsolate() {
+ ScopedLock lock(process_wide_mutex_);
+ if (default_isolate_ == NULL) {
+ isolate_key_ = Thread::CreateThreadLocalKey();
+ thread_id_key_ = Thread::CreateThreadLocalKey();
+ per_isolate_thread_data_key_ = Thread::CreateThreadLocalKey();
+#ifdef DEBUG
+ PerThreadAssertScopeBase::thread_local_key = Thread::CreateThreadLocalKey();
+#endif // DEBUG
+ thread_data_table_ = new Isolate::ThreadDataTable();
+ default_isolate_ = new Isolate();
+ }
+ // Can't use SetIsolateThreadLocals(default_isolate_, NULL) here
+ // because a non-null thread data may be already set.
+ if (Thread::GetThreadLocal(isolate_key_) == NULL) {
+ Thread::SetThreadLocal(isolate_key_, default_isolate_);
+ }
+}
+
+struct StaticInitializer {
+ StaticInitializer() {
+ Isolate::EnsureDefaultIsolate();
+ }
+} static_initializer;
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+Debugger* Isolate::GetDefaultIsolateDebugger() {
+ EnsureDefaultIsolate();
+ return default_isolate_->debugger();
+}
+#endif
+
+
+StackGuard* Isolate::GetDefaultIsolateStackGuard() {
+ EnsureDefaultIsolate();
+ return default_isolate_->stack_guard();
+}
+
+
+void Isolate::EnterDefaultIsolate() {
+ EnsureDefaultIsolate();
+ ASSERT(default_isolate_ != NULL);
+
+ PerIsolateThreadData* data = CurrentPerIsolateThreadData();
+ // If not yet in default isolate - enter it.
+ if (data == NULL || data->isolate() != default_isolate_) {
+ default_isolate_->Enter();
+ }
+}
+
+
+v8::Isolate* Isolate::GetDefaultIsolateForLocking() {
+ EnsureDefaultIsolate();
+ return reinterpret_cast<v8::Isolate*>(default_isolate_);
+}
+
+
+Address Isolate::get_address_from_id(Isolate::AddressId id) {
+ return isolate_addresses_[id];
+}
+
+
+char* Isolate::Iterate(ObjectVisitor* v, char* thread_storage) {
+ ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(thread_storage);
+ Iterate(v, thread);
+ return thread_storage + sizeof(ThreadLocalTop);
+}
+
+
+void Isolate::IterateThread(ThreadVisitor* v, char* t) {
+ ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(t);
+ v->VisitThread(this, thread);
+}
+
+
+void Isolate::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) {
+ // Visit the roots from the top for a given thread.
+ Object* pending;
+ // The pending exception can sometimes be a failure. We can't show
+ // that to the GC, which only understands objects.
+ if (thread->pending_exception_->ToObject(&pending)) {
+ v->VisitPointer(&pending);
+ thread->pending_exception_ = pending; // In case GC updated it.
+ }
+ v->VisitPointer(&(thread->pending_message_obj_));
+ v->VisitPointer(BitCast<Object**>(&(thread->pending_message_script_)));
+ v->VisitPointer(BitCast<Object**>(&(thread->context_)));
+ Object* scheduled;
+ if (thread->scheduled_exception_->ToObject(&scheduled)) {
+ v->VisitPointer(&scheduled);
+ thread->scheduled_exception_ = scheduled;
+ }
+
+ for (v8::TryCatch* block = thread->TryCatchHandler();
+ block != NULL;
+ block = TRY_CATCH_FROM_ADDRESS(block->next_)) {
+ v->VisitPointer(BitCast<Object**>(&(block->exception_)));
+ v->VisitPointer(BitCast<Object**>(&(block->message_obj_)));
+ v->VisitPointer(BitCast<Object**>(&(block->message_script_)));
+ }
+
+ // Iterate over pointers on native execution stack.
+ 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);
+}
+
+
+void Isolate::Iterate(ObjectVisitor* v) {
+ ThreadLocalTop* current_t = thread_local_top();
+ Iterate(v, current_t);
+}
+
+
+void Isolate::IterateDeferredHandles(ObjectVisitor* visitor) {
+ for (DeferredHandles* deferred = deferred_handles_head_;
+ deferred != NULL;
+ deferred = deferred->next_) {
+ deferred->Iterate(visitor);
+ }
+}
+
+
+#ifdef DEBUG
+bool Isolate::IsDeferredHandle(Object** handle) {
+ // Each DeferredHandles instance keeps the handles to one job in the
+ // parallel recompilation queue, containing a list of blocks. Each block
+ // contains kHandleBlockSize handles except for the first block, which may
+ // not be fully filled.
+ // We iterate through all the blocks to see whether the argument handle
+ // belongs to one of the blocks. If so, it is deferred.
+ for (DeferredHandles* deferred = deferred_handles_head_;
+ deferred != NULL;
+ deferred = deferred->next_) {
+ List<Object**>* blocks = &deferred->blocks_;
+ for (int i = 0; i < blocks->length(); i++) {
+ Object** block_limit = (i == 0) ? deferred->first_block_limit_
+ : blocks->at(i) + kHandleBlockSize;
+ if (blocks->at(i) <= handle && handle < block_limit) return true;
+ }
+ }
+ return false;
+}
+#endif // DEBUG
+
+
+void Isolate::RegisterTryCatchHandler(v8::TryCatch* that) {
+ // The ARM simulator has a separate JS stack. We therefore register
+ // the C++ try catch handler with the simulator and get back an
+ // address that can be used for comparisons with addresses into the
+ // JS stack. When running without the simulator, the address
+ // returned will be the address of the C++ try catch handler itself.
+ Address address = reinterpret_cast<Address>(
+ SimulatorStack::RegisterCTryCatch(reinterpret_cast<uintptr_t>(that)));
+ thread_local_top()->set_try_catch_handler_address(address);
+}
+
+
+void Isolate::UnregisterTryCatchHandler(v8::TryCatch* that) {
+ ASSERT(thread_local_top()->TryCatchHandler() == that);
+ thread_local_top()->set_try_catch_handler_address(
+ reinterpret_cast<Address>(that->next_));
+ thread_local_top()->catcher_ = NULL;
+ SimulatorStack::UnregisterCTryCatch();
+}
+
+
+Handle<String> Isolate::StackTraceString() {
+ if (stack_trace_nesting_level_ == 0) {
+ stack_trace_nesting_level_++;
+ HeapStringAllocator allocator;
+ StringStream::ClearMentionedObjectCache();
+ StringStream accumulator(&allocator);
+ incomplete_message_ = &accumulator;
+ PrintStack(&accumulator);
+ Handle<String> stack_trace = accumulator.ToString();
+ incomplete_message_ = NULL;
+ stack_trace_nesting_level_ = 0;
+ return stack_trace;
+ } else if (stack_trace_nesting_level_ == 1) {
+ stack_trace_nesting_level_++;
+ OS::PrintError(
+ "\n\nAttempt to print stack while printing stack (double fault)\n");
+ OS::PrintError(
+ "If you are lucky you may find a partial stack dump on stdout.\n\n");
+ incomplete_message_->OutputToStdOut();
+ return factory()->empty_string();
+ } else {
+ OS::Abort();
+ // Unreachable
+ return factory()->empty_string();
+ }
+}
+
+
+void Isolate::PushStackTraceAndDie(unsigned int magic,
+ Object* object,
+ Map* map,
+ unsigned int magic2) {
+ const int kMaxStackTraceSize = 8192;
+ Handle<String> trace = StackTraceString();
+ uint8_t buffer[kMaxStackTraceSize];
+ int length = Min(kMaxStackTraceSize - 1, trace->length());
+ String::WriteToFlat(*trace, buffer, 0, length);
+ buffer[length] = '\0';
+ // TODO(dcarney): convert buffer to utf8?
+ OS::PrintError("Stacktrace (%x-%x) %p %p: %s\n",
+ magic, magic2,
+ static_cast<void*>(object), static_cast<void*>(map),
+ reinterpret_cast<char*>(buffer));
+ OS::Abort();
+}
+
+
+// Determines whether the given stack frame should be displayed in
+// a stack trace. The caller is the error constructor that asked
+// for the stack trace to be collected. The first time a construct
+// call to this function is encountered it is skipped. The seen_caller
+// in/out parameter is used to remember if the caller has been seen
+// yet.
+static bool IsVisibleInStackTrace(StackFrame* raw_frame,
+ Object* caller,
+ bool* seen_caller) {
+ // Only display JS frames.
+ if (!raw_frame->is_java_script()) return false;
+ JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
+ JSFunction* fun = frame->function();
+ if ((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 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) {
+ if (frame->receiver()->IsJSBuiltinsObject() ||
+ (fun->IsBuiltin() && !fun->shared()->native())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+Handle<JSArray> Isolate::CaptureSimpleStackTrace(Handle<JSObject> error_object,
+ Handle<Object> caller,
+ int limit) {
+ limit = Max(limit, 0); // Ensure that limit is not negative.
+ int initial_size = Min(limit, 10);
+ Handle<FixedArray> elements =
+ factory()->NewFixedArrayWithHoles(initial_size * 4 + 1);
+
+ // If the caller parameter is a function we skip frames until we're
+ // under it before starting to collect.
+ bool seen_caller = !caller->IsJSFunction();
+ // First element is reserved to store the number of non-strict frames.
+ int cursor = 1;
+ int frames_seen = 0;
+ int non_strict_frames = 0;
+ bool encountered_strict_function = false;
+ for (StackFrameIterator iter(this);
+ !iter.done() && frames_seen < limit;
+ iter.Advance()) {
+ StackFrame* raw_frame = iter.frame();
+ if (IsVisibleInStackTrace(raw_frame, *caller, &seen_caller)) {
+ frames_seen++;
+ JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
+ // Set initial size to the maximum inlining level + 1 for the outermost
+ // function.
+ List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
+ frame->Summarize(&frames);
+ for (int i = frames.length() - 1; i >= 0; i--) {
+ if (cursor + 4 > elements->length()) {
+ int new_capacity = JSObject::NewElementsCapacity(elements->length());
+ Handle<FixedArray> new_elements =
+ factory()->NewFixedArrayWithHoles(new_capacity);
+ for (int i = 0; i < cursor; i++) {
+ new_elements->set(i, elements->get(i));
+ }
+ elements = new_elements;
+ }
+ ASSERT(cursor + 4 <= elements->length());
+
+ Handle<Object> recv = frames[i].receiver();
+ Handle<JSFunction> fun = frames[i].function();
+ Handle<Code> code = frames[i].code();
+ Handle<Smi> offset(Smi::FromInt(frames[i].offset()), this);
+ // The stack trace API should not expose receivers and function
+ // objects on frames deeper than the top-most one with a strict
+ // mode function. The number of non-strict frames is stored as
+ // first element in the result array.
+ if (!encountered_strict_function) {
+ if (!fun->shared()->is_classic_mode()) {
+ encountered_strict_function = true;
+ } else {
+ non_strict_frames++;
+ }
+ }
+ elements->set(cursor++, *recv);
+ elements->set(cursor++, *fun);
+ elements->set(cursor++, *code);
+ elements->set(cursor++, *offset);
+ }
+ }
+ }
+ elements->set(0, Smi::FromInt(non_strict_frames));
+ Handle<JSArray> result = factory()->NewJSArrayWithElements(elements);
+ result->set_length(Smi::FromInt(cursor));
+ return result;
+}
+
+
+void Isolate::CaptureAndSetDetailedStackTrace(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_string();
+ 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.
+ int limit = Max(frame_limit, 0);
+ Handle<JSArray> stack_trace = factory()->NewJSArray(frame_limit);
+
+ Handle<String> column_key =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("column"));
+ Handle<String> line_key =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("lineNumber"));
+ Handle<String> script_key =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("scriptName"));
+ Handle<String> script_name_or_source_url_key =
+ factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("scriptNameOrSourceURL"));
+ Handle<String> function_key =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("functionName"));
+ Handle<String> eval_key =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("isEval"));
+ Handle<String> constructor_key =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("isConstructor"));
+
+ StackTraceFrameIterator it(this);
+ int frames_seen = 0;
+ while (!it.done() && (frames_seen < limit)) {
+ JavaScriptFrame* frame = it.frame();
+ // Set initial size to the maximum inlining level + 1 for the outermost
+ // function.
+ List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
+ 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> stack_frame = factory()->NewJSObject(object_function());
+
+ Handle<JSFunction> fun = frames[i].function();
+ Handle<Script> script(Script::cast(fun->shared()->script()));
+
+ if (options & StackTrace::kLineNumber) {
+ int script_line_offset = script->line_offset()->value();
+ int position = frames[i].code()->SourcePosition(frames[i].pc());
+ int line_number = GetScriptLineNumber(script, position);
+ // line_number is already shifted by the script_line_offset.
+ int relative_line_number = line_number - script_line_offset;
+ if (options & StackTrace::kColumnOffset && relative_line_number >= 0) {
+ Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
+ int start = (relative_line_number == 0) ? 0 :
+ Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1;
+ int column_offset = position - start;
+ if (relative_line_number == 0) {
+ // For the case where the code is on the same line as the script
+ // tag.
+ column_offset += script->column_offset()->value();
+ }
+ CHECK_NOT_EMPTY_HANDLE(
+ this,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ stack_frame, column_key,
+ Handle<Smi>(Smi::FromInt(column_offset + 1), this), NONE));
+ }
+ CHECK_NOT_EMPTY_HANDLE(
+ this,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ stack_frame, line_key,
+ Handle<Smi>(Smi::FromInt(line_number + 1), this), NONE));
+ }
+
+ if (options & StackTrace::kScriptName) {
+ Handle<Object> script_name(script->name(), this);
+ CHECK_NOT_EMPTY_HANDLE(this,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ stack_frame, script_key, script_name, NONE));
+ }
+
+ if (options & StackTrace::kScriptNameOrSourceURL) {
+ Handle<Object> result = GetScriptNameOrSourceURL(script);
+ CHECK_NOT_EMPTY_HANDLE(this,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ stack_frame, script_name_or_source_url_key,
+ result, NONE));
+ }
+
+ if (options & StackTrace::kFunctionName) {
+ Handle<Object> fun_name(fun->shared()->name(), this);
+ if (!fun_name->BooleanValue()) {
+ fun_name = Handle<Object>(fun->shared()->inferred_name(), this);
+ }
+ CHECK_NOT_EMPTY_HANDLE(this,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ stack_frame, function_key, fun_name, NONE));
+ }
+
+ if (options & StackTrace::kIsEval) {
+ Handle<Object> is_eval =
+ script->compilation_type() == Script::COMPILATION_TYPE_EVAL ?
+ factory()->true_value() : factory()->false_value();
+ 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();
+ CHECK_NOT_EMPTY_HANDLE(this,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ stack_frame, constructor_key,
+ is_constructor, NONE));
+ }
+
+ FixedArray::cast(stack_trace->elements())->set(frames_seen, *stack_frame);
+ frames_seen++;
+ }
+ it.Advance();
+ }
+
+ stack_trace->set_length(Smi::FromInt(frames_seen));
+ return stack_trace;
+}
+
+
+void Isolate::PrintStack() {
+ PrintStack(stdout);
+}
+
+
+void Isolate::PrintStack(FILE* out) {
+ if (stack_trace_nesting_level_ == 0) {
+ stack_trace_nesting_level_++;
+
+ StringAllocator* allocator;
+ if (preallocated_message_space_ == NULL) {
+ allocator = new HeapStringAllocator();
+ } else {
+ allocator = preallocated_message_space_;
+ }
+
+ StringStream::ClearMentionedObjectCache();
+ StringStream accumulator(allocator);
+ incomplete_message_ = &accumulator;
+ PrintStack(&accumulator);
+ accumulator.OutputToFile(out);
+ InitializeLoggingAndCounters();
+ accumulator.Log();
+ incomplete_message_ = NULL;
+ stack_trace_nesting_level_ = 0;
+ if (preallocated_message_space_ == NULL) {
+ // Remove the HeapStringAllocator created above.
+ delete allocator;
+ }
+ } else if (stack_trace_nesting_level_ == 1) {
+ stack_trace_nesting_level_++;
+ OS::PrintError(
+ "\n\nAttempt to print stack while printing stack (double fault)\n");
+ OS::PrintError(
+ "If you are lucky you may find a partial stack dump on stdout.\n\n");
+ incomplete_message_->OutputToFile(out);
+ }
+}
+
+
+static void PrintFrames(Isolate* isolate,
+ StringStream* accumulator,
+ StackFrame::PrintMode mode) {
+ StackFrameIterator it(isolate);
+ for (int i = 0; !it.done(); it.Advance()) {
+ it.frame()->Print(accumulator, mode, i++);
+ }
+}
+
+
+void Isolate::PrintStack(StringStream* accumulator) {
+ if (!IsInitialized()) {
+ accumulator->Add(
+ "\n==== JS stack trace is not available =======================\n\n");
+ accumulator->Add(
+ "\n==== Isolate for the thread is not initialized =============\n\n");
+ return;
+ }
+ // The MentionedObjectCache is not GC-proof at the moment.
+ DisallowHeapAllocation no_gc;
+ ASSERT(StringStream::IsMentionedObjectCacheClear());
+
+ // Avoid printing anything if there are no frames.
+ if (c_entry_fp(thread_local_top()) == 0) return;
+
+ accumulator->Add(
+ "\n==== JS stack trace =========================================\n\n");
+ PrintFrames(this, accumulator, StackFrame::OVERVIEW);
+
+ accumulator->Add(
+ "\n==== Details ================================================\n\n");
+ PrintFrames(this, accumulator, StackFrame::DETAILS);
+
+ accumulator->PrintMentionedObjectCache();
+ accumulator->Add("=====================\n\n");
+}
+
+
+void Isolate::SetFailedAccessCheckCallback(
+ v8::FailedAccessCheckCallback callback) {
+ thread_local_top()->failed_access_check_callback_ = callback;
+}
+
+
+void Isolate::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) {
+ if (!thread_local_top()->failed_access_check_callback_) return;
+
+ ASSERT(receiver->IsAccessCheckNeeded());
+ ASSERT(context());
+
+ // Get the data object from access check info.
+ JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
+ if (!constructor->shared()->IsApiFunction()) return;
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
+ if (data_obj == heap_.undefined_value()) return;
+
+ HandleScope scope(this);
+ Handle<JSObject> receiver_handle(receiver);
+ Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
+ { VMState<EXTERNAL> state(this);
+ thread_local_top()->failed_access_check_callback_(
+ v8::Utils::ToLocal(receiver_handle),
+ type,
+ v8::Utils::ToLocal(data));
+ }
+}
+
+
+enum MayAccessDecision {
+ YES, NO, UNKNOWN
+};
+
+
+static MayAccessDecision MayAccessPreCheck(Isolate* isolate,
+ JSObject* receiver,
+ v8::AccessType type) {
+ // During bootstrapping, callback functions are not enabled yet.
+ if (isolate->bootstrapper()->IsActive()) return YES;
+
+ if (receiver->IsJSGlobalProxy()) {
+ Object* receiver_context = JSGlobalProxy::cast(receiver)->native_context();
+ if (!receiver_context->IsContext()) return NO;
+
+ // Get the native context of current top context.
+ // avoid using Isolate::native_context() because it uses Handle.
+ Context* native_context =
+ isolate->context()->global_object()->native_context();
+ if (receiver_context == native_context) return YES;
+
+ if (Context::cast(receiver_context)->security_token() ==
+ native_context->security_token())
+ return YES;
+ }
+
+ return UNKNOWN;
+}
+
+
+bool Isolate::MayNamedAccess(JSObject* receiver, Object* key,
+ v8::AccessType type) {
+ ASSERT(receiver->IsAccessCheckNeeded());
+
+ // The callers of this method are not expecting a GC.
+ DisallowHeapAllocation no_gc;
+
+ // Skip checks for hidden properties access. Note, we do not
+ // require existence of a context in this case.
+ if (key == heap_.hidden_string()) return true;
+
+ // Check for compatibility between the security tokens in the
+ // current lexical context and the accessed object.
+ ASSERT(context());
+
+ MayAccessDecision decision = MayAccessPreCheck(this, receiver, type);
+ if (decision != UNKNOWN) return decision == YES;
+
+ // Get named access check callback
+ JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
+ if (!constructor->shared()->IsApiFunction()) return false;
+
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
+ if (data_obj == heap_.undefined_value()) return false;
+
+ Object* fun_obj = AccessCheckInfo::cast(data_obj)->named_callback();
+ v8::NamedSecurityCallback callback =
+ v8::ToCData<v8::NamedSecurityCallback>(fun_obj);
+
+ if (!callback) return false;
+
+ HandleScope scope(this);
+ Handle<JSObject> receiver_handle(receiver, this);
+ Handle<Object> key_handle(key, this);
+ Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
+ LOG(this, ApiNamedSecurityCheck(key));
+ bool result = false;
+ {
+ // Leaving JavaScript.
+ VMState<EXTERNAL> state(this);
+ result = callback(v8::Utils::ToLocal(receiver_handle),
+ v8::Utils::ToLocal(key_handle),
+ type,
+ v8::Utils::ToLocal(data));
+ }
+ return result;
+}
+
+
+bool Isolate::MayIndexedAccess(JSObject* receiver,
+ uint32_t index,
+ v8::AccessType type) {
+ ASSERT(receiver->IsAccessCheckNeeded());
+ // Check for compatibility between the security tokens in the
+ // current lexical context and the accessed object.
+ ASSERT(context());
+
+ MayAccessDecision decision = MayAccessPreCheck(this, receiver, type);
+ if (decision != UNKNOWN) return decision == YES;
+
+ // Get indexed access check callback
+ JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
+ if (!constructor->shared()->IsApiFunction()) return false;
+
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
+ if (data_obj == heap_.undefined_value()) return false;
+
+ Object* fun_obj = AccessCheckInfo::cast(data_obj)->indexed_callback();
+ v8::IndexedSecurityCallback callback =
+ v8::ToCData<v8::IndexedSecurityCallback>(fun_obj);
+
+ if (!callback) return false;
+
+ HandleScope scope(this);
+ Handle<JSObject> receiver_handle(receiver, this);
+ Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
+ LOG(this, ApiIndexedSecurityCheck(index));
+ bool result = false;
+ {
+ // Leaving JavaScript.
+ VMState<EXTERNAL> state(this);
+ result = callback(v8::Utils::ToLocal(receiver_handle),
+ index,
+ type,
+ v8::Utils::ToLocal(data));
+ }
+ return result;
+}
+
+
+const char* const Isolate::kStackOverflowMessage =
+ "Uncaught RangeError: Maximum call stack size exceeded";
+
+
+Failure* Isolate::StackOverflow() {
+ HandleScope scope(this);
+ // At this point we cannot create an Error object using its javascript
+ // constructor. Instead, we copy the pre-constructed boilerplate and
+ // attach the stack trace as a hidden property.
+ Handle<String> key = factory()->stack_overflow_string();
+ Handle<JSObject> boilerplate =
+ Handle<JSObject>::cast(GetProperty(this, js_builtins_object(), key));
+ Handle<JSObject> exception = Copy(boilerplate);
+ DoThrow(*exception, NULL);
+
+ // Get stack trace limit.
+ Handle<Object> error = GetProperty(js_builtins_object(), "$Error");
+ if (!error->IsJSObject()) return Failure::Exception();
+ Handle<Object> stack_trace_limit =
+ GetProperty(Handle<JSObject>::cast(error), "stackTraceLimit");
+ if (!stack_trace_limit->IsNumber()) return Failure::Exception();
+ double dlimit = stack_trace_limit->Number();
+ int limit = std::isnan(dlimit) ? 0 : static_cast<int>(dlimit);
+
+ Handle<JSArray> stack_trace = CaptureSimpleStackTrace(
+ exception, factory()->undefined_value(), limit);
+ JSObject::SetHiddenProperty(exception,
+ factory()->hidden_stack_trace_string(),
+ stack_trace);
+ return Failure::Exception();
+}
+
+
+Failure* Isolate::TerminateExecution() {
+ DoThrow(heap_.termination_exception(), NULL);
+ return Failure::Exception();
+}
+
+
+void Isolate::CancelTerminateExecution() {
+ if (try_catch_handler()) {
+ try_catch_handler()->has_terminated_ = false;
+ }
+ if (has_pending_exception() &&
+ pending_exception() == heap_.termination_exception()) {
+ thread_local_top()->external_caught_exception_ = false;
+ clear_pending_exception();
+ }
+ if (has_scheduled_exception() &&
+ scheduled_exception() == heap_.termination_exception()) {
+ thread_local_top()->external_caught_exception_ = false;
+ clear_scheduled_exception();
+ }
+}
+
+
+Failure* Isolate::Throw(Object* exception, MessageLocation* location) {
+ DoThrow(exception, location);
+ return Failure::Exception();
+}
+
+
+Failure* Isolate::ReThrow(MaybeObject* exception) {
+ bool can_be_caught_externally = false;
+ bool catchable_by_javascript = is_catchable_by_javascript(exception);
+ ShouldReportException(&can_be_caught_externally, catchable_by_javascript);
+
+ thread_local_top()->catcher_ = can_be_caught_externally ?
+ try_catch_handler() : NULL;
+
+ // Set the exception being re-thrown.
+ set_pending_exception(exception);
+ if (exception->IsFailure()) return exception->ToFailureUnchecked();
+ return Failure::Exception();
+}
+
+
+Failure* Isolate::ThrowIllegalOperation() {
+ return Throw(heap_.illegal_access_string());
+}
+
+
+void Isolate::ScheduleThrow(Object* exception) {
+ // When scheduling a throw we first throw the exception to get the
+ // error reporting if it is uncaught before rescheduling it.
+ Throw(exception);
+ PropagatePendingExceptionToExternalTryCatch();
+ if (has_pending_exception()) {
+ thread_local_top()->scheduled_exception_ = pending_exception();
+ thread_local_top()->external_caught_exception_ = false;
+ clear_pending_exception();
+ }
+}
+
+
+void Isolate::RestorePendingMessageFromTryCatch(v8::TryCatch* handler) {
+ ASSERT(handler == try_catch_handler());
+ ASSERT(handler->HasCaught());
+ ASSERT(handler->rethrow_);
+ ASSERT(handler->capture_message_);
+ Object* message = reinterpret_cast<Object*>(handler->message_obj_);
+ Object* script = reinterpret_cast<Object*>(handler->message_script_);
+ ASSERT(message->IsJSMessageObject() || message->IsTheHole());
+ ASSERT(script->IsScript() || script->IsTheHole());
+ thread_local_top()->pending_message_obj_ = message;
+ thread_local_top()->pending_message_script_ = script;
+ thread_local_top()->pending_message_start_pos_ = handler->message_start_pos_;
+ thread_local_top()->pending_message_end_pos_ = handler->message_end_pos_;
+}
+
+
+Failure* Isolate::PromoteScheduledException() {
+ MaybeObject* thrown = scheduled_exception();
+ clear_scheduled_exception();
+ // Re-throw the exception to avoid getting repeated error reporting.
+ return ReThrow(thrown);
+}
+
+
+void Isolate::PrintCurrentStackTrace(FILE* out) {
+ StackTraceFrameIterator it(this);
+ while (!it.done()) {
+ HandleScope scope(this);
+ // Find code position if recorded in relocation info.
+ JavaScriptFrame* frame = it.frame();
+ int pos = frame->LookupCode()->SourcePosition(frame->pc());
+ Handle<Object> pos_obj(Smi::FromInt(pos), this);
+ // Fetch function and receiver.
+ Handle<JSFunction> fun(frame->function());
+ Handle<Object> recv(frame->receiver(), this);
+ // Advance to the next JavaScript frame and determine if the
+ // current frame is the top-level frame.
+ it.Advance();
+ Handle<Object> is_top_level = it.done()
+ ? factory()->true_value()
+ : factory()->false_value();
+ // Generate and print stack trace line.
+ Handle<String> line =
+ Execution::GetStackTraceLine(recv, fun, pos_obj, is_top_level);
+ if (line->length() > 0) {
+ line->PrintOn(out);
+ PrintF(out, "\n");
+ }
+ }
+}
+
+
+void Isolate::ComputeLocation(MessageLocation* target) {
+ *target = MessageLocation(Handle<Script>(heap_.empty_script()), -1, -1);
+ StackTraceFrameIterator it(this);
+ if (!it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ JSFunction* fun = frame->function();
+ Object* script = fun->shared()->script();
+ if (script->IsScript() &&
+ !(Script::cast(script)->source()->IsUndefined())) {
+ int pos = frame->LookupCode()->SourcePosition(frame->pc());
+ // Compute the location from the function and the reloc info.
+ Handle<Script> casted_script(Script::cast(script));
+ *target = MessageLocation(casted_script, pos, pos + 1);
+ }
+ }
+}
+
+
+bool Isolate::ShouldReportException(bool* can_be_caught_externally,
+ bool catchable_by_javascript) {
+ // Find the top-most try-catch handler.
+ StackHandler* handler =
+ StackHandler::FromAddress(Isolate::handler(thread_local_top()));
+ while (handler != NULL && !handler->is_catch()) {
+ handler = handler->next();
+ }
+
+ // Get the address of the external handler so we can compare the address to
+ // determine which one is closer to the top of the stack.
+ Address external_handler_address =
+ thread_local_top()->try_catch_handler_address();
+
+ // The exception has been externally caught if and only if there is
+ // an external handler which is on top of the top-most try-catch
+ // handler.
+ *can_be_caught_externally = external_handler_address != NULL &&
+ (handler == NULL || handler->address() > external_handler_address ||
+ !catchable_by_javascript);
+
+ if (*can_be_caught_externally) {
+ // Only report the exception if the external handler is verbose.
+ return try_catch_handler()->is_verbose_;
+ } else {
+ // Report the exception if it isn't caught by JavaScript code.
+ return handler == NULL;
+ }
+}
+
+
+bool Isolate::IsErrorObject(Handle<Object> obj) {
+ if (!obj->IsJSObject()) return false;
+
+ String* error_key =
+ *(factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("$Error")));
+ Object* error_constructor =
+ js_builtins_object()->GetPropertyNoExceptionThrown(error_key);
+
+ for (Object* prototype = *obj; !prototype->IsNull();
+ prototype = prototype->GetPrototype(this)) {
+ if (!prototype->IsJSObject()) return false;
+ if (JSObject::cast(prototype)->map()->constructor() == error_constructor) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static int fatal_exception_depth = 0;
+
+void Isolate::DoThrow(Object* exception, MessageLocation* location) {
+ ASSERT(!has_pending_exception());
+
+ HandleScope scope(this);
+ Handle<Object> exception_handle(exception, this);
+
+ // Determine reporting and whether the exception is caught externally.
+ bool catchable_by_javascript = is_catchable_by_javascript(exception);
+ 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_ &&
+ !thread_local_top()->rethrowing_message_;
+ bool bootstrapping = bootstrapper()->IsActive();
+
+ thread_local_top()->rethrowing_message_ = false;
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Notify debugger of exception.
+ if (catchable_by_javascript) {
+ debugger_->OnException(exception_handle, report_exception);
+ }
+#endif
+
+ // 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.
+ ComputeLocation(&potential_computed_location);
+ location = &potential_computed_location;
+ }
+ // 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 (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_string();
+ 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_);
+ }
+ }
+
+ Handle<Object> exception_arg = exception_handle;
+ // If the exception argument is a custom object, turn it into a string
+ // before throwing as uncaught exception. Note that the pending
+ // exception object to be set later must not be turned into a string.
+ if (exception_arg->IsJSObject() && !IsErrorObject(exception_arg)) {
+ bool failed = false;
+ exception_arg = Execution::ToDetailString(exception_arg, &failed);
+ if (failed) {
+ exception_arg = factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("exception"));
+ }
+ }
+ Handle<Object> message_obj = MessageHandler::MakeMessageObject(
+ this,
+ "uncaught_exception",
+ location,
+ HandleVector<Object>(&exception_arg, 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();
+ }
+
+ // If the abort-on-uncaught-exception flag is specified, abort on any
+ // exception not caught by JavaScript, even when an external handler is
+ // present. This flag is intended for use by JavaScript developers, so
+ // print a user-friendly stack trace (not an internal one).
+ if (fatal_exception_depth == 0 &&
+ FLAG_abort_on_uncaught_exception &&
+ (report_exception || can_be_caught_externally)) {
+ fatal_exception_depth++;
+ PrintF(stderr,
+ "%s\n\nFROM\n",
+ *MessageHandler::GetLocalizedMessage(this, message_obj));
+ PrintCurrentStackTrace(stderr);
+ OS::Abort();
+ }
+ } 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());
+ if (exception->IsString()) {
+ OS::PrintError(
+ "Extension or internal compilation error: %s in %s at line %d.\n",
+ *String::cast(exception)->ToCString(),
+ *String::cast(location->script()->name())->ToCString(),
+ line_number + 1);
+ } else {
+ OS::PrintError(
+ "Extension or internal compilation error in %s at line %d.\n",
+ *String::cast(location->script()->name())->ToCString(),
+ line_number + 1);
+ }
+ }
+ }
+
+ // Save the message for reporting if the the exception remains uncaught.
+ thread_local_top()->has_pending_message_ = report_exception;
+
+ // 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;
+
+ set_pending_exception(*exception_handle);
+}
+
+
+bool Isolate::IsExternallyCaught() {
+ ASSERT(has_pending_exception());
+
+ if ((thread_local_top()->catcher_ == NULL) ||
+ (try_catch_handler() != thread_local_top()->catcher_)) {
+ // When throwing the exception, we found no v8::TryCatch
+ // which should care about this exception.
+ return false;
+ }
+
+ if (!is_catchable_by_javascript(pending_exception())) {
+ return true;
+ }
+
+ // Get the address of the external handler so we can compare the address to
+ // determine which one is closer to the top of the stack.
+ Address external_handler_address =
+ thread_local_top()->try_catch_handler_address();
+ ASSERT(external_handler_address != NULL);
+
+ // The exception has been externally caught if and only if there is
+ // an external handler which is on top of the top-most try-finally
+ // handler.
+ // There should be no try-catch blocks as they would prohibit us from
+ // finding external catcher in the first place (see catcher_ check above).
+ //
+ // Note, that finally clause would rethrow an exception unless it's
+ // aborted by jumps in control flow like return, break, etc. and we'll
+ // have another chances to set proper v8::TryCatch.
+ StackHandler* handler =
+ StackHandler::FromAddress(Isolate::handler(thread_local_top()));
+ while (handler != NULL && handler->address() < external_handler_address) {
+ ASSERT(!handler->is_catch());
+ if (handler->is_finally()) return false;
+
+ handler = handler->next();
+ }
+
+ return true;
+}
+
+
+void Isolate::ReportPendingMessages() {
+ ASSERT(has_pending_exception());
+ PropagatePendingExceptionToExternalTryCatch();
+
+ // If the pending exception is OutOfMemoryException set out_of_memory in
+ // the native context. Note: We have to mark the native context here
+ // since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to
+ // set it.
+ HandleScope scope(this);
+ if (thread_local_top_.pending_exception_->IsOutOfMemory()) {
+ context()->mark_out_of_memory();
+ } else if (thread_local_top_.pending_exception_ ==
+ heap()->termination_exception()) {
+ // Do nothing: if needed, the exception has been already propagated to
+ // v8::TryCatch.
+ } else {
+ if (thread_local_top_.has_pending_message_) {
+ thread_local_top_.has_pending_message_ = false;
+ if (!thread_local_top_.pending_message_obj_->IsTheHole()) {
+ HandleScope scope(this);
+ Handle<Object> message_obj(thread_local_top_.pending_message_obj_,
+ this);
+ if (!thread_local_top_.pending_message_script_->IsTheHole()) {
+ Handle<Script> script(
+ Script::cast(thread_local_top_.pending_message_script_));
+ int start_pos = thread_local_top_.pending_message_start_pos_;
+ int end_pos = thread_local_top_.pending_message_end_pos_;
+ MessageLocation location(script, start_pos, end_pos);
+ MessageHandler::ReportMessage(this, &location, message_obj);
+ } else {
+ MessageHandler::ReportMessage(this, NULL, message_obj);
+ }
+ }
+ }
+ }
+ clear_pending_message();
+}
+
+
+MessageLocation Isolate::GetMessageLocation() {
+ ASSERT(has_pending_exception());
+
+ if (!thread_local_top_.pending_exception_->IsOutOfMemory() &&
+ thread_local_top_.pending_exception_ != heap()->termination_exception() &&
+ thread_local_top_.has_pending_message_ &&
+ !thread_local_top_.pending_message_obj_->IsTheHole() &&
+ !thread_local_top_.pending_message_obj_->IsTheHole()) {
+ Handle<Script> script(
+ Script::cast(thread_local_top_.pending_message_script_));
+ int start_pos = thread_local_top_.pending_message_start_pos_;
+ int end_pos = thread_local_top_.pending_message_end_pos_;
+ return MessageLocation(script, start_pos, end_pos);
+ }
+
+ return MessageLocation();
+}
+
+
+void Isolate::TraceException(bool flag) {
+ FLAG_trace_exception = flag; // TODO(isolates): This is an unfortunate use.
+}
+
+
+bool Isolate::OptionalRescheduleException(bool is_bottom_call) {
+ ASSERT(has_pending_exception());
+ PropagatePendingExceptionToExternalTryCatch();
+
+ // Always reschedule out of memory exceptions.
+ if (!is_out_of_memory()) {
+ bool is_termination_exception =
+ pending_exception() == heap_.termination_exception();
+
+ // Do not reschedule the exception if this is the bottom call.
+ bool clear_exception = is_bottom_call;
+
+ if (is_termination_exception) {
+ if (is_bottom_call) {
+ thread_local_top()->external_caught_exception_ = false;
+ clear_pending_exception();
+ return false;
+ }
+ } else if (thread_local_top()->external_caught_exception_) {
+ // If the exception is externally caught, clear it if there are no
+ // JavaScript frames on the way to the C++ frame that has the
+ // external handler.
+ ASSERT(thread_local_top()->try_catch_handler_address() != NULL);
+ Address external_handler_address =
+ thread_local_top()->try_catch_handler_address();
+ JavaScriptFrameIterator it(this);
+ if (it.done() || (it.frame()->sp() > external_handler_address)) {
+ clear_exception = true;
+ }
+ }
+
+ // Clear the exception if needed.
+ if (clear_exception) {
+ thread_local_top()->external_caught_exception_ = false;
+ clear_pending_exception();
+ return false;
+ }
+ }
+
+ // Reschedule the exception.
+ thread_local_top()->scheduled_exception_ = pending_exception();
+ clear_pending_exception();
+ return true;
+}
+
+
+void Isolate::SetCaptureStackTraceForUncaughtExceptions(
+ bool capture,
+ int frame_limit,
+ StackTrace::StackTraceOptions options) {
+ capture_stack_trace_for_uncaught_exceptions_ = capture;
+ stack_trace_for_uncaught_exceptions_frame_limit_ = frame_limit;
+ stack_trace_for_uncaught_exceptions_options_ = options;
+}
+
+
+bool Isolate::is_out_of_memory() {
+ if (has_pending_exception()) {
+ MaybeObject* e = pending_exception();
+ if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
+ return true;
+ }
+ }
+ if (has_scheduled_exception()) {
+ MaybeObject* e = scheduled_exception();
+ if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+Handle<Context> Isolate::native_context() {
+ return Handle<Context>(context()->global_object()->native_context());
+}
+
+
+Handle<Context> Isolate::global_context() {
+ return Handle<Context>(context()->global_object()->global_context());
+}
+
+
+Handle<Context> Isolate::GetCallingNativeContext() {
+ JavaScriptFrameIterator it(this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (debug_->InDebugger()) {
+ while (!it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ Context* context = Context::cast(frame->context());
+ if (context->native_context() == *debug_->debug_context()) {
+ it.Advance();
+ } else {
+ break;
+ }
+ }
+ }
+#endif // ENABLE_DEBUGGER_SUPPORT
+ if (it.done()) return Handle<Context>::null();
+ JavaScriptFrame* frame = it.frame();
+ Context* context = Context::cast(frame->context());
+ return Handle<Context>(context->native_context());
+}
+
+
+char* Isolate::ArchiveThread(char* to) {
+ OS::MemCopy(to, reinterpret_cast<char*>(thread_local_top()),
+ sizeof(ThreadLocalTop));
+ InitializeThreadLocal();
+ clear_pending_exception();
+ clear_pending_message();
+ clear_scheduled_exception();
+ return to + sizeof(ThreadLocalTop);
+}
+
+
+char* Isolate::RestoreThread(char* from) {
+ OS::MemCopy(reinterpret_cast<char*>(thread_local_top()), from,
+ sizeof(ThreadLocalTop));
+ // This might be just paranoia, but it seems to be needed in case a
+ // thread_local_top_ is restored on a separate OS thread.
+#ifdef USE_SIMULATOR
+#if V8_TARGET_ARCH_ARM
+ thread_local_top()->simulator_ = Simulator::current(this);
+#elif V8_TARGET_ARCH_MIPS
+ thread_local_top()->simulator_ = Simulator::current(this);
+#endif
+#endif
+ ASSERT(context() == NULL || context()->IsContext());
+ return from + sizeof(ThreadLocalTop);
+}
+
+
+Isolate::ThreadDataTable::ThreadDataTable()
+ : list_(NULL) {
+}
+
+
+Isolate::ThreadDataTable::~ThreadDataTable() {
+ // TODO(svenpanne) The assertion below would fire if an embedder does not
+ // cleanly dispose all Isolates before disposing v8, so we are conservative
+ // and leave it out for now.
+ // ASSERT_EQ(NULL, list_);
+}
+
+
+Isolate::PerIsolateThreadData*
+ Isolate::ThreadDataTable::Lookup(Isolate* isolate,
+ ThreadId thread_id) {
+ for (PerIsolateThreadData* data = list_; data != NULL; data = data->next_) {
+ if (data->Matches(isolate, thread_id)) return data;
+ }
+ return NULL;
+}
+
+
+void Isolate::ThreadDataTable::Insert(Isolate::PerIsolateThreadData* data) {
+ if (list_ != NULL) list_->prev_ = data;
+ data->next_ = list_;
+ list_ = data;
+}
+
+
+void Isolate::ThreadDataTable::Remove(PerIsolateThreadData* data) {
+ if (list_ == data) list_ = data->next_;
+ if (data->next_ != NULL) data->next_->prev_ = data->prev_;
+ if (data->prev_ != NULL) data->prev_->next_ = data->next_;
+ delete data;
+}
+
+
+void Isolate::ThreadDataTable::Remove(Isolate* isolate,
+ ThreadId thread_id) {
+ PerIsolateThreadData* data = Lookup(isolate, thread_id);
+ if (data != NULL) {
+ Remove(data);
+ }
+}
+
+
+void Isolate::ThreadDataTable::RemoveAllThreads(Isolate* isolate) {
+ PerIsolateThreadData* data = list_;
+ while (data != NULL) {
+ PerIsolateThreadData* next = data->next_;
+ if (data->isolate() == isolate) Remove(data);
+ data = next;
+ }
+}
+
+
+#ifdef DEBUG
+#define TRACE_ISOLATE(tag) \
+ do { \
+ if (FLAG_trace_isolates) { \
+ PrintF("Isolate %p (id %d)" #tag "\n", \
+ reinterpret_cast<void*>(this), id()); \
+ } \
+ } while (false)
+#else
+#define TRACE_ISOLATE(tag)
+#endif
+
+
+Isolate::Isolate()
+ : state_(UNINITIALIZED),
+ embedder_data_(NULL),
+ entry_stack_(NULL),
+ stack_trace_nesting_level_(0),
+ incomplete_message_(NULL),
+ preallocated_memory_thread_(NULL),
+ preallocated_message_space_(NULL),
+ bootstrapper_(NULL),
+ runtime_profiler_(NULL),
+ compilation_cache_(NULL),
+ counters_(NULL),
+ code_range_(NULL),
+ // Must be initialized early to allow v8::SetResourceConstraints calls.
+ break_access_(OS::CreateMutex()),
+ debugger_initialized_(false),
+ // Must be initialized early to allow v8::Debug calls.
+ debugger_access_(OS::CreateMutex()),
+ logger_(NULL),
+ stats_table_(NULL),
+ stub_cache_(NULL),
+ deoptimizer_data_(NULL),
+ capture_stack_trace_for_uncaught_exceptions_(false),
+ stack_trace_for_uncaught_exceptions_frame_limit_(0),
+ stack_trace_for_uncaught_exceptions_options_(StackTrace::kOverview),
+ transcendental_cache_(NULL),
+ memory_allocator_(NULL),
+ keyed_lookup_cache_(NULL),
+ context_slot_cache_(NULL),
+ descriptor_lookup_cache_(NULL),
+ handle_scope_implementer_(NULL),
+ unicode_cache_(NULL),
+ runtime_zone_(this),
+ in_use_list_(0),
+ free_list_(0),
+ preallocated_storage_preallocated_(false),
+ inner_pointer_to_code_cache_(NULL),
+ write_iterator_(NULL),
+ global_handles_(NULL),
+ eternal_handles_(NULL),
+ context_switcher_(NULL),
+ thread_manager_(NULL),
+ fp_stubs_generated_(false),
+ has_installed_extensions_(false),
+ string_tracker_(NULL),
+ regexp_stack_(NULL),
+ date_cache_(NULL),
+ code_stub_interface_descriptors_(NULL),
+ initialized_from_snapshot_(false),
+ cpu_profiler_(NULL),
+ heap_profiler_(NULL),
+ function_entry_hook_(NULL),
+ deferred_handles_head_(NULL),
+ optimizing_compiler_thread_(this),
+ marking_thread_(NULL),
+ sweeper_thread_(NULL),
+ callback_table_(NULL),
+ stress_deopt_count_(0) {
+ id_ = NoBarrier_AtomicIncrement(&isolate_counter_, 1);
+ TRACE_ISOLATE(constructor);
+
+ memset(isolate_addresses_, 0,
+ sizeof(isolate_addresses_[0]) * (kIsolateAddressCount + 1));
+
+ heap_.isolate_ = this;
+ stack_guard_.isolate_ = this;
+
+ // ThreadManager is initialized early to support locking an isolate
+ // before it is entered.
+ thread_manager_ = new ThreadManager();
+ thread_manager_->isolate_ = this;
+
+#if V8_TARGET_ARCH_ARM && !defined(__arm__) || \
+ V8_TARGET_ARCH_MIPS && !defined(__mips__)
+ simulator_initialized_ = false;
+ simulator_i_cache_ = NULL;
+ simulator_redirection_ = NULL;
+#endif
+
+#ifdef DEBUG
+ // heap_histograms_ initializes itself.
+ memset(&js_spill_information_, 0, sizeof(js_spill_information_));
+ memset(code_kind_statistics_, 0,
+ sizeof(code_kind_statistics_[0]) * Code::NUMBER_OF_KINDS);
+#endif
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ debug_ = NULL;
+ debugger_ = NULL;
+#endif
+
+ handle_scope_data_.Initialize();
+
+#define ISOLATE_INIT_EXECUTE(type, name, initial_value) \
+ name##_ = (initial_value);
+ ISOLATE_INIT_LIST(ISOLATE_INIT_EXECUTE)
+#undef ISOLATE_INIT_EXECUTE
+
+#define ISOLATE_INIT_ARRAY_EXECUTE(type, name, length) \
+ memset(name##_, 0, sizeof(type) * length);
+ ISOLATE_INIT_ARRAY_LIST(ISOLATE_INIT_ARRAY_EXECUTE)
+#undef ISOLATE_INIT_ARRAY_EXECUTE
+}
+
+
+void Isolate::TearDown() {
+ TRACE_ISOLATE(tear_down);
+
+ // Temporarily set this isolate as current so that various parts of
+ // the isolate can access it in their destructors without having a
+ // direct pointer. We don't use Enter/Exit here to avoid
+ // initializing the thread data.
+ PerIsolateThreadData* saved_data = CurrentPerIsolateThreadData();
+ Isolate* saved_isolate = UncheckedCurrent();
+ SetIsolateThreadLocals(this, NULL);
+
+ Deinit();
+
+ { ScopedLock lock(process_wide_mutex_);
+ thread_data_table_->RemoveAllThreads(this);
+ }
+
+ if (serialize_partial_snapshot_cache_ != NULL) {
+ delete[] serialize_partial_snapshot_cache_;
+ serialize_partial_snapshot_cache_ = NULL;
+ }
+
+ if (!IsDefaultIsolate()) {
+ delete this;
+ }
+
+ // Restore the previous current isolate.
+ SetIsolateThreadLocals(saved_isolate, saved_data);
+}
+
+
+void Isolate::GlobalTearDown() {
+ delete thread_data_table_;
+}
+
+
+void Isolate::Deinit() {
+ if (state_ == INITIALIZED) {
+ TRACE_ISOLATE(deinit);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ debugger()->UnloadDebugger();
+#endif
+
+ if (FLAG_parallel_recompilation) optimizing_compiler_thread_.Stop();
+
+ if (FLAG_sweeper_threads > 0) {
+ for (int i = 0; i < FLAG_sweeper_threads; i++) {
+ sweeper_thread_[i]->Stop();
+ delete sweeper_thread_[i];
+ }
+ delete[] sweeper_thread_;
+ }
+
+ if (FLAG_marking_threads > 0) {
+ for (int i = 0; i < FLAG_marking_threads; i++) {
+ marking_thread_[i]->Stop();
+ delete marking_thread_[i];
+ }
+ delete[] marking_thread_;
+ }
+
+ if (FLAG_hydrogen_stats) GetHStatistics()->Print();
+
+ if (FLAG_print_deopt_stress) {
+ PrintF(stdout, "=== Stress deopt counter: %u\n", stress_deopt_count_);
+ }
+
+ // We must stop the logger before we tear down other components.
+ Sampler* sampler = logger_->sampler();
+ if (sampler && sampler->IsActive()) sampler->Stop();
+
+ delete deoptimizer_data_;
+ deoptimizer_data_ = NULL;
+ if (FLAG_preemption) {
+ v8::Locker locker(reinterpret_cast<v8::Isolate*>(this));
+ v8::Locker::StopPreemption();
+ }
+ builtins_.TearDown();
+ bootstrapper_->TearDown();
+
+ // Remove the external reference to the preallocated stack memory.
+ delete preallocated_message_space_;
+ preallocated_message_space_ = NULL;
+ PreallocatedMemoryThreadStop();
+
+ if (runtime_profiler_ != NULL) {
+ runtime_profiler_->TearDown();
+ delete runtime_profiler_;
+ runtime_profiler_ = NULL;
+ }
+ heap_.TearDown();
+ logger_->TearDown();
+
+ delete heap_profiler_;
+ heap_profiler_ = NULL;
+ delete cpu_profiler_;
+ cpu_profiler_ = NULL;
+
+ // The default isolate is re-initializable due to legacy API.
+ state_ = UNINITIALIZED;
+ }
+}
+
+
+void Isolate::PushToPartialSnapshotCache(Object* obj) {
+ int length = serialize_partial_snapshot_cache_length();
+ int capacity = serialize_partial_snapshot_cache_capacity();
+
+ if (length >= capacity) {
+ int new_capacity = static_cast<int>((capacity + 10) * 1.2);
+ Object** new_array = new Object*[new_capacity];
+ for (int i = 0; i < length; i++) {
+ new_array[i] = serialize_partial_snapshot_cache()[i];
+ }
+ if (capacity != 0) delete[] serialize_partial_snapshot_cache();
+ set_serialize_partial_snapshot_cache(new_array);
+ set_serialize_partial_snapshot_cache_capacity(new_capacity);
+ }
+
+ serialize_partial_snapshot_cache()[length] = obj;
+ set_serialize_partial_snapshot_cache_length(length + 1);
+}
+
+
+void Isolate::SetIsolateThreadLocals(Isolate* isolate,
+ PerIsolateThreadData* data) {
+ Thread::SetThreadLocal(isolate_key_, isolate);
+ Thread::SetThreadLocal(per_isolate_thread_data_key_, data);
+}
+
+
+Isolate::~Isolate() {
+ TRACE_ISOLATE(destructor);
+
+ // Has to be called while counters_ are still alive
+ runtime_zone_.DeleteKeptSegment();
+
+ // The entry stack must be empty when we get here,
+ // except for the default isolate, where it can
+ // still contain up to one entry stack item
+ ASSERT(entry_stack_ == NULL || this == default_isolate_);
+ ASSERT(entry_stack_ == NULL || entry_stack_->previous_item == NULL);
+
+ delete entry_stack_;
+ entry_stack_ = NULL;
+
+ delete[] assembler_spare_buffer_;
+ assembler_spare_buffer_ = NULL;
+
+ delete unicode_cache_;
+ unicode_cache_ = NULL;
+
+ delete date_cache_;
+ date_cache_ = NULL;
+
+ delete[] code_stub_interface_descriptors_;
+ code_stub_interface_descriptors_ = NULL;
+
+ delete regexp_stack_;
+ regexp_stack_ = NULL;
+
+ delete descriptor_lookup_cache_;
+ descriptor_lookup_cache_ = NULL;
+ delete context_slot_cache_;
+ context_slot_cache_ = NULL;
+ delete keyed_lookup_cache_;
+ keyed_lookup_cache_ = NULL;
+
+ delete transcendental_cache_;
+ transcendental_cache_ = NULL;
+ delete stub_cache_;
+ stub_cache_ = NULL;
+ delete stats_table_;
+ stats_table_ = NULL;
+
+ delete logger_;
+ logger_ = NULL;
+
+ delete counters_;
+ counters_ = NULL;
+
+ delete handle_scope_implementer_;
+ handle_scope_implementer_ = NULL;
+ delete break_access_;
+ break_access_ = NULL;
+ delete debugger_access_;
+ debugger_access_ = NULL;
+
+ delete compilation_cache_;
+ compilation_cache_ = NULL;
+ delete bootstrapper_;
+ bootstrapper_ = NULL;
+ delete inner_pointer_to_code_cache_;
+ inner_pointer_to_code_cache_ = NULL;
+ delete write_iterator_;
+ write_iterator_ = NULL;
+
+ delete context_switcher_;
+ context_switcher_ = NULL;
+ delete thread_manager_;
+ thread_manager_ = NULL;
+
+ delete string_tracker_;
+ string_tracker_ = NULL;
+
+ delete memory_allocator_;
+ memory_allocator_ = NULL;
+ delete code_range_;
+ code_range_ = NULL;
+ delete global_handles_;
+ global_handles_ = NULL;
+ delete eternal_handles_;
+ eternal_handles_ = NULL;
+
+ delete string_stream_debug_object_cache_;
+ string_stream_debug_object_cache_ = NULL;
+
+ delete external_reference_table_;
+ external_reference_table_ = NULL;
+
+ delete callback_table_;
+ callback_table_ = NULL;
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ delete debugger_;
+ debugger_ = NULL;
+ delete debug_;
+ debug_ = NULL;
+#endif
+}
+
+
+void Isolate::InitializeThreadLocal() {
+ thread_local_top_.isolate_ = this;
+ thread_local_top_.Initialize();
+}
+
+
+void Isolate::PropagatePendingExceptionToExternalTryCatch() {
+ ASSERT(has_pending_exception());
+
+ bool external_caught = IsExternallyCaught();
+ thread_local_top_.external_caught_exception_ = external_caught;
+
+ if (!external_caught) return;
+
+ if (thread_local_top_.pending_exception_->IsOutOfMemory()) {
+ // Do not propagate OOM exception: we should kill VM asap.
+ } else if (thread_local_top_.pending_exception_ ==
+ heap()->termination_exception()) {
+ try_catch_handler()->can_continue_ = false;
+ try_catch_handler()->has_terminated_ = true;
+ try_catch_handler()->exception_ = heap()->null_value();
+ } else {
+ v8::TryCatch* handler = try_catch_handler();
+ // At this point all non-object (failure) exceptions have
+ // been dealt with so this shouldn't fail.
+ ASSERT(!pending_exception()->IsFailure());
+ ASSERT(thread_local_top_.pending_message_obj_->IsJSMessageObject() ||
+ thread_local_top_.pending_message_obj_->IsTheHole());
+ ASSERT(thread_local_top_.pending_message_script_->IsScript() ||
+ thread_local_top_.pending_message_script_->IsTheHole());
+ handler->can_continue_ = true;
+ handler->has_terminated_ = false;
+ handler->exception_ = pending_exception();
+ // Propagate to the external try-catch only if we got an actual message.
+ if (thread_local_top_.pending_message_obj_->IsTheHole()) return;
+
+ handler->message_obj_ = thread_local_top_.pending_message_obj_;
+ handler->message_script_ = thread_local_top_.pending_message_script_;
+ handler->message_start_pos_ = thread_local_top_.pending_message_start_pos_;
+ handler->message_end_pos_ = thread_local_top_.pending_message_end_pos_;
+ }
+}
+
+
+void Isolate::InitializeLoggingAndCounters() {
+ if (logger_ == NULL) {
+ logger_ = new Logger(this);
+ }
+ if (counters_ == NULL) {
+ counters_ = new Counters(this);
+ }
+}
+
+
+void Isolate::InitializeDebugger() {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ ScopedLock lock(debugger_access_);
+ if (NoBarrier_Load(&debugger_initialized_)) return;
+ InitializeLoggingAndCounters();
+ debug_ = new Debug(this);
+ debugger_ = new Debugger(this);
+ Release_Store(&debugger_initialized_, true);
+#endif
+}
+
+
+bool Isolate::Init(Deserializer* des) {
+ ASSERT(state_ != INITIALIZED);
+ ASSERT(Isolate::Current() == this);
+ TRACE_ISOLATE(init);
+
+ stress_deopt_count_ = FLAG_deopt_every_n_times;
+
+ if (function_entry_hook() != NULL) {
+ // When function entry hooking is in effect, we have to create the code
+ // stubs from scratch to get entry hooks, rather than loading the previously
+ // generated stubs from disk.
+ // If this assert fires, the initialization path has regressed.
+ ASSERT(des == NULL);
+ }
+
+ // The initialization process does not handle memory exhaustion.
+ DisallowAllocationFailure disallow_allocation_failure;
+
+ InitializeLoggingAndCounters();
+
+ InitializeDebugger();
+
+ memory_allocator_ = new MemoryAllocator(this);
+ code_range_ = new CodeRange(this);
+
+ // Safe after setting Heap::isolate_, initializing StackGuard and
+ // ensuring that Isolate::Current() == this.
+ heap_.SetStackLimits();
+
+#define ASSIGN_ELEMENT(CamelName, hacker_name) \
+ isolate_addresses_[Isolate::k##CamelName##Address] = \
+ reinterpret_cast<Address>(hacker_name##_address());
+ FOR_EACH_ISOLATE_ADDRESS_NAME(ASSIGN_ELEMENT)
+#undef ASSIGN_ELEMENT
+
+ string_tracker_ = new StringTracker();
+ string_tracker_->isolate_ = this;
+ compilation_cache_ = new CompilationCache(this);
+ transcendental_cache_ = new TranscendentalCache();
+ keyed_lookup_cache_ = new KeyedLookupCache();
+ context_slot_cache_ = new ContextSlotCache();
+ descriptor_lookup_cache_ = new DescriptorLookupCache();
+ unicode_cache_ = new UnicodeCache();
+ inner_pointer_to_code_cache_ = new InnerPointerToCodeCache(this);
+ write_iterator_ = new ConsStringIteratorOp();
+ global_handles_ = new GlobalHandles(this);
+ eternal_handles_ = new EternalHandles();
+ bootstrapper_ = new Bootstrapper(this);
+ handle_scope_implementer_ = new HandleScopeImplementer(this);
+ stub_cache_ = new StubCache(this);
+ regexp_stack_ = new RegExpStack();
+ regexp_stack_->isolate_ = this;
+ date_cache_ = new DateCache();
+ code_stub_interface_descriptors_ =
+ new CodeStubInterfaceDescriptor[CodeStub::NUMBER_OF_IDS];
+ cpu_profiler_ = new CpuProfiler(this);
+ heap_profiler_ = new HeapProfiler(heap());
+
+ // Enable logging before setting up the heap
+ logger_->SetUp(this);
+
+ // Initialize other runtime facilities
+#if defined(USE_SIMULATOR)
+#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
+ Simulator::Initialize(this);
+#endif
+#endif
+
+ { // NOLINT
+ // Ensure that the thread has a valid stack guard. The v8::Locker object
+ // will ensure this too, but we don't have to use lockers if we are only
+ // using one thread.
+ ExecutionAccess lock(this);
+ stack_guard_.InitThread(lock);
+ }
+
+ // SetUp the object heap.
+ ASSERT(!heap_.HasBeenSetUp());
+ if (!heap_.SetUp()) {
+ V8::FatalProcessOutOfMemory("heap setup");
+ return false;
+ }
+
+ deoptimizer_data_ = new DeoptimizerData(memory_allocator_);
+
+ const bool create_heap_objects = (des == NULL);
+ if (create_heap_objects && !heap_.CreateHeapObjects()) {
+ V8::FatalProcessOutOfMemory("heap object creation");
+ return false;
+ }
+
+ if (create_heap_objects) {
+ // Terminate the cache array with the sentinel so we can iterate.
+ PushToPartialSnapshotCache(heap_.undefined_value());
+ }
+
+ InitializeThreadLocal();
+
+ bootstrapper_->Initialize(create_heap_objects);
+ builtins_.SetUp(create_heap_objects);
+
+ // Only preallocate on the first initialization.
+ if (FLAG_preallocate_message_memory && preallocated_message_space_ == NULL) {
+ // Start the thread which will set aside some memory.
+ PreallocatedMemoryThreadStart();
+ preallocated_message_space_ =
+ new NoAllocationStringAllocator(
+ preallocated_memory_thread_->data(),
+ preallocated_memory_thread_->length());
+ PreallocatedStorageInit(preallocated_memory_thread_->length() / 4);
+ }
+
+ if (FLAG_preemption) {
+ v8::Locker locker(reinterpret_cast<v8::Isolate*>(this));
+ v8::Locker::StartPreemption(100);
+ }
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ debug_->SetUp(create_heap_objects);
+#endif
+
+ // If we are deserializing, read the state into the now-empty heap.
+ if (!create_heap_objects) {
+ des->Deserialize();
+ }
+ stub_cache_->Initialize();
+
+ // 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();
+
+ // Quiet the heap NaN if needed on target platform.
+ if (!create_heap_objects) Assembler::QuietNaN(heap_.nan_value());
+
+ runtime_profiler_ = new RuntimeProfiler(this);
+ runtime_profiler_->SetUp();
+
+ // If we are deserializing, log non-function code objects and compiled
+ // functions found in the snapshot.
+ if (!create_heap_objects &&
+ (FLAG_log_code || FLAG_ll_prof || logger_->is_logging_code_events())) {
+ HandleScope scope(this);
+ LOG(this, LogCodeObjects());
+ LOG(this, LogCompiledFunctions());
+ }
+
+ CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, embedder_data_)),
+ Internals::kIsolateEmbedderDataOffset);
+ CHECK_EQ(static_cast<int>(OFFSET_OF(Isolate, heap_.roots_)),
+ Internals::kIsolateRootsOffset);
+
+ state_ = INITIALIZED;
+ time_millis_at_init_ = OS::TimeCurrentMillis();
+
+ if (!create_heap_objects) {
+ // Now that the heap is consistent, it's OK to generate the code for the
+ // deopt entry table that might have been referred to by optimized code in
+ // the snapshot.
+ HandleScope scope(this);
+ Deoptimizer::EnsureCodeForDeoptimizationEntry(
+ this,
+ Deoptimizer::LAZY,
+ kDeoptTableSerializeEntryCount - 1);
+ }
+
+ if (!Serializer::enabled()) {
+ // Ensure that all stubs which need to be generated ahead of time, but
+ // cannot be serialized into the snapshot have been generated.
+ HandleScope scope(this);
+ CodeStub::GenerateFPStubs(this);
+ StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(this);
+ StubFailureTrampolineStub::GenerateAheadOfTime(this);
+ // TODO(mstarzinger): The following is an ugly hack to make sure the
+ // interface descriptor is initialized even when stubs have been
+ // deserialized out of the snapshot without the graph builder.
+ FastCloneShallowArrayStub stub(FastCloneShallowArrayStub::CLONE_ELEMENTS,
+ DONT_TRACK_ALLOCATION_SITE, 0);
+ stub.InitializeInterfaceDescriptor(
+ this, code_stub_interface_descriptor(CodeStub::FastCloneShallowArray));
+ CompareNilICStub::InitializeForIsolate(this);
+ ToBooleanStub::InitializeForIsolate(this);
+ ArrayConstructorStubBase::InstallDescriptors(this);
+ InternalArrayConstructorStubBase::InstallDescriptors(this);
+ }
+
+ if (FLAG_parallel_recompilation) optimizing_compiler_thread_.Start();
+
+ if (FLAG_marking_threads > 0) {
+ marking_thread_ = new MarkingThread*[FLAG_marking_threads];
+ for (int i = 0; i < FLAG_marking_threads; i++) {
+ marking_thread_[i] = new MarkingThread(this);
+ marking_thread_[i]->Start();
+ }
+ }
+
+ if (FLAG_sweeper_threads > 0) {
+ sweeper_thread_ = new SweeperThread*[FLAG_sweeper_threads];
+ for (int i = 0; i < FLAG_sweeper_threads; i++) {
+ sweeper_thread_[i] = new SweeperThread(this);
+ sweeper_thread_[i]->Start();
+ }
+ }
+
+ initialized_from_snapshot_ = (des != NULL);
+
+ return true;
+}
+
+
+// Initialized lazily to allow early
+// v8::V8::SetAddHistogramSampleFunction calls.
+StatsTable* Isolate::stats_table() {
+ if (stats_table_ == NULL) {
+ stats_table_ = new StatsTable;
+ }
+ return stats_table_;
+}
+
+
+void Isolate::Enter() {
+ Isolate* current_isolate = NULL;
+ PerIsolateThreadData* current_data = CurrentPerIsolateThreadData();
+ if (current_data != NULL) {
+ current_isolate = current_data->isolate_;
+ ASSERT(current_isolate != NULL);
+ if (current_isolate == this) {
+ ASSERT(Current() == this);
+ ASSERT(entry_stack_ != NULL);
+ ASSERT(entry_stack_->previous_thread_data == NULL ||
+ entry_stack_->previous_thread_data->thread_id().Equals(
+ ThreadId::Current()));
+ // Same thread re-enters the isolate, no need to re-init anything.
+ entry_stack_->entry_count++;
+ return;
+ }
+ }
+
+ // Threads can have default isolate set into TLS as Current but not yet have
+ // PerIsolateThreadData for it, as it requires more advanced phase of the
+ // initialization. For example, a thread might be the one that system used for
+ // static initializers - in this case the default isolate is set in TLS but
+ // the thread did not yet Enter the isolate. If PerisolateThreadData is not
+ // there, use the isolate set in TLS.
+ if (current_isolate == NULL) {
+ current_isolate = Isolate::UncheckedCurrent();
+ }
+
+ PerIsolateThreadData* data = FindOrAllocatePerThreadDataForThisThread();
+ ASSERT(data != NULL);
+ ASSERT(data->isolate_ == this);
+
+ EntryStackItem* item = new EntryStackItem(current_data,
+ current_isolate,
+ entry_stack_);
+ entry_stack_ = item;
+
+ SetIsolateThreadLocals(this, data);
+
+ // In case it's the first time some thread enters the isolate.
+ set_thread_id(data->thread_id());
+}
+
+
+void Isolate::Exit() {
+ ASSERT(entry_stack_ != NULL);
+ ASSERT(entry_stack_->previous_thread_data == NULL ||
+ entry_stack_->previous_thread_data->thread_id().Equals(
+ ThreadId::Current()));
+
+ if (--entry_stack_->entry_count > 0) return;
+
+ ASSERT(CurrentPerIsolateThreadData() != NULL);
+ ASSERT(CurrentPerIsolateThreadData()->isolate_ == this);
+
+ // Pop the stack.
+ EntryStackItem* item = entry_stack_;
+ entry_stack_ = item->previous_item;
+
+ PerIsolateThreadData* previous_thread_data = item->previous_thread_data;
+ Isolate* previous_isolate = item->previous_isolate;
+
+ delete item;
+
+ // Reinit the current thread for the isolate it was running before this one.
+ SetIsolateThreadLocals(previous_isolate, previous_thread_data);
+}
+
+
+void Isolate::LinkDeferredHandles(DeferredHandles* deferred) {
+ deferred->next_ = deferred_handles_head_;
+ if (deferred_handles_head_ != NULL) {
+ deferred_handles_head_->previous_ = deferred;
+ }
+ deferred_handles_head_ = deferred;
+}
+
+
+void Isolate::UnlinkDeferredHandles(DeferredHandles* deferred) {
+#ifdef DEBUG
+ // In debug mode assert that the linked list is well-formed.
+ DeferredHandles* deferred_iterator = deferred;
+ while (deferred_iterator->previous_ != NULL) {
+ deferred_iterator = deferred_iterator->previous_;
+ }
+ ASSERT(deferred_handles_head_ == deferred_iterator);
+#endif
+ if (deferred_handles_head_ == deferred) {
+ deferred_handles_head_ = deferred_handles_head_->next_;
+ }
+ if (deferred->next_ != NULL) {
+ deferred->next_->previous_ = deferred->previous_;
+ }
+ if (deferred->previous_ != NULL) {
+ deferred->previous_->next_ = deferred->next_;
+ }
+}
+
+
+HStatistics* Isolate::GetHStatistics() {
+ if (hstatistics() == NULL) set_hstatistics(new HStatistics());
+ return hstatistics();
+}
+
+
+HTracer* Isolate::GetHTracer() {
+ if (htracer() == NULL) set_htracer(new HTracer(id()));
+ return htracer();
+}
+
+
+Map* Isolate::get_initial_js_array_map(ElementsKind kind) {
+ Context* native_context = context()->native_context();
+ Object* maybe_map_array = native_context->js_array_maps();
+ if (!maybe_map_array->IsUndefined()) {
+ Object* maybe_transitioned_map =
+ FixedArray::cast(maybe_map_array)->get(kind);
+ if (!maybe_transitioned_map->IsUndefined()) {
+ return Map::cast(maybe_transitioned_map);
+ }
+ }
+ return NULL;
+}
+
+
+bool Isolate::IsFastArrayConstructorPrototypeChainIntact() {
+ Map* root_array_map =
+ get_initial_js_array_map(GetInitialFastElementsKind());
+ ASSERT(root_array_map != NULL);
+ JSObject* initial_array_proto = JSObject::cast(*initial_array_prototype());
+
+ // Check that the array prototype hasn't been altered WRT empty elements.
+ if (root_array_map->prototype() != initial_array_proto) return false;
+ if (initial_array_proto->elements() != heap()->empty_fixed_array()) {
+ return false;
+ }
+
+ // Check that the object prototype hasn't been altered WRT empty elements.
+ JSObject* initial_object_proto = JSObject::cast(*initial_object_prototype());
+ Object* root_array_map_proto = initial_array_proto->GetPrototype();
+ if (root_array_map_proto != initial_object_proto) return false;
+ if (initial_object_proto->elements() != heap()->empty_fixed_array()) {
+ return false;
+ }
+
+ return initial_object_proto->GetPrototype()->IsNull();
+}
+
+
+CodeStubInterfaceDescriptor*
+ Isolate::code_stub_interface_descriptor(int index) {
+ return code_stub_interface_descriptors_ + index;
+}
+
+
+Object* Isolate::FindCodeObject(Address a) {
+ return inner_pointer_to_code_cache()->GcSafeFindCodeForInnerPointer(a);
+}
+
+
+#ifdef DEBUG
+#define ISOLATE_FIELD_OFFSET(type, name, ignored) \
+const intptr_t Isolate::name##_debug_offset_ = OFFSET_OF(Isolate, name##_);
+ISOLATE_INIT_LIST(ISOLATE_FIELD_OFFSET)
+ISOLATE_INIT_ARRAY_LIST(ISOLATE_FIELD_OFFSET)
+#undef ISOLATE_FIELD_OFFSET
+#endif
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/isolate.h b/chromium/v8/src/isolate.h
new file mode 100644
index 00000000000..401505afdda
--- /dev/null
+++ b/chromium/v8/src/isolate.h
@@ -0,0 +1,1525 @@
+// Copyright 2012 the V8 project authors. 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_ISOLATE_H_
+#define V8_ISOLATE_H_
+
+#include "../include/v8-debug.h"
+#include "allocation.h"
+#include "apiutils.h"
+#include "assert-scope.h"
+#include "atomicops.h"
+#include "builtins.h"
+#include "contexts.h"
+#include "execution.h"
+#include "frames.h"
+#include "date.h"
+#include "global-handles.h"
+#include "handles.h"
+#include "hashmap.h"
+#include "heap.h"
+#include "optimizing-compiler-thread.h"
+#include "regexp-stack.h"
+#include "runtime-profiler.h"
+#include "runtime.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+class Bootstrapper;
+class CallbackTable;
+class CodeGenerator;
+class CodeRange;
+struct CodeStubInterfaceDescriptor;
+class CompilationCache;
+class ContextSlotCache;
+class ContextSwitcher;
+class Counters;
+class CpuFeatures;
+class CpuProfiler;
+class DeoptimizerData;
+class Deserializer;
+class EmptyStatement;
+class ExternalCallbackScope;
+class ExternalReferenceTable;
+class Factory;
+class FunctionInfoListener;
+class HandleScopeImplementer;
+class HeapProfiler;
+class HStatistics;
+class HTracer;
+class InlineRuntimeFunctionsTable;
+class NoAllocationStringAllocator;
+class InnerPointerToCodeCache;
+class MarkingThread;
+class PreallocatedMemoryThread;
+class RegExpStack;
+class SaveContext;
+class UnicodeCache;
+class ConsStringIteratorOp;
+class StringTracker;
+class StubCache;
+class SweeperThread;
+class ThreadManager;
+class ThreadState;
+class ThreadVisitor; // Defined in v8threads.h
+template <StateTag Tag> class VMState;
+
+// 'void function pointer', used to roundtrip the
+// ExternalReference::ExternalReferenceRedirector since we can not include
+// assembler.h, where it is defined, here.
+typedef void* ExternalReferenceRedirectorPointer();
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+class Debug;
+class Debugger;
+class DebuggerAgent;
+#endif
+
+#if !defined(__arm__) && V8_TARGET_ARCH_ARM || \
+ !defined(__mips__) && V8_TARGET_ARCH_MIPS
+class Redirection;
+class Simulator;
+#endif
+
+
+// Static indirection table for handles to constants. If a frame
+// element represents a constant, the data contains an index into
+// this table of handles to the actual constants.
+// Static indirection table for handles to constants. If a Result
+// represents a constant, the data contains an index into this table
+// of handles to the actual constants.
+typedef ZoneList<Handle<Object> > ZoneObjectList;
+
+#define RETURN_IF_SCHEDULED_EXCEPTION(isolate) \
+ do { \
+ Isolate* __isolate__ = (isolate); \
+ if (__isolate__->has_scheduled_exception()) { \
+ return __isolate__->PromoteScheduledException(); \
+ } \
+ } while (false)
+
+#define RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, T) \
+ do { \
+ Isolate* __isolate__ = (isolate); \
+ if (__isolate__->has_scheduled_exception()) { \
+ __isolate__->PromoteScheduledException(); \
+ return Handle<T>::null(); \
+ } \
+ } while (false)
+
+#define RETURN_IF_EMPTY_HANDLE_VALUE(isolate, call, 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())
+
+#define FOR_EACH_ISOLATE_ADDRESS_NAME(C) \
+ C(Handler, handler) \
+ C(CEntryFP, c_entry_fp) \
+ C(Context, context) \
+ C(PendingException, pending_exception) \
+ C(ExternalCaughtException, external_caught_exception) \
+ C(JSEntrySP, js_entry_sp)
+
+
+// Platform-independent, reliable thread identifier.
+class ThreadId {
+ public:
+ // Creates an invalid ThreadId.
+ ThreadId() : id_(kInvalidId) {}
+
+ // Returns ThreadId for current thread.
+ static ThreadId Current() { return ThreadId(GetCurrentThreadId()); }
+
+ // Returns invalid ThreadId (guaranteed not to be equal to any thread).
+ static ThreadId Invalid() { return ThreadId(kInvalidId); }
+
+ // Compares ThreadIds for equality.
+ INLINE(bool Equals(const ThreadId& other) const) {
+ return id_ == other.id_;
+ }
+
+ // Checks whether this ThreadId refers to any thread.
+ INLINE(bool IsValid() const) {
+ return id_ != kInvalidId;
+ }
+
+ // Converts ThreadId to an integer representation
+ // (required for public API: V8::V8::GetCurrentThreadId).
+ int ToInteger() const { return id_; }
+
+ // Converts ThreadId to an integer representation
+ // (required for public API: V8::V8::TerminateExecution).
+ static ThreadId FromInteger(int id) { return ThreadId(id); }
+
+ private:
+ static const int kInvalidId = -1;
+
+ explicit ThreadId(int id) : id_(id) {}
+
+ static int AllocateThreadId();
+
+ static int GetCurrentThreadId();
+
+ int id_;
+
+ static Atomic32 highest_thread_id_;
+
+ friend class Isolate;
+};
+
+
+class ThreadLocalTop BASE_EMBEDDED {
+ public:
+ // Does early low-level initialization that does not depend on the
+ // isolate being present.
+ ThreadLocalTop();
+
+ // Initialize the thread data.
+ void Initialize();
+
+ // Get the top C++ try catch handler or NULL if none are registered.
+ //
+ // This method is not guarenteed to return an address that can be
+ // used for comparison with addresses into the JS stack. If such an
+ // address is needed, use try_catch_handler_address.
+ v8::TryCatch* TryCatchHandler();
+
+ // Get the address of the top C++ try catch handler or NULL if
+ // none are registered.
+ //
+ // This method always returns an address that can be compared to
+ // pointers into the JavaScript stack. When running on actual
+ // hardware, try_catch_handler_address and TryCatchHandler return
+ // the same pointer. When running on a simulator with a separate JS
+ // stack, try_catch_handler_address returns a JS stack address that
+ // corresponds to the place on the JS stack where the C++ handler
+ // would have been if the stack were not separate.
+ inline Address try_catch_handler_address() {
+ return try_catch_handler_address_;
+ }
+
+ // Set the address of the top C++ try catch handler.
+ inline void set_try_catch_handler_address(Address address) {
+ try_catch_handler_address_ = address;
+ }
+
+ void Free() {
+ ASSERT(!has_pending_message_);
+ ASSERT(!external_caught_exception_);
+ ASSERT(try_catch_handler_address_ == NULL);
+ }
+
+ Isolate* isolate_;
+ // The context where the current execution method is created and for variable
+ // lookups.
+ Context* context_;
+ ThreadId thread_id_;
+ MaybeObject* pending_exception_;
+ bool has_pending_message_;
+ bool rethrowing_message_;
+ Object* pending_message_obj_;
+ Object* pending_message_script_;
+ int pending_message_start_pos_;
+ int pending_message_end_pos_;
+ // Use a separate value for scheduled exceptions to preserve the
+ // invariants that hold about pending_exception. We may want to
+ // unify them later.
+ MaybeObject* scheduled_exception_;
+ bool external_caught_exception_;
+ SaveContext* save_context_;
+ v8::TryCatch* catcher_;
+
+ // Stack.
+ Address c_entry_fp_; // the frame pointer of the top c entry frame
+ Address handler_; // try-blocks are chained through the stack
+
+#ifdef USE_SIMULATOR
+#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
+ Simulator* simulator_;
+#endif
+#endif // USE_SIMULATOR
+
+ Address js_entry_sp_; // the stack pointer of the bottom JS entry frame
+ // the external callback we're currently in
+ ExternalCallbackScope* external_callback_scope_;
+ StateTag current_vm_state_;
+
+ // Generated code scratch locations.
+ int32_t formal_count_;
+
+ // 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_;
+
+ private:
+ void InitializeInternal();
+
+ Address try_catch_handler_address_;
+};
+
+
+class SystemThreadManager {
+ public:
+ enum ParallelSystemComponent {
+ PARALLEL_SWEEPING,
+ CONCURRENT_SWEEPING,
+ PARALLEL_MARKING,
+ PARALLEL_RECOMPILATION
+ };
+
+ static int NumberOfParallelSystemThreads(ParallelSystemComponent type);
+
+ static const int kMaxThreads = 4;
+};
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+#define ISOLATE_DEBUGGER_INIT_LIST(V) \
+ V(v8::Debug::EventCallback, debug_event_callback, NULL) \
+ V(DebuggerAgent*, debugger_agent_instance, NULL)
+#else
+
+#define ISOLATE_DEBUGGER_INIT_LIST(V)
+
+#endif
+
+#ifdef DEBUG
+
+#define ISOLATE_INIT_DEBUG_ARRAY_LIST(V) \
+ V(CommentStatistic, paged_space_comments_statistics, \
+ CommentStatistic::kMaxComments + 1)
+#else
+
+#define ISOLATE_INIT_DEBUG_ARRAY_LIST(V)
+
+#endif
+
+#define ISOLATE_INIT_ARRAY_LIST(V) \
+ /* SerializerDeserializer state. */ \
+ V(int32_t, jsregexp_static_offsets_vector, kJSRegexpStaticOffsetsVectorSize) \
+ V(int, bad_char_shift_table, kUC16AlphabetSize) \
+ V(int, good_suffix_shift_table, (kBMMaxShift + 1)) \
+ V(int, suffix_table, (kBMMaxShift + 1)) \
+ V(uint32_t, private_random_seed, 2) \
+ ISOLATE_INIT_DEBUG_ARRAY_LIST(V)
+
+typedef List<HeapObject*, PreallocatedStorageAllocationPolicy> DebugObjectCache;
+
+#define ISOLATE_INIT_LIST(V) \
+ /* SerializerDeserializer state. */ \
+ V(int, serialize_partial_snapshot_cache_length, 0) \
+ V(int, serialize_partial_snapshot_cache_capacity, 0) \
+ V(Object**, serialize_partial_snapshot_cache, NULL) \
+ /* Assembler state. */ \
+ /* A previously allocated buffer of kMinimalBufferSize bytes, or NULL. */ \
+ V(byte*, assembler_spare_buffer, NULL) \
+ V(FatalErrorCallback, exception_behavior, NULL) \
+ V(AllowCodeGenerationFromStringsCallback, allow_code_gen_callback, NULL) \
+ V(v8::Debug::MessageHandler, message_handler, NULL) \
+ /* To distinguish the function templates, so that we can find them in the */ \
+ /* function cache of the native context. */ \
+ V(int, next_serial_number, 0) \
+ V(ExternalReferenceRedirectorPointer*, external_reference_redirector, NULL) \
+ V(bool, always_allow_natives_syntax, false) \
+ /* Part of the state of liveedit. */ \
+ V(FunctionInfoListener*, active_function_info_listener, NULL) \
+ /* State for Relocatable. */ \
+ V(Relocatable*, relocatable_top, NULL) \
+ V(DebugObjectCache*, string_stream_debug_object_cache, NULL) \
+ V(Object*, string_stream_current_security_token, NULL) \
+ /* TODO(isolates): Release this on destruction? */ \
+ V(int*, irregexp_interpreter_backtrack_stack_cache, NULL) \
+ /* Serializer state. */ \
+ V(ExternalReferenceTable*, external_reference_table, NULL) \
+ /* AstNode state. */ \
+ V(int, ast_node_id, 0) \
+ V(unsigned, ast_node_count, 0) \
+ V(bool, observer_delivery_pending, false) \
+ V(HStatistics*, hstatistics, NULL) \
+ V(HTracer*, htracer, NULL) \
+ ISOLATE_DEBUGGER_INIT_LIST(V)
+
+class Isolate {
+ // These forward declarations are required to make the friend declarations in
+ // PerIsolateThreadData work on some older versions of gcc.
+ class ThreadDataTable;
+ class EntryStackItem;
+ public:
+ ~Isolate();
+
+ // A thread has a PerIsolateThreadData instance for each isolate that it has
+ // entered. That instance is allocated when the isolate is initially entered
+ // and reused on subsequent entries.
+ class PerIsolateThreadData {
+ public:
+ PerIsolateThreadData(Isolate* isolate, ThreadId thread_id)
+ : isolate_(isolate),
+ thread_id_(thread_id),
+ stack_limit_(0),
+ thread_state_(NULL),
+#if !defined(__arm__) && V8_TARGET_ARCH_ARM || \
+ !defined(__mips__) && V8_TARGET_ARCH_MIPS
+ simulator_(NULL),
+#endif
+ next_(NULL),
+ prev_(NULL) { }
+ Isolate* isolate() const { return isolate_; }
+ ThreadId thread_id() const { return thread_id_; }
+ void set_stack_limit(uintptr_t value) { stack_limit_ = value; }
+ uintptr_t stack_limit() const { return stack_limit_; }
+ ThreadState* thread_state() const { return thread_state_; }
+ void set_thread_state(ThreadState* value) { thread_state_ = value; }
+
+#if !defined(__arm__) && V8_TARGET_ARCH_ARM || \
+ !defined(__mips__) && V8_TARGET_ARCH_MIPS
+ Simulator* simulator() const { return simulator_; }
+ void set_simulator(Simulator* simulator) {
+ simulator_ = simulator;
+ }
+#endif
+
+ bool Matches(Isolate* isolate, ThreadId thread_id) const {
+ return isolate_ == isolate && thread_id_.Equals(thread_id);
+ }
+
+ private:
+ Isolate* isolate_;
+ ThreadId thread_id_;
+ uintptr_t stack_limit_;
+ ThreadState* thread_state_;
+
+#if !defined(__arm__) && V8_TARGET_ARCH_ARM || \
+ !defined(__mips__) && V8_TARGET_ARCH_MIPS
+ Simulator* simulator_;
+#endif
+
+ PerIsolateThreadData* next_;
+ PerIsolateThreadData* prev_;
+
+ friend class Isolate;
+ friend class ThreadDataTable;
+ friend class EntryStackItem;
+
+ DISALLOW_COPY_AND_ASSIGN(PerIsolateThreadData);
+ };
+
+
+ enum AddressId {
+#define DECLARE_ENUM(CamelName, hacker_name) k##CamelName##Address,
+ FOR_EACH_ISOLATE_ADDRESS_NAME(DECLARE_ENUM)
+#undef DECLARE_ENUM
+ kIsolateAddressCount
+ };
+
+ // Returns the PerIsolateThreadData for the current thread (or NULL if one is
+ // not currently set).
+ static PerIsolateThreadData* CurrentPerIsolateThreadData() {
+ return reinterpret_cast<PerIsolateThreadData*>(
+ Thread::GetThreadLocal(per_isolate_thread_data_key_));
+ }
+
+ // Returns the isolate inside which the current thread is running.
+ INLINE(static Isolate* Current()) {
+ Isolate* isolate = reinterpret_cast<Isolate*>(
+ Thread::GetExistingThreadLocal(isolate_key_));
+ ASSERT(isolate != NULL);
+ return isolate;
+ }
+
+ INLINE(static Isolate* UncheckedCurrent()) {
+ return reinterpret_cast<Isolate*>(Thread::GetThreadLocal(isolate_key_));
+ }
+
+ // Usually called by Init(), but can be called early e.g. to allow
+ // testing components that require logging but not the whole
+ // isolate.
+ //
+ // Safe to call more than once.
+ void InitializeLoggingAndCounters();
+
+ bool Init(Deserializer* des);
+
+ bool IsInitialized() { return state_ == INITIALIZED; }
+
+ // True if at least one thread Enter'ed this isolate.
+ bool IsInUse() { return entry_stack_ != NULL; }
+
+ // Destroys the non-default isolates.
+ // Sets default isolate into "has_been_disposed" state rather then destroying,
+ // for legacy API reasons.
+ void TearDown();
+
+ static void GlobalTearDown();
+
+ 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 cases, for
+ // example if you are using V8 from within the body of a static initializer.
+ // Safe to call multiple times.
+ static void EnsureDefaultIsolate();
+
+ // Find the PerThread for this particular (isolate, thread) combination
+ // If one does not yet exist, return null.
+ PerIsolateThreadData* FindPerThreadDataForThisThread();
+
+ // Find the PerThread for given (isolate, thread) combination
+ // If one does not yet exist, return null.
+ PerIsolateThreadData* FindPerThreadDataForThread(ThreadId thread_id);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Get the debugger from the default isolate. Preinitializes the
+ // default isolate if needed.
+ static Debugger* GetDefaultIsolateDebugger();
+#endif
+
+ // Get the stack guard from the default isolate. Preinitializes the
+ // default isolate if needed.
+ static StackGuard* GetDefaultIsolateStackGuard();
+
+ // Returns the key used to store the pointer to the current isolate.
+ // Used internally for V8 threads that do not execute JavaScript but still
+ // are part of the domain of an isolate (like the context switcher).
+ static Thread::LocalStorageKey isolate_key() {
+ return isolate_key_;
+ }
+
+ // Returns the key used to store process-wide thread IDs.
+ static Thread::LocalStorageKey thread_id_key() {
+ return thread_id_key_;
+ }
+
+ static Thread::LocalStorageKey per_isolate_thread_data_key();
+
+ // If a client attempts to create a Locker without specifying an isolate,
+ // we assume that the client is using legacy behavior. Set up the current
+ // thread to be inside the implicit isolate (or fail a check if we have
+ // switched to non-legacy behavior).
+ static void EnterDefaultIsolate();
+
+ // Mutex for serializing access to break control structures.
+ Mutex* break_access() { return break_access_; }
+
+ // Mutex for serializing access to debugger.
+ Mutex* debugger_access() { return debugger_access_; }
+
+ Address get_address_from_id(AddressId id);
+
+ // Access to top context (where the current function object was created).
+ Context* context() { return thread_local_top_.context_; }
+ void set_context(Context* context) {
+ ASSERT(context == NULL || context->IsContext());
+ thread_local_top_.context_ = context;
+ }
+ Context** context_address() { return &thread_local_top_.context_; }
+
+ SaveContext* save_context() { return thread_local_top_.save_context_; }
+ void set_save_context(SaveContext* save) {
+ thread_local_top_.save_context_ = save;
+ }
+
+ // Access to current thread id.
+ ThreadId thread_id() { return thread_local_top_.thread_id_; }
+ void set_thread_id(ThreadId id) { thread_local_top_.thread_id_ = id; }
+
+ // Interface to pending exception.
+ MaybeObject* pending_exception() {
+ ASSERT(has_pending_exception());
+ return thread_local_top_.pending_exception_;
+ }
+ bool external_caught_exception() {
+ return thread_local_top_.external_caught_exception_;
+ }
+ void set_external_caught_exception(bool value) {
+ thread_local_top_.external_caught_exception_ = value;
+ }
+ void set_pending_exception(MaybeObject* exception) {
+ thread_local_top_.pending_exception_ = exception;
+ }
+ void clear_pending_exception() {
+ thread_local_top_.pending_exception_ = heap_.the_hole_value();
+ }
+ MaybeObject** pending_exception_address() {
+ return &thread_local_top_.pending_exception_;
+ }
+ bool has_pending_exception() {
+ return !thread_local_top_.pending_exception_->IsTheHole();
+ }
+ void clear_pending_message() {
+ thread_local_top_.has_pending_message_ = false;
+ thread_local_top_.pending_message_obj_ = heap_.the_hole_value();
+ thread_local_top_.pending_message_script_ = heap_.the_hole_value();
+ }
+ v8::TryCatch* try_catch_handler() {
+ return thread_local_top_.TryCatchHandler();
+ }
+ Address try_catch_handler_address() {
+ return thread_local_top_.try_catch_handler_address();
+ }
+ bool* external_caught_exception_address() {
+ return &thread_local_top_.external_caught_exception_;
+ }
+ v8::TryCatch* catcher() {
+ return thread_local_top_.catcher_;
+ }
+ void set_catcher(v8::TryCatch* catcher) {
+ thread_local_top_.catcher_ = catcher;
+ }
+
+ MaybeObject** scheduled_exception_address() {
+ return &thread_local_top_.scheduled_exception_;
+ }
+
+ Address pending_message_obj_address() {
+ return reinterpret_cast<Address>(&thread_local_top_.pending_message_obj_);
+ }
+
+ Address has_pending_message_address() {
+ return reinterpret_cast<Address>(&thread_local_top_.has_pending_message_);
+ }
+
+ Address pending_message_script_address() {
+ return reinterpret_cast<Address>(
+ &thread_local_top_.pending_message_script_);
+ }
+
+ MaybeObject* scheduled_exception() {
+ ASSERT(has_scheduled_exception());
+ return thread_local_top_.scheduled_exception_;
+ }
+ bool has_scheduled_exception() {
+ return thread_local_top_.scheduled_exception_ != heap_.the_hole_value();
+ }
+ void clear_scheduled_exception() {
+ thread_local_top_.scheduled_exception_ = heap_.the_hole_value();
+ }
+
+ bool IsExternallyCaught();
+
+ bool is_catchable_by_javascript(MaybeObject* exception) {
+ return (!exception->IsOutOfMemory()) &&
+ (exception != heap()->termination_exception());
+ }
+
+ // Serializer.
+ void PushToPartialSnapshotCache(Object* obj);
+
+ // JS execution stack (see frames.h).
+ static Address c_entry_fp(ThreadLocalTop* thread) {
+ return thread->c_entry_fp_;
+ }
+ static Address handler(ThreadLocalTop* thread) { return thread->handler_; }
+
+ inline Address* c_entry_fp_address() {
+ return &thread_local_top_.c_entry_fp_;
+ }
+ inline Address* handler_address() { return &thread_local_top_.handler_; }
+
+ // Bottom JS entry.
+ Address js_entry_sp() {
+ return thread_local_top_.js_entry_sp_;
+ }
+ inline Address* js_entry_sp_address() {
+ return &thread_local_top_.js_entry_sp_;
+ }
+
+ // Generated code scratch locations.
+ 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.
+ Handle<GlobalObject> global_object() {
+ return Handle<GlobalObject>(context()->global_object());
+ }
+
+ // Returns the global proxy object of the current context.
+ Object* global_proxy() {
+ return context()->global_proxy();
+ }
+
+ Handle<JSBuiltinsObject> js_builtins_object() {
+ return Handle<JSBuiltinsObject>(thread_local_top_.context_->builtins());
+ }
+
+ static int ArchiveSpacePerThread() { return sizeof(ThreadLocalTop); }
+ void FreeThreadResources() { thread_local_top_.Free(); }
+
+ // This method is called by the api after operations that may throw
+ // exceptions. If an exception was thrown and not handled by an external
+ // handler the exception is scheduled to be rethrown when we return to running
+ // JavaScript code. If an exception is scheduled true is returned.
+ bool OptionalRescheduleException(bool is_bottom_call);
+
+ class ExceptionScope {
+ public:
+ explicit ExceptionScope(Isolate* isolate) :
+ // Scope currently can only be used for regular exceptions, not
+ // failures like OOM or termination exception.
+ isolate_(isolate),
+ pending_exception_(isolate_->pending_exception()->ToObjectUnchecked(),
+ isolate_),
+ catcher_(isolate_->catcher())
+ { }
+
+ ~ExceptionScope() {
+ isolate_->set_catcher(catcher_);
+ isolate_->set_pending_exception(*pending_exception_);
+ }
+
+ private:
+ Isolate* isolate_;
+ Handle<Object> pending_exception_;
+ v8::TryCatch* catcher_;
+ };
+
+ void SetCaptureStackTraceForUncaughtExceptions(
+ bool capture,
+ int frame_limit,
+ StackTrace::StackTraceOptions options);
+
+ // Tells whether the current context has experienced an out of memory
+ // exception.
+ bool is_out_of_memory();
+ bool ignore_out_of_memory() {
+ return thread_local_top_.ignore_out_of_memory_;
+ }
+ void set_ignore_out_of_memory(bool value) {
+ thread_local_top_.ignore_out_of_memory_ = value;
+ }
+
+ void PrintCurrentStackTrace(FILE* out);
+ void PrintStackTrace(FILE* out, char* thread_data);
+ void PrintStack(StringStream* accumulator);
+ void PrintStack(FILE* out);
+ void PrintStack();
+ Handle<String> StackTraceString();
+ NO_INLINE(void PushStackTraceAndDie(unsigned int magic,
+ Object* object,
+ Map* map,
+ unsigned int magic2));
+ Handle<JSArray> CaptureCurrentStackTrace(
+ int frame_limit,
+ StackTrace::StackTraceOptions options);
+
+ Handle<JSArray> CaptureSimpleStackTrace(Handle<JSObject> error_object,
+ Handle<Object> caller,
+ int limit);
+ void CaptureAndSetDetailedStackTrace(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.
+ bool MayNamedAccess(JSObject* receiver,
+ Object* key,
+ v8::AccessType type);
+ bool MayIndexedAccess(JSObject* receiver,
+ uint32_t index,
+ v8::AccessType type);
+
+ void SetFailedAccessCheckCallback(v8::FailedAccessCheckCallback callback);
+ void ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type);
+
+ // Exception throwing support. The caller should use the result
+ // of Throw() as its return value.
+ Failure* Throw(Object* exception, MessageLocation* location = NULL);
+ // Re-throw an exception. This involves no error reporting since
+ // error reporting was handled when the exception was thrown
+ // originally.
+ Failure* ReThrow(MaybeObject* exception);
+ void ScheduleThrow(Object* exception);
+ // Re-set pending message, script and positions reported to the TryCatch
+ // back to the TLS for re-use when rethrowing.
+ void RestorePendingMessageFromTryCatch(v8::TryCatch* handler);
+ void ReportPendingMessages();
+ // Return pending location if any or unfilled structure.
+ MessageLocation GetMessageLocation();
+ Failure* ThrowIllegalOperation();
+
+ // Promote a scheduled exception to pending. Asserts has_scheduled_exception.
+ Failure* PromoteScheduledException();
+ 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,
+ bool catchable_by_javascript);
+
+ // Attempts to compute the current source location, storing the
+ // result in the target out parameter.
+ void ComputeLocation(MessageLocation* target);
+
+ // Override command line flag.
+ void TraceException(bool flag);
+
+ // Out of resource exception helpers.
+ Failure* StackOverflow();
+ Failure* TerminateExecution();
+ void CancelTerminateExecution();
+
+ // Administration
+ void Iterate(ObjectVisitor* v);
+ void Iterate(ObjectVisitor* v, ThreadLocalTop* t);
+ char* Iterate(ObjectVisitor* v, char* t);
+ void IterateThread(ThreadVisitor* v, char* t);
+
+
+ // Returns the current native and global context.
+ Handle<Context> native_context();
+ Handle<Context> global_context();
+
+ // Returns the native context of the calling JavaScript code. That
+ // is, the native context of the top-most JavaScript frame.
+ Handle<Context> GetCallingNativeContext();
+
+ void RegisterTryCatchHandler(v8::TryCatch* that);
+ void UnregisterTryCatchHandler(v8::TryCatch* that);
+
+ char* ArchiveThread(char* to);
+ char* RestoreThread(char* from);
+
+ static const char* const kStackOverflowMessage;
+
+ static const int kUC16AlphabetSize = 256; // See StringSearchBase.
+ static const int kBMMaxShift = 250; // See StringSearchBase.
+
+ // Accessors.
+#define GLOBAL_ACCESSOR(type, name, initialvalue) \
+ inline type name() const { \
+ ASSERT(OFFSET_OF(Isolate, name##_) == name##_debug_offset_); \
+ return name##_; \
+ } \
+ inline void set_##name(type value) { \
+ ASSERT(OFFSET_OF(Isolate, name##_) == name##_debug_offset_); \
+ name##_ = value; \
+ }
+ ISOLATE_INIT_LIST(GLOBAL_ACCESSOR)
+#undef GLOBAL_ACCESSOR
+
+#define GLOBAL_ARRAY_ACCESSOR(type, name, length) \
+ inline type* name() { \
+ ASSERT(OFFSET_OF(Isolate, name##_) == name##_debug_offset_); \
+ return &(name##_)[0]; \
+ }
+ ISOLATE_INIT_ARRAY_LIST(GLOBAL_ARRAY_ACCESSOR)
+#undef GLOBAL_ARRAY_ACCESSOR
+
+#define NATIVE_CONTEXT_FIELD_ACCESSOR(index, type, name) \
+ Handle<type> name() { \
+ return Handle<type>(context()->native_context()->name(), this); \
+ } \
+ bool is_##name(type* value) { \
+ return context()->native_context()->is_##name(value); \
+ }
+ NATIVE_CONTEXT_FIELDS(NATIVE_CONTEXT_FIELD_ACCESSOR)
+#undef NATIVE_CONTEXT_FIELD_ACCESSOR
+
+ Bootstrapper* bootstrapper() { return bootstrapper_; }
+ Counters* counters() {
+ // Call InitializeLoggingAndCounters() if logging is needed before
+ // the isolate is fully initialized.
+ ASSERT(counters_ != NULL);
+ return counters_;
+ }
+ CodeRange* code_range() { return code_range_; }
+ RuntimeProfiler* runtime_profiler() { return runtime_profiler_; }
+ CompilationCache* compilation_cache() { return compilation_cache_; }
+ Logger* logger() {
+ // Call InitializeLoggingAndCounters() if logging is needed before
+ // the isolate is fully initialized.
+ ASSERT(logger_ != NULL);
+ return logger_;
+ }
+ StackGuard* stack_guard() { return &stack_guard_; }
+ Heap* heap() { return &heap_; }
+ StatsTable* stats_table();
+ StubCache* stub_cache() { return stub_cache_; }
+ DeoptimizerData* deoptimizer_data() { return deoptimizer_data_; }
+ ThreadLocalTop* thread_local_top() { return &thread_local_top_; }
+
+ TranscendentalCache* transcendental_cache() const {
+ return transcendental_cache_;
+ }
+
+ MemoryAllocator* memory_allocator() {
+ return memory_allocator_;
+ }
+
+ KeyedLookupCache* keyed_lookup_cache() {
+ return keyed_lookup_cache_;
+ }
+
+ ContextSlotCache* context_slot_cache() {
+ return context_slot_cache_;
+ }
+
+ DescriptorLookupCache* descriptor_lookup_cache() {
+ return descriptor_lookup_cache_;
+ }
+
+ v8::ImplementationUtilities::HandleScopeData* handle_scope_data() {
+ return &handle_scope_data_;
+ }
+ HandleScopeImplementer* handle_scope_implementer() {
+ ASSERT(handle_scope_implementer_);
+ return handle_scope_implementer_;
+ }
+ Zone* runtime_zone() { return &runtime_zone_; }
+
+ UnicodeCache* unicode_cache() {
+ return unicode_cache_;
+ }
+
+ InnerPointerToCodeCache* inner_pointer_to_code_cache() {
+ return inner_pointer_to_code_cache_;
+ }
+
+ ConsStringIteratorOp* write_iterator() { return write_iterator_; }
+
+ GlobalHandles* global_handles() { return global_handles_; }
+
+ EternalHandles* eternal_handles() { return eternal_handles_; }
+
+ ThreadManager* thread_manager() { return thread_manager_; }
+
+ ContextSwitcher* context_switcher() { return context_switcher_; }
+
+ void set_context_switcher(ContextSwitcher* switcher) {
+ context_switcher_ = switcher;
+ }
+
+ StringTracker* string_tracker() { return string_tracker_; }
+
+ unibrow::Mapping<unibrow::Ecma262UnCanonicalize>* jsregexp_uncanonicalize() {
+ return &jsregexp_uncanonicalize_;
+ }
+
+ unibrow::Mapping<unibrow::CanonicalizationRange>* jsregexp_canonrange() {
+ return &jsregexp_canonrange_;
+ }
+
+ ConsStringIteratorOp* objects_string_compare_iterator_a() {
+ return &objects_string_compare_iterator_a_;
+ }
+
+ ConsStringIteratorOp* objects_string_compare_iterator_b() {
+ return &objects_string_compare_iterator_b_;
+ }
+
+ StaticResource<ConsStringIteratorOp>* objects_string_iterator() {
+ return &objects_string_iterator_;
+ }
+
+ 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_; }
+
+ 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_;
+ }
+
+ RegExpStack* regexp_stack() { return regexp_stack_; }
+
+ unibrow::Mapping<unibrow::Ecma262Canonicalize>*
+ interp_canonicalize_mapping() {
+ return &interp_canonicalize_mapping_;
+ }
+
+ void* PreallocatedStorageNew(size_t size);
+ void PreallocatedStorageDelete(void* p);
+ void PreallocatedStorageInit(size_t size);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Debugger* debugger() {
+ if (!NoBarrier_Load(&debugger_initialized_)) InitializeDebugger();
+ return debugger_;
+ }
+ Debug* debug() {
+ if (!NoBarrier_Load(&debugger_initialized_)) InitializeDebugger();
+ return debug_;
+ }
+#endif
+
+ inline bool IsDebuggerActive();
+ inline bool DebuggerHasBreakPoints();
+
+ CpuProfiler* cpu_profiler() const { return cpu_profiler_; }
+ HeapProfiler* heap_profiler() const { return heap_profiler_; }
+
+#ifdef DEBUG
+ HistogramInfo* heap_histograms() { return heap_histograms_; }
+
+ JSObject::SpillInformation* js_spill_information() {
+ return &js_spill_information_;
+ }
+
+ int* code_kind_statistics() { return code_kind_statistics_; }
+#endif
+
+#if V8_TARGET_ARCH_ARM && !defined(__arm__) || \
+ V8_TARGET_ARCH_MIPS && !defined(__mips__)
+ bool simulator_initialized() { return simulator_initialized_; }
+ void set_simulator_initialized(bool initialized) {
+ simulator_initialized_ = initialized;
+ }
+
+ HashMap* simulator_i_cache() { return simulator_i_cache_; }
+ void set_simulator_i_cache(HashMap* hash_map) {
+ simulator_i_cache_ = hash_map;
+ }
+
+ Redirection* simulator_redirection() {
+ return simulator_redirection_;
+ }
+ void set_simulator_redirection(Redirection* redirection) {
+ simulator_redirection_ = redirection;
+ }
+#endif
+
+ Factory* factory() { return reinterpret_cast<Factory*>(this); }
+
+ static const int kJSRegexpStaticOffsetsVectorSize = 128;
+
+ ExternalCallbackScope* external_callback_scope() {
+ return thread_local_top_.external_callback_scope_;
+ }
+ void set_external_callback_scope(ExternalCallbackScope* scope) {
+ thread_local_top_.external_callback_scope_ = scope;
+ }
+
+ StateTag current_vm_state() {
+ return thread_local_top_.current_vm_state_;
+ }
+
+ void set_current_vm_state(StateTag state) {
+ thread_local_top_.current_vm_state_ = state;
+ }
+
+ 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 initialized_from_snapshot() { return initialized_from_snapshot_; }
+
+ double time_millis_since_init() {
+ return OS::TimeCurrentMillis() - time_millis_at_init_;
+ }
+
+ DateCache* date_cache() {
+ return date_cache_;
+ }
+
+ void set_date_cache(DateCache* date_cache) {
+ if (date_cache != date_cache_) {
+ delete date_cache_;
+ }
+ date_cache_ = date_cache;
+ }
+
+ Map* get_initial_js_array_map(ElementsKind kind);
+
+ bool IsFastArrayConstructorPrototypeChainIntact();
+
+ CodeStubInterfaceDescriptor*
+ code_stub_interface_descriptor(int index);
+
+ void IterateDeferredHandles(ObjectVisitor* visitor);
+ void LinkDeferredHandles(DeferredHandles* deferred_handles);
+ void UnlinkDeferredHandles(DeferredHandles* deferred_handles);
+
+#ifdef DEBUG
+ bool IsDeferredHandle(Object** location);
+#endif // DEBUG
+
+ OptimizingCompilerThread* optimizing_compiler_thread() {
+ return &optimizing_compiler_thread_;
+ }
+
+ // PreInits and returns a default isolate. Needed when a new thread tries
+ // to create a Locker for the first time (the lock itself is in the isolate).
+ // TODO(svenpanne) This method is on death row...
+ static v8::Isolate* GetDefaultIsolateForLocking();
+
+ MarkingThread** marking_threads() {
+ return marking_thread_;
+ }
+
+ SweeperThread** sweeper_threads() {
+ return sweeper_thread_;
+ }
+
+ CallbackTable* callback_table() {
+ return callback_table_;
+ }
+ void set_callback_table(CallbackTable* callback_table) {
+ callback_table_ = callback_table;
+ }
+
+ int id() const { return static_cast<int>(id_); }
+
+ HStatistics* GetHStatistics();
+ HTracer* GetHTracer();
+
+ FunctionEntryHook function_entry_hook() { return function_entry_hook_; }
+ void set_function_entry_hook(FunctionEntryHook function_entry_hook) {
+ function_entry_hook_ = function_entry_hook;
+ }
+
+ void* stress_deopt_count_address() { return &stress_deopt_count_; }
+
+ // Given an address occupied by a live code object, return that object.
+ Object* FindCodeObject(Address a);
+
+ private:
+ Isolate();
+
+ friend struct GlobalState;
+ friend struct InitializeGlobalState;
+
+ enum State {
+ UNINITIALIZED, // Some components may not have been allocated.
+ INITIALIZED // All components are fully initialized.
+ };
+
+ // These fields are accessed through the API, offsets must be kept in sync
+ // with v8::internal::Internals (in include/v8.h) constants. This is also
+ // verified in Isolate::Init() using runtime checks.
+ State state_; // Will be padded to kApiPointerSize.
+ void* embedder_data_;
+ Heap heap_;
+
+ // The per-process lock should be acquired before the ThreadDataTable is
+ // modified.
+ class ThreadDataTable {
+ public:
+ ThreadDataTable();
+ ~ThreadDataTable();
+
+ PerIsolateThreadData* Lookup(Isolate* isolate, ThreadId thread_id);
+ void Insert(PerIsolateThreadData* data);
+ void Remove(Isolate* isolate, ThreadId thread_id);
+ void Remove(PerIsolateThreadData* data);
+ void RemoveAllThreads(Isolate* isolate);
+
+ private:
+ PerIsolateThreadData* list_;
+ };
+
+ // These items form a stack synchronously with threads Enter'ing and Exit'ing
+ // the Isolate. The top of the stack points to a thread which is currently
+ // running the Isolate. When the stack is empty, the Isolate is considered
+ // not entered by any thread and can be Disposed.
+ // If the same thread enters the Isolate more then once, the entry_count_
+ // is incremented rather then a new item pushed to the stack.
+ class EntryStackItem {
+ public:
+ EntryStackItem(PerIsolateThreadData* previous_thread_data,
+ Isolate* previous_isolate,
+ EntryStackItem* previous_item)
+ : entry_count(1),
+ previous_thread_data(previous_thread_data),
+ previous_isolate(previous_isolate),
+ previous_item(previous_item) { }
+
+ int entry_count;
+ PerIsolateThreadData* previous_thread_data;
+ Isolate* previous_isolate;
+ EntryStackItem* previous_item;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EntryStackItem);
+ };
+
+ // This mutex protects highest_thread_id_, thread_data_table_ and
+ // default_isolate_.
+ static Mutex* process_wide_mutex_;
+
+ static Thread::LocalStorageKey per_isolate_thread_data_key_;
+ static Thread::LocalStorageKey isolate_key_;
+ static Thread::LocalStorageKey thread_id_key_;
+ static Isolate* default_isolate_;
+ static ThreadDataTable* thread_data_table_;
+
+ // A global counter for all generated Isolates, might overflow.
+ static Atomic32 isolate_counter_;
+
+ void Deinit();
+
+ static void SetIsolateThreadLocals(Isolate* isolate,
+ PerIsolateThreadData* data);
+
+ // Allocate and insert PerIsolateThreadData into the ThreadDataTable
+ // (regardless of whether such data already exists).
+ PerIsolateThreadData* AllocatePerIsolateThreadData(ThreadId thread_id);
+
+ // Find the PerThread for this particular (isolate, thread) combination.
+ // If one does not yet exist, allocate a new one.
+ PerIsolateThreadData* FindOrAllocatePerThreadDataForThisThread();
+
+ // Initializes the current thread to run this Isolate.
+ // Not thread-safe. Multiple threads should not Enter/Exit the same isolate
+ // at the same time, this should be prevented using external locking.
+ void Enter();
+
+ // Exits the current thread. The previosuly entered Isolate is restored
+ // for the thread.
+ // Not thread-safe. Multiple threads should not Enter/Exit the same isolate
+ // at the same time, this should be prevented using external locking.
+ void Exit();
+
+ void PreallocatedMemoryThreadStart();
+ void PreallocatedMemoryThreadStop();
+ void InitializeThreadLocal();
+
+ void PrintStackTrace(FILE* out, ThreadLocalTop* thread);
+ void MarkCompactPrologue(bool is_compacting,
+ ThreadLocalTop* archived_thread_data);
+ void MarkCompactEpilogue(bool is_compacting,
+ ThreadLocalTop* archived_thread_data);
+
+ void FillCache();
+
+ void PropagatePendingExceptionToExternalTryCatch();
+
+ void InitializeDebugger();
+
+ // Traverse prototype chain to find out whether the object is derived from
+ // the Error object.
+ bool IsErrorObject(Handle<Object> obj);
+
+ Atomic32 id_;
+ EntryStackItem* entry_stack_;
+ int stack_trace_nesting_level_;
+ StringStream* incomplete_message_;
+ // The preallocated memory thread singleton.
+ PreallocatedMemoryThread* preallocated_memory_thread_;
+ Address isolate_addresses_[kIsolateAddressCount + 1]; // NOLINT
+ NoAllocationStringAllocator* preallocated_message_space_;
+ Bootstrapper* bootstrapper_;
+ RuntimeProfiler* runtime_profiler_;
+ CompilationCache* compilation_cache_;
+ Counters* counters_;
+ CodeRange* code_range_;
+ Mutex* break_access_;
+ Atomic32 debugger_initialized_;
+ Mutex* debugger_access_;
+ Logger* logger_;
+ StackGuard stack_guard_;
+ StatsTable* stats_table_;
+ StubCache* stub_cache_;
+ DeoptimizerData* deoptimizer_data_;
+ ThreadLocalTop thread_local_top_;
+ bool capture_stack_trace_for_uncaught_exceptions_;
+ int stack_trace_for_uncaught_exceptions_frame_limit_;
+ StackTrace::StackTraceOptions stack_trace_for_uncaught_exceptions_options_;
+ TranscendentalCache* transcendental_cache_;
+ MemoryAllocator* memory_allocator_;
+ KeyedLookupCache* keyed_lookup_cache_;
+ ContextSlotCache* context_slot_cache_;
+ DescriptorLookupCache* descriptor_lookup_cache_;
+ v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
+ HandleScopeImplementer* handle_scope_implementer_;
+ UnicodeCache* unicode_cache_;
+ Zone runtime_zone_;
+ PreallocatedStorage in_use_list_;
+ PreallocatedStorage free_list_;
+ bool preallocated_storage_preallocated_;
+ InnerPointerToCodeCache* inner_pointer_to_code_cache_;
+ ConsStringIteratorOp* write_iterator_;
+ GlobalHandles* global_handles_;
+ EternalHandles* eternal_handles_;
+ ContextSwitcher* context_switcher_;
+ ThreadManager* thread_manager_;
+ RuntimeState runtime_state_;
+ bool fp_stubs_generated_;
+ Builtins builtins_;
+ bool has_installed_extensions_;
+ StringTracker* string_tracker_;
+ unibrow::Mapping<unibrow::Ecma262UnCanonicalize> jsregexp_uncanonicalize_;
+ unibrow::Mapping<unibrow::CanonicalizationRange> jsregexp_canonrange_;
+ ConsStringIteratorOp objects_string_compare_iterator_a_;
+ ConsStringIteratorOp objects_string_compare_iterator_b_;
+ StaticResource<ConsStringIteratorOp> objects_string_iterator_;
+ unibrow::Mapping<unibrow::Ecma262Canonicalize>
+ regexp_macro_assembler_canonicalize_;
+ RegExpStack* regexp_stack_;
+ DateCache* date_cache_;
+ unibrow::Mapping<unibrow::Ecma262Canonicalize> interp_canonicalize_mapping_;
+ CodeStubInterfaceDescriptor* code_stub_interface_descriptors_;
+
+ // True if this isolate was initialized from a snapshot.
+ bool initialized_from_snapshot_;
+
+ // Time stamp at initialization.
+ double time_millis_at_init_;
+
+#if V8_TARGET_ARCH_ARM && !defined(__arm__) || \
+ V8_TARGET_ARCH_MIPS && !defined(__mips__)
+ bool simulator_initialized_;
+ HashMap* simulator_i_cache_;
+ Redirection* simulator_redirection_;
+#endif
+
+#ifdef DEBUG
+ // A static array of histogram info for each type.
+ HistogramInfo heap_histograms_[LAST_TYPE + 1];
+ JSObject::SpillInformation js_spill_information_;
+ int code_kind_statistics_[Code::NUMBER_OF_KINDS];
+#endif
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Debugger* debugger_;
+ Debug* debug_;
+#endif
+ CpuProfiler* cpu_profiler_;
+ HeapProfiler* heap_profiler_;
+ FunctionEntryHook function_entry_hook_;
+
+#define GLOBAL_BACKING_STORE(type, name, initialvalue) \
+ type name##_;
+ ISOLATE_INIT_LIST(GLOBAL_BACKING_STORE)
+#undef GLOBAL_BACKING_STORE
+
+#define GLOBAL_ARRAY_BACKING_STORE(type, name, length) \
+ type name##_[length];
+ ISOLATE_INIT_ARRAY_LIST(GLOBAL_ARRAY_BACKING_STORE)
+#undef GLOBAL_ARRAY_BACKING_STORE
+
+#ifdef DEBUG
+ // This class is huge and has a number of fields controlled by
+ // preprocessor defines. Make sure the offsets of these fields agree
+ // between compilation units.
+#define ISOLATE_FIELD_OFFSET(type, name, ignored) \
+ static const intptr_t name##_debug_offset_;
+ ISOLATE_INIT_LIST(ISOLATE_FIELD_OFFSET)
+ ISOLATE_INIT_ARRAY_LIST(ISOLATE_FIELD_OFFSET)
+#undef ISOLATE_FIELD_OFFSET
+#endif
+
+ DeferredHandles* deferred_handles_head_;
+ OptimizingCompilerThread optimizing_compiler_thread_;
+ MarkingThread** marking_thread_;
+ SweeperThread** sweeper_thread_;
+ CallbackTable* callback_table_;
+
+ // Counts deopt points if deopt_every_n_times is enabled.
+ unsigned int stress_deopt_count_;
+
+ friend class ExecutionAccess;
+ friend class HandleScopeImplementer;
+ friend class IsolateInitializer;
+ friend class MarkingThread;
+ friend class OptimizingCompilerThread;
+ friend class SweeperThread;
+ friend class ThreadManager;
+ friend class Simulator;
+ friend class StackGuard;
+ friend class ThreadId;
+ friend class TestMemoryAllocatorScope;
+ friend class TestCodeRangeScope;
+ friend class v8::Isolate;
+ friend class v8::Locker;
+ friend class v8::Unlocker;
+
+ DISALLOW_COPY_AND_ASSIGN(Isolate);
+};
+
+
+// If the GCC version is 4.1.x or 4.2.x an additional field is added to the
+// class as a work around for a bug in the generated code found with these
+// versions of GCC. See V8 issue 122 for details.
+class SaveContext BASE_EMBEDDED {
+ public:
+ inline explicit SaveContext(Isolate* isolate);
+
+ ~SaveContext() {
+ if (context_.is_null()) {
+ Isolate* isolate = Isolate::Current();
+ isolate->set_context(NULL);
+ isolate->set_save_context(prev_);
+ } else {
+ Isolate* isolate = context_->GetIsolate();
+ isolate->set_context(*context_);
+ isolate->set_save_context(prev_);
+ }
+ }
+
+ Handle<Context> context() { return context_; }
+ SaveContext* prev() { return prev_; }
+
+ // Returns true if this save context is below a given JavaScript frame.
+ bool IsBelowFrame(JavaScriptFrame* frame) {
+ return (c_entry_fp_ == 0) || (c_entry_fp_ > frame->sp());
+ }
+
+ private:
+ Handle<Context> context_;
+#if __GNUC_VERSION__ >= 40100 && __GNUC_VERSION__ < 40300
+ Handle<Context> dummy_;
+#endif
+ SaveContext* prev_;
+ Address c_entry_fp_;
+};
+
+
+class AssertNoContextChange BASE_EMBEDDED {
+#ifdef DEBUG
+ public:
+ AssertNoContextChange() :
+ scope_(Isolate::Current()),
+ context_(Isolate::Current()->context(), Isolate::Current()) {
+ }
+
+ ~AssertNoContextChange() {
+ ASSERT(Isolate::Current()->context() == *context_);
+ }
+
+ private:
+ HandleScope scope_;
+ Handle<Context> context_;
+#else
+ public:
+ AssertNoContextChange() { }
+#endif
+};
+
+
+class ExecutionAccess BASE_EMBEDDED {
+ public:
+ explicit ExecutionAccess(Isolate* isolate) : isolate_(isolate) {
+ Lock(isolate);
+ }
+ ~ExecutionAccess() { Unlock(isolate_); }
+
+ static void Lock(Isolate* isolate) { isolate->break_access_->Lock(); }
+ static void Unlock(Isolate* isolate) { isolate->break_access_->Unlock(); }
+
+ static bool TryLock(Isolate* isolate) {
+ return isolate->break_access_->TryLock();
+ }
+
+ private:
+ Isolate* isolate_;
+};
+
+
+// Support for checking for stack-overflows in C++ code.
+class StackLimitCheck BASE_EMBEDDED {
+ public:
+ explicit StackLimitCheck(Isolate* isolate) : isolate_(isolate) { }
+
+ bool HasOverflowed() const {
+ StackGuard* stack_guard = isolate_->stack_guard();
+ return (reinterpret_cast<uintptr_t>(this) < stack_guard->real_climit());
+ }
+ private:
+ Isolate* isolate_;
+};
+
+
+// Support for temporarily postponing interrupts. When the outermost
+// postpone scope is left the interrupts will be re-enabled and any
+// interrupts that occurred while in the scope will be taken into
+// account.
+class PostponeInterruptsScope BASE_EMBEDDED {
+ public:
+ explicit PostponeInterruptsScope(Isolate* isolate)
+ : stack_guard_(isolate->stack_guard()) {
+ stack_guard_->thread_local_.postpone_interrupts_nesting_++;
+ stack_guard_->DisableInterrupts();
+ }
+
+ ~PostponeInterruptsScope() {
+ if (--stack_guard_->thread_local_.postpone_interrupts_nesting_ == 0) {
+ stack_guard_->EnableInterrupts();
+ }
+ }
+ private:
+ StackGuard* stack_guard_;
+};
+
+
+// Temporary macros for accessing current isolate and its subobjects.
+// They provide better readability, especially when used a lot in the code.
+#define HEAP (v8::internal::Isolate::Current()->heap())
+#define ISOLATE (v8::internal::Isolate::Current())
+
+
+// Tells whether the native context is marked with out of memory.
+inline bool Context::has_out_of_memory() {
+ return native_context()->out_of_memory()->IsTrue();
+}
+
+
+// Mark the native context with out of memory.
+inline void Context::mark_out_of_memory() {
+ native_context()->set_out_of_memory(HEAP->true_value());
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_ISOLATE_H_
diff --git a/chromium/v8/src/json-parser.h b/chromium/v8/src/json-parser.h
new file mode 100644
index 00000000000..72c69100d16
--- /dev/null
+++ b/chromium/v8/src/json-parser.h
@@ -0,0 +1,809 @@
+// 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_JSON_PARSER_H_
+#define V8_JSON_PARSER_H_
+
+#include "v8.h"
+
+#include "char-predicates-inl.h"
+#include "v8conversions.h"
+#include "messages.h"
+#include "spaces-inl.h"
+#include "token.h"
+
+namespace v8 {
+namespace internal {
+
+// A simple json parser.
+template <bool seq_ascii>
+class JsonParser BASE_EMBEDDED {
+ public:
+ static Handle<Object> Parse(Handle<String> source) {
+ return JsonParser(source).ParseJson();
+ }
+
+ static const int kEndOfString = -1;
+
+ private:
+ explicit JsonParser(Handle<String> source)
+ : source_(source),
+ source_length_(source->length()),
+ isolate_(source->map()->GetHeap()->isolate()),
+ factory_(isolate_->factory()),
+ zone_(isolate_),
+ object_constructor_(isolate_->native_context()->object_function(),
+ isolate_),
+ position_(-1) {
+ FlattenString(source_);
+ pretenure_ = (source_length_ >= kPretenureTreshold) ? TENURED : NOT_TENURED;
+
+ // Optimized fast case where we only have ASCII characters.
+ if (seq_ascii) {
+ seq_source_ = Handle<SeqOneByteString>::cast(source_);
+ }
+ }
+
+ // Parse a string containing a single JSON value.
+ Handle<Object> ParseJson();
+
+ inline void Advance() {
+ position_++;
+ if (position_ >= source_length_) {
+ c0_ = kEndOfString;
+ } else if (seq_ascii) {
+ c0_ = seq_source_->SeqOneByteStringGet(position_);
+ } else {
+ c0_ = source_->Get(position_);
+ }
+ }
+
+ // The JSON lexical grammar is specified in the ECMAScript 5 standard,
+ // section 15.12.1.1. The only allowed whitespace characters between tokens
+ // are tab, carriage-return, newline and space.
+
+ inline void AdvanceSkipWhitespace() {
+ do {
+ Advance();
+ } while (c0_ == ' ' || c0_ == '\t' || c0_ == '\n' || c0_ == '\r');
+ }
+
+ inline void SkipWhitespace() {
+ while (c0_ == ' ' || c0_ == '\t' || c0_ == '\n' || c0_ == '\r') {
+ Advance();
+ }
+ }
+
+ inline uc32 AdvanceGetChar() {
+ Advance();
+ return c0_;
+ }
+
+ // Checks that current charater is c.
+ // If so, then consume c and skip whitespace.
+ inline bool MatchSkipWhiteSpace(uc32 c) {
+ if (c0_ == c) {
+ AdvanceSkipWhitespace();
+ return true;
+ }
+ return false;
+ }
+
+ // A JSON string (production JSONString) is subset of valid JavaScript string
+ // literals. The string must only be double-quoted (not single-quoted), and
+ // the only allowed backslash-escapes are ", /, \, b, f, n, r, t and
+ // four-digit hex escapes (uXXXX). Any other use of backslashes is invalid.
+ Handle<String> ParseJsonString() {
+ return ScanJsonString<false>();
+ }
+
+ bool ParseJsonString(Handle<String> expected) {
+ int length = expected->length();
+ if (source_->length() - position_ - 1 > length) {
+ DisallowHeapAllocation no_gc;
+ String::FlatContent content = expected->GetFlatContent();
+ if (content.IsAscii()) {
+ ASSERT_EQ('"', c0_);
+ const uint8_t* input_chars = seq_source_->GetChars() + position_ + 1;
+ const uint8_t* expected_chars = content.ToOneByteVector().start();
+ for (int i = 0; i < length; i++) {
+ uint8_t c0 = input_chars[i];
+ if (c0 != expected_chars[i] ||
+ c0 == '"' || c0 < 0x20 || c0 == '\\') {
+ return false;
+ }
+ }
+ if (input_chars[length] == '"') {
+ position_ = position_ + length + 1;
+ AdvanceSkipWhitespace();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ Handle<String> ParseJsonInternalizedString() {
+ return ScanJsonString<true>();
+ }
+
+ template <bool is_internalized>
+ Handle<String> ScanJsonString();
+ // Creates a new string and copies prefix[start..end] into the beginning
+ // of it. Then scans the rest of the string, adding characters after the
+ // prefix. Called by ScanJsonString when reaching a '\' or non-ASCII char.
+ template <typename StringType, typename SinkChar>
+ Handle<String> SlowScanJsonString(Handle<String> prefix, int start, int end);
+
+ // A JSON number (production JSONNumber) is a subset of the valid JavaScript
+ // decimal number literals.
+ // It includes an optional minus sign, must have at least one
+ // digit before and after a decimal point, may not have prefixed zeros (unless
+ // the integer part is zero), and may include an exponent part (e.g., "e-10").
+ // Hexadecimal and octal numbers are not allowed.
+ Handle<Object> ParseJsonNumber();
+
+ // Parse a single JSON value from input (grammar production JSONValue).
+ // A JSON value is either a (double-quoted) string literal, a number literal,
+ // one of "true", "false", or "null", or an object or array literal.
+ Handle<Object> ParseJsonValue();
+
+ // Parse a JSON object literal (grammar production JSONObject).
+ // 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 doesn't allow numbers and identifiers as keys, like a
+ // JavaScript array.
+ Handle<Object> ParseJsonObject();
+
+ // Parses a JSON array literal (grammar production JSONArray). An array
+ // literal is a square-bracketed and comma separated sequence (possibly empty)
+ // of JSON values.
+ // A JSON array doesn't allow leaving out values from the sequence, nor does
+ // it allow a terminal comma, like a JavaScript array does.
+ Handle<Object> ParseJsonArray();
+
+
+ // Mark that a parsing error has happened at the current token, and
+ // return a null handle. Primarily for readability.
+ inline Handle<Object> ReportUnexpectedCharacter() {
+ return Handle<Object>::null();
+ }
+
+ inline Isolate* isolate() { return isolate_; }
+ inline Factory* factory() { return factory_; }
+ inline Handle<JSFunction> object_constructor() { return object_constructor_; }
+
+ static const int kInitialSpecialStringLength = 1024;
+ static const int kPretenureTreshold = 100 * 1024;
+
+
+ private:
+ Zone* zone() { return &zone_; }
+
+ Handle<String> source_;
+ int source_length_;
+ Handle<SeqOneByteString> seq_source_;
+
+ PretenureFlag pretenure_;
+ Isolate* isolate_;
+ Factory* factory_;
+ Zone zone_;
+ Handle<JSFunction> object_constructor_;
+ uc32 c0_;
+ int position_;
+};
+
+template <bool seq_ascii>
+Handle<Object> JsonParser<seq_ascii>::ParseJson() {
+ // Advance to the first character (possibly EOS)
+ AdvanceSkipWhitespace();
+ Handle<Object> result = ParseJsonValue();
+ if (result.is_null() || c0_ != kEndOfString) {
+ // Some exception (for example stack overflow) is already pending.
+ if (isolate_->has_pending_exception()) return Handle<Object>::null();
+
+ // Parse failed. Current character is the unexpected token.
+ const char* message;
+ Factory* factory = this->factory();
+ Handle<JSArray> array;
+
+ switch (c0_) {
+ case kEndOfString:
+ message = "unexpected_eos";
+ array = factory->NewJSArray(0);
+ break;
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ message = "unexpected_token_number";
+ array = factory->NewJSArray(0);
+ break;
+ case '"':
+ message = "unexpected_token_string";
+ array = factory->NewJSArray(0);
+ break;
+ default:
+ message = "unexpected_token";
+ Handle<Object> name =
+ LookupSingleCharacterStringFromCode(isolate_, c0_);
+ Handle<FixedArray> element = factory->NewFixedArray(1);
+ element->set(0, *name);
+ array = factory->NewJSArrayWithElements(element);
+ break;
+ }
+
+ MessageLocation location(factory->NewScript(source_),
+ position_,
+ position_ + 1);
+ Handle<Object> result = factory->NewSyntaxError(message, array);
+ isolate()->Throw(*result, &location);
+ return Handle<Object>::null();
+ }
+ return result;
+}
+
+
+// Parse any JSON value.
+template <bool seq_ascii>
+Handle<Object> JsonParser<seq_ascii>::ParseJsonValue() {
+ StackLimitCheck stack_check(isolate_);
+ if (stack_check.HasOverflowed()) {
+ isolate_->StackOverflow();
+ return Handle<Object>::null();
+ }
+
+ if (c0_ == '"') return ParseJsonString();
+ if ((c0_ >= '0' && c0_ <= '9') || c0_ == '-') return ParseJsonNumber();
+ if (c0_ == '{') return ParseJsonObject();
+ if (c0_ == '[') return ParseJsonArray();
+ if (c0_ == 'f') {
+ if (AdvanceGetChar() == 'a' && AdvanceGetChar() == 'l' &&
+ AdvanceGetChar() == 's' && AdvanceGetChar() == 'e') {
+ AdvanceSkipWhitespace();
+ return factory()->false_value();
+ }
+ return ReportUnexpectedCharacter();
+ }
+ if (c0_ == 't') {
+ if (AdvanceGetChar() == 'r' && AdvanceGetChar() == 'u' &&
+ AdvanceGetChar() == 'e') {
+ AdvanceSkipWhitespace();
+ return factory()->true_value();
+ }
+ return ReportUnexpectedCharacter();
+ }
+ if (c0_ == 'n') {
+ if (AdvanceGetChar() == 'u' && AdvanceGetChar() == 'l' &&
+ AdvanceGetChar() == 'l') {
+ AdvanceSkipWhitespace();
+ return factory()->null_value();
+ }
+ return ReportUnexpectedCharacter();
+ }
+ return ReportUnexpectedCharacter();
+}
+
+
+// Parse a JSON object. Position must be right at '{'.
+template <bool seq_ascii>
+Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
+ HandleScope scope(isolate());
+ Handle<JSObject> json_object =
+ factory()->NewJSObject(object_constructor(), pretenure_);
+ Handle<Map> map(json_object->map());
+ ZoneList<Handle<Object> > properties(8, zone());
+ ASSERT_EQ(c0_, '{');
+
+ bool transitioning = true;
+
+ AdvanceSkipWhitespace();
+ if (c0_ != '}') {
+ do {
+ if (c0_ != '"') return ReportUnexpectedCharacter();
+
+ int start_position = position_;
+ Advance();
+
+ uint32_t index = 0;
+ if (c0_ >= '0' && c0_ <= '9') {
+ // Maybe an array index, try to parse it.
+ if (c0_ == '0') {
+ // With a leading zero, the string has to be "0" only to be an index.
+ Advance();
+ } else {
+ do {
+ int d = c0_ - '0';
+ if (index > 429496729U - ((d > 5) ? 1 : 0)) break;
+ index = (index * 10) + d;
+ Advance();
+ } while (c0_ >= '0' && c0_ <= '9');
+ }
+
+ if (c0_ == '"') {
+ // Successfully parsed index, parse and store element.
+ AdvanceSkipWhitespace();
+
+ if (c0_ != ':') return ReportUnexpectedCharacter();
+ AdvanceSkipWhitespace();
+ Handle<Object> value = ParseJsonValue();
+ if (value.is_null()) return ReportUnexpectedCharacter();
+
+ JSObject::SetOwnElement(json_object, index, value, kNonStrictMode);
+ continue;
+ }
+ // Not an index, fallback to the slow path.
+ }
+
+ position_ = start_position;
+#ifdef DEBUG
+ c0_ = '"';
+#endif
+
+ Handle<String> key;
+ Handle<Object> value;
+
+ // Try to follow existing transitions as long as possible. Once we stop
+ // transitioning, no transition can be found anymore.
+ if (transitioning) {
+ // First check whether there is a single expected transition. If so, try
+ // to parse it first.
+ bool follow_expected = false;
+ Handle<Map> target;
+ if (seq_ascii) {
+ key = JSObject::ExpectedTransitionKey(map);
+ follow_expected = !key.is_null() && ParseJsonString(key);
+ }
+ // If the expected transition hits, follow it.
+ if (follow_expected) {
+ target = JSObject::ExpectedTransitionTarget(map);
+ } else {
+ // If the expected transition failed, parse an internalized string and
+ // try to find a matching transition.
+ key = ParseJsonInternalizedString();
+ if (key.is_null()) return ReportUnexpectedCharacter();
+
+ target = JSObject::FindTransitionToField(map, key);
+ // If a transition was found, follow it and continue.
+ transitioning = !target.is_null();
+ }
+ if (c0_ != ':') return ReportUnexpectedCharacter();
+
+ AdvanceSkipWhitespace();
+ value = ParseJsonValue();
+ if (value.is_null()) return ReportUnexpectedCharacter();
+
+ if (transitioning) {
+ int descriptor = map->NumberOfOwnDescriptors();
+ PropertyDetails details =
+ target->instance_descriptors()->GetDetails(descriptor);
+ Representation expected_representation = details.representation();
+
+ if (value->FitsRepresentation(expected_representation)) {
+ // If the target representation is double and the value is already
+ // double, use the existing box.
+ if (FLAG_track_double_fields &&
+ value->IsSmi() &&
+ expected_representation.IsDouble()) {
+ value = factory()->NewHeapNumber(
+ Handle<Smi>::cast(value)->value());
+ }
+ properties.Add(value, zone());
+ map = target;
+ continue;
+ } else {
+ transitioning = false;
+ }
+ }
+
+ // Commit the intermediate state to the object and stop transitioning.
+ JSObject::AllocateStorageForMap(json_object, map);
+ int length = properties.length();
+ for (int i = 0; i < length; i++) {
+ Handle<Object> value = properties[i];
+ json_object->FastPropertyAtPut(i, *value);
+ }
+ } else {
+ key = ParseJsonInternalizedString();
+ if (key.is_null() || c0_ != ':') return ReportUnexpectedCharacter();
+
+ AdvanceSkipWhitespace();
+ value = ParseJsonValue();
+ if (value.is_null()) return ReportUnexpectedCharacter();
+ }
+
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ json_object, key, value, NONE);
+ } while (MatchSkipWhiteSpace(','));
+ if (c0_ != '}') {
+ return ReportUnexpectedCharacter();
+ }
+
+ // If we transitioned until the very end, transition the map now.
+ if (transitioning) {
+ JSObject::AllocateStorageForMap(json_object, map);
+ int length = properties.length();
+ for (int i = 0; i < length; i++) {
+ Handle<Object> value = properties[i];
+ json_object->FastPropertyAtPut(i, *value);
+ }
+ }
+ }
+ AdvanceSkipWhitespace();
+ return scope.CloseAndEscape(json_object);
+}
+
+// Parse a JSON array. Position must be right at '['.
+template <bool seq_ascii>
+Handle<Object> JsonParser<seq_ascii>::ParseJsonArray() {
+ HandleScope scope(isolate());
+ ZoneList<Handle<Object> > elements(4, zone());
+ ASSERT_EQ(c0_, '[');
+
+ AdvanceSkipWhitespace();
+ if (c0_ != ']') {
+ do {
+ Handle<Object> element = ParseJsonValue();
+ if (element.is_null()) return ReportUnexpectedCharacter();
+ elements.Add(element, zone());
+ } while (MatchSkipWhiteSpace(','));
+ if (c0_ != ']') {
+ return ReportUnexpectedCharacter();
+ }
+ }
+ AdvanceSkipWhitespace();
+ // Allocate a fixed array with all the elements.
+ Handle<FixedArray> fast_elements =
+ factory()->NewFixedArray(elements.length(), pretenure_);
+ for (int i = 0, n = elements.length(); i < n; i++) {
+ fast_elements->set(i, *elements[i]);
+ }
+ Handle<Object> json_array = factory()->NewJSArrayWithElements(
+ fast_elements, FAST_ELEMENTS, pretenure_);
+ return scope.CloseAndEscape(json_array);
+}
+
+
+template <bool seq_ascii>
+Handle<Object> JsonParser<seq_ascii>::ParseJsonNumber() {
+ bool negative = false;
+ int beg_pos = position_;
+ if (c0_ == '-') {
+ Advance();
+ negative = true;
+ }
+ if (c0_ == '0') {
+ Advance();
+ // Prefix zero is only allowed if it's the only digit before
+ // a decimal point or exponent.
+ if ('0' <= c0_ && c0_ <= '9') return ReportUnexpectedCharacter();
+ } else {
+ int i = 0;
+ int digits = 0;
+ if (c0_ < '1' || c0_ > '9') return ReportUnexpectedCharacter();
+ do {
+ i = i * 10 + c0_ - '0';
+ digits++;
+ Advance();
+ } while (c0_ >= '0' && c0_ <= '9');
+ if (c0_ != '.' && c0_ != 'e' && c0_ != 'E' && digits < 10) {
+ SkipWhitespace();
+ return Handle<Smi>(Smi::FromInt((negative ? -i : i)), isolate());
+ }
+ }
+ if (c0_ == '.') {
+ Advance();
+ if (c0_ < '0' || c0_ > '9') return ReportUnexpectedCharacter();
+ do {
+ Advance();
+ } while (c0_ >= '0' && c0_ <= '9');
+ }
+ if (AsciiAlphaToLower(c0_) == 'e') {
+ Advance();
+ if (c0_ == '-' || c0_ == '+') Advance();
+ if (c0_ < '0' || c0_ > '9') return ReportUnexpectedCharacter();
+ do {
+ Advance();
+ } while (c0_ >= '0' && c0_ <= '9');
+ }
+ int length = position_ - beg_pos;
+ double number;
+ if (seq_ascii) {
+ Vector<const uint8_t> chars(seq_source_->GetChars() + beg_pos, length);
+ number = StringToDouble(isolate()->unicode_cache(),
+ Vector<const char>::cast(chars),
+ NO_FLAGS, // Hex, octal or trailing junk.
+ OS::nan_value());
+ } else {
+ Vector<uint8_t> buffer = Vector<uint8_t>::New(length);
+ String::WriteToFlat(*source_, buffer.start(), beg_pos, position_);
+ Vector<const uint8_t> result =
+ Vector<const uint8_t>(buffer.start(), length);
+ number = StringToDouble(isolate()->unicode_cache(),
+ // TODO(dcarney): Convert StringToDouble to uint_t.
+ Vector<const char>::cast(result),
+ NO_FLAGS, // Hex, octal or trailing junk.
+ 0.0);
+ buffer.Dispose();
+ }
+ SkipWhitespace();
+ return factory()->NewNumber(number, pretenure_);
+}
+
+
+template <typename StringType>
+inline void SeqStringSet(Handle<StringType> seq_str, int i, uc32 c);
+
+template <>
+inline void SeqStringSet(Handle<SeqTwoByteString> seq_str, int i, uc32 c) {
+ seq_str->SeqTwoByteStringSet(i, c);
+}
+
+template <>
+inline void SeqStringSet(Handle<SeqOneByteString> seq_str, int i, uc32 c) {
+ seq_str->SeqOneByteStringSet(i, c);
+}
+
+template <typename StringType>
+inline Handle<StringType> NewRawString(Factory* factory,
+ int length,
+ PretenureFlag pretenure);
+
+template <>
+inline Handle<SeqTwoByteString> NewRawString(Factory* factory,
+ int length,
+ PretenureFlag pretenure) {
+ return factory->NewRawTwoByteString(length, pretenure);
+}
+
+template <>
+inline Handle<SeqOneByteString> NewRawString(Factory* factory,
+ int length,
+ PretenureFlag pretenure) {
+ return factory->NewRawOneByteString(length, pretenure);
+}
+
+
+// Scans the rest of a JSON string starting from position_ and writes
+// prefix[start..end] along with the scanned characters into a
+// sequential string of type StringType.
+template <bool seq_ascii>
+template <typename StringType, typename SinkChar>
+Handle<String> JsonParser<seq_ascii>::SlowScanJsonString(
+ Handle<String> prefix, int start, int end) {
+ int count = end - start;
+ int max_length = count + source_length_ - position_;
+ int length = Min(max_length, Max(kInitialSpecialStringLength, 2 * count));
+ Handle<StringType> seq_string =
+ NewRawString<StringType>(factory(), length, pretenure_);
+ // Copy prefix into seq_str.
+ SinkChar* dest = seq_string->GetChars();
+ String::WriteToFlat(*prefix, dest, start, end);
+
+ while (c0_ != '"') {
+ // Check for control character (0x00-0x1f) or unterminated string (<0).
+ if (c0_ < 0x20) return Handle<String>::null();
+ if (count >= length) {
+ // We need to create a longer sequential string for the result.
+ return SlowScanJsonString<StringType, SinkChar>(seq_string, 0, count);
+ }
+ if (c0_ != '\\') {
+ // If the sink can contain UC16 characters, or source_ contains only
+ // ASCII characters, there's no need to test whether we can store the
+ // character. Otherwise check whether the UC16 source character can fit
+ // in the ASCII sink.
+ if (sizeof(SinkChar) == kUC16Size ||
+ seq_ascii ||
+ c0_ <= String::kMaxOneByteCharCode) {
+ SeqStringSet(seq_string, count++, c0_);
+ Advance();
+ } else {
+ // StringType is SeqOneByteString and we just read a non-ASCII char.
+ return SlowScanJsonString<SeqTwoByteString, uc16>(seq_string, 0, count);
+ }
+ } else {
+ Advance(); // Advance past the \.
+ switch (c0_) {
+ case '"':
+ case '\\':
+ case '/':
+ SeqStringSet(seq_string, count++, c0_);
+ break;
+ case 'b':
+ SeqStringSet(seq_string, count++, '\x08');
+ break;
+ case 'f':
+ SeqStringSet(seq_string, count++, '\x0c');
+ break;
+ case 'n':
+ SeqStringSet(seq_string, count++, '\x0a');
+ break;
+ case 'r':
+ SeqStringSet(seq_string, count++, '\x0d');
+ break;
+ case 't':
+ SeqStringSet(seq_string, count++, '\x09');
+ break;
+ case 'u': {
+ uc32 value = 0;
+ for (int i = 0; i < 4; i++) {
+ Advance();
+ int digit = HexValue(c0_);
+ if (digit < 0) {
+ return Handle<String>::null();
+ }
+ value = value * 16 + digit;
+ }
+ if (sizeof(SinkChar) == kUC16Size ||
+ value <= String::kMaxOneByteCharCode) {
+ SeqStringSet(seq_string, count++, value);
+ break;
+ } else {
+ // StringType is SeqOneByteString and we just read a non-ASCII char.
+ position_ -= 6; // Rewind position_ to \ in \uxxxx.
+ Advance();
+ return SlowScanJsonString<SeqTwoByteString, uc16>(seq_string,
+ 0,
+ count);
+ }
+ }
+ default:
+ return Handle<String>::null();
+ }
+ Advance();
+ }
+ }
+
+ ASSERT_EQ('"', c0_);
+ // Advance past the last '"'.
+ AdvanceSkipWhitespace();
+
+ // Shrink seq_string length to count and return.
+ return SeqString::Truncate(seq_string, count);
+}
+
+
+template <bool seq_ascii>
+template <bool is_internalized>
+Handle<String> JsonParser<seq_ascii>::ScanJsonString() {
+ ASSERT_EQ('"', c0_);
+ Advance();
+ if (c0_ == '"') {
+ AdvanceSkipWhitespace();
+ return factory()->empty_string();
+ }
+
+ if (seq_ascii && is_internalized) {
+ // Fast path for existing internalized strings. If the the string being
+ // parsed is not a known internalized string, contains backslashes or
+ // unexpectedly reaches the end of string, return with an empty handle.
+ uint32_t running_hash = isolate()->heap()->HashSeed();
+ int position = position_;
+ uc32 c0 = c0_;
+ do {
+ if (c0 == '\\') {
+ c0_ = c0;
+ int beg_pos = position_;
+ position_ = position;
+ return SlowScanJsonString<SeqOneByteString, uint8_t>(source_,
+ beg_pos,
+ position_);
+ }
+ if (c0 < 0x20) return Handle<String>::null();
+ if (static_cast<uint32_t>(c0) >
+ unibrow::Utf16::kMaxNonSurrogateCharCode) {
+ running_hash =
+ StringHasher::AddCharacterCore(running_hash,
+ unibrow::Utf16::LeadSurrogate(c0));
+ running_hash =
+ StringHasher::AddCharacterCore(running_hash,
+ unibrow::Utf16::TrailSurrogate(c0));
+ } else {
+ running_hash = StringHasher::AddCharacterCore(running_hash, c0);
+ }
+ position++;
+ if (position >= source_length_) return Handle<String>::null();
+ c0 = seq_source_->SeqOneByteStringGet(position);
+ } while (c0 != '"');
+ int length = position - position_;
+ uint32_t hash = (length <= String::kMaxHashCalcLength)
+ ? StringHasher::GetHashCore(running_hash) : length;
+ Vector<const uint8_t> string_vector(
+ seq_source_->GetChars() + position_, length);
+ StringTable* string_table = isolate()->heap()->string_table();
+ uint32_t capacity = string_table->Capacity();
+ uint32_t entry = StringTable::FirstProbe(hash, capacity);
+ uint32_t count = 1;
+ Handle<String> result;
+ while (true) {
+ Object* element = string_table->KeyAt(entry);
+ if (element == isolate()->heap()->undefined_value()) {
+ // Lookup failure.
+ result = factory()->InternalizeOneByteString(
+ seq_source_, position_, length);
+ break;
+ }
+ if (element != isolate()->heap()->the_hole_value() &&
+ String::cast(element)->IsOneByteEqualTo(string_vector)) {
+ result = Handle<String>(String::cast(element), isolate());
+#ifdef DEBUG
+ uint32_t hash_field =
+ (hash << String::kHashShift) | String::kIsNotArrayIndexMask;
+ ASSERT_EQ(static_cast<int>(result->Hash()),
+ static_cast<int>(hash_field >> String::kHashShift));
+#endif
+ break;
+ }
+ entry = StringTable::NextProbe(entry, count++, capacity);
+ }
+ position_ = position;
+ // Advance past the last '"'.
+ AdvanceSkipWhitespace();
+ return result;
+ }
+
+ int beg_pos = position_;
+ // Fast case for ASCII only without escape characters.
+ do {
+ // Check for control character (0x00-0x1f) or unterminated string (<0).
+ if (c0_ < 0x20) return Handle<String>::null();
+ if (c0_ != '\\') {
+ if (seq_ascii || c0_ <= String::kMaxOneByteCharCode) {
+ Advance();
+ } else {
+ return SlowScanJsonString<SeqTwoByteString, uc16>(source_,
+ beg_pos,
+ position_);
+ }
+ } else {
+ return SlowScanJsonString<SeqOneByteString, uint8_t>(source_,
+ beg_pos,
+ position_);
+ }
+ } while (c0_ != '"');
+ int length = position_ - beg_pos;
+ Handle<String> result = factory()->NewRawOneByteString(length, pretenure_);
+ uint8_t* dest = SeqOneByteString::cast(*result)->GetChars();
+ String::WriteToFlat(*source_, dest, beg_pos, position_);
+
+ ASSERT_EQ('"', c0_);
+ // Advance past the last '"'.
+ AdvanceSkipWhitespace();
+ return result;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_JSON_PARSER_H_
diff --git a/chromium/v8/src/json-stringifier.h b/chromium/v8/src/json-stringifier.h
new file mode 100644
index 00000000000..5ebdb40b519
--- /dev/null
+++ b/chromium/v8/src/json-stringifier.h
@@ -0,0 +1,852 @@
+// Copyright 2012 the V8 project authors. 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_JSON_STRINGIFIER_H_
+#define V8_JSON_STRINGIFIER_H_
+
+#include "v8.h"
+#include "v8utils.h"
+#include "v8conversions.h"
+
+namespace v8 {
+namespace internal {
+
+class BasicJsonStringifier BASE_EMBEDDED {
+ public:
+ explicit BasicJsonStringifier(Isolate* isolate);
+
+ MaybeObject* Stringify(Handle<Object> object);
+
+ INLINE(static MaybeObject* StringifyString(Isolate* isolate,
+ Handle<String> object));
+
+ private:
+ static const int kInitialPartLength = 32;
+ static const int kMaxPartLength = 16 * 1024;
+ static const int kPartLengthGrowthFactor = 2;
+
+ enum Result { UNCHANGED, SUCCESS, EXCEPTION, CIRCULAR, STACK_OVERFLOW };
+
+ void Extend();
+
+ void ChangeEncoding();
+
+ INLINE(void ShrinkCurrentPart());
+
+ template <bool is_ascii, typename Char>
+ INLINE(void Append_(Char c));
+
+ template <bool is_ascii, typename Char>
+ INLINE(void Append_(const Char* chars));
+
+ INLINE(void Append(uint8_t c)) {
+ if (is_ascii_) {
+ Append_<true>(c);
+ } else {
+ Append_<false>(c);
+ }
+ }
+
+ INLINE(void AppendAscii(const char* chars)) {
+ if (is_ascii_) {
+ Append_<true>(reinterpret_cast<const uint8_t*>(chars));
+ } else {
+ Append_<false>(reinterpret_cast<const uint8_t*>(chars));
+ }
+ }
+
+ Handle<Object> ApplyToJsonFunction(Handle<Object> object,
+ Handle<Object> key);
+
+ Result SerializeGeneric(Handle<Object> object,
+ Handle<Object> key,
+ bool deferred_comma,
+ bool deferred_key);
+
+ template <typename ResultType, typename Char>
+ INLINE(static MaybeObject* StringifyString_(Isolate* isolate,
+ Vector<Char> vector,
+ Handle<String> result));
+
+ // Entry point to serialize the object.
+ INLINE(Result SerializeObject(Handle<Object> obj)) {
+ return Serialize_<false>(obj, false, factory_->empty_string());
+ }
+
+ // Serialize an array element.
+ // The index may serve as argument for the toJSON function.
+ INLINE(Result SerializeElement(Isolate* isolate,
+ Handle<Object> object,
+ int i)) {
+ return Serialize_<false>(object,
+ false,
+ Handle<Object>(Smi::FromInt(i), isolate));
+ }
+
+ // Serialize a object property.
+ // The key may or may not be serialized depending on the property.
+ // The key may also serve as argument for the toJSON function.
+ INLINE(Result SerializeProperty(Handle<Object> object,
+ bool deferred_comma,
+ Handle<String> deferred_key)) {
+ ASSERT(!deferred_key.is_null());
+ return Serialize_<true>(object, deferred_comma, deferred_key);
+ }
+
+ template <bool deferred_string_key>
+ Result Serialize_(Handle<Object> object, bool comma, Handle<Object> key);
+
+ void SerializeDeferredKey(bool deferred_comma, Handle<Object> deferred_key) {
+ if (deferred_comma) Append(',');
+ SerializeString(Handle<String>::cast(deferred_key));
+ Append(':');
+ }
+
+ Result SerializeSmi(Smi* object);
+
+ Result SerializeDouble(double number);
+ INLINE(Result SerializeHeapNumber(Handle<HeapNumber> object)) {
+ return SerializeDouble(object->value());
+ }
+
+ Result SerializeJSValue(Handle<JSValue> object);
+
+ INLINE(Result SerializeJSArray(Handle<JSArray> object));
+ INLINE(Result SerializeJSObject(Handle<JSObject> object));
+
+ Result SerializeJSArraySlow(Handle<JSArray> object, int length);
+
+ void SerializeString(Handle<String> object);
+
+ template <typename SrcChar, typename DestChar>
+ INLINE(static int SerializeStringUnchecked_(const SrcChar* src,
+ DestChar* dest,
+ int length));
+
+ template <bool is_ascii, typename Char>
+ INLINE(void SerializeString_(Handle<String> string));
+
+ template <typename Char>
+ INLINE(static bool DoNotEscape(Char c));
+
+ template <typename Char>
+ INLINE(static Vector<const Char> GetCharVector(Handle<String> string));
+
+ Result StackPush(Handle<Object> object);
+ void StackPop();
+
+ INLINE(Handle<String> accumulator()) {
+ return Handle<String>(String::cast(accumulator_store_->value()), isolate_);
+ }
+
+ INLINE(void set_accumulator(Handle<String> string)) {
+ return accumulator_store_->set_value(*string);
+ }
+
+ Isolate* isolate_;
+ Factory* factory_;
+ // We use a value wrapper for the string accumulator to keep the
+ // (indirect) handle to it in the outermost handle scope.
+ Handle<JSValue> accumulator_store_;
+ Handle<String> current_part_;
+ Handle<String> tojson_string_;
+ Handle<JSArray> stack_;
+ int current_index_;
+ int part_length_;
+ bool is_ascii_;
+
+ static const int kJsonEscapeTableEntrySize = 8;
+ static const char* const JsonEscapeTable;
+};
+
+
+// Translation table to escape ASCII characters.
+// Table entries start at a multiple of 8 and are null-terminated.
+const char* const BasicJsonStringifier::JsonEscapeTable =
+ "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 "
+ "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 "
+ "\\b\0 \\t\0 \\n\0 \\u000b\0 "
+ "\\f\0 \\r\0 \\u000e\0 \\u000f\0 "
+ "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 "
+ "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 "
+ "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 "
+ "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 "
+ " \0 !\0 \\\"\0 #\0 "
+ "$\0 %\0 &\0 '\0 "
+ "(\0 )\0 *\0 +\0 "
+ ",\0 -\0 .\0 /\0 "
+ "0\0 1\0 2\0 3\0 "
+ "4\0 5\0 6\0 7\0 "
+ "8\0 9\0 :\0 ;\0 "
+ "<\0 =\0 >\0 ?\0 "
+ "@\0 A\0 B\0 C\0 "
+ "D\0 E\0 F\0 G\0 "
+ "H\0 I\0 J\0 K\0 "
+ "L\0 M\0 N\0 O\0 "
+ "P\0 Q\0 R\0 S\0 "
+ "T\0 U\0 V\0 W\0 "
+ "X\0 Y\0 Z\0 [\0 "
+ "\\\\\0 ]\0 ^\0 _\0 "
+ "`\0 a\0 b\0 c\0 "
+ "d\0 e\0 f\0 g\0 "
+ "h\0 i\0 j\0 k\0 "
+ "l\0 m\0 n\0 o\0 "
+ "p\0 q\0 r\0 s\0 "
+ "t\0 u\0 v\0 w\0 "
+ "x\0 y\0 z\0 {\0 "
+ "|\0 }\0 ~\0 \177\0 "
+ "\200\0 \201\0 \202\0 \203\0 "
+ "\204\0 \205\0 \206\0 \207\0 "
+ "\210\0 \211\0 \212\0 \213\0 "
+ "\214\0 \215\0 \216\0 \217\0 "
+ "\220\0 \221\0 \222\0 \223\0 "
+ "\224\0 \225\0 \226\0 \227\0 "
+ "\230\0 \231\0 \232\0 \233\0 "
+ "\234\0 \235\0 \236\0 \237\0 "
+ "\240\0 \241\0 \242\0 \243\0 "
+ "\244\0 \245\0 \246\0 \247\0 "
+ "\250\0 \251\0 \252\0 \253\0 "
+ "\254\0 \255\0 \256\0 \257\0 "
+ "\260\0 \261\0 \262\0 \263\0 "
+ "\264\0 \265\0 \266\0 \267\0 "
+ "\270\0 \271\0 \272\0 \273\0 "
+ "\274\0 \275\0 \276\0 \277\0 "
+ "\300\0 \301\0 \302\0 \303\0 "
+ "\304\0 \305\0 \306\0 \307\0 "
+ "\310\0 \311\0 \312\0 \313\0 "
+ "\314\0 \315\0 \316\0 \317\0 "
+ "\320\0 \321\0 \322\0 \323\0 "
+ "\324\0 \325\0 \326\0 \327\0 "
+ "\330\0 \331\0 \332\0 \333\0 "
+ "\334\0 \335\0 \336\0 \337\0 "
+ "\340\0 \341\0 \342\0 \343\0 "
+ "\344\0 \345\0 \346\0 \347\0 "
+ "\350\0 \351\0 \352\0 \353\0 "
+ "\354\0 \355\0 \356\0 \357\0 "
+ "\360\0 \361\0 \362\0 \363\0 "
+ "\364\0 \365\0 \366\0 \367\0 "
+ "\370\0 \371\0 \372\0 \373\0 "
+ "\374\0 \375\0 \376\0 \377\0 ";
+
+
+BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
+ : isolate_(isolate), current_index_(0), is_ascii_(true) {
+ factory_ = isolate_->factory();
+ accumulator_store_ = Handle<JSValue>::cast(
+ factory_->ToObject(factory_->empty_string()));
+ part_length_ = kInitialPartLength;
+ current_part_ = factory_->NewRawOneByteString(part_length_);
+ tojson_string_ = factory_->toJSON_string();
+ stack_ = factory_->NewJSArray(8);
+}
+
+
+MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) {
+ switch (SerializeObject(object)) {
+ case UNCHANGED:
+ return isolate_->heap()->undefined_value();
+ case SUCCESS:
+ ShrinkCurrentPart();
+ return *factory_->NewConsString(accumulator(), current_part_);
+ case CIRCULAR:
+ return isolate_->Throw(*factory_->NewTypeError(
+ "circular_structure", HandleVector<Object>(NULL, 0)));
+ case STACK_OVERFLOW:
+ return isolate_->StackOverflow();
+ default:
+ return Failure::Exception();
+ }
+}
+
+
+MaybeObject* BasicJsonStringifier::StringifyString(Isolate* isolate,
+ Handle<String> object) {
+ static const int kJsonQuoteWorstCaseBlowup = 6;
+ static const int kSpaceForQuotes = 2;
+ int worst_case_length =
+ object->length() * kJsonQuoteWorstCaseBlowup + kSpaceForQuotes;
+
+ if (worst_case_length > 32 * KB) { // Slow path if too large.
+ BasicJsonStringifier stringifier(isolate);
+ return stringifier.Stringify(object);
+ }
+
+ FlattenString(object);
+ ASSERT(object->IsFlat());
+ if (object->IsOneByteRepresentationUnderneath()) {
+ Handle<String> result =
+ isolate->factory()->NewRawOneByteString(worst_case_length);
+ DisallowHeapAllocation no_gc;
+ return StringifyString_<SeqOneByteString>(
+ isolate,
+ object->GetFlatContent().ToOneByteVector(),
+ result);
+ } else {
+ Handle<String> result =
+ isolate->factory()->NewRawTwoByteString(worst_case_length);
+ DisallowHeapAllocation no_gc;
+ return StringifyString_<SeqTwoByteString>(
+ isolate,
+ object->GetFlatContent().ToUC16Vector(),
+ result);
+ }
+}
+
+
+template <typename ResultType, typename Char>
+MaybeObject* BasicJsonStringifier::StringifyString_(Isolate* isolate,
+ Vector<Char> vector,
+ Handle<String> result) {
+ DisallowHeapAllocation no_gc;
+ int final_size = 0;
+ ResultType* dest = ResultType::cast(*result);
+ dest->Set(final_size++, '\"');
+ final_size += SerializeStringUnchecked_(vector.start(),
+ dest->GetChars() + 1,
+ vector.length());
+ dest->Set(final_size++, '\"');
+ return *SeqString::Truncate(Handle<SeqString>::cast(result), final_size);
+}
+
+
+template <bool is_ascii, typename Char>
+void BasicJsonStringifier::Append_(Char c) {
+ if (is_ascii) {
+ SeqOneByteString::cast(*current_part_)->SeqOneByteStringSet(
+ current_index_++, c);
+ } else {
+ SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet(
+ current_index_++, c);
+ }
+ if (current_index_ == part_length_) Extend();
+}
+
+
+template <bool is_ascii, typename Char>
+void BasicJsonStringifier::Append_(const Char* chars) {
+ for ( ; *chars != '\0'; chars++) Append_<is_ascii, Char>(*chars);
+}
+
+
+Handle<Object> BasicJsonStringifier::ApplyToJsonFunction(
+ Handle<Object> object, Handle<Object> key) {
+ LookupResult lookup(isolate_);
+ JSObject::cast(*object)->LookupRealNamedProperty(*tojson_string_, &lookup);
+ if (!lookup.IsProperty()) return object;
+ PropertyAttributes attr;
+ Handle<Object> fun =
+ Object::GetProperty(object, object, &lookup, tojson_string_, &attr);
+ if (!fun->IsJSFunction()) return object;
+
+ // Call toJSON function.
+ if (key->IsSmi()) key = factory_->NumberToString(key);
+ Handle<Object> argv[] = { key };
+ bool has_exception = false;
+ HandleScope scope(isolate_);
+ object = Execution::Call(fun, object, 1, argv, &has_exception);
+ // Return empty handle to signal an exception.
+ if (has_exception) return Handle<Object>::null();
+ return scope.CloseAndEscape(object);
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::StackPush(
+ Handle<Object> object) {
+ StackLimitCheck check(isolate_);
+ if (check.HasOverflowed()) return STACK_OVERFLOW;
+
+ int length = Smi::cast(stack_->length())->value();
+ FixedArray* elements = FixedArray::cast(stack_->elements());
+ for (int i = 0; i < length; i++) {
+ if (elements->get(i) == *object) {
+ return CIRCULAR;
+ }
+ }
+ stack_->EnsureSize(length + 1);
+ FixedArray::cast(stack_->elements())->set(length, *object);
+ stack_->set_length(Smi::FromInt(length + 1));
+ return SUCCESS;
+}
+
+
+void BasicJsonStringifier::StackPop() {
+ int length = Smi::cast(stack_->length())->value();
+ stack_->set_length(Smi::FromInt(length - 1));
+}
+
+
+template <bool deferred_string_key>
+BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
+ Handle<Object> object, bool comma, Handle<Object> key) {
+ if (object->IsJSObject()) {
+ object = ApplyToJsonFunction(object, key);
+ if (object.is_null()) return EXCEPTION;
+ }
+
+ if (object->IsSmi()) {
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeSmi(Smi::cast(*object));
+ }
+
+ switch (HeapObject::cast(*object)->map()->instance_type()) {
+ case HEAP_NUMBER_TYPE:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
+ case ODDBALL_TYPE:
+ switch (Oddball::cast(*object)->kind()) {
+ case Oddball::kFalse:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ AppendAscii("false");
+ return SUCCESS;
+ case Oddball::kTrue:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ AppendAscii("true");
+ return SUCCESS;
+ case Oddball::kNull:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ AppendAscii("null");
+ return SUCCESS;
+ default:
+ return UNCHANGED;
+ }
+ case JS_ARRAY_TYPE:
+ if (object->IsAccessCheckNeeded()) break;
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeJSArray(Handle<JSArray>::cast(object));
+ case JS_VALUE_TYPE:
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeJSValue(Handle<JSValue>::cast(object));
+ case JS_FUNCTION_TYPE:
+ return UNCHANGED;
+ default:
+ if (object->IsString()) {
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ SerializeString(Handle<String>::cast(object));
+ return SUCCESS;
+ } else if (object->IsJSObject()) {
+ if (object->IsAccessCheckNeeded()) break;
+ if (deferred_string_key) SerializeDeferredKey(comma, key);
+ return SerializeJSObject(Handle<JSObject>::cast(object));
+ }
+ }
+
+ return SerializeGeneric(object, key, comma, deferred_string_key);
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeGeneric(
+ Handle<Object> object,
+ Handle<Object> key,
+ bool deferred_comma,
+ bool deferred_key) {
+ Handle<JSObject> builtins(isolate_->native_context()->builtins());
+ Handle<JSFunction> builtin =
+ Handle<JSFunction>::cast(GetProperty(builtins, "JSONSerializeAdapter"));
+
+ Handle<Object> argv[] = { key, object };
+ bool has_exception = false;
+ Handle<Object> result =
+ Execution::Call(builtin, object, 2, argv, &has_exception);
+ if (has_exception) return EXCEPTION;
+ if (result->IsUndefined()) return UNCHANGED;
+ if (deferred_key) {
+ if (key->IsSmi()) key = factory_->NumberToString(key);
+ SerializeDeferredKey(deferred_comma, key);
+ }
+
+ Handle<String> result_string = Handle<String>::cast(result);
+ // Shrink current part, attach it to the accumulator, also attach the result
+ // string to the accumulator, and allocate a new part.
+ ShrinkCurrentPart(); // Shrink.
+ part_length_ = kInitialPartLength; // Allocate conservatively.
+ Extend(); // Attach current part and allocate new part.
+ // Attach result string to the accumulator.
+ set_accumulator(factory_->NewConsString(accumulator(), result_string));
+ return SUCCESS;
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSValue(
+ Handle<JSValue> object) {
+ bool has_exception = false;
+ String* class_name = object->class_name();
+ if (class_name == isolate_->heap()->String_string()) {
+ Handle<Object> value = Execution::ToString(object, &has_exception);
+ if (has_exception) return EXCEPTION;
+ SerializeString(Handle<String>::cast(value));
+ } else if (class_name == isolate_->heap()->Number_string()) {
+ Handle<Object> value = Execution::ToNumber(object, &has_exception);
+ if (has_exception) return EXCEPTION;
+ if (value->IsSmi()) return SerializeSmi(Smi::cast(*value));
+ SerializeHeapNumber(Handle<HeapNumber>::cast(value));
+ } else {
+ ASSERT(class_name == isolate_->heap()->Boolean_string());
+ Object* value = JSValue::cast(*object)->value();
+ ASSERT(value->IsBoolean());
+ AppendAscii(value->IsTrue() ? "true" : "false");
+ }
+ return SUCCESS;
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeSmi(Smi* object) {
+ static const int kBufferSize = 100;
+ char chars[kBufferSize];
+ Vector<char> buffer(chars, kBufferSize);
+ AppendAscii(IntToCString(object->value(), buffer));
+ return SUCCESS;
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeDouble(
+ double number) {
+ if (std::isinf(number) || std::isnan(number)) {
+ AppendAscii("null");
+ return SUCCESS;
+ }
+ static const int kBufferSize = 100;
+ char chars[kBufferSize];
+ Vector<char> buffer(chars, kBufferSize);
+ AppendAscii(DoubleToCString(number, buffer));
+ return SUCCESS;
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArray(
+ Handle<JSArray> object) {
+ HandleScope handle_scope(isolate_);
+ Result stack_push = StackPush(object);
+ if (stack_push != SUCCESS) return stack_push;
+ int length = Smi::cast(object->length())->value();
+ Append('[');
+ switch (object->GetElementsKind()) {
+ case FAST_SMI_ELEMENTS: {
+ Handle<FixedArray> elements(
+ FixedArray::cast(object->elements()), isolate_);
+ for (int i = 0; i < length; i++) {
+ if (i > 0) Append(',');
+ SerializeSmi(Smi::cast(elements->get(i)));
+ }
+ break;
+ }
+ case FAST_DOUBLE_ELEMENTS: {
+ Handle<FixedDoubleArray> elements(
+ FixedDoubleArray::cast(object->elements()), isolate_);
+ for (int i = 0; i < length; i++) {
+ if (i > 0) Append(',');
+ SerializeDouble(elements->get_scalar(i));
+ }
+ break;
+ }
+ case FAST_ELEMENTS: {
+ Handle<FixedArray> elements(
+ FixedArray::cast(object->elements()), isolate_);
+ for (int i = 0; i < length; i++) {
+ if (i > 0) Append(',');
+ Result result =
+ SerializeElement(isolate_,
+ Handle<Object>(elements->get(i), isolate_),
+ i);
+ if (result == SUCCESS) continue;
+ if (result == UNCHANGED) {
+ AppendAscii("null");
+ } else {
+ return result;
+ }
+ }
+ break;
+ }
+ // TODO(yangguo): The FAST_HOLEY_* cases could be handled in a faster way.
+ // They resemble the non-holey cases except that a prototype chain lookup
+ // is necessary for holes.
+ default: {
+ Result result = SerializeJSArraySlow(object, length);
+ if (result != SUCCESS) return result;
+ break;
+ }
+ }
+ Append(']');
+ StackPop();
+ current_part_ = handle_scope.CloseAndEscape(current_part_);
+ return SUCCESS;
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArraySlow(
+ Handle<JSArray> object, int length) {
+ for (int i = 0; i < length; i++) {
+ if (i > 0) Append(',');
+ Handle<Object> element = Object::GetElement(object, i);
+ RETURN_IF_EMPTY_HANDLE_VALUE(isolate_, element, EXCEPTION);
+ if (element->IsUndefined()) {
+ AppendAscii("null");
+ } else {
+ Result result = SerializeElement(object->GetIsolate(), element, i);
+ if (result == SUCCESS) continue;
+ if (result == UNCHANGED) {
+ AppendAscii("null");
+ } else {
+ return result;
+ }
+ }
+ }
+ return SUCCESS;
+}
+
+
+BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
+ Handle<JSObject> object) {
+ HandleScope handle_scope(isolate_);
+ Result stack_push = StackPush(object);
+ if (stack_push != SUCCESS) return stack_push;
+ if (object->IsJSGlobalProxy()) {
+ object = Handle<JSObject>(
+ JSObject::cast(object->GetPrototype()), isolate_);
+ ASSERT(object->IsGlobalObject());
+ }
+
+ Append('{');
+ bool comma = false;
+
+ if (object->HasFastProperties() &&
+ !object->HasIndexedInterceptor() &&
+ !object->HasNamedInterceptor() &&
+ object->elements()->length() == 0) {
+ Handle<Map> map(object->map());
+ for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
+ Handle<Name> name(map->instance_descriptors()->GetKey(i), isolate_);
+ // TODO(rossberg): Should this throw?
+ if (!name->IsString()) continue;
+ Handle<String> key = Handle<String>::cast(name);
+ PropertyDetails details = map->instance_descriptors()->GetDetails(i);
+ if (details.IsDontEnum()) continue;
+ Handle<Object> property;
+ if (details.type() == FIELD && *map == object->map()) {
+ property = Handle<Object>(
+ object->RawFastPropertyAt(
+ map->instance_descriptors()->GetFieldIndex(i)),
+ isolate_);
+ } else {
+ property = GetProperty(isolate_, object, key);
+ if (property.is_null()) return EXCEPTION;
+ }
+ Result result = SerializeProperty(property, comma, key);
+ if (!comma && result == SUCCESS) comma = true;
+ if (result >= EXCEPTION) return result;
+ }
+ } else {
+ bool has_exception = false;
+ Handle<FixedArray> contents =
+ GetKeysInFixedArrayFor(object, LOCAL_ONLY, &has_exception);
+ if (has_exception) return EXCEPTION;
+
+ for (int i = 0; i < contents->length(); i++) {
+ Object* key = contents->get(i);
+ Handle<String> key_handle;
+ Handle<Object> property;
+ if (key->IsString()) {
+ key_handle = Handle<String>(String::cast(key), isolate_);
+ property = GetProperty(isolate_, object, key_handle);
+ } else {
+ ASSERT(key->IsNumber());
+ key_handle = factory_->NumberToString(Handle<Object>(key, isolate_));
+ uint32_t index;
+ if (key->IsSmi()) {
+ property = Object::GetElement(object, Smi::cast(key)->value());
+ } else if (key_handle->AsArrayIndex(&index)) {
+ property = Object::GetElement(object, index);
+ } else {
+ property = GetProperty(isolate_, object, key_handle);
+ }
+ }
+ if (property.is_null()) return EXCEPTION;
+ Result result = SerializeProperty(property, comma, key_handle);
+ if (!comma && result == SUCCESS) comma = true;
+ if (result >= EXCEPTION) return result;
+ }
+ }
+
+ Append('}');
+ StackPop();
+ current_part_ = handle_scope.CloseAndEscape(current_part_);
+ return SUCCESS;
+}
+
+
+void BasicJsonStringifier::ShrinkCurrentPart() {
+ ASSERT(current_index_ < part_length_);
+ current_part_ = SeqString::Truncate(Handle<SeqString>::cast(current_part_),
+ current_index_);
+}
+
+
+void BasicJsonStringifier::Extend() {
+ set_accumulator(factory_->NewConsString(accumulator(), current_part_));
+ if (part_length_ <= kMaxPartLength / kPartLengthGrowthFactor) {
+ part_length_ *= kPartLengthGrowthFactor;
+ }
+ if (is_ascii_) {
+ current_part_ = factory_->NewRawOneByteString(part_length_);
+ } else {
+ current_part_ = factory_->NewRawTwoByteString(part_length_);
+ }
+ current_index_ = 0;
+}
+
+
+void BasicJsonStringifier::ChangeEncoding() {
+ ShrinkCurrentPart();
+ set_accumulator(factory_->NewConsString(accumulator(), current_part_));
+ current_part_ = factory_->NewRawTwoByteString(part_length_);
+ current_index_ = 0;
+ is_ascii_ = false;
+}
+
+
+template <typename SrcChar, typename DestChar>
+int BasicJsonStringifier::SerializeStringUnchecked_(const SrcChar* src,
+ DestChar* dest,
+ int length) {
+ DestChar* dest_start = dest;
+
+ // Assert that uc16 character is not truncated down to 8 bit.
+ // The <uc16, char> version of this method must not be called.
+ ASSERT(sizeof(*dest) >= sizeof(*src));
+
+ for (int i = 0; i < length; i++) {
+ SrcChar c = src[i];
+ if (DoNotEscape(c)) {
+ *(dest++) = static_cast<DestChar>(c);
+ } else {
+ const uint8_t* chars = reinterpret_cast<const uint8_t*>(
+ &JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
+ while (*chars != '\0') *(dest++) = *(chars++);
+ }
+ }
+
+ return static_cast<int>(dest - dest_start);
+}
+
+
+template <bool is_ascii, typename Char>
+void BasicJsonStringifier::SerializeString_(Handle<String> string) {
+ int length = string->length();
+ Append_<is_ascii, char>('"');
+ // We make a rough estimate to find out if the current string can be
+ // serialized without allocating a new string part. The worst case length of
+ // an escaped character is 6. Shifting the remainin string length right by 3
+ // is a more pessimistic estimate, but faster to calculate.
+
+ if (((part_length_ - current_index_) >> 3) > length) {
+ DisallowHeapAllocation no_gc;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ if (is_ascii) {
+ current_index_ += SerializeStringUnchecked_(
+ vector.start(),
+ SeqOneByteString::cast(*current_part_)->GetChars() + current_index_,
+ length);
+ } else {
+ current_index_ += SerializeStringUnchecked_(
+ vector.start(),
+ SeqTwoByteString::cast(*current_part_)->GetChars() + current_index_,
+ length);
+ }
+ } else {
+ String* string_location = NULL;
+ Vector<const Char> vector(NULL, 0);
+ for (int i = 0; i < length; i++) {
+ // If GC moved the string, we need to refresh the vector.
+ if (*string != string_location) {
+ DisallowHeapAllocation no_gc;
+ // This does not actually prevent the string from being relocated later.
+ vector = GetCharVector<Char>(string);
+ string_location = *string;
+ }
+ Char c = vector[i];
+ if (DoNotEscape(c)) {
+ Append_<is_ascii, Char>(c);
+ } else {
+ Append_<is_ascii, uint8_t>(reinterpret_cast<const uint8_t*>(
+ &JsonEscapeTable[c * kJsonEscapeTableEntrySize]));
+ }
+ }
+ }
+
+ Append_<is_ascii, uint8_t>('"');
+}
+
+
+template <>
+bool BasicJsonStringifier::DoNotEscape(uint8_t c) {
+ return c >= '#' && c <= '~' && c != '\\';
+}
+
+
+template <>
+bool BasicJsonStringifier::DoNotEscape(uint16_t c) {
+ return c >= '#' && c != '\\' && c != 0x7f;
+}
+
+
+template <>
+Vector<const uint8_t> BasicJsonStringifier::GetCharVector(
+ Handle<String> string) {
+ String::FlatContent flat = string->GetFlatContent();
+ ASSERT(flat.IsAscii());
+ return flat.ToOneByteVector();
+}
+
+
+template <>
+Vector<const uc16> BasicJsonStringifier::GetCharVector(Handle<String> string) {
+ String::FlatContent flat = string->GetFlatContent();
+ ASSERT(flat.IsTwoByte());
+ return flat.ToUC16Vector();
+}
+
+
+void BasicJsonStringifier::SerializeString(Handle<String> object) {
+ object = FlattenGetString(object);
+ if (is_ascii_) {
+ if (object->IsOneByteRepresentation()) {
+ SerializeString_<true, uint8_t>(object);
+ } else {
+ ChangeEncoding();
+ SerializeString(object);
+ }
+ } else {
+ if (object->IsOneByteRepresentation()) {
+ SerializeString_<false, uint8_t>(object);
+ } else {
+ SerializeString_<false, uc16>(object);
+ }
+ }
+}
+
+} } // namespace v8::internal
+
+#endif // V8_JSON_STRINGIFIER_H_
diff --git a/chromium/v8/src/json.js b/chromium/v8/src/json.js
new file mode 100644
index 00000000000..b0e14e1965d
--- /dev/null
+++ b/chromium/v8/src/json.js
@@ -0,0 +1,240 @@
+// 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.
+
+// This file relies on the fact that the following declarations have been made
+// in runtime.js:
+// var $Array = global.Array;
+// var $String = global.String;
+
+var $JSON = global.JSON;
+
+// -------------------------------------------------------------------
+
+function Revive(holder, name, reviver) {
+ var val = holder[name];
+ if (IS_OBJECT(val)) {
+ if (IS_ARRAY(val)) {
+ var length = val.length;
+ for (var i = 0; i < length; i++) {
+ var newElement = Revive(val, $String(i), reviver);
+ val[i] = newElement;
+ }
+ } else {
+ for (var p in val) {
+ if (%_CallFunction(val, p, ObjectHasOwnProperty)) {
+ var newElement = Revive(val, p, reviver);
+ if (IS_UNDEFINED(newElement)) {
+ delete val[p];
+ } else {
+ val[p] = newElement;
+ }
+ }
+ }
+ }
+ }
+ return %_CallFunction(holder, name, val, reviver);
+}
+
+function JSONParse(text, reviver) {
+ var unfiltered = %ParseJson(TO_STRING_INLINE(text));
+ if (IS_SPEC_FUNCTION(reviver)) {
+ return Revive({'': unfiltered}, '', reviver);
+ } else {
+ return unfiltered;
+ }
+}
+
+function SerializeArray(value, replacer, stack, indent, gap) {
+ if (!%PushIfAbsent(stack, value)) {
+ throw MakeTypeError('circular_structure', $Array());
+ }
+ var stepback = indent;
+ indent += gap;
+ var partial = new InternalArray();
+ var len = value.length;
+ for (var i = 0; i < len; i++) {
+ var strP = JSONSerialize($String(i), value, replacer, stack,
+ indent, gap);
+ if (IS_UNDEFINED(strP)) {
+ strP = "null";
+ }
+ partial.push(strP);
+ }
+ var final;
+ if (gap == "") {
+ final = "[" + partial.join(",") + "]";
+ } else if (partial.length > 0) {
+ var separator = ",\n" + indent;
+ final = "[\n" + indent + partial.join(separator) + "\n" +
+ stepback + "]";
+ } else {
+ final = "[]";
+ }
+ stack.pop();
+ return final;
+}
+
+function SerializeObject(value, replacer, stack, indent, gap) {
+ if (!%PushIfAbsent(stack, value)) {
+ throw MakeTypeError('circular_structure', $Array());
+ }
+ var stepback = indent;
+ indent += gap;
+ var partial = new InternalArray();
+ if (IS_ARRAY(replacer)) {
+ var length = replacer.length;
+ for (var i = 0; i < length; i++) {
+ if (%_CallFunction(replacer, i, ObjectHasOwnProperty)) {
+ var p = replacer[i];
+ var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
+ if (!IS_UNDEFINED(strP)) {
+ var member = %QuoteJSONString(p) + ":";
+ if (gap != "") member += " ";
+ member += strP;
+ partial.push(member);
+ }
+ }
+ }
+ } else {
+ for (var p in value) {
+ if (%_CallFunction(value, p, ObjectHasOwnProperty)) {
+ var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
+ if (!IS_UNDEFINED(strP)) {
+ var member = %QuoteJSONString(p) + ":";
+ if (gap != "") member += " ";
+ member += strP;
+ partial.push(member);
+ }
+ }
+ }
+ }
+ var final;
+ if (gap == "") {
+ final = "{" + partial.join(",") + "}";
+ } else if (partial.length > 0) {
+ var separator = ",\n" + indent;
+ final = "{\n" + indent + partial.join(separator) + "\n" +
+ stepback + "}";
+ } else {
+ final = "{}";
+ }
+ stack.pop();
+ return final;
+}
+
+function JSONSerialize(key, holder, replacer, stack, indent, gap) {
+ var value = holder[key];
+ if (IS_SPEC_OBJECT(value)) {
+ var toJSON = value.toJSON;
+ if (IS_SPEC_FUNCTION(toJSON)) {
+ value = %_CallFunction(value, key, toJSON);
+ }
+ }
+ if (IS_SPEC_FUNCTION(replacer)) {
+ value = %_CallFunction(holder, key, value, replacer);
+ }
+ if (IS_STRING(value)) {
+ return %QuoteJSONString(value);
+ } else if (IS_NUMBER(value)) {
+ return JSON_NUMBER_TO_STRING(value);
+ } else if (IS_BOOLEAN(value)) {
+ return value ? "true" : "false";
+ } else if (IS_NULL(value)) {
+ return "null";
+ } else if (IS_SPEC_OBJECT(value) && !(typeof value == "function")) {
+ // Non-callable object. If it's a primitive wrapper, it must be unwrapped.
+ if (IS_ARRAY(value)) {
+ return SerializeArray(value, replacer, stack, indent, gap);
+ } else if (IS_NUMBER_WRAPPER(value)) {
+ value = ToNumber(value);
+ return JSON_NUMBER_TO_STRING(value);
+ } else if (IS_STRING_WRAPPER(value)) {
+ return %QuoteJSONString(ToString(value));
+ } else if (IS_BOOLEAN_WRAPPER(value)) {
+ return %_ValueOf(value) ? "true" : "false";
+ } else {
+ return SerializeObject(value, replacer, stack, indent, gap);
+ }
+ }
+ // Undefined or a callable object.
+ return void 0;
+}
+
+
+function JSONStringify(value, replacer, space) {
+ if (%_ArgumentsLength() == 1) {
+ return %BasicJSONStringify(value);
+ }
+ if (IS_OBJECT(space)) {
+ // Unwrap 'space' if it is wrapped
+ if (IS_NUMBER_WRAPPER(space)) {
+ space = ToNumber(space);
+ } else if (IS_STRING_WRAPPER(space)) {
+ space = ToString(space);
+ }
+ }
+ var gap;
+ if (IS_NUMBER(space)) {
+ space = MathMax(0, MathMin(ToInteger(space), 10));
+ gap = %_SubString(" ", 0, space);
+ } else if (IS_STRING(space)) {
+ if (space.length > 10) {
+ gap = %_SubString(space, 0, 10);
+ } else {
+ gap = space;
+ }
+ } else {
+ gap = "";
+ }
+ return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpJSON() {
+ %CheckIsBootstrapping();
+
+ // Set up non-enumerable properties of the JSON object.
+ InstallFunctions($JSON, DONT_ENUM, $Array(
+ "parse", JSONParse,
+ "stringify", JSONStringify
+ ));
+}
+
+SetUpJSON();
+
+
+// -------------------------------------------------------------------
+// JSON Builtins
+
+function JSONSerializeAdapter(key, object) {
+ var holder = {};
+ holder[key] = object;
+ // No need to pass the actual holder since there is no replacer function.
+ return JSONSerialize(key, holder, void 0, new InternalArray(), "", "");
+}
diff --git a/chromium/v8/src/jsregexp-inl.h b/chromium/v8/src/jsregexp-inl.h
new file mode 100644
index 00000000000..3ef07d8c540
--- /dev/null
+++ b/chromium/v8/src/jsregexp-inl.h
@@ -0,0 +1,106 @@
+// Copyright 2013 the V8 project authors. 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_JSREGEXP_INL_H_
+#define V8_JSREGEXP_INL_H_
+
+#include "allocation.h"
+#include "handles.h"
+#include "heap.h"
+#include "jsregexp.h"
+#include "objects.h"
+
+namespace v8 {
+namespace internal {
+
+
+RegExpImpl::GlobalCache::~GlobalCache() {
+ // Deallocate the register array if we allocated it in the constructor
+ // (as opposed to using the existing jsregexp_static_offsets_vector).
+ if (register_array_size_ > Isolate::kJSRegexpStaticOffsetsVectorSize) {
+ DeleteArray(register_array_);
+ }
+}
+
+
+int32_t* RegExpImpl::GlobalCache::FetchNext() {
+ current_match_index_++;
+ if (current_match_index_ >= num_matches_) {
+ // Current batch of results exhausted.
+ // Fail if last batch was not even fully filled.
+ if (num_matches_ < max_matches_) {
+ num_matches_ = 0; // Signal failed match.
+ return NULL;
+ }
+
+ int32_t* last_match =
+ &register_array_[(current_match_index_ - 1) * registers_per_match_];
+ int last_end_index = last_match[1];
+
+ if (regexp_->TypeTag() == JSRegExp::ATOM) {
+ num_matches_ = RegExpImpl::AtomExecRaw(regexp_,
+ subject_,
+ last_end_index,
+ register_array_,
+ register_array_size_);
+ } else {
+ int last_start_index = last_match[0];
+ if (last_start_index == last_end_index) last_end_index++;
+ if (last_end_index > subject_->length()) {
+ num_matches_ = 0; // Signal failed match.
+ return NULL;
+ }
+ num_matches_ = RegExpImpl::IrregexpExecRaw(regexp_,
+ subject_,
+ last_end_index,
+ register_array_,
+ register_array_size_);
+ }
+
+ if (num_matches_ <= 0) return NULL;
+ current_match_index_ = 0;
+ return register_array_;
+ } else {
+ return &register_array_[current_match_index_ * registers_per_match_];
+ }
+}
+
+
+int32_t* RegExpImpl::GlobalCache::LastSuccessfulMatch() {
+ int index = current_match_index_ * registers_per_match_;
+ if (num_matches_ == 0) {
+ // After a failed match we shift back by one result.
+ index -= registers_per_match_;
+ }
+ return &register_array_[index];
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_JSREGEXP_INL_H_
diff --git a/chromium/v8/src/jsregexp.cc b/chromium/v8/src/jsregexp.cc
new file mode 100644
index 00000000000..666866ed32e
--- /dev/null
+++ b/chromium/v8/src/jsregexp.cc
@@ -0,0 +1,6134 @@
+// Copyright 2012 the V8 project authors. 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 "ast.h"
+#include "compiler.h"
+#include "execution.h"
+#include "factory.h"
+#include "jsregexp.h"
+#include "jsregexp-inl.h"
+#include "platform.h"
+#include "string-search.h"
+#include "runtime.h"
+#include "compilation-cache.h"
+#include "string-stream.h"
+#include "parser.h"
+#include "regexp-macro-assembler.h"
+#include "regexp-macro-assembler-tracer.h"
+#include "regexp-macro-assembler-irregexp.h"
+#include "regexp-stack.h"
+
+#ifndef V8_INTERPRETED_REGEXP
+#if V8_TARGET_ARCH_IA32
+#include "ia32/regexp-macro-assembler-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/regexp-macro-assembler-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/regexp-macro-assembler-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/regexp-macro-assembler-mips.h"
+#else
+#error Unsupported target architecture.
+#endif
+#endif
+
+#include "interpreter-irregexp.h"
+
+
+namespace v8 {
+namespace internal {
+
+Handle<Object> RegExpImpl::CreateRegExpLiteral(Handle<JSFunction> constructor,
+ Handle<String> pattern,
+ Handle<String> flags,
+ bool* has_pending_exception) {
+ // Call the construct code with 2 arguments.
+ Handle<Object> argv[] = { pattern, flags };
+ return Execution::New(constructor, ARRAY_SIZE(argv), argv,
+ has_pending_exception);
+}
+
+
+static JSRegExp::Flags RegExpFlagsFromString(Handle<String> str) {
+ int flags = JSRegExp::NONE;
+ for (int i = 0; i < str->length(); i++) {
+ switch (str->Get(i)) {
+ case 'i':
+ flags |= JSRegExp::IGNORE_CASE;
+ break;
+ case 'g':
+ flags |= JSRegExp::GLOBAL;
+ break;
+ case 'm':
+ flags |= JSRegExp::MULTILINE;
+ break;
+ }
+ }
+ return JSRegExp::Flags(flags);
+}
+
+
+static inline void ThrowRegExpException(Handle<JSRegExp> re,
+ Handle<String> pattern,
+ Handle<String> error_text,
+ const char* message) {
+ Isolate* isolate = re->GetIsolate();
+ Factory* factory = isolate->factory();
+ Handle<FixedArray> elements = factory->NewFixedArray(2);
+ elements->set(0, *pattern);
+ elements->set(1, *error_text);
+ Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
+ Handle<Object> regexp_err = factory->NewSyntaxError(message, array);
+ isolate->Throw(*regexp_err);
+}
+
+
+ContainedInLattice AddRange(ContainedInLattice containment,
+ const int* ranges,
+ int ranges_length,
+ Interval new_range) {
+ ASSERT((ranges_length & 1) == 1);
+ ASSERT(ranges[ranges_length - 1] == String::kMaxUtf16CodeUnit + 1);
+ if (containment == kLatticeUnknown) return containment;
+ bool inside = false;
+ int last = 0;
+ for (int i = 0; i < ranges_length; inside = !inside, last = ranges[i], i++) {
+ // Consider the range from last to ranges[i].
+ // We haven't got to the new range yet.
+ if (ranges[i] <= new_range.from()) continue;
+ // New range is wholly inside last-ranges[i]. Note that new_range.to() is
+ // inclusive, but the values in ranges are not.
+ if (last <= new_range.from() && new_range.to() < ranges[i]) {
+ return Combine(containment, inside ? kLatticeIn : kLatticeOut);
+ }
+ return kLatticeUnknown;
+ }
+ return containment;
+}
+
+
+// More makes code generation slower, less makes V8 benchmark score lower.
+const int kMaxLookaheadForBoyerMoore = 8;
+// In a 3-character pattern you can maximally step forwards 3 characters
+// at a time, which is not always enough to pay for the extra logic.
+const int kPatternTooShortForBoyerMoore = 2;
+
+
+// Identifies the sort of regexps where the regexp engine is faster
+// than the code used for atom matches.
+static bool HasFewDifferentCharacters(Handle<String> pattern) {
+ int length = Min(kMaxLookaheadForBoyerMoore, pattern->length());
+ if (length <= kPatternTooShortForBoyerMoore) return false;
+ const int kMod = 128;
+ bool character_found[kMod];
+ int different = 0;
+ memset(&character_found[0], 0, sizeof(character_found));
+ for (int i = 0; i < length; i++) {
+ int ch = (pattern->Get(i) & (kMod - 1));
+ if (!character_found[ch]) {
+ character_found[ch] = true;
+ different++;
+ // We declare a regexp low-alphabet if it has at least 3 times as many
+ // characters as it has different characters.
+ if (different * 3 > length) return false;
+ }
+ }
+ return true;
+}
+
+
+// Generic RegExp methods. Dispatches to implementation specific methods.
+
+
+Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re,
+ Handle<String> pattern,
+ Handle<String> flag_str) {
+ Isolate* isolate = re->GetIsolate();
+ Zone zone(isolate);
+ JSRegExp::Flags flags = RegExpFlagsFromString(flag_str);
+ CompilationCache* compilation_cache = isolate->compilation_cache();
+ Handle<FixedArray> cached = compilation_cache->LookupRegExp(pattern, flags);
+ bool in_cache = !cached.is_null();
+ LOG(isolate, RegExpCompileEvent(re, in_cache));
+
+ Handle<Object> result;
+ if (in_cache) {
+ re->set_data(*cached);
+ return re;
+ }
+ pattern = FlattenGetString(pattern);
+ PostponeInterruptsScope postpone(isolate);
+ RegExpCompileData parse_result;
+ FlatStringReader reader(isolate, pattern);
+ if (!RegExpParser::ParseRegExp(&reader, flags.is_multiline(),
+ &parse_result, &zone)) {
+ // Throw an exception if we fail to parse the pattern.
+ ThrowRegExpException(re,
+ pattern,
+ parse_result.error,
+ "malformed_regexp");
+ return Handle<Object>::null();
+ }
+
+ bool has_been_compiled = false;
+
+ if (parse_result.simple &&
+ !flags.is_ignore_case() &&
+ !HasFewDifferentCharacters(pattern)) {
+ // Parse-tree is a single atom that is equal to the pattern.
+ AtomCompile(re, pattern, flags, pattern);
+ has_been_compiled = true;
+ } else if (parse_result.tree->IsAtom() &&
+ !flags.is_ignore_case() &&
+ parse_result.capture_count == 0) {
+ RegExpAtom* atom = parse_result.tree->AsAtom();
+ Vector<const uc16> atom_pattern = atom->data();
+ Handle<String> atom_string =
+ isolate->factory()->NewStringFromTwoByte(atom_pattern);
+ if (!HasFewDifferentCharacters(atom_string)) {
+ AtomCompile(re, pattern, flags, atom_string);
+ has_been_compiled = true;
+ }
+ }
+ if (!has_been_compiled) {
+ IrregexpInitialize(re, pattern, flags, parse_result.capture_count);
+ }
+ ASSERT(re->data()->IsFixedArray());
+ // Compilation succeeded so the data is set on the regexp
+ // and we can store it in the cache.
+ Handle<FixedArray> data(FixedArray::cast(re->data()));
+ compilation_cache->PutRegExp(pattern, flags, data);
+
+ return re;
+}
+
+
+Handle<Object> RegExpImpl::Exec(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ Handle<JSArray> last_match_info) {
+ switch (regexp->TypeTag()) {
+ case JSRegExp::ATOM:
+ return AtomExec(regexp, subject, index, last_match_info);
+ case JSRegExp::IRREGEXP: {
+ Handle<Object> result =
+ IrregexpExec(regexp, subject, index, last_match_info);
+ ASSERT(!result.is_null() ||
+ regexp->GetIsolate()->has_pending_exception());
+ return result;
+ }
+ default:
+ UNREACHABLE();
+ return Handle<Object>::null();
+ }
+}
+
+
+// RegExp Atom implementation: Simple string search using indexOf.
+
+
+void RegExpImpl::AtomCompile(Handle<JSRegExp> re,
+ Handle<String> pattern,
+ JSRegExp::Flags flags,
+ Handle<String> match_pattern) {
+ re->GetIsolate()->factory()->SetRegExpAtomData(re,
+ JSRegExp::ATOM,
+ pattern,
+ flags,
+ match_pattern);
+}
+
+
+static void SetAtomLastCapture(FixedArray* array,
+ String* subject,
+ int from,
+ int to) {
+ SealHandleScope shs(array->GetIsolate());
+ RegExpImpl::SetLastCaptureCount(array, 2);
+ RegExpImpl::SetLastSubject(array, subject);
+ RegExpImpl::SetLastInput(array, subject);
+ RegExpImpl::SetCapture(array, 0, from);
+ RegExpImpl::SetCapture(array, 1, to);
+}
+
+
+int RegExpImpl::AtomExecRaw(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ int32_t* output,
+ int output_size) {
+ Isolate* isolate = regexp->GetIsolate();
+
+ ASSERT(0 <= index);
+ ASSERT(index <= subject->length());
+
+ if (!subject->IsFlat()) FlattenString(subject);
+ DisallowHeapAllocation no_gc; // ensure vectors stay valid
+
+ String* needle = String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex));
+ int needle_len = needle->length();
+ ASSERT(needle->IsFlat());
+ ASSERT_LT(0, needle_len);
+
+ if (index + needle_len > subject->length()) {
+ return RegExpImpl::RE_FAILURE;
+ }
+
+ for (int i = 0; i < output_size; i += 2) {
+ String::FlatContent needle_content = needle->GetFlatContent();
+ String::FlatContent subject_content = subject->GetFlatContent();
+ ASSERT(needle_content.IsFlat());
+ ASSERT(subject_content.IsFlat());
+ // dispatch on type of strings
+ index = (needle_content.IsAscii()
+ ? (subject_content.IsAscii()
+ ? SearchString(isolate,
+ subject_content.ToOneByteVector(),
+ needle_content.ToOneByteVector(),
+ index)
+ : SearchString(isolate,
+ subject_content.ToUC16Vector(),
+ needle_content.ToOneByteVector(),
+ index))
+ : (subject_content.IsAscii()
+ ? SearchString(isolate,
+ subject_content.ToOneByteVector(),
+ needle_content.ToUC16Vector(),
+ index)
+ : SearchString(isolate,
+ subject_content.ToUC16Vector(),
+ needle_content.ToUC16Vector(),
+ index)));
+ if (index == -1) {
+ return i / 2; // Return number of matches.
+ } else {
+ output[i] = index;
+ output[i+1] = index + needle_len;
+ index += needle_len;
+ }
+ }
+ return output_size / 2;
+}
+
+
+Handle<Object> RegExpImpl::AtomExec(Handle<JSRegExp> re,
+ Handle<String> subject,
+ int index,
+ Handle<JSArray> last_match_info) {
+ Isolate* isolate = re->GetIsolate();
+
+ static const int kNumRegisters = 2;
+ STATIC_ASSERT(kNumRegisters <= Isolate::kJSRegexpStaticOffsetsVectorSize);
+ int32_t* output_registers = isolate->jsregexp_static_offsets_vector();
+
+ int res = AtomExecRaw(re, subject, index, output_registers, kNumRegisters);
+
+ if (res == RegExpImpl::RE_FAILURE) return isolate->factory()->null_value();
+
+ ASSERT_EQ(res, RegExpImpl::RE_SUCCESS);
+ SealHandleScope shs(isolate);
+ FixedArray* array = FixedArray::cast(last_match_info->elements());
+ SetAtomLastCapture(array, *subject, output_registers[0], output_registers[1]);
+ return last_match_info;
+}
+
+
+// Irregexp implementation.
+
+// Ensures that the regexp object contains a compiled version of the
+// source for either ASCII or non-ASCII strings.
+// If the compiled version doesn't already exist, it is compiled
+// from the source pattern.
+// If compilation fails, an exception is thrown and this function
+// returns false.
+bool RegExpImpl::EnsureCompiledIrregexp(
+ Handle<JSRegExp> re, Handle<String> sample_subject, bool is_ascii) {
+ Object* compiled_code = re->DataAt(JSRegExp::code_index(is_ascii));
+#ifdef V8_INTERPRETED_REGEXP
+ if (compiled_code->IsByteArray()) return true;
+#else // V8_INTERPRETED_REGEXP (RegExp native code)
+ if (compiled_code->IsCode()) return true;
+#endif
+ // We could potentially have marked this as flushable, but have kept
+ // a saved version if we did not flush it yet.
+ Object* saved_code = re->DataAt(JSRegExp::saved_code_index(is_ascii));
+ if (saved_code->IsCode()) {
+ // Reinstate the code in the original place.
+ re->SetDataAt(JSRegExp::code_index(is_ascii), saved_code);
+ ASSERT(compiled_code->IsSmi());
+ return true;
+ }
+ return CompileIrregexp(re, sample_subject, is_ascii);
+}
+
+
+static bool CreateRegExpErrorObjectAndThrow(Handle<JSRegExp> re,
+ bool is_ascii,
+ Handle<String> error_message,
+ Isolate* isolate) {
+ Factory* factory = isolate->factory();
+ Handle<FixedArray> elements = factory->NewFixedArray(2);
+ elements->set(0, re->Pattern());
+ elements->set(1, *error_message);
+ Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
+ Handle<Object> regexp_err =
+ factory->NewSyntaxError("malformed_regexp", array);
+ isolate->Throw(*regexp_err);
+ return false;
+}
+
+
+bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re,
+ Handle<String> sample_subject,
+ bool is_ascii) {
+ // Compile the RegExp.
+ Isolate* isolate = re->GetIsolate();
+ Zone zone(isolate);
+ PostponeInterruptsScope postpone(isolate);
+ // If we had a compilation error the last time this is saved at the
+ // saved code index.
+ Object* entry = re->DataAt(JSRegExp::code_index(is_ascii));
+ // When arriving here entry can only be a smi, either representing an
+ // uncompiled regexp, a previous compilation error, or code that has
+ // been flushed.
+ ASSERT(entry->IsSmi());
+ int entry_value = Smi::cast(entry)->value();
+ ASSERT(entry_value == JSRegExp::kUninitializedValue ||
+ entry_value == JSRegExp::kCompilationErrorValue ||
+ (entry_value < JSRegExp::kCodeAgeMask && entry_value >= 0));
+
+ if (entry_value == JSRegExp::kCompilationErrorValue) {
+ // A previous compilation failed and threw an error which we store in
+ // the saved code index (we store the error message, not the actual
+ // error). Recreate the error object and throw it.
+ Object* error_string = re->DataAt(JSRegExp::saved_code_index(is_ascii));
+ ASSERT(error_string->IsString());
+ Handle<String> error_message(String::cast(error_string));
+ CreateRegExpErrorObjectAndThrow(re, is_ascii, error_message, isolate);
+ return false;
+ }
+
+ JSRegExp::Flags flags = re->GetFlags();
+
+ Handle<String> pattern(re->Pattern());
+ if (!pattern->IsFlat()) FlattenString(pattern);
+ RegExpCompileData compile_data;
+ FlatStringReader reader(isolate, pattern);
+ if (!RegExpParser::ParseRegExp(&reader, flags.is_multiline(),
+ &compile_data,
+ &zone)) {
+ // Throw an exception if we fail to parse the pattern.
+ // THIS SHOULD NOT HAPPEN. We already pre-parsed it successfully once.
+ ThrowRegExpException(re,
+ pattern,
+ compile_data.error,
+ "malformed_regexp");
+ return false;
+ }
+ RegExpEngine::CompilationResult result =
+ RegExpEngine::Compile(&compile_data,
+ flags.is_ignore_case(),
+ flags.is_global(),
+ flags.is_multiline(),
+ pattern,
+ sample_subject,
+ is_ascii,
+ &zone);
+ if (result.error_message != NULL) {
+ // Unable to compile regexp.
+ Handle<String> error_message =
+ isolate->factory()->NewStringFromUtf8(CStrVector(result.error_message));
+ CreateRegExpErrorObjectAndThrow(re, is_ascii, error_message, isolate);
+ return false;
+ }
+
+ Handle<FixedArray> data = Handle<FixedArray>(FixedArray::cast(re->data()));
+ data->set(JSRegExp::code_index(is_ascii), result.code);
+ int register_max = IrregexpMaxRegisterCount(*data);
+ if (result.num_registers > register_max) {
+ SetIrregexpMaxRegisterCount(*data, result.num_registers);
+ }
+
+ return true;
+}
+
+
+int RegExpImpl::IrregexpMaxRegisterCount(FixedArray* re) {
+ return Smi::cast(
+ re->get(JSRegExp::kIrregexpMaxRegisterCountIndex))->value();
+}
+
+
+void RegExpImpl::SetIrregexpMaxRegisterCount(FixedArray* re, int value) {
+ re->set(JSRegExp::kIrregexpMaxRegisterCountIndex, Smi::FromInt(value));
+}
+
+
+int RegExpImpl::IrregexpNumberOfCaptures(FixedArray* re) {
+ return Smi::cast(re->get(JSRegExp::kIrregexpCaptureCountIndex))->value();
+}
+
+
+int RegExpImpl::IrregexpNumberOfRegisters(FixedArray* re) {
+ return Smi::cast(re->get(JSRegExp::kIrregexpMaxRegisterCountIndex))->value();
+}
+
+
+ByteArray* RegExpImpl::IrregexpByteCode(FixedArray* re, bool is_ascii) {
+ return ByteArray::cast(re->get(JSRegExp::code_index(is_ascii)));
+}
+
+
+Code* RegExpImpl::IrregexpNativeCode(FixedArray* re, bool is_ascii) {
+ return Code::cast(re->get(JSRegExp::code_index(is_ascii)));
+}
+
+
+void RegExpImpl::IrregexpInitialize(Handle<JSRegExp> re,
+ Handle<String> pattern,
+ JSRegExp::Flags flags,
+ int capture_count) {
+ // Initialize compiled code entries to null.
+ re->GetIsolate()->factory()->SetRegExpIrregexpData(re,
+ JSRegExp::IRREGEXP,
+ pattern,
+ flags,
+ capture_count);
+}
+
+
+int RegExpImpl::IrregexpPrepare(Handle<JSRegExp> regexp,
+ Handle<String> subject) {
+ if (!subject->IsFlat()) FlattenString(subject);
+
+ // Check the asciiness of the underlying storage.
+ bool is_ascii = subject->IsOneByteRepresentationUnderneath();
+ if (!EnsureCompiledIrregexp(regexp, subject, is_ascii)) return -1;
+
+#ifdef V8_INTERPRETED_REGEXP
+ // Byte-code regexp needs space allocated for all its registers.
+ // The result captures are copied to the start of the registers array
+ // if the match succeeds. This way those registers are not clobbered
+ // when we set the last match info from last successful match.
+ return IrregexpNumberOfRegisters(FixedArray::cast(regexp->data())) +
+ (IrregexpNumberOfCaptures(FixedArray::cast(regexp->data())) + 1) * 2;
+#else // V8_INTERPRETED_REGEXP
+ // Native regexp only needs room to output captures. Registers are handled
+ // internally.
+ return (IrregexpNumberOfCaptures(FixedArray::cast(regexp->data())) + 1) * 2;
+#endif // V8_INTERPRETED_REGEXP
+}
+
+
+int RegExpImpl::IrregexpExecRaw(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ int32_t* output,
+ int output_size) {
+ Isolate* isolate = regexp->GetIsolate();
+
+ Handle<FixedArray> irregexp(FixedArray::cast(regexp->data()), isolate);
+
+ ASSERT(index >= 0);
+ ASSERT(index <= subject->length());
+ ASSERT(subject->IsFlat());
+
+ bool is_ascii = subject->IsOneByteRepresentationUnderneath();
+
+#ifndef V8_INTERPRETED_REGEXP
+ ASSERT(output_size >= (IrregexpNumberOfCaptures(*irregexp) + 1) * 2);
+ do {
+ EnsureCompiledIrregexp(regexp, subject, is_ascii);
+ Handle<Code> code(IrregexpNativeCode(*irregexp, is_ascii), isolate);
+ // The stack is used to allocate registers for the compiled regexp code.
+ // This means that in case of failure, the output registers array is left
+ // untouched and contains the capture results from the previous successful
+ // match. We can use that to set the last match info lazily.
+ NativeRegExpMacroAssembler::Result res =
+ NativeRegExpMacroAssembler::Match(code,
+ subject,
+ output,
+ output_size,
+ index,
+ isolate);
+ if (res != NativeRegExpMacroAssembler::RETRY) {
+ ASSERT(res != NativeRegExpMacroAssembler::EXCEPTION ||
+ isolate->has_pending_exception());
+ STATIC_ASSERT(
+ static_cast<int>(NativeRegExpMacroAssembler::SUCCESS) == RE_SUCCESS);
+ STATIC_ASSERT(
+ static_cast<int>(NativeRegExpMacroAssembler::FAILURE) == RE_FAILURE);
+ STATIC_ASSERT(static_cast<int>(NativeRegExpMacroAssembler::EXCEPTION)
+ == RE_EXCEPTION);
+ return static_cast<IrregexpResult>(res);
+ }
+ // If result is RETRY, the string has changed representation, and we
+ // must restart from scratch.
+ // In this case, it means we must make sure we are prepared to handle
+ // the, potentially, different subject (the string can switch between
+ // being internal and external, and even between being ASCII and UC16,
+ // but the characters are always the same).
+ IrregexpPrepare(regexp, subject);
+ is_ascii = subject->IsOneByteRepresentationUnderneath();
+ } while (true);
+ UNREACHABLE();
+ return RE_EXCEPTION;
+#else // V8_INTERPRETED_REGEXP
+
+ ASSERT(output_size >= IrregexpNumberOfRegisters(*irregexp));
+ // We must have done EnsureCompiledIrregexp, so we can get the number of
+ // registers.
+ int number_of_capture_registers =
+ (IrregexpNumberOfCaptures(*irregexp) + 1) * 2;
+ int32_t* raw_output = &output[number_of_capture_registers];
+ // We do not touch the actual capture result registers until we know there
+ // has been a match so that we can use those capture results to set the
+ // last match info.
+ for (int i = number_of_capture_registers - 1; i >= 0; i--) {
+ raw_output[i] = -1;
+ }
+ Handle<ByteArray> byte_codes(IrregexpByteCode(*irregexp, is_ascii), isolate);
+
+ IrregexpResult result = IrregexpInterpreter::Match(isolate,
+ byte_codes,
+ subject,
+ raw_output,
+ index);
+ if (result == RE_SUCCESS) {
+ // Copy capture results to the start of the registers array.
+ OS::MemCopy(
+ output, raw_output, number_of_capture_registers * sizeof(int32_t));
+ }
+ if (result == RE_EXCEPTION) {
+ ASSERT(!isolate->has_pending_exception());
+ isolate->StackOverflow();
+ }
+ return result;
+#endif // V8_INTERPRETED_REGEXP
+}
+
+
+Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int previous_index,
+ Handle<JSArray> last_match_info) {
+ Isolate* isolate = regexp->GetIsolate();
+ ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
+
+ // Prepare space for the return values.
+#if defined(V8_INTERPRETED_REGEXP) && defined(DEBUG)
+ if (FLAG_trace_regexp_bytecodes) {
+ String* pattern = regexp->Pattern();
+ PrintF("\n\nRegexp match: /%s/\n\n", *(pattern->ToCString()));
+ PrintF("\n\nSubject string: '%s'\n\n", *(subject->ToCString()));
+ }
+#endif
+ int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
+ if (required_registers < 0) {
+ // Compiling failed with an exception.
+ ASSERT(isolate->has_pending_exception());
+ return Handle<Object>::null();
+ }
+
+ int32_t* output_registers = NULL;
+ if (required_registers > Isolate::kJSRegexpStaticOffsetsVectorSize) {
+ output_registers = NewArray<int32_t>(required_registers);
+ }
+ SmartArrayPointer<int32_t> auto_release(output_registers);
+ if (output_registers == NULL) {
+ output_registers = isolate->jsregexp_static_offsets_vector();
+ }
+
+ int res = RegExpImpl::IrregexpExecRaw(
+ regexp, subject, previous_index, output_registers, required_registers);
+ if (res == RE_SUCCESS) {
+ int capture_count =
+ IrregexpNumberOfCaptures(FixedArray::cast(regexp->data()));
+ return SetLastMatchInfo(
+ last_match_info, subject, capture_count, output_registers);
+ }
+ if (res == RE_EXCEPTION) {
+ ASSERT(isolate->has_pending_exception());
+ return Handle<Object>::null();
+ }
+ ASSERT(res == RE_FAILURE);
+ return isolate->factory()->null_value();
+}
+
+
+Handle<JSArray> RegExpImpl::SetLastMatchInfo(Handle<JSArray> last_match_info,
+ Handle<String> subject,
+ int capture_count,
+ int32_t* match) {
+ ASSERT(last_match_info->HasFastObjectElements());
+ int capture_register_count = (capture_count + 1) * 2;
+ last_match_info->EnsureSize(capture_register_count + kLastMatchOverhead);
+ DisallowHeapAllocation no_allocation;
+ FixedArray* array = FixedArray::cast(last_match_info->elements());
+ if (match != NULL) {
+ for (int i = 0; i < capture_register_count; i += 2) {
+ SetCapture(array, i, match[i]);
+ SetCapture(array, i + 1, match[i + 1]);
+ }
+ }
+ SetLastCaptureCount(array, capture_register_count);
+ SetLastSubject(array, *subject);
+ SetLastInput(array, *subject);
+ return last_match_info;
+}
+
+
+RegExpImpl::GlobalCache::GlobalCache(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ bool is_global,
+ Isolate* isolate)
+ : register_array_(NULL),
+ register_array_size_(0),
+ regexp_(regexp),
+ subject_(subject) {
+#ifdef V8_INTERPRETED_REGEXP
+ bool interpreted = true;
+#else
+ bool interpreted = false;
+#endif // V8_INTERPRETED_REGEXP
+
+ if (regexp_->TypeTag() == JSRegExp::ATOM) {
+ static const int kAtomRegistersPerMatch = 2;
+ registers_per_match_ = kAtomRegistersPerMatch;
+ // There is no distinction between interpreted and native for atom regexps.
+ interpreted = false;
+ } else {
+ registers_per_match_ = RegExpImpl::IrregexpPrepare(regexp_, subject_);
+ if (registers_per_match_ < 0) {
+ num_matches_ = -1; // Signal exception.
+ return;
+ }
+ }
+
+ if (is_global && !interpreted) {
+ register_array_size_ =
+ Max(registers_per_match_, Isolate::kJSRegexpStaticOffsetsVectorSize);
+ max_matches_ = register_array_size_ / registers_per_match_;
+ } else {
+ // Global loop in interpreted regexp is not implemented. We choose
+ // the size of the offsets vector so that it can only store one match.
+ register_array_size_ = registers_per_match_;
+ max_matches_ = 1;
+ }
+
+ if (register_array_size_ > Isolate::kJSRegexpStaticOffsetsVectorSize) {
+ register_array_ = NewArray<int32_t>(register_array_size_);
+ } else {
+ register_array_ = isolate->jsregexp_static_offsets_vector();
+ }
+
+ // Set state so that fetching the results the first time triggers a call
+ // to the compiled regexp.
+ current_match_index_ = max_matches_ - 1;
+ num_matches_ = max_matches_;
+ ASSERT(registers_per_match_ >= 2); // Each match has at least one capture.
+ ASSERT_GE(register_array_size_, registers_per_match_);
+ int32_t* last_match =
+ &register_array_[current_match_index_ * registers_per_match_];
+ last_match[0] = -1;
+ last_match[1] = 0;
+}
+
+
+// -------------------------------------------------------------------
+// Implementation of the Irregexp regular expression engine.
+//
+// The Irregexp regular expression engine is intended to be a complete
+// implementation of ECMAScript regular expressions. It generates either
+// bytecodes or native code.
+
+// The Irregexp regexp engine is structured in three steps.
+// 1) The parser generates an abstract syntax tree. See ast.cc.
+// 2) From the AST a node network is created. The nodes are all
+// subclasses of RegExpNode. The nodes represent states when
+// executing a regular expression. Several optimizations are
+// performed on the node network.
+// 3) From the nodes we generate either byte codes or native code
+// that can actually execute the regular expression (perform
+// the search). The code generation step is described in more
+// detail below.
+
+// Code generation.
+//
+// The nodes are divided into four main categories.
+// * Choice nodes
+// These represent places where the regular expression can
+// match in more than one way. For example on entry to an
+// alternation (foo|bar) or a repetition (*, +, ? or {}).
+// * Action nodes
+// These represent places where some action should be
+// performed. Examples include recording the current position
+// in the input string to a register (in order to implement
+// captures) or other actions on register for example in order
+// to implement the counters needed for {} repetitions.
+// * Matching nodes
+// These attempt to match some element part of the input string.
+// Examples of elements include character classes, plain strings
+// or back references.
+// * End nodes
+// These are used to implement the actions required on finding
+// a successful match or failing to find a match.
+//
+// The code generated (whether as byte codes or native code) maintains
+// some state as it runs. This consists of the following elements:
+//
+// * The capture registers. Used for string captures.
+// * Other registers. Used for counters etc.
+// * The current position.
+// * The stack of backtracking information. Used when a matching node
+// fails to find a match and needs to try an alternative.
+//
+// Conceptual regular expression execution model:
+//
+// There is a simple conceptual model of regular expression execution
+// which will be presented first. The actual code generated is a more
+// efficient simulation of the simple conceptual model:
+//
+// * Choice nodes are implemented as follows:
+// For each choice except the last {
+// push current position
+// push backtrack code location
+// <generate code to test for choice>
+// backtrack code location:
+// pop current position
+// }
+// <generate code to test for last choice>
+//
+// * Actions nodes are generated as follows
+// <push affected registers on backtrack stack>
+// <generate code to perform action>
+// push backtrack code location
+// <generate code to test for following nodes>
+// backtrack code location:
+// <pop affected registers to restore their state>
+// <pop backtrack location from stack and go to it>
+//
+// * Matching nodes are generated as follows:
+// if input string matches at current position
+// update current position
+// <generate code to test for following nodes>
+// else
+// <pop backtrack location from stack and go to it>
+//
+// Thus it can be seen that the current position is saved and restored
+// by the choice nodes, whereas the registers are saved and restored by
+// by the action nodes that manipulate them.
+//
+// The other interesting aspect of this model is that nodes are generated
+// at the point where they are needed by a recursive call to Emit(). If
+// the node has already been code generated then the Emit() call will
+// generate a jump to the previously generated code instead. In order to
+// limit recursion it is possible for the Emit() function to put the node
+// on a work list for later generation and instead generate a jump. The
+// destination of the jump is resolved later when the code is generated.
+//
+// Actual regular expression code generation.
+//
+// Code generation is actually more complicated than the above. In order
+// to improve the efficiency of the generated code some optimizations are
+// performed
+//
+// * Choice nodes have 1-character lookahead.
+// A choice node looks at the following character and eliminates some of
+// the choices immediately based on that character. This is not yet
+// implemented.
+// * Simple greedy loops store reduced backtracking information.
+// A quantifier like /.*foo/m will greedily match the whole input. It will
+// then need to backtrack to a point where it can match "foo". The naive
+// implementation of this would push each character position onto the
+// backtracking stack, then pop them off one by one. This would use space
+// proportional to the length of the input string. However since the "."
+// can only match in one way and always has a constant length (in this case
+// of 1) it suffices to store the current position on the top of the stack
+// once. Matching now becomes merely incrementing the current position and
+// backtracking becomes decrementing the current position and checking the
+// result against the stored current position. This is faster and saves
+// space.
+// * The current state is virtualized.
+// This is used to defer expensive operations until it is clear that they
+// are needed and to generate code for a node more than once, allowing
+// specialized an efficient versions of the code to be created. This is
+// explained in the section below.
+//
+// Execution state virtualization.
+//
+// Instead of emitting code, nodes that manipulate the state can record their
+// manipulation in an object called the Trace. The Trace object can record a
+// current position offset, an optional backtrack code location on the top of
+// 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 (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
+// known backtrack code location than it is to pop an unknown backtrack
+// location from the stack and jump there.
+//
+// The virtual state found in the Trace affects code generation. For example
+// the virtual state contains the difference between the actual current
+// position and the virtual current position, and matching code needs to use
+// this offset to attempt a match in the correct location of the input
+// string. Therefore code generated for a non-trivial trace is specialized
+// to that trace. The code generator therefore has the ability to generate
+// code for each node several times. In order to limit the size of the
+// generated code there is an arbitrary limit on how many specialized sets of
+// code may be generated for a given node. If the limit is reached, the
+// trace is flushed and a generic version of the code for a node is emitted.
+// This is subsequently used for that node. The code emitted for non-generic
+// trace is not recorded in the node and so it cannot currently be reused in
+// the event that code generation is requested for an identical trace.
+
+
+void RegExpTree::AppendToText(RegExpText* text, Zone* zone) {
+ UNREACHABLE();
+}
+
+
+void RegExpAtom::AppendToText(RegExpText* text, Zone* zone) {
+ text->AddElement(TextElement::Atom(this), zone);
+}
+
+
+void RegExpCharacterClass::AppendToText(RegExpText* text, Zone* zone) {
+ text->AddElement(TextElement::CharClass(this), zone);
+}
+
+
+void RegExpText::AppendToText(RegExpText* text, Zone* zone) {
+ for (int i = 0; i < elements()->length(); i++)
+ text->AddElement(elements()->at(i), zone);
+}
+
+
+TextElement TextElement::Atom(RegExpAtom* atom) {
+ TextElement result = TextElement(ATOM);
+ result.data.u_atom = atom;
+ return result;
+}
+
+
+TextElement TextElement::CharClass(
+ RegExpCharacterClass* char_class) {
+ TextElement result = TextElement(CHAR_CLASS);
+ result.data.u_char_class = char_class;
+ return result;
+}
+
+
+int TextElement::length() {
+ if (text_type == ATOM) {
+ return data.u_atom->length();
+ } else {
+ ASSERT(text_type == CHAR_CLASS);
+ return 1;
+ }
+}
+
+
+DispatchTable* ChoiceNode::GetTable(bool ignore_case) {
+ if (table_ == NULL) {
+ table_ = new(zone()) DispatchTable(zone());
+ DispatchTableConstructor cons(table_, ignore_case, zone());
+ cons.BuildTable(this);
+ }
+ return table_;
+}
+
+
+class FrequencyCollator {
+ public:
+ FrequencyCollator() : total_samples_(0) {
+ for (int i = 0; i < RegExpMacroAssembler::kTableSize; i++) {
+ frequencies_[i] = CharacterFrequency(i);
+ }
+ }
+
+ void CountCharacter(int character) {
+ int index = (character & RegExpMacroAssembler::kTableMask);
+ frequencies_[index].Increment();
+ total_samples_++;
+ }
+
+ // Does not measure in percent, but rather per-128 (the table size from the
+ // regexp macro assembler).
+ int Frequency(int in_character) {
+ ASSERT((in_character & RegExpMacroAssembler::kTableMask) == in_character);
+ if (total_samples_ < 1) return 1; // Division by zero.
+ int freq_in_per128 =
+ (frequencies_[in_character].counter() * 128) / total_samples_;
+ return freq_in_per128;
+ }
+
+ private:
+ class CharacterFrequency {
+ public:
+ CharacterFrequency() : counter_(0), character_(-1) { }
+ explicit CharacterFrequency(int character)
+ : counter_(0), character_(character) { }
+
+ void Increment() { counter_++; }
+ int counter() { return counter_; }
+ int character() { return character_; }
+
+ private:
+ int counter_;
+ int character_;
+ };
+
+
+ private:
+ CharacterFrequency frequencies_[RegExpMacroAssembler::kTableSize];
+ int total_samples_;
+};
+
+
+class RegExpCompiler {
+ public:
+ RegExpCompiler(int capture_count, bool ignore_case, bool is_ascii,
+ Zone* zone);
+
+ int AllocateRegister() {
+ if (next_register_ >= RegExpMacroAssembler::kMaxRegister) {
+ reg_exp_too_big_ = true;
+ return next_register_;
+ }
+ return next_register_++;
+ }
+
+ RegExpEngine::CompilationResult Assemble(RegExpMacroAssembler* assembler,
+ RegExpNode* start,
+ int capture_count,
+ Handle<String> pattern);
+
+ inline void AddWork(RegExpNode* node) { work_list_->Add(node); }
+
+ static const int kImplementationOffset = 0;
+ static const int kNumberOfRegistersOffset = 0;
+ static const int kCodeOffset = 1;
+
+ RegExpMacroAssembler* macro_assembler() { return macro_assembler_; }
+ EndNode* accept() { return accept_; }
+
+ static const int kMaxRecursion = 100;
+ inline int recursion_depth() { return recursion_depth_; }
+ inline void IncrementRecursionDepth() { recursion_depth_++; }
+ inline void DecrementRecursionDepth() { recursion_depth_--; }
+
+ void SetRegExpTooBig() { reg_exp_too_big_ = true; }
+
+ inline bool ignore_case() { return ignore_case_; }
+ inline bool ascii() { return ascii_; }
+ FrequencyCollator* frequency_collator() { return &frequency_collator_; }
+
+ int current_expansion_factor() { return current_expansion_factor_; }
+ void set_current_expansion_factor(int value) {
+ current_expansion_factor_ = value;
+ }
+
+ Zone* zone() const { return zone_; }
+
+ static const int kNoRegister = -1;
+
+ private:
+ EndNode* accept_;
+ int next_register_;
+ List<RegExpNode*>* work_list_;
+ int recursion_depth_;
+ RegExpMacroAssembler* macro_assembler_;
+ bool ignore_case_;
+ bool ascii_;
+ bool reg_exp_too_big_;
+ int current_expansion_factor_;
+ FrequencyCollator frequency_collator_;
+ Zone* zone_;
+};
+
+
+class RecursionCheck {
+ public:
+ explicit RecursionCheck(RegExpCompiler* compiler) : compiler_(compiler) {
+ compiler->IncrementRecursionDepth();
+ }
+ ~RecursionCheck() { compiler_->DecrementRecursionDepth(); }
+ private:
+ RegExpCompiler* compiler_;
+};
+
+
+static RegExpEngine::CompilationResult IrregexpRegExpTooBig() {
+ return RegExpEngine::CompilationResult("RegExp too big");
+}
+
+
+// Attempts to compile the regexp using an Irregexp code generator. Returns
+// a fixed array or a null handle depending on whether it succeeded.
+RegExpCompiler::RegExpCompiler(int capture_count, bool ignore_case, bool ascii,
+ Zone* zone)
+ : next_register_(2 * (capture_count + 1)),
+ work_list_(NULL),
+ recursion_depth_(0),
+ ignore_case_(ignore_case),
+ ascii_(ascii),
+ reg_exp_too_big_(false),
+ current_expansion_factor_(1),
+ frequency_collator_(),
+ zone_(zone) {
+ accept_ = new(zone) EndNode(EndNode::ACCEPT, zone);
+ ASSERT(next_register_ - 1 <= RegExpMacroAssembler::kMaxRegister);
+}
+
+
+RegExpEngine::CompilationResult RegExpCompiler::Assemble(
+ RegExpMacroAssembler* macro_assembler,
+ RegExpNode* start,
+ int capture_count,
+ Handle<String> pattern) {
+ Heap* heap = pattern->GetHeap();
+
+ bool use_slow_safe_regexp_compiler = false;
+ if (heap->total_regexp_code_generated() >
+ RegExpImpl::kRegWxpCompiledLimit &&
+ heap->isolate()->memory_allocator()->SizeExecutable() >
+ RegExpImpl::kRegExpExecutableMemoryLimit) {
+ use_slow_safe_regexp_compiler = true;
+ }
+
+ macro_assembler->set_slow_safe(use_slow_safe_regexp_compiler);
+
+#ifdef DEBUG
+ if (FLAG_trace_regexp_assembler)
+ macro_assembler_ = new RegExpMacroAssemblerTracer(macro_assembler);
+ else
+#endif
+ macro_assembler_ = macro_assembler;
+
+ List <RegExpNode*> work_list(0);
+ work_list_ = &work_list;
+ Label fail;
+ macro_assembler_->PushBacktrack(&fail);
+ Trace new_trace;
+ start->Emit(this, &new_trace);
+ macro_assembler_->Bind(&fail);
+ macro_assembler_->Fail();
+ while (!work_list.is_empty()) {
+ work_list.RemoveLast()->Emit(this, &new_trace);
+ }
+ if (reg_exp_too_big_) return IrregexpRegExpTooBig();
+
+ Handle<HeapObject> code = macro_assembler_->GetCode(pattern);
+ heap->IncreaseTotalRegexpCodeGenerated(code->Size());
+ work_list_ = NULL;
+#ifdef DEBUG
+ if (FLAG_print_code) {
+ Handle<Code>::cast(code)->Disassemble(*pattern->ToCString());
+ }
+ if (FLAG_trace_regexp_assembler) {
+ delete macro_assembler_;
+ }
+#endif
+ return RegExpEngine::CompilationResult(*code, next_register_);
+}
+
+
+bool Trace::DeferredAction::Mentions(int that) {
+ if (action_type() == ActionNode::CLEAR_CAPTURES) {
+ Interval range = static_cast<DeferredClearCaptures*>(this)->range();
+ return range.Contains(that);
+ } else {
+ return reg() == that;
+ }
+}
+
+
+bool Trace::mentions_reg(int reg) {
+ for (DeferredAction* action = actions_;
+ action != NULL;
+ action = action->next()) {
+ if (action->Mentions(reg))
+ return true;
+ }
+ return false;
+}
+
+
+bool Trace::GetStoredPosition(int reg, int* cp_offset) {
+ ASSERT_EQ(0, *cp_offset);
+ for (DeferredAction* action = actions_;
+ action != NULL;
+ action = action->next()) {
+ if (action->Mentions(reg)) {
+ if (action->action_type() == ActionNode::STORE_POSITION) {
+ *cp_offset = static_cast<DeferredCapture*>(action)->cp_offset();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+
+int Trace::FindAffectedRegisters(OutSet* affected_registers,
+ Zone* zone) {
+ int max_register = RegExpCompiler::kNoRegister;
+ for (DeferredAction* action = actions_;
+ action != NULL;
+ action = action->next()) {
+ if (action->action_type() == ActionNode::CLEAR_CAPTURES) {
+ Interval range = static_cast<DeferredClearCaptures*>(action)->range();
+ for (int i = range.from(); i <= range.to(); i++)
+ affected_registers->Set(i, zone);
+ if (range.to() > max_register) max_register = range.to();
+ } else {
+ affected_registers->Set(action->reg(), zone);
+ if (action->reg() > max_register) max_register = action->reg();
+ }
+ }
+ return max_register;
+}
+
+
+void Trace::RestoreAffectedRegisters(RegExpMacroAssembler* assembler,
+ int max_register,
+ OutSet& registers_to_pop,
+ OutSet& registers_to_clear) {
+ for (int reg = max_register; reg >= 0; reg--) {
+ if (registers_to_pop.Get(reg)) assembler->PopRegister(reg);
+ else if (registers_to_clear.Get(reg)) {
+ int clear_to = reg;
+ while (reg > 0 && registers_to_clear.Get(reg - 1)) {
+ reg--;
+ }
+ assembler->ClearRegisters(reg, clear_to);
+ }
+ }
+}
+
+
+void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
+ int max_register,
+ OutSet& affected_registers,
+ OutSet* registers_to_pop,
+ OutSet* registers_to_clear,
+ Zone* zone) {
+ // The "+1" is to avoid a push_limit of zero if stack_limit_slack() is 1.
+ const int push_limit = (assembler->stack_limit_slack() + 1) / 2;
+
+ // Count pushes performed to force a stack limit check occasionally.
+ int pushes = 0;
+
+ for (int reg = 0; reg <= max_register; reg++) {
+ if (!affected_registers.Get(reg)) {
+ continue;
+ }
+
+ // The chronologically first deferred action in the trace
+ // is used to infer the action needed to restore a register
+ // to its previous state (or not, if it's safe to ignore it).
+ enum DeferredActionUndoType { IGNORE, RESTORE, CLEAR };
+ DeferredActionUndoType undo_action = IGNORE;
+
+ int value = 0;
+ bool absolute = false;
+ bool clear = false;
+ int store_position = -1;
+ // This is a little tricky because we are scanning the actions in reverse
+ // historical order (newest first).
+ for (DeferredAction* action = actions_;
+ action != NULL;
+ action = action->next()) {
+ if (action->Mentions(reg)) {
+ switch (action->action_type()) {
+ case ActionNode::SET_REGISTER: {
+ Trace::DeferredSetRegister* psr =
+ static_cast<Trace::DeferredSetRegister*>(action);
+ if (!absolute) {
+ value += psr->value();
+ absolute = true;
+ }
+ // SET_REGISTER is currently only used for newly introduced loop
+ // counters. They can have a significant previous value if they
+ // occour in a loop. TODO(lrn): Propagate this information, so
+ // we can set undo_action to IGNORE if we know there is no value to
+ // restore.
+ undo_action = RESTORE;
+ ASSERT_EQ(store_position, -1);
+ ASSERT(!clear);
+ break;
+ }
+ case ActionNode::INCREMENT_REGISTER:
+ if (!absolute) {
+ value++;
+ }
+ ASSERT_EQ(store_position, -1);
+ ASSERT(!clear);
+ undo_action = RESTORE;
+ break;
+ case ActionNode::STORE_POSITION: {
+ Trace::DeferredCapture* pc =
+ static_cast<Trace::DeferredCapture*>(action);
+ if (!clear && store_position == -1) {
+ store_position = pc->cp_offset();
+ }
+
+ // For captures we know that stores and clears alternate.
+ // Other register, are never cleared, and if the occur
+ // inside a loop, they might be assigned more than once.
+ if (reg <= 1) {
+ // Registers zero and one, aka "capture zero", is
+ // always set correctly if we succeed. There is no
+ // need to undo a setting on backtrack, because we
+ // will set it again or fail.
+ undo_action = IGNORE;
+ } else {
+ undo_action = pc->is_capture() ? CLEAR : RESTORE;
+ }
+ ASSERT(!absolute);
+ ASSERT_EQ(value, 0);
+ break;
+ }
+ case ActionNode::CLEAR_CAPTURES: {
+ // Since we're scanning in reverse order, if we've already
+ // set the position we have to ignore historically earlier
+ // clearing operations.
+ if (store_position == -1) {
+ clear = true;
+ }
+ undo_action = RESTORE;
+ ASSERT(!absolute);
+ ASSERT_EQ(value, 0);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+ // Prepare for the undo-action (e.g., push if it's going to be popped).
+ if (undo_action == RESTORE) {
+ pushes++;
+ RegExpMacroAssembler::StackCheckFlag stack_check =
+ RegExpMacroAssembler::kNoStackLimitCheck;
+ if (pushes == push_limit) {
+ stack_check = RegExpMacroAssembler::kCheckStackLimit;
+ pushes = 0;
+ }
+
+ assembler->PushRegister(reg, stack_check);
+ registers_to_pop->Set(reg, zone);
+ } else if (undo_action == CLEAR) {
+ registers_to_clear->Set(reg, zone);
+ }
+ // Perform the chronologically last action (or accumulated increment)
+ // for the register.
+ if (store_position != -1) {
+ assembler->WriteCurrentPositionToRegister(reg, store_position);
+ } else if (clear) {
+ assembler->ClearRegisters(reg, reg);
+ } else if (absolute) {
+ assembler->SetRegister(reg, value);
+ } else if (value != 0) {
+ assembler->AdvanceRegister(reg, value);
+ }
+ }
+}
+
+
+// This is called as we come into a loop choice node and some other tricky
+// nodes. It normalizes the state of the code generator to ensure we can
+// generate generic code.
+void Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+
+ ASSERT(!is_trivial());
+
+ if (actions_ == NULL && backtrack() == NULL) {
+ // Here we just have some deferred cp advances to fix and we are back to
+ // a normal situation. We may also have to forget some information gained
+ // through a quick check that was already performed.
+ if (cp_offset_ != 0) assembler->AdvanceCurrentPosition(cp_offset_);
+ // Create a new trivial state and generate the node with that.
+ Trace new_state;
+ successor->Emit(compiler, &new_state);
+ return;
+ }
+
+ // Generate deferred actions here along with code to undo them again.
+ OutSet affected_registers;
+
+ if (backtrack() != NULL) {
+ // Here we have a concrete backtrack location. These are set up by choice
+ // nodes and so they indicate that we have a deferred save of the current
+ // position which we may need to emit here.
+ assembler->PushCurrentPosition();
+ }
+
+ int max_register = FindAffectedRegisters(&affected_registers,
+ compiler->zone());
+ OutSet registers_to_pop;
+ OutSet registers_to_clear;
+ PerformDeferredActions(assembler,
+ max_register,
+ affected_registers,
+ &registers_to_pop,
+ &registers_to_clear,
+ compiler->zone());
+ if (cp_offset_ != 0) {
+ assembler->AdvanceCurrentPosition(cp_offset_);
+ }
+
+ // Create a new trivial state and generate the node with that.
+ Label undo;
+ assembler->PushBacktrack(&undo);
+ Trace new_state;
+ successor->Emit(compiler, &new_state);
+
+ // On backtrack we need to restore state.
+ assembler->Bind(&undo);
+ RestoreAffectedRegisters(assembler,
+ max_register,
+ registers_to_pop,
+ registers_to_clear);
+ if (backtrack() == NULL) {
+ assembler->Backtrack();
+ } else {
+ assembler->PopCurrentPosition();
+ assembler->GoTo(backtrack());
+ }
+}
+
+
+void NegativeSubmatchSuccess::Emit(RegExpCompiler* compiler, Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+
+ // Omit flushing the trace. We discard the entire stack frame anyway.
+
+ if (!label()->is_bound()) {
+ // We are completely independent of the trace, since we ignore it,
+ // so this code can be used as the generic version.
+ assembler->Bind(label());
+ }
+
+ // Throw away everything on the backtrack stack since the start
+ // of the negative submatch and restore the character position.
+ assembler->ReadCurrentPositionFromRegister(current_position_register_);
+ assembler->ReadStackPointerFromRegister(stack_pointer_register_);
+ if (clear_capture_count_ > 0) {
+ // Clear any captures that might have been performed during the success
+ // of the body of the negative look-ahead.
+ int clear_capture_end = clear_capture_start_ + clear_capture_count_ - 1;
+ assembler->ClearRegisters(clear_capture_start_, clear_capture_end);
+ }
+ // Now that we have unwound the stack we find at the top of the stack the
+ // backtrack that the BeginSubmatch node got.
+ assembler->Backtrack();
+}
+
+
+void EndNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ return;
+ }
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ if (!label()->is_bound()) {
+ assembler->Bind(label());
+ }
+ switch (action_) {
+ case ACCEPT:
+ assembler->Succeed();
+ return;
+ case BACKTRACK:
+ assembler->GoTo(trace->backtrack());
+ return;
+ case NEGATIVE_SUBMATCH_SUCCESS:
+ // This case is handled in a different virtual method.
+ UNREACHABLE();
+ }
+ UNIMPLEMENTED();
+}
+
+
+void GuardedAlternative::AddGuard(Guard* guard, Zone* zone) {
+ if (guards_ == NULL)
+ guards_ = new(zone) ZoneList<Guard*>(1, zone);
+ guards_->Add(guard, zone);
+}
+
+
+ActionNode* ActionNode::SetRegister(int reg,
+ int val,
+ RegExpNode* on_success) {
+ ActionNode* result =
+ new(on_success->zone()) ActionNode(SET_REGISTER, on_success);
+ result->data_.u_store_register.reg = reg;
+ result->data_.u_store_register.value = val;
+ return result;
+}
+
+
+ActionNode* ActionNode::IncrementRegister(int reg, RegExpNode* on_success) {
+ ActionNode* result =
+ new(on_success->zone()) ActionNode(INCREMENT_REGISTER, on_success);
+ result->data_.u_increment_register.reg = reg;
+ return result;
+}
+
+
+ActionNode* ActionNode::StorePosition(int reg,
+ bool is_capture,
+ RegExpNode* on_success) {
+ ActionNode* result =
+ new(on_success->zone()) ActionNode(STORE_POSITION, on_success);
+ result->data_.u_position_register.reg = reg;
+ result->data_.u_position_register.is_capture = is_capture;
+ return result;
+}
+
+
+ActionNode* ActionNode::ClearCaptures(Interval range,
+ RegExpNode* on_success) {
+ ActionNode* result =
+ new(on_success->zone()) ActionNode(CLEAR_CAPTURES, on_success);
+ result->data_.u_clear_captures.range_from = range.from();
+ result->data_.u_clear_captures.range_to = range.to();
+ return result;
+}
+
+
+ActionNode* ActionNode::BeginSubmatch(int stack_reg,
+ int position_reg,
+ RegExpNode* on_success) {
+ ActionNode* result =
+ new(on_success->zone()) ActionNode(BEGIN_SUBMATCH, on_success);
+ result->data_.u_submatch.stack_pointer_register = stack_reg;
+ result->data_.u_submatch.current_position_register = position_reg;
+ return result;
+}
+
+
+ActionNode* ActionNode::PositiveSubmatchSuccess(int stack_reg,
+ int position_reg,
+ int clear_register_count,
+ int clear_register_from,
+ RegExpNode* on_success) {
+ ActionNode* result =
+ new(on_success->zone()) ActionNode(POSITIVE_SUBMATCH_SUCCESS, on_success);
+ result->data_.u_submatch.stack_pointer_register = stack_reg;
+ result->data_.u_submatch.current_position_register = position_reg;
+ result->data_.u_submatch.clear_register_count = clear_register_count;
+ result->data_.u_submatch.clear_register_from = clear_register_from;
+ return result;
+}
+
+
+ActionNode* ActionNode::EmptyMatchCheck(int start_register,
+ int repetition_register,
+ int repetition_limit,
+ RegExpNode* on_success) {
+ ActionNode* result =
+ new(on_success->zone()) ActionNode(EMPTY_MATCH_CHECK, on_success);
+ result->data_.u_empty_match_check.start_register = start_register;
+ result->data_.u_empty_match_check.repetition_register = repetition_register;
+ result->data_.u_empty_match_check.repetition_limit = repetition_limit;
+ return result;
+}
+
+
+#define DEFINE_ACCEPT(Type) \
+ void Type##Node::Accept(NodeVisitor* visitor) { \
+ visitor->Visit##Type(this); \
+ }
+FOR_EACH_NODE_TYPE(DEFINE_ACCEPT)
+#undef DEFINE_ACCEPT
+
+
+void LoopChoiceNode::Accept(NodeVisitor* visitor) {
+ visitor->VisitLoopChoice(this);
+}
+
+
+// -------------------------------------------------------------------
+// Emit code.
+
+
+void ChoiceNode::GenerateGuard(RegExpMacroAssembler* macro_assembler,
+ Guard* guard,
+ Trace* trace) {
+ switch (guard->op()) {
+ case Guard::LT:
+ ASSERT(!trace->mentions_reg(guard->reg()));
+ macro_assembler->IfRegisterGE(guard->reg(),
+ guard->value(),
+ trace->backtrack());
+ break;
+ case Guard::GEQ:
+ ASSERT(!trace->mentions_reg(guard->reg()));
+ macro_assembler->IfRegisterLT(guard->reg(),
+ guard->value(),
+ trace->backtrack());
+ break;
+ }
+}
+
+
+// Returns the number of characters in the equivalence class, omitting those
+// that cannot occur in the source string because it is ASCII.
+static int GetCaseIndependentLetters(Isolate* isolate,
+ uc16 character,
+ bool ascii_subject,
+ unibrow::uchar* letters) {
+ int length =
+ isolate->jsregexp_uncanonicalize()->get(character, '\0', letters);
+ // Unibrow returns 0 or 1 for characters where case independence is
+ // trivial.
+ if (length == 0) {
+ letters[0] = character;
+ length = 1;
+ }
+ if (!ascii_subject || character <= String::kMaxOneByteCharCode) {
+ return length;
+ }
+ // The standard requires that non-ASCII characters cannot have ASCII
+ // character codes in their equivalence class.
+ return 0;
+}
+
+
+static inline bool EmitSimpleCharacter(Isolate* isolate,
+ RegExpCompiler* compiler,
+ uc16 c,
+ Label* on_failure,
+ int cp_offset,
+ bool check,
+ bool preloaded) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ bool bound_checked = false;
+ if (!preloaded) {
+ assembler->LoadCurrentCharacter(
+ cp_offset,
+ on_failure,
+ check);
+ bound_checked = true;
+ }
+ assembler->CheckNotCharacter(c, on_failure);
+ return bound_checked;
+}
+
+
+// Only emits non-letters (things that don't have case). Only used for case
+// independent matches.
+static inline bool EmitAtomNonLetter(Isolate* isolate,
+ RegExpCompiler* compiler,
+ uc16 c,
+ Label* on_failure,
+ int cp_offset,
+ bool check,
+ bool preloaded) {
+ RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
+ bool ascii = compiler->ascii();
+ unibrow::uchar chars[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int length = GetCaseIndependentLetters(isolate, c, ascii, chars);
+ if (length < 1) {
+ // This can't match. Must be an ASCII subject and a non-ASCII character.
+ // We do not need to do anything since the ASCII pass already handled this.
+ return false; // Bounds not checked.
+ }
+ bool checked = false;
+ // We handle the length > 1 case in a later pass.
+ if (length == 1) {
+ if (ascii && c > String::kMaxOneByteCharCodeU) {
+ // Can't match - see above.
+ return false; // Bounds not checked.
+ }
+ if (!preloaded) {
+ macro_assembler->LoadCurrentCharacter(cp_offset, on_failure, check);
+ checked = check;
+ }
+ macro_assembler->CheckNotCharacter(c, on_failure);
+ }
+ return checked;
+}
+
+
+static bool ShortCutEmitCharacterPair(RegExpMacroAssembler* macro_assembler,
+ bool ascii,
+ uc16 c1,
+ uc16 c2,
+ Label* on_failure) {
+ uc16 char_mask;
+ if (ascii) {
+ char_mask = String::kMaxOneByteCharCode;
+ } else {
+ char_mask = String::kMaxUtf16CodeUnit;
+ }
+ uc16 exor = c1 ^ c2;
+ // Check whether exor has only one bit set.
+ if (((exor - 1) & exor) == 0) {
+ // If c1 and c2 differ only by one bit.
+ // Ecma262UnCanonicalize always gives the highest number last.
+ ASSERT(c2 > c1);
+ uc16 mask = char_mask ^ exor;
+ macro_assembler->CheckNotCharacterAfterAnd(c1, mask, on_failure);
+ return true;
+ }
+ ASSERT(c2 > c1);
+ uc16 diff = c2 - c1;
+ if (((diff - 1) & diff) == 0 && c1 >= diff) {
+ // If the characters differ by 2^n but don't differ by one bit then
+ // subtract the difference from the found character, then do the or
+ // trick. We avoid the theoretical case where negative numbers are
+ // involved in order to simplify code generation.
+ uc16 mask = char_mask ^ diff;
+ macro_assembler->CheckNotCharacterAfterMinusAnd(c1 - diff,
+ diff,
+ mask,
+ on_failure);
+ return true;
+ }
+ return false;
+}
+
+
+typedef bool EmitCharacterFunction(Isolate* isolate,
+ RegExpCompiler* compiler,
+ uc16 c,
+ Label* on_failure,
+ int cp_offset,
+ bool check,
+ bool preloaded);
+
+// Only emits letters (things that have case). Only used for case independent
+// matches.
+static inline bool EmitAtomLetter(Isolate* isolate,
+ RegExpCompiler* compiler,
+ uc16 c,
+ Label* on_failure,
+ int cp_offset,
+ bool check,
+ bool preloaded) {
+ RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
+ bool ascii = compiler->ascii();
+ unibrow::uchar chars[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int length = GetCaseIndependentLetters(isolate, c, ascii, chars);
+ if (length <= 1) return false;
+ // We may not need to check against the end of the input string
+ // if this character lies before a character that matched.
+ if (!preloaded) {
+ macro_assembler->LoadCurrentCharacter(cp_offset, on_failure, check);
+ }
+ Label ok;
+ ASSERT(unibrow::Ecma262UnCanonicalize::kMaxWidth == 4);
+ switch (length) {
+ case 2: {
+ if (ShortCutEmitCharacterPair(macro_assembler,
+ ascii,
+ chars[0],
+ chars[1],
+ on_failure)) {
+ } else {
+ macro_assembler->CheckCharacter(chars[0], &ok);
+ macro_assembler->CheckNotCharacter(chars[1], on_failure);
+ macro_assembler->Bind(&ok);
+ }
+ break;
+ }
+ case 4:
+ macro_assembler->CheckCharacter(chars[3], &ok);
+ // Fall through!
+ case 3:
+ macro_assembler->CheckCharacter(chars[0], &ok);
+ macro_assembler->CheckCharacter(chars[1], &ok);
+ macro_assembler->CheckNotCharacter(chars[2], on_failure);
+ macro_assembler->Bind(&ok);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ return true;
+}
+
+
+static void EmitBoundaryTest(RegExpMacroAssembler* masm,
+ int border,
+ Label* fall_through,
+ Label* above_or_equal,
+ Label* below) {
+ if (below != fall_through) {
+ masm->CheckCharacterLT(border, below);
+ if (above_or_equal != fall_through) masm->GoTo(above_or_equal);
+ } else {
+ masm->CheckCharacterGT(border - 1, above_or_equal);
+ }
+}
+
+
+static void EmitDoubleBoundaryTest(RegExpMacroAssembler* masm,
+ int first,
+ int last,
+ Label* fall_through,
+ Label* in_range,
+ Label* out_of_range) {
+ if (in_range == fall_through) {
+ if (first == last) {
+ masm->CheckNotCharacter(first, out_of_range);
+ } else {
+ masm->CheckCharacterNotInRange(first, last, out_of_range);
+ }
+ } else {
+ if (first == last) {
+ masm->CheckCharacter(first, in_range);
+ } else {
+ masm->CheckCharacterInRange(first, last, in_range);
+ }
+ if (out_of_range != fall_through) masm->GoTo(out_of_range);
+ }
+}
+
+
+// even_label is for ranges[i] to ranges[i + 1] where i - start_index is even.
+// odd_label is for ranges[i] to ranges[i + 1] where i - start_index is odd.
+static void EmitUseLookupTable(
+ RegExpMacroAssembler* masm,
+ ZoneList<int>* ranges,
+ int start_index,
+ int end_index,
+ int min_char,
+ Label* fall_through,
+ Label* even_label,
+ Label* odd_label) {
+ static const int kSize = RegExpMacroAssembler::kTableSize;
+ static const int kMask = RegExpMacroAssembler::kTableMask;
+
+ int base = (min_char & ~kMask);
+ USE(base);
+
+ // Assert that everything is on one kTableSize page.
+ for (int i = start_index; i <= end_index; i++) {
+ ASSERT_EQ(ranges->at(i) & ~kMask, base);
+ }
+ ASSERT(start_index == 0 || (ranges->at(start_index - 1) & ~kMask) <= base);
+
+ char templ[kSize];
+ Label* on_bit_set;
+ Label* on_bit_clear;
+ int bit;
+ if (even_label == fall_through) {
+ on_bit_set = odd_label;
+ on_bit_clear = even_label;
+ bit = 1;
+ } else {
+ on_bit_set = even_label;
+ on_bit_clear = odd_label;
+ bit = 0;
+ }
+ for (int i = 0; i < (ranges->at(start_index) & kMask) && i < kSize; i++) {
+ templ[i] = bit;
+ }
+ int j = 0;
+ bit ^= 1;
+ for (int i = start_index; i < end_index; i++) {
+ for (j = (ranges->at(i) & kMask); j < (ranges->at(i + 1) & kMask); j++) {
+ templ[j] = bit;
+ }
+ bit ^= 1;
+ }
+ for (int i = j; i < kSize; i++) {
+ templ[i] = bit;
+ }
+ Factory* factory = Isolate::Current()->factory();
+ // TODO(erikcorry): Cache these.
+ Handle<ByteArray> ba = factory->NewByteArray(kSize, TENURED);
+ for (int i = 0; i < kSize; i++) {
+ ba->set(i, templ[i]);
+ }
+ masm->CheckBitInTable(ba, on_bit_set);
+ if (on_bit_clear != fall_through) masm->GoTo(on_bit_clear);
+}
+
+
+static void CutOutRange(RegExpMacroAssembler* masm,
+ ZoneList<int>* ranges,
+ int start_index,
+ int end_index,
+ int cut_index,
+ Label* even_label,
+ Label* odd_label) {
+ bool odd = (((cut_index - start_index) & 1) == 1);
+ Label* in_range_label = odd ? odd_label : even_label;
+ Label dummy;
+ EmitDoubleBoundaryTest(masm,
+ ranges->at(cut_index),
+ ranges->at(cut_index + 1) - 1,
+ &dummy,
+ in_range_label,
+ &dummy);
+ ASSERT(!dummy.is_linked());
+ // Cut out the single range by rewriting the array. This creates a new
+ // range that is a merger of the two ranges on either side of the one we
+ // are cutting out. The oddity of the labels is preserved.
+ for (int j = cut_index; j > start_index; j--) {
+ ranges->at(j) = ranges->at(j - 1);
+ }
+ for (int j = cut_index + 1; j < end_index; j++) {
+ ranges->at(j) = ranges->at(j + 1);
+ }
+}
+
+
+// Unicode case. Split the search space into kSize spaces that are handled
+// with recursion.
+static void SplitSearchSpace(ZoneList<int>* ranges,
+ int start_index,
+ int end_index,
+ int* new_start_index,
+ int* new_end_index,
+ int* border) {
+ static const int kSize = RegExpMacroAssembler::kTableSize;
+ static const int kMask = RegExpMacroAssembler::kTableMask;
+
+ int first = ranges->at(start_index);
+ int last = ranges->at(end_index) - 1;
+
+ *new_start_index = start_index;
+ *border = (ranges->at(start_index) & ~kMask) + kSize;
+ while (*new_start_index < end_index) {
+ if (ranges->at(*new_start_index) > *border) break;
+ (*new_start_index)++;
+ }
+ // new_start_index is the index of the first edge that is beyond the
+ // current kSize space.
+
+ // For very large search spaces we do a binary chop search of the non-ASCII
+ // space instead of just going to the end of the current kSize space. The
+ // heuristics are complicated a little by the fact that any 128-character
+ // encoding space can be quickly tested with a table lookup, so we don't
+ // wish to do binary chop search at a smaller granularity than that. A
+ // 128-character space can take up a lot of space in the ranges array if,
+ // for example, we only want to match every second character (eg. the lower
+ // case characters on some Unicode pages).
+ int binary_chop_index = (end_index + start_index) / 2;
+ // The first test ensures that we get to the code that handles the ASCII
+ // range with a single not-taken branch, speeding up this important
+ // character range (even non-ASCII charset-based text has spaces and
+ // punctuation).
+ if (*border - 1 > String::kMaxOneByteCharCode && // ASCII case.
+ end_index - start_index > (*new_start_index - start_index) * 2 &&
+ last - first > kSize * 2 &&
+ binary_chop_index > *new_start_index &&
+ ranges->at(binary_chop_index) >= first + 2 * kSize) {
+ int scan_forward_for_section_border = binary_chop_index;;
+ int new_border = (ranges->at(binary_chop_index) | kMask) + 1;
+
+ while (scan_forward_for_section_border < end_index) {
+ if (ranges->at(scan_forward_for_section_border) > new_border) {
+ *new_start_index = scan_forward_for_section_border;
+ *border = new_border;
+ break;
+ }
+ scan_forward_for_section_border++;
+ }
+ }
+
+ ASSERT(*new_start_index > start_index);
+ *new_end_index = *new_start_index - 1;
+ if (ranges->at(*new_end_index) == *border) {
+ (*new_end_index)--;
+ }
+ if (*border >= ranges->at(end_index)) {
+ *border = ranges->at(end_index);
+ *new_start_index = end_index; // Won't be used.
+ *new_end_index = end_index - 1;
+ }
+}
+
+
+// Gets a series of segment boundaries representing a character class. If the
+// character is in the range between an even and an odd boundary (counting from
+// start_index) then go to even_label, otherwise go to odd_label. We already
+// know that the character is in the range of min_char to max_char inclusive.
+// Either label can be NULL indicating backtracking. Either label can also be
+// equal to the fall_through label.
+static void GenerateBranches(RegExpMacroAssembler* masm,
+ ZoneList<int>* ranges,
+ int start_index,
+ int end_index,
+ uc16 min_char,
+ uc16 max_char,
+ Label* fall_through,
+ Label* even_label,
+ Label* odd_label) {
+ int first = ranges->at(start_index);
+ int last = ranges->at(end_index) - 1;
+
+ ASSERT_LT(min_char, first);
+
+ // Just need to test if the character is before or on-or-after
+ // a particular character.
+ if (start_index == end_index) {
+ EmitBoundaryTest(masm, first, fall_through, even_label, odd_label);
+ return;
+ }
+
+ // Another almost trivial case: There is one interval in the middle that is
+ // different from the end intervals.
+ if (start_index + 1 == end_index) {
+ EmitDoubleBoundaryTest(
+ masm, first, last, fall_through, even_label, odd_label);
+ return;
+ }
+
+ // It's not worth using table lookup if there are very few intervals in the
+ // character class.
+ if (end_index - start_index <= 6) {
+ // It is faster to test for individual characters, so we look for those
+ // first, then try arbitrary ranges in the second round.
+ static int kNoCutIndex = -1;
+ int cut = kNoCutIndex;
+ for (int i = start_index; i < end_index; i++) {
+ if (ranges->at(i) == ranges->at(i + 1) - 1) {
+ cut = i;
+ break;
+ }
+ }
+ if (cut == kNoCutIndex) cut = start_index;
+ CutOutRange(
+ masm, ranges, start_index, end_index, cut, even_label, odd_label);
+ ASSERT_GE(end_index - start_index, 2);
+ GenerateBranches(masm,
+ ranges,
+ start_index + 1,
+ end_index - 1,
+ min_char,
+ max_char,
+ fall_through,
+ even_label,
+ odd_label);
+ return;
+ }
+
+ // If there are a lot of intervals in the regexp, then we will use tables to
+ // determine whether the character is inside or outside the character class.
+ static const int kBits = RegExpMacroAssembler::kTableSizeBits;
+
+ if ((max_char >> kBits) == (min_char >> kBits)) {
+ EmitUseLookupTable(masm,
+ ranges,
+ start_index,
+ end_index,
+ min_char,
+ fall_through,
+ even_label,
+ odd_label);
+ return;
+ }
+
+ if ((min_char >> kBits) != (first >> kBits)) {
+ masm->CheckCharacterLT(first, odd_label);
+ GenerateBranches(masm,
+ ranges,
+ start_index + 1,
+ end_index,
+ first,
+ max_char,
+ fall_through,
+ odd_label,
+ even_label);
+ return;
+ }
+
+ int new_start_index = 0;
+ int new_end_index = 0;
+ int border = 0;
+
+ SplitSearchSpace(ranges,
+ start_index,
+ end_index,
+ &new_start_index,
+ &new_end_index,
+ &border);
+
+ Label handle_rest;
+ Label* above = &handle_rest;
+ if (border == last + 1) {
+ // We didn't find any section that started after the limit, so everything
+ // above the border is one of the terminal labels.
+ above = (end_index & 1) != (start_index & 1) ? odd_label : even_label;
+ ASSERT(new_end_index == end_index - 1);
+ }
+
+ ASSERT_LE(start_index, new_end_index);
+ ASSERT_LE(new_start_index, end_index);
+ ASSERT_LT(start_index, new_start_index);
+ ASSERT_LT(new_end_index, end_index);
+ ASSERT(new_end_index + 1 == new_start_index ||
+ (new_end_index + 2 == new_start_index &&
+ border == ranges->at(new_end_index + 1)));
+ ASSERT_LT(min_char, border - 1);
+ ASSERT_LT(border, max_char);
+ ASSERT_LT(ranges->at(new_end_index), border);
+ ASSERT(border < ranges->at(new_start_index) ||
+ (border == ranges->at(new_start_index) &&
+ new_start_index == end_index &&
+ new_end_index == end_index - 1 &&
+ border == last + 1));
+ ASSERT(new_start_index == 0 || border >= ranges->at(new_start_index - 1));
+
+ masm->CheckCharacterGT(border - 1, above);
+ Label dummy;
+ GenerateBranches(masm,
+ ranges,
+ start_index,
+ new_end_index,
+ min_char,
+ border - 1,
+ &dummy,
+ even_label,
+ odd_label);
+ if (handle_rest.is_linked()) {
+ masm->Bind(&handle_rest);
+ bool flip = (new_start_index & 1) != (start_index & 1);
+ GenerateBranches(masm,
+ ranges,
+ new_start_index,
+ end_index,
+ border,
+ max_char,
+ &dummy,
+ flip ? odd_label : even_label,
+ flip ? even_label : odd_label);
+ }
+}
+
+
+static void EmitCharClass(RegExpMacroAssembler* macro_assembler,
+ RegExpCharacterClass* cc,
+ bool ascii,
+ Label* on_failure,
+ int cp_offset,
+ bool check_offset,
+ bool preloaded,
+ Zone* zone) {
+ ZoneList<CharacterRange>* ranges = cc->ranges(zone);
+ if (!CharacterRange::IsCanonical(ranges)) {
+ CharacterRange::Canonicalize(ranges);
+ }
+
+ int max_char;
+ if (ascii) {
+ max_char = String::kMaxOneByteCharCode;
+ } else {
+ max_char = String::kMaxUtf16CodeUnit;
+ }
+
+ int range_count = ranges->length();
+
+ int last_valid_range = range_count - 1;
+ while (last_valid_range >= 0) {
+ CharacterRange& range = ranges->at(last_valid_range);
+ if (range.from() <= max_char) {
+ break;
+ }
+ last_valid_range--;
+ }
+
+ if (last_valid_range < 0) {
+ if (!cc->is_negated()) {
+ macro_assembler->GoTo(on_failure);
+ }
+ if (check_offset) {
+ macro_assembler->CheckPosition(cp_offset, on_failure);
+ }
+ return;
+ }
+
+ if (last_valid_range == 0 &&
+ ranges->at(0).IsEverything(max_char)) {
+ if (cc->is_negated()) {
+ macro_assembler->GoTo(on_failure);
+ } else {
+ // This is a common case hit by non-anchored expressions.
+ if (check_offset) {
+ macro_assembler->CheckPosition(cp_offset, on_failure);
+ }
+ }
+ return;
+ }
+ if (last_valid_range == 0 &&
+ !cc->is_negated() &&
+ ranges->at(0).IsEverything(max_char)) {
+ // This is a common case hit by non-anchored expressions.
+ if (check_offset) {
+ macro_assembler->CheckPosition(cp_offset, on_failure);
+ }
+ return;
+ }
+
+ if (!preloaded) {
+ macro_assembler->LoadCurrentCharacter(cp_offset, on_failure, check_offset);
+ }
+
+ if (cc->is_standard(zone) &&
+ macro_assembler->CheckSpecialCharacterClass(cc->standard_type(),
+ on_failure)) {
+ return;
+ }
+
+
+ // A new list with ascending entries. Each entry is a code unit
+ // where there is a boundary between code units that are part of
+ // the class and code units that are not. Normally we insert an
+ // entry at zero which goes to the failure label, but if there
+ // was already one there we fall through for success on that entry.
+ // Subsequent entries have alternating meaning (success/failure).
+ ZoneList<int>* range_boundaries =
+ new(zone) ZoneList<int>(last_valid_range, zone);
+
+ bool zeroth_entry_is_failure = !cc->is_negated();
+
+ for (int i = 0; i <= last_valid_range; i++) {
+ CharacterRange& range = ranges->at(i);
+ if (range.from() == 0) {
+ ASSERT_EQ(i, 0);
+ zeroth_entry_is_failure = !zeroth_entry_is_failure;
+ } else {
+ range_boundaries->Add(range.from(), zone);
+ }
+ range_boundaries->Add(range.to() + 1, zone);
+ }
+ int end_index = range_boundaries->length() - 1;
+ if (range_boundaries->at(end_index) > max_char) {
+ end_index--;
+ }
+
+ Label fall_through;
+ GenerateBranches(macro_assembler,
+ range_boundaries,
+ 0, // start_index.
+ end_index,
+ 0, // min_char.
+ max_char,
+ &fall_through,
+ zeroth_entry_is_failure ? &fall_through : on_failure,
+ zeroth_entry_is_failure ? on_failure : &fall_through);
+ macro_assembler->Bind(&fall_through);
+}
+
+
+RegExpNode::~RegExpNode() {
+}
+
+
+RegExpNode::LimitResult RegExpNode::LimitVersions(RegExpCompiler* compiler,
+ Trace* trace) {
+ // If we are generating a greedy loop then don't stop and don't reuse code.
+ if (trace->stop_node() != NULL) {
+ return CONTINUE;
+ }
+
+ RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
+ if (trace->is_trivial()) {
+ if (label_.is_bound()) {
+ // We are being asked to generate a generic version, but that's already
+ // been done so just go to it.
+ macro_assembler->GoTo(&label_);
+ return DONE;
+ }
+ if (compiler->recursion_depth() >= RegExpCompiler::kMaxRecursion) {
+ // To avoid too deep recursion we push the node to the work queue and just
+ // generate a goto here.
+ compiler->AddWork(this);
+ macro_assembler->GoTo(&label_);
+ return DONE;
+ }
+ // Generate generic version of the node and bind the label for later use.
+ macro_assembler->Bind(&label_);
+ return CONTINUE;
+ }
+
+ // We are being asked to make a non-generic version. Keep track of how many
+ // non-generic versions we generate so as not to overdo it.
+ trace_count_++;
+ if (FLAG_regexp_optimization &&
+ trace_count_ < kMaxCopiesCodeGenerated &&
+ compiler->recursion_depth() <= RegExpCompiler::kMaxRecursion) {
+ return CONTINUE;
+ }
+
+ // If we get here code has been generated for this node too many times or
+ // recursion is too deep. Time to switch to a generic version. The code for
+ // generic versions above can handle deep recursion properly.
+ trace->Flush(compiler, this);
+ return DONE;
+}
+
+
+int ActionNode::EatsAtLeast(int still_to_find,
+ int budget,
+ bool not_at_start) {
+ if (budget <= 0) return 0;
+ if (action_type_ == POSITIVE_SUBMATCH_SUCCESS) return 0; // Rewinds input!
+ return on_success()->EatsAtLeast(still_to_find,
+ budget - 1,
+ not_at_start);
+}
+
+
+void ActionNode::FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ if (action_type_ == BEGIN_SUBMATCH) {
+ bm->SetRest(offset);
+ } else if (action_type_ != POSITIVE_SUBMATCH_SUCCESS) {
+ on_success()->FillInBMInfo(offset, budget - 1, bm, not_at_start);
+ }
+ SaveBMInfo(bm, not_at_start, offset);
+}
+
+
+int AssertionNode::EatsAtLeast(int still_to_find,
+ int budget,
+ bool not_at_start) {
+ if (budget <= 0) return 0;
+ // If we know we are not at the start and we are asked "how many characters
+ // will you match if you succeed?" then we can answer anything since false
+ // implies false. So lets just return the max answer (still_to_find) since
+ // that won't prevent us from preloading a lot of characters for the other
+ // branches in the node graph.
+ if (assertion_type() == AT_START && not_at_start) return still_to_find;
+ return on_success()->EatsAtLeast(still_to_find,
+ budget - 1,
+ not_at_start);
+}
+
+
+void AssertionNode::FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ // Match the behaviour of EatsAtLeast on this node.
+ if (assertion_type() == AT_START && not_at_start) return;
+ on_success()->FillInBMInfo(offset, budget - 1, bm, not_at_start);
+ SaveBMInfo(bm, not_at_start, offset);
+}
+
+
+int BackReferenceNode::EatsAtLeast(int still_to_find,
+ int budget,
+ bool not_at_start) {
+ if (budget <= 0) return 0;
+ return on_success()->EatsAtLeast(still_to_find,
+ budget - 1,
+ not_at_start);
+}
+
+
+int TextNode::EatsAtLeast(int still_to_find,
+ int budget,
+ bool not_at_start) {
+ int answer = Length();
+ if (answer >= still_to_find) return answer;
+ if (budget <= 0) return answer;
+ // We are not at start after this node so we set the last argument to 'true'.
+ return answer + on_success()->EatsAtLeast(still_to_find - answer,
+ budget - 1,
+ true);
+}
+
+
+int NegativeLookaheadChoiceNode::EatsAtLeast(int still_to_find,
+ int budget,
+ bool not_at_start) {
+ if (budget <= 0) return 0;
+ // Alternative 0 is the negative lookahead, alternative 1 is what comes
+ // afterwards.
+ RegExpNode* node = alternatives_->at(1).node();
+ return node->EatsAtLeast(still_to_find, budget - 1, not_at_start);
+}
+
+
+void NegativeLookaheadChoiceNode::GetQuickCheckDetails(
+ QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int filled_in,
+ bool not_at_start) {
+ // Alternative 0 is the negative lookahead, alternative 1 is what comes
+ // afterwards.
+ RegExpNode* node = alternatives_->at(1).node();
+ return node->GetQuickCheckDetails(details, compiler, filled_in, not_at_start);
+}
+
+
+int ChoiceNode::EatsAtLeastHelper(int still_to_find,
+ int budget,
+ RegExpNode* ignore_this_node,
+ bool not_at_start) {
+ if (budget <= 0) return 0;
+ int min = 100;
+ int choice_count = alternatives_->length();
+ budget = (budget - 1) / choice_count;
+ for (int i = 0; i < choice_count; i++) {
+ RegExpNode* node = alternatives_->at(i).node();
+ if (node == ignore_this_node) continue;
+ int node_eats_at_least =
+ node->EatsAtLeast(still_to_find, budget, not_at_start);
+ if (node_eats_at_least < min) min = node_eats_at_least;
+ if (min == 0) return 0;
+ }
+ return min;
+}
+
+
+int LoopChoiceNode::EatsAtLeast(int still_to_find,
+ int budget,
+ bool not_at_start) {
+ return EatsAtLeastHelper(still_to_find,
+ budget - 1,
+ loop_node_,
+ not_at_start);
+}
+
+
+int ChoiceNode::EatsAtLeast(int still_to_find,
+ int budget,
+ bool not_at_start) {
+ return EatsAtLeastHelper(still_to_find,
+ budget,
+ NULL,
+ not_at_start);
+}
+
+
+// Takes the left-most 1-bit and smears it out, setting all bits to its right.
+static inline uint32_t SmearBitsRight(uint32_t v) {
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return v;
+}
+
+
+bool QuickCheckDetails::Rationalize(bool asc) {
+ bool found_useful_op = false;
+ uint32_t char_mask;
+ if (asc) {
+ char_mask = String::kMaxOneByteCharCode;
+ } else {
+ char_mask = String::kMaxUtf16CodeUnit;
+ }
+ mask_ = 0;
+ value_ = 0;
+ int char_shift = 0;
+ for (int i = 0; i < characters_; i++) {
+ Position* pos = &positions_[i];
+ if ((pos->mask & String::kMaxOneByteCharCode) != 0) {
+ found_useful_op = true;
+ }
+ mask_ |= (pos->mask & char_mask) << char_shift;
+ value_ |= (pos->value & char_mask) << char_shift;
+ char_shift += asc ? 8 : 16;
+ }
+ return found_useful_op;
+}
+
+
+bool RegExpNode::EmitQuickCheck(RegExpCompiler* compiler,
+ Trace* trace,
+ bool preload_has_checked_bounds,
+ Label* on_possible_success,
+ QuickCheckDetails* details,
+ bool fall_through_on_failure) {
+ if (details->characters() == 0) return false;
+ GetQuickCheckDetails(
+ details, compiler, 0, trace->at_start() == Trace::FALSE_VALUE);
+ if (details->cannot_match()) return false;
+ if (!details->Rationalize(compiler->ascii())) return false;
+ ASSERT(details->characters() == 1 ||
+ compiler->macro_assembler()->CanReadUnaligned());
+ uint32_t mask = details->mask();
+ uint32_t value = details->value();
+
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+
+ if (trace->characters_preloaded() != details->characters()) {
+ assembler->LoadCurrentCharacter(trace->cp_offset(),
+ trace->backtrack(),
+ !preload_has_checked_bounds,
+ details->characters());
+ }
+
+
+ bool need_mask = true;
+
+ if (details->characters() == 1) {
+ // If number of characters preloaded is 1 then we used a byte or 16 bit
+ // load so the value is already masked down.
+ uint32_t char_mask;
+ if (compiler->ascii()) {
+ char_mask = String::kMaxOneByteCharCode;
+ } else {
+ char_mask = String::kMaxUtf16CodeUnit;
+ }
+ if ((mask & char_mask) == char_mask) need_mask = false;
+ mask &= char_mask;
+ } else {
+ // For 2-character preloads in ASCII mode or 1-character preloads in
+ // TWO_BYTE mode we also use a 16 bit load with zero extend.
+ if (details->characters() == 2 && compiler->ascii()) {
+ if ((mask & 0xffff) == 0xffff) need_mask = false;
+ } else if (details->characters() == 1 && !compiler->ascii()) {
+ if ((mask & 0xffff) == 0xffff) need_mask = false;
+ } else {
+ if (mask == 0xffffffff) need_mask = false;
+ }
+ }
+
+ if (fall_through_on_failure) {
+ if (need_mask) {
+ assembler->CheckCharacterAfterAnd(value, mask, on_possible_success);
+ } else {
+ assembler->CheckCharacter(value, on_possible_success);
+ }
+ } else {
+ if (need_mask) {
+ assembler->CheckNotCharacterAfterAnd(value, mask, trace->backtrack());
+ } else {
+ assembler->CheckNotCharacter(value, trace->backtrack());
+ }
+ }
+ return true;
+}
+
+
+// Here is the meat of GetQuickCheckDetails (see also the comment on the
+// super-class in the .h file).
+//
+// We iterate along the text object, building up for each character a
+// mask and value that can be used to test for a quick failure to match.
+// The masks and values for the positions will be combined into a single
+// machine word for the current character width in order to be used in
+// generating a quick check.
+void TextNode::GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start) {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(characters_filled_in < details->characters());
+ int characters = details->characters();
+ int char_mask;
+ if (compiler->ascii()) {
+ char_mask = String::kMaxOneByteCharCode;
+ } else {
+ char_mask = String::kMaxUtf16CodeUnit;
+ }
+ for (int k = 0; k < elms_->length(); k++) {
+ TextElement elm = elms_->at(k);
+ if (elm.text_type == TextElement::ATOM) {
+ Vector<const uc16> quarks = elm.data.u_atom->data();
+ for (int i = 0; i < characters && i < quarks.length(); i++) {
+ QuickCheckDetails::Position* pos =
+ details->positions(characters_filled_in);
+ uc16 c = quarks[i];
+ if (c > char_mask) {
+ // If we expect a non-ASCII character from an ASCII string,
+ // there is no way we can match. Not even case independent
+ // matching can turn an ASCII character into non-ASCII or
+ // vice versa.
+ details->set_cannot_match();
+ pos->determines_perfectly = false;
+ return;
+ }
+ if (compiler->ignore_case()) {
+ unibrow::uchar chars[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int length = GetCaseIndependentLetters(isolate, c, compiler->ascii(),
+ chars);
+ ASSERT(length != 0); // Can only happen if c > char_mask (see above).
+ if (length == 1) {
+ // This letter has no case equivalents, so it's nice and simple
+ // and the mask-compare will determine definitely whether we have
+ // a match at this character position.
+ pos->mask = char_mask;
+ pos->value = c;
+ pos->determines_perfectly = true;
+ } else {
+ uint32_t common_bits = char_mask;
+ uint32_t bits = chars[0];
+ for (int j = 1; j < length; j++) {
+ uint32_t differing_bits = ((chars[j] & common_bits) ^ bits);
+ common_bits ^= differing_bits;
+ bits &= common_bits;
+ }
+ // If length is 2 and common bits has only one zero in it then
+ // our mask and compare instruction will determine definitely
+ // whether we have a match at this character position. Otherwise
+ // it can only be an approximate check.
+ uint32_t one_zero = (common_bits | ~char_mask);
+ if (length == 2 && ((~one_zero) & ((~one_zero) - 1)) == 0) {
+ pos->determines_perfectly = true;
+ }
+ pos->mask = common_bits;
+ pos->value = bits;
+ }
+ } else {
+ // Don't ignore case. Nice simple case where the mask-compare will
+ // determine definitely whether we have a match at this character
+ // position.
+ pos->mask = char_mask;
+ pos->value = c;
+ pos->determines_perfectly = true;
+ }
+ characters_filled_in++;
+ ASSERT(characters_filled_in <= details->characters());
+ if (characters_filled_in == details->characters()) {
+ return;
+ }
+ }
+ } else {
+ QuickCheckDetails::Position* pos =
+ details->positions(characters_filled_in);
+ RegExpCharacterClass* tree = elm.data.u_char_class;
+ ZoneList<CharacterRange>* ranges = tree->ranges(zone());
+ if (tree->is_negated()) {
+ // A quick check uses multi-character mask and compare. There is no
+ // useful way to incorporate a negative char class into this scheme
+ // so we just conservatively create a mask and value that will always
+ // succeed.
+ pos->mask = 0;
+ pos->value = 0;
+ } else {
+ int first_range = 0;
+ while (ranges->at(first_range).from() > char_mask) {
+ first_range++;
+ if (first_range == ranges->length()) {
+ details->set_cannot_match();
+ pos->determines_perfectly = false;
+ return;
+ }
+ }
+ CharacterRange range = ranges->at(first_range);
+ uc16 from = range.from();
+ uc16 to = range.to();
+ if (to > char_mask) {
+ to = char_mask;
+ }
+ uint32_t differing_bits = (from ^ to);
+ // A mask and compare is only perfect if the differing bits form a
+ // number like 00011111 with one single block of trailing 1s.
+ if ((differing_bits & (differing_bits + 1)) == 0 &&
+ from + differing_bits == to) {
+ pos->determines_perfectly = true;
+ }
+ uint32_t common_bits = ~SmearBitsRight(differing_bits);
+ uint32_t bits = (from & common_bits);
+ for (int i = first_range + 1; i < ranges->length(); i++) {
+ CharacterRange range = ranges->at(i);
+ uc16 from = range.from();
+ uc16 to = range.to();
+ if (from > char_mask) continue;
+ if (to > char_mask) to = char_mask;
+ // Here we are combining more ranges into the mask and compare
+ // value. With each new range the mask becomes more sparse and
+ // so the chances of a false positive rise. A character class
+ // with multiple ranges is assumed never to be equivalent to a
+ // mask and compare operation.
+ pos->determines_perfectly = false;
+ uint32_t new_common_bits = (from ^ to);
+ new_common_bits = ~SmearBitsRight(new_common_bits);
+ common_bits &= new_common_bits;
+ bits &= new_common_bits;
+ uint32_t differing_bits = (from & common_bits) ^ bits;
+ common_bits ^= differing_bits;
+ bits &= common_bits;
+ }
+ pos->mask = common_bits;
+ pos->value = bits;
+ }
+ characters_filled_in++;
+ ASSERT(characters_filled_in <= details->characters());
+ if (characters_filled_in == details->characters()) {
+ return;
+ }
+ }
+ }
+ ASSERT(characters_filled_in != details->characters());
+ if (!details->cannot_match()) {
+ on_success()-> GetQuickCheckDetails(details,
+ compiler,
+ characters_filled_in,
+ true);
+ }
+}
+
+
+void QuickCheckDetails::Clear() {
+ for (int i = 0; i < characters_; i++) {
+ positions_[i].mask = 0;
+ positions_[i].value = 0;
+ positions_[i].determines_perfectly = false;
+ }
+ characters_ = 0;
+}
+
+
+void QuickCheckDetails::Advance(int by, bool ascii) {
+ ASSERT(by >= 0);
+ if (by >= characters_) {
+ Clear();
+ return;
+ }
+ for (int i = 0; i < characters_ - by; i++) {
+ positions_[i] = positions_[by + i];
+ }
+ for (int i = characters_ - by; i < characters_; i++) {
+ positions_[i].mask = 0;
+ positions_[i].value = 0;
+ positions_[i].determines_perfectly = false;
+ }
+ characters_ -= by;
+ // We could change mask_ and value_ here but we would never advance unless
+ // they had already been used in a check and they won't be used again because
+ // it would gain us nothing. So there's no point.
+}
+
+
+void QuickCheckDetails::Merge(QuickCheckDetails* other, int from_index) {
+ ASSERT(characters_ == other->characters_);
+ if (other->cannot_match_) {
+ return;
+ }
+ if (cannot_match_) {
+ *this = *other;
+ return;
+ }
+ for (int i = from_index; i < characters_; i++) {
+ QuickCheckDetails::Position* pos = positions(i);
+ QuickCheckDetails::Position* other_pos = other->positions(i);
+ if (pos->mask != other_pos->mask ||
+ pos->value != other_pos->value ||
+ !other_pos->determines_perfectly) {
+ // Our mask-compare operation will be approximate unless we have the
+ // exact same operation on both sides of the alternation.
+ pos->determines_perfectly = false;
+ }
+ pos->mask &= other_pos->mask;
+ pos->value &= pos->mask;
+ other_pos->value &= pos->mask;
+ uc16 differing_bits = (pos->value ^ other_pos->value);
+ pos->mask &= ~differing_bits;
+ pos->value &= pos->mask;
+ }
+}
+
+
+class VisitMarker {
+ public:
+ explicit VisitMarker(NodeInfo* info) : info_(info) {
+ ASSERT(!info->visited);
+ info->visited = true;
+ }
+ ~VisitMarker() {
+ info_->visited = false;
+ }
+ private:
+ NodeInfo* info_;
+};
+
+
+RegExpNode* SeqRegExpNode::FilterASCII(int depth, bool ignore_case) {
+ if (info()->replacement_calculated) return replacement();
+ if (depth < 0) return this;
+ ASSERT(!info()->visited);
+ VisitMarker marker(info());
+ return FilterSuccessor(depth - 1, ignore_case);
+}
+
+
+RegExpNode* SeqRegExpNode::FilterSuccessor(int depth, bool ignore_case) {
+ RegExpNode* next = on_success_->FilterASCII(depth - 1, ignore_case);
+ if (next == NULL) return set_replacement(NULL);
+ on_success_ = next;
+ return set_replacement(this);
+}
+
+
+// We need to check for the following characters: 0x39c 0x3bc 0x178.
+static inline bool RangeContainsLatin1Equivalents(CharacterRange range) {
+ // TODO(dcarney): this could be a lot more efficient.
+ return range.Contains(0x39c) ||
+ range.Contains(0x3bc) || range.Contains(0x178);
+}
+
+
+static bool RangesContainLatin1Equivalents(ZoneList<CharacterRange>* ranges) {
+ for (int i = 0; i < ranges->length(); i++) {
+ // TODO(dcarney): this could be a lot more efficient.
+ if (RangeContainsLatin1Equivalents(ranges->at(i))) return true;
+ }
+ return false;
+}
+
+
+RegExpNode* TextNode::FilterASCII(int depth, bool ignore_case) {
+ if (info()->replacement_calculated) return replacement();
+ if (depth < 0) return this;
+ ASSERT(!info()->visited);
+ VisitMarker marker(info());
+ int element_count = elms_->length();
+ for (int i = 0; i < element_count; i++) {
+ TextElement elm = elms_->at(i);
+ if (elm.text_type == TextElement::ATOM) {
+ Vector<const uc16> quarks = elm.data.u_atom->data();
+ for (int j = 0; j < quarks.length(); j++) {
+ uint16_t c = quarks[j];
+ if (c <= String::kMaxOneByteCharCode) continue;
+ if (!ignore_case) return set_replacement(NULL);
+ // Here, we need to check for characters whose upper and lower cases
+ // are outside the Latin-1 range.
+ uint16_t converted = unibrow::Latin1::ConvertNonLatin1ToLatin1(c);
+ // Character is outside Latin-1 completely
+ if (converted == 0) return set_replacement(NULL);
+ // Convert quark to Latin-1 in place.
+ uint16_t* copy = const_cast<uint16_t*>(quarks.start());
+ copy[j] = converted;
+ }
+ } else {
+ ASSERT(elm.text_type == TextElement::CHAR_CLASS);
+ RegExpCharacterClass* cc = elm.data.u_char_class;
+ ZoneList<CharacterRange>* ranges = cc->ranges(zone());
+ if (!CharacterRange::IsCanonical(ranges)) {
+ CharacterRange::Canonicalize(ranges);
+ }
+ // Now they are in order so we only need to look at the first.
+ int range_count = ranges->length();
+ if (cc->is_negated()) {
+ if (range_count != 0 &&
+ ranges->at(0).from() == 0 &&
+ ranges->at(0).to() >= String::kMaxOneByteCharCode) {
+ // This will be handled in a later filter.
+ if (ignore_case && RangesContainLatin1Equivalents(ranges)) continue;
+ return set_replacement(NULL);
+ }
+ } else {
+ if (range_count == 0 ||
+ ranges->at(0).from() > String::kMaxOneByteCharCode) {
+ // This will be handled in a later filter.
+ if (ignore_case && RangesContainLatin1Equivalents(ranges)) continue;
+ return set_replacement(NULL);
+ }
+ }
+ }
+ }
+ return FilterSuccessor(depth - 1, ignore_case);
+}
+
+
+RegExpNode* LoopChoiceNode::FilterASCII(int depth, bool ignore_case) {
+ if (info()->replacement_calculated) return replacement();
+ if (depth < 0) return this;
+ if (info()->visited) return this;
+ {
+ VisitMarker marker(info());
+
+ RegExpNode* continue_replacement =
+ continue_node_->FilterASCII(depth - 1, ignore_case);
+ // If we can't continue after the loop then there is no sense in doing the
+ // loop.
+ if (continue_replacement == NULL) return set_replacement(NULL);
+ }
+
+ return ChoiceNode::FilterASCII(depth - 1, ignore_case);
+}
+
+
+RegExpNode* ChoiceNode::FilterASCII(int depth, bool ignore_case) {
+ if (info()->replacement_calculated) return replacement();
+ if (depth < 0) return this;
+ if (info()->visited) return this;
+ VisitMarker marker(info());
+ int choice_count = alternatives_->length();
+
+ for (int i = 0; i < choice_count; i++) {
+ GuardedAlternative alternative = alternatives_->at(i);
+ if (alternative.guards() != NULL && alternative.guards()->length() != 0) {
+ set_replacement(this);
+ return this;
+ }
+ }
+
+ int surviving = 0;
+ RegExpNode* survivor = NULL;
+ for (int i = 0; i < choice_count; i++) {
+ GuardedAlternative alternative = alternatives_->at(i);
+ RegExpNode* replacement =
+ alternative.node()->FilterASCII(depth - 1, ignore_case);
+ ASSERT(replacement != this); // No missing EMPTY_MATCH_CHECK.
+ if (replacement != NULL) {
+ alternatives_->at(i).set_node(replacement);
+ surviving++;
+ survivor = replacement;
+ }
+ }
+ if (surviving < 2) return set_replacement(survivor);
+
+ set_replacement(this);
+ if (surviving == choice_count) {
+ return this;
+ }
+ // Only some of the nodes survived the filtering. We need to rebuild the
+ // alternatives list.
+ ZoneList<GuardedAlternative>* new_alternatives =
+ new(zone()) ZoneList<GuardedAlternative>(surviving, zone());
+ for (int i = 0; i < choice_count; i++) {
+ RegExpNode* replacement =
+ alternatives_->at(i).node()->FilterASCII(depth - 1, ignore_case);
+ if (replacement != NULL) {
+ alternatives_->at(i).set_node(replacement);
+ new_alternatives->Add(alternatives_->at(i), zone());
+ }
+ }
+ alternatives_ = new_alternatives;
+ return this;
+}
+
+
+RegExpNode* NegativeLookaheadChoiceNode::FilterASCII(int depth,
+ bool ignore_case) {
+ if (info()->replacement_calculated) return replacement();
+ if (depth < 0) return this;
+ if (info()->visited) return this;
+ VisitMarker marker(info());
+ // Alternative 0 is the negative lookahead, alternative 1 is what comes
+ // afterwards.
+ RegExpNode* node = alternatives_->at(1).node();
+ RegExpNode* replacement = node->FilterASCII(depth - 1, ignore_case);
+ if (replacement == NULL) return set_replacement(NULL);
+ alternatives_->at(1).set_node(replacement);
+
+ RegExpNode* neg_node = alternatives_->at(0).node();
+ RegExpNode* neg_replacement = neg_node->FilterASCII(depth - 1, ignore_case);
+ // If the negative lookahead is always going to fail then
+ // we don't need to check it.
+ if (neg_replacement == NULL) return set_replacement(replacement);
+ alternatives_->at(0).set_node(neg_replacement);
+ return set_replacement(this);
+}
+
+
+void LoopChoiceNode::GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start) {
+ if (body_can_be_zero_length_ || info()->visited) return;
+ VisitMarker marker(info());
+ return ChoiceNode::GetQuickCheckDetails(details,
+ compiler,
+ characters_filled_in,
+ not_at_start);
+}
+
+
+void LoopChoiceNode::FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ if (body_can_be_zero_length_ || budget <= 0) {
+ bm->SetRest(offset);
+ SaveBMInfo(bm, not_at_start, offset);
+ return;
+ }
+ ChoiceNode::FillInBMInfo(offset, budget - 1, bm, not_at_start);
+ SaveBMInfo(bm, not_at_start, offset);
+}
+
+
+void ChoiceNode::GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start) {
+ not_at_start = (not_at_start || not_at_start_);
+ int choice_count = alternatives_->length();
+ ASSERT(choice_count > 0);
+ alternatives_->at(0).node()->GetQuickCheckDetails(details,
+ compiler,
+ characters_filled_in,
+ not_at_start);
+ for (int i = 1; i < choice_count; i++) {
+ QuickCheckDetails new_details(details->characters());
+ RegExpNode* node = alternatives_->at(i).node();
+ node->GetQuickCheckDetails(&new_details, compiler,
+ characters_filled_in,
+ not_at_start);
+ // Here we merge the quick match details of the two branches.
+ details->Merge(&new_details, characters_filled_in);
+ }
+}
+
+
+// Check for [0-9A-Z_a-z].
+static void EmitWordCheck(RegExpMacroAssembler* assembler,
+ Label* word,
+ Label* non_word,
+ bool fall_through_on_word) {
+ if (assembler->CheckSpecialCharacterClass(
+ fall_through_on_word ? 'w' : 'W',
+ fall_through_on_word ? non_word : word)) {
+ // Optimized implementation available.
+ return;
+ }
+ assembler->CheckCharacterGT('z', non_word);
+ assembler->CheckCharacterLT('0', non_word);
+ assembler->CheckCharacterGT('a' - 1, word);
+ assembler->CheckCharacterLT('9' + 1, word);
+ assembler->CheckCharacterLT('A', non_word);
+ assembler->CheckCharacterLT('Z' + 1, word);
+ if (fall_through_on_word) {
+ assembler->CheckNotCharacter('_', non_word);
+ } else {
+ assembler->CheckCharacter('_', word);
+ }
+}
+
+
+// Emit the code to check for a ^ in multiline mode (1-character lookbehind
+// that matches newline or the start of input).
+static void EmitHat(RegExpCompiler* compiler,
+ RegExpNode* on_success,
+ Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ // We will be loading the previous character into the current character
+ // register.
+ Trace new_trace(*trace);
+ new_trace.InvalidateCurrentCharacter();
+
+ Label ok;
+ if (new_trace.cp_offset() == 0) {
+ // The start of input counts as a newline in this context, so skip to
+ // ok if we are at the start.
+ assembler->CheckAtStart(&ok);
+ }
+ // We already checked that we are not at the start of input so it must be
+ // OK to load the previous character.
+ assembler->LoadCurrentCharacter(new_trace.cp_offset() -1,
+ new_trace.backtrack(),
+ false);
+ if (!assembler->CheckSpecialCharacterClass('n',
+ new_trace.backtrack())) {
+ // Newline means \n, \r, 0x2028 or 0x2029.
+ if (!compiler->ascii()) {
+ assembler->CheckCharacterAfterAnd(0x2028, 0xfffe, &ok);
+ }
+ assembler->CheckCharacter('\n', &ok);
+ assembler->CheckNotCharacter('\r', new_trace.backtrack());
+ }
+ assembler->Bind(&ok);
+ on_success->Emit(compiler, &new_trace);
+}
+
+
+// Emit the code to handle \b and \B (word-boundary or non-word-boundary).
+void AssertionNode::EmitBoundaryCheck(RegExpCompiler* compiler, Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ Trace::TriBool next_is_word_character = Trace::UNKNOWN;
+ bool not_at_start = (trace->at_start() == Trace::FALSE_VALUE);
+ BoyerMooreLookahead* lookahead = bm_info(not_at_start);
+ if (lookahead == NULL) {
+ int eats_at_least =
+ Min(kMaxLookaheadForBoyerMoore, EatsAtLeast(kMaxLookaheadForBoyerMoore,
+ kRecursionBudget,
+ not_at_start));
+ if (eats_at_least >= 1) {
+ BoyerMooreLookahead* bm =
+ new(zone()) BoyerMooreLookahead(eats_at_least, compiler, zone());
+ FillInBMInfo(0, kRecursionBudget, bm, not_at_start);
+ if (bm->at(0)->is_non_word())
+ next_is_word_character = Trace::FALSE_VALUE;
+ if (bm->at(0)->is_word()) next_is_word_character = Trace::TRUE_VALUE;
+ }
+ } else {
+ if (lookahead->at(0)->is_non_word())
+ next_is_word_character = Trace::FALSE_VALUE;
+ if (lookahead->at(0)->is_word())
+ next_is_word_character = Trace::TRUE_VALUE;
+ }
+ bool at_boundary = (assertion_type_ == AssertionNode::AT_BOUNDARY);
+ if (next_is_word_character == Trace::UNKNOWN) {
+ Label before_non_word;
+ Label before_word;
+ if (trace->characters_preloaded() != 1) {
+ assembler->LoadCurrentCharacter(trace->cp_offset(), &before_non_word);
+ }
+ // Fall through on non-word.
+ EmitWordCheck(assembler, &before_word, &before_non_word, false);
+ // Next character is not a word character.
+ assembler->Bind(&before_non_word);
+ Label ok;
+ BacktrackIfPrevious(compiler, trace, at_boundary ? kIsNonWord : kIsWord);
+ assembler->GoTo(&ok);
+
+ assembler->Bind(&before_word);
+ BacktrackIfPrevious(compiler, trace, at_boundary ? kIsWord : kIsNonWord);
+ assembler->Bind(&ok);
+ } else if (next_is_word_character == Trace::TRUE_VALUE) {
+ BacktrackIfPrevious(compiler, trace, at_boundary ? kIsWord : kIsNonWord);
+ } else {
+ ASSERT(next_is_word_character == Trace::FALSE_VALUE);
+ BacktrackIfPrevious(compiler, trace, at_boundary ? kIsNonWord : kIsWord);
+ }
+}
+
+
+void AssertionNode::BacktrackIfPrevious(
+ RegExpCompiler* compiler,
+ Trace* trace,
+ AssertionNode::IfPrevious backtrack_if_previous) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ Trace new_trace(*trace);
+ new_trace.InvalidateCurrentCharacter();
+
+ Label fall_through, dummy;
+
+ Label* non_word = backtrack_if_previous == kIsNonWord ?
+ new_trace.backtrack() :
+ &fall_through;
+ Label* word = backtrack_if_previous == kIsNonWord ?
+ &fall_through :
+ new_trace.backtrack();
+
+ if (new_trace.cp_offset() == 0) {
+ // The start of input counts as a non-word character, so the question is
+ // decided if we are at the start.
+ assembler->CheckAtStart(non_word);
+ }
+ // We already checked that we are not at the start of input so it must be
+ // OK to load the previous character.
+ assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1, &dummy, false);
+ EmitWordCheck(assembler, word, non_word, backtrack_if_previous == kIsNonWord);
+
+ assembler->Bind(&fall_through);
+ on_success()->Emit(compiler, &new_trace);
+}
+
+
+void AssertionNode::GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int filled_in,
+ bool not_at_start) {
+ if (assertion_type_ == AT_START && not_at_start) {
+ details->set_cannot_match();
+ return;
+ }
+ return on_success()->GetQuickCheckDetails(details,
+ compiler,
+ filled_in,
+ not_at_start);
+}
+
+
+void AssertionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ switch (assertion_type_) {
+ case AT_END: {
+ Label ok;
+ assembler->CheckPosition(trace->cp_offset(), &ok);
+ assembler->GoTo(trace->backtrack());
+ assembler->Bind(&ok);
+ break;
+ }
+ case AT_START: {
+ if (trace->at_start() == Trace::FALSE_VALUE) {
+ assembler->GoTo(trace->backtrack());
+ return;
+ }
+ if (trace->at_start() == Trace::UNKNOWN) {
+ assembler->CheckNotAtStart(trace->backtrack());
+ Trace at_start_trace = *trace;
+ at_start_trace.set_at_start(true);
+ on_success()->Emit(compiler, &at_start_trace);
+ return;
+ }
+ }
+ break;
+ case AFTER_NEWLINE:
+ EmitHat(compiler, on_success(), trace);
+ return;
+ case AT_BOUNDARY:
+ case AT_NON_BOUNDARY: {
+ EmitBoundaryCheck(compiler, trace);
+ return;
+ }
+ }
+ on_success()->Emit(compiler, trace);
+}
+
+
+static bool DeterminedAlready(QuickCheckDetails* quick_check, int offset) {
+ if (quick_check == NULL) return false;
+ if (offset >= quick_check->characters()) return false;
+ return quick_check->positions(offset)->determines_perfectly;
+}
+
+
+static void UpdateBoundsCheck(int index, int* checked_up_to) {
+ if (index > *checked_up_to) {
+ *checked_up_to = index;
+ }
+}
+
+
+// We call this repeatedly to generate code for each pass over the text node.
+// The passes are in increasing order of difficulty because we hope one
+// of the first passes will fail in which case we are saved the work of the
+// later passes. for example for the case independent regexp /%[asdfghjkl]a/
+// we will check the '%' in the first pass, the case independent 'a' in the
+// second pass and the character class in the last pass.
+//
+// The passes are done from right to left, so for example to test for /bar/
+// we will first test for an 'r' with offset 2, then an 'a' with offset 1
+// and then a 'b' with offset 0. This means we can avoid the end-of-input
+// bounds check most of the time. In the example we only need to check for
+// end-of-input when loading the putative 'r'.
+//
+// A slight complication involves the fact that the first character may already
+// be fetched into a register by the previous node. In this case we want to
+// do the test for that character first. We do this in separate passes. The
+// 'preloaded' argument indicates that we are doing such a 'pass'. If such a
+// pass has been performed then subsequent passes will have true in
+// first_element_checked to indicate that that character does not need to be
+// checked again.
+//
+// In addition to all this we are passed a Trace, which can
+// contain an AlternativeGeneration object. In this AlternativeGeneration
+// object we can see details of any quick check that was already passed in
+// order to get to the code we are now generating. The quick check can involve
+// loading characters, which means we do not need to recheck the bounds
+// up to the limit the quick check already checked. In addition the quick
+// check can have involved a mask and compare operation which may simplify
+// or obviate the need for further checks at some character positions.
+void TextNode::TextEmitPass(RegExpCompiler* compiler,
+ TextEmitPassType pass,
+ bool preloaded,
+ Trace* trace,
+ bool first_element_checked,
+ int* checked_up_to) {
+ Isolate* isolate = Isolate::Current();
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ bool ascii = compiler->ascii();
+ Label* backtrack = trace->backtrack();
+ QuickCheckDetails* quick_check = trace->quick_check_performed();
+ int element_count = elms_->length();
+ for (int i = preloaded ? 0 : element_count - 1; i >= 0; i--) {
+ TextElement elm = elms_->at(i);
+ int cp_offset = trace->cp_offset() + elm.cp_offset;
+ if (elm.text_type == TextElement::ATOM) {
+ Vector<const uc16> quarks = elm.data.u_atom->data();
+ for (int j = preloaded ? 0 : quarks.length() - 1; j >= 0; j--) {
+ if (first_element_checked && i == 0 && j == 0) continue;
+ if (DeterminedAlready(quick_check, elm.cp_offset + j)) continue;
+ EmitCharacterFunction* emit_function = NULL;
+ switch (pass) {
+ case NON_ASCII_MATCH:
+ ASSERT(ascii);
+ if (quarks[j] > String::kMaxOneByteCharCode) {
+ assembler->GoTo(backtrack);
+ return;
+ }
+ break;
+ case NON_LETTER_CHARACTER_MATCH:
+ emit_function = &EmitAtomNonLetter;
+ break;
+ case SIMPLE_CHARACTER_MATCH:
+ emit_function = &EmitSimpleCharacter;
+ break;
+ case CASE_CHARACTER_MATCH:
+ emit_function = &EmitAtomLetter;
+ break;
+ default:
+ break;
+ }
+ if (emit_function != NULL) {
+ bool bound_checked = emit_function(isolate,
+ compiler,
+ quarks[j],
+ backtrack,
+ cp_offset + j,
+ *checked_up_to < cp_offset + j,
+ preloaded);
+ if (bound_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
+ }
+ }
+ } else {
+ ASSERT_EQ(elm.text_type, TextElement::CHAR_CLASS);
+ if (pass == CHARACTER_CLASS_MATCH) {
+ if (first_element_checked && i == 0) continue;
+ if (DeterminedAlready(quick_check, elm.cp_offset)) continue;
+ RegExpCharacterClass* cc = elm.data.u_char_class;
+ EmitCharClass(assembler,
+ cc,
+ ascii,
+ backtrack,
+ cp_offset,
+ *checked_up_to < cp_offset,
+ preloaded,
+ zone());
+ UpdateBoundsCheck(cp_offset, checked_up_to);
+ }
+ }
+ }
+}
+
+
+int TextNode::Length() {
+ TextElement elm = elms_->last();
+ ASSERT(elm.cp_offset >= 0);
+ if (elm.text_type == TextElement::ATOM) {
+ return elm.cp_offset + elm.data.u_atom->data().length();
+ } else {
+ return elm.cp_offset + 1;
+ }
+}
+
+
+bool TextNode::SkipPass(int int_pass, bool ignore_case) {
+ TextEmitPassType pass = static_cast<TextEmitPassType>(int_pass);
+ if (ignore_case) {
+ return pass == SIMPLE_CHARACTER_MATCH;
+ } else {
+ return pass == NON_LETTER_CHARACTER_MATCH || pass == CASE_CHARACTER_MATCH;
+ }
+}
+
+
+// This generates the code to match a text node. A text node can contain
+// straight character sequences (possibly to be matched in a case-independent
+// way) and character classes. For efficiency we do not do this in a single
+// pass from left to right. Instead we pass over the text node several times,
+// emitting code for some character positions every time. See the comment on
+// TextEmitPass for details.
+void TextNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ LimitResult limit_result = LimitVersions(compiler, trace);
+ if (limit_result == DONE) return;
+ ASSERT(limit_result == CONTINUE);
+
+ if (trace->cp_offset() + Length() > RegExpMacroAssembler::kMaxCPOffset) {
+ compiler->SetRegExpTooBig();
+ return;
+ }
+
+ if (compiler->ascii()) {
+ int dummy = 0;
+ TextEmitPass(compiler, NON_ASCII_MATCH, false, trace, false, &dummy);
+ }
+
+ bool first_elt_done = false;
+ int bound_checked_to = trace->cp_offset() - 1;
+ bound_checked_to += trace->bound_checked_up_to();
+
+ // If a character is preloaded into the current character register then
+ // check that now.
+ if (trace->characters_preloaded() == 1) {
+ for (int pass = kFirstRealPass; pass <= kLastPass; pass++) {
+ if (!SkipPass(pass, compiler->ignore_case())) {
+ TextEmitPass(compiler,
+ static_cast<TextEmitPassType>(pass),
+ true,
+ trace,
+ false,
+ &bound_checked_to);
+ }
+ }
+ first_elt_done = true;
+ }
+
+ for (int pass = kFirstRealPass; pass <= kLastPass; pass++) {
+ if (!SkipPass(pass, compiler->ignore_case())) {
+ TextEmitPass(compiler,
+ static_cast<TextEmitPassType>(pass),
+ false,
+ trace,
+ first_elt_done,
+ &bound_checked_to);
+ }
+ }
+
+ Trace successor_trace(*trace);
+ successor_trace.set_at_start(false);
+ successor_trace.AdvanceCurrentPositionInTrace(Length(), compiler);
+ RecursionCheck rc(compiler);
+ on_success()->Emit(compiler, &successor_trace);
+}
+
+
+void Trace::InvalidateCurrentCharacter() {
+ characters_preloaded_ = 0;
+}
+
+
+void Trace::AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler) {
+ ASSERT(by > 0);
+ // We don't have an instruction for shifting the current character register
+ // down or for using a shifted value for anything so lets just forget that
+ // we preloaded any characters into it.
+ characters_preloaded_ = 0;
+ // Adjust the offsets of the quick check performed information. This
+ // information is used to find out what we already determined about the
+ // characters by means of mask and compare.
+ quick_check_performed_.Advance(by, compiler->ascii());
+ cp_offset_ += by;
+ if (cp_offset_ > RegExpMacroAssembler::kMaxCPOffset) {
+ compiler->SetRegExpTooBig();
+ cp_offset_ = 0;
+ }
+ bound_checked_up_to_ = Max(0, bound_checked_up_to_ - by);
+}
+
+
+void TextNode::MakeCaseIndependent(bool is_ascii) {
+ int element_count = elms_->length();
+ for (int i = 0; i < element_count; i++) {
+ TextElement elm = elms_->at(i);
+ if (elm.text_type == TextElement::CHAR_CLASS) {
+ RegExpCharacterClass* cc = elm.data.u_char_class;
+ // 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(zone())) continue;
+ ZoneList<CharacterRange>* ranges = cc->ranges(zone());
+ int range_count = ranges->length();
+ for (int j = 0; j < range_count; j++) {
+ ranges->at(j).AddCaseEquivalents(ranges, is_ascii, zone());
+ }
+ }
+ }
+}
+
+
+int TextNode::GreedyLoopTextLength() {
+ TextElement elm = elms_->at(elms_->length() - 1);
+ if (elm.text_type == TextElement::CHAR_CLASS) {
+ return elm.cp_offset + 1;
+ } else {
+ return elm.cp_offset + elm.data.u_atom->data().length();
+ }
+}
+
+
+RegExpNode* TextNode::GetSuccessorOfOmnivorousTextNode(
+ RegExpCompiler* compiler) {
+ if (elms_->length() != 1) return NULL;
+ TextElement elm = elms_->at(0);
+ if (elm.text_type != TextElement::CHAR_CLASS) return NULL;
+ RegExpCharacterClass* node = elm.data.u_char_class;
+ ZoneList<CharacterRange>* ranges = node->ranges(zone());
+ if (!CharacterRange::IsCanonical(ranges)) {
+ CharacterRange::Canonicalize(ranges);
+ }
+ if (node->is_negated()) {
+ return ranges->length() == 0 ? on_success() : NULL;
+ }
+ if (ranges->length() != 1) return NULL;
+ uint32_t max_char;
+ if (compiler->ascii()) {
+ max_char = String::kMaxOneByteCharCode;
+ } else {
+ max_char = String::kMaxUtf16CodeUnit;
+ }
+ return ranges->at(0).IsEverything(max_char) ? on_success() : NULL;
+}
+
+
+// Finds the fixed match length of a sequence of nodes that goes from
+// this alternative and back to this choice node. If there are variable
+// length nodes or other complications in the way then return a sentinel
+// value indicating that a greedy loop cannot be constructed.
+int ChoiceNode::GreedyLoopTextLengthForAlternative(
+ GuardedAlternative* alternative) {
+ int length = 0;
+ RegExpNode* node = alternative->node();
+ // Later we will generate code for all these text nodes using recursion
+ // so we have to limit the max number.
+ int recursion_depth = 0;
+ while (node != this) {
+ if (recursion_depth++ > RegExpCompiler::kMaxRecursion) {
+ return kNodeIsTooComplexForGreedyLoops;
+ }
+ int node_length = node->GreedyLoopTextLength();
+ if (node_length == kNodeIsTooComplexForGreedyLoops) {
+ return kNodeIsTooComplexForGreedyLoops;
+ }
+ length += node_length;
+ SeqRegExpNode* seq_node = static_cast<SeqRegExpNode*>(node);
+ node = seq_node->on_success();
+ }
+ return length;
+}
+
+
+void LoopChoiceNode::AddLoopAlternative(GuardedAlternative alt) {
+ ASSERT_EQ(loop_node_, NULL);
+ AddAlternative(alt);
+ loop_node_ = alt.node();
+}
+
+
+void LoopChoiceNode::AddContinueAlternative(GuardedAlternative alt) {
+ ASSERT_EQ(continue_node_, NULL);
+ AddAlternative(alt);
+ continue_node_ = alt.node();
+}
+
+
+void LoopChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
+ if (trace->stop_node() == this) {
+ int text_length =
+ GreedyLoopTextLengthForAlternative(&(alternatives_->at(0)));
+ ASSERT(text_length != kNodeIsTooComplexForGreedyLoops);
+ // Update the counter-based backtracking info on the stack. This is an
+ // optimization for greedy loops (see below).
+ ASSERT(trace->cp_offset() == text_length);
+ macro_assembler->AdvanceCurrentPosition(text_length);
+ macro_assembler->GoTo(trace->loop_label());
+ return;
+ }
+ ASSERT(trace->stop_node() == NULL);
+ if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ return;
+ }
+ ChoiceNode::Emit(compiler, trace);
+}
+
+
+int ChoiceNode::CalculatePreloadCharacters(RegExpCompiler* compiler,
+ int eats_at_least) {
+ int preload_characters = Min(4, eats_at_least);
+ if (compiler->macro_assembler()->CanReadUnaligned()) {
+ bool ascii = compiler->ascii();
+ if (ascii) {
+ if (preload_characters > 4) preload_characters = 4;
+ // We can't preload 3 characters because there is no machine instruction
+ // to do that. We can't just load 4 because we could be reading
+ // beyond the end of the string, which could cause a memory fault.
+ if (preload_characters == 3) preload_characters = 2;
+ } else {
+ if (preload_characters > 2) preload_characters = 2;
+ }
+ } else {
+ if (preload_characters > 1) preload_characters = 1;
+ }
+ return preload_characters;
+}
+
+
+// This class is used when generating the alternatives in a choice node. It
+// records the way the alternative is being code generated.
+class AlternativeGeneration: public Malloced {
+ public:
+ AlternativeGeneration()
+ : possible_success(),
+ expects_preload(false),
+ after(),
+ quick_check_details() { }
+ Label possible_success;
+ bool expects_preload;
+ Label after;
+ QuickCheckDetails quick_check_details;
+};
+
+
+// Creates a list of AlternativeGenerations. If the list has a reasonable
+// size then it is on the stack, otherwise the excess is on the heap.
+class AlternativeGenerationList {
+ public:
+ AlternativeGenerationList(int count, Zone* zone)
+ : alt_gens_(count, zone) {
+ for (int i = 0; i < count && i < kAFew; i++) {
+ alt_gens_.Add(a_few_alt_gens_ + i, zone);
+ }
+ for (int i = kAFew; i < count; i++) {
+ alt_gens_.Add(new AlternativeGeneration(), zone);
+ }
+ }
+ ~AlternativeGenerationList() {
+ for (int i = kAFew; i < alt_gens_.length(); i++) {
+ delete alt_gens_[i];
+ alt_gens_[i] = NULL;
+ }
+ }
+
+ AlternativeGeneration* at(int i) {
+ return alt_gens_[i];
+ }
+
+ private:
+ static const int kAFew = 10;
+ ZoneList<AlternativeGeneration*> alt_gens_;
+ AlternativeGeneration a_few_alt_gens_[kAFew];
+};
+
+
+// The '2' variant is has inclusive from and exclusive to.
+static const int kSpaceRanges[] = { '\t', '\r' + 1, ' ', ' ' + 1, 0x00A0,
+ 0x00A1, 0x1680, 0x1681, 0x180E, 0x180F, 0x2000, 0x200B, 0x2028, 0x202A,
+ 0x202F, 0x2030, 0x205F, 0x2060, 0x3000, 0x3001, 0xFEFF, 0xFF00, 0x10000 };
+static const int kSpaceRangeCount = ARRAY_SIZE(kSpaceRanges);
+
+static const int kWordRanges[] = {
+ '0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1, 0x10000 };
+static const int kWordRangeCount = ARRAY_SIZE(kWordRanges);
+static const int kDigitRanges[] = { '0', '9' + 1, 0x10000 };
+static const int kDigitRangeCount = ARRAY_SIZE(kDigitRanges);
+static const int kSurrogateRanges[] = { 0xd800, 0xe000, 0x10000 };
+static const int kSurrogateRangeCount = ARRAY_SIZE(kSurrogateRanges);
+static const int kLineTerminatorRanges[] = { 0x000A, 0x000B, 0x000D, 0x000E,
+ 0x2028, 0x202A, 0x10000 };
+static const int kLineTerminatorRangeCount = ARRAY_SIZE(kLineTerminatorRanges);
+
+
+void BoyerMoorePositionInfo::Set(int character) {
+ SetInterval(Interval(character, character));
+}
+
+
+void BoyerMoorePositionInfo::SetInterval(const Interval& interval) {
+ s_ = AddRange(s_, kSpaceRanges, kSpaceRangeCount, interval);
+ w_ = AddRange(w_, kWordRanges, kWordRangeCount, interval);
+ d_ = AddRange(d_, kDigitRanges, kDigitRangeCount, interval);
+ surrogate_ =
+ AddRange(surrogate_, kSurrogateRanges, kSurrogateRangeCount, interval);
+ if (interval.to() - interval.from() >= kMapSize - 1) {
+ if (map_count_ != kMapSize) {
+ map_count_ = kMapSize;
+ for (int i = 0; i < kMapSize; i++) map_->at(i) = true;
+ }
+ return;
+ }
+ for (int i = interval.from(); i <= interval.to(); i++) {
+ int mod_character = (i & kMask);
+ if (!map_->at(mod_character)) {
+ map_count_++;
+ map_->at(mod_character) = true;
+ }
+ if (map_count_ == kMapSize) return;
+ }
+}
+
+
+void BoyerMoorePositionInfo::SetAll() {
+ s_ = w_ = d_ = kLatticeUnknown;
+ if (map_count_ != kMapSize) {
+ map_count_ = kMapSize;
+ for (int i = 0; i < kMapSize; i++) map_->at(i) = true;
+ }
+}
+
+
+BoyerMooreLookahead::BoyerMooreLookahead(
+ int length, RegExpCompiler* compiler, Zone* zone)
+ : length_(length),
+ compiler_(compiler) {
+ if (compiler->ascii()) {
+ max_char_ = String::kMaxOneByteCharCode;
+ } else {
+ max_char_ = String::kMaxUtf16CodeUnit;
+ }
+ bitmaps_ = new(zone) ZoneList<BoyerMoorePositionInfo*>(length, zone);
+ for (int i = 0; i < length; i++) {
+ bitmaps_->Add(new(zone) BoyerMoorePositionInfo(zone), zone);
+ }
+}
+
+
+// Find the longest range of lookahead that has the fewest number of different
+// characters that can occur at a given position. Since we are optimizing two
+// different parameters at once this is a tradeoff.
+bool BoyerMooreLookahead::FindWorthwhileInterval(int* from, int* to) {
+ int biggest_points = 0;
+ // If more than 32 characters out of 128 can occur it is unlikely that we can
+ // be lucky enough to step forwards much of the time.
+ const int kMaxMax = 32;
+ for (int max_number_of_chars = 4;
+ max_number_of_chars < kMaxMax;
+ max_number_of_chars *= 2) {
+ biggest_points =
+ FindBestInterval(max_number_of_chars, biggest_points, from, to);
+ }
+ if (biggest_points == 0) return false;
+ return true;
+}
+
+
+// Find the highest-points range between 0 and length_ where the character
+// information is not too vague. 'Too vague' means that there are more than
+// max_number_of_chars that can occur at this position. Calculates the number
+// of points as the product of width-of-the-range and
+// probability-of-finding-one-of-the-characters, where the probability is
+// calculated using the frequency distribution of the sample subject string.
+int BoyerMooreLookahead::FindBestInterval(
+ int max_number_of_chars, int old_biggest_points, int* from, int* to) {
+ int biggest_points = old_biggest_points;
+ static const int kSize = RegExpMacroAssembler::kTableSize;
+ for (int i = 0; i < length_; ) {
+ while (i < length_ && Count(i) > max_number_of_chars) i++;
+ if (i == length_) break;
+ int remembered_from = i;
+ bool union_map[kSize];
+ for (int j = 0; j < kSize; j++) union_map[j] = false;
+ while (i < length_ && Count(i) <= max_number_of_chars) {
+ BoyerMoorePositionInfo* map = bitmaps_->at(i);
+ for (int j = 0; j < kSize; j++) union_map[j] |= map->at(j);
+ i++;
+ }
+ int frequency = 0;
+ for (int j = 0; j < kSize; j++) {
+ if (union_map[j]) {
+ // Add 1 to the frequency to give a small per-character boost for
+ // the cases where our sampling is not good enough and many
+ // characters have a frequency of zero. This means the frequency
+ // can theoretically be up to 2*kSize though we treat it mostly as
+ // a fraction of kSize.
+ frequency += compiler_->frequency_collator()->Frequency(j) + 1;
+ }
+ }
+ // We use the probability of skipping times the distance we are skipping to
+ // judge the effectiveness of this. Actually we have a cut-off: By
+ // dividing by 2 we switch off the skipping if the probability of skipping
+ // is less than 50%. This is because the multibyte mask-and-compare
+ // skipping in quickcheck is more likely to do well on this case.
+ bool in_quickcheck_range = ((i - remembered_from < 4) ||
+ (compiler_->ascii() ? remembered_from <= 4 : remembered_from <= 2));
+ // Called 'probability' but it is only a rough estimate and can actually
+ // be outside the 0-kSize range.
+ int probability = (in_quickcheck_range ? kSize / 2 : kSize) - frequency;
+ int points = (i - remembered_from) * probability;
+ if (points > biggest_points) {
+ *from = remembered_from;
+ *to = i - 1;
+ biggest_points = points;
+ }
+ }
+ return biggest_points;
+}
+
+
+// Take all the characters that will not prevent a successful match if they
+// occur in the subject string in the range between min_lookahead and
+// max_lookahead (inclusive) measured from the current position. If the
+// character at max_lookahead offset is not one of these characters, then we
+// can safely skip forwards by the number of characters in the range.
+int BoyerMooreLookahead::GetSkipTable(int min_lookahead,
+ int max_lookahead,
+ Handle<ByteArray> boolean_skip_table) {
+ const int kSize = RegExpMacroAssembler::kTableSize;
+
+ const int kSkipArrayEntry = 0;
+ const int kDontSkipArrayEntry = 1;
+
+ for (int i = 0; i < kSize; i++) {
+ boolean_skip_table->set(i, kSkipArrayEntry);
+ }
+ int skip = max_lookahead + 1 - min_lookahead;
+
+ for (int i = max_lookahead; i >= min_lookahead; i--) {
+ BoyerMoorePositionInfo* map = bitmaps_->at(i);
+ for (int j = 0; j < kSize; j++) {
+ if (map->at(j)) {
+ boolean_skip_table->set(j, kDontSkipArrayEntry);
+ }
+ }
+ }
+
+ return skip;
+}
+
+
+// See comment above on the implementation of GetSkipTable.
+bool BoyerMooreLookahead::EmitSkipInstructions(RegExpMacroAssembler* masm) {
+ const int kSize = RegExpMacroAssembler::kTableSize;
+
+ int min_lookahead = 0;
+ int max_lookahead = 0;
+
+ if (!FindWorthwhileInterval(&min_lookahead, &max_lookahead)) return false;
+
+ bool found_single_character = false;
+ int single_character = 0;
+ for (int i = max_lookahead; i >= min_lookahead; i--) {
+ BoyerMoorePositionInfo* map = bitmaps_->at(i);
+ if (map->map_count() > 1 ||
+ (found_single_character && map->map_count() != 0)) {
+ found_single_character = false;
+ break;
+ }
+ for (int j = 0; j < kSize; j++) {
+ if (map->at(j)) {
+ found_single_character = true;
+ single_character = j;
+ break;
+ }
+ }
+ }
+
+ int lookahead_width = max_lookahead + 1 - min_lookahead;
+
+ if (found_single_character && lookahead_width == 1 && max_lookahead < 3) {
+ // The mask-compare can probably handle this better.
+ return false;
+ }
+
+ if (found_single_character) {
+ Label cont, again;
+ masm->Bind(&again);
+ masm->LoadCurrentCharacter(max_lookahead, &cont, true);
+ if (max_char_ > kSize) {
+ masm->CheckCharacterAfterAnd(single_character,
+ RegExpMacroAssembler::kTableMask,
+ &cont);
+ } else {
+ masm->CheckCharacter(single_character, &cont);
+ }
+ masm->AdvanceCurrentPosition(lookahead_width);
+ masm->GoTo(&again);
+ masm->Bind(&cont);
+ return true;
+ }
+
+ Factory* factory = Isolate::Current()->factory();
+ Handle<ByteArray> boolean_skip_table = factory->NewByteArray(kSize, TENURED);
+ int skip_distance = GetSkipTable(
+ min_lookahead, max_lookahead, boolean_skip_table);
+ ASSERT(skip_distance != 0);
+
+ Label cont, again;
+ masm->Bind(&again);
+ masm->LoadCurrentCharacter(max_lookahead, &cont, true);
+ masm->CheckBitInTable(boolean_skip_table, &cont);
+ masm->AdvanceCurrentPosition(skip_distance);
+ masm->GoTo(&again);
+ masm->Bind(&cont);
+
+ return true;
+}
+
+
+/* Code generation for choice nodes.
+ *
+ * We generate quick checks that do a mask and compare to eliminate a
+ * choice. If the quick check succeeds then it jumps to the continuation to
+ * do slow checks and check subsequent nodes. If it fails (the common case)
+ * it falls through to the next choice.
+ *
+ * Here is the desired flow graph. Nodes directly below each other imply
+ * fallthrough. Alternatives 1 and 2 have quick checks. Alternative
+ * 3 doesn't have a quick check so we have to call the slow check.
+ * Nodes are marked Qn for quick checks and Sn for slow checks. The entire
+ * regexp continuation is generated directly after the Sn node, up to the
+ * next GoTo if we decide to reuse some already generated code. Some
+ * nodes expect preload_characters to be preloaded into the current
+ * character register. R nodes do this preloading. Vertices are marked
+ * F for failures and S for success (possible success in the case of quick
+ * nodes). L, V, < and > are used as arrow heads.
+ *
+ * ----------> R
+ * |
+ * V
+ * Q1 -----> S1
+ * | S /
+ * F| /
+ * | F/
+ * | /
+ * | R
+ * | /
+ * V L
+ * Q2 -----> S2
+ * | S /
+ * F| /
+ * | F/
+ * | /
+ * | R
+ * | /
+ * V L
+ * S3
+ * |
+ * F|
+ * |
+ * R
+ * |
+ * backtrack V
+ * <----------Q4
+ * \ F |
+ * \ |S
+ * \ F V
+ * \-----S4
+ *
+ * For greedy loops we reverse our expectation and expect to match rather
+ * than fail. Therefore we want the loop code to look like this (U is the
+ * unwind code that steps back in the greedy loop). The following alternatives
+ * look the same as above.
+ * _____
+ * / \
+ * V |
+ * ----------> S1 |
+ * /| |
+ * / |S |
+ * F/ \_____/
+ * /
+ * |<-----------
+ * | \
+ * V \
+ * Q2 ---> S2 \
+ * | S / |
+ * F| / |
+ * | F/ |
+ * | / |
+ * | R |
+ * | / |
+ * F VL |
+ * <------U |
+ * back |S |
+ * \______________/
+ */
+
+void ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
+ int choice_count = alternatives_->length();
+#ifdef DEBUG
+ for (int i = 0; i < choice_count - 1; i++) {
+ GuardedAlternative alternative = alternatives_->at(i);
+ ZoneList<Guard*>* guards = alternative.guards();
+ int guard_count = (guards == NULL) ? 0 : guards->length();
+ for (int j = 0; j < guard_count; j++) {
+ ASSERT(!trace->mentions_reg(guards->at(j)->reg()));
+ }
+ }
+#endif
+
+ LimitResult limit_result = LimitVersions(compiler, trace);
+ if (limit_result == DONE) return;
+ ASSERT(limit_result == CONTINUE);
+
+ int new_flush_budget = trace->flush_budget() / choice_count;
+ if (trace->flush_budget() == 0 && trace->actions() != NULL) {
+ trace->Flush(compiler, this);
+ return;
+ }
+
+ RecursionCheck rc(compiler);
+
+ Trace* current_trace = trace;
+
+ int text_length = GreedyLoopTextLengthForAlternative(&(alternatives_->at(0)));
+ bool greedy_loop = false;
+ Label greedy_loop_label;
+ Trace counter_backtrack_trace;
+ counter_backtrack_trace.set_backtrack(&greedy_loop_label);
+ if (not_at_start()) counter_backtrack_trace.set_at_start(false);
+
+ if (choice_count > 1 && text_length != kNodeIsTooComplexForGreedyLoops) {
+ // Here we have special handling for greedy loops containing only text nodes
+ // and other simple nodes. These are handled by pushing the current
+ // position on the stack and then incrementing the current position each
+ // time around the switch. On backtrack we decrement the current position
+ // and check it against the pushed value. This avoids pushing backtrack
+ // information for each iteration of the loop, which could take up a lot of
+ // space.
+ greedy_loop = true;
+ ASSERT(trace->stop_node() == NULL);
+ macro_assembler->PushCurrentPosition();
+ current_trace = &counter_backtrack_trace;
+ Label greedy_match_failed;
+ Trace greedy_match_trace;
+ if (not_at_start()) greedy_match_trace.set_at_start(false);
+ greedy_match_trace.set_backtrack(&greedy_match_failed);
+ Label loop_label;
+ macro_assembler->Bind(&loop_label);
+ greedy_match_trace.set_stop_node(this);
+ greedy_match_trace.set_loop_label(&loop_label);
+ alternatives_->at(0).node()->Emit(compiler, &greedy_match_trace);
+ macro_assembler->Bind(&greedy_match_failed);
+ }
+
+ Label second_choice; // For use in greedy matches.
+ macro_assembler->Bind(&second_choice);
+
+ int first_normal_choice = greedy_loop ? 1 : 0;
+
+ bool not_at_start = current_trace->at_start() == Trace::FALSE_VALUE;
+ const int kEatsAtLeastNotYetInitialized = -1;
+ int eats_at_least = kEatsAtLeastNotYetInitialized;
+
+ bool skip_was_emitted = false;
+
+ if (!greedy_loop && choice_count == 2) {
+ GuardedAlternative alt1 = alternatives_->at(1);
+ if (alt1.guards() == NULL || alt1.guards()->length() == 0) {
+ RegExpNode* eats_anything_node = alt1.node();
+ if (eats_anything_node->GetSuccessorOfOmnivorousTextNode(compiler) ==
+ this) {
+ // At this point we know that we are at a non-greedy loop that will eat
+ // any character one at a time. Any non-anchored regexp has such a
+ // loop prepended to it in order to find where it starts. We look for
+ // a pattern of the form ...abc... where we can look 6 characters ahead
+ // and step forwards 3 if the character is not one of abc. Abc need
+ // not be atoms, they can be any reasonably limited character class or
+ // small alternation.
+ ASSERT(trace->is_trivial()); // This is the case on LoopChoiceNodes.
+ BoyerMooreLookahead* lookahead = bm_info(not_at_start);
+ if (lookahead == NULL) {
+ eats_at_least = Min(kMaxLookaheadForBoyerMoore,
+ EatsAtLeast(kMaxLookaheadForBoyerMoore,
+ kRecursionBudget,
+ not_at_start));
+ if (eats_at_least >= 1) {
+ BoyerMooreLookahead* bm =
+ new(zone()) BoyerMooreLookahead(eats_at_least,
+ compiler,
+ zone());
+ GuardedAlternative alt0 = alternatives_->at(0);
+ alt0.node()->FillInBMInfo(0, kRecursionBudget, bm, not_at_start);
+ skip_was_emitted = bm->EmitSkipInstructions(macro_assembler);
+ }
+ } else {
+ skip_was_emitted = lookahead->EmitSkipInstructions(macro_assembler);
+ }
+ }
+ }
+ }
+
+ if (eats_at_least == kEatsAtLeastNotYetInitialized) {
+ // Save some time by looking at most one machine word ahead.
+ eats_at_least =
+ EatsAtLeast(compiler->ascii() ? 4 : 2, kRecursionBudget, not_at_start);
+ }
+ int preload_characters = CalculatePreloadCharacters(compiler, eats_at_least);
+
+ bool preload_is_current = !skip_was_emitted &&
+ (current_trace->characters_preloaded() == preload_characters);
+ bool preload_has_checked_bounds = preload_is_current;
+
+ AlternativeGenerationList alt_gens(choice_count, zone());
+
+ // For now we just call all choices one after the other. The idea ultimately
+ // is to use the Dispatch table to try only the relevant ones.
+ for (int i = first_normal_choice; i < choice_count; i++) {
+ GuardedAlternative alternative = alternatives_->at(i);
+ AlternativeGeneration* alt_gen = alt_gens.at(i);
+ alt_gen->quick_check_details.set_characters(preload_characters);
+ ZoneList<Guard*>* guards = alternative.guards();
+ int guard_count = (guards == NULL) ? 0 : guards->length();
+ Trace new_trace(*current_trace);
+ new_trace.set_characters_preloaded(preload_is_current ?
+ preload_characters :
+ 0);
+ if (preload_has_checked_bounds) {
+ new_trace.set_bound_checked_up_to(preload_characters);
+ }
+ new_trace.quick_check_performed()->Clear();
+ if (not_at_start_) new_trace.set_at_start(Trace::FALSE_VALUE);
+ alt_gen->expects_preload = preload_is_current;
+ bool generate_full_check_inline = false;
+ if (FLAG_regexp_optimization &&
+ try_to_emit_quick_check_for_alternative(i) &&
+ alternative.node()->EmitQuickCheck(compiler,
+ &new_trace,
+ preload_has_checked_bounds,
+ &alt_gen->possible_success,
+ &alt_gen->quick_check_details,
+ i < choice_count - 1)) {
+ // Quick check was generated for this choice.
+ preload_is_current = true;
+ preload_has_checked_bounds = true;
+ // On the last choice in the ChoiceNode we generated the quick
+ // check to fall through on possible success. So now we need to
+ // generate the full check inline.
+ if (i == choice_count - 1) {
+ macro_assembler->Bind(&alt_gen->possible_success);
+ new_trace.set_quick_check_performed(&alt_gen->quick_check_details);
+ new_trace.set_characters_preloaded(preload_characters);
+ new_trace.set_bound_checked_up_to(preload_characters);
+ generate_full_check_inline = true;
+ }
+ } else if (alt_gen->quick_check_details.cannot_match()) {
+ if (i == choice_count - 1 && !greedy_loop) {
+ macro_assembler->GoTo(trace->backtrack());
+ }
+ continue;
+ } else {
+ // No quick check was generated. Put the full code here.
+ // If this is not the first choice then there could be slow checks from
+ // previous cases that go here when they fail. There's no reason to
+ // insist that they preload characters since the slow check we are about
+ // to generate probably can't use it.
+ if (i != first_normal_choice) {
+ alt_gen->expects_preload = false;
+ new_trace.InvalidateCurrentCharacter();
+ }
+ if (i < choice_count - 1) {
+ new_trace.set_backtrack(&alt_gen->after);
+ }
+ generate_full_check_inline = true;
+ }
+ if (generate_full_check_inline) {
+ if (new_trace.actions() != NULL) {
+ new_trace.set_flush_budget(new_flush_budget);
+ }
+ for (int j = 0; j < guard_count; j++) {
+ GenerateGuard(macro_assembler, guards->at(j), &new_trace);
+ }
+ alternative.node()->Emit(compiler, &new_trace);
+ preload_is_current = false;
+ }
+ macro_assembler->Bind(&alt_gen->after);
+ }
+ if (greedy_loop) {
+ macro_assembler->Bind(&greedy_loop_label);
+ // If we have unwound to the bottom then backtrack.
+ macro_assembler->CheckGreedyLoop(trace->backtrack());
+ // Otherwise try the second priority at an earlier position.
+ macro_assembler->AdvanceCurrentPosition(-text_length);
+ macro_assembler->GoTo(&second_choice);
+ }
+
+ // At this point we need to generate slow checks for the alternatives where
+ // the quick check was inlined. We can recognize these because the associated
+ // label was bound.
+ for (int i = first_normal_choice; i < choice_count - 1; i++) {
+ AlternativeGeneration* alt_gen = alt_gens.at(i);
+ Trace new_trace(*current_trace);
+ // If there are actions to be flushed we have to limit how many times
+ // they are flushed. Take the budget of the parent trace and distribute
+ // it fairly amongst the children.
+ if (new_trace.actions() != NULL) {
+ new_trace.set_flush_budget(new_flush_budget);
+ }
+ EmitOutOfLineContinuation(compiler,
+ &new_trace,
+ alternatives_->at(i),
+ alt_gen,
+ preload_characters,
+ alt_gens.at(i + 1)->expects_preload);
+ }
+}
+
+
+void ChoiceNode::EmitOutOfLineContinuation(RegExpCompiler* compiler,
+ Trace* trace,
+ GuardedAlternative alternative,
+ AlternativeGeneration* alt_gen,
+ int preload_characters,
+ bool next_expects_preload) {
+ if (!alt_gen->possible_success.is_linked()) return;
+
+ RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
+ macro_assembler->Bind(&alt_gen->possible_success);
+ Trace out_of_line_trace(*trace);
+ out_of_line_trace.set_characters_preloaded(preload_characters);
+ out_of_line_trace.set_quick_check_performed(&alt_gen->quick_check_details);
+ if (not_at_start_) out_of_line_trace.set_at_start(Trace::FALSE_VALUE);
+ ZoneList<Guard*>* guards = alternative.guards();
+ int guard_count = (guards == NULL) ? 0 : guards->length();
+ if (next_expects_preload) {
+ Label reload_current_char;
+ out_of_line_trace.set_backtrack(&reload_current_char);
+ for (int j = 0; j < guard_count; j++) {
+ GenerateGuard(macro_assembler, guards->at(j), &out_of_line_trace);
+ }
+ alternative.node()->Emit(compiler, &out_of_line_trace);
+ macro_assembler->Bind(&reload_current_char);
+ // Reload the current character, since the next quick check expects that.
+ // We don't need to check bounds here because we only get into this
+ // code through a quick check which already did the checked load.
+ macro_assembler->LoadCurrentCharacter(trace->cp_offset(),
+ NULL,
+ false,
+ preload_characters);
+ macro_assembler->GoTo(&(alt_gen->after));
+ } else {
+ out_of_line_trace.set_backtrack(&(alt_gen->after));
+ for (int j = 0; j < guard_count; j++) {
+ GenerateGuard(macro_assembler, guards->at(j), &out_of_line_trace);
+ }
+ alternative.node()->Emit(compiler, &out_of_line_trace);
+ }
+}
+
+
+void ActionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ LimitResult limit_result = LimitVersions(compiler, trace);
+ if (limit_result == DONE) return;
+ ASSERT(limit_result == CONTINUE);
+
+ RecursionCheck rc(compiler);
+
+ switch (action_type_) {
+ case STORE_POSITION: {
+ Trace::DeferredCapture
+ new_capture(data_.u_position_register.reg,
+ data_.u_position_register.is_capture,
+ trace);
+ Trace new_trace = *trace;
+ new_trace.add_action(&new_capture);
+ on_success()->Emit(compiler, &new_trace);
+ break;
+ }
+ case INCREMENT_REGISTER: {
+ Trace::DeferredIncrementRegister
+ new_increment(data_.u_increment_register.reg);
+ Trace new_trace = *trace;
+ new_trace.add_action(&new_increment);
+ on_success()->Emit(compiler, &new_trace);
+ break;
+ }
+ case SET_REGISTER: {
+ Trace::DeferredSetRegister
+ new_set(data_.u_store_register.reg, data_.u_store_register.value);
+ Trace new_trace = *trace;
+ new_trace.add_action(&new_set);
+ on_success()->Emit(compiler, &new_trace);
+ break;
+ }
+ case CLEAR_CAPTURES: {
+ Trace::DeferredClearCaptures
+ new_capture(Interval(data_.u_clear_captures.range_from,
+ data_.u_clear_captures.range_to));
+ Trace new_trace = *trace;
+ new_trace.add_action(&new_capture);
+ on_success()->Emit(compiler, &new_trace);
+ break;
+ }
+ case BEGIN_SUBMATCH:
+ if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ } else {
+ assembler->WriteCurrentPositionToRegister(
+ data_.u_submatch.current_position_register, 0);
+ assembler->WriteStackPointerToRegister(
+ data_.u_submatch.stack_pointer_register);
+ on_success()->Emit(compiler, trace);
+ }
+ break;
+ case EMPTY_MATCH_CHECK: {
+ int start_pos_reg = data_.u_empty_match_check.start_register;
+ int stored_pos = 0;
+ int rep_reg = data_.u_empty_match_check.repetition_register;
+ bool has_minimum = (rep_reg != RegExpCompiler::kNoRegister);
+ bool know_dist = trace->GetStoredPosition(start_pos_reg, &stored_pos);
+ if (know_dist && !has_minimum && stored_pos == trace->cp_offset()) {
+ // If we know we haven't advanced and there is no minimum we
+ // can just backtrack immediately.
+ assembler->GoTo(trace->backtrack());
+ } else if (know_dist && stored_pos < trace->cp_offset()) {
+ // If we know we've advanced we can generate the continuation
+ // immediately.
+ on_success()->Emit(compiler, trace);
+ } else if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ } else {
+ Label skip_empty_check;
+ // If we have a minimum number of repetitions we check the current
+ // number first and skip the empty check if it's not enough.
+ if (has_minimum) {
+ int limit = data_.u_empty_match_check.repetition_limit;
+ assembler->IfRegisterLT(rep_reg, limit, &skip_empty_check);
+ }
+ // If the match is empty we bail out, otherwise we fall through
+ // to the on-success continuation.
+ assembler->IfRegisterEqPos(data_.u_empty_match_check.start_register,
+ trace->backtrack());
+ assembler->Bind(&skip_empty_check);
+ on_success()->Emit(compiler, trace);
+ }
+ break;
+ }
+ case POSITIVE_SUBMATCH_SUCCESS: {
+ if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ return;
+ }
+ assembler->ReadCurrentPositionFromRegister(
+ data_.u_submatch.current_position_register);
+ assembler->ReadStackPointerFromRegister(
+ data_.u_submatch.stack_pointer_register);
+ int clear_register_count = data_.u_submatch.clear_register_count;
+ if (clear_register_count == 0) {
+ on_success()->Emit(compiler, trace);
+ return;
+ }
+ int clear_registers_from = data_.u_submatch.clear_register_from;
+ Label clear_registers_backtrack;
+ Trace new_trace = *trace;
+ new_trace.set_backtrack(&clear_registers_backtrack);
+ on_success()->Emit(compiler, &new_trace);
+
+ assembler->Bind(&clear_registers_backtrack);
+ int clear_registers_to = clear_registers_from + clear_register_count - 1;
+ assembler->ClearRegisters(clear_registers_from, clear_registers_to);
+
+ ASSERT(trace->backtrack() == NULL);
+ assembler->Backtrack();
+ return;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ return;
+ }
+
+ LimitResult limit_result = LimitVersions(compiler, trace);
+ if (limit_result == DONE) return;
+ ASSERT(limit_result == CONTINUE);
+
+ RecursionCheck rc(compiler);
+
+ ASSERT_EQ(start_reg_ + 1, end_reg_);
+ if (compiler->ignore_case()) {
+ assembler->CheckNotBackReferenceIgnoreCase(start_reg_,
+ trace->backtrack());
+ } else {
+ assembler->CheckNotBackReference(start_reg_, trace->backtrack());
+ }
+ on_success()->Emit(compiler, trace);
+}
+
+
+// -------------------------------------------------------------------
+// Dot/dotty output
+
+
+#ifdef DEBUG
+
+
+class DotPrinter: public NodeVisitor {
+ public:
+ explicit DotPrinter(bool ignore_case)
+ : ignore_case_(ignore_case),
+ stream_(&alloc_) { }
+ void PrintNode(const char* label, RegExpNode* node);
+ void Visit(RegExpNode* node);
+ void PrintAttributes(RegExpNode* from);
+ StringStream* stream() { return &stream_; }
+ void PrintOnFailure(RegExpNode* from, RegExpNode* to);
+#define DECLARE_VISIT(Type) \
+ virtual void Visit##Type(Type##Node* that);
+FOR_EACH_NODE_TYPE(DECLARE_VISIT)
+#undef DECLARE_VISIT
+ private:
+ bool ignore_case_;
+ HeapStringAllocator alloc_;
+ StringStream stream_;
+};
+
+
+void DotPrinter::PrintNode(const char* label, RegExpNode* node) {
+ stream()->Add("digraph G {\n graph [label=\"");
+ for (int i = 0; label[i]; i++) {
+ switch (label[i]) {
+ case '\\':
+ stream()->Add("\\\\");
+ break;
+ case '"':
+ stream()->Add("\"");
+ break;
+ default:
+ stream()->Put(label[i]);
+ break;
+ }
+ }
+ stream()->Add("\"];\n");
+ Visit(node);
+ stream()->Add("}\n");
+ printf("%s", *(stream()->ToCString()));
+}
+
+
+void DotPrinter::Visit(RegExpNode* node) {
+ if (node->info()->visited) return;
+ node->info()->visited = true;
+ node->Accept(this);
+}
+
+
+void DotPrinter::PrintOnFailure(RegExpNode* from, RegExpNode* on_failure) {
+ stream()->Add(" n%p -> n%p [style=dotted];\n", from, on_failure);
+ Visit(on_failure);
+}
+
+
+class TableEntryBodyPrinter {
+ public:
+ TableEntryBodyPrinter(StringStream* stream, ChoiceNode* choice)
+ : stream_(stream), choice_(choice) { }
+ void Call(uc16 from, DispatchTable::Entry entry) {
+ OutSet* out_set = entry.out_set();
+ for (unsigned i = 0; i < OutSet::kFirstLimit; i++) {
+ if (out_set->Get(i)) {
+ stream()->Add(" n%p:s%io%i -> n%p;\n",
+ choice(),
+ from,
+ i,
+ choice()->alternatives()->at(i).node());
+ }
+ }
+ }
+ private:
+ StringStream* stream() { return stream_; }
+ ChoiceNode* choice() { return choice_; }
+ StringStream* stream_;
+ ChoiceNode* choice_;
+};
+
+
+class TableEntryHeaderPrinter {
+ public:
+ explicit TableEntryHeaderPrinter(StringStream* stream)
+ : first_(true), stream_(stream) { }
+ void Call(uc16 from, DispatchTable::Entry entry) {
+ if (first_) {
+ first_ = false;
+ } else {
+ stream()->Add("|");
+ }
+ stream()->Add("{\\%k-\\%k|{", from, entry.to());
+ OutSet* out_set = entry.out_set();
+ int priority = 0;
+ for (unsigned i = 0; i < OutSet::kFirstLimit; i++) {
+ if (out_set->Get(i)) {
+ if (priority > 0) stream()->Add("|");
+ stream()->Add("<s%io%i> %i", from, i, priority);
+ priority++;
+ }
+ }
+ stream()->Add("}}");
+ }
+
+ private:
+ bool first_;
+ StringStream* stream() { return stream_; }
+ StringStream* stream_;
+};
+
+
+class AttributePrinter {
+ public:
+ explicit AttributePrinter(DotPrinter* out)
+ : out_(out), first_(true) { }
+ void PrintSeparator() {
+ if (first_) {
+ first_ = false;
+ } else {
+ out_->stream()->Add("|");
+ }
+ }
+ void PrintBit(const char* name, bool value) {
+ if (!value) return;
+ PrintSeparator();
+ out_->stream()->Add("{%s}", name);
+ }
+ void PrintPositive(const char* name, int value) {
+ if (value < 0) return;
+ PrintSeparator();
+ out_->stream()->Add("{%s|%x}", name, value);
+ }
+ private:
+ DotPrinter* out_;
+ bool first_;
+};
+
+
+void DotPrinter::PrintAttributes(RegExpNode* that) {
+ stream()->Add(" a%p [shape=Mrecord, color=grey, fontcolor=grey, "
+ "margin=0.1, fontsize=10, label=\"{",
+ that);
+ AttributePrinter printer(this);
+ NodeInfo* info = that->info();
+ printer.PrintBit("NI", info->follows_newline_interest);
+ printer.PrintBit("WI", info->follows_word_interest);
+ printer.PrintBit("SI", info->follows_start_interest);
+ Label* label = that->label();
+ if (label->is_bound())
+ printer.PrintPositive("@", label->pos());
+ stream()->Add("}\"];\n");
+ stream()->Add(" a%p -> n%p [style=dashed, color=grey, "
+ "arrowhead=none];\n", that, that);
+}
+
+
+static const bool kPrintDispatchTable = false;
+void DotPrinter::VisitChoice(ChoiceNode* that) {
+ if (kPrintDispatchTable) {
+ stream()->Add(" n%p [shape=Mrecord, label=\"", that);
+ TableEntryHeaderPrinter header_printer(stream());
+ that->GetTable(ignore_case_)->ForEach(&header_printer);
+ stream()->Add("\"]\n", that);
+ PrintAttributes(that);
+ TableEntryBodyPrinter body_printer(stream(), that);
+ that->GetTable(ignore_case_)->ForEach(&body_printer);
+ } else {
+ stream()->Add(" n%p [shape=Mrecord, label=\"?\"];\n", that);
+ for (int i = 0; i < that->alternatives()->length(); i++) {
+ GuardedAlternative alt = that->alternatives()->at(i);
+ stream()->Add(" n%p -> n%p;\n", that, alt.node());
+ }
+ }
+ for (int i = 0; i < that->alternatives()->length(); i++) {
+ GuardedAlternative alt = that->alternatives()->at(i);
+ alt.node()->Accept(this);
+ }
+}
+
+
+void DotPrinter::VisitText(TextNode* that) {
+ Zone* zone = that->zone();
+ stream()->Add(" n%p [label=\"", that);
+ for (int i = 0; i < that->elements()->length(); i++) {
+ if (i > 0) stream()->Add(" ");
+ TextElement elm = that->elements()->at(i);
+ switch (elm.text_type) {
+ case TextElement::ATOM: {
+ stream()->Add("'%w'", elm.data.u_atom->data());
+ break;
+ }
+ case TextElement::CHAR_CLASS: {
+ RegExpCharacterClass* node = elm.data.u_char_class;
+ stream()->Add("[");
+ if (node->is_negated())
+ stream()->Add("^");
+ for (int j = 0; j < node->ranges(zone)->length(); j++) {
+ CharacterRange range = node->ranges(zone)->at(j);
+ stream()->Add("%k-%k", range.from(), range.to());
+ }
+ stream()->Add("]");
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ }
+ stream()->Add("\", shape=box, peripheries=2];\n");
+ PrintAttributes(that);
+ stream()->Add(" n%p -> n%p;\n", that, that->on_success());
+ Visit(that->on_success());
+}
+
+
+void DotPrinter::VisitBackReference(BackReferenceNode* that) {
+ stream()->Add(" n%p [label=\"$%i..$%i\", shape=doubleoctagon];\n",
+ that,
+ that->start_register(),
+ that->end_register());
+ PrintAttributes(that);
+ stream()->Add(" n%p -> n%p;\n", that, that->on_success());
+ Visit(that->on_success());
+}
+
+
+void DotPrinter::VisitEnd(EndNode* that) {
+ stream()->Add(" n%p [style=bold, shape=point];\n", that);
+ PrintAttributes(that);
+}
+
+
+void DotPrinter::VisitAssertion(AssertionNode* that) {
+ stream()->Add(" n%p [", that);
+ switch (that->assertion_type()) {
+ case AssertionNode::AT_END:
+ stream()->Add("label=\"$\", shape=septagon");
+ break;
+ case AssertionNode::AT_START:
+ stream()->Add("label=\"^\", shape=septagon");
+ break;
+ case AssertionNode::AT_BOUNDARY:
+ stream()->Add("label=\"\\b\", shape=septagon");
+ break;
+ case AssertionNode::AT_NON_BOUNDARY:
+ stream()->Add("label=\"\\B\", shape=septagon");
+ break;
+ case AssertionNode::AFTER_NEWLINE:
+ stream()->Add("label=\"(?<=\\n)\", shape=septagon");
+ break;
+ }
+ stream()->Add("];\n");
+ PrintAttributes(that);
+ RegExpNode* successor = that->on_success();
+ stream()->Add(" n%p -> n%p;\n", that, successor);
+ Visit(successor);
+}
+
+
+void DotPrinter::VisitAction(ActionNode* that) {
+ stream()->Add(" n%p [", that);
+ switch (that->action_type_) {
+ case ActionNode::SET_REGISTER:
+ stream()->Add("label=\"$%i:=%i\", shape=octagon",
+ that->data_.u_store_register.reg,
+ that->data_.u_store_register.value);
+ break;
+ case ActionNode::INCREMENT_REGISTER:
+ stream()->Add("label=\"$%i++\", shape=octagon",
+ that->data_.u_increment_register.reg);
+ break;
+ case ActionNode::STORE_POSITION:
+ stream()->Add("label=\"$%i:=$pos\", shape=octagon",
+ that->data_.u_position_register.reg);
+ break;
+ case ActionNode::BEGIN_SUBMATCH:
+ stream()->Add("label=\"$%i:=$pos,begin\", shape=septagon",
+ that->data_.u_submatch.current_position_register);
+ break;
+ case ActionNode::POSITIVE_SUBMATCH_SUCCESS:
+ stream()->Add("label=\"escape\", shape=septagon");
+ break;
+ case ActionNode::EMPTY_MATCH_CHECK:
+ stream()->Add("label=\"$%i=$pos?,$%i<%i?\", shape=septagon",
+ that->data_.u_empty_match_check.start_register,
+ that->data_.u_empty_match_check.repetition_register,
+ that->data_.u_empty_match_check.repetition_limit);
+ break;
+ case ActionNode::CLEAR_CAPTURES: {
+ stream()->Add("label=\"clear $%i to $%i\", shape=septagon",
+ that->data_.u_clear_captures.range_from,
+ that->data_.u_clear_captures.range_to);
+ break;
+ }
+ }
+ stream()->Add("];\n");
+ PrintAttributes(that);
+ RegExpNode* successor = that->on_success();
+ stream()->Add(" n%p -> n%p;\n", that, successor);
+ Visit(successor);
+}
+
+
+class DispatchTableDumper {
+ public:
+ explicit DispatchTableDumper(StringStream* stream) : stream_(stream) { }
+ void Call(uc16 key, DispatchTable::Entry entry);
+ StringStream* stream() { return stream_; }
+ private:
+ StringStream* stream_;
+};
+
+
+void DispatchTableDumper::Call(uc16 key, DispatchTable::Entry entry) {
+ stream()->Add("[%k-%k]: {", key, entry.to());
+ OutSet* set = entry.out_set();
+ bool first = true;
+ for (unsigned i = 0; i < OutSet::kFirstLimit; i++) {
+ if (set->Get(i)) {
+ if (first) {
+ first = false;
+ } else {
+ stream()->Add(", ");
+ }
+ stream()->Add("%i", i);
+ }
+ }
+ stream()->Add("}\n");
+}
+
+
+void DispatchTable::Dump() {
+ HeapStringAllocator alloc;
+ StringStream stream(&alloc);
+ DispatchTableDumper dumper(&stream);
+ tree()->ForEach(&dumper);
+ OS::PrintError("%s", *stream.ToCString());
+}
+
+
+void RegExpEngine::DotPrint(const char* label,
+ RegExpNode* node,
+ bool ignore_case) {
+ DotPrinter printer(ignore_case);
+ printer.PrintNode(label, node);
+}
+
+
+#endif // DEBUG
+
+
+// -------------------------------------------------------------------
+// Tree to graph conversion
+
+RegExpNode* RegExpAtom::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ ZoneList<TextElement>* elms =
+ new(compiler->zone()) ZoneList<TextElement>(1, compiler->zone());
+ elms->Add(TextElement::Atom(this), compiler->zone());
+ return new(compiler->zone()) TextNode(elms, on_success);
+}
+
+
+RegExpNode* RegExpText::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ return new(compiler->zone()) TextNode(elements(), on_success);
+}
+
+
+static bool CompareInverseRanges(ZoneList<CharacterRange>* ranges,
+ const int* special_class,
+ int length) {
+ length--; // Remove final 0x10000.
+ ASSERT(special_class[length] == 0x10000);
+ ASSERT(ranges->length() != 0);
+ ASSERT(length != 0);
+ ASSERT(special_class[0] != 0);
+ if (ranges->length() != (length >> 1) + 1) {
+ return false;
+ }
+ CharacterRange range = ranges->at(0);
+ if (range.from() != 0) {
+ return false;
+ }
+ for (int i = 0; i < length; i += 2) {
+ if (special_class[i] != (range.to() + 1)) {
+ return false;
+ }
+ range = ranges->at((i >> 1) + 1);
+ if (special_class[i+1] != range.from()) {
+ return false;
+ }
+ }
+ if (range.to() != 0xffff) {
+ return false;
+ }
+ return true;
+}
+
+
+static bool CompareRanges(ZoneList<CharacterRange>* ranges,
+ const int* special_class,
+ int length) {
+ length--; // Remove final 0x10000.
+ ASSERT(special_class[length] == 0x10000);
+ if (ranges->length() * 2 != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i += 2) {
+ CharacterRange range = ranges->at(i >> 1);
+ if (range.from() != special_class[i] ||
+ range.to() != special_class[i + 1] - 1) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+bool RegExpCharacterClass::is_standard(Zone* zone) {
+ // TODO(lrn): Remove need for this function, by not throwing away information
+ // along the way.
+ if (is_negated_) {
+ return false;
+ }
+ if (set_.is_standard()) {
+ return true;
+ }
+ if (CompareRanges(set_.ranges(zone), kSpaceRanges, kSpaceRangeCount)) {
+ set_.set_standard_set_type('s');
+ return true;
+ }
+ if (CompareInverseRanges(set_.ranges(zone), kSpaceRanges, kSpaceRangeCount)) {
+ set_.set_standard_set_type('S');
+ return true;
+ }
+ if (CompareInverseRanges(set_.ranges(zone),
+ kLineTerminatorRanges,
+ kLineTerminatorRangeCount)) {
+ set_.set_standard_set_type('.');
+ return true;
+ }
+ if (CompareRanges(set_.ranges(zone),
+ kLineTerminatorRanges,
+ kLineTerminatorRangeCount)) {
+ set_.set_standard_set_type('n');
+ return true;
+ }
+ if (CompareRanges(set_.ranges(zone), kWordRanges, kWordRangeCount)) {
+ set_.set_standard_set_type('w');
+ return true;
+ }
+ if (CompareInverseRanges(set_.ranges(zone), kWordRanges, kWordRangeCount)) {
+ set_.set_standard_set_type('W');
+ return true;
+ }
+ return false;
+}
+
+
+RegExpNode* RegExpCharacterClass::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ return new(compiler->zone()) TextNode(this, on_success);
+}
+
+
+RegExpNode* RegExpDisjunction::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ ZoneList<RegExpTree*>* alternatives = this->alternatives();
+ int length = alternatives->length();
+ ChoiceNode* result =
+ new(compiler->zone()) ChoiceNode(length, compiler->zone());
+ for (int i = 0; i < length; i++) {
+ GuardedAlternative alternative(alternatives->at(i)->ToNode(compiler,
+ on_success));
+ result->AddAlternative(alternative);
+ }
+ return result;
+}
+
+
+RegExpNode* RegExpQuantifier::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ return ToNode(min(),
+ max(),
+ is_greedy(),
+ body(),
+ compiler,
+ on_success);
+}
+
+
+// Scoped object to keep track of how much we unroll quantifier loops in the
+// regexp graph generator.
+class RegExpExpansionLimiter {
+ public:
+ static const int kMaxExpansionFactor = 6;
+ RegExpExpansionLimiter(RegExpCompiler* compiler, int factor)
+ : compiler_(compiler),
+ saved_expansion_factor_(compiler->current_expansion_factor()),
+ ok_to_expand_(saved_expansion_factor_ <= kMaxExpansionFactor) {
+ ASSERT(factor > 0);
+ if (ok_to_expand_) {
+ if (factor > kMaxExpansionFactor) {
+ // Avoid integer overflow of the current expansion factor.
+ ok_to_expand_ = false;
+ compiler->set_current_expansion_factor(kMaxExpansionFactor + 1);
+ } else {
+ int new_factor = saved_expansion_factor_ * factor;
+ ok_to_expand_ = (new_factor <= kMaxExpansionFactor);
+ compiler->set_current_expansion_factor(new_factor);
+ }
+ }
+ }
+
+ ~RegExpExpansionLimiter() {
+ compiler_->set_current_expansion_factor(saved_expansion_factor_);
+ }
+
+ bool ok_to_expand() { return ok_to_expand_; }
+
+ private:
+ RegExpCompiler* compiler_;
+ int saved_expansion_factor_;
+ bool ok_to_expand_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(RegExpExpansionLimiter);
+};
+
+
+RegExpNode* RegExpQuantifier::ToNode(int min,
+ int max,
+ bool is_greedy,
+ RegExpTree* body,
+ RegExpCompiler* compiler,
+ RegExpNode* on_success,
+ bool not_at_start) {
+ // x{f, t} becomes this:
+ //
+ // (r++)<-.
+ // | `
+ // | (x)
+ // v ^
+ // (r=0)-->(?)---/ [if r < t]
+ // |
+ // [if r >= f] \----> ...
+ //
+
+ // 15.10.2.5 RepeatMatcher algorithm.
+ // The parser has already eliminated the case where max is 0. In the case
+ // where max_match is zero the parser has removed the quantifier if min was
+ // > 0 and removed the atom if min was 0. See AddQuantifierToAtom.
+
+ // If we know that we cannot match zero length then things are a little
+ // simpler since we don't need to make the special zero length match check
+ // from step 2.1. If the min and max are small we can unroll a little in
+ // this case.
+ static const int kMaxUnrolledMinMatches = 3; // Unroll (foo)+ and (foo){3,}
+ static const int kMaxUnrolledMaxMatches = 3; // Unroll (foo)? and (foo){x,3}
+ if (max == 0) return on_success; // This can happen due to recursion.
+ bool body_can_be_empty = (body->min_match() == 0);
+ int body_start_reg = RegExpCompiler::kNoRegister;
+ Interval capture_registers = body->CaptureRegisters();
+ bool needs_capture_clearing = !capture_registers.is_empty();
+ Zone* zone = compiler->zone();
+
+ if (body_can_be_empty) {
+ body_start_reg = compiler->AllocateRegister();
+ } else if (FLAG_regexp_optimization && !needs_capture_clearing) {
+ // Only unroll if there are no captures and the body can't be
+ // empty.
+ {
+ RegExpExpansionLimiter limiter(
+ compiler, min + ((max != min) ? 1 : 0));
+ if (min > 0 && min <= kMaxUnrolledMinMatches && limiter.ok_to_expand()) {
+ int new_max = (max == kInfinity) ? max : max - min;
+ // Recurse once to get the loop or optional matches after the fixed
+ // ones.
+ RegExpNode* answer = ToNode(
+ 0, new_max, is_greedy, body, compiler, on_success, true);
+ // Unroll the forced matches from 0 to min. This can cause chains of
+ // TextNodes (which the parser does not generate). These should be
+ // combined if it turns out they hinder good code generation.
+ for (int i = 0; i < min; i++) {
+ answer = body->ToNode(compiler, answer);
+ }
+ return answer;
+ }
+ }
+ if (max <= kMaxUnrolledMaxMatches && min == 0) {
+ ASSERT(max > 0); // Due to the 'if' above.
+ RegExpExpansionLimiter limiter(compiler, max);
+ if (limiter.ok_to_expand()) {
+ // Unroll the optional matches up to max.
+ RegExpNode* answer = on_success;
+ for (int i = 0; i < max; i++) {
+ ChoiceNode* alternation = new(zone) ChoiceNode(2, zone);
+ if (is_greedy) {
+ alternation->AddAlternative(
+ GuardedAlternative(body->ToNode(compiler, answer)));
+ alternation->AddAlternative(GuardedAlternative(on_success));
+ } else {
+ alternation->AddAlternative(GuardedAlternative(on_success));
+ alternation->AddAlternative(
+ GuardedAlternative(body->ToNode(compiler, answer)));
+ }
+ answer = alternation;
+ if (not_at_start) alternation->set_not_at_start();
+ }
+ return answer;
+ }
+ }
+ }
+ bool has_min = min > 0;
+ bool has_max = max < RegExpTree::kInfinity;
+ bool needs_counter = has_min || has_max;
+ int reg_ctr = needs_counter
+ ? compiler->AllocateRegister()
+ : RegExpCompiler::kNoRegister;
+ LoopChoiceNode* center = new(zone) LoopChoiceNode(body->min_match() == 0,
+ zone);
+ if (not_at_start) center->set_not_at_start();
+ RegExpNode* loop_return = needs_counter
+ ? static_cast<RegExpNode*>(ActionNode::IncrementRegister(reg_ctr, center))
+ : static_cast<RegExpNode*>(center);
+ if (body_can_be_empty) {
+ // If the body can be empty we need to check if it was and then
+ // backtrack.
+ loop_return = ActionNode::EmptyMatchCheck(body_start_reg,
+ reg_ctr,
+ min,
+ loop_return);
+ }
+ RegExpNode* body_node = body->ToNode(compiler, loop_return);
+ if (body_can_be_empty) {
+ // If the body can be empty we need to store the start position
+ // so we can bail out if it was empty.
+ body_node = ActionNode::StorePosition(body_start_reg, false, body_node);
+ }
+ if (needs_capture_clearing) {
+ // Before entering the body of this loop we need to clear captures.
+ body_node = ActionNode::ClearCaptures(capture_registers, body_node);
+ }
+ GuardedAlternative body_alt(body_node);
+ if (has_max) {
+ Guard* body_guard =
+ new(zone) Guard(reg_ctr, Guard::LT, max);
+ body_alt.AddGuard(body_guard, zone);
+ }
+ GuardedAlternative rest_alt(on_success);
+ if (has_min) {
+ Guard* rest_guard = new(compiler->zone()) Guard(reg_ctr, Guard::GEQ, min);
+ rest_alt.AddGuard(rest_guard, zone);
+ }
+ if (is_greedy) {
+ center->AddLoopAlternative(body_alt);
+ center->AddContinueAlternative(rest_alt);
+ } else {
+ center->AddContinueAlternative(rest_alt);
+ center->AddLoopAlternative(body_alt);
+ }
+ if (needs_counter) {
+ return ActionNode::SetRegister(reg_ctr, 0, center);
+ } else {
+ return center;
+ }
+}
+
+
+RegExpNode* RegExpAssertion::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ NodeInfo info;
+ Zone* zone = compiler->zone();
+
+ switch (assertion_type()) {
+ case START_OF_LINE:
+ return AssertionNode::AfterNewline(on_success);
+ case START_OF_INPUT:
+ return AssertionNode::AtStart(on_success);
+ case BOUNDARY:
+ return AssertionNode::AtBoundary(on_success);
+ case NON_BOUNDARY:
+ return AssertionNode::AtNonBoundary(on_success);
+ case END_OF_INPUT:
+ return AssertionNode::AtEnd(on_success);
+ case END_OF_LINE: {
+ // Compile $ in multiline regexps as an alternation with a positive
+ // lookahead in one side and an end-of-input on the other side.
+ // We need two registers for the lookahead.
+ int stack_pointer_register = compiler->AllocateRegister();
+ int position_register = compiler->AllocateRegister();
+ // The ChoiceNode to distinguish between a newline and end-of-input.
+ ChoiceNode* result = new(zone) ChoiceNode(2, zone);
+ // Create a newline atom.
+ ZoneList<CharacterRange>* newline_ranges =
+ new(zone) ZoneList<CharacterRange>(3, zone);
+ CharacterRange::AddClassEscape('n', newline_ranges, zone);
+ RegExpCharacterClass* newline_atom = new(zone) RegExpCharacterClass('n');
+ TextNode* newline_matcher = new(zone) TextNode(
+ newline_atom,
+ ActionNode::PositiveSubmatchSuccess(stack_pointer_register,
+ position_register,
+ 0, // No captures inside.
+ -1, // Ignored if no captures.
+ on_success));
+ // Create an end-of-input matcher.
+ RegExpNode* end_of_line = ActionNode::BeginSubmatch(
+ stack_pointer_register,
+ position_register,
+ newline_matcher);
+ // Add the two alternatives to the ChoiceNode.
+ GuardedAlternative eol_alternative(end_of_line);
+ result->AddAlternative(eol_alternative);
+ GuardedAlternative end_alternative(AssertionNode::AtEnd(on_success));
+ result->AddAlternative(end_alternative);
+ return result;
+ }
+ default:
+ UNREACHABLE();
+ }
+ return on_success;
+}
+
+
+RegExpNode* RegExpBackReference::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ return new(compiler->zone())
+ BackReferenceNode(RegExpCapture::StartRegister(index()),
+ RegExpCapture::EndRegister(index()),
+ on_success);
+}
+
+
+RegExpNode* RegExpEmpty::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ return on_success;
+}
+
+
+RegExpNode* RegExpLookahead::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ int stack_pointer_register = compiler->AllocateRegister();
+ int position_register = compiler->AllocateRegister();
+
+ const int registers_per_capture = 2;
+ const int register_of_first_capture = 2;
+ int register_count = capture_count_ * registers_per_capture;
+ int register_start =
+ register_of_first_capture + capture_from_ * registers_per_capture;
+
+ RegExpNode* success;
+ if (is_positive()) {
+ RegExpNode* node = ActionNode::BeginSubmatch(
+ stack_pointer_register,
+ position_register,
+ body()->ToNode(
+ compiler,
+ ActionNode::PositiveSubmatchSuccess(stack_pointer_register,
+ position_register,
+ register_count,
+ register_start,
+ on_success)));
+ return node;
+ } else {
+ // We use a ChoiceNode for a negative lookahead because it has most of
+ // the characteristics we need. It has the body of the lookahead as its
+ // first alternative and the expression after the lookahead of the second
+ // alternative. If the first alternative succeeds then the
+ // NegativeSubmatchSuccess will unwind the stack including everything the
+ // choice node set up and backtrack. If the first alternative fails then
+ // the second alternative is tried, which is exactly the desired result
+ // for a negative lookahead. The NegativeLookaheadChoiceNode is a special
+ // ChoiceNode that knows to ignore the first exit when calculating quick
+ // checks.
+ Zone* zone = compiler->zone();
+
+ GuardedAlternative body_alt(
+ body()->ToNode(
+ compiler,
+ success = new(zone) NegativeSubmatchSuccess(stack_pointer_register,
+ position_register,
+ register_count,
+ register_start,
+ zone)));
+ ChoiceNode* choice_node =
+ new(zone) NegativeLookaheadChoiceNode(body_alt,
+ GuardedAlternative(on_success),
+ zone);
+ return ActionNode::BeginSubmatch(stack_pointer_register,
+ position_register,
+ choice_node);
+ }
+}
+
+
+RegExpNode* RegExpCapture::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ return ToNode(body(), index(), compiler, on_success);
+}
+
+
+RegExpNode* RegExpCapture::ToNode(RegExpTree* body,
+ int index,
+ RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ int start_reg = RegExpCapture::StartRegister(index);
+ int end_reg = RegExpCapture::EndRegister(index);
+ RegExpNode* store_end = ActionNode::StorePosition(end_reg, true, on_success);
+ RegExpNode* body_node = body->ToNode(compiler, store_end);
+ return ActionNode::StorePosition(start_reg, true, body_node);
+}
+
+
+RegExpNode* RegExpAlternative::ToNode(RegExpCompiler* compiler,
+ RegExpNode* on_success) {
+ ZoneList<RegExpTree*>* children = nodes();
+ RegExpNode* current = on_success;
+ for (int i = children->length() - 1; i >= 0; i--) {
+ current = children->at(i)->ToNode(compiler, current);
+ }
+ return current;
+}
+
+
+static void AddClass(const int* elmv,
+ int elmc,
+ ZoneList<CharacterRange>* ranges,
+ Zone* zone) {
+ elmc--;
+ ASSERT(elmv[elmc] == 0x10000);
+ for (int i = 0; i < elmc; i += 2) {
+ ASSERT(elmv[i] < elmv[i + 1]);
+ ranges->Add(CharacterRange(elmv[i], elmv[i + 1] - 1), zone);
+ }
+}
+
+
+static void AddClassNegated(const int *elmv,
+ int elmc,
+ ZoneList<CharacterRange>* ranges,
+ Zone* zone) {
+ elmc--;
+ ASSERT(elmv[elmc] == 0x10000);
+ ASSERT(elmv[0] != 0x0000);
+ ASSERT(elmv[elmc-1] != String::kMaxUtf16CodeUnit);
+ uc16 last = 0x0000;
+ for (int i = 0; i < elmc; i += 2) {
+ ASSERT(last <= elmv[i] - 1);
+ ASSERT(elmv[i] < elmv[i + 1]);
+ ranges->Add(CharacterRange(last, elmv[i] - 1), zone);
+ last = elmv[i + 1];
+ }
+ ranges->Add(CharacterRange(last, String::kMaxUtf16CodeUnit), zone);
+}
+
+
+void CharacterRange::AddClassEscape(uc16 type,
+ ZoneList<CharacterRange>* ranges,
+ Zone* zone) {
+ switch (type) {
+ case 's':
+ AddClass(kSpaceRanges, kSpaceRangeCount, ranges, zone);
+ break;
+ case 'S':
+ AddClassNegated(kSpaceRanges, kSpaceRangeCount, ranges, zone);
+ break;
+ case 'w':
+ AddClass(kWordRanges, kWordRangeCount, ranges, zone);
+ break;
+ case 'W':
+ AddClassNegated(kWordRanges, kWordRangeCount, ranges, zone);
+ break;
+ case 'd':
+ AddClass(kDigitRanges, kDigitRangeCount, ranges, zone);
+ break;
+ case 'D':
+ AddClassNegated(kDigitRanges, kDigitRangeCount, ranges, zone);
+ break;
+ case '.':
+ AddClassNegated(kLineTerminatorRanges,
+ kLineTerminatorRangeCount,
+ ranges,
+ zone);
+ break;
+ // This is not a character range as defined by the spec but a
+ // convenient shorthand for a character class that matches any
+ // character.
+ case '*':
+ ranges->Add(CharacterRange::Everything(), zone);
+ break;
+ // This is the set of characters matched by the $ and ^ symbols
+ // in multiline mode.
+ case 'n':
+ AddClass(kLineTerminatorRanges,
+ kLineTerminatorRangeCount,
+ ranges,
+ zone);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+Vector<const int> CharacterRange::GetWordBounds() {
+ return Vector<const int>(kWordRanges, kWordRangeCount - 1);
+}
+
+
+class CharacterRangeSplitter {
+ public:
+ CharacterRangeSplitter(ZoneList<CharacterRange>** included,
+ ZoneList<CharacterRange>** excluded,
+ Zone* zone)
+ : included_(included),
+ excluded_(excluded),
+ zone_(zone) { }
+ void Call(uc16 from, DispatchTable::Entry entry);
+
+ static const int kInBase = 0;
+ static const int kInOverlay = 1;
+
+ private:
+ ZoneList<CharacterRange>** included_;
+ ZoneList<CharacterRange>** excluded_;
+ Zone* zone_;
+};
+
+
+void CharacterRangeSplitter::Call(uc16 from, DispatchTable::Entry entry) {
+ if (!entry.out_set()->Get(kInBase)) return;
+ ZoneList<CharacterRange>** target = entry.out_set()->Get(kInOverlay)
+ ? included_
+ : excluded_;
+ if (*target == NULL) *target = new(zone_) ZoneList<CharacterRange>(2, zone_);
+ (*target)->Add(CharacterRange(entry.from(), entry.to()), zone_);
+}
+
+
+void CharacterRange::Split(ZoneList<CharacterRange>* base,
+ Vector<const int> overlay,
+ ZoneList<CharacterRange>** included,
+ ZoneList<CharacterRange>** excluded,
+ Zone* zone) {
+ ASSERT_EQ(NULL, *included);
+ ASSERT_EQ(NULL, *excluded);
+ DispatchTable table(zone);
+ for (int i = 0; i < base->length(); i++)
+ table.AddRange(base->at(i), CharacterRangeSplitter::kInBase, zone);
+ for (int i = 0; i < overlay.length(); i += 2) {
+ table.AddRange(CharacterRange(overlay[i], overlay[i + 1] - 1),
+ CharacterRangeSplitter::kInOverlay, zone);
+ }
+ CharacterRangeSplitter callback(included, excluded, zone);
+ table.ForEach(&callback);
+}
+
+
+void CharacterRange::AddCaseEquivalents(ZoneList<CharacterRange>* ranges,
+ bool is_ascii,
+ Zone* zone) {
+ Isolate* isolate = Isolate::Current();
+ uc16 bottom = from();
+ uc16 top = to();
+ if (is_ascii && !RangeContainsLatin1Equivalents(*this)) {
+ if (bottom > String::kMaxOneByteCharCode) return;
+ if (top > String::kMaxOneByteCharCode) top = String::kMaxOneByteCharCode;
+ }
+ unibrow::uchar chars[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ if (top == bottom) {
+ // If this is a singleton we just expand the one character.
+ int length = isolate->jsregexp_uncanonicalize()->get(bottom, '\0', chars);
+ for (int i = 0; i < length; i++) {
+ uc32 chr = chars[i];
+ if (chr != bottom) {
+ ranges->Add(CharacterRange::Singleton(chars[i]), zone);
+ }
+ }
+ } else {
+ // If this is a range we expand the characters block by block,
+ // expanding contiguous subranges (blocks) one at a time.
+ // The approach is as follows. For a given start character we
+ // look up the remainder of the block that contains it (represented
+ // by the end point), for instance we find 'z' if the character
+ // is 'c'. A block is characterized by the property
+ // that all characters uncanonicalize in the same way, except that
+ // each entry in the result is incremented by the distance from the first
+ // element. So a-z is a block because 'a' uncanonicalizes to ['a', 'A'] and
+ // the k'th letter uncanonicalizes to ['a' + k, 'A' + k].
+ // Once we've found the end point we look up its uncanonicalization
+ // and produce a range for each element. For instance for [c-f]
+ // we look up ['z', 'Z'] and produce [c-f] and [C-F]. We then only
+ // add a range if it is not already contained in the input, so [c-f]
+ // will be skipped but [C-F] will be added. If this range is not
+ // completely contained in a block we do this for all the blocks
+ // covered by the range (handling characters that is not in a block
+ // as a "singleton block").
+ unibrow::uchar range[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int pos = bottom;
+ while (pos <= top) {
+ int length = isolate->jsregexp_canonrange()->get(pos, '\0', range);
+ uc16 block_end;
+ if (length == 0) {
+ block_end = pos;
+ } else {
+ ASSERT_EQ(1, length);
+ block_end = range[0];
+ }
+ int end = (block_end > top) ? top : block_end;
+ length = isolate->jsregexp_uncanonicalize()->get(block_end, '\0', range);
+ for (int i = 0; i < length; i++) {
+ uc32 c = range[i];
+ uc16 range_from = c - (block_end - pos);
+ uc16 range_to = c - (block_end - end);
+ if (!(bottom <= range_from && range_to <= top)) {
+ ranges->Add(CharacterRange(range_from, range_to), zone);
+ }
+ }
+ pos = end + 1;
+ }
+ }
+}
+
+
+bool CharacterRange::IsCanonical(ZoneList<CharacterRange>* ranges) {
+ ASSERT_NOT_NULL(ranges);
+ int n = ranges->length();
+ if (n <= 1) return true;
+ int max = ranges->at(0).to();
+ for (int i = 1; i < n; i++) {
+ CharacterRange next_range = ranges->at(i);
+ if (next_range.from() <= max + 1) return false;
+ max = next_range.to();
+ }
+ return true;
+}
+
+
+ZoneList<CharacterRange>* CharacterSet::ranges(Zone* zone) {
+ if (ranges_ == NULL) {
+ ranges_ = new(zone) ZoneList<CharacterRange>(2, zone);
+ CharacterRange::AddClassEscape(standard_set_type_, ranges_, zone);
+ }
+ return ranges_;
+}
+
+
+// Move a number of elements in a zonelist to another position
+// in the same list. Handles overlapping source and target areas.
+static void MoveRanges(ZoneList<CharacterRange>* list,
+ int from,
+ int to,
+ int count) {
+ // Ranges are potentially overlapping.
+ if (from < to) {
+ for (int i = count - 1; i >= 0; i--) {
+ list->at(to + i) = list->at(from + i);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ list->at(to + i) = list->at(from + i);
+ }
+ }
+}
+
+
+static int InsertRangeInCanonicalList(ZoneList<CharacterRange>* list,
+ int count,
+ CharacterRange insert) {
+ // Inserts a range into list[0..count[, which must be sorted
+ // by from value and non-overlapping and non-adjacent, using at most
+ // list[0..count] for the result. Returns the number of resulting
+ // canonicalized ranges. Inserting a range may collapse existing ranges into
+ // fewer ranges, so the return value can be anything in the range 1..count+1.
+ uc16 from = insert.from();
+ uc16 to = insert.to();
+ int start_pos = 0;
+ int end_pos = count;
+ for (int i = count - 1; i >= 0; i--) {
+ CharacterRange current = list->at(i);
+ if (current.from() > to + 1) {
+ end_pos = i;
+ } else if (current.to() + 1 < from) {
+ start_pos = i + 1;
+ break;
+ }
+ }
+
+ // Inserted range overlaps, or is adjacent to, ranges at positions
+ // [start_pos..end_pos[. Ranges before start_pos or at or after end_pos are
+ // not affected by the insertion.
+ // If start_pos == end_pos, the range must be inserted before start_pos.
+ // if start_pos < end_pos, the entire range from start_pos to end_pos
+ // must be merged with the insert range.
+
+ if (start_pos == end_pos) {
+ // Insert between existing ranges at position start_pos.
+ if (start_pos < count) {
+ MoveRanges(list, start_pos, start_pos + 1, count - start_pos);
+ }
+ list->at(start_pos) = insert;
+ return count + 1;
+ }
+ if (start_pos + 1 == end_pos) {
+ // Replace single existing range at position start_pos.
+ CharacterRange to_replace = list->at(start_pos);
+ int new_from = Min(to_replace.from(), from);
+ int new_to = Max(to_replace.to(), to);
+ list->at(start_pos) = CharacterRange(new_from, new_to);
+ return count;
+ }
+ // Replace a number of existing ranges from start_pos to end_pos - 1.
+ // Move the remaining ranges down.
+
+ int new_from = Min(list->at(start_pos).from(), from);
+ int new_to = Max(list->at(end_pos - 1).to(), to);
+ if (end_pos < count) {
+ MoveRanges(list, end_pos, start_pos + 1, count - end_pos);
+ }
+ list->at(start_pos) = CharacterRange(new_from, new_to);
+ return count - (end_pos - start_pos) + 1;
+}
+
+
+void CharacterSet::Canonicalize() {
+ // Special/default classes are always considered canonical. The result
+ // of calling ranges() will be sorted.
+ if (ranges_ == NULL) return;
+ CharacterRange::Canonicalize(ranges_);
+}
+
+
+void CharacterRange::Canonicalize(ZoneList<CharacterRange>* character_ranges) {
+ if (character_ranges->length() <= 1) return;
+ // Check whether ranges are already canonical (increasing, non-overlapping,
+ // non-adjacent).
+ int n = character_ranges->length();
+ int max = character_ranges->at(0).to();
+ int i = 1;
+ while (i < n) {
+ CharacterRange current = character_ranges->at(i);
+ if (current.from() <= max + 1) {
+ break;
+ }
+ max = current.to();
+ i++;
+ }
+ // Canonical until the i'th range. If that's all of them, we are done.
+ if (i == n) return;
+
+ // The ranges at index i and forward are not canonicalized. Make them so by
+ // doing the equivalent of insertion sort (inserting each into the previous
+ // list, in order).
+ // Notice that inserting a range can reduce the number of ranges in the
+ // result due to combining of adjacent and overlapping ranges.
+ int read = i; // Range to insert.
+ int num_canonical = i; // Length of canonicalized part of list.
+ do {
+ num_canonical = InsertRangeInCanonicalList(character_ranges,
+ num_canonical,
+ character_ranges->at(read));
+ read++;
+ } while (read < n);
+ character_ranges->Rewind(num_canonical);
+
+ ASSERT(CharacterRange::IsCanonical(character_ranges));
+}
+
+
+void CharacterRange::Negate(ZoneList<CharacterRange>* ranges,
+ ZoneList<CharacterRange>* negated_ranges,
+ Zone* zone) {
+ ASSERT(CharacterRange::IsCanonical(ranges));
+ ASSERT_EQ(0, negated_ranges->length());
+ int range_count = ranges->length();
+ uc16 from = 0;
+ int i = 0;
+ if (range_count > 0 && ranges->at(0).from() == 0) {
+ from = ranges->at(0).to();
+ i = 1;
+ }
+ while (i < range_count) {
+ CharacterRange range = ranges->at(i);
+ negated_ranges->Add(CharacterRange(from + 1, range.from() - 1), zone);
+ from = range.to();
+ i++;
+ }
+ if (from < String::kMaxUtf16CodeUnit) {
+ negated_ranges->Add(CharacterRange(from + 1, String::kMaxUtf16CodeUnit),
+ zone);
+ }
+}
+
+
+// -------------------------------------------------------------------
+// Splay tree
+
+
+OutSet* OutSet::Extend(unsigned value, Zone* zone) {
+ if (Get(value))
+ return this;
+ if (successors(zone) != NULL) {
+ for (int i = 0; i < successors(zone)->length(); i++) {
+ OutSet* successor = successors(zone)->at(i);
+ if (successor->Get(value))
+ return successor;
+ }
+ } else {
+ successors_ = new(zone) ZoneList<OutSet*>(2, zone);
+ }
+ OutSet* result = new(zone) OutSet(first_, remaining_);
+ result->Set(value, zone);
+ successors(zone)->Add(result, zone);
+ return result;
+}
+
+
+void OutSet::Set(unsigned value, Zone *zone) {
+ if (value < kFirstLimit) {
+ first_ |= (1 << value);
+ } else {
+ if (remaining_ == NULL)
+ remaining_ = new(zone) ZoneList<unsigned>(1, zone);
+ if (remaining_->is_empty() || !remaining_->Contains(value))
+ remaining_->Add(value, zone);
+ }
+}
+
+
+bool OutSet::Get(unsigned value) {
+ if (value < kFirstLimit) {
+ return (first_ & (1 << value)) != 0;
+ } else if (remaining_ == NULL) {
+ return false;
+ } else {
+ return remaining_->Contains(value);
+ }
+}
+
+
+const uc16 DispatchTable::Config::kNoKey = unibrow::Utf8::kBadChar;
+
+
+void DispatchTable::AddRange(CharacterRange full_range, int value,
+ Zone* zone) {
+ CharacterRange current = full_range;
+ if (tree()->is_empty()) {
+ // If this is the first range we just insert into the table.
+ ZoneSplayTree<Config>::Locator loc;
+ ASSERT_RESULT(tree()->Insert(current.from(), &loc));
+ loc.set_value(Entry(current.from(), current.to(),
+ empty()->Extend(value, zone)));
+ return;
+ }
+ // First see if there is a range to the left of this one that
+ // overlaps.
+ ZoneSplayTree<Config>::Locator loc;
+ if (tree()->FindGreatestLessThan(current.from(), &loc)) {
+ Entry* entry = &loc.value();
+ // If we've found a range that overlaps with this one, and it
+ // starts strictly to the left of this one, we have to fix it
+ // because the following code only handles ranges that start on
+ // or after the start point of the range we're adding.
+ if (entry->from() < current.from() && entry->to() >= current.from()) {
+ // Snap the overlapping range in half around the start point of
+ // the range we're adding.
+ CharacterRange left(entry->from(), current.from() - 1);
+ CharacterRange right(current.from(), entry->to());
+ // The left part of the overlapping range doesn't overlap.
+ // Truncate the whole entry to be just the left part.
+ entry->set_to(left.to());
+ // The right part is the one that overlaps. We add this part
+ // to the map and let the next step deal with merging it with
+ // the range we're adding.
+ ZoneSplayTree<Config>::Locator loc;
+ ASSERT_RESULT(tree()->Insert(right.from(), &loc));
+ loc.set_value(Entry(right.from(),
+ right.to(),
+ entry->out_set()));
+ }
+ }
+ while (current.is_valid()) {
+ if (tree()->FindLeastGreaterThan(current.from(), &loc) &&
+ (loc.value().from() <= current.to()) &&
+ (loc.value().to() >= current.from())) {
+ Entry* entry = &loc.value();
+ // We have overlap. If there is space between the start point of
+ // the range we're adding and where the overlapping range starts
+ // then we have to add a range covering just that space.
+ if (current.from() < entry->from()) {
+ ZoneSplayTree<Config>::Locator ins;
+ ASSERT_RESULT(tree()->Insert(current.from(), &ins));
+ ins.set_value(Entry(current.from(),
+ entry->from() - 1,
+ empty()->Extend(value, zone)));
+ current.set_from(entry->from());
+ }
+ ASSERT_EQ(current.from(), entry->from());
+ // If the overlapping range extends beyond the one we want to add
+ // we have to snap the right part off and add it separately.
+ if (entry->to() > current.to()) {
+ ZoneSplayTree<Config>::Locator ins;
+ ASSERT_RESULT(tree()->Insert(current.to() + 1, &ins));
+ ins.set_value(Entry(current.to() + 1,
+ entry->to(),
+ entry->out_set()));
+ entry->set_to(current.to());
+ }
+ ASSERT(entry->to() <= current.to());
+ // The overlapping range is now completely contained by the range
+ // we're adding so we can just update it and move the start point
+ // of the range we're adding just past it.
+ entry->AddValue(value, zone);
+ // Bail out if the last interval ended at 0xFFFF since otherwise
+ // adding 1 will wrap around to 0.
+ if (entry->to() == String::kMaxUtf16CodeUnit)
+ break;
+ ASSERT(entry->to() + 1 > current.from());
+ current.set_from(entry->to() + 1);
+ } else {
+ // There is no overlap so we can just add the range
+ ZoneSplayTree<Config>::Locator ins;
+ ASSERT_RESULT(tree()->Insert(current.from(), &ins));
+ ins.set_value(Entry(current.from(),
+ current.to(),
+ empty()->Extend(value, zone)));
+ break;
+ }
+ }
+}
+
+
+OutSet* DispatchTable::Get(uc16 value) {
+ ZoneSplayTree<Config>::Locator loc;
+ if (!tree()->FindGreatestLessThan(value, &loc))
+ return empty();
+ Entry* entry = &loc.value();
+ if (value <= entry->to())
+ return entry->out_set();
+ else
+ return empty();
+}
+
+
+// -------------------------------------------------------------------
+// Analysis
+
+
+void Analysis::EnsureAnalyzed(RegExpNode* that) {
+ StackLimitCheck check(Isolate::Current());
+ if (check.HasOverflowed()) {
+ fail("Stack overflow");
+ return;
+ }
+ if (that->info()->been_analyzed || that->info()->being_analyzed)
+ return;
+ that->info()->being_analyzed = true;
+ that->Accept(this);
+ that->info()->being_analyzed = false;
+ that->info()->been_analyzed = true;
+}
+
+
+void Analysis::VisitEnd(EndNode* that) {
+ // nothing to do
+}
+
+
+void TextNode::CalculateOffsets() {
+ int element_count = elements()->length();
+ // Set up the offsets of the elements relative to the start. This is a fixed
+ // quantity since a TextNode can only contain fixed-width things.
+ int cp_offset = 0;
+ for (int i = 0; i < element_count; i++) {
+ TextElement& elm = elements()->at(i);
+ elm.cp_offset = cp_offset;
+ if (elm.text_type == TextElement::ATOM) {
+ cp_offset += elm.data.u_atom->data().length();
+ } else {
+ cp_offset++;
+ }
+ }
+}
+
+
+void Analysis::VisitText(TextNode* that) {
+ if (ignore_case_) {
+ that->MakeCaseIndependent(is_ascii_);
+ }
+ EnsureAnalyzed(that->on_success());
+ if (!has_failed()) {
+ that->CalculateOffsets();
+ }
+}
+
+
+void Analysis::VisitAction(ActionNode* that) {
+ RegExpNode* target = that->on_success();
+ EnsureAnalyzed(target);
+ if (!has_failed()) {
+ // If the next node is interested in what it follows then this node
+ // has to be interested too so it can pass the information on.
+ that->info()->AddFromFollowing(target->info());
+ }
+}
+
+
+void Analysis::VisitChoice(ChoiceNode* that) {
+ NodeInfo* info = that->info();
+ for (int i = 0; i < that->alternatives()->length(); i++) {
+ RegExpNode* node = that->alternatives()->at(i).node();
+ EnsureAnalyzed(node);
+ if (has_failed()) return;
+ // Anything the following nodes need to know has to be known by
+ // this node also, so it can pass it on.
+ info->AddFromFollowing(node->info());
+ }
+}
+
+
+void Analysis::VisitLoopChoice(LoopChoiceNode* that) {
+ NodeInfo* info = that->info();
+ for (int i = 0; i < that->alternatives()->length(); i++) {
+ RegExpNode* node = that->alternatives()->at(i).node();
+ if (node != that->loop_node()) {
+ EnsureAnalyzed(node);
+ if (has_failed()) return;
+ info->AddFromFollowing(node->info());
+ }
+ }
+ // Check the loop last since it may need the value of this node
+ // to get a correct result.
+ EnsureAnalyzed(that->loop_node());
+ if (!has_failed()) {
+ info->AddFromFollowing(that->loop_node()->info());
+ }
+}
+
+
+void Analysis::VisitBackReference(BackReferenceNode* that) {
+ EnsureAnalyzed(that->on_success());
+}
+
+
+void Analysis::VisitAssertion(AssertionNode* that) {
+ EnsureAnalyzed(that->on_success());
+}
+
+
+void BackReferenceNode::FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ // Working out the set of characters that a backreference can match is too
+ // hard, so we just say that any character can match.
+ bm->SetRest(offset);
+ SaveBMInfo(bm, not_at_start, offset);
+}
+
+
+STATIC_ASSERT(BoyerMoorePositionInfo::kMapSize ==
+ RegExpMacroAssembler::kTableSize);
+
+
+void ChoiceNode::FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ ZoneList<GuardedAlternative>* alts = alternatives();
+ budget = (budget - 1) / alts->length();
+ for (int i = 0; i < alts->length(); i++) {
+ GuardedAlternative& alt = alts->at(i);
+ if (alt.guards() != NULL && alt.guards()->length() != 0) {
+ bm->SetRest(offset); // Give up trying to fill in info.
+ SaveBMInfo(bm, not_at_start, offset);
+ return;
+ }
+ alt.node()->FillInBMInfo(offset, budget, bm, not_at_start);
+ }
+ SaveBMInfo(bm, not_at_start, offset);
+}
+
+
+void TextNode::FillInBMInfo(int initial_offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ if (initial_offset >= bm->length()) return;
+ int offset = initial_offset;
+ int max_char = bm->max_char();
+ for (int i = 0; i < elements()->length(); i++) {
+ if (offset >= bm->length()) {
+ if (initial_offset == 0) set_bm_info(not_at_start, bm);
+ return;
+ }
+ TextElement text = elements()->at(i);
+ if (text.text_type == TextElement::ATOM) {
+ RegExpAtom* atom = text.data.u_atom;
+ for (int j = 0; j < atom->length(); j++, offset++) {
+ if (offset >= bm->length()) {
+ if (initial_offset == 0) set_bm_info(not_at_start, bm);
+ return;
+ }
+ uc16 character = atom->data()[j];
+ if (bm->compiler()->ignore_case()) {
+ unibrow::uchar chars[unibrow::Ecma262UnCanonicalize::kMaxWidth];
+ int length = GetCaseIndependentLetters(
+ ISOLATE,
+ character,
+ bm->max_char() == String::kMaxOneByteCharCode,
+ chars);
+ for (int j = 0; j < length; j++) {
+ bm->Set(offset, chars[j]);
+ }
+ } else {
+ if (character <= max_char) bm->Set(offset, character);
+ }
+ }
+ } else {
+ ASSERT(text.text_type == TextElement::CHAR_CLASS);
+ RegExpCharacterClass* char_class = text.data.u_char_class;
+ ZoneList<CharacterRange>* ranges = char_class->ranges(zone());
+ if (char_class->is_negated()) {
+ bm->SetAll(offset);
+ } else {
+ for (int k = 0; k < ranges->length(); k++) {
+ CharacterRange& range = ranges->at(k);
+ if (range.from() > max_char) continue;
+ int to = Min(max_char, static_cast<int>(range.to()));
+ bm->SetInterval(offset, Interval(range.from(), to));
+ }
+ }
+ offset++;
+ }
+ }
+ if (offset >= bm->length()) {
+ if (initial_offset == 0) set_bm_info(not_at_start, bm);
+ return;
+ }
+ on_success()->FillInBMInfo(offset,
+ budget - 1,
+ bm,
+ true); // Not at start after a text node.
+ if (initial_offset == 0) set_bm_info(not_at_start, bm);
+}
+
+
+// -------------------------------------------------------------------
+// Dispatch table construction
+
+
+void DispatchTableConstructor::VisitEnd(EndNode* that) {
+ AddRange(CharacterRange::Everything());
+}
+
+
+void DispatchTableConstructor::BuildTable(ChoiceNode* node) {
+ node->set_being_calculated(true);
+ ZoneList<GuardedAlternative>* alternatives = node->alternatives();
+ for (int i = 0; i < alternatives->length(); i++) {
+ set_choice_index(i);
+ alternatives->at(i).node()->Accept(this);
+ }
+ node->set_being_calculated(false);
+}
+
+
+class AddDispatchRange {
+ public:
+ explicit AddDispatchRange(DispatchTableConstructor* constructor)
+ : constructor_(constructor) { }
+ void Call(uc32 from, DispatchTable::Entry entry);
+ private:
+ DispatchTableConstructor* constructor_;
+};
+
+
+void AddDispatchRange::Call(uc32 from, DispatchTable::Entry entry) {
+ CharacterRange range(from, entry.to());
+ constructor_->AddRange(range);
+}
+
+
+void DispatchTableConstructor::VisitChoice(ChoiceNode* node) {
+ if (node->being_calculated())
+ return;
+ DispatchTable* table = node->GetTable(ignore_case_);
+ AddDispatchRange adder(this);
+ table->ForEach(&adder);
+}
+
+
+void DispatchTableConstructor::VisitBackReference(BackReferenceNode* that) {
+ // TODO(160): Find the node that we refer back to and propagate its start
+ // set back to here. For now we just accept anything.
+ AddRange(CharacterRange::Everything());
+}
+
+
+void DispatchTableConstructor::VisitAssertion(AssertionNode* that) {
+ RegExpNode* target = that->on_success();
+ target->Accept(this);
+}
+
+
+static int CompareRangeByFrom(const CharacterRange* a,
+ const CharacterRange* b) {
+ return Compare<uc16>(a->from(), b->from());
+}
+
+
+void DispatchTableConstructor::AddInverse(ZoneList<CharacterRange>* ranges) {
+ ranges->Sort(CompareRangeByFrom);
+ uc16 last = 0;
+ for (int i = 0; i < ranges->length(); i++) {
+ CharacterRange range = ranges->at(i);
+ if (last < range.from())
+ AddRange(CharacterRange(last, range.from() - 1));
+ if (range.to() >= last) {
+ if (range.to() == String::kMaxUtf16CodeUnit) {
+ return;
+ } else {
+ last = range.to() + 1;
+ }
+ }
+ }
+ AddRange(CharacterRange(last, String::kMaxUtf16CodeUnit));
+}
+
+
+void DispatchTableConstructor::VisitText(TextNode* that) {
+ TextElement elm = that->elements()->at(0);
+ switch (elm.text_type) {
+ case TextElement::ATOM: {
+ uc16 c = elm.data.u_atom->data()[0];
+ AddRange(CharacterRange(c, c));
+ break;
+ }
+ case TextElement::CHAR_CLASS: {
+ RegExpCharacterClass* tree = elm.data.u_char_class;
+ ZoneList<CharacterRange>* ranges = tree->ranges(that->zone());
+ if (tree->is_negated()) {
+ AddInverse(ranges);
+ } else {
+ for (int i = 0; i < ranges->length(); i++)
+ AddRange(ranges->at(i));
+ }
+ break;
+ }
+ default: {
+ UNIMPLEMENTED();
+ }
+ }
+}
+
+
+void DispatchTableConstructor::VisitAction(ActionNode* that) {
+ RegExpNode* target = that->on_success();
+ target->Accept(this);
+}
+
+
+RegExpEngine::CompilationResult RegExpEngine::Compile(
+ RegExpCompileData* data,
+ bool ignore_case,
+ bool is_global,
+ bool is_multiline,
+ Handle<String> pattern,
+ Handle<String> sample_subject,
+ bool is_ascii,
+ Zone* zone) {
+ if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) {
+ return IrregexpRegExpTooBig();
+ }
+ RegExpCompiler compiler(data->capture_count, ignore_case, is_ascii, zone);
+
+ // Sample some characters from the middle of the string.
+ static const int kSampleSize = 128;
+
+ FlattenString(sample_subject);
+ int chars_sampled = 0;
+ int half_way = (sample_subject->length() - kSampleSize) / 2;
+ for (int i = Max(0, half_way);
+ i < sample_subject->length() && chars_sampled < kSampleSize;
+ i++, chars_sampled++) {
+ compiler.frequency_collator()->CountCharacter(sample_subject->Get(i));
+ }
+
+ // Wrap the body of the regexp in capture #0.
+ RegExpNode* captured_body = RegExpCapture::ToNode(data->tree,
+ 0,
+ &compiler,
+ compiler.accept());
+ RegExpNode* node = captured_body;
+ bool is_end_anchored = data->tree->IsAnchoredAtEnd();
+ bool is_start_anchored = data->tree->IsAnchoredAtStart();
+ int max_length = data->tree->max_match();
+ if (!is_start_anchored) {
+ // Add a .*? at the beginning, outside the body capture, unless
+ // this expression is anchored at the beginning.
+ RegExpNode* loop_node =
+ RegExpQuantifier::ToNode(0,
+ RegExpTree::kInfinity,
+ false,
+ new(zone) RegExpCharacterClass('*'),
+ &compiler,
+ captured_body,
+ data->contains_anchor);
+
+ if (data->contains_anchor) {
+ // Unroll loop once, to take care of the case that might start
+ // at the start of input.
+ ChoiceNode* first_step_node = new(zone) ChoiceNode(2, zone);
+ first_step_node->AddAlternative(GuardedAlternative(captured_body));
+ first_step_node->AddAlternative(GuardedAlternative(
+ new(zone) TextNode(new(zone) RegExpCharacterClass('*'), loop_node)));
+ node = first_step_node;
+ } else {
+ node = loop_node;
+ }
+ }
+ if (is_ascii) {
+ node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case);
+ // Do it again to propagate the new nodes to places where they were not
+ // put because they had not been calculated yet.
+ if (node != NULL) {
+ node = node->FilterASCII(RegExpCompiler::kMaxRecursion, ignore_case);
+ }
+ }
+
+ if (node == NULL) node = new(zone) EndNode(EndNode::BACKTRACK, zone);
+ data->node = node;
+ Analysis analysis(ignore_case, is_ascii);
+ analysis.EnsureAnalyzed(node);
+ if (analysis.has_failed()) {
+ const char* error_message = analysis.error_message();
+ return CompilationResult(error_message);
+ }
+
+ // Create the correct assembler for the architecture.
+#ifndef V8_INTERPRETED_REGEXP
+ // Native regexp implementation.
+
+ NativeRegExpMacroAssembler::Mode mode =
+ is_ascii ? NativeRegExpMacroAssembler::ASCII
+ : NativeRegExpMacroAssembler::UC16;
+
+#if V8_TARGET_ARCH_IA32
+ RegExpMacroAssemblerIA32 macro_assembler(mode, (data->capture_count + 1) * 2,
+ zone);
+#elif V8_TARGET_ARCH_X64
+ RegExpMacroAssemblerX64 macro_assembler(mode, (data->capture_count + 1) * 2,
+ zone);
+#elif V8_TARGET_ARCH_ARM
+ RegExpMacroAssemblerARM macro_assembler(mode, (data->capture_count + 1) * 2,
+ zone);
+#elif V8_TARGET_ARCH_MIPS
+ RegExpMacroAssemblerMIPS macro_assembler(mode, (data->capture_count + 1) * 2,
+ zone);
+#endif
+
+#else // V8_INTERPRETED_REGEXP
+ // Interpreted regexp implementation.
+ EmbeddedVector<byte, 1024> codes;
+ RegExpMacroAssemblerIrregexp macro_assembler(codes, zone);
+#endif // V8_INTERPRETED_REGEXP
+
+ // Inserted here, instead of in Assembler, because it depends on information
+ // in the AST that isn't replicated in the Node structure.
+ static const int kMaxBacksearchLimit = 1024;
+ if (is_end_anchored &&
+ !is_start_anchored &&
+ max_length < kMaxBacksearchLimit) {
+ macro_assembler.SetCurrentPositionFromEnd(max_length);
+ }
+
+ if (is_global) {
+ macro_assembler.set_global_mode(
+ (data->tree->min_match() > 0)
+ ? RegExpMacroAssembler::GLOBAL_NO_ZERO_LENGTH_CHECK
+ : RegExpMacroAssembler::GLOBAL);
+ }
+
+ return compiler.Assemble(&macro_assembler,
+ node,
+ data->capture_count,
+ pattern);
+}
+
+
+}} // namespace v8::internal
diff --git a/chromium/v8/src/jsregexp.h b/chromium/v8/src/jsregexp.h
new file mode 100644
index 00000000000..20c0ac416f0
--- /dev/null
+++ b/chromium/v8/src/jsregexp.h
@@ -0,0 +1,1624 @@
+// Copyright 2012 the V8 project authors. 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_JSREGEXP_H_
+#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 BoyerMooreLookahead;
+
+class RegExpImpl {
+ public:
+ // Whether V8 is compiled with native regexp support or not.
+ static bool UsesNativeRegExp() {
+#ifdef V8_INTERPRETED_REGEXP
+ return false;
+#else
+ return true;
+#endif
+ }
+
+ // Creates a regular expression literal in the old space.
+ // This function calls the garbage collector if necessary.
+ static Handle<Object> CreateRegExpLiteral(Handle<JSFunction> constructor,
+ Handle<String> pattern,
+ Handle<String> flags,
+ bool* has_pending_exception);
+
+ // Returns a string representation of a regular expression.
+ // Implements RegExp.prototype.toString, see ECMA-262 section 15.10.6.4.
+ // This function calls the garbage collector if necessary.
+ static Handle<String> ToString(Handle<Object> value);
+
+ // Parses the RegExp pattern and prepares the JSRegExp object with
+ // generic data and choice of implementation - as well as what
+ // the implementation wants to store in the data field.
+ // Returns false if compilation fails.
+ static Handle<Object> Compile(Handle<JSRegExp> re,
+ Handle<String> pattern,
+ Handle<String> flags);
+
+ // See ECMA-262 section 15.10.6.2.
+ // This function calls the garbage collector if necessary.
+ static Handle<Object> Exec(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ Handle<JSArray> lastMatchInfo);
+
+ // Prepares a JSRegExp object with Irregexp-specific data.
+ static void IrregexpInitialize(Handle<JSRegExp> re,
+ Handle<String> pattern,
+ JSRegExp::Flags flags,
+ int capture_register_count);
+
+
+ static void AtomCompile(Handle<JSRegExp> re,
+ Handle<String> pattern,
+ JSRegExp::Flags flags,
+ Handle<String> match_pattern);
+
+
+ static int AtomExecRaw(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ int32_t* output,
+ int output_size);
+
+
+ static Handle<Object> AtomExec(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ Handle<JSArray> lastMatchInfo);
+
+ enum IrregexpResult { RE_FAILURE = 0, RE_SUCCESS = 1, RE_EXCEPTION = -1 };
+
+ // Prepare a RegExp for being executed one or more times (using
+ // IrregexpExecOnce) on the subject.
+ // This ensures that the regexp is compiled for the subject, and that
+ // the subject is flat.
+ // Returns the number of integer spaces required by IrregexpExecOnce
+ // as its "registers" argument. If the regexp cannot be compiled,
+ // an exception is set as pending, and this function returns negative.
+ static int IrregexpPrepare(Handle<JSRegExp> regexp,
+ Handle<String> subject);
+
+ // Execute a regular expression on the subject, starting from index.
+ // If matching succeeds, return the number of matches. This can be larger
+ // than one in the case of global regular expressions.
+ // The captures and subcaptures are stored into the registers vector.
+ // If matching fails, returns RE_FAILURE.
+ // If execution fails, sets a pending exception and returns RE_EXCEPTION.
+ static int IrregexpExecRaw(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ int32_t* output,
+ int output_size);
+
+ // Execute an Irregexp bytecode pattern.
+ // On a successful match, the result is a JSArray containing
+ // captured positions. On a failure, the result is the null value.
+ // Returns an empty handle in case of an exception.
+ static Handle<Object> IrregexpExec(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ Handle<JSArray> lastMatchInfo);
+
+ // Set last match info. If match is NULL, then setting captures is omitted.
+ static Handle<JSArray> SetLastMatchInfo(Handle<JSArray> last_match_info,
+ Handle<String> subject,
+ int capture_count,
+ int32_t* match);
+
+
+ class GlobalCache {
+ public:
+ GlobalCache(Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ bool is_global,
+ Isolate* isolate);
+
+ INLINE(~GlobalCache());
+
+ // Fetch the next entry in the cache for global regexp match results.
+ // This does not set the last match info. Upon failure, NULL is returned.
+ // The cause can be checked with Result(). The previous
+ // result is still in available in memory when a failure happens.
+ INLINE(int32_t* FetchNext());
+
+ INLINE(int32_t* LastSuccessfulMatch());
+
+ INLINE(bool HasException()) { return num_matches_ < 0; }
+
+ private:
+ int num_matches_;
+ int max_matches_;
+ int current_match_index_;
+ int registers_per_match_;
+ // Pointer to the last set of captures.
+ int32_t* register_array_;
+ int register_array_size_;
+ Handle<JSRegExp> regexp_;
+ Handle<String> subject_;
+ };
+
+
+ // Array index in the lastMatchInfo array.
+ static const int kLastCaptureCount = 0;
+ static const int kLastSubject = 1;
+ static const int kLastInput = 2;
+ static const int kFirstCapture = 3;
+ static const int kLastMatchOverhead = 3;
+
+ // Direct offset into the lastMatchInfo array.
+ static const int kLastCaptureCountOffset =
+ FixedArray::kHeaderSize + kLastCaptureCount * kPointerSize;
+ static const int kLastSubjectOffset =
+ FixedArray::kHeaderSize + kLastSubject * kPointerSize;
+ static const int kLastInputOffset =
+ FixedArray::kHeaderSize + kLastInput * kPointerSize;
+ static const int kFirstCaptureOffset =
+ FixedArray::kHeaderSize + kFirstCapture * kPointerSize;
+
+ // Used to access the lastMatchInfo array.
+ static int GetCapture(FixedArray* array, int index) {
+ return Smi::cast(array->get(index + kFirstCapture))->value();
+ }
+
+ static void SetLastCaptureCount(FixedArray* array, int to) {
+ array->set(kLastCaptureCount, Smi::FromInt(to));
+ }
+
+ static void SetLastSubject(FixedArray* array, String* to) {
+ array->set(kLastSubject, to);
+ }
+
+ static void SetLastInput(FixedArray* array, String* to) {
+ array->set(kLastInput, to);
+ }
+
+ static void SetCapture(FixedArray* array, int index, int to) {
+ array->set(index + kFirstCapture, Smi::FromInt(to));
+ }
+
+ static int GetLastCaptureCount(FixedArray* array) {
+ return Smi::cast(array->get(kLastCaptureCount))->value();
+ }
+
+ // For acting on the JSRegExp data FixedArray.
+ static int IrregexpMaxRegisterCount(FixedArray* re);
+ static void SetIrregexpMaxRegisterCount(FixedArray* re, int value);
+ static int IrregexpNumberOfCaptures(FixedArray* re);
+ static int IrregexpNumberOfRegisters(FixedArray* re);
+ static ByteArray* IrregexpByteCode(FixedArray* re, bool is_ascii);
+ static Code* IrregexpNativeCode(FixedArray* re, bool is_ascii);
+
+ // Limit the space regexps take up on the heap. In order to limit this we
+ // would like to keep track of the amount of regexp code on the heap. This
+ // is not tracked, however. As a conservative approximation we track the
+ // total regexp code compiled including code that has subsequently been freed
+ // and the total executable memory at any point.
+ static const int kRegExpExecutableMemoryLimit = 16 * MB;
+ static const int kRegWxpCompiledLimit = 1 * MB;
+
+ private:
+ static bool CompileIrregexp(
+ Handle<JSRegExp> re, Handle<String> sample_subject, bool is_ascii);
+ static inline bool EnsureCompiledIrregexp(
+ Handle<JSRegExp> re, Handle<String> sample_subject, bool is_ascii);
+};
+
+
+// Represents the location of one element relative to the intersection of
+// two sets. Corresponds to the four areas of a Venn diagram.
+enum ElementInSetsRelation {
+ kInsideNone = 0,
+ kInsideFirst = 1,
+ kInsideSecond = 2,
+ kInsideBoth = 3
+};
+
+
+// Represents code units in the range from from_ to to_, both ends are
+// inclusive.
+class CharacterRange {
+ public:
+ CharacterRange() : from_(0), to_(0) { }
+ // For compatibility with the CHECK_OK macro
+ CharacterRange(void* null) { ASSERT_EQ(NULL, null); } //NOLINT
+ CharacterRange(uc16 from, uc16 to) : from_(from), to_(to) { }
+ static void AddClassEscape(uc16 type, ZoneList<CharacterRange>* ranges,
+ Zone* zone);
+ static Vector<const int> GetWordBounds();
+ static inline CharacterRange Singleton(uc16 value) {
+ return CharacterRange(value, value);
+ }
+ static inline CharacterRange Range(uc16 from, uc16 to) {
+ ASSERT(from <= to);
+ return CharacterRange(from, to);
+ }
+ static inline CharacterRange Everything() {
+ return CharacterRange(0, 0xFFFF);
+ }
+ bool Contains(uc16 i) { return from_ <= i && i <= to_; }
+ uc16 from() const { return from_; }
+ void set_from(uc16 value) { from_ = value; }
+ uc16 to() const { return to_; }
+ void set_to(uc16 value) { to_ = value; }
+ bool is_valid() { return from_ <= to_; }
+ bool IsEverything(uc16 max) { return from_ == 0 && to_ >= max; }
+ bool IsSingleton() { return (from_ == to_); }
+ void AddCaseEquivalents(ZoneList<CharacterRange>* ranges, bool is_ascii,
+ Zone* zone);
+ static void Split(ZoneList<CharacterRange>* base,
+ Vector<const int> overlay,
+ ZoneList<CharacterRange>** included,
+ ZoneList<CharacterRange>** excluded,
+ Zone* zone);
+ // Whether a range list is in canonical form: Ranges ordered by from value,
+ // and ranges non-overlapping and non-adjacent.
+ static bool IsCanonical(ZoneList<CharacterRange>* ranges);
+ // Convert range list to canonical form. The characters covered by the ranges
+ // will still be the same, but no character is in more than one range, and
+ // adjacent ranges are merged. The resulting list may be shorter than the
+ // original, but cannot be longer.
+ static void Canonicalize(ZoneList<CharacterRange>* ranges);
+ // Negate the contents of a character range in canonical form.
+ static void Negate(ZoneList<CharacterRange>* src,
+ ZoneList<CharacterRange>* dst,
+ Zone* zone);
+ static const int kStartMarker = (1 << 24);
+ static const int kPayloadMask = (1 << 24) - 1;
+
+ private:
+ uc16 from_;
+ uc16 to_;
+};
+
+
+// A set of unsigned integers that behaves especially well on small
+// integers (< 32). May do zone-allocation.
+class OutSet: public ZoneObject {
+ public:
+ OutSet() : first_(0), remaining_(NULL), successors_(NULL) { }
+ OutSet* Extend(unsigned value, Zone* zone);
+ bool Get(unsigned value);
+ static const unsigned kFirstLimit = 32;
+
+ private:
+ // Destructively set a value in this set. In most cases you want
+ // to use Extend instead to ensure that only one instance exists
+ // that contains the same values.
+ void Set(unsigned value, Zone* zone);
+
+ // The successors are a list of sets that contain the same values
+ // as this set and the one more value that is not present in this
+ // set.
+ ZoneList<OutSet*>* successors(Zone* zone) { return successors_; }
+
+ OutSet(uint32_t first, ZoneList<unsigned>* remaining)
+ : first_(first), remaining_(remaining), successors_(NULL) { }
+ uint32_t first_;
+ ZoneList<unsigned>* remaining_;
+ ZoneList<OutSet*>* successors_;
+ friend class Trace;
+};
+
+
+// A mapping from integers, specified as ranges, to a set of integers.
+// Used for mapping character ranges to choices.
+class DispatchTable : public ZoneObject {
+ public:
+ explicit DispatchTable(Zone* zone) : tree_(zone) { }
+
+ class Entry {
+ public:
+ Entry() : from_(0), to_(0), out_set_(NULL) { }
+ Entry(uc16 from, uc16 to, OutSet* out_set)
+ : from_(from), to_(to), out_set_(out_set) { }
+ uc16 from() { return from_; }
+ uc16 to() { return to_; }
+ void set_to(uc16 value) { to_ = value; }
+ void AddValue(int value, Zone* zone) {
+ out_set_ = out_set_->Extend(value, zone);
+ }
+ OutSet* out_set() { return out_set_; }
+ private:
+ uc16 from_;
+ uc16 to_;
+ OutSet* out_set_;
+ };
+
+ class Config {
+ public:
+ typedef uc16 Key;
+ typedef Entry Value;
+ static const uc16 kNoKey;
+ static const Entry NoValue() { return Value(); }
+ static inline int Compare(uc16 a, uc16 b) {
+ if (a == b)
+ return 0;
+ else if (a < b)
+ return -1;
+ else
+ return 1;
+ }
+ };
+
+ void AddRange(CharacterRange range, int value, Zone* zone);
+ OutSet* Get(uc16 value);
+ void Dump();
+
+ template <typename Callback>
+ void ForEach(Callback* callback) {
+ return tree()->ForEach(callback);
+ }
+
+ private:
+ // There can't be a static empty set since it allocates its
+ // successors in a zone and caches them.
+ OutSet* empty() { return &empty_; }
+ OutSet empty_;
+ ZoneSplayTree<Config>* tree() { return &tree_; }
+ ZoneSplayTree<Config> tree_;
+};
+
+
+#define FOR_EACH_NODE_TYPE(VISIT) \
+ VISIT(End) \
+ VISIT(Action) \
+ VISIT(Choice) \
+ VISIT(BackReference) \
+ VISIT(Assertion) \
+ VISIT(Text)
+
+
+#define FOR_EACH_REG_EXP_TREE_TYPE(VISIT) \
+ VISIT(Disjunction) \
+ VISIT(Alternative) \
+ VISIT(Assertion) \
+ VISIT(CharacterClass) \
+ VISIT(Atom) \
+ VISIT(Quantifier) \
+ VISIT(Capture) \
+ VISIT(Lookahead) \
+ VISIT(BackReference) \
+ VISIT(Empty) \
+ VISIT(Text)
+
+
+#define FORWARD_DECLARE(Name) class RegExp##Name;
+FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE)
+#undef FORWARD_DECLARE
+
+
+class TextElement {
+ public:
+ enum TextType {UNINITIALIZED, ATOM, CHAR_CLASS};
+ TextElement() : text_type(UNINITIALIZED) { }
+ explicit TextElement(TextType t) : text_type(t), cp_offset(-1) { }
+ static TextElement Atom(RegExpAtom* atom);
+ static TextElement CharClass(RegExpCharacterClass* char_class);
+ int length();
+ TextType text_type;
+ union {
+ RegExpAtom* u_atom;
+ RegExpCharacterClass* u_char_class;
+ } data;
+ int cp_offset;
+};
+
+
+class Trace;
+
+
+struct NodeInfo {
+ NodeInfo()
+ : being_analyzed(false),
+ been_analyzed(false),
+ follows_word_interest(false),
+ follows_newline_interest(false),
+ follows_start_interest(false),
+ at_end(false),
+ visited(false),
+ replacement_calculated(false) { }
+
+ // Returns true if the interests and assumptions of this node
+ // matches the given one.
+ bool Matches(NodeInfo* that) {
+ return (at_end == that->at_end) &&
+ (follows_word_interest == that->follows_word_interest) &&
+ (follows_newline_interest == that->follows_newline_interest) &&
+ (follows_start_interest == that->follows_start_interest);
+ }
+
+ // Updates the interests of this node given the interests of the
+ // node preceding it.
+ void AddFromPreceding(NodeInfo* that) {
+ at_end |= that->at_end;
+ follows_word_interest |= that->follows_word_interest;
+ follows_newline_interest |= that->follows_newline_interest;
+ follows_start_interest |= that->follows_start_interest;
+ }
+
+ bool HasLookbehind() {
+ return follows_word_interest ||
+ follows_newline_interest ||
+ follows_start_interest;
+ }
+
+ // Sets the interests of this node to include the interests of the
+ // following node.
+ void AddFromFollowing(NodeInfo* that) {
+ follows_word_interest |= that->follows_word_interest;
+ follows_newline_interest |= that->follows_newline_interest;
+ follows_start_interest |= that->follows_start_interest;
+ }
+
+ void ResetCompilationState() {
+ being_analyzed = false;
+ been_analyzed = false;
+ }
+
+ bool being_analyzed: 1;
+ bool been_analyzed: 1;
+
+ // These bits are set of this node has to know what the preceding
+ // character was.
+ bool follows_word_interest: 1;
+ bool follows_newline_interest: 1;
+ bool follows_start_interest: 1;
+
+ bool at_end: 1;
+ bool visited: 1;
+ bool replacement_calculated: 1;
+};
+
+
+// Details of a quick mask-compare check that can look ahead in the
+// input stream.
+class QuickCheckDetails {
+ public:
+ QuickCheckDetails()
+ : characters_(0),
+ mask_(0),
+ value_(0),
+ cannot_match_(false) { }
+ explicit QuickCheckDetails(int characters)
+ : characters_(characters),
+ mask_(0),
+ value_(0),
+ cannot_match_(false) { }
+ bool Rationalize(bool ascii);
+ // Merge in the information from another branch of an alternation.
+ void Merge(QuickCheckDetails* other, int from_index);
+ // Advance the current position by some amount.
+ void Advance(int by, bool ascii);
+ void Clear();
+ bool cannot_match() { return cannot_match_; }
+ void set_cannot_match() { cannot_match_ = true; }
+ struct Position {
+ Position() : mask(0), value(0), determines_perfectly(false) { }
+ uc16 mask;
+ uc16 value;
+ bool determines_perfectly;
+ };
+ int characters() { return characters_; }
+ void set_characters(int characters) { characters_ = characters; }
+ Position* positions(int index) {
+ ASSERT(index >= 0);
+ ASSERT(index < characters_);
+ return positions_ + index;
+ }
+ uint32_t mask() { return mask_; }
+ uint32_t value() { return value_; }
+
+ private:
+ // How many characters do we have quick check information from. This is
+ // the same for all branches of a choice node.
+ int characters_;
+ Position positions_[4];
+ // These values are the condensate of the above array after Rationalize().
+ uint32_t mask_;
+ uint32_t value_;
+ // If set to true, there is no way this quick check can match at all.
+ // E.g., if it requires to be at the start of the input, and isn't.
+ bool cannot_match_;
+};
+
+
+extern int kUninitializedRegExpNodePlaceHolder;
+
+
+class RegExpNode: public ZoneObject {
+ public:
+ explicit RegExpNode(Zone* zone)
+ : replacement_(NULL), trace_count_(0), zone_(zone) {
+ bm_info_[0] = bm_info_[1] = NULL;
+ }
+ virtual ~RegExpNode();
+ virtual void Accept(NodeVisitor* visitor) = 0;
+ // Generates a goto to this node or actually generates the code at this point.
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace) = 0;
+ // How many characters must this node consume at a minimum in order to
+ // succeed. If we have found at least 'still_to_find' characters that
+ // must be consumed there is no need to ask any following nodes whether
+ // they are sure to eat any more characters. The not_at_start argument is
+ // used to indicate that we know we are not at the start of the input. In
+ // this case anchored branches will always fail and can be ignored when
+ // determining how many characters are consumed on success.
+ virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start) = 0;
+ // Emits some quick code that checks whether the preloaded characters match.
+ // Falls through on certain failure, jumps to the label on possible success.
+ // If the node cannot make a quick check it does nothing and returns false.
+ bool EmitQuickCheck(RegExpCompiler* compiler,
+ Trace* trace,
+ bool preload_has_checked_bounds,
+ Label* on_possible_success,
+ QuickCheckDetails* details_return,
+ bool fall_through_on_failure);
+ // For a given number of characters this returns a mask and a value. The
+ // next n characters are anded with the mask and compared with the value.
+ // A comparison failure indicates the node cannot match the next n characters.
+ // A comparison success indicates the node may match.
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start) = 0;
+ static const int kNodeIsTooComplexForGreedyLoops = -1;
+ virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; }
+ // Only returns the successor for a text node of length 1 that matches any
+ // character and that has no guards on it.
+ virtual RegExpNode* GetSuccessorOfOmnivorousTextNode(
+ RegExpCompiler* compiler) {
+ return NULL;
+ }
+
+ // Collects information on the possible code units (mod 128) that can match if
+ // we look forward. This is used for a Boyer-Moore-like string searching
+ // implementation. TODO(erikcorry): This should share more code with
+ // EatsAtLeast, GetQuickCheckDetails. The budget argument is used to limit
+ // the number of nodes we are willing to look at in order to create this data.
+ static const int kRecursionBudget = 200;
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ UNREACHABLE();
+ }
+
+ // If we know that the input is ASCII then there are some nodes that can
+ // never match. This method returns a node that can be substituted for
+ // itself, or NULL if the node can never match.
+ virtual RegExpNode* FilterASCII(int depth, bool ignore_case) { return this; }
+ // Helper for FilterASCII.
+ RegExpNode* replacement() {
+ ASSERT(info()->replacement_calculated);
+ return replacement_;
+ }
+ RegExpNode* set_replacement(RegExpNode* replacement) {
+ info()->replacement_calculated = true;
+ replacement_ = replacement;
+ return replacement; // For convenience.
+ }
+
+ // We want to avoid recalculating the lookahead info, so we store it on the
+ // node. Only info that is for this node is stored. We can tell that the
+ // info is for this node when offset == 0, so the information is calculated
+ // relative to this node.
+ void SaveBMInfo(BoyerMooreLookahead* bm, bool not_at_start, int offset) {
+ if (offset == 0) set_bm_info(not_at_start, bm);
+ }
+
+ Label* label() { return &label_; }
+ // 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
+ // the deferred actions in the current trace and generating a goto.
+ static const int kMaxCopiesCodeGenerated = 10;
+
+ NodeInfo* info() { return &info_; }
+
+ BoyerMooreLookahead* bm_info(bool not_at_start) {
+ return bm_info_[not_at_start ? 1 : 0];
+ }
+
+ Zone* zone() const { return zone_; }
+
+ protected:
+ enum LimitResult { DONE, CONTINUE };
+ RegExpNode* replacement_;
+
+ LimitResult LimitVersions(RegExpCompiler* compiler, Trace* trace);
+
+ void set_bm_info(bool not_at_start, BoyerMooreLookahead* bm) {
+ bm_info_[not_at_start ? 1 : 0] = bm;
+ }
+
+ private:
+ static const int kFirstCharBudget = 10;
+ Label label_;
+ NodeInfo info_;
+ // This variable keeps track of how many times code has been generated for
+ // this node (in different traces). We don't keep track of where the
+ // generated code is located unless the code is generated at the start of
+ // a trace, in which case it is generic and can be reused by flushing the
+ // deferred operations in the current trace and generating a goto.
+ int trace_count_;
+ BoyerMooreLookahead* bm_info_[2];
+
+ Zone* zone_;
+};
+
+
+// A simple closed interval.
+class Interval {
+ public:
+ Interval() : from_(kNone), to_(kNone) { }
+ Interval(int from, int to) : from_(from), to_(to) { }
+ Interval Union(Interval that) {
+ if (that.from_ == kNone)
+ return *this;
+ else if (from_ == kNone)
+ return that;
+ else
+ return Interval(Min(from_, that.from_), Max(to_, that.to_));
+ }
+ bool Contains(int value) {
+ return (from_ <= value) && (value <= to_);
+ }
+ bool is_empty() { return from_ == kNone; }
+ int from() const { return from_; }
+ int to() const { return to_; }
+ static Interval Empty() { return Interval(); }
+ static const int kNone = -1;
+ private:
+ int from_;
+ int to_;
+};
+
+
+class SeqRegExpNode: public RegExpNode {
+ public:
+ explicit SeqRegExpNode(RegExpNode* on_success)
+ : RegExpNode(on_success->zone()), on_success_(on_success) { }
+ RegExpNode* on_success() { return on_success_; }
+ void set_on_success(RegExpNode* node) { on_success_ = node; }
+ virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ on_success_->FillInBMInfo(offset, budget - 1, bm, not_at_start);
+ if (offset == 0) set_bm_info(not_at_start, bm);
+ }
+
+ protected:
+ RegExpNode* FilterSuccessor(int depth, bool ignore_case);
+
+ private:
+ RegExpNode* on_success_;
+};
+
+
+class ActionNode: public SeqRegExpNode {
+ public:
+ enum ActionType {
+ SET_REGISTER,
+ INCREMENT_REGISTER,
+ STORE_POSITION,
+ BEGIN_SUBMATCH,
+ POSITIVE_SUBMATCH_SUCCESS,
+ EMPTY_MATCH_CHECK,
+ CLEAR_CAPTURES
+ };
+ static ActionNode* SetRegister(int reg, int val, RegExpNode* on_success);
+ static ActionNode* IncrementRegister(int reg, RegExpNode* on_success);
+ static ActionNode* StorePosition(int reg,
+ bool is_capture,
+ RegExpNode* on_success);
+ static ActionNode* ClearCaptures(Interval range, RegExpNode* on_success);
+ static ActionNode* BeginSubmatch(int stack_pointer_reg,
+ int position_reg,
+ RegExpNode* on_success);
+ static ActionNode* PositiveSubmatchSuccess(int stack_pointer_reg,
+ int restore_reg,
+ int clear_capture_count,
+ int clear_capture_from,
+ RegExpNode* on_success);
+ static ActionNode* EmptyMatchCheck(int start_register,
+ int repetition_register,
+ int repetition_limit,
+ RegExpNode* on_success);
+ virtual void Accept(NodeVisitor* visitor);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int filled_in,
+ bool not_at_start) {
+ return on_success()->GetQuickCheckDetails(
+ details, compiler, filled_in, not_at_start);
+ }
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start);
+ ActionType action_type() { return action_type_; }
+ // TODO(erikcorry): We should allow some action nodes in greedy loops.
+ virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; }
+
+ private:
+ union {
+ struct {
+ int reg;
+ int value;
+ } u_store_register;
+ struct {
+ int reg;
+ } u_increment_register;
+ struct {
+ int reg;
+ bool is_capture;
+ } u_position_register;
+ struct {
+ int stack_pointer_register;
+ int current_position_register;
+ int clear_register_count;
+ int clear_register_from;
+ } u_submatch;
+ struct {
+ int start_register;
+ int repetition_register;
+ int repetition_limit;
+ } u_empty_match_check;
+ struct {
+ int range_from;
+ int range_to;
+ } u_clear_captures;
+ } data_;
+ ActionNode(ActionType action_type, RegExpNode* on_success)
+ : SeqRegExpNode(on_success),
+ action_type_(action_type) { }
+ ActionType action_type_;
+ friend class DotPrinter;
+};
+
+
+class TextNode: public SeqRegExpNode {
+ public:
+ TextNode(ZoneList<TextElement>* elms,
+ RegExpNode* on_success)
+ : SeqRegExpNode(on_success),
+ elms_(elms) { }
+ TextNode(RegExpCharacterClass* that,
+ RegExpNode* on_success)
+ : SeqRegExpNode(on_success),
+ elms_(new(zone()) ZoneList<TextElement>(1, zone())) {
+ elms_->Add(TextElement::CharClass(that), zone());
+ }
+ virtual void Accept(NodeVisitor* visitor);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start);
+ ZoneList<TextElement>* elements() { return elms_; }
+ void MakeCaseIndependent(bool is_ascii);
+ virtual int GreedyLoopTextLength();
+ virtual RegExpNode* GetSuccessorOfOmnivorousTextNode(
+ RegExpCompiler* compiler);
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start);
+ void CalculateOffsets();
+ virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
+
+ private:
+ enum TextEmitPassType {
+ NON_ASCII_MATCH, // Check for characters that can't match.
+ SIMPLE_CHARACTER_MATCH, // Case-dependent single character check.
+ NON_LETTER_CHARACTER_MATCH, // Check characters that have no case equivs.
+ CASE_CHARACTER_MATCH, // Case-independent single character check.
+ CHARACTER_CLASS_MATCH // Character class.
+ };
+ static bool SkipPass(int pass, bool ignore_case);
+ static const int kFirstRealPass = SIMPLE_CHARACTER_MATCH;
+ static const int kLastPass = CHARACTER_CLASS_MATCH;
+ void TextEmitPass(RegExpCompiler* compiler,
+ TextEmitPassType pass,
+ bool preloaded,
+ Trace* trace,
+ bool first_element_checked,
+ int* checked_up_to);
+ int Length();
+ ZoneList<TextElement>* elms_;
+};
+
+
+class AssertionNode: public SeqRegExpNode {
+ public:
+ enum AssertionType {
+ AT_END,
+ AT_START,
+ AT_BOUNDARY,
+ AT_NON_BOUNDARY,
+ AFTER_NEWLINE
+ };
+ static AssertionNode* AtEnd(RegExpNode* on_success) {
+ return new(on_success->zone()) AssertionNode(AT_END, on_success);
+ }
+ static AssertionNode* AtStart(RegExpNode* on_success) {
+ return new(on_success->zone()) AssertionNode(AT_START, on_success);
+ }
+ static AssertionNode* AtBoundary(RegExpNode* on_success) {
+ return new(on_success->zone()) AssertionNode(AT_BOUNDARY, on_success);
+ }
+ static AssertionNode* AtNonBoundary(RegExpNode* on_success) {
+ return new(on_success->zone()) AssertionNode(AT_NON_BOUNDARY, on_success);
+ }
+ static AssertionNode* AfterNewline(RegExpNode* on_success) {
+ return new(on_success->zone()) AssertionNode(AFTER_NEWLINE, on_success);
+ }
+ virtual void Accept(NodeVisitor* visitor);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int filled_in,
+ bool not_at_start);
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start);
+ AssertionType assertion_type() { return assertion_type_; }
+
+ private:
+ void EmitBoundaryCheck(RegExpCompiler* compiler, Trace* trace);
+ enum IfPrevious { kIsNonWord, kIsWord };
+ void BacktrackIfPrevious(RegExpCompiler* compiler,
+ Trace* trace,
+ IfPrevious backtrack_if_previous);
+ AssertionNode(AssertionType t, RegExpNode* on_success)
+ : SeqRegExpNode(on_success), assertion_type_(t) { }
+ AssertionType assertion_type_;
+};
+
+
+class BackReferenceNode: public SeqRegExpNode {
+ public:
+ BackReferenceNode(int start_reg,
+ int end_reg,
+ RegExpNode* on_success)
+ : SeqRegExpNode(on_success),
+ start_reg_(start_reg),
+ end_reg_(end_reg) { }
+ virtual void Accept(NodeVisitor* visitor);
+ int start_register() { return start_reg_; }
+ int end_register() { return end_reg_; }
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find,
+ int recursion_depth,
+ bool not_at_start);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start) {
+ return;
+ }
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start);
+
+ private:
+ int start_reg_;
+ int end_reg_;
+};
+
+
+class EndNode: public RegExpNode {
+ public:
+ enum Action { ACCEPT, BACKTRACK, NEGATIVE_SUBMATCH_SUCCESS };
+ explicit EndNode(Action action, Zone* zone)
+ : RegExpNode(zone), action_(action) { }
+ virtual void Accept(NodeVisitor* visitor);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find,
+ int recursion_depth,
+ bool not_at_start) { return 0; }
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start) {
+ // Returning 0 from EatsAtLeast should ensure we never get here.
+ UNREACHABLE();
+ }
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ // Returning 0 from EatsAtLeast should ensure we never get here.
+ UNREACHABLE();
+ }
+
+ private:
+ Action action_;
+};
+
+
+class NegativeSubmatchSuccess: public EndNode {
+ public:
+ NegativeSubmatchSuccess(int stack_pointer_reg,
+ int position_reg,
+ int clear_capture_count,
+ int clear_capture_start,
+ Zone* zone)
+ : EndNode(NEGATIVE_SUBMATCH_SUCCESS, zone),
+ stack_pointer_register_(stack_pointer_reg),
+ current_position_register_(position_reg),
+ clear_capture_count_(clear_capture_count),
+ clear_capture_start_(clear_capture_start) { }
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+
+ private:
+ int stack_pointer_register_;
+ int current_position_register_;
+ int clear_capture_count_;
+ int clear_capture_start_;
+};
+
+
+class Guard: public ZoneObject {
+ public:
+ enum Relation { LT, GEQ };
+ Guard(int reg, Relation op, int value)
+ : reg_(reg),
+ op_(op),
+ value_(value) { }
+ int reg() { return reg_; }
+ Relation op() { return op_; }
+ int value() { return value_; }
+
+ private:
+ int reg_;
+ Relation op_;
+ int value_;
+};
+
+
+class GuardedAlternative {
+ public:
+ explicit GuardedAlternative(RegExpNode* node) : node_(node), guards_(NULL) { }
+ void AddGuard(Guard* guard, Zone* zone);
+ RegExpNode* node() { return node_; }
+ void set_node(RegExpNode* node) { node_ = node; }
+ ZoneList<Guard*>* guards() { return guards_; }
+
+ private:
+ RegExpNode* node_;
+ ZoneList<Guard*>* guards_;
+};
+
+
+class AlternativeGeneration;
+
+
+class ChoiceNode: public RegExpNode {
+ public:
+ explicit ChoiceNode(int expected_size, Zone* zone)
+ : RegExpNode(zone),
+ alternatives_(new(zone)
+ ZoneList<GuardedAlternative>(expected_size, zone)),
+ table_(NULL),
+ not_at_start_(false),
+ being_calculated_(false) { }
+ virtual void Accept(NodeVisitor* visitor);
+ void AddAlternative(GuardedAlternative node) {
+ alternatives()->Add(node, zone());
+ }
+ ZoneList<GuardedAlternative>* alternatives() { return alternatives_; }
+ DispatchTable* GetTable(bool ignore_case);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
+ int EatsAtLeastHelper(int still_to_find,
+ int budget,
+ RegExpNode* ignore_this_node,
+ bool not_at_start);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start);
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start);
+
+ bool being_calculated() { return being_calculated_; }
+ bool not_at_start() { return not_at_start_; }
+ void set_not_at_start() { not_at_start_ = true; }
+ void set_being_calculated(bool b) { being_calculated_ = b; }
+ virtual bool try_to_emit_quick_check_for_alternative(int i) { return true; }
+ virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
+
+ protected:
+ int GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative);
+ ZoneList<GuardedAlternative>* alternatives_;
+
+ private:
+ friend class DispatchTableConstructor;
+ friend class Analysis;
+ void GenerateGuard(RegExpMacroAssembler* macro_assembler,
+ Guard* guard,
+ Trace* trace);
+ int CalculatePreloadCharacters(RegExpCompiler* compiler, int eats_at_least);
+ void EmitOutOfLineContinuation(RegExpCompiler* compiler,
+ Trace* trace,
+ GuardedAlternative alternative,
+ AlternativeGeneration* alt_gen,
+ int preload_characters,
+ bool next_expects_preload);
+ DispatchTable* table_;
+ // If true, this node is never checked at the start of the input.
+ // Allows a new trace to start with at_start() set to false.
+ bool not_at_start_;
+ bool being_calculated_;
+};
+
+
+class NegativeLookaheadChoiceNode: public ChoiceNode {
+ public:
+ explicit NegativeLookaheadChoiceNode(GuardedAlternative this_must_fail,
+ GuardedAlternative then_do_this,
+ Zone* zone)
+ : ChoiceNode(2, zone) {
+ AddAlternative(this_must_fail);
+ AddAlternative(then_do_this);
+ }
+ virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start);
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start) {
+ alternatives_->at(1).node()->FillInBMInfo(
+ offset, budget - 1, bm, not_at_start);
+ if (offset == 0) set_bm_info(not_at_start, bm);
+ }
+ // For a negative lookahead we don't emit the quick check for the
+ // alternative that is expected to fail. This is because quick check code
+ // starts by loading enough characters for the alternative that takes fewest
+ // characters, but on a negative lookahead the negative branch did not take
+ // part in that calculation (EatsAtLeast) so the assumptions don't hold.
+ virtual bool try_to_emit_quick_check_for_alternative(int i) { return i != 0; }
+ virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
+};
+
+
+class LoopChoiceNode: public ChoiceNode {
+ public:
+ explicit LoopChoiceNode(bool body_can_be_zero_length, Zone* zone)
+ : ChoiceNode(2, zone),
+ loop_node_(NULL),
+ continue_node_(NULL),
+ body_can_be_zero_length_(body_can_be_zero_length) { }
+ void AddLoopAlternative(GuardedAlternative alt);
+ void AddContinueAlternative(GuardedAlternative alt);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int budget, bool not_at_start);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in,
+ bool not_at_start);
+ virtual void FillInBMInfo(int offset,
+ int budget,
+ BoyerMooreLookahead* bm,
+ bool not_at_start);
+ RegExpNode* loop_node() { return loop_node_; }
+ RegExpNode* continue_node() { return continue_node_; }
+ bool body_can_be_zero_length() { return body_can_be_zero_length_; }
+ virtual void Accept(NodeVisitor* visitor);
+ virtual RegExpNode* FilterASCII(int depth, bool ignore_case);
+
+ private:
+ // AddAlternative is made private for loop nodes because alternatives
+ // should not be added freely, we need to keep track of which node
+ // goes back to the node itself.
+ void AddAlternative(GuardedAlternative node) {
+ ChoiceNode::AddAlternative(node);
+ }
+
+ RegExpNode* loop_node_;
+ RegExpNode* continue_node_;
+ bool body_can_be_zero_length_;
+};
+
+
+// Improve the speed that we scan for an initial point where a non-anchored
+// regexp can match by using a Boyer-Moore-like table. This is done by
+// identifying non-greedy non-capturing loops in the nodes that eat any
+// character one at a time. For example in the middle of the regexp
+// /foo[\s\S]*?bar/ we find such a loop. There is also such a loop implicitly
+// inserted at the start of any non-anchored regexp.
+//
+// When we have found such a loop we look ahead in the nodes to find the set of
+// characters that can come at given distances. For example for the regexp
+// /.?foo/ we know that there are at least 3 characters ahead of us, and the
+// sets of characters that can occur are [any, [f, o], [o]]. We find a range in
+// the lookahead info where the set of characters is reasonably constrained. In
+// our example this is from index 1 to 2 (0 is not constrained). We can now
+// look 3 characters ahead and if we don't find one of [f, o] (the union of
+// [f, o] and [o]) then we can skip forwards by the range size (in this case 2).
+//
+// For Unicode input strings we do the same, but modulo 128.
+//
+// We also look at the first string fed to the regexp and use that to get a hint
+// of the character frequencies in the inputs. This affects the assessment of
+// whether the set of characters is 'reasonably constrained'.
+//
+// We also have another lookahead mechanism (called quick check in the code),
+// which uses a wide load of multiple characters followed by a mask and compare
+// to determine whether a match is possible at this point.
+enum ContainedInLattice {
+ kNotYet = 0,
+ kLatticeIn = 1,
+ kLatticeOut = 2,
+ kLatticeUnknown = 3 // Can also mean both in and out.
+};
+
+
+inline ContainedInLattice Combine(ContainedInLattice a, ContainedInLattice b) {
+ return static_cast<ContainedInLattice>(a | b);
+}
+
+
+ContainedInLattice AddRange(ContainedInLattice a,
+ const int* ranges,
+ int ranges_size,
+ Interval new_range);
+
+
+class BoyerMoorePositionInfo : public ZoneObject {
+ public:
+ explicit BoyerMoorePositionInfo(Zone* zone)
+ : map_(new(zone) ZoneList<bool>(kMapSize, zone)),
+ map_count_(0),
+ w_(kNotYet),
+ s_(kNotYet),
+ d_(kNotYet),
+ surrogate_(kNotYet) {
+ for (int i = 0; i < kMapSize; i++) {
+ map_->Add(false, zone);
+ }
+ }
+
+ bool& at(int i) { return map_->at(i); }
+
+ static const int kMapSize = 128;
+ static const int kMask = kMapSize - 1;
+
+ int map_count() const { return map_count_; }
+
+ void Set(int character);
+ void SetInterval(const Interval& interval);
+ void SetAll();
+ bool is_non_word() { return w_ == kLatticeOut; }
+ bool is_word() { return w_ == kLatticeIn; }
+
+ private:
+ ZoneList<bool>* map_;
+ int map_count_; // Number of set bits in the map.
+ ContainedInLattice w_; // The \w character class.
+ ContainedInLattice s_; // The \s character class.
+ ContainedInLattice d_; // The \d character class.
+ ContainedInLattice surrogate_; // Surrogate UTF-16 code units.
+};
+
+
+class BoyerMooreLookahead : public ZoneObject {
+ public:
+ BoyerMooreLookahead(int length, RegExpCompiler* compiler, Zone* zone);
+
+ int length() { return length_; }
+ int max_char() { return max_char_; }
+ RegExpCompiler* compiler() { return compiler_; }
+
+ int Count(int map_number) {
+ return bitmaps_->at(map_number)->map_count();
+ }
+
+ BoyerMoorePositionInfo* at(int i) { return bitmaps_->at(i); }
+
+ void Set(int map_number, int character) {
+ if (character > max_char_) return;
+ BoyerMoorePositionInfo* info = bitmaps_->at(map_number);
+ info->Set(character);
+ }
+
+ void SetInterval(int map_number, const Interval& interval) {
+ if (interval.from() > max_char_) return;
+ BoyerMoorePositionInfo* info = bitmaps_->at(map_number);
+ if (interval.to() > max_char_) {
+ info->SetInterval(Interval(interval.from(), max_char_));
+ } else {
+ info->SetInterval(interval);
+ }
+ }
+
+ void SetAll(int map_number) {
+ bitmaps_->at(map_number)->SetAll();
+ }
+
+ void SetRest(int from_map) {
+ for (int i = from_map; i < length_; i++) SetAll(i);
+ }
+ bool EmitSkipInstructions(RegExpMacroAssembler* masm);
+
+ private:
+ // This is the value obtained by EatsAtLeast. If we do not have at least this
+ // many characters left in the sample string then the match is bound to fail.
+ // Therefore it is OK to read a character this far ahead of the current match
+ // point.
+ int length_;
+ RegExpCompiler* compiler_;
+ // 0x7f for ASCII, 0xffff for UTF-16.
+ int max_char_;
+ ZoneList<BoyerMoorePositionInfo*>* bitmaps_;
+
+ int GetSkipTable(int min_lookahead,
+ int max_lookahead,
+ Handle<ByteArray> boolean_skip_table);
+ bool FindWorthwhileInterval(int* from, int* to);
+ int FindBestInterval(
+ int max_number_of_chars, int old_biggest_points, int* from, int* to);
+};
+
+
+// There are many ways to generate code for a node. This class encapsulates
+// the current way we should be generating. In other words it encapsulates
+// the current state of the code generator. The effect of this is that we
+// generate code for paths that the matcher can take through the regular
+// expression. A given node in the regexp can be code-generated several times
+// as it can be part of several traces. For example for the regexp:
+// /foo(bar|ip)baz/ the code to match baz will be generated twice, once as part
+// of the foo-bar-baz trace and once as part of the foo-ip-baz trace. The code
+// to match foo is generated only once (the traces have a common prefix). The
+// code to store the capture is deferred and generated (twice) after the places
+// where baz has been matched.
+class Trace {
+ public:
+ // A value for a property that is either known to be true, know to be false,
+ // or not known.
+ enum TriBool {
+ UNKNOWN = -1, FALSE_VALUE = 0, TRUE_VALUE = 1
+ };
+
+ class DeferredAction {
+ public:
+ DeferredAction(ActionNode::ActionType action_type, int reg)
+ : action_type_(action_type), reg_(reg), next_(NULL) { }
+ DeferredAction* next() { return next_; }
+ bool Mentions(int reg);
+ int reg() { return reg_; }
+ ActionNode::ActionType action_type() { return action_type_; }
+ private:
+ ActionNode::ActionType action_type_;
+ int reg_;
+ DeferredAction* next_;
+ friend class Trace;
+ };
+
+ class DeferredCapture : public DeferredAction {
+ public:
+ DeferredCapture(int reg, bool is_capture, Trace* trace)
+ : DeferredAction(ActionNode::STORE_POSITION, reg),
+ cp_offset_(trace->cp_offset()),
+ is_capture_(is_capture) { }
+ int cp_offset() { return cp_offset_; }
+ bool is_capture() { return is_capture_; }
+ private:
+ int cp_offset_;
+ bool is_capture_;
+ void set_cp_offset(int cp_offset) { cp_offset_ = cp_offset; }
+ };
+
+ class DeferredSetRegister : public DeferredAction {
+ public:
+ DeferredSetRegister(int reg, int value)
+ : DeferredAction(ActionNode::SET_REGISTER, reg),
+ value_(value) { }
+ int value() { return value_; }
+ private:
+ int value_;
+ };
+
+ class DeferredClearCaptures : public DeferredAction {
+ public:
+ explicit DeferredClearCaptures(Interval range)
+ : DeferredAction(ActionNode::CLEAR_CAPTURES, -1),
+ range_(range) { }
+ Interval range() { return range_; }
+ private:
+ Interval range_;
+ };
+
+ class DeferredIncrementRegister : public DeferredAction {
+ public:
+ explicit DeferredIncrementRegister(int reg)
+ : DeferredAction(ActionNode::INCREMENT_REGISTER, reg) { }
+ };
+
+ Trace()
+ : cp_offset_(0),
+ actions_(NULL),
+ backtrack_(NULL),
+ stop_node_(NULL),
+ loop_label_(NULL),
+ characters_preloaded_(0),
+ bound_checked_up_to_(0),
+ flush_budget_(100),
+ at_start_(UNKNOWN) { }
+
+ // End the trace. This involves flushing the deferred actions in the trace
+ // and pushing a backtrack location onto the backtrack stack. Once this is
+ // done we can start a new trace or go to one that has already been
+ // generated.
+ void Flush(RegExpCompiler* compiler, RegExpNode* successor);
+ int cp_offset() { return cp_offset_; }
+ DeferredAction* actions() { return actions_; }
+ // A trivial trace is one that has no deferred actions or other state that
+ // affects the assumptions used when generating code. There is no recorded
+ // backtrack location in a trivial trace, so with a trivial trace we will
+ // generate code that, on a failure to match, gets the backtrack location
+ // from the backtrack stack rather than using a direct jump instruction. We
+ // always start code generation with a trivial trace and non-trivial traces
+ // are created as we emit code for nodes or add to the list of deferred
+ // actions in the trace. The location of the code generated for a node using
+ // a trivial trace is recorded in a label in the node so that gotos can be
+ // generated to that code.
+ bool is_trivial() {
+ return backtrack_ == NULL &&
+ actions_ == NULL &&
+ cp_offset_ == 0 &&
+ characters_preloaded_ == 0 &&
+ bound_checked_up_to_ == 0 &&
+ quick_check_performed_.characters() == 0 &&
+ at_start_ == UNKNOWN;
+ }
+ TriBool at_start() { return at_start_; }
+ void set_at_start(bool at_start) {
+ at_start_ = at_start ? TRUE_VALUE : FALSE_VALUE;
+ }
+ Label* backtrack() { return backtrack_; }
+ Label* loop_label() { return loop_label_; }
+ RegExpNode* stop_node() { return stop_node_; }
+ int characters_preloaded() { return characters_preloaded_; }
+ int bound_checked_up_to() { return bound_checked_up_to_; }
+ int flush_budget() { return flush_budget_; }
+ QuickCheckDetails* quick_check_performed() { return &quick_check_performed_; }
+ bool mentions_reg(int reg);
+ // Returns true if a deferred position store exists to the specified
+ // register and stores the offset in the out-parameter. Otherwise
+ // returns false.
+ bool GetStoredPosition(int reg, int* cp_offset);
+ // These set methods and AdvanceCurrentPositionInTrace should be used only on
+ // new traces - the intention is that traces are immutable after creation.
+ void add_action(DeferredAction* new_action) {
+ ASSERT(new_action->next_ == NULL);
+ new_action->next_ = actions_;
+ actions_ = new_action;
+ }
+ void set_backtrack(Label* backtrack) { backtrack_ = backtrack; }
+ void set_stop_node(RegExpNode* node) { stop_node_ = node; }
+ void set_loop_label(Label* label) { loop_label_ = label; }
+ void set_characters_preloaded(int count) { characters_preloaded_ = count; }
+ void set_bound_checked_up_to(int to) { bound_checked_up_to_ = to; }
+ void set_flush_budget(int to) { flush_budget_ = to; }
+ void set_quick_check_performed(QuickCheckDetails* d) {
+ quick_check_performed_ = *d;
+ }
+ void InvalidateCurrentCharacter();
+ void AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler);
+
+ private:
+ int FindAffectedRegisters(OutSet* affected_registers, Zone* zone);
+ void PerformDeferredActions(RegExpMacroAssembler* macro,
+ int max_register,
+ OutSet& affected_registers,
+ OutSet* registers_to_pop,
+ OutSet* registers_to_clear,
+ Zone* zone);
+ void RestoreAffectedRegisters(RegExpMacroAssembler* macro,
+ int max_register,
+ OutSet& registers_to_pop,
+ OutSet& registers_to_clear);
+ int cp_offset_;
+ DeferredAction* actions_;
+ Label* backtrack_;
+ RegExpNode* stop_node_;
+ Label* loop_label_;
+ int characters_preloaded_;
+ int bound_checked_up_to_;
+ QuickCheckDetails quick_check_performed_;
+ int flush_budget_;
+ TriBool at_start_;
+};
+
+
+class NodeVisitor {
+ public:
+ virtual ~NodeVisitor() { }
+#define DECLARE_VISIT(Type) \
+ virtual void Visit##Type(Type##Node* that) = 0;
+FOR_EACH_NODE_TYPE(DECLARE_VISIT)
+#undef DECLARE_VISIT
+ virtual void VisitLoopChoice(LoopChoiceNode* that) { VisitChoice(that); }
+};
+
+
+// Node visitor used to add the start set of the alternatives to the
+// dispatch table of a choice node.
+class DispatchTableConstructor: public NodeVisitor {
+ public:
+ DispatchTableConstructor(DispatchTable* table, bool ignore_case,
+ Zone* zone)
+ : table_(table),
+ choice_index_(-1),
+ ignore_case_(ignore_case),
+ zone_(zone) { }
+
+ void BuildTable(ChoiceNode* node);
+
+ void AddRange(CharacterRange range) {
+ table()->AddRange(range, choice_index_, zone_);
+ }
+
+ void AddInverse(ZoneList<CharacterRange>* ranges);
+
+#define DECLARE_VISIT(Type) \
+ virtual void Visit##Type(Type##Node* that);
+FOR_EACH_NODE_TYPE(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ DispatchTable* table() { return table_; }
+ void set_choice_index(int value) { choice_index_ = value; }
+
+ protected:
+ DispatchTable* table_;
+ int choice_index_;
+ bool ignore_case_;
+ Zone* zone_;
+};
+
+
+// Assertion propagation moves information about assertions such as
+// \b to the affected nodes. For instance, in /.\b./ information must
+// be propagated to the first '.' that whatever follows needs to know
+// if it matched a word or a non-word, and to the second '.' that it
+// has to check if it succeeds a word or non-word. In this case the
+// result will be something like:
+//
+// +-------+ +------------+
+// | . | | . |
+// +-------+ ---> +------------+
+// | word? | | check word |
+// +-------+ +------------+
+class Analysis: public NodeVisitor {
+ public:
+ Analysis(bool ignore_case, bool is_ascii)
+ : ignore_case_(ignore_case),
+ is_ascii_(is_ascii),
+ error_message_(NULL) { }
+ void EnsureAnalyzed(RegExpNode* node);
+
+#define DECLARE_VISIT(Type) \
+ virtual void Visit##Type(Type##Node* that);
+FOR_EACH_NODE_TYPE(DECLARE_VISIT)
+#undef DECLARE_VISIT
+ virtual void VisitLoopChoice(LoopChoiceNode* that);
+
+ bool has_failed() { return error_message_ != NULL; }
+ const char* error_message() {
+ ASSERT(error_message_ != NULL);
+ return error_message_;
+ }
+ void fail(const char* error_message) {
+ error_message_ = error_message;
+ }
+
+ private:
+ bool ignore_case_;
+ bool is_ascii_;
+ const char* error_message_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis);
+};
+
+
+struct RegExpCompileData {
+ RegExpCompileData()
+ : tree(NULL),
+ node(NULL),
+ simple(true),
+ contains_anchor(false),
+ capture_count(0) { }
+ RegExpTree* tree;
+ RegExpNode* node;
+ bool simple;
+ bool contains_anchor;
+ Handle<String> error;
+ int capture_count;
+};
+
+
+class RegExpEngine: public AllStatic {
+ public:
+ struct CompilationResult {
+ explicit CompilationResult(const char* error_message)
+ : error_message(error_message),
+ code(HEAP->the_hole_value()),
+ num_registers(0) {}
+ CompilationResult(Object* code, int registers)
+ : error_message(NULL),
+ code(code),
+ num_registers(registers) {}
+ const char* error_message;
+ Object* code;
+ int num_registers;
+ };
+
+ static CompilationResult Compile(RegExpCompileData* input,
+ bool ignore_case,
+ bool global,
+ bool multiline,
+ Handle<String> pattern,
+ Handle<String> sample_subject,
+ bool is_ascii, Zone* zone);
+
+ static void DotPrint(const char* label, RegExpNode* node, bool ignore_case);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_JSREGEXP_H_
diff --git a/chromium/v8/src/lazy-instance.h b/chromium/v8/src/lazy-instance.h
new file mode 100644
index 00000000000..9d68b8cacce
--- /dev/null
+++ b/chromium/v8/src/lazy-instance.h
@@ -0,0 +1,263 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// The LazyInstance<Type, Traits> class manages a single instance of Type,
+// which will be lazily created on the first time it's accessed. This class is
+// useful for places you would normally use a function-level static, but you
+// need to have guaranteed thread-safety. The Type constructor will only ever
+// be called once, even if two threads are racing to create the object. Get()
+// and Pointer() will always return the same, completely initialized instance.
+//
+// LazyInstance is completely thread safe, assuming that you create it safely.
+// The class was designed to be POD initialized, so it shouldn't require a
+// static constructor. It really only makes sense to declare a LazyInstance as
+// a global variable using the LAZY_INSTANCE_INITIALIZER initializer.
+//
+// LazyInstance is similar to Singleton, except it does not have the singleton
+// property. You can have multiple LazyInstance's of the same type, and each
+// will manage a unique instance. It also preallocates the space for Type, as
+// to avoid allocating the Type instance on the heap. This may help with the
+// performance of creating the instance, and reducing heap fragmentation. This
+// requires that Type be a complete type so we can determine the size. See
+// notes for advanced users below for more explanations.
+//
+// Example usage:
+// static LazyInstance<MyClass>::type my_instance = LAZY_INSTANCE_INITIALIZER;
+// void SomeMethod() {
+// my_instance.Get().SomeMethod(); // MyClass::SomeMethod()
+//
+// MyClass* ptr = my_instance.Pointer();
+// ptr->DoDoDo(); // MyClass::DoDoDo
+// }
+//
+// Additionally you can override the way your instance is constructed by
+// providing your own trait:
+// Example usage:
+// struct MyCreateTrait {
+// static void Construct(MyClass* allocated_ptr) {
+// new (allocated_ptr) MyClass(/* extra parameters... */);
+// }
+// };
+// static LazyInstance<MyClass, MyCreateTrait>::type my_instance =
+// LAZY_INSTANCE_INITIALIZER;
+//
+// WARNINGS:
+// - This implementation of LazyInstance is NOT THREAD-SAFE by default. See
+// ThreadSafeInitOnceTrait declared below for that.
+// - Lazy initialization comes with a cost. Make sure that you don't use it on
+// critical path. Consider adding your initialization code to a function
+// which is explicitly called once.
+//
+// Notes for advanced users:
+// LazyInstance can actually be used in two different ways:
+//
+// - "Static mode" which is the default mode since it is the most efficient
+// (no extra heap allocation). In this mode, the instance is statically
+// allocated (stored in the global data section at compile time).
+// The macro LAZY_STATIC_INSTANCE_INITIALIZER (= LAZY_INSTANCE_INITIALIZER)
+// must be used to initialize static lazy instances.
+//
+// - "Dynamic mode". In this mode, the instance is dynamically allocated and
+// constructed (using new) by default. This mode is useful if you have to
+// deal with some code already allocating the instance for you (e.g.
+// OS::Mutex() which returns a new private OS-dependent subclass of Mutex).
+// The macro LAZY_DYNAMIC_INSTANCE_INITIALIZER must be used to initialize
+// dynamic lazy instances.
+
+#ifndef V8_LAZY_INSTANCE_H_
+#define V8_LAZY_INSTANCE_H_
+
+#include "once.h"
+
+namespace v8 {
+namespace internal {
+
+#define LAZY_STATIC_INSTANCE_INITIALIZER { V8_ONCE_INIT, {} }
+#define LAZY_DYNAMIC_INSTANCE_INITIALIZER { V8_ONCE_INIT, 0 }
+
+// Default to static mode.
+#define LAZY_INSTANCE_INITIALIZER LAZY_STATIC_INSTANCE_INITIALIZER
+
+
+template <typename T>
+struct LeakyInstanceTrait {
+ static void Destroy(T* /* instance */) {}
+};
+
+
+// Traits that define how an instance is allocated and accessed.
+
+// TODO(kalmard): __alignof__ is only defined for GCC > 4.2. Fix alignment issue
+// on MIPS with other compilers.
+#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2))
+#define LAZY_ALIGN(x) __attribute__((aligned(__alignof__(x))))
+#else
+#define LAZY_ALIGN(x)
+#endif
+
+template <typename T>
+struct StaticallyAllocatedInstanceTrait {
+ typedef char StorageType[sizeof(T)] LAZY_ALIGN(T);
+
+ static T* MutableInstance(StorageType* storage) {
+ return reinterpret_cast<T*>(storage);
+ }
+
+ template <typename ConstructTrait>
+ static void InitStorageUsingTrait(StorageType* storage) {
+ ConstructTrait::Construct(MutableInstance(storage));
+ }
+};
+
+#undef LAZY_ALIGN
+
+
+template <typename T>
+struct DynamicallyAllocatedInstanceTrait {
+ typedef T* StorageType;
+
+ static T* MutableInstance(StorageType* storage) {
+ return *storage;
+ }
+
+ template <typename CreateTrait>
+ static void InitStorageUsingTrait(StorageType* storage) {
+ *storage = CreateTrait::Create();
+ }
+};
+
+
+template <typename T>
+struct DefaultConstructTrait {
+ // Constructs the provided object which was already allocated.
+ static void Construct(T* allocated_ptr) {
+ new(allocated_ptr) T();
+ }
+};
+
+
+template <typename T>
+struct DefaultCreateTrait {
+ static T* Create() {
+ return new T();
+ }
+};
+
+
+struct ThreadSafeInitOnceTrait {
+ template <typename Function, typename Storage>
+ static void Init(OnceType* once, Function function, Storage storage) {
+ CallOnce(once, function, storage);
+ }
+};
+
+
+// Initialization trait for users who don't care about thread-safety.
+struct SingleThreadInitOnceTrait {
+ template <typename Function, typename Storage>
+ static void Init(OnceType* once, Function function, Storage storage) {
+ if (*once == ONCE_STATE_UNINITIALIZED) {
+ function(storage);
+ *once = ONCE_STATE_DONE;
+ }
+ }
+};
+
+
+// TODO(pliard): Handle instances destruction (using global destructors).
+template <typename T, typename AllocationTrait, typename CreateTrait,
+ typename InitOnceTrait, typename DestroyTrait /* not used yet. */>
+struct LazyInstanceImpl {
+ public:
+ typedef typename AllocationTrait::StorageType StorageType;
+
+ private:
+ static void InitInstance(StorageType* storage) {
+ AllocationTrait::template InitStorageUsingTrait<CreateTrait>(storage);
+ }
+
+ void Init() const {
+ InitOnceTrait::Init(
+ &once_,
+ // Casts to void* are needed here to avoid breaking strict aliasing
+ // rules.
+ reinterpret_cast<void(*)(void*)>(&InitInstance), // NOLINT
+ reinterpret_cast<void*>(&storage_));
+ }
+
+ public:
+ T* Pointer() {
+ Init();
+ return AllocationTrait::MutableInstance(&storage_);
+ }
+
+ const T& Get() const {
+ Init();
+ return *AllocationTrait::MutableInstance(&storage_);
+ }
+
+ mutable OnceType once_;
+ // Note that the previous field, OnceType, is an AtomicWord which guarantees
+ // 4-byte alignment of the storage field below. If compiling with GCC (>4.2),
+ // the LAZY_ALIGN macro above will guarantee correctness for any alignment.
+ mutable StorageType storage_;
+};
+
+
+template <typename T,
+ typename CreateTrait = DefaultConstructTrait<T>,
+ typename InitOnceTrait = SingleThreadInitOnceTrait,
+ typename DestroyTrait = LeakyInstanceTrait<T> >
+struct LazyStaticInstance {
+ typedef LazyInstanceImpl<T, StaticallyAllocatedInstanceTrait<T>,
+ CreateTrait, InitOnceTrait, DestroyTrait> type;
+};
+
+
+template <typename T,
+ typename CreateTrait = DefaultConstructTrait<T>,
+ typename InitOnceTrait = SingleThreadInitOnceTrait,
+ typename DestroyTrait = LeakyInstanceTrait<T> >
+struct LazyInstance {
+ // A LazyInstance is a LazyStaticInstance.
+ typedef typename LazyStaticInstance<T, CreateTrait, InitOnceTrait,
+ DestroyTrait>::type type;
+};
+
+
+template <typename T,
+ typename CreateTrait = DefaultCreateTrait<T>,
+ typename InitOnceTrait = SingleThreadInitOnceTrait,
+ typename DestroyTrait = LeakyInstanceTrait<T> >
+struct LazyDynamicInstance {
+ typedef LazyInstanceImpl<T, DynamicallyAllocatedInstanceTrait<T>,
+ CreateTrait, InitOnceTrait, DestroyTrait> type;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_LAZY_INSTANCE_H_
diff --git a/chromium/v8/src/list-inl.h b/chromium/v8/src/list-inl.h
new file mode 100644
index 00000000000..143c830ee92
--- /dev/null
+++ b/chromium/v8/src/list-inl.h
@@ -0,0 +1,282 @@
+// Copyright 2006-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.
+
+#ifndef V8_LIST_INL_H_
+#define V8_LIST_INL_H_
+
+#include "list.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+
+template<typename T, class P>
+void List<T, P>::Add(const T& element, P alloc) {
+ if (length_ < capacity_) {
+ data_[length_++] = element;
+ } else {
+ List<T, P>::ResizeAdd(element, alloc);
+ }
+}
+
+
+template<typename T, class P>
+void List<T, P>::AddAll(const List<T, P>& other, P alloc) {
+ AddAll(other.ToVector(), alloc);
+}
+
+
+template<typename T, class P>
+void List<T, P>::AddAll(const Vector<T>& other, P alloc) {
+ int result_length = length_ + other.length();
+ if (capacity_ < result_length) Resize(result_length, alloc);
+ for (int i = 0; i < other.length(); i++) {
+ data_[length_ + i] = other.at(i);
+ }
+ length_ = result_length;
+}
+
+
+// Use two layers of inlining so that the non-inlined function can
+// use the same implementation as the inlined version.
+template<typename T, class P>
+void List<T, P>::ResizeAdd(const T& element, P alloc) {
+ ResizeAddInternal(element, alloc);
+}
+
+
+template<typename T, class P>
+void List<T, P>::ResizeAddInternal(const T& element, P alloc) {
+ ASSERT(length_ >= capacity_);
+ // 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 + 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;
+ Resize(new_capacity, alloc);
+ data_[length_++] = temp;
+}
+
+
+template<typename T, class P>
+void List<T, P>::Resize(int new_capacity, P alloc) {
+ ASSERT_LE(length_, new_capacity);
+ T* new_data = NewData(new_capacity, alloc);
+ OS::MemCopy(new_data, data_, length_ * sizeof(T));
+ List<T, P>::DeleteData(data_);
+ data_ = new_data;
+ capacity_ = new_capacity;
+}
+
+
+template<typename T, class P>
+Vector<T> List<T, P>::AddBlock(T value, int count, P alloc) {
+ int start = length_;
+ for (int i = 0; i < count; i++) Add(value, alloc);
+ return Vector<T>(&data_[start], count);
+}
+
+
+template<typename T, class P>
+void List<T, P>::Set(int index, const T& elm) {
+ ASSERT(index >= 0 && index <= length_);
+ data_[index] = elm;
+}
+
+
+template<typename T, class P>
+void List<T, P>::InsertAt(int index, const T& elm, P alloc) {
+ ASSERT(index >= 0 && index <= length_);
+ Add(elm, alloc);
+ for (int i = length_ - 1; i > index; --i) {
+ data_[i] = data_[i - 1];
+ }
+ data_[index] = elm;
+}
+
+
+template<typename T, class P>
+T List<T, P>::Remove(int i) {
+ T element = at(i);
+ length_--;
+ while (i < length_) {
+ data_[i] = data_[i + 1];
+ i++;
+ }
+ return element;
+}
+
+
+template<typename T, class P>
+bool List<T, P>::RemoveElement(const T& elm) {
+ for (int i = 0; i < length_; i++) {
+ if (data_[i] == elm) {
+ Remove(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+template<typename T, class P>
+void List<T, P>::Allocate(int length, P allocator) {
+ DeleteData(data_);
+ Initialize(length, allocator);
+ length_ = length;
+}
+
+
+template<typename T, class P>
+void List<T, P>::Clear() {
+ DeleteData(data_);
+ // We don't call Initialize(0) since that requires passing a Zone,
+ // which we don't really need.
+ data_ = NULL;
+ capacity_ = 0;
+ length_ = 0;
+}
+
+
+template<typename T, class P>
+void List<T, P>::Rewind(int pos) {
+ length_ = pos;
+}
+
+
+template<typename T, class P>
+void List<T, P>::Trim(P alloc) {
+ if (length_ < capacity_ / 4) {
+ Resize(capacity_ / 2, alloc);
+ }
+}
+
+
+template<typename T, class P>
+void List<T, P>::Iterate(void (*callback)(T* x)) {
+ for (int i = 0; i < length_; i++) callback(&data_[i]);
+}
+
+
+template<typename T, class P>
+template<class Visitor>
+void List<T, P>::Iterate(Visitor* visitor) {
+ for (int i = 0; i < length_; i++) visitor->Apply(&data_[i]);
+}
+
+
+template<typename T, class P>
+bool List<T, P>::Contains(const T& elm) const {
+ for (int i = 0; i < length_; i++) {
+ if (data_[i] == elm)
+ return true;
+ }
+ return false;
+}
+
+
+template<typename T, class P>
+int List<T, P>::CountOccurrences(const T& elm, int start, int end) const {
+ int result = 0;
+ for (int i = start; i <= end; i++) {
+ if (data_[i] == elm) ++result;
+ }
+ return result;
+}
+
+
+template<typename T, class P>
+void List<T, P>::Sort(int (*cmp)(const T* x, const T* y)) {
+ ToVector().Sort(cmp);
+#ifdef DEBUG
+ for (int i = 1; i < length_; i++)
+ ASSERT(cmp(&data_[i - 1], &data_[i]) <= 0);
+#endif
+}
+
+
+template<typename T, class P>
+void List<T, P>::Sort() {
+ ToVector().Sort();
+}
+
+
+template<typename T, class P>
+void List<T, P>::Initialize(int capacity, P allocator) {
+ ASSERT(capacity >= 0);
+ data_ = (capacity > 0) ? NewData(capacity, allocator) : NULL;
+ capacity_ = capacity;
+ length_ = 0;
+}
+
+
+template <typename T, typename P>
+int SortedListBSearch(const List<T>& list, P cmp) {
+ int low = 0;
+ int high = list.length() - 1;
+ while (low <= high) {
+ int mid = (low + high) / 2;
+ T mid_elem = list[mid];
+
+ if (cmp(&mid_elem) > 0) {
+ high = mid - 1;
+ continue;
+ }
+ if (cmp(&mid_elem) < 0) {
+ low = mid + 1;
+ continue;
+ }
+ // Found the elememt.
+ return mid;
+ }
+ return -1;
+}
+
+
+template<typename T>
+class ElementCmp {
+ public:
+ explicit ElementCmp(T e) : elem_(e) {}
+ int operator()(const T* other) {
+ return PointerValueCompare(other, &elem_);
+ }
+ private:
+ T elem_;
+};
+
+
+template <typename T>
+int SortedListBSearch(const List<T>& list, T elem) {
+ return SortedListBSearch<T, ElementCmp<T> > (list, ElementCmp<T>(elem));
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_LIST_INL_H_
diff --git a/chromium/v8/src/list.h b/chromium/v8/src/list.h
new file mode 100644
index 00000000000..0e4e35bb41b
--- /dev/null
+++ b/chromium/v8/src/list.h
@@ -0,0 +1,221 @@
+// 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_LIST_H_
+#define V8_LIST_H_
+
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+
+// ----------------------------------------------------------------------------
+// The list is a template for very light-weight lists. We are not
+// using the STL because we want full control over space and speed of
+// the code. This implementation is based on code by Robert Griesemer
+// and Rob Pike.
+//
+// The list is parameterized by the type of its elements (T) and by an
+// allocation policy (P). The policy is used for allocating lists in
+// the C free store or the zone; see zone.h.
+
+// Forward defined as
+// template <typename T,
+// class AllocationPolicy = FreeStoreAllocationPolicy> class List;
+template <typename T, class AllocationPolicy>
+class List {
+ public:
+ explicit List(AllocationPolicy allocator = AllocationPolicy()) {
+ Initialize(0, allocator);
+ }
+ INLINE(explicit List(int capacity,
+ AllocationPolicy allocator = AllocationPolicy())) {
+ Initialize(capacity, allocator);
+ }
+ INLINE(~List()) { DeleteData(data_); }
+
+ // Deallocates memory used by the list and leaves the list in a consistent
+ // empty state.
+ void Free() {
+ DeleteData(data_);
+ Initialize(0);
+ }
+
+ INLINE(void* operator new(size_t size,
+ AllocationPolicy allocator = AllocationPolicy())) {
+ return allocator.New(static_cast<int>(size));
+ }
+ INLINE(void operator delete(void* p)) {
+ AllocationPolicy::Delete(p);
+ }
+
+ // Please the MSVC compiler. We should never have to execute this.
+ INLINE(void operator delete(void* p, AllocationPolicy allocator)) {
+ UNREACHABLE();
+ }
+
+ // 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 (e.g. Add).
+ inline T& operator[](int i) const {
+ ASSERT(0 <= i);
+ ASSERT(i < length_);
+ return data_[i];
+ }
+ inline T& at(int i) const { return operator[](i); }
+ inline T& last() const { return at(length_ - 1); }
+ inline T& first() const { return at(0); }
+
+ INLINE(bool is_empty() const) { return length_ == 0; }
+ INLINE(int length() const) { return length_; }
+ INLINE(int capacity() const) { return capacity_; }
+
+ Vector<T> ToVector() const { return Vector<T>(data_, length_); }
+
+ Vector<const T> ToConstVector() { return Vector<const T>(data_, length_); }
+
+ // Adds a copy of the given 'element' to the end of the list,
+ // expanding the list if necessary.
+ void Add(const T& element, AllocationPolicy allocator = AllocationPolicy());
+
+ // Add all the elements from the argument list to this list.
+ void AddAll(const List<T, AllocationPolicy>& other,
+ AllocationPolicy allocator = AllocationPolicy());
+
+ // Add all the elements from the vector to this list.
+ void AddAll(const Vector<T>& other,
+ AllocationPolicy allocator = AllocationPolicy());
+
+ // Inserts the element at the specific index.
+ void InsertAt(int index, const T& element,
+ AllocationPolicy allocator = AllocationPolicy());
+
+ // Overwrites the element at the specific index.
+ void Set(int index, const T& element);
+
+ // Added 'count' elements with the value 'value' and returns a
+ // vector that allows access to the elements. The vector is valid
+ // until the next change is made to this list.
+ Vector<T> AddBlock(T value, int count,
+ AllocationPolicy allocator = AllocationPolicy());
+
+ // Removes the i'th element without deleting it even if T is a
+ // pointer type; moves all elements above i "down". Returns the
+ // removed element. This function's complexity is linear in the
+ // size of the list.
+ T Remove(int i);
+
+ // Remove the given element from the list. Returns whether or not
+ // the input is included in the list in the first place.
+ bool RemoveElement(const T& elm);
+
+ // Removes the last element without deleting it even if T is a
+ // pointer type. Returns the removed element.
+ INLINE(T RemoveLast()) { return Remove(length_ - 1); }
+
+ // Deletes current list contents and allocates space for 'length' elements.
+ INLINE(void Allocate(int length,
+ AllocationPolicy allocator = AllocationPolicy()));
+
+ // Clears the list by setting the length to zero. Even if T is a
+ // pointer type, clearing the list doesn't delete the entries.
+ INLINE(void Clear());
+
+ // Drops all but the first 'pos' elements from the list.
+ INLINE(void Rewind(int pos));
+
+ // Drop the last 'count' elements from the list.
+ INLINE(void RewindBy(int count)) { Rewind(length_ - count); }
+
+ // Halve the capacity if fill level is less than a quarter.
+ INLINE(void Trim(AllocationPolicy allocator = AllocationPolicy()));
+
+ bool Contains(const T& elm) const;
+ int CountOccurrences(const T& elm, int start, int end) const;
+
+ // Iterate through all list entries, starting at index 0.
+ void Iterate(void (*callback)(T* x));
+ template<class Visitor>
+ void Iterate(Visitor* visitor);
+
+ // Sort all list entries (using QuickSort)
+ void Sort(int (*cmp)(const T* x, const T* y));
+ void Sort();
+
+ INLINE(void Initialize(int capacity,
+ AllocationPolicy allocator = AllocationPolicy()));
+
+ private:
+ T* data_;
+ int capacity_;
+ int length_;
+
+ INLINE(T* NewData(int n, AllocationPolicy allocator)) {
+ return static_cast<T*>(allocator.New(n * sizeof(T)));
+ }
+ INLINE(void DeleteData(T* data)) {
+ AllocationPolicy::Delete(data);
+ }
+
+ // Increase the capacity of a full list, and add an element.
+ // List must be full already.
+ void ResizeAdd(const T& element, AllocationPolicy allocator);
+
+ // Inlined implementation of ResizeAdd, shared by inlined and
+ // non-inlined versions of ResizeAdd.
+ void ResizeAddInternal(const T& element, AllocationPolicy allocator);
+
+ // Resize the list.
+ void Resize(int new_capacity, AllocationPolicy allocator);
+
+ DISALLOW_COPY_AND_ASSIGN(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.
+// |cmp| is a predicate that takes a pointer to an element of the List
+// and returns +1 if it is greater, -1 if it is less than the element
+// being searched.
+template <typename T, class P>
+int SortedListBSearch(const List<T>& list, P cmp);
+template <typename T>
+int SortedListBSearch(const List<T>& list, T elem);
+
+
+} } // namespace v8::internal
+
+
+#endif // V8_LIST_H_
diff --git a/chromium/v8/src/lithium-allocator-inl.h b/chromium/v8/src/lithium-allocator-inl.h
new file mode 100644
index 00000000000..8cca19b2efa
--- /dev/null
+++ b/chromium/v8/src/lithium-allocator-inl.h
@@ -0,0 +1,163 @@
+// 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_LITHIUM_ALLOCATOR_INL_H_
+#define V8_LITHIUM_ALLOCATOR_INL_H_
+
+#include "lithium-allocator.h"
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/lithium-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/lithium-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/lithium-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/lithium-mips.h"
+#else
+#error "Unknown architecture."
+#endif
+
+namespace v8 {
+namespace internal {
+
+bool LAllocator::IsGapAt(int index) { return chunk_->IsGapAt(index); }
+
+
+LInstruction* LAllocator::InstructionAt(int index) {
+ return chunk_->instructions()->at(index);
+}
+
+
+LGap* LAllocator::GapAt(int index) {
+ return chunk_->GetGapAt(index);
+}
+
+
+TempIterator::TempIterator(LInstruction* instr)
+ : instr_(instr),
+ limit_(instr->TempCount()),
+ current_(0) {
+ SkipUninteresting();
+}
+
+
+bool TempIterator::Done() { return current_ >= limit_; }
+
+
+LOperand* TempIterator::Current() {
+ ASSERT(!Done());
+ return instr_->TempAt(current_);
+}
+
+
+void TempIterator::SkipUninteresting() {
+ while (current_ < limit_ && instr_->TempAt(current_) == NULL) ++current_;
+}
+
+
+void TempIterator::Advance() {
+ ++current_;
+ SkipUninteresting();
+}
+
+
+InputIterator::InputIterator(LInstruction* instr)
+ : instr_(instr),
+ limit_(instr->InputCount()),
+ current_(0) {
+ SkipUninteresting();
+}
+
+
+bool InputIterator::Done() { return current_ >= limit_; }
+
+
+LOperand* InputIterator::Current() {
+ ASSERT(!Done());
+ ASSERT(instr_->InputAt(current_) != NULL);
+ return instr_->InputAt(current_);
+}
+
+
+void InputIterator::Advance() {
+ ++current_;
+ SkipUninteresting();
+}
+
+
+void InputIterator::SkipUninteresting() {
+ while (current_ < limit_) {
+ LOperand* current = instr_->InputAt(current_);
+ if (current != NULL && !current->IsConstantOperand()) break;
+ ++current_;
+ }
+}
+
+
+UseIterator::UseIterator(LInstruction* instr)
+ : input_iterator_(instr), env_iterator_(instr->environment()) { }
+
+
+bool UseIterator::Done() {
+ return input_iterator_.Done() && env_iterator_.Done();
+}
+
+
+LOperand* UseIterator::Current() {
+ ASSERT(!Done());
+ LOperand* result = input_iterator_.Done()
+ ? env_iterator_.Current()
+ : input_iterator_.Current();
+ ASSERT(result != NULL);
+ return result;
+}
+
+
+void UseIterator::Advance() {
+ input_iterator_.Done()
+ ? env_iterator_.Advance()
+ : input_iterator_.Advance();
+}
+
+
+void LAllocator::SetLiveRangeAssignedRegister(
+ LiveRange* range,
+ int reg,
+ RegisterKind register_kind) {
+ if (register_kind == DOUBLE_REGISTERS) {
+ assigned_double_registers_->Add(reg);
+ } else {
+ assigned_registers_->Add(reg);
+ }
+ range->set_assigned_register(reg, register_kind, chunk()->zone());
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_LITHIUM_ALLOCATOR_INL_H_
diff --git a/chromium/v8/src/lithium-allocator.cc b/chromium/v8/src/lithium-allocator.cc
new file mode 100644
index 00000000000..2e2f802558c
--- /dev/null
+++ b/chromium/v8/src/lithium-allocator.cc
@@ -0,0 +1,2206 @@
+// Copyright 2012 the V8 project authors. 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 "hydrogen.h"
+#include "string-stream.h"
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/lithium-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/lithium-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/lithium-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/lithium-mips.h"
+#else
+#error "Unknown architecture."
+#endif
+
+namespace v8 {
+namespace internal {
+
+static inline LifetimePosition Min(LifetimePosition a, LifetimePosition b) {
+ return a.Value() < b.Value() ? a : b;
+}
+
+
+static inline LifetimePosition Max(LifetimePosition a, LifetimePosition b) {
+ return a.Value() > b.Value() ? a : b;
+}
+
+
+UsePosition::UsePosition(LifetimePosition pos,
+ LOperand* operand,
+ LOperand* hint)
+ : operand_(operand),
+ hint_(hint),
+ pos_(pos),
+ next_(NULL),
+ requires_reg_(false),
+ register_beneficial_(true) {
+ if (operand_ != NULL && operand_->IsUnallocated()) {
+ LUnallocated* unalloc = LUnallocated::cast(operand_);
+ requires_reg_ = unalloc->HasRegisterPolicy();
+ register_beneficial_ = !unalloc->HasAnyPolicy();
+ }
+ ASSERT(pos_.IsValid());
+}
+
+
+bool UsePosition::HasHint() const {
+ return hint_ != NULL && !hint_->IsUnallocated();
+}
+
+
+bool UsePosition::RequiresRegister() const {
+ return requires_reg_;
+}
+
+
+bool UsePosition::RegisterIsBeneficial() const {
+ return register_beneficial_;
+}
+
+
+void UseInterval::SplitAt(LifetimePosition pos, Zone* zone) {
+ ASSERT(Contains(pos) && pos.Value() != start().Value());
+ UseInterval* after = new(zone) UseInterval(pos, end_);
+ after->next_ = next_;
+ next_ = after;
+ end_ = pos;
+}
+
+
+#ifdef DEBUG
+
+
+void LiveRange::Verify() const {
+ UsePosition* cur = first_pos_;
+ while (cur != NULL) {
+ ASSERT(Start().Value() <= cur->pos().Value() &&
+ cur->pos().Value() <= End().Value());
+ cur = cur->next();
+ }
+}
+
+
+bool LiveRange::HasOverlap(UseInterval* target) const {
+ UseInterval* current_interval = first_interval_;
+ while (current_interval != NULL) {
+ // Intervals overlap if the start of one is contained in the other.
+ if (current_interval->Contains(target->start()) ||
+ target->Contains(current_interval->start())) {
+ return true;
+ }
+ current_interval = current_interval->next();
+ }
+ return false;
+}
+
+
+#endif
+
+
+LiveRange::LiveRange(int id, Zone* zone)
+ : id_(id),
+ spilled_(false),
+ is_double_(false),
+ assigned_register_(kInvalidAssignment),
+ last_interval_(NULL),
+ first_interval_(NULL),
+ first_pos_(NULL),
+ parent_(NULL),
+ next_(NULL),
+ current_interval_(NULL),
+ last_processed_use_(NULL),
+ current_hint_operand_(NULL),
+ spill_operand_(new(zone) LOperand()),
+ spill_start_index_(kMaxInt) { }
+
+
+void LiveRange::set_assigned_register(int reg,
+ RegisterKind register_kind,
+ Zone* zone) {
+ ASSERT(!HasRegisterAssigned() && !IsSpilled());
+ assigned_register_ = reg;
+ is_double_ = (register_kind == DOUBLE_REGISTERS);
+ ConvertOperands(zone);
+}
+
+
+void LiveRange::MakeSpilled(Zone* zone) {
+ ASSERT(!IsSpilled());
+ ASSERT(TopLevel()->HasAllocatedSpillOperand());
+ spilled_ = true;
+ assigned_register_ = kInvalidAssignment;
+ ConvertOperands(zone);
+}
+
+
+bool LiveRange::HasAllocatedSpillOperand() const {
+ ASSERT(spill_operand_ != NULL);
+ return !spill_operand_->IsIgnored();
+}
+
+
+void LiveRange::SetSpillOperand(LOperand* operand) {
+ ASSERT(!operand->IsUnallocated());
+ ASSERT(spill_operand_ != NULL);
+ ASSERT(spill_operand_->IsIgnored());
+ spill_operand_->ConvertTo(operand->kind(), operand->index());
+}
+
+
+UsePosition* LiveRange::NextUsePosition(LifetimePosition start) {
+ UsePosition* use_pos = last_processed_use_;
+ if (use_pos == NULL) use_pos = first_pos();
+ while (use_pos != NULL && use_pos->pos().Value() < start.Value()) {
+ use_pos = use_pos->next();
+ }
+ last_processed_use_ = use_pos;
+ return use_pos;
+}
+
+
+UsePosition* LiveRange::NextUsePositionRegisterIsBeneficial(
+ LifetimePosition start) {
+ UsePosition* pos = NextUsePosition(start);
+ while (pos != NULL && !pos->RegisterIsBeneficial()) {
+ pos = pos->next();
+ }
+ return pos;
+}
+
+
+UsePosition* LiveRange::PreviousUsePositionRegisterIsBeneficial(
+ LifetimePosition start) {
+ UsePosition* pos = first_pos();
+ UsePosition* prev = NULL;
+ while (pos != NULL && pos->pos().Value() < start.Value()) {
+ if (pos->RegisterIsBeneficial()) prev = pos;
+ pos = pos->next();
+ }
+ return prev;
+}
+
+
+UsePosition* LiveRange::NextRegisterPosition(LifetimePosition start) {
+ UsePosition* pos = NextUsePosition(start);
+ while (pos != NULL && !pos->RequiresRegister()) {
+ pos = pos->next();
+ }
+ return pos;
+}
+
+
+bool LiveRange::CanBeSpilled(LifetimePosition pos) {
+ // We cannot spill a live range that has a use requiring a register
+ // 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().InstructionEnd().Value();
+}
+
+
+LOperand* LiveRange::CreateAssignedOperand(Zone* zone) {
+ LOperand* op = NULL;
+ if (HasRegisterAssigned()) {
+ ASSERT(!IsSpilled());
+ if (IsDouble()) {
+ op = LDoubleRegister::Create(assigned_register(), zone);
+ } else {
+ op = LRegister::Create(assigned_register(), zone);
+ }
+ } else if (IsSpilled()) {
+ ASSERT(!HasRegisterAssigned());
+ op = TopLevel()->GetSpillOperand();
+ ASSERT(!op->IsUnallocated());
+ } else {
+ LUnallocated* unalloc = new(zone) LUnallocated(LUnallocated::NONE);
+ unalloc->set_virtual_register(id_);
+ op = unalloc;
+ }
+ return op;
+}
+
+
+UseInterval* LiveRange::FirstSearchIntervalForPosition(
+ LifetimePosition position) const {
+ if (current_interval_ == NULL) return first_interval_;
+ if (current_interval_->start().Value() > position.Value()) {
+ current_interval_ = NULL;
+ return first_interval_;
+ }
+ return current_interval_;
+}
+
+
+void LiveRange::AdvanceLastProcessedMarker(
+ UseInterval* to_start_of, LifetimePosition but_not_past) const {
+ if (to_start_of == NULL) return;
+ if (to_start_of->start().Value() > but_not_past.Value()) return;
+ LifetimePosition start =
+ current_interval_ == NULL ? LifetimePosition::Invalid()
+ : current_interval_->start();
+ if (to_start_of->start().Value() > start.Value()) {
+ current_interval_ = to_start_of;
+ }
+}
+
+
+void LiveRange::SplitAt(LifetimePosition position,
+ LiveRange* result,
+ Zone* zone) {
+ ASSERT(Start().Value() < position.Value());
+ ASSERT(result->IsEmpty());
+ // Find the last interval that ends before the position. If the
+ // position is contained in one of the intervals in the chain, we
+ // split that interval and use the first part.
+ UseInterval* current = FirstSearchIntervalForPosition(position);
+
+ // If the split position coincides with the beginning of a use interval
+ // we need to split use positons in a special way.
+ bool split_at_start = false;
+
+ if (current->start().Value() == position.Value()) {
+ // When splitting at start we need to locate the previous use interval.
+ current = first_interval_;
+ }
+
+ while (current != NULL) {
+ if (current->Contains(position)) {
+ current->SplitAt(position, zone);
+ break;
+ }
+ UseInterval* next = current->next();
+ if (next->start().Value() >= position.Value()) {
+ split_at_start = (next->start().Value() == position.Value());
+ break;
+ }
+ current = next;
+ }
+
+ // Partition original use intervals to the two live ranges.
+ UseInterval* before = current;
+ UseInterval* after = before->next();
+ result->last_interval_ = (last_interval_ == before)
+ ? after // Only interval in the range after split.
+ : last_interval_; // Last interval of the original range.
+ result->first_interval_ = after;
+ last_interval_ = before;
+
+ // Find the last use position before the split and the first use
+ // position after it.
+ UsePosition* use_after = first_pos_;
+ UsePosition* use_before = NULL;
+ if (split_at_start) {
+ // The split position coincides with the beginning of a use interval (the
+ // end of a lifetime hole). Use at this position should be attributed to
+ // the split child because split child owns use interval covering it.
+ while (use_after != NULL && use_after->pos().Value() < position.Value()) {
+ use_before = use_after;
+ use_after = use_after->next();
+ }
+ } else {
+ while (use_after != NULL && use_after->pos().Value() <= position.Value()) {
+ use_before = use_after;
+ use_after = use_after->next();
+ }
+ }
+
+ // Partition original use positions to the two live ranges.
+ if (use_before != NULL) {
+ use_before->next_ = NULL;
+ } else {
+ first_pos_ = NULL;
+ }
+ result->first_pos_ = use_after;
+
+ // Discard cached iteration state. It might be pointing
+ // to the use that no longer belongs to this live range.
+ last_processed_use_ = NULL;
+ current_interval_ = NULL;
+
+ // Link the new live range in the chain before any of the other
+ // ranges linked from the range before the split.
+ result->parent_ = (parent_ == NULL) ? this : parent_;
+ result->next_ = next_;
+ next_ = result;
+
+#ifdef DEBUG
+ Verify();
+ result->Verify();
+#endif
+}
+
+
+// This implements an ordering on live ranges so that they are ordered by their
+// start positions. This is needed for the correctness of the register
+// allocation algorithm. If two live ranges start at the same offset then there
+// is a tie breaker based on where the value is first used. This part of the
+// ordering is merely a heuristic.
+bool LiveRange::ShouldBeAllocatedBefore(const LiveRange* other) const {
+ LifetimePosition start = Start();
+ LifetimePosition other_start = other->Start();
+ if (start.Value() == other_start.Value()) {
+ UsePosition* pos = first_pos();
+ if (pos == NULL) return false;
+ UsePosition* other_pos = other->first_pos();
+ if (other_pos == NULL) return true;
+ return pos->pos().Value() < other_pos->pos().Value();
+ }
+ return start.Value() < other_start.Value();
+}
+
+
+void LiveRange::ShortenTo(LifetimePosition start) {
+ LAllocator::TraceAlloc("Shorten live range %d to [%d\n", id_, start.Value());
+ ASSERT(first_interval_ != NULL);
+ ASSERT(first_interval_->start().Value() <= start.Value());
+ ASSERT(start.Value() < first_interval_->end().Value());
+ first_interval_->set_start(start);
+}
+
+
+void LiveRange::EnsureInterval(LifetimePosition start,
+ LifetimePosition end,
+ Zone* zone) {
+ LAllocator::TraceAlloc("Ensure live range %d in interval [%d %d[\n",
+ id_,
+ start.Value(),
+ end.Value());
+ LifetimePosition new_end = end;
+ while (first_interval_ != NULL &&
+ first_interval_->start().Value() <= end.Value()) {
+ if (first_interval_->end().Value() > end.Value()) {
+ new_end = first_interval_->end();
+ }
+ first_interval_ = first_interval_->next();
+ }
+
+ UseInterval* new_interval = new(zone) UseInterval(start, new_end);
+ new_interval->next_ = first_interval_;
+ first_interval_ = new_interval;
+ if (new_interval->next() == NULL) {
+ last_interval_ = new_interval;
+ }
+}
+
+
+void LiveRange::AddUseInterval(LifetimePosition start,
+ LifetimePosition end,
+ Zone* zone) {
+ LAllocator::TraceAlloc("Add to live range %d interval [%d %d[\n",
+ id_,
+ start.Value(),
+ end.Value());
+ if (first_interval_ == NULL) {
+ UseInterval* interval = new(zone) UseInterval(start, end);
+ first_interval_ = interval;
+ last_interval_ = interval;
+ } else {
+ if (end.Value() == first_interval_->start().Value()) {
+ first_interval_->set_start(start);
+ } else if (end.Value() < first_interval_->start().Value()) {
+ UseInterval* interval = new(zone) UseInterval(start, end);
+ interval->set_next(first_interval_);
+ first_interval_ = interval;
+ } else {
+ // Order of instruction's processing (see ProcessInstructions) guarantees
+ // that each new use interval either precedes or intersects with
+ // last added interval.
+ ASSERT(start.Value() < first_interval_->end().Value());
+ first_interval_->start_ = Min(start, first_interval_->start_);
+ first_interval_->end_ = Max(end, first_interval_->end_);
+ }
+ }
+}
+
+
+void LiveRange::AddUsePosition(LifetimePosition pos,
+ LOperand* operand,
+ LOperand* hint,
+ Zone* zone) {
+ LAllocator::TraceAlloc("Add to live range %d use position %d\n",
+ id_,
+ pos.Value());
+ UsePosition* use_pos = new(zone) UsePosition(pos, operand, hint);
+ UsePosition* prev_hint = NULL;
+ UsePosition* prev = NULL;
+ UsePosition* current = first_pos_;
+ while (current != NULL && current->pos().Value() < pos.Value()) {
+ prev_hint = current->HasHint() ? current : prev_hint;
+ prev = current;
+ current = current->next();
+ }
+
+ if (prev == NULL) {
+ use_pos->set_next(first_pos_);
+ first_pos_ = use_pos;
+ } else {
+ use_pos->next_ = prev->next_;
+ prev->next_ = use_pos;
+ }
+
+ if (prev_hint == NULL && use_pos->HasHint()) {
+ current_hint_operand_ = hint;
+ }
+}
+
+
+void LiveRange::ConvertOperands(Zone* zone) {
+ LOperand* op = CreateAssignedOperand(zone);
+ UsePosition* use_pos = first_pos();
+ while (use_pos != NULL) {
+ ASSERT(Start().Value() <= use_pos->pos().Value() &&
+ use_pos->pos().Value() <= End().Value());
+
+ if (use_pos->HasOperand()) {
+ ASSERT(op->IsRegister() || op->IsDoubleRegister() ||
+ !use_pos->RequiresRegister());
+ use_pos->operand()->ConvertTo(op->kind(), op->index());
+ }
+ use_pos = use_pos->next();
+ }
+}
+
+
+bool LiveRange::CanCover(LifetimePosition position) const {
+ if (IsEmpty()) return false;
+ return Start().Value() <= position.Value() &&
+ position.Value() < End().Value();
+}
+
+
+bool LiveRange::Covers(LifetimePosition position) {
+ if (!CanCover(position)) return false;
+ UseInterval* start_search = FirstSearchIntervalForPosition(position);
+ for (UseInterval* interval = start_search;
+ interval != NULL;
+ interval = interval->next()) {
+ ASSERT(interval->next() == NULL ||
+ interval->next()->start().Value() >= interval->start().Value());
+ AdvanceLastProcessedMarker(interval, position);
+ if (interval->Contains(position)) return true;
+ if (interval->start().Value() > position.Value()) return false;
+ }
+ return false;
+}
+
+
+LifetimePosition LiveRange::FirstIntersection(LiveRange* other) {
+ UseInterval* b = other->first_interval();
+ if (b == NULL) return LifetimePosition::Invalid();
+ LifetimePosition advance_last_processed_up_to = b->start();
+ UseInterval* a = FirstSearchIntervalForPosition(b->start());
+ while (a != NULL && b != NULL) {
+ if (a->start().Value() > other->End().Value()) break;
+ if (b->start().Value() > End().Value()) break;
+ LifetimePosition cur_intersection = a->Intersect(b);
+ if (cur_intersection.IsValid()) {
+ return cur_intersection;
+ }
+ if (a->start().Value() < b->start().Value()) {
+ a = a->next();
+ if (a == NULL || a->start().Value() > other->End().Value()) break;
+ AdvanceLastProcessedMarker(a, advance_last_processed_up_to);
+ } else {
+ b = b->next();
+ }
+ }
+ return LifetimePosition::Invalid();
+}
+
+
+LAllocator::LAllocator(int num_values, HGraph* graph)
+ : zone_(graph->isolate()),
+ chunk_(NULL),
+ live_in_sets_(graph->blocks()->length(), zone()),
+ live_ranges_(num_values * 2, zone()),
+ fixed_live_ranges_(NULL),
+ fixed_double_live_ranges_(NULL),
+ unhandled_live_ranges_(num_values * 2, zone()),
+ active_live_ranges_(8, zone()),
+ inactive_live_ranges_(8, zone()),
+ reusable_slots_(8, zone()),
+ next_virtual_register_(num_values),
+ first_artificial_register_(num_values),
+ mode_(GENERAL_REGISTERS),
+ num_registers_(-1),
+ graph_(graph),
+ has_osr_entry_(false),
+ allocation_ok_(true) { }
+
+
+void LAllocator::InitializeLivenessAnalysis() {
+ // Initialize the live_in sets for each block to NULL.
+ int block_count = graph_->blocks()->length();
+ live_in_sets_.Initialize(block_count, zone());
+ live_in_sets_.AddBlock(NULL, block_count, zone());
+}
+
+
+BitVector* LAllocator::ComputeLiveOut(HBasicBlock* block) {
+ // Compute live out for the given block, except not including backward
+ // successor edges.
+ BitVector* live_out = new(zone()) BitVector(next_virtual_register_, zone());
+
+ // Process all successor blocks.
+ for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) {
+ // Add values live on entry to the successor. Note the successor's
+ // live_in will not be computed yet for backwards edges.
+ HBasicBlock* successor = it.Current();
+ BitVector* live_in = live_in_sets_[successor->block_id()];
+ if (live_in != NULL) live_out->Union(*live_in);
+
+ // All phi input operands corresponding to this successor edge are live
+ // out from this block.
+ int index = successor->PredecessorIndexOf(block);
+ const ZoneList<HPhi*>* phis = successor->phis();
+ for (int i = 0; i < phis->length(); ++i) {
+ HPhi* phi = phis->at(i);
+ if (!phi->OperandAt(index)->IsConstant()) {
+ live_out->Add(phi->OperandAt(index)->id());
+ }
+ }
+ }
+
+ return live_out;
+}
+
+
+void LAllocator::AddInitialIntervals(HBasicBlock* block,
+ BitVector* live_out) {
+ // Add an interval that includes the entire block to the live range for
+ // each live_out value.
+ LifetimePosition start = LifetimePosition::FromInstructionIndex(
+ block->first_instruction_index());
+ LifetimePosition end = LifetimePosition::FromInstructionIndex(
+ block->last_instruction_index()).NextInstruction();
+ BitVector::Iterator iterator(live_out);
+ while (!iterator.Done()) {
+ int operand_index = iterator.Current();
+ LiveRange* range = LiveRangeFor(operand_index);
+ range->AddUseInterval(start, end, zone());
+ iterator.Advance();
+ }
+}
+
+
+int LAllocator::FixedDoubleLiveRangeID(int index) {
+ return -index - 1 - Register::kMaxNumAllocatableRegisters;
+}
+
+
+LOperand* LAllocator::AllocateFixed(LUnallocated* operand,
+ int pos,
+ bool is_tagged) {
+ TraceAlloc("Allocating fixed reg for op %d\n", operand->virtual_register());
+ ASSERT(operand->HasFixedPolicy());
+ if (operand->HasFixedSlotPolicy()) {
+ operand->ConvertTo(LOperand::STACK_SLOT, operand->fixed_slot_index());
+ } else if (operand->HasFixedRegisterPolicy()) {
+ int reg_index = operand->fixed_register_index();
+ operand->ConvertTo(LOperand::REGISTER, reg_index);
+ } else if (operand->HasFixedDoubleRegisterPolicy()) {
+ int reg_index = operand->fixed_register_index();
+ operand->ConvertTo(LOperand::DOUBLE_REGISTER, reg_index);
+ } else {
+ UNREACHABLE();
+ }
+ if (is_tagged) {
+ TraceAlloc("Fixed reg is tagged at %d\n", pos);
+ LInstruction* instr = InstructionAt(pos);
+ if (instr->HasPointerMap()) {
+ instr->pointer_map()->RecordPointer(operand, chunk()->zone());
+ }
+ }
+ return operand;
+}
+
+
+LiveRange* LAllocator::FixedLiveRangeFor(int index) {
+ ASSERT(index < Register::kMaxNumAllocatableRegisters);
+ LiveRange* result = fixed_live_ranges_[index];
+ if (result == NULL) {
+ result = new(zone()) LiveRange(FixedLiveRangeID(index), chunk()->zone());
+ ASSERT(result->IsFixed());
+ SetLiveRangeAssignedRegister(result, index, GENERAL_REGISTERS);
+ fixed_live_ranges_[index] = result;
+ }
+ return result;
+}
+
+
+LiveRange* LAllocator::FixedDoubleLiveRangeFor(int index) {
+ ASSERT(index < DoubleRegister::NumAllocatableRegisters());
+ LiveRange* result = fixed_double_live_ranges_[index];
+ if (result == NULL) {
+ result = new(zone()) LiveRange(FixedDoubleLiveRangeID(index),
+ chunk()->zone());
+ ASSERT(result->IsFixed());
+ SetLiveRangeAssignedRegister(result, index, DOUBLE_REGISTERS);
+ fixed_double_live_ranges_[index] = result;
+ }
+ return result;
+}
+
+
+LiveRange* LAllocator::LiveRangeFor(int index) {
+ if (index >= live_ranges_.length()) {
+ live_ranges_.AddBlock(NULL, index - live_ranges_.length() + 1, zone());
+ }
+ LiveRange* result = live_ranges_[index];
+ if (result == NULL) {
+ result = new(zone()) LiveRange(index, chunk()->zone());
+ live_ranges_[index] = result;
+ }
+ return result;
+}
+
+
+LGap* LAllocator::GetLastGap(HBasicBlock* block) {
+ int last_instruction = block->last_instruction_index();
+ int index = chunk_->NearestGapPos(last_instruction);
+ return GapAt(index);
+}
+
+
+HPhi* LAllocator::LookupPhi(LOperand* operand) const {
+ if (!operand->IsUnallocated()) return NULL;
+ int index = LUnallocated::cast(operand)->virtual_register();
+ HValue* instr = graph_->LookupValue(index);
+ if (instr != NULL && instr->IsPhi()) {
+ return HPhi::cast(instr);
+ }
+ return NULL;
+}
+
+
+LiveRange* LAllocator::LiveRangeFor(LOperand* operand) {
+ if (operand->IsUnallocated()) {
+ return LiveRangeFor(LUnallocated::cast(operand)->virtual_register());
+ } else if (operand->IsRegister()) {
+ return FixedLiveRangeFor(operand->index());
+ } else if (operand->IsDoubleRegister()) {
+ return FixedDoubleLiveRangeFor(operand->index());
+ } else {
+ return NULL;
+ }
+}
+
+
+void LAllocator::Define(LifetimePosition position,
+ LOperand* operand,
+ LOperand* hint) {
+ LiveRange* range = LiveRangeFor(operand);
+ if (range == NULL) return;
+
+ if (range->IsEmpty() || range->Start().Value() > position.Value()) {
+ // Can happen if there is a definition without use.
+ range->AddUseInterval(position, position.NextInstruction(), zone());
+ range->AddUsePosition(position.NextInstruction(), NULL, NULL, zone());
+ } else {
+ range->ShortenTo(position);
+ }
+
+ if (operand->IsUnallocated()) {
+ LUnallocated* unalloc_operand = LUnallocated::cast(operand);
+ range->AddUsePosition(position, unalloc_operand, hint, zone());
+ }
+}
+
+
+void LAllocator::Use(LifetimePosition block_start,
+ LifetimePosition position,
+ LOperand* operand,
+ LOperand* hint) {
+ LiveRange* range = LiveRangeFor(operand);
+ if (range == NULL) return;
+ if (operand->IsUnallocated()) {
+ LUnallocated* unalloc_operand = LUnallocated::cast(operand);
+ range->AddUsePosition(position, unalloc_operand, hint, zone());
+ }
+ range->AddUseInterval(block_start, position, zone());
+}
+
+
+void LAllocator::AddConstraintsGapMove(int index,
+ LOperand* from,
+ LOperand* to) {
+ LGap* gap = GapAt(index);
+ LParallelMove* move = gap->GetOrCreateParallelMove(LGap::START,
+ chunk()->zone());
+ if (from->IsUnallocated()) {
+ const ZoneList<LMoveOperands>* move_operands = move->move_operands();
+ for (int i = 0; i < move_operands->length(); ++i) {
+ LMoveOperands cur = move_operands->at(i);
+ LOperand* cur_to = cur.destination();
+ if (cur_to->IsUnallocated()) {
+ if (LUnallocated::cast(cur_to)->virtual_register() ==
+ LUnallocated::cast(from)->virtual_register()) {
+ move->AddMove(cur.source(), to, chunk()->zone());
+ return;
+ }
+ }
+ }
+ }
+ move->AddMove(from, to, chunk()->zone());
+}
+
+
+void LAllocator::MeetRegisterConstraints(HBasicBlock* block) {
+ int start = block->first_instruction_index();
+ int end = block->last_instruction_index();
+ if (start == -1) return;
+ for (int i = start; i <= end; ++i) {
+ if (IsGapAt(i)) {
+ LInstruction* instr = NULL;
+ LInstruction* prev_instr = NULL;
+ if (i < end) instr = InstructionAt(i + 1);
+ if (i > start) prev_instr = InstructionAt(i - 1);
+ MeetConstraintsBetween(prev_instr, instr, i);
+ if (!AllocationOk()) return;
+ }
+ }
+}
+
+
+void LAllocator::MeetConstraintsBetween(LInstruction* first,
+ LInstruction* second,
+ int gap_index) {
+ // Handle fixed temporaries.
+ if (first != NULL) {
+ for (TempIterator it(first); !it.Done(); it.Advance()) {
+ LUnallocated* temp = LUnallocated::cast(it.Current());
+ if (temp->HasFixedPolicy()) {
+ AllocateFixed(temp, gap_index - 1, false);
+ }
+ }
+ }
+
+ // Handle fixed output operand.
+ if (first != NULL && first->Output() != NULL) {
+ LUnallocated* first_output = LUnallocated::cast(first->Output());
+ LiveRange* range = LiveRangeFor(first_output->virtual_register());
+ bool assigned = false;
+ if (first_output->HasFixedPolicy()) {
+ LUnallocated* output_copy = first_output->CopyUnconstrained(
+ chunk()->zone());
+ 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.
+ if (first_output->IsStackSlot()) {
+ range->SetSpillOperand(first_output);
+ range->SetSpillStartIndex(gap_index - 1);
+ assigned = true;
+ }
+ chunk_->AddGapMove(gap_index, first_output, output_copy);
+ }
+
+ if (!assigned) {
+ range->SetSpillStartIndex(gap_index);
+
+ // This move to spill operand is not a real use. Liveness analysis
+ // and splitting of live ranges do not account for it.
+ // Thus it should be inserted to a lifetime position corresponding to
+ // the instruction end.
+ LGap* gap = GapAt(gap_index);
+ LParallelMove* move = gap->GetOrCreateParallelMove(LGap::BEFORE,
+ chunk()->zone());
+ move->AddMove(first_output, range->GetSpillOperand(),
+ chunk()->zone());
+ }
+ }
+
+ // Handle fixed input operands of second instruction.
+ if (second != NULL) {
+ for (UseIterator it(second); !it.Done(); it.Advance()) {
+ LUnallocated* cur_input = LUnallocated::cast(it.Current());
+ if (cur_input->HasFixedPolicy()) {
+ LUnallocated* input_copy = cur_input->CopyUnconstrained(
+ chunk()->zone());
+ 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->HasWritableRegisterPolicy()) {
+ // The live range of writable input registers always goes until the end
+ // of the instruction.
+ ASSERT(!cur_input->IsUsedAtStart());
+
+ LUnallocated* input_copy = cur_input->CopyUnconstrained(
+ chunk()->zone());
+ int vreg = GetVirtualRegister();
+ if (!AllocationOk()) return;
+ cur_input->set_virtual_register(vreg);
+
+ if (RequiredRegisterKind(input_copy->virtual_register()) ==
+ DOUBLE_REGISTERS) {
+ double_artificial_registers_.Add(
+ cur_input->virtual_register() - first_artificial_register_,
+ zone());
+ }
+
+ AddConstraintsGapMove(gap_index, input_copy, cur_input);
+ }
+ }
+ }
+
+ // Handle "output same as input" for second instruction.
+ if (second != NULL && second->Output() != NULL) {
+ LUnallocated* second_output = LUnallocated::cast(second->Output());
+ if (second_output->HasSameAsInputPolicy()) {
+ LUnallocated* cur_input = LUnallocated::cast(second->FirstInput());
+ int output_vreg = second_output->virtual_register();
+ int input_vreg = cur_input->virtual_register();
+
+ LUnallocated* input_copy = cur_input->CopyUnconstrained(
+ chunk()->zone());
+ cur_input->set_virtual_register(second_output->virtual_register());
+ AddConstraintsGapMove(gap_index, input_copy, cur_input);
+
+ if (HasTaggedValue(input_vreg) && !HasTaggedValue(output_vreg)) {
+ int index = gap_index + 1;
+ LInstruction* instr = InstructionAt(index);
+ if (instr->HasPointerMap()) {
+ instr->pointer_map()->RecordPointer(input_copy, chunk()->zone());
+ }
+ } else if (!HasTaggedValue(input_vreg) && HasTaggedValue(output_vreg)) {
+ // The input is assumed to immediately have a tagged representation,
+ // before the pointer map can be used. I.e. the pointer map at the
+ // instruction will include the output operand (whose value at the
+ // beginning of the instruction is equal to the input operand). If
+ // this is not desired, then the pointer map at this instruction needs
+ // to be adjusted manually.
+ }
+ }
+ }
+}
+
+
+void LAllocator::ProcessInstructions(HBasicBlock* block, BitVector* live) {
+ int block_start = block->first_instruction_index();
+ int index = block->last_instruction_index();
+
+ LifetimePosition block_start_position =
+ LifetimePosition::FromInstructionIndex(block_start);
+
+ while (index >= block_start) {
+ LifetimePosition curr_position =
+ LifetimePosition::FromInstructionIndex(index);
+
+ if (IsGapAt(index)) {
+ // We have a gap at this position.
+ LGap* gap = GapAt(index);
+ LParallelMove* move = gap->GetOrCreateParallelMove(LGap::START,
+ chunk()->zone());
+ const ZoneList<LMoveOperands>* move_operands = move->move_operands();
+ for (int i = 0; i < move_operands->length(); ++i) {
+ LMoveOperands* cur = &move_operands->at(i);
+ if (cur->IsIgnored()) continue;
+ LOperand* from = cur->source();
+ LOperand* to = cur->destination();
+ HPhi* phi = LookupPhi(to);
+ LOperand* hint = to;
+ if (phi != NULL) {
+ // This is a phi resolving move.
+ if (!phi->block()->IsLoopHeader()) {
+ hint = LiveRangeFor(phi->id())->current_hint_operand();
+ }
+ } else {
+ if (to->IsUnallocated()) {
+ if (live->Contains(LUnallocated::cast(to)->virtual_register())) {
+ Define(curr_position, to, from);
+ live->Remove(LUnallocated::cast(to)->virtual_register());
+ } else {
+ cur->Eliminate();
+ continue;
+ }
+ } else {
+ Define(curr_position, to, from);
+ }
+ }
+ Use(block_start_position, curr_position, from, hint);
+ if (from->IsUnallocated()) {
+ live->Add(LUnallocated::cast(from)->virtual_register());
+ }
+ }
+ } else {
+ ASSERT(!IsGapAt(index));
+ LInstruction* instr = InstructionAt(index);
+
+ if (instr != NULL) {
+ LOperand* output = instr->Output();
+ if (output != NULL) {
+ if (output->IsUnallocated()) {
+ live->Remove(LUnallocated::cast(output)->virtual_register());
+ }
+ Define(curr_position, output, NULL);
+ }
+
+ if (instr->ClobbersRegisters()) {
+ for (int i = 0; i < Register::kMaxNumAllocatableRegisters; ++i) {
+ if (output == NULL || !output->IsRegister() ||
+ output->index() != i) {
+ LiveRange* range = FixedLiveRangeFor(i);
+ range->AddUseInterval(curr_position,
+ curr_position.InstructionEnd(),
+ zone());
+ }
+ }
+ }
+
+ if (instr->ClobbersDoubleRegisters()) {
+ for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); ++i) {
+ if (output == NULL || !output->IsDoubleRegister() ||
+ output->index() != i) {
+ LiveRange* range = FixedDoubleLiveRangeFor(i);
+ range->AddUseInterval(curr_position,
+ curr_position.InstructionEnd(),
+ zone());
+ }
+ }
+ }
+
+ for (UseIterator it(instr); !it.Done(); it.Advance()) {
+ LOperand* input = it.Current();
+
+ LifetimePosition use_pos;
+ if (input->IsUnallocated() &&
+ LUnallocated::cast(input)->IsUsedAtStart()) {
+ use_pos = curr_position;
+ } else {
+ use_pos = curr_position.InstructionEnd();
+ }
+
+ Use(block_start_position, use_pos, input, NULL);
+ if (input->IsUnallocated()) {
+ live->Add(LUnallocated::cast(input)->virtual_register());
+ }
+ }
+
+ for (TempIterator it(instr); !it.Done(); it.Advance()) {
+ LOperand* temp = it.Current();
+ if (instr->ClobbersTemps()) {
+ if (temp->IsRegister()) continue;
+ if (temp->IsUnallocated()) {
+ LUnallocated* temp_unalloc = LUnallocated::cast(temp);
+ if (temp_unalloc->HasFixedPolicy()) {
+ continue;
+ }
+ }
+ }
+ Use(block_start_position, curr_position.InstructionEnd(), temp, NULL);
+ Define(curr_position, temp, NULL);
+ }
+ }
+ }
+
+ index = index - 1;
+ }
+}
+
+
+void LAllocator::ResolvePhis(HBasicBlock* block) {
+ const ZoneList<HPhi*>* phis = block->phis();
+ for (int i = 0; i < phis->length(); ++i) {
+ HPhi* phi = phis->at(i);
+ LUnallocated* phi_operand =
+ new(chunk()->zone()) LUnallocated(LUnallocated::NONE);
+ phi_operand->set_virtual_register(phi->id());
+ for (int j = 0; j < phi->OperandCount(); ++j) {
+ HValue* op = phi->OperandAt(j);
+ LOperand* operand = NULL;
+ if (op->IsConstant() && op->EmitAtUses()) {
+ HConstant* constant = HConstant::cast(op);
+ operand = chunk_->DefineConstantOperand(constant);
+ } else {
+ ASSERT(!op->EmitAtUses());
+ LUnallocated* unalloc =
+ new(chunk()->zone()) LUnallocated(LUnallocated::ANY);
+ unalloc->set_virtual_register(op->id());
+ operand = unalloc;
+ }
+ HBasicBlock* cur_block = block->predecessors()->at(j);
+ // The gap move must be added without any special processing as in
+ // the AddConstraintsGapMove.
+ chunk_->AddGapMove(cur_block->last_instruction_index() - 1,
+ operand,
+ phi_operand);
+
+ // We are going to insert a move before the branch instruction.
+ // Some branch instructions (e.g. loops' back edges)
+ // can potentially cause a GC so they have a pointer map.
+ // By inserting a move we essentially create a copy of a
+ // value which is invisible to PopulatePointerMaps(), because we store
+ // it into a location different from the operand of a live range
+ // covering a branch instruction.
+ // Thus we need to manually record a pointer.
+ LInstruction* branch =
+ InstructionAt(cur_block->last_instruction_index());
+ if (branch->HasPointerMap()) {
+ if (phi->representation().IsTagged() && !phi->type().IsSmi()) {
+ branch->pointer_map()->RecordPointer(phi_operand, chunk()->zone());
+ } else if (!phi->representation().IsDouble()) {
+ branch->pointer_map()->RecordUntagged(phi_operand, chunk()->zone());
+ }
+ }
+ }
+
+ LiveRange* live_range = LiveRangeFor(phi->id());
+ LLabel* label = chunk_->GetLabel(phi->block()->block_id());
+ label->GetOrCreateParallelMove(LGap::START, chunk()->zone())->
+ AddMove(phi_operand, live_range->GetSpillOperand(), chunk()->zone());
+ live_range->SetSpillStartIndex(phi->block()->first_instruction_index());
+ }
+}
+
+
+bool LAllocator::Allocate(LChunk* chunk) {
+ ASSERT(chunk_ == NULL);
+ chunk_ = static_cast<LPlatformChunk*>(chunk);
+ assigned_registers_ =
+ new(chunk->zone()) BitVector(Register::NumAllocatableRegisters(),
+ chunk->zone());
+ assigned_double_registers_ =
+ new(chunk->zone()) BitVector(DoubleRegister::NumAllocatableRegisters(),
+ chunk->zone());
+ MeetRegisterConstraints();
+ if (!AllocationOk()) return false;
+ ResolvePhis();
+ BuildLiveRanges();
+ AllocateGeneralRegisters();
+ if (!AllocationOk()) return false;
+ AllocateDoubleRegisters();
+ if (!AllocationOk()) return false;
+ PopulatePointerMaps();
+ ConnectRanges();
+ ResolveControlFlow();
+ return true;
+}
+
+
+void LAllocator::MeetRegisterConstraints() {
+ LAllocatorPhase phase("L_Register constraints", this);
+ first_artificial_register_ = next_virtual_register_;
+ const ZoneList<HBasicBlock*>* blocks = graph_->blocks();
+ for (int i = 0; i < blocks->length(); ++i) {
+ HBasicBlock* block = blocks->at(i);
+ MeetRegisterConstraints(block);
+ if (!AllocationOk()) return;
+ }
+}
+
+
+void LAllocator::ResolvePhis() {
+ LAllocatorPhase phase("L_Resolve phis", this);
+
+ // Process the blocks in reverse order.
+ const ZoneList<HBasicBlock*>* blocks = graph_->blocks();
+ for (int block_id = blocks->length() - 1; block_id >= 0; --block_id) {
+ HBasicBlock* block = blocks->at(block_id);
+ ResolvePhis(block);
+ }
+}
+
+
+void LAllocator::ResolveControlFlow(LiveRange* range,
+ HBasicBlock* block,
+ HBasicBlock* pred) {
+ LifetimePosition pred_end =
+ LifetimePosition::FromInstructionIndex(pred->last_instruction_index());
+ LifetimePosition cur_start =
+ LifetimePosition::FromInstructionIndex(block->first_instruction_index());
+ LiveRange* pred_cover = NULL;
+ LiveRange* cur_cover = NULL;
+ LiveRange* cur_range = range;
+ while (cur_range != NULL && (cur_cover == NULL || pred_cover == NULL)) {
+ if (cur_range->CanCover(cur_start)) {
+ ASSERT(cur_cover == NULL);
+ cur_cover = cur_range;
+ }
+ if (cur_range->CanCover(pred_end)) {
+ ASSERT(pred_cover == NULL);
+ pred_cover = cur_range;
+ }
+ cur_range = cur_range->next();
+ }
+
+ if (cur_cover->IsSpilled()) return;
+ ASSERT(pred_cover != NULL && cur_cover != NULL);
+ if (pred_cover != cur_cover) {
+ LOperand* pred_op = pred_cover->CreateAssignedOperand(chunk()->zone());
+ LOperand* cur_op = cur_cover->CreateAssignedOperand(chunk()->zone());
+ if (!pred_op->Equals(cur_op)) {
+ LGap* gap = NULL;
+ if (block->predecessors()->length() == 1) {
+ gap = GapAt(block->first_instruction_index());
+ } else {
+ ASSERT(pred->end()->SecondSuccessor() == NULL);
+ gap = GetLastGap(pred);
+
+ // We are going to insert a move before the branch instruction.
+ // Some branch instructions (e.g. loops' back edges)
+ // can potentially cause a GC so they have a pointer map.
+ // By inserting a move we essentially create a copy of a
+ // value which is invisible to PopulatePointerMaps(), because we store
+ // it into a location different from the operand of a live range
+ // covering a branch instruction.
+ // Thus we need to manually record a pointer.
+ LInstruction* branch = InstructionAt(pred->last_instruction_index());
+ if (branch->HasPointerMap()) {
+ if (HasTaggedValue(range->id())) {
+ branch->pointer_map()->RecordPointer(cur_op, chunk()->zone());
+ } else if (!cur_op->IsDoubleStackSlot() &&
+ !cur_op->IsDoubleRegister()) {
+ branch->pointer_map()->RemovePointer(cur_op);
+ }
+ }
+ }
+ gap->GetOrCreateParallelMove(
+ LGap::START, chunk()->zone())->AddMove(pred_op, cur_op,
+ chunk()->zone());
+ }
+ }
+}
+
+
+LParallelMove* LAllocator::GetConnectingParallelMove(LifetimePosition pos) {
+ int index = pos.InstructionIndex();
+ if (IsGapAt(index)) {
+ LGap* gap = GapAt(index);
+ return gap->GetOrCreateParallelMove(
+ pos.IsInstructionStart() ? LGap::START : LGap::END, chunk()->zone());
+ }
+ int gap_pos = pos.IsInstructionStart() ? (index - 1) : (index + 1);
+ return GapAt(gap_pos)->GetOrCreateParallelMove(
+ (gap_pos < index) ? LGap::AFTER : LGap::BEFORE, chunk()->zone());
+}
+
+
+HBasicBlock* LAllocator::GetBlock(LifetimePosition pos) {
+ LGap* gap = GapAt(chunk_->NearestGapPos(pos.InstructionIndex()));
+ return gap->block();
+}
+
+
+void LAllocator::ConnectRanges() {
+ LAllocatorPhase phase("L_Connect ranges", this);
+ for (int i = 0; i < live_ranges()->length(); ++i) {
+ LiveRange* first_range = live_ranges()->at(i);
+ if (first_range == NULL || first_range->parent() != NULL) continue;
+
+ LiveRange* second_range = first_range->next();
+ while (second_range != NULL) {
+ LifetimePosition pos = second_range->Start();
+
+ if (!second_range->IsSpilled()) {
+ // Add gap move if the two live ranges touch and there is no block
+ // boundary.
+ if (first_range->End().Value() == pos.Value()) {
+ bool should_insert = true;
+ if (IsBlockBoundary(pos)) {
+ should_insert = CanEagerlyResolveControlFlow(GetBlock(pos));
+ }
+ if (should_insert) {
+ LParallelMove* move = GetConnectingParallelMove(pos);
+ LOperand* prev_operand = first_range->CreateAssignedOperand(
+ chunk()->zone());
+ LOperand* cur_operand = second_range->CreateAssignedOperand(
+ chunk()->zone());
+ move->AddMove(prev_operand, cur_operand,
+ chunk()->zone());
+ }
+ }
+ }
+
+ first_range = second_range;
+ second_range = second_range->next();
+ }
+ }
+}
+
+
+bool LAllocator::CanEagerlyResolveControlFlow(HBasicBlock* block) const {
+ if (block->predecessors()->length() != 1) return false;
+ return block->predecessors()->first()->block_id() == block->block_id() - 1;
+}
+
+
+void LAllocator::ResolveControlFlow() {
+ LAllocatorPhase phase("L_Resolve control flow", this);
+ const ZoneList<HBasicBlock*>* blocks = graph_->blocks();
+ for (int block_id = 1; block_id < blocks->length(); ++block_id) {
+ HBasicBlock* block = blocks->at(block_id);
+ if (CanEagerlyResolveControlFlow(block)) continue;
+ BitVector* live = live_in_sets_[block->block_id()];
+ BitVector::Iterator iterator(live);
+ while (!iterator.Done()) {
+ int operand_index = iterator.Current();
+ for (int i = 0; i < block->predecessors()->length(); ++i) {
+ HBasicBlock* cur = block->predecessors()->at(i);
+ LiveRange* cur_range = LiveRangeFor(operand_index);
+ ResolveControlFlow(cur_range, block, cur);
+ }
+ iterator.Advance();
+ }
+ }
+}
+
+
+void LAllocator::BuildLiveRanges() {
+ LAllocatorPhase phase("L_Build live ranges", this);
+ InitializeLivenessAnalysis();
+ // Process the blocks in reverse order.
+ const ZoneList<HBasicBlock*>* blocks = graph_->blocks();
+ for (int block_id = blocks->length() - 1; block_id >= 0; --block_id) {
+ HBasicBlock* block = blocks->at(block_id);
+ BitVector* live = ComputeLiveOut(block);
+ // Initially consider all live_out values live for the entire block. We
+ // will shorten these intervals if necessary.
+ AddInitialIntervals(block, live);
+
+ // Process the instructions in reverse order, generating and killing
+ // live values.
+ ProcessInstructions(block, live);
+ // All phi output operands are killed by this block.
+ const ZoneList<HPhi*>* phis = block->phis();
+ for (int i = 0; i < phis->length(); ++i) {
+ // The live range interval already ends at the first instruction of the
+ // block.
+ HPhi* phi = phis->at(i);
+ live->Remove(phi->id());
+
+ LOperand* hint = NULL;
+ LOperand* phi_operand = NULL;
+ LGap* gap = GetLastGap(phi->block()->predecessors()->at(0));
+ LParallelMove* move = gap->GetOrCreateParallelMove(LGap::START,
+ chunk()->zone());
+ for (int j = 0; j < move->move_operands()->length(); ++j) {
+ LOperand* to = move->move_operands()->at(j).destination();
+ if (to->IsUnallocated() &&
+ LUnallocated::cast(to)->virtual_register() == phi->id()) {
+ hint = move->move_operands()->at(j).source();
+ phi_operand = to;
+ break;
+ }
+ }
+ ASSERT(hint != NULL);
+
+ LifetimePosition block_start = LifetimePosition::FromInstructionIndex(
+ block->first_instruction_index());
+ Define(block_start, phi_operand, hint);
+ }
+
+ // Now live is live_in for this block except not including values live
+ // out on backward successor edges.
+ live_in_sets_[block_id] = live;
+
+ // If this block is a loop header go back and patch up the necessary
+ // predecessor blocks.
+ if (block->IsLoopHeader()) {
+ // TODO(kmillikin): Need to be able to get the last block of the loop
+ // in the loop information. Add a live range stretching from the first
+ // loop instruction to the last for each value live on entry to the
+ // header.
+ HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
+ BitVector::Iterator iterator(live);
+ LifetimePosition start = LifetimePosition::FromInstructionIndex(
+ block->first_instruction_index());
+ LifetimePosition end = LifetimePosition::FromInstructionIndex(
+ back_edge->last_instruction_index()).NextInstruction();
+ while (!iterator.Done()) {
+ int operand_index = iterator.Current();
+ LiveRange* range = LiveRangeFor(operand_index);
+ range->EnsureInterval(start, end, zone());
+ iterator.Advance();
+ }
+
+ for (int i = block->block_id() + 1; i <= back_edge->block_id(); ++i) {
+ live_in_sets_[i]->Union(*live);
+ }
+ }
+
+#ifdef DEBUG
+ if (block_id == 0) {
+ BitVector::Iterator iterator(live);
+ bool found = false;
+ while (!iterator.Done()) {
+ found = true;
+ int operand_index = iterator.Current();
+ if (chunk_->info()->IsStub()) {
+ CodeStub::Major major_key = chunk_->info()->code_stub()->MajorKey();
+ PrintF("Function: %s\n", CodeStub::MajorName(major_key, false));
+ } else {
+ ASSERT(chunk_->info()->IsOptimizing());
+ AllowHandleDereference allow_deref;
+ PrintF("Function: %s\n",
+ *chunk_->info()->function()->debug_name()->ToCString());
+ }
+ PrintF("Value %d used before first definition!\n", operand_index);
+ LiveRange* range = LiveRangeFor(operand_index);
+ PrintF("First use is at %d\n", range->first_pos()->pos().Value());
+ iterator.Advance();
+ }
+ ASSERT(!found);
+ }
+#endif
+ }
+}
+
+
+bool LAllocator::SafePointsAreInOrder() const {
+ const ZoneList<LPointerMap*>* pointer_maps = chunk_->pointer_maps();
+ int safe_point = 0;
+ for (int i = 0; i < pointer_maps->length(); ++i) {
+ LPointerMap* map = pointer_maps->at(i);
+ if (safe_point > map->lithium_position()) return false;
+ safe_point = map->lithium_position();
+ }
+ return true;
+}
+
+
+void LAllocator::PopulatePointerMaps() {
+ LAllocatorPhase phase("L_Populate pointer maps", this);
+ const ZoneList<LPointerMap*>* pointer_maps = chunk_->pointer_maps();
+
+ ASSERT(SafePointsAreInOrder());
+
+ // Iterate over all safe point positions and record a pointer
+ // for all spilled live ranges at this point.
+ int first_safe_point_index = 0;
+ int last_range_start = 0;
+ for (int range_idx = 0; range_idx < live_ranges()->length(); ++range_idx) {
+ LiveRange* range = live_ranges()->at(range_idx);
+ if (range == NULL) continue;
+ // Iterate over the first parts of multi-part live ranges.
+ if (range->parent() != NULL) continue;
+ // Skip non-pointer values.
+ if (!HasTaggedValue(range->id())) continue;
+ // Skip empty live ranges.
+ if (range->IsEmpty()) continue;
+
+ // Find the extent of the range and its children.
+ int start = range->Start().InstructionIndex();
+ int end = 0;
+ for (LiveRange* cur = range; cur != NULL; cur = cur->next()) {
+ LifetimePosition this_end = cur->End();
+ if (this_end.InstructionIndex() > end) end = this_end.InstructionIndex();
+ ASSERT(cur->Start().InstructionIndex() >= start);
+ }
+
+ // Most of the ranges are in order, but not all. Keep an eye on when
+ // they step backwards and reset the first_safe_point_index so we don't
+ // miss any safe points.
+ if (start < last_range_start) {
+ first_safe_point_index = 0;
+ }
+ last_range_start = start;
+
+ // Step across all the safe points that are before the start of this range,
+ // recording how far we step in order to save doing this for the next range.
+ while (first_safe_point_index < pointer_maps->length()) {
+ LPointerMap* map = pointer_maps->at(first_safe_point_index);
+ int safe_point = map->lithium_position();
+ if (safe_point >= start) break;
+ first_safe_point_index++;
+ }
+
+ // Step through the safe points to see whether they are in the range.
+ for (int safe_point_index = first_safe_point_index;
+ safe_point_index < pointer_maps->length();
+ ++safe_point_index) {
+ LPointerMap* map = pointer_maps->at(safe_point_index);
+ int safe_point = map->lithium_position();
+
+ // The safe points are sorted so we can stop searching here.
+ if (safe_point - 1 > end) break;
+
+ // Advance to the next active range that covers the current
+ // safe point position.
+ LifetimePosition safe_point_pos =
+ LifetimePosition::FromInstructionIndex(safe_point);
+ LiveRange* cur = range;
+ while (cur != NULL && !cur->Covers(safe_point_pos)) {
+ cur = cur->next();
+ }
+ if (cur == NULL) continue;
+
+ // Check if the live range is spilled and the safe point is after
+ // the spill position.
+ if (range->HasAllocatedSpillOperand() &&
+ safe_point >= range->spill_start_index()) {
+ TraceAlloc("Pointer for range %d (spilled at %d) at safe point %d\n",
+ range->id(), range->spill_start_index(), safe_point);
+ map->RecordPointer(range->GetSpillOperand(), chunk()->zone());
+ }
+
+ if (!cur->IsSpilled()) {
+ TraceAlloc("Pointer in register for range %d (start at %d) "
+ "at safe point %d\n",
+ cur->id(), cur->Start().Value(), safe_point);
+ LOperand* operand = cur->CreateAssignedOperand(chunk()->zone());
+ ASSERT(!operand->IsStackSlot());
+ map->RecordPointer(operand, chunk()->zone());
+ }
+ }
+ }
+}
+
+
+void LAllocator::AllocateGeneralRegisters() {
+ LAllocatorPhase phase("L_Allocate general registers", this);
+ num_registers_ = Register::NumAllocatableRegisters();
+ AllocateRegisters();
+}
+
+
+void LAllocator::AllocateDoubleRegisters() {
+ LAllocatorPhase phase("L_Allocate double registers", this);
+ num_registers_ = DoubleRegister::NumAllocatableRegisters();
+ mode_ = DOUBLE_REGISTERS;
+ AllocateRegisters();
+}
+
+
+void LAllocator::AllocateRegisters() {
+ ASSERT(unhandled_live_ranges_.is_empty());
+
+ for (int i = 0; i < live_ranges_.length(); ++i) {
+ if (live_ranges_[i] != NULL) {
+ if (RequiredRegisterKind(live_ranges_[i]->id()) == mode_) {
+ AddToUnhandledUnsorted(live_ranges_[i]);
+ }
+ }
+ }
+ SortUnhandled();
+ ASSERT(UnhandledIsSorted());
+
+ ASSERT(reusable_slots_.is_empty());
+ ASSERT(active_live_ranges_.is_empty());
+ ASSERT(inactive_live_ranges_.is_empty());
+
+ if (mode_ == DOUBLE_REGISTERS) {
+ for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); ++i) {
+ LiveRange* current = fixed_double_live_ranges_.at(i);
+ if (current != NULL) {
+ AddToInactive(current);
+ }
+ }
+ } else {
+ for (int i = 0; i < fixed_live_ranges_.length(); ++i) {
+ LiveRange* current = fixed_live_ranges_.at(i);
+ if (current != NULL) {
+ AddToInactive(current);
+ }
+ }
+ }
+
+ while (!unhandled_live_ranges_.is_empty()) {
+ ASSERT(UnhandledIsSorted());
+ LiveRange* current = unhandled_live_ranges_.RemoveLast();
+ ASSERT(UnhandledIsSorted());
+ LifetimePosition position = current->Start();
+#ifdef DEBUG
+ allocation_finger_ = position;
+#endif
+ TraceAlloc("Processing interval %d start=%d\n",
+ current->id(),
+ position.Value());
+
+ if (current->HasAllocatedSpillOperand()) {
+ TraceAlloc("Live range %d already has a spill operand\n", current->id());
+ LifetimePosition next_pos = position;
+ if (IsGapAt(next_pos.InstructionIndex())) {
+ next_pos = next_pos.NextInstruction();
+ }
+ UsePosition* pos = current->NextUsePositionRegisterIsBeneficial(next_pos);
+ // If the range already has a spill operand and it doesn't need a
+ // register immediately, split it and spill the first part of the range.
+ if (pos == NULL) {
+ Spill(current);
+ continue;
+ } else if (pos->pos().Value() >
+ current->Start().NextInstruction().Value()) {
+ // 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;
+ }
+ }
+
+ for (int i = 0; i < active_live_ranges_.length(); ++i) {
+ LiveRange* cur_active = active_live_ranges_.at(i);
+ if (cur_active->End().Value() <= position.Value()) {
+ ActiveToHandled(cur_active);
+ --i; // The live range was removed from the list of active live ranges.
+ } else if (!cur_active->Covers(position)) {
+ ActiveToInactive(cur_active);
+ --i; // The live range was removed from the list of active live ranges.
+ }
+ }
+
+ for (int i = 0; i < inactive_live_ranges_.length(); ++i) {
+ LiveRange* cur_inactive = inactive_live_ranges_.at(i);
+ if (cur_inactive->End().Value() <= position.Value()) {
+ InactiveToHandled(cur_inactive);
+ --i; // Live range was removed from the list of inactive live ranges.
+ } else if (cur_inactive->Covers(position)) {
+ InactiveToActive(cur_inactive);
+ --i; // Live range was removed from the list of inactive live ranges.
+ }
+ }
+
+ ASSERT(!current->HasRegisterAssigned() && !current->IsSpilled());
+
+ bool result = TryAllocateFreeReg(current);
+ if (!AllocationOk()) return;
+
+ if (!result) AllocateBlockedReg(current);
+ if (!AllocationOk()) return;
+
+ if (current->HasRegisterAssigned()) {
+ AddToActive(current);
+ }
+ }
+
+ reusable_slots_.Rewind(0);
+ active_live_ranges_.Rewind(0);
+ inactive_live_ranges_.Rewind(0);
+}
+
+
+const char* LAllocator::RegisterName(int allocation_index) {
+ if (mode_ == GENERAL_REGISTERS) {
+ return Register::AllocationIndexToString(allocation_index);
+ } else {
+ return DoubleRegister::AllocationIndexToString(allocation_index);
+ }
+}
+
+
+void LAllocator::TraceAlloc(const char* msg, ...) {
+ if (FLAG_trace_alloc) {
+ va_list arguments;
+ va_start(arguments, msg);
+ OS::VPrint(msg, arguments);
+ va_end(arguments);
+ }
+}
+
+
+bool LAllocator::HasTaggedValue(int virtual_register) const {
+ HValue* value = graph_->LookupValue(virtual_register);
+ if (value == NULL) return false;
+ return value->representation().IsTagged() && !value->type().IsSmi();
+}
+
+
+RegisterKind LAllocator::RequiredRegisterKind(int virtual_register) const {
+ if (virtual_register < first_artificial_register_) {
+ HValue* value = graph_->LookupValue(virtual_register);
+ if (value != NULL && value->representation().IsDouble()) {
+ return DOUBLE_REGISTERS;
+ }
+ } else if (double_artificial_registers_.Contains(
+ virtual_register - first_artificial_register_)) {
+ return DOUBLE_REGISTERS;
+ }
+
+ return GENERAL_REGISTERS;
+}
+
+
+void LAllocator::AddToActive(LiveRange* range) {
+ TraceAlloc("Add live range %d to active\n", range->id());
+ active_live_ranges_.Add(range, zone());
+}
+
+
+void LAllocator::AddToInactive(LiveRange* range) {
+ TraceAlloc("Add live range %d to inactive\n", range->id());
+ inactive_live_ranges_.Add(range, zone());
+}
+
+
+void LAllocator::AddToUnhandledSorted(LiveRange* range) {
+ if (range == NULL || range->IsEmpty()) return;
+ ASSERT(!range->HasRegisterAssigned() && !range->IsSpilled());
+ ASSERT(allocation_finger_.Value() <= range->Start().Value());
+ for (int i = unhandled_live_ranges_.length() - 1; i >= 0; --i) {
+ LiveRange* cur_range = unhandled_live_ranges_.at(i);
+ if (range->ShouldBeAllocatedBefore(cur_range)) {
+ TraceAlloc("Add live range %d to unhandled at %d\n", range->id(), i + 1);
+ unhandled_live_ranges_.InsertAt(i + 1, range, zone());
+ ASSERT(UnhandledIsSorted());
+ return;
+ }
+ }
+ TraceAlloc("Add live range %d to unhandled at start\n", range->id());
+ unhandled_live_ranges_.InsertAt(0, range, zone());
+ ASSERT(UnhandledIsSorted());
+}
+
+
+void LAllocator::AddToUnhandledUnsorted(LiveRange* range) {
+ if (range == NULL || range->IsEmpty()) return;
+ ASSERT(!range->HasRegisterAssigned() && !range->IsSpilled());
+ TraceAlloc("Add live range %d to unhandled unsorted at end\n", range->id());
+ unhandled_live_ranges_.Add(range, zone());
+}
+
+
+static int UnhandledSortHelper(LiveRange* const* a, LiveRange* const* b) {
+ ASSERT(!(*a)->ShouldBeAllocatedBefore(*b) ||
+ !(*b)->ShouldBeAllocatedBefore(*a));
+ if ((*a)->ShouldBeAllocatedBefore(*b)) return 1;
+ if ((*b)->ShouldBeAllocatedBefore(*a)) return -1;
+ return (*a)->id() - (*b)->id();
+}
+
+
+// Sort the unhandled live ranges so that the ranges to be processed first are
+// at the end of the array list. This is convenient for the register allocation
+// algorithm because it is efficient to remove elements from the end.
+void LAllocator::SortUnhandled() {
+ TraceAlloc("Sort unhandled\n");
+ unhandled_live_ranges_.Sort(&UnhandledSortHelper);
+}
+
+
+bool LAllocator::UnhandledIsSorted() {
+ int len = unhandled_live_ranges_.length();
+ for (int i = 1; i < len; i++) {
+ LiveRange* a = unhandled_live_ranges_.at(i - 1);
+ LiveRange* b = unhandled_live_ranges_.at(i);
+ if (a->Start().Value() < b->Start().Value()) return false;
+ }
+ return true;
+}
+
+
+void LAllocator::FreeSpillSlot(LiveRange* range) {
+ // Check that we are the last range.
+ if (range->next() != NULL) return;
+
+ if (!range->TopLevel()->HasAllocatedSpillOperand()) return;
+
+ int index = range->TopLevel()->GetSpillOperand()->index();
+ if (index >= 0) {
+ reusable_slots_.Add(range, zone());
+ }
+}
+
+
+LOperand* LAllocator::TryReuseSpillSlot(LiveRange* range) {
+ if (reusable_slots_.is_empty()) return NULL;
+ if (reusable_slots_.first()->End().Value() >
+ range->TopLevel()->Start().Value()) {
+ return NULL;
+ }
+ LOperand* result = reusable_slots_.first()->TopLevel()->GetSpillOperand();
+ reusable_slots_.Remove(0);
+ return result;
+}
+
+
+void LAllocator::ActiveToHandled(LiveRange* range) {
+ ASSERT(active_live_ranges_.Contains(range));
+ active_live_ranges_.RemoveElement(range);
+ TraceAlloc("Moving live range %d from active to handled\n", range->id());
+ FreeSpillSlot(range);
+}
+
+
+void LAllocator::ActiveToInactive(LiveRange* range) {
+ ASSERT(active_live_ranges_.Contains(range));
+ active_live_ranges_.RemoveElement(range);
+ inactive_live_ranges_.Add(range, zone());
+ TraceAlloc("Moving live range %d from active to inactive\n", range->id());
+}
+
+
+void LAllocator::InactiveToHandled(LiveRange* range) {
+ ASSERT(inactive_live_ranges_.Contains(range));
+ inactive_live_ranges_.RemoveElement(range);
+ TraceAlloc("Moving live range %d from inactive to handled\n", range->id());
+ FreeSpillSlot(range);
+}
+
+
+void LAllocator::InactiveToActive(LiveRange* range) {
+ ASSERT(inactive_live_ranges_.Contains(range));
+ inactive_live_ranges_.RemoveElement(range);
+ active_live_ranges_.Add(range, zone());
+ TraceAlloc("Moving live range %d from inactive to active\n", range->id());
+}
+
+
+// TryAllocateFreeReg and AllocateBlockedReg assume this
+// when allocating local arrays.
+STATIC_ASSERT(DoubleRegister::kMaxNumAllocatableRegisters >=
+ Register::kMaxNumAllocatableRegisters);
+
+
+bool LAllocator::TryAllocateFreeReg(LiveRange* current) {
+ LifetimePosition free_until_pos[DoubleRegister::kMaxNumAllocatableRegisters];
+
+ for (int i = 0; i < num_registers_; i++) {
+ free_until_pos[i] = LifetimePosition::MaxPosition();
+ }
+
+ for (int i = 0; i < active_live_ranges_.length(); ++i) {
+ LiveRange* cur_active = active_live_ranges_.at(i);
+ free_until_pos[cur_active->assigned_register()] =
+ LifetimePosition::FromInstructionIndex(0);
+ }
+
+ for (int i = 0; i < inactive_live_ranges_.length(); ++i) {
+ LiveRange* cur_inactive = inactive_live_ranges_.at(i);
+ ASSERT(cur_inactive->End().Value() > current->Start().Value());
+ LifetimePosition next_intersection =
+ cur_inactive->FirstIntersection(current);
+ if (!next_intersection.IsValid()) continue;
+ int cur_reg = cur_inactive->assigned_register();
+ free_until_pos[cur_reg] = Min(free_until_pos[cur_reg], next_intersection);
+ }
+
+ LOperand* hint = current->FirstHint();
+ if (hint != NULL && (hint->IsRegister() || hint->IsDoubleRegister())) {
+ int register_index = hint->index();
+ TraceAlloc(
+ "Found reg hint %s (free until [%d) for live range %d (end %d[).\n",
+ RegisterName(register_index),
+ free_until_pos[register_index].Value(),
+ current->id(),
+ current->End().Value());
+
+ // The desired register is free until the end of the current live range.
+ if (free_until_pos[register_index].Value() >= current->End().Value()) {
+ TraceAlloc("Assigning preferred reg %s to live range %d\n",
+ RegisterName(register_index),
+ current->id());
+ SetLiveRangeAssignedRegister(current, register_index, mode_);
+ return true;
+ }
+ }
+
+ // Find the register which stays free for the longest time.
+ int reg = 0;
+ for (int i = 1; i < RegisterCount(); ++i) {
+ if (free_until_pos[i].Value() > free_until_pos[reg].Value()) {
+ reg = i;
+ }
+ }
+
+ LifetimePosition pos = free_until_pos[reg];
+
+ if (pos.Value() <= current->Start().Value()) {
+ // All registers are blocked.
+ return false;
+ }
+
+ 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 = SplitRangeAt(current, pos);
+ if (!AllocationOk()) return false;
+ AddToUnhandledSorted(tail);
+ }
+
+
+ // Register reg is available at the range start and is free until
+ // the range end.
+ ASSERT(pos.Value() >= current->End().Value());
+ TraceAlloc("Assigning free reg %s to live range %d\n",
+ RegisterName(reg),
+ current->id());
+ SetLiveRangeAssignedRegister(current, reg, mode_);
+
+ return true;
+}
+
+
+void LAllocator::AllocateBlockedReg(LiveRange* current) {
+ UsePosition* register_use = current->NextRegisterPosition(current->Start());
+ if (register_use == NULL) {
+ // There is no use in the current live range that requires a register.
+ // We can just spill it.
+ Spill(current);
+ return;
+ }
+
+
+ LifetimePosition use_pos[DoubleRegister::kMaxNumAllocatableRegisters];
+ LifetimePosition block_pos[DoubleRegister::kMaxNumAllocatableRegisters];
+
+ for (int i = 0; i < num_registers_; i++) {
+ use_pos[i] = block_pos[i] = LifetimePosition::MaxPosition();
+ }
+
+ for (int i = 0; i < active_live_ranges_.length(); ++i) {
+ LiveRange* range = active_live_ranges_[i];
+ int cur_reg = range->assigned_register();
+ if (range->IsFixed() || !range->CanBeSpilled(current->Start())) {
+ block_pos[cur_reg] = use_pos[cur_reg] =
+ LifetimePosition::FromInstructionIndex(0);
+ } else {
+ UsePosition* next_use = range->NextUsePositionRegisterIsBeneficial(
+ current->Start());
+ if (next_use == NULL) {
+ use_pos[cur_reg] = range->End();
+ } else {
+ use_pos[cur_reg] = next_use->pos();
+ }
+ }
+ }
+
+ for (int i = 0; i < inactive_live_ranges_.length(); ++i) {
+ LiveRange* range = inactive_live_ranges_.at(i);
+ ASSERT(range->End().Value() > current->Start().Value());
+ LifetimePosition next_intersection = range->FirstIntersection(current);
+ if (!next_intersection.IsValid()) continue;
+ int cur_reg = range->assigned_register();
+ if (range->IsFixed()) {
+ block_pos[cur_reg] = Min(block_pos[cur_reg], next_intersection);
+ use_pos[cur_reg] = Min(block_pos[cur_reg], use_pos[cur_reg]);
+ } else {
+ use_pos[cur_reg] = Min(use_pos[cur_reg], next_intersection);
+ }
+ }
+
+ int reg = 0;
+ for (int i = 1; i < RegisterCount(); ++i) {
+ if (use_pos[i].Value() > use_pos[reg].Value()) {
+ reg = i;
+ }
+ }
+
+ LifetimePosition pos = use_pos[reg];
+
+ if (pos.Value() < register_use->pos().Value()) {
+ // All registers are blocked before the first use that requires a register.
+ // Spill starting part of live range up to that use.
+ SpillBetween(current, current->Start(), register_use->pos());
+ return;
+ }
+
+ if (block_pos[reg].Value() < current->End().Value()) {
+ // Register becomes blocked before the current range end. Split before that
+ // position.
+ LiveRange* tail = SplitBetween(current,
+ current->Start(),
+ block_pos[reg].InstructionStart());
+ if (!AllocationOk()) return;
+ AddToUnhandledSorted(tail);
+ }
+
+ // Register reg is not blocked for the whole range.
+ ASSERT(block_pos[reg].Value() >= current->End().Value());
+ TraceAlloc("Assigning blocked reg %s to live range %d\n",
+ RegisterName(reg),
+ current->id());
+ SetLiveRangeAssignedRegister(current, reg, mode_);
+
+ // This register was not free. Thus we need to find and spill
+ // parts of active and inactive live regions that use the same register
+ // at the same lifetime positions as current.
+ SplitAndSpillIntersecting(current);
+}
+
+
+LifetimePosition LAllocator::FindOptimalSpillingPos(LiveRange* range,
+ LifetimePosition pos) {
+ HBasicBlock* block = GetBlock(pos.InstructionStart());
+ HBasicBlock* loop_header =
+ block->IsLoopHeader() ? block : block->parent_loop_header();
+
+ if (loop_header == NULL) return pos;
+
+ UsePosition* prev_use =
+ range->PreviousUsePositionRegisterIsBeneficial(pos);
+
+ while (loop_header != NULL) {
+ // We are going to spill live range inside the loop.
+ // If possible try to move spilling position backwards to loop header.
+ // This will reduce number of memory moves on the back edge.
+ LifetimePosition loop_start = LifetimePosition::FromInstructionIndex(
+ loop_header->first_instruction_index());
+
+ if (range->Covers(loop_start)) {
+ if (prev_use == NULL || prev_use->pos().Value() < loop_start.Value()) {
+ // No register beneficial use inside the loop before the pos.
+ pos = loop_start;
+ }
+ }
+
+ // Try hoisting out to an outer loop.
+ loop_header = loop_header->parent_loop_header();
+ }
+
+ return pos;
+}
+
+
+void LAllocator::SplitAndSpillIntersecting(LiveRange* current) {
+ ASSERT(current->HasRegisterAssigned());
+ int reg = current->assigned_register();
+ LifetimePosition split_pos = current->Start();
+ for (int i = 0; i < active_live_ranges_.length(); ++i) {
+ LiveRange* range = active_live_ranges_[i];
+ if (range->assigned_register() == reg) {
+ UsePosition* next_pos = range->NextRegisterPosition(current->Start());
+ LifetimePosition spill_pos = FindOptimalSpillingPos(range, split_pos);
+ if (next_pos == NULL) {
+ SpillAfter(range, spill_pos);
+ } else {
+ // When spilling between spill_pos and next_pos ensure that the range
+ // remains spilled at least until the start of the current live range.
+ // This guarantees that we will not introduce new unhandled ranges that
+ // start before the current range as this violates allocation invariant
+ // and will lead to an inconsistent state of active and inactive
+ // live-ranges: ranges are allocated in order of their start positions,
+ // ranges are retired from active/inactive when the start of the
+ // current live-range is larger than their end.
+ SpillBetweenUntil(range, spill_pos, current->Start(), next_pos->pos());
+ }
+ if (!AllocationOk()) return;
+ ActiveToHandled(range);
+ --i;
+ }
+ }
+
+ for (int i = 0; i < inactive_live_ranges_.length(); ++i) {
+ LiveRange* range = inactive_live_ranges_[i];
+ ASSERT(range->End().Value() > current->Start().Value());
+ if (range->assigned_register() == reg && !range->IsFixed()) {
+ LifetimePosition next_intersection = range->FirstIntersection(current);
+ if (next_intersection.IsValid()) {
+ UsePosition* next_pos = range->NextRegisterPosition(current->Start());
+ if (next_pos == NULL) {
+ SpillAfter(range, split_pos);
+ } else {
+ next_intersection = Min(next_intersection, next_pos->pos());
+ SpillBetween(range, split_pos, next_intersection);
+ }
+ if (!AllocationOk()) return;
+ InactiveToHandled(range);
+ --i;
+ }
+ }
+ }
+}
+
+
+bool LAllocator::IsBlockBoundary(LifetimePosition pos) {
+ return pos.IsInstructionStart() &&
+ InstructionAt(pos.InstructionIndex())->IsLabel();
+}
+
+
+LiveRange* LAllocator::SplitRangeAt(LiveRange* range, LifetimePosition pos) {
+ ASSERT(!range->IsFixed());
+ TraceAlloc("Splitting live range %d at %d\n", range->id(), pos.Value());
+
+ if (pos.Value() <= range->Start().Value()) return range;
+
+ // We can't properly connect liveranges if split occured at the end
+ // of control instruction.
+ ASSERT(pos.IsInstructionStart() ||
+ !chunk_->instructions()->at(pos.InstructionIndex())->IsControl());
+
+ int vreg = GetVirtualRegister();
+ if (!AllocationOk()) return NULL;
+ LiveRange* result = LiveRangeFor(vreg);
+ range->SplitAt(pos, result, zone());
+ return result;
+}
+
+
+LiveRange* LAllocator::SplitBetween(LiveRange* range,
+ LifetimePosition start,
+ LifetimePosition end) {
+ ASSERT(!range->IsFixed());
+ TraceAlloc("Splitting live range %d in position between [%d, %d]\n",
+ range->id(),
+ start.Value(),
+ end.Value());
+
+ LifetimePosition split_pos = FindOptimalSplitPos(start, end);
+ ASSERT(split_pos.Value() >= start.Value());
+ return SplitRangeAt(range, split_pos);
+}
+
+
+LifetimePosition LAllocator::FindOptimalSplitPos(LifetimePosition start,
+ LifetimePosition end) {
+ int start_instr = start.InstructionIndex();
+ int end_instr = end.InstructionIndex();
+ ASSERT(start_instr <= end_instr);
+
+ // We have no choice
+ if (start_instr == end_instr) return end;
+
+ HBasicBlock* start_block = GetBlock(start);
+ HBasicBlock* end_block = GetBlock(end);
+
+ if (end_block == start_block) {
+ // The interval is split in the same basic block. Split at the latest
+ // possible position.
+ return end;
+ }
+
+ HBasicBlock* block = end_block;
+ // Find header of outermost loop.
+ while (block->parent_loop_header() != NULL &&
+ block->parent_loop_header()->block_id() > start_block->block_id()) {
+ block = block->parent_loop_header();
+ }
+
+ // We did not find any suitable outer loop. Split at the latest possible
+ // position unless end_block is a loop header itself.
+ if (block == end_block && !end_block->IsLoopHeader()) return end;
+
+ return LifetimePosition::FromInstructionIndex(
+ block->first_instruction_index());
+}
+
+
+void LAllocator::SpillAfter(LiveRange* range, LifetimePosition pos) {
+ LiveRange* second_part = SplitRangeAt(range, pos);
+ if (!AllocationOk()) return;
+ Spill(second_part);
+}
+
+
+void LAllocator::SpillBetween(LiveRange* range,
+ LifetimePosition start,
+ LifetimePosition end) {
+ SpillBetweenUntil(range, start, start, end);
+}
+
+
+void LAllocator::SpillBetweenUntil(LiveRange* range,
+ LifetimePosition start,
+ LifetimePosition until,
+ LifetimePosition end) {
+ CHECK(start.Value() < end.Value());
+ LiveRange* second_part = SplitRangeAt(range, start);
+ if (!AllocationOk()) return;
+
+ if (second_part->Start().Value() < end.Value()) {
+ // The split result intersects with [start, end[.
+ // Split it at position between ]start+1, end[, spill the middle part
+ // and put the rest to unhandled.
+ LiveRange* third_part = SplitBetween(
+ second_part,
+ Max(second_part->Start().InstructionEnd(), until),
+ end.PrevInstruction().InstructionEnd());
+ if (!AllocationOk()) return;
+
+ ASSERT(third_part != second_part);
+
+ Spill(second_part);
+ AddToUnhandledSorted(third_part);
+ } else {
+ // The split result does not intersect with [start, end[.
+ // Nothing to spill. Just put it to unhandled as whole.
+ AddToUnhandledSorted(second_part);
+ }
+}
+
+
+void LAllocator::Spill(LiveRange* range) {
+ ASSERT(!range->IsSpilled());
+ TraceAlloc("Spilling live range %d\n", range->id());
+ LiveRange* first = range->TopLevel();
+
+ if (!first->HasAllocatedSpillOperand()) {
+ LOperand* op = TryReuseSpillSlot(range);
+ if (op == NULL) op = chunk_->GetNextSpillSlot(mode_ == DOUBLE_REGISTERS);
+ first->SetSpillOperand(op);
+ }
+ range->MakeSpilled(chunk()->zone());
+}
+
+
+int LAllocator::RegisterCount() const {
+ return num_registers_;
+}
+
+
+#ifdef DEBUG
+
+
+void LAllocator::Verify() const {
+ for (int i = 0; i < live_ranges()->length(); ++i) {
+ LiveRange* current = live_ranges()->at(i);
+ if (current != NULL) current->Verify();
+ }
+}
+
+
+#endif
+
+
+LAllocatorPhase::LAllocatorPhase(const char* name, LAllocator* allocator)
+ : CompilationPhase(name, allocator->graph()->info()),
+ allocator_(allocator) {
+ if (FLAG_hydrogen_stats) {
+ allocator_zone_start_allocation_size_ =
+ allocator->zone()->allocation_size();
+ }
+}
+
+
+LAllocatorPhase::~LAllocatorPhase() {
+ if (FLAG_hydrogen_stats) {
+ unsigned size = allocator_->zone()->allocation_size() -
+ allocator_zone_start_allocation_size_;
+ isolate()->GetHStatistics()->SaveTiming(name(), 0, size);
+ }
+
+ if (ShouldProduceTraceOutput()) {
+ isolate()->GetHTracer()->TraceLithium(name(), allocator_->chunk());
+ isolate()->GetHTracer()->TraceLiveRanges(name(), allocator_);
+ }
+
+#ifdef DEBUG
+ if (allocator_ != NULL) allocator_->Verify();
+#endif
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/lithium-allocator.h b/chromium/v8/src/lithium-allocator.h
new file mode 100644
index 00000000000..e5edd3cf039
--- /dev/null
+++ b/chromium/v8/src/lithium-allocator.h
@@ -0,0 +1,662 @@
+// Copyright 2012 the V8 project authors. 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_LITHIUM_ALLOCATOR_H_
+#define V8_LITHIUM_ALLOCATOR_H_
+
+#include "v8.h"
+
+#include "allocation.h"
+#include "lithium.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class HBasicBlock;
+class HGraph;
+class HInstruction;
+class HPhi;
+class HTracer;
+class HValue;
+class BitVector;
+class StringStream;
+
+class LArgument;
+class LPlatformChunk;
+class LOperand;
+class LUnallocated;
+class LConstantOperand;
+class LGap;
+class LParallelMove;
+class LPointerMap;
+class LStackSlot;
+class LRegister;
+
+
+// This class represents a single point of a LOperand's lifetime.
+// For each lithium instruction there are exactly two lifetime positions:
+// the beginning and the end of the instruction. Lifetime positions for
+// different lithium instructions are disjoint.
+class LifetimePosition {
+ public:
+ // Return the lifetime position that corresponds to the beginning of
+ // the instruction with the given index.
+ static LifetimePosition FromInstructionIndex(int index) {
+ return LifetimePosition(index * kStep);
+ }
+
+ // Returns a numeric representation of this lifetime position.
+ int Value() const {
+ return value_;
+ }
+
+ // Returns the index of the instruction to which this lifetime position
+ // corresponds.
+ int InstructionIndex() const {
+ ASSERT(IsValid());
+ return value_ / kStep;
+ }
+
+ // Returns true if this lifetime position corresponds to the instruction
+ // start.
+ bool IsInstructionStart() const {
+ return (value_ & (kStep - 1)) == 0;
+ }
+
+ // Returns the lifetime position for the start of the instruction which
+ // corresponds to this lifetime position.
+ LifetimePosition InstructionStart() const {
+ ASSERT(IsValid());
+ return LifetimePosition(value_ & ~(kStep - 1));
+ }
+
+ // Returns the lifetime position for the end of the instruction which
+ // corresponds to this lifetime position.
+ LifetimePosition InstructionEnd() const {
+ ASSERT(IsValid());
+ return LifetimePosition(InstructionStart().Value() + kStep/2);
+ }
+
+ // Returns the lifetime position for the beginning of the next instruction.
+ LifetimePosition NextInstruction() const {
+ ASSERT(IsValid());
+ return LifetimePosition(InstructionStart().Value() + kStep);
+ }
+
+ // Returns the lifetime position for the beginning of the previous
+ // instruction.
+ LifetimePosition PrevInstruction() const {
+ ASSERT(IsValid());
+ ASSERT(value_ > 1);
+ return LifetimePosition(InstructionStart().Value() - kStep);
+ }
+
+ // Constructs the lifetime position which does not correspond to any
+ // instruction.
+ LifetimePosition() : value_(-1) {}
+
+ // Returns true if this lifetime positions corrensponds to some
+ // instruction.
+ bool IsValid() const { return value_ != -1; }
+
+ static inline LifetimePosition Invalid() { return LifetimePosition(); }
+
+ static inline LifetimePosition MaxPosition() {
+ // We have to use this kind of getter instead of static member due to
+ // crash bug in GDB.
+ return LifetimePosition(kMaxInt);
+ }
+
+ private:
+ static const int kStep = 2;
+
+ // Code relies on kStep being a power of two.
+ STATIC_ASSERT(IS_POWER_OF_TWO(kStep));
+
+ explicit LifetimePosition(int value) : value_(value) { }
+
+ int value_;
+};
+
+
+enum RegisterKind {
+ GENERAL_REGISTERS,
+ DOUBLE_REGISTERS
+};
+
+
+// A register-allocator view of a Lithium instruction. It contains the id of
+// the output operand and a list of input operand uses.
+
+class LInstruction;
+class LEnvironment;
+
+// Iterator for non-null temp operands.
+class TempIterator BASE_EMBEDDED {
+ public:
+ inline explicit TempIterator(LInstruction* instr);
+ inline bool Done();
+ inline LOperand* Current();
+ inline void Advance();
+
+ private:
+ inline void SkipUninteresting();
+ LInstruction* instr_;
+ int limit_;
+ int current_;
+};
+
+
+// Iterator for non-constant input operands.
+class InputIterator BASE_EMBEDDED {
+ public:
+ inline explicit InputIterator(LInstruction* instr);
+ inline bool Done();
+ inline LOperand* Current();
+ inline void Advance();
+
+ private:
+ inline void SkipUninteresting();
+ LInstruction* instr_;
+ int limit_;
+ int current_;
+};
+
+
+class UseIterator BASE_EMBEDDED {
+ public:
+ inline explicit UseIterator(LInstruction* instr);
+ inline bool Done();
+ inline LOperand* Current();
+ inline void Advance();
+
+ private:
+ InputIterator input_iterator_;
+ DeepIterator env_iterator_;
+};
+
+
+// Representation of the non-empty interval [start,end[.
+class UseInterval: public ZoneObject {
+ public:
+ UseInterval(LifetimePosition start, LifetimePosition end)
+ : start_(start), end_(end), next_(NULL) {
+ ASSERT(start.Value() < end.Value());
+ }
+
+ LifetimePosition start() const { return start_; }
+ LifetimePosition end() const { return end_; }
+ UseInterval* next() const { return next_; }
+
+ // Split this interval at the given position without effecting the
+ // live range that owns it. The interval must contain the position.
+ void SplitAt(LifetimePosition pos, Zone* zone);
+
+ // If this interval intersects with other return smallest position
+ // that belongs to both of them.
+ LifetimePosition Intersect(const UseInterval* other) const {
+ if (other->start().Value() < start_.Value()) return other->Intersect(this);
+ if (other->start().Value() < end_.Value()) return other->start();
+ return LifetimePosition::Invalid();
+ }
+
+ bool Contains(LifetimePosition point) const {
+ return start_.Value() <= point.Value() && point.Value() < end_.Value();
+ }
+
+ private:
+ void set_start(LifetimePosition start) { start_ = start; }
+ void set_next(UseInterval* next) { next_ = next; }
+
+ LifetimePosition start_;
+ LifetimePosition end_;
+ UseInterval* next_;
+
+ friend class LiveRange; // Assigns to start_.
+};
+
+// Representation of a use position.
+class UsePosition: public ZoneObject {
+ public:
+ UsePosition(LifetimePosition pos, LOperand* operand, LOperand* hint);
+
+ LOperand* operand() const { return operand_; }
+ bool HasOperand() const { return operand_ != NULL; }
+
+ LOperand* hint() const { return hint_; }
+ bool HasHint() const;
+ bool RequiresRegister() const;
+ bool RegisterIsBeneficial() const;
+
+ LifetimePosition pos() const { return pos_; }
+ UsePosition* next() const { return next_; }
+
+ private:
+ void set_next(UsePosition* next) { next_ = next; }
+
+ LOperand* const operand_;
+ LOperand* const hint_;
+ LifetimePosition const pos_;
+ UsePosition* next_;
+ bool requires_reg_;
+ bool register_beneficial_;
+
+ friend class LiveRange;
+};
+
+// Representation of SSA values' live ranges as a collection of (continuous)
+// intervals over the instruction ordering.
+class LiveRange: public ZoneObject {
+ public:
+ static const int kInvalidAssignment = 0x7fffffff;
+
+ LiveRange(int id, Zone* zone);
+
+ UseInterval* first_interval() const { return first_interval_; }
+ UsePosition* first_pos() const { return first_pos_; }
+ LiveRange* parent() const { return parent_; }
+ LiveRange* TopLevel() { return (parent_ == NULL) ? this : parent_; }
+ LiveRange* next() const { return next_; }
+ bool IsChild() const { return parent() != NULL; }
+ int id() const { return id_; }
+ bool IsFixed() const { return id_ < 0; }
+ bool IsEmpty() const { return first_interval() == NULL; }
+ LOperand* CreateAssignedOperand(Zone* zone);
+ int assigned_register() const { return assigned_register_; }
+ int spill_start_index() const { return spill_start_index_; }
+ void set_assigned_register(int reg,
+ RegisterKind register_kind,
+ Zone* zone);
+ void MakeSpilled(Zone* zone);
+
+ // Returns use position in this live range that follows both start
+ // and last processed use position.
+ // Modifies internal state of live range!
+ UsePosition* NextUsePosition(LifetimePosition start);
+
+ // Returns use position for which register is required in this live
+ // range and which follows both start and last processed use position
+ // Modifies internal state of live range!
+ UsePosition* NextRegisterPosition(LifetimePosition start);
+
+ // Returns use position for which register is beneficial in this live
+ // range and which follows both start and last processed use position
+ // Modifies internal state of live range!
+ UsePosition* NextUsePositionRegisterIsBeneficial(LifetimePosition start);
+
+ // Returns use position for which register is beneficial in this live
+ // range and which precedes start.
+ UsePosition* PreviousUsePositionRegisterIsBeneficial(LifetimePosition start);
+
+ // Can this live range be spilled at this position.
+ bool CanBeSpilled(LifetimePosition pos);
+
+ // Split this live range at the given position which must follow the start of
+ // the range.
+ // All uses following the given position will be moved from this
+ // live range to the result live range.
+ void SplitAt(LifetimePosition position, LiveRange* result, Zone* zone);
+
+ bool IsDouble() const { return is_double_; }
+ bool HasRegisterAssigned() const {
+ return assigned_register_ != kInvalidAssignment;
+ }
+ bool IsSpilled() const { return spilled_; }
+
+ LOperand* current_hint_operand() const {
+ ASSERT(current_hint_operand_ == FirstHint());
+ return current_hint_operand_;
+ }
+ LOperand* FirstHint() const {
+ UsePosition* pos = first_pos_;
+ while (pos != NULL && !pos->HasHint()) pos = pos->next();
+ if (pos != NULL) return pos->hint();
+ return NULL;
+ }
+
+ LifetimePosition Start() const {
+ ASSERT(!IsEmpty());
+ return first_interval()->start();
+ }
+
+ LifetimePosition End() const {
+ ASSERT(!IsEmpty());
+ return last_interval_->end();
+ }
+
+ bool HasAllocatedSpillOperand() const;
+ LOperand* GetSpillOperand() const { return spill_operand_; }
+ void SetSpillOperand(LOperand* operand);
+
+ void SetSpillStartIndex(int start) {
+ spill_start_index_ = Min(start, spill_start_index_);
+ }
+
+ bool ShouldBeAllocatedBefore(const LiveRange* other) const;
+ bool CanCover(LifetimePosition position) const;
+ bool Covers(LifetimePosition position);
+ LifetimePosition FirstIntersection(LiveRange* other);
+
+ // Add a new interval or a new use position to this live range.
+ void EnsureInterval(LifetimePosition start,
+ LifetimePosition end,
+ Zone* zone);
+ void AddUseInterval(LifetimePosition start,
+ LifetimePosition end,
+ Zone* zone);
+ void AddUsePosition(LifetimePosition pos,
+ LOperand* operand,
+ LOperand* hint,
+ Zone* zone);
+
+ // Shorten the most recently added interval by setting a new start.
+ void ShortenTo(LifetimePosition start);
+
+#ifdef DEBUG
+ // True if target overlaps an existing interval.
+ bool HasOverlap(UseInterval* target) const;
+ void Verify() const;
+#endif
+
+ private:
+ void ConvertOperands(Zone* zone);
+ UseInterval* FirstSearchIntervalForPosition(LifetimePosition position) const;
+ void AdvanceLastProcessedMarker(UseInterval* to_start_of,
+ LifetimePosition but_not_past) const;
+
+ int id_;
+ bool spilled_;
+ bool is_double_;
+ int assigned_register_;
+ UseInterval* last_interval_;
+ UseInterval* first_interval_;
+ UsePosition* first_pos_;
+ LiveRange* parent_;
+ LiveRange* next_;
+ // This is used as a cache, it doesn't affect correctness.
+ mutable UseInterval* current_interval_;
+ UsePosition* last_processed_use_;
+ // This is used as a cache, it's invalid outside of BuildLiveRanges.
+ LOperand* current_hint_operand_;
+ LOperand* spill_operand_;
+ int spill_start_index_;
+};
+
+
+class LAllocator BASE_EMBEDDED {
+ public:
+ LAllocator(int first_virtual_register, HGraph* graph);
+
+ static void TraceAlloc(const char* msg, ...);
+
+ // 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;
+
+ bool Allocate(LChunk* chunk);
+
+ const ZoneList<LiveRange*>* live_ranges() const { return &live_ranges_; }
+ const Vector<LiveRange*>* fixed_live_ranges() const {
+ return &fixed_live_ranges_;
+ }
+ const Vector<LiveRange*>* fixed_double_live_ranges() const {
+ return &fixed_double_live_ranges_;
+ }
+
+ LPlatformChunk* chunk() const { return chunk_; }
+ HGraph* graph() const { return graph_; }
+ Isolate* isolate() const { return graph_->isolate(); }
+ Zone* zone() { return &zone_; }
+
+ int GetVirtualRegister() {
+ if (next_virtual_register_ >= LUnallocated::kMaxVirtualRegisters) {
+ allocation_ok_ = false;
+ // Maintain the invariant that we return something below the maximum.
+ return 0;
+ }
+ return next_virtual_register_++;
+ }
+
+ bool AllocationOk() { return allocation_ok_; }
+
+ void MarkAsOsrEntry() {
+ // There can be only one.
+ ASSERT(!has_osr_entry_);
+ // Simply set a flag to find and process instruction later.
+ has_osr_entry_ = true;
+ }
+
+#ifdef DEBUG
+ void Verify() const;
+#endif
+
+ BitVector* assigned_registers() {
+ return assigned_registers_;
+ }
+ BitVector* assigned_double_registers() {
+ return assigned_double_registers_;
+ }
+
+ private:
+ void MeetRegisterConstraints();
+ void ResolvePhis();
+ void BuildLiveRanges();
+ void AllocateGeneralRegisters();
+ void AllocateDoubleRegisters();
+ void ConnectRanges();
+ void ResolveControlFlow();
+ void PopulatePointerMaps();
+ void AllocateRegisters();
+ bool CanEagerlyResolveControlFlow(HBasicBlock* block) const;
+ inline bool SafePointsAreInOrder() const;
+
+ // Liveness analysis support.
+ void InitializeLivenessAnalysis();
+ BitVector* ComputeLiveOut(HBasicBlock* block);
+ void AddInitialIntervals(HBasicBlock* block, BitVector* live_out);
+ void ProcessInstructions(HBasicBlock* block, BitVector* live);
+ void MeetRegisterConstraints(HBasicBlock* block);
+ void MeetConstraintsBetween(LInstruction* first,
+ LInstruction* second,
+ int gap_index);
+ void ResolvePhis(HBasicBlock* block);
+
+ // Helper methods for building intervals.
+ LOperand* AllocateFixed(LUnallocated* operand, int pos, bool is_tagged);
+ LiveRange* LiveRangeFor(LOperand* operand);
+ void Define(LifetimePosition position, LOperand* operand, LOperand* hint);
+ void Use(LifetimePosition block_start,
+ LifetimePosition position,
+ LOperand* operand,
+ LOperand* hint);
+ void AddConstraintsGapMove(int index, LOperand* from, LOperand* to);
+
+ // Helper methods for updating the life range lists.
+ void AddToActive(LiveRange* range);
+ void AddToInactive(LiveRange* range);
+ void AddToUnhandledSorted(LiveRange* range);
+ void AddToUnhandledUnsorted(LiveRange* range);
+ void SortUnhandled();
+ bool UnhandledIsSorted();
+ void ActiveToHandled(LiveRange* range);
+ void ActiveToInactive(LiveRange* range);
+ void InactiveToHandled(LiveRange* range);
+ void InactiveToActive(LiveRange* range);
+ void FreeSpillSlot(LiveRange* range);
+ LOperand* TryReuseSpillSlot(LiveRange* range);
+
+ // Helper methods for allocating registers.
+ bool TryAllocateFreeReg(LiveRange* range);
+ void AllocateBlockedReg(LiveRange* range);
+
+ // Live range splitting helpers.
+
+ // Split the given range at the given position.
+ // If range starts at or after the given position then the
+ // original range is returned.
+ // 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* SplitRangeAt(LiveRange* range, LifetimePosition pos);
+
+ // Split the given range in a position from the interval [start, end].
+ LiveRange* SplitBetween(LiveRange* range,
+ LifetimePosition start,
+ LifetimePosition end);
+
+ // Find a lifetime position in the interval [start, end] which
+ // is optimal for splitting: it is either header of the outermost
+ // loop covered by this interval or the latest possible position.
+ LifetimePosition FindOptimalSplitPos(LifetimePosition start,
+ LifetimePosition end);
+
+ // Spill the given life range after position pos.
+ void SpillAfter(LiveRange* range, LifetimePosition pos);
+
+ // Spill the given life range after position [start] and up to position [end].
+ void SpillBetween(LiveRange* range,
+ LifetimePosition start,
+ LifetimePosition end);
+
+ // Spill the given life range after position [start] and up to position [end].
+ // Range is guaranteed to be spilled at least until position [until].
+ void SpillBetweenUntil(LiveRange* range,
+ LifetimePosition start,
+ LifetimePosition until,
+ LifetimePosition end);
+
+ void SplitAndSpillIntersecting(LiveRange* range);
+
+ // If we are trying to spill a range inside the loop try to
+ // hoist spill position out to the point just before the loop.
+ LifetimePosition FindOptimalSpillingPos(LiveRange* range,
+ LifetimePosition pos);
+
+ void Spill(LiveRange* range);
+ bool IsBlockBoundary(LifetimePosition pos);
+
+ // Helper methods for resolving control flow.
+ void ResolveControlFlow(LiveRange* range,
+ HBasicBlock* block,
+ HBasicBlock* pred);
+
+ inline void SetLiveRangeAssignedRegister(LiveRange* range,
+ int reg,
+ RegisterKind register_kind);
+
+ // Return parallel move that should be used to connect ranges split at the
+ // given position.
+ LParallelMove* GetConnectingParallelMove(LifetimePosition pos);
+
+ // Return the block which contains give lifetime position.
+ HBasicBlock* GetBlock(LifetimePosition pos);
+
+ // Helper methods for the fixed registers.
+ int RegisterCount() const;
+ static int FixedLiveRangeID(int index) { return -index - 1; }
+ static int FixedDoubleLiveRangeID(int index);
+ LiveRange* FixedLiveRangeFor(int index);
+ LiveRange* FixedDoubleLiveRangeFor(int index);
+ LiveRange* LiveRangeFor(int index);
+ HPhi* LookupPhi(LOperand* operand) const;
+ LGap* GetLastGap(HBasicBlock* block);
+
+ const char* RegisterName(int allocation_index);
+
+ inline bool IsGapAt(int index);
+
+ inline LInstruction* InstructionAt(int index);
+
+ inline LGap* GapAt(int index);
+
+ Zone zone_;
+
+ LPlatformChunk* chunk_;
+
+ // During liveness analysis keep a mapping from block id to live_in sets
+ // for blocks already analyzed.
+ ZoneList<BitVector*> live_in_sets_;
+
+ // Liveness analysis results.
+ ZoneList<LiveRange*> live_ranges_;
+
+ // Lists of live ranges
+ EmbeddedVector<LiveRange*, Register::kMaxNumAllocatableRegisters>
+ fixed_live_ranges_;
+ EmbeddedVector<LiveRange*, DoubleRegister::kMaxNumAllocatableRegisters>
+ fixed_double_live_ranges_;
+ ZoneList<LiveRange*> unhandled_live_ranges_;
+ ZoneList<LiveRange*> active_live_ranges_;
+ ZoneList<LiveRange*> inactive_live_ranges_;
+ ZoneList<LiveRange*> reusable_slots_;
+
+ // Next virtual register number to be assigned to temporaries.
+ int next_virtual_register_;
+ int first_artificial_register_;
+ GrowableBitVector double_artificial_registers_;
+
+ RegisterKind mode_;
+ int num_registers_;
+
+ BitVector* assigned_registers_;
+ BitVector* assigned_double_registers_;
+
+ HGraph* graph_;
+
+ bool has_osr_entry_;
+
+ // Indicates success or failure during register allocation.
+ bool allocation_ok_;
+
+#ifdef DEBUG
+ LifetimePosition allocation_finger_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(LAllocator);
+};
+
+
+class LAllocatorPhase : public CompilationPhase {
+ public:
+ LAllocatorPhase(const char* name, LAllocator* allocator);
+ ~LAllocatorPhase();
+
+ private:
+ LAllocator* allocator_;
+ unsigned allocator_zone_start_allocation_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(LAllocatorPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_LITHIUM_ALLOCATOR_H_
diff --git a/chromium/v8/src/lithium.cc b/chromium/v8/src/lithium.cc
new file mode 100644
index 00000000000..790a2182b17
--- /dev/null
+++ b/chromium/v8/src/lithium.cc
@@ -0,0 +1,502 @@
+// Copyright 2012 the V8 project authors. 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.h"
+#include "scopes.h"
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/lithium-ia32.h"
+#include "ia32/lithium-codegen-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/lithium-x64.h"
+#include "x64/lithium-codegen-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/lithium-arm.h"
+#include "arm/lithium-codegen-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/lithium-mips.h"
+#include "mips/lithium-codegen-mips.h"
+#else
+#error "Unknown architecture."
+#endif
+
+namespace v8 {
+namespace internal {
+
+
+void LOperand::PrintTo(StringStream* stream) {
+ LUnallocated* unalloc = NULL;
+ switch (kind()) {
+ case INVALID:
+ stream->Add("(0)");
+ break;
+ case UNALLOCATED:
+ unalloc = LUnallocated::cast(this);
+ stream->Add("v%d", unalloc->virtual_register());
+ if (unalloc->basic_policy() == LUnallocated::FIXED_SLOT) {
+ stream->Add("(=%dS)", unalloc->fixed_slot_index());
+ break;
+ }
+ switch (unalloc->extended_policy()) {
+ case LUnallocated::NONE:
+ break;
+ case LUnallocated::FIXED_REGISTER: {
+ int reg_index = unalloc->fixed_register_index();
+ const char* register_name =
+ Register::AllocationIndexToString(reg_index);
+ stream->Add("(=%s)", register_name);
+ break;
+ }
+ case LUnallocated::FIXED_DOUBLE_REGISTER: {
+ int reg_index = unalloc->fixed_register_index();
+ const char* double_register_name =
+ DoubleRegister::AllocationIndexToString(reg_index);
+ stream->Add("(=%s)", double_register_name);
+ break;
+ }
+ case LUnallocated::MUST_HAVE_REGISTER:
+ stream->Add("(R)");
+ break;
+ case LUnallocated::WRITABLE_REGISTER:
+ stream->Add("(WR)");
+ break;
+ case LUnallocated::SAME_AS_FIRST_INPUT:
+ stream->Add("(1)");
+ break;
+ case LUnallocated::ANY:
+ stream->Add("(-)");
+ break;
+ }
+ break;
+ case CONSTANT_OPERAND:
+ stream->Add("[constant:%d]", index());
+ break;
+ case STACK_SLOT:
+ stream->Add("[stack:%d]", index());
+ break;
+ case DOUBLE_STACK_SLOT:
+ stream->Add("[double_stack:%d]", index());
+ break;
+ case REGISTER:
+ stream->Add("[%s|R]", Register::AllocationIndexToString(index()));
+ break;
+ case DOUBLE_REGISTER:
+ stream->Add("[%s|R]", DoubleRegister::AllocationIndexToString(index()));
+ break;
+ case ARGUMENT:
+ stream->Add("[arg:%d]", index());
+ break;
+ }
+}
+
+#define DEFINE_OPERAND_CACHE(name, type) \
+ L##name* L##name::cache = NULL; \
+ \
+ void L##name::SetUpCache() { \
+ if (cache) return; \
+ cache = new L##name[kNumCachedOperands]; \
+ for (int i = 0; i < kNumCachedOperands; i++) { \
+ cache[i].ConvertTo(type, i); \
+ } \
+ } \
+ \
+ void L##name::TearDownCache() { \
+ delete[] cache; \
+ }
+
+LITHIUM_OPERAND_LIST(DEFINE_OPERAND_CACHE)
+#undef DEFINE_OPERAND_CACHE
+
+void LOperand::SetUpCaches() {
+#define LITHIUM_OPERAND_SETUP(name, type) L##name::SetUpCache();
+ LITHIUM_OPERAND_LIST(LITHIUM_OPERAND_SETUP)
+#undef LITHIUM_OPERAND_SETUP
+}
+
+
+void LOperand::TearDownCaches() {
+#define LITHIUM_OPERAND_TEARDOWN(name, type) L##name::TearDownCache();
+ LITHIUM_OPERAND_LIST(LITHIUM_OPERAND_TEARDOWN)
+#undef LITHIUM_OPERAND_TEARDOWN
+}
+
+
+bool LParallelMove::IsRedundant() const {
+ for (int i = 0; i < move_operands_.length(); ++i) {
+ if (!move_operands_[i].IsRedundant()) return false;
+ }
+ return true;
+}
+
+
+void LParallelMove::PrintDataTo(StringStream* stream) const {
+ bool first = true;
+ for (int i = 0; i < move_operands_.length(); ++i) {
+ if (!move_operands_[i].IsEliminated()) {
+ LOperand* source = move_operands_[i].source();
+ LOperand* destination = move_operands_[i].destination();
+ if (!first) stream->Add(" ");
+ first = false;
+ if (source->Equals(destination)) {
+ destination->PrintTo(stream);
+ } else {
+ destination->PrintTo(stream);
+ stream->Add(" = ");
+ source->PrintTo(stream);
+ }
+ stream->Add(";");
+ }
+ }
+}
+
+
+void LEnvironment::PrintTo(StringStream* stream) {
+ stream->Add("[id=%d|", ast_id().ToInt());
+ if (deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
+ stream->Add("deopt_id=%d|", deoptimization_index());
+ }
+ stream->Add("parameters=%d|", parameter_count());
+ stream->Add("arguments_stack_height=%d|", arguments_stack_height());
+ for (int i = 0; i < values_.length(); ++i) {
+ if (i != 0) stream->Add(";");
+ if (values_[i] == NULL) {
+ stream->Add("[hole]");
+ } else {
+ values_[i]->PrintTo(stream);
+ }
+ }
+ stream->Add("]");
+}
+
+
+void LPointerMap::RecordPointer(LOperand* op, Zone* zone) {
+ // Do not record arguments as pointers.
+ if (op->IsStackSlot() && op->index() < 0) return;
+ ASSERT(!op->IsDoubleRegister() && !op->IsDoubleStackSlot());
+ pointer_operands_.Add(op, zone);
+}
+
+
+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, Zone* zone) {
+ // Do not record arguments as pointers.
+ if (op->IsStackSlot() && op->index() < 0) return;
+ ASSERT(!op->IsDoubleRegister() && !op->IsDoubleStackSlot());
+ untagged_operands_.Add(op, zone);
+}
+
+
+void LPointerMap::PrintTo(StringStream* stream) {
+ stream->Add("{");
+ for (int i = 0; i < pointer_operands_.length(); ++i) {
+ if (i != 0) stream->Add(";");
+ pointer_operands_[i]->PrintTo(stream);
+ }
+ stream->Add("} @%d", position());
+}
+
+
+int ElementsKindToShiftSize(ElementsKind elements_kind) {
+ switch (elements_kind) {
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ return 0;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ return 1;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ case EXTERNAL_FLOAT_ELEMENTS:
+ return 2;
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ return 3;
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ return kPointerSizeLog2;
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+
+int StackSlotOffset(int index) {
+ if (index >= 0) {
+ // Local or spill slot. Skip the frame pointer, function, and
+ // context in the fixed part of the frame.
+ return -(index + 3) * kPointerSize;
+ } else {
+ // Incoming parameter. Skip the return address.
+ return -(index + 1) * kPointerSize + kFPOnStackSize + kPCOnStackSize;
+ }
+}
+
+
+LChunk::LChunk(CompilationInfo* info, HGraph* graph)
+ : spill_slot_count_(0),
+ info_(info),
+ graph_(graph),
+ instructions_(32, graph->zone()),
+ pointer_maps_(8, graph->zone()),
+ inlined_closures_(1, graph->zone()) {
+}
+
+
+LLabel* LChunk::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 LChunk::LookupDestination(int block_id) const {
+ LLabel* cur = GetLabel(block_id);
+ while (cur->replacement() != NULL) {
+ cur = cur->replacement();
+ }
+ return cur->block_id();
+}
+
+Label* LChunk::GetAssemblyLabel(int block_id) const {
+ LLabel* label = GetLabel(block_id);
+ ASSERT(!label->HasReplacement());
+ return label->label();
+}
+
+
+void LChunk::MarkEmptyBlocks() {
+ LPhase phase("L_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(graph_->zone()) LInstructionGap(block);
+ gap->set_hydrogen_value(instr->hydrogen_value());
+ int index = -1;
+ if (instr->IsControl()) {
+ instructions_.Add(gap, zone());
+ index = instructions_.length();
+ instructions_.Add(instr, zone());
+ } else {
+ index = instructions_.length();
+ instructions_.Add(instr, zone());
+ instructions_.Add(gap, zone());
+ }
+ if (instr->HasPointerMap()) {
+ pointer_maps_.Add(instr->pointer_map(), zone());
+ instr->pointer_map()->set_lithium_position(index);
+ }
+}
+
+
+LConstantOperand* LChunk::DefineConstantOperand(HConstant* constant) {
+ return LConstantOperand::Create(constant->id(), zone());
+}
+
+
+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, zone())->AddMove(from, to, zone());
+}
+
+
+HConstant* LChunk::LookupConstant(LConstantOperand* operand) const {
+ return HConstant::cast(graph_->LookupValue(operand->index()));
+}
+
+
+Representation LChunk::LookupLiteralRepresentation(
+ LConstantOperand* operand) const {
+ return graph_->LookupValue(operand->index())->representation();
+}
+
+
+LChunk* LChunk::NewChunk(HGraph* graph) {
+ DisallowHandleAllocation no_handles;
+ DisallowHeapAllocation no_gc;
+ int values = graph->GetMaximumValueID();
+ CompilationInfo* info = graph->info();
+ if (values > LUnallocated::kMaxVirtualRegisters) {
+ info->set_bailout_reason(kNotEnoughVirtualRegistersForValues);
+ return NULL;
+ }
+ LAllocator allocator(values, graph);
+ LChunkBuilder builder(info, graph, &allocator);
+ LChunk* chunk = builder.Build();
+ if (chunk == NULL) return NULL;
+
+ if (!allocator.Allocate(chunk)) {
+ info->set_bailout_reason(kNotEnoughVirtualRegistersRegalloc);
+ return NULL;
+ }
+
+ chunk->set_allocated_double_registers(
+ allocator.assigned_double_registers());
+
+ return chunk;
+}
+
+
+Handle<Code> LChunk::Codegen() {
+ MacroAssembler assembler(info()->isolate(), NULL, 0);
+ LOG_CODE_EVENT(info()->isolate(),
+ CodeStartLinePosInfoRecordEvent(
+ assembler.positions_recorder()));
+ LCodeGen generator(this, &assembler, info());
+
+ MarkEmptyBlocks();
+
+ if (generator.GenerateCode()) {
+ CodeGenerator::MakeCodePrologue(info(), "optimized");
+ Code::Flags flags = info()->flags();
+ Handle<Code> code =
+ CodeGenerator::MakeCodeEpilogue(&assembler, flags, info());
+ generator.FinishCode(code);
+ code->set_is_crankshafted(true);
+ if (!code.is_null()) {
+ void* jit_handler_data =
+ assembler.positions_recorder()->DetachJITHandlerData();
+ LOG_CODE_EVENT(info()->isolate(),
+ CodeEndLinePosInfoRecordEvent(*code, jit_handler_data));
+ }
+
+ CodeGenerator::PrintCode(code, info());
+ return code;
+ }
+ return Handle<Code>::null();
+}
+
+
+void LChunk::set_allocated_double_registers(BitVector* allocated_registers) {
+ allocated_double_registers_ = allocated_registers;
+ BitVector* doubles = allocated_double_registers();
+ BitVector::Iterator iterator(doubles);
+ while (!iterator.Done()) {
+ if (info()->saves_caller_doubles()) {
+ if (kDoubleSize == kPointerSize * 2) {
+ spill_slot_count_ += 2;
+ } else {
+ spill_slot_count_++;
+ }
+ }
+ iterator.Advance();
+ }
+}
+
+
+LPhase::~LPhase() {
+ if (ShouldProduceTraceOutput()) {
+ isolate()->GetHTracer()->TraceLithium(name(), chunk_);
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/lithium.h b/chromium/v8/src/lithium.h
new file mode 100644
index 00000000000..f773916485d
--- /dev/null
+++ b/chromium/v8/src/lithium.h
@@ -0,0 +1,822 @@
+// Copyright 2012 the V8 project authors. 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_LITHIUM_H_
+#define V8_LITHIUM_H_
+
+#include "allocation.h"
+#include "hydrogen.h"
+#include "safepoint-table.h"
+
+namespace v8 {
+namespace internal {
+
+#define LITHIUM_OPERAND_LIST(V) \
+ V(ConstantOperand, CONSTANT_OPERAND) \
+ V(StackSlot, STACK_SLOT) \
+ V(DoubleStackSlot, DOUBLE_STACK_SLOT) \
+ V(Register, REGISTER) \
+ V(DoubleRegister, DOUBLE_REGISTER)
+
+
+class LOperand: public ZoneObject {
+ public:
+ enum Kind {
+ INVALID,
+ UNALLOCATED,
+ CONSTANT_OPERAND,
+ STACK_SLOT,
+ DOUBLE_STACK_SLOT,
+ REGISTER,
+ DOUBLE_REGISTER,
+ ARGUMENT
+ };
+
+ LOperand() : value_(KindField::encode(INVALID)) { }
+
+ Kind kind() const { return KindField::decode(value_); }
+ int index() const { return static_cast<int>(value_) >> kKindFieldWidth; }
+#define LITHIUM_OPERAND_PREDICATE(name, type) \
+ bool Is##name() const { return kind() == type; }
+ LITHIUM_OPERAND_LIST(LITHIUM_OPERAND_PREDICATE)
+ LITHIUM_OPERAND_PREDICATE(Argument, ARGUMENT)
+ LITHIUM_OPERAND_PREDICATE(Unallocated, UNALLOCATED)
+ LITHIUM_OPERAND_PREDICATE(Ignored, INVALID)
+#undef LITHIUM_OPERAND_PREDICATE
+ bool Equals(LOperand* other) const { return value_ == other->value_; }
+
+ void PrintTo(StringStream* stream);
+ void ConvertTo(Kind kind, int index) {
+ value_ = KindField::encode(kind);
+ value_ |= index << kKindFieldWidth;
+ ASSERT(this->index() == index);
+ }
+
+ // Calls SetUpCache()/TearDownCache() for each subclass.
+ static void SetUpCaches();
+ static void TearDownCaches();
+
+ protected:
+ static const int kKindFieldWidth = 3;
+ class KindField : public BitField<Kind, 0, kKindFieldWidth> { };
+
+ LOperand(Kind kind, int index) { ConvertTo(kind, index); }
+
+ unsigned value_;
+};
+
+
+class LUnallocated: public LOperand {
+ public:
+ enum BasicPolicy {
+ FIXED_SLOT,
+ EXTENDED_POLICY
+ };
+
+ enum ExtendedPolicy {
+ NONE,
+ ANY,
+ FIXED_REGISTER,
+ FIXED_DOUBLE_REGISTER,
+ MUST_HAVE_REGISTER,
+ WRITABLE_REGISTER,
+ SAME_AS_FIRST_INPUT
+ };
+
+ // Lifetime of operand inside the instruction.
+ enum Lifetime {
+ // USED_AT_START operand 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).
+ USED_AT_START,
+
+ // USED_AT_END operand is treated as live until the end of
+ // instruction. This means that register allocator will not reuse it's
+ // register for any other operand inside instruction.
+ USED_AT_END
+ };
+
+ explicit LUnallocated(ExtendedPolicy policy) : LOperand(UNALLOCATED, 0) {
+ value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
+ value_ |= ExtendedPolicyField::encode(policy);
+ value_ |= LifetimeField::encode(USED_AT_END);
+ }
+
+ LUnallocated(BasicPolicy policy, int index) : LOperand(UNALLOCATED, 0) {
+ ASSERT(policy == FIXED_SLOT);
+ value_ |= BasicPolicyField::encode(policy);
+ value_ |= index << FixedSlotIndexField::kShift;
+ ASSERT(this->fixed_slot_index() == index);
+ }
+
+ LUnallocated(ExtendedPolicy policy, int index) : LOperand(UNALLOCATED, 0) {
+ ASSERT(policy == FIXED_REGISTER || policy == FIXED_DOUBLE_REGISTER);
+ value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
+ value_ |= ExtendedPolicyField::encode(policy);
+ value_ |= LifetimeField::encode(USED_AT_END);
+ value_ |= FixedRegisterField::encode(index);
+ }
+
+ LUnallocated(ExtendedPolicy policy, Lifetime lifetime)
+ : LOperand(UNALLOCATED, 0) {
+ value_ |= BasicPolicyField::encode(EXTENDED_POLICY);
+ value_ |= ExtendedPolicyField::encode(policy);
+ value_ |= LifetimeField::encode(lifetime);
+ }
+
+ LUnallocated* CopyUnconstrained(Zone* zone) {
+ LUnallocated* result = new(zone) LUnallocated(ANY);
+ result->set_virtual_register(virtual_register());
+ return result;
+ }
+
+ static LUnallocated* cast(LOperand* op) {
+ ASSERT(op->IsUnallocated());
+ return reinterpret_cast<LUnallocated*>(op);
+ }
+
+ // The encoding used for LUnallocated operands depends on the policy that is
+ // stored within the operand. The FIXED_SLOT policy uses a compact encoding
+ // because it accommodates a larger pay-load.
+ //
+ // For FIXED_SLOT policy:
+ // +------------------------------------------+
+ // | slot_index | vreg | 0 | 001 |
+ // +------------------------------------------+
+ //
+ // For all other (extended) policies:
+ // +------------------------------------------+
+ // | reg_index | L | PPP | vreg | 1 | 001 | L ... Lifetime
+ // +------------------------------------------+ P ... Policy
+ //
+ // The slot index is a signed value which requires us to decode it manually
+ // instead of using the BitField utility class.
+
+ // The superclass has a KindField.
+ STATIC_ASSERT(kKindFieldWidth == 3);
+
+ // BitFields for all unallocated operands.
+ class BasicPolicyField : public BitField<BasicPolicy, 3, 1> {};
+ class VirtualRegisterField : public BitField<unsigned, 4, 18> {};
+
+ // BitFields specific to BasicPolicy::FIXED_SLOT.
+ class FixedSlotIndexField : public BitField<int, 22, 10> {};
+
+ // BitFields specific to BasicPolicy::EXTENDED_POLICY.
+ class ExtendedPolicyField : public BitField<ExtendedPolicy, 22, 3> {};
+ class LifetimeField : public BitField<Lifetime, 25, 1> {};
+ class FixedRegisterField : public BitField<int, 26, 6> {};
+
+ static const int kMaxVirtualRegisters = VirtualRegisterField::kMax + 1;
+ static const int kFixedSlotIndexWidth = FixedSlotIndexField::kSize;
+ static const int kMaxFixedSlotIndex = (1 << (kFixedSlotIndexWidth - 1)) - 1;
+ static const int kMinFixedSlotIndex = -(1 << (kFixedSlotIndexWidth - 1));
+
+ // Predicates for the operand policy.
+ bool HasAnyPolicy() const {
+ return basic_policy() == EXTENDED_POLICY &&
+ extended_policy() == ANY;
+ }
+ bool HasFixedPolicy() const {
+ return basic_policy() == FIXED_SLOT ||
+ extended_policy() == FIXED_REGISTER ||
+ extended_policy() == FIXED_DOUBLE_REGISTER;
+ }
+ bool HasRegisterPolicy() const {
+ return basic_policy() == EXTENDED_POLICY && (
+ extended_policy() == WRITABLE_REGISTER ||
+ extended_policy() == MUST_HAVE_REGISTER);
+ }
+ bool HasSameAsInputPolicy() const {
+ return basic_policy() == EXTENDED_POLICY &&
+ extended_policy() == SAME_AS_FIRST_INPUT;
+ }
+ bool HasFixedSlotPolicy() const {
+ return basic_policy() == FIXED_SLOT;
+ }
+ bool HasFixedRegisterPolicy() const {
+ return basic_policy() == EXTENDED_POLICY &&
+ extended_policy() == FIXED_REGISTER;
+ }
+ bool HasFixedDoubleRegisterPolicy() const {
+ return basic_policy() == EXTENDED_POLICY &&
+ extended_policy() == FIXED_DOUBLE_REGISTER;
+ }
+ bool HasWritableRegisterPolicy() const {
+ return basic_policy() == EXTENDED_POLICY &&
+ extended_policy() == WRITABLE_REGISTER;
+ }
+
+ // [basic_policy]: Distinguish between FIXED_SLOT and all other policies.
+ BasicPolicy basic_policy() const {
+ return BasicPolicyField::decode(value_);
+ }
+
+ // [extended_policy]: Only for non-FIXED_SLOT. The finer-grained policy.
+ ExtendedPolicy extended_policy() const {
+ ASSERT(basic_policy() == EXTENDED_POLICY);
+ return ExtendedPolicyField::decode(value_);
+ }
+
+ // [fixed_slot_index]: Only for FIXED_SLOT.
+ int fixed_slot_index() const {
+ ASSERT(HasFixedSlotPolicy());
+ return static_cast<int>(value_) >> FixedSlotIndexField::kShift;
+ }
+
+ // [fixed_register_index]: Only for FIXED_REGISTER or FIXED_DOUBLE_REGISTER.
+ int fixed_register_index() const {
+ ASSERT(HasFixedRegisterPolicy() || HasFixedDoubleRegisterPolicy());
+ return FixedRegisterField::decode(value_);
+ }
+
+ // [virtual_register]: The virtual register ID for this operand.
+ int virtual_register() const {
+ return VirtualRegisterField::decode(value_);
+ }
+ void set_virtual_register(unsigned id) {
+ value_ = VirtualRegisterField::update(value_, id);
+ }
+
+ // [lifetime]: Only for non-FIXED_SLOT.
+ bool IsUsedAtStart() {
+ ASSERT(basic_policy() == EXTENDED_POLICY);
+ return LifetimeField::decode(value_) == USED_AT_START;
+ }
+};
+
+
+class LMoveOperands BASE_EMBEDDED {
+ public:
+ LMoveOperands(LOperand* source, LOperand* destination)
+ : source_(source), destination_(destination) {
+ }
+
+ LOperand* source() const { return source_; }
+ void set_source(LOperand* operand) { source_ = operand; }
+
+ LOperand* destination() const { return destination_; }
+ void set_destination(LOperand* operand) { destination_ = operand; }
+
+ // The gap resolver marks moves as "in-progress" by clearing the
+ // destination (but not the source).
+ bool IsPending() const {
+ return destination_ == NULL && source_ != NULL;
+ }
+
+ // True if this move a move into the given destination operand.
+ bool Blocks(LOperand* operand) const {
+ return !IsEliminated() && source()->Equals(operand);
+ }
+
+ // A move is redundant if it's been eliminated, if its source and
+ // destination are the same, or if its destination is unneeded.
+ bool IsRedundant() const {
+ return IsEliminated() || source_->Equals(destination_) || IsIgnored();
+ }
+
+ bool IsIgnored() const {
+ return destination_ != NULL && destination_->IsIgnored();
+ }
+
+ // We clear both operands to indicate move that's been eliminated.
+ void Eliminate() { source_ = destination_ = NULL; }
+ bool IsEliminated() const {
+ ASSERT(source_ != NULL || destination_ == NULL);
+ return source_ == NULL;
+ }
+
+ private:
+ LOperand* source_;
+ LOperand* destination_;
+};
+
+
+class LConstantOperand: public LOperand {
+ public:
+ static LConstantOperand* Create(int index, Zone* zone) {
+ ASSERT(index >= 0);
+ if (index < kNumCachedOperands) return &cache[index];
+ return new(zone) LConstantOperand(index);
+ }
+
+ static LConstantOperand* cast(LOperand* op) {
+ ASSERT(op->IsConstantOperand());
+ return reinterpret_cast<LConstantOperand*>(op);
+ }
+
+ static void SetUpCache();
+ static void TearDownCache();
+
+ private:
+ static const int kNumCachedOperands = 128;
+ static LConstantOperand* cache;
+
+ LConstantOperand() : LOperand() { }
+ explicit LConstantOperand(int index) : LOperand(CONSTANT_OPERAND, index) { }
+};
+
+
+class LArgument: public LOperand {
+ public:
+ explicit LArgument(int index) : LOperand(ARGUMENT, index) { }
+
+ static LArgument* cast(LOperand* op) {
+ ASSERT(op->IsArgument());
+ return reinterpret_cast<LArgument*>(op);
+ }
+};
+
+
+class LStackSlot: public LOperand {
+ public:
+ static LStackSlot* Create(int index, Zone* zone) {
+ ASSERT(index >= 0);
+ if (index < kNumCachedOperands) return &cache[index];
+ return new(zone) LStackSlot(index);
+ }
+
+ static LStackSlot* cast(LOperand* op) {
+ ASSERT(op->IsStackSlot());
+ return reinterpret_cast<LStackSlot*>(op);
+ }
+
+ static void SetUpCache();
+ static void TearDownCache();
+
+ private:
+ static const int kNumCachedOperands = 128;
+ static LStackSlot* cache;
+
+ LStackSlot() : LOperand() { }
+ explicit LStackSlot(int index) : LOperand(STACK_SLOT, index) { }
+};
+
+
+class LDoubleStackSlot: public LOperand {
+ public:
+ static LDoubleStackSlot* Create(int index, Zone* zone) {
+ ASSERT(index >= 0);
+ if (index < kNumCachedOperands) return &cache[index];
+ return new(zone) LDoubleStackSlot(index);
+ }
+
+ static LDoubleStackSlot* cast(LOperand* op) {
+ ASSERT(op->IsStackSlot());
+ return reinterpret_cast<LDoubleStackSlot*>(op);
+ }
+
+ static void SetUpCache();
+ static void TearDownCache();
+
+ private:
+ static const int kNumCachedOperands = 128;
+ static LDoubleStackSlot* cache;
+
+ LDoubleStackSlot() : LOperand() { }
+ explicit LDoubleStackSlot(int index) : LOperand(DOUBLE_STACK_SLOT, index) { }
+};
+
+
+class LRegister: public LOperand {
+ public:
+ static LRegister* Create(int index, Zone* zone) {
+ ASSERT(index >= 0);
+ if (index < kNumCachedOperands) return &cache[index];
+ return new(zone) LRegister(index);
+ }
+
+ static LRegister* cast(LOperand* op) {
+ ASSERT(op->IsRegister());
+ return reinterpret_cast<LRegister*>(op);
+ }
+
+ static void SetUpCache();
+ static void TearDownCache();
+
+ private:
+ static const int kNumCachedOperands = 16;
+ static LRegister* cache;
+
+ LRegister() : LOperand() { }
+ explicit LRegister(int index) : LOperand(REGISTER, index) { }
+};
+
+
+class LDoubleRegister: public LOperand {
+ public:
+ static LDoubleRegister* Create(int index, Zone* zone) {
+ ASSERT(index >= 0);
+ if (index < kNumCachedOperands) return &cache[index];
+ return new(zone) LDoubleRegister(index);
+ }
+
+ static LDoubleRegister* cast(LOperand* op) {
+ ASSERT(op->IsDoubleRegister());
+ return reinterpret_cast<LDoubleRegister*>(op);
+ }
+
+ static void SetUpCache();
+ static void TearDownCache();
+
+ private:
+ static const int kNumCachedOperands = 16;
+ static LDoubleRegister* cache;
+
+ LDoubleRegister() : LOperand() { }
+ explicit LDoubleRegister(int index) : LOperand(DOUBLE_REGISTER, index) { }
+};
+
+
+class LParallelMove : public ZoneObject {
+ public:
+ explicit LParallelMove(Zone* zone) : move_operands_(4, zone) { }
+
+ void AddMove(LOperand* from, LOperand* to, Zone* zone) {
+ move_operands_.Add(LMoveOperands(from, to), zone);
+ }
+
+ bool IsRedundant() const;
+
+ const ZoneList<LMoveOperands>* move_operands() const {
+ return &move_operands_;
+ }
+
+ void PrintDataTo(StringStream* stream) const;
+
+ private:
+ ZoneList<LMoveOperands> move_operands_;
+};
+
+
+class LPointerMap: public ZoneObject {
+ public:
+ explicit LPointerMap(int position, Zone* zone)
+ : pointer_operands_(8, zone),
+ untagged_operands_(0, zone),
+ 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_; }
+
+ void set_lithium_position(int pos) {
+ ASSERT(lithium_position_ == -1);
+ lithium_position_ = pos;
+ }
+
+ void RecordPointer(LOperand* op, Zone* zone);
+ void RemovePointer(LOperand* op);
+ void RecordUntagged(LOperand* op, Zone* zone);
+ void PrintTo(StringStream* stream);
+
+ private:
+ ZoneList<LOperand*> pointer_operands_;
+ ZoneList<LOperand*> untagged_operands_;
+ int position_;
+ int lithium_position_;
+};
+
+
+class LEnvironment: public ZoneObject {
+ public:
+ LEnvironment(Handle<JSFunction> closure,
+ FrameType frame_type,
+ BailoutId ast_id,
+ int parameter_count,
+ int argument_count,
+ int value_count,
+ LEnvironment* outer,
+ HEnterInlined* entry,
+ Zone* zone)
+ : closure_(closure),
+ frame_type_(frame_type),
+ arguments_stack_height_(argument_count),
+ deoptimization_index_(Safepoint::kNoDeoptimizationIndex),
+ translation_index_(-1),
+ ast_id_(ast_id),
+ translation_size_(value_count),
+ parameter_count_(parameter_count),
+ pc_offset_(-1),
+ values_(value_count, zone),
+ is_tagged_(value_count, zone),
+ is_uint32_(value_count, zone),
+ object_mapping_(0, zone),
+ outer_(outer),
+ entry_(entry),
+ zone_(zone) { }
+
+ Handle<JSFunction> closure() const { return closure_; }
+ FrameType frame_type() const { return frame_type_; }
+ int arguments_stack_height() const { return arguments_stack_height_; }
+ int deoptimization_index() const { return deoptimization_index_; }
+ int translation_index() const { return translation_index_; }
+ BailoutId ast_id() const { return ast_id_; }
+ int translation_size() const { return translation_size_; }
+ int parameter_count() const { return parameter_count_; }
+ int pc_offset() const { return pc_offset_; }
+ const ZoneList<LOperand*>* values() const { return &values_; }
+ LEnvironment* outer() const { return outer_; }
+ HEnterInlined* entry() { return entry_; }
+ Zone* zone() const { return zone_; }
+
+ void AddValue(LOperand* operand,
+ Representation representation,
+ bool is_uint32) {
+ values_.Add(operand, zone());
+ if (representation.IsSmiOrTagged()) {
+ ASSERT(!is_uint32);
+ is_tagged_.Add(values_.length() - 1, zone());
+ }
+
+ if (is_uint32) {
+ is_uint32_.Add(values_.length() - 1, zone());
+ }
+ }
+
+ bool HasTaggedValueAt(int index) const {
+ return is_tagged_.Contains(index);
+ }
+
+ bool HasUint32ValueAt(int index) const {
+ return is_uint32_.Contains(index);
+ }
+
+ void AddNewObject(int length, bool is_arguments) {
+ uint32_t encoded = LengthOrDupeField::encode(length) |
+ IsArgumentsField::encode(is_arguments) |
+ IsDuplicateField::encode(false);
+ object_mapping_.Add(encoded, zone());
+ }
+
+ void AddDuplicateObject(int dupe_of) {
+ uint32_t encoded = LengthOrDupeField::encode(dupe_of) |
+ IsDuplicateField::encode(true);
+ object_mapping_.Add(encoded, zone());
+ }
+
+ int ObjectDuplicateOfAt(int index) {
+ ASSERT(ObjectIsDuplicateAt(index));
+ return LengthOrDupeField::decode(object_mapping_[index]);
+ }
+
+ int ObjectLengthAt(int index) {
+ ASSERT(!ObjectIsDuplicateAt(index));
+ return LengthOrDupeField::decode(object_mapping_[index]);
+ }
+
+ bool ObjectIsArgumentsAt(int index) {
+ ASSERT(!ObjectIsDuplicateAt(index));
+ return IsArgumentsField::decode(object_mapping_[index]);
+ }
+
+ bool ObjectIsDuplicateAt(int index) {
+ return IsDuplicateField::decode(object_mapping_[index]);
+ }
+
+ void Register(int deoptimization_index,
+ int translation_index,
+ int pc_offset) {
+ ASSERT(!HasBeenRegistered());
+ deoptimization_index_ = deoptimization_index;
+ translation_index_ = translation_index;
+ pc_offset_ = pc_offset;
+ }
+ bool HasBeenRegistered() const {
+ return deoptimization_index_ != Safepoint::kNoDeoptimizationIndex;
+ }
+
+ void PrintTo(StringStream* stream);
+
+ // Marker value indicating a de-materialized object.
+ static LOperand* materialization_marker() { return NULL; }
+
+ // Encoding used for the object_mapping map below.
+ class LengthOrDupeField : public BitField<int, 0, 30> { };
+ class IsArgumentsField : public BitField<bool, 30, 1> { };
+ class IsDuplicateField : public BitField<bool, 31, 1> { };
+
+ private:
+ Handle<JSFunction> closure_;
+ FrameType frame_type_;
+ int arguments_stack_height_;
+ int deoptimization_index_;
+ int translation_index_;
+ BailoutId ast_id_;
+ int translation_size_;
+ int parameter_count_;
+ int pc_offset_;
+
+ // Value array: [parameters] [locals] [expression stack] [de-materialized].
+ // |>--------- translation_size ---------<|
+ ZoneList<LOperand*> values_;
+ GrowableBitVector is_tagged_;
+ GrowableBitVector is_uint32_;
+
+ // Map with encoded information about materialization_marker operands.
+ ZoneList<uint32_t> object_mapping_;
+
+ LEnvironment* outer_;
+ HEnterInlined* entry_;
+ Zone* zone_;
+};
+
+
+// Iterates over the non-null, non-constant operands in an environment.
+class ShallowIterator BASE_EMBEDDED {
+ public:
+ explicit ShallowIterator(LEnvironment* env)
+ : env_(env),
+ limit_(env != NULL ? env->values()->length() : 0),
+ current_(0) {
+ SkipUninteresting();
+ }
+
+ bool Done() { return current_ >= limit_; }
+
+ LOperand* Current() {
+ ASSERT(!Done());
+ ASSERT(env_->values()->at(current_) != NULL);
+ return env_->values()->at(current_);
+ }
+
+ void Advance() {
+ ASSERT(!Done());
+ ++current_;
+ SkipUninteresting();
+ }
+
+ LEnvironment* env() { return env_; }
+
+ private:
+ bool ShouldSkip(LOperand* op) {
+ return op == NULL || op->IsConstantOperand() || op->IsArgument();
+ }
+
+ // Skip until something interesting, beginning with and including current_.
+ void SkipUninteresting() {
+ while (current_ < limit_ && ShouldSkip(env_->values()->at(current_))) {
+ ++current_;
+ }
+ }
+
+ LEnvironment* env_;
+ int limit_;
+ int current_;
+};
+
+
+// Iterator for non-null, non-constant operands incl. outer environments.
+class DeepIterator BASE_EMBEDDED {
+ public:
+ explicit DeepIterator(LEnvironment* env)
+ : current_iterator_(env) {
+ SkipUninteresting();
+ }
+
+ bool Done() { return current_iterator_.Done(); }
+
+ LOperand* Current() {
+ ASSERT(!current_iterator_.Done());
+ ASSERT(current_iterator_.Current() != NULL);
+ return current_iterator_.Current();
+ }
+
+ void Advance() {
+ current_iterator_.Advance();
+ SkipUninteresting();
+ }
+
+ private:
+ void SkipUninteresting() {
+ while (current_iterator_.env() != NULL && current_iterator_.Done()) {
+ current_iterator_ = ShallowIterator(current_iterator_.env()->outer());
+ }
+ }
+
+ ShallowIterator current_iterator_;
+};
+
+
+class LPlatformChunk;
+class LGap;
+class LLabel;
+
+// Superclass providing data and behavior common to all the
+// arch-specific LPlatformChunk classes.
+class LChunk: public ZoneObject {
+ public:
+ static LChunk* NewChunk(HGraph* graph);
+
+ void AddInstruction(LInstruction* instruction, HBasicBlock* block);
+ LConstantOperand* DefineConstantOperand(HConstant* constant);
+ HConstant* LookupConstant(LConstantOperand* operand) const;
+ Representation LookupLiteralRepresentation(LConstantOperand* operand) const;
+
+ 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_; }
+ Isolate* isolate() const { return graph_->isolate(); }
+ 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;
+ int LookupDestination(int block_id) const;
+ Label* GetAssemblyLabel(int block_id) const;
+
+ const ZoneList<Handle<JSFunction> >* inlined_closures() const {
+ return &inlined_closures_;
+ }
+
+ void AddInlinedClosure(Handle<JSFunction> closure) {
+ inlined_closures_.Add(closure, zone());
+ }
+
+ Zone* zone() const { return info_->zone(); }
+
+ Handle<Code> Codegen();
+
+ void set_allocated_double_registers(BitVector* allocated_registers);
+ BitVector* allocated_double_registers() {
+ return allocated_double_registers_;
+ }
+
+ protected:
+ LChunk(CompilationInfo* info, HGraph* graph);
+
+ int spill_slot_count_;
+
+ private:
+ CompilationInfo* info_;
+ HGraph* const graph_;
+ BitVector* allocated_double_registers_;
+ ZoneList<LInstruction*> instructions_;
+ ZoneList<LPointerMap*> pointer_maps_;
+ ZoneList<Handle<JSFunction> > inlined_closures_;
+};
+
+
+int ElementsKindToShiftSize(ElementsKind elements_kind);
+int StackSlotOffset(int index);
+
+enum NumberUntagDMode {
+ NUMBER_CANDIDATE_IS_SMI,
+ NUMBER_CANDIDATE_IS_ANY_TAGGED
+};
+
+
+class LPhase : public CompilationPhase {
+ public:
+ LPhase(const char* name, LChunk* chunk)
+ : CompilationPhase(name, chunk->info()),
+ chunk_(chunk) { }
+ ~LPhase();
+
+ private:
+ LChunk* chunk_;
+
+ DISALLOW_COPY_AND_ASSIGN(LPhase);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_LITHIUM_H_
diff --git a/chromium/v8/src/liveedit-debugger.js b/chromium/v8/src/liveedit-debugger.js
new file mode 100644
index 00000000000..451b146bde7
--- /dev/null
+++ b/chromium/v8/src/liveedit-debugger.js
@@ -0,0 +1,1137 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// LiveEdit feature implementation. The script should be executed after
+// debug-debugger.js.
+
+// A LiveEdit namespace. It contains functions that modifies JavaScript code
+// according to changes of script source (if possible).
+//
+// When new script source is put in, the difference is calculated textually,
+// in form of list of delete/add/change chunks. The functions that include
+// change chunk(s) get recompiled, or their enclosing functions are
+// recompiled instead.
+// If the function may not be recompiled (e.g. it was completely erased in new
+// version of the script) it remains unchanged, but the code that could
+// create a new instance of this function goes away. An old version of script
+// is created to back up this obsolete function.
+// All unchanged functions have their positions updated accordingly.
+//
+// LiveEdit namespace is declared inside a single function constructor.
+Debug.LiveEdit = new function() {
+
+ // Forward declaration for minifier.
+ var FunctionStatus;
+
+ var NEEDS_STEP_IN_PROPERTY_NAME = "stack_update_needs_step_in";
+
+ // Applies the change to the script.
+ // The change is in form of list of chunks encoded in a single array as
+ // a series of triplets (pos1_start, pos1_end, pos2_end)
+ function ApplyPatchMultiChunk(script, diff_array, new_source, preview_only,
+ change_log) {
+
+ var old_source = script.source;
+
+ // Gather compile information about old version of script.
+ var old_compile_info = GatherCompileInfo(old_source, script);
+
+ // Build tree structures for old and new versions of the script.
+ var root_old_node = BuildCodeInfoTree(old_compile_info);
+
+ var pos_translator = new PosTranslator(diff_array);
+
+ // Analyze changes.
+ MarkChangedFunctions(root_old_node, pos_translator.GetChunks());
+
+ // Find all SharedFunctionInfo's that were compiled from this script.
+ FindLiveSharedInfos(root_old_node, script);
+
+ // Gather compile information about new version of script.
+ var new_compile_info;
+ try {
+ new_compile_info = GatherCompileInfo(new_source, script);
+ } catch (e) {
+ var failure =
+ new Failure("Failed to compile new version of script: " + e);
+ if (e instanceof SyntaxError) {
+ var details = {
+ type: "liveedit_compile_error",
+ syntaxErrorMessage: e.message
+ };
+ CopyErrorPositionToDetails(e, details);
+ failure.details = details;
+ }
+ throw failure;
+ }
+ var root_new_node = BuildCodeInfoTree(new_compile_info);
+
+ // Link recompiled script data with other data.
+ FindCorrespondingFunctions(root_old_node, root_new_node);
+
+ // Prepare to-do lists.
+ var replace_code_list = new Array();
+ var link_to_old_script_list = new Array();
+ var link_to_original_script_list = new Array();
+ var update_positions_list = new Array();
+
+ function HarvestTodo(old_node) {
+ function CollectDamaged(node) {
+ link_to_old_script_list.push(node);
+ for (var i = 0; i < node.children.length; i++) {
+ CollectDamaged(node.children[i]);
+ }
+ }
+
+ // Recursively collects all newly compiled functions that are going into
+ // business and should have link to the actual script updated.
+ function CollectNew(node_list) {
+ for (var i = 0; i < node_list.length; i++) {
+ link_to_original_script_list.push(node_list[i]);
+ CollectNew(node_list[i].children);
+ }
+ }
+
+ if (old_node.status == FunctionStatus.DAMAGED) {
+ CollectDamaged(old_node);
+ return;
+ }
+ if (old_node.status == FunctionStatus.UNCHANGED) {
+ update_positions_list.push(old_node);
+ } else if (old_node.status == FunctionStatus.SOURCE_CHANGED) {
+ update_positions_list.push(old_node);
+ } else if (old_node.status == FunctionStatus.CHANGED) {
+ replace_code_list.push(old_node);
+ CollectNew(old_node.unmatched_new_nodes);
+ }
+ for (var i = 0; i < old_node.children.length; i++) {
+ HarvestTodo(old_node.children[i]);
+ }
+ }
+
+ var preview_description = {
+ change_tree: DescribeChangeTree(root_old_node),
+ textual_diff: {
+ old_len: old_source.length,
+ new_len: new_source.length,
+ chunks: diff_array
+ },
+ updated: false
+ };
+
+ if (preview_only) {
+ return preview_description;
+ }
+
+ HarvestTodo(root_old_node);
+
+ // Collect shared infos for functions whose code need to be patched.
+ var replaced_function_infos = new Array();
+ for (var i = 0; i < replace_code_list.length; i++) {
+ var live_shared_function_infos =
+ replace_code_list[i].live_shared_function_infos;
+
+ if (live_shared_function_infos) {
+ for (var j = 0; j < live_shared_function_infos.length; j++) {
+ replaced_function_infos.push(live_shared_function_infos[j]);
+ }
+ }
+ }
+
+ // We haven't changed anything before this line yet.
+ // Committing all changes.
+
+ // Check that function being patched is not currently on stack or drop them.
+ var dropped_functions_number =
+ CheckStackActivations(replaced_function_infos, change_log);
+
+ preview_description.stack_modified = dropped_functions_number != 0;
+
+ // Our current implementation requires client to manually issue "step in"
+ // command for correct stack state.
+ preview_description[NEEDS_STEP_IN_PROPERTY_NAME] =
+ preview_description.stack_modified;
+
+ // Start with breakpoints. Convert their line/column positions and
+ // temporary remove.
+ var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log);
+
+ var old_script;
+
+ // Create an old script only if there are function that should be linked
+ // to old version.
+ if (link_to_old_script_list.length == 0) {
+ %LiveEditReplaceScript(script, new_source, null);
+ old_script = void 0;
+ } else {
+ var old_script_name = CreateNameForOldScript(script);
+
+ // Update the script text and create a new script representing an old
+ // version of the script.
+ old_script = %LiveEditReplaceScript(script, new_source,
+ old_script_name);
+
+ var link_to_old_script_report = new Array();
+ change_log.push( { linked_to_old_script: link_to_old_script_report } );
+
+ // We need to link to old script all former nested functions.
+ for (var i = 0; i < link_to_old_script_list.length; i++) {
+ LinkToOldScript(link_to_old_script_list[i], old_script,
+ link_to_old_script_report);
+ }
+
+ preview_description.created_script_name = old_script_name;
+ }
+
+ // Link to an actual script all the functions that we are going to use.
+ for (var i = 0; i < link_to_original_script_list.length; i++) {
+ %LiveEditFunctionSetScript(
+ link_to_original_script_list[i].info.shared_function_info, script);
+ }
+
+ for (var i = 0; i < replace_code_list.length; i++) {
+ PatchFunctionCode(replace_code_list[i], change_log);
+ }
+
+ var position_patch_report = new Array();
+ change_log.push( {position_patched: position_patch_report} );
+
+ for (var i = 0; i < update_positions_list.length; i++) {
+ // TODO(LiveEdit): take into account wether it's source_changed or
+ // unchanged and whether positions changed at all.
+ PatchPositions(update_positions_list[i], diff_array,
+ position_patch_report);
+
+ if (update_positions_list[i].live_shared_function_infos) {
+ update_positions_list[i].live_shared_function_infos.
+ forEach(function (info) {
+ %LiveEditFunctionSourceUpdated(info.raw_array);
+ });
+ }
+ }
+
+ break_points_restorer(pos_translator, old_script);
+
+ preview_description.updated = true;
+ return preview_description;
+ }
+ // Function is public.
+ this.ApplyPatchMultiChunk = ApplyPatchMultiChunk;
+
+
+ // Fully compiles source string as a script. Returns Array of
+ // FunctionCompileInfo -- a descriptions of all functions of the script.
+ // Elements of array are ordered by start positions of functions (from top
+ // to bottom) in the source. Fields outer_index and next_sibling_index help
+ // to navigate the nesting structure of functions.
+ //
+ // All functions get compiled linked to script provided as parameter script.
+ // TODO(LiveEdit): consider not using actual scripts as script, because
+ // we have to manually erase all links right after compile.
+ function GatherCompileInfo(source, script) {
+ // Get function info, elements are partially sorted (it is a tree of
+ // nested functions serialized as parent followed by serialized children.
+ var raw_compile_info = %LiveEditGatherCompileInfo(script, source);
+
+ // Sort function infos by start position field.
+ var compile_info = new Array();
+ var old_index_map = new Array();
+ for (var i = 0; i < raw_compile_info.length; i++) {
+ var info = new FunctionCompileInfo(raw_compile_info[i]);
+ // Remove all links to the actual script. Breakpoints system and
+ // LiveEdit itself believe that any function in heap that points to a
+ // particular script is a regular function.
+ // For some functions we will restore this link later.
+ %LiveEditFunctionSetScript(info.shared_function_info, void 0);
+ compile_info.push(info);
+ old_index_map.push(i);
+ }
+
+ for (var i = 0; i < compile_info.length; i++) {
+ var k = i;
+ for (var j = i + 1; j < compile_info.length; j++) {
+ if (compile_info[k].start_position > compile_info[j].start_position) {
+ k = j;
+ }
+ }
+ if (k != i) {
+ var temp_info = compile_info[k];
+ var temp_index = old_index_map[k];
+ compile_info[k] = compile_info[i];
+ old_index_map[k] = old_index_map[i];
+ compile_info[i] = temp_info;
+ old_index_map[i] = temp_index;
+ }
+ }
+
+ // After sorting update outer_inder field using old_index_map. Also
+ // set next_sibling_index field.
+ var current_index = 0;
+
+ // The recursive function, that goes over all children of a particular
+ // node (i.e. function info).
+ function ResetIndexes(new_parent_index, old_parent_index) {
+ var previous_sibling = -1;
+ while (current_index < compile_info.length &&
+ compile_info[current_index].outer_index == old_parent_index) {
+ var saved_index = current_index;
+ compile_info[saved_index].outer_index = new_parent_index;
+ if (previous_sibling != -1) {
+ compile_info[previous_sibling].next_sibling_index = saved_index;
+ }
+ previous_sibling = saved_index;
+ current_index++;
+ ResetIndexes(saved_index, old_index_map[saved_index]);
+ }
+ if (previous_sibling != -1) {
+ compile_info[previous_sibling].next_sibling_index = -1;
+ }
+ }
+
+ ResetIndexes(-1, -1);
+ Assert(current_index == compile_info.length);
+
+ return compile_info;
+ }
+
+
+ // Replaces function's Code.
+ function PatchFunctionCode(old_node, change_log) {
+ var new_info = old_node.corresponding_node.info;
+ if (old_node.live_shared_function_infos) {
+ old_node.live_shared_function_infos.forEach(function (old_info) {
+ %LiveEditReplaceFunctionCode(new_info.raw_array,
+ old_info.raw_array);
+
+ // The function got a new code. However, this new code brings all new
+ // instances of SharedFunctionInfo for nested functions. However,
+ // we want the original instances to be used wherever possible.
+ // (This is because old instances and new instances will be both
+ // linked to a script and breakpoints subsystem does not really
+ // expects this; neither does LiveEdit subsystem on next call).
+ for (var i = 0; i < old_node.children.length; i++) {
+ if (old_node.children[i].corresponding_node) {
+ var corresponding_child_info =
+ old_node.children[i].corresponding_node.info.
+ shared_function_info;
+
+ 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);
+ });
+ }
+ }
+ }
+ });
+
+ change_log.push( {function_patched: new_info.function_name} );
+ } else {
+ change_log.push( {function_patched: new_info.function_name,
+ function_info_not_found: true} );
+ }
+ }
+
+
+ // Makes a function associated with another instance of a script (the
+ // one representing its old version). This way the function still
+ // may access its own text.
+ function LinkToOldScript(old_info_node, old_script, report_array) {
+ if (old_info_node.live_shared_function_infos) {
+ old_info_node.live_shared_function_infos.
+ forEach(function (info) {
+ %LiveEditFunctionSetScript(info.info, old_script);
+ });
+
+ report_array.push( { name: old_info_node.info.function_name } );
+ } else {
+ report_array.push(
+ { name: old_info_node.info.function_name, not_found: true } );
+ }
+ }
+
+
+ // Returns function that restores breakpoints.
+ function TemporaryRemoveBreakPoints(original_script, change_log) {
+ var script_break_points = GetScriptBreakPoints(original_script);
+
+ var break_points_update_report = [];
+ change_log.push( { break_points_update: break_points_update_report } );
+
+ var break_point_old_positions = [];
+ for (var i = 0; i < script_break_points.length; i++) {
+ var break_point = script_break_points[i];
+
+ break_point.clear();
+
+ // TODO(LiveEdit): be careful with resource offset here.
+ var break_point_position = Debug.findScriptSourcePosition(original_script,
+ break_point.line(), break_point.column());
+
+ var old_position_description = {
+ position: break_point_position,
+ line: break_point.line(),
+ column: break_point.column()
+ };
+ break_point_old_positions.push(old_position_description);
+ }
+
+
+ // Restores breakpoints and creates their copies in the "old" copy of
+ // the script.
+ return function (pos_translator, old_script_copy_opt) {
+ // Update breakpoints (change positions and restore them in old version
+ // of script.
+ for (var i = 0; i < script_break_points.length; i++) {
+ var break_point = script_break_points[i];
+ if (old_script_copy_opt) {
+ var clone = break_point.cloneForOtherScript(old_script_copy_opt);
+ clone.set(old_script_copy_opt);
+
+ break_points_update_report.push( {
+ type: "copied_to_old",
+ id: break_point.number(),
+ new_id: clone.number(),
+ positions: break_point_old_positions[i]
+ } );
+ }
+
+ var updated_position = pos_translator.Translate(
+ break_point_old_positions[i].position,
+ PosTranslator.ShiftWithTopInsideChunkHandler);
+
+ var new_location =
+ original_script.locationFromPosition(updated_position, false);
+
+ break_point.update_positions(new_location.line, new_location.column);
+
+ var new_position_description = {
+ position: updated_position,
+ line: new_location.line,
+ column: new_location.column
+ };
+
+ break_point.set(original_script);
+
+ break_points_update_report.push( { type: "position_changed",
+ id: break_point.number(),
+ old_positions: break_point_old_positions[i],
+ new_positions: new_position_description
+ } );
+ }
+ };
+ }
+
+
+ function Assert(condition, message) {
+ if (!condition) {
+ if (message) {
+ throw "Assert " + message;
+ } else {
+ throw "Assert";
+ }
+ }
+ }
+
+ function DiffChunk(pos1, pos2, len1, len2) {
+ this.pos1 = pos1;
+ this.pos2 = pos2;
+ this.len1 = len1;
+ this.len2 = len2;
+ }
+
+ function PosTranslator(diff_array) {
+ var chunks = new Array();
+ var current_diff = 0;
+ for (var i = 0; i < diff_array.length; i += 3) {
+ var pos1_begin = diff_array[i];
+ var pos2_begin = pos1_begin + current_diff;
+ var pos1_end = diff_array[i + 1];
+ var pos2_end = diff_array[i + 2];
+ chunks.push(new DiffChunk(pos1_begin, pos2_begin, pos1_end - pos1_begin,
+ pos2_end - pos2_begin));
+ current_diff = pos2_end - pos1_end;
+ }
+ this.chunks = chunks;
+ }
+ PosTranslator.prototype.GetChunks = function() {
+ return this.chunks;
+ };
+
+ PosTranslator.prototype.Translate = function(pos, inside_chunk_handler) {
+ var array = this.chunks;
+ if (array.length == 0 || pos < array[0].pos1) {
+ return pos;
+ }
+ var chunk_index1 = 0;
+ var chunk_index2 = array.length - 1;
+
+ while (chunk_index1 < chunk_index2) {
+ var middle_index = Math.floor((chunk_index1 + chunk_index2) / 2);
+ if (pos < array[middle_index + 1].pos1) {
+ chunk_index2 = middle_index;
+ } else {
+ chunk_index1 = middle_index + 1;
+ }
+ }
+ var chunk = array[chunk_index1];
+ if (pos >= chunk.pos1 + chunk.len1) {
+ return pos + chunk.pos2 + chunk.len2 - chunk.pos1 - chunk.len1;
+ }
+
+ if (!inside_chunk_handler) {
+ 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
+ // in script may have been shifted.
+ UNCHANGED: "unchanged",
+ // The code of a function remains unchanged, but something happened inside
+ // some inner functions.
+ SOURCE_CHANGED: "source changed",
+ // The code of a function is changed or some nested function cannot be
+ // properly patched so this function must be recompiled.
+ CHANGED: "changed",
+ // Function is changed but cannot be patched.
+ DAMAGED: "damaged"
+ };
+
+ function CodeInfoTreeNode(code_info, children, array_index) {
+ this.info = code_info;
+ this.children = children;
+ // an index in array of compile_info
+ this.array_index = array_index;
+ this.parent = void 0;
+
+ this.status = FunctionStatus.UNCHANGED;
+ // Status explanation is used for debugging purposes and will be shown
+ // in user UI if some explanations are needed.
+ this.status_explanation = void 0;
+ this.new_start_pos = void 0;
+ this.new_end_pos = void 0;
+ this.corresponding_node = void 0;
+ this.unmatched_new_nodes = void 0;
+
+ // 'Textual' correspondence/matching is weaker than 'pure'
+ // correspondence/matching. We need 'textual' level for visual presentation
+ // in UI, we use 'pure' level for actual code manipulation.
+ // Sometimes only function body is changed (functions in old and new script
+ // textually correspond), but we cannot patch the code, so we see them
+ // as an old function deleted and new function created.
+ this.textual_corresponding_node = void 0;
+ this.textually_unmatched_new_nodes = void 0;
+
+ this.live_shared_function_infos = void 0;
+ }
+
+ // From array of function infos that is implicitly a tree creates
+ // an actual tree of functions in script.
+ function BuildCodeInfoTree(code_info_array) {
+ // Throughtout all function we iterate over input array.
+ var index = 0;
+
+ // Recursive function that builds a branch of tree.
+ function BuildNode() {
+ var my_index = index;
+ index++;
+ var child_array = new Array();
+ while (index < code_info_array.length &&
+ code_info_array[index].outer_index == my_index) {
+ child_array.push(BuildNode());
+ }
+ var node = new CodeInfoTreeNode(code_info_array[my_index], child_array,
+ my_index);
+ for (var i = 0; i < child_array.length; i++) {
+ child_array[i].parent = node;
+ }
+ return node;
+ }
+
+ var root = BuildNode();
+ Assert(index == code_info_array.length);
+ return root;
+ }
+
+ // Applies a list of the textual diff chunks onto the tree of functions.
+ // Determines status of each function (from unchanged to damaged). However
+ // children of unchanged functions are ignored.
+ function MarkChangedFunctions(code_info_tree, chunks) {
+
+ // 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.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; };
+ };
+
+ // A recursive function that processes internals of a function and all its
+ // inner functions. Iterator chunk_it initially points to a chunk that is
+ // below function start.
+ function ProcessInternals(info_node) {
+ info_node.new_start_pos = chunk_it.TranslatePos(
+ info_node.info.start_position);
+ var child_index = 0;
+ var code_changed = false;
+ var source_changed = false;
+ // Simultaneously iterates over child functions and over chunks.
+ while (!chunk_it.done() &&
+ chunk_it.current().pos1 < info_node.info.end_position) {
+ if (child_index < info_node.children.length) {
+ var child = info_node.children[child_index];
+
+ if (child.info.end_position <= chunk_it.current().pos1) {
+ ProcessUnchangedChild(child);
+ child_index++;
+ continue;
+ } else if (child.info.start_position >=
+ chunk_it.current().pos1 + chunk_it.current().len1) {
+ code_changed = true;
+ chunk_it.next();
+ continue;
+ } else if (child.info.start_position <= chunk_it.current().pos1 &&
+ child.info.end_position >= chunk_it.current().pos1 +
+ chunk_it.current().len1) {
+ ProcessInternals(child);
+ source_changed = source_changed ||
+ ( child.status != FunctionStatus.UNCHANGED );
+ code_changed = code_changed ||
+ ( child.status == FunctionStatus.DAMAGED );
+ child_index++;
+ continue;
+ } else {
+ code_changed = true;
+ child.status = FunctionStatus.DAMAGED;
+ child.status_explanation =
+ "Text diff overlaps with function boundary";
+ child_index++;
+ continue;
+ }
+ } else {
+ if (chunk_it.current().pos1 + chunk_it.current().len1 <=
+ info_node.info.end_position) {
+ info_node.status = FunctionStatus.CHANGED;
+ chunk_it.next();
+ continue;
+ } else {
+ info_node.status = FunctionStatus.DAMAGED;
+ info_node.status_explanation =
+ "Text diff overlaps with function boundary";
+ return;
+ }
+ }
+ Assert("Unreachable", false);
+ }
+ while (child_index < info_node.children.length) {
+ var child = info_node.children[child_index];
+ ProcessUnchangedChild(child);
+ child_index++;
+ }
+ if (code_changed) {
+ info_node.status = FunctionStatus.CHANGED;
+ } else if (source_changed) {
+ info_node.status = FunctionStatus.SOURCE_CHANGED;
+ }
+ info_node.new_end_pos =
+ chunk_it.TranslatePos(info_node.info.end_position);
+ }
+
+ function ProcessUnchangedChild(node) {
+ node.new_start_pos = chunk_it.TranslatePos(node.info.start_position);
+ node.new_end_pos = chunk_it.TranslatePos(node.info.end_position);
+ }
+
+ ProcessInternals(code_info_tree);
+ }
+
+ // For ecah old function (if it is not damaged) tries to find a corresponding
+ // function in new script. Typically it should succeed (non-damaged functions
+ // by definition may only have changes inside their bodies). However there are
+ // reasons for corresponence not to be found; function with unmodified text
+ // in new script may become enclosed into other function; the innocent change
+ // inside function body may in fact be something like "} function B() {" that
+ // splits a function into 2 functions.
+ function FindCorrespondingFunctions(old_code_tree, new_code_tree) {
+
+ // A recursive function that tries to find a correspondence for all
+ // child functions and for their inner functions.
+ function ProcessChildren(old_node, new_node) {
+ var old_children = old_node.children;
+ var new_children = new_node.children;
+
+ var unmatched_new_nodes_list = [];
+ var textually_unmatched_new_nodes_list = [];
+
+ var old_index = 0;
+ var new_index = 0;
+ while (old_index < old_children.length) {
+ if (old_children[old_index].status == FunctionStatus.DAMAGED) {
+ old_index++;
+ } else if (new_index < new_children.length) {
+ if (new_children[new_index].info.start_position <
+ old_children[old_index].new_start_pos) {
+ unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
+ new_index++;
+ } else if (new_children[new_index].info.start_position ==
+ old_children[old_index].new_start_pos) {
+ if (new_children[new_index].info.end_position ==
+ old_children[old_index].new_end_pos) {
+ old_children[old_index].corresponding_node =
+ new_children[new_index];
+ old_children[old_index].textual_corresponding_node =
+ new_children[new_index];
+ if (old_children[old_index].status != FunctionStatus.UNCHANGED) {
+ ProcessChildren(old_children[old_index],
+ new_children[new_index]);
+ if (old_children[old_index].status == FunctionStatus.DAMAGED) {
+ unmatched_new_nodes_list.push(
+ old_children[old_index].corresponding_node);
+ old_children[old_index].corresponding_node = void 0;
+ old_node.status = FunctionStatus.CHANGED;
+ }
+ }
+ } else {
+ old_children[old_index].status = FunctionStatus.DAMAGED;
+ old_children[old_index].status_explanation =
+ "No corresponding function in new script found";
+ old_node.status = FunctionStatus.CHANGED;
+ unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
+ }
+ new_index++;
+ old_index++;
+ } else {
+ old_children[old_index].status = FunctionStatus.DAMAGED;
+ old_children[old_index].status_explanation =
+ "No corresponding function in new script found";
+ old_node.status = FunctionStatus.CHANGED;
+ old_index++;
+ }
+ } else {
+ old_children[old_index].status = FunctionStatus.DAMAGED;
+ old_children[old_index].status_explanation =
+ "No corresponding function in new script found";
+ old_node.status = FunctionStatus.CHANGED;
+ old_index++;
+ }
+ }
+
+ while (new_index < new_children.length) {
+ unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
+ new_index++;
+ }
+
+ if (old_node.status == FunctionStatus.CHANGED) {
+ var why_wrong_expectations =
+ WhyFunctionExpectationsDiffer(old_node.info, new_node.info);
+ if (why_wrong_expectations) {
+ old_node.status = FunctionStatus.DAMAGED;
+ old_node.status_explanation = why_wrong_expectations;
+ }
+ }
+ old_node.unmatched_new_nodes = unmatched_new_nodes_list;
+ old_node.textually_unmatched_new_nodes =
+ textually_unmatched_new_nodes_list;
+ }
+
+ ProcessChildren(old_code_tree, new_code_tree);
+
+ old_code_tree.corresponding_node = new_code_tree;
+ old_code_tree.textual_corresponding_node = new_code_tree;
+
+ Assert(old_code_tree.status != FunctionStatus.DAMAGED,
+ "Script became damaged");
+ }
+
+ function FindLiveSharedInfos(old_code_tree, script) {
+ var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script);
+
+ var shared_infos = new Array();
+
+ for (var i = 0; i < shared_raw_list.length; i++) {
+ shared_infos.push(new SharedInfoWrapper(shared_raw_list[i]));
+ }
+
+ // Finds all SharedFunctionInfos that corresponds to compile info
+ // in old version of the script.
+ function FindFunctionInfos(compile_info) {
+ var wrappers = [];
+
+ for (var i = 0; i < shared_infos.length; i++) {
+ var wrapper = shared_infos[i];
+ if (wrapper.start_position == compile_info.start_position &&
+ wrapper.end_position == compile_info.end_position) {
+ wrappers.push(wrapper);
+ }
+ }
+
+ if (wrappers.length > 0) {
+ return wrappers;
+ }
+ }
+
+ function TraverseTree(node) {
+ node.live_shared_function_infos = FindFunctionInfos(node.info);
+
+ for (var i = 0; i < node.children.length; i++) {
+ TraverseTree(node.children[i]);
+ }
+ }
+
+ TraverseTree(old_code_tree);
+ }
+
+
+ // An object describing function compilation details. Its index fields
+ // apply to indexes inside array that stores these objects.
+ function FunctionCompileInfo(raw_array) {
+ this.function_name = raw_array[0];
+ this.start_position = raw_array[1];
+ this.end_position = raw_array[2];
+ this.param_num = raw_array[3];
+ this.code = raw_array[4];
+ this.code_scope_info = raw_array[5];
+ this.scope_info = raw_array[6];
+ this.outer_index = raw_array[7];
+ this.shared_function_info = raw_array[8];
+ this.next_sibling_index = null;
+ this.raw_array = raw_array;
+ }
+
+ function SharedInfoWrapper(raw_array) {
+ this.function_name = raw_array[0];
+ this.start_position = raw_array[1];
+ this.end_position = raw_array[2];
+ this.info = raw_array[3];
+ this.raw_array = raw_array;
+ }
+
+ // Changes positions (including all statments) in function.
+ function PatchPositions(old_info_node, diff_array, report_array) {
+ if (old_info_node.live_shared_function_infos) {
+ old_info_node.live_shared_function_infos.forEach(function (info) {
+ %LiveEditPatchFunctionPositions(info.raw_array,
+ diff_array);
+ });
+
+ report_array.push( { name: old_info_node.info.function_name } );
+ } else {
+ // TODO(LiveEdit): function is not compiled yet or is already collected.
+ report_array.push(
+ { name: old_info_node.info.function_name, info_not_found: true } );
+ }
+ }
+
+ // Adds a suffix to script name to mark that it is old version.
+ function CreateNameForOldScript(script) {
+ // TODO(635): try better than this; support several changes.
+ return script.name + " (old)";
+ }
+
+ // Compares a function interface old and new version, whether it
+ // changed or not. Returns explanation if they differ.
+ function WhyFunctionExpectationsDiffer(function_info1, function_info2) {
+ // Check that function has the same number of parameters (there may exist
+ // an adapter, that won't survive function parameter number change).
+ if (function_info1.param_num != function_info2.param_num) {
+ return "Changed parameter number: " + function_info1.param_num +
+ " and " + function_info2.param_num;
+ }
+ var scope_info1 = function_info1.scope_info;
+ var scope_info2 = function_info2.scope_info;
+
+ var scope_info1_text;
+ var scope_info2_text;
+
+ if (scope_info1) {
+ scope_info1_text = scope_info1.toString();
+ } else {
+ scope_info1_text = "";
+ }
+ if (scope_info2) {
+ scope_info2_text = scope_info2.toString();
+ } else {
+ scope_info2_text = "";
+ }
+
+ if (scope_info1_text != scope_info2_text) {
+ return "Incompatible variable maps: [" + scope_info1_text +
+ "] and [" + scope_info2_text + "]";
+ }
+ // No differences. Return undefined.
+ return;
+ }
+
+ // Minifier forward declaration.
+ var FunctionPatchabilityStatus;
+
+ // For array of wrapped shared function infos checks that none of them
+ // have activations on stack (of any thread). Throws a Failure exception
+ // if this proves to be false.
+ function CheckStackActivations(shared_wrapper_list, change_log) {
+ var shared_list = new Array();
+ for (var i = 0; i < shared_wrapper_list.length; i++) {
+ shared_list[i] = shared_wrapper_list[i].info;
+ }
+ var result = %LiveEditCheckAndDropActivations(shared_list, true);
+ if (result[shared_list.length]) {
+ // Extra array element may contain error message.
+ throw new Failure(result[shared_list.length]);
+ }
+
+ var problems = new Array();
+ var dropped = new Array();
+ for (var i = 0; i < shared_list.length; i++) {
+ var shared = shared_wrapper_list[i];
+ if (result[i] == FunctionPatchabilityStatus.REPLACED_ON_ACTIVE_STACK) {
+ dropped.push({ name: shared.function_name } );
+ } else if (result[i] != FunctionPatchabilityStatus.AVAILABLE_FOR_PATCH) {
+ var description = {
+ name: shared.function_name,
+ start_pos: shared.start_position,
+ end_pos: shared.end_position,
+ replace_problem:
+ FunctionPatchabilityStatus.SymbolName(result[i])
+ };
+ problems.push(description);
+ }
+ }
+ if (dropped.length > 0) {
+ change_log.push({ dropped_from_stack: dropped });
+ }
+ if (problems.length > 0) {
+ change_log.push( { functions_on_stack: problems } );
+ throw new Failure("Blocked by functions on stack");
+ }
+
+ return dropped.length;
+ }
+
+ // A copy of the FunctionPatchabilityStatus enum from liveedit.h
+ var FunctionPatchabilityStatus = {
+ AVAILABLE_FOR_PATCH: 1,
+ BLOCKED_ON_ACTIVE_STACK: 2,
+ BLOCKED_ON_OTHER_STACK: 3,
+ BLOCKED_UNDER_NATIVE_CODE: 4,
+ REPLACED_ON_ACTIVE_STACK: 5
+ };
+
+ FunctionPatchabilityStatus.SymbolName = function(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
+ // is valid and consistent description of what happened.
+ function Failure(message) {
+ this.message = message;
+ }
+ // Function (constructor) is public.
+ this.Failure = Failure;
+
+ Failure.prototype.toString = function() {
+ return "LiveEdit Failure: " + this.message;
+ };
+
+ function CopyErrorPositionToDetails(e, details) {
+ function createPositionStruct(script, position) {
+ if (position == -1) return;
+ var location = script.locationFromPosition(position, true);
+ if (location == null) return;
+ return {
+ line: location.line + 1,
+ column: location.column + 1,
+ position: position
+ };
+ }
+
+ if (!("scriptObject" in e) || !("startPosition" in e)) {
+ return;
+ }
+
+ var script = e.scriptObject;
+
+ var position_struct = {
+ start: createPositionStruct(script, e.startPosition),
+ end: createPositionStruct(script, e.endPosition)
+ };
+ details.position = position_struct;
+ }
+
+ // A testing entry.
+ function GetPcFromSourcePos(func, source_pos) {
+ return %GetFunctionCodePositionFromSource(func, source_pos);
+ }
+ // Function is public.
+ this.GetPcFromSourcePos = GetPcFromSourcePos;
+
+ // LiveEdit main entry point: changes a script text to a new string.
+ function SetScriptSource(script, new_source, preview_only, change_log) {
+ var old_source = script.source;
+ var diff = CompareStrings(old_source, new_source);
+ return ApplyPatchMultiChunk(script, diff, new_source, preview_only,
+ change_log);
+ }
+ // Function is public.
+ this.SetScriptSource = SetScriptSource;
+
+ function CompareStrings(s1, s2) {
+ return %LiveEditCompareStrings(s1, s2);
+ }
+
+ // Applies the change to the script.
+ // The change is always a substring (change_pos, change_pos + change_len)
+ // being replaced with a completely different string new_str.
+ // This API is a legacy and is obsolete.
+ //
+ // @param {Script} script that is being changed
+ // @param {Array} change_log a list that collects engineer-readable
+ // description of what happened.
+ function ApplySingleChunkPatch(script, change_pos, change_len, new_str,
+ change_log) {
+ var old_source = script.source;
+
+ // Prepare new source string.
+ var new_source = old_source.substring(0, change_pos) +
+ new_str + old_source.substring(change_pos + change_len);
+
+ return ApplyPatchMultiChunk(script,
+ [ change_pos, change_pos + change_len, change_pos + new_str.length],
+ new_source, false, change_log);
+ }
+
+ // Creates JSON description for a change tree.
+ function DescribeChangeTree(old_code_tree) {
+
+ function ProcessOldNode(node) {
+ var child_infos = [];
+ for (var i = 0; i < node.children.length; i++) {
+ var child = node.children[i];
+ if (child.status != FunctionStatus.UNCHANGED) {
+ child_infos.push(ProcessOldNode(child));
+ }
+ }
+ var new_child_infos = [];
+ if (node.textually_unmatched_new_nodes) {
+ for (var i = 0; i < node.textually_unmatched_new_nodes.length; i++) {
+ var child = node.textually_unmatched_new_nodes[i];
+ new_child_infos.push(ProcessNewNode(child));
+ }
+ }
+ var res = {
+ name: node.info.function_name,
+ positions: DescribePositions(node),
+ status: node.status,
+ children: child_infos,
+ new_children: new_child_infos
+ };
+ if (node.status_explanation) {
+ res.status_explanation = node.status_explanation;
+ }
+ if (node.textual_corresponding_node) {
+ res.new_positions = DescribePositions(node.textual_corresponding_node);
+ }
+ return res;
+ }
+
+ function ProcessNewNode(node) {
+ var child_infos = [];
+ // Do not list ancestors.
+ if (false) {
+ for (var i = 0; i < node.children.length; i++) {
+ child_infos.push(ProcessNewNode(node.children[i]));
+ }
+ }
+ var res = {
+ name: node.info.function_name,
+ positions: DescribePositions(node),
+ children: child_infos,
+ };
+ return res;
+ }
+
+ function DescribePositions(node) {
+ return {
+ start_position: node.info.start_position,
+ end_position: node.info.end_position
+ };
+ }
+
+ return ProcessOldNode(old_code_tree);
+ }
+
+ // Restarts call frame and returns value similar to what LiveEdit returns.
+ function RestartFrame(frame_mirror) {
+ var result = frame_mirror.restart();
+ if (IS_STRING(result)) {
+ throw new Failure("Failed to restart frame: " + result);
+ }
+ var result = {};
+ result[NEEDS_STEP_IN_PROPERTY_NAME] = true;
+ return result;
+ }
+ // Function is public.
+ this.RestartFrame = RestartFrame;
+
+ // Functions are public for tests.
+ this.TestApi = {
+ PosTranslator: PosTranslator,
+ CompareStrings: CompareStrings,
+ ApplySingleChunkPatch: ApplySingleChunkPatch
+ };
+};
diff --git a/chromium/v8/src/liveedit.cc b/chromium/v8/src/liveedit.cc
new file mode 100644
index 00000000000..406510a3b86
--- /dev/null
+++ b/chromium/v8/src/liveedit.cc
@@ -0,0 +1,2137 @@
+// Copyright 2012 the V8 project authors. 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 "liveedit.h"
+
+#include "code-stubs.h"
+#include "compilation-cache.h"
+#include "compiler.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "global-handles.h"
+#include "messages.h"
+#include "parser.h"
+#include "scopeinfo.h"
+#include "scopes.h"
+#include "v8memory.h"
+
+namespace v8 {
+namespace internal {
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+
+void SetElementNonStrict(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> value) {
+ // Ignore return value from SetElement. It can only be a failure if there
+ // are element setters causing exceptions and the debugger context has none
+ // of these.
+ Handle<Object> no_failure =
+ JSObject::SetElement(object, index, value, NONE, kNonStrictMode);
+ ASSERT(!no_failure.is_null());
+ USE(no_failure);
+}
+
+
+// A simple implementation of dynamic programming algorithm. It solves
+// the problem of finding the difference of 2 arrays. It uses a table of results
+// of subproblems. Each cell contains a number together with 2-bit flag
+// that helps building the chunk list.
+class Differencer {
+ public:
+ explicit Differencer(Comparator::Input* input)
+ : input_(input), len1_(input->GetLength1()), len2_(input->GetLength2()) {
+ buffer_ = NewArray<int>(len1_ * len2_);
+ }
+ ~Differencer() {
+ DeleteArray(buffer_);
+ }
+
+ void Initialize() {
+ int array_size = len1_ * len2_;
+ for (int i = 0; i < array_size; i++) {
+ buffer_[i] = kEmptyCellValue;
+ }
+ }
+
+ // Makes sure that result for the full problem is calculated and stored
+ // in the table together with flags showing a path through subproblems.
+ void FillTable() {
+ CompareUpToTail(0, 0);
+ }
+
+ void SaveResult(Comparator::Output* chunk_writer) {
+ ResultWriter writer(chunk_writer);
+
+ int pos1 = 0;
+ int pos2 = 0;
+ while (true) {
+ if (pos1 < len1_) {
+ if (pos2 < len2_) {
+ Direction dir = get_direction(pos1, pos2);
+ switch (dir) {
+ case EQ:
+ writer.eq();
+ pos1++;
+ pos2++;
+ break;
+ case SKIP1:
+ writer.skip1(1);
+ pos1++;
+ break;
+ case SKIP2:
+ case SKIP_ANY:
+ writer.skip2(1);
+ pos2++;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ writer.skip1(len1_ - pos1);
+ break;
+ }
+ } else {
+ if (len2_ != pos2) {
+ writer.skip2(len2_ - pos2);
+ }
+ break;
+ }
+ }
+ writer.close();
+ }
+
+ private:
+ Comparator::Input* input_;
+ int* buffer_;
+ int len1_;
+ int len2_;
+
+ enum Direction {
+ EQ = 0,
+ SKIP1,
+ SKIP2,
+ SKIP_ANY,
+
+ MAX_DIRECTION_FLAG_VALUE = SKIP_ANY
+ };
+
+ // Computes result for a subtask and optionally caches it in the buffer table.
+ // All results values are shifted to make space for flags in the lower bits.
+ int CompareUpToTail(int pos1, int pos2) {
+ if (pos1 < len1_) {
+ if (pos2 < len2_) {
+ int cached_res = get_value4(pos1, pos2);
+ if (cached_res == kEmptyCellValue) {
+ Direction dir;
+ int res;
+ if (input_->Equals(pos1, pos2)) {
+ res = CompareUpToTail(pos1 + 1, pos2 + 1);
+ dir = EQ;
+ } else {
+ int res1 = CompareUpToTail(pos1 + 1, pos2) +
+ (1 << kDirectionSizeBits);
+ int res2 = CompareUpToTail(pos1, pos2 + 1) +
+ (1 << kDirectionSizeBits);
+ if (res1 == res2) {
+ res = res1;
+ dir = SKIP_ANY;
+ } else if (res1 < res2) {
+ res = res1;
+ dir = SKIP1;
+ } else {
+ res = res2;
+ dir = SKIP2;
+ }
+ }
+ set_value4_and_dir(pos1, pos2, res, dir);
+ cached_res = res;
+ }
+ return cached_res;
+ } else {
+ return (len1_ - pos1) << kDirectionSizeBits;
+ }
+ } else {
+ return (len2_ - pos2) << kDirectionSizeBits;
+ }
+ }
+
+ inline int& get_cell(int i1, int i2) {
+ return buffer_[i1 + i2 * len1_];
+ }
+
+ // Each cell keeps a value plus direction. Value is multiplied by 4.
+ void set_value4_and_dir(int i1, int i2, int value4, Direction dir) {
+ ASSERT((value4 & kDirectionMask) == 0);
+ get_cell(i1, i2) = value4 | dir;
+ }
+
+ int get_value4(int i1, int i2) {
+ return get_cell(i1, i2) & (kMaxUInt32 ^ kDirectionMask);
+ }
+ Direction get_direction(int i1, int i2) {
+ return static_cast<Direction>(get_cell(i1, i2) & kDirectionMask);
+ }
+
+ static const int kDirectionSizeBits = 2;
+ static const int kDirectionMask = (1 << kDirectionSizeBits) - 1;
+ static const int kEmptyCellValue = -1 << kDirectionSizeBits;
+
+ // This method only holds static assert statement (unfortunately you cannot
+ // place one in class scope).
+ void StaticAssertHolder() {
+ STATIC_ASSERT(MAX_DIRECTION_FLAG_VALUE < (1 << kDirectionSizeBits));
+ }
+
+ class ResultWriter {
+ public:
+ explicit ResultWriter(Comparator::Output* chunk_writer)
+ : chunk_writer_(chunk_writer), pos1_(0), pos2_(0),
+ pos1_begin_(-1), pos2_begin_(-1), has_open_chunk_(false) {
+ }
+ void eq() {
+ FlushChunk();
+ pos1_++;
+ pos2_++;
+ }
+ void skip1(int len1) {
+ StartChunk();
+ pos1_ += len1;
+ }
+ void skip2(int len2) {
+ StartChunk();
+ pos2_ += len2;
+ }
+ void close() {
+ FlushChunk();
+ }
+
+ private:
+ Comparator::Output* chunk_writer_;
+ int pos1_;
+ int pos2_;
+ int pos1_begin_;
+ int pos2_begin_;
+ bool has_open_chunk_;
+
+ void StartChunk() {
+ if (!has_open_chunk_) {
+ pos1_begin_ = pos1_;
+ pos2_begin_ = pos2_;
+ has_open_chunk_ = true;
+ }
+ }
+
+ void FlushChunk() {
+ if (has_open_chunk_) {
+ chunk_writer_->AddChunk(pos1_begin_, pos2_begin_,
+ pos1_ - pos1_begin_, pos2_ - pos2_begin_);
+ has_open_chunk_ = false;
+ }
+ }
+ };
+};
+
+
+void Comparator::CalculateDifference(Comparator::Input* input,
+ Comparator::Output* result_writer) {
+ Differencer differencer(input);
+ differencer.Initialize();
+ differencer.FillTable();
+ differencer.SaveResult(result_writer);
+}
+
+
+static bool CompareSubstrings(Handle<String> s1, int pos1,
+ Handle<String> s2, int pos2, int len) {
+ for (int i = 0; i < len; i++) {
+ if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// Additional to Input interface. Lets switch Input range to subrange.
+// More elegant way would be to wrap one Input as another Input object
+// and translate positions there, but that would cost us additional virtual
+// call per comparison.
+class SubrangableInput : public Comparator::Input {
+ public:
+ virtual void SetSubrange1(int offset, int len) = 0;
+ virtual void SetSubrange2(int offset, int len) = 0;
+};
+
+
+class SubrangableOutput : public Comparator::Output {
+ public:
+ virtual void SetSubrange1(int offset, int len) = 0;
+ virtual void SetSubrange2(int offset, int len) = 0;
+};
+
+
+static int min(int a, int b) {
+ return a < b ? a : b;
+}
+
+
+// Finds common prefix and suffix in input. This parts shouldn't take space in
+// linear programming table. Enable subranging in input and output.
+static void NarrowDownInput(SubrangableInput* input,
+ SubrangableOutput* output) {
+ const int len1 = input->GetLength1();
+ const int len2 = input->GetLength2();
+
+ int common_prefix_len;
+ int common_suffix_len;
+
+ {
+ common_prefix_len = 0;
+ int prefix_limit = min(len1, len2);
+ while (common_prefix_len < prefix_limit &&
+ input->Equals(common_prefix_len, common_prefix_len)) {
+ common_prefix_len++;
+ }
+
+ common_suffix_len = 0;
+ int suffix_limit = min(len1 - common_prefix_len, len2 - common_prefix_len);
+
+ while (common_suffix_len < suffix_limit &&
+ input->Equals(len1 - common_suffix_len - 1,
+ len2 - common_suffix_len - 1)) {
+ common_suffix_len++;
+ }
+ }
+
+ if (common_prefix_len > 0 || common_suffix_len > 0) {
+ int new_len1 = len1 - common_suffix_len - common_prefix_len;
+ int new_len2 = len2 - common_suffix_len - common_prefix_len;
+
+ input->SetSubrange1(common_prefix_len, new_len1);
+ input->SetSubrange2(common_prefix_len, new_len2);
+
+ output->SetSubrange1(common_prefix_len, new_len1);
+ output->SetSubrange2(common_prefix_len, new_len2);
+ }
+}
+
+
+// A helper class that writes chunk numbers into JSArray.
+// Each chunk is stored as 3 array elements: (pos1_begin, pos1_end, pos2_end).
+class CompareOutputArrayWriter {
+ public:
+ explicit CompareOutputArrayWriter(Isolate* isolate)
+ : array_(isolate->factory()->NewJSArray(10)), current_size_(0) {}
+
+ Handle<JSArray> GetResult() {
+ return array_;
+ }
+
+ void WriteChunk(int char_pos1, int char_pos2, int char_len1, int char_len2) {
+ Isolate* isolate = array_->GetIsolate();
+ SetElementNonStrict(array_,
+ current_size_,
+ Handle<Object>(Smi::FromInt(char_pos1), isolate));
+ SetElementNonStrict(array_,
+ current_size_ + 1,
+ Handle<Object>(Smi::FromInt(char_pos1 + char_len1),
+ isolate));
+ SetElementNonStrict(array_,
+ current_size_ + 2,
+ Handle<Object>(Smi::FromInt(char_pos2 + char_len2),
+ isolate));
+ current_size_ += 3;
+ }
+
+ private:
+ Handle<JSArray> array_;
+ int current_size_;
+};
+
+
+// Represents 2 strings as 2 arrays of tokens.
+// TODO(LiveEdit): Currently it's actually an array of charactres.
+// Make array of tokens instead.
+class TokensCompareInput : public Comparator::Input {
+ public:
+ TokensCompareInput(Handle<String> s1, int offset1, int len1,
+ Handle<String> s2, int offset2, int len2)
+ : s1_(s1), offset1_(offset1), len1_(len1),
+ s2_(s2), offset2_(offset2), len2_(len2) {
+ }
+ virtual int GetLength1() {
+ return len1_;
+ }
+ virtual int GetLength2() {
+ return len2_;
+ }
+ bool Equals(int index1, int index2) {
+ return s1_->Get(offset1_ + index1) == s2_->Get(offset2_ + index2);
+ }
+
+ private:
+ Handle<String> s1_;
+ int offset1_;
+ int len1_;
+ Handle<String> s2_;
+ int offset2_;
+ int len2_;
+};
+
+
+// Stores compare result in JSArray. Converts substring positions
+// to absolute positions.
+class TokensCompareOutput : public Comparator::Output {
+ public:
+ TokensCompareOutput(CompareOutputArrayWriter* array_writer,
+ int offset1, int offset2)
+ : array_writer_(array_writer), offset1_(offset1), offset2_(offset2) {
+ }
+
+ void AddChunk(int pos1, int pos2, int len1, int len2) {
+ array_writer_->WriteChunk(pos1 + offset1_, pos2 + offset2_, len1, len2);
+ }
+
+ private:
+ CompareOutputArrayWriter* array_writer_;
+ int offset1_;
+ int offset2_;
+};
+
+
+// Wraps raw n-elements line_ends array as a list of n+1 lines. The last line
+// never has terminating new line character.
+class LineEndsWrapper {
+ public:
+ explicit LineEndsWrapper(Handle<String> string)
+ : ends_array_(CalculateLineEnds(string, false)),
+ string_len_(string->length()) {
+ }
+ int length() {
+ return ends_array_->length() + 1;
+ }
+ // Returns start for any line including start of the imaginary line after
+ // the last line.
+ int GetLineStart(int index) {
+ if (index == 0) {
+ return 0;
+ } else {
+ return GetLineEnd(index - 1);
+ }
+ }
+ int GetLineEnd(int index) {
+ if (index == ends_array_->length()) {
+ // End of the last line is always an end of the whole string.
+ // If the string ends with a new line character, the last line is an
+ // empty string after this character.
+ return string_len_;
+ } else {
+ return GetPosAfterNewLine(index);
+ }
+ }
+
+ private:
+ Handle<FixedArray> ends_array_;
+ int string_len_;
+
+ int GetPosAfterNewLine(int index) {
+ return Smi::cast(ends_array_->get(index))->value() + 1;
+ }
+};
+
+
+// Represents 2 strings as 2 arrays of lines.
+class LineArrayCompareInput : public SubrangableInput {
+ public:
+ LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
+ LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
+ : s1_(s1), s2_(s2), line_ends1_(line_ends1),
+ line_ends2_(line_ends2),
+ subrange_offset1_(0), subrange_offset2_(0),
+ subrange_len1_(line_ends1_.length()),
+ subrange_len2_(line_ends2_.length()) {
+ }
+ int GetLength1() {
+ return subrange_len1_;
+ }
+ int GetLength2() {
+ return subrange_len2_;
+ }
+ bool Equals(int index1, int index2) {
+ index1 += subrange_offset1_;
+ index2 += subrange_offset2_;
+
+ int line_start1 = line_ends1_.GetLineStart(index1);
+ int line_start2 = line_ends2_.GetLineStart(index2);
+ int line_end1 = line_ends1_.GetLineEnd(index1);
+ int line_end2 = line_ends2_.GetLineEnd(index2);
+ int len1 = line_end1 - line_start1;
+ int len2 = line_end2 - line_start2;
+ if (len1 != len2) {
+ return false;
+ }
+ return CompareSubstrings(s1_, line_start1, s2_, line_start2,
+ len1);
+ }
+ void SetSubrange1(int offset, int len) {
+ subrange_offset1_ = offset;
+ subrange_len1_ = len;
+ }
+ void SetSubrange2(int offset, int len) {
+ subrange_offset2_ = offset;
+ subrange_len2_ = len;
+ }
+
+ private:
+ Handle<String> s1_;
+ Handle<String> s2_;
+ LineEndsWrapper line_ends1_;
+ LineEndsWrapper line_ends2_;
+ int subrange_offset1_;
+ int subrange_offset2_;
+ int subrange_len1_;
+ int subrange_len2_;
+};
+
+
+// Stores compare result in JSArray. For each chunk tries to conduct
+// a fine-grained nested diff token-wise.
+class TokenizingLineArrayCompareOutput : public SubrangableOutput {
+ public:
+ TokenizingLineArrayCompareOutput(LineEndsWrapper line_ends1,
+ LineEndsWrapper line_ends2,
+ Handle<String> s1, Handle<String> s2)
+ : array_writer_(s1->GetIsolate()),
+ line_ends1_(line_ends1), line_ends2_(line_ends2), s1_(s1), s2_(s2),
+ subrange_offset1_(0), subrange_offset2_(0) {
+ }
+
+ void AddChunk(int line_pos1, int line_pos2, int line_len1, int line_len2) {
+ line_pos1 += subrange_offset1_;
+ line_pos2 += subrange_offset2_;
+
+ int char_pos1 = line_ends1_.GetLineStart(line_pos1);
+ int char_pos2 = line_ends2_.GetLineStart(line_pos2);
+ int char_len1 = line_ends1_.GetLineStart(line_pos1 + line_len1) - char_pos1;
+ int char_len2 = line_ends2_.GetLineStart(line_pos2 + line_len2) - char_pos2;
+
+ if (char_len1 < CHUNK_LEN_LIMIT && char_len2 < CHUNK_LEN_LIMIT) {
+ // Chunk is small enough to conduct a nested token-level diff.
+ HandleScope subTaskScope(s1_->GetIsolate());
+
+ TokensCompareInput tokens_input(s1_, char_pos1, char_len1,
+ s2_, char_pos2, char_len2);
+ TokensCompareOutput tokens_output(&array_writer_, char_pos1,
+ char_pos2);
+
+ Comparator::CalculateDifference(&tokens_input, &tokens_output);
+ } else {
+ array_writer_.WriteChunk(char_pos1, char_pos2, char_len1, char_len2);
+ }
+ }
+ void SetSubrange1(int offset, int len) {
+ subrange_offset1_ = offset;
+ }
+ void SetSubrange2(int offset, int len) {
+ subrange_offset2_ = offset;
+ }
+
+ Handle<JSArray> GetResult() {
+ return array_writer_.GetResult();
+ }
+
+ private:
+ static const int CHUNK_LEN_LIMIT = 800;
+
+ CompareOutputArrayWriter array_writer_;
+ LineEndsWrapper line_ends1_;
+ LineEndsWrapper line_ends2_;
+ Handle<String> s1_;
+ Handle<String> s2_;
+ int subrange_offset1_;
+ int subrange_offset2_;
+};
+
+
+Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
+ Handle<String> s2) {
+ s1 = FlattenGetString(s1);
+ s2 = FlattenGetString(s2);
+
+ LineEndsWrapper line_ends1(s1);
+ LineEndsWrapper line_ends2(s2);
+
+ LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
+ TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
+
+ NarrowDownInput(&input, &output);
+
+ Comparator::CalculateDifference(&input, &output);
+
+ return output.GetResult();
+}
+
+
+static void CompileScriptForTracker(Isolate* isolate, Handle<Script> script) {
+ // TODO(635): support extensions.
+ PostponeInterruptsScope postpone(isolate);
+
+ // Build AST.
+ CompilationInfoWithZone info(script);
+ info.MarkAsGlobal();
+ // Parse and don't allow skipping lazy functions.
+ if (Parser::Parse(&info)) {
+ // Compile the code.
+ LiveEditFunctionTracker tracker(info.isolate(), info.function());
+ if (Compiler::MakeCodeForLiveEdit(&info)) {
+ ASSERT(!info.code().is_null());
+ tracker.RecordRootFunctionInfo(info.code());
+ } else {
+ info.isolate()->StackOverflow();
+ }
+ }
+}
+
+
+// Unwraps JSValue object, returning its field "value"
+static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
+ return Handle<Object>(jsValue->value(), jsValue->GetIsolate());
+}
+
+
+// Wraps any object into a OpaqueReference, that will hide the object
+// from JavaScript.
+static Handle<JSValue> WrapInJSValue(Handle<Object> object) {
+ Isolate* isolate = Isolate::Current();
+ Handle<JSFunction> constructor = isolate->opaque_reference_function();
+ Handle<JSValue> result =
+ Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
+ result->set_value(*object);
+ return result;
+}
+
+
+static Handle<SharedFunctionInfo> UnwrapSharedFunctionInfoFromJSValue(
+ Handle<JSValue> jsValue) {
+ Object* shared = jsValue->value();
+ CHECK(shared->IsSharedFunctionInfo());
+ return Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(shared));
+}
+
+
+static int GetArrayLength(Handle<JSArray> array) {
+ Object* length = array->length();
+ CHECK(length->IsSmi());
+ return Smi::cast(length)->value();
+}
+
+
+// Simple helper class that creates more or less typed structures over
+// JSArray object. This is an adhoc method of passing structures from C++
+// to JavaScript.
+template<typename S>
+class JSArrayBasedStruct {
+ public:
+ static S Create() {
+ Factory* factory = Isolate::Current()->factory();
+ Handle<JSArray> array = factory->NewJSArray(S::kSize_);
+ return S(array);
+ }
+ static S cast(Object* object) {
+ JSArray* array = JSArray::cast(object);
+ Handle<JSArray> array_handle(array);
+ return S(array_handle);
+ }
+ explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
+ }
+ Handle<JSArray> GetJSArray() {
+ return array_;
+ }
+ Isolate* isolate() const {
+ return array_->GetIsolate();
+ }
+
+ protected:
+ void SetField(int field_position, Handle<Object> value) {
+ SetElementNonStrict(array_, field_position, value);
+ }
+ void SetSmiValueField(int field_position, int value) {
+ SetElementNonStrict(array_,
+ field_position,
+ Handle<Smi>(Smi::FromInt(value), isolate()));
+ }
+ Object* GetField(int field_position) {
+ return array_->GetElementNoExceptionThrown(field_position);
+ }
+ int GetSmiValueField(int field_position) {
+ Object* res = GetField(field_position);
+ CHECK(res->IsSmi());
+ return Smi::cast(res)->value();
+ }
+
+ private:
+ Handle<JSArray> array_;
+};
+
+
+// Represents some function compilation details. This structure will be used
+// from JavaScript. It contains Code object, which is kept wrapped
+// into a BlindReference for sanitizing reasons.
+class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
+ public:
+ explicit FunctionInfoWrapper(Handle<JSArray> array)
+ : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
+ }
+ void SetInitialProperties(Handle<String> name, int start_position,
+ int end_position, int param_num,
+ int literal_count, int parent_index) {
+ HandleScope scope(isolate());
+ this->SetField(kFunctionNameOffset_, name);
+ this->SetSmiValueField(kStartPositionOffset_, start_position);
+ this->SetSmiValueField(kEndPositionOffset_, end_position);
+ this->SetSmiValueField(kParamNumOffset_, param_num);
+ this->SetSmiValueField(kLiteralNumOffset_, literal_count);
+ this->SetSmiValueField(kParentIndexOffset_, parent_index);
+ }
+ void SetFunctionCode(Handle<Code> function_code,
+ Handle<Object> code_scope_info) {
+ Handle<JSValue> code_wrapper = WrapInJSValue(function_code);
+ this->SetField(kCodeOffset_, code_wrapper);
+
+ Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info);
+ this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
+ }
+ void SetOuterScopeInfo(Handle<Object> scope_info_array) {
+ this->SetField(kOuterScopeInfoOffset_, scope_info_array);
+ }
+ void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info) {
+ Handle<JSValue> info_holder = WrapInJSValue(info);
+ this->SetField(kSharedFunctionInfoOffset_, info_holder);
+ }
+ int GetLiteralCount() {
+ return this->GetSmiValueField(kLiteralNumOffset_);
+ }
+ int GetParentIndex() {
+ return this->GetSmiValueField(kParentIndexOffset_);
+ }
+ Handle<Code> GetFunctionCode() {
+ Object* element = this->GetField(kCodeOffset_);
+ CHECK(element->IsJSValue());
+ Handle<JSValue> value_wrapper(JSValue::cast(element));
+ Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
+ CHECK(raw_result->IsCode());
+ return Handle<Code>::cast(raw_result);
+ }
+ Handle<Object> GetCodeScopeInfo() {
+ Object* element = this->GetField(kCodeScopeInfoOffset_);
+ CHECK(element->IsJSValue());
+ return UnwrapJSValue(Handle<JSValue>(JSValue::cast(element)));
+ }
+ int GetStartPosition() {
+ return this->GetSmiValueField(kStartPositionOffset_);
+ }
+ int GetEndPosition() {
+ return this->GetSmiValueField(kEndPositionOffset_);
+ }
+
+ private:
+ static const int kFunctionNameOffset_ = 0;
+ static const int kStartPositionOffset_ = 1;
+ static const int kEndPositionOffset_ = 2;
+ static const int kParamNumOffset_ = 3;
+ static const int kCodeOffset_ = 4;
+ static const int kCodeScopeInfoOffset_ = 5;
+ static const int kOuterScopeInfoOffset_ = 6;
+ static const int kParentIndexOffset_ = 7;
+ static const int kSharedFunctionInfoOffset_ = 8;
+ static const int kLiteralNumOffset_ = 9;
+ static const int kSize_ = 10;
+
+ friend class JSArrayBasedStruct<FunctionInfoWrapper>;
+};
+
+
+// Wraps SharedFunctionInfo along with some of its fields for passing it
+// back to JavaScript. SharedFunctionInfo object itself is additionally
+// wrapped into BlindReference for sanitizing reasons.
+class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
+ public:
+ static bool IsInstance(Handle<JSArray> array) {
+ return array->length() == Smi::FromInt(kSize_) &&
+ array->GetElementNoExceptionThrown(kSharedInfoOffset_)->IsJSValue();
+ }
+
+ explicit SharedInfoWrapper(Handle<JSArray> array)
+ : JSArrayBasedStruct<SharedInfoWrapper>(array) {
+ }
+
+ void SetProperties(Handle<String> name, int start_position, int end_position,
+ Handle<SharedFunctionInfo> info) {
+ HandleScope scope(isolate());
+ this->SetField(kFunctionNameOffset_, name);
+ Handle<JSValue> info_holder = WrapInJSValue(info);
+ this->SetField(kSharedInfoOffset_, info_holder);
+ this->SetSmiValueField(kStartPositionOffset_, start_position);
+ this->SetSmiValueField(kEndPositionOffset_, end_position);
+ }
+ Handle<SharedFunctionInfo> GetInfo() {
+ Object* element = this->GetField(kSharedInfoOffset_);
+ CHECK(element->IsJSValue());
+ Handle<JSValue> value_wrapper(JSValue::cast(element));
+ return UnwrapSharedFunctionInfoFromJSValue(value_wrapper);
+ }
+
+ private:
+ static const int kFunctionNameOffset_ = 0;
+ static const int kStartPositionOffset_ = 1;
+ static const int kEndPositionOffset_ = 2;
+ static const int kSharedInfoOffset_ = 3;
+ static const int kSize_ = 4;
+
+ friend class JSArrayBasedStruct<SharedInfoWrapper>;
+};
+
+
+class FunctionInfoListener {
+ public:
+ explicit FunctionInfoListener(Isolate* isolate) {
+ current_parent_index_ = -1;
+ len_ = 0;
+ result_ = isolate->factory()->NewJSArray(10);
+ }
+
+ void FunctionStarted(FunctionLiteral* fun) {
+ HandleScope scope(isolate());
+ FunctionInfoWrapper info = FunctionInfoWrapper::Create();
+ info.SetInitialProperties(fun->name(), fun->start_position(),
+ fun->end_position(), fun->parameter_count(),
+ fun->materialized_literal_count(),
+ current_parent_index_);
+ current_parent_index_ = len_;
+ SetElementNonStrict(result_, len_, info.GetJSArray());
+ len_++;
+ }
+
+ void FunctionDone() {
+ HandleScope scope(isolate());
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(
+ result_->GetElementNoExceptionThrown(current_parent_index_));
+ current_parent_index_ = info.GetParentIndex();
+ }
+
+ // Saves only function code, because for a script function we
+ // may never create a SharedFunctionInfo object.
+ void FunctionCode(Handle<Code> function_code) {
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(
+ result_->GetElementNoExceptionThrown(current_parent_index_));
+ info.SetFunctionCode(function_code,
+ Handle<Object>(isolate()->heap()->null_value(),
+ isolate()));
+ }
+
+ // Saves full information about a function: its code, its scope info
+ // and a SharedFunctionInfo object.
+ void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope,
+ Zone* zone) {
+ if (!shared->IsSharedFunctionInfo()) {
+ return;
+ }
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(
+ result_->GetElementNoExceptionThrown(current_parent_index_));
+ info.SetFunctionCode(Handle<Code>(shared->code()),
+ Handle<Object>(shared->scope_info(), isolate()));
+ info.SetSharedFunctionInfo(shared);
+
+ Handle<Object> scope_info_list(SerializeFunctionScope(scope, zone),
+ isolate());
+ info.SetOuterScopeInfo(scope_info_list);
+ }
+
+ Handle<JSArray> GetResult() { return result_; }
+
+ private:
+ Isolate* isolate() const { return result_->GetIsolate(); }
+
+ Object* SerializeFunctionScope(Scope* scope, Zone* zone) {
+ HandleScope handle_scope(isolate());
+
+ Handle<JSArray> scope_info_list = isolate()->factory()->NewJSArray(10);
+ int scope_info_length = 0;
+
+ // Saves some description of scope. It stores name and indexes of
+ // variables in the whole scope chain. Null-named slots delimit
+ // scopes of this chain.
+ Scope* outer_scope = scope->outer_scope();
+ if (outer_scope == NULL) {
+ return isolate()->heap()->undefined_value();
+ }
+ do {
+ ZoneList<Variable*> stack_list(outer_scope->StackLocalCount(), zone);
+ ZoneList<Variable*> context_list(outer_scope->ContextLocalCount(), zone);
+ outer_scope->CollectStackAndContextLocals(&stack_list, &context_list);
+ context_list.Sort(&Variable::CompareIndex);
+
+ for (int i = 0; i < context_list.length(); i++) {
+ SetElementNonStrict(scope_info_list,
+ scope_info_length,
+ context_list[i]->name());
+ scope_info_length++;
+ SetElementNonStrict(
+ scope_info_list,
+ scope_info_length,
+ Handle<Smi>(Smi::FromInt(context_list[i]->index()), isolate()));
+ scope_info_length++;
+ }
+ SetElementNonStrict(scope_info_list,
+ scope_info_length,
+ Handle<Object>(isolate()->heap()->null_value(),
+ isolate()));
+ scope_info_length++;
+
+ outer_scope = outer_scope->outer_scope();
+ } while (outer_scope != NULL);
+
+ return *scope_info_list;
+ }
+
+ Handle<JSArray> result_;
+ int len_;
+ int current_parent_index_;
+};
+
+
+JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script,
+ Handle<String> source) {
+ Isolate* isolate = Isolate::Current();
+
+ FunctionInfoListener listener(isolate);
+ Handle<Object> original_source =
+ Handle<Object>(script->source(), isolate);
+ script->set_source(*source);
+ isolate->set_active_function_info_listener(&listener);
+
+ {
+ // Creating verbose TryCatch from public API is currently the only way to
+ // force code save location. We do not use this the object directly.
+ v8::TryCatch try_catch;
+ try_catch.SetVerbose(true);
+
+ // A logical 'try' section.
+ CompileScriptForTracker(isolate, script);
+ }
+
+ // A logical 'catch' section.
+ Handle<JSObject> rethrow_exception;
+ if (isolate->has_pending_exception()) {
+ Handle<Object> exception(isolate->pending_exception()->ToObjectChecked(),
+ isolate);
+ MessageLocation message_location = isolate->GetMessageLocation();
+
+ isolate->clear_pending_message();
+ isolate->clear_pending_exception();
+
+ // If possible, copy positions from message object to exception object.
+ if (exception->IsJSObject() && !message_location.script().is_null()) {
+ rethrow_exception = Handle<JSObject>::cast(exception);
+
+ Factory* factory = isolate->factory();
+ Handle<String> start_pos_key = factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("startPosition"));
+ Handle<String> end_pos_key = factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("endPosition"));
+ Handle<String> script_obj_key = factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("scriptObject"));
+ Handle<Smi> start_pos(
+ Smi::FromInt(message_location.start_pos()), isolate);
+ Handle<Smi> end_pos(Smi::FromInt(message_location.end_pos()), isolate);
+ Handle<JSValue> script_obj = GetScriptWrapper(message_location.script());
+ JSReceiver::SetProperty(
+ rethrow_exception, start_pos_key, start_pos, NONE, kNonStrictMode);
+ JSReceiver::SetProperty(
+ rethrow_exception, end_pos_key, end_pos, NONE, kNonStrictMode);
+ JSReceiver::SetProperty(
+ rethrow_exception, script_obj_key, script_obj, NONE, kNonStrictMode);
+ }
+ }
+
+ // A logical 'finally' section.
+ isolate->set_active_function_info_listener(NULL);
+ script->set_source(*original_source);
+
+ if (rethrow_exception.is_null()) {
+ return *(listener.GetResult());
+ } else {
+ isolate->Throw(*rethrow_exception);
+ return 0;
+ }
+}
+
+
+void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
+ HandleScope scope(array->GetIsolate());
+ int len = GetArrayLength(array);
+ for (int i = 0; i < len; i++) {
+ Handle<SharedFunctionInfo> info(
+ SharedFunctionInfo::cast(array->GetElementNoExceptionThrown(i)));
+ SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create();
+ Handle<String> name_handle(String::cast(info->name()));
+ info_wrapper.SetProperties(name_handle, info->start_position(),
+ info->end_position(), info);
+ SetElementNonStrict(array, i, info_wrapper.GetJSArray());
+ }
+}
+
+
+// Visitor that finds all references to a particular code object,
+// including "CODE_TARGET" references in other code objects and replaces
+// them on the fly.
+class ReplacingVisitor : public ObjectVisitor {
+ public:
+ explicit ReplacingVisitor(Code* original, Code* substitution)
+ : original_(original), substitution_(substitution) {
+ }
+
+ virtual void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) {
+ if (*p == original_) {
+ *p = substitution_;
+ }
+ }
+ }
+
+ virtual void VisitCodeEntry(Address entry) {
+ if (Code::GetObjectFromEntryAddress(entry) == original_) {
+ Address substitution_entry = substitution_->instruction_start();
+ Memory::Address_at(entry) = substitution_entry;
+ }
+ }
+
+ virtual void VisitCodeTarget(RelocInfo* rinfo) {
+ if (RelocInfo::IsCodeTarget(rinfo->rmode()) &&
+ Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
+ Address substitution_entry = substitution_->instruction_start();
+ rinfo->set_target_address(substitution_entry);
+ }
+ }
+
+ virtual void VisitDebugTarget(RelocInfo* rinfo) {
+ VisitCodeTarget(rinfo);
+ }
+
+ private:
+ Code* original_;
+ Code* substitution_;
+};
+
+
+// Finds all references to original and replaces them with substitution.
+static void ReplaceCodeObject(Handle<Code> original,
+ Handle<Code> substitution) {
+ // Perform a full GC in order to ensure that we are not in the middle of an
+ // incremental marking phase when we are replacing the code object.
+ // Since we are not in an incremental marking phase we can write pointers
+ // to code objects (that are never in new space) without worrying about
+ // write barriers.
+ Heap* heap = original->GetHeap();
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask,
+ "liveedit.cc ReplaceCodeObject");
+
+ ASSERT(!heap->InNewSpace(*substitution));
+
+ DisallowHeapAllocation no_allocation;
+
+ ReplacingVisitor visitor(*original, *substitution);
+
+ // Iterate over all roots. Stack frames may have pointer into original code,
+ // so temporary replace the pointers with offset numbers
+ // in prologue/epilogue.
+ heap->IterateRoots(&visitor, VISIT_ALL);
+
+ // Now iterate over all pointers of all objects, including code_target
+ // implicit pointers.
+ HeapIterator iterator(heap);
+ for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
+ obj->Iterate(&visitor);
+ }
+}
+
+
+// Patch function literals.
+// Name 'literals' is a misnomer. Rather it's a cache for complex object
+// boilerplates and for a native context. We must clean cached values.
+// Additionally we may need to allocate a new array if number of literals
+// changed.
+class LiteralFixer {
+ public:
+ static void PatchLiterals(FunctionInfoWrapper* compile_info_wrapper,
+ Handle<SharedFunctionInfo> shared_info,
+ Isolate* isolate) {
+ int new_literal_count = compile_info_wrapper->GetLiteralCount();
+ if (new_literal_count > 0) {
+ new_literal_count += JSFunction::kLiteralsPrefixSize;
+ }
+ int old_literal_count = shared_info->num_literals();
+
+ if (old_literal_count == new_literal_count) {
+ // If literal count didn't change, simply go over all functions
+ // and clear literal arrays.
+ ClearValuesVisitor visitor;
+ IterateJSFunctions(*shared_info, &visitor);
+ } else {
+ // When literal count changes, we have to create new array instances.
+ // Since we cannot create instances when iterating heap, we should first
+ // collect all functions and fix their literal arrays.
+ Handle<FixedArray> function_instances =
+ CollectJSFunctions(shared_info, isolate);
+ for (int i = 0; i < function_instances->length(); i++) {
+ Handle<JSFunction> fun(JSFunction::cast(function_instances->get(i)));
+ Handle<FixedArray> old_literals(fun->literals());
+ Handle<FixedArray> new_literals =
+ isolate->factory()->NewFixedArray(new_literal_count);
+ if (new_literal_count > 0) {
+ Handle<Context> native_context;
+ if (old_literals->length() >
+ JSFunction::kLiteralNativeContextIndex) {
+ native_context = Handle<Context>(
+ JSFunction::NativeContextFromLiterals(fun->literals()));
+ } else {
+ native_context = Handle<Context>(fun->context()->native_context());
+ }
+ new_literals->set(JSFunction::kLiteralNativeContextIndex,
+ *native_context);
+ }
+ fun->set_literals(*new_literals);
+ }
+
+ shared_info->set_num_literals(new_literal_count);
+ }
+ }
+
+ private:
+ // Iterates all function instances in the HEAP that refers to the
+ // provided shared_info.
+ template<typename Visitor>
+ static void IterateJSFunctions(SharedFunctionInfo* shared_info,
+ Visitor* visitor) {
+ DisallowHeapAllocation no_allocation;
+
+ HeapIterator iterator(shared_info->GetHeap());
+ for (HeapObject* obj = iterator.next(); obj != NULL;
+ obj = iterator.next()) {
+ if (obj->IsJSFunction()) {
+ JSFunction* function = JSFunction::cast(obj);
+ if (function->shared() == shared_info) {
+ visitor->visit(function);
+ }
+ }
+ }
+ }
+
+ // Finds all instances of JSFunction that refers to the provided shared_info
+ // and returns array with them.
+ static Handle<FixedArray> CollectJSFunctions(
+ Handle<SharedFunctionInfo> shared_info, Isolate* isolate) {
+ CountVisitor count_visitor;
+ count_visitor.count = 0;
+ IterateJSFunctions(*shared_info, &count_visitor);
+ int size = count_visitor.count;
+
+ Handle<FixedArray> result = isolate->factory()->NewFixedArray(size);
+ if (size > 0) {
+ CollectVisitor collect_visitor(result);
+ IterateJSFunctions(*shared_info, &collect_visitor);
+ }
+ return result;
+ }
+
+ class ClearValuesVisitor {
+ public:
+ void visit(JSFunction* fun) {
+ FixedArray* literals = fun->literals();
+ int len = literals->length();
+ for (int j = JSFunction::kLiteralsPrefixSize; j < len; j++) {
+ literals->set_undefined(j);
+ }
+ }
+ };
+
+ class CountVisitor {
+ public:
+ void visit(JSFunction* fun) {
+ count++;
+ }
+ int count;
+ };
+
+ class CollectVisitor {
+ public:
+ explicit CollectVisitor(Handle<FixedArray> output)
+ : m_output(output), m_pos(0) {}
+
+ void visit(JSFunction* fun) {
+ m_output->set(m_pos, fun);
+ m_pos++;
+ }
+ private:
+ Handle<FixedArray> m_output;
+ int m_pos;
+ };
+};
+
+
+// Check whether the code is natural function code (not a lazy-compile stub
+// code).
+static bool IsJSFunctionCode(Code* code) {
+ return code->kind() == Code::FUNCTION;
+}
+
+
+// Returns true if an instance of candidate were inlined into function's code.
+static bool IsInlined(JSFunction* function, SharedFunctionInfo* candidate) {
+ DisallowHeapAllocation no_gc;
+
+ if (function->code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
+
+ DeoptimizationInputData* data =
+ DeoptimizationInputData::cast(function->code()->deoptimization_data());
+
+ if (data == HEAP->empty_fixed_array()) return false;
+
+ FixedArray* literals = data->LiteralArray();
+
+ int inlined_count = data->InlinedFunctionCount()->value();
+ for (int i = 0; i < inlined_count; ++i) {
+ JSFunction* inlined = JSFunction::cast(literals->get(i));
+ if (inlined->shared() == candidate) return true;
+ }
+
+ return false;
+}
+
+
+class DependentFunctionFilter : public OptimizedFunctionFilter {
+ public:
+ explicit DependentFunctionFilter(
+ SharedFunctionInfo* function_info)
+ : function_info_(function_info) {}
+
+ virtual bool TakeFunction(JSFunction* function) {
+ return (function->shared() == function_info_ ||
+ IsInlined(function, function_info_));
+ }
+
+ private:
+ SharedFunctionInfo* function_info_;
+};
+
+
+static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
+ DisallowHeapAllocation no_allocation;
+
+ DependentFunctionFilter filter(function_info);
+ Deoptimizer::DeoptimizeAllFunctionsWith(function_info->GetIsolate(), &filter);
+}
+
+
+MaybeObject* LiveEdit::ReplaceFunctionCode(
+ Handle<JSArray> new_compile_info_array,
+ Handle<JSArray> shared_info_array) {
+ Isolate* isolate = Isolate::Current();
+ HandleScope scope(isolate);
+
+ if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
+ return isolate->ThrowIllegalOperation();
+ }
+
+ FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+
+ Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
+
+ isolate->heap()->EnsureHeapIsIterable();
+
+ if (IsJSFunctionCode(shared_info->code())) {
+ Handle<Code> code = compile_info_wrapper.GetFunctionCode();
+ ReplaceCodeObject(Handle<Code>(shared_info->code()), code);
+ Handle<Object> code_scope_info = compile_info_wrapper.GetCodeScopeInfo();
+ if (code_scope_info->IsFixedArray()) {
+ shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info));
+ }
+ shared_info->DisableOptimization(kLiveEdit);
+ }
+
+ if (shared_info->debug_info()->IsDebugInfo()) {
+ Handle<DebugInfo> debug_info(DebugInfo::cast(shared_info->debug_info()));
+ Handle<Code> new_original_code =
+ isolate->factory()->CopyCode(compile_info_wrapper.GetFunctionCode());
+ debug_info->set_original_code(*new_original_code);
+ }
+
+ int start_position = compile_info_wrapper.GetStartPosition();
+ int end_position = compile_info_wrapper.GetEndPosition();
+ shared_info->set_start_position(start_position);
+ shared_info->set_end_position(end_position);
+
+ LiteralFixer::PatchLiterals(&compile_info_wrapper, shared_info, isolate);
+
+ shared_info->set_construct_stub(
+ isolate->builtins()->builtin(Builtins::kJSConstructStubGeneric));
+
+ DeoptimizeDependentFunctions(*shared_info);
+ isolate->compilation_cache()->Remove(shared_info);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+MaybeObject* LiveEdit::FunctionSourceUpdated(
+ Handle<JSArray> shared_info_array) {
+ Isolate* isolate = shared_info_array->GetIsolate();
+ HandleScope scope(isolate);
+
+ if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
+ return isolate->ThrowIllegalOperation();
+ }
+
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+ Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
+
+ DeoptimizeDependentFunctions(*shared_info);
+ isolate->compilation_cache()->Remove(shared_info);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
+ Handle<Object> script_handle) {
+ Handle<SharedFunctionInfo> shared_info =
+ UnwrapSharedFunctionInfoFromJSValue(function_wrapper);
+ CHECK(script_handle->IsScript() || script_handle->IsUndefined());
+ shared_info->set_script(*script_handle);
+
+ Isolate::Current()->compilation_cache()->Remove(shared_info);
+}
+
+
+// For a script text change (defined as position_change_array), translates
+// position in unchanged text to position in changed text.
+// Text change is a set of non-overlapping regions in text, that have changed
+// their contents and length. It is specified as array of groups of 3 numbers:
+// (change_begin, change_end, change_end_new_position).
+// Each group describes a change in text; groups are sorted by change_begin.
+// Only position in text beyond any changes may be successfully translated.
+// If a positions is inside some region that changed, result is currently
+// undefined.
+static int TranslatePosition(int original_position,
+ Handle<JSArray> position_change_array) {
+ int position_diff = 0;
+ int array_len = GetArrayLength(position_change_array);
+ // TODO(635): binary search may be used here
+ for (int i = 0; i < array_len; i += 3) {
+ Object* element = position_change_array->GetElementNoExceptionThrown(i);
+ CHECK(element->IsSmi());
+ int chunk_start = Smi::cast(element)->value();
+ if (original_position < chunk_start) {
+ break;
+ }
+ element = position_change_array->GetElementNoExceptionThrown(i + 1);
+ CHECK(element->IsSmi());
+ int chunk_end = Smi::cast(element)->value();
+ // Position mustn't be inside a chunk.
+ ASSERT(original_position >= chunk_end);
+ element = position_change_array->GetElementNoExceptionThrown(i + 2);
+ CHECK(element->IsSmi());
+ int chunk_changed_end = Smi::cast(element)->value();
+ position_diff = chunk_changed_end - chunk_end;
+ }
+
+ return original_position + position_diff;
+}
+
+
+// Auto-growing buffer for writing relocation info code section. This buffer
+// is a simplified version of buffer from Assembler. Unlike Assembler, this
+// class is platform-independent and it works without dealing with instructions.
+// As specified by RelocInfo format, the buffer is filled in reversed order:
+// from upper to lower addresses.
+// It uses NewArray/DeleteArray for memory management.
+class RelocInfoBuffer {
+ public:
+ RelocInfoBuffer(int buffer_initial_capicity, byte* pc) {
+ buffer_size_ = buffer_initial_capicity + kBufferGap;
+ buffer_ = NewArray<byte>(buffer_size_);
+
+ reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc);
+ }
+ ~RelocInfoBuffer() {
+ DeleteArray(buffer_);
+ }
+
+ // As specified by RelocInfo format, the buffer is filled in reversed order:
+ // from upper to lower addresses.
+ void Write(const RelocInfo* rinfo) {
+ if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) {
+ Grow();
+ }
+ reloc_info_writer_.Write(rinfo);
+ }
+
+ Vector<byte> GetResult() {
+ // Return the bytes from pos up to end of buffer.
+ int result_size =
+ static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer_.pos());
+ return Vector<byte>(reloc_info_writer_.pos(), result_size);
+ }
+
+ private:
+ void Grow() {
+ // Compute new buffer size.
+ int new_buffer_size;
+ if (buffer_size_ < 2 * KB) {
+ new_buffer_size = 4 * KB;
+ } else {
+ new_buffer_size = 2 * buffer_size_;
+ }
+ // Some internal data structures overflow for very large buffers,
+ // they must ensure that kMaximalBufferSize is not too large.
+ if (new_buffer_size > kMaximalBufferSize) {
+ V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer");
+ }
+
+ // Set up new buffer.
+ byte* new_buffer = NewArray<byte>(new_buffer_size);
+
+ // Copy the data.
+ int curently_used_size =
+ static_cast<int>(buffer_ + buffer_size_ - reloc_info_writer_.pos());
+ OS::MemMove(new_buffer + new_buffer_size - curently_used_size,
+ reloc_info_writer_.pos(), curently_used_size);
+
+ reloc_info_writer_.Reposition(
+ new_buffer + new_buffer_size - curently_used_size,
+ reloc_info_writer_.last_pc());
+
+ DeleteArray(buffer_);
+ buffer_ = new_buffer;
+ buffer_size_ = new_buffer_size;
+ }
+
+ RelocInfoWriter reloc_info_writer_;
+ byte* buffer_;
+ int buffer_size_;
+
+ static const int kBufferGap = RelocInfoWriter::kMaxSize;
+ static const int kMaximalBufferSize = 512*MB;
+};
+
+
+// Patch positions in code (changes relocation info section) and possibly
+// returns new instance of code.
+static Handle<Code> PatchPositionsInCode(
+ Handle<Code> code,
+ Handle<JSArray> position_change_array) {
+ Isolate* isolate = code->GetIsolate();
+
+ RelocInfoBuffer buffer_writer(code->relocation_size(),
+ code->instruction_start());
+
+ {
+ DisallowHeapAllocation no_allocation;
+ for (RelocIterator it(*code); !it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ if (RelocInfo::IsPosition(rinfo->rmode())) {
+ int position = static_cast<int>(rinfo->data());
+ int new_position = TranslatePosition(position,
+ position_change_array);
+ if (position != new_position) {
+ RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position, NULL);
+ buffer_writer.Write(&info_copy);
+ continue;
+ }
+ }
+ if (RelocInfo::IsRealRelocMode(rinfo->rmode())) {
+ buffer_writer.Write(it.rinfo());
+ }
+ }
+ }
+
+ Vector<byte> buffer = buffer_writer.GetResult();
+
+ if (buffer.length() == code->relocation_size()) {
+ // Simply patch relocation area of code.
+ OS::MemCopy(code->relocation_start(), buffer.start(), buffer.length());
+ return code;
+ } else {
+ // Relocation info section now has different size. We cannot simply
+ // rewrite it inside code object. Instead we have to create a new
+ // code object.
+ Handle<Code> result(isolate->factory()->CopyCode(code, buffer));
+ return result;
+ }
+}
+
+
+MaybeObject* LiveEdit::PatchFunctionPositions(
+ Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array) {
+ if (!SharedInfoWrapper::IsInstance(shared_info_array)) {
+ return Isolate::Current()->ThrowIllegalOperation();
+ }
+
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+ Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
+
+ int old_function_start = info->start_position();
+ int new_function_start = TranslatePosition(old_function_start,
+ position_change_array);
+ int new_function_end = TranslatePosition(info->end_position(),
+ position_change_array);
+ int new_function_token_pos =
+ TranslatePosition(info->function_token_position(), position_change_array);
+
+ info->set_start_position(new_function_start);
+ 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()),
+ position_change_array);
+ if (*patched_code != info->code()) {
+ // Replace all references to the code across the heap. In particular,
+ // some stubs may refer to this code and this code may be being executed
+ // on stack (it is safe to substitute the code object on stack, because
+ // we only change the structure of rinfo and leave instructions
+ // untouched).
+ ReplaceCodeObject(Handle<Code>(info->code()), patched_code);
+ }
+ }
+
+ return HEAP->undefined_value();
+}
+
+
+static Handle<Script> CreateScriptCopy(Handle<Script> original) {
+ Isolate* isolate = original->GetIsolate();
+
+ Handle<String> original_source(String::cast(original->source()));
+ Handle<Script> copy = isolate->factory()->NewScript(original_source);
+
+ copy->set_name(original->name());
+ copy->set_line_offset(original->line_offset());
+ copy->set_column_offset(original->column_offset());
+ copy->set_data(original->data());
+ copy->set_type(original->type());
+ copy->set_context_data(original->context_data());
+ copy->set_eval_from_shared(original->eval_from_shared());
+ copy->set_eval_from_instructions_offset(
+ original->eval_from_instructions_offset());
+
+ // Copy all the flags, but clear compilation state.
+ copy->set_flags(original->flags());
+ copy->set_compilation_state(Script::COMPILATION_STATE_INITIAL);
+
+ return copy;
+}
+
+
+Object* LiveEdit::ChangeScriptSource(Handle<Script> original_script,
+ Handle<String> new_source,
+ Handle<Object> old_script_name) {
+ Isolate* isolate = original_script->GetIsolate();
+ Handle<Object> old_script_object;
+ if (old_script_name->IsString()) {
+ Handle<Script> old_script = CreateScriptCopy(original_script);
+ old_script->set_name(String::cast(*old_script_name));
+ old_script_object = old_script;
+ isolate->debugger()->OnAfterCompile(
+ old_script, Debugger::SEND_WHEN_DEBUGGING);
+ } else {
+ old_script_object = isolate->factory()->null_value();
+ }
+
+ original_script->set_source(*new_source);
+
+ // Drop line ends so that they will be recalculated.
+ original_script->set_line_ends(HEAP->undefined_value());
+
+ return *old_script_object;
+}
+
+
+
+void LiveEdit::ReplaceRefToNestedFunction(
+ Handle<JSValue> parent_function_wrapper,
+ Handle<JSValue> orig_function_wrapper,
+ Handle<JSValue> subst_function_wrapper) {
+
+ Handle<SharedFunctionInfo> parent_shared =
+ UnwrapSharedFunctionInfoFromJSValue(parent_function_wrapper);
+ Handle<SharedFunctionInfo> orig_shared =
+ UnwrapSharedFunctionInfoFromJSValue(orig_function_wrapper);
+ Handle<SharedFunctionInfo> subst_shared =
+ UnwrapSharedFunctionInfoFromJSValue(subst_function_wrapper);
+
+ for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
+ if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
+ if (it.rinfo()->target_object() == *orig_shared) {
+ it.rinfo()->set_target_object(*subst_shared);
+ }
+ }
+ }
+}
+
+
+// Check an activation against list of functions. If there is a function
+// that matches, its status in result array is changed to status argument value.
+static bool CheckActivation(Handle<JSArray> shared_info_array,
+ Handle<JSArray> result,
+ StackFrame* frame,
+ LiveEdit::FunctionPatchabilityStatus status) {
+ if (!frame->is_java_script()) return false;
+
+ Handle<JSFunction> function(JavaScriptFrame::cast(frame)->function());
+
+ Isolate* isolate = shared_info_array->GetIsolate();
+ int len = GetArrayLength(shared_info_array);
+ for (int i = 0; i < len; i++) {
+ Object* element = shared_info_array->GetElementNoExceptionThrown(i);
+ CHECK(element->IsJSValue());
+ Handle<JSValue> jsvalue(JSValue::cast(element));
+ Handle<SharedFunctionInfo> shared =
+ UnwrapSharedFunctionInfoFromJSValue(jsvalue);
+
+ if (function->shared() == *shared || IsInlined(*function, *shared)) {
+ SetElementNonStrict(result, i, Handle<Smi>(Smi::FromInt(status),
+ isolate));
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Iterates over handler chain and removes all elements that are inside
+// frames being dropped.
+static bool FixTryCatchHandler(StackFrame* top_frame,
+ StackFrame* bottom_frame) {
+ Address* pointer_address =
+ &Memory::Address_at(Isolate::Current()->get_address_from_id(
+ Isolate::kHandlerAddress));
+
+ while (*pointer_address < top_frame->sp()) {
+ pointer_address = &Memory::Address_at(*pointer_address);
+ }
+ Address* above_frame_address = pointer_address;
+ while (*pointer_address < bottom_frame->fp()) {
+ pointer_address = &Memory::Address_at(*pointer_address);
+ }
+ bool change = *above_frame_address != *pointer_address;
+ *above_frame_address = *pointer_address;
+ return change;
+}
+
+
+// Removes specified range of frames from stack. There may be 1 or more
+// frames in range. Anyway the bottom frame is restarted rather than dropped,
+// and therefore has to be a JavaScript frame.
+// Returns error message or NULL.
+static const char* DropFrames(Vector<StackFrame*> frames,
+ int top_frame_index,
+ int bottom_js_frame_index,
+ Debug::FrameDropMode* mode,
+ Object*** restarter_frame_function_pointer) {
+ if (!Debug::kFrameDropperSupported) {
+ return "Stack manipulations are not supported in this architecture.";
+ }
+
+ StackFrame* pre_top_frame = frames[top_frame_index - 1];
+ StackFrame* top_frame = frames[top_frame_index];
+ StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
+
+ ASSERT(bottom_js_frame->is_java_script());
+
+ // Check the nature of the top frame.
+ Isolate* isolate = Isolate::Current();
+ Code* pre_top_frame_code = pre_top_frame->LookupCode();
+ bool frame_has_padding;
+ if (pre_top_frame_code->is_inline_cache_stub() &&
+ pre_top_frame_code->is_debug_break()) {
+ // OK, we can drop inline cache calls.
+ *mode = Debug::FRAME_DROPPED_IN_IC_CALL;
+ frame_has_padding = Debug::FramePaddingLayout::kIsSupported;
+ } else if (pre_top_frame_code ==
+ isolate->debug()->debug_break_slot()) {
+ // OK, we can drop debug break slot.
+ *mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
+ frame_has_padding = Debug::FramePaddingLayout::kIsSupported;
+ } else if (pre_top_frame_code ==
+ isolate->builtins()->builtin(
+ Builtins::kFrameDropper_LiveEdit)) {
+ // OK, we can drop our own code.
+ pre_top_frame = frames[top_frame_index - 2];
+ top_frame = frames[top_frame_index - 1];
+ *mode = Debug::CURRENTLY_SET_MODE;
+ frame_has_padding = false;
+ } else if (pre_top_frame_code ==
+ isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
+ *mode = Debug::FRAME_DROPPED_IN_RETURN_CALL;
+ frame_has_padding = Debug::FramePaddingLayout::kIsSupported;
+ } else if (pre_top_frame_code->kind() == Code::STUB &&
+ pre_top_frame_code->major_key() == CodeStub::CEntry) {
+ // Entry from our unit tests on 'debugger' statement.
+ // It's fine, we support this case.
+ *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
+ // We don't have a padding from 'debugger' statement call.
+ // Here the stub is CEntry, it's not debug-only and can't be padded.
+ // If anyone would complain, a proxy padded stub could be added.
+ frame_has_padding = false;
+ } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) {
+ // This must be adaptor that remain from the frame dropping that
+ // is still on stack. A frame dropper frame must be above it.
+ ASSERT(frames[top_frame_index - 2]->LookupCode() ==
+ isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit));
+ pre_top_frame = frames[top_frame_index - 3];
+ top_frame = frames[top_frame_index - 2];
+ *mode = Debug::CURRENTLY_SET_MODE;
+ frame_has_padding = false;
+ } else {
+ return "Unknown structure of stack above changing function";
+ }
+
+ Address unused_stack_top = top_frame->sp();
+ Address unused_stack_bottom = bottom_js_frame->fp()
+ - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame.
+ + kPointerSize; // Bigger address end is exclusive.
+
+ Address* top_frame_pc_address = top_frame->pc_address();
+
+ // top_frame may be damaged below this point. Do not used it.
+ ASSERT(!(top_frame = NULL));
+
+ if (unused_stack_top > unused_stack_bottom) {
+ if (frame_has_padding) {
+ int shortage_bytes =
+ static_cast<int>(unused_stack_top - unused_stack_bottom);
+
+ Address padding_start = pre_top_frame->fp() -
+ Debug::FramePaddingLayout::kFrameBaseSize * kPointerSize;
+
+ Address padding_pointer = padding_start;
+ Smi* padding_object =
+ Smi::FromInt(Debug::FramePaddingLayout::kPaddingValue);
+ while (Memory::Object_at(padding_pointer) == padding_object) {
+ padding_pointer -= kPointerSize;
+ }
+ int padding_counter =
+ Smi::cast(Memory::Object_at(padding_pointer))->value();
+ if (padding_counter * kPointerSize < shortage_bytes) {
+ return "Not enough space for frame dropper frame "
+ "(even with padding frame)";
+ }
+ Memory::Object_at(padding_pointer) =
+ Smi::FromInt(padding_counter - shortage_bytes / kPointerSize);
+
+ StackFrame* pre_pre_frame = frames[top_frame_index - 2];
+
+ OS::MemMove(padding_start + kPointerSize - shortage_bytes,
+ padding_start + kPointerSize,
+ Debug::FramePaddingLayout::kFrameBaseSize * kPointerSize);
+
+ pre_top_frame->UpdateFp(pre_top_frame->fp() - shortage_bytes);
+ pre_pre_frame->SetCallerFp(pre_top_frame->fp());
+ unused_stack_top -= shortage_bytes;
+
+ STATIC_ASSERT(sizeof(Address) == kPointerSize);
+ top_frame_pc_address -= shortage_bytes / kPointerSize;
+ } else {
+ return "Not enough space for frame dropper frame";
+ }
+ }
+
+ // Committing now. After this point we should return only NULL value.
+
+ FixTryCatchHandler(pre_top_frame, bottom_js_frame);
+ // Make sure FixTryCatchHandler is idempotent.
+ ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame));
+
+ Handle<Code> code = Isolate::Current()->builtins()->FrameDropper_LiveEdit();
+ *top_frame_pc_address = code->entry();
+ pre_top_frame->SetCallerFp(bottom_js_frame->fp());
+
+ *restarter_frame_function_pointer =
+ Debug::SetUpFrameDropperFrame(bottom_js_frame, code);
+
+ ASSERT((**restarter_frame_function_pointer)->IsJSFunction());
+
+ for (Address a = unused_stack_top;
+ a < unused_stack_bottom;
+ a += kPointerSize) {
+ Memory::Object_at(a) = Smi::FromInt(0);
+ }
+
+ return NULL;
+}
+
+
+static bool IsDropableFrame(StackFrame* frame) {
+ return !frame->is_exit();
+}
+
+
+// Describes a set of call frames that execute any of listed functions.
+// Finding no such frames does not mean error.
+class MultipleFunctionTarget {
+ public:
+ MultipleFunctionTarget(Handle<JSArray> shared_info_array,
+ Handle<JSArray> result)
+ : m_shared_info_array(shared_info_array),
+ m_result(result) {}
+ bool MatchActivation(StackFrame* frame,
+ LiveEdit::FunctionPatchabilityStatus status) {
+ return CheckActivation(m_shared_info_array, m_result, frame, status);
+ }
+ const char* GetNotFoundMessage() {
+ return NULL;
+ }
+ private:
+ Handle<JSArray> m_shared_info_array;
+ Handle<JSArray> m_result;
+};
+
+
+// Drops all call frame matched by target and all frames above them.
+template<typename TARGET>
+static const char* DropActivationsInActiveThreadImpl(
+ TARGET& target, bool do_drop) {
+ Isolate* isolate = Isolate::Current();
+ Debug* debug = isolate->debug();
+ Zone zone(isolate);
+ Vector<StackFrame*> frames = CreateStackMap(isolate, &zone);
+
+
+ int top_frame_index = -1;
+ int frame_index = 0;
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (frame->id() == debug->break_frame_id()) {
+ top_frame_index = frame_index;
+ break;
+ }
+ if (target.MatchActivation(
+ frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
+ // We are still above break_frame. It is not a target frame,
+ // it is a problem.
+ return "Debugger mark-up on stack is not found";
+ }
+ }
+
+ if (top_frame_index == -1) {
+ // We haven't found break frame, but no function is blocking us anyway.
+ return target.GetNotFoundMessage();
+ }
+
+ bool target_frame_found = false;
+ int bottom_js_frame_index = top_frame_index;
+ bool c_code_found = false;
+
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (!IsDropableFrame(frame)) {
+ c_code_found = true;
+ break;
+ }
+ if (target.MatchActivation(
+ frame, LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
+ target_frame_found = true;
+ bottom_js_frame_index = frame_index;
+ }
+ }
+
+ if (c_code_found) {
+ // There is a C frames on stack. Check that there are no target frames
+ // below them.
+ for (; frame_index < frames.length(); frame_index++) {
+ StackFrame* frame = frames[frame_index];
+ if (frame->is_java_script()) {
+ if (target.MatchActivation(
+ frame, LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) {
+ // Cannot drop frame under C frames.
+ return NULL;
+ }
+ }
+ }
+ }
+
+ if (!do_drop) {
+ // We are in check-only mode.
+ return NULL;
+ }
+
+ if (!target_frame_found) {
+ // Nothing to drop.
+ return target.GetNotFoundMessage();
+ }
+
+ Debug::FrameDropMode drop_mode = Debug::FRAMES_UNTOUCHED;
+ Object** restarter_frame_function_pointer = NULL;
+ const char* error_message = DropFrames(frames, top_frame_index,
+ bottom_js_frame_index, &drop_mode,
+ &restarter_frame_function_pointer);
+
+ if (error_message != NULL) {
+ return error_message;
+ }
+
+ // Adjust break_frame after some frames has been dropped.
+ StackFrame::Id new_id = StackFrame::NO_ID;
+ for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) {
+ if (frames[i]->type() == StackFrame::JAVA_SCRIPT) {
+ new_id = frames[i]->id();
+ break;
+ }
+ }
+ debug->FramesHaveBeenDropped(new_id, drop_mode,
+ restarter_frame_function_pointer);
+ return NULL;
+}
+
+
+// Fills result array with statuses of functions. Modifies the stack
+// removing all listed function if possible and if do_drop is true.
+static const char* DropActivationsInActiveThread(
+ Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
+ MultipleFunctionTarget target(shared_info_array, result);
+
+ const char* message =
+ DropActivationsInActiveThreadImpl(target, do_drop);
+ if (message) {
+ return message;
+ }
+
+ Isolate* isolate = shared_info_array->GetIsolate();
+ int array_len = GetArrayLength(shared_info_array);
+
+ // Replace "blocked on active" with "replaced on active" status.
+ for (int i = 0; i < array_len; i++) {
+ if (result->GetElement(i) ==
+ Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) {
+ Handle<Object> replaced(
+ Smi::FromInt(LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK), isolate);
+ SetElementNonStrict(result, i, replaced);
+ }
+ }
+ return NULL;
+}
+
+
+class InactiveThreadActivationsChecker : public ThreadVisitor {
+ public:
+ InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array,
+ Handle<JSArray> result)
+ : shared_info_array_(shared_info_array), result_(result),
+ has_blocked_functions_(false) {
+ }
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+ has_blocked_functions_ |= CheckActivation(
+ shared_info_array_, result_, it.frame(),
+ LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK);
+ }
+ }
+ bool HasBlockedFunctions() {
+ return has_blocked_functions_;
+ }
+
+ private:
+ Handle<JSArray> shared_info_array_;
+ Handle<JSArray> result_;
+ bool has_blocked_functions_;
+};
+
+
+Handle<JSArray> LiveEdit::CheckAndDropActivations(
+ Handle<JSArray> shared_info_array, bool do_drop) {
+ Isolate* isolate = shared_info_array->GetIsolate();
+ int len = GetArrayLength(shared_info_array);
+
+ Handle<JSArray> result = isolate->factory()->NewJSArray(len);
+
+ // Fill the default values.
+ for (int i = 0; i < len; i++) {
+ SetElementNonStrict(
+ result,
+ i,
+ Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH), isolate));
+ }
+
+
+ // First check inactive threads. Fail if some functions are blocked there.
+ InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array,
+ result);
+ Isolate::Current()->thread_manager()->IterateArchivedThreads(
+ &inactive_threads_checker);
+ if (inactive_threads_checker.HasBlockedFunctions()) {
+ return result;
+ }
+
+ // Try to drop activations from the current stack.
+ const char* error_message =
+ DropActivationsInActiveThread(shared_info_array, result, do_drop);
+ if (error_message != NULL) {
+ // Add error message as an array extra element.
+ Vector<const char> vector_message(error_message, StrLength(error_message));
+ Handle<String> str = isolate->factory()->NewStringFromAscii(vector_message);
+ SetElementNonStrict(result, len, str);
+ }
+ return result;
+}
+
+
+// Describes a single callframe a target. Not finding this frame
+// means an error.
+class SingleFrameTarget {
+ public:
+ explicit SingleFrameTarget(JavaScriptFrame* frame)
+ : m_frame(frame),
+ m_saved_status(LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH) {}
+
+ bool MatchActivation(StackFrame* frame,
+ LiveEdit::FunctionPatchabilityStatus status) {
+ if (frame->fp() == m_frame->fp()) {
+ m_saved_status = status;
+ return true;
+ }
+ return false;
+ }
+ const char* GetNotFoundMessage() {
+ return "Failed to found requested frame";
+ }
+ LiveEdit::FunctionPatchabilityStatus saved_status() {
+ return m_saved_status;
+ }
+ private:
+ JavaScriptFrame* m_frame;
+ LiveEdit::FunctionPatchabilityStatus m_saved_status;
+};
+
+
+// Finds a drops required frame and all frames above.
+// Returns error message or NULL.
+const char* LiveEdit::RestartFrame(JavaScriptFrame* frame) {
+ SingleFrameTarget target(frame);
+
+ const char* result = DropActivationsInActiveThreadImpl(target, true);
+ if (result != NULL) {
+ return result;
+ }
+ if (target.saved_status() == LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE) {
+ return "Function is blocked under native code";
+ }
+ return NULL;
+}
+
+
+LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
+ FunctionLiteral* fun)
+ : isolate_(isolate) {
+ if (isolate_->active_function_info_listener() != NULL) {
+ isolate_->active_function_info_listener()->FunctionStarted(fun);
+ }
+}
+
+
+LiveEditFunctionTracker::~LiveEditFunctionTracker() {
+ if (isolate_->active_function_info_listener() != NULL) {
+ isolate_->active_function_info_listener()->FunctionDone();
+ }
+}
+
+
+void LiveEditFunctionTracker::RecordFunctionInfo(
+ Handle<SharedFunctionInfo> info, FunctionLiteral* lit,
+ Zone* zone) {
+ if (isolate_->active_function_info_listener() != NULL) {
+ isolate_->active_function_info_listener()->FunctionInfo(info, lit->scope(),
+ zone);
+ }
+}
+
+
+void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
+ isolate_->active_function_info_listener()->FunctionCode(code);
+}
+
+
+bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
+ return isolate->active_function_info_listener() != NULL;
+}
+
+
+#else // ENABLE_DEBUGGER_SUPPORT
+
+// This ifdef-else-endif section provides working or stub implementation of
+// LiveEditFunctionTracker.
+LiveEditFunctionTracker::LiveEditFunctionTracker(Isolate* isolate,
+ FunctionLiteral* fun) {
+}
+
+
+LiveEditFunctionTracker::~LiveEditFunctionTracker() {
+}
+
+
+void LiveEditFunctionTracker::RecordFunctionInfo(
+ Handle<SharedFunctionInfo> info, FunctionLiteral* lit,
+ Zone* zone) {
+}
+
+
+void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
+}
+
+
+bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
+ return false;
+}
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/liveedit.h b/chromium/v8/src/liveedit.h
new file mode 100644
index 00000000000..0efbb95cc0b
--- /dev/null
+++ b/chromium/v8/src/liveedit.h
@@ -0,0 +1,183 @@
+// Copyright 2012 the V8 project authors. 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_LIVEEDIT_H_
+#define V8_LIVEEDIT_H_
+
+
+
+// Live Edit feature implementation.
+// User should be able to change script on already running VM. This feature
+// matches hot swap features in other frameworks.
+//
+// The basic use-case is when user spots some mistake in function body
+// from debugger and wishes to change the algorithm without restart.
+//
+// A single change always has a form of a simple replacement (in pseudo-code):
+// script.source[positions, positions+length] = new_string;
+// Implementation first determines, which function's body includes this
+// change area. Then both old and new versions of script are fully compiled
+// in order to analyze, whether the function changed its outer scope
+// expectations (or number of parameters). If it didn't, function's code is
+// patched with a newly compiled code. If it did change, enclosing function
+// gets patched. All inner functions are left untouched, whatever happened
+// to them in a new script version. However, new version of code will
+// instantiate newly compiled functions.
+
+
+#include "allocation.h"
+#include "compiler.h"
+
+namespace v8 {
+namespace internal {
+
+// This class collects some specific information on structure of functions
+// in a particular script. It gets called from compiler all the time, but
+// actually records any data only when liveedit operation is in process;
+// in any other time this class is very cheap.
+//
+// The primary interest of the Tracker is to record function scope structures
+// in order to analyze whether function code maybe safely patched (with new
+// code successfully reading existing data from function scopes). The Tracker
+// also collects compiled function codes.
+class LiveEditFunctionTracker {
+ public:
+ explicit LiveEditFunctionTracker(Isolate* isolate, FunctionLiteral* fun);
+ ~LiveEditFunctionTracker();
+ void RecordFunctionInfo(Handle<SharedFunctionInfo> info,
+ FunctionLiteral* lit, Zone* zone);
+ void RecordRootFunctionInfo(Handle<Code> code);
+
+ static bool IsActive(Isolate* isolate);
+
+ private:
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Isolate* isolate_;
+#endif
+};
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+class LiveEdit : AllStatic {
+ public:
+ static JSArray* GatherCompileInfo(Handle<Script> script,
+ Handle<String> source);
+
+ static void WrapSharedFunctionInfos(Handle<JSArray> array);
+
+ MUST_USE_RESULT static MaybeObject* ReplaceFunctionCode(
+ Handle<JSArray> new_compile_info_array,
+ Handle<JSArray> shared_info_array);
+
+ static MaybeObject* FunctionSourceUpdated(Handle<JSArray> shared_info_array);
+
+ // Updates script field in FunctionSharedInfo.
+ static void SetFunctionScript(Handle<JSValue> function_wrapper,
+ Handle<Object> script_handle);
+
+ MUST_USE_RESULT static MaybeObject* PatchFunctionPositions(
+ Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array);
+
+ // For a script updates its source field. If old_script_name is provided
+ // (i.e. is a String), also creates a copy of the script with its original
+ // source and sends notification to debugger.
+ static Object* ChangeScriptSource(Handle<Script> original_script,
+ Handle<String> new_source,
+ Handle<Object> old_script_name);
+
+ // In a code of a parent function replaces original function as embedded
+ // object with a substitution one.
+ static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared,
+ Handle<JSValue> orig_function_shared,
+ Handle<JSValue> subst_function_shared);
+
+ // Checks listed functions on stack and return array with corresponding
+ // FunctionPatchabilityStatus statuses; extra array element may
+ // contain general error message. Modifies the current stack and
+ // has restart the lowest found frames and drops all other frames above
+ // if possible and if do_drop is true.
+ static Handle<JSArray> CheckAndDropActivations(
+ Handle<JSArray> shared_info_array, bool do_drop);
+
+ // Restarts the call frame and completely drops all frames above it.
+ // Return error message or NULL.
+ static const char* RestartFrame(JavaScriptFrame* frame);
+
+ // A copy of this is in liveedit-debugger.js.
+ enum FunctionPatchabilityStatus {
+ FUNCTION_AVAILABLE_FOR_PATCH = 1,
+ FUNCTION_BLOCKED_ON_ACTIVE_STACK = 2,
+ FUNCTION_BLOCKED_ON_OTHER_STACK = 3,
+ FUNCTION_BLOCKED_UNDER_NATIVE_CODE = 4,
+ FUNCTION_REPLACED_ON_ACTIVE_STACK = 5
+ };
+
+ // Compares 2 strings line-by-line, then token-wise and returns diff in form
+ // of array of triplets (pos1, pos1_end, pos2_end) describing list
+ // of diff chunks.
+ static Handle<JSArray> CompareStrings(Handle<String> s1,
+ Handle<String> s2);
+};
+
+
+// A general-purpose comparator between 2 arrays.
+class Comparator {
+ public:
+ // Holds 2 arrays of some elements allowing to compare any pair of
+ // element from the first array and element from the second array.
+ class Input {
+ public:
+ virtual int GetLength1() = 0;
+ virtual int GetLength2() = 0;
+ virtual bool Equals(int index1, int index2) = 0;
+
+ protected:
+ virtual ~Input() {}
+ };
+
+ // Receives compare result as a series of chunks.
+ class Output {
+ public:
+ // Puts another chunk in result list. Note that technically speaking
+ // only 3 arguments actually needed with 4th being derivable.
+ virtual void AddChunk(int pos1, int pos2, int len1, int len2) = 0;
+
+ protected:
+ virtual ~Output() {}
+ };
+
+ // Finds the difference between 2 arrays of elements.
+ static void CalculateDifference(Input* input,
+ Output* result_writer);
+};
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+} } // namespace v8::internal
+
+#endif /* V*_LIVEEDIT_H_ */
diff --git a/chromium/v8/src/log-inl.h b/chromium/v8/src/log-inl.h
new file mode 100644
index 00000000000..7f653cb7283
--- /dev/null
+++ b/chromium/v8/src/log-inl.h
@@ -0,0 +1,54 @@
+// Copyright 2006-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.
+
+#ifndef V8_LOG_INL_H_
+#define V8_LOG_INL_H_
+
+#include "log.h"
+
+namespace v8 {
+namespace internal {
+
+Logger::LogEventsAndTags Logger::ToNativeByScript(Logger::LogEventsAndTags tag,
+ Script* script) {
+ if ((tag == FUNCTION_TAG || tag == LAZY_COMPILE_TAG || tag == SCRIPT_TAG)
+ && script->type()->value() == Script::TYPE_NATIVE) {
+ switch (tag) {
+ case FUNCTION_TAG: return NATIVE_FUNCTION_TAG;
+ case LAZY_COMPILE_TAG: return NATIVE_LAZY_COMPILE_TAG;
+ case SCRIPT_TAG: return NATIVE_SCRIPT_TAG;
+ default: return tag;
+ }
+ } else {
+ return tag;
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_LOG_INL_H_
diff --git a/chromium/v8/src/log-utils.cc b/chromium/v8/src/log-utils.cc
new file mode 100644
index 00000000000..6bba8823e30
--- /dev/null
+++ b/chromium/v8/src/log-utils.cc
@@ -0,0 +1,264 @@
+// 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.
+
+#include "v8.h"
+
+#include "log-utils.h"
+#include "string-stream.h"
+
+namespace v8 {
+namespace internal {
+
+
+const char* const Log::kLogToTemporaryFile = "&";
+const char* const Log::kLogToConsole = "-";
+
+
+Log::Log(Logger* logger)
+ : is_stopped_(false),
+ output_handle_(NULL),
+ mutex_(NULL),
+ message_buffer_(NULL),
+ logger_(logger) {
+}
+
+
+void Log::Initialize(const char* log_file_name) {
+ mutex_ = OS::CreateMutex();
+ message_buffer_ = NewArray<char>(kMessageBufferSize);
+
+ // --log-all enables all the log flags.
+ if (FLAG_log_all) {
+ FLAG_log_runtime = true;
+ FLAG_log_api = true;
+ FLAG_log_code = true;
+ FLAG_log_gc = true;
+ FLAG_log_suspect = true;
+ FLAG_log_handles = true;
+ FLAG_log_regexp = true;
+ FLAG_log_internal_timer_events = true;
+ }
+
+ // --prof implies --log-code.
+ if (FLAG_prof) FLAG_log_code = true;
+
+ // --prof_lazy controls --log-code.
+ if (FLAG_prof_lazy) {
+ FLAG_log_code = false;
+ }
+
+ // If we're logging anything, we need to open the log file.
+ if (Log::InitLogAtStart()) {
+ if (strcmp(log_file_name, kLogToConsole) == 0) {
+ OpenStdout();
+ } else if (strcmp(log_file_name, kLogToTemporaryFile) == 0) {
+ OpenTemporaryFile();
+ } else {
+ OpenFile(log_file_name);
+ }
+ }
+}
+
+
+void Log::OpenStdout() {
+ ASSERT(!IsEnabled());
+ output_handle_ = stdout;
+}
+
+
+void Log::OpenTemporaryFile() {
+ ASSERT(!IsEnabled());
+ output_handle_ = i::OS::OpenTemporaryFile();
+}
+
+
+void Log::OpenFile(const char* name) {
+ ASSERT(!IsEnabled());
+ output_handle_ = OS::FOpen(name, OS::LogFileOpenMode);
+}
+
+
+FILE* Log::Close() {
+ FILE* result = NULL;
+ if (output_handle_ != NULL) {
+ if (strcmp(FLAG_logfile, kLogToTemporaryFile) != 0) {
+ fclose(output_handle_);
+ } else {
+ result = output_handle_;
+ }
+ }
+ output_handle_ = NULL;
+
+ DeleteArray(message_buffer_);
+ message_buffer_ = NULL;
+
+ delete mutex_;
+ mutex_ = NULL;
+
+ is_stopped_ = false;
+ return result;
+}
+
+
+Log::MessageBuilder::MessageBuilder(Log* log)
+ : log_(log),
+ sl(log_->mutex_),
+ pos_(0) {
+ ASSERT(log_->message_buffer_ != NULL);
+}
+
+
+void Log::MessageBuilder::Append(const char* format, ...) {
+ Vector<char> buf(log_->message_buffer_ + pos_,
+ Log::kMessageBufferSize - pos_);
+ va_list args;
+ va_start(args, format);
+ AppendVA(format, args);
+ va_end(args);
+ ASSERT(pos_ <= Log::kMessageBufferSize);
+}
+
+
+void Log::MessageBuilder::AppendVA(const char* format, va_list args) {
+ Vector<char> buf(log_->message_buffer_ + pos_,
+ Log::kMessageBufferSize - pos_);
+ int result = v8::internal::OS::VSNPrintF(buf, format, args);
+
+ // Result is -1 if output was truncated.
+ if (result >= 0) {
+ pos_ += result;
+ } else {
+ pos_ = Log::kMessageBufferSize;
+ }
+ ASSERT(pos_ <= Log::kMessageBufferSize);
+}
+
+
+void Log::MessageBuilder::Append(const char c) {
+ if (pos_ < Log::kMessageBufferSize) {
+ log_->message_buffer_[pos_++] = c;
+ }
+ ASSERT(pos_ <= Log::kMessageBufferSize);
+}
+
+
+void Log::MessageBuilder::AppendDoubleQuotedString(const char* string) {
+ Append('"');
+ for (const char* p = string; *p != '\0'; p++) {
+ if (*p == '"') {
+ Append('\\');
+ }
+ Append(*p);
+ }
+ Append('"');
+}
+
+
+void Log::MessageBuilder::Append(String* str) {
+ DisallowHeapAllocation no_gc; // Ensure string stay valid.
+ int length = str->length();
+ for (int i = 0; i < length; i++) {
+ Append(static_cast<char>(str->Get(i)));
+ }
+}
+
+
+void Log::MessageBuilder::AppendAddress(Address addr) {
+ Append("0x%" V8PRIxPTR, addr);
+}
+
+
+void Log::MessageBuilder::AppendSymbolName(Symbol* symbol) {
+ ASSERT(symbol);
+ Append("symbol(");
+ if (!symbol->name()->IsUndefined()) {
+ Append("\"");
+ AppendDetailed(String::cast(symbol->name()), false);
+ Append("\" ");
+ }
+ Append("hash %x)", symbol->Hash());
+}
+
+
+void Log::MessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
+ if (str == NULL) return;
+ DisallowHeapAllocation no_gc; // Ensure string stay valid.
+ int len = str->length();
+ if (len > 0x1000)
+ len = 0x1000;
+ if (show_impl_info) {
+ Append(str->IsOneByteRepresentation() ? 'a' : '2');
+ if (StringShape(str).IsExternal())
+ Append('e');
+ if (StringShape(str).IsInternalized())
+ Append('#');
+ Append(":%i:", str->length());
+ }
+ for (int i = 0; i < len; i++) {
+ uc32 c = str->Get(i);
+ if (c > 0xff) {
+ Append("\\u%04x", c);
+ } else if (c < 32 || c > 126) {
+ Append("\\x%02x", c);
+ } else if (c == ',') {
+ Append("\\,");
+ } else if (c == '\\') {
+ Append("\\\\");
+ } else if (c == '\"') {
+ Append("\"\"");
+ } else {
+ Append("%lc", c);
+ }
+ }
+}
+
+
+void Log::MessageBuilder::AppendStringPart(const char* str, int len) {
+ if (pos_ + len > Log::kMessageBufferSize) {
+ len = Log::kMessageBufferSize - pos_;
+ ASSERT(len >= 0);
+ if (len == 0) return;
+ }
+ Vector<char> buf(log_->message_buffer_ + pos_,
+ Log::kMessageBufferSize - pos_);
+ OS::StrNCpy(buf, str, len);
+ pos_ += len;
+ ASSERT(pos_ <= Log::kMessageBufferSize);
+}
+
+
+void Log::MessageBuilder::WriteToLogFile() {
+ ASSERT(pos_ <= Log::kMessageBufferSize);
+ const int written = log_->WriteToFile(log_->message_buffer_, pos_);
+ if (written != pos_) {
+ log_->stop();
+ log_->logger_->LogFailure();
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/log-utils.h b/chromium/v8/src/log-utils.h
new file mode 100644
index 00000000000..861a8263b84
--- /dev/null
+++ b/chromium/v8/src/log-utils.h
@@ -0,0 +1,159 @@
+// Copyright 2006-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.
+
+#ifndef V8_LOG_UTILS_H_
+#define V8_LOG_UTILS_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+class Logger;
+
+// Functions and data for performing output of log messages.
+class Log {
+ public:
+ // Performs process-wide initialization.
+ void Initialize(const char* log_file_name);
+
+ // Disables logging, but preserves acquired resources.
+ void stop() { is_stopped_ = true; }
+
+ static bool InitLogAtStart() {
+ return FLAG_log || FLAG_log_runtime || FLAG_log_api
+ || FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
+ || FLAG_log_regexp || FLAG_ll_prof || FLAG_log_internal_timer_events;
+ }
+
+ // Frees all resources acquired in Initialize and Open... functions.
+ // When a temporary file is used for the log, returns its stream descriptor,
+ // leaving the file open.
+ FILE* Close();
+
+ // Returns whether logging is enabled.
+ bool IsEnabled() {
+ return !is_stopped_ && output_handle_ != NULL;
+ }
+
+ // Size of buffer used for formatting log messages.
+ static const int kMessageBufferSize = 2048;
+
+ // This mode is only used in tests, as temporary files are automatically
+ // deleted on close and thus can't be accessed afterwards.
+ static const char* const kLogToTemporaryFile;
+ static const char* const kLogToConsole;
+
+ // Utility class for formatting log messages. It fills the message into the
+ // static buffer in Log.
+ class MessageBuilder BASE_EMBEDDED {
+ public:
+ // Create a message builder starting from position 0.
+ // This acquires the mutex in the log as well.
+ explicit MessageBuilder(Log* log);
+ ~MessageBuilder() { }
+
+ // Append string data to the log message.
+ void Append(const char* format, ...);
+
+ // Append string data to the log message.
+ void AppendVA(const char* format, va_list args);
+
+ // Append a character to the log message.
+ void Append(const char c);
+
+ // Append double quoted string to the log message.
+ void AppendDoubleQuotedString(const char* string);
+
+ // Append a heap string.
+ void Append(String* str);
+
+ // Appends an address.
+ void AppendAddress(Address addr);
+
+ void AppendSymbolName(Symbol* symbol);
+
+ void AppendDetailed(String* str, bool show_impl_info);
+
+ // Append a portion of a string.
+ void AppendStringPart(const char* str, int len);
+
+ // Write the log message to the log file currently opened.
+ void WriteToLogFile();
+
+ private:
+ Log* log_;
+ ScopedLock sl;
+ int pos_;
+ };
+
+ private:
+ explicit Log(Logger* logger);
+
+ // Opens stdout for logging.
+ void OpenStdout();
+
+ // Opens file for logging.
+ void OpenFile(const char* name);
+
+ // Opens a temporary file for logging.
+ void OpenTemporaryFile();
+
+ // Implementation of writing to a log file.
+ int WriteToFile(const char* msg, int length) {
+ ASSERT(output_handle_ != NULL);
+ size_t rv = fwrite(msg, 1, length, output_handle_);
+ ASSERT(static_cast<size_t>(length) == rv);
+ USE(rv);
+ fflush(output_handle_);
+ return length;
+ }
+
+ // Whether logging is stopped (e.g. due to insufficient resources).
+ bool is_stopped_;
+
+ // When logging is active output_handle_ is used to store a pointer to log
+ // destination. mutex_ should be acquired before using output_handle_.
+ FILE* output_handle_;
+
+ // mutex_ is a Mutex used for enforcing exclusive
+ // access to the formatting buffer and the log file or log memory buffer.
+ Mutex* mutex_;
+
+ // Buffer used for formatting log messages. This is a singleton buffer and
+ // mutex_ should be acquired before using it.
+ char* message_buffer_;
+
+ Logger* logger_;
+
+ friend class Logger;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_LOG_UTILS_H_
diff --git a/chromium/v8/src/log.cc b/chromium/v8/src/log.cc
new file mode 100644
index 00000000000..a1e5a6752b1
--- /dev/null
+++ b/chromium/v8/src/log.cc
@@ -0,0 +1,1956 @@
+// 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 <stdarg.h>
+
+#include "v8.h"
+
+#include "bootstrapper.h"
+#include "code-stubs.h"
+#include "cpu-profiler.h"
+#include "deoptimizer.h"
+#include "global-handles.h"
+#include "log.h"
+#include "log-utils.h"
+#include "macro-assembler.h"
+#include "platform.h"
+#include "runtime-profiler.h"
+#include "serialize.h"
+#include "string-stream.h"
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+#define DECLARE_EVENT(ignore1, name) name,
+static const char* const kLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
+ LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT)
+};
+#undef DECLARE_EVENT
+
+
+#define CALL_LISTENERS(Call) \
+for (int i = 0; i < listeners_.length(); ++i) { \
+ listeners_[i]->Call; \
+}
+
+#define PROFILER_LOG(Call) \
+ do { \
+ CpuProfiler* cpu_profiler = isolate_->cpu_profiler(); \
+ if (cpu_profiler->is_profiling()) { \
+ cpu_profiler->Call; \
+ } \
+ } while (false);
+
+// ComputeMarker must only be used when SharedFunctionInfo is known.
+static const char* ComputeMarker(Code* code) {
+ switch (code->kind()) {
+ case Code::FUNCTION: return code->optimizable() ? "~" : "";
+ case Code::OPTIMIZED_FUNCTION: return "*";
+ default: return "";
+ }
+}
+
+
+class CodeEventLogger::NameBuffer {
+ public:
+ NameBuffer() { Reset(); }
+
+ void Reset() {
+ utf8_pos_ = 0;
+ }
+
+ void Init(Logger::LogEventsAndTags tag) {
+ Reset();
+ AppendBytes(kLogEventsNames[tag]);
+ AppendByte(':');
+ }
+
+ void AppendName(Name* name) {
+ if (name->IsString()) {
+ AppendString(String::cast(name));
+ } else {
+ Symbol* symbol = Symbol::cast(name);
+ AppendBytes("symbol(");
+ if (!symbol->name()->IsUndefined()) {
+ AppendBytes("\"");
+ AppendString(String::cast(symbol->name()));
+ AppendBytes("\" ");
+ }
+ AppendBytes("hash ");
+ AppendHex(symbol->Hash());
+ AppendByte(')');
+ }
+ }
+
+ void AppendString(String* str) {
+ if (str == NULL) return;
+ int uc16_length = Min(str->length(), kUtf16BufferSize);
+ String::WriteToFlat(str, utf16_buffer, 0, uc16_length);
+ int previous = unibrow::Utf16::kNoPreviousCharacter;
+ for (int i = 0; i < uc16_length && utf8_pos_ < kUtf8BufferSize; ++i) {
+ uc16 c = utf16_buffer[i];
+ if (c <= unibrow::Utf8::kMaxOneByteChar) {
+ utf8_buffer_[utf8_pos_++] = static_cast<char>(c);
+ } else {
+ int char_length = unibrow::Utf8::Length(c, previous);
+ if (utf8_pos_ + char_length > kUtf8BufferSize) break;
+ unibrow::Utf8::Encode(utf8_buffer_ + utf8_pos_, c, previous);
+ utf8_pos_ += char_length;
+ }
+ previous = c;
+ }
+ }
+
+ void AppendBytes(const char* bytes, int size) {
+ size = Min(size, kUtf8BufferSize - utf8_pos_);
+ OS::MemCopy(utf8_buffer_ + utf8_pos_, bytes, size);
+ utf8_pos_ += size;
+ }
+
+ void AppendBytes(const char* bytes) {
+ AppendBytes(bytes, StrLength(bytes));
+ }
+
+ void AppendByte(char c) {
+ if (utf8_pos_ >= kUtf8BufferSize) return;
+ utf8_buffer_[utf8_pos_++] = c;
+ }
+
+ void AppendInt(int n) {
+ Vector<char> buffer(utf8_buffer_ + utf8_pos_,
+ kUtf8BufferSize - utf8_pos_);
+ int size = OS::SNPrintF(buffer, "%d", n);
+ if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) {
+ utf8_pos_ += size;
+ }
+ }
+
+ void AppendHex(uint32_t n) {
+ Vector<char> buffer(utf8_buffer_ + utf8_pos_,
+ kUtf8BufferSize - utf8_pos_);
+ int size = OS::SNPrintF(buffer, "%x", n);
+ if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) {
+ utf8_pos_ += size;
+ }
+ }
+
+ const char* get() { return utf8_buffer_; }
+ int size() const { return utf8_pos_; }
+
+ private:
+ static const int kUtf8BufferSize = 512;
+ static const int kUtf16BufferSize = 128;
+
+ int utf8_pos_;
+ char utf8_buffer_[kUtf8BufferSize];
+ uc16 utf16_buffer[kUtf16BufferSize];
+};
+
+
+CodeEventLogger::CodeEventLogger() : name_buffer_(new NameBuffer) { }
+
+CodeEventLogger::~CodeEventLogger() { delete name_buffer_; }
+
+
+void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ const char* comment) {
+ name_buffer_->Init(tag);
+ name_buffer_->AppendBytes(comment);
+ LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size());
+}
+
+
+void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ Name* name) {
+ name_buffer_->Init(tag);
+ name_buffer_->AppendName(name);
+ LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size());
+}
+
+
+void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* name) {
+ name_buffer_->Init(tag);
+ name_buffer_->AppendBytes(ComputeMarker(code));
+ name_buffer_->AppendName(name);
+ LogRecordedBuffer(code, shared, name_buffer_->get(), name_buffer_->size());
+}
+
+
+void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* source, int line) {
+ name_buffer_->Init(tag);
+ name_buffer_->AppendBytes(ComputeMarker(code));
+ name_buffer_->AppendString(shared->DebugName());
+ name_buffer_->AppendByte(' ');
+ if (source->IsString()) {
+ name_buffer_->AppendString(String::cast(source));
+ } else {
+ name_buffer_->AppendBytes("symbol(hash ");
+ name_buffer_->AppendHex(Name::cast(source)->Hash());
+ name_buffer_->AppendByte(')');
+ }
+ name_buffer_->AppendByte(':');
+ name_buffer_->AppendInt(line);
+ LogRecordedBuffer(code, shared, name_buffer_->get(), name_buffer_->size());
+}
+
+
+void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ int args_count) {
+ name_buffer_->Init(tag);
+ name_buffer_->AppendInt(args_count);
+ LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size());
+}
+
+
+void CodeEventLogger::RegExpCodeCreateEvent(Code* code, String* source) {
+ name_buffer_->Init(Logger::REG_EXP_TAG);
+ name_buffer_->AppendString(source);
+ LogRecordedBuffer(code, NULL, name_buffer_->get(), name_buffer_->size());
+}
+
+
+// Low-level logging support.
+#define LL_LOG(Call) if (ll_logger_) ll_logger_->Call;
+
+class LowLevelLogger : public CodeEventLogger {
+ public:
+ explicit LowLevelLogger(const char* file_name);
+ virtual ~LowLevelLogger();
+
+ virtual void CodeMoveEvent(Address from, Address to);
+ virtual void CodeDeleteEvent(Address from);
+ virtual void SnapshotPositionEvent(Address addr, int pos);
+ virtual void CodeMovingGCEvent();
+
+ private:
+ virtual void LogRecordedBuffer(Code* code,
+ SharedFunctionInfo* shared,
+ const char* name,
+ int length);
+
+ // Low-level profiling event structures.
+ struct CodeCreateStruct {
+ static const char kTag = 'C';
+
+ int32_t name_size;
+ Address code_address;
+ int32_t code_size;
+ };
+
+
+ struct CodeMoveStruct {
+ static const char kTag = 'M';
+
+ Address from_address;
+ Address to_address;
+ };
+
+
+ struct CodeDeleteStruct {
+ static const char kTag = 'D';
+
+ Address address;
+ };
+
+
+ struct SnapshotPositionStruct {
+ static const char kTag = 'P';
+
+ Address address;
+ int32_t position;
+ };
+
+
+ static const char kCodeMovingGCTag = 'G';
+
+
+ // Extension added to V8 log file name to get the low-level log name.
+ static const char kLogExt[];
+
+ // File buffer size of the low-level log. We don't use the default to
+ // minimize the associated overhead.
+ static const int kLogBufferSize = 2 * MB;
+
+ void LogCodeInfo();
+ void LogWriteBytes(const char* bytes, int size);
+
+ template <typename T>
+ void LogWriteStruct(const T& s) {
+ char tag = T::kTag;
+ LogWriteBytes(reinterpret_cast<const char*>(&tag), sizeof(tag));
+ LogWriteBytes(reinterpret_cast<const char*>(&s), sizeof(s));
+ }
+
+ FILE* ll_output_handle_;
+};
+
+const char LowLevelLogger::kLogExt[] = ".ll";
+
+LowLevelLogger::LowLevelLogger(const char* name)
+ : ll_output_handle_(NULL) {
+ // Open the low-level log file.
+ size_t len = strlen(name);
+ ScopedVector<char> ll_name(static_cast<int>(len + sizeof(kLogExt)));
+ OS::MemCopy(ll_name.start(), name, len);
+ OS::MemCopy(ll_name.start() + len, kLogExt, sizeof(kLogExt));
+ ll_output_handle_ = OS::FOpen(ll_name.start(), OS::LogFileOpenMode);
+ setvbuf(ll_output_handle_, NULL, _IOFBF, kLogBufferSize);
+
+ LogCodeInfo();
+}
+
+
+LowLevelLogger::~LowLevelLogger() {
+ fclose(ll_output_handle_);
+ ll_output_handle_ = NULL;
+}
+
+
+void LowLevelLogger::LogCodeInfo() {
+#if V8_TARGET_ARCH_IA32
+ const char arch[] = "ia32";
+#elif V8_TARGET_ARCH_X64
+ 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
+ LogWriteBytes(arch, sizeof(arch));
+}
+
+
+void LowLevelLogger::LogRecordedBuffer(Code* code,
+ SharedFunctionInfo*,
+ const char* name,
+ int length) {
+ CodeCreateStruct event;
+ event.name_size = length;
+ event.code_address = code->instruction_start();
+ ASSERT(event.code_address == code->address() + Code::kHeaderSize);
+ event.code_size = code->instruction_size();
+ LogWriteStruct(event);
+ LogWriteBytes(name, length);
+ LogWriteBytes(
+ reinterpret_cast<const char*>(code->instruction_start()),
+ code->instruction_size());
+}
+
+
+void LowLevelLogger::CodeMoveEvent(Address from, Address to) {
+ CodeMoveStruct event;
+ event.from_address = from + Code::kHeaderSize;
+ event.to_address = to + Code::kHeaderSize;
+ LogWriteStruct(event);
+}
+
+
+void LowLevelLogger::CodeDeleteEvent(Address from) {
+ CodeDeleteStruct event;
+ event.address = from + Code::kHeaderSize;
+ LogWriteStruct(event);
+}
+
+
+void LowLevelLogger::SnapshotPositionEvent(Address addr, int pos) {
+ SnapshotPositionStruct event;
+ event.address = addr + Code::kHeaderSize;
+ event.position = pos;
+ LogWriteStruct(event);
+}
+
+
+void LowLevelLogger::LogWriteBytes(const char* bytes, int size) {
+ size_t rv = fwrite(bytes, 1, size, ll_output_handle_);
+ ASSERT(static_cast<size_t>(size) == rv);
+ USE(rv);
+}
+
+
+void LowLevelLogger::CodeMovingGCEvent() {
+ const char tag = kCodeMovingGCTag;
+
+ LogWriteBytes(&tag, sizeof(tag));
+}
+
+
+#define JIT_LOG(Call) if (jit_logger_) jit_logger_->Call;
+
+
+class JitLogger : public CodeEventLogger {
+ public:
+ explicit JitLogger(JitCodeEventHandler code_event_handler);
+
+ virtual void CodeMoveEvent(Address from, Address to);
+ virtual void CodeDeleteEvent(Address from);
+ virtual void AddCodeLinePosInfoEvent(
+ void* jit_handler_data,
+ int pc_offset,
+ int position,
+ JitCodeEvent::PositionType position_type);
+
+ void* StartCodePosInfoEvent();
+ void EndCodePosInfoEvent(Code* code, void* jit_handler_data);
+
+ private:
+ virtual void LogRecordedBuffer(Code* code,
+ SharedFunctionInfo* shared,
+ const char* name,
+ int length);
+
+ JitCodeEventHandler code_event_handler_;
+};
+
+
+JitLogger::JitLogger(JitCodeEventHandler code_event_handler)
+ : code_event_handler_(code_event_handler) {
+}
+
+
+void JitLogger::LogRecordedBuffer(Code* code,
+ SharedFunctionInfo* shared,
+ const char* name,
+ int length) {
+ JitCodeEvent event;
+ memset(&event, 0, sizeof(event));
+ event.type = JitCodeEvent::CODE_ADDED;
+ event.code_start = code->instruction_start();
+ event.code_len = code->instruction_size();
+ Handle<Script> script_handle;
+ if (shared && shared->script()->IsScript()) {
+ script_handle = Handle<Script>(Script::cast(shared->script()));
+ }
+ event.script = ToApiHandle<v8::Script>(script_handle);
+ event.name.str = name;
+ event.name.len = length;
+ code_event_handler_(&event);
+}
+
+
+void JitLogger::CodeMoveEvent(Address from, Address to) {
+ Code* from_code = Code::cast(HeapObject::FromAddress(from));
+
+ JitCodeEvent event;
+ event.type = JitCodeEvent::CODE_MOVED;
+ event.code_start = from_code->instruction_start();
+ event.code_len = from_code->instruction_size();
+
+ // Calculate the header size.
+ const size_t header_size =
+ from_code->instruction_start() - reinterpret_cast<byte*>(from_code);
+
+ // Calculate the new start address of the instructions.
+ event.new_code_start =
+ reinterpret_cast<byte*>(HeapObject::FromAddress(to)) + header_size;
+
+ code_event_handler_(&event);
+}
+
+
+void JitLogger::CodeDeleteEvent(Address from) {
+ Code* from_code = Code::cast(HeapObject::FromAddress(from));
+
+ JitCodeEvent event;
+ event.type = JitCodeEvent::CODE_REMOVED;
+ event.code_start = from_code->instruction_start();
+ event.code_len = from_code->instruction_size();
+
+ code_event_handler_(&event);
+}
+
+void JitLogger::AddCodeLinePosInfoEvent(
+ void* jit_handler_data,
+ int pc_offset,
+ int position,
+ JitCodeEvent::PositionType position_type) {
+ JitCodeEvent event;
+ memset(&event, 0, sizeof(event));
+ event.type = JitCodeEvent::CODE_ADD_LINE_POS_INFO;
+ event.user_data = jit_handler_data;
+ event.line_info.offset = pc_offset;
+ event.line_info.pos = position;
+ event.line_info.position_type = position_type;
+
+ code_event_handler_(&event);
+}
+
+
+void* JitLogger::StartCodePosInfoEvent() {
+ JitCodeEvent event;
+ memset(&event, 0, sizeof(event));
+ event.type = JitCodeEvent::CODE_START_LINE_INFO_RECORDING;
+
+ code_event_handler_(&event);
+ return event.user_data;
+}
+
+
+void JitLogger::EndCodePosInfoEvent(Code* code, void* jit_handler_data) {
+ JitCodeEvent event;
+ memset(&event, 0, sizeof(event));
+ event.type = JitCodeEvent::CODE_END_LINE_INFO_RECORDING;
+ event.code_start = code->instruction_start();
+ event.user_data = jit_handler_data;
+
+ code_event_handler_(&event);
+}
+
+
+// The Profiler samples pc and sp values for the main thread.
+// Each sample is appended to a circular buffer.
+// An independent thread removes data and writes it to the log.
+// This design minimizes the time spent in the sampler.
+//
+class Profiler: public Thread {
+ public:
+ explicit Profiler(Isolate* isolate);
+ void Engage();
+ void Disengage();
+
+ // Inserts collected profiling data into buffer.
+ void Insert(TickSample* sample) {
+ if (paused_)
+ return;
+
+ if (Succ(head_) == tail_) {
+ overflow_ = true;
+ } else {
+ buffer_[head_] = *sample;
+ head_ = Succ(head_);
+ buffer_semaphore_->Signal(); // Tell we have an element.
+ }
+ }
+
+ // Waits for a signal and removes profiling data.
+ bool Remove(TickSample* sample) {
+ buffer_semaphore_->Wait(); // Wait for an element.
+ *sample = buffer_[tail_];
+ bool result = overflow_;
+ tail_ = Succ(tail_);
+ overflow_ = false;
+ return result;
+ }
+
+ void Run();
+
+ // Pause and Resume TickSample data collection.
+ bool paused() const { return paused_; }
+ void pause() { paused_ = true; }
+ void resume() { paused_ = false; }
+
+ private:
+ // Returns the next index in the cyclic buffer.
+ int Succ(int index) { return (index + 1) % kBufferSize; }
+
+ Isolate* isolate_;
+ // Cyclic buffer for communicating profiling samples
+ // between the signal handler and the worker thread.
+ static const int kBufferSize = 128;
+ TickSample buffer_[kBufferSize]; // Buffer storage.
+ int head_; // Index to the buffer head.
+ int tail_; // Index to the buffer tail.
+ bool overflow_; // Tell whether a buffer overflow has occurred.
+ Semaphore* buffer_semaphore_; // Sempahore used for buffer synchronization.
+
+ // Tells whether profiler is engaged, that is, processing thread is stated.
+ bool engaged_;
+
+ // Tells whether worker thread should continue running.
+ bool running_;
+
+ // Tells whether we are currently recording tick samples.
+ bool paused_;
+};
+
+
+//
+// Ticker used to provide ticks to the profiler and the sliding state
+// window.
+//
+class Ticker: public Sampler {
+ public:
+ Ticker(Isolate* isolate, int interval):
+ Sampler(isolate, interval),
+ profiler_(NULL) {}
+
+ ~Ticker() { if (IsActive()) Stop(); }
+
+ virtual void Tick(TickSample* sample) {
+ if (profiler_) profiler_->Insert(sample);
+ }
+
+ void SetProfiler(Profiler* profiler) {
+ ASSERT(profiler_ == NULL);
+ profiler_ = profiler;
+ IncreaseProfilingDepth();
+ if (!FLAG_prof_lazy && !IsActive()) Start();
+ }
+
+ void ClearProfiler() {
+ DecreaseProfilingDepth();
+ profiler_ = NULL;
+ if (IsActive()) Stop();
+ }
+
+ private:
+ Profiler* profiler_;
+};
+
+
+//
+// Profiler implementation.
+//
+Profiler::Profiler(Isolate* isolate)
+ : Thread("v8:Profiler"),
+ isolate_(isolate),
+ head_(0),
+ tail_(0),
+ overflow_(false),
+ buffer_semaphore_(OS::CreateSemaphore(0)),
+ engaged_(false),
+ running_(false),
+ paused_(false) {
+}
+
+
+void Profiler::Engage() {
+ if (engaged_) return;
+ engaged_ = true;
+
+ OS::LogSharedLibraryAddresses();
+
+ // Start thread processing the profiler buffer.
+ running_ = true;
+ Start();
+
+ // Register to get ticks.
+ Logger* logger = isolate_->logger();
+ logger->ticker_->SetProfiler(this);
+
+ logger->ProfilerBeginEvent();
+}
+
+
+void Profiler::Disengage() {
+ if (!engaged_) return;
+
+ // Stop receiving ticks.
+ isolate_->logger()->ticker_->ClearProfiler();
+
+ // Terminate the worker thread by setting running_ to false,
+ // inserting a fake element in the queue and then wait for
+ // the thread to terminate.
+ running_ = false;
+ TickSample sample;
+ // Reset 'paused_' flag, otherwise semaphore may not be signalled.
+ resume();
+ Insert(&sample);
+ Join();
+
+ LOG(ISOLATE, UncheckedStringEvent("profiler", "end"));
+}
+
+
+void Profiler::Run() {
+ TickSample sample;
+ bool overflow = Remove(&sample);
+ while (running_) {
+ LOG(isolate_, TickEvent(&sample, overflow));
+ overflow = Remove(&sample);
+ }
+}
+
+
+//
+// Logger class implementation.
+//
+
+Logger::Logger(Isolate* isolate)
+ : isolate_(isolate),
+ ticker_(NULL),
+ profiler_(NULL),
+ log_events_(NULL),
+ logging_nesting_(0),
+ cpu_profiler_nesting_(0),
+ log_(new Log(this)),
+ ll_logger_(NULL),
+ jit_logger_(NULL),
+ listeners_(5),
+ is_initialized_(false),
+ epoch_(0) {
+}
+
+
+Logger::~Logger() {
+ delete log_;
+}
+
+
+void Logger::addCodeEventListener(CodeEventListener* listener) {
+ ASSERT(!hasCodeEventListener(listener));
+ listeners_.Add(listener);
+}
+
+
+void Logger::removeCodeEventListener(CodeEventListener* listener) {
+ ASSERT(hasCodeEventListener(listener));
+ listeners_.RemoveElement(listener);
+}
+
+
+bool Logger::hasCodeEventListener(CodeEventListener* listener) {
+ return listeners_.Contains(listener);
+}
+
+
+void Logger::ProfilerBeginEvent() {
+ if (!log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("profiler,\"begin\",%d\n", kSamplingIntervalMs);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::StringEvent(const char* name, const char* value) {
+ if (FLAG_log) UncheckedStringEvent(name, value);
+}
+
+
+void Logger::UncheckedStringEvent(const char* name, const char* value) {
+ if (!log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,\"%s\"\n", name, value);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::IntEvent(const char* name, int value) {
+ if (FLAG_log) UncheckedIntEvent(name, value);
+}
+
+
+void Logger::IntPtrTEvent(const char* name, intptr_t value) {
+ if (FLAG_log) UncheckedIntPtrTEvent(name, value);
+}
+
+
+void Logger::UncheckedIntEvent(const char* name, int value) {
+ if (!log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,%d\n", name, value);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::UncheckedIntPtrTEvent(const char* name, intptr_t value) {
+ if (!log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,%" V8_PTR_PREFIX "d\n", name, value);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::HandleEvent(const char* name, Object** location) {
+ if (!log_->IsEnabled() || !FLAG_log_handles) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,0x%" V8PRIxPTR "\n", name, location);
+ msg.WriteToLogFile();
+}
+
+
+// ApiEvent is private so all the calls come from the Logger class. It is the
+// caller's responsibility to ensure that log is enabled and that
+// FLAG_log_api is true.
+void Logger::ApiEvent(const char* format, ...) {
+ ASSERT(log_->IsEnabled() && FLAG_log_api);
+ Log::MessageBuilder msg(log_);
+ va_list ap;
+ va_start(ap, format);
+ msg.AppendVA(format, ap);
+ va_end(ap);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::ApiNamedSecurityCheck(Object* key) {
+ if (!log_->IsEnabled() || !FLAG_log_api) return;
+ if (key->IsString()) {
+ SmartArrayPointer<char> str =
+ String::cast(key)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ ApiEvent("api,check-security,\"%s\"\n", *str);
+ } else if (key->IsSymbol()) {
+ Symbol* symbol = Symbol::cast(key);
+ if (symbol->name()->IsUndefined()) {
+ ApiEvent("api,check-security,symbol(hash %x)\n",
+ Symbol::cast(key)->Hash());
+ } else {
+ SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
+ DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ ApiEvent("api,check-security,symbol(\"%s\" hash %x)\n",
+ *str,
+ Symbol::cast(key)->Hash());
+ }
+ } else if (key->IsUndefined()) {
+ ApiEvent("api,check-security,undefined\n");
+ } else {
+ ApiEvent("api,check-security,['no-name']\n");
+ }
+}
+
+
+void Logger::SharedLibraryEvent(const char* library_path,
+ uintptr_t start,
+ uintptr_t end) {
+ if (!log_->IsEnabled() || !FLAG_prof) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("shared-library,\"%s\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR "\n",
+ library_path,
+ start,
+ end);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::SharedLibraryEvent(const wchar_t* library_path,
+ uintptr_t start,
+ uintptr_t end) {
+ if (!log_->IsEnabled() || !FLAG_prof) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("shared-library,\"%ls\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR "\n",
+ library_path,
+ start,
+ end);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::CodeDeoptEvent(Code* code) {
+ if (!log_->IsEnabled()) return;
+ ASSERT(FLAG_log_internal_timer_events);
+ Log::MessageBuilder msg(log_);
+ int since_epoch = static_cast<int>(OS::Ticks() - epoch_);
+ msg.Append("code-deopt,%ld,%d\n", since_epoch, code->CodeSize());
+ msg.WriteToLogFile();
+}
+
+
+void Logger::TimerEvent(StartEnd se, const char* name) {
+ if (!log_->IsEnabled()) return;
+ ASSERT(FLAG_log_internal_timer_events);
+ Log::MessageBuilder msg(log_);
+ int since_epoch = static_cast<int>(OS::Ticks() - epoch_);
+ const char* format = (se == START) ? "timer-event-start,\"%s\",%ld\n"
+ : "timer-event-end,\"%s\",%ld\n";
+ msg.Append(format, name, since_epoch);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::EnterExternal(Isolate* isolate) {
+ LOG(isolate, TimerEvent(START, TimerEventScope::v8_external));
+ ASSERT(isolate->current_vm_state() == JS);
+ isolate->set_current_vm_state(EXTERNAL);
+}
+
+
+void Logger::LeaveExternal(Isolate* isolate) {
+ LOG(isolate, TimerEvent(END, TimerEventScope::v8_external));
+ ASSERT(isolate->current_vm_state() == EXTERNAL);
+ isolate->set_current_vm_state(JS);
+}
+
+
+void Logger::TimerEventScope::LogTimerEvent(StartEnd se) {
+ LOG(isolate_, TimerEvent(se, name_));
+}
+
+
+const char* Logger::TimerEventScope::v8_recompile_synchronous =
+ "V8.RecompileSynchronous";
+const char* Logger::TimerEventScope::v8_recompile_parallel =
+ "V8.RecompileParallel";
+const char* Logger::TimerEventScope::v8_compile_full_code =
+ "V8.CompileFullCode";
+const char* Logger::TimerEventScope::v8_execute = "V8.Execute";
+const char* Logger::TimerEventScope::v8_external = "V8.External";
+
+
+void Logger::LogRegExpSource(Handle<JSRegExp> regexp) {
+ // Prints "/" + re.source + "/" +
+ // (re.global?"g":"") + (re.ignorecase?"i":"") + (re.multiline?"m":"")
+ Log::MessageBuilder msg(log_);
+
+ Handle<Object> source = GetProperty(regexp, "source");
+ if (!source->IsString()) {
+ msg.Append("no source");
+ return;
+ }
+
+ switch (regexp->TypeTag()) {
+ case JSRegExp::ATOM:
+ msg.Append('a');
+ break;
+ default:
+ break;
+ }
+ msg.Append('/');
+ msg.AppendDetailed(*Handle<String>::cast(source), false);
+ msg.Append('/');
+
+ // global flag
+ Handle<Object> global = GetProperty(regexp, "global");
+ if (global->IsTrue()) {
+ msg.Append('g');
+ }
+ // ignorecase flag
+ Handle<Object> ignorecase = GetProperty(regexp, "ignoreCase");
+ if (ignorecase->IsTrue()) {
+ msg.Append('i');
+ }
+ // multiline flag
+ Handle<Object> multiline = GetProperty(regexp, "multiline");
+ if (multiline->IsTrue()) {
+ msg.Append('m');
+ }
+
+ msg.WriteToLogFile();
+}
+
+
+void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache) {
+ if (!log_->IsEnabled() || !FLAG_log_regexp) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("regexp-compile,");
+ LogRegExpSource(regexp);
+ msg.Append(in_cache ? ",hit\n" : ",miss\n");
+ msg.WriteToLogFile();
+}
+
+
+void Logger::LogRuntime(Vector<const char> format,
+ JSArray* args) {
+ if (!log_->IsEnabled() || !FLAG_log_runtime) return;
+ HandleScope scope(isolate_);
+ Log::MessageBuilder msg(log_);
+ for (int i = 0; i < format.length(); i++) {
+ char c = format[i];
+ if (c == '%' && i <= format.length() - 2) {
+ i++;
+ ASSERT('0' <= format[i] && format[i] <= '9');
+ MaybeObject* maybe = args->GetElement(format[i] - '0');
+ Object* obj;
+ if (!maybe->ToObject(&obj)) {
+ msg.Append("<exception>");
+ continue;
+ }
+ i++;
+ switch (format[i]) {
+ case 's':
+ msg.AppendDetailed(String::cast(obj), false);
+ break;
+ case 'S':
+ msg.AppendDetailed(String::cast(obj), true);
+ break;
+ case 'r':
+ Logger::LogRegExpSource(Handle<JSRegExp>(JSRegExp::cast(obj)));
+ break;
+ case 'x':
+ msg.Append("0x%x", Smi::cast(obj)->value());
+ break;
+ case 'i':
+ msg.Append("%i", Smi::cast(obj)->value());
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ msg.Append(c);
+ }
+ }
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::ApiIndexedSecurityCheck(uint32_t index) {
+ if (!log_->IsEnabled() || !FLAG_log_api) return;
+ ApiEvent("api,check-security,%u\n", index);
+}
+
+
+void Logger::ApiNamedPropertyAccess(const char* tag,
+ JSObject* holder,
+ Object* name) {
+ ASSERT(name->IsName());
+ if (!log_->IsEnabled() || !FLAG_log_api) return;
+ String* class_name_obj = holder->class_name();
+ SmartArrayPointer<char> class_name =
+ class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ if (name->IsString()) {
+ SmartArrayPointer<char> property_name =
+ String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ ApiEvent("api,%s,\"%s\",\"%s\"\n", tag, *class_name, *property_name);
+ } else {
+ Symbol* symbol = Symbol::cast(name);
+ uint32_t hash = symbol->Hash();
+ if (symbol->name()->IsUndefined()) {
+ ApiEvent("api,%s,\"%s\",symbol(hash %x)\n", tag, *class_name, hash);
+ } else {
+ SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
+ DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ ApiEvent("api,%s,\"%s\",symbol(\"%s\" hash %x)\n",
+ tag, *class_name, *str, hash);
+ }
+ }
+}
+
+void Logger::ApiIndexedPropertyAccess(const char* tag,
+ JSObject* holder,
+ uint32_t index) {
+ if (!log_->IsEnabled() || !FLAG_log_api) return;
+ String* class_name_obj = holder->class_name();
+ SmartArrayPointer<char> class_name =
+ class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ ApiEvent("api,%s,\"%s\",%u\n", tag, *class_name, index);
+}
+
+
+void Logger::ApiObjectAccess(const char* tag, JSObject* object) {
+ if (!log_->IsEnabled() || !FLAG_log_api) return;
+ String* class_name_obj = object->class_name();
+ SmartArrayPointer<char> class_name =
+ class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ ApiEvent("api,%s,\"%s\"\n", tag, *class_name);
+}
+
+
+void Logger::ApiEntryCall(const char* name) {
+ if (!log_->IsEnabled() || !FLAG_log_api) return;
+ ApiEvent("api,%s\n", name);
+}
+
+
+void Logger::NewEvent(const char* name, void* object, size_t size) {
+ if (!log_->IsEnabled() || !FLAG_log) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("new,%s,0x%" V8PRIxPTR ",%u\n", name, object,
+ static_cast<unsigned int>(size));
+ msg.WriteToLogFile();
+}
+
+
+void Logger::DeleteEvent(const char* name, void* object) {
+ if (!log_->IsEnabled() || !FLAG_log) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("delete,%s,0x%" V8PRIxPTR "\n", name, object);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::NewEventStatic(const char* name, void* object, size_t size) {
+ Isolate::Current()->logger()->NewEvent(name, object, size);
+}
+
+
+void Logger::DeleteEventStatic(const char* name, void* object) {
+ Isolate::Current()->logger()->DeleteEvent(name, object);
+}
+
+
+void Logger::CallbackEventInternal(const char* prefix, Name* name,
+ Address entry_point) {
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,%s,-2,",
+ kLogEventsNames[CODE_CREATION_EVENT],
+ kLogEventsNames[CALLBACK_TAG]);
+ msg.AppendAddress(entry_point);
+ if (name->IsString()) {
+ SmartArrayPointer<char> str =
+ String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ msg.Append(",1,\"%s%s\"", prefix, *str);
+ } else {
+ Symbol* symbol = Symbol::cast(name);
+ if (symbol->name()->IsUndefined()) {
+ msg.Append(",1,symbol(hash %x)", prefix, symbol->Hash());
+ } else {
+ SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
+ DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ msg.Append(",1,symbol(\"%s\" hash %x)", prefix, *str, symbol->Hash());
+ }
+ }
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::CallbackEvent(Name* name, Address entry_point) {
+ PROFILER_LOG(CallbackEvent(name, entry_point));
+ CallbackEventInternal("", name, entry_point);
+}
+
+
+void Logger::GetterCallbackEvent(Name* name, Address entry_point) {
+ PROFILER_LOG(GetterCallbackEvent(name, entry_point));
+ CallbackEventInternal("get ", name, entry_point);
+}
+
+
+void Logger::SetterCallbackEvent(Name* name, Address entry_point) {
+ PROFILER_LOG(SetterCallbackEvent(name, entry_point));
+ CallbackEventInternal("set ", name, entry_point);
+}
+
+
+static void AppendCodeCreateHeader(Log::MessageBuilder* msg,
+ Logger::LogEventsAndTags tag,
+ Code* code) {
+ ASSERT(msg);
+ msg->Append("%s,%s,%d,",
+ kLogEventsNames[Logger::CODE_CREATION_EVENT],
+ kLogEventsNames[tag],
+ code->kind());
+ msg->AppendAddress(code->address());
+ msg->Append(",%d,", code->ExecutableSize());
+}
+
+
+void Logger::CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ const char* comment) {
+ PROFILER_LOG(CodeCreateEvent(tag, code, comment));
+
+ if (!is_logging_code_events()) return;
+ CALL_LISTENERS(CodeCreateEvent(tag, code, comment));
+
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ AppendCodeCreateHeader(&msg, tag, code);
+ msg.AppendDoubleQuotedString(comment);
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ Name* name) {
+ PROFILER_LOG(CodeCreateEvent(tag, code, name));
+
+ if (!is_logging_code_events()) return;
+ CALL_LISTENERS(CodeCreateEvent(tag, code, name));
+
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ AppendCodeCreateHeader(&msg, tag, code);
+ if (name->IsString()) {
+ msg.Append('"');
+ msg.AppendDetailed(String::cast(name), false);
+ msg.Append('"');
+ } else {
+ msg.AppendSymbolName(Symbol::cast(name));
+ }
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* name) {
+ PROFILER_LOG(CodeCreateEvent(tag, code, shared, info, name));
+
+ if (!is_logging_code_events()) return;
+ CALL_LISTENERS(CodeCreateEvent(tag, code, shared, info, name));
+
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ if (code == isolate_->builtins()->builtin(
+ Builtins::kLazyCompile))
+ return;
+
+ Log::MessageBuilder msg(log_);
+ AppendCodeCreateHeader(&msg, tag, code);
+ if (name->IsString()) {
+ SmartArrayPointer<char> str =
+ String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ msg.Append("\"%s\"", *str);
+ } else {
+ msg.AppendSymbolName(Symbol::cast(name));
+ }
+ msg.Append(',');
+ msg.AppendAddress(shared->address());
+ msg.Append(",%s", ComputeMarker(code));
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+// Although, it is possible to extract source and line from
+// the SharedFunctionInfo object, we left it to caller
+// to leave logging functions free from heap allocations.
+void Logger::CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* source, int line) {
+ PROFILER_LOG(CodeCreateEvent(tag, code, shared, info, source, line));
+
+ if (!is_logging_code_events()) return;
+ CALL_LISTENERS(CodeCreateEvent(tag, code, shared, info, source, line));
+
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ AppendCodeCreateHeader(&msg, tag, code);
+ SmartArrayPointer<char> name =
+ shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ msg.Append("\"%s ", *name);
+ if (source->IsString()) {
+ SmartArrayPointer<char> sourcestr =
+ String::cast(source)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ msg.Append("%s", *sourcestr);
+ } else {
+ msg.AppendSymbolName(Symbol::cast(source));
+ }
+ msg.Append(":%d\",", line);
+ msg.AppendAddress(shared->address());
+ msg.Append(",%s", ComputeMarker(code));
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ int args_count) {
+ PROFILER_LOG(CodeCreateEvent(tag, code, args_count));
+
+ if (!is_logging_code_events()) return;
+ CALL_LISTENERS(CodeCreateEvent(tag, code, args_count));
+
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ AppendCodeCreateHeader(&msg, tag, code);
+ msg.Append("\"args_count: %d\"", args_count);
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::CodeMovingGCEvent() {
+ PROFILER_LOG(CodeMovingGCEvent());
+
+ if (!is_logging_code_events()) return;
+ if (!log_->IsEnabled() || !FLAG_ll_prof) return;
+ CALL_LISTENERS(CodeMovingGCEvent());
+ OS::SignalCodeMovingGC();
+}
+
+
+void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
+ PROFILER_LOG(RegExpCodeCreateEvent(code, source));
+
+ if (!is_logging_code_events()) return;
+ CALL_LISTENERS(RegExpCodeCreateEvent(code, source));
+
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ AppendCodeCreateHeader(&msg, REG_EXP_TAG, code);
+ msg.Append('"');
+ msg.AppendDetailed(source, false);
+ msg.Append('"');
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::CodeMoveEvent(Address from, Address to) {
+ PROFILER_LOG(CodeMoveEvent(from, to));
+
+ if (!is_logging_code_events()) return;
+ CALL_LISTENERS(CodeMoveEvent(from, to));
+ MoveEventInternal(CODE_MOVE_EVENT, from, to);
+}
+
+
+void Logger::CodeDeleteEvent(Address from) {
+ PROFILER_LOG(CodeDeleteEvent(from));
+
+ if (!is_logging_code_events()) return;
+ CALL_LISTENERS(CodeDeleteEvent(from));
+
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,", kLogEventsNames[CODE_DELETE_EVENT]);
+ msg.AppendAddress(from);
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::CodeLinePosInfoAddPositionEvent(void* jit_handler_data,
+ int pc_offset,
+ int position) {
+ JIT_LOG(AddCodeLinePosInfoEvent(jit_handler_data,
+ pc_offset,
+ position,
+ JitCodeEvent::POSITION));
+}
+
+
+void Logger::CodeLinePosInfoAddStatementPositionEvent(void* jit_handler_data,
+ int pc_offset,
+ int position) {
+ JIT_LOG(AddCodeLinePosInfoEvent(jit_handler_data,
+ pc_offset,
+ position,
+ JitCodeEvent::STATEMENT_POSITION));
+}
+
+
+void Logger::CodeStartLinePosInfoRecordEvent(PositionsRecorder* pos_recorder) {
+ if (jit_logger_ != NULL) {
+ pos_recorder->AttachJITHandlerData(jit_logger_->StartCodePosInfoEvent());
+ }
+}
+
+
+void Logger::CodeEndLinePosInfoRecordEvent(Code* code,
+ void* jit_handler_data) {
+ JIT_LOG(EndCodePosInfoEvent(code, jit_handler_data));
+}
+
+
+void Logger::CodeNameEvent(Address addr, int pos, const char* code_name) {
+ if (code_name == NULL) return; // Not a code object.
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,%d,", kLogEventsNames[SNAPSHOT_CODE_NAME_EVENT], pos);
+ msg.AppendDoubleQuotedString(code_name);
+ msg.Append("\n");
+ msg.WriteToLogFile();
+}
+
+
+void Logger::SnapshotPositionEvent(Address addr, int pos) {
+ if (!log_->IsEnabled()) return;
+ LL_LOG(SnapshotPositionEvent(addr, pos));
+ if (!FLAG_log_snapshot_positions) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,", kLogEventsNames[SNAPSHOT_POSITION_EVENT]);
+ msg.AppendAddress(addr);
+ msg.Append(",%d", pos);
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::SharedFunctionInfoMoveEvent(Address from, Address to) {
+ PROFILER_LOG(SharedFunctionInfoMoveEvent(from, to));
+
+ if (!is_logging_code_events()) return;
+ MoveEventInternal(SHARED_FUNC_MOVE_EVENT, from, to);
+}
+
+
+void Logger::MoveEventInternal(LogEventsAndTags event,
+ Address from,
+ Address to) {
+ if (!FLAG_log_code || !log_->IsEnabled()) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,", kLogEventsNames[event]);
+ msg.AppendAddress(from);
+ msg.Append(',');
+ msg.AppendAddress(to);
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::ResourceEvent(const char* name, const char* tag) {
+ if (!log_->IsEnabled() || !FLAG_log) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,%s,", name, tag);
+
+ uint32_t sec, usec;
+ if (OS::GetUserTime(&sec, &usec) != -1) {
+ msg.Append("%d,%d,", sec, usec);
+ }
+ msg.Append("%.0f", OS::TimeCurrentMillis());
+
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::SuspectReadEvent(Name* name, Object* obj) {
+ if (!log_->IsEnabled() || !FLAG_log_suspect) return;
+ Log::MessageBuilder msg(log_);
+ String* class_name = obj->IsJSObject()
+ ? JSObject::cast(obj)->class_name()
+ : isolate_->heap()->empty_string();
+ msg.Append("suspect-read,");
+ msg.Append(class_name);
+ msg.Append(',');
+ if (name->IsString()) {
+ msg.Append('"');
+ msg.Append(String::cast(name));
+ msg.Append('"');
+ } else {
+ msg.AppendSymbolName(Symbol::cast(name));
+ }
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+void Logger::HeapSampleBeginEvent(const char* space, const char* kind) {
+ if (!log_->IsEnabled() || !FLAG_log_gc) return;
+ Log::MessageBuilder msg(log_);
+ // Using non-relative system time in order to be able to synchronize with
+ // external memory profiling events (e.g. DOM memory size).
+ msg.Append("heap-sample-begin,\"%s\",\"%s\",%.0f\n",
+ space, kind, OS::TimeCurrentMillis());
+ msg.WriteToLogFile();
+}
+
+
+void Logger::HeapSampleEndEvent(const char* space, const char* kind) {
+ if (!log_->IsEnabled() || !FLAG_log_gc) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("heap-sample-end,\"%s\",\"%s\"\n", space, kind);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::HeapSampleItemEvent(const char* type, int number, int bytes) {
+ if (!log_->IsEnabled() || !FLAG_log_gc) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("heap-sample-item,%s,%d,%d\n", type, number, bytes);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::DebugTag(const char* call_site_tag) {
+ if (!log_->IsEnabled() || !FLAG_log) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("debug-tag,%s\n", call_site_tag);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::DebugEvent(const char* event_type, Vector<uint16_t> parameter) {
+ if (!log_->IsEnabled() || !FLAG_log) return;
+ StringBuilder s(parameter.length() + 1);
+ for (int i = 0; i < parameter.length(); ++i) {
+ s.AddCharacter(static_cast<char>(parameter[i]));
+ }
+ char* parameter_string = s.Finalize();
+ Log::MessageBuilder msg(log_);
+ msg.Append("debug-queue-event,%s,%15.3f,%s\n",
+ event_type,
+ OS::TimeCurrentMillis(),
+ parameter_string);
+ DeleteArray(parameter_string);
+ msg.WriteToLogFile();
+}
+
+
+void Logger::TickEvent(TickSample* sample, bool overflow) {
+ if (!log_->IsEnabled() || !FLAG_prof) return;
+ Log::MessageBuilder msg(log_);
+ msg.Append("%s,", kLogEventsNames[TICK_EVENT]);
+ msg.AppendAddress(sample->pc);
+ msg.Append(",%ld", static_cast<int>(OS::Ticks() - epoch_));
+ if (sample->has_external_callback) {
+ msg.Append(",1,");
+ msg.AppendAddress(sample->external_callback);
+ } else {
+ msg.Append(",0,");
+ msg.AppendAddress(sample->tos);
+ }
+ msg.Append(",%d", static_cast<int>(sample->state));
+ if (overflow) {
+ msg.Append(",overflow");
+ }
+ for (int i = 0; i < sample->frames_count; ++i) {
+ msg.Append(',');
+ msg.AppendAddress(sample->stack[i]);
+ }
+ msg.Append('\n');
+ msg.WriteToLogFile();
+}
+
+
+bool Logger::IsProfilerPaused() {
+ return profiler_ == NULL || profiler_->paused();
+}
+
+
+void Logger::PauseProfiler() {
+ if (!log_->IsEnabled()) return;
+ if (profiler_ != NULL) {
+ // It is OK to have negative nesting.
+ if (--cpu_profiler_nesting_ == 0) {
+ profiler_->pause();
+ if (FLAG_prof_lazy) {
+ ticker_->Stop();
+ FLAG_log_code = false;
+ LOG(ISOLATE, UncheckedStringEvent("profiler", "pause"));
+ }
+ --logging_nesting_;
+ }
+ }
+}
+
+
+void Logger::ResumeProfiler() {
+ if (!log_->IsEnabled()) return;
+ if (profiler_ != NULL) {
+ if (cpu_profiler_nesting_++ == 0) {
+ ++logging_nesting_;
+ if (FLAG_prof_lazy) {
+ profiler_->Engage();
+ LOG(ISOLATE, UncheckedStringEvent("profiler", "resume"));
+ FLAG_log_code = true;
+ LogCompiledFunctions();
+ LogAccessorCallbacks();
+ if (!ticker_->IsActive()) ticker_->Start();
+ }
+ profiler_->resume();
+ }
+ }
+}
+
+
+// This function can be called when Log's mutex is acquired,
+// either from main or Profiler's thread.
+void Logger::LogFailure() {
+ PauseProfiler();
+}
+
+
+class EnumerateOptimizedFunctionsVisitor: public OptimizedFunctionVisitor {
+ public:
+ EnumerateOptimizedFunctionsVisitor(Handle<SharedFunctionInfo>* sfis,
+ Handle<Code>* code_objects,
+ int* count)
+ : sfis_(sfis), code_objects_(code_objects), count_(count) { }
+
+ virtual void EnterContext(Context* context) {}
+ virtual void LeaveContext(Context* context) {}
+
+ virtual void VisitFunction(JSFunction* function) {
+ SharedFunctionInfo* sfi = SharedFunctionInfo::cast(function->shared());
+ Object* maybe_script = sfi->script();
+ if (maybe_script->IsScript()
+ && !Script::cast(maybe_script)->HasValidSource()) return;
+ if (sfis_ != NULL) {
+ sfis_[*count_] = Handle<SharedFunctionInfo>(sfi);
+ }
+ if (code_objects_ != NULL) {
+ ASSERT(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
+ code_objects_[*count_] = Handle<Code>(function->code());
+ }
+ *count_ = *count_ + 1;
+ }
+
+ private:
+ Handle<SharedFunctionInfo>* sfis_;
+ Handle<Code>* code_objects_;
+ int* count_;
+};
+
+
+static int EnumerateCompiledFunctions(Heap* heap,
+ Handle<SharedFunctionInfo>* sfis,
+ Handle<Code>* code_objects) {
+ HeapIterator iterator(heap);
+ DisallowHeapAllocation no_gc;
+ int compiled_funcs_count = 0;
+
+ // Iterate the heap to find shared function info objects and record
+ // the unoptimized code for them.
+ for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
+ if (!obj->IsSharedFunctionInfo()) continue;
+ SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
+ if (sfi->is_compiled()
+ && (!sfi->script()->IsScript()
+ || Script::cast(sfi->script())->HasValidSource())) {
+ if (sfis != NULL) {
+ sfis[compiled_funcs_count] = Handle<SharedFunctionInfo>(sfi);
+ }
+ if (code_objects != NULL) {
+ code_objects[compiled_funcs_count] = Handle<Code>(sfi->code());
+ }
+ ++compiled_funcs_count;
+ }
+ }
+
+ // Iterate all optimized functions in all contexts.
+ EnumerateOptimizedFunctionsVisitor visitor(sfis,
+ code_objects,
+ &compiled_funcs_count);
+ Deoptimizer::VisitAllOptimizedFunctions(heap->isolate(), &visitor);
+
+ return compiled_funcs_count;
+}
+
+
+void Logger::LogCodeObject(Object* object) {
+ Code* code_object = Code::cast(object);
+ LogEventsAndTags tag = Logger::STUB_TAG;
+ const char* description = "Unknown code from the snapshot";
+ switch (code_object->kind()) {
+ case Code::FUNCTION:
+ case Code::OPTIMIZED_FUNCTION:
+ return; // We log this later using LogCompiledFunctions.
+ case Code::BINARY_OP_IC: // fall through
+ case Code::COMPARE_IC: // fall through
+ case Code::COMPARE_NIL_IC: // fall through
+ case Code::TO_BOOLEAN_IC: // fall through
+ case Code::STUB:
+ description =
+ CodeStub::MajorName(CodeStub::GetMajorKey(code_object), true);
+ if (description == NULL)
+ description = "A stub from the snapshot";
+ tag = Logger::STUB_TAG;
+ break;
+ case Code::REGEXP:
+ description = "Regular expression code";
+ tag = Logger::REG_EXP_TAG;
+ break;
+ case Code::BUILTIN:
+ description = "A builtin from the snapshot";
+ tag = Logger::BUILTIN_TAG;
+ break;
+ case Code::KEYED_LOAD_IC:
+ description = "A keyed load IC from the snapshot";
+ tag = Logger::KEYED_LOAD_IC_TAG;
+ break;
+ case Code::LOAD_IC:
+ description = "A load IC from the snapshot";
+ tag = Logger::LOAD_IC_TAG;
+ break;
+ case Code::STORE_IC:
+ description = "A store IC from the snapshot";
+ tag = Logger::STORE_IC_TAG;
+ break;
+ case Code::KEYED_STORE_IC:
+ description = "A keyed store IC from the snapshot";
+ tag = Logger::KEYED_STORE_IC_TAG;
+ break;
+ case Code::CALL_IC:
+ description = "A call IC from the snapshot";
+ tag = Logger::CALL_IC_TAG;
+ break;
+ case Code::KEYED_CALL_IC:
+ description = "A keyed call IC from the snapshot";
+ tag = Logger::KEYED_CALL_IC_TAG;
+ break;
+ case Code::NUMBER_OF_KINDS:
+ break;
+ }
+ PROFILE(isolate_, CodeCreateEvent(tag, code_object, description));
+}
+
+
+void Logger::LogCodeObjects() {
+ Heap* heap = isolate_->heap();
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask,
+ "Logger::LogCodeObjects");
+ HeapIterator iterator(heap);
+ DisallowHeapAllocation no_gc;
+ for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
+ if (obj->IsCode()) LogCodeObject(obj);
+ }
+}
+
+
+void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
+ Handle<Code> code) {
+ Handle<String> func_name(shared->DebugName());
+ if (shared->script()->IsScript()) {
+ Handle<Script> script(Script::cast(shared->script()));
+ int line_num = GetScriptLineNumber(script, shared->start_position()) + 1;
+ if (script->name()->IsString()) {
+ Handle<String> script_name(String::cast(script->name()));
+ if (line_num > 0) {
+ PROFILE(isolate_,
+ CodeCreateEvent(
+ Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
+ *code, *shared, NULL,
+ *script_name, line_num));
+ } else {
+ // Can't distinguish eval and script here, so always use Script.
+ PROFILE(isolate_,
+ CodeCreateEvent(
+ Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
+ *code, *shared, NULL, *script_name));
+ }
+ } else {
+ PROFILE(isolate_,
+ CodeCreateEvent(
+ Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
+ *code, *shared, NULL,
+ isolate_->heap()->empty_string(), line_num));
+ }
+ } else if (shared->IsApiFunction()) {
+ // API function.
+ FunctionTemplateInfo* fun_data = shared->get_api_func_data();
+ Object* raw_call_data = fun_data->call_code();
+ if (!raw_call_data->IsUndefined()) {
+ CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
+ Object* callback_obj = call_data->callback();
+ Address entry_point = v8::ToCData<Address>(callback_obj);
+ PROFILE(isolate_, CallbackEvent(*func_name, entry_point));
+ }
+ } else {
+ PROFILE(isolate_,
+ CodeCreateEvent(
+ Logger::LAZY_COMPILE_TAG, *code, *shared, NULL, *func_name));
+ }
+}
+
+
+void Logger::LogCompiledFunctions() {
+ Heap* heap = isolate_->heap();
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask,
+ "Logger::LogCompiledFunctions");
+ HandleScope scope(isolate_);
+ const int compiled_funcs_count = EnumerateCompiledFunctions(heap, NULL, NULL);
+ ScopedVector< Handle<SharedFunctionInfo> > sfis(compiled_funcs_count);
+ ScopedVector< Handle<Code> > code_objects(compiled_funcs_count);
+ EnumerateCompiledFunctions(heap, sfis.start(), code_objects.start());
+
+ // During iteration, there can be heap allocation due to
+ // GetScriptLineNumber call.
+ for (int i = 0; i < compiled_funcs_count; ++i) {
+ if (*code_objects[i] == isolate_->builtins()->builtin(
+ Builtins::kLazyCompile))
+ continue;
+ LogExistingFunction(sfis[i], code_objects[i]);
+ }
+}
+
+
+void Logger::LogAccessorCallbacks() {
+ Heap* heap = isolate_->heap();
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask,
+ "Logger::LogAccessorCallbacks");
+ HeapIterator iterator(heap);
+ DisallowHeapAllocation no_gc;
+ for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
+ if (!obj->IsExecutableAccessorInfo()) continue;
+ ExecutableAccessorInfo* ai = ExecutableAccessorInfo::cast(obj);
+ if (!ai->name()->IsName()) continue;
+ Address getter_entry = v8::ToCData<Address>(ai->getter());
+ Name* name = Name::cast(ai->name());
+ if (getter_entry != 0) {
+ PROFILE(isolate_, GetterCallbackEvent(name, getter_entry));
+ }
+ Address setter_entry = v8::ToCData<Address>(ai->setter());
+ if (setter_entry != 0) {
+ PROFILE(isolate_, SetterCallbackEvent(name, setter_entry));
+ }
+ }
+}
+
+
+static void AddIsolateIdIfNeeded(StringStream* stream) {
+ Isolate* isolate = Isolate::Current();
+ if (isolate->IsDefaultIsolate()) return;
+ stream->Add("isolate-%p-", isolate);
+}
+
+
+static SmartArrayPointer<const char> PrepareLogFileName(const char* file_name) {
+ if (strchr(file_name, '%') != NULL ||
+ !Isolate::Current()->IsDefaultIsolate()) {
+ // If there's a '%' in the log file name we have to expand
+ // placeholders.
+ HeapStringAllocator allocator;
+ StringStream stream(&allocator);
+ AddIsolateIdIfNeeded(&stream);
+ for (const char* p = file_name; *p; p++) {
+ if (*p == '%') {
+ p++;
+ switch (*p) {
+ case '\0':
+ // If there's a % at the end of the string we back up
+ // one character so we can escape the loop properly.
+ p--;
+ break;
+ case 'p':
+ stream.Add("%d", OS::GetCurrentProcessId());
+ break;
+ case 't': {
+ // %t expands to the current time in milliseconds.
+ double time = OS::TimeCurrentMillis();
+ stream.Add("%.0f", FmtElm(time));
+ break;
+ }
+ case '%':
+ // %% expands (contracts really) to %.
+ stream.Put('%');
+ break;
+ default:
+ // All other %'s expand to themselves.
+ stream.Put('%');
+ stream.Put(*p);
+ break;
+ }
+ } else {
+ stream.Put(*p);
+ }
+ }
+ return SmartArrayPointer<const char>(stream.ToCString());
+ }
+ int length = StrLength(file_name);
+ char* str = NewArray<char>(length + 1);
+ OS::MemCopy(str, file_name, length);
+ str[length] = '\0';
+ return SmartArrayPointer<const char>(str);
+}
+
+
+bool Logger::SetUp(Isolate* isolate) {
+ // Tests and EnsureInitialize() can call this twice in a row. It's harmless.
+ if (is_initialized_) return true;
+ is_initialized_ = true;
+
+ // --ll-prof implies --log-code and --log-snapshot-positions.
+ if (FLAG_ll_prof) {
+ FLAG_log_snapshot_positions = true;
+ }
+
+ // --prof_lazy controls --log-code.
+ if (FLAG_prof_lazy) {
+ FLAG_log_code = false;
+ }
+
+ SmartArrayPointer<const char> log_file_name =
+ PrepareLogFileName(FLAG_logfile);
+ log_->Initialize(*log_file_name);
+
+ if (FLAG_ll_prof) {
+ ll_logger_ = new LowLevelLogger(*log_file_name);
+ addCodeEventListener(ll_logger_);
+ }
+
+ ticker_ = new Ticker(isolate, kSamplingIntervalMs);
+
+ if (Log::InitLogAtStart()) {
+ logging_nesting_ = 1;
+ }
+
+ if (FLAG_prof) {
+ profiler_ = new Profiler(isolate);
+ if (FLAG_prof_lazy) {
+ profiler_->pause();
+ } else {
+ logging_nesting_ = 1;
+ profiler_->Engage();
+ }
+ }
+
+ if (FLAG_log_internal_timer_events || FLAG_prof) epoch_ = OS::Ticks();
+
+ return true;
+}
+
+
+void Logger::SetCodeEventHandler(uint32_t options,
+ JitCodeEventHandler event_handler) {
+ if (jit_logger_) {
+ removeCodeEventListener(jit_logger_);
+ delete jit_logger_;
+ jit_logger_ = NULL;
+ }
+
+ if (event_handler) {
+ jit_logger_ = new JitLogger(event_handler);
+ addCodeEventListener(jit_logger_);
+ if (options & kJitCodeEventEnumExisting) {
+ HandleScope scope(isolate_);
+ LogCodeObjects();
+ LogCompiledFunctions();
+ }
+ }
+}
+
+
+Sampler* Logger::sampler() {
+ return ticker_;
+}
+
+
+FILE* Logger::TearDown() {
+ if (!is_initialized_) return NULL;
+ is_initialized_ = false;
+
+ // Stop the profiler before closing the file.
+ if (profiler_ != NULL) {
+ profiler_->Disengage();
+ delete profiler_;
+ profiler_ = NULL;
+ }
+
+ delete ticker_;
+ ticker_ = NULL;
+
+ if (ll_logger_) {
+ removeCodeEventListener(ll_logger_);
+ delete ll_logger_;
+ ll_logger_ = NULL;
+ }
+
+ if (jit_logger_) {
+ removeCodeEventListener(jit_logger_);
+ delete jit_logger_;
+ jit_logger_ = NULL;
+ }
+
+ return log_->Close();
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/log.h b/chromium/v8/src/log.h
new file mode 100644
index 00000000000..24d83ef1277
--- /dev/null
+++ b/chromium/v8/src/log.h
@@ -0,0 +1,542 @@
+// Copyright 2012 the V8 project authors. 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_LOG_H_
+#define V8_LOG_H_
+
+#include "allocation.h"
+#include "objects.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+// Logger is used for collecting logging information from V8 during
+// execution. The result is dumped to a file.
+//
+// Available command line flags:
+//
+// --log
+// Minimal logging (no API, code, or GC sample events), default is off.
+//
+// --log-all
+// Log all events to the file, default is off. This is the same as combining
+// --log-api, --log-code, --log-gc, and --log-regexp.
+//
+// --log-api
+// Log API events to the logfile, default is off. --log-api implies --log.
+//
+// --log-code
+// Log code (create, move, and delete) events to the logfile, default is off.
+// --log-code implies --log.
+//
+// --log-gc
+// Log GC heap samples after each GC that can be processed by hp2ps, default
+// is off. --log-gc implies --log.
+//
+// --log-regexp
+// Log creation and use of regular expressions, Default is off.
+// --log-regexp implies --log.
+//
+// --logfile <filename>
+// Specify the name of the logfile, default is "v8.log".
+//
+// --prof
+// Collect statistical profiling information (ticks), default is off. The
+// tick profiler requires code events, so --prof implies --log-code.
+
+// Forward declarations.
+class CodeEventListener;
+class CompilationInfo;
+class CpuProfiler;
+class Isolate;
+class Log;
+class PositionsRecorder;
+class Profiler;
+class Semaphore;
+class Ticker;
+struct TickSample;
+
+#undef LOG
+#define LOG(isolate, Call) \
+ do { \
+ v8::internal::Logger* logger = \
+ (isolate)->logger(); \
+ if (logger->is_logging()) \
+ logger->Call; \
+ } while (false)
+
+#define LOG_CODE_EVENT(isolate, Call) \
+ do { \
+ v8::internal::Logger* logger = \
+ (isolate)->logger(); \
+ if (logger->is_logging_code_events()) \
+ logger->Call; \
+ } while (false)
+
+
+#define LOG_EVENTS_AND_TAGS_LIST(V) \
+ V(CODE_CREATION_EVENT, "code-creation") \
+ V(CODE_MOVE_EVENT, "code-move") \
+ V(CODE_DELETE_EVENT, "code-delete") \
+ V(CODE_MOVING_GC, "code-moving-gc") \
+ V(SHARED_FUNC_MOVE_EVENT, "sfi-move") \
+ V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \
+ V(SNAPSHOT_CODE_NAME_EVENT, "snapshot-code-name") \
+ V(TICK_EVENT, "tick") \
+ V(REPEAT_META_EVENT, "repeat") \
+ V(BUILTIN_TAG, "Builtin") \
+ V(CALL_DEBUG_BREAK_TAG, "CallDebugBreak") \
+ V(CALL_DEBUG_PREPARE_STEP_IN_TAG, "CallDebugPrepareStepIn") \
+ V(CALL_IC_TAG, "CallIC") \
+ V(CALL_INITIALIZE_TAG, "CallInitialize") \
+ V(CALL_MEGAMORPHIC_TAG, "CallMegamorphic") \
+ V(CALL_MISS_TAG, "CallMiss") \
+ V(CALL_NORMAL_TAG, "CallNormal") \
+ V(CALL_PRE_MONOMORPHIC_TAG, "CallPreMonomorphic") \
+ V(KEYED_CALL_DEBUG_BREAK_TAG, "KeyedCallDebugBreak") \
+ V(KEYED_CALL_DEBUG_PREPARE_STEP_IN_TAG, \
+ "KeyedCallDebugPrepareStepIn") \
+ V(KEYED_CALL_IC_TAG, "KeyedCallIC") \
+ V(KEYED_CALL_INITIALIZE_TAG, "KeyedCallInitialize") \
+ V(KEYED_CALL_MEGAMORPHIC_TAG, "KeyedCallMegamorphic") \
+ V(KEYED_CALL_MISS_TAG, "KeyedCallMiss") \
+ V(KEYED_CALL_NORMAL_TAG, "KeyedCallNormal") \
+ V(KEYED_CALL_PRE_MONOMORPHIC_TAG, "KeyedCallPreMonomorphic") \
+ V(CALLBACK_TAG, "Callback") \
+ V(EVAL_TAG, "Eval") \
+ V(FUNCTION_TAG, "Function") \
+ V(KEYED_LOAD_IC_TAG, "KeyedLoadIC") \
+ V(KEYED_LOAD_POLYMORPHIC_IC_TAG, "KeyedLoadPolymorphicIC") \
+ V(KEYED_EXTERNAL_ARRAY_LOAD_IC_TAG, "KeyedExternalArrayLoadIC") \
+ V(KEYED_STORE_IC_TAG, "KeyedStoreIC") \
+ V(KEYED_STORE_POLYMORPHIC_IC_TAG, "KeyedStorePolymorphicIC") \
+ V(KEYED_EXTERNAL_ARRAY_STORE_IC_TAG, "KeyedExternalArrayStoreIC") \
+ V(LAZY_COMPILE_TAG, "LazyCompile") \
+ V(LOAD_IC_TAG, "LoadIC") \
+ V(LOAD_POLYMORPHIC_IC_TAG, "LoadPolymorphicIC") \
+ V(REG_EXP_TAG, "RegExp") \
+ V(SCRIPT_TAG, "Script") \
+ V(STORE_IC_TAG, "StoreIC") \
+ V(STORE_POLYMORPHIC_IC_TAG, "StorePolymorphicIC") \
+ V(STUB_TAG, "Stub") \
+ V(NATIVE_FUNCTION_TAG, "Function") \
+ V(NATIVE_LAZY_COMPILE_TAG, "LazyCompile") \
+ V(NATIVE_SCRIPT_TAG, "Script")
+// Note that 'NATIVE_' cases for functions and scripts are mapped onto
+// original tags when writing to the log.
+
+
+class JitLogger;
+class LowLevelLogger;
+class Sampler;
+
+class Logger {
+ public:
+#define DECLARE_ENUM(enum_item, ignore) enum_item,
+ enum LogEventsAndTags {
+ LOG_EVENTS_AND_TAGS_LIST(DECLARE_ENUM)
+ NUMBER_OF_LOG_EVENTS
+ };
+#undef DECLARE_ENUM
+
+ // Acquires resources for logging if the right flags are set.
+ bool SetUp(Isolate* isolate);
+
+ // Sets the current code event handler.
+ void SetCodeEventHandler(uint32_t options,
+ JitCodeEventHandler event_handler);
+
+ Sampler* sampler();
+
+ // Frees resources acquired in SetUp.
+ // When a temporary file is used for the log, returns its stream descriptor,
+ // leaving the file open.
+ FILE* TearDown();
+
+ // Emits an event with a string value -> (name, value).
+ void StringEvent(const char* name, const char* value);
+
+ // Emits an event with an int value -> (name, value).
+ void IntEvent(const char* name, int value);
+ void IntPtrTEvent(const char* name, intptr_t value);
+
+ // Emits an event with an handle value -> (name, location).
+ void HandleEvent(const char* name, Object** location);
+
+ // Emits memory management events for C allocated structures.
+ void NewEvent(const char* name, void* object, size_t size);
+ void DeleteEvent(const char* name, void* object);
+
+ // Static versions of the above, operate on current isolate's logger.
+ // Used in TRACK_MEMORY(TypeName) defined in globals.h
+ static void NewEventStatic(const char* name, void* object, size_t size);
+ static void DeleteEventStatic(const char* name, void* object);
+
+ // Emits an event with a tag, and some resource usage information.
+ // -> (name, tag, <rusage information>).
+ // Currently, the resource usage information is a process time stamp
+ // and a real time timestamp.
+ void ResourceEvent(const char* name, const char* tag);
+
+ // Emits an event that an undefined property was read from an
+ // object.
+ void SuspectReadEvent(Name* name, Object* obj);
+
+ // Emits an event when a message is put on or read from a debugging queue.
+ // DebugTag lets us put a call-site specific label on the event.
+ void DebugTag(const char* call_site_tag);
+ void DebugEvent(const char* event_type, Vector<uint16_t> parameter);
+
+
+ // ==== Events logged by --log-api. ====
+ void ApiNamedSecurityCheck(Object* key);
+ void ApiIndexedSecurityCheck(uint32_t index);
+ void ApiNamedPropertyAccess(const char* tag, JSObject* holder, Object* name);
+ void ApiIndexedPropertyAccess(const char* tag,
+ JSObject* holder,
+ uint32_t index);
+ void ApiObjectAccess(const char* tag, JSObject* obj);
+ void ApiEntryCall(const char* name);
+
+
+ // ==== Events logged by --log-code. ====
+ void addCodeEventListener(CodeEventListener* listener);
+ void removeCodeEventListener(CodeEventListener* listener);
+ bool hasCodeEventListener(CodeEventListener* listener);
+
+
+ // Emits a code event for a callback function.
+ void CallbackEvent(Name* name, Address entry_point);
+ void GetterCallbackEvent(Name* name, Address entry_point);
+ void SetterCallbackEvent(Name* name, Address entry_point);
+ // Emits a code create event.
+ void CodeCreateEvent(LogEventsAndTags tag,
+ Code* code, const char* source);
+ void CodeCreateEvent(LogEventsAndTags tag,
+ Code* code, Name* name);
+ void CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* name);
+ void CodeCreateEvent(LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* source, int line);
+ void CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count);
+ void CodeMovingGCEvent();
+ // Emits a code create event for a RegExp.
+ void RegExpCodeCreateEvent(Code* code, String* source);
+ // Emits a code move event.
+ void CodeMoveEvent(Address from, Address to);
+ // Emits a code delete event.
+ void CodeDeleteEvent(Address from);
+ // Emits a code line info add event with Postion type.
+ void CodeLinePosInfoAddPositionEvent(void* jit_handler_data,
+ int pc_offset,
+ int position);
+ // Emits a code line info add event with StatementPostion type.
+ void CodeLinePosInfoAddStatementPositionEvent(void* jit_handler_data,
+ int pc_offset,
+ int position);
+ // Emits a code line info start to record event
+ void CodeStartLinePosInfoRecordEvent(PositionsRecorder* pos_recorder);
+ // Emits a code line info finish record event.
+ // It's the callee's responsibility to dispose the parameter jit_handler_data.
+ void CodeEndLinePosInfoRecordEvent(Code* code, void* jit_handler_data);
+
+ void SharedFunctionInfoMoveEvent(Address from, Address to);
+
+ void CodeNameEvent(Address addr, int pos, const char* code_name);
+ void SnapshotPositionEvent(Address addr, int pos);
+
+ // ==== Events logged by --log-gc. ====
+ // Heap sampling events: start, end, and individual types.
+ void HeapSampleBeginEvent(const char* space, const char* kind);
+ void HeapSampleEndEvent(const char* space, const char* kind);
+ void HeapSampleItemEvent(const char* type, int number, int bytes);
+ void HeapSampleJSConstructorEvent(const char* constructor,
+ int number, int bytes);
+ void HeapSampleJSRetainersEvent(const char* constructor,
+ const char* event);
+ void HeapSampleJSProducerEvent(const char* constructor,
+ Address* stack);
+ void HeapSampleStats(const char* space, const char* kind,
+ intptr_t capacity, intptr_t used);
+
+ void SharedLibraryEvent(const char* library_path,
+ uintptr_t start,
+ uintptr_t end);
+ void SharedLibraryEvent(const wchar_t* library_path,
+ uintptr_t start,
+ uintptr_t end);
+
+ // ==== Events logged by --log-timer-events. ====
+ enum StartEnd { START, END };
+
+ void CodeDeoptEvent(Code* code);
+
+ void TimerEvent(StartEnd se, const char* name);
+
+ static void EnterExternal(Isolate* isolate);
+ static void LeaveExternal(Isolate* isolate);
+
+ class TimerEventScope {
+ public:
+ TimerEventScope(Isolate* isolate, const char* name)
+ : isolate_(isolate), name_(name) {
+ if (FLAG_log_internal_timer_events) LogTimerEvent(START);
+ }
+
+ ~TimerEventScope() {
+ if (FLAG_log_internal_timer_events) LogTimerEvent(END);
+ }
+
+ void LogTimerEvent(StartEnd se);
+
+ static const char* v8_recompile_synchronous;
+ static const char* v8_recompile_parallel;
+ static const char* v8_compile_full_code;
+ static const char* v8_execute;
+ static const char* v8_external;
+
+ private:
+ Isolate* isolate_;
+ const char* name_;
+ };
+
+ // ==== Events logged by --log-regexp ====
+ // Regexp compilation and execution events.
+
+ void RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache);
+
+ // Log an event reported from generated code
+ void LogRuntime(Vector<const char> format, JSArray* args);
+
+ bool is_logging() {
+ return logging_nesting_ > 0;
+ }
+
+ bool is_logging_code_events() {
+ return is_logging() || jit_logger_ != NULL;
+ }
+
+ // Pause/Resume collection of profiling data.
+ // When data collection is paused, CPU Tick events are discarded until
+ // data collection is Resumed.
+ void PauseProfiler();
+ void ResumeProfiler();
+ bool IsProfilerPaused();
+
+ void LogExistingFunction(Handle<SharedFunctionInfo> shared,
+ Handle<Code> code);
+ // Logs all compiled functions found in the heap.
+ void LogCompiledFunctions();
+ // Logs all accessor callbacks found in the heap.
+ void LogAccessorCallbacks();
+ // Used for logging stubs found in the snapshot.
+ void LogCodeObjects();
+
+ // Converts tag to a corresponding NATIVE_... if the script is native.
+ INLINE(static LogEventsAndTags ToNativeByScript(LogEventsAndTags, Script*));
+
+ // Profiler's sampling interval (in milliseconds).
+#if defined(ANDROID)
+ // Phones and tablets have processors that are much slower than desktop
+ // and laptop computers for which current heuristics are tuned.
+ static const int kSamplingIntervalMs = 5;
+#else
+ static const int kSamplingIntervalMs = 1;
+#endif
+
+ // Callback from Log, stops profiling in case of insufficient resources.
+ void LogFailure();
+
+ private:
+ explicit Logger(Isolate* isolate);
+ ~Logger();
+
+ // Emits the profiler's first message.
+ void ProfilerBeginEvent();
+
+ // Emits callback event messages.
+ void CallbackEventInternal(const char* prefix,
+ Name* name,
+ Address entry_point);
+
+ // Internal configurable move event.
+ void MoveEventInternal(LogEventsAndTags event, Address from, Address to);
+
+ // Emits the source code of a regexp. Used by regexp events.
+ void LogRegExpSource(Handle<JSRegExp> regexp);
+
+ // Used for logging stubs found in the snapshot.
+ void LogCodeObject(Object* code_object);
+
+ // Helper method. It resets name_buffer_ and add tag name into it.
+ void InitNameBuffer(LogEventsAndTags tag);
+
+ // Emits a profiler tick event. Used by the profiler thread.
+ void TickEvent(TickSample* sample, bool overflow);
+
+ void ApiEvent(const char* name, ...);
+
+ // Logs a StringEvent regardless of whether FLAG_log is true.
+ void UncheckedStringEvent(const char* name, const char* value);
+
+ // Logs an IntEvent regardless of whether FLAG_log is true.
+ void UncheckedIntEvent(const char* name, int value);
+ void UncheckedIntPtrTEvent(const char* name, intptr_t value);
+
+ Isolate* isolate_;
+
+ // The sampler used by the profiler and the sliding state window.
+ Ticker* ticker_;
+
+ // When the statistical profile is active, profiler_
+ // points to a Profiler, that handles collection
+ // of samples.
+ Profiler* profiler_;
+
+ // An array of log events names.
+ const char* const* log_events_;
+
+ // Internal implementation classes with access to
+ // private members.
+ friend class EventLog;
+ friend class Isolate;
+ friend class TimeLog;
+ friend class Profiler;
+ template <StateTag Tag> friend class VMState;
+
+ friend class LoggerTestHelper;
+
+
+ int logging_nesting_;
+ int cpu_profiler_nesting_;
+
+ Log* log_;
+ LowLevelLogger* ll_logger_;
+ JitLogger* jit_logger_;
+ List<CodeEventListener*> listeners_;
+
+ // Guards against multiple calls to TearDown() that can happen in some tests.
+ // 'true' between SetUp() and TearDown().
+ bool is_initialized_;
+
+ int64_t epoch_;
+
+ friend class CpuProfiler;
+};
+
+
+class CodeEventListener {
+ public:
+ virtual ~CodeEventListener() {}
+
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ const char* comment) = 0;
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ Name* name) = 0;
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* name) = 0;
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* source,
+ int line) = 0;
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ int args_count) = 0;
+ virtual void CallbackEvent(Name* name, Address entry_point) = 0;
+ virtual void GetterCallbackEvent(Name* name, Address entry_point) = 0;
+ virtual void SetterCallbackEvent(Name* name, Address entry_point) = 0;
+ virtual void RegExpCodeCreateEvent(Code* code, String* source) = 0;
+ virtual void CodeMoveEvent(Address from, Address to) = 0;
+ virtual void CodeDeleteEvent(Address from) = 0;
+ virtual void SharedFunctionInfoMoveEvent(Address from, Address to) = 0;
+ virtual void CodeMovingGCEvent() = 0;
+};
+
+
+class CodeEventLogger : public CodeEventListener {
+ public:
+ CodeEventLogger();
+ virtual ~CodeEventLogger();
+
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ const char* comment);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ Name* name);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ int args_count);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* name);
+ virtual void CodeCreateEvent(Logger::LogEventsAndTags tag,
+ Code* code,
+ SharedFunctionInfo* shared,
+ CompilationInfo* info,
+ Name* source,
+ int line);
+ virtual void RegExpCodeCreateEvent(Code* code, String* source);
+
+ virtual void CallbackEvent(Name* name, Address entry_point) { }
+ virtual void GetterCallbackEvent(Name* name, Address entry_point) { }
+ virtual void SetterCallbackEvent(Name* name, Address entry_point) { }
+ virtual void SharedFunctionInfoMoveEvent(Address from, Address to) { }
+ virtual void CodeMovingGCEvent() { }
+
+ private:
+ class NameBuffer;
+
+ virtual void LogRecordedBuffer(Code* code,
+ SharedFunctionInfo* shared,
+ const char* name,
+ int length) = 0;
+
+ NameBuffer* name_buffer_;
+};
+
+
+} } // namespace v8::internal
+
+
+#endif // V8_LOG_H_
diff --git a/chromium/v8/src/macro-assembler.h b/chromium/v8/src/macro-assembler.h
new file mode 100644
index 00000000000..9fdf2ee7d86
--- /dev/null
+++ b/chromium/v8/src/macro-assembler.h
@@ -0,0 +1,206 @@
+// Copyright 2012 the V8 project authors. 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_MACRO_ASSEMBLER_H_
+#define V8_MACRO_ASSEMBLER_H_
+
+
+// Helper types to make boolean flag easier to read at call-site.
+enum InvokeFlag {
+ CALL_FUNCTION,
+ JUMP_FUNCTION
+};
+
+
+// Flags used for the AllocateInNewSpace functions.
+enum AllocationFlags {
+ // No special flags.
+ NO_ALLOCATION_FLAGS = 0,
+ // Return the pointer to the allocated already tagged as a heap object.
+ TAG_OBJECT = 1 << 0,
+ // The content of the result register already contains the allocation top in
+ // new space.
+ RESULT_CONTAINS_TOP = 1 << 1,
+ // Specify that the requested size of the space to allocate is specified in
+ // words instead of bytes.
+ SIZE_IN_WORDS = 1 << 2,
+ // Align the allocation to a multiple of kDoubleSize
+ DOUBLE_ALIGNMENT = 1 << 3,
+ // Directly allocate in old pointer space
+ PRETENURE_OLD_POINTER_SPACE = 1 << 4,
+ // Directly allocate in old data space
+ PRETENURE_OLD_DATA_SPACE = 1 << 5
+};
+
+
+// Invalid depth in prototype chain.
+const int kInvalidProtoDepth = -1;
+
+#if V8_TARGET_ARCH_IA32
+#include "assembler.h"
+#include "ia32/assembler-ia32.h"
+#include "ia32/assembler-ia32-inl.h"
+#include "code.h" // must be after assembler_*.h
+#include "ia32/macro-assembler-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "assembler.h"
+#include "x64/assembler-x64.h"
+#include "x64/assembler-x64-inl.h"
+#include "code.h" // must be after assembler_*.h
+#include "x64/macro-assembler-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/constants-arm.h"
+#include "assembler.h"
+#include "arm/assembler-arm.h"
+#include "arm/assembler-arm-inl.h"
+#include "code.h" // must be after assembler_*.h
+#include "arm/macro-assembler-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/constants-mips.h"
+#include "assembler.h"
+#include "mips/assembler-mips.h"
+#include "mips/assembler-mips-inl.h"
+#include "code.h" // must be after assembler_*.h
+#include "mips/macro-assembler-mips.h"
+#else
+#error Unsupported target architecture.
+#endif
+
+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
+
+class Comment {
+ public:
+ Comment(MacroAssembler* masm, const char* msg);
+ ~Comment();
+
+ private:
+ MacroAssembler* masm_;
+ const char* msg_;
+};
+
+#else
+
+class Comment {
+ public:
+ Comment(MacroAssembler*, const char*) {}
+};
+
+#endif // DEBUG
+
+
+class AllocationUtils {
+ public:
+ static ExternalReference GetAllocationTopReference(
+ Isolate* isolate, AllocationFlags flags) {
+ if ((flags & PRETENURE_OLD_POINTER_SPACE) != 0) {
+ return ExternalReference::old_pointer_space_allocation_top_address(
+ isolate);
+ } else if ((flags & PRETENURE_OLD_DATA_SPACE) != 0) {
+ return ExternalReference::old_data_space_allocation_top_address(isolate);
+ }
+ return ExternalReference::new_space_allocation_top_address(isolate);
+ }
+
+
+ static ExternalReference GetAllocationLimitReference(
+ Isolate* isolate, AllocationFlags flags) {
+ if ((flags & PRETENURE_OLD_POINTER_SPACE) != 0) {
+ return ExternalReference::old_pointer_space_allocation_limit_address(
+ isolate);
+ } else if ((flags & PRETENURE_OLD_DATA_SPACE) != 0) {
+ return ExternalReference::old_data_space_allocation_limit_address(
+ isolate);
+ }
+ return ExternalReference::new_space_allocation_limit_address(isolate);
+ }
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_MACRO_ASSEMBLER_H_
diff --git a/chromium/v8/src/macros.py b/chromium/v8/src/macros.py
new file mode 100644
index 00000000000..d50231dcefc
--- /dev/null
+++ b/chromium/v8/src/macros.py
@@ -0,0 +1,248 @@
+# Copyright 2006-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.
+
+# Dictionary that is passed as defines for js2c.py.
+# Used for defines that must be defined for all native JS files.
+
+const NONE = 0;
+const READ_ONLY = 1;
+const DONT_ENUM = 2;
+const DONT_DELETE = 4;
+const NEW_ONE_BYTE_STRING = true;
+const NEW_TWO_BYTE_STRING = false;
+
+# Constants used for getter and setter operations.
+const GETTER = 0;
+const SETTER = 1;
+
+# These definitions must match the index of the properties in objects.h.
+const kApiTagOffset = 0;
+const kApiPropertyListOffset = 1;
+const kApiSerialNumberOffset = 2;
+const kApiConstructorOffset = 2;
+const kApiPrototypeTemplateOffset = 5;
+const kApiParentTemplateOffset = 6;
+const kApiFlagOffset = 14;
+
+const NO_HINT = 0;
+const NUMBER_HINT = 1;
+const STRING_HINT = 2;
+
+const kFunctionTag = 0;
+const kNewObjectTag = 1;
+
+# For date.js.
+const HoursPerDay = 24;
+const MinutesPerHour = 60;
+const SecondsPerMinute = 60;
+const msPerSecond = 1000;
+const msPerMinute = 60000;
+const msPerHour = 3600000;
+const msPerDay = 86400000;
+const msPerMonth = 2592000000;
+
+# For apinatives.js
+const kUninitialized = -1;
+const kReadOnlyPrototypeBit = 3; # For FunctionTemplateInfo, matches objects.h
+
+# Note: kDayZeroInJulianDay = ToJulianDay(1970, 0, 1).
+const kInvalidDate = 'Invalid Date';
+const kDayZeroInJulianDay = 2440588;
+const kMonthMask = 0x1e0;
+const kDayMask = 0x01f;
+const kYearShift = 9;
+const kMonthShift = 5;
+
+# Limits for parts of the date, so that we support all the dates that
+# ECMA 262 - 15.9.1.1 requires us to, but at the same time be sure that
+# the date (days since 1970) is in SMI range.
+const kMinYear = -1000000;
+const kMaxYear = 1000000;
+const kMinMonth = -10000000;
+const kMaxMonth = 10000000;
+
+# Native cache ids.
+const STRING_TO_REGEXP_CACHE_ID = 0;
+
+# Type query macros.
+#
+# Note: We have special support for typeof(foo) === 'bar' in the compiler.
+# It will *not* generate a runtime typeof call for the most important
+# values of 'bar'.
+macro IS_NULL(arg) = (arg === null);
+macro IS_NULL_OR_UNDEFINED(arg) = (arg == null);
+macro IS_UNDEFINED(arg) = (typeof(arg) === 'undefined');
+macro IS_NUMBER(arg) = (typeof(arg) === 'number');
+macro IS_STRING(arg) = (typeof(arg) === 'string');
+macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean');
+macro IS_SYMBOL(arg) = (typeof(arg) === 'symbol');
+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_WEAKSET(arg) = (%_ClassOf(arg) === 'WeakSet');
+macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date');
+macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number');
+macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String');
+macro IS_SYMBOL_WRAPPER(arg) = (%_ClassOf(arg) === 'Symbol');
+macro IS_BOOLEAN_WRAPPER(arg) = (%_ClassOf(arg) === 'Boolean');
+macro IS_ERROR(arg) = (%_ClassOf(arg) === 'Error');
+macro IS_SCRIPT(arg) = (%_ClassOf(arg) === 'Script');
+macro IS_ARGUMENTS(arg) = (%_ClassOf(arg) === 'Arguments');
+macro IS_GLOBAL(arg) = (%_ClassOf(arg) === 'global');
+macro IS_ARRAYBUFFER(arg) = (%_ClassOf(arg) === 'ArrayBuffer');
+macro IS_DATAVIEW(arg) = (%_ClassOf(arg) === 'DataView');
+macro IS_GENERATOR(arg) = (%_ClassOf(arg) === 'Generator');
+macro IS_UNDETECTABLE(arg) = (%_IsUndetectableObject(arg));
+macro FLOOR(arg) = $floor(arg);
+
+# Macro for ECMAScript 5 queries of the type:
+# "Type(O) is object."
+# This is the same as being either a function or an object in V8 terminology
+# (including proxies).
+# In addition, an undetectable object is also included by this.
+macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg));
+
+# Macro for ECMAScript 5 queries of the type:
+# "IsCallable(O)"
+# We assume here that this is the same as being either a function or a function
+# proxy. That ignores host objects with [[Call]] methods, but in most situations
+# 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)));
+macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToInteger(ToNumber(arg)));
+macro TO_INTEGER_FOR_SIDE_EFFECT(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : ToNumber(arg));
+macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(ToNumber(arg)));
+macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0));
+macro TO_UINT32(arg) = (arg >>> 0);
+macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg));
+macro TO_NUMBER_INLINE(arg) = (IS_NUMBER(%IS_VAR(arg)) ? arg : NonNumberToNumber(arg));
+macro TO_OBJECT_INLINE(arg) = (IS_SPEC_OBJECT(%IS_VAR(arg)) ? arg : ToObject(arg));
+macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");
+
+# Macros implemented in Python.
+python macro CHAR_CODE(str) = ord(str[1]);
+
+# Constants used on an array to implement the properties of the RegExp object.
+const REGEXP_NUMBER_OF_CAPTURES = 0;
+const REGEXP_FIRST_CAPTURE = 3;
+
+# We can't put macros in macros so we use constants here.
+# REGEXP_NUMBER_OF_CAPTURES
+macro NUMBER_OF_CAPTURES(array) = ((array)[0]);
+
+# Limit according to ECMA 262 15.9.1.1
+const MAX_TIME_MS = 8640000000000000;
+# Limit which is MAX_TIME_MS + msPerMonth.
+const MAX_TIME_BEFORE_UTC = 8640002592000000;
+
+# Gets the value of a Date object. If arg is not a Date object
+# a type error is thrown.
+macro CHECK_DATE(arg) = if (%_ClassOf(arg) !== 'Date') ThrowDateTypeError();
+macro LOCAL_DATE_VALUE(arg) = (%_DateField(arg, 0) + %_DateField(arg, 21));
+macro UTC_DATE_VALUE(arg) = (%_DateField(arg, 0));
+
+macro LOCAL_YEAR(arg) = (%_DateField(arg, 1));
+macro LOCAL_MONTH(arg) = (%_DateField(arg, 2));
+macro LOCAL_DAY(arg) = (%_DateField(arg, 3));
+macro LOCAL_WEEKDAY(arg) = (%_DateField(arg, 4));
+macro LOCAL_HOUR(arg) = (%_DateField(arg, 5));
+macro LOCAL_MIN(arg) = (%_DateField(arg, 6));
+macro LOCAL_SEC(arg) = (%_DateField(arg, 7));
+macro LOCAL_MS(arg) = (%_DateField(arg, 8));
+macro LOCAL_DAYS(arg) = (%_DateField(arg, 9));
+macro LOCAL_TIME_IN_DAY(arg) = (%_DateField(arg, 10));
+
+macro UTC_YEAR(arg) = (%_DateField(arg, 11));
+macro UTC_MONTH(arg) = (%_DateField(arg, 12));
+macro UTC_DAY(arg) = (%_DateField(arg, 13));
+macro UTC_WEEKDAY(arg) = (%_DateField(arg, 14));
+macro UTC_HOUR(arg) = (%_DateField(arg, 15));
+macro UTC_MIN(arg) = (%_DateField(arg, 16));
+macro UTC_SEC(arg) = (%_DateField(arg, 17));
+macro UTC_MS(arg) = (%_DateField(arg, 18));
+macro UTC_DAYS(arg) = (%_DateField(arg, 19));
+macro UTC_TIME_IN_DAY(arg) = (%_DateField(arg, 20));
+
+macro TIMEZONE_OFFSET(arg) = (%_DateField(arg, 21));
+
+macro SET_UTC_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 1));
+macro SET_LOCAL_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 0));
+
+# Last input and last subject of regexp matches.
+const LAST_SUBJECT_INDEX = 1;
+macro LAST_SUBJECT(array) = ((array)[1]);
+macro LAST_INPUT(array) = ((array)[2]);
+
+# REGEXP_FIRST_CAPTURE
+macro CAPTURE(index) = (3 + (index));
+const CAPTURE0 = 3;
+const CAPTURE1 = 4;
+
+# For the regexp capture override array. This has the same
+# format as the arguments to a function called from
+# String.prototype.replace.
+macro OVERRIDE_MATCH(override) = ((override)[0]);
+macro OVERRIDE_POS(override) = ((override)[(override).length - 2]);
+macro OVERRIDE_SUBJECT(override) = ((override)[(override).length - 1]);
+# 1-based so index of 1 returns the first capture
+macro OVERRIDE_CAPTURE(override, index) = ((override)[(index)]);
+
+# PropertyDescriptor return value indices - must match
+# PropertyDescriptorIndices in runtime.cc.
+const IS_ACCESSOR_INDEX = 0;
+const VALUE_INDEX = 1;
+const GETTER_INDEX = 2;
+const SETTER_INDEX = 3;
+const WRITABLE_INDEX = 4;
+const ENUMERABLE_INDEX = 5;
+const CONFIGURABLE_INDEX = 6;
+
+# For messages.js
+# Matches Script::Type from objects.h
+const TYPE_NATIVE = 0;
+const TYPE_EXTENSION = 1;
+const TYPE_NORMAL = 2;
+
+# Matches Script::CompilationType from objects.h
+const COMPILATION_TYPE_HOST = 0;
+const COMPILATION_TYPE_EVAL = 1;
+const COMPILATION_TYPE_JSON = 2;
+
+# Matches Messages::kNoLineNumberInfo from v8.h
+const kNoLineNumberInfo = 0;
diff --git a/chromium/v8/src/mark-compact-inl.h b/chromium/v8/src/mark-compact-inl.h
new file mode 100644
index 00000000000..10773e7202a
--- /dev/null
+++ b/chromium/v8/src/mark-compact-inl.h
@@ -0,0 +1,100 @@
+// Copyright 2012 the V8 project authors. 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::kSweepPreciselyMask) != 0);
+ reduce_memory_footprint_ = ((flags & Heap::kReduceMemoryFootprintMask) != 0);
+ abort_incremental_marking_ =
+ ((flags & Heap::kAbortIncrementalMarkingMask) != 0);
+}
+
+
+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());
+ ASSERT(IsMarked(obj));
+ ASSERT(HEAP->Contains(obj));
+ marking_deque_.PushBlack(obj);
+ }
+}
+
+
+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());
+}
+
+
+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/chromium/v8/src/mark-compact.cc b/chromium/v8/src/mark-compact.cc
new file mode 100644
index 00000000000..0e842670280
--- /dev/null
+++ b/chromium/v8/src/mark-compact.cc
@@ -0,0 +1,4351 @@
+// Copyright 2012 the V8 project authors. 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 "code-stubs.h"
+#include "compilation-cache.h"
+#include "cpu-profiler.h"
+#include "deoptimizer.h"
+#include "execution.h"
+#include "gdb-jit.h"
+#include "global-handles.h"
+#include "heap-profiler.h"
+#include "ic-inl.h"
+#include "incremental-marking.h"
+#include "mark-compact.h"
+#include "marking-thread.h"
+#include "objects-visiting.h"
+#include "objects-visiting-inl.h"
+#include "stub-cache.h"
+#include "sweeper-thread.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
+
+MarkCompactCollector::MarkCompactCollector() : // NOLINT
+#ifdef DEBUG
+ state_(IDLE),
+#endif
+ sweep_precisely_(false),
+ reduce_memory_footprint_(false),
+ abort_incremental_marking_(false),
+ marking_parity_(ODD_MARKING_PARITY),
+ compacting_(false),
+ was_marked_incrementally_(false),
+ sweeping_pending_(false),
+ sequential_sweeping_(false),
+ tracer_(NULL),
+ migration_slots_buffer_(NULL),
+ heap_(NULL),
+ code_flusher_(NULL),
+ encountered_weak_collections_(NULL),
+ code_to_deoptimize_(NULL) { }
+
+#ifdef VERIFY_HEAP
+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);
+ CHECK(HEAP->mark_compact_collector()->IsMarked(object));
+ }
+ }
+ }
+
+ void VisitEmbeddedPointer(RelocInfo* rinfo) {
+ ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT);
+ if (!FLAG_weak_embedded_maps_in_optimized_code || !FLAG_collect_maps ||
+ rinfo->host()->kind() != Code::OPTIMIZED_FUNCTION ||
+ !rinfo->target_object()->IsMap() ||
+ !Map::cast(rinfo->target_object())->CanTransition()) {
+ VisitPointer(rinfo->target_object_address());
+ }
+ }
+};
+
+
+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)) {
+ CHECK(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->area_start() as start of range on all pages.
+ CHECK_EQ(space->bottom(),
+ NewSpacePage::FromAddress(space->bottom())->area_start());
+ while (it.has_next()) {
+ NewSpacePage* page = it.next();
+ Address limit = it.has_next() ? page->area_end() : end;
+ CHECK(limit == end || !page->Contains(end));
+ VerifyMarking(page->area_start(), limit);
+ }
+}
+
+
+static void VerifyMarking(PagedSpace* space) {
+ PageIterator it(space);
+
+ while (it.has_next()) {
+ Page* p = it.next();
+ VerifyMarking(p->area_start(), p->area_end());
+ }
+}
+
+
+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->property_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)) {
+ CHECK(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->area_start();
+ Address limit = it.has_next() ? page->area_end() : space->top();
+ CHECK(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->area_start(), p->area_end());
+ }
+}
+
+
+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->property_cell_space());
+ VerifyEvacuation(heap->map_space());
+ VerifyEvacuation(heap->new_space());
+
+ VerifyEvacuationVisitor visitor;
+ heap->IterateStrongRoots(&visitor, VISIT_ALL);
+}
+#endif // VERIFY_HEAP
+
+
+#ifdef DEBUG
+class VerifyNativeContextSeparationVisitor: public ObjectVisitor {
+ public:
+ VerifyNativeContextSeparationVisitor() : current_native_context_(NULL) {}
+
+ void VisitPointers(Object** start, Object** end) {
+ for (Object** current = start; current < end; current++) {
+ if ((*current)->IsHeapObject()) {
+ HeapObject* object = HeapObject::cast(*current);
+ if (object->IsString()) continue;
+ switch (object->map()->instance_type()) {
+ case JS_FUNCTION_TYPE:
+ CheckContext(JSFunction::cast(object)->context());
+ break;
+ case JS_GLOBAL_PROXY_TYPE:
+ CheckContext(JSGlobalProxy::cast(object)->native_context());
+ break;
+ case JS_GLOBAL_OBJECT_TYPE:
+ case JS_BUILTINS_OBJECT_TYPE:
+ CheckContext(GlobalObject::cast(object)->native_context());
+ break;
+ case JS_ARRAY_TYPE:
+ case JS_DATE_TYPE:
+ case JS_OBJECT_TYPE:
+ case JS_REGEXP_TYPE:
+ VisitPointer(HeapObject::RawField(object, JSObject::kMapOffset));
+ break;
+ case MAP_TYPE:
+ VisitPointer(HeapObject::RawField(object, Map::kPrototypeOffset));
+ VisitPointer(HeapObject::RawField(object, Map::kConstructorOffset));
+ break;
+ case FIXED_ARRAY_TYPE:
+ if (object->IsContext()) {
+ CheckContext(object);
+ } else {
+ FixedArray* array = FixedArray::cast(object);
+ int length = array->length();
+ // Set array length to zero to prevent cycles while iterating
+ // over array bodies, this is easier than intrusive marking.
+ array->set_length(0);
+ array->IterateBody(
+ FIXED_ARRAY_TYPE, FixedArray::SizeFor(length), this);
+ array->set_length(length);
+ }
+ break;
+ case CELL_TYPE:
+ case JS_PROXY_TYPE:
+ case JS_VALUE_TYPE:
+ case TYPE_FEEDBACK_INFO_TYPE:
+ object->Iterate(this);
+ break;
+ case DECLARED_ACCESSOR_INFO_TYPE:
+ case EXECUTABLE_ACCESSOR_INFO_TYPE:
+ case BYTE_ARRAY_TYPE:
+ case CALL_HANDLER_INFO_TYPE:
+ case CODE_TYPE:
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ case HEAP_NUMBER_TYPE:
+ case INTERCEPTOR_INFO_TYPE:
+ case ODDBALL_TYPE:
+ case SCRIPT_TYPE:
+ case SHARED_FUNCTION_INFO_TYPE:
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+ }
+
+ private:
+ void CheckContext(Object* context) {
+ if (!context->IsContext()) return;
+ Context* native_context = Context::cast(context)->native_context();
+ if (current_native_context_ == NULL) {
+ current_native_context_ = native_context;
+ } else {
+ CHECK_EQ(current_native_context_, native_context);
+ }
+ }
+
+ Context* current_native_context_;
+};
+
+
+static void VerifyNativeContextSeparation(Heap* heap) {
+ HeapObjectIterator it(heap->code_space());
+
+ for (Object* object = it.Next(); object != NULL; object = it.Next()) {
+ VerifyNativeContextSeparationVisitor visitor;
+ Code::cast(object)->CodeIterateBody(&visitor);
+ }
+}
+#endif
+
+
+void MarkCompactCollector::TearDown() {
+ AbortCompaction();
+}
+
+
+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 * space->AreaSize());
+ 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);
+
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ // If GDBJIT interface is active disable compaction.
+ if (FLAG_gdbjit) return false;
+#endif
+
+ CollectEvacuationCandidates(heap()->old_pointer_space());
+ CollectEvacuationCandidates(heap()->old_data_space());
+
+ if (FLAG_compact_code_space &&
+ (mode == NON_INCREMENTAL_COMPACTION ||
+ FLAG_incremental_code_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());
+ TraceFragmentation(heap()->property_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_collections_ == Smi::FromInt(0));
+
+ MarkLiveObjects();
+ ASSERT(heap_->incremental_marking()->IsStopped());
+
+ if (FLAG_collect_maps) ClearNonLiveReferences();
+
+ ClearWeakCollections();
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ VerifyMarking(heap_);
+ }
+#endif
+
+ SweepSpaces();
+
+ if (!FLAG_collect_maps) ReattachInitialMaps();
+
+#ifdef DEBUG
+ if (FLAG_verify_native_context_separation) {
+ VerifyNativeContextSeparation(heap_);
+ }
+#endif
+
+#ifdef VERIFY_HEAP
+ if (FLAG_collect_maps && FLAG_weak_embedded_maps_in_optimized_code &&
+ heap()->weak_embedded_maps_verification_enabled()) {
+ VerifyWeakEmbeddedMapsInOptimizedCode();
+ }
+ if (FLAG_collect_maps && FLAG_omit_map_checks_for_leaf_maps) {
+ VerifyOmittedMapChecks();
+ }
+#endif
+
+ Finish();
+
+ if (marking_parity_ == EVEN_MARKING_PARITY) {
+ marking_parity_ = ODD_MARKING_PARITY;
+ } else {
+ ASSERT(marking_parity_ == ODD_MARKING_PARITY);
+ marking_parity_ = EVEN_MARKING_PARITY;
+ }
+
+ tracer_ = NULL;
+}
+
+
+#ifdef VERIFY_HEAP
+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_->property_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);
+ CHECK(Marking::IsWhite(mark_bit));
+ CHECK_EQ(0, Page::FromAddress(obj->address())->LiveBytes());
+ }
+}
+
+
+void MarkCompactCollector::VerifyWeakEmbeddedMapsInOptimizedCode() {
+ HeapObjectIterator code_iterator(heap()->code_space());
+ for (HeapObject* obj = code_iterator.Next();
+ obj != NULL;
+ obj = code_iterator.Next()) {
+ Code* code = Code::cast(obj);
+ if (code->kind() != Code::OPTIMIZED_FUNCTION) continue;
+ if (WillBeDeoptimized(code)) continue;
+ code->VerifyEmbeddedMapsDependency();
+ }
+}
+
+
+void MarkCompactCollector::VerifyOmittedMapChecks() {
+ HeapObjectIterator iterator(heap()->map_space());
+ for (HeapObject* obj = iterator.Next();
+ obj != NULL;
+ obj = iterator.Next()) {
+ Map* map = Map::cast(obj);
+ map->VerifyOmittedMapChecks();
+ }
+}
+#endif // VERIFY_HEAP
+
+
+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());
+ ClearMarkbitsInPagedSpace(heap_->property_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();
+ Page::FromAddress(obj->address())->ResetProgressBar();
+ Page::FromAddress(obj->address())->ResetLiveBytes();
+ }
+}
+
+
+void MarkCompactCollector::StartSweeperThreads() {
+ sweeping_pending_ = true;
+ for (int i = 0; i < FLAG_sweeper_threads; i++) {
+ isolate()->sweeper_threads()[i]->StartSweeping();
+ }
+}
+
+
+void MarkCompactCollector::WaitUntilSweepingCompleted() {
+ ASSERT(sweeping_pending_ == true);
+ for (int i = 0; i < FLAG_sweeper_threads; i++) {
+ isolate()->sweeper_threads()[i]->WaitForSweeperThread();
+ }
+ sweeping_pending_ = false;
+ StealMemoryFromSweeperThreads(heap()->paged_space(OLD_DATA_SPACE));
+ StealMemoryFromSweeperThreads(heap()->paged_space(OLD_POINTER_SPACE));
+ heap()->paged_space(OLD_DATA_SPACE)->ResetUnsweptFreeBytes();
+ heap()->paged_space(OLD_POINTER_SPACE)->ResetUnsweptFreeBytes();
+}
+
+
+intptr_t MarkCompactCollector::
+ StealMemoryFromSweeperThreads(PagedSpace* space) {
+ intptr_t freed_bytes = 0;
+ for (int i = 0; i < FLAG_sweeper_threads; i++) {
+ freed_bytes += isolate()->sweeper_threads()[i]->StealMemory(space);
+ }
+ space->AddToAccountingStats(freed_bytes);
+ space->DecrementUnsweptFreeBytes(freed_bytes);
+ return freed_bytes;
+}
+
+
+bool MarkCompactCollector::AreSweeperThreadsActivated() {
+ return isolate()->sweeper_threads() != NULL;
+}
+
+
+bool MarkCompactCollector::IsConcurrentSweepingInProgress() {
+ return sweeping_pending_;
+}
+
+
+void MarkCompactCollector::MarkInParallel() {
+ for (int i = 0; i < FLAG_marking_threads; i++) {
+ isolate()->marking_threads()[i]->StartMarking();
+ }
+}
+
+
+void MarkCompactCollector::WaitUntilMarkingCompleted() {
+ for (int i = 0; i < FLAG_marking_threads; i++) {
+ isolate()->marking_threads()[i]->WaitForMarkingThread();
+ }
+}
+
+
+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 PROPERTY_CELL_SPACE:
+ return "PROPERTY_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;
+ }
+
+ PagedSpace::SizeStats sizes;
+ space->ObtainFreeListStatistics(p, &sizes);
+
+ intptr_t ratio;
+ intptr_t ratio_threshold;
+ intptr_t area_size = space->AreaSize();
+ if (space->identity() == CODE_SPACE) {
+ ratio = (sizes.medium_size_ * 10 + sizes.large_size_ * 2) * 100 /
+ area_size;
+ ratio_threshold = 10;
+ } else {
+ ratio = (sizes.small_size_ * 5 + sizes.medium_size_) * 100 /
+ area_size;
+ 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) /
+ area_size,
+ static_cast<int>(sizes.medium_size_),
+ static_cast<double>(sizes.medium_size_ * 100) /
+ area_size,
+ static_cast<int>(sizes.large_size_),
+ static_cast<double>(sizes.large_size_ * 100) /
+ area_size,
+ static_cast<int>(sizes.huge_size_),
+ static_cast<double>(sizes.huge_size_ * 100) /
+ area_size,
+ (ratio > ratio_threshold) ? "[fragmented]" : "");
+ }
+
+ if (FLAG_always_compact && sizes.Total() != area_size) {
+ 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);
+
+ static const int kMaxMaxEvacuationCandidates = 1000;
+ int number_of_pages = space->CountTotalPages();
+ int max_evacuation_candidates =
+ static_cast<int>(sqrt(number_of_pages / 2.0) + 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 * space->AreaSize();
+ intptr_t over_reserved = reserved - space->SizeOfObjects();
+ static const intptr_t kFreenessThreshold = 50;
+
+ if (reduce_memory_footprint_ && over_reserved >= space->AreaSize()) {
+ // If reduction of memory footprint was requested, we are aggressive
+ // about choosing pages to free. We expect that half-empty pages
+ // are easier to compact so slightly bump the limit.
+ mode = REDUCE_MEMORY_FOOTPRINT;
+ max_evacuation_candidates += 2;
+ }
+
+
+ if (over_reserved > reserved / 3 && over_reserved >= 2 * space->AreaSize()) {
+ // If over-usage is very high (more than a third of the space), we
+ // try to free all mostly empty pages. We expect that almost empty
+ // pages are even easier to compact so bump the limit even more.
+ mode = REDUCE_MEMORY_FOOTPRINT;
+ max_evacuation_candidates *= 2;
+ }
+
+ if (FLAG_trace_fragmentation && mode == REDUCE_MEMORY_FOOTPRINT) {
+ PrintF("Estimated over reserved memory: %.1f / %.1f MB (threshold %d), "
+ "evacuation candidate limit: %d\n",
+ static_cast<double>(over_reserved) / MB,
+ static_cast<double>(reserved) / MB,
+ static_cast<int>(kFreenessThreshold),
+ max_evacuation_candidates);
+ }
+
+ intptr_t estimated_release = 0;
+
+ Candidate candidates[kMaxMaxEvacuationCandidates];
+
+ max_evacuation_candidates =
+ Min(kMaxMaxEvacuationCandidates, max_evacuation_candidates);
+
+ 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) {
+ unsigned 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) {
+ continue;
+ }
+
+ intptr_t free_bytes = 0;
+
+ if (!p->WasSwept()) {
+ free_bytes = (p->area_size() - p->LiveBytes());
+ } else {
+ PagedSpace::SizeStats sizes;
+ space->ObtainFreeListStatistics(p, &sizes);
+ free_bytes = sizes.Total();
+ }
+
+ int free_pct = static_cast<int>(free_bytes * 100) / p->area_size();
+
+ if (free_pct >= kFreenessThreshold) {
+ estimated_release += 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) / p->area_size(),
+ (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();
+
+ // Rather than passing the tracer around we stash it in a static member
+ // variable.
+ tracer_ = tracer;
+
+#ifdef DEBUG
+ ASSERT(state_ == IDLE);
+ state_ = PREPARE_GC;
+#endif
+
+ ASSERT(!FLAG_never_compact || !FLAG_always_compact);
+
+ if (IsConcurrentSweepingInProgress()) {
+ // Instead of waiting we could also abort the sweeper threads here.
+ WaitUntilSweepingCompleted();
+ }
+
+ // Clear marking bits if incremental marking is aborted.
+ if (was_marked_incrementally_ && abort_incremental_marking_) {
+ 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(heap());
+ for (PagedSpace* space = spaces.next();
+ space != NULL;
+ space = spaces.next()) {
+ space->PrepareForMarkCompact();
+ }
+
+#ifdef VERIFY_HEAP
+ if (!was_marked_incrementally_ && FLAG_verify_heap) {
+ VerifyMarkbitsAreClean();
+ }
+#endif
+}
+
+
+void MarkCompactCollector::Finish() {
+#ifdef DEBUG
+ ASSERT(state_ == SWEEP_SPACES || state_ == RELOCATE_OBJECTS);
+ state_ = IDLE;
+#endif
+ // The stub cache is not traversed during GC; clear the cache to
+ // force lazy re-initialization of it. This must be done after the
+ // GC, because it relies on the new address of certain old space
+ // objects (empty string, illegal builtin).
+ isolate()->stub_cache()->Clear();
+
+ if (code_to_deoptimize_ != Smi::FromInt(0)) {
+ // Convert the linked list of Code objects into a ZoneList.
+ Zone zone(isolate());
+ ZoneList<Code*> codes(4, &zone);
+
+ Object *list = code_to_deoptimize_;
+ while (list->IsCode()) {
+ Code *code = Code::cast(list);
+ list = code->code_to_deoptimize_link();
+ codes.Add(code, &zone);
+ // Destroy the link and don't ever try to deoptimize this code again.
+ code->set_code_to_deoptimize_link(Smi::FromInt(0));
+ }
+ code_to_deoptimize_ = Smi::FromInt(0);
+
+ Deoptimizer::DeoptimizeCodeList(isolate(), &codes);
+ }
+}
+
+
+// -------------------------------------------------------------------------
+// Phase 1: tracing and marking live objects.
+// before: all objects are in normal state.
+// after: a live object's map pointer is marked as '00'.
+
+// Marking all live objects in the heap as part of mark-sweep or mark-compact
+// collection. Before marking, all objects are in their normal state. After
+// marking, live objects' map pointers are marked indicating that the object
+// has been found reachable.
+//
+// The marking algorithm is a (mostly) depth-first (because of possible stack
+// overflow) traversal of the graph of objects reachable from the roots. It
+// uses an explicit stack of pointers rather than recursion. The young
+// generation's inactive ('from') space is used as a marking stack. The
+// objects in the marking stack are the ones that have been reached and marked
+// but their children have not yet been visited.
+//
+// The marking stack can overflow during traversal. In that case, we set an
+// overflow flag. When the overflow flag is set, we continue marking objects
+// reachable from the objects on the marking stack, but no longer push them on
+// the marking stack. Instead, we mark them as both marked and overflowed.
+// When the stack is in the overflowed state, objects marked as overflowed
+// have been reached and marked but their children have not been visited yet.
+// After emptying the marking stack, we clear the overflow flag and traverse
+// the heap looking for objects marked as overflowed, push them on the stack,
+// and continue with marking. This process repeats until all reachable
+// objects have been marked.
+
+void CodeFlusher::ProcessJSFunctionCandidates() {
+ Code* lazy_compile = isolate_->builtins()->builtin(Builtins::kLazyCompile);
+ Object* undefined = isolate_->heap()->undefined_value();
+
+ JSFunction* candidate = jsfunction_candidates_head_;
+ JSFunction* next_candidate;
+ while (candidate != NULL) {
+ next_candidate = GetNextCandidate(candidate);
+ ClearNextCandidate(candidate, undefined);
+
+ SharedFunctionInfo* shared = candidate->shared();
+
+ Code* code = shared->code();
+ MarkBit code_mark = Marking::MarkBitFrom(code);
+ if (!code_mark.Get()) {
+ if (FLAG_trace_code_flushing && shared->is_compiled()) {
+ PrintF("[code-flushing clears: ");
+ shared->ShortPrint();
+ PrintF(" - age: %d]\n", code->GetAge());
+ }
+ shared->set_code(lazy_compile);
+ candidate->set_code(lazy_compile);
+ } else {
+ candidate->set_code(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);
+
+ Object** shared_code_slot =
+ HeapObject::RawField(shared, SharedFunctionInfo::kCodeOffset);
+ isolate_->heap()->mark_compact_collector()->
+ RecordSlot(shared_code_slot, shared_code_slot, *shared_code_slot);
+
+ candidate = next_candidate;
+ }
+
+ jsfunction_candidates_head_ = NULL;
+}
+
+
+void CodeFlusher::ProcessSharedFunctionInfoCandidates() {
+ Code* lazy_compile = isolate_->builtins()->builtin(Builtins::kLazyCompile);
+
+ SharedFunctionInfo* candidate = shared_function_info_candidates_head_;
+ SharedFunctionInfo* next_candidate;
+ while (candidate != NULL) {
+ next_candidate = GetNextCandidate(candidate);
+ ClearNextCandidate(candidate);
+
+ Code* code = candidate->code();
+ MarkBit code_mark = Marking::MarkBitFrom(code);
+ if (!code_mark.Get()) {
+ if (FLAG_trace_code_flushing && candidate->is_compiled()) {
+ PrintF("[code-flushing clears: ");
+ candidate->ShortPrint();
+ PrintF(" - age: %d]\n", code->GetAge());
+ }
+ candidate->set_code(lazy_compile);
+ }
+
+ Object** code_slot =
+ HeapObject::RawField(candidate, SharedFunctionInfo::kCodeOffset);
+ isolate_->heap()->mark_compact_collector()->
+ RecordSlot(code_slot, code_slot, *code_slot);
+
+ candidate = next_candidate;
+ }
+
+ shared_function_info_candidates_head_ = NULL;
+}
+
+
+void CodeFlusher::ProcessOptimizedCodeMaps() {
+ static const int kEntriesStart = SharedFunctionInfo::kEntriesStart;
+ static const int kEntryLength = SharedFunctionInfo::kEntryLength;
+ static const int kContextOffset = 0;
+ static const int kCodeOffset = 1;
+ static const int kLiteralsOffset = 2;
+ STATIC_ASSERT(kEntryLength == 3);
+
+ SharedFunctionInfo* holder = optimized_code_map_holder_head_;
+ SharedFunctionInfo* next_holder;
+ while (holder != NULL) {
+ next_holder = GetNextCodeMap(holder);
+ ClearNextCodeMap(holder);
+
+ FixedArray* code_map = FixedArray::cast(holder->optimized_code_map());
+ int new_length = kEntriesStart;
+ int old_length = code_map->length();
+ for (int i = kEntriesStart; i < old_length; i += kEntryLength) {
+ Code* code = Code::cast(code_map->get(i + kCodeOffset));
+ MarkBit code_mark = Marking::MarkBitFrom(code);
+ if (!code_mark.Get()) {
+ continue;
+ }
+
+ // Update and record the context slot in the optimized code map.
+ Object** context_slot = HeapObject::RawField(code_map,
+ FixedArray::OffsetOfElementAt(new_length));
+ code_map->set(new_length++, code_map->get(i + kContextOffset));
+ ASSERT(Marking::IsBlack(
+ Marking::MarkBitFrom(HeapObject::cast(*context_slot))));
+ isolate_->heap()->mark_compact_collector()->
+ RecordSlot(context_slot, context_slot, *context_slot);
+
+ // Update and record the code slot in the optimized code map.
+ Object** code_slot = HeapObject::RawField(code_map,
+ FixedArray::OffsetOfElementAt(new_length));
+ code_map->set(new_length++, code_map->get(i + kCodeOffset));
+ ASSERT(Marking::IsBlack(
+ Marking::MarkBitFrom(HeapObject::cast(*code_slot))));
+ isolate_->heap()->mark_compact_collector()->
+ RecordSlot(code_slot, code_slot, *code_slot);
+
+ // Update and record the literals slot in the optimized code map.
+ Object** literals_slot = HeapObject::RawField(code_map,
+ FixedArray::OffsetOfElementAt(new_length));
+ code_map->set(new_length++, code_map->get(i + kLiteralsOffset));
+ ASSERT(Marking::IsBlack(
+ Marking::MarkBitFrom(HeapObject::cast(*literals_slot))));
+ isolate_->heap()->mark_compact_collector()->
+ RecordSlot(literals_slot, literals_slot, *literals_slot);
+ }
+
+ // Trim the optimized code map if entries have been removed.
+ if (new_length < old_length) {
+ holder->TrimOptimizedCodeMap(old_length - new_length);
+ }
+
+ holder = next_holder;
+ }
+
+ optimized_code_map_holder_head_ = NULL;
+}
+
+
+void CodeFlusher::EvictCandidate(SharedFunctionInfo* shared_info) {
+ // Make sure previous flushing decisions are revisited.
+ isolate_->heap()->incremental_marking()->RecordWrites(shared_info);
+
+ if (FLAG_trace_code_flushing) {
+ PrintF("[code-flushing abandons function-info: ");
+ shared_info->ShortPrint();
+ PrintF("]\n");
+ }
+
+ SharedFunctionInfo* candidate = shared_function_info_candidates_head_;
+ SharedFunctionInfo* next_candidate;
+ if (candidate == shared_info) {
+ next_candidate = GetNextCandidate(shared_info);
+ shared_function_info_candidates_head_ = next_candidate;
+ ClearNextCandidate(shared_info);
+ } else {
+ while (candidate != NULL) {
+ next_candidate = GetNextCandidate(candidate);
+
+ if (next_candidate == shared_info) {
+ next_candidate = GetNextCandidate(shared_info);
+ SetNextCandidate(candidate, next_candidate);
+ ClearNextCandidate(shared_info);
+ break;
+ }
+
+ candidate = next_candidate;
+ }
+ }
+}
+
+
+void CodeFlusher::EvictCandidate(JSFunction* function) {
+ ASSERT(!function->next_function_link()->IsUndefined());
+ Object* undefined = isolate_->heap()->undefined_value();
+
+ // Make sure previous flushing decisions are revisited.
+ isolate_->heap()->incremental_marking()->RecordWrites(function);
+ isolate_->heap()->incremental_marking()->RecordWrites(function->shared());
+
+ if (FLAG_trace_code_flushing) {
+ PrintF("[code-flushing abandons closure: ");
+ function->shared()->ShortPrint();
+ PrintF("]\n");
+ }
+
+ JSFunction* candidate = jsfunction_candidates_head_;
+ JSFunction* next_candidate;
+ if (candidate == function) {
+ next_candidate = GetNextCandidate(function);
+ jsfunction_candidates_head_ = next_candidate;
+ ClearNextCandidate(function, undefined);
+ } else {
+ while (candidate != NULL) {
+ next_candidate = GetNextCandidate(candidate);
+
+ if (next_candidate == function) {
+ next_candidate = GetNextCandidate(function);
+ SetNextCandidate(candidate, next_candidate);
+ ClearNextCandidate(function, undefined);
+ break;
+ }
+
+ candidate = next_candidate;
+ }
+ }
+}
+
+
+void CodeFlusher::EvictOptimizedCodeMap(SharedFunctionInfo* code_map_holder) {
+ ASSERT(!FixedArray::cast(code_map_holder->optimized_code_map())->
+ get(SharedFunctionInfo::kNextMapIndex)->IsUndefined());
+
+ // Make sure previous flushing decisions are revisited.
+ isolate_->heap()->incremental_marking()->RecordWrites(code_map_holder);
+
+ if (FLAG_trace_code_flushing) {
+ PrintF("[code-flushing abandons code-map: ");
+ code_map_holder->ShortPrint();
+ PrintF("]\n");
+ }
+
+ SharedFunctionInfo* holder = optimized_code_map_holder_head_;
+ SharedFunctionInfo* next_holder;
+ if (holder == code_map_holder) {
+ next_holder = GetNextCodeMap(code_map_holder);
+ optimized_code_map_holder_head_ = next_holder;
+ ClearNextCodeMap(code_map_holder);
+ } else {
+ while (holder != NULL) {
+ next_holder = GetNextCodeMap(holder);
+
+ if (next_holder == code_map_holder) {
+ next_holder = GetNextCodeMap(code_map_holder);
+ SetNextCodeMap(holder, next_holder);
+ ClearNextCodeMap(code_map_holder);
+ break;
+ }
+
+ holder = next_holder;
+ }
+ }
+}
+
+
+void CodeFlusher::EvictJSFunctionCandidates() {
+ JSFunction* candidate = jsfunction_candidates_head_;
+ JSFunction* next_candidate;
+ while (candidate != NULL) {
+ next_candidate = GetNextCandidate(candidate);
+ EvictCandidate(candidate);
+ candidate = next_candidate;
+ }
+ ASSERT(jsfunction_candidates_head_ == NULL);
+}
+
+
+void CodeFlusher::EvictSharedFunctionInfoCandidates() {
+ SharedFunctionInfo* candidate = shared_function_info_candidates_head_;
+ SharedFunctionInfo* next_candidate;
+ while (candidate != NULL) {
+ next_candidate = GetNextCandidate(candidate);
+ EvictCandidate(candidate);
+ candidate = next_candidate;
+ }
+ ASSERT(shared_function_info_candidates_head_ == NULL);
+}
+
+
+void CodeFlusher::EvictOptimizedCodeMaps() {
+ SharedFunctionInfo* holder = optimized_code_map_holder_head_;
+ SharedFunctionInfo* next_holder;
+ while (holder != NULL) {
+ next_holder = GetNextCodeMap(holder);
+ EvictOptimizedCodeMap(holder);
+ holder = next_holder;
+ }
+ ASSERT(optimized_code_map_holder_head_ == NULL);
+}
+
+
+void CodeFlusher::IteratePointersToFromSpace(ObjectVisitor* v) {
+ Heap* heap = isolate_->heap();
+
+ JSFunction** slot = &jsfunction_candidates_head_;
+ JSFunction* candidate = jsfunction_candidates_head_;
+ while (candidate != NULL) {
+ if (heap->InFromSpace(candidate)) {
+ v->VisitPointer(reinterpret_cast<Object**>(slot));
+ }
+ candidate = GetNextCandidate(*slot);
+ slot = GetNextCandidateSlot(*slot);
+ }
+}
+
+
+MarkCompactCollector::~MarkCompactCollector() {
+ if (code_flusher_ != NULL) {
+ delete code_flusher_;
+ code_flusher_ = NULL;
+ }
+}
+
+
+static inline HeapObject* ShortCircuitConsString(Object** p) {
+ // Optimization: If the heap object pointed to by p is a non-internalized
+ // cons string whose right substring is HEAP->empty_string, update
+ // 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
+ // (i.e., the left substring of a cons string is always a heap object).
+ //
+ // The check performed is:
+ // object->IsConsString() && !object->IsInternalizedString() &&
+ // (ConsString::cast(object)->second() == HEAP->empty_string())
+ // except the maps for the object and its possible substrings might be
+ // marked.
+ HeapObject* object = HeapObject::cast(*p);
+ 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)->second();
+ Heap* heap = map->GetHeap();
+ if (second != heap->empty_string()) {
+ return object;
+ }
+
+ // Since we don't have the object's start, it is impossible to update the
+ // page dirty marks. Therefore, we only replace the string with its left
+ // substring when page dirty marks do not change.
+ Object* first = reinterpret_cast<ConsString*>(object)->first();
+ if (!heap->InNewSpace(object) && heap->InNewSpace(first)) return object;
+
+ *p = first;
+ return HeapObject::cast(first);
+}
+
+
+class MarkCompactMarkingVisitor
+ : public StaticMarkingVisitor<MarkCompactMarkingVisitor> {
+ public:
+ static void ObjectStatsVisitBase(StaticVisitorBase::VisitorId id,
+ Map* map, HeapObject* obj);
+
+ static void ObjectStatsCountFixedArray(
+ FixedArrayBase* fixed_array,
+ FixedArraySubInstanceType fast_type,
+ FixedArraySubInstanceType dictionary_type);
+
+ template<MarkCompactMarkingVisitor::VisitorId id>
+ class ObjectStatsTracker {
+ public:
+ static inline void Visit(Map* map, HeapObject* obj);
+ };
+
+ static void Initialize();
+
+ INLINE(static void VisitPointer(Heap* heap, Object** p)) {
+ MarkObjectByPointer(heap->mark_compact_collector(), p, p);
+ }
+
+ INLINE(static void VisitPointers(Heap* heap, Object** start, Object** end)) {
+ // Mark all objects pointed to in [start, end).
+ const int kMinRangeForMarkingRecursion = 64;
+ if (end - start >= kMinRangeForMarkingRecursion) {
+ if (VisitUnmarkedObjects(heap, start, end)) return;
+ // We are close to a stack overflow, so just mark the objects.
+ }
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+ for (Object** p = start; p < end; p++) {
+ MarkObjectByPointer(collector, start, p);
+ }
+ }
+
+ // Marks the object black and pushes it on the marking stack.
+ INLINE(static void MarkObject(Heap* heap, HeapObject* object)) {
+ MarkBit mark = Marking::MarkBitFrom(object);
+ heap->mark_compact_collector()->MarkObject(object, mark);
+ }
+
+ // Marks the object black without pushing it on the marking stack.
+ // Returns true if object needed marking and false otherwise.
+ INLINE(static bool MarkObjectWithoutPush(Heap* heap, HeapObject* object)) {
+ MarkBit mark_bit = Marking::MarkBitFrom(object);
+ if (!mark_bit.Get()) {
+ heap->mark_compact_collector()->SetMark(object, mark_bit);
+ return true;
+ }
+ return false;
+ }
+
+ // Mark object pointed to by p.
+ INLINE(static void MarkObjectByPointer(MarkCompactCollector* collector,
+ Object** anchor_slot,
+ Object** p)) {
+ if (!(*p)->IsHeapObject()) return;
+ HeapObject* object = ShortCircuitConsString(p);
+ collector->RecordSlot(anchor_slot, p, object);
+ MarkBit mark = Marking::MarkBitFrom(object);
+ collector->MarkObject(object, mark);
+ }
+
+
+ // Visit an unmarked object.
+ INLINE(static void VisitUnmarkedObject(MarkCompactCollector* collector,
+ HeapObject* obj)) {
+#ifdef DEBUG
+ ASSERT(Isolate::Current()->heap()->Contains(obj));
+ ASSERT(!HEAP->mark_compact_collector()->IsMarked(obj));
+#endif
+ Map* map = obj->map();
+ Heap* heap = obj->GetHeap();
+ MarkBit mark = Marking::MarkBitFrom(obj);
+ heap->mark_compact_collector()->SetMark(obj, mark);
+ // Mark the map pointer and the body.
+ MarkBit map_mark = Marking::MarkBitFrom(map);
+ heap->mark_compact_collector()->MarkObject(map, map_mark);
+ IterateBody(map, obj);
+ }
+
+ // Visit all unmarked objects pointed to by [start, end).
+ // Returns false if the operation fails (lack of stack space).
+ INLINE(static bool VisitUnmarkedObjects(Heap* heap,
+ Object** start,
+ Object** end)) {
+ // Return false is we are close to the stack limit.
+ StackLimitCheck check(heap->isolate());
+ if (check.HasOverflowed()) return false;
+
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+ // Visit the unmarked objects.
+ for (Object** p = start; p < end; p++) {
+ 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;
+ }
+
+ INLINE(static void BeforeVisitingSharedFunctionInfo(HeapObject* object)) {
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
+ shared->BeforeVisitingPointers();
+ }
+
+ static void VisitWeakCollection(Map* map, HeapObject* object) {
+ MarkCompactCollector* collector = map->GetHeap()->mark_compact_collector();
+ JSWeakCollection* weak_collection =
+ reinterpret_cast<JSWeakCollection*>(object);
+
+ // Enqueue weak map in linked list of encountered weak maps.
+ if (weak_collection->next() == Smi::FromInt(0)) {
+ weak_collection->set_next(collector->encountered_weak_collections());
+ collector->set_encountered_weak_collections(weak_collection);
+ }
+
+ // Skip visiting the backing hash table containing the mappings.
+ int object_size = JSWeakCollection::BodyDescriptor::SizeOf(map, object);
+ BodyVisitorBase<MarkCompactMarkingVisitor>::IteratePointers(
+ map->GetHeap(),
+ object,
+ JSWeakCollection::BodyDescriptor::kStartOffset,
+ JSWeakCollection::kTableOffset);
+ BodyVisitorBase<MarkCompactMarkingVisitor>::IteratePointers(
+ map->GetHeap(),
+ object,
+ JSWeakCollection::kTableOffset + kPointerSize,
+ object_size);
+
+ // Mark the backing hash table without pushing it on the marking stack.
+ Object* table_object = weak_collection->table();
+ if (!table_object->IsHashTable()) return;
+ ObjectHashTable* table = ObjectHashTable::cast(table_object);
+ Object** table_slot =
+ HeapObject::RawField(weak_collection, JSWeakCollection::kTableOffset);
+ MarkBit table_mark = Marking::MarkBitFrom(table);
+ collector->RecordSlot(table_slot, table_slot, table);
+ if (!table_mark.Get()) collector->SetMark(table, table_mark);
+ // Recording the map slot can be skipped, because maps are not compacted.
+ collector->MarkObject(table->map(), Marking::MarkBitFrom(table->map()));
+ ASSERT(MarkCompactCollector::IsMarked(table->map()));
+ }
+
+ private:
+ template<int id>
+ static inline void TrackObjectStatsAndVisit(Map* map, HeapObject* obj);
+
+ // Code flushing support.
+
+ static const int kRegExpCodeThreshold = 5;
+
+ static void UpdateRegExpCodeAgeAndFlush(Heap* heap,
+ JSRegExp* re,
+ 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 (HeapObject::cast(re->data())->map()->instance_type() !=
+ FIXED_ARRAY_TYPE) return;
+
+ // Make sure this is a RegExp that actually contains code.
+ if (re->TypeTag() != JSRegExp::IRREGEXP) return;
+
+ Object* code = re->DataAt(JSRegExp::code_index(is_ascii));
+ 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->SetDataAt(JSRegExp::saved_code_index(is_ascii), code);
+
+ // 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->SetDataAt(JSRegExp::code_index(is_ascii),
+ Smi::FromInt(heap->sweep_generation() & 0xff));
+ } else if (code->IsSmi()) {
+ int value = Smi::cast(code)->value();
+ // The regexp has not been compiled yet or there was a compilation error.
+ if (value == JSRegExp::kUninitializedValue ||
+ value == JSRegExp::kCompilationErrorValue) {
+ return;
+ }
+
+ // Check if we should flush now.
+ if (value == ((heap->sweep_generation() - kRegExpCodeThreshold) & 0xff)) {
+ re->SetDataAt(JSRegExp::code_index(is_ascii),
+ Smi::FromInt(JSRegExp::kUninitializedValue));
+ re->SetDataAt(JSRegExp::saved_code_index(is_ascii),
+ Smi::FromInt(JSRegExp::kUninitializedValue));
+ }
+ }
+ }
+
+
+ // Works by setting the current sweep_generation (as a smi) in the
+ // code object place in the data array of the RegExp and keeps a copy
+ // around that can be reinstated if we reuse the RegExp before flushing.
+ // 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->GetHeap();
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+ if (!collector->is_code_flushing_enabled()) {
+ VisitJSRegExp(map, object);
+ return;
+ }
+ JSRegExp* re = reinterpret_cast<JSRegExp*>(object);
+ // 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.
+ VisitJSRegExp(map, object);
+ }
+
+ static VisitorDispatchTable<Callback> non_count_table_;
+};
+
+
+void MarkCompactMarkingVisitor::ObjectStatsCountFixedArray(
+ FixedArrayBase* fixed_array,
+ FixedArraySubInstanceType fast_type,
+ FixedArraySubInstanceType dictionary_type) {
+ Heap* heap = fixed_array->map()->GetHeap();
+ if (fixed_array->map() != heap->fixed_cow_array_map() &&
+ fixed_array->map() != heap->fixed_double_array_map() &&
+ fixed_array != heap->empty_fixed_array()) {
+ if (fixed_array->IsDictionary()) {
+ heap->RecordObjectStats(FIXED_ARRAY_TYPE,
+ dictionary_type,
+ fixed_array->Size());
+ } else {
+ heap->RecordObjectStats(FIXED_ARRAY_TYPE,
+ fast_type,
+ fixed_array->Size());
+ }
+ }
+}
+
+
+void MarkCompactMarkingVisitor::ObjectStatsVisitBase(
+ MarkCompactMarkingVisitor::VisitorId id, Map* map, HeapObject* obj) {
+ Heap* heap = map->GetHeap();
+ int object_size = obj->Size();
+ heap->RecordObjectStats(map->instance_type(), -1, object_size);
+ non_count_table_.GetVisitorById(id)(map, obj);
+ if (obj->IsJSObject()) {
+ JSObject* object = JSObject::cast(obj);
+ ObjectStatsCountFixedArray(object->elements(),
+ DICTIONARY_ELEMENTS_SUB_TYPE,
+ FAST_ELEMENTS_SUB_TYPE);
+ ObjectStatsCountFixedArray(object->properties(),
+ DICTIONARY_PROPERTIES_SUB_TYPE,
+ FAST_PROPERTIES_SUB_TYPE);
+ }
+}
+
+
+template<MarkCompactMarkingVisitor::VisitorId id>
+void MarkCompactMarkingVisitor::ObjectStatsTracker<id>::Visit(
+ Map* map, HeapObject* obj) {
+ ObjectStatsVisitBase(id, map, obj);
+}
+
+
+template<>
+class MarkCompactMarkingVisitor::ObjectStatsTracker<
+ MarkCompactMarkingVisitor::kVisitMap> {
+ public:
+ static inline void Visit(Map* map, HeapObject* obj) {
+ Heap* heap = map->GetHeap();
+ Map* map_obj = Map::cast(obj);
+ ASSERT(map->instance_type() == MAP_TYPE);
+ DescriptorArray* array = map_obj->instance_descriptors();
+ if (map_obj->owns_descriptors() &&
+ array != heap->empty_descriptor_array()) {
+ int fixed_array_size = array->Size();
+ heap->RecordObjectStats(FIXED_ARRAY_TYPE,
+ DESCRIPTOR_ARRAY_SUB_TYPE,
+ fixed_array_size);
+ }
+ if (map_obj->HasTransitionArray()) {
+ int fixed_array_size = map_obj->transitions()->Size();
+ heap->RecordObjectStats(FIXED_ARRAY_TYPE,
+ TRANSITION_ARRAY_SUB_TYPE,
+ fixed_array_size);
+ }
+ if (map_obj->has_code_cache()) {
+ CodeCache* cache = CodeCache::cast(map_obj->code_cache());
+ heap->RecordObjectStats(
+ FIXED_ARRAY_TYPE,
+ MAP_CODE_CACHE_SUB_TYPE,
+ cache->default_cache()->Size());
+ if (!cache->normal_type_cache()->IsUndefined()) {
+ heap->RecordObjectStats(
+ FIXED_ARRAY_TYPE,
+ MAP_CODE_CACHE_SUB_TYPE,
+ FixedArray::cast(cache->normal_type_cache())->Size());
+ }
+ }
+ ObjectStatsVisitBase(kVisitMap, map, obj);
+ }
+};
+
+
+template<>
+class MarkCompactMarkingVisitor::ObjectStatsTracker<
+ MarkCompactMarkingVisitor::kVisitCode> {
+ public:
+ static inline void Visit(Map* map, HeapObject* obj) {
+ Heap* heap = map->GetHeap();
+ int object_size = obj->Size();
+ ASSERT(map->instance_type() == CODE_TYPE);
+ heap->RecordObjectStats(CODE_TYPE, Code::cast(obj)->kind(), object_size);
+ ObjectStatsVisitBase(kVisitCode, map, obj);
+ }
+};
+
+
+template<>
+class MarkCompactMarkingVisitor::ObjectStatsTracker<
+ MarkCompactMarkingVisitor::kVisitSharedFunctionInfo> {
+ public:
+ static inline void Visit(Map* map, HeapObject* obj) {
+ Heap* heap = map->GetHeap();
+ SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
+ if (sfi->scope_info() != heap->empty_fixed_array()) {
+ heap->RecordObjectStats(
+ FIXED_ARRAY_TYPE,
+ SCOPE_INFO_SUB_TYPE,
+ FixedArray::cast(sfi->scope_info())->Size());
+ }
+ ObjectStatsVisitBase(kVisitSharedFunctionInfo, map, obj);
+ }
+};
+
+
+template<>
+class MarkCompactMarkingVisitor::ObjectStatsTracker<
+ MarkCompactMarkingVisitor::kVisitFixedArray> {
+ public:
+ static inline void Visit(Map* map, HeapObject* obj) {
+ Heap* heap = map->GetHeap();
+ FixedArray* fixed_array = FixedArray::cast(obj);
+ if (fixed_array == heap->string_table()) {
+ heap->RecordObjectStats(
+ FIXED_ARRAY_TYPE,
+ STRING_TABLE_SUB_TYPE,
+ fixed_array->Size());
+ }
+ ObjectStatsVisitBase(kVisitFixedArray, map, obj);
+ }
+};
+
+
+void MarkCompactMarkingVisitor::Initialize() {
+ StaticMarkingVisitor<MarkCompactMarkingVisitor>::Initialize();
+
+ table_.Register(kVisitJSRegExp,
+ &VisitRegExpAndFlushCode);
+
+ if (FLAG_track_gc_object_stats) {
+ // Copy the visitor table to make call-through possible.
+ non_count_table_.CopyFrom(&table_);
+#define VISITOR_ID_COUNT_FUNCTION(id) \
+ table_.Register(kVisit##id, ObjectStatsTracker<kVisit##id>::Visit);
+ VISITOR_ID_LIST(VISITOR_ID_COUNT_FUNCTION)
+#undef VISITOR_ID_COUNT_FUNCTION
+ }
+}
+
+
+VisitorDispatchTable<MarkCompactMarkingVisitor::Callback>
+ MarkCompactMarkingVisitor::non_count_table_;
+
+
+class CodeMarkingVisitor : public ThreadVisitor {
+ public:
+ explicit CodeMarkingVisitor(MarkCompactCollector* collector)
+ : collector_(collector) {}
+
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ collector_->PrepareThreadForCodeFlushing(isolate, top);
+ }
+
+ private:
+ MarkCompactCollector* collector_;
+};
+
+
+class SharedFunctionInfoMarkingVisitor : public ObjectVisitor {
+ public:
+ explicit SharedFunctionInfoMarkingVisitor(MarkCompactCollector* collector)
+ : collector_(collector) {}
+
+ void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) VisitPointer(p);
+ }
+
+ void VisitPointer(Object** slot) {
+ Object* obj = *slot;
+ if (obj->IsSharedFunctionInfo()) {
+ SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(obj);
+ MarkBit shared_mark = Marking::MarkBitFrom(shared);
+ MarkBit code_mark = Marking::MarkBitFrom(shared->code());
+ collector_->MarkObject(shared->code(), code_mark);
+ collector_->MarkObject(shared, shared_mark);
+ }
+ }
+
+ private:
+ MarkCompactCollector* collector_;
+};
+
+
+void MarkCompactCollector::PrepareThreadForCodeFlushing(Isolate* isolate,
+ ThreadLocalTop* top) {
+ for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+ // Note: for the frame that has a pending lazy deoptimization
+ // StackFrame::unchecked_code will return a non-optimized code object for
+ // the outermost function and StackFrame::LookupCode will return
+ // actual optimized code object.
+ StackFrame* frame = it.frame();
+ Code* code = frame->unchecked_code();
+ MarkBit code_mark = Marking::MarkBitFrom(code);
+ MarkObject(code, code_mark);
+ if (frame->is_optimized()) {
+ MarkCompactMarkingVisitor::MarkInlinedFunctionsCode(heap(),
+ frame->LookupCode());
+ }
+ }
+}
+
+
+void MarkCompactCollector::PrepareForCodeFlushing() {
+ ASSERT(heap() == Isolate::Current()->heap());
+
+ // Enable code flushing for non-incremental cycles.
+ if (FLAG_flush_code && !FLAG_flush_code_incrementally) {
+ EnableCodeFlushing(!was_marked_incrementally_);
+ }
+
+ // If code flushing is disabled, there is no need to prepare for it.
+ if (!is_code_flushing_enabled()) return;
+
+ // Ensure that empty descriptor array is marked. Method MarkDescriptorArray
+ // relies on it being marked before any other 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());
+ PrepareThreadForCodeFlushing(heap()->isolate(),
+ heap()->isolate()->thread_local_top());
+
+ // Iterate the archived stacks in all threads to check if
+ // the code is referenced.
+ CodeMarkingVisitor code_marking_visitor(this);
+ heap()->isolate()->thread_manager()->IterateArchivedThreads(
+ &code_marking_visitor);
+
+ SharedFunctionInfoMarkingVisitor visitor(this);
+ heap()->isolate()->compilation_cache()->IterateFunctions(&visitor);
+ heap()->isolate()->handle_scope_implementer()->Iterate(&visitor);
+
+ ProcessMarkingDeque();
+}
+
+
+// Visitor class for marking heap roots.
+class RootMarkingVisitor : public ObjectVisitor {
+ public:
+ explicit RootMarkingVisitor(Heap* heap)
+ : collector_(heap->mark_compact_collector()) { }
+
+ 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) {
+ if (!(*p)->IsHeapObject()) return;
+
+ // Replace flat cons strings in place.
+ HeapObject* object = ShortCircuitConsString(p);
+ MarkBit mark_bit = Marking::MarkBitFrom(object);
+ if (mark_bit.Get()) return;
+
+ Map* map = object->map();
+ // Mark the object.
+ collector_->SetMark(object, mark_bit);
+
+ // Mark the map pointer and body, and push them on the marking stack.
+ MarkBit map_mark = Marking::MarkBitFrom(map);
+ collector_->MarkObject(map, map_mark);
+ MarkCompactMarkingVisitor::IterateBody(map, object);
+
+ // Mark all the objects reachable from the map and body. May leave
+ // overflowed objects in the heap.
+ collector_->EmptyMarkingDeque();
+ }
+
+ MarkCompactCollector* collector_;
+};
+
+
+// Helper class for pruning the string table.
+class StringTableCleaner : public ObjectVisitor {
+ public:
+ explicit StringTableCleaner(Heap* heap)
+ : heap_(heap), pointers_removed_(0) { }
+
+ virtual void VisitPointers(Object** start, Object** end) {
+ // Visit all HeapObject pointers in [start, end).
+ for (Object** p = start; p < end; p++) {
+ Object* o = *p;
+ if (o->IsHeapObject() &&
+ !Marking::MarkBitFrom(HeapObject::cast(o)).Get()) {
+ // Check if the internalized string being pruned is external. We need to
+ // delete the associated external data as this string is going away.
+
+ // Since no objects have yet been moved we can safely access the map of
+ // the object.
+ if (o->IsExternalString()) {
+ heap_->FinalizeExternalString(String::cast(*p));
+ }
+ // Set the entry to the_hole_value (as deleted).
+ *p = heap_->the_hole_value();
+ pointers_removed_++;
+ }
+ }
+ }
+
+ int PointersRemoved() {
+ return pointers_removed_;
+ }
+
+ private:
+ Heap* heap_;
+ int pointers_removed_;
+};
+
+
+// Implementation of WeakObjectRetainer for mark compact GCs. All marked objects
+// are retained.
+class MarkCompactWeakObjectRetainer : public WeakObjectRetainer {
+ public:
+ virtual Object* RetainAs(Object* object) {
+ if (Marking::MarkBitFrom(HeapObject::cast(object)).Get()) {
+ return object;
+ } else {
+ return NULL;
+ }
+ }
+};
+
+
+// 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;
+ }
+ }
+}
+
+
+static inline int MarkWordToObjectStarts(uint32_t mark_bits, int* starts);
+
+
+static void DiscoverGreyObjectsOnPage(MarkingDeque* marking_deque,
+ MemoryChunk* p) {
+ ASSERT(!marking_deque->IsFull());
+ ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0);
+ ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0);
+ ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0);
+ ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0);
+
+ for (MarkBitCellIterator it(p); !it.Done(); it.Advance()) {
+ Address cell_base = it.CurrentCellBase();
+ MarkBit::CellType* cell = it.CurrentCell();
+
+ const MarkBit::CellType current_cell = *cell;
+ if (current_cell == 0) continue;
+
+ MarkBit::CellType grey_objects;
+ if (it.HasNext()) {
+ const MarkBit::CellType next_cell = *(cell+1);
+ grey_objects = current_cell &
+ ((current_cell >> 1) | (next_cell << (Bitmap::kBitsPerCell - 1)));
+ } else {
+ grey_objects = current_cell & (current_cell >> 1);
+ }
+
+ int offset = 0;
+ while (grey_objects != 0) {
+ int trailing_zeros = CompilerIntrinsics::CountTrailingZeros(grey_objects);
+ grey_objects >>= trailing_zeros;
+ offset += trailing_zeros;
+ MarkBit markbit(cell, 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);
+ }
+}
+
+
+int MarkCompactCollector::DiscoverAndPromoteBlackObjectsOnPage(
+ NewSpace* new_space,
+ NewSpacePage* 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 survivors_size = 0;
+
+ for (MarkBitCellIterator it(p); !it.Done(); it.Advance()) {
+ Address cell_base = it.CurrentCellBase();
+ MarkBit::CellType* cell = it.CurrentCell();
+
+ MarkBit::CellType current_cell = *cell;
+ if (current_cell == 0) continue;
+
+ int offset = 0;
+ while (current_cell != 0) {
+ int trailing_zeros = CompilerIntrinsics::CountTrailingZeros(current_cell);
+ current_cell >>= trailing_zeros;
+ offset += trailing_zeros;
+ Address address = cell_base + offset * kPointerSize;
+ HeapObject* object = HeapObject::FromAddress(address);
+
+ int size = object->Size();
+ survivors_size += size;
+
+ offset++;
+ current_cell >>= 1;
+ // Aggressively promote young survivors to the old space.
+ if (TryPromoteObject(object, size)) {
+ continue;
+ }
+
+ // Promotion failed. Just migrate object to another semispace.
+ 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(HeapObject::cast(target)->address(),
+ object->address(),
+ size,
+ NEW_SPACE);
+ }
+ *cells = 0;
+ }
+ return survivors_size;
+}
+
+
+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;
+ }
+ }
+}
+
+
+static void DiscoverGreyObjectsInNewSpace(Heap* heap,
+ MarkingDeque* marking_deque) {
+ NewSpace* space = heap->new_space();
+ NewSpacePageIterator it(space->bottom(), space->top());
+ while (it.has_next()) {
+ NewSpacePage* page = it.next();
+ DiscoverGreyObjectsOnPage(marking_deque, page);
+ if (marking_deque->IsFull()) return;
+ }
+}
+
+
+bool MarkCompactCollector::IsUnmarkedHeapObject(Object** p) {
+ Object* o = *p;
+ if (!o->IsHeapObject()) return false;
+ HeapObject* heap_object = HeapObject::cast(o);
+ MarkBit mark = Marking::MarkBitFrom(heap_object);
+ return !mark.Get();
+}
+
+
+bool MarkCompactCollector::IsUnmarkedHeapObjectWithHeap(Heap* heap,
+ Object** p) {
+ Object* o = *p;
+ ASSERT(o->IsHeapObject());
+ HeapObject* heap_object = HeapObject::cast(o);
+ MarkBit mark = Marking::MarkBitFrom(heap_object);
+ return !mark.Get();
+}
+
+
+void MarkCompactCollector::MarkStringTable(RootMarkingVisitor* visitor) {
+ StringTable* string_table = heap()->string_table();
+ // Mark the string table itself.
+ MarkBit string_table_mark = Marking::MarkBitFrom(string_table);
+ SetMark(string_table, string_table_mark);
+ // Explicitly mark the prefix.
+ string_table->IteratePrefix(visitor);
+ ProcessMarkingDeque();
+}
+
+
+void MarkCompactCollector::MarkRoots(RootMarkingVisitor* visitor) {
+ // Mark the heap roots including global variables, stack variables,
+ // etc., and all objects reachable from them.
+ heap()->IterateStrongRoots(visitor, VISIT_ONLY_STRONG);
+
+ // Handle the string table specially.
+ MarkStringTable(visitor);
+
+ // There may be overflowed objects in the heap. Visit them now.
+ while (marking_deque_.overflowed()) {
+ RefillMarkingDeque();
+ EmptyMarkingDeque();
+ }
+}
+
+
+void MarkCompactCollector::MarkImplicitRefGroups() {
+ List<ImplicitRefGroup*>* ref_groups =
+ isolate()->global_handles()->implicit_ref_groups();
+
+ int last = 0;
+ for (int i = 0; i < ref_groups->length(); i++) {
+ ImplicitRefGroup* entry = ref_groups->at(i);
+ ASSERT(entry != NULL);
+
+ if (!IsMarked(*entry->parent)) {
+ (*ref_groups)[last++] = entry;
+ continue;
+ }
+
+ Object*** children = entry->children;
+ // A parent object is marked, so mark all child heap objects.
+ for (size_t j = 0; j < entry->length; ++j) {
+ if ((*children[j])->IsHeapObject()) {
+ HeapObject* child = HeapObject::cast(*children[j]);
+ MarkBit mark = Marking::MarkBitFrom(child);
+ MarkObject(child, mark);
+ }
+ }
+
+ // Once the entire group has been marked, dispose it because it's
+ // not needed anymore.
+ delete entry;
+ }
+ ref_groups->Rewind(last);
+}
+
+
+// Mark all objects reachable from the objects on the marking stack.
+// 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::EmptyMarkingDeque() {
+ while (!marking_deque_.IsEmpty()) {
+ HeapObject* object = marking_deque_.Pop();
+ ASSERT(object->IsHeapObject());
+ ASSERT(heap()->Contains(object));
+ ASSERT(Marking::IsBlack(Marking::MarkBitFrom(object)));
+
+ Map* map = object->map();
+ MarkBit map_mark = Marking::MarkBitFrom(map);
+ MarkObject(map, map_mark);
+
+ MarkCompactMarkingVisitor::IterateBody(map, object);
+ }
+}
+
+
+// Sweep the heap for overflowed objects, clear their overflow bits, and
+// push them on the marking stack. Stop early if the marking stack fills
+// 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::RefillMarkingDeque() {
+ ASSERT(marking_deque_.overflowed());
+
+ DiscoverGreyObjectsInNewSpace(heap(), &marking_deque_);
+ 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;
+
+ DiscoverGreyObjectsInSpace(heap(),
+ &marking_deque_,
+ heap()->property_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();
+}
+
+
+// Mark all objects reachable (transitively) from objects on the marking
+// 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::ProcessMarkingDeque() {
+ EmptyMarkingDeque();
+ while (marking_deque_.overflowed()) {
+ RefillMarkingDeque();
+ EmptyMarkingDeque();
+ }
+}
+
+
+// Mark all objects reachable (transitively) from objects on the marking
+// stack including references only considered in the atomic marking pause.
+void MarkCompactCollector::ProcessEphemeralMarking(ObjectVisitor* visitor) {
+ bool work_to_do = true;
+ ASSERT(marking_deque_.IsEmpty());
+ while (work_to_do) {
+ isolate()->global_handles()->IterateObjectGroups(
+ visitor, &IsUnmarkedHeapObjectWithHeap);
+ MarkImplicitRefGroups();
+ ProcessWeakCollections();
+ work_to_do = !marking_deque_.IsEmpty();
+ ProcessMarkingDeque();
+ }
+}
+
+
+void MarkCompactCollector::ProcessTopOptimizedFrame(ObjectVisitor* visitor) {
+ for (StackFrameIterator it(isolate(), isolate()->thread_local_top());
+ !it.done(); it.Advance()) {
+ if (it.frame()->type() == StackFrame::JAVA_SCRIPT) {
+ return;
+ }
+ if (it.frame()->type() == StackFrame::OPTIMIZED) {
+ Code* code = it.frame()->LookupCode();
+ if (!code->CanDeoptAt(it.frame()->pc())) {
+ code->CodeIterateBody(visitor);
+ }
+ ProcessMarkingDeque();
+ return;
+ }
+ }
+}
+
+
+void MarkCompactCollector::MarkLiveObjects() {
+ GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_MARK);
+ // The recursive GC marker detects when it is nearing stack overflow,
+ // and switches to a different marking system. JS interrupts interfere
+ // with the C stack limit check.
+ PostponeInterruptsScope postpone(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 its 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, 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());
+
+ 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->IsCell());
+ if (IsMarked(cell)) {
+ int offset = Cell::kValueOffset;
+ MarkCompactMarkingVisitor::VisitPointer(
+ heap(),
+ reinterpret_cast<Object**>(cell->address() + offset));
+ }
+ }
+ }
+ {
+ HeapObjectIterator js_global_property_cell_iterator(
+ heap()->property_cell_space());
+ HeapObject* cell;
+ while ((cell = js_global_property_cell_iterator.Next()) != NULL) {
+ ASSERT(cell->IsPropertyCell());
+ if (IsMarked(cell)) {
+ MarkCompactMarkingVisitor::VisitPropertyCell(cell->map(), cell);
+ }
+ }
+ }
+ }
+
+ RootMarkingVisitor root_visitor(heap());
+ MarkRoots(&root_visitor);
+
+ ProcessTopOptimizedFrame(&root_visitor);
+
+ // The objects reachable from the roots are marked, yet unreachable
+ // objects are unmarked. Mark objects reachable due to host
+ // application specific logic or through Harmony weak maps.
+ ProcessEphemeralMarking(&root_visitor);
+
+ // The objects reachable from the roots, weak maps or object groups
+ // are marked, yet unreachable objects are unmarked. Mark objects
+ // reachable only from weak global handles.
+ //
+ // First we identify nonlive weak handles and mark them as pending
+ // destruction.
+ heap()->isolate()->global_handles()->IdentifyWeakHandles(
+ &IsUnmarkedHeapObject);
+ // Then we mark the objects and process the transitive closure.
+ heap()->isolate()->global_handles()->IterateWeakRoots(&root_visitor);
+ while (marking_deque_.overflowed()) {
+ RefillMarkingDeque();
+ EmptyMarkingDeque();
+ }
+
+ // Repeat host application specific and Harmony weak maps marking to
+ // mark unmarked objects reachable from the weak roots.
+ ProcessEphemeralMarking(&root_visitor);
+
+ AfterMarking();
+}
+
+
+void MarkCompactCollector::AfterMarking() {
+ // Object literal map caches reference strings (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
+ // string table.
+ ProcessMapCaches();
+
+ // Prune the string table removing all strings only pointed to by the
+ // string table. Cannot use string_table() here because the string
+ // table is marked.
+ StringTable* string_table = heap()->string_table();
+ StringTableCleaner v(heap());
+ string_table->IterateElements(&v);
+ string_table->ElementsRemoved(v.PointersRemoved());
+ heap()->external_string_table_.Iterate(&v);
+ heap()->external_string_table_.CleanUp();
+
+ // Process the weak references.
+ MarkCompactWeakObjectRetainer mark_compact_object_retainer;
+ heap()->ProcessWeakReferences(&mark_compact_object_retainer);
+
+ // Remove object groups after marking phase.
+ heap()->isolate()->global_handles()->RemoveObjectGroups();
+ heap()->isolate()->global_handles()->RemoveImplicitRefGroups();
+
+ // Flush code from collected candidates.
+ if (is_code_flushing_enabled()) {
+ code_flusher_->ProcessCandidates();
+ // If incremental marker does not support code flushing, we need to
+ // disable it before incremental marking steps for next cycle.
+ if (FLAG_flush_code && !FLAG_flush_code_incrementally) {
+ EnableCodeFlushing(false);
+ }
+ }
+
+ if (!FLAG_watch_ic_patching) {
+ // Clean up dead objects from the runtime profiler.
+ heap()->isolate()->runtime_profiler()->RemoveDeadSamples();
+ }
+
+ if (FLAG_track_gc_object_stats) {
+ heap()->CheckpointObjectStats();
+ }
+}
+
+
+void MarkCompactCollector::ProcessMapCaches() {
+ Object* raw_context = heap()->native_contexts_list_;
+ while (raw_context != heap()->undefined_value()) {
+ Context* context = reinterpret_cast<Context*>(raw_context);
+ 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 (!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();
+ int used_elements = 0;
+ for (int i = MapCache::kElementsStartIndex;
+ i < map_cache->length();
+ i += MapCache::kEntrySize) {
+ Object* raw_key = map_cache->get(i);
+ if (raw_key == heap()->undefined_value() ||
+ raw_key == heap()->the_hole_value()) continue;
+ STATIC_ASSERT(MapCache::kEntrySize == 2);
+ Object* raw_map = map_cache->get(i + 1);
+ if (raw_map->IsHeapObject() && IsMarked(raw_map)) {
+ ++used_elements;
+ } else {
+ // Delete useless entries with unmarked maps.
+ ASSERT(raw_map->IsMap());
+ map_cache->set_the_hole(i);
+ map_cache->set_the_hole(i + 1);
+ }
+ }
+ if (used_elements == 0) {
+ context->set(Context::MAP_CACHE_INDEX, heap()->undefined_value());
+ } else {
+ // Note: we don't actually shrink the cache here to avoid
+ // extra complexity during GC. We rely on subsequent cache
+ // usages (EnsureCapacity) to do this.
+ map_cache->ElementsRemoved(existing_elements - used_elements);
+ 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);
+ }
+ ProcessMarkingDeque();
+}
+
+
+void MarkCompactCollector::ReattachInitialMaps() {
+ HeapObjectIterator map_iterator(heap()->map_space());
+ for (HeapObject* obj = map_iterator.Next();
+ obj != NULL;
+ obj = map_iterator.Next()) {
+ Map* map = Map::cast(obj);
+
+ STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
+ if (map->instance_type() < FIRST_JS_RECEIVER_TYPE) continue;
+
+ if (map->attached_to_shared_function_info()) {
+ JSFunction::cast(map->constructor())->shared()->AttachInitialMap(map);
+ }
+ }
+}
+
+
+void MarkCompactCollector::ClearNonLiveReferences() {
+ // Iterate over the map space, setting map transitions that go from
+ // a marked map to an unmarked map to null transitions. This action
+ // is carried out only on maps of JSObjects and related subtypes.
+ HeapObjectIterator map_iterator(heap()->map_space());
+ for (HeapObject* obj = map_iterator.Next();
+ obj != NULL;
+ obj = map_iterator.Next()) {
+ Map* map = Map::cast(obj);
+
+ if (!map->CanTransition()) continue;
+
+ MarkBit map_mark = Marking::MarkBitFrom(map);
+ 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.
+ JSFunction::cast(map->constructor())->shared()->AttachInitialMap(map);
+ }
+
+ ClearNonLivePrototypeTransitions(map);
+ ClearNonLiveMapTransitions(map, map_mark);
+
+ if (map_mark.Get()) {
+ ClearNonLiveDependentCode(map->dependent_code());
+ } else {
+ ClearAndDeoptimizeDependentCode(map);
+ }
+ }
+
+ // Iterate over property cell space, removing dependent code that is not
+ // otherwise kept alive by strong references.
+ HeapObjectIterator cell_iterator(heap_->property_cell_space());
+ for (HeapObject* cell = cell_iterator.Next();
+ cell != NULL;
+ cell = cell_iterator.Next()) {
+ if (IsMarked(cell)) {
+ ClearNonLiveDependentCode(PropertyCell::cast(cell)->dependent_code());
+ }
+ }
+}
+
+
+void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
+ int number_of_transitions = map->NumberOfProtoTransitions();
+ FixedArray* prototype_transitions = map->GetPrototypeTransitions();
+
+ 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(
+ proto_index,
+ prototype,
+ UPDATE_WRITE_BARRIER);
+ prototype_transitions->set(
+ map_index,
+ cached_map,
+ SKIP_WRITE_BARRIER);
+ }
+ 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) {
+ Object* potential_parent = map->GetBackPointer();
+ if (!potential_parent->IsMap()) return;
+ Map* parent = Map::cast(potential_parent);
+
+ // Follow back pointer, check whether we are dealing with a map transition
+ // from a live map to a dead path and in case clear transitions of parent.
+ bool current_is_alive = map_mark.Get();
+ bool parent_is_alive = Marking::MarkBitFrom(parent).Get();
+ if (!current_is_alive && parent_is_alive) {
+ parent->ClearNonLiveTransitions(heap());
+ }
+}
+
+
+void MarkCompactCollector::ClearAndDeoptimizeDependentCode(Map* map) {
+ DisallowHeapAllocation no_allocation;
+ DependentCode* entries = map->dependent_code();
+ DependentCode::GroupStartIndexes starts(entries);
+ int number_of_entries = starts.number_of_entries();
+ if (number_of_entries == 0) return;
+ for (int i = 0; i < number_of_entries; i++) {
+ // If the entry is compilation info then the map must be alive,
+ // and ClearAndDeoptimizeDependentCode shouldn't be called.
+ ASSERT(entries->is_code_at(i));
+ Code* code = entries->code_at(i);
+
+ if (IsMarked(code) && !WillBeDeoptimized(code)) {
+ // Insert the code into the code_to_deoptimize linked list.
+ Object* next = code_to_deoptimize_;
+ if (next != Smi::FromInt(0)) {
+ // Record the slot so that it is updated.
+ Object** slot = code->code_to_deoptimize_link_slot();
+ RecordSlot(slot, slot, next);
+ }
+ code->set_code_to_deoptimize_link(next);
+ code_to_deoptimize_ = code;
+ }
+ entries->clear_at(i);
+ }
+ map->set_dependent_code(DependentCode::cast(heap()->empty_fixed_array()));
+}
+
+
+void MarkCompactCollector::ClearNonLiveDependentCode(DependentCode* entries) {
+ DisallowHeapAllocation no_allocation;
+ DependentCode::GroupStartIndexes starts(entries);
+ int number_of_entries = starts.number_of_entries();
+ if (number_of_entries == 0) return;
+ int new_number_of_entries = 0;
+ // Go through all groups, remove dead codes and compact.
+ for (int g = 0; g < DependentCode::kGroupCount; g++) {
+ int group_number_of_entries = 0;
+ for (int i = starts.at(g); i < starts.at(g + 1); i++) {
+ Object* obj = entries->object_at(i);
+ ASSERT(obj->IsCode() || IsMarked(obj));
+ if (IsMarked(obj) &&
+ (!obj->IsCode() || !WillBeDeoptimized(Code::cast(obj)))) {
+ if (new_number_of_entries + group_number_of_entries != i) {
+ entries->set_object_at(
+ new_number_of_entries + group_number_of_entries, obj);
+ }
+ Object** slot = entries->slot_at(new_number_of_entries +
+ group_number_of_entries);
+ RecordSlot(slot, slot, obj);
+ group_number_of_entries++;
+ }
+ }
+ entries->set_number_of_entries(
+ static_cast<DependentCode::DependencyGroup>(g),
+ group_number_of_entries);
+ new_number_of_entries += group_number_of_entries;
+ }
+ for (int i = new_number_of_entries; i < number_of_entries; i++) {
+ entries->clear_at(i);
+ }
+}
+
+
+void MarkCompactCollector::ProcessWeakCollections() {
+ GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_WEAKCOLLECTION_PROCESS);
+ Object* weak_collection_obj = encountered_weak_collections();
+ while (weak_collection_obj != Smi::FromInt(0)) {
+ ASSERT(MarkCompactCollector::IsMarked(
+ HeapObject::cast(weak_collection_obj)));
+ JSWeakCollection* weak_collection =
+ reinterpret_cast<JSWeakCollection*>(weak_collection_obj);
+ ObjectHashTable* table = ObjectHashTable::cast(weak_collection->table());
+ Object** anchor = reinterpret_cast<Object**>(table->address());
+ for (int i = 0; i < table->Capacity(); i++) {
+ if (MarkCompactCollector::IsMarked(HeapObject::cast(table->KeyAt(i)))) {
+ Object** key_slot =
+ HeapObject::RawField(table, FixedArray::OffsetOfElementAt(
+ ObjectHashTable::EntryToIndex(i)));
+ RecordSlot(anchor, key_slot, *key_slot);
+ Object** value_slot =
+ HeapObject::RawField(table, FixedArray::OffsetOfElementAt(
+ ObjectHashTable::EntryToValueIndex(i)));
+ MarkCompactMarkingVisitor::MarkObjectByPointer(
+ this, anchor, value_slot);
+ }
+ }
+ weak_collection_obj = weak_collection->next();
+ }
+}
+
+
+void MarkCompactCollector::ClearWeakCollections() {
+ GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_WEAKCOLLECTION_CLEAR);
+ Object* weak_collection_obj = encountered_weak_collections();
+ while (weak_collection_obj != Smi::FromInt(0)) {
+ ASSERT(MarkCompactCollector::IsMarked(
+ HeapObject::cast(weak_collection_obj)));
+ JSWeakCollection* weak_collection =
+ reinterpret_cast<JSWeakCollection*>(weak_collection_obj);
+ ObjectHashTable* table = ObjectHashTable::cast(weak_collection->table());
+ for (int i = 0; i < table->Capacity(); i++) {
+ if (!MarkCompactCollector::IsMarked(HeapObject::cast(table->KeyAt(i)))) {
+ table->RemoveEntry(i);
+ }
+ }
+ weak_collection_obj = weak_collection->next();
+ weak_collection->set_next(Smi::FromInt(0));
+ }
+ set_encountered_weak_collections(Smi::FromInt(0));
+}
+
+
+// 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. Forwarding address is written directly into
+// first word of object without any encoding. If object is dead we write
+// NULL as a forwarding address.
+//
+// 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));
+ // TODO(hpayer): Replace that check with an assert.
+ CHECK(dest != LO_SPACE && size <= Page::kMaxNonCodeHeapObjectSize);
+ if (dest == OLD_POINTER_SPACE) {
+ // TODO(hpayer): Replace this check with an assert.
+ HeapObject* heap_object = HeapObject::FromAddress(src);
+ CHECK(heap_->TargetSpace(heap_object) == heap_->old_pointer_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);
+ }
+
+ src_slot += kPointerSize;
+ dst_slot += kPointerSize;
+ }
+
+ if (compacting_ && HeapObject::FromAddress(dst)->IsJSFunction()) {
+ Address code_entry_slot = dst + JSFunction::kCodeEntryOffset;
+ Address code_entry = Memory::Address_at(code_entry_slot);
+
+ if (Page::FromAddress(code_entry)->IsEvacuationCandidate()) {
+ SlotsBuffer::AddTo(&slots_buffer_allocator_,
+ &migration_slots_buffer_,
+ SlotsBuffer::CODE_ENTRY_SLOT,
+ code_entry_slot,
+ SlotsBuffer::IGNORE_OVERFLOW);
+ }
+ }
+ } else if (dest == CODE_SPACE) {
+ PROFILE(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 {
+ ASSERT(dest == OLD_DATA_SPACE || dest == NEW_SPACE);
+ // Objects in old data space can just be moved by compaction to a different
+ // page in old data space.
+ // TODO(hpayer): Replace the following check with an assert.
+ CHECK(!heap_->old_data_space()->Contains(src) ||
+ (heap_->old_data_space()->Contains(dst) &&
+ heap_->TargetSpace(HeapObject::FromAddress(src)) ==
+ heap_->old_data_space()));
+ heap()->MoveBlock(dst, src, size);
+ }
+ Memory::Address_at(src) = dst;
+}
+
+
+// Visitor for updating pointers from live objects in old spaces to new space.
+// It does not expect to encounter pointers to dead objects.
+class PointersUpdatingVisitor: public ObjectVisitor {
+ public:
+ explicit PointersUpdatingVisitor(Heap* heap) : heap_(heap) { }
+
+ void VisitPointer(Object** p) {
+ UpdatePointer(p);
+ }
+
+ void VisitPointers(Object** start, Object** end) {
+ for (Object** p = start; p < end; p++) UpdatePointer(p);
+ }
+
+ void VisitEmbeddedPointer(RelocInfo* rinfo) {
+ ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT);
+ Object* target = rinfo->target_object();
+ Object* old_target = target;
+ VisitPointer(&target);
+ // Avoid unnecessary changes that might unnecessary flush the instruction
+ // cache.
+ if (target != old_target) {
+ rinfo->set_target_object(target);
+ }
+ }
+
+ void VisitCodeTarget(RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
+ Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ Object* old_target = target;
+ VisitPointer(&target);
+ if (target != old_target) {
+ rinfo->set_target_address(Code::cast(target)->instruction_start());
+ }
+ }
+
+ void VisitCodeAgeSequence(RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsCodeAgeSequence(rinfo->rmode()));
+ Object* stub = rinfo->code_age_stub();
+ ASSERT(stub != NULL);
+ VisitPointer(&stub);
+ if (stub != rinfo->code_age_stub()) {
+ rinfo->set_code_age_stub(Code::cast(stub));
+ }
+ }
+
+ 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(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_;
+};
+
+
+static void UpdatePointer(HeapObject** p, HeapObject* object) {
+ ASSERT(*p == object);
+
+ Address old_addr = object->address();
+
+ Address new_addr = Memory::Address_at(old_addr);
+
+ // 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.
+ // TODO(mstarzinger): This was changed to a sentinel value to track down
+ // rare crashes, change it back to Smi::FromInt(0) later.
+ *p = reinterpret_cast<HeapObject*>(Smi::FromInt(0x0f100d00 >> 1)); // flood
+ }
+}
+
+
+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);
+}
+
+
+bool MarkCompactCollector::TryPromoteObject(HeapObject* object,
+ int object_size) {
+ // TODO(hpayer): Replace that check with an assert.
+ CHECK(object_size <= Page::kMaxNonCodeHeapObjectSize);
+
+ OldSpace* target_space = heap()->TargetSpace(object);
+
+ ASSERT(target_space == heap()->old_pointer_space() ||
+ target_space == heap()->old_data_space());
+ Object* result;
+ MaybeObject* maybe_result = target_space->AllocateRaw(object_size);
+ if (maybe_result->ToObject(&result)) {
+ HeapObject* target = HeapObject::cast(result);
+ MigrateObject(target->address(),
+ object->address(),
+ object_size,
+ target_space->identity());
+ heap()->mark_compact_collector()->tracer()->
+ increment_promoted_objects_size(object_size);
+ return true;
+ }
+
+ return false;
+}
+
+
+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();
+
+ // 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.
+ new_space->Flip();
+ new_space->ResetAllocationInfo();
+
+ int survivors_size = 0;
+
+ // First pass: traverse all objects in inactive semispace, remove marks,
+ // 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.
+ NewSpacePageIterator it(from_bottom, from_top);
+ while (it.has_next()) {
+ NewSpacePage* p = it.next();
+ survivors_size += DiscoverAndPromoteBlackObjectsOnPage(new_space, p);
+ }
+
+ heap_->IncrementYoungSurvivorsCounter(survivors_size);
+ new_space->set_age_mark(new_space->top());
+}
+
+
+void MarkCompactCollector::EvacuateLiveObjectsFromPage(Page* p) {
+ AlwaysAllocateScope always_allocate;
+ PagedSpace* space = static_cast<PagedSpace*>(p->owner());
+ ASSERT(p->IsEvacuationCandidate() && !p->WasSwept());
+ p->MarkSweptPrecisely();
+
+ int offsets[16];
+
+ for (MarkBitCellIterator it(p); !it.Done(); it.Advance()) {
+ Address cell_base = it.CurrentCellBase();
+ MarkBit::CellType* cell = it.CurrentCell();
+
+ if (*cell == 0) continue;
+
+ int live_objects = MarkWordToObjectStarts(*cell, 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;
+ }
+
+ Object* target_object = target->ToObjectUnchecked();
+
+ MigrateObject(HeapObject::cast(target_object)->address(),
+ object_addr,
+ size,
+ space->identity());
+ ASSERT(object->map_word().IsForwardingAddress());
+ }
+
+ // Clear marking bits for current cell.
+ *cell = 0;
+ }
+ p->ResetLiveBytes();
+}
+
+
+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 {
+ // 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);
+ page->InsertAfter(static_cast<PagedSpace*>(page->owner())->anchor());
+ }
+ return;
+ }
+ }
+ }
+}
+
+
+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();
+ }
+ }
+ return object;
+ }
+};
+
+
+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;
+ }
+ 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;
+ }
+}
+
+
+enum SweepingMode {
+ SWEEP_ONLY,
+ SWEEP_AND_VISIT_LIVE_OBJECTS
+};
+
+
+enum SkipListRebuildingMode {
+ REBUILD_SKIP_LIST,
+ IGNORE_SKIP_LIST
+};
+
+
+// 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));
+
+ double start_time = 0.0;
+ if (FLAG_print_cumulative_gc_stat) {
+ start_time = OS::TimeCurrentMillis();
+ }
+
+ p->MarkSweptPrecisely();
+
+ Address free_start = p->area_start();
+ ASSERT(reinterpret_cast<intptr_t>(free_start) % (32 * kPointerSize) == 0);
+ 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 (MarkBitCellIterator it(p); !it.Done(); it.Advance()) {
+ Address cell_base = it.CurrentCellBase();
+ MarkBit::CellType* cell = it.CurrentCell();
+ int live_objects = MarkWordToObjectStarts(*cell, offsets);
+ int live_index = 0;
+ for ( ; live_objects != 0; live_objects--) {
+ Address free_end = cell_base + offsets[live_index++] * kPointerSize;
+ if (free_end != free_start) {
+ space->Free(free_start, static_cast<int>(free_end - free_start));
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ if (FLAG_gdbjit && space->identity() == CODE_SPACE) {
+ GDBJITInterface::RemoveCodeRange(free_start, free_end);
+ }
+#endif
+ }
+ 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;
+ }
+ // Clear marking bits for current cell.
+ *cell = 0;
+ }
+ if (free_start != p->area_end()) {
+ space->Free(free_start, static_cast<int>(p->area_end() - free_start));
+#ifdef ENABLE_GDB_JIT_INTERFACE
+ if (FLAG_gdbjit && space->identity() == CODE_SPACE) {
+ GDBJITInterface::RemoveCodeRange(free_start, p->area_end());
+ }
+#endif
+ }
+ p->ResetLiveBytes();
+ if (FLAG_print_cumulative_gc_stat) {
+ space->heap()->AddSweepingTime(OS::TimeCurrentMillis() - start_time);
+ }
+}
+
+
+static bool SetMarkBitsUnderInvalidatedCode(Code* code, bool value) {
+ Page* p = Page::FromAddress(code->address());
+
+ if (p->IsEvacuationCandidate() ||
+ p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) {
+ return false;
+ }
+
+ Address code_start = code->address();
+ Address code_end = code_start + code->Size();
+
+ uint32_t start_index = MemoryChunk::FastAddressToMarkbitIndex(code_start);
+ uint32_t end_index =
+ MemoryChunk::FastAddressToMarkbitIndex(code_end - kPointerSize);
+
+ Bitmap* b = p->markbits();
+
+ MarkBit start_mark_bit = b->MarkBitFromIndex(start_index);
+ MarkBit end_mark_bit = b->MarkBitFromIndex(end_index);
+
+ MarkBit::CellType* start_cell = start_mark_bit.cell();
+ MarkBit::CellType* end_cell = end_mark_bit.cell();
+
+ if (value) {
+ MarkBit::CellType start_mask = ~(start_mark_bit.mask() - 1);
+ MarkBit::CellType end_mask = (end_mark_bit.mask() << 1) - 1;
+
+ 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;
+ }
+ } else {
+ for (MarkBit::CellType* cell = start_cell ; cell <= end_cell; cell++) {
+ *cell = 0;
+ }
+ }
+
+ return true;
+}
+
+
+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);
+
+ // 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;
+
+ // 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));
+
+ return mark_bit.Get();
+}
+
+
+void MarkCompactCollector::InvalidateCode(Code* code) {
+ if (heap_->incremental_marking()->IsCompacting() &&
+ !ShouldSkipEvacuationSlotRecording(code)) {
+ ASSERT(compacting_);
+
+ // 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;
+
+ invalidated_code_.Add(code);
+ }
+}
+
+
+// Return true if the given code is deoptimized or will be deoptimized.
+bool MarkCompactCollector::WillBeDeoptimized(Code* code) {
+ // We assume the code_to_deoptimize_link is initialized to undefined.
+ // If it is 0, or refers to another Code object, then this code
+ // is already linked, or was already linked into the list.
+ return code->code_to_deoptimize_link() != heap()->undefined_value()
+ || code->marked_for_deoptimization();
+}
+
+
+bool MarkCompactCollector::MarkInvalidatedCode() {
+ bool code_marked = false;
+
+ int length = invalidated_code_.length();
+ for (int i = 0; i < length; i++) {
+ Code* code = invalidated_code_[i];
+
+ if (SetMarkBitsUnderInvalidatedCode(code, true)) {
+ code_marked = true;
+ }
+ }
+
+ return code_marked;
+}
+
+
+void MarkCompactCollector::RemoveDeadInvalidatedCode() {
+ int length = invalidated_code_.length();
+ for (int i = 0; i < length; i++) {
+ if (!IsMarked(invalidated_code_[i])) invalidated_code_[i] = NULL;
+ }
+}
+
+
+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);
+ }
+ }
+ invalidated_code_.Rewind(0);
+}
+
+
+void MarkCompactCollector::EvacuateNewSpaceAndCandidates() {
+ Heap::RelocationLock relocation_lock(heap());
+
+ bool code_slots_filtering_required;
+ { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_SWEEP_NEWSPACE);
+ code_slots_filtering_required = MarkInvalidatedCode();
+ EvacuateNewSpace();
+ }
+
+ { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_EVACUATE_PAGES);
+ EvacuatePages();
+ }
+
+ // 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);
+ }
+ }
+
+ { GCTracer::Scope gc_scope(tracer_,
+ GCTracer::Scope::MC_UPDATE_ROOT_TO_NEW_POINTERS);
+ // Update roots.
+ heap_->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE);
+ }
+
+ { GCTracer::Scope gc_scope(tracer_,
+ GCTracer::Scope::MC_UPDATE_OLD_TO_NEW_POINTERS);
+ StoreBufferRebuildScope scope(heap_,
+ heap_->store_buffer(),
+ &Heap::ScavengeStoreBufferCallback);
+ heap_->store_buffer()->IteratePointersToNewSpaceAndClearMaps(
+ &UpdatePointer);
+ }
+
+ { 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_));
+ }
+
+ 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);
+ }
+ }
+ }
+ }
+
+ 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()));
+ }
+
+ // 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<SWEEP_SEQUENTIALLY>(space, NULL, 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;
+ }
+ }
+ }
+ }
+
+ GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_UPDATE_MISC_POINTERS);
+
+ // Update pointers from cells.
+ HeapObjectIterator cell_iterator(heap_->cell_space());
+ for (HeapObject* cell = cell_iterator.Next();
+ cell != NULL;
+ cell = cell_iterator.Next()) {
+ if (cell->IsCell()) {
+ Cell::BodyDescriptor::IterateBody(cell, &updating_visitor);
+ }
+ }
+
+ HeapObjectIterator js_global_property_cell_iterator(
+ heap_->property_cell_space());
+ for (HeapObject* cell = js_global_property_cell_iterator.Next();
+ cell != NULL;
+ cell = js_global_property_cell_iterator.Next()) {
+ if (cell->IsPropertyCell()) {
+ PropertyCell::BodyDescriptor::IterateBody(cell, &updating_visitor);
+ }
+ }
+
+ // Update the heads of the native contexts list the code to deoptimize list.
+ updating_visitor.VisitPointer(heap_->native_contexts_list_address());
+ updating_visitor.VisitPointer(&code_to_deoptimize_);
+
+ heap_->string_table()->Iterate(&updating_visitor);
+
+ // Update pointers from external string table.
+ heap_->UpdateReferencesInExternalStringTable(
+ &UpdateReferenceInExternalStringTableEntry);
+
+ if (!FLAG_watch_ic_patching) {
+ // Update JSFunction pointers from the runtime profiler.
+ heap()->isolate()->runtime_profiler()->UpdateSamplesAfterCompact(
+ &updating_visitor);
+ }
+
+ EvacuationWeakObjectRetainer evacuation_object_retainer;
+ heap()->ProcessWeakReferences(&evacuation_object_retainer);
+
+ // Visit invalidated code (we ignored all slots on it) and clear mark-bits
+ // under it.
+ ProcessInvalidatedCode(&updating_visitor);
+
+ heap_->isolate()->inner_pointer_to_code_cache()->Flush();
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ VerifyEvacuation(heap_);
+ }
+#endif
+
+ slots_buffer_allocator_.DeallocateChain(&migration_slots_buffer_);
+ ASSERT(migration_slots_buffer_ == NULL);
+}
+
+
+void MarkCompactCollector::UnlinkEvacuationCandidates() {
+ int npages = evacuation_candidates_.length();
+ for (int i = 0; i < npages; i++) {
+ Page* p = evacuation_candidates_[i];
+ if (!p->IsEvacuationCandidate()) continue;
+ p->Unlink();
+ p->ClearSweptPrecisely();
+ p->ClearSweptConservatively();
+ }
+}
+
+
+void MarkCompactCollector::ReleaseEvacuationCandidates() {
+ int npages = evacuation_candidates_.length();
+ 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->area_start(), p->area_size());
+ p->set_scan_on_scavenge(false);
+ slots_buffer_allocator_.DeallocateChain(p->slots_buffer_address());
+ p->ResetLiveBytes();
+ space->ReleasePage(p, false);
+ }
+ evacuation_candidates_.Rewind(0);
+ compacting_ = false;
+ heap()->FreeQueuedChunks();
+}
+
+
+static const int kStartTableEntriesPerLine = 5;
+static const int kStartTableLines = 171;
+static const int kStartTableInvalidLine = 127;
+static const int kStartTableUnusedEntry = 126;
+
+#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;
+}
+
+
+static inline Address DigestFreeStart(Address approximate_free_start,
+ uint32_t free_start_cell) {
+ ASSERT(free_start_cell != 0);
+
+ // No consecutive 1 bits.
+ ASSERT((free_start_cell & (free_start_cell << 1)) == 0);
+
+ 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;
+}
+
+
+static inline Address StartOfLiveObject(Address block_address, uint32_t cell) {
+ ASSERT(cell != 0);
+
+ // No consecutive 1 bits.
+ ASSERT((cell & (cell << 1)) == 0);
+
+ 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;
+}
+
+
+template<MarkCompactCollector::SweepingParallelism mode>
+static intptr_t Free(PagedSpace* space,
+ FreeList* free_list,
+ Address start,
+ int size) {
+ if (mode == MarkCompactCollector::SWEEP_SEQUENTIALLY) {
+ return space->Free(start, size);
+ } else {
+ return size - free_list->Free(start, size);
+ }
+}
+
+
+// Force instantiation of templatized SweepConservatively method for
+// SWEEP_SEQUENTIALLY mode.
+template intptr_t MarkCompactCollector::
+ SweepConservatively<MarkCompactCollector::SWEEP_SEQUENTIALLY>(
+ PagedSpace*, FreeList*, Page*);
+
+
+// Force instantiation of templatized SweepConservatively method for
+// SWEEP_IN_PARALLEL mode.
+template intptr_t MarkCompactCollector::
+ SweepConservatively<MarkCompactCollector::SWEEP_IN_PARALLEL>(
+ PagedSpace*, FreeList*, Page*);
+
+
+// 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.
+template<MarkCompactCollector::SweepingParallelism mode>
+intptr_t MarkCompactCollector::SweepConservatively(PagedSpace* space,
+ FreeList* free_list,
+ Page* p) {
+ ASSERT(!p->IsEvacuationCandidate() && !p->WasSwept());
+ ASSERT((mode == MarkCompactCollector::SWEEP_IN_PARALLEL &&
+ free_list != NULL) ||
+ (mode == MarkCompactCollector::SWEEP_SEQUENTIALLY &&
+ free_list == NULL));
+
+ p->MarkSweptConservatively();
+
+ intptr_t freed_bytes = 0;
+ size_t size = 0;
+
+ // Skip over all the dead objects at the start of the page and mark them free.
+ Address cell_base = 0;
+ MarkBit::CellType* cell = NULL;
+ MarkBitCellIterator it(p);
+ for (; !it.Done(); it.Advance()) {
+ cell_base = it.CurrentCellBase();
+ cell = it.CurrentCell();
+ if (*cell != 0) break;
+ }
+
+ if (it.Done()) {
+ size = p->area_end() - p->area_start();
+ freed_bytes += Free<mode>(space, free_list, p->area_start(),
+ 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(cell_base, *cell);
+ // Free the first free space.
+ size = free_end - p->area_start();
+ freed_bytes += Free<mode>(space, free_list, p->area_start(),
+ 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 = cell_base;
+ MarkBit::CellType free_start_cell = *cell;
+
+ for (; !it.Done(); it.Advance()) {
+ cell_base = it.CurrentCellBase();
+ cell = it.CurrentCell();
+ if (*cell != 0) {
+ // We have a live object. Check approximately whether it is more than 32
+ // words since the last live object.
+ if (cell_base - free_start > 32 * kPointerSize) {
+ free_start = DigestFreeStart(free_start, free_start_cell);
+ if (cell_base - 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(cell_base, *cell);
+ freed_bytes += Free<mode>(space, free_list, free_start,
+ static_cast<int>(free_end - free_start));
+ }
+ }
+ // Update our undigested record of where the current free area started.
+ free_start = cell_base;
+ free_start_cell = *cell;
+ // Clear marking bits for current cell.
+ *cell = 0;
+ }
+ }
+
+ // Handle the free space at the end of the page.
+ if (cell_base - free_start > 32 * kPointerSize) {
+ free_start = DigestFreeStart(free_start, free_start_cell);
+ freed_bytes += Free<mode>(space, free_list, free_start,
+ static_cast<int>(p->area_end() - free_start));
+ }
+
+ p->ResetLiveBytes();
+ return freed_bytes;
+}
+
+
+void MarkCompactCollector::SweepInParallel(PagedSpace* space,
+ FreeList* private_free_list,
+ FreeList* free_list) {
+ PageIterator it(space);
+ while (it.has_next()) {
+ Page* p = it.next();
+
+ if (p->TryParallelSweeping()) {
+ SweepConservatively<SWEEP_IN_PARALLEL>(space, private_free_list, p);
+ free_list->Concatenate(private_free_list);
+ }
+ }
+}
+
+
+void MarkCompactCollector::SweepSpace(PagedSpace* space, SweeperType sweeper) {
+ space->set_was_swept_conservatively(sweeper == CONSERVATIVE ||
+ sweeper == LAZY_CONSERVATIVE ||
+ sweeper == PARALLEL_CONSERVATIVE ||
+ sweeper == CONCURRENT_CONSERVATIVE);
+ space->ClearStats();
+
+ PageIterator it(space);
+
+ int pages_swept = 0;
+ bool lazy_sweeping_active = false;
+ bool unused_page_present = false;
+ bool parallel_sweeping_active = false;
+
+ while (it.has_next()) {
+ Page* p = it.next();
+
+ ASSERT(p->parallel_sweeping() == 0);
+ ASSERT(!p->IsEvacuationCandidate());
+
+ // Clear sweeping flags indicating that marking bits are still intact.
+ p->ClearSweptPrecisely();
+ p->ClearSweptConservatively();
+
+ if (p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) {
+ // Will be processed in EvacuateNewSpaceAndCandidates.
+ ASSERT(evacuation_candidates_.length() > 0);
+ continue;
+ }
+
+ // 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, true);
+ continue;
+ }
+ unused_page_present = true;
+ }
+
+ switch (sweeper) {
+ case CONSERVATIVE: {
+ if (FLAG_gc_verbose) {
+ PrintF("Sweeping 0x%" V8PRIxPTR " conservatively.\n",
+ reinterpret_cast<intptr_t>(p));
+ }
+ SweepConservatively<SWEEP_SEQUENTIALLY>(space, NULL, p);
+ pages_swept++;
+ break;
+ }
+ case LAZY_CONSERVATIVE: {
+ if (lazy_sweeping_active) {
+ if (FLAG_gc_verbose) {
+ PrintF("Sweeping 0x%" V8PRIxPTR " lazily postponed.\n",
+ reinterpret_cast<intptr_t>(p));
+ }
+ space->IncreaseUnsweptFreeBytes(p);
+ } else {
+ if (FLAG_gc_verbose) {
+ PrintF("Sweeping 0x%" V8PRIxPTR " conservatively.\n",
+ reinterpret_cast<intptr_t>(p));
+ }
+ SweepConservatively<SWEEP_SEQUENTIALLY>(space, NULL, p);
+ pages_swept++;
+ space->SetPagesToSweep(p->next_page());
+ lazy_sweeping_active = true;
+ }
+ break;
+ }
+ case CONCURRENT_CONSERVATIVE:
+ case PARALLEL_CONSERVATIVE: {
+ if (!parallel_sweeping_active) {
+ if (FLAG_gc_verbose) {
+ PrintF("Sweeping 0x%" V8PRIxPTR " conservatively.\n",
+ reinterpret_cast<intptr_t>(p));
+ }
+ SweepConservatively<SWEEP_SEQUENTIALLY>(space, NULL, p);
+ pages_swept++;
+ parallel_sweeping_active = true;
+ } else {
+ if (FLAG_gc_verbose) {
+ PrintF("Sweeping 0x%" V8PRIxPTR " conservatively in parallel.\n",
+ reinterpret_cast<intptr_t>(p));
+ }
+ p->set_parallel_sweeping(1);
+ space->IncreaseUnsweptFreeBytes(p);
+ }
+ 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();
+ }
+ }
+ }
+
+ if (FLAG_gc_verbose) {
+ PrintF("SweepSpace: %s (%d pages swept)\n",
+ AllocationSpaceName(space->identity()),
+ pages_swept);
+ }
+
+ // Give pages that are queued to be freed back to the OS.
+ heap()->FreeQueuedChunks();
+}
+
+
+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_parallel_sweeping) how_to_sweep = PARALLEL_CONSERVATIVE;
+ if (FLAG_concurrent_sweeping) how_to_sweep = CONCURRENT_CONSERVATIVE;
+ if (FLAG_expose_gc) how_to_sweep = CONSERVATIVE;
+ if (sweep_precisely_) how_to_sweep = PRECISE;
+
+ // Unlink evacuation candidates before sweeper threads access the list of
+ // pages to avoid race condition.
+ UnlinkEvacuationCandidates();
+
+ // 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.
+ SequentialSweepingScope scope(this);
+ SweepSpace(heap()->old_pointer_space(), how_to_sweep);
+ SweepSpace(heap()->old_data_space(), how_to_sweep);
+
+ if (how_to_sweep == PARALLEL_CONSERVATIVE ||
+ how_to_sweep == CONCURRENT_CONSERVATIVE) {
+ // TODO(hpayer): fix race with concurrent sweeper
+ StartSweeperThreads();
+ }
+
+ if (how_to_sweep == PARALLEL_CONSERVATIVE) {
+ WaitUntilSweepingCompleted();
+ }
+
+ RemoveDeadInvalidatedCode();
+ SweepSpace(heap()->code_space(), PRECISE);
+
+ SweepSpace(heap()->cell_space(), PRECISE);
+ SweepSpace(heap()->property_cell_space(), PRECISE);
+
+ EvacuateNewSpaceAndCandidates();
+
+ // 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);
+
+ // Deallocate unmarked objects and clear marked bits for marked objects.
+ heap_->lo_space()->FreeUnmarkedObjects();
+
+ // Deallocate evacuated candidate pages.
+ ReleaseEvacuationCandidates();
+}
+
+
+void MarkCompactCollector::EnableCodeFlushing(bool enable) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (isolate()->debug()->IsLoaded() ||
+ isolate()->debug()->has_break_points()) {
+ enable = false;
+ }
+#endif
+
+ if (enable) {
+ if (code_flusher_ != NULL) return;
+ code_flusher_ = new CodeFlusher(isolate());
+ } else {
+ if (code_flusher_ == NULL) return;
+ code_flusher_->EvictAllCandidates();
+ delete code_flusher_;
+ code_flusher_ = NULL;
+ }
+
+ if (FLAG_trace_code_flushing) {
+ PrintF("[code-flushing is now %s]\n", enable ? "on" : "off");
+ }
+}
+
+
+// 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()));
+ }
+}
+
+
+Isolate* MarkCompactCollector::isolate() const {
+ return heap_->isolate();
+}
+
+
+void MarkCompactCollector::Initialize() {
+ MarkCompactMarkingVisitor::Initialize();
+ IncrementalMarking::Initialize();
+}
+
+
+bool SlotsBuffer::IsTypedSlot(ObjectSlot slot) {
+ return reinterpret_cast<uintptr_t>(slot) < NUMBER_OF_SLOT_TYPES;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
+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);
+ }
+ }
+}
+
+
+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);
+ }
+ }
+}
+
+
+void MarkCompactCollector::RecordCodeTargetPatch(Address pc, Code* target) {
+ ASSERT(heap()->gc_state() == Heap::MARK_COMPACT);
+ if (is_compacting()) {
+ Code* host = isolate()->inner_pointer_to_code_cache()->
+ GcSafeFindCodeForInnerPointer(pc);
+ MarkBit mark_bit = Marking::MarkBitFrom(host);
+ if (Marking::IsBlack(mark_bit)) {
+ RelocInfo rinfo(pc, RelocInfo::CODE_TARGET, 0, host);
+ RecordRelocSlot(&rinfo, target);
+ }
+ }
+}
+
+
+static inline SlotsBuffer::SlotType DecodeSlotType(
+ SlotsBuffer::ObjectSlot slot) {
+ return static_cast<SlotsBuffer::SlotType>(reinterpret_cast<intptr_t>(slot));
+}
+
+
+void SlotsBuffer::UpdateSlots(Heap* heap) {
+ PointersUpdatingVisitor v(heap);
+
+ 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]));
+ }
+ }
+}
+
+
+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]));
+ }
+ }
+ }
+}
+
+
+SlotsBuffer* SlotsBufferAllocator::AllocateBuffer(SlotsBuffer* next_buffer) {
+ return new SlotsBuffer(next_buffer);
+}
+
+
+void SlotsBufferAllocator::DeallocateBuffer(SlotsBuffer* buffer) {
+ delete buffer;
+}
+
+
+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;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/mark-compact.h b/chromium/v8/src/mark-compact.h
new file mode 100644
index 00000000000..ee845a08370
--- /dev/null
+++ b/chromium/v8/src/mark-compact.h
@@ -0,0 +1,1025 @@
+// Copyright 2012 the V8 project authors. 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_H_
+#define V8_MARK_COMPACT_H_
+
+#include "compiler-intrinsics.h"
+#include "spaces.h"
+
+namespace v8 {
+namespace internal {
+
+// Callback function, returns whether an object is alive. The heap size
+// of the object is returned in size. It optionally updates the offset
+// to the first live object in the page (only used for old and map objects).
+typedef bool (*IsAliveFunction)(HeapObject* obj, int* size, int* offset);
+
+// Forward declarations.
+class CodeFlusher;
+class GCTracer;
+class MarkCompactCollector;
+class MarkingVisitor;
+class RootMarkingVisitor;
+
+
+class Marking {
+ public:
+ explicit Marking(Heap* heap)
+ : heap_(heap) {
+ }
+
+ INLINE(static MarkBit MarkBitFrom(Address addr));
+
+ INLINE(static MarkBit MarkBitFrom(HeapObject* obj)) {
+ return MarkBitFrom(reinterpret_cast<Address>(obj));
+ }
+
+ // Impossible markbits: 01
+ static const char* kImpossibleBitPattern;
+ INLINE(static 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;
+ INLINE(static 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;
+ INLINE(static bool IsWhite(MarkBit mark_bit)) {
+ return !mark_bit.Get();
+ }
+
+ // Grey markbits: 11
+ static const char* kGreyBitPattern;
+ INLINE(static bool IsGrey(MarkBit mark_bit)) {
+ return mark_bit.Get() && mark_bit.Next().Get();
+ }
+
+ INLINE(static void MarkBlack(MarkBit mark_bit)) {
+ mark_bit.Set();
+ mark_bit.Next().Clear();
+ }
+
+ INLINE(static void BlackToGrey(MarkBit markbit)) {
+ markbit.Next().Set();
+ }
+
+ INLINE(static void WhiteToGrey(MarkBit markbit)) {
+ markbit.Set();
+ markbit.Next().Set();
+ }
+
+ INLINE(static void GreyToBlack(MarkBit markbit)) {
+ markbit.Next().Clear();
+ }
+
+ INLINE(static void BlackToGrey(HeapObject* obj)) {
+ BlackToGrey(MarkBitFrom(obj));
+ }
+
+ INLINE(static 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 deque for tracing live objects.
+class MarkingDeque {
+ public:
+ MarkingDeque()
+ : array_(NULL), top_(0), bottom_(0), mask_(0), overflowed_(false) { }
+
+ void Initialize(Address low, Address 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;
+ }
+
+ inline bool IsFull() { return ((top_ + 1) & mask_) == bottom_; }
+
+ inline bool IsEmpty() { return top_ == bottom_; }
+
+ bool overflowed() const { return overflowed_; }
+
+ 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.
+ INLINE(void PushBlack(HeapObject* object)) {
+ ASSERT(object->IsHeapObject());
+ if (IsFull()) {
+ Marking::BlackToGrey(object);
+ MemoryChunk::IncrementLiveBytesFromGC(object->address(), -object->Size());
+ SetOverflowed();
+ } else {
+ array_[top_] = object;
+ top_ = ((top_ + 1) & mask_);
+ }
+ }
+
+ 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** 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(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
+ };
+
+ static const char* SlotTypeToString(SlotType type) {
+ switch (type) {
+ case EMBEDDED_OBJECT_SLOT:
+ return "EMBEDDED_OBJECT_SLOT";
+ case RELOCATED_CODE_OBJECT:
+ return "RELOCATED_CODE_OBJECT";
+ case CODE_TARGET_SLOT:
+ return "CODE_TARGET_SLOT";
+ case CODE_ENTRY_SLOT:
+ return "CODE_ENTRY_SLOT";
+ case DEBUG_TARGET_SLOT:
+ return "DEBUG_TARGET_SLOT";
+ case JS_RETURN_SLOT:
+ return "JS_RETURN_SLOT";
+ case NUMBER_OF_SLOT_TYPES:
+ return "NUMBER_OF_SLOT_TYPES";
+ }
+ return "UNKNOWN SlotType";
+ }
+
+ 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;
+ }
+
+ INLINE(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];
+};
+
+
+// CodeFlusher collects candidates for code flushing during marking and
+// processes those candidates after marking has completed in order to
+// reset those functions referencing code objects that would otherwise
+// be unreachable. Code objects can be referenced in three ways:
+// - SharedFunctionInfo references unoptimized code.
+// - JSFunction references either unoptimized or optimized code.
+// - OptimizedCodeMap references optimized code.
+// We are not allowed to flush unoptimized code for functions that got
+// optimized or inlined into optimized code, because we might bailout
+// into the unoptimized code again during deoptimization.
+class CodeFlusher {
+ public:
+ explicit CodeFlusher(Isolate* isolate)
+ : isolate_(isolate),
+ jsfunction_candidates_head_(NULL),
+ shared_function_info_candidates_head_(NULL),
+ optimized_code_map_holder_head_(NULL) {}
+
+ void AddCandidate(SharedFunctionInfo* shared_info) {
+ if (GetNextCandidate(shared_info) == NULL) {
+ SetNextCandidate(shared_info, shared_function_info_candidates_head_);
+ shared_function_info_candidates_head_ = shared_info;
+ }
+ }
+
+ void AddCandidate(JSFunction* function) {
+ ASSERT(function->code() == function->shared()->code());
+ if (GetNextCandidate(function)->IsUndefined()) {
+ SetNextCandidate(function, jsfunction_candidates_head_);
+ jsfunction_candidates_head_ = function;
+ }
+ }
+
+ void AddOptimizedCodeMap(SharedFunctionInfo* code_map_holder) {
+ if (GetNextCodeMap(code_map_holder)->IsUndefined()) {
+ SetNextCodeMap(code_map_holder, optimized_code_map_holder_head_);
+ optimized_code_map_holder_head_ = code_map_holder;
+ }
+ }
+
+ void EvictOptimizedCodeMap(SharedFunctionInfo* code_map_holder);
+ void EvictCandidate(SharedFunctionInfo* shared_info);
+ void EvictCandidate(JSFunction* function);
+
+ void ProcessCandidates() {
+ ProcessOptimizedCodeMaps();
+ ProcessSharedFunctionInfoCandidates();
+ ProcessJSFunctionCandidates();
+ }
+
+ void EvictAllCandidates() {
+ EvictOptimizedCodeMaps();
+ EvictJSFunctionCandidates();
+ EvictSharedFunctionInfoCandidates();
+ }
+
+ void IteratePointersToFromSpace(ObjectVisitor* v);
+
+ private:
+ void ProcessOptimizedCodeMaps();
+ void ProcessJSFunctionCandidates();
+ void ProcessSharedFunctionInfoCandidates();
+ void EvictOptimizedCodeMaps();
+ void EvictJSFunctionCandidates();
+ void EvictSharedFunctionInfoCandidates();
+
+ static JSFunction** GetNextCandidateSlot(JSFunction* candidate) {
+ return reinterpret_cast<JSFunction**>(
+ HeapObject::RawField(candidate, JSFunction::kNextFunctionLinkOffset));
+ }
+
+ static JSFunction* GetNextCandidate(JSFunction* candidate) {
+ Object* next_candidate = candidate->next_function_link();
+ return reinterpret_cast<JSFunction*>(next_candidate);
+ }
+
+ static void SetNextCandidate(JSFunction* candidate,
+ JSFunction* next_candidate) {
+ candidate->set_next_function_link(next_candidate);
+ }
+
+ static void ClearNextCandidate(JSFunction* candidate, Object* undefined) {
+ ASSERT(undefined->IsUndefined());
+ candidate->set_next_function_link(undefined, SKIP_WRITE_BARRIER);
+ }
+
+ static SharedFunctionInfo* GetNextCandidate(SharedFunctionInfo* candidate) {
+ Object* next_candidate = candidate->code()->gc_metadata();
+ return reinterpret_cast<SharedFunctionInfo*>(next_candidate);
+ }
+
+ static void SetNextCandidate(SharedFunctionInfo* candidate,
+ SharedFunctionInfo* next_candidate) {
+ candidate->code()->set_gc_metadata(next_candidate);
+ }
+
+ static void ClearNextCandidate(SharedFunctionInfo* candidate) {
+ candidate->code()->set_gc_metadata(NULL, SKIP_WRITE_BARRIER);
+ }
+
+ static SharedFunctionInfo* GetNextCodeMap(SharedFunctionInfo* holder) {
+ FixedArray* code_map = FixedArray::cast(holder->optimized_code_map());
+ Object* next_map = code_map->get(SharedFunctionInfo::kNextMapIndex);
+ return reinterpret_cast<SharedFunctionInfo*>(next_map);
+ }
+
+ static void SetNextCodeMap(SharedFunctionInfo* holder,
+ SharedFunctionInfo* next_holder) {
+ FixedArray* code_map = FixedArray::cast(holder->optimized_code_map());
+ code_map->set(SharedFunctionInfo::kNextMapIndex, next_holder);
+ }
+
+ static void ClearNextCodeMap(SharedFunctionInfo* holder) {
+ FixedArray* code_map = FixedArray::cast(holder->optimized_code_map());
+ code_map->set_undefined(SharedFunctionInfo::kNextMapIndex);
+ }
+
+ Isolate* isolate_;
+ JSFunction* jsfunction_candidates_head_;
+ SharedFunctionInfo* shared_function_info_candidates_head_;
+ SharedFunctionInfo* optimized_code_map_holder_head_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodeFlusher);
+};
+
+
+// Defined in isolate.h.
+class ThreadLocalTop;
+
+
+// -------------------------------------------------------------------------
+// Mark-Compact collector
+class MarkCompactCollector {
+ public:
+ // Type of functions to compute forwarding addresses of objects in
+ // compacted spaces. Given an object and its size, return a (non-failure)
+ // Object* that will be the object after forwarding. There is a separate
+ // allocation function for each (compactable) space based on the location
+ // of the object before compaction.
+ typedef MaybeObject* (*AllocationFunction)(Heap* heap,
+ HeapObject* object,
+ int object_size);
+
+ // Type of functions to encode the forwarding address for an object.
+ // Given the object, its size, and the new (non-failure) object it will be
+ // forwarded to, encode the forwarding address. For paged spaces, the
+ // 'offset' input/output parameter contains the offset of the forwarded
+ // 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 (i.e., contiguous spaces), the offset parameter is ignored.
+ typedef void (*EncodingFunction)(Heap* heap,
+ HeapObject* old_object,
+ int object_size,
+ Object* new_object,
+ int* offset);
+
+ // Type of functions to process non-live objects.
+ typedef void (*ProcessNonLiveFunction)(HeapObject* object, Isolate* isolate);
+
+ // Pointer to member function, used in IterateLiveObjects.
+ typedef int (MarkCompactCollector::*LiveObjectCallback)(HeapObject* obj);
+
+ // Set the global flags, it must be called before Prepare to take effect.
+ inline void SetFlags(int flags);
+
+ static void Initialize();
+
+ void TearDown();
+
+ 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);
+
+ // Performs a global garbage collection.
+ void CollectGarbage();
+
+ enum CompactionMode {
+ INCREMENTAL_COMPACTION,
+ NON_INCREMENTAL_COMPACTION
+ };
+
+ bool StartCompaction(CompactionMode mode);
+
+ void AbortCompaction();
+
+ // During a full GC, there is a stack-allocated GCTracer that is used for
+ // bookkeeping information. Return a pointer to that tracer.
+ GCTracer* tracer() { return tracer_; }
+
+#ifdef DEBUG
+ // Checks whether performing mark-compact collection.
+ bool in_use() { return state_ > PREPARE_GC; }
+ bool are_map_pointers_encoded() { return state_ == UPDATE_POINTERS; }
+#endif
+
+ // Determine type of object and emit deletion log event.
+ static void ReportDeleteIfNeeded(HeapObject* obj, Isolate* isolate);
+
+ // 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_; }
+ inline Isolate* isolate() const;
+
+ 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,
+ PARALLEL_CONSERVATIVE,
+ CONCURRENT_CONSERVATIVE,
+ PRECISE
+ };
+
+ enum SweepingParallelism {
+ SWEEP_SEQUENTIALLY,
+ SWEEP_IN_PARALLEL
+ };
+
+#ifdef VERIFY_HEAP
+ void VerifyMarkbitsAreClean();
+ static void VerifyMarkbitsAreClean(PagedSpace* space);
+ static void VerifyMarkbitsAreClean(NewSpace* space);
+ void VerifyWeakEmbeddedMapsInOptimizedCode();
+ void VerifyOmittedMapChecks();
+#endif
+
+ // Sweep a single page from the given space conservatively.
+ // Return a number of reclaimed bytes.
+ template<SweepingParallelism type>
+ static intptr_t SweepConservatively(PagedSpace* space,
+ FreeList* free_list,
+ 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();
+ }
+
+ INLINE(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);
+ void RecordCodeTargetPatch(Address pc, 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_collections() {
+ return encountered_weak_collections_;
+ }
+ inline void set_encountered_weak_collections(Object* weak_collection) {
+ encountered_weak_collections_ = weak_collection;
+ }
+
+ void InvalidateCode(Code* code);
+
+ void ClearMarkbits();
+
+ bool abort_incremental_marking() const { return abort_incremental_marking_; }
+
+ bool is_compacting() const { return compacting_; }
+
+ MarkingParity marking_parity() { return marking_parity_; }
+
+ // Concurrent and parallel sweeping support.
+ void SweepInParallel(PagedSpace* space,
+ FreeList* private_free_list,
+ FreeList* free_list);
+
+ void WaitUntilSweepingCompleted();
+
+ intptr_t StealMemoryFromSweeperThreads(PagedSpace* space);
+
+ bool AreSweeperThreadsActivated();
+
+ bool IsConcurrentSweepingInProgress();
+
+ void set_sequential_sweeping(bool sequential_sweeping) {
+ sequential_sweeping_ = sequential_sweeping;
+ }
+
+ bool sequential_sweeping() const {
+ return sequential_sweeping_;
+ }
+
+ // Parallel marking support.
+ void MarkInParallel();
+
+ void WaitUntilMarkingCompleted();
+
+ private:
+ MarkCompactCollector();
+ ~MarkCompactCollector();
+
+ bool MarkInvalidatedCode();
+ bool WillBeDeoptimized(Code* code);
+ void RemoveDeadInvalidatedCode();
+ void ProcessInvalidatedCode(ObjectVisitor* visitor);
+
+ void UnlinkEvacuationCandidates();
+ void ReleaseEvacuationCandidates();
+
+ void StartSweeperThreads();
+
+#ifdef DEBUG
+ enum CollectorState {
+ IDLE,
+ PREPARE_GC,
+ MARK_LIVE_OBJECTS,
+ SWEEP_SPACES,
+ ENCODE_FORWARDING_ADDRESSES,
+ UPDATE_POINTERS,
+ RELOCATE_OBJECTS
+ };
+
+ // The current stage of the collector.
+ CollectorState state_;
+#endif
+
+ // Global flag that forces sweeping to be precise, so we can traverse the
+ // heap.
+ bool sweep_precisely_;
+
+ bool reduce_memory_footprint_;
+
+ bool abort_incremental_marking_;
+
+ MarkingParity marking_parity_;
+
+ // True if we are collecting slots to perform evacuation from evacuation
+ // candidates.
+ bool compacting_;
+
+ bool was_marked_incrementally_;
+
+ // True if concurrent or parallel sweeping is currently in progress.
+ bool sweeping_pending_;
+
+ bool sequential_sweeping_;
+
+ // 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();
+
+ // -----------------------------------------------------------------------
+ // Phase 1: Marking live objects.
+ //
+ // Before: The heap has been prepared for garbage collection by
+ // MarkCompactCollector::Prepare() and is otherwise in its
+ // normal state.
+ //
+ // After: Live objects are marked and non-live objects are unmarked.
+
+ friend class RootMarkingVisitor;
+ friend class MarkingVisitor;
+ friend class MarkCompactMarkingVisitor;
+ friend class CodeMarkingVisitor;
+ friend class SharedFunctionInfoMarkingVisitor;
+
+ // Mark code objects that are active on the stack to prevent them
+ // from being flushed.
+ void PrepareThreadForCodeFlushing(Isolate* isolate, ThreadLocalTop* top);
+
+ void PrepareForCodeFlushing();
+
+ // Marking operations for objects reachable from roots.
+ void MarkLiveObjects();
+
+ void AfterMarking();
+
+ // Marks the object black and pushes it on the marking stack.
+ // This is for non-incremental marking only.
+ INLINE(void MarkObject(HeapObject* obj, MarkBit mark_bit));
+
+ // Marks the object black assuming that it is not yet marked.
+ // This is for non-incremental marking only.
+ INLINE(void SetMark(HeapObject* obj, MarkBit mark_bit));
+
+ // Mark the heap roots and all objects reachable from them.
+ void MarkRoots(RootMarkingVisitor* visitor);
+
+ // Mark the string table specially. References to internalized strings from
+ // the string table are weak.
+ void MarkStringTable(RootMarkingVisitor* visitor);
+
+ // Mark objects in implicit references groups if their parent object
+ // is marked.
+ void MarkImplicitRefGroups();
+
+ // Mark objects reachable (transitively) from objects in the marking stack
+ // or overflowed in the heap.
+ void ProcessMarkingDeque();
+
+ // Mark objects reachable (transitively) from objects in the marking stack
+ // or overflowed in the heap. This respects references only considered in
+ // the final atomic marking pause including the following:
+ // - Processing of objects reachable through Harmony WeakMaps.
+ // - Objects reachable due to host application logic like object groups
+ // or implicit references' groups.
+ void ProcessEphemeralMarking(ObjectVisitor* visitor);
+
+ // If the call-site of the top optimized code was not prepared for
+ // deoptimization, then treat the maps in the code as strong pointers,
+ // otherwise a map can die and deoptimize the code.
+ void ProcessTopOptimizedFrame(ObjectVisitor* visitor);
+
+ // 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 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 RefillMarkingDeque();
+
+ // After reachable maps have been marked process per context object
+ // literal map caches removing unmarked entries.
+ void ProcessMapCaches();
+
+ // Callback function for telling whether the object *p is an unmarked
+ // heap object.
+ static bool IsUnmarkedHeapObject(Object** p);
+ static bool IsUnmarkedHeapObjectWithHeap(Heap* heap, Object** p);
+
+ // 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 ClearNonLiveReferences();
+ void ClearNonLivePrototypeTransitions(Map* map);
+ void ClearNonLiveMapTransitions(Map* map, MarkBit map_mark);
+
+ void ClearAndDeoptimizeDependentCode(Map* map);
+ void ClearNonLiveDependentCode(DependentCode* dependent_code);
+
+ // 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 collections
+ // encountered so far. This might push new object or even new weak maps onto
+ // the marking stack.
+ void ProcessWeakCollections();
+
+ // After all reachable objects have been marked those weak map entries
+ // with an unreachable key are removed from all encountered weak maps.
+ // The linked list of all encountered weak maps is destroyed.
+ void ClearWeakCollections();
+
+ // -----------------------------------------------------------------------
+ // Phase 2: Sweeping to clear mark bits and free non-live objects for
+ // a non-compacting collection.
+ //
+ // Before: Live objects are marked and non-live objects are unmarked.
+ //
+ // After: Live objects are unmarked, non-live regions have been added to
+ // their space's free list. Active eden semispace is compacted by
+ // evacuation.
+ //
+
+ // 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();
+
+ int DiscoverAndPromoteBlackObjectsOnPage(NewSpace* new_space,
+ NewSpacePage* p);
+
+ void EvacuateNewSpace();
+
+ void EvacuateLiveObjectsFromPage(Page* p);
+
+ void EvacuatePages();
+
+ void EvacuateNewSpaceAndCandidates();
+
+ void SweepSpace(PagedSpace* space, SweeperType sweeper);
+
+#ifdef DEBUG
+ friend class MarkObjectVisitor;
+ static void VisitObject(HeapObject* obj);
+
+ friend class UnmarkObjectVisitor;
+ static void UnmarkObject(HeapObject* obj);
+#endif
+
+ Heap* heap_;
+ MarkingDeque marking_deque_;
+ CodeFlusher* code_flusher_;
+ Object* encountered_weak_collections_;
+ Object* code_to_deoptimize_;
+
+ List<Page*> evacuation_candidates_;
+ List<Code*> invalidated_code_;
+
+ friend class Heap;
+};
+
+
+class MarkBitCellIterator BASE_EMBEDDED {
+ public:
+ explicit MarkBitCellIterator(MemoryChunk* chunk)
+ : chunk_(chunk) {
+ last_cell_index_ = Bitmap::IndexToCell(
+ Bitmap::CellAlignIndex(
+ chunk_->AddressToMarkbitIndex(chunk_->area_end())));
+ cell_base_ = chunk_->area_start();
+ cell_index_ = Bitmap::IndexToCell(
+ Bitmap::CellAlignIndex(
+ chunk_->AddressToMarkbitIndex(cell_base_)));
+ cells_ = chunk_->markbits()->cells();
+ }
+
+ inline bool Done() { return cell_index_ == last_cell_index_; }
+
+ inline bool HasNext() { return cell_index_ < last_cell_index_ - 1; }
+
+ inline MarkBit::CellType* CurrentCell() {
+ ASSERT(cell_index_ == Bitmap::IndexToCell(Bitmap::CellAlignIndex(
+ chunk_->AddressToMarkbitIndex(cell_base_))));
+ return &cells_[cell_index_];
+ }
+
+ inline Address CurrentCellBase() {
+ ASSERT(cell_index_ == Bitmap::IndexToCell(Bitmap::CellAlignIndex(
+ chunk_->AddressToMarkbitIndex(cell_base_))));
+ return cell_base_;
+ }
+
+ inline void Advance() {
+ cell_index_++;
+ cell_base_ += 32 * kPointerSize;
+ }
+
+ private:
+ MemoryChunk* chunk_;
+ MarkBit::CellType* cells_;
+ unsigned int last_cell_index_;
+ unsigned int cell_index_;
+ Address cell_base_;
+};
+
+
+class SequentialSweepingScope BASE_EMBEDDED {
+ public:
+ explicit SequentialSweepingScope(MarkCompactCollector *collector) :
+ collector_(collector) {
+ collector_->set_sequential_sweeping(true);
+ }
+
+ ~SequentialSweepingScope() {
+ collector_->set_sequential_sweeping(false);
+ }
+
+ private:
+ MarkCompactCollector* collector_;
+};
+
+
+const char* AllocationSpaceName(AllocationSpace space);
+
+} } // namespace v8::internal
+
+#endif // V8_MARK_COMPACT_H_
diff --git a/chromium/v8/src/marking-thread.cc b/chromium/v8/src/marking-thread.cc
new file mode 100644
index 00000000000..ac9f944fe7e
--- /dev/null
+++ b/chromium/v8/src/marking-thread.cc
@@ -0,0 +1,89 @@
+// Copyright 2013 the V8 project authors. 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 "marking-thread.h"
+
+#include "v8.h"
+
+#include "isolate.h"
+#include "v8threads.h"
+
+namespace v8 {
+namespace internal {
+
+MarkingThread::MarkingThread(Isolate* isolate)
+ : Thread("MarkingThread"),
+ isolate_(isolate),
+ heap_(isolate->heap()),
+ start_marking_semaphore_(OS::CreateSemaphore(0)),
+ end_marking_semaphore_(OS::CreateSemaphore(0)),
+ stop_semaphore_(OS::CreateSemaphore(0)) {
+ NoBarrier_Store(&stop_thread_, static_cast<AtomicWord>(false));
+ id_ = NoBarrier_AtomicIncrement(&id_counter_, 1);
+}
+
+
+Atomic32 MarkingThread::id_counter_ = -1;
+
+
+void MarkingThread::Run() {
+ Isolate::SetIsolateThreadLocals(isolate_, NULL);
+ DisallowHeapAllocation no_allocation;
+ DisallowHandleAllocation no_handles;
+ DisallowHandleDereference no_deref;
+
+ while (true) {
+ start_marking_semaphore_->Wait();
+
+ if (Acquire_Load(&stop_thread_)) {
+ stop_semaphore_->Signal();
+ return;
+ }
+
+ end_marking_semaphore_->Signal();
+ }
+}
+
+
+void MarkingThread::Stop() {
+ Release_Store(&stop_thread_, static_cast<AtomicWord>(true));
+ start_marking_semaphore_->Signal();
+ stop_semaphore_->Wait();
+ Join();
+}
+
+
+void MarkingThread::StartMarking() {
+ start_marking_semaphore_->Signal();
+}
+
+
+void MarkingThread::WaitForMarkingThread() {
+ end_marking_semaphore_->Wait();
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/marking-thread.h b/chromium/v8/src/marking-thread.h
new file mode 100644
index 00000000000..9efa3af1326
--- /dev/null
+++ b/chromium/v8/src/marking-thread.h
@@ -0,0 +1,71 @@
+// Copyright 2013 the V8 project authors. 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_MARKING_THREAD_H_
+#define V8_MARKING_THREAD_H_
+
+#include "atomicops.h"
+#include "flags.h"
+#include "platform.h"
+#include "v8utils.h"
+
+#include "spaces.h"
+
+#include "heap.h"
+
+namespace v8 {
+namespace internal {
+
+class MarkingThread : public Thread {
+ public:
+ explicit MarkingThread(Isolate* isolate);
+
+ void Run();
+ void Stop();
+ void StartMarking();
+ void WaitForMarkingThread();
+
+ ~MarkingThread() {
+ delete start_marking_semaphore_;
+ delete end_marking_semaphore_;
+ delete stop_semaphore_;
+ }
+
+ private:
+ Isolate* isolate_;
+ Heap* heap_;
+ Semaphore* start_marking_semaphore_;
+ Semaphore* end_marking_semaphore_;
+ Semaphore* stop_semaphore_;
+ volatile AtomicWord stop_thread_;
+ int id_;
+ static Atomic32 id_counter_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_MARKING_THREAD_H_
diff --git a/chromium/v8/src/math.js b/chromium/v8/src/math.js
new file mode 100644
index 00000000000..9ba1934b851
--- /dev/null
+++ b/chromium/v8/src/math.js
@@ -0,0 +1,298 @@
+// Copyright 2012 the V8 project authors. 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 declarations have been made
+// in runtime.js:
+// var $Object = global.Object;
+
+// Keep reference to original values of some global properties. This
+// has the added benefit that the code in this file is isolated from
+// changes to these properties.
+var $floor = MathFloor;
+var $abs = MathAbs;
+
+// Instance class name can only be set on functions. That is the only
+// purpose for MathConstructor.
+function MathConstructor() {}
+var $Math = new MathConstructor();
+
+// -------------------------------------------------------------------
+
+// ECMA 262 - 15.8.2.1
+function MathAbs(x) {
+ if (%_IsSmi(x)) return x >= 0 ? x : -x;
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ if (x === 0) return 0; // To handle -0.
+ return x > 0 ? x : -x;
+}
+
+// ECMA 262 - 15.8.2.2
+function MathAcos(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %Math_acos(x);
+}
+
+// ECMA 262 - 15.8.2.3
+function MathAsin(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %Math_asin(x);
+}
+
+// ECMA 262 - 15.8.2.4
+function MathAtan(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %Math_atan(x);
+}
+
+// ECMA 262 - 15.8.2.5
+// The naming of y and x matches the spec, as does the order in which
+// ToNumber (valueOf) is called.
+function MathAtan2(y, x) {
+ if (!IS_NUMBER(y)) y = NonNumberToNumber(y);
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %Math_atan2(y, x);
+}
+
+// ECMA 262 - 15.8.2.6
+function MathCeil(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %Math_ceil(x);
+}
+
+// ECMA 262 - 15.8.2.7
+function MathCos(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %_MathCos(x);
+}
+
+// ECMA 262 - 15.8.2.8
+function MathExp(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %Math_exp(x);
+}
+
+// ECMA 262 - 15.8.2.9
+function MathFloor(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ // It's more common to call this with a positive number that's out
+ // of range than negative numbers; check the upper bound first.
+ if (x < 0x80000000 && x > 0) {
+ // Numbers in the range [0, 2^31) can be floored by converting
+ // them to an unsigned 32-bit value using the shift operator.
+ // We avoid doing so for -0, because the result of Math.floor(-0)
+ // has to be -0, which wouldn't be the case with the shift.
+ return TO_UINT32(x);
+ } else {
+ return %Math_floor(x);
+ }
+}
+
+// ECMA 262 - 15.8.2.10
+function MathLog(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %_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.
+ }
+ var r = -1/0; // Compiler constant-folds this to -Infinity.
+ for (var i = 0; i < length; i++) {
+ var n = %_Arguments(i);
+ if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
+ // Make sure +0 is considered greater than -0. -0 is never a Smi, +0 can be
+ // a Smi or heap number.
+ if (NUMBER_IS_NAN(n) || n > r ||
+ (r == 0 && n == 0 && !%_IsSmi(r) && 1 / r < 0)) {
+ r = n;
+ }
+ }
+ return r;
+}
+
+// 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.
+ }
+ var r = 1/0; // Compiler constant-folds this to Infinity.
+ for (var i = 0; i < length; i++) {
+ var n = %_Arguments(i);
+ if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
+ // Make sure -0 is considered less than +0. -0 is never a Smi, +0 can be a
+ // Smi or a heap number.
+ if (NUMBER_IS_NAN(n) || n < r ||
+ (r == 0 && n == 0 && !%_IsSmi(n) && 1 / n < 0)) {
+ r = n;
+ }
+ }
+ return r;
+}
+
+// ECMA 262 - 15.8.2.13
+function MathPow(x, y) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ if (!IS_NUMBER(y)) y = NonNumberToNumber(y);
+ return %_MathPow(x, y);
+}
+
+// ECMA 262 - 15.8.2.14
+function MathRandom() {
+ return %_RandomHeapNumber();
+}
+
+// ECMA 262 - 15.8.2.15
+function MathRound(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %RoundNumber(x);
+}
+
+// ECMA 262 - 15.8.2.16
+function MathSin(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %_MathSin(x);
+}
+
+// ECMA 262 - 15.8.2.17
+function MathSqrt(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %_MathSqrt(x);
+}
+
+// ECMA 262 - 15.8.2.18
+function MathTan(x) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ return %_MathTan(x);
+}
+
+// Non-standard extension.
+function MathImul(x, y) {
+ if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+ if (!IS_NUMBER(y)) y = NonNumberToNumber(y);
+ return %NumberImul(x, y);
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpMath() {
+ %CheckIsBootstrapping();
+
+ %SetPrototype($Math, $Object.prototype);
+ %SetProperty(global, "Math", $Math, DONT_ENUM);
+ %FunctionSetInstanceClassName(MathConstructor, 'Math');
+
+ // Set up math constants.
+ // ECMA-262, section 15.8.1.1.
+ %OptimizeObjectForAddingMultipleProperties($Math, 8);
+ %SetProperty($Math,
+ "E",
+ 2.7182818284590452354,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ // ECMA-262, section 15.8.1.2.
+ %SetProperty($Math,
+ "LN10",
+ 2.302585092994046,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ // ECMA-262, section 15.8.1.3.
+ %SetProperty($Math,
+ "LN2",
+ 0.6931471805599453,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ // ECMA-262, section 15.8.1.4.
+ %SetProperty($Math,
+ "LOG2E",
+ 1.4426950408889634,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetProperty($Math,
+ "LOG10E",
+ 0.4342944819032518,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetProperty($Math,
+ "PI",
+ 3.1415926535897932,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetProperty($Math,
+ "SQRT1_2",
+ 0.7071067811865476,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetProperty($Math,
+ "SQRT2",
+ 1.4142135623730951,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %ToFastProperties($Math);
+
+ // Set up non-enumerable functions of the Math object and
+ // set their names.
+ InstallFunctions($Math, DONT_ENUM, $Array(
+ "random", MathRandom,
+ "abs", MathAbs,
+ "acos", MathAcos,
+ "asin", MathAsin,
+ "atan", MathAtan,
+ "ceil", MathCeil,
+ "cos", MathCos,
+ "exp", MathExp,
+ "floor", MathFloor,
+ "log", MathLog,
+ "round", MathRound,
+ "sin", MathSin,
+ "sqrt", MathSqrt,
+ "tan", MathTan,
+ "atan2", MathAtan2,
+ "pow", MathPow,
+ "max", MathMax,
+ "min", MathMin,
+ "imul", MathImul
+ ));
+}
+
+SetUpMath();
diff --git a/chromium/v8/src/messages.cc b/chromium/v8/src/messages.cc
new file mode 100644
index 00000000000..9eae67a7280
--- /dev/null
+++ b/chromium/v8/src/messages.cc
@@ -0,0 +1,202 @@
+// 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 "api.h"
+#include "execution.h"
+#include "messages.h"
+#include "spaces-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+// If no message listeners have been registered this one is called
+// by default.
+void MessageHandler::DefaultMessageReport(Isolate* isolate,
+ const MessageLocation* loc,
+ Handle<Object> message_obj) {
+ SmartArrayPointer<char> str = GetLocalizedMessage(isolate, message_obj);
+ if (loc == NULL) {
+ PrintF("%s\n", *str);
+ } else {
+ HandleScope scope(isolate);
+ Handle<Object> data(loc->script()->name(), isolate);
+ SmartArrayPointer<char> data_str;
+ if (data->IsString())
+ data_str = Handle<String>::cast(data)->ToCString(DISALLOW_NULLS);
+ PrintF("%s:%i: %s\n", *data_str ? *data_str : "<unknown>",
+ loc->start_pos(), *str);
+ }
+}
+
+
+Handle<JSMessageObject> MessageHandler::MakeMessageObject(
+ Isolate* isolate,
+ const char* type,
+ MessageLocation* loc,
+ Vector< Handle<Object> > args,
+ Handle<String> stack_trace,
+ Handle<JSArray> stack_frames) {
+ Factory* factory = isolate->factory();
+ Handle<String> type_handle = factory->InternalizeUtf8String(type);
+ Handle<FixedArray> arguments_elements =
+ factory->NewFixedArray(args.length());
+ for (int i = 0; i < args.length(); i++) {
+ arguments_elements->set(i, *args[i]);
+ }
+ Handle<JSArray> arguments_handle =
+ factory->NewJSArrayWithElements(arguments_elements);
+
+ int start = 0;
+ int end = 0;
+ Handle<Object> script_handle = factory->undefined_value();
+ if (loc) {
+ start = loc->start_pos();
+ end = loc->end_pos();
+ script_handle = GetScriptWrapper(loc->script());
+ }
+
+ Handle<Object> stack_trace_handle = stack_trace.is_null()
+ ? Handle<Object>::cast(factory->undefined_value())
+ : Handle<Object>::cast(stack_trace);
+
+ Handle<Object> stack_frames_handle = stack_frames.is_null()
+ ? Handle<Object>::cast(factory->undefined_value())
+ : Handle<Object>::cast(stack_frames);
+
+ Handle<JSMessageObject> message =
+ factory->NewJSMessageObject(type_handle,
+ arguments_handle,
+ start,
+ end,
+ script_handle,
+ stack_trace_handle,
+ stack_frames_handle);
+
+ return message;
+}
+
+
+void MessageHandler::ReportMessage(Isolate* isolate,
+ MessageLocation* loc,
+ Handle<Object> message) {
+ // We are calling into embedder's code which can throw exceptions.
+ // Thus we need to save current exception state, reset it to the clean one
+ // and ignore scheduled exceptions callbacks can throw.
+
+ // We pass the exception object into the message handler callback though.
+ Object* exception_object = isolate->heap()->undefined_value();
+ if (isolate->has_pending_exception()) {
+ isolate->pending_exception()->ToObject(&exception_object);
+ }
+ Handle<Object> exception_handle(exception_object, isolate);
+
+ Isolate::ExceptionScope exception_scope(isolate);
+ isolate->clear_pending_exception();
+ isolate->set_external_caught_exception(false);
+
+ v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
+ v8::Local<v8::Value> api_exception_obj = v8::Utils::ToLocal(exception_handle);
+
+ v8::NeanderArray global_listeners(isolate->factory()->message_listeners());
+ int global_length = global_listeners.length();
+ if (global_length == 0) {
+ DefaultMessageReport(isolate, loc, message);
+ if (isolate->has_scheduled_exception()) {
+ isolate->clear_scheduled_exception();
+ }
+ } else {
+ for (int i = 0; i < global_length; i++) {
+ HandleScope scope(isolate);
+ if (global_listeners.get(i)->IsUndefined()) continue;
+ 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->foreign_address());
+ Handle<Object> callback_data(listener.get(1), isolate);
+ {
+ // Do not allow exceptions to propagate.
+ v8::TryCatch try_catch;
+ callback(api_message_obj, callback_data->IsUndefined()
+ ? api_exception_obj
+ : v8::Utils::ToLocal(callback_data));
+ }
+ if (isolate->has_scheduled_exception()) {
+ isolate->clear_scheduled_exception();
+ }
+ }
+ }
+}
+
+
+Handle<String> MessageHandler::GetMessage(Isolate* isolate,
+ Handle<Object> data) {
+ Factory* factory = isolate->factory();
+ Handle<String> fmt_str =
+ factory->InternalizeOneByteString(STATIC_ASCII_VECTOR("FormatMessage"));
+ Handle<JSFunction> fun =
+ Handle<JSFunction>(
+ JSFunction::cast(
+ isolate->js_builtins_object()->
+ GetPropertyNoExceptionThrown(*fmt_str)));
+ Handle<JSMessageObject> message = Handle<JSMessageObject>::cast(data);
+ Handle<Object> argv[] = { Handle<Object>(message->type(), isolate),
+ Handle<Object>(message->arguments(), isolate) };
+
+ bool caught_exception;
+ Handle<Object> result =
+ Execution::TryCall(fun,
+ isolate->js_builtins_object(),
+ ARRAY_SIZE(argv),
+ argv,
+ &caught_exception);
+
+ if (caught_exception || !result->IsString()) {
+ return factory->InternalizeOneByteString(STATIC_ASCII_VECTOR("<error>"));
+ }
+ Handle<String> result_string = Handle<String>::cast(result);
+ // A string that has been obtained from JS code in this way is
+ // likely to be a complicated ConsString of some sort. We flatten it
+ // here to improve the efficiency of converting it to a C string and
+ // other operations that are likely to take place (see GetLocalizedMessage
+ // for example).
+ FlattenString(result_string);
+ return result_string;
+}
+
+
+SmartArrayPointer<char> MessageHandler::GetLocalizedMessage(
+ Isolate* isolate,
+ Handle<Object> data) {
+ HandleScope scope(isolate);
+ return GetMessage(isolate, data)->ToCString(DISALLOW_NULLS);
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/messages.h b/chromium/v8/src/messages.h
new file mode 100644
index 00000000000..5d84e46caa9
--- /dev/null
+++ b/chromium/v8/src/messages.h
@@ -0,0 +1,116 @@
+// 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.
+
+// The infrastructure used for (localized) message reporting in V8.
+//
+// Note: there's a big unresolved issue about ownership of the data
+// structures used by this framework.
+
+#ifndef V8_MESSAGES_H_
+#define V8_MESSAGES_H_
+
+#include "handles-inl.h"
+
+// Forward declaration of MessageLocation.
+namespace v8 {
+namespace internal {
+class MessageLocation;
+} } // namespace v8::internal
+
+
+class V8Message {
+ public:
+ V8Message(char* type,
+ v8::internal::Handle<v8::internal::JSArray> args,
+ const v8::internal::MessageLocation* loc) :
+ type_(type), args_(args), loc_(loc) { }
+ char* type() const { return type_; }
+ v8::internal::Handle<v8::internal::JSArray> args() const { return args_; }
+ const v8::internal::MessageLocation* loc() const { return loc_; }
+ private:
+ char* type_;
+ v8::internal::Handle<v8::internal::JSArray> const args_;
+ const v8::internal::MessageLocation* loc_;
+};
+
+
+namespace v8 {
+namespace internal {
+
+struct Language;
+class SourceInfo;
+
+class MessageLocation {
+ public:
+ MessageLocation(Handle<Script> script,
+ int start_pos,
+ int end_pos)
+ : script_(script),
+ start_pos_(start_pos),
+ end_pos_(end_pos) { }
+ MessageLocation() : start_pos_(-1), end_pos_(-1) { }
+
+ Handle<Script> script() const { return script_; }
+ int start_pos() const { return start_pos_; }
+ int end_pos() const { return end_pos_; }
+
+ private:
+ Handle<Script> script_;
+ int start_pos_;
+ int end_pos_;
+};
+
+
+// A message handler is a convenience interface for accessing the list
+// of message listeners registered in an environment
+class MessageHandler {
+ public:
+ // Returns a message object for the API to use.
+ static Handle<JSMessageObject> MakeMessageObject(
+ Isolate* isolate,
+ const char* type,
+ MessageLocation* loc,
+ Vector< Handle<Object> > args,
+ Handle<String> stack_trace,
+ Handle<JSArray> stack_frames);
+
+ // Report a formatted message (needs JS allocation).
+ static void ReportMessage(Isolate* isolate,
+ MessageLocation* loc,
+ Handle<Object> message);
+
+ static void DefaultMessageReport(Isolate* isolate,
+ const MessageLocation* loc,
+ Handle<Object> message_obj);
+ static Handle<String> GetMessage(Isolate* isolate, Handle<Object> data);
+ static SmartArrayPointer<char> GetLocalizedMessage(Isolate* isolate,
+ Handle<Object> data);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_MESSAGES_H_
diff --git a/chromium/v8/src/messages.js b/chromium/v8/src/messages.js
new file mode 100644
index 00000000000..2debbf86540
--- /dev/null
+++ b/chromium/v8/src/messages.js
@@ -0,0 +1,1350 @@
+// Copyright 2012 the V8 project authors. 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 kMessages = {
+ // Error
+ cyclic_proto: ["Cyclic __proto__ value"],
+ code_gen_from_strings: ["%0"],
+ generator_running: ["Generator is already running"],
+ generator_finished: ["Generator has already finished"],
+ // TypeError
+ unexpected_token: ["Unexpected token ", "%0"],
+ unexpected_token_number: ["Unexpected number"],
+ unexpected_token_string: ["Unexpected string"],
+ unexpected_token_identifier: ["Unexpected identifier"],
+ unexpected_reserved: ["Unexpected reserved word"],
+ unexpected_strict_reserved: ["Unexpected strict mode reserved word"],
+ unexpected_eos: ["Unexpected end of input"],
+ malformed_regexp: ["Invalid regular expression: /", "%0", "/: ", "%1"],
+ unterminated_regexp: ["Invalid regular expression: missing /"],
+ regexp_flags: ["Cannot supply flags when constructing one RegExp from another"],
+ incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"],
+ invalid_lhs_in_assignment: ["Invalid left-hand side in assignment"],
+ invalid_lhs_in_for_in: ["Invalid left-hand side in for-in"],
+ invalid_lhs_in_postfix_op: ["Invalid left-hand side expression in postfix operation"],
+ invalid_lhs_in_prefix_op: ["Invalid left-hand side expression in prefix operation"],
+ multiple_defaults_in_switch: ["More than one default clause in switch statement"],
+ newline_after_throw: ["Illegal newline after throw"],
+ redeclaration: ["%0", " '", "%1", "' has already been declared"],
+ no_catch_or_finally: ["Missing catch or finally after try"],
+ unknown_label: ["Undefined label '", "%0", "'"],
+ uncaught_exception: ["Uncaught ", "%0"],
+ stack_trace: ["Stack Trace:\n", "%0"],
+ called_non_callable: ["%0", " is not a function"],
+ undefined_method: ["Object ", "%1", " has no method '", "%0", "'"],
+ property_not_function: ["Property '", "%0", "' of object ", "%1", " is not a function"],
+ cannot_convert_to_primitive: ["Cannot convert object to primitive value"],
+ not_constructor: ["%0", " is not a constructor"],
+ not_defined: ["%0", " is not defined"],
+ non_object_property_load: ["Cannot read property '", "%0", "' of ", "%1"],
+ non_object_property_store: ["Cannot set property '", "%0", "' of ", "%1"],
+ non_object_property_call: ["Cannot call method '", "%0", "' of ", "%1"],
+ with_expression: ["%0", " has no properties"],
+ illegal_invocation: ["Illegal invocation"],
+ no_setter_in_callback: ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"],
+ apply_non_function: ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"],
+ apply_wrong_args: ["Function.prototype.apply: Arguments list has wrong type"],
+ invalid_in_operator_use: ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"],
+ instanceof_function_expected: ["Expecting a function in instanceof check, but got ", "%0"],
+ instanceof_nonobject_proto: ["Function has non-object prototype '", "%0", "' in instanceof check"],
+ undefined_or_null_to_object: ["Cannot convert undefined or null to object"],
+ reduce_no_initial: ["Reduce of empty array with no initial value"],
+ getter_must_be_callable: ["Getter must be a function: ", "%0"],
+ setter_must_be_callable: ["Setter must be a function: ", "%0"],
+ value_and_accessor: ["Invalid property. A property cannot both have accessors and be writable or have a value, ", "%0"],
+ proto_object_or_null: ["Object prototype may only be an Object or null"],
+ property_desc_object: ["Property description must be an object: ", "%0"],
+ redefine_disallowed: ["Cannot redefine property: ", "%0"],
+ 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"],
+ 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 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"],
+ invalid_weakset_value: ["Invalid value used in weak set"],
+ not_date_object: ["this is not a Date object."],
+ observe_non_object: ["Object.", "%0", " cannot ", "%0", " non-object"],
+ observe_non_function: ["Object.", "%0", " cannot deliver to non-function"],
+ observe_callback_frozen: ["Object.observe cannot deliver to a frozen function object"],
+ observe_invalid_accept: ["Object.observe accept must be an array of strings."],
+ observe_type_non_string: ["Invalid changeRecord with non-string 'type' property"],
+ observe_perform_non_string: ["Invalid non-string changeType"],
+ observe_perform_non_function: ["Cannot perform non-function"],
+ observe_notify_non_notifier: ["notify called on non-notifier object"],
+ proto_poison_pill: ["Generic use of __proto__ accessor not allowed"],
+ not_typed_array: ["this is not a typed array."],
+ invalid_argument: ["invalid_argument"],
+ data_view_not_array_buffer: ["First argument to DataView constructor must be an ArrayBuffer"],
+ constructor_not_function: ["Constructor ", "%0", " requires 'new'"],
+ // RangeError
+ invalid_array_length: ["Invalid array length"],
+ invalid_array_buffer_length: ["Invalid array buffer length"],
+ invalid_typed_array_offset: ["Start offset is too large:"],
+ invalid_typed_array_length: ["Invalid typed array length"],
+ invalid_typed_array_alignment: ["%0", "of", "%1", "should be a multiple of", "%3"],
+ typed_array_set_source_too_large:
+ ["Source is too large"],
+ typed_array_set_negative_offset:
+ ["Start offset is negative"],
+ invalid_data_view_offset: ["Start offset is outside the bounds of the buffer"],
+ invalid_data_view_length: ["Invalid data view length"],
+ invalid_data_view_accessor_offset:
+ ["Offset is outside the bounds of the DataView"],
+
+ stack_overflow: ["Maximum call stack size exceeded"],
+ invalid_time_value: ["Invalid time value"],
+ invalid_count_value: ["Invalid count value"],
+ // SyntaxError
+ paren_in_arg_string: ["Function arg string contains parenthesis"],
+ not_isvar: ["builtin %IS_VAR: not a variable"],
+ single_function_literal: ["Single function literal required"],
+ invalid_regexp_flags: ["Invalid flags supplied to RegExp constructor '", "%0", "'"],
+ invalid_regexp: ["Invalid RegExp pattern /", "%0", "/"],
+ 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"],
+ circular_structure: ["Converting circular structure to JSON"],
+ called_on_non_object: ["%0", " called on non-object"],
+ called_on_null_or_undefined: ["%0", " called on null or undefined"],
+ array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"],
+ object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"],
+ illegal_access: ["Illegal access"],
+ invalid_preparser_data: ["Invalid preparser data for function ", "%0"],
+ strict_mode_with: ["Strict mode code may not include a with statement"],
+ strict_catch_variable: ["Catch variable may not be eval or arguments in strict mode"],
+ too_many_arguments: ["Too many arguments in function call (only 32766 allowed)"],
+ too_many_parameters: ["Too many parameters in function definition (only 32766 allowed)"],
+ too_many_variables: ["Too many variables declared (only 131071 allowed)"],
+ strict_param_name: ["Parameter name eval or arguments is not allowed in strict mode"],
+ strict_param_dupe: ["Strict mode function may not have duplicate parameter names"],
+ strict_var_name: ["Variable name may not be eval or arguments in strict mode"],
+ strict_function_name: ["Function name may not be eval or arguments in strict mode"],
+ strict_octal_literal: ["Octal literals are not allowed in strict mode."],
+ strict_duplicate_property: ["Duplicate data property in object literal not allowed in strict mode"],
+ accessor_data_property: ["Object literal may not have data and accessor property with the same name"],
+ accessor_get_set: ["Object literal may not have multiple get/set accessors with the same name"],
+ strict_lhs_assignment: ["Assignment to eval or arguments is not allowed in strict mode"],
+ strict_lhs_postfix: ["Postfix increment/decrement may not have eval or arguments operand in strict mode"],
+ strict_lhs_prefix: ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
+ strict_reserved_word: ["Use of future reserved word in strict mode"],
+ strict_delete: ["Delete of an unqualified identifier in strict mode."],
+ strict_delete_property: ["Cannot delete property '", "%0", "' of ", "%1"],
+ strict_const: ["Use of const in strict mode."],
+ strict_function: ["In strict mode code, functions can only be declared at top level or immediately within another function." ],
+ strict_read_only_property: ["Cannot assign to read only property '", "%0", "' of ", "%1"],
+ strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"],
+ 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."],
+ symbol_to_string: ["Conversion from symbol to string"],
+ invalid_module_path: ["Module does not export '", "%0", "', or export is not itself a module"],
+ module_type_error: ["Module '", "%0", "' used improperly"],
+ module_export_undefined: ["Export '", "%0", "' is not defined in module"]
+};
+
+
+function FormatString(format, args) {
+ var result = "";
+ var arg_num = 0;
+ for (var i = 0; i < format.length; i++) {
+ var str = format[i];
+ if (str.length == 2 && %_StringCharCodeAt(str, 0) == 0x25) {
+ // Two-char string starts with "%".
+ var arg_num = (%_StringCharCodeAt(str, 1) - 0x30) >>> 0;
+ if (arg_num < 4) {
+ // str is one of %0, %1, %2 or %3.
+ try {
+ str = NoSideEffectToString(args[arg_num]);
+ } catch (e) {
+ if (%IsJSModule(args[arg_num]))
+ str = "module";
+ else if (IS_SPEC_OBJECT(args[arg_num]))
+ str = "object";
+ else
+ str = "#<error>";
+ }
+ }
+ }
+ result += str;
+ }
+ return result;
+}
+
+
+function NoSideEffectToString(obj) {
+ if (IS_STRING(obj)) return obj;
+ if (IS_NUMBER(obj)) return %_NumberToString(obj);
+ if (IS_BOOLEAN(obj)) return x ? 'true' : 'false';
+ if (IS_UNDEFINED(obj)) return 'undefined';
+ if (IS_NULL(obj)) return 'null';
+ if (IS_FUNCTION(obj)) return %_CallFunction(obj, FunctionToString);
+ if (IS_OBJECT(obj) && %GetDataProperty(obj, "toString") === ObjectToString) {
+ var constructor = %GetDataProperty(obj, "constructor");
+ if (typeof constructor == "function") {
+ var constructorName = constructor.name;
+ if (IS_STRING(constructorName) && constructorName !== "") {
+ return "#<" + constructorName + ">";
+ }
+ }
+ }
+ if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
+ return %_CallFunction(obj, ErrorToString);
+ }
+ return %_CallFunction(obj, ObjectToString);
+}
+
+// To determine whether we can safely stringify an object using ErrorToString
+// without the risk of side-effects, we need to check whether the object is
+// either an instance of a native error type (via '%_ClassOf'), or has $Error
+// in its prototype chain and hasn't overwritten 'toString' with something
+// strange and unusual.
+function CanBeSafelyTreatedAsAnErrorObject(obj) {
+ switch (%_ClassOf(obj)) {
+ case 'Error':
+ case 'EvalError':
+ case 'RangeError':
+ case 'ReferenceError':
+ case 'SyntaxError':
+ case 'TypeError':
+ case 'URIError':
+ return true;
+ }
+
+ var objToString = %GetDataProperty(obj, "toString");
+ return obj instanceof $Error && objToString === ErrorToString;
+}
+
+
+// When formatting internally created error messages, do not
+// invoke overwritten error toString methods but explicitly use
+// the error to string method. This is to avoid leaking error
+// objects between script tags in a browser setting.
+function ToStringCheckErrorObject(obj) {
+ if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
+ return %_CallFunction(obj, ErrorToString);
+ } else {
+ return ToString(obj);
+ }
+}
+
+
+function ToDetailString(obj) {
+ if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) {
+ var constructor = obj.constructor;
+ if (typeof constructor == "function") {
+ var constructorName = constructor.name;
+ if (IS_STRING(constructorName) && constructorName !== "") {
+ return "#<" + constructorName + ">";
+ }
+ }
+ }
+ return ToStringCheckErrorObject(obj);
+}
+
+
+function MakeGenericError(constructor, type, args) {
+ if (IS_UNDEFINED(args)) args = [];
+ return new constructor(FormatMessage(type, args));
+}
+
+
+/**
+ * Set up the Script function and constructor.
+ */
+%FunctionSetInstanceClassName(Script, 'Script');
+%SetProperty(Script.prototype, 'constructor', Script,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+%SetCode(Script, function(x) {
+ // Script objects can only be created by the VM.
+ throw new $Error("Not supported");
+});
+
+
+// Helper functions; called from the runtime system.
+function FormatMessage(type, args) {
+ var format = kMessages[type];
+ if (!format) return "<unknown message " + type + ">";
+ return FormatString(format, args);
+}
+
+
+function GetLineNumber(message) {
+ var start_position = %MessageGetStartPosition(message);
+ if (start_position == -1) return kNoLineNumberInfo;
+ var script = %MessageGetScript(message);
+ var location = script.locationFromPosition(start_position, true);
+ if (location == null) return kNoLineNumberInfo;
+ return location.line + 1;
+}
+
+
+// Returns the source code line containing the given source
+// position, or the empty string if the position is invalid.
+function GetSourceLine(message) {
+ var script = %MessageGetScript(message);
+ var start_position = %MessageGetStartPosition(message);
+ var location = script.locationFromPosition(start_position, true);
+ if (location == null) return "";
+ location.restrict();
+ return location.sourceText();
+}
+
+
+function MakeTypeError(type, args) {
+ return MakeGenericError($TypeError, type, args);
+}
+
+
+function MakeRangeError(type, args) {
+ return MakeGenericError($RangeError, type, args);
+}
+
+
+function MakeSyntaxError(type, args) {
+ return MakeGenericError($SyntaxError, type, args);
+}
+
+
+function MakeReferenceError(type, args) {
+ return MakeGenericError($ReferenceError, type, args);
+}
+
+
+function MakeEvalError(type, args) {
+ return MakeGenericError($EvalError, type, args);
+}
+
+
+function MakeError(type, args) {
+ return MakeGenericError($Error, type, args);
+}
+
+/**
+ * Find a line number given a specific source position.
+ * @param {number} position The source position.
+ * @return {number} 0 if input too small, -1 if input too large,
+ else the line number.
+ */
+function ScriptLineFromPosition(position) {
+ var lower = 0;
+ var upper = this.lineCount() - 1;
+ var line_ends = this.line_ends;
+
+ // We'll never find invalid positions so bail right away.
+ if (position > line_ends[upper]) {
+ return -1;
+ }
+
+ // This means we don't have to safe-guard indexing line_ends[i - 1].
+ if (position <= line_ends[0]) {
+ return 0;
+ }
+
+ // Binary search to find line # from position range.
+ while (upper >= 1) {
+ var i = (lower + upper) >> 1;
+
+ if (position > line_ends[i]) {
+ lower = i + 1;
+ } else if (position <= line_ends[i - 1]) {
+ upper = i - 1;
+ } else {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Get information on a specific source position.
+ * @param {number} position The source position
+ * @param {boolean} include_resource_offset Set to true to have the resource
+ * offset added to the location
+ * @return {SourceLocation}
+ * If line is negative or not in the source null is returned.
+ */
+function ScriptLocationFromPosition(position,
+ include_resource_offset) {
+ var line = this.lineFromPosition(position);
+ if (line == -1) return null;
+
+ // Determine start, end and column.
+ var line_ends = this.line_ends;
+ var start = line == 0 ? 0 : line_ends[line - 1] + 1;
+ var end = line_ends[line];
+ if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') {
+ end--;
+ }
+ var column = position - start;
+
+ // Adjust according to the offset within the resource.
+ if (include_resource_offset) {
+ line += this.line_offset;
+ if (line == this.line_offset) {
+ column += this.column_offset;
+ }
+ }
+
+ return new SourceLocation(this, position, line, column, start, end);
+}
+
+
+/**
+ * Get information on a specific source line and column possibly offset by a
+ * fixed source position. This function is used to find a source position from
+ * a line and column position. The fixed source position offset is typically
+ * used to find a source position in a function based on a line and column in
+ * the source for the function alone. The offset passed will then be the
+ * start position of the source for the function within the full script source.
+ * @param {number} opt_line The line within the source. Default value is 0
+ * @param {number} opt_column The column in within the line. Default value is 0
+ * @param {number} opt_offset_position The offset from the begining of the
+ * source from where the line and column calculation starts.
+ * Default value is 0
+ * @return {SourceLocation}
+ * If line is negative or not in the source null is returned.
+ */
+function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) {
+ // Default is the first line in the script. Lines in the script is relative
+ // to the offset within the resource.
+ var line = 0;
+ if (!IS_UNDEFINED(opt_line)) {
+ line = opt_line - this.line_offset;
+ }
+
+ // Default is first column. If on the first line add the offset within the
+ // resource.
+ var column = opt_column || 0;
+ if (line == 0) {
+ column -= this.column_offset;
+ }
+
+ var offset_position = opt_offset_position || 0;
+ if (line < 0 || column < 0 || offset_position < 0) return null;
+ if (line == 0) {
+ return this.locationFromPosition(offset_position + column, false);
+ } else {
+ // Find the line where the offset position is located.
+ var offset_line = this.lineFromPosition(offset_position);
+
+ if (offset_line == -1 || offset_line + line >= this.lineCount()) {
+ return null;
+ }
+
+ return this.locationFromPosition(
+ this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here.
+ }
+}
+
+
+/**
+ * Get a slice of source code from the script. The boundaries for the slice is
+ * specified in lines.
+ * @param {number} opt_from_line The first line (zero bound) in the slice.
+ * Default is 0
+ * @param {number} opt_to_column The last line (zero bound) in the slice (non
+ * inclusive). Default is the number of lines in the script
+ * @return {SourceSlice} The source slice or null of the parameters where
+ * 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;
+
+ // Adjust according to the offset within the resource.
+ from_line -= this.line_offset;
+ to_line -= this.line_offset;
+ if (from_line < 0) from_line = 0;
+ if (to_line > this.lineCount()) to_line = this.lineCount();
+
+ // Check parameters.
+ if (from_line >= this.lineCount() ||
+ to_line < 0 ||
+ from_line > to_line) {
+ return null;
+ }
+
+ var line_ends = this.line_ends;
+ var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
+ 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);
+}
+
+
+function ScriptSourceLine(opt_line) {
+ // Default is the first line in the script. Lines in the script are relative
+ // to the offset within the resource.
+ var line = 0;
+ if (!IS_UNDEFINED(opt_line)) {
+ line = opt_line - this.line_offset;
+ }
+
+ // Check parameter.
+ if (line < 0 || this.lineCount() <= line) {
+ return null;
+ }
+
+ // Return the source line.
+ var line_ends = this.line_ends;
+ var start = line == 0 ? 0 : line_ends[line - 1] + 1;
+ var end = line_ends[line];
+ return %_CallFunction(this.source, start, end, StringSubstring);
+}
+
+
+/**
+ * Returns the number of source lines.
+ * @return {number}
+ * Number of source lines.
+ */
+function ScriptLineCount() {
+ // Return number of source lines.
+ return this.line_ends.length;
+}
+
+
+/**
+ * If sourceURL comment is available and script starts at zero returns sourceURL
+ * comment contents. Otherwise, script name is returned. See
+ * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
+ * and Source Map Revision 3 proposal for details on using //# sourceURL and
+ * deprecated //@ sourceURL comment to identify scripts that don't have name.
+ *
+ * @return {?string} script name if present, value for //# sourceURL or
+ * deprecated //@ sourceURL comment otherwise.
+ */
+function ScriptNameOrSourceURL() {
+ if (this.line_offset > 0 || this.column_offset > 0) {
+ return this.name;
+ }
+
+ // The result is cached as on long scripts it takes noticable time to search
+ // for the sourceURL.
+ if (this.hasCachedNameOrSourceURL) {
+ return this.cachedNameOrSourceURL;
+ }
+ this.hasCachedNameOrSourceURL = true;
+
+ // TODO(608): the spaces in a regexp below had to be escaped as \040
+ // because this file is being processed by js2c whose handling of spaces
+ // in regexps is broken. Also, ['"] are excluded from allowed URLs to
+ // avoid matches against sources that invoke evals with sourceURL.
+ // A better solution would be to detect these special comments in
+ // the scanner/parser.
+ var source = ToString(this.source);
+ var sourceUrlPos = %StringIndexOf(source, "sourceURL=", 0);
+ this.cachedNameOrSourceURL = this.name;
+ if (sourceUrlPos > 4) {
+ var sourceUrlPattern =
+ /\/\/[#@][\040\t]sourceURL=[\040\t]*([^\s\'\"]*)[\040\t]*$/gm;
+ // Don't reuse lastMatchInfo here, so we create a new array with room
+ // for four captures (array with length one longer than the index
+ // of the fourth capture, where the numbering is zero-based).
+ var matchInfo = new InternalArray(CAPTURE(3) + 1);
+ var match =
+ %_RegExpExec(sourceUrlPattern, source, sourceUrlPos - 4, matchInfo);
+ if (match) {
+ this.cachedNameOrSourceURL =
+ %_SubString(source, matchInfo[CAPTURE(2)], matchInfo[CAPTURE(3)]);
+ }
+ }
+ return this.cachedNameOrSourceURL;
+}
+
+
+SetUpLockedPrototype(Script,
+ $Array("source", "name", "line_ends", "line_offset", "column_offset",
+ "cachedNameOrSourceURL", "hasCachedNameOrSourceURL" ),
+ $Array(
+ "lineFromPosition", ScriptLineFromPosition,
+ "locationFromPosition", ScriptLocationFromPosition,
+ "locationFromLine", ScriptLocationFromLine,
+ "sourceSlice", ScriptSourceSlice,
+ "sourceLine", ScriptSourceLine,
+ "lineCount", ScriptLineCount,
+ "nameOrSourceURL", ScriptNameOrSourceURL
+ )
+);
+
+
+/**
+ * Class for source location. A source location is a position within some
+ * source with the following properties:
+ * script : script object for the source
+ * line : source line number
+ * column : source column within the line
+ * 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.
+ * @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
+ * @param {number} column The column within the line for the location
+ * @param {number} start Source position for start of source context
+ * @param {number} end Source position for end of source context
+ * @constructor
+ */
+function SourceLocation(script, position, line, column, start, end) {
+ this.script = script;
+ this.position = position;
+ this.line = line;
+ this.column = column;
+ this.start = start;
+ this.end = end;
+}
+
+var kLineLengthLimit = 78;
+
+/**
+ * Restrict source location start and end positions to make the source slice
+ * no more that a certain number of characters wide.
+ * @param {number} opt_limit The with limit of the source text with a default
+ * of 78
+ * @param {number} opt_before The number of characters to prefer before the
+ * position with a default value of 10 less that the limit
+ */
+function SourceLocationRestrict(opt_limit, opt_before) {
+ // Find the actual limit to use.
+ var limit;
+ var before;
+ if (!IS_UNDEFINED(opt_limit)) {
+ limit = opt_limit;
+ } else {
+ limit = kLineLengthLimit;
+ }
+ if (!IS_UNDEFINED(opt_before)) {
+ before = opt_before;
+ } else {
+ // If no before is specified center for small limits and perfer more source
+ // before the the position that after for longer limits.
+ if (limit <= 20) {
+ before = $floor(limit / 2);
+ } else {
+ before = limit - 10;
+ }
+ }
+ if (before >= limit) {
+ before = limit - 1;
+ }
+
+ // If the [start, end[ interval is too big we restrict
+ // it in one or both ends. We make sure to always produce
+ // restricted intervals of maximum allowed size.
+ if (this.end - this.start > limit) {
+ var start_limit = this.position - before;
+ var end_limit = this.position + limit - before;
+ if (this.start < start_limit && end_limit < this.end) {
+ this.start = start_limit;
+ this.end = end_limit;
+ } else if (this.start < start_limit) {
+ this.start = this.end - limit;
+ } else {
+ this.end = this.start + limit;
+ }
+ }
+}
+
+
+/**
+ * Get the source text for a SourceLocation
+ * @return {String}
+ * Source text for this location.
+ */
+function SourceLocationSourceText() {
+ return %_CallFunction(this.script.source,
+ this.start,
+ this.end,
+ StringSubstring);
+}
+
+
+SetUpLockedPrototype(SourceLocation,
+ $Array("script", "position", "line", "column", "start", "end"),
+ $Array(
+ "restrict", SourceLocationRestrict,
+ "sourceText", SourceLocationSourceText
+ )
+);
+
+
+/**
+ * Class for a source slice. A source slice is a part of a script source with
+ * the following properties:
+ * script : script object for the source
+ * from_line : line number for the first line in the slice
+ * to_line : source line number for the last line in the slice
+ * from_position : position of the first character in the slice
+ * to_position : position of the last character in the slice
+ * The to_line and to_position are not included in the slice, that is the lines
+ * in the slice are [from_line, to_line[. Likewise the characters in the slice
+ * are [from_position, to_position[.
+ * @param {Script} script The Script object for the source slice
+ * @param {number} from_line
+ * @param {number} to_line
+ * @param {number} from_position
+ * @param {number} to_position
+ * @constructor
+ */
+function SourceSlice(script, from_line, to_line, from_position, to_position) {
+ this.script = script;
+ this.from_line = from_line;
+ this.to_line = to_line;
+ this.from_position = from_position;
+ this.to_position = to_position;
+}
+
+/**
+ * Get the source text for a SourceSlice
+ * @return {String} Source text for this slice. The last line will include
+ * the line terminating characters (if any)
+ */
+function SourceSliceSourceText() {
+ return %_CallFunction(this.script.source,
+ this.from_position,
+ this.to_position,
+ StringSubstring);
+}
+
+SetUpLockedPrototype(SourceSlice,
+ $Array("script", "from_line", "to_line", "from_position", "to_position"),
+ $Array("sourceText", SourceSliceSourceText)
+);
+
+
+// Returns the offset of the given position within the containing
+// line.
+function GetPositionInLine(message) {
+ var script = %MessageGetScript(message);
+ var start_position = %MessageGetStartPosition(message);
+ var location = script.locationFromPosition(start_position, false);
+ if (location == null) return -1;
+ location.restrict();
+ return start_position - location.start;
+}
+
+
+function GetStackTraceLine(recv, fun, pos, isGlobal) {
+ return new CallSite(recv, fun, pos, false).toString();
+}
+
+// ----------------------------------------------------------------------------
+// Error implementation
+
+var CallSiteReceiverKey = %CreateSymbol("receiver");
+var CallSiteFunctionKey = %CreateSymbol("function");
+var CallSitePositionKey = %CreateSymbol("position");
+var CallSiteStrictModeKey = %CreateSymbol("strict mode");
+
+function CallSite(receiver, fun, pos, strict_mode) {
+ this[CallSiteReceiverKey] = receiver;
+ this[CallSiteFunctionKey] = fun;
+ this[CallSitePositionKey] = pos;
+ this[CallSiteStrictModeKey] = strict_mode;
+}
+
+function CallSiteGetThis() {
+ return this[CallSiteStrictModeKey] ? void 0 : this[CallSiteReceiverKey];
+}
+
+function CallSiteGetTypeName() {
+ return GetTypeName(this[CallSiteReceiverKey], false);
+}
+
+function CallSiteIsToplevel() {
+ if (this[CallSiteReceiverKey] == null) {
+ return true;
+ }
+ return IS_GLOBAL(this[CallSiteReceiverKey]);
+}
+
+function CallSiteIsEval() {
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ return script && script.compilation_type == COMPILATION_TYPE_EVAL;
+}
+
+function CallSiteGetEvalOrigin() {
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ return FormatEvalOrigin(script);
+}
+
+function CallSiteGetScriptNameOrSourceURL() {
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ return script ? script.nameOrSourceURL() : null;
+}
+
+function CallSiteGetFunction() {
+ return this[CallSiteStrictModeKey] ? void 0 : this[CallSiteFunctionKey];
+}
+
+function CallSiteGetFunctionName() {
+ // See if the function knows its own name
+ var name = this[CallSiteFunctionKey].name;
+ if (name) {
+ return name;
+ }
+ name = %FunctionGetInferredName(this[CallSiteFunctionKey]);
+ if (name) {
+ return name;
+ }
+ // Maybe this is an evaluation?
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ if (script && script.compilation_type == COMPILATION_TYPE_EVAL) {
+ return "eval";
+ }
+ return null;
+}
+
+function CallSiteGetMethodName() {
+ // See if we can find a unique property on the receiver that holds
+ // this function.
+ var receiver = this[CallSiteReceiverKey];
+ var fun = this[CallSiteFunctionKey];
+ var ownName = fun.name;
+ if (ownName && receiver &&
+ (%_CallFunction(receiver, ownName, ObjectLookupGetter) === fun ||
+ %_CallFunction(receiver, ownName, ObjectLookupSetter) === fun ||
+ (IS_OBJECT(receiver) && %GetDataProperty(receiver, ownName) === fun))) {
+ // To handle DontEnum properties we guess that the method has
+ // the same name as the function.
+ return ownName;
+ }
+ var name = null;
+ for (var prop in receiver) {
+ if (%_CallFunction(receiver, prop, ObjectLookupGetter) === fun ||
+ %_CallFunction(receiver, prop, ObjectLookupSetter) === fun ||
+ (IS_OBJECT(receiver) && %GetDataProperty(receiver, prop) === fun)) {
+ // If we find more than one match bail out to avoid confusion.
+ if (name) {
+ return null;
+ }
+ name = prop;
+ }
+ }
+ if (name) {
+ return name;
+ }
+ return null;
+}
+
+function CallSiteGetFileName() {
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ return script ? script.name : null;
+}
+
+function CallSiteGetLineNumber() {
+ if (this[CallSitePositionKey] == -1) {
+ return null;
+ }
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var location = null;
+ if (script) {
+ location = script.locationFromPosition(this[CallSitePositionKey], true);
+ }
+ return location ? location.line + 1 : null;
+}
+
+function CallSiteGetColumnNumber() {
+ if (this[CallSitePositionKey] == -1) {
+ return null;
+ }
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ var location = null;
+ if (script) {
+ location = script.locationFromPosition(this[CallSitePositionKey], true);
+ }
+ return location ? location.column + 1: null;
+}
+
+function CallSiteIsNative() {
+ var script = %FunctionGetScript(this[CallSiteFunctionKey]);
+ return script ? (script.type == TYPE_NATIVE) : false;
+}
+
+function CallSiteGetPosition() {
+ return this[CallSitePositionKey];
+}
+
+function CallSiteIsConstructor() {
+ var receiver = this[CallSiteReceiverKey];
+ var constructor = (receiver != null && IS_OBJECT(receiver))
+ ? %GetDataProperty(receiver, "constructor") : null;
+ if (!constructor) return false;
+ return this[CallSiteFunctionKey] === constructor;
+}
+
+function CallSiteToString() {
+ var fileName;
+ var fileLocation = "";
+ if (this.isNative()) {
+ fileLocation = "native";
+ } else {
+ if (this.isEval()) {
+ fileName = this.getScriptNameOrSourceURL();
+ if (!fileName) {
+ fileLocation = this.getEvalOrigin();
+ fileLocation += ", "; // Expecting source position to follow.
+ }
+ } else {
+ fileName = this.getFileName();
+ }
+
+ if (fileName) {
+ fileLocation += fileName;
+ } else {
+ // Source code does not originate from a file and is not native, but we
+ // can still get the source position inside the source string, e.g. in
+ // an eval string.
+ fileLocation += "<anonymous>";
+ }
+ var lineNumber = this.getLineNumber();
+ if (lineNumber != null) {
+ fileLocation += ":" + lineNumber;
+ var columnNumber = this.getColumnNumber();
+ if (columnNumber) {
+ fileLocation += ":" + columnNumber;
+ }
+ }
+ }
+
+ var line = "";
+ var functionName = this.getFunctionName();
+ var addSuffix = true;
+ var isConstructor = this.isConstructor();
+ var isMethodCall = !(this.isToplevel() || isConstructor);
+ if (isMethodCall) {
+ var typeName = GetTypeName(this[CallSiteReceiverKey], true);
+ var methodName = this.getMethodName();
+ if (functionName) {
+ if (typeName &&
+ %_CallFunction(functionName, typeName, StringIndexOf) != 0) {
+ line += typeName + ".";
+ }
+ line += functionName;
+ if (methodName &&
+ (%_CallFunction(functionName, "." + methodName, StringIndexOf) !=
+ functionName.length - methodName.length - 1)) {
+ line += " [as " + methodName + "]";
+ }
+ } else {
+ line += typeName + "." + (methodName || "<anonymous>");
+ }
+ } else if (isConstructor) {
+ line += "new " + (functionName || "<anonymous>");
+ } else if (functionName) {
+ line += functionName;
+ } else {
+ line += fileLocation;
+ addSuffix = false;
+ }
+ if (addSuffix) {
+ line += " (" + fileLocation + ")";
+ }
+ return line;
+}
+
+SetUpLockedPrototype(CallSite, $Array("receiver", "fun", "pos"), $Array(
+ "getThis", CallSiteGetThis,
+ "getTypeName", CallSiteGetTypeName,
+ "isToplevel", CallSiteIsToplevel,
+ "isEval", CallSiteIsEval,
+ "getEvalOrigin", CallSiteGetEvalOrigin,
+ "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL,
+ "getFunction", CallSiteGetFunction,
+ "getFunctionName", CallSiteGetFunctionName,
+ "getMethodName", CallSiteGetMethodName,
+ "getFileName", CallSiteGetFileName,
+ "getLineNumber", CallSiteGetLineNumber,
+ "getColumnNumber", CallSiteGetColumnNumber,
+ "isNative", CallSiteIsNative,
+ "getPosition", CallSiteGetPosition,
+ "isConstructor", CallSiteIsConstructor,
+ "toString", CallSiteToString
+));
+
+
+function FormatEvalOrigin(script) {
+ var sourceURL = script.nameOrSourceURL();
+ if (sourceURL) {
+ return sourceURL;
+ }
+
+ var eval_origin = "eval at ";
+ if (script.eval_from_function_name) {
+ eval_origin += script.eval_from_function_name;
+ } else {
+ eval_origin += "<anonymous>";
+ }
+
+ var eval_from_script = script.eval_from_script;
+ if (eval_from_script) {
+ if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
+ // eval script originated from another eval.
+ eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
+ } else {
+ // 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);
+ if (location) {
+ eval_origin += ":" + (location.line + 1);
+ eval_origin += ":" + (location.column + 1);
+ }
+ eval_origin += ")";
+ } else {
+ eval_origin += " (unknown source)";
+ }
+ }
+ }
+
+ return eval_origin;
+}
+
+
+function FormatErrorString(error) {
+ try {
+ return %_CallFunction(error, ErrorToString);
+ } catch (e) {
+ try {
+ return "<error: " + e + ">";
+ } catch (ee) {
+ return "<error>";
+ }
+ }
+}
+
+
+function GetStackFrames(raw_stack) {
+ var frames = new InternalArray();
+ var non_strict_frames = raw_stack[0];
+ for (var i = 1; i < raw_stack.length; i += 4) {
+ var recv = raw_stack[i];
+ var fun = raw_stack[i + 1];
+ var code = raw_stack[i + 2];
+ var pc = raw_stack[i + 3];
+ var pos = %FunctionGetPositionForOffset(code, pc);
+ non_strict_frames--;
+ frames.push(new CallSite(recv, fun, pos, (non_strict_frames < 0)));
+ }
+ return frames;
+}
+
+
+// Flag to prevent recursive call of Error.prepareStackTrace.
+var formatting_custom_stack_trace = false;
+
+
+function FormatStackTrace(obj, error_string, frames) {
+ if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) {
+ var array = [];
+ %MoveArrayContents(frames, array);
+ formatting_custom_stack_trace = true;
+ var stack_trace = void 0;
+ try {
+ stack_trace = $Error.prepareStackTrace(obj, array);
+ } catch (e) {
+ throw e; // The custom formatting function threw. Rethrow.
+ } finally {
+ formatting_custom_stack_trace = false;
+ }
+ return stack_trace;
+ }
+
+ var lines = new InternalArray();
+ lines.push(error_string);
+ for (var i = 0; i < frames.length; i++) {
+ var frame = frames[i];
+ var line;
+ try {
+ line = frame.toString();
+ } catch (e) {
+ try {
+ line = "<error: " + e + ">";
+ } catch (ee) {
+ // Any code that reaches this point is seriously nasty!
+ line = "<error>";
+ }
+ }
+ lines.push(" at " + line);
+ }
+ return %_CallFunction(lines, "\n", ArrayJoin);
+}
+
+
+function GetTypeName(receiver, requireConstructor) {
+ var constructor = receiver.constructor;
+ if (!constructor) {
+ return requireConstructor ? null :
+ %_CallFunction(receiver, ObjectToString);
+ }
+ var constructorName = constructor.name;
+ if (!constructorName) {
+ return requireConstructor ? null :
+ %_CallFunction(receiver, ObjectToString);
+ }
+ return constructorName;
+}
+
+
+function captureStackTrace(obj, cons_opt) {
+ var stackTraceLimit = $Error.stackTraceLimit;
+ if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
+ if (stackTraceLimit < 0 || stackTraceLimit > 10000) {
+ stackTraceLimit = 10000;
+ }
+ var stack = %CollectStackTrace(obj,
+ cons_opt ? cons_opt : captureStackTrace,
+ stackTraceLimit);
+
+ var error_string = FormatErrorString(obj);
+ // The holder of this getter ('obj') may not be the receiver ('this').
+ // When this getter is called the first time, we use the context values to
+ // format a stack trace string and turn this accessor pair into a data
+ // property (on the holder).
+ var getter = function() {
+ // Stack is still a raw array awaiting to be formatted.
+ var result = FormatStackTrace(obj, error_string, GetStackFrames(stack));
+ // Turn this accessor into a data property.
+ %DefineOrRedefineDataProperty(obj, 'stack', result, NONE);
+ // Release context values.
+ stack = error_string = void 0;
+ return result;
+ };
+
+ // Set the 'stack' property on the receiver. If the receiver is the same as
+ // holder of this setter, the accessor pair is turned into a data property.
+ var setter = function(v) {
+ // Set data property on the receiver (not necessarily holder).
+ %DefineOrRedefineDataProperty(this, 'stack', v, NONE);
+ if (this === obj) {
+ // Release context values if holder is the same as the receiver.
+ stack = error_string = void 0;
+ }
+ };
+
+ %DefineOrRedefineAccessorProperty(obj, 'stack', getter, setter, DONT_ENUM);
+}
+
+
+function SetUpError() {
+ // Define special error type constructors.
+
+ var DefineError = function(f) {
+ // Store the error function in both the global object
+ // and the runtime object. The function is fetched
+ // from the runtime object when throwing errors from
+ // within the runtime system to avoid strange side
+ // effects when overwriting the error functions from
+ // user code.
+ var name = f.name;
+ %SetProperty(global, name, f, DONT_ENUM);
+ %SetProperty(builtins, '$' + name, f, DONT_ENUM | DONT_DELETE | READ_ONLY);
+ // Configure the error function.
+ if (name == 'Error') {
+ // The prototype of the Error object must itself be an error.
+ // However, it can't be an instance of the Error object because
+ // it hasn't been properly configured yet. Instead we create a
+ // special not-a-true-error-but-close-enough object.
+ var ErrorPrototype = function() {};
+ %FunctionSetPrototype(ErrorPrototype, $Object.prototype);
+ %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
+ %FunctionSetPrototype(f, new ErrorPrototype());
+ } else {
+ %FunctionSetPrototype(f, new $Error());
+ }
+ %FunctionSetInstanceClassName(f, 'Error');
+ %SetProperty(f.prototype, 'constructor', f, DONT_ENUM);
+ %SetProperty(f.prototype, "name", name, DONT_ENUM);
+ %SetCode(f, function(m) {
+ if (%_IsConstructCall()) {
+ // Define all the expected properties directly on the error
+ // object. This avoids going through getters and setters defined
+ // on prototype objects.
+ %IgnoreAttributesAndSetProperty(this, 'stack', void 0, DONT_ENUM);
+ if (!IS_UNDEFINED(m)) {
+ %IgnoreAttributesAndSetProperty(
+ this, 'message', ToString(m), DONT_ENUM);
+ }
+ captureStackTrace(this, f);
+ } else {
+ return new f(m);
+ }
+ });
+ %SetNativeFlag(f);
+ };
+
+ DefineError(function Error() { });
+ DefineError(function TypeError() { });
+ DefineError(function RangeError() { });
+ DefineError(function SyntaxError() { });
+ DefineError(function ReferenceError() { });
+ DefineError(function EvalError() { });
+ DefineError(function URIError() { });
+}
+
+SetUpError();
+
+$Error.captureStackTrace = captureStackTrace;
+
+%SetProperty($Error.prototype, 'message', '', DONT_ENUM);
+
+// Global list of error objects visited during ErrorToString. This is
+// used to detect cycles in error toString formatting.
+var visited_errors = new InternalArray();
+var cyclic_error_marker = new $Object();
+
+function GetPropertyWithoutInvokingMonkeyGetters(error, name) {
+ // Climb the prototype chain until we find the holder.
+ while (error && !%HasLocalProperty(error, name)) {
+ error = %GetPrototype(error);
+ }
+ if (error === null) return void 0;
+ if (!IS_OBJECT(error)) return error[name];
+ // If the property is an accessor on one of the predefined errors that can be
+ // generated statically by the compiler, don't touch it. This is to address
+ // http://code.google.com/p/chromium/issues/detail?id=69187
+ var desc = %GetOwnProperty(error, name);
+ if (desc && desc[IS_ACCESSOR_INDEX]) {
+ var isName = name === "name";
+ if (error === $ReferenceError.prototype)
+ return isName ? "ReferenceError" : void 0;
+ if (error === $SyntaxError.prototype)
+ return isName ? "SyntaxError" : void 0;
+ if (error === $TypeError.prototype)
+ return isName ? "TypeError" : void 0;
+ }
+ // Otherwise, read normally.
+ return error[name];
+}
+
+function ErrorToStringDetectCycle(error) {
+ if (!%PushIfAbsent(visited_errors, error)) throw cyclic_error_marker;
+ try {
+ var name = GetPropertyWithoutInvokingMonkeyGetters(error, "name");
+ name = IS_UNDEFINED(name) ? "Error" : TO_STRING_INLINE(name);
+ var message = GetPropertyWithoutInvokingMonkeyGetters(error, "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() {
+ if (!IS_SPEC_OBJECT(this)) {
+ throw MakeTypeError("called_on_non_object", ["Error.prototype.toString"]);
+ }
+
+ try {
+ return ErrorToStringDetectCycle(this);
+ } catch(e) {
+ // If this error message was encountered already return the empty
+ // string for it instead of recursively formatting it.
+ if (e === cyclic_error_marker) {
+ return '';
+ }
+ throw e;
+ }
+}
+
+
+InstallFunctions($Error.prototype, DONT_ENUM, ['toString', ErrorToString]);
+
+// Boilerplate for exceptions for stack overflows. Used from
+// Isolate::StackOverflow().
+function SetUpStackOverflowBoilerplate() {
+ var boilerplate = MakeRangeError('stack_overflow', []);
+
+ var error_string = boilerplate.name + ": " + boilerplate.message;
+
+ // The raw stack trace is stored as a hidden property on the holder of this
+ // getter, which may not be the same as the receiver. Find the holder to
+ // retrieve the raw stack trace and then turn this accessor pair into a
+ // data property.
+ var getter = function() {
+ var holder = this;
+ while (!IS_ERROR(holder)) {
+ holder = %GetPrototype(holder);
+ if (IS_NULL(holder)) return MakeSyntaxError('illegal_access', []);
+ }
+ var stack = %GetAndClearOverflowedStackTrace(holder);
+ // We may not have captured any stack trace.
+ if (IS_UNDEFINED(stack)) return stack;
+
+ var result = FormatStackTrace(holder, error_string, GetStackFrames(stack));
+ // Replace this accessor with a data property.
+ %DefineOrRedefineDataProperty(holder, 'stack', result, NONE);
+ return result;
+ };
+
+ // Set the 'stack' property on the receiver. If the receiver is the same as
+ // holder of this setter, the accessor pair is turned into a data property.
+ var setter = function(v) {
+ %DefineOrRedefineDataProperty(this, 'stack', v, NONE);
+ // Tentatively clear the hidden property. If the receiver is the same as
+ // holder, we release the raw stack trace this way.
+ %GetAndClearOverflowedStackTrace(this);
+ };
+
+ %DefineOrRedefineAccessorProperty(
+ boilerplate, 'stack', getter, setter, DONT_ENUM);
+
+ return boilerplate;
+}
+
+var kStackOverflowBoilerplate = SetUpStackOverflowBoilerplate();
diff --git a/chromium/v8/src/mips/OWNERS b/chromium/v8/src/mips/OWNERS
new file mode 100644
index 00000000000..38473b56d1f
--- /dev/null
+++ b/chromium/v8/src/mips/OWNERS
@@ -0,0 +1,2 @@
+plind44@gmail.com
+gergely@homejinni.com
diff --git a/chromium/v8/src/mips/assembler-mips-inl.h b/chromium/v8/src/mips/assembler-mips-inl.h
new file mode 100644
index 00000000000..2ca00831cfb
--- /dev/null
+++ b/chromium/v8/src/mips/assembler-mips-inl.h
@@ -0,0 +1,425 @@
+
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// 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"
+
+
+namespace v8 {
+namespace internal {
+
+// -----------------------------------------------------------------------------
+// Operand and MemOperand.
+
+Operand::Operand(int32_t immediate, RelocInfo::Mode rmode) {
+ rm_ = no_reg;
+ imm32_ = immediate;
+ rmode_ = rmode;
+}
+
+
+Operand::Operand(const ExternalReference& f) {
+ rm_ = no_reg;
+ imm32_ = reinterpret_cast<int32_t>(f.address());
+ rmode_ = RelocInfo::EXTERNAL_REFERENCE;
+}
+
+
+Operand::Operand(Smi* value) {
+ rm_ = no_reg;
+ imm32_ = reinterpret_cast<intptr_t>(value);
+ rmode_ = RelocInfo::NONE32;
+}
+
+
+Operand::Operand(Register rm) {
+ rm_ = rm;
+}
+
+
+bool Operand::is_reg() const {
+ return rm_.is_valid();
+}
+
+
+int Register::NumAllocatableRegisters() {
+ return kMaxNumAllocatableRegisters;
+}
+
+
+int DoubleRegister::NumRegisters() {
+ return FPURegister::kMaxNumRegisters;
+}
+
+
+int DoubleRegister::NumAllocatableRegisters() {
+ return FPURegister::kMaxNumAllocatableRegisters;
+}
+
+
+int FPURegister::ToAllocationIndex(FPURegister reg) {
+ ASSERT(reg.code() % 2 == 0);
+ ASSERT(reg.code() / 2 < kMaxNumAllocatableRegisters);
+ ASSERT(reg.is_valid());
+ ASSERT(!reg.is(kDoubleRegZero));
+ ASSERT(!reg.is(kLithiumScratchDouble));
+ return (reg.code() / 2);
+}
+
+
+// -----------------------------------------------------------------------------
+// RelocInfo.
+
+void RelocInfo::apply(intptr_t delta) {
+ if (IsCodeTarget(rmode_)) {
+ uint32_t scope1 = (uint32_t) target_address() & ~kImm28Mask;
+ uint32_t scope2 = reinterpret_cast<uint32_t>(pc_) & ~kImm28Mask;
+
+ if (scope1 != scope2) {
+ Assembler::JumpLabelToJumpRegister(pc_);
+ }
+ }
+ if (IsInternalReference(rmode_)) {
+ // Absolute code pointer inside code object moves with the code object.
+ byte* p = reinterpret_cast<byte*>(pc_);
+ int count = Assembler::RelocateInternalReference(p, delta);
+ CPU::FlushICache(p, count * sizeof(uint32_t));
+ }
+}
+
+
+Address RelocInfo::target_address() {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
+ return Assembler::target_address_at(pc_);
+}
+
+
+Address RelocInfo::target_address_address() {
+ ASSERT(IsCodeTarget(rmode_) ||
+ IsRuntimeEntry(rmode_) ||
+ rmode_ == EMBEDDED_OBJECT ||
+ rmode_ == EXTERNAL_REFERENCE);
+ // Read the address of the word containing the target_address in an
+ // instruction stream.
+ // The only architecture-independent user of this function is the serializer.
+ // The serializer uses it to find out how many raw bytes of instruction to
+ // output before the next target.
+ // For an instruction like LUI/ORI where the target bits are mixed into the
+ // instruction bits, the size of the target will be zero, indicating that the
+ // serializer should not step forward in memory after a target is resolved
+ // and written. In this case the target_address_address function should
+ // return the end of the instructions to be patched, allowing the
+ // deserializer to deserialize the instructions as raw bytes and put them in
+ // place, ready to be patched with the target. After jump optimization,
+ // that is the address of the instruction that follows J/JAL/JR/JALR
+ // instruction.
+ return reinterpret_cast<Address>(
+ pc_ + Assembler::kInstructionsFor32BitConstant * Assembler::kInstrSize);
+}
+
+
+int RelocInfo::target_address_size() {
+ return Assembler::kSpecialTargetSize;
+}
+
+
+void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
+ 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));
+ }
+}
+
+
+Address Assembler::target_address_from_return_address(Address pc) {
+ return pc - kCallTargetAddressOffset;
+}
+
+
+Object* RelocInfo::target_object() {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ return reinterpret_cast<Object*>(Assembler::target_address_at(pc_));
+}
+
+
+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_)));
+}
+
+
+Object** RelocInfo::target_object_address() {
+ // Provide a "natural pointer" to the embedded object,
+ // which can be de-referenced during heap iteration.
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ reconstructed_obj_ptr_ =
+ reinterpret_cast<Object*>(Assembler::target_address_at(pc_));
+ return &reconstructed_obj_ptr_;
+}
+
+
+void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ ASSERT(!target->IsConsString());
+ 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));
+ }
+}
+
+
+Address* RelocInfo::target_reference_address() {
+ ASSERT(rmode_ == EXTERNAL_REFERENCE);
+ reconstructed_adr_ptr_ = Assembler::target_address_at(pc_);
+ return &reconstructed_adr_ptr_;
+}
+
+
+Address RelocInfo::target_runtime_entry(Assembler* origin) {
+ ASSERT(IsRuntimeEntry(rmode_));
+ return target_address();
+}
+
+
+void RelocInfo::set_target_runtime_entry(Address target,
+ WriteBarrierMode mode) {
+ ASSERT(IsRuntimeEntry(rmode_));
+ if (target_address() != target) set_target_address(target, mode);
+}
+
+
+Handle<Cell> RelocInfo::target_cell_handle() {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ Address address = Memory::Address_at(pc_);
+ return Handle<Cell>(reinterpret_cast<Cell**>(address));
+}
+
+
+Cell* RelocInfo::target_cell() {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ return Cell::FromValueAddress(Memory::Address_at(pc_));
+}
+
+
+void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ Address address = cell->address() + Cell::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);
+ }
+}
+
+
+static const int kNoCodeAgeSequenceLength = 7;
+
+Code* RelocInfo::code_age_stub() {
+ ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
+ return Code::GetCodeFromTargetAddress(
+ Memory::Address_at(pc_ + Assembler::kInstrSize *
+ (kNoCodeAgeSequenceLength - 1)));
+}
+
+
+void RelocInfo::set_code_age_stub(Code* stub) {
+ ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
+ Memory::Address_at(pc_ + Assembler::kInstrSize *
+ (kNoCodeAgeSequenceLength - 1)) =
+ stub->instruction_start();
+}
+
+
+Address RelocInfo::call_address() {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ // The pc_ offset of 0 assumes mips patched return sequence per
+ // debug-mips.cc BreakLocationIterator::SetDebugBreakAtReturn(), or
+ // debug break slot per BreakLocationIterator::SetDebugBreakAtSlot().
+ return Assembler::target_address_at(pc_);
+}
+
+
+void RelocInfo::set_call_address(Address target) {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ // The pc_ offset of 0 assumes mips patched return sequence per
+ // 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));
+ }
+}
+
+
+Object* RelocInfo::call_object() {
+ return *call_object_address();
+}
+
+
+Object** RelocInfo::call_object_address() {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
+}
+
+
+void RelocInfo::set_call_object(Object* target) {
+ *call_object_address() = target;
+}
+
+
+bool RelocInfo::IsPatchedReturnSequence() {
+ Instr instr0 = Assembler::instr_at(pc_);
+ Instr instr1 = Assembler::instr_at(pc_ + 1 * Assembler::kInstrSize);
+ Instr instr2 = Assembler::instr_at(pc_ + 2 * Assembler::kInstrSize);
+ bool patched_return = ((instr0 & kOpcodeMask) == LUI &&
+ (instr1 & kOpcodeMask) == ORI &&
+ ((instr2 & kOpcodeMask) == JAL ||
+ ((instr2 & kOpcodeMask) == SPECIAL &&
+ (instr2 & kFunctionFieldMask) == JALR)));
+ return patched_return;
+}
+
+
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+ Instr current_instr = Assembler::instr_at(pc_);
+ return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
+}
+
+
+void RelocInfo::Visit(ObjectVisitor* visitor) {
+ RelocInfo::Mode mode = rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT) {
+ visitor->VisitEmbeddedPointer(this);
+ } else if (RelocInfo::IsCodeTarget(mode)) {
+ visitor->VisitCodeTarget(this);
+ } else if (mode == RelocInfo::CELL) {
+ visitor->VisitCell(this);
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
+ visitor->VisitExternalReference(this);
+ } else if (RelocInfo::IsCodeAgeSequence(mode)) {
+ visitor->VisitCodeAgeSequence(this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // TODO(isolates): Get a cached isolate below.
+ } else if (((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence())) &&
+ Isolate::Current()->debug()->has_break_points()) {
+ visitor->VisitDebugTarget(this);
+#endif
+ } else if (RelocInfo::IsRuntimeEntry(mode)) {
+ visitor->VisitRuntimeEntry(this);
+ }
+}
+
+
+template<typename StaticVisitor>
+void RelocInfo::Visit(Heap* heap) {
+ RelocInfo::Mode mode = rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT) {
+ StaticVisitor::VisitEmbeddedPointer(heap, this);
+ } else if (RelocInfo::IsCodeTarget(mode)) {
+ StaticVisitor::VisitCodeTarget(heap, this);
+ } else if (mode == RelocInfo::CELL) {
+ StaticVisitor::VisitCell(heap, this);
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
+ StaticVisitor::VisitExternalReference(this);
+ } else if (RelocInfo::IsCodeAgeSequence(mode)) {
+ StaticVisitor::VisitCodeAgeSequence(heap, this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ } else if (heap->isolate()->debug()->has_break_points() &&
+ ((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()))) {
+ StaticVisitor::VisitDebugTarget(heap, this);
+#endif
+ } else if (RelocInfo::IsRuntimeEntry(mode)) {
+ StaticVisitor::VisitRuntimeEntry(this);
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Assembler.
+
+
+void Assembler::CheckBuffer() {
+ if (buffer_space() <= kGap) {
+ GrowBuffer();
+ }
+}
+
+
+void Assembler::CheckTrampolinePoolQuick() {
+ if (pc_offset() >= next_buffer_check_) {
+ CheckTrampolinePool();
+ }
+}
+
+
+void Assembler::emit(Instr x) {
+ if (!is_buffer_growth_blocked()) {
+ CheckBuffer();
+ }
+ *reinterpret_cast<Instr*>(pc_) = x;
+ pc_ += kInstrSize;
+ CheckTrampolinePoolQuick();
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_MIPS_ASSEMBLER_MIPS_INL_H_
diff --git a/chromium/v8/src/mips/assembler-mips.cc b/chromium/v8/src/mips/assembler-mips.cc
new file mode 100644
index 00000000000..fcf49f110d1
--- /dev/null
+++ b/chromium/v8/src/mips/assembler-mips.cc
@@ -0,0 +1,2304 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "mips/assembler-mips-inl.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef DEBUG
+bool CpuFeatures::initialized_ = false;
+#endif
+unsigned CpuFeatures::supported_ = 0;
+unsigned CpuFeatures::found_by_runtime_probing_only_ = 0;
+
+
+ExternalReference ExternalReference::cpu_features() {
+ ASSERT(CpuFeatures::initialized_);
+ return ExternalReference(&CpuFeatures::supported_);
+}
+
+
+// Get the CPU features enabled by the build. For cross compilation the
+// preprocessor symbols CAN_USE_FPU_INSTRUCTIONS
+// can be defined to enable FPU instructions when building the
+// snapshot.
+static uint64_t CpuFeaturesImpliedByCompiler() {
+ uint64_t answer = 0;
+#ifdef CAN_USE_FPU_INSTRUCTIONS
+ answer |= static_cast<uint64_t>(1) << FPU;
+#endif // def CAN_USE_FPU_INSTRUCTIONS
+
+#ifdef __mips__
+ // If the compiler is allowed to use FPU then we can use FPU too in our code
+ // generation even when generating snapshots. This won't work for cross
+ // compilation.
+#if(defined(__mips_hard_float) && __mips_hard_float != 0)
+ answer |= static_cast<uint64_t>(1) << FPU;
+#endif // defined(__mips_hard_float) && __mips_hard_float != 0
+#endif // def __mips__
+
+ return answer;
+}
+
+
+const char* DoubleRegister::AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ const char* const names[] = {
+ "f0",
+ "f2",
+ "f4",
+ "f6",
+ "f8",
+ "f10",
+ "f12",
+ "f14",
+ "f16",
+ "f18",
+ "f20",
+ "f22",
+ "f24",
+ "f26"
+ };
+ return names[index];
+}
+
+
+void CpuFeatures::Probe() {
+ unsigned standard_features = (OS::CpuFeaturesImpliedByPlatform() |
+ CpuFeaturesImpliedByCompiler());
+ ASSERT(supported_ == 0 || supported_ == standard_features);
+#ifdef DEBUG
+ initialized_ = true;
+#endif
+
+ // 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_ |= standard_features;
+
+ if (Serializer::enabled()) {
+ // No probing for features if we might serialize (generate snapshot).
+ return;
+ }
+
+ // If the compiler is allowed to use fpu then we can use fpu too in our
+ // code generation.
+#if !defined(__mips__)
+ // For the simulator build, use FPU.
+ supported_ |= static_cast<uint64_t>(1) << FPU;
+#else
+ // Probe for additional features not already known to be available.
+ if (OS::MipsCpuHasFeature(FPU)) {
+ // This implementation also sets the FPU flags if
+ // runtime detection of FPU returns true.
+ supported_ |= static_cast<uint64_t>(1) << FPU;
+ found_by_runtime_probing_only_ |= static_cast<uint64_t>(1) << FPU;
+ }
+#endif
+}
+
+
+int ToNumber(Register reg) {
+ ASSERT(reg.is_valid());
+ const int kNumbers[] = {
+ 0, // zero_reg
+ 1, // at
+ 2, // v0
+ 3, // v1
+ 4, // a0
+ 5, // a1
+ 6, // a2
+ 7, // a3
+ 8, // t0
+ 9, // t1
+ 10, // t2
+ 11, // t3
+ 12, // t4
+ 13, // t5
+ 14, // t6
+ 15, // t7
+ 16, // s0
+ 17, // s1
+ 18, // s2
+ 19, // s3
+ 20, // s4
+ 21, // s5
+ 22, // s6
+ 23, // s7
+ 24, // t8
+ 25, // t9
+ 26, // k0
+ 27, // k1
+ 28, // gp
+ 29, // sp
+ 30, // fp
+ 31, // ra
+ };
+ return kNumbers[reg.code()];
+}
+
+
+Register ToRegister(int num) {
+ ASSERT(num >= 0 && num < kNumRegisters);
+ const Register kRegisters[] = {
+ zero_reg,
+ at,
+ v0, v1,
+ a0, a1, a2, a3,
+ t0, t1, t2, t3, t4, t5, t6, t7,
+ s0, s1, s2, s3, s4, s5, s6, s7,
+ t8, t9,
+ k0, k1,
+ gp,
+ sp,
+ fp,
+ ra
+ };
+ return kRegisters[num];
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of RelocInfo.
+
+const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask |
+ 1 << RelocInfo::INTERNAL_REFERENCE;
+
+
+bool RelocInfo::IsCodedSpecially() {
+ // The deserializer needs to know whether a pointer is specially coded. Being
+ // specially coded on MIPS means that it is a lui/ori instruction, and that is
+ // always the case inside code objects.
+ return true;
+}
+
+
+// Patch the code at the current address with the supplied instructions.
+void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
+ Instr* pc = reinterpret_cast<Instr*>(pc_);
+ Instr* instr = reinterpret_cast<Instr*>(instructions);
+ for (int i = 0; i < instruction_count; i++) {
+ *(pc + i) = *(instr + i);
+ }
+
+ // Indicate that code has changed.
+ CPU::FlushICache(pc_, instruction_count * Assembler::kInstrSize);
+}
+
+
+// Patch the code at the current PC with a call to the target address.
+// Additional guard instructions can be added if required.
+void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
+ // Patch the code at the current address with a call to the target.
+ UNIMPLEMENTED_MIPS();
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Operand and MemOperand.
+// See assembler-mips-inl.h for inlined constructors.
+
+Operand::Operand(Handle<Object> handle) {
+#ifdef DEBUG
+ Isolate* isolate = Isolate::Current();
+#endif
+ AllowDeferredHandleDereference using_raw_address;
+ rm_ = no_reg;
+ // Verify all Objects referred by code are NOT in new space.
+ Object* obj = *handle;
+ ASSERT(!isolate->heap()->InNewSpace(obj));
+ if (obj->IsHeapObject()) {
+ imm32_ = reinterpret_cast<intptr_t>(handle.location());
+ rmode_ = RelocInfo::EMBEDDED_OBJECT;
+ } else {
+ // No relocation needed.
+ imm32_ = reinterpret_cast<intptr_t>(obj);
+ rmode_ = RelocInfo::NONE32;
+ }
+}
+
+
+MemOperand::MemOperand(Register rm, int32_t offset) : Operand(rm) {
+ offset_ = offset;
+}
+
+
+// -----------------------------------------------------------------------------
+// Specific instructions, constants, and masks.
+
+static const int kNegOffset = 0x00008000;
+// addiu(sp, sp, 4) aka Pop() operation or part of Pop(r)
+// operations as post-increment of sp.
+const Instr kPopInstruction = ADDIU | (kRegister_sp_Code << kRsShift)
+ | (kRegister_sp_Code << kRtShift) | (kPointerSize & kImm16Mask);
+// addiu(sp, sp, -4) part of Push(r) operation as pre-decrement of sp.
+const Instr kPushInstruction = ADDIU | (kRegister_sp_Code << kRsShift)
+ | (kRegister_sp_Code << kRtShift) | (-kPointerSize & kImm16Mask);
+// sw(r, MemOperand(sp, 0))
+const Instr kPushRegPattern = SW | (kRegister_sp_Code << kRsShift)
+ | (0 & kImm16Mask);
+// lw(r, MemOperand(sp, 0))
+const Instr kPopRegPattern = LW | (kRegister_sp_Code << kRsShift)
+ | (0 & kImm16Mask);
+
+const Instr kLwRegFpOffsetPattern = LW | (kRegister_fp_Code << kRsShift)
+ | (0 & kImm16Mask);
+
+const Instr kSwRegFpOffsetPattern = SW | (kRegister_fp_Code << kRsShift)
+ | (0 & kImm16Mask);
+
+const Instr kLwRegFpNegOffsetPattern = LW | (kRegister_fp_Code << kRsShift)
+ | (kNegOffset & kImm16Mask);
+
+const Instr kSwRegFpNegOffsetPattern = SW | (kRegister_fp_Code << kRsShift)
+ | (kNegOffset & kImm16Mask);
+// A mask for the Rt register for push, pop, lw, sw instructions.
+const Instr kRtMask = kRtFieldMask;
+const Instr kLwSwInstrTypeMask = 0xffe00000;
+const Instr kLwSwInstrArgumentMask = ~kLwSwInstrTypeMask;
+const Instr kLwSwOffsetMask = kImm16Mask;
+
+
+Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
+ : AssemblerBase(isolate, buffer, buffer_size),
+ recorded_ast_id_(TypeFeedbackId::None()),
+ positions_recorder_(this) {
+ reloc_info_writer.Reposition(buffer_ + buffer_size_, pc_);
+
+ last_trampoline_pool_end_ = 0;
+ no_trampoline_pool_before_ = 0;
+ trampoline_pool_blocked_nesting_ = 0;
+ // We leave space (16 * kTrampolineSlotsSize)
+ // for BlockTrampolinePoolScope buffer.
+ next_buffer_check_ = kMaxBranchOffset - kTrampolineSlotsSize * 16;
+ internal_trampoline_exception_ = false;
+ last_bound_pos_ = 0;
+
+ trampoline_emitted_ = false;
+ unbound_labels_count_ = 0;
+ block_buffer_growth_ = false;
+
+ ClearRecordedAstId();
+}
+
+
+void Assembler::GetCode(CodeDesc* desc) {
+ ASSERT(pc_ <= reloc_info_writer.pos()); // No overlap.
+ // Set up code descriptor.
+ desc->buffer = buffer_;
+ desc->buffer_size = buffer_size_;
+ desc->instr_size = pc_offset();
+ desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
+}
+
+
+void Assembler::Align(int m) {
+ ASSERT(m >= 4 && IsPowerOf2(m));
+ while ((pc_offset() & (m - 1)) != 0) {
+ nop();
+ }
+}
+
+
+void Assembler::CodeTargetAlign() {
+ // No advantage to aligning branch/call targets to more than
+ // single instruction, that I am aware of.
+ Align(4);
+}
+
+
+Register Assembler::GetRtReg(Instr instr) {
+ Register rt;
+ rt.code_ = (instr & kRtFieldMask) >> kRtShift;
+ return rt;
+}
+
+
+Register Assembler::GetRsReg(Instr instr) {
+ Register rs;
+ rs.code_ = (instr & kRsFieldMask) >> kRsShift;
+ return rs;
+}
+
+
+Register Assembler::GetRdReg(Instr instr) {
+ Register rd;
+ rd.code_ = (instr & kRdFieldMask) >> kRdShift;
+ return rd;
+}
+
+
+uint32_t Assembler::GetRt(Instr instr) {
+ return (instr & kRtFieldMask) >> kRtShift;
+}
+
+
+uint32_t Assembler::GetRtField(Instr instr) {
+ return instr & kRtFieldMask;
+}
+
+
+uint32_t Assembler::GetRs(Instr instr) {
+ return (instr & kRsFieldMask) >> kRsShift;
+}
+
+
+uint32_t Assembler::GetRsField(Instr instr) {
+ return instr & kRsFieldMask;
+}
+
+
+uint32_t Assembler::GetRd(Instr instr) {
+ return (instr & kRdFieldMask) >> kRdShift;
+}
+
+
+uint32_t Assembler::GetRdField(Instr instr) {
+ return instr & kRdFieldMask;
+}
+
+
+uint32_t Assembler::GetSa(Instr instr) {
+ return (instr & kSaFieldMask) >> kSaShift;
+}
+
+
+uint32_t Assembler::GetSaField(Instr instr) {
+ return instr & kSaFieldMask;
+}
+
+
+uint32_t Assembler::GetOpcodeField(Instr instr) {
+ return instr & kOpcodeMask;
+}
+
+
+uint32_t Assembler::GetFunction(Instr instr) {
+ return (instr & kFunctionFieldMask) >> kFunctionShift;
+}
+
+
+uint32_t Assembler::GetFunctionField(Instr instr) {
+ return instr & kFunctionFieldMask;
+}
+
+
+uint32_t Assembler::GetImmediate16(Instr instr) {
+ return instr & kImm16Mask;
+}
+
+
+uint32_t Assembler::GetLabelConst(Instr instr) {
+ return instr & ~kImm16Mask;
+}
+
+
+bool Assembler::IsPop(Instr instr) {
+ return (instr & ~kRtMask) == kPopRegPattern;
+}
+
+
+bool Assembler::IsPush(Instr instr) {
+ return (instr & ~kRtMask) == kPushRegPattern;
+}
+
+
+bool Assembler::IsSwRegFpOffset(Instr instr) {
+ return ((instr & kLwSwInstrTypeMask) == kSwRegFpOffsetPattern);
+}
+
+
+bool Assembler::IsLwRegFpOffset(Instr instr) {
+ return ((instr & kLwSwInstrTypeMask) == kLwRegFpOffsetPattern);
+}
+
+
+bool Assembler::IsSwRegFpNegOffset(Instr instr) {
+ return ((instr & (kLwSwInstrTypeMask | kNegOffset)) ==
+ kSwRegFpNegOffsetPattern);
+}
+
+
+bool Assembler::IsLwRegFpNegOffset(Instr instr) {
+ return ((instr & (kLwSwInstrTypeMask | kNegOffset)) ==
+ kLwRegFpNegOffsetPattern);
+}
+
+
+// Labels refer to positions in the (to be) generated code.
+// There are bound, linked, and unused labels.
+//
+// Bound labels refer to known positions in the already
+// generated code. pos() is the position the label refers to.
+//
+// Linked labels refer to unknown positions in the code
+// to be generated; pos() is the position of the last
+// instruction using the label.
+
+// The link chain is terminated by a value in the instruction of -1,
+// which is an otherwise illegal value (branch -1 is inf loop).
+// The instruction 16-bit offset field addresses 32-bit words, but in
+// code is conv to an 18-bit value addressing bytes, hence the -4 value.
+
+const int kEndOfChain = -4;
+// Determines the end of the Jump chain (a subset of the label link chain).
+const int kEndOfJumpChain = 0;
+
+
+bool Assembler::IsBranch(Instr instr) {
+ uint32_t opcode = GetOpcodeField(instr);
+ uint32_t rt_field = GetRtField(instr);
+ uint32_t rs_field = GetRsField(instr);
+ // Checks if the instruction is a branch.
+ return opcode == BEQ ||
+ opcode == BNE ||
+ opcode == BLEZ ||
+ opcode == BGTZ ||
+ opcode == BEQL ||
+ opcode == BNEL ||
+ opcode == BLEZL ||
+ opcode == BGTZL ||
+ (opcode == REGIMM && (rt_field == BLTZ || rt_field == BGEZ ||
+ rt_field == BLTZAL || rt_field == BGEZAL)) ||
+ (opcode == COP1 && rs_field == BC1); // Coprocessor branch.
+}
+
+
+bool Assembler::IsEmittedConstant(Instr instr) {
+ uint32_t label_constant = GetLabelConst(instr);
+ return label_constant == 0; // Emitted label const in reg-exp engine.
+}
+
+
+bool Assembler::IsBeq(Instr instr) {
+ return GetOpcodeField(instr) == BEQ;
+}
+
+
+bool Assembler::IsBne(Instr instr) {
+ return GetOpcodeField(instr) == BNE;
+}
+
+
+bool Assembler::IsJump(Instr instr) {
+ uint32_t opcode = GetOpcodeField(instr);
+ uint32_t rt_field = GetRtField(instr);
+ uint32_t rd_field = GetRdField(instr);
+ uint32_t function_field = GetFunctionField(instr);
+ // Checks if the instruction is a jump.
+ return opcode == J || opcode == JAL ||
+ (opcode == SPECIAL && rt_field == 0 &&
+ ((function_field == JALR) || (rd_field == 0 && (function_field == JR))));
+}
+
+
+bool Assembler::IsJ(Instr instr) {
+ uint32_t opcode = GetOpcodeField(instr);
+ // Checks if the instruction is a jump.
+ return opcode == J;
+}
+
+
+bool Assembler::IsJal(Instr instr) {
+ return GetOpcodeField(instr) == JAL;
+}
+
+
+bool Assembler::IsJr(Instr instr) {
+ return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JR;
+}
+
+
+bool Assembler::IsJalr(Instr instr) {
+ return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JALR;
+}
+
+
+bool Assembler::IsLui(Instr instr) {
+ uint32_t opcode = GetOpcodeField(instr);
+ // Checks if the instruction is a load upper immediate.
+ return opcode == LUI;
+}
+
+
+bool Assembler::IsOri(Instr instr) {
+ uint32_t opcode = GetOpcodeField(instr);
+ // Checks if the instruction is a load upper immediate.
+ return opcode == ORI;
+}
+
+
+bool Assembler::IsNop(Instr instr, unsigned int type) {
+ // See Assembler::nop(type).
+ ASSERT(type < 32);
+ uint32_t opcode = GetOpcodeField(instr);
+ uint32_t function = GetFunctionField(instr);
+ uint32_t rt = GetRt(instr);
+ uint32_t rd = GetRd(instr);
+ uint32_t sa = GetSa(instr);
+
+ // Traditional mips nop == sll(zero_reg, zero_reg, 0)
+ // When marking non-zero type, use sll(zero_reg, at, type)
+ // to avoid use of mips ssnop and ehb special encodings
+ // of the sll instruction.
+
+ Register nop_rt_reg = (type == 0) ? zero_reg : at;
+ bool ret = (opcode == SPECIAL && function == SLL &&
+ rd == static_cast<uint32_t>(ToNumber(zero_reg)) &&
+ rt == static_cast<uint32_t>(ToNumber(nop_rt_reg)) &&
+ sa == type);
+
+ return ret;
+}
+
+
+int32_t Assembler::GetBranchOffset(Instr instr) {
+ ASSERT(IsBranch(instr));
+ return (static_cast<int16_t>(instr & kImm16Mask)) << 2;
+}
+
+
+bool Assembler::IsLw(Instr instr) {
+ return ((instr & kOpcodeMask) == LW);
+}
+
+
+int16_t Assembler::GetLwOffset(Instr instr) {
+ ASSERT(IsLw(instr));
+ return ((instr & kImm16Mask));
+}
+
+
+Instr Assembler::SetLwOffset(Instr instr, int16_t offset) {
+ ASSERT(IsLw(instr));
+
+ // We actually create a new lw instruction based on the original one.
+ Instr temp_instr = LW | (instr & kRsFieldMask) | (instr & kRtFieldMask)
+ | (offset & kImm16Mask);
+
+ return temp_instr;
+}
+
+
+bool Assembler::IsSw(Instr instr) {
+ return ((instr & kOpcodeMask) == SW);
+}
+
+
+Instr Assembler::SetSwOffset(Instr instr, int16_t offset) {
+ ASSERT(IsSw(instr));
+ return ((instr & ~kImm16Mask) | (offset & kImm16Mask));
+}
+
+
+bool Assembler::IsAddImmediate(Instr instr) {
+ return ((instr & kOpcodeMask) == ADDIU);
+}
+
+
+Instr Assembler::SetAddImmediateOffset(Instr instr, int16_t offset) {
+ ASSERT(IsAddImmediate(instr));
+ return ((instr & ~kImm16Mask) | (offset & kImm16Mask));
+}
+
+
+bool Assembler::IsAndImmediate(Instr instr) {
+ return GetOpcodeField(instr) == ANDI;
+}
+
+
+int Assembler::target_at(int32_t pos) {
+ Instr instr = instr_at(pos);
+ if ((instr & ~kImm16Mask) == 0) {
+ // Emitted label constant, not part of a branch.
+ if (instr == 0) {
+ return kEndOfChain;
+ } else {
+ int32_t imm18 =((instr & static_cast<int32_t>(kImm16Mask)) << 16) >> 14;
+ return (imm18 + pos);
+ }
+ }
+ // Check we have a branch or jump instruction.
+ ASSERT(IsBranch(instr) || IsJ(instr) || IsLui(instr));
+ // Do NOT change this to <<2. We rely on arithmetic shifts here, assuming
+ // the compiler uses arithmectic shifts for signed integers.
+ if (IsBranch(instr)) {
+ int32_t imm18 = ((instr & static_cast<int32_t>(kImm16Mask)) << 16) >> 14;
+
+ if (imm18 == kEndOfChain) {
+ // EndOfChain sentinel is returned directly, not relative to pc or pos.
+ return kEndOfChain;
+ } else {
+ return pos + kBranchPCOffset + imm18;
+ }
+ } else if (IsLui(instr)) {
+ Instr instr_lui = instr_at(pos + 0 * Assembler::kInstrSize);
+ Instr instr_ori = instr_at(pos + 1 * Assembler::kInstrSize);
+ ASSERT(IsOri(instr_ori));
+ int32_t imm = (instr_lui & static_cast<int32_t>(kImm16Mask)) << kLuiShift;
+ imm |= (instr_ori & static_cast<int32_t>(kImm16Mask));
+
+ if (imm == kEndOfJumpChain) {
+ // EndOfChain sentinel is returned directly, not relative to pc or pos.
+ return kEndOfChain;
+ } else {
+ uint32_t instr_address = reinterpret_cast<int32_t>(buffer_ + pos);
+ int32_t delta = instr_address - imm;
+ ASSERT(pos > delta);
+ return pos - delta;
+ }
+ } else {
+ int32_t imm28 = (instr & static_cast<int32_t>(kImm26Mask)) << 2;
+ if (imm28 == kEndOfJumpChain) {
+ // EndOfChain sentinel is returned directly, not relative to pc or pos.
+ return kEndOfChain;
+ } else {
+ uint32_t instr_address = reinterpret_cast<int32_t>(buffer_ + pos);
+ instr_address &= kImm28Mask;
+ int32_t delta = instr_address - imm28;
+ ASSERT(pos > delta);
+ return pos - delta;
+ }
+ }
+}
+
+
+void Assembler::target_at_put(int32_t pos, int32_t target_pos) {
+ Instr instr = instr_at(pos);
+ if ((instr & ~kImm16Mask) == 0) {
+ ASSERT(target_pos == kEndOfChain || target_pos >= 0);
+ // Emitted label constant, not part of a branch.
+ // Make label relative to Code* of generated Code object.
+ instr_at_put(pos, target_pos + (Code::kHeaderSize - kHeapObjectTag));
+ return;
+ }
+
+ ASSERT(IsBranch(instr) || IsJ(instr) || IsLui(instr));
+ if (IsBranch(instr)) {
+ int32_t imm18 = target_pos - (pos + kBranchPCOffset);
+ ASSERT((imm18 & 3) == 0);
+
+ instr &= ~kImm16Mask;
+ int32_t imm16 = imm18 >> 2;
+ ASSERT(is_int16(imm16));
+
+ instr_at_put(pos, instr | (imm16 & kImm16Mask));
+ } else if (IsLui(instr)) {
+ Instr instr_lui = instr_at(pos + 0 * Assembler::kInstrSize);
+ Instr instr_ori = instr_at(pos + 1 * Assembler::kInstrSize);
+ ASSERT(IsOri(instr_ori));
+ uint32_t imm = reinterpret_cast<uint32_t>(buffer_) + target_pos;
+ ASSERT((imm & 3) == 0);
+
+ instr_lui &= ~kImm16Mask;
+ instr_ori &= ~kImm16Mask;
+
+ instr_at_put(pos + 0 * Assembler::kInstrSize,
+ instr_lui | ((imm & kHiMask) >> kLuiShift));
+ instr_at_put(pos + 1 * Assembler::kInstrSize,
+ instr_ori | (imm & kImm16Mask));
+ } else {
+ uint32_t imm28 = reinterpret_cast<uint32_t>(buffer_) + target_pos;
+ imm28 &= kImm28Mask;
+ ASSERT((imm28 & 3) == 0);
+
+ instr &= ~kImm26Mask;
+ uint32_t imm26 = imm28 >> 2;
+ ASSERT(is_uint26(imm26));
+
+ instr_at_put(pos, instr | (imm26 & kImm26Mask));
+ }
+}
+
+
+void Assembler::print(Label* L) {
+ if (L->is_unused()) {
+ PrintF("unused label\n");
+ } else if (L->is_bound()) {
+ PrintF("bound label to %d\n", L->pos());
+ } else if (L->is_linked()) {
+ Label l = *L;
+ PrintF("unbound label");
+ while (l.is_linked()) {
+ PrintF("@ %d ", l.pos());
+ Instr instr = instr_at(l.pos());
+ if ((instr & ~kImm16Mask) == 0) {
+ PrintF("value\n");
+ } else {
+ PrintF("%d\n", instr);
+ }
+ next(&l);
+ }
+ } else {
+ PrintF("label in inconsistent state (pos = %d)\n", L->pos_);
+ }
+}
+
+
+void Assembler::bind_to(Label* L, int pos) {
+ ASSERT(0 <= pos && pos <= pc_offset()); // Must have valid binding position.
+ int32_t trampoline_pos = kInvalidSlotPos;
+ if (L->is_linked() && !trampoline_emitted_) {
+ unbound_labels_count_--;
+ next_buffer_check_ += kTrampolineSlotsSize;
+ }
+
+ while (L->is_linked()) {
+ int32_t fixup_pos = L->pos();
+ int32_t dist = pos - fixup_pos;
+ next(L); // Call next before overwriting link with target at fixup_pos.
+ Instr instr = instr_at(fixup_pos);
+ if (IsBranch(instr)) {
+ if (dist > kMaxBranchOffset) {
+ if (trampoline_pos == kInvalidSlotPos) {
+ trampoline_pos = get_trampoline_entry(fixup_pos);
+ CHECK(trampoline_pos != kInvalidSlotPos);
+ }
+ ASSERT((trampoline_pos - fixup_pos) <= kMaxBranchOffset);
+ target_at_put(fixup_pos, trampoline_pos);
+ fixup_pos = trampoline_pos;
+ dist = pos - fixup_pos;
+ }
+ target_at_put(fixup_pos, pos);
+ } else {
+ ASSERT(IsJ(instr) || IsLui(instr) || IsEmittedConstant(instr));
+ target_at_put(fixup_pos, pos);
+ }
+ }
+ L->bind_to(pos);
+
+ // Keep track of the last bound label so we don't eliminate any instructions
+ // before a bound label.
+ if (pos > last_bound_pos_)
+ last_bound_pos_ = pos;
+}
+
+
+void Assembler::bind(Label* L) {
+ ASSERT(!L->is_bound()); // Label can only be bound once.
+ bind_to(L, pc_offset());
+}
+
+
+void Assembler::next(Label* L) {
+ ASSERT(L->is_linked());
+ int link = target_at(L->pos());
+ if (link == kEndOfChain) {
+ L->Unuse();
+ } else {
+ ASSERT(link >= 0);
+ L->link_to(link);
+ }
+}
+
+
+bool Assembler::is_near(Label* L) {
+ if (L->is_bound()) {
+ return ((pc_offset() - L->pos()) < kMaxBranchOffset - 4 * kInstrSize);
+ }
+ return false;
+}
+
+
+// We have to use a temporary register for things that can be relocated even
+// if they can be encoded in the MIPS's 16 bits of immediate-offset instruction
+// space. There is no guarantee that the relocated location can be similarly
+// encoded.
+bool Assembler::MustUseReg(RelocInfo::Mode rmode) {
+ return !RelocInfo::IsNone(rmode);
+}
+
+void Assembler::GenInstrRegister(Opcode opcode,
+ Register rs,
+ Register rt,
+ Register rd,
+ uint16_t sa,
+ SecondaryField func) {
+ ASSERT(rd.is_valid() && rs.is_valid() && rt.is_valid() && is_uint5(sa));
+ Instr instr = opcode | (rs.code() << kRsShift) | (rt.code() << kRtShift)
+ | (rd.code() << kRdShift) | (sa << kSaShift) | func;
+ emit(instr);
+}
+
+
+void Assembler::GenInstrRegister(Opcode opcode,
+ Register rs,
+ Register rt,
+ uint16_t msb,
+ uint16_t lsb,
+ SecondaryField func) {
+ ASSERT(rs.is_valid() && rt.is_valid() && is_uint5(msb) && is_uint5(lsb));
+ Instr instr = opcode | (rs.code() << kRsShift) | (rt.code() << kRtShift)
+ | (msb << kRdShift) | (lsb << kSaShift) | func;
+ emit(instr);
+}
+
+
+void Assembler::GenInstrRegister(Opcode opcode,
+ SecondaryField fmt,
+ FPURegister ft,
+ FPURegister fs,
+ FPURegister fd,
+ SecondaryField func) {
+ ASSERT(fd.is_valid() && fs.is_valid() && ft.is_valid());
+ Instr instr = opcode | fmt | (ft.code() << kFtShift) | (fs.code() << kFsShift)
+ | (fd.code() << kFdShift) | func;
+ emit(instr);
+}
+
+
+void Assembler::GenInstrRegister(Opcode opcode,
+ FPURegister fr,
+ FPURegister ft,
+ FPURegister fs,
+ FPURegister fd,
+ SecondaryField func) {
+ ASSERT(fd.is_valid() && fr.is_valid() && fs.is_valid() && ft.is_valid());
+ Instr instr = opcode | (fr.code() << kFrShift) | (ft.code() << kFtShift)
+ | (fs.code() << kFsShift) | (fd.code() << kFdShift) | func;
+ emit(instr);
+}
+
+
+void Assembler::GenInstrRegister(Opcode opcode,
+ SecondaryField fmt,
+ Register rt,
+ FPURegister fs,
+ FPURegister fd,
+ SecondaryField func) {
+ ASSERT(fd.is_valid() && fs.is_valid() && rt.is_valid());
+ Instr instr = opcode | fmt | (rt.code() << kRtShift)
+ | (fs.code() << kFsShift) | (fd.code() << kFdShift) | func;
+ emit(instr);
+}
+
+
+void Assembler::GenInstrRegister(Opcode opcode,
+ SecondaryField fmt,
+ Register rt,
+ FPUControlRegister fs,
+ SecondaryField func) {
+ ASSERT(fs.is_valid() && rt.is_valid());
+ Instr instr =
+ opcode | fmt | (rt.code() << kRtShift) | (fs.code() << kFsShift) | func;
+ emit(instr);
+}
+
+
+// Instructions with immediate value.
+// Registers are in the order of the instruction encoding, from left to right.
+void Assembler::GenInstrImmediate(Opcode opcode,
+ Register rs,
+ Register rt,
+ int32_t j) {
+ ASSERT(rs.is_valid() && rt.is_valid() && (is_int16(j) || is_uint16(j)));
+ Instr instr = opcode | (rs.code() << kRsShift) | (rt.code() << kRtShift)
+ | (j & kImm16Mask);
+ emit(instr);
+}
+
+
+void Assembler::GenInstrImmediate(Opcode opcode,
+ Register rs,
+ SecondaryField SF,
+ int32_t j) {
+ ASSERT(rs.is_valid() && (is_int16(j) || is_uint16(j)));
+ Instr instr = opcode | (rs.code() << kRsShift) | SF | (j & kImm16Mask);
+ emit(instr);
+}
+
+
+void Assembler::GenInstrImmediate(Opcode opcode,
+ Register rs,
+ FPURegister ft,
+ int32_t j) {
+ ASSERT(rs.is_valid() && ft.is_valid() && (is_int16(j) || is_uint16(j)));
+ Instr instr = opcode | (rs.code() << kRsShift) | (ft.code() << kFtShift)
+ | (j & kImm16Mask);
+ emit(instr);
+}
+
+
+void Assembler::GenInstrJump(Opcode opcode,
+ uint32_t address) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ ASSERT(is_uint26(address));
+ Instr instr = opcode | address;
+ emit(instr);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+// Returns the next free trampoline entry.
+int32_t Assembler::get_trampoline_entry(int32_t pos) {
+ int32_t trampoline_entry = kInvalidSlotPos;
+
+ if (!internal_trampoline_exception_) {
+ if (trampoline_.start() > pos) {
+ trampoline_entry = trampoline_.take_slot();
+ }
+
+ if (kInvalidSlotPos == trampoline_entry) {
+ internal_trampoline_exception_ = true;
+ }
+ }
+ return trampoline_entry;
+}
+
+
+uint32_t Assembler::jump_address(Label* L) {
+ int32_t target_pos;
+
+ if (L->is_bound()) {
+ target_pos = L->pos();
+ } else {
+ if (L->is_linked()) {
+ target_pos = L->pos(); // L's link.
+ L->link_to(pc_offset());
+ } else {
+ L->link_to(pc_offset());
+ return kEndOfJumpChain;
+ }
+ }
+
+ uint32_t imm = reinterpret_cast<uint32_t>(buffer_) + target_pos;
+ ASSERT((imm & 3) == 0);
+
+ return imm;
+}
+
+
+int32_t Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
+ int32_t target_pos;
+
+ if (L->is_bound()) {
+ target_pos = L->pos();
+ } else {
+ if (L->is_linked()) {
+ target_pos = L->pos();
+ L->link_to(pc_offset());
+ } else {
+ L->link_to(pc_offset());
+ if (!trampoline_emitted_) {
+ unbound_labels_count_++;
+ next_buffer_check_ -= kTrampolineSlotsSize;
+ }
+ return kEndOfChain;
+ }
+ }
+
+ int32_t offset = target_pos - (pc_offset() + kBranchPCOffset);
+ ASSERT((offset & 3) == 0);
+ ASSERT(is_int16(offset >> 2));
+
+ return offset;
+}
+
+
+void Assembler::label_at_put(Label* L, int at_offset) {
+ int target_pos;
+ if (L->is_bound()) {
+ target_pos = L->pos();
+ instr_at_put(at_offset, target_pos + (Code::kHeaderSize - kHeapObjectTag));
+ } else {
+ if (L->is_linked()) {
+ target_pos = L->pos(); // L's link.
+ int32_t imm18 = target_pos - at_offset;
+ ASSERT((imm18 & 3) == 0);
+ int32_t imm16 = imm18 >> 2;
+ ASSERT(is_int16(imm16));
+ instr_at_put(at_offset, (imm16 & kImm16Mask));
+ } else {
+ target_pos = kEndOfChain;
+ instr_at_put(at_offset, 0);
+ if (!trampoline_emitted_) {
+ unbound_labels_count_++;
+ next_buffer_check_ -= kTrampolineSlotsSize;
+ }
+ }
+ L->link_to(at_offset);
+ }
+}
+
+
+//------- Branch and jump instructions --------
+
+void Assembler::b(int16_t offset) {
+ beq(zero_reg, zero_reg, offset);
+}
+
+
+void Assembler::bal(int16_t offset) {
+ positions_recorder()->WriteRecordedPositions();
+ bgezal(zero_reg, offset);
+}
+
+
+void Assembler::beq(Register rs, Register rt, int16_t offset) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ GenInstrImmediate(BEQ, rs, rt, offset);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::bgez(Register rs, int16_t offset) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ GenInstrImmediate(REGIMM, rs, BGEZ, offset);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::bgezal(Register rs, int16_t offset) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ positions_recorder()->WriteRecordedPositions();
+ GenInstrImmediate(REGIMM, rs, BGEZAL, offset);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::bgtz(Register rs, int16_t offset) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ GenInstrImmediate(BGTZ, rs, zero_reg, offset);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::blez(Register rs, int16_t offset) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ GenInstrImmediate(BLEZ, rs, zero_reg, offset);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::bltz(Register rs, int16_t offset) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ GenInstrImmediate(REGIMM, rs, BLTZ, offset);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::bltzal(Register rs, int16_t offset) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ positions_recorder()->WriteRecordedPositions();
+ GenInstrImmediate(REGIMM, rs, BLTZAL, offset);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::bne(Register rs, Register rt, int16_t offset) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ GenInstrImmediate(BNE, rs, rt, offset);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::j(int32_t target) {
+#if DEBUG
+ // Get pc of delay slot.
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
+ bool in_range = (ipc ^ static_cast<uint32_t>(target) >>
+ (kImm26Bits + kImmFieldShift)) == 0;
+ ASSERT(in_range && ((target & 3) == 0));
+#endif
+ GenInstrJump(J, target >> 2);
+}
+
+
+void Assembler::jr(Register rs) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ if (rs.is(ra)) {
+ positions_recorder()->WriteRecordedPositions();
+ }
+ GenInstrRegister(SPECIAL, rs, zero_reg, zero_reg, 0, JR);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::jal(int32_t target) {
+#ifdef DEBUG
+ // Get pc of delay slot.
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
+ bool in_range = (ipc ^ static_cast<uint32_t>(target) >>
+ (kImm26Bits + kImmFieldShift)) == 0;
+ ASSERT(in_range && ((target & 3) == 0));
+#endif
+ positions_recorder()->WriteRecordedPositions();
+ GenInstrJump(JAL, target >> 2);
+}
+
+
+void Assembler::jalr(Register rs, Register rd) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ positions_recorder()->WriteRecordedPositions();
+ GenInstrRegister(SPECIAL, rs, zero_reg, rd, 0, JALR);
+ BlockTrampolinePoolFor(1); // For associated delay slot.
+}
+
+
+void Assembler::j_or_jr(int32_t target, Register rs) {
+ // Get pc of delay slot.
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
+ bool in_range = (ipc ^ static_cast<uint32_t>(target) >>
+ (kImm26Bits + kImmFieldShift)) == 0;
+ if (in_range) {
+ j(target);
+ } else {
+ jr(t9);
+ }
+}
+
+
+void Assembler::jal_or_jalr(int32_t target, Register rs) {
+ // Get pc of delay slot.
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
+ bool in_range = (ipc ^ static_cast<uint32_t>(target) >>
+ (kImm26Bits+kImmFieldShift)) == 0;
+ if (in_range) {
+ jal(target);
+ } else {
+ jalr(t9);
+ }
+}
+
+
+//-------Data-processing-instructions---------
+
+// Arithmetic.
+
+void Assembler::addu(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, ADDU);
+}
+
+
+void Assembler::addiu(Register rd, Register rs, int32_t j) {
+ GenInstrImmediate(ADDIU, rs, rd, j);
+}
+
+
+void Assembler::subu(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, SUBU);
+}
+
+
+void Assembler::mul(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL2, rs, rt, rd, 0, MUL);
+}
+
+
+void Assembler::mult(Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, zero_reg, 0, MULT);
+}
+
+
+void Assembler::multu(Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, zero_reg, 0, MULTU);
+}
+
+
+void Assembler::div(Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, zero_reg, 0, DIV);
+}
+
+
+void Assembler::divu(Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, zero_reg, 0, DIVU);
+}
+
+
+// Logical.
+
+void Assembler::and_(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, AND);
+}
+
+
+void Assembler::andi(Register rt, Register rs, int32_t j) {
+ ASSERT(is_uint16(j));
+ GenInstrImmediate(ANDI, rs, rt, j);
+}
+
+
+void Assembler::or_(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, OR);
+}
+
+
+void Assembler::ori(Register rt, Register rs, int32_t j) {
+ ASSERT(is_uint16(j));
+ GenInstrImmediate(ORI, rs, rt, j);
+}
+
+
+void Assembler::xor_(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, XOR);
+}
+
+
+void Assembler::xori(Register rt, Register rs, int32_t j) {
+ ASSERT(is_uint16(j));
+ GenInstrImmediate(XORI, rs, rt, j);
+}
+
+
+void Assembler::nor(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, NOR);
+}
+
+
+// Shifts.
+void Assembler::sll(Register rd,
+ Register rt,
+ uint16_t sa,
+ bool coming_from_nop) {
+ // Don't allow nop instructions in the form sll zero_reg, zero_reg to be
+ // generated using the sll instruction. They must be generated using
+ // nop(int/NopMarkerTypes) or MarkCode(int/NopMarkerTypes) pseudo
+ // instructions.
+ ASSERT(coming_from_nop || !(rd.is(zero_reg) && rt.is(zero_reg)));
+ GenInstrRegister(SPECIAL, zero_reg, rt, rd, sa, SLL);
+}
+
+
+void Assembler::sllv(Register rd, Register rt, Register rs) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, SLLV);
+}
+
+
+void Assembler::srl(Register rd, Register rt, uint16_t sa) {
+ GenInstrRegister(SPECIAL, zero_reg, rt, rd, sa, SRL);
+}
+
+
+void Assembler::srlv(Register rd, Register rt, Register rs) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, SRLV);
+}
+
+
+void Assembler::sra(Register rd, Register rt, uint16_t sa) {
+ GenInstrRegister(SPECIAL, zero_reg, rt, rd, sa, SRA);
+}
+
+
+void Assembler::srav(Register rd, Register rt, Register rs) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, SRAV);
+}
+
+
+void Assembler::rotr(Register rd, Register rt, uint16_t sa) {
+ // Should be called via MacroAssembler::Ror.
+ ASSERT(rd.is_valid() && rt.is_valid() && is_uint5(sa));
+ ASSERT(kArchVariant == kMips32r2);
+ Instr instr = SPECIAL | (1 << kRsShift) | (rt.code() << kRtShift)
+ | (rd.code() << kRdShift) | (sa << kSaShift) | SRL;
+ emit(instr);
+}
+
+
+void Assembler::rotrv(Register rd, Register rt, Register rs) {
+ // Should be called via MacroAssembler::Ror.
+ ASSERT(rd.is_valid() && rt.is_valid() && rs.is_valid() );
+ ASSERT(kArchVariant == kMips32r2);
+ Instr instr = SPECIAL | (rs.code() << kRsShift) | (rt.code() << kRtShift)
+ | (rd.code() << kRdShift) | (1 << kSaShift) | SRLV;
+ emit(instr);
+}
+
+
+//------------Memory-instructions-------------
+
+// Helper for base-reg + offset, when offset is larger than int16.
+void Assembler::LoadRegPlusOffsetToAt(const MemOperand& src) {
+ ASSERT(!src.rm().is(at));
+ lui(at, (src.offset_ >> kLuiShift) & kImm16Mask);
+ ori(at, at, src.offset_ & kImm16Mask); // Load 32-bit offset.
+ addu(at, at, src.rm()); // Add base register.
+}
+
+
+void Assembler::lb(Register rd, const MemOperand& rs) {
+ if (is_int16(rs.offset_)) {
+ GenInstrImmediate(LB, rs.rm(), rd, rs.offset_);
+ } else { // Offset > 16 bits, use multiple instructions to load.
+ LoadRegPlusOffsetToAt(rs);
+ GenInstrImmediate(LB, at, rd, 0); // Equiv to lb(rd, MemOperand(at, 0));
+ }
+}
+
+
+void Assembler::lbu(Register rd, const MemOperand& rs) {
+ if (is_int16(rs.offset_)) {
+ GenInstrImmediate(LBU, rs.rm(), rd, rs.offset_);
+ } else { // Offset > 16 bits, use multiple instructions to load.
+ LoadRegPlusOffsetToAt(rs);
+ GenInstrImmediate(LBU, at, rd, 0); // Equiv to lbu(rd, MemOperand(at, 0));
+ }
+}
+
+
+void Assembler::lh(Register rd, const MemOperand& rs) {
+ if (is_int16(rs.offset_)) {
+ GenInstrImmediate(LH, rs.rm(), rd, rs.offset_);
+ } else { // Offset > 16 bits, use multiple instructions to load.
+ LoadRegPlusOffsetToAt(rs);
+ GenInstrImmediate(LH, at, rd, 0); // Equiv to lh(rd, MemOperand(at, 0));
+ }
+}
+
+
+void Assembler::lhu(Register rd, const MemOperand& rs) {
+ if (is_int16(rs.offset_)) {
+ GenInstrImmediate(LHU, rs.rm(), rd, rs.offset_);
+ } else { // Offset > 16 bits, use multiple instructions to load.
+ LoadRegPlusOffsetToAt(rs);
+ GenInstrImmediate(LHU, at, rd, 0); // Equiv to lhu(rd, MemOperand(at, 0));
+ }
+}
+
+
+void Assembler::lw(Register rd, const MemOperand& rs) {
+ if (is_int16(rs.offset_)) {
+ GenInstrImmediate(LW, rs.rm(), rd, rs.offset_);
+ } else { // Offset > 16 bits, use multiple instructions to load.
+ LoadRegPlusOffsetToAt(rs);
+ GenInstrImmediate(LW, at, rd, 0); // Equiv to lw(rd, MemOperand(at, 0));
+ }
+}
+
+
+void Assembler::lwl(Register rd, const MemOperand& rs) {
+ GenInstrImmediate(LWL, rs.rm(), rd, rs.offset_);
+}
+
+
+void Assembler::lwr(Register rd, const MemOperand& rs) {
+ GenInstrImmediate(LWR, rs.rm(), rd, rs.offset_);
+}
+
+
+void Assembler::sb(Register rd, const MemOperand& rs) {
+ if (is_int16(rs.offset_)) {
+ GenInstrImmediate(SB, rs.rm(), rd, rs.offset_);
+ } else { // Offset > 16 bits, use multiple instructions to store.
+ LoadRegPlusOffsetToAt(rs);
+ GenInstrImmediate(SB, at, rd, 0); // Equiv to sb(rd, MemOperand(at, 0));
+ }
+}
+
+
+void Assembler::sh(Register rd, const MemOperand& rs) {
+ if (is_int16(rs.offset_)) {
+ GenInstrImmediate(SH, rs.rm(), rd, rs.offset_);
+ } else { // Offset > 16 bits, use multiple instructions to store.
+ LoadRegPlusOffsetToAt(rs);
+ GenInstrImmediate(SH, at, rd, 0); // Equiv to sh(rd, MemOperand(at, 0));
+ }
+}
+
+
+void Assembler::sw(Register rd, const MemOperand& rs) {
+ if (is_int16(rs.offset_)) {
+ GenInstrImmediate(SW, rs.rm(), rd, rs.offset_);
+ } else { // Offset > 16 bits, use multiple instructions to store.
+ LoadRegPlusOffsetToAt(rs);
+ GenInstrImmediate(SW, at, rd, 0); // Equiv to sw(rd, MemOperand(at, 0));
+ }
+}
+
+
+void Assembler::swl(Register rd, const MemOperand& rs) {
+ GenInstrImmediate(SWL, rs.rm(), rd, rs.offset_);
+}
+
+
+void Assembler::swr(Register rd, const MemOperand& rs) {
+ GenInstrImmediate(SWR, rs.rm(), rd, rs.offset_);
+}
+
+
+void Assembler::lui(Register rd, int32_t j) {
+ ASSERT(is_uint16(j));
+ GenInstrImmediate(LUI, zero_reg, rd, j);
+}
+
+
+//-------------Misc-instructions--------------
+
+// Break / Trap instructions.
+void Assembler::break_(uint32_t code, bool break_as_stop) {
+ ASSERT((code & ~0xfffff) == 0);
+ // We need to invalidate breaks that could be stops as well because the
+ // simulator expects a char pointer after the stop instruction.
+ // See constants-mips.h for explanation.
+ ASSERT((break_as_stop &&
+ code <= kMaxStopCode &&
+ code > kMaxWatchpointCode) ||
+ (!break_as_stop &&
+ (code > kMaxStopCode ||
+ code <= kMaxWatchpointCode)));
+ Instr break_instr = SPECIAL | BREAK | (code << 6);
+ emit(break_instr);
+}
+
+
+void Assembler::stop(const char* msg, uint32_t code) {
+ ASSERT(code > kMaxWatchpointCode);
+ ASSERT(code <= kMaxStopCode);
+#if V8_HOST_ARCH_MIPS
+ break_(0x54321);
+#else // V8_HOST_ARCH_MIPS
+ BlockTrampolinePoolFor(2);
+ // The Simulator will handle the stop instruction and get the message address.
+ // On MIPS stop() is just a special kind of break_().
+ break_(code, true);
+ emit(reinterpret_cast<Instr>(msg));
+#endif
+}
+
+
+void Assembler::tge(Register rs, Register rt, uint16_t code) {
+ ASSERT(is_uint10(code));
+ Instr instr = SPECIAL | TGE | rs.code() << kRsShift
+ | rt.code() << kRtShift | code << 6;
+ emit(instr);
+}
+
+
+void Assembler::tgeu(Register rs, Register rt, uint16_t code) {
+ ASSERT(is_uint10(code));
+ Instr instr = SPECIAL | TGEU | rs.code() << kRsShift
+ | rt.code() << kRtShift | code << 6;
+ emit(instr);
+}
+
+
+void Assembler::tlt(Register rs, Register rt, uint16_t code) {
+ ASSERT(is_uint10(code));
+ Instr instr =
+ SPECIAL | TLT | rs.code() << kRsShift | rt.code() << kRtShift | code << 6;
+ emit(instr);
+}
+
+
+void Assembler::tltu(Register rs, Register rt, uint16_t code) {
+ ASSERT(is_uint10(code));
+ Instr instr =
+ SPECIAL | TLTU | rs.code() << kRsShift
+ | rt.code() << kRtShift | code << 6;
+ emit(instr);
+}
+
+
+void Assembler::teq(Register rs, Register rt, uint16_t code) {
+ ASSERT(is_uint10(code));
+ Instr instr =
+ SPECIAL | TEQ | rs.code() << kRsShift | rt.code() << kRtShift | code << 6;
+ emit(instr);
+}
+
+
+void Assembler::tne(Register rs, Register rt, uint16_t code) {
+ ASSERT(is_uint10(code));
+ Instr instr =
+ SPECIAL | TNE | rs.code() << kRsShift | rt.code() << kRtShift | code << 6;
+ emit(instr);
+}
+
+
+// Move from HI/LO register.
+
+void Assembler::mfhi(Register rd) {
+ GenInstrRegister(SPECIAL, zero_reg, zero_reg, rd, 0, MFHI);
+}
+
+
+void Assembler::mflo(Register rd) {
+ GenInstrRegister(SPECIAL, zero_reg, zero_reg, rd, 0, MFLO);
+}
+
+
+// Set on less than instructions.
+void Assembler::slt(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, SLT);
+}
+
+
+void Assembler::sltu(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, SLTU);
+}
+
+
+void Assembler::slti(Register rt, Register rs, int32_t j) {
+ GenInstrImmediate(SLTI, rs, rt, j);
+}
+
+
+void Assembler::sltiu(Register rt, Register rs, int32_t j) {
+ GenInstrImmediate(SLTIU, rs, rt, j);
+}
+
+
+// Conditional move.
+void Assembler::movz(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, MOVZ);
+}
+
+
+void Assembler::movn(Register rd, Register rs, Register rt) {
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, MOVN);
+}
+
+
+void Assembler::movt(Register rd, Register rs, uint16_t cc) {
+ Register rt;
+ rt.code_ = (cc & 0x0007) << 2 | 1;
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, MOVCI);
+}
+
+
+void Assembler::movf(Register rd, Register rs, uint16_t cc) {
+ Register rt;
+ rt.code_ = (cc & 0x0007) << 2 | 0;
+ GenInstrRegister(SPECIAL, rs, rt, rd, 0, MOVCI);
+}
+
+
+// Bit twiddling.
+void Assembler::clz(Register rd, Register rs) {
+ // Clz instr requires same GPR number in 'rd' and 'rt' fields.
+ GenInstrRegister(SPECIAL2, rs, rd, rd, 0, CLZ);
+}
+
+
+void Assembler::ins_(Register rt, Register rs, uint16_t pos, uint16_t size) {
+ // Should be called via MacroAssembler::Ins.
+ // Ins instr has 'rt' field as dest, and two uint5: msb, lsb.
+ ASSERT(kArchVariant == kMips32r2);
+ GenInstrRegister(SPECIAL3, rs, rt, pos + size - 1, pos, INS);
+}
+
+
+void Assembler::ext_(Register rt, Register rs, uint16_t pos, uint16_t size) {
+ // Should be called via MacroAssembler::Ext.
+ // Ext instr has 'rt' field as dest, and two uint5: msb, lsb.
+ ASSERT(kArchVariant == kMips32r2);
+ GenInstrRegister(SPECIAL3, rs, rt, size - 1, pos, EXT);
+}
+
+
+//--------Coprocessor-instructions----------------
+
+// Load, store, move.
+void Assembler::lwc1(FPURegister fd, const MemOperand& src) {
+ GenInstrImmediate(LWC1, src.rm(), fd, src.offset_);
+}
+
+
+void Assembler::ldc1(FPURegister fd, const MemOperand& src) {
+ // Workaround for non-8-byte alignment of HeapNumber, convert 64-bit
+ // load to two 32-bit loads.
+ GenInstrImmediate(LWC1, src.rm(), fd, src.offset_);
+ FPURegister nextfpreg;
+ nextfpreg.setcode(fd.code() + 1);
+ GenInstrImmediate(LWC1, src.rm(), nextfpreg, src.offset_ + 4);
+}
+
+
+void Assembler::swc1(FPURegister fd, const MemOperand& src) {
+ GenInstrImmediate(SWC1, src.rm(), fd, src.offset_);
+}
+
+
+void Assembler::sdc1(FPURegister fd, const MemOperand& src) {
+ // Workaround for non-8-byte alignment of HeapNumber, convert 64-bit
+ // store to two 32-bit stores.
+ GenInstrImmediate(SWC1, src.rm(), fd, src.offset_);
+ FPURegister nextfpreg;
+ nextfpreg.setcode(fd.code() + 1);
+ GenInstrImmediate(SWC1, src.rm(), nextfpreg, src.offset_ + 4);
+}
+
+
+void Assembler::mtc1(Register rt, FPURegister fs) {
+ GenInstrRegister(COP1, MTC1, rt, fs, f0);
+}
+
+
+void Assembler::mfc1(Register rt, FPURegister fs) {
+ GenInstrRegister(COP1, MFC1, rt, fs, f0);
+}
+
+
+void Assembler::ctc1(Register rt, FPUControlRegister fs) {
+ GenInstrRegister(COP1, CTC1, rt, fs);
+}
+
+
+void Assembler::cfc1(Register rt, FPUControlRegister fs) {
+ GenInstrRegister(COP1, CFC1, rt, fs);
+}
+
+
+void Assembler::DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi) {
+ uint64_t i;
+ OS::MemCopy(&i, &d, 8);
+
+ *lo = i & 0xffffffff;
+ *hi = i >> 32;
+}
+
+
+// Arithmetic.
+
+void Assembler::add_d(FPURegister fd, FPURegister fs, FPURegister ft) {
+ GenInstrRegister(COP1, D, ft, fs, fd, ADD_D);
+}
+
+
+void Assembler::sub_d(FPURegister fd, FPURegister fs, FPURegister ft) {
+ GenInstrRegister(COP1, D, ft, fs, fd, SUB_D);
+}
+
+
+void Assembler::mul_d(FPURegister fd, FPURegister fs, FPURegister ft) {
+ GenInstrRegister(COP1, D, ft, fs, fd, MUL_D);
+}
+
+
+void Assembler::madd_d(FPURegister fd, FPURegister fr, FPURegister fs,
+ FPURegister ft) {
+ GenInstrRegister(COP1X, fr, ft, fs, fd, MADD_D);
+}
+
+
+void Assembler::div_d(FPURegister fd, FPURegister fs, FPURegister ft) {
+ GenInstrRegister(COP1, D, ft, fs, fd, DIV_D);
+}
+
+
+void Assembler::abs_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, ABS_D);
+}
+
+
+void Assembler::mov_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, MOV_D);
+}
+
+
+void Assembler::neg_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, NEG_D);
+}
+
+
+void Assembler::sqrt_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, SQRT_D);
+}
+
+
+// Conversions.
+
+void Assembler::cvt_w_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, CVT_W_S);
+}
+
+
+void Assembler::cvt_w_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, CVT_W_D);
+}
+
+
+void Assembler::trunc_w_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, TRUNC_W_S);
+}
+
+
+void Assembler::trunc_w_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, TRUNC_W_D);
+}
+
+
+void Assembler::round_w_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, ROUND_W_S);
+}
+
+
+void Assembler::round_w_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, ROUND_W_D);
+}
+
+
+void Assembler::floor_w_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, FLOOR_W_S);
+}
+
+
+void Assembler::floor_w_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, FLOOR_W_D);
+}
+
+
+void Assembler::ceil_w_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, CEIL_W_S);
+}
+
+
+void Assembler::ceil_w_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, CEIL_W_D);
+}
+
+
+void Assembler::cvt_l_s(FPURegister fd, FPURegister fs) {
+ ASSERT(kArchVariant == kMips32r2);
+ GenInstrRegister(COP1, S, f0, fs, fd, CVT_L_S);
+}
+
+
+void Assembler::cvt_l_d(FPURegister fd, FPURegister fs) {
+ ASSERT(kArchVariant == kMips32r2);
+ GenInstrRegister(COP1, D, f0, fs, fd, CVT_L_D);
+}
+
+
+void Assembler::trunc_l_s(FPURegister fd, FPURegister fs) {
+ ASSERT(kArchVariant == kMips32r2);
+ GenInstrRegister(COP1, S, f0, fs, fd, TRUNC_L_S);
+}
+
+
+void Assembler::trunc_l_d(FPURegister fd, FPURegister fs) {
+ ASSERT(kArchVariant == kMips32r2);
+ GenInstrRegister(COP1, D, f0, fs, fd, TRUNC_L_D);
+}
+
+
+void Assembler::round_l_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, ROUND_L_S);
+}
+
+
+void Assembler::round_l_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, ROUND_L_D);
+}
+
+
+void Assembler::floor_l_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, FLOOR_L_S);
+}
+
+
+void Assembler::floor_l_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, FLOOR_L_D);
+}
+
+
+void Assembler::ceil_l_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, CEIL_L_S);
+}
+
+
+void Assembler::ceil_l_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, CEIL_L_D);
+}
+
+
+void Assembler::cvt_s_w(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, W, f0, fs, fd, CVT_S_W);
+}
+
+
+void Assembler::cvt_s_l(FPURegister fd, FPURegister fs) {
+ ASSERT(kArchVariant == kMips32r2);
+ GenInstrRegister(COP1, L, f0, fs, fd, CVT_S_L);
+}
+
+
+void Assembler::cvt_s_d(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, D, f0, fs, fd, CVT_S_D);
+}
+
+
+void Assembler::cvt_d_w(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, W, f0, fs, fd, CVT_D_W);
+}
+
+
+void Assembler::cvt_d_l(FPURegister fd, FPURegister fs) {
+ ASSERT(kArchVariant == kMips32r2);
+ GenInstrRegister(COP1, L, f0, fs, fd, CVT_D_L);
+}
+
+
+void Assembler::cvt_d_s(FPURegister fd, FPURegister fs) {
+ GenInstrRegister(COP1, S, f0, fs, fd, CVT_D_S);
+}
+
+
+// Conditions.
+void Assembler::c(FPUCondition cond, SecondaryField fmt,
+ FPURegister fs, FPURegister ft, uint16_t cc) {
+ ASSERT(is_uint3(cc));
+ ASSERT((fmt & ~(31 << kRsShift)) == 0);
+ Instr instr = COP1 | fmt | ft.code() << 16 | fs.code() << kFsShift
+ | cc << 8 | 3 << 4 | cond;
+ emit(instr);
+}
+
+
+void Assembler::fcmp(FPURegister src1, const double src2,
+ FPUCondition cond) {
+ ASSERT(src2 == 0.0);
+ mtc1(zero_reg, f14);
+ cvt_d_w(f14, f14);
+ c(cond, D, src1, f14, 0);
+}
+
+
+void Assembler::bc1f(int16_t offset, uint16_t cc) {
+ ASSERT(is_uint3(cc));
+ Instr instr = COP1 | BC1 | cc << 18 | 0 << 16 | (offset & kImm16Mask);
+ emit(instr);
+}
+
+
+void Assembler::bc1t(int16_t offset, uint16_t cc) {
+ ASSERT(is_uint3(cc));
+ Instr instr = COP1 | BC1 | cc << 18 | 1 << 16 | (offset & kImm16Mask);
+ emit(instr);
+}
+
+
+// Debugging.
+void Assembler::RecordJSReturn() {
+ positions_recorder()->WriteRecordedPositions();
+ CheckBuffer();
+ RecordRelocInfo(RelocInfo::JS_RETURN);
+}
+
+
+void Assembler::RecordDebugBreakSlot() {
+ positions_recorder()->WriteRecordedPositions();
+ CheckBuffer();
+ RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
+void Assembler::RecordComment(const char* msg) {
+ if (FLAG_code_comments) {
+ CheckBuffer();
+ RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg));
+ }
+}
+
+
+int Assembler::RelocateInternalReference(byte* pc, intptr_t pc_delta) {
+ Instr instr = instr_at(pc);
+ ASSERT(IsJ(instr) || IsLui(instr));
+ if (IsLui(instr)) {
+ Instr instr_lui = instr_at(pc + 0 * Assembler::kInstrSize);
+ Instr instr_ori = instr_at(pc + 1 * Assembler::kInstrSize);
+ ASSERT(IsOri(instr_ori));
+ int32_t imm = (instr_lui & static_cast<int32_t>(kImm16Mask)) << kLuiShift;
+ imm |= (instr_ori & static_cast<int32_t>(kImm16Mask));
+ if (imm == kEndOfJumpChain) {
+ return 0; // Number of instructions patched.
+ }
+ imm += pc_delta;
+ ASSERT((imm & 3) == 0);
+
+ instr_lui &= ~kImm16Mask;
+ instr_ori &= ~kImm16Mask;
+
+ instr_at_put(pc + 0 * Assembler::kInstrSize,
+ instr_lui | ((imm >> kLuiShift) & kImm16Mask));
+ instr_at_put(pc + 1 * Assembler::kInstrSize,
+ instr_ori | (imm & kImm16Mask));
+ return 2; // Number of instructions patched.
+ } else {
+ uint32_t imm28 = (instr & static_cast<int32_t>(kImm26Mask)) << 2;
+ if (static_cast<int32_t>(imm28) == kEndOfJumpChain) {
+ return 0; // Number of instructions patched.
+ }
+ imm28 += pc_delta;
+ imm28 &= kImm28Mask;
+ ASSERT((imm28 & 3) == 0);
+
+ instr &= ~kImm26Mask;
+ uint32_t imm26 = imm28 >> 2;
+ ASSERT(is_uint26(imm26));
+
+ instr_at_put(pc, instr | (imm26 & kImm26Mask));
+ return 1; // Number of instructions patched.
+ }
+}
+
+
+void Assembler::GrowBuffer() {
+ if (!own_buffer_) FATAL("external code buffer is too small");
+
+ // Compute new buffer size.
+ CodeDesc desc; // The new buffer.
+ if (buffer_size_ < 4*KB) {
+ desc.buffer_size = 4*KB;
+ } else if (buffer_size_ < 1*MB) {
+ desc.buffer_size = 2*buffer_size_;
+ } else {
+ desc.buffer_size = buffer_size_ + 1*MB;
+ }
+ CHECK_GT(desc.buffer_size, 0); // No overflow.
+
+ // 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();
+
+ // Copy the data.
+ int pc_delta = desc.buffer - buffer_;
+ int rc_delta = (desc.buffer + desc.buffer_size) - (buffer_ + buffer_size_);
+ OS::MemMove(desc.buffer, buffer_, desc.instr_size);
+ OS::MemMove(reloc_info_writer.pos() + rc_delta,
+ reloc_info_writer.pos(), desc.reloc_size);
+
+ // Switch buffers.
+ DeleteArray(buffer_);
+ buffer_ = desc.buffer;
+ buffer_size_ = desc.buffer_size;
+ pc_ += pc_delta;
+ reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
+ reloc_info_writer.last_pc() + pc_delta);
+
+ // Relocate runtime entries.
+ for (RelocIterator it(desc); !it.done(); it.next()) {
+ RelocInfo::Mode rmode = it.rinfo()->rmode();
+ if (rmode == RelocInfo::INTERNAL_REFERENCE) {
+ byte* p = reinterpret_cast<byte*>(it.rinfo()->pc());
+ RelocateInternalReference(p, pc_delta);
+ }
+ }
+
+ ASSERT(!overflow());
+}
+
+
+void Assembler::db(uint8_t data) {
+ CheckBuffer();
+ *reinterpret_cast<uint8_t*>(pc_) = data;
+ pc_ += sizeof(uint8_t);
+}
+
+
+void Assembler::dd(uint32_t data) {
+ CheckBuffer();
+ *reinterpret_cast<uint32_t*>(pc_) = data;
+ pc_ += sizeof(uint32_t);
+}
+
+
+void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
+ // 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)
+ || RelocInfo::IsJSReturn(rmode)
+ || RelocInfo::IsComment(rmode)
+ || RelocInfo::IsPosition(rmode));
+ // These modes do not need an entry in the constant pool.
+ }
+ if (!RelocInfo::IsNone(rinfo.rmode())) {
+ // Don't record external references unless the heap will be serialized.
+ if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
+#ifdef DEBUG
+ if (!Serializer::enabled()) {
+ Serializer::TooLateToEnableNow();
+ }
+#endif
+ if (!Serializer::enabled() && !emit_debug_code()) {
+ return;
+ }
+ }
+ 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().ToInt(),
+ NULL);
+ ClearRecordedAstId();
+ reloc_info_writer.Write(&reloc_info_with_ast_id);
+ } else {
+ reloc_info_writer.Write(&rinfo);
+ }
+ }
+}
+
+
+void Assembler::BlockTrampolinePoolFor(int instructions) {
+ BlockTrampolinePoolBefore(pc_offset() + instructions * kInstrSize);
+}
+
+
+void Assembler::CheckTrampolinePool() {
+ // Some small sequences of instructions must not be broken up by the
+ // insertion of a trampoline pool; such sequences are protected by setting
+ // either trampoline_pool_blocked_nesting_ or no_trampoline_pool_before_,
+ // which are both checked here. Also, recursive calls to CheckTrampolinePool
+ // are blocked by trampoline_pool_blocked_nesting_.
+ if ((trampoline_pool_blocked_nesting_ > 0) ||
+ (pc_offset() < no_trampoline_pool_before_)) {
+ // Emission is currently blocked; make sure we try again as soon as
+ // possible.
+ if (trampoline_pool_blocked_nesting_ > 0) {
+ next_buffer_check_ = pc_offset() + kInstrSize;
+ } else {
+ next_buffer_check_ = no_trampoline_pool_before_;
+ }
+ return;
+ }
+
+ ASSERT(!trampoline_emitted_);
+ ASSERT(unbound_labels_count_ >= 0);
+ if (unbound_labels_count_ > 0) {
+ // First we emit jump (2 instructions), then we emit trampoline pool.
+ { BlockTrampolinePoolScope block_trampoline_pool(this);
+ Label after_pool;
+ b(&after_pool);
+ nop();
+
+ int pool_start = pc_offset();
+ for (int i = 0; i < unbound_labels_count_; i++) {
+ uint32_t imm32;
+ imm32 = jump_address(&after_pool);
+ { BlockGrowBufferScope block_buf_growth(this);
+ // Buffer growth (and relocation) must be blocked for internal
+ // references until associated instructions are emitted and available
+ // to be patched.
+ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE);
+ lui(at, (imm32 & kHiMask) >> kLuiShift);
+ ori(at, at, (imm32 & kImm16Mask));
+ }
+ jr(at);
+ nop();
+ }
+ bind(&after_pool);
+ trampoline_ = Trampoline(pool_start, unbound_labels_count_);
+
+ trampoline_emitted_ = true;
+ // As we are only going to emit trampoline once, we need to prevent any
+ // further emission.
+ next_buffer_check_ = kMaxInt;
+ }
+ } else {
+ // Number of branches to unbound label at this point is zero, so we can
+ // move next buffer check to maximum.
+ next_buffer_check_ = pc_offset() +
+ kMaxBranchOffset - kTrampolineSlotsSize * 16;
+ }
+ return;
+}
+
+
+Address Assembler::target_address_at(Address pc) {
+ Instr instr1 = instr_at(pc);
+ Instr instr2 = instr_at(pc + kInstrSize);
+ // Interpret 2 instructions generated by li: lui/ori
+ if ((GetOpcodeField(instr1) == LUI) && (GetOpcodeField(instr2) == ORI)) {
+ // Assemble the 32 bit value.
+ return reinterpret_cast<Address>(
+ (GetImmediate16(instr1) << 16) | GetImmediate16(instr2));
+ }
+
+ // We should never get here, force a bad address if we do.
+ UNREACHABLE();
+ return (Address)0x0;
+}
+
+
+// MIPS and ia32 use opposite encoding for qNaN and sNaN, such that ia32
+// qNaN is a MIPS sNaN, and ia32 sNaN is MIPS qNaN. If running from a heap
+// snapshot generated on ia32, the resulting MIPS sNaN must be quieted.
+// OS::nan_value() returns a qNaN.
+void Assembler::QuietNaN(HeapObject* object) {
+ HeapNumber::cast(object)->set_value(OS::nan_value());
+}
+
+
+// On Mips, a target address is stored in a lui/ori instruction pair, each
+// of which load 16 bits of the 32-bit address to a register.
+// Patching the address must replace both instr, and flush the i-cache.
+//
+// There is an optimization below, which emits a nop when the address
+// fits in just 16 bits. This is unlikely to help, and should be benchmarked,
+// and possibly removed.
+void Assembler::set_target_address_at(Address pc, Address target) {
+ Instr instr2 = instr_at(pc + kInstrSize);
+ uint32_t rt_code = GetRtField(instr2);
+ uint32_t* p = reinterpret_cast<uint32_t*>(pc);
+ uint32_t itarget = reinterpret_cast<uint32_t>(target);
+
+#ifdef DEBUG
+ // Check we have the result from a li macro-instruction, using instr pair.
+ Instr instr1 = instr_at(pc);
+ CHECK((GetOpcodeField(instr1) == LUI && GetOpcodeField(instr2) == ORI));
+#endif
+
+ // Must use 2 instructions to insure patchable code => just use lui and ori.
+ // lui rt, upper-16.
+ // ori rt rt, lower-16.
+ *p = LUI | rt_code | ((itarget & kHiMask) >> kLuiShift);
+ *(p+1) = ORI | rt_code | (rt_code << 5) | (itarget & kImm16Mask);
+
+ // The following code is an optimization for the common case of Call()
+ // or Jump() which is load to register, and jump through register:
+ // li(t9, address); jalr(t9) (or jr(t9)).
+ // If the destination address is in the same 256 MB page as the call, it
+ // is faster to do a direct jal, or j, rather than jump thru register, since
+ // that lets the cpu pipeline prefetch the target address. However each
+ // time the address above is patched, we have to patch the direct jal/j
+ // instruction, as well as possibly revert to jalr/jr if we now cross a
+ // 256 MB page. Note that with the jal/j instructions, we do not need to
+ // load the register, but that code is left, since it makes it easy to
+ // revert this process. A further optimization could try replacing the
+ // li sequence with nops.
+ // This optimization can only be applied if the rt-code from instr2 is the
+ // register used for the jalr/jr. Finally, we have to skip 'jr ra', which is
+ // mips return. Occasionally this lands after an li().
+
+ Instr instr3 = instr_at(pc + 2 * kInstrSize);
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc + 3 * kInstrSize);
+ bool in_range = (ipc ^ static_cast<uint32_t>(itarget) >>
+ (kImm26Bits + kImmFieldShift)) == 0;
+ uint32_t target_field =
+ static_cast<uint32_t>(itarget & kJumpAddrMask) >> kImmFieldShift;
+ bool patched_jump = false;
+
+#ifndef ALLOW_JAL_IN_BOUNDARY_REGION
+ // This is a workaround to the 24k core E156 bug (affect some 34k cores also).
+ // Since the excluded space is only 64KB out of 256MB (0.02 %), we will just
+ // apply this workaround for all cores so we don't have to identify the core.
+ if (in_range) {
+ // The 24k core E156 bug has some very specific requirements, we only check
+ // the most simple one: if the address of the delay slot instruction is in
+ // the first or last 32 KB of the 256 MB segment.
+ uint32_t segment_mask = ((256 * MB) - 1) ^ ((32 * KB) - 1);
+ uint32_t ipc_segment_addr = ipc & segment_mask;
+ if (ipc_segment_addr == 0 || ipc_segment_addr == segment_mask)
+ in_range = false;
+ }
+#endif
+
+ if (IsJalr(instr3)) {
+ // Try to convert JALR to JAL.
+ if (in_range && GetRt(instr2) == GetRs(instr3)) {
+ *(p+2) = JAL | target_field;
+ patched_jump = true;
+ }
+ } else if (IsJr(instr3)) {
+ // Try to convert JR to J, skip returns (jr ra).
+ bool is_ret = static_cast<int>(GetRs(instr3)) == ra.code();
+ if (in_range && !is_ret && GetRt(instr2) == GetRs(instr3)) {
+ *(p+2) = J | target_field;
+ patched_jump = true;
+ }
+ } else if (IsJal(instr3)) {
+ if (in_range) {
+ // We are patching an already converted JAL.
+ *(p+2) = JAL | target_field;
+ } else {
+ // Patch JAL, but out of range, revert to JALR.
+ // JALR rs reg is the rt reg specified in the ORI instruction.
+ uint32_t rs_field = GetRt(instr2) << kRsShift;
+ uint32_t rd_field = ra.code() << kRdShift; // Return-address (ra) reg.
+ *(p+2) = SPECIAL | rs_field | rd_field | JALR;
+ }
+ patched_jump = true;
+ } else if (IsJ(instr3)) {
+ if (in_range) {
+ // We are patching an already converted J (jump).
+ *(p+2) = J | target_field;
+ } else {
+ // Trying patch J, but out of range, just go back to JR.
+ // JR 'rs' reg is the 'rt' reg specified in the ORI instruction (instr2).
+ uint32_t rs_field = GetRt(instr2) << kRsShift;
+ *(p+2) = SPECIAL | rs_field | JR;
+ }
+ patched_jump = true;
+ }
+
+ CPU::FlushICache(pc, (patched_jump ? 3 : 2) * sizeof(int32_t));
+}
+
+
+void Assembler::JumpLabelToJumpRegister(Address pc) {
+ // Address pc points to lui/ori instructions.
+ // Jump to label may follow at pc + 2 * kInstrSize.
+ uint32_t* p = reinterpret_cast<uint32_t*>(pc);
+#ifdef DEBUG
+ Instr instr1 = instr_at(pc);
+#endif
+ Instr instr2 = instr_at(pc + 1 * kInstrSize);
+ Instr instr3 = instr_at(pc + 2 * kInstrSize);
+ bool patched = false;
+
+ if (IsJal(instr3)) {
+ ASSERT(GetOpcodeField(instr1) == LUI);
+ ASSERT(GetOpcodeField(instr2) == ORI);
+
+ uint32_t rs_field = GetRt(instr2) << kRsShift;
+ uint32_t rd_field = ra.code() << kRdShift; // Return-address (ra) reg.
+ *(p+2) = SPECIAL | rs_field | rd_field | JALR;
+ patched = true;
+ } else if (IsJ(instr3)) {
+ ASSERT(GetOpcodeField(instr1) == LUI);
+ ASSERT(GetOpcodeField(instr2) == ORI);
+
+ uint32_t rs_field = GetRt(instr2) << kRsShift;
+ *(p+2) = SPECIAL | rs_field | JR;
+ patched = true;
+ }
+
+ if (patched) {
+ CPU::FlushICache(pc+2, sizeof(Address));
+ }
+}
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/assembler-mips.h b/chromium/v8/src/mips/assembler-mips.h
new file mode 100644
index 00000000000..cb0896a8ded
--- /dev/null
+++ b/chromium/v8/src/mips/assembler-mips.h
@@ -0,0 +1,1219 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+
+#ifndef V8_MIPS_ASSEMBLER_MIPS_H_
+#define V8_MIPS_ASSEMBLER_MIPS_H_
+
+#include <stdio.h>
+#include "assembler.h"
+#include "constants-mips.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+// CPU Registers.
+//
+// 1) We would prefer to use an enum, but enum values are assignment-
+// compatible with int, which has caused code-generation bugs.
+//
+// 2) We would prefer to use a class instead of a struct but we don't like
+// the register initialization to depend on the particular initialization
+// order (which appears to be different on OS X, Linux, and Windows for the
+// installed versions of C++ we tried). Using a struct permits C-style
+// "initialization". Also, the Register objects cannot be const as this
+// forces initialization stubs in MSVC, making us dependent on initialization
+// order.
+//
+// 3) By not using an enum, we are possibly preventing the compiler from
+// doing certain constant folds, which may significantly reduce the
+// code generated for some assembly instructions (because they boil down
+// to a few constants). If this is a problem, we could change the code
+// such that we use an enum in optimized mode, and the struct in debug
+// mode. This way we get the compile-time error checking in debug mode
+// and best performance in optimized code.
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Register and FPURegister.
+
+// Core register.
+struct Register {
+ static const int kNumRegisters = v8::internal::kNumRegisters;
+ static const int kMaxNumAllocatableRegisters = 14; // v0 through t7.
+ static const int kSizeInBytes = 4;
+
+ inline static int NumAllocatableRegisters();
+
+ static int ToAllocationIndex(Register reg) {
+ return reg.code() - 2; // zero_reg and 'at' are skipped.
+ }
+
+ static Register FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ return from_code(index + 2); // zero_reg and 'at' are skipped.
+ }
+
+ static const char* AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ const char* const names[] = {
+ "v0",
+ "v1",
+ "a0",
+ "a1",
+ "a2",
+ "a3",
+ "t0",
+ "t1",
+ "t2",
+ "t3",
+ "t4",
+ "t5",
+ "t6",
+ "t7",
+ };
+ return names[index];
+ }
+
+ static Register from_code(int code) {
+ Register r = { code };
+ return r;
+ }
+
+ bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; }
+ bool is(Register reg) const { return code_ == reg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ ASSERT(is_valid());
+ return 1 << code_;
+ }
+
+ // Unfortunately we can't make this private in a struct.
+ int code_;
+};
+
+#define REGISTER(N, C) \
+ const int kRegister_ ## N ## _Code = C; \
+ const Register N = { C }
+
+REGISTER(no_reg, -1);
+// Always zero.
+REGISTER(zero_reg, 0);
+// at: Reserved for synthetic instructions.
+REGISTER(at, 1);
+// v0, v1: Used when returning multiple values from subroutines.
+REGISTER(v0, 2);
+REGISTER(v1, 3);
+// a0 - a4: Used to pass non-FP parameters.
+REGISTER(a0, 4);
+REGISTER(a1, 5);
+REGISTER(a2, 6);
+REGISTER(a3, 7);
+// t0 - t9: Can be used without reservation, act as temporary registers and are
+// allowed to be destroyed by subroutines.
+REGISTER(t0, 8);
+REGISTER(t1, 9);
+REGISTER(t2, 10);
+REGISTER(t3, 11);
+REGISTER(t4, 12);
+REGISTER(t5, 13);
+REGISTER(t6, 14);
+REGISTER(t7, 15);
+// s0 - s7: Subroutine register variables. Subroutines that write to these
+// registers must restore their values before exiting so that the caller can
+// expect the values to be preserved.
+REGISTER(s0, 16);
+REGISTER(s1, 17);
+REGISTER(s2, 18);
+REGISTER(s3, 19);
+REGISTER(s4, 20);
+REGISTER(s5, 21);
+REGISTER(s6, 22);
+REGISTER(s7, 23);
+REGISTER(t8, 24);
+REGISTER(t9, 25);
+// k0, k1: Reserved for system calls and interrupt handlers.
+REGISTER(k0, 26);
+REGISTER(k1, 27);
+// gp: Reserved.
+REGISTER(gp, 28);
+// sp: Stack pointer.
+REGISTER(sp, 29);
+// fp: Frame pointer.
+REGISTER(fp, 30);
+// ra: Return address pointer.
+REGISTER(ra, 31);
+
+#undef REGISTER
+
+
+int ToNumber(Register reg);
+
+Register ToRegister(int num);
+
+// Coprocessor register.
+struct FPURegister {
+ static const int kMaxNumRegisters = v8::internal::kNumFPURegisters;
+
+ // TODO(plind): Warning, inconsistent numbering here. kNumFPURegisters refers
+ // to number of 32-bit FPU regs, but kNumAllocatableRegisters refers to
+ // number of Double regs (64-bit regs, or FPU-reg-pairs).
+
+ // A few double registers are reserved: one as a scratch register and one to
+ // hold 0.0.
+ // f28: 0.0
+ // f30: scratch register.
+ static const int kNumReservedRegisters = 2;
+ static const int kMaxNumAllocatableRegisters = kMaxNumRegisters / 2 -
+ kNumReservedRegisters;
+
+ inline static int NumRegisters();
+ inline static int NumAllocatableRegisters();
+ inline static int ToAllocationIndex(FPURegister reg);
+ static const char* AllocationIndexToString(int index);
+
+ static FPURegister FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ return from_code(index * 2);
+ }
+
+ static FPURegister from_code(int code) {
+ FPURegister r = { code };
+ return r;
+ }
+
+ bool is_valid() const { return 0 <= code_ && code_ < kMaxNumRegisters ; }
+ bool is(FPURegister creg) const { return code_ == creg.code_; }
+ FPURegister low() const {
+ // Find low reg of a Double-reg pair, which is the reg itself.
+ ASSERT(code_ % 2 == 0); // Specified Double reg must be even.
+ FPURegister reg;
+ reg.code_ = code_;
+ ASSERT(reg.is_valid());
+ return reg;
+ }
+ FPURegister high() const {
+ // Find high reg of a Doubel-reg pair, which is reg + 1.
+ ASSERT(code_ % 2 == 0); // Specified Double reg must be even.
+ FPURegister reg;
+ reg.code_ = code_ + 1;
+ ASSERT(reg.is_valid());
+ return reg;
+ }
+
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ ASSERT(is_valid());
+ return 1 << code_;
+ }
+ void setcode(int f) {
+ code_ = f;
+ ASSERT(is_valid());
+ }
+ // Unfortunately we can't make this private in a struct.
+ int code_;
+};
+
+// V8 now supports the O32 ABI, and the FPU Registers are organized as 32
+// 32-bit registers, f0 through f31. When used as 'double' they are used
+// in pairs, starting with the even numbered register. So a double operation
+// on f0 really uses f0 and f1.
+// (Modern mips hardware also supports 32 64-bit registers, via setting
+// (priviledged) Status Register FR bit to 1. This is used by the N32 ABI,
+// but it is not in common use. Someday we will want to support this in v8.)
+
+// For O32 ABI, Floats and Doubles refer to same set of 32 32-bit registers.
+typedef FPURegister DoubleRegister;
+typedef FPURegister FloatRegister;
+
+const FPURegister no_freg = { -1 };
+
+const FPURegister f0 = { 0 }; // Return value in hard float mode.
+const FPURegister f1 = { 1 };
+const FPURegister f2 = { 2 };
+const FPURegister f3 = { 3 };
+const FPURegister f4 = { 4 };
+const FPURegister f5 = { 5 };
+const FPURegister f6 = { 6 };
+const FPURegister f7 = { 7 };
+const FPURegister f8 = { 8 };
+const FPURegister f9 = { 9 };
+const FPURegister f10 = { 10 };
+const FPURegister f11 = { 11 };
+const FPURegister f12 = { 12 }; // Arg 0 in hard float mode.
+const FPURegister f13 = { 13 };
+const FPURegister f14 = { 14 }; // Arg 1 in hard float mode.
+const FPURegister f15 = { 15 };
+const FPURegister f16 = { 16 };
+const FPURegister f17 = { 17 };
+const FPURegister f18 = { 18 };
+const FPURegister f19 = { 19 };
+const FPURegister f20 = { 20 };
+const FPURegister f21 = { 21 };
+const FPURegister f22 = { 22 };
+const FPURegister f23 = { 23 };
+const FPURegister f24 = { 24 };
+const FPURegister f25 = { 25 };
+const FPURegister f26 = { 26 };
+const FPURegister f27 = { 27 };
+const FPURegister f28 = { 28 };
+const FPURegister f29 = { 29 };
+const FPURegister f30 = { 30 };
+const FPURegister f31 = { 31 };
+
+// Register aliases.
+// cp is assumed to be a callee saved register.
+// Defined using #define instead of "static const Register&" because Clang
+// complains otherwise when a compilation unit that includes this header
+// doesn't use the variables.
+#define kRootRegister s6
+#define cp s7
+#define kLithiumScratchReg s3
+#define kLithiumScratchReg2 s4
+#define kLithiumScratchDouble f30
+#define kDoubleRegZero f28
+
+// FPU (coprocessor 1) control registers.
+// Currently only FCSR (#31) is implemented.
+struct FPUControlRegister {
+ bool is_valid() const { return code_ == kFCSRRegister; }
+ bool is(FPUControlRegister creg) const { return code_ == creg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ ASSERT(is_valid());
+ return 1 << code_;
+ }
+ void setcode(int f) {
+ code_ = f;
+ ASSERT(is_valid());
+ }
+ // Unfortunately we can't make this private in a struct.
+ int code_;
+};
+
+const FPUControlRegister no_fpucreg = { kInvalidFPUControlRegister };
+const FPUControlRegister FCSR = { kFCSRRegister };
+
+
+// -----------------------------------------------------------------------------
+// Machine instruction Operands.
+
+// Class Operand represents a shifter operand in data processing instructions.
+class Operand BASE_EMBEDDED {
+ public:
+ // Immediate.
+ INLINE(explicit Operand(int32_t immediate,
+ RelocInfo::Mode rmode = RelocInfo::NONE32));
+ INLINE(explicit Operand(const ExternalReference& f));
+ INLINE(explicit Operand(const char* s));
+ INLINE(explicit Operand(Object** opp));
+ INLINE(explicit Operand(Context** cpp));
+ explicit Operand(Handle<Object> handle);
+ INLINE(explicit Operand(Smi* value));
+
+ // Register.
+ INLINE(explicit Operand(Register rm));
+
+ // Return true if this is a register operand.
+ INLINE(bool is_reg() const);
+
+ inline int32_t immediate() const {
+ ASSERT(!is_reg());
+ return imm32_;
+ }
+
+ Register rm() const { return rm_; }
+
+ private:
+ Register rm_;
+ int32_t imm32_; // Valid if rm_ == no_reg.
+ RelocInfo::Mode rmode_;
+
+ friend class Assembler;
+ friend class MacroAssembler;
+};
+
+
+// On MIPS we have only one adressing mode with base_reg + offset.
+// Class MemOperand represents a memory operand in load and store instructions.
+class MemOperand : public Operand {
+ public:
+ explicit MemOperand(Register rn, int32_t offset = 0);
+ int32_t offset() const { return offset_; }
+
+ bool OffsetIsInt16Encodable() const {
+ return is_int16(offset_);
+ }
+
+ private:
+ int32_t offset_;
+
+ friend class Assembler;
+};
+
+
+// CpuFeatures keeps track of which features are supported by the target CPU.
+// Supported features must be enabled by a CpuFeatureScope before use.
+class CpuFeatures : public AllStatic {
+ public:
+ // Detect features of the target CPU. Set safe defaults if the serializer
+ // is enabled (snapshots must be portable).
+ static void Probe();
+
+ // Check whether a feature is supported by the target CPU.
+ static bool IsSupported(CpuFeature f) {
+ ASSERT(initialized_);
+ return (supported_ & (1u << f)) != 0;
+ }
+
+ static bool IsFoundByRuntimeProbingOnly(CpuFeature f) {
+ ASSERT(initialized_);
+ return (found_by_runtime_probing_only_ &
+ (static_cast<uint64_t>(1) << f)) != 0;
+ }
+
+ static bool IsSafeForSnapshot(CpuFeature f) {
+ return (IsSupported(f) &&
+ (!Serializer::enabled() || !IsFoundByRuntimeProbingOnly(f)));
+ }
+
+ private:
+#ifdef DEBUG
+ static bool initialized_;
+#endif
+ static unsigned supported_;
+ static unsigned found_by_runtime_probing_only_;
+
+ friend class ExternalReference;
+ DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
+};
+
+
+class Assembler : public AssemblerBase {
+ public:
+ // Create an assembler. Instructions and relocation information are emitted
+ // into a buffer, with the instructions starting from the beginning and the
+ // relocation information starting from the end of the buffer. See CodeDesc
+ // for a detailed comment on the layout (globals.h).
+ //
+ // If the provided buffer is NULL, the assembler allocates and grows its own
+ // buffer, and buffer_size determines the initial buffer size. The buffer is
+ // owned by the assembler and deallocated upon destruction of the assembler.
+ //
+ // If the provided buffer is not NULL, the assembler uses the provided buffer
+ // for code generation and assumes its size to be buffer_size. If the buffer
+ // is too small, a fatal error occurs. No deallocation of the buffer is done
+ // upon destruction of the assembler.
+ Assembler(Isolate* isolate, void* buffer, int buffer_size);
+ virtual ~Assembler() { }
+
+ // GetCode emits any pending (non-emitted) code and fills the descriptor
+ // desc. GetCode() is idempotent; it returns the same result if no other
+ // Assembler functions are invoked in between GetCode() calls.
+ void GetCode(CodeDesc* desc);
+
+ // Label operations & relative jumps (PPUM Appendix D).
+ //
+ // Takes a branch opcode (cc) and a label (L) and generates
+ // either a backward branch or a forward branch and links it
+ // to the label fixup chain. Usage:
+ //
+ // Label L; // unbound label
+ // j(cc, &L); // forward branch to unbound label
+ // bind(&L); // bind label to the current pc
+ // j(cc, &L); // backward branch to bound label
+ // bind(&L); // illegal: a label may be bound only once
+ //
+ // Note: The same Label can be used for forward and backward branches
+ // but it may be bound only once.
+ void bind(Label* L); // Binds an unbound label L to current code position.
+ // Determines if Label is bound and near enough so that branch instruction
+ // can be used to reach it, instead of jump instruction.
+ bool is_near(Label* L);
+
+ // Returns the branch offset to the given label from the current code
+ // position. Links the label to the current position if it is still unbound.
+ // Manages the jump elimination optimization if the second parameter is true.
+ int32_t branch_offset(Label* L, bool jump_elimination_allowed);
+ int32_t shifted_branch_offset(Label* L, bool jump_elimination_allowed) {
+ int32_t o = branch_offset(L, jump_elimination_allowed);
+ ASSERT((o & 3) == 0); // Assert the offset is aligned.
+ return o >> 2;
+ }
+ uint32_t jump_address(Label* L);
+
+ // Puts a labels target address at the given position.
+ // The high 8 bits are set to zero.
+ void label_at_put(Label* L, int at_offset);
+
+ // Read/Modify the code target address in the branch/call instruction at pc.
+ static Address target_address_at(Address pc);
+ static void set_target_address_at(Address pc, Address target);
+
+ // Return the code target address at a call site from the return address
+ // of that call in the instruction stream.
+ inline static Address target_address_from_return_address(Address pc);
+
+ static void JumpLabelToJumpRegister(Address pc);
+
+ static void QuietNaN(HeapObject* nan);
+
+ // This sets the branch destination (which gets loaded at the call address).
+ // This is for calls and branches within generated code. The serializer
+ // has already deserialized the lui/ori instructions etc.
+ inline static void deserialization_set_special_target_at(
+ Address instruction_payload, Address target) {
+ set_target_address_at(
+ instruction_payload - kInstructionsFor32BitConstant * kInstrSize,
+ target);
+ }
+
+ // This sets the branch destination.
+ // This is for calls and branches to runtime code.
+ inline static void set_external_target_at(Address instruction_payload,
+ Address target) {
+ set_target_address_at(instruction_payload, target);
+ }
+
+ // Size of an instruction.
+ static const int kInstrSize = sizeof(Instr);
+
+ // Difference between address of current opcode and target address offset.
+ static const int kBranchPCOffset = 4;
+
+ // Here we are patching the address in the LUI/ORI instruction pair.
+ // These values are used in the serialization process and must be zero for
+ // MIPS platform, as Code, Embedded Object or External-reference pointers
+ // are split across two consecutive instructions and don't exist separately
+ // in the code, so the serializer should not step forwards in memory after
+ // a target is resolved and written.
+ static const int kSpecialTargetSize = 0;
+
+ // Number of consecutive instructions used to store 32bit constant.
+ // Before jump-optimizations, this constant was used in
+ // RelocInfo::target_address_address() function to tell serializer address of
+ // the instruction that follows LUI/ORI instruction pair. Now, with new jump
+ // optimization, where jump-through-register instruction that usually
+ // follows LUI/ORI pair is substituted with J/JAL, this constant equals
+ // to 3 instructions (LUI+ORI+J/JAL/JR/JALR).
+ static const int kInstructionsFor32BitConstant = 3;
+
+ // Distance between the instruction referring to the address of the call
+ // target and the return address.
+ static const int kCallTargetAddressOffset = 4 * kInstrSize;
+
+ // Distance between start of patched return sequence and the emitted address
+ // to jump to.
+ static const int kPatchReturnSequenceAddressOffset = 0;
+
+ // Distance between start of patched debug break slot and the emitted address
+ // to jump to.
+ static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize;
+
+ // Difference between address of current opcode and value read from pc
+ // register.
+ static const int kPcLoadDelta = 4;
+
+ static const int kPatchDebugBreakSlotReturnOffset = 4 * kInstrSize;
+
+ // Number of instructions used for the JS return sequence. The constant is
+ // used by the debugger to patch the JS return sequence.
+ static const int kJSReturnSequenceInstructions = 7;
+ static const int kDebugBreakSlotInstructions = 4;
+ static const int kDebugBreakSlotLength =
+ kDebugBreakSlotInstructions * kInstrSize;
+
+
+ // ---------------------------------------------------------------------------
+ // Code generation.
+
+ // Insert the smallest number of nop instructions
+ // possible to align the pc offset to a multiple
+ // of m. m must be a power of 2 (>= 4).
+ void Align(int m);
+ // Aligns code to something that's optimal for a jump target for the platform.
+ void CodeTargetAlign();
+
+ // Different nop operations are used by the code generator to detect certain
+ // states of the generated code.
+ enum NopMarkerTypes {
+ NON_MARKING_NOP = 0,
+ DEBUG_BREAK_NOP,
+ // IC markers.
+ PROPERTY_ACCESS_INLINED,
+ PROPERTY_ACCESS_INLINED_CONTEXT,
+ PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE,
+ // Helper values.
+ LAST_CODE_MARKER,
+ FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED,
+ // Code aging
+ CODE_AGE_MARKER_NOP = 6,
+ CODE_AGE_SEQUENCE_NOP
+ };
+
+ // Type == 0 is the default non-marking nop. For mips this is a
+ // sll(zero_reg, zero_reg, 0). We use rt_reg == at for non-zero
+ // marking, to avoid conflict with ssnop and ehb instructions.
+ void nop(unsigned int type = 0) {
+ ASSERT(type < 32);
+ Register nop_rt_reg = (type == 0) ? zero_reg : at;
+ sll(zero_reg, nop_rt_reg, type, true);
+ }
+
+
+ // --------Branch-and-jump-instructions----------
+ // We don't use likely variant of instructions.
+ void b(int16_t offset);
+ void b(Label* L) { b(branch_offset(L, false)>>2); }
+ void bal(int16_t offset);
+ void bal(Label* L) { bal(branch_offset(L, false)>>2); }
+
+ void beq(Register rs, Register rt, int16_t offset);
+ void beq(Register rs, Register rt, Label* L) {
+ beq(rs, rt, branch_offset(L, false) >> 2);
+ }
+ void bgez(Register rs, int16_t offset);
+ void bgezal(Register rs, int16_t offset);
+ void bgtz(Register rs, int16_t offset);
+ void blez(Register rs, int16_t offset);
+ void bltz(Register rs, int16_t offset);
+ void bltzal(Register rs, int16_t offset);
+ void bne(Register rs, Register rt, int16_t offset);
+ void bne(Register rs, Register rt, Label* L) {
+ bne(rs, rt, branch_offset(L, false)>>2);
+ }
+
+ // 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. i.e. 28 bits.
+ void j(int32_t target);
+ void jal(int32_t target);
+ void jalr(Register rs, Register rd = ra);
+ void jr(Register target);
+ void j_or_jr(int32_t target, Register rs);
+ void jal_or_jalr(int32_t target, Register rs);
+
+
+ //-------Data-processing-instructions---------
+
+ // Arithmetic.
+ void addu(Register rd, Register rs, Register rt);
+ void subu(Register rd, Register rs, Register rt);
+ void mult(Register rs, Register rt);
+ void multu(Register rs, Register rt);
+ void div(Register rs, Register rt);
+ void divu(Register rs, Register rt);
+ void mul(Register rd, Register rs, Register rt);
+
+ void addiu(Register rd, Register rs, int32_t j);
+
+ // Logical.
+ void and_(Register rd, Register rs, Register rt);
+ void or_(Register rd, Register rs, Register rt);
+ void xor_(Register rd, Register rs, Register rt);
+ void nor(Register rd, Register rs, Register rt);
+
+ void andi(Register rd, Register rs, int32_t j);
+ void ori(Register rd, Register rs, int32_t j);
+ void xori(Register rd, Register rs, int32_t j);
+ void lui(Register rd, int32_t j);
+
+ // Shifts.
+ // Please note: sll(zero_reg, zero_reg, x) instructions are reserved as nop
+ // and may cause problems in normal code. coming_from_nop makes sure this
+ // doesn't happen.
+ void sll(Register rd, Register rt, uint16_t sa, bool coming_from_nop = false);
+ void sllv(Register rd, Register rt, Register rs);
+ void srl(Register rd, Register rt, uint16_t sa);
+ void srlv(Register rd, Register rt, Register rs);
+ void sra(Register rt, Register rd, uint16_t sa);
+ void srav(Register rt, Register rd, Register rs);
+ void rotr(Register rd, Register rt, uint16_t sa);
+ void rotrv(Register rd, Register rt, Register rs);
+
+
+ //------------Memory-instructions-------------
+
+ void lb(Register rd, const MemOperand& rs);
+ void lbu(Register rd, const MemOperand& rs);
+ void lh(Register rd, const MemOperand& rs);
+ void lhu(Register rd, const MemOperand& rs);
+ void lw(Register rd, const MemOperand& rs);
+ void lwl(Register rd, const MemOperand& rs);
+ void lwr(Register rd, const MemOperand& rs);
+ void sb(Register rd, const MemOperand& rs);
+ void sh(Register rd, const MemOperand& rs);
+ void sw(Register rd, const MemOperand& rs);
+ void swl(Register rd, const MemOperand& rs);
+ void swr(Register rd, const MemOperand& rs);
+
+
+ //-------------Misc-instructions--------------
+
+ // Break / Trap instructions.
+ void break_(uint32_t code, bool break_as_stop = false);
+ void stop(const char* msg, uint32_t code = kMaxStopCode);
+ void tge(Register rs, Register rt, uint16_t code);
+ void tgeu(Register rs, Register rt, uint16_t code);
+ void tlt(Register rs, Register rt, uint16_t code);
+ void tltu(Register rs, Register rt, uint16_t code);
+ void teq(Register rs, Register rt, uint16_t code);
+ void tne(Register rs, Register rt, uint16_t code);
+
+ // Move from HI/LO register.
+ void mfhi(Register rd);
+ void mflo(Register rd);
+
+ // Set on less than.
+ void slt(Register rd, Register rs, Register rt);
+ void sltu(Register rd, Register rs, Register rt);
+ void slti(Register rd, Register rs, int32_t j);
+ void sltiu(Register rd, Register rs, int32_t j);
+
+ // Conditional move.
+ void movz(Register rd, Register rs, Register rt);
+ void movn(Register rd, Register rs, Register rt);
+ void movt(Register rd, Register rs, uint16_t cc = 0);
+ void movf(Register rd, Register rs, uint16_t cc = 0);
+
+ // Bit twiddling.
+ void clz(Register rd, Register rs);
+ void ins_(Register rt, Register rs, uint16_t pos, uint16_t size);
+ void ext_(Register rt, Register rs, uint16_t pos, uint16_t size);
+
+ //--------Coprocessor-instructions----------------
+
+ // Load, store, and move.
+ void lwc1(FPURegister fd, const MemOperand& src);
+ void ldc1(FPURegister fd, const MemOperand& src);
+
+ void swc1(FPURegister fs, const MemOperand& dst);
+ void sdc1(FPURegister fs, const MemOperand& dst);
+
+ void mtc1(Register rt, FPURegister fs);
+ void mfc1(Register rt, FPURegister fs);
+
+ void ctc1(Register rt, FPUControlRegister fs);
+ void cfc1(Register rt, FPUControlRegister fs);
+
+ // Arithmetic.
+ void add_d(FPURegister fd, FPURegister fs, FPURegister ft);
+ void sub_d(FPURegister fd, FPURegister fs, FPURegister ft);
+ void mul_d(FPURegister fd, FPURegister fs, FPURegister ft);
+ void madd_d(FPURegister fd, FPURegister fr, FPURegister fs, FPURegister ft);
+ void div_d(FPURegister fd, FPURegister fs, FPURegister ft);
+ void abs_d(FPURegister fd, FPURegister fs);
+ void mov_d(FPURegister fd, FPURegister fs);
+ void neg_d(FPURegister fd, FPURegister fs);
+ void sqrt_d(FPURegister fd, FPURegister fs);
+
+ // Conversion.
+ void cvt_w_s(FPURegister fd, FPURegister fs);
+ void cvt_w_d(FPURegister fd, FPURegister fs);
+ void trunc_w_s(FPURegister fd, FPURegister fs);
+ void trunc_w_d(FPURegister fd, FPURegister fs);
+ void round_w_s(FPURegister fd, FPURegister fs);
+ void round_w_d(FPURegister fd, FPURegister fs);
+ void floor_w_s(FPURegister fd, FPURegister fs);
+ void floor_w_d(FPURegister fd, FPURegister fs);
+ void ceil_w_s(FPURegister fd, FPURegister fs);
+ void ceil_w_d(FPURegister fd, FPURegister fs);
+
+ void cvt_l_s(FPURegister fd, FPURegister fs);
+ void cvt_l_d(FPURegister fd, FPURegister fs);
+ void trunc_l_s(FPURegister fd, FPURegister fs);
+ void trunc_l_d(FPURegister fd, FPURegister fs);
+ void round_l_s(FPURegister fd, FPURegister fs);
+ void round_l_d(FPURegister fd, FPURegister fs);
+ void floor_l_s(FPURegister fd, FPURegister fs);
+ void floor_l_d(FPURegister fd, FPURegister fs);
+ void ceil_l_s(FPURegister fd, FPURegister fs);
+ void ceil_l_d(FPURegister fd, FPURegister fs);
+
+ void cvt_s_w(FPURegister fd, FPURegister fs);
+ void cvt_s_l(FPURegister fd, FPURegister fs);
+ void cvt_s_d(FPURegister fd, FPURegister fs);
+
+ void cvt_d_w(FPURegister fd, FPURegister fs);
+ void cvt_d_l(FPURegister fd, FPURegister fs);
+ void cvt_d_s(FPURegister fd, FPURegister fs);
+
+ // Conditions and branches.
+ void c(FPUCondition cond, SecondaryField fmt,
+ FPURegister ft, FPURegister fs, uint16_t cc = 0);
+
+ void bc1f(int16_t offset, uint16_t cc = 0);
+ void bc1f(Label* L, uint16_t cc = 0) { bc1f(branch_offset(L, false)>>2, cc); }
+ void bc1t(int16_t offset, uint16_t cc = 0);
+ void bc1t(Label* L, uint16_t cc = 0) { bc1t(branch_offset(L, false)>>2, cc); }
+ void fcmp(FPURegister src1, const double src2, FPUCondition cond);
+
+ // Check the code size generated from label to here.
+ int SizeOfCodeGeneratedSince(Label* label) {
+ return pc_offset() - label->pos();
+ }
+
+ // Check the number of instructions generated from label to here.
+ int InstructionsGeneratedSince(Label* label) {
+ return SizeOfCodeGeneratedSince(label) / kInstrSize;
+ }
+
+ // Class for scoping postponing the trampoline pool generation.
+ class BlockTrampolinePoolScope {
+ public:
+ explicit BlockTrampolinePoolScope(Assembler* assem) : assem_(assem) {
+ assem_->StartBlockTrampolinePool();
+ }
+ ~BlockTrampolinePoolScope() {
+ assem_->EndBlockTrampolinePool();
+ }
+
+ private:
+ Assembler* assem_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BlockTrampolinePoolScope);
+ };
+
+ // Class for postponing the assembly buffer growth. Typically used for
+ // sequences of instructions that must be emitted as a unit, before
+ // buffer growth (and relocation) can occur.
+ // This blocking scope is not nestable.
+ class BlockGrowBufferScope {
+ public:
+ explicit BlockGrowBufferScope(Assembler* assem) : assem_(assem) {
+ assem_->StartBlockGrowBuffer();
+ }
+ ~BlockGrowBufferScope() {
+ assem_->EndBlockGrowBuffer();
+ }
+
+ private:
+ Assembler* assem_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BlockGrowBufferScope);
+ };
+
+ // Debugging.
+
+ // Mark address of the ExitJSFrame code.
+ void RecordJSReturn();
+
+ // Mark address of a debug break slot.
+ void RecordDebugBreakSlot();
+
+ // Record the AST id of the CallIC being compiled, so that it can be placed
+ // in the relocation information.
+ void SetRecordedAstId(TypeFeedbackId ast_id) {
+ ASSERT(recorded_ast_id_.IsNone());
+ recorded_ast_id_ = ast_id;
+ }
+
+ TypeFeedbackId RecordedAstId() {
+ ASSERT(!recorded_ast_id_.IsNone());
+ return recorded_ast_id_;
+ }
+
+ void ClearRecordedAstId() { recorded_ast_id_ = TypeFeedbackId::None(); }
+
+ // Record a comment relocation entry that can be used by a disassembler.
+ // Use --code-comments to enable.
+ void RecordComment(const char* msg);
+
+ static int RelocateInternalReference(byte* pc, intptr_t pc_delta);
+
+ // Writes a single byte or word of data in the code stream. Used for
+ // inline tables, e.g., jump-tables.
+ void db(uint8_t data);
+ void dd(uint32_t data);
+
+ PositionsRecorder* positions_recorder() { return &positions_recorder_; }
+
+ // Postpone the generation of the trampoline pool for the specified number of
+ // instructions.
+ void BlockTrampolinePoolFor(int instructions);
+
+ // Check if there is less than kGap bytes available in the buffer.
+ // If this is the case, we need to grow the buffer before emitting
+ // an instruction or relocation information.
+ inline bool overflow() const { return pc_ >= reloc_info_writer.pos() - kGap; }
+
+ // Get the number of bytes available in the buffer.
+ inline int available_space() const { return reloc_info_writer.pos() - pc_; }
+
+ // Read/patch instructions.
+ 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;
+ }
+ 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;
+ }
+
+ // Check if an instruction is a branch of some kind.
+ static bool IsBranch(Instr instr);
+ static bool IsBeq(Instr instr);
+ static bool IsBne(Instr instr);
+
+ static bool IsJump(Instr instr);
+ static bool IsJ(Instr instr);
+ static bool IsLui(Instr instr);
+ static bool IsOri(Instr instr);
+
+ static bool IsJal(Instr instr);
+ static bool IsJr(Instr instr);
+ static bool IsJalr(Instr instr);
+
+ static bool IsNop(Instr instr, unsigned int type);
+ static bool IsPop(Instr instr);
+ static bool IsPush(Instr instr);
+ static bool IsLwRegFpOffset(Instr instr);
+ static bool IsSwRegFpOffset(Instr instr);
+ static bool IsLwRegFpNegOffset(Instr instr);
+ static bool IsSwRegFpNegOffset(Instr instr);
+
+ static Register GetRtReg(Instr instr);
+ static Register GetRsReg(Instr instr);
+ static Register GetRdReg(Instr instr);
+
+ static uint32_t GetRt(Instr instr);
+ static uint32_t GetRtField(Instr instr);
+ static uint32_t GetRs(Instr instr);
+ static uint32_t GetRsField(Instr instr);
+ static uint32_t GetRd(Instr instr);
+ static uint32_t GetRdField(Instr instr);
+ static uint32_t GetSa(Instr instr);
+ static uint32_t GetSaField(Instr instr);
+ static uint32_t GetOpcodeField(Instr instr);
+ static uint32_t GetFunction(Instr instr);
+ static uint32_t GetFunctionField(Instr instr);
+ static uint32_t GetImmediate16(Instr instr);
+ static uint32_t GetLabelConst(Instr instr);
+
+ static int32_t GetBranchOffset(Instr instr);
+ static bool IsLw(Instr instr);
+ static int16_t GetLwOffset(Instr instr);
+ static Instr SetLwOffset(Instr instr, int16_t offset);
+
+ static bool IsSw(Instr instr);
+ static Instr SetSwOffset(Instr instr, int16_t offset);
+ static bool IsAddImmediate(Instr instr);
+ static Instr SetAddImmediateOffset(Instr instr, int16_t offset);
+
+ static bool IsAndImmediate(Instr instr);
+ static bool IsEmittedConstant(Instr instr);
+
+ void CheckTrampolinePool();
+
+ protected:
+ // Relocation for a type-recording IC has the AST id added to it. This
+ // member variable is a way to pass the information from the call site to
+ // the relocation info.
+ TypeFeedbackId recorded_ast_id_;
+
+ int32_t buffer_space() const { return reloc_info_writer.pos() - pc_; }
+
+ // Decode branch instruction at pos and return branch target pos.
+ int target_at(int32_t pos);
+
+ // Patch branch instruction at pos to branch to given branch target pos.
+ void target_at_put(int32_t pos, int32_t target_pos);
+
+ // Say if we need to relocate with this mode.
+ bool MustUseReg(RelocInfo::Mode rmode);
+
+ // Record reloc info for current pc_.
+ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
+
+ // Block the emission of the trampoline pool before pc_offset.
+ void BlockTrampolinePoolBefore(int pc_offset) {
+ if (no_trampoline_pool_before_ < pc_offset)
+ no_trampoline_pool_before_ = pc_offset;
+ }
+
+ void StartBlockTrampolinePool() {
+ trampoline_pool_blocked_nesting_++;
+ }
+
+ void EndBlockTrampolinePool() {
+ trampoline_pool_blocked_nesting_--;
+ }
+
+ bool is_trampoline_pool_blocked() const {
+ return trampoline_pool_blocked_nesting_ > 0;
+ }
+
+ bool has_exception() const {
+ return internal_trampoline_exception_;
+ }
+
+ void DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi);
+
+ bool is_trampoline_emitted() const {
+ return trampoline_emitted_;
+ }
+
+ // Temporarily block automatic assembly buffer growth.
+ void StartBlockGrowBuffer() {
+ ASSERT(!block_buffer_growth_);
+ block_buffer_growth_ = true;
+ }
+
+ void EndBlockGrowBuffer() {
+ ASSERT(block_buffer_growth_);
+ block_buffer_growth_ = false;
+ }
+
+ bool is_buffer_growth_blocked() const {
+ return block_buffer_growth_;
+ }
+
+ private:
+ // Buffer size and constant pool distance are checked together at regular
+ // intervals of kBufferCheckInterval emitted bytes.
+ static const int kBufferCheckInterval = 1*KB/2;
+
+ // Code generation.
+ // The relocation writer's position is at least kGap bytes below the end of
+ // the generated instructions. This is so that multi-instruction sequences do
+ // not have to check for overflow. The same is true for writes of large
+ // relocation info entries.
+ static const int kGap = 32;
+
+
+ // Repeated checking whether the trampoline pool should be emitted is rather
+ // expensive. By default we only check again once a number of instructions
+ // has been generated.
+ static const int kCheckConstIntervalInst = 32;
+ static const int kCheckConstInterval = kCheckConstIntervalInst * kInstrSize;
+
+ int next_buffer_check_; // pc offset of next buffer check.
+
+ // Emission of the trampoline pool may be blocked in some code sequences.
+ int trampoline_pool_blocked_nesting_; // Block emission if this is not zero.
+ int no_trampoline_pool_before_; // Block emission before this pc offset.
+
+ // Keep track of the last emitted pool to guarantee a maximal distance.
+ int last_trampoline_pool_end_; // pc offset of the end of the last pool.
+
+ // Automatic growth of the assembly buffer may be blocked for some sequences.
+ bool block_buffer_growth_; // Block growth when true.
+
+ // Relocation information generation.
+ // Each relocation is encoded as a variable size value.
+ static const int kMaxRelocSize = RelocInfoWriter::kMaxSize;
+ RelocInfoWriter reloc_info_writer;
+
+ // The bound position, before this we cannot do instruction elimination.
+ int last_bound_pos_;
+
+ // Code emission.
+ inline void CheckBuffer();
+ void GrowBuffer();
+ inline void emit(Instr x);
+ inline void CheckTrampolinePoolQuick();
+
+ // Instruction generation.
+ // We have 3 different kind of encoding layout on MIPS.
+ // However due to many different types of objects encoded in the same fields
+ // we have quite a few aliases for each mode.
+ // Using the same structure to refer to Register and FPURegister would spare a
+ // few aliases, but mixing both does not look clean to me.
+ // Anyway we could surely implement this differently.
+
+ void GenInstrRegister(Opcode opcode,
+ Register rs,
+ Register rt,
+ Register rd,
+ uint16_t sa = 0,
+ SecondaryField func = NULLSF);
+
+ void GenInstrRegister(Opcode opcode,
+ Register rs,
+ Register rt,
+ uint16_t msb,
+ uint16_t lsb,
+ SecondaryField func);
+
+ void GenInstrRegister(Opcode opcode,
+ SecondaryField fmt,
+ FPURegister ft,
+ FPURegister fs,
+ FPURegister fd,
+ SecondaryField func = NULLSF);
+
+ void GenInstrRegister(Opcode opcode,
+ FPURegister fr,
+ FPURegister ft,
+ FPURegister fs,
+ FPURegister fd,
+ SecondaryField func = NULLSF);
+
+ void GenInstrRegister(Opcode opcode,
+ SecondaryField fmt,
+ Register rt,
+ FPURegister fs,
+ FPURegister fd,
+ SecondaryField func = NULLSF);
+
+ void GenInstrRegister(Opcode opcode,
+ SecondaryField fmt,
+ Register rt,
+ FPUControlRegister fs,
+ SecondaryField func = NULLSF);
+
+
+ void GenInstrImmediate(Opcode opcode,
+ Register rs,
+ Register rt,
+ int32_t j);
+ void GenInstrImmediate(Opcode opcode,
+ Register rs,
+ SecondaryField SF,
+ int32_t j);
+ void GenInstrImmediate(Opcode opcode,
+ Register r1,
+ FPURegister r2,
+ int32_t j);
+
+
+ void GenInstrJump(Opcode opcode,
+ uint32_t address);
+
+ // Helpers.
+ void LoadRegPlusOffsetToAt(const MemOperand& src);
+
+ // Labels.
+ void print(Label* L);
+ void bind_to(Label* L, int pos);
+ void next(Label* L);
+
+ // One trampoline consists of:
+ // - space for trampoline slots,
+ // - space for labels.
+ //
+ // Space for trampoline slots is equal to slot_count * 2 * kInstrSize.
+ // Space for trampoline slots preceeds space for labels. Each label is of one
+ // instruction size, so total amount for labels is equal to
+ // label_count * kInstrSize.
+ class Trampoline {
+ public:
+ Trampoline() {
+ start_ = 0;
+ next_slot_ = 0;
+ free_slot_count_ = 0;
+ end_ = 0;
+ }
+ Trampoline(int start, int slot_count) {
+ start_ = start;
+ next_slot_ = start;
+ free_slot_count_ = slot_count;
+ end_ = start + slot_count * kTrampolineSlotsSize;
+ }
+ int start() {
+ return start_;
+ }
+ int end() {
+ return end_;
+ }
+ int take_slot() {
+ int trampoline_slot = kInvalidSlotPos;
+ if (free_slot_count_ <= 0) {
+ // We have run out of space on trampolines.
+ // Make sure we fail in debug mode, so we become aware of each case
+ // when this happens.
+ ASSERT(0);
+ // Internal exception will be caught.
+ } else {
+ trampoline_slot = next_slot_;
+ free_slot_count_--;
+ next_slot_ += kTrampolineSlotsSize;
+ }
+ return trampoline_slot;
+ }
+
+ private:
+ int start_;
+ int end_;
+ int next_slot_;
+ int free_slot_count_;
+ };
+
+ int32_t get_trampoline_entry(int32_t pos);
+ int unbound_labels_count_;
+ // If trampoline is emitted, generated code is becoming large. As this is
+ // already a slow case which can possibly break our code generation for the
+ // extreme case, we use this information to trigger different mode of
+ // branch instruction generation, where we use jump instructions rather
+ // than regular branch instructions.
+ bool trampoline_emitted_;
+ static const int kTrampolineSlotsSize = 4 * kInstrSize;
+ static const int kMaxBranchOffset = (1 << (18 - 1)) - 1;
+ static const int kInvalidSlotPos = -1;
+
+ Trampoline trampoline_;
+ bool internal_trampoline_exception_;
+
+ friend class RegExpMacroAssemblerMIPS;
+ friend class RelocInfo;
+ friend class CodePatcher;
+ friend class BlockTrampolinePoolScope;
+
+ PositionsRecorder positions_recorder_;
+ friend class PositionsRecorder;
+ friend class EnsureSpace;
+};
+
+
+class EnsureSpace BASE_EMBEDDED {
+ public:
+ explicit EnsureSpace(Assembler* assembler) {
+ assembler->CheckBuffer();
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_ARM_ASSEMBLER_MIPS_H_
diff --git a/chromium/v8/src/mips/builtins-mips.cc b/chromium/v8/src/mips/builtins-mips.cc
new file mode 100644
index 00000000000..d424cbc7261
--- /dev/null
+++ b/chromium/v8/src/mips/builtins-mips.cc
@@ -0,0 +1,1524 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "codegen.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+#include "runtime.h"
+
+namespace v8 {
+namespace internal {
+
+
+#define __ ACCESS_MASM(masm)
+
+
+void Builtins::Generate_Adaptor(MacroAssembler* masm,
+ CFunctionId id,
+ BuiltinExtraArguments extra_args) {
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments excluding receiver
+ // -- a1 : called function (only guaranteed when
+ // -- extra_args requires it)
+ // -- cp : context
+ // -- sp[0] : last argument
+ // -- ...
+ // -- sp[4 * (argc - 1)] : first argument
+ // -- sp[4 * agrc] : receiver
+ // -----------------------------------
+
+ // Insert extra arguments.
+ int num_extra_args = 0;
+ if (extra_args == NEEDS_CALLED_FUNCTION) {
+ num_extra_args = 1;
+ __ push(a1);
+ } else {
+ ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
+ }
+
+ // JumpToExternalReference expects s0 to contain the number of arguments
+ // including the receiver and the extra arguments.
+ __ Addu(s0, a0, num_extra_args + 1);
+ __ sll(s1, s0, kPointerSizeLog2);
+ __ Subu(s1, s1, kPointerSize);
+ __ JumpToExternalReference(ExternalReference(id, masm->isolate()));
+}
+
+
+// Load the built-in InternalArray function from the current context.
+static void GenerateLoadInternalArrayFunction(MacroAssembler* masm,
+ Register result) {
+ // Load the native context.
+
+ __ lw(result,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ lw(result,
+ FieldMemOperand(result, GlobalObject::kNativeContextOffset));
+ // Load the InternalArray function from the native context.
+ __ lw(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 native context.
+
+ __ lw(result,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ lw(result,
+ FieldMemOperand(result, GlobalObject::kNativeContextOffset));
+ // Load the Array function from the native context.
+ __ lw(result,
+ MemOperand(result,
+ Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
+}
+
+
+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, kUnexpectedInitialMapForInternalArrayFunction,
+ t0, Operand(zero_reg));
+ __ GetObjectType(a2, a3, t0);
+ __ Assert(eq, kUnexpectedInitialMapForInternalArrayFunction,
+ t0, Operand(MAP_TYPE));
+ }
+
+ // Run the native code for the InternalArray function called as a normal
+ // function.
+ // Tail call a stub.
+ InternalArrayConstructorStub stub(masm->isolate());
+ __ TailCallStub(&stub);
+}
+
+
+void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments
+ // -- ra : return address
+ // -- sp[...]: constructor arguments
+ // -----------------------------------
+ Label generic_array_code;
+
+ // Get the Array function.
+ GenerateLoadArrayFunction(masm, a1);
+
+ if (FLAG_debug_code) {
+ // Initial map for the builtin Array functions should be maps.
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
+ __ And(t0, a2, Operand(kSmiTagMask));
+ __ Assert(ne, kUnexpectedInitialMapForArrayFunction1,
+ t0, Operand(zero_reg));
+ __ GetObjectType(a2, a3, t0);
+ __ Assert(eq, kUnexpectedInitialMapForArrayFunction2,
+ t0, Operand(MAP_TYPE));
+ }
+
+ // Run the native code for the Array function called as a normal function.
+ // Tail call a stub.
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(),
+ masm->isolate());
+ __ li(a2, Operand(undefined_sentinel));
+ ArrayConstructorStub stub(masm->isolate());
+ __ TailCallStub(&stub);
+}
+
+
+void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments
+ // -- a1 : constructor function
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero based)
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->string_ctor_calls(), 1, a2, a3);
+
+ Register function = a1;
+ if (FLAG_debug_code) {
+ __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, a2);
+ __ Assert(eq, kUnexpectedStringFunction, function, Operand(a2));
+ }
+
+ // Load the first arguments in a0 and get rid of the rest.
+ Label no_arguments;
+ __ Branch(&no_arguments, eq, a0, Operand(zero_reg));
+ // First args = sp[(argc - 1) * 4].
+ __ Subu(a0, a0, Operand(1));
+ __ sll(a0, a0, kPointerSizeLog2);
+ __ Addu(sp, a0, sp);
+ __ lw(a0, MemOperand(sp));
+ // sp now point to args[0], drop args[0] + receiver.
+ __ Drop(2);
+
+ Register argument = a2;
+ Label not_cached, argument_is_string;
+ NumberToStringStub::GenerateLookupNumberStringCache(
+ masm,
+ a0, // Input.
+ argument, // Result.
+ a3, // Scratch.
+ t0, // Scratch.
+ t1, // Scratch.
+ &not_cached);
+ __ IncrementCounter(counters->string_ctor_cached_number(), 1, a3, t0);
+ __ bind(&argument_is_string);
+
+ // ----------- S t a t e -------------
+ // -- a2 : argument converted to string
+ // -- a1 : constructor function
+ // -- ra : return address
+ // -----------------------------------
+
+ Label gc_required;
+ __ Allocate(JSValue::kSize,
+ v0, // Result.
+ a3, // Scratch.
+ t0, // Scratch.
+ &gc_required,
+ TAG_OBJECT);
+
+ // Initialising the String Object.
+ Register map = a3;
+ __ LoadGlobalFunctionInitialMap(function, map, t0);
+ if (FLAG_debug_code) {
+ __ lbu(t0, FieldMemOperand(map, Map::kInstanceSizeOffset));
+ __ Assert(eq, kUnexpectedStringWrapperInstanceSize,
+ t0, Operand(JSValue::kSize >> kPointerSizeLog2));
+ __ lbu(t0, FieldMemOperand(map, Map::kUnusedPropertyFieldsOffset));
+ __ Assert(eq, kUnexpectedUnusedPropertiesOfStringWrapper,
+ t0, Operand(zero_reg));
+ }
+ __ sw(map, FieldMemOperand(v0, HeapObject::kMapOffset));
+
+ __ LoadRoot(a3, Heap::kEmptyFixedArrayRootIndex);
+ __ sw(a3, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ sw(a3, FieldMemOperand(v0, JSObject::kElementsOffset));
+
+ __ sw(argument, FieldMemOperand(v0, JSValue::kValueOffset));
+
+ // Ensure the object is fully initialized.
+ STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
+
+ __ Ret();
+
+ // The argument was not found in the number to string cache. Check
+ // if it's a string already before calling the conversion builtin.
+ Label convert_argument;
+ __ bind(&not_cached);
+ __ JumpIfSmi(a0, &convert_argument);
+
+ // Is it a String?
+ __ lw(a2, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ And(t0, a3, Operand(kIsNotStringMask));
+ __ Branch(&convert_argument, ne, t0, Operand(zero_reg));
+ __ mov(argument, a0);
+ __ IncrementCounter(counters->string_ctor_conversions(), 1, a3, t0);
+ __ Branch(&argument_is_string);
+
+ // Invoke the conversion builtin and put the result into a2.
+ __ bind(&convert_argument);
+ __ push(function); // Preserve the function.
+ __ IncrementCounter(counters->string_ctor_conversions(), 1, a3, t0);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(a0);
+ __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
+ }
+ __ pop(function);
+ __ mov(argument, v0);
+ __ Branch(&argument_is_string);
+
+ // Load the empty string into a2, remove the receiver from the
+ // stack, and jump back to the case where the argument is a string.
+ __ bind(&no_arguments);
+ __ LoadRoot(argument, Heap::kempty_stringRootIndex);
+ __ Drop(1);
+ __ Branch(&argument_is_string);
+
+ // At this point the argument is already a string. Call runtime to
+ // create a string wrapper.
+ __ bind(&gc_required);
+ __ IncrementCounter(counters->string_ctor_gc_required(), 1, a3, t0);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(argument);
+ __ CallRuntime(Runtime::kNewStringWrapper, 1);
+ }
+ __ Ret();
+}
+
+
+static void GenerateTailCallToSharedCode(MacroAssembler* masm) {
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(a2, FieldMemOperand(a2, SharedFunctionInfo::kCodeOffset));
+ __ Addu(at, a2, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ Jump(at);
+}
+
+
+void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) {
+ GenerateTailCallToSharedCode(masm);
+}
+
+
+void Builtins::Generate_InstallRecompiledCode(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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::kInstallRecompiledCode, 1);
+ // Calculate the entry point.
+ __ Addu(t9, v0, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Restore call kind information.
+ __ pop(t1);
+ // Restore saved function.
+ __ pop(a1);
+
+ // Tear down temporary frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ Jump(t9);
+}
+
+
+void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Push a copy of the function onto the stack.
+ __ push(a1);
+ // Push call kind information.
+ __ push(t1);
+
+ __ push(a1); // Function is also the parameter to the runtime call.
+ __ CallRuntime(Runtime::kParallelRecompile, 1);
+
+ // Restore call kind information.
+ __ pop(t1);
+ // Restore receiver.
+ __ pop(a1);
+
+ // Tear down internal frame.
+ }
+
+ GenerateTailCallToSharedCode(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
+ // -- ra : return address
+ // -- sp[...]: constructor arguments
+ // -----------------------------------
+
+ // Should never count constructions for api objects.
+ ASSERT(!is_api_function || !count_constructions);
+
+ Isolate* isolate = masm->isolate();
+
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments
+ // -- a1 : constructor function
+ // -- ra : return address
+ // -- sp[...]: constructor arguments
+ // -----------------------------------
+
+ // Enter a construct frame.
+ {
+ 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());
+
+ // 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;
+#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));
+#endif
+
+ // 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));
+
+ 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);
+ }
+
+ // Now allocate the JSObject on the heap.
+ // a1: constructor function
+ // a2: initial map
+ __ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset));
+ __ Allocate(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, kUnexpectedNumberOfPreAllocatedPropertyFields,
+ a0, Operand(t6));
+ }
+ __ InitializeFieldsWithFiller(t5, a0, t7);
+ // To allow for truncation.
+ __ LoadRoot(t7, Heap::kOnePointerFillerMapRootIndex);
+ }
+ __ 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, kPropertyAllocationCountFailed,
+ 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));
+ __ Allocate(
+ 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, kUndefinedValueNotLoaded, t7, Operand(t8));
+ }
+ __ 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);
+ }
+
+ __ 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(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
+ // 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));
+
+ // 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);
+ }
+
+ // Store offset of return address for deoptimizer.
+ if (!is_api_function && !count_constructions) {
+ masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // 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, a1, 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.
+ }
+
+ __ sll(t0, a1, kPointerSizeLog2 - 1);
+ __ Addu(sp, sp, t0);
+ __ Addu(sp, sp, kPointerSize);
+ __ IncrementCounter(isolate->counters()->constructed_objects(), 1, a1, a2);
+ __ Ret();
+}
+
+
+void Builtins::Generate_JSConstructStubCountdown(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, true);
+}
+
+
+void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, false);
+}
+
+
+void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, true, false);
+}
+
+
+static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
+ bool is_construct) {
+ // Called from JSEntryStub::GenerateBody
+
+ // ----------- S t a t e -------------
+ // -- a0: code entry
+ // -- a1: function
+ // -- a2: receiver_pointer
+ // -- a3: argc
+ // -- s0: argv
+ // -----------------------------------
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Clear the context before we push it when entering the JS frame.
+ __ mov(cp, zero_reg);
+
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Set up the context from the function argument.
+ __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+
+ // Push the function and the receiver onto the stack.
+ __ Push(a1, a2);
+
+ // 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) {
+ // No type feedback cell is available
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(), masm->isolate());
+ __ li(a2, Operand(undefined_sentinel));
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ __ CallStub(&stub);
+ } else {
+ ParameterCount actual(a0);
+ __ InvokeFunction(a1, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
+ }
+
+ // Leave internal frame.
+ }
+
+ __ Jump(ra);
+}
+
+
+void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
+ Generate_JSEntryTrampolineHelper(masm, false);
+}
+
+
+void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
+ Generate_JSEntryTrampolineHelper(masm, true);
+}
+
+
+void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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);
+
+ // Restore call kind information.
+ __ pop(t1);
+ // Restore saved function.
+ __ pop(a1);
+
+ // Tear down temporary frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ Jump(t9);
+}
+
+
+void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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));
+
+ // Restore call kind information.
+ __ pop(t1);
+ // Restore saved function.
+ __ pop(a1);
+
+ // Tear down temporary frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ Jump(t9);
+}
+
+
+static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) {
+ // For now, we are relying on the fact that make_code_young doesn't do any
+ // garbage collection which allows us to save/restore the registers without
+ // worrying about which of them contain pointers. We also don't build an
+ // internal frame to make the code faster, since we shouldn't have to do stack
+ // crawls in MakeCodeYoung. This seems a bit fragile.
+
+ __ mov(a0, ra);
+ // Adjust a0 to point to the head of the PlatformCodeAge sequence
+ __ Subu(a0, a0,
+ Operand((kNoCodeAgeSequenceLength - 1) * Assembler::kInstrSize));
+ // Restore the original return address of the function
+ __ mov(ra, at);
+
+ // The following registers must be saved and restored when calling through to
+ // the runtime:
+ // a0 - contains return address (beginning of patch sequence)
+ // a1 - function object
+ RegList saved_regs =
+ (a0.bit() | a1.bit() | ra.bit() | fp.bit()) & ~sp.bit();
+ FrameScope scope(masm, StackFrame::MANUAL);
+ __ MultiPush(saved_regs);
+ __ PrepareCallCFunction(1, 0, a1);
+ __ CallCFunction(
+ ExternalReference::get_make_code_young_function(masm->isolate()), 1);
+ __ MultiPop(saved_regs);
+ __ Jump(a0);
+}
+
+#define DEFINE_CODE_AGE_BUILTIN_GENERATOR(C) \
+void Builtins::Generate_Make##C##CodeYoungAgainEvenMarking( \
+ MacroAssembler* masm) { \
+ GenerateMakeCodeYoungAgainCommon(masm); \
+} \
+void Builtins::Generate_Make##C##CodeYoungAgainOddMarking( \
+ MacroAssembler* masm) { \
+ GenerateMakeCodeYoungAgainCommon(masm); \
+}
+CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR)
+#undef DEFINE_CODE_AGE_BUILTIN_GENERATOR
+
+
+void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Preserve registers across notification, this is important for compiled
+ // stubs that tail call the runtime on deopts passing their parameters in
+ // registers.
+ __ MultiPush(kJSCallerSaved | kCalleeSaved);
+ // Pass the function and deoptimization type to the runtime system.
+ __ CallRuntime(Runtime::kNotifyStubFailure, 0);
+ __ MultiPop(kJSCallerSaved | kCalleeSaved);
+ }
+
+ __ Addu(sp, sp, Operand(kPointerSize)); // Ignore state
+ __ Jump(ra); // Jump to miss handler
+}
+
+
+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));
+ __ Ret(USE_DELAY_SLOT);
+ // Safe to fill delay slot Addu will emit one instruction.
+ __ Addu(sp, sp, Operand(1 * kPointerSize)); // Remove state.
+
+ __ bind(&with_tos_register);
+ __ lw(v0, MemOperand(sp, 1 * kPointerSize));
+ __ Branch(&unknown_state, ne, t2, Operand(FullCodeGenerator::TOS_REG));
+
+ __ Ret(USE_DELAY_SLOT);
+ // Safe to fill delay slot Addu will emit one instruction.
+ __ Addu(sp, sp, Operand(2 * kPointerSize)); // Remove state.
+
+ __ bind(&unknown_state);
+ __ stop("no cases left");
+}
+
+
+void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
+}
+
+
+void Builtins::Generate_NotifySoftDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
+}
+
+
+void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
+}
+
+
+void Builtins::Generate_NotifyOSR(MacroAssembler* masm) {
+ // 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) {
+ // 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();
+}
+
+
+void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
+ // 1. Make sure we have at least one argument.
+ // a0: actual number of arguments
+ { Label done;
+ __ Branch(&done, ne, a0, Operand(zero_reg));
+ __ LoadRoot(t2, Heap::kUndefinedValueRootIndex);
+ __ push(t2);
+ __ Addu(a0, a0, Operand(1));
+ __ bind(&done);
+ }
+
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ // a0: actual number of arguments
+ Label slow, non_function;
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(at, sp, at);
+ __ lw(a1, MemOperand(at));
+ __ JumpIfSmi(a1, &non_function);
+ __ GetObjectType(a1, a2, a2);
+ __ 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::NONE32)); // 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));
+
+ // Do not transform the receiver for strict mode functions.
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(a3, FieldMemOperand(a2, SharedFunctionInfo::kCompilerHintsOffset));
+ __ And(t3, a3, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
+ kSmiTagSize)));
+ __ Branch(&shift_arguments, ne, t3, Operand(zero_reg));
+
+ // Do not transform the receiver for native (Compilerhints already in a3).
+ __ 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).
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(a2, sp, at);
+ __ lw(a2, MemOperand(a2, -kPointerSize));
+ // a0: actual number of arguments
+ // a1: function
+ // a2: first argument
+ __ JumpIfSmi(a2, &convert_to_object, t2);
+
+ __ LoadRoot(a3, Heap::kUndefinedValueRootIndex);
+ __ Branch(&use_global_receiver, eq, a2, Operand(a3));
+ __ LoadRoot(a3, Heap::kNullValueRootIndex);
+ __ Branch(&use_global_receiver, eq, a2, Operand(a3));
+
+ STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
+ __ GetObjectType(a2, a3, a3);
+ __ Branch(&shift_arguments, ge, a3, Operand(FIRST_SPEC_OBJECT_TYPE));
+
+ __ bind(&convert_to_object);
+ // 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::NONE32));
+ __ Branch(&patch_receiver);
+
+ // Use the global receiver object from the called function as the
+ // receiver.
+ __ bind(&use_global_receiver);
+ const int kGlobalIndex =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ __ lw(a2, FieldMemOperand(cp, kGlobalIndex));
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kNativeContextOffset));
+ __ lw(a2, FieldMemOperand(a2, kGlobalIndex));
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalReceiverOffset));
+
+ __ bind(&patch_receiver);
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(a3, sp, at);
+ __ sw(a2, MemOperand(a3, -kPointerSize));
+
+ __ Branch(&shift_arguments);
+ }
+
+ // 3b. Check for function proxy.
+ __ bind(&slow);
+ __ li(t0, Operand(1, RelocInfo::NONE32)); // Indicate function proxy.
+ __ Branch(&shift_arguments, eq, a2, Operand(JS_FUNCTION_PROXY_TYPE));
+
+ __ bind(&non_function);
+ __ li(t0, Operand(2, RelocInfo::NONE32)); // 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
+ // 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));
+
+ // 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.
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(a2, sp, at);
+
+ __ bind(&loop);
+ __ lw(at, MemOperand(a2, -kPointerSize));
+ __ sw(at, MemOperand(a2));
+ __ Subu(a2, a2, Operand(kPointerSize));
+ __ Branch(&loop, ne, a2, Operand(sp));
+ // Adjust the actual number of arguments and remove the top element
+ // (which is a copy of the last argument).
+ __ Subu(a0, a0, Operand(1));
+ __ Pop();
+ }
+
+ // 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
+ // 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);
+ }
+
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ // a0: actual number of arguments
+ // a1: function
+ __ lw(a3, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(a2,
+ FieldMemOperand(a3, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ sra(a2, a2, kSmiTagSize);
+ __ lw(a3, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
+ __ SetCallKind(t1, CALL_AS_METHOD);
+ // Check formal and actual parameter counts.
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET, ne, a2, Operand(a0));
+
+ ParameterCount expected(0);
+ __ InvokeCode(a3, expected, expected, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
+}
+
+
+void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
+ const int kIndexOffset = -5 * kPointerSize;
+ const int kLimitOffset = -4 * kPointerSize;
+ const int kArgsOffset = 2 * kPointerSize;
+ const int kRecvOffset = 3 * kPointerSize;
+ const int kFunctionOffset = 4 * kPointerSize;
+
+ {
+ 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);
+
+ // Get the receiver.
+ __ lw(a0, MemOperand(fp, kRecvOffset));
+
+ // 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));
+
+ // 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_OBJECT_INDEX * kPointerSize;
+ __ lw(a0, FieldMemOperand(cp, kGlobalOffset));
+ __ lw(a0, FieldMemOperand(a0, GlobalObject::kNativeContextOffset));
+ __ 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::NONE32));
+ __ 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.
+}
+
+
+static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
+ __ sll(a0, a0, kSmiTagSize);
+ __ li(t0, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ MultiPush(a0.bit() | a1.bit() | t0.bit() | fp.bit() | ra.bit());
+ __ Addu(fp, sp, Operand(3 * kPointerSize));
+}
+
+
+static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- v0 : result being passed through
+ // -----------------------------------
+ // Get the number of arguments passed (as a smi), tear down the frame and
+ // then tear down the parameters.
+ __ lw(a1, MemOperand(fp, -3 * kPointerSize));
+ __ mov(sp, fp);
+ __ MultiPop(fp.bit() | ra.bit());
+ __ sll(t0, a1, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(sp, sp, t0);
+ // Adjust for the receiver.
+ __ Addu(sp, sp, Operand(kPointerSize));
+}
+
+
+void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
+ // State setup as expected by MacroAssembler::InvokePrologue.
+ // ----------- S t a t e -------------
+ // -- a0: actual arguments count
+ // -- a1: function (passed through to callee)
+ // -- a2: expected arguments count
+ // -- a3: callee code entry
+ // -- t1: call kind information
+ // -----------------------------------
+
+ Label invoke, dont_adapt_arguments;
+
+ Label enough, too_few;
+ __ Branch(&dont_adapt_arguments, eq,
+ a2, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
+ // We use Uless as the number of argument should always be greater than 0.
+ __ Branch(&too_few, Uless, a0, Operand(a2));
+
+ { // Enough parameters: actual >= expected.
+ // a0: actual number of arguments as a smi
+ // a1: function
+ // a2: expected number of arguments
+ // a3: code entry to call
+ __ bind(&enough);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Calculate copy start address into a0 and copy end address into a2.
+ __ sll(a0, a0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a0, fp, a0);
+ // Adjust for return address and receiver.
+ __ Addu(a0, a0, Operand(2 * kPointerSize));
+ // Compute copy end address.
+ __ sll(a2, a2, kPointerSizeLog2);
+ __ subu(a2, a0, a2);
+
+ // Copy the arguments (including the receiver) to the new stack frame.
+ // a0: copy start address
+ // a1: function
+ // a2: copy end address
+ // a3: code entry to call
+
+ Label copy;
+ __ bind(&copy);
+ __ lw(t0, MemOperand(a0));
+ __ push(t0);
+ __ Branch(USE_DELAY_SLOT, &copy, ne, a0, Operand(a2));
+ __ addiu(a0, a0, -kPointerSize); // In delay slot.
+
+ __ jmp(&invoke);
+ }
+
+ { // Too few parameters: Actual < expected.
+ __ bind(&too_few);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Calculate copy start address into a0 and copy end address is fp.
+ // a0: actual number of arguments as a smi
+ // a1: function
+ // a2: expected number of arguments
+ // a3: code entry to call
+ __ sll(a0, a0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a0, fp, a0);
+ // Adjust for return address and receiver.
+ __ Addu(a0, a0, Operand(2 * kPointerSize));
+ // Compute copy end address. Also adjust for return address.
+ __ Addu(t3, fp, kPointerSize);
+
+ // Copy the arguments (including the receiver) to the new stack frame.
+ // a0: copy start address
+ // a1: function
+ // a2: expected number of arguments
+ // a3: code entry to call
+ // t3: copy end address
+ Label copy;
+ __ bind(&copy);
+ __ lw(t0, MemOperand(a0)); // Adjusted above for return addr and receiver.
+ __ Subu(sp, sp, kPointerSize);
+ __ Subu(a0, a0, kPointerSize);
+ __ Branch(USE_DELAY_SLOT, &copy, ne, a0, Operand(t3));
+ __ sw(t0, MemOperand(sp)); // In the delay slot.
+
+ // Fill the remaining expected arguments with undefined.
+ // a1: function
+ // a2: expected number of arguments
+ // a3: code entry to call
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ sll(t2, a2, kPointerSizeLog2);
+ __ Subu(a2, fp, Operand(t2));
+ __ Addu(a2, a2, Operand(-4 * kPointerSize)); // Adjust for frame.
+
+ Label fill;
+ __ bind(&fill);
+ __ Subu(sp, sp, kPointerSize);
+ __ Branch(USE_DELAY_SLOT, &fill, ne, sp, Operand(a2));
+ __ sw(t0, MemOperand(sp));
+ }
+
+ // Call the entry point.
+ __ bind(&invoke);
+
+ __ Call(a3);
+
+ // Store offset of return address for deoptimizer.
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
+
+ // Exit frame and return.
+ LeaveArgumentsAdaptorFrame(masm);
+ __ Ret();
+
+
+ // -------------------------------------------
+ // Don't adapt arguments.
+ // -------------------------------------------
+ __ bind(&dont_adapt_arguments);
+ __ Jump(a3);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/code-stubs-mips.cc b/chromium/v8/src/mips/code-stubs-mips.cc
new file mode 100644
index 00000000000..8a03a9a31a5
--- /dev/null
+++ b/chromium/v8/src/mips/code-stubs-mips.cc
@@ -0,0 +1,7588 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "bootstrapper.h"
+#include "code-stubs.h"
+#include "codegen.h"
+#include "regexp-macro-assembler.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+
+void ToNumberStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void FastCloneShallowArrayStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a3, a2, a1 };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kCreateArrayLiteralShallow)->entry;
+}
+
+
+void FastCloneShallowObjectStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a3, a2, a1, a0 };
+ descriptor->register_param_count_ = 4;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kCreateObjectLiteralShallow)->entry;
+}
+
+
+void CreateAllocationSiteStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a2 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedLoadFastElementStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a1, a0 };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure);
+}
+
+
+void LoadFieldStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedLoadFieldStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a1 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedStoreFastElementStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a2, a1, a0 };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure);
+}
+
+
+void TransitionElementsKindStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a0, a1 };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ Address entry =
+ Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry;
+ descriptor->deoptimization_handler_ = FUNCTION_ADDR(entry);
+}
+
+
+void CompareNilICStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(CompareNilIC_Miss);
+ descriptor->SetMissHandler(
+ ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate));
+}
+
+
+static void InitializeArrayConstructorDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor,
+ int constant_stack_parameter_count) {
+ // register state
+ // a0 -- number of arguments
+ // a1 -- function
+ // a2 -- type info cell with elements kind
+ static Register registers[] = { a1, a2 };
+ descriptor->register_param_count_ = 2;
+ if (constant_stack_parameter_count != 0) {
+ // stack param count needs (constructor pointer, and single argument)
+ descriptor->stack_parameter_count_ = &a0;
+ }
+ descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
+ descriptor->register_params_ = registers;
+ descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
+}
+
+
+static void InitializeInternalArrayConstructorDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor,
+ int constant_stack_parameter_count) {
+ // register state
+ // a0 -- number of arguments
+ // a1 -- constructor function
+ static Register registers[] = { a1 };
+ descriptor->register_param_count_ = 1;
+
+ if (constant_stack_parameter_count != 0) {
+ // Stack param count needs (constructor pointer, and single argument).
+ descriptor->stack_parameter_count_ = &a0;
+ }
+ descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
+ descriptor->register_params_ = registers;
+ descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
+}
+
+
+void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, 0);
+}
+
+
+void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, 1);
+}
+
+
+void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, -1);
+}
+
+
+void ToBooleanStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a0 };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(ToBooleanIC_Miss);
+ descriptor->SetMissHandler(
+ ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate));
+}
+
+
+void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0);
+}
+
+
+void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1);
+}
+
+
+void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1);
+}
+
+
+void StoreGlobalStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a1, a2, a0 };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(StoreIC_MissFromStubFailure);
+}
+
+
+void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { a0, a3, a1, a2 };
+ descriptor->register_param_count_ = 4;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss);
+}
+
+
+#define __ ACCESS_MASM(masm)
+
+
+static void EmitIdenticalObjectComparison(MacroAssembler* masm,
+ Label* slow,
+ Condition cc);
+static void EmitSmiNonsmiComparison(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* rhs_not_nan,
+ Label* slow,
+ bool strict);
+static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
+ Register lhs,
+ Register rhs);
+
+
+void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
+ // Update the static counter each time a new code stub is generated.
+ Isolate* isolate = masm->isolate();
+ isolate->counters()->code_stubs()->Increment();
+
+ CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate);
+ int param_count = descriptor->register_param_count_;
+ {
+ // Call the runtime system in a fresh internal frame.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ ASSERT(descriptor->register_param_count_ == 0 ||
+ a0.is(descriptor->register_params_[param_count - 1]));
+ // Push arguments
+ for (int i = 0; i < param_count; ++i) {
+ __ push(descriptor->register_params_[i]);
+ }
+ ExternalReference miss = descriptor->miss_handler();
+ __ CallExternalReference(miss, descriptor->register_param_count_);
+ }
+
+ __ Ret();
+}
+
+
+void FastNewClosureStub::Generate(MacroAssembler* masm) {
+ // Create a new closure from the given function info in new
+ // space. Set the context to the current context in cp.
+ Counters* counters = masm->isolate()->counters();
+
+ Label gc;
+
+ // Pop the function info from the stack.
+ __ pop(a3);
+
+ // Attempt to allocate new JSFunction in new space.
+ __ Allocate(JSFunction::kSize, v0, a1, a2, &gc, TAG_OBJECT);
+
+ __ IncrementCounter(counters->fast_new_closure_total(), 1, t2, t3);
+
+ int map_index = Context::FunctionMapIndex(language_mode_, is_generator_);
+
+ // Compute the function map in the current native context and set that
+ // as the map of the allocated object.
+ __ lw(a2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kNativeContextOffset));
+ __ lw(t1, MemOperand(a2, Context::SlotOffset(map_index)));
+ __ sw(t1, FieldMemOperand(v0, HeapObject::kMapOffset));
+
+ // Initialize the rest of the function. We don't have to update the
+ // write barrier because the allocated object is in new space.
+ __ LoadRoot(a1, Heap::kEmptyFixedArrayRootIndex);
+ __ LoadRoot(t1, Heap::kTheHoleValueRootIndex);
+ __ sw(a1, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ sw(a1, FieldMemOperand(v0, JSObject::kElementsOffset));
+ __ sw(t1, FieldMemOperand(v0, JSFunction::kPrototypeOrInitialMapOffset));
+ __ sw(a3, FieldMemOperand(v0, JSFunction::kSharedFunctionInfoOffset));
+ __ sw(cp, FieldMemOperand(v0, JSFunction::kContextOffset));
+ __ sw(a1, FieldMemOperand(v0, JSFunction::kLiteralsOffset));
+
+ // Initialize the code pointer in the function to be the one
+ // found in the shared function info object.
+ // But first check if there is an optimized version for our context.
+ Label check_optimized;
+ Label install_unoptimized;
+ if (FLAG_cache_optimized_code) {
+ __ lw(a1,
+ FieldMemOperand(a3, SharedFunctionInfo::kOptimizedCodeMapOffset));
+ __ And(at, a1, a1);
+ __ Branch(&check_optimized, ne, at, Operand(zero_reg));
+ }
+ __ bind(&install_unoptimized);
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ sw(t0, FieldMemOperand(v0, JSFunction::kNextFunctionLinkOffset));
+ __ lw(a3, FieldMemOperand(a3, SharedFunctionInfo::kCodeOffset));
+ __ Addu(a3, a3, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Return result. The argument function info has been popped already.
+ __ Ret(USE_DELAY_SLOT);
+ __ sw(a3, FieldMemOperand(v0, JSFunction::kCodeEntryOffset));
+
+ __ bind(&check_optimized);
+
+ __ IncrementCounter(counters->fast_new_closure_try_optimized(), 1, t2, t3);
+
+ // a2 holds native context, a1 points to fixed array of 3-element entries
+ // (native context, optimized code, literals).
+ // The optimized code map must never be empty, so check the first elements.
+ Label install_optimized;
+ // Speculatively move code object into t0.
+ __ lw(t0, FieldMemOperand(a1, SharedFunctionInfo::kFirstCodeSlot));
+ __ lw(t1, FieldMemOperand(a1, SharedFunctionInfo::kFirstContextSlot));
+ __ Branch(&install_optimized, eq, a2, Operand(t1));
+
+ // Iterate through the rest of map backwards. t0 holds an index as a Smi.
+ Label loop;
+ __ lw(t0, FieldMemOperand(a1, FixedArray::kLengthOffset));
+ __ bind(&loop);
+ // Do not double check first entry.
+ __ Branch(&install_unoptimized, eq, t0,
+ Operand(Smi::FromInt(SharedFunctionInfo::kSecondEntryIndex)));
+ __ Subu(t0, t0, Operand(Smi::FromInt(SharedFunctionInfo::kEntryLength)));
+ __ Addu(t1, a1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(at, t0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(t1, t1, Operand(at));
+ __ lw(t1, MemOperand(t1));
+ __ Branch(&loop, ne, a2, Operand(t1));
+ // Hit: fetch the optimized code.
+ __ Addu(t1, a1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(at, t0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(t1, t1, Operand(at));
+ __ Addu(t1, t1, Operand(kPointerSize));
+ __ lw(t0, MemOperand(t1));
+
+ __ bind(&install_optimized);
+ __ IncrementCounter(counters->fast_new_closure_install_optimized(),
+ 1, t2, t3);
+
+ // TODO(fschneider): Idea: store proper code pointers in the map and either
+ // unmangle them on marking or do nothing as the whole map is discarded on
+ // major GC anyway.
+ __ Addu(t0, t0, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ sw(t0, FieldMemOperand(v0, JSFunction::kCodeEntryOffset));
+
+ // Now link a function into a list of optimized functions.
+ __ lw(t0, ContextOperand(a2, Context::OPTIMIZED_FUNCTIONS_LIST));
+
+ __ sw(t0, FieldMemOperand(v0, JSFunction::kNextFunctionLinkOffset));
+ // No need for write barrier as JSFunction (eax) is in the new space.
+
+ __ sw(v0, ContextOperand(a2, Context::OPTIMIZED_FUNCTIONS_LIST));
+ // Store JSFunction (eax) into edx before issuing write barrier as
+ // it clobbers all the registers passed.
+ __ mov(t0, v0);
+ __ RecordWriteContextSlot(
+ a2,
+ Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST),
+ t0,
+ a1,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs);
+
+ // Return result. The argument function info has been popped already.
+ __ Ret();
+
+ // Create a new closure through the slower runtime call.
+ __ bind(&gc);
+ __ LoadRoot(t0, Heap::kFalseValueRootIndex);
+ __ Push(cp, a3, t0);
+ __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
+}
+
+
+void FastNewContextStub::Generate(MacroAssembler* masm) {
+ // Try to allocate the context in new space.
+ Label gc;
+ int length = slots_ + Context::MIN_CONTEXT_SLOTS;
+
+ // Attempt to allocate the context in new space.
+ __ Allocate(FixedArray::SizeFor(length), v0, a1, a2, &gc, TAG_OBJECT);
+
+ // Load the function from the stack.
+ __ lw(a3, MemOperand(sp, 0));
+
+ // 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));
+
+ // Set up the fixed slots, copy the global object from the previous context.
+ __ lw(a2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_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)));
+ __ sw(a2, MemOperand(v0, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+
+ // Initialize the rest of the slots to undefined.
+ __ LoadRoot(a1, Heap::kUndefinedValueRootIndex);
+ for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
+ __ sw(a1, MemOperand(v0, Context::SlotOffset(i)));
+ }
+
+ // Remove the on-stack argument and return.
+ __ mov(cp, v0);
+ __ DropAndRet(1);
+
+ // Need to collect. Call into runtime system.
+ __ bind(&gc);
+ __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
+}
+
+
+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;
+ __ Allocate(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 native context we get a smi
+ // sentinel instead of a function. The block context should get the
+ // canonical empty function of the native context as its closure which
+ // we still have to look up.
+ Label after_sentinel;
+ __ JumpIfNotSmi(a3, &after_sentinel);
+ if (FLAG_debug_code) {
+ __ Assert(eq, kExpected0AsASmiSentinel, a3, Operand(zero_reg));
+ }
+ __ lw(a3, GlobalObjectOperand());
+ __ lw(a3, FieldMemOperand(a3, GlobalObject::kNativeContextOffset));
+ __ 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_OBJECT_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_OBJECT_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);
+ __ DropAndRet(2);
+
+ // Need to collect. Call into runtime system.
+ __ bind(&gc);
+ __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1);
+}
+
+
+// Takes a Smi and converts to an IEEE 64 bit floating point value in two
+// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
+// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
+// scratch register. Destroys the source register. No GC occurs during this
+// stub so you don't have to set up the frame.
+class ConvertToDoubleStub : public PlatformCodeStub {
+ public:
+ ConvertToDoubleStub(Register result_reg_1,
+ Register result_reg_2,
+ Register source_reg,
+ Register scratch_reg)
+ : result1_(result_reg_1),
+ result2_(result_reg_2),
+ source_(source_reg),
+ zeros_(scratch_reg) { }
+
+ private:
+ Register result1_;
+ Register result2_;
+ Register source_;
+ Register zeros_;
+
+ // Minor key encoding in 16 bits.
+ class ModeBits: public BitField<OverwriteMode, 0, 2> {};
+ class OpBits: public BitField<Token::Value, 2, 14> {};
+
+ Major MajorKey() { return ConvertToDouble; }
+ int MinorKey() {
+ // Encode the parameters in a unique 16 bit value.
+ return result1_.code() +
+ (result2_.code() << 4) +
+ (source_.code() << 8) +
+ (zeros_.code() << 12);
+ }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
+#ifndef BIG_ENDIAN_FLOATING_POINT
+ Register exponent = result1_;
+ Register mantissa = result2_;
+#else
+ Register exponent = result2_;
+ Register mantissa = result1_;
+#endif
+ Label not_special;
+ // Convert from Smi to integer.
+ __ sra(source_, source_, kSmiTagSize);
+ // Move sign bit from source to destination. This works because the sign bit
+ // in the exponent word of the double has the same position and polarity as
+ // the 2's complement sign bit in a Smi.
+ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
+ __ And(exponent, source_, Operand(HeapNumber::kSignMask));
+ // Subtract from 0 if source was negative.
+ __ subu(at, zero_reg, source_);
+ __ Movn(source_, at, exponent);
+
+ // We have -1, 0 or 1, which we treat specially. Register source_ contains
+ // absolute value: it is either equal to 1 (special case of -1 and 1),
+ // greater than 1 (not a special case) or less than 1 (special case of 0).
+ __ Branch(&not_special, gt, source_, Operand(1));
+
+ // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
+ const uint32_t exponent_word_for_1 =
+ HeapNumber::kExponentBias << HeapNumber::kExponentShift;
+ // Safe to use 'at' as dest reg here.
+ __ Or(at, exponent, Operand(exponent_word_for_1));
+ __ Movn(exponent, at, source_); // Write exp when source not 0.
+ // 1, 0 and -1 all have 0 for the second word.
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(mantissa, zero_reg);
+
+ __ bind(&not_special);
+ // Count leading zeros.
+ // Gets the wrong answer for 0, but we already checked for that case above.
+ __ Clz(zeros_, source_);
+ // Compute exponent and or it into the exponent register.
+ // We use mantissa as a scratch register here.
+ __ li(mantissa, Operand(31 + HeapNumber::kExponentBias));
+ __ subu(mantissa, mantissa, zeros_);
+ __ sll(mantissa, mantissa, HeapNumber::kExponentShift);
+ __ Or(exponent, exponent, mantissa);
+
+ // Shift up the source chopping the top bit off.
+ __ Addu(zeros_, zeros_, Operand(1));
+ // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
+ __ sllv(source_, source_, zeros_);
+ // Compute lower part of fraction (last 12 bits).
+ __ sll(mantissa, source_, HeapNumber::kMantissaBitsInTopWord);
+ // And the top (top 20 bits).
+ __ srl(source_, source_, 32 - HeapNumber::kMantissaBitsInTopWord);
+
+ __ Ret(USE_DELAY_SLOT);
+ __ or_(exponent, exponent, source_);
+}
+
+
+void FloatingPointHelper::LoadSmis(MacroAssembler* masm,
+ FloatingPointHelper::Destination destination,
+ Register scratch1,
+ Register scratch2) {
+ __ sra(scratch1, a0, kSmiTagSize);
+ __ mtc1(scratch1, f14);
+ __ cvt_d_w(f14, f14);
+ __ sra(scratch1, a1, kSmiTagSize);
+ __ mtc1(scratch1, f12);
+ __ cvt_d_w(f12, f12);
+ if (destination == kCoreRegisters) {
+ __ Move(a2, a3, f14);
+ __ Move(a0, a1, f12);
+ }
+}
+
+
+void FloatingPointHelper::LoadNumber(MacroAssembler* masm,
+ Destination destination,
+ Register object,
+ FPURegister dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* not_number) {
+ __ AssertRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ kHeapNumberMapRegisterClobbered);
+
+ Label is_smi, done;
+
+ // 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.
+ if (destination == kFPURegisters) {
+ // Load the double from tagged HeapNumber to double register.
+
+ // ARM uses a workaround here because of the unaligned HeapNumber
+ // kValueOffset. On MIPS this workaround is built into ldc1 so there's no
+ // point in generating even more instructions.
+ __ ldc1(dst, FieldMemOperand(object, HeapNumber::kValueOffset));
+ } else {
+ ASSERT(destination == kCoreRegisters);
+ // Load the double from heap number to dst1 and dst2 in double format.
+ __ lw(dst1, FieldMemOperand(object, HeapNumber::kValueOffset));
+ __ lw(dst2, FieldMemOperand(object,
+ HeapNumber::kValueOffset + kPointerSize));
+ }
+ __ Branch(&done);
+
+ // Handle loading a double from a smi.
+ __ bind(&is_smi);
+ // Convert smi to double using FPU instructions.
+ __ mtc1(scratch1, dst);
+ __ cvt_d_w(dst, dst);
+ if (destination == kCoreRegisters) {
+ // Load the converted smi to dst1 and dst2 in double format.
+ __ Move(dst1, dst2, dst);
+ }
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ FPURegister double_scratch,
+ Label* not_number) {
+ __ AssertRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ kHeapNumberMapRegisterClobbered);
+ Label done;
+ Label not_in_int32_range;
+
+ __ UntagAndJumpIfSmi(dst, object, &done);
+ __ lw(scratch1, FieldMemOperand(object, HeapNumber::kMapOffset));
+ __ Branch(not_number, ne, scratch1, Operand(heap_number_map));
+ __ ConvertToInt32(object,
+ dst,
+ scratch1,
+ scratch2,
+ double_scratch,
+ &not_in_int32_range);
+ __ jmp(&done);
+
+ __ bind(&not_in_int32_range);
+ __ lw(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ lw(scratch2, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+
+ __ EmitOutOfInt32RangeTruncate(dst,
+ scratch1,
+ scratch2,
+ scratch3);
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::ConvertIntToDouble(MacroAssembler* masm,
+ Register int_scratch,
+ Destination destination,
+ FPURegister double_dst,
+ Register dst_mantissa,
+ Register dst_exponent,
+ Register scratch2,
+ FPURegister single_scratch) {
+ ASSERT(!int_scratch.is(scratch2));
+ ASSERT(!int_scratch.is(dst_mantissa));
+ ASSERT(!int_scratch.is(dst_exponent));
+
+ __ mtc1(int_scratch, single_scratch);
+ __ cvt_d_w(double_dst, single_scratch);
+ if (destination == kCoreRegisters) {
+ __ Move(dst_mantissa, dst_exponent, double_dst);
+ }
+}
+
+
+void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
+ Register object,
+ Destination destination,
+ DoubleRegister double_dst,
+ DoubleRegister double_scratch,
+ Register dst_mantissa,
+ Register dst_exponent,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ FPURegister single_scratch,
+ Label* not_int32) {
+ ASSERT(!scratch1.is(object) && !scratch2.is(object));
+ ASSERT(!scratch1.is(scratch2));
+ ASSERT(!heap_number_map.is(object) &&
+ !heap_number_map.is(scratch1) &&
+ !heap_number_map.is(scratch2));
+
+ Label done, obj_is_not_smi;
+
+ __ JumpIfNotSmi(object, &obj_is_not_smi);
+ __ SmiUntag(scratch1, object);
+ ConvertIntToDouble(masm, scratch1, destination, double_dst, dst_mantissa,
+ dst_exponent, scratch2, single_scratch);
+ __ Branch(&done);
+
+ __ bind(&obj_is_not_smi);
+ __ AssertRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ kHeapNumberMapRegisterClobbered);
+ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
+
+ // Load the number.
+ // Load the double value.
+ __ ldc1(double_dst, FieldMemOperand(object, HeapNumber::kValueOffset));
+
+ Register except_flag = scratch2;
+ __ EmitFPUTruncate(kRoundToZero,
+ scratch1,
+ double_dst,
+ at,
+ double_scratch,
+ except_flag,
+ kCheckForInexactConversion);
+
+ // Jump to not_int32 if the operation did not succeed.
+ __ Branch(not_int32, ne, except_flag, Operand(zero_reg));
+ if (destination == kCoreRegisters) {
+ __ Move(dst_mantissa, dst_exponent, double_dst);
+ }
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DoubleRegister double_scratch0,
+ DoubleRegister double_scratch1,
+ Label* not_int32) {
+ ASSERT(!dst.is(object));
+ ASSERT(!scratch1.is(object) && !scratch2.is(object) && !scratch3.is(object));
+ ASSERT(!scratch1.is(scratch2) &&
+ !scratch1.is(scratch3) &&
+ !scratch2.is(scratch3));
+
+ Label done, maybe_undefined;
+
+ __ UntagAndJumpIfSmi(dst, object, &done);
+
+ __ AssertRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ kHeapNumberMapRegisterClobbered);
+
+ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, &maybe_undefined);
+
+ // Object is a heap number.
+ // Convert the floating point value to a 32-bit integer.
+ // Load the double value.
+ __ ldc1(double_scratch0, FieldMemOperand(object, HeapNumber::kValueOffset));
+
+ Register except_flag = scratch2;
+ __ EmitFPUTruncate(kRoundToZero,
+ dst,
+ double_scratch0,
+ scratch1,
+ double_scratch1,
+ except_flag,
+ kCheckForInexactConversion);
+
+ // Jump to not_int32 if the operation did not succeed.
+ __ Branch(not_int32, ne, except_flag, Operand(zero_reg));
+ __ Branch(&done);
+
+ __ bind(&maybe_undefined);
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(not_int32, ne, object, Operand(at));
+ // |undefined| is truncated to 0.
+ __ li(dst, Operand(Smi::FromInt(0)));
+ // Fall through.
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::CallCCodeForDoubleOperation(
+ MacroAssembler* masm,
+ Token::Value op,
+ Register heap_number_result,
+ Register scratch) {
+ // Using core registers:
+ // a0: Left value (least significant part of mantissa).
+ // a1: Left value (sign, exponent, top of mantissa).
+ // a2: Right value (least significant part of mantissa).
+ // a3: Right value (sign, exponent, top of mantissa).
+
+ // Assert that heap_number_result is saved.
+ // We currently always use s0 to pass it.
+ ASSERT(heap_number_result.is(s0));
+
+ // Push the current return address before the C call.
+ __ push(ra);
+ __ PrepareCallCFunction(4, scratch); // Two doubles are 4 arguments.
+ 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
+ // calling is compiled with hard-float flag and expecting hard float ABI
+ // (parameters in f12/f14 registers). We need to copy parameters from
+ // a0-a3 registers to f12/f14 register pairs.
+ __ Move(f12, a0, a1);
+ __ Move(f14, a2, a3);
+ }
+ {
+ AllowExternalCallThatCantCauseGC scope(masm);
+ __ CallCFunction(
+ ExternalReference::double_fp_operation(op, masm->isolate()), 0, 2);
+ }
+ // Store answer in the overwritable heap number.
+ if (!IsMipsSoftFloatABI) {
+ // Double returned in register f0.
+ __ sdc1(f0, FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
+ } else {
+ // Double returned in registers v0 and v1.
+ __ sw(v1, FieldMemOperand(heap_number_result, HeapNumber::kExponentOffset));
+ __ sw(v0, FieldMemOperand(heap_number_result, HeapNumber::kMantissaOffset));
+ }
+ // Place heap_number_result in v0 and return to the pushed return address.
+ __ pop(ra);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, heap_number_result);
+}
+
+
+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(
+ Isolate* isolate) {
+ WriteInt32ToHeapNumberStub stub1(a1, v0, a2, a3);
+ WriteInt32ToHeapNumberStub stub2(a2, v0, a3, a0);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ stub2.GetCode(isolate)->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;
+ // the_int_ has the answer which is a signed int32 but not a Smi.
+ // We test for the special value that has a different exponent.
+ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
+ // Test sign, and save for later conditionals.
+ __ And(sign_, the_int_, Operand(0x80000000u));
+ __ Branch(&max_negative_int, eq, the_int_, Operand(0x80000000u));
+
+ // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
+ // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
+ uint32_t non_smi_exponent =
+ (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
+ __ li(scratch_, Operand(non_smi_exponent));
+ // Set the sign bit in scratch_ if the value was negative.
+ __ or_(scratch_, scratch_, sign_);
+ // Subtract from 0 if the value was negative.
+ __ subu(at, zero_reg, the_int_);
+ __ Movn(the_int_, at, sign_);
+ // We should be masking the implict first digit of the mantissa away here,
+ // but it just ends up combining harmlessly with the last digit of the
+ // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
+ // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
+ ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
+ const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
+ __ srl(at, the_int_, shift_distance);
+ __ or_(scratch_, scratch_, at);
+ __ sw(scratch_, FieldMemOperand(the_heap_number_,
+ HeapNumber::kExponentOffset));
+ __ sll(scratch_, the_int_, 32 - shift_distance);
+ __ Ret(USE_DELAY_SLOT);
+ __ sw(scratch_, FieldMemOperand(the_heap_number_,
+ HeapNumber::kMantissaOffset));
+
+ __ bind(&max_negative_int);
+ // The max negative int32 is stored as a positive number in the mantissa of
+ // a double because it uses a sign bit instead of using two's complement.
+ // The actual mantissa bits stored are all 0 because the implicit most
+ // significant 1 bit is not stored.
+ non_smi_exponent += 1 << HeapNumber::kExponentShift;
+ __ li(scratch_, Operand(HeapNumber::kSignMask | non_smi_exponent));
+ __ sw(scratch_,
+ FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
+ __ mov(scratch_, zero_reg);
+ __ Ret(USE_DELAY_SLOT);
+ __ sw(scratch_,
+ FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
+}
+
+
+// Handle the case where the lhs and rhs are the same object.
+// Equality is almost reflexive (everything but NaN), so this is a test
+// for "identity and not NaN".
+static void EmitIdenticalObjectComparison(MacroAssembler* masm,
+ Label* slow,
+ Condition cc) {
+ Label not_identical;
+ Label heap_number, return_equal;
+ Register exp_mask_reg = t5;
+
+ __ Branch(&not_identical, ne, a0, Operand(a1));
+
+ __ li(exp_mask_reg, Operand(HeapNumber::kExponentMask));
+
+ // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
+ // so we do the second best thing - test it ourselves.
+ // They are both equal and they are not both Smis so both of them are not
+ // Smis. If it's not a heap number, then return equal.
+ if (cc == less || cc == greater) {
+ __ GetObjectType(a0, t4, t4);
+ __ Branch(slow, greater, t4, Operand(FIRST_SPEC_OBJECT_TYPE));
+ } else {
+ __ GetObjectType(a0, t4, t4);
+ __ Branch(&heap_number, eq, t4, Operand(HEAP_NUMBER_TYPE));
+ // Comparing JS objects with <=, >= is complicated.
+ if (cc != eq) {
+ __ Branch(slow, greater, t4, Operand(FIRST_SPEC_OBJECT_TYPE));
+ // Normally here we fall through to return_equal, but undefined is
+ // special: (undefined == undefined) == true, but
+ // (undefined <= undefined) == false! See ECMAScript 11.8.5.
+ if (cc == less_equal || cc == greater_equal) {
+ __ Branch(&return_equal, ne, t4, Operand(ODDBALL_TYPE));
+ __ LoadRoot(t2, Heap::kUndefinedValueRootIndex);
+ __ Branch(&return_equal, ne, a0, Operand(t2));
+ ASSERT(is_int16(GREATER) && is_int16(LESS));
+ __ Ret(USE_DELAY_SLOT);
+ if (cc == le) {
+ // undefined <= undefined should fail.
+ __ li(v0, Operand(GREATER));
+ } else {
+ // undefined >= undefined should fail.
+ __ li(v0, Operand(LESS));
+ }
+ }
+ }
+ }
+
+ __ bind(&return_equal);
+ ASSERT(is_int16(GREATER) && is_int16(LESS));
+ __ Ret(USE_DELAY_SLOT);
+ if (cc == less) {
+ __ li(v0, Operand(GREATER)); // Things aren't less than themselves.
+ } else if (cc == greater) {
+ __ li(v0, Operand(LESS)); // Things aren't greater than themselves.
+ } else {
+ __ mov(v0, zero_reg); // Things are <=, >=, ==, === themselves.
+ }
+
+ // For less and greater we don't have to check for NaN since the result of
+ // x < x is false regardless. For the others here is some code to check
+ // for NaN.
+ if (cc != lt && cc != gt) {
+ __ bind(&heap_number);
+ // It is a heap number, so return non-equal if it's NaN and equal if it's
+ // not NaN.
+
+ // The representation of NaN values has all exponent bits (52..62) set,
+ // and not all mantissa bits (0..51) clear.
+ // Read top bits of double representation (second word of value).
+ __ lw(t2, FieldMemOperand(a0, HeapNumber::kExponentOffset));
+ // Test that exponent bits are all set.
+ __ And(t3, t2, Operand(exp_mask_reg));
+ // If all bits not set (ne cond), then not a NaN, objects are equal.
+ __ Branch(&return_equal, ne, t3, Operand(exp_mask_reg));
+
+ // Shift out flag and all exponent bits, retaining only mantissa.
+ __ sll(t2, t2, HeapNumber::kNonMantissaBitsInTopWord);
+ // Or with all low-bits of mantissa.
+ __ lw(t3, FieldMemOperand(a0, HeapNumber::kMantissaOffset));
+ __ Or(v0, t3, Operand(t2));
+ // For equal we already have the right value in v0: Return zero (equal)
+ // if all bits in mantissa are zero (it's an Infinity) and non-zero if
+ // not (it's a NaN). For <= and >= we need to load v0 with the failing
+ // value if it's a NaN.
+ if (cc != eq) {
+ // All-zero means Infinity means equal.
+ __ Ret(eq, v0, Operand(zero_reg));
+ ASSERT(is_int16(GREATER) && is_int16(LESS));
+ __ Ret(USE_DELAY_SLOT);
+ if (cc == le) {
+ __ li(v0, Operand(GREATER)); // NaN <= NaN should fail.
+ } else {
+ __ li(v0, Operand(LESS)); // NaN >= NaN should fail.
+ }
+ }
+ }
+ // No fall through here.
+
+ __ bind(&not_identical);
+}
+
+
+static void EmitSmiNonsmiComparison(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* both_loaded_as_doubles,
+ Label* slow,
+ bool strict) {
+ ASSERT((lhs.is(a0) && rhs.is(a1)) ||
+ (lhs.is(a1) && rhs.is(a0)));
+
+ Label lhs_is_smi;
+ __ JumpIfSmi(lhs, &lhs_is_smi);
+ // Rhs is a Smi.
+ // Check whether the non-smi is a heap number.
+ __ GetObjectType(lhs, t4, t4);
+ if (strict) {
+ // If lhs was not a number and rhs was a Smi then strict equality cannot
+ // succeed. Return non-equal (lhs is already not zero).
+ __ Ret(USE_DELAY_SLOT, ne, t4, Operand(HEAP_NUMBER_TYPE));
+ __ mov(v0, lhs);
+ } else {
+ // Smi compared non-strictly with a non-Smi non-heap-number. Call
+ // the runtime.
+ __ Branch(slow, ne, t4, Operand(HEAP_NUMBER_TYPE));
+ }
+
+ // Rhs is a smi, lhs is a number.
+ // Convert smi rhs to double.
+ __ sra(at, rhs, kSmiTagSize);
+ __ mtc1(at, f14);
+ __ cvt_d_w(f14, f14);
+ __ ldc1(f12, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+
+ // We now have both loaded as doubles.
+ __ jmp(both_loaded_as_doubles);
+
+ __ bind(&lhs_is_smi);
+ // Lhs is a Smi. Check whether the non-smi is a heap number.
+ __ GetObjectType(rhs, t4, t4);
+ if (strict) {
+ // If lhs was not a number and rhs was a Smi then strict equality cannot
+ // succeed. Return non-equal.
+ __ Ret(USE_DELAY_SLOT, ne, t4, Operand(HEAP_NUMBER_TYPE));
+ __ li(v0, Operand(1));
+ } else {
+ // Smi compared non-strictly with a non-Smi non-heap-number. Call
+ // the runtime.
+ __ Branch(slow, ne, t4, Operand(HEAP_NUMBER_TYPE));
+ }
+
+ // Lhs is a smi, rhs is a number.
+ // Convert smi lhs to double.
+ __ sra(at, lhs, kSmiTagSize);
+ __ mtc1(at, f12);
+ __ cvt_d_w(f12, f12);
+ __ ldc1(f14, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+ // Fall through to both_loaded_as_doubles.
+}
+
+
+static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
+ Register lhs,
+ Register rhs) {
+ // 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_SPEC_OBJECT_TYPE);
+ Label first_non_object;
+ // Get the type of the first operand into a2 and compare it with
+ // FIRST_SPEC_OBJECT_TYPE.
+ __ GetObjectType(lhs, a2, a2);
+ __ Branch(&first_non_object, less, a2, Operand(FIRST_SPEC_OBJECT_TYPE));
+
+ // Return non-zero.
+ Label return_not_equal;
+ __ bind(&return_not_equal);
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(1));
+
+ __ bind(&first_non_object);
+ // Check for oddballs: true, false, null, undefined.
+ __ Branch(&return_not_equal, eq, a2, Operand(ODDBALL_TYPE));
+
+ __ GetObjectType(rhs, a3, a3);
+ __ Branch(&return_not_equal, greater, a3, Operand(FIRST_SPEC_OBJECT_TYPE));
+
+ // Check for oddballs: true, false, null, undefined.
+ __ Branch(&return_not_equal, eq, a3, Operand(ODDBALL_TYPE));
+
+ // Now that we have the types we might as well check for
+ // internalized-internalized.
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ Or(a2, a2, Operand(a3));
+ __ And(at, a2, Operand(kIsNotStringMask | kIsNotInternalizedMask));
+ __ Branch(&return_not_equal, eq, at, Operand(zero_reg));
+}
+
+
+static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* both_loaded_as_doubles,
+ Label* not_heap_numbers,
+ Label* slow) {
+ __ GetObjectType(lhs, a3, a2);
+ __ Branch(not_heap_numbers, ne, a2, Operand(HEAP_NUMBER_TYPE));
+ __ lw(a2, FieldMemOperand(rhs, HeapObject::kMapOffset));
+ // If first was a heap number & second wasn't, go to slow case.
+ __ Branch(slow, ne, a3, Operand(a2));
+
+ // Both are heap numbers. Load them up then jump to the code we have
+ // for that.
+ __ ldc1(f12, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+ __ ldc1(f14, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+
+ __ jmp(both_loaded_as_doubles);
+}
+
+
+// Fast negative check for internalized-to-internalized equality.
+static void EmitCheckForInternalizedStringsOrObjects(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* possible_strings,
+ Label* not_both_strings) {
+ ASSERT((lhs.is(a0) && rhs.is(a1)) ||
+ (lhs.is(a1) && rhs.is(a0)));
+
+ // a2 is object type of rhs.
+ Label object_test;
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ And(at, a2, Operand(kIsNotStringMask));
+ __ Branch(&object_test, ne, at, Operand(zero_reg));
+ __ And(at, a2, Operand(kIsNotInternalizedMask));
+ __ Branch(possible_strings, ne, at, Operand(zero_reg));
+ __ GetObjectType(rhs, a3, a3);
+ __ Branch(not_both_strings, ge, a3, Operand(FIRST_NONSTRING_TYPE));
+ __ And(at, a3, Operand(kIsNotInternalizedMask));
+ __ Branch(possible_strings, ne, at, Operand(zero_reg));
+
+ // Both are internalized strings. We already checked they weren't the same
+ // pointer so they are not equal.
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(1)); // Non-zero indicates not equal.
+
+ __ bind(&object_test);
+ __ Branch(not_both_strings, lt, a2, Operand(FIRST_SPEC_OBJECT_TYPE));
+ __ GetObjectType(rhs, a2, a3);
+ __ Branch(not_both_strings, lt, a3, Operand(FIRST_SPEC_OBJECT_TYPE));
+
+ // If both objects are undetectable, they are equal. Otherwise, they
+ // are not equal, since they are different objects and an object is not
+ // equal to undefined.
+ __ lw(a3, FieldMemOperand(lhs, HeapObject::kMapOffset));
+ __ lbu(a2, FieldMemOperand(a2, Map::kBitFieldOffset));
+ __ lbu(a3, FieldMemOperand(a3, Map::kBitFieldOffset));
+ __ and_(a0, a2, a3);
+ __ And(a0, a0, Operand(1 << Map::kIsUndetectable));
+ __ Ret(USE_DELAY_SLOT);
+ __ xori(v0, a0, 1 << Map::kIsUndetectable);
+}
+
+
+void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_found) {
+ // Use of registers. Register result is used as a temporary.
+ Register number_string_cache = result;
+ Register mask = scratch3;
+
+ // Load the number string cache.
+ __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
+
+ // Make the hash mask from the length of the number string cache. It
+ // contains two elements (number and string) for each cache entry.
+ __ lw(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
+ // Divide length by two (length is a smi).
+ __ sra(mask, mask, kSmiTagSize + 1);
+ __ Addu(mask, mask, -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
+ // doubles is the xor of the upper and lower words. See
+ // Heap::GetNumberStringCache.
+ Isolate* isolate = masm->isolate();
+ Label is_smi;
+ Label load_result_from_cache;
+ __ JumpIfSmi(object, &is_smi);
+ __ CheckMap(object,
+ scratch1,
+ Heap::kHeapNumberMapRootIndex,
+ not_found,
+ DONT_DO_SMI_CHECK);
+
+ STATIC_ASSERT(8 == kDoubleSize);
+ __ Addu(scratch1,
+ object,
+ Operand(HeapNumber::kValueOffset - kHeapObjectTag));
+ __ lw(scratch2, MemOperand(scratch1, kPointerSize));
+ __ lw(scratch1, MemOperand(scratch1, 0));
+ __ Xor(scratch1, scratch1, Operand(scratch2));
+ __ And(scratch1, scratch1, Operand(mask));
+
+ // Calculate address of entry in string cache: each entry consists
+ // of two pointer sized fields.
+ __ sll(scratch1, scratch1, kPointerSizeLog2 + 1);
+ __ Addu(scratch1, number_string_cache, scratch1);
+
+ Register probe = mask;
+ __ lw(probe,
+ FieldMemOperand(scratch1, FixedArray::kHeaderSize));
+ __ JumpIfSmi(probe, not_found);
+ __ ldc1(f12, FieldMemOperand(object, HeapNumber::kValueOffset));
+ __ ldc1(f14, FieldMemOperand(probe, HeapNumber::kValueOffset));
+ __ BranchF(&load_result_from_cache, NULL, eq, f12, f14);
+ __ Branch(not_found);
+
+ __ bind(&is_smi);
+ Register scratch = scratch1;
+ __ sra(scratch, object, 1); // Shift away the tag.
+ __ And(scratch, mask, Operand(scratch));
+
+ // Calculate address of entry in string cache: each entry consists
+ // of two pointer sized fields.
+ __ sll(scratch, scratch, kPointerSizeLog2 + 1);
+ __ Addu(scratch, number_string_cache, scratch);
+
+ // Check if the entry is the smi we are looking for.
+ __ lw(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
+ __ Branch(not_found, ne, object, Operand(probe));
+
+ // Get the result from the cache.
+ __ bind(&load_result_from_cache);
+ __ lw(result,
+ FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
+
+ __ IncrementCounter(isolate->counters()->number_to_string_native(),
+ 1,
+ scratch1,
+ scratch2);
+}
+
+
+void NumberToStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ __ lw(a1, MemOperand(sp, 0));
+
+ // Generate code to lookup number in the number string cache.
+ GenerateLookupNumberStringCache(masm, a1, v0, a2, a3, t0, &runtime);
+ __ DropAndRet(1);
+
+ __ bind(&runtime);
+ // Handle number to string in the runtime system if not found in the cache.
+ __ TailCallRuntime(Runtime::kNumberToString, 1, 1);
+}
+
+
+static void ICCompareStub_CheckInputType(MacroAssembler* masm,
+ Register input,
+ Register scratch,
+ CompareIC::State expected,
+ Label* fail) {
+ Label ok;
+ if (expected == CompareIC::SMI) {
+ __ JumpIfNotSmi(input, fail);
+ } else if (expected == CompareIC::NUMBER) {
+ __ JumpIfSmi(input, &ok);
+ __ CheckMap(input, scratch, Heap::kHeapNumberMapRootIndex, fail,
+ DONT_DO_SMI_CHECK);
+ }
+ // We could be strict about internalized/string here, but as long as
+ // hydrogen doesn't care, the stub doesn't have to care either.
+ __ bind(&ok);
+}
+
+
+// On entry a1 and a2 are the values to be compared.
+// On exit a0 is 0, positive or negative to indicate the result of
+// the comparison.
+void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
+ Register lhs = a1;
+ Register rhs = a0;
+ Condition cc = GetCondition();
+
+ Label miss;
+ ICCompareStub_CheckInputType(masm, lhs, a2, left_, &miss);
+ ICCompareStub_CheckInputType(masm, rhs, a3, right_, &miss);
+
+ Label slow; // Call builtin.
+ Label not_smis, both_loaded_as_doubles;
+
+ Label not_two_smis, smi_done;
+ __ Or(a2, a1, a0);
+ __ JumpIfNotSmi(a2, &not_two_smis);
+ __ sra(a1, a1, 1);
+ __ sra(a0, a0, 1);
+ __ Ret(USE_DELAY_SLOT);
+ __ subu(v0, a1, a0);
+ __ bind(&not_two_smis);
+
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+ // it is certain that at least one operand isn't a smi.
+
+ // Handle the case where the objects are identical. Either returns the answer
+ // or goes to slow. Only falls through if the objects were not identical.
+ EmitIdenticalObjectComparison(masm, &slow, cc);
+
+ // If either is a Smi (we know that not both are), then they can only
+ // be strictly equal if the other is a HeapNumber.
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT_EQ(0, Smi::FromInt(0));
+ __ And(t2, lhs, Operand(rhs));
+ __ JumpIfNotSmi(t2, &not_smis, t0);
+ // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
+ // 1) Return the answer.
+ // 2) Go to slow.
+ // 3) Fall through to both_loaded_as_doubles.
+ // 4) Jump to rhs_not_nan.
+ // In cases 3 and 4 we have found out we were dealing with a number-number
+ // comparison and the numbers have been loaded into f12 and f14 as doubles,
+ // or in GP registers (a0, a1, a2, a3) depending on the presence of the FPU.
+ EmitSmiNonsmiComparison(masm, lhs, rhs,
+ &both_loaded_as_doubles, &slow, strict());
+
+ __ bind(&both_loaded_as_doubles);
+ // f12, f14 are the double representations of the left hand side
+ // and the right hand side if we have FPU. Otherwise a2, a3 represent
+ // left hand side and a0, a1 represent right hand side.
+
+ Isolate* isolate = masm->isolate();
+ Label nan;
+ __ li(t0, Operand(LESS));
+ __ li(t1, Operand(GREATER));
+ __ li(t2, Operand(EQUAL));
+
+ // Check if either rhs or lhs is NaN.
+ __ BranchF(NULL, &nan, eq, f12, f14);
+
+ // Check if LESS condition is satisfied. If true, move conditionally
+ // result to v0.
+ __ c(OLT, D, f12, f14);
+ __ Movt(v0, t0);
+ // Use previous check to store conditionally to v0 oposite condition
+ // (GREATER). If rhs is equal to lhs, this will be corrected in next
+ // check.
+ __ Movf(v0, t1);
+ // Check if EQUAL condition is satisfied. If true, move conditionally
+ // result to v0.
+ __ c(EQ, D, f12, f14);
+ __ Movt(v0, t2);
+
+ __ Ret();
+
+ __ bind(&nan);
+ // NaN comparisons always fail.
+ // Load whatever we need in v0 to make the comparison fail.
+ ASSERT(is_int16(GREATER) && is_int16(LESS));
+ __ Ret(USE_DELAY_SLOT);
+ if (cc == lt || cc == le) {
+ __ li(v0, Operand(GREATER));
+ } else {
+ __ li(v0, Operand(LESS));
+ }
+
+
+ __ bind(&not_smis);
+ // At this point we know we are dealing with two different objects,
+ // and neither of them is a Smi. The objects are in lhs_ and rhs_.
+ if (strict()) {
+ // This returns non-equal for some object types, or falls through if it
+ // was not lucky.
+ EmitStrictTwoHeapObjectCompare(masm, lhs, rhs);
+ }
+
+ Label check_for_internalized_strings;
+ Label flat_string_check;
+ // Check for heap-number-heap-number comparison. Can jump to slow case,
+ // or load both doubles and jump to the code that handles
+ // that case. If the inputs are not doubles then jumps to
+ // check_for_internalized_strings.
+ // In this case a2 will contain the type of lhs_.
+ EmitCheckForTwoHeapNumbers(masm,
+ lhs,
+ rhs,
+ &both_loaded_as_doubles,
+ &check_for_internalized_strings,
+ &flat_string_check);
+
+ __ bind(&check_for_internalized_strings);
+ if (cc == eq && !strict()) {
+ // Returns an answer for two internalized strings or two
+ // detectable objects.
+ // Otherwise jumps to string case or not both strings case.
+ // Assumes that a2 is the type of lhs_ on entry.
+ EmitCheckForInternalizedStringsOrObjects(
+ masm, lhs, rhs, &flat_string_check, &slow);
+ }
+
+ // Check for both being sequential ASCII strings, and inline if that is the
+ // case.
+ __ bind(&flat_string_check);
+
+ __ JumpIfNonSmisNotBothSequentialAsciiStrings(lhs, rhs, a2, a3, &slow);
+
+ __ IncrementCounter(isolate->counters()->string_compare_native(), 1, a2, a3);
+ if (cc == eq) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
+ lhs,
+ rhs,
+ a2,
+ a3,
+ t0);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ lhs,
+ rhs,
+ a2,
+ a3,
+ t0,
+ t1);
+ }
+ // Never falls through to here.
+
+ __ bind(&slow);
+ // Prepare for call to builtin. Push object pointers, a0 (lhs) first,
+ // a1 (rhs) second.
+ __ Push(lhs, rhs);
+ // Figure out which native to call and setup the arguments.
+ Builtins::JavaScript native;
+ if (cc == eq) {
+ native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
+ } else {
+ native = Builtins::COMPARE;
+ int ncr; // NaN compare result.
+ if (cc == lt || cc == le) {
+ ncr = GREATER;
+ } else {
+ ASSERT(cc == gt || cc == ge); // Remaining cases.
+ ncr = LESS;
+ }
+ __ li(a0, Operand(Smi::FromInt(ncr)));
+ __ push(a0);
+ }
+
+ // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ InvokeBuiltin(native, JUMP_FUNCTION);
+
+ __ bind(&miss);
+ GenerateMiss(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.
+ __ MultiPush(kJSCallerSaved | ra.bit());
+ if (save_doubles_ == kSaveFPRegs) {
+ __ 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(masm->isolate())));
+ __ CallCFunction(
+ ExternalReference::store_buffer_overflow_function(masm->isolate()),
+ argument_count);
+ if (save_doubles_ == kSaveFPRegs) {
+ __ MultiPopFPU(kCallerSavedFPU);
+ }
+
+ __ MultiPop(kJSCallerSaved | ra.bit());
+ __ Ret();
+}
+
+
+void BinaryOpStub::Initialize() {
+ platform_specific_bit_ = true; // FPU is a base requirement for V8.
+}
+
+
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ Label get_result;
+
+ __ Push(a1, a0);
+
+ __ li(a2, Operand(Smi::FromInt(MinorKey())));
+ __ push(a2);
+
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
+ masm->isolate()),
+ 3,
+ 1);
+}
+
+
+void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(
+ MacroAssembler* masm) {
+ UNIMPLEMENTED();
+}
+
+
+void BinaryOpStub_GenerateSmiSmiOperation(MacroAssembler* masm,
+ Token::Value op) {
+ Register left = a1;
+ Register right = a0;
+
+ Register scratch1 = t0;
+ Register scratch2 = t1;
+
+ ASSERT(right.is(a0));
+ STATIC_ASSERT(kSmiTag == 0);
+
+ Label not_smi_result;
+ switch (op) {
+ case Token::ADD:
+ __ AdduAndCheckForOverflow(v0, left, right, scratch1);
+ __ RetOnNoOverflow(scratch1);
+ // No need to revert anything - right and left are intact.
+ break;
+ case Token::SUB:
+ __ SubuAndCheckForOverflow(v0, left, right, scratch1);
+ __ RetOnNoOverflow(scratch1);
+ // No need to revert anything - right and left are intact.
+ break;
+ case Token::MUL: {
+ // Remove tag from one of the operands. This way the multiplication result
+ // will be a smi if it fits the smi range.
+ __ SmiUntag(scratch1, right);
+ // Do multiplication.
+ // lo = lower 32 bits of scratch1 * left.
+ // hi = higher 32 bits of scratch1 * left.
+ __ Mult(left, scratch1);
+ // Check for overflowing the smi range - no overflow if higher 33 bits of
+ // the result are identical.
+ __ mflo(scratch1);
+ __ mfhi(scratch2);
+ __ sra(scratch1, scratch1, 31);
+ __ Branch(&not_smi_result, ne, scratch1, Operand(scratch2));
+ // Go slow on zero result to handle -0.
+ __ mflo(v0);
+ __ Ret(ne, v0, Operand(zero_reg));
+ // We need -0 if we were multiplying a negative number with 0 to get 0.
+ // We know one of them was zero.
+ __ Addu(scratch2, right, left);
+ Label skip;
+ // ARM uses the 'pl' condition, which is 'ge'.
+ // Negating it results in 'lt'.
+ __ Branch(&skip, lt, scratch2, Operand(zero_reg));
+ ASSERT(Smi::FromInt(0) == 0);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, zero_reg); // Return smi 0 if the non-zero one was positive.
+ __ bind(&skip);
+ // We fall through here if we multiplied a negative number with 0, because
+ // that would mean we should produce -0.
+ }
+ break;
+ case Token::DIV: {
+ Label done;
+ __ SmiUntag(scratch2, right);
+ __ SmiUntag(scratch1, left);
+ __ Div(scratch1, scratch2);
+ // A minor optimization: div may be calculated asynchronously, so we check
+ // for division by zero before getting the result.
+ __ Branch(&not_smi_result, eq, scratch2, Operand(zero_reg));
+ // If the result is 0, we need to make sure the dividsor (right) is
+ // positive, otherwise it is a -0 case.
+ // Quotient is in 'lo', remainder is in 'hi'.
+ // Check for no remainder first.
+ __ mfhi(scratch1);
+ __ Branch(&not_smi_result, ne, scratch1, Operand(zero_reg));
+ __ mflo(scratch1);
+ __ Branch(&done, ne, scratch1, Operand(zero_reg));
+ __ Branch(&not_smi_result, lt, scratch2, Operand(zero_reg));
+ __ bind(&done);
+ // Check that the signed result fits in a Smi.
+ __ Addu(scratch2, scratch1, Operand(0x40000000));
+ __ Branch(&not_smi_result, lt, scratch2, Operand(zero_reg));
+ __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot.
+ __ SmiTag(v0, scratch1);
+ }
+ break;
+ case Token::MOD: {
+ Label done;
+ __ SmiUntag(scratch2, right);
+ __ SmiUntag(scratch1, left);
+ __ Div(scratch1, scratch2);
+ // A minor optimization: div may be calculated asynchronously, so we check
+ // for division by 0 before calling mfhi.
+ // Check for zero on the right hand side.
+ __ Branch(&not_smi_result, eq, scratch2, Operand(zero_reg));
+ // If the result is 0, we need to make sure the dividend (left) is
+ // positive (or 0), otherwise it is a -0 case.
+ // Remainder is in 'hi'.
+ __ mfhi(scratch2);
+ __ Branch(&done, ne, scratch2, Operand(zero_reg));
+ __ Branch(&not_smi_result, lt, scratch1, Operand(zero_reg));
+ __ bind(&done);
+ // Check that the signed result fits in a Smi.
+ __ Addu(scratch1, scratch2, Operand(0x40000000));
+ __ Branch(&not_smi_result, lt, scratch1, Operand(zero_reg));
+ __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot.
+ __ SmiTag(v0, scratch2);
+ }
+ break;
+ case Token::BIT_OR:
+ __ Ret(USE_DELAY_SLOT);
+ __ or_(v0, left, right);
+ break;
+ case Token::BIT_AND:
+ __ Ret(USE_DELAY_SLOT);
+ __ and_(v0, left, right);
+ break;
+ case Token::BIT_XOR:
+ __ Ret(USE_DELAY_SLOT);
+ __ xor_(v0, left, right);
+ break;
+ case Token::SAR:
+ // Remove tags from right operand.
+ __ GetLeastBitsFromSmi(scratch1, right, 5);
+ __ srav(scratch1, left, scratch1);
+ // Smi tag result.
+ __ And(v0, scratch1, ~kSmiTagMask);
+ __ Ret();
+ break;
+ case Token::SHR:
+ // Remove tags from operands. We can't do this on a 31 bit number
+ // because then the 0s get shifted into bit 30 instead of bit 31.
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ srlv(v0, scratch1, scratch2);
+ // Unsigned shift is not allowed to produce a negative number, so
+ // check the sign bit and the sign bit after Smi tagging.
+ __ And(scratch1, v0, Operand(0xc0000000));
+ __ Branch(&not_smi_result, ne, scratch1, Operand(zero_reg));
+ // Smi tag result.
+ __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot.
+ __ SmiTag(v0);
+ break;
+ case Token::SHL:
+ // Remove tags from operands.
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ sllv(scratch1, scratch1, scratch2);
+ // Check that the signed result fits in a Smi.
+ __ Addu(scratch2, scratch1, Operand(0x40000000));
+ __ Branch(&not_smi_result, lt, scratch2, Operand(zero_reg));
+ __ Ret(USE_DELAY_SLOT);
+ __ SmiTag(v0, scratch1); // SmiTag emits one instruction in delay slot.
+ break;
+ default:
+ UNREACHABLE();
+ }
+ __ bind(&not_smi_result);
+}
+
+
+void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
+ Register result,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ OverwriteMode mode);
+
+
+void BinaryOpStub_GenerateFPOperation(MacroAssembler* masm,
+ BinaryOpIC::TypeInfo left_type,
+ BinaryOpIC::TypeInfo right_type,
+ bool smi_operands,
+ Label* not_numbers,
+ Label* gc_required,
+ Label* miss,
+ Token::Value op,
+ OverwriteMode mode) {
+ Register left = a1;
+ Register right = a0;
+ Register scratch1 = t3;
+ Register scratch2 = t5;
+ Register scratch3 = t0;
+
+ ASSERT(smi_operands || (not_numbers != NULL));
+ if (smi_operands) {
+ __ AssertSmi(left);
+ __ AssertSmi(right);
+ }
+ if (left_type == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(left, miss);
+ }
+ if (right_type == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(right, miss);
+ }
+
+ Register heap_number_map = t2;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ switch (op) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD: {
+ // Load left and right operands into f12 and f14 or a0/a1 and a2/a3
+ // depending on operation.
+ FloatingPointHelper::Destination destination =
+ op != Token::MOD ?
+ FloatingPointHelper::kFPURegisters :
+ FloatingPointHelper::kCoreRegisters;
+
+ // Allocate new heap number for result.
+ Register result = s0;
+ BinaryOpStub_GenerateHeapResultAllocation(
+ masm, result, heap_number_map, scratch1, scratch2, gc_required, mode);
+
+ // Load the operands.
+ if (smi_operands) {
+ FloatingPointHelper::LoadSmis(masm, destination, scratch1, scratch2);
+ } else {
+ // Load right operand to f14 or a2/a3.
+ if (right_type == BinaryOpIC::INT32) {
+ FloatingPointHelper::LoadNumberAsInt32Double(
+ masm, right, destination, f14, f16, a2, a3, heap_number_map,
+ scratch1, scratch2, f2, miss);
+ } else {
+ Label* fail = (right_type == BinaryOpIC::NUMBER) ? miss : not_numbers;
+ FloatingPointHelper::LoadNumber(
+ masm, destination, right, f14, a2, a3, heap_number_map,
+ scratch1, scratch2, fail);
+ }
+ // Load left operand to f12 or a0/a1. This keeps a0/a1 intact if it
+ // jumps to |miss|.
+ if (left_type == BinaryOpIC::INT32) {
+ FloatingPointHelper::LoadNumberAsInt32Double(
+ masm, left, destination, f12, f16, a0, a1, heap_number_map,
+ scratch1, scratch2, f2, miss);
+ } else {
+ Label* fail = (left_type == BinaryOpIC::NUMBER) ? miss : not_numbers;
+ FloatingPointHelper::LoadNumber(
+ masm, destination, left, f12, a0, a1, heap_number_map,
+ scratch1, scratch2, fail);
+ }
+ }
+
+ // Calculate the result.
+ if (destination == FloatingPointHelper::kFPURegisters) {
+ // Using FPU registers:
+ // f12: Left value.
+ // f14: Right value.
+ switch (op) {
+ case Token::ADD:
+ __ add_d(f10, f12, f14);
+ break;
+ case Token::SUB:
+ __ sub_d(f10, f12, f14);
+ break;
+ case Token::MUL:
+ __ mul_d(f10, f12, f14);
+ break;
+ case Token::DIV:
+ __ div_d(f10, f12, f14);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // ARM uses a workaround here because of the unaligned HeapNumber
+ // kValueOffset. On MIPS this workaround is built into sdc1 so
+ // there's no point in generating even more instructions.
+ __ sdc1(f10, FieldMemOperand(result, HeapNumber::kValueOffset));
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, result);
+ } else {
+ // Call the C function to handle the double operation.
+ FloatingPointHelper::CallCCodeForDoubleOperation(masm,
+ op,
+ result,
+ scratch1);
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
+ }
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::SHL: {
+ if (smi_operands) {
+ __ SmiUntag(a3, left);
+ __ SmiUntag(a2, right);
+ } else {
+ // Convert operands to 32-bit integers. Right in a2 and left in a3.
+ FloatingPointHelper::ConvertNumberToInt32(masm,
+ left,
+ a3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ f0,
+ not_numbers);
+ FloatingPointHelper::ConvertNumberToInt32(masm,
+ right,
+ a2,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ f0,
+ not_numbers);
+ }
+ Label result_not_a_smi;
+ switch (op) {
+ case Token::BIT_OR:
+ __ Or(a2, a3, Operand(a2));
+ break;
+ case Token::BIT_XOR:
+ __ Xor(a2, a3, Operand(a2));
+ break;
+ case Token::BIT_AND:
+ __ And(a2, a3, Operand(a2));
+ break;
+ case Token::SAR:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(a2, a2, 5);
+ __ srav(a2, a3, a2);
+ break;
+ case Token::SHR:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(a2, a2, 5);
+ __ srlv(a2, a3, a2);
+ // SHR is special because it is required to produce a positive answer.
+ // The code below for writing into heap numbers isn't capable of
+ // writing the register as an unsigned int so we go to slow case if we
+ // hit this case.
+ __ Branch(&result_not_a_smi, lt, a2, Operand(zero_reg));
+ break;
+ case Token::SHL:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(a2, a2, 5);
+ __ sllv(a2, a3, a2);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ // Check that the *signed* result fits in a smi.
+ __ Addu(a3, a2, Operand(0x40000000));
+ __ Branch(&result_not_a_smi, lt, a3, Operand(zero_reg));
+ __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot.
+ __ SmiTag(v0, a2);
+
+ // Allocate new heap number for result.
+ __ bind(&result_not_a_smi);
+ Register result = t1;
+ if (smi_operands) {
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ } else {
+ BinaryOpStub_GenerateHeapResultAllocation(
+ masm, result, heap_number_map, scratch1, scratch2, gc_required,
+ mode);
+ }
+
+ // a2: Answer as signed int32.
+ // t1: Heap number to write answer into.
+
+ // Nothing can go wrong now, so move the heap number to v0, which is the
+ // result.
+ __ mov(v0, t1);
+ // Convert the int32 in a2 to the heap number in a0. As
+ // mentioned above SHR needs to always produce a positive result.
+ __ mtc1(a2, f0);
+ if (op == Token::SHR) {
+ __ Cvt_d_uw(f0, f0, f22);
+ } else {
+ __ cvt_d_w(f0, f0);
+ }
+ // ARM uses a workaround here because of the unaligned HeapNumber
+ // kValueOffset. On MIPS this workaround is built into sdc1 so
+ // there's no point in generating even more instructions.
+ __ sdc1(f0, FieldMemOperand(v0, HeapNumber::kValueOffset));
+ __ Ret();
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+// Generate the smi code. If the operation on smis are successful this return is
+// generated. If the result is not a smi and heap number allocation is not
+// requested the code falls through. If number allocation is requested but a
+// heap number cannot be allocated the code jumps to the label gc_required.
+void BinaryOpStub_GenerateSmiCode(
+ MacroAssembler* masm,
+ Label* use_runtime,
+ Label* gc_required,
+ Token::Value op,
+ BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results,
+ OverwriteMode mode) {
+ Label not_smis;
+
+ Register left = a1;
+ Register right = a0;
+ Register scratch1 = t3;
+
+ // Perform combined smi check on both operands.
+ __ Or(scratch1, left, Operand(right));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(scratch1, &not_smis);
+
+ // If the smi-smi operation results in a smi return is generated.
+ BinaryOpStub_GenerateSmiSmiOperation(masm, op);
+
+ // If heap number results are possible generate the result in an allocated
+ // heap number.
+ if (allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS) {
+ BinaryOpStub_GenerateFPOperation(
+ masm, BinaryOpIC::UNINITIALIZED, BinaryOpIC::UNINITIALIZED, true,
+ use_runtime, gc_required, &not_smis, op, mode);
+ }
+ __ bind(&not_smis);
+}
+
+
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ Label right_arg_changed, call_runtime;
+
+ if (op_ == Token::MOD && encoded_right_arg_.has_value) {
+ // It is guaranteed that the value will fit into a Smi, because if it
+ // didn't, we wouldn't be here, see BinaryOp_Patch.
+ __ Branch(&right_arg_changed,
+ ne,
+ a0,
+ Operand(Smi::FromInt(fixed_right_arg_value())));
+ }
+
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
+ // Only allow smi results.
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, NULL, op_, NO_HEAPNUMBER_RESULTS, mode_);
+ } else {
+ // Allow heap number result and don't make a transition if a heap number
+ // cannot be allocated.
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, &call_runtime, op_, ALLOW_HEAPNUMBER_RESULTS,
+ mode_);
+ }
+
+ // Code falls through if the result is not returned as either a smi or heap
+ // number.
+ __ bind(&right_arg_changed);
+ GenerateTypeTransition(masm);
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = a1;
+ Register right = a0;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ GetObjectType(left, a2, a2);
+ __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE));
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ GetObjectType(right, a2, a2);
+ __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE));
+
+ StringAddStub string_add_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
+void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+ ASSERT(Max(left_type_, right_type_) == BinaryOpIC::INT32);
+
+ Register left = a1;
+ Register right = a0;
+ Register scratch1 = t3;
+ Register scratch2 = t5;
+ FPURegister double_scratch = f0;
+ FPURegister single_scratch = f6;
+
+ Register heap_number_result = no_reg;
+ Register heap_number_map = t2;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ Label call_runtime;
+ // Labels for type transition, used for wrong input or output types.
+ // Both label are currently actually bound to the same position. We use two
+ // different label to differentiate the cause leading to type transition.
+ Label transition;
+
+ // Smi-smi fast case.
+ Label skip;
+ __ Or(scratch1, left, right);
+ __ JumpIfNotSmi(scratch1, &skip);
+ BinaryOpStub_GenerateSmiSmiOperation(masm, op_);
+ // Fall through if the result is not a smi.
+ __ bind(&skip);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD: {
+ // It could be that only SMIs have been seen at either the left
+ // or the right operand. For precise type feedback, patch the IC
+ // again if this changes.
+ if (left_type_ == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(left, &transition);
+ }
+ if (right_type_ == BinaryOpIC::SMI) {
+ __ JumpIfNotSmi(right, &transition);
+ }
+ // Load both operands and check that they are 32-bit integer.
+ // Jump to type transition if they are not. The registers a0 and a1 (right
+ // and left) are preserved for the runtime call.
+ FloatingPointHelper::Destination destination = (op_ != Token::MOD)
+ ? FloatingPointHelper::kFPURegisters
+ : FloatingPointHelper::kCoreRegisters;
+
+ FloatingPointHelper::LoadNumberAsInt32Double(masm,
+ right,
+ destination,
+ f14,
+ f16,
+ a2,
+ a3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ f2,
+ &transition);
+ FloatingPointHelper::LoadNumberAsInt32Double(masm,
+ left,
+ destination,
+ f12,
+ f16,
+ t0,
+ t1,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ f2,
+ &transition);
+
+ if (destination == FloatingPointHelper::kFPURegisters) {
+ Label return_heap_number;
+ switch (op_) {
+ case Token::ADD:
+ __ add_d(f10, f12, f14);
+ break;
+ case Token::SUB:
+ __ sub_d(f10, f12, f14);
+ break;
+ case Token::MUL:
+ __ mul_d(f10, f12, f14);
+ break;
+ case Token::DIV:
+ __ div_d(f10, f12, f14);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (result_type_ <= BinaryOpIC::INT32) {
+ Register except_flag = scratch2;
+ const FPURoundingMode kRoundingMode = op_ == Token::DIV ?
+ kRoundToMinusInf : kRoundToZero;
+ const CheckForInexactConversion kConversion = op_ == Token::DIV ?
+ kCheckForInexactConversion : kDontCheckForInexactConversion;
+ __ EmitFPUTruncate(kRoundingMode,
+ scratch1,
+ f10,
+ at,
+ f16,
+ except_flag,
+ kConversion);
+ // If except_flag != 0, result does not fit in a 32-bit integer.
+ __ Branch(&transition, ne, except_flag, Operand(zero_reg));
+ // Try to tag the result as a Smi, return heap number on overflow.
+ __ SmiTagCheckOverflow(scratch1, scratch1, scratch2);
+ __ Branch(&return_heap_number, lt, scratch2, Operand(zero_reg));
+ // Check for minus zero, transition in that case (because we need
+ // to return a heap number).
+ Label not_zero;
+ ASSERT(kSmiTag == 0);
+ __ Branch(&not_zero, ne, scratch1, Operand(zero_reg));
+ __ mfc1(scratch2, f11);
+ __ And(scratch2, scratch2, HeapNumber::kSignMask);
+ __ Branch(&transition, ne, scratch2, Operand(zero_reg));
+ __ bind(&not_zero);
+
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, scratch1);
+ }
+
+ __ bind(&return_heap_number);
+ // Return a heap number, or fall through to type transition or runtime
+ // call if we can't.
+ // We are using FPU registers so s0 is available.
+ heap_number_result = s0;
+ BinaryOpStub_GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime,
+ mode_);
+ __ sdc1(f10,
+ FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, heap_number_result);
+
+ // A DIV operation expecting an integer result falls through
+ // to type transition.
+
+ } else {
+ if (encoded_right_arg_.has_value) {
+ __ Move(f16, fixed_right_arg_value());
+ __ BranchF(&transition, NULL, ne, f14, f16);
+ }
+
+ // We preserved a0 and a1 to be able to call runtime.
+ // Save the left value on the stack.
+ __ Push(t1, t0);
+
+ Label pop_and_call_runtime;
+
+ // Allocate a heap number to store the result.
+ heap_number_result = s0;
+ BinaryOpStub_GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &pop_and_call_runtime,
+ mode_);
+
+ // Load the left value from the value saved on the stack.
+ __ Pop(a1, a0);
+
+ // Call the C function to handle the double operation.
+ FloatingPointHelper::CallCCodeForDoubleOperation(
+ masm, op_, heap_number_result, scratch1);
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
+
+ __ bind(&pop_and_call_runtime);
+ __ Drop(2);
+ __ Branch(&call_runtime);
+ }
+
+ break;
+ }
+
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::SHL: {
+ Label return_heap_number;
+ Register scratch3 = t1;
+ // Convert operands to 32-bit integers. Right in a2 and left in a3. The
+ // registers a0 and a1 (right and left) are preserved for the runtime
+ // call.
+ FloatingPointHelper::LoadNumberAsInt32(masm,
+ left,
+ a3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ f0,
+ f2,
+ &transition);
+ FloatingPointHelper::LoadNumberAsInt32(masm,
+ right,
+ a2,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ f0,
+ f2,
+ &transition);
+
+ // The ECMA-262 standard specifies that, for shift operations, only the
+ // 5 least significant bits of the shift value should be used.
+ switch (op_) {
+ case Token::BIT_OR:
+ __ Or(a2, a3, Operand(a2));
+ break;
+ case Token::BIT_XOR:
+ __ Xor(a2, a3, Operand(a2));
+ break;
+ case Token::BIT_AND:
+ __ And(a2, a3, Operand(a2));
+ break;
+ case Token::SAR:
+ __ And(a2, a2, Operand(0x1f));
+ __ srav(a2, a3, a2);
+ break;
+ case Token::SHR:
+ __ And(a2, a2, Operand(0x1f));
+ __ srlv(a2, a3, a2);
+ // SHR is special because it is required to produce a positive answer.
+ // We only get a negative result if the shift value (a2) is 0.
+ // This result cannot be respresented as a signed 32-bit integer, try
+ // to return a heap number if we can.
+ __ Branch((result_type_ <= BinaryOpIC::INT32)
+ ? &transition
+ : &return_heap_number,
+ lt,
+ a2,
+ Operand(zero_reg));
+ break;
+ case Token::SHL:
+ __ And(a2, a2, Operand(0x1f));
+ __ sllv(a2, a3, a2);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // Check if the result fits in a smi.
+ __ Addu(scratch1, a2, Operand(0x40000000));
+ // If not try to return a heap number. (We know the result is an int32.)
+ __ Branch(&return_heap_number, lt, scratch1, Operand(zero_reg));
+ // Tag the result and return.
+ __ Ret(USE_DELAY_SLOT); // SmiTag emits one instruction in delay slot.
+ __ SmiTag(v0, a2);
+
+ __ bind(&return_heap_number);
+ heap_number_result = t1;
+ BinaryOpStub_GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime,
+ mode_);
+
+ if (op_ != Token::SHR) {
+ // Convert the result to a floating point value.
+ __ mtc1(a2, double_scratch);
+ __ cvt_d_w(double_scratch, double_scratch);
+ } else {
+ // The result must be interpreted as an unsigned 32-bit integer.
+ __ mtc1(a2, double_scratch);
+ __ Cvt_d_uw(double_scratch, double_scratch, single_scratch);
+ }
+
+ // Store the result.
+ __ sdc1(double_scratch,
+ FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, heap_number_result);
+
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+
+ // We never expect DIV to yield an integer result, so we always generate
+ // type transition code for DIV operations expecting an integer result: the
+ // code will fall through to this type transition.
+ if (transition.is_linked() ||
+ ((op_ == Token::DIV) && (result_type_ <= BinaryOpIC::INT32))) {
+ __ bind(&transition);
+ GenerateTypeTransition(masm);
+ }
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
+ Label call_runtime;
+
+ if (op_ == Token::ADD) {
+ // Handle string addition here, because it is the only operation
+ // that does not do a ToNumber conversion on the operands.
+ GenerateAddStrings(masm);
+ }
+
+ // Convert oddball arguments to numbers.
+ Label check, done;
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ Branch(&check, ne, a1, Operand(t0));
+ if (Token::IsBitOp(op_)) {
+ __ li(a1, Operand(Smi::FromInt(0)));
+ } else {
+ __ LoadRoot(a1, Heap::kNanValueRootIndex);
+ }
+ __ jmp(&done);
+ __ bind(&check);
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ Branch(&done, ne, a0, Operand(t0));
+ if (Token::IsBitOp(op_)) {
+ __ li(a0, Operand(Smi::FromInt(0)));
+ } else {
+ __ LoadRoot(a0, Heap::kNanValueRootIndex);
+ }
+ __ bind(&done);
+
+ GenerateNumberStub(masm);
+}
+
+
+void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
+ Label call_runtime, transition;
+ BinaryOpStub_GenerateFPOperation(
+ masm, left_type_, right_type_, false,
+ &transition, &call_runtime, &transition, op_, mode_);
+
+ __ bind(&transition);
+ GenerateTypeTransition(masm);
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+ Label call_runtime, call_string_add_or_runtime, transition;
+
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, &call_runtime, op_, ALLOW_HEAPNUMBER_RESULTS, mode_);
+
+ BinaryOpStub_GenerateFPOperation(
+ masm, left_type_, right_type_, false,
+ &call_string_add_or_runtime, &call_runtime, &transition, op_, mode_);
+
+ __ bind(&transition);
+ GenerateTypeTransition(masm);
+
+ __ bind(&call_string_add_or_runtime);
+ if (op_ == Token::ADD) {
+ GenerateAddStrings(masm);
+ }
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
+ ASSERT(op_ == Token::ADD);
+ Label left_not_string, call_runtime;
+
+ Register left = a1;
+ Register right = a0;
+
+ // Check if left argument is a string.
+ __ JumpIfSmi(left, &left_not_string);
+ __ GetObjectType(left, a2, a2);
+ __ Branch(&left_not_string, ge, a2, Operand(FIRST_NONSTRING_TYPE));
+
+ StringAddStub string_add_left_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_left_stub);
+
+ // Left operand is not a string, test right.
+ __ bind(&left_not_string);
+ __ JumpIfSmi(right, &call_runtime);
+ __ GetObjectType(right, a2, a2);
+ __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE));
+
+ StringAddStub string_add_right_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME));
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_right_stub);
+
+ // At least one argument is not a string.
+ __ bind(&call_runtime);
+}
+
+
+void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
+ Register result,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ OverwriteMode mode) {
+ // Code below will scratch result if allocation fails. To keep both arguments
+ // intact for the runtime call result cannot be one of these.
+ ASSERT(!result.is(a0) && !result.is(a1));
+
+ if (mode == OVERWRITE_LEFT || mode == OVERWRITE_RIGHT) {
+ Label skip_allocation, allocated;
+ Register overwritable_operand = mode == OVERWRITE_LEFT ? a1 : a0;
+ // If the overwritable operand is already an object, we skip the
+ // allocation of a heap number.
+ __ JumpIfNotSmi(overwritable_operand, &skip_allocation);
+ // Allocate a heap number for the result.
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ __ Branch(&allocated);
+ __ bind(&skip_allocation);
+ // Use object holding the overwritable operand for result.
+ __ mov(result, overwritable_operand);
+ __ bind(&allocated);
+ } else {
+ ASSERT(mode == NO_OVERWRITE);
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ }
+}
+
+
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ Push(a1, a0);
+}
+
+
+
+void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
+ // Untagged case: double input in f4, double result goes
+ // into f4.
+ // Tagged case: tagged input on top of stack and in a0,
+ // tagged result (heap number) goes into v0.
+
+ Label input_not_smi;
+ Label loaded;
+ Label calculate;
+ Label invalid_cache;
+ const Register scratch0 = t5;
+ const Register scratch1 = t3;
+ const Register cache_entry = a0;
+ const bool tagged = (argument_type_ == TAGGED);
+
+ if (tagged) {
+ // Argument is a number and is on stack and in a0.
+ // Load argument and check if it is a smi.
+ __ JumpIfNotSmi(a0, &input_not_smi);
+
+ // Input is a smi. Convert to double and load the low and high words
+ // of the double into a2, a3.
+ __ sra(t0, a0, kSmiTagSize);
+ __ mtc1(t0, f4);
+ __ cvt_d_w(f4, f4);
+ __ Move(a2, a3, f4);
+ __ Branch(&loaded);
+
+ __ bind(&input_not_smi);
+ // Check if input is a HeapNumber.
+ __ CheckMap(a0,
+ a1,
+ Heap::kHeapNumberMapRootIndex,
+ &calculate,
+ DONT_DO_SMI_CHECK);
+ // Input is a HeapNumber. Store the
+ // low and high words into a2, a3.
+ __ lw(a2, FieldMemOperand(a0, HeapNumber::kValueOffset));
+ __ lw(a3, FieldMemOperand(a0, HeapNumber::kValueOffset + 4));
+ } else {
+ // Input is untagged double in f4. Output goes to f4.
+ __ Move(a2, a3, f4);
+ }
+ __ bind(&loaded);
+ // a2 = low 32 bits of double value.
+ // a3 = high 32 bits of double value.
+ // Compute hash (the shifts are arithmetic):
+ // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
+ __ Xor(a1, a2, a3);
+ __ sra(t0, a1, 16);
+ __ Xor(a1, a1, t0);
+ __ sra(t0, a1, 8);
+ __ Xor(a1, a1, t0);
+ ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
+ __ And(a1, a1, Operand(TranscendentalCache::SubCache::kCacheSize - 1));
+
+ // a2 = low 32 bits of double value.
+ // a3 = high 32 bits of double value.
+ // a1 = TranscendentalCache::hash(double value).
+ __ li(cache_entry, Operand(
+ ExternalReference::transcendental_cache_array_address(
+ masm->isolate())));
+ // a0 points to cache array.
+ __ lw(cache_entry, MemOperand(cache_entry, type_ * sizeof(
+ Isolate::Current()->transcendental_cache()->caches_[0])));
+ // a0 points to the cache for the type type_.
+ // If NULL, the cache hasn't been initialized yet, so go through runtime.
+ __ Branch(&invalid_cache, eq, cache_entry, Operand(zero_reg));
+
+#ifdef DEBUG
+ // Check that the layout of cache elements match expectations.
+ { TranscendentalCache::SubCache::Element test_elem[2];
+ char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
+ char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
+ char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
+ char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
+ char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
+ CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
+ CHECK_EQ(0, elem_in0 - elem_start);
+ CHECK_EQ(kIntSize, elem_in1 - elem_start);
+ CHECK_EQ(2 * kIntSize, elem_out - elem_start);
+ }
+#endif
+
+ // Find the address of the a1'st entry in the cache, i.e., &a0[a1*12].
+ __ sll(t0, a1, 1);
+ __ Addu(a1, a1, t0);
+ __ sll(t0, a1, 2);
+ __ Addu(cache_entry, cache_entry, t0);
+
+ // Check if cache matches: Double value is stored in uint32_t[2] array.
+ __ lw(t0, MemOperand(cache_entry, 0));
+ __ lw(t1, MemOperand(cache_entry, 4));
+ __ lw(t2, MemOperand(cache_entry, 8));
+ __ 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);
+ __ mov(v0, t2);
+ } else {
+ // Load result into f4.
+ __ ldc1(f4, FieldMemOperand(t2, HeapNumber::kValueOffset));
+ }
+ __ Ret();
+
+ __ bind(&calculate);
+ __ IncrementCounter(
+ counters->transcendental_cache_miss(), 1, scratch0, scratch1);
+ if (tagged) {
+ __ bind(&invalid_cache);
+ __ TailCallExternalReference(ExternalReference(RuntimeFunction(),
+ masm->isolate()),
+ 1,
+ 1);
+ } else {
+ Label no_update;
+ Label skip_cache;
+
+ // Call C function to calculate the result and update the cache.
+ // a0: precalculated cache entry address.
+ // a2 and a3: parts of the double value.
+ // Store a0, a2 and a3 on stack for later before calling C function.
+ __ Push(a3, a2, cache_entry);
+ GenerateCallCFunction(masm, scratch0);
+ __ GetCFunctionDoubleResult(f4);
+
+ // Try to update the cache. If we cannot allocate a
+ // heap number, we return the result without updating.
+ __ Pop(a3, a2, cache_entry);
+ __ LoadRoot(t1, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(t2, scratch0, scratch1, t1, &no_update);
+ __ sdc1(f4, FieldMemOperand(t2, HeapNumber::kValueOffset));
+
+ __ sw(a2, MemOperand(cache_entry, 0 * kPointerSize));
+ __ sw(a3, MemOperand(cache_entry, 1 * kPointerSize));
+ __ sw(t2, MemOperand(cache_entry, 2 * kPointerSize));
+
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, cache_entry);
+
+ __ bind(&invalid_cache);
+ // The cache is invalid. Call runtime which will recreate the
+ // cache.
+ __ LoadRoot(t1, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(a0, scratch0, scratch1, t1, &skip_cache);
+ __ sdc1(f4, FieldMemOperand(a0, HeapNumber::kValueOffset));
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(a0);
+ __ CallRuntime(RuntimeFunction(), 1);
+ }
+ __ ldc1(f4, FieldMemOperand(v0, HeapNumber::kValueOffset));
+ __ Ret();
+
+ __ bind(&skip_cache);
+ // Call C function to calculate the result and answer directly
+ // without updating the cache.
+ GenerateCallCFunction(masm, scratch0);
+ __ GetCFunctionDoubleResult(f4);
+ __ bind(&no_update);
+
+ // We return the value in f4 without adding it to the cache, but
+ // we cause a scavenging GC so that future allocations will succeed.
+ {
+ 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();
+ }
+}
+
+
+void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm,
+ Register scratch) {
+ __ push(ra);
+ __ PrepareCallCFunction(2, scratch);
+ if (IsMipsSoftFloatABI) {
+ __ 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(isolate),
+ 0, 1);
+ break;
+ case TranscendentalCache::COS:
+ __ 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);
+ break;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+ __ pop(ra);
+}
+
+
+Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
+ switch (type_) {
+ // 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();
+ return Runtime::kAbort;
+ }
+}
+
+
+void StackCheckStub::Generate(MacroAssembler* masm) {
+ __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
+}
+
+
+void InterruptStub::Generate(MacroAssembler* masm) {
+ __ TailCallRuntime(Runtime::kInterrupt, 0, 1);
+}
+
+
+void MathPowStub::Generate(MacroAssembler* masm) {
+ 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));
+
+ __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex);
+
+ __ UntagAndJumpIfSmi(scratch, base, &base_is_smi);
+ __ lw(scratch, FieldMemOperand(base, JSObject::kMapOffset));
+ __ Branch(&call_runtime, ne, scratch, Operand(heapnumbermap));
+
+ __ ldc1(double_base, FieldMemOperand(base, HeapNumber::kValueOffset));
+ __ jmp(&unpack_exponent);
+
+ __ bind(&base_is_smi);
+ __ mtc1(scratch, single_scratch);
+ __ cvt_d_w(double_base, single_scratch);
+ __ bind(&unpack_exponent);
+
+ __ UntagAndJumpIfSmi(scratch, exponent, &int_exponent);
+
+ __ lw(scratch, FieldMemOperand(exponent, JSObject::kMapOffset));
+ __ Branch(&call_runtime, ne, scratch, Operand(heapnumbermap));
+ __ 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,
+ scratch,
+ double_exponent,
+ at,
+ double_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);
+ // double_scratch can be overwritten in the delay slot.
+ // 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);
+ // double_scratch can be overwritten in the delay slot.
+ // 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);
+ }
+
+ __ push(ra);
+ {
+ AllowExternalCallThatCantCauseGC scope(masm);
+ __ PrepareCallCFunction(0, 2, scratch2);
+ __ 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);
+ }
+
+ // 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));
+ 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(&done);
+ __ IncrementCounter(counters->math_pow(), 1, scratch, scratch2);
+ __ Ret();
+ }
+}
+
+
+bool CEntryStub::NeedsImmovableCode() {
+ return true;
+}
+
+
+bool CEntryStub::IsPregenerated() {
+ return (!save_doubles_ || ISOLATE->fp_stubs_generated()) &&
+ result_size_ == 1;
+}
+
+
+void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) {
+ CEntryStub::GenerateAheadOfTime(isolate);
+ WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ StubFailureTrampolineStub::GenerateAheadOfTime(isolate);
+ RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
+ CreateAllocationSiteStub::GenerateAheadOfTime(isolate);
+}
+
+
+void CodeStub::GenerateFPStubs(Isolate* isolate) {
+ SaveFPRegsMode mode = kSaveFPRegs;
+ CEntryStub save_doubles(1, mode);
+ StoreBufferOverflowStub stub(mode);
+ // These stubs might already be in the snapshot, detect that and don't
+ // regenerate, which would lead to code stub initialization state being messed
+ // up.
+ Code* save_doubles_code;
+ if (!save_doubles.FindCodeInCache(&save_doubles_code, isolate)) {
+ save_doubles_code = *save_doubles.GetCode(isolate);
+ }
+ Code* store_buffer_overflow_code;
+ if (!stub.FindCodeInCache(&store_buffer_overflow_code, isolate)) {
+ store_buffer_overflow_code = *stub.GetCode(isolate);
+ }
+ save_doubles_code->set_is_pregenerated(true);
+ store_buffer_overflow_code->set_is_pregenerated(true);
+ isolate->set_fp_stubs_generated(true);
+}
+
+
+void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
+ CEntryStub stub(1, kDontSaveFPRegs);
+ Handle<Code> code = stub.GetCode(isolate);
+ code->set_is_pregenerated(true);
+}
+
+
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ andi(scratch, value, 0xf);
+ __ Branch(oom_label, eq, scratch, Operand(0xf));
+}
+
+
+void CEntryStub::GenerateCore(MacroAssembler* masm,
+ Label* throw_normal_exception,
+ Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
+ bool do_gc,
+ bool always_allocate) {
+ // v0: result parameter for PerformGC, if any
+ // s0: number of arguments including receiver (C callee-saved)
+ // 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, 0, a1);
+ __ CallCFunction(ExternalReference::perform_gc_function(isolate), 1, 0);
+ }
+
+ ExternalReference scope_depth =
+ ExternalReference::heap_always_allocate_scope_depth(isolate);
+ if (always_allocate) {
+ __ li(a0, Operand(scope_depth));
+ __ lw(a1, MemOperand(a0));
+ __ Addu(a1, a1, Operand(1));
+ __ sw(a1, MemOperand(a0));
+ }
+
+ // Prepare arguments for C routine.
+ // a0 = argc
+ __ mov(a0, s0);
+ // a1 = argv (set in the delay slot after find_ra below).
+
+ // We are calling compiled C/C++ code. a0 and a1 hold our two arguments. We
+ // also need to reserve the 4 argument slots on the stack.
+
+ __ AssertStackIsAligned();
+
+ __ li(a2, Operand(ExternalReference::isolate_address(isolate)));
+
+ // To let the GC traverse the return address of the exit frames, we need to
+ // know where the return address is. The CEntryStub is unmovable, so
+ // we can store the address on the stack to be able to find it again and
+ // we never have to restore it, because it will not change.
+ { Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
+ // This branch-and-link sequence is needed to find the current PC on mips,
+ // saved to the ra register.
+ // Use masm-> here instead of the double-underscore macro since extra
+ // coverage code can interfere with the proper calculation of ra.
+ Label find_ra;
+ masm->bal(&find_ra); // bal exposes branch delay slot.
+ masm->mov(a1, s1);
+ masm->bind(&find_ra);
+
+ // Adjust the value in ra to point to the correct return location, 2nd
+ // instruction past the real call into C code (the jalr(t9)), and push it.
+ // This is the return address of the exit frame.
+ const int kNumInstructionsToJump = 5;
+ masm->Addu(ra, ra, kNumInstructionsToJump * kPointerSize);
+ masm->sw(ra, MemOperand(sp)); // This spot was reserved in EnterExitFrame.
+ // Stack space reservation moved to the branch delay slot below.
+ // Stack is still aligned.
+
+ // Call the C routine.
+ masm->mov(t9, s2); // Function pointer to t9 to conform to ABI for PIC.
+ masm->jalr(t9);
+ // Set up sp in the delay slot.
+ masm->addiu(sp, sp, -kCArgsSlotsSize);
+ // Make sure the stored 'ra' points to this position.
+ ASSERT_EQ(kNumInstructionsToJump,
+ masm->InstructionsGeneratedSince(&find_ra));
+ }
+
+ if (always_allocate) {
+ // It's okay to clobber a2 and a3 here. v0 & v1 contain result.
+ __ li(a2, Operand(scope_depth));
+ __ lw(a3, MemOperand(a2));
+ __ Subu(a3, a3, Operand(1));
+ __ sw(a3, MemOperand(a2));
+ }
+
+ // Check for failure result.
+ Label failure_returned;
+ STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
+ __ addiu(a2, v0, 1);
+ __ andi(t0, a2, kFailureTagMask);
+ __ Branch(USE_DELAY_SLOT, &failure_returned, eq, t0, Operand(zero_reg));
+ // Restore stack (remove arg slots) in branch delay slot.
+ __ addiu(sp, sp, kCArgsSlotsSize);
+
+
+ // Exit C frame and return.
+ // v0:v1: result
+ // sp: stack pointer
+ // fp: frame pointer
+ __ LeaveExitFrame(save_doubles_, s0, true);
+
+ // Check if we should retry or throw exception.
+ Label retry;
+ __ bind(&failure_returned);
+ STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
+ __ andi(t0, v0, ((1 << kFailureTypeTagSize) - 1) << kFailureTagSize);
+ __ Branch(&retry, eq, t0, Operand(zero_reg));
+
+ // Special handling of out of memory exceptions.
+ JumpIfOOM(masm, v0, t0, throw_out_of_memory_exception);
+
+ // Retrieve the pending exception.
+ __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ lw(v0, MemOperand(t0));
+
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, v0, t0, throw_out_of_memory_exception);
+
+ // Clear the pending exception.
+ __ li(a3, Operand(isolate->factory()->the_hole_value()));
+ __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ sw(a3, MemOperand(t0));
+
+ // Special handling of termination exceptions which are uncatchable
+ // by javascript code.
+ __ LoadRoot(t0, Heap::kTerminationExceptionRootIndex);
+ __ Branch(throw_termination_exception, eq, v0, Operand(t0));
+
+ // Handle normal exception.
+ __ jmp(throw_normal_exception);
+
+ __ bind(&retry);
+ // Last failure (v0) will be moved to (a0) for parameter when retrying.
+}
+
+
+void CEntryStub::Generate(MacroAssembler* masm) {
+ // Called from JavaScript; parameters are on stack as if calling JS function
+ // s0: number of arguments including receiver
+ // s1: size of arguments excluding receiver
+ // s2: pointer to builtin function
+ // fp: frame pointer (restored after C call)
+ // sp: stack pointer (restored as callee's sp after C call)
+ // cp: current context (C callee-saved)
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // NOTE: Invocations of builtins may return failure objects
+ // instead of a proper result. The builtin entry handles
+ // this by performing a garbage collection and retrying the
+ // builtin once.
+
+ // NOTE: s0-s2 hold the arguments of this function instead of a0-a2.
+ // The reason for this is that these arguments would need to be saved anyway
+ // so it's faster to set them up directly.
+ // See MacroAssembler::PrepareCEntryArgs and PrepareCEntryFunction.
+
+ // Compute the argv pointer in a callee-saved register.
+ __ Addu(s1, sp, s1);
+
+ // Enter the exit frame that transitions from JavaScript to C++.
+ FrameScope scope(masm, StackFrame::MANUAL);
+ __ EnterExitFrame(save_doubles_);
+
+ // s0: number of arguments (C callee-saved)
+ // s1: pointer to first argument (C callee-saved)
+ // s2: pointer to builtin function (C callee-saved)
+
+ Label throw_normal_exception;
+ Label throw_termination_exception;
+ Label throw_out_of_memory_exception;
+
+ // Call into the runtime system.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ false,
+ false);
+
+ // Do space-specific GC and retry runtime call.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ false);
+
+ // Do full GC and retry runtime call one final time.
+ Failure* failure = Failure::InternalError();
+ __ li(v0, Operand(reinterpret_cast<int32_t>(failure)));
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ true);
+
+ __ bind(&throw_out_of_memory_exception);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
+ isolate);
+ __ li(a0, Operand(false, RelocInfo::NONE32));
+ __ li(a2, Operand(external_caught));
+ __ sw(a0, MemOperand(a2));
+
+ // Set pending exception and v0 to out of memory exception.
+ Label already_have_failure;
+ JumpIfOOM(masm, v0, t0, &already_have_failure);
+ Failure* out_of_memory = Failure::OutOfMemoryException(0x1);
+ __ li(v0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+ __ bind(&already_have_failure);
+ __ li(a2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ sw(v0, MemOperand(a2));
+ // Fall through to the next label.
+
+ __ bind(&throw_termination_exception);
+ __ ThrowUncatchable(v0);
+
+ __ bind(&throw_normal_exception);
+ __ Throw(v0);
+}
+
+
+void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
+ Label invoke, handler_entry, exit;
+ Isolate* isolate = masm->isolate();
+
+ // Registers:
+ // a0: entry address
+ // a1: function
+ // a2: receiver
+ // a3: argc
+ //
+ // Stack:
+ // 4 args slots
+ // args
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Save callee saved registers on the stack.
+ __ MultiPush(kCalleeSaved | ra.bit());
+
+ // 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;
+ offset_to_argv += kNumCalleeSavedFPU * kDoubleSize;
+
+ __ InitializeRootRegister();
+ __ lw(s0, MemOperand(sp, offset_to_argv + kCArgsSlotsSize));
+
+ // We build an EntryFrame.
+ __ li(t3, Operand(-1)); // Push a bad frame pointer to fail if it is used.
+ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
+ __ li(t2, Operand(Smi::FromInt(marker)));
+ __ li(t1, Operand(Smi::FromInt(marker)));
+ __ li(t0, Operand(ExternalReference(Isolate::kCEntryFPAddress,
+ isolate)));
+ __ lw(t0, MemOperand(t0));
+ __ Push(t3, t2, t1, t0);
+ // Set up frame pointer for the frame to be pushed.
+ __ addiu(fp, sp, -EntryFrameConstants::kCallerFPOffset);
+
+ // Registers:
+ // a0: entry_address
+ // a1: function
+ // a2: receiver_pointer
+ // a3: argc
+ // s0: argv
+ //
+ // Stack:
+ // caller fp |
+ // function slot | entry frame
+ // context slot |
+ // bad fp (0xff...f) |
+ // callee saved registers + ra
+ // 4 args slots
+ // args
+
+ // If this is the outermost JS call, set js_entry_sp value.
+ Label non_outermost_js;
+ 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));
+ __ sw(fp, MemOperand(t1));
+ __ li(t0, Operand(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
+ Label cont;
+ __ b(&cont);
+ __ nop(); // Branch delay slot nop.
+ __ bind(&non_outermost_js);
+ __ li(t0, Operand(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
+ __ bind(&cont);
+ __ push(t0);
+
+ // 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,
+ 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. There's only one
+ // handler block in this code object, so its index is 0.
+ __ bind(&invoke);
+ __ 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.
+ __ LoadRoot(t1, Heap::kTheHoleValueRootIndex);
+ __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ sw(t1, MemOperand(t0));
+
+ // Invoke the function by calling through JS entry trampoline builtin.
+ // Notice that we cannot store a reference to the trampoline code directly in
+ // this stub, because runtime stubs are not traversed when doing GC.
+
+ // Registers:
+ // a0: entry_address
+ // a1: function
+ // a2: receiver_pointer
+ // a3: argc
+ // s0: argv
+ //
+ // Stack:
+ // handler frame
+ // entry frame
+ // callee saved registers + ra
+ // 4 args slots
+ // args
+
+ if (is_construct) {
+ ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
+ isolate);
+ __ li(t0, Operand(construct_entry));
+ } else {
+ ExternalReference entry(Builtins::kJSEntryTrampoline, masm->isolate());
+ __ li(t0, Operand(entry));
+ }
+ __ lw(t9, MemOperand(t0)); // Deref address.
+
+ // Call JSEntryTrampoline.
+ __ addiu(t9, t9, Code::kHeaderSize - kHeapObjectTag);
+ __ Call(t9);
+
+ // Unlink this frame from the handler chain.
+ __ PopTryHandler();
+
+ __ bind(&exit); // v0 holds result
+ // Check if the current stack frame is marked as the outermost JS frame.
+ Label non_outermost_js_2;
+ __ pop(t1);
+ __ Branch(&non_outermost_js_2,
+ ne,
+ t1,
+ Operand(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
+ __ li(t1, Operand(ExternalReference(js_entry_sp)));
+ __ sw(zero_reg, MemOperand(t1));
+ __ bind(&non_outermost_js_2);
+
+ // Restore the top frame descriptors from the stack.
+ __ pop(t1);
+ __ li(t0, Operand(ExternalReference(Isolate::kCEntryFPAddress,
+ isolate)));
+ __ sw(t1, MemOperand(t0));
+
+ // Reset the stack to the callee saved registers.
+ __ addiu(sp, sp, -EntryFrameConstants::kCallerFPOffset);
+
+ // Restore callee-saved fpu registers.
+ __ MultiPopFPU(kCalleeSavedFPU);
+
+ // Restore callee saved registers from the stack.
+ __ MultiPop(kCalleeSaved | ra.bit());
+ // Return.
+ __ Jump(ra);
+}
+
+
+// Uses registers a0 to t0.
+// Expected input (depending on whether args are in registers or on the stack):
+// * object: a0 or at sp + 1 * kPointerSize.
+// * function: a1 or at sp.
+//
+// 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) {
+ // Call site inlining and patching implies arguments in registers.
+ ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
+ // ReturnTrueFalse is only implemented for inlined call sites.
+ ASSERT(!ReturnTrueFalseObject() || HasCallSiteInlineCheck());
+
+ // Fixed register usage throughout the stub:
+ const Register object = a0; // Object (lhs).
+ Register map = a3; // Map of the object.
+ const Register function = a1; // Function (rhs).
+ const Register prototype = t0; // Prototype of the function.
+ 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()) {
+ __ lw(object, MemOperand(sp, 1 * kPointerSize));
+ __ lw(function, MemOperand(sp, 0));
+ }
+
+ // Check that the left hand is a JS object and load map.
+ __ JumpIfSmi(object, &not_js_object);
+ __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
+
+ // If there is a call site cache don't look in the global cache, but do the
+ // real lookup and update the call site cache.
+ if (!HasCallSiteInlineCheck()) {
+ Label miss;
+ __ 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);
+
+ __ bind(&miss);
+ }
+
+ // Get the prototype of the function.
+ __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
+
+ // Check that the function prototype is a JS object.
+ __ JumpIfSmi(prototype, &slow);
+ __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
+
+ // Update the global instanceof or call site inlined cache with the current
+ // map and function. The cached answer will be set when it is known below.
+ if (!HasCallSiteInlineCheck()) {
+ __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
+ __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
+ } else {
+ 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, Cell::kValueOffset));
+ }
+
+ // Register mapping: a3 is object map and t0 is function prototype.
+ // Get prototype of object into a2.
+ __ lw(scratch, FieldMemOperand(map, Map::kPrototypeOffset));
+
+ // We don't need map any more. Use it as a scratch register.
+ Register scratch2 = map;
+ map = no_reg;
+
+ // Loop through the prototype chain looking for the function prototype.
+ __ LoadRoot(scratch2, Heap::kNullValueRootIndex);
+ __ bind(&loop);
+ __ Branch(&is_instance, eq, scratch, Operand(prototype));
+ __ Branch(&is_not_instance, eq, scratch, Operand(scratch2));
+ __ lw(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset));
+ __ lw(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
+ __ Branch(&loop);
+
+ __ bind(&is_instance);
+ ASSERT(Smi::FromInt(0) == 0);
+ if (!HasCallSiteInlineCheck()) {
+ __ mov(v0, zero_reg);
+ __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ // 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);
+
+ __ bind(&is_not_instance);
+ if (!HasCallSiteInlineCheck()) {
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ // 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;
+ __ bind(&not_js_object);
+ // Before null, smi and string value checks, check that the rhs is a function
+ // as for a non-function rhs an exception needs to be thrown.
+ __ JumpIfSmi(function, &slow);
+ __ GetObjectType(function, scratch2, scratch);
+ __ Branch(&slow, ne, scratch, Operand(JS_FUNCTION_TYPE));
+
+ // Null is not instance of anything.
+ __ Branch(&object_not_null,
+ ne,
+ scratch,
+ Operand(masm->isolate()->factory()->null_value()));
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&object_not_null);
+ // Smi values are not instances of anything.
+ __ JumpIfNotSmi(object, &object_not_null_or_smi);
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&object_not_null_or_smi);
+ // String values are not instances of anything.
+ __ IsObjectJSStringType(object, scratch, &slow);
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ // Slow-case. Tail call builtin.
+ __ bind(&slow);
+ if (!ReturnTrueFalseObject()) {
+ if (HasArgsInRegisters()) {
+ __ Push(a0, a1);
+ }
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
+ } else {
+ {
+ 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));
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex);
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+ }
+}
+
+
+void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
+ Label miss;
+ Register receiver;
+ if (kind() == Code::KEYED_LOAD_IC) {
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ __ Branch(&miss, ne, a0,
+ Operand(masm->isolate()->factory()->prototype_string()));
+ receiver = a1;
+ } else {
+ ASSERT(kind() == Code::LOAD_IC);
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ receiver = a0;
+ }
+
+ StubCompiler::GenerateLoadFunctionPrototype(masm, receiver, a3, t0, &miss);
+ __ bind(&miss);
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void StringLengthStub::Generate(MacroAssembler* masm) {
+ Label miss;
+ Register receiver;
+ if (kind() == Code::KEYED_LOAD_IC) {
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ __ Branch(&miss, ne, a0,
+ Operand(masm->isolate()->factory()->length_string()));
+ receiver = a1;
+ } else {
+ ASSERT(kind() == Code::LOAD_IC);
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ receiver = a0;
+ }
+
+ StubCompiler::GenerateLoadStringLength(masm, receiver, a3, t0, &miss,
+ support_wrapper_);
+
+ __ bind(&miss);
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void StoreArrayLengthStub::Generate(MacroAssembler* masm) {
+ // 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;
+
+ Register receiver;
+ Register value;
+ if (kind() == Code::KEYED_STORE_IC) {
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -----------------------------------
+ __ Branch(&miss, ne, a1,
+ Operand(masm->isolate()->factory()->length_string()));
+ receiver = a2;
+ value = a0;
+ } else {
+ ASSERT(kind() == Code::STORE_IC);
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : key
+ // -----------------------------------
+ receiver = a1;
+ value = a0;
+ }
+ Register scratch = a3;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Check that the object is a JS array.
+ __ GetObjectType(receiver, scratch, scratch);
+ __ Branch(&miss, ne, scratch, Operand(JS_ARRAY_TYPE));
+
+ // Check that elements are FixedArray.
+ // We rely on StoreIC_ArrayLength below to deal with all types of
+ // fast elements (including COW).
+ __ lw(scratch, FieldMemOperand(receiver, JSArray::kElementsOffset));
+ __ 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.
+ __ lw(scratch, FieldMemOperand(receiver, JSArray::kPropertiesOffset));
+ __ lw(scratch, FieldMemOperand(scratch, FixedArray::kMapOffset));
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&miss, eq, scratch, Operand(at));
+
+ // Check that value is a smi.
+ __ JumpIfNotSmi(value, &miss);
+
+ // Prepare tail call to StoreIC_ArrayLength.
+ __ Push(receiver, value);
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+
+ __ bind(&miss);
+
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+Register InstanceofStub::left() { return a0; }
+
+
+Register InstanceofStub::right() { return a1; }
+
+
+void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+ // The displacement is the offset of the last parameter (if any)
+ // relative to the frame pointer.
+ const int kDisplacement =
+ StandardFrameConstants::kCallerSPOffset - kPointerSize;
+
+ // Check that the key is a smiGenerateReadElement.
+ Label slow;
+ __ JumpIfNotSmi(a1, &slow);
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor;
+ __ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ lw(a3, MemOperand(a2, StandardFrameConstants::kContextOffset));
+ __ Branch(&adaptor,
+ eq,
+ a3,
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Check index (a1) against formal parameters count limit passed in
+ // through register a0. Use unsigned comparison to get negative
+ // check for free.
+ __ Branch(&slow, hs, a1, Operand(a0));
+
+ // Read the argument from the stack and return it.
+ __ subu(a3, a0, a1);
+ __ sll(t3, a3, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a3, fp, Operand(t3));
+ __ Ret(USE_DELAY_SLOT);
+ __ lw(v0, MemOperand(a3, kDisplacement));
+
+ // Arguments adaptor case: Check index (a1) against actual arguments
+ // limit found in the arguments adaptor frame. Use unsigned
+ // comparison to get negative check for free.
+ __ bind(&adaptor);
+ __ lw(a0, MemOperand(a2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ Branch(&slow, Ugreater_equal, a1, Operand(a0));
+
+ // Read the argument from the adaptor frame and return it.
+ __ subu(a3, a0, a1);
+ __ sll(t3, a3, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a3, a2, Operand(t3));
+ __ Ret(USE_DELAY_SLOT);
+ __ lw(v0, MemOperand(a3, kDisplacement));
+
+ // Slow-case: Handle non-smi or out-of-bounds access to arguments
+ // by calling the runtime system.
+ __ bind(&slow);
+ __ push(a1);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
+ // sp[0] : number of parameters
+ // sp[4] : receiver displacement
+ // sp[8] : function
+ // Check if the calling frame is an arguments adaptor frame.
+ Label runtime;
+ __ lw(a3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ lw(a2, MemOperand(a3, StandardFrameConstants::kContextOffset));
+ __ Branch(&runtime,
+ ne,
+ a2,
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Patch the arguments.length and the parameters pointer in the current frame.
+ __ lw(a2, MemOperand(a3, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ sw(a2, MemOperand(sp, 0 * kPointerSize));
+ __ sll(t3, a2, 1);
+ __ Addu(a3, a3, Operand(t3));
+ __ addiu(a3, a3, StandardFrameConstants::kCallerSPOffset);
+ __ sw(a3, MemOperand(sp, 1 * kPointerSize));
+
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
+ // Stack layout:
+ // sp[0] : number of parameters (tagged)
+ // sp[4] : address of receiver argument
+ // sp[8] : function
+ // Registers used over whole function:
+ // t2 : allocated object (tagged)
+ // t5 : mapped parameter count (tagged)
+
+ __ lw(a1, MemOperand(sp, 0 * kPointerSize));
+ // a1 = parameter count (tagged)
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label runtime;
+ Label adaptor_frame, try_allocate;
+ __ lw(a3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ lw(a2, MemOperand(a3, StandardFrameConstants::kContextOffset));
+ __ Branch(&adaptor_frame,
+ eq,
+ a2,
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // No adaptor, parameter count = argument count.
+ __ mov(a2, a1);
+ __ b(&try_allocate);
+ __ nop(); // Branch delay slot nop.
+
+ // We have an adaptor frame. Patch the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ lw(a2, MemOperand(a3, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ sll(t6, a2, 1);
+ __ Addu(a3, a3, Operand(t6));
+ __ Addu(a3, a3, Operand(StandardFrameConstants::kCallerSPOffset));
+ __ sw(a3, MemOperand(sp, 1 * kPointerSize));
+
+ // a1 = parameter count (tagged)
+ // a2 = argument count (tagged)
+ // Compute the mapped parameter count = min(a1, a2) in a1.
+ Label skip_min;
+ __ Branch(&skip_min, lt, a1, Operand(a2));
+ __ mov(a1, a2);
+ __ bind(&skip_min);
+
+ __ bind(&try_allocate);
+
+ // Compute the sizes of backing store, parameter map, and arguments object.
+ // 1. Parameter map, has 2 extra words containing context and backing store.
+ const int kParameterMapHeaderSize =
+ FixedArray::kHeaderSize + 2 * kPointerSize;
+ // If there are no mapped parameters, we do not need the parameter_map.
+ Label param_map_size;
+ ASSERT_EQ(0, Smi::FromInt(0));
+ __ Branch(USE_DELAY_SLOT, &param_map_size, eq, a1, Operand(zero_reg));
+ __ mov(t5, zero_reg); // In delay slot: param map size = 0 when a1 == 0.
+ __ sll(t5, a1, 1);
+ __ addiu(t5, t5, kParameterMapHeaderSize);
+ __ bind(&param_map_size);
+
+ // 2. Backing store.
+ __ sll(t6, a2, 1);
+ __ Addu(t5, t5, Operand(t6));
+ __ Addu(t5, t5, Operand(FixedArray::kHeaderSize));
+
+ // 3. Arguments object.
+ __ Addu(t5, t5, Operand(Heap::kArgumentsObjectSize));
+
+ // Do the allocation of all three objects in one go.
+ __ Allocate(t5, v0, a3, t0, &runtime, TAG_OBJECT);
+
+ // v0 = address of new object(s) (tagged)
+ // a2 = argument count (tagged)
+ // Get the arguments boilerplate from the current native context into t0.
+ const int kNormalOffset =
+ Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
+ const int kAliasedOffset =
+ Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX);
+
+ __ lw(t0, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ lw(t0, FieldMemOperand(t0, GlobalObject::kNativeContextOffset));
+ Label skip2_ne, skip2_eq;
+ __ Branch(&skip2_ne, ne, a1, Operand(zero_reg));
+ __ lw(t0, MemOperand(t0, kNormalOffset));
+ __ bind(&skip2_ne);
+
+ __ Branch(&skip2_eq, eq, a1, Operand(zero_reg));
+ __ lw(t0, MemOperand(t0, kAliasedOffset));
+ __ bind(&skip2_eq);
+
+ // v0 = address of new object (tagged)
+ // a1 = mapped parameter count (tagged)
+ // a2 = argument count (tagged)
+ // t0 = address of boilerplate object (tagged)
+ // Copy the JS object part.
+ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+ __ lw(a3, FieldMemOperand(t0, i));
+ __ sw(a3, FieldMemOperand(v0, i));
+ }
+
+ // Set up the callee in-object property.
+ STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
+ __ lw(a3, MemOperand(sp, 2 * kPointerSize));
+ const int kCalleeOffset = JSObject::kHeaderSize +
+ Heap::kArgumentsCalleeIndex * kPointerSize;
+ __ sw(a3, FieldMemOperand(v0, kCalleeOffset));
+
+ // Use the length (smi tagged) and set that as an in-object property too.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ const int kLengthOffset = JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize;
+ __ sw(a2, FieldMemOperand(v0, kLengthOffset));
+
+ // 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));
+ __ sw(t0, FieldMemOperand(v0, JSObject::kElementsOffset));
+
+ // v0 = address of new object (tagged)
+ // a1 = mapped parameter count (tagged)
+ // a2 = argument count (tagged)
+ // t0 = address of parameter map or backing store (tagged)
+ // Initialize parameter map. If there are no mapped arguments, we're done.
+ Label skip_parameter_map;
+ Label skip3;
+ __ Branch(&skip3, ne, a1, Operand(Smi::FromInt(0)));
+ // Move backing store address to a3, because it is
+ // expected there when filling in the unmapped arguments.
+ __ mov(a3, t0);
+ __ bind(&skip3);
+
+ __ Branch(&skip_parameter_map, eq, a1, Operand(Smi::FromInt(0)));
+
+ __ LoadRoot(t2, Heap::kNonStrictArgumentsElementsMapRootIndex);
+ __ sw(t2, FieldMemOperand(t0, FixedArray::kMapOffset));
+ __ Addu(t2, a1, Operand(Smi::FromInt(2)));
+ __ sw(t2, FieldMemOperand(t0, FixedArray::kLengthOffset));
+ __ sw(cp, FieldMemOperand(t0, FixedArray::kHeaderSize + 0 * kPointerSize));
+ __ sll(t6, a1, 1);
+ __ Addu(t2, t0, Operand(t6));
+ __ Addu(t2, t2, Operand(kParameterMapHeaderSize));
+ __ sw(t2, FieldMemOperand(t0, FixedArray::kHeaderSize + 1 * kPointerSize));
+
+ // Copy the parameter slots and the holes in the arguments.
+ // We need to fill in mapped_parameter_count slots. They index the context,
+ // where parameters are stored in reverse order, at
+ // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
+ // The mapped parameter thus need to get indices
+ // MIN_CONTEXT_SLOTS+parameter_count-1 ..
+ // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
+ // We loop from right to left.
+ Label parameters_loop, parameters_test;
+ __ mov(t2, a1);
+ __ lw(t5, MemOperand(sp, 0 * kPointerSize));
+ __ Addu(t5, t5, Operand(Smi::FromInt(Context::MIN_CONTEXT_SLOTS)));
+ __ Subu(t5, t5, Operand(a1));
+ __ LoadRoot(t3, Heap::kTheHoleValueRootIndex);
+ __ sll(t6, t2, 1);
+ __ Addu(a3, t0, Operand(t6));
+ __ Addu(a3, a3, Operand(kParameterMapHeaderSize));
+
+ // t2 = loop variable (tagged)
+ // a1 = mapping index (tagged)
+ // a3 = address of backing store (tagged)
+ // t0 = address of parameter map (tagged)
+ // t1 = temporary scratch (a.o., for address calculation)
+ // t3 = the hole value
+ __ jmp(&parameters_test);
+
+ __ bind(&parameters_loop);
+ __ Subu(t2, t2, Operand(Smi::FromInt(1)));
+ __ sll(t1, t2, 1);
+ __ Addu(t1, t1, Operand(kParameterMapHeaderSize - kHeapObjectTag));
+ __ Addu(t6, t0, t1);
+ __ sw(t5, MemOperand(t6));
+ __ Subu(t1, t1, Operand(kParameterMapHeaderSize - FixedArray::kHeaderSize));
+ __ Addu(t6, a3, t1);
+ __ sw(t3, MemOperand(t6));
+ __ Addu(t5, t5, Operand(Smi::FromInt(1)));
+ __ bind(&parameters_test);
+ __ Branch(&parameters_loop, ne, t2, Operand(Smi::FromInt(0)));
+
+ __ bind(&skip_parameter_map);
+ // a2 = argument count (tagged)
+ // a3 = address of backing store (tagged)
+ // t1 = scratch
+ // Copy arguments header and remaining slots (if there are any).
+ __ LoadRoot(t1, Heap::kFixedArrayMapRootIndex);
+ __ sw(t1, FieldMemOperand(a3, FixedArray::kMapOffset));
+ __ sw(a2, FieldMemOperand(a3, FixedArray::kLengthOffset));
+
+ Label arguments_loop, arguments_test;
+ __ mov(t5, a1);
+ __ lw(t0, MemOperand(sp, 1 * kPointerSize));
+ __ sll(t6, t5, 1);
+ __ Subu(t0, t0, Operand(t6));
+ __ jmp(&arguments_test);
+
+ __ bind(&arguments_loop);
+ __ Subu(t0, t0, Operand(kPointerSize));
+ __ lw(t2, MemOperand(t0, 0));
+ __ sll(t6, t5, 1);
+ __ Addu(t1, a3, Operand(t6));
+ __ sw(t2, FieldMemOperand(t1, FixedArray::kHeaderSize));
+ __ Addu(t5, t5, Operand(Smi::FromInt(1)));
+
+ __ bind(&arguments_test);
+ __ Branch(&arguments_loop, lt, t5, Operand(a2));
+
+ // Return and remove the on-stack parameters.
+ __ DropAndRet(3);
+
+ // Do the runtime call to allocate the arguments object.
+ // a2 = argument count (tagged)
+ __ bind(&runtime);
+ __ sw(a2, MemOperand(sp, 0 * kPointerSize)); // Patch argument count.
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
+ // sp[0] : number of parameters
+ // sp[4] : receiver displacement
+ // sp[8] : function
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor_frame, try_allocate, runtime;
+ __ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ lw(a3, MemOperand(a2, StandardFrameConstants::kContextOffset));
+ __ Branch(&adaptor_frame,
+ eq,
+ a3,
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Get the length from the frame.
+ __ lw(a1, MemOperand(sp, 0));
+ __ Branch(&try_allocate);
+
+ // Patch the arguments.length and the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ lw(a1, MemOperand(a2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ sw(a1, MemOperand(sp, 0));
+ __ sll(at, a1, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a3, a2, Operand(at));
+
+ __ Addu(a3, a3, Operand(StandardFrameConstants::kCallerSPOffset));
+ __ sw(a3, MemOperand(sp, 1 * kPointerSize));
+
+ // Try the new space allocation. Start out with computing the size
+ // of the arguments object and the elements array in words.
+ Label add_arguments_object;
+ __ bind(&try_allocate);
+ __ Branch(&add_arguments_object, eq, a1, Operand(zero_reg));
+ __ srl(a1, a1, kSmiTagSize);
+
+ __ Addu(a1, a1, Operand(FixedArray::kHeaderSize / kPointerSize));
+ __ bind(&add_arguments_object);
+ __ Addu(a1, a1, Operand(Heap::kArgumentsObjectSizeStrict / kPointerSize));
+
+ // Do the allocation of both objects in one go.
+ __ Allocate(a1, v0, a2, a3, &runtime,
+ static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
+
+ // Get the arguments boilerplate from the current native context.
+ __ lw(t0, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ lw(t0, FieldMemOperand(t0, GlobalObject::kNativeContextOffset));
+ __ lw(t0, MemOperand(t0, Context::SlotOffset(
+ Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX)));
+
+ // Copy the JS object part.
+ __ CopyFields(v0, t0, a3.bit(), JSObject::kHeaderSize / kPointerSize);
+
+ // Get the length (smi tagged) and set that as an in-object property too.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ __ lw(a1, MemOperand(sp, 0 * kPointerSize));
+ __ sw(a1, FieldMemOperand(v0, JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize));
+
+ Label done;
+ __ Branch(&done, eq, a1, Operand(zero_reg));
+
+ // Get the parameters pointer from the stack.
+ __ lw(a2, MemOperand(sp, 1 * kPointerSize));
+
+ // 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));
+ __ LoadRoot(a3, Heap::kFixedArrayMapRootIndex);
+ __ sw(a3, FieldMemOperand(t0, FixedArray::kMapOffset));
+ __ sw(a1, FieldMemOperand(t0, FixedArray::kLengthOffset));
+ // Untag the length for the loop.
+ __ srl(a1, a1, kSmiTagSize);
+
+ // Copy the fixed array slots.
+ Label loop;
+ // 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.
+ // Pre-decrement in order to skip receiver.
+ __ Addu(a2, a2, Operand(-kPointerSize));
+ __ lw(a3, MemOperand(a2));
+ // Post-increment t0 with kPointerSize on each iteration.
+ __ sw(a3, MemOperand(t0));
+ __ Addu(t0, t0, Operand(kPointerSize));
+ __ Subu(a1, a1, Operand(1));
+ __ Branch(&loop, ne, a1, Operand(zero_reg));
+
+ // Return and remove the on-stack parameters.
+ __ bind(&done);
+ __ DropAndRet(3);
+
+ // Do the runtime call to allocate the arguments object.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
+}
+
+
+void RegExpExecStub::Generate(MacroAssembler* masm) {
+ // Just jump directly to runtime if native RegExp is not selected at compile
+ // time or if regexp entry in generated code is turned off runtime switch or
+ // at compilation.
+#ifdef V8_INTERPRETED_REGEXP
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+#else // V8_INTERPRETED_REGEXP
+
+ // Stack frame on entry.
+ // sp[0]: last_match_info (expected JSArray)
+ // sp[4]: previous index
+ // sp[8]: subject string
+ // sp[12]: JSRegExp object
+
+ const int kLastMatchInfoOffset = 0 * kPointerSize;
+ const int kPreviousIndexOffset = 1 * kPointerSize;
+ const int kSubjectOffset = 2 * kPointerSize;
+ const int kJSRegExpOffset = 3 * kPointerSize;
+
+ Isolate* isolate = masm->isolate();
+
+ Label runtime;
+ // Allocation of registers for this function. These are in callee save
+ // registers and will be preserved by the call to the native RegExp code, as
+ // this code is called using the normal C calling convention. When calling
+ // directly from generated code the native RegExp code will not do a GC and
+ // therefore the content of these registers are safe to use after the call.
+ // MIPS - using s0..s2, since we are not using CEntry Stub.
+ Register subject = s0;
+ Register regexp_data = s1;
+ Register last_match_info_elements = s2;
+
+ // Ensure that a RegExp stack is allocated.
+ ExternalReference address_of_regexp_stack_memory_address =
+ ExternalReference::address_of_regexp_stack_memory_address(
+ isolate);
+ ExternalReference address_of_regexp_stack_memory_size =
+ 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));
+
+ // Check that the first argument is a JSRegExp object.
+ __ lw(a0, MemOperand(sp, kJSRegExpOffset));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(a0, &runtime);
+ __ GetObjectType(a0, a1, a1);
+ __ Branch(&runtime, ne, a1, Operand(JS_REGEXP_TYPE));
+
+ // Check that the RegExp has been compiled (data contains a fixed array).
+ __ lw(regexp_data, FieldMemOperand(a0, JSRegExp::kDataOffset));
+ if (FLAG_debug_code) {
+ __ And(t0, regexp_data, Operand(kSmiTagMask));
+ __ Check(nz,
+ kUnexpectedTypeForRegExpDataFixedArrayExpected,
+ t0,
+ Operand(zero_reg));
+ __ GetObjectType(regexp_data, a0, a0);
+ __ Check(eq,
+ kUnexpectedTypeForRegExpDataFixedArrayExpected,
+ a0,
+ Operand(FIXED_ARRAY_TYPE));
+ }
+
+ // regexp_data: RegExp data (FixedArray)
+ // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
+ __ lw(a0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
+ __ Branch(&runtime, ne, a0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
+
+ // regexp_data: RegExp data (FixedArray)
+ // Check that the number of captures fit in the static offsets vector buffer.
+ __ lw(a2,
+ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
+ // Check (number_of_captures + 1) * 2 <= offsets vector size
+ // Or number_of_captures * 2 <= offsets vector size - 2
+ // Multiplying by 2 comes for free since a2 is smi-tagged.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
+ __ Branch(
+ &runtime, hi, a2, Operand(Isolate::kJSRegexpStaticOffsetsVectorSize - 2));
+
+ // Reset offset for possibly sliced string.
+ __ mov(t0, zero_reg);
+ __ lw(subject, MemOperand(sp, kSubjectOffset));
+ __ JumpIfSmi(subject, &runtime);
+ __ mov(a3, subject); // Make a copy of the original subject string.
+ __ lw(a0, FieldMemOperand(subject, HeapObject::kMapOffset));
+ __ lbu(a0, FieldMemOperand(a0, Map::kInstanceTypeOffset));
+ // subject: subject string
+ // a3: subject string
+ // a0: subject string instance type
+ // regexp_data: RegExp data (FixedArray)
+ // Handle subject string according to its encoding and representation:
+ // (1) Sequential string? If yes, go to (5).
+ // (2) Anything but sequential or cons? If yes, go to (6).
+ // (3) Cons string. If the string is flat, replace subject with first string.
+ // Otherwise bailout.
+ // (4) Is subject external? If yes, go to (7).
+ // (5) Sequential string. Load regexp code according to encoding.
+ // (E) Carry on.
+ /// [...]
+
+ // Deferred code at the end of the stub:
+ // (6) Not a long external string? If yes, go to (8).
+ // (7) External string. Make it, offset-wise, look like a sequential string.
+ // Go to (5).
+ // (8) Short external string or not a string? If yes, bail out to runtime.
+ // (9) Sliced string. Replace subject with parent. Go to (4).
+
+ Label seq_string /* 5 */, external_string /* 7 */,
+ check_underlying /* 4 */, not_seq_nor_cons /* 6 */,
+ not_long_external /* 8 */;
+
+ // (1) Sequential string? If yes, go to (5).
+ __ And(a1,
+ a0,
+ Operand(kIsNotStringMask |
+ kStringRepresentationMask |
+ kShortExternalStringMask));
+ STATIC_ASSERT((kStringTag | kSeqStringTag) == 0);
+ __ Branch(&seq_string, eq, a1, Operand(zero_reg)); // Go to (5).
+
+ // (2) Anything but sequential or cons? If yes, go to (6).
+ STATIC_ASSERT(kConsStringTag < kExternalStringTag);
+ STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
+ STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
+ STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
+ // Go to (6).
+ __ Branch(&not_seq_nor_cons, ge, a1, Operand(kExternalStringTag));
+
+ // (3) Cons string. Check that it's flat.
+ // Replace subject with first string and reload instance type.
+ __ lw(a0, FieldMemOperand(subject, ConsString::kSecondOffset));
+ __ LoadRoot(a1, Heap::kempty_stringRootIndex);
+ __ Branch(&runtime, ne, a0, Operand(a1));
+ __ lw(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
+
+ // (4) Is subject external? If yes, go to (7).
+ __ bind(&check_underlying);
+ __ lw(a0, FieldMemOperand(subject, HeapObject::kMapOffset));
+ __ lbu(a0, FieldMemOperand(a0, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kSeqStringTag == 0);
+ __ And(at, a0, Operand(kStringRepresentationMask));
+ // The underlying external string is never a short external string.
+ STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength);
+ STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength);
+ __ Branch(&external_string, ne, at, Operand(zero_reg)); // Go to (7).
+
+ // (5) Sequential string. Load regexp code according to encoding.
+ __ bind(&seq_string);
+ // subject: sequential subject string (or look-alike, external string)
+ // a3: original subject string
+ // Load previous index and check range before a3 is overwritten. We have to
+ // use a3 instead of subject here because subject might have been only made
+ // to look like a sequential string when it actually is an external string.
+ __ lw(a1, MemOperand(sp, kPreviousIndexOffset));
+ __ JumpIfNotSmi(a1, &runtime);
+ __ lw(a3, FieldMemOperand(a3, String::kLengthOffset));
+ __ Branch(&runtime, ls, a3, Operand(a1));
+ __ sra(a1, a1, kSmiTagSize); // Untag the Smi.
+
+ STATIC_ASSERT(kStringEncodingMask == 4);
+ STATIC_ASSERT(kOneByteStringTag == 4);
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ __ 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 (used below).
+ __ lw(t1, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset));
+ __ Movz(t9, t1, a0); // If UC16 (a0 is 0), replace t9 w/kDataUC16CodeOffset.
+
+ // (E) Carry on. String handling is done.
+ // t9: irregexp code
+ // Check that the irregexp code has been generated for the actual string
+ // encoding. If it has, the field contains a code object otherwise it contains
+ // a smi (code flushing support).
+ __ JumpIfSmi(t9, &runtime);
+
+ // a1: previous index
+ // a3: encoding of subject string (1 if ASCII, 0 if two_byte);
+ // t9: code
+ // subject: Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // All checks done. Now push arguments for native regexp code.
+ __ IncrementCounter(isolate->counters()->regexp_entry_native(),
+ 1, a0, a2);
+
+ // Isolates: note we add an additional parameter here (isolate pointer).
+ const int kRegExpExecuteArguments = 9;
+ const int kParameterRegisters = 4;
+ __ EnterExitFrame(false, kRegExpExecuteArguments - kParameterRegisters);
+
+ // Stack pointer now points to cell where return address is to be written.
+ // Arguments are before that on the stack or in registers, meaning we
+ // treat the return address as argument 5. Thus every argument after that
+ // needs to be shifted back by 1. Since DirectCEntryStub will handle
+ // allocating space for the c argument slots, we don't need to calculate
+ // that into the argument positions on the stack. This is how the stack will
+ // look (sp meaning the value of sp at this moment):
+ // [sp + 5] - Argument 9
+ // [sp + 4] - Argument 8
+ // [sp + 3] - Argument 7
+ // [sp + 2] - Argument 6
+ // [sp + 1] - Argument 5
+ // [sp + 0] - saved ra
+
+ // Argument 9: Pass current isolate address.
+ // CFunctionArgumentOperand handles MIPS stack argument slots.
+ __ li(a0, Operand(ExternalReference::isolate_address(isolate)));
+ __ sw(a0, MemOperand(sp, 5 * kPointerSize));
+
+ // Argument 8: Indicate that this is a direct call from JavaScript.
+ __ li(a0, Operand(1));
+ __ sw(a0, MemOperand(sp, 4 * kPointerSize));
+
+ // Argument 7: Start (high end) of backtracking stack memory area.
+ __ li(a0, Operand(address_of_regexp_stack_memory_address));
+ __ lw(a0, MemOperand(a0, 0));
+ __ li(a2, Operand(address_of_regexp_stack_memory_size));
+ __ lw(a2, MemOperand(a2, 0));
+ __ addu(a0, a0, a2);
+ __ sw(a0, MemOperand(sp, 3 * kPointerSize));
+
+ // Argument 6: Set the number of capture registers to zero to force global
+ // regexps to behave as non-global. This does not affect non-global regexps.
+ __ mov(a0, zero_reg);
+ __ sw(a0, MemOperand(sp, 2 * kPointerSize));
+
+ // Argument 5: static offsets vector buffer.
+ __ li(a0, Operand(
+ 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).
+ __ 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
+ // sizes below the previous sp. (Because creating a new stack frame pushes
+ // the previous fp onto the stack and moves up sp by 2 * kPointerSize.)
+ __ lw(subject, MemOperand(fp, kSubjectOffset + 2 * kPointerSize));
+ // If slice offset is not 0, load the length from the original sliced string.
+ // Argument 4, a3: End of string data
+ // Argument 3, a2: Start of string data
+ // Prepare start and end index of the input.
+ __ sllv(t1, t0, a3);
+ __ addu(t0, t2, t1);
+ __ sllv(t1, a1, a3);
+ __ addu(a2, t0, t1);
+
+ __ lw(t2, FieldMemOperand(subject, String::kLengthOffset));
+ __ sra(t2, t2, kSmiTagSize);
+ __ sllv(t1, t2, a3);
+ __ addu(a3, t0, t1);
+ // Argument 2 (a1): Previous index.
+ // Already there
+
+ // Argument 1 (a0): Subject string.
+ __ mov(a0, subject);
+
+ // Locate the code entry and call it.
+ __ Addu(t9, t9, Operand(Code::kHeaderSize - kHeapObjectTag));
+ DirectCEntryStub stub;
+ stub.GenerateCall(masm, t9);
+
+ __ LeaveExitFrame(false, no_reg);
+
+ // v0: result
+ // subject: subject string (callee saved)
+ // regexp_data: RegExp data (callee saved)
+ // last_match_info_elements: Last match info elements (callee saved)
+ // Check the result.
+ Label success;
+ __ Branch(&success, eq, v0, Operand(1));
+ // We expect exactly one result since we force the called regexp to behave
+ // as non-global.
+ Label failure;
+ __ Branch(&failure, eq, v0, Operand(NativeRegExpMacroAssembler::FAILURE));
+ // If not exception it can only be retry. Handle that in the runtime system.
+ __ Branch(&runtime, ne, v0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
+ // Result must now be exception. If there is no pending exception already a
+ // 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(isolate->factory()->the_hole_value()));
+ __ li(a2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
+ isolate)));
+ __ lw(v0, MemOperand(a2, 0));
+ __ Branch(&runtime, eq, v0, Operand(a1));
+
+ __ sw(a1, MemOperand(a2, 0)); // Clear pending exception.
+
+ // Check if the exception is a termination. If so, throw as uncatchable.
+ __ LoadRoot(a0, Heap::kTerminationExceptionRootIndex);
+ Label termination_exception;
+ __ Branch(&termination_exception, eq, v0, Operand(a0));
+
+ __ Throw(v0);
+
+ __ bind(&termination_exception);
+ __ ThrowUncatchable(v0);
+
+ __ bind(&failure);
+ // For failure and exception return null.
+ __ li(v0, Operand(isolate->factory()->null_value()));
+ __ DropAndRet(4);
+
+ // Process the result from the native regexp code.
+ __ bind(&success);
+ __ lw(a1,
+ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
+ // Calculate number of capture registers (number_of_captures + 1) * 2.
+ // Multiplying by 2 comes for free since r1 is smi-tagged.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ __ Addu(a1, a1, Operand(2)); // a1 was a smi.
+
+ __ lw(a0, MemOperand(sp, kLastMatchInfoOffset));
+ __ JumpIfSmi(a0, &runtime);
+ __ GetObjectType(a0, a2, a2);
+ __ Branch(&runtime, ne, a2, Operand(JS_ARRAY_TYPE));
+ // Check that the JSArray is in fast case.
+ __ lw(last_match_info_elements,
+ FieldMemOperand(a0, JSArray::kElementsOffset));
+ __ lw(a0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kFixedArrayMapRootIndex);
+ __ Branch(&runtime, ne, a0, Operand(at));
+ // Check that the last match info has space for the capture registers and the
+ // additional information.
+ __ lw(a0,
+ FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
+ __ Addu(a2, a1, Operand(RegExpImpl::kLastMatchOverhead));
+ __ sra(at, a0, kSmiTagSize);
+ __ Branch(&runtime, gt, a2, Operand(at));
+
+ // a1: number of capture registers
+ // subject: subject string
+ // Store the capture count.
+ __ sll(a2, a1, kSmiTagSize + kSmiShiftSize); // To smi.
+ __ sw(a2, FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastCaptureCountOffset));
+ // Store last subject and last input.
+ __ sw(subject,
+ FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastSubjectOffset));
+ __ mov(a2, subject);
+ __ RecordWriteField(last_match_info_elements,
+ RegExpImpl::kLastSubjectOffset,
+ subject,
+ t3,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs);
+ __ mov(subject, a2);
+ __ sw(subject,
+ FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastInputOffset));
+ __ 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(isolate);
+ __ li(a2, Operand(address_of_static_offsets_vector));
+
+ // a1: number of capture registers
+ // a2: offsets vector
+ Label next_capture, done;
+ // Capture register counter starts from number of capture registers and
+ // counts down until wrapping after zero.
+ __ Addu(a0,
+ last_match_info_elements,
+ Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
+ __ bind(&next_capture);
+ __ Subu(a1, a1, Operand(1));
+ __ Branch(&done, lt, a1, Operand(zero_reg));
+ // Read the value from the static offsets vector buffer.
+ __ lw(a3, MemOperand(a2, 0));
+ __ addiu(a2, a2, kPointerSize);
+ // Store the smi value in the last match info.
+ __ sll(a3, a3, kSmiTagSize); // Convert to Smi.
+ __ sw(a3, MemOperand(a0, 0));
+ __ Branch(&next_capture, USE_DELAY_SLOT);
+ __ addiu(a0, a0, kPointerSize); // In branch delay slot.
+
+ __ bind(&done);
+
+ // Return last match info.
+ __ lw(v0, MemOperand(sp, kLastMatchInfoOffset));
+ __ DropAndRet(4);
+
+ // Do the runtime call to execute the regexp.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+
+ // Deferred code for string handling.
+ // (6) Not a long external string? If yes, go to (8).
+ __ bind(&not_seq_nor_cons);
+ // Go to (8).
+ __ Branch(&not_long_external, gt, a1, Operand(kExternalStringTag));
+
+ // (7) External string. Make it, offset-wise, look like a sequential string.
+ __ 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,
+ kExternalStringExpectedButNotFound,
+ 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 == SeqOneByteString::kHeaderSize);
+ __ Subu(subject,
+ subject,
+ SeqTwoByteString::kHeaderSize - kHeapObjectTag);
+ __ jmp(&seq_string); // Go to (5).
+
+ // (8) Short external string or not a string? If yes, bail out to runtime.
+ __ bind(&not_long_external);
+ STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
+ __ And(at, a1, Operand(kIsNotStringMask | kShortExternalStringMask));
+ __ Branch(&runtime, ne, at, Operand(zero_reg));
+
+ // (9) Sliced string. Replace subject with parent. Go to (4).
+ // Load offset into t0 and replace subject string with parent.
+ __ lw(t0, FieldMemOperand(subject, SlicedString::kOffsetOffset));
+ __ sra(t0, t0, kSmiTagSize);
+ __ lw(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
+ __ jmp(&check_underlying); // Go to (4).
+#endif // V8_INTERPRETED_REGEXP
+}
+
+
+void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
+ const int kMaxInlineLength = 100;
+ Label slowcase;
+ Label done;
+ __ lw(a1, MemOperand(sp, kPointerSize * 2));
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ __ JumpIfNotSmi(a1, &slowcase);
+ __ Branch(&slowcase, hi, a1, Operand(Smi::FromInt(kMaxInlineLength)));
+ // Smi-tagging is equivalent to multiplying by 2.
+ // Allocate RegExpResult followed by FixedArray with size in ebx.
+ // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
+ // Elements: [Map][Length][..elements..]
+ // Size of JSArray with two in-object properties and the header of a
+ // FixedArray.
+ int objects_size =
+ (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
+ __ srl(t1, a1, kSmiTagSize + kSmiShiftSize);
+ __ Addu(a2, t1, Operand(objects_size));
+ __ Allocate(
+ a2, // In: Size, in words.
+ v0, // Out: Start of allocation (tagged).
+ a3, // Scratch register.
+ t0, // Scratch register.
+ &slowcase,
+ static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
+ // v0: Start of allocated area, object-tagged.
+ // a1: Number of elements in array, as smi.
+ // t1: Number of elements, untagged.
+
+ // Set JSArray map to global.regexp_result_map().
+ // Set empty properties FixedArray.
+ // Set elements to point to FixedArray allocated right after the JSArray.
+ // Interleave operations for better latency.
+ __ lw(a2, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+ __ Addu(a3, v0, Operand(JSRegExpResult::kSize));
+ __ li(t0, Operand(masm->isolate()->factory()->empty_fixed_array()));
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kNativeContextOffset));
+ __ sw(a3, FieldMemOperand(v0, JSObject::kElementsOffset));
+ __ lw(a2, ContextOperand(a2, Context::REGEXP_RESULT_MAP_INDEX));
+ __ sw(t0, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ sw(a2, FieldMemOperand(v0, HeapObject::kMapOffset));
+
+ // 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));
+ __ sw(a2, FieldMemOperand(v0, JSRegExpResult::kIndexOffset));
+ __ sw(t2, FieldMemOperand(v0, JSArray::kLengthOffset));
+
+ // Fill out the elements FixedArray.
+ // v0: JSArray, tagged.
+ // a3: FixedArray, tagged.
+ // t1: Number of elements in array, untagged.
+
+ // Set map.
+ __ li(a2, Operand(masm->isolate()->factory()->fixed_array_map()));
+ __ sw(a2, FieldMemOperand(a3, HeapObject::kMapOffset));
+ // Set FixedArray length.
+ __ sll(t2, t1, kSmiTagSize);
+ __ sw(t2, FieldMemOperand(a3, FixedArray::kLengthOffset));
+ // Fill contents of fixed-array with undefined.
+ __ LoadRoot(a2, Heap::kUndefinedValueRootIndex);
+ __ Addu(a3, a3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // Fill fixed array elements with undefined.
+ // v0: JSArray, tagged.
+ // a2: undefined.
+ // a3: Start of elements in FixedArray.
+ // t1: Number of elements to fill.
+ Label loop;
+ __ sll(t1, t1, kPointerSizeLog2); // Convert num elements to num bytes.
+ __ addu(t1, t1, a3); // Point past last element to store.
+ __ bind(&loop);
+ __ Branch(&done, ge, a3, Operand(t1)); // Break when a3 past end of elem.
+ __ sw(a2, MemOperand(a3));
+ __ Branch(&loop, USE_DELAY_SLOT);
+ __ addiu(a3, a3, kPointerSize); // In branch delay slot.
+
+ __ bind(&done);
+ __ DropAndRet(3);
+
+ __ bind(&slowcase);
+ __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
+}
+
+
+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 initialize, done, miss, megamorphic, not_array_function;
+
+ 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, Cell::kValueOffset));
+
+ // A monomorphic cache hit or an already megamorphic state: invoke the
+ // function without changing the state.
+ __ Branch(&done, eq, a3, Operand(a1));
+
+ // If we came here, we need to see if we are the array function.
+ // If we didn't have a matching function, and we didn't find the megamorph
+ // sentinel, then we have in the cell either some other function or an
+ // AllocationSite. Do a map check on the object in a3.
+ Handle<Map> allocation_site_map(
+ masm->isolate()->heap()->allocation_site_map(),
+ masm->isolate());
+ __ lw(t1, FieldMemOperand(a3, 0));
+ __ LoadRoot(at, Heap::kAllocationSiteMapRootIndex);
+ __ Branch(&miss, ne, t1, Operand(at));
+
+ // Make sure the function is the Array() function
+ __ LoadArrayFunction(a3);
+ __ Branch(&megamorphic, ne, a1, Operand(a3));
+ __ jmp(&done);
+
+ __ bind(&miss);
+
+ // A monomorphic miss (i.e, here the cache is not uninitialized) goes
+ // megamorphic.
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(&initialize, eq, a3, Operand(at));
+ // MegamorphicSentinel is an immortal immovable object (undefined) so no
+ // write-barrier is needed.
+ __ bind(&megamorphic);
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ sw(at, FieldMemOperand(a2, Cell::kValueOffset));
+ __ jmp(&done);
+
+ // An uninitialized cache is patched with the function or sentinel to
+ // indicate the ElementsKind if function is the Array constructor.
+ __ bind(&initialize);
+ // Make sure the function is the Array() function
+ __ LoadArrayFunction(a3);
+ __ Branch(&not_array_function, ne, a1, Operand(a3));
+
+ // The target function is the Array constructor.
+ // Create an AllocationSite if we don't already have it, store it in the cell.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ const RegList kSavedRegs =
+ 1 << 4 | // a0
+ 1 << 5 | // a1
+ 1 << 6; // a2
+
+ __ MultiPush(kSavedRegs);
+
+ CreateAllocationSiteStub create_stub;
+ __ CallStub(&create_stub);
+
+ __ MultiPop(kSavedRegs);
+ }
+ __ Branch(&done);
+
+ __ bind(&not_array_function);
+ __ sw(a1, FieldMemOperand(a2, Cell::kValueOffset));
+ // No need for a write barrier here - cells are rescanned.
+
+ __ bind(&done);
+}
+
+
+void CallFunctionStub::Generate(MacroAssembler* masm) {
+ // 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
+ // function stub.
+ if (ReceiverMightBeImplicit()) {
+ Label call;
+ // Get the receiver from the stack.
+ // function, receiver [, arguments]
+ __ lw(t0, MemOperand(sp, argc_ * kPointerSize));
+ // Call as function is indicated with the hole.
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(&call, ne, t0, Operand(at));
+ // Patch the receiver on the stack with the global receiver object.
+ __ lw(a3,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ lw(a3, FieldMemOperand(a3, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a3, MemOperand(sp, argc_ * kPointerSize));
+ __ bind(&call);
+ }
+
+ // Check that the function is really a JavaScript function.
+ // a1: pushed function (to be verified)
+ __ JumpIfSmi(a1, &non_function);
+ // Get the map of the function object.
+ __ GetObjectType(a1, a3, a3);
+ __ Branch(&slow, ne, a3, Operand(JS_FUNCTION_TYPE));
+
+ if (RecordCallTarget()) {
+ GenerateRecordCallTarget(masm);
+ }
+
+ // Fast-case: Invoke the function now.
+ // a1: pushed function
+ ParameterCount actual(argc_);
+
+ if (ReceiverMightBeImplicit()) {
+ Label call_as_function;
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(&call_as_function, eq, t0, Operand(at));
+ __ InvokeFunction(a1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_METHOD);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(a1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
+
+ // 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.
+ ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(masm->isolate()),
+ masm->isolate()->heap()->undefined_value());
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ sw(at, FieldMemOperand(a2, Cell::kValueOffset));
+ }
+ // Check for function proxy.
+ __ Branch(&non_function, ne, a3, Operand(JS_FUNCTION_PROXY_TYPE));
+ __ push(a1); // Put proxy as additional argument.
+ __ li(a0, Operand(argc_ + 1, RelocInfo::NONE32));
+ __ li(a2, Operand(0, RelocInfo::NONE32));
+ __ 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_)); // Set up the number of arguments.
+ __ mov(a2, zero_reg);
+ __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION);
+ __ SetCallKind(t1, CALL_AS_METHOD);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+}
+
+
+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.
+ Register jmp_reg = a3;
+ __ lw(jmp_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(jmp_reg, FieldMemOperand(jmp_reg,
+ SharedFunctionInfo::kConstructStubOffset));
+ __ Addu(at, jmp_reg, 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::NONE32));
+ __ SetCallKind(t1, CALL_AS_METHOD);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+}
+
+
+// StringCharCodeAtGenerator.
+void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
+ Label flat_string;
+ Label ascii_string;
+ Label got_char_code;
+ Label sliced_string;
+
+ ASSERT(!t0.is(index_));
+ ASSERT(!t0.is(result_));
+ ASSERT(!t0.is(object_));
+
+ // If the receiver is a smi trigger the non-string case.
+ __ JumpIfSmi(object_, receiver_not_string_);
+
+ // Fetch the instance type of the receiver into result register.
+ __ lw(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
+ __ lbu(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
+ // If the receiver is not a string trigger the non-string case.
+ __ And(t0, result_, Operand(kIsNotStringMask));
+ __ Branch(receiver_not_string_, ne, t0, Operand(zero_reg));
+
+ // If the index is non-smi trigger the non-smi case.
+ __ JumpIfNotSmi(index_, &index_not_smi_);
+
+ __ bind(&got_smi_index_);
+
+ // Check for index out of range.
+ __ lw(t0, FieldMemOperand(object_, String::kLengthOffset));
+ __ Branch(index_out_of_range_, ls, t0, Operand(index_));
+
+ __ sra(index_, index_, kSmiTagSize);
+
+ StringCharLoadGenerator::Generate(masm,
+ object_,
+ index_,
+ result_,
+ &call_runtime_);
+
+ __ sll(result_, result_, kSmiTagSize);
+ __ bind(&exit_);
+}
+
+
+void StringCharCodeAtGenerator::GenerateSlow(
+ MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase);
+
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_,
+ result_,
+ Heap::kHeapNumberMapRootIndex,
+ index_not_number_,
+ DONT_DO_SMI_CHECK);
+ call_helper.BeforeCall(masm);
+ // Consumed by runtime conversion function:
+ __ Push(object_, index_);
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+
+ __ 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(index_, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ Branch(&got_smi_index_);
+
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // 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);
+
+ __ Move(result_, v0);
+
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase);
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharFromCodeGenerator
+
+void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+
+ ASSERT(!t0.is(result_));
+ ASSERT(!t0.is(code_));
+
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiShiftSize == 0);
+ ASSERT(IsPowerOf2(String::kMaxOneByteCharCode + 1));
+ __ And(t0,
+ code_,
+ Operand(kSmiTagMask |
+ ((~String::kMaxOneByteCharCode) << kSmiTagSize)));
+ __ Branch(&slow_case_, ne, t0, Operand(zero_reg));
+
+ __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
+ // At this point code register contains smi tagged ASCII char code.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ sll(t0, code_, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(result_, result_, t0);
+ __ lw(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ Branch(&slow_case_, eq, result_, Operand(t0));
+ __ bind(&exit_);
+}
+
+
+void StringCharFromCodeGenerator::GenerateSlow(
+ MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ __ Abort(kUnexpectedFallthroughToCharFromCodeSlowCase);
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ __ Move(result_, v0);
+
+ call_helper.AfterCall(masm);
+ __ Branch(&exit_);
+
+ __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase);
+}
+
+
+void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii) {
+ Label loop;
+ Label done;
+ // This loop just copies one character at a time, as it is only used for
+ // very short strings.
+ if (!ascii) {
+ __ addu(count, count, count);
+ }
+ __ Branch(&done, eq, count, Operand(zero_reg));
+ __ addu(count, dest, count); // Count now points to the last dest byte.
+
+ __ bind(&loop);
+ __ lbu(scratch, MemOperand(src));
+ __ addiu(src, src, 1);
+ __ sb(scratch, MemOperand(dest));
+ __ addiu(dest, dest, 1);
+ __ Branch(&loop, lt, dest, Operand(count));
+
+ __ bind(&done);
+}
+
+
+enum CopyCharactersFlags {
+ COPY_ASCII = 1,
+ DEST_ALWAYS_ALIGNED = 2
+};
+
+
+void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Register scratch5,
+ int flags) {
+ bool ascii = (flags & COPY_ASCII) != 0;
+ bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
+
+ if (dest_always_aligned && FLAG_debug_code) {
+ // Check that destination is actually word aligned if the flag says
+ // that it is.
+ __ And(scratch4, dest, Operand(kPointerAlignmentMask));
+ __ Check(eq,
+ kDestinationOfCopyNotAligned,
+ scratch4,
+ Operand(zero_reg));
+ }
+
+ const int kReadAlignment = 4;
+ const int kReadAlignmentMask = kReadAlignment - 1;
+ // Ensure that reading an entire aligned word containing the last character
+ // of a string will not read outside the allocated area (because we pad up
+ // to kObjectAlignment).
+ STATIC_ASSERT(kObjectAlignment >= kReadAlignment);
+ // Assumes word reads and writes are little endian.
+ // Nothing to do for zero characters.
+ Label done;
+
+ if (!ascii) {
+ __ addu(count, count, count);
+ }
+ __ Branch(&done, eq, count, Operand(zero_reg));
+
+ Label byte_loop;
+ // Must copy at least eight bytes, otherwise just do it one byte at a time.
+ __ Subu(scratch1, count, Operand(8));
+ __ Addu(count, dest, Operand(count));
+ Register limit = count; // Read until src equals this.
+ __ Branch(&byte_loop, lt, scratch1, Operand(zero_reg));
+
+ if (!dest_always_aligned) {
+ // Align dest by byte copying. Copies between zero and three bytes.
+ __ And(scratch4, dest, Operand(kReadAlignmentMask));
+ Label dest_aligned;
+ __ Branch(&dest_aligned, eq, scratch4, Operand(zero_reg));
+ Label aligned_loop;
+ __ bind(&aligned_loop);
+ __ lbu(scratch1, MemOperand(src));
+ __ addiu(src, src, 1);
+ __ sb(scratch1, MemOperand(dest));
+ __ addiu(dest, dest, 1);
+ __ addiu(scratch4, scratch4, 1);
+ __ Branch(&aligned_loop, le, scratch4, Operand(kReadAlignmentMask));
+ __ bind(&dest_aligned);
+ }
+
+ Label simple_loop;
+
+ __ And(scratch4, src, Operand(kReadAlignmentMask));
+ __ Branch(&simple_loop, eq, scratch4, Operand(zero_reg));
+
+ // Loop for src/dst that are not aligned the same way.
+ // This loop uses lwl and lwr instructions. These instructions
+ // depend on the endianness, and the implementation assumes little-endian.
+ {
+ Label loop;
+ __ bind(&loop);
+ __ lwr(scratch1, MemOperand(src));
+ __ Addu(src, src, Operand(kReadAlignment));
+ __ lwl(scratch1, MemOperand(src, -1));
+ __ sw(scratch1, MemOperand(dest));
+ __ Addu(dest, dest, Operand(kReadAlignment));
+ __ Subu(scratch2, limit, dest);
+ __ Branch(&loop, ge, scratch2, Operand(kReadAlignment));
+ }
+
+ __ Branch(&byte_loop);
+
+ // Simple loop.
+ // Copy words from src to dest, until less than four bytes left.
+ // Both src and dest are word aligned.
+ __ bind(&simple_loop);
+ {
+ Label loop;
+ __ bind(&loop);
+ __ lw(scratch1, MemOperand(src));
+ __ Addu(src, src, Operand(kReadAlignment));
+ __ sw(scratch1, MemOperand(dest));
+ __ Addu(dest, dest, Operand(kReadAlignment));
+ __ Subu(scratch2, limit, dest);
+ __ Branch(&loop, ge, scratch2, Operand(kReadAlignment));
+ }
+
+ // Copy bytes from src to dest until dest hits limit.
+ __ bind(&byte_loop);
+ // Test if dest has already reached the limit.
+ __ Branch(&done, ge, dest, Operand(limit));
+ __ lbu(scratch1, MemOperand(src));
+ __ addiu(src, src, 1);
+ __ sb(scratch1, MemOperand(dest));
+ __ addiu(dest, dest, 1);
+ __ Branch(&byte_loop);
+
+ __ bind(&done);
+}
+
+
+void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Register scratch5,
+ Label* not_found) {
+ // Register scratch3 is the general scratch register in this function.
+ Register scratch = scratch3;
+
+ // Make sure that both characters are not digits as such strings has a
+ // different hash algorithm. Don't try to look for these in the string table.
+ Label not_array_index;
+ __ Subu(scratch, c1, Operand(static_cast<int>('0')));
+ __ Branch(&not_array_index,
+ Ugreater,
+ scratch,
+ Operand(static_cast<int>('9' - '0')));
+ __ Subu(scratch, c2, Operand(static_cast<int>('0')));
+
+ // If check failed combine both characters into single halfword.
+ // This is required by the contract of the method: code at the
+ // not_found branch expects this combination in c1 register.
+ Label tmp;
+ __ sll(scratch1, c2, kBitsPerByte);
+ __ Branch(&tmp, Ugreater, scratch, Operand(static_cast<int>('9' - '0')));
+ __ Or(c1, c1, scratch1);
+ __ bind(&tmp);
+ __ Branch(
+ not_found, Uless_equal, scratch, Operand(static_cast<int>('9' - '0')));
+
+ __ bind(&not_array_index);
+ // Calculate the two character string hash.
+ Register hash = scratch1;
+ StringHelper::GenerateHashInit(masm, hash, c1);
+ StringHelper::GenerateHashAddCharacter(masm, hash, c2);
+ StringHelper::GenerateHashGetHash(masm, hash);
+
+ // Collect the two characters in a register.
+ Register chars = c1;
+ __ sll(scratch, c2, kBitsPerByte);
+ __ Or(chars, chars, scratch);
+
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string.
+
+ // Load string table.
+ // Load address of first element of the string table.
+ Register string_table = c2;
+ __ LoadRoot(string_table, Heap::kStringTableRootIndex);
+
+ Register undefined = scratch4;
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ // Calculate capacity mask from the string table capacity.
+ Register mask = scratch2;
+ __ lw(mask, FieldMemOperand(string_table, StringTable::kCapacityOffset));
+ __ sra(mask, mask, 1);
+ __ Addu(mask, mask, -1);
+
+ // Calculate untagged address of the first element of the string table.
+ Register first_string_table_element = string_table;
+ __ Addu(first_string_table_element, string_table,
+ Operand(StringTable::kElementsStartOffset - kHeapObjectTag));
+
+ // Registers.
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string
+ // mask: capacity mask
+ // first_string_table_element: address of the first element of
+ // the string table
+ // undefined: the undefined object
+ // scratch: -
+
+ // Perform a number of probes in the string table.
+ const int kProbes = 4;
+ Label found_in_string_table;
+ Label next_probe[kProbes];
+ Register candidate = scratch5; // Scratch register contains candidate.
+ for (int i = 0; i < kProbes; i++) {
+ // Calculate entry in string table.
+ if (i > 0) {
+ __ Addu(candidate, hash, Operand(StringTable::GetProbeOffset(i)));
+ } else {
+ __ mov(candidate, hash);
+ }
+
+ __ And(candidate, candidate, Operand(mask));
+
+ // Load the entry from the symble table.
+ STATIC_ASSERT(StringTable::kEntrySize == 1);
+ __ sll(scratch, candidate, kPointerSizeLog2);
+ __ Addu(scratch, scratch, first_string_table_element);
+ __ lw(candidate, MemOperand(scratch));
+
+ // If entry is undefined no string with this hash can be found.
+ Label is_string;
+ __ GetObjectType(candidate, scratch, scratch);
+ __ Branch(&is_string, ne, scratch, Operand(ODDBALL_TYPE));
+
+ __ Branch(not_found, eq, undefined, Operand(candidate));
+ // Must be the hole (deleted entry).
+ if (FLAG_debug_code) {
+ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
+ __ Assert(eq, kOddballInStringTableIsNotUndefinedOrTheHole,
+ scratch, Operand(candidate));
+ }
+ __ jmp(&next_probe[i]);
+
+ __ bind(&is_string);
+
+ // Check that the candidate is a non-external ASCII string. The instance
+ // type is still in the scratch register from the CompareObjectType
+ // operation.
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch, &next_probe[i]);
+
+ // If length is not 2 the string is not a candidate.
+ __ lw(scratch, FieldMemOperand(candidate, String::kLengthOffset));
+ __ Branch(&next_probe[i], ne, scratch, Operand(Smi::FromInt(2)));
+
+ // Check if the two characters match.
+ // Assumes that word load is little endian.
+ __ lhu(scratch, FieldMemOperand(candidate, SeqOneByteString::kHeaderSize));
+ __ Branch(&found_in_string_table, eq, chars, Operand(scratch));
+ __ bind(&next_probe[i]);
+ }
+
+ // No matching 2 character string found by probing.
+ __ jmp(not_found);
+
+ // Scratch register contains result when we fall through to here.
+ Register result = candidate;
+ __ bind(&found_in_string_table);
+ __ mov(v0, result);
+}
+
+
+void StringHelper::GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character) {
+ // hash = seed + character + ((seed + character) << 10);
+ __ LoadRoot(hash, Heap::kHashSeedRootIndex);
+ // Untag smi seed and add the character.
+ __ SmiUntag(hash);
+ __ addu(hash, hash, character);
+ __ sll(at, hash, 10);
+ __ addu(hash, hash, at);
+ // hash ^= hash >> 6;
+ __ srl(at, hash, 6);
+ __ xor_(hash, hash, at);
+}
+
+
+void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character) {
+ // hash += character;
+ __ addu(hash, hash, character);
+ // hash += hash << 10;
+ __ sll(at, hash, 10);
+ __ addu(hash, hash, at);
+ // hash ^= hash >> 6;
+ __ srl(at, hash, 6);
+ __ xor_(hash, hash, at);
+}
+
+
+void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
+ Register hash) {
+ // hash += hash << 3;
+ __ sll(at, hash, 3);
+ __ addu(hash, hash, at);
+ // hash ^= 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, StringHasher::kZeroHash);
+ __ Movz(hash, at, hash);
+}
+
+
+void SubStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+ // Stack frame on entry.
+ // ra: return address
+ // sp[0]: to
+ // sp[4]: from
+ // sp[8]: string
+
+ // This stub is called from the native-call %_SubString(...), so
+ // nothing can be assumed about the arguments. It is tested that:
+ // "string" is a sequential string,
+ // both "from" and "to" are smis, and
+ // 0 <= from <= to <= string.length.
+ // If any of these assumptions fail, we call the runtime system.
+
+ const int kToOffset = 0 * kPointerSize;
+ const int kFromOffset = 1 * kPointerSize;
+ const int kStringOffset = 2 * kPointerSize;
+
+ __ lw(a2, MemOperand(sp, kToOffset));
+ __ lw(a3, MemOperand(sp, kFromOffset));
+ STATIC_ASSERT(kFromOffset == kToOffset + 4);
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+
+ // Utilize delay slots. SmiUntag doesn't emit a jump, everything else is
+ // safe in this case.
+ __ UntagAndJumpIfNotSmi(a2, a2, &runtime);
+ __ UntagAndJumpIfNotSmi(a3, a3, &runtime);
+ // Both a2 and a3 are untagged integers.
+
+ __ Branch(&runtime, lt, a3, Operand(zero_reg)); // From < 0.
+
+ __ Branch(&runtime, gt, a3, Operand(a2)); // Fail if from > to.
+ __ Subu(a2, a2, a3);
+
+ // Make sure first argument is a string.
+ __ lw(v0, MemOperand(sp, kStringOffset));
+ __ JumpIfSmi(v0, &runtime);
+ __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
+ __ And(t0, a1, Operand(kIsNotStringMask));
+
+ __ Branch(&runtime, ne, t0, Operand(zero_reg));
+
+ Label single_char;
+ __ Branch(&single_char, eq, a2, Operand(1));
+
+ // Short-cut for the case of trivial substring.
+ Label return_v0;
+ // v0: original string
+ // a2: result string length
+ __ lw(t0, FieldMemOperand(v0, String::kLengthOffset));
+ __ sra(t0, t0, 1);
+ // Return original string.
+ __ Branch(&return_v0, eq, a2, Operand(t0));
+ // Longer than original string's length or negative: unsafe arguments.
+ __ Branch(&runtime, hi, a2, Operand(t0));
+ // Shorter than original string's length: an actual substring.
+
+ // 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));
+ // t0 is used as a scratch register and can be overwritten in either case.
+ __ 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::kempty_stringRootIndex);
+ __ 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);
+
+ __ bind(&sliced_string);
+ // Sliced string. Fetch parent and correct start index by offset.
+ __ lw(t1, FieldMemOperand(v0, SlicedString::kParentOffset));
+ __ lw(t0, FieldMemOperand(v0, SlicedString::kOffsetOffset));
+ __ 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 & kOneByteStringTag) != 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(t1, FieldMemOperand(v0, SlicedString::kParentOffset));
+ __ sw(a3, FieldMemOperand(v0, SlicedString::kOffsetOffset));
+ __ 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 == SeqOneByteString::kHeaderSize);
+ __ Addu(t1, t1, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+
+ __ bind(&allocate_result);
+ // Sequential acii string. Allocate the result.
+ STATIC_ASSERT((kOneByteStringTag & 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);
+
+ // Locate first character of result.
+ __ Addu(a1, v0, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+
+ // v0: result string
+ // a1: first character of result string
+ // a2: result string length
+ // t1: first character of substring to copy
+ STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ StringHelper::GenerateCopyCharactersLong(
+ masm, a1, t1, a2, a3, t0, t2, t3, t4, COPY_ASCII | DEST_ALWAYS_ALIGNED);
+ __ jmp(&return_v0);
+
+ // Allocate and copy the resulting two-byte string.
+ __ bind(&two_byte_sequential);
+ __ AllocateTwoByteString(v0, a2, t0, t2, t3, &runtime);
+
+ // 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));
+
+ // v0: result string.
+ // a1: first character of result.
+ // a2: result length.
+ // t1: first character of substring to copy.
+ STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ StringHelper::GenerateCopyCharactersLong(
+ masm, a1, t1, a2, a3, t0, t2, t3, t4, DEST_ALWAYS_ALIGNED);
+
+ __ bind(&return_v0);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->sub_string_native(), 1, a3, t0);
+ __ DropAndRet(3);
+
+ // Just jump to runtime to create the sub string.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kSubString, 3, 1);
+
+ __ bind(&single_char);
+ // v0: original string
+ // a1: instance type
+ // a2: length
+ // a3: from index (untagged)
+ __ SmiTag(a3, a3);
+ StringCharAtGenerator generator(
+ v0, a3, a2, v0, &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm);
+ __ DropAndRet(3);
+ generator.SkipSlow(masm, &runtime);
+}
+
+
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label strings_not_equal, check_zero_length;
+ __ lw(length, FieldMemOperand(left, String::kLengthOffset));
+ __ lw(scratch2, FieldMemOperand(right, String::kLengthOffset));
+ __ Branch(&check_zero_length, eq, length, Operand(scratch2));
+ __ bind(&strings_not_equal);
+ ASSERT(is_int16(NOT_EQUAL));
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(Smi::FromInt(NOT_EQUAL)));
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Branch(&compare_chars, ne, length, Operand(zero_reg));
+ ASSERT(is_int16(EQUAL));
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+
+ // Compare characters.
+ __ bind(&compare_chars);
+
+ GenerateAsciiCharsCompareLoop(masm,
+ left, right, length, scratch2, scratch3, v0,
+ &strings_not_equal);
+
+ // Characters are equal.
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+}
+
+
+void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4) {
+ Label result_not_equal, compare_lengths;
+ // Find minimum length and length difference.
+ __ lw(scratch1, FieldMemOperand(left, String::kLengthOffset));
+ __ lw(scratch2, FieldMemOperand(right, String::kLengthOffset));
+ __ Subu(scratch3, scratch1, Operand(scratch2));
+ Register length_delta = scratch3;
+ __ slt(scratch4, scratch2, scratch1);
+ __ Movn(scratch1, scratch2, scratch4);
+ Register min_length = scratch1;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Branch(&compare_lengths, eq, min_length, Operand(zero_reg));
+
+ // Compare loop.
+ GenerateAsciiCharsCompareLoop(masm,
+ left, right, min_length, scratch2, scratch4, v0,
+ &result_not_equal);
+
+ // Compare lengths - strings up to min-length are equal.
+ __ bind(&compare_lengths);
+ ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
+ // Use length_delta as result if it's zero.
+ __ mov(scratch2, length_delta);
+ __ mov(scratch4, zero_reg);
+ __ mov(v0, zero_reg);
+
+ __ bind(&result_not_equal);
+ // Conditionally update the result based either on length_delta or
+ // the last comparion performed in the loop above.
+ Label ret;
+ __ Branch(&ret, eq, scratch2, Operand(scratch4));
+ __ li(v0, Operand(Smi::FromInt(GREATER)));
+ __ Branch(&ret, gt, scratch2, Operand(scratch4));
+ __ li(v0, Operand(Smi::FromInt(LESS)));
+ __ bind(&ret);
+ __ Ret();
+}
+
+
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* chars_not_equal) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiUntag(length);
+ __ Addu(scratch1, length,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ Addu(left, left, Operand(scratch1));
+ __ Addu(right, right, Operand(scratch1));
+ __ Subu(length, zero_reg, length);
+ Register index = length; // index = -length;
+
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ Addu(scratch3, left, index);
+ __ lbu(scratch1, MemOperand(scratch3));
+ __ Addu(scratch3, right, index);
+ __ lbu(scratch2, MemOperand(scratch3));
+ __ Branch(chars_not_equal, ne, scratch1, Operand(scratch2));
+ __ Addu(index, index, 1);
+ __ Branch(&loop, ne, index, Operand(zero_reg));
+}
+
+
+void StringCompareStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ Counters* counters = masm->isolate()->counters();
+
+ // Stack frame on entry.
+ // sp[0]: right string
+ // sp[4]: left string
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize)); // Left.
+ __ lw(a0, MemOperand(sp, 0 * kPointerSize)); // Right.
+
+ Label not_same;
+ __ Branch(&not_same, ne, a0, Operand(a1));
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+ __ IncrementCounter(counters->string_compare_native(), 1, a1, a2);
+ __ DropAndRet(2);
+
+ __ bind(&not_same);
+
+ // Check that both objects are sequential ASCII strings.
+ __ JumpIfNotBothSequentialAsciiStrings(a1, a0, a2, a3, &runtime);
+
+ // Compare flat ASCII strings natively. Remove arguments from stack first.
+ __ IncrementCounter(counters->string_compare_native(), 1, a2, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ GenerateCompareFlatAsciiStrings(masm, a1, a0, a2, a3, t0, t1);
+
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+}
+
+
+void StringAddStub::Generate(MacroAssembler* masm) {
+ Label call_runtime, call_builtin;
+ Builtins::JavaScript builtin_id = Builtins::ADD;
+
+ Counters* counters = masm->isolate()->counters();
+
+ // Stack on entry:
+ // sp[0]: second argument (right).
+ // sp[4]: first argument (left).
+
+ // Load the two arguments.
+ __ lw(a0, MemOperand(sp, 1 * kPointerSize)); // First argument.
+ __ lw(a1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
+
+ // Make sure that both arguments are strings if not known in advance.
+ // Otherwise, at least one of the arguments is definitely a string,
+ // and we convert the one that is not known to be a string.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
+ ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT);
+ ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT);
+ __ JumpIfEitherSmi(a0, a1, &call_runtime);
+ // Load instance types.
+ __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kStringTag == 0);
+ // If either is not a string, go to runtime.
+ __ Or(t4, t0, Operand(t1));
+ __ And(t4, t4, Operand(kIsNotStringMask));
+ __ Branch(&call_runtime, ne, t4, Operand(zero_reg));
+ } else if ((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
+ ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == 0);
+ GenerateConvertArgument(
+ masm, 1 * kPointerSize, a0, a2, a3, t0, t1, &call_builtin);
+ builtin_id = Builtins::STRING_ADD_RIGHT;
+ } else if ((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
+ ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == 0);
+ GenerateConvertArgument(
+ masm, 0 * kPointerSize, a1, a2, a3, t0, t1, &call_builtin);
+ builtin_id = Builtins::STRING_ADD_LEFT;
+ }
+
+ // Both arguments are strings.
+ // a0: first string
+ // a1: second string
+ // t0: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // t1: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ {
+ Label strings_not_empty;
+ // Check if either of the strings are empty. In that case return the other.
+ // These tests use zero-length check on string-length whch is an Smi.
+ // Assert that Smi::FromInt(0) is really 0.
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT(Smi::FromInt(0) == 0);
+ __ lw(a2, FieldMemOperand(a0, String::kLengthOffset));
+ __ lw(a3, FieldMemOperand(a1, String::kLengthOffset));
+ __ mov(v0, a0); // Assume we'll return first string (from a0).
+ __ Movz(v0, a1, a2); // If first is empty, return second (from a1).
+ __ slt(t4, zero_reg, a2); // if (a2 > 0) t4 = 1.
+ __ slt(t5, zero_reg, a3); // if (a3 > 0) t5 = 1.
+ __ and_(t4, t4, t5); // Branch if both strings were non-empty.
+ __ Branch(&strings_not_empty, ne, t4, Operand(zero_reg));
+
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ DropAndRet(2);
+
+ __ bind(&strings_not_empty);
+ }
+
+ // Untag both string-lengths.
+ __ sra(a2, a2, kSmiTagSize);
+ __ sra(a3, a3, kSmiTagSize);
+
+ // Both strings are non-empty.
+ // a0: first string
+ // a1: second string
+ // a2: length of first string
+ // a3: length of second string
+ // t0: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // t1: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // Look at the length of the result of adding the two strings.
+ Label string_add_flat_result, longer_than_two;
+ // Adding two lengths can't overflow.
+ STATIC_ASSERT(String::kMaxLength < String::kMaxLength * 2);
+ __ Addu(t2, a2, Operand(a3));
+ // Use the string table when adding two one character strings, as it
+ // helps later optimizations to return a string here.
+ __ Branch(&longer_than_two, ne, t2, Operand(2));
+
+ // Check that both strings are non-external ASCII strings.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
+ __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
+ }
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(t0, t1, t2, t3,
+ &call_runtime);
+
+ // Get the two characters forming the sub string.
+ __ lbu(a2, FieldMemOperand(a0, SeqOneByteString::kHeaderSize));
+ __ lbu(a3, FieldMemOperand(a1, SeqOneByteString::kHeaderSize));
+
+ // Try to lookup two character string in string table. If it is not found
+ // just allocate a new one.
+ Label make_two_character_string;
+ StringHelper::GenerateTwoCharacterStringTableProbe(
+ masm, a2, a3, t2, t3, t0, t1, t5, &make_two_character_string);
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ DropAndRet(2);
+
+ __ bind(&make_two_character_string);
+ // Resulting string has length 2 and first chars of two strings
+ // are combined into single halfword in a2 register.
+ // So we can fill resulting string without two loops by a single
+ // halfword store instruction (which assumes that processor is
+ // in a little endian mode).
+ __ li(t2, Operand(2));
+ __ AllocateAsciiString(v0, t2, t0, t1, t5, &call_runtime);
+ __ sh(a2, FieldMemOperand(v0, SeqOneByteString::kHeaderSize));
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ DropAndRet(2);
+
+ __ bind(&longer_than_two);
+ // Check if resulting string will be flat.
+ __ Branch(&string_add_flat_result, lt, t2, 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(&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.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
+ __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
+ }
+ Label non_ascii, allocated, ascii_data;
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ // 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(v0, t2, t0, t1, &call_runtime);
+ __ bind(&allocated);
+ // Fill the fields of the cons string.
+ Label skip_write_barrier, after_writing;
+ ExternalReference high_promotion_mode = ExternalReference::
+ new_space_high_promotion_mode_active_address(masm->isolate());
+ __ li(t0, Operand(high_promotion_mode));
+ __ lw(t0, MemOperand(t0, 0));
+ __ Branch(&skip_write_barrier, eq, t0, Operand(zero_reg));
+
+ __ mov(t3, v0);
+ __ sw(a0, FieldMemOperand(t3, ConsString::kFirstOffset));
+ __ RecordWriteField(t3,
+ ConsString::kFirstOffset,
+ a0,
+ t0,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs);
+ __ sw(a1, FieldMemOperand(t3, ConsString::kSecondOffset));
+ __ RecordWriteField(t3,
+ ConsString::kSecondOffset,
+ a1,
+ t0,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs);
+ __ jmp(&after_writing);
+
+ __ bind(&skip_write_barrier);
+ __ sw(a0, FieldMemOperand(v0, ConsString::kFirstOffset));
+ __ sw(a1, FieldMemOperand(v0, ConsString::kSecondOffset));
+
+ __ bind(&after_writing);
+
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ DropAndRet(2);
+
+ __ bind(&non_ascii);
+ // At least one of the strings is two-byte. Check whether it happens
+ // to contain only one byte characters.
+ // t0: first instance type.
+ // t1: second instance type.
+ // Branch to if _both_ instances have kOneByteDataHintMask set.
+ __ And(at, t0, Operand(kOneByteDataHintMask));
+ __ and_(at, at, t1);
+ __ Branch(&ascii_data, ne, at, Operand(zero_reg));
+ __ Xor(t0, t0, Operand(t1));
+ STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
+ __ And(t0, t0, Operand(kOneByteStringTag | kOneByteDataHintTag));
+ __ Branch(&ascii_data, eq, t0,
+ Operand(kOneByteStringTag | kOneByteDataHintTag));
+
+ // Allocate a two byte cons string.
+ __ AllocateTwoByteConsString(v0, t2, t0, t1, &call_runtime);
+ __ Branch(&allocated);
+
+ // 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
+ // a3: length of second string
+ // 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_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
+ __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
+ }
+ // 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);
+ __ And(t4, t0, Operand(kStringRepresentationMask));
+
+ STATIC_ASSERT(SeqOneByteString::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, SeqOneByteString::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(SeqOneByteString::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, SeqOneByteString::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
+ // t2: sum of lengths.
+ // 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(SeqOneByteString::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.
+
+ StringHelper::GenerateCopyCharacters(masm, t2, t3, a2, t0, true);
+ // t2: next character of result.
+ StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, true);
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ DropAndRet(2);
+
+ __ bind(&non_ascii_string_add_flat_result);
+ __ 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.
+ StringHelper::GenerateCopyCharacters(masm, t2, t3, a2, t0, false);
+ // t2: next character of result.
+ StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, false);
+
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ DropAndRet(2);
+
+ // Just jump to runtime to add the two strings.
+ __ bind(&call_runtime);
+ if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
+ GenerateRegisterArgsPop(masm);
+ // Build a frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ __ CallRuntime(Runtime::kStringAdd, 2);
+ }
+ __ Ret();
+ } else {
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
+ }
+
+ if (call_builtin.is_linked()) {
+ __ bind(&call_builtin);
+ if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
+ GenerateRegisterArgsPop(masm);
+ // Build a frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ __ InvokeBuiltin(builtin_id, CALL_FUNCTION);
+ }
+ __ Ret();
+ } else {
+ __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
+ }
+ }
+}
+
+
+void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ push(a0);
+ __ push(a1);
+}
+
+
+void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm) {
+ __ pop(a1);
+ __ pop(a0);
+}
+
+
+void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* slow) {
+ // First check if the argument is already a string.
+ Label not_string, done;
+ __ JumpIfSmi(arg, &not_string);
+ __ GetObjectType(arg, scratch1, scratch1);
+ __ Branch(&done, lt, scratch1, Operand(FIRST_NONSTRING_TYPE));
+
+ // Check the number to string cache.
+ Label not_cached;
+ __ bind(&not_string);
+ // Puts the cached result into scratch1.
+ NumberToStringStub::GenerateLookupNumberStringCache(masm,
+ arg,
+ scratch1,
+ scratch2,
+ scratch3,
+ scratch4,
+ &not_cached);
+ __ mov(arg, scratch1);
+ __ sw(arg, MemOperand(sp, stack_offset));
+ __ jmp(&done);
+
+ // Check if the argument is a safe string wrapper.
+ __ bind(&not_cached);
+ __ JumpIfSmi(arg, slow);
+ __ GetObjectType(arg, scratch1, scratch2); // map -> scratch1.
+ __ Branch(slow, ne, scratch2, Operand(JS_VALUE_TYPE));
+ __ lbu(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset));
+ __ li(scratch4, 1 << Map::kStringWrapperSafeForDefaultValueOf);
+ __ And(scratch2, scratch2, scratch4);
+ __ Branch(slow, ne, scratch2, Operand(scratch4));
+ __ lw(arg, FieldMemOperand(arg, JSValue::kValueOffset));
+ __ sw(arg, MemOperand(sp, stack_offset));
+
+ __ bind(&done);
+}
+
+
+void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SMI);
+ Label miss;
+ __ Or(a2, a1, a0);
+ __ JumpIfNotSmi(a2, &miss);
+
+ if (GetCondition() == eq) {
+ // For equality we do not care about the sign of the result.
+ __ Ret(USE_DELAY_SLOT);
+ __ Subu(v0, a0, a1);
+ } else {
+ // Untag before subtracting to avoid handling overflow.
+ __ SmiUntag(a1);
+ __ SmiUntag(a0);
+ __ Ret(USE_DELAY_SLOT);
+ __ Subu(v0, a1, a0);
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateNumbers(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::NUMBER);
+
+ Label generic_stub;
+ Label unordered, maybe_undefined1, maybe_undefined2;
+ Label miss;
+
+ if (left_ == CompareIC::SMI) {
+ __ JumpIfNotSmi(a1, &miss);
+ }
+ if (right_ == CompareIC::SMI) {
+ __ JumpIfNotSmi(a0, &miss);
+ }
+
+ // Inlining the double comparison and falling back to the general compare
+ // stub if NaN is involved.
+ // Load left and right operand.
+ Label done, left, left_smi, right_smi;
+ __ JumpIfSmi(a0, &right_smi);
+ __ CheckMap(a0, a2, Heap::kHeapNumberMapRootIndex, &maybe_undefined1,
+ DONT_DO_SMI_CHECK);
+ __ Subu(a2, a0, Operand(kHeapObjectTag));
+ __ ldc1(f2, MemOperand(a2, HeapNumber::kValueOffset));
+ __ Branch(&left);
+ __ bind(&right_smi);
+ __ SmiUntag(a2, a0); // Can't clobber a0 yet.
+ FPURegister single_scratch = f6;
+ __ mtc1(a2, single_scratch);
+ __ cvt_d_w(f2, single_scratch);
+
+ __ bind(&left);
+ __ JumpIfSmi(a1, &left_smi);
+ __ CheckMap(a1, a2, Heap::kHeapNumberMapRootIndex, &maybe_undefined2,
+ DONT_DO_SMI_CHECK);
+ __ Subu(a2, a1, Operand(kHeapObjectTag));
+ __ ldc1(f0, MemOperand(a2, HeapNumber::kValueOffset));
+ __ Branch(&done);
+ __ bind(&left_smi);
+ __ SmiUntag(a2, a1); // Can't clobber a1 yet.
+ single_scratch = f8;
+ __ mtc1(a2, single_scratch);
+ __ cvt_d_w(f0, single_scratch);
+
+ __ bind(&done);
+
+ // 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 less (unordered case is already handled).
+ __ BranchF(&fpu_lt, NULL, lt, f0, f2);
+
+ // Otherwise it's greater, so just fall thru, and return.
+ ASSERT(is_int16(GREATER) && is_int16(EQUAL) && is_int16(LESS));
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(GREATER));
+
+ __ bind(&fpu_eq);
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(EQUAL));
+
+ __ bind(&fpu_lt);
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(LESS));
+
+ __ bind(&unordered);
+ __ bind(&generic_stub);
+ ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC,
+ CompareIC::GENERIC);
+ __ Jump(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+
+ __ bind(&maybe_undefined1);
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&miss, ne, a0, Operand(at));
+ __ JumpIfSmi(a1, &unordered);
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&maybe_undefined2, ne, a2, Operand(HEAP_NUMBER_TYPE));
+ __ jmp(&unordered);
+ }
+
+ __ bind(&maybe_undefined2);
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&unordered, eq, a1, Operand(at));
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::INTERNALIZED_STRING);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = a1;
+ Register right = a0;
+ Register tmp1 = a2;
+ Register tmp2 = a3;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are internalized strings.
+ __ lw(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ lw(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ lbu(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ lbu(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ Or(tmp1, tmp1, Operand(tmp2));
+ __ And(at, tmp1, Operand(kIsNotStringMask | kIsNotInternalizedMask));
+ __ Branch(&miss, ne, at, Operand(zero_reg));
+
+ // Make sure a0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(a0));
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(v0, right);
+ // Internalized strings are compared by identity.
+ __ Ret(ne, left, Operand(right));
+ ASSERT(is_int16(EQUAL));
+ __ Ret(USE_DELAY_SLOT);
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::UNIQUE_NAME);
+ ASSERT(GetCondition() == eq);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = a1;
+ Register right = a0;
+ Register tmp1 = a2;
+ Register tmp2 = a3;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are unique names. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ lw(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ lw(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ lbu(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ lbu(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+
+ __ JumpIfNotUniqueName(tmp1, &miss);
+ __ JumpIfNotUniqueName(tmp2, &miss);
+
+ // Use a0 as result
+ __ mov(v0, a0);
+
+ // Unique names are compared by identity.
+ Label done;
+ __ Branch(&done, ne, left, Operand(right));
+ // Make sure a0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(a0));
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+ __ bind(&done);
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRING);
+ Label miss;
+
+ bool equality = Token::IsEqualityOp(op_);
+
+ // Registers containing left and right operands respectively.
+ Register left = a1;
+ Register right = a0;
+ Register tmp1 = a2;
+ Register tmp2 = a3;
+ Register tmp3 = t0;
+ Register tmp4 = t1;
+ Register tmp5 = t2;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ lw(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ lw(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ lbu(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ lbu(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ Or(tmp3, tmp1, tmp2);
+ __ And(tmp5, tmp3, Operand(kIsNotStringMask));
+ __ Branch(&miss, ne, tmp5, Operand(zero_reg));
+
+ // Fast check for identical strings.
+ Label left_ne_right;
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Branch(&left_ne_right, ne, left, Operand(right));
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, zero_reg); // In the delay slot.
+ __ bind(&left_ne_right);
+
+ // Handle not identical strings.
+
+ // Check that both strings are internalized strings. If they are, we're done
+ // because we already know they are not identical. We know they are both
+ // strings.
+ if (equality) {
+ ASSERT(GetCondition() == eq);
+ STATIC_ASSERT(kInternalizedTag == 0);
+ __ Or(tmp3, tmp1, Operand(tmp2));
+ __ And(tmp5, tmp3, Operand(kIsNotInternalizedMask));
+ Label is_symbol;
+ __ Branch(&is_symbol, ne, tmp5, Operand(zero_reg));
+ // Make sure a0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(a0));
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0); // In the delay slot.
+ __ bind(&is_symbol);
+ }
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(
+ tmp1, tmp2, tmp3, tmp4, &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ if (equality) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2, tmp3);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(
+ masm, left, right, tmp1, tmp2, tmp3, tmp4);
+ }
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ Push(left, right);
+ if (equality) {
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ } else {
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::OBJECT);
+ Label miss;
+ __ And(a2, a1, Operand(a0));
+ __ JumpIfSmi(a2, &miss);
+
+ __ GetObjectType(a0, a2, a2);
+ __ Branch(&miss, ne, a2, Operand(JS_OBJECT_TYPE));
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&miss, ne, a2, Operand(JS_OBJECT_TYPE));
+
+ ASSERT(GetCondition() == eq);
+ __ Ret(USE_DELAY_SLOT);
+ __ subu(v0, a0, a1);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+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_));
+
+ __ 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_)));
+ __ addiu(sp, sp, -kPointerSize);
+ __ CallExternalReference(miss, 3, USE_DELAY_SLOT);
+ __ sw(t0, MemOperand(sp)); // In the delay slot.
+ // Compute the entry point of the rewritten stub.
+ __ Addu(a2, v0, Operand(Code::kHeaderSize - kHeapObjectTag));
+ // Restore registers.
+ __ Pop(a1, a0, ra);
+ }
+ __ Jump(a2);
+}
+
+
+void DirectCEntryStub::Generate(MacroAssembler* masm) {
+ // No need to pop or drop anything, LeaveExitFrame will restore the old
+ // stack, thus dropping the allocated space for the return value.
+ // The saved ra is after the reserved stack space for the 4 args.
+ __ lw(t9, MemOperand(sp, kCArgsSlotsSize));
+
+ 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.
+ __ lw(t0, MemOperand(t9));
+ __ Assert(ne, kReceivedInvalidReturnAddress, t0,
+ Operand(reinterpret_cast<uint32_t>(kZapValue)));
+ }
+ __ Jump(t9);
+}
+
+
+void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
+ Register target) {
+ __ Move(t9, target);
+ __ AssertStackIsAligned();
+ // Allocate space for arg slots.
+ __ Subu(sp, sp, kCArgsSlotsSize);
+
+ // Block the trampoline pool through the whole function to make sure the
+ // number of generated instructions is constant.
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
+
+ // We need to get the current 'pc' value, which is not available on MIPS.
+ Label find_ra;
+ masm->bal(&find_ra); // ra = pc + 8.
+ masm->nop(); // Branch delay slot nop.
+ masm->bind(&find_ra);
+
+ const int kNumInstructionsToJump = 6;
+ masm->addiu(ra, ra, kNumInstructionsToJump * kPointerSize);
+ // Push return address (accessible to GC through exit frame pc).
+ // This spot for ra was reserved in EnterExitFrame.
+ masm->sw(ra, MemOperand(sp, kCArgsSlotsSize));
+ intptr_t loc =
+ reinterpret_cast<intptr_t>(GetCode(masm->isolate()).location());
+ masm->li(ra, Operand(loc, RelocInfo::CODE_TARGET), CONSTANT_SIZE);
+ // Call the function.
+ masm->Jump(t9);
+ // Make sure the stored 'ra' points to this position.
+ ASSERT_EQ(kNumInstructionsToJump, masm->InstructionsGeneratedSince(&find_ra));
+}
+
+
+void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register receiver,
+ Register properties,
+ Handle<Name> name,
+ Register scratch0) {
+ ASSERT(name->IsUniqueName());
+ // 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
+ // (their names are the hole value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // scratch0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = scratch0;
+ // Capacity is smi 2^n.
+ __ lw(index, FieldMemOperand(properties, kCapacityOffset));
+ __ Subu(index, index, Operand(1));
+ __ And(index, index, Operand(
+ Smi::FromInt(name->Hash() + NameDictionary::GetProbeOffset(i))));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ 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));
+
+ ASSERT(!tmp.is(entity_name));
+ __ LoadRoot(tmp, Heap::kUndefinedValueRootIndex);
+ __ Branch(done, eq, entity_name, Operand(tmp));
+
+ // Load the hole ready for use below:
+ __ LoadRoot(tmp, Heap::kTheHoleValueRootIndex);
+
+ // Stop if found the property.
+ __ Branch(miss, eq, entity_name, Operand(Handle<Name>(name)));
+
+ Label good;
+ __ Branch(&good, eq, entity_name, Operand(tmp));
+
+ // Check if the entry name is not a unique name.
+ __ lw(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset));
+ __ lbu(entity_name,
+ FieldMemOperand(entity_name, Map::kInstanceTypeOffset));
+ __ JumpIfNotUniqueName(entity_name, miss);
+ __ bind(&good);
+
+ // Restore the properties.
+ __ lw(properties,
+ FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ }
+
+ const int spill_mask =
+ (ra.bit() | t2.bit() | t1.bit() | t0.bit() | a3.bit() |
+ a2.bit() | a1.bit() | a0.bit() | v0.bit());
+
+ __ MultiPush(spill_mask);
+ __ lw(a0, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ __ li(a1, Operand(Handle<Name>(name)));
+ NameDictionaryLookupStub stub(NEGATIVE_LOOKUP);
+ __ CallStub(&stub);
+ __ mov(at, v0);
+ __ MultiPop(spill_mask);
+
+ __ Branch(done, eq, at, Operand(zero_reg));
+ __ Branch(miss, ne, at, Operand(zero_reg));
+}
+
+
+// Probe the name dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found. Jump to
+// the |miss| label otherwise.
+// If lookup was successful |scratch2| will be equal to elements + 4 * index.
+void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register scratch1,
+ Register scratch2) {
+ ASSERT(!elements.is(scratch1));
+ ASSERT(!elements.is(scratch2));
+ ASSERT(!name.is(scratch1));
+ ASSERT(!name.is(scratch2));
+
+ __ AssertName(name);
+
+ // Compute the capacity mask.
+ __ lw(scratch1, FieldMemOperand(elements, kCapacityOffset));
+ __ sra(scratch1, scratch1, kSmiTagSize); // convert smi to int
+ __ Subu(scratch1, scratch1, Operand(1));
+
+ // Generate an unrolled loop that performs a few probes before
+ // giving up. Measurements done on Gmail indicate that 2 probes
+ // cover ~93% of loads from dictionaries.
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ lw(scratch2, FieldMemOperand(name, Name::kHashFieldOffset));
+ if (i > 0) {
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(NameDictionary::GetProbeOffset(i) <
+ 1 << (32 - Name::kHashFieldOffset));
+ __ Addu(scratch2, scratch2, Operand(
+ NameDictionary::GetProbeOffset(i) << Name::kHashShift));
+ }
+ __ srl(scratch2, scratch2, Name::kHashShift);
+ __ And(scratch2, scratch1, scratch2);
+
+ // Scale the index by multiplying by the element size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ // scratch2 = scratch2 * 3.
+
+ __ sll(at, scratch2, 1);
+ __ Addu(scratch2, scratch2, at);
+
+ // Check if the key is identical to the name.
+ __ sll(at, scratch2, 2);
+ __ Addu(scratch2, elements, at);
+ __ lw(at, FieldMemOperand(scratch2, kElementsStartOffset));
+ __ Branch(done, eq, name, Operand(at));
+ }
+
+ const int spill_mask =
+ (ra.bit() | t2.bit() | t1.bit() | t0.bit() |
+ a3.bit() | a2.bit() | a1.bit() | a0.bit() | v0.bit()) &
+ ~(scratch1.bit() | scratch2.bit());
+
+ __ MultiPush(spill_mask);
+ if (name.is(a0)) {
+ ASSERT(!elements.is(a1));
+ __ Move(a1, name);
+ __ Move(a0, elements);
+ } else {
+ __ Move(a0, elements);
+ __ Move(a1, name);
+ }
+ NameDictionaryLookupStub stub(POSITIVE_LOOKUP);
+ __ CallStub(&stub);
+ __ mov(scratch2, a2);
+ __ mov(at, v0);
+ __ MultiPop(spill_mask);
+
+ __ Branch(done, ne, at, Operand(zero_reg));
+ __ Branch(miss, eq, at, Operand(zero_reg));
+}
+
+
+void NameDictionaryLookupStub::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: NameDictionary to probe
+ // a1: key
+ // dictionary: NameDictionary to probe.
+ // index: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Register result = v0;
+ Register dictionary = a0;
+ Register key = a1;
+ Register index = a2;
+ Register mask = a3;
+ Register hash = t0;
+ Register undefined = t1;
+ Register entry_key = t2;
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ __ lw(mask, FieldMemOperand(dictionary, kCapacityOffset));
+ __ sra(mask, mask, kSmiTagSize);
+ __ Subu(mask, mask, Operand(1));
+
+ __ lw(hash, FieldMemOperand(key, Name::kHashFieldOffset));
+
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ // Capacity is smi 2^n.
+ if (i > 0) {
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(NameDictionary::GetProbeOffset(i) <
+ 1 << (32 - Name::kHashFieldOffset));
+ __ Addu(index, hash, Operand(
+ NameDictionary::GetProbeOffset(i) << Name::kHashShift));
+ } else {
+ __ mov(index, hash);
+ }
+ __ srl(index, index, Name::kHashShift);
+ __ And(index, mask, index);
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ // index *= 3.
+ __ mov(at, index);
+ __ sll(index, index, 1);
+ __ Addu(index, index, at);
+
+
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ sll(index, index, 2);
+ __ Addu(index, index, dictionary);
+ __ lw(entry_key, FieldMemOperand(index, kElementsStartOffset));
+
+ // Having undefined at this place means the name is not contained.
+ __ Branch(&not_in_dictionary, eq, entry_key, Operand(undefined));
+
+ // Stop if found the property.
+ __ Branch(&in_dictionary, eq, entry_key, Operand(key));
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // Check if the entry name is not a unique name.
+ __ lw(entry_key, FieldMemOperand(entry_key, HeapObject::kMapOffset));
+ __ lbu(entry_key,
+ FieldMemOperand(entry_key, Map::kInstanceTypeOffset));
+ __ JumpIfNotUniqueName(entry_key, &maybe_in_dictionary);
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(result, zero_reg);
+ }
+
+ __ bind(&in_dictionary);
+ __ Ret(USE_DELAY_SLOT);
+ __ li(result, 1);
+
+ __ bind(&not_in_dictionary);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(result, zero_reg);
+}
+
+
+struct AheadOfTimeWriteBarrierStubList {
+ Register object, value, address;
+ RememberedSetAction action;
+};
+
+
+#define REG(Name) { kRegister_ ## Name ## _Code }
+
+static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
+ // Used in RegExpExecStub.
+ { REG(s2), REG(s0), REG(t3), EMIT_REMEMBERED_SET },
+ // Used in CompileArrayPushCall.
+ // Also used in StoreIC::GenerateNormal via GenerateDictionaryStore.
+ // Also used in KeyedStoreIC::GenerateGeneric.
+ { REG(a3), REG(t0), REG(t1), EMIT_REMEMBERED_SET },
+ // Used in CompileStoreGlobal.
+ { REG(t0), REG(a1), REG(a2), OMIT_REMEMBERED_SET },
+ // Used in StoreStubCompiler::CompileStoreField via GenerateStoreField.
+ { REG(a1), REG(a2), REG(a3), EMIT_REMEMBERED_SET },
+ { REG(a3), REG(a2), REG(a1), EMIT_REMEMBERED_SET },
+ // Used in KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
+ { REG(a2), REG(a1), REG(a3), EMIT_REMEMBERED_SET },
+ { REG(a3), REG(a1), REG(a2), EMIT_REMEMBERED_SET },
+ // KeyedStoreStubCompiler::GenerateStoreFastElement.
+ { REG(a3), REG(a2), REG(t0), EMIT_REMEMBERED_SET },
+ { REG(a2), REG(a3), REG(t0), EMIT_REMEMBERED_SET },
+ // ElementsTransitionGenerator::GenerateMapChangeElementTransition
+ // and ElementsTransitionGenerator::GenerateSmiToDouble
+ // and ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(a2), REG(a3), REG(t5), EMIT_REMEMBERED_SET },
+ { REG(a2), REG(a3), REG(t5), OMIT_REMEMBERED_SET },
+ // ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(t2), REG(a2), REG(a0), EMIT_REMEMBERED_SET },
+ { REG(a2), REG(t2), REG(t5), EMIT_REMEMBERED_SET },
+ // StoreArrayLiteralElementStub::Generate
+ { REG(t1), REG(a0), REG(t2), EMIT_REMEMBERED_SET },
+ // FastNewClosureStub::Generate
+ { REG(a2), REG(t0), REG(a1), EMIT_REMEMBERED_SET },
+ // StringAddStub::Generate
+ { REG(t3), REG(a1), REG(t0), EMIT_REMEMBERED_SET },
+ { REG(t3), REG(a0), REG(t0), EMIT_REMEMBERED_SET },
+ // Null termination.
+ { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
+};
+
+#undef REG
+
+
+bool RecordWriteStub::IsPregenerated() {
+ for (const 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(
+ Isolate* isolate) {
+ StoreBufferOverflowStub stub1(kDontSaveFPRegs);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ // Hydrogen code stubs need stub2 at snapshot time.
+ StoreBufferOverflowStub stub2(kSaveFPRegs);
+ stub2.GetCode(isolate)->set_is_pregenerated(true);
+}
+
+
+void RecordWriteStub::GenerateFixedRegStubsAheadOfTime(Isolate* isolate) {
+ for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
+ !entry->object.is(no_reg);
+ entry++) {
+ RecordWriteStub stub(entry->object,
+ entry->value,
+ entry->address,
+ entry->action,
+ kDontSaveFPRegs);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+bool CodeStub::CanUseFPRegisters() {
+ return true; // FPU is a base requirement for V8.
+}
+
+
+// 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());
+ __ Move(a1, address);
+ __ li(a2, Operand(ExternalReference::isolate_address(masm->isolate())));
+
+ 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;
+
+ __ And(regs_.scratch0(), regs_.object(), Operand(~Page::kPageAlignmentMask));
+ __ lw(regs_.scratch1(),
+ MemOperand(regs_.scratch0(),
+ MemoryChunk::kWriteBarrierCounterOffset));
+ __ Subu(regs_.scratch1(), regs_.scratch1(), Operand(1));
+ __ sw(regs_.scratch1(),
+ MemOperand(regs_.scratch0(),
+ MemoryChunk::kWriteBarrierCounterOffset));
+ __ Branch(&need_incremental, lt, regs_.scratch1(), Operand(zero_reg));
+
+ // 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
+ // -- a3 : element index as smi
+ // -- sp[0] : array literal index in function as smi
+ // -- sp[4] : array literal
+ // clobbers a1, a2, t0
+ // -----------------------------------
+
+ Label element_done;
+ Label double_elements;
+ Label smi_element;
+ Label slow_elements;
+ Label fast_elements;
+
+ // Get array literal index, array literal and its map.
+ __ lw(t0, MemOperand(sp, 0 * kPointerSize));
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+ __ lw(a2, FieldMemOperand(a1, JSObject::kMapOffset));
+
+ __ CheckFastElements(a2, t1, &double_elements);
+ // Check for FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS elements
+ __ JumpIfSmi(a0, &smi_element);
+ __ CheckFastSmiElements(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_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,
+ // Overwrites all regs after this.
+ t1, t2, t3, t5, a2,
+ &slow_elements);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0);
+}
+
+
+void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
+ CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs);
+ __ Call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+ int parameter_count_offset =
+ StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
+ __ lw(a1, MemOperand(fp, parameter_count_offset));
+ if (function_mode_ == JS_FUNCTION_STUB_MODE) {
+ __ Addu(a1, a1, Operand(1));
+ }
+ masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE);
+ __ sll(a1, a1, kPointerSizeLog2);
+ __ Ret(USE_DELAY_SLOT);
+ __ Addu(sp, sp, a1);
+}
+
+
+void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
+ if (masm->isolate()->function_entry_hook() != NULL) {
+ AllowStubCallsScope allow_stub_calls(masm, true);
+ ProfileEntryHookStub stub;
+ __ push(ra);
+ __ CallStub(&stub);
+ __ pop(ra);
+ }
+}
+
+
+void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
+ // The entry hook is a "push ra" instruction, followed by a call.
+ // Note: on MIPS "push" is 2 instruction
+ const int32_t kReturnAddressDistanceFromFunctionStart =
+ Assembler::kCallTargetAddressOffset + (2 * Assembler::kInstrSize);
+
+ // This should contain all kJSCallerSaved registers.
+ const RegList kSavedRegs =
+ kJSCallerSaved | // Caller saved registers.
+ s5.bit(); // Saved stack pointer.
+
+ // We also save ra, so the count here is one higher than the mask indicates.
+ const int32_t kNumSavedRegs = kNumJSCallerSaved + 2;
+
+ // Save all caller-save registers as this may be called from anywhere.
+ __ MultiPush(kSavedRegs | ra.bit());
+
+ // Compute the function's address for the first argument.
+ __ Subu(a0, ra, Operand(kReturnAddressDistanceFromFunctionStart));
+
+ // The caller's return address is above the saved temporaries.
+ // Grab that for the second argument to the hook.
+ __ Addu(a1, sp, Operand(kNumSavedRegs * kPointerSize));
+
+ // Align the stack if necessary.
+ int frame_alignment = masm->ActivationFrameAlignment();
+ if (frame_alignment > kPointerSize) {
+ __ mov(s5, sp);
+ ASSERT(IsPowerOf2(frame_alignment));
+ __ And(sp, sp, Operand(-frame_alignment));
+ }
+
+#if defined(V8_HOST_ARCH_MIPS)
+ int32_t entry_hook =
+ reinterpret_cast<int32_t>(masm->isolate()->function_entry_hook());
+ __ li(at, Operand(entry_hook));
+#else
+ // Under the simulator we need to indirect the entry hook through a
+ // trampoline function at a known address.
+ ApiFunction dispatcher(FUNCTION_ADDR(EntryHookTrampoline));
+ __ li(at, Operand(ExternalReference(&dispatcher,
+ ExternalReference::BUILTIN_CALL,
+ masm->isolate())));
+#endif
+ __ Call(at);
+
+ // Restore the stack pointer if needed.
+ if (frame_alignment > kPointerSize) {
+ __ mov(sp, s5);
+ }
+
+ // Also pop ra to get Ret(0).
+ __ MultiPop(kSavedRegs | ra.bit());
+ __ Ret();
+}
+
+
+template<class T>
+static void CreateArrayDispatch(MacroAssembler* masm) {
+ int last_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= last_index; ++i) {
+ Label next;
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ __ Branch(&next, ne, a3, Operand(kind));
+ T stub(kind);
+ __ TailCallStub(&stub);
+ __ bind(&next);
+ }
+
+ // If we reached this point there is a problem.
+ __ Abort(kUnexpectedElementsKindInArrayConstructor);
+}
+
+
+static void CreateArrayDispatchOneArgument(MacroAssembler* masm) {
+ // a2 - type info cell
+ // a3 - kind
+ // a0 - number of arguments
+ // a1 - constructor?
+ // sp[0] - last argument
+ ASSERT(FAST_SMI_ELEMENTS == 0);
+ ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ ASSERT(FAST_ELEMENTS == 2);
+ ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ ASSERT(FAST_DOUBLE_ELEMENTS == 4);
+ ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5);
+
+ // is the low bit set? If so, we are holey and that is good.
+ Label normal_sequence;
+ __ And(at, a3, Operand(1));
+ __ Branch(&normal_sequence, ne, at, Operand(zero_reg));
+
+ // look at the first argument
+ __ lw(t1, MemOperand(sp, 0));
+ __ Branch(&normal_sequence, eq, t1, Operand(zero_reg));
+
+ // We are going to create a holey array, but our kind is non-holey.
+ // Fix kind and retry (only if we have an allocation site in the cell).
+ __ Addu(a3, a3, Operand(1));
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&normal_sequence, eq, a2, Operand(at));
+ __ lw(t1, FieldMemOperand(a2, Cell::kValueOffset));
+ __ lw(t1, FieldMemOperand(t1, 0));
+ __ LoadRoot(at, Heap::kAllocationSiteMapRootIndex);
+ __ Branch(&normal_sequence, ne, t1, Operand(at));
+
+ // Save the resulting elements kind in type info
+ __ SmiTag(a3);
+ __ lw(t1, FieldMemOperand(a2, Cell::kValueOffset));
+ __ sw(a3, FieldMemOperand(t1, AllocationSite::kTransitionInfoOffset));
+ __ SmiUntag(a3);
+
+ __ bind(&normal_sequence);
+ int last_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= last_index; ++i) {
+ Label next;
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ __ Branch(&next, ne, a3, Operand(kind));
+ ArraySingleArgumentConstructorStub stub(kind);
+ __ TailCallStub(&stub);
+ __ bind(&next);
+ }
+
+ // If we reached this point there is a problem.
+ __ Abort(kUnexpectedElementsKindInArrayConstructor);
+}
+
+
+template<class T>
+static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) {
+ int to_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= to_index; ++i) {
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ T stub(kind);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ if (AllocationSite::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) {
+ T stub1(kind, CONTEXT_CHECK_REQUIRED, DISABLE_ALLOCATION_SITES);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ }
+ }
+}
+
+
+void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) {
+ ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>(
+ isolate);
+ ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
+ isolate);
+ ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>(
+ isolate);
+}
+
+
+void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(
+ Isolate* isolate) {
+ ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS };
+ for (int i = 0; i < 2; i++) {
+ // For internal arrays we only need a few things.
+ InternalArrayNoArgumentConstructorStub stubh1(kinds[i]);
+ stubh1.GetCode(isolate)->set_is_pregenerated(true);
+ InternalArraySingleArgumentConstructorStub stubh2(kinds[i]);
+ stubh2.GetCode(isolate)->set_is_pregenerated(true);
+ InternalArrayNArgumentsConstructorStub stubh3(kinds[i]);
+ stubh3.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+void ArrayConstructorStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a0 : argc (only if argument_count_ == ANY)
+ // -- a1 : constructor
+ // -- a2 : type info cell
+ // -- sp[0] : return address
+ // -- sp[4] : last argument
+ // -----------------------------------
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the global and natives
+ // builtin Array functions which always have maps.
+
+ // Initial map for the builtin Array function should be a map.
+ __ lw(a3, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ __ And(at, a3, Operand(kSmiTagMask));
+ __ Assert(ne, kUnexpectedInitialMapForArrayFunction,
+ at, Operand(zero_reg));
+ __ GetObjectType(a3, a3, t0);
+ __ Assert(eq, kUnexpectedInitialMapForArrayFunction,
+ t0, Operand(MAP_TYPE));
+
+ // We should either have undefined in a2 or a valid cell.
+ Label okay_here;
+ Handle<Map> cell_map = masm->isolate()->factory()->cell_map();
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&okay_here, eq, a2, Operand(at));
+ __ lw(a3, FieldMemOperand(a2, 0));
+ __ Assert(eq, kExpectedPropertyCellInRegisterA2,
+ a3, Operand(cell_map));
+ __ bind(&okay_here);
+ }
+
+ Label no_info, switch_ready;
+ // Get the elements kind and case on that.
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&no_info, eq, a2, Operand(at));
+ __ lw(a3, FieldMemOperand(a2, Cell::kValueOffset));
+
+ // The type cell may have undefined in its value.
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&no_info, eq, a3, Operand(at));
+
+ // The type cell has either an AllocationSite or a JSFunction.
+ __ lw(t0, FieldMemOperand(a3, 0));
+ __ LoadRoot(at, Heap::kAllocationSiteMapRootIndex);
+ __ Branch(&no_info, ne, t0, Operand(at));
+
+ __ lw(a3, FieldMemOperand(a3, AllocationSite::kTransitionInfoOffset));
+ __ SmiUntag(a3);
+ __ jmp(&switch_ready);
+ __ bind(&no_info);
+ __ li(a3, Operand(GetInitialFastElementsKind()));
+ __ bind(&switch_ready);
+
+ if (argument_count_ == ANY) {
+ Label not_zero_case, not_one_case;
+ __ And(at, a0, a0);
+ __ Branch(&not_zero_case, ne, at, Operand(zero_reg));
+ CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
+
+ __ bind(&not_zero_case);
+ __ Branch(&not_one_case, gt, a0, Operand(1));
+ CreateArrayDispatchOneArgument(masm);
+
+ __ bind(&not_one_case);
+ CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
+ } else if (argument_count_ == NONE) {
+ CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
+ } else if (argument_count_ == ONE) {
+ CreateArrayDispatchOneArgument(masm);
+ } else if (argument_count_ == MORE_THAN_ONE) {
+ CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void InternalArrayConstructorStub::GenerateCase(
+ MacroAssembler* masm, ElementsKind kind) {
+ Label not_zero_case, not_one_case;
+ Label normal_sequence;
+
+ __ Branch(&not_zero_case, ne, a0, Operand(zero_reg));
+ InternalArrayNoArgumentConstructorStub stub0(kind);
+ __ TailCallStub(&stub0);
+
+ __ bind(&not_zero_case);
+ __ Branch(&not_one_case, gt, a0, Operand(1));
+
+ if (IsFastPackedElementsKind(kind)) {
+ // We might need to create a holey array
+ // look at the first argument.
+ __ lw(at, MemOperand(sp, 0));
+ __ Branch(&normal_sequence, eq, at, Operand(zero_reg));
+
+ InternalArraySingleArgumentConstructorStub
+ stub1_holey(GetHoleyElementsKind(kind));
+ __ TailCallStub(&stub1_holey);
+ }
+
+ __ bind(&normal_sequence);
+ InternalArraySingleArgumentConstructorStub stub1(kind);
+ __ TailCallStub(&stub1);
+
+ __ bind(&not_one_case);
+ InternalArrayNArgumentsConstructorStub stubN(kind);
+ __ TailCallStub(&stubN);
+}
+
+
+void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a0 : argc
+ // -- a1 : constructor
+ // -- sp[0] : return address
+ // -- sp[4] : last argument
+ // -----------------------------------
+
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the global and natives
+ // builtin Array functions which always have maps.
+
+ // Initial map for the builtin Array function should be a map.
+ __ lw(a3, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ __ And(at, a3, Operand(kSmiTagMask));
+ __ Assert(ne, kUnexpectedInitialMapForArrayFunction,
+ at, Operand(zero_reg));
+ __ GetObjectType(a3, a3, t0);
+ __ Assert(eq, kUnexpectedInitialMapForArrayFunction,
+ t0, Operand(MAP_TYPE));
+ }
+
+ // Figure out the right elements kind.
+ __ lw(a3, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Load the map's "bit field 2" into a3. We only need the first byte,
+ // but the following bit field extraction takes care of that anyway.
+ __ lbu(a3, FieldMemOperand(a3, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ Ext(a3, a3, Map::kElementsKindShift, Map::kElementsKindBitCount);
+
+ if (FLAG_debug_code) {
+ Label done;
+ __ Branch(&done, eq, a3, Operand(FAST_ELEMENTS));
+ __ Assert(
+ eq, kInvalidElementsKindForInternalArrayOrInternalPackedArray,
+ a3, Operand(FAST_HOLEY_ELEMENTS));
+ __ bind(&done);
+ }
+
+ Label fast_elements_case;
+ __ Branch(&fast_elements_case, eq, a3, Operand(FAST_ELEMENTS));
+ GenerateCase(masm, FAST_HOLEY_ELEMENTS);
+
+ __ bind(&fast_elements_case);
+ GenerateCase(masm, FAST_ELEMENTS);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/code-stubs-mips.h b/chromium/v8/src/mips/code-stubs-mips.h
new file mode 100644
index 00000000000..1ae1d3454fb
--- /dev/null
+++ b/chromium/v8/src/mips/code-stubs-mips.h
@@ -0,0 +1,696 @@
+// 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_CODE_STUBS_ARM_H_
+#define V8_MIPS_CODE_STUBS_ARM_H_
+
+#include "ic-inl.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code);
+
+
+// Compute a transcendental math function natively, or call the
+// TranscendentalCache runtime function.
+class TranscendentalCacheStub: public PlatformCodeStub {
+ public:
+ enum ArgumentType {
+ TAGGED = 0 << TranscendentalCache::kTranscendentalTypeBits,
+ UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
+ };
+
+ TranscendentalCacheStub(TranscendentalCache::Type type,
+ ArgumentType argument_type)
+ : type_(type), argument_type_(argument_type) { }
+ void Generate(MacroAssembler* masm);
+ private:
+ TranscendentalCache::Type type_;
+ ArgumentType argument_type_;
+ void GenerateCallCFunction(MacroAssembler* masm, Register scratch);
+
+ Major MajorKey() { return TranscendentalCache; }
+ int MinorKey() { return type_ | argument_type_; }
+ Runtime::FunctionId RuntimeFunction();
+};
+
+
+class StoreBufferOverflowStub: public PlatformCodeStub {
+ public:
+ explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
+ : save_doubles_(save_fp) {}
+
+ void Generate(MacroAssembler* masm);
+
+ virtual bool IsPregenerated() { return true; }
+ static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ private:
+ SaveFPRegsMode save_doubles_;
+
+ Major MajorKey() { return StoreBufferOverflow; }
+ int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
+};
+
+
+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 string 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 string 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 GenerateTwoCharacterStringTableProbe(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);
+};
+
+
+class StringAddStub: public PlatformCodeStub {
+ public:
+ explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
+
+ private:
+ Major MajorKey() { return StringAdd; }
+ int MinorKey() { return flags_; }
+
+ void Generate(MacroAssembler* masm);
+
+ void GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* slow);
+
+ void GenerateRegisterArgsPush(MacroAssembler* masm);
+ void GenerateRegisterArgsPop(MacroAssembler* masm);
+
+ const StringAddFlags flags_;
+};
+
+
+class SubStringStub: public PlatformCodeStub {
+ public:
+ SubStringStub() {}
+
+ private:
+ Major MajorKey() { return SubString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class StringCompareStub: public PlatformCodeStub {
+ public:
+ StringCompareStub() { }
+
+ // Compare two flat ASCII strings and returns result in v0.
+ static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4);
+
+ // Compares two flat ASCII strings for equality and returns result
+ // in v0.
+ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3);
+
+ private:
+ virtual Major MajorKey() { return StringCompare; }
+ virtual int MinorKey() { return 0; }
+ virtual void Generate(MacroAssembler* masm);
+
+ static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* chars_not_equal);
+};
+
+
+// This stub can convert a signed int32 to a heap number (double). It does
+// not work for int32s that are in Smi range! No GC occurs during this stub
+// so you don't have to set up the frame.
+class WriteInt32ToHeapNumberStub : public PlatformCodeStub {
+ public:
+ WriteInt32ToHeapNumberStub(Register the_int,
+ Register the_heap_number,
+ Register scratch,
+ Register scratch2)
+ : the_int_(the_int),
+ the_heap_number_(the_heap_number),
+ scratch_(scratch),
+ 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(Isolate* isolate);
+
+ private:
+ Register the_int_;
+ Register the_heap_number_;
+ Register scratch_;
+ Register sign_;
+
+ // Minor key encoding in 16 bits.
+ 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())
+ | SignRegisterBits::encode(sign_.code());
+ }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class NumberToStringStub: public PlatformCodeStub {
+ public:
+ NumberToStringStub() { }
+
+ // Generate code to do a lookup in the number string cache. If the number in
+ // the register object is found in the cache the generated code falls through
+ // with the result in the result register. The object and the result register
+ // can be the same. If the number is not found in the cache the code jumps to
+ // the label not_found with only the content of register object unchanged.
+ static void GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_found);
+
+ private:
+ Major MajorKey() { return NumberToString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class RecordWriteStub: public PlatformCodeStub {
+ 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(Isolate* isolate);
+ 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) {
+ masm->MultiPushFPU(kCallerSavedFPU);
+ }
+ }
+
+ inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
+ SaveFPRegsMode mode) {
+ if (mode == kSaveFPRegs) {
+ 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::NumAllocatableRegisters(); 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.
+class RegExpCEntryStub: public PlatformCodeStub {
+ public:
+ RegExpCEntryStub() {}
+ virtual ~RegExpCEntryStub() {}
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Major MajorKey() { return RegExpCEntry; }
+ int MinorKey() { return 0; }
+
+ bool NeedsImmovableCode() { return true; }
+};
+
+// Trampoline stub to call into native code. To call safely into native code
+// in the presence of compacting GC (which can move code objects) we need to
+// keep the code which called into native pinned in the memory. Currently the
+// simplest approach is to generate such stub early enough so it can never be
+// moved by GC
+class DirectCEntryStub: public PlatformCodeStub {
+ public:
+ DirectCEntryStub() {}
+ void Generate(MacroAssembler* masm);
+ void GenerateCall(MacroAssembler* masm, Register target);
+
+ private:
+ Major MajorKey() { return DirectCEntry; }
+ int MinorKey() { return 0; }
+
+ bool NeedsImmovableCode() { return true; }
+};
+
+class FloatingPointHelper : public AllStatic {
+ public:
+ enum Destination {
+ kFPURegisters,
+ kCoreRegisters
+ };
+
+
+ // Loads smis from a0 and a1 (right and left in binary operations) into
+ // floating point registers. Depending on the destination the values ends up
+ // either f14 and f12 or in a2/a3 and a0/a1 respectively. If the destination
+ // is floating point registers FPU must be supported. If core registers are
+ // requested when FPU is supported f12 and f14 will be scratched.
+ static void LoadSmis(MacroAssembler* masm,
+ Destination destination,
+ Register scratch1,
+ Register scratch2);
+
+ // Convert the smi or heap number in object to an int32 using the rules
+ // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
+ // and brought into the range -2^31 .. +2^31 - 1.
+ static void ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ FPURegister double_scratch,
+ Label* not_int32);
+
+ // Converts the integer (untagged smi) in |int_scratch| to a double, storing
+ // the result either in |double_dst| or |dst2:dst1|, depending on
+ // |destination|.
+ // Warning: The value in |int_scratch| will be changed in the process!
+ static void ConvertIntToDouble(MacroAssembler* masm,
+ Register int_scratch,
+ Destination destination,
+ FPURegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register scratch2,
+ FPURegister single_scratch);
+
+ // Load the number from object into double_dst in the double format.
+ // Control will jump to not_int32 if the value cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be loaded.
+ static void LoadNumberAsInt32Double(MacroAssembler* masm,
+ Register object,
+ Destination destination,
+ FPURegister double_dst,
+ FPURegister double_scratch,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ FPURegister single_scratch,
+ Label* not_int32);
+
+ // Loads the number from object into dst as a 32-bit integer.
+ // Control will jump to not_int32 if the object cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be converted.
+ // scratch3 is not used when FPU is supported.
+ static void LoadNumberAsInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ FPURegister double_scratch0,
+ FPURegister double_scratch1,
+ Label* not_int32);
+
+ // Generates code to call a C function to do a double operation using core
+ // registers. (Used when FPU is not supported.)
+ // This code never falls through, but returns with a heap number containing
+ // the result in v0.
+ // Register heapnumber_result must be a heap number in which the
+ // result of the operation will be stored.
+ // Requires the following layout on entry:
+ // a0: Left value (least significant part of mantissa).
+ // a1: Left value (sign, exponent, top of mantissa).
+ // a2: Right value (least significant part of mantissa).
+ // a3: Right value (sign, exponent, top of mantissa).
+ static void CallCCodeForDoubleOperation(MacroAssembler* masm,
+ Token::Value op,
+ Register heap_number_result,
+ Register scratch);
+
+ // Loads the objects from |object| into floating point registers.
+ // Depending on |destination| the value ends up either in |dst| or
+ // in |dst1|/|dst2|. If |destination| is kFPURegisters, then FPU
+ // must be supported. If kCoreRegisters are requested and FPU is
+ // supported, |dst| will be scratched. If |object| is neither smi nor
+ // heap number, |not_number| is jumped to with |object| still intact.
+ static void LoadNumber(MacroAssembler* masm,
+ FloatingPointHelper::Destination destination,
+ Register object,
+ FPURegister dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* not_number);
+};
+
+
+class NameDictionaryLookupStub: public PlatformCodeStub {
+ public:
+ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
+
+ explicit NameDictionaryLookupStub(LookupMode mode) : mode_(mode) { }
+
+ void Generate(MacroAssembler* masm);
+
+ static void GenerateNegativeLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register receiver,
+ Register properties,
+ Handle<Name> name,
+ Register scratch0);
+
+ static void GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1);
+
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ private:
+ static const int kInlinedProbes = 4;
+ static const int kTotalProbes = 20;
+
+ static const int kCapacityOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kCapacityIndex * kPointerSize;
+
+ static const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+
+ Major MajorKey() { return NameDictionaryLookup; }
+
+ int MinorKey() {
+ return LookupModeBits::encode(mode_);
+ }
+
+ class LookupModeBits: public BitField<LookupMode, 0, 1> {};
+
+ LookupMode mode_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_MIPS_CODE_STUBS_ARM_H_
diff --git a/chromium/v8/src/mips/codegen-mips.cc b/chromium/v8/src/mips/codegen-mips.cc
new file mode 100644
index 00000000000..5c847fc8f62
--- /dev/null
+++ b/chromium/v8/src/mips/codegen-mips.cc
@@ -0,0 +1,668 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "codegen.h"
+#include "macro-assembler.h"
+#include "simulator-mips.h"
+
+namespace v8 {
+namespace internal {
+
+
+UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type) {
+ switch (type) {
+ case TranscendentalCache::SIN: return &sin;
+ case TranscendentalCache::COS: return &cos;
+ case TranscendentalCache::TAN: return &tan;
+ case TranscendentalCache::LOG: return &log;
+ default: UNIMPLEMENTED();
+ }
+ return NULL;
+}
+
+
+#define __ masm.
+
+
+#if defined(USE_SIMULATOR)
+byte* fast_exp_mips_machine_code = NULL;
+double fast_exp_simulator(double x) {
+ return Simulator::current(Isolate::Current())->CallFP(
+ fast_exp_mips_machine_code, x, 0);
+}
+#endif
+
+
+UnaryMathFunction CreateExpFunction() {
+ if (!FLAG_fast_math) return &exp;
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true));
+ if (buffer == NULL) return &exp;
+ ExternalReference::InitializeMathExpData();
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+
+ {
+ DoubleRegister input = f12;
+ DoubleRegister result = f0;
+ DoubleRegister double_scratch1 = f4;
+ DoubleRegister double_scratch2 = f6;
+ Register temp1 = t0;
+ Register temp2 = t1;
+ Register temp3 = t2;
+
+ if (!IsMipsSoftFloatABI) {
+ // Input value is in f12 anyway, nothing to do.
+ } else {
+ __ Move(input, a0, a1);
+ }
+ __ Push(temp3, temp2, temp1);
+ MathExpGenerator::EmitMathExp(
+ &masm, input, result, double_scratch1, double_scratch2,
+ temp1, temp2, temp3);
+ __ Pop(temp3, temp2, temp1);
+ if (!IsMipsSoftFloatABI) {
+ // Result is already in f0, nothing to do.
+ } else {
+ __ Move(v0, v1, result);
+ }
+ __ Ret();
+ }
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+
+#if !defined(USE_SIMULATOR)
+ return FUNCTION_CAST<UnaryMathFunction>(buffer);
+#else
+ fast_exp_mips_machine_code = buffer;
+ return &fast_exp_simulator;
+#endif
+}
+
+
+#undef __
+
+
+UnaryMathFunction CreateSqrtFunction() {
+ return &sqrt;
+}
+
+
+// -------------------------------------------------------------------------
+// Platform-specific RuntimeCallHelper functions.
+
+void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ masm->EnterFrame(StackFrame::INTERNAL);
+ ASSERT(!masm->has_frame());
+ masm->set_has_frame(true);
+}
+
+
+void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ masm->LeaveFrame(StackFrame::INTERNAL);
+ ASSERT(masm->has_frame());
+ masm->set_has_frame(false);
+}
+
+
+// -------------------------------------------------------------------------
+// Code generators
+
+#define __ ACCESS_MASM(masm)
+
+void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
+ MacroAssembler* masm, AllocationSiteMode mode,
+ Label* allocation_memento_found) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -- a3 : target map, scratch for subsequent call
+ // -- t0 : scratch (elements)
+ // -----------------------------------
+ if (mode == TRACK_ALLOCATION_SITE) {
+ ASSERT(allocation_memento_found != NULL);
+ masm->TestJSArrayForAllocationMemento(a2, t0, eq,
+ allocation_memento_found);
+ }
+
+ // 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::GenerateSmiToDouble(
+ MacroAssembler* masm, AllocationSiteMode mode, 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, only_change_map, done;
+
+ Register scratch = t6;
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ masm->TestJSArrayForAllocationMemento(a2, t0, eq, fail);
+ }
+
+ // Check for empty arrays, which only require a map transition and no changes
+ // to the backing store.
+ __ lw(t0, FieldMemOperand(a2, JSObject::kElementsOffset));
+ __ LoadRoot(at, Heap::kEmptyFixedArrayRootIndex);
+ __ Branch(&only_change_map, eq, at, Operand(t0));
+
+ __ push(ra);
+ __ 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);
+ __ Allocate(scratch, t2, t3, t5, &gc_required, DOUBLE_ALIGNMENT);
+ // 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,
+ OMIT_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
+
+ __ Branch(&entry);
+
+ __ bind(&only_change_map);
+ __ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset));
+ __ RecordWriteField(a2,
+ HeapObject::kMapOffset,
+ a3,
+ t5,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ __ Branch(&done);
+
+ // 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.
+ __ mtc1(t5, f0);
+ __ cvt_d_w(f0, f0);
+ __ sdc1(f0, MemOperand(t3));
+ __ 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, kObjectFoundInSmiOnlyArray, 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));
+
+ __ pop(ra);
+ __ bind(&done);
+}
+
+
+void ElementsTransitionGenerator::GenerateDoubleToObject(
+ MacroAssembler* masm, AllocationSiteMode mode, 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, only_change_map;
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ masm->TestJSArrayForAllocationMemento(a2, t0, eq, fail);
+ }
+
+ // Check for empty arrays, which only require a map transition and no changes
+ // to the backing store.
+ __ lw(t0, FieldMemOperand(a2, JSObject::kElementsOffset));
+ __ LoadRoot(at, Heap::kEmptyFixedArrayRootIndex);
+ __ Branch(&only_change_map, eq, at, Operand(t0));
+
+ __ MultiPush(a0.bit() | a1.bit() | a2.bit() | a3.bit() | ra.bit());
+
+ __ 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);
+ __ Allocate(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());
+ // 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);
+
+ __ bind(&only_change_map);
+ // Update receiver's map.
+ __ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset));
+ __ RecordWriteField(a2,
+ HeapObject::kMapOffset,
+ a3,
+ t5,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+}
+
+
+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::kempty_stringRootIndex);
+ __ 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 == SeqOneByteString::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, kExternalStringExpectedButNotFound,
+ 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);
+}
+
+
+static MemOperand ExpConstant(int index, Register base) {
+ return MemOperand(base, index * kDoubleSize);
+}
+
+
+void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
+ DoubleRegister input,
+ DoubleRegister result,
+ DoubleRegister double_scratch1,
+ DoubleRegister double_scratch2,
+ Register temp1,
+ Register temp2,
+ Register temp3) {
+ ASSERT(!input.is(result));
+ ASSERT(!input.is(double_scratch1));
+ ASSERT(!input.is(double_scratch2));
+ ASSERT(!result.is(double_scratch1));
+ ASSERT(!result.is(double_scratch2));
+ ASSERT(!double_scratch1.is(double_scratch2));
+ ASSERT(!temp1.is(temp2));
+ ASSERT(!temp1.is(temp3));
+ ASSERT(!temp2.is(temp3));
+ ASSERT(ExternalReference::math_exp_constants(0).address() != NULL);
+
+ Label done;
+
+ __ li(temp3, Operand(ExternalReference::math_exp_constants(0)));
+
+ __ ldc1(double_scratch1, ExpConstant(0, temp3));
+ __ Move(result, kDoubleRegZero);
+ __ BranchF(&done, NULL, ge, double_scratch1, input);
+ __ ldc1(double_scratch2, ExpConstant(1, temp3));
+ __ ldc1(result, ExpConstant(2, temp3));
+ __ BranchF(&done, NULL, ge, input, double_scratch2);
+ __ ldc1(double_scratch1, ExpConstant(3, temp3));
+ __ ldc1(result, ExpConstant(4, temp3));
+ __ mul_d(double_scratch1, double_scratch1, input);
+ __ add_d(double_scratch1, double_scratch1, result);
+ __ Move(temp2, temp1, double_scratch1);
+ __ sub_d(double_scratch1, double_scratch1, result);
+ __ ldc1(result, ExpConstant(6, temp3));
+ __ ldc1(double_scratch2, ExpConstant(5, temp3));
+ __ mul_d(double_scratch1, double_scratch1, double_scratch2);
+ __ sub_d(double_scratch1, double_scratch1, input);
+ __ sub_d(result, result, double_scratch1);
+ __ mul_d(input, double_scratch1, double_scratch1);
+ __ mul_d(result, result, input);
+ __ srl(temp1, temp2, 11);
+ __ ldc1(double_scratch2, ExpConstant(7, temp3));
+ __ mul_d(result, result, double_scratch2);
+ __ sub_d(result, result, double_scratch1);
+ __ ldc1(double_scratch2, ExpConstant(8, temp3));
+ __ add_d(result, result, double_scratch2);
+ __ li(at, 0x7ff);
+ __ And(temp2, temp2, at);
+ __ Addu(temp1, temp1, Operand(0x3ff));
+ __ sll(temp1, temp1, 20);
+
+ // Must not call ExpConstant() after overwriting temp3!
+ __ li(temp3, Operand(ExternalReference::math_exp_log_table()));
+ __ sll(at, temp2, 3);
+ __ addu(at, at, temp3);
+ __ lw(at, MemOperand(at));
+ __ Addu(temp3, temp3, Operand(kPointerSize));
+ __ sll(temp2, temp2, 3);
+ __ addu(temp2, temp2, temp3);
+ __ lw(temp2, MemOperand(temp2));
+ __ Or(temp1, temp1, temp2);
+ __ Move(input, at, temp1);
+ __ mul_d(result, result, input);
+ __ bind(&done);
+}
+
+
+// nop(CODE_AGE_MARKER_NOP)
+static const uint32_t kCodeAgePatchFirstInstruction = 0x00010180;
+
+static byte* GetNoCodeAgeSequence(uint32_t* length) {
+ // The sequence of instructions that is patched out for aging code is the
+ // following boilerplate stack-building prologue that is found in FUNCTIONS
+ static bool initialized = false;
+ static uint32_t sequence[kNoCodeAgeSequenceLength];
+ byte* byte_sequence = reinterpret_cast<byte*>(sequence);
+ *length = kNoCodeAgeSequenceLength * Assembler::kInstrSize;
+ if (!initialized) {
+ CodePatcher patcher(byte_sequence, kNoCodeAgeSequenceLength);
+ patcher.masm()->Push(ra, fp, cp, a1);
+ patcher.masm()->nop(Assembler::CODE_AGE_SEQUENCE_NOP);
+ patcher.masm()->Addu(fp, sp, Operand(2 * kPointerSize));
+ initialized = true;
+ }
+ return byte_sequence;
+}
+
+
+bool Code::IsYoungSequence(byte* sequence) {
+ uint32_t young_length;
+ byte* young_sequence = GetNoCodeAgeSequence(&young_length);
+ bool result = !memcmp(sequence, young_sequence, young_length);
+ ASSERT(result ||
+ Memory::uint32_at(sequence) == kCodeAgePatchFirstInstruction);
+ return result;
+}
+
+
+void Code::GetCodeAgeAndParity(byte* sequence, Age* age,
+ MarkingParity* parity) {
+ if (IsYoungSequence(sequence)) {
+ *age = kNoAge;
+ *parity = NO_MARKING_PARITY;
+ } else {
+ Address target_address = Memory::Address_at(
+ sequence + Assembler::kInstrSize * (kNoCodeAgeSequenceLength - 1));
+ Code* stub = GetCodeFromTargetAddress(target_address);
+ GetCodeAgeAndParity(stub, age, parity);
+ }
+}
+
+
+void Code::PatchPlatformCodeAge(byte* sequence,
+ Code::Age age,
+ MarkingParity parity) {
+ uint32_t young_length;
+ byte* young_sequence = GetNoCodeAgeSequence(&young_length);
+ if (age == kNoAge) {
+ CopyBytes(sequence, young_sequence, young_length);
+ CPU::FlushICache(sequence, young_length);
+ } else {
+ Code* stub = GetCodeAgeStub(age, parity);
+ CodePatcher patcher(sequence, young_length / Assembler::kInstrSize);
+ // Mark this code sequence for FindPlatformCodeAgeSequence()
+ patcher.masm()->nop(Assembler::CODE_AGE_MARKER_NOP);
+ // Save the function's original return address
+ // (it will be clobbered by Call(t9))
+ patcher.masm()->mov(at, ra);
+ // Load the stub address to t9 and call it
+ patcher.masm()->li(t9,
+ Operand(reinterpret_cast<uint32_t>(stub->instruction_start())));
+ patcher.masm()->Call(t9);
+ // Record the stub address in the empty space for GetCodeAgeAndParity()
+ patcher.masm()->dd(reinterpret_cast<uint32_t>(stub->instruction_start()));
+ }
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/codegen-mips.h b/chromium/v8/src/mips/codegen-mips.h
new file mode 100644
index 00000000000..240b02ce44b
--- /dev/null
+++ b/chromium/v8/src/mips/codegen-mips.h
@@ -0,0 +1,117 @@
+// 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_CODEGEN_MIPS_H_
+#define V8_MIPS_CODEGEN_MIPS_H_
+
+
+#include "ast.h"
+#include "ic-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations
+class CompilationInfo;
+
+enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
+
+// -------------------------------------------------------------------------
+// CodeGenerator
+
+class CodeGenerator: public AstVisitor {
+ public:
+ CodeGenerator() {
+ InitializeAstVisitor();
+ }
+
+ static bool MakeCode(CompilationInfo* info);
+
+ // Printing of AST, etc. as requested by flags.
+ static void MakeCodePrologue(CompilationInfo* info, const char* kind);
+
+ // Allocate and install the code.
+ static Handle<Code> MakeCodeEpilogue(MacroAssembler* masm,
+ Code::Flags flags,
+ CompilationInfo* info);
+
+ // Print the code after compiling it.
+ static void PrintCode(Handle<Code> code, CompilationInfo* info);
+
+ static bool ShouldGenerateLog(Expression* type);
+
+ static void SetFunctionInfo(Handle<JSFunction> fun,
+ FunctionLiteral* lit,
+ bool is_toplevel,
+ Handle<Script> script);
+
+ static bool RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here = false);
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+
+ 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);
+};
+
+
+class MathExpGenerator : public AllStatic {
+ public:
+ static void EmitMathExp(MacroAssembler* masm,
+ DoubleRegister input,
+ DoubleRegister result,
+ DoubleRegister double_scratch1,
+ DoubleRegister double_scratch2,
+ Register temp1,
+ Register temp2,
+ Register temp3);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MathExpGenerator);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_MIPS_CODEGEN_MIPS_H_
diff --git a/chromium/v8/src/mips/constants-mips.cc b/chromium/v8/src/mips/constants-mips.cc
new file mode 100644
index 00000000000..2dd7a31f388
--- /dev/null
+++ b/chromium/v8/src/mips/constants-mips.cc
@@ -0,0 +1,359 @@
+// 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "constants-mips.h"
+
+namespace v8 {
+namespace internal {
+
+
+// -----------------------------------------------------------------------------
+// Registers.
+
+
+// These register names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+const char* Registers::names_[kNumSimuRegisters] = {
+ "zero_reg",
+ "at",
+ "v0", "v1",
+ "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9",
+ "k0", "k1",
+ "gp",
+ "sp",
+ "fp",
+ "ra",
+ "LO", "HI",
+ "pc"
+};
+
+
+// List of alias names which can be used when referring to MIPS registers.
+const Registers::RegisterAlias Registers::aliases_[] = {
+ {0, "zero"},
+ {23, "cp"},
+ {30, "s8"},
+ {30, "s8_fp"},
+ {kInvalidRegister, NULL}
+};
+
+
+const char* Registers::Name(int reg) {
+ const char* result;
+ if ((0 <= reg) && (reg < kNumSimuRegisters)) {
+ result = names_[reg];
+ } else {
+ result = "noreg";
+ }
+ return result;
+}
+
+
+int Registers::Number(const char* name) {
+ // Look through the canonical names.
+ for (int i = 0; i < kNumSimuRegisters; i++) {
+ if (strcmp(names_[i], name) == 0) {
+ return i;
+ }
+ }
+
+ // Look through the alias names.
+ int i = 0;
+ while (aliases_[i].reg != kInvalidRegister) {
+ if (strcmp(aliases_[i].name, name) == 0) {
+ return aliases_[i].reg;
+ }
+ i++;
+ }
+
+ // No register with the reguested name found.
+ return kInvalidRegister;
+}
+
+
+const char* FPURegisters::names_[kNumFPURegisters] = {
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11",
+ "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21",
+ "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"
+};
+
+
+// List of alias names which can be used when referring to MIPS registers.
+const FPURegisters::RegisterAlias FPURegisters::aliases_[] = {
+ {kInvalidRegister, NULL}
+};
+
+
+const char* FPURegisters::Name(int creg) {
+ const char* result;
+ if ((0 <= creg) && (creg < kNumFPURegisters)) {
+ result = names_[creg];
+ } else {
+ result = "nocreg";
+ }
+ return result;
+}
+
+
+int FPURegisters::Number(const char* name) {
+ // Look through the canonical names.
+ for (int i = 0; i < kNumFPURegisters; i++) {
+ if (strcmp(names_[i], name) == 0) {
+ return i;
+ }
+ }
+
+ // Look through the alias names.
+ int i = 0;
+ while (aliases_[i].creg != kInvalidRegister) {
+ if (strcmp(aliases_[i].name, name) == 0) {
+ return aliases_[i].creg;
+ }
+ i++;
+ }
+
+ // No Cregister with the reguested name found.
+ return kInvalidFPURegister;
+}
+
+
+// -----------------------------------------------------------------------------
+// Instructions.
+
+bool Instruction::IsForbiddenInBranchDelay() const {
+ const int op = OpcodeFieldRaw();
+ switch (op) {
+ case J:
+ case JAL:
+ case BEQ:
+ case BNE:
+ case BLEZ:
+ case BGTZ:
+ case BEQL:
+ case BNEL:
+ case BLEZL:
+ case BGTZL:
+ return true;
+ case REGIMM:
+ switch (RtFieldRaw()) {
+ case BLTZ:
+ case BGEZ:
+ case BLTZAL:
+ case BGEZAL:
+ return true;
+ default:
+ return false;
+ };
+ break;
+ case SPECIAL:
+ switch (FunctionFieldRaw()) {
+ case JR:
+ case JALR:
+ return true;
+ default:
+ return false;
+ };
+ break;
+ default:
+ return false;
+ };
+}
+
+
+bool Instruction::IsLinkingInstruction() const {
+ const int op = OpcodeFieldRaw();
+ switch (op) {
+ case JAL:
+ return true;
+ case REGIMM:
+ switch (RtFieldRaw()) {
+ case BGEZAL:
+ case BLTZAL:
+ return true;
+ default:
+ return false;
+ };
+ case SPECIAL:
+ switch (FunctionFieldRaw()) {
+ case JALR:
+ return true;
+ default:
+ return false;
+ };
+ default:
+ return false;
+ };
+}
+
+
+bool Instruction::IsTrap() const {
+ if (OpcodeFieldRaw() != SPECIAL) {
+ return false;
+ } else {
+ switch (FunctionFieldRaw()) {
+ case BREAK:
+ case TGE:
+ case TGEU:
+ case TLT:
+ case TLTU:
+ case TEQ:
+ case TNE:
+ return true;
+ default:
+ return false;
+ };
+ }
+}
+
+
+Instruction::Type Instruction::InstructionType() const {
+ switch (OpcodeFieldRaw()) {
+ case SPECIAL:
+ switch (FunctionFieldRaw()) {
+ case JR:
+ case JALR:
+ case BREAK:
+ case SLL:
+ case SRL:
+ case SRA:
+ case SLLV:
+ case SRLV:
+ case SRAV:
+ case MFHI:
+ case MFLO:
+ case MULT:
+ case MULTU:
+ case DIV:
+ case DIVU:
+ case ADD:
+ case ADDU:
+ case SUB:
+ case SUBU:
+ case AND:
+ case OR:
+ case XOR:
+ case NOR:
+ case SLT:
+ case SLTU:
+ case TGE:
+ case TGEU:
+ case TLT:
+ case TLTU:
+ case TEQ:
+ case TNE:
+ case MOVZ:
+ case MOVN:
+ case MOVCI:
+ return kRegisterType;
+ default:
+ return kUnsupported;
+ };
+ break;
+ case SPECIAL2:
+ switch (FunctionFieldRaw()) {
+ case MUL:
+ case CLZ:
+ return kRegisterType;
+ default:
+ return kUnsupported;
+ };
+ break;
+ case SPECIAL3:
+ switch (FunctionFieldRaw()) {
+ case INS:
+ case EXT:
+ return kRegisterType;
+ default:
+ return kUnsupported;
+ };
+ break;
+ case COP1: // Coprocessor instructions.
+ switch (RsFieldRawNoAssert()) {
+ case BC1: // Branch on coprocessor condition.
+ return kImmediateType;
+ default:
+ return kRegisterType;
+ };
+ break;
+ case COP1X:
+ return kRegisterType;
+ // 16 bits Immediate type instructions. e.g.: addi dest, src, imm16.
+ case REGIMM:
+ case BEQ:
+ case BNE:
+ case BLEZ:
+ case BGTZ:
+ case ADDI:
+ case ADDIU:
+ case SLTI:
+ case SLTIU:
+ case ANDI:
+ case ORI:
+ case XORI:
+ case LUI:
+ case BEQL:
+ case BNEL:
+ case BLEZL:
+ case BGTZL:
+ case LB:
+ case LH:
+ case LWL:
+ case LW:
+ case LBU:
+ case LHU:
+ case LWR:
+ case SB:
+ case SH:
+ case SWL:
+ case SW:
+ case SWR:
+ case LWC1:
+ case LDC1:
+ case SWC1:
+ case SDC1:
+ return kImmediateType;
+ // 26 bits immediate type instructions. e.g.: j imm26.
+ case J:
+ case JAL:
+ return kJumpType;
+ default:
+ return kUnsupported;
+ };
+ return kUnsupported;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/constants-mips.h b/chromium/v8/src/mips/constants-mips.h
new file mode 100644
index 00000000000..5a0870fd218
--- /dev/null
+++ b/chromium/v8/src/mips/constants-mips.h
@@ -0,0 +1,808 @@
+// Copyright 2012 the V8 project authors. 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_CONSTANTS_H_
+#define V8_MIPS_CONSTANTS_H_
+
+// UNIMPLEMENTED_ macro for MIPS.
+#ifdef DEBUG
+#define UNIMPLEMENTED_MIPS() \
+ v8::internal::PrintF("%s, \tline %d: \tfunction %s not implemented. \n", \
+ __FILE__, __LINE__, __func__)
+#else
+#define UNIMPLEMENTED_MIPS()
+#endif
+
+#define UNSUPPORTED_MIPS() v8::internal::PrintF("Unsupported instruction.\n")
+
+enum ArchVariants {
+ kMips32r2,
+ kMips32r1,
+ kLoongson
+};
+
+#ifdef _MIPS_ARCH_MIPS32R2
+ static const ArchVariants kArchVariant = kMips32r2;
+#elif _MIPS_ARCH_LOONGSON
+// The loongson flag refers to the LOONGSON architectures based on MIPS-III,
+// which predates (and is a subset of) the mips32r2 and r1 architectures.
+ static const ArchVariants kArchVariant = kLoongson;
+#else
+ static const ArchVariants kArchVariant = kMips32r1;
+#endif
+
+
+#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.
+const bool IsMipsSoftFloatABI = false;
+#elif(defined(__mips_soft_float) && __mips_soft_float != 0)
+// This flag is raised when -msoft-float is passed to the compiler.
+// Although FPU is a base requirement for v8, soft-float ABI is used
+// on soft-float systems with FPU kernel emulation.
+const bool IsMipsSoftFloatABI = true;
+#else
+const bool IsMipsSoftFloatABI = true;
+#endif
+
+
+// Defines constants and accessor classes to assemble, disassemble and
+// simulate MIPS32 instructions.
+//
+// See: MIPS32 Architecture For Programmers
+// Volume II: The MIPS32 Instruction Set
+// Try www.cs.cornell.edu/courses/cs3410/2008fa/MIPS_Vol2.pdf.
+
+namespace v8 {
+namespace internal {
+
+// -----------------------------------------------------------------------------
+// Registers and FPURegisters.
+
+// Number of general purpose registers.
+const int kNumRegisters = 32;
+const int kInvalidRegister = -1;
+
+// Number of registers with HI, LO, and pc.
+const int kNumSimuRegisters = 35;
+
+// In the simulator, the PC register is simulated as the 34th register.
+const int kPCRegister = 34;
+
+// Number coprocessor registers.
+const int kNumFPURegisters = 32;
+const int kInvalidFPURegister = -1;
+
+// FPU (coprocessor 1) control registers. Currently only FCSR is implemented.
+const int kFCSRRegister = 31;
+const int kInvalidFPUControlRegister = -1;
+const uint32_t kFPUInvalidResult = static_cast<uint32_t>(1 << 31) - 1;
+
+// FCSR constants.
+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;
+
+const uint32_t kFCSRExceptionFlagMask = kFCSRFlagMask ^ kFCSRInexactFlagMask;
+
+// Helper functions for converting between register numbers and names.
+class Registers {
+ public:
+ // Return the name of the register.
+ static const char* Name(int reg);
+
+ // Lookup the register number for the name provided.
+ static int Number(const char* name);
+
+ struct RegisterAlias {
+ int reg;
+ const char* name;
+ };
+
+ static const int32_t kMaxValue = 0x7fffffff;
+ static const int32_t kMinValue = 0x80000000;
+
+ private:
+ static const char* names_[kNumSimuRegisters];
+ static const RegisterAlias aliases_[];
+};
+
+// Helper functions for converting between register numbers and names.
+class FPURegisters {
+ public:
+ // Return the name of the register.
+ static const char* Name(int reg);
+
+ // Lookup the register number for the name provided.
+ static int Number(const char* name);
+
+ struct RegisterAlias {
+ int creg;
+ const char* name;
+ };
+
+ private:
+ static const char* names_[kNumFPURegisters];
+ static const RegisterAlias aliases_[];
+};
+
+
+// -----------------------------------------------------------------------------
+// Instructions encoding constants.
+
+// On MIPS all instructions are 32 bits.
+typedef int32_t Instr;
+
+// Special Software Interrupt codes when used in the presence of the MIPS
+// simulator.
+enum SoftwareInterruptCodes {
+ // Transition to C code.
+ call_rt_redirected = 0xfffff
+};
+
+// On MIPS Simulator breakpoints can have different codes:
+// - Breaks between 0 and kMaxWatchpointCode are treated as simple watchpoints,
+// the simulator will run through them and print the registers.
+// - Breaks between kMaxWatchpointCode and kMaxStopCode are treated as stop()
+// instructions (see Assembler::stop()).
+// - Breaks larger than kMaxStopCode are simple breaks, dropping you into the
+// debugger.
+const uint32_t kMaxWatchpointCode = 31;
+const uint32_t kMaxStopCode = 127;
+STATIC_ASSERT(kMaxWatchpointCode < kMaxStopCode);
+
+
+// ----- Fields offset and length.
+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.
+const int kImmFieldShift = 2;
+
+const int kFrBits = 5;
+const int kFrShift = 21;
+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.
+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.
+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
+// MIPS32 Architecture For Programmers, Volume II: The MIPS32 Instruction Set.
+enum Opcode {
+ SPECIAL = 0 << kOpcodeShift,
+ REGIMM = 1 << kOpcodeShift,
+
+ J = ((0 << 3) + 2) << kOpcodeShift,
+ JAL = ((0 << 3) + 3) << kOpcodeShift,
+ BEQ = ((0 << 3) + 4) << kOpcodeShift,
+ BNE = ((0 << 3) + 5) << kOpcodeShift,
+ BLEZ = ((0 << 3) + 6) << kOpcodeShift,
+ BGTZ = ((0 << 3) + 7) << kOpcodeShift,
+
+ ADDI = ((1 << 3) + 0) << kOpcodeShift,
+ ADDIU = ((1 << 3) + 1) << kOpcodeShift,
+ SLTI = ((1 << 3) + 2) << kOpcodeShift,
+ SLTIU = ((1 << 3) + 3) << kOpcodeShift,
+ ANDI = ((1 << 3) + 4) << kOpcodeShift,
+ ORI = ((1 << 3) + 5) << kOpcodeShift,
+ XORI = ((1 << 3) + 6) << kOpcodeShift,
+ LUI = ((1 << 3) + 7) << kOpcodeShift,
+
+ COP1 = ((2 << 3) + 1) << kOpcodeShift, // Coprocessor 1 class.
+ BEQL = ((2 << 3) + 4) << kOpcodeShift,
+ BNEL = ((2 << 3) + 5) << kOpcodeShift,
+ BLEZL = ((2 << 3) + 6) << kOpcodeShift,
+ BGTZL = ((2 << 3) + 7) << kOpcodeShift,
+
+ SPECIAL2 = ((3 << 3) + 4) << kOpcodeShift,
+ SPECIAL3 = ((3 << 3) + 7) << kOpcodeShift,
+
+ LB = ((4 << 3) + 0) << kOpcodeShift,
+ LH = ((4 << 3) + 1) << kOpcodeShift,
+ LWL = ((4 << 3) + 2) << kOpcodeShift,
+ LW = ((4 << 3) + 3) << kOpcodeShift,
+ LBU = ((4 << 3) + 4) << kOpcodeShift,
+ LHU = ((4 << 3) + 5) << kOpcodeShift,
+ LWR = ((4 << 3) + 6) << kOpcodeShift,
+ SB = ((5 << 3) + 0) << kOpcodeShift,
+ SH = ((5 << 3) + 1) << kOpcodeShift,
+ SWL = ((5 << 3) + 2) << kOpcodeShift,
+ SW = ((5 << 3) + 3) << kOpcodeShift,
+ SWR = ((5 << 3) + 6) << kOpcodeShift,
+
+ LWC1 = ((6 << 3) + 1) << kOpcodeShift,
+ LDC1 = ((6 << 3) + 5) << kOpcodeShift,
+
+ SWC1 = ((7 << 3) + 1) << kOpcodeShift,
+ SDC1 = ((7 << 3) + 5) << kOpcodeShift,
+
+ COP1X = ((1 << 4) + 3) << kOpcodeShift
+};
+
+enum SecondaryField {
+ // SPECIAL Encoding of Function Field.
+ SLL = ((0 << 3) + 0),
+ MOVCI = ((0 << 3) + 1),
+ SRL = ((0 << 3) + 2),
+ SRA = ((0 << 3) + 3),
+ SLLV = ((0 << 3) + 4),
+ SRLV = ((0 << 3) + 6),
+ SRAV = ((0 << 3) + 7),
+
+ JR = ((1 << 3) + 0),
+ JALR = ((1 << 3) + 1),
+ MOVZ = ((1 << 3) + 2),
+ MOVN = ((1 << 3) + 3),
+ BREAK = ((1 << 3) + 5),
+
+ MFHI = ((2 << 3) + 0),
+ MFLO = ((2 << 3) + 2),
+
+ MULT = ((3 << 3) + 0),
+ MULTU = ((3 << 3) + 1),
+ DIV = ((3 << 3) + 2),
+ DIVU = ((3 << 3) + 3),
+
+ ADD = ((4 << 3) + 0),
+ ADDU = ((4 << 3) + 1),
+ SUB = ((4 << 3) + 2),
+ SUBU = ((4 << 3) + 3),
+ AND = ((4 << 3) + 4),
+ OR = ((4 << 3) + 5),
+ XOR = ((4 << 3) + 6),
+ NOR = ((4 << 3) + 7),
+
+ SLT = ((5 << 3) + 2),
+ SLTU = ((5 << 3) + 3),
+
+ TGE = ((6 << 3) + 0),
+ TGEU = ((6 << 3) + 1),
+ TLT = ((6 << 3) + 2),
+ TLTU = ((6 << 3) + 3),
+ TEQ = ((6 << 3) + 4),
+ TNE = ((6 << 3) + 6),
+
+ // SPECIAL2 Encoding of Function Field.
+ MUL = ((0 << 3) + 2),
+ CLZ = ((4 << 3) + 0),
+ CLO = ((4 << 3) + 1),
+
+ // SPECIAL3 Encoding of Function Field.
+ EXT = ((0 << 3) + 0),
+ INS = ((0 << 3) + 4),
+
+ // REGIMM encoding of rt Field.
+ BLTZ = ((0 << 3) + 0) << 16,
+ BGEZ = ((0 << 3) + 1) << 16,
+ BLTZAL = ((2 << 3) + 0) << 16,
+ BGEZAL = ((2 << 3) + 1) << 16,
+
+ // COP1 Encoding of rs Field.
+ MFC1 = ((0 << 3) + 0) << 21,
+ CFC1 = ((0 << 3) + 2) << 21,
+ MFHC1 = ((0 << 3) + 3) << 21,
+ MTC1 = ((0 << 3) + 4) << 21,
+ CTC1 = ((0 << 3) + 6) << 21,
+ MTHC1 = ((0 << 3) + 7) << 21,
+ BC1 = ((1 << 3) + 0) << 21,
+ S = ((2 << 3) + 0) << 21,
+ D = ((2 << 3) + 1) << 21,
+ W = ((2 << 3) + 4) << 21,
+ L = ((2 << 3) + 5) << 21,
+ PS = ((2 << 3) + 6) << 21,
+ // COP1 Encoding of Function Field When rs=S.
+ ROUND_L_S = ((1 << 3) + 0),
+ TRUNC_L_S = ((1 << 3) + 1),
+ CEIL_L_S = ((1 << 3) + 2),
+ FLOOR_L_S = ((1 << 3) + 3),
+ ROUND_W_S = ((1 << 3) + 4),
+ TRUNC_W_S = ((1 << 3) + 5),
+ CEIL_W_S = ((1 << 3) + 6),
+ FLOOR_W_S = ((1 << 3) + 7),
+ CVT_D_S = ((4 << 3) + 1),
+ CVT_W_S = ((4 << 3) + 4),
+ CVT_L_S = ((4 << 3) + 5),
+ CVT_PS_S = ((4 << 3) + 6),
+ // COP1 Encoding of Function Field When rs=D.
+ ADD_D = ((0 << 3) + 0),
+ SUB_D = ((0 << 3) + 1),
+ MUL_D = ((0 << 3) + 2),
+ DIV_D = ((0 << 3) + 3),
+ SQRT_D = ((0 << 3) + 4),
+ ABS_D = ((0 << 3) + 5),
+ MOV_D = ((0 << 3) + 6),
+ NEG_D = ((0 << 3) + 7),
+ ROUND_L_D = ((1 << 3) + 0),
+ TRUNC_L_D = ((1 << 3) + 1),
+ CEIL_L_D = ((1 << 3) + 2),
+ FLOOR_L_D = ((1 << 3) + 3),
+ ROUND_W_D = ((1 << 3) + 4),
+ TRUNC_W_D = ((1 << 3) + 5),
+ CEIL_W_D = ((1 << 3) + 6),
+ FLOOR_W_D = ((1 << 3) + 7),
+ CVT_S_D = ((4 << 3) + 0),
+ CVT_W_D = ((4 << 3) + 4),
+ CVT_L_D = ((4 << 3) + 5),
+ C_F_D = ((6 << 3) + 0),
+ C_UN_D = ((6 << 3) + 1),
+ C_EQ_D = ((6 << 3) + 2),
+ C_UEQ_D = ((6 << 3) + 3),
+ C_OLT_D = ((6 << 3) + 4),
+ C_ULT_D = ((6 << 3) + 5),
+ C_OLE_D = ((6 << 3) + 6),
+ C_ULE_D = ((6 << 3) + 7),
+ // COP1 Encoding of Function Field When rs=W or L.
+ CVT_S_W = ((4 << 3) + 0),
+ CVT_D_W = ((4 << 3) + 1),
+ CVT_S_L = ((4 << 3) + 0),
+ CVT_D_L = ((4 << 3) + 1),
+ // COP1 Encoding of Function Field When rs=PS.
+ // COP1X Encoding of Function Field.
+ MADD_D = ((4 << 3) + 1),
+
+ NULLSF = 0
+};
+
+
+// ----- Emulated conditions.
+// On MIPS we use this enum to abstract from conditionnal branch instructions.
+// The 'U' prefix is used to specify unsigned comparisons.
+// Oppposite conditions must be paired as odd/even numbers
+// because 'NegateCondition' function flips LSB to negate condition.
+enum Condition {
+ // Any value < 0 is considered no_condition.
+ kNoCondition = -1,
+
+ overflow = 0,
+ no_overflow = 1,
+ Uless = 2,
+ Ugreater_equal= 3,
+ equal = 4,
+ not_equal = 5,
+ Uless_equal = 6,
+ Ugreater = 7,
+ negative = 8,
+ positive = 9,
+ parity_even = 10,
+ parity_odd = 11,
+ less = 12,
+ greater_equal = 13,
+ less_equal = 14,
+ greater = 15,
+ ueq = 16, // Unordered or Equal.
+ nue = 17, // Not (Unordered or Equal).
+
+ cc_always = 18,
+
+ // Aliases.
+ carry = Uless,
+ not_carry = Ugreater_equal,
+ zero = equal,
+ eq = equal,
+ not_zero = not_equal,
+ ne = not_equal,
+ nz = not_equal,
+ sign = negative,
+ not_sign = positive,
+ mi = negative,
+ pl = positive,
+ hi = Ugreater,
+ ls = Uless_equal,
+ ge = greater_equal,
+ lt = less,
+ gt = greater,
+ le = less_equal,
+ hs = Ugreater_equal,
+ lo = Uless,
+ al = cc_always,
+
+ cc_default = kNoCondition
+};
+
+
+// Returns the equivalent of !cc.
+// Negation of the default kNoCondition (-1) results in a non-default
+// no_condition value (-2). As long as tests for no_condition check
+// for condition < 0, this will work as expected.
+inline Condition NegateCondition(Condition cc) {
+ ASSERT(cc != cc_always);
+ return static_cast<Condition>(cc ^ 1);
+}
+
+
+inline Condition ReverseCondition(Condition cc) {
+ switch (cc) {
+ case Uless:
+ return Ugreater;
+ case Ugreater:
+ return Uless;
+ case Ugreater_equal:
+ return Uless_equal;
+ case Uless_equal:
+ return Ugreater_equal;
+ case less:
+ return greater;
+ case greater:
+ return less;
+ case greater_equal:
+ return less_equal;
+ case less_equal:
+ return greater_equal;
+ default:
+ return cc;
+ };
+}
+
+
+// ----- Coprocessor conditions.
+enum FPUCondition {
+ kNoFPUCondition = -1,
+
+ F = 0, // False.
+ UN = 1, // Unordered.
+ EQ = 2, // Equal.
+ UEQ = 3, // Unordered or Equal.
+ OLT = 4, // Ordered or Less Than.
+ ULT = 5, // Unordered or Less Than.
+ OLE = 6, // Ordered or Less Than or Equal.
+ ULE = 7 // Unordered or Less Than or Equal.
+};
+
+
+// FPU rounding modes.
+enum FPURoundingMode {
+ RN = 0 << 0, // Round to Nearest.
+ RZ = 1 << 0, // Round towards zero.
+ RP = 2 << 0, // Round towards Plus Infinity.
+ RM = 3 << 0, // Round towards Minus Infinity.
+
+ // Aliases.
+ kRoundToNearest = RN,
+ kRoundToZero = RZ,
+ kRoundToPlusInf = RP,
+ kRoundToMinusInf = RM
+};
+
+const uint32_t kFPURoundingModeMask = 3 << 0;
+
+enum CheckForInexactConversion {
+ kCheckForInexactConversion,
+ kDontCheckForInexactConversion
+};
+
+
+// -----------------------------------------------------------------------------
+// Hints.
+
+// Branch hints are not used on the MIPS. They are defined so that they can
+// appear in shared function signatures, but will be ignored in MIPS
+// implementations.
+enum Hint {
+ no_hint = 0
+};
+
+
+inline Hint NegateHint(Hint hint) {
+ return no_hint;
+}
+
+
+// -----------------------------------------------------------------------------
+// Specific instructions, constants, and masks.
+// These constants are declared in assembler-mips.cc, as they use named
+// registers and other constants.
+
+// addiu(sp, sp, 4) aka Pop() operation or part of Pop(r)
+// operations as post-increment of sp.
+extern const Instr kPopInstruction;
+// addiu(sp, sp, -4) part of Push(r) operation as pre-decrement of sp.
+extern const Instr kPushInstruction;
+// sw(r, MemOperand(sp, 0))
+extern const Instr kPushRegPattern;
+// lw(r, MemOperand(sp, 0))
+extern const Instr kPopRegPattern;
+extern const Instr kLwRegFpOffsetPattern;
+extern const Instr kSwRegFpOffsetPattern;
+extern const Instr kLwRegFpNegOffsetPattern;
+extern const Instr kSwRegFpNegOffsetPattern;
+// A mask for the Rt register for push, pop, lw, sw instructions.
+extern const Instr kRtMask;
+extern const Instr kLwSwInstrTypeMask;
+extern const Instr kLwSwInstrArgumentMask;
+extern const Instr kLwSwOffsetMask;
+
+// Break 0xfffff, reserved for redirected real time call.
+const Instr rtCallRedirInstr = SPECIAL | BREAK | call_rt_redirected << 6;
+// A nop instruction. (Encoding of sll 0 0 0).
+const Instr nopInstr = 0;
+
+class Instruction {
+ public:
+ enum {
+ kInstrSize = 4,
+ kInstrSizeLog2 = 2,
+ // On MIPS PC cannot actually be directly accessed. We behave as if PC was
+ // always the value of the current instruction being executed.
+ kPCReadOffset = 0
+ };
+
+ // Get the raw instruction bits.
+ inline Instr InstructionBits() const {
+ return *reinterpret_cast<const Instr*>(this);
+ }
+
+ // Set the raw instruction bits to value.
+ inline void SetInstructionBits(Instr value) {
+ *reinterpret_cast<Instr*>(this) = value;
+ }
+
+ // Read one particular bit out of the instruction bits.
+ inline int Bit(int nr) const {
+ return (InstructionBits() >> nr) & 1;
+ }
+
+ // Read a bit field out of the instruction bits.
+ inline int Bits(int hi, int lo) const {
+ return (InstructionBits() >> lo) & ((2 << (hi - lo)) - 1);
+ }
+
+ // Instruction type.
+ enum Type {
+ kRegisterType,
+ kImmediateType,
+ kJumpType,
+ kUnsupported = -1
+ };
+
+ // Get the encoding type of the instruction.
+ Type InstructionType() const;
+
+
+ // Accessors for the different named fields used in the MIPS encoding.
+ inline Opcode OpcodeValue() const {
+ return static_cast<Opcode>(
+ Bits(kOpcodeShift + kOpcodeBits - 1, kOpcodeShift));
+ }
+
+ inline int RsValue() const {
+ ASSERT(InstructionType() == kRegisterType ||
+ InstructionType() == kImmediateType);
+ return Bits(kRsShift + kRsBits - 1, kRsShift);
+ }
+
+ inline int RtValue() const {
+ ASSERT(InstructionType() == kRegisterType ||
+ InstructionType() == kImmediateType);
+ return Bits(kRtShift + kRtBits - 1, kRtShift);
+ }
+
+ inline int RdValue() const {
+ ASSERT(InstructionType() == kRegisterType);
+ return Bits(kRdShift + kRdBits - 1, kRdShift);
+ }
+
+ inline int SaValue() const {
+ ASSERT(InstructionType() == kRegisterType);
+ return Bits(kSaShift + kSaBits - 1, kSaShift);
+ }
+
+ inline int FunctionValue() const {
+ ASSERT(InstructionType() == kRegisterType ||
+ InstructionType() == kImmediateType);
+ return Bits(kFunctionShift + kFunctionBits - 1, kFunctionShift);
+ }
+
+ inline int FdValue() const {
+ return Bits(kFdShift + kFdBits - 1, kFdShift);
+ }
+
+ inline int FsValue() const {
+ return Bits(kFsShift + kFsBits - 1, kFsShift);
+ }
+
+ inline int FtValue() const {
+ return Bits(kFtShift + kFtBits - 1, kFtShift);
+ }
+
+ inline int FrValue() const {
+ return Bits(kFrShift + kFrBits -1, kFrShift);
+ }
+
+ // Float Compare condition code instruction bits.
+ inline int FCccValue() const {
+ return Bits(kFCccShift + kFCccBits - 1, kFCccShift);
+ }
+
+ // Float Branch condition code instruction bits.
+ inline int FBccValue() const {
+ return Bits(kFBccShift + kFBccBits - 1, kFBccShift);
+ }
+
+ // Float Branch true/false instruction bit.
+ inline int FBtrueValue() const {
+ return Bits(kFBtrueShift + kFBtrueBits - 1, kFBtrueShift);
+ }
+
+ // Return the fields at their original place in the instruction encoding.
+ inline Opcode OpcodeFieldRaw() const {
+ return static_cast<Opcode>(InstructionBits() & kOpcodeMask);
+ }
+
+ inline int RsFieldRaw() const {
+ ASSERT(InstructionType() == kRegisterType ||
+ InstructionType() == kImmediateType);
+ return InstructionBits() & kRsFieldMask;
+ }
+
+ // Same as above function, but safe to call within InstructionType().
+ inline int RsFieldRawNoAssert() const {
+ return InstructionBits() & kRsFieldMask;
+ }
+
+ inline int RtFieldRaw() const {
+ ASSERT(InstructionType() == kRegisterType ||
+ InstructionType() == kImmediateType);
+ return InstructionBits() & kRtFieldMask;
+ }
+
+ inline int RdFieldRaw() const {
+ ASSERT(InstructionType() == kRegisterType);
+ return InstructionBits() & kRdFieldMask;
+ }
+
+ inline int SaFieldRaw() const {
+ ASSERT(InstructionType() == kRegisterType);
+ return InstructionBits() & kSaFieldMask;
+ }
+
+ inline int FunctionFieldRaw() const {
+ return InstructionBits() & kFunctionFieldMask;
+ }
+
+ // Get the secondary field according to the opcode.
+ inline int SecondaryValue() const {
+ Opcode op = OpcodeFieldRaw();
+ switch (op) {
+ case SPECIAL:
+ case SPECIAL2:
+ return FunctionValue();
+ case COP1:
+ return RsValue();
+ case REGIMM:
+ return RtValue();
+ default:
+ return NULLSF;
+ }
+ }
+
+ inline int32_t Imm16Value() const {
+ ASSERT(InstructionType() == kImmediateType);
+ return Bits(kImm16Shift + kImm16Bits - 1, kImm16Shift);
+ }
+
+ inline int32_t Imm26Value() const {
+ ASSERT(InstructionType() == kJumpType);
+ return Bits(kImm26Shift + kImm26Bits - 1, kImm26Shift);
+ }
+
+ // Say if the instruction should not be used in a branch delay slot.
+ bool IsForbiddenInBranchDelay() const;
+ // 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;
+
+ // Instructions are read of out a code stream. The only way to get a
+ // reference to an instruction is to convert a pointer. There is no way
+ // to allocate or create instances of class Instruction.
+ // Use the At(pc) function to create references to Instruction.
+ static Instruction* At(byte* pc) {
+ return reinterpret_cast<Instruction*>(pc);
+ }
+
+ private:
+ // We need to prevent the creation of instances of class Instruction.
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction);
+};
+
+
+// -----------------------------------------------------------------------------
+// MIPS assembly various constants.
+
+// C/C++ argument slots size.
+const int kCArgSlotCount = 4;
+const int kCArgsSlotsSize = kCArgSlotCount * Instruction::kInstrSize;
+// JS argument slots size.
+const int kJSArgsSlotsSize = 0 * Instruction::kInstrSize;
+// Assembly builtins argument slots size.
+const int kBArgsSlotsSize = 0 * Instruction::kInstrSize;
+
+const int kBranchReturnOffset = 2 * Instruction::kInstrSize;
+
+} } // namespace v8::internal
+
+#endif // #ifndef V8_MIPS_CONSTANTS_H_
diff --git a/chromium/v8/src/mips/cpu-mips.cc b/chromium/v8/src/mips/cpu-mips.cc
new file mode 100644
index 00000000000..d13b23330fe
--- /dev/null
+++ b/chromium/v8/src/mips/cpu-mips.cc
@@ -0,0 +1,100 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// CPU specific code for arm independent of OS goes here.
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#ifdef __mips
+#include <asm/cachectl.h>
+#endif // #ifdef __mips
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "cpu.h"
+#include "macro-assembler.h"
+
+#include "simulator.h" // For cache flushing.
+
+namespace v8 {
+namespace internal {
+
+
+void CPU::SetUp() {
+ CpuFeatures::Probe();
+}
+
+
+bool CPU::SupportsCrankshaft() {
+ return CpuFeatures::IsSupported(FPU);
+}
+
+
+void CPU::FlushICache(void* start, size_t size) {
+ // Nothing to do, flushing no instructions.
+ if (size == 0) {
+ return;
+ }
+
+#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
+ // that the Icache was flushed.
+ // None of this code ends up in the snapshot so there are no issues
+ // around whether or not to generate the code when building snapshots.
+ Simulator::FlushICache(Isolate::Current()->simulator_i_cache(), start, size);
+#endif // USE_SIMULATOR.
+}
+
+
+void CPU::DebugBreak() {
+#ifdef __mips
+ asm volatile("break");
+#endif // #ifdef __mips
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/debug-mips.cc b/chromium/v8/src/mips/debug-mips.cc
new file mode 100644
index 00000000000..020228fc6b5
--- /dev/null
+++ b/chromium/v8/src/mips/debug-mips.cc
@@ -0,0 +1,345 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "codegen.h"
+#include "debug.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+bool BreakLocationIterator::IsDebugBreakAtReturn() {
+ return Debug::IsDebugBreakAtReturn(rinfo());
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtReturn() {
+ // Mips return sequence:
+ // mov sp, fp
+ // lw fp, sp(0)
+ // lw ra, sp(4)
+ // addiu sp, sp, 8
+ // addiu sp, sp, N
+ // jr ra
+ // nop (in branch delay slot)
+
+ // Make sure this constant matches the number if instrucntions we emit.
+ ASSERT(Assembler::kJSReturnSequenceInstructions == 7);
+ CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
+ // li and Call pseudo-instructions emit two instructions each.
+ patcher.masm()->li(v8::internal::t9,
+ Operand(reinterpret_cast<int32_t>(
+ Isolate::Current()->debug()->debug_break_return()->entry())));
+ patcher.masm()->Call(v8::internal::t9);
+ patcher.masm()->nop();
+ patcher.masm()->nop();
+ patcher.masm()->nop();
+
+ // TODO(mips): Open issue about using breakpoint instruction instead of nops.
+ // patcher.masm()->bkpt(0);
+}
+
+
+// Restore the JS frame exit code.
+void BreakLocationIterator::ClearDebugBreakAtReturn() {
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kJSReturnSequenceInstructions);
+}
+
+
+// A debug break in the exit code is identified by the JS frame exit code
+// having been patched with li/call psuedo-instrunction (liu/ori/jalr).
+bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
+ return rinfo->IsPatchedReturnSequence();
+}
+
+
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return rinfo()->IsPatchedDebugBreakSlotSequence();
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Patch the code changing the debug break slot code from:
+ // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // to a call to the debug break slot code.
+ // li t9, address (lui t9 / ori t9 instruction pair)
+ // call t9 (jalr t9 / nop instruction pair)
+ CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
+ patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>(
+ Isolate::Current()->debug()->debug_break_slot()->entry())));
+ patcher.masm()->Call(v8::internal::t9);
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kDebugBreakSlotInstructions);
+}
+
+const bool Debug::FramePaddingLayout::kIsSupported = false;
+
+
+#define __ ACCESS_MASM(masm)
+
+
+
+static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
+ RegList object_regs,
+ RegList non_object_regs) {
+ {
+ 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, kUnableToEncodeValueAsSmi, at, Operand(zero_reg));
+ }
+ __ sll(reg, reg, kSmiTagSize);
+ }
+ }
+ __ MultiPush(object_regs | non_object_regs);
+ }
+
+#ifdef DEBUG
+ __ RecordComment("// Calling from debug break to runtime - come in - over");
+#endif
+ __ PrepareCEntryArgs(0); // No arguments.
+ __ PrepareCEntryFunction(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);
+ }
+ }
+ }
+
+ // 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
+ // overwritten by the address of DebugBreakXXX.
+ __ li(t9, Operand(
+ ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate())));
+ __ lw(t9, MemOperand(t9));
+ __ Jump(t9);
+}
+
+
+void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
+ // Calling convention for IC load (from ic-mips.cc).
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -- [sp] : receiver
+ // -----------------------------------
+ // Registers a0 and a2 contain objects that need to be pushed on the
+ // expression stack of the fake JS frame.
+ Generate_DebugBreakCallHelper(masm, a0.bit() | a2.bit(), 0);
+}
+
+
+void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
+ // Calling convention for IC store (from ic-mips.cc).
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ // Registers a0, a1, and a2 contain objects that need to be pushed on the
+ // expression stack of the fake JS frame.
+ Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0);
+}
+
+
+void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit(), 0);
+}
+
+
+void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0);
+}
+
+
+void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
+ // Register state for CompareNil IC
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, a0.bit(), 0);
+}
+
+
+void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
+ // Calling convention for IC call (from ic-mips.cc).
+ // ----------- S t a t e -------------
+ // -- a2: name
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, a2.bit(), 0);
+}
+
+
+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
+ // care.
+ Generate_DebugBreakCallHelper(masm, v0.bit(), 0);
+}
+
+
+void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
+ // Register state for CallFunctionStub (from code-stubs-mips.cc).
+ // ----------- S t a t e -------------
+ // -- a1 : function
+ // -----------------------------------
+ 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());
+}
+
+
+void Debug::GenerateSlot(MacroAssembler* masm) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the trampoline pool in the debug break slot code.
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ RecordDebugBreakSlot();
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ ASSERT_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_codesize));
+}
+
+
+void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
+ // In the places where a debug break slot is inserted no registers can contain
+ // object pointers.
+ Generate_DebugBreakCallHelper(masm, 0, 0);
+}
+
+
+void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnMips);
+}
+
+
+void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnMips);
+}
+
+
+const bool Debug::kFrameDropperSupported = false;
+
+#undef __
+
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/deoptimizer-mips.cc b/chromium/v8/src/mips/deoptimizer-mips.cc
new file mode 100644
index 00000000000..57d3880edec
--- /dev/null
+++ b/chromium/v8/src/mips/deoptimizer-mips.cc
@@ -0,0 +1,627 @@
+
+// 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 "codegen.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+#include "safepoint-table.h"
+
+namespace v8 {
+namespace internal {
+
+
+int Deoptimizer::patch_size() {
+ const int kCallInstructionSizeInWords = 4;
+ return kCallInstructionSizeInWords * Assembler::kInstrSize;
+}
+
+
+void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* 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(isolate, i, LAZY);
+ int call_size_in_bytes = MacroAssembler::CallSize(deopt_entry,
+ RelocInfo::NONE32);
+ 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::NONE32);
+ 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
+ }
+}
+
+
+// This structure comes from FullCodeGenerator::EmitBackEdgeBookkeeping.
+// The back edge bookkeeping code matches the pattern:
+//
+// sltu at, sp, t0 / slt at, a3, zero_reg (in case of count based interrupts)
+// beq at, zero_reg, ok
+// lui t9, <interrupt stub address> upper
+// ori t9, <interrupt stub address> lower
+// jalr t9
+// nop
+// ok-label ----- pc_after points here
+//
+// We patch 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
+// ok-label ----- pc_after points here
+
+void Deoptimizer::PatchInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ ASSERT(!InterruptCodeIsPatched(unoptimized_code,
+ pc_after,
+ interrupt_code,
+ replacement_code));
+ static const int kInstrSize = Assembler::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.
+ Assembler::set_target_address_at(pc_after - 4 * kInstrSize,
+ replacement_code->entry());
+
+ unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
+ unoptimized_code, pc_after - 4 * kInstrSize, replacement_code);
+}
+
+
+void Deoptimizer::RevertInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ ASSERT(InterruptCodeIsPatched(unoptimized_code,
+ pc_after,
+ interrupt_code,
+ replacement_code));
+ static const int kInstrSize = Assembler::kInstrSize;
+ // Restore the sltu instruction so beq can be taken again.
+ CodePatcher patcher(pc_after - 6 * kInstrSize, 1);
+ patcher.masm()->slt(at, a3, zero_reg);
+ // Restore the original call address.
+ Assembler::set_target_address_at(pc_after - 4 * kInstrSize,
+ interrupt_code->entry());
+
+ interrupt_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
+ unoptimized_code, pc_after - 4 * kInstrSize, interrupt_code);
+}
+
+
+#ifdef DEBUG
+bool Deoptimizer::InterruptCodeIsPatched(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ static const int kInstrSize = Assembler::kInstrSize;
+ ASSERT(Assembler::IsBeq(Assembler::instr_at(pc_after - 5 * kInstrSize)));
+ if (Assembler::IsAddImmediate(
+ Assembler::instr_at(pc_after - 6 * kInstrSize))) {
+ ASSERT(reinterpret_cast<uint32_t>(
+ Assembler::target_address_at(pc_after - 4 * kInstrSize)) ==
+ reinterpret_cast<uint32_t>(replacement_code->entry()));
+ return true;
+ } else {
+ ASSERT(reinterpret_cast<uint32_t>(
+ Assembler::target_address_at(pc_after - 4 * kInstrSize)) ==
+ reinterpret_cast<uint32_t>(interrupt_code->entry()));
+ return false;
+ }
+}
+#endif // DEBUG
+
+
+static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) {
+ ByteArray* translations = data->TranslationByteArray();
+ int length = data->DeoptCount();
+ for (int i = 0; i < length; i++) {
+ if (data->AstId(i) == 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() {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ compiled_code_->deoptimization_data());
+ unsigned ast_id = data->OsrAstId()->value();
+
+ int bailout_id = LookupBailoutId(data, BailoutId(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);
+ int closure_id = iterator.Next();
+ USE(closure_id);
+ ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
+ 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 = compiled_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_));
+ PrintFunctionName();
+ 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>(
+ compiled_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_));
+ PrintFunctionName();
+ PrintF(" => pc=0x%0x]\n", output_[0]->GetPc());
+ }
+}
+
+
+void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
+ // 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::NumAllocatableRegisters(); 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));
+ }
+}
+
+
+void Deoptimizer::SetPlatformCompiledStubRegisters(
+ FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
+ ApiFunction function(descriptor->deoptimization_handler_);
+ ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
+ intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
+ int params = descriptor->register_param_count_;
+ if (descriptor->stack_parameter_count_ != NULL) {
+ params++;
+ }
+ output_frame->SetRegister(s0.code(), params);
+ output_frame->SetRegister(s1.code(), (params - 1) * kPointerSize);
+ output_frame->SetRegister(s2.code(), handler);
+}
+
+
+void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
+ for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) {
+ double double_value = input_->GetDoubleRegister(i);
+ output_frame->SetDoubleRegister(i, double_value);
+ }
+}
+
+
+bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
+ // There is no dynamic alignment padding on MIPS in the input frame.
+ return false;
+}
+
+
+#define __ masm()->
+
+
+// This code tries to be close to ia32 code so that any changes can be
+// easily ported.
+void Deoptimizer::EntryGenerator::Generate() {
+ GeneratePrologue();
+
+ // 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::kMaxNumAllocatableRegisters;
+
+ // Save all FPU registers before messing with them.
+ __ Subu(sp, sp, Operand(kDoubleRegsSize));
+ for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++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 (a3) (return
+ // address for lazy deoptimization) and compute the fp-to-sp delta in
+ // register t0.
+ __ mov(a3, ra);
+ // Correct one word for bailout id.
+ __ Addu(t0, sp, Operand(kSavedRegistersAreaSize + (1 * 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(isolate())));
+ __ 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));
+ }
+ }
+
+ int double_regs_offset = FrameDescription::double_registers_offset();
+ // Copy FPU registers to
+ // double_registers_[DoubleRegister::kNumAllocatableRegisters]
+ for (int i = 0; i < FPURegister::NumAllocatableRegisters(); ++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 and the saved registers from the stack.
+ __ Addu(sp, sp, Operand(kSavedRegistersAreaSize + (1 * 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;
+ Label pop_loop_header;
+ __ Branch(&pop_loop_header);
+ __ bind(&pop_loop);
+ __ pop(t0);
+ __ sw(t0, MemOperand(a3, 0));
+ __ addiu(a3, a3, sizeof(uint32_t));
+ __ bind(&pop_loop_header);
+ __ Branch(&pop_loop, ne, a2, Operand(sp));
+
+ // 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_header, inner_loop_header;
+ // Outer loop state: t0 = current "FrameDescription** output_",
+ // a1 = one past the last FrameDescription**.
+ __ lw(a1, MemOperand(a0, Deoptimizer::output_count_offset()));
+ __ lw(t0, MemOperand(a0, Deoptimizer::output_offset())); // t0 is output_.
+ __ sll(a1, a1, kPointerSizeLog2); // Count to offset.
+ __ addu(a1, t0, a1); // a1 = one past the last FrameDescription**.
+ __ jmp(&outer_loop_header);
+ __ bind(&outer_push_loop);
+ // Inner loop state: a2 = current FrameDescription*, a3 = loop index.
+ __ lw(a2, MemOperand(t0, 0)); // output_[ix]
+ __ lw(a3, MemOperand(a2, FrameDescription::frame_size_offset()));
+ __ jmp(&inner_loop_header);
+ __ 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);
+ __ bind(&inner_loop_header);
+ __ Branch(&inner_push_loop, ne, a3, Operand(zero_reg));
+
+ __ Addu(t0, t0, Operand(kPointerSize));
+ __ bind(&outer_loop_header);
+ __ Branch(&outer_push_loop, lt, t0, Operand(a1));
+
+ __ lw(a1, MemOperand(a0, Deoptimizer::input_offset()));
+ for (int i = 0; i < FPURegister::kMaxNumAllocatableRegisters; ++i) {
+ const FPURegister fpu_reg = FPURegister::FromAllocationIndex(i);
+ int src_offset = i * kDoubleSize + double_regs_offset;
+ __ ldc1(fpu_reg, MemOperand(a1, src_offset));
+ }
+
+ // 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.");
+}
+
+
+// Maximum size of a table entry generated below.
+const int Deoptimizer::table_entry_size_ = 7 * Assembler::kInstrSize;
+
+void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm());
+
+ // Create a sequence of deoptimization entries.
+ // Note that registers are still live when jumping to an entry.
+ Label table_start;
+ __ bind(&table_start);
+ for (int i = 0; i < count(); i++) {
+ Label start;
+ __ bind(&start);
+ __ addiu(sp, sp, -1 * kPointerSize);
+ // Jump over the remaining deopt entries (including this one).
+ // This code is always reached by calling Jump, which puts the target (label
+ // start) into t9.
+ const int remaining_entries = (count() - i) * table_entry_size_;
+ __ Addu(t9, t9, remaining_entries);
+ // 'at' was clobbered so we can only load the current entry value here.
+ __ li(at, i);
+ __ jr(t9); // Expose delay slot.
+ __ sw(at, MemOperand(sp, 0 * kPointerSize)); // In the delay slot.
+
+ // Pad the rest of the code.
+ while (table_entry_size_ > (masm()->SizeOfCodeGeneratedSince(&start))) {
+ __ nop();
+ }
+
+ ASSERT_EQ(table_entry_size_, masm()->SizeOfCodeGeneratedSince(&start));
+ }
+
+ ASSERT_EQ(masm()->SizeOfCodeGeneratedSince(&table_start),
+ count() * table_entry_size_);
+}
+
+
+void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
+ SetFrameSlot(offset, value);
+}
+
+
+void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
+ SetFrameSlot(offset, value);
+}
+
+
+#undef __
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/mips/disasm-mips.cc b/chromium/v8/src/mips/disasm-mips.cc
new file mode 100644
index 00000000000..708df39d24e
--- /dev/null
+++ b/chromium/v8/src/mips/disasm-mips.cc
@@ -0,0 +1,1064 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// A Disassembler object is used to disassemble a block of code instruction by
+// instruction. The default implementation of the NameConverter object can be
+// overriden to modify register names or to do symbol lookup on addresses.
+//
+// The example below will disassemble a block of code and print it to stdout.
+//
+// NameConverter converter;
+// Disassembler d(converter);
+// for (byte* pc = begin; pc < end;) {
+// v8::internal::EmbeddedVector<char, 256> buffer;
+// byte* prev_pc = pc;
+// pc += d.InstructionDecode(buffer, pc);
+// printf("%p %08x %s\n",
+// prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer);
+// }
+//
+// The Disassembler class also has a convenience method to disassemble a block
+// of code into a FILE*, meaning that the above functionality could also be
+// achieved by just calling Disassembler::Disassemble(stdout, begin, end);
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef WIN32
+#include <stdint.h>
+#endif
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "mips/constants-mips.h"
+#include "disasm.h"
+#include "macro-assembler.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+//------------------------------------------------------------------------------
+
+// Decoder decodes and disassembles instructions into an output buffer.
+// It uses the converter to convert register names and call destinations into
+// more informative description.
+class Decoder {
+ public:
+ Decoder(const disasm::NameConverter& converter,
+ v8::internal::Vector<char> out_buffer)
+ : converter_(converter),
+ out_buffer_(out_buffer),
+ out_buffer_pos_(0) {
+ out_buffer_[out_buffer_pos_] = '\0';
+ }
+
+ ~Decoder() {}
+
+ // Writes one disassembled instruction into 'buffer' (0-terminated).
+ // Returns the length of the disassembled machine instruction in bytes.
+ int InstructionDecode(byte* instruction);
+
+ private:
+ // Bottleneck functions to print into the out_buffer.
+ void PrintChar(const char ch);
+ void Print(const char* str);
+
+ // Printing of common values.
+ void PrintRegister(int reg);
+ void PrintFPURegister(int freg);
+ void PrintRs(Instruction* instr);
+ void PrintRt(Instruction* instr);
+ void PrintRd(Instruction* instr);
+ void PrintFs(Instruction* instr);
+ void PrintFt(Instruction* instr);
+ void PrintFd(Instruction* instr);
+ void PrintSa(Instruction* instr);
+ void PrintSd(Instruction* instr);
+ void PrintSs1(Instruction* instr);
+ void PrintSs2(Instruction* instr);
+ void PrintBc(Instruction* instr);
+ void PrintCc(Instruction* instr);
+ void PrintFunction(Instruction* instr);
+ void PrintSecondaryField(Instruction* instr);
+ void PrintUImm16(Instruction* instr);
+ void PrintSImm16(Instruction* instr);
+ void PrintXImm16(Instruction* instr);
+ void PrintXImm26(Instruction* instr);
+ void PrintCode(Instruction* instr); // For break and trap instructions.
+ // Printing of instruction name.
+ void PrintInstructionName(Instruction* instr);
+
+ // Handle formatting of instructions and their options.
+ int FormatRegister(Instruction* instr, const char* option);
+ int FormatFPURegister(Instruction* instr, const char* option);
+ int FormatOption(Instruction* instr, const char* option);
+ void Format(Instruction* instr, const char* format);
+ void Unknown(Instruction* instr);
+
+ // Each of these functions decodes one particular instruction type.
+ void DecodeTypeRegister(Instruction* instr);
+ void DecodeTypeImmediate(Instruction* instr);
+ void DecodeTypeJump(Instruction* instr);
+
+ const disasm::NameConverter& converter_;
+ v8::internal::Vector<char> out_buffer_;
+ int out_buffer_pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(Decoder);
+};
+
+
+// Support for assertions in the Decoder formatting functions.
+#define STRING_STARTS_WITH(string, compare_string) \
+ (strncmp(string, compare_string, strlen(compare_string)) == 0)
+
+
+// Append the ch to the output buffer.
+void Decoder::PrintChar(const char ch) {
+ out_buffer_[out_buffer_pos_++] = ch;
+}
+
+
+// Append the str to the output buffer.
+void Decoder::Print(const char* str) {
+ char cur = *str++;
+ while (cur != '\0' && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
+ PrintChar(cur);
+ cur = *str++;
+ }
+ out_buffer_[out_buffer_pos_] = 0;
+}
+
+
+// Print the register name according to the active name converter.
+void Decoder::PrintRegister(int reg) {
+ Print(converter_.NameOfCPURegister(reg));
+}
+
+
+void Decoder::PrintRs(Instruction* instr) {
+ int reg = instr->RsValue();
+ PrintRegister(reg);
+}
+
+
+void Decoder::PrintRt(Instruction* instr) {
+ int reg = instr->RtValue();
+ PrintRegister(reg);
+}
+
+
+void Decoder::PrintRd(Instruction* instr) {
+ int reg = instr->RdValue();
+ PrintRegister(reg);
+}
+
+
+// Print the FPUregister name according to the active name converter.
+void Decoder::PrintFPURegister(int freg) {
+ Print(converter_.NameOfXMMRegister(freg));
+}
+
+
+void Decoder::PrintFs(Instruction* instr) {
+ int freg = instr->RsValue();
+ PrintFPURegister(freg);
+}
+
+
+void Decoder::PrintFt(Instruction* instr) {
+ int freg = instr->RtValue();
+ PrintFPURegister(freg);
+}
+
+
+void Decoder::PrintFd(Instruction* instr) {
+ int freg = instr->RdValue();
+ PrintFPURegister(freg);
+}
+
+
+// Print the integer value of the sa field.
+void Decoder::PrintSa(Instruction* instr) {
+ int sa = instr->SaValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", sa);
+}
+
+
+// Print the integer value of the rd field, when it is not used as reg.
+void Decoder::PrintSd(Instruction* instr) {
+ int sd = instr->RdValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", sd);
+}
+
+
+// Print the integer value of the rd field, when used as 'ext' size.
+void Decoder::PrintSs1(Instruction* instr) {
+ int ss = instr->RdValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", ss + 1);
+}
+
+
+// Print the integer value of the rd field, when used as 'ins' size.
+void Decoder::PrintSs2(Instruction* instr) {
+ int ss = instr->RdValue();
+ int pos = instr->SaValue();
+ out_buffer_pos_ +=
+ OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", ss - pos + 1);
+}
+
+
+// Print the integer value of the cc field for the bc1t/f instructions.
+void Decoder::PrintBc(Instruction* instr) {
+ int cc = instr->FBccValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", cc);
+}
+
+
+// Print the integer value of the cc field for the FP compare instructions.
+void Decoder::PrintCc(Instruction* instr) {
+ int cc = instr->FCccValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "cc(%d)", cc);
+}
+
+
+// Print 16-bit unsigned immediate value.
+void Decoder::PrintUImm16(Instruction* instr) {
+ int32_t imm = instr->Imm16Value();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%u", imm);
+}
+
+
+// Print 16-bit signed immediate value.
+void Decoder::PrintSImm16(Instruction* instr) {
+ int32_t imm = ((instr->Imm16Value()) << 16) >> 16;
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
+}
+
+
+// Print 16-bit hexa immediate value.
+void Decoder::PrintXImm16(Instruction* instr) {
+ int32_t imm = instr->Imm16Value();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
+}
+
+
+// Print 26-bit immediate value.
+void Decoder::PrintXImm26(Instruction* instr) {
+ uint32_t imm = instr->Imm26Value() << kImmFieldShift;
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
+}
+
+
+// Print 26-bit immediate value.
+void Decoder::PrintCode(Instruction* instr) {
+ if (instr->OpcodeFieldRaw() != SPECIAL)
+ return; // Not a break or trap instruction.
+ switch (instr->FunctionFieldRaw()) {
+ case BREAK: {
+ int32_t code = instr->Bits(25, 6);
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "0x%05x (%d)", code, code);
+ break;
+ }
+ case TGE:
+ case TGEU:
+ case TLT:
+ case TLTU:
+ case TEQ:
+ case TNE: {
+ int32_t code = instr->Bits(15, 6);
+ out_buffer_pos_ +=
+ OS::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%03x", code);
+ break;
+ }
+ default: // Not a break or trap instruction.
+ break;
+ };
+}
+
+
+// Printing of instruction name.
+void Decoder::PrintInstructionName(Instruction* instr) {
+}
+
+
+// Handle all register based formatting in this function to reduce the
+// complexity of FormatOption.
+int Decoder::FormatRegister(Instruction* instr, const char* format) {
+ ASSERT(format[0] == 'r');
+ if (format[1] == 's') { // 'rs: Rs register.
+ int reg = instr->RsValue();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 't') { // 'rt: rt register.
+ int reg = instr->RtValue();
+ PrintRegister(reg);
+ return 2;
+ } else if (format[1] == 'd') { // 'rd: rd register.
+ int reg = instr->RdValue();
+ PrintRegister(reg);
+ return 2;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+// Handle all FPUregister based formatting in this function to reduce the
+// complexity of FormatOption.
+int Decoder::FormatFPURegister(Instruction* instr, const char* format) {
+ ASSERT(format[0] == 'f');
+ if (format[1] == 's') { // 'fs: fs register.
+ int reg = instr->FsValue();
+ PrintFPURegister(reg);
+ return 2;
+ } else if (format[1] == 't') { // 'ft: ft register.
+ int reg = instr->FtValue();
+ PrintFPURegister(reg);
+ return 2;
+ } else if (format[1] == 'd') { // 'fd: fd register.
+ int reg = instr->FdValue();
+ PrintFPURegister(reg);
+ return 2;
+ } else if (format[1] == 'r') { // 'fr: fr register.
+ int reg = instr->FrValue();
+ PrintFPURegister(reg);
+ return 2;
+ }
+ UNREACHABLE();
+ return -1;
+}
+
+
+// FormatOption takes a formatting string and interprets it based on
+// the current instructions. The format string points to the first
+// character of the option string (the option escape has already been
+// consumed by the caller.) FormatOption returns the number of
+// characters that were consumed from the formatting string.
+int Decoder::FormatOption(Instruction* instr, const char* format) {
+ switch (format[0]) {
+ case 'c': { // 'code for break or trap instructions.
+ ASSERT(STRING_STARTS_WITH(format, "code"));
+ PrintCode(instr);
+ return 4;
+ }
+ case 'i': { // 'imm16u or 'imm26.
+ if (format[3] == '1') {
+ ASSERT(STRING_STARTS_WITH(format, "imm16"));
+ if (format[5] == 's') {
+ ASSERT(STRING_STARTS_WITH(format, "imm16s"));
+ PrintSImm16(instr);
+ } else if (format[5] == 'u') {
+ ASSERT(STRING_STARTS_WITH(format, "imm16u"));
+ PrintSImm16(instr);
+ } else {
+ ASSERT(STRING_STARTS_WITH(format, "imm16x"));
+ PrintXImm16(instr);
+ }
+ return 6;
+ } else {
+ ASSERT(STRING_STARTS_WITH(format, "imm26x"));
+ PrintXImm26(instr);
+ return 6;
+ }
+ }
+ case 'r': { // 'r: registers.
+ return FormatRegister(instr, format);
+ }
+ case 'f': { // 'f: FPUregisters.
+ return FormatFPURegister(instr, format);
+ }
+ case 's': { // 'sa.
+ switch (format[1]) {
+ case 'a': {
+ ASSERT(STRING_STARTS_WITH(format, "sa"));
+ PrintSa(instr);
+ return 2;
+ }
+ case 'd': {
+ ASSERT(STRING_STARTS_WITH(format, "sd"));
+ PrintSd(instr);
+ return 2;
+ }
+ case 's': {
+ if (format[2] == '1') {
+ ASSERT(STRING_STARTS_WITH(format, "ss1")); /* ext size */
+ PrintSs1(instr);
+ return 3;
+ } else {
+ ASSERT(STRING_STARTS_WITH(format, "ss2")); /* ins size */
+ PrintSs2(instr);
+ return 3;
+ }
+ }
+ }
+ }
+ case 'b': { // 'bc - Special for bc1 cc field.
+ ASSERT(STRING_STARTS_WITH(format, "bc"));
+ PrintBc(instr);
+ return 2;
+ }
+ case 'C': { // 'Cc - Special for c.xx.d cc field.
+ ASSERT(STRING_STARTS_WITH(format, "Cc"));
+ PrintCc(instr);
+ return 2;
+ }
+ };
+ UNREACHABLE();
+ return -1;
+}
+
+
+// Format takes a formatting string for a whole instruction and prints it into
+// the output buffer. All escaped options are handed to FormatOption to be
+// parsed further.
+void Decoder::Format(Instruction* instr, const char* format) {
+ char cur = *format++;
+ while ((cur != 0) && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
+ if (cur == '\'') { // Single quote is used as the formatting escape.
+ format += FormatOption(instr, format);
+ } else {
+ out_buffer_[out_buffer_pos_++] = cur;
+ }
+ cur = *format++;
+ }
+ out_buffer_[out_buffer_pos_] = '\0';
+}
+
+
+// For currently unimplemented decodings the disassembler calls Unknown(instr)
+// which will just print "unknown" of the instruction bits.
+void Decoder::Unknown(Instruction* instr) {
+ Format(instr, "unknown");
+}
+
+
+void Decoder::DecodeTypeRegister(Instruction* instr) {
+ switch (instr->OpcodeFieldRaw()) {
+ case COP1: // Coprocessor instructions.
+ switch (instr->RsFieldRaw()) {
+ case BC1: // bc1 handled in DecodeTypeImmediate.
+ UNREACHABLE();
+ break;
+ case MFC1:
+ Format(instr, "mfc1 'rt, 'fs");
+ break;
+ case MFHC1:
+ Format(instr, "mfhc1 'rt, 'fs");
+ break;
+ case MTC1:
+ Format(instr, "mtc1 'rt, 'fs");
+ break;
+ // These are called "fs" too, although they are not FPU registers.
+ case CTC1:
+ Format(instr, "ctc1 'rt, 'fs");
+ break;
+ case CFC1:
+ Format(instr, "cfc1 'rt, 'fs");
+ break;
+ case MTHC1:
+ Format(instr, "mthc1 'rt, 'fs");
+ break;
+ case D:
+ switch (instr->FunctionFieldRaw()) {
+ case ADD_D:
+ Format(instr, "add.d 'fd, 'fs, 'ft");
+ break;
+ case SUB_D:
+ Format(instr, "sub.d 'fd, 'fs, 'ft");
+ break;
+ case MUL_D:
+ Format(instr, "mul.d 'fd, 'fs, 'ft");
+ break;
+ case DIV_D:
+ Format(instr, "div.d 'fd, 'fs, 'ft");
+ break;
+ case ABS_D:
+ Format(instr, "abs.d 'fd, 'fs");
+ break;
+ case MOV_D:
+ Format(instr, "mov.d 'fd, 'fs");
+ break;
+ case NEG_D:
+ Format(instr, "neg.d 'fd, 'fs");
+ break;
+ case SQRT_D:
+ Format(instr, "sqrt.d 'fd, 'fs");
+ break;
+ case CVT_W_D:
+ Format(instr, "cvt.w.d 'fd, 'fs");
+ break;
+ case CVT_L_D: {
+ if (kArchVariant == kMips32r2) {
+ Format(instr, "cvt.l.d 'fd, 'fs");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ case TRUNC_W_D:
+ Format(instr, "trunc.w.d 'fd, 'fs");
+ break;
+ case TRUNC_L_D: {
+ if (kArchVariant == kMips32r2) {
+ Format(instr, "trunc.l.d 'fd, 'fs");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ case ROUND_W_D:
+ Format(instr, "round.w.d 'fd, 'fs");
+ break;
+ case FLOOR_W_D:
+ Format(instr, "floor.w.d 'fd, 'fs");
+ break;
+ case CEIL_W_D:
+ Format(instr, "ceil.w.d 'fd, 'fs");
+ break;
+ case CVT_S_D:
+ Format(instr, "cvt.s.d 'fd, 'fs");
+ break;
+ case C_F_D:
+ Format(instr, "c.f.d 'fs, 'ft, 'Cc");
+ break;
+ case C_UN_D:
+ Format(instr, "c.un.d 'fs, 'ft, 'Cc");
+ break;
+ case C_EQ_D:
+ Format(instr, "c.eq.d 'fs, 'ft, 'Cc");
+ break;
+ case C_UEQ_D:
+ Format(instr, "c.ueq.d 'fs, 'ft, 'Cc");
+ break;
+ case C_OLT_D:
+ Format(instr, "c.olt.d 'fs, 'ft, 'Cc");
+ break;
+ case C_ULT_D:
+ Format(instr, "c.ult.d 'fs, 'ft, 'Cc");
+ break;
+ case C_OLE_D:
+ Format(instr, "c.ole.d 'fs, 'ft, 'Cc");
+ break;
+ case C_ULE_D:
+ Format(instr, "c.ule.d 'fs, 'ft, 'Cc");
+ break;
+ default:
+ Format(instr, "unknown.cop1.d");
+ break;
+ }
+ break;
+ case S:
+ UNIMPLEMENTED_MIPS();
+ break;
+ case W:
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_S_W: // Convert word to float (single).
+ Format(instr, "cvt.s.w 'fd, 'fs");
+ break;
+ case CVT_D_W: // Convert word to double.
+ Format(instr, "cvt.d.w 'fd, 'fs");
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case L:
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_L: {
+ if (kArchVariant == kMips32r2) {
+ Format(instr, "cvt.d.l 'fd, 'fs");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ case CVT_S_L: {
+ if (kArchVariant == kMips32r2) {
+ Format(instr, "cvt.s.l 'fd, 'fs");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case PS:
+ UNIMPLEMENTED_MIPS();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case COP1X:
+ switch (instr->FunctionFieldRaw()) {
+ case MADD_D:
+ Format(instr, "madd.d 'fd, 'fr, 'fs, 'ft");
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break;
+ case SPECIAL:
+ switch (instr->FunctionFieldRaw()) {
+ case JR:
+ Format(instr, "jr 'rs");
+ break;
+ case JALR:
+ Format(instr, "jalr 'rs");
+ break;
+ case SLL:
+ if ( 0x0 == static_cast<int>(instr->InstructionBits()))
+ Format(instr, "nop");
+ else
+ Format(instr, "sll 'rd, 'rt, 'sa");
+ break;
+ case SRL:
+ if (instr->RsValue() == 0) {
+ Format(instr, "srl 'rd, 'rt, 'sa");
+ } else {
+ if (kArchVariant == kMips32r2) {
+ Format(instr, "rotr 'rd, 'rt, 'sa");
+ } else {
+ Unknown(instr);
+ }
+ }
+ break;
+ case SRA:
+ Format(instr, "sra 'rd, 'rt, 'sa");
+ break;
+ case SLLV:
+ Format(instr, "sllv 'rd, 'rt, 'rs");
+ break;
+ case SRLV:
+ if (instr->SaValue() == 0) {
+ Format(instr, "srlv 'rd, 'rt, 'rs");
+ } else {
+ if (kArchVariant == kMips32r2) {
+ Format(instr, "rotrv 'rd, 'rt, 'rs");
+ } else {
+ Unknown(instr);
+ }
+ }
+ break;
+ case SRAV:
+ Format(instr, "srav 'rd, 'rt, 'rs");
+ break;
+ case MFHI:
+ Format(instr, "mfhi 'rd");
+ break;
+ case MFLO:
+ Format(instr, "mflo 'rd");
+ break;
+ case MULT:
+ Format(instr, "mult 'rs, 'rt");
+ break;
+ case MULTU:
+ Format(instr, "multu 'rs, 'rt");
+ break;
+ case DIV:
+ Format(instr, "div 'rs, 'rt");
+ break;
+ case DIVU:
+ Format(instr, "divu 'rs, 'rt");
+ break;
+ case ADD:
+ Format(instr, "add 'rd, 'rs, 'rt");
+ break;
+ case ADDU:
+ Format(instr, "addu 'rd, 'rs, 'rt");
+ break;
+ case SUB:
+ Format(instr, "sub 'rd, 'rs, 'rt");
+ break;
+ case SUBU:
+ Format(instr, "subu 'rd, 'rs, 'rt");
+ break;
+ case AND:
+ Format(instr, "and 'rd, 'rs, 'rt");
+ break;
+ case OR:
+ if (0 == instr->RsValue()) {
+ Format(instr, "mov 'rd, 'rt");
+ } else if (0 == instr->RtValue()) {
+ Format(instr, "mov 'rd, 'rs");
+ } else {
+ Format(instr, "or 'rd, 'rs, 'rt");
+ }
+ break;
+ case XOR:
+ Format(instr, "xor 'rd, 'rs, 'rt");
+ break;
+ case NOR:
+ Format(instr, "nor 'rd, 'rs, 'rt");
+ break;
+ case SLT:
+ Format(instr, "slt 'rd, 'rs, 'rt");
+ break;
+ case SLTU:
+ Format(instr, "sltu 'rd, 'rs, 'rt");
+ break;
+ case BREAK:
+ Format(instr, "break, code: 'code");
+ break;
+ case TGE:
+ Format(instr, "tge 'rs, 'rt, code: 'code");
+ break;
+ case TGEU:
+ Format(instr, "tgeu 'rs, 'rt, code: 'code");
+ break;
+ case TLT:
+ Format(instr, "tlt 'rs, 'rt, code: 'code");
+ break;
+ case TLTU:
+ Format(instr, "tltu 'rs, 'rt, code: 'code");
+ break;
+ case TEQ:
+ Format(instr, "teq 'rs, 'rt, code: 'code");
+ break;
+ case TNE:
+ Format(instr, "tne 'rs, 'rt, code: 'code");
+ break;
+ case MOVZ:
+ Format(instr, "movz 'rd, 'rs, 'rt");
+ break;
+ case MOVN:
+ Format(instr, "movn 'rd, 'rs, 'rt");
+ break;
+ case MOVCI:
+ if (instr->Bit(16)) {
+ Format(instr, "movt 'rd, 'rs, 'bc");
+ } else {
+ Format(instr, "movf 'rd, 'rs, 'bc");
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case SPECIAL2:
+ switch (instr->FunctionFieldRaw()) {
+ case MUL:
+ Format(instr, "mul 'rd, 'rs, 'rt");
+ break;
+ case CLZ:
+ Format(instr, "clz 'rd, 'rs");
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case SPECIAL3:
+ switch (instr->FunctionFieldRaw()) {
+ case INS: {
+ if (kArchVariant == kMips32r2) {
+ Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ case EXT: {
+ if (kArchVariant == kMips32r2) {
+ Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
+ } else {
+ Unknown(instr);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void Decoder::DecodeTypeImmediate(Instruction* instr) {
+ switch (instr->OpcodeFieldRaw()) {
+ // ------------- REGIMM class.
+ case COP1:
+ switch (instr->RsFieldRaw()) {
+ case BC1:
+ if (instr->FBtrueValue()) {
+ Format(instr, "bc1t 'bc, 'imm16u");
+ } else {
+ Format(instr, "bc1f 'bc, 'imm16u");
+ }
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break; // Case COP1.
+ case REGIMM:
+ switch (instr->RtFieldRaw()) {
+ case BLTZ:
+ Format(instr, "bltz 'rs, 'imm16u");
+ break;
+ case BLTZAL:
+ Format(instr, "bltzal 'rs, 'imm16u");
+ break;
+ case BGEZ:
+ Format(instr, "bgez 'rs, 'imm16u");
+ break;
+ case BGEZAL:
+ Format(instr, "bgezal 'rs, 'imm16u");
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break; // Case REGIMM.
+ // ------------- Branch instructions.
+ case BEQ:
+ Format(instr, "beq 'rs, 'rt, 'imm16u");
+ break;
+ case BNE:
+ Format(instr, "bne 'rs, 'rt, 'imm16u");
+ break;
+ case BLEZ:
+ Format(instr, "blez 'rs, 'imm16u");
+ break;
+ case BGTZ:
+ Format(instr, "bgtz 'rs, 'imm16u");
+ break;
+ // ------------- Arithmetic instructions.
+ case ADDI:
+ Format(instr, "addi 'rt, 'rs, 'imm16s");
+ break;
+ case ADDIU:
+ Format(instr, "addiu 'rt, 'rs, 'imm16s");
+ break;
+ case SLTI:
+ Format(instr, "slti 'rt, 'rs, 'imm16s");
+ break;
+ case SLTIU:
+ Format(instr, "sltiu 'rt, 'rs, 'imm16u");
+ break;
+ case ANDI:
+ Format(instr, "andi 'rt, 'rs, 'imm16x");
+ break;
+ case ORI:
+ Format(instr, "ori 'rt, 'rs, 'imm16x");
+ break;
+ case XORI:
+ Format(instr, "xori 'rt, 'rs, 'imm16x");
+ break;
+ case LUI:
+ Format(instr, "lui 'rt, 'imm16x");
+ break;
+ // ------------- Memory instructions.
+ case LB:
+ Format(instr, "lb 'rt, 'imm16s('rs)");
+ break;
+ case LH:
+ Format(instr, "lh 'rt, 'imm16s('rs)");
+ break;
+ case LWL:
+ Format(instr, "lwl 'rt, 'imm16s('rs)");
+ break;
+ case LW:
+ Format(instr, "lw 'rt, 'imm16s('rs)");
+ break;
+ case LBU:
+ Format(instr, "lbu 'rt, 'imm16s('rs)");
+ break;
+ case LHU:
+ Format(instr, "lhu 'rt, 'imm16s('rs)");
+ break;
+ case LWR:
+ Format(instr, "lwr 'rt, 'imm16s('rs)");
+ break;
+ case SB:
+ Format(instr, "sb 'rt, 'imm16s('rs)");
+ break;
+ case SH:
+ Format(instr, "sh 'rt, 'imm16s('rs)");
+ break;
+ case SWL:
+ Format(instr, "swl 'rt, 'imm16s('rs)");
+ break;
+ case SW:
+ Format(instr, "sw 'rt, 'imm16s('rs)");
+ break;
+ case SWR:
+ Format(instr, "swr 'rt, 'imm16s('rs)");
+ break;
+ case LWC1:
+ Format(instr, "lwc1 'ft, 'imm16s('rs)");
+ break;
+ case LDC1:
+ Format(instr, "ldc1 'ft, 'imm16s('rs)");
+ break;
+ case SWC1:
+ Format(instr, "swc1 'ft, 'imm16s('rs)");
+ break;
+ case SDC1:
+ Format(instr, "sdc1 'ft, 'imm16s('rs)");
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ };
+}
+
+
+void Decoder::DecodeTypeJump(Instruction* instr) {
+ switch (instr->OpcodeFieldRaw()) {
+ case J:
+ Format(instr, "j 'imm26x");
+ break;
+ case JAL:
+ Format(instr, "jal 'imm26x");
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+// Disassemble the instruction at *instr_ptr into the output buffer.
+int Decoder::InstructionDecode(byte* instr_ptr) {
+ Instruction* instr = Instruction::At(instr_ptr);
+ // Print raw instruction bytes.
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%08x ",
+ instr->InstructionBits());
+ switch (instr->InstructionType()) {
+ case Instruction::kRegisterType: {
+ DecodeTypeRegister(instr);
+ break;
+ }
+ case Instruction::kImmediateType: {
+ DecodeTypeImmediate(instr);
+ break;
+ }
+ case Instruction::kJumpType: {
+ DecodeTypeJump(instr);
+ break;
+ }
+ default: {
+ Format(instr, "UNSUPPORTED");
+ UNSUPPORTED_MIPS();
+ }
+ }
+ return Instruction::kInstrSize;
+}
+
+
+} } // namespace v8::internal
+
+
+
+//------------------------------------------------------------------------------
+
+namespace disasm {
+
+const char* NameConverter::NameOfAddress(byte* addr) const {
+ v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
+ return tmp_buffer_.start();
+}
+
+
+const char* NameConverter::NameOfConstant(byte* addr) const {
+ return NameOfAddress(addr);
+}
+
+
+const char* NameConverter::NameOfCPURegister(int reg) const {
+ return v8::internal::Registers::Name(reg);
+}
+
+
+const char* NameConverter::NameOfXMMRegister(int reg) const {
+ return v8::internal::FPURegisters::Name(reg);
+}
+
+
+const char* NameConverter::NameOfByteCPURegister(int reg) const {
+ UNREACHABLE(); // MIPS does not have the concept of a byte register.
+ return "nobytereg";
+}
+
+
+const char* NameConverter::NameInCode(byte* addr) const {
+ // The default name converter is called for unknown code. So we will not try
+ // to access any memory.
+ return "";
+}
+
+
+//------------------------------------------------------------------------------
+
+Disassembler::Disassembler(const NameConverter& converter)
+ : converter_(converter) {}
+
+
+Disassembler::~Disassembler() {}
+
+
+int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
+ byte* instruction) {
+ v8::internal::Decoder d(converter_, buffer);
+ return d.InstructionDecode(instruction);
+}
+
+
+// The MIPS assembler does not currently use constant pools.
+int Disassembler::ConstantPoolSizeAt(byte* instruction) {
+ return -1;
+}
+
+
+void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
+ NameConverter converter;
+ Disassembler d(converter);
+ for (byte* pc = begin; pc < end;) {
+ v8::internal::EmbeddedVector<char, 128> buffer;
+ buffer[0] = '\0';
+ byte* prev_pc = pc;
+ pc += d.InstructionDecode(buffer, pc);
+ v8::internal::PrintF(f, "%p %08x %s\n",
+ prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer.start());
+ }
+}
+
+
+#undef UNSUPPORTED
+
+} // namespace disasm
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/frames-mips.cc b/chromium/v8/src/mips/frames-mips.cc
new file mode 100644
index 00000000000..1bd511654a1
--- /dev/null
+++ b/chromium/v8/src/mips/frames-mips.cc
@@ -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.
+
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "assembler.h"
+#include "assembler-mips.h"
+#include "assembler-mips-inl.h"
+#include "frames.h"
+
+namespace v8 {
+namespace internal {
+
+
+Register JavaScriptFrame::fp_register() { return v8::internal::fp; }
+Register JavaScriptFrame::context_register() { return cp; }
+
+
+Register StubFailureTrampolineFrame::fp_register() { return v8::internal::fp; }
+Register StubFailureTrampolineFrame::context_register() { return cp; }
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/frames-mips.h b/chromium/v8/src/mips/frames-mips.h
new file mode 100644
index 00000000000..437bf3a9f13
--- /dev/null
+++ b/chromium/v8/src/mips/frames-mips.h
@@ -0,0 +1,240 @@
+// 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_FRAMES_MIPS_H_
+#define V8_MIPS_FRAMES_MIPS_H_
+
+namespace v8 {
+namespace internal {
+
+// Register lists.
+// Note that the bit values must match those used in actual instruction
+// encoding.
+const int kNumRegs = 32;
+
+const RegList kJSCallerSaved =
+ 1 << 2 | // v0
+ 1 << 3 | // v1
+ 1 << 4 | // a0
+ 1 << 5 | // a1
+ 1 << 6 | // a2
+ 1 << 7 | // a3
+ 1 << 8 | // t0
+ 1 << 9 | // t1
+ 1 << 10 | // t2
+ 1 << 11 | // t3
+ 1 << 12 | // t4
+ 1 << 13 | // t5
+ 1 << 14 | // t6
+ 1 << 15; // t7
+
+const int kNumJSCallerSaved = 14;
+
+
+// Return the code of the n-th caller-saved register available to JavaScript
+// e.g. JSCallerSavedReg(0) returns a0.code() == 4.
+int JSCallerSavedCode(int n);
+
+
+// Callee-saved registers preserved when switching from C to JavaScript.
+const RegList kCalleeSaved =
+ 1 << 16 | // s0
+ 1 << 17 | // s1
+ 1 << 18 | // s2
+ 1 << 19 | // s3
+ 1 << 20 | // s4
+ 1 << 21 | // s5
+ 1 << 22 | // s6 (roots in Javascript code)
+ 1 << 23 | // s7 (cp in Javascript code)
+ 1 << 30; // fp/s8
+
+const int kNumCalleeSaved = 9;
+
+const RegList kCalleeSavedFPU =
+ 1 << 20 | // f20
+ 1 << 22 | // f22
+ 1 << 24 | // f24
+ 1 << 26 | // f26
+ 1 << 28 | // f28
+ 1 << 30; // f30
+
+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.
+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.
+const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved;
+const int kNumSafepointSavedRegisters =
+ kNumJSCallerSaved + kNumCalleeSaved;
+
+typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
+
+const int kUndefIndex = -1;
+// Map with indexes on stack that corresponds to codes of saved registers.
+const int kSafepointRegisterStackIndexMap[kNumRegs] = {
+ kUndefIndex, // zero_reg
+ kUndefIndex, // at
+ 0, // v0
+ 1, // v1
+ 2, // a0
+ 3, // a1
+ 4, // a2
+ 5, // a3
+ 6, // t0
+ 7, // t1
+ 8, // t2
+ 9, // t3
+ 10, // t4
+ 11, // t5
+ 12, // t6
+ 13, // t7
+ 14, // s0
+ 15, // s1
+ 16, // s2
+ 17, // s3
+ 18, // s4
+ 19, // s5
+ 20, // s6
+ 21, // s7
+ kUndefIndex, // t8
+ kUndefIndex, // t9
+ kUndefIndex, // k0
+ kUndefIndex, // k1
+ kUndefIndex, // gp
+ kUndefIndex, // sp
+ 22, // fp
+ kUndefIndex
+};
+
+
+// ----------------------------------------------------
+
+class EntryFrameConstants : public AllStatic {
+ public:
+ static const int kCallerFPOffset = -3 * kPointerSize;
+};
+
+
+class ExitFrameConstants : public AllStatic {
+ public:
+ // See some explanation in MacroAssembler::EnterExitFrame.
+ // This marks the top of the extra allocated stack space.
+ static const int kStackSpaceOffset = -3 * kPointerSize;
+
+ static const int kCodeOffset = -2 * kPointerSize;
+
+ static const int kSPOffset = -1 * kPointerSize;
+
+ // The caller fields are below the frame pointer on the stack.
+ static const int kCallerFPOffset = +0 * kPointerSize;
+ // The calling JS function is between FP and PC.
+ static const int kCallerPCOffset = +1 * kPointerSize;
+
+ // MIPS-specific: a pointer to the old sp to avoid unnecessary calculations.
+ static const int kCallerSPOffset = +2 * kPointerSize;
+
+ // FP-relative displacement of the caller's SP.
+ static const int kCallerSPDisplacement = +2 * kPointerSize;
+};
+
+
+class JavaScriptFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
+ static const int kLastParameterOffset = +2 * kPointerSize;
+ static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
+
+ // Caller SP-relative.
+ static const int kParam0Offset = -2 * kPointerSize;
+ static const int kReceiverOffset = -1 * kPointerSize;
+};
+
+
+class ArgumentsAdaptorFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
+};
+
+
+class ConstructFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kImplicitReceiverOffset = -6 * kPointerSize;
+ static const int kConstructorOffset = -5 * kPointerSize;
+ static const int kLengthOffset = -4 * kPointerSize;
+ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
+
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + 4 * kPointerSize;
+};
+
+
+class InternalFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
+};
+
+
+inline Object* JavaScriptFrame::function_slot_object() const {
+ const int offset = JavaScriptFrameConstants::kFunctionOffset;
+ return Memory::Object_at(fp() + offset);
+}
+
+
+inline void StackHandler::SetFp(Address slot, Address fp) {
+ Memory::Address_at(slot) = fp;
+}
+
+
+} } // namespace v8::internal
+
+#endif
diff --git a/chromium/v8/src/mips/full-codegen-mips.cc b/chromium/v8/src/mips/full-codegen-mips.cc
new file mode 100644
index 00000000000..b60502c9a5b
--- /dev/null
+++ b/chromium/v8/src/mips/full-codegen-mips.cc
@@ -0,0 +1,4933 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+// Note on Mips implementation:
+//
+// The result_register() for mips is the 'v0' register, which is defined
+// by the ABI to contain function return values. However, the first
+// parameter to a function is defined to be 'a0'. So there are many
+// places where we have to move a previous result in v0 to a0 for the
+// next call: mov(a0, v0). This is not needed on the other architectures.
+
+#include "code-stubs.h"
+#include "codegen.h"
+#include "compiler.h"
+#include "debug.h"
+#include "full-codegen.h"
+#include "isolate-inl.h"
+#include "parser.h"
+#include "scopes.h"
+#include "stub-cache.h"
+
+#include "mips/code-stubs-mips.h"
+#include "mips/macro-assembler-mips.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm_)
+
+
+// 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 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) {
+#ifdef DEBUG
+ info_emitted_ = false;
+#endif
+ }
+
+ ~JumpPatchSite() {
+ ASSERT(patch_site_.is_bound() == info_emitted_);
+ }
+
+ // When initially emitting this ensure that a jump is always generated to skip
+ // the inlined smi code.
+ void EmitJumpIfNotSmi(Register reg, Label* target) {
+ ASSERT(!patch_site_.is_bound() && !info_emitted_);
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
+ __ bind(&patch_site_);
+ __ andi(at, reg, 0);
+ // Always taken before patched.
+ __ Branch(target, eq, at, Operand(zero_reg));
+ }
+
+ // When initially emitting this ensure that a jump is never generated to skip
+ // the inlined smi code.
+ void EmitJumpIfSmi(Register reg, Label* target) {
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
+ ASSERT(!patch_site_.is_bound() && !info_emitted_);
+ __ bind(&patch_site_);
+ __ andi(at, reg, 0);
+ // Never taken before patched.
+ __ Branch(target, ne, at, Operand(zero_reg));
+ }
+
+ void EmitPatchInfo() {
+ 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(zero_reg, reg, delta_to_patch_site % kImm16Mask);
+#ifdef DEBUG
+ info_emitted_ = true;
+#endif
+ } else {
+ __ nop(); // Signals no inlined code.
+ }
+ }
+
+ private:
+ MacroAssembler* masm_;
+ Label patch_site_;
+#ifdef DEBUG
+ bool info_emitted_;
+#endif
+};
+
+
+// Generate code for a JS function. On entry to the function the receiver
+// and arguments have been pushed on the stack left to right. The actual
+// argument count matches the formal parameter count expected by the
+// function.
+//
+// The live registers are:
+// 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
+// o ra: return address
+//
+// The function builds a JS frame. Please see JavaScriptFrameConstants in
+// frames-mips.h for its layout.
+void FullCodeGenerator::Generate() {
+ CompilationInfo* info = info_;
+ handler_table_ =
+ isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
+ profiling_counter_ = isolate()->factory()->NewCell(
+ Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate()));
+ SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info->function()->name()->IsUtf8EqualTo(CStrVector(FLAG_stop_at))) {
+ __ stop("stop-at");
+ }
+#endif
+
+ // Strict mode functions and builtins need to replace the receiver
+ // 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_classic_mode() || info->is_native()) {
+ Label ok;
+ __ Branch(&ok, eq, t1, Operand(zero_reg));
+ int receiver_offset = info->scope()->num_parameters() * kPointerSize;
+ __ LoadRoot(a2, Heap::kUndefinedValueRootIndex);
+ __ sw(a2, MemOperand(sp, receiver_offset));
+ __ 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);
+
+ info->set_prologue_offset(masm_->pc_offset());
+ // The following three instructions must remain together and unmodified for
+ // code aging to work properly.
+ __ Push(ra, fp, cp, a1);
+ __ nop(Assembler::CODE_AGE_SEQUENCE_NOP);
+ // Adjust fp to point to caller's fp.
+ __ Addu(fp, sp, Operand(2 * kPointerSize));
+ info->AddNoFrameRange(0, masm_->pc_offset());
+
+ { Comment cmnt(masm_, "[ Allocate locals");
+ int locals_count = info->scope()->num_stack_slots();
+ // Generators allocate locals, if any, in context slots.
+ ASSERT(!info->function()->is_generator() || locals_count == 0);
+ if (locals_count > 0) {
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ for (int i = 0; i < locals_count; i++) {
+ __ push(at);
+ }
+ }
+ }
+
+ bool function_in_register = true;
+
+ // Possibly allocate a local context.
+ int heap_slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ if (heap_slots > 0) {
+ Comment cmnt(masm_, "[ Allocate context");
+ // Argument to NewContext is the function, which is still in a1.
+ __ push(a1);
+ if (FLAG_harmony_scoping && info->scope()->is_global_scope()) {
+ __ Push(info->scope()->GetScopeInfo());
+ __ CallRuntime(Runtime::kNewGlobalContext, 2);
+ } else if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kNewFunctionContext, 1);
+ }
+ function_in_register = false;
+ // 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 = info->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.
+ __ RecordWriteContextSlot(
+ cp, target.offset(), a0, a3, kRAHasBeenSaved, kDontSaveFPRegs);
+ }
+ }
+ }
+
+ Variable* arguments = scope()->arguments();
+ if (arguments != NULL) {
+ // Function uses arguments object.
+ Comment cmnt(masm_, "[ Allocate arguments object");
+ if (!function_in_register) {
+ // Load this again, if it's used by the local context below.
+ __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ } else {
+ __ mov(a3, a1);
+ }
+ // Receiver is just before the parameters on the caller's stack.
+ int num_parameters = info->scope()->num_parameters();
+ int offset = num_parameters * kPointerSize;
+ __ Addu(a2, fp,
+ Operand(StandardFrameConstants::kCallerSPOffset + offset));
+ __ li(a1, Operand(Smi::FromInt(num_parameters)));
+ __ Push(a3, a2, a1);
+
+ // Arguments to ArgumentsAccessStub:
+ // function, receiver address, parameter count.
+ // The stub will rewrite receiever and parameter count if the previous
+ // stack frame was an arguments adapter frame.
+ 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, v0, a1, a2);
+ }
+
+ if (FLAG_trace) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+
+ // Visit the declarations and body unless there is an illegal
+ // redeclaration.
+ if (scope()->HasIllegalRedeclaration()) {
+ Comment cmnt(masm_, "[ Declarations");
+ scope()->VisitIllegalRedeclaration(this);
+
+ } else {
+ PrepareForBailoutForId(BailoutId::FunctionEntry(), NO_REGISTERS);
+ { Comment cmnt(masm_, "[ Declarations");
+ // For named function expressions, declare the function name as a
+ // constant.
+ if (scope()->is_function_scope() && scope()->function() != NULL) {
+ VariableDeclaration* function = scope()->function();
+ ASSERT(function->proxy()->var()->mode() == CONST ||
+ function->proxy()->var()->mode() == CONST_HARMONY);
+ ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED);
+ VisitVariableDeclaration(function);
+ }
+ VisitDeclarations(scope()->declarations());
+ }
+
+ { Comment cmnt(masm_, "[ Stack check");
+ PrepareForBailoutForId(BailoutId::Declarations(), NO_REGISTERS);
+ Label ok;
+ __ LoadRoot(t0, Heap::kStackLimitRootIndex);
+ __ Branch(&ok, hs, sp, Operand(t0));
+ StackCheckStub stub;
+ __ CallStub(&stub);
+ __ bind(&ok);
+ }
+
+ { Comment cmnt(masm_, "[ Body");
+ ASSERT(loop_depth() == 0);
+ VisitStatements(function()->body());
+ ASSERT(loop_depth() == 0);
+ }
+ }
+
+ // Always emit a 'return undefined' in case control fell off the end of
+ // the body.
+ { Comment cmnt(masm_, "[ return <undefined>;");
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ }
+ EmitReturnSequence();
+}
+
+
+void FullCodeGenerator::ClearAccumulator() {
+ ASSERT(Smi::FromInt(0) == 0);
+ __ mov(v0, zero_reg);
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) {
+ __ li(a2, Operand(profiling_counter_));
+ __ lw(a3, FieldMemOperand(a2, Cell::kValueOffset));
+ __ Subu(a3, a3, Operand(Smi::FromInt(delta)));
+ __ sw(a3, FieldMemOperand(a2, Cell::kValueOffset));
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterReset() {
+ int reset_value = FLAG_interrupt_budget;
+ if (info_->ShouldSelfOptimize() && !FLAG_retry_self_opt) {
+ // Self-optimization is a one-off thing: if it fails, don't try again.
+ reset_value = Smi::kMaxValue;
+ }
+ if (isolate()->IsDebuggerActive()) {
+ // Detect debug break requests as soon as possible.
+ reset_value = FLAG_interrupt_budget >> 4;
+ }
+ __ li(a2, Operand(profiling_counter_));
+ __ li(a3, Operand(Smi::FromInt(reset_value)));
+ __ sw(a3, FieldMemOperand(a2, Cell::kValueOffset));
+}
+
+
+void FullCodeGenerator::EmitBackEdgeBookkeeping(IterationStatement* stmt,
+ Label* back_edge_target) {
+ // 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_, "[ Back edge bookkeeping");
+ Label ok;
+ int weight = 1;
+ if (FLAG_weighted_back_edges) {
+ ASSERT(back_edge_target->is_bound());
+ int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target);
+ weight = Min(kMaxBackEdgeWeight,
+ Max(1, distance / kCodeSizeMultiplier));
+ }
+ EmitProfilingCounterDecrement(weight);
+ __ slt(at, a3, zero_reg);
+ __ beq(at, zero_reg, &ok);
+ // CallStub will emit a li t9 first, so it is safe to use the delay slot.
+ InterruptStub 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.
+ RecordBackEdge(stmt->OsrEntryId());
+ EmitProfilingCounterReset();
+
+ __ bind(&ok);
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ // Record a mapping of the OSR id to this PC. This is used if the OSR
+ // entry becomes the target of a bailout. We don't expect it to be, but
+ // we want it to work if it is.
+ PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::EmitReturnSequence() {
+ Comment cmnt(masm_, "[ Return sequence");
+ if (return_label_.is_bound()) {
+ __ Branch(&return_label_);
+ } else {
+ __ bind(&return_label_);
+ 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);
+ }
+ if (FLAG_interrupt_at_exit || FLAG_self_optimization) {
+ // Pretend that the exit is a backwards jump to the entry.
+ int weight = 1;
+ if (info_->ShouldSelfOptimize()) {
+ weight = FLAG_interrupt_budget / FLAG_self_opt_count;
+ } else if (FLAG_weighted_back_edges) {
+ int distance = masm_->pc_offset();
+ weight = Min(kMaxBackEdgeWeight,
+ Max(1, distance / kCodeSizeMultiplier));
+ }
+ EmitProfilingCounterDecrement(weight);
+ Label ok;
+ __ Branch(&ok, ge, a3, Operand(zero_reg));
+ __ push(v0);
+ if (info_->ShouldSelfOptimize() && FLAG_direct_self_opt) {
+ __ lw(a2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(a2);
+ __ CallRuntime(Runtime::kOptimizeFunctionOnNextCall, 1);
+ } else {
+ InterruptStub stub;
+ __ CallStub(&stub);
+ }
+ __ pop(v0);
+ EmitProfilingCounterReset();
+ __ bind(&ok);
+ }
+
+#ifdef DEBUG
+ // Add a label for checking the size of the code used for returning.
+ Label check_exit_codesize;
+ masm_->bind(&check_exit_codesize);
+#endif
+ // Make sure that the constant pool is not emitted inside of the return
+ // sequence.
+ { Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
+ // Here we use masm_-> instead of the __ macro to avoid the code coverage
+ // tool from instrumenting as we rely on the code size here.
+ int32_t sp_delta = (info_->scope()->num_parameters() + 1) * kPointerSize;
+ CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
+ __ RecordJSReturn();
+ masm_->mov(sp, fp);
+ int no_frame_start = masm_->pc_offset();
+ masm_->MultiPop(static_cast<RegList>(fp.bit() | ra.bit()));
+ masm_->Addu(sp, sp, Operand(sp_delta));
+ masm_->Jump(ra);
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
+
+#ifdef DEBUG
+ // Check that the size of the code used for returning is large enough
+ // for the debugger's requirements.
+ ASSERT(Assembler::kJSReturnSequenceInstructions <=
+ masm_->InstructionsGeneratedSince(&check_exit_codesize));
+#endif
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ codegen()->GetVar(result_register(), var);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ codegen()->GetVar(result_register(), var);
+ __ push(result_register());
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Variable* var) const {
+ // For simplicity we always test the accumulator register.
+ codegen()->GetVar(result_register(), var);
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Heap::RootListIndex index) const {
+ __ LoadRoot(result_register(), index);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(
+ Heap::RootListIndex index) const {
+ __ LoadRoot(result_register(), index);
+ __ push(result_register());
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ if (index == Heap::kUndefinedValueRootIndex ||
+ index == Heap::kNullValueRootIndex ||
+ index == Heap::kFalseValueRootIndex) {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ } else if (index == Heap::kTrueValueRootIndex) {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ } else {
+ __ LoadRoot(result_register(), index);
+ codegen()->DoTest(this);
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Handle<Object> lit) const {
+ __ li(result_register(), Operand(lit));
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const {
+ // Immediates cannot be pushed directly.
+ __ li(result_register(), Operand(lit));
+ __ push(result_register());
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ ASSERT(!lit->IsUndetectableObject()); // There are no undetectable literals.
+ if (lit->IsUndefined() || lit->IsNull() || lit->IsFalse()) {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ } else if (lit->IsTrue() || lit->IsJSObject()) {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ } else if (lit->IsString()) {
+ if (String::cast(*lit)->length() == 0) {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ }
+ } else if (lit->IsSmi()) {
+ if (Smi::cast(*lit)->value() == 0) {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ }
+ } else {
+ // For simplicity we always test the accumulator register.
+ __ li(result_register(), Operand(lit));
+ codegen()->DoTest(this);
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ __ Drop(count);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::DropAndPlug(
+ int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ __ Drop(count);
+ __ Move(result_register(), reg);
+}
+
+
+void FullCodeGenerator::StackValueContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ if (count > 1) __ Drop(count - 1);
+ __ sw(reg, MemOperand(sp, 0));
+}
+
+
+void FullCodeGenerator::TestContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ // For simplicity we always test the accumulator register.
+ __ Drop(count);
+ __ Move(result_register(), reg);
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Label* materialize_true,
+ Label* materialize_false) const {
+ ASSERT(materialize_true == materialize_false);
+ __ bind(materialize_true);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Label* materialize_true,
+ Label* materialize_false) const {
+ Label done;
+ __ bind(materialize_true);
+ __ LoadRoot(result_register(), Heap::kTrueValueRootIndex);
+ __ Branch(&done);
+ __ bind(materialize_false);
+ __ LoadRoot(result_register(), Heap::kFalseValueRootIndex);
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(
+ Label* materialize_true,
+ Label* materialize_false) const {
+ Label done;
+ __ bind(materialize_true);
+ __ LoadRoot(at, Heap::kTrueValueRootIndex);
+ __ push(at);
+ __ Branch(&done);
+ __ bind(materialize_false);
+ __ LoadRoot(at, Heap::kFalseValueRootIndex);
+ __ push(at);
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Label* materialize_true,
+ Label* materialize_false) const {
+ ASSERT(materialize_true == true_label_);
+ ASSERT(materialize_false == false_label_);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(bool flag) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const {
+ Heap::RootListIndex value_root_index =
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
+ __ LoadRoot(result_register(), value_root_index);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(bool flag) const {
+ Heap::RootListIndex value_root_index =
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
+ __ LoadRoot(at, value_root_index);
+ __ push(at);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(bool flag) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ if (flag) {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ } else {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ }
+}
+
+
+void FullCodeGenerator::DoTest(Expression* condition,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ __ mov(a0, result_register());
+ Handle<Code> ic = ToBooleanStub::GetUninitialized(isolate());
+ CallIC(ic, RelocInfo::CODE_TARGET, condition->test_id());
+ __ mov(at, zero_reg);
+ Split(ne, v0, Operand(at), if_true, if_false, fall_through);
+}
+
+
+void FullCodeGenerator::Split(Condition cc,
+ Register lhs,
+ const Operand& rhs,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ if (if_false == fall_through) {
+ __ Branch(if_true, cc, lhs, rhs);
+ } else if (if_true == fall_through) {
+ __ Branch(if_false, NegateCondition(cc), lhs, rhs);
+ } else {
+ __ Branch(if_true, cc, lhs, rhs);
+ __ Branch(if_false);
+ }
+}
+
+
+MemOperand FullCodeGenerator::StackOperand(Variable* var) {
+ ASSERT(var->IsStackAllocated());
+ // Offset is negative because higher indexes are at lower addresses.
+ int offset = -var->index() * kPointerSize;
+ // Adjust by a (parameter or local) base offset.
+ if (var->IsParameter()) {
+ offset += (info_->scope()->num_parameters() + 1) * kPointerSize;
+ } else {
+ offset += JavaScriptFrameConstants::kLocal0Offset;
+ }
+ return MemOperand(fp, offset);
+}
+
+
+MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ if (var->IsContextSlot()) {
+ int context_chain_length = scope()->ContextChainLength(var->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return ContextOperand(scratch, var->index());
+ } else {
+ return StackOperand(var);
+ }
+}
+
+
+void FullCodeGenerator::GetVar(Register dest, Variable* var) {
+ // Use destination as scratch.
+ MemOperand location = VarOperand(var, dest);
+ __ lw(dest, location);
+}
+
+
+void FullCodeGenerator::SetVar(Variable* var,
+ Register src,
+ Register scratch0,
+ Register scratch1) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ ASSERT(!scratch0.is(src));
+ ASSERT(!scratch0.is(scratch1));
+ ASSERT(!scratch1.is(src));
+ MemOperand location = VarOperand(var, scratch0);
+ __ sw(src, location);
+ // Emit the write barrier code if the location is in the heap.
+ if (var->IsContextSlot()) {
+ __ RecordWriteContextSlot(scratch0,
+ location.offset(),
+ src,
+ scratch1,
+ kRAHasBeenSaved,
+ kDontSaveFPRegs);
+ }
+}
+
+
+void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
+ bool should_normalize,
+ Label* if_true,
+ Label* if_false) {
+ // Only prepare for bailouts before splits if we're in a test
+ // context. Otherwise, we let the Visit function deal with the
+ // preparation to avoid preparing with the same AST id twice.
+ if (!context()->IsTest() || !info_->IsOptimizable()) return;
+
+ Label skip;
+ if (should_normalize) __ Branch(&skip);
+ PrepareForBailout(expr, TOS_REG);
+ if (should_normalize) {
+ __ LoadRoot(t0, Heap::kTrueValueRootIndex);
+ Split(eq, a0, Operand(t0), if_true, if_false, NULL);
+ __ bind(&skip);
+ }
+}
+
+
+void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
+ // The variable in the declaration always resides in the current function
+ // context.
+ ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
+ if (generate_debug_code_) {
+ // Check that we're not inside a with or catch context.
+ __ lw(a1, FieldMemOperand(cp, HeapObject::kMapOffset));
+ __ LoadRoot(t0, Heap::kWithContextMapRootIndex);
+ __ Check(ne, kDeclarationInWithContext,
+ a1, Operand(t0));
+ __ LoadRoot(t0, Heap::kCatchContextMapRootIndex);
+ __ Check(ne, kDeclarationInCatchContext,
+ a1, Operand(t0));
+ }
+}
+
+
+void FullCodeGenerator::VisitVariableDeclaration(
+ VariableDeclaration* declaration) {
+ // 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.
+ VariableProxy* proxy = declaration->proxy();
+ VariableMode mode = declaration->mode();
+ Variable* variable = proxy->var();
+ bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ globals_->Add(variable->name(), zone());
+ globals_->Add(variable->binding_needs_init()
+ ? isolate()->factory()->the_hole_value()
+ : isolate()->factory()->undefined_value(),
+ zone());
+ break;
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ if (hole_init) {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ __ sw(t0, StackOperand(variable));
+ }
+ break;
+
+ case Variable::CONTEXT:
+ if (hole_init) {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ sw(at, ContextOperand(cp, variable->index()));
+ // No write barrier since the_hole_value is in old space.
+ PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
+ }
+ break;
+
+ case Variable::LOOKUP: {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ __ li(a2, Operand(variable->name()));
+ // Declaration nodes are always introduced in one of four modes.
+ ASSERT(IsDeclaredVariableMode(mode));
+ PropertyAttributes attr =
+ IsImmutableVariableMode(mode) ? 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
+ // 'undefined') because we may have a (legal) redeclaration and we
+ // must not destroy the current value.
+ if (hole_init) {
+ __ LoadRoot(a0, Heap::kTheHoleValueRootIndex);
+ __ Push(cp, a2, a1, a0);
+ } else {
+ ASSERT(Smi::FromInt(0) == 0);
+ __ mov(a0, zero_reg); // Smi::FromInt(0) indicates no initial value.
+ __ Push(cp, a2, a1, a0);
+ }
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitFunctionDeclaration(
+ FunctionDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED: {
+ globals_->Add(variable->name(), zone());
+ Handle<SharedFunctionInfo> function =
+ Compiler::BuildFunctionInfo(declaration->fun(), script());
+ // Check for stack-overflow exception.
+ if (function.is_null()) return SetStackOverflow();
+ globals_->Add(function, zone());
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ VisitForAccumulatorValue(declaration->fun());
+ __ sw(result_register(), StackOperand(variable));
+ break;
+ }
+
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ VisitForAccumulatorValue(declaration->fun());
+ __ 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.
+ __ RecordWriteContextSlot(cp,
+ offset,
+ result_register(),
+ a2,
+ kRAHasBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
+ break;
+ }
+
+ case Variable::LOOKUP: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ __ li(a2, Operand(variable->name()));
+ __ li(a1, Operand(Smi::FromInt(NONE)));
+ __ Push(cp, a2, a1);
+ // Push initial value for function declaration.
+ VisitForStackValue(declaration->fun());
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
+ Variable* variable = declaration->proxy()->var();
+ ASSERT(variable->location() == Variable::CONTEXT);
+ ASSERT(variable->interface()->IsFrozen());
+
+ Comment cmnt(masm_, "[ ModuleDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+
+ // Load instance object.
+ __ LoadContext(a1, scope_->ContextChainLength(scope_->GlobalScope()));
+ __ lw(a1, ContextOperand(a1, variable->interface()->Index()));
+ __ lw(a1, ContextOperand(a1, Context::EXTENSION_INDEX));
+
+ // Assign it.
+ __ sw(a1, ContextOperand(cp, variable->index()));
+ // We know that we have written a module, which is not a smi.
+ __ RecordWriteContextSlot(cp,
+ Context::SlotOffset(variable->index()),
+ a1,
+ a3,
+ kRAHasBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS);
+
+ // Traverse into body.
+ Visit(declaration->module());
+}
+
+
+void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ // TODO(rossberg)
+ break;
+
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, "[ ImportDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ // TODO(rossberg)
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ case Variable::LOOKUP:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
+ // TODO(rossberg)
+}
+
+
+void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
+ // Call the runtime to declare the globals.
+ // The context is the first argument.
+ __ li(a1, Operand(pairs));
+ __ li(a0, Operand(Smi::FromInt(DeclareGlobalsFlags())));
+ __ Push(cp, a1, a0);
+ __ CallRuntime(Runtime::kDeclareGlobals, 3);
+ // Return value is ignored.
+}
+
+
+void FullCodeGenerator::DeclareModules(Handle<FixedArray> descriptions) {
+ // Call the runtime to declare the modules.
+ __ Push(descriptions);
+ __ CallRuntime(Runtime::kDeclareModules, 1);
+ // Return value is ignored.
+}
+
+
+void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
+ Comment cmnt(masm_, "[ SwitchStatement");
+ Breakable nested_statement(this, stmt);
+ SetStatementPosition(stmt);
+
+ // Keep the switch value on the stack until a case matches.
+ VisitForStackValue(stmt->tag());
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+
+ ZoneList<CaseClause*>* clauses = stmt->cases();
+ CaseClause* default_clause = NULL; // Can occur anywhere in the list.
+
+ Label next_test; // Recycled for each test.
+ // Compile all the tests with branches to their bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ CaseClause* clause = clauses->at(i);
+ clause->body_target()->Unuse();
+
+ // The default is not a test, but remember it as final fall through.
+ if (clause->is_default()) {
+ default_clause = clause;
+ continue;
+ }
+
+ Comment cmnt(masm_, "[ Case comparison");
+ __ bind(&next_test);
+ next_test.Unuse();
+
+ // Compile the label expression.
+ VisitForAccumulatorValue(clause->label());
+ __ mov(a0, result_register()); // CompareStub requires args in a0, a1.
+
+ // Perform the comparison as if via '==='.
+ __ lw(a1, MemOperand(sp, 0)); // Switch value.
+ bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ or_(a2, a1, a0);
+ patch_site.EmitJumpIfNotSmi(a2, &slow_case);
+
+ __ Branch(&next_test, ne, a1, Operand(a0));
+ __ Drop(1); // Switch value is no longer needed.
+ __ Branch(clause->body_target());
+
+ __ bind(&slow_case);
+ }
+
+ // Record position before stub call for type feedback.
+ SetSourcePosition(clause->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), Token::EQ_STRICT);
+ CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
+ patch_site.EmitPatchInfo();
+
+ __ Branch(&next_test, ne, v0, Operand(zero_reg));
+ __ Drop(1); // Switch value is no longer needed.
+ __ Branch(clause->body_target());
+ }
+
+ // Discard the test value and jump to the default if present, otherwise to
+ // the end of the statement.
+ __ bind(&next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ if (default_clause == NULL) {
+ __ Branch(nested_statement.break_label());
+ } else {
+ __ Branch(default_clause->body_target());
+ }
+
+ // Compile all the case bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ Comment cmnt(masm_, "[ Case body");
+ CaseClause* clause = clauses->at(i);
+ __ bind(clause->body_target());
+ PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
+ VisitStatements(clause->statements());
+ }
+
+ __ bind(nested_statement.break_label());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
+ Comment cmnt(masm_, "[ ForInStatement");
+ SetStatementPosition(stmt);
+
+ Label loop, exit;
+ ForIn loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // Get the object to enumerate over. If the object is null or undefined, skip
+ // over the loop. See ECMA-262 version 5, section 12.6.4.
+ VisitForAccumulatorValue(stmt->enumerable());
+ __ mov(a0, result_register()); // Result as param to InvokeBuiltin below.
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&exit, eq, a0, Operand(at));
+ Register null_value = t1;
+ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
+ __ Branch(&exit, eq, a0, Operand(null_value));
+ PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
+ __ mov(a0, v0);
+ // Convert the object to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(a0, &convert);
+ __ GetObjectType(a0, a1, a1);
+ __ Branch(&done_convert, ge, a1, Operand(FIRST_SPEC_OBJECT_TYPE));
+ __ bind(&convert);
+ __ push(a0);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ mov(a0, v0);
+ __ 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.
+ __ CheckEnumCache(null_value, &call_runtime);
+
+ // The enum cache is valid. Load the map of the object being
+ // iterated over and use the cache for the iteration.
+ Label use_cache;
+ __ lw(v0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ Branch(&use_cache);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(a0); // Duplicate the enumerable object on the stack.
+ __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
+
+ // If we got a map from the runtime call, we can do a fast
+ // modification check. Otherwise, we got a fixed array, and we have
+ // to do a slow check.
+ Label fixed_array;
+ __ lw(a2, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kMetaMapRootIndex);
+ __ Branch(&fixed_array, ne, a2, Operand(at));
+
+ // We got a map in register v0. Get the enumeration cache from it.
+ Label no_descriptors;
+ __ bind(&use_cache);
+
+ __ EnumLength(a1, v0);
+ __ Branch(&no_descriptors, eq, a1, Operand(Smi::FromInt(0)));
+
+ __ LoadInstanceDescriptors(v0, a2);
+ __ lw(a2, FieldMemOperand(a2, DescriptorArray::kEnumCacheOffset));
+ __ lw(a2, FieldMemOperand(a2, DescriptorArray::kEnumCacheBridgeCacheOffset));
+
+ // Set up the four remaining stack slots.
+ __ push(v0); // Map.
+ __ li(a0, Operand(Smi::FromInt(0)));
+ // Push enumeration cache, enumeration cache length (as smi) and zero.
+ __ Push(a2, a1, a0);
+ __ jmp(&loop);
+
+ __ bind(&no_descriptors);
+ __ Drop(1);
+ __ jmp(&exit);
+
+ // We got a fixed array in register v0. Iterate through that.
+ Label non_proxy;
+ __ bind(&fixed_array);
+
+ Handle<Cell> cell = isolate()->factory()->NewCell(
+ Handle<Object>(Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker),
+ isolate()));
+ RecordTypeFeedbackCell(stmt->ForInFeedbackId(), cell);
+ __ LoadHeapObject(a1, cell);
+ __ li(a2, Operand(Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker)));
+ __ sw(a2, FieldMemOperand(a1, Cell::kValueOffset));
+
+ __ 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.
+
+ // Generate code for doing the condition check.
+ PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
+ __ bind(&loop);
+ // Load the current count to a0, load the length to a1.
+ __ lw(a0, MemOperand(sp, 0 * kPointerSize));
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+ __ Branch(loop_statement.break_label(), hs, a0, Operand(a1));
+
+ // Get the current entry of the array into register a3.
+ __ lw(a2, MemOperand(sp, 2 * kPointerSize));
+ __ Addu(a2, a2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(t0, a0, kPointerSizeLog2 - kSmiTagSize);
+ __ addu(t0, a2, t0); // Array base + scaled (smi) index.
+ __ lw(a3, MemOperand(t0)); // Current entry.
+
+ // 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 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.
+ __ push(a1); // Enumerable.
+ __ push(a3); // Current entry.
+ __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
+ __ mov(a3, result_register());
+ __ Branch(loop_statement.continue_label(), eq, a3, Operand(zero_reg));
+
+ // Update the 'each' property or variable from the possibly filtered
+ // entry in register a3.
+ __ bind(&update_each);
+ __ mov(result_register(), a3);
+ // Perform the assignment as if via '='.
+ { EffectContext context(this);
+ EmitAssignment(stmt->each());
+ }
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Generate code for the going to the next element by incrementing
+ // the index (smi) stored on top of the stack.
+ __ bind(loop_statement.continue_label());
+ __ pop(a0);
+ __ Addu(a0, a0, Operand(Smi::FromInt(1)));
+ __ push(a0);
+
+ EmitBackEdgeBookkeeping(stmt, &loop);
+ __ Branch(&loop);
+
+ // Remove the pointers stored on the stack.
+ __ bind(loop_statement.break_label());
+ __ Drop(5);
+
+ // Exit and decrement the loop depth.
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(&exit);
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
+ Comment cmnt(masm_, "[ ForOfStatement");
+ SetStatementPosition(stmt);
+
+ Iteration loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // var iterator = iterable[@@iterator]()
+ VisitForAccumulatorValue(stmt->assign_iterator());
+ __ mov(a0, v0);
+
+ // As with for-in, skip the loop if the iterator is null or undefined.
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(loop_statement.break_label(), eq, a0, Operand(at));
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ __ Branch(loop_statement.break_label(), eq, a0, Operand(at));
+
+ // Convert the iterator to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(a0, &convert);
+ __ GetObjectType(a0, a1, a1);
+ __ Branch(&done_convert, ge, a1, Operand(FIRST_SPEC_OBJECT_TYPE));
+ __ bind(&convert);
+ __ push(a0);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ mov(a0, v0);
+ __ bind(&done_convert);
+ __ push(a0);
+
+ // Loop entry.
+ __ bind(loop_statement.continue_label());
+
+ // result = iterator.next()
+ VisitForEffect(stmt->next_result());
+
+ // if (result.done) break;
+ Label result_not_done;
+ VisitForControl(stmt->result_done(),
+ loop_statement.break_label(),
+ &result_not_done,
+ &result_not_done);
+ __ bind(&result_not_done);
+
+ // each = result.value
+ VisitForEffect(stmt->assign_each());
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Check stack before looping.
+ PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS);
+ EmitBackEdgeBookkeeping(stmt, loop_statement.continue_label());
+ __ jmp(loop_statement.continue_label());
+
+ // Exit and decrement the loop depth.
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(loop_statement.break_label());
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
+ bool pretenure) {
+ // Use the fast case closure allocation code that allocates in new
+ // space for nested functions that don't need literals cloning. If
+ // we're running with the --always-opt or the --prepare-always-opt
+ // flag, we need to use the runtime function so that the new function
+ // we are creating here gets a chance to have its code optimized and
+ // doesn't just get a copy of the existing unoptimized code.
+ if (!FLAG_always_opt &&
+ !FLAG_prepare_always_opt &&
+ !pretenure &&
+ scope()->is_function_scope() &&
+ info->num_literals() == 0) {
+ FastNewClosureStub stub(info->language_mode(), info->is_generator());
+ __ li(a0, Operand(info));
+ __ push(a0);
+ __ CallStub(&stub);
+ } else {
+ __ li(a0, Operand(info));
+ __ LoadRoot(a1, pretenure ? Heap::kTrueValueRootIndex
+ : Heap::kFalseValueRootIndex);
+ __ Push(cp, a0, a1);
+ __ CallRuntime(Runtime::kNewClosure, 3);
+ }
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
+ Comment cmnt(masm_, "[ VariableProxy");
+ EmitVariableLoad(expr);
+}
+
+
+void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
+ TypeofState typeof_state,
+ Label* slow) {
+ Register current = cp;
+ Register next = a1;
+ Register temp = a2;
+
+ Scope* s = scope();
+ while (s != NULL) {
+ if (s->num_heap_slots() > 0) {
+ 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));
+ }
+ // Load next context in chain.
+ __ lw(next, ContextOperand(current, Context::PREVIOUS_INDEX));
+ // Walk the rest of the chain without clobbering cp.
+ current = next;
+ }
+ // If no outer scope calls eval, we do not need to check more
+ // context extensions.
+ if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break;
+ s = s->outer_scope();
+ }
+
+ if (s->is_eval_scope()) {
+ Label loop, fast;
+ if (!current.is(next)) {
+ __ Move(next, current);
+ }
+ __ bind(&loop);
+ // Terminate at native context.
+ __ lw(temp, FieldMemOperand(next, HeapObject::kMapOffset));
+ __ LoadRoot(t0, Heap::kNativeContextMapRootIndex);
+ __ Branch(&fast, eq, temp, Operand(t0));
+ // Check that extension is NULL.
+ __ lw(temp, ContextOperand(next, Context::EXTENSION_INDEX));
+ __ Branch(slow, ne, temp, Operand(zero_reg));
+ // Load next context in chain.
+ __ lw(next, ContextOperand(next, Context::PREVIOUS_INDEX));
+ __ Branch(&loop);
+ __ bind(&fast);
+ }
+
+ __ lw(a0, GlobalObjectOperand());
+ __ li(a2, Operand(var->name()));
+ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+ ? RelocInfo::CODE_TARGET
+ : RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, mode);
+}
+
+
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var,
+ Label* slow) {
+ ASSERT(var->IsContextSlot());
+ Register context = cp;
+ Register next = a3;
+ Register temp = t0;
+
+ for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) {
+ if (s->num_heap_slots() > 0) {
+ 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));
+ }
+ __ lw(next, ContextOperand(context, Context::PREVIOUS_INDEX));
+ // Walk the rest of the chain without clobbering cp.
+ context = next;
+ }
+ }
+ // Check that last extension is NULL.
+ __ lw(temp, ContextOperand(context, Context::EXTENSION_INDEX));
+ __ Branch(slow, ne, temp, Operand(zero_reg));
+
+ // This function is used only for loads, not stores, so it's safe to
+ // return an cp-based operand (the write barrier cannot be allowed to
+ // destroy the cp register).
+ return ContextOperand(context, var->index());
+}
+
+
+void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var,
+ TypeofState typeof_state,
+ Label* slow,
+ Label* done) {
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // 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() == DYNAMIC_GLOBAL) {
+ EmitLoadGlobalCheckExtensions(var, typeof_state, slow);
+ __ Branch(done);
+ } else if (var->mode() == DYNAMIC_LOCAL) {
+ Variable* local = var->local_if_not_shadowed();
+ __ lw(v0, ContextSlotOperandCheckExtensions(local, slow));
+ if (local->mode() == LET ||
+ local->mode() == CONST ||
+ local->mode() == CONST_HARMONY) {
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ subu(at, v0, at); // Sub as compare: at == 0 on eq.
+ 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);
+ }
+}
+
+
+void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
+ // Record position before possible IC call.
+ SetSourcePosition(proxy->position());
+ Variable* var = proxy->var();
+
+ // Three cases: global variables, lookup variables, and all other types of
+ // variables.
+ switch (var->location()) {
+ case Variable::UNALLOCATED: {
+ Comment cmnt(masm_, "Global variable");
+ // Use inline caching. Variable name is passed in a2 and the global
+ // object (receiver) in a0.
+ __ lw(a0, GlobalObjectOperand());
+ __ li(a2, Operand(var->name()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ context()->Plug(v0);
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, var->IsContextSlot()
+ ? "Context variable"
+ : "Stack variable");
+ 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.
+ 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(var);
+ break;
+ }
+
+ case Variable::LOOKUP: {
+ Label done, slow;
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done);
+ __ bind(&slow);
+ Comment cmnt(masm_, "Lookup variable");
+ __ li(a1, Operand(var->name()));
+ __ Push(cp, a1); // Context and name.
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ bind(&done);
+ context()->Plug(v0);
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
+ Comment cmnt(masm_, "[ RegExpLiteral");
+ Label materialized;
+ // Registers will be used as follows:
+ // t1 = materialized value (RegExp literal)
+ // t0 = JS function, literals array
+ // a3 = literal index
+ // a2 = RegExp pattern
+ // a1 = RegExp flags
+ // a0 = RegExp literal clone
+ __ lw(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ lw(t0, FieldMemOperand(a0, JSFunction::kLiteralsOffset));
+ int literal_offset =
+ FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
+ __ lw(t1, FieldMemOperand(t0, literal_offset));
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&materialized, ne, t1, Operand(at));
+
+ // Create regexp literal using runtime function.
+ // Result will be in v0.
+ __ li(a3, Operand(Smi::FromInt(expr->literal_index())));
+ __ li(a2, Operand(expr->pattern()));
+ __ li(a1, Operand(expr->flags()));
+ __ Push(t0, a3, a2, a1);
+ __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
+ __ mov(t1, v0);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+ __ Allocate(size, v0, a2, a3, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ push(t1);
+ __ li(a0, Operand(Smi::FromInt(size)));
+ __ push(a0);
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ pop(t1);
+
+ __ bind(&allocated);
+
+ // After this, registers are used as follows:
+ // v0: Newly allocated regexp.
+ // t1: Materialized regexp.
+ // a2: temp.
+ __ CopyFields(v0, t1, a2.bit(), size / kPointerSize);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitAccessor(Expression* expression) {
+ if (expression == NULL) {
+ __ LoadRoot(a1, Heap::kNullValueRootIndex);
+ __ push(a1);
+ } else {
+ VisitForStackValue(expression);
+ }
+}
+
+
+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(constant_properties));
+ int flags = expr->fast_elements()
+ ? ObjectLiteral::kFastElements
+ : ObjectLiteral::kNoFlags;
+ flags |= expr->has_function()
+ ? ObjectLiteral::kHasFunction
+ : ObjectLiteral::kNoFlags;
+ __ li(a0, Operand(Smi::FromInt(flags)));
+ int properties_count = constant_properties->length() / 2;
+ if ((FLAG_track_double_fields && expr->may_store_doubles()) ||
+ expr->depth() > 1) {
+ __ Push(a3, a2, a1, a0);
+ __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
+ } else if (Serializer::enabled() || flags != ObjectLiteral::kFastElements ||
+ properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) {
+ __ Push(a3, a2, a1, a0);
+ __ 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
+ // result_saved is false the result is in v0.
+ bool result_saved = false;
+
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ expr->CalculateEmitStore(zone());
+
+ AccessorTable accessor_table(zone());
+ for (int i = 0; i < expr->properties()->length(); i++) {
+ ObjectLiteral::Property* property = expr->properties()->at(i);
+ if (property->IsCompileTimeValue()) continue;
+
+ Literal* key = property->key();
+ Expression* value = property->value();
+ if (!result_saved) {
+ __ push(v0); // Save result on stack.
+ result_saved = true;
+ }
+ switch (property->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ UNREACHABLE();
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+ ASSERT(!CompileTimeValue::IsCompileTimeValue(property->value()));
+ // Fall through.
+ case ObjectLiteral::Property::COMPUTED:
+ if (key->value()->IsInternalizedString()) {
+ if (property->emit_store()) {
+ VisitForAccumulatorValue(value);
+ __ mov(a0, result_register());
+ __ li(a2, Operand(key->value()));
+ __ lw(a1, MemOperand(sp));
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, key->LiteralFeedbackId());
+ PrepareForBailoutForId(key->id(), NO_REGISTERS);
+ } else {
+ VisitForEffect(value);
+ }
+ break;
+ }
+ // Duplicate receiver on stack.
+ __ lw(a0, MemOperand(sp));
+ __ push(a0);
+ VisitForStackValue(key);
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ li(a0, Operand(Smi::FromInt(NONE))); // PropertyAttributes.
+ __ push(a0);
+ __ CallRuntime(Runtime::kSetProperty, 4);
+ } else {
+ __ Drop(3);
+ }
+ break;
+ case ObjectLiteral::Property::PROTOTYPE:
+ // Duplicate receiver on stack.
+ __ lw(a0, MemOperand(sp));
+ __ push(a0);
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ CallRuntime(Runtime::kSetPrototype, 2);
+ } else {
+ __ Drop(2);
+ }
+ break;
+ case ObjectLiteral::Property::GETTER:
+ accessor_table.lookup(key)->second->getter = value;
+ break;
+ case ObjectLiteral::Property::SETTER:
+ accessor_table.lookup(key)->second->setter = value;
+ break;
+ }
+ }
+
+ // Emit code to define accessors, using only a single call to the runtime for
+ // each pair of corresponding getters and setters.
+ for (AccessorTable::Iterator it = accessor_table.begin();
+ it != accessor_table.end();
+ ++it) {
+ __ lw(a0, MemOperand(sp)); // Duplicate receiver.
+ __ push(a0);
+ VisitForStackValue(it->first);
+ EmitAccessor(it->second->getter);
+ EmitAccessor(it->second->setter);
+ __ li(a0, Operand(Smi::FromInt(NONE)));
+ __ push(a0);
+ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ }
+
+ if (expr->has_function()) {
+ ASSERT(result_saved);
+ __ lw(a0, MemOperand(sp));
+ __ push(a0);
+ __ CallRuntime(Runtime::kToFastProperties, 1);
+ }
+
+ if (result_saved) {
+ context()->PlugTOS();
+ } else {
+ context()->Plug(v0);
+ }
+}
+
+
+void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
+ Comment cmnt(masm_, "[ ArrayLiteral");
+
+ 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 =
+ IsFastObjectElementsKind(constant_elements_kind);
+ 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(constant_elements));
+ if (has_fast_elements && constant_elements_values->map() ==
+ isolate()->heap()->fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS,
+ DONT_TRACK_ALLOCATION_SITE,
+ length);
+ __ CallStub(&stub);
+ __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(),
+ 1, a1, a2);
+ } else if (expr->depth() > 1) {
+ __ Push(a3, a2, a1);
+ __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
+ } else if (Serializer::enabled() ||
+ length > FastCloneShallowArrayStub::kMaximumClonedLength) {
+ __ Push(a3, a2, a1);
+ __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
+ } else {
+ ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) ||
+ FLAG_smi_only_arrays);
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
+ AllocationSiteMode allocation_site_mode = FLAG_track_allocation_sites
+ ? TRACK_ALLOCATION_SITE : DONT_TRACK_ALLOCATION_SITE;
+
+ if (has_fast_elements) {
+ mode = FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
+ }
+
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
+ __ CallStub(&stub);
+ }
+
+ bool result_saved = false; // Is the result saved to the stack?
+
+ // Emit code to evaluate all the non-constant subexpressions and to store
+ // them into the newly cloned array.
+ for (int i = 0; i < length; i++) {
+ Expression* subexpr = subexprs->at(i);
+ // If the subexpression is a literal or a simple materialized literal it
+ // is already set in the cloned array.
+ if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
+
+ if (!result_saved) {
+ __ push(v0); // array literal
+ __ Push(Smi::FromInt(expr->literal_index()));
+ result_saved = true;
+ }
+
+ VisitForAccumulatorValue(subexpr);
+
+ if (IsFastObjectElementsKind(constant_elements_kind)) {
+ int offset = FixedArray::kHeaderSize + (i * kPointerSize);
+ __ lw(t2, MemOperand(sp, kPointerSize)); // 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 {
+ __ li(a3, Operand(Smi::FromInt(i)));
+ __ mov(a0, result_register());
+ StoreArrayLiteralElementStub stub;
+ __ CallStub(&stub);
+ }
+
+ PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
+ }
+ if (result_saved) {
+ __ Pop(); // literal index
+ context()->PlugTOS();
+ } else {
+ context()->Plug(v0);
+ }
+}
+
+
+void FullCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // on the left-hand side.
+ if (!expr->target()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->target());
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* property = expr->target()->AsProperty();
+ if (property != NULL) {
+ assign_type = (property->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ // Evaluate LHS expression.
+ switch (assign_type) {
+ case VARIABLE:
+ // Nothing to do here.
+ break;
+ case NAMED_PROPERTY:
+ if (expr->is_compound()) {
+ // We need the receiver both on the stack and in the accumulator.
+ VisitForAccumulatorValue(property->obj());
+ __ push(result_register());
+ } else {
+ VisitForStackValue(property->obj());
+ }
+ break;
+ case KEYED_PROPERTY:
+ // We need the key and receiver on both the stack and in v0 and a1.
+ if (expr->is_compound()) {
+ VisitForStackValue(property->obj());
+ VisitForAccumulatorValue(property->key());
+ __ lw(a1, MemOperand(sp, 0));
+ __ push(v0);
+ } else {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ }
+ break;
+ }
+
+ // For compound assignments we need another deoptimization point after the
+ // variable/property load.
+ if (expr->is_compound()) {
+ { AccumulatorValueContext context(this);
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableLoad(expr->target()->AsVariableProxy());
+ PrepareForBailout(expr->target(), TOS_REG);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
+ }
+ }
+
+ Token::Value op = expr->binary_op();
+ __ push(v0); // Left operand goes on the stack.
+ VisitForAccumulatorValue(expr->value());
+
+ OverwriteMode mode = expr->value()->ResultOverwriteAllowed()
+ ? OVERWRITE_RIGHT
+ : NO_OVERWRITE;
+ SetSourcePosition(expr->position() + 1);
+ AccumulatorValueContext context(this);
+ if (ShouldInlineSmiCase(op)) {
+ EmitInlineSmiBinaryOp(expr->binary_operation(),
+ op,
+ mode,
+ expr->target(),
+ expr->value());
+ } else {
+ EmitBinaryOp(expr->binary_operation(), op, mode);
+ }
+
+ // Deoptimization point in case the binary operation may have side effects.
+ PrepareForBailout(expr->binary_operation(), TOS_REG);
+ } else {
+ VisitForAccumulatorValue(expr->value());
+ }
+
+ // Record source position before possible IC call.
+ SetSourcePosition(expr->position());
+
+ // Store the value.
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ expr->op());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(v0);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyAssignment(expr);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyAssignment(expr);
+ break;
+ }
+}
+
+
+void FullCodeGenerator::VisitYield(Yield* expr) {
+ Comment cmnt(masm_, "[ Yield");
+ // Evaluate yielded value first; the initial iterator definition depends on
+ // this. It stays on the stack while we update the iterator.
+ VisitForStackValue(expr->expression());
+
+ switch (expr->yield_kind()) {
+ case Yield::SUSPEND:
+ // Pop value from top-of-stack slot; box result into result register.
+ EmitCreateIteratorResult(false);
+ __ push(result_register());
+ // Fall through.
+ case Yield::INITIAL: {
+ Label suspend, continuation, post_runtime, resume;
+
+ __ jmp(&suspend);
+
+ __ bind(&continuation);
+ __ jmp(&resume);
+
+ __ bind(&suspend);
+ VisitForAccumulatorValue(expr->generator_object());
+ ASSERT(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
+ __ li(a1, Operand(Smi::FromInt(continuation.pos())));
+ __ sw(a1, FieldMemOperand(v0, JSGeneratorObject::kContinuationOffset));
+ __ sw(cp, FieldMemOperand(v0, JSGeneratorObject::kContextOffset));
+ __ mov(a1, cp);
+ __ RecordWriteField(v0, JSGeneratorObject::kContextOffset, a1, a2,
+ kRAHasBeenSaved, kDontSaveFPRegs);
+ __ Addu(a1, fp, Operand(StandardFrameConstants::kExpressionsOffset));
+ __ Branch(&post_runtime, eq, sp, Operand(a1));
+ __ push(v0); // generator object
+ __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ __ bind(&post_runtime);
+ __ pop(result_register());
+ EmitReturnSequence();
+
+ __ bind(&resume);
+ context()->Plug(result_register());
+ break;
+ }
+
+ case Yield::FINAL: {
+ VisitForAccumulatorValue(expr->generator_object());
+ __ li(a1, Operand(Smi::FromInt(JSGeneratorObject::kGeneratorClosed)));
+ __ sw(a1, FieldMemOperand(result_register(),
+ JSGeneratorObject::kContinuationOffset));
+ // Pop value from top-of-stack slot, box result into result register.
+ EmitCreateIteratorResult(true);
+ EmitUnwindBeforeReturn();
+ EmitReturnSequence();
+ break;
+ }
+
+ case Yield::DELEGATING: {
+ VisitForStackValue(expr->generator_object());
+
+ // Initial stack layout is as follows:
+ // [sp + 1 * kPointerSize] iter
+ // [sp + 0 * kPointerSize] g
+
+ Label l_catch, l_try, l_suspend, l_continuation, l_resume;
+ Label l_next, l_call, l_loop;
+ // Initial send value is undefined.
+ __ LoadRoot(a0, Heap::kUndefinedValueRootIndex);
+ __ Branch(&l_next);
+
+ // catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; }
+ __ bind(&l_catch);
+ __ mov(a0, v0);
+ handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
+ __ LoadRoot(a2, Heap::kthrow_stringRootIndex); // "throw"
+ __ lw(a3, MemOperand(sp, 1 * kPointerSize)); // iter
+ __ push(a3); // iter
+ __ push(a0); // exception
+ __ jmp(&l_call);
+
+ // try { received = %yield result }
+ // Shuffle the received result above a try handler and yield it without
+ // re-boxing.
+ __ bind(&l_try);
+ __ pop(a0); // result
+ __ PushTryHandler(StackHandler::CATCH, expr->index());
+ const int handler_size = StackHandlerConstants::kSize;
+ __ push(a0); // result
+ __ jmp(&l_suspend);
+ __ bind(&l_continuation);
+ __ mov(a0, v0);
+ __ jmp(&l_resume);
+ __ bind(&l_suspend);
+ const int generator_object_depth = kPointerSize + handler_size;
+ __ lw(a0, MemOperand(sp, generator_object_depth));
+ __ push(a0); // g
+ ASSERT(l_continuation.pos() > 0 && Smi::IsValid(l_continuation.pos()));
+ __ li(a1, Operand(Smi::FromInt(l_continuation.pos())));
+ __ sw(a1, FieldMemOperand(a0, JSGeneratorObject::kContinuationOffset));
+ __ sw(cp, FieldMemOperand(a0, JSGeneratorObject::kContextOffset));
+ __ mov(a1, cp);
+ __ RecordWriteField(a0, JSGeneratorObject::kContextOffset, a1, a2,
+ kRAHasBeenSaved, kDontSaveFPRegs);
+ __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ __ pop(v0); // result
+ EmitReturnSequence();
+ __ mov(a0, v0);
+ __ bind(&l_resume); // received in a0
+ __ PopTryHandler();
+
+ // receiver = iter; f = 'next'; arg = received;
+ __ bind(&l_next);
+ __ LoadRoot(a2, Heap::knext_stringRootIndex); // "next"
+ __ lw(a3, MemOperand(sp, 1 * kPointerSize)); // iter
+ __ push(a3); // iter
+ __ push(a0); // received
+
+ // result = receiver[f](arg);
+ __ bind(&l_call);
+ Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(1);
+ CallIC(ic);
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+
+ // if (!result.done) goto l_try;
+ __ bind(&l_loop);
+ __ mov(a0, v0);
+ __ push(a0); // save result
+ __ LoadRoot(a2, Heap::kdone_stringRootIndex); // "done"
+ Handle<Code> done_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(done_ic); // result.done in v0
+ __ mov(a0, v0);
+ Handle<Code> bool_ic = ToBooleanStub::GetUninitialized(isolate());
+ CallIC(bool_ic);
+ __ Branch(&l_try, eq, v0, Operand(zero_reg));
+
+ // result.value
+ __ pop(a0); // result
+ __ LoadRoot(a2, Heap::kvalue_stringRootIndex); // "value"
+ Handle<Code> value_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(value_ic); // result.value in v0
+ context()->DropAndPlug(2, v0); // drop iter and g
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode) {
+ // The value stays in a0, and is ultimately read by the resumed generator, as
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. a1
+ // will hold the generator object until the activation has been resumed.
+ VisitForStackValue(generator);
+ VisitForAccumulatorValue(value);
+ __ pop(a1);
+
+ // Check generator state.
+ Label wrong_state, done;
+ __ lw(a3, FieldMemOperand(a1, JSGeneratorObject::kContinuationOffset));
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ __ Branch(&wrong_state, le, a3, Operand(zero_reg));
+
+ // Load suspended function and context.
+ __ lw(cp, FieldMemOperand(a1, JSGeneratorObject::kContextOffset));
+ __ lw(t0, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset));
+
+ // Load receiver and store as the first argument.
+ __ lw(a2, FieldMemOperand(a1, JSGeneratorObject::kReceiverOffset));
+ __ push(a2);
+
+ // Push holes for the rest of the arguments to the generator function.
+ __ lw(a3, FieldMemOperand(t0, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(a3,
+ FieldMemOperand(a3, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ LoadRoot(a2, Heap::kTheHoleValueRootIndex);
+ Label push_argument_holes, push_frame;
+ __ bind(&push_argument_holes);
+ __ Subu(a3, a3, Operand(Smi::FromInt(1)));
+ __ Branch(&push_frame, lt, a3, Operand(zero_reg));
+ __ push(a2);
+ __ jmp(&push_argument_holes);
+
+ // Enter a new JavaScript frame, and initialize its slots as they were when
+ // the generator was suspended.
+ Label resume_frame;
+ __ bind(&push_frame);
+ __ Call(&resume_frame);
+ __ jmp(&done);
+ __ bind(&resume_frame);
+ __ push(ra); // Return address.
+ __ push(fp); // Caller's frame pointer.
+ __ mov(fp, sp);
+ __ push(cp); // Callee's context.
+ __ push(t0); // Callee's JS Function.
+
+ // Load the operand stack size.
+ __ lw(a3, FieldMemOperand(a1, JSGeneratorObject::kOperandStackOffset));
+ __ lw(a3, FieldMemOperand(a3, FixedArray::kLengthOffset));
+ __ SmiUntag(a3);
+
+ // If we are sending a value and there is no operand stack, we can jump back
+ // in directly.
+ if (resume_mode == JSGeneratorObject::NEXT) {
+ Label slow_resume;
+ __ Branch(&slow_resume, ne, a3, Operand(zero_reg));
+ __ lw(a3, FieldMemOperand(t0, JSFunction::kCodeEntryOffset));
+ __ lw(a2, FieldMemOperand(a1, JSGeneratorObject::kContinuationOffset));
+ __ SmiUntag(a2);
+ __ Addu(a3, a3, Operand(a2));
+ __ li(a2, Operand(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting)));
+ __ sw(a2, FieldMemOperand(a1, JSGeneratorObject::kContinuationOffset));
+ __ Jump(a3);
+ __ bind(&slow_resume);
+ }
+
+ // Otherwise, we push holes for the operand stack and call the runtime to fix
+ // up the stack and the handlers.
+ Label push_operand_holes, call_resume;
+ __ bind(&push_operand_holes);
+ __ Subu(a3, a3, Operand(1));
+ __ Branch(&call_resume, lt, a3, Operand(zero_reg));
+ __ push(a2);
+ __ Branch(&push_operand_holes);
+ __ bind(&call_resume);
+ __ push(a1);
+ __ push(result_register());
+ __ Push(Smi::FromInt(resume_mode));
+ __ CallRuntime(Runtime::kResumeJSGeneratorObject, 3);
+ // Not reached: the runtime call returns elsewhere.
+ __ stop("not-reached");
+
+ // Throw error if we attempt to operate on a running generator.
+ __ bind(&wrong_state);
+ __ push(a1);
+ __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
+
+ __ bind(&done);
+ context()->Plug(result_register());
+}
+
+
+void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
+ Label gc_required;
+ Label allocated;
+
+ Handle<Map> map(isolate()->native_context()->generator_result_map());
+
+ __ Allocate(map->instance_size(), v0, a2, a3, &gc_required, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&gc_required);
+ __ Push(Smi::FromInt(map->instance_size()));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ lw(context_register(),
+ MemOperand(fp, StandardFrameConstants::kContextOffset));
+
+ __ bind(&allocated);
+ __ li(a1, Operand(map));
+ __ pop(a2);
+ __ li(a3, Operand(isolate()->factory()->ToBoolean(done)));
+ __ li(t0, Operand(isolate()->factory()->empty_fixed_array()));
+ ASSERT_EQ(map->instance_size(), 5 * kPointerSize);
+ __ sw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ sw(t0, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ sw(t0, FieldMemOperand(v0, JSObject::kElementsOffset));
+ __ sw(a2,
+ FieldMemOperand(v0, JSGeneratorObject::kResultValuePropertyOffset));
+ __ sw(a3,
+ FieldMemOperand(v0, JSGeneratorObject::kResultDonePropertyOffset));
+
+ // Only the value field needs a write barrier, as the other values are in the
+ // root set.
+ __ RecordWriteField(v0, JSGeneratorObject::kResultValuePropertyOffset,
+ a2, a3, kRAHasBeenSaved, kDontSaveFPRegs);
+}
+
+
+void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ Literal* key = prop->key()->AsLiteral();
+ __ mov(a0, result_register());
+ __ li(a2, Operand(key->value()));
+ // Call load IC. It has arguments receiver and property name a0 and a2.
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
+}
+
+
+void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ __ 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();
+ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
+}
+
+
+void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode,
+ Expression* left_expr,
+ Expression* right_expr) {
+ Label done, smi_case, stub_call;
+
+ Register scratch1 = a2;
+ Register scratch2 = a3;
+
+ // Get the arguments.
+ Register left = a1;
+ Register right = a0;
+ __ pop(left);
+ __ mov(a0, result_register());
+
+ // Perform combined smi check on both operands.
+ __ Or(scratch1, left, Operand(right));
+ STATIC_ASSERT(kSmiTag == 0);
+ JumpPatchSite patch_site(masm_);
+ patch_site.EmitJumpIfSmi(scratch1, &smi_case);
+
+ __ bind(&stub_call);
+ BinaryOpStub stub(op, mode);
+ CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET,
+ expr->BinaryOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ __ jmp(&done);
+
+ __ bind(&smi_case);
+ // Smi case. This code works the same way as the smi-smi case in the type
+ // recording binary operation stub, see
+ // BinaryOpStub::GenerateSmiSmiOperation for comments.
+ switch (op) {
+ case Token::SAR:
+ __ Branch(&stub_call);
+ __ GetLeastBitsFromSmi(scratch1, right, 5);
+ __ srav(right, left, scratch1);
+ __ And(v0, right, Operand(~kSmiTagMask));
+ break;
+ case Token::SHL: {
+ __ Branch(&stub_call);
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ sllv(scratch1, scratch1, scratch2);
+ __ Addu(scratch2, scratch1, Operand(0x40000000));
+ __ Branch(&stub_call, lt, scratch2, Operand(zero_reg));
+ __ SmiTag(v0, scratch1);
+ break;
+ }
+ case Token::SHR: {
+ __ Branch(&stub_call);
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ srlv(scratch1, scratch1, scratch2);
+ __ And(scratch2, scratch1, 0xc0000000);
+ __ Branch(&stub_call, ne, scratch2, Operand(zero_reg));
+ __ SmiTag(v0, scratch1);
+ break;
+ }
+ case Token::ADD:
+ __ AdduAndCheckForOverflow(v0, left, right, scratch1);
+ __ BranchOnOverflow(&stub_call, scratch1);
+ break;
+ case Token::SUB:
+ __ SubuAndCheckForOverflow(v0, left, right, scratch1);
+ __ BranchOnOverflow(&stub_call, scratch1);
+ break;
+ case Token::MUL: {
+ __ SmiUntag(scratch1, right);
+ __ Mult(left, scratch1);
+ __ mflo(scratch1);
+ __ mfhi(scratch2);
+ __ sra(scratch1, scratch1, 31);
+ __ Branch(&stub_call, ne, scratch1, Operand(scratch2));
+ __ mflo(v0);
+ __ Branch(&done, ne, v0, Operand(zero_reg));
+ __ Addu(scratch2, right, left);
+ __ Branch(&stub_call, lt, scratch2, Operand(zero_reg));
+ ASSERT(Smi::FromInt(0) == 0);
+ __ mov(v0, zero_reg);
+ break;
+ }
+ case Token::BIT_OR:
+ __ Or(v0, left, Operand(right));
+ break;
+ case Token::BIT_AND:
+ __ And(v0, left, Operand(right));
+ break;
+ case Token::BIT_XOR:
+ __ Xor(v0, left, Operand(right));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ __ bind(&done);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode) {
+ __ mov(a0, result_register());
+ __ pop(a1);
+ BinaryOpStub stub(op, mode);
+ JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code.
+ CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET,
+ expr->BinaryOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitAssignment(Expression* expr) {
+ // Invalid left-hand sides are rewritten by the parser to have a 'throw
+ // ReferenceError' on the left-hand side.
+ if (!expr->IsValidLeftHandSide()) {
+ VisitForEffect(expr);
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->AsProperty();
+ if (prop != NULL) {
+ assign_type = (prop->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ switch (assign_type) {
+ case VARIABLE: {
+ Variable* var = expr->AsVariableProxy()->var();
+ EffectContext context(this);
+ EmitVariableAssignment(var, Token::ASSIGN);
+ break;
+ }
+ case NAMED_PROPERTY: {
+ __ push(result_register()); // Preserve value.
+ VisitForAccumulatorValue(prop->obj());
+ __ mov(a1, result_register());
+ __ pop(a0); // Restore value.
+ __ li(a2, Operand(prop->key()->AsLiteral()->value()));
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic);
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ push(result_register()); // Preserve value.
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ __ mov(a1, result_register());
+ __ pop(a2);
+ __ pop(a0); // Restore value.
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic);
+ break;
+ }
+ }
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitVariableAssignment(Variable* var,
+ Token::Value op) {
+ if (var->IsUnallocated()) {
+ // Global var, const, or let.
+ __ mov(a0, result_register());
+ __ li(a2, Operand(var->name()));
+ __ lw(a1, GlobalObjectOperand());
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+
+ } else if (op == Token::INIT_CONST) {
+ // Const initializers need a write barrier.
+ ASSERT(!var->IsParameter()); // No const parameters.
+ if (var->IsStackLocal()) {
+ Label skip;
+ __ lw(a1, StackOperand(var));
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ __ Branch(&skip, ne, a1, Operand(t0));
+ __ sw(result_register(), StackOperand(var));
+ __ bind(&skip);
+ } else {
+ ASSERT(var->IsContextSlot() || var->IsLookupSlot());
+ // Like var declarations, const declarations are hoisted to function
+ // scope. However, unlike var initializers, const initializers are
+ // able to drill a hole to that function context, even from inside a
+ // 'with' context. We thus bypass the normal static scope lookup for
+ // var->IsContextSlot().
+ __ push(v0);
+ __ li(a0, Operand(var->name()));
+ __ Push(cp, a0); // Context and name.
+ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
+ }
+
+ } 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(language_mode())));
+ __ Push(cp, a1, a0); // Context, name, strict mode.
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ } else {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ Label assign;
+ MemOperand location = VarOperand(var, a1);
+ __ lw(a3, location);
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ __ Branch(&assign, ne, a3, Operand(t0));
+ __ li(a3, Operand(var->name()));
+ __ push(a3);
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ // Perform the assignment.
+ __ bind(&assign);
+ __ sw(result_register(), location);
+ if (var->IsContextSlot()) {
+ // RecordWrite may destroy all its register arguments.
+ __ mov(a3, result_register());
+ int offset = Context::SlotOffset(var->index());
+ __ RecordWriteContextSlot(
+ a1, offset, a3, a2, kRAHasBeenSaved, kDontSaveFPRegs);
+ }
+ }
+
+ } 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 (generate_debug_code_ && op == Token::INIT_LET) {
+ // Check for an uninitialized let binding.
+ __ lw(a2, location);
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ __ Check(eq, kLetBindingReInitialization, a2, Operand(t0));
+ }
+ // Perform the assignment.
+ __ sw(v0, location);
+ if (var->IsContextSlot()) {
+ __ mov(a3, v0);
+ 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(language_mode())));
+ __ Push(cp, a1, a0); // Context, name, strict mode.
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ }
+ }
+ // Non-initializing assignments to consts are ignored.
+}
+
+
+void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
+ // Assignment to a property, using a named store IC.
+ Property* prop = expr->target()->AsProperty();
+ ASSERT(prop != NULL);
+ ASSERT(prop->key()->AsLiteral() != NULL);
+
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ __ mov(a0, result_register()); // Load the value.
+ __ li(a2, Operand(prop->key()->AsLiteral()->value()));
+ __ pop(a1);
+
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
+
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
+ // Assignment to a property, using a keyed store IC.
+
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ // Call keyed store IC.
+ // The arguments are:
+ // - a0 is the value,
+ // - a1 is the key,
+ // - a2 is the receiver.
+ __ mov(a0, result_register());
+ __ pop(a1); // Key.
+ __ pop(a2);
+
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
+
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::VisitProperty(Property* expr) {
+ Comment cmnt(masm_, "[ Property");
+ Expression* key = expr->key();
+
+ if (key->IsPropertyName()) {
+ VisitForAccumulatorValue(expr->obj());
+ EmitNamedPropertyLoad(expr);
+ PrepareForBailoutForId(expr->LoadId(), TOS_REG);
+ context()->Plug(v0);
+ } else {
+ VisitForStackValue(expr->obj());
+ VisitForAccumulatorValue(expr->key());
+ __ pop(a1);
+ EmitKeyedPropertyLoad(expr);
+ context()->Plug(v0);
+ }
+}
+
+
+void FullCodeGenerator::CallIC(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId id) {
+ ic_total_count_++;
+ __ Call(code, rmode, id);
+}
+
+
+void FullCodeGenerator::EmitCallWithIC(Call* expr,
+ Handle<Object> name,
+ RelocInfo::Mode mode) {
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ __ li(a2, Operand(name));
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ CallIC(ic, mode, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
+ Expression* key) {
+ // Load the key.
+ VisitForAccumulatorValue(key);
+
+ // Swap the name of the function and the receiver on the stack to follow
+ // the calling convention for call ICs.
+ __ pop(a1);
+ __ push(v0);
+ __ push(a1);
+
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
+ __ lw(a2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key.
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, v0); // Drop the key still on the stack.
+}
+
+
+void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
+ // Code common for calls using the call stub.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+
+ // Record call targets.
+ flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<Cell> cell = isolate()->factory()->NewCell(uninitialized);
+ RecordTypeFeedbackCell(expr->CallFeedbackId(), cell);
+ __ li(a2, Operand(cell));
+
+ CallFunctionStub stub(arg_count, flags);
+ __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ CallStub(&stub, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, v0);
+}
+
+
+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));
+ } else {
+ __ LoadRoot(a1, Heap::kUndefinedValueRootIndex);
+ }
+ __ push(a1);
+
+ // 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 language mode.
+ __ li(a1, Operand(Smi::FromInt(language_mode())));
+ __ push(a1);
+
+ // 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);
+}
+
+
+void FullCodeGenerator::VisitCall(Call* expr) {
+#ifdef DEBUG
+ // We want to verify that RecordJSReturnSite gets called on all paths
+ // through this function. Avoid early returns.
+ expr->return_is_recorded_ = false;
+#endif
+
+ Comment cmnt(masm_, "[ Call");
+ Expression* callee = expr->expression();
+ VariableProxy* proxy = callee->AsVariableProxy();
+ Property* property = callee->AsProperty();
+
+ if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
+ // In a call to eval, we first call %ResolvePossiblyDirectEval to
+ // resolve the function we need to call and the receiver of the
+ // call. Then we call the resolved function using the given
+ // arguments.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+
+ { PreservePositionScope pos_scope(masm()->positions_recorder());
+ VisitForStackValue(callee);
+ __ LoadRoot(a2, Heap::kUndefinedValueRootIndex);
+ __ push(a2); // Reserved receiver slot.
+
+ // Push the arguments.
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Push a copy of the function (found below the arguments) and
+ // resolve eval.
+ __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ push(a1);
+ 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.
+ __ sw(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ sw(v1, MemOperand(sp, arg_count * kPointerSize));
+ }
+ // 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.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, v0);
+ } else if (proxy != NULL && proxy->var()->IsUnallocated()) {
+ // Push global object as receiver for the call IC.
+ __ lw(a0, GlobalObjectOperand());
+ __ push(a0);
+ EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
+ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
+ // Call to a lookup slot (dynamically introduced variable).
+ Label slow, done;
+
+ { PreservePositionScope scope(masm()->positions_recorder());
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done);
+ }
+
+ __ bind(&slow);
+ // Call the runtime to find the function to call (returned in v0)
+ // and the object holding it (returned in v1).
+ __ push(context_register());
+ __ li(a2, Operand(proxy->name()));
+ __ push(a2);
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ Push(v0, v1); // Function, receiver.
+
+ // If fast case code has been generated, emit code to push the
+ // function and receiver and have the slow path jump around this
+ // code.
+ if (done.is_linked()) {
+ Label call;
+ __ Branch(&call);
+ __ bind(&done);
+ // Push function.
+ __ push(v0);
+ // The receiver is implicitly the global receiver. Indicate this
+ // by passing the hole to the call function stub.
+ __ LoadRoot(a1, Heap::kTheHoleValueRootIndex);
+ __ push(a1);
+ __ bind(&call);
+ }
+
+ // The receiver is either the global receiver or an object found
+ // by LoadContextSlot. That object could be the hole if the
+ // receiver is implicitly the global object.
+ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
+ } else if (property != NULL) {
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(property->obj());
+ }
+ if (property->key()->IsPropertyName()) {
+ EmitCallWithIC(expr,
+ property->key()->AsLiteral()->value(),
+ RelocInfo::CODE_TARGET);
+ } else {
+ EmitKeyedCallWithIC(expr, property->key());
+ }
+ } else {
+ // Call to an arbitrary expression not handled specially above.
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(callee);
+ }
+ // Load global receiver object.
+ __ lw(a1, GlobalObjectOperand());
+ __ lw(a1, FieldMemOperand(a1, GlobalObject::kGlobalReceiverOffset));
+ __ push(a1);
+ // Emit function call.
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ }
+
+#ifdef DEBUG
+ // RecordJSReturnSite should have been called.
+ ASSERT(expr->return_is_recorded_);
+#endif
+}
+
+
+void FullCodeGenerator::VisitCallNew(CallNew* expr) {
+ Comment cmnt(masm_, "[ CallNew");
+ // According to ECMA-262, section 11.2.2, page 44, the function
+ // expression in new calls must be evaluated before the
+ // arguments.
+
+ // Push constructor on the stack. If it's not a function it's used as
+ // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
+ // ignored.
+ VisitForStackValue(expr->expression());
+
+ // Push the arguments ("left-to-right") on the stack.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Call the construct call builtin that handles allocation and
+ // constructor invocation.
+ SetSourcePosition(expr->position());
+
+ // Load function and argument count into a1 and a0.
+ __ li(a0, Operand(arg_count));
+ __ lw(a1, MemOperand(sp, arg_count * kPointerSize));
+
+ // Record call targets in unoptimized code.
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<Cell> cell = isolate()->factory()->NewCell(uninitialized);
+ RecordTypeFeedbackCell(expr->CallNewFeedbackId(), cell);
+ __ li(a2, Operand(cell));
+
+ CallConstructStub stub(RECORD_CALL_TARGET);
+ __ Call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL);
+ PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ And(t0, v0, Operand(kSmiTagMask));
+ Split(eq, t0, Operand(zero_reg), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ 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);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsObject(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(v0, if_false);
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ __ Branch(if_true, eq, v0, Operand(at));
+ __ lw(a2, FieldMemOperand(v0, HeapObject::kMapOffset));
+ // Undetectable objects behave like undefined when tested with typeof.
+ __ lbu(a1, FieldMemOperand(a2, Map::kBitFieldOffset));
+ __ And(at, a1, Operand(1 << Map::kIsUndetectable));
+ __ 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(expr, true, if_true, if_false);
+ Split(le, a1, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE),
+ if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, a1);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(ge, a1, Operand(FIRST_SPEC_OBJECT_TYPE),
+ if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(v0, if_false);
+ __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ lbu(a1, FieldMemOperand(a1, Map::kBitFieldOffset));
+ __ And(at, a1, Operand(1 << Map::kIsUndetectable));
+ 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);
+}
+
+
+void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
+ CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ AssertNotSmi(v0);
+
+ __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(a1, Map::kBitField2Offset));
+ __ And(t0, t0, 1 << Map::kStringWrapperSafeForDefaultValueOf);
+ __ Branch(if_true, ne, t0, Operand(zero_reg));
+
+ // Check for fast case object. Generate false result for slow case object.
+ __ lw(a2, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ lw(a2, FieldMemOperand(a2, HeapObject::kMapOffset));
+ __ LoadRoot(t0, Heap::kHashTableMapRootIndex);
+ __ Branch(if_false, eq, a2, Operand(t0));
+
+ // Look for valueOf name in the descriptor array, and indicate false if
+ // found. Since we omit an enumeration index check, if it is added via a
+ // transition that shares its descriptor array, this is a false positive.
+ Label entry, loop, done;
+
+ // Skip loop if no descriptors are valid.
+ __ NumberOfOwnDescriptors(a3, a1);
+ __ Branch(&done, eq, a3, Operand(zero_reg));
+
+ __ LoadInstanceDescriptors(a1, t0);
+ // t0: descriptor array.
+ // a3: valid entries in the descriptor array.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kPointerSize == 4);
+ __ li(at, Operand(DescriptorArray::kDescriptorSize));
+ __ Mul(a3, a3, at);
+ // Calculate location of the first key name.
+ __ Addu(t0, t0, Operand(DescriptorArray::kFirstOffset - kHeapObjectTag));
+ // Calculate the end of the descriptor array.
+ __ mov(a2, t0);
+ __ sll(t1, a3, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a2, a2, t1);
+
+ // Loop through all the keys in the descriptor array. If one of these is the
+ // string "valueOf" the result is false.
+ // The use of t2 to store the valueOf string assumes that it is not otherwise
+ // used in the loop below.
+ __ li(t2, Operand(isolate()->factory()->value_of_string()));
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ lw(a3, MemOperand(t0, 0));
+ __ Branch(if_false, eq, a3, Operand(t2));
+ __ Addu(t0, t0, Operand(DescriptorArray::kDescriptorSize * kPointerSize));
+ __ bind(&entry);
+ __ Branch(&loop, ne, t0, Operand(a2));
+
+ __ bind(&done);
+ // If a valueOf property is not found on the object check that its
+ // prototype is the un-modified String prototype. If not result is false.
+ __ lw(a2, FieldMemOperand(a1, Map::kPrototypeOffset));
+ __ JumpIfSmi(a2, if_false);
+ __ lw(a2, FieldMemOperand(a2, HeapObject::kMapOffset));
+ __ lw(a3, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+ __ lw(a3, FieldMemOperand(a3, GlobalObject::kNativeContextOffset));
+ __ lw(a3, ContextOperand(a3, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
+ __ Branch(if_false, ne, a2, Operand(a3));
+
+ // Set the bit in the map to indicate that it has been checked safe for
+ // default valueOf and set true result.
+ __ lbu(a2, FieldMemOperand(a1, Map::kBitField2Offset));
+ __ Or(a2, a2, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ sb(a2, FieldMemOperand(a1, Map::kBitField2Offset));
+ __ jmp(if_true);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, a2);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ Branch(if_true, eq, a2, Operand(JS_FUNCTION_TYPE));
+ __ Branch(if_false);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, a1);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, a1, Operand(JS_ARRAY_TYPE),
+ if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, a1);
+ 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(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+
+ 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);
+
+ // Get the frame pointer for the calling frame.
+ __ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+
+ // Skip the arguments adaptor frame if it exists.
+ Label check_frame_marker;
+ __ lw(a1, MemOperand(a2, StandardFrameConstants::kContextOffset));
+ __ Branch(&check_frame_marker, ne,
+ a1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ lw(a2, MemOperand(a2, StandardFrameConstants::kCallerFPOffset));
+
+ // Check the marker in the calling frame.
+ __ bind(&check_frame_marker);
+ __ lw(a1, MemOperand(a2, StandardFrameConstants::kMarkerOffset));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(eq, a1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)),
+ if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ // Load the two objects into registers and perform the comparison.
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ 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);
+
+ __ pop(a1);
+ 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(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ // ArgumentsAccessStub expects the key in a1 and the formal
+ // parameter count in a0.
+ VisitForAccumulatorValue(args->at(0));
+ __ mov(a1, v0);
+ __ li(a0, Operand(Smi::FromInt(info_->scope()->num_parameters())));
+ ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
+ __ CallStub(&stub);
+ context()->Plug(v0);
+}
+
+
+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())));
+
+ // Check if the calling frame is an arguments adaptor frame.
+ __ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ lw(a3, MemOperand(a2, StandardFrameConstants::kContextOffset));
+ __ Branch(&exit, ne, a3,
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Arguments adaptor case: Read the arguments length from the
+ // adaptor frame.
+ __ lw(v0, MemOperand(a2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ __ bind(&exit);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitClassOf(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ Label done, null, function, non_function_constructor;
+
+ VisitForAccumulatorValue(args->at(0));
+
+ // If the object is a smi, we return null.
+ __ JumpIfSmi(v0, &null);
+
+ // 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));
+
+ 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 JS function.
+ __ lw(v0, FieldMemOperand(v0, Map::kConstructorOffset));
+ __ GetObjectType(v0, a1, a1);
+ __ Branch(&non_function_constructor, ne, a1, Operand(JS_FUNCTION_TYPE));
+
+ // v0 now contains the constructor function. Grab the
+ // instance class name from there.
+ __ lw(v0, FieldMemOperand(v0, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(v0, FieldMemOperand(v0, SharedFunctionInfo::kInstanceClassNameOffset));
+ __ Branch(&done);
+
+ // Functions have class 'Function'.
+ __ bind(&function);
+ __ LoadRoot(v0, Heap::kfunction_class_stringRootIndex);
+ __ jmp(&done);
+
+ // Objects with a non-function constructor have class 'Object'.
+ __ bind(&non_function_constructor);
+ __ LoadRoot(v0, Heap::kObject_stringRootIndex);
+ __ jmp(&done);
+
+ // Non-JS objects have class null.
+ __ bind(&null);
+ __ LoadRoot(v0, Heap::kNullValueRootIndex);
+
+ // All done.
+ __ bind(&done);
+
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitLog(CallRuntime* expr) {
+ // Conditionally generate a log call.
+ // Args:
+ // 0 (literal string): The type of logging (corresponds to the flags).
+ // This is used to determine whether or not to generate the log call.
+ // 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);
+ }
+
+ // Finally, we're expected to leave a value on the top of the stack.
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+ Label slow_allocate_heapnumber;
+ Label heapnumber_allocated;
+
+ // Save the new heap number in callee-saved register s0, since
+ // we call out to external C code below.
+ __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(s0, a1, a2, t6, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+
+ // Allocate a heap number.
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ mov(s0, v0); // Save result in s0, so it is saved thru CFunc call.
+
+ __ bind(&heapnumber_allocated);
+
+ // Convert 32 random bits in v0 to 0.(32 random bits) in a double
+ // by computing:
+ // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
+ __ PrepareCallCFunction(1, a0);
+ __ lw(a0, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+ __ lw(a0, FieldMemOperand(a0, GlobalObject::kNativeContextOffset));
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
+
+ // 0x41300000 is the top half of 1.0 x 2^20 as a double.
+ __ li(a1, Operand(0x41300000));
+ // Move 0x41300000xxxxxxxx (x = random bits in v0) to FPU.
+ __ Move(f12, v0, a1);
+ // Move 0x4130000000000000 to FPU.
+ __ Move(f14, zero_reg, a1);
+ // Subtract and store the result in the heap number.
+ __ sub_d(f0, f12, f14);
+ __ sdc1(f0, FieldMemOperand(s0, HeapNumber::kValueOffset));
+ __ mov(v0, s0);
+
+ context()->Plug(v0);
+}
+
+
+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);
+ context()->Plug(v0);
+}
+
+
+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);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitValueOf(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label done;
+ // If the object is a smi return the object.
+ __ JumpIfSmi(v0, &done);
+ // If the object is not a value type, return the object.
+ __ GetObjectType(v0, a1, a1);
+ __ Branch(&done, ne, a1, Operand(JS_VALUE_TYPE));
+
+ __ lw(v0, FieldMemOperand(v0, JSValue::kValueOffset));
+
+ __ bind(&done);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ ASSERT_NE(NULL, args->at(1)->AsLiteral());
+ Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->value()));
+
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label runtime, done, not_date_object;
+ Register object = v0;
+ Register result = v0;
+ Register scratch0 = t5;
+ Register scratch1 = a1;
+
+ __ JumpIfSmi(object, &not_date_object);
+ __ GetObjectType(object, scratch1, scratch1);
+ __ Branch(&not_date_object, ne, scratch1, Operand(JS_DATE_TYPE));
+
+ if (index->value() == 0) {
+ __ lw(result, FieldMemOperand(object, JSDate::kValueOffset));
+ __ jmp(&done);
+ } else {
+ if (index->value() < JSDate::kFirstUncachedField) {
+ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+ __ li(scratch1, Operand(stamp));
+ __ lw(scratch1, MemOperand(scratch1));
+ __ lw(scratch0, FieldMemOperand(object, JSDate::kCacheStampOffset));
+ __ Branch(&runtime, ne, scratch1, Operand(scratch0));
+ __ lw(result, FieldMemOperand(object, JSDate::kValueOffset +
+ kPointerSize * index->value()));
+ __ jmp(&done);
+ }
+ __ bind(&runtime);
+ __ PrepareCallCFunction(2, scratch1);
+ __ li(a1, Operand(index));
+ __ Move(a0, object);
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ jmp(&done);
+ }
+
+ __ bind(&not_date_object);
+ __ CallRuntime(Runtime::kThrowNotDateError, 0);
+ __ bind(&done);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitSeqStringSetCharCheck(Register string,
+ Register index,
+ Register value,
+ uint32_t encoding_mask) {
+ __ And(at, index, Operand(kSmiTagMask));
+ __ Check(eq, kNonSmiIndex, at, Operand(zero_reg));
+ __ And(at, value, Operand(kSmiTagMask));
+ __ Check(eq, kNonSmiValue, at, Operand(zero_reg));
+
+ __ lw(at, FieldMemOperand(string, String::kLengthOffset));
+ __ Check(lt, kIndexIsTooLarge, index, Operand(at));
+
+ __ Check(ge, kIndexIsNegative, index, Operand(zero_reg));
+
+ __ lw(at, FieldMemOperand(string, HeapObject::kMapOffset));
+ __ lbu(at, FieldMemOperand(at, Map::kInstanceTypeOffset));
+
+ __ And(at, at, Operand(kStringRepresentationMask | kStringEncodingMask));
+ __ Subu(at, at, Operand(encoding_mask));
+ __ Check(eq, kUnexpectedStringType, at, Operand(zero_reg));
+}
+
+
+void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(3, args->length());
+
+ Register string = v0;
+ Register index = a1;
+ Register value = a2;
+
+ VisitForStackValue(args->at(1)); // index
+ VisitForStackValue(args->at(2)); // value
+ __ pop(value);
+ __ pop(index);
+ VisitForAccumulatorValue(args->at(0)); // string
+
+ if (FLAG_debug_code) {
+ static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
+ EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
+ }
+
+ __ SmiUntag(value, value);
+ __ Addu(at,
+ string,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ SmiUntag(index);
+ __ Addu(at, at, index);
+ __ sb(value, MemOperand(at));
+ context()->Plug(string);
+}
+
+
+void FullCodeGenerator::EmitTwoByteSeqStringSetChar(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(3, args->length());
+
+ Register string = v0;
+ Register index = a1;
+ Register value = a2;
+
+ VisitForStackValue(args->at(1)); // index
+ VisitForStackValue(args->at(2)); // value
+ __ pop(value);
+ __ pop(index);
+ VisitForAccumulatorValue(args->at(0)); // string
+
+ if (FLAG_debug_code) {
+ static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
+ EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
+ }
+
+ __ SmiUntag(value, value);
+ __ Addu(at,
+ string,
+ Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ __ Addu(at, at, index);
+ STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
+ __ sh(value, MemOperand(at));
+ context()->Plug(string);
+}
+
+
+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::ON_STACK);
+ __ CallStub(&stub);
+ context()->Plug(v0);
+}
+
+
+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(a1); // v0 = value. a1 = object.
+
+ Label done;
+ // If the object is a smi, return the value.
+ __ JumpIfSmi(a1, &done);
+
+ // If the object is not a value type, return the value.
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&done, ne, a2, Operand(JS_VALUE_TYPE));
+
+ // Store the value.
+ __ 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.
+ __ mov(a2, v0);
+ __ RecordWriteField(
+ a1, JSValue::kValueOffset, a2, a3, kRAHasBeenSaved, kDontSaveFPRegs);
+
+ __ bind(&done);
+ context()->Plug(v0);
+}
+
+
+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));
+
+ NumberToStringStub stub;
+ __ CallStub(&stub);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label done;
+ StringCharFromCodeGenerator generator(v0, a1);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(a1);
+}
+
+
+void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+ __ mov(a0, result_register());
+
+ Register object = a1;
+ Register index = a0;
+ Register result = v0;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharCodeAtGenerator generator(object,
+ index,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ LoadRoot(result, Heap::kNanValueRootIndex);
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Load the undefined value into the result register, which will
+ // trigger conversion.
+ __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+ __ mov(a0, result_register());
+
+ Register object = a1;
+ Register index = a0;
+ Register scratch = a3;
+ Register result = v0;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharAtGenerator generator(object,
+ index,
+ scratch,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // the empty string.
+ __ LoadRoot(result, Heap::kempty_stringRootIndex);
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ li(result, Operand(Smi::FromInt(0)));
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(STRING_ADD_CHECK_BOTH);
+ __ CallStub(&stub);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringCompareStub stub;
+ __ CallStub(&stub);
+ context()->Plug(v0);
+}
+
+
+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.
+ __ CallStub(&stub);
+ context()->Plug(v0);
+}
+
+
+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.
+ __ CallStub(&stub);
+ context()->Plug(v0);
+}
+
+
+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.
+ __ CallStub(&stub);
+ context()->Plug(v0);
+}
+
+
+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);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() >= 2);
+
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ VisitForAccumulatorValue(args->last()); // Function.
+
+ Label runtime, done;
+ // Check for non-function argument (including proxy).
+ __ JumpIfSmi(v0, &runtime);
+ __ GetObjectType(v0, a1, a1);
+ __ Branch(&runtime, ne, a1, Operand(JS_FUNCTION_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(&runtime);
+ __ push(v0);
+ __ CallRuntime(Runtime::kCall, args->length());
+ __ bind(&done);
+
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) {
+ 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);
+ context()->Plug(v0);
+}
+
+
+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()->value()))->value();
+
+ Handle<FixedArray> jsfunction_result_caches(
+ isolate()->native_context()->jsfunction_result_caches());
+ if (jsfunction_result_caches->length() <= cache_id) {
+ __ Abort(kAttemptToUseUndefinedCache);
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ context()->Plug(v0);
+ return;
+ }
+
+ VisitForAccumulatorValue(args->at(1));
+
+ Register key = v0;
+ Register cache = a1;
+ __ lw(cache, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+ __ lw(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset));
+ __ lw(cache,
+ ContextOperand(
+ cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+ __ lw(cache,
+ FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
+
+
+ Label done, not_found;
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ lw(a2, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset));
+ // a2 now holds finger offset as a smi.
+ __ Addu(a3, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // a3 now points to the start of fixed array elements.
+ __ sll(at, a2, kPointerSizeLog2 - kSmiTagSize);
+ __ addu(a3, a3, at);
+ // a3 now points to key of indexed element of cache.
+ __ lw(a2, MemOperand(a3));
+ __ Branch(&not_found, ne, key, Operand(a2));
+
+ __ lw(v0, MemOperand(a3, kPointerSize));
+ __ Branch(&done);
+
+ __ bind(&not_found);
+ // Call runtime to perform the lookup.
+ __ Push(cache, key);
+ __ CallRuntime(Runtime::kGetFromCache, 2);
+
+ __ bind(&done);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ Register right = v0;
+ Register left = a1;
+ Register tmp = a2;
+ Register tmp2 = a3;
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1)); // Result (right) in v0.
+ __ pop(left);
+
+ Label done, fail, ok;
+ __ Branch(&ok, eq, left, Operand(right));
+ // Fail if either is a non-HeapObject.
+ __ And(tmp, left, Operand(right));
+ __ JumpIfSmi(tmp, &fail);
+ __ lw(tmp, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ lbu(tmp2, FieldMemOperand(tmp, Map::kInstanceTypeOffset));
+ __ Branch(&fail, ne, tmp2, Operand(JS_REGEXP_TYPE));
+ __ lw(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ Branch(&fail, ne, tmp, Operand(tmp2));
+ __ lw(tmp, FieldMemOperand(left, JSRegExp::kDataOffset));
+ __ lw(tmp2, FieldMemOperand(right, JSRegExp::kDataOffset));
+ __ Branch(&ok, eq, tmp, Operand(tmp2));
+ __ bind(&fail);
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex);
+ __ jmp(&done);
+ __ bind(&ok);
+ __ LoadRoot(v0, Heap::kTrueValueRootIndex);
+ __ bind(&done);
+
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ lw(a0, FieldMemOperand(v0, String::kHashFieldOffset));
+ __ And(a0, a0, Operand(String::kContainsCachedArrayIndexMask));
+
+ 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(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ VisitForAccumulatorValue(args->at(0));
+
+ __ AssertString(v0);
+
+ __ lw(v0, FieldMemOperand(v0, String::kHashFieldOffset));
+ __ IndexFromHash(v0, v0);
+
+ context()->Plug(v0);
+}
+
+
+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));
+
+ // All aliases of the same register have disjoint lifetimes.
+ Register array = v0;
+ Register elements = no_reg; // Will be v0.
+ Register result = no_reg; // Will be v0.
+ Register separator = a1;
+ Register array_length = a2;
+ Register result_pos = no_reg; // Will be a2.
+ Register string_length = a3;
+ Register string = t0;
+ Register element = t1;
+ Register elements_end = t2;
+ Register scratch1 = t3;
+ Register scratch2 = t5;
+ Register scratch3 = t4;
+
+ // Separator operand is on the stack.
+ __ pop(separator);
+
+ // Check that the array is a JSArray.
+ __ JumpIfSmi(array, &bailout);
+ __ GetObjectType(array, scratch1, scratch2);
+ __ Branch(&bailout, ne, scratch2, Operand(JS_ARRAY_TYPE));
+
+ // Check that the array has fast elements.
+ __ CheckFastElements(scratch1, scratch2, &bailout);
+
+ // If the array has length zero, return the empty string.
+ __ lw(array_length, FieldMemOperand(array, JSArray::kLengthOffset));
+ __ SmiUntag(array_length);
+ __ Branch(&non_trivial_array, ne, array_length, Operand(zero_reg));
+ __ LoadRoot(v0, Heap::kempty_stringRootIndex);
+ __ Branch(&done);
+
+ __ bind(&non_trivial_array);
+
+ // Get the FixedArray containing array's elements.
+ elements = array;
+ __ lw(elements, FieldMemOperand(array, JSArray::kElementsOffset));
+ array = no_reg; // End of array's live range.
+
+ // Check that all array elements are sequential ASCII strings, and
+ // accumulate the sum of their lengths, as a smi-encoded value.
+ __ mov(string_length, zero_reg);
+ __ Addu(element,
+ elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(elements_end, array_length, kPointerSizeLog2);
+ __ Addu(elements_end, element, elements_end);
+ // Loop condition: while (element < elements_end).
+ // Live values in registers:
+ // elements: Fixed array of strings.
+ // array_length: Length of the fixed array of strings (not smi)
+ // separator: Separator string
+ // string_length: Accumulated sum of string lengths (smi).
+ // element: Current array element.
+ // elements_end: Array end.
+ if (generate_debug_code_) {
+ __ Assert(gt, kNoEmptyArraysHereInEmitFastAsciiArrayJoin,
+ array_length, Operand(zero_reg));
+ }
+ __ bind(&loop);
+ __ lw(string, MemOperand(element));
+ __ Addu(element, element, kPointerSize);
+ __ JumpIfSmi(string, &bailout);
+ __ lw(scratch1, FieldMemOperand(string, HeapObject::kMapOffset));
+ __ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout);
+ __ lw(scratch1, FieldMemOperand(string, SeqOneByteString::kLengthOffset));
+ __ AdduAndCheckForOverflow(string_length, string_length, scratch1, scratch3);
+ __ BranchOnOverflow(&bailout, scratch3);
+ __ Branch(&loop, lt, element, Operand(elements_end));
+
+ // If array_length is 1, return elements[0], a string.
+ __ Branch(&not_size_one_array, ne, array_length, Operand(1));
+ __ lw(v0, FieldMemOperand(elements, FixedArray::kHeaderSize));
+ __ Branch(&done);
+
+ __ bind(&not_size_one_array);
+
+ // Live values in registers:
+ // separator: Separator string
+ // array_length: Length of the array.
+ // string_length: Sum of string lengths (smi).
+ // elements: FixedArray of strings.
+
+ // Check that the separator is a flat ASCII string.
+ __ JumpIfSmi(separator, &bailout);
+ __ lw(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset));
+ __ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout);
+
+ // Add (separator length times array_length) - separator length to the
+ // string_length to get the length of the result string. array_length is not
+ // smi but the other values are, so the result is a smi.
+ __ lw(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
+ __ Subu(string_length, string_length, Operand(scratch1));
+ __ Mult(array_length, scratch1);
+ // Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
+ // zero.
+ __ mfhi(scratch2);
+ __ Branch(&bailout, ne, scratch2, Operand(zero_reg));
+ __ mflo(scratch2);
+ __ And(scratch3, scratch2, Operand(0x80000000));
+ __ Branch(&bailout, ne, scratch3, Operand(zero_reg));
+ __ AdduAndCheckForOverflow(string_length, string_length, scratch2, scratch3);
+ __ BranchOnOverflow(&bailout, scratch3);
+ __ SmiUntag(string_length);
+
+ // Get first element in the array to free up the elements register to be used
+ // for the result.
+ __ Addu(element,
+ elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ result = elements; // End of live range for elements.
+ elements = no_reg;
+ // Live values in registers:
+ // element: First array element
+ // separator: Separator string
+ // string_length: Length of result string (not smi)
+ // array_length: Length of the array.
+ __ AllocateAsciiString(result,
+ string_length,
+ scratch1,
+ scratch2,
+ elements_end,
+ &bailout);
+ // Prepare for looping. Set up elements_end to end of the array. Set
+ // result_pos to the position of the result where to write the first
+ // character.
+ __ sll(elements_end, array_length, kPointerSizeLog2);
+ __ Addu(elements_end, element, elements_end);
+ result_pos = array_length; // End of live range for array_length.
+ array_length = no_reg;
+ __ Addu(result_pos,
+ result,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+
+ // Check the length of the separator.
+ __ lw(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
+ __ li(at, Operand(Smi::FromInt(1)));
+ __ Branch(&one_char_separator, eq, scratch1, Operand(at));
+ __ Branch(&long_separator, gt, scratch1, Operand(at));
+
+ // Empty separator case.
+ __ bind(&empty_separator_loop);
+ // Live values in registers:
+ // result_pos: the position to which we are currently copying characters.
+ // element: Current array element.
+ // elements_end: Array end.
+
+ // Copy next array element to the result.
+ __ lw(string, MemOperand(element));
+ __ Addu(element, element, kPointerSize);
+ __ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ Addu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ // End while (element < elements_end).
+ __ Branch(&empty_separator_loop, lt, element, Operand(elements_end));
+ ASSERT(result.is(v0));
+ __ Branch(&done);
+
+ // One-character separator case.
+ __ bind(&one_char_separator);
+ // Replace separator with its ASCII character value.
+ __ lbu(separator, FieldMemOperand(separator, SeqOneByteString::kHeaderSize));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator.
+ __ jmp(&one_char_separator_loop_entry);
+
+ __ bind(&one_char_separator_loop);
+ // Live values in registers:
+ // 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).
+
+ // Copy the separator character to the result.
+ __ sb(separator, MemOperand(result_pos));
+ __ Addu(result_pos, result_pos, 1);
+
+ // Copy next array element to the result.
+ __ bind(&one_char_separator_loop_entry);
+ __ lw(string, MemOperand(element));
+ __ Addu(element, element, kPointerSize);
+ __ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ Addu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ // End while (element < elements_end).
+ __ Branch(&one_char_separator_loop, lt, element, Operand(elements_end));
+ ASSERT(result.is(v0));
+ __ Branch(&done);
+
+ // Long separator case (separator is more than one character). Entry is at the
+ // label long_separator below.
+ __ bind(&long_separator_loop);
+ // Live values in registers:
+ // result_pos: the position to which we are currently copying characters.
+ // element: Current array element.
+ // elements_end: Array end.
+ // separator: Separator string.
+
+ // Copy the separator to the result.
+ __ lw(string_length, FieldMemOperand(separator, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ Addu(string,
+ separator,
+ Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+
+ __ bind(&long_separator);
+ __ lw(string, MemOperand(element));
+ __ Addu(element, element, kPointerSize);
+ __ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ Addu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ // End while (element < elements_end).
+ __ Branch(&long_separator_loop, lt, element, Operand(elements_end));
+ ASSERT(result.is(v0));
+ __ Branch(&done);
+
+ __ bind(&bailout);
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ __ bind(&done);
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
+ Handle<String> name = expr->name();
+ if (name->length() > 0 && name->Get(0) == '_') {
+ Comment cmnt(masm_, "[ InlineRuntimeCall");
+ EmitInlineRuntimeCall(expr);
+ return;
+ }
+
+ Comment cmnt(masm_, "[ CallRuntime");
+ ZoneList<Expression*>* args = expr->arguments();
+
+ if (expr->is_jsruntime()) {
+ // Prepare for calling JS runtime function.
+ __ lw(a0, GlobalObjectOperand());
+ __ lw(a0, FieldMemOperand(a0, GlobalObject::kBuiltinsOffset));
+ __ push(a0);
+ }
+
+ // Push the arguments ("left-to-right").
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ if (expr->is_jsruntime()) {
+ // Call the JS runtime function.
+ __ li(a2, Operand(expr->name()));
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ CallIC(ic, mode, expr->CallRuntimeFeedbackId());
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ } else {
+ // Call the C runtime function.
+ __ CallRuntime(expr->function(), arg_count);
+ }
+ context()->Plug(v0);
+}
+
+
+void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
+ switch (expr->op()) {
+ case Token::DELETE: {
+ Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
+ Property* property = expr->expression()->AsProperty();
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+
+ if (property != NULL) {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ 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);
+ } else if (proxy != NULL) {
+ Variable* var = proxy->var();
+ // Delete of an unqualified identifier is disallowed in strict mode
+ // but "delete this" is allowed.
+ ASSERT(language_mode() == CLASSIC_MODE || var->is_this());
+ if (var->IsUnallocated()) {
+ __ lw(a2, GlobalObjectOperand());
+ __ li(a1, Operand(var->name()));
+ __ li(a0, Operand(Smi::FromInt(kNonStrictMode)));
+ __ Push(a2, a1, a0);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
+ context()->Plug(v0);
+ } else if (var->IsStackAllocated() || var->IsContextSlot()) {
+ // Result of deleting non-global, non-dynamic variables is false.
+ // The subexpression does not have side effects.
+ context()->Plug(var->is_this());
+ } else {
+ // Non-global variable. Call the runtime to try to delete from the
+ // context where the variable was introduced.
+ __ push(context_register());
+ __ li(a2, Operand(var->name()));
+ __ push(a2);
+ __ CallRuntime(Runtime::kDeleteContextSlot, 2);
+ context()->Plug(v0);
+ }
+ } else {
+ // Result of deleting non-property, non-variable reference is true.
+ // The subexpression may have side effects.
+ VisitForEffect(expr->expression());
+ context()->Plug(true);
+ }
+ break;
+ }
+
+ case Token::VOID: {
+ Comment cmnt(masm_, "[ UnaryOperation (VOID)");
+ VisitForEffect(expr->expression());
+ context()->Plug(Heap::kUndefinedValueRootIndex);
+ break;
+ }
+
+ case Token::NOT: {
+ Comment cmnt(masm_, "[ UnaryOperation (NOT)");
+ if (context()->IsEffect()) {
+ // 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 {
+ // 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;
+ }
+
+ case Token::TYPEOF: {
+ Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
+ { StackValueContext context(this);
+ VisitForTypeofValue(expr->expression());
+ }
+ __ CallRuntime(Runtime::kTypeof, 1);
+ context()->Plug(v0);
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
+ Comment cmnt(masm_, "[ CountOperation");
+ SetSourcePosition(expr->position());
+
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // as the left-hand side.
+ if (!expr->expression()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->expression());
+ return;
+ }
+
+ // Expression can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->expression()->AsProperty();
+ // In case of a property we use the uninitialized expression context
+ // of the key to detect a named property.
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate expression and get value.
+ if (assign_type == VARIABLE) {
+ ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
+ AccumulatorValueContext context(this);
+ EmitVariableLoad(expr->expression()->AsVariableProxy());
+ } else {
+ // Reserve space for result of postfix operation.
+ if (expr->is_postfix() && !context()->IsEffect()) {
+ __ li(at, Operand(Smi::FromInt(0)));
+ __ push(at);
+ }
+ if (assign_type == NAMED_PROPERTY) {
+ // Put the object both on the stack and in the accumulator.
+ VisitForAccumulatorValue(prop->obj());
+ __ push(v0);
+ EmitNamedPropertyLoad(prop);
+ } else {
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ __ lw(a1, MemOperand(sp, 0));
+ __ push(v0);
+ EmitKeyedPropertyLoad(prop);
+ }
+ }
+
+ // We need a second deoptimization point after loading the value
+ // in case evaluating the property load my have a side effect.
+ if (assign_type == VARIABLE) {
+ PrepareForBailout(expr->expression(), TOS_REG);
+ } else {
+ PrepareForBailoutForId(prop->LoadId(), TOS_REG);
+ }
+
+ // Call ToNumber only if operand is not a smi.
+ Label no_conversion;
+ if (ShouldInlineSmiCase(expr->op())) {
+ __ JumpIfSmi(v0, &no_conversion);
+ }
+ __ mov(a0, v0);
+ ToNumberStub convert_stub;
+ __ CallStub(&convert_stub);
+ __ bind(&no_conversion);
+
+ // Save result for postfix expressions.
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ // Save the result on the stack. If we have a named or keyed property
+ // we store the result under the receiver that is currently on top
+ // of the stack.
+ switch (assign_type) {
+ case VARIABLE:
+ __ push(v0);
+ break;
+ case NAMED_PROPERTY:
+ __ sw(v0, MemOperand(sp, kPointerSize));
+ break;
+ case KEYED_PROPERTY:
+ __ sw(v0, MemOperand(sp, 2 * kPointerSize));
+ break;
+ }
+ }
+ }
+ __ mov(a0, result_register());
+
+ // Inline smi case if we are in a loop.
+ Label stub_call, done;
+ JumpPatchSite patch_site(masm_);
+
+ int count_value = expr->op() == Token::INC ? 1 : -1;
+ if (ShouldInlineSmiCase(expr->op())) {
+ __ li(a1, Operand(Smi::FromInt(count_value)));
+ __ AdduAndCheckForOverflow(v0, a0, a1, t0);
+ __ BranchOnOverflow(&stub_call, t0); // Do stub on overflow.
+
+ // We could eliminate this smi check if we split the code at
+ // the first smi check before calling ToNumber.
+ patch_site.EmitJumpIfSmi(v0, &done);
+ __ bind(&stub_call);
+ }
+ __ mov(a1, a0);
+ __ li(a0, Operand(Smi::FromInt(count_value)));
+
+ // Record position before stub call.
+ SetSourcePosition(expr->position());
+
+ BinaryOpStub stub(Token::ADD, NO_OVERWRITE);
+ CallIC(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ expr->CountBinOpFeedbackId());
+ patch_site.EmitPatchInfo();
+ __ bind(&done);
+
+ // Store the value returned in v0.
+ switch (assign_type) {
+ case VARIABLE:
+ if (expr->is_postfix()) {
+ { EffectContext context(this);
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context.Plug(v0);
+ }
+ // For all contexts except EffectConstant we have the result on
+ // top of the stack.
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(v0);
+ }
+ break;
+ case NAMED_PROPERTY: {
+ __ mov(a0, result_register()); // Value.
+ __ li(a2, Operand(prop->key()->AsLiteral()->value())); // Name.
+ __ pop(a1); // Receiver.
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(v0);
+ }
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ mov(a0, result_register()); // Value.
+ __ pop(a1); // Key.
+ __ pop(a2); // Receiver.
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(v0);
+ }
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
+ ASSERT(!context()->IsEffect());
+ ASSERT(!context()->IsTest());
+ VariableProxy* proxy = expr->AsVariableProxy();
+ if (proxy != NULL && proxy->var()->IsUnallocated()) {
+ Comment cmnt(masm_, "Global variable");
+ __ lw(a0, GlobalObjectOperand());
+ __ li(a2, Operand(proxy->name()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ // Use a regular load, not a contextual load, to avoid a reference
+ // error.
+ CallIC(ic);
+ PrepareForBailout(expr, TOS_REG);
+ context()->Plug(v0);
+ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
+ Label done, slow;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done);
+
+ __ bind(&slow);
+ __ li(a0, Operand(proxy->name()));
+ __ Push(cp, a0);
+ __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ PrepareForBailout(expr, TOS_REG);
+ __ bind(&done);
+
+ context()->Plug(v0);
+ } else {
+ // This expression cannot throw a reference error at the top level.
+ VisitInDuplicateContext(expr);
+ }
+}
+
+void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
+ 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(sub_expr);
+ }
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+
+ if (check->Equals(isolate()->heap()->number_string())) {
+ __ JumpIfSmi(v0, if_true);
+ __ lw(v0, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHeapNumberMapRootIndex);
+ Split(eq, v0, Operand(at), if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->string_string())) {
+ __ JumpIfSmi(v0, if_false);
+ // Check for undetectable objects => false.
+ __ GetObjectType(v0, v0, a1);
+ __ Branch(if_false, ge, a1, Operand(FIRST_NONSTRING_TYPE));
+ __ lbu(a1, FieldMemOperand(v0, Map::kBitFieldOffset));
+ __ And(a1, a1, Operand(1 << Map::kIsUndetectable));
+ Split(eq, a1, Operand(zero_reg),
+ if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->symbol_string())) {
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, v0, a1);
+ Split(eq, a1, Operand(SYMBOL_TYPE), if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->boolean_string())) {
+ __ LoadRoot(at, Heap::kTrueValueRootIndex);
+ __ Branch(if_true, eq, v0, Operand(at));
+ __ LoadRoot(at, Heap::kFalseValueRootIndex);
+ Split(eq, v0, Operand(at), if_true, if_false, fall_through);
+ } else if (FLAG_harmony_typeof &&
+ check->Equals(isolate()->heap()->null_string())) {
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ Split(eq, v0, Operand(at), if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->undefined_string())) {
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(if_true, eq, v0, Operand(at));
+ __ JumpIfSmi(v0, if_false);
+ // Check for undetectable objects => true.
+ __ lw(v0, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ lbu(a1, FieldMemOperand(v0, Map::kBitFieldOffset));
+ __ And(a1, a1, Operand(1 << Map::kIsUndetectable));
+ Split(ne, a1, Operand(zero_reg), if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->function_string())) {
+ __ JumpIfSmi(v0, if_false);
+ 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_string())) {
+ __ JumpIfSmi(v0, if_false);
+ if (!FLAG_harmony_typeof) {
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ __ Branch(if_true, eq, v0, Operand(at));
+ }
+ // Check for JS objects => true.
+ __ GetObjectType(v0, v0, a1);
+ __ Branch(if_false, lt, a1, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ __ lbu(a1, FieldMemOperand(v0, Map::kInstanceTypeOffset));
+ __ Branch(if_false, gt, a1, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ // Check for undetectable objects => false.
+ __ lbu(a1, FieldMemOperand(v0, Map::kBitFieldOffset));
+ __ And(a1, a1, Operand(1 << Map::kIsUndetectable));
+ Split(eq, a1, Operand(zero_reg), if_true, if_false, fall_through);
+ } else {
+ if (if_false != fall_through) __ jmp(if_false);
+ }
+ context()->Plug(if_true, if_false);
+}
+
+
+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;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ Token::Value op = expr->op();
+ VisitForStackValue(expr->left());
+ switch (op) {
+ case Token::IN:
+ VisitForStackValue(expr->right());
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ LoadRoot(t0, Heap::kTrueValueRootIndex);
+ Split(eq, v0, Operand(t0), if_true, if_false, fall_through);
+ break;
+
+ case Token::INSTANCEOF: {
+ VisitForStackValue(expr->right());
+ InstanceofStub stub(InstanceofStub::kNoFlags);
+ __ CallStub(&stub);
+ 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;
+ }
+
+ default: {
+ VisitForAccumulatorValue(expr->right());
+ Condition cc = CompareIC::ComputeCondition(op);
+ __ mov(a0, result_register());
+ __ pop(a1);
+
+ bool inline_smi_code = ShouldInlineSmiCase(op);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ Or(a2, a0, Operand(a1));
+ patch_site.EmitJumpIfNotSmi(a2, &slow_case);
+ Split(cc, a1, Operand(a0), if_true, if_false, NULL);
+ __ bind(&slow_case);
+ }
+ // Record position and call the compare IC.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(cc, v0, Operand(zero_reg), if_true, if_false, fall_through);
+ }
+ }
+
+ // Convert the result of the comparison into one expected for this
+ // expression's context.
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr,
+ Expression* sub_expr,
+ NilValue nil) {
+ 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);
+
+ VisitForAccumulatorValue(sub_expr);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ mov(a0, result_register());
+ if (expr->op() == Token::EQ_STRICT) {
+ Heap::RootListIndex nil_value = nil == kNullValue ?
+ Heap::kNullValueRootIndex :
+ Heap::kUndefinedValueRootIndex;
+ __ LoadRoot(a1, nil_value);
+ Split(eq, a0, Operand(a1), if_true, if_false, fall_through);
+ } else {
+ Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(), nil);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ Split(ne, v0, Operand(zero_reg), if_true, if_false, fall_through);
+ }
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
+ __ lw(v0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ context()->Plug(v0);
+}
+
+
+Register FullCodeGenerator::result_register() {
+ return v0;
+}
+
+
+Register FullCodeGenerator::context_register() {
+ return cp;
+}
+
+
+void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
+ ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset);
+ __ sw(value, MemOperand(fp, frame_offset));
+}
+
+
+void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
+ __ lw(dst, ContextOperand(cp, context_index));
+}
+
+
+void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
+ Scope* declaration_scope = scope()->DeclarationScope();
+ if (declaration_scope->is_global_scope() ||
+ declaration_scope->is_module_scope()) {
+ // Contexts nested in the native context have a canonical empty function
+ // as their closure, not the anonymous closure containing the global
+ // code. Pass a smi sentinel and let the runtime look up the empty
+ // function.
+ __ li(at, Operand(Smi::FromInt(0)));
+ } else if (declaration_scope->is_eval_scope()) {
+ // Contexts created by a call to eval have the same closure as the
+ // context calling eval, not the anonymous closure containing the eval
+ // code. Fetch it from the context.
+ __ lw(at, ContextOperand(cp, Context::CLOSURE_INDEX));
+ } else {
+ ASSERT(declaration_scope->is_function_scope());
+ __ lw(at, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ }
+ __ push(at);
+}
+
+
+// ----------------------------------------------------------------------------
+// Non-local control flow support.
+
+void FullCodeGenerator::EnterFinallyBlock() {
+ ASSERT(!result_register().is(a1));
+ // Store result register while executing finally block.
+ __ push(result_register());
+ // Cook return address in link register to stack (smi encoded Code* delta).
+ __ Subu(a1, ra, Operand(masm_->CodeObject()));
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
+ STATIC_ASSERT(0 == kSmiTag);
+ __ Addu(a1, a1, Operand(a1)); // Convert to smi.
+
+ // Store result register while executing finally block.
+ __ push(a1);
+
+ // Store pending message while executing finally block.
+ ExternalReference pending_message_obj =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ __ li(at, Operand(pending_message_obj));
+ __ lw(a1, MemOperand(at));
+ __ push(a1);
+
+ ExternalReference has_pending_message =
+ ExternalReference::address_of_has_pending_message(isolate());
+ __ li(at, Operand(has_pending_message));
+ __ lw(a1, MemOperand(at));
+ __ SmiTag(a1);
+ __ push(a1);
+
+ ExternalReference pending_message_script =
+ ExternalReference::address_of_pending_message_script(isolate());
+ __ li(at, Operand(pending_message_script));
+ __ lw(a1, MemOperand(at));
+ __ push(a1);
+}
+
+
+void FullCodeGenerator::ExitFinallyBlock() {
+ ASSERT(!result_register().is(a1));
+ // Restore pending message from stack.
+ __ pop(a1);
+ ExternalReference pending_message_script =
+ ExternalReference::address_of_pending_message_script(isolate());
+ __ li(at, Operand(pending_message_script));
+ __ sw(a1, MemOperand(at));
+
+ __ pop(a1);
+ __ SmiUntag(a1);
+ ExternalReference has_pending_message =
+ ExternalReference::address_of_has_pending_message(isolate());
+ __ li(at, Operand(has_pending_message));
+ __ sw(a1, MemOperand(at));
+
+ __ pop(a1);
+ ExternalReference pending_message_obj =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ __ li(at, Operand(pending_message_obj));
+ __ sw(a1, MemOperand(at));
+
+ // Restore result register from stack.
+ __ pop(a1);
+
+ // Uncook return address and return.
+ __ pop(result_register());
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
+ __ sra(a1, a1, 1); // Un-smi-tag value.
+ __ Addu(at, a1, Operand(masm_->CodeObject()));
+ __ Jump(at);
+}
+
+
+#undef __
+
+#define __ ACCESS_MASM(masm())
+
+FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
+ int* stack_depth,
+ int* context_length) {
+ // The macros used here must preserve the result register.
+
+ // Because the handler block contains the context of the finally
+ // code, we can restore it directly from there for the finally code
+ // rather than iteratively unwinding contexts via their previous
+ // links.
+ __ Drop(*stack_depth); // Down to the handler block.
+ if (*context_length > 0) {
+ // Restore the context to its dedicated register and the stack.
+ __ lw(cp, MemOperand(sp, StackHandlerConstants::kContextOffset));
+ __ sw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ }
+ __ PopTryHandler();
+ __ Call(finally_entry_);
+
+ *stack_depth = 0;
+ *context_length = 0;
+ return previous_;
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/ic-mips.cc b/chromium/v8/src/mips/ic-mips.cc
new file mode 100644
index 00000000000..eb730bb3881
--- /dev/null
+++ b/chromium/v8/src/mips/ic-mips.cc
@@ -0,0 +1,1669 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "codegen.h"
+#include "code-stubs.h"
+#include "ic-inl.h"
+#include "runtime.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+
+// ----------------------------------------------------------------------------
+// Static IC stub generators.
+//
+
+#define __ ACCESS_MASM(masm)
+
+
+static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
+ Register type,
+ Label* global_object) {
+ // Register usage:
+ // type: holds the receiver instance type on entry.
+ __ Branch(global_object, eq, type, Operand(JS_GLOBAL_OBJECT_TYPE));
+ __ Branch(global_object, eq, type, Operand(JS_BUILTINS_OBJECT_TYPE));
+ __ Branch(global_object, eq, type, Operand(JS_GLOBAL_PROXY_TYPE));
+}
+
+
+// Generated code falls through if the receiver is a regular non-global
+// JS object with slow properties and no interceptors.
+static void GenerateNameDictionaryReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register elements,
+ Register scratch0,
+ Register scratch1,
+ Label* miss) {
+ // Register usage:
+ // receiver: holds the receiver on entry and is unchanged.
+ // elements: holds the property dictionary on fall through.
+ // Scratch registers:
+ // scratch0: used to holds the receiver map.
+ // scratch1: used to holds the receiver instance type, receiver bit mask
+ // and elements map.
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // Check that the receiver is a valid JS object.
+ __ GetObjectType(receiver, scratch0, scratch1);
+ __ Branch(miss, lt, scratch1, Operand(FIRST_SPEC_OBJECT_TYPE));
+
+ // If this assert fails, we have to check upper bound too.
+ STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
+
+ GenerateGlobalInstanceTypeCheck(masm, scratch1, miss);
+
+ // Check that the global object does not require access checks.
+ __ lbu(scratch1, FieldMemOperand(scratch0, Map::kBitFieldOffset));
+ __ And(scratch1, scratch1, Operand((1 << Map::kIsAccessCheckNeeded) |
+ (1 << Map::kHasNamedInterceptor)));
+ __ Branch(miss, ne, scratch1, Operand(zero_reg));
+
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ __ lw(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(scratch0, Heap::kHashTableMapRootIndex);
+ __ Branch(miss, ne, scratch1, Operand(scratch0));
+}
+
+
+// Helper function used from LoadIC/CallIC GenerateNormal.
+//
+// elements: Property dictionary. It is not clobbered if a jump to the miss
+// label is done.
+// name: Property name. It is not clobbered if a jump to the miss label is
+// done
+// result: Register for the result. It is only updated if a jump to the miss
+// label is not done. Can be the same as elements or name clobbering
+// one of these in the case of not jumping to the miss label.
+// The two scratch registers need to be different from elements, name and
+// result.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+// The address returned from GenerateStringDictionaryProbes() in scratch2
+// is used.
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register name,
+ Register result,
+ Register scratch1,
+ Register scratch2) {
+ // Main use of the scratch registers.
+ // scratch1: Used as temporary and to hold the capacity of the property
+ // dictionary.
+ // scratch2: Used as temporary.
+ Label done;
+
+ // Probe the dictionary.
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
+
+ // If probing finds an entry check that the value is a normal
+ // property.
+ __ bind(&done); // scratch2 == elements + 4 * index.
+ const int kElementsStartOffset = NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
+ __ And(at,
+ scratch1,
+ Operand(PropertyDetails::TypeField::kMask << kSmiTagSize));
+ __ Branch(miss, ne, at, Operand(zero_reg));
+
+ // Get the value at the masked, scaled index and return.
+ __ lw(result,
+ FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
+}
+
+
+// Helper function used from StoreIC::GenerateNormal.
+//
+// elements: Property dictionary. It is not clobbered if a jump to the miss
+// label is done.
+// name: Property name. It is not clobbered if a jump to the miss label is
+// done
+// value: The value to store.
+// The two scratch registers need to be different from elements, name and
+// result.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+// The address returned from GenerateStringDictionaryProbes() in scratch2
+// is used.
+static void GenerateDictionaryStore(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register name,
+ Register value,
+ Register scratch1,
+ Register scratch2) {
+ // Main use of the scratch registers.
+ // scratch1: Used as temporary and to hold the capacity of the property
+ // dictionary.
+ // scratch2: Used as temporary.
+ Label done;
+
+ // Probe the dictionary.
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
+
+ // If probing finds an entry in the dictionary check that the value
+ // is a normal property that is not read only.
+ __ bind(&done); // scratch2 == elements + 4 * index.
+ const int kElementsStartOffset = NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ const int kTypeAndReadOnlyMask =
+ (PropertyDetails::TypeField::kMask |
+ PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
+ __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
+ __ And(at, scratch1, Operand(kTypeAndReadOnlyMask));
+ __ Branch(miss, ne, at, Operand(zero_reg));
+
+ // Store the value at the masked, scaled index and return.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ Addu(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
+ __ sw(value, MemOperand(scratch2));
+
+ // Update the write barrier. Make sure not to clobber the value.
+ __ mov(scratch1, value);
+ __ RecordWrite(
+ elements, scratch2, scratch1, kRAHasNotBeenSaved, kDontSaveFPRegs);
+}
+
+
+// Checks the receiver for special cases (value type, slow case bits).
+// Falls through for regular JS object.
+static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register map,
+ Register scratch,
+ int interceptor_bit,
+ Label* slow) {
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, slow);
+ // Get the map of the receiver.
+ __ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ // Check bit field.
+ __ lbu(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ And(at, scratch, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
+ __ Branch(slow, ne, at, Operand(zero_reg));
+ // Check that the object is some kind of JS object EXCEPT JS Value type.
+ // In the case that the object is a value-wrapper object,
+ // we enter the runtime system to make sure that indexing into string
+ // objects work as intended.
+ ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
+ __ lbu(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ Branch(slow, lt, scratch, Operand(JS_OBJECT_TYPE));
+}
+
+
+// Loads an indexed element from a fast case array.
+// If not_fast_array is NULL, doesn't perform the elements map check.
+static void GenerateFastArrayLoad(MacroAssembler* masm,
+ Register receiver,
+ Register key,
+ Register elements,
+ Register scratch1,
+ Register scratch2,
+ Register result,
+ Label* not_fast_array,
+ Label* out_of_range) {
+ // Register use:
+ //
+ // receiver - holds the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // elements - holds the elements of the receiver on exit.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the the same as 'receiver' or 'key'.
+ // Unchanged on bailout so 'receiver' and 'key' can be safely
+ // used by further computation.
+ //
+ // Scratch registers:
+ //
+ // scratch1 - used to hold elements map and elements length.
+ // Holds the elements map if not_fast_array branch is taken.
+ //
+ // scratch2 - used to hold the loaded value.
+
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ if (not_fast_array != NULL) {
+ // Check that the object is in fast mode (not dictionary).
+ __ lw(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kFixedArrayMapRootIndex);
+ __ Branch(not_fast_array, ne, scratch1, Operand(at));
+ } else {
+ __ AssertFastElements(elements);
+ }
+
+ // Check that the key (index) is within bounds.
+ __ lw(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
+ __ Branch(out_of_range, hs, key, Operand(scratch1));
+
+ // Fast case: Do the load.
+ __ Addu(scratch1, elements,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // The key is a smi.
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ sll(at, key, kPointerSizeLog2 - kSmiTagSize);
+ __ addu(at, at, scratch1);
+ __ lw(scratch2, MemOperand(at));
+
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ // In case the loaded value is the_hole we have to consult GetProperty
+ // to ensure the prototype chain is searched.
+ __ Branch(out_of_range, eq, scratch2, Operand(at));
+ __ mov(result, scratch2);
+}
+
+
+// Checks whether a key is an array index string or a unique name.
+// Falls through if a key is a unique name.
+static void GenerateKeyNameCheck(MacroAssembler* masm,
+ Register key,
+ Register map,
+ Register hash,
+ Label* index_string,
+ Label* not_unique) {
+ // The key is not a smi.
+ Label unique;
+ // Is it a name?
+ __ GetObjectType(key, map, hash);
+ __ Branch(not_unique, hi, hash, Operand(LAST_UNIQUE_NAME_TYPE));
+ STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
+ __ Branch(&unique, eq, hash, Operand(LAST_UNIQUE_NAME_TYPE));
+
+ // Is the string an array index, with cached numeric value?
+ __ lw(hash, FieldMemOperand(key, Name::kHashFieldOffset));
+ __ And(at, hash, Operand(Name::kContainsCachedArrayIndexMask));
+ __ Branch(index_string, eq, at, Operand(zero_reg));
+
+ // Is the string internalized? We know it's a string, so a single
+ // bit test is enough.
+ // map: key map
+ __ lbu(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kInternalizedTag == 0);
+ __ And(at, hash, Operand(kIsNotInternalizedMask));
+ __ Branch(not_unique, ne, at, Operand(zero_reg));
+
+ __ bind(&unique);
+}
+
+
+// Defined in ic.cc.
+Object* CallIC_Miss(Arguments args);
+
+// The generated code does not accept smi keys.
+// The generated code falls through if both probes miss.
+void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm,
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state) {
+ // ----------- S t a t e -------------
+ // -- a1 : receiver
+ // -- a2 : name
+ // -----------------------------------
+ Label number, non_number, non_string, boolean, probe, miss;
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(kind,
+ MONOMORPHIC,
+ extra_state,
+ Code::NORMAL,
+ argc);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, a1, a2, a3, t0, t1, t2);
+
+ // If the stub cache probing failed, the receiver might be a value.
+ // For value objects, we use the map of the prototype objects for
+ // the corresponding JSValue for the cache and that is what we need
+ // to probe.
+ //
+ // Check for number.
+ __ JumpIfSmi(a1, &number, t1);
+ __ GetObjectType(a1, a3, a3);
+ __ Branch(&non_number, ne, a3, Operand(HEAP_NUMBER_TYPE));
+ __ bind(&number);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::NUMBER_FUNCTION_INDEX, a1);
+ __ Branch(&probe);
+
+ // Check for string.
+ __ bind(&non_number);
+ __ Branch(&non_string, Ugreater_equal, a3, Operand(FIRST_NONSTRING_TYPE));
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::STRING_FUNCTION_INDEX, a1);
+ __ Branch(&probe);
+
+ // Check for boolean.
+ __ bind(&non_string);
+ __ LoadRoot(t0, Heap::kTrueValueRootIndex);
+ __ Branch(&boolean, eq, a1, Operand(t0));
+ __ LoadRoot(t1, Heap::kFalseValueRootIndex);
+ __ Branch(&miss, ne, a1, Operand(t1));
+ __ bind(&boolean);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::BOOLEAN_FUNCTION_INDEX, a1);
+
+ // Probe the stub cache for the value object.
+ __ bind(&probe);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, a1, a2, a3, t0, t1, t2);
+
+ __ bind(&miss);
+}
+
+
+static void GenerateFunctionTailCall(MacroAssembler* masm,
+ int argc,
+ Label* miss,
+ Register scratch) {
+ // a1: function
+
+ // Check that the value isn't a smi.
+ __ JumpIfSmi(a1, miss);
+
+ // Check that the value is a JSFunction.
+ __ GetObjectType(a1, scratch, scratch);
+ __ Branch(miss, ne, scratch, Operand(JS_FUNCTION_TYPE));
+
+ // Invoke the function.
+ ParameterCount actual(argc);
+ __ InvokeFunction(a1, actual, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
+}
+
+
+void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ // Get the receiver of the function from the stack into a1.
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ GenerateNameDictionaryReceiverCheck(masm, a1, a0, a3, t0, &miss);
+
+ // a0: elements
+ // Search the dictionary - put result in register a1.
+ GenerateDictionaryLoad(masm, &miss, a0, a2, a1, a3, t0);
+
+ GenerateFunctionTailCall(masm, argc, &miss, t0);
+
+ // Cache miss: Jump to runtime.
+ __ bind(&miss);
+}
+
+
+void CallICBase::GenerateMiss(MacroAssembler* masm,
+ int argc,
+ IC::UtilityId id,
+ Code::ExtraICState extra_state) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ if (id == IC::kCallIC_Miss) {
+ __ IncrementCounter(isolate->counters()->call_miss(), 1, a3, t0);
+ } else {
+ __ IncrementCounter(isolate->counters()->keyed_call_miss(), 1, a3, t0);
+ }
+
+ // Get the receiver of the function from the stack.
+ __ lw(a3, MemOperand(sp, argc*kPointerSize));
+
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Push the receiver and the name of the function.
+ __ Push(a3, a2);
+
+ // Call the entry.
+ __ PrepareCEntryArgs(2);
+ __ PrepareCEntryFunction(ExternalReference(IC_Utility(id), isolate));
+
+ CEntryStub stub(1);
+ __ CallStub(&stub);
+
+ // 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));
+ __ 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));
+
+ // Patch the receiver on the stack.
+ __ bind(&global);
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a2, MemOperand(sp, argc * kPointerSize));
+ __ bind(&invoke);
+ }
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount actual(argc);
+ __ InvokeFunction(a1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ call_kind);
+}
+
+
+void CallIC::GenerateMegamorphic(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack into a1.
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state);
+ GenerateMiss(masm, argc, extra_ic_state);
+}
+
+
+void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack into a1.
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ Label do_call, slow_call, slow_load, slow_reload_receiver;
+ Label check_number_dictionary, check_name, lookup_monomorphic_cache;
+ Label index_smi, index_name;
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(a2, &check_name);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, a1, a0, a3, Map::kHasIndexedInterceptor, &slow_call);
+
+ GenerateFastArrayLoad(
+ masm, a1, a2, t0, a3, a0, a1, &check_number_dictionary, &slow_load);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->keyed_call_generic_smi_fast(), 1, a0, a3);
+
+ __ bind(&do_call);
+ // receiver in a1 is not used after this point.
+ // a2: key
+ // a1: function
+
+ GenerateFunctionTailCall(masm, argc, &slow_call, a0);
+
+ __ bind(&check_number_dictionary);
+ // a2: key
+ // a3: elements map
+ // t0: elements pointer
+ // Check whether the elements is a number dictionary.
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&slow_load, ne, a3, Operand(at));
+ __ sra(a0, a2, kSmiTagSize);
+ // a0: untagged index
+ __ LoadFromNumberDictionary(&slow_load, t0, a2, a1, a0, a3, t1);
+ __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1, a0, a3);
+ __ jmp(&do_call);
+
+ __ bind(&slow_load);
+ // This branch is taken when calling KeyedCallIC_Miss is neither required
+ // nor beneficial.
+ __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1, a0, a3);
+ {
+ 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);
+
+ __ bind(&check_name);
+ GenerateKeyNameCheck(masm, a2, a0, a3, &index_name, &slow_call);
+
+ // The key is known to be a unique name.
+ // If the receiver is a regular JS object with slow properties then do
+ // a quick inline probe of the receiver's dictionary.
+ // Otherwise do the monomorphic cache probe.
+ GenerateKeyedLoadReceiverCheck(
+ masm, a1, a0, a3, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
+
+ __ lw(a0, FieldMemOperand(a1, JSObject::kPropertiesOffset));
+ __ lw(a3, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&lookup_monomorphic_cache, ne, a3, Operand(at));
+
+ GenerateDictionaryLoad(masm, &slow_load, a0, a2, a1, a3, t0);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1, a0, a3);
+ __ jmp(&do_call);
+
+ __ bind(&lookup_monomorphic_cache);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1, a0, a3);
+ GenerateMonomorphicCacheProbe(masm,
+ argc,
+ Code::KEYED_CALL_IC,
+ Code::kNoExtraICState);
+ // Fall through on miss.
+
+ __ bind(&slow_call);
+ // This branch is taken if:
+ // - the receiver requires boxing or access check,
+ // - the key is neither smi nor a unique name,
+ // - the value loaded is not a function,
+ // - there is hope that the runtime will create a monomorphic call stub,
+ // that will get fetched next time.
+ __ IncrementCounter(counters->keyed_call_generic_slow(), 1, a0, a3);
+ GenerateMiss(masm, argc);
+
+ __ bind(&index_name);
+ __ IndexFromHash(a3, a2);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
+}
+
+
+void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ // Check if the name is really a name.
+ Label miss;
+ __ JumpIfSmi(a2, &miss);
+ __ IsObjectNameType(a2, a0, &miss);
+
+ CallICBase::GenerateNormal(masm, argc);
+ __ bind(&miss);
+ GenerateMiss(masm, argc);
+}
+
+
+void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -----------------------------------
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, Code::kNoExtraICState,
+ Code::NORMAL, Code::LOAD_IC);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, a0, a2, a3, t0, t1, t2);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
+}
+
+
+void LoadIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- lr : return address
+ // -- a0 : receiver
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameDictionaryReceiverCheck(masm, a0, a1, a3, t0, &miss);
+
+ // a1: elements
+ GenerateDictionaryLoad(masm, &miss, a1, a2, v0, a3, t0);
+ __ Ret();
+
+ // Cache miss: Jump to runtime.
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void LoadIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, a3, t0);
+
+ __ mov(a3, a0);
+ __ Push(a3, a2);
+
+ // Perform tail call to the entry.
+ ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss), isolate);
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -----------------------------------
+
+ __ mov(a3, a0);
+ __ Push(a3, a2);
+
+ __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
+}
+
+
+static MemOperand GenerateMappedArgumentsLookup(MacroAssembler* masm,
+ Register object,
+ Register key,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* unmapped_case,
+ Label* slow_case) {
+ // Check that the receiver is a JSObject. Because of the map check
+ // later, we do not need to check for interceptors or whether it
+ // requires access checks.
+ __ JumpIfSmi(object, slow_case);
+ // Check that the object is some kind of JSObject.
+ __ GetObjectType(object, scratch1, scratch2);
+ __ Branch(slow_case, lt, scratch2, Operand(FIRST_JS_RECEIVER_TYPE));
+
+ // Check that the key is a positive smi.
+ __ And(scratch1, key, Operand(0x80000001));
+ __ Branch(slow_case, ne, scratch1, Operand(zero_reg));
+
+ // Load the elements into scratch1 and check its map.
+ __ lw(scratch1, FieldMemOperand(object, JSObject::kElementsOffset));
+ __ CheckMap(scratch1,
+ scratch2,
+ Heap::kNonStrictArgumentsElementsMapRootIndex,
+ slow_case,
+ DONT_DO_SMI_CHECK);
+ // Check if element is in the range of mapped arguments. If not, jump
+ // to the unmapped lookup with the parameter map in scratch1.
+ __ lw(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
+ __ Subu(scratch2, scratch2, Operand(Smi::FromInt(2)));
+ __ Branch(unmapped_case, Ugreater_equal, key, Operand(scratch2));
+
+ // Load element index and check whether it is the hole.
+ const int kOffset =
+ FixedArray::kHeaderSize + 2 * kPointerSize - kHeapObjectTag;
+
+ __ li(scratch3, Operand(kPointerSize >> 1));
+ __ Mul(scratch3, key, scratch3);
+ __ Addu(scratch3, scratch3, Operand(kOffset));
+
+ __ Addu(scratch2, scratch1, scratch3);
+ __ lw(scratch2, MemOperand(scratch2));
+ __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex);
+ __ Branch(unmapped_case, eq, scratch2, Operand(scratch3));
+
+ // Load value from context and return it. We can reuse scratch1 because
+ // we do not jump to the unmapped lookup (which requires the parameter
+ // map in scratch1).
+ __ lw(scratch1, FieldMemOperand(scratch1, FixedArray::kHeaderSize));
+ __ li(scratch3, Operand(kPointerSize >> 1));
+ __ Mul(scratch3, scratch2, scratch3);
+ __ Addu(scratch3, scratch3, Operand(Context::kHeaderSize - kHeapObjectTag));
+ __ Addu(scratch2, scratch1, scratch3);
+ return MemOperand(scratch2);
+}
+
+
+static MemOperand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
+ Register key,
+ Register parameter_map,
+ Register scratch,
+ Label* slow_case) {
+ // Element is in arguments backing store, which is referenced by the
+ // second element of the parameter_map. The parameter_map register
+ // must be loaded with the parameter map of the arguments object and is
+ // overwritten.
+ const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize;
+ Register backing_store = parameter_map;
+ __ lw(backing_store, FieldMemOperand(parameter_map, kBackingStoreOffset));
+ __ CheckMap(backing_store,
+ scratch,
+ Heap::kFixedArrayMapRootIndex,
+ slow_case,
+ DONT_DO_SMI_CHECK);
+ __ lw(scratch, FieldMemOperand(backing_store, FixedArray::kLengthOffset));
+ __ Branch(slow_case, Ugreater_equal, key, Operand(scratch));
+ __ li(scratch, Operand(kPointerSize >> 1));
+ __ Mul(scratch, key, scratch);
+ __ Addu(scratch,
+ scratch,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ Addu(scratch, backing_store, scratch);
+ return MemOperand(scratch);
+}
+
+
+void KeyedLoadIC::GenerateNonStrictArguments(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label slow, notin;
+ MemOperand mapped_location =
+ GenerateMappedArgumentsLookup(masm, a1, a0, a2, a3, t0, &notin, &slow);
+ __ Ret(USE_DELAY_SLOT);
+ __ lw(v0, mapped_location);
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in a2.
+ MemOperand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, a0, a2, a3, &slow);
+ __ lw(a2, unmapped_location);
+ __ LoadRoot(a3, Heap::kTheHoleValueRootIndex);
+ __ Branch(&slow, eq, a2, Operand(a3));
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a2);
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- 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);
+ __ mov(t5, a0);
+ ASSERT_EQ(mapped_location.offset(), 0);
+ __ 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,
+ kRAHasNotBeenSaved, kDontSaveFPRegs);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0); // (In delay slot) return the value stored in v0.
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedCallIC::GenerateNonStrictArguments(MacroAssembler* masm,
+ int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- lr : return address
+ // -----------------------------------
+ Label slow, notin;
+ // Load receiver.
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+ MemOperand mapped_location =
+ GenerateMappedArgumentsLookup(masm, a1, a2, a3, t0, t1, &notin, &slow);
+ __ lw(a1, mapped_location);
+ GenerateFunctionTailCall(masm, argc, &slow, a3);
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in a3.
+ MemOperand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, a2, a3, t0, &slow);
+ __ lw(a1, unmapped_location);
+ __ LoadRoot(a3, Heap::kTheHoleValueRootIndex);
+ __ Branch(&slow, eq, a1, Operand(a3));
+ GenerateFunctionTailCall(masm, argc, &slow, a3);
+ __ bind(&slow);
+ GenerateMiss(masm, argc);
+}
+
+
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, a3, t0);
+
+ __ Push(a1, a0);
+
+ // Perform tail call to the entry.
+ ExternalReference ref = miss_mode == MISS_FORCE_GENERIC
+ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric), isolate)
+ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate);
+
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+
+ __ Push(a1, a0);
+
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label slow, check_name, index_smi, index_name, property_array_property;
+ Label probe_dictionary, check_number_dictionary;
+
+ Register key = a0;
+ Register receiver = a1;
+
+ Isolate* isolate = masm->isolate();
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(key, &check_name);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, receiver, a2, a3, Map::kHasIndexedInterceptor, &slow);
+
+ // Check the receiver's map to see if it has fast elements.
+ __ CheckFastElements(a2, a3, &check_number_dictionary);
+
+ GenerateFastArrayLoad(
+ masm, receiver, key, t0, a3, a2, v0, NULL, &slow);
+
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_smi(), 1, a2, a3);
+ __ Ret();
+
+ __ bind(&check_number_dictionary);
+ __ lw(t0, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ lw(a3, FieldMemOperand(t0, JSObject::kMapOffset));
+
+ // Check whether the elements is a number dictionary.
+ // a0: key
+ // a3: elements map
+ // t0: elements
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&slow, ne, a3, Operand(at));
+ __ sra(a2, a0, kSmiTagSize);
+ __ LoadFromNumberDictionary(&slow, t0, a0, v0, a2, a3, t1);
+ __ Ret();
+
+ // Slow case, key and receiver still in a0 and a1.
+ __ bind(&slow);
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_slow(),
+ 1,
+ a2,
+ a3);
+ GenerateRuntimeGetProperty(masm);
+
+ __ bind(&check_name);
+ GenerateKeyNameCheck(masm, key, a2, a3, &index_name, &slow);
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, receiver, a2, a3, Map::kHasIndexedInterceptor, &slow);
+
+
+ // If the receiver is a fast-case object, check the keyed lookup
+ // cache. Otherwise probe the dictionary.
+ __ lw(a3, FieldMemOperand(a1, JSObject::kPropertiesOffset));
+ __ lw(t0, FieldMemOperand(a3, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&probe_dictionary, eq, t0, Operand(at));
+
+ // Load the map of the receiver, compute the keyed lookup cache hash
+ // based on 32 bits of the map pointer and the name hash.
+ __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ sra(a3, a2, KeyedLookupCache::kMapHashShift);
+ __ lw(t0, FieldMemOperand(a0, Name::kHashFieldOffset));
+ __ sra(at, t0, Name::kHashShift);
+ __ xor_(a3, a3, at);
+ int mask = KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask;
+ __ And(a3, a3, Operand(mask));
+
+ // Load the key (consisting of map and unique name) 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);
+
+ 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, kPointerSize * ((kEntriesPerBucket - 1) * 2 + 1)));
+ __ Branch(&slow, ne, a0, Operand(t1));
+
+ // Get field offset.
+ // a0 : key
+ // a1 : receiver
+ // a2 : receiver's map
+ // a3 : lookup cache index
+ ExternalReference cache_field_offsets =
+ ExternalReference::keyed_lookup_cache_field_offsets(isolate);
+
+ // 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.
+ __ sll(at, t2, kPointerSizeLog2);
+ __ addu(at, a1, at);
+ __ lw(v0, MemOperand(at));
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
+ 1,
+ a2,
+ a3);
+ __ Ret();
+
+ // Load property array property.
+ __ bind(&property_array_property);
+ __ lw(a1, FieldMemOperand(a1, JSObject::kPropertiesOffset));
+ __ Addu(a1, a1, FixedArray::kHeaderSize - kHeapObjectTag);
+ __ sll(t0, t1, kPointerSizeLog2);
+ __ Addu(t0, t0, a1);
+ __ lw(v0, MemOperand(t0));
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
+ 1,
+ a2,
+ a3);
+ __ Ret();
+
+
+ // Do a quick inline probe of the receiver's dictionary, if it
+ // exists.
+ __ bind(&probe_dictionary);
+ // a1: receiver
+ // a0: key
+ // a3: elements
+ __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(a2, FieldMemOperand(a2, Map::kInstanceTypeOffset));
+ GenerateGlobalInstanceTypeCheck(masm, a2, &slow);
+ // Load the property to v0.
+ GenerateDictionaryLoad(masm, &slow, a3, a0, v0, a2, t0);
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(),
+ 1,
+ a2,
+ a3);
+ __ Ret();
+
+ __ bind(&index_name);
+ __ IndexFromHash(a3, key);
+ // Now jump to the place where smi keys are handled.
+ __ Branch(&index_smi);
+}
+
+
+void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key (index)
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ Register receiver = a1;
+ Register index = a0;
+ Register scratch = a3;
+ Register result = v0;
+
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &miss, // When index out of range.
+ STRING_INDEX_IS_ARRAY_INDEX);
+ char_at_generator.GenerateFast(masm);
+ __ Ret();
+
+ StubRuntimeCallHelper call_helper;
+ char_at_generator.GenerateSlow(masm, call_helper);
+
+ __ bind(&miss);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(a2, a1, a0);
+ __ li(a1, Operand(Smi::FromInt(NONE))); // PropertyAttributes.
+ __ li(a0, Operand(Smi::FromInt(strict_mode))); // Strict mode.
+ __ Push(a1, a0);
+
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+}
+
+
+static void KeyedStoreGenerateGenericHelper(
+ MacroAssembler* masm,
+ Label* fast_object,
+ Label* fast_double,
+ Label* slow,
+ KeyedStoreCheckMap check_map,
+ KeyedStoreIncrementLength increment_length,
+ Register value,
+ Register key,
+ Register receiver,
+ Register receiver_map,
+ Register elements_map,
+ Register elements) {
+ Label transition_smi_elements;
+ Label finish_object_store, non_double_value, transition_double_elements;
+ Label fast_double_without_map_check;
+
+ // Fast case: Do the store, could be either Object or double.
+ __ bind(fast_object);
+ Register scratch_value = t0;
+ Register address = t1;
+ if (check_map == kCheckMap) {
+ __ lw(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ Branch(fast_double, ne, elements_map,
+ Operand(masm->isolate()->factory()->fixed_array_map()));
+ }
+ // Smi stores don't require further checks.
+ Label non_smi_value;
+ __ JumpIfNotSmi(value, &non_smi_value);
+
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ Addu(scratch_value, key, Operand(Smi::FromInt(1)));
+ __ sw(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ }
+ // 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();
+
+ __ 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);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ Addu(scratch_value, key, Operand(Smi::FromInt(1)));
+ __ sw(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ }
+ __ 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.
+ __ mov(scratch_value, value); // Preserve the value which is returned.
+ __ RecordWrite(elements,
+ address,
+ scratch_value,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ __ Ret();
+
+ __ bind(fast_double);
+ if (check_map == kCheckMap) {
+ // Check for fast double array case. If this fails, call through to the
+ // runtime.
+ __ LoadRoot(at, Heap::kFixedDoubleArrayMapRootIndex);
+ __ Branch(slow, ne, elements_map, Operand(at));
+ }
+ __ bind(&fast_double_without_map_check);
+ __ StoreNumberToDoubleElements(value,
+ key,
+ elements, // Overwritten.
+ a3, // Scratch regs...
+ t0,
+ t1,
+ t2,
+ &transition_double_elements);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ Addu(scratch_value, key, Operand(Smi::FromInt(1)));
+ __ sw(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ }
+ __ Ret();
+
+ __ 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_ELEMENTS ->
+ // FAST_DOUBLE_ELEMENTS and complete the store.
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS,
+ receiver_map,
+ t0,
+ slow);
+ ASSERT(receiver_map.is(a3)); // Transition code expects map in a3
+ AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, slow);
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ jmp(&fast_double_without_map_check);
+
+ __ bind(&non_double_value);
+ // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_ELEMENTS,
+ receiver_map,
+ t0,
+ slow);
+ ASSERT(receiver_map.is(a3)); // Transition code expects map in a3
+ mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm, mode,
+ slow);
+ __ 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
+ mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, slow);
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ jmp(&finish_object_store);
+}
+
+
+void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+ Label slow, fast_object, fast_object_grow;
+ Label fast_double, fast_double_grow;
+ Label array, extra, check_if_double_array;
+
+ // Register usage.
+ Register value = a0;
+ Register key = a1;
+ Register receiver = a2;
+ 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(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(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(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, t0, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // Object case: Check key against length in the elements array.
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ // Check array bounds. Both the key and the length of FixedArray are smis.
+ __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
+ __ Branch(&fast_object, 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, Heap::kFixedArrayMapRootIndex);
+
+ __ jmp(&fast_object_grow);
+
+ __ bind(&check_if_double_array);
+ __ Branch(&slow, ne, elements_map, Heap::kFixedDoubleArrayMapRootIndex);
+ __ jmp(&fast_double_grow);
+
+ // 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));
+
+ // Check the key against the length in the array.
+ __ lw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Branch(&extra, hs, key, Operand(t0));
+
+ KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double,
+ &slow, kCheckMap, kDontIncrementLength,
+ value, key, receiver, receiver_map,
+ elements_map, elements);
+ KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow,
+ &slow, kDontCheckMap, kIncrementLength,
+ value, key, receiver, receiver_map,
+ elements_map, elements);
+}
+
+
+void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label slow;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(a1, &slow);
+
+ // Check that the key is an array index, that is Uint32.
+ __ And(t0, a0, Operand(kSmiTagMask | kSmiSignMask));
+ __ Branch(&slow, ne, t0, Operand(zero_reg));
+
+ // Get the map of the receiver.
+ __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
+
+ // Check that it has indexed interceptor and access checks
+ // are not enabled for this object.
+ __ lbu(a3, FieldMemOperand(a2, Map::kBitFieldOffset));
+ __ And(a3, a3, Operand(kSlowCaseBitFieldMask));
+ __ Branch(&slow, ne, a3, Operand(1 << Map::kHasIndexedInterceptor));
+ // Everything is fine, call runtime.
+ __ Push(a1, a0); // Receiver, key.
+
+ // Perform tail call to the entry.
+ __ TailCallExternalReference(ExternalReference(
+ IC_Utility(kKeyedLoadPropertyWithInterceptor), masm->isolate()), 2, 1);
+
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(a2, a1, a0);
+
+ ExternalReference ref = miss_mode == MISS_FORCE_GENERIC
+ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a2 : key
+ // -- a1 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(a1, a2, a0);
+
+ // The slow case calls into the runtime to complete the store without causing
+ // an IC miss that would otherwise cause a transition to the generic stub.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kStoreIC_Slow), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ // We can't use MultiPush as the order of the registers is important.
+ __ Push(a2, a1, a0);
+
+ // The slow case calls into the runtime to complete the store without causing
+ // an IC miss that would otherwise cause a transition to the generic stub.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
+
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateMegamorphic(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ // Get the receiver from the stack and probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, strict_mode,
+ Code::NORMAL, Code::STORE_IC);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, a1, a2, a3, t0, t1, t2);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
+}
+
+
+void StoreIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ __ Push(a1, a2, a0);
+ // Perform tail call to the entry.
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss),
+ masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameDictionaryReceiverCheck(masm, a1, a3, t0, t1, &miss);
+
+ GenerateDictionaryStore(masm, &miss, a3, a2, a0, t0, t1);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->store_normal_hit(), 1, t0, t1);
+ __ Ret();
+
+ __ bind(&miss);
+ __ IncrementCounter(counters->store_normal_miss(), 1, t0, t1);
+ GenerateMiss(masm);
+}
+
+
+void StoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ __ Push(a1, a2, a0);
+
+ __ li(a1, Operand(Smi::FromInt(NONE))); // PropertyAttributes.
+ __ li(a0, Operand(Smi::FromInt(strict_mode)));
+ __ Push(a1, a0);
+
+ // Do tail-call to runtime routine.
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+}
+
+
+#undef __
+
+
+Condition CompareIC::ComputeCondition(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;
+ }
+}
+
+
+bool CompareIC::HasInlinedSmiCode(Address address) {
+ // The address of the instruction following the call.
+ Address andi_instruction_address =
+ address + Assembler::kCallTargetAddressOffset;
+
+ // If the instruction following the call is not a andi at, rx, #yyy, nothing
+ // was inlined.
+ Instr instr = Assembler::instr_at(andi_instruction_address);
+ return Assembler::IsAndImmediate(instr) &&
+ Assembler::GetRt(instr) == static_cast<uint32_t>(zero_reg.code());
+}
+
+
+void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
+ Address andi_instruction_address =
+ address + Assembler::kCallTargetAddressOffset;
+
+ // 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) &&
+ Assembler::GetRt(instr) == static_cast<uint32_t>(zero_reg.code()))) {
+ return;
+ }
+
+ // The delta to the start of the map check instruction and the
+ // condition code uses at the patched jump.
+ int delta = Assembler::GetImmediate16(instr);
+ delta += Assembler::GetRs(instr) * kImm16Mask;
+ // If the delta is 0 the instruction is andi at, zero_reg, #0 which also
+ // signals that nothing was inlined.
+ if (delta == 0) {
+ return;
+ }
+
+#ifdef DEBUG
+ if (FLAG_trace_ic) {
+ PrintF("[ patching ic at %p, andi=%p, delta=%d\n",
+ address, andi_instruction_address, delta);
+ }
+#endif
+
+ Address patch_address =
+ andi_instruction_address - delta * Instruction::kInstrSize;
+ Instr instr_at_patch = Assembler::instr_at(patch_address);
+ Instr branch_instr =
+ Assembler::instr_at(patch_address + Instruction::kInstrSize);
+ // This is patching a conditional "jump if not smi/jump if smi" site.
+ // Enabling by changing from
+ // andi at, rx, 0
+ // Branch <target>, eq, at, Operand(zero_reg)
+ // to:
+ // andi at, rx, #kSmiTagMask
+ // Branch <target>, ne, at, Operand(zero_reg)
+ // and vice-versa to be disabled again.
+ CodePatcher patcher(patch_address, 2);
+ Register reg = Register::from_code(Assembler::GetRs(instr_at_patch));
+ if (check == ENABLE_INLINED_SMI_CHECK) {
+ ASSERT(Assembler::IsAndImmediate(instr_at_patch));
+ ASSERT_EQ(0, Assembler::GetImmediate16(instr_at_patch));
+ patcher.masm()->andi(at, reg, kSmiTagMask);
+ } else {
+ ASSERT(check == DISABLE_INLINED_SMI_CHECK);
+ ASSERT(Assembler::IsAndImmediate(instr_at_patch));
+ patcher.masm()->andi(at, reg, 0);
+ }
+ ASSERT(Assembler::IsBranch(branch_instr));
+ if (Assembler::IsBeq(branch_instr)) {
+ patcher.ChangeBranchCondition(ne);
+ } else {
+ ASSERT(Assembler::IsBne(branch_instr));
+ patcher.ChangeBranchCondition(eq);
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/lithium-codegen-mips.cc b/chromium/v8/src/mips/lithium-codegen-mips.cc
new file mode 100644
index 00000000000..2bc52e4f3ff
--- /dev/null
+++ b/chromium/v8/src/mips/lithium-codegen-mips.cc
@@ -0,0 +1,5807 @@
+// Copyright 2012 the V8 project authors. 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() {
+ LPhase phase("Z_Code generation", chunk());
+ ASSERT(is_unused());
+ status_ = GENERATING;
+
+ // 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() &&
+ GenerateDeoptJumpTable() &&
+ GenerateSafepointTable();
+}
+
+
+void LCodeGen::FinishCode(Handle<Code> code) {
+ ASSERT(is_done());
+ code->set_stack_slots(GetStackSlotCount());
+ code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
+ if (FLAG_weak_embedded_maps_in_optimized_code) {
+ RegisterDependentCodeForEmbeddedMaps(code);
+ }
+ PopulateDeoptimizationData(code);
+ info()->CommitDependencies(code);
+}
+
+
+void LChunkBuilder::Abort(BailoutReason reason) {
+ info()->set_bailout_reason(reason);
+ 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);
+ OS::MemCopy(copy.start(), builder.Finalize(), copy.length());
+ masm()->RecordComment(copy.start());
+}
+
+
+bool LCodeGen::GeneratePrologue() {
+ ASSERT(is_generating());
+
+ if (info()->IsOptimizing()) {
+ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info_->function()->name()->IsUtf8EqualTo(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);
+ }
+ }
+
+ info()->set_prologue_offset(masm_->pc_offset());
+ if (NeedsEagerFrame()) {
+ if (info()->IsStub()) {
+ __ Push(ra, fp, cp);
+ __ Push(Smi::FromInt(StackFrame::STUB));
+ // Adjust FP to point to saved FP.
+ __ Addu(fp, sp, Operand(2 * kPointerSize));
+ } else {
+ // The following three instructions must remain together and unmodified
+ // for code aging to work properly.
+ __ Push(ra, fp, cp, a1);
+ // Add unused nop to ensure prologue sequence is identical for
+ // full-codegen and lithium-codegen.
+ __ nop(Assembler::CODE_AGE_SEQUENCE_NOP);
+ // Adj. FP to point to saved FP.
+ __ Addu(fp, sp, Operand(2 * kPointerSize));
+ }
+ frame_is_built_ = true;
+ info_->AddNoFrameRange(0, masm_->pc_offset());
+ }
+
+ // Reserve space for the stack slots needed by the code.
+ int slots = GetStackSlotCount();
+ if (slots > 0) {
+ if (FLAG_debug_code) {
+ __ Subu(sp, sp, Operand(slots * kPointerSize));
+ __ push(a0);
+ __ push(a1);
+ __ Addu(a0, sp, Operand(slots * kPointerSize));
+ __ li(a1, Operand(kSlotsZapValue));
+ Label loop;
+ __ bind(&loop);
+ __ Subu(a0, a0, Operand(kPointerSize));
+ __ sw(a1, MemOperand(a0, 2 * kPointerSize));
+ __ Branch(&loop, ne, a0, Operand(sp));
+ __ pop(a1);
+ __ pop(a0);
+ } else {
+ __ Subu(sp, sp, Operand(slots * kPointerSize));
+ }
+ }
+
+ if (info()->saves_caller_doubles()) {
+ Comment(";;; Save clobbered callee double registers");
+ int count = 0;
+ BitVector* doubles = chunk()->allocated_double_registers();
+ BitVector::Iterator save_iterator(doubles);
+ while (!save_iterator.Done()) {
+ __ sdc1(DoubleRegister::FromAllocationIndex(save_iterator.Current()),
+ MemOperand(sp, count * kDoubleSize));
+ save_iterator.Advance();
+ count++;
+ }
+ }
+
+ // Possibly allocate a local context.
+ int heap_slots = info()->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, GetRAState(), kSaveFPRegs);
+ }
+ }
+ Comment(";;; End allocate local context");
+ }
+
+ // Trace the call.
+ if (FLAG_trace && info()->IsOptimizing()) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+ 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_);
+
+ // Don't emit code for basic blocks with a replacement.
+ if (instr->IsLabel()) {
+ emit_instructions = !LLabel::cast(instr)->HasReplacement();
+ }
+ if (!emit_instructions) continue;
+
+ if (FLAG_code_comments && instr->HasInterestingComment(this)) {
+ Comment(";;; <@%d,#%d> %s",
+ current_instruction_,
+ instr->hydrogen_value()->id(),
+ instr->Mnemonic());
+ }
+
+ RecordAndUpdatePosition(instr->position());
+
+ instr->CompileToNative(this);
+ }
+ EnsureSpaceForLazyDeopt();
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ 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];
+
+ int pos = instructions_->at(code->instruction_index())->position();
+ RecordAndUpdatePosition(pos);
+
+ Comment(";;; <@%d,#%d> "
+ "-------------------- Deferred %s --------------------",
+ code->instruction_index(),
+ code->instr()->hydrogen_value()->id(),
+ code->instr()->Mnemonic());
+ __ bind(code->entry());
+ if (NeedsDeferredFrame()) {
+ Comment(";;; Build frame");
+ ASSERT(!frame_is_built_);
+ ASSERT(info()->IsStub());
+ frame_is_built_ = true;
+ __ MultiPush(cp.bit() | fp.bit() | ra.bit());
+ __ li(scratch0(), Operand(Smi::FromInt(StackFrame::STUB)));
+ __ push(scratch0());
+ __ Addu(fp, sp, Operand(2 * kPointerSize));
+ Comment(";;; Deferred code");
+ }
+ code->Generate();
+ if (NeedsDeferredFrame()) {
+ Comment(";;; Destroy frame");
+ ASSERT(frame_is_built_);
+ __ pop(at);
+ __ MultiPop(cp.bit() | fp.bit() | ra.bit());
+ frame_is_built_ = false;
+ }
+ __ 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() {
+ // Check that the jump table is accessible from everywhere in the function
+ // code, i.e. that offsets to the table can be encoded in the 16bit signed
+ // immediate of a branch instruction.
+ // To simplify we consider the code size from the first instruction to the
+ // end of the jump table.
+ if (!is_int16((masm()->pc_offset() / Assembler::kInstrSize) +
+ deopt_jump_table_.length() * 12)) {
+ Abort(kGeneratedCodeIsTooLarge);
+ }
+
+ if (deopt_jump_table_.length() > 0) {
+ Comment(";;; -------------------- Jump table --------------------");
+ }
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
+ Label table_start;
+ __ bind(&table_start);
+ Label needs_frame;
+ for (int i = 0; i < deopt_jump_table_.length(); i++) {
+ __ bind(&deopt_jump_table_[i].label);
+ Address entry = deopt_jump_table_[i].address;
+ Deoptimizer::BailoutType type = deopt_jump_table_[i].bailout_type;
+ int id = Deoptimizer::GetDeoptimizationId(isolate(), entry, type);
+ if (id == Deoptimizer::kNotDeoptimizationEntry) {
+ Comment(";;; jump table entry %d.", i);
+ } else {
+ Comment(";;; jump table entry %d: deoptimization bailout %d.", i, id);
+ }
+ __ li(t9, Operand(ExternalReference::ForDeoptEntry(entry)));
+ if (deopt_jump_table_[i].needs_frame) {
+ if (needs_frame.is_bound()) {
+ __ Branch(&needs_frame);
+ } else {
+ __ bind(&needs_frame);
+ __ MultiPush(cp.bit() | fp.bit() | ra.bit());
+ // This variant of deopt can only be used with stubs. Since we don't
+ // have a function pointer to install in the stack frame that we're
+ // building, install a special marker there instead.
+ ASSERT(info()->IsStub());
+ __ li(scratch0(), Operand(Smi::FromInt(StackFrame::STUB)));
+ __ push(scratch0());
+ __ Addu(fp, sp, Operand(2 * kPointerSize));
+ __ Call(t9);
+ }
+ } else {
+ __ Call(t9);
+ }
+ }
+ __ RecordComment("]");
+
+ // The deoptimization jump table 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::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);
+ HConstant* constant = chunk_->LookupConstant(const_op);
+ Handle<Object> literal = constant->handle();
+ Representation r = chunk_->LookupLiteralRepresentation(const_op);
+ if (r.IsInteger32()) {
+ ASSERT(literal->IsNumber());
+ __ li(scratch, Operand(static_cast<int32_t>(literal->Number())));
+ } else if (r.IsSmi()) {
+ ASSERT(constant->HasSmiValue());
+ __ li(scratch, Operand(Smi::FromInt(constant->Integer32Value())));
+ } else if (r.IsDouble()) {
+ Abort(kEmitLoadRegisterUnsupportedDoubleImmediate);
+ } else {
+ ASSERT(r.IsTagged());
+ __ LoadObject(scratch, 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);
+ HConstant* constant = chunk_->LookupConstant(const_op);
+ Handle<Object> literal = constant->handle();
+ 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(kUnsupportedDoubleImmediate);
+ } else if (r.IsTagged()) {
+ Abort(kUnsupportedTaggedImmediate);
+ }
+ } 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 {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(chunk_->LookupLiteralRepresentation(op).IsSmiOrTagged());
+ return constant->handle();
+}
+
+
+bool LCodeGen::IsInteger32(LConstantOperand* op) const {
+ return chunk_->LookupLiteralRepresentation(op).IsSmiOrInteger32();
+}
+
+
+bool LCodeGen::IsSmi(LConstantOperand* op) const {
+ return chunk_->LookupLiteralRepresentation(op).IsSmi();
+}
+
+
+int32_t LCodeGen::ToInteger32(LConstantOperand* op) const {
+ return ToRepresentation(op, Representation::Integer32());
+}
+
+
+int32_t LCodeGen::ToRepresentation(LConstantOperand* op,
+ const Representation& r) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ int32_t value = constant->Integer32Value();
+ if (r.IsInteger32()) return value;
+ ASSERT(r.IsSmiOrTagged());
+ return reinterpret_cast<int32_t>(Smi::FromInt(value));
+}
+
+
+Smi* LCodeGen::ToSmi(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ return Smi::FromInt(constant->Integer32Value());
+}
+
+
+double LCodeGen::ToDouble(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(constant->HasDoubleValue());
+ return constant->DoubleValue();
+}
+
+
+Operand LCodeGen::ToOperand(LOperand* op) {
+ if (op->IsConstantOperand()) {
+ LConstantOperand* const_op = LConstantOperand::cast(op);
+ HConstant* constant = chunk()->LookupConstant(const_op);
+ Representation r = chunk_->LookupLiteralRepresentation(const_op);
+ if (r.IsSmi()) {
+ ASSERT(constant->HasSmiValue());
+ return Operand(Smi::FromInt(constant->Integer32Value()));
+ } else if (r.IsInteger32()) {
+ ASSERT(constant->HasInteger32Value());
+ return Operand(constant->Integer32Value());
+ } else if (r.IsDouble()) {
+ Abort(kToOperandUnsupportedDoubleImmediate);
+ }
+ ASSERT(r.IsTagged());
+ return Operand(constant->handle());
+ } else if (op->IsRegister()) {
+ return Operand(ToRegister(op));
+ } else if (op->IsDoubleRegister()) {
+ Abort(kToOperandIsDoubleRegisterUnimplemented);
+ 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());
+ return MemOperand(fp, StackSlotOffset(op->index()));
+}
+
+
+MemOperand LCodeGen::ToHighMemOperand(LOperand* op) const {
+ ASSERT(op->IsDoubleStackSlot());
+ return MemOperand(fp, StackSlotOffset(op->index()) + 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->translation_size();
+ // The output frame height does not include the parameters.
+ int height = translation_size - environment->parameter_count();
+
+ WriteTranslation(environment->outer(), translation);
+ bool has_closure_id = !info()->closure().is_null() &&
+ !info()->closure().is_identical_to(environment->closure());
+ int closure_id = has_closure_id
+ ? DefineDeoptimizationLiteral(environment->closure())
+ : Translation::kSelfLiteralId;
+
+ switch (environment->frame_type()) {
+ case JS_FUNCTION:
+ translation->BeginJSFrame(environment->ast_id(), closure_id, height);
+ break;
+ case JS_CONSTRUCT:
+ translation->BeginConstructStubFrame(closure_id, translation_size);
+ break;
+ case JS_GETTER:
+ ASSERT(translation_size == 1);
+ ASSERT(height == 0);
+ translation->BeginGetterStubFrame(closure_id);
+ break;
+ case JS_SETTER:
+ ASSERT(translation_size == 2);
+ ASSERT(height == 0);
+ translation->BeginSetterStubFrame(closure_id);
+ break;
+ case STUB:
+ translation->BeginCompiledStubFrame();
+ break;
+ case ARGUMENTS_ADAPTOR:
+ translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
+ break;
+ }
+
+ int object_index = 0;
+ int dematerialized_index = 0;
+ for (int i = 0; i < translation_size; ++i) {
+ LOperand* value = environment->values()->at(i);
+ AddToTranslation(environment,
+ translation,
+ value,
+ environment->HasTaggedValueAt(i),
+ environment->HasUint32ValueAt(i),
+ &object_index,
+ &dematerialized_index);
+ }
+}
+
+
+void LCodeGen::AddToTranslation(LEnvironment* environment,
+ Translation* translation,
+ LOperand* op,
+ bool is_tagged,
+ bool is_uint32,
+ int* object_index_pointer,
+ int* dematerialized_index_pointer) {
+ if (op == LEnvironment::materialization_marker()) {
+ int object_index = (*object_index_pointer)++;
+ if (environment->ObjectIsDuplicateAt(object_index)) {
+ int dupe_of = environment->ObjectDuplicateOfAt(object_index);
+ translation->DuplicateObject(dupe_of);
+ return;
+ }
+ int object_length = environment->ObjectLengthAt(object_index);
+ if (environment->ObjectIsArgumentsAt(object_index)) {
+ translation->BeginArgumentsObject(object_length);
+ } else {
+ translation->BeginCapturedObject(object_length);
+ }
+ int dematerialized_index = *dematerialized_index_pointer;
+ int env_offset = environment->translation_size() + dematerialized_index;
+ *dematerialized_index_pointer += object_length;
+ for (int i = 0; i < object_length; ++i) {
+ LOperand* value = environment->values()->at(env_offset + i);
+ AddToTranslation(environment,
+ translation,
+ value,
+ environment->HasTaggedValueAt(env_offset + i),
+ environment->HasUint32ValueAt(env_offset + i),
+ object_index_pointer,
+ dematerialized_index_pointer);
+ }
+ return;
+ }
+
+ if (op->IsStackSlot()) {
+ if (is_tagged) {
+ translation->StoreStackSlot(op->index());
+ } else if (is_uint32) {
+ translation->StoreUint32StackSlot(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 if (is_uint32) {
+ translation->StoreUint32Register(reg);
+ } else {
+ translation->StoreInt32Register(reg);
+ }
+ } else if (op->IsDoubleRegister()) {
+ DoubleRegister reg = ToDoubleRegister(op);
+ translation->StoreDoubleRegister(reg);
+ } else if (op->IsConstantOperand()) {
+ HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op));
+ int src_index = DefineDeoptimizationLiteral(constant->handle());
+ 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) {
+ EnsureSpaceForLazyDeopt();
+ 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->frame_type() == JS_FUNCTION) {
+ ++jsframe_count;
+ }
+ }
+ Translation translation(&translations_, frame_count, jsframe_count, zone());
+ 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, zone());
+ }
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition cc,
+ LEnvironment* environment,
+ Deoptimizer::BailoutType bailout_type,
+ Register src1,
+ const Operand& src2) {
+ RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt);
+ ASSERT(environment->HasBeenRegistered());
+ int id = environment->deoptimization_index();
+ ASSERT(info()->IsOptimizing() || info()->IsStub());
+ Address entry =
+ Deoptimizer::GetDeoptimizationEntry(isolate(), id, bailout_type);
+ if (entry == NULL) {
+ Abort(kBailoutWasNotPrepared);
+ return;
+ }
+
+ ASSERT(FLAG_deopt_every_n_times < 2); // Other values not supported on MIPS.
+ if (FLAG_deopt_every_n_times == 1 &&
+ !info()->IsStub() &&
+ info()->opt_count() == id) {
+ ASSERT(frame_is_built_);
+ __ Call(entry, RelocInfo::RUNTIME_ENTRY);
+ return;
+ }
+
+ if (info()->ShouldTrapOnDeopt()) {
+ Label skip;
+ if (cc != al) {
+ __ Branch(&skip, NegateCondition(cc), src1, src2);
+ }
+ __ stop("trap_on_deopt");
+ __ bind(&skip);
+ }
+
+ ASSERT(info()->IsStub() || frame_is_built_);
+ if (cc == al && frame_is_built_) {
+ __ Call(entry, RelocInfo::RUNTIME_ENTRY, cc, src1, src2);
+ } else {
+ // We often have several deopts to the same entry, reuse the last
+ // jump entry if this is the case.
+ if (deopt_jump_table_.is_empty() ||
+ (deopt_jump_table_.last().address != entry) ||
+ (deopt_jump_table_.last().bailout_type != bailout_type) ||
+ (deopt_jump_table_.last().needs_frame != !frame_is_built_)) {
+ Deoptimizer::JumpTableEntry table_entry(entry,
+ bailout_type,
+ !frame_is_built_);
+ deopt_jump_table_.Add(table_entry, zone());
+ }
+ __ Branch(&deopt_jump_table_.last().label, cc, src1, src2);
+ }
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition cc,
+ LEnvironment* environment,
+ Register src1,
+ const Operand& src2) {
+ Deoptimizer::BailoutType bailout_type = info()->IsStub()
+ ? Deoptimizer::LAZY
+ : Deoptimizer::EAGER;
+ DeoptimizeIf(cc, environment, bailout_type, src1, src2);
+}
+
+
+void LCodeGen::RegisterDependentCodeForEmbeddedMaps(Handle<Code> code) {
+ ZoneList<Handle<Map> > maps(1, zone());
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) {
+ RelocInfo::Mode mode = it.rinfo()->rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT &&
+ it.rinfo()->target_object()->IsMap()) {
+ Handle<Map> map(Map::cast(it.rinfo()->target_object()));
+ if (map->CanTransition()) {
+ maps.Add(map, zone());
+ }
+ }
+ }
+#ifdef VERIFY_HEAP
+ // This disables verification of weak embedded maps after full GC.
+ // AddDependentCode can cause a GC, which would observe the state where
+ // this code is not yet in the depended code lists of the embedded maps.
+ NoWeakEmbeddedMapsVerificationScope disable_verification_of_embedded_maps;
+#endif
+ for (int i = 0; i < maps.length(); i++) {
+ maps.at(i)->AddDependentCode(DependentCode::kWeaklyEmbeddedGroup, code);
+ }
+}
+
+
+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(isolate()->factory());
+ data->SetTranslationByteArray(*translations);
+ data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_));
+
+ Handle<FixedArray> literals =
+ factory()->NewFixedArray(deoptimization_literals_.length(), TENURED);
+ { AllowDeferredHandleDereference copy_handles;
+ 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().ToInt()));
+ 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, 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, zone());
+ 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(), zone());
+ } else if (pointer->IsRegister() && (kind & Safepoint::kWithRegisters)) {
+ safepoint.DefinePointerRegister(ToRegister(pointer), zone());
+ }
+ }
+ if (kind & Safepoint::kWithRegisters) {
+ // Register cp always contains a pointer to the context.
+ safepoint.DefinePointerRegister(cp, zone());
+ }
+}
+
+
+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, zone());
+ 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::RecordAndUpdatePosition(int position) {
+ if (position >= 0 && position != old_position_) {
+ masm()->positions_recorder()->RecordPosition(position);
+ old_position_ = position;
+ }
+}
+
+
+static const char* LabelType(LLabel* label) {
+ if (label->is_loop_header()) return " (loop header)";
+ if (label->is_osr_entry()) return " (OSR entry)";
+ return "";
+}
+
+
+void LCodeGen::DoLabel(LLabel* label) {
+ Comment(";;; <@%d,#%d> -------------------- B%d%s --------------------",
+ current_instruction_,
+ label->hydrogen_value()->id(),
+ label->block_id(),
+ LabelType(label));
+ __ 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(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::RegExpExec: {
+ RegExpExecStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::SubString: {
+ SubStringStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::NumberToString: {
+ NumberToStringStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::StringCompare: {
+ StringCompareStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::TranscendentalCache: {
+ __ lw(a0, MemOperand(sp, 0));
+ TranscendentalCacheStub stub(instr->transcendental_type(),
+ TranscendentalCacheStub::TAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) {
+ // Record the address of the first unknown OSR value as the place to enter.
+ if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset();
+}
+
+
+void LCodeGen::DoModI(LModI* instr) {
+ HMod* hmod = instr->hydrogen();
+ HValue* left = hmod->left();
+ HValue* right = hmod->right();
+ if (hmod->HasPowerOf2Divisor()) {
+ const Register left_reg = ToRegister(instr->left());
+ const Register result_reg = ToRegister(instr->result());
+
+ // Note: The code below even works when right contains kMinInt.
+ int32_t divisor = Abs(right->GetInteger32Constant());
+
+ Label left_is_not_negative, done;
+ if (left->CanBeNegative()) {
+ __ Branch(left_reg.is(result_reg) ? PROTECT : USE_DELAY_SLOT,
+ &left_is_not_negative, ge, left_reg, Operand(zero_reg));
+ __ subu(result_reg, zero_reg, left_reg);
+ __ And(result_reg, result_reg, divisor - 1);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(eq, instr->environment(), result_reg, Operand(zero_reg));
+ }
+ __ Branch(USE_DELAY_SLOT, &done);
+ __ subu(result_reg, zero_reg, result_reg);
+ }
+
+ __ bind(&left_is_not_negative);
+ __ And(result_reg, left_reg, divisor - 1);
+ __ bind(&done);
+
+ } else if (hmod->fixed_right_arg().has_value) {
+ const Register left_reg = ToRegister(instr->left());
+ const Register result_reg = ToRegister(instr->result());
+ const Register right_reg = ToRegister(instr->right());
+
+ int32_t divisor = hmod->fixed_right_arg().value;
+ ASSERT(IsPowerOf2(divisor));
+
+ // Check if our assumption of a fixed right operand still holds.
+ DeoptimizeIf(ne, instr->environment(), right_reg, Operand(divisor));
+
+ Label left_is_not_negative, done;
+ if (left->CanBeNegative()) {
+ __ Branch(left_reg.is(result_reg) ? PROTECT : USE_DELAY_SLOT,
+ &left_is_not_negative, ge, left_reg, Operand(zero_reg));
+ __ subu(result_reg, zero_reg, left_reg);
+ __ And(result_reg, result_reg, divisor - 1);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(eq, instr->environment(), result_reg, Operand(zero_reg));
+ }
+ __ Branch(USE_DELAY_SLOT, &done);
+ __ subu(result_reg, zero_reg, result_reg);
+ }
+
+ __ bind(&left_is_not_negative);
+ __ And(result_reg, left_reg, divisor - 1);
+ __ bind(&done);
+
+ } else {
+ const Register scratch = scratch0();
+ const Register left_reg = ToRegister(instr->left());
+ const Register result_reg = ToRegister(instr->result());
+
+ // div runs in the background while we check for special cases.
+ Register right_reg = EmitLoadRegister(instr->right(), scratch);
+ __ div(left_reg, right_reg);
+
+ Label done;
+ // Check for x % 0, we have to deopt in this case because we can't return a
+ // NaN.
+ if (right->CanBeZero()) {
+ DeoptimizeIf(eq, instr->environment(), right_reg, Operand(zero_reg));
+ }
+
+ // Check for kMinInt % -1, we have to deopt if we care about -0, because we
+ // can't return that.
+ if (left->RangeCanInclude(kMinInt) && right->RangeCanInclude(-1)) {
+ Label left_not_min_int;
+ __ Branch(&left_not_min_int, ne, left_reg, Operand(kMinInt));
+ // TODO(svenpanne) Don't deopt when we don't care about -0.
+ DeoptimizeIf(eq, instr->environment(), right_reg, Operand(-1));
+ __ bind(&left_not_min_int);
+ }
+
+ // TODO(svenpanne) Only emit the test/deopt if we have to.
+ __ Branch(USE_DELAY_SLOT, &done, ge, left_reg, Operand(zero_reg));
+ __ mfhi(result_reg);
+
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(eq, instr->environment(), result_reg, Operand(zero_reg));
+ }
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::EmitSignedIntegerDivisionByConstant(
+ Register result,
+ Register dividend,
+ int32_t divisor,
+ Register remainder,
+ Register scratch,
+ LEnvironment* environment) {
+ ASSERT(!AreAliased(dividend, scratch, at, no_reg));
+
+ uint32_t divisor_abs = abs(divisor);
+
+ int32_t power_of_2_factor =
+ CompilerIntrinsics::CountTrailingZeros(divisor_abs);
+
+ switch (divisor_abs) {
+ case 0:
+ DeoptimizeIf(al, environment);
+ return;
+
+ case 1:
+ if (divisor > 0) {
+ __ Move(result, dividend);
+ } else {
+ __ SubuAndCheckForOverflow(result, zero_reg, dividend, scratch);
+ DeoptimizeIf(lt, environment, scratch, Operand(zero_reg));
+ }
+ // Compute the remainder.
+ __ Move(remainder, zero_reg);
+ return;
+
+ default:
+ if (IsPowerOf2(divisor_abs)) {
+ // Branch and condition free code for integer division by a power
+ // of two.
+ int32_t power = WhichPowerOf2(divisor_abs);
+ if (power > 1) {
+ __ sra(scratch, dividend, power - 1);
+ }
+ __ srl(scratch, scratch, 32 - power);
+ __ Addu(scratch, dividend, Operand(scratch));
+ __ sra(result, scratch, power);
+ // Negate if necessary.
+ // We don't need to check for overflow because the case '-1' is
+ // handled separately.
+ if (divisor < 0) {
+ ASSERT(divisor != -1);
+ __ Subu(result, zero_reg, Operand(result));
+ }
+ // Compute the remainder.
+ if (divisor > 0) {
+ __ sll(scratch, result, power);
+ __ Subu(remainder, dividend, Operand(scratch));
+ } else {
+ __ sll(scratch, result, power);
+ __ Addu(remainder, dividend, Operand(scratch));
+ }
+ return;
+ } else if (LChunkBuilder::HasMagicNumberForDivisor(divisor)) {
+ // Use magic numbers for a few specific divisors.
+ // Details and proofs can be found in:
+ // - Hacker's Delight, Henry S. Warren, Jr.
+ // - The PowerPC Compiler Writer's Guide
+ // and probably many others.
+ //
+ // We handle
+ // <divisor with magic numbers> * <power of 2>
+ // but not
+ // <divisor with magic numbers> * <other divisor with magic numbers>
+ DivMagicNumbers magic_numbers =
+ DivMagicNumberFor(divisor_abs >> power_of_2_factor);
+ // Branch and condition free code for integer division by a power
+ // of two.
+ const int32_t M = magic_numbers.M;
+ const int32_t s = magic_numbers.s + power_of_2_factor;
+
+ __ li(scratch, Operand(M));
+ __ mult(dividend, scratch);
+ __ mfhi(scratch);
+ if (M < 0) {
+ __ Addu(scratch, scratch, Operand(dividend));
+ }
+ if (s > 0) {
+ __ sra(scratch, scratch, s);
+ __ mov(scratch, scratch);
+ }
+ __ srl(at, dividend, 31);
+ __ Addu(result, scratch, Operand(at));
+ if (divisor < 0) __ Subu(result, zero_reg, Operand(result));
+ // Compute the remainder.
+ __ li(scratch, Operand(divisor));
+ __ Mul(scratch, result, Operand(scratch));
+ __ Subu(remainder, dividend, Operand(scratch));
+ } else {
+ __ li(scratch, Operand(divisor));
+ __ div(dividend, scratch);
+ __ mfhi(remainder);
+ __ mflo(result);
+ }
+ }
+}
+
+
+void LCodeGen::DoDivI(LDivI* instr) {
+ const Register left = ToRegister(instr->left());
+ const Register right = ToRegister(instr->right());
+ 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);
+ }
+
+ if (!instr->hydrogen()->CheckFlag(HInstruction::kAllUsesTruncatingToInt32)) {
+ __ mfhi(result);
+ DeoptimizeIf(ne, instr->environment(), result, Operand(zero_reg));
+ }
+ __ mflo(result);
+}
+
+
+void LCodeGen::DoMultiplyAddD(LMultiplyAddD* instr) {
+ DoubleRegister addend = ToDoubleRegister(instr->addend());
+ DoubleRegister multiplier = ToDoubleRegister(instr->multiplier());
+ DoubleRegister multiplicand = ToDoubleRegister(instr->multiplicand());
+
+ // This is computed in-place.
+ ASSERT(addend.is(ToDoubleRegister(instr->result())));
+
+ __ madd_d(addend, addend, multiplier, multiplicand);
+}
+
+
+void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
+ const Register result = ToRegister(instr->result());
+ const Register left = ToRegister(instr->left());
+ const Register remainder = ToRegister(instr->temp());
+ const Register scratch = scratch0();
+
+ if (instr->right()->IsConstantOperand()) {
+ Label done;
+ int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right()));
+ if (divisor < 0) {
+ DeoptimizeIf(eq, instr->environment(), left, Operand(zero_reg));
+ }
+ EmitSignedIntegerDivisionByConstant(result,
+ left,
+ divisor,
+ remainder,
+ scratch,
+ instr->environment());
+ // We performed a truncating division. Correct the result if necessary.
+ __ Branch(&done, eq, remainder, Operand(zero_reg), USE_DELAY_SLOT);
+ __ Xor(scratch , remainder, Operand(divisor));
+ __ Branch(&done, ge, scratch, Operand(zero_reg));
+ __ Subu(result, result, Operand(1));
+ __ bind(&done);
+ } else {
+ Label done;
+ const Register right = ToRegister(instr->right());
+
+ // On MIPS div is asynchronous - it will run in the background while we
+ // check for special cases.
+ __ div(left, right);
+
+ // Check for x / 0.
+ 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(remainder);
+ __ mflo(result);
+
+ // We performed a truncating division. Correct the result if necessary.
+ __ Branch(&done, eq, remainder, Operand(zero_reg), USE_DELAY_SLOT);
+ __ Xor(scratch , remainder, Operand(right));
+ __ Branch(&done, ge, scratch, Operand(zero_reg));
+ __ Subu(result, result, Operand(1));
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoMulI(LMulI* instr) {
+ Register scratch = scratch0();
+ Register result = ToRegister(instr->result());
+ // Note that result may alias left.
+ Register left = ToRegister(instr->left());
+ LOperand* right_op = instr->right();
+
+ 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 = ToRepresentation(
+ LConstantOperand::cast(right_op),
+ instr->hydrogen()->right()->representation());
+
+ 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(scratch, left, shift);
+ __ Addu(result, scratch, left);
+ } else if (IsPowerOf2(constant_abs + 1)) {
+ int32_t shift = WhichPowerOf2(constant_abs + 1);
+ __ sll(scratch, left, shift);
+ __ Subu(result, scratch, 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->temp()), left, right);
+ }
+
+ if (can_overflow) {
+ // hi:lo = left * right.
+ if (instr->hydrogen()->representation().IsSmi()) {
+ __ SmiUntag(result, left);
+ __ mult(result, right);
+ __ mfhi(scratch);
+ __ mflo(result);
+ } else {
+ __ mult(left, right);
+ __ mfhi(scratch);
+ __ mflo(result);
+ }
+ __ sra(at, result, 31);
+ DeoptimizeIf(ne, instr->environment(), scratch, Operand(at));
+ } else {
+ if (instr->hydrogen()->representation().IsSmi()) {
+ __ SmiUntag(result, left);
+ __ Mul(result, result, right);
+ } 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->temp()),
+ Operand(zero_reg));
+ __ bind(&done);
+ }
+ }
+}
+
+
+void LCodeGen::DoBitI(LBitI* instr) {
+ LOperand* left_op = instr->left();
+ LOperand* right_op = instr->right();
+ 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:
+ if (right_op->IsConstantOperand() && right.immediate() == int32_t(~0)) {
+ __ Nor(result, zero_reg, left);
+ } else {
+ __ 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->right();
+ Register left = ToRegister(instr->left());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+
+ 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::ROR:
+ __ Ror(result, left, Operand(ToRegister(right_op)));
+ break;
+ 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::ROR:
+ if (shift_count != 0) {
+ __ Ror(result, left, Operand(shift_count));
+ } else {
+ __ Move(result, left);
+ }
+ break;
+ 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) {
+ if (instr->hydrogen_value()->representation().IsSmi() &&
+ instr->can_deopt()) {
+ if (shift_count != 1) {
+ __ sll(result, left, shift_count - 1);
+ __ SmiTagCheckOverflow(result, result, scratch);
+ } else {
+ __ SmiTagCheckOverflow(result, left, scratch);
+ }
+ DeoptimizeIf(lt, instr->environment(), scratch, Operand(zero_reg));
+ } else {
+ __ sll(result, left, shift_count);
+ }
+ } else {
+ __ Move(result, left);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoSubI(LSubI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ 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) {
+ __ li(ToRegister(instr->result()), Operand(instr->value()));
+}
+
+
+void LCodeGen::DoConstantS(LConstantS* instr) {
+ __ 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::DoConstantE(LConstantE* instr) {
+ __ li(ToRegister(instr->result()), Operand(instr->value()));
+}
+
+
+void LCodeGen::DoConstantT(LConstantT* instr) {
+ Handle<Object> value = instr->value();
+ AllowDeferredHandleDereference smi_check;
+ __ LoadObject(ToRegister(instr->result()), value);
+}
+
+
+void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
+ Register result = ToRegister(instr->result());
+ Register map = ToRegister(instr->value());
+ __ EnumLength(result, map);
+}
+
+
+void LCodeGen::DoElementsKind(LElementsKind* instr) {
+ Register result = ToRegister(instr->result());
+ Register input = ToRegister(instr->value());
+
+ // 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->value());
+ Register result = ToRegister(instr->result());
+ Register map = ToRegister(instr->temp());
+ Label done;
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ // 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::DoDateField(LDateField* instr) {
+ Register object = ToRegister(instr->date());
+ Register result = ToRegister(instr->result());
+ Register scratch = ToRegister(instr->temp());
+ Smi* index = instr->index();
+ Label runtime, done;
+ ASSERT(object.is(a0));
+ ASSERT(result.is(v0));
+ ASSERT(!scratch.is(scratch0()));
+ ASSERT(!scratch.is(object));
+
+ __ And(at, object, Operand(kSmiTagMask));
+ DeoptimizeIf(eq, instr->environment(), at, Operand(zero_reg));
+ __ GetObjectType(object, scratch, scratch);
+ DeoptimizeIf(ne, instr->environment(), scratch, Operand(JS_DATE_TYPE));
+
+ if (index->value() == 0) {
+ __ lw(result, FieldMemOperand(object, JSDate::kValueOffset));
+ } else {
+ if (index->value() < JSDate::kFirstUncachedField) {
+ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+ __ li(scratch, Operand(stamp));
+ __ lw(scratch, MemOperand(scratch));
+ __ lw(scratch0(), FieldMemOperand(object, JSDate::kCacheStampOffset));
+ __ Branch(&runtime, ne, scratch, Operand(scratch0()));
+ __ lw(result, FieldMemOperand(object, JSDate::kValueOffset +
+ kPointerSize * index->value()));
+ __ jmp(&done);
+ }
+ __ bind(&runtime);
+ __ PrepareCallCFunction(2, scratch);
+ __ li(a1, Operand(index));
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) {
+ Register string = ToRegister(instr->string());
+ Register index = ToRegister(instr->index());
+ Register value = ToRegister(instr->value());
+ Register scratch = scratch0();
+ String::Encoding encoding = instr->encoding();
+
+ if (FLAG_debug_code) {
+ __ lw(at, FieldMemOperand(string, HeapObject::kMapOffset));
+ __ lbu(at, FieldMemOperand(at, Map::kInstanceTypeOffset));
+
+ __ And(at, at, Operand(kStringRepresentationMask | kStringEncodingMask));
+ static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
+ static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
+ __ Subu(at, at, Operand(encoding == String::ONE_BYTE_ENCODING
+ ? one_byte_seq_type : two_byte_seq_type));
+ __ Check(eq, kUnexpectedStringType, at, Operand(zero_reg));
+ }
+
+ __ Addu(scratch,
+ string,
+ Operand(SeqString::kHeaderSize - kHeapObjectTag));
+ if (encoding == String::ONE_BYTE_ENCODING) {
+ __ Addu(at, scratch, index);
+ __ sb(value, MemOperand(at));
+ } else {
+ __ sll(at, index, 1);
+ __ Addu(at, scratch, at);
+ __ sh(value, MemOperand(at));
+ }
+}
+
+
+void LCodeGen::DoThrow(LThrow* instr) {
+ Register input_reg = EmitLoadRegister(instr->value(), at);
+ __ push(input_reg);
+ CallRuntime(Runtime::kThrow, 1, instr);
+
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
+}
+
+
+void LCodeGen::DoAddI(LAddI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ 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::DoMathMinMax(LMathMinMax* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ HMathMinMax::Operation operation = instr->hydrogen()->operation();
+ Condition condition = (operation == HMathMinMax::kMathMin) ? le : ge;
+ if (instr->hydrogen()->representation().IsSmiOrInteger32()) {
+ Register left_reg = ToRegister(left);
+ Operand right_op = (right->IsRegister() || right->IsConstantOperand())
+ ? ToOperand(right)
+ : Operand(EmitLoadRegister(right, at));
+ Register result_reg = ToRegister(instr->result());
+ Label return_right, done;
+ if (!result_reg.is(left_reg)) {
+ __ Branch(&return_right, NegateCondition(condition), left_reg, right_op);
+ __ mov(result_reg, left_reg);
+ __ Branch(&done);
+ }
+ __ Branch(&done, condition, left_reg, right_op);
+ __ bind(&return_right);
+ __ Addu(result_reg, zero_reg, right_op);
+ __ bind(&done);
+ } else {
+ ASSERT(instr->hydrogen()->representation().IsDouble());
+ FPURegister left_reg = ToDoubleRegister(left);
+ FPURegister right_reg = ToDoubleRegister(right);
+ FPURegister result_reg = ToDoubleRegister(instr->result());
+ Label check_nan_left, check_zero, return_left, return_right, done;
+ __ BranchF(&check_zero, &check_nan_left, eq, left_reg, right_reg);
+ __ BranchF(&return_left, NULL, condition, left_reg, right_reg);
+ __ Branch(&return_right);
+
+ __ bind(&check_zero);
+ // left == right != 0.
+ __ BranchF(&return_left, NULL, ne, left_reg, kDoubleRegZero);
+ // At this point, both left and right are either 0 or -0.
+ if (operation == HMathMinMax::kMathMin) {
+ __ neg_d(left_reg, left_reg);
+ __ sub_d(result_reg, left_reg, right_reg);
+ __ neg_d(result_reg, result_reg);
+ } else {
+ __ add_d(result_reg, left_reg, right_reg);
+ }
+ __ Branch(&done);
+
+ __ bind(&check_nan_left);
+ // left == NaN.
+ __ BranchF(NULL, &return_left, eq, left_reg, left_reg);
+ __ bind(&return_right);
+ if (!right_reg.is(result_reg)) {
+ __ mov_d(result_reg, right_reg);
+ }
+ __ Branch(&done);
+
+ __ bind(&return_left);
+ if (!left_reg.is(result_reg)) {
+ __ mov_d(result_reg, left_reg);
+ }
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
+ DoubleRegister left = ToDoubleRegister(instr->left());
+ DoubleRegister right = ToDoubleRegister(instr->right());
+ 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->left()).is(a1));
+ ASSERT(ToRegister(instr->right()).is(a0));
+ ASSERT(ToRegister(instr->result()).is(v0));
+
+ BinaryOpStub stub(instr->op(), NO_OVERWRITE);
+ CallCode(stub.GetCode(isolate()), 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() const {
+ for (int i = current_block_ + 1; i < graph()->blocks()->length(); ++i) {
+ if (!chunk_->GetLabel(i)->HasReplacement()) return i;
+ }
+ return -1;
+}
+
+template<class InstrType>
+void LCodeGen::EmitBranch(InstrType instr,
+ Condition cc, Register src1, const Operand& src2) {
+ int left_block = instr->TrueDestination(chunk_);
+ int right_block = instr->FalseDestination(chunk_);
+
+ int next_block = GetNextEmittedBlock();
+ if (right_block == left_block || cc == al) {
+ 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));
+ }
+}
+
+
+template<class InstrType>
+void LCodeGen::EmitBranchF(InstrType instr,
+ Condition cc, FPURegister src1, FPURegister src2) {
+ int right_block = instr->FalseDestination(chunk_);
+ int left_block = instr->TrueDestination(chunk_);
+
+ int next_block = GetNextEmittedBlock();
+ 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::DoDebugBreak(LDebugBreak* instr) {
+ __ stop("LDebugBreak");
+}
+
+
+void LCodeGen::DoIsNumberAndBranch(LIsNumberAndBranch* instr) {
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsSmiOrInteger32() || r.IsDouble()) {
+ EmitBranch(instr, al, zero_reg, Operand(zero_reg));
+ } else {
+ ASSERT(r.IsTagged());
+ Register reg = ToRegister(instr->value());
+ HType type = instr->hydrogen()->value()->type();
+ if (type.IsTaggedNumber()) {
+ EmitBranch(instr, al, zero_reg, Operand(zero_reg));
+ }
+ __ JumpIfSmi(reg, instr->TrueLabel(chunk_));
+ __ lw(scratch0(), FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHeapNumberMapRootIndex);
+ EmitBranch(instr, eq, scratch0(), Operand(at));
+ }
+}
+
+
+void LCodeGen::DoBranch(LBranch* instr) {
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsInteger32() || r.IsSmi()) {
+ ASSERT(!info()->IsStub());
+ Register reg = ToRegister(instr->value());
+ EmitBranch(instr, ne, reg, Operand(zero_reg));
+ } else if (r.IsDouble()) {
+ ASSERT(!info()->IsStub());
+ DoubleRegister reg = ToDoubleRegister(instr->value());
+ // Test the double value. Zero and NaN are false.
+ EmitBranchF(instr, nue, reg, kDoubleRegZero);
+ } else {
+ ASSERT(r.IsTagged());
+ Register reg = ToRegister(instr->value());
+ HType type = instr->hydrogen()->value()->type();
+ if (type.IsBoolean()) {
+ ASSERT(!info()->IsStub());
+ __ LoadRoot(at, Heap::kTrueValueRootIndex);
+ EmitBranch(instr, eq, reg, Operand(at));
+ } else if (type.IsSmi()) {
+ ASSERT(!info()->IsStub());
+ EmitBranch(instr, ne, reg, Operand(zero_reg));
+ } else if (type.IsJSArray()) {
+ ASSERT(!info()->IsStub());
+ EmitBranch(instr, al, zero_reg, Operand(zero_reg));
+ } else if (type.IsHeapNumber()) {
+ ASSERT(!info()->IsStub());
+ DoubleRegister dbl_scratch = double_scratch0();
+ __ ldc1(dbl_scratch, FieldMemOperand(reg, HeapNumber::kValueOffset));
+ // Test the double value. Zero and NaN are false.
+ EmitBranchF(instr, nue, dbl_scratch, kDoubleRegZero);
+ } else if (type.IsString()) {
+ ASSERT(!info()->IsStub());
+ __ lw(at, FieldMemOperand(reg, String::kLengthOffset));
+ EmitBranch(instr, ne, at, Operand(zero_reg));
+ } else {
+ 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::Types::Generic();
+
+ if (expected.Contains(ToBooleanStub::UNDEFINED)) {
+ // undefined -> false.
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(instr->FalseLabel(chunk_), eq, reg, Operand(at));
+ }
+ if (expected.Contains(ToBooleanStub::BOOLEAN)) {
+ // Boolean -> its value.
+ __ LoadRoot(at, Heap::kTrueValueRootIndex);
+ __ Branch(instr->TrueLabel(chunk_), eq, reg, Operand(at));
+ __ LoadRoot(at, Heap::kFalseValueRootIndex);
+ __ Branch(instr->FalseLabel(chunk_), eq, reg, Operand(at));
+ }
+ if (expected.Contains(ToBooleanStub::NULL_TYPE)) {
+ // 'null' -> false.
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ __ Branch(instr->FalseLabel(chunk_), eq, reg, Operand(at));
+ }
+
+ if (expected.Contains(ToBooleanStub::SMI)) {
+ // Smis: 0 -> false, all other -> true.
+ __ Branch(instr->FalseLabel(chunk_), eq, reg, Operand(zero_reg));
+ __ JumpIfSmi(reg, instr->TrueLabel(chunk_));
+ } 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(instr->FalseLabel(chunk_), ne, at, Operand(zero_reg));
+ }
+ }
+
+ if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) {
+ // spec object -> true.
+ __ lbu(at, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ Branch(instr->TrueLabel(chunk_),
+ 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(instr->TrueLabel(chunk_), ne, at, Operand(zero_reg));
+ __ Branch(instr->FalseLabel(chunk_));
+ __ bind(&not_string);
+ }
+
+ if (expected.Contains(ToBooleanStub::SYMBOL)) {
+ // Symbol value -> true.
+ const Register scratch = scratch1();
+ __ lbu(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ Branch(instr->TrueLabel(chunk_), eq, scratch, Operand(SYMBOL_TYPE));
+ }
+
+ 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(instr->TrueLabel(chunk_), instr->FalseLabel(chunk_),
+ ne, dbl_scratch, kDoubleRegZero);
+ // Falls through if dbl_scratch == 0.
+ __ Branch(instr->FalseLabel(chunk_));
+ __ bind(&not_heap_number);
+ }
+
+ if (!expected.IsGeneric()) {
+ // We've seen something for the first time -> deopt.
+ // This can only happen if we are not generic already.
+ DeoptimizeIf(al, instr->environment(), zero_reg, Operand(zero_reg));
+ }
+ }
+ }
+}
+
+
+void LCodeGen::EmitGoto(int block) {
+ if (!IsNextEmittedBlock(block)) {
+ __ jmp(chunk_->GetAssemblyLabel(LookupDestination(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::DoCompareNumericAndBranch(LCompareNumericAndBranch* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ 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) ?
+ instr->TrueDestination(chunk_) : instr->FalseDestination(chunk_);
+ 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, instr->FalseLabel(chunk_), eq,
+ left_reg, right_reg);
+
+ EmitBranchF(instr, cond, left_reg, right_reg);
+ } else {
+ Register cmp_left;
+ Operand cmp_right = Operand(0);
+
+ if (right->IsConstantOperand()) {
+ int32_t value = ToInteger32(LConstantOperand::cast(right));
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ cmp_left = ToRegister(left);
+ cmp_right = Operand(Smi::FromInt(value));
+ } else {
+ cmp_left = ToRegister(left);
+ cmp_right = Operand(value);
+ }
+ } else if (left->IsConstantOperand()) {
+ int32_t value = ToInteger32(LConstantOperand::cast(left));
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ cmp_left = ToRegister(right);
+ cmp_right = Operand(Smi::FromInt(value));
+ } else {
+ cmp_left = ToRegister(right);
+ cmp_right = Operand(value);
+ }
+ // We transposed the operands. Reverse the condition.
+ cond = ReverseCondition(cond);
+ } else {
+ cmp_left = ToRegister(left);
+ cmp_right = Operand(ToRegister(right));
+ }
+
+ EmitBranch(instr, cond, cmp_left, cmp_right);
+ }
+ }
+}
+
+
+void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) {
+ Register left = ToRegister(instr->left());
+ Register right = ToRegister(instr->right());
+
+ EmitBranch(instr, eq, left, Operand(right));
+}
+
+
+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->value());
+ Register temp1 = ToRegister(instr->temp());
+ Register temp2 = scratch0();
+
+ Condition true_cond =
+ EmitIsObject(reg, temp1, temp2,
+ instr->FalseLabel(chunk_), instr->TrueLabel(chunk_));
+
+ EmitBranch(instr, true_cond, temp2,
+ Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+}
+
+
+Condition LCodeGen::EmitIsString(Register input,
+ Register temp1,
+ Label* is_not_string,
+ SmiCheck check_needed = INLINE_SMI_CHECK) {
+ if (check_needed == INLINE_SMI_CHECK) {
+ __ JumpIfSmi(input, is_not_string);
+ }
+ __ GetObjectType(input, temp1, temp1);
+
+ return lt;
+}
+
+
+void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ Register temp1 = ToRegister(instr->temp());
+
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ Condition true_cond =
+ EmitIsString(reg, temp1, instr->FalseLabel(chunk_), check_needed);
+
+ EmitBranch(instr, true_cond, temp1,
+ Operand(FIRST_NONSTRING_TYPE));
+}
+
+
+void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
+ Register input_reg = EmitLoadRegister(instr->value(), at);
+ __ And(at, input_reg, kSmiTagMask);
+ EmitBranch(instr, eq, at, Operand(zero_reg));
+}
+
+
+void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ __ JumpIfSmi(input, instr->FalseLabel(chunk_));
+ }
+ __ lw(temp, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ lbu(temp, FieldMemOperand(temp, Map::kBitFieldOffset));
+ __ And(at, temp, Operand(1 << Map::kIsUndetectable));
+ EmitBranch(instr, 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();
+
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+
+ Condition condition = ComputeCompareCondition(op);
+
+ EmitBranch(instr, 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->value());
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ __ JumpIfSmi(input, instr->FalseLabel(chunk_));
+ }
+
+ __ GetObjectType(input, scratch, scratch);
+ EmitBranch(instr,
+ BranchCondition(instr->hydrogen()),
+ scratch,
+ Operand(TestType(instr->hydrogen())));
+}
+
+
+void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+
+ __ AssertString(input);
+
+ __ lw(result, FieldMemOperand(input, String::kHashFieldOffset));
+ __ IndexFromHash(result, result);
+}
+
+
+void LCodeGen::DoHasCachedArrayIndexAndBranch(
+ LHasCachedArrayIndexAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register scratch = scratch0();
+
+ __ lw(scratch,
+ FieldMemOperand(input, String::kHashFieldOffset));
+ __ And(at, scratch, Operand(String::kContainsCachedArrayIndexMask));
+ EmitBranch(instr, 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->IsOneByteEqualTo(STATIC_ASCII_VECTOR("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->IsOneByteEqualTo(STATIC_ASCII_VECTOR("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 internalized since it's a literal.
+ // The name in the constructor is internalized 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 internalized 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->value());
+ Register temp = scratch0();
+ Register temp2 = ToRegister(instr->temp());
+ Handle<String> class_name = instr->hydrogen()->class_name();
+
+ EmitClassOfTest(instr->TrueLabel(chunk_), instr->FalseLabel(chunk_),
+ class_name, input, temp, temp2);
+
+ EmitBranch(instr, eq, temp, Operand(class_name));
+}
+
+
+void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ __ lw(temp, FieldMemOperand(reg, HeapObject::kMapOffset));
+ EmitBranch(instr, eq, temp, Operand(instr->map()));
+}
+
+
+void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
+ Label true_label, done;
+ ASSERT(ToRegister(instr->left()).is(a0)); // Object is in a0.
+ ASSERT(ToRegister(instr->right()).is(a1)); // Function is in a1.
+ Register result = ToRegister(instr->result());
+ ASSERT(result.is(v0));
+
+ InstanceofStub stub(InstanceofStub::kArgsInRegisters);
+ CallCode(stub.GetCode(isolate()), 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(zone()) DeferredInstanceOfKnownGlobal(this, instr);
+
+ Label done, false_result;
+ Register object = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+ 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<Cell> cell = factory()->NewCell(factory()->the_hole_value());
+ __ li(at, Operand(Handle<Object>(cell)));
+ __ lw(at, FieldMemOperand(at, PropertyCell::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()), CONSTANT_SIZE);
+ __ 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->temp());
+ 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), CONSTANT_SIZE);
+ __ StoreToSafepointRegisterSlot(temp, temp);
+ }
+ CallCodeGeneric(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ instr,
+ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
+ LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
+ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
+ // Put the result value into the result register slot and
+ // restore all registers.
+ __ StoreToSafepointRegisterSlot(result, result);
+}
+
+
+void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
+ Register object = ToRegister(instr->object());
+ Register result = ToRegister(instr->result());
+ __ lw(result, FieldMemOperand(object, HeapObject::kMapOffset));
+ __ lbu(result, FieldMemOperand(result, Map::kInstanceSizeOffset));
+}
+
+
+void LCodeGen::DoCmpT(LCmpT* instr) {
+ Token::Value op = instr->op();
+
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), 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, check;
+ __ Branch(USE_DELAY_SLOT, &done, condition, v0, Operand(zero_reg));
+ __ bind(&check);
+ __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
+ ASSERT_EQ(1, masm()->InstructionsGeneratedSince(&check));
+ __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoReturn(LReturn* instr) {
+ if (FLAG_trace && info()->IsOptimizing()) {
+ // Push the return value on the stack as the parameter.
+ // Runtime::TraceExit returns its parameter in v0.
+ __ push(v0);
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+ if (info()->saves_caller_doubles()) {
+ ASSERT(NeedsEagerFrame());
+ BitVector* doubles = chunk()->allocated_double_registers();
+ BitVector::Iterator save_iterator(doubles);
+ int count = 0;
+ while (!save_iterator.Done()) {
+ __ ldc1(DoubleRegister::FromAllocationIndex(save_iterator.Current()),
+ MemOperand(sp, count * kDoubleSize));
+ save_iterator.Advance();
+ count++;
+ }
+ }
+ int no_frame_start = -1;
+ if (NeedsEagerFrame()) {
+ __ mov(sp, fp);
+ no_frame_start = masm_->pc_offset();
+ __ Pop(ra, fp);
+ }
+ if (instr->has_constant_parameter_count()) {
+ int parameter_count = ToInteger32(instr->constant_parameter_count());
+ int32_t sp_delta = (parameter_count + 1) * kPointerSize;
+ if (sp_delta != 0) {
+ __ Addu(sp, sp, Operand(sp_delta));
+ }
+ } else {
+ Register reg = ToRegister(instr->parameter_count());
+ // The argument count parameter is a smi
+ __ SmiUntag(reg);
+ __ sll(at, reg, kPointerSizeLog2);
+ __ Addu(sp, sp, at);
+ }
+
+ __ Jump(ra);
+
+ if (no_frame_start != -1) {
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
+}
+
+
+void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
+ Register result = ToRegister(instr->result());
+ __ li(at, Operand(Handle<Object>(instr->hydrogen()->cell())));
+ __ lw(result, FieldMemOperand(at, Cell::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->temp());
+ __ lw(payload, FieldMemOperand(cell, Cell::kValueOffset));
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(eq, instr->environment(), payload, Operand(at));
+ }
+
+ // Store the value.
+ __ sw(value, FieldMemOperand(cell, Cell::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()) {
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ __ RecordWriteContextSlot(context,
+ target.offset(),
+ value,
+ scratch0(),
+ GetRAState(),
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+
+ __ bind(&skip_assignment);
+}
+
+
+void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
+ HObjectAccess access = instr->hydrogen()->access();
+ int offset = access.offset();
+ Register object = ToRegister(instr->object());
+
+ if (access.IsExternalMemory()) {
+ Register result = ToRegister(instr->result());
+ __ lw(result, MemOperand(object, offset));
+ return;
+ }
+
+ if (instr->hydrogen()->representation().IsDouble()) {
+ DoubleRegister result = ToDoubleRegister(instr->result());
+ __ ldc1(result, FieldMemOperand(object, offset));
+ return;
+ }
+
+ Register result = ToRegister(instr->result());
+ if (access.IsInobject()) {
+ __ lw(result, FieldMemOperand(object, offset));
+ } else {
+ __ lw(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
+ __ lw(result, FieldMemOperand(result, offset));
+ }
+}
+
+
+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::DoLoadExternalArrayPointer(
+ LLoadExternalArrayPointer* instr) {
+ Register to_reg = ToRegister(instr->result());
+ Register from_reg = ToRegister(instr->object());
+ __ lw(to_reg, FieldMemOperand(from_reg,
+ ExternalArray::kExternalPointerOffset));
+}
+
+
+void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
+ Register arguments = ToRegister(instr->arguments());
+ Register result = ToRegister(instr->result());
+ if (instr->length()->IsConstantOperand() &&
+ instr->index()->IsConstantOperand()) {
+ int const_index = ToInteger32(LConstantOperand::cast(instr->index()));
+ int const_length = ToInteger32(LConstantOperand::cast(instr->length()));
+ int index = (const_length - const_index) + 1;
+ __ lw(result, MemOperand(arguments, index * kPointerSize));
+ } else {
+ Register length = ToRegister(instr->length());
+ Register index = ToRegister(instr->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::DoLoadKeyedExternalArray(LLoadKeyed* instr) {
+ Register external_pointer = ToRegister(instr->elements());
+ 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(kArrayIndexConstantValueTooBig);
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+ int element_size_shift = ElementsKindToShiftSize(elements_kind);
+ int shift_size = (instr->hydrogen()->key()->representation().IsSmi())
+ ? (element_size_shift - kSmiTagSize) : element_size_shift;
+ int additional_offset = instr->additional_index() << element_size_shift;
+
+ 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 << element_size_shift);
+ } else {
+ __ sll(scratch0(), key, shift_size);
+ __ Addu(scratch0(), scratch0(), external_pointer);
+ }
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ __ lwc1(result, MemOperand(scratch0(), additional_offset));
+ __ cvt_d_s(result, result);
+ } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS
+ __ ldc1(result, MemOperand(scratch0(), additional_offset));
+ }
+ } else {
+ Register result = ToRegister(instr->result());
+ MemOperand mem_operand = PrepareKeyedOperand(
+ key, external_pointer, key_is_constant, constant_key,
+ element_size_shift, shift_size,
+ instr->additional_index(), additional_offset);
+ 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);
+ if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) {
+ 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_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedFixedDoubleArray(LLoadKeyed* 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 element_size_shift = ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS);
+ int shift_size = (instr->hydrogen()->key()->representation().IsSmi())
+ ? (element_size_shift - kSmiTagSize) : element_size_shift;
+ int constant_key = 0;
+ if (key_is_constant) {
+ constant_key = ToInteger32(LConstantOperand::cast(instr->key()));
+ if (constant_key & 0xF0000000) {
+ Abort(kArrayIndexConstantValueTooBig);
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+
+ int base_offset = (FixedDoubleArray::kHeaderSize - kHeapObjectTag) +
+ ((constant_key + instr->additional_index()) << element_size_shift);
+ if (!key_is_constant) {
+ __ sll(scratch, key, shift_size);
+ __ Addu(elements, elements, scratch);
+ }
+ __ Addu(elements, elements, Operand(base_offset));
+ __ ldc1(result, MemOperand(elements));
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ lw(scratch, MemOperand(elements, sizeof(kHoleNanLower32)));
+ DeoptimizeIf(eq, instr->environment(), scratch, Operand(kHoleNanUpper32));
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedFixedArray(LLoadKeyed* instr) {
+ Register elements = ToRegister(instr->elements());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+ Register store_base = scratch;
+ int offset = 0;
+
+ if (instr->key()->IsConstantOperand()) {
+ LConstantOperand* const_operand = LConstantOperand::cast(instr->key());
+ offset = FixedArray::OffsetOfElementAt(ToInteger32(const_operand) +
+ instr->additional_index());
+ store_base = elements;
+ } else {
+ Register key = EmitLoadRegister(instr->key(), scratch0());
+ // Even though the HLoadKeyed instruction forces the input
+ // representation for the key to be an integer, the input gets replaced
+ // during bound check elimination with the index argument to the bounds
+ // check, which can be tagged, so that case must be handled here, too.
+ if (instr->hydrogen()->key()->representation().IsSmi()) {
+ __ sll(scratch, key, kPointerSizeLog2 - kSmiTagSize);
+ __ addu(scratch, elements, scratch);
+ } else {
+ __ sll(scratch, key, kPointerSizeLog2);
+ __ addu(scratch, elements, scratch);
+ }
+ offset = FixedArray::OffsetOfElementAt(instr->additional_index());
+ }
+ __ lw(result, FieldMemOperand(store_base, offset));
+
+ // Check for the hole value.
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ if (IsFastSmiElementsKind(instr->hydrogen()->elements_kind())) {
+ __ And(scratch, result, Operand(kSmiTagMask));
+ DeoptimizeIf(ne, instr->environment(), scratch, Operand(zero_reg));
+ } else {
+ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(eq, instr->environment(), result, Operand(scratch));
+ }
+ }
+}
+
+
+void LCodeGen::DoLoadKeyed(LLoadKeyed* instr) {
+ if (instr->is_external()) {
+ DoLoadKeyedExternalArray(instr);
+ } else if (instr->hydrogen()->representation().IsDouble()) {
+ DoLoadKeyedFixedDoubleArray(instr);
+ } else {
+ DoLoadKeyedFixedArray(instr);
+ }
+}
+
+
+MemOperand LCodeGen::PrepareKeyedOperand(Register key,
+ Register base,
+ bool key_is_constant,
+ int constant_key,
+ int element_size,
+ int shift_size,
+ int additional_index,
+ int additional_offset) {
+ if (additional_index != 0 && !key_is_constant) {
+ additional_index *= 1 << (element_size - shift_size);
+ __ Addu(scratch0(), key, Operand(additional_index));
+ }
+
+ if (key_is_constant) {
+ return MemOperand(base,
+ (constant_key << element_size) + additional_offset);
+ }
+
+ if (additional_index == 0) {
+ if (shift_size >= 0) {
+ __ sll(scratch0(), key, shift_size);
+ __ Addu(scratch0(), base, scratch0());
+ return MemOperand(scratch0());
+ } else {
+ ASSERT_EQ(-1, shift_size);
+ __ srl(scratch0(), key, 1);
+ __ Addu(scratch0(), base, scratch0());
+ return MemOperand(scratch0());
+ }
+ }
+
+ if (shift_size >= 0) {
+ __ sll(scratch0(), scratch0(), shift_size);
+ __ Addu(scratch0(), base, scratch0());
+ return MemOperand(scratch0());
+ } else {
+ ASSERT_EQ(-1, shift_size);
+ __ srl(scratch0(), scratch0(), 1);
+ __ Addu(scratch0(), base, scratch0());
+ return MemOperand(scratch0());
+ }
+}
+
+
+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());
+
+ if (instr->hydrogen()->from_inlined()) {
+ __ Subu(result, sp, 2 * kPointerSize);
+ } else {
+ // 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->elements());
+ 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::DoWrapReceiver(LWrapReceiver* instr) {
+ Register receiver = ToRegister(instr->receiver());
+ Register function = ToRegister(instr->function());
+ Register scratch = scratch0();
+
+ // 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);
+}
+
+
+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));
+
+ // 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());
+ 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->value();
+ if (argument->IsDoubleRegister() || argument->IsDoubleStackSlot()) {
+ Abort(kDoPushArgumentNotImplementedForDoubleType);
+ } else {
+ Register argument_reg = EmitLoadRegister(argument, at);
+ __ push(argument_reg);
+ }
+}
+
+
+void LCodeGen::DoDrop(LDrop* instr) {
+ __ Drop(instr->count());
+}
+
+
+void LCodeGen::DoThisFunction(LThisFunction* instr) {
+ Register result = ToRegister(instr->result());
+ __ lw(result, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+}
+
+
+void LCodeGen::DoContext(LContext* instr) {
+ // If there is a non-return use, the context must be moved to a register.
+ Register result = ToRegister(instr->result());
+ for (HUseIterator it(instr->hydrogen()->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->IsReturn()) {
+ __ mov(result, cp);
+ return;
+ }
+ }
+}
+
+
+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::DoDeclareGlobals(LDeclareGlobals* instr) {
+ __ LoadHeapObject(scratch0(), instr->hydrogen()->pairs());
+ __ li(scratch1(), Operand(Smi::FromInt(instr->hydrogen()->flags())));
+ // The context is the first argument.
+ __ Push(cp, scratch0(), scratch1());
+ CallRuntime(Runtime::kDeclareGlobals, 3, instr);
+}
+
+
+void LCodeGen::DoGlobalObject(LGlobalObject* instr) {
+ Register result = ToRegister(instr->result());
+ __ lw(result, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+}
+
+
+void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
+ Register global = ToRegister(instr->global_object());
+ Register result = ToRegister(instr->result());
+ __ lw(result, FieldMemOperand(global, GlobalObject::kGlobalReceiverOffset));
+}
+
+
+void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
+ int formal_parameter_count,
+ int arity,
+ LInstruction* instr,
+ CallKind call_kind,
+ A1State a1_state) {
+ bool dont_adapt_arguments =
+ formal_parameter_count == SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+ bool can_invoke_directly =
+ dont_adapt_arguments || formal_parameter_count == arity;
+
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+
+ if (can_invoke_directly) {
+ if (a1_state == A1_UNINITIALIZED) {
+ __ LoadHeapObject(a1, function);
+ }
+
+ // 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 (dont_adapt_arguments) {
+ __ 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);
+ ParameterCount expected(formal_parameter_count);
+ __ InvokeFunction(
+ function, expected, 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->hydrogen()->function(),
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD,
+ A1_UNINITIALIZED);
+}
+
+
+void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr) {
+ Register input = ToRegister(instr->value());
+ 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(LMathAbs* instr) {
+ Register input = ToRegister(instr->value());
+ 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);
+ __ 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(LMathAbs* instr) {
+ // Class for deferred case.
+ class DeferredMathAbsTaggedHeapNumber: public LDeferredCode {
+ public:
+ DeferredMathAbsTaggedHeapNumber(LCodeGen* codegen, LMathAbs* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LMathAbs* instr_;
+ };
+
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsDouble()) {
+ FPURegister input = ToDoubleRegister(instr->value());
+ FPURegister result = ToDoubleRegister(instr->result());
+ __ abs_d(result, input);
+ } else if (r.IsSmiOrInteger32()) {
+ EmitIntegerMathAbs(instr);
+ } else {
+ // Representation is tagged.
+ DeferredMathAbsTaggedHeapNumber* deferred =
+ new(zone()) DeferredMathAbsTaggedHeapNumber(this, instr);
+ Register input = ToRegister(instr->value());
+ // Smi check.
+ __ JumpIfNotSmi(input, deferred->entry());
+ // If smi, handle it directly.
+ EmitIntegerMathAbs(instr);
+ __ bind(deferred->exit());
+ }
+}
+
+
+void LCodeGen::DoMathFloor(LMathFloor* instr) {
+ DoubleRegister input = ToDoubleRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ Register scratch1 = scratch0();
+ Register except_flag = ToRegister(instr->temp());
+
+ __ EmitFPUTruncate(kRoundToMinusInf,
+ result,
+ input,
+ scratch1,
+ double_scratch0(),
+ except_flag);
+
+ // Deopt if the operation did not succeed.
+ DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg));
+
+ 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(LMathRound* instr) {
+ DoubleRegister input = ToDoubleRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ DoubleRegister double_scratch1 = ToDoubleRegister(instr->temp());
+ 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,
+ result,
+ double_scratch0(),
+ at,
+ double_scratch1,
+ except_flag);
+
+ DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg));
+
+ 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(LMathSqrt* instr) {
+ DoubleRegister input = ToDoubleRegister(instr->value());
+ DoubleRegister result = ToDoubleRegister(instr->result());
+ __ sqrt_d(result, input);
+}
+
+
+void LCodeGen::DoMathPowHalf(LMathPowHalf* instr) {
+ DoubleRegister input = ToDoubleRegister(instr->value());
+ DoubleRegister result = ToDoubleRegister(instr->result());
+ DoubleRegister temp = ToDoubleRegister(instr->temp());
+
+ 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->right()->IsDoubleRegister() ||
+ ToDoubleRegister(instr->right()).is(f4));
+ ASSERT(!instr->right()->IsRegister() ||
+ ToRegister(instr->right()).is(a2));
+ ASSERT(ToDoubleRegister(instr->left()).is(f2));
+ ASSERT(ToDoubleRegister(instr->result()).is(f0));
+
+ if (exponent_type.IsSmi()) {
+ MathPowStub stub(MathPowStub::TAGGED);
+ __ CallStub(&stub);
+ } else 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) {
+ class DeferredDoRandom: public LDeferredCode {
+ public:
+ DeferredDoRandom(LCodeGen* codegen, LRandom* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredRandom(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LRandom* instr_;
+ };
+
+ DeferredDoRandom* deferred = new(zone()) DeferredDoRandom(this, instr);
+ // Having marked this instruction as a call we can use any
+ // registers.
+ ASSERT(ToDoubleRegister(instr->result()).is(f0));
+ ASSERT(ToRegister(instr->global_object()).is(a0));
+
+ static const int kSeedSize = sizeof(uint32_t);
+ STATIC_ASSERT(kPointerSize == kSeedSize);
+
+ __ lw(a0, FieldMemOperand(a0, GlobalObject::kNativeContextOffset));
+ static const int kRandomSeedOffset =
+ FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
+ __ lw(a2, FieldMemOperand(a0, kRandomSeedOffset));
+ // a2: FixedArray of the native context's random seeds
+
+ // Load state[0].
+ __ lw(a1, FieldMemOperand(a2, ByteArray::kHeaderSize));
+ __ Branch(deferred->entry(), eq, a1, Operand(zero_reg));
+ // Load state[1].
+ __ lw(a0, FieldMemOperand(a2, ByteArray::kHeaderSize + kSeedSize));
+ // a1: state[0].
+ // a0: state[1].
+
+ // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
+ __ And(a3, a1, Operand(0xFFFF));
+ __ li(t0, Operand(18273));
+ __ Mul(a3, a3, t0);
+ __ srl(a1, a1, 16);
+ __ Addu(a1, a3, a1);
+ // Save state[0].
+ __ sw(a1, FieldMemOperand(a2, ByteArray::kHeaderSize));
+
+ // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
+ __ And(a3, a0, Operand(0xFFFF));
+ __ li(t0, Operand(36969));
+ __ Mul(a3, a3, t0);
+ __ srl(a0, a0, 16),
+ __ Addu(a0, a3, a0);
+ // Save state[1].
+ __ sw(a0, FieldMemOperand(a2, ByteArray::kHeaderSize + kSeedSize));
+
+ // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF)
+ __ And(a0, a0, Operand(0x3FFFF));
+ __ sll(a1, a1, 14);
+ __ Addu(v0, a0, a1);
+
+ __ bind(deferred->exit());
+
+ // 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::DoDeferredRandom(LRandom* instr) {
+ __ PrepareCallCFunction(1, scratch0());
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
+ // Return value is in v0.
+}
+
+
+void LCodeGen::DoMathExp(LMathExp* instr) {
+ DoubleRegister input = ToDoubleRegister(instr->value());
+ DoubleRegister result = ToDoubleRegister(instr->result());
+ DoubleRegister double_scratch1 = ToDoubleRegister(instr->double_temp());
+ DoubleRegister double_scratch2 = double_scratch0();
+ Register temp1 = ToRegister(instr->temp1());
+ Register temp2 = ToRegister(instr->temp2());
+
+ MathExpGenerator::EmitMathExp(
+ masm(), input, result, double_scratch1, double_scratch2,
+ temp1, temp2, scratch0());
+}
+
+
+void LCodeGen::DoMathLog(LMathLog* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(f4));
+ TranscendentalCacheStub stub(TranscendentalCache::LOG,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathTan(LMathTan* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(f4));
+ TranscendentalCacheStub stub(TranscendentalCache::TAN,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathCos(LMathCos* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(f4));
+ TranscendentalCacheStub stub(TranscendentalCache::COS,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathSin(LMathSin* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(f4));
+ TranscendentalCacheStub stub(TranscendentalCache::SIN,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(a1));
+ ASSERT(instr->HasPointerMap());
+
+ Handle<JSFunction> known_function = instr->hydrogen()->known_function();
+ if (known_function.is_null()) {
+ 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));
+ } else {
+ CallKnownFunction(known_function,
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD,
+ A1_CONTAINS_TARGET);
+ }
+}
+
+
+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(isolate()), 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->hydrogen()->target(),
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_FUNCTION,
+ A1_UNINITIALIZED);
+}
+
+
+void LCodeGen::DoCallNew(LCallNew* instr) {
+ ASSERT(ToRegister(instr->constructor()).is(a1));
+ ASSERT(ToRegister(instr->result()).is(v0));
+
+ __ li(a0, Operand(instr->arity()));
+ // No cell in a2 for construct type feedback in optimized code
+ Handle<Object> undefined_value(isolate()->factory()->undefined_value());
+ __ li(a2, Operand(undefined_value));
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+}
+
+
+void LCodeGen::DoCallNewArray(LCallNewArray* instr) {
+ ASSERT(ToRegister(instr->constructor()).is(a1));
+ ASSERT(ToRegister(instr->result()).is(v0));
+
+ __ li(a0, Operand(instr->arity()));
+ __ li(a2, Operand(instr->hydrogen()->property_cell()));
+ ElementsKind kind = instr->hydrogen()->elements_kind();
+ AllocationSiteOverrideMode override_mode =
+ (AllocationSite::GetMode(kind) == TRACK_ALLOCATION_SITE)
+ ? DISABLE_ALLOCATION_SITES
+ : DONT_OVERRIDE;
+ ContextCheckMode context_mode = CONTEXT_CHECK_NOT_REQUIRED;
+
+ if (instr->arity() == 0) {
+ ArrayNoArgumentConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ } else if (instr->arity() == 1) {
+ Label done;
+ if (IsFastPackedElementsKind(kind)) {
+ Label packed_case;
+ // We might need a change here,
+ // look at the first argument.
+ __ lw(t1, MemOperand(sp, 0));
+ __ Branch(&packed_case, eq, t1, Operand(zero_reg));
+
+ ElementsKind holey_kind = GetHoleyElementsKind(kind);
+ ArraySingleArgumentConstructorStub stub(holey_kind, context_mode,
+ override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ __ jmp(&done);
+ __ bind(&packed_case);
+ }
+
+ ArraySingleArgumentConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ __ bind(&done);
+ } else {
+ ArrayNArgumentsConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ }
+}
+
+
+void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
+ CallRuntime(instr->function(), instr->arity(), instr);
+}
+
+
+void LCodeGen::DoInnerAllocatedObject(LInnerAllocatedObject* instr) {
+ Register result = ToRegister(instr->result());
+ Register base = ToRegister(instr->base_object());
+ __ Addu(result, base, Operand(instr->offset()));
+}
+
+
+void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
+ Representation representation = instr->representation();
+
+ Register object = ToRegister(instr->object());
+ Register scratch = scratch0();
+ HObjectAccess access = instr->hydrogen()->access();
+ int offset = access.offset();
+
+ if (access.IsExternalMemory()) {
+ Register value = ToRegister(instr->value());
+ __ sw(value, MemOperand(object, offset));
+ return;
+ }
+
+ Handle<Map> transition = instr->transition();
+
+ if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ Register value = ToRegister(instr->value());
+ if (!instr->hydrogen()->value()->type().IsHeapObject()) {
+ __ And(scratch, value, Operand(kSmiTagMask));
+ DeoptimizeIf(eq, instr->environment(), scratch, Operand(zero_reg));
+ }
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ ASSERT(transition.is_null());
+ ASSERT(access.IsInobject());
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ DoubleRegister value = ToDoubleRegister(instr->value());
+ __ sdc1(value, FieldMemOperand(object, offset));
+ return;
+ }
+
+ if (!transition.is_null()) {
+ __ li(scratch, Operand(transition));
+ __ sw(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ if (instr->hydrogen()->NeedsWriteBarrierForMap()) {
+ Register temp = ToRegister(instr->temp());
+ // Update the write barrier for the map field.
+ __ RecordWriteField(object,
+ HeapObject::kMapOffset,
+ scratch,
+ temp,
+ GetRAState(),
+ kSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ }
+ }
+
+ // Do the store.
+ Register value = ToRegister(instr->value());
+ ASSERT(!object.is(value));
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ if (access.IsInobject()) {
+ __ 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,
+ GetRAState(),
+ 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,
+ GetRAState(),
+ 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::ApplyCheckIf(Condition cc,
+ LBoundsCheck* check,
+ Register src1,
+ const Operand& src2) {
+ if (FLAG_debug_code && check->hydrogen()->skip_check()) {
+ Label done;
+ __ Branch(&done, NegateCondition(cc), src1, src2);
+ __ stop("eliminated bounds check failed");
+ __ bind(&done);
+ } else {
+ DeoptimizeIf(cc, check->environment(), src1, src2);
+ }
+}
+
+
+void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
+ if (instr->hydrogen()->skip_check()) return;
+
+ Condition condition = instr->hydrogen()->allow_equality() ? hi : hs;
+ if (instr->index()->IsConstantOperand()) {
+ int constant_index =
+ ToInteger32(LConstantOperand::cast(instr->index()));
+ if (instr->hydrogen()->length()->representation().IsSmi()) {
+ __ li(at, Operand(Smi::FromInt(constant_index)));
+ } else {
+ __ li(at, Operand(constant_index));
+ }
+ ApplyCheckIf(condition,
+ instr,
+ at,
+ Operand(ToRegister(instr->length())));
+ } else {
+ ApplyCheckIf(condition,
+ instr,
+ ToRegister(instr->index()),
+ Operand(ToRegister(instr->length())));
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) {
+ Register external_pointer = ToRegister(instr->elements());
+ 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(kArrayIndexConstantValueTooBig);
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+ int element_size_shift = ElementsKindToShiftSize(elements_kind);
+ int shift_size = (instr->hydrogen()->key()->representation().IsSmi())
+ ? (element_size_shift - kSmiTagSize) : element_size_shift;
+ int additional_offset = instr->additional_index() << element_size_shift;
+
+ 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 <<
+ element_size_shift);
+ } 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(), additional_offset));
+ } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS
+ __ sdc1(value, MemOperand(scratch0(), additional_offset));
+ }
+ } else {
+ Register value(ToRegister(instr->value()));
+ MemOperand mem_operand = PrepareKeyedOperand(
+ key, external_pointer, key_is_constant, constant_key,
+ element_size_shift, shift_size,
+ instr->additional_index(), additional_offset);
+ 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_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedFixedDoubleArray(LStoreKeyed* 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(kArrayIndexConstantValueTooBig);
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+ int element_size_shift = ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS);
+ int shift_size = (instr->hydrogen()->key()->representation().IsSmi())
+ ? (element_size_shift - kSmiTagSize) : element_size_shift;
+ if (key_is_constant) {
+ __ Addu(scratch, elements, Operand((constant_key << element_size_shift) +
+ FixedDoubleArray::kHeaderSize - kHeapObjectTag));
+ } else {
+ __ sll(scratch, key, shift_size);
+ __ Addu(scratch, elements, Operand(scratch));
+ __ Addu(scratch, scratch,
+ Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag));
+ }
+
+ if (instr->NeedsCanonicalization()) {
+ 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, instr->additional_index() <<
+ element_size_shift));
+}
+
+
+void LCodeGen::DoStoreKeyedFixedArray(LStoreKeyed* instr) {
+ Register value = ToRegister(instr->value());
+ Register elements = ToRegister(instr->elements());
+ Register key = instr->key()->IsRegister() ? ToRegister(instr->key())
+ : no_reg;
+ Register scratch = scratch0();
+ Register store_base = scratch;
+ int offset = 0;
+
+ // Do the store.
+ if (instr->key()->IsConstantOperand()) {
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ LConstantOperand* const_operand = LConstantOperand::cast(instr->key());
+ offset = FixedArray::OffsetOfElementAt(ToInteger32(const_operand) +
+ instr->additional_index());
+ store_base = elements;
+ } else {
+ // Even though the HLoadKeyed instruction forces the input
+ // representation for the key to be an integer, the input gets replaced
+ // during bound check elimination with the index argument to the bounds
+ // check, which can be tagged, so that case must be handled here, too.
+ if (instr->hydrogen()->key()->representation().IsSmi()) {
+ __ sll(scratch, key, kPointerSizeLog2 - kSmiTagSize);
+ __ addu(scratch, elements, scratch);
+ } else {
+ __ sll(scratch, key, kPointerSizeLog2);
+ __ addu(scratch, elements, scratch);
+ }
+ offset = FixedArray::OffsetOfElementAt(instr->additional_index());
+ }
+ __ sw(value, FieldMemOperand(store_base, offset));
+
+ if (instr->hydrogen()->NeedsWriteBarrier()) {
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ // Compute address of modified element and store it into key register.
+ __ Addu(key, store_base, Operand(offset - kHeapObjectTag));
+ __ RecordWrite(elements,
+ key,
+ value,
+ GetRAState(),
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+}
+
+
+void LCodeGen::DoStoreKeyed(LStoreKeyed* instr) {
+ // By cases: external, fast double
+ if (instr->is_external()) {
+ DoStoreKeyedExternalArray(instr);
+ } else if (instr->hydrogen()->value()->representation().IsDouble()) {
+ DoStoreKeyedFixedDoubleArray(instr);
+ } else {
+ DoStoreKeyedFixedArray(instr);
+ }
+}
+
+
+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 scratch = scratch0();
+
+ Handle<Map> from_map = instr->original_map();
+ Handle<Map> to_map = instr->transitioned_map();
+ ElementsKind from_kind = instr->from_kind();
+ ElementsKind to_kind = instr->to_kind();
+
+ Label not_applicable;
+ __ lw(scratch, FieldMemOperand(object_reg, HeapObject::kMapOffset));
+ __ Branch(&not_applicable, ne, scratch, Operand(from_map));
+
+ if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
+ Register new_map_reg = ToRegister(instr->new_map_temp());
+ __ li(new_map_reg, Operand(to_map));
+ __ sw(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset));
+ // Write barrier.
+ __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
+ scratch, GetRAState(), kDontSaveFPRegs);
+ } else {
+ PushSafepointRegistersScope scope(
+ this, Safepoint::kWithRegistersAndDoubles);
+ __ mov(a0, object_reg);
+ __ li(a1, Operand(to_map));
+ TransitionElementsKindStub stub(from_kind, to_kind);
+ __ CallStub(&stub);
+ RecordSafepointWithRegistersAndDoubles(
+ instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
+ }
+ __ bind(&not_applicable);
+}
+
+
+void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
+ Register object = ToRegister(instr->object());
+ Register temp = ToRegister(instr->temp());
+ Label fail;
+ __ TestJSArrayForAllocationMemento(object, temp, ne, &fail);
+ DeoptimizeIf(al, instr->environment());
+ __ bind(&fail);
+}
+
+
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ __ push(ToRegister(instr->left()));
+ __ push(ToRegister(instr->right()));
+ StringAddStub stub(instr->hydrogen()->flags());
+ CallCode(stub.GetCode(isolate()), 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(zone()) 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);
+ __ AssertSmi(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(zone()) 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::kMaxOneByteCharCode));
+ __ 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::DoInteger32ToDouble(LInteger32ToDouble* instr) {
+ LOperand* input = instr->value();
+ 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::DoInteger32ToSmi(LInteger32ToSmi* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ LOperand* output = instr->result();
+ ASSERT(output->IsRegister());
+ Register scratch = scratch0();
+
+ __ SmiTagCheckOverflow(ToRegister(output), ToRegister(input), scratch);
+ if (!instr->hydrogen()->value()->HasRange() ||
+ !instr->hydrogen()->value()->range()->IsInSmiRange()) {
+ DeoptimizeIf(lt, instr->environment(), scratch, Operand(zero_reg));
+ }
+}
+
+
+void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) {
+ LOperand* input = instr->value();
+ LOperand* output = instr->result();
+
+ FPURegister dbl_scratch = double_scratch0();
+ __ mtc1(ToRegister(input), dbl_scratch);
+ __ Cvt_d_uw(ToDoubleRegister(output), dbl_scratch, f22);
+}
+
+
+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_,
+ instr_->value(),
+ SIGNED_INT32);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LNumberTagI* instr_;
+ };
+
+ Register src = ToRegister(instr->value());
+ Register dst = ToRegister(instr->result());
+ Register overflow = scratch0();
+
+ DeferredNumberTagI* deferred = new(zone()) DeferredNumberTagI(this, instr);
+ __ SmiTagCheckOverflow(dst, src, overflow);
+ __ BranchOnOverflow(deferred->entry(), overflow);
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
+ class DeferredNumberTagU: public LDeferredCode {
+ public:
+ DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredNumberTagI(instr_,
+ instr_->value(),
+ UNSIGNED_INT32);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LNumberTagU* instr_;
+ };
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ Register reg = ToRegister(input);
+
+ DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr);
+ __ Branch(deferred->entry(), hi, reg, Operand(Smi::kMaxValue));
+ __ SmiTag(reg, reg);
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredNumberTagI(LInstruction* instr,
+ LOperand* value,
+ IntegerSignedness signedness) {
+ Label slow;
+ Register src = ToRegister(value);
+ Register dst = ToRegister(instr->result());
+ DoubleRegister dbl_scratch = double_scratch0();
+
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+
+ Label done;
+ if (signedness == SIGNED_INT32) {
+ // 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.
+ if (dst.is(src)) {
+ __ SmiUntag(src, dst);
+ __ Xor(src, src, Operand(0x80000000));
+ }
+ __ mtc1(src, dbl_scratch);
+ __ cvt_d_w(dbl_scratch, dbl_scratch);
+ } else {
+ __ mtc1(src, dbl_scratch);
+ __ Cvt_d_uw(dbl_scratch, dbl_scratch, f22);
+ }
+
+ if (FLAG_inline_new) {
+ __ LoadRoot(scratch0(), Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(t1, a3, t0, scratch0(), &slow, DONT_TAG_RESULT);
+ __ 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);
+ __ Subu(dst, dst, kHeapObjectTag);
+
+ // Done. Put the value in dbl_scratch into the value of the allocated heap
+ // number.
+ __ bind(&done);
+ __ sdc1(dbl_scratch, MemOperand(dst, HeapNumber::kValueOffset));
+ __ Addu(dst, dst, kHeapObjectTag);
+ __ 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->value());
+ Register scratch = scratch0();
+ Register reg = ToRegister(instr->result());
+ Register temp1 = ToRegister(instr->temp());
+ Register temp2 = ToRegister(instr->temp2());
+
+ bool convert_hole = false;
+ HValue* change_input = instr->hydrogen()->value();
+ if (change_input->IsLoadKeyed()) {
+ HLoadKeyed* load = HLoadKeyed::cast(change_input);
+ convert_hole = load->UsesMustHandleHole();
+ }
+
+ Label no_special_nan_handling;
+ Label done;
+ if (convert_hole) {
+ DoubleRegister input_reg = ToDoubleRegister(instr->value());
+ __ BranchF(&no_special_nan_handling, NULL, eq, input_reg, input_reg);
+ __ Move(reg, scratch0(), input_reg);
+ Label canonicalize;
+ __ Branch(&canonicalize, ne, scratch0(), Operand(kHoleNanUpper32));
+ __ li(reg, factory()->undefined_value());
+ __ Branch(&done);
+ __ bind(&canonicalize);
+ __ Move(input_reg,
+ FixedDoubleArray::canonical_not_the_hole_nan_as_double());
+ }
+
+ __ bind(&no_special_nan_handling);
+ DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr);
+ if (FLAG_inline_new) {
+ __ LoadRoot(scratch, Heap::kHeapNumberMapRootIndex);
+ // We want the untagged address first for performance
+ __ AllocateHeapNumber(reg, temp1, temp2, scratch, deferred->entry(),
+ DONT_TAG_RESULT);
+ } else {
+ __ Branch(deferred->entry());
+ }
+ __ bind(deferred->exit());
+ __ sdc1(input_reg, MemOperand(reg, HeapNumber::kValueOffset));
+ // Now that we have finished with the object's real address tag it
+ __ Addu(reg, reg, kHeapObjectTag);
+ __ bind(&done);
+}
+
+
+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);
+ __ Subu(v0, v0, kHeapObjectTag);
+ __ StoreToSafepointRegisterSlot(v0, reg);
+}
+
+
+void LCodeGen::DoSmiTag(LSmiTag* instr) {
+ ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow));
+ __ SmiTag(ToRegister(instr->result()), ToRegister(instr->value()));
+}
+
+
+void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
+ Register scratch = scratch0();
+ Register input = ToRegister(instr->value());
+ 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 allow_undefined_as_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode) {
+ Register scratch = scratch0();
+
+ Label load_smi, heap_number, done;
+
+ STATIC_ASSERT(NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE >
+ NUMBER_CANDIDATE_IS_ANY_TAGGED);
+ if (mode >= NUMBER_CANDIDATE_IS_ANY_TAGGED) {
+ // Smi check.
+ __ UntagAndJumpIfSmi(scratch, input_reg, &load_smi);
+
+ // Heap number map check.
+ __ lw(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHeapNumberMapRootIndex);
+ if (!allow_undefined_as_nan) {
+ DeoptimizeIf(ne, env, scratch, Operand(at));
+ } else {
+ Label heap_number, convert;
+ __ Branch(&heap_number, eq, scratch, Operand(at));
+
+ // Convert undefined (and hole) to NaN.
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE) {
+ __ Branch(&convert, eq, input_reg, Operand(at));
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ }
+ DeoptimizeIf(ne, env, input_reg, Operand(at));
+
+ __ bind(&convert);
+ __ 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);
+ } else {
+ __ SmiUntag(scratch, input_reg);
+ ASSERT(mode == NUMBER_CANDIDATE_IS_SMI);
+ }
+
+ // 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->value());
+ Register scratch1 = scratch0();
+ Register scratch2 = ToRegister(instr->temp());
+ DoubleRegister double_scratch = double_scratch0();
+ DoubleRegister double_scratch2 = ToDoubleRegister(instr->temp3());
+
+ 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->temp2());
+ FPURegister single_scratch = double_scratch.low();
+ 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,
+ input_reg,
+ double_scratch,
+ scratch1,
+ double_scratch2,
+ except_flag,
+ kCheckForInexactConversion);
+
+ // Deopt if the operation did not succeed.
+ DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg));
+
+ 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->value();
+ ASSERT(input->IsRegister());
+ ASSERT(input->Equals(instr->result()));
+
+ Register input_reg = ToRegister(input);
+
+ DeferredTaggedToI* deferred = new(zone()) 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->value();
+ ASSERT(input->IsRegister());
+ LOperand* result = instr->result();
+ ASSERT(result->IsDoubleRegister());
+
+ Register input_reg = ToRegister(input);
+ DoubleRegister result_reg = ToDoubleRegister(result);
+
+ NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED;
+ HValue* value = instr->hydrogen()->value();
+ if (value->type().IsSmi()) {
+ mode = NUMBER_CANDIDATE_IS_SMI;
+ } else if (value->IsLoadKeyed()) {
+ HLoadKeyed* load = HLoadKeyed::cast(value);
+ if (load->UsesMustHandleHole()) {
+ if (load->hole_mode() == ALLOW_RETURN_HOLE) {
+ mode = NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE;
+ }
+ }
+ }
+
+ EmitNumberUntagD(input_reg, result_reg,
+ instr->hydrogen()->allow_undefined_as_nan(),
+ instr->hydrogen()->deoptimize_on_minus_zero(),
+ instr->environment(),
+ mode);
+}
+
+
+void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
+ Register result_reg = ToRegister(instr->result());
+ Register scratch1 = scratch0();
+ Register scratch2 = ToRegister(instr->temp());
+ DoubleRegister double_input = ToDoubleRegister(instr->value());
+
+ if (instr->truncating()) {
+ Register scratch3 = ToRegister(instr->temp2());
+ FPURegister single_scratch = double_scratch0().low();
+ __ EmitECMATruncate(result_reg,
+ double_input,
+ single_scratch,
+ scratch1,
+ scratch2,
+ scratch3);
+ } else {
+ Register except_flag = scratch2;
+
+ __ EmitFPUTruncate(kRoundToMinusInf,
+ result_reg,
+ double_input,
+ scratch1,
+ double_scratch0(),
+ except_flag,
+ kCheckForInexactConversion);
+
+ // Deopt if the operation did not succeed (except_flag != 0).
+ DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg));
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label done;
+ __ Branch(&done, ne, result_reg, Operand(zero_reg));
+ __ mfc1(scratch1, double_input.high());
+ __ And(scratch1, scratch1, Operand(HeapNumber::kSignMask));
+ DeoptimizeIf(ne, instr->environment(), scratch1, Operand(zero_reg));
+ __ bind(&done);
+ }
+ }
+}
+
+
+void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) {
+ Register result_reg = ToRegister(instr->result());
+ Register scratch1 = scratch0();
+ Register scratch2 = ToRegister(instr->temp());
+ DoubleRegister double_input = ToDoubleRegister(instr->value());
+
+ if (instr->truncating()) {
+ Register scratch3 = ToRegister(instr->temp2());
+ FPURegister single_scratch = double_scratch0().low();
+ __ EmitECMATruncate(result_reg,
+ double_input,
+ single_scratch,
+ scratch1,
+ scratch2,
+ scratch3);
+ } else {
+ Register except_flag = scratch2;
+
+ __ EmitFPUTruncate(kRoundToMinusInf,
+ result_reg,
+ double_input,
+ scratch1,
+ double_scratch0(),
+ except_flag,
+ kCheckForInexactConversion);
+
+ // Deopt if the operation did not succeed (except_flag != 0).
+ DeoptimizeIf(ne, instr->environment(), except_flag, Operand(zero_reg));
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label done;
+ __ Branch(&done, ne, result_reg, Operand(zero_reg));
+ __ mfc1(scratch1, double_input.high());
+ __ And(scratch1, scratch1, Operand(HeapNumber::kSignMask));
+ DeoptimizeIf(ne, instr->environment(), scratch1, Operand(zero_reg));
+ __ bind(&done);
+ }
+ }
+ __ SmiTagCheckOverflow(result_reg, result_reg, scratch1);
+ DeoptimizeIf(lt, instr->environment(), scratch1, Operand(zero_reg));
+}
+
+
+void LCodeGen::DoCheckSmi(LCheckSmi* instr) {
+ LOperand* input = instr->value();
+ __ And(at, ToRegister(input), Operand(kSmiTagMask));
+ DeoptimizeIf(ne, instr->environment(), at, Operand(zero_reg));
+}
+
+
+void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ LOperand* input = instr->value();
+ __ And(at, ToRegister(input), Operand(kSmiTagMask));
+ DeoptimizeIf(eq, instr->environment(), at, Operand(zero_reg));
+ }
+}
+
+
+void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
+ Register input = ToRegister(instr->value());
+ 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();
+ AllowDeferredHandleDereference smi_check;
+ if (isolate()->heap()->InNewSpace(*target)) {
+ Register reg = ToRegister(instr->value());
+ Handle<Cell> cell = isolate()->factory()->NewCell(target);
+ __ li(at, Operand(Handle<Object>(cell)));
+ __ lw(at, FieldMemOperand(at, Cell::kValueOffset));
+ DeoptimizeIf(ne, instr->environment(), reg,
+ Operand(at));
+ } else {
+ DeoptimizeIf(ne, instr->environment(), reg,
+ Operand(target));
+ }
+}
+
+
+void LCodeGen::DoDeferredInstanceMigration(LCheckMaps* instr, Register object) {
+ {
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+ __ push(object);
+ CallRuntimeFromDeferred(Runtime::kMigrateInstance, 1, instr);
+ __ StoreToSafepointRegisterSlot(v0, scratch0());
+ }
+ __ And(at, scratch0(), Operand(kSmiTagMask));
+ DeoptimizeIf(eq, instr->environment(), at, Operand(zero_reg));
+}
+
+
+void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
+ class DeferredCheckMaps: public LDeferredCode {
+ public:
+ DeferredCheckMaps(LCodeGen* codegen, LCheckMaps* instr, Register object)
+ : LDeferredCode(codegen), instr_(instr), object_(object) {
+ SetExit(check_maps());
+ }
+ virtual void Generate() {
+ codegen()->DoDeferredInstanceMigration(instr_, object_);
+ }
+ Label* check_maps() { return &check_maps_; }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LCheckMaps* instr_;
+ Label check_maps_;
+ Register object_;
+ };
+
+ if (instr->hydrogen()->CanOmitMapChecks()) return;
+ Register map_reg = scratch0();
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ Register reg = ToRegister(input);
+ SmallMapList* map_set = instr->hydrogen()->map_set();
+ __ lw(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
+
+ DeferredCheckMaps* deferred = NULL;
+ if (instr->hydrogen()->has_migration_target()) {
+ deferred = new(zone()) DeferredCheckMaps(this, instr, reg);
+ __ bind(deferred->check_maps());
+ }
+
+ Label success;
+ for (int i = 0; i < map_set->length() - 1; i++) {
+ Handle<Map> map = map_set->at(i);
+ __ CompareMapAndBranch(map_reg, map, &success, eq, &success);
+ }
+ Handle<Map> map = map_set->last();
+ // Do the CompareMap() directly within the Branch() and DeoptimizeIf().
+ if (instr->hydrogen()->has_migration_target()) {
+ __ Branch(deferred->entry(), ne, map_reg, Operand(map));
+ } else {
+ DeoptimizeIf(ne, instr->environment(), map_reg, Operand(map));
+ }
+
+ __ bind(&success);
+}
+
+
+void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
+ DoubleRegister value_reg = ToDoubleRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ DoubleRegister temp_reg = ToDoubleRegister(instr->temp());
+ __ 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->temp());
+ 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::DoAllocate(LAllocate* instr) {
+ class DeferredAllocate: public LDeferredCode {
+ public:
+ DeferredAllocate(LCodeGen* codegen, LAllocate* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredAllocate(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LAllocate* instr_;
+ };
+
+ DeferredAllocate* deferred =
+ new(zone()) DeferredAllocate(this, instr);
+
+ Register result = ToRegister(instr->result());
+ Register scratch = ToRegister(instr->temp1());
+ Register scratch2 = ToRegister(instr->temp2());
+
+ // Allocate memory for the object.
+ AllocationFlags flags = TAG_OBJECT;
+ if (instr->hydrogen()->MustAllocateDoubleAligned()) {
+ flags = static_cast<AllocationFlags>(flags | DOUBLE_ALIGNMENT);
+ }
+ if (instr->hydrogen()->IsOldPointerSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation());
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ flags = static_cast<AllocationFlags>(flags | PRETENURE_OLD_POINTER_SPACE);
+ } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ flags = static_cast<AllocationFlags>(flags | PRETENURE_OLD_DATA_SPACE);
+ }
+ if (instr->size()->IsConstantOperand()) {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ Allocate(size, result, scratch, scratch2, deferred->entry(), flags);
+ } else {
+ Register size = ToRegister(instr->size());
+ __ Allocate(size,
+ result,
+ scratch,
+ scratch2,
+ deferred->entry(),
+ flags);
+ }
+
+ __ bind(deferred->exit());
+
+ if (instr->hydrogen()->MustPrefillWithFiller()) {
+ if (instr->size()->IsConstantOperand()) {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ li(scratch, Operand(size));
+ } else {
+ scratch = ToRegister(instr->size());
+ }
+ __ Subu(scratch, scratch, Operand(kPointerSize));
+ __ Subu(result, result, Operand(kHeapObjectTag));
+ Label loop;
+ __ bind(&loop);
+ __ li(scratch2, Operand(isolate()->factory()->one_pointer_filler_map()));
+ __ Addu(at, result, Operand(scratch));
+ __ sw(scratch2, MemOperand(at));
+ __ Subu(scratch, scratch, Operand(kPointerSize));
+ __ Branch(&loop, ge, scratch, Operand(zero_reg));
+ __ Addu(result, result, Operand(kHeapObjectTag));
+ }
+}
+
+
+void LCodeGen::DoDeferredAllocate(LAllocate* instr) {
+ 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);
+ if (instr->size()->IsRegister()) {
+ Register size = ToRegister(instr->size());
+ ASSERT(!size.is(result));
+ __ SmiTag(size);
+ __ push(size);
+ } else {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ Push(Smi::FromInt(size));
+ }
+
+ if (instr->hydrogen()->IsOldPointerSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation());
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ CallRuntimeFromDeferred(Runtime::kAllocateInOldPointerSpace, 1, instr);
+ } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ CallRuntimeFromDeferred(Runtime::kAllocateInOldDataSpace, 1, instr);
+ } else {
+ CallRuntimeFromDeferred(Runtime::kAllocateInNewSpace, 1, instr);
+ }
+ __ StoreToSafepointRegisterSlot(v0, result);
+}
+
+
+void LCodeGen::DoToFastProperties(LToFastProperties* instr) {
+ ASSERT(ToRegister(instr->value()).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:
+ // t3 = literals array.
+ // a1 = regexp literal.
+ // a0 = regexp literal clone.
+ // a2 and t0-t2 are used as temporaries.
+ int literal_offset =
+ FixedArray::OffsetOfElementAt(instr->hydrogen()->literal_index());
+ __ LoadHeapObject(t3, instr->hydrogen()->literals());
+ __ 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;
+
+ __ Allocate(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.
+ bool pretenure = instr->hydrogen()->pretenure();
+ if (!pretenure && instr->hydrogen()->has_no_literals()) {
+ FastNewClosureStub stub(instr->hydrogen()->language_mode(),
+ instr->hydrogen()->is_generator());
+ __ li(a1, Operand(instr->hydrogen()->shared_info()));
+ __ push(a1);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ } else {
+ __ li(a2, Operand(instr->hydrogen()->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->value());
+ __ push(input);
+ CallRuntime(Runtime::kTypeof, 1, instr);
+}
+
+
+void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+
+ Register cmp1 = no_reg;
+ Operand cmp2 = Operand(no_reg);
+
+ Condition final_branch_condition = EmitTypeofIs(instr->TrueLabel(chunk_),
+ instr->FalseLabel(chunk_),
+ input,
+ instr->type_literal(),
+ cmp1,
+ cmp2);
+
+ ASSERT(cmp1.is_valid());
+ ASSERT(!cmp2.is_reg() || cmp2.rm().is_valid());
+
+ if (final_branch_condition != kNoCondition) {
+ EmitBranch(instr, 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_string())) {
+ __ 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_string())) {
+ __ 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()->symbol_string())) {
+ __ JumpIfSmi(input, false_label);
+ __ GetObjectType(input, input, scratch);
+ cmp1 = scratch;
+ cmp2 = Operand(SYMBOL_TYPE);
+ final_branch_condition = eq;
+
+ } else if (type_name->Equals(heap()->boolean_string())) {
+ __ 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_string())) {
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ cmp1 = at;
+ cmp2 = Operand(input);
+ final_branch_condition = eq;
+
+ } else if (type_name->Equals(heap()->undefined_string())) {
+ __ 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_string())) {
+ 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_string())) {
+ __ JumpIfSmi(input, false_label);
+ if (!FLAG_harmony_typeof) {
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ __ Branch(USE_DELAY_SLOT, true_label, eq, at, Operand(input));
+ }
+ Register map = input;
+ __ GetObjectType(input, map, scratch);
+ __ Branch(false_label,
+ lt, scratch, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ __ Branch(USE_DELAY_SLOT, false_label,
+ gt, scratch, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ // map is still valid, so the BitField can be loaded in delay slot.
+ // Check for undetectable objects => false.
+ __ lbu(at, FieldMemOperand(map, 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->temp());
+
+ EmitIsConstructCall(temp1, scratch0());
+
+ EmitBranch(instr, 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() {
+ if (info()->IsStub()) return;
+ // 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;
+ }
+ }
+}
+
+
+void LCodeGen::DoLazyBailout(LLazyBailout* instr) {
+ EnsureSpaceForLazyDeopt();
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ ASSERT(instr->HasEnvironment());
+ LEnvironment* env = instr->environment();
+ RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt);
+ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
+}
+
+
+void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
+ Deoptimizer::BailoutType type = instr->hydrogen()->type();
+ // TODO(danno): Stubs expect all deopts to be lazy for historical reasons (the
+ // needed return address), even though the implementation of LAZY and EAGER is
+ // now identical. When LAZY is eventually completely folded into EAGER, remove
+ // the special case below.
+ if (info()->IsStub() && type == Deoptimizer::EAGER) {
+ type = Deoptimizer::LAZY;
+ }
+
+ Comment(";;; deoptimize: %s", instr->hydrogen()->reason());
+ DeoptimizeIf(al, instr->environment(), type, zero_reg, Operand(zero_reg));
+}
+
+
+void LCodeGen::DoDummyUse(LDummyUse* instr) {
+ // Nothing to see here, move on!
+}
+
+
+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(isolate()), RelocInfo::CODE_TARGET, instr);
+ EnsureSpaceForLazyDeopt();
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ __ 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(zone()) DeferredStackCheck(this, instr);
+ __ LoadRoot(at, Heap::kStackLimitRootIndex);
+ __ Branch(deferred_stack_check->entry(), lo, sp, Operand(at));
+ EnsureSpaceForLazyDeopt();
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ __ 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();
+
+ // 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);
+
+ // Normally we record the first unknown OSR value as the entrypoint to the OSR
+ // code, but if there were none, record the entrypoint here.
+ if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset();
+}
+
+
+void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
+ Register result = ToRegister(instr->result());
+ Register object = ToRegister(instr->object());
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ DeoptimizeIf(eq, instr->environment(), object, Operand(at));
+
+ Register null_value = t1;
+ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
+ DeoptimizeIf(eq, instr->environment(), object, Operand(null_value));
+
+ __ And(at, object, kSmiTagMask);
+ DeoptimizeIf(eq, instr->environment(), at, Operand(zero_reg));
+
+ STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
+ __ GetObjectType(object, a1, a1);
+ DeoptimizeIf(le, instr->environment(), a1, Operand(LAST_JS_PROXY_TYPE));
+
+ Label use_cache, call_runtime;
+ ASSERT(object.is(a0));
+ __ CheckEnumCache(null_value, &call_runtime);
+
+ __ lw(result, FieldMemOperand(object, HeapObject::kMapOffset));
+ __ Branch(&use_cache);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(object);
+ CallRuntime(Runtime::kGetPropertyNamesFast, 1, instr);
+
+ __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
+ ASSERT(result.is(v0));
+ __ LoadRoot(at, Heap::kMetaMapRootIndex);
+ DeoptimizeIf(ne, instr->environment(), a1, Operand(at));
+ __ bind(&use_cache);
+}
+
+
+void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
+ Register map = ToRegister(instr->map());
+ Register result = ToRegister(instr->result());
+ Label load_cache, done;
+ __ EnumLength(result, map);
+ __ Branch(&load_cache, ne, result, Operand(Smi::FromInt(0)));
+ __ li(result, Operand(isolate()->factory()->empty_fixed_array()));
+ __ jmp(&done);
+
+ __ bind(&load_cache);
+ __ LoadInstanceDescriptors(map, result);
+ __ lw(result,
+ FieldMemOperand(result, DescriptorArray::kEnumCacheOffset));
+ __ lw(result,
+ FieldMemOperand(result, FixedArray::SizeFor(instr->idx())));
+ DeoptimizeIf(eq, instr->environment(), result, Operand(zero_reg));
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoCheckMapValue(LCheckMapValue* instr) {
+ Register object = ToRegister(instr->value());
+ Register map = ToRegister(instr->map());
+ __ lw(scratch0(), FieldMemOperand(object, HeapObject::kMapOffset));
+ DeoptimizeIf(ne, instr->environment(), map, Operand(scratch0()));
+}
+
+
+void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
+ Register object = ToRegister(instr->object());
+ Register index = ToRegister(instr->index());
+ Register result = ToRegister(instr->result());
+ Register scratch = scratch0();
+
+ Label out_of_object, done;
+ __ Branch(USE_DELAY_SLOT, &out_of_object, lt, index, Operand(zero_reg));
+ __ sll(scratch, index, kPointerSizeLog2 - kSmiTagSize); // In delay slot.
+
+ STATIC_ASSERT(kPointerSizeLog2 > kSmiTagSize);
+ __ Addu(scratch, object, scratch);
+ __ lw(result, FieldMemOperand(scratch, JSObject::kHeaderSize));
+
+ __ Branch(&done);
+
+ __ bind(&out_of_object);
+ __ lw(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
+ // Index is equal to negated out of object property index plus 1.
+ __ Subu(scratch, result, scratch);
+ __ lw(result, FieldMemOperand(scratch,
+ FixedArray::kHeaderSize - kPointerSize));
+ __ bind(&done);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/mips/lithium-codegen-mips.h b/chromium/v8/src/mips/lithium-codegen-mips.h
new file mode 100644
index 00000000000..b97a3cdbaf1
--- /dev/null
+++ b/chromium/v8/src/mips/lithium-codegen-mips.h
@@ -0,0 +1,519 @@
+// Copyright 2012 the V8 project authors. 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_CODEGEN_MIPS_H_
+#define V8_MIPS_LITHIUM_CODEGEN_MIPS_H_
+
+#include "deoptimizer.h"
+#include "mips/lithium-gap-resolver-mips.h"
+#include "mips/lithium-mips.h"
+#include "safepoint-table.h"
+#include "scopes.h"
+#include "v8utils.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class LDeferredCode;
+class SafepointGenerator;
+
+class LCodeGen BASE_EMBEDDED {
+ public:
+ LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info)
+ : zone_(info->zone()),
+ chunk_(static_cast<LPlatformChunk*>(chunk)),
+ masm_(assembler),
+ info_(info),
+ current_block_(-1),
+ current_instruction_(-1),
+ instructions_(chunk->instructions()),
+ deoptimizations_(4, info->zone()),
+ deopt_jump_table_(4, info->zone()),
+ deoptimization_literals_(8, info->zone()),
+ inlined_function_count_(0),
+ scope_(info->scope()),
+ status_(UNUSED),
+ translations_(info->zone()),
+ deferred_(8, info->zone()),
+ osr_pc_offset_(-1),
+ last_lazy_deopt_pc_(0),
+ frame_is_built_(false),
+ safepoints_(info->zone()),
+ resolver_(this),
+ expected_safepoint_kind_(Safepoint::kSimple),
+ old_position_(RelocInfo::kNoPosition) {
+ 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(); }
+ Zone* zone() const { return zone_; }
+
+ int LookupDestination(int block_id) const {
+ return chunk()->LookupDestination(block_id);
+ }
+
+ bool IsNextEmittedBlock(int block_id) const {
+ return LookupDestination(block_id) == GetNextEmittedBlock();
+ }
+
+ bool NeedsEagerFrame() const {
+ return GetStackSlotCount() > 0 ||
+ info()->is_non_deferred_calling() ||
+ !info()->IsStub() ||
+ info()->requires_frame();
+ }
+ bool NeedsDeferredFrame() const {
+ return !NeedsEagerFrame() && info()->is_deferred_calling();
+ }
+
+ RAStatus GetRAState() const {
+ return frame_is_built_ ? kRAHasBeenSaved : kRAHasNotBeenSaved;
+ }
+
+ // 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);
+ int32_t ToRepresentation(LConstantOperand* op, const Representation& r) const;
+ int32_t ToInteger32(LConstantOperand* op) const;
+ Smi* ToSmi(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;
+ bool IsSmi(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();
+
+ // Finish the code by setting stack height, safepoint, and bailout
+ // information on it.
+ void FinishCode(Handle<Code> code);
+
+ void DoDeferredNumberTagD(LNumberTagD* instr);
+
+ enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 };
+ void DoDeferredNumberTagI(LInstruction* instr,
+ LOperand* value,
+ IntegerSignedness signedness);
+
+ void DoDeferredTaggedToI(LTaggedToI* instr);
+ void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
+ void DoDeferredStackCheck(LStackCheck* instr);
+ void DoDeferredRandom(LRandom* instr);
+ void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+ void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
+ void DoDeferredAllocate(LAllocate* instr);
+ void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check);
+
+ void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
+
+ // Parallel move support.
+ void DoParallelMove(LParallelMove* move);
+ void DoGap(LGap* instr);
+
+ MemOperand PrepareKeyedOperand(Register key,
+ Register base,
+ bool key_is_constant,
+ int constant_key,
+ int element_size,
+ int shift_size,
+ int additional_index,
+ int additional_offset);
+
+ // 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;
+ }
+
+ LPlatformChunk* 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() const;
+ 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(); }
+
+ void Abort(BailoutReason reason);
+ void FPRINTF_CHECKING Comment(const char* format, ...);
+
+ void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code, zone()); }
+
+ // 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);
+
+ enum A1State {
+ A1_UNINITIALIZED,
+ A1_CONTAINS_TARGET
+ };
+
+ // Generate a direct call to a known function. Expects the function
+ // to be in a1.
+ void CallKnownFunction(Handle<JSFunction> function,
+ int formal_parameter_count,
+ int arity,
+ LInstruction* instr,
+ CallKind call_kind,
+ A1State a1_state);
+
+ 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,
+ Deoptimizer::BailoutType bailout_type,
+ Register src1 = zero_reg,
+ const Operand& src2 = Operand(zero_reg));
+ void DeoptimizeIf(Condition cc,
+ LEnvironment* environment,
+ Register src1 = zero_reg,
+ const Operand& src2 = Operand(zero_reg));
+ void ApplyCheckIf(Condition cc,
+ LBoundsCheck* check,
+ Register src1 = zero_reg,
+ const Operand& src2 = Operand(zero_reg));
+
+ void AddToTranslation(LEnvironment* environment,
+ Translation* translation,
+ LOperand* op,
+ bool is_tagged,
+ bool is_uint32,
+ int* object_index_pointer,
+ int* dematerialized_index_pointer);
+ void RegisterDependentCodeForEmbeddedMaps(Handle<Code> code);
+ void PopulateDeoptimizationData(Handle<Code> code);
+ int DefineDeoptimizationLiteral(Handle<Object> literal);
+
+ void PopulateDeoptimizationLiteralsWithInlinedFunctions();
+
+ Register ToRegister(int index) const;
+ DoubleRegister ToDoubleRegister(int index) const;
+
+ void EmitIntegerMathAbs(LMathAbs* 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);
+ void RecordAndUpdatePosition(int position);
+
+ static Condition TokenToCondition(Token::Value op, bool is_unsigned);
+ void EmitGoto(int block);
+ template<class InstrType>
+ void EmitBranch(InstrType instr,
+ Condition cc,
+ Register src1,
+ const Operand& src2);
+ template<class InstrType>
+ void EmitBranchF(InstrType instr,
+ Condition cc,
+ FPURegister src1,
+ FPURegister src2);
+ void EmitCmpI(LOperand* left, LOperand* right);
+ void EmitNumberUntagD(Register input,
+ DoubleRegister result,
+ bool allow_undefined_as_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode);
+
+ // 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,
+ SmiCheck check_needed);
+
+ // Emits optimized code for %_IsConstructCall().
+ // Caller should branch on equal condition.
+ void EmitIsConstructCall(Register temp1, Register temp2);
+
+ // 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,
+ AllocationSiteMode mode);
+ // Emit optimized code for integer division.
+ // Inputs are signed.
+ // All registers are clobbered.
+ // If 'remainder' is no_reg, it is not computed.
+ void EmitSignedIntegerDivisionByConstant(Register result,
+ Register dividend,
+ int32_t divisor,
+ Register remainder,
+ Register scratch,
+ LEnvironment* environment);
+
+
+ void EnsureSpaceForLazyDeopt();
+ void DoLoadKeyedExternalArray(LLoadKeyed* instr);
+ void DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr);
+ void DoLoadKeyedFixedArray(LLoadKeyed* instr);
+ void DoStoreKeyedExternalArray(LStoreKeyed* instr);
+ void DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr);
+ void DoStoreKeyedFixedArray(LStoreKeyed* instr);
+
+ Zone* zone_;
+ LPlatformChunk* const chunk_;
+ MacroAssembler* const masm_;
+ CompilationInfo* const info_;
+
+ int current_block_;
+ int current_instruction_;
+ const ZoneList<LInstruction*>* instructions_;
+ ZoneList<LEnvironment*> deoptimizations_;
+ ZoneList<Deoptimizer::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_;
+ bool frame_is_built_;
+
+ // 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_;
+
+ int old_position_;
+
+ class PushSafepointRegistersScope BASE_EMBEDDED {
+ public:
+ PushSafepointRegistersScope(LCodeGen* codegen,
+ Safepoint::Kind kind)
+ : codegen_(codegen) {
+ ASSERT(codegen_->info()->is_calling());
+ 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
+
+#endif // V8_MIPS_LITHIUM_CODEGEN_MIPS_H_
diff --git a/chromium/v8/src/mips/lithium-gap-resolver-mips.cc b/chromium/v8/src/mips/lithium-gap-resolver-mips.cc
new file mode 100644
index 00000000000..460e13bf0a9
--- /dev/null
+++ b/chromium/v8/src/mips/lithium-gap-resolver-mips.cc
@@ -0,0 +1,323 @@
+// Copyright 2012 the V8 project authors. 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 {
+
+LGapResolver::LGapResolver(LCodeGen* owner)
+ : cgen_(owner),
+ moves_(32, owner->zone()),
+ 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, cgen_->zone());
+ }
+ 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(kLithiumScratchReg, cgen_->ToRegister(source));
+ } else if (source->IsStackSlot()) {
+ __ lw(kLithiumScratchReg, 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 kLithiumScratchReg or kLithiumScratchDouble.
+ if (saved_destination_->IsRegister()) {
+ __ mov(cgen_->ToRegister(saved_destination_), kLithiumScratchReg);
+ } else if (saved_destination_->IsStackSlot()) {
+ __ sw(kLithiumScratchReg, 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(kLithiumScratchReg, source_operand);
+ __ sw(kLithiumScratchReg, destination_operand);
+ }
+ }
+
+ } else if (source->IsConstantOperand()) {
+ LConstantOperand* constant_source = LConstantOperand::cast(source);
+ if (destination->IsRegister()) {
+ Register dst = cgen_->ToRegister(destination);
+ Representation r = cgen_->IsSmi(constant_source)
+ ? Representation::Smi() : Representation::Integer32();
+ if (cgen_->IsInteger32(constant_source)) {
+ __ li(dst, Operand(cgen_->ToRepresentation(constant_source, r)));
+ } else {
+ __ LoadObject(dst, cgen_->ToHandle(constant_source));
+ }
+ } else if (destination->IsDoubleRegister()) {
+ DoubleRegister result = cgen_->ToDoubleRegister(destination);
+ double v = cgen_->ToDouble(constant_source);
+ __ Move(result, v);
+ } else {
+ ASSERT(destination->IsStackSlot());
+ ASSERT(!in_cycle_); // Constant moves happen after all cycles are gone.
+ Representation r = cgen_->IsSmi(constant_source)
+ ? Representation::Smi() : Representation::Integer32();
+ if (cgen_->IsInteger32(constant_source)) {
+ __ li(kLithiumScratchReg,
+ Operand(cgen_->ToRepresentation(constant_source, r)));
+ } else {
+ __ LoadObject(kLithiumScratchReg,
+ cgen_->ToHandle(constant_source));
+ }
+ __ sw(kLithiumScratchReg, 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 kLithiumScratchReg is free.
+ MemOperand source_high_operand =
+ cgen_->ToHighMemOperand(source);
+ MemOperand destination_high_operand =
+ cgen_->ToHighMemOperand(destination);
+ __ lw(kLithiumScratchReg, source_operand);
+ __ sw(kLithiumScratchReg, destination_operand);
+ __ lw(kLithiumScratchReg, source_high_operand);
+ __ sw(kLithiumScratchReg, destination_high_operand);
+ } else {
+ __ ldc1(kLithiumScratchDouble, source_operand);
+ __ sdc1(kLithiumScratchDouble, destination_operand);
+ }
+ }
+ } else {
+ UNREACHABLE();
+ }
+
+ moves_[index].Eliminate();
+}
+
+
+#undef __
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/mips/lithium-gap-resolver-mips.h b/chromium/v8/src/mips/lithium-gap-resolver-mips.h
new file mode 100644
index 00000000000..2506e38c351
--- /dev/null
+++ b/chromium/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/chromium/v8/src/mips/lithium-mips.cc b/chromium/v8/src/mips/lithium-mips.cc
new file mode 100644
index 00000000000..23f48a7709d
--- /dev/null
+++ b/chromium/v8/src/mips/lithium-mips.cc
@@ -0,0 +1,2522 @@
+// Copyright 2012 the V8 project authors. 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
+
+#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 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);
+ }
+}
+
+
+void LInstruction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ for (int i = 0; i < InputCount(); i++) {
+ if (i > 0) stream->Add(" ");
+ if (InputAt(i) == NULL) {
+ stream->Add("NULL");
+ } else {
+ InputAt(i)->PrintTo(stream);
+ }
+ }
+}
+
+
+void LInstruction::PrintOutputOperandTo(StringStream* stream) {
+ if (HasResult()) result()->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::ROR: return "ror-t";
+ case Token::SHL: return "sll-t";
+ case Token::SAR: return "sra-t";
+ case Token::SHR: return "srl-t";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+bool LGoto::HasInterestingComment(LCodeGen* gen) const {
+ return !gen->IsNextEmittedBlock(block_id());
+}
+
+
+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());
+ value()->PrintTo(stream);
+}
+
+
+LInstruction* LChunkBuilder::DoDebugBreak(HDebugBreak* instr) {
+ return new(zone()) LDebugBreak();
+}
+
+
+void LCompareNumericAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if ");
+ left()->PrintTo(stream);
+ stream->Add(" %s ", Token::String(op()));
+ right()->PrintTo(stream);
+ stream->Add(" then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_object(");
+ value()->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(");
+ value()->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(");
+ value()->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(");
+ value()->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(");
+ left()->PrintTo(stream);
+ right()->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(");
+ value()->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(");
+ value()->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(");
+ value()->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 ");
+ value()->PrintTo(stream);
+ stream->Add(" == \"%s\" then B%d else B%d",
+ *hydrogen()->type_literal()->ToCString(),
+ true_block_id(), false_block_id());
+}
+
+
+void LInnerAllocatedObject::PrintDataTo(StringStream* stream) {
+ stream->Add(" = ");
+ base_object()->PrintTo(stream);
+ stream->Add(" + %d", offset());
+}
+
+
+void LCallConstantFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("#%d / ", arity());
+}
+
+
+void LLoadContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintTo(stream);
+ stream->Add("[%d]", slot_index());
+}
+
+
+void LStoreContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintTo(stream);
+ stream->Add("[%d] <- ", slot_index());
+ value()->PrintTo(stream);
+}
+
+
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ function()->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("= ");
+ constructor()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
+void LCallNewArray::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ constructor()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+ ElementsKind kind = hydrogen()->elements_kind();
+ stream->Add(" (%s) ", ElementsKindToString(kind));
+}
+
+
+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);
+ hydrogen()->access().PrintTo(stream);
+ 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 LLoadKeyed::PrintDataTo(StringStream* stream) {
+ elements()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ if (hydrogen()->IsDehoisted()) {
+ stream->Add(" + %d]", additional_index());
+ } else {
+ stream->Add("]");
+ }
+}
+
+
+void LStoreKeyed::PrintDataTo(StringStream* stream) {
+ elements()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ if (hydrogen()->IsDehoisted()) {
+ stream->Add(" + %d] <-", additional_index());
+ } else {
+ stream->Add("] <- ");
+ }
+
+ if (value() == NULL) {
+ ASSERT(hydrogen()->IsConstantHoleStore() &&
+ hydrogen()->value()->representation().IsDouble());
+ stream->Add("<the hole(nan)>");
+ } else {
+ 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());
+}
+
+
+int LPlatformChunk::GetNextSpillIndex(bool is_double) {
+ // Skip a slot if for a double-width slot.
+ if (is_double) spill_slot_count_++;
+ return spill_slot_count_++;
+}
+
+
+LOperand* LPlatformChunk::GetNextSpillSlot(bool is_double) {
+ int index = GetNextSpillIndex(is_double);
+ if (is_double) {
+ return LDoubleStackSlot::Create(index, zone());
+ } else {
+ return LStackSlot::Create(index, zone());
+ }
+}
+
+
+LPlatformChunk* LChunkBuilder::Build() {
+ ASSERT(is_unused());
+ chunk_ = new(zone()) LPlatformChunk(info(), graph());
+ LPhase phase("L_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 LCodeGen::Abort(BailoutReason reason) {
+ info()->set_bailout_reason(reason);
+ status_ = ABORTED;
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(Register reg) {
+ return new(zone()) LUnallocated(LUnallocated::FIXED_REGISTER,
+ Register::ToAllocationIndex(reg));
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(DoubleRegister reg) {
+ return new(zone()) 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(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) {
+ return Use(value,
+ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER,
+ LUnallocated::USED_AT_START));
+}
+
+
+LOperand* LChunkBuilder::UseTempRegister(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::WRITABLE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::Use(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::NONE));
+}
+
+
+LOperand* LChunkBuilder::UseAtStart(HValue* value) {
+ return Use(value, new(zone()) 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::UseConstant(HValue* value) {
+ return chunk_->DefineConstantOperand(HConstant::cast(value));
+}
+
+
+LOperand* LChunkBuilder::UseAny(HValue* value) {
+ return value->IsConstant()
+ ? chunk_->DefineConstantOperand(HConstant::cast(value))
+ : Use(value, new(zone()) 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(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineAsSpilled(
+ LTemplateInstruction<1, I, T>* instr, int 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(zone()) 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;
+ ZoneList<HValue*> objects_to_materialize(0, zone());
+ instr->set_environment(CreateEnvironment(hydrogen_env,
+ &argument_index_accumulator,
+ &objects_to_materialize));
+ return instr;
+}
+
+
+LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
+ HInstruction* hinstr,
+ CanDeoptimize can_deoptimize) {
+ info()->MarkAsNonDeferredCalling();
+#ifdef DEBUG
+ instr->VerifyCall();
+#endif
+ instr->MarkAsCall();
+ instr = AssignPointerMap(instr);
+
+ if (hinstr->HasObservableSideEffects()) {
+ ASSERT(hinstr->next()->IsSimulate());
+ HSimulate* sim = HSimulate::cast(hinstr->next());
+ ASSERT(instruction_pending_deoptimization_environment_ == NULL);
+ ASSERT(pending_deoptimization_ast_id_.IsNone());
+ instruction_pending_deoptimization_environment_ = instr;
+ pending_deoptimization_ast_id_ = 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::AssignPointerMap(LInstruction* instr) {
+ ASSERT(!instr->HasPointerMap());
+ instr->set_pointer_map(new(zone()) LPointerMap(position_, zone()));
+ return instr;
+}
+
+
+LUnallocated* LChunkBuilder::TempRegister() {
+ LUnallocated* operand =
+ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER);
+ int vreg = allocator_->GetVirtualRegister();
+ if (!allocator_->AllocationOk()) {
+ Abort(kOutOfVirtualRegistersWhileTryingToAllocateTempRegister);
+ vreg = 0;
+ }
+ operand->set_virtual_register(vreg);
+ 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(zone()) LLabel(instr->block());
+}
+
+
+LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
+ return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value())));
+}
+
+
+LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
+ return AssignEnvironment(new(zone()) 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(zone()) LArithmeticT(op, left, right);
+ return MarkAsCall(DefineFixed(result, v0), instr);
+ }
+
+ ASSERT(instr->representation().IsSmiOrInteger32());
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->left());
+
+ HValue* right_value = instr->right();
+ LOperand* right = NULL;
+ int constant_value = 0;
+ bool does_deopt = false;
+ if (right_value->IsConstant()) {
+ HConstant* constant = HConstant::cast(right_value);
+ right = chunk_->DefineConstantOperand(constant);
+ constant_value = constant->Integer32Value() & 0x1f;
+ // Left shifts can deoptimize if we shift by > 0 and the result cannot be
+ // truncated to smi.
+ if (instr->representation().IsSmi() && constant_value > 0) {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToSmi)) {
+ does_deopt = true;
+ break;
+ }
+ }
+ }
+ } else {
+ right = UseRegisterAtStart(right_value);
+ }
+
+ // Shift operations can deoptimize if we do a logical shift
+ // by 0 and the result cannot be truncated to int32.
+ if (op == Token::SHR && constant_value == 0) {
+ if (FLAG_opt_safe_uint32_operations) {
+ does_deopt = !instr->CheckFlag(HInstruction::kUint32);
+ } else {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
+ does_deopt = true;
+ break;
+ }
+ }
+ }
+ }
+
+ LInstruction* result =
+ DefineAsRegister(new(zone()) 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(zone()) 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(zone()) 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);
+ if (phi->HasMergedIndex()) {
+ last_environment->SetValueAt(phi->merged_index(), phi);
+ }
+ }
+ for (int i = 0; i < block->deleted_phis()->length(); ++i) {
+ if (block->deleted_phis()->at(i) < last_environment->length()) {
+ 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 DEBUG
+ // Make sure that the lithium instruction has either no fixed register
+ // constraints in temps or the result OR no uses that are only used at
+ // start. If this invariant doesn't hold, the register allocator can decide
+ // to insert a split of a range immediately before the instruction due to an
+ // already allocated register needing to be used for the instruction's fixed
+ // register constraint. In this case, The register allocator won't see an
+ // interference between the split child and the use-at-start (it would if
+ // the it was just a plain use), so it is free to move the split child into
+ // the same register that is used for the use-at-start.
+ // See https://code.google.com/p/chromium/issues/detail?id=201590
+ if (!(instr->ClobbersRegisters() && instr->ClobbersDoubleRegisters())) {
+ int fixed = 0;
+ int used_at_start = 0;
+ for (UseIterator it(instr); !it.Done(); it.Advance()) {
+ LUnallocated* operand = LUnallocated::cast(it.Current());
+ if (operand->IsUsedAtStart()) ++used_at_start;
+ }
+ if (instr->Output() != NULL) {
+ if (LUnallocated::cast(instr->Output())->HasFixedPolicy()) ++fixed;
+ }
+ for (TempIterator it(instr); !it.Done(); it.Advance()) {
+ LUnallocated* operand = LUnallocated::cast(it.Current());
+ if (operand->HasFixedPolicy()) ++fixed;
+ }
+ ASSERT(fixed == 0 || used_at_start == 0);
+ }
+#endif
+
+ instr->set_position(position_);
+ 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,
+ ZoneList<HValue*>* objects_to_materialize) {
+ if (hydrogen_env == NULL) return NULL;
+
+ LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
+ argument_index_accumulator,
+ objects_to_materialize);
+ BailoutId ast_id = hydrogen_env->ast_id();
+ ASSERT(!ast_id.IsNone() ||
+ hydrogen_env->frame_type() != JS_FUNCTION);
+ int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
+ LEnvironment* result = new(zone()) LEnvironment(
+ hydrogen_env->closure(),
+ hydrogen_env->frame_type(),
+ ast_id,
+ hydrogen_env->parameter_count(),
+ argument_count_,
+ value_count,
+ outer,
+ hydrogen_env->entry(),
+ zone());
+ int argument_index = *argument_index_accumulator;
+ int object_index = objects_to_materialize->length();
+ for (int i = 0; i < hydrogen_env->length(); ++i) {
+ if (hydrogen_env->is_special_index(i)) continue;
+
+ LOperand* op;
+ HValue* value = hydrogen_env->values()->at(i);
+ if (value->IsArgumentsObject() || value->IsCapturedObject()) {
+ objects_to_materialize->Add(value, zone());
+ op = LEnvironment::materialization_marker();
+ } else if (value->IsPushArgument()) {
+ op = new(zone()) LArgument(argument_index++);
+ } else {
+ op = UseAny(value);
+ }
+ result->AddValue(op,
+ value->representation(),
+ value->CheckFlag(HInstruction::kUint32));
+ }
+
+ for (int i = object_index; i < objects_to_materialize->length(); ++i) {
+ HValue* object_to_materialize = objects_to_materialize->at(i);
+ int previously_materialized_object = -1;
+ for (int prev = 0; prev < i; ++prev) {
+ if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) {
+ previously_materialized_object = prev;
+ break;
+ }
+ }
+ int length = object_to_materialize->OperandCount();
+ bool is_arguments = object_to_materialize->IsArgumentsObject();
+ if (previously_materialized_object >= 0) {
+ result->AddDuplicateObject(previously_materialized_object);
+ continue;
+ } else {
+ result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
+ }
+ for (int i = is_arguments ? 1 : 0; i < length; ++i) {
+ LOperand* op;
+ HValue* value = object_to_materialize->OperandAt(i);
+ if (value->IsArgumentsObject() || value->IsCapturedObject()) {
+ objects_to_materialize->Add(value, zone());
+ op = LEnvironment::materialization_marker();
+ } else {
+ ASSERT(!value->IsPushArgument());
+ op = UseAny(value);
+ }
+ result->AddValue(op,
+ value->representation(),
+ value->CheckFlag(HInstruction::kUint32));
+ }
+ }
+
+ if (hydrogen_env->frame_type() == JS_FUNCTION) {
+ *argument_index_accumulator = argument_index;
+ }
+
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
+ return new(zone()) LGoto(instr->FirstSuccessor()->block_id());
+}
+
+
+LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
+ HValue* value = instr->value();
+ if (value->EmitAtUses()) {
+ HBasicBlock* successor = HConstant::cast(value)->BooleanValue()
+ ? instr->FirstSuccessor()
+ : instr->SecondSuccessor();
+ return new(zone()) LGoto(successor->block_id());
+ }
+
+ LBranch* result = new(zone()) LBranch(UseRegister(value));
+ // Tagged values that are not known smis or booleans require a
+ // deoptimization environment. If the instruction is generic no
+ // environment is needed since all cases are handled.
+ Representation rep = value->representation();
+ HType type = value->type();
+ ToBooleanStub::Types expected = instr->expected_input_types();
+ if (rep.IsTagged() && !type.IsSmi() && !type.IsBoolean() &&
+ !expected.IsGeneric()) {
+ 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(zone()) LCmpMapAndBranch(value, temp);
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* length) {
+ info()->MarkAsRequiresFrame();
+ return DefineAsRegister(
+ new(zone()) LArgumentsLength(UseRegister(length->value())));
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
+ info()->MarkAsRequiresFrame();
+ return DefineAsRegister(new(zone()) LArgumentsElements);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
+ LInstanceOf* result =
+ new(zone()) LInstanceOf(UseFixed(instr->left(), a0),
+ UseFixed(instr->right(), a1));
+ return MarkAsCall(DefineFixed(result, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
+ HInstanceOfKnownGlobal* instr) {
+ LInstanceOfKnownGlobal* result =
+ new(zone()) LInstanceOfKnownGlobal(UseFixed(instr->left(), a0),
+ FixedTemp(t0));
+ return MarkAsCall(DefineFixed(result, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceSize(HInstanceSize* instr) {
+ LOperand* object = UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new(zone()) LInstanceSize(object));
+}
+
+
+LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) {
+ LOperand* receiver = UseRegisterAtStart(instr->receiver());
+ LOperand* function = UseRegisterAtStart(instr->function());
+ LWrapReceiver* result = new(zone()) LWrapReceiver(receiver, function);
+ return AssignEnvironment(DefineSameAsFirst(result));
+}
+
+
+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(zone()) 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(zone()) LPushArgument(argument);
+}
+
+
+LInstruction* LChunkBuilder::DoInnerAllocatedObject(
+ HInnerAllocatedObject* inner_object) {
+ LOperand* base_object = UseRegisterAtStart(inner_object->base_object());
+ LInnerAllocatedObject* result =
+ new(zone()) LInnerAllocatedObject(base_object);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoThisFunction(HThisFunction* instr) {
+ return instr->HasNoUses()
+ ? NULL
+ : DefineAsRegister(new(zone()) LThisFunction);
+}
+
+
+LInstruction* LChunkBuilder::DoContext(HContext* instr) {
+ // If there is a non-return use, the context must be allocated in a register.
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->IsReturn()) {
+ return DefineAsRegister(new(zone()) LContext);
+ }
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LOuterContext(context));
+}
+
+
+LInstruction* LChunkBuilder::DoDeclareGlobals(HDeclareGlobals* instr) {
+ return MarkAsCall(new(zone()) LDeclareGlobals, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LGlobalObject(context));
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalReceiver(HGlobalReceiver* instr) {
+ LOperand* global_object = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LGlobalReceiver(global_object));
+}
+
+
+LInstruction* LChunkBuilder::DoCallConstantFunction(
+ HCallConstantFunction* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallConstantFunction, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), a1);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new(zone()) LInvokeFunction(function);
+ return MarkAsCall(DefineFixed(result, v0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
+ switch (instr->op()) {
+ case kMathFloor: return DoMathFloor(instr);
+ case kMathRound: return DoMathRound(instr);
+ case kMathAbs: return DoMathAbs(instr);
+ case kMathLog: return DoMathLog(instr);
+ case kMathSin: return DoMathSin(instr);
+ case kMathCos: return DoMathCos(instr);
+ case kMathTan: return DoMathTan(instr);
+ case kMathExp: return DoMathExp(instr);
+ case kMathSqrt: return DoMathSqrt(instr);
+ case kMathPowHalf: return DoMathPowHalf(instr);
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMathLog(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), f4);
+ LMathLog* result = new(zone()) LMathLog(input);
+ return MarkAsCall(DefineFixedDouble(result, f4), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathSin(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), f4);
+ LMathSin* result = new(zone()) LMathSin(input);
+ return MarkAsCall(DefineFixedDouble(result, f4), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathCos(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), f4);
+ LMathCos* result = new(zone()) LMathCos(input);
+ return MarkAsCall(DefineFixedDouble(result, f4), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathTan(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), f4);
+ LMathTan* result = new(zone()) LMathTan(input);
+ return MarkAsCall(DefineFixedDouble(result, f4), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathExp(HUnaryMathOperation* instr) {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->value()->representation().IsDouble());
+ LOperand* input = UseTempRegister(instr->value());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LOperand* double_temp = FixedTemp(f6); // Chosen by fair dice roll.
+ LMathExp* result = new(zone()) LMathExp(input, double_temp, temp1, temp2);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathPowHalf(HUnaryMathOperation* instr) {
+ // Input cannot be the same as the result, see LCodeGen::DoMathPowHalf.
+ LOperand* input = UseFixedDouble(instr->value(), f8);
+ LOperand* temp = FixedTemp(f6);
+ LMathPowHalf* result = new(zone()) LMathPowHalf(input, temp);
+ return DefineFixedDouble(result, f4);
+}
+
+
+LInstruction* LChunkBuilder::DoMathAbs(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegister(instr->value());
+ LMathAbs* result = new(zone()) LMathAbs(input);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoMathFloor(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegister(instr->value());
+ LOperand* temp = TempRegister();
+ LMathFloor* result = new(zone()) LMathFloor(input, temp);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoMathSqrt(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegister(instr->value());
+ LMathSqrt* result = new(zone()) LMathSqrt(input);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathRound(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegister(instr->value());
+ LOperand* temp = FixedTemp(f6);
+ LMathRound* result = new(zone()) LMathRound(input, temp);
+ return AssignEnvironment(DefineAsRegister(result));
+}
+
+
+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(zone()) LCallKeyed(key), v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallNamed, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallGlobal(HCallGlobal* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallGlobal, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallKnownGlobal(HCallKnownGlobal* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallKnownGlobal, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
+ LOperand* constructor = UseFixed(instr->constructor(), a1);
+ argument_count_ -= instr->argument_count();
+ LCallNew* result = new(zone()) LCallNew(constructor);
+ return MarkAsCall(DefineFixed(result, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) {
+ LOperand* constructor = UseFixed(instr->constructor(), a1);
+ argument_count_ -= instr->argument_count();
+ LCallNewArray* result = new(zone()) LCallNewArray(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(zone()) LCallFunction(function), v0),
+ instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallRuntime, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoRor(HRor* instr) {
+ return DoShift(Token::ROR, 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().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ LOperand* right = UseOrConstantAtStart(instr->BetterRightOperand());
+ return DefineAsRegister(new(zone()) 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(zone()) LArithmeticT(instr->op(), left, right);
+ return MarkAsCall(DefineFixed(result, v0), instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
+ if (instr->representation().IsDouble()) {
+ return DoArithmeticD(Token::DIV, instr);
+ } else if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* dividend = UseRegister(instr->left());
+ LOperand* divisor = UseRegister(instr->right());
+ LDivI* div = new(zone()) LDivI(dividend, divisor);
+ return AssignEnvironment(DefineAsRegister(div));
+ } else {
+ return DoArithmeticT(Token::DIV, instr);
+ }
+}
+
+
+bool LChunkBuilder::HasMagicNumberForDivisor(int32_t divisor) {
+ uint32_t divisor_abs = abs(divisor);
+ // Dividing by 0, 1, and powers of 2 is easy.
+ // Note that IsPowerOf2(0) returns true;
+ ASSERT(IsPowerOf2(0) == true);
+ if (IsPowerOf2(divisor_abs)) return true;
+
+ // We have magic numbers for a few specific divisors.
+ // Details and proofs can be found in:
+ // - Hacker's Delight, Henry S. Warren, Jr.
+ // - The PowerPC Compiler Writer's Guide
+ // and probably many others.
+ //
+ // We handle
+ // <divisor with magic numbers> * <power of 2>
+ // but not
+ // <divisor with magic numbers> * <other divisor with magic numbers>
+ int32_t power_of_2_factor =
+ CompilerIntrinsics::CountTrailingZeros(divisor_abs);
+ DivMagicNumbers magic_numbers =
+ DivMagicNumberFor(divisor_abs >> power_of_2_factor);
+ if (magic_numbers.M != InvalidDivMagicNumber.M) return true;
+
+ return false;
+}
+
+
+HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
+ // Only optimize when we have magic numbers for the divisor.
+ // The standard integer division routine is usually slower than transitionning
+ // to FPU.
+ if (divisor->IsConstant() &&
+ HConstant::cast(divisor)->HasInteger32Value()) {
+ HConstant* constant_val = HConstant::cast(divisor);
+ return constant_val->CopyToRepresentation(Representation::Integer32(),
+ divisor->block()->zone());
+ }
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
+ HValue* right = instr->right();
+ LOperand* dividend = UseRegister(instr->left());
+ LOperand* divisor = UseRegisterOrConstant(right);
+ LOperand* remainder = TempRegister();
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LMathFloorOfDiv(dividend, divisor, remainder)));
+}
+
+
+LInstruction* LChunkBuilder::DoMod(HMod* instr) {
+ HValue* left = instr->left();
+ HValue* right = instr->right();
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!right->CanBeZero());
+ LModI* mod = new(zone()) LModI(UseRegisterAtStart(left),
+ UseOrConstant(right));
+ LInstruction* result = DefineAsRegister(mod);
+ return (left->CanBeNegative() &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero))
+ ? AssignEnvironment(result)
+ : result;
+ } else if (instr->fixed_right_arg().has_value) {
+ LModI* mod = new(zone()) LModI(UseRegisterAtStart(left),
+ UseRegisterAtStart(right));
+ return AssignEnvironment(DefineAsRegister(mod));
+ } else {
+ LModI* mod = new(zone()) LModI(UseRegister(left),
+ UseRegister(right),
+ TempRegister(),
+ FixedTemp(f20),
+ FixedTemp(f22));
+ LInstruction* result = DefineAsRegister(mod);
+ return (right->CanBeZero() ||
+ (left->RangeCanInclude(kMinInt) &&
+ right->RangeCanInclude(-1)) ||
+ instr->CheckFlag(HValue::kBailoutOnMinusZero))
+ ? AssignEnvironment(result)
+ : result;
+ }
+ } 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.
+ LArithmeticD* mod = new(zone()) LArithmeticD(Token::MOD,
+ UseFixedDouble(left, f2),
+ UseFixedDouble(right, f4));
+ return MarkAsCall(DefineFixedDouble(mod, f2), instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMul(HMul* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left;
+ LOperand* right = UseOrConstant(instr->BetterRightOperand());
+ LOperand* temp = NULL;
+ if (instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
+ (instr->CheckFlag(HValue::kCanOverflow) ||
+ !right->IsConstantOperand())) {
+ left = UseRegister(instr->BetterLeftOperand());
+ temp = TempRegister();
+ } else {
+ left = UseRegisterAtStart(instr->BetterLeftOperand());
+ }
+ LMulI* mul = new(zone()) LMulI(left, right, temp);
+ if (instr->CheckFlag(HValue::kCanOverflow) ||
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ AssignEnvironment(mul);
+ }
+ return DefineAsRegister(mul);
+
+ } else if (instr->representation().IsDouble()) {
+ if (kArchVariant == kMips32r2) {
+ if (instr->UseCount() == 1 && instr->uses().value()->IsAdd()) {
+ HAdd* add = HAdd::cast(instr->uses().value());
+ if (instr == add->left()) {
+ // This mul is the lhs of an add. The add and mul will be folded
+ // into a multiply-add.
+ return NULL;
+ }
+ if (instr == add->right() && !add->left()->IsMul()) {
+ // This mul is the rhs of an add, where the lhs is not another mul.
+ // The add and mul will be folded into a multiply-add.
+ return NULL;
+ }
+ }
+ }
+ return DoArithmeticD(Token::MUL, instr);
+ } else {
+ return DoArithmeticT(Token::MUL, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoSub(HSub* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ LSubI* sub = new(zone()) 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::DoMultiplyAdd(HMul* mul, HValue* addend) {
+ LOperand* multiplier_op = UseRegisterAtStart(mul->left());
+ LOperand* multiplicand_op = UseRegisterAtStart(mul->right());
+ LOperand* addend_op = UseRegisterAtStart(addend);
+ return DefineSameAsFirst(new(zone()) LMultiplyAddD(addend_op, multiplier_op,
+ multiplicand_op));
+}
+
+
+LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ LOperand* right = UseOrConstantAtStart(instr->BetterRightOperand());
+ LAddI* add = new(zone()) LAddI(left, right);
+ LInstruction* result = DefineAsRegister(add);
+ if (instr->CheckFlag(HValue::kCanOverflow)) {
+ result = AssignEnvironment(result);
+ }
+ return result;
+ } else if (instr->representation().IsDouble()) {
+ if (kArchVariant == kMips32r2) {
+ if (instr->left()->IsMul())
+ return DoMultiplyAdd(HMul::cast(instr->left()), instr->right());
+
+ if (instr->right()->IsMul()) {
+ ASSERT(!instr->left()->IsMul());
+ return DoMultiplyAdd(HMul::cast(instr->right()), instr->left());
+ }
+ }
+ return DoArithmeticD(Token::ADD, instr);
+ } else {
+ ASSERT(instr->representation().IsTagged());
+ return DoArithmeticT(Token::ADD, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) {
+ LOperand* left = NULL;
+ LOperand* right = NULL;
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ left = UseRegisterAtStart(instr->BetterLeftOperand());
+ right = UseOrConstantAtStart(instr->BetterRightOperand());
+ } else {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->left()->representation().IsDouble());
+ ASSERT(instr->right()->representation().IsDouble());
+ left = UseRegisterAtStart(instr->left());
+ right = UseRegisterAtStart(instr->right());
+ }
+ return DefineAsRegister(new(zone()) LMathMinMax(left, right));
+}
+
+
+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(zone()) 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(zone()) LRandom(global_object);
+ return MarkAsCall(DefineFixedDouble(result, f0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
+ 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(zone()) LCmpT(left, right);
+ return MarkAsCall(DefineFixed(result, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareNumericAndBranch(
+ HCompareNumericAndBranch* instr) {
+ Representation r = instr->representation();
+ if (r.IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(r));
+ ASSERT(instr->right()->representation().Equals(r));
+ LOperand* left = UseRegisterOrConstantAtStart(instr->left());
+ LOperand* right = UseRegisterOrConstantAtStart(instr->right());
+ return new(zone()) LCompareNumericAndBranch(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(zone()) LCompareNumericAndBranch(left, right);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch(
+ HCompareObjectEqAndBranch* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterAtStart(instr->right());
+ return new(zone()) LCmpObjectEqAndBranch(left, right);
+}
+
+
+LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* temp = TempRegister();
+ return new(zone()) LIsObjectAndBranch(UseRegisterAtStart(instr->value()),
+ temp);
+}
+
+
+LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* temp = TempRegister();
+ return new(zone()) LIsStringAndBranch(UseRegisterAtStart(instr->value()),
+ temp);
+}
+
+
+LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LIsSmiAndBranch(Use(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
+ HIsUndetectableAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) 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(zone()) LStringCompareAndBranch(left, right);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
+ HHasInstanceTypeAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return new(zone()) LHasInstanceTypeAndBranch(value);
+}
+
+
+LInstruction* LChunkBuilder::DoGetCachedArrayIndex(
+ HGetCachedArrayIndex* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+
+ return DefineAsRegister(new(zone()) LGetCachedArrayIndex(value));
+}
+
+
+LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch(
+ HHasCachedArrayIndexAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LHasCachedArrayIndexAndBranch(
+ UseRegisterAtStart(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoClassOfTestAndBranch(
+ HClassOfTestAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LClassOfTestAndBranch(UseRegister(instr->value()),
+ TempRegister());
+}
+
+
+LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
+ LOperand* map = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LMapEnumLength(map));
+}
+
+
+LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
+ LOperand* object = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LElementsKind(object));
+}
+
+
+LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
+ LOperand* object = UseRegister(instr->value());
+ LValueOf* result = new(zone()) LValueOf(object, TempRegister());
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+ LOperand* object = UseFixed(instr->value(), a0);
+ LDateField* result =
+ new(zone()) LDateField(object, FixedTemp(a1), instr->index());
+ return MarkAsCall(DefineFixed(result, v0), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) {
+ LOperand* string = UseRegister(instr->string());
+ LOperand* index = UseRegister(instr->index());
+ LOperand* value = UseTempRegister(instr->value());
+ LSeqStringSetChar* result =
+ new(zone()) LSeqStringSetChar(instr->encoding(), string, index, value);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
+ LOperand* value = UseRegisterOrConstantAtStart(instr->index());
+ LOperand* length = UseRegister(instr->length());
+ return AssignEnvironment(new(zone()) LBoundsCheck(value, length));
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheckBaseIndexInformation(
+ HBoundsCheckBaseIndexInformation* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+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(zone()) 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.IsSmi()) {
+ if (to.IsTagged()) {
+ LOperand* value = UseRegister(instr->value());
+ return DefineSameAsFirst(new(zone()) LDummyUse(value));
+ }
+ from = Representation::Tagged();
+ }
+ if (from.IsTagged()) {
+ if (to.IsDouble()) {
+ info()->MarkAsDeferredCalling();
+ LOperand* value = UseRegister(instr->value());
+ LNumberUntagD* res = new(zone()) LNumberUntagD(value);
+ return AssignEnvironment(DefineAsRegister(res));
+ } else if (to.IsSmi()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ if (val->type().IsSmi()) {
+ return DefineSameAsFirst(new(zone()) LDummyUse(value));
+ }
+ return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value)));
+ } else {
+ ASSERT(to.IsInteger32());
+ LOperand* value = NULL;
+ LInstruction* res = NULL;
+ if (instr->value()->type().IsSmi()) {
+ value = UseRegisterAtStart(instr->value());
+ res = DefineAsRegister(new(zone()) LSmiUntag(value, false));
+ } else {
+ value = UseRegister(instr->value());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = instr->CanTruncateToInt32() ? TempRegister()
+ : NULL;
+ LOperand* temp3 = FixedTemp(f22);
+ res = DefineSameAsFirst(new(zone()) LTaggedToI(value,
+ temp1,
+ temp2,
+ temp3));
+ res = AssignEnvironment(res);
+ }
+ return res;
+ }
+ } else if (from.IsDouble()) {
+ if (to.IsTagged()) {
+ info()->MarkAsDeferredCalling();
+ 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(zone()) LNumberTagD(value, temp1, temp2);
+ Define(result, result_temp);
+ return AssignPointerMap(result);
+ } else if (to.IsSmi()) {
+ LOperand* value = UseRegister(instr->value());
+ return AssignEnvironment(DefineAsRegister(new(zone()) LDoubleToSmi(value,
+ TempRegister(), TempRegister())));
+ } else {
+ ASSERT(to.IsInteger32());
+ LOperand* value = UseRegister(instr->value());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = instr->CanTruncateToInt32() ? TempRegister() : NULL;
+ LDoubleToI* res = new(zone()) LDoubleToI(value, temp1, temp2);
+ return AssignEnvironment(DefineAsRegister(res));
+ }
+ } else if (from.IsInteger32()) {
+ info()->MarkAsDeferredCalling();
+ if (to.IsTagged()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegisterAtStart(val);
+ if (val->CheckFlag(HInstruction::kUint32)) {
+ LNumberTagU* result = new(zone()) LNumberTagU(value);
+ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
+ } else if (val->HasRange() && val->range()->IsInSmiRange()) {
+ return DefineAsRegister(new(zone()) LSmiTag(value));
+ } else {
+ LNumberTagI* result = new(zone()) LNumberTagI(value);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+ }
+ } else if (to.IsSmi()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ LInstruction* result =
+ DefineSameAsFirst(new(zone()) LInteger32ToSmi(value));
+ if (val->HasRange() && val->range()->IsInSmiRange()) {
+ return result;
+ }
+ return AssignEnvironment(result);
+ } else {
+ ASSERT(to.IsDouble());
+ if (instr->value()->CheckFlag(HInstruction::kUint32)) {
+ return DefineAsRegister(
+ new(zone()) LUint32ToDouble(UseRegister(instr->value())));
+ } else {
+ return DefineAsRegister(
+ new(zone()) LInteger32ToDouble(Use(instr->value())));
+ }
+ }
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckHeapObject(HCheckHeapObject* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckNonSmi(value));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckSmi(value));
+}
+
+
+LInstruction* LChunkBuilder::DoIsNumberAndBranch(HIsNumberAndBranch* instr) {
+ return new(zone())
+ LIsNumberAndBranch(UseRegisterOrConstantAtStart(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LInstruction* result = new(zone()) LCheckInstanceType(value);
+ return AssignEnvironment(result);
+}
+
+
+LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckFunction(value));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMaps(HCheckMaps* instr) {
+ LOperand* value = NULL;
+ if (!instr->CanOmitMapChecks()) {
+ value = UseRegisterAtStart(instr->value());
+ if (instr->has_migration_target()) info()->MarkAsDeferredCalling();
+ }
+ LCheckMaps* result = new(zone()) LCheckMaps(value);
+ if (!instr->CanOmitMapChecks()) {
+ AssignEnvironment(result);
+ if (instr->has_migration_target()) return AssignPointerMap(result);
+ }
+ return 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(zone()) LClampDToUint8(reg, FixedTemp(f22)));
+ } else if (input_rep.IsInteger32()) {
+ return DefineAsRegister(new(zone()) LClampIToUint8(reg));
+ } else {
+ ASSERT(input_rep.IsSmiOrTagged());
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve f22 explicitly.
+ LClampTToUint8* result = new(zone()) LClampTToUint8(reg, FixedTemp(f22));
+ return AssignEnvironment(DefineAsRegister(result));
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
+ LOperand* parameter_count = UseRegisterOrConstant(instr->parameter_count());
+ return new(zone()) LReturn(UseFixed(instr->value(), v0),
+ parameter_count);
+}
+
+
+LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
+ Representation r = instr->representation();
+ if (r.IsSmi()) {
+ return DefineAsRegister(new(zone()) LConstantS);
+ } else if (r.IsInteger32()) {
+ return DefineAsRegister(new(zone()) LConstantI);
+ } else if (r.IsDouble()) {
+ return DefineAsRegister(new(zone()) LConstantD);
+ } else if (r.IsExternal()) {
+ return DefineAsRegister(new(zone()) LConstantE);
+ } else if (r.IsTagged()) {
+ return DefineAsRegister(new(zone()) LConstantT);
+ } else {
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
+ LLoadGlobalCell* result = new(zone()) 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(zone()) 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(zone()) LStoreGlobalCell(value, TempRegister()))
+ : new(zone()) 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(zone()) LStoreGlobalGeneric(global_object, value);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ LInstruction* result =
+ DefineAsRegister(new(zone()) 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(zone()) LStoreContextSlot(context, value);
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
+ LOperand* obj = UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new(zone()) LLoadNamedField(obj));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
+ LOperand* object = UseFixed(instr->object(), a0);
+ LInstruction* result = DefineFixed(new(zone()) LLoadNamedGeneric(object), v0);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFunctionPrototype(
+ HLoadFunctionPrototype* instr) {
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LLoadFunctionPrototype(UseRegister(instr->function()))));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadExternalArrayPointer(
+ HLoadExternalArrayPointer* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LLoadExternalArrayPointer(input));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyed(HLoadKeyed* instr) {
+ ASSERT(instr->key()->representation().IsSmiOrInteger32());
+ ElementsKind elements_kind = instr->elements_kind();
+ LOperand* key = UseRegisterOrConstantAtStart(instr->key());
+ LLoadKeyed* result = NULL;
+
+ if (!instr->is_external()) {
+ LOperand* obj = NULL;
+ if (instr->representation().IsDouble()) {
+ obj = UseTempRegister(instr->elements());
+ } else {
+ ASSERT(instr->representation().IsSmiOrTagged());
+ obj = UseRegisterAtStart(instr->elements());
+ }
+ result = new(zone()) LLoadKeyed(obj, key);
+ } else {
+ ASSERT(
+ (instr->representation().IsInteger32() &&
+ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
+ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
+ (instr->representation().IsDouble() &&
+ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
+ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
+ LOperand* external_pointer = UseRegister(instr->elements());
+ result = new(zone()) LLoadKeyed(external_pointer, key);
+ }
+
+ DefineAsRegister(result);
+ // An unsigned int array load might overflow and cause a deopt, make sure it
+ // has an environment.
+ bool can_deoptimize = instr->RequiresHoleCheck() ||
+ (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS);
+ return can_deoptimize ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
+ LOperand* object = UseFixed(instr->object(), a1);
+ LOperand* key = UseFixed(instr->key(), a0);
+
+ LInstruction* result =
+ DefineFixed(new(zone()) LLoadKeyedGeneric(object, key), v0);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) {
+ ElementsKind elements_kind = instr->elements_kind();
+
+ if (!instr->is_external()) {
+ ASSERT(instr->elements()->representation().IsTagged());
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
+ LOperand* object = NULL;
+ LOperand* val = NULL;
+ LOperand* key = NULL;
+
+ if (instr->value()->representation().IsDouble()) {
+ object = UseRegisterAtStart(instr->elements());
+ key = UseRegisterOrConstantAtStart(instr->key());
+ val = UseTempRegister(instr->value());
+ } else {
+ ASSERT(instr->value()->representation().IsSmiOrTagged());
+ object = UseTempRegister(instr->elements());
+ val = needs_write_barrier ? UseTempRegister(instr->value())
+ : UseRegisterAtStart(instr->value());
+ key = needs_write_barrier ? UseTempRegister(instr->key())
+ : UseRegisterOrConstantAtStart(instr->key());
+ }
+
+ return new(zone()) LStoreKeyed(object, key, val);
+ }
+
+ ASSERT(
+ (instr->value()->representation().IsInteger32() &&
+ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
+ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
+ (instr->value()->representation().IsDouble() &&
+ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
+ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
+ ASSERT(instr->elements()->representation().IsExternal());
+ 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 = UseRegisterOrConstantAtStart(instr->key());
+ LOperand* external_pointer = UseRegister(instr->elements());
+
+ return new(zone()) LStoreKeyed(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(zone()) LStoreKeyedGeneric(obj, key, val), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTransitionElementsKind(
+ HTransitionElementsKind* instr) {
+ LOperand* object = UseRegister(instr->object());
+ if (IsSimpleMapChangeTransition(instr->from_kind(), instr->to_kind())) {
+ LOperand* new_map_reg = TempRegister();
+ LTransitionElementsKind* result =
+ new(zone()) LTransitionElementsKind(object, new_map_reg);
+ return result;
+ } else {
+ LTransitionElementsKind* result =
+ new(zone()) LTransitionElementsKind(object, NULL);
+ return AssignPointerMap(result);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoTrapAllocationMemento(
+ HTrapAllocationMemento* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* temp = TempRegister();
+ LTrapAllocationMemento* result =
+ new(zone()) LTrapAllocationMemento(object, temp);
+ return AssignEnvironment(result);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
+ bool is_in_object = instr->access().IsInobject();
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
+ bool needs_write_barrier_for_map = instr->has_transition() &&
+ instr->NeedsWriteBarrierForMap();
+
+ LOperand* obj;
+ if (needs_write_barrier) {
+ obj = is_in_object
+ ? UseRegister(instr->object())
+ : UseTempRegister(instr->object());
+ } else {
+ obj = needs_write_barrier_for_map
+ ? UseRegister(instr->object())
+ : UseRegisterAtStart(instr->object());
+ }
+
+ LOperand* val;
+ if (needs_write_barrier ||
+ (FLAG_track_fields && instr->field_representation().IsSmi())) {
+ val = UseTempRegister(instr->value());
+ } else if (FLAG_track_double_fields &&
+ instr->field_representation().IsDouble()) {
+ val = UseRegisterAtStart(instr->value());
+ } else {
+ val = UseRegister(instr->value());
+ }
+
+ // We need a temporary register for write barrier of the map field.
+ LOperand* temp = needs_write_barrier_for_map ? TempRegister() : NULL;
+
+ LStoreNamedField* result = new(zone()) LStoreNamedField(obj, val, temp);
+ if (FLAG_track_heap_object_fields &&
+ instr->field_representation().IsHeapObject()) {
+ if (!instr->value()->type().IsHeapObject()) {
+ return AssignEnvironment(result);
+ }
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
+ LOperand* obj = UseFixed(instr->object(), a1);
+ LOperand* val = UseFixed(instr->value(), a0);
+
+ LInstruction* result = new(zone()) 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(zone()) LStringAdd(left, right), v0),
+ instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
+ LOperand* string = UseTempRegister(instr->string());
+ LOperand* index = UseTempRegister(instr->index());
+ LStringCharCodeAt* result = new(zone()) LStringCharCodeAt(string, index);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+ LOperand* char_code = UseRegister(instr->value());
+ LStringCharFromCode* result = new(zone()) LStringCharFromCode(char_code);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) {
+ info()->MarkAsDeferredCalling();
+ LOperand* size = instr->size()->IsConstant()
+ ? UseConstant(instr->size())
+ : UseTempRegister(instr->size());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LAllocate* result = new(zone()) LAllocate(size, temp1, temp2);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) {
+ return MarkAsCall(DefineFixed(new(zone()) LRegExpLiteral, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
+ return MarkAsCall(DefineFixed(new(zone()) LFunctionLiteral, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) {
+ ASSERT(argument_count_ == 0);
+ allocator_->MarkAsOsrEntry();
+ current_block_->last_environment()->set_ast_id(instr->ast_id());
+ return AssignEnvironment(new(zone()) LOsrEntry);
+}
+
+
+LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
+ LParameter* result = new(zone()) LParameter;
+ if (instr->kind() == HParameter::STACK_PARAMETER) {
+ int spill_index = chunk()->GetParameterStackSlot(instr->index());
+ return DefineAsSpilled(result, spill_index);
+ } else {
+ ASSERT(info()->IsStub());
+ CodeStubInterfaceDescriptor* descriptor =
+ info()->code_stub()->GetInterfaceDescriptor(info()->isolate());
+ int index = static_cast<int>(instr->index());
+ Register reg = DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index);
+ return DefineFixed(result, reg);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
+ int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
+ if (spill_index > LUnallocated::kMaxFixedSlotIndex) {
+ Abort(kTooManySpillSlotsNeededForOSR);
+ spill_index = 0;
+ }
+ return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index);
+}
+
+
+LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) 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::DoCapturedObject(HCapturedObject* instr) {
+ // There are no real uses of a captured object.
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
+ info()->MarkAsRequiresFrame();
+ LOperand* args = UseRegister(instr->arguments());
+ LOperand* length;
+ LOperand* index;
+ if (instr->length()->IsConstant() && instr->index()->IsConstant()) {
+ length = UseRegisterOrConstant(instr->length());
+ index = UseOrConstant(instr->index());
+ } else {
+ length = UseTempRegister(instr->length());
+ index = UseRegisterAtStart(instr->index());
+ }
+ return DefineAsRegister(new(zone()) LAccessArgumentsAt(args, length, index));
+}
+
+
+LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) {
+ LOperand* object = UseFixed(instr->value(), a0);
+ LToFastProperties* result = new(zone()) LToFastProperties(object);
+ return MarkAsCall(DefineFixed(result, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
+ LTypeof* result = new(zone()) LTypeof(UseFixed(instr->value(), a0));
+ return MarkAsCall(DefineFixed(result, v0), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTypeofIsAndBranch(HTypeofIsAndBranch* instr) {
+ return new(zone()) LTypeofIsAndBranch(UseTempRegister(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
+ HIsConstructCallAndBranch* instr) {
+ return new(zone()) 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 = instr->values()->length() - 1; i >= 0; --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(zone()) LLazyBailout;
+ result = AssignEnvironment(result);
+ // Store the lazy deopt environment with the instruction if needed. Right
+ // now it is only used for LInstanceOfKnownGlobal.
+ instruction_pending_deoptimization_environment_->
+ SetDeferredLazyDeoptimizationEnvironment(result->environment());
+ instruction_pending_deoptimization_environment_ = NULL;
+ pending_deoptimization_ast_id_ = BailoutId::None();
+ return result;
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) {
+ if (instr->is_function_entry()) {
+ return MarkAsCall(new(zone()) LStackCheck, instr);
+ } else {
+ ASSERT(instr->is_backwards_branch());
+ return AssignEnvironment(AssignPointerMap(new(zone()) 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->inlining_kind(),
+ instr->undefined_receiver());
+ // Only replay binding of arguments object if it wasn't removed from graph.
+ if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
+ inner->Bind(instr->arguments_var(), instr->arguments_object());
+ }
+ inner->set_entry(instr);
+ current_block_->UpdateEnvironment(inner);
+ chunk_->AddInlinedClosure(instr->closure());
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
+ LInstruction* pop = NULL;
+
+ HEnvironment* env = current_block_->last_environment();
+
+ if (env->entry()->arguments_pushed()) {
+ int argument_count = env->arguments_environment()->parameter_count();
+ pop = new(zone()) LDrop(argument_count);
+ argument_count_ -= argument_count;
+ }
+
+ HEnvironment* outer = current_block_->last_environment()->
+ DiscardInlined(false);
+ current_block_->UpdateEnvironment(outer);
+
+ return pop;
+}
+
+
+LInstruction* LChunkBuilder::DoForInPrepareMap(HForInPrepareMap* instr) {
+ LOperand* object = UseFixed(instr->enumerable(), a0);
+ LForInPrepareMap* result = new(zone()) LForInPrepareMap(object);
+ return MarkAsCall(DefineFixed(result, v0), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoForInCacheArray(HForInCacheArray* instr) {
+ LOperand* map = UseRegister(instr->map());
+ return AssignEnvironment(DefineAsRegister(new(zone()) LForInCacheArray(map)));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMapValue(HCheckMapValue* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* map = UseRegisterAtStart(instr->map());
+ return AssignEnvironment(new(zone()) LCheckMapValue(value, map));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* index = UseRegister(instr->index());
+ return DefineAsRegister(new(zone()) LLoadFieldByIndex(object, index));
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/mips/lithium-mips.h b/chromium/v8/src/mips/lithium-mips.h
new file mode 100644
index 00000000000..a1792b17b22
--- /dev/null
+++ b/chromium/v8/src/mips/lithium-mips.h
@@ -0,0 +1,2730 @@
+// Copyright 2012 the V8 project authors. 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_MIPS_H_
+#define V8_MIPS_LITHIUM_MIPS_H_
+
+#include "hydrogen.h"
+#include "lithium-allocator.h"
+#include "lithium.h"
+#include "safepoint-table.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class LCodeGen;
+
+#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
+ V(AccessArgumentsAt) \
+ V(AddI) \
+ V(Allocate) \
+ V(ApplyArguments) \
+ V(ArgumentsElements) \
+ V(ArgumentsLength) \
+ V(ArithmeticD) \
+ V(ArithmeticT) \
+ V(BitI) \
+ V(BoundsCheck) \
+ V(Branch) \
+ V(CallConstantFunction) \
+ V(CallFunction) \
+ V(CallGlobal) \
+ V(CallKeyed) \
+ V(CallKnownGlobal) \
+ V(CallNamed) \
+ V(CallNew) \
+ V(CallNewArray) \
+ V(CallRuntime) \
+ V(CallStub) \
+ V(CheckFunction) \
+ V(CheckInstanceType) \
+ V(CheckMaps) \
+ V(CheckMapValue) \
+ V(CheckNonSmi) \
+ V(CheckSmi) \
+ V(ClampDToUint8) \
+ V(ClampIToUint8) \
+ V(ClampTToUint8) \
+ V(ClassOfTestAndBranch) \
+ V(CompareNumericAndBranch) \
+ V(CmpObjectEqAndBranch) \
+ V(CmpMapAndBranch) \
+ V(CmpT) \
+ V(ConstantD) \
+ V(ConstantE) \
+ V(ConstantI) \
+ V(ConstantS) \
+ V(ConstantT) \
+ V(Context) \
+ V(DateField) \
+ V(DebugBreak) \
+ V(DeclareGlobals) \
+ V(Deoptimize) \
+ V(DivI) \
+ V(DoubleToI) \
+ V(DoubleToSmi) \
+ V(Drop) \
+ V(DummyUse) \
+ V(ElementsKind) \
+ V(ForInCacheArray) \
+ V(ForInPrepareMap) \
+ V(FunctionLiteral) \
+ V(GetCachedArrayIndex) \
+ V(GlobalObject) \
+ V(GlobalReceiver) \
+ V(Goto) \
+ V(HasCachedArrayIndexAndBranch) \
+ V(HasInstanceTypeAndBranch) \
+ V(InnerAllocatedObject) \
+ V(InstanceOf) \
+ V(InstanceOfKnownGlobal) \
+ V(InstanceSize) \
+ V(InstructionGap) \
+ V(Integer32ToDouble) \
+ V(Integer32ToSmi) \
+ V(InvokeFunction) \
+ V(IsConstructCallAndBranch) \
+ V(IsObjectAndBranch) \
+ V(IsStringAndBranch) \
+ V(IsNumberAndBranch) \
+ V(IsSmiAndBranch) \
+ V(IsUndetectableAndBranch) \
+ V(Label) \
+ V(LazyBailout) \
+ V(LoadContextSlot) \
+ V(LoadExternalArrayPointer) \
+ V(LoadFieldByIndex) \
+ V(LoadFunctionPrototype) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
+ V(LoadKeyed) \
+ V(LoadKeyedGeneric) \
+ V(LoadNamedField) \
+ V(LoadNamedGeneric) \
+ V(MapEnumLength) \
+ V(MathAbs) \
+ V(MathCos) \
+ V(MathExp) \
+ V(MathFloor) \
+ V(MathFloorOfDiv) \
+ V(MathLog) \
+ V(MathMinMax) \
+ V(MathPowHalf) \
+ V(MathRound) \
+ V(MathSin) \
+ V(MathSqrt) \
+ V(MathTan) \
+ V(ModI) \
+ V(MulI) \
+ V(MultiplyAddD) \
+ V(NumberTagD) \
+ V(NumberTagI) \
+ V(NumberTagU) \
+ V(NumberUntagD) \
+ V(OsrEntry) \
+ V(OuterContext) \
+ V(Parameter) \
+ V(Power) \
+ V(PushArgument) \
+ V(Random) \
+ V(RegExpLiteral) \
+ V(Return) \
+ V(SeqStringSetChar) \
+ V(ShiftI) \
+ V(SmiTag) \
+ V(SmiUntag) \
+ V(StackCheck) \
+ V(StoreContextSlot) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
+ V(StoreKeyed) \
+ V(StoreKeyedGeneric) \
+ V(StoreNamedField) \
+ V(StoreNamedGeneric) \
+ V(StringAdd) \
+ V(StringCharCodeAt) \
+ V(StringCharFromCode) \
+ V(StringCompareAndBranch) \
+ V(SubI) \
+ V(TaggedToI) \
+ V(ThisFunction) \
+ V(Throw) \
+ V(ToFastProperties) \
+ V(TransitionElementsKind) \
+ V(TrapAllocationMemento) \
+ V(Typeof) \
+ V(TypeofIsAndBranch) \
+ V(Uint32ToDouble) \
+ V(UnknownOSRValue) \
+ V(ValueOf) \
+ V(WrapReceiver)
+
+#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()
+ : environment_(NULL),
+ hydrogen_value_(NULL),
+ bit_field_(IsCallBits::encode(false)) {
+ set_position(RelocInfo::kNoPosition);
+ }
+
+ virtual ~LInstruction() { }
+
+ virtual void CompileToNative(LCodeGen* generator) = 0;
+ virtual const char* Mnemonic() const = 0;
+ virtual void PrintTo(StringStream* stream);
+ virtual void PrintDataTo(StringStream* stream);
+ virtual void PrintOutputOperandTo(StringStream* stream);
+
+ 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(); }
+
+ // The 31 bits PositionBits is used to store the int position value. And the
+ // position value may be RelocInfo::kNoPosition (-1). The accessor always
+ // +1/-1 so that the encoded value of position in bit_field_ is always >= 0
+ // and can fit into the 31 bits PositionBits.
+ void set_position(int pos) {
+ bit_field_ = PositionBits::update(bit_field_, pos + 1);
+ }
+ int position() { return PositionBits::decode(bit_field_) - 1; }
+
+ void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
+ HValue* hydrogen_value() const { return hydrogen_value_; }
+
+ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
+
+ void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
+ bool IsCall() const { return IsCallBits::decode(bit_field_); }
+
+ // Interface to the register allocator and iterators.
+ bool ClobbersTemps() const { return IsCall(); }
+ bool ClobbersRegisters() const { return IsCall(); }
+ bool ClobbersDoubleRegisters() const { return IsCall(); }
+
+ // Interface to the register allocator and iterators.
+ bool IsMarkedAsCall() const { return IsCall(); }
+
+ virtual bool HasResult() const = 0;
+ virtual LOperand* result() const = 0;
+
+ LOperand* FirstInput() { return InputAt(0); }
+ LOperand* Output() { return HasResult() ? result() : NULL; }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return true; }
+
+#ifdef DEBUG
+ void VerifyCall();
+#endif
+
+ private:
+ // Iterator interface.
+ friend class InputIterator;
+ virtual int InputCount() = 0;
+ virtual LOperand* InputAt(int i) = 0;
+
+ friend class TempIterator;
+ virtual int TempCount() = 0;
+ virtual LOperand* TempAt(int i) = 0;
+
+ class IsCallBits: public BitField<bool, 0, 1> {};
+ class PositionBits: public BitField<int, 1, 31> {};
+
+ LEnvironment* environment_;
+ SetOncePointer<LPointerMap> pointer_map_;
+ HValue* hydrogen_value_;
+ int bit_field_;
+};
+
+
+// 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 && result() != NULL; }
+ void set_result(LOperand* operand) { results_[0] = operand; }
+ LOperand* result() const { return results_[0]; }
+
+ protected:
+ EmbeddedContainer<LOperand*, R> results_;
+ EmbeddedContainer<LOperand*, I> inputs_;
+ EmbeddedContainer<LOperand*, T> temps_;
+
+ private:
+ virtual int InputCount() { return I; }
+ virtual LOperand* InputAt(int i) { return inputs_[i]; }
+
+ virtual int TempCount() { return T; }
+ virtual LOperand* TempAt(int i) { return temps_[i]; }
+};
+
+
+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;
+ }
+
+ // 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);
+ }
+
+ bool IsRedundant() const;
+
+ HBasicBlock* block() const { return block_; }
+
+ enum InnerPosition {
+ BEFORE,
+ START,
+ END,
+ AFTER,
+ FIRST_INNER_POSITION = BEFORE,
+ LAST_INNER_POSITION = AFTER
+ };
+
+ LParallelMove* GetOrCreateParallelMove(InnerPosition pos, Zone* zone) {
+ if (parallel_moves_[pos] == NULL) {
+ parallel_moves_[pos] = new(zone) LParallelMove(zone);
+ }
+ return parallel_moves_[pos];
+ }
+
+ LParallelMove* GetParallelMove(InnerPosition pos) {
+ return parallel_moves_[pos];
+ }
+
+ private:
+ LParallelMove* parallel_moves_[LAST_INNER_POSITION + 1];
+ HBasicBlock* block_;
+};
+
+
+class LInstructionGap: public LGap {
+ public:
+ explicit LInstructionGap(HBasicBlock* block) : LGap(block) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const {
+ return !IsRedundant();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap")
+};
+
+
+class LGoto: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LGoto(int block_id) : block_id_(block_id) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const;
+ 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_; }
+
+ private:
+ int gap_instructions_size_;
+};
+
+
+class LDummyUse: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDummyUse(LOperand* value) {
+ inputs_[0] = value;
+ }
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use")
+};
+
+
+class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
+ DECLARE_HYDROGEN_ACCESSOR(Deoptimize)
+};
+
+
+class LLabel: public LGap {
+ public:
+ explicit LLabel(HBasicBlock* block)
+ : LGap(block), replacement_(NULL) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ 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(); }
+ bool is_osr_entry() const { return block()->is_osr_entry(); }
+ 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:
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ 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();
+ }
+};
+
+
+class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
+ public:
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value")
+};
+
+
+template<int I, int T>
+class LControlInstruction: public LTemplateInstruction<0, I, T> {
+ public:
+ LControlInstruction() : false_label_(NULL), true_label_(NULL) { }
+
+ virtual bool IsControl() const { return true; }
+
+ int SuccessorCount() { return hydrogen()->SuccessorCount(); }
+ HBasicBlock* SuccessorAt(int i) { return hydrogen()->SuccessorAt(i); }
+
+ int TrueDestination(LChunk* chunk) {
+ return chunk->LookupDestination(true_block_id());
+ }
+ int FalseDestination(LChunk* chunk) {
+ return chunk->LookupDestination(false_block_id());
+ }
+
+ Label* TrueLabel(LChunk* chunk) {
+ if (true_label_ == NULL) {
+ true_label_ = chunk->GetAssemblyLabel(TrueDestination(chunk));
+ }
+ return true_label_;
+ }
+ Label* FalseLabel(LChunk* chunk) {
+ if (false_label_ == NULL) {
+ false_label_ = chunk->GetAssemblyLabel(FalseDestination(chunk));
+ }
+ return false_label_;
+ }
+
+ protected:
+ int true_block_id() { return SuccessorAt(0)->block_id(); }
+ int false_block_id() { return SuccessorAt(1)->block_id(); }
+
+ private:
+ HControlInstruction* hydrogen() {
+ return HControlInstruction::cast(this->hydrogen_value());
+ }
+
+ Label* false_label_;
+ Label* true_label_;
+};
+
+
+class LWrapReceiver: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LWrapReceiver(LOperand* receiver, LOperand* function) {
+ inputs_[0] = receiver;
+ inputs_[1] = function;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver")
+
+ LOperand* receiver() { return inputs_[0]; }
+ LOperand* function() { return inputs_[1]; }
+};
+
+
+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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ LOperand* elements() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length")
+};
+
+
+class LArgumentsElements: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments-elements")
+ DECLARE_HYDROGEN_ACCESSOR(ArgumentsElements)
+};
+
+
+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;
+ }
+
+ // Used for the standard case.
+ LModI(LOperand* left,
+ LOperand* right,
+ LOperand* temp,
+ LOperand* temp2,
+ LOperand* temp3) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ temps_[2] = temp3;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+ LOperand* temp3() { return temps_[2]; }
+
+ 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* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
+ DECLARE_HYDROGEN_ACCESSOR(Div)
+};
+
+
+class LMathFloorOfDiv: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LMathFloorOfDiv(LOperand* left,
+ LOperand* right,
+ LOperand* temp = NULL) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
+ DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
+};
+
+
+class LMulI: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LMulI(LOperand* left, LOperand* right, LOperand* temp) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mul)
+};
+
+
+// Instruction for computing multiplier * multiplicand + addend.
+class LMultiplyAddD: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LMultiplyAddD(LOperand* addend, LOperand* multiplier,
+ LOperand* multiplicand) {
+ inputs_[0] = addend;
+ inputs_[1] = multiplier;
+ inputs_[2] = multiplicand;
+ }
+
+ LOperand* addend() { return inputs_[0]; }
+ LOperand* multiplier() { return inputs_[1]; }
+ LOperand* multiplicand() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MultiplyAddD, "multiply-add-d")
+};
+
+
+class LDebugBreak: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(DebugBreak, "break")
+};
+
+
+class LCompareNumericAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCompareNumericAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareNumericAndBranch,
+ "compare-numeric-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareNumericAndBranch)
+
+ Token::Value op() const { return hydrogen()->token(); }
+ bool is_double() const {
+ return hydrogen()->representation().IsDouble();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LMathFloor: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LMathFloor(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloor, "math-floor")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathRound: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LMathRound(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathRound, "math-round")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathAbs: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathAbs(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathAbs, "math-abs")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathLog: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathLog(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathLog, "math-log")
+};
+
+
+class LMathSin: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathSin(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathSin, "math-sin")
+};
+
+
+class LMathCos: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathCos(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathCos, "math-cos")
+};
+
+
+class LMathTan: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathTan(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathTan, "math-tan")
+};
+
+
+class LMathExp: public LTemplateInstruction<1, 1, 3> {
+ public:
+ LMathExp(LOperand* value,
+ LOperand* double_temp,
+ LOperand* temp1,
+ LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
+ temps_[2] = double_temp;
+ ExternalReference::InitializeMathExpData();
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp1() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+ LOperand* double_temp() { return temps_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathExp, "math-exp")
+};
+
+
+class LMathSqrt: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathSqrt(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathSqrt, "math-sqrt")
+};
+
+
+class LMathPowHalf: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LMathPowHalf(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathPowHalf, "math-pow-half")
+};
+
+
+class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCmpObjectEqAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch,
+ "cmp-object-eq-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareObjectEqAndBranch)
+};
+
+
+class LIsObjectAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LIsObjectAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsObjectAndBranch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LIsNumberAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LIsNumberAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsNumberAndBranch, "is-number-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsNumberAndBranch)
+};
+
+
+class LIsStringAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LIsStringAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get-cached-array-index")
+ DECLARE_HYDROGEN_ACCESSOR(GetCachedArrayIndex)
+};
+
+
+class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LHasCachedArrayIndexAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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:
+ LCmpT(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
+ "instance-of-known-global")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+
+ Handle<JSFunction> function() const { return hydrogen()->function(); }
+ LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
+ return lazy_deopt_env_;
+ }
+ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) {
+ lazy_deopt_env_ = env;
+ }
+
+ private:
+ LEnvironment* lazy_deopt_env_;
+};
+
+
+class LInstanceSize: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInstanceSize(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceSize, "instance-size")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceSize)
+};
+
+
+class LBoundsCheck: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LBoundsCheck(LOperand* index, LOperand* length) {
+ inputs_[0] = index;
+ inputs_[1] = length;
+ }
+
+ LOperand* index() { return inputs_[0]; }
+ LOperand* length() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check")
+ DECLARE_HYDROGEN_ACCESSOR(BoundsCheck)
+};
+
+
+class LBitI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LBitI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ Token::Value op() const { return op_; }
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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 LConstantS: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ConstantS, "constant-s")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ Smi* value() const { return Smi::FromInt(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 LConstantE: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ConstantE, "constant-e")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ ExternalReference value() const {
+ return hydrogen()->ExternalReferenceValue();
+ }
+};
+
+
+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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Branch, "branch")
+ DECLARE_HYDROGEN_ACCESSOR(Branch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LCmpMapAndBranch: public LControlInstruction<1, 1> {
+ public:
+ LCmpMapAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMap)
+
+ Handle<Map> map() const { return hydrogen()->map(); }
+};
+
+
+class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMapEnumLength(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length")
+};
+
+
+class LElementsKind: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LElementsKind(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+};
+
+
+class LDateField: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LDateField(LOperand* date, LOperand* temp, Smi* index) : index_(index) {
+ inputs_[0] = date;
+ temps_[0] = temp;
+ }
+
+ LOperand* date() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ Smi* index() const { return index_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+
+ private:
+ Smi* index_;
+};
+
+
+class LSeqStringSetChar: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LSeqStringSetChar(String::Encoding encoding,
+ LOperand* string,
+ LOperand* index,
+ LOperand* value) : encoding_(encoding) {
+ inputs_[0] = string;
+ inputs_[1] = index;
+ inputs_[2] = value;
+ }
+
+ String::Encoding encoding() { return encoding_; }
+ LOperand* string() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar, "seq-string-set-char")
+ DECLARE_HYDROGEN_ACCESSOR(SeqStringSetChar)
+
+ private:
+ String::Encoding encoding_;
+};
+
+
+class LThrow: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LThrow(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
+};
+
+
+class LAddI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LAddI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i")
+ DECLARE_HYDROGEN_ACCESSOR(Add)
+};
+
+
+class LMathMinMax: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LMathMinMax(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathMinMax, "math-min-max")
+ DECLARE_HYDROGEN_ACCESSOR(MathMinMax)
+};
+
+
+class LPower: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LPower(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+
+ 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_; }
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ Token::Value op() const { return op_; }
+
+ virtual Opcode opcode() const { return LInstruction::kArithmeticT; }
+ virtual void CompileToNative(LCodeGen* generator);
+ virtual const char* Mnemonic() const;
+
+ private:
+ Token::Value op_;
+};
+
+
+class LReturn: public LTemplateInstruction<0, 2, 0> {
+ public:
+ explicit LReturn(LOperand* value, LOperand* parameter_count) {
+ inputs_[0] = value;
+ inputs_[1] = parameter_count;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ bool has_constant_parameter_count() {
+ return parameter_count()->IsConstantOperand();
+ }
+ LConstantOperand* constant_parameter_count() {
+ ASSERT(has_constant_parameter_count());
+ return LConstantOperand::cast(parameter_count());
+ }
+ LOperand* parameter_count() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Return, "return")
+};
+
+
+class LLoadNamedField: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadNamedField(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedField)
+};
+
+
+class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadNamedGeneric(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedGeneric)
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+};
+
+
+class LLoadFunctionPrototype: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadFunctionPrototype(LOperand* function) {
+ inputs_[0] = function;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load-function-prototype")
+ DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype)
+};
+
+
+class LLoadExternalArrayPointer: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadExternalArrayPointer(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer,
+ "load-external-array-pointer")
+};
+
+
+class LLoadKeyed: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadKeyed(LOperand* elements, LOperand* key) {
+ inputs_[0] = elements;
+ inputs_[1] = key;
+ }
+
+ LOperand* elements() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ ElementsKind elements_kind() const {
+ return hydrogen()->elements_kind();
+ }
+ bool is_external() const {
+ return hydrogen()->is_external();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyed, "load-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(LoadKeyed)
+
+ virtual void PrintDataTo(StringStream* stream);
+ uint32_t additional_index() const { return hydrogen()->index_offset(); }
+};
+
+
+class LLoadKeyedGeneric: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadKeyedGeneric(LOperand* object, LOperand* key) {
+ inputs_[0] = object;
+ inputs_[1] = key;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load-keyed-generic")
+};
+
+
+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;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell)
+};
+
+
+class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> {
+ public:
+ explicit LStoreGlobalGeneric(LOperand* global_object,
+ LOperand* value) {
+ inputs_[0] = global_object;
+ inputs_[1] = value;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric)
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LLoadContextSlot: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadContextSlot(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot)
+
+ 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;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument")
+};
+
+
+class LDrop: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LDrop(int count) : count_(count) { }
+
+ int count() const { return count_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Drop, "drop")
+
+ private:
+ int count_;
+};
+
+
+class LInnerAllocatedObject: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInnerAllocatedObject(LOperand* base_object) {
+ inputs_[0] = base_object;
+ }
+
+ LOperand* base_object() { return inputs_[0]; }
+ int offset() { return hydrogen()->offset(); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(InnerAllocatedObject, "sub-allocated-object")
+ DECLARE_HYDROGEN_ACCESSOR(InnerAllocatedObject)
+};
+
+
+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")
+ DECLARE_HYDROGEN_ACCESSOR(Context)
+};
+
+
+class LOuterContext: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LOuterContext(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context")
+};
+
+
+class LDeclareGlobals: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals, "declare-globals")
+ DECLARE_HYDROGEN_ACCESSOR(DeclareGlobals)
+};
+
+
+class LGlobalObject: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LGlobalObject(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object")
+};
+
+
+class LGlobalReceiver: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LGlobalReceiver(LOperand* global_object) {
+ inputs_[0] = global_object;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver")
+};
+
+
+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;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ 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;
+ }
+
+ LOperand* key() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
+ DECLARE_HYDROGEN_ACCESSOR(CallFunction)
+
+ 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);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNew: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallNew(LOperand* constructor) {
+ inputs_[0] = constructor;
+ }
+
+ LOperand* constructor() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new")
+ DECLARE_HYDROGEN_ACCESSOR(CallNew)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNewArray: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallNewArray(LOperand* constructor) {
+ inputs_[0] = constructor;
+ }
+
+ LOperand* constructor() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNewArray, "call-new-array")
+ DECLARE_HYDROGEN_ACCESSOR(CallNewArray)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double")
+};
+
+
+class LInteger32ToSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInteger32ToSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToSmi, "int32-to-smi")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+};
+
+
+class LUint32ToDouble: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LUint32ToDouble(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double")
+};
+
+
+class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberTagI(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i")
+};
+
+
+class LNumberTagU: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberTagU(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u")
+};
+
+
+class LNumberTagD: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LNumberTagD(LOperand* value, LOperand* temp, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+};
+
+
+class LDoubleToSmi: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LDoubleToSmi(LOperand* value, LOperand* temp, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DoubleToSmi, "double-to-smi")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+
+ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+};
+
+
+// Sometimes truncating conversion from a tagged value to an int32.
+class LDoubleToI: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LDoubleToI(LOperand* value, LOperand* temp, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ 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* temp,
+ LOperand* temp2,
+ LOperand* temp3) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ temps_[2] = temp3;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+ LOperand* temp3() { return temps_[2]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag")
+};
+
+
+class LNumberUntagD: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberUntagD(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ bool needs_check() const { return needs_check_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag")
+
+ private:
+ bool needs_check_;
+};
+
+
+class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
+ public:
+ LStoreNamedField(LOperand* object, LOperand* value, LOperand* temp) {
+ inputs_[0] = object;
+ inputs_[1] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> transition() const { return hydrogen()->transition_map(); }
+ Representation representation() const {
+ return hydrogen()->field_representation();
+ }
+};
+
+
+class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LStoreNamedGeneric(LOperand* object, LOperand* value) {
+ inputs_[0] = object;
+ inputs_[1] = value;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreKeyed(LOperand* object, LOperand* key, LOperand* value) {
+ inputs_[0] = object;
+ inputs_[1] = key;
+ inputs_[2] = value;
+ }
+
+ bool is_external() const { return hydrogen()->is_external(); }
+ LOperand* elements() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+ ElementsKind elements_kind() const {
+ return hydrogen()->elements_kind();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyed, "store-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyed)
+
+ virtual void PrintDataTo(StringStream* stream);
+ bool NeedsCanonicalization() { return hydrogen()->NeedsCanonicalization(); }
+ uint32_t additional_index() const { return hydrogen()->index_offset(); }
+};
+
+
+class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* value) {
+ inputs_[0] = obj;
+ inputs_[1] = key;
+ inputs_[2] = value;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LTransitionElementsKind: public LTemplateInstruction<0, 1, 1> {
+ public:
+ LTransitionElementsKind(LOperand* object,
+ LOperand* new_map_temp) {
+ inputs_[0] = object;
+ temps_[0] = new_map_temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* new_map_temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind,
+ "transition-elements-kind")
+ DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> original_map() { return hydrogen()->original_map(); }
+ Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+ ElementsKind from_kind() { return hydrogen()->from_kind(); }
+ ElementsKind to_kind() { return hydrogen()->to_kind(); }
+};
+
+
+class LTrapAllocationMemento : public LTemplateInstruction<0, 1, 1> {
+ public:
+ LTrapAllocationMemento(LOperand* object,
+ LOperand* temp) {
+ inputs_[0] = object;
+ temps_[0] = temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TrapAllocationMemento,
+ "trap-allocation-memento")
+};
+
+
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+};
+
+
+
+class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringCharCodeAt(LOperand* string, LOperand* index) {
+ inputs_[0] = string;
+ inputs_[1] = index;
+ }
+
+ LOperand* string() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt)
+};
+
+
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LStringCharFromCode(LOperand* char_code) {
+ inputs_[0] = char_code;
+ }
+
+ LOperand* char_code() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+};
+
+
+class LCheckFunction: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckFunction(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type")
+ DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType)
+};
+
+
+class LCheckMaps: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckMaps(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMaps, "check-maps")
+ DECLARE_HYDROGEN_ACCESSOR(CheckMaps)
+};
+
+
+class LCheckSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCheckSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check-smi")
+};
+
+
+class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckNonSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check-non-smi")
+ DECLARE_HYDROGEN_ACCESSOR(CheckHeapObject)
+};
+
+
+class LClampDToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LClampDToUint8(LOperand* unclamped, LOperand* temp) {
+ inputs_[0] = unclamped;
+ temps_[0] = temp;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampIToUint8(LOperand* unclamped) {
+ inputs_[0] = unclamped;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LClampTToUint8(LOperand* unclamped, LOperand* temp) {
+ inputs_[0] = unclamped;
+ temps_[0] = temp;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8")
+};
+
+
+class LAllocate: public LTemplateInstruction<1, 2, 2> {
+ public:
+ LAllocate(LOperand* size, LOperand* temp1, LOperand* temp2) {
+ inputs_[1] = size;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
+ }
+
+ LOperand* size() { return inputs_[1]; }
+ LOperand* temp1() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Allocate, "allocate")
+ DECLARE_HYDROGEN_ACCESSOR(Allocate)
+};
+
+
+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)
+};
+
+
+class LToFastProperties: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LToFastProperties(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
+};
+
+
+class LTypeofIsAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LTypeofIsAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch,
+ "is-construct-call-and-branch")
+};
+
+
+class LOsrEntry: public LTemplateInstruction<0, 0, 0> {
+ public:
+ LOsrEntry() {}
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr-entry")
+};
+
+
+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 LForInPrepareMap: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LForInPrepareMap(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap, "for-in-prepare-map")
+};
+
+
+class LForInCacheArray: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LForInCacheArray(LOperand* map) {
+ inputs_[0] = map;
+ }
+
+ LOperand* map() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray, "for-in-cache-array")
+
+ int idx() {
+ return HForInCacheArray::cast(this->hydrogen_value())->idx();
+ }
+};
+
+
+class LCheckMapValue: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LCheckMapValue(LOperand* value, LOperand* map) {
+ inputs_[0] = value;
+ inputs_[1] = map;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* map() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMapValue, "check-map-value")
+};
+
+
+class LLoadFieldByIndex: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadFieldByIndex(LOperand* object, LOperand* index) {
+ inputs_[0] = object;
+ inputs_[1] = index;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex, "load-field-by-index")
+};
+
+
+class LChunkBuilder;
+class LPlatformChunk: public LChunk {
+ public:
+ LPlatformChunk(CompilationInfo* info, HGraph* graph)
+ : LChunk(info, graph) { }
+
+ int GetNextSpillIndex(bool is_double);
+ LOperand* GetNextSpillSlot(bool is_double);
+};
+
+
+class LChunkBuilder BASE_EMBEDDED {
+ public:
+ LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
+ : chunk_(NULL),
+ info_(info),
+ graph_(graph),
+ zone_(graph->zone()),
+ 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_(BailoutId::None()) { }
+
+ // Build the sequence for the graph.
+ LPlatformChunk* Build();
+
+ // Declare methods that deal with the individual node types.
+#define DECLARE_DO(type) LInstruction* Do##type(H##type* node);
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
+#undef DECLARE_DO
+
+ LInstruction* DoMultiplyAdd(HMul* mul, HValue* addend);
+
+ static bool HasMagicNumberForDivisor(int32_t divisor);
+ static HValue* SimplifiedDivisorForMathFloorOfDiv(HValue* val);
+
+ LInstruction* DoMathFloor(HUnaryMathOperation* instr);
+ LInstruction* DoMathRound(HUnaryMathOperation* instr);
+ LInstruction* DoMathAbs(HUnaryMathOperation* instr);
+ LInstruction* DoMathLog(HUnaryMathOperation* instr);
+ LInstruction* DoMathSin(HUnaryMathOperation* instr);
+ LInstruction* DoMathCos(HUnaryMathOperation* instr);
+ LInstruction* DoMathTan(HUnaryMathOperation* instr);
+ LInstruction* DoMathExp(HUnaryMathOperation* instr);
+ LInstruction* DoMathSqrt(HUnaryMathOperation* instr);
+ LInstruction* DoMathPowHalf(HUnaryMathOperation* instr);
+
+ private:
+ enum Status {
+ UNUSED,
+ BUILDING,
+ DONE,
+ ABORTED
+ };
+
+ LPlatformChunk* chunk() const { return chunk_; }
+ CompilationInfo* info() const { return info_; }
+ HGraph* graph() const { return graph_; }
+ Zone* zone() const { return zone_; }
+
+ 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(BailoutReason reason);
+
+ // 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 a constant operand.
+ MUST_USE_RESULT LOperand* UseConstant(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);
+
+ LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
+ int* argument_index_accumulator,
+ ZoneList<HValue*>* objects_to_materialize);
+
+ 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);
+
+ LPlatformChunk* chunk_;
+ CompilationInfo* info_;
+ HGraph* const graph_;
+ Zone* zone_;
+ Status status_;
+ HInstruction* current_instruction_;
+ HBasicBlock* current_block_;
+ HBasicBlock* next_block_;
+ int argument_count_;
+ LAllocator* allocator_;
+ int position_;
+ LInstruction* instruction_pending_deoptimization_environment_;
+ BailoutId pending_deoptimization_ast_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
+};
+
+#undef DECLARE_HYDROGEN_ACCESSOR
+#undef DECLARE_CONCRETE_INSTRUCTION
+
+} } // namespace v8::internal
+
+#endif // V8_MIPS_LITHIUM_MIPS_H_
diff --git a/chromium/v8/src/mips/macro-assembler-mips.cc b/chromium/v8/src/mips/macro-assembler-mips.cc
new file mode 100644
index 00000000000..e53f10afaca
--- /dev/null
+++ b/chromium/v8/src/mips/macro-assembler-mips.cc
@@ -0,0 +1,5604 @@
+// Copyright 2012 the V8 project authors. 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 <limits.h> // For LONG_MIN, LONG_MAX.
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "cpu-profiler.h"
+#include "debug.h"
+#include "runtime.h"
+
+namespace v8 {
+namespace internal {
+
+MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
+ : Assembler(arg_isolate, buffer, size),
+ generating_stub_(false),
+ allow_stub_calls_(true),
+ has_frame_(false) {
+ if (isolate() != NULL) {
+ code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
+ isolate());
+ }
+}
+
+
+void MacroAssembler::LoadRoot(Register destination,
+ Heap::RootListIndex index) {
+ lw(destination, MemOperand(s6, index << kPointerSizeLog2));
+}
+
+
+void MacroAssembler::LoadRoot(Register destination,
+ Heap::RootListIndex index,
+ Condition cond,
+ Register src1, const Operand& src2) {
+ Branch(2, NegateCondition(cond), src1, src2);
+ lw(destination, MemOperand(s6, index << kPointerSizeLog2));
+}
+
+
+void MacroAssembler::StoreRoot(Register source,
+ Heap::RootListIndex index) {
+ sw(source, MemOperand(s6, index << kPointerSizeLog2));
+}
+
+
+void MacroAssembler::StoreRoot(Register source,
+ Heap::RootListIndex index,
+ Condition cond,
+ Register src1, const Operand& src2) {
+ Branch(2, NegateCondition(cond), src1, src2);
+ sw(source, MemOperand(s6, index << kPointerSizeLog2));
+}
+
+
+void MacroAssembler::LoadHeapObject(Register result,
+ Handle<HeapObject> object) {
+ AllowDeferredHandleDereference using_raw_address;
+ if (isolate()->heap()->InNewSpace(*object)) {
+ Handle<Cell> cell = isolate()->factory()->NewCell(object);
+ li(result, Operand(cell));
+ lw(result, FieldMemOperand(result, Cell::kValueOffset));
+ } else {
+ li(result, Operand(object));
+ }
+}
+
+
+// Push and pop all registers that can hold pointers.
+void MacroAssembler::PushSafepointRegisters() {
+ // Safepoints expect a block of kNumSafepointRegisters values on the
+ // stack, so adjust the stack for unsaved registers.
+ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
+ ASSERT(num_unsaved >= 0);
+ if (num_unsaved > 0) {
+ Subu(sp, sp, Operand(num_unsaved * kPointerSize));
+ }
+ MultiPush(kSafepointSavedRegisters);
+}
+
+
+void MacroAssembler::PopSafepointRegisters() {
+ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
+ MultiPop(kSafepointSavedRegisters);
+ if (num_unsaved > 0) {
+ Addu(sp, sp, Operand(num_unsaved * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::PushSafepointRegistersAndDoubles() {
+ PushSafepointRegisters();
+ Subu(sp, sp, Operand(FPURegister::NumAllocatableRegisters() * kDoubleSize));
+ for (int i = 0; i < FPURegister::NumAllocatableRegisters(); i+=2) {
+ FPURegister reg = FPURegister::FromAllocationIndex(i);
+ sdc1(reg, MemOperand(sp, i * kDoubleSize));
+ }
+}
+
+
+void MacroAssembler::PopSafepointRegistersAndDoubles() {
+ for (int i = 0; i < FPURegister::NumAllocatableRegisters(); i+=2) {
+ FPURegister reg = FPURegister::FromAllocationIndex(i);
+ ldc1(reg, MemOperand(sp, i * kDoubleSize));
+ }
+ Addu(sp, sp, Operand(FPURegister::NumAllocatableRegisters() * kDoubleSize));
+ PopSafepointRegisters();
+}
+
+
+void MacroAssembler::StoreToSafepointRegistersAndDoublesSlot(Register src,
+ Register dst) {
+ sw(src, SafepointRegistersAndDoublesSlot(dst));
+}
+
+
+void MacroAssembler::StoreToSafepointRegisterSlot(Register src, Register dst) {
+ sw(src, SafepointRegisterSlot(dst));
+}
+
+
+void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
+ lw(dst, SafepointRegisterSlot(src));
+}
+
+
+int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
+ // The registers are pushed starting with the highest encoding,
+ // which means that lowest encodings are closest to the stack pointer.
+ return kSafepointRegisterStackIndexMap[reg_code];
+}
+
+
+MemOperand MacroAssembler::SafepointRegisterSlot(Register reg) {
+ return MemOperand(sp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
+}
+
+
+MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) {
+ UNIMPLEMENTED_MIPS();
+ // General purpose registers are pushed last on the stack.
+ int doubles_size = FPURegister::NumAllocatableRegisters() * kDoubleSize;
+ int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize;
+ return MemOperand(sp, doubles_size + register_offset);
+}
+
+
+void MacroAssembler::InNewSpace(Register object,
+ Register scratch,
+ Condition cc,
+ Label* branch) {
+ ASSERT(cc == eq || cc == ne);
+ And(scratch, object, Operand(ExternalReference::new_space_mask(isolate())));
+ Branch(branch, cc, scratch,
+ Operand(ExternalReference::new_space_start(isolate())));
+}
+
+
+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;
+
+ // 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));
+
+ 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 clobbered input registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (emit_debug_code()) {
+ li(value, Operand(BitCast<int32_t>(kZapValue + 4)));
+ li(dst, Operand(BitCast<int32_t>(kZapValue + 8)));
+ }
+}
+
+
+// Will clobber 4 registers: object, address, scratch, ip. The
+// register 'object' contains a heap object pointer. The heap object
+// tag is shifted away.
+void MacroAssembler::RecordWrite(Register object,
+ Register address,
+ 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(!address.is(cp) && !value.is(cp));
+
+ if (emit_debug_code()) {
+ lw(at, MemOperand(address));
+ Assert(
+ eq, kWrongAddressOrValuePassedToRecordWrite, at, Operand(value));
+ }
+
+ Label 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.
+ 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 clobbered registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (emit_debug_code()) {
+ 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();
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Allocation support.
+
+
+void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss) {
+ Label same_contexts;
+
+ ASSERT(!holder_reg.is(scratch));
+ ASSERT(!holder_reg.is(at));
+ ASSERT(!scratch.is(at));
+
+ // Load current lexical context from the stack frame.
+ lw(scratch, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ // In debug mode, make sure the lexical context is set.
+#ifdef DEBUG
+ Check(ne, kWeShouldNotHaveAnEmptyLexicalContext,
+ scratch, Operand(zero_reg));
+#endif
+
+ // Load the native context of the current context.
+ int offset =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ lw(scratch, FieldMemOperand(scratch, offset));
+ lw(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset));
+
+ // Check the context is a native context.
+ if (emit_debug_code()) {
+ push(holder_reg); // Temporarily save holder on the stack.
+ // Read the first word and compare to the native_context_map.
+ lw(holder_reg, FieldMemOperand(scratch, HeapObject::kMapOffset));
+ LoadRoot(at, Heap::kNativeContextMapRootIndex);
+ Check(eq, kJSGlobalObjectNativeContextShouldBeANativeContext,
+ holder_reg, Operand(at));
+ pop(holder_reg); // Restore holder.
+ }
+
+ // Check if both contexts are the same.
+ lw(at, FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+ Branch(&same_contexts, eq, scratch, Operand(at));
+
+ // Check the context is a native context.
+ if (emit_debug_code()) {
+ push(holder_reg); // Temporarily save holder on the stack.
+ mov(holder_reg, at); // Move at to its holding place.
+ LoadRoot(at, Heap::kNullValueRootIndex);
+ Check(ne, kJSGlobalProxyContextShouldNotBeNull,
+ holder_reg, Operand(at));
+
+ lw(holder_reg, FieldMemOperand(holder_reg, HeapObject::kMapOffset));
+ LoadRoot(at, Heap::kNativeContextMapRootIndex);
+ Check(eq, kJSGlobalObjectNativeContextShouldBeANativeContext,
+ holder_reg, Operand(at));
+ // Restore at is not needed. at is reloaded below.
+ pop(holder_reg); // Restore holder.
+ // Restore at to holder's context.
+ lw(at, FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+ }
+
+ // Check that the security token in the calling global object is
+ // compatible with the security token in the receiving global
+ // object.
+ int token_offset = Context::kHeaderSize +
+ Context::SECURITY_TOKEN_INDEX * kPointerSize;
+
+ lw(scratch, FieldMemOperand(scratch, token_offset));
+ lw(at, FieldMemOperand(at, token_offset));
+ Branch(miss, ne, scratch, Operand(at));
+
+ bind(&same_contexts);
+}
+
+
+void MacroAssembler::GetNumberHash(Register reg0, Register scratch) {
+ // First of all we assign the hash seed to scratch.
+ LoadRoot(scratch, Heap::kHashSeedRootIndex);
+ SmiUntag(scratch);
+
+ // Xor original key with a seed.
+ xor_(reg0, reg0, scratch);
+
+ // Compute the hash code from the untagged key. This must be kept in sync
+ // with ComputeIntegerHash in utils.h.
+ //
+ // hash = ~hash + (hash << 15);
+ nor(scratch, reg0, zero_reg);
+ sll(at, reg0, 15);
+ addu(reg0, scratch, at);
+
+ // hash = hash ^ (hash >> 12);
+ srl(at, reg0, 12);
+ xor_(reg0, reg0, at);
+
+ // hash = hash + (hash << 2);
+ sll(at, reg0, 2);
+ addu(reg0, reg0, at);
+
+ // hash = hash ^ (hash >> 4);
+ srl(at, reg0, 4);
+ xor_(reg0, reg0, at);
+
+ // hash = hash * 2057;
+ sll(scratch, reg0, 11);
+ sll(at, reg0, 3);
+ addu(reg0, reg0, at);
+ addu(reg0, reg0, scratch);
+
+ // hash = hash ^ (hash >> 16);
+ srl(at, reg0, 16);
+ xor_(reg0, reg0, at);
+}
+
+
+void MacroAssembler::LoadFromNumberDictionary(Label* miss,
+ Register elements,
+ Register key,
+ Register result,
+ Register reg0,
+ Register reg1,
+ Register reg2) {
+ // Register use:
+ //
+ // elements - holds the slow-case elements of the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the same as 'key' or 'result'.
+ // Unchanged on bailout so 'key' or 'result' can be used
+ // in further computation.
+ //
+ // Scratch registers:
+ //
+ // reg0 - holds the untagged key on entry and holds the hash once computed.
+ //
+ // reg1 - Used to hold the capacity mask of the dictionary.
+ //
+ // reg2 - Used for the index into the dictionary.
+ // at - Temporary (avoid MacroAssembler instructions also using 'at').
+ Label done;
+
+ GetNumberHash(reg0, reg1);
+
+ // Compute the capacity mask.
+ lw(reg1, FieldMemOperand(elements, SeededNumberDictionary::kCapacityOffset));
+ sra(reg1, reg1, kSmiTagSize);
+ Subu(reg1, reg1, Operand(1));
+
+ // Generate an unrolled loop that performs a few probes before giving up.
+ static const int kProbes = 4;
+ for (int i = 0; i < kProbes; i++) {
+ // Use reg2 for index calculations and keep the hash intact in reg0.
+ mov(reg2, reg0);
+ // Compute the masked index: (hash + i + i * i) & mask.
+ if (i > 0) {
+ Addu(reg2, reg2, Operand(SeededNumberDictionary::GetProbeOffset(i)));
+ }
+ and_(reg2, reg2, reg1);
+
+ // Scale the index by multiplying by the element size.
+ ASSERT(SeededNumberDictionary::kEntrySize == 3);
+ sll(at, reg2, 1); // 2x.
+ addu(reg2, reg2, at); // reg2 = reg2 * 3.
+
+ // Check if the key is identical to the name.
+ sll(at, reg2, kPointerSizeLog2);
+ addu(reg2, elements, at);
+
+ lw(at, FieldMemOperand(reg2, SeededNumberDictionary::kElementsStartOffset));
+ if (i != kProbes - 1) {
+ Branch(&done, eq, key, Operand(at));
+ } else {
+ Branch(miss, ne, key, Operand(at));
+ }
+ }
+
+ bind(&done);
+ // Check that the value is a normal property.
+ // reg2: elements + (index * kPointerSize).
+ const int kDetailsOffset =
+ SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize;
+ lw(reg1, FieldMemOperand(reg2, kDetailsOffset));
+ And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask)));
+ Branch(miss, ne, at, Operand(zero_reg));
+
+ // Get the value at the masked, scaled index and return.
+ const int kValueOffset =
+ SeededNumberDictionary::kElementsStartOffset + kPointerSize;
+ lw(result, FieldMemOperand(reg2, kValueOffset));
+}
+
+
+// ---------------------------------------------------------------------------
+// Instruction macros.
+
+void MacroAssembler::Addu(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ addu(rd, rs, rt.rm());
+ } else {
+ if (is_int16(rt.imm32_) && !MustUseReg(rt.rmode_)) {
+ addiu(rd, rs, rt.imm32_);
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ addu(rd, rs, at);
+ }
+ }
+}
+
+
+void MacroAssembler::Subu(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ subu(rd, rs, rt.rm());
+ } else {
+ if (is_int16(rt.imm32_) && !MustUseReg(rt.rmode_)) {
+ addiu(rd, rs, -rt.imm32_); // No subiu instr, use addiu(x, y, -imm).
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ subu(rd, rs, at);
+ }
+ }
+}
+
+
+void MacroAssembler::Mul(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ if (kArchVariant == kLoongson) {
+ mult(rs, rt.rm());
+ mflo(rd);
+ } else {
+ mul(rd, rs, rt.rm());
+ }
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ if (kArchVariant == kLoongson) {
+ mult(rs, at);
+ mflo(rd);
+ } else {
+ mul(rd, rs, at);
+ }
+ }
+}
+
+
+void MacroAssembler::Mult(Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ mult(rs, rt.rm());
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ mult(rs, at);
+ }
+}
+
+
+void MacroAssembler::Multu(Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ multu(rs, rt.rm());
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ multu(rs, at);
+ }
+}
+
+
+void MacroAssembler::Div(Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ div(rs, rt.rm());
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ div(rs, at);
+ }
+}
+
+
+void MacroAssembler::Divu(Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ divu(rs, rt.rm());
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ divu(rs, at);
+ }
+}
+
+
+void MacroAssembler::And(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ and_(rd, rs, rt.rm());
+ } else {
+ if (is_uint16(rt.imm32_) && !MustUseReg(rt.rmode_)) {
+ andi(rd, rs, rt.imm32_);
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ and_(rd, rs, at);
+ }
+ }
+}
+
+
+void MacroAssembler::Or(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ or_(rd, rs, rt.rm());
+ } else {
+ if (is_uint16(rt.imm32_) && !MustUseReg(rt.rmode_)) {
+ ori(rd, rs, rt.imm32_);
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ or_(rd, rs, at);
+ }
+ }
+}
+
+
+void MacroAssembler::Xor(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ xor_(rd, rs, rt.rm());
+ } else {
+ if (is_uint16(rt.imm32_) && !MustUseReg(rt.rmode_)) {
+ xori(rd, rs, rt.imm32_);
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ xor_(rd, rs, at);
+ }
+ }
+}
+
+
+void MacroAssembler::Nor(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ nor(rd, rs, rt.rm());
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ nor(rd, rs, at);
+ }
+}
+
+
+void MacroAssembler::Neg(Register rs, const Operand& rt) {
+ ASSERT(rt.is_reg());
+ ASSERT(!at.is(rs));
+ ASSERT(!at.is(rt.rm()));
+ li(at, -1);
+ xor_(rs, rt.rm(), at);
+}
+
+
+void MacroAssembler::Slt(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ slt(rd, rs, rt.rm());
+ } else {
+ if (is_int16(rt.imm32_) && !MustUseReg(rt.rmode_)) {
+ slti(rd, rs, rt.imm32_);
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ slt(rd, rs, at);
+ }
+ }
+}
+
+
+void MacroAssembler::Sltu(Register rd, Register rs, const Operand& rt) {
+ if (rt.is_reg()) {
+ sltu(rd, rs, rt.rm());
+ } else {
+ if (is_uint16(rt.imm32_) && !MustUseReg(rt.rmode_)) {
+ sltiu(rd, rs, rt.imm32_);
+ } else {
+ // li handles the relocation.
+ ASSERT(!rs.is(at));
+ li(at, rt);
+ sltu(rd, rs, at);
+ }
+ }
+}
+
+
+void MacroAssembler::Ror(Register rd, Register rs, const Operand& rt) {
+ if (kArchVariant == kMips32r2) {
+ if (rt.is_reg()) {
+ rotrv(rd, rs, rt.rm());
+ } else {
+ rotr(rd, rs, rt.imm32_);
+ }
+ } else {
+ if (rt.is_reg()) {
+ subu(at, zero_reg, rt.rm());
+ sllv(at, rs, at);
+ srlv(rd, rs, rt.rm());
+ or_(rd, rd, at);
+ } else {
+ if (rt.imm32_ == 0) {
+ srl(rd, rs, 0);
+ } else {
+ srl(at, rs, rt.imm32_);
+ sll(rd, rs, (0x20 - rt.imm32_) & 0x1f);
+ or_(rd, rd, at);
+ }
+ }
+ }
+}
+
+
+//------------Pseudo-instructions-------------
+
+void MacroAssembler::li(Register rd, Operand j, LiFlags mode) {
+ ASSERT(!j.is_reg());
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ if (!MustUseReg(j.rmode_) && mode == OPTIMIZE_SIZE) {
+ // Normal load of an immediate value which does not need Relocation Info.
+ if (is_int16(j.imm32_)) {
+ addiu(rd, zero_reg, j.imm32_);
+ } else if (!(j.imm32_ & kHiMask)) {
+ ori(rd, zero_reg, j.imm32_);
+ } else if (!(j.imm32_ & kImm16Mask)) {
+ lui(rd, (j.imm32_ >> kLuiShift) & kImm16Mask);
+ } else {
+ lui(rd, (j.imm32_ >> kLuiShift) & kImm16Mask);
+ ori(rd, rd, (j.imm32_ & kImm16Mask));
+ }
+ } else {
+ if (MustUseReg(j.rmode_)) {
+ RecordRelocInfo(j.rmode_, j.imm32_);
+ }
+ // 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_ >> kLuiShift) & kImm16Mask);
+ ori(rd, rd, (j.imm32_ & kImm16Mask));
+ }
+}
+
+
+void MacroAssembler::MultiPush(RegList regs) {
+ int16_t num_to_push = NumberOfBitsSet(regs);
+ int16_t stack_offset = num_to_push * kPointerSize;
+
+ Subu(sp, sp, Operand(stack_offset));
+ for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
+ if ((regs & (1 << i)) != 0) {
+ stack_offset -= kPointerSize;
+ sw(ToRegister(i), MemOperand(sp, stack_offset));
+ }
+ }
+}
+
+
+void MacroAssembler::MultiPushReversed(RegList regs) {
+ int16_t num_to_push = NumberOfBitsSet(regs);
+ int16_t stack_offset = num_to_push * kPointerSize;
+
+ Subu(sp, sp, Operand(stack_offset));
+ for (int16_t i = 0; i < kNumRegisters; i++) {
+ if ((regs & (1 << i)) != 0) {
+ stack_offset -= kPointerSize;
+ sw(ToRegister(i), MemOperand(sp, stack_offset));
+ }
+ }
+}
+
+
+void MacroAssembler::MultiPop(RegList regs) {
+ int16_t stack_offset = 0;
+
+ for (int16_t i = 0; i < kNumRegisters; i++) {
+ if ((regs & (1 << i)) != 0) {
+ lw(ToRegister(i), MemOperand(sp, stack_offset));
+ stack_offset += kPointerSize;
+ }
+ }
+ addiu(sp, sp, stack_offset);
+}
+
+
+void MacroAssembler::MultiPopReversed(RegList regs) {
+ int16_t stack_offset = 0;
+
+ for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
+ if ((regs & (1 << i)) != 0) {
+ lw(ToRegister(i), MemOperand(sp, stack_offset));
+ stack_offset += kPointerSize;
+ }
+ }
+ addiu(sp, sp, stack_offset);
+}
+
+
+void MacroAssembler::MultiPushFPU(RegList regs) {
+ int16_t num_to_push = NumberOfBitsSet(regs);
+ int16_t stack_offset = num_to_push * kDoubleSize;
+
+ Subu(sp, sp, Operand(stack_offset));
+ 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));
+ }
+ }
+}
+
+
+void MacroAssembler::MultiPushReversedFPU(RegList regs) {
+ int16_t num_to_push = NumberOfBitsSet(regs);
+ int16_t stack_offset = num_to_push * kDoubleSize;
+
+ Subu(sp, sp, Operand(stack_offset));
+ for (int16_t i = 0; i < kNumRegisters; i++) {
+ if ((regs & (1 << i)) != 0) {
+ stack_offset -= kDoubleSize;
+ sdc1(FPURegister::from_code(i), MemOperand(sp, stack_offset));
+ }
+ }
+}
+
+
+void MacroAssembler::MultiPopFPU(RegList regs) {
+ int16_t stack_offset = 0;
+
+ for (int16_t i = 0; i < kNumRegisters; i++) {
+ if ((regs & (1 << i)) != 0) {
+ ldc1(FPURegister::from_code(i), MemOperand(sp, stack_offset));
+ stack_offset += kDoubleSize;
+ }
+ }
+ addiu(sp, sp, stack_offset);
+}
+
+
+void MacroAssembler::MultiPopReversedFPU(RegList regs) {
+ int16_t stack_offset = 0;
+
+ 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;
+ }
+ }
+ addiu(sp, sp, stack_offset);
+}
+
+
+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,
+ uint16_t size) {
+ ASSERT(pos < 32);
+ ASSERT(pos + size < 33);
+
+ if (kArchVariant == kMips32r2) {
+ ext_(rt, rs, pos, size);
+ } else {
+ // Move rs to rt and shift it left then right to get the
+ // desired bitfield on the right side and zeroes on the left.
+ int shift_left = 32 - (pos + size);
+ sll(rt, rs, shift_left); // Acts as a move if shift_left == 0.
+
+ int shift_right = 32 - size;
+ if (shift_right > 0) {
+ srl(rt, rt, shift_right);
+ }
+ }
+}
+
+
+void MacroAssembler::Ins(Register rt,
+ Register rs,
+ uint16_t pos,
+ uint16_t size) {
+ ASSERT(pos < 32);
+ ASSERT(pos + size <= 32);
+ ASSERT(size != 0);
+
+ if (kArchVariant == kMips32r2) {
+ ins_(rt, rs, pos, size);
+ } else {
+ ASSERT(!rt.is(t8) && !rs.is(t8));
+ 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);
+ }
+}
+
+
+void MacroAssembler::Cvt_d_uw(FPURegister fd,
+ FPURegister fs,
+ FPURegister scratch) {
+ // Move the data from fs to t8.
+ mfc1(t8, fs);
+ Cvt_d_uw(fd, t8, scratch);
+}
+
+
+void MacroAssembler::Cvt_d_uw(FPURegister fd,
+ Register rs,
+ FPURegister scratch) {
+ // Convert rs to a FP value in fd (and fd + 1).
+ // We do this by converting rs minus the MSB to avoid sign conversion,
+ // then adding 2^31 to the result (if needed).
+
+ ASSERT(!fd.is(scratch));
+ ASSERT(!rs.is(t9));
+ ASSERT(!rs.is(at));
+
+ // Save rs's MSB to t9.
+ Ext(t9, rs, 31, 1);
+ // Remove rs's MSB.
+ Ext(at, rs, 0, 31);
+ // Move the result to fd.
+ mtc1(at, fd);
+
+ // Convert fd to a real FP value.
+ cvt_d_w(fd, fd);
+
+ Label conversion_done;
+
+ // If rs's MSB was 0, it's done.
+ // Otherwise we need to add that to the FP register.
+ Branch(&conversion_done, eq, t9, Operand(zero_reg));
+
+ // Load 2^31 into f20 as its float representation.
+ li(at, 0x41E00000);
+ mtc1(at, FPURegister::from_code(scratch.code() + 1));
+ mtc1(zero_reg, scratch);
+ // Add it to fd.
+ add_d(fd, fd, scratch);
+
+ bind(&conversion_done);
+}
+
+
+void MacroAssembler::Trunc_uw_d(FPURegister fd,
+ FPURegister fs,
+ FPURegister scratch) {
+ Trunc_uw_d(fs, t8, scratch);
+ mtc1(t8, fd);
+}
+
+
+void MacroAssembler::Trunc_w_d(FPURegister fd, FPURegister fs) {
+ if (kArchVariant == kLoongson && fd.is(fs)) {
+ mfc1(t8, FPURegister::from_code(fs.code() + 1));
+ trunc_w_d(fd, fs);
+ mtc1(t8, FPURegister::from_code(fs.code() + 1));
+ } else {
+ trunc_w_d(fd, fs);
+ }
+}
+
+
+void MacroAssembler::Round_w_d(FPURegister fd, FPURegister fs) {
+ if (kArchVariant == kLoongson && fd.is(fs)) {
+ mfc1(t8, FPURegister::from_code(fs.code() + 1));
+ round_w_d(fd, fs);
+ mtc1(t8, FPURegister::from_code(fs.code() + 1));
+ } else {
+ round_w_d(fd, fs);
+ }
+}
+
+
+void MacroAssembler::Floor_w_d(FPURegister fd, FPURegister fs) {
+ if (kArchVariant == kLoongson && fd.is(fs)) {
+ mfc1(t8, FPURegister::from_code(fs.code() + 1));
+ floor_w_d(fd, fs);
+ mtc1(t8, FPURegister::from_code(fs.code() + 1));
+ } else {
+ floor_w_d(fd, fs);
+ }
+}
+
+
+void MacroAssembler::Ceil_w_d(FPURegister fd, FPURegister fs) {
+ if (kArchVariant == kLoongson && fd.is(fs)) {
+ mfc1(t8, FPURegister::from_code(fs.code() + 1));
+ ceil_w_d(fd, fs);
+ mtc1(t8, FPURegister::from_code(fs.code() + 1));
+ } else {
+ ceil_w_d(fd, fs);
+ }
+}
+
+
+void MacroAssembler::Trunc_uw_d(FPURegister fd,
+ Register rs,
+ FPURegister scratch) {
+ ASSERT(!fd.is(scratch));
+ ASSERT(!rs.is(at));
+
+ // Load 2^31 into scratch as its float representation.
+ li(at, 0x41E00000);
+ mtc1(at, FPURegister::from_code(scratch.code() + 1));
+ mtc1(zero_reg, scratch);
+ // Test if scratch > fd.
+ // If fd < 2^31 we can convert it normally.
+ 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.
+ sub_d(scratch, fd, scratch);
+ trunc_w_d(scratch, scratch);
+ mfc1(rs, scratch);
+ Or(rs, rs, 1 << 31);
+
+ Label done;
+ Branch(&done);
+ // Simple conversion.
+ bind(&simple_convert);
+ trunc_w_d(scratch, fd);
+ mfc1(rs, scratch);
+
+ bind(&done);
+}
+
+
+void MacroAssembler::BranchF(Label* target,
+ Label* nan,
+ Condition cc,
+ FPURegister cmp1,
+ FPURegister cmp2,
+ BranchDelaySlot bd) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ 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 lt:
+ c(OLT, D, cmp1, cmp2);
+ bc1t(target);
+ break;
+ case gt:
+ c(ULE, D, cmp1, cmp2);
+ bc1f(target);
+ break;
+ case ge:
+ c(ULT, D, cmp1, cmp2);
+ bc1f(target);
+ break;
+ case le:
+ c(OLE, D, cmp1, cmp2);
+ bc1t(target);
+ break;
+ case eq:
+ c(EQ, D, cmp1, cmp2);
+ bc1t(target);
+ break;
+ case ueq:
+ c(UEQ, D, cmp1, cmp2);
+ bc1t(target);
+ break;
+ case ne:
+ c(EQ, D, cmp1, cmp2);
+ bc1f(target);
+ break;
+ case nue:
+ c(UEQ, D, cmp1, cmp2);
+ bc1f(target);
+ break;
+ default:
+ CHECK(0);
+ };
+ }
+
+ if (bd == PROTECT) {
+ nop();
+ }
+}
+
+
+void MacroAssembler::Move(FPURegister dst, double imm) {
+ 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());
+ }
+ }
+}
+
+
+void MacroAssembler::Movz(Register rd, Register rs, Register rt) {
+ if (kArchVariant == kLoongson) {
+ Label done;
+ Branch(&done, ne, rt, Operand(zero_reg));
+ mov(rd, rs);
+ bind(&done);
+ } else {
+ movz(rd, rs, rt);
+ }
+}
+
+
+void MacroAssembler::Movn(Register rd, Register rs, Register rt) {
+ if (kArchVariant == kLoongson) {
+ Label done;
+ Branch(&done, eq, rt, Operand(zero_reg));
+ mov(rd, rs);
+ bind(&done);
+ } else {
+ movn(rd, rs, rt);
+ }
+}
+
+
+void MacroAssembler::Movt(Register rd, Register rs, uint16_t cc) {
+ if (kArchVariant == kLoongson) {
+ // Tests an FP condition code and then conditionally move rs to rd.
+ // We do not currently use any FPU cc bit other than bit 0.
+ ASSERT(cc == 0);
+ ASSERT(!(rs.is(t8) || rd.is(t8)));
+ Label done;
+ Register scratch = t8;
+ // For testing purposes we need to fetch content of the FCSR register and
+ // than test its cc (floating point condition code) bit (for cc = 0, it is
+ // 24. bit of the FCSR).
+ cfc1(scratch, FCSR);
+ // For the MIPS I, II and III architectures, the contents of scratch is
+ // UNPREDICTABLE for the instruction immediately following CFC1.
+ nop();
+ srl(scratch, scratch, 16);
+ andi(scratch, scratch, 0x0080);
+ Branch(&done, eq, scratch, Operand(zero_reg));
+ mov(rd, rs);
+ bind(&done);
+ } else {
+ movt(rd, rs, cc);
+ }
+}
+
+
+void MacroAssembler::Movf(Register rd, Register rs, uint16_t cc) {
+ if (kArchVariant == kLoongson) {
+ // Tests an FP condition code and then conditionally move rs to rd.
+ // We do not currently use any FPU cc bit other than bit 0.
+ ASSERT(cc == 0);
+ ASSERT(!(rs.is(t8) || rd.is(t8)));
+ Label done;
+ Register scratch = t8;
+ // For testing purposes we need to fetch content of the FCSR register and
+ // than test its cc (floating point condition code) bit (for cc = 0, it is
+ // 24. bit of the FCSR).
+ cfc1(scratch, FCSR);
+ // For the MIPS I, II and III architectures, the contents of scratch is
+ // UNPREDICTABLE for the instruction immediately following CFC1.
+ nop();
+ srl(scratch, scratch, 16);
+ andi(scratch, scratch, 0x0080);
+ Branch(&done, ne, scratch, Operand(zero_reg));
+ mov(rd, rs);
+ bind(&done);
+ } else {
+ movf(rd, rs, cc);
+ }
+}
+
+
+void MacroAssembler::Clz(Register rd, Register rs) {
+ if (kArchVariant == kLoongson) {
+ ASSERT(!(rd.is(t8) || rd.is(t9)) && !(rs.is(t8) || rs.is(t9)));
+ Register mask = t8;
+ Register scratch = t9;
+ Label loop, end;
+ mov(at, rs);
+ mov(rd, zero_reg);
+ lui(mask, 0x8000);
+ bind(&loop);
+ and_(scratch, at, mask);
+ Branch(&end, ne, scratch, Operand(zero_reg));
+ addiu(rd, rd, 1);
+ Branch(&loop, ne, mask, Operand(zero_reg), USE_DELAY_SLOT);
+ srl(mask, mask, 1);
+ bind(&end);
+ } else {
+ clz(rd, rs);
+ }
+}
+
+
+// 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.
+// This method implementation differs from the ARM version for performance
+// reasons.
+void MacroAssembler::ConvertToInt32(Register source,
+ Register dest,
+ Register scratch,
+ Register scratch2,
+ FPURegister double_scratch,
+ Label *not_int32) {
+ Label right_exponent, done;
+ // Get exponent word (ENDIAN issues).
+ lw(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
+ // Get exponent alone in scratch2.
+ And(scratch2, scratch, Operand(HeapNumber::kExponentMask));
+ // Load dest with zero. We use this either for the final shift or
+ // for the answer.
+ mov(dest, zero_reg);
+ // Check whether the exponent matches a 32 bit signed int that is not a Smi.
+ // A non-Smi 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;
+ // If we have a match of the int32-but-not-Smi exponent then skip some logic.
+ Branch(&right_exponent, eq, scratch2, Operand(non_smi_exponent));
+ // If the exponent is higher than that then go to not_int32 case. This
+ // catches numbers that don't fit in a signed int32, infinities and NaNs.
+ 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, i.e.
+ // it rounds to zero.
+ const uint32_t zero_exponent =
+ (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
+ Subu(scratch2, scratch2, Operand(zero_exponent));
+ // Dest already has a Smi zero.
+ Branch(&done, lt, scratch2, Operand(zero_reg));
+ bind(&right_exponent);
+
+ // MIPS FPU instructions implementing double precision to integer
+ // conversion using round to zero. Since the FP value was qualified
+ // above, the resulting integer should be a legal int32.
+ // The original 'Exponent' word is still in scratch.
+ lwc1(double_scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset));
+ mtc1(scratch, FPURegister::from_code(double_scratch.code() + 1));
+ trunc_w_d(double_scratch, double_scratch);
+ mfc1(dest, double_scratch);
+
+ bind(&done);
+}
+
+
+void MacroAssembler::EmitFPUTruncate(FPURoundingMode rounding_mode,
+ Register result,
+ DoubleRegister double_input,
+ Register scratch,
+ DoubleRegister double_scratch,
+ Register except_flag,
+ CheckForInexactConversion check_inexact) {
+ ASSERT(!result.is(scratch));
+ ASSERT(!double_input.is(double_scratch));
+ ASSERT(!except_flag.is(scratch));
+
+ Label done;
+
+ // Clear the except flag (0 = no exception)
+ mov(except_flag, zero_reg);
+
+ // Test for values that can be exactly represented as a signed 32-bit integer.
+ cvt_w_d(double_scratch, double_input);
+ mfc1(result, double_scratch);
+ cvt_d_w(double_scratch, double_scratch);
+ BranchF(&done, NULL, eq, double_input, double_scratch);
+
+ int32_t except_mask = kFCSRFlagMask; // Assume interested in all exceptions.
+
+ if (check_inexact == kDontCheckForInexactConversion) {
+ // Ignore inexact exceptions.
+ except_mask &= ~kFCSRInexactFlagMask;
+ }
+
+ // Save FCSR.
+ cfc1(scratch, FCSR);
+ // Disable FPU exceptions.
+ ctc1(zero_reg, FCSR);
+
+ // Do operation based on rounding mode.
+ switch (rounding_mode) {
+ case kRoundToNearest:
+ Round_w_d(double_scratch, double_input);
+ break;
+ case kRoundToZero:
+ Trunc_w_d(double_scratch, double_input);
+ break;
+ case kRoundToPlusInf:
+ Ceil_w_d(double_scratch, double_input);
+ break;
+ case kRoundToMinusInf:
+ Floor_w_d(double_scratch, double_input);
+ break;
+ } // End of switch-statement.
+
+ // Retrieve FCSR.
+ cfc1(except_flag, FCSR);
+ // Restore FCSR.
+ ctc1(scratch, FCSR);
+ // Move the converted value into the result register.
+ mfc1(result, double_scratch);
+
+ // Check for fpu exceptions.
+ And(except_flag, except_flag, Operand(except_mask));
+
+ bind(&done);
+}
+
+
+void MacroAssembler::EmitOutOfInt32RangeTruncate(Register result,
+ Register input_high,
+ Register input_low,
+ Register scratch) {
+ Label done, normal_exponent, restore_sign;
+ // Extract the biased exponent in result.
+ Ext(result,
+ input_high,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+
+ // Check for Infinity and NaNs, which should return 0.
+ Subu(scratch, result, HeapNumber::kExponentMask);
+ Movz(result, zero_reg, scratch);
+ Branch(&done, eq, scratch, Operand(zero_reg));
+
+ // Express exponent as delta to (number of mantissa bits + 31).
+ Subu(result,
+ result,
+ Operand(HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 31));
+
+ // If the delta is strictly positive, all bits would be shifted away,
+ // which means that we can return 0.
+ Branch(&normal_exponent, le, result, Operand(zero_reg));
+ mov(result, zero_reg);
+ Branch(&done);
+
+ bind(&normal_exponent);
+ const int kShiftBase = HeapNumber::kNonMantissaBitsInTopWord - 1;
+ // Calculate shift.
+ Addu(scratch, result, Operand(kShiftBase + HeapNumber::kMantissaBits));
+
+ // Save the sign.
+ Register sign = result;
+ result = no_reg;
+ And(sign, input_high, Operand(HeapNumber::kSignMask));
+
+ // On ARM shifts > 31 bits are valid and will result in zero. On MIPS we need
+ // to check for this specific case.
+ Label high_shift_needed, high_shift_done;
+ Branch(&high_shift_needed, lt, scratch, Operand(32));
+ mov(input_high, zero_reg);
+ Branch(&high_shift_done);
+ bind(&high_shift_needed);
+
+ // Set the implicit 1 before the mantissa part in input_high.
+ Or(input_high,
+ input_high,
+ Operand(1 << HeapNumber::kMantissaBitsInTopWord));
+ // Shift the mantissa bits to the correct position.
+ // We don't need to clear non-mantissa bits as they will be shifted away.
+ // If they weren't, it would mean that the answer is in the 32bit range.
+ sllv(input_high, input_high, scratch);
+
+ bind(&high_shift_done);
+
+ // Replace the shifted bits with bits from the lower mantissa word.
+ Label pos_shift, shift_done;
+ li(at, 32);
+ subu(scratch, at, scratch);
+ Branch(&pos_shift, ge, scratch, Operand(zero_reg));
+
+ // Negate scratch.
+ Subu(scratch, zero_reg, scratch);
+ sllv(input_low, input_low, scratch);
+ Branch(&shift_done);
+
+ bind(&pos_shift);
+ srlv(input_low, input_low, scratch);
+
+ bind(&shift_done);
+ Or(input_high, input_high, Operand(input_low));
+ // Restore sign if necessary.
+ mov(scratch, sign);
+ result = sign;
+ sign = no_reg;
+ Subu(result, zero_reg, input_high);
+ Movz(result, input_high, scratch);
+ bind(&done);
+}
+
+
+void MacroAssembler::EmitECMATruncate(Register result,
+ FPURegister double_input,
+ FPURegister single_scratch,
+ Register scratch,
+ Register scratch2,
+ Register scratch3) {
+ ASSERT(!scratch2.is(result));
+ ASSERT(!scratch3.is(result));
+ ASSERT(!scratch3.is(scratch2));
+ ASSERT(!scratch.is(result) &&
+ !scratch.is(scratch2) &&
+ !scratch.is(scratch3));
+ ASSERT(!single_scratch.is(double_input));
+
+ Label done;
+ Label manual;
+
+ // Clear cumulative exception flags and save the FCSR.
+ cfc1(scratch2, FCSR);
+ ctc1(zero_reg, FCSR);
+ // Try a conversion to a signed integer.
+ trunc_w_d(single_scratch, double_input);
+ mfc1(result, single_scratch);
+ // Retrieve and restore the FCSR.
+ cfc1(scratch, FCSR);
+ ctc1(scratch2, FCSR);
+ // Check for overflow and NaNs.
+ And(scratch,
+ scratch,
+ kFCSROverflowFlagMask | kFCSRUnderflowFlagMask | kFCSRInvalidOpFlagMask);
+ // If we had no exceptions we are done.
+ 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,
+ input_low,
+ scratch);
+ bind(&done);
+}
+
+
+void MacroAssembler::GetLeastBitsFromSmi(Register dst,
+ Register src,
+ int num_least_bits) {
+ Ext(dst, src, kSmiTagSize, num_least_bits);
+}
+
+
+void MacroAssembler::GetLeastBitsFromInt32(Register dst,
+ Register src,
+ int num_least_bits) {
+ And(dst, src, Operand((1 << num_least_bits) - 1));
+}
+
+
+// Emulated condtional branches do not emit a nop in the branch delay slot.
+//
+// BRANCH_ARGS_CHECK checks that conditional jump arguments are correct.
+#define BRANCH_ARGS_CHECK(cond, rs, rt) ASSERT( \
+ (cond == cc_always && rs.is(zero_reg) && rt.rm().is(zero_reg)) || \
+ (cond != cc_always && (!rs.is(zero_reg) || !rt.rm().is(zero_reg))))
+
+
+void MacroAssembler::Branch(int16_t offset, BranchDelaySlot bdslot) {
+ BranchShort(offset, bdslot);
+}
+
+
+void MacroAssembler::Branch(int16_t offset, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot) {
+ BranchShort(offset, cond, rs, rt, bdslot);
+}
+
+
+void MacroAssembler::Branch(Label* L, BranchDelaySlot bdslot) {
+ if (L->is_bound()) {
+ if (is_near(L)) {
+ BranchShort(L, bdslot);
+ } else {
+ Jr(L, bdslot);
+ }
+ } else {
+ if (is_trampoline_emitted()) {
+ Jr(L, bdslot);
+ } else {
+ BranchShort(L, bdslot);
+ }
+ }
+}
+
+
+void MacroAssembler::Branch(Label* L, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot) {
+ 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 {
+ 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);
+ }
+ }
+}
+
+
+void MacroAssembler::Branch(Label* L,
+ Condition cond,
+ Register rs,
+ Heap::RootListIndex index,
+ BranchDelaySlot bdslot) {
+ LoadRoot(at, index);
+ Branch(L, cond, rs, Operand(at), bdslot);
+}
+
+
+void MacroAssembler::BranchShort(int16_t offset, BranchDelaySlot bdslot) {
+ b(offset);
+
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::BranchShort(int16_t offset, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot) {
+ BRANCH_ARGS_CHECK(cond, rs, rt);
+ ASSERT(!rs.is(zero_reg));
+ Register r2 = no_reg;
+ Register scratch = at;
+
+ if (rt.is_reg()) {
+ // NOTE: 'at' can be clobbered by Branch but it is legal to use it as rs or
+ // rt.
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ r2 = rt.rm_;
+ switch (cond) {
+ case cc_always:
+ b(offset);
+ break;
+ case eq:
+ beq(rs, r2, offset);
+ break;
+ case ne:
+ bne(rs, r2, offset);
+ break;
+ // Signed comparison.
+ case greater:
+ if (r2.is(zero_reg)) {
+ bgtz(rs, offset);
+ } else {
+ slt(scratch, r2, rs);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case greater_equal:
+ if (r2.is(zero_reg)) {
+ bgez(rs, offset);
+ } else {
+ slt(scratch, rs, r2);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ case less:
+ if (r2.is(zero_reg)) {
+ bltz(rs, offset);
+ } else {
+ slt(scratch, rs, r2);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case less_equal:
+ if (r2.is(zero_reg)) {
+ blez(rs, offset);
+ } else {
+ slt(scratch, r2, rs);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ // Unsigned comparison.
+ case Ugreater:
+ if (r2.is(zero_reg)) {
+ bgtz(rs, offset);
+ } else {
+ sltu(scratch, r2, rs);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case Ugreater_equal:
+ if (r2.is(zero_reg)) {
+ bgez(rs, offset);
+ } else {
+ sltu(scratch, rs, r2);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ case Uless:
+ if (r2.is(zero_reg)) {
+ // No code needs to be emitted.
+ return;
+ } else {
+ sltu(scratch, rs, r2);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case Uless_equal:
+ if (r2.is(zero_reg)) {
+ b(offset);
+ } else {
+ sltu(scratch, r2, rs);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ // Be careful to always use shifted_branch_offset only just before the
+ // branch instruction, as the location will be remember for patching the
+ // target.
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ switch (cond) {
+ case cc_always:
+ b(offset);
+ break;
+ case eq:
+ // We don't want any other register but scratch clobbered.
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ beq(rs, r2, offset);
+ break;
+ case ne:
+ // We don't want any other register but scratch clobbered.
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ bne(rs, r2, offset);
+ break;
+ // Signed comparison.
+ case greater:
+ if (rt.imm32_ == 0) {
+ bgtz(rs, offset);
+ } else {
+ r2 = scratch;
+ li(r2, rt);
+ slt(scratch, r2, rs);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case greater_equal:
+ if (rt.imm32_ == 0) {
+ bgez(rs, offset);
+ } else if (is_int16(rt.imm32_)) {
+ slti(scratch, rs, rt.imm32_);
+ beq(scratch, zero_reg, offset);
+ } else {
+ r2 = scratch;
+ li(r2, rt);
+ slt(scratch, rs, r2);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ case less:
+ if (rt.imm32_ == 0) {
+ bltz(rs, offset);
+ } else if (is_int16(rt.imm32_)) {
+ slti(scratch, rs, rt.imm32_);
+ bne(scratch, zero_reg, offset);
+ } else {
+ r2 = scratch;
+ li(r2, rt);
+ slt(scratch, rs, r2);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case less_equal:
+ if (rt.imm32_ == 0) {
+ blez(rs, offset);
+ } else {
+ r2 = scratch;
+ li(r2, rt);
+ slt(scratch, r2, rs);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ // Unsigned comparison.
+ case Ugreater:
+ if (rt.imm32_ == 0) {
+ bgtz(rs, offset);
+ } else {
+ r2 = scratch;
+ li(r2, rt);
+ sltu(scratch, r2, rs);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case Ugreater_equal:
+ if (rt.imm32_ == 0) {
+ bgez(rs, offset);
+ } else if (is_int16(rt.imm32_)) {
+ sltiu(scratch, rs, rt.imm32_);
+ beq(scratch, zero_reg, offset);
+ } else {
+ r2 = scratch;
+ li(r2, rt);
+ sltu(scratch, rs, r2);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ case Uless:
+ if (rt.imm32_ == 0) {
+ // No code needs to be emitted.
+ return;
+ } else if (is_int16(rt.imm32_)) {
+ sltiu(scratch, rs, rt.imm32_);
+ bne(scratch, zero_reg, offset);
+ } else {
+ r2 = scratch;
+ li(r2, rt);
+ sltu(scratch, rs, r2);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case Uless_equal:
+ if (rt.imm32_ == 0) {
+ b(offset);
+ } else {
+ r2 = scratch;
+ li(r2, rt);
+ sltu(scratch, r2, rs);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::BranchShort(Label* L, BranchDelaySlot bdslot) {
+ // We use branch_offset as an argument for the branch instructions to be sure
+ // it is called just before generating the branch instruction, as needed.
+
+ b(shifted_branch_offset(L, false));
+
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::BranchShort(Label* L, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot) {
+ BRANCH_ARGS_CHECK(cond, rs, rt);
+
+ int32_t offset = 0;
+ Register r2 = no_reg;
+ Register scratch = at;
+ if (rt.is_reg()) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ r2 = rt.rm_;
+ // Be careful to always use shifted_branch_offset only just before the
+ // branch instruction, as the location will be remember for patching the
+ // target.
+ switch (cond) {
+ case cc_always:
+ offset = shifted_branch_offset(L, false);
+ b(offset);
+ break;
+ case eq:
+ offset = shifted_branch_offset(L, false);
+ beq(rs, r2, offset);
+ break;
+ case ne:
+ offset = shifted_branch_offset(L, false);
+ bne(rs, r2, offset);
+ break;
+ // Signed comparison.
+ case greater:
+ if (r2.is(zero_reg)) {
+ offset = shifted_branch_offset(L, false);
+ bgtz(rs, offset);
+ } else {
+ slt(scratch, r2, rs);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case greater_equal:
+ if (r2.is(zero_reg)) {
+ offset = shifted_branch_offset(L, false);
+ bgez(rs, offset);
+ } else {
+ slt(scratch, rs, r2);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ case less:
+ if (r2.is(zero_reg)) {
+ offset = shifted_branch_offset(L, false);
+ bltz(rs, offset);
+ } else {
+ slt(scratch, rs, r2);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case less_equal:
+ if (r2.is(zero_reg)) {
+ offset = shifted_branch_offset(L, false);
+ blez(rs, offset);
+ } else {
+ slt(scratch, r2, rs);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ // Unsigned comparison.
+ case Ugreater:
+ if (r2.is(zero_reg)) {
+ offset = shifted_branch_offset(L, false);
+ bgtz(rs, offset);
+ } else {
+ sltu(scratch, r2, rs);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case Ugreater_equal:
+ if (r2.is(zero_reg)) {
+ offset = shifted_branch_offset(L, false);
+ bgez(rs, offset);
+ } else {
+ sltu(scratch, rs, r2);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ case Uless:
+ if (r2.is(zero_reg)) {
+ // No code needs to be emitted.
+ return;
+ } else {
+ sltu(scratch, rs, r2);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case Uless_equal:
+ if (r2.is(zero_reg)) {
+ offset = shifted_branch_offset(L, false);
+ b(offset);
+ } else {
+ sltu(scratch, r2, rs);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ // Be careful to always use shifted_branch_offset only just before the
+ // branch instruction, as the location will be remember for patching the
+ // target.
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ switch (cond) {
+ case cc_always:
+ offset = shifted_branch_offset(L, false);
+ b(offset);
+ break;
+ case eq:
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ offset = shifted_branch_offset(L, false);
+ beq(rs, r2, offset);
+ break;
+ case ne:
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ offset = shifted_branch_offset(L, false);
+ bne(rs, r2, offset);
+ break;
+ // Signed comparison.
+ case greater:
+ if (rt.imm32_ == 0) {
+ offset = shifted_branch_offset(L, false);
+ bgtz(rs, offset);
+ } else {
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ slt(scratch, r2, rs);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case greater_equal:
+ if (rt.imm32_ == 0) {
+ offset = shifted_branch_offset(L, false);
+ bgez(rs, offset);
+ } else if (is_int16(rt.imm32_)) {
+ slti(scratch, rs, rt.imm32_);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ } else {
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ slt(scratch, rs, r2);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ case less:
+ if (rt.imm32_ == 0) {
+ offset = shifted_branch_offset(L, false);
+ bltz(rs, offset);
+ } else if (is_int16(rt.imm32_)) {
+ slti(scratch, rs, rt.imm32_);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ } else {
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ slt(scratch, rs, r2);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case less_equal:
+ if (rt.imm32_ == 0) {
+ offset = shifted_branch_offset(L, false);
+ blez(rs, offset);
+ } else {
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ slt(scratch, r2, rs);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ // Unsigned comparison.
+ case Ugreater:
+ if (rt.imm32_ == 0) {
+ offset = shifted_branch_offset(L, false);
+ bgtz(rs, offset);
+ } else {
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ sltu(scratch, r2, rs);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case Ugreater_equal:
+ if (rt.imm32_ == 0) {
+ offset = shifted_branch_offset(L, false);
+ bgez(rs, offset);
+ } else if (is_int16(rt.imm32_)) {
+ sltiu(scratch, rs, rt.imm32_);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ } else {
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ sltu(scratch, rs, r2);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ case Uless:
+ if (rt.imm32_ == 0) {
+ // No code needs to be emitted.
+ return;
+ } else if (is_int16(rt.imm32_)) {
+ sltiu(scratch, rs, rt.imm32_);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ } else {
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ sltu(scratch, rs, r2);
+ offset = shifted_branch_offset(L, false);
+ bne(scratch, zero_reg, offset);
+ }
+ break;
+ case Uless_equal:
+ if (rt.imm32_ == 0) {
+ offset = shifted_branch_offset(L, false);
+ b(offset);
+ } else {
+ ASSERT(!scratch.is(rs));
+ r2 = scratch;
+ li(r2, rt);
+ sltu(scratch, r2, rs);
+ offset = shifted_branch_offset(L, false);
+ beq(scratch, zero_reg, offset);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ // Check that offset could actually hold on an int16_t.
+ ASSERT(is_int16(offset));
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::BranchAndLink(int16_t offset, BranchDelaySlot bdslot) {
+ BranchAndLinkShort(offset, bdslot);
+}
+
+
+void MacroAssembler::BranchAndLink(int16_t offset, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot) {
+ BranchAndLinkShort(offset, cond, rs, rt, bdslot);
+}
+
+
+void MacroAssembler::BranchAndLink(Label* L, BranchDelaySlot bdslot) {
+ if (L->is_bound()) {
+ if (is_near(L)) {
+ BranchAndLinkShort(L, bdslot);
+ } else {
+ Jalr(L, bdslot);
+ }
+ } else {
+ if (is_trampoline_emitted()) {
+ Jalr(L, bdslot);
+ } else {
+ BranchAndLinkShort(L, bdslot);
+ }
+ }
+}
+
+
+void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot) {
+ 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 {
+ 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);
+ }
+ }
+}
+
+
+// We need to use a bgezal or bltzal, but they can't be used directly with the
+// slt instructions. We could use sub or add instead but we would miss overflow
+// cases, so we keep slt and add an intermediate third instruction.
+void MacroAssembler::BranchAndLinkShort(int16_t offset,
+ BranchDelaySlot bdslot) {
+ bal(offset);
+
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::BranchAndLinkShort(int16_t offset, Condition cond,
+ Register rs, const Operand& rt,
+ BranchDelaySlot bdslot) {
+ BRANCH_ARGS_CHECK(cond, rs, rt);
+ Register r2 = no_reg;
+ Register scratch = at;
+
+ if (rt.is_reg()) {
+ r2 = rt.rm_;
+ } else if (cond != cc_always) {
+ r2 = scratch;
+ li(r2, rt);
+ }
+
+ {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ switch (cond) {
+ case cc_always:
+ bal(offset);
+ break;
+ case eq:
+ bne(rs, r2, 2);
+ nop();
+ bal(offset);
+ break;
+ case ne:
+ beq(rs, r2, 2);
+ nop();
+ bal(offset);
+ break;
+
+ // Signed comparison.
+ case greater:
+ slt(scratch, r2, rs);
+ addiu(scratch, scratch, -1);
+ bgezal(scratch, offset);
+ break;
+ case greater_equal:
+ slt(scratch, rs, r2);
+ addiu(scratch, scratch, -1);
+ bltzal(scratch, offset);
+ break;
+ case less:
+ slt(scratch, rs, r2);
+ addiu(scratch, scratch, -1);
+ bgezal(scratch, offset);
+ break;
+ case less_equal:
+ slt(scratch, r2, rs);
+ addiu(scratch, scratch, -1);
+ bltzal(scratch, offset);
+ break;
+
+ // Unsigned comparison.
+ case Ugreater:
+ sltu(scratch, r2, rs);
+ addiu(scratch, scratch, -1);
+ bgezal(scratch, offset);
+ break;
+ case Ugreater_equal:
+ sltu(scratch, rs, r2);
+ addiu(scratch, scratch, -1);
+ bltzal(scratch, offset);
+ break;
+ case Uless:
+ sltu(scratch, rs, r2);
+ addiu(scratch, scratch, -1);
+ bgezal(scratch, offset);
+ break;
+ case Uless_equal:
+ sltu(scratch, r2, rs);
+ addiu(scratch, scratch, -1);
+ bltzal(scratch, offset);
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ }
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::BranchAndLinkShort(Label* L, BranchDelaySlot bdslot) {
+ bal(shifted_branch_offset(L, false));
+
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::BranchAndLinkShort(Label* L, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot) {
+ BRANCH_ARGS_CHECK(cond, rs, rt);
+
+ int32_t offset = 0;
+ Register r2 = no_reg;
+ Register scratch = at;
+ if (rt.is_reg()) {
+ r2 = rt.rm_;
+ } else if (cond != cc_always) {
+ r2 = scratch;
+ li(r2, rt);
+ }
+
+ {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ switch (cond) {
+ case cc_always:
+ offset = shifted_branch_offset(L, false);
+ bal(offset);
+ break;
+ case eq:
+ bne(rs, r2, 2);
+ nop();
+ offset = shifted_branch_offset(L, false);
+ bal(offset);
+ break;
+ case ne:
+ beq(rs, r2, 2);
+ nop();
+ offset = shifted_branch_offset(L, false);
+ bal(offset);
+ break;
+
+ // Signed comparison.
+ case greater:
+ slt(scratch, r2, rs);
+ addiu(scratch, scratch, -1);
+ offset = shifted_branch_offset(L, false);
+ bgezal(scratch, offset);
+ break;
+ case greater_equal:
+ slt(scratch, rs, r2);
+ addiu(scratch, scratch, -1);
+ offset = shifted_branch_offset(L, false);
+ bltzal(scratch, offset);
+ break;
+ case less:
+ slt(scratch, rs, r2);
+ addiu(scratch, scratch, -1);
+ offset = shifted_branch_offset(L, false);
+ bgezal(scratch, offset);
+ break;
+ case less_equal:
+ slt(scratch, r2, rs);
+ addiu(scratch, scratch, -1);
+ offset = shifted_branch_offset(L, false);
+ bltzal(scratch, offset);
+ break;
+
+ // Unsigned comparison.
+ case Ugreater:
+ sltu(scratch, r2, rs);
+ addiu(scratch, scratch, -1);
+ offset = shifted_branch_offset(L, false);
+ bgezal(scratch, offset);
+ break;
+ case Ugreater_equal:
+ sltu(scratch, rs, r2);
+ addiu(scratch, scratch, -1);
+ offset = shifted_branch_offset(L, false);
+ bltzal(scratch, offset);
+ break;
+ case Uless:
+ sltu(scratch, rs, r2);
+ addiu(scratch, scratch, -1);
+ offset = shifted_branch_offset(L, false);
+ bgezal(scratch, offset);
+ break;
+ case Uless_equal:
+ sltu(scratch, r2, rs);
+ addiu(scratch, scratch, -1);
+ offset = shifted_branch_offset(L, false);
+ bltzal(scratch, offset);
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ }
+ // Check that offset could actually hold on an int16_t.
+ ASSERT(is_int16(offset));
+
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::Jump(Register target,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ if (cond == cc_always) {
+ jr(target);
+ } else {
+ BRANCH_ARGS_CHECK(cond, rs, rt);
+ Branch(2, NegateCondition(cond), rs, rt);
+ jr(target);
+ }
+ // Emit a nop in the branch delay slot if required.
+ if (bd == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::Jump(intptr_t target,
+ RelocInfo::Mode rmode,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ Label skip;
+ if (cond != cc_always) {
+ Branch(USE_DELAY_SLOT, &skip, NegateCondition(cond), rs, rt);
+ }
+ // The first instruction of 'li' may be placed in the delay slot.
+ // This is not an issue, t9 is expected to be clobbered anyway.
+ li(t9, Operand(target, rmode));
+ Jump(t9, al, zero_reg, Operand(zero_reg), bd);
+ bind(&skip);
+}
+
+
+void MacroAssembler::Jump(Address target,
+ RelocInfo::Mode rmode,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ ASSERT(!RelocInfo::IsCodeTarget(rmode));
+ Jump(reinterpret_cast<intptr_t>(target), rmode, cond, rs, rt, bd);
+}
+
+
+void MacroAssembler::Jump(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ AllowDeferredHandleDereference embedding_raw_address;
+ Jump(reinterpret_cast<intptr_t>(code.location()), rmode, cond, rs, rt, bd);
+}
+
+
+int MacroAssembler::CallSize(Register target,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ int size = 0;
+
+ if (cond == cc_always) {
+ size += 1;
+ } else {
+ size += 3;
+ }
+
+ if (bd == PROTECT)
+ size += 1;
+
+ return size * kInstrSize;
+}
+
+
+// Note: To call gcc-compiled C code on mips, you must call thru t9.
+void MacroAssembler::Call(Register target,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ Label start;
+ bind(&start);
+ if (cond == cc_always) {
+ jalr(target);
+ } else {
+ BRANCH_ARGS_CHECK(cond, rs, rt);
+ Branch(2, NegateCondition(cond), rs, rt);
+ jalr(target);
+ }
+ // Emit a nop in the branch delay slot if required.
+ if (bd == PROTECT)
+ nop();
+
+ ASSERT_EQ(CallSize(target, cond, rs, rt, bd),
+ SizeOfCodeGeneratedSince(&start));
+}
+
+
+int MacroAssembler::CallSize(Address target,
+ RelocInfo::Mode rmode,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ int size = CallSize(t9, cond, rs, rt, bd);
+ return size + 2 * kInstrSize;
+}
+
+
+void MacroAssembler::Call(Address target,
+ RelocInfo::Mode rmode,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ Label start;
+ bind(&start);
+ int32_t target_int = reinterpret_cast<int32_t>(target);
+ // Must record previous source positions before the
+ // li() generates a new code target.
+ positions_recorder()->WriteRecordedPositions();
+ li(t9, Operand(target_int, rmode), CONSTANT_SIZE);
+ Call(t9, cond, rs, rt, bd);
+ ASSERT_EQ(CallSize(target, rmode, cond, rs, rt, bd),
+ SizeOfCodeGeneratedSince(&start));
+}
+
+
+int MacroAssembler::CallSize(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ AllowDeferredHandleDereference using_raw_address;
+ return CallSize(reinterpret_cast<Address>(code.location()),
+ rmode, cond, rs, rt, bd);
+}
+
+
+void MacroAssembler::Call(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id,
+ Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+ Label start;
+ bind(&start);
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) {
+ SetRecordedAstId(ast_id);
+ rmode = RelocInfo::CODE_TARGET_WITH_ID;
+ }
+ AllowDeferredHandleDereference embedding_raw_address;
+ Call(reinterpret_cast<Address>(code.location()), rmode, cond, rs, rt, bd);
+ ASSERT_EQ(CallSize(code, rmode, ast_id, cond, rs, rt, bd),
+ SizeOfCodeGeneratedSince(&start));
+}
+
+
+void MacroAssembler::Ret(Condition cond,
+ Register rs,
+ const Operand& rt,
+ BranchDelaySlot bd) {
+ Jump(ra, cond, rs, rt, bd);
+}
+
+
+void MacroAssembler::J(Label* L, BranchDelaySlot bdslot) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+
+ uint32_t imm28;
+ imm28 = jump_address(L);
+ imm28 &= kImm28Mask;
+ { BlockGrowBufferScope block_buf_growth(this);
+ // Buffer growth (and relocation) must be blocked for internal references
+ // until associated instructions are emitted and available to be patched.
+ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE);
+ j(imm28);
+ }
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::Jr(Label* L, BranchDelaySlot bdslot) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+
+ uint32_t imm32;
+ imm32 = jump_address(L);
+ { BlockGrowBufferScope block_buf_growth(this);
+ // Buffer growth (and relocation) must be blocked for internal references
+ // until associated instructions are emitted and available to be patched.
+ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE);
+ lui(at, (imm32 & kHiMask) >> kLuiShift);
+ ori(at, at, (imm32 & kImm16Mask));
+ }
+ jr(at);
+
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::Jalr(Label* L, BranchDelaySlot bdslot) {
+ BlockTrampolinePoolScope block_trampoline_pool(this);
+
+ uint32_t imm32;
+ imm32 = jump_address(L);
+ { BlockGrowBufferScope block_buf_growth(this);
+ // Buffer growth (and relocation) must be blocked for internal references
+ // until associated instructions are emitted and available to be patched.
+ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE);
+ lui(at, (imm32 & kHiMask) >> kLuiShift);
+ ori(at, at, (imm32 & kImm16Mask));
+ }
+ jalr(at);
+
+ // Emit a nop in the branch delay slot if required.
+ if (bdslot == PROTECT)
+ nop();
+}
+
+
+void MacroAssembler::DropAndRet(int drop) {
+ Ret(USE_DELAY_SLOT);
+ addiu(sp, sp, drop * kPointerSize);
+}
+
+void MacroAssembler::DropAndRet(int drop,
+ Condition cond,
+ Register r1,
+ const Operand& r2) {
+ // Both Drop and Ret need to be conditional.
+ Label skip;
+ if (cond != cc_always) {
+ Branch(&skip, NegateCondition(cond), r1, r2);
+ }
+
+ Drop(drop);
+ Ret();
+
+ if (cond != cc_always) {
+ bind(&skip);
+ }
+}
+
+
+void MacroAssembler::Drop(int count,
+ Condition cond,
+ Register reg,
+ const Operand& op) {
+ if (count <= 0) {
+ return;
+ }
+
+ Label skip;
+
+ if (cond != al) {
+ Branch(&skip, NegateCondition(cond), reg, op);
+ }
+
+ addiu(sp, sp, count * kPointerSize);
+
+ if (cond != al) {
+ bind(&skip);
+ }
+}
+
+
+
+void MacroAssembler::Swap(Register reg1,
+ Register reg2,
+ Register scratch) {
+ if (scratch.is(no_reg)) {
+ Xor(reg1, reg1, Operand(reg2));
+ Xor(reg2, reg2, Operand(reg1));
+ Xor(reg1, reg1, Operand(reg2));
+ } else {
+ mov(scratch, reg1);
+ mov(reg1, reg2);
+ mov(reg2, scratch);
+ }
+}
+
+
+void MacroAssembler::Call(Label* target) {
+ BranchAndLink(target);
+}
+
+
+void MacroAssembler::Push(Handle<Object> handle) {
+ li(at, Operand(handle));
+ push(at);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+void MacroAssembler::DebugBreak() {
+ PrepareCEntryArgs(0);
+ PrepareCEntryFunction(ExternalReference(Runtime::kDebugBreak, isolate()));
+ CEntryStub ces(1);
+ ASSERT(AllowThisStubCall(&ces));
+ Call(ces.GetCode(isolate()), RelocInfo::DEBUG_BREAK);
+}
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+// ---------------------------------------------------------------------------
+// Exception handling.
+
+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::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()), CONSTANT_SIZE);
+ 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 {
+ 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));
+}
+
+
+void MacroAssembler::PopTryHandler() {
+ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
+ pop(a1);
+ Addu(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
+ li(at, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
+ sw(a1, MemOperand(at));
+}
+
+
+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);
+ 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 stack pointer to the top of the top handler.
+ li(a3, Operand(ExternalReference(Isolate::kHandlerAddress,
+ isolate())));
+ lw(sp, MemOperand(a3));
+
+ // Restore the next handler.
+ pop(a2);
+ sw(a2, MemOperand(a3));
+
+ // 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.
+ // (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp
+ // or cp.
+ Label done;
+ Branch(&done, eq, cp, Operand(zero_reg));
+ sw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ bind(&done);
+
+ JumpToHandlerEntry();
+}
+
+
+void MacroAssembler::ThrowUncatchable(Register value) {
+ // Adjust this code if not the case.
+ STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
+ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
+ 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 (!value.is(v0)) {
+ mov(v0, value);
+ }
+ // Drop the stack pointer to the top of 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 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));
+
+ // 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());
+
+ JumpToHandlerEntry();
+}
+
+
+void MacroAssembler::Allocate(int object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT(object_size <= Page::kMaxNonCodeHeapObjectSize);
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ li(result, 0x7091);
+ li(scratch1, 0x7191);
+ li(scratch2, 0x7291);
+ }
+ jmp(gc_required);
+ return;
+ }
+
+ ASSERT(!result.is(scratch1));
+ ASSERT(!result.is(scratch2));
+ ASSERT(!scratch1.is(scratch2));
+ ASSERT(!scratch1.is(t9));
+ ASSERT(!scratch2.is(t9));
+ ASSERT(!result.is(t9));
+
+ // Make object size into bytes.
+ if ((flags & SIZE_IN_WORDS) != 0) {
+ object_size *= kPointerSize;
+ }
+ ASSERT_EQ(0, object_size & kObjectAlignmentMask);
+
+ // Check relative positions of allocation top and limit addresses.
+ // ARM adds additional checks to make sure the ldm instruction can be
+ // used. On MIPS we don't have ldm so we don't need additional checks either.
+ ExternalReference allocation_top =
+ AllocationUtils::GetAllocationTopReference(isolate(), flags);
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+
+ intptr_t top =
+ reinterpret_cast<intptr_t>(allocation_top.address());
+ intptr_t limit =
+ reinterpret_cast<intptr_t>(allocation_limit.address());
+ ASSERT((limit - top) == kPointerSize);
+
+ // Set up allocation top address and object size registers.
+ Register topaddr = scratch1;
+ li(topaddr, Operand(allocation_top));
+
+ // This code stores a temporary value in t9.
+ if ((flags & RESULT_CONTAINS_TOP) == 0) {
+ // Load allocation top into result and allocation limit into t9.
+ lw(result, MemOperand(topaddr));
+ lw(t9, MemOperand(topaddr, kPointerSize));
+ } else {
+ if (emit_debug_code()) {
+ // Assert that result actually contains top on entry. t9 is used
+ // immediately below so this use of t9 does not cause difference with
+ // respect to register content between debug and release mode.
+ lw(t9, MemOperand(topaddr));
+ Check(eq, kUnexpectedAllocationTop, result, Operand(t9));
+ }
+ // Load allocation limit into t9. Result already contains allocation top.
+ lw(t9, MemOperand(topaddr, limit - top));
+ }
+
+ if ((flags & DOUBLE_ALIGNMENT) != 0) {
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0);
+ ASSERT(kPointerAlignment * 2 == kDoubleAlignment);
+ And(scratch2, result, Operand(kDoubleAlignmentMask));
+ Label aligned;
+ Branch(&aligned, eq, scratch2, Operand(zero_reg));
+ li(scratch2, Operand(isolate()->factory()->one_pointer_filler_map()));
+ sw(scratch2, MemOperand(result));
+ Addu(result, result, Operand(kDoubleSize / 2));
+ bind(&aligned);
+ }
+
+ // Calculate new top and bail out if new space is exhausted. Use result
+ // to calculate the new top.
+ Addu(scratch2, result, Operand(object_size));
+ Branch(gc_required, Ugreater, scratch2, Operand(t9));
+ sw(scratch2, MemOperand(topaddr));
+
+ // Tag object if requested.
+ if ((flags & TAG_OBJECT) != 0) {
+ Addu(result, result, Operand(kHeapObjectTag));
+ }
+}
+
+
+void MacroAssembler::Allocate(Register object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags) {
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ li(result, 0x7091);
+ li(scratch1, 0x7191);
+ li(scratch2, 0x7291);
+ }
+ jmp(gc_required);
+ return;
+ }
+
+ 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.
+ // ARM adds additional checks to make sure the ldm instruction can be
+ // used. On MIPS we don't have ldm so we don't need additional checks either.
+ ExternalReference allocation_top =
+ AllocationUtils::GetAllocationTopReference(isolate(), flags);
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+ intptr_t top =
+ reinterpret_cast<intptr_t>(allocation_top.address());
+ intptr_t limit =
+ reinterpret_cast<intptr_t>(allocation_limit.address());
+ ASSERT((limit - top) == kPointerSize);
+
+ // Set up allocation top address and object size registers.
+ Register topaddr = scratch1;
+ li(topaddr, Operand(allocation_top));
+
+ // This code stores a temporary value in t9.
+ if ((flags & RESULT_CONTAINS_TOP) == 0) {
+ // Load allocation top into result and allocation limit into t9.
+ lw(result, MemOperand(topaddr));
+ lw(t9, MemOperand(topaddr, kPointerSize));
+ } else {
+ if (emit_debug_code()) {
+ // Assert that result actually contains top on entry. t9 is used
+ // immediately below so this use of t9 does not cause difference with
+ // respect to register content between debug and release mode.
+ lw(t9, MemOperand(topaddr));
+ Check(eq, kUnexpectedAllocationTop, result, Operand(t9));
+ }
+ // Load allocation limit into t9. Result already contains allocation top.
+ lw(t9, MemOperand(topaddr, limit - top));
+ }
+
+ if ((flags & DOUBLE_ALIGNMENT) != 0) {
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0);
+ ASSERT(kPointerAlignment * 2 == kDoubleAlignment);
+ And(scratch2, result, Operand(kDoubleAlignmentMask));
+ Label aligned;
+ Branch(&aligned, eq, scratch2, Operand(zero_reg));
+ li(scratch2, Operand(isolate()->factory()->one_pointer_filler_map()));
+ sw(scratch2, MemOperand(result));
+ Addu(result, result, Operand(kDoubleSize / 2));
+ bind(&aligned);
+ }
+
+ // Calculate new top and bail out if new space is exhausted. Use result
+ // to calculate the new top. Object size may be in words so a shift is
+ // required to get the number of bytes.
+ if ((flags & SIZE_IN_WORDS) != 0) {
+ sll(scratch2, object_size, kPointerSizeLog2);
+ Addu(scratch2, result, scratch2);
+ } else {
+ Addu(scratch2, result, Operand(object_size));
+ }
+ Branch(gc_required, Ugreater, scratch2, Operand(t9));
+
+ // Update allocation top. result temporarily holds the new top.
+ if (emit_debug_code()) {
+ And(t9, scratch2, Operand(kObjectAlignmentMask));
+ Check(eq, kUnalignedAllocationInNewSpace, t9, Operand(zero_reg));
+ }
+ sw(scratch2, MemOperand(topaddr));
+
+ // Tag object if requested.
+ if ((flags & TAG_OBJECT) != 0) {
+ Addu(result, result, Operand(kHeapObjectTag));
+ }
+}
+
+
+void MacroAssembler::UndoAllocationInNewSpace(Register object,
+ Register scratch) {
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+
+ // Make sure the object has no tag before resetting top.
+ And(object, object, Operand(~kHeapObjectTagMask));
+#ifdef DEBUG
+ // Check that the object un-allocated is below the current top.
+ li(scratch, Operand(new_space_allocation_top));
+ lw(scratch, MemOperand(scratch));
+ Check(less, kUndoAllocationOfNonAllocatedMemory,
+ object, Operand(scratch));
+#endif
+ // Write the address of the object to un-allocate as the current top.
+ li(scratch, Operand(new_space_allocation_top));
+ sw(object, MemOperand(scratch));
+}
+
+
+void MacroAssembler::AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ sll(scratch1, length, 1); // Length in bytes, not chars.
+ addiu(scratch1, scratch1,
+ kObjectAlignmentMask + SeqTwoByteString::kHeaderSize);
+ And(scratch1, scratch1, Operand(~kObjectAlignmentMask));
+
+ // Allocate two-byte string in new space.
+ Allocate(scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ InitializeNewString(result,
+ length,
+ Heap::kStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string
+ // while observing object alignment.
+ ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ ASSERT(kCharSize == 1);
+ addiu(scratch1, length, kObjectAlignmentMask + SeqOneByteString::kHeaderSize);
+ And(scratch1, scratch1, Operand(~kObjectAlignmentMask));
+
+ // Allocate ASCII string in new space.
+ Allocate(scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ InitializeNewString(result,
+ length,
+ Heap::kAsciiStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateTwoByteConsString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Allocate(ConsString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+ InitializeNewString(result,
+ length,
+ Heap::kConsStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateAsciiConsString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Label allocate_new_space, install_map;
+ AllocationFlags flags = TAG_OBJECT;
+
+ ExternalReference high_promotion_mode = ExternalReference::
+ new_space_high_promotion_mode_active_address(isolate());
+ li(scratch1, Operand(high_promotion_mode));
+ lw(scratch1, MemOperand(scratch1, 0));
+ Branch(&allocate_new_space, eq, scratch1, Operand(zero_reg));
+
+ Allocate(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ static_cast<AllocationFlags>(flags | PRETENURE_OLD_POINTER_SPACE));
+
+ jmp(&install_map);
+
+ bind(&allocate_new_space);
+ Allocate(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ flags);
+
+ bind(&install_map);
+
+ InitializeNewString(result,
+ length,
+ Heap::kConsAsciiStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateTwoByteSlicedString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ InitializeNewString(result,
+ length,
+ Heap::kSlicedStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::AllocateAsciiSlicedString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ InitializeNewString(result,
+ length,
+ Heap::kSlicedAsciiStringMapRootIndex,
+ scratch1,
+ scratch2);
+}
+
+
+void MacroAssembler::JumpIfNotUniqueName(Register reg,
+ Label* not_unique_name) {
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ Label succeed;
+ And(at, reg, Operand(kIsNotStringMask | kIsNotInternalizedMask));
+ Branch(&succeed, eq, at, Operand(zero_reg));
+ Branch(not_unique_name, ne, reg, Operand(SYMBOL_TYPE));
+
+ bind(&succeed);
+}
+
+
+// Allocates a heap number or jumps to the label if the young space is full and
+// a scavenge is needed.
+void MacroAssembler::AllocateHeapNumber(Register result,
+ Register scratch1,
+ Register scratch2,
+ Register heap_number_map,
+ Label* need_gc,
+ TaggingMode tagging_mode) {
+ // Allocate an object in the heap for the heap number and tag it as a heap
+ // object.
+ Allocate(HeapNumber::kSize, result, scratch1, scratch2, need_gc,
+ tagging_mode == TAG_RESULT ? TAG_OBJECT : NO_ALLOCATION_FLAGS);
+
+ // Store heap number map in the allocated object.
+ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+ if (tagging_mode == TAG_RESULT) {
+ sw(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset));
+ } else {
+ sw(heap_number_map, MemOperand(result, HeapObject::kMapOffset));
+ }
+}
+
+
+void MacroAssembler::AllocateHeapNumberWithValue(Register result,
+ FPURegister value,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ LoadRoot(t8, Heap::kHeapNumberMapRootIndex);
+ AllocateHeapNumber(result, scratch1, scratch2, t8, gc_required);
+ sdc1(value, FieldMemOperand(result, HeapNumber::kValueOffset));
+}
+
+
+// Copies a fixed number of fields of heap objects from src to dst.
+void MacroAssembler::CopyFields(Register dst,
+ Register src,
+ RegList temps,
+ int field_count) {
+ ASSERT((temps & dst.bit()) == 0);
+ ASSERT((temps & src.bit()) == 0);
+ // Primitive implementation using only one temporary register.
+
+ Register tmp = no_reg;
+ // Find a temp register in temps list.
+ for (int i = 0; i < kNumRegisters; i++) {
+ if ((temps & (1 << i)) != 0) {
+ tmp.code_ = i;
+ break;
+ }
+ }
+ ASSERT(!tmp.is(no_reg));
+
+ for (int i = 0; i < field_count; i++) {
+ lw(tmp, FieldMemOperand(src, i * kPointerSize));
+ sw(tmp, FieldMemOperand(dst, i * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::CopyBytes(Register src,
+ Register dst,
+ Register length,
+ Register scratch) {
+ Label align_loop, align_loop_1, word_loop, byte_loop, byte_loop_1, done;
+
+ // Align src before copying in word size chunks.
+ bind(&align_loop);
+ Branch(&done, eq, length, Operand(zero_reg));
+ bind(&align_loop_1);
+ And(scratch, src, kPointerSize - 1);
+ Branch(&word_loop, eq, scratch, Operand(zero_reg));
+ lbu(scratch, MemOperand(src));
+ Addu(src, src, 1);
+ sb(scratch, MemOperand(dst));
+ Addu(dst, dst, 1);
+ Subu(length, length, Operand(1));
+ Branch(&byte_loop_1, ne, length, Operand(zero_reg));
+
+ // Copy bytes in word size chunks.
+ bind(&word_loop);
+ if (emit_debug_code()) {
+ And(scratch, src, kPointerSize - 1);
+ Assert(eq, kExpectingAlignmentForCopyBytes,
+ scratch, Operand(zero_reg));
+ }
+ Branch(&byte_loop, lt, length, Operand(kPointerSize));
+ lw(scratch, MemOperand(src));
+ Addu(src, src, kPointerSize);
+
+ // TODO(kalmard) check if this can be optimized to use sw in most cases.
+ // Can't use unaligned access - copy byte by byte.
+ sb(scratch, MemOperand(dst, 0));
+ srl(scratch, scratch, 8);
+ sb(scratch, MemOperand(dst, 1));
+ srl(scratch, scratch, 8);
+ sb(scratch, MemOperand(dst, 2));
+ srl(scratch, scratch, 8);
+ sb(scratch, MemOperand(dst, 3));
+ Addu(dst, dst, 4);
+
+ Subu(length, length, Operand(kPointerSize));
+ Branch(&word_loop);
+
+ // Copy the last bytes if any left.
+ bind(&byte_loop);
+ Branch(&done, eq, length, Operand(zero_reg));
+ bind(&byte_loop_1);
+ lbu(scratch, MemOperand(src));
+ Addu(src, src, 1);
+ sb(scratch, MemOperand(dst));
+ Addu(dst, dst, 1);
+ Subu(length, length, Operand(1));
+ Branch(&byte_loop_1, ne, length, Operand(zero_reg));
+ bind(&done);
+}
+
+
+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_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ STATIC_ASSERT(FAST_ELEMENTS == 2);
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset));
+ Branch(fail, hi, scratch,
+ Operand(Map::kMaximumBitField2FastHoleyElementValue));
+}
+
+
+void MacroAssembler::CheckFastObjectElements(Register map,
+ Register scratch,
+ Label* fail) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ STATIC_ASSERT(FAST_ELEMENTS == 2);
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset));
+ Branch(fail, ls, scratch,
+ Operand(Map::kMaximumBitField2FastHoleySmiElementValue));
+ Branch(fail, hi, scratch,
+ Operand(Map::kMaximumBitField2FastHoleyElementValue));
+}
+
+
+void MacroAssembler::CheckFastSmiElements(Register map,
+ Register scratch,
+ Label* fail) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset));
+ Branch(fail, hi, scratch,
+ Operand(Map::kMaximumBitField2FastHoleySmiElementValue));
+}
+
+
+void MacroAssembler::StoreNumberToDoubleElements(Register value_reg,
+ Register key_reg,
+ Register elements_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* fail,
+ int elements_offset) {
+ 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,
+ Heap::kHeapNumberMapRootIndex,
+ 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 - elements_offset));
+ uint32_t offset = FixedDoubleArray::kHeaderSize - elements_offset +
+ 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 -
+ elements_offset));
+ sll(scratch2, key_reg, kDoubleSizeLog2 - kSmiTagSize);
+ Addu(scratch1, scratch1, scratch2);
+ // scratch1 is now effective address of the double element
+
+ FloatingPointHelper::Destination destination;
+ destination = FloatingPointHelper::kFPURegisters;
+
+ Register untagged_value = elements_reg;
+ SmiUntag(untagged_value, value_reg);
+ FloatingPointHelper::ConvertIntToDouble(this,
+ untagged_value,
+ destination,
+ f0,
+ mantissa_reg,
+ exponent_reg,
+ scratch4,
+ f2);
+ if (destination == FloatingPointHelper::kFPURegisters) {
+ 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) {
+ lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
+ CompareMapAndBranch(scratch, map, early_success, cond, branch_to);
+}
+
+
+void MacroAssembler::CompareMapAndBranch(Register obj_map,
+ Handle<Map> map,
+ Label* early_success,
+ Condition cond,
+ Label* branch_to) {
+ Branch(branch_to, cond, obj_map, Operand(map));
+}
+
+
+void MacroAssembler::CheckMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Label* fail,
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, fail);
+ }
+ Label success;
+ CompareMapAndBranch(obj, scratch, map, &success, ne, fail);
+ bind(&success);
+}
+
+
+void MacroAssembler::DispatchMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type) {
+ Label fail;
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, &fail);
+ }
+ lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
+ Jump(success, RelocInfo::CODE_TARGET, eq, scratch, Operand(map));
+ bind(&fail);
+}
+
+
+void MacroAssembler::CheckMap(Register obj,
+ Register scratch,
+ Heap::RootListIndex index,
+ Label* fail,
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, fail);
+ }
+ lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
+ LoadRoot(at, index);
+ Branch(fail, ne, scratch, Operand(at));
+}
+
+
+void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) {
+ if (IsMipsSoftFloatABI) {
+ Move(dst, v0, v1);
+ } else {
+ Move(dst, f0); // Reg f0 is o32 ABI FP return value.
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg) {
+ if (!IsMipsSoftFloatABI) {
+ Move(f12, dreg);
+ } else {
+ Move(a0, a1, dreg);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg1,
+ DoubleRegister dreg2) {
+ if (!IsMipsSoftFloatABI) {
+ if (dreg2.is(f12)) {
+ ASSERT(!dreg1.is(f14));
+ Move(f14, dreg2);
+ Move(f12, dreg1);
+ } else {
+ Move(f12, dreg1);
+ Move(f14, dreg2);
+ }
+ } else {
+ Move(a0, a1, dreg1);
+ Move(a2, a3, dreg2);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg,
+ Register reg) {
+ if (!IsMipsSoftFloatABI) {
+ Move(f12, dreg);
+ Move(a2, reg);
+ } else {
+ Move(a2, reg);
+ Move(a0, a1, dreg);
+ }
+}
+
+
+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 t1 to
+ // follow the calling convention which requires the call type to be
+ // in t1.
+ ASSERT(dst.is(t1));
+ if (call_kind == CALL_AS_FUNCTION) {
+ li(dst, Operand(Smi::FromInt(1)));
+ } else {
+ li(dst, Operand(Smi::FromInt(0)));
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// JavaScript invokes.
+
+void MacroAssembler::InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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,
+ // setup registers according to contract with ArgumentsAdaptorTrampoline:
+ // a0: actual arguments count
+ // a1: function (passed through to callee)
+ // a2: expected arguments count
+ // a3: callee code entry
+
+ // The code below is made a lot easier because the calling code already sets
+ // up actual and expected registers according to the contract if values are
+ // passed in registers.
+ ASSERT(actual.is_immediate() || actual.reg().is(a0));
+ ASSERT(expected.is_immediate() || expected.reg().is(a2));
+ ASSERT((!code_constant.is_null() && code_reg.is(no_reg)) || code_reg.is(a3));
+
+ if (expected.is_immediate()) {
+ ASSERT(actual.is_immediate());
+ if (expected.immediate() == actual.immediate()) {
+ definitely_matches = true;
+ } else {
+ li(a0, Operand(actual.immediate()));
+ const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+ if (expected.immediate() == sentinel) {
+ // Don't worry about adapting arguments for builtins that
+ // don't want that done. Skip adaption code by making it look
+ // like we have a match between expected and actual number of
+ // arguments.
+ definitely_matches = true;
+ } else {
+ *definitely_mismatches = true;
+ li(a2, Operand(expected.immediate()));
+ }
+ }
+ } else if (actual.is_immediate()) {
+ Branch(&regular_invoke, eq, expected.reg(), Operand(actual.immediate()));
+ li(a0, Operand(actual.immediate()));
+ } else {
+ Branch(&regular_invoke, eq, expected.reg(), Operand(actual.reg()));
+ }
+
+ if (!definitely_matches) {
+ if (!code_constant.is_null()) {
+ li(a3, Operand(code_constant));
+ addiu(a3, a3, Code::kHeaderSize - kHeapObjectTag);
+ }
+
+ Handle<Code> adaptor =
+ isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(adaptor));
+ SetCallKind(t1, call_kind);
+ Call(adaptor);
+ call_wrapper.AfterCall();
+ if (!*definitely_mismatches) {
+ Branch(done);
+ }
+ } else {
+ SetCallKind(t1, call_kind);
+ Jump(adaptor, RelocInfo::CODE_TARGET);
+ }
+ bind(&regular_invoke);
+ }
+}
+
+
+void MacroAssembler::InvokeCode(Register code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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,
+ call_wrapper, call_kind);
+ 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);
+ }
+}
+
+
+void MacroAssembler::InvokeCode(Handle<Code> code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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;
+
+ bool definitely_mismatches = false;
+ InvokePrologue(expected, actual, code, no_reg,
+ &done, &definitely_mismatches, flag,
+ NullCallWrapper(), call_kind);
+ 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);
+ }
+}
+
+
+void MacroAssembler::InvokeFunction(Register function,
+ const ParameterCount& actual,
+ 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;
+ Register code_reg = a3;
+
+ lw(code_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+ lw(expected_reg,
+ FieldMemOperand(code_reg,
+ SharedFunctionInfo::kFormalParameterCountOffset));
+ sra(expected_reg, expected_reg, kSmiTagSize);
+ lw(code_reg, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
+
+ ParameterCount expected(expected_reg);
+ InvokeCode(code_reg, expected, actual, flag, call_wrapper, call_kind);
+}
+
+
+void MacroAssembler::InvokeFunction(Handle<JSFunction> function,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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());
+
+ // Get the function and setup the context.
+ LoadHeapObject(a1, function);
+ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+
+ // 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);
+}
+
+
+void MacroAssembler::IsObjectJSObjectType(Register heap_object,
+ Register map,
+ Register scratch,
+ Label* fail) {
+ lw(map, FieldMemOperand(heap_object, HeapObject::kMapOffset));
+ IsInstanceJSObjectType(map, scratch, fail);
+}
+
+
+void MacroAssembler::IsInstanceJSObjectType(Register map,
+ Register scratch,
+ Label* fail) {
+ lbu(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ Branch(fail, lt, scratch, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ Branch(fail, gt, scratch, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+}
+
+
+void MacroAssembler::IsObjectJSStringType(Register object,
+ Register scratch,
+ Label* fail) {
+ ASSERT(kNotStringTag != 0);
+
+ lw(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ lbu(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
+ And(scratch, scratch, Operand(kIsNotStringMask));
+ Branch(fail, ne, scratch, Operand(zero_reg));
+}
+
+
+void MacroAssembler::IsObjectNameType(Register object,
+ Register scratch,
+ Label* fail) {
+ lw(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ lbu(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
+ Branch(fail, hi, scratch, Operand(LAST_NAME_TYPE));
+}
+
+
+// ---------------------------------------------------------------------------
+// Support functions.
+
+
+void MacroAssembler::TryGetFunctionPrototype(Register function,
+ Register result,
+ Register scratch,
+ Label* miss,
+ bool miss_on_bound_function) {
+ // Check that the receiver isn't a smi.
+ JumpIfSmi(function, miss);
+
+ // Check that the function really is a function. Load map into result reg.
+ 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));
+ 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));
+
+ // 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.
+ LoadRoot(t8, Heap::kTheHoleValueRootIndex);
+ Branch(miss, eq, result, Operand(t8));
+
+ // 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));
+ jmp(&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 MacroAssembler::GetObjectType(Register object,
+ Register map,
+ Register type_reg) {
+ lw(map, FieldMemOperand(object, HeapObject::kMapOffset));
+ lbu(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
+}
+
+
+// -----------------------------------------------------------------------------
+// Runtime calls.
+
+void MacroAssembler::CallStub(CodeStub* stub,
+ TypeFeedbackId ast_id,
+ Condition cond,
+ Register r1,
+ const Operand& r2,
+ BranchDelaySlot bd) {
+ ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs.
+ Call(stub->GetCode(isolate()), RelocInfo::CODE_TARGET, ast_id,
+ cond, r1, r2, bd);
+}
+
+
+void MacroAssembler::TailCallStub(CodeStub* stub) {
+ ASSERT(allow_stub_calls_ ||
+ stub->CompilingCallsToThisStubIsGCSafe(isolate()));
+ Jump(stub->GetCode(isolate()), RelocInfo::CODE_TARGET);
+}
+
+
+static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
+ return ref0.address() - ref1.address();
+}
+
+
+void MacroAssembler::CallApiFunctionAndReturn(ExternalReference function,
+ Address function_address,
+ ExternalReference thunk_ref,
+ Register thunk_last_arg,
+ int stack_space,
+ bool returns_handle,
+ int return_value_offset_from_fp) {
+ ExternalReference next_address =
+ ExternalReference::handle_scope_next_address(isolate());
+ const int kNextOffset = 0;
+ const int kLimitOffset = AddressOffset(
+ ExternalReference::handle_scope_limit_address(isolate()),
+ next_address);
+ const int kLevelOffset = AddressOffset(
+ ExternalReference::handle_scope_level_address(isolate()),
+ next_address);
+
+ // Allocate HandleScope in callee-save registers.
+ li(s3, Operand(next_address));
+ lw(s0, MemOperand(s3, kNextOffset));
+ lw(s1, MemOperand(s3, kLimitOffset));
+ lw(s2, MemOperand(s3, kLevelOffset));
+ Addu(s2, s2, Operand(1));
+ sw(s2, MemOperand(s3, kLevelOffset));
+
+ if (FLAG_log_timer_events) {
+ FrameScope frame(this, StackFrame::MANUAL);
+ PushSafepointRegisters();
+ PrepareCallCFunction(1, a0);
+ li(a0, Operand(ExternalReference::isolate_address(isolate())));
+ CallCFunction(ExternalReference::log_enter_external_function(isolate()), 1);
+ PopSafepointRegisters();
+ }
+
+ // The O32 ABI requires us to pass a pointer in a0 where the returned struct
+ // (4 bytes) will be placed. This is also built into the Simulator.
+ // Set up the pointer to the returned value (a0). It was allocated in
+ // EnterExitFrame.
+ if (returns_handle) {
+ addiu(a0, fp, ExitFrameConstants::kStackSpaceOffset);
+ }
+
+ Label profiler_disabled;
+ Label end_profiler_check;
+ bool* is_profiling_flag =
+ isolate()->cpu_profiler()->is_profiling_address();
+ STATIC_ASSERT(sizeof(*is_profiling_flag) == 1);
+ li(t9, reinterpret_cast<int32_t>(is_profiling_flag));
+ lb(t9, MemOperand(t9, 0));
+ beq(t9, zero_reg, &profiler_disabled);
+
+ // Third parameter is the address of the actual getter function.
+ li(thunk_last_arg, reinterpret_cast<int32_t>(function_address));
+ li(t9, Operand(thunk_ref));
+ jmp(&end_profiler_check);
+
+ bind(&profiler_disabled);
+ li(t9, Operand(function));
+
+ bind(&end_profiler_check);
+
+ // Native call returns to the DirectCEntry stub which redirects to the
+ // return address pushed on stack (could have moved after GC).
+ // DirectCEntry stub itself is generated early and never moves.
+ DirectCEntryStub stub;
+ stub.GenerateCall(this, t9);
+
+ if (FLAG_log_timer_events) {
+ FrameScope frame(this, StackFrame::MANUAL);
+ PushSafepointRegisters();
+ PrepareCallCFunction(1, a0);
+ li(a0, Operand(ExternalReference::isolate_address(isolate())));
+ CallCFunction(ExternalReference::log_leave_external_function(isolate()), 1);
+ PopSafepointRegisters();
+ }
+
+ Label promote_scheduled_exception;
+ Label delete_allocated_handles;
+ Label leave_exit_frame;
+ Label return_value_loaded;
+
+ if (returns_handle) {
+ Label load_return_value;
+
+ // As mentioned above, on MIPS a pointer is returned - we need to
+ // dereference it to get the actual return value (which is also a pointer).
+ lw(v0, MemOperand(v0));
+
+ Branch(&load_return_value, eq, v0, Operand(zero_reg));
+ // Dereference returned value.
+ lw(v0, MemOperand(v0));
+ Branch(&return_value_loaded);
+ bind(&load_return_value);
+ }
+ // Load value from ReturnValue.
+ lw(v0, MemOperand(fp, return_value_offset_from_fp*kPointerSize));
+ bind(&return_value_loaded);
+
+ // No more valid handles (the result handle was the last one). Restore
+ // previous handle scope.
+ sw(s0, MemOperand(s3, kNextOffset));
+ if (emit_debug_code()) {
+ lw(a1, MemOperand(s3, kLevelOffset));
+ Check(eq, kUnexpectedLevelAfterReturnFromApiCall, a1, Operand(s2));
+ }
+ Subu(s2, s2, Operand(1));
+ sw(s2, MemOperand(s3, kLevelOffset));
+ lw(at, MemOperand(s3, kLimitOffset));
+ Branch(&delete_allocated_handles, ne, s1, Operand(at));
+
+ // Check if the function scheduled an exception.
+ bind(&leave_exit_frame);
+ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ li(at, Operand(ExternalReference::scheduled_exception_address(isolate())));
+ lw(t1, MemOperand(at));
+ Branch(&promote_scheduled_exception, ne, t0, Operand(t1));
+ li(s0, Operand(stack_space));
+ LeaveExitFrame(false, s0, true);
+
+ bind(&promote_scheduled_exception);
+ TailCallExternalReference(
+ ExternalReference(Runtime::kPromoteScheduledException, isolate()),
+ 0,
+ 1);
+
+ // HandleScope limit has changed. Delete allocated extensions.
+ bind(&delete_allocated_handles);
+ sw(s1, MemOperand(s3, kLimitOffset));
+ mov(s0, v0);
+ mov(a0, v0);
+ PrepareCallCFunction(1, s1);
+ li(a0, Operand(ExternalReference::isolate_address(isolate())));
+ CallCFunction(ExternalReference::delete_handle_scope_extensions(isolate()),
+ 1);
+ mov(v0, s0);
+ jmp(&leave_exit_frame);
+}
+
+
+bool MacroAssembler::AllowThisStubCall(CodeStub* stub) {
+ if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false;
+ return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe(isolate());
+}
+
+
+void MacroAssembler::IllegalOperation(int num_arguments) {
+ if (num_arguments > 0) {
+ addiu(sp, sp, num_arguments * kPointerSize);
+ }
+ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+}
+
+
+void MacroAssembler::IndexFromHash(Register hash,
+ Register index) {
+ // If the hash field contains an array index pick it out. The assert checks
+ // that the constants for the maximum number of digits for an array index
+ // cached in the hash field and the number of bits reserved for it does not
+ // conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
+ // the low kHashShift bits.
+ STATIC_ASSERT(kSmiTag == 0);
+ Ext(hash, hash, String::kHashShift, String::kArrayIndexValueBits);
+ sll(index, hash, kSmiTagSize);
+}
+
+
+void MacroAssembler::ObjectToDoubleFPURegister(Register object,
+ FPURegister result,
+ Register scratch1,
+ Register scratch2,
+ Register heap_number_map,
+ Label* not_number,
+ ObjectToDoubleFlags flags) {
+ Label done;
+ if ((flags & OBJECT_NOT_SMI) == 0) {
+ Label not_smi;
+ JumpIfNotSmi(object, &not_smi);
+ // Remove smi tag and convert to double.
+ sra(scratch1, object, kSmiTagSize);
+ mtc1(scratch1, result);
+ cvt_d_w(result, result);
+ Branch(&done);
+ bind(&not_smi);
+ }
+ // Check for heap number and load double value from it.
+ lw(scratch1, FieldMemOperand(object, HeapObject::kMapOffset));
+ Branch(not_number, ne, scratch1, Operand(heap_number_map));
+
+ if ((flags & AVOID_NANS_AND_INFINITIES) != 0) {
+ // If exponent is all ones the number is either a NaN or +/-Infinity.
+ Register exponent = scratch1;
+ Register mask_reg = scratch2;
+ lw(exponent, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ li(mask_reg, HeapNumber::kExponentMask);
+
+ And(exponent, exponent, mask_reg);
+ Branch(not_number, eq, exponent, Operand(mask_reg));
+ }
+ ldc1(result, FieldMemOperand(object, HeapNumber::kValueOffset));
+ bind(&done);
+}
+
+
+void MacroAssembler::SmiToDoubleFPURegister(Register smi,
+ FPURegister value,
+ Register scratch1) {
+ sra(scratch1, smi, kSmiTagSize);
+ mtc1(scratch1, value);
+ cvt_d_w(value, value);
+}
+
+
+void MacroAssembler::AdduAndCheckForOverflow(Register dst,
+ Register left,
+ Register right,
+ Register overflow_dst,
+ Register scratch) {
+ ASSERT(!dst.is(overflow_dst));
+ ASSERT(!dst.is(scratch));
+ ASSERT(!overflow_dst.is(scratch));
+ ASSERT(!overflow_dst.is(left));
+ ASSERT(!overflow_dst.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.
+ addu(dst, left, right); // Left is overwritten.
+ xor_(scratch, dst, scratch); // Original left.
+ xor_(overflow_dst, dst, right);
+ and_(overflow_dst, overflow_dst, scratch);
+ } else if (dst.is(right)) {
+ mov(scratch, right); // Preserve right.
+ addu(dst, left, right); // Right is overwritten.
+ xor_(scratch, dst, scratch); // Original right.
+ xor_(overflow_dst, dst, left);
+ and_(overflow_dst, overflow_dst, scratch);
+ } else {
+ addu(dst, left, right);
+ xor_(overflow_dst, dst, left);
+ xor_(scratch, dst, right);
+ and_(overflow_dst, scratch, overflow_dst);
+ }
+}
+
+
+void MacroAssembler::SubuAndCheckForOverflow(Register dst,
+ Register left,
+ Register right,
+ Register overflow_dst,
+ Register scratch) {
+ ASSERT(!dst.is(overflow_dst));
+ ASSERT(!dst.is(scratch));
+ ASSERT(!overflow_dst.is(scratch));
+ ASSERT(!overflow_dst.is(left));
+ ASSERT(!overflow_dst.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.
+ xor_(overflow_dst, dst, scratch); // scratch is original left.
+ xor_(scratch, scratch, right); // scratch is original left.
+ and_(overflow_dst, scratch, overflow_dst);
+ } else if (dst.is(right)) {
+ mov(scratch, right); // Preserve right.
+ subu(dst, left, right); // Right is overwritten.
+ xor_(overflow_dst, dst, left);
+ xor_(scratch, left, scratch); // Original right.
+ and_(overflow_dst, scratch, overflow_dst);
+ } else {
+ subu(dst, left, right);
+ xor_(overflow_dst, dst, left);
+ xor_(scratch, left, right);
+ and_(overflow_dst, scratch, overflow_dst);
+ }
+}
+
+
+void MacroAssembler::CallRuntime(const Runtime::Function* f,
+ int num_arguments) {
+ // All parameters are on the stack. v0 has the return value after call.
+
+ // If the expected number of arguments of the runtime function is
+ // constant, we check that the actual number of arguments match the
+ // expectation.
+ if (f->nargs >= 0 && f->nargs != num_arguments) {
+ IllegalOperation(num_arguments);
+ return;
+ }
+
+ // 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.
+ PrepareCEntryArgs(num_arguments);
+ PrepareCEntryFunction(ExternalReference(f, isolate()));
+ CEntryStub stub(1);
+ CallStub(&stub);
+}
+
+
+void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) {
+ const Runtime::Function* function = Runtime::FunctionForId(id);
+ PrepareCEntryArgs(function->nargs);
+ PrepareCEntryFunction(ExternalReference(function, isolate()));
+ CEntryStub stub(1, kSaveFPRegs);
+ CallStub(&stub);
+}
+
+
+void MacroAssembler::CallRuntime(Runtime::FunctionId fid, int num_arguments) {
+ CallRuntime(Runtime::FunctionForId(fid), num_arguments);
+}
+
+
+void MacroAssembler::CallExternalReference(const ExternalReference& ext,
+ int num_arguments,
+ BranchDelaySlot bd) {
+ PrepareCEntryArgs(num_arguments);
+ PrepareCEntryFunction(ext);
+
+ CEntryStub stub(1);
+ CallStub(&stub, TypeFeedbackId::None(), al, zero_reg, Operand(zero_reg), bd);
+}
+
+
+void MacroAssembler::TailCallExternalReference(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.
+ PrepareCEntryArgs(num_arguments);
+ JumpToExternalReference(ext);
+}
+
+
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(ExternalReference(fid, isolate()),
+ num_arguments,
+ result_size);
+}
+
+
+void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
+ BranchDelaySlot bd) {
+ PrepareCEntryFunction(builtin);
+ CEntryStub stub(1);
+ Jump(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ al,
+ zero_reg,
+ Operand(zero_reg),
+ bd);
+}
+
+
+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));
+ SetCallKind(t1, CALL_AS_METHOD);
+ Call(t9);
+ call_wrapper.AfterCall();
+ } else {
+ ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(t1, CALL_AS_METHOD);
+ Jump(t9);
+ }
+}
+
+
+void MacroAssembler::GetBuiltinFunction(Register target,
+ Builtins::JavaScript id) {
+ // Load the builtins object into target register.
+ lw(target, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ lw(target, FieldMemOperand(target, GlobalObject::kBuiltinsOffset));
+ // Load the JavaScript builtin function from the builtins object.
+ lw(target, FieldMemOperand(target,
+ JSBuiltinsObject::OffsetOfFunctionWithId(id)));
+}
+
+
+void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
+ ASSERT(!target.is(a1));
+ GetBuiltinFunction(a1, id);
+ // Load the code entry point from the builtins object.
+ lw(target, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
+}
+
+
+void MacroAssembler::SetCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2) {
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ li(scratch1, Operand(value));
+ li(scratch2, Operand(ExternalReference(counter)));
+ sw(scratch1, MemOperand(scratch2));
+ }
+}
+
+
+void MacroAssembler::IncrementCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ li(scratch2, Operand(ExternalReference(counter)));
+ lw(scratch1, MemOperand(scratch2));
+ Addu(scratch1, scratch1, Operand(value));
+ sw(scratch1, MemOperand(scratch2));
+ }
+}
+
+
+void MacroAssembler::DecrementCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ li(scratch2, Operand(ExternalReference(counter)));
+ lw(scratch1, MemOperand(scratch2));
+ Subu(scratch1, scratch1, Operand(value));
+ sw(scratch1, MemOperand(scratch2));
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Debugging.
+
+void MacroAssembler::Assert(Condition cc, BailoutReason reason,
+ Register rs, Operand rt) {
+ if (emit_debug_code())
+ Check(cc, reason, rs, rt);
+}
+
+
+void MacroAssembler::AssertRegisterIsRoot(Register reg,
+ Heap::RootListIndex index) {
+ if (emit_debug_code()) {
+ LoadRoot(at, index);
+ Check(eq, kRegisterDidNotMatchExpectedRoot, reg, Operand(at));
+ }
+}
+
+
+void MacroAssembler::AssertFastElements(Register elements) {
+ if (emit_debug_code()) {
+ ASSERT(!elements.is(at));
+ Label ok;
+ push(elements);
+ lw(elements, FieldMemOperand(elements, HeapObject::kMapOffset));
+ LoadRoot(at, Heap::kFixedArrayMapRootIndex);
+ Branch(&ok, eq, elements, Operand(at));
+ LoadRoot(at, Heap::kFixedDoubleArrayMapRootIndex);
+ Branch(&ok, eq, elements, Operand(at));
+ LoadRoot(at, Heap::kFixedCOWArrayMapRootIndex);
+ Branch(&ok, eq, elements, Operand(at));
+ Abort(kJSObjectWithFastElementsMapHasSlowElements);
+ bind(&ok);
+ pop(elements);
+ }
+}
+
+
+void MacroAssembler::Check(Condition cc, BailoutReason reason,
+ Register rs, Operand rt) {
+ Label L;
+ Branch(&L, cc, rs, rt);
+ Abort(reason);
+ // Will not return here.
+ bind(&L);
+}
+
+
+void MacroAssembler::Abort(BailoutReason reason) {
+ Label abort_start;
+ bind(&abort_start);
+ // We want to pass the msg string like a smi to avoid GC
+ // problems, however msg is not guaranteed to be aligned
+ // properly. Instead, we pass an aligned pointer that is
+ // a proper v8 smi, but also pass the alignment difference
+ // from the real pointer as a smi.
+ const char* msg = GetBailoutReason(reason);
+ intptr_t p1 = reinterpret_cast<intptr_t>(msg);
+ intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
+ ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
+#ifdef DEBUG
+ if (msg != NULL) {
+ RecordComment("Abort message: ");
+ RecordComment(msg);
+ }
+#endif
+
+ li(a0, Operand(p0));
+ push(a0);
+ li(a0, Operand(Smi::FromInt(p1 - p0)));
+ push(a0);
+ // 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
+ // instructions generated, we insert padding here to keep the size
+ // of the Abort macro constant.
+ // Currently in debug mode with debug_code enabled the number of
+ // generated instructions is 14, so we use this as a maximum value.
+ static const int kExpectedAbortInstructions = 14;
+ int abort_instructions = InstructionsGeneratedSince(&abort_start);
+ ASSERT(abort_instructions <= kExpectedAbortInstructions);
+ while (abort_instructions++ < kExpectedAbortInstructions) {
+ nop();
+ }
+ }
+}
+
+
+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.
+ lw(dst, MemOperand(cp, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ for (int i = 1; i < context_chain_length; i++) {
+ lw(dst, MemOperand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ }
+ } else {
+ // Slot is in the current function context. Move it into the
+ // destination register in case we store into it (the write barrier
+ // cannot be allowed to destroy the context in esi).
+ Move(dst, cp);
+ }
+}
+
+
+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_OBJECT_INDEX)));
+ lw(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset));
+
+ // Check that the function's map is the same as the expected cached map.
+ lw(scratch,
+ MemOperand(scratch,
+ Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX)));
+ size_t offset = expected_kind * kPointerSize +
+ FixedArrayBase::kHeaderSize;
+ lw(at, FieldMemOperand(scratch, offset));
+ Branch(no_map_match, ne, map_in_out, Operand(at));
+
+ // Use the transitioned cached map.
+ offset = transitioned_kind * kPointerSize +
+ FixedArrayBase::kHeaderSize;
+ lw(map_in_out, FieldMemOperand(scratch, offset));
+}
+
+
+void MacroAssembler::LoadInitialArrayMap(
+ Register function_in, Register scratch,
+ Register map_out, bool can_have_holes) {
+ ASSERT(!function_in.is(map_out));
+ Label done;
+ lw(map_out, FieldMemOperand(function_in,
+ JSFunction::kPrototypeOrInitialMapOffset));
+ if (!FLAG_smi_only_arrays) {
+ ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
+ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ kind,
+ map_out,
+ scratch,
+ &done);
+ } else if (can_have_holes) {
+ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_HOLEY_SMI_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_OBJECT_INDEX)));
+ // Load the native context from the global or builtins object.
+ lw(function, FieldMemOperand(function,
+ GlobalObject::kNativeContextOffset));
+ // Load the function from the native context.
+ lw(function, MemOperand(function, Context::SlotOffset(index)));
+}
+
+
+void MacroAssembler::LoadArrayFunction(Register function) {
+ // Load the global or builtins object from the current context.
+ lw(function,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ // Load the global context from the global or builtins object.
+ lw(function,
+ FieldMemOperand(function, GlobalObject::kGlobalContextOffset));
+ // Load the array function from the native context.
+ lw(function,
+ MemOperand(function, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
+}
+
+
+void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
+ Register map,
+ Register scratch) {
+ // Load the initial map. The global functions all have initial maps.
+ lw(map, FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+ if (emit_debug_code()) {
+ Label ok, fail;
+ CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, DO_SMI_CHECK);
+ Branch(&ok);
+ bind(&fail);
+ Abort(kGlobalFunctionsMustHaveInitialMap);
+ bind(&ok);
+ }
+}
+
+
+void MacroAssembler::EnterFrame(StackFrame::Type type) {
+ addiu(sp, sp, -5 * kPointerSize);
+ li(t8, Operand(Smi::FromInt(type)));
+ li(t9, Operand(CodeObject()), CONSTANT_SIZE);
+ sw(ra, MemOperand(sp, 4 * kPointerSize));
+ sw(fp, MemOperand(sp, 3 * kPointerSize));
+ sw(cp, MemOperand(sp, 2 * kPointerSize));
+ sw(t8, MemOperand(sp, 1 * kPointerSize));
+ sw(t9, MemOperand(sp, 0 * kPointerSize));
+ addiu(fp, sp, 3 * kPointerSize);
+}
+
+
+void MacroAssembler::LeaveFrame(StackFrame::Type type) {
+ mov(sp, fp);
+ lw(fp, MemOperand(sp, 0 * kPointerSize));
+ lw(ra, MemOperand(sp, 1 * kPointerSize));
+ addiu(sp, sp, 2 * kPointerSize);
+}
+
+
+void MacroAssembler::EnterExitFrame(bool save_doubles,
+ int stack_space) {
+ // 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);
+
+ // This is how the stack will look:
+ // fp + 2 (==kCallerSPDisplacement) - old stack's end
+ // [fp + 1 (==kCallerPCOffset)] - saved old ra
+ // [fp + 0 (==kCallerFPOffset)] - saved old fp
+ // [fp - 1 (==kSPOffset)] - sp of the called function
+ // [fp - 2 (==kCodeOffset)] - CodeObject
+ // fp - (2 + stack_space + alignment) == sp == [fp - kSPOffset] - top of the
+ // new stack (will contain saved ra)
+
+ // Save registers.
+ addiu(sp, sp, -4 * kPointerSize);
+ sw(ra, MemOperand(sp, 3 * kPointerSize));
+ sw(fp, MemOperand(sp, 2 * kPointerSize));
+ addiu(fp, sp, 2 * kPointerSize); // Set up new frame pointer.
+
+ if (emit_debug_code()) {
+ sw(zero_reg, MemOperand(fp, ExitFrameConstants::kSPOffset));
+ }
+
+ // Accessed from ExitFrame::code_slot.
+ li(t8, Operand(CodeObject()), CONSTANT_SIZE);
+ sw(t8, MemOperand(fp, ExitFrameConstants::kCodeOffset));
+
+ // Save the frame pointer and the context in top.
+ li(t8, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate())));
+ sw(fp, MemOperand(t8));
+ li(t8, Operand(ExternalReference(Isolate::kContextAddress, isolate())));
+ sw(cp, MemOperand(t8));
+
+ const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
+ if (save_doubles) {
+ // The stack must be allign to 0 modulo 8 for stores with sdc1.
+ ASSERT(kDoubleSize == frame_alignment);
+ if (frame_alignment > 0) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ And(sp, sp, Operand(-frame_alignment)); // Align stack.
+ }
+ int space = FPURegister::kMaxNumRegisters * kDoubleSize;
+ Subu(sp, sp, Operand(space));
+ // Remember: we only need to save every 2nd double FPU value.
+ for (int i = 0; i < FPURegister::kMaxNumRegisters; i+=2) {
+ FPURegister reg = FPURegister::from_code(i);
+ sdc1(reg, MemOperand(sp, i * kDoubleSize));
+ }
+ }
+
+ // Reserve place for the return address, stack space and an optional slot
+ // (used by the DirectCEntryStub to hold the return value if a struct is
+ // returned) and align the frame preparing for calling the runtime function.
+ ASSERT(stack_space >= 0);
+ Subu(sp, sp, Operand((stack_space + 2) * kPointerSize));
+ if (frame_alignment > 0) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ And(sp, sp, Operand(-frame_alignment)); // Align stack.
+ }
+
+ // Set the exit frame sp value to point just before the return address
+ // location.
+ addiu(at, sp, kPointerSize);
+ sw(at, MemOperand(fp, ExitFrameConstants::kSPOffset));
+}
+
+
+void MacroAssembler::LeaveExitFrame(bool save_doubles,
+ Register argument_count,
+ bool do_return) {
+ // Optionally restore all double registers.
+ if (save_doubles) {
+ // Remember: we only need to restore every 2nd double FPU value.
+ lw(t8, MemOperand(fp, ExitFrameConstants::kSPOffset));
+ for (int i = 0; i < FPURegister::kMaxNumRegisters; i+=2) {
+ FPURegister reg = FPURegister::from_code(i);
+ ldc1(reg, MemOperand(t8, i * kDoubleSize + kPointerSize));
+ }
+ }
+
+ // Clear top frame.
+ li(t8, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate())));
+ sw(zero_reg, MemOperand(t8));
+
+ // Restore current context from top and clear it in debug mode.
+ li(t8, Operand(ExternalReference(Isolate::kContextAddress, isolate())));
+ lw(cp, MemOperand(t8));
+#ifdef DEBUG
+ sw(a3, MemOperand(t8));
+#endif
+
+ // Pop the arguments, restore registers, and return.
+ mov(sp, fp); // Respect ABI stack constraint.
+ lw(fp, MemOperand(sp, ExitFrameConstants::kCallerFPOffset));
+ lw(ra, MemOperand(sp, ExitFrameConstants::kCallerPCOffset));
+
+ if (argument_count.is_valid()) {
+ sll(t8, argument_count, kPointerSizeLog2);
+ addu(sp, sp, t8);
+ }
+
+ if (do_return) {
+ Ret(USE_DELAY_SLOT);
+ // If returning, the instruction in the delay slot will be the addiu below.
+ }
+ addiu(sp, sp, 8);
+}
+
+
+void MacroAssembler::InitializeNewString(Register string,
+ Register length,
+ Heap::RootListIndex map_index,
+ Register scratch1,
+ Register scratch2) {
+ sll(scratch1, length, kSmiTagSize);
+ LoadRoot(scratch2, map_index);
+ sw(scratch1, FieldMemOperand(string, String::kLengthOffset));
+ li(scratch1, Operand(String::kEmptyHashField));
+ sw(scratch2, FieldMemOperand(string, HeapObject::kMapOffset));
+ sw(scratch1, FieldMemOperand(string, String::kHashFieldOffset));
+}
+
+
+int MacroAssembler::ActivationFrameAlignment() {
+#if V8_HOST_ARCH_MIPS
+ // Running on the real platform. Use the alignment as mandated by the local
+ // environment.
+ // Note: This will break if we ever start generating snapshots on one Mips
+ // platform for another Mips platform with a different alignment.
+ return OS::ActivationFrameAlignment();
+#else // V8_HOST_ARCH_MIPS
+ // If we are using the simulator then we should always align to the expected
+ // alignment. As the simulator is used to generate snapshots we do not know
+ // if the target platform will need alignment, so this is controlled from a
+ // flag.
+ return FLAG_sim_stack_alignment;
+#endif // V8_HOST_ARCH_MIPS
+}
+
+
+void MacroAssembler::AssertStackIsAligned() {
+ if (emit_debug_code()) {
+ const int frame_alignment = ActivationFrameAlignment();
+ const int frame_alignment_mask = frame_alignment - 1;
+
+ if (frame_alignment > kPointerSize) {
+ Label alignment_as_expected;
+ ASSERT(IsPowerOf2(frame_alignment));
+ andi(at, sp, frame_alignment_mask);
+ Branch(&alignment_as_expected, eq, at, Operand(zero_reg));
+ // Don't use Check here, as it will call Runtime_Abort re-entering here.
+ stop("Unexpected stack alignment");
+ bind(&alignment_as_expected);
+ }
+ }
+}
+
+
+void MacroAssembler::JumpIfNotPowerOfTwoOrZero(
+ Register reg,
+ Register scratch,
+ Label* not_power_of_two_or_zero) {
+ Subu(scratch, reg, Operand(1));
+ Branch(USE_DELAY_SLOT, not_power_of_two_or_zero, lt,
+ scratch, Operand(zero_reg));
+ and_(at, scratch, reg); // In the delay slot.
+ Branch(not_power_of_two_or_zero, ne, at, Operand(zero_reg));
+}
+
+
+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);
+ JumpIfNotSmi(at, on_not_both_smi);
+}
+
+
+void MacroAssembler::JumpIfEitherSmi(Register reg1,
+ Register reg2,
+ Label* on_either_smi) {
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT_EQ(1, kSmiTagMask);
+ // Both Smi tags must be 1 (not Smi).
+ and_(at, reg1, reg2);
+ JumpIfSmi(at, on_either_smi);
+}
+
+
+void MacroAssembler::AssertNotSmi(Register object) {
+ if (emit_debug_code()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ andi(at, object, kSmiTagMask);
+ Check(ne, kOperandIsASmi, at, Operand(zero_reg));
+ }
+}
+
+
+void MacroAssembler::AssertSmi(Register object) {
+ if (emit_debug_code()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ andi(at, object, kSmiTagMask);
+ Check(eq, kOperandIsASmi, at, Operand(zero_reg));
+ }
+}
+
+
+void MacroAssembler::AssertString(Register object) {
+ if (emit_debug_code()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ And(t0, object, Operand(kSmiTagMask));
+ Check(ne, kOperandIsASmiAndNotAString, t0, Operand(zero_reg));
+ push(object);
+ lw(object, FieldMemOperand(object, HeapObject::kMapOffset));
+ lbu(object, FieldMemOperand(object, Map::kInstanceTypeOffset));
+ Check(lo, kOperandIsNotAString, object, Operand(FIRST_NONSTRING_TYPE));
+ pop(object);
+ }
+}
+
+
+void MacroAssembler::AssertName(Register object) {
+ if (emit_debug_code()) {
+ STATIC_ASSERT(kSmiTag == 0);
+ And(t0, object, Operand(kSmiTagMask));
+ Check(ne, kOperandIsASmiAndNotAName, t0, Operand(zero_reg));
+ push(object);
+ lw(object, FieldMemOperand(object, HeapObject::kMapOffset));
+ lbu(object, FieldMemOperand(object, Map::kInstanceTypeOffset));
+ Check(le, kOperandIsNotAName, object, Operand(LAST_NAME_TYPE));
+ pop(object);
+ }
+}
+
+
+void MacroAssembler::AssertRootValue(Register src,
+ Heap::RootListIndex root_value_index,
+ BailoutReason reason) {
+ if (emit_debug_code()) {
+ ASSERT(!src.is(at));
+ LoadRoot(at, root_value_index);
+ Check(eq, reason, src, Operand(at));
+ }
+}
+
+
+void MacroAssembler::JumpIfNotHeapNumber(Register object,
+ Register heap_number_map,
+ Register scratch,
+ Label* on_not_heap_number) {
+ lw(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
+ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+ Branch(on_not_heap_number, ne, scratch, Operand(heap_number_map));
+}
+
+
+void MacroAssembler::JumpIfNonSmisNotBothSequentialAsciiStrings(
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* failure) {
+ // Test that both first and second are sequential ASCII strings.
+ // Assume that they are non-smis.
+ lw(scratch1, FieldMemOperand(first, HeapObject::kMapOffset));
+ lw(scratch2, FieldMemOperand(second, HeapObject::kMapOffset));
+ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ lbu(scratch2, FieldMemOperand(scratch2, Map::kInstanceTypeOffset));
+
+ JumpIfBothInstanceTypesAreNotSequentialAscii(scratch1,
+ scratch2,
+ scratch1,
+ scratch2,
+ failure);
+}
+
+
+void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* failure) {
+ // Check that neither is a smi.
+ STATIC_ASSERT(kSmiTag == 0);
+ And(scratch1, first, Operand(second));
+ JumpIfSmi(scratch1, failure);
+ JumpIfNonSmisNotBothSequentialAsciiStrings(first,
+ second,
+ scratch1,
+ scratch2,
+ failure);
+}
+
+
+void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* failure) {
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
+ const int kFlatAsciiStringTag =
+ kStringTag | kOneByteStringTag | kSeqStringTag;
+ ASSERT(kFlatAsciiStringTag <= 0xffff); // Ensure this fits 16-bit immed.
+ andi(scratch1, first, kFlatAsciiStringMask);
+ Branch(failure, ne, scratch1, Operand(kFlatAsciiStringTag));
+ andi(scratch2, second, kFlatAsciiStringMask);
+ Branch(failure, ne, scratch2, Operand(kFlatAsciiStringTag));
+}
+
+
+void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(Register type,
+ Register scratch,
+ Label* failure) {
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
+ const int kFlatAsciiStringTag =
+ kStringTag | kOneByteStringTag | kSeqStringTag;
+ And(scratch, type, Operand(kFlatAsciiStringMask));
+ Branch(failure, ne, scratch, Operand(kFlatAsciiStringTag));
+}
+
+
+static const int kRegisterPassedArguments = 4;
+
+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.
+ // Those four arguments must have reserved argument slots on the stack for
+ // 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 = 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.
+ mov(scratch, sp);
+ Subu(sp, sp, Operand((stack_passed_arguments + 1) * kPointerSize));
+ ASSERT(IsPowerOf2(frame_alignment));
+ And(sp, sp, Operand(-frame_alignment));
+ sw(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
+ } else {
+ Subu(sp, sp, Operand(stack_passed_arguments * kPointerSize));
+ }
+}
+
+
+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) {
+ CallCFunction(function, num_arguments, 0);
+}
+
+
+void MacroAssembler::CallCFunction(Register function,
+ int num_arguments) {
+ CallCFunction(function, num_arguments, 0);
+}
+
+
+void MacroAssembler::CallCFunctionHelper(Register function,
+ 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.
+ // The argument stots are presumed to have been set up by
+ // PrepareCallCFunction. The C function must be called via t9, for mips ABI.
+
+#if V8_HOST_ARCH_MIPS
+ if (emit_debug_code()) {
+ int frame_alignment = OS::ActivationFrameAlignment();
+ int frame_alignment_mask = frame_alignment - 1;
+ if (frame_alignment > kPointerSize) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ Label alignment_as_expected;
+ And(at, sp, Operand(frame_alignment_mask));
+ Branch(&alignment_as_expected, eq, at, Operand(zero_reg));
+ // Don't use Check here, as it will call Runtime_Abort possibly
+ // re-entering here.
+ stop("Unexpected alignment in CallCFunction");
+ bind(&alignment_as_expected);
+ }
+ }
+#endif // V8_HOST_ARCH_MIPS
+
+ // 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(t9)) {
+ mov(t9, function);
+ function = t9;
+ }
+
+ Call(function);
+
+ int stack_passed_arguments = CalculateStackPassedWords(
+ num_reg_arguments, num_double_arguments);
+
+ if (OS::ActivationFrameAlignment() > kPointerSize) {
+ lw(sp, MemOperand(sp, stack_passed_arguments * kPointerSize));
+ } else {
+ Addu(sp, sp, Operand(stack_passed_arguments * sizeof(kPointerSize)));
+ }
+}
+
+
+#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, kTheInstructionToPatchShouldBeALui,
+ 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, kTheInstructionToPatchShouldBeAnOri,
+ 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, kTheInstructionShouldBeALui,
+ 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, kTheInstructionShouldBeAnOri,
+ 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::CheckMapDeprecated(Handle<Map> map,
+ Register scratch,
+ Label* if_deprecated) {
+ if (map->CanBeDeprecated()) {
+ li(scratch, Operand(map));
+ lw(scratch, FieldMemOperand(scratch, Map::kBitField3Offset));
+ And(scratch, scratch, Operand(Smi::FromInt(Map::Deprecated::kMask)));
+ Branch(if_deprecated, ne, 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(kOneByteStringTag == 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, FieldMemOperand(map, Map::kDescriptorsOffset));
+}
+
+
+void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
+ lw(dst, FieldMemOperand(map, Map::kBitField3Offset));
+ DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
+}
+
+
+void MacroAssembler::EnumLength(Register dst, Register map) {
+ STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
+ lw(dst, FieldMemOperand(map, Map::kBitField3Offset));
+ And(dst, dst, Operand(Smi::FromInt(Map::EnumLengthBits::kMask)));
+}
+
+
+void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
+ Register empty_fixed_array_value = t2;
+ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
+ Label next, start;
+ mov(a2, a0);
+
+ // Check if the enum length field is properly initialized, indicating that
+ // there is an enum cache.
+ lw(a1, FieldMemOperand(a2, HeapObject::kMapOffset));
+
+ EnumLength(a3, a1);
+ Branch(call_runtime, eq, a3, Operand(Smi::FromInt(Map::kInvalidEnumCache)));
+
+ jmp(&start);
+
+ bind(&next);
+ lw(a1, FieldMemOperand(a2, HeapObject::kMapOffset));
+
+ // For all objects but the receiver, check that the cache is empty.
+ EnumLength(a3, a1);
+ Branch(call_runtime, ne, a3, Operand(Smi::FromInt(0)));
+
+ bind(&start);
+
+ // Check that there are no elements. Register r2 contains the current JS
+ // object we've reached through the prototype chain.
+ lw(a2, FieldMemOperand(a2, JSObject::kElementsOffset));
+ Branch(call_runtime, ne, a2, Operand(empty_fixed_array_value));
+
+ lw(a2, FieldMemOperand(a1, Map::kPrototypeOffset));
+ Branch(&next, ne, a2, Operand(null_value));
+}
+
+
+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);
+ cvt_w_d(temp_double_reg, input_reg);
+ mfc1(result_reg, temp_double_reg);
+ bind(&done);
+}
+
+
+void MacroAssembler::TestJSArrayForAllocationMemento(
+ Register receiver_reg,
+ Register scratch_reg,
+ Condition cond,
+ Label* allocation_memento_present) {
+ Label no_memento_available;
+ ExternalReference new_space_start =
+ ExternalReference::new_space_start(isolate());
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+ Addu(scratch_reg, receiver_reg,
+ Operand(JSArray::kSize + AllocationMemento::kSize - kHeapObjectTag));
+ Branch(&no_memento_available, lt, scratch_reg, Operand(new_space_start));
+ li(at, Operand(new_space_allocation_top));
+ lw(at, MemOperand(at));
+ Branch(&no_memento_available, gt, scratch_reg, Operand(at));
+ lw(scratch_reg, MemOperand(scratch_reg, -AllocationMemento::kSize));
+ Branch(allocation_memento_present, cond, scratch_reg,
+ Operand(Handle<Map>(isolate()->heap()->allocation_memento_map())));
+ bind(&no_memento_available);
+}
+
+
+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),
+ size_(instructions * Assembler::kInstrSize),
+ masm_(NULL, address, size_ + Assembler::kGap) {
+ // Create a new macro assembler pointing to the address of the code to patch.
+ // The size is adjusted with kGap on order for the assembler to generate size
+ // bytes of instructions without failing with buffer size constraints.
+ ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
+}
+
+
+CodePatcher::~CodePatcher() {
+ // Indicate that code has changed.
+ CPU::FlushICache(address_, size_);
+
+ // Check that the code was patched as expected.
+ ASSERT(masm_.pc_ == address_ + size_);
+ ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
+}
+
+
+void CodePatcher::Emit(Instr instr) {
+ masm()->emit(instr);
+}
+
+
+void CodePatcher::Emit(Address addr) {
+ masm()->emit(reinterpret_cast<Instr>(addr));
+}
+
+
+void CodePatcher::ChangeBranchCondition(Condition cond) {
+ Instr instr = Assembler::instr_at(masm_.pc_);
+ ASSERT(Assembler::IsBranch(instr));
+ uint32_t opcode = Assembler::GetOpcodeField(instr);
+ // Currently only the 'eq' and 'ne' cond values are supported and the simple
+ // branch instructions (with opcode being the branch type).
+ // There are some special cases (see Assembler::IsBranch()) so extending this
+ // would be tricky.
+ ASSERT(opcode == BEQ ||
+ opcode == BNE ||
+ opcode == BLEZ ||
+ opcode == BGTZ ||
+ opcode == BEQL ||
+ opcode == BNEL ||
+ opcode == BLEZL ||
+ opcode == BGTZL);
+ opcode = (cond == eq) ? BEQ : BNE;
+ instr = (instr & ~kOpcodeMask) | opcode;
+ masm_.emit(instr);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/macro-assembler-mips.h b/chromium/v8/src/mips/macro-assembler-mips.h
new file mode 100644
index 00000000000..61a0c3a228f
--- /dev/null
+++ b/chromium/v8/src/mips/macro-assembler-mips.h
@@ -0,0 +1,1582 @@
+// Copyright 2012 the V8 project authors. 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_MACRO_ASSEMBLER_MIPS_H_
+#define V8_MIPS_MACRO_ASSEMBLER_MIPS_H_
+
+#include "assembler.h"
+#include "mips/assembler-mips.h"
+#include "v8globals.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declaration.
+class JumpTarget;
+
+// Reserved Register Usage Summary.
+//
+// Registers t8, t9, and at are reserved for use by the MacroAssembler.
+//
+// The programmer should know that the MacroAssembler may clobber these three,
+// but won't touch other registers except in special cases.
+//
+// Per the MIPS ABI, register t9 must be used for indirect function call
+// via 'jalr t9' or 'jr t9' instructions. This is relied upon by gcc when
+// trying to update gp register for position-independent-code. Whenever
+// MIPS generated code calls C code, it must be via t9 register.
+
+
+// Flags used for AllocateHeapNumber
+enum TaggingMode {
+ // Tag the result.
+ TAG_RESULT,
+ // Don't tag
+ DONT_TAG_RESULT
+};
+
+// Flags used for the ObjectToDoubleFPURegister function.
+enum ObjectToDoubleFlags {
+ // No special flags.
+ NO_OBJECT_TO_DOUBLE_FLAGS = 0,
+ // Object is known to be a non smi.
+ OBJECT_NOT_SMI = 1 << 0,
+ // Don't load NaNs or infinities, branch to the non number case instead.
+ AVOID_NANS_AND_INFINITIES = 1 << 1
+};
+
+// Allow programmer to use Branch Delay Slot of Branches, Jumps, Calls.
+enum BranchDelaySlot {
+ USE_DELAY_SLOT,
+ PROTECT
+};
+
+// Flags used for the li macro-assembler function.
+enum LiFlags {
+ // If the constant value can be represented in just 16 bits, then
+ // optimize the li to use a single instruction, rather than lui/ori pair.
+ OPTIMIZE_SIZE = 0,
+ // Always use 2 instructions (lui/ori pair), even if the constant could
+ // be loaded with just one, so that this value is patchable later.
+ CONSTANT_SIZE = 1
+};
+
+
+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_OBJECT_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:
+ // The isolate parameter can be NULL if the macro assembler should
+ // not use isolate-dependent functionality. In this case, it's the
+ // responsibility of the caller to never invoke such function on the
+ // macro assembler.
+ MacroAssembler(Isolate* isolate, void* buffer, int size);
+
+ // Arguments macros.
+#define COND_TYPED_ARGS Condition cond, Register r1, const Operand& r2
+#define COND_ARGS cond, r1, r2
+
+ // Cases when relocation is not needed.
+#define DECLARE_NORELOC_PROTOTYPE(Name, target_type) \
+ void Name(target_type target, BranchDelaySlot bd = PROTECT); \
+ inline void Name(BranchDelaySlot bd, target_type target) { \
+ Name(target, bd); \
+ } \
+ void Name(target_type target, \
+ COND_TYPED_ARGS, \
+ BranchDelaySlot bd = PROTECT); \
+ inline void Name(BranchDelaySlot bd, \
+ target_type target, \
+ COND_TYPED_ARGS) { \
+ Name(target, COND_ARGS, bd); \
+ }
+
+#define DECLARE_BRANCH_PROTOTYPES(Name) \
+ DECLARE_NORELOC_PROTOTYPE(Name, Label*) \
+ DECLARE_NORELOC_PROTOTYPE(Name, int16_t)
+
+ DECLARE_BRANCH_PROTOTYPES(Branch)
+ DECLARE_BRANCH_PROTOTYPES(BranchAndLink)
+
+#undef DECLARE_BRANCH_PROTOTYPES
+#undef COND_TYPED_ARGS
+#undef COND_ARGS
+
+
+ // Jump, Call, and Ret pseudo instructions implementing inter-working.
+#define COND_ARGS Condition cond = al, Register rs = zero_reg, \
+ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT
+
+ void Jump(Register target, COND_ARGS);
+ 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);
+ static int CallSize(Register target, COND_ARGS);
+ void Call(Register target, 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,
+ TypeFeedbackId ast_id = TypeFeedbackId::None(),
+ COND_ARGS);
+ void Call(Handle<Code> code,
+ RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
+ TypeFeedbackId ast_id = TypeFeedbackId::None(),
+ COND_ARGS);
+ void Ret(COND_ARGS);
+ inline void Ret(BranchDelaySlot bd, Condition cond = al,
+ Register rs = zero_reg, const Operand& rt = Operand(zero_reg)) {
+ Ret(cond, rs, rt, bd);
+ }
+
+ void Branch(Label* L,
+ Condition cond,
+ Register rs,
+ Heap::RootListIndex index,
+ BranchDelaySlot bdslot = PROTECT);
+
+#undef COND_ARGS
+
+ // Emit code to discard a non-negative number of pointer-sized elements
+ // from the stack, clobbering only the sp register.
+ void Drop(int count,
+ Condition cond = cc_always,
+ Register reg = no_reg,
+ const Operand& op = Operand(no_reg));
+
+ // Trivial case of DropAndRet that utilizes the delay slot and only emits
+ // 2 instructions.
+ void DropAndRet(int drop);
+
+ void DropAndRet(int drop,
+ Condition cond,
+ Register reg,
+ const Operand& op);
+
+ // Swap two registers. If the scratch register is omitted then a slightly
+ // less efficient form using xor instead of mov is emitted.
+ void Swap(Register reg1, Register reg2, Register scratch = no_reg);
+
+ void Call(Label* target);
+
+ inline void Move(Register dst, Register src) {
+ if (!dst.is(src)) {
+ mov(dst, src);
+ }
+ }
+
+ inline void Move(FPURegister dst, FPURegister src) {
+ if (!dst.is(src)) {
+ mov_d(dst, src);
+ }
+ }
+
+ inline void Move(Register dst_low, Register dst_high, FPURegister src) {
+ mfc1(dst_low, src);
+ mfc1(dst_high, FPURegister::from_code(src.code() + 1));
+ }
+
+ inline void Move(FPURegister dst, Register src_low, Register src_high) {
+ mtc1(src_low, dst);
+ mtc1(src_high, FPURegister::from_code(dst.code() + 1));
+ }
+
+ // Conditional move.
+ void Move(FPURegister dst, double imm);
+ void Movz(Register rd, Register rs, Register rt);
+ void Movn(Register rd, Register rs, Register rt);
+ void Movt(Register rd, Register rs, uint16_t cc = 0);
+ void Movf(Register rd, Register rs, uint16_t cc = 0);
+
+ void Clz(Register rd, Register rs);
+
+ // Jump unconditionally to given label.
+ // We NEED a nop in the branch delay slot, as it used by v8, for example in
+ // CodeGenerator::ProcessDeferred().
+ // Currently the branch delay slot is filled by the MacroAssembler.
+ // Use rather b(Label) for code generation.
+ void jmp(Label* L) {
+ Branch(L);
+ }
+
+ // Load an object from the root table.
+ void LoadRoot(Register destination,
+ Heap::RootListIndex index);
+ void LoadRoot(Register destination,
+ Heap::RootListIndex index,
+ Condition cond, Register src1, const Operand& src2);
+
+ // Store an object to the root table.
+ void StoreRoot(Register source,
+ Heap::RootListIndex index);
+ void StoreRoot(Register source,
+ Heap::RootListIndex index,
+ Condition cond, Register src1, const Operand& src2);
+
+ void LoadHeapObject(Register dst, Handle<HeapObject> object);
+
+ void LoadObject(Register result, Handle<Object> object) {
+ AllowDeferredHandleDereference heap_object_check;
+ if (object->IsHeapObject()) {
+ LoadHeapObject(result, Handle<HeapObject>::cast(object));
+ } else {
+ li(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);
+
+ void CheckMapDeprecated(Handle<Map> map,
+ Register scratch,
+ Label* if_deprecated);
+
+ // 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);
+ }
+
+ // 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,
+ 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 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);
+
+
+ // ---------------------------------------------------------------------------
+ // Inline caching support.
+
+ // Generate code for checking access rights - used for security checks
+ // on access to global objects across environments. The holder register
+ // is left untouched, whereas both scratch registers are clobbered.
+ void CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss);
+
+ void GetNumberHash(Register reg0, Register scratch);
+
+ void LoadFromNumberDictionary(Label* miss,
+ Register elements,
+ Register key,
+ Register result,
+ Register reg0,
+ Register reg1,
+ Register reg2);
+
+
+ inline void MarkCode(NopMarkerTypes type) {
+ nop(type);
+ }
+
+ // Check if the given instruction is a 'type' marker.
+ // 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) {
+ ASSERT((FIRST_IC_MARKER <= type) && (type < LAST_CODE_MARKER));
+ return IsNop(instr, type);
+ }
+
+
+ static inline int GetCodeMarker(Instr instr) {
+ uint32_t opcode = ((instr & kOpcodeMask));
+ uint32_t rt = ((instr & kRtFieldMask) >> kRtShift);
+ uint32_t rs = ((instr & kRsFieldMask) >> kRsShift);
+ uint32_t sa = ((instr & kSaFieldMask) >> kSaShift);
+
+ // Return <n> if we have a sll zero_reg, zero_reg, n
+ // else return -1.
+ bool sllzz = (opcode == SLL &&
+ rt == static_cast<uint32_t>(ToNumber(zero_reg)) &&
+ rs == static_cast<uint32_t>(ToNumber(zero_reg)));
+ int type =
+ (sllzz && FIRST_IC_MARKER <= sa && sa < LAST_CODE_MARKER) ? sa : -1;
+ ASSERT((type == -1) ||
+ ((FIRST_IC_MARKER <= type) && (type < LAST_CODE_MARKER)));
+ return type;
+ }
+
+
+
+ // ---------------------------------------------------------------------------
+ // Allocation support.
+
+ // Allocate an object in new space or old pointer space. The object_size is
+ // specified either in bytes or in words if the allocation flag SIZE_IN_WORDS
+ // is passed. If the space is exhausted control continues at the gc_required
+ // label. The allocated object is returned in result. If the flag
+ // tag_allocated_object is true the result is tagged as as a heap object.
+ // All registers are clobbered also when control continues at the gc_required
+ // label.
+ void Allocate(int object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ void Allocate(Register object_size,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ // Undo allocation in new space. The object passed and objects allocated after
+ // it will no longer be allocated. The caller must make sure that no pointers
+ // are left to the object(s) no longer allocated as they would be invalid when
+ // allocation is undone.
+ void UndoAllocationInNewSpace(Register object, Register scratch);
+
+
+ void AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateTwoByteConsString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiConsString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateTwoByteSlicedString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiSlicedString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // Allocates a heap number or jumps to the gc_required label if the young
+ // space is full and a scavenge is needed. All registers are clobbered also
+ // when control continues at the gc_required label.
+ void AllocateHeapNumber(Register result,
+ Register scratch1,
+ Register scratch2,
+ Register heap_number_map,
+ Label* gc_required,
+ TaggingMode tagging_mode = TAG_RESULT);
+ void AllocateHeapNumberWithValue(Register result,
+ FPURegister value,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // ---------------------------------------------------------------------------
+ // Instruction macros.
+
+#define DEFINE_INSTRUCTION(instr) \
+ void instr(Register rd, Register rs, const Operand& rt); \
+ void instr(Register rd, Register rs, Register rt) { \
+ instr(rd, rs, Operand(rt)); \
+ } \
+ void instr(Register rs, Register rt, int32_t j) { \
+ instr(rs, rt, Operand(j)); \
+ }
+
+#define DEFINE_INSTRUCTION2(instr) \
+ void instr(Register rs, const Operand& rt); \
+ void instr(Register rs, Register rt) { \
+ instr(rs, Operand(rt)); \
+ } \
+ void instr(Register rs, int32_t j) { \
+ instr(rs, Operand(j)); \
+ }
+
+ DEFINE_INSTRUCTION(Addu);
+ DEFINE_INSTRUCTION(Subu);
+ DEFINE_INSTRUCTION(Mul);
+ DEFINE_INSTRUCTION2(Mult);
+ DEFINE_INSTRUCTION2(Multu);
+ DEFINE_INSTRUCTION2(Div);
+ DEFINE_INSTRUCTION2(Divu);
+
+ DEFINE_INSTRUCTION(And);
+ DEFINE_INSTRUCTION(Or);
+ DEFINE_INSTRUCTION(Xor);
+ DEFINE_INSTRUCTION(Nor);
+ DEFINE_INSTRUCTION2(Neg);
+
+ DEFINE_INSTRUCTION(Slt);
+ DEFINE_INSTRUCTION(Sltu);
+
+ // MIPS32 R2 instruction macro.
+ DEFINE_INSTRUCTION(Ror);
+
+#undef DEFINE_INSTRUCTION
+#undef DEFINE_INSTRUCTION2
+
+
+ // ---------------------------------------------------------------------------
+ // Pseudo-instructions.
+
+ void mov(Register rd, Register rt) { or_(rd, rt, zero_reg); }
+
+ // Load int32 in the rd register.
+ void li(Register rd, Operand j, LiFlags mode = OPTIMIZE_SIZE);
+ inline void li(Register rd, int32_t j, LiFlags mode = OPTIMIZE_SIZE) {
+ li(rd, Operand(j), mode);
+ }
+ inline void li(Register dst, Handle<Object> value,
+ LiFlags mode = OPTIMIZE_SIZE) {
+ li(dst, Operand(value), mode);
+ }
+
+ // Push multiple registers on the stack.
+ // Registers are saved in numerical order, with higher numbered registers
+ // saved in higher memory addresses.
+ void MultiPush(RegList regs);
+ void MultiPushReversed(RegList regs);
+
+ void MultiPushFPU(RegList regs);
+ void MultiPushReversedFPU(RegList regs);
+
+ void push(Register src) {
+ Addu(sp, sp, Operand(-kPointerSize));
+ sw(src, MemOperand(sp, 0));
+ }
+ void Push(Register src) { push(src); }
+
+ // Push a handle.
+ void Push(Handle<Object> handle);
+ void Push(Smi* smi) { Push(Handle<Smi>(smi, isolate())); }
+
+ // Push two registers. Pushes leftmost register first (to highest address).
+ void Push(Register src1, Register src2) {
+ Subu(sp, sp, Operand(2 * kPointerSize));
+ sw(src1, MemOperand(sp, 1 * kPointerSize));
+ sw(src2, MemOperand(sp, 0 * kPointerSize));
+ }
+
+ // Push three registers. Pushes leftmost register first (to highest address).
+ void Push(Register src1, Register src2, Register src3) {
+ Subu(sp, sp, Operand(3 * kPointerSize));
+ sw(src1, MemOperand(sp, 2 * kPointerSize));
+ sw(src2, MemOperand(sp, 1 * kPointerSize));
+ sw(src3, MemOperand(sp, 0 * kPointerSize));
+ }
+
+ // Push four registers. Pushes leftmost register first (to highest address).
+ void Push(Register src1, Register src2, Register src3, Register src4) {
+ Subu(sp, sp, Operand(4 * kPointerSize));
+ sw(src1, MemOperand(sp, 3 * kPointerSize));
+ sw(src2, MemOperand(sp, 2 * kPointerSize));
+ sw(src3, MemOperand(sp, 1 * kPointerSize));
+ sw(src4, MemOperand(sp, 0 * kPointerSize));
+ }
+
+ void Push(Register src, Condition cond, Register tst1, Register tst2) {
+ // Since we don't have conditional execution we use a Branch.
+ Branch(3, cond, tst1, Operand(tst2));
+ Subu(sp, sp, Operand(kPointerSize));
+ sw(src, MemOperand(sp, 0));
+ }
+
+ // Pops multiple values from the stack and load them in the
+ // registers specified in regs. Pop order is the opposite as in MultiPush.
+ void MultiPop(RegList regs);
+ void MultiPopReversed(RegList regs);
+
+ void MultiPopFPU(RegList regs);
+ void MultiPopReversedFPU(RegList regs);
+
+ void pop(Register dst) {
+ lw(dst, MemOperand(sp, 0));
+ Addu(sp, sp, Operand(kPointerSize));
+ }
+ void Pop(Register dst) { pop(dst); }
+
+ // Pop two registers. Pops rightmost register first (from lower address).
+ void Pop(Register src1, Register src2) {
+ ASSERT(!src1.is(src2));
+ lw(src2, MemOperand(sp, 0 * kPointerSize));
+ lw(src1, MemOperand(sp, 1 * kPointerSize));
+ 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));
+ }
+
+ // Push and pop the registers that can hold pointers, as defined by the
+ // RegList constant kSafepointSavedRegisters.
+ void PushSafepointRegisters();
+ void PopSafepointRegisters();
+ void PushSafepointRegistersAndDoubles();
+ void PopSafepointRegistersAndDoubles();
+ // Store value in register src in the safepoint stack slot for
+ // register dst.
+ void StoreToSafepointRegisterSlot(Register src, Register dst);
+ void StoreToSafepointRegistersAndDoublesSlot(Register src, Register dst);
+ // Load the value of the src register from its safepoint stack slot
+ // 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);
+
+ // Convert double to unsigned word.
+ void Trunc_uw_d(FPURegister fd, FPURegister fs, FPURegister scratch);
+ void Trunc_uw_d(FPURegister fd, Register rs, FPURegister scratch);
+
+ void Trunc_w_d(FPURegister fd, FPURegister fs);
+ void Round_w_d(FPURegister fd, FPURegister fs);
+ void Floor_w_d(FPURegister fd, FPURegister fs);
+ void Ceil_w_d(FPURegister fd, FPURegister fs);
+ // 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
+ // scratch2.
+ void ConvertToInt32(Register source,
+ Register dest,
+ Register scratch,
+ Register scratch2,
+ FPURegister double_scratch,
+ Label *not_int32);
+
+ // Truncates a double using a specific rounding mode, and writes the value
+ // to the result register.
+ // The except_flag will contain any exceptions caused by the instruction.
+ // If check_inexact is kDontCheckForInexactConversion, then the inexact
+ // exception is masked.
+ void EmitFPUTruncate(FPURoundingMode rounding_mode,
+ Register result,
+ DoubleRegister double_input,
+ Register scratch,
+ DoubleRegister double_scratch,
+ 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.
+ // Expects the double value loaded in input_high and input_low.
+ // Exits with the answer in 'result'.
+ // Note that this code does not work for values in the 32bit range!
+ void EmitOutOfInt32RangeTruncate(Register result,
+ Register input_high,
+ Register input_low,
+ Register scratch);
+
+ // Performs a truncating conversion of a floating point number as used by
+ // the JS bitwise operations. See ECMA-262 9.5: ToInt32.
+ // Exits with 'result' holding the answer and all other registers clobbered.
+ void EmitECMATruncate(Register result,
+ FPURegister double_input,
+ FPURegister single_scratch,
+ Register scratch,
+ Register scratch2,
+ Register scratch3);
+
+ // Enter exit frame.
+ // argc - argument count to be dropped by LeaveExitFrame.
+ // save_doubles - saves FPU registers on stack, currently disabled.
+ // stack_space - extra stack space.
+ void EnterExitFrame(bool save_doubles,
+ int stack_space = 0);
+
+ // Leave the current exit frame.
+ void LeaveExitFrame(bool save_doubles,
+ Register arg_count,
+ bool do_return = false);
+
+ // Get the actual activation frame alignment for target environment.
+ static int ActivationFrameAlignment();
+
+ // Make sure the stack is aligned. Only emits code in debug mode.
+ void AssertStackIsAligned();
+
+ void LoadContext(Register dst, int context_chain_length);
+
+ // Conditionally load the cached Array transitioned map of type
+ // transitioned_kind from the native context if the map in register
+ // map_in_out is the cached Array map in the native 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,
+ bool can_have_holes);
+
+ void LoadGlobalFunction(int index, Register function);
+ void LoadArrayFunction(Register function);
+
+ // Load the initial map from the global function. The registers
+ // function and map can be the same, function is then overwritten.
+ void LoadGlobalFunctionInitialMap(Register function,
+ Register map,
+ Register scratch);
+
+ void InitializeRootRegister() {
+ ExternalReference roots_array_start =
+ ExternalReference::roots_array_start(isolate());
+ li(kRootRegister, Operand(roots_array_start));
+ }
+
+ // -------------------------------------------------------------------------
+ // JavaScript invokes.
+
+ // 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);
+
+ // 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);
+
+ void InvokeCode(Handle<Code> code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ RelocInfo::Mode rmode,
+ InvokeFlag flag,
+ CallKind call_kind);
+
+ // Invoke the JavaScript function in the given register. Changes the
+ // current context to the context in the function before invoking.
+ void InvokeFunction(Register function,
+ const ParameterCount& actual,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ void InvokeFunction(Handle<JSFunction> function,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+
+ void IsObjectJSObjectType(Register heap_object,
+ Register map,
+ Register scratch,
+ Label* fail);
+
+ void IsInstanceJSObjectType(Register map,
+ Register scratch,
+ Label* fail);
+
+ void IsObjectJSStringType(Register object,
+ Register scratch,
+ Label* fail);
+
+ void IsObjectNameType(Register object,
+ Register scratch,
+ Label* fail);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // -------------------------------------------------------------------------
+ // Debugger Support.
+
+ void DebugBreak();
+#endif
+
+
+ // -------------------------------------------------------------------------
+ // Exception handling.
+
+ // Push a new try handler and link 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.
+ // Must preserve the result register.
+ void PopTryHandler();
+
+ // Passes thrown value to the handler of top of the try handler chain.
+ void Throw(Register value);
+
+ // Propagates an uncatchable exception to the top of the current JS stack's
+ // handler chain.
+ void ThrowUncatchable(Register value);
+
+ // Copies a fixed number of fields of heap objects from src to dst.
+ void CopyFields(Register dst, Register src, RegList temps, int field_count);
+
+ // Copies a number of bytes from src to dst. All registers are clobbered. On
+ // exit src and dst will point to the place just after where the last byte was
+ // read or written and length will be zero.
+ void CopyBytes(Register src,
+ Register dst,
+ 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.
+
+ // Try to get function prototype of a function and puts the value in
+ // the result register. Checks that the function really is a
+ // function and jumps to the miss label if the fast checks fail. The
+ // function register will be untouched; the other registers may be
+ // clobbered.
+ void TryGetFunctionPrototype(Register function,
+ Register result,
+ Register scratch,
+ Label* miss,
+ bool miss_on_bound_function = false);
+
+ void GetObjectType(Register function,
+ Register map,
+ Register type_reg);
+
+ // Check if a map for a JSObject indicates that the object has fast elements.
+ // Jump to the specified label if it does not.
+ void CheckFastElements(Register map,
+ Register scratch,
+ Label* fail);
+
+ // 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 CheckFastSmiElements(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,
+ // All regs below here overwritten.
+ Register elements_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* fail,
+ int elements_offset = 0);
+
+ // 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);
+
+ // As above, but the map of the object is already loaded into the register
+ // which is preserved by the code generated.
+ void CompareMapAndBranch(Register obj_map,
+ Handle<Map> map,
+ Label* early_success,
+ Condition cond,
+ Label* branch_to);
+
+ // 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);
+
+
+ void CheckMap(Register obj,
+ Register scratch,
+ Heap::RootListIndex index,
+ Label* fail,
+ SmiCheckType smi_check_type);
+
+ // 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
+ // known to be a heap object)
+ void DispatchMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type);
+
+ // Generates code for reporting that an illegal operation has
+ // 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.
+ // index - holds the overwritten index on exit.
+ void IndexFromHash(Register hash, Register index);
+
+ // Get the number of least significant bits from a register.
+ void GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits);
+ void GetLeastBitsFromInt32(Register dst, Register src, int mun_least_bits);
+
+ // Load the value of a number object into a FPU double register. If the
+ // object is not a number a jump to the label not_number is performed
+ // and the FPU double register is unchanged.
+ void ObjectToDoubleFPURegister(
+ Register object,
+ FPURegister value,
+ Register scratch1,
+ Register scratch2,
+ Register heap_number_map,
+ Label* not_number,
+ ObjectToDoubleFlags flags = NO_OBJECT_TO_DOUBLE_FLAGS);
+
+ // Load the value of a smi object into a FPU double register. The register
+ // scratch1 can be the same register as smi in which case smi will hold the
+ // untagged value afterwards.
+ void SmiToDoubleFPURegister(Register smi,
+ FPURegister value,
+ Register scratch1);
+
+ // -------------------------------------------------------------------------
+ // Overflow handling functions.
+ // Usage: first call the appropriate arithmetic function, then call one of the
+ // jump functions with the overflow_dst register as the second parameter.
+
+ void AdduAndCheckForOverflow(Register dst,
+ Register left,
+ Register right,
+ Register overflow_dst,
+ Register scratch = at);
+
+ void SubuAndCheckForOverflow(Register dst,
+ Register left,
+ Register right,
+ Register overflow_dst,
+ Register scratch = at);
+
+ void BranchOnOverflow(Label* label,
+ Register overflow_check,
+ BranchDelaySlot bd = PROTECT) {
+ Branch(label, lt, overflow_check, Operand(zero_reg), bd);
+ }
+
+ void BranchOnNoOverflow(Label* label,
+ Register overflow_check,
+ BranchDelaySlot bd = PROTECT) {
+ Branch(label, ge, overflow_check, Operand(zero_reg), bd);
+ }
+
+ void RetOnOverflow(Register overflow_check, BranchDelaySlot bd = PROTECT) {
+ Ret(lt, overflow_check, Operand(zero_reg), bd);
+ }
+
+ void RetOnNoOverflow(Register overflow_check, BranchDelaySlot bd = PROTECT) {
+ Ret(ge, overflow_check, Operand(zero_reg), bd);
+ }
+
+ // -------------------------------------------------------------------------
+ // Runtime calls.
+
+ // See comments at the beginning of CEntryStub::Generate.
+ inline void PrepareCEntryArgs(int num_args) {
+ li(s0, num_args);
+ li(s1, (num_args - 1) * kPointerSize);
+ }
+
+ inline void PrepareCEntryFunction(const ExternalReference& ref) {
+ li(s2, Operand(ref));
+ }
+
+ // Call a code stub.
+ void CallStub(CodeStub* stub,
+ TypeFeedbackId ast_id = TypeFeedbackId::None(),
+ Condition cond = cc_always,
+ Register r1 = zero_reg,
+ const Operand& r2 = Operand(zero_reg),
+ BranchDelaySlot bd = PROTECT);
+
+ // Tail call a code stub (jump).
+ void TailCallStub(CodeStub* stub);
+
+ void CallJSExitStub(CodeStub* stub);
+
+ // Call a runtime routine.
+ void CallRuntime(const Runtime::Function* f, int num_arguments);
+ void CallRuntimeSaveDoubles(Runtime::FunctionId id);
+
+ // Convenience function: Same as above, but takes the fid instead.
+ void CallRuntime(Runtime::FunctionId fid, int num_arguments);
+
+ // Convenience function: call an external reference.
+ void CallExternalReference(const ExternalReference& ext,
+ int num_arguments,
+ BranchDelaySlot bd = PROTECT);
+
+ // Tail call of a runtime routine (jump).
+ // Like JumpToExternalReference, but also takes care of passing the number
+ // of parameters.
+ void TailCallExternalReference(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
+ // stack, after the argument-slots using helper: CFunctionArgumentOperand().
+ // The argument count assumes all arguments are word sized.
+ // Some compilers/platforms require the stack to be aligned when calling
+ // C++ code.
+ // Needs a scratch register to do some arithmetic. This register will be
+ // trashed.
+ 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:
+ // sw(t0, CFunctionArgumentOperand(5));
+
+ // Calls a C function and cleans up the space for arguments allocated
+ // by PrepareCallCFunction. The called function is not allowed to trigger a
+ // garbage collection, since that might move the code and invalidate the
+ // return address (unless this is somehow accounted for by the called
+ // function).
+ void CallCFunction(ExternalReference function, 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
+ // whether soft or hard floating point ABI is used. These functions
+ // abstract parameter passing for the three different ways we call
+ // C functions from generated code.
+ void SetCallCDoubleArguments(DoubleRegister dreg);
+ 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. 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,
+ Address function_address,
+ ExternalReference thunk_ref,
+ Register thunk_last_arg,
+ int stack_space,
+ bool returns_handle,
+ int return_value_offset_from_fp);
+
+ // Jump to the builtin routine.
+ void JumpToExternalReference(const ExternalReference& builtin,
+ BranchDelaySlot bd = PROTECT);
+
+ // Invoke specified builtin JavaScript function. Adds an entry to
+ // the unresolved list if the name does not resolve.
+ void InvokeBuiltin(Builtins::JavaScript id,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper = NullCallWrapper());
+
+ // Store the code object for the given builtin in the target register and
+ // setup the function in a1.
+ void GetBuiltinEntry(Register target, Builtins::JavaScript id);
+
+ // Store the function for the given builtin in the target register.
+ void GetBuiltinFunction(Register target, Builtins::JavaScript id);
+
+ struct Unresolved {
+ int pc;
+ uint32_t flags; // See Bootstrapper::FixupFlags decoders/encoders.
+ const char* name;
+ };
+
+ Handle<Object> CodeObject() {
+ ASSERT(!code_object_.is_null());
+ return code_object_;
+ }
+
+ // -------------------------------------------------------------------------
+ // StatsCounter support.
+
+ void SetCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2);
+ void IncrementCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2);
+ void DecrementCounter(StatsCounter* counter, int value,
+ Register scratch1, Register scratch2);
+
+
+ // -------------------------------------------------------------------------
+ // Debugging.
+
+ // Calls Abort(msg) if the condition cc is not satisfied.
+ // Use --debug_code to enable.
+ void Assert(Condition cc, BailoutReason reason, Register rs, Operand rt);
+ void AssertRegisterIsRoot(Register reg, Heap::RootListIndex index);
+ void AssertFastElements(Register elements);
+
+ // Like Assert(), but always enabled.
+ void Check(Condition cc, BailoutReason reason, Register rs, Operand rt);
+
+ // Print a message to stdout and abort execution.
+ void Abort(BailoutReason msg);
+
+ // Verify restrictions about code generated in stubs.
+ void set_generating_stub(bool value) { generating_stub_ = value; }
+ 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.
+
+ // Check whether the value of reg is a power of two and not zero. If not
+ // control continues at the label not_power_of_two. If reg is a power of two
+ // the register scratch contains the value of (reg - 1) when control falls
+ // through.
+ void JumpIfNotPowerOfTwoOrZero(Register reg,
+ Register scratch,
+ Label* not_power_of_two_or_zero);
+
+ // -------------------------------------------------------------------------
+ // Smi utilities.
+
+ 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);
+ }
+
+ void SmiUntag(Register reg) {
+ sra(reg, reg, kSmiTagSize);
+ }
+
+ void SmiUntag(Register dst, Register src) {
+ 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.
+ void JumpIfSmi(Register value,
+ Label* smi_label,
+ Register scratch = at,
+ BranchDelaySlot bd = PROTECT);
+
+ // Jump if the register contains a non-smi.
+ 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);
+ // Jump if either of the registers contain a smi.
+ void JumpIfEitherSmi(Register reg1, Register reg2, Label* on_either_smi);
+
+ // Abort execution if argument is a smi, enabled via --debug-code.
+ void AssertNotSmi(Register object);
+ void AssertSmi(Register object);
+
+ // Abort execution if argument is not a string, enabled via --debug-code.
+ void AssertString(Register object);
+
+ // Abort execution if argument is not a name, enabled via --debug-code.
+ void AssertName(Register object);
+
+ // Abort execution if argument is not the root value with the given index,
+ // enabled via --debug-code.
+ void AssertRootValue(Register src,
+ Heap::RootListIndex root_value_index,
+ BailoutReason reason);
+
+ // ---------------------------------------------------------------------------
+ // HeapNumber utilities.
+
+ void JumpIfNotHeapNumber(Register object,
+ Register heap_number_map,
+ Register scratch,
+ Label* on_not_heap_number);
+
+ // -------------------------------------------------------------------------
+ // String utilities.
+
+ // Checks if both instance types are sequential ASCII strings and jumps to
+ // label if either is not.
+ void JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first_object_instance_type,
+ Register second_object_instance_type,
+ Register scratch1,
+ Register scratch2,
+ Label* failure);
+
+ // Check if instance type is sequential ASCII string and jump to label if
+ // it is not.
+ void JumpIfInstanceTypeIsNotSequentialAscii(Register type,
+ Register scratch,
+ Label* failure);
+
+ void JumpIfNotUniqueName(Register reg, Label* not_unique_name);
+
+ // Test that both first and second are sequential ASCII strings.
+ // Assume that they are non-smis.
+ void JumpIfNonSmisNotBothSequentialAsciiStrings(Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* failure);
+
+ // Test that both first and second are sequential ASCII strings.
+ // Check that they are non-smis.
+ void JumpIfNotBothSequentialAsciiStrings(Register first,
+ Register second,
+ Register scratch1,
+ 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);
+ void EnumLength(Register dst, Register map);
+ void NumberOfOwnDescriptors(Register dst, Register map);
+
+ template<typename Field>
+ void DecodeField(Register reg) {
+ static const int shift = Field::kShift;
+ static const int mask = (Field::kMask >> shift) << kSmiTagSize;
+ srl(reg, reg, shift);
+ And(reg, reg, Operand(mask));
+ }
+
+ // 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);
+
+ // Expects object in a0 and returns map with validated enum cache
+ // in a0. Assumes that any other register can be used as a scratch.
+ void CheckEnumCache(Register null_value, Label* call_runtime);
+
+ // AllocationMemento support. Arrays may have an associated
+ // AllocationMemento object that can be checked for in order to pretransition
+ // to another type.
+ // On entry, receiver_reg should point to the array object.
+ // scratch_reg gets clobbered.
+ // If allocation info is present, jump to allocation_info_present
+ void TestJSArrayForAllocationMemento(Register receiver_reg,
+ Register scratch_reg,
+ Condition cond,
+ Label* allocation_memento_present);
+
+ private:
+ void CallCFunctionHelper(Register function,
+ 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,
+ const Operand& rt,
+ BranchDelaySlot bdslot = PROTECT);
+ void BranchShort(Label* L, BranchDelaySlot bdslot = PROTECT);
+ void BranchShort(Label* L, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot = PROTECT);
+ void BranchAndLinkShort(int16_t offset, BranchDelaySlot bdslot = PROTECT);
+ void BranchAndLinkShort(int16_t offset, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot = PROTECT);
+ void BranchAndLinkShort(Label* L, BranchDelaySlot bdslot = PROTECT);
+ void BranchAndLinkShort(Label* L, Condition cond, Register rs,
+ const Operand& rt,
+ BranchDelaySlot bdslot = PROTECT);
+ void J(Label* L, BranchDelaySlot bdslot);
+ void Jr(Label* L, BranchDelaySlot bdslot);
+ void Jalr(Label* L, BranchDelaySlot bdslot);
+
+ // Helper functions for generating invokes.
+ void InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ Handle<Code> code_constant,
+ Register code_reg,
+ Label* done,
+ bool* definitely_mismatches,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ // Get the code for the given builtin. Returns if able to resolve
+ // the function in the 'resolved' flag.
+ Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved);
+
+ 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 generating_stub_;
+ bool allow_stub_calls_;
+ bool has_frame_;
+ // This handle will be patched with the code object on installation.
+ Handle<Object> code_object_;
+
+ // Needs access to SafepointRegisterStackIndex for compiled frame
+ // traversal.
+ friend class StandardFrame;
+};
+
+
+// The code patcher is used to patch (typically) small parts of code e.g. for
+// debugging and other types of instrumentation. When using the code patcher
+// the exact number of bytes specified must be emitted. It is not legal to emit
+// relocation information. If any of these constraints are violated it causes
+// an assertion to fail.
+class CodePatcher {
+ public:
+ CodePatcher(byte* address, int instructions);
+ virtual ~CodePatcher();
+
+ // Macro assembler to emit code.
+ MacroAssembler* masm() { return &masm_; }
+
+ // Emit an instruction directly.
+ void Emit(Instr instr);
+
+ // Emit an address directly.
+ void Emit(Address addr);
+
+ // Change the condition part of an instruction leaving the rest of the current
+ // instruction unchanged.
+ void ChangeBranchCondition(Condition cond);
+
+ private:
+ byte* address_; // The address of the code being patched.
+ int size_; // Number of bytes of the expected patch size.
+ MacroAssembler masm_; // Macro assembler used to generate the code.
+};
+
+
+
+#ifdef GENERATED_CODE_COVERAGE
+#define CODE_COVERAGE_STRINGIFY(x) #x
+#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
+#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
+#define ACCESS_MASM(masm) masm->stop(__FILE_LINE__); masm->
+#else
+#define ACCESS_MASM(masm) masm->
+#endif
+
+} } // namespace v8::internal
+
+#endif // V8_MIPS_MACRO_ASSEMBLER_MIPS_H_
diff --git a/chromium/v8/src/mips/regexp-macro-assembler-mips.cc b/chromium/v8/src/mips/regexp-macro-assembler-mips.cc
new file mode 100644
index 00000000000..7b67a7b47f9
--- /dev/null
+++ b/chromium/v8/src/mips/regexp-macro-assembler-mips.cc
@@ -0,0 +1,1339 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "unicode.h"
+#include "log.h"
+#include "code-stubs.h"
+#include "regexp-stack.h"
+#include "macro-assembler.h"
+#include "regexp-macro-assembler.h"
+#include "mips/regexp-macro-assembler-mips.h"
+
+namespace v8 {
+namespace internal {
+
+#ifndef V8_INTERPRETED_REGEXP
+/*
+ * This assembler uses the following register assignment convention
+ * - t7 : Temporarily stores the index of capture start after a matching pass
+ * for a global regexp.
+ * - t1 : Pointer to current code object (Code*) including heap object tag.
+ * - t2 : Current position in input, as negative offset from end of string.
+ * Please notice that this is the byte offset, not the character offset!
+ * - t3 : Currently loaded character. Must be loaded using
+ * LoadCurrentCharacter before using any of the dispatch methods.
+ * - t4 : Points to tip of backtrack stack
+ * - t5 : Unused.
+ * - t6 : End of input (points to byte after last character in input).
+ * - fp : Frame pointer. Used to access arguments, local variables and
+ * RegExp registers.
+ * - sp : Points to tip of C stack.
+ *
+ * The remaining registers are free for computations.
+ * Each call to a public method should retain this convention.
+ *
+ * The stack will have the following structure:
+ *
+ * - fp[64] Isolate* isolate (address of the current isolate)
+ * - fp[60] direct_call (if 1, direct call from JavaScript code,
+ * if 0, call through the runtime system).
+ * - fp[56] stack_area_base (High end of the memory area to use as
+ * backtracking stack).
+ * - fp[52] capture array size (may fit multiple sets of matches)
+ * - fp[48] int* capture_array (int[num_saved_registers_], for output).
+ * - fp[44] secondary link/return address used by native call.
+ * --- sp when called ---
+ * - fp[40] return address (lr).
+ * - fp[36] old frame pointer (r11).
+ * - fp[0..32] backup of registers s0..s7.
+ * --- frame pointer ----
+ * - fp[-4] end of input (address of end of string).
+ * - fp[-8] start of input (address of first character in string).
+ * - fp[-12] start index (character index of start).
+ * - fp[-16] void* input_string (location of a handle containing the string).
+ * - fp[-20] success counter (only for global regexps to count matches).
+ * - fp[-24] Offset of location before start of input (effectively character
+ * position -1). Used to initialize capture registers to a
+ * non-position.
+ * - fp[-28] At start (if 1, we are starting at the start of the
+ * string, otherwise 0)
+ * - fp[-32] register 0 (Only positions must be stored in the first
+ * - register 1 num_saved_registers_ registers)
+ * - ...
+ * - register num_registers-1
+ * --- sp ---
+ *
+ * The first num_saved_registers_ registers are initialized to point to
+ * "character -1" in the string (i.e., char_size() bytes before the first
+ * character of the string). The remaining registers start out as garbage.
+ *
+ * The data up to the return address must be placed there by the calling
+ * code and the remaining arguments are passed in registers, e.g. by calling the
+ * code entry as cast to a function with the signature:
+ * int (*match)(String* input_string,
+ * int start_index,
+ * Address start,
+ * Address end,
+ * Address secondary_return_address, // Only used by native call.
+ * int* capture_output_array,
+ * byte* stack_area_base,
+ * bool direct_call = false)
+ * The call is performed by NativeRegExpMacroAssembler::Execute()
+ * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro
+ * in mips/simulator-mips.h.
+ * When calling as a non-direct call (i.e., from C++ code), the return address
+ * area is overwritten with the ra register by the RegExp code. When doing a
+ * direct call from generated code, the return address is placed there by
+ * the calling code, as in a normal exit frame.
+ */
+
+#define __ ACCESS_MASM(masm_)
+
+RegExpMacroAssemblerMIPS::RegExpMacroAssemblerMIPS(
+ Mode mode,
+ int registers_to_save,
+ Zone* zone)
+ : NativeRegExpMacroAssembler(zone),
+ masm_(new MacroAssembler(zone->isolate(), NULL, kRegExpCodeSize)),
+ mode_(mode),
+ num_registers_(registers_to_save),
+ num_saved_registers_(registers_to_save),
+ entry_label_(),
+ start_label_(),
+ success_label_(),
+ backtrack_label_(),
+ exit_label_(),
+ internal_failure_label_() {
+ ASSERT_EQ(0, registers_to_save % 2);
+ __ jmp(&entry_label_); // We'll write the entry code later.
+ // If the code gets too big or corrupted, an internal exception will be
+ // raised, and we will exit right away.
+ __ bind(&internal_failure_label_);
+ __ li(v0, Operand(FAILURE));
+ __ Ret();
+ __ bind(&start_label_); // And then continue from here.
+}
+
+
+RegExpMacroAssemblerMIPS::~RegExpMacroAssemblerMIPS() {
+ delete masm_;
+ // Unuse labels in case we throw away the assembler without calling GetCode.
+ entry_label_.Unuse();
+ start_label_.Unuse();
+ success_label_.Unuse();
+ backtrack_label_.Unuse();
+ exit_label_.Unuse();
+ check_preempt_label_.Unuse();
+ stack_overflow_label_.Unuse();
+ internal_failure_label_.Unuse();
+}
+
+
+int RegExpMacroAssemblerMIPS::stack_limit_slack() {
+ return RegExpStack::kStackLimitSlack;
+}
+
+
+void RegExpMacroAssemblerMIPS::AdvanceCurrentPosition(int by) {
+ if (by != 0) {
+ __ Addu(current_input_offset(),
+ current_input_offset(), Operand(by * char_size()));
+ }
+}
+
+
+void RegExpMacroAssemblerMIPS::AdvanceRegister(int reg, int by) {
+ ASSERT(reg >= 0);
+ ASSERT(reg < num_registers_);
+ if (by != 0) {
+ __ lw(a0, register_location(reg));
+ __ Addu(a0, a0, Operand(by));
+ __ sw(a0, register_location(reg));
+ }
+}
+
+
+void RegExpMacroAssemblerMIPS::Backtrack() {
+ CheckPreemption();
+ // Pop Code* offset from backtrack stack, add Code* and jump to location.
+ Pop(a0);
+ __ Addu(a0, a0, code_pointer());
+ __ Jump(a0);
+}
+
+
+void RegExpMacroAssemblerMIPS::Bind(Label* label) {
+ __ bind(label);
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckCharacter(uint32_t c, Label* on_equal) {
+ BranchOrBacktrack(on_equal, eq, current_character(), Operand(c));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckCharacterGT(uc16 limit, Label* on_greater) {
+ BranchOrBacktrack(on_greater, gt, current_character(), Operand(limit));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckAtStart(Label* on_at_start) {
+ Label not_at_start;
+ // Did we start the match at the start of the string at all?
+ __ lw(a0, MemOperand(frame_pointer(), kStartIndex));
+ BranchOrBacktrack(&not_at_start, ne, a0, Operand(zero_reg));
+
+ // If we did, are we still at the start of the input?
+ __ lw(a1, MemOperand(frame_pointer(), kInputStart));
+ __ Addu(a0, end_of_input_address(), Operand(current_input_offset()));
+ BranchOrBacktrack(on_at_start, eq, a0, Operand(a1));
+ __ bind(&not_at_start);
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckNotAtStart(Label* on_not_at_start) {
+ // Did we start the match at the start of the string at all?
+ __ lw(a0, MemOperand(frame_pointer(), kStartIndex));
+ BranchOrBacktrack(on_not_at_start, ne, a0, Operand(zero_reg));
+ // If we did, are we still at the start of the input?
+ __ lw(a1, MemOperand(frame_pointer(), kInputStart));
+ __ Addu(a0, end_of_input_address(), Operand(current_input_offset()));
+ BranchOrBacktrack(on_not_at_start, ne, a0, Operand(a1));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckCharacterLT(uc16 limit, Label* on_less) {
+ BranchOrBacktrack(on_less, lt, current_character(), Operand(limit));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckGreedyLoop(Label* on_equal) {
+ Label backtrack_non_equal;
+ __ lw(a0, MemOperand(backtrack_stackpointer(), 0));
+ __ Branch(&backtrack_non_equal, ne, current_input_offset(), Operand(a0));
+ __ Addu(backtrack_stackpointer(),
+ backtrack_stackpointer(),
+ Operand(kPointerSize));
+ __ bind(&backtrack_non_equal);
+ BranchOrBacktrack(on_equal, eq, current_input_offset(), Operand(a0));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
+ int start_reg,
+ Label* on_no_match) {
+ Label fallthrough;
+ __ lw(a0, register_location(start_reg)); // Index of start of capture.
+ __ lw(a1, register_location(start_reg + 1)); // Index of end of capture.
+ __ Subu(a1, a1, a0); // Length of capture.
+
+ // If length is zero, either the capture is empty or it is not participating.
+ // In either case succeed immediately.
+ __ Branch(&fallthrough, eq, a1, Operand(zero_reg));
+
+ __ Addu(t5, a1, current_input_offset());
+ // Check that there are enough characters left in the input.
+ BranchOrBacktrack(on_no_match, gt, t5, Operand(zero_reg));
+
+ if (mode_ == ASCII) {
+ Label success;
+ Label fail;
+ Label loop_check;
+
+ // a0 - offset of start of capture.
+ // a1 - length of capture.
+ __ Addu(a0, a0, Operand(end_of_input_address()));
+ __ Addu(a2, end_of_input_address(), Operand(current_input_offset()));
+ __ Addu(a1, a0, Operand(a1));
+
+ // a0 - Address of start of capture.
+ // a1 - Address of end of capture.
+ // a2 - Address of current input position.
+
+ Label loop;
+ __ bind(&loop);
+ __ lbu(a3, MemOperand(a0, 0));
+ __ addiu(a0, a0, char_size());
+ __ lbu(t0, MemOperand(a2, 0));
+ __ addiu(a2, a2, char_size());
+
+ __ Branch(&loop_check, eq, t0, Operand(a3));
+
+ // Mismatch, try case-insensitive match (converting letters to lower-case).
+ __ Or(a3, a3, Operand(0x20)); // Convert capture character to lower-case.
+ __ Or(t0, t0, Operand(0x20)); // Also convert input character.
+ __ Branch(&fail, ne, t0, Operand(a3));
+ __ Subu(a3, a3, Operand('a'));
+ __ Branch(&loop_check, ls, a3, Operand('z' - 'a'));
+ // Latin-1: Check for values in range [224,254] but not 247.
+ __ Subu(a3, a3, Operand(224 - 'a'));
+ // Weren't Latin-1 letters.
+ __ Branch(&fail, hi, a3, Operand(254 - 224));
+ // Check for 247.
+ __ Branch(&fail, eq, a3, Operand(247 - 224));
+
+ __ bind(&loop_check);
+ __ Branch(&loop, lt, a0, Operand(a1));
+ __ jmp(&success);
+
+ __ bind(&fail);
+ GoTo(on_no_match);
+
+ __ bind(&success);
+ // Compute new value of character position after the matched part.
+ __ Subu(current_input_offset(), a2, end_of_input_address());
+ } else {
+ ASSERT(mode_ == UC16);
+ // Put regexp engine registers on stack.
+ RegList regexp_registers_to_retain = current_input_offset().bit() |
+ current_character().bit() | backtrack_stackpointer().bit();
+ __ MultiPush(regexp_registers_to_retain);
+
+ int argument_count = 4;
+ __ PrepareCallCFunction(argument_count, a2);
+
+ // a0 - offset of start of capture.
+ // a1 - length of capture.
+
+ // Put arguments into arguments registers.
+ // Parameters are
+ // a0: Address byte_offset1 - Address captured substring's start.
+ // a1: Address byte_offset2 - Address of current character position.
+ // a2: size_t byte_length - length of capture in bytes(!).
+ // a3: Isolate* isolate.
+
+ // Address of start of capture.
+ __ Addu(a0, a0, Operand(end_of_input_address()));
+ // Length of capture.
+ __ mov(a2, a1);
+ // Save length in callee-save register for use on return.
+ __ mov(s3, a1);
+ // Address of current input position.
+ __ Addu(a1, current_input_offset(), Operand(end_of_input_address()));
+ // Isolate.
+ __ li(a3, Operand(ExternalReference::isolate_address(masm_->isolate())));
+
+ {
+ 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);
+ __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE);
+ __ lw(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+
+ // Check if function returned non-zero for success or zero for failure.
+ BranchOrBacktrack(on_no_match, eq, v0, Operand(zero_reg));
+ // On success, increment position by length of capture.
+ __ Addu(current_input_offset(), current_input_offset(), Operand(s3));
+ }
+
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckNotBackReference(
+ int start_reg,
+ Label* on_no_match) {
+ Label fallthrough;
+ Label success;
+
+ // Find length of back-referenced capture.
+ __ lw(a0, register_location(start_reg));
+ __ lw(a1, register_location(start_reg + 1));
+ __ Subu(a1, a1, a0); // Length to check.
+ // Succeed on empty capture (including no capture).
+ __ Branch(&fallthrough, eq, a1, Operand(zero_reg));
+
+ __ Addu(t5, a1, current_input_offset());
+ // Check that there are enough characters left in the input.
+ BranchOrBacktrack(on_no_match, gt, t5, Operand(zero_reg));
+
+ // Compute pointers to match string and capture string.
+ __ Addu(a0, a0, Operand(end_of_input_address()));
+ __ Addu(a2, end_of_input_address(), Operand(current_input_offset()));
+ __ Addu(a1, a1, Operand(a0));
+
+ Label loop;
+ __ bind(&loop);
+ if (mode_ == ASCII) {
+ __ lbu(a3, MemOperand(a0, 0));
+ __ addiu(a0, a0, char_size());
+ __ lbu(t0, MemOperand(a2, 0));
+ __ addiu(a2, a2, char_size());
+ } else {
+ ASSERT(mode_ == UC16);
+ __ lhu(a3, MemOperand(a0, 0));
+ __ addiu(a0, a0, char_size());
+ __ lhu(t0, MemOperand(a2, 0));
+ __ addiu(a2, a2, char_size());
+ }
+ BranchOrBacktrack(on_no_match, ne, a3, Operand(t0));
+ __ Branch(&loop, lt, a0, Operand(a1));
+
+ // Move current character position to position after match.
+ __ Subu(current_input_offset(), a2, end_of_input_address());
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckNotCharacter(uint32_t c,
+ Label* on_not_equal) {
+ BranchOrBacktrack(on_not_equal, ne, current_character(), Operand(c));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_equal) {
+ __ And(a0, current_character(), Operand(mask));
+ Operand rhs = (c == 0) ? Operand(zero_reg) : Operand(c);
+ BranchOrBacktrack(on_equal, eq, a0, rhs);
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckNotCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_not_equal) {
+ __ And(a0, current_character(), Operand(mask));
+ Operand rhs = (c == 0) ? Operand(zero_reg) : Operand(c);
+ BranchOrBacktrack(on_not_equal, ne, a0, rhs);
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckNotCharacterAfterMinusAnd(
+ uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal) {
+ ASSERT(minus < String::kMaxUtf16CodeUnit);
+ __ Subu(a0, current_character(), Operand(minus));
+ __ And(a0, a0, Operand(mask));
+ BranchOrBacktrack(on_not_equal, ne, a0, Operand(c));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckCharacterInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_in_range) {
+ __ Subu(a0, current_character(), Operand(from));
+ // Unsigned lower-or-same condition.
+ BranchOrBacktrack(on_in_range, ls, a0, Operand(to - from));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckCharacterNotInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_not_in_range) {
+ __ Subu(a0, current_character(), Operand(from));
+ // Unsigned higher condition.
+ BranchOrBacktrack(on_not_in_range, hi, a0, Operand(to - from));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckBitInTable(
+ Handle<ByteArray> table,
+ Label* on_bit_set) {
+ __ li(a0, Operand(table));
+ if (mode_ != ASCII || kTableMask != String::kMaxOneByteCharCode) {
+ __ And(a1, current_character(), Operand(kTableSize - 1));
+ __ Addu(a0, a0, a1);
+ } else {
+ __ Addu(a0, a0, current_character());
+ }
+
+ __ lbu(a0, FieldMemOperand(a0, ByteArray::kHeaderSize));
+ BranchOrBacktrack(on_bit_set, ne, a0, Operand(zero_reg));
+}
+
+
+bool RegExpMacroAssemblerMIPS::CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match) {
+ // Range checks (c in min..max) are generally implemented by an unsigned
+ // (c - min) <= (max - min) check.
+ switch (type) {
+ case 's':
+ // Match space-characters.
+ if (mode_ == ASCII) {
+ // One byte space characters are '\t'..'\r', ' ' and \u00a0.
+ Label success;
+ __ Branch(&success, eq, current_character(), Operand(' '));
+ // Check range 0x09..0x0d.
+ __ Subu(a0, current_character(), Operand('\t'));
+ __ Branch(&success, ls, a0, Operand('\r' - '\t'));
+ // \u00a0 (NBSP).
+ BranchOrBacktrack(on_no_match, ne, a0, Operand(0x00a0 - '\t'));
+ __ bind(&success);
+ return true;
+ }
+ return false;
+ case 'S':
+ // The emitted code for generic character classes is good enough.
+ return false;
+ case 'd':
+ // Match ASCII digits ('0'..'9').
+ __ Subu(a0, current_character(), Operand('0'));
+ BranchOrBacktrack(on_no_match, hi, a0, Operand('9' - '0'));
+ return true;
+ case 'D':
+ // Match non ASCII-digits.
+ __ Subu(a0, current_character(), Operand('0'));
+ BranchOrBacktrack(on_no_match, ls, a0, Operand('9' - '0'));
+ return true;
+ case '.': {
+ // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029).
+ __ Xor(a0, current_character(), Operand(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c.
+ __ Subu(a0, a0, Operand(0x0b));
+ BranchOrBacktrack(on_no_match, ls, a0, Operand(0x0c - 0x0b));
+ 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.
+ __ Subu(a0, a0, Operand(0x2028 - 0x0b));
+ BranchOrBacktrack(on_no_match, ls, a0, Operand(1));
+ }
+ return true;
+ }
+ case 'n': {
+ // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029).
+ __ Xor(a0, current_character(), Operand(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c.
+ __ Subu(a0, a0, Operand(0x0b));
+ if (mode_ == ASCII) {
+ BranchOrBacktrack(on_no_match, hi, a0, Operand(0x0c - 0x0b));
+ } else {
+ Label done;
+ BranchOrBacktrack(&done, ls, a0, Operand(0x0c - 0x0b));
+ // Compare original value to 0x2028 and 0x2029, using the already
+ // computed (current_char ^ 0x01 - 0x0b). I.e., check for
+ // 0x201d (0x2028 - 0x0b) or 0x201e.
+ __ Subu(a0, a0, Operand(0x2028 - 0x0b));
+ BranchOrBacktrack(on_no_match, hi, a0, Operand(1));
+ __ bind(&done);
+ }
+ return true;
+ }
+ case 'w': {
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ BranchOrBacktrack(on_no_match, hi, current_character(), Operand('z'));
+ }
+ ExternalReference map = ExternalReference::re_word_character_map();
+ __ li(a0, Operand(map));
+ __ Addu(a0, a0, current_character());
+ __ lbu(a0, MemOperand(a0, 0));
+ BranchOrBacktrack(on_no_match, eq, a0, Operand(zero_reg));
+ return true;
+ }
+ case 'W': {
+ Label done;
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ Branch(&done, hi, current_character(), Operand('z'));
+ }
+ ExternalReference map = ExternalReference::re_word_character_map();
+ __ li(a0, Operand(map));
+ __ Addu(a0, a0, current_character());
+ __ lbu(a0, MemOperand(a0, 0));
+ BranchOrBacktrack(on_no_match, ne, a0, Operand(zero_reg));
+ if (mode_ != ASCII) {
+ __ bind(&done);
+ }
+ return true;
+ }
+ case '*':
+ // Match any character.
+ return true;
+ // No custom implementation (yet): s(UC16), S(UC16).
+ default:
+ return false;
+ }
+}
+
+
+void RegExpMacroAssemblerMIPS::Fail() {
+ __ li(v0, Operand(FAILURE));
+ __ jmp(&exit_label_);
+}
+
+
+Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) {
+ Label return_v0;
+ if (masm_->has_exception()) {
+ // If the code gets corrupted due to long regular expressions and lack of
+ // space on trampolines, an internal exception flag is set. If this case
+ // is detected, we will jump into exit sequence right away.
+ __ bind_to(&entry_label_, internal_failure_label_.pos());
+ } else {
+ // Finalize code - write the entry point code now we know how many
+ // registers we need.
+
+ // 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.
+ // Store link register in existing stack-cell.
+ // Order here should correspond to order of offset constants in header file.
+ RegList registers_to_retain = s0.bit() | s1.bit() | s2.bit() |
+ s3.bit() | s4.bit() | s5.bit() | s6.bit() | s7.bit() | fp.bit();
+ RegList argument_registers = a0.bit() | a1.bit() | a2.bit() | a3.bit();
+ __ MultiPush(argument_registers | registers_to_retain | ra.bit());
+ // Set frame pointer in space for it if this is not a direct call
+ // from generated code.
+ __ Addu(frame_pointer(), sp, Operand(4 * kPointerSize));
+ __ mov(a0, zero_reg);
+ __ push(a0); // Make room for success counter and initialize it to 0.
+ __ push(a0); // Make room for "position - 1" constant (value irrelevant).
+
+ // Check if we have space on the stack for registers.
+ Label stack_limit_hit;
+ Label stack_ok;
+
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(masm_->isolate());
+ __ li(a0, Operand(stack_limit));
+ __ lw(a0, MemOperand(a0));
+ __ Subu(a0, sp, a0);
+ // Handle it if the stack pointer is already below the stack limit.
+ __ Branch(&stack_limit_hit, le, a0, Operand(zero_reg));
+ // Check if there is room for the variable number of registers above
+ // the stack limit.
+ __ Branch(&stack_ok, hs, a0, Operand(num_registers_ * kPointerSize));
+ // Exit with OutOfMemory exception. There is not enough space on the stack
+ // for our working registers.
+ __ li(v0, Operand(EXCEPTION));
+ __ jmp(&return_v0);
+
+ __ bind(&stack_limit_hit);
+ CallCheckStackGuardState(a0);
+ // If returned value is non-zero, we exit with the returned value as result.
+ __ Branch(&return_v0, ne, v0, Operand(zero_reg));
+
+ __ bind(&stack_ok);
+ // Allocate space on stack for registers.
+ __ Subu(sp, sp, Operand(num_registers_ * kPointerSize));
+ // Load string end.
+ __ lw(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+ // Load input start.
+ __ lw(a0, MemOperand(frame_pointer(), kInputStart));
+ // Find negative length (offset of start relative to end).
+ __ Subu(current_input_offset(), a0, end_of_input_address());
+ // Set a0 to address of char before start of the input string
+ // (effectively string position -1).
+ __ lw(a1, MemOperand(frame_pointer(), kStartIndex));
+ __ Subu(a0, current_input_offset(), Operand(char_size()));
+ __ sll(t5, a1, (mode_ == UC16) ? 1 : 0);
+ __ Subu(a0, a0, t5);
+ // Store this value in a local variable, for use when clearing
+ // position registers.
+ __ sw(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
+
+ // Initialize code pointer register
+ __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE);
+
+ Label load_char_start_regexp, start_regexp;
+ // Load newline if index is at start, previous character otherwise.
+ __ Branch(&load_char_start_regexp, ne, a1, Operand(zero_reg));
+ __ li(current_character(), Operand('\n'));
+ __ jmp(&start_regexp);
+
+ // Global regexp restarts matching here.
+ __ bind(&load_char_start_regexp);
+ // Load previous char as initial value of current character register.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&start_regexp);
+
+ // Initialize on-stack registers.
+ if (num_saved_registers_ > 0) { // Always is, if generated from a regexp.
+ // Fill saved registers with initial value = start offset - 1.
+ if (num_saved_registers_ > 8) {
+ // Address of register 0.
+ __ Addu(a1, frame_pointer(), Operand(kRegisterZero));
+ __ li(a2, Operand(num_saved_registers_));
+ Label init_loop;
+ __ bind(&init_loop);
+ __ sw(a0, MemOperand(a1));
+ __ Addu(a1, a1, Operand(-kPointerSize));
+ __ Subu(a2, a2, Operand(1));
+ __ Branch(&init_loop, ne, a2, Operand(zero_reg));
+ } else {
+ for (int i = 0; i < num_saved_registers_; i++) {
+ __ sw(a0, register_location(i));
+ }
+ }
+ }
+
+ // Initialize backtrack stack pointer.
+ __ lw(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackHighEnd));
+
+ __ jmp(&start_label_);
+
+
+ // Exit code:
+ if (success_label_.is_linked()) {
+ // Save captures when successful.
+ __ bind(&success_label_);
+ if (num_saved_registers_ > 0) {
+ // Copy captures to output.
+ __ lw(a1, MemOperand(frame_pointer(), kInputStart));
+ __ lw(a0, MemOperand(frame_pointer(), kRegisterOutput));
+ __ lw(a2, MemOperand(frame_pointer(), kStartIndex));
+ __ Subu(a1, end_of_input_address(), a1);
+ // a1 is length of input in bytes.
+ if (mode_ == UC16) {
+ __ srl(a1, a1, 1);
+ }
+ // a1 is length of input in characters.
+ __ Addu(a1, a1, Operand(a2));
+ // a1 is length of string in characters.
+
+ ASSERT_EQ(0, num_saved_registers_ % 2);
+ // Always an even number of capture registers. This allows us to
+ // unroll the loop once to add an operation between a load of a register
+ // and the following use of that register.
+ for (int i = 0; i < num_saved_registers_; i += 2) {
+ __ lw(a2, register_location(i));
+ __ lw(a3, register_location(i + 1));
+ if (i == 0 && global_with_zero_length_check()) {
+ // Keep capture start in a4 for the zero-length check later.
+ __ mov(t7, a2);
+ }
+ if (mode_ == UC16) {
+ __ sra(a2, a2, 1);
+ __ Addu(a2, a2, a1);
+ __ sra(a3, a3, 1);
+ __ Addu(a3, a3, a1);
+ } else {
+ __ Addu(a2, a1, Operand(a2));
+ __ Addu(a3, a1, Operand(a3));
+ }
+ __ sw(a2, MemOperand(a0));
+ __ Addu(a0, a0, kPointerSize);
+ __ sw(a3, MemOperand(a0));
+ __ Addu(a0, a0, kPointerSize);
+ }
+ }
+
+ if (global()) {
+ // Restart matching if the regular expression is flagged as global.
+ __ lw(a0, MemOperand(frame_pointer(), kSuccessfulCaptures));
+ __ lw(a1, MemOperand(frame_pointer(), kNumOutputRegisters));
+ __ lw(a2, MemOperand(frame_pointer(), kRegisterOutput));
+ // Increment success counter.
+ __ Addu(a0, a0, 1);
+ __ sw(a0, MemOperand(frame_pointer(), kSuccessfulCaptures));
+ // Capture results have been stored, so the number of remaining global
+ // output registers is reduced by the number of stored captures.
+ __ Subu(a1, a1, num_saved_registers_);
+ // Check whether we have enough room for another set of capture results.
+ __ mov(v0, a0);
+ __ Branch(&return_v0, lt, a1, Operand(num_saved_registers_));
+
+ __ sw(a1, MemOperand(frame_pointer(), kNumOutputRegisters));
+ // Advance the location for output.
+ __ Addu(a2, a2, num_saved_registers_ * kPointerSize);
+ __ sw(a2, MemOperand(frame_pointer(), kRegisterOutput));
+
+ // Prepare a0 to initialize registers with its value in the next run.
+ __ lw(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
+
+ if (global_with_zero_length_check()) {
+ // Special case for zero-length matches.
+ // t7: capture start index
+ // Not a zero-length match, restart.
+ __ Branch(
+ &load_char_start_regexp, ne, current_input_offset(), Operand(t7));
+ // Offset from the end is zero if we already reached the end.
+ __ Branch(&exit_label_, eq, current_input_offset(),
+ Operand(zero_reg));
+ // Advance current position after a zero-length match.
+ __ Addu(current_input_offset(),
+ current_input_offset(),
+ Operand((mode_ == UC16) ? 2 : 1));
+ }
+
+ __ Branch(&load_char_start_regexp);
+ } else {
+ __ li(v0, Operand(SUCCESS));
+ }
+ }
+ // Exit and return v0.
+ __ bind(&exit_label_);
+ if (global()) {
+ __ lw(v0, MemOperand(frame_pointer(), kSuccessfulCaptures));
+ }
+
+ __ bind(&return_v0);
+ // Skip sp past regexp registers and local variables..
+ __ mov(sp, frame_pointer());
+ // Restore registers s0..s7 and return (restoring ra to pc).
+ __ MultiPop(registers_to_retain | ra.bit());
+ __ Ret();
+
+ // Backtrack code (branch target for conditional backtracks).
+ if (backtrack_label_.is_linked()) {
+ __ bind(&backtrack_label_);
+ Backtrack();
+ }
+
+ Label exit_with_exception;
+
+ // Preempt-code.
+ if (check_preempt_label_.is_linked()) {
+ SafeCallTarget(&check_preempt_label_);
+ // Put regexp engine registers on stack.
+ RegList regexp_registers_to_retain = current_input_offset().bit() |
+ current_character().bit() | backtrack_stackpointer().bit();
+ __ MultiPush(regexp_registers_to_retain);
+ CallCheckStackGuardState(a0);
+ __ MultiPop(regexp_registers_to_retain);
+ // If returning non-zero, we should end execution with the given
+ // result as return value.
+ __ Branch(&return_v0, ne, v0, Operand(zero_reg));
+
+ // String might have moved: Reload end of string from frame.
+ __ lw(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+ __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE);
+ SafeReturn();
+ }
+
+ // Backtrack stack overflow code.
+ if (stack_overflow_label_.is_linked()) {
+ SafeCallTarget(&stack_overflow_label_);
+ // Reached if the backtrack-stack limit has been hit.
+ // Put regexp engine registers on stack first.
+ RegList regexp_registers = current_input_offset().bit() |
+ current_character().bit();
+ __ MultiPush(regexp_registers);
+ Label grow_failed;
+ // Call GrowStack(backtrack_stackpointer(), &stack_base)
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, a0);
+ __ mov(a0, backtrack_stackpointer());
+ __ Addu(a1, frame_pointer(), Operand(kStackHighEnd));
+ __ li(a2, Operand(ExternalReference::isolate_address(masm_->isolate())));
+ ExternalReference grow_stack =
+ ExternalReference::re_grow_stack(masm_->isolate());
+ __ CallCFunction(grow_stack, num_arguments);
+ // Restore regexp registers.
+ __ MultiPop(regexp_registers);
+ // If return NULL, we have failed to grow the stack, and
+ // must exit with a stack-overflow exception.
+ __ Branch(&exit_with_exception, eq, v0, Operand(zero_reg));
+ // Otherwise use return value as new stack pointer.
+ __ mov(backtrack_stackpointer(), v0);
+ // Restore saved registers and continue.
+ __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE);
+ __ lw(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+ SafeReturn();
+ }
+
+ if (exit_with_exception.is_linked()) {
+ // If any of the code above needed to exit with an exception.
+ __ bind(&exit_with_exception);
+ // Exit with Result EXCEPTION(-1) to signal thrown exception.
+ __ li(v0, Operand(EXCEPTION));
+ __ jmp(&return_v0);
+ }
+ }
+
+ CodeDesc code_desc;
+ masm_->GetCode(&code_desc);
+ Handle<Code> code = isolate()->factory()->NewCode(
+ code_desc, Code::ComputeFlags(Code::REGEXP), masm_->CodeObject());
+ LOG(Isolate::Current(), RegExpCodeCreateEvent(*code, *source));
+ return Handle<HeapObject>::cast(code);
+}
+
+
+void RegExpMacroAssemblerMIPS::GoTo(Label* to) {
+ if (to == NULL) {
+ Backtrack();
+ return;
+ }
+ __ jmp(to);
+ return;
+}
+
+
+void RegExpMacroAssemblerMIPS::IfRegisterGE(int reg,
+ int comparand,
+ Label* if_ge) {
+ __ lw(a0, register_location(reg));
+ BranchOrBacktrack(if_ge, ge, a0, Operand(comparand));
+}
+
+
+void RegExpMacroAssemblerMIPS::IfRegisterLT(int reg,
+ int comparand,
+ Label* if_lt) {
+ __ lw(a0, register_location(reg));
+ BranchOrBacktrack(if_lt, lt, a0, Operand(comparand));
+}
+
+
+void RegExpMacroAssemblerMIPS::IfRegisterEqPos(int reg,
+ Label* if_eq) {
+ __ lw(a0, register_location(reg));
+ BranchOrBacktrack(if_eq, eq, a0, Operand(current_input_offset()));
+}
+
+
+RegExpMacroAssembler::IrregexpImplementation
+ RegExpMacroAssemblerMIPS::Implementation() {
+ return kMIPSImplementation;
+}
+
+
+void RegExpMacroAssemblerMIPS::LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds,
+ int characters) {
+ ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
+ ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works).
+ if (check_bounds) {
+ CheckPosition(cp_offset + characters - 1, on_end_of_input);
+ }
+ LoadCurrentCharacterUnchecked(cp_offset, characters);
+}
+
+
+void RegExpMacroAssemblerMIPS::PopCurrentPosition() {
+ Pop(current_input_offset());
+}
+
+
+void RegExpMacroAssemblerMIPS::PopRegister(int register_index) {
+ Pop(a0);
+ __ sw(a0, register_location(register_index));
+}
+
+
+void RegExpMacroAssemblerMIPS::PushBacktrack(Label* label) {
+ if (label->is_bound()) {
+ int target = label->pos();
+ __ li(a0, Operand(target + Code::kHeaderSize - kHeapObjectTag));
+ } else {
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
+ Label after_constant;
+ __ Branch(&after_constant);
+ int offset = masm_->pc_offset();
+ int cp_offset = offset + Code::kHeaderSize - kHeapObjectTag;
+ __ emit(0);
+ masm_->label_at_put(label, offset);
+ __ bind(&after_constant);
+ if (is_int16(cp_offset)) {
+ __ lw(a0, MemOperand(code_pointer(), cp_offset));
+ } else {
+ __ Addu(a0, code_pointer(), cp_offset);
+ __ lw(a0, MemOperand(a0, 0));
+ }
+ }
+ Push(a0);
+ CheckStackLimit();
+}
+
+
+void RegExpMacroAssemblerMIPS::PushCurrentPosition() {
+ Push(current_input_offset());
+}
+
+
+void RegExpMacroAssemblerMIPS::PushRegister(int register_index,
+ StackCheckFlag check_stack_limit) {
+ __ lw(a0, register_location(register_index));
+ Push(a0);
+ if (check_stack_limit) CheckStackLimit();
+}
+
+
+void RegExpMacroAssemblerMIPS::ReadCurrentPositionFromRegister(int reg) {
+ __ lw(current_input_offset(), register_location(reg));
+}
+
+
+void RegExpMacroAssemblerMIPS::ReadStackPointerFromRegister(int reg) {
+ __ lw(backtrack_stackpointer(), register_location(reg));
+ __ lw(a0, MemOperand(frame_pointer(), kStackHighEnd));
+ __ Addu(backtrack_stackpointer(), backtrack_stackpointer(), Operand(a0));
+}
+
+
+void RegExpMacroAssemblerMIPS::SetCurrentPositionFromEnd(int by) {
+ Label after_position;
+ __ Branch(&after_position,
+ ge,
+ current_input_offset(),
+ Operand(-by * char_size()));
+ __ li(current_input_offset(), -by * char_size());
+ // On RegExp code entry (where this operation is used), the character before
+ // the current position is expected to be already loaded.
+ // We have advanced the position, so it's safe to read backwards.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&after_position);
+}
+
+
+void RegExpMacroAssemblerMIPS::SetRegister(int register_index, int to) {
+ ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
+ __ li(a0, Operand(to));
+ __ sw(a0, register_location(register_index));
+}
+
+
+bool RegExpMacroAssemblerMIPS::Succeed() {
+ __ jmp(&success_label_);
+ return global();
+}
+
+
+void RegExpMacroAssemblerMIPS::WriteCurrentPositionToRegister(int reg,
+ int cp_offset) {
+ if (cp_offset == 0) {
+ __ sw(current_input_offset(), register_location(reg));
+ } else {
+ __ Addu(a0, current_input_offset(), Operand(cp_offset * char_size()));
+ __ sw(a0, register_location(reg));
+ }
+}
+
+
+void RegExpMacroAssemblerMIPS::ClearRegisters(int reg_from, int reg_to) {
+ ASSERT(reg_from <= reg_to);
+ __ lw(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ __ sw(a0, register_location(reg));
+ }
+}
+
+
+void RegExpMacroAssemblerMIPS::WriteStackPointerToRegister(int reg) {
+ __ lw(a1, MemOperand(frame_pointer(), kStackHighEnd));
+ __ Subu(a0, backtrack_stackpointer(), a1);
+ __ sw(a0, register_location(reg));
+}
+
+
+bool RegExpMacroAssemblerMIPS::CanReadUnaligned() {
+ return false;
+}
+
+
+// Private methods:
+
+void RegExpMacroAssemblerMIPS::CallCheckStackGuardState(Register scratch) {
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, scratch);
+ __ mov(a2, frame_pointer());
+ // Code* of self.
+ __ li(a1, Operand(masm_->CodeObject()), CONSTANT_SIZE);
+ // a0 becomes return address pointer.
+ ExternalReference stack_guard_check =
+ ExternalReference::re_check_stack_guard_state(masm_->isolate());
+ CallCFunctionUsingStub(stack_guard_check, num_arguments);
+}
+
+
+// Helper function for reading a value out of a stack frame.
+template <typename T>
+static T& frame_entry(Address re_frame, int frame_offset) {
+ return reinterpret_cast<T&>(Memory::int32_at(re_frame + frame_offset));
+}
+
+
+int RegExpMacroAssemblerMIPS::CheckStackGuardState(Address* return_address,
+ Code* re_code,
+ Address re_frame) {
+ Isolate* isolate = frame_entry<Isolate*>(re_frame, kIsolate);
+ ASSERT(isolate == Isolate::Current());
+ if (isolate->stack_guard()->IsStackOverflow()) {
+ isolate->StackOverflow();
+ return EXCEPTION;
+ }
+
+ // If not real stack overflow the stack guard was used to interrupt
+ // execution for another purpose.
+
+ // If this is a direct call from JavaScript retry the RegExp forcing the call
+ // through the runtime system. Currently the direct call cannot handle a GC.
+ if (frame_entry<int>(re_frame, kDirectCall) == 1) {
+ return RETRY;
+ }
+
+ // Prepare for possible GC.
+ HandleScope handles(isolate);
+ Handle<Code> code_handle(re_code);
+
+ Handle<String> subject(frame_entry<String*>(re_frame, kInputString));
+ // Current string.
+ bool is_ascii = subject->IsOneByteRepresentationUnderneath();
+
+ ASSERT(re_code->instruction_start() <= *return_address);
+ ASSERT(*return_address <=
+ re_code->instruction_start() + re_code->instruction_size());
+
+ MaybeObject* result = Execution::HandleStackGuardInterrupt(isolate);
+
+ if (*code_handle != re_code) { // Return address no longer valid.
+ int delta = code_handle->address() - re_code->address();
+ // Overwrite the return address on the stack.
+ *return_address += delta;
+ }
+
+ if (result->IsException()) {
+ return EXCEPTION;
+ }
+
+ Handle<String> subject_tmp = subject;
+ int slice_offset = 0;
+
+ // Extract the underlying string and the slice offset.
+ if (StringShape(*subject_tmp).IsCons()) {
+ subject_tmp = Handle<String>(ConsString::cast(*subject_tmp)->first());
+ } else if (StringShape(*subject_tmp).IsSliced()) {
+ SlicedString* slice = SlicedString::cast(*subject_tmp);
+ subject_tmp = Handle<String>(slice->parent());
+ slice_offset = slice->offset();
+ }
+
+ // String might have changed.
+ if (subject_tmp->IsOneByteRepresentation() != is_ascii) {
+ // If we changed between an ASCII and an UC16 string, the specialized
+ // code cannot be used, and we need to restart regexp matching from
+ // scratch (including, potentially, compiling a new version of the code).
+ return RETRY;
+ }
+
+ // Otherwise, the content of the string might have moved. It must still
+ // be a sequential or external string with the same content.
+ // Update the start and end pointers in the stack frame to the current
+ // location (whether it has actually moved or not).
+ ASSERT(StringShape(*subject_tmp).IsSequential() ||
+ StringShape(*subject_tmp).IsExternal());
+
+ // The original start address of the characters to match.
+ const byte* start_address = frame_entry<const byte*>(re_frame, kInputStart);
+
+ // Find the current start address of the same character at the current string
+ // position.
+ int start_index = frame_entry<int>(re_frame, kStartIndex);
+ const byte* new_address = StringCharacterPosition(*subject_tmp,
+ start_index + slice_offset);
+
+ if (start_address != new_address) {
+ // If there is a difference, update the object pointer and start and end
+ // addresses in the RegExp stack frame to match the new value.
+ const byte* end_address = frame_entry<const byte* >(re_frame, kInputEnd);
+ int byte_length = static_cast<int>(end_address - start_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;
+}
+
+
+MemOperand RegExpMacroAssemblerMIPS::register_location(int register_index) {
+ ASSERT(register_index < (1<<30));
+ if (num_registers_ <= register_index) {
+ num_registers_ = register_index + 1;
+ }
+ return MemOperand(frame_pointer(),
+ kRegisterZero - register_index * kPointerSize);
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckPosition(int cp_offset,
+ Label* on_outside_input) {
+ BranchOrBacktrack(on_outside_input,
+ ge,
+ current_input_offset(),
+ Operand(-cp_offset * char_size()));
+}
+
+
+void RegExpMacroAssemblerMIPS::BranchOrBacktrack(Label* to,
+ Condition condition,
+ Register rs,
+ const Operand& rt) {
+ if (condition == al) { // Unconditional.
+ if (to == NULL) {
+ Backtrack();
+ return;
+ }
+ __ jmp(to);
+ return;
+ }
+ if (to == NULL) {
+ __ Branch(&backtrack_label_, condition, rs, rt);
+ return;
+ }
+ __ Branch(to, condition, rs, rt);
+}
+
+
+void RegExpMacroAssemblerMIPS::SafeCall(Label* to,
+ Condition cond,
+ Register rs,
+ const Operand& rt) {
+ __ BranchAndLink(to, cond, rs, rt);
+}
+
+
+void RegExpMacroAssemblerMIPS::SafeReturn() {
+ __ pop(ra);
+ __ Addu(t5, ra, Operand(masm_->CodeObject()));
+ __ Jump(t5);
+}
+
+
+void RegExpMacroAssemblerMIPS::SafeCallTarget(Label* name) {
+ __ bind(name);
+ __ Subu(ra, ra, Operand(masm_->CodeObject()));
+ __ push(ra);
+}
+
+
+void RegExpMacroAssemblerMIPS::Push(Register source) {
+ ASSERT(!source.is(backtrack_stackpointer()));
+ __ Addu(backtrack_stackpointer(),
+ backtrack_stackpointer(),
+ Operand(-kPointerSize));
+ __ sw(source, MemOperand(backtrack_stackpointer()));
+}
+
+
+void RegExpMacroAssemblerMIPS::Pop(Register target) {
+ ASSERT(!target.is(backtrack_stackpointer()));
+ __ lw(target, MemOperand(backtrack_stackpointer()));
+ __ Addu(backtrack_stackpointer(), backtrack_stackpointer(), kPointerSize);
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckPreemption() {
+ // Check for preemption.
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(masm_->isolate());
+ __ li(a0, Operand(stack_limit));
+ __ lw(a0, MemOperand(a0));
+ SafeCall(&check_preempt_label_, ls, sp, Operand(a0));
+}
+
+
+void RegExpMacroAssemblerMIPS::CheckStackLimit() {
+ ExternalReference stack_limit =
+ ExternalReference::address_of_regexp_stack_limit(masm_->isolate());
+
+ __ li(a0, Operand(stack_limit));
+ __ lw(a0, MemOperand(a0));
+ SafeCall(&stack_overflow_label_, ls, backtrack_stackpointer(), Operand(a0));
+}
+
+
+void RegExpMacroAssemblerMIPS::CallCFunctionUsingStub(
+ ExternalReference function,
+ int num_arguments) {
+ // Must pass all arguments in registers. The stub pushes on the stack.
+ ASSERT(num_arguments <= 4);
+ __ li(code_pointer(), Operand(function));
+ RegExpCEntryStub stub;
+ __ CallStub(&stub);
+ if (OS::ActivationFrameAlignment() != 0) {
+ __ lw(sp, MemOperand(sp, 16));
+ }
+ __ li(code_pointer(), Operand(masm_->CodeObject()), CONSTANT_SIZE);
+}
+
+
+void RegExpMacroAssemblerMIPS::LoadCurrentCharacterUnchecked(int cp_offset,
+ int characters) {
+ Register offset = current_input_offset();
+ if (cp_offset != 0) {
+ // t7 is not being used to store the capture start index at this point.
+ __ Addu(t7, current_input_offset(), Operand(cp_offset * char_size()));
+ offset = t7;
+ }
+ // We assume that we cannot do unaligned loads on MIPS, so this function
+ // must only be used to load a single character at a time.
+ ASSERT(characters == 1);
+ __ Addu(t5, end_of_input_address(), Operand(offset));
+ if (mode_ == ASCII) {
+ __ lbu(current_character(), MemOperand(t5, 0));
+ } else {
+ ASSERT(mode_ == UC16);
+ __ lhu(current_character(), MemOperand(t5, 0));
+ }
+}
+
+
+void RegExpCEntryStub::Generate(MacroAssembler* masm_) {
+ int stack_alignment = OS::ActivationFrameAlignment();
+ 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 + 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, return_address_offset));
+ __ Addu(sp, sp, Operand(stack_alignment + kCArgsSlotsSize));
+ __ Jump(ra);
+}
+
+
+#undef __
+
+#endif // V8_INTERPRETED_REGEXP
+
+}} // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/regexp-macro-assembler-mips.h b/chromium/v8/src/mips/regexp-macro-assembler-mips.h
new file mode 100644
index 00000000000..86ae4d45eeb
--- /dev/null
+++ b/chromium/v8/src/mips/regexp-macro-assembler-mips.h
@@ -0,0 +1,258 @@
+// 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_REGEXP_MACRO_ASSEMBLER_MIPS_H_
+#define V8_MIPS_REGEXP_MACRO_ASSEMBLER_MIPS_H_
+
+#include "mips/assembler-mips.h"
+#include "mips/assembler-mips-inl.h"
+#include "macro-assembler.h"
+#include "code.h"
+#include "mips/macro-assembler-mips.h"
+
+namespace v8 {
+namespace internal {
+
+#ifndef V8_INTERPRETED_REGEXP
+class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
+ public:
+ RegExpMacroAssemblerMIPS(Mode mode, int registers_to_save, Zone* zone);
+ virtual ~RegExpMacroAssemblerMIPS();
+ virtual int stack_limit_slack();
+ virtual void AdvanceCurrentPosition(int by);
+ virtual void AdvanceRegister(int reg, int by);
+ virtual void Backtrack();
+ virtual void Bind(Label* label);
+ virtual void CheckAtStart(Label* on_at_start);
+ virtual void CheckCharacter(uint32_t c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_equal);
+ virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
+ virtual void CheckCharacterLT(uc16 limit, Label* on_less);
+ // A "greedy loop" is a loop that is both greedy and with a simple
+ // body. It has a particularly simple implementation.
+ virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
+ virtual void CheckNotAtStart(Label* on_not_at_start);
+ virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
+ virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
+ Label* on_no_match);
+ virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_not_equal);
+ virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal);
+ virtual void CheckCharacterInRange(uc16 from,
+ uc16 to,
+ Label* on_in_range);
+ virtual void CheckCharacterNotInRange(uc16 from,
+ uc16 to,
+ Label* on_not_in_range);
+ virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
+
+ // Checks whether the given offset from the current position is before
+ // the end of the string.
+ virtual void CheckPosition(int cp_offset, Label* on_outside_input);
+ virtual bool CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match);
+ virtual void Fail();
+ virtual Handle<HeapObject> GetCode(Handle<String> source);
+ virtual void GoTo(Label* label);
+ virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
+ virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
+ virtual void IfRegisterEqPos(int reg, Label* if_eq);
+ virtual IrregexpImplementation Implementation();
+ virtual void LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds = true,
+ int characters = 1);
+ virtual void PopCurrentPosition();
+ virtual void PopRegister(int register_index);
+ virtual void PushBacktrack(Label* label);
+ virtual void PushCurrentPosition();
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
+ virtual void ReadCurrentPositionFromRegister(int reg);
+ virtual void ReadStackPointerFromRegister(int reg);
+ virtual void SetCurrentPositionFromEnd(int by);
+ virtual void SetRegister(int register_index, int to);
+ virtual bool Succeed();
+ virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+ virtual void ClearRegisters(int reg_from, int reg_to);
+ virtual void WriteStackPointerToRegister(int reg);
+ virtual bool CanReadUnaligned();
+
+ // Called from RegExp if the stack-guard is triggered.
+ // If the code object is relocated, the return address is fixed before
+ // returning.
+ static int CheckStackGuardState(Address* return_address,
+ Code* re_code,
+ Address re_frame);
+
+ private:
+ // Offsets from frame_pointer() of function parameters and stored registers.
+ static const int kFramePointer = 0;
+
+ // Above the frame pointer - Stored registers and stack passed parameters.
+ // Registers s0 to s7, fp, and ra.
+ static const int kStoredRegisters = kFramePointer;
+ // Return address (stored from link register, read into pc on return).
+ static const int kReturnAddress = kStoredRegisters + 9 * kPointerSize;
+ static const int kSecondaryReturnAddress = kReturnAddress + kPointerSize;
+ // Stack frame header.
+ static const int kStackFrameHeader = kReturnAddress + kPointerSize;
+ // Stack parameters placed by caller.
+ static const int kRegisterOutput = kStackFrameHeader + 20;
+ static const int kNumOutputRegisters = kRegisterOutput + kPointerSize;
+ static const int kStackHighEnd = kNumOutputRegisters + kPointerSize;
+ static const int kDirectCall = kStackHighEnd + kPointerSize;
+ static const int kIsolate = kDirectCall + kPointerSize;
+
+ // Below the frame pointer.
+ // Register parameters stored by setup code.
+ static const int kInputEnd = kFramePointer - kPointerSize;
+ static const int kInputStart = kInputEnd - kPointerSize;
+ static const int kStartIndex = kInputStart - kPointerSize;
+ static const int kInputString = kStartIndex - kPointerSize;
+ // When adding local variables remember to push space for them in
+ // the frame in GetCode.
+ static const int kSuccessfulCaptures = kInputString - kPointerSize;
+ static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
+ // First register address. Following registers are below it on the stack.
+ static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
+
+ // Initial size of code buffer.
+ static const size_t kRegExpCodeSize = 1024;
+
+ // Load a number of characters at the given offset from the
+ // current position, into the current-character register.
+ void LoadCurrentCharacterUnchecked(int cp_offset, int character_count);
+
+ // Check whether preemption has been requested.
+ void CheckPreemption();
+
+ // Check whether we are exceeding the stack limit on the backtrack stack.
+ void CheckStackLimit();
+
+
+ // Generate a call to CheckStackGuardState.
+ void CallCheckStackGuardState(Register scratch);
+
+ // The ebp-relative location of a regexp register.
+ MemOperand register_location(int register_index);
+
+ // Register holding the current input position as negative offset from
+ // the end of the string.
+ inline Register current_input_offset() { return t2; }
+
+ // The register containing the current character after LoadCurrentCharacter.
+ inline Register current_character() { return t3; }
+
+ // Register holding address of the end of the input string.
+ inline Register end_of_input_address() { return t6; }
+
+ // Register holding the frame address. Local variables, parameters and
+ // regexp registers are addressed relative to this.
+ inline Register frame_pointer() { return fp; }
+
+ // The register containing the backtrack stack top. Provides a meaningful
+ // name to the register.
+ inline Register backtrack_stackpointer() { return t4; }
+
+ // Register holding pointer to the current code object.
+ inline Register code_pointer() { return t1; }
+
+ // Byte size of chars in the string to match (decided by the Mode argument).
+ inline int char_size() { return static_cast<int>(mode_); }
+
+ // Equivalent to a conditional branch to the label, unless the label
+ // is NULL, in which case it is a conditional Backtrack.
+ void BranchOrBacktrack(Label* to,
+ Condition condition,
+ Register rs,
+ const Operand& rt);
+
+ // Call and return internally in the generated code in a way that
+ // is GC-safe (i.e., doesn't leave absolute code addresses on the stack)
+ inline void SafeCall(Label* to,
+ Condition cond,
+ Register rs,
+ const Operand& rt);
+ inline void SafeReturn();
+ inline void SafeCallTarget(Label* name);
+
+ // Pushes the value of a register on the backtrack stack. Decrements the
+ // stack pointer by a word size and stores the register's value there.
+ inline void Push(Register source);
+
+ // Pops a value from the backtrack stack. Reads the word at the stack pointer
+ // and increments it by a word size.
+ inline void Pop(Register target);
+
+ // Calls a C function and cleans up the frame alignment done by
+ // by FrameAlign. The called function *is* allowed to trigger a garbage
+ // collection, but may not take more than four arguments (no arguments
+ // passed on the stack), and the first argument will be a pointer to the
+ // return address.
+ inline void CallCFunctionUsingStub(ExternalReference function,
+ int num_arguments);
+
+ Isolate* isolate() const { return masm_->isolate(); }
+
+ MacroAssembler* masm_;
+
+ // Which mode to generate code for (ASCII or UC16).
+ Mode mode_;
+
+ // One greater than maximal register index actually used.
+ int num_registers_;
+
+ // Number of registers to output at the end (the saved registers
+ // are always 0..num_saved_registers_-1).
+ int num_saved_registers_;
+
+ // Labels used internally.
+ Label entry_label_;
+ Label start_label_;
+ Label success_label_;
+ Label backtrack_label_;
+ Label exit_label_;
+ Label check_preempt_label_;
+ Label stack_overflow_label_;
+ Label internal_failure_label_;
+};
+
+#endif // V8_INTERPRETED_REGEXP
+
+
+}} // namespace v8::internal
+
+#endif // V8_MIPS_REGEXP_MACRO_ASSEMBLER_MIPS_H_
diff --git a/chromium/v8/src/mips/simulator-mips.cc b/chromium/v8/src/mips/simulator-mips.cc
new file mode 100644
index 00000000000..914a7586623
--- /dev/null
+++ b/chromium/v8/src/mips/simulator-mips.cc
@@ -0,0 +1,3002 @@
+// 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 <limits.h>
+#include <cmath>
+#include <cstdarg>
+#include "v8.h"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "cpu.h"
+#include "disasm.h"
+#include "assembler.h"
+#include "globals.h" // Need the BitCast.
+#include "mips/constants-mips.h"
+#include "mips/simulator-mips.h"
+
+
+// Only build the simulator if not compiling for real MIPS hardware.
+#if defined(USE_SIMULATOR)
+
+namespace v8 {
+namespace internal {
+
+// Utils functions.
+bool HaveSameSign(int32_t a, int32_t b) {
+ return ((a ^ b) >= 0);
+}
+
+
+uint32_t get_fcsr_condition_bit(uint32_t cc) {
+ if (cc == 0) {
+ return 23;
+ } else {
+ return 24 + cc;
+ }
+}
+
+
+// This macro provides a platform independent use of sscanf. The reason for
+// SScanF not being implemented in a platform independent was through
+// ::v8::internal::OS in the same way as SNPrintF is that the Windows C Run-Time
+// Library does not provide vsscanf.
+#define SScanF sscanf // NOLINT
+
+// The MipsDebugger class is used by the simulator while debugging simulated
+// code.
+class MipsDebugger {
+ public:
+ explicit MipsDebugger(Simulator* sim) : sim_(sim) { }
+ ~MipsDebugger();
+
+ void Stop(Instruction* instr);
+ void Debug();
+ // Print all registers with a nice formatting.
+ void PrintAllRegs();
+ void PrintAllRegsIncludingFPU();
+
+ private:
+ // We set the breakpoint code to 0xfffff to easily recognize it.
+ static const Instr kBreakpointInstr = SPECIAL | BREAK | 0xfffff << 6;
+ static const Instr kNopInstr = 0x0;
+
+ Simulator* sim_;
+
+ int32_t GetRegisterValue(int regnum);
+ int32_t GetFPURegisterValueInt(int regnum);
+ int64_t GetFPURegisterValueLong(int regnum);
+ float GetFPURegisterValueFloat(int regnum);
+ double GetFPURegisterValueDouble(int regnum);
+ bool GetValue(const char* desc, int32_t* value);
+
+ // Set or delete a breakpoint. Returns true if successful.
+ bool SetBreakpoint(Instruction* breakpc);
+ bool DeleteBreakpoint(Instruction* breakpc);
+
+ // Undo and redo all breakpoints. This is needed to bracket disassembly and
+ // execution to skip past breakpoints when run from the debugger.
+ void UndoBreakpoints();
+ void RedoBreakpoints();
+};
+
+
+MipsDebugger::~MipsDebugger() {
+}
+
+
+#ifdef GENERATED_CODE_COVERAGE
+static FILE* coverage_log = NULL;
+
+
+static void InitializeCoverage() {
+ char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG");
+ if (file_name != NULL) {
+ coverage_log = fopen(file_name, "aw+");
+ }
+}
+
+
+void MipsDebugger::Stop(Instruction* instr) {
+ // Get the stop code.
+ uint32_t code = instr->Bits(25, 6);
+ // Retrieve the encoded address, which comes just after this stop.
+ char** msg_address =
+ reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize);
+ char* msg = *msg_address;
+ ASSERT(msg != NULL);
+
+ // Update this stop description.
+ if (!watched_stops_[code].desc) {
+ watched_stops_[code].desc = msg;
+ }
+
+ if (strlen(msg) > 0) {
+ if (coverage_log != NULL) {
+ fprintf(coverage_log, "%s\n", str);
+ fflush(coverage_log);
+ }
+ // Overwrite the instruction and address with nops.
+ instr->SetInstructionBits(kNopInstr);
+ reinterpret_cast<Instr*>(msg_address)->SetInstructionBits(kNopInstr);
+ }
+ sim_->set_pc(sim_->get_pc() + 2 * Instruction::kInstructionSize);
+}
+
+
+#else // GENERATED_CODE_COVERAGE
+
+#define UNSUPPORTED() printf("Unsupported instruction.\n");
+
+static void InitializeCoverage() {}
+
+
+void MipsDebugger::Stop(Instruction* instr) {
+ // Get the stop code.
+ uint32_t code = instr->Bits(25, 6);
+ // Retrieve the encoded address, which comes just after this stop.
+ char* msg = *reinterpret_cast<char**>(sim_->get_pc() +
+ Instruction::kInstrSize);
+ // Update this stop description.
+ if (!sim_->watched_stops_[code].desc) {
+ sim_->watched_stops_[code].desc = msg;
+ }
+ PrintF("Simulator hit %s (%u)\n", msg, code);
+ sim_->set_pc(sim_->get_pc() + 2 * Instruction::kInstrSize);
+ Debug();
+}
+#endif // GENERATED_CODE_COVERAGE
+
+
+int32_t MipsDebugger::GetRegisterValue(int regnum) {
+ if (regnum == kNumSimuRegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->get_register(regnum);
+ }
+}
+
+
+int32_t MipsDebugger::GetFPURegisterValueInt(int regnum) {
+ if (regnum == kNumFPURegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->get_fpu_register(regnum);
+ }
+}
+
+
+int64_t MipsDebugger::GetFPURegisterValueLong(int regnum) {
+ if (regnum == kNumFPURegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->get_fpu_register_long(regnum);
+ }
+}
+
+
+float MipsDebugger::GetFPURegisterValueFloat(int regnum) {
+ if (regnum == kNumFPURegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->get_fpu_register_float(regnum);
+ }
+}
+
+
+double MipsDebugger::GetFPURegisterValueDouble(int regnum) {
+ if (regnum == kNumFPURegisters) {
+ return sim_->get_pc();
+ } else {
+ return sim_->get_fpu_register_double(regnum);
+ }
+}
+
+
+bool MipsDebugger::GetValue(const char* desc, int32_t* value) {
+ int regnum = Registers::Number(desc);
+ int fpuregnum = FPURegisters::Number(desc);
+
+ if (regnum != kInvalidRegister) {
+ *value = GetRegisterValue(regnum);
+ return true;
+ } else if (fpuregnum != kInvalidFPURegister) {
+ *value = GetFPURegisterValueInt(fpuregnum);
+ return true;
+ } else if (strncmp(desc, "0x", 2) == 0) {
+ return SScanF(desc, "%x", reinterpret_cast<uint32_t*>(value)) == 1;
+ } else {
+ return SScanF(desc, "%i", value) == 1;
+ }
+ return false;
+}
+
+
+bool MipsDebugger::SetBreakpoint(Instruction* breakpc) {
+ // Check if a breakpoint can be set. If not return without any side-effects.
+ if (sim_->break_pc_ != NULL) {
+ return false;
+ }
+
+ // Set the breakpoint.
+ sim_->break_pc_ = breakpc;
+ sim_->break_instr_ = breakpc->InstructionBits();
+ // Not setting the breakpoint instruction in the code itself. It will be set
+ // when the debugger shell continues.
+ return true;
+}
+
+
+bool MipsDebugger::DeleteBreakpoint(Instruction* breakpc) {
+ if (sim_->break_pc_ != NULL) {
+ sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
+ }
+
+ sim_->break_pc_ = NULL;
+ sim_->break_instr_ = 0;
+ return true;
+}
+
+
+void MipsDebugger::UndoBreakpoints() {
+ if (sim_->break_pc_ != NULL) {
+ sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
+ }
+}
+
+
+void MipsDebugger::RedoBreakpoints() {
+ if (sim_->break_pc_ != NULL) {
+ sim_->break_pc_->SetInstructionBits(kBreakpointInstr);
+ }
+}
+
+
+void MipsDebugger::PrintAllRegs() {
+#define REG_INFO(n) Registers::Name(n), GetRegisterValue(n), GetRegisterValue(n)
+
+ PrintF("\n");
+ // at, v0, a0.
+ PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
+ REG_INFO(1), REG_INFO(2), REG_INFO(4));
+ // v1, a1.
+ PrintF("%26s\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
+ "", REG_INFO(3), REG_INFO(5));
+ // a2.
+ PrintF("%26s\t%26s\t%3s: 0x%08x %10d\n", "", "", REG_INFO(6));
+ // a3.
+ PrintF("%26s\t%26s\t%3s: 0x%08x %10d\n", "", "", REG_INFO(7));
+ PrintF("\n");
+ // t0-t7, s0-s7
+ for (int i = 0; i < 8; i++) {
+ PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
+ REG_INFO(8+i), REG_INFO(16+i));
+ }
+ PrintF("\n");
+ // t8, k0, LO.
+ PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
+ REG_INFO(24), REG_INFO(26), REG_INFO(32));
+ // t9, k1, HI.
+ PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
+ REG_INFO(25), REG_INFO(27), REG_INFO(33));
+ // sp, fp, gp.
+ PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
+ REG_INFO(29), REG_INFO(30), REG_INFO(28));
+ // pc.
+ PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
+ REG_INFO(31), REG_INFO(34));
+
+#undef REG_INFO
+#undef FPU_REG_INFO
+}
+
+
+void MipsDebugger::PrintAllRegsIncludingFPU() {
+#define FPU_REG_INFO(n) FPURegisters::Name(n), FPURegisters::Name(n+1), \
+ GetFPURegisterValueInt(n+1), \
+ GetFPURegisterValueInt(n), \
+ GetFPURegisterValueDouble(n)
+
+ PrintAllRegs();
+
+ PrintF("\n\n");
+ // f0, f1, f2, ... f31.
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(0) );
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(2) );
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(4) );
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(6) );
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(8) );
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(10));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(12));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(14));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(16));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(18));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(20));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(22));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(24));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(26));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(28));
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(30));
+
+#undef REG_INFO
+#undef FPU_REG_INFO
+}
+
+
+void MipsDebugger::Debug() {
+ intptr_t last_pc = -1;
+ bool done = false;
+
+#define COMMAND_SIZE 63
+#define ARG_SIZE 255
+
+#define STR(a) #a
+#define XSTR(a) STR(a)
+
+ char cmd[COMMAND_SIZE + 1];
+ char arg1[ARG_SIZE + 1];
+ char arg2[ARG_SIZE + 1];
+ char* argv[3] = { cmd, arg1, arg2 };
+
+ // Make sure to have a proper terminating character if reaching the limit.
+ cmd[COMMAND_SIZE] = 0;
+ arg1[ARG_SIZE] = 0;
+ arg2[ARG_SIZE] = 0;
+
+ // Undo all set breakpoints while running in the debugger shell. This will
+ // make them invisible to all commands.
+ UndoBreakpoints();
+
+ while (!done && (sim_->get_pc() != Simulator::end_sim_pc)) {
+ if (last_pc != sim_->get_pc()) {
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // Use a reasonably large buffer.
+ v8::internal::EmbeddedVector<char, 256> buffer;
+ dasm.InstructionDecode(buffer,
+ reinterpret_cast<byte*>(sim_->get_pc()));
+ PrintF(" 0x%08x %s\n", sim_->get_pc(), buffer.start());
+ last_pc = sim_->get_pc();
+ }
+ char* line = ReadLine("sim> ");
+ 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,
+ "%" XSTR(COMMAND_SIZE) "s "
+ "%" XSTR(ARG_SIZE) "s "
+ "%" XSTR(ARG_SIZE) "s",
+ cmd, arg1, arg2);
+ if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
+ Instruction* instr = reinterpret_cast<Instruction*>(sim_->get_pc());
+ if (!(instr->IsTrap()) ||
+ instr->InstructionBits() == rtCallRedirInstr) {
+ sim_->InstructionDecode(
+ reinterpret_cast<Instruction*>(sim_->get_pc()));
+ } else {
+ // Allow si to jump over generated breakpoints.
+ PrintF("/!\\ Jumping over generated breakpoint.\n");
+ sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize);
+ }
+ } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
+ // Execute the one instruction we broke at with breakpoints disabled.
+ sim_->InstructionDecode(reinterpret_cast<Instruction*>(sim_->get_pc()));
+ // Leave the debugger shell.
+ done = true;
+ } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
+ if (argc == 2) {
+ int32_t value;
+ float fvalue;
+ if (strcmp(arg1, "all") == 0) {
+ PrintAllRegs();
+ } else if (strcmp(arg1, "allf") == 0) {
+ PrintAllRegsIncludingFPU();
+ } else {
+ int regnum = Registers::Number(arg1);
+ int fpuregnum = FPURegisters::Number(arg1);
+
+ if (regnum != kInvalidRegister) {
+ value = GetRegisterValue(regnum);
+ PrintF("%s: 0x%08x %d \n", arg1, value, value);
+ } else if (fpuregnum != kInvalidFPURegister) {
+ if (fpuregnum % 2 == 1) {
+ value = GetFPURegisterValueInt(fpuregnum);
+ fvalue = GetFPURegisterValueFloat(fpuregnum);
+ PrintF("%s: 0x%08x %11.4e\n", arg1, value, fvalue);
+ } else {
+ double dfvalue;
+ int32_t lvalue1 = GetFPURegisterValueInt(fpuregnum);
+ int32_t lvalue2 = GetFPURegisterValueInt(fpuregnum + 1);
+ dfvalue = GetFPURegisterValueDouble(fpuregnum);
+ PrintF("%3s,%3s: 0x%08x%08x %16.4e\n",
+ FPURegisters::Name(fpuregnum+1),
+ FPURegisters::Name(fpuregnum),
+ lvalue1,
+ lvalue2,
+ dfvalue);
+ }
+ } else {
+ PrintF("%s unrecognized\n", arg1);
+ }
+ }
+ } else {
+ if (argc == 3) {
+ if (strcmp(arg2, "single") == 0) {
+ int32_t value;
+ float fvalue;
+ int fpuregnum = FPURegisters::Number(arg1);
+
+ if (fpuregnum != kInvalidFPURegister) {
+ value = GetFPURegisterValueInt(fpuregnum);
+ fvalue = GetFPURegisterValueFloat(fpuregnum);
+ PrintF("%s: 0x%08x %11.4e\n", arg1, value, fvalue);
+ } else {
+ PrintF("%s unrecognized\n", arg1);
+ }
+ } else {
+ PrintF("print <fpu register> single\n");
+ }
+ } else {
+ PrintF("print <register> or print <fpu register> single\n");
+ }
+ }
+ } else if ((strcmp(cmd, "po") == 0)
+ || (strcmp(cmd, "printobject") == 0)) {
+ if (argc == 2) {
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ Object* obj = reinterpret_cast<Object*>(value);
+ PrintF("%s: \n", arg1);
+#ifdef DEBUG
+ obj->PrintLn();
+#else
+ obj->ShortPrint();
+ PrintF("\n");
+#endif
+ } else {
+ PrintF("%s unrecognized\n", arg1);
+ }
+ } else {
+ PrintF("printobject <value>\n");
+ }
+ } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) {
+ int32_t* cur = NULL;
+ int32_t* end = NULL;
+ int next_arg = 1;
+
+ if (strcmp(cmd, "stack") == 0) {
+ cur = reinterpret_cast<int32_t*>(sim_->get_register(Simulator::sp));
+ } else { // Command "mem".
+ int32_t value;
+ if (!GetValue(arg1, &value)) {
+ PrintF("%s unrecognized\n", arg1);
+ continue;
+ }
+ cur = reinterpret_cast<int32_t*>(value);
+ next_arg++;
+ }
+
+ int32_t words;
+ if (argc == next_arg) {
+ words = 10;
+ } else {
+ if (!GetValue(argv[next_arg], &words)) {
+ words = 10;
+ }
+ }
+ end = cur + words;
+
+ while (cur < end) {
+ PrintF(" 0x%08x: 0x%08x %10d",
+ reinterpret_cast<intptr_t>(cur), *cur, *cur);
+ HeapObject* obj = reinterpret_cast<HeapObject*>(*cur);
+ int value = *cur;
+ Heap* current_heap = v8::internal::Isolate::Current()->heap();
+ if (((value & 1) == 0) || current_heap->Contains(obj)) {
+ PrintF(" (");
+ if ((value & 1) == 0) {
+ PrintF("smi %d", value / 2);
+ } else {
+ obj->ShortPrint();
+ }
+ PrintF(")");
+ }
+ PrintF("\n");
+ cur++;
+ }
+
+ } else if ((strcmp(cmd, "disasm") == 0) ||
+ (strcmp(cmd, "dpc") == 0) ||
+ (strcmp(cmd, "di") == 0)) {
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // Use a reasonably large buffer.
+ v8::internal::EmbeddedVector<char, 256> buffer;
+
+ byte* cur = NULL;
+ byte* end = NULL;
+
+ if (argc == 1) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ end = cur + (10 * Instruction::kInstrSize);
+ } else if (argc == 2) {
+ int regnum = Registers::Number(arg1);
+ if (regnum != kInvalidRegister || strncmp(arg1, "0x", 2) == 0) {
+ // The argument is an address or a register name.
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(value);
+ // Disassemble 10 instructions at <arg1>.
+ end = cur + (10 * Instruction::kInstrSize);
+ }
+ } else {
+ // The argument is the number of instructions.
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ // Disassemble <arg1> instructions.
+ end = cur + (value * Instruction::kInstrSize);
+ }
+ }
+ } else {
+ int32_t value1;
+ int32_t value2;
+ if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) {
+ cur = reinterpret_cast<byte*>(value1);
+ end = cur + (value2 * Instruction::kInstrSize);
+ }
+ }
+
+ while (cur < end) {
+ dasm.InstructionDecode(buffer, cur);
+ PrintF(" 0x%08x %s\n",
+ reinterpret_cast<intptr_t>(cur), buffer.start());
+ cur += Instruction::kInstrSize;
+ }
+ } else if (strcmp(cmd, "gdb") == 0) {
+ PrintF("relinquishing control to gdb\n");
+ v8::internal::OS::DebugBreak();
+ PrintF("regaining control from gdb\n");
+ } else if (strcmp(cmd, "break") == 0) {
+ if (argc == 2) {
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ if (!SetBreakpoint(reinterpret_cast<Instruction*>(value))) {
+ PrintF("setting breakpoint failed\n");
+ }
+ } else {
+ PrintF("%s unrecognized\n", arg1);
+ }
+ } else {
+ PrintF("break <address>\n");
+ }
+ } else if (strcmp(cmd, "del") == 0) {
+ if (!DeleteBreakpoint(NULL)) {
+ PrintF("deleting breakpoint failed\n");
+ }
+ } else if (strcmp(cmd, "flags") == 0) {
+ PrintF("No flags on MIPS !\n");
+ } else if (strcmp(cmd, "stop") == 0) {
+ int32_t value;
+ intptr_t stop_pc = sim_->get_pc() -
+ 2 * Instruction::kInstrSize;
+ Instruction* stop_instr = reinterpret_cast<Instruction*>(stop_pc);
+ Instruction* msg_address =
+ reinterpret_cast<Instruction*>(stop_pc +
+ Instruction::kInstrSize);
+ if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
+ // Remove the current stop.
+ if (sim_->IsStopInstruction(stop_instr)) {
+ stop_instr->SetInstructionBits(kNopInstr);
+ msg_address->SetInstructionBits(kNopInstr);
+ } else {
+ PrintF("Not at debugger stop.\n");
+ }
+ } else if (argc == 3) {
+ // Print information about all/the specified breakpoint(s).
+ if (strcmp(arg1, "info") == 0) {
+ if (strcmp(arg2, "all") == 0) {
+ PrintF("Stop information:\n");
+ for (uint32_t i = kMaxWatchpointCode + 1;
+ i <= kMaxStopCode;
+ i++) {
+ sim_->PrintStopInfo(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->PrintStopInfo(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ } else if (strcmp(arg1, "enable") == 0) {
+ // Enable all/the specified breakpoint(s).
+ if (strcmp(arg2, "all") == 0) {
+ for (uint32_t i = kMaxWatchpointCode + 1;
+ i <= kMaxStopCode;
+ i++) {
+ sim_->EnableStop(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->EnableStop(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ } else if (strcmp(arg1, "disable") == 0) {
+ // Disable all/the specified breakpoint(s).
+ if (strcmp(arg2, "all") == 0) {
+ for (uint32_t i = kMaxWatchpointCode + 1;
+ i <= kMaxStopCode;
+ i++) {
+ sim_->DisableStop(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->DisableStop(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ }
+ } else {
+ PrintF("Wrong usage. Use help command for more information.\n");
+ }
+ } else if ((strcmp(cmd, "stat") == 0) || (strcmp(cmd, "st") == 0)) {
+ // Print registers and disassemble.
+ PrintAllRegs();
+ PrintF("\n");
+
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // Use a reasonably large buffer.
+ v8::internal::EmbeddedVector<char, 256> buffer;
+
+ byte* cur = NULL;
+ byte* end = NULL;
+
+ if (argc == 1) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ end = cur + (10 * Instruction::kInstrSize);
+ } else if (argc == 2) {
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(value);
+ // no length parameter passed, assume 10 instructions
+ end = cur + (10 * Instruction::kInstrSize);
+ }
+ } else {
+ int32_t value1;
+ int32_t value2;
+ if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) {
+ cur = reinterpret_cast<byte*>(value1);
+ end = cur + (value2 * Instruction::kInstrSize);
+ }
+ }
+
+ while (cur < end) {
+ dasm.InstructionDecode(buffer, cur);
+ PrintF(" 0x%08x %s\n",
+ reinterpret_cast<intptr_t>(cur), buffer.start());
+ cur += Instruction::kInstrSize;
+ }
+ } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
+ PrintF("cont\n");
+ PrintF(" continue execution (alias 'c')\n");
+ PrintF("stepi\n");
+ PrintF(" step one instruction (alias 'si')\n");
+ PrintF("print <register>\n");
+ PrintF(" print register content (alias 'p')\n");
+ PrintF(" use register name 'all' to print all registers\n");
+ PrintF("printobject <register>\n");
+ PrintF(" print an object from a register (alias 'po')\n");
+ PrintF("stack [<words>]\n");
+ PrintF(" dump stack content, default dump 10 words)\n");
+ PrintF("mem <address> [<words>]\n");
+ PrintF(" dump memory content, default dump 10 words)\n");
+ PrintF("flags\n");
+ PrintF(" print flags\n");
+ PrintF("disasm [<instructions>]\n");
+ PrintF("disasm [<address/register>]\n");
+ PrintF("disasm [[<address/register>] <instructions>]\n");
+ PrintF(" disassemble code, default is 10 instructions\n");
+ PrintF(" from pc (alias 'di')\n");
+ PrintF("gdb\n");
+ PrintF(" enter gdb\n");
+ PrintF("break <address>\n");
+ PrintF(" set a break point on the address\n");
+ PrintF("del\n");
+ PrintF(" delete the breakpoint\n");
+ PrintF("stop feature:\n");
+ PrintF(" Description:\n");
+ PrintF(" Stops are debug instructions inserted by\n");
+ PrintF(" the Assembler::stop() function.\n");
+ PrintF(" When hitting a stop, the Simulator will\n");
+ PrintF(" stop and and give control to the Debugger.\n");
+ PrintF(" All stop codes are watched:\n");
+ PrintF(" - They can be enabled / disabled: the Simulator\n");
+ PrintF(" will / won't stop when hitting them.\n");
+ PrintF(" - The Simulator keeps track of how many times they \n");
+ PrintF(" are met. (See the info command.) Going over a\n");
+ PrintF(" disabled stop still increases its counter. \n");
+ PrintF(" Commands:\n");
+ PrintF(" stop info all/<code> : print infos about number <code>\n");
+ PrintF(" or all stop(s).\n");
+ PrintF(" stop enable/disable all/<code> : enables / disables\n");
+ PrintF(" all or number <code> stop(s)\n");
+ PrintF(" stop unstop\n");
+ PrintF(" ignore the stop instruction at the current location\n");
+ PrintF(" from now on\n");
+ } else {
+ PrintF("Unknown command: %s\n", cmd);
+ }
+ }
+ }
+
+ // Add all the breakpoints back to stop execution and enter the debugger
+ // shell when hit.
+ RedoBreakpoints();
+
+#undef COMMAND_SIZE
+#undef ARG_SIZE
+
+#undef STR
+#undef XSTR
+}
+
+
+static bool ICacheMatch(void* one, void* two) {
+ ASSERT((reinterpret_cast<intptr_t>(one) & CachePage::kPageMask) == 0);
+ ASSERT((reinterpret_cast<intptr_t>(two) & CachePage::kPageMask) == 0);
+ return one == two;
+}
+
+
+static uint32_t ICacheHash(void* key) {
+ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key)) >> 2;
+}
+
+
+static bool AllOnOnePage(uintptr_t start, int size) {
+ intptr_t start_page = (start & ~CachePage::kPageMask);
+ intptr_t end_page = ((start + size) & ~CachePage::kPageMask);
+ return start_page == end_page;
+}
+
+
+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) {
+ intptr_t start = reinterpret_cast<intptr_t>(start_addr);
+ int intra_line = (start & CachePage::kLineMask);
+ start -= intra_line;
+ size += intra_line;
+ size = ((size - 1) | CachePage::kLineMask) + 1;
+ int offset = (start & CachePage::kPageMask);
+ while (!AllOnOnePage(start, size - 1)) {
+ int bytes_to_flush = CachePage::kPageSize - offset;
+ FlushOnePage(i_cache, start, bytes_to_flush);
+ start += bytes_to_flush;
+ size -= bytes_to_flush;
+ ASSERT_EQ(0, start & CachePage::kPageMask);
+ offset = 0;
+ }
+ if (size != 0) {
+ FlushOnePage(i_cache, start, size);
+ }
+}
+
+
+CachePage* Simulator::GetCachePage(v8::internal::HashMap* i_cache, void* page) {
+ v8::internal::HashMap::Entry* entry = i_cache->Lookup(page,
+ ICacheHash(page),
+ true);
+ if (entry->value == NULL) {
+ CachePage* new_page = new CachePage();
+ entry->value = new_page;
+ }
+ return reinterpret_cast<CachePage*>(entry->value);
+}
+
+
+// Flush from start up to and not including start + size.
+void Simulator::FlushOnePage(v8::internal::HashMap* i_cache,
+ intptr_t start,
+ int size) {
+ ASSERT(size <= CachePage::kPageSize);
+ ASSERT(AllOnOnePage(start, size - 1));
+ ASSERT((start & CachePage::kLineMask) == 0);
+ ASSERT((size & CachePage::kLineMask) == 0);
+ void* page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
+ int offset = (start & CachePage::kPageMask);
+ CachePage* cache_page = GetCachePage(i_cache, page);
+ char* valid_bytemap = cache_page->ValidityByte(offset);
+ memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
+}
+
+
+void Simulator::CheckICache(v8::internal::HashMap* i_cache,
+ Instruction* instr) {
+ intptr_t address = reinterpret_cast<intptr_t>(instr);
+ void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
+ void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
+ int offset = (address & CachePage::kPageMask);
+ CachePage* cache_page = GetCachePage(i_cache, page);
+ char* cache_valid_byte = cache_page->ValidityByte(offset);
+ bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
+ char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask);
+ if (cache_hit) {
+ // Check that the data in memory matches the contents of the I-cache.
+ CHECK(memcmp(reinterpret_cast<void*>(instr),
+ cache_page->CachedData(offset),
+ Instruction::kInstrSize) == 0);
+ } else {
+ // Cache miss. Load memory into the cache.
+ OS::MemCopy(cached_line, line, CachePage::kLineLength);
+ *cache_valid_byte = CachePage::LINE_VALID;
+ }
+}
+
+
+void Simulator::Initialize(Isolate* isolate) {
+ if (isolate->simulator_initialized()) return;
+ isolate->set_simulator_initialized(true);
+ ::v8::internal::ExternalReference::set_redirector(isolate,
+ &RedirectExternalReference);
+}
+
+
+Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
+ i_cache_ = isolate_->simulator_i_cache();
+ if (i_cache_ == NULL) {
+ i_cache_ = new v8::internal::HashMap(&ICacheMatch);
+ isolate_->set_simulator_i_cache(i_cache_);
+ }
+ Initialize(isolate);
+ // 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;
+ icount_ = 0;
+ break_count_ = 0;
+ break_pc_ = NULL;
+ break_instr_ = 0;
+
+ // Set up architecture state.
+ // All registers are initialized to zero to start with.
+ for (int i = 0; i < kNumSimuRegisters; i++) {
+ registers_[i] = 0;
+ }
+ for (int i = 0; i < kNumFPURegisters; i++) {
+ FPUregisters_[i] = 0;
+ }
+ FCSR_ = 0;
+
+ // The sp is initialized to point to the bottom (high address) of the
+ // allocated stack area. To be safe in potential stack underflows we leave
+ // some buffer below.
+ registers_[sp] = reinterpret_cast<int32_t>(stack_) + stack_size_ - 64;
+ // The ra and pc are initialized to a known bad value that will cause an
+ // access violation if the simulator ever tries to execute it.
+ registers_[pc] = bad_ra;
+ registers_[ra] = bad_ra;
+ InitializeCoverage();
+ for (int i = 0; i < kNumExceptions; i++) {
+ exceptions[i] = 0;
+ }
+
+ last_debugger_input_ = NULL;
+}
+
+
+// When the generated code calls an external reference we need to catch that in
+// the simulator. The external reference will be a function compiled for the
+// host architecture. We need to call that function instead of trying to
+// execute it with the simulator. We do that by redirecting the external
+// reference to a swi (software-interrupt) instruction that is handled by
+// the simulator. We write the original destination of the jump just at a known
+// offset from the swi instruction so the simulator knows what to call.
+class Redirection {
+ public:
+ Redirection(void* external_function, ExternalReference::Type type)
+ : external_function_(external_function),
+ swi_instruction_(rtCallRedirInstr),
+ type_(type),
+ next_(NULL) {
+ Isolate* isolate = Isolate::Current();
+ next_ = isolate->simulator_redirection();
+ Simulator::current(isolate)->
+ FlushICache(isolate->simulator_i_cache(),
+ reinterpret_cast<void*>(&swi_instruction_),
+ Instruction::kInstrSize);
+ isolate->set_simulator_redirection(this);
+ }
+
+ void* address_of_swi_instruction() {
+ return reinterpret_cast<void*>(&swi_instruction_);
+ }
+
+ void* external_function() { return external_function_; }
+ ExternalReference::Type type() { return type_; }
+
+ static Redirection* Get(void* external_function,
+ ExternalReference::Type type) {
+ Isolate* isolate = Isolate::Current();
+ Redirection* current = isolate->simulator_redirection();
+ for (; current != NULL; current = current->next_) {
+ if (current->external_function_ == external_function) return current;
+ }
+ return new Redirection(external_function, type);
+ }
+
+ static Redirection* FromSwiInstruction(Instruction* swi_instruction) {
+ char* addr_of_swi = reinterpret_cast<char*>(swi_instruction);
+ char* addr_of_redirection =
+ addr_of_swi - OFFSET_OF(Redirection, swi_instruction_);
+ return reinterpret_cast<Redirection*>(addr_of_redirection);
+ }
+
+ private:
+ void* external_function_;
+ uint32_t swi_instruction_;
+ ExternalReference::Type type_;
+ Redirection* next_;
+};
+
+
+void* Simulator::RedirectExternalReference(void* external_function,
+ ExternalReference::Type type) {
+ Redirection* redirection = Redirection::Get(external_function, type);
+ return redirection->address_of_swi_instruction();
+}
+
+
+// Get the active Simulator for the current thread.
+Simulator* Simulator::current(Isolate* isolate) {
+ v8::internal::Isolate::PerIsolateThreadData* isolate_data =
+ isolate->FindOrAllocatePerThreadDataForThisThread();
+ ASSERT(isolate_data != NULL);
+ ASSERT(isolate_data != NULL);
+
+ Simulator* sim = isolate_data->simulator();
+ if (sim == NULL) {
+ // TODO(146): delete the simulator object when a thread/isolate goes away.
+ sim = new Simulator(isolate);
+ isolate_data->set_simulator(sim);
+ }
+ return sim;
+}
+
+
+// Sets the register in the architecture state. It will also deal with updating
+// Simulator internal state for special registers such as PC.
+void Simulator::set_register(int reg, int32_t value) {
+ ASSERT((reg >= 0) && (reg < kNumSimuRegisters));
+ if (reg == pc) {
+ pc_modified_ = true;
+ }
+
+ // Zero register always holds 0.
+ registers_[reg] = (reg == 0) ? 0 : value;
+}
+
+
+void Simulator::set_dw_register(int reg, const int* dbl) {
+ ASSERT((reg >= 0) && (reg < kNumSimuRegisters));
+ registers_[reg] = dbl[0];
+ registers_[reg + 1] = dbl[1];
+}
+
+
+void Simulator::set_fpu_register(int fpureg, int32_t value) {
+ ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters));
+ FPUregisters_[fpureg] = value;
+}
+
+
+void Simulator::set_fpu_register_float(int fpureg, float value) {
+ ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters));
+ *BitCast<float*>(&FPUregisters_[fpureg]) = value;
+}
+
+
+void Simulator::set_fpu_register_double(int fpureg, double value) {
+ ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0));
+ *BitCast<double*>(&FPUregisters_[fpureg]) = value;
+}
+
+
+// Get the register from the architecture state. This function does handle
+// the special case of accessing the PC register.
+int32_t Simulator::get_register(int reg) const {
+ ASSERT((reg >= 0) && (reg < kNumSimuRegisters));
+ if (reg == 0)
+ return 0;
+ else
+ return registers_[reg] + ((reg == pc) ? Instruction::kPCReadOffset : 0);
+}
+
+
+double Simulator::get_double_from_register_pair(int reg) {
+ ASSERT((reg >= 0) && (reg < kNumSimuRegisters) && ((reg % 2) == 0));
+
+ double dm_val = 0.0;
+ // Read the bits from the unsigned integer register_[] array
+ // into the double precision floating point value and return it.
+ char buffer[2 * sizeof(registers_[0])];
+ OS::MemCopy(buffer, &registers_[reg], 2 * sizeof(registers_[0]));
+ OS::MemCopy(&dm_val, buffer, 2 * sizeof(registers_[0]));
+ return(dm_val);
+}
+
+
+int32_t Simulator::get_fpu_register(int fpureg) const {
+ ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters));
+ return FPUregisters_[fpureg];
+}
+
+
+int64_t Simulator::get_fpu_register_long(int fpureg) const {
+ ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0));
+ return *BitCast<int64_t*>(
+ const_cast<int32_t*>(&FPUregisters_[fpureg]));
+}
+
+
+float Simulator::get_fpu_register_float(int fpureg) const {
+ ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters));
+ return *BitCast<float*>(
+ const_cast<int32_t*>(&FPUregisters_[fpureg]));
+}
+
+
+double Simulator::get_fpu_register_double(int fpureg) const {
+ ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters) && ((fpureg % 2) == 0));
+ return *BitCast<double*>(const_cast<int32_t*>(&FPUregisters_[fpureg]));
+}
+
+
+// Runtime FP routines take up to two double arguments and zero
+// or one integer arguments. All are constructed here,
+// from a0-a3 or f12 and f14.
+void Simulator::GetFpArgs(double* x, double* y, int32_t* z) {
+ if (!IsMipsSoftFloatABI) {
+ *x = get_fpu_register_double(12);
+ *y = get_fpu_register_double(14);
+ *z = get_register(a2);
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+
+ // Registers a0 and a1 -> x.
+ reg_buffer[0] = get_register(a0);
+ reg_buffer[1] = get_register(a1);
+ OS::MemCopy(x, buffer, sizeof(buffer));
+ // Registers a2 and a3 -> y.
+ reg_buffer[0] = get_register(a2);
+ reg_buffer[1] = get_register(a3);
+ OS::MemCopy(y, buffer, sizeof(buffer));
+ // Register 2 -> z.
+ reg_buffer[0] = get_register(a2);
+ OS::MemCopy(z, buffer, sizeof(*z));
+ }
+}
+
+
+// The return value is either in v0/v1 or f0.
+void Simulator::SetFpResult(const double& result) {
+ if (!IsMipsSoftFloatABI) {
+ set_fpu_register_double(0, result);
+ } else {
+ char buffer[2 * sizeof(registers_[0])];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+ OS::MemCopy(buffer, &result, sizeof(buffer));
+ // Copy result to v0 and v1.
+ set_register(v0, reg_buffer[0]);
+ set_register(v1, reg_buffer[1]);
+ }
+}
+
+
+// Helper functions for setting and testing the FCSR register's bits.
+void Simulator::set_fcsr_bit(uint32_t cc, bool value) {
+ if (value) {
+ FCSR_ |= (1 << cc);
+ } else {
+ FCSR_ &= ~(1 << cc);
+ }
+}
+
+
+bool Simulator::test_fcsr_bit(uint32_t cc) {
+ return FCSR_ & (1 << cc);
+}
+
+
+// Sets the rounding error codes in FCSR based on the result of the rounding.
+// Returns true if the operation was invalid.
+bool Simulator::set_fcsr_round_error(double original, double rounded) {
+ bool ret = false;
+
+ if (!std::isfinite(original) || !std::isfinite(rounded)) {
+ set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
+ ret = true;
+ }
+
+ if (original != rounded) {
+ set_fcsr_bit(kFCSRInexactFlagBit, true);
+ }
+
+ if (rounded < DBL_MIN && rounded > -DBL_MIN && rounded != 0) {
+ set_fcsr_bit(kFCSRUnderflowFlagBit, true);
+ ret = true;
+ }
+
+ if (rounded > INT_MAX || rounded < INT_MIN) {
+ set_fcsr_bit(kFCSROverflowFlagBit, true);
+ // The reference is not really clear but it seems this is required:
+ set_fcsr_bit(kFCSRInvalidOpFlagBit, true);
+ ret = true;
+ }
+
+ return ret;
+}
+
+
+// Raw access to the PC register.
+void Simulator::set_pc(int32_t value) {
+ pc_modified_ = true;
+ registers_[pc] = value;
+}
+
+
+bool Simulator::has_bad_pc() const {
+ return ((registers_[pc] == bad_ra) || (registers_[pc] == end_sim_pc));
+}
+
+
+// Raw access to the PC register without the special adjustment when reading.
+int32_t Simulator::get_pc() const {
+ return registers_[pc];
+}
+
+
+// The MIPS cannot do unaligned reads and writes. On some MIPS platforms an
+// interrupt is caused. On others it does a funky rotation thing. For now we
+// simply disallow unaligned reads, but at some point we may want to move to
+// emulating the rotate behaviour. Note that simulator runs have the runtime
+// system running directly on the host system and only generated code is
+// executed in the simulator. Since the host is typically IA32 we will not
+// get the correct MIPS-like behaviour on unaligned accesses.
+
+int Simulator::ReadW(int32_t addr, Instruction* instr) {
+ if (addr >=0 && addr < 0x400) {
+ // This has to be a NULL-dereference, drop into debugger.
+ PrintF("Memory read from bad address: 0x%08x, pc=0x%08x\n",
+ addr, reinterpret_cast<intptr_t>(instr));
+ MipsDebugger dbg(this);
+ dbg.Debug();
+ }
+ if ((addr & kPointerAlignmentMask) == 0) {
+ intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
+ return *ptr;
+ }
+ PrintF("Unaligned read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ MipsDebugger dbg(this);
+ dbg.Debug();
+ return 0;
+}
+
+
+void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
+ if (addr >= 0 && addr < 0x400) {
+ // This has to be a NULL-dereference, drop into debugger.
+ PrintF("Memory write to bad address: 0x%08x, pc=0x%08x\n",
+ addr, reinterpret_cast<intptr_t>(instr));
+ MipsDebugger dbg(this);
+ dbg.Debug();
+ }
+ if ((addr & kPointerAlignmentMask) == 0) {
+ intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
+ *ptr = value;
+ return;
+ }
+ PrintF("Unaligned write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ MipsDebugger dbg(this);
+ dbg.Debug();
+}
+
+
+double Simulator::ReadD(int32_t addr, Instruction* instr) {
+ if ((addr & kDoubleAlignmentMask) == 0) {
+ double* ptr = reinterpret_cast<double*>(addr);
+ return *ptr;
+ }
+ PrintF("Unaligned (double) read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ OS::Abort();
+ return 0;
+}
+
+
+void Simulator::WriteD(int32_t addr, double value, Instruction* instr) {
+ if ((addr & kDoubleAlignmentMask) == 0) {
+ double* ptr = reinterpret_cast<double*>(addr);
+ *ptr = value;
+ return;
+ }
+ PrintF("Unaligned (double) write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ OS::Abort();
+}
+
+
+uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) {
+ if ((addr & 1) == 0) {
+ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
+ return *ptr;
+ }
+ PrintF("Unaligned unsigned halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ OS::Abort();
+ return 0;
+}
+
+
+int16_t Simulator::ReadH(int32_t addr, Instruction* instr) {
+ if ((addr & 1) == 0) {
+ int16_t* ptr = reinterpret_cast<int16_t*>(addr);
+ return *ptr;
+ }
+ PrintF("Unaligned signed halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ OS::Abort();
+ return 0;
+}
+
+
+void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) {
+ if ((addr & 1) == 0) {
+ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
+ *ptr = value;
+ return;
+ }
+ PrintF("Unaligned unsigned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ OS::Abort();
+}
+
+
+void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) {
+ if ((addr & 1) == 0) {
+ int16_t* ptr = reinterpret_cast<int16_t*>(addr);
+ *ptr = value;
+ return;
+ }
+ PrintF("Unaligned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
+ OS::Abort();
+}
+
+
+uint32_t Simulator::ReadBU(int32_t addr) {
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
+ return *ptr & 0xff;
+}
+
+
+int32_t Simulator::ReadB(int32_t addr) {
+ int8_t* ptr = reinterpret_cast<int8_t*>(addr);
+ return *ptr;
+}
+
+
+void Simulator::WriteB(int32_t addr, uint8_t value) {
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
+ *ptr = value;
+}
+
+
+void Simulator::WriteB(int32_t addr, int8_t value) {
+ int8_t* ptr = reinterpret_cast<int8_t*>(addr);
+ *ptr = value;
+}
+
+
+// Returns the limit of the stack area to enable checking for stack overflows.
+uintptr_t Simulator::StackLimit() const {
+ // Leave a safety margin of 1024 bytes to prevent overrunning the stack when
+ // pushing values.
+ return reinterpret_cast<uintptr_t>(stack_) + 1024;
+}
+
+
+// Unsupported instructions use Format to print an error and stop execution.
+void Simulator::Format(Instruction* instr, const char* format) {
+ PrintF("Simulator found unsupported instruction:\n 0x%08x: %s\n",
+ reinterpret_cast<intptr_t>(instr), format);
+ UNIMPLEMENTED_MIPS();
+}
+
+
+// Calls into the V8 runtime are based on this very simple interface.
+// Note: To be able to return two values from some calls the code in runtime.cc
+// uses the ObjectPair which is essentially two 32-bit values stuffed into a
+// 64-bit value. With the code below we assume that all runtime calls return
+// 64 bits of result. If they don't, the v1 result register contains a bogus
+// value, which is fine because it is caller-saved.
+typedef int64_t (*SimulatorRuntimeCall)(int32_t arg0,
+ int32_t arg1,
+ int32_t arg2,
+ int32_t arg3,
+ int32_t arg4,
+ int32_t arg5);
+
+// These prototypes handle the four types of FP calls.
+typedef int64_t (*SimulatorRuntimeCompareCall)(double darg0, double darg1);
+typedef double (*SimulatorRuntimeFPFPCall)(double darg0, double darg1);
+typedef double (*SimulatorRuntimeFPCall)(double darg0);
+typedef double (*SimulatorRuntimeFPIntCall)(double darg0, int32_t arg0);
+
+// This signature supports direct call in to API function native callback
+// (refer to InvocationCallback in v8.h).
+// 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 first argument in a1 instead of a0.
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeDirectApiCall)(int32_t arg0);
+// Here, we pass the first argument in a0, because this function
+// does not return a struct.
+typedef void (*SimulatorRuntimeDirectApiCallNew)(int32_t arg0);
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeProfilingApiCall)(
+ int32_t arg0, int32_t arg1);
+typedef void (*SimulatorRuntimeProfilingApiCallNew)(int32_t arg0, int32_t arg1);
+
+// This signature supports direct call to accessor getter callback.
+// See comment at SimulatorRuntimeDirectApiCall.
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeDirectGetterCall)(int32_t arg0,
+ int32_t arg1);
+// See comment at SimulatorRuntimeDirectApiCallNew.
+typedef void (*SimulatorRuntimeDirectGetterCallNew)(int32_t arg0,
+ int32_t arg1);
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeProfilingGetterCall)(
+ int32_t arg0, int32_t arg1, int32_t arg2);
+typedef void (*SimulatorRuntimeProfilingGetterCallNew)(
+ int32_t arg0, int32_t arg1, int32_t arg2);
+
+// Software interrupt instructions are used by the simulator to call into the
+// C-based V8 runtime. They are also used for debugging with simulator.
+void Simulator::SoftwareInterrupt(Instruction* instr) {
+ // There are several instructions that could get us here,
+ // the break_ instruction, or several variants of traps. All
+ // Are "SPECIAL" class opcode, and are distinuished by function.
+ int32_t func = instr->FunctionFieldRaw();
+ uint32_t code = (func == BREAK) ? instr->Bits(25, 6) : -1;
+
+ // We first check if we met a call_rt_redirected.
+ if (instr->InstructionBits() == rtCallRedirInstr) {
+ Redirection* redirection = Redirection::FromSwiInstruction(instr);
+ int32_t arg0 = get_register(a0);
+ int32_t arg1 = get_register(a1);
+ int32_t arg2 = get_register(a2);
+ int32_t arg3 = get_register(a3);
+
+ int32_t* stack_pointer = reinterpret_cast<int32_t*>(get_register(sp));
+ // Args 4 and 5 are on the stack after the reserved space for args 0..3.
+ int32_t arg4 = stack_pointer[4];
+ int32_t arg5 = stack_pointer[5];
+
+ bool fp_call =
+ (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_FP_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL);
+
+ if (!IsMipsSoftFloatABI) {
+ // With the hard floating point calling convention, double
+ // arguments are passed in FPU registers. Fetch the arguments
+ // from there and call the builtin using soft floating point
+ // convention.
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ arg2 = get_fpu_register(f14);
+ arg3 = get_fpu_register(f15);
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ arg2 = get_register(a2);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // This is dodgy but it works because the C entry stubs are never moved.
+ // See comment in codegen-arm.cc and bug 1242173.
+ int32_t saved_ra = get_register(ra);
+
+ intptr_t external =
+ reinterpret_cast<intptr_t>(redirection->external_function());
+
+ // Based on CpuFeatures::IsSupported(FPU), Mips will use either hardware
+ // FPU, or gcc soft-float routines. Hardware FPU is simulated in this
+ // simulator. Soft-float has additional abstraction of ExternalReference,
+ // to support serialization.
+ if (fp_call) {
+ double dval0, dval1; // one or two double parameters
+ int32_t ival; // zero or one integer parameters
+ int64_t iresult = 0; // integer return value
+ double dresult = 0; // double return value
+ GetFpArgs(&dval0, &dval1, &ival);
+ SimulatorRuntimeCall generic_target =
+ reinterpret_cast<SimulatorRuntimeCall>(external);
+ if (::v8::internal::FLAG_trace_sim) {
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ PrintF("Call to host function at %p with args %f, %f",
+ FUNCTION_ADDR(generic_target), dval0, dval1);
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ PrintF("Call to host function at %p with arg %f",
+ FUNCTION_ADDR(generic_target), dval0);
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ PrintF("Call to host function at %p with args %f, %d",
+ FUNCTION_ADDR(generic_target), dval0, ival);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_COMPARE_CALL: {
+ SimulatorRuntimeCompareCall target =
+ reinterpret_cast<SimulatorRuntimeCompareCall>(external);
+ iresult = target(dval0, dval1);
+ set_register(v0, static_cast<int32_t>(iresult));
+ set_register(v1, static_cast<int32_t>(iresult >> 32));
+ break;
+ }
+ case ExternalReference::BUILTIN_FP_FP_CALL: {
+ SimulatorRuntimeFPFPCall target =
+ reinterpret_cast<SimulatorRuntimeFPFPCall>(external);
+ dresult = target(dval0, dval1);
+ SetFpResult(dresult);
+ break;
+ }
+ case ExternalReference::BUILTIN_FP_CALL: {
+ SimulatorRuntimeFPCall target =
+ reinterpret_cast<SimulatorRuntimeFPCall>(external);
+ dresult = target(dval0);
+ SetFpResult(dresult);
+ break;
+ }
+ case ExternalReference::BUILTIN_FP_INT_CALL: {
+ SimulatorRuntimeFPIntCall target =
+ reinterpret_cast<SimulatorRuntimeFPIntCall>(external);
+ dresult = target(dval0, ival);
+ SetFpResult(dresult);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ if (::v8::internal::FLAG_trace_sim) {
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ PrintF("Returned %08x\n", static_cast<int32_t>(iresult));
+ break;
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_FP_CALL:
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ PrintF("Returned %f\n", dresult);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else if (
+ redirection->type() == ExternalReference::DIRECT_API_CALL ||
+ redirection->type() == ExternalReference::DIRECT_API_CALL_NEW) {
+ if (redirection->type() == ExternalReference::DIRECT_API_CALL) {
+ // See comment at type definition of SimulatorRuntimeDirectApiCall
+ // for explanation of register usage.
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x\n",
+ reinterpret_cast<void*>(external), arg1);
+ }
+ SimulatorRuntimeDirectApiCall target =
+ reinterpret_cast<SimulatorRuntimeDirectApiCall>(external);
+ v8::Handle<v8::Value> result = target(arg1);
+ *(reinterpret_cast<int*>(arg0)) = reinterpret_cast<int32_t>(*result);
+ set_register(v0, arg0);
+ } else {
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x\n",
+ reinterpret_cast<void*>(external), arg0);
+ }
+ SimulatorRuntimeDirectApiCallNew target =
+ reinterpret_cast<SimulatorRuntimeDirectApiCallNew>(external);
+ target(arg0);
+ }
+ } else if (
+ redirection->type() == ExternalReference::PROFILING_API_CALL ||
+ redirection->type() == ExternalReference::PROFILING_API_CALL_NEW) {
+ if (redirection->type() == ExternalReference::PROFILING_API_CALL) {
+ // See comment at type definition of SimulatorRuntimeDirectApiCall
+ // for explanation of register usage.
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x %08x\n",
+ reinterpret_cast<void*>(external), arg1, arg2);
+ }
+ SimulatorRuntimeProfilingApiCall target =
+ reinterpret_cast<SimulatorRuntimeProfilingApiCall>(external);
+ v8::Handle<v8::Value> result = target(arg1, arg2);
+ *(reinterpret_cast<int*>(arg0)) = reinterpret_cast<int32_t>(*result);
+ set_register(v0, arg0);
+ } else {
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x %08x\n",
+ reinterpret_cast<void*>(external), arg0, arg1);
+ }
+ SimulatorRuntimeProfilingApiCallNew target =
+ reinterpret_cast<SimulatorRuntimeProfilingApiCallNew>(external);
+ target(arg0, arg1);
+ }
+ } else if (
+ redirection->type() == ExternalReference::DIRECT_GETTER_CALL ||
+ redirection->type() == ExternalReference::DIRECT_GETTER_CALL_NEW) {
+ if (redirection->type() == ExternalReference::DIRECT_GETTER_CALL) {
+ // See comment at type definition of SimulatorRuntimeDirectGetterCall
+ // for explanation of register usage.
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x %08x\n",
+ reinterpret_cast<void*>(external), arg1, arg2);
+ }
+ SimulatorRuntimeDirectGetterCall target =
+ reinterpret_cast<SimulatorRuntimeDirectGetterCall>(external);
+ v8::Handle<v8::Value> result = target(arg1, arg2);
+ *(reinterpret_cast<int*>(arg0)) = reinterpret_cast<int32_t>(*result);
+ set_register(v0, arg0);
+ } else {
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x %08x\n",
+ reinterpret_cast<void*>(external), arg0, arg1);
+ }
+ SimulatorRuntimeDirectGetterCallNew target =
+ reinterpret_cast<SimulatorRuntimeDirectGetterCallNew>(external);
+ target(arg0, arg1);
+ }
+ } else if (
+ redirection->type() == ExternalReference::PROFILING_GETTER_CALL ||
+ redirection->type() == ExternalReference::PROFILING_GETTER_CALL_NEW) {
+ if (redirection->type() == ExternalReference::PROFILING_GETTER_CALL) {
+ // See comment at type definition of SimulatorRuntimeProfilingGetterCall
+ // for explanation of register usage.
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x %08x %08x\n",
+ reinterpret_cast<void*>(external), arg1, arg2, arg3);
+ }
+ SimulatorRuntimeProfilingGetterCall target =
+ reinterpret_cast<SimulatorRuntimeProfilingGetterCall>(external);
+ v8::Handle<v8::Value> result = target(arg1, arg2, arg3);
+ *(reinterpret_cast<int*>(arg0)) = reinterpret_cast<int32_t>(*result);
+ set_register(v0, arg0);
+ } else {
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x %08x %08x\n",
+ reinterpret_cast<void*>(external), arg0, arg1, arg2);
+ }
+ SimulatorRuntimeProfilingGetterCallNew target =
+ reinterpret_cast<SimulatorRuntimeProfilingGetterCallNew>(external);
+ target(arg0, arg1, arg2);
+ }
+ } else {
+ SimulatorRuntimeCall target =
+ reinterpret_cast<SimulatorRuntimeCall>(external);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF(
+ "Call to host function at %p "
+ "args %08x, %08x, %08x, %08x, %08x, %08x\n",
+ FUNCTION_ADDR(target),
+ arg0,
+ arg1,
+ arg2,
+ arg3,
+ arg4,
+ arg5);
+ }
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
+ set_register(v0, static_cast<int32_t>(result));
+ set_register(v1, static_cast<int32_t>(result >> 32));
+ }
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %08x : %08x\n", get_register(v1), get_register(v0));
+ }
+ set_register(ra, saved_ra);
+ set_pc(get_register(ra));
+
+ } else if (func == BREAK && code <= kMaxStopCode) {
+ if (IsWatchpoint(code)) {
+ PrintWatchpoint(code);
+ } else {
+ IncreaseStopCounter(code);
+ HandleStop(code, instr);
+ }
+ } else {
+ // All remaining break_ codes, and all traps are handled here.
+ MipsDebugger dbg(this);
+ dbg.Debug();
+ }
+}
+
+
+// Stop helper functions.
+bool Simulator::IsWatchpoint(uint32_t code) {
+ return (code <= kMaxWatchpointCode);
+}
+
+
+void Simulator::PrintWatchpoint(uint32_t code) {
+ MipsDebugger dbg(this);
+ ++break_count_;
+ PrintF("\n---- break %d marker: %3d (instr count: %8d) ----------"
+ "----------------------------------",
+ code, break_count_, icount_);
+ dbg.PrintAllRegs(); // Print registers and continue running.
+}
+
+
+void Simulator::HandleStop(uint32_t code, Instruction* instr) {
+ // Stop if it is enabled, otherwise go on jumping over the stop
+ // and the message address.
+ if (IsEnabledStop(code)) {
+ MipsDebugger dbg(this);
+ dbg.Stop(instr);
+ } else {
+ set_pc(get_pc() + 2 * Instruction::kInstrSize);
+ }
+}
+
+
+bool Simulator::IsStopInstruction(Instruction* instr) {
+ int32_t func = instr->FunctionFieldRaw();
+ uint32_t code = static_cast<uint32_t>(instr->Bits(25, 6));
+ return (func == BREAK) && code > kMaxWatchpointCode && code <= kMaxStopCode;
+}
+
+
+bool Simulator::IsEnabledStop(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ ASSERT(code > kMaxWatchpointCode);
+ return !(watched_stops_[code].count & kStopDisabledBit);
+}
+
+
+void Simulator::EnableStop(uint32_t code) {
+ if (!IsEnabledStop(code)) {
+ watched_stops_[code].count &= ~kStopDisabledBit;
+ }
+}
+
+
+void Simulator::DisableStop(uint32_t code) {
+ if (IsEnabledStop(code)) {
+ watched_stops_[code].count |= kStopDisabledBit;
+ }
+}
+
+
+void Simulator::IncreaseStopCounter(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ if ((watched_stops_[code].count & ~(1 << 31)) == 0x7fffffff) {
+ PrintF("Stop counter for code %i has overflowed.\n"
+ "Enabling this code and reseting the counter to 0.\n", code);
+ watched_stops_[code].count = 0;
+ EnableStop(code);
+ } else {
+ watched_stops_[code].count++;
+ }
+}
+
+
+// Print a stop status.
+void Simulator::PrintStopInfo(uint32_t code) {
+ if (code <= kMaxWatchpointCode) {
+ PrintF("That is a watchpoint, not a stop.\n");
+ return;
+ } else if (code > kMaxStopCode) {
+ PrintF("Code too large, only %u stops can be used\n", kMaxStopCode + 1);
+ return;
+ }
+ const char* state = IsEnabledStop(code) ? "Enabled" : "Disabled";
+ int32_t count = watched_stops_[code].count & ~kStopDisabledBit;
+ // Don't print the state of unused breakpoints.
+ if (count != 0) {
+ if (watched_stops_[code].desc) {
+ PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n",
+ code, code, state, count, watched_stops_[code].desc);
+ } else {
+ PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n",
+ code, code, state, count);
+ }
+ }
+}
+
+
+void Simulator::SignalExceptions() {
+ for (int i = 1; i < kNumExceptions; i++) {
+ if (exceptions[i] != 0) {
+ V8_Fatal(__FILE__, __LINE__, "Error: Exception %i raised.", i);
+ }
+ }
+}
+
+
+// Handle execution based on instruction types.
+
+void Simulator::ConfigureTypeRegister(Instruction* instr,
+ int32_t& alu_out,
+ int64_t& i64hilo,
+ uint64_t& u64hilo,
+ int32_t& next_pc,
+ bool& do_interrupt) {
+ // Every local variable declared here needs to be const.
+ // This is to make sure that changed values are sent back to
+ // DecodeTypeRegister correctly.
+
+ // Instruction fields.
+ const Opcode op = instr->OpcodeFieldRaw();
+ const int32_t rs_reg = instr->RsValue();
+ const int32_t rs = get_register(rs_reg);
+ const uint32_t rs_u = static_cast<uint32_t>(rs);
+ const int32_t rt_reg = instr->RtValue();
+ const int32_t rt = get_register(rt_reg);
+ const uint32_t rt_u = static_cast<uint32_t>(rt);
+ const int32_t rd_reg = instr->RdValue();
+ const uint32_t sa = instr->SaValue();
+
+ const int32_t fs_reg = instr->FsValue();
+
+
+ // ---------- Configuration.
+ switch (op) {
+ case COP1: // Coprocessor instructions.
+ switch (instr->RsFieldRaw()) {
+ case BC1: // Handled in DecodeTypeImmed, should never come here.
+ UNREACHABLE();
+ break;
+ case CFC1:
+ // At the moment only FCSR is supported.
+ ASSERT(fs_reg == kFCSRRegister);
+ alu_out = FCSR_;
+ break;
+ case MFC1:
+ alu_out = get_fpu_register(fs_reg);
+ break;
+ case MFHC1:
+ UNIMPLEMENTED_MIPS();
+ break;
+ case CTC1:
+ case MTC1:
+ case MTHC1:
+ // Do the store in the execution step.
+ break;
+ case S:
+ case D:
+ case W:
+ case L:
+ case PS:
+ // Do everything in the execution step.
+ break;
+ default:
+ UNIMPLEMENTED_MIPS();
+ };
+ break;
+ case COP1X:
+ break;
+ case SPECIAL:
+ switch (instr->FunctionFieldRaw()) {
+ case JR:
+ case JALR:
+ next_pc = get_register(instr->RsValue());
+ break;
+ case SLL:
+ alu_out = rt << sa;
+ break;
+ case SRL:
+ if (rs_reg == 0) {
+ // Regular logical right shift of a word by a fixed number of
+ // bits instruction. RS field is always equal to 0.
+ alu_out = rt_u >> sa;
+ } else {
+ // Logical right-rotate of a word by a fixed number of bits. This
+ // is special case of SRL instruction, added in MIPS32 Release 2.
+ // RS field is equal to 00001.
+ alu_out = (rt_u >> sa) | (rt_u << (32 - sa));
+ }
+ break;
+ case SRA:
+ alu_out = rt >> sa;
+ break;
+ case SLLV:
+ alu_out = rt << rs;
+ break;
+ case SRLV:
+ if (sa == 0) {
+ // Regular logical right-shift of a word by a variable number of
+ // bits instruction. SA field is always equal to 0.
+ alu_out = rt_u >> rs;
+ } else {
+ // Logical right-rotate of a word by a variable number of bits.
+ // This is special case od SRLV instruction, added in MIPS32
+ // Release 2. SA field is equal to 00001.
+ alu_out = (rt_u >> rs_u) | (rt_u << (32 - rs_u));
+ }
+ break;
+ case SRAV:
+ alu_out = rt >> rs;
+ break;
+ case MFHI:
+ alu_out = get_register(HI);
+ break;
+ case MFLO:
+ alu_out = get_register(LO);
+ break;
+ case MULT:
+ i64hilo = static_cast<int64_t>(rs) * static_cast<int64_t>(rt);
+ break;
+ case MULTU:
+ u64hilo = static_cast<uint64_t>(rs_u) * static_cast<uint64_t>(rt_u);
+ break;
+ case ADD:
+ if (HaveSameSign(rs, rt)) {
+ if (rs > 0) {
+ exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue - rt);
+ } else if (rs < 0) {
+ exceptions[kIntegerUnderflow] = rs < (Registers::kMinValue - rt);
+ }
+ }
+ alu_out = rs + rt;
+ break;
+ case ADDU:
+ alu_out = rs + rt;
+ break;
+ case SUB:
+ if (!HaveSameSign(rs, rt)) {
+ if (rs > 0) {
+ exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue + rt);
+ } else if (rs < 0) {
+ exceptions[kIntegerUnderflow] = rs < (Registers::kMinValue + rt);
+ }
+ }
+ alu_out = rs - rt;
+ break;
+ case SUBU:
+ alu_out = rs - rt;
+ break;
+ case AND:
+ alu_out = rs & rt;
+ break;
+ case OR:
+ alu_out = rs | rt;
+ break;
+ case XOR:
+ alu_out = rs ^ rt;
+ break;
+ case NOR:
+ alu_out = ~(rs | rt);
+ break;
+ case SLT:
+ alu_out = rs < rt ? 1 : 0;
+ break;
+ case SLTU:
+ alu_out = rs_u < rt_u ? 1 : 0;
+ break;
+ // Break and trap instructions.
+ case BREAK:
+
+ do_interrupt = true;
+ break;
+ case TGE:
+ do_interrupt = rs >= rt;
+ break;
+ case TGEU:
+ do_interrupt = rs_u >= rt_u;
+ break;
+ case TLT:
+ do_interrupt = rs < rt;
+ break;
+ case TLTU:
+ do_interrupt = rs_u < rt_u;
+ break;
+ case TEQ:
+ do_interrupt = rs == rt;
+ break;
+ case TNE:
+ do_interrupt = rs != rt;
+ break;
+ case MOVN:
+ case MOVZ:
+ case MOVCI:
+ // No action taken on decode.
+ break;
+ case DIV:
+ case DIVU:
+ // div and divu never raise exceptions.
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break;
+ case SPECIAL2:
+ switch (instr->FunctionFieldRaw()) {
+ case MUL:
+ alu_out = rs_u * rt_u; // Only the lower 32 bits are kept.
+ break;
+ case CLZ:
+ alu_out = __builtin_clz(rs_u);
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break;
+ case SPECIAL3:
+ switch (instr->FunctionFieldRaw()) {
+ case INS: { // Mips32r2 instruction.
+ // Interpret rd field as 5-bit msb of insert.
+ uint16_t msb = rd_reg;
+ // Interpret sa field as 5-bit lsb of insert.
+ uint16_t lsb = sa;
+ uint16_t size = msb - lsb + 1;
+ uint32_t mask = (1 << size) - 1;
+ alu_out = (rt_u & ~(mask << lsb)) | ((rs_u & mask) << lsb);
+ break;
+ }
+ case EXT: { // Mips32r2 instruction.
+ // Interpret rd field as 5-bit msb of extract.
+ uint16_t msb = rd_reg;
+ // Interpret sa field as 5-bit lsb of extract.
+ uint16_t lsb = sa;
+ uint16_t size = msb + 1;
+ uint32_t mask = (1 << size) - 1;
+ alu_out = (rs_u & (mask << lsb)) >> lsb;
+ break;
+ }
+ default:
+ UNREACHABLE();
+ };
+ break;
+ default:
+ UNREACHABLE();
+ };
+}
+
+
+void Simulator::DecodeTypeRegister(Instruction* instr) {
+ // Instruction fields.
+ const Opcode op = instr->OpcodeFieldRaw();
+ const int32_t rs_reg = instr->RsValue();
+ const int32_t rs = get_register(rs_reg);
+ const uint32_t rs_u = static_cast<uint32_t>(rs);
+ const int32_t rt_reg = instr->RtValue();
+ const int32_t rt = get_register(rt_reg);
+ const uint32_t rt_u = static_cast<uint32_t>(rt);
+ const int32_t rd_reg = instr->RdValue();
+
+ const int32_t fr_reg = instr->FrValue();
+ const int32_t fs_reg = instr->FsValue();
+ const int32_t ft_reg = instr->FtValue();
+ const int32_t fd_reg = instr->FdValue();
+ int64_t i64hilo = 0;
+ uint64_t u64hilo = 0;
+
+ // ALU output.
+ // It should not be used as is. Instructions using it should always
+ // initialize it first.
+ int32_t alu_out = 0x12345678;
+
+ // For break and trap instructions.
+ bool do_interrupt = false;
+
+ // For jr and jalr.
+ // Get current pc.
+ int32_t current_pc = get_pc();
+ // Next pc
+ int32_t next_pc = 0;
+
+ // Set up the variables if needed before executing the instruction.
+ ConfigureTypeRegister(instr,
+ alu_out,
+ i64hilo,
+ u64hilo,
+ next_pc,
+ do_interrupt);
+
+ // ---------- Raise exceptions triggered.
+ SignalExceptions();
+
+ // ---------- Execution.
+ switch (op) {
+ case COP1:
+ switch (instr->RsFieldRaw()) {
+ case BC1: // Branch on coprocessor condition.
+ UNREACHABLE();
+ break;
+ case CFC1:
+ set_register(rt_reg, alu_out);
+ case MFC1:
+ set_register(rt_reg, alu_out);
+ break;
+ case MFHC1:
+ UNIMPLEMENTED_MIPS();
+ break;
+ case CTC1:
+ // At the moment only FCSR is supported.
+ ASSERT(fs_reg == kFCSRRegister);
+ FCSR_ = registers_[rt_reg];
+ break;
+ case MTC1:
+ FPUregisters_[fs_reg] = registers_[rt_reg];
+ break;
+ case MTHC1:
+ UNIMPLEMENTED_MIPS();
+ break;
+ case S:
+ float f;
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_S:
+ f = get_fpu_register_float(fs_reg);
+ set_fpu_register_double(fd_reg, static_cast<double>(f));
+ break;
+ case CVT_W_S:
+ case CVT_L_S:
+ case TRUNC_W_S:
+ case TRUNC_L_S:
+ case ROUND_W_S:
+ case ROUND_L_S:
+ case FLOOR_W_S:
+ case FLOOR_L_S:
+ case CEIL_W_S:
+ case CEIL_L_S:
+ case CVT_PS_S:
+ UNIMPLEMENTED_MIPS();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case D:
+ double ft, fs;
+ uint32_t cc, fcsr_cc;
+ int64_t i64;
+ fs = get_fpu_register_double(fs_reg);
+ ft = get_fpu_register_double(ft_reg);
+ cc = instr->FCccValue();
+ fcsr_cc = get_fcsr_condition_bit(cc);
+ switch (instr->FunctionFieldRaw()) {
+ case ADD_D:
+ set_fpu_register_double(fd_reg, fs + ft);
+ break;
+ case SUB_D:
+ set_fpu_register_double(fd_reg, fs - ft);
+ break;
+ case MUL_D:
+ set_fpu_register_double(fd_reg, fs * ft);
+ break;
+ case DIV_D:
+ set_fpu_register_double(fd_reg, fs / ft);
+ break;
+ case ABS_D:
+ set_fpu_register_double(fd_reg, fabs(fs));
+ break;
+ case MOV_D:
+ set_fpu_register_double(fd_reg, fs);
+ break;
+ case NEG_D:
+ set_fpu_register_double(fd_reg, -fs);
+ break;
+ case SQRT_D:
+ set_fpu_register_double(fd_reg, sqrt(fs));
+ break;
+ case C_UN_D:
+ set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft));
+ break;
+ case C_EQ_D:
+ set_fcsr_bit(fcsr_cc, (fs == ft));
+ break;
+ case C_UEQ_D:
+ set_fcsr_bit(fcsr_cc,
+ (fs == ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case C_OLT_D:
+ set_fcsr_bit(fcsr_cc, (fs < ft));
+ break;
+ case C_ULT_D:
+ set_fcsr_bit(fcsr_cc,
+ (fs < ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case C_OLE_D:
+ set_fcsr_bit(fcsr_cc, (fs <= ft));
+ break;
+ case C_ULE_D:
+ set_fcsr_bit(fcsr_cc,
+ (fs <= ft) || (std::isnan(fs) || std::isnan(ft)));
+ break;
+ case CVT_W_D: // Convert double to word.
+ // Rounding modes are not yet supported.
+ ASSERT((FCSR_ & 3) == 0);
+ // In rounding mode 0 it should behave like ROUND.
+ case ROUND_W_D: // Round double to word (round half to even).
+ {
+ double rounded = floor(fs + 0.5);
+ int32_t result = static_cast<int32_t>(rounded);
+ if ((result & 1) != 0 && result - fs == 0.5) {
+ // If the number is halfway between two integers,
+ // round to the even one.
+ result--;
+ }
+ set_fpu_register(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPUInvalidResult);
+ }
+ }
+ break;
+ case TRUNC_W_D: // Truncate double to word (round towards 0).
+ {
+ double rounded = trunc(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPUInvalidResult);
+ }
+ }
+ break;
+ case FLOOR_W_D: // Round double to word towards negative infinity.
+ {
+ double rounded = floor(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPUInvalidResult);
+ }
+ }
+ break;
+ case CEIL_W_D: // Round double to word towards positive infinity.
+ {
+ double rounded = ceil(fs);
+ int32_t result = static_cast<int32_t>(rounded);
+ set_fpu_register(fd_reg, result);
+ if (set_fcsr_round_error(fs, rounded)) {
+ set_fpu_register(fd_reg, kFPUInvalidResult);
+ }
+ }
+ break;
+ case CVT_S_D: // Convert double to float (single).
+ set_fpu_register_float(fd_reg, static_cast<float>(fs));
+ break;
+ case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word.
+ double rounded = trunc(fs);
+ i64 = static_cast<int64_t>(rounded);
+ set_fpu_register(fd_reg, i64 & 0xffffffff);
+ set_fpu_register(fd_reg + 1, i64 >> 32);
+ break;
+ }
+ case TRUNC_L_D: { // Mips32r2 instruction.
+ double rounded = trunc(fs);
+ i64 = static_cast<int64_t>(rounded);
+ set_fpu_register(fd_reg, i64 & 0xffffffff);
+ set_fpu_register(fd_reg + 1, i64 >> 32);
+ break;
+ }
+ case ROUND_L_D: { // Mips32r2 instruction.
+ double rounded = fs > 0 ? floor(fs + 0.5) : ceil(fs - 0.5);
+ i64 = static_cast<int64_t>(rounded);
+ set_fpu_register(fd_reg, i64 & 0xffffffff);
+ set_fpu_register(fd_reg + 1, i64 >> 32);
+ break;
+ }
+ case FLOOR_L_D: // Mips32r2 instruction.
+ i64 = static_cast<int64_t>(floor(fs));
+ set_fpu_register(fd_reg, i64 & 0xffffffff);
+ set_fpu_register(fd_reg + 1, i64 >> 32);
+ break;
+ case CEIL_L_D: // Mips32r2 instruction.
+ i64 = static_cast<int64_t>(ceil(fs));
+ set_fpu_register(fd_reg, i64 & 0xffffffff);
+ set_fpu_register(fd_reg + 1, i64 >> 32);
+ break;
+ case C_F_D:
+ UNIMPLEMENTED_MIPS();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case W:
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_S_W: // Convert word to float (single).
+ alu_out = get_fpu_register(fs_reg);
+ set_fpu_register_float(fd_reg, static_cast<float>(alu_out));
+ break;
+ case CVT_D_W: // Convert word to double.
+ alu_out = get_fpu_register(fs_reg);
+ set_fpu_register_double(fd_reg, static_cast<double>(alu_out));
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break;
+ case L:
+ switch (instr->FunctionFieldRaw()) {
+ case CVT_D_L: // Mips32r2 instruction.
+ // Watch the signs here, we want 2 32-bit vals
+ // to make a sign-64.
+ i64 = static_cast<uint32_t>(get_fpu_register(fs_reg));
+ i64 |= static_cast<int64_t>(get_fpu_register(fs_reg + 1)) << 32;
+ set_fpu_register_double(fd_reg, static_cast<double>(i64));
+ break;
+ case CVT_S_L:
+ UNIMPLEMENTED_MIPS();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+ case PS:
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break;
+ case COP1X:
+ switch (instr->FunctionFieldRaw()) {
+ case MADD_D:
+ double fr, ft, fs;
+ fr = get_fpu_register_double(fr_reg);
+ fs = get_fpu_register_double(fs_reg);
+ ft = get_fpu_register_double(ft_reg);
+ set_fpu_register_double(fd_reg, fs * ft + fr);
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break;
+ case SPECIAL:
+ switch (instr->FunctionFieldRaw()) {
+ case JR: {
+ Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
+ current_pc+Instruction::kInstrSize);
+ BranchDelayInstructionDecode(branch_delay_instr);
+ set_pc(next_pc);
+ pc_modified_ = true;
+ break;
+ }
+ case JALR: {
+ Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
+ current_pc+Instruction::kInstrSize);
+ BranchDelayInstructionDecode(branch_delay_instr);
+ set_register(31, current_pc + 2 * Instruction::kInstrSize);
+ set_pc(next_pc);
+ pc_modified_ = true;
+ break;
+ }
+ // Instructions using HI and LO registers.
+ case MULT:
+ set_register(LO, static_cast<int32_t>(i64hilo & 0xffffffff));
+ set_register(HI, static_cast<int32_t>(i64hilo >> 32));
+ break;
+ case MULTU:
+ set_register(LO, static_cast<int32_t>(u64hilo & 0xffffffff));
+ set_register(HI, static_cast<int32_t>(u64hilo >> 32));
+ break;
+ case DIV:
+ // Divide by zero and overflow was not checked in the configuration
+ // step - div and divu do not raise exceptions. On division by 0 and
+ // on overflow (INT_MIN/-1), the result will be UNPREDICTABLE.
+ if (rt != 0 && !(rs == INT_MIN && rt == -1)) {
+ set_register(LO, rs / rt);
+ set_register(HI, rs % rt);
+ }
+ break;
+ case DIVU:
+ if (rt_u != 0) {
+ set_register(LO, rs_u / rt_u);
+ set_register(HI, rs_u % rt_u);
+ }
+ break;
+ // Break and trap instructions.
+ case BREAK:
+ case TGE:
+ case TGEU:
+ case TLT:
+ case TLTU:
+ case TEQ:
+ case TNE:
+ if (do_interrupt) {
+ SoftwareInterrupt(instr);
+ }
+ break;
+ // Conditional moves.
+ case MOVN:
+ if (rt) set_register(rd_reg, rs);
+ break;
+ case MOVCI: {
+ uint32_t cc = instr->FBccValue();
+ uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
+ if (instr->Bit(16)) { // Read Tf bit.
+ if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
+ } else {
+ if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
+ }
+ break;
+ }
+ case MOVZ:
+ if (!rt) set_register(rd_reg, rs);
+ break;
+ default: // For other special opcodes we do the default operation.
+ set_register(rd_reg, alu_out);
+ };
+ break;
+ case SPECIAL2:
+ switch (instr->FunctionFieldRaw()) {
+ case MUL:
+ set_register(rd_reg, alu_out);
+ // HI and LO are UNPREDICTABLE after the operation.
+ set_register(LO, Unpredictable);
+ set_register(HI, Unpredictable);
+ break;
+ default: // For other special2 opcodes we do the default operation.
+ set_register(rd_reg, alu_out);
+ }
+ break;
+ case SPECIAL3:
+ switch (instr->FunctionFieldRaw()) {
+ case INS:
+ // Ins instr leaves result in Rt, rather than Rd.
+ set_register(rt_reg, alu_out);
+ break;
+ case EXT:
+ // Ext instr leaves result in Rt, rather than Rd.
+ set_register(rt_reg, alu_out);
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break;
+ // Unimplemented opcodes raised an error in the configuration step before,
+ // so we can use the default here to set the destination register in common
+ // cases.
+ default:
+ set_register(rd_reg, alu_out);
+ };
+}
+
+
+// Type 2: instructions using a 16 bytes immediate. (e.g. addi, beq).
+void Simulator::DecodeTypeImmediate(Instruction* instr) {
+ // Instruction fields.
+ Opcode op = instr->OpcodeFieldRaw();
+ int32_t rs = get_register(instr->RsValue());
+ uint32_t rs_u = static_cast<uint32_t>(rs);
+ int32_t rt_reg = instr->RtValue(); // Destination register.
+ int32_t rt = get_register(rt_reg);
+ int16_t imm16 = instr->Imm16Value();
+
+ int32_t ft_reg = instr->FtValue(); // Destination register.
+
+ // Zero extended immediate.
+ uint32_t oe_imm16 = 0xffff & imm16;
+ // Sign extended immediate.
+ int32_t se_imm16 = imm16;
+
+ // Get current pc.
+ int32_t current_pc = get_pc();
+ // Next pc.
+ int32_t next_pc = bad_ra;
+
+ // Used for conditional branch instructions.
+ bool do_branch = false;
+ bool execute_branch_delay_instruction = false;
+
+ // Used for arithmetic instructions.
+ int32_t alu_out = 0;
+ // Floating point.
+ double fp_out = 0.0;
+ uint32_t cc, cc_value, fcsr_cc;
+
+ // Used for memory instructions.
+ int32_t addr = 0x0;
+ // Value to be written in memory.
+ uint32_t mem_value = 0x0;
+
+ // ---------- Configuration (and execution for REGIMM).
+ switch (op) {
+ // ------------- COP1. Coprocessor instructions.
+ case COP1:
+ switch (instr->RsFieldRaw()) {
+ case BC1: // Branch on coprocessor condition.
+ cc = instr->FBccValue();
+ fcsr_cc = get_fcsr_condition_bit(cc);
+ cc_value = test_fcsr_bit(fcsr_cc);
+ do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value;
+ execute_branch_delay_instruction = true;
+ // Set next_pc.
+ if (do_branch) {
+ next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
+ } else {
+ next_pc = current_pc + kBranchReturnOffset;
+ }
+ break;
+ default:
+ UNREACHABLE();
+ };
+ break;
+ // ------------- REGIMM class.
+ case REGIMM:
+ switch (instr->RtFieldRaw()) {
+ case BLTZ:
+ do_branch = (rs < 0);
+ break;
+ case BLTZAL:
+ do_branch = rs < 0;
+ break;
+ case BGEZ:
+ do_branch = rs >= 0;
+ break;
+ case BGEZAL:
+ do_branch = rs >= 0;
+ break;
+ default:
+ UNREACHABLE();
+ };
+ switch (instr->RtFieldRaw()) {
+ case BLTZ:
+ case BLTZAL:
+ case BGEZ:
+ case BGEZAL:
+ // Branch instructions common part.
+ execute_branch_delay_instruction = true;
+ // Set next_pc.
+ if (do_branch) {
+ next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
+ if (instr->IsLinkingInstruction()) {
+ set_register(31, current_pc + kBranchReturnOffset);
+ }
+ } else {
+ next_pc = current_pc + kBranchReturnOffset;
+ }
+ default:
+ break;
+ };
+ break; // case REGIMM.
+ // ------------- Branch instructions.
+ // When comparing to zero, the encoding of rt field is always 0, so we don't
+ // need to replace rt with zero.
+ case BEQ:
+ do_branch = (rs == rt);
+ break;
+ case BNE:
+ do_branch = rs != rt;
+ break;
+ case BLEZ:
+ do_branch = rs <= 0;
+ break;
+ case BGTZ:
+ do_branch = rs > 0;
+ break;
+ // ------------- Arithmetic instructions.
+ case ADDI:
+ if (HaveSameSign(rs, se_imm16)) {
+ if (rs > 0) {
+ exceptions[kIntegerOverflow] = rs > (Registers::kMaxValue - se_imm16);
+ } else if (rs < 0) {
+ exceptions[kIntegerUnderflow] =
+ rs < (Registers::kMinValue - se_imm16);
+ }
+ }
+ alu_out = rs + se_imm16;
+ break;
+ case ADDIU:
+ alu_out = rs + se_imm16;
+ break;
+ case SLTI:
+ alu_out = (rs < se_imm16) ? 1 : 0;
+ break;
+ case SLTIU:
+ alu_out = (rs_u < static_cast<uint32_t>(se_imm16)) ? 1 : 0;
+ break;
+ case ANDI:
+ alu_out = rs & oe_imm16;
+ break;
+ case ORI:
+ alu_out = rs | oe_imm16;
+ break;
+ case XORI:
+ alu_out = rs ^ oe_imm16;
+ break;
+ case LUI:
+ alu_out = (oe_imm16 << 16);
+ break;
+ // ------------- Memory instructions.
+ case LB:
+ addr = rs + se_imm16;
+ alu_out = ReadB(addr);
+ break;
+ case LH:
+ addr = rs + se_imm16;
+ alu_out = ReadH(addr, instr);
+ break;
+ case LWL: {
+ // al_offset is offset of the effective address within an aligned word.
+ uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
+ uint8_t byte_shift = kPointerAlignmentMask - al_offset;
+ uint32_t mask = (1 << byte_shift * 8) - 1;
+ addr = rs + se_imm16 - al_offset;
+ alu_out = ReadW(addr, instr);
+ alu_out <<= byte_shift * 8;
+ alu_out |= rt & mask;
+ break;
+ }
+ case LW:
+ addr = rs + se_imm16;
+ alu_out = ReadW(addr, instr);
+ break;
+ case LBU:
+ addr = rs + se_imm16;
+ alu_out = ReadBU(addr);
+ break;
+ case LHU:
+ addr = rs + se_imm16;
+ alu_out = ReadHU(addr, instr);
+ break;
+ case LWR: {
+ // al_offset is offset of the effective address within an aligned word.
+ uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
+ uint8_t byte_shift = kPointerAlignmentMask - al_offset;
+ uint32_t mask = al_offset ? (~0 << (byte_shift + 1) * 8) : 0;
+ addr = rs + se_imm16 - al_offset;
+ alu_out = ReadW(addr, instr);
+ alu_out = static_cast<uint32_t> (alu_out) >> al_offset * 8;
+ alu_out |= rt & mask;
+ break;
+ }
+ case SB:
+ addr = rs + se_imm16;
+ break;
+ case SH:
+ addr = rs + se_imm16;
+ break;
+ case SWL: {
+ uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
+ uint8_t byte_shift = kPointerAlignmentMask - al_offset;
+ uint32_t mask = byte_shift ? (~0 << (al_offset + 1) * 8) : 0;
+ addr = rs + se_imm16 - al_offset;
+ mem_value = ReadW(addr, instr) & mask;
+ mem_value |= static_cast<uint32_t>(rt) >> byte_shift * 8;
+ break;
+ }
+ case SW:
+ addr = rs + se_imm16;
+ break;
+ case SWR: {
+ uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
+ uint32_t mask = (1 << al_offset * 8) - 1;
+ addr = rs + se_imm16 - al_offset;
+ mem_value = ReadW(addr, instr);
+ mem_value = (rt << al_offset * 8) | (mem_value & mask);
+ break;
+ }
+ case LWC1:
+ addr = rs + se_imm16;
+ alu_out = ReadW(addr, instr);
+ break;
+ case LDC1:
+ addr = rs + se_imm16;
+ fp_out = ReadD(addr, instr);
+ break;
+ case SWC1:
+ case SDC1:
+ addr = rs + se_imm16;
+ break;
+ default:
+ UNREACHABLE();
+ };
+
+ // ---------- Raise exceptions triggered.
+ SignalExceptions();
+
+ // ---------- Execution.
+ switch (op) {
+ // ------------- Branch instructions.
+ case BEQ:
+ case BNE:
+ case BLEZ:
+ case BGTZ:
+ // Branch instructions common part.
+ execute_branch_delay_instruction = true;
+ // Set next_pc.
+ if (do_branch) {
+ next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
+ if (instr->IsLinkingInstruction()) {
+ set_register(31, current_pc + 2* Instruction::kInstrSize);
+ }
+ } else {
+ next_pc = current_pc + 2 * Instruction::kInstrSize;
+ }
+ break;
+ // ------------- Arithmetic instructions.
+ case ADDI:
+ case ADDIU:
+ case SLTI:
+ case SLTIU:
+ case ANDI:
+ case ORI:
+ case XORI:
+ case LUI:
+ set_register(rt_reg, alu_out);
+ break;
+ // ------------- Memory instructions.
+ case LB:
+ case LH:
+ case LWL:
+ case LW:
+ case LBU:
+ case LHU:
+ case LWR:
+ set_register(rt_reg, alu_out);
+ break;
+ case SB:
+ WriteB(addr, static_cast<int8_t>(rt));
+ break;
+ case SH:
+ WriteH(addr, static_cast<uint16_t>(rt), instr);
+ break;
+ case SWL:
+ WriteW(addr, mem_value, instr);
+ break;
+ case SW:
+ WriteW(addr, rt, instr);
+ break;
+ case SWR:
+ WriteW(addr, mem_value, instr);
+ break;
+ case LWC1:
+ set_fpu_register(ft_reg, alu_out);
+ break;
+ case LDC1:
+ set_fpu_register_double(ft_reg, fp_out);
+ break;
+ case SWC1:
+ addr = rs + se_imm16;
+ WriteW(addr, get_fpu_register(ft_reg), instr);
+ break;
+ case SDC1:
+ addr = rs + se_imm16;
+ WriteD(addr, get_fpu_register_double(ft_reg), instr);
+ break;
+ default:
+ break;
+ };
+
+
+ if (execute_branch_delay_instruction) {
+ // Execute branch delay slot
+ // We don't check for end_sim_pc. First it should not be met as the current
+ // pc is valid. Secondly a jump should always execute its branch delay slot.
+ Instruction* branch_delay_instr =
+ reinterpret_cast<Instruction*>(current_pc+Instruction::kInstrSize);
+ BranchDelayInstructionDecode(branch_delay_instr);
+ }
+
+ // If needed update pc after the branch delay execution.
+ if (next_pc != bad_ra) {
+ set_pc(next_pc);
+ }
+}
+
+
+// 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();
+ // Get unchanged bits of pc.
+ int32_t pc_high_bits = current_pc & 0xf0000000;
+ // Next pc.
+ int32_t next_pc = pc_high_bits | (instr->Imm26Value() << 2);
+
+ // Execute branch delay slot.
+ // We don't check for end_sim_pc. First it should not be met as the current pc
+ // is valid. Secondly a jump should always execute its branch delay slot.
+ Instruction* branch_delay_instr =
+ reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
+ BranchDelayInstructionDecode(branch_delay_instr);
+
+ // Update pc and ra if necessary.
+ // Do this after the branch delay execution.
+ if (instr->IsLinkingInstruction()) {
+ set_register(31, current_pc + 2 * Instruction::kInstrSize);
+ }
+ set_pc(next_pc);
+ pc_modified_ = true;
+}
+
+
+// Executes the current instruction.
+void Simulator::InstructionDecode(Instruction* instr) {
+ if (v8::internal::FLAG_check_icache) {
+ CheckICache(isolate_->simulator_i_cache(), instr);
+ }
+ pc_modified_ = false;
+ if (::v8::internal::FLAG_trace_sim) {
+ disasm::NameConverter converter;
+ disasm::Disassembler dasm(converter);
+ // Use a reasonably large buffer.
+ v8::internal::EmbeddedVector<char, 256> buffer;
+ dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(instr));
+ PrintF(" 0x%08x %s\n", reinterpret_cast<intptr_t>(instr),
+ buffer.start());
+ }
+
+ switch (instr->InstructionType()) {
+ case Instruction::kRegisterType:
+ DecodeTypeRegister(instr);
+ break;
+ case Instruction::kImmediateType:
+ DecodeTypeImmediate(instr);
+ break;
+ case Instruction::kJumpType:
+ DecodeTypeJump(instr);
+ break;
+ default:
+ UNSUPPORTED();
+ }
+ if (!pc_modified_) {
+ set_register(pc, reinterpret_cast<int32_t>(instr) +
+ Instruction::kInstrSize);
+ }
+}
+
+
+
+void Simulator::Execute() {
+ // Get the PC to simulate. Cannot use the accessor here as we need the
+ // raw PC value and not the one used as input to arithmetic instructions.
+ int program_counter = get_pc();
+ if (::v8::internal::FLAG_stop_sim_at == 0) {
+ // Fast version of the dispatch loop without checking whether the simulator
+ // should be stopping at a particular executed instruction.
+ while (program_counter != end_sim_pc) {
+ Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
+ icount_++;
+ InstructionDecode(instr);
+ program_counter = get_pc();
+ }
+ } else {
+ // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when
+ // we reach the particular instuction count.
+ while (program_counter != end_sim_pc) {
+ Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
+ icount_++;
+ if (icount_ == ::v8::internal::FLAG_stop_sim_at) {
+ MipsDebugger dbg(this);
+ dbg.Debug();
+ } else {
+ InstructionDecode(instr);
+ }
+ program_counter = get_pc();
+ }
+ }
+}
+
+
+void Simulator::CallInternal(byte* entry) {
+ // Prepare to execute the code at entry.
+ set_register(pc, reinterpret_cast<int32_t>(entry));
+ // Put down marker for end of simulation. The simulator will stop simulation
+ // when the PC reaches this value. By saving the "end simulation" value into
+ // the LR the simulation stops when returning to this call point.
+ set_register(ra, end_sim_pc);
+
+ // Remember the values of callee-saved registers.
+ // The code below assumes that r9 is not used as sb (static base) in
+ // simulator code and therefore is regarded as a callee-saved register.
+ int32_t s0_val = get_register(s0);
+ int32_t s1_val = get_register(s1);
+ int32_t s2_val = get_register(s2);
+ int32_t s3_val = get_register(s3);
+ int32_t s4_val = get_register(s4);
+ int32_t s5_val = get_register(s5);
+ int32_t s6_val = get_register(s6);
+ int32_t s7_val = get_register(s7);
+ int32_t gp_val = get_register(gp);
+ int32_t sp_val = get_register(sp);
+ int32_t fp_val = get_register(fp);
+
+ // 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);
+ set_register(s1, callee_saved_value);
+ set_register(s2, callee_saved_value);
+ set_register(s3, callee_saved_value);
+ set_register(s4, callee_saved_value);
+ set_register(s5, callee_saved_value);
+ set_register(s6, callee_saved_value);
+ set_register(s7, callee_saved_value);
+ set_register(gp, callee_saved_value);
+ set_register(fp, callee_saved_value);
+
+ // Start the simulation.
+ Execute();
+
+ // Check that the callee-saved registers have been preserved.
+ CHECK_EQ(callee_saved_value, get_register(s0));
+ CHECK_EQ(callee_saved_value, get_register(s1));
+ CHECK_EQ(callee_saved_value, get_register(s2));
+ CHECK_EQ(callee_saved_value, get_register(s3));
+ CHECK_EQ(callee_saved_value, get_register(s4));
+ CHECK_EQ(callee_saved_value, get_register(s5));
+ CHECK_EQ(callee_saved_value, get_register(s6));
+ CHECK_EQ(callee_saved_value, get_register(s7));
+ CHECK_EQ(callee_saved_value, get_register(gp));
+ CHECK_EQ(callee_saved_value, get_register(fp));
+
+ // Restore callee-saved registers with the original value.
+ set_register(s0, s0_val);
+ set_register(s1, s1_val);
+ set_register(s2, s2_val);
+ set_register(s3, s3_val);
+ set_register(s4, s4_val);
+ set_register(s5, s5_val);
+ set_register(s6, s6_val);
+ set_register(s7, s7_val);
+ set_register(gp, gp_val);
+ set_register(sp, sp_val);
+ set_register(fp, fp_val);
+}
+
+
+int32_t Simulator::Call(byte* entry, int argument_count, ...) {
+ va_list parameters;
+ va_start(parameters, argument_count);
+ // Set up arguments.
+
+ // First four arguments passed in registers.
+ ASSERT(argument_count >= 4);
+ set_register(a0, va_arg(parameters, int32_t));
+ set_register(a1, va_arg(parameters, int32_t));
+ set_register(a2, va_arg(parameters, int32_t));
+ set_register(a3, va_arg(parameters, int32_t));
+
+ // Remaining arguments passed on stack.
+ int original_stack = get_register(sp);
+ // Compute position of stack on entry to generated code.
+ int entry_stack = (original_stack - (argument_count - 4) * sizeof(int32_t)
+ - kCArgsSlotsSize);
+ if (OS::ActivationFrameAlignment() != 0) {
+ entry_stack &= -OS::ActivationFrameAlignment();
+ }
+ // Store remaining arguments on stack, from low to high memory.
+ intptr_t* stack_argument = reinterpret_cast<intptr_t*>(entry_stack);
+ for (int i = 4; i < argument_count; i++) {
+ stack_argument[i - 4 + kCArgSlotCount] = va_arg(parameters, int32_t);
+ }
+ va_end(parameters);
+ set_register(sp, entry_stack);
+
+ CallInternal(entry);
+
+ // Pop stack passed arguments.
+ CHECK_EQ(entry_stack, get_register(sp));
+ set_register(sp, original_stack);
+
+ int32_t result = get_register(v0);
+ return result;
+}
+
+
+double Simulator::CallFP(byte* entry, double d0, double d1) {
+ if (!IsMipsSoftFloatABI) {
+ set_fpu_register_double(f12, d0);
+ set_fpu_register_double(f14, d1);
+ } else {
+ int buffer[2];
+ ASSERT(sizeof(buffer[0]) * 2 == sizeof(d0));
+ OS::MemCopy(buffer, &d0, sizeof(d0));
+ set_dw_register(a0, buffer);
+ OS::MemCopy(buffer, &d1, sizeof(d1));
+ set_dw_register(a2, buffer);
+ }
+ CallInternal(entry);
+ if (!IsMipsSoftFloatABI) {
+ return get_fpu_register_double(f0);
+ } else {
+ return get_double_from_register_pair(v0);
+ }
+}
+
+
+uintptr_t Simulator::PushAddress(uintptr_t address) {
+ int new_sp = get_register(sp) - sizeof(uintptr_t);
+ uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(new_sp);
+ *stack_slot = address;
+ set_register(sp, new_sp);
+ return new_sp;
+}
+
+
+uintptr_t Simulator::PopAddress() {
+ int current_sp = get_register(sp);
+ uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(current_sp);
+ uintptr_t address = *stack_slot;
+ set_register(sp, current_sp + sizeof(uintptr_t));
+ return address;
+}
+
+
+#undef UNSUPPORTED
+
+} } // namespace v8::internal
+
+#endif // USE_SIMULATOR
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mips/simulator-mips.h b/chromium/v8/src/mips/simulator-mips.h
new file mode 100644
index 00000000000..601cd6d99d1
--- /dev/null
+++ b/chromium/v8/src/mips/simulator-mips.h
@@ -0,0 +1,441 @@
+// 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.
+
+
+// Declares a Simulator for MIPS instructions if we are not generating a native
+// MIPS binary. This Simulator allows us to run and debug MIPS code generation
+// on regular desktop machines.
+// V8 calls into generated code by "calling" the CALL_GENERATED_CODE macro,
+// which will start execution in the Simulator or forwards to the real entry
+// on a MIPS HW platform.
+
+#ifndef V8_MIPS_SIMULATOR_MIPS_H_
+#define V8_MIPS_SIMULATOR_MIPS_H_
+
+#include "allocation.h"
+#include "constants-mips.h"
+
+#if !defined(USE_SIMULATOR)
+// Running without a simulator on a native mips platform.
+
+namespace v8 {
+namespace internal {
+
+// When running without a simulator we call the entry directly.
+#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
+ entry(p0, p1, p2, p3, p4)
+
+typedef int (*mips_regexp_matcher)(String*, int, const byte*, const byte*,
+ void*, int*, int, Address, int, Isolate*);
+
+
+// Call the generated regexp code directly. The code at the entry address
+// should act as a function matching the type arm_regexp_matcher.
+// The fifth argument is a dummy that reserves the space used for
+// the return address added by the ExitFrame in native calls.
+#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \
+ (FUNCTION_CAST<mips_regexp_matcher>(entry)( \
+ p0, p1, p2, p3, NULL, p4, p5, p6, p7, p8))
+
+#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
+ reinterpret_cast<TryCatch*>(try_catch_address)
+
+// The stack limit beyond which we will throw stack overflow errors in
+// generated code. Because generated code on mips uses the C stack, we
+// just use the C stack limit.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+ static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+ uintptr_t c_limit) {
+ return c_limit;
+ }
+
+ static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
+ return try_catch_address;
+ }
+
+ static inline void UnregisterCTryCatch() { }
+};
+
+} } // namespace v8::internal
+
+// Calculated the stack limit beyond which we will throw stack overflow errors.
+// This macro must be called from a C++ method. It relies on being able to take
+// the address of "this" to get a value on the current execution stack and then
+// calculates the stack limit based on that value.
+// NOTE: The check for overflow is not safe as there is no guarantee that the
+// running thread has its stack in all memory up to address 0x00000000.
+#define GENERATED_CODE_STACK_LIMIT(limit) \
+ (reinterpret_cast<uintptr_t>(this) >= limit ? \
+ reinterpret_cast<uintptr_t>(this) - limit : 0)
+
+#else // !defined(USE_SIMULATOR)
+// Running with a simulator.
+
+#include "hashmap.h"
+#include "assembler.h"
+
+namespace v8 {
+namespace internal {
+
+// -----------------------------------------------------------------------------
+// Utility functions
+
+class CachePage {
+ public:
+ static const int LINE_VALID = 0;
+ static const int LINE_INVALID = 1;
+
+ static const int kPageShift = 12;
+ static const int kPageSize = 1 << kPageShift;
+ static const int kPageMask = kPageSize - 1;
+ static const int kLineShift = 2; // The cache line is only 4 bytes right now.
+ static const int kLineLength = 1 << kLineShift;
+ static const int kLineMask = kLineLength - 1;
+
+ CachePage() {
+ memset(&validity_map_, LINE_INVALID, sizeof(validity_map_));
+ }
+
+ char* ValidityByte(int offset) {
+ return &validity_map_[offset >> kLineShift];
+ }
+
+ char* CachedData(int offset) {
+ return &data_[offset];
+ }
+
+ private:
+ char data_[kPageSize]; // The cached data.
+ static const int kValidityMapSize = kPageSize >> kLineShift;
+ char validity_map_[kValidityMapSize]; // One byte per line.
+};
+
+class Simulator {
+ public:
+ friend class MipsDebugger;
+
+ // Registers are declared in order. See SMRL chapter 2.
+ enum Register {
+ no_reg = -1,
+ zero_reg = 0,
+ at,
+ v0, v1,
+ a0, a1, a2, a3,
+ t0, t1, t2, t3, t4, t5, t6, t7,
+ s0, s1, s2, s3, s4, s5, s6, s7,
+ t8, t9,
+ k0, k1,
+ gp,
+ sp,
+ s8,
+ ra,
+ // LO, HI, and pc.
+ LO,
+ HI,
+ pc, // pc must be the last register.
+ kNumSimuRegisters,
+ // aliases
+ fp = s8
+ };
+
+ // Coprocessor registers.
+ // Generated code will always use doubles. So we will only use even registers.
+ enum FPURegister {
+ f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11,
+ f12, f13, f14, f15, // f12 and f14 are arguments FPURegisters.
+ f16, f17, f18, f19, f20, f21, f22, f23, f24, f25,
+ f26, f27, f28, f29, f30, f31,
+ kNumFPURegisters
+ };
+
+ explicit Simulator(Isolate* isolate);
+ ~Simulator();
+
+ // The currently executing Simulator instance. Potentially there can be one
+ // for each native thread.
+ static Simulator* current(v8::internal::Isolate* isolate);
+
+ // Accessors for register state. Reading the pc value adheres to the MIPS
+ // architecture specification and is off by a 8 from the currently executing
+ // instruction.
+ void set_register(int reg, int32_t value);
+ void set_dw_register(int dreg, const int* dbl);
+ int32_t get_register(int reg) const;
+ double get_double_from_register_pair(int reg);
+ // Same for FPURegisters.
+ void set_fpu_register(int fpureg, int32_t value);
+ void set_fpu_register_float(int fpureg, float value);
+ void set_fpu_register_double(int fpureg, double value);
+ int32_t get_fpu_register(int fpureg) const;
+ int64_t get_fpu_register_long(int fpureg) const;
+ float get_fpu_register_float(int fpureg) const;
+ double get_fpu_register_double(int fpureg) const;
+ void set_fcsr_bit(uint32_t cc, bool value);
+ bool test_fcsr_bit(uint32_t cc);
+ bool set_fcsr_round_error(double original, double rounded);
+
+ // Special case of set_register and get_register to access the raw PC value.
+ void set_pc(int32_t value);
+ int32_t get_pc() const;
+
+ // Accessor to the internal simulator stack area.
+ uintptr_t StackLimit() const;
+
+ // Executes MIPS instructions until the PC reaches end_sim_pc.
+ void Execute();
+
+ // Call on program start.
+ static void Initialize(Isolate* isolate);
+
+ // V8 generally calls into generated JS code with 5 parameters and into
+ // generated RegExp code with 7 parameters. This is a convenience function,
+ // which sets up the simulator state and grabs the result on return.
+ int32_t Call(byte* entry, int argument_count, ...);
+ // Alternative: call a 2-argument double function.
+ double CallFP(byte* entry, double d0, double d1);
+
+ // Push an address onto the JS stack.
+ uintptr_t PushAddress(uintptr_t address);
+
+ // 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);
+
+ // Returns true if pc register contains one of the 'special_values' defined
+ // below (bad_ra, end_sim_pc).
+ bool has_bad_pc() const;
+
+ private:
+ enum special_values {
+ // Known bad pc value to ensure that the simulator does not execute
+ // without being properly setup.
+ bad_ra = -1,
+ // A pc value used to signal the simulator to stop execution. Generally
+ // the ra is set to this value on transition from native C code to
+ // simulated execution, so that the simulator can "return" to the native
+ // C code.
+ end_sim_pc = -2,
+ // Unpredictable value.
+ Unpredictable = 0xbadbeaf
+ };
+
+ // Unsupported instructions use Format to print an error and stop execution.
+ void Format(Instruction* instr, const char* format);
+
+ // Read and write memory.
+ inline uint32_t ReadBU(int32_t addr);
+ inline int32_t ReadB(int32_t addr);
+ inline void WriteB(int32_t addr, uint8_t value);
+ inline void WriteB(int32_t addr, int8_t value);
+
+ inline uint16_t ReadHU(int32_t addr, Instruction* instr);
+ inline int16_t ReadH(int32_t addr, Instruction* instr);
+ // Note: Overloaded on the sign of the value.
+ inline void WriteH(int32_t addr, uint16_t value, Instruction* instr);
+ inline void WriteH(int32_t addr, int16_t value, Instruction* instr);
+
+ inline int ReadW(int32_t addr, Instruction* instr);
+ inline void WriteW(int32_t addr, int value, Instruction* instr);
+
+ inline double ReadD(int32_t addr, Instruction* instr);
+ inline void WriteD(int32_t addr, double value, Instruction* instr);
+
+ // Operations depending on endianness.
+ // Get Double Higher / Lower word.
+ inline int32_t GetDoubleHIW(double* addr);
+ inline int32_t GetDoubleLOW(double* addr);
+ // Set Double Higher / Lower word.
+ inline int32_t SetDoubleHIW(double* addr);
+ inline int32_t SetDoubleLOW(double* addr);
+
+ // Executing is handled based on the instruction type.
+ void DecodeTypeRegister(Instruction* instr);
+
+ // Helper function for DecodeTypeRegister.
+ void ConfigureTypeRegister(Instruction* instr,
+ int32_t& alu_out,
+ int64_t& i64hilo,
+ uint64_t& u64hilo,
+ int32_t& next_pc,
+ bool& do_interrupt);
+
+ void DecodeTypeImmediate(Instruction* instr);
+ void DecodeTypeJump(Instruction* instr);
+
+ // Used for breakpoints and traps.
+ void SoftwareInterrupt(Instruction* instr);
+
+ // Stop helper functions.
+ bool IsWatchpoint(uint32_t code);
+ void PrintWatchpoint(uint32_t code);
+ void HandleStop(uint32_t code, Instruction* instr);
+ bool IsStopInstruction(Instruction* instr);
+ bool IsEnabledStop(uint32_t code);
+ void EnableStop(uint32_t code);
+ void DisableStop(uint32_t code);
+ void IncreaseStopCounter(uint32_t code);
+ void PrintStopInfo(uint32_t code);
+
+
+ // Executes one instruction.
+ void InstructionDecode(Instruction* instr);
+ // Execute one instruction placed in a branch delay slot.
+ void BranchDelayInstructionDecode(Instruction* instr) {
+ if (instr->InstructionBits() == nopInstr) {
+ // Short-cut generic nop instructions. They are always valid and they
+ // never change the simulator state.
+ return;
+ }
+
+ if (instr->IsForbiddenInBranchDelay()) {
+ V8_Fatal(__FILE__, __LINE__,
+ "Eror:Unexpected %i opcode in a branch delay slot.",
+ instr->OpcodeValue());
+ }
+ InstructionDecode(instr);
+ }
+
+ // ICache.
+ static void CheckICache(v8::internal::HashMap* i_cache, Instruction* instr);
+ static void FlushOnePage(v8::internal::HashMap* i_cache, intptr_t start,
+ int size);
+ static CachePage* GetCachePage(v8::internal::HashMap* i_cache, void* page);
+
+ enum Exception {
+ none,
+ kIntegerOverflow,
+ kIntegerUnderflow,
+ kDivideByZero,
+ kNumExceptions
+ };
+ int16_t exceptions[kNumExceptions];
+
+ // Exceptions.
+ void SignalExceptions();
+
+ // Runtime call support.
+ static void* RedirectExternalReference(void* external_function,
+ ExternalReference::Type type);
+
+ // Handle arguments and return value for runtime FP functions.
+ void GetFpArgs(double* x, double* y, int32_t* z);
+ void SetFpResult(const double& result);
+
+ void CallInternal(byte* entry);
+
+ // Architecture state.
+ // Registers.
+ int32_t registers_[kNumSimuRegisters];
+ // Coprocessor Registers.
+ int32_t FPUregisters_[kNumFPURegisters];
+ // FPU control register.
+ uint32_t FCSR_;
+
+ // Simulator support.
+ // Allocate 1MB for stack.
+ static const size_t stack_size_ = 1 * 1024*1024;
+ char* stack_;
+ bool pc_modified_;
+ int icount_;
+ int break_count_;
+
+ // Debugger input.
+ char* last_debugger_input_;
+
+ // Icache simulation.
+ v8::internal::HashMap* i_cache_;
+
+ v8::internal::Isolate* isolate_;
+
+ // Registered breakpoints.
+ Instruction* break_pc_;
+ Instr break_instr_;
+
+ // Stop is disabled if bit 31 is set.
+ static const uint32_t kStopDisabledBit = 1 << 31;
+
+ // A stop is enabled, meaning the simulator will stop when meeting the
+ // instruction, if bit 31 of watched_stops_[code].count is unset.
+ // The value watched_stops_[code].count & ~(1 << 31) indicates how many times
+ // the breakpoint was hit or gone through.
+ struct StopCountAndDesc {
+ uint32_t count;
+ char* desc;
+ };
+ StopCountAndDesc watched_stops_[kMaxStopCode + 1];
+};
+
+
+// When running with the simulator transition into simulated execution at this
+// point.
+#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
+ reinterpret_cast<Object*>(Simulator::current(Isolate::Current())->Call( \
+ FUNCTION_ADDR(entry), 5, p0, p1, p2, p3, p4))
+
+#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \
+ Simulator::current(Isolate::Current())->Call( \
+ entry, 10, p0, p1, p2, p3, NULL, p4, p5, p6, p7, p8)
+
+#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
+ try_catch_address == NULL ? \
+ NULL : *(reinterpret_cast<TryCatch**>(try_catch_address))
+
+
+// The simulator has its own stack. Thus it has a different stack limit from
+// the C-based native code. Setting the c_limit to indicate a very small
+// stack cause stack overflow errors, since the simulator ignores the input.
+// This is unlikely to be an issue in practice, though it might cause testing
+// trouble down the line.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+ static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+ uintptr_t c_limit) {
+ return Simulator::current(isolate)->StackLimit();
+ }
+
+ static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
+ Simulator* sim = Simulator::current(Isolate::Current());
+ return sim->PushAddress(try_catch_address);
+ }
+
+ static inline void UnregisterCTryCatch() {
+ Simulator::current(Isolate::Current())->PopAddress();
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // !defined(USE_SIMULATOR)
+#endif // V8_MIPS_SIMULATOR_MIPS_H_
diff --git a/chromium/v8/src/mips/stub-cache-mips.cc b/chromium/v8/src/mips/stub-cache-mips.cc
new file mode 100644
index 00000000000..c4b1ee57a72
--- /dev/null
+++ b/chromium/v8/src/mips/stub-cache-mips.cc
@@ -0,0 +1,3800 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_MIPS
+
+#include "ic-inl.h"
+#include "codegen.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+static void ProbeTable(Isolate* isolate,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ StubCache::Table table,
+ Register receiver,
+ Register name,
+ // Number of the cache entry, not scaled.
+ Register offset,
+ Register scratch,
+ Register scratch2,
+ Register offset_scratch) {
+ ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
+ ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
+ ExternalReference map_offset(isolate->stub_cache()->map_reference(table));
+
+ uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address());
+ uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address());
+ uint32_t map_off_addr = reinterpret_cast<uint32_t>(map_offset.address());
+
+ // Check the relative positions of the address fields.
+ ASSERT(value_off_addr > key_off_addr);
+ ASSERT((value_off_addr - key_off_addr) % 4 == 0);
+ ASSERT((value_off_addr - key_off_addr) < (256 * 4));
+ ASSERT(map_off_addr > key_off_addr);
+ ASSERT((map_off_addr - key_off_addr) % 4 == 0);
+ ASSERT((map_off_addr - key_off_addr) < (256 * 4));
+
+ Label miss;
+ Register base_addr = scratch;
+ scratch = no_reg;
+
+ // Multiply by 3 because there are 3 fields per entry (name, code, map).
+ __ sll(offset_scratch, offset, 1);
+ __ Addu(offset_scratch, offset_scratch, offset);
+
+ // Calculate the base address of the entry.
+ __ li(base_addr, Operand(key_offset));
+ __ sll(at, offset_scratch, kPointerSizeLog2);
+ __ Addu(base_addr, base_addr, at);
+
+ // Check that the key in the entry matches the name.
+ __ lw(at, MemOperand(base_addr, 0));
+ __ Branch(&miss, ne, name, Operand(at));
+
+ // Check the map matches.
+ __ lw(at, MemOperand(base_addr, map_off_addr - key_off_addr));
+ __ lw(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ Branch(&miss, ne, at, Operand(scratch2));
+
+ // Get the code entry from the cache.
+ Register code = scratch2;
+ scratch2 = no_reg;
+ __ lw(code, MemOperand(base_addr, value_off_addr - key_off_addr));
+
+ // Check that the flags match what we're looking for.
+ Register flags_reg = base_addr;
+ base_addr = no_reg;
+ __ lw(flags_reg, FieldMemOperand(code, Code::kFlagsOffset));
+ __ And(flags_reg, flags_reg, Operand(~Code::kFlagsNotUsedInLookup));
+ __ Branch(&miss, ne, flags_reg, Operand(flags));
+
+#ifdef DEBUG
+ if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
+ __ jmp(&miss);
+ } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
+ __ jmp(&miss);
+ }
+#endif
+
+ // Jump to the first instruction in the code stub.
+ __ Addu(at, code, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ Jump(at);
+
+ // Miss: fall through.
+ __ bind(&miss);
+}
+
+
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// 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 unique and receiver must be a heap object.
+static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ Handle<Name> name,
+ Register scratch0,
+ Register scratch1) {
+ ASSERT(name->IsUniqueName());
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
+ __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
+
+ Label done;
+
+ const int kInterceptorOrAccessCheckNeededMask =
+ (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
+
+ // Bail out if the receiver has a named interceptor or requires access checks.
+ Register map = scratch1;
+ __ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ lbu(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ 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));
+ __ Branch(miss_label, lt, scratch0, Operand(FIRST_SPEC_OBJECT_TYPE));
+
+ // Load properties array.
+ Register properties = scratch0;
+ __ lw(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ // Check that the properties array is a dictionary.
+ __ lw(map, FieldMemOperand(properties, HeapObject::kMapOffset));
+ Register tmp = properties;
+ __ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
+ __ Branch(miss_label, ne, map, Operand(tmp));
+
+ // Restore the temporarily used register.
+ __ lw(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+
+
+ NameDictionaryLookupStub::GenerateNegativeLookup(masm,
+ miss_label,
+ &done,
+ receiver,
+ properties,
+ name,
+ scratch1);
+ __ bind(&done);
+ __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
+}
+
+
+void StubCache::GenerateProbe(MacroAssembler* masm,
+ Code::Flags flags,
+ Register receiver,
+ Register name,
+ Register scratch,
+ Register extra,
+ Register extra2,
+ Register extra3) {
+ Isolate* isolate = masm->isolate();
+ Label miss;
+
+ // Make sure that code is valid. The multiplying code relies on the
+ // entry size being 12.
+ ASSERT(sizeof(Entry) == 12);
+
+ // Make sure the flags does not name a specific type.
+ ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
+
+ // Make sure 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));
+ ASSERT(!extra2.is(receiver));
+ ASSERT(!extra2.is(name));
+ ASSERT(!extra2.is(scratch));
+ ASSERT(!extra2.is(extra));
+
+ // Check register validity.
+ ASSERT(!scratch.is(no_reg));
+ ASSERT(!extra.is(no_reg));
+ ASSERT(!extra2.is(no_reg));
+ ASSERT(!extra3.is(no_reg));
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1,
+ extra2, extra3);
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Get the map of the receiver and compute the hash.
+ __ lw(scratch, FieldMemOperand(name, Name::kHashFieldOffset));
+ __ lw(at, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ Addu(scratch, scratch, at);
+ uint32_t mask = kPrimaryTableSize - 1;
+ // We shift out the last two bits because they are not part of the hash and
+ // they are always 01 for maps.
+ __ srl(scratch, scratch, kHeapObjectTagSize);
+ __ Xor(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask));
+ __ And(scratch, scratch, Operand(mask));
+
+ // Probe the primary table.
+ ProbeTable(isolate,
+ masm,
+ flags,
+ kPrimary,
+ receiver,
+ name,
+ scratch,
+ extra,
+ extra2,
+ extra3);
+
+ // Primary miss: Compute hash for secondary probe.
+ __ srl(at, name, kHeapObjectTagSize);
+ __ Subu(scratch, scratch, at);
+ uint32_t mask2 = kSecondaryTableSize - 1;
+ __ Addu(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask2));
+ __ And(scratch, scratch, Operand(mask2));
+
+ // Probe the secondary table.
+ ProbeTable(isolate,
+ masm,
+ flags,
+ kSecondary,
+ receiver,
+ name,
+ scratch,
+ extra,
+ extra2,
+ extra3);
+
+ // Cache miss: Fall-through and let caller handle the miss by
+ // entering the runtime system.
+ __ bind(&miss);
+ __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1,
+ extra2, extra3);
+}
+
+
+void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
+ int index,
+ Register prototype) {
+ // Load the global or builtins object from the current context.
+ __ lw(prototype,
+ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ // Load the native context from the global or builtins object.
+ __ lw(prototype,
+ FieldMemOperand(prototype, GlobalObject::kNativeContextOffset));
+ // Load the function from the native context.
+ __ lw(prototype, MemOperand(prototype, Context::SlotOffset(index)));
+ // Load the initial map. The global functions all have initial maps.
+ __ lw(prototype,
+ FieldMemOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset));
+ // Load the prototype from the initial map.
+ __ lw(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
+}
+
+
+void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
+ 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_OBJECT_INDEX)));
+ ASSERT(!prototype.is(at));
+ __ li(at, isolate->global_object());
+ __ Branch(miss, ne, prototype, Operand(at));
+ // Get the global function with the given index.
+ Handle<JSFunction> function(
+ JSFunction::cast(isolate->native_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.
+ __ lw(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
+}
+
+
+void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
+ Register dst,
+ Register src,
+ bool inobject,
+ int index,
+ Representation representation) {
+ ASSERT(!FLAG_track_double_fields || !representation.IsDouble());
+ int offset = index * kPointerSize;
+ if (!inobject) {
+ // Calculate the offset into the properties array.
+ offset = offset + FixedArray::kHeaderSize;
+ __ lw(dst, FieldMemOperand(src, JSObject::kPropertiesOffset));
+ src = dst;
+ }
+ __ lw(dst, FieldMemOperand(src, offset));
+}
+
+
+void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch,
+ Label* miss_label) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss_label);
+
+ // Check that the object is a JS array.
+ __ GetObjectType(receiver, scratch, scratch);
+ __ Branch(miss_label, ne, scratch, Operand(JS_ARRAY_TYPE));
+
+ // Load length directly from the JS array.
+ __ Ret(USE_DELAY_SLOT);
+ __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+}
+
+
+// Generate code to check if an object is a string. If the object is a
+// heap object, its map's instance type is left in the scratch1 register.
+// If this is not needed, scratch1 and scratch2 may be the same register.
+static void GenerateStringCheck(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* smi,
+ Label* non_string_object) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, smi, t0);
+
+ // Check that the object is a string.
+ __ lw(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ And(scratch2, scratch1, Operand(kIsNotStringMask));
+ // The cast is to resolve the overload for the argument of 0x0.
+ __ Branch(non_string_object,
+ ne,
+ scratch2,
+ Operand(static_cast<int32_t>(kStringTag)));
+}
+
+
+// Generate code to load the length from a string object and return the length.
+// If the receiver object is not a string or a wrapped string object the
+// execution continues at the miss label. The register containing the
+// receiver is potentially clobbered.
+void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss,
+ bool support_wrappers) {
+ Label check_wrapper;
+
+ // Check if the object is a string leaving the instance type in the
+ // scratch1 register.
+ GenerateStringCheck(masm, receiver, scratch1, scratch2, miss,
+ support_wrappers ? &check_wrapper : miss);
+
+ // Load length directly from the string.
+ __ Ret(USE_DELAY_SLOT);
+ __ lw(v0, FieldMemOperand(receiver, String::kLengthOffset));
+
+ if (support_wrappers) {
+ // Check if the object is a JSValue wrapper.
+ __ bind(&check_wrapper);
+ __ Branch(miss, ne, scratch1, Operand(JS_VALUE_TYPE));
+
+ // Unwrap the value and check if the wrapped value is a string.
+ __ lw(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset));
+ GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss);
+ __ Ret(USE_DELAY_SLOT);
+ __ lw(v0, FieldMemOperand(scratch1, String::kLengthOffset));
+ }
+}
+
+
+void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label) {
+ __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, scratch1);
+}
+
+
+// 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.
+static void GenerateCheckPropertyCell(MacroAssembler* masm,
+ Handle<GlobalObject> global,
+ Handle<Name> name,
+ Register scratch,
+ Label* miss) {
+ Handle<Cell> cell = GlobalObject::EnsurePropertyCell(global, name);
+ ASSERT(cell->value()->IsTheHole());
+ __ li(scratch, Operand(cell));
+ __ lw(scratch, FieldMemOperand(scratch, Cell::kValueOffset));
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(miss, ne, scratch, Operand(at));
+}
+
+
+void BaseStoreStubCompiler::GenerateNegativeHolderLookup(
+ MacroAssembler* masm,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Handle<Name> name,
+ Label* miss) {
+ if (holder->IsJSGlobalObject()) {
+ GenerateCheckPropertyCell(
+ masm, Handle<GlobalObject>::cast(holder), name, scratch1(), miss);
+ } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) {
+ GenerateDictionaryNegativeLookup(
+ masm, miss, holder_reg, name, scratch1(), scratch2());
+ }
+}
+
+
+// Generate StoreTransition code, value is passed in a0 register.
+// After executing generated code, the receiver_reg and name_reg
+// may be clobbered.
+void BaseStoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ Handle<Name> name,
+ Register receiver_reg,
+ Register storage_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* miss_label,
+ Label* slow) {
+ // a0 : value.
+ Label exit;
+
+ int descriptor = transition->LastAdded();
+ DescriptorArray* descriptors = transition->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+ Representation representation = details.representation();
+ ASSERT(!representation.IsNone());
+
+ if (details.type() == CONSTANT) {
+ Handle<Object> constant(descriptors->GetValue(descriptor), masm->isolate());
+ __ LoadObject(scratch1, constant);
+ __ Branch(miss_label, ne, value_reg, Operand(scratch1));
+ } else if (FLAG_track_fields && representation.IsSmi()) {
+ __ JumpIfNotSmi(value_reg, miss_label);
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ __ JumpIfSmi(value_reg, miss_label);
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ Label do_store, heap_number;
+ __ LoadRoot(scratch3, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow);
+
+ __ JumpIfNotSmi(value_reg, &heap_number);
+ __ SmiUntag(scratch1, value_reg);
+ __ mtc1(scratch1, f6);
+ __ cvt_d_w(f4, f6);
+ __ jmp(&do_store);
+
+ __ bind(&heap_number);
+ __ CheckMap(value_reg, scratch1, Heap::kHeapNumberMapRootIndex,
+ miss_label, DONT_DO_SMI_CHECK);
+ __ ldc1(f4, FieldMemOperand(value_reg, HeapNumber::kValueOffset));
+
+ __ bind(&do_store);
+ __ sdc1(f4, FieldMemOperand(storage_reg, HeapNumber::kValueOffset));
+ }
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ // Perform map transition for the receiver if necessary.
+ if (details.type() == FIELD &&
+ 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(transition));
+ __ Push(a2, a0);
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
+ masm->isolate()),
+ 3, 1);
+ return;
+ }
+
+ // Update the map of the object.
+ __ li(scratch1, Operand(transition));
+ __ sw(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
+
+ // Update the write barrier for the map field.
+ __ RecordWriteField(receiver_reg,
+ HeapObject::kMapOffset,
+ scratch1,
+ scratch2,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+
+ if (details.type() == CONSTANT) {
+ ASSERT(value_reg.is(a0));
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0);
+ return;
+ }
+
+ int index = transition->instance_descriptors()->GetFieldIndex(
+ transition->LastAdded());
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ // TODO(verwaest): Share this code as a code stub.
+ SmiCheck smi_check = representation.IsTagged()
+ ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ __ sw(storage_reg, FieldMemOperand(receiver_reg, offset));
+ } else {
+ __ sw(value_reg, FieldMemOperand(receiver_reg, offset));
+ }
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ if (!FLAG_track_double_fields || !representation.IsDouble()) {
+ __ mov(storage_reg, value_reg);
+ }
+ __ RecordWriteField(receiver_reg,
+ offset,
+ storage_reg,
+ scratch1,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array
+ __ lw(scratch1,
+ FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ __ sw(storage_reg, FieldMemOperand(scratch1, offset));
+ } else {
+ __ sw(value_reg, FieldMemOperand(scratch1, offset));
+ }
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ if (!FLAG_track_double_fields || !representation.IsDouble()) {
+ __ mov(storage_reg, value_reg);
+ }
+ __ RecordWriteField(scratch1,
+ offset,
+ storage_reg,
+ receiver_reg,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ }
+
+ // Return the value (register v0).
+ ASSERT(value_reg.is(a0));
+ __ bind(&exit);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0);
+}
+
+
+// Generate StoreField code, value is passed in a0 register.
+// When leaving generated code after success, the receiver_reg and name_reg
+// may be clobbered. Upon branch to miss_label, the receiver and name
+// registers have their original values.
+void BaseStoreStubCompiler::GenerateStoreField(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Register receiver_reg,
+ Register name_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label) {
+ // a0 : value
+ Label exit;
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ int index = lookup->GetFieldIndex().field_index();
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ Representation representation = lookup->representation();
+ ASSERT(!representation.IsNone());
+ if (FLAG_track_fields && representation.IsSmi()) {
+ __ JumpIfNotSmi(value_reg, miss_label);
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ __ JumpIfSmi(value_reg, miss_label);
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ // Load the double storage.
+ if (index < 0) {
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ lw(scratch1, FieldMemOperand(receiver_reg, offset));
+ } else {
+ __ lw(scratch1,
+ FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ __ lw(scratch1, FieldMemOperand(scratch1, offset));
+ }
+
+ // Store the value into the storage.
+ Label do_store, heap_number;
+ __ JumpIfNotSmi(value_reg, &heap_number);
+ __ SmiUntag(scratch2, value_reg);
+ __ mtc1(scratch2, f6);
+ __ cvt_d_w(f4, f6);
+ __ jmp(&do_store);
+
+ __ bind(&heap_number);
+ __ CheckMap(value_reg, scratch2, Heap::kHeapNumberMapRootIndex,
+ miss_label, DONT_DO_SMI_CHECK);
+ __ ldc1(f4, FieldMemOperand(value_reg, HeapNumber::kValueOffset));
+
+ __ bind(&do_store);
+ __ sdc1(f4, FieldMemOperand(scratch1, HeapNumber::kValueOffset));
+ // Return the value (register v0).
+ ASSERT(value_reg.is(a0));
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0);
+ return;
+ }
+
+ // TODO(verwaest): Share this code as a code stub.
+ SmiCheck smi_check = representation.IsTagged()
+ ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ sw(value_reg, FieldMemOperand(receiver_reg, offset));
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Skip updating write barrier if storing a smi.
+ __ JumpIfSmi(value_reg, &exit);
+
+ // Update the write barrier for the array address.
+ // Pass the now unused name_reg as a scratch register.
+ __ mov(name_reg, value_reg);
+ __ RecordWriteField(receiver_reg,
+ offset,
+ name_reg,
+ scratch1,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array.
+ __ lw(scratch1,
+ FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
+ __ sw(value_reg, FieldMemOperand(scratch1, offset));
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Skip updating write barrier if storing a smi.
+ __ JumpIfSmi(value_reg, &exit);
+
+ // Update the write barrier for the array address.
+ // Ok to clobber receiver_reg and name_reg, since we return.
+ __ mov(name_reg, value_reg);
+ __ RecordWriteField(scratch1,
+ offset,
+ name_reg,
+ receiver_reg,
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ smi_check);
+ }
+ }
+
+ // Return the value (register v0).
+ ASSERT(value_reg.is(a0));
+ __ bind(&exit);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0);
+}
+
+
+void BaseStoreStubCompiler::GenerateRestoreName(MacroAssembler* masm,
+ Label* label,
+ Handle<Name> name) {
+ if (!label->is_unused()) {
+ __ bind(label);
+ __ li(this->name(), Operand(name));
+ }
+}
+
+
+static void GenerateCallFunction(MacroAssembler* masm,
+ Handle<Object> object,
+ const ParameterCount& arguments,
+ Label* miss,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- a0: receiver
+ // -- a1: function to call
+ // -----------------------------------
+ // Check that the function really is a function.
+ __ JumpIfSmi(a1, miss);
+ __ GetObjectType(a1, a3, a3);
+ __ Branch(miss, ne, a3, Operand(JS_FUNCTION_TYPE));
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ lw(a3, FieldMemOperand(a0, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a3, MemOperand(sp, arguments.immediate() * kPointerSize));
+ }
+
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(a1, arguments, JUMP_FUNCTION, NullCallWrapper(), call_kind);
+}
+
+
+static void PushInterceptorArguments(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ Handle<JSObject> holder_obj) {
+ __ push(name);
+ Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor());
+ ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor));
+ Register scratch = name;
+ __ li(scratch, Operand(interceptor));
+ __ Push(scratch, receiver, holder);
+ __ lw(scratch, FieldMemOperand(scratch, InterceptorInfo::kDataOffset));
+ __ push(scratch);
+ __ li(scratch, Operand(ExternalReference::isolate_address(masm->isolate())));
+ __ push(scratch);
+}
+
+
+static void CompileCallLoadPropertyWithInterceptor(
+ MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ Handle<JSObject> holder_obj) {
+ PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly),
+ masm->isolate());
+ __ PrepareCEntryArgs(6);
+ __ PrepareCEntryFunction(ref);
+
+ CEntryStub stub(1);
+ __ CallStub(&stub);
+}
+
+
+static const int kFastApiCallArguments = FunctionCallbackArguments::kArgsLength;
+
+// Reserves space for the extra arguments to API function in the
+// caller's frame.
+//
+// These arguments are set by CheckPrototypes and GenerateFastApiDirectCall.
+static void ReserveSpaceForFastApiCall(MacroAssembler* masm,
+ Register scratch) {
+ ASSERT(Smi::FromInt(0) == 0);
+ for (int i = 0; i < kFastApiCallArguments; i++) {
+ __ push(zero_reg);
+ }
+}
+
+
+// Undoes the effects of ReserveSpaceForFastApiCall.
+static void FreeSpaceForFastApiCall(MacroAssembler* masm) {
+ __ Drop(kFastApiCallArguments);
+}
+
+
+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[8] : call data
+ // -- sp[12] : isolate
+ // -- sp[16] : ReturnValue default value
+ // -- sp[20] : ReturnValue
+ // -- sp[24] : last JS argument
+ // -- ...
+ // -- sp[(argc + 5) * 4] : first JS argument
+ // -- sp[(argc + 6) * 4] : receiver
+ // -----------------------------------
+ // Get the function and setup the context.
+ Handle<JSFunction> function = optimization.constant_function();
+ __ LoadHeapObject(t1, function);
+ __ lw(cp, FieldMemOperand(t1, JSFunction::kContextOffset));
+
+ // Pass the additional arguments.
+ Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
+ Handle<Object> call_data(api_call_info->data(), masm->isolate());
+ if (masm->isolate()->heap()->InNewSpace(*call_data)) {
+ __ li(a0, api_call_info);
+ __ lw(t2, FieldMemOperand(a0, CallHandlerInfo::kDataOffset));
+ } else {
+ __ li(t2, call_data);
+ }
+
+ __ li(t3, Operand(ExternalReference::isolate_address(masm->isolate())));
+ // Store JS function, call data, isolate ReturnValue default and ReturnValue.
+ __ sw(t1, MemOperand(sp, 1 * kPointerSize));
+ __ sw(t2, MemOperand(sp, 2 * kPointerSize));
+ __ sw(t3, MemOperand(sp, 3 * kPointerSize));
+ __ LoadRoot(t1, Heap::kUndefinedValueRootIndex);
+ __ sw(t1, MemOperand(sp, 4 * kPointerSize));
+ __ sw(t1, MemOperand(sp, 5 * kPointerSize));
+
+ // Prepare arguments.
+ __ Addu(a2, sp, Operand(5 * kPointerSize));
+
+ // Allocate the v8::Arguments structure in the arguments' space since
+ // it's not controlled by GC.
+ 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
+ // struct from the function (which is currently the case). This means we pass
+ // the first argument in a1 instead of a0, if returns_handle is true.
+ // CallApiFunctionAndReturn will set up a0.
+
+ Address function_address = v8::ToCData<Address>(api_call_info->callback());
+ bool returns_handle =
+ !CallbackTable::ReturnsVoid(masm->isolate(), function_address);
+
+ Register first_arg = returns_handle ? a1 : a0;
+ Register second_arg = returns_handle ? a2 : a1;
+
+ // first_arg = v8::Arguments&
+ // Arguments is built at sp + 1 (sp is a reserved spot for ra).
+ __ Addu(first_arg, sp, kPointerSize);
+
+ // v8::Arguments::implicit_args_
+ __ sw(a2, MemOperand(first_arg, 0 * kPointerSize));
+ // v8::Arguments::values_
+ __ Addu(t0, a2, Operand(argc * kPointerSize));
+ __ sw(t0, MemOperand(first_arg, 1 * kPointerSize));
+ // v8::Arguments::length_ = argc
+ __ li(t0, Operand(argc));
+ __ sw(t0, MemOperand(first_arg, 2 * kPointerSize));
+ // v8::Arguments::is_construct_call = 0
+ __ sw(zero_reg, MemOperand(first_arg, 3 * kPointerSize));
+
+ const int kStackUnwindSpace = argc + kFastApiCallArguments + 1;
+ ApiFunction fun(function_address);
+ ExternalReference::Type type =
+ returns_handle ?
+ ExternalReference::DIRECT_API_CALL :
+ ExternalReference::DIRECT_API_CALL_NEW;
+ ExternalReference ref =
+ ExternalReference(&fun,
+ type,
+ masm->isolate());
+
+ Address thunk_address = returns_handle
+ ? FUNCTION_ADDR(&InvokeInvocationCallback)
+ : FUNCTION_ADDR(&InvokeFunctionCallback);
+ ExternalReference::Type thunk_type =
+ returns_handle ?
+ ExternalReference::PROFILING_API_CALL :
+ ExternalReference::PROFILING_API_CALL_NEW;
+ ApiFunction thunk_fun(thunk_address);
+ ExternalReference thunk_ref = ExternalReference(&thunk_fun, thunk_type,
+ masm->isolate());
+
+ AllowExternalCallThatCantCauseGC scope(masm);
+ __ CallApiFunctionAndReturn(ref,
+ function_address,
+ thunk_ref,
+ second_arg,
+ kStackUnwindSpace,
+ returns_handle,
+ kFastApiCallArguments + 1);
+}
+
+class CallInterceptorCompiler BASE_EMBEDDED {
+ public:
+ CallInterceptorCompiler(StubCompiler* stub_compiler,
+ const ParameterCount& arguments,
+ Register name,
+ Code::ExtraICState extra_ic_state)
+ : stub_compiler_(stub_compiler),
+ arguments_(arguments),
+ name_(name),
+ extra_ic_state_(extra_ic_state) {}
+
+ void Compile(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> 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()) {
+ CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3,
+ holder, lookup, name, optimization, miss);
+ } else {
+ CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3,
+ name, holder, miss);
+ }
+ }
+
+ private:
+ void CompileCacheable(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<JSObject> interceptor_holder,
+ LookupResult* lookup,
+ Handle<Name> 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, Handle<JSObject>(lookup->holder()));
+ }
+ can_do_fast_api_call =
+ depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth;
+ }
+
+ __ IncrementCounter(counters->call_const_interceptor(), 1,
+ scratch1, scratch2);
+
+ if (can_do_fast_api_call) {
+ __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1,
+ scratch1, scratch2);
+ ReserveSpaceForFastApiCall(masm, scratch1);
+ }
+
+ // Check that the maps from receiver to interceptor's holder
+ // haven't changed and thus we can invoke interceptor.
+ 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);
+
+ // Invoke an interceptor and if it provides a value,
+ // branch to |regular_invoke|.
+ Label regular_invoke;
+ LoadWithInterceptor(masm, receiver, holder, interceptor_holder, scratch2,
+ &regular_invoke);
+
+ // Interceptor returned nothing for this property. Try to use cached
+ // constant function.
+
+ // 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()) {
+ stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
+ 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
+ // safe to omit it here, as if present, it should be fetched
+ // by the previous CheckPrototypes.
+ ASSERT(depth2 == kInvalidProtoDepth);
+ }
+
+ // Invoke function.
+ if (can_do_fast_api_call) {
+ GenerateFastApiDirectCall(masm, optimization, arguments_.immediate());
+ } else {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ Handle<JSFunction> function = optimization.constant_function();
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments_,
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+ }
+
+ // Deferred code for fast API call case---clean preallocated space.
+ if (can_do_fast_api_call) {
+ __ bind(&miss_cleanup);
+ FreeSpaceForFastApiCall(masm);
+ __ Branch(miss_label);
+ }
+
+ // Invoke a regular function.
+ __ bind(&regular_invoke);
+ if (can_do_fast_api_call) {
+ FreeSpaceForFastApiCall(masm);
+ }
+ }
+
+ void CompileRegular(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<Name> name,
+ Handle<JSObject> interceptor_holder,
+ Label* miss_label) {
+ Register holder =
+ stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3,
+ name, miss_label);
+
+ // Call a runtime function to load the interceptor property.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ // Save the name_ register across the call.
+ __ push(name_);
+
+ PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder);
+
+ __ CallExternalReference(
+ ExternalReference(
+ IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
+ masm->isolate()),
+ 6);
+ // Restore the name_ register.
+ __ pop(name_);
+ // Leave the internal frame.
+ }
+
+ void LoadWithInterceptor(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Handle<JSObject> holder_obj,
+ Register scratch,
+ Label* interceptor_succeeded) {
+ {
+ 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));
+ }
+
+ StubCompiler* stub_compiler_;
+ const ParameterCount& arguments_;
+ Register name_;
+ Code::ExtraICState extra_ic_state_;
+};
+
+
+// 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<Name> 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()));
+ }
+}
+
+
+// Convert and store int passed in register ival to IEEE 754 single precision
+// floating point value at memory location (dst + 4 * wordoffset)
+// If FPU is available use it for conversion.
+static void StoreIntAsFloat(MacroAssembler* masm,
+ Register dst,
+ Register wordoffset,
+ Register ival,
+ Register scratch1) {
+ __ mtc1(ival, f0);
+ __ cvt_s_w(f0, f0);
+ __ sll(scratch1, wordoffset, 2);
+ __ addu(scratch1, dst, scratch1);
+ __ swc1(f0, MemOperand(scratch1, 0));
+}
+
+
+void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) {
+ __ Jump(code, RelocInfo::CODE_TARGET);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ Handle<Name> name,
+ int save_at_depth,
+ Label* miss,
+ PrototypeCheckType check) {
+ // Make sure that the type feedback oracle harvests the receiver map.
+ // TODO(svenpanne) Remove this hack when all ICs are reworked.
+ __ li(scratch1, Operand(Handle<Map>(object->map())));
+
+ Handle<JSObject> first = object;
+ // Make sure there's no overlap between holder and object registers.
+ ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
+ ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
+ && !scratch2.is(scratch1));
+
+ // Keep track of the current object in register reg.
+ Register reg = object_reg;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ __ sw(reg, MemOperand(sp));
+ }
+
+ // Check the maps in the prototype chain.
+ // Traverse the prototype chain from the object and do map checks.
+ 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());
+
+ Handle<JSObject> prototype(JSObject::cast(current->GetPrototype()));
+ if (!current->HasFastProperties() &&
+ !current->IsJSGlobalObject() &&
+ !current->IsJSGlobalProxy()) {
+ if (!name->IsUniqueName()) {
+ ASSERT(name->IsString());
+ name = factory()->InternalizeString(Handle<String>::cast(name));
+ }
+ ASSERT(current->property_dictionary()->FindEntry(*name) ==
+ NameDictionary::kNotFound);
+
+ GenerateDictionaryNegativeLookup(masm(), miss, reg, name,
+ scratch1, scratch2);
+
+ __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ reg = holder_reg; // From now on the object will be in holder_reg.
+ __ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
+ } else {
+ Register map_reg = scratch1;
+ if (!current.is_identical_to(first) || check == CHECK_ALL_MAPS) {
+ Handle<Map> current_map(current->map());
+ // CheckMap implicitly loads the map of |reg| into |map_reg|.
+ __ CheckMap(reg, map_reg, current_map, miss, DONT_DO_SMI_CHECK);
+ } else {
+ __ lw(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
+ }
+ // 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, scratch2, miss);
+ }
+ reg = holder_reg; // From now on the object will be in holder_reg.
+
+ 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(map_reg, Map::kPrototypeOffset));
+ } else {
+ // The prototype is in old space; load it directly.
+ __ li(reg, Operand(prototype));
+ }
+ }
+
+ if (save_at_depth == depth) {
+ __ sw(reg, MemOperand(sp));
+ }
+
+ // Go to the next object in the prototype chain.
+ current = prototype;
+ }
+
+ // Log the check depth.
+ LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
+
+ if (!holder.is_identical_to(first) || check == CHECK_ALL_MAPS) {
+ // Check the holder map.
+ __ CheckMap(reg, scratch1, Handle<Map>(holder->map()), miss,
+ DONT_DO_SMI_CHECK);
+ }
+
+ // 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.
+ GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss);
+
+ // Return the register containing the holder.
+ return reg;
+}
+
+
+void BaseLoadStubCompiler::HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) {
+ if (!miss->is_unused()) {
+ __ Branch(success);
+ __ bind(miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+ }
+}
+
+
+void BaseStoreStubCompiler::HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) {
+ if (!miss->is_unused()) {
+ __ b(success);
+ GenerateRestoreName(masm(), miss, name);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+ }
+}
+
+
+Register BaseLoadStubCompiler::CallbackHandlerFrontend(
+ Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* success,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label miss;
+
+ Register reg = HandlerFrontendHeader(object, object_reg, holder, name, &miss);
+
+ if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+ ASSERT(!reg.is(scratch2()));
+ ASSERT(!reg.is(scratch3()));
+ ASSERT(!reg.is(scratch4()));
+
+ // Load the properties dictionary.
+ Register dictionary = scratch4();
+ __ lw(dictionary, FieldMemOperand(reg, JSObject::kPropertiesOffset));
+
+ // Probe the dictionary.
+ Label probe_done;
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm(),
+ &miss,
+ &probe_done,
+ dictionary,
+ this->name(),
+ scratch2(),
+ scratch3());
+ __ bind(&probe_done);
+
+ // If probing finds an entry in the dictionary, scratch3 contains the
+ // pointer into the dictionary. Check that the value is the callback.
+ Register pointer = scratch3();
+ const int kElementsStartOffset = NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ lw(scratch2(), FieldMemOperand(pointer, kValueOffset));
+ __ Branch(&miss, ne, scratch2(), Operand(callback));
+ }
+
+ HandlerFrontendFooter(name, success, &miss);
+ return reg;
+}
+
+
+void BaseLoadStubCompiler::NonexistentHandlerFrontend(
+ Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Label* success,
+ Handle<GlobalObject> global) {
+ Label miss;
+
+ HandlerFrontendHeader(object, receiver(), last, name, &miss);
+
+ // If the last object in the prototype chain is a global object,
+ // check that the global property cell is empty.
+ if (!global.is_null()) {
+ GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss);
+ }
+
+ HandlerFrontendFooter(name, success, &miss);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadField(Register reg,
+ Handle<JSObject> holder,
+ PropertyIndex field,
+ Representation representation) {
+ if (!reg.is(receiver())) __ mov(receiver(), reg);
+ if (kind() == Code::LOAD_IC) {
+ LoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode(isolate()));
+ } else {
+ KeyedLoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode(isolate()));
+ }
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadConstant(Handle<Object> value) {
+ // Return the constant value.
+ __ LoadObject(v0, value);
+ __ Ret();
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadCallback(
+ Register reg,
+ Handle<ExecutableAccessorInfo> callback) {
+ // 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_
+ if (heap()->InNewSpace(callback->data())) {
+ __ li(scratch3(), callback);
+ __ lw(scratch3(), FieldMemOperand(scratch3(),
+ ExecutableAccessorInfo::kDataOffset));
+ } else {
+ __ li(scratch3(), Handle<Object>(callback->data(), isolate()));
+ }
+ __ Subu(sp, sp, 6 * kPointerSize);
+ __ sw(reg, MemOperand(sp, 5 * kPointerSize));
+ __ sw(scratch3(), MemOperand(sp, 4 * kPointerSize));
+ __ LoadRoot(scratch3(), Heap::kUndefinedValueRootIndex);
+ __ sw(scratch3(), MemOperand(sp, 3 * kPointerSize));
+ __ sw(scratch3(), MemOperand(sp, 2 * kPointerSize));
+ __ li(scratch4(),
+ Operand(ExternalReference::isolate_address(isolate())));
+ __ sw(scratch4(), MemOperand(sp, 1 * kPointerSize));
+ __ sw(name(), MemOperand(sp, 0 * kPointerSize));
+
+ Address getter_address = v8::ToCData<Address>(callback->getter());
+ bool returns_handle =
+ !CallbackTable::ReturnsVoid(isolate(), getter_address);
+
+ Register first_arg = returns_handle ? a1 : a0;
+ Register second_arg = returns_handle ? a2 : a1;
+ Register third_arg = returns_handle ? a3 : a2;
+
+ __ mov(a2, scratch2()); // Saved in case scratch2 == a1.
+ __ mov(first_arg, sp); // (first argument - see note below) = Handle<Name>
+
+ // 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, if returns_handle is true.
+ // CallApiFunctionAndReturn will set 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.
+ __ sw(a2, MemOperand(sp, kPointerSize));
+ // (second argument - see note above) = AccessorInfo&
+ __ Addu(second_arg, sp, kPointerSize);
+
+ const int kStackUnwindSpace = kFastApiCallArguments + 1;
+
+ ApiFunction fun(getter_address);
+ ExternalReference::Type type =
+ returns_handle ?
+ ExternalReference::DIRECT_GETTER_CALL :
+ ExternalReference::DIRECT_GETTER_CALL_NEW;
+ ExternalReference ref = ExternalReference(&fun, type, isolate());
+
+ Address thunk_address = returns_handle
+ ? FUNCTION_ADDR(&InvokeAccessorGetter)
+ : FUNCTION_ADDR(&InvokeAccessorGetterCallback);
+ ExternalReference::Type thunk_type =
+ returns_handle ?
+ ExternalReference::PROFILING_GETTER_CALL :
+ ExternalReference::PROFILING_GETTER_CALL_NEW;
+ ApiFunction thunk_fun(thunk_address);
+ ExternalReference thunk_ref = ExternalReference(&thunk_fun, thunk_type,
+ isolate());
+ __ CallApiFunctionAndReturn(ref,
+ getter_address,
+ thunk_ref,
+ third_arg,
+ kStackUnwindSpace,
+ returns_handle,
+ 5);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadInterceptor(
+ Register holder_reg,
+ Handle<JSObject> object,
+ Handle<JSObject> interceptor_holder,
+ LookupResult* lookup,
+ Handle<Name> name) {
+ ASSERT(interceptor_holder->HasNamedInterceptor());
+ ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // So far the most popular follow ups for interceptor loads are FIELD
+ // and CALLBACKS, so inline only them, other cases may be added
+ // later.
+ bool compile_followup_inline = false;
+ if (lookup->IsFound() && lookup->IsCacheable()) {
+ if (lookup->IsField()) {
+ compile_followup_inline = true;
+ } else if (lookup->type() == CALLBACKS &&
+ lookup->GetCallbackObject()->IsExecutableAccessorInfo()) {
+ ExecutableAccessorInfo* callback =
+ ExecutableAccessorInfo::cast(lookup->GetCallbackObject());
+ compile_followup_inline = callback->getter() != NULL &&
+ callback->IsCompatibleReceiver(*object);
+ }
+ }
+
+ if (compile_followup_inline) {
+ // Compile the interceptor call, followed by inline code to load the
+ // property from further up the prototype chain if the call fails.
+ // Check that the maps haven't changed.
+ ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
+
+ // Preserve the receiver register explicitly whenever it is different from
+ // the holder and it is needed should the interceptor return without any
+ // result. The CALLBACKS case needs the receiver to be passed into C++ code,
+ // the FIELD case might cause a miss during the prototype check.
+ bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
+ bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
+ (lookup->type() == CALLBACKS || must_perfrom_prototype_check);
+
+ // Save necessary data before invoking an interceptor.
+ // Requires a frame to make GC aware of pushed pointers.
+ {
+ FrameScope frame_scope(masm(), StackFrame::INTERNAL);
+ if (must_preserve_receiver_reg) {
+ __ Push(receiver(), holder_reg, this->name());
+ } else {
+ __ Push(holder_reg, this->name());
+ }
+ // 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,
+ this->name(),
+ 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(this->name());
+ __ pop(holder_reg);
+ if (must_preserve_receiver_reg) {
+ __ pop(receiver());
+ }
+ // Leave the internal frame.
+ }
+ GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup);
+ } else { // !compile_followup_inline
+ // Call the runtime system to load the interceptor.
+ // Check that the maps haven't changed.
+ PushInterceptorArguments(masm(), receiver(), holder_reg,
+ this->name(), interceptor_holder);
+
+ ExternalReference ref = ExternalReference(
+ IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), isolate());
+ __ TailCallExternalReference(ref, 6, 1);
+ }
+}
+
+
+void CallStubCompiler::GenerateNameCheck(Handle<Name> name, Label* miss) {
+ if (kind_ == Code::KEYED_CALL_IC) {
+ __ Branch(miss, ne, a2, Operand(name));
+ }
+}
+
+
+void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss) {
+ ASSERT(holder->IsGlobalObject());
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ // Get the receiver from the stack.
+ __ lw(a0, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the maps haven't changed.
+ __ JumpIfSmi(a0, miss);
+ CheckPrototypes(object, a0, holder, a3, a1, t0, name, miss);
+}
+
+
+void CallStubCompiler::GenerateLoadFunctionFromCell(
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Label* miss) {
+ // Get the value from the cell.
+ __ li(a3, Operand(cell));
+ __ lw(a1, FieldMemOperand(a3, Cell::kValueOffset));
+
+ // Check that the cell contains the same 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
+ // function can all use this call IC. Before we load through the
+ // function, we have to verify that it still is a function.
+ __ JumpIfSmi(a1, miss);
+ __ GetObjectType(a1, a3, a3);
+ __ Branch(miss, ne, a3, Operand(JS_FUNCTION_TYPE));
+
+ // Check the shared function info. Make sure it hasn't changed.
+ __ li(a3, Handle<SharedFunctionInfo>(function->shared()));
+ __ lw(t0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ Branch(miss, ne, t0, Operand(a3));
+ } else {
+ __ Branch(miss, ne, a1, Operand(function));
+ }
+}
+
+
+void CallStubCompiler::GenerateMissBranch() {
+ Handle<Code> code =
+ isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
+ kind_,
+ extra_state_);
+ __ Jump(code, RelocInfo::CODE_TARGET);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ PropertyIndex index,
+ Handle<Name> name) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ const int argc = arguments().immediate();
+
+ // Get the receiver of the function from the stack into a0.
+ __ lw(a0, MemOperand(sp, argc * kPointerSize));
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(a0, &miss, t0);
+
+ // Do the right check and compute the holder register.
+ Register reg = CheckPrototypes(object, a0, holder, a1, a3, t0, name, &miss);
+ GenerateFastPropertyLoad(masm(), a1, reg, index.is_inobject(holder),
+ index.translate(holder), Representation::Tagged());
+
+ GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::FIELD, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayCodeCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ Label miss;
+
+ // Check that function is still array.
+ const int argc = arguments().immediate();
+ GenerateNameCheck(name, &miss);
+ Register receiver = a1;
+
+ if (cell.is_null()) {
+ __ 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(Handle<JSObject>::cast(object), receiver, holder, a3, a0,
+ t0, name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ Handle<AllocationSite> site = isolate()->factory()->NewAllocationSite();
+ site->set_transition_info(Smi::FromInt(GetInitialFastElementsKind()));
+ Handle<Cell> site_feedback_cell = isolate()->factory()->NewCell(site);
+ __ li(a0, Operand(argc));
+ __ li(a2, Operand(site_feedback_cell));
+ __ li(a1, Operand(function));
+
+ ArrayConstructorStub stub(isolate());
+ __ TailCallStub(&stub);
+
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayPushCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null();
+
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ Register receiver = a1;
+
+ // 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(Handle<JSObject>::cast(object), receiver, holder, a3, v0, t0,
+ name, &miss);
+
+ if (argc == 0) {
+ // Nothing to do, just return the length.
+ __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ DropAndRet(argc + 1);
+ } else {
+ Label call_builtin;
+ if (argc == 1) { // Otherwise fall through to call the builtin.
+ Label attempt_to_grow_elements, with_write_barrier, check_double;
+
+ Register elements = t2;
+ 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,
+ &check_double,
+ DONT_DO_SMI_CHECK);
+
+ // Get the array's length into v0 and calculate new length.
+ __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Addu(v0, v0, Operand(Smi::FromInt(argc)));
+
+ // 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.
+ __ lw(t0, MemOperand(sp, (argc - 1) * kPointerSize));
+ __ JumpIfNotSmi(t0, &with_write_barrier);
+
+ // 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);
+ const int kEndElementsOffset =
+ FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize;
+ __ Addu(end_elements, end_elements, kEndElementsOffset);
+ __ sw(t0, MemOperand(end_elements));
+
+ // Check for a smi.
+ __ DropAndRet(argc + 1);
+
+ __ bind(&check_double);
+
+ // Check that the elements are in fast mode and writable.
+ __ CheckMap(elements,
+ a0,
+ Heap::kFixedDoubleArrayMapRootIndex,
+ &call_builtin,
+ DONT_DO_SMI_CHECK);
+
+ // Get the array's length into r0 and calculate new length.
+ __ lw(a0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Addu(a0, a0, Operand(Smi::FromInt(argc)));
+
+ // Get the elements' length.
+ __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
+
+ // Check if we could survive without allocation.
+ __ Branch(&call_builtin, gt, a0, Operand(t0));
+
+ __ lw(t0, MemOperand(sp, (argc - 1) * kPointerSize));
+ __ StoreNumberToDoubleElements(
+ t0, a0, elements, a3, t1, a2, t5,
+ &call_builtin, argc * kDoubleSize);
+
+ // Save new length.
+ __ sw(a0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+
+ // Check for a smi.
+ __ DropAndRet(argc + 1);
+
+ __ bind(&with_write_barrier);
+
+ __ 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);
+ __ CheckFastSmiElements(a3, t3, &call_builtin);
+
+ __ lw(t3, FieldMemOperand(t0, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHeapNumberMapRootIndex);
+ __ Branch(&call_builtin, eq, t3, Operand(at));
+ // edx: receiver
+ // a3: map
+ Label try_holey_map;
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_ELEMENTS,
+ a3,
+ t3,
+ &try_holey_map);
+ __ mov(a2, receiver);
+ ElementsTransitionGenerator::
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
+ __ jmp(&fast_object);
+
+ __ bind(&try_holey_map);
+ __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS,
+ FAST_HOLEY_ELEMENTS,
+ a3,
+ t3,
+ &call_builtin);
+ __ mov(a2, receiver);
+ ElementsTransitionGenerator::
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
+ __ 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);
+ __ DropAndRet(argc + 1);
+
+ __ bind(&attempt_to_grow_elements);
+ // v0: array's length + 1.
+ // t0: elements' length.
+
+ if (!FLAG_inline_new) {
+ __ 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(isolate());
+ ExternalReference new_space_allocation_limit =
+ ExternalReference::new_space_allocation_limit_address(isolate());
+
+ const int kAllocationDelta = 4;
+ // Load top and check if it is the end of elements.
+ __ sll(end_elements, v0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(end_elements, elements, end_elements);
+ __ Addu(end_elements, end_elements, Operand(kEndElementsOffset));
+ __ li(t3, Operand(new_space_allocation_top));
+ __ lw(a3, MemOperand(t3));
+ __ Branch(&call_builtin, ne, end_elements, Operand(a3));
+
+ __ li(t5, Operand(new_space_allocation_limit));
+ __ lw(t5, MemOperand(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(a3, MemOperand(t3));
+ // Push the argument.
+ __ sw(a2, MemOperand(end_elements));
+ // Fill the rest with holes.
+ __ LoadRoot(a3, Heap::kTheHoleValueRootIndex);
+ for (int i = 1; i < kAllocationDelta; i++) {
+ __ sw(a3, MemOperand(end_elements, i * kPointerSize));
+ }
+
+ // Update elements' and array's sizes.
+ __ sw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Addu(t0, t0, Operand(Smi::FromInt(kAllocationDelta)));
+ __ sw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
+
+ // Elements are in new space, so write barrier is not required.
+ __ DropAndRet(argc + 1);
+ }
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(
+ ExternalReference(Builtins::c_ArrayPush, isolate()), argc + 1, 1);
+ }
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayPopCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ 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(Handle<JSObject>::cast(object), receiver, holder, elements,
+ t0, v0, name, &miss);
+
+ // 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);
+
+ // Get the array's length into t0 and calculate new length.
+ __ lw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Subu(t0, t0, Operand(Smi::FromInt(1)));
+ __ Branch(&return_undefined, lt, t0, Operand(zero_reg));
+
+ // Get the last element.
+ __ LoadRoot(t2, Heap::kTheHoleValueRootIndex);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ // We can't address the last element in one operation. Compute the more
+ // expensive shift first, and use an offset later on.
+ __ sll(t1, t0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(elements, elements, t1);
+ __ lw(v0, FieldMemOperand(elements, FixedArray::kHeaderSize));
+ __ Branch(&call_builtin, eq, v0, Operand(t2));
+
+ // Set the array's length.
+ __ sw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+
+ // Fill with the hole.
+ __ sw(t2, FieldMemOperand(elements, FixedArray::kHeaderSize));
+ __ DropAndRet(argc + 1);
+
+ __ bind(&return_undefined);
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ __ DropAndRet(argc + 1);
+
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(
+ ExternalReference(Builtins::c_ArrayPop, isolate()), argc + 1, 1);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ 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_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.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ v0,
+ &miss);
+ ASSERT(!object.is_identical_to(holder));
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ v0, holder, a1, a3, t0, name, &miss);
+
+ Register receiver = a1;
+ Register index = t1;
+ Register result = v0;
+ __ lw(receiver, MemOperand(sp, argc * kPointerSize));
+ if (argc > 0) {
+ __ lw(index, MemOperand(sp, (argc - 1) * kPointerSize));
+ } else {
+ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
+ }
+
+ 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());
+ __ DropAndRet(argc + 1);
+
+ StubRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ LoadRoot(v0, Heap::kNanValueRootIndex);
+ __ DropAndRet(argc + 1);
+ }
+
+ __ bind(&miss);
+ // Restore function name in a2.
+ __ li(a2, name);
+ __ bind(&name_miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringCharAtCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ 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_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.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ v0,
+ &miss);
+ ASSERT(!object.is_identical_to(holder));
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ v0, holder, a1, a3, t0, name, &miss);
+
+ Register receiver = v0;
+ Register index = t1;
+ Register scratch = a3;
+ Register result = v0;
+ __ lw(receiver, MemOperand(sp, argc * kPointerSize));
+ if (argc > 0) {
+ __ lw(index, MemOperand(sp, (argc - 1) * kPointerSize));
+ } else {
+ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
+ }
+
+ 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());
+ __ DropAndRet(argc + 1);
+
+ StubRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ LoadRoot(v0, Heap::kempty_stringRootIndex);
+ __ DropAndRet(argc + 1);
+ }
+
+ __ bind(&miss);
+ // Restore function name in a2.
+ __ li(a2, name);
+ __ bind(&name_miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : 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 Handle<Code>::null();
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(a1, &miss);
+
+ CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, v0, a3, t0,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the char code argument.
+ Register code = a1;
+ __ lw(code, MemOperand(sp, 0 * kPointerSize));
+
+ // Check the code is a smi.
+ Label slow;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(code, &slow);
+
+ // Convert the smi code to uint16.
+ __ And(code, code, Operand(Smi::FromInt(0xffff)));
+
+ StringCharFromCodeGenerator generator(code, v0);
+ generator.GenerateFast(masm());
+ __ DropAndRet(argc + 1);
+
+ StubRuntimeCallHelper 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);
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+
+ __ bind(&miss);
+ // a2: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileMathFloorCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : 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 Handle<Code>::null();
+
+ Label miss, slow;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(a1, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, a0, a3, t0,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the (only) argument into v0.
+ __ lw(v0, MemOperand(sp, 0 * kPointerSize));
+
+ // If the argument is a smi, just return.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ And(t0, v0, Operand(kSmiTagMask));
+ __ DropAndRet(argc + 1, eq, t0, Operand(zero_reg));
+
+ __ CheckMap(v0, a1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
+
+ Label wont_fit_smi, no_fpu_error, restore_fcsr_and_return;
+
+ // If fpu is enabled, we use the floor instruction.
+
+ // Load the HeapNumber value.
+ __ ldc1(f0, FieldMemOperand(v0, HeapNumber::kValueOffset));
+
+ // Backup FCSR.
+ __ cfc1(a3, FCSR);
+ // Clearing FCSR clears the exception mask with no side-effects.
+ __ ctc1(zero_reg, FCSR);
+ // Convert the argument to an integer.
+ __ floor_w_d(f0, f0);
+
+ // Start checking for special cases.
+ // Get the argument exponent and clear the sign bit.
+ __ lw(t1, FieldMemOperand(v0, HeapNumber::kValueOffset + kPointerSize));
+ __ And(t2, t1, Operand(~HeapNumber::kSignMask));
+ __ srl(t2, t2, HeapNumber::kMantissaBitsInTopWord);
+
+ // Retrieve FCSR and check for fpu errors.
+ __ cfc1(t5, FCSR);
+ __ And(t5, t5, Operand(kFCSRExceptionFlagMask));
+ __ Branch(&no_fpu_error, eq, t5, Operand(zero_reg));
+
+ // Check for NaN, Infinity, and -Infinity.
+ // They are invariant through a Math.Floor call, so just
+ // return the original argument.
+ __ Subu(t3, t2, Operand(HeapNumber::kExponentMask
+ >> HeapNumber::kMantissaBitsInTopWord));
+ __ Branch(&restore_fcsr_and_return, eq, t3, Operand(zero_reg));
+ // We had an overflow or underflow in the conversion. Check if we
+ // have a big exponent.
+ // If greater or equal, the argument is already round and in v0.
+ __ Branch(&restore_fcsr_and_return, ge, t3,
+ Operand(HeapNumber::kMantissaBits));
+ __ Branch(&wont_fit_smi);
+
+ __ bind(&no_fpu_error);
+ // Move the result back to v0.
+ __ mfc1(v0, f0);
+ // Check if the result fits into a smi.
+ __ Addu(a1, v0, Operand(0x40000000));
+ __ Branch(&wont_fit_smi, lt, a1, Operand(zero_reg));
+ // Tag the result.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ sll(v0, v0, kSmiTagSize);
+
+ // Check for -0.
+ __ Branch(&restore_fcsr_and_return, ne, v0, Operand(zero_reg));
+ // t1 already holds the HeapNumber exponent.
+ __ And(t0, t1, Operand(HeapNumber::kSignMask));
+ // If our HeapNumber is negative it was -0, so load its address and return.
+ // Else v0 is loaded with 0, so we can also just return.
+ __ Branch(&restore_fcsr_and_return, eq, t0, Operand(zero_reg));
+ __ lw(v0, MemOperand(sp, 0 * kPointerSize));
+
+ __ bind(&restore_fcsr_and_return);
+ // Restore FCSR and return.
+ __ ctc1(a3, FCSR);
+
+ __ DropAndRet(argc + 1);
+
+ __ bind(&wont_fit_smi);
+ // Restore FCSR and fall to slow case.
+ __ ctc1(a3, FCSR);
+
+ __ bind(&slow);
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+
+ __ bind(&miss);
+ // a2: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileMathAbsCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : 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 Handle<Code>::null();
+
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+ if (cell.is_null()) {
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(a1, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, v0, a3, t0,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the (only) argument into v0.
+ __ lw(v0, MemOperand(sp, 0 * kPointerSize));
+
+ // Check if the argument is a smi.
+ Label not_smi;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(v0, &not_smi);
+
+ // Do bitwise not or do nothing depending on the sign of the
+ // argument.
+ __ sra(t0, v0, kBitsPerInt - 1);
+ __ Xor(a1, v0, t0);
+
+ // Add 1 or do nothing depending on the sign of the argument.
+ __ Subu(v0, a1, t0);
+
+ // If the result is still negative, go to the slow case.
+ // This only happens for the most negative smi.
+ Label slow;
+ __ Branch(&slow, lt, v0, Operand(zero_reg));
+
+ // Smi case done.
+ __ DropAndRet(argc + 1);
+
+ // Check if the argument is a heap number and load its exponent and
+ // sign.
+ __ bind(&not_smi);
+ __ CheckMap(v0, a1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
+ __ lw(a1, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+
+ // Check the sign of the argument. If the argument is positive,
+ // just return it.
+ Label negative_sign;
+ __ And(t0, a1, Operand(HeapNumber::kSignMask));
+ __ Branch(&negative_sign, ne, t0, Operand(zero_reg));
+ __ DropAndRet(argc + 1);
+
+ // If the argument is negative, clear the sign, and return a new
+ // number.
+ __ bind(&negative_sign);
+ __ Xor(a1, a1, Operand(HeapNumber::kSignMask));
+ __ lw(a3, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ LoadRoot(t2, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(v0, t0, t1, t2, &slow);
+ __ sw(a1, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+ __ sw(a3, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ DropAndRet(argc + 1);
+
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ __ bind(&slow);
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+
+ __ bind(&miss);
+ // a2: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileFastApiCall(
+ const CallOptimization& optimization,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> 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 Handle<Code>::null();
+ if (!cell.is_null()) return Handle<Code>::null();
+ if (!object->IsJSObject()) return Handle<Code>::null();
+ int depth = optimization.GetPrototypeDepthOfExpectedType(
+ 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.
+ const int argc = arguments().immediate();
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(a1, &miss_before_stack_reserved);
+
+ __ IncrementCounter(counters->call_const(), 1, a0, a3);
+ __ IncrementCounter(counters->call_const_fast_api(), 1, a0, a3);
+
+ ReserveSpaceForFastApiCall(masm(), a0);
+
+ // Check that the maps haven't changed and find a Holder as a side effect.
+ CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, a0, a3, t0, name,
+ depth, &miss);
+
+ GenerateFastApiDirectCall(masm(), optimization, argc);
+
+ __ bind(&miss);
+ FreeSpaceForFastApiCall(masm());
+
+ __ bind(&miss_before_stack_reserved);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Label* success) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ if (check != NUMBER_CHECK) {
+ __ 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);
+ switch (check) {
+ case RECEIVER_MAP_CHECK:
+ __ IncrementCounter(isolate()->counters()->call_const(), 1, a0, a3);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(Handle<JSObject>::cast(object), a1, holder, a0, a3, t0,
+ name, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ lw(a3, FieldMemOperand(a1, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a3, MemOperand(sp, argc * kPointerSize));
+ }
+ break;
+
+ case STRING_CHECK:
+ // Check that the object is a string.
+ __ 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(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ a0, holder, a3, a1, t0, name, &miss);
+ break;
+
+ case SYMBOL_CHECK:
+ // Check that the object is a symbol.
+ __ GetObjectType(a1, a1, a3);
+ __ Branch(&miss, ne, a3, Operand(SYMBOL_TYPE));
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::SYMBOL_FUNCTION_INDEX, a0, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ a0, holder, a3, a1, t0, name, &miss);
+ break;
+
+ case NUMBER_CHECK: {
+ Label fast;
+ // Check that the object is a smi or a heap number.
+ __ 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(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ a0, holder, a3, a1, t0, name, &miss);
+ break;
+ }
+ case BOOLEAN_CHECK: {
+ Label fast;
+ // Check that the object is a boolean.
+ __ LoadRoot(t0, Heap::kTrueValueRootIndex);
+ __ Branch(&fast, eq, a1, Operand(t0));
+ __ LoadRoot(t0, Heap::kFalseValueRootIndex);
+ __ Branch(&miss, ne, a1, Operand(t0));
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::BOOLEAN_FUNCTION_INDEX, a0, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ a0, holder, a3, a1, t0, name, &miss);
+ break;
+ }
+ }
+
+ __ jmp(success);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+
+ GenerateMissBranch();
+}
+
+
+void CallStubCompiler::CompileHandlerBackend(Handle<JSFunction> function) {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallConstant(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Handle<JSFunction> function) {
+ if (HasCustomCallGenerator(function)) {
+ Handle<Code> code = CompileCustomCall(object, holder,
+ Handle<Cell>::null(),
+ function, Handle<String>::cast(name),
+ Code::CONSTANT);
+ // A null handle means bail out to the regular compiler code below.
+ if (!code.is_null()) return code;
+ }
+
+ Label success;
+
+ CompileHandlerFrontend(object, holder, name, check, &success);
+ __ bind(&success);
+ CompileHandlerBackend(function);
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+ 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_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_state_);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::INTERCEPTOR, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallGlobal(
+ Handle<JSObject> object,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ Handle<JSFunction> function,
+ Handle<Name> name) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ if (HasCustomCallGenerator(function)) {
+ Handle<Code> code = CompileCustomCall(
+ object, holder, cell, function, Handle<String>::cast(name),
+ Code::NORMAL);
+ // 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
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ lw(a3, FieldMemOperand(a0, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a3, MemOperand(sp, argc * kPointerSize));
+ }
+
+ // Set up the context (function already in r1).
+ __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+
+ // Jump to the cached code (tail call).
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->call_global_inline(), 1, a3, t0);
+ ParameterCount expected(function->shared()->formal_parameter_count());
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ // 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);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::NORMAL, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreCallback(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
+
+ __ push(receiver()); // Receiver.
+ __ li(at, Operand(callback)); // Callback info.
+ __ push(at);
+ __ li(at, Operand(name));
+ __ Push(at, value());
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_callback_property =
+ ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate());
+ __ TailCallExternalReference(store_callback_property, 4, 1);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void StoreStubCompiler::GenerateStoreViaSetter(
+ MacroAssembler* masm,
+ Handle<JSFunction> setter) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Save value register, so we can restore it later.
+ __ push(a0);
+
+ if (!setter.is_null()) {
+ // Call the JavaScript setter with receiver and value on the stack.
+ __ push(a1);
+ __ push(a0);
+ ParameterCount actual(1);
+ ParameterCount expected(setter);
+ __ InvokeFunction(setter, expected, actual,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+ } else {
+ // If we generate a global code snippet for deoptimization only, remember
+ // the place to continue after deoptimization.
+ masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // We have to return the passed value, not the return value of the setter.
+ __ pop(v0);
+
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ }
+ __ Ret();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> StoreStubCompiler::CompileStoreInterceptor(
+ Handle<JSObject> object,
+ Handle<Name> name) {
+ Label miss;
+
+ // Check that the map of the object hasn't changed.
+ __ CheckMap(receiver(), scratch1(), Handle<Map>(object->map()), &miss,
+ DO_SMI_CHECK);
+
+ // Perform global security token check if needed.
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(receiver(), scratch1(), &miss);
+ }
+
+ // Stub is never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ __ Push(receiver(), this->name(), value());
+
+ __ li(scratch1(), Operand(Smi::FromInt(strict_mode())));
+ __ push(scratch1()); // strict mode
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_ic_property =
+ ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate());
+ __ TailCallExternalReference(store_ic_property, 4, 1);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::INTERCEPTOR, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreGlobal(
+ Handle<GlobalObject> object,
+ Handle<PropertyCell> cell,
+ Handle<Name> name) {
+ Label miss;
+
+ // Check that the map of the global has not changed.
+ __ lw(scratch1(), FieldMemOperand(receiver(), HeapObject::kMapOffset));
+ __ Branch(&miss, ne, scratch1(), Operand(Handle<Map>(object->map())));
+
+ // 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.
+ __ li(scratch1(), Operand(cell));
+ __ LoadRoot(scratch2(), Heap::kTheHoleValueRootIndex);
+ __ lw(scratch3(), FieldMemOperand(scratch1(), Cell::kValueOffset));
+ __ Branch(&miss, eq, scratch3(), Operand(scratch2()));
+
+ // Store the value in the cell.
+ __ sw(value(), FieldMemOperand(scratch1(), Cell::kValueOffset));
+ __ mov(v0, a0); // Stored value must be returned in v0.
+ // Cells are always rescanned, so no write barrier here.
+
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(
+ counters->named_store_global_inline(), 1, scratch1(), scratch2());
+ __ Ret();
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ __ IncrementCounter(
+ counters->named_store_global_inline_miss(), 1, scratch1(), scratch2());
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, name);
+}
+
+
+Handle<Code> LoadStubCompiler::CompileLoadNonexistent(
+ Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Handle<GlobalObject> global) {
+ Label success;
+
+ NonexistentHandlerFrontend(object, last, name, &success, global);
+
+ __ bind(&success);
+ // Return undefined if maps of the full prototype chain is still the same.
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ __ Ret();
+
+ // Return the generated code.
+ return GetCode(kind(), Code::NONEXISTENT, name);
+}
+
+
+Register* LoadStubCompiler::registers() {
+ // receiver, name, scratch1, scratch2, scratch3, scratch4.
+ static Register registers[] = { a0, a2, a3, a1, t0, t1 };
+ return registers;
+}
+
+
+Register* KeyedLoadStubCompiler::registers() {
+ // receiver, name, scratch1, scratch2, scratch3, scratch4.
+ static Register registers[] = { a1, a0, a2, a3, t0, t1 };
+ return registers;
+}
+
+
+Register* StoreStubCompiler::registers() {
+ // receiver, name, value, scratch1, scratch2, scratch3.
+ static Register registers[] = { a1, a2, a0, a3, t0, t1 };
+ return registers;
+}
+
+
+Register* KeyedStoreStubCompiler::registers() {
+ // receiver, name, value, scratch1, scratch2, scratch3.
+ static Register registers[] = { a2, a1, a0, a3, t0, t1 };
+ return registers;
+}
+
+
+void KeyedLoadStubCompiler::GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) {
+ __ Branch(miss, ne, name_reg, Operand(name));
+}
+
+
+void KeyedStoreStubCompiler::GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) {
+ __ Branch(miss, ne, name_reg, Operand(name));
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm,
+ Handle<JSFunction> getter) {
+ // ----------- S t a t e -------------
+ // -- a0 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ if (!getter.is_null()) {
+ // Call the JavaScript getter with the receiver on the stack.
+ __ push(a0);
+ ParameterCount actual(0);
+ ParameterCount expected(getter);
+ __ InvokeFunction(getter, expected, actual,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+ } else {
+ // If we generate a global code snippet for deoptimization only, remember
+ // the place to continue after deoptimization.
+ masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ }
+ __ Ret();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> LoadStubCompiler::CompileLoadGlobal(
+ Handle<JSObject> object,
+ Handle<GlobalObject> global,
+ Handle<PropertyCell> cell,
+ Handle<Name> name,
+ bool is_dont_delete) {
+ Label success, miss;
+
+ __ CheckMap(
+ receiver(), scratch1(), Handle<Map>(object->map()), &miss, DO_SMI_CHECK);
+ HandlerFrontendHeader(
+ object, receiver(), Handle<JSObject>::cast(global), name, &miss);
+
+ // Get the value from the cell.
+ __ li(a3, Operand(cell));
+ __ lw(t0, FieldMemOperand(a3, Cell::kValueOffset));
+
+ // Check for deleted property if property can actually be deleted.
+ if (!is_dont_delete) {
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(&miss, eq, t0, Operand(at));
+ }
+
+ HandlerFrontendFooter(name, &success, &miss);
+ __ bind(&success);
+
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->named_load_global_stub(), 1, a1, a3);
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, t0);
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, name);
+}
+
+
+Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC(
+ MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ Handle<Name> name,
+ Code::StubType type,
+ IcCheckType check) {
+ Label miss;
+
+ if (check == PROPERTY) {
+ GenerateNameCheck(name, this->name(), &miss);
+ }
+
+ __ JumpIfSmi(receiver(), &miss);
+ Register map_reg = scratch1();
+
+ int receiver_count = receiver_maps->length();
+ int number_of_handled_maps = 0;
+ __ lw(map_reg, FieldMemOperand(receiver(), HeapObject::kMapOffset));
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map = receiver_maps->at(current);
+ if (!map->is_deprecated()) {
+ number_of_handled_maps++;
+ __ Jump(handlers->at(current), RelocInfo::CODE_TARGET,
+ eq, map_reg, Operand(receiver_maps->at(current)));
+ }
+ }
+ ASSERT(number_of_handled_maps != 0);
+
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ InlineCacheState state =
+ number_of_handled_maps > 1 ? POLYMORPHIC : MONOMORPHIC;
+ return GetICCode(kind(), type, name, state);
+}
+
+
+Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic(
+ MapHandleList* receiver_maps,
+ CodeHandleList* handler_stubs,
+ MapHandleList* transitioned_maps) {
+ Label miss;
+ __ JumpIfSmi(receiver(), &miss);
+
+ int receiver_count = receiver_maps->length();
+ __ lw(scratch1(), FieldMemOperand(receiver(), HeapObject::kMapOffset));
+ for (int i = 0; i < receiver_count; ++i) {
+ if (transitioned_maps->at(i).is_null()) {
+ __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, eq,
+ scratch1(), Operand(receiver_maps->at(i)));
+ } else {
+ Label next_map;
+ __ Branch(&next_map, ne, scratch1(), Operand(receiver_maps->at(i)));
+ __ li(transition_map(), Operand(transitioned_maps->at(i)));
+ __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET);
+ __ bind(&next_map);
+ }
+ }
+
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(
+ kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void KeyedLoadStubCompiler::GenerateLoadDictionaryElement(
+ MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label slow, miss_force_generic;
+
+ Register key = a0;
+ Register receiver = a1;
+
+ __ JumpIfNotSmi(key, &miss_force_generic);
+ __ lw(t0, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ sra(a2, a0, kSmiTagSize);
+ __ LoadFromNumberDictionary(&slow, t0, a0, v0, a2, a3, t1);
+ __ Ret();
+
+ // Slow case, key and receiver still in a0 and a1.
+ __ bind(&slow);
+ __ IncrementCounter(
+ masm->isolate()->counters()->keyed_load_external_array_slow(),
+ 1, a2, a3);
+ // Entry registers are intact.
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow);
+
+ // Miss case, call the runtime.
+ __ bind(&miss_force_generic);
+
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedLoadIC_MissForceGeneric);
+}
+
+
+static void GenerateSmiKeyCheck(MacroAssembler* masm,
+ Register key,
+ Register scratch0,
+ Register scratch1,
+ FPURegister double_scratch0,
+ FPURegister double_scratch1,
+ Label* fail) {
+ Label key_ok;
+ // Check for smi or a smi inside a heap number. We convert the heap
+ // number and check if the conversion is exact and fits into the smi
+ // range.
+ __ JumpIfSmi(key, &key_ok);
+ __ CheckMap(key,
+ scratch0,
+ Heap::kHeapNumberMapRootIndex,
+ fail,
+ DONT_DO_SMI_CHECK);
+ __ ldc1(double_scratch0, FieldMemOperand(key, HeapNumber::kValueOffset));
+ __ EmitFPUTruncate(kRoundToZero,
+ scratch0,
+ double_scratch0,
+ at,
+ double_scratch1,
+ scratch1,
+ kCheckForInexactConversion);
+
+ __ Branch(fail, ne, scratch1, Operand(zero_reg));
+
+ __ SmiTagCheckOverflow(key, scratch0, scratch1);
+ __ BranchOnOverflow(fail, scratch1);
+ __ bind(&key_ok);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreExternalArray(
+ MacroAssembler* masm,
+ ElementsKind elements_kind) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ Label slow, check_heap_number, miss_force_generic;
+
+ // Register usage.
+ Register value = a0;
+ Register key = a1;
+ Register receiver = a2;
+ // a3 mostly holds the elements array or the destination external array.
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, key, t0, t1, f2, f4, &miss_force_generic);
+
+ __ lw(a3, FieldMemOperand(receiver, JSObject::kElementsOffset));
+
+ // Check that the index is in range.
+ __ lw(t1, FieldMemOperand(a3, ExternalArray::kLengthOffset));
+ // Unsigned comparison catches both negative and too-large values.
+ __ Branch(&miss_force_generic, Ugreater_equal, key, Operand(t1));
+
+ // Handle both smis and HeapNumbers in the fast path. Go to the
+ // runtime for all other kinds of values.
+ // a3: external array.
+
+ if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) {
+ // Double to pixel conversion is only implemented in the runtime for now.
+ __ JumpIfNotSmi(value, &slow);
+ } else {
+ __ JumpIfNotSmi(value, &check_heap_number);
+ }
+ __ SmiUntag(t1, value);
+ __ lw(a3, FieldMemOperand(a3, ExternalArray::kExternalPointerOffset));
+
+ // a3: base pointer of external storage.
+ // t1: value (integer).
+
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS: {
+ // Clamp the value to [0..255].
+ // v0 is used as a scratch register here.
+ Label done;
+ __ li(v0, Operand(255));
+ // Normal branch: nop in delay slot.
+ __ Branch(&done, gt, t1, Operand(v0));
+ // Use delay slot in this branch.
+ __ Branch(USE_DELAY_SLOT, &done, lt, t1, Operand(zero_reg));
+ __ mov(v0, zero_reg); // In delay slot.
+ __ mov(v0, t1); // Value is in range 0..255.
+ __ bind(&done);
+ __ mov(t1, v0);
+
+ __ srl(t8, key, 1);
+ __ addu(t8, a3, t8);
+ __ sb(t1, MemOperand(t8, 0));
+ }
+ break;
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ srl(t8, key, 1);
+ __ addu(t8, a3, t8);
+ __ sb(t1, MemOperand(t8, 0));
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ addu(t8, a3, key);
+ __ sh(t1, MemOperand(t8, 0));
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ 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);
+ break;
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ __ sll(t8, key, 2);
+ __ addu(a3, a3, t8);
+ // a3: effective address of the double element
+ FloatingPointHelper::Destination destination;
+ destination = FloatingPointHelper::kFPURegisters;
+ FloatingPointHelper::ConvertIntToDouble(
+ masm, t1, destination,
+ f0, t2, t3, // These are: double_dst, dst_mantissa, dst_exponent.
+ t0, f2); // These are: scratch2, single_scratch.
+ __ sdc1(f0, MemOperand(a3, 0));
+ break;
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+
+ // Entry registers are intact, a0 holds the value which is the return value.
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0);
+
+ if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) {
+ // a3: external array.
+ __ bind(&check_heap_number);
+ __ GetObjectType(value, t1, t2);
+ __ Branch(&slow, ne, t2, Operand(HEAP_NUMBER_TYPE));
+
+ __ lw(a3, FieldMemOperand(a3, ExternalArray::kExternalPointerOffset));
+
+ // a3: base pointer of external storage.
+
+ // The WebGL specification leaves the behavior of storing NaN and
+ // +/-Infinity into integer arrays basically undefined. For more
+ // reproducible behavior, convert these to zero.
+
+
+ __ ldc1(f0, FieldMemOperand(a0, HeapNumber::kValueOffset));
+
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ __ cvt_s_d(f0, f0);
+ __ sll(t8, key, 1);
+ __ addu(t8, a3, t8);
+ __ swc1(f0, MemOperand(t8, 0));
+ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ __ sll(t8, key, 2);
+ __ addu(t8, a3, t8);
+ __ sdc1(f0, MemOperand(t8, 0));
+ } else {
+ __ EmitECMATruncate(t3, f0, f2, t2, t1, t5);
+
+ switch (elements_kind) {
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ srl(t8, key, 1);
+ __ addu(t8, a3, t8);
+ __ sb(t3, MemOperand(t8, 0));
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ addu(t8, a3, key);
+ __ sh(t3, MemOperand(t8, 0));
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ sll(t8, key, 1);
+ __ addu(t8, a3, t8);
+ __ sw(t3, MemOperand(t8, 0));
+ break;
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ // Entry registers are intact, a0 holds the value
+ // which is the return value.
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, a0);
+ }
+
+ // Slow case, key and receiver still in a0 and a1.
+ __ bind(&slow);
+ __ IncrementCounter(
+ masm->isolate()->counters()->keyed_load_external_array_slow(),
+ 1, a2, a3);
+ // Entry registers are intact.
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+
+ // Miss case, call the runtime.
+ __ bind(&miss_force_generic);
+
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastElement(
+ MacroAssembler* masm,
+ bool is_js_array,
+ ElementsKind elements_kind,
+ KeyedAccessStoreMode store_mode) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -- a3 : scratch
+ // -- a4 : scratch (elements)
+ // -----------------------------------
+ Label miss_force_generic, transition_elements_kind, grow, slow;
+ Label finish_store, check_capacity;
+
+ Register value_reg = a0;
+ Register key_reg = a1;
+ Register receiver_reg = a2;
+ Register scratch = t0;
+ Register elements_reg = a3;
+ Register length_reg = t1;
+ Register scratch2 = t2;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, key_reg, t0, t1, f2, f4, &miss_force_generic);
+
+ if (IsFastSmiElementsKind(elements_kind)) {
+ __ JumpIfNotSmi(value_reg, &transition_elements_kind);
+ }
+
+ // Check that the key is within bounds.
+ __ lw(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ if (is_js_array) {
+ __ lw(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ } else {
+ __ lw(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ }
+ // Compare smis.
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ __ Branch(&grow, hs, key_reg, Operand(scratch));
+ } else {
+ __ Branch(&miss_force_generic, hs, key_reg, Operand(scratch));
+ }
+
+ // Make sure elements is a fast element array, not 'cow'.
+ __ CheckMap(elements_reg,
+ scratch,
+ Heap::kFixedArrayMapRootIndex,
+ &miss_force_generic,
+ DONT_DO_SMI_CHECK);
+
+ __ bind(&finish_store);
+
+ if (IsFastSmiElementsKind(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(IsFastObjectElementsKind(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));
+ __ mov(receiver_reg, value_reg);
+ __ RecordWrite(elements_reg, // Object.
+ scratch, // Address.
+ receiver_reg, // Value.
+ kRAHasNotBeenSaved,
+ kDontSaveFPRegs);
+ }
+ // value_reg (a0) is preserved.
+ // Done.
+ __ Ret();
+
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+
+ __ bind(&transition_elements_kind);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Miss);
+
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ // Grow the array by a single element if possible.
+ __ bind(&grow);
+
+ // Make sure the array is only growing by a single element, anything else
+ // must be handled by the runtime.
+ __ Branch(&miss_force_generic, ne, key_reg, Operand(scratch));
+
+ // Check for the empty array, and preallocate a small backing store if
+ // possible.
+ __ lw(length_reg,
+ FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ lw(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ LoadRoot(at, Heap::kEmptyFixedArrayRootIndex);
+ __ Branch(&check_capacity, ne, elements_reg, Operand(at));
+
+ int size = FixedArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ Allocate(size, elements_reg, scratch, scratch2, &slow, TAG_OBJECT);
+
+ __ LoadRoot(scratch, Heap::kFixedArrayMapRootIndex);
+ __ sw(scratch, FieldMemOperand(elements_reg, JSObject::kMapOffset));
+ __ li(scratch, Operand(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
+ __ sw(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
+ for (int i = 1; i < JSArray::kPreallocatedArrayElements; ++i) {
+ __ sw(scratch, FieldMemOperand(elements_reg, FixedArray::SizeFor(i)));
+ }
+
+ // Store the element at index zero.
+ __ sw(value_reg, FieldMemOperand(elements_reg, FixedArray::SizeFor(0)));
+
+ // Install the new backing store in the JSArray.
+ __ sw(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ RecordWriteField(receiver_reg, JSObject::kElementsOffset, elements_reg,
+ scratch, kRAHasNotBeenSaved, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+
+ // Increment the length of the array.
+ __ li(length_reg, Operand(Smi::FromInt(1)));
+ __ Ret(USE_DELAY_SLOT);
+ __ sw(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+
+ __ bind(&check_capacity);
+ // Check for cow elements, in general they are not handled by this stub
+ __ CheckMap(elements_reg,
+ scratch,
+ Heap::kFixedCOWArrayMapRootIndex,
+ &miss_force_generic,
+ DONT_DO_SMI_CHECK);
+
+ __ lw(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ __ Branch(&slow, hs, length_reg, Operand(scratch));
+
+ // Grow the array and finish the store.
+ __ Addu(length_reg, length_reg, Operand(Smi::FromInt(1)));
+ __ sw(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ jmp(&finish_store);
+
+ __ bind(&slow);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+ }
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
+ MacroAssembler* masm,
+ bool is_js_array,
+ KeyedAccessStoreMode store_mode) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -- a3 : scratch (elements backing store)
+ // -- t0 : scratch (elements_reg)
+ // -- t1 : scratch (mantissa_reg)
+ // -- t2 : scratch (exponent_reg)
+ // -- t3 : scratch4
+ // -- t4 : scratch
+ // -----------------------------------
+ Label miss_force_generic, transition_elements_kind, grow, slow;
+ Label finish_store, check_capacity;
+
+ Register value_reg = a0;
+ Register key_reg = a1;
+ Register receiver_reg = a2;
+ Register elements_reg = a3;
+ Register scratch1 = t0;
+ Register scratch2 = t1;
+ Register scratch3 = t2;
+ Register scratch4 = t3;
+ Register scratch5 = t4;
+ Register length_reg = t3;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, key_reg, t0, t1, f2, f4, &miss_force_generic);
+
+ __ lw(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+
+ // Check that the key is within bounds.
+ if (is_js_array) {
+ __ lw(scratch1, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ } else {
+ __ lw(scratch1,
+ FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ }
+ // Compare smis, unsigned compare catches both negative and out-of-bound
+ // indexes.
+ if (IsGrowStoreMode(store_mode)) {
+ __ Branch(&grow, hs, key_reg, Operand(scratch1));
+ } else {
+ __ Branch(&miss_force_generic, hs, key_reg, Operand(scratch1));
+ }
+
+ __ bind(&finish_store);
+
+ __ StoreNumberToDoubleElements(value_reg,
+ key_reg,
+ // All registers after this are overwritten.
+ elements_reg,
+ scratch1,
+ scratch2,
+ scratch3,
+ scratch4,
+ &transition_elements_kind);
+
+ __ Ret(USE_DELAY_SLOT);
+ __ mov(v0, value_reg); // In delay slot.
+
+ // Handle store cache miss, replacing the ic with the generic stub.
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+
+ __ bind(&transition_elements_kind);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Miss);
+
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ // Grow the array by a single element if possible.
+ __ bind(&grow);
+
+ // Make sure the array is only growing by a single element, anything else
+ // must be handled by the runtime.
+ __ Branch(&miss_force_generic, ne, key_reg, Operand(scratch1));
+
+ // Transition on values that can't be stored in a FixedDoubleArray.
+ Label value_is_smi;
+ __ JumpIfSmi(value_reg, &value_is_smi);
+ __ lw(scratch1, FieldMemOperand(value_reg, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHeapNumberMapRootIndex);
+ __ Branch(&transition_elements_kind, ne, scratch1, Operand(at));
+ __ bind(&value_is_smi);
+
+ // Check for the empty array, and preallocate a small backing store if
+ // possible.
+ __ lw(length_reg,
+ FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ lw(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ LoadRoot(at, Heap::kEmptyFixedArrayRootIndex);
+ __ Branch(&check_capacity, ne, elements_reg, Operand(at));
+
+ int size = FixedDoubleArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ Allocate(size, elements_reg, scratch1, scratch2, &slow, TAG_OBJECT);
+
+ // Initialize the new FixedDoubleArray.
+ __ LoadRoot(scratch1, Heap::kFixedDoubleArrayMapRootIndex);
+ __ sw(scratch1, FieldMemOperand(elements_reg, JSObject::kMapOffset));
+ __ li(scratch1, Operand(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
+ __ sw(scratch1,
+ FieldMemOperand(elements_reg, FixedDoubleArray::kLengthOffset));
+
+ __ mov(scratch1, elements_reg);
+ __ StoreNumberToDoubleElements(value_reg,
+ key_reg,
+ // All registers after this are overwritten.
+ scratch1,
+ scratch2,
+ scratch3,
+ scratch4,
+ scratch5,
+ &transition_elements_kind);
+
+ __ li(scratch1, Operand(kHoleNanLower32));
+ __ li(scratch2, Operand(kHoleNanUpper32));
+ for (int i = 1; i < JSArray::kPreallocatedArrayElements; i++) {
+ int offset = FixedDoubleArray::OffsetOfElementAt(i);
+ __ sw(scratch1, FieldMemOperand(elements_reg, offset));
+ __ sw(scratch2, FieldMemOperand(elements_reg, offset + kPointerSize));
+ }
+
+ // Install the new backing store in the JSArray.
+ __ sw(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ RecordWriteField(receiver_reg, JSObject::kElementsOffset, elements_reg,
+ scratch1, kRAHasNotBeenSaved, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+
+ // Increment the length of the array.
+ __ li(length_reg, Operand(Smi::FromInt(1)));
+ __ sw(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ Ret(USE_DELAY_SLOT);
+ __ lw(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+
+ __ bind(&check_capacity);
+ // Make sure that the backing store can hold additional elements.
+ __ lw(scratch1,
+ FieldMemOperand(elements_reg, FixedDoubleArray::kLengthOffset));
+ __ Branch(&slow, hs, length_reg, Operand(scratch1));
+
+ // Grow the array and finish the store.
+ __ Addu(length_reg, length_reg, Operand(Smi::FromInt(1)));
+ __ sw(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ __ jmp(&finish_store);
+
+ __ bind(&slow);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+ }
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_MIPS
diff --git a/chromium/v8/src/mirror-debugger.js b/chromium/v8/src/mirror-debugger.js
new file mode 100644
index 00000000000..3b360bb5d77
--- /dev/null
+++ b/chromium/v8/src/mirror-debugger.js
@@ -0,0 +1,2653 @@
+// 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:
+//
+// * 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.
+
+// Handle id counters.
+var next_handle_ = 0;
+var next_transient_handle_ = -1;
+
+// Mirror cache.
+var mirror_cache_ = [];
+
+
+/**
+ * Clear the mirror handle cache.
+ */
+function ClearMirrorCache() {
+ next_handle_ = 0;
+ mirror_cache_ = [];
+}
+
+
+/**
+ * Returns the mirror for a specified value or object.
+ *
+ * @param {value or Object} value the value or object to retreive the mirror for
+ * @param {boolean} transient indicate whether this object is transient and
+ * should not be added to the mirror cache. The default is not transient.
+ * @returns {Mirror} the mirror reflects the passed value or object
+ */
+function MakeMirror(value, opt_transient) {
+ var mirror;
+
+ // Look for non transient mirrors in the mirror cache.
+ if (!opt_transient) {
+ for (id in mirror_cache_) {
+ mirror = mirror_cache_[id];
+ if (mirror.value() === value) {
+ return mirror;
+ }
+ // Special check for NaN as NaN == NaN is false.
+ if (mirror.isNumber() && isNaN(mirror.value()) &&
+ typeof value == 'number' && isNaN(value)) {
+ return mirror;
+ }
+ }
+ }
+
+ if (IS_UNDEFINED(value)) {
+ mirror = new UndefinedMirror();
+ } else if (IS_NULL(value)) {
+ mirror = new NullMirror();
+ } else if (IS_BOOLEAN(value)) {
+ mirror = new BooleanMirror(value);
+ } else if (IS_NUMBER(value)) {
+ mirror = new NumberMirror(value);
+ } else if (IS_STRING(value)) {
+ mirror = new StringMirror(value);
+ } else if (IS_ARRAY(value)) {
+ mirror = new ArrayMirror(value);
+ } else if (IS_DATE(value)) {
+ mirror = new DateMirror(value);
+ } else if (IS_FUNCTION(value)) {
+ mirror = new FunctionMirror(value);
+ } else if (IS_REGEXP(value)) {
+ mirror = new RegExpMirror(value);
+ } else if (IS_ERROR(value)) {
+ mirror = new ErrorMirror(value);
+ } else if (IS_SCRIPT(value)) {
+ mirror = new ScriptMirror(value);
+ } else {
+ mirror = new ObjectMirror(value, OBJECT_TYPE, opt_transient);
+ }
+
+ mirror_cache_[mirror.handle()] = mirror;
+ return mirror;
+}
+
+
+/**
+ * Returns the mirror for a specified mirror handle.
+ *
+ * @param {number} handle the handle to find the mirror for
+ * @returns {Mirror or undefiend} the mirror with the requested handle or
+ * undefined if no mirror with the requested handle was found
+ */
+function LookupMirror(handle) {
+ return mirror_cache_[handle];
+}
+
+
+/**
+ * Returns the mirror for the undefined value.
+ *
+ * @returns {Mirror} the mirror reflects the undefined value
+ */
+function GetUndefinedMirror() {
+ return MakeMirror(void 0);
+}
+
+
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * The Function.prototype.inherits from lang.js rewritten as a standalone
+ * function (not on Function.prototype). NOTE: If this file is to be loaded
+ * during bootstrapping this function needs to be revritten using some native
+ * functions as prototype setup using normal JavaScript does not work as
+ * expected during bootstrapping (see mirror.js in r114903).
+ *
+ * @param {function} ctor Constructor function which needs to inherit the
+ * prototype
+ * @param {function} superCtor Constructor function to inherit prototype from
+ */
+function inherits(ctor, superCtor) {
+ var tempCtor = function(){};
+ tempCtor.prototype = superCtor.prototype;
+ ctor.super_ = superCtor.prototype;
+ ctor.prototype = new tempCtor();
+ ctor.prototype.constructor = ctor;
+}
+
+
+// Type names of the different mirrors.
+var UNDEFINED_TYPE = 'undefined';
+var NULL_TYPE = 'null';
+var BOOLEAN_TYPE = 'boolean';
+var NUMBER_TYPE = 'number';
+var STRING_TYPE = 'string';
+var OBJECT_TYPE = 'object';
+var FUNCTION_TYPE = 'function';
+var REGEXP_TYPE = 'regexp';
+var ERROR_TYPE = 'error';
+var PROPERTY_TYPE = 'property';
+var INTERNAL_PROPERTY_TYPE = 'internalProperty';
+var FRAME_TYPE = 'frame';
+var SCRIPT_TYPE = 'script';
+var CONTEXT_TYPE = 'context';
+var SCOPE_TYPE = 'scope';
+
+// Maximum length when sending strings through the JSON protocol.
+var kMaxProtocolStringLength = 80;
+
+// Different kind of properties.
+var PropertyKind = {};
+PropertyKind.Named = 1;
+PropertyKind.Indexed = 2;
+
+
+// A copy of the PropertyType enum from global.h
+var PropertyType = {};
+PropertyType.Normal = 0;
+PropertyType.Field = 1;
+PropertyType.Constant = 2;
+PropertyType.Callbacks = 3;
+PropertyType.Handler = 4;
+PropertyType.Interceptor = 5;
+PropertyType.Transition = 6;
+PropertyType.Nonexistent = 7;
+
+
+// Different attributes for a property.
+var PropertyAttribute = {};
+PropertyAttribute.None = NONE;
+PropertyAttribute.ReadOnly = READ_ONLY;
+PropertyAttribute.DontEnum = DONT_ENUM;
+PropertyAttribute.DontDelete = DONT_DELETE;
+
+
+// A copy of the scope types from runtime.cc.
+var ScopeType = { Global: 0,
+ Local: 1,
+ With: 2,
+ Closure: 3,
+ Catch: 4,
+ Block: 5 };
+
+
+// Mirror hierarchy:
+// - Mirror
+// - ValueMirror
+// - UndefinedMirror
+// - NullMirror
+// - NumberMirror
+// - StringMirror
+// - ObjectMirror
+// - FunctionMirror
+// - UnresolvedFunctionMirror
+// - ArrayMirror
+// - DateMirror
+// - RegExpMirror
+// - ErrorMirror
+// - PropertyMirror
+// - InternalPropertyMirror
+// - FrameMirror
+// - ScriptMirror
+
+
+/**
+ * Base class for all mirror objects.
+ * @param {string} type The type of the mirror
+ * @constructor
+ */
+function Mirror(type) {
+ this.type_ = type;
+}
+
+
+Mirror.prototype.type = function() {
+ return this.type_;
+};
+
+
+/**
+ * Check whether the mirror reflects a value.
+ * @returns {boolean} True if the mirror reflects a value.
+ */
+Mirror.prototype.isValue = function() {
+ return this instanceof ValueMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects the undefined value.
+ * @returns {boolean} True if the mirror reflects the undefined value.
+ */
+Mirror.prototype.isUndefined = function() {
+ return this instanceof UndefinedMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects the null value.
+ * @returns {boolean} True if the mirror reflects the null value
+ */
+Mirror.prototype.isNull = function() {
+ return this instanceof NullMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a boolean value.
+ * @returns {boolean} True if the mirror reflects a boolean value
+ */
+Mirror.prototype.isBoolean = function() {
+ return this instanceof BooleanMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a number value.
+ * @returns {boolean} True if the mirror reflects a number value
+ */
+Mirror.prototype.isNumber = function() {
+ return this instanceof NumberMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a string value.
+ * @returns {boolean} True if the mirror reflects a string value
+ */
+Mirror.prototype.isString = function() {
+ return this instanceof StringMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an object.
+ * @returns {boolean} True if the mirror reflects an object
+ */
+Mirror.prototype.isObject = function() {
+ return this instanceof ObjectMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a function.
+ * @returns {boolean} True if the mirror reflects a function
+ */
+Mirror.prototype.isFunction = function() {
+ return this instanceof FunctionMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an unresolved function.
+ * @returns {boolean} True if the mirror reflects an unresolved function
+ */
+Mirror.prototype.isUnresolvedFunction = function() {
+ return this instanceof UnresolvedFunctionMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an array.
+ * @returns {boolean} True if the mirror reflects an array
+ */
+Mirror.prototype.isArray = function() {
+ return this instanceof ArrayMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a date.
+ * @returns {boolean} True if the mirror reflects a date
+ */
+Mirror.prototype.isDate = function() {
+ return this instanceof DateMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a regular expression.
+ * @returns {boolean} True if the mirror reflects a regular expression
+ */
+Mirror.prototype.isRegExp = function() {
+ return this instanceof RegExpMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an error.
+ * @returns {boolean} True if the mirror reflects an error
+ */
+Mirror.prototype.isError = function() {
+ return this instanceof ErrorMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a property.
+ * @returns {boolean} True if the mirror reflects a property
+ */
+Mirror.prototype.isProperty = function() {
+ return this instanceof PropertyMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects an internal property.
+ * @returns {boolean} True if the mirror reflects an internal property
+ */
+Mirror.prototype.isInternalProperty = function() {
+ return this instanceof InternalPropertyMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a stack frame.
+ * @returns {boolean} True if the mirror reflects a stack frame
+ */
+Mirror.prototype.isFrame = function() {
+ return this instanceof FrameMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a script.
+ * @returns {boolean} True if the mirror reflects a script
+ */
+Mirror.prototype.isScript = function() {
+ return this instanceof ScriptMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a context.
+ * @returns {boolean} True if the mirror reflects a context
+ */
+Mirror.prototype.isContext = function() {
+ return this instanceof ContextMirror;
+};
+
+
+/**
+ * Check whether the mirror reflects a scope.
+ * @returns {boolean} True if the mirror reflects a scope
+ */
+Mirror.prototype.isScope = function() {
+ return this instanceof ScopeMirror;
+};
+
+
+/**
+ * Allocate a handle id for this object.
+ */
+Mirror.prototype.allocateHandle_ = function() {
+ this.handle_ = next_handle_++;
+};
+
+
+/**
+ * Allocate a transient handle id for this object. Transient handles are
+ * negative.
+ */
+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 + ">";
+};
+
+
+/**
+ * Base class for all value mirror objects.
+ * @param {string} type The type of the mirror
+ * @param {value} value The value reflected by this mirror
+ * @param {boolean} transient indicate whether this object is transient with a
+ * transient handle
+ * @constructor
+ * @extends Mirror
+ */
+function ValueMirror(type, value, transient) {
+ %_CallFunction(this, type, Mirror);
+ this.value_ = value;
+ if (!transient) {
+ this.allocateHandle_();
+ } else {
+ this.allocateTransientHandle_();
+ }
+}
+inherits(ValueMirror, Mirror);
+
+
+Mirror.prototype.handle = function() {
+ return this.handle_;
+};
+
+
+/**
+ * Check whether this is a primitive value.
+ * @return {boolean} True if the mirror reflects a primitive value
+ */
+ValueMirror.prototype.isPrimitive = function() {
+ var type = this.type();
+ return type === 'undefined' ||
+ type === 'null' ||
+ type === 'boolean' ||
+ type === 'number' ||
+ type === 'string';
+};
+
+
+/**
+ * Get the actual value reflected by this mirror.
+ * @return {value} The value reflected by this mirror
+ */
+ValueMirror.prototype.value = function() {
+ return this.value_;
+};
+
+
+/**
+ * Mirror object for Undefined.
+ * @constructor
+ * @extends ValueMirror
+ */
+function UndefinedMirror() {
+ %_CallFunction(this, UNDEFINED_TYPE, void 0, ValueMirror);
+}
+inherits(UndefinedMirror, ValueMirror);
+
+
+UndefinedMirror.prototype.toText = function() {
+ return 'undefined';
+};
+
+
+/**
+ * Mirror object for null.
+ * @constructor
+ * @extends ValueMirror
+ */
+function NullMirror() {
+ %_CallFunction(this, NULL_TYPE, null, ValueMirror);
+}
+inherits(NullMirror, ValueMirror);
+
+
+NullMirror.prototype.toText = function() {
+ return 'null';
+};
+
+
+/**
+ * Mirror object for boolean values.
+ * @param {boolean} value The boolean value reflected by this mirror
+ * @constructor
+ * @extends ValueMirror
+ */
+function BooleanMirror(value) {
+ %_CallFunction(this, BOOLEAN_TYPE, value, ValueMirror);
+}
+inherits(BooleanMirror, ValueMirror);
+
+
+BooleanMirror.prototype.toText = function() {
+ return this.value_ ? 'true' : 'false';
+};
+
+
+/**
+ * Mirror object for number values.
+ * @param {number} value The number value reflected by this mirror
+ * @constructor
+ * @extends ValueMirror
+ */
+function NumberMirror(value) {
+ %_CallFunction(this, NUMBER_TYPE, value, ValueMirror);
+}
+inherits(NumberMirror, ValueMirror);
+
+
+NumberMirror.prototype.toText = function() {
+ return %NumberToString(this.value_);
+};
+
+
+/**
+ * Mirror object for string values.
+ * @param {string} value The string value reflected by this mirror
+ * @constructor
+ * @extends ValueMirror
+ */
+function StringMirror(value) {
+ %_CallFunction(this, STRING_TYPE, value, ValueMirror);
+}
+inherits(StringMirror, ValueMirror);
+
+
+StringMirror.prototype.length = function() {
+ return this.value_.length;
+};
+
+StringMirror.prototype.getTruncatedValue = function(maxLength) {
+ if (maxLength != -1 && this.length() > maxLength) {
+ return this.value_.substring(0, maxLength) +
+ '... (length: ' + this.length() + ')';
+ }
+ return this.value_;
+};
+
+StringMirror.prototype.toText = function() {
+ return this.getTruncatedValue(kMaxProtocolStringLength);
+};
+
+
+/**
+ * Mirror object for objects.
+ * @param {object} value The object reflected by this mirror
+ * @param {boolean} transient indicate whether this object is transient with a
+ * transient handle
+ * @constructor
+ * @extends ValueMirror
+ */
+function ObjectMirror(value, type, transient) {
+ %_CallFunction(this, type || OBJECT_TYPE, value, transient, ValueMirror);
+}
+inherits(ObjectMirror, ValueMirror);
+
+
+ObjectMirror.prototype.className = function() {
+ return %_ClassOf(this.value_);
+};
+
+
+ObjectMirror.prototype.constructorFunction = function() {
+ return MakeMirror(%DebugGetProperty(this.value_, 'constructor'));
+};
+
+
+ObjectMirror.prototype.prototypeObject = function() {
+ return MakeMirror(%DebugGetProperty(this.value_, 'prototype'));
+};
+
+
+ObjectMirror.prototype.protoObject = function() {
+ return MakeMirror(%DebugGetPrototype(this.value_));
+};
+
+
+ObjectMirror.prototype.hasNamedInterceptor = function() {
+ // Get information on interceptors for this object.
+ var x = %GetInterceptorInfo(this.value_);
+ return (x & 2) != 0;
+};
+
+
+ObjectMirror.prototype.hasIndexedInterceptor = function() {
+ // Get information on interceptors for this object.
+ var x = %GetInterceptorInfo(this.value_);
+ return (x & 1) != 0;
+};
+
+
+/**
+ * Return the property names for this object.
+ * @param {number} kind Indicate whether named, indexed or both kinds of
+ * properties are requested
+ * @param {number} limit Limit the number of names returend to the specified
+ value
+ * @return {Array} Property names for this object
+ */
+ObjectMirror.prototype.propertyNames = function(kind, limit) {
+ // Find kind and limit and allocate array for the result
+ kind = kind || PropertyKind.Named | PropertyKind.Indexed;
+
+ var propertyNames;
+ var elementNames;
+ var total = 0;
+
+ // Find all the named properties.
+ if (kind & PropertyKind.Named) {
+ // Get the local property names.
+ propertyNames = %GetLocalPropertyNames(this.value_, true);
+ total += propertyNames.length;
+
+ // Get names for named interceptor properties if any.
+ if (this.hasNamedInterceptor() && (kind & PropertyKind.Named)) {
+ var namedInterceptorNames =
+ %GetNamedInterceptorPropertyNames(this.value_);
+ if (namedInterceptorNames) {
+ propertyNames = propertyNames.concat(namedInterceptorNames);
+ total += namedInterceptorNames.length;
+ }
+ }
+ }
+
+ // Find all the indexed properties.
+ if (kind & PropertyKind.Indexed) {
+ // Get the local element names.
+ elementNames = %GetLocalElementNames(this.value_);
+ total += elementNames.length;
+
+ // Get names for indexed interceptor properties.
+ if (this.hasIndexedInterceptor() && (kind & PropertyKind.Indexed)) {
+ var indexedInterceptorNames =
+ %GetIndexedInterceptorElementNames(this.value_);
+ if (indexedInterceptorNames) {
+ elementNames = elementNames.concat(indexedInterceptorNames);
+ total += indexedInterceptorNames.length;
+ }
+ }
+ }
+ limit = Math.min(limit || total, total);
+
+ var names = new Array(limit);
+ var index = 0;
+
+ // Copy names for named properties.
+ if (kind & PropertyKind.Named) {
+ for (var i = 0; index < limit && i < propertyNames.length; i++) {
+ names[index++] = propertyNames[i];
+ }
+ }
+
+ // Copy names for indexed properties.
+ if (kind & PropertyKind.Indexed) {
+ for (var i = 0; index < limit && i < elementNames.length; i++) {
+ names[index++] = elementNames[i];
+ }
+ }
+
+ return names;
+};
+
+
+/**
+ * Return the properties for this object as an array of PropertyMirror objects.
+ * @param {number} kind Indicate whether named, indexed or both kinds of
+ * properties are requested
+ * @param {number} limit Limit the number of properties returned to the
+ specified value
+ * @return {Array} Property mirrors for this object
+ */
+ObjectMirror.prototype.properties = function(kind, limit) {
+ var names = this.propertyNames(kind, limit);
+ var properties = new Array(names.length);
+ for (var i = 0; i < names.length; i++) {
+ properties[i] = this.property(names[i]);
+ }
+
+ return properties;
+};
+
+
+/**
+ * Return the internal properties for this object as an array of
+ * InternalPropertyMirror objects.
+ * @return {Array} Property mirrors for this object
+ */
+ObjectMirror.prototype.internalProperties = function() {
+ return ObjectMirror.GetInternalProperties(this.value_);
+}
+
+
+ObjectMirror.prototype.property = function(name) {
+ var details = %DebugGetPropertyDetails(this.value_, %ToString(name));
+ if (details) {
+ return new PropertyMirror(this, name, details);
+ }
+
+ // Nothing found.
+ return GetUndefinedMirror();
+};
+
+
+
+/**
+ * Try to find a property from its value.
+ * @param {Mirror} value The property value to look for
+ * @return {PropertyMirror} The property with the specified value. If no
+ * property was found with the specified value UndefinedMirror is returned
+ */
+ObjectMirror.prototype.lookupProperty = function(value) {
+ var properties = this.properties();
+
+ // Look for property value in properties.
+ for (var i = 0; i < properties.length; i++) {
+
+ // Skip properties which are defined through assessors.
+ var property = properties[i];
+ if (property.propertyType() != PropertyType.Callbacks) {
+ if (%_ObjectEquals(property.value_, value.value_)) {
+ return property;
+ }
+ }
+ }
+
+ // Nothing found.
+ return GetUndefinedMirror();
+};
+
+
+/**
+ * Returns objects which has direct references to this object
+ * @param {number} opt_max_objects Optional parameter specifying the maximum
+ * number of referencing objects to return.
+ * @return {Array} The objects which has direct references to this object.
+ */
+ObjectMirror.prototype.referencedBy = function(opt_max_objects) {
+ // Find all objects with direct references to this object.
+ var result = %DebugReferencedBy(this.value_,
+ Mirror.prototype, opt_max_objects || 0);
+
+ // Make mirrors for all the references found.
+ for (var i = 0; i < result.length; i++) {
+ result[i] = MakeMirror(result[i]);
+ }
+
+ return result;
+};
+
+
+ObjectMirror.prototype.toText = function() {
+ var name;
+ var ctor = this.constructorFunction();
+ if (!ctor.isFunction()) {
+ name = this.className();
+ } else {
+ name = ctor.name();
+ if (!name) {
+ name = this.className();
+ }
+ }
+ return '#<' + name + '>';
+};
+
+
+/**
+ * Return the internal properties of the value, such as [[PrimitiveValue]] of
+ * scalar wrapper objects and properties of the bound function.
+ * This method is done static to be accessible from Debug API with the bare
+ * values without mirrors.
+ * @return {Array} array (possibly empty) of InternalProperty instances
+ */
+ObjectMirror.GetInternalProperties = function(value) {
+ if (IS_STRING_WRAPPER(value) || IS_NUMBER_WRAPPER(value) ||
+ IS_BOOLEAN_WRAPPER(value)) {
+ var primitiveValue = %_ValueOf(value);
+ return [new InternalPropertyMirror("[[PrimitiveValue]]", primitiveValue)];
+ } else if (IS_FUNCTION(value)) {
+ var bindings = %BoundFunctionGetBindings(value);
+ var result = [];
+ if (bindings && IS_ARRAY(bindings)) {
+ result.push(new InternalPropertyMirror("[[TargetFunction]]",
+ bindings[0]));
+ result.push(new InternalPropertyMirror("[[BoundThis]]", bindings[1]));
+ var boundArgs = [];
+ for (var i = 2; i < bindings.length; i++) {
+ boundArgs.push(bindings[i]);
+ }
+ result.push(new InternalPropertyMirror("[[BoundArgs]]", boundArgs));
+ }
+ return result;
+ }
+ return [];
+}
+
+
+/**
+ * Mirror object for functions.
+ * @param {function} value The function object reflected by this mirror.
+ * @constructor
+ * @extends ObjectMirror
+ */
+function FunctionMirror(value) {
+ %_CallFunction(this, value, FUNCTION_TYPE, ObjectMirror);
+ this.resolved_ = true;
+}
+inherits(FunctionMirror, ObjectMirror);
+
+
+/**
+ * Returns whether the function is resolved.
+ * @return {boolean} True if the function is resolved. Unresolved functions can
+ * only originate as functions from stack frames
+ */
+FunctionMirror.prototype.resolved = function() {
+ return this.resolved_;
+};
+
+
+/**
+ * Returns the name of the function.
+ * @return {string} Name of the function
+ */
+FunctionMirror.prototype.name = function() {
+ return %FunctionGetName(this.value_);
+};
+
+
+/**
+ * Returns the inferred name of the function.
+ * @return {string} Name of the function
+ */
+FunctionMirror.prototype.inferredName = function() {
+ return %FunctionGetInferredName(this.value_);
+};
+
+
+/**
+ * Returns the source code for the function.
+ * @return {string or undefined} The source code for the function. If the
+ * function is not resolved undefined will be returned.
+ */
+FunctionMirror.prototype.source = function() {
+ // Return source if function is resolved. Otherwise just fall through to
+ // return undefined.
+ if (this.resolved()) {
+ return builtins.FunctionSourceString(this.value_);
+ }
+};
+
+
+/**
+ * Returns the script object for the function.
+ * @return {ScriptMirror or undefined} Script object for the function or
+ * undefined if the function has no script
+ */
+FunctionMirror.prototype.script = function() {
+ // Return script if function is resolved. Otherwise just fall through
+ // to return undefined.
+ if (this.resolved()) {
+ var script = %FunctionGetScript(this.value_);
+ if (script) {
+ return MakeMirror(script);
+ }
+ }
+};
+
+
+/**
+ * Returns the script source position for the function. Only makes sense
+ * for functions which has a script defined.
+ * @return {Number or undefined} in-script position for the function
+ */
+FunctionMirror.prototype.sourcePosition_ = function() {
+ // Return script if function is resolved. Otherwise just fall through
+ // to return undefined.
+ if (this.resolved()) {
+ return %FunctionGetScriptSourcePosition(this.value_);
+ }
+};
+
+
+/**
+ * Returns the script source location object for the function. Only makes sense
+ * for functions which has a script defined.
+ * @return {Location or undefined} in-script location for the function begin
+ */
+FunctionMirror.prototype.sourceLocation = function() {
+ if (this.resolved() && this.script()) {
+ return this.script().locationFromPosition(this.sourcePosition_(),
+ true);
+ }
+};
+
+
+/**
+ * Returns objects constructed by this function.
+ * @param {number} opt_max_instances Optional parameter specifying the maximum
+ * number of instances to return.
+ * @return {Array or undefined} The objects constructed by this function.
+ */
+FunctionMirror.prototype.constructedBy = function(opt_max_instances) {
+ if (this.resolved()) {
+ // Find all objects constructed from this function.
+ var result = %DebugConstructedBy(this.value_, opt_max_instances || 0);
+
+ // Make mirrors for all the instances found.
+ for (var i = 0; i < result.length; i++) {
+ result[i] = MakeMirror(result[i]);
+ }
+
+ return result;
+ } else {
+ return [];
+ }
+};
+
+
+FunctionMirror.prototype.scopeCount = function() {
+ if (this.resolved()) {
+ return %GetFunctionScopeCount(this.value());
+ } else {
+ return 0;
+ }
+};
+
+
+FunctionMirror.prototype.scope = function(index) {
+ if (this.resolved()) {
+ return new ScopeMirror(void 0, this, index);
+ }
+};
+
+
+FunctionMirror.prototype.toText = function() {
+ return this.source();
+};
+
+
+/**
+ * Mirror object for unresolved functions.
+ * @param {string} value The name for the unresolved function reflected by this
+ * mirror.
+ * @constructor
+ * @extends ObjectMirror
+ */
+function UnresolvedFunctionMirror(value) {
+ // Construct this using the ValueMirror as an unresolved function is not a
+ // real object but just a string.
+ %_CallFunction(this, FUNCTION_TYPE, value, ValueMirror);
+ this.propertyCount_ = 0;
+ this.elementCount_ = 0;
+ this.resolved_ = false;
+}
+inherits(UnresolvedFunctionMirror, FunctionMirror);
+
+
+UnresolvedFunctionMirror.prototype.className = function() {
+ return 'Function';
+};
+
+
+UnresolvedFunctionMirror.prototype.constructorFunction = function() {
+ return GetUndefinedMirror();
+};
+
+
+UnresolvedFunctionMirror.prototype.prototypeObject = function() {
+ return GetUndefinedMirror();
+};
+
+
+UnresolvedFunctionMirror.prototype.protoObject = function() {
+ return GetUndefinedMirror();
+};
+
+
+UnresolvedFunctionMirror.prototype.name = function() {
+ return this.value_;
+};
+
+
+UnresolvedFunctionMirror.prototype.inferredName = function() {
+ return undefined;
+};
+
+
+UnresolvedFunctionMirror.prototype.propertyNames = function(kind, limit) {
+ return [];
+};
+
+
+/**
+ * Mirror object for arrays.
+ * @param {Array} value The Array object reflected by this mirror
+ * @constructor
+ * @extends ObjectMirror
+ */
+function ArrayMirror(value) {
+ %_CallFunction(this, value, ObjectMirror);
+}
+inherits(ArrayMirror, ObjectMirror);
+
+
+ArrayMirror.prototype.length = function() {
+ return this.value_.length;
+};
+
+
+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();
+ var values = new Array(to_index - from_index + 1);
+ for (var i = from_index; i <= to_index; i++) {
+ var details = %DebugGetPropertyDetails(this.value_, %ToString(i));
+ var value;
+ if (details) {
+ value = new PropertyMirror(this, i, details);
+ } else {
+ value = GetUndefinedMirror();
+ }
+ values[i - from_index] = value;
+ }
+ return values;
+};
+
+
+/**
+ * Mirror object for dates.
+ * @param {Date} value The Date object reflected by this mirror
+ * @constructor
+ * @extends ObjectMirror
+ */
+function DateMirror(value) {
+ %_CallFunction(this, value, ObjectMirror);
+}
+inherits(DateMirror, ObjectMirror);
+
+
+DateMirror.prototype.toText = function() {
+ var s = JSON.stringify(this.value_);
+ return s.substring(1, s.length - 1); // cut quotes
+};
+
+
+/**
+ * Mirror object for regular expressions.
+ * @param {RegExp} value The RegExp object reflected by this mirror
+ * @constructor
+ * @extends ObjectMirror
+ */
+function RegExpMirror(value) {
+ %_CallFunction(this, value, REGEXP_TYPE, ObjectMirror);
+}
+inherits(RegExpMirror, ObjectMirror);
+
+
+/**
+ * Returns the source to the regular expression.
+ * @return {string or undefined} The source to the regular expression
+ */
+RegExpMirror.prototype.source = function() {
+ return this.value_.source;
+};
+
+
+/**
+ * Returns whether this regular expression has the global (g) flag set.
+ * @return {boolean} Value of the global flag
+ */
+RegExpMirror.prototype.global = function() {
+ return this.value_.global;
+};
+
+
+/**
+ * Returns whether this regular expression has the ignore case (i) flag set.
+ * @return {boolean} Value of the ignore case flag
+ */
+RegExpMirror.prototype.ignoreCase = function() {
+ return this.value_.ignoreCase;
+};
+
+
+/**
+ * Returns whether this regular expression has the multiline (m) flag set.
+ * @return {boolean} Value of the multiline flag
+ */
+RegExpMirror.prototype.multiline = function() {
+ return this.value_.multiline;
+};
+
+
+RegExpMirror.prototype.toText = function() {
+ // Simpel to text which is used when on specialization in subclass.
+ return "/" + this.source() + "/";
+};
+
+
+/**
+ * Mirror object for error objects.
+ * @param {Error} value The error object reflected by this mirror
+ * @constructor
+ * @extends ObjectMirror
+ */
+function ErrorMirror(value) {
+ %_CallFunction(this, value, ERROR_TYPE, ObjectMirror);
+}
+inherits(ErrorMirror, ObjectMirror);
+
+
+/**
+ * Returns the message for this eror object.
+ * @return {string or undefined} The message for this eror object
+ */
+ErrorMirror.prototype.message = function() {
+ return this.value_.message;
+};
+
+
+ErrorMirror.prototype.toText = function() {
+ // Use the same text representation as in messages.js.
+ var text;
+ try {
+ str = %_CallFunction(this.value_, builtins.ErrorToString);
+ } catch (e) {
+ str = '#<Error>';
+ }
+ return str;
+};
+
+
+/**
+ * Base mirror object for properties.
+ * @param {ObjectMirror} mirror The mirror object having this property
+ * @param {string} name The name of the property
+ * @param {Array} details Details about the property
+ * @constructor
+ * @extends Mirror
+ */
+function PropertyMirror(mirror, name, details) {
+ %_CallFunction(this, PROPERTY_TYPE, Mirror);
+ this.mirror_ = mirror;
+ this.name_ = name;
+ this.value_ = details[0];
+ this.details_ = details[1];
+ if (details.length > 2) {
+ this.exception_ = details[2];
+ this.getter_ = details[3];
+ this.setter_ = details[4];
+ }
+}
+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() {
+ for (var i = 0; i < this.name_.length; i++) {
+ if (this.name_[i] < '0' || '9' < this.name_[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+PropertyMirror.prototype.value = function() {
+ return MakeMirror(this.value_, false);
+};
+
+
+/**
+ * Returns whether this property value is an exception.
+ * @return {booolean} True if this property value is an exception
+ */
+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_);
+};
+
+
+/**
+ * Returns whether this property has a getter defined through __defineGetter__.
+ * @return {booolean} True if this property has a getter
+ */
+PropertyMirror.prototype.hasGetter = function() {
+ return this.getter_ ? true : false;
+};
+
+
+/**
+ * Returns whether this property has a setter defined through __defineSetter__.
+ * @return {booolean} True if this property has a setter
+ */
+PropertyMirror.prototype.hasSetter = function() {
+ return this.setter_ ? true : false;
+};
+
+
+/**
+ * Returns the getter for this property defined through __defineGetter__.
+ * @return {Mirror} FunctionMirror reflecting the getter function or
+ * UndefinedMirror if there is no getter for this property
+ */
+PropertyMirror.prototype.getter = function() {
+ if (this.hasGetter()) {
+ return MakeMirror(this.getter_);
+ } else {
+ return GetUndefinedMirror();
+ }
+};
+
+
+/**
+ * Returns the setter for this property defined through __defineSetter__.
+ * @return {Mirror} FunctionMirror reflecting the setter function or
+ * UndefinedMirror if there is no setter for this property
+ */
+PropertyMirror.prototype.setter = function() {
+ if (this.hasSetter()) {
+ return MakeMirror(this.setter_);
+ } else {
+ return GetUndefinedMirror();
+ }
+};
+
+
+/**
+ * Returns whether this property is natively implemented by the host or a set
+ * through JavaScript code.
+ * @return {boolean} True if the property is
+ * UndefinedMirror if there is no setter for this property
+ */
+PropertyMirror.prototype.isNative = function() {
+ return (this.propertyType() == PropertyType.Interceptor) ||
+ ((this.propertyType() == PropertyType.Callbacks) &&
+ !this.hasGetter() && !this.hasSetter());
+};
+
+
+/**
+ * Mirror object for internal properties. Internal property reflects properties
+ * not accessible from user code such as [[BoundThis]] in bound function.
+ * Their names are merely symbolic.
+ * @param {string} name The name of the property
+ * @param {value} property value
+ * @constructor
+ * @extends Mirror
+ */
+function InternalPropertyMirror(name, value) {
+ %_CallFunction(this, INTERNAL_PROPERTY_TYPE, Mirror);
+ this.name_ = name;
+ this.value_ = value;
+}
+inherits(InternalPropertyMirror, Mirror);
+
+
+InternalPropertyMirror.prototype.name = function() {
+ return this.name_;
+};
+
+
+InternalPropertyMirror.prototype.value = function() {
+ return MakeMirror(this.value_, false);
+};
+
+
+var kFrameDetailsFrameIdIndex = 0;
+var kFrameDetailsReceiverIndex = 1;
+var kFrameDetailsFunctionIndex = 2;
+var kFrameDetailsArgumentCountIndex = 3;
+var kFrameDetailsLocalCountIndex = 4;
+var kFrameDetailsSourcePositionIndex = 5;
+var kFrameDetailsConstructCallIndex = 6;
+var kFrameDetailsAtReturnIndex = 7;
+var kFrameDetailsFlagsIndex = 8;
+var kFrameDetailsFirstDynamicIndex = 9;
+
+var kFrameDetailsNameIndex = 0;
+var kFrameDetailsValueIndex = 1;
+var kFrameDetailsNameValueSize = 2;
+
+var kFrameDetailsFlagDebuggerFrameMask = 1 << 0;
+var kFrameDetailsFlagOptimizedFrameMask = 1 << 1;
+var kFrameDetailsFlagInlinedFrameIndexMask = 7 << 2;
+
+/**
+ * Wrapper for the frame details information retreived from the VM. The frame
+ * details from the VM is an array with the following content. See runtime.cc
+ * Runtime_GetFrameDetails.
+ * 0: Id
+ * 1: Receiver
+ * 2: Function
+ * 3: Argument count
+ * 4: Local count
+ * 5: Source position
+ * 6: Construct call
+ * 7: Is at return
+ * 8: Flags (debugger frame, optimized frame, inlined frame index)
+ * Arguments name, value
+ * Locals name, value
+ * Return value if any
+ * @param {number} break_id Current break id
+ * @param {number} index Frame number
+ * @constructor
+ */
+function FrameDetails(break_id, index) {
+ this.break_id_ = break_id;
+ this.details_ = %GetFrameDetails(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;
+};
+
+
+FrameDetails.prototype.argumentCount = function() {
+ %CheckExecutionState(this.break_id_);
+ return this.details_[kFrameDetailsArgumentCountIndex];
+};
+
+
+FrameDetails.prototype.argumentName = function(index) {
+ %CheckExecutionState(this.break_id_);
+ if (index >= 0 && index < this.argumentCount()) {
+ return this.details_[kFrameDetailsFirstDynamicIndex +
+ index * kFrameDetailsNameValueSize +
+ kFrameDetailsNameIndex];
+ }
+};
+
+
+FrameDetails.prototype.argumentValue = function(index) {
+ %CheckExecutionState(this.break_id_);
+ if (index >= 0 && index < this.argumentCount()) {
+ return this.details_[kFrameDetailsFirstDynamicIndex +
+ index * kFrameDetailsNameValueSize +
+ 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;
+ return this.details_[locals_offset +
+ index * kFrameDetailsNameValueSize +
+ kFrameDetailsNameIndex];
+ }
+};
+
+
+FrameDetails.prototype.localValue = function(index) {
+ %CheckExecutionState(this.break_id_);
+ if (index >= 0 && index < this.localCount()) {
+ var locals_offset = kFrameDetailsFirstDynamicIndex +
+ this.argumentCount() * kFrameDetailsNameValueSize;
+ return this.details_[locals_offset +
+ index * kFrameDetailsNameValueSize +
+ kFrameDetailsValueIndex];
+ }
+};
+
+
+FrameDetails.prototype.returnValue = function() {
+ %CheckExecutionState(this.break_id_);
+ var return_value_offset =
+ kFrameDetailsFirstDynamicIndex +
+ (this.argumentCount() + this.localCount()) * kFrameDetailsNameValueSize;
+ if (this.details_[kFrameDetailsAtReturnIndex]) {
+ return this.details_[return_value_offset];
+ }
+};
+
+
+FrameDetails.prototype.scopeCount = function() {
+ return %GetScopeCount(this.break_id_, this.frameId());
+};
+
+
+FrameDetails.prototype.stepInPositionsImpl = function() {
+ return %GetStepInPositions(this.break_id_, this.frameId());
+};
+
+
+/**
+ * Mirror object for stack frames.
+ * @param {number} break_id The break id in the VM for which this frame is
+ valid
+ * @param {number} index The frame index (top frame is index 0)
+ * @constructor
+ * @extends Mirror
+ */
+function FrameMirror(break_id, index) {
+ %_CallFunction(this, FRAME_TYPE, Mirror);
+ this.break_id_ = break_id;
+ this.index_ = index;
+ this.details_ = new FrameDetails(break_id, index);
+}
+inherits(FrameMirror, Mirror);
+
+
+FrameMirror.prototype.index = function() {
+ return this.index_;
+};
+
+
+FrameMirror.prototype.func = function() {
+ // Get the function for this frame from the VM.
+ var f = this.details_.func();
+
+ // Create a function mirror. NOTE: MakeMirror cannot be used here as the
+ // value returned from the VM might be a string if the function for the
+ // frame is unresolved.
+ if (IS_FUNCTION(f)) {
+ return MakeMirror(f);
+ } else {
+ return new UnresolvedFunctionMirror(f);
+ }
+};
+
+
+FrameMirror.prototype.receiver = function() {
+ return MakeMirror(this.details_.receiver());
+};
+
+
+FrameMirror.prototype.isConstructCall = function() {
+ return this.details_.isConstructCall();
+};
+
+
+FrameMirror.prototype.isAtReturn = function() {
+ return this.details_.isAtReturn();
+};
+
+
+FrameMirror.prototype.isDebuggerFrame = function() {
+ return this.details_.isDebuggerFrame();
+};
+
+
+FrameMirror.prototype.isOptimizedFrame = function() {
+ return this.details_.isOptimizedFrame();
+};
+
+
+FrameMirror.prototype.isInlinedFrame = function() {
+ return this.details_.isInlinedFrame();
+};
+
+
+FrameMirror.prototype.inlinedFrameIndex = function() {
+ return this.details_.inlinedFrameIndex();
+};
+
+
+FrameMirror.prototype.argumentCount = function() {
+ return this.details_.argumentCount();
+};
+
+
+FrameMirror.prototype.argumentName = function(index) {
+ return this.details_.argumentName(index);
+};
+
+
+FrameMirror.prototype.argumentValue = function(index) {
+ return MakeMirror(this.details_.argumentValue(index));
+};
+
+
+FrameMirror.prototype.localCount = function() {
+ return this.details_.localCount();
+};
+
+
+FrameMirror.prototype.localName = function(index) {
+ return this.details_.localName(index);
+};
+
+
+FrameMirror.prototype.localValue = function(index) {
+ return MakeMirror(this.details_.localValue(index));
+};
+
+
+FrameMirror.prototype.returnValue = function() {
+ return MakeMirror(this.details_.returnValue());
+};
+
+
+FrameMirror.prototype.sourcePosition = function() {
+ return this.details_.sourcePosition();
+};
+
+
+FrameMirror.prototype.sourceLocation = function() {
+ if (this.func().resolved() && this.func().script()) {
+ return this.func().script().locationFromPosition(this.sourcePosition(),
+ true);
+ }
+};
+
+
+FrameMirror.prototype.sourceLine = function() {
+ if (this.func().resolved()) {
+ var location = this.sourceLocation();
+ if (location) {
+ return location.line;
+ }
+ }
+};
+
+
+FrameMirror.prototype.sourceColumn = function() {
+ if (this.func().resolved()) {
+ var location = this.sourceLocation();
+ if (location) {
+ return location.column;
+ }
+ }
+};
+
+
+FrameMirror.prototype.sourceLineText = function() {
+ if (this.func().resolved()) {
+ var location = this.sourceLocation();
+ if (location) {
+ return location.sourceText();
+ }
+ }
+};
+
+
+FrameMirror.prototype.scopeCount = function() {
+ return this.details_.scopeCount();
+};
+
+
+FrameMirror.prototype.scope = function(index) {
+ return new ScopeMirror(this, void 0, index);
+};
+
+
+FrameMirror.prototype.stepInPositions = function() {
+ var script = this.func().script();
+ var funcOffset = this.func().sourcePosition_();
+
+ var stepInRaw = this.details_.stepInPositionsImpl();
+ var result = [];
+ if (stepInRaw) {
+ for (var i = 0; i < stepInRaw.length; i++) {
+ var posStruct = {};
+ var offset = script.locationFromPosition(funcOffset + stepInRaw[i],
+ true);
+ serializeLocationFields(offset, posStruct);
+ var item = {
+ position: posStruct
+ };
+ result.push(item);
+ }
+ }
+
+ return result;
+};
+
+
+FrameMirror.prototype.evaluate = function(source, disable_break,
+ opt_context_object) {
+ return MakeMirror(%DebugEvaluate(this.break_id_,
+ this.details_.frameId(),
+ this.details_.inlinedFrameIndex(),
+ source,
+ Boolean(disable_break),
+ opt_context_object));
+};
+
+
+FrameMirror.prototype.invocationText = function() {
+ // Format frame invoaction (receiver, function and arguments).
+ var result = '';
+ var func = this.func();
+ var receiver = this.receiver();
+ if (this.isConstructCall()) {
+ // For constructor frames display new followed by the function name.
+ result += 'new ';
+ result += func.name() ? func.name() : '[anonymous]';
+ } else if (this.isDebuggerFrame()) {
+ result += '[debugger]';
+ } else {
+ // If the receiver has a className which is 'global' don't display it.
+ var display_receiver =
+ !receiver.className || (receiver.className() != 'global');
+ if (display_receiver) {
+ result += receiver.toText();
+ }
+ // Try to find the function as a property in the receiver. Include the
+ // prototype chain in the lookup.
+ var property = GetUndefinedMirror();
+ if (receiver.isObject()) {
+ for (var r = receiver;
+ !r.isNull() && property.isUndefined();
+ r = r.protoObject()) {
+ property = r.lookupProperty(func);
+ }
+ }
+ if (!property.isUndefined()) {
+ // The function invoked was found on the receiver. Use the property name
+ // for the backtrace.
+ if (!property.isIndexed()) {
+ if (display_receiver) {
+ result += '.';
+ }
+ result += property.name();
+ } else {
+ result += '[';
+ result += property.name();
+ result += ']';
+ }
+ // Also known as - if the name in the function doesn't match the name
+ // under which it was looked up.
+ if (func.name() && func.name() != property.name()) {
+ result += '(aka ' + func.name() + ')';
+ }
+ } else {
+ // The function invoked was not found on the receiver. Use the function
+ // name if available for the backtrace.
+ if (display_receiver) {
+ result += '.';
+ }
+ result += func.name() ? func.name() : '[anonymous]';
+ }
+ }
+
+ // Render arguments for normal frames.
+ if (!this.isDebuggerFrame()) {
+ result += '(';
+ for (var i = 0; i < this.argumentCount(); i++) {
+ if (i != 0) result += ', ';
+ if (this.argumentName(i)) {
+ result += this.argumentName(i);
+ result += '=';
+ }
+ result += this.argumentValue(i).toText();
+ }
+ result += ')';
+ }
+
+ if (this.isAtReturn()) {
+ result += ' returning ';
+ result += this.returnValue().toText();
+ }
+
+ return result;
+};
+
+
+FrameMirror.prototype.sourceAndPositionText = function() {
+ // Format source and position.
+ var result = '';
+ var func = this.func();
+ if (func.resolved()) {
+ if (func.script()) {
+ if (func.script().name()) {
+ result += func.script().name();
+ } else {
+ result += '[unnamed]';
+ }
+ if (!this.isDebuggerFrame()) {
+ var location = this.sourceLocation();
+ result += ' line ';
+ result += !IS_UNDEFINED(location) ? (location.line + 1) : '?';
+ result += ' column ';
+ result += !IS_UNDEFINED(location) ? (location.column + 1) : '?';
+ if (!IS_UNDEFINED(this.sourcePosition())) {
+ result += ' (position ' + (this.sourcePosition() + 1) + ')';
+ }
+ }
+ } else {
+ result += '[no source]';
+ }
+ } else {
+ result += '[unresolved]';
+ }
+
+ return result;
+};
+
+
+FrameMirror.prototype.localsText = function() {
+ // Format local variables.
+ var result = '';
+ var locals_count = this.localCount();
+ if (locals_count > 0) {
+ for (var i = 0; i < locals_count; ++i) {
+ result += ' var ';
+ result += this.localName(i);
+ result += ' = ';
+ result += this.localValue(i).toText();
+ if (i < locals_count - 1) result += '\n';
+ }
+ }
+
+ return result;
+};
+
+
+FrameMirror.prototype.restart = function() {
+ var result = %LiveEditRestartFrame(this.break_id_, this.index_);
+ if (IS_UNDEFINED(result)) {
+ result = "Failed to find requested frame";
+ }
+ return result;
+};
+
+
+FrameMirror.prototype.toText = function(opt_locals) {
+ var result = '';
+ result += '#' + (this.index() <= 9 ? '0' : '') + this.index();
+ result += ' ';
+ result += this.invocationText();
+ result += ' ';
+ result += this.sourceAndPositionText();
+ if (opt_locals) {
+ result += '\n';
+ result += this.localsText();
+ }
+ return result;
+};
+
+
+var kScopeDetailsTypeIndex = 0;
+var kScopeDetailsObjectIndex = 1;
+
+function ScopeDetails(frame, fun, index) {
+ if (frame) {
+ this.break_id_ = frame.break_id_;
+ this.details_ = %GetScopeDetails(frame.break_id_,
+ frame.details_.frameId(),
+ frame.details_.inlinedFrameIndex(),
+ index);
+ this.frame_id_ = frame.details_.frameId();
+ this.inlined_frame_id_ = frame.details_.inlinedFrameIndex();
+ } else {
+ this.details_ = %GetFunctionScopeDetails(fun.value(), index);
+ this.fun_value_ = fun.value();
+ this.break_id_ = undefined;
+ }
+ this.index_ = index;
+}
+
+
+ScopeDetails.prototype.type = function() {
+ if (!IS_UNDEFINED(this.break_id_)) {
+ %CheckExecutionState(this.break_id_);
+ }
+ return this.details_[kScopeDetailsTypeIndex];
+};
+
+
+ScopeDetails.prototype.object = function() {
+ if (!IS_UNDEFINED(this.break_id_)) {
+ %CheckExecutionState(this.break_id_);
+ }
+ return this.details_[kScopeDetailsObjectIndex];
+};
+
+
+ScopeDetails.prototype.setVariableValueImpl = function(name, new_value) {
+ var raw_res;
+ if (!IS_UNDEFINED(this.break_id_)) {
+ %CheckExecutionState(this.break_id_);
+ raw_res = %SetScopeVariableValue(this.break_id_, this.frame_id_,
+ this.inlined_frame_id_, this.index_, name, new_value);
+ } else {
+ raw_res = %SetScopeVariableValue(this.fun_value_, null, null, this.index_,
+ name, new_value);
+ }
+ if (!raw_res) {
+ throw new Error("Failed to set variable value");
+ }
+};
+
+
+/**
+ * Mirror object for scope of frame or function. Either frame or function must
+ * be specified.
+ * @param {FrameMirror} frame The frame this scope is a part of
+ * @param {FunctionMirror} function The function this scope is a part of
+ * @param {number} index The scope index in the frame
+ * @constructor
+ * @extends Mirror
+ */
+function ScopeMirror(frame, function, index) {
+ %_CallFunction(this, SCOPE_TYPE, Mirror);
+ if (frame) {
+ this.frame_index_ = frame.index_;
+ } else {
+ this.frame_index_ = undefined;
+ }
+ this.scope_index_ = index;
+ this.details_ = new ScopeDetails(frame, function, index);
+}
+inherits(ScopeMirror, Mirror);
+
+
+ScopeMirror.prototype.frameIndex = function() {
+ return this.frame_index_;
+};
+
+
+ScopeMirror.prototype.scopeIndex = function() {
+ return this.scope_index_;
+};
+
+
+ScopeMirror.prototype.scopeType = function() {
+ return this.details_.type();
+};
+
+
+ScopeMirror.prototype.scopeObject = function() {
+ // For local and closure scopes create a transient mirror as these objects are
+ // created on the fly materializing the local or closure scopes and
+ // therefore will not preserve identity.
+ var transient = this.scopeType() == ScopeType.Local ||
+ this.scopeType() == ScopeType.Closure;
+ return MakeMirror(this.details_.object(), transient);
+};
+
+
+ScopeMirror.prototype.setVariableValue = function(name, new_value) {
+ this.details_.setVariableValueImpl(name, new_value);
+};
+
+
+/**
+ * Mirror object for script source.
+ * @param {Script} script The script object
+ * @constructor
+ * @extends Mirror
+ */
+function ScriptMirror(script) {
+ %_CallFunction(this, SCRIPT_TYPE, Mirror);
+ this.script_ = script;
+ this.context_ = new ContextMirror(script.context_data);
+ this.allocateHandle_();
+}
+inherits(ScriptMirror, Mirror);
+
+
+ScriptMirror.prototype.value = function() {
+ return this.script_;
+};
+
+
+ScriptMirror.prototype.name = function() {
+ return this.script_.name || this.script_.nameOrSourceURL();
+};
+
+
+ScriptMirror.prototype.id = function() {
+ return this.script_.id;
+};
+
+
+ScriptMirror.prototype.source = function() {
+ return this.script_.source;
+};
+
+
+ScriptMirror.prototype.setSource = function(source) {
+ %DebugSetScriptSource(this.script_, source);
+};
+
+
+ScriptMirror.prototype.lineOffset = function() {
+ return this.script_.line_offset;
+};
+
+
+ScriptMirror.prototype.columnOffset = function() {
+ return this.script_.column_offset;
+};
+
+
+ScriptMirror.prototype.data = function() {
+ return this.script_.data;
+};
+
+
+ScriptMirror.prototype.scriptType = function() {
+ return this.script_.type;
+};
+
+
+ScriptMirror.prototype.compilationType = function() {
+ return this.script_.compilation_type;
+};
+
+
+ScriptMirror.prototype.lineCount = function() {
+ return this.script_.lineCount();
+};
+
+
+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() {
+ return this.context_;
+};
+
+
+ScriptMirror.prototype.evalFromScript = function() {
+ return MakeMirror(this.script_.eval_from_script);
+};
+
+
+ScriptMirror.prototype.evalFromFunctionName = function() {
+ return MakeMirror(this.script_.eval_from_function_name);
+};
+
+
+ScriptMirror.prototype.evalFromLocation = function() {
+ var eval_from_script = this.evalFromScript();
+ if (!eval_from_script.isUndefined()) {
+ var position = this.script_.eval_from_script_position;
+ return eval_from_script.locationFromPosition(position, true);
+ }
+};
+
+
+ScriptMirror.prototype.toText = function() {
+ var result = '';
+ result += this.name();
+ result += ' (lines: ';
+ if (this.lineOffset() > 0) {
+ result += this.lineOffset();
+ result += '-';
+ result += this.lineOffset() + this.lineCount() - 1;
+ } else {
+ result += this.lineCount();
+ }
+ result += ')';
+ return result;
+};
+
+
+/**
+ * Mirror object for context.
+ * @param {Object} data The context data
+ * @constructor
+ * @extends Mirror
+ */
+function ContextMirror(data) {
+ %_CallFunction(this, CONTEXT_TYPE, Mirror);
+ this.data_ = data;
+ this.allocateHandle_();
+}
+inherits(ContextMirror, Mirror);
+
+
+ContextMirror.prototype.data = function() {
+ return this.data_;
+};
+
+
+/**
+ * Returns a mirror serializer
+ *
+ * @param {boolean} details Set to true to include details
+ * @param {Object} options Options comtrolling the serialization
+ * The following options can be set:
+ * includeSource: include ths full source of scripts
+ * @returns {MirrorSerializer} mirror serializer
+ */
+function MakeMirrorSerializer(details, options) {
+ return new JSONProtocolSerializer(details, options);
+}
+
+
+/**
+ * Object for serializing a mirror objects and its direct references.
+ * @param {boolean} details Indicates whether to include details for the mirror
+ * serialized
+ * @constructor
+ */
+function JSONProtocolSerializer(details, options) {
+ this.details_ = details;
+ this.options_ = options;
+ this.mirrors_ = [ ];
+}
+
+
+/**
+ * Returns a serialization of an object reference. The referenced object are
+ * added to the serialization state.
+ *
+ * @param {Mirror} mirror The mirror to serialize
+ * @returns {String} JSON serialization
+ */
+JSONProtocolSerializer.prototype.serializeReference = function(mirror) {
+ return this.serialize_(mirror, true, true);
+};
+
+
+/**
+ * Returns a serialization of an object value. The referenced objects are
+ * added to the serialization state.
+ *
+ * @param {Mirror} mirror The mirror to serialize
+ * @returns {String} JSON serialization
+ */
+JSONProtocolSerializer.prototype.serializeValue = function(mirror) {
+ var json = this.serialize_(mirror, false, true);
+ return json;
+};
+
+
+/**
+ * Returns a serialization of all the objects referenced.
+ *
+ * @param {Mirror} mirror The mirror to serialize.
+ * @returns {Array.<Object>} Array of the referenced objects converted to
+ * protcol objects.
+ */
+JSONProtocolSerializer.prototype.serializeReferencedObjects = function() {
+ // Collect the protocol representation of the referenced objects in an array.
+ var content = [];
+
+ // Get the number of referenced objects.
+ var count = this.mirrors_.length;
+
+ for (var i = 0; i < count; i++) {
+ content.push(this.serialize_(this.mirrors_[i], false, false));
+ }
+
+ 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() {
+ if (IS_UNDEFINED(this.options_) ||
+ IS_UNDEFINED(this.options_.maxStringLength)) {
+ return kMaxProtocolStringLength;
+ }
+ return this.options_.maxStringLength;
+};
+
+
+JSONProtocolSerializer.prototype.add_ = function(mirror) {
+ // If this mirror is already in the list just return.
+ for (var i = 0; i < this.mirrors_.length; i++) {
+ if (this.mirrors_[i] === mirror) {
+ return;
+ }
+ }
+
+ // Add the mirror to the list of mirrors to be serialized.
+ this.mirrors_.push(mirror);
+};
+
+
+/**
+ * Formats mirror object to protocol reference object with some data that can
+ * be used to display the value in debugger.
+ * @param {Mirror} mirror Mirror to serialize.
+ * @return {Object} Protocol reference object.
+ */
+JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ =
+ function(mirror) {
+ var o = {};
+ o.ref = mirror.handle();
+ o.type = mirror.type();
+ switch (mirror.type()) {
+ case UNDEFINED_TYPE:
+ case NULL_TYPE:
+ case BOOLEAN_TYPE:
+ case NUMBER_TYPE:
+ o.value = mirror.value();
+ break;
+ case STRING_TYPE:
+ o.value = mirror.getTruncatedValue(this.maxStringLength_());
+ break;
+ case FUNCTION_TYPE:
+ o.name = mirror.name();
+ o.inferredName = mirror.inferredName();
+ if (mirror.script()) {
+ o.scriptId = mirror.script().id();
+ }
+ break;
+ case ERROR_TYPE:
+ case REGEXP_TYPE:
+ o.value = mirror.toText();
+ break;
+ case OBJECT_TYPE:
+ o.className = mirror.className();
+ break;
+ }
+ return o;
+};
+
+
+JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference,
+ details) {
+ // If serializing a reference to a mirror just return the reference and add
+ // the mirror to the referenced mirrors.
+ if (reference &&
+ (mirror.isValue() || mirror.isScript() || mirror.isContext())) {
+ if (this.inlineRefs_() && mirror.isValue()) {
+ return this.serializeReferenceWithDisplayData_(mirror);
+ } else {
+ this.add_(mirror);
+ return {'ref' : mirror.handle()};
+ }
+ }
+
+ // Collect the JSON property/value pairs.
+ var content = {};
+
+ // Add the mirror handle.
+ if (mirror.isValue() || mirror.isScript() || mirror.isContext()) {
+ content.handle = mirror.handle();
+ }
+
+ // Always add the type.
+ content.type = mirror.type();
+
+ switch (mirror.type()) {
+ case UNDEFINED_TYPE:
+ case NULL_TYPE:
+ // Undefined and null are represented just by their type.
+ break;
+
+ case BOOLEAN_TYPE:
+ // Boolean values are simply represented by their value.
+ content.value = mirror.value();
+ break;
+
+ case NUMBER_TYPE:
+ // Number values are simply represented by their value.
+ content.value = NumberToJSON_(mirror.value());
+ break;
+
+ case STRING_TYPE:
+ // String values might have their value cropped to keep down size.
+ if (this.maxStringLength_() != -1 &&
+ mirror.length() > this.maxStringLength_()) {
+ var str = mirror.getTruncatedValue(this.maxStringLength_());
+ content.value = str;
+ content.fromIndex = 0;
+ content.toIndex = this.maxStringLength_();
+ } else {
+ content.value = mirror.value();
+ }
+ content.length = mirror.length();
+ break;
+
+ case OBJECT_TYPE:
+ case FUNCTION_TYPE:
+ case ERROR_TYPE:
+ case REGEXP_TYPE:
+ // Add object representation.
+ this.serializeObject_(mirror, content, details);
+ break;
+
+ case PROPERTY_TYPE:
+ case INTERNAL_PROPERTY_TYPE:
+ throw new Error('PropertyMirror cannot be serialized independently');
+ break;
+
+ case FRAME_TYPE:
+ // Add object representation.
+ this.serializeFrame_(mirror, content);
+ break;
+
+ case SCOPE_TYPE:
+ // Add object representation.
+ this.serializeScope_(mirror, content);
+ break;
+
+ case SCRIPT_TYPE:
+ // Script is represented by id, name and source attributes.
+ if (mirror.name()) {
+ content.name = mirror.name();
+ }
+ content.id = mirror.id();
+ content.lineOffset = mirror.lineOffset();
+ content.columnOffset = mirror.columnOffset();
+ content.lineCount = mirror.lineCount();
+ if (mirror.data()) {
+ content.data = mirror.data();
+ }
+ if (this.includeSource_()) {
+ content.source = mirror.source();
+ } else {
+ var sourceStart = mirror.source().substring(0, 80);
+ content.sourceStart = sourceStart;
+ }
+ content.sourceLength = mirror.source().length;
+ content.scriptType = mirror.scriptType();
+ content.compilationType = mirror.compilationType();
+ // For compilation type eval emit information on the script from which
+ // eval was called if a script is present.
+ if (mirror.compilationType() == 1 &&
+ mirror.evalFromScript()) {
+ content.evalFromScript =
+ this.serializeReference(mirror.evalFromScript());
+ var evalFromLocation = mirror.evalFromLocation();
+ if (evalFromLocation) {
+ content.evalFromLocation = { line: evalFromLocation.line,
+ column: evalFromLocation.column };
+ }
+ if (mirror.evalFromFunctionName()) {
+ content.evalFromFunctionName = mirror.evalFromFunctionName();
+ }
+ }
+ if (mirror.context()) {
+ content.context = this.serializeReference(mirror.context());
+ }
+ break;
+
+ case CONTEXT_TYPE:
+ content.data = mirror.data();
+ break;
+ }
+
+ // Always add the text representation.
+ content.text = mirror.toText();
+
+ // Create and return the JSON string.
+ return content;
+};
+
+
+/**
+ * Serialize object information to the following JSON format.
+ *
+ * {"className":"<class name>",
+ * "constructorFunction":{"ref":<number>},
+ * "protoObject":{"ref":<number>},
+ * "prototypeObject":{"ref":<number>},
+ * "namedInterceptor":<boolean>,
+ * "indexedInterceptor":<boolean>,
+ * "properties":[<properties>],
+ * "internalProperties":[<internal properties>]}
+ */
+JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content,
+ details) {
+ // Add general object properties.
+ content.className = mirror.className();
+ content.constructorFunction =
+ this.serializeReference(mirror.constructorFunction());
+ content.protoObject = this.serializeReference(mirror.protoObject());
+ content.prototypeObject = this.serializeReference(mirror.prototypeObject());
+
+ // Add flags to indicate whether there are interceptors.
+ if (mirror.hasNamedInterceptor()) {
+ content.namedInterceptor = true;
+ }
+ if (mirror.hasIndexedInterceptor()) {
+ content.indexedInterceptor = true;
+ }
+
+ // Add function specific properties.
+ if (mirror.isFunction()) {
+ // Add function specific properties.
+ content.name = mirror.name();
+ if (!IS_UNDEFINED(mirror.inferredName())) {
+ content.inferredName = mirror.inferredName();
+ }
+ content.resolved = mirror.resolved();
+ if (mirror.resolved()) {
+ content.source = mirror.source();
+ }
+ if (mirror.script()) {
+ content.script = this.serializeReference(mirror.script());
+ content.scriptId = mirror.script().id();
+
+ serializeLocationFields(mirror.sourceLocation(), content);
+ }
+
+ content.scopes = [];
+ for (var i = 0; i < mirror.scopeCount(); i++) {
+ var scope = mirror.scope(i);
+ content.scopes.push({
+ type: scope.scopeType(),
+ index: i
+ });
+ }
+ }
+
+ // Add date specific properties.
+ if (mirror.isDate()) {
+ // Add date specific properties.
+ content.value = mirror.value();
+ }
+
+ // Add actual properties - named properties followed by indexed properties.
+ var propertyNames = mirror.propertyNames(PropertyKind.Named);
+ var propertyIndexes = mirror.propertyNames(PropertyKind.Indexed);
+ var p = new Array(propertyNames.length + propertyIndexes.length);
+ for (var i = 0; i < propertyNames.length; i++) {
+ var propertyMirror = mirror.property(propertyNames[i]);
+ p[i] = this.serializeProperty_(propertyMirror);
+ if (details) {
+ this.add_(propertyMirror.value());
+ }
+ }
+ for (var i = 0; i < propertyIndexes.length; i++) {
+ var propertyMirror = mirror.property(propertyIndexes[i]);
+ p[propertyNames.length + i] = this.serializeProperty_(propertyMirror);
+ if (details) {
+ this.add_(propertyMirror.value());
+ }
+ }
+ content.properties = p;
+
+ var internalProperties = mirror.internalProperties();
+ if (internalProperties.length > 0) {
+ var ip = [];
+ for (var i = 0; i < internalProperties.length; i++) {
+ ip.push(this.serializeInternalProperty_(internalProperties[i]));
+ }
+ content.internalProperties = ip;
+ }
+};
+
+
+/**
+ * Serialize location information to the following JSON format:
+ *
+ * "position":"<position>",
+ * "line":"<line>",
+ * "column":"<column>",
+ *
+ * @param {SourceLocation} location The location to serialize, may be undefined.
+ */
+function serializeLocationFields (location, content) {
+ if (!location) {
+ return;
+ }
+ content.position = location.position;
+ var line = location.line;
+ if (!IS_UNDEFINED(line)) {
+ content.line = line;
+ }
+ var column = location.column;
+ if (!IS_UNDEFINED(column)) {
+ content.column = column;
+ }
+}
+
+
+/**
+ * Serialize property information to the following JSON format for building the
+ * array of properties.
+ *
+ * {"name":"<property name>",
+ * "attributes":<number>,
+ * "propertyType":<number>,
+ * "ref":<number>}
+ *
+ * If the attribute for the property is PropertyAttribute.None it is not added.
+ * If the propertyType for the property is PropertyType.Normal it is not added.
+ * Here are a couple of examples.
+ *
+ * {"name":"hello","ref":1}
+ * {"name":"length","attributes":7,"propertyType":3,"ref":2}
+ *
+ * @param {PropertyMirror} propertyMirror The property to serialize.
+ * @returns {Object} Protocol object representing the property.
+ */
+JSONProtocolSerializer.prototype.serializeProperty_ = function(propertyMirror) {
+ var result = {};
+
+ result.name = propertyMirror.name();
+ var propertyValue = propertyMirror.value();
+ if (this.inlineRefs_() && propertyValue.isValue()) {
+ result.value = this.serializeReferenceWithDisplayData_(propertyValue);
+ } else {
+ if (propertyMirror.attributes() != PropertyAttribute.None) {
+ result.attributes = propertyMirror.attributes();
+ }
+ if (propertyMirror.propertyType() != PropertyType.Normal) {
+ result.propertyType = propertyMirror.propertyType();
+ }
+ result.ref = propertyValue.handle();
+ }
+ return result;
+};
+
+
+/**
+ * Serialize internal property information to the following JSON format for
+ * building the array of properties.
+ *
+ * {"name":"<property name>",
+ * "ref":<number>}
+ *
+ * {"name":"[[BoundThis]]","ref":117}
+ *
+ * @param {InternalPropertyMirror} propertyMirror The property to serialize.
+ * @returns {Object} Protocol object representing the property.
+ */
+JSONProtocolSerializer.prototype.serializeInternalProperty_ =
+ function(propertyMirror) {
+ var result = {};
+
+ result.name = propertyMirror.name();
+ var propertyValue = propertyMirror.value();
+ if (this.inlineRefs_() && propertyValue.isValue()) {
+ result.value = this.serializeReferenceWithDisplayData_(propertyValue);
+ } else {
+ result.ref = propertyValue.handle();
+ }
+ return result;
+};
+
+
+JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) {
+ content.index = mirror.index();
+ content.receiver = this.serializeReference(mirror.receiver());
+ var func = mirror.func();
+ content.func = this.serializeReference(func);
+ if (func.script()) {
+ content.script = this.serializeReference(func.script());
+ }
+ content.constructCall = mirror.isConstructCall();
+ content.atReturn = mirror.isAtReturn();
+ if (mirror.isAtReturn()) {
+ content.returnValue = this.serializeReference(mirror.returnValue());
+ }
+ content.debuggerFrame = mirror.isDebuggerFrame();
+ var x = new Array(mirror.argumentCount());
+ for (var i = 0; i < mirror.argumentCount(); i++) {
+ var arg = {};
+ var argument_name = mirror.argumentName(i);
+ if (argument_name) {
+ arg.name = argument_name;
+ }
+ arg.value = this.serializeReference(mirror.argumentValue(i));
+ x[i] = arg;
+ }
+ content.arguments = x;
+ var x = new Array(mirror.localCount());
+ for (var i = 0; i < mirror.localCount(); i++) {
+ var local = {};
+ local.name = mirror.localName(i);
+ local.value = this.serializeReference(mirror.localValue(i));
+ x[i] = local;
+ }
+ content.locals = x;
+ serializeLocationFields(mirror.sourceLocation(), content);
+ var source_line_text = mirror.sourceLineText();
+ if (!IS_UNDEFINED(source_line_text)) {
+ content.sourceLineText = source_line_text;
+ }
+
+ content.scopes = [];
+ for (var i = 0; i < mirror.scopeCount(); i++) {
+ var scope = mirror.scope(i);
+ content.scopes.push({
+ type: scope.scopeType(),
+ index: i
+ });
+ }
+};
+
+
+JSONProtocolSerializer.prototype.serializeScope_ = function(mirror, content) {
+ content.index = mirror.scopeIndex();
+ content.frameIndex = mirror.frameIndex();
+ content.type = mirror.scopeType();
+ content.object = this.inlineRefs_() ?
+ this.serializeValue(mirror.scopeObject()) :
+ this.serializeReference(mirror.scopeObject());
+};
+
+
+/**
+ * Convert a number to a protocol value. For all finite numbers the number
+ * itself is returned. For non finite numbers NaN, Infinite and
+ * -Infinite the string representation "NaN", "Infinite" or "-Infinite"
+ * (not including the quotes) is returned.
+ *
+ * @param {number} value The number value to convert to a protocol value.
+ * @returns {number|string} Protocol value.
+ */
+function NumberToJSON_(value) {
+ if (isNaN(value)) {
+ return 'NaN';
+ }
+ if (!NUMBER_IS_FINITE(value)) {
+ if (value > 0) {
+ return 'Infinity';
+ } else {
+ return '-Infinity';
+ }
+ }
+ return value;
+}
diff --git a/chromium/v8/src/misc-intrinsics.h b/chromium/v8/src/misc-intrinsics.h
new file mode 100644
index 00000000000..5393de2c217
--- /dev/null
+++ b/chromium/v8/src/misc-intrinsics.h
@@ -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.
+
+#ifndef V8_MISC_INTRINSICS_H_
+#define V8_MISC_INTRINSICS_H_
+
+#include "../include/v8.h"
+#include "globals.h"
+
+namespace v8 {
+namespace internal {
+
+// Returns the index of the leading 1 bit, counting the least significant bit at
+// index 0. (1 << IntegerLog2(x)) is a mask for the most significant bit of x.
+// Result is undefined if input is zero.
+int IntegerLog2(uint32_t value);
+
+#if defined(__GNUC__)
+
+inline int IntegerLog2(uint32_t value) {
+ return 31 - __builtin_clz(value);
+}
+
+#elif defined(_MSC_VER)
+
+#pragma intrinsic(_BitScanReverse)
+
+inline int IntegerLog2(uint32_t value) {
+ unsigned long result; // NOLINT: MSVC intrinsic demands this type.
+ _BitScanReverse(&result, value);
+ return result;
+}
+
+#else
+
+// Default version using regular operations. Code taken from:
+// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
+inline int IntegerLog2(uint32_t value) {
+ int result, shift;
+
+ shift = (value > 0xFFFF) << 4;
+ value >>= shift;
+ result = shift;
+
+ shift = (value > 0xFF) << 3;
+ value >>= shift;
+ result |= shift;
+
+ shift = (value > 0xF) << 2;
+ value >>= shift;
+ result |= shift;
+
+ shift = (value > 0x3) << 1;
+ value >>= shift;
+ result |= shift;
+
+ result |= (value >> 1);
+
+ return result;
+}
+#endif
+
+} } // namespace v8::internal
+
+#endif // V8_MISC_INTRINSICS_H_
diff --git a/chromium/v8/src/mksnapshot.cc b/chromium/v8/src/mksnapshot.cc
new file mode 100644
index 00000000000..c1edcb1b3a1
--- /dev/null
+++ b/chromium/v8/src/mksnapshot.cc
@@ -0,0 +1,442 @@
+// 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.
+
+#include <errno.h>
+#include <stdio.h>
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+#include <bzlib.h>
+#endif
+#include <signal.h>
+
+#include "v8.h"
+
+#include "bootstrapper.h"
+#include "flags.h"
+#include "natives.h"
+#include "platform.h"
+#include "serialize.h"
+#include "list.h"
+
+using namespace v8;
+
+static const unsigned int kMaxCounters = 256;
+
+// A single counter in a counter collection.
+class Counter {
+ public:
+ static const int kMaxNameSize = 64;
+ int32_t* Bind(const char* name) {
+ int i;
+ for (i = 0; i < kMaxNameSize - 1 && name[i]; i++) {
+ name_[i] = name[i];
+ }
+ name_[i] = '\0';
+ return &counter_;
+ }
+ private:
+ int32_t counter_;
+ uint8_t name_[kMaxNameSize];
+};
+
+
+// A set of counters and associated information. An instance of this
+// class is stored directly in the memory-mapped counters file if
+// the --save-counters options is used
+class CounterCollection {
+ public:
+ CounterCollection() {
+ magic_number_ = 0xDEADFACE;
+ max_counters_ = kMaxCounters;
+ max_name_size_ = Counter::kMaxNameSize;
+ counters_in_use_ = 0;
+ }
+ Counter* GetNextCounter() {
+ if (counters_in_use_ == kMaxCounters) return NULL;
+ return &counters_[counters_in_use_++];
+ }
+ private:
+ uint32_t magic_number_;
+ uint32_t max_counters_;
+ uint32_t max_name_size_;
+ uint32_t counters_in_use_;
+ Counter counters_[kMaxCounters];
+};
+
+
+class Compressor {
+ public:
+ virtual ~Compressor() {}
+ virtual bool Compress(i::Vector<char> input) = 0;
+ virtual i::Vector<char>* output() = 0;
+};
+
+
+class PartialSnapshotSink : public i::SnapshotByteSink {
+ public:
+ PartialSnapshotSink() : data_(), raw_size_(-1) { }
+ virtual ~PartialSnapshotSink() { data_.Free(); }
+ virtual void Put(int byte, const char* description) {
+ data_.Add(byte);
+ }
+ virtual int Position() { return data_.length(); }
+ void Print(FILE* fp) {
+ int length = Position();
+ for (int j = 0; j < length; j++) {
+ if ((j & 0x1f) == 0x1f) {
+ fprintf(fp, "\n");
+ }
+ if (j != 0) {
+ fprintf(fp, ",");
+ }
+ fprintf(fp, "%u", static_cast<unsigned char>(at(j)));
+ }
+ }
+ char at(int i) { return data_[i]; }
+ bool Compress(Compressor* compressor) {
+ ASSERT_EQ(-1, raw_size_);
+ raw_size_ = data_.length();
+ if (!compressor->Compress(data_.ToVector())) return false;
+ data_.Clear();
+ data_.AddAll(*compressor->output());
+ return true;
+ }
+ int raw_size() { return raw_size_; }
+
+ private:
+ i::List<char> data_;
+ int raw_size_;
+};
+
+
+class CppByteSink : public PartialSnapshotSink {
+ public:
+ explicit CppByteSink(const char* snapshot_file) {
+ fp_ = i::OS::FOpen(snapshot_file, "wb");
+ if (fp_ == NULL) {
+ i::PrintF("Unable to write to snapshot file \"%s\"\n", snapshot_file);
+ exit(1);
+ }
+ fprintf(fp_, "// Autogenerated snapshot file. Do not edit.\n\n");
+ fprintf(fp_, "#include \"v8.h\"\n");
+ fprintf(fp_, "#include \"platform.h\"\n\n");
+ fprintf(fp_, "#include \"snapshot.h\"\n\n");
+ fprintf(fp_, "namespace v8 {\nnamespace internal {\n\n");
+ fprintf(fp_, "const byte Snapshot::data_[] = {");
+ }
+
+ virtual ~CppByteSink() {
+ fprintf(fp_, "const int Snapshot::size_ = %d;\n", Position());
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ fprintf(fp_, "const byte* Snapshot::raw_data_ = NULL;\n");
+ fprintf(fp_,
+ "const int Snapshot::raw_size_ = %d;\n\n",
+ raw_size());
+#else
+ fprintf(fp_,
+ "const byte* Snapshot::raw_data_ = Snapshot::data_;\n");
+ fprintf(fp_,
+ "const int Snapshot::raw_size_ = Snapshot::size_;\n\n");
+#endif
+ fprintf(fp_, "} } // namespace v8::internal\n");
+ fclose(fp_);
+ }
+
+ void WriteSpaceUsed(
+ const char* prefix,
+ int new_space_used,
+ int pointer_space_used,
+ int data_space_used,
+ int code_space_used,
+ int map_space_used,
+ int cell_space_used,
+ int property_cell_space_used) {
+ fprintf(fp_,
+ "const int Snapshot::%snew_space_used_ = %d;\n",
+ prefix,
+ new_space_used);
+ fprintf(fp_,
+ "const int Snapshot::%spointer_space_used_ = %d;\n",
+ prefix,
+ pointer_space_used);
+ fprintf(fp_,
+ "const int Snapshot::%sdata_space_used_ = %d;\n",
+ prefix,
+ data_space_used);
+ fprintf(fp_,
+ "const int Snapshot::%scode_space_used_ = %d;\n",
+ prefix,
+ code_space_used);
+ fprintf(fp_,
+ "const int Snapshot::%smap_space_used_ = %d;\n",
+ prefix,
+ map_space_used);
+ fprintf(fp_,
+ "const int Snapshot::%scell_space_used_ = %d;\n",
+ prefix,
+ cell_space_used);
+ fprintf(fp_,
+ "const int Snapshot::%sproperty_cell_space_used_ = %d;\n",
+ prefix,
+ property_cell_space_used);
+ }
+
+ void WritePartialSnapshot() {
+ int length = partial_sink_.Position();
+ fprintf(fp_, "};\n\n");
+ fprintf(fp_, "const int Snapshot::context_size_ = %d;\n", length);
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ fprintf(fp_,
+ "const int Snapshot::context_raw_size_ = %d;\n",
+ partial_sink_.raw_size());
+#else
+ fprintf(fp_,
+ "const int Snapshot::context_raw_size_ = "
+ "Snapshot::context_size_;\n");
+#endif
+ fprintf(fp_, "const byte Snapshot::context_data_[] = {\n");
+ partial_sink_.Print(fp_);
+ fprintf(fp_, "};\n\n");
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ fprintf(fp_, "const byte* Snapshot::context_raw_data_ = NULL;\n");
+#else
+ fprintf(fp_, "const byte* Snapshot::context_raw_data_ ="
+ " Snapshot::context_data_;\n");
+#endif
+ }
+
+ void WriteSnapshot() {
+ Print(fp_);
+ }
+
+ PartialSnapshotSink* partial_sink() { return &partial_sink_; }
+
+ private:
+ FILE* fp_;
+ PartialSnapshotSink partial_sink_;
+};
+
+
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+class BZip2Compressor : public Compressor {
+ public:
+ BZip2Compressor() : output_(NULL) {}
+ virtual ~BZip2Compressor() {
+ delete output_;
+ }
+ virtual bool Compress(i::Vector<char> input) {
+ delete output_;
+ output_ = new i::ScopedVector<char>((input.length() * 101) / 100 + 1000);
+ unsigned int output_length_ = output_->length();
+ int result = BZ2_bzBuffToBuffCompress(output_->start(), &output_length_,
+ input.start(), input.length(),
+ 9, 1, 0);
+ if (result == BZ_OK) {
+ output_->Truncate(output_length_);
+ return true;
+ } else {
+ fprintf(stderr, "bzlib error code: %d\n", result);
+ return false;
+ }
+ }
+ virtual i::Vector<char>* output() { return output_; }
+
+ private:
+ i::ScopedVector<char>* output_;
+};
+
+
+class BZip2Decompressor : public StartupDataDecompressor {
+ public:
+ virtual ~BZip2Decompressor() { }
+
+ protected:
+ virtual int DecompressData(char* raw_data,
+ int* raw_data_size,
+ const char* compressed_data,
+ int compressed_data_size) {
+ ASSERT_EQ(StartupData::kBZip2,
+ V8::GetCompressedStartupDataAlgorithm());
+ unsigned int decompressed_size = *raw_data_size;
+ int result =
+ BZ2_bzBuffToBuffDecompress(raw_data,
+ &decompressed_size,
+ const_cast<char*>(compressed_data),
+ compressed_data_size,
+ 0, 1);
+ if (result == BZ_OK) {
+ *raw_data_size = decompressed_size;
+ }
+ return result;
+ }
+};
+#endif
+
+
+void DumpException(Handle<Message> message) {
+ String::Utf8Value message_string(message->Get());
+ String::Utf8Value message_line(message->GetSourceLine());
+ fprintf(stderr, "%s at line %d\n", *message_string, message->GetLineNumber());
+ fprintf(stderr, "%s\n", *message_line);
+ for (int i = 0; i <= message->GetEndColumn(); ++i) {
+ fprintf(stderr, "%c", i < message->GetStartColumn() ? ' ' : '^');
+ }
+ fprintf(stderr, "\n");
+}
+
+
+int main(int argc, char** argv) {
+ V8::InitializeICU();
+
+ // By default, log code create information in the snapshot.
+ i::FLAG_log_code = true;
+
+ // Disable the i18n extension, as it doesn't support being snapshotted yet.
+ i::FLAG_enable_i18n = false;
+
+ // Print the usage if an error occurs when parsing the command line
+ // flags or if the help flag is set.
+ int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
+ if (result > 0 || argc != 2 || i::FLAG_help) {
+ ::printf("Usage: %s [flag] ... outfile\n", argv[0]);
+ i::FlagList::PrintHelp();
+ return !i::FLAG_help;
+ }
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ BZip2Decompressor natives_decompressor;
+ int bz2_result = natives_decompressor.Decompress();
+ if (bz2_result != BZ_OK) {
+ fprintf(stderr, "bzip error code: %d\n", bz2_result);
+ exit(1);
+ }
+#endif
+ i::Serializer::Enable();
+ Isolate* isolate = Isolate::GetCurrent();
+ Persistent<Context> context;
+ {
+ HandleScope handle_scope(isolate);
+ context.Reset(isolate, Context::New(isolate));
+ }
+
+ if (context.IsEmpty()) {
+ fprintf(stderr,
+ "\nException thrown while compiling natives - see above.\n\n");
+ exit(1);
+ }
+ if (i::FLAG_extra_code != NULL) {
+ // Capture 100 frames if anything happens.
+ V8::SetCaptureStackTraceForUncaughtExceptions(true, 100);
+ HandleScope scope(isolate);
+ v8::Context::Scope(v8::Local<v8::Context>::New(isolate, context));
+ const char* name = i::FLAG_extra_code;
+ FILE* file = i::OS::FOpen(name, "rb");
+ if (file == NULL) {
+ fprintf(stderr, "Failed to open '%s': errno %d\n", name, errno);
+ exit(1);
+ }
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+ rewind(file);
+
+ char* chars = new char[size + 1];
+ chars[size] = '\0';
+ for (int i = 0; i < size;) {
+ int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
+ if (read < 0) {
+ fprintf(stderr, "Failed to read '%s': errno %d\n", name, errno);
+ exit(1);
+ }
+ i += read;
+ }
+ fclose(file);
+ Local<String> source = String::New(chars);
+ TryCatch try_catch;
+ Local<Script> script = Script::Compile(source);
+ if (try_catch.HasCaught()) {
+ fprintf(stderr, "Failure compiling '%s'\n", name);
+ DumpException(try_catch.Message());
+ exit(1);
+ }
+ script->Run();
+ if (try_catch.HasCaught()) {
+ fprintf(stderr, "Failure running '%s'\n", name);
+ DumpException(try_catch.Message());
+ exit(1);
+ }
+ }
+ // Make sure all builtin scripts are cached.
+ { HandleScope scope(isolate);
+ for (int i = 0; i < i::Natives::GetBuiltinsCount(); i++) {
+ i::Isolate::Current()->bootstrapper()->NativesSourceLookup(i);
+ }
+ }
+ // 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(i::Heap::kNoGCFlags, "mksnapshot");
+ i::Object* raw_context = *v8::Utils::OpenPersistent(context);
+ context.Dispose(isolate);
+ CppByteSink sink(argv[1]);
+ // This results in a somewhat smaller snapshot, probably because it gets rid
+ // of some things that are cached between garbage collections.
+ i::StartupSerializer ser(&sink);
+ ser.SerializeStrongReferences();
+
+ i::PartialSerializer partial_ser(&ser, sink.partial_sink());
+ partial_ser.Serialize(&raw_context);
+
+ ser.SerializeWeakReferences();
+
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ BZip2Compressor compressor;
+ if (!sink.Compress(&compressor))
+ return 1;
+ if (!sink.partial_sink()->Compress(&compressor))
+ return 1;
+#endif
+ sink.WriteSnapshot();
+ sink.WritePartialSnapshot();
+
+ sink.WriteSpaceUsed(
+ "context_",
+ partial_ser.CurrentAllocationAddress(i::NEW_SPACE),
+ partial_ser.CurrentAllocationAddress(i::OLD_POINTER_SPACE),
+ partial_ser.CurrentAllocationAddress(i::OLD_DATA_SPACE),
+ partial_ser.CurrentAllocationAddress(i::CODE_SPACE),
+ partial_ser.CurrentAllocationAddress(i::MAP_SPACE),
+ partial_ser.CurrentAllocationAddress(i::CELL_SPACE),
+ partial_ser.CurrentAllocationAddress(i::PROPERTY_CELL_SPACE));
+ sink.WriteSpaceUsed(
+ "",
+ ser.CurrentAllocationAddress(i::NEW_SPACE),
+ ser.CurrentAllocationAddress(i::OLD_POINTER_SPACE),
+ ser.CurrentAllocationAddress(i::OLD_DATA_SPACE),
+ ser.CurrentAllocationAddress(i::CODE_SPACE),
+ ser.CurrentAllocationAddress(i::MAP_SPACE),
+ ser.CurrentAllocationAddress(i::CELL_SPACE),
+ ser.CurrentAllocationAddress(i::PROPERTY_CELL_SPACE));
+ return 0;
+}
diff --git a/chromium/v8/src/natives.h b/chromium/v8/src/natives.h
new file mode 100644
index 00000000000..e3f69d1dae0
--- /dev/null
+++ b/chromium/v8/src/natives.h
@@ -0,0 +1,68 @@
+// 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_NATIVES_H_
+#define V8_NATIVES_H_
+
+namespace v8 {
+namespace internal {
+
+typedef bool (*NativeSourceCallback)(Vector<const char> name,
+ Vector<const char> source,
+ int index);
+
+enum NativeType {
+ CORE, EXPERIMENTAL, D8, TEST, I18N
+};
+
+template <NativeType type>
+class NativesCollection {
+ public:
+ // Number of built-in scripts.
+ static int GetBuiltinsCount();
+ // Number of debugger implementation scripts.
+ static int GetDebuggerCount();
+
+ // These are used to access built-in scripts. The debugger implementation
+ // scripts have an index in the interval [0, GetDebuggerCount()). The
+ // non-debugger scripts have an index in the interval [GetDebuggerCount(),
+ // GetNativesCount()).
+ static int GetIndex(const char* name);
+ static int GetRawScriptsSize();
+ static Vector<const char> GetRawScriptSource(int index);
+ static Vector<const char> GetScriptName(int index);
+ static Vector<const byte> GetScriptsSource();
+ static void SetRawScriptsSource(Vector<const char> raw_source);
+};
+
+typedef NativesCollection<CORE> Natives;
+typedef NativesCollection<EXPERIMENTAL> ExperimentalNatives;
+typedef NativesCollection<I18N> I18NNatives;
+
+} } // namespace v8::internal
+
+#endif // V8_NATIVES_H_
diff --git a/chromium/v8/src/object-observe.js b/chromium/v8/src/object-observe.js
new file mode 100644
index 00000000000..f5e0d9d563a
--- /dev/null
+++ b/chromium/v8/src/object-observe.js
@@ -0,0 +1,465 @@
+// Copyright 2012 the V8 project authors. 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.
+
+"use strict";
+
+var observationState = %GetObservationState();
+if (IS_UNDEFINED(observationState.callbackInfoMap)) {
+ observationState.callbackInfoMap = %ObservationWeakMapCreate();
+ observationState.objectInfoMap = %ObservationWeakMapCreate();
+ observationState.notifierTargetMap = %ObservationWeakMapCreate();
+ observationState.pendingObservers = new InternalArray;
+ observationState.nextCallbackPriority = 0;
+}
+
+function ObservationWeakMap(map) {
+ this.map_ = map;
+}
+
+ObservationWeakMap.prototype = {
+ get: function(key) {
+ key = %UnwrapGlobalProxy(key);
+ if (!IS_SPEC_OBJECT(key)) return void 0;
+ return %WeakCollectionGet(this.map_, key);
+ },
+ set: function(key, value) {
+ key = %UnwrapGlobalProxy(key);
+ if (!IS_SPEC_OBJECT(key)) return void 0;
+ %WeakCollectionSet(this.map_, key, value);
+ },
+ has: function(key) {
+ return !IS_UNDEFINED(this.get(key));
+ }
+};
+
+var callbackInfoMap =
+ new ObservationWeakMap(observationState.callbackInfoMap);
+var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap);
+var notifierTargetMap =
+ new ObservationWeakMap(observationState.notifierTargetMap);
+
+function CreateObjectInfo(object) {
+ var info = {
+ changeObservers: new InternalArray,
+ notifier: null,
+ inactiveObservers: new InternalArray,
+ performing: { __proto__: null },
+ performingCount: 0,
+ };
+ objectInfoMap.set(object, info);
+ return info;
+}
+
+var defaultAcceptTypes = {
+ __proto__: null,
+ 'new': true,
+ 'updated': true,
+ 'deleted': true,
+ 'prototype': true,
+ 'reconfigured': true
+};
+
+function CreateObserver(callback, accept) {
+ var observer = {
+ __proto__: null,
+ callback: callback,
+ accept: defaultAcceptTypes
+ };
+
+ if (IS_UNDEFINED(accept))
+ return observer;
+
+ var acceptMap = { __proto__: null };
+ for (var i = 0; i < accept.length; i++)
+ acceptMap[accept[i]] = true;
+
+ observer.accept = acceptMap;
+ return observer;
+}
+
+function ObserverIsActive(observer, objectInfo) {
+ if (objectInfo.performingCount === 0)
+ return true;
+
+ var performing = objectInfo.performing;
+ for (var type in performing) {
+ if (performing[type] > 0 && observer.accept[type])
+ return false;
+ }
+
+ return true;
+}
+
+function ObserverIsInactive(observer, objectInfo) {
+ return !ObserverIsActive(observer, objectInfo);
+}
+
+function RemoveNullElements(from) {
+ var i = 0;
+ var j = 0;
+ for (; i < from.length; i++) {
+ if (from[i] === null)
+ continue;
+ if (j < i)
+ from[j] = from[i];
+ j++;
+ }
+
+ if (i !== j)
+ from.length = from.length - (i - j);
+}
+
+function RepartitionObservers(conditionFn, from, to, objectInfo) {
+ var anyRemoved = false;
+ for (var i = 0; i < from.length; i++) {
+ var observer = from[i];
+ if (conditionFn(observer, objectInfo)) {
+ anyRemoved = true;
+ from[i] = null;
+ to.push(observer);
+ }
+ }
+
+ if (anyRemoved)
+ RemoveNullElements(from);
+}
+
+function BeginPerformChange(objectInfo, type) {
+ objectInfo.performing[type] = (objectInfo.performing[type] || 0) + 1;
+ objectInfo.performingCount++;
+ RepartitionObservers(ObserverIsInactive,
+ objectInfo.changeObservers,
+ objectInfo.inactiveObservers,
+ objectInfo);
+}
+
+function EndPerformChange(objectInfo, type) {
+ objectInfo.performing[type]--;
+ objectInfo.performingCount--;
+ RepartitionObservers(ObserverIsActive,
+ objectInfo.inactiveObservers,
+ objectInfo.changeObservers,
+ objectInfo);
+}
+
+function EnsureObserverRemoved(objectInfo, callback) {
+ function remove(observerList) {
+ for (var i = 0; i < observerList.length; i++) {
+ if (observerList[i].callback === callback) {
+ observerList.splice(i, 1);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if (!remove(objectInfo.changeObservers))
+ remove(objectInfo.inactiveObservers);
+}
+
+function AcceptArgIsValid(arg) {
+ if (IS_UNDEFINED(arg))
+ return true;
+
+ if (!IS_SPEC_OBJECT(arg) ||
+ !IS_NUMBER(arg.length) ||
+ arg.length < 0)
+ return false;
+
+ var length = arg.length;
+ for (var i = 0; i < length; i++) {
+ if (!IS_STRING(arg[i]))
+ return false;
+ }
+ return true;
+}
+
+function EnsureCallbackPriority(callback) {
+ if (!callbackInfoMap.has(callback))
+ callbackInfoMap.set(callback, observationState.nextCallbackPriority++);
+}
+
+function NormalizeCallbackInfo(callback) {
+ var callbackInfo = callbackInfoMap.get(callback);
+ if (IS_NUMBER(callbackInfo)) {
+ var priority = callbackInfo;
+ callbackInfo = new InternalArray;
+ callbackInfo.priority = priority;
+ callbackInfoMap.set(callback, callbackInfo);
+ }
+ return callbackInfo;
+}
+
+function ObjectObserve(object, callback, accept) {
+ if (!IS_SPEC_OBJECT(object))
+ throw MakeTypeError("observe_non_object", ["observe"]);
+ if (!IS_SPEC_FUNCTION(callback))
+ throw MakeTypeError("observe_non_function", ["observe"]);
+ if (ObjectIsFrozen(callback))
+ throw MakeTypeError("observe_callback_frozen");
+ if (!AcceptArgIsValid(accept))
+ throw MakeTypeError("observe_accept_invalid");
+
+ EnsureCallbackPriority(callback);
+
+ var objectInfo = objectInfoMap.get(object);
+ if (IS_UNDEFINED(objectInfo)) {
+ objectInfo = CreateObjectInfo(object);
+ %SetIsObserved(object);
+ }
+
+ EnsureObserverRemoved(objectInfo, callback);
+
+ var observer = CreateObserver(callback, accept);
+ if (ObserverIsActive(observer, objectInfo))
+ objectInfo.changeObservers.push(observer);
+ else
+ objectInfo.inactiveObservers.push(observer);
+
+ return object;
+}
+
+function ObjectUnobserve(object, callback) {
+ if (!IS_SPEC_OBJECT(object))
+ throw MakeTypeError("observe_non_object", ["unobserve"]);
+ if (!IS_SPEC_FUNCTION(callback))
+ throw MakeTypeError("observe_non_function", ["unobserve"]);
+
+ var objectInfo = objectInfoMap.get(object);
+ if (IS_UNDEFINED(objectInfo))
+ return object;
+
+ EnsureObserverRemoved(objectInfo, callback);
+ return object;
+}
+
+function ArrayObserve(object, callback) {
+ return ObjectObserve(object, callback, ['new',
+ 'updated',
+ 'deleted',
+ 'splice']);
+}
+
+function ArrayUnobserve(object, callback) {
+ return ObjectUnobserve(object, callback);
+}
+
+function EnqueueToCallback(callback, changeRecord) {
+ var callbackInfo = NormalizeCallbackInfo(callback);
+ observationState.pendingObservers[callbackInfo.priority] = callback;
+ callbackInfo.push(changeRecord);
+ %SetObserverDeliveryPending();
+}
+
+function EnqueueChangeRecord(changeRecord, observers) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(changeRecord.name)) return;
+
+ for (var i = 0; i < observers.length; i++) {
+ var observer = observers[i];
+ if (IS_UNDEFINED(observer.accept[changeRecord.type]))
+ continue;
+
+ EnqueueToCallback(observer.callback, changeRecord);
+ }
+}
+
+function BeginPerformSplice(array) {
+ var objectInfo = objectInfoMap.get(array);
+ if (!IS_UNDEFINED(objectInfo))
+ BeginPerformChange(objectInfo, 'splice');
+}
+
+function EndPerformSplice(array) {
+ var objectInfo = objectInfoMap.get(array);
+ if (!IS_UNDEFINED(objectInfo))
+ EndPerformChange(objectInfo, 'splice');
+}
+
+function EnqueueSpliceRecord(array, index, removed, addedCount) {
+ var objectInfo = objectInfoMap.get(array);
+ if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
+ return;
+
+ var changeRecord = {
+ type: 'splice',
+ object: array,
+ index: index,
+ removed: removed,
+ addedCount: addedCount
+ };
+
+ ObjectFreeze(changeRecord);
+ ObjectFreeze(changeRecord.removed);
+ EnqueueChangeRecord(changeRecord, objectInfo.changeObservers);
+}
+
+function NotifyChange(type, object, name, oldValue) {
+ var objectInfo = objectInfoMap.get(object);
+ if (objectInfo.changeObservers.length === 0)
+ return;
+
+ var changeRecord = (arguments.length < 4) ?
+ { type: type, object: object, name: name } :
+ { type: type, object: object, name: name, oldValue: oldValue };
+ ObjectFreeze(changeRecord);
+ EnqueueChangeRecord(changeRecord, objectInfo.changeObservers);
+}
+
+var notifierPrototype = {};
+
+function ObjectNotifierNotify(changeRecord) {
+ if (!IS_SPEC_OBJECT(this))
+ throw MakeTypeError("called_on_non_object", ["notify"]);
+
+ var target = notifierTargetMap.get(this);
+ if (IS_UNDEFINED(target))
+ throw MakeTypeError("observe_notify_non_notifier");
+ if (!IS_STRING(changeRecord.type))
+ throw MakeTypeError("observe_type_non_string");
+
+ var objectInfo = objectInfoMap.get(target);
+ if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
+ return;
+
+ var newRecord = { object: target };
+ for (var prop in changeRecord) {
+ if (prop === 'object') continue;
+ %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop],
+ READ_ONLY + DONT_DELETE);
+ }
+ ObjectFreeze(newRecord);
+
+ EnqueueChangeRecord(newRecord, objectInfo.changeObservers);
+}
+
+function ObjectNotifierPerformChange(changeType, changeFn, receiver) {
+ if (!IS_SPEC_OBJECT(this))
+ throw MakeTypeError("called_on_non_object", ["performChange"]);
+
+ var target = notifierTargetMap.get(this);
+ if (IS_UNDEFINED(target))
+ throw MakeTypeError("observe_notify_non_notifier");
+ if (!IS_STRING(changeType))
+ throw MakeTypeError("observe_perform_non_string");
+ if (!IS_SPEC_FUNCTION(changeFn))
+ throw MakeTypeError("observe_perform_non_function");
+
+ if (IS_NULL_OR_UNDEFINED(receiver)) {
+ receiver = %GetDefaultReceiver(changeFn) || receiver;
+ } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(changeFn)) {
+ receiver = ToObject(receiver);
+ }
+
+ var objectInfo = objectInfoMap.get(target);
+ if (IS_UNDEFINED(objectInfo))
+ return;
+
+ BeginPerformChange(objectInfo, changeType);
+ try {
+ %_CallFunction(receiver, changeFn);
+ } finally {
+ EndPerformChange(objectInfo, changeType);
+ }
+}
+
+function ObjectGetNotifier(object) {
+ if (!IS_SPEC_OBJECT(object))
+ throw MakeTypeError("observe_non_object", ["getNotifier"]);
+
+ if (ObjectIsFrozen(object)) return null;
+
+ var objectInfo = objectInfoMap.get(object);
+ if (IS_UNDEFINED(objectInfo)) {
+ objectInfo = CreateObjectInfo(object);
+ %SetIsObserved(object);
+ }
+
+ if (IS_NULL(objectInfo.notifier)) {
+ objectInfo.notifier = { __proto__: notifierPrototype };
+ notifierTargetMap.set(objectInfo.notifier, object);
+ }
+
+ return objectInfo.notifier;
+}
+
+function CallbackDeliverPending(callback) {
+ var callbackInfo = callbackInfoMap.get(callback);
+ if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
+ return false;
+
+ // Clear the pending change records from callback and return it to its
+ // "optimized" state.
+ var priority = callbackInfo.priority;
+ callbackInfoMap.set(callback, priority);
+
+ delete observationState.pendingObservers[priority];
+ var delivered = [];
+ %MoveArrayContents(callbackInfo, delivered);
+
+ try {
+ %Call(void 0, delivered, callback);
+ } catch (ex) {}
+ return true;
+}
+
+function ObjectDeliverChangeRecords(callback) {
+ if (!IS_SPEC_FUNCTION(callback))
+ throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
+
+ while (CallbackDeliverPending(callback)) {}
+}
+
+function DeliverChangeRecords() {
+ while (observationState.pendingObservers.length) {
+ var pendingObservers = observationState.pendingObservers;
+ observationState.pendingObservers = new InternalArray;
+ for (var i in pendingObservers) {
+ CallbackDeliverPending(pendingObservers[i]);
+ }
+ }
+}
+
+function SetupObjectObserve() {
+ %CheckIsBootstrapping();
+ InstallFunctions($Object, DONT_ENUM, $Array(
+ "deliverChangeRecords", ObjectDeliverChangeRecords,
+ "getNotifier", ObjectGetNotifier,
+ "observe", ObjectObserve,
+ "unobserve", ObjectUnobserve
+ ));
+ InstallFunctions($Array, DONT_ENUM, $Array(
+ "observe", ArrayObserve,
+ "unobserve", ArrayUnobserve
+ ));
+ InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
+ "notify", ObjectNotifierNotify,
+ "performChange", ObjectNotifierPerformChange
+ ));
+}
+
+SetupObjectObserve();
diff --git a/chromium/v8/src/objects-debug.cc b/chromium/v8/src/objects-debug.cc
new file mode 100644
index 00000000000..e0cb8c92948
--- /dev/null
+++ b/chromium/v8/src/objects-debug.cc
@@ -0,0 +1,1177 @@
+// Copyright 2012 the V8 project authors. 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 "disassembler.h"
+#include "disasm.h"
+#include "jsregexp.h"
+#include "macro-assembler.h"
+#include "objects-visiting.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef VERIFY_HEAP
+
+void MaybeObject::Verify() {
+ Object* this_as_object;
+ if (ToObject(&this_as_object)) {
+ if (this_as_object->IsSmi()) {
+ Smi::cast(this_as_object)->SmiVerify();
+ } else {
+ HeapObject::cast(this_as_object)->HeapObjectVerify();
+ }
+ } else {
+ Failure::cast(this)->FailureVerify();
+ }
+}
+
+
+void Object::VerifyPointer(Object* p) {
+ if (p->IsHeapObject()) {
+ HeapObject::VerifyHeapPointer(p);
+ } else {
+ CHECK(p->IsSmi());
+ }
+}
+
+
+void Smi::SmiVerify() {
+ CHECK(IsSmi());
+}
+
+
+void Failure::FailureVerify() {
+ CHECK(IsFailure());
+}
+
+
+void HeapObject::HeapObjectVerify() {
+ InstanceType instance_type = map()->instance_type();
+
+ if (instance_type < FIRST_NONSTRING_TYPE) {
+ String::cast(this)->StringVerify();
+ return;
+ }
+
+ switch (instance_type) {
+ case SYMBOL_TYPE:
+ Symbol::cast(this)->SymbolVerify();
+ break;
+ case MAP_TYPE:
+ Map::cast(this)->MapVerify();
+ break;
+ case HEAP_NUMBER_TYPE:
+ HeapNumber::cast(this)->HeapNumberVerify();
+ break;
+ case FIXED_ARRAY_TYPE:
+ FixedArray::cast(this)->FixedArrayVerify();
+ break;
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ FixedDoubleArray::cast(this)->FixedDoubleArrayVerify();
+ break;
+ 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;
+ case EXTERNAL_BYTE_ARRAY_TYPE:
+ ExternalByteArray::cast(this)->ExternalByteArrayVerify();
+ break;
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ ExternalUnsignedByteArray::cast(this)->ExternalUnsignedByteArrayVerify();
+ break;
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ ExternalShortArray::cast(this)->ExternalShortArrayVerify();
+ break;
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ ExternalUnsignedShortArray::cast(this)->
+ ExternalUnsignedShortArrayVerify();
+ break;
+ case EXTERNAL_INT_ARRAY_TYPE:
+ ExternalIntArray::cast(this)->ExternalIntArrayVerify();
+ break;
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ ExternalUnsignedIntArray::cast(this)->ExternalUnsignedIntArrayVerify();
+ break;
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ ExternalFloatArray::cast(this)->ExternalFloatArrayVerify();
+ break;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ ExternalDoubleArray::cast(this)->ExternalDoubleArrayVerify();
+ break;
+ case CODE_TYPE:
+ Code::cast(this)->CodeVerify();
+ break;
+ case ODDBALL_TYPE:
+ Oddball::cast(this)->OddballVerify();
+ break;
+ case JS_OBJECT_TYPE:
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
+ JSObject::cast(this)->JSObjectVerify();
+ break;
+ case JS_GENERATOR_OBJECT_TYPE:
+ JSGeneratorObject::cast(this)->JSGeneratorObjectVerify();
+ break;
+ case JS_MODULE_TYPE:
+ JSModule::cast(this)->JSModuleVerify();
+ break;
+ case JS_VALUE_TYPE:
+ JSValue::cast(this)->JSValueVerify();
+ break;
+ case JS_DATE_TYPE:
+ JSDate::cast(this)->JSDateVerify();
+ break;
+ case JS_FUNCTION_TYPE:
+ JSFunction::cast(this)->JSFunctionVerify();
+ break;
+ case JS_GLOBAL_PROXY_TYPE:
+ JSGlobalProxy::cast(this)->JSGlobalProxyVerify();
+ break;
+ case JS_GLOBAL_OBJECT_TYPE:
+ JSGlobalObject::cast(this)->JSGlobalObjectVerify();
+ break;
+ case JS_BUILTINS_OBJECT_TYPE:
+ JSBuiltinsObject::cast(this)->JSBuiltinsObjectVerify();
+ break;
+ case CELL_TYPE:
+ Cell::cast(this)->CellVerify();
+ break;
+ case PROPERTY_CELL_TYPE:
+ PropertyCell::cast(this)->PropertyCellVerify();
+ break;
+ 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;
+ case JS_WEAK_SET_TYPE:
+ JSWeakSet::cast(this)->JSWeakSetVerify();
+ break;
+ case JS_REGEXP_TYPE:
+ JSRegExp::cast(this)->JSRegExpVerify();
+ break;
+ case FILLER_TYPE:
+ break;
+ case JS_PROXY_TYPE:
+ JSProxy::cast(this)->JSProxyVerify();
+ break;
+ case JS_FUNCTION_PROXY_TYPE:
+ JSFunctionProxy::cast(this)->JSFunctionProxyVerify();
+ break;
+ case FOREIGN_TYPE:
+ Foreign::cast(this)->ForeignVerify();
+ break;
+ case SHARED_FUNCTION_INFO_TYPE:
+ SharedFunctionInfo::cast(this)->SharedFunctionInfoVerify();
+ break;
+ case JS_MESSAGE_OBJECT_TYPE:
+ JSMessageObject::cast(this)->JSMessageObjectVerify();
+ break;
+ case JS_ARRAY_BUFFER_TYPE:
+ JSArrayBuffer::cast(this)->JSArrayBufferVerify();
+ break;
+ case JS_TYPED_ARRAY_TYPE:
+ JSTypedArray::cast(this)->JSTypedArrayVerify();
+ break;
+ case JS_DATA_VIEW_TYPE:
+ JSDataView::cast(this)->JSDataViewVerify();
+ break;
+
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE: \
+ Name::cast(this)->Name##Verify(); \
+ break;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void HeapObject::VerifyHeapPointer(Object* p) {
+ CHECK(p->IsHeapObject());
+ CHECK(HEAP->Contains(HeapObject::cast(p)));
+}
+
+
+void Symbol::SymbolVerify() {
+ CHECK(IsSymbol());
+ CHECK(HasHashCode());
+ CHECK_GT(Hash(), 0);
+ CHECK(name()->IsUndefined() || name()->IsString());
+}
+
+
+void HeapNumber::HeapNumberVerify() {
+ CHECK(IsHeapNumber());
+}
+
+
+void ByteArray::ByteArrayVerify() {
+ CHECK(IsByteArray());
+}
+
+
+void FreeSpace::FreeSpaceVerify() {
+ CHECK(IsFreeSpace());
+}
+
+
+void ExternalPixelArray::ExternalPixelArrayVerify() {
+ CHECK(IsExternalPixelArray());
+}
+
+
+void ExternalByteArray::ExternalByteArrayVerify() {
+ CHECK(IsExternalByteArray());
+}
+
+
+void ExternalUnsignedByteArray::ExternalUnsignedByteArrayVerify() {
+ CHECK(IsExternalUnsignedByteArray());
+}
+
+
+void ExternalShortArray::ExternalShortArrayVerify() {
+ CHECK(IsExternalShortArray());
+}
+
+
+void ExternalUnsignedShortArray::ExternalUnsignedShortArrayVerify() {
+ CHECK(IsExternalUnsignedShortArray());
+}
+
+
+void ExternalIntArray::ExternalIntArrayVerify() {
+ CHECK(IsExternalIntArray());
+}
+
+
+void ExternalUnsignedIntArray::ExternalUnsignedIntArrayVerify() {
+ CHECK(IsExternalUnsignedIntArray());
+}
+
+
+void ExternalFloatArray::ExternalFloatArrayVerify() {
+ CHECK(IsExternalFloatArray());
+}
+
+
+void ExternalDoubleArray::ExternalDoubleArrayVerify() {
+ CHECK(IsExternalDoubleArray());
+}
+
+
+void JSObject::JSObjectVerify() {
+ VerifyHeapPointer(properties());
+ VerifyHeapPointer(elements());
+
+ if (GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS) {
+ CHECK(this->elements()->IsFixedArray());
+ CHECK_GE(this->elements()->length(), 2);
+ }
+
+ if (HasFastProperties()) {
+ CHECK_EQ(map()->unused_property_fields(),
+ (map()->inobject_properties() + properties()->length() -
+ map()->NextFreePropertyIndex()));
+ DescriptorArray* descriptors = map()->instance_descriptors();
+ for (int i = 0; i < map()->NumberOfOwnDescriptors(); i++) {
+ if (descriptors->GetDetails(i).type() == FIELD) {
+ Representation r = descriptors->GetDetails(i).representation();
+ int field = descriptors->GetFieldIndex(i);
+ Object* value = RawFastPropertyAt(field);
+ if (r.IsDouble()) ASSERT(value->IsHeapNumber());
+ if (value->IsUninitialized()) continue;
+ if (r.IsSmi()) ASSERT(value->IsSmi());
+ if (r.IsHeapObject()) ASSERT(value->IsHeapObject());
+ }
+ }
+ }
+ CHECK_EQ((map()->has_fast_smi_or_object_elements() ||
+ (elements() == GetHeap()->empty_fixed_array())),
+ (elements()->map() == GetHeap()->fixed_array_map() ||
+ elements()->map() == GetHeap()->fixed_cow_array_map()));
+ CHECK(map()->has_fast_object_elements() == HasFastObjectElements());
+}
+
+
+void Map::MapVerify() {
+ CHECK(!HEAP->InNewSpace(this));
+ CHECK(FIRST_TYPE <= instance_type() && instance_type() <= LAST_TYPE);
+ CHECK(instance_size() == kVariableSizeSentinel ||
+ (kPointerSize <= instance_size() &&
+ instance_size() < HEAP->Capacity()));
+ VerifyHeapPointer(prototype());
+ VerifyHeapPointer(instance_descriptors());
+ SLOW_ASSERT(instance_descriptors()->IsSortedNoDuplicates());
+ if (HasTransitionArray()) {
+ SLOW_ASSERT(transitions()->IsSortedNoDuplicates());
+ SLOW_ASSERT(transitions()->IsConsistentWithBackPointers(this));
+ }
+ ASSERT(!is_observed() || instance_type() < FIRST_JS_OBJECT_TYPE ||
+ instance_type() > LAST_JS_OBJECT_TYPE ||
+ has_slow_elements_kind() || has_external_array_elements());
+}
+
+
+void Map::SharedMapVerify() {
+ MapVerify();
+ CHECK(is_shared());
+ CHECK(instance_descriptors()->IsEmpty());
+ CHECK_EQ(0, pre_allocated_property_fields());
+ CHECK_EQ(0, unused_property_fields());
+ CHECK_EQ(StaticVisitorBase::GetVisitorId(instance_type(), instance_size()),
+ visitor_id());
+}
+
+
+void Map::VerifyOmittedMapChecks() {
+ if (!FLAG_omit_map_checks_for_leaf_maps) return;
+ if (!is_stable() ||
+ is_deprecated() ||
+ HasTransitionArray() ||
+ is_dictionary_map()) {
+ CHECK_EQ(0, dependent_code()->number_of_entries(
+ DependentCode::kPrototypeCheckGroup));
+ }
+}
+
+
+void CodeCache::CodeCacheVerify() {
+ VerifyHeapPointer(default_cache());
+ VerifyHeapPointer(normal_type_cache());
+ CHECK(default_cache()->IsFixedArray());
+ CHECK(normal_type_cache()->IsUndefined()
+ || normal_type_cache()->IsCodeCacheHashTable());
+}
+
+
+void PolymorphicCodeCache::PolymorphicCodeCacheVerify() {
+ VerifyHeapPointer(cache());
+ CHECK(cache()->IsUndefined() || cache()->IsPolymorphicCodeCacheHashTable());
+}
+
+
+void TypeFeedbackInfo::TypeFeedbackInfoVerify() {
+ VerifyObjectField(kStorage1Offset);
+ VerifyObjectField(kStorage2Offset);
+ VerifyHeapPointer(type_feedback_cells());
+}
+
+
+void AliasedArgumentsEntry::AliasedArgumentsEntryVerify() {
+ VerifySmiField(kAliasedContextSlot);
+}
+
+
+void FixedArray::FixedArrayVerify() {
+ for (int i = 0; i < length(); i++) {
+ Object* e = get(i);
+ if (e->IsHeapObject()) {
+ VerifyHeapPointer(e);
+ } else {
+ e->Verify();
+ }
+ }
+}
+
+
+void FixedDoubleArray::FixedDoubleArrayVerify() {
+ for (int i = 0; i < length(); i++) {
+ if (!is_the_hole(i)) {
+ double value = get_scalar(i);
+ CHECK(!std::isnan(value) ||
+ (BitCast<uint64_t>(value) ==
+ BitCast<uint64_t>(canonical_not_the_hole_nan_as_double())) ||
+ ((BitCast<uint64_t>(value) & Double::kSignMask) != 0));
+ }
+ }
+}
+
+
+void JSGeneratorObject::JSGeneratorObjectVerify() {
+ // In an expression like "new g()", there can be a point where a generator
+ // object is allocated but its fields are all undefined, as it hasn't yet been
+ // initialized by the generator. Hence these weak checks.
+ VerifyObjectField(kFunctionOffset);
+ VerifyObjectField(kContextOffset);
+ VerifyObjectField(kReceiverOffset);
+ VerifyObjectField(kOperandStackOffset);
+ VerifyObjectField(kContinuationOffset);
+ VerifyObjectField(kStackHandlerIndexOffset);
+}
+
+
+void JSModule::JSModuleVerify() {
+ VerifyObjectField(kContextOffset);
+ VerifyObjectField(kScopeInfoOffset);
+ CHECK(context()->IsUndefined() ||
+ Context::cast(context())->IsModuleContext());
+}
+
+
+void JSValue::JSValueVerify() {
+ Object* v = value();
+ if (v->IsHeapObject()) {
+ VerifyHeapPointer(v);
+ }
+}
+
+
+void JSDate::JSDateVerify() {
+ if (value()->IsHeapObject()) {
+ VerifyHeapPointer(value());
+ }
+ CHECK(value()->IsUndefined() || value()->IsSmi() || value()->IsHeapNumber());
+ CHECK(year()->IsUndefined() || year()->IsSmi() || year()->IsNaN());
+ CHECK(month()->IsUndefined() || month()->IsSmi() || month()->IsNaN());
+ CHECK(day()->IsUndefined() || day()->IsSmi() || day()->IsNaN());
+ CHECK(weekday()->IsUndefined() || weekday()->IsSmi() || weekday()->IsNaN());
+ CHECK(hour()->IsUndefined() || hour()->IsSmi() || hour()->IsNaN());
+ CHECK(min()->IsUndefined() || min()->IsSmi() || min()->IsNaN());
+ CHECK(sec()->IsUndefined() || sec()->IsSmi() || sec()->IsNaN());
+ CHECK(cache_stamp()->IsUndefined() ||
+ cache_stamp()->IsSmi() ||
+ cache_stamp()->IsNaN());
+
+ if (month()->IsSmi()) {
+ int month = Smi::cast(this->month())->value();
+ CHECK(0 <= month && month <= 11);
+ }
+ if (day()->IsSmi()) {
+ int day = Smi::cast(this->day())->value();
+ CHECK(1 <= day && day <= 31);
+ }
+ if (hour()->IsSmi()) {
+ int hour = Smi::cast(this->hour())->value();
+ CHECK(0 <= hour && hour <= 23);
+ }
+ if (min()->IsSmi()) {
+ int min = Smi::cast(this->min())->value();
+ CHECK(0 <= min && min <= 59);
+ }
+ if (sec()->IsSmi()) {
+ int sec = Smi::cast(this->sec())->value();
+ CHECK(0 <= sec && sec <= 59);
+ }
+ if (weekday()->IsSmi()) {
+ int weekday = Smi::cast(this->weekday())->value();
+ CHECK(0 <= weekday && weekday <= 6);
+ }
+ if (cache_stamp()->IsSmi()) {
+ CHECK(Smi::cast(cache_stamp())->value() <=
+ Smi::cast(Isolate::Current()->date_cache()->stamp())->value());
+ }
+}
+
+
+void JSMessageObject::JSMessageObjectVerify() {
+ CHECK(IsJSMessageObject());
+ CHECK(type()->IsString());
+ CHECK(arguments()->IsJSArray());
+ VerifyObjectField(kStartPositionOffset);
+ VerifyObjectField(kEndPositionOffset);
+ VerifyObjectField(kArgumentsOffset);
+ VerifyObjectField(kScriptOffset);
+ VerifyObjectField(kStackTraceOffset);
+ VerifyObjectField(kStackFramesOffset);
+}
+
+
+void String::StringVerify() {
+ CHECK(IsString());
+ CHECK(length() >= 0 && length() <= Smi::kMaxValue);
+ if (IsInternalizedString()) {
+ CHECK(!HEAP->InNewSpace(this));
+ }
+ if (IsConsString()) {
+ ConsString::cast(this)->ConsStringVerify();
+ } else if (IsSlicedString()) {
+ SlicedString::cast(this)->SlicedStringVerify();
+ }
+}
+
+
+void ConsString::ConsStringVerify() {
+ CHECK(this->first()->IsString());
+ CHECK(this->second() == GetHeap()->empty_string() ||
+ this->second()->IsString());
+ 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.
+ CHECK(this->first()->IsSeqString() || this->first()->IsExternalString());
+ }
+}
+
+
+void SlicedString::SlicedStringVerify() {
+ CHECK(!this->parent()->IsConsString());
+ CHECK(!this->parent()->IsSlicedString());
+ CHECK(this->length() >= SlicedString::kMinLength);
+}
+
+
+void JSFunction::JSFunctionVerify() {
+ CHECK(IsJSFunction());
+ VerifyObjectField(kPrototypeOrInitialMapOffset);
+ VerifyObjectField(kNextFunctionLinkOffset);
+ CHECK(code()->IsCode());
+ CHECK(next_function_link() == NULL ||
+ next_function_link()->IsUndefined() ||
+ next_function_link()->IsJSFunction());
+}
+
+
+void SharedFunctionInfo::SharedFunctionInfoVerify() {
+ CHECK(IsSharedFunctionInfo());
+ VerifyObjectField(kNameOffset);
+ VerifyObjectField(kCodeOffset);
+ VerifyObjectField(kOptimizedCodeMapOffset);
+ VerifyObjectField(kScopeInfoOffset);
+ VerifyObjectField(kInstanceClassNameOffset);
+ VerifyObjectField(kFunctionDataOffset);
+ VerifyObjectField(kScriptOffset);
+ VerifyObjectField(kDebugInfoOffset);
+}
+
+
+void JSGlobalProxy::JSGlobalProxyVerify() {
+ CHECK(IsJSGlobalProxy());
+ JSObjectVerify();
+ VerifyObjectField(JSGlobalProxy::kNativeContextOffset);
+ // Make sure that this object has no properties, elements.
+ CHECK_EQ(0, properties()->length());
+ CHECK(HasFastObjectElements());
+ CHECK_EQ(0, FixedArray::cast(elements())->length());
+}
+
+
+void JSGlobalObject::JSGlobalObjectVerify() {
+ CHECK(IsJSGlobalObject());
+ JSObjectVerify();
+ for (int i = GlobalObject::kBuiltinsOffset;
+ i < JSGlobalObject::kSize;
+ i += kPointerSize) {
+ VerifyObjectField(i);
+ }
+}
+
+
+void JSBuiltinsObject::JSBuiltinsObjectVerify() {
+ CHECK(IsJSBuiltinsObject());
+ JSObjectVerify();
+ for (int i = GlobalObject::kBuiltinsOffset;
+ i < JSBuiltinsObject::kSize;
+ i += kPointerSize) {
+ VerifyObjectField(i);
+ }
+}
+
+
+void Oddball::OddballVerify() {
+ CHECK(IsOddball());
+ VerifyHeapPointer(to_string());
+ Object* number = to_number();
+ if (number->IsHeapObject()) {
+ CHECK(number == HEAP->nan_value());
+ } else {
+ CHECK(number->IsSmi());
+ int value = Smi::cast(number)->value();
+ // Hidden oddballs have negative smis.
+ const int kLeastHiddenOddballNumber = -4;
+ CHECK_LE(value, 1);
+ CHECK(value >= kLeastHiddenOddballNumber);
+ }
+}
+
+
+void Cell::CellVerify() {
+ CHECK(IsCell());
+ VerifyObjectField(kValueOffset);
+}
+
+
+void PropertyCell::PropertyCellVerify() {
+ CHECK(IsPropertyCell());
+ VerifyObjectField(kValueOffset);
+ VerifyObjectField(kTypeOffset);
+}
+
+
+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();
+ // Ensure that GC will not iterate twice over the same pointer.
+ if (RelocInfo::IsGCRelocMode(it.rinfo()->rmode())) {
+ CHECK(it.rinfo()->pc() != last_gc_pc);
+ last_gc_pc = it.rinfo()->pc();
+ }
+ }
+}
+
+
+void Code::VerifyEmbeddedMapsDependency() {
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
+ RelocInfo::Mode mode = it.rinfo()->rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT &&
+ it.rinfo()->target_object()->IsMap()) {
+ Map* map = Map::cast(it.rinfo()->target_object());
+ if (map->CanTransition()) {
+ CHECK(map->dependent_code()->Contains(
+ DependentCode::kWeaklyEmbeddedGroup, this));
+ }
+ }
+ }
+}
+
+
+void JSArray::JSArrayVerify() {
+ JSObjectVerify();
+ CHECK(length()->IsNumber() || length()->IsUndefined());
+ CHECK(elements()->IsUndefined() ||
+ elements()->IsFixedArray() ||
+ elements()->IsFixedDoubleArray());
+}
+
+
+void JSSet::JSSetVerify() {
+ CHECK(IsJSSet());
+ JSObjectVerify();
+ VerifyHeapPointer(table());
+ CHECK(table()->IsHashTable() || table()->IsUndefined());
+}
+
+
+void JSMap::JSMapVerify() {
+ CHECK(IsJSMap());
+ JSObjectVerify();
+ VerifyHeapPointer(table());
+ CHECK(table()->IsHashTable() || table()->IsUndefined());
+}
+
+
+void JSWeakMap::JSWeakMapVerify() {
+ CHECK(IsJSWeakMap());
+ JSObjectVerify();
+ VerifyHeapPointer(table());
+ CHECK(table()->IsHashTable() || table()->IsUndefined());
+}
+
+
+void JSWeakSet::JSWeakSetVerify() {
+ CHECK(IsJSWeakSet());
+ JSObjectVerify();
+ VerifyHeapPointer(table());
+ CHECK(table()->IsHashTable() || table()->IsUndefined());
+}
+
+
+void JSRegExp::JSRegExpVerify() {
+ JSObjectVerify();
+ CHECK(data()->IsUndefined() || data()->IsFixedArray());
+ switch (TypeTag()) {
+ case JSRegExp::ATOM: {
+ FixedArray* arr = FixedArray::cast(data());
+ CHECK(arr->get(JSRegExp::kAtomPatternIndex)->IsString());
+ break;
+ }
+ case JSRegExp::IRREGEXP: {
+ bool is_native = RegExpImpl::UsesNativeRegExp();
+
+ FixedArray* arr = FixedArray::cast(data());
+ Object* ascii_data = arr->get(JSRegExp::kIrregexpASCIICodeIndex);
+ // Smi : Not compiled yet (-1) or code prepared for flushing.
+ // JSObject: Compilation error.
+ // Code/ByteArray: Compiled code.
+ CHECK(ascii_data->IsSmi() ||
+ (is_native ? ascii_data->IsCode() : ascii_data->IsByteArray()));
+ Object* uc16_data = arr->get(JSRegExp::kIrregexpUC16CodeIndex);
+ CHECK(uc16_data->IsSmi() ||
+ (is_native ? uc16_data->IsCode() : uc16_data->IsByteArray()));
+
+ Object* ascii_saved = arr->get(JSRegExp::kIrregexpASCIICodeSavedIndex);
+ CHECK(ascii_saved->IsSmi() || ascii_saved->IsString() ||
+ ascii_saved->IsCode());
+ Object* uc16_saved = arr->get(JSRegExp::kIrregexpUC16CodeSavedIndex);
+ CHECK(uc16_saved->IsSmi() || uc16_saved->IsString() ||
+ uc16_saved->IsCode());
+
+ CHECK(arr->get(JSRegExp::kIrregexpCaptureCountIndex)->IsSmi());
+ CHECK(arr->get(JSRegExp::kIrregexpMaxRegisterCountIndex)->IsSmi());
+ break;
+ }
+ default:
+ CHECK_EQ(JSRegExp::NOT_COMPILED, TypeTag());
+ CHECK(data()->IsUndefined());
+ break;
+ }
+}
+
+
+void JSProxy::JSProxyVerify() {
+ CHECK(IsJSProxy());
+ VerifyPointer(handler());
+ CHECK(hash()->IsSmi() || hash()->IsUndefined());
+}
+
+
+void JSFunctionProxy::JSFunctionProxyVerify() {
+ CHECK(IsJSFunctionProxy());
+ JSProxyVerify();
+ VerifyPointer(call_trap());
+ VerifyPointer(construct_trap());
+}
+
+
+void JSArrayBuffer::JSArrayBufferVerify() {
+ CHECK(IsJSArrayBuffer());
+ JSObjectVerify();
+ VerifyPointer(byte_length());
+ CHECK(byte_length()->IsSmi() || byte_length()->IsHeapNumber()
+ || byte_length()->IsUndefined());
+}
+
+
+void JSArrayBufferView::JSArrayBufferViewVerify() {
+ CHECK(IsJSArrayBufferView());
+ JSObjectVerify();
+ VerifyPointer(buffer());
+ CHECK(buffer()->IsJSArrayBuffer() || buffer()->IsUndefined());
+
+ VerifyPointer(byte_offset());
+ CHECK(byte_offset()->IsSmi() || byte_offset()->IsHeapNumber()
+ || byte_offset()->IsUndefined());
+
+ VerifyPointer(byte_length());
+ CHECK(byte_length()->IsSmi() || byte_length()->IsHeapNumber()
+ || byte_length()->IsUndefined());
+}
+
+
+void JSTypedArray::JSTypedArrayVerify() {
+ CHECK(IsJSTypedArray());
+ JSArrayBufferViewVerify();
+ VerifyPointer(length());
+ CHECK(length()->IsSmi() || length()->IsHeapNumber()
+ || length()->IsUndefined());
+
+ VerifyPointer(elements());
+}
+
+
+void JSDataView::JSDataViewVerify() {
+ CHECK(IsJSDataView());
+ JSArrayBufferViewVerify();
+}
+
+
+void Foreign::ForeignVerify() {
+ CHECK(IsForeign());
+}
+
+
+void Box::BoxVerify() {
+ CHECK(IsBox());
+ value()->Verify();
+}
+
+
+void AccessorInfo::AccessorInfoVerify() {
+ VerifyPointer(name());
+ VerifyPointer(flag());
+ VerifyPointer(expected_receiver_type());
+}
+
+
+void ExecutableAccessorInfo::ExecutableAccessorInfoVerify() {
+ CHECK(IsExecutableAccessorInfo());
+ AccessorInfoVerify();
+ VerifyPointer(getter());
+ VerifyPointer(setter());
+ VerifyPointer(data());
+}
+
+
+void DeclaredAccessorDescriptor::DeclaredAccessorDescriptorVerify() {
+ CHECK(IsDeclaredAccessorDescriptor());
+ VerifyPointer(serialized_data());
+}
+
+
+void DeclaredAccessorInfo::DeclaredAccessorInfoVerify() {
+ CHECK(IsDeclaredAccessorInfo());
+ AccessorInfoVerify();
+ VerifyPointer(descriptor());
+}
+
+
+void AccessorPair::AccessorPairVerify() {
+ CHECK(IsAccessorPair());
+ VerifyPointer(getter());
+ VerifyPointer(setter());
+}
+
+
+void AccessCheckInfo::AccessCheckInfoVerify() {
+ CHECK(IsAccessCheckInfo());
+ VerifyPointer(named_callback());
+ VerifyPointer(indexed_callback());
+ VerifyPointer(data());
+}
+
+
+void InterceptorInfo::InterceptorInfoVerify() {
+ CHECK(IsInterceptorInfo());
+ VerifyPointer(getter());
+ VerifyPointer(setter());
+ VerifyPointer(query());
+ VerifyPointer(deleter());
+ VerifyPointer(enumerator());
+ VerifyPointer(data());
+}
+
+
+void CallHandlerInfo::CallHandlerInfoVerify() {
+ CHECK(IsCallHandlerInfo());
+ VerifyPointer(callback());
+ VerifyPointer(data());
+}
+
+
+void TemplateInfo::TemplateInfoVerify() {
+ VerifyPointer(tag());
+ VerifyPointer(property_list());
+}
+
+
+void FunctionTemplateInfo::FunctionTemplateInfoVerify() {
+ CHECK(IsFunctionTemplateInfo());
+ TemplateInfoVerify();
+ VerifyPointer(serial_number());
+ VerifyPointer(call_code());
+ VerifyPointer(property_accessors());
+ VerifyPointer(prototype_template());
+ VerifyPointer(parent_template());
+ VerifyPointer(named_property_handler());
+ VerifyPointer(indexed_property_handler());
+ VerifyPointer(instance_template());
+ VerifyPointer(signature());
+ VerifyPointer(access_check_info());
+}
+
+
+void ObjectTemplateInfo::ObjectTemplateInfoVerify() {
+ CHECK(IsObjectTemplateInfo());
+ TemplateInfoVerify();
+ VerifyPointer(constructor());
+ VerifyPointer(internal_field_count());
+}
+
+
+void SignatureInfo::SignatureInfoVerify() {
+ CHECK(IsSignatureInfo());
+ VerifyPointer(receiver());
+ VerifyPointer(args());
+}
+
+
+void TypeSwitchInfo::TypeSwitchInfoVerify() {
+ CHECK(IsTypeSwitchInfo());
+ VerifyPointer(types());
+}
+
+
+void AllocationSite::AllocationSiteVerify() {
+ CHECK(IsAllocationSite());
+}
+
+
+void AllocationMemento::AllocationMementoVerify() {
+ CHECK(IsAllocationMemento());
+ VerifyHeapPointer(allocation_site());
+ CHECK(!IsValid() || GetAllocationSite()->IsAllocationSite());
+}
+
+
+void Script::ScriptVerify() {
+ CHECK(IsScript());
+ VerifyPointer(source());
+ VerifyPointer(name());
+ line_offset()->SmiVerify();
+ column_offset()->SmiVerify();
+ VerifyPointer(data());
+ VerifyPointer(wrapper());
+ type()->SmiVerify();
+ VerifyPointer(line_ends());
+ VerifyPointer(id());
+}
+
+
+void JSFunctionResultCache::JSFunctionResultCacheVerify() {
+ JSFunction::cast(get(kFactoryIndex))->Verify();
+
+ int size = Smi::cast(get(kCacheSizeIndex))->value();
+ CHECK(kEntriesIndex <= size);
+ CHECK(size <= length());
+ CHECK_EQ(0, size % kEntrySize);
+
+ int finger = Smi::cast(get(kFingerIndex))->value();
+ CHECK(kEntriesIndex <= finger);
+ CHECK((finger < size) || (finger == kEntriesIndex && finger == size));
+ CHECK_EQ(0, finger % kEntrySize);
+
+ if (FLAG_enable_slow_asserts) {
+ for (int i = kEntriesIndex; i < size; i++) {
+ CHECK(!get(i)->IsTheHole());
+ get(i)->Verify();
+ }
+ for (int i = size; i < length(); i++) {
+ CHECK(get(i)->IsTheHole());
+ get(i)->Verify();
+ }
+ }
+}
+
+
+void NormalizedMapCache::NormalizedMapCacheVerify() {
+ FixedArray::cast(this)->Verify();
+ if (FLAG_enable_slow_asserts) {
+ for (int i = 0; i < length(); i++) {
+ Object* e = get(i);
+ if (e->IsMap()) {
+ Map::cast(e)->SharedMapVerify();
+ } else {
+ CHECK(e->IsUndefined());
+ }
+ }
+ }
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void DebugInfo::DebugInfoVerify() {
+ CHECK(IsDebugInfo());
+ VerifyPointer(shared());
+ VerifyPointer(original_code());
+ VerifyPointer(code());
+ VerifyPointer(break_points());
+}
+
+
+void BreakPointInfo::BreakPointInfoVerify() {
+ CHECK(IsBreakPointInfo());
+ code_position()->SmiVerify();
+ source_position()->SmiVerify();
+ statement_position()->SmiVerify();
+ VerifyPointer(break_point_objects());
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+#endif // VERIFY_HEAP
+
+#ifdef DEBUG
+
+void JSObject::IncrementSpillStatistics(SpillInformation* info) {
+ info->number_of_objects_++;
+ // Named properties
+ if (HasFastProperties()) {
+ info->number_of_objects_with_fast_properties_++;
+ info->number_of_fast_used_fields_ += map()->NextFreePropertyIndex();
+ info->number_of_fast_unused_fields_ += map()->unused_property_fields();
+ } else {
+ NameDictionary* dict = property_dictionary();
+ info->number_of_slow_used_properties_ += dict->NumberOfElements();
+ info->number_of_slow_unused_properties_ +=
+ dict->Capacity() - dict->NumberOfElements();
+ }
+ // Indexed properties
+ switch (GetElementsKind()) {
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_ELEMENTS: {
+ info->number_of_objects_with_fast_elements_++;
+ int holes = 0;
+ FixedArray* e = FixedArray::cast(elements());
+ int len = e->length();
+ Heap* heap = HEAP;
+ for (int i = 0; i < len; i++) {
+ if (e->get(i) == heap->the_hole_value()) holes++;
+ }
+ info->number_of_fast_used_elements_ += len - holes;
+ info->number_of_fast_unused_elements_ += holes;
+ 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: {
+ info->number_of_objects_with_fast_elements_++;
+ ExternalPixelArray* e = ExternalPixelArray::cast(elements());
+ info->number_of_fast_used_elements_ += e->length();
+ break;
+ }
+ case DICTIONARY_ELEMENTS: {
+ SeededNumberDictionary* dict = element_dictionary();
+ info->number_of_slow_used_elements_ += dict->NumberOfElements();
+ info->number_of_slow_unused_elements_ +=
+ dict->Capacity() - dict->NumberOfElements();
+ break;
+ }
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ break;
+ }
+}
+
+
+void JSObject::SpillInformation::Clear() {
+ number_of_objects_ = 0;
+ number_of_objects_with_fast_properties_ = 0;
+ number_of_objects_with_fast_elements_ = 0;
+ number_of_fast_used_fields_ = 0;
+ number_of_fast_unused_fields_ = 0;
+ number_of_slow_used_properties_ = 0;
+ number_of_slow_unused_properties_ = 0;
+ number_of_fast_used_elements_ = 0;
+ number_of_fast_unused_elements_ = 0;
+ number_of_slow_used_elements_ = 0;
+ number_of_slow_unused_elements_ = 0;
+}
+
+
+void JSObject::SpillInformation::Print() {
+ PrintF("\n JSObject Spill Statistics (#%d):\n", number_of_objects_);
+
+ PrintF(" - fast properties (#%d): %d (used) %d (unused)\n",
+ number_of_objects_with_fast_properties_,
+ number_of_fast_used_fields_, number_of_fast_unused_fields_);
+
+ PrintF(" - slow properties (#%d): %d (used) %d (unused)\n",
+ number_of_objects_ - number_of_objects_with_fast_properties_,
+ number_of_slow_used_properties_, number_of_slow_unused_properties_);
+
+ PrintF(" - fast elements (#%d): %d (used) %d (unused)\n",
+ number_of_objects_with_fast_elements_,
+ number_of_fast_used_elements_, number_of_fast_unused_elements_);
+
+ PrintF(" - slow elements (#%d): %d (used) %d (unused)\n",
+ number_of_objects_ - number_of_objects_with_fast_elements_,
+ number_of_slow_used_elements_, number_of_slow_unused_elements_);
+
+ PrintF("\n");
+}
+
+
+bool DescriptorArray::IsSortedNoDuplicates(int valid_entries) {
+ if (valid_entries == -1) valid_entries = number_of_descriptors();
+ Name* current_key = NULL;
+ uint32_t current = 0;
+ for (int i = 0; i < number_of_descriptors(); i++) {
+ Name* key = GetSortedKey(i);
+ if (key == current_key) {
+ PrintDescriptors();
+ return false;
+ }
+ current_key = key;
+ uint32_t hash = GetSortedKey(i)->Hash();
+ if (hash < current) {
+ PrintDescriptors();
+ return false;
+ }
+ current = hash;
+ }
+ return true;
+}
+
+
+bool TransitionArray::IsSortedNoDuplicates(int valid_entries) {
+ ASSERT(valid_entries == -1);
+ Name* current_key = NULL;
+ uint32_t current = 0;
+ for (int i = 0; i < number_of_transitions(); i++) {
+ Name* key = GetSortedKey(i);
+ if (key == current_key) {
+ PrintTransitions();
+ return false;
+ }
+ current_key = key;
+ uint32_t hash = GetSortedKey(i)->Hash();
+ if (hash < current) {
+ PrintTransitions();
+ return false;
+ }
+ current = hash;
+ }
+ return true;
+}
+
+
+static bool CheckOneBackPointer(Map* current_map, Object* target) {
+ return !target->IsMap() || Map::cast(target)->GetBackPointer() == current_map;
+}
+
+
+bool TransitionArray::IsConsistentWithBackPointers(Map* current_map) {
+ for (int i = 0; i < number_of_transitions(); ++i) {
+ if (!CheckOneBackPointer(current_map, GetTarget(i))) return false;
+ }
+ return true;
+}
+
+
+#endif // DEBUG
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/objects-inl.h b/chromium/v8/src/objects-inl.h
new file mode 100644
index 00000000000..169475791d0
--- /dev/null
+++ b/chromium/v8/src/objects-inl.h
@@ -0,0 +1,6253 @@
+// Copyright 2012 the V8 project authors. 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.
+//
+// Review notes:
+//
+// - The use of macros in these inline functions may seem superfluous
+// but it is absolutely needed to make sure gcc generates optimal
+// code. gcc is not happy when attempting to inline too deep.
+//
+
+#ifndef V8_OBJECTS_INL_H_
+#define V8_OBJECTS_INL_H_
+
+#include "elements.h"
+#include "objects.h"
+#include "contexts.h"
+#include "conversions-inl.h"
+#include "heap.h"
+#include "isolate.h"
+#include "property.h"
+#include "spaces.h"
+#include "store-buffer.h"
+#include "v8memory.h"
+#include "factory.h"
+#include "incremental-marking.h"
+#include "transitions-inl.h"
+
+namespace v8 {
+namespace internal {
+
+PropertyDetails::PropertyDetails(Smi* smi) {
+ value_ = smi->value();
+}
+
+
+Smi* PropertyDetails::AsSmi() {
+ // Ensure the upper 2 bits have the same value by sign extending it. This is
+ // necessary to be able to use the 31st bit of the property details.
+ int value = value_ << 1;
+ return Smi::FromInt(value >> 1);
+}
+
+
+PropertyDetails PropertyDetails::AsDeleted() {
+ Smi* smi = Smi::FromInt(value_ | DeletedField::encode(1));
+ return PropertyDetails(smi);
+}
+
+
+#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()); \
+ return reinterpret_cast<type*>(object); \
+ }
+
+
+#define INT_ACCESSORS(holder, name, offset) \
+ int holder::name() { return READ_INT_FIELD(this, offset); } \
+ void holder::set_##name(int value) { WRITE_INT_FIELD(this, offset, value); }
+
+
+#define ACCESSORS(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(GetHeap(), this, offset, value, mode); \
+ }
+
+
+// Getter that returns a tagged Smi and setter that writes a tagged Smi.
+#define ACCESSORS_TO_SMI(holder, name, offset) \
+ Smi* holder::name() { return Smi::cast(READ_FIELD(this, offset)); } \
+ void holder::set_##name(Smi* value, WriteBarrierMode mode) { \
+ WRITE_FIELD(this, offset, value); \
+ }
+
+
+// Getter that returns a Smi as an int and writes an int as a Smi.
+#define SMI_ACCESSORS(holder, name, offset) \
+ int holder::name() { \
+ Object* value = READ_FIELD(this, offset); \
+ return Smi::cast(value)->value(); \
+ } \
+ void holder::set_##name(int value) { \
+ WRITE_FIELD(this, offset, Smi::FromInt(value)); \
+ }
+
+
+#define BOOL_GETTER(holder, field, name, offset) \
+ bool holder::name() { \
+ return BooleanBit::get(field(), offset); \
+ } \
+
+
+#define BOOL_ACCESSORS(holder, field, name, offset) \
+ bool holder::name() { \
+ return BooleanBit::get(field(), offset); \
+ } \
+ void holder::set_##name(bool value) { \
+ set_##field(BooleanBit::set(field(), offset, value)); \
+ }
+
+
+bool Object::IsFixedArrayBase() {
+ return IsFixedArray() || IsFixedDoubleArray();
+}
+
+
+// External objects are not extensible, so the map check is enough.
+bool Object::IsExternal() {
+ return Object::IsHeapObject() &&
+ HeapObject::cast(this)->map() ==
+ HeapObject::cast(this)->GetHeap()->external_map();
+}
+
+
+bool Object::IsAccessorInfo() {
+ return IsExecutableAccessorInfo() || IsDeclaredAccessorInfo();
+}
+
+
+bool Object::IsInstanceOf(FunctionTemplateInfo* expected) {
+ // There is a constraint on the object; check.
+ if (!this->IsJSObject()) return false;
+ // Fetch the constructor function of the object.
+ Object* cons_obj = JSObject::cast(this)->map()->constructor();
+ if (!cons_obj->IsJSFunction()) return false;
+ JSFunction* fun = JSFunction::cast(cons_obj);
+ // Iterate through the chain of inheriting function templates to
+ // see if the required one occurs.
+ for (Object* type = fun->shared()->function_data();
+ type->IsFunctionTemplateInfo();
+ type = FunctionTemplateInfo::cast(type)->parent_template()) {
+ if (type == expected) return true;
+ }
+ // Didn't find the required type in the inheritance chain.
+ return false;
+}
+
+
+bool Object::IsSmi() {
+ return HAS_SMI_TAG(this);
+}
+
+
+bool Object::IsHeapObject() {
+ return Internals::HasHeapObjectTag(this);
+}
+
+
+bool Object::NonFailureIsHeapObject() {
+ ASSERT(!this->IsFailure());
+ return (reinterpret_cast<intptr_t>(this) & kSmiTagMask) != 0;
+}
+
+
+TYPE_CHECKER(HeapNumber, HEAP_NUMBER_TYPE)
+TYPE_CHECKER(Symbol, SYMBOL_TYPE)
+
+
+bool Object::IsString() {
+ return Object::IsHeapObject()
+ && HeapObject::cast(this)->map()->instance_type() < FIRST_NONSTRING_TYPE;
+}
+
+
+bool Object::IsName() {
+ return IsString() || IsSymbol();
+}
+
+
+bool Object::IsUniqueName() {
+ return IsInternalizedString() || IsSymbol();
+}
+
+
+bool Object::IsSpecObject() {
+ return Object::IsHeapObject()
+ && HeapObject::cast(this)->map()->instance_type() >= FIRST_SPEC_OBJECT_TYPE;
+}
+
+
+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::IsInternalizedString() {
+ if (!this->IsHeapObject()) return false;
+ uint32_t type = HeapObject::cast(this)->map()->instance_type();
+ STATIC_ASSERT(kNotInternalizedTag != 0);
+ return (type & (kIsNotStringMask | kIsNotInternalizedMask)) ==
+ (kStringTag | kInternalizedTag);
+}
+
+
+bool Object::IsConsString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsCons();
+}
+
+
+bool Object::IsSlicedString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsSliced();
+}
+
+
+bool Object::IsSeqString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsSequential();
+}
+
+
+bool Object::IsSeqOneByteString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsSequential() &&
+ String::cast(this)->IsOneByteRepresentation();
+}
+
+
+bool Object::IsSeqTwoByteString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsSequential() &&
+ String::cast(this)->IsTwoByteRepresentation();
+}
+
+
+bool Object::IsExternalString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsExternal();
+}
+
+
+bool Object::IsExternalAsciiString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsExternal() &&
+ String::cast(this)->IsOneByteRepresentation();
+}
+
+
+bool Object::IsExternalTwoByteString() {
+ if (!IsString()) return false;
+ return StringShape(String::cast(this)).IsExternal() &&
+ String::cast(this)->IsTwoByteRepresentation();
+}
+
+bool Object::HasValidElements() {
+ // Dictionary is covered under FixedArray.
+ return IsFixedArray() || IsFixedDoubleArray() || IsExternalArray();
+}
+
+
+MaybeObject* Object::AllocateNewStorageFor(Heap* heap,
+ Representation representation,
+ PretenureFlag tenure) {
+ if (!FLAG_track_double_fields) return this;
+ if (!representation.IsDouble()) return this;
+ if (IsUninitialized()) {
+ return heap->AllocateHeapNumber(0, tenure);
+ }
+ return heap->AllocateHeapNumber(Number(), tenure);
+}
+
+
+StringShape::StringShape(String* str)
+ : type_(str->map()->instance_type()) {
+ set_valid();
+ ASSERT((type_ & kIsNotStringMask) == kStringTag);
+}
+
+
+StringShape::StringShape(Map* map)
+ : type_(map->instance_type()) {
+ set_valid();
+ ASSERT((type_ & kIsNotStringMask) == kStringTag);
+}
+
+
+StringShape::StringShape(InstanceType t)
+ : type_(static_cast<uint32_t>(t)) {
+ set_valid();
+ ASSERT((type_ & kIsNotStringMask) == kStringTag);
+}
+
+
+bool StringShape::IsInternalized() {
+ ASSERT(valid());
+ STATIC_ASSERT(kNotInternalizedTag != 0);
+ return (type_ & (kIsNotStringMask | kIsNotInternalizedMask)) ==
+ (kStringTag | kInternalizedTag);
+}
+
+
+bool String::IsOneByteRepresentation() {
+ uint32_t type = map()->instance_type();
+ return (type & kStringEncodingMask) == kOneByteStringTag;
+}
+
+
+bool String::IsTwoByteRepresentation() {
+ uint32_t type = map()->instance_type();
+ return (type & kStringEncodingMask) == kTwoByteStringTag;
+}
+
+
+bool String::IsOneByteRepresentationUnderneath() {
+ uint32_t type = map()->instance_type();
+ STATIC_ASSERT(kIsIndirectStringTag != 0);
+ STATIC_ASSERT((kIsIndirectStringMask & kStringEncodingMask) == 0);
+ ASSERT(IsFlat());
+ switch (type & (kIsIndirectStringMask | kStringEncodingMask)) {
+ case kOneByteStringTag:
+ return true;
+ case kTwoByteStringTag:
+ return false;
+ default: // Cons or sliced string. Need to go deeper.
+ return GetUnderlying()->IsOneByteRepresentation();
+ }
+}
+
+
+bool String::IsTwoByteRepresentationUnderneath() {
+ uint32_t type = map()->instance_type();
+ STATIC_ASSERT(kIsIndirectStringTag != 0);
+ STATIC_ASSERT((kIsIndirectStringMask & kStringEncodingMask) == 0);
+ ASSERT(IsFlat());
+ switch (type & (kIsIndirectStringMask | kStringEncodingMask)) {
+ case kOneByteStringTag:
+ return false;
+ case kTwoByteStringTag:
+ return true;
+ default: // Cons or sliced string. Need to go deeper.
+ return GetUnderlying()->IsTwoByteRepresentation();
+ }
+}
+
+
+bool String::HasOnlyOneByteChars() {
+ uint32_t type = map()->instance_type();
+ return (type & kOneByteDataHintMask) == kOneByteDataHintTag ||
+ IsOneByteRepresentation();
+}
+
+
+bool StringShape::IsCons() {
+ return (type_ & kStringRepresentationMask) == kConsStringTag;
+}
+
+
+bool StringShape::IsSliced() {
+ return (type_ & kStringRepresentationMask) == kSlicedStringTag;
+}
+
+
+bool StringShape::IsIndirect() {
+ return (type_ & kIsIndirectStringMask) == kIsIndirectStringTag;
+}
+
+
+bool StringShape::IsExternal() {
+ return (type_ & kStringRepresentationMask) == kExternalStringTag;
+}
+
+
+bool StringShape::IsSequential() {
+ return (type_ & kStringRepresentationMask) == kSeqStringTag;
+}
+
+
+StringRepresentationTag StringShape::representation_tag() {
+ uint32_t tag = (type_ & kStringRepresentationMask);
+ return static_cast<StringRepresentationTag>(tag);
+}
+
+
+uint32_t StringShape::encoding_tag() {
+ return type_ & kStringEncodingMask;
+}
+
+
+uint32_t StringShape::full_representation_tag() {
+ return (type_ & (kStringRepresentationMask | kStringEncodingMask));
+}
+
+
+STATIC_CHECK((kStringRepresentationMask | kStringEncodingMask) ==
+ Internals::kFullStringRepresentationMask);
+
+STATIC_CHECK(static_cast<uint32_t>(kStringEncodingMask) ==
+ Internals::kStringEncodingMask);
+
+
+bool StringShape::IsSequentialAscii() {
+ return full_representation_tag() == (kSeqStringTag | kOneByteStringTag);
+}
+
+
+bool StringShape::IsSequentialTwoByte() {
+ return full_representation_tag() == (kSeqStringTag | kTwoByteStringTag);
+}
+
+
+bool StringShape::IsExternalAscii() {
+ return full_representation_tag() == (kExternalStringTag | kOneByteStringTag);
+}
+
+
+STATIC_CHECK((kExternalStringTag | kOneByteStringTag) ==
+ Internals::kExternalAsciiRepresentationTag);
+
+STATIC_CHECK(v8::String::ASCII_ENCODING == kOneByteStringTag);
+
+
+bool StringShape::IsExternalTwoByte() {
+ return full_representation_tag() == (kExternalStringTag | kTwoByteStringTag);
+}
+
+
+STATIC_CHECK((kExternalStringTag | kTwoByteStringTag) ==
+ Internals::kExternalTwoByteRepresentationTag);
+
+STATIC_CHECK(v8::String::TWO_BYTE_ENCODING == kTwoByteStringTag);
+
+uc32 FlatStringReader::Get(int index) {
+ ASSERT(0 <= index && index <= length_);
+ if (is_ascii_) {
+ return static_cast<const byte*>(start_)[index];
+ } else {
+ return static_cast<const uc16*>(start_)[index];
+ }
+}
+
+
+bool Object::IsNumber() {
+ return IsSmi() || IsHeapNumber();
+}
+
+
+TYPE_CHECKER(ByteArray, BYTE_ARRAY_TYPE)
+TYPE_CHECKER(FreeSpace, FREE_SPACE_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;
+ InstanceType instance_type =
+ HeapObject::cast(this)->map()->instance_type();
+ return (instance_type >= FIRST_EXTERNAL_ARRAY_TYPE &&
+ instance_type <= LAST_EXTERNAL_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() {
+ return HAS_FAILURE_TAG(this);
+}
+
+
+bool MaybeObject::IsRetryAfterGC() {
+ return HAS_FAILURE_TAG(this)
+ && Failure::cast(this)->type() == Failure::RETRY_AFTER_GC;
+}
+
+
+bool MaybeObject::IsOutOfMemory() {
+ return HAS_FAILURE_TAG(this)
+ && Failure::cast(this)->IsOutOfMemoryException();
+}
+
+
+bool MaybeObject::IsException() {
+ return this == Failure::Exception();
+}
+
+
+bool MaybeObject::IsTheHole() {
+ return !IsFailure() && ToObjectUnchecked()->IsTheHole();
+}
+
+
+bool MaybeObject::IsUninitialized() {
+ return !IsFailure() && ToObjectUnchecked()->IsUninitialized();
+}
+
+
+Failure* Failure::cast(MaybeObject* obj) {
+ ASSERT(HAS_FAILURE_TAG(obj));
+ return reinterpret_cast<Failure*>(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() {
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE);
+ return IsHeapObject() &&
+ HeapObject::cast(this)->map()->instance_type() >= FIRST_JS_OBJECT_TYPE;
+}
+
+
+bool Object::IsJSProxy() {
+ if (!Object::IsHeapObject()) return false;
+ InstanceType type = HeapObject::cast(this)->map()->instance_type();
+ return FIRST_JS_PROXY_TYPE <= type && type <= LAST_JS_PROXY_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(JSWeakSet, JS_WEAK_SET_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::IsJSWeakCollection() {
+ return IsJSWeakMap() || IsJSWeakSet();
+}
+
+
+bool Object::IsDescriptorArray() {
+ return IsFixedArray();
+}
+
+
+bool Object::IsTransitionArray() {
+ return IsFixedArray();
+}
+
+
+bool Object::IsDeoptimizationInputData() {
+ // Must be a fixed array.
+ if (!IsFixedArray()) return false;
+
+ // There's no sure way to detect the difference between a fixed array and
+ // a deoptimization data array. Since this is used for asserts we can
+ // check that the length is zero or else the fixed size plus a multiple of
+ // the entry size.
+ int length = FixedArray::cast(this)->length();
+ if (length == 0) return true;
+
+ length -= DeoptimizationInputData::kFirstDeoptEntryIndex;
+ return length >= 0 &&
+ length % DeoptimizationInputData::kDeoptEntrySize == 0;
+}
+
+
+bool Object::IsDeoptimizationOutputData() {
+ if (!IsFixedArray()) return false;
+ // There's actually no way to see the difference between a fixed array and
+ // a deoptimization data 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::IsDependentCode() {
+ if (!IsFixedArray()) return false;
+ // There's actually no way to see the difference between a fixed array and
+ // a dependent codes array.
+ return true;
+}
+
+
+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()) return false;
+ Map* map = HeapObject::cast(this)->map();
+ Heap* heap = map->GetHeap();
+ return (map == heap->function_context_map() ||
+ map == heap->catch_context_map() ||
+ map == heap->with_context_map() ||
+ map == heap->native_context_map() ||
+ map == heap->block_context_map() ||
+ map == heap->module_context_map() ||
+ map == heap->global_context_map());
+}
+
+
+bool Object::IsNativeContext() {
+ return Object::IsHeapObject() &&
+ HeapObject::cast(this)->map() ==
+ HeapObject::cast(this)->GetHeap()->native_context_map();
+}
+
+
+bool Object::IsScopeInfo() {
+ return Object::IsHeapObject() &&
+ HeapObject::cast(this)->map() ==
+ HeapObject::cast(this)->GetHeap()->scope_info_map();
+}
+
+
+TYPE_CHECKER(JSFunction, JS_FUNCTION_TYPE)
+
+
+template <> inline bool Is<JSFunction>(Object* obj) {
+ return obj->IsJSFunction();
+}
+
+
+TYPE_CHECKER(Code, CODE_TYPE)
+TYPE_CHECKER(Oddball, ODDBALL_TYPE)
+TYPE_CHECKER(Cell, CELL_TYPE)
+TYPE_CHECKER(PropertyCell, PROPERTY_CELL_TYPE)
+TYPE_CHECKER(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE)
+TYPE_CHECKER(JSGeneratorObject, JS_GENERATOR_OBJECT_TYPE)
+TYPE_CHECKER(JSModule, JS_MODULE_TYPE)
+TYPE_CHECKER(JSValue, JS_VALUE_TYPE)
+TYPE_CHECKER(JSDate, JS_DATE_TYPE)
+TYPE_CHECKER(JSMessageObject, JS_MESSAGE_OBJECT_TYPE)
+
+
+bool Object::IsStringWrapper() {
+ return IsJSValue() && JSValue::cast(this)->value()->IsString();
+}
+
+
+TYPE_CHECKER(Foreign, FOREIGN_TYPE)
+
+
+bool Object::IsBoolean() {
+ return IsOddball() &&
+ ((Oddball::cast(this)->kind() & Oddball::kNotBooleanMask) == 0);
+}
+
+
+TYPE_CHECKER(JSArray, JS_ARRAY_TYPE)
+TYPE_CHECKER(JSArrayBuffer, JS_ARRAY_BUFFER_TYPE)
+TYPE_CHECKER(JSTypedArray, JS_TYPED_ARRAY_TYPE)
+TYPE_CHECKER(JSDataView, JS_DATA_VIEW_TYPE)
+
+
+bool Object::IsJSArrayBufferView() {
+ return IsJSDataView() || IsJSTypedArray();
+}
+
+
+TYPE_CHECKER(JSRegExp, JS_REGEXP_TYPE)
+
+
+template <> inline bool Is<JSArray>(Object* obj) {
+ return obj->IsJSArray();
+}
+
+
+bool Object::IsHashTable() {
+ return Object::IsHeapObject() &&
+ HeapObject::cast(this)->map() ==
+ HeapObject::cast(this)->GetHeap()->hash_table_map();
+}
+
+
+bool Object::IsDictionary() {
+ return IsHashTable() &&
+ this != HeapObject::cast(this)->GetHeap()->string_table();
+}
+
+
+bool Object::IsStringTable() {
+ return IsHashTable() &&
+ this == HeapObject::cast(this)->GetHeap()->raw_unchecked_string_table();
+}
+
+
+bool Object::IsJSFunctionResultCache() {
+ if (!IsFixedArray()) return false;
+ FixedArray* self = FixedArray::cast(this);
+ int length = self->length();
+ if (length < JSFunctionResultCache::kEntriesIndex) return false;
+ if ((length - JSFunctionResultCache::kEntriesIndex)
+ % JSFunctionResultCache::kEntrySize != 0) {
+ return false;
+ }
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ reinterpret_cast<JSFunctionResultCache*>(this)->
+ JSFunctionResultCacheVerify();
+ }
+#endif
+ return true;
+}
+
+
+bool Object::IsNormalizedMapCache() {
+ if (!IsFixedArray()) return false;
+ if (FixedArray::cast(this)->length() != NormalizedMapCache::kEntries) {
+ return false;
+ }
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ reinterpret_cast<NormalizedMapCache*>(this)->NormalizedMapCacheVerify();
+ }
+#endif
+ return true;
+}
+
+
+bool Object::IsCompilationCacheTable() {
+ return IsHashTable();
+}
+
+
+bool Object::IsCodeCacheHashTable() {
+ return IsHashTable();
+}
+
+
+bool Object::IsPolymorphicCodeCacheHashTable() {
+ return IsHashTable();
+}
+
+
+bool Object::IsMapCache() {
+ return IsHashTable();
+}
+
+
+bool Object::IsObjectHashTable() {
+ return IsHashTable();
+}
+
+
+bool Object::IsPrimitive() {
+ return IsOddball() || IsNumber() || IsString();
+}
+
+
+bool Object::IsJSGlobalProxy() {
+ bool result = IsHeapObject() &&
+ (HeapObject::cast(this)->map()->instance_type() ==
+ JS_GLOBAL_PROXY_TYPE);
+ ASSERT(!result || IsAccessCheckNeeded());
+ return result;
+}
+
+
+bool Object::IsGlobalObject() {
+ if (!IsHeapObject()) return false;
+
+ InstanceType type = HeapObject::cast(this)->map()->instance_type();
+ return type == JS_GLOBAL_OBJECT_TYPE ||
+ type == JS_BUILTINS_OBJECT_TYPE;
+}
+
+
+TYPE_CHECKER(JSGlobalObject, JS_GLOBAL_OBJECT_TYPE)
+TYPE_CHECKER(JSBuiltinsObject, JS_BUILTINS_OBJECT_TYPE)
+
+
+bool Object::IsUndetectableObject() {
+ return IsHeapObject()
+ && HeapObject::cast(this)->map()->is_undetectable();
+}
+
+
+bool Object::IsAccessCheckNeeded() {
+ return IsHeapObject()
+ && HeapObject::cast(this)->map()->is_access_check_needed();
+}
+
+
+bool Object::IsStruct() {
+ if (!IsHeapObject()) return false;
+ switch (HeapObject::cast(this)->map()->instance_type()) {
+#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return true;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ default: return false;
+ }
+}
+
+
+#define MAKE_STRUCT_PREDICATE(NAME, Name, name) \
+ bool Object::Is##Name() { \
+ return Object::IsHeapObject() \
+ && HeapObject::cast(this)->map()->instance_type() == NAME##_TYPE; \
+ }
+ STRUCT_LIST(MAKE_STRUCT_PREDICATE)
+#undef MAKE_STRUCT_PREDICATE
+
+
+bool Object::IsUndefined() {
+ return IsOddball() && Oddball::cast(this)->kind() == Oddball::kUndefined;
+}
+
+
+bool Object::IsNull() {
+ return IsOddball() && Oddball::cast(this)->kind() == Oddball::kNull;
+}
+
+
+bool Object::IsTheHole() {
+ return IsOddball() && Oddball::cast(this)->kind() == Oddball::kTheHole;
+}
+
+
+bool Object::IsUninitialized() {
+ return IsOddball() && Oddball::cast(this)->kind() == Oddball::kUninitialized;
+}
+
+
+bool Object::IsTrue() {
+ return IsOddball() && Oddball::cast(this)->kind() == Oddball::kTrue;
+}
+
+
+bool Object::IsFalse() {
+ return IsOddball() && Oddball::cast(this)->kind() == Oddball::kFalse;
+}
+
+
+bool Object::IsArgumentsMarker() {
+ return IsOddball() && Oddball::cast(this)->kind() == Oddball::kArgumentMarker;
+}
+
+
+double Object::Number() {
+ ASSERT(IsNumber());
+ return IsSmi()
+ ? static_cast<double>(reinterpret_cast<Smi*>(this)->value())
+ : reinterpret_cast<HeapNumber*>(this)->value();
+}
+
+
+bool Object::IsNaN() {
+ return this->IsHeapNumber() && std::isnan(HeapNumber::cast(this)->value());
+}
+
+
+MaybeObject* Object::ToSmi() {
+ if (IsSmi()) return this;
+ if (IsHeapNumber()) {
+ double value = HeapNumber::cast(this)->value();
+ int int_value = FastD2I(value);
+ if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
+ return Smi::FromInt(int_value);
+ }
+ }
+ return Failure::Exception();
+}
+
+
+bool Object::HasSpecificClassOf(String* name) {
+ return this->IsJSObject() && (JSObject::cast(this)->class_name() == name);
+}
+
+
+MaybeObject* Object::GetElement(uint32_t index) {
+ // GetElement can trigger a getter which can cause allocation.
+ // This was not always the case. This ASSERT is here to catch
+ // leftover incorrect uses.
+ ASSERT(AllowHeapAllocation::IsAllowed());
+ return GetElementWithReceiver(this, index);
+}
+
+
+Object* Object::GetElementNoExceptionThrown(uint32_t index) {
+ MaybeObject* maybe = GetElementWithReceiver(this, index);
+ ASSERT(!maybe->IsFailure());
+ Object* result = NULL; // Initialization to please compiler.
+ maybe->ToObject(&result);
+ return result;
+}
+
+
+MaybeObject* Object::GetProperty(Name* key) {
+ PropertyAttributes attributes;
+ return GetPropertyWithReceiver(this, key, &attributes);
+}
+
+
+MaybeObject* Object::GetProperty(Name* key, PropertyAttributes* attributes) {
+ return GetPropertyWithReceiver(this, key, attributes);
+}
+
+
+#define FIELD_ADDR(p, offset) \
+ (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
+
+#define READ_FIELD(p, offset) \
+ (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)))
+
+#define WRITE_FIELD(p, offset, value) \
+ (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
+
+#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
+ #define READ_DOUBLE_FIELD(p, offset) \
+ (*reinterpret_cast<double*>(FIELD_ADDR(p, offset)))
+#else // V8_TARGET_ARCH_MIPS
+ // Prevent gcc from using load-double (mips ldc1) on (possibly)
+ // non-64-bit aligned HeapNumber::value.
+ static inline double read_double_field(void* p, int offset) {
+ union conversion {
+ double d;
+ uint32_t u[2];
+ } c;
+ c.u[0] = (*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset)));
+ c.u[1] = (*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset + 4)));
+ return c.d;
+ }
+ #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)
+#else // V8_TARGET_ARCH_MIPS
+ // Prevent gcc from using store-double (mips sdc1) on (possibly)
+ // non-64-bit aligned HeapNumber::value.
+ static inline void write_double_field(void* p, int offset,
+ double value) {
+ union conversion {
+ double d;
+ uint32_t u[2];
+ } c;
+ c.d = value;
+ (*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset))) = c.u[0];
+ (*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset + 4))) = c.u[1];
+ }
+ #define WRITE_DOUBLE_FIELD(p, offset, value) \
+ write_double_field(p, offset, value)
+#endif // V8_TARGET_ARCH_MIPS
+
+
+#define READ_INT_FIELD(p, offset) \
+ (*reinterpret_cast<int*>(FIELD_ADDR(p, offset)))
+
+#define WRITE_INT_FIELD(p, offset, value) \
+ (*reinterpret_cast<int*>(FIELD_ADDR(p, offset)) = value)
+
+#define READ_INTPTR_FIELD(p, offset) \
+ (*reinterpret_cast<intptr_t*>(FIELD_ADDR(p, offset)))
+
+#define WRITE_INTPTR_FIELD(p, offset, value) \
+ (*reinterpret_cast<intptr_t*>(FIELD_ADDR(p, offset)) = value)
+
+#define READ_UINT32_FIELD(p, offset) \
+ (*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset)))
+
+#define WRITE_UINT32_FIELD(p, offset, value) \
+ (*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset)) = value)
+
+#define READ_INT64_FIELD(p, offset) \
+ (*reinterpret_cast<int64_t*>(FIELD_ADDR(p, offset)))
+
+#define WRITE_INT64_FIELD(p, offset, value) \
+ (*reinterpret_cast<int64_t*>(FIELD_ADDR(p, offset)) = value)
+
+#define READ_SHORT_FIELD(p, offset) \
+ (*reinterpret_cast<uint16_t*>(FIELD_ADDR(p, offset)))
+
+#define WRITE_SHORT_FIELD(p, offset, value) \
+ (*reinterpret_cast<uint16_t*>(FIELD_ADDR(p, offset)) = value)
+
+#define READ_BYTE_FIELD(p, offset) \
+ (*reinterpret_cast<byte*>(FIELD_ADDR(p, offset)))
+
+#define WRITE_BYTE_FIELD(p, offset, value) \
+ (*reinterpret_cast<byte*>(FIELD_ADDR(p, offset)) = value)
+
+
+Object** HeapObject::RawField(HeapObject* obj, int byte_offset) {
+ return &READ_FIELD(obj, byte_offset);
+}
+
+
+int Smi::value() {
+ return Internals::SmiValue(this);
+}
+
+
+Smi* Smi::FromInt(int value) {
+ ASSERT(Smi::IsValid(value));
+ return reinterpret_cast<Smi*>(Internals::IntToSmi(value));
+}
+
+
+Smi* Smi::FromIntptr(intptr_t value) {
+ ASSERT(Smi::IsValid(value));
+ int smi_shift_bits = kSmiTagSize + kSmiShiftSize;
+ return reinterpret_cast<Smi*>((value << smi_shift_bits) | kSmiTag);
+}
+
+
+Failure::Type Failure::type() const {
+ return static_cast<Type>(value() & kFailureTypeTagMask);
+}
+
+
+bool Failure::IsInternalError() const {
+ return type() == INTERNAL_ERROR;
+}
+
+
+bool Failure::IsOutOfMemoryException() const {
+ return type() == OUT_OF_MEMORY_EXCEPTION;
+}
+
+
+AllocationSpace Failure::allocation_space() const {
+ ASSERT_EQ(RETRY_AFTER_GC, type());
+ return static_cast<AllocationSpace>((value() >> kFailureTypeTagSize)
+ & kSpaceTagMask);
+}
+
+
+Failure* Failure::InternalError() {
+ return Construct(INTERNAL_ERROR);
+}
+
+
+Failure* Failure::Exception() {
+ return Construct(EXCEPTION);
+}
+
+
+Failure* Failure::OutOfMemoryException(intptr_t value) {
+ return Construct(OUT_OF_MEMORY_EXCEPTION, value);
+}
+
+
+intptr_t Failure::value() const {
+ return static_cast<intptr_t>(
+ reinterpret_cast<uintptr_t>(this) >> kFailureTagSize);
+}
+
+
+Failure* Failure::RetryAfterGC() {
+ return RetryAfterGC(NEW_SPACE);
+}
+
+
+Failure* Failure::RetryAfterGC(AllocationSpace space) {
+ ASSERT((space & ~kSpaceTagMask) == 0);
+ return Construct(RETRY_AFTER_GC, space);
+}
+
+
+Failure* Failure::Construct(Type type, intptr_t value) {
+ uintptr_t info =
+ (static_cast<uintptr_t>(value) << kFailureTypeTagSize) | type;
+ ASSERT(((info << kFailureTagSize) >> kFailureTagSize) == info);
+ // Fill the unused bits with a pattern that's easy to recognize in crash
+ // dumps.
+ static const int kFailureMagicPattern = 0x0BAD0000;
+ return reinterpret_cast<Failure*>(
+ (info << kFailureTagSize) | kFailureTag | kFailureMagicPattern);
+}
+
+
+bool Smi::IsValid(intptr_t value) {
+ bool result = Internals::IsValidSmi(value);
+ ASSERT_EQ(result, value >= kMinValue && value <= kMaxValue);
+ return result;
+}
+
+
+MapWord MapWord::FromMap(Map* map) {
+ return MapWord(reinterpret_cast<uintptr_t>(map));
+}
+
+
+Map* MapWord::ToMap() {
+ return reinterpret_cast<Map*>(value_);
+}
+
+
+bool MapWord::IsForwardingAddress() {
+ return HAS_SMI_TAG(reinterpret_cast<Object*>(value_));
+}
+
+
+MapWord MapWord::FromForwardingAddress(HeapObject* object) {
+ Address raw = reinterpret_cast<Address>(object) - kHeapObjectTag;
+ return MapWord(reinterpret_cast<uintptr_t>(raw));
+}
+
+
+HeapObject* MapWord::ToForwardingAddress() {
+ ASSERT(IsForwardingAddress());
+ return HeapObject::FromAddress(reinterpret_cast<Address>(value_));
+}
+
+
+#ifdef VERIFY_HEAP
+void HeapObject::VerifyObjectField(int offset) {
+ VerifyPointer(READ_FIELD(this, offset));
+}
+
+void HeapObject::VerifySmiField(int offset) {
+ CHECK(READ_FIELD(this, offset)->IsSmi());
+}
+#endif
+
+
+Heap* HeapObject::GetHeap() {
+ Heap* heap =
+ MemoryChunk::FromAddress(reinterpret_cast<Address>(this))->heap();
+ ASSERT(heap != NULL);
+ ASSERT(heap->isolate() == Isolate::Current());
+ return heap;
+}
+
+
+Isolate* HeapObject::GetIsolate() {
+ return GetHeap()->isolate();
+}
+
+
+Map* HeapObject::map() {
+ return map_word().ToMap();
+}
+
+
+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));
+}
+
+
+MapWord HeapObject::map_word() {
+ return MapWord(reinterpret_cast<uintptr_t>(READ_FIELD(this, kMapOffset)));
+}
+
+
+void HeapObject::set_map_word(MapWord map_word) {
+ // WRITE_FIELD does not invoke write barrier, but there is no need
+ // here.
+ WRITE_FIELD(this, kMapOffset, reinterpret_cast<Object*>(map_word.value_));
+}
+
+
+HeapObject* HeapObject::FromAddress(Address address) {
+ ASSERT_TAG_ALIGNED(address);
+ return reinterpret_cast<HeapObject*>(address + kHeapObjectTag);
+}
+
+
+Address HeapObject::address() {
+ return reinterpret_cast<Address>(this) - kHeapObjectTag;
+}
+
+
+int HeapObject::Size() {
+ return SizeFromMap(map());
+}
+
+
+void HeapObject::IteratePointers(ObjectVisitor* v, int start, int end) {
+ v->VisitPointers(reinterpret_cast<Object**>(FIELD_ADDR(this, start)),
+ reinterpret_cast<Object**>(FIELD_ADDR(this, end)));
+}
+
+
+void HeapObject::IteratePointer(ObjectVisitor* v, int offset) {
+ v->VisitPointer(reinterpret_cast<Object**>(FIELD_ADDR(this, offset)));
+}
+
+
+double HeapNumber::value() {
+ return READ_DOUBLE_FIELD(this, kValueOffset);
+}
+
+
+void HeapNumber::set_value(double value) {
+ WRITE_DOUBLE_FIELD(this, kValueOffset, value);
+}
+
+
+int HeapNumber::get_exponent() {
+ return ((READ_INT_FIELD(this, kExponentOffset) & kExponentMask) >>
+ kExponentShift) - kExponentBias;
+}
+
+
+int HeapNumber::get_sign() {
+ return READ_INT_FIELD(this, kExponentOffset) & kSignMask;
+}
+
+
+ACCESSORS(JSObject, properties, FixedArray, kPropertiesOffset)
+
+
+Object** FixedArray::GetFirstElementAddress() {
+ return reinterpret_cast<Object**>(FIELD_ADDR(this, OffsetOfElementAt(0)));
+}
+
+
+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;
+}
+
+
+FixedArrayBase* JSObject::elements() {
+ Object* array = READ_FIELD(this, kElementsOffset);
+ return static_cast<FixedArrayBase*>(array);
+}
+
+
+void JSObject::ValidateElements() {
+#if DEBUG
+ if (FLAG_enable_slow_asserts) {
+ ElementsAccessor* accessor = GetElementsAccessor();
+ accessor->Validate(this);
+ }
+#endif
+}
+
+
+bool JSObject::ShouldTrackAllocationInfo() {
+ if (map()->CanTrackAllocationSite()) {
+ if (!IsJSArray()) {
+ return true;
+ }
+
+ return AllocationSite::GetMode(GetElementsKind()) ==
+ TRACK_ALLOCATION_SITE;
+ }
+ return false;
+}
+
+
+// Heuristic: We only need to create allocation site info if the boilerplate
+// elements kind is the initial elements kind.
+AllocationSiteMode AllocationSite::GetMode(
+ ElementsKind boilerplate_elements_kind) {
+ if (FLAG_track_allocation_sites &&
+ IsFastSmiElementsKind(boilerplate_elements_kind)) {
+ return TRACK_ALLOCATION_SITE;
+ }
+
+ return DONT_TRACK_ALLOCATION_SITE;
+}
+
+
+AllocationSiteMode AllocationSite::GetMode(ElementsKind from,
+ ElementsKind to) {
+ if (FLAG_track_allocation_sites &&
+ IsFastSmiElementsKind(from) &&
+ IsMoreGeneralElementsKindTransition(from, to)) {
+ return TRACK_ALLOCATION_SITE;
+ }
+
+ return DONT_TRACK_ALLOCATION_SITE;
+}
+
+
+MaybeObject* JSObject::EnsureCanContainHeapObjectElements() {
+ ValidateElements();
+ ElementsKind elements_kind = map()->elements_kind();
+ if (!IsFastObjectElementsKind(elements_kind)) {
+ if (IsFastHoleyElementsKind(elements_kind)) {
+ return TransitionElementsKind(FAST_HOLEY_ELEMENTS);
+ } else {
+ return TransitionElementsKind(FAST_ELEMENTS);
+ }
+ }
+ return this;
+}
+
+
+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);
+ bool is_holey = IsFastHoleyElementsKind(current_kind);
+ if (current_kind == FAST_HOLEY_ELEMENTS) return this;
+ Heap* heap = GetHeap();
+ Object* the_hole = heap->the_hole_value();
+ for (uint32_t i = 0; i < count; ++i) {
+ Object* current = *objects++;
+ if (current == the_hole) {
+ is_holey = true;
+ target_kind = GetHoleyElementsKind(target_kind);
+ } else if (!current->IsSmi()) {
+ if (mode == ALLOW_CONVERTED_DOUBLE_ELEMENTS && current->IsNumber()) {
+ if (IsFastSmiElementsKind(target_kind)) {
+ if (is_holey) {
+ target_kind = FAST_HOLEY_DOUBLE_ELEMENTS;
+ } else {
+ target_kind = FAST_DOUBLE_ELEMENTS;
+ }
+ }
+ } else if (is_holey) {
+ target_kind = FAST_HOLEY_ELEMENTS;
+ break;
+ } else {
+ target_kind = FAST_ELEMENTS;
+ }
+ }
+ }
+
+ if (target_kind != current_kind) {
+ return TransitionElementsKind(target_kind);
+ }
+ return this;
+}
+
+
+MaybeObject* JSObject::EnsureCanContainElements(FixedArrayBase* elements,
+ uint32_t length,
+ 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, length, mode);
+ }
+
+ ASSERT(mode == ALLOW_COPIED_DOUBLE_ELEMENTS);
+ if (GetElementsKind() == FAST_HOLEY_SMI_ELEMENTS) {
+ return TransitionElementsKind(FAST_HOLEY_DOUBLE_ELEMENTS);
+ } else if (GetElementsKind() == FAST_SMI_ELEMENTS) {
+ FixedDoubleArray* double_array = FixedDoubleArray::cast(elements);
+ for (uint32_t i = 0; i < length; ++i) {
+ if (double_array->is_the_hole(i)) {
+ return TransitionElementsKind(FAST_HOLEY_DOUBLE_ELEMENTS);
+ }
+ }
+ return TransitionElementsKind(FAST_DOUBLE_ELEMENTS);
+ }
+
+ return this;
+}
+
+
+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;
+
+ Context* native_context = isolate->context()->native_context();
+ Object* maybe_array_maps = native_context->js_array_maps();
+ if (maybe_array_maps->IsFixedArray()) {
+ FixedArray* array_maps = FixedArray::cast(maybe_array_maps);
+ if (array_maps->get(from_kind) == current_map) {
+ Object* maybe_transitioned_map = array_maps->get(to_kind);
+ if (maybe_transitioned_map->IsMap()) {
+ return Map::cast(maybe_transitioned_map);
+ }
+ }
+ }
+
+ return GetElementsTransitionMapSlow(to_kind);
+}
+
+
+void JSObject::set_map_and_elements(Map* new_map,
+ FixedArrayBase* value,
+ WriteBarrierMode mode) {
+ ASSERT(value->HasValidElements());
+ 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_smi_or_object_elements() ||
+ (value == GetHeap()->empty_fixed_array())) ==
+ (value->map() == GetHeap()->fixed_array_map() ||
+ value->map() == GetHeap()->fixed_cow_array_map()));
+ ASSERT((value == GetHeap()->empty_fixed_array()) ||
+ (map()->has_fast_double_elements() == value->IsFixedDoubleArray()));
+ WRITE_FIELD(this, kElementsOffset, value);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kElementsOffset, value, mode);
+}
+
+
+void JSObject::set_elements(FixedArrayBase* value, WriteBarrierMode mode) {
+ set_map_and_elements(NULL, value, mode);
+}
+
+
+void JSObject::initialize_properties() {
+ ASSERT(!GetHeap()->InNewSpace(GetHeap()->empty_fixed_array()));
+ WRITE_FIELD(this, kPropertiesOffset, GetHeap()->empty_fixed_array());
+}
+
+
+void JSObject::initialize_elements() {
+ if (map()->has_fast_smi_or_object_elements() ||
+ map()->has_fast_double_elements()) {
+ ASSERT(!GetHeap()->InNewSpace(GetHeap()->empty_fixed_array()));
+ WRITE_FIELD(this, kElementsOffset, GetHeap()->empty_fixed_array());
+ } else if (map()->has_external_array_elements()) {
+ ExternalArray* empty_array = GetHeap()->EmptyExternalArrayForMap(map());
+ ASSERT(!GetHeap()->InNewSpace(empty_array));
+ WRITE_FIELD(this, kElementsOffset, empty_array);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+MaybeObject* JSObject::ResetElements() {
+ if (map()->is_observed()) {
+ // Maintain invariant that observed elements are always in dictionary mode.
+ SeededNumberDictionary* dictionary;
+ MaybeObject* maybe = SeededNumberDictionary::Allocate(GetHeap(), 0);
+ if (!maybe->To(&dictionary)) return maybe;
+ if (map() == GetHeap()->non_strict_arguments_elements_map()) {
+ FixedArray::cast(elements())->set(1, dictionary);
+ } else {
+ set_elements(dictionary);
+ }
+ return this;
+ }
+
+ ElementsKind elements_kind = GetInitialFastElementsKind();
+ if (!FLAG_smi_only_arrays) {
+ elements_kind = FastSmiToObjectElementsKind(elements_kind);
+ }
+ MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(), elements_kind);
+ Map* map;
+ if (!maybe->To(&map)) return maybe;
+ set_map(map);
+ initialize_elements();
+
+ return this;
+}
+
+
+MaybeObject* JSObject::AllocateStorageForMap(Map* map) {
+ ASSERT(this->map()->inobject_properties() == map->inobject_properties());
+ ElementsKind obj_kind = this->map()->elements_kind();
+ ElementsKind map_kind = map->elements_kind();
+ if (map_kind != obj_kind) {
+ ElementsKind to_kind = map_kind;
+ if (IsMoreGeneralElementsKindTransition(map_kind, obj_kind) ||
+ IsDictionaryElementsKind(obj_kind)) {
+ to_kind = obj_kind;
+ }
+ MaybeObject* maybe_obj =
+ IsDictionaryElementsKind(to_kind) ? NormalizeElements()
+ : TransitionElementsKind(to_kind);
+ if (maybe_obj->IsFailure()) return maybe_obj;
+ MaybeObject* maybe_map = map->AsElementsKind(to_kind);
+ if (!maybe_map->To(&map)) return maybe_map;
+ }
+ int total_size =
+ map->NumberOfOwnDescriptors() + map->unused_property_fields();
+ int out_of_object = total_size - map->inobject_properties();
+ if (out_of_object != properties()->length()) {
+ FixedArray* new_properties;
+ MaybeObject* maybe_properties = properties()->CopySize(out_of_object);
+ if (!maybe_properties->To(&new_properties)) return maybe_properties;
+ set_properties(new_properties);
+ }
+ set_map(map);
+ return this;
+}
+
+
+MaybeObject* JSObject::MigrateInstance() {
+ // Converting any field to the most specific type will cause the
+ // GeneralizeFieldRepresentation algorithm to create the most general existing
+ // transition that matches the object. This achieves what is needed.
+ return GeneralizeFieldRepresentation(0, Representation::None());
+}
+
+
+MaybeObject* JSObject::TryMigrateInstance() {
+ Map* new_map = map()->CurrentMapForDeprecated();
+ if (new_map == NULL) return Smi::FromInt(0);
+ return MigrateToMap(new_map);
+}
+
+
+Handle<String> JSObject::ExpectedTransitionKey(Handle<Map> map) {
+ DisallowHeapAllocation no_gc;
+ if (!map->HasTransitionArray()) return Handle<String>::null();
+ TransitionArray* transitions = map->transitions();
+ if (!transitions->IsSimpleTransition()) return Handle<String>::null();
+ int transition = TransitionArray::kSimpleTransitionIndex;
+ PropertyDetails details = transitions->GetTargetDetails(transition);
+ Name* name = transitions->GetKey(transition);
+ if (details.type() != FIELD) return Handle<String>::null();
+ if (details.attributes() != NONE) return Handle<String>::null();
+ if (!name->IsString()) return Handle<String>::null();
+ return Handle<String>(String::cast(name));
+}
+
+
+Handle<Map> JSObject::ExpectedTransitionTarget(Handle<Map> map) {
+ ASSERT(!ExpectedTransitionKey(map).is_null());
+ return Handle<Map>(map->transitions()->GetTarget(
+ TransitionArray::kSimpleTransitionIndex));
+}
+
+
+Handle<Map> JSObject::FindTransitionToField(Handle<Map> map, Handle<Name> key) {
+ DisallowHeapAllocation no_allocation;
+ if (!map->HasTransitionArray()) return Handle<Map>::null();
+ TransitionArray* transitions = map->transitions();
+ int transition = transitions->Search(*key);
+ if (transition == TransitionArray::kNotFound) return Handle<Map>::null();
+ PropertyDetails target_details = transitions->GetTargetDetails(transition);
+ if (target_details.type() != FIELD) return Handle<Map>::null();
+ if (target_details.attributes() != NONE) return Handle<Map>::null();
+ return Handle<Map>(transitions->GetTarget(transition));
+}
+
+
+int JSObject::LastAddedFieldIndex() {
+ Map* map = this->map();
+ int last_added = map->LastAdded();
+ return map->instance_descriptors()->GetFieldIndex(last_added);
+}
+
+
+ACCESSORS(Oddball, to_string, String, kToStringOffset)
+ACCESSORS(Oddball, to_number, Object, kToNumberOffset)
+
+
+byte Oddball::kind() {
+ return Smi::cast(READ_FIELD(this, kKindOffset))->value();
+}
+
+
+void Oddball::set_kind(byte value) {
+ WRITE_FIELD(this, kKindOffset, Smi::FromInt(value));
+}
+
+
+Object* Cell::value() {
+ return READ_FIELD(this, kValueOffset);
+}
+
+
+void Cell::set_value(Object* val, WriteBarrierMode ignored) {
+ // The write barrier is not used for global property cells.
+ ASSERT(!val->IsPropertyCell() && !val->IsCell());
+ WRITE_FIELD(this, kValueOffset, val);
+}
+
+ACCESSORS(PropertyCell, dependent_code, DependentCode, kDependentCodeOffset)
+
+Object* PropertyCell::type_raw() {
+ return READ_FIELD(this, kTypeOffset);
+}
+
+
+void PropertyCell::set_type_raw(Object* val, WriteBarrierMode ignored) {
+ WRITE_FIELD(this, kTypeOffset, val);
+}
+
+
+int JSObject::GetHeaderSize() {
+ InstanceType type = map()->instance_type();
+ // Check for the most common kind of JavaScript object before
+ // falling into the generic switch. This speeds up the internal
+ // field operations considerably on average.
+ if (type == JS_OBJECT_TYPE) return JSObject::kHeaderSize;
+ switch (type) {
+ case JS_GENERATOR_OBJECT_TYPE:
+ return JSGeneratorObject::kSize;
+ case JS_MODULE_TYPE:
+ return JSModule::kSize;
+ case JS_GLOBAL_PROXY_TYPE:
+ return JSGlobalProxy::kSize;
+ case JS_GLOBAL_OBJECT_TYPE:
+ return JSGlobalObject::kSize;
+ case JS_BUILTINS_OBJECT_TYPE:
+ return JSBuiltinsObject::kSize;
+ case JS_FUNCTION_TYPE:
+ return JSFunction::kSize;
+ case JS_VALUE_TYPE:
+ return JSValue::kSize;
+ case JS_DATE_TYPE:
+ return JSDate::kSize;
+ case JS_ARRAY_TYPE:
+ return JSArray::kSize;
+ case JS_ARRAY_BUFFER_TYPE:
+ return JSArrayBuffer::kSize;
+ case JS_TYPED_ARRAY_TYPE:
+ return JSTypedArray::kSize;
+ case JS_DATA_VIEW_TYPE:
+ return JSDataView::kSize;
+ case JS_SET_TYPE:
+ return JSSet::kSize;
+ case JS_MAP_TYPE:
+ return JSMap::kSize;
+ case JS_WEAK_MAP_TYPE:
+ return JSWeakMap::kSize;
+ case JS_WEAK_SET_TYPE:
+ return JSWeakSet::kSize;
+ case JS_REGEXP_TYPE:
+ return JSRegExp::kSize;
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
+ return JSObject::kHeaderSize;
+ case JS_MESSAGE_OBJECT_TYPE:
+ return JSMessageObject::kSize;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+}
+
+
+int JSObject::GetInternalFieldCount() {
+ ASSERT(1 << kPointerSizeLog2 == kPointerSize);
+ // Make sure to adjust for the number of in-object properties. These
+ // properties do contribute to the size, but are not internal fields.
+ return ((Size() - GetHeaderSize()) >> kPointerSizeLog2) -
+ map()->inobject_properties();
+}
+
+
+int JSObject::GetInternalFieldOffset(int index) {
+ ASSERT(index < GetInternalFieldCount() && index >= 0);
+ return GetHeaderSize() + (kPointerSize * index);
+}
+
+
+Object* JSObject::GetInternalField(int index) {
+ 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.
+ return READ_FIELD(this, GetHeaderSize() + (kPointerSize * index));
+}
+
+
+void JSObject::SetInternalField(int index, Object* 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);
+ 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);
+}
+
+
+MaybeObject* JSObject::FastPropertyAt(Representation representation,
+ int index) {
+ Object* raw_value = RawFastPropertyAt(index);
+ return raw_value->AllocateNewStorageFor(GetHeap(), representation);
+}
+
+
+// Access fast-case object properties at index. The use of these routines
+// is needed to correctly distinguish between properties stored in-object and
+// properties stored in the properties array.
+Object* JSObject::RawFastPropertyAt(int index) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ if (index < 0) {
+ int offset = map()->instance_size() + (index * kPointerSize);
+ return READ_FIELD(this, offset);
+ } else {
+ ASSERT(index < properties()->length());
+ return properties()->get(index);
+ }
+}
+
+
+void JSObject::FastPropertyAtPut(int index, Object* value) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ if (index < 0) {
+ int offset = map()->instance_size() + (index * kPointerSize);
+ WRITE_FIELD(this, offset, value);
+ WRITE_BARRIER(GetHeap(), this, offset, value);
+ } else {
+ ASSERT(index < properties()->length());
+ properties()->set(index, value);
+ }
+}
+
+
+int JSObject::GetInObjectPropertyOffset(int index) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ ASSERT(index < 0);
+ return map()->instance_size() + (index * kPointerSize);
+}
+
+
+Object* JSObject::InObjectPropertyAt(int index) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ ASSERT(index < 0);
+ int offset = map()->instance_size() + (index * kPointerSize);
+ return READ_FIELD(this, offset);
+}
+
+
+Object* JSObject::InObjectPropertyAtPut(int index,
+ Object* value,
+ WriteBarrierMode mode) {
+ // Adjust for the number of properties stored in the object.
+ index -= map()->inobject_properties();
+ ASSERT(index < 0);
+ int offset = map()->instance_size() + (index * kPointerSize);
+ WRITE_FIELD(this, offset, value);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode);
+ return 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;
+ }
+}
+
+
+bool JSObject::HasFastProperties() {
+ ASSERT(properties()->IsDictionary() == map()->is_dictionary_map());
+ return !properties()->IsDictionary();
+}
+
+
+bool JSObject::TooManyFastProperties(int properties,
+ JSObject::StoreFromKeyed store_mode) {
+ // Allow extra fast properties if the object has more than
+ // kFastPropertiesSoftLimit in-object properties. When this is the case,
+ // it is very unlikely that the object is being used as a dictionary
+ // and there is a good chance that allowing more map transitions
+ // will be worth it.
+ int inobject = map()->inobject_properties();
+
+ int limit;
+ if (store_mode == CERTAINLY_NOT_STORE_FROM_KEYED) {
+ limit = Max(inobject, kMaxFastProperties);
+ } else {
+ limit = Max(inobject, kFastPropertiesSoftLimit);
+ }
+ return properties > limit;
+}
+
+
+void Struct::InitializeBody(int object_size) {
+ Object* value = GetHeap()->undefined_value();
+ for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
+ WRITE_FIELD(this, offset, value);
+ }
+}
+
+
+bool Object::ToArrayIndex(uint32_t* index) {
+ if (IsSmi()) {
+ int value = Smi::cast(this)->value();
+ if (value < 0) return false;
+ *index = value;
+ return true;
+ }
+ if (IsHeapNumber()) {
+ double value = HeapNumber::cast(this)->value();
+ uint32_t uint_value = static_cast<uint32_t>(value);
+ if (value == static_cast<double>(uint_value)) {
+ *index = uint_value;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Object::IsStringObjectWithCharacterAt(uint32_t index) {
+ if (!this->IsJSValue()) return false;
+
+ JSValue* js_value = JSValue::cast(this);
+ if (!js_value->value()->IsString()) return false;
+
+ String* str = String::cast(js_value->value());
+ if (index >= static_cast<uint32_t>(str->length())) return false;
+
+ return true;
+}
+
+
+
+void Object::VerifyApiCallResultType() {
+#if ENABLE_EXTRA_CHECKS
+ if (!(IsSmi() ||
+ IsString() ||
+ IsSpecObject() ||
+ IsHeapNumber() ||
+ IsUndefined() ||
+ IsTrue() ||
+ IsFalse() ||
+ IsNull())) {
+ FATAL("API call returned invalid object");
+ }
+#endif // ENABLE_EXTRA_CHECKS
+}
+
+
+FixedArrayBase* FixedArrayBase::cast(Object* object) {
+ ASSERT(object->IsFixedArray() || object->IsFixedDoubleArray());
+ return reinterpret_cast<FixedArrayBase*>(object);
+}
+
+
+Object* FixedArray::get(int index) {
+ ASSERT(index >= 0 && index < this->length());
+ return READ_FIELD(this, kHeaderSize + index * kPointerSize);
+}
+
+
+bool FixedArray::is_the_hole(int index) {
+ return get(index) == GetHeap()->the_hole_value();
+}
+
+
+void FixedArray::set(int index, Smi* value) {
+ ASSERT(map() != HEAP->fixed_cow_array_map());
+ ASSERT(index >= 0 && index < this->length());
+ ASSERT(reinterpret_cast<Object*>(value)->IsSmi());
+ int offset = kHeaderSize + index * kPointerSize;
+ WRITE_FIELD(this, offset, value);
+}
+
+
+void FixedArray::set(int index, Object* value) {
+ ASSERT(map() != HEAP->fixed_cow_array_map());
+ ASSERT(index >= 0 && index < this->length());
+ int offset = kHeaderSize + index * kPointerSize;
+ WRITE_FIELD(this, offset, value);
+ WRITE_BARRIER(GetHeap(), this, offset, value);
+}
+
+
+inline bool FixedDoubleArray::is_the_hole_nan(double value) {
+ return BitCast<uint64_t, double>(value) == kHoleNanInt64;
+}
+
+
+inline double FixedDoubleArray::hole_nan_as_double() {
+ return BitCast<double, uint64_t>(kHoleNanInt64);
+}
+
+
+inline double FixedDoubleArray::canonical_not_the_hole_nan_as_double() {
+ ASSERT(BitCast<uint64_t>(OS::nan_value()) != kHoleNanInt64);
+ ASSERT((BitCast<uint64_t>(OS::nan_value()) >> 32) != kHoleNanUpper32);
+ return OS::nan_value();
+}
+
+
+double FixedDoubleArray::get_scalar(int index) {
+ ASSERT(map() != HEAP->fixed_cow_array_map() &&
+ map() != HEAP->fixed_array_map());
+ ASSERT(index >= 0 && index < this->length());
+ double result = READ_DOUBLE_FIELD(this, kHeaderSize + index * kDoubleSize);
+ ASSERT(!is_the_hole_nan(result));
+ return result;
+}
+
+int64_t FixedDoubleArray::get_representation(int index) {
+ ASSERT(map() != HEAP->fixed_cow_array_map() &&
+ map() != HEAP->fixed_array_map());
+ ASSERT(index >= 0 && index < this->length());
+ return READ_INT64_FIELD(this, kHeaderSize + index * kDoubleSize);
+}
+
+MaybeObject* FixedDoubleArray::get(int index) {
+ if (is_the_hole(index)) {
+ return GetHeap()->the_hole_value();
+ } else {
+ return GetHeap()->NumberFromDouble(get_scalar(index));
+ }
+}
+
+
+void FixedDoubleArray::set(int index, double value) {
+ ASSERT(map() != HEAP->fixed_cow_array_map() &&
+ map() != HEAP->fixed_array_map());
+ int offset = kHeaderSize + index * kDoubleSize;
+ if (std::isnan(value)) value = canonical_not_the_hole_nan_as_double();
+ WRITE_DOUBLE_FIELD(this, offset, value);
+}
+
+
+void FixedDoubleArray::set_the_hole(int index) {
+ ASSERT(map() != HEAP->fixed_cow_array_map() &&
+ map() != HEAP->fixed_array_map());
+ int offset = kHeaderSize + index * kDoubleSize;
+ WRITE_DOUBLE_FIELD(this, offset, hole_nan_as_double());
+}
+
+
+bool FixedDoubleArray::is_the_hole(int index) {
+ int offset = kHeaderSize + index * kDoubleSize;
+ return is_the_hole_nan(READ_DOUBLE_FIELD(this, offset));
+}
+
+
+WriteBarrierMode HeapObject::GetWriteBarrierMode(
+ const DisallowHeapAllocation& promise) {
+ Heap* heap = GetHeap();
+ if (heap->incremental_marking()->IsMarking()) return UPDATE_WRITE_BARRIER;
+ if (heap->InNewSpace(this)) return SKIP_WRITE_BARRIER;
+ return UPDATE_WRITE_BARRIER;
+}
+
+
+void FixedArray::set(int index,
+ Object* value,
+ WriteBarrierMode mode) {
+ ASSERT(map() != HEAP->fixed_cow_array_map());
+ ASSERT(index >= 0 && index < this->length());
+ int offset = kHeaderSize + index * kPointerSize;
+ WRITE_FIELD(this, offset, value);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode);
+}
+
+
+void FixedArray::NoIncrementalWriteBarrierSet(FixedArray* array,
+ int index,
+ Object* value) {
+ ASSERT(array->map() != HEAP->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::NoWriteBarrierSet(FixedArray* array,
+ int index,
+ Object* value) {
+ ASSERT(array->map() != HEAP->fixed_cow_array_map());
+ ASSERT(index >= 0 && index < array->length());
+ ASSERT(!HEAP->InNewSpace(value));
+ WRITE_FIELD(array, kHeaderSize + index * kPointerSize, value);
+}
+
+
+void FixedArray::set_undefined(int index) {
+ ASSERT(map() != HEAP->fixed_cow_array_map());
+ set_undefined(GetHeap(), index);
+}
+
+
+void FixedArray::set_undefined(Heap* heap, int index) {
+ ASSERT(index >= 0 && index < this->length());
+ ASSERT(!heap->InNewSpace(heap->undefined_value()));
+ WRITE_FIELD(this, kHeaderSize + index * kPointerSize,
+ heap->undefined_value());
+}
+
+
+void FixedArray::set_null(int index) {
+ set_null(GetHeap(), index);
+}
+
+
+void FixedArray::set_null(Heap* heap, int index) {
+ ASSERT(index >= 0 && index < this->length());
+ ASSERT(!heap->InNewSpace(heap->null_value()));
+ WRITE_FIELD(this, kHeaderSize + index * kPointerSize, heap->null_value());
+}
+
+
+void FixedArray::set_the_hole(int index) {
+ ASSERT(map() != HEAP->fixed_cow_array_map());
+ ASSERT(index >= 0 && index < this->length());
+ ASSERT(!HEAP->InNewSpace(HEAP->the_hole_value()));
+ WRITE_FIELD(this,
+ kHeaderSize + index * kPointerSize,
+ GetHeap()->the_hole_value());
+}
+
+
+double* FixedDoubleArray::data_start() {
+ return reinterpret_cast<double*>(FIELD_ADDR(this, kHeaderSize));
+}
+
+
+Object** FixedArray::data_start() {
+ return HeapObject::RawField(this, kHeaderSize);
+}
+
+
+bool DescriptorArray::IsEmpty() {
+ ASSERT(length() >= kFirstIndex ||
+ this == HEAP->empty_descriptor_array());
+ return length() < kFirstIndex;
+}
+
+
+void DescriptorArray::SetNumberOfDescriptors(int number_of_descriptors) {
+ WRITE_FIELD(
+ this, kDescriptorLengthOffset, Smi::FromInt(number_of_descriptors));
+}
+
+
+// Perform a binary search in a fixed array. Low and high are entry indices. If
+// there are three entries in this array it should be called with low=0 and
+// high=2.
+template<SearchMode search_mode, typename T>
+int BinarySearch(T* array, Name* name, int low, int high, int valid_entries) {
+ uint32_t hash = name->Hash();
+ int limit = high;
+
+ ASSERT(low <= high);
+
+ while (low != high) {
+ int mid = (low + high) / 2;
+ Name* mid_name = array->GetSortedKey(mid);
+ uint32_t mid_hash = mid_name->Hash();
+
+ if (mid_hash >= hash) {
+ high = mid;
+ } else {
+ low = mid + 1;
+ }
+ }
+
+ for (; low <= limit; ++low) {
+ int sort_index = array->GetSortedKeyIndex(low);
+ Name* entry = array->GetKey(sort_index);
+ if (entry->Hash() != hash) break;
+ if (entry->Equals(name)) {
+ if (search_mode == ALL_ENTRIES || sort_index < valid_entries) {
+ return sort_index;
+ }
+ return T::kNotFound;
+ }
+ }
+
+ return T::kNotFound;
+}
+
+
+// Perform a linear search in this fixed array. len is the number of entry
+// indices that are valid.
+template<SearchMode search_mode, typename T>
+int LinearSearch(T* array, Name* name, int len, int valid_entries) {
+ uint32_t hash = name->Hash();
+ if (search_mode == ALL_ENTRIES) {
+ for (int number = 0; number < len; number++) {
+ int sorted_index = array->GetSortedKeyIndex(number);
+ Name* entry = array->GetKey(sorted_index);
+ uint32_t current_hash = entry->Hash();
+ if (current_hash > hash) break;
+ if (current_hash == hash && entry->Equals(name)) return sorted_index;
+ }
+ } else {
+ ASSERT(len >= valid_entries);
+ for (int number = 0; number < valid_entries; number++) {
+ Name* entry = array->GetKey(number);
+ uint32_t current_hash = entry->Hash();
+ if (current_hash == hash && entry->Equals(name)) return number;
+ }
+ }
+ return T::kNotFound;
+}
+
+
+template<SearchMode search_mode, typename T>
+int Search(T* array, Name* name, int valid_entries) {
+ if (search_mode == VALID_ENTRIES) {
+ SLOW_ASSERT(array->IsSortedNoDuplicates(valid_entries));
+ } else {
+ SLOW_ASSERT(array->IsSortedNoDuplicates());
+ }
+
+ int nof = array->number_of_entries();
+ if (nof == 0) return T::kNotFound;
+
+ // Fast case: do linear search for small arrays.
+ const int kMaxElementsForLinearSearch = 8;
+ if ((search_mode == ALL_ENTRIES &&
+ nof <= kMaxElementsForLinearSearch) ||
+ (search_mode == VALID_ENTRIES &&
+ valid_entries <= (kMaxElementsForLinearSearch * 3))) {
+ return LinearSearch<search_mode>(array, name, nof, valid_entries);
+ }
+
+ // Slow case: perform binary search.
+ return BinarySearch<search_mode>(array, name, 0, nof - 1, valid_entries);
+}
+
+
+int DescriptorArray::Search(Name* name, int valid_descriptors) {
+ return internal::Search<VALID_ENTRIES>(this, name, valid_descriptors);
+}
+
+
+int DescriptorArray::SearchWithCache(Name* name, Map* map) {
+ int number_of_own_descriptors = map->NumberOfOwnDescriptors();
+ if (number_of_own_descriptors == 0) return kNotFound;
+
+ DescriptorLookupCache* cache = GetIsolate()->descriptor_lookup_cache();
+ int number = cache->Lookup(map, name);
+
+ if (number == DescriptorLookupCache::kAbsent) {
+ number = Search(name, number_of_own_descriptors);
+ cache->Update(map, name, number);
+ }
+
+ return number;
+}
+
+
+void Map::LookupDescriptor(JSObject* holder,
+ Name* name,
+ LookupResult* result) {
+ DescriptorArray* descriptors = this->instance_descriptors();
+ int number = descriptors->SearchWithCache(name, this);
+ if (number == DescriptorArray::kNotFound) return result->NotFound();
+ result->DescriptorResult(holder, descriptors->GetDetails(number), number);
+}
+
+
+void Map::LookupTransition(JSObject* holder,
+ Name* name,
+ LookupResult* result) {
+ if (HasTransitionArray()) {
+ TransitionArray* transition_array = transitions();
+ int number = transition_array->Search(name);
+ if (number != TransitionArray::kNotFound) {
+ return result->TransitionResult(holder, number);
+ }
+ }
+ result->NotFound();
+}
+
+
+Object** DescriptorArray::GetKeySlot(int descriptor_number) {
+ ASSERT(descriptor_number < number_of_descriptors());
+ return HeapObject::RawField(
+ reinterpret_cast<HeapObject*>(this),
+ OffsetOfElementAt(ToKeyIndex(descriptor_number)));
+}
+
+
+Object** DescriptorArray::GetDescriptorStartSlot(int descriptor_number) {
+ return GetKeySlot(descriptor_number);
+}
+
+
+Object** DescriptorArray::GetDescriptorEndSlot(int descriptor_number) {
+ return GetValueSlot(descriptor_number - 1) + 1;
+}
+
+
+Name* DescriptorArray::GetKey(int descriptor_number) {
+ ASSERT(descriptor_number < number_of_descriptors());
+ return Name::cast(get(ToKeyIndex(descriptor_number)));
+}
+
+
+int DescriptorArray::GetSortedKeyIndex(int descriptor_number) {
+ return GetDetails(descriptor_number).pointer();
+}
+
+
+Name* DescriptorArray::GetSortedKey(int descriptor_number) {
+ return GetKey(GetSortedKeyIndex(descriptor_number));
+}
+
+
+void DescriptorArray::SetSortedKey(int descriptor_index, int pointer) {
+ PropertyDetails details = GetDetails(descriptor_index);
+ set(ToDetailsIndex(descriptor_index), details.set_pointer(pointer).AsSmi());
+}
+
+
+void DescriptorArray::SetRepresentation(int descriptor_index,
+ Representation representation) {
+ ASSERT(!representation.IsNone());
+ PropertyDetails details = GetDetails(descriptor_index);
+ set(ToDetailsIndex(descriptor_index),
+ details.CopyWithRepresentation(representation).AsSmi());
+}
+
+
+void DescriptorArray::InitializeRepresentations(Representation representation) {
+ int length = number_of_descriptors();
+ for (int i = 0; i < length; i++) {
+ SetRepresentation(i, representation);
+ }
+}
+
+
+Object** DescriptorArray::GetValueSlot(int descriptor_number) {
+ ASSERT(descriptor_number < number_of_descriptors());
+ return HeapObject::RawField(
+ reinterpret_cast<HeapObject*>(this),
+ OffsetOfElementAt(ToValueIndex(descriptor_number)));
+}
+
+
+Object* DescriptorArray::GetValue(int descriptor_number) {
+ ASSERT(descriptor_number < number_of_descriptors());
+ return get(ToValueIndex(descriptor_number));
+}
+
+
+PropertyDetails DescriptorArray::GetDetails(int descriptor_number) {
+ ASSERT(descriptor_number < number_of_descriptors());
+ Object* details = get(ToDetailsIndex(descriptor_number));
+ return PropertyDetails(Smi::cast(details));
+}
+
+
+PropertyType DescriptorArray::GetType(int descriptor_number) {
+ return GetDetails(descriptor_number).type();
+}
+
+
+int DescriptorArray::GetFieldIndex(int descriptor_number) {
+ return GetDetails(descriptor_number).field_index();
+}
+
+
+Object* DescriptorArray::GetConstant(int descriptor_number) {
+ return GetValue(descriptor_number);
+}
+
+
+Object* DescriptorArray::GetCallbacksObject(int descriptor_number) {
+ ASSERT(GetType(descriptor_number) == CALLBACKS);
+ return GetValue(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->foreign_address());
+}
+
+
+void DescriptorArray::Get(int descriptor_number, Descriptor* desc) {
+ desc->Init(GetKey(descriptor_number),
+ GetValue(descriptor_number),
+ GetDetails(descriptor_number));
+}
+
+
+void DescriptorArray::Set(int descriptor_number,
+ Descriptor* desc,
+ const WhitenessWitness&) {
+ // Range check.
+ ASSERT(descriptor_number < number_of_descriptors());
+
+ NoIncrementalWriteBarrierSet(this,
+ ToKeyIndex(descriptor_number),
+ desc->GetKey());
+ NoIncrementalWriteBarrierSet(this,
+ ToValueIndex(descriptor_number),
+ desc->GetValue());
+ NoIncrementalWriteBarrierSet(this,
+ ToDetailsIndex(descriptor_number),
+ desc->GetDetails().AsSmi());
+}
+
+
+void DescriptorArray::Set(int descriptor_number, Descriptor* desc) {
+ // Range check.
+ ASSERT(descriptor_number < number_of_descriptors());
+
+ set(ToKeyIndex(descriptor_number), desc->GetKey());
+ set(ToValueIndex(descriptor_number), desc->GetValue());
+ set(ToDetailsIndex(descriptor_number), desc->GetDetails().AsSmi());
+}
+
+
+void DescriptorArray::Append(Descriptor* desc,
+ const WhitenessWitness& witness) {
+ int descriptor_number = number_of_descriptors();
+ SetNumberOfDescriptors(descriptor_number + 1);
+ Set(descriptor_number, desc, witness);
+
+ uint32_t hash = desc->GetKey()->Hash();
+
+ int insertion;
+
+ for (insertion = descriptor_number; insertion > 0; --insertion) {
+ Name* key = GetSortedKey(insertion - 1);
+ if (key->Hash() <= hash) break;
+ SetSortedKey(insertion, GetSortedKeyIndex(insertion - 1));
+ }
+
+ SetSortedKey(insertion, descriptor_number);
+}
+
+
+void DescriptorArray::Append(Descriptor* desc) {
+ int descriptor_number = number_of_descriptors();
+ SetNumberOfDescriptors(descriptor_number + 1);
+ Set(descriptor_number, desc);
+
+ uint32_t hash = desc->GetKey()->Hash();
+
+ int insertion;
+
+ for (insertion = descriptor_number; insertion > 0; --insertion) {
+ Name* key = GetSortedKey(insertion - 1);
+ if (key->Hash() <= hash) break;
+ SetSortedKey(insertion, GetSortedKeyIndex(insertion - 1));
+ }
+
+ SetSortedKey(insertion, descriptor_number);
+}
+
+
+void DescriptorArray::SwapSortedKeys(int first, int second) {
+ int first_key = GetSortedKeyIndex(first);
+ SetSortedKey(first, GetSortedKeyIndex(second));
+ SetSortedKey(second, first_key);
+}
+
+
+DescriptorArray::WhitenessWitness::WhitenessWitness(FixedArray* array)
+ : marking_(array->GetHeap()->incremental_marking()) {
+ marking_->EnterNoMarkingScope();
+ ASSERT(Marking::Color(array) == Marking::WHITE_OBJECT);
+}
+
+
+DescriptorArray::WhitenessWitness::~WhitenessWitness() {
+ marking_->LeaveNoMarkingScope();
+}
+
+
+template<typename Shape, typename Key>
+int HashTable<Shape, Key>::ComputeCapacity(int at_least_space_for) {
+ const int kMinCapacity = 32;
+ int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
+ if (capacity < kMinCapacity) {
+ capacity = kMinCapacity; // Guarantee min capacity.
+ }
+ return capacity;
+}
+
+
+template<typename Shape, typename Key>
+int HashTable<Shape, Key>::FindEntry(Key key) {
+ return FindEntry(GetIsolate(), key);
+}
+
+
+// Find entry for key otherwise return kNotFound.
+template<typename Shape, typename Key>
+int HashTable<Shape, Key>::FindEntry(Isolate* isolate, Key key) {
+ uint32_t capacity = Capacity();
+ uint32_t entry = FirstProbe(HashTable<Shape, Key>::Hash(key), capacity);
+ uint32_t count = 1;
+ // EnsureCapacity will guarantee the hash table is never full.
+ while (true) {
+ Object* element = KeyAt(entry);
+ // Empty entry. Uses raw unchecked accessors because it is called by the
+ // string table during bootstrapping.
+ if (element == isolate->heap()->raw_unchecked_undefined_value()) break;
+ if (element != isolate->heap()->raw_unchecked_the_hole_value() &&
+ Shape::IsMatch(key, element)) return entry;
+ entry = NextProbe(entry, count++, capacity);
+ }
+ return kNotFound;
+}
+
+
+bool SeededNumberDictionary::requires_slow_elements() {
+ Object* max_index_object = get(kMaxNumberKeyIndex);
+ if (!max_index_object->IsSmi()) return false;
+ return 0 !=
+ (Smi::cast(max_index_object)->value() & kRequiresSlowElementsMask);
+}
+
+uint32_t SeededNumberDictionary::max_number_key() {
+ ASSERT(!requires_slow_elements());
+ Object* max_index_object = get(kMaxNumberKeyIndex);
+ if (!max_index_object->IsSmi()) return 0;
+ uint32_t value = static_cast<uint32_t>(Smi::cast(max_index_object)->value());
+ return value >> kRequiresSlowElementsTagSize;
+}
+
+void SeededNumberDictionary::set_requires_slow_elements() {
+ set(kMaxNumberKeyIndex, Smi::FromInt(kRequiresSlowElementsMask));
+}
+
+
+// ------------------------------------
+// Cast operations
+
+
+CAST_ACCESSOR(FixedArray)
+CAST_ACCESSOR(FixedDoubleArray)
+CAST_ACCESSOR(DescriptorArray)
+CAST_ACCESSOR(DeoptimizationInputData)
+CAST_ACCESSOR(DeoptimizationOutputData)
+CAST_ACCESSOR(DependentCode)
+CAST_ACCESSOR(TypeFeedbackCells)
+CAST_ACCESSOR(StringTable)
+CAST_ACCESSOR(JSFunctionResultCache)
+CAST_ACCESSOR(NormalizedMapCache)
+CAST_ACCESSOR(ScopeInfo)
+CAST_ACCESSOR(CompilationCacheTable)
+CAST_ACCESSOR(CodeCacheHashTable)
+CAST_ACCESSOR(PolymorphicCodeCacheHashTable)
+CAST_ACCESSOR(MapCache)
+CAST_ACCESSOR(String)
+CAST_ACCESSOR(SeqString)
+CAST_ACCESSOR(SeqOneByteString)
+CAST_ACCESSOR(SeqTwoByteString)
+CAST_ACCESSOR(SlicedString)
+CAST_ACCESSOR(ConsString)
+CAST_ACCESSOR(ExternalString)
+CAST_ACCESSOR(ExternalAsciiString)
+CAST_ACCESSOR(ExternalTwoByteString)
+CAST_ACCESSOR(Symbol)
+CAST_ACCESSOR(Name)
+CAST_ACCESSOR(JSReceiver)
+CAST_ACCESSOR(JSObject)
+CAST_ACCESSOR(Smi)
+CAST_ACCESSOR(HeapObject)
+CAST_ACCESSOR(HeapNumber)
+CAST_ACCESSOR(Oddball)
+CAST_ACCESSOR(Cell)
+CAST_ACCESSOR(PropertyCell)
+CAST_ACCESSOR(SharedFunctionInfo)
+CAST_ACCESSOR(Map)
+CAST_ACCESSOR(JSFunction)
+CAST_ACCESSOR(GlobalObject)
+CAST_ACCESSOR(JSGlobalProxy)
+CAST_ACCESSOR(JSGlobalObject)
+CAST_ACCESSOR(JSBuiltinsObject)
+CAST_ACCESSOR(Code)
+CAST_ACCESSOR(JSArray)
+CAST_ACCESSOR(JSArrayBuffer)
+CAST_ACCESSOR(JSArrayBufferView)
+CAST_ACCESSOR(JSTypedArray)
+CAST_ACCESSOR(JSDataView)
+CAST_ACCESSOR(JSRegExp)
+CAST_ACCESSOR(JSProxy)
+CAST_ACCESSOR(JSFunctionProxy)
+CAST_ACCESSOR(JSSet)
+CAST_ACCESSOR(JSMap)
+CAST_ACCESSOR(JSWeakMap)
+CAST_ACCESSOR(JSWeakSet)
+CAST_ACCESSOR(Foreign)
+CAST_ACCESSOR(ByteArray)
+CAST_ACCESSOR(FreeSpace)
+CAST_ACCESSOR(ExternalArray)
+CAST_ACCESSOR(ExternalByteArray)
+CAST_ACCESSOR(ExternalUnsignedByteArray)
+CAST_ACCESSOR(ExternalShortArray)
+CAST_ACCESSOR(ExternalUnsignedShortArray)
+CAST_ACCESSOR(ExternalIntArray)
+CAST_ACCESSOR(ExternalUnsignedIntArray)
+CAST_ACCESSOR(ExternalFloatArray)
+CAST_ACCESSOR(ExternalDoubleArray)
+CAST_ACCESSOR(ExternalPixelArray)
+CAST_ACCESSOR(Struct)
+CAST_ACCESSOR(AccessorInfo)
+
+
+#define MAKE_STRUCT_CAST(NAME, Name, name) CAST_ACCESSOR(Name)
+ STRUCT_LIST(MAKE_STRUCT_CAST)
+#undef MAKE_STRUCT_CAST
+
+
+template <typename Shape, typename Key>
+HashTable<Shape, Key>* HashTable<Shape, Key>::cast(Object* obj) {
+ ASSERT(obj->IsHashTable());
+ return reinterpret_cast<HashTable*>(obj);
+}
+
+
+SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset)
+SMI_ACCESSORS(FreeSpace, size, kSizeOffset)
+
+SMI_ACCESSORS(String, length, kLengthOffset)
+
+
+uint32_t Name::hash_field() {
+ return READ_UINT32_FIELD(this, kHashFieldOffset);
+}
+
+
+void Name::set_hash_field(uint32_t value) {
+ WRITE_UINT32_FIELD(this, kHashFieldOffset, value);
+#if V8_HOST_ARCH_64_BIT
+ WRITE_UINT32_FIELD(this, kHashFieldOffset + kIntSize, 0);
+#endif
+}
+
+
+bool Name::Equals(Name* other) {
+ if (other == this) return true;
+ if ((this->IsInternalizedString() && other->IsInternalizedString()) ||
+ this->IsSymbol() || other->IsSymbol()) {
+ return false;
+ }
+ return String::cast(this)->SlowEquals(String::cast(other));
+}
+
+
+ACCESSORS(Symbol, name, Object, kNameOffset)
+
+
+bool String::Equals(String* other) {
+ if (other == this) return true;
+ if (this->IsInternalizedString() && other->IsInternalizedString()) {
+ return false;
+ }
+ return SlowEquals(other);
+}
+
+
+MaybeObject* String::TryFlatten(PretenureFlag pretenure) {
+ if (!StringShape(this).IsCons()) return this;
+ ConsString* cons = ConsString::cast(this);
+ if (cons->IsFlat()) return cons->first();
+ return SlowTryFlatten(pretenure);
+}
+
+
+String* String::TryFlattenGetString(PretenureFlag pretenure) {
+ MaybeObject* flat = TryFlatten(pretenure);
+ Object* successfully_flattened;
+ if (!flat->ToObject(&successfully_flattened)) return this;
+ return String::cast(successfully_flattened);
+}
+
+
+uint16_t String::Get(int index) {
+ ASSERT(index >= 0 && index < length());
+ switch (StringShape(this).full_representation_tag()) {
+ case kSeqStringTag | kOneByteStringTag:
+ return SeqOneByteString::cast(this)->SeqOneByteStringGet(index);
+ case kSeqStringTag | kTwoByteStringTag:
+ return SeqTwoByteString::cast(this)->SeqTwoByteStringGet(index);
+ case kConsStringTag | kOneByteStringTag:
+ case kConsStringTag | kTwoByteStringTag:
+ return ConsString::cast(this)->ConsStringGet(index);
+ case kExternalStringTag | kOneByteStringTag:
+ return ExternalAsciiString::cast(this)->ExternalAsciiStringGet(index);
+ case kExternalStringTag | kTwoByteStringTag:
+ return ExternalTwoByteString::cast(this)->ExternalTwoByteStringGet(index);
+ case kSlicedStringTag | kOneByteStringTag:
+ case kSlicedStringTag | kTwoByteStringTag:
+ return SlicedString::cast(this)->SlicedStringGet(index);
+ default:
+ break;
+ }
+
+ UNREACHABLE();
+ return 0;
+}
+
+
+void String::Set(int index, uint16_t value) {
+ ASSERT(index >= 0 && index < length());
+ ASSERT(StringShape(this).IsSequential());
+
+ return this->IsOneByteRepresentation()
+ ? SeqOneByteString::cast(this)->SeqOneByteStringSet(index, value)
+ : SeqTwoByteString::cast(this)->SeqTwoByteStringSet(index, value);
+}
+
+
+bool String::IsFlat() {
+ if (!StringShape(this).IsCons()) return true;
+ return ConsString::cast(this)->second()->length() == 0;
+}
+
+
+String* String::GetUnderlying() {
+ // Giving direct access to underlying string only makes sense if the
+ // wrapping string is already flattened.
+ ASSERT(this->IsFlat());
+ ASSERT(StringShape(this).IsIndirect());
+ STATIC_ASSERT(ConsString::kFirstOffset == SlicedString::kParentOffset);
+ const int kUnderlyingOffset = SlicedString::kParentOffset;
+ return String::cast(READ_FIELD(this, kUnderlyingOffset));
+}
+
+
+template<class Visitor, class ConsOp>
+void String::Visit(
+ String* string,
+ unsigned offset,
+ Visitor& visitor,
+ ConsOp& cons_op,
+ int32_t type,
+ unsigned length) {
+ ASSERT(length == static_cast<unsigned>(string->length()));
+ ASSERT(offset <= length);
+ unsigned slice_offset = offset;
+ while (true) {
+ ASSERT(type == string->map()->instance_type());
+
+ switch (type & (kStringRepresentationMask | kStringEncodingMask)) {
+ case kSeqStringTag | kOneByteStringTag:
+ visitor.VisitOneByteString(
+ SeqOneByteString::cast(string)->GetChars() + slice_offset,
+ length - offset);
+ return;
+
+ case kSeqStringTag | kTwoByteStringTag:
+ visitor.VisitTwoByteString(
+ SeqTwoByteString::cast(string)->GetChars() + slice_offset,
+ length - offset);
+ return;
+
+ case kExternalStringTag | kOneByteStringTag:
+ visitor.VisitOneByteString(
+ ExternalAsciiString::cast(string)->GetChars() + slice_offset,
+ length - offset);
+ return;
+
+ case kExternalStringTag | kTwoByteStringTag:
+ visitor.VisitTwoByteString(
+ ExternalTwoByteString::cast(string)->GetChars() + slice_offset,
+ length - offset);
+ return;
+
+ case kSlicedStringTag | kOneByteStringTag:
+ case kSlicedStringTag | kTwoByteStringTag: {
+ SlicedString* slicedString = SlicedString::cast(string);
+ slice_offset += slicedString->offset();
+ string = slicedString->parent();
+ type = string->map()->instance_type();
+ continue;
+ }
+
+ case kConsStringTag | kOneByteStringTag:
+ case kConsStringTag | kTwoByteStringTag:
+ string = cons_op.Operate(string, &offset, &type, &length);
+ if (string == NULL) return;
+ slice_offset = offset;
+ ASSERT(length == static_cast<unsigned>(string->length()));
+ continue;
+
+ default:
+ UNREACHABLE();
+ return;
+ }
+ }
+}
+
+
+// TODO(dcarney): Remove this class after conversion to VisitFlat.
+class ConsStringCaptureOp {
+ public:
+ inline ConsStringCaptureOp() : cons_string_(NULL) {}
+ inline String* Operate(String* string, unsigned*, int32_t*, unsigned*) {
+ cons_string_ = ConsString::cast(string);
+ return NULL;
+ }
+ ConsString* cons_string_;
+};
+
+
+template<class Visitor>
+ConsString* String::VisitFlat(Visitor* visitor,
+ String* string,
+ int offset,
+ int length,
+ int32_t type) {
+ ASSERT(length >= 0 && length == string->length());
+ ASSERT(offset >= 0 && offset <= length);
+ ConsStringCaptureOp op;
+ Visit(string, offset, *visitor, op, type, static_cast<unsigned>(length));
+ return op.cons_string_;
+}
+
+
+uint16_t SeqOneByteString::SeqOneByteStringGet(int index) {
+ ASSERT(index >= 0 && index < length());
+ return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
+}
+
+
+void SeqOneByteString::SeqOneByteStringSet(int index, uint16_t value) {
+ ASSERT(index >= 0 && index < length() && value <= kMaxOneByteCharCode);
+ WRITE_BYTE_FIELD(this, kHeaderSize + index * kCharSize,
+ static_cast<byte>(value));
+}
+
+
+Address SeqOneByteString::GetCharsAddress() {
+ return FIELD_ADDR(this, kHeaderSize);
+}
+
+
+uint8_t* SeqOneByteString::GetChars() {
+ return reinterpret_cast<uint8_t*>(GetCharsAddress());
+}
+
+
+Address SeqTwoByteString::GetCharsAddress() {
+ return FIELD_ADDR(this, kHeaderSize);
+}
+
+
+uc16* SeqTwoByteString::GetChars() {
+ return reinterpret_cast<uc16*>(FIELD_ADDR(this, kHeaderSize));
+}
+
+
+uint16_t SeqTwoByteString::SeqTwoByteStringGet(int index) {
+ ASSERT(index >= 0 && index < length());
+ return READ_SHORT_FIELD(this, kHeaderSize + index * kShortSize);
+}
+
+
+void SeqTwoByteString::SeqTwoByteStringSet(int index, uint16_t value) {
+ ASSERT(index >= 0 && index < length());
+ WRITE_SHORT_FIELD(this, kHeaderSize + index * kShortSize, value);
+}
+
+
+int SeqTwoByteString::SeqTwoByteStringSize(InstanceType instance_type) {
+ return SizeFor(length());
+}
+
+
+int SeqOneByteString::SeqOneByteStringSize(InstanceType instance_type) {
+ return SizeFor(length());
+}
+
+
+String* SlicedString::parent() {
+ return String::cast(READ_FIELD(this, kParentOffset));
+}
+
+
+void SlicedString::set_parent(String* parent, WriteBarrierMode mode) {
+ ASSERT(parent->IsSeqString() || parent->IsExternalString());
+ WRITE_FIELD(this, kParentOffset, parent);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kParentOffset, parent, mode);
+}
+
+
+SMI_ACCESSORS(SlicedString, offset, kOffsetOffset)
+
+
+String* ConsString::first() {
+ return String::cast(READ_FIELD(this, kFirstOffset));
+}
+
+
+Object* ConsString::unchecked_first() {
+ return READ_FIELD(this, kFirstOffset);
+}
+
+
+void ConsString::set_first(String* value, WriteBarrierMode mode) {
+ WRITE_FIELD(this, kFirstOffset, value);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kFirstOffset, value, mode);
+}
+
+
+String* ConsString::second() {
+ return String::cast(READ_FIELD(this, kSecondOffset));
+}
+
+
+Object* ConsString::unchecked_second() {
+ return READ_FIELD(this, kSecondOffset);
+}
+
+
+void ConsString::set_second(String* value, WriteBarrierMode mode) {
+ WRITE_FIELD(this, kSecondOffset, value);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kSecondOffset, value, mode);
+}
+
+
+bool ExternalString::is_short() {
+ InstanceType type = map()->instance_type();
+ return (type & kShortExternalStringMask) == kShortExternalStringTag;
+}
+
+
+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(
+ const ExternalAsciiString::Resource* resource) {
+ *reinterpret_cast<const Resource**>(
+ FIELD_ADDR(this, kResourceOffset)) = resource;
+ if (resource != NULL) update_data_cache();
+}
+
+
+const uint8_t* ExternalAsciiString::GetChars() {
+ return reinterpret_cast<const uint8_t*>(resource()->data());
+}
+
+
+uint16_t ExternalAsciiString::ExternalAsciiStringGet(int index) {
+ ASSERT(index >= 0 && index < length());
+ return GetChars()[index];
+}
+
+
+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(
+ 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;
+}
+
+
+String* ConsStringNullOp::Operate(String*, unsigned*, int32_t*, unsigned*) {
+ return NULL;
+}
+
+
+unsigned ConsStringIteratorOp::OffsetForDepth(unsigned depth) {
+ return depth & kDepthMask;
+}
+
+
+void ConsStringIteratorOp::PushLeft(ConsString* string) {
+ frames_[depth_++ & kDepthMask] = string;
+}
+
+
+void ConsStringIteratorOp::PushRight(ConsString* string) {
+ // Inplace update.
+ frames_[(depth_-1) & kDepthMask] = string;
+}
+
+
+void ConsStringIteratorOp::AdjustMaximumDepth() {
+ if (depth_ > maximum_depth_) maximum_depth_ = depth_;
+}
+
+
+void ConsStringIteratorOp::Pop() {
+ ASSERT(depth_ > 0);
+ ASSERT(depth_ <= maximum_depth_);
+ depth_--;
+}
+
+
+bool ConsStringIteratorOp::HasMore() {
+ return depth_ != 0;
+}
+
+
+void ConsStringIteratorOp::Reset() {
+ depth_ = 0;
+}
+
+
+String* ConsStringIteratorOp::ContinueOperation(int32_t* type_out,
+ unsigned* length_out) {
+ bool blew_stack = false;
+ String* string = NextLeaf(&blew_stack, type_out, length_out);
+ // String found.
+ if (string != NULL) {
+ // Verify output.
+ ASSERT(*length_out == static_cast<unsigned>(string->length()));
+ ASSERT(*type_out == string->map()->instance_type());
+ return string;
+ }
+ // Traversal complete.
+ if (!blew_stack) return NULL;
+ // Restart search from root.
+ unsigned offset_out;
+ string = Search(&offset_out, type_out, length_out);
+ // Verify output.
+ ASSERT(string == NULL || offset_out == 0);
+ ASSERT(string == NULL ||
+ *length_out == static_cast<unsigned>(string->length()));
+ ASSERT(string == NULL || *type_out == string->map()->instance_type());
+ return string;
+}
+
+
+uint16_t StringCharacterStream::GetNext() {
+ ASSERT(buffer8_ != NULL && end_ != NULL);
+ // Advance cursor if needed.
+ // TODO(dcarney): Ensure uses of the api call HasMore first and avoid this.
+ if (buffer8_ == end_) HasMore();
+ ASSERT(buffer8_ < end_);
+ return is_one_byte_ ? *buffer8_++ : *buffer16_++;
+}
+
+
+StringCharacterStream::StringCharacterStream(String* string,
+ ConsStringIteratorOp* op,
+ unsigned offset)
+ : is_one_byte_(false),
+ op_(op) {
+ Reset(string, offset);
+}
+
+
+void StringCharacterStream::Reset(String* string, unsigned offset) {
+ op_->Reset();
+ buffer8_ = NULL;
+ end_ = NULL;
+ int32_t type = string->map()->instance_type();
+ unsigned length = string->length();
+ String::Visit(string, offset, *this, *op_, type, length);
+}
+
+
+bool StringCharacterStream::HasMore() {
+ if (buffer8_ != end_) return true;
+ if (!op_->HasMore()) return false;
+ unsigned length;
+ int32_t type;
+ String* string = op_->ContinueOperation(&type, &length);
+ if (string == NULL) return false;
+ ASSERT(!string->IsConsString());
+ ASSERT(string->length() != 0);
+ ConsStringNullOp null_op;
+ String::Visit(string, 0, *this, null_op, type, length);
+ ASSERT(buffer8_ != end_);
+ return true;
+}
+
+
+void StringCharacterStream::VisitOneByteString(
+ const uint8_t* chars, unsigned length) {
+ is_one_byte_ = true;
+ buffer8_ = chars;
+ end_ = chars + length;
+}
+
+
+void StringCharacterStream::VisitTwoByteString(
+ const uint16_t* chars, unsigned length) {
+ is_one_byte_ = false;
+ buffer16_ = chars;
+ end_ = reinterpret_cast<const uint8_t*>(chars + length);
+}
+
+
+void JSFunctionResultCache::MakeZeroSize() {
+ set_finger_index(kEntriesIndex);
+ set_size(kEntriesIndex);
+}
+
+
+void JSFunctionResultCache::Clear() {
+ int cache_size = size();
+ Object** entries_start = RawField(this, OffsetOfElementAt(kEntriesIndex));
+ MemsetPointer(entries_start,
+ GetHeap()->the_hole_value(),
+ cache_size - kEntriesIndex);
+ MakeZeroSize();
+}
+
+
+int JSFunctionResultCache::size() {
+ return Smi::cast(get(kCacheSizeIndex))->value();
+}
+
+
+void JSFunctionResultCache::set_size(int size) {
+ set(kCacheSizeIndex, Smi::FromInt(size));
+}
+
+
+int JSFunctionResultCache::finger_index() {
+ return Smi::cast(get(kFingerIndex))->value();
+}
+
+
+void JSFunctionResultCache::set_finger_index(int finger_index) {
+ set(kFingerIndex, Smi::FromInt(finger_index));
+}
+
+
+byte ByteArray::get(int index) {
+ ASSERT(index >= 0 && index < this->length());
+ return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
+}
+
+
+void ByteArray::set(int index, byte value) {
+ ASSERT(index >= 0 && index < this->length());
+ WRITE_BYTE_FIELD(this, kHeaderSize + index * kCharSize, value);
+}
+
+
+int ByteArray::get_int(int index) {
+ ASSERT(index >= 0 && (index * kIntSize) < this->length());
+ return READ_INT_FIELD(this, kHeaderSize + index * kIntSize);
+}
+
+
+ByteArray* ByteArray::FromDataStartAddress(Address address) {
+ ASSERT_TAG_ALIGNED(address);
+ return reinterpret_cast<ByteArray*>(address - kHeaderSize + kHeapObjectTag);
+}
+
+
+Address ByteArray::GetDataStartAddress() {
+ return reinterpret_cast<Address>(this) - kHeapObjectTag + kHeaderSize;
+}
+
+
+uint8_t* ExternalPixelArray::external_pixel_pointer() {
+ return reinterpret_cast<uint8_t*>(external_pointer());
+}
+
+
+uint8_t ExternalPixelArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ uint8_t* ptr = external_pixel_pointer();
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalPixelArray::get(int index) {
+ return Smi::FromInt(static_cast<int>(get_scalar(index)));
+}
+
+
+void ExternalPixelArray::set(int index, uint8_t value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ uint8_t* ptr = external_pixel_pointer();
+ ptr[index] = value;
+}
+
+
+void* ExternalArray::external_pointer() {
+ intptr_t ptr = READ_INTPTR_FIELD(this, kExternalPointerOffset);
+ return reinterpret_cast<void*>(ptr);
+}
+
+
+void ExternalArray::set_external_pointer(void* value, WriteBarrierMode mode) {
+ intptr_t ptr = reinterpret_cast<intptr_t>(value);
+ WRITE_INTPTR_FIELD(this, kExternalPointerOffset, ptr);
+}
+
+
+int8_t ExternalByteArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ int8_t* ptr = static_cast<int8_t*>(external_pointer());
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalByteArray::get(int index) {
+ return Smi::FromInt(static_cast<int>(get_scalar(index)));
+}
+
+
+void ExternalByteArray::set(int index, int8_t value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ int8_t* ptr = static_cast<int8_t*>(external_pointer());
+ ptr[index] = value;
+}
+
+
+uint8_t ExternalUnsignedByteArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ uint8_t* ptr = static_cast<uint8_t*>(external_pointer());
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalUnsignedByteArray::get(int index) {
+ return Smi::FromInt(static_cast<int>(get_scalar(index)));
+}
+
+
+void ExternalUnsignedByteArray::set(int index, uint8_t value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ uint8_t* ptr = static_cast<uint8_t*>(external_pointer());
+ ptr[index] = value;
+}
+
+
+int16_t ExternalShortArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ int16_t* ptr = static_cast<int16_t*>(external_pointer());
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalShortArray::get(int index) {
+ return Smi::FromInt(static_cast<int>(get_scalar(index)));
+}
+
+
+void ExternalShortArray::set(int index, int16_t value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ int16_t* ptr = static_cast<int16_t*>(external_pointer());
+ ptr[index] = value;
+}
+
+
+uint16_t ExternalUnsignedShortArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ uint16_t* ptr = static_cast<uint16_t*>(external_pointer());
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalUnsignedShortArray::get(int index) {
+ return Smi::FromInt(static_cast<int>(get_scalar(index)));
+}
+
+
+void ExternalUnsignedShortArray::set(int index, uint16_t value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ uint16_t* ptr = static_cast<uint16_t*>(external_pointer());
+ ptr[index] = value;
+}
+
+
+int32_t ExternalIntArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ int32_t* ptr = static_cast<int32_t*>(external_pointer());
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalIntArray::get(int index) {
+ return GetHeap()->NumberFromInt32(get_scalar(index));
+}
+
+
+void ExternalIntArray::set(int index, int32_t value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ int32_t* ptr = static_cast<int32_t*>(external_pointer());
+ ptr[index] = value;
+}
+
+
+uint32_t ExternalUnsignedIntArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ uint32_t* ptr = static_cast<uint32_t*>(external_pointer());
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalUnsignedIntArray::get(int index) {
+ return GetHeap()->NumberFromUint32(get_scalar(index));
+}
+
+
+void ExternalUnsignedIntArray::set(int index, uint32_t value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ uint32_t* ptr = static_cast<uint32_t*>(external_pointer());
+ ptr[index] = value;
+}
+
+
+float ExternalFloatArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ float* ptr = static_cast<float*>(external_pointer());
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalFloatArray::get(int index) {
+ return GetHeap()->NumberFromDouble(get_scalar(index));
+}
+
+
+void ExternalFloatArray::set(int index, float value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ float* ptr = static_cast<float*>(external_pointer());
+ ptr[index] = value;
+}
+
+
+double ExternalDoubleArray::get_scalar(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ double* ptr = static_cast<double*>(external_pointer());
+ return ptr[index];
+}
+
+
+MaybeObject* ExternalDoubleArray::get(int index) {
+ return GetHeap()->NumberFromDouble(get_scalar(index));
+}
+
+
+void ExternalDoubleArray::set(int index, double value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ double* ptr = static_cast<double*>(external_pointer());
+ ptr[index] = value;
+}
+
+
+int Map::visitor_id() {
+ return READ_BYTE_FIELD(this, kVisitorIdOffset);
+}
+
+
+void Map::set_visitor_id(int id) {
+ ASSERT(0 <= id && id < 256);
+ WRITE_BYTE_FIELD(this, kVisitorIdOffset, static_cast<byte>(id));
+}
+
+
+int Map::instance_size() {
+ return READ_BYTE_FIELD(this, kInstanceSizeOffset) << kPointerSizeLog2;
+}
+
+
+int Map::inobject_properties() {
+ return READ_BYTE_FIELD(this, kInObjectPropertiesOffset);
+}
+
+
+int Map::pre_allocated_property_fields() {
+ return READ_BYTE_FIELD(this, kPreAllocatedPropertyFieldsOffset);
+}
+
+
+int HeapObject::SizeFromMap(Map* map) {
+ int instance_size = map->instance_size();
+ if (instance_size != kVariableSizeSentinel) return instance_size;
+ // Only inline the most frequent cases.
+ int instance_type = static_cast<int>(map->instance_type());
+ if (instance_type == FIXED_ARRAY_TYPE) {
+ return FixedArray::BodyDescriptor::SizeOf(map, this);
+ }
+ if (instance_type == ASCII_STRING_TYPE ||
+ instance_type == ASCII_INTERNALIZED_STRING_TYPE) {
+ return SeqOneByteString::SizeFor(
+ reinterpret_cast<SeqOneByteString*>(this)->length());
+ }
+ 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 ||
+ instance_type == INTERNALIZED_STRING_TYPE) {
+ return SeqTwoByteString::SizeFor(
+ reinterpret_cast<SeqTwoByteString*>(this)->length());
+ }
+ if (instance_type == FIXED_DOUBLE_ARRAY_TYPE) {
+ return FixedDoubleArray::SizeFor(
+ reinterpret_cast<FixedDoubleArray*>(this)->length());
+ }
+ ASSERT(instance_type == CODE_TYPE);
+ return reinterpret_cast<Code*>(this)->CodeSize();
+}
+
+
+void Map::set_instance_size(int value) {
+ ASSERT_EQ(0, value & (kPointerSize - 1));
+ value >>= kPointerSizeLog2;
+ ASSERT(0 <= value && value < 256);
+ WRITE_BYTE_FIELD(this, kInstanceSizeOffset, static_cast<byte>(value));
+}
+
+
+void Map::set_inobject_properties(int value) {
+ ASSERT(0 <= value && value < 256);
+ WRITE_BYTE_FIELD(this, kInObjectPropertiesOffset, static_cast<byte>(value));
+}
+
+
+void Map::set_pre_allocated_property_fields(int value) {
+ ASSERT(0 <= value && value < 256);
+ WRITE_BYTE_FIELD(this,
+ kPreAllocatedPropertyFieldsOffset,
+ static_cast<byte>(value));
+}
+
+
+InstanceType Map::instance_type() {
+ return static_cast<InstanceType>(READ_BYTE_FIELD(this, kInstanceTypeOffset));
+}
+
+
+void Map::set_instance_type(InstanceType value) {
+ WRITE_BYTE_FIELD(this, kInstanceTypeOffset, value);
+}
+
+
+int Map::unused_property_fields() {
+ return READ_BYTE_FIELD(this, kUnusedPropertyFieldsOffset);
+}
+
+
+void Map::set_unused_property_fields(int value) {
+ WRITE_BYTE_FIELD(this, kUnusedPropertyFieldsOffset, Min(value, 255));
+}
+
+
+byte Map::bit_field() {
+ return READ_BYTE_FIELD(this, kBitFieldOffset);
+}
+
+
+void Map::set_bit_field(byte value) {
+ WRITE_BYTE_FIELD(this, kBitFieldOffset, value);
+}
+
+
+byte Map::bit_field2() {
+ return READ_BYTE_FIELD(this, kBitField2Offset);
+}
+
+
+void Map::set_bit_field2(byte value) {
+ WRITE_BYTE_FIELD(this, kBitField2Offset, value);
+}
+
+
+void Map::set_non_instance_prototype(bool value) {
+ if (value) {
+ set_bit_field(bit_field() | (1 << kHasNonInstancePrototype));
+ } else {
+ set_bit_field(bit_field() & ~(1 << kHasNonInstancePrototype));
+ }
+}
+
+
+bool Map::has_non_instance_prototype() {
+ return ((1 << kHasNonInstancePrototype) & bit_field()) != 0;
+}
+
+
+void Map::set_function_with_prototype(bool value) {
+ set_bit_field3(FunctionWithPrototype::update(bit_field3(), value));
+}
+
+
+bool Map::function_with_prototype() {
+ return FunctionWithPrototype::decode(bit_field3());
+}
+
+
+void Map::set_is_access_check_needed(bool access_check_needed) {
+ if (access_check_needed) {
+ set_bit_field(bit_field() | (1 << kIsAccessCheckNeeded));
+ } else {
+ set_bit_field(bit_field() & ~(1 << kIsAccessCheckNeeded));
+ }
+}
+
+
+bool Map::is_access_check_needed() {
+ return ((1 << kIsAccessCheckNeeded) & bit_field()) != 0;
+}
+
+
+void Map::set_is_extensible(bool value) {
+ if (value) {
+ set_bit_field2(bit_field2() | (1 << kIsExtensible));
+ } else {
+ set_bit_field2(bit_field2() & ~(1 << kIsExtensible));
+ }
+}
+
+bool Map::is_extensible() {
+ return ((1 << kIsExtensible) & bit_field2()) != 0;
+}
+
+
+void Map::set_attached_to_shared_function_info(bool value) {
+ if (value) {
+ set_bit_field2(bit_field2() | (1 << kAttachedToSharedFunctionInfo));
+ } else {
+ set_bit_field2(bit_field2() & ~(1 << kAttachedToSharedFunctionInfo));
+ }
+}
+
+bool Map::attached_to_shared_function_info() {
+ return ((1 << kAttachedToSharedFunctionInfo) & bit_field2()) != 0;
+}
+
+
+void Map::set_is_shared(bool value) {
+ set_bit_field3(IsShared::update(bit_field3(), value));
+}
+
+
+bool Map::is_shared() {
+ return IsShared::decode(bit_field3());
+}
+
+
+void Map::set_dictionary_map(bool value) {
+ if (value) mark_unstable();
+ set_bit_field3(DictionaryMap::update(bit_field3(), value));
+}
+
+
+bool Map::is_dictionary_map() {
+ return DictionaryMap::decode(bit_field3());
+}
+
+
+Code::Flags Code::flags() {
+ return static_cast<Flags>(READ_INT_FIELD(this, kFlagsOffset));
+}
+
+
+inline bool Map::CanTrackAllocationSite() {
+ return instance_type() == JS_ARRAY_TYPE;
+}
+
+
+void Map::set_owns_descriptors(bool is_shared) {
+ set_bit_field3(OwnsDescriptors::update(bit_field3(), is_shared));
+}
+
+
+bool Map::owns_descriptors() {
+ return OwnsDescriptors::decode(bit_field3());
+}
+
+
+void Map::set_is_observed(bool is_observed) {
+ ASSERT(instance_type() < FIRST_JS_OBJECT_TYPE ||
+ instance_type() > LAST_JS_OBJECT_TYPE ||
+ has_slow_elements_kind() || has_external_array_elements());
+ set_bit_field3(IsObserved::update(bit_field3(), is_observed));
+}
+
+
+bool Map::is_observed() {
+ return IsObserved::decode(bit_field3());
+}
+
+
+void Map::deprecate() {
+ set_bit_field3(Deprecated::update(bit_field3(), true));
+}
+
+
+bool Map::is_deprecated() {
+ if (!FLAG_track_fields) return false;
+ return Deprecated::decode(bit_field3());
+}
+
+
+void Map::set_migration_target(bool value) {
+ set_bit_field3(IsMigrationTarget::update(bit_field3(), value));
+}
+
+
+bool Map::is_migration_target() {
+ if (!FLAG_track_fields) return false;
+ return IsMigrationTarget::decode(bit_field3());
+}
+
+
+void Map::freeze() {
+ set_bit_field3(IsFrozen::update(bit_field3(), true));
+}
+
+
+bool Map::is_frozen() {
+ return IsFrozen::decode(bit_field3());
+}
+
+
+void Map::mark_unstable() {
+ set_bit_field3(IsUnstable::update(bit_field3(), true));
+}
+
+
+bool Map::is_stable() {
+ return !IsUnstable::decode(bit_field3());
+}
+
+
+bool Map::has_code_cache() {
+ return code_cache() != GetIsolate()->heap()->empty_fixed_array();
+}
+
+
+bool Map::CanBeDeprecated() {
+ int descriptor = LastAdded();
+ for (int i = 0; i <= descriptor; i++) {
+ PropertyDetails details = instance_descriptors()->GetDetails(i);
+ if (FLAG_track_fields && details.representation().IsNone()) {
+ return true;
+ }
+ if (FLAG_track_fields && details.representation().IsSmi()) {
+ return true;
+ }
+ if (FLAG_track_double_fields && details.representation().IsDouble()) {
+ return true;
+ }
+ if (FLAG_track_heap_object_fields &&
+ details.representation().IsHeapObject()) {
+ return true;
+ }
+ if (FLAG_track_fields && details.type() == CONSTANT) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Map::NotifyLeafMapLayoutChange() {
+ if (is_stable()) {
+ mark_unstable();
+ dependent_code()->DeoptimizeDependentCodeGroup(
+ GetIsolate(),
+ DependentCode::kPrototypeCheckGroup);
+ }
+}
+
+
+bool Map::CanOmitMapChecks() {
+ return is_stable() && FLAG_omit_map_checks_for_leaf_maps;
+}
+
+
+int DependentCode::number_of_entries(DependencyGroup group) {
+ if (length() == 0) return 0;
+ return Smi::cast(get(group))->value();
+}
+
+
+void DependentCode::set_number_of_entries(DependencyGroup group, int value) {
+ set(group, Smi::FromInt(value));
+}
+
+
+bool DependentCode::is_code_at(int i) {
+ return get(kCodesStartIndex + i)->IsCode();
+}
+
+Code* DependentCode::code_at(int i) {
+ return Code::cast(get(kCodesStartIndex + i));
+}
+
+
+CompilationInfo* DependentCode::compilation_info_at(int i) {
+ return reinterpret_cast<CompilationInfo*>(
+ Foreign::cast(get(kCodesStartIndex + i))->foreign_address());
+}
+
+
+void DependentCode::set_object_at(int i, Object* object) {
+ set(kCodesStartIndex + i, object);
+}
+
+
+Object* DependentCode::object_at(int i) {
+ return get(kCodesStartIndex + i);
+}
+
+
+Object** DependentCode::slot_at(int i) {
+ return HeapObject::RawField(
+ this, FixedArray::OffsetOfElementAt(kCodesStartIndex + i));
+}
+
+
+void DependentCode::clear_at(int i) {
+ set_undefined(kCodesStartIndex + i);
+}
+
+
+void DependentCode::copy(int from, int to) {
+ set(kCodesStartIndex + to, get(kCodesStartIndex + from));
+}
+
+
+void DependentCode::ExtendGroup(DependencyGroup group) {
+ GroupStartIndexes starts(this);
+ for (int g = kGroupCount - 1; g > group; g--) {
+ if (starts.at(g) < starts.at(g + 1)) {
+ copy(starts.at(g), starts.at(g + 1));
+ }
+ }
+}
+
+
+void Code::set_flags(Code::Flags flags) {
+ STATIC_ASSERT(Code::NUMBER_OF_KINDS <= KindField::kMax + 1);
+ // Make sure that all call stubs have an arguments count.
+ ASSERT((ExtractKindFromFlags(flags) != CALL_IC &&
+ ExtractKindFromFlags(flags) != KEYED_CALL_IC) ||
+ ExtractArgumentsCountFromFlags(flags) >= 0);
+ WRITE_INT_FIELD(this, kFlagsOffset, flags);
+}
+
+
+Code::Kind Code::kind() {
+ return ExtractKindFromFlags(flags());
+}
+
+
+InlineCacheState Code::ic_state() {
+ InlineCacheState result = ExtractICStateFromFlags(flags());
+ // Only allow uninitialized or debugger states for non-IC code
+ // objects. This is used in the debugger to determine whether or not
+ // a call to code object has been replaced with a debug break call.
+ ASSERT(is_inline_cache_stub() ||
+ result == UNINITIALIZED ||
+ result == DEBUG_STUB);
+ return result;
+}
+
+
+Code::ExtraICState Code::extra_ic_state() {
+ ASSERT((is_inline_cache_stub() && !needs_extended_extra_ic_state(kind()))
+ || ic_state() == DEBUG_STUB);
+ return ExtractExtraICStateFromFlags(flags());
+}
+
+
+Code::ExtraICState Code::extended_extra_ic_state() {
+ ASSERT(is_inline_cache_stub() || ic_state() == DEBUG_STUB);
+ ASSERT(needs_extended_extra_ic_state(kind()));
+ return ExtractExtendedExtraICStateFromFlags(flags());
+}
+
+
+Code::StubType Code::type() {
+ return ExtractTypeFromFlags(flags());
+}
+
+
+int Code::arguments_count() {
+ ASSERT(is_call_stub() || is_keyed_call_stub() || kind() == STUB);
+ return ExtractArgumentsCountFromFlags(flags());
+}
+
+
+inline bool Code::is_crankshafted() {
+ return IsCrankshaftedField::decode(
+ READ_UINT32_FIELD(this, kKindSpecificFlags2Offset));
+}
+
+
+inline void Code::set_is_crankshafted(bool value) {
+ int previous = READ_UINT32_FIELD(this, kKindSpecificFlags2Offset);
+ int updated = IsCrankshaftedField::update(previous, value);
+ WRITE_UINT32_FIELD(this, kKindSpecificFlags2Offset, updated);
+}
+
+
+int Code::major_key() {
+ ASSERT(kind() == STUB ||
+ kind() == BINARY_OP_IC ||
+ kind() == COMPARE_IC ||
+ kind() == COMPARE_NIL_IC ||
+ kind() == STORE_IC ||
+ kind() == LOAD_IC ||
+ kind() == KEYED_LOAD_IC ||
+ kind() == TO_BOOLEAN_IC);
+ return StubMajorKeyField::decode(
+ READ_UINT32_FIELD(this, kKindSpecificFlags2Offset));
+}
+
+
+void Code::set_major_key(int major) {
+ ASSERT(kind() == STUB ||
+ kind() == BINARY_OP_IC ||
+ kind() == COMPARE_IC ||
+ kind() == COMPARE_NIL_IC ||
+ kind() == LOAD_IC ||
+ kind() == KEYED_LOAD_IC ||
+ kind() == STORE_IC ||
+ kind() == KEYED_STORE_IC ||
+ kind() == TO_BOOLEAN_IC);
+ ASSERT(0 <= major && major < 256);
+ int previous = READ_UINT32_FIELD(this, kKindSpecificFlags2Offset);
+ int updated = StubMajorKeyField::update(previous, major);
+ WRITE_UINT32_FIELD(this, kKindSpecificFlags2Offset, updated);
+}
+
+
+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_EQ(FUNCTION, kind());
+ return READ_BYTE_FIELD(this, kOptimizableOffset) == 1;
+}
+
+
+void Code::set_optimizable(bool value) {
+ ASSERT_EQ(FUNCTION, kind());
+ WRITE_BYTE_FIELD(this, kOptimizableOffset, value ? 1 : 0);
+}
+
+
+bool Code::has_deoptimization_support() {
+ ASSERT_EQ(FUNCTION, kind());
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ return FullCodeFlagsHasDeoptimizationSupportField::decode(flags);
+}
+
+
+void Code::set_has_deoptimization_support(bool value) {
+ ASSERT_EQ(FUNCTION, kind());
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ flags = FullCodeFlagsHasDeoptimizationSupportField::update(flags, value);
+ WRITE_BYTE_FIELD(this, kFullCodeFlags, flags);
+}
+
+
+bool Code::has_debug_break_slots() {
+ ASSERT_EQ(FUNCTION, kind());
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ return FullCodeFlagsHasDebugBreakSlotsField::decode(flags);
+}
+
+
+void Code::set_has_debug_break_slots(bool value) {
+ ASSERT_EQ(FUNCTION, kind());
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ flags = FullCodeFlagsHasDebugBreakSlotsField::update(flags, value);
+ WRITE_BYTE_FIELD(this, kFullCodeFlags, flags);
+}
+
+
+bool Code::is_compiled_optimizable() {
+ ASSERT_EQ(FUNCTION, kind());
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ return FullCodeFlagsIsCompiledOptimizable::decode(flags);
+}
+
+
+void Code::set_compiled_optimizable(bool value) {
+ ASSERT_EQ(FUNCTION, kind());
+ 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_EQ(FUNCTION, kind());
+ return READ_BYTE_FIELD(this, kAllowOSRAtLoopNestingLevelOffset);
+}
+
+
+void Code::set_allow_osr_at_loop_nesting_level(int level) {
+ ASSERT_EQ(FUNCTION, kind());
+ ASSERT(level >= 0 && level <= kMaxLoopNestingMarker);
+ WRITE_BYTE_FIELD(this, kAllowOSRAtLoopNestingLevelOffset, level);
+}
+
+
+int Code::profiler_ticks() {
+ ASSERT_EQ(FUNCTION, kind());
+ return READ_BYTE_FIELD(this, kProfilerTicksOffset);
+}
+
+
+void Code::set_profiler_ticks(int ticks) {
+ ASSERT_EQ(FUNCTION, kind());
+ ASSERT(ticks < 256);
+ WRITE_BYTE_FIELD(this, kProfilerTicksOffset, ticks);
+}
+
+
+unsigned Code::stack_slots() {
+ ASSERT(is_crankshafted());
+ return StackSlotsField::decode(
+ READ_UINT32_FIELD(this, kKindSpecificFlags1Offset));
+}
+
+
+void Code::set_stack_slots(unsigned slots) {
+ CHECK(slots <= (1 << kStackSlotsBitCount));
+ ASSERT(is_crankshafted());
+ int previous = READ_UINT32_FIELD(this, kKindSpecificFlags1Offset);
+ int updated = StackSlotsField::update(previous, slots);
+ WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
+}
+
+
+unsigned Code::safepoint_table_offset() {
+ ASSERT(is_crankshafted());
+ return SafepointTableOffsetField::decode(
+ READ_UINT32_FIELD(this, kKindSpecificFlags2Offset));
+}
+
+
+void Code::set_safepoint_table_offset(unsigned offset) {
+ CHECK(offset <= (1 << kSafepointTableOffsetBitCount));
+ ASSERT(is_crankshafted());
+ ASSERT(IsAligned(offset, static_cast<unsigned>(kIntSize)));
+ int previous = READ_UINT32_FIELD(this, kKindSpecificFlags2Offset);
+ int updated = SafepointTableOffsetField::update(previous, offset);
+ WRITE_UINT32_FIELD(this, kKindSpecificFlags2Offset, updated);
+}
+
+
+unsigned Code::back_edge_table_offset() {
+ ASSERT_EQ(FUNCTION, kind());
+ return BackEdgeTableOffsetField::decode(
+ READ_UINT32_FIELD(this, kKindSpecificFlags2Offset));
+}
+
+
+void Code::set_back_edge_table_offset(unsigned offset) {
+ ASSERT_EQ(FUNCTION, kind());
+ ASSERT(IsAligned(offset, static_cast<unsigned>(kIntSize)));
+ int previous = READ_UINT32_FIELD(this, kKindSpecificFlags2Offset);
+ int updated = BackEdgeTableOffsetField::update(previous, offset);
+ WRITE_UINT32_FIELD(this, kKindSpecificFlags2Offset, updated);
+}
+
+
+bool Code::back_edges_patched_for_osr() {
+ ASSERT_EQ(FUNCTION, kind());
+ return BackEdgesPatchedForOSRField::decode(
+ READ_UINT32_FIELD(this, kKindSpecificFlags2Offset));
+}
+
+
+void Code::set_back_edges_patched_for_osr(bool value) {
+ ASSERT_EQ(FUNCTION, kind());
+ int previous = READ_UINT32_FIELD(this, kKindSpecificFlags2Offset);
+ int updated = BackEdgesPatchedForOSRField::update(previous, value);
+ WRITE_UINT32_FIELD(this, kKindSpecificFlags2Offset, updated);
+}
+
+
+
+CheckType Code::check_type() {
+ ASSERT(is_call_stub() || is_keyed_call_stub());
+ byte type = READ_BYTE_FIELD(this, kCheckTypeOffset);
+ return static_cast<CheckType>(type);
+}
+
+
+void Code::set_check_type(CheckType value) {
+ ASSERT(is_call_stub() || is_keyed_call_stub());
+ WRITE_BYTE_FIELD(this, kCheckTypeOffset, value);
+}
+
+
+byte Code::to_boolean_state() {
+ return extended_extra_ic_state();
+}
+
+
+bool Code::has_function_cache() {
+ ASSERT(kind() == STUB);
+ return HasFunctionCacheField::decode(
+ READ_UINT32_FIELD(this, kKindSpecificFlags1Offset));
+}
+
+
+void Code::set_has_function_cache(bool flag) {
+ ASSERT(kind() == STUB);
+ int previous = READ_UINT32_FIELD(this, kKindSpecificFlags1Offset);
+ int updated = HasFunctionCacheField::update(previous, flag);
+ WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
+}
+
+
+bool Code::marked_for_deoptimization() {
+ ASSERT(kind() == OPTIMIZED_FUNCTION);
+ return MarkedForDeoptimizationField::decode(
+ READ_UINT32_FIELD(this, kKindSpecificFlags1Offset));
+}
+
+
+void Code::set_marked_for_deoptimization(bool flag) {
+ ASSERT(kind() == OPTIMIZED_FUNCTION);
+ int previous = READ_UINT32_FIELD(this, kKindSpecificFlags1Offset);
+ int updated = MarkedForDeoptimizationField::update(previous, flag);
+ WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
+}
+
+
+bool Code::is_inline_cache_stub() {
+ Kind kind = this->kind();
+ switch (kind) {
+#define CASE(name) case name: return true;
+ IC_KIND_LIST(CASE)
+#undef CASE
+ default: return false;
+ }
+}
+
+
+bool Code::is_debug_break() {
+ return ic_state() == DEBUG_STUB && extra_ic_state() == DEBUG_BREAK;
+}
+
+
+Code::Flags Code::ComputeFlags(Kind kind,
+ InlineCacheState ic_state,
+ ExtraICState extra_ic_state,
+ StubType type,
+ int argc,
+ InlineCacheHolderFlag holder) {
+ ASSERT(argc <= Code::kMaxArguments);
+ // Since the extended extra ic state overlaps with the argument count
+ // for CALL_ICs, do so checks to make sure that they don't interfere.
+ ASSERT((kind != Code::CALL_IC &&
+ kind != Code::KEYED_CALL_IC) ||
+ (ExtraICStateField::encode(extra_ic_state) | true));
+ // Compute the bit mask.
+ unsigned int bits = KindField::encode(kind)
+ | ICStateField::encode(ic_state)
+ | TypeField::encode(type)
+ | ExtendedExtraICStateField::encode(extra_ic_state)
+ | CacheHolderField::encode(holder);
+ if (!Code::needs_extended_extra_ic_state(kind)) {
+ bits |= (argc << kArgumentsCountShift);
+ }
+ return static_cast<Flags>(bits);
+}
+
+
+Code::Flags Code::ComputeMonomorphicFlags(Kind kind,
+ ExtraICState extra_ic_state,
+ StubType type,
+ int argc,
+ InlineCacheHolderFlag holder) {
+ return ComputeFlags(kind, MONOMORPHIC, extra_ic_state, type, argc, holder);
+}
+
+
+Code::Kind Code::ExtractKindFromFlags(Flags flags) {
+ return KindField::decode(flags);
+}
+
+
+InlineCacheState Code::ExtractICStateFromFlags(Flags flags) {
+ return ICStateField::decode(flags);
+}
+
+
+Code::ExtraICState Code::ExtractExtraICStateFromFlags(Flags flags) {
+ return ExtraICStateField::decode(flags);
+}
+
+
+Code::ExtraICState Code::ExtractExtendedExtraICStateFromFlags(
+ Flags flags) {
+ return ExtendedExtraICStateField::decode(flags);
+}
+
+
+Code::StubType Code::ExtractTypeFromFlags(Flags flags) {
+ return TypeField::decode(flags);
+}
+
+
+int Code::ExtractArgumentsCountFromFlags(Flags flags) {
+ return (flags & kArgumentsCountMask) >> kArgumentsCountShift;
+}
+
+
+InlineCacheHolderFlag Code::ExtractCacheHolderFromFlags(Flags flags) {
+ return CacheHolderField::decode(flags);
+}
+
+
+Code::Flags Code::RemoveTypeFromFlags(Flags flags) {
+ int bits = flags & ~TypeField::kMask;
+ return static_cast<Flags>(bits);
+}
+
+
+Code* Code::GetCodeFromTargetAddress(Address address) {
+ HeapObject* code = HeapObject::FromAddress(address - Code::kHeaderSize);
+ // GetCodeFromTargetAddress might be called when marking objects during mark
+ // sweep. reinterpret_cast is therefore used instead of the more appropriate
+ // Code::cast. Code::cast does not work when the object's map is
+ // marked.
+ Code* result = reinterpret_cast<Code*>(code);
+ return result;
+}
+
+
+Object* Code::GetObjectFromEntryAddress(Address location_of_address) {
+ return HeapObject::
+ FromAddress(Memory::Address_at(location_of_address) - Code::kHeaderSize);
+}
+
+
+Object* Map::prototype() {
+ return READ_FIELD(this, kPrototypeOffset);
+}
+
+
+void Map::set_prototype(Object* value, WriteBarrierMode mode) {
+ ASSERT(value->IsNull() || value->IsJSReceiver());
+ WRITE_FIELD(this, kPrototypeOffset, value);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kPrototypeOffset, value, mode);
+}
+
+
+// If the descriptor is using the empty transition array, install a new empty
+// transition array that will have place for an element transition.
+static MaybeObject* EnsureHasTransitionArray(Map* map) {
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions;
+ if (!map->HasTransitionArray()) {
+ maybe_transitions = TransitionArray::Allocate(0);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ transitions->set_back_pointer_storage(map->GetBackPointer());
+ } else if (!map->transitions()->IsFullTransitionArray()) {
+ maybe_transitions = map->transitions()->ExtendToFullTransitionArray();
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ } else {
+ return map;
+ }
+ map->set_transitions(transitions);
+ return transitions;
+}
+
+
+void Map::InitializeDescriptors(DescriptorArray* descriptors) {
+ int len = descriptors->number_of_descriptors();
+ set_instance_descriptors(descriptors);
+ SetNumberOfOwnDescriptors(len);
+}
+
+
+ACCESSORS(Map, instance_descriptors, DescriptorArray, kDescriptorsOffset)
+
+
+void Map::set_bit_field3(uint32_t bits) {
+ // Ensure the upper 2 bits have the same value by sign extending it. This is
+ // necessary to be able to use the 31st bit.
+ int value = bits << 1;
+ WRITE_FIELD(this, kBitField3Offset, Smi::FromInt(value >> 1));
+}
+
+
+uint32_t Map::bit_field3() {
+ Object* value = READ_FIELD(this, kBitField3Offset);
+ return Smi::cast(value)->value();
+}
+
+
+void Map::ClearTransitions(Heap* heap, WriteBarrierMode mode) {
+ Object* back_pointer = GetBackPointer();
+
+ if (Heap::ShouldZapGarbage() && HasTransitionArray()) {
+ ZapTransitions();
+ }
+
+ WRITE_FIELD(this, kTransitionsOrBackPointerOffset, back_pointer);
+ CONDITIONAL_WRITE_BARRIER(
+ heap, this, kTransitionsOrBackPointerOffset, back_pointer, mode);
+}
+
+
+void Map::AppendDescriptor(Descriptor* desc,
+ const DescriptorArray::WhitenessWitness& witness) {
+ DescriptorArray* descriptors = instance_descriptors();
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ ASSERT(descriptors->number_of_descriptors() == number_of_own_descriptors);
+ descriptors->Append(desc, witness);
+ SetNumberOfOwnDescriptors(number_of_own_descriptors + 1);
+}
+
+
+Object* Map::GetBackPointer() {
+ Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset);
+ if (object->IsDescriptorArray()) {
+ return TransitionArray::cast(object)->back_pointer_storage();
+ } else {
+ ASSERT(object->IsMap() || object->IsUndefined());
+ return object;
+ }
+}
+
+
+bool Map::HasElementsTransition() {
+ return HasTransitionArray() && transitions()->HasElementsTransition();
+}
+
+
+bool Map::HasTransitionArray() {
+ Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset);
+ return object->IsTransitionArray();
+}
+
+
+Map* Map::elements_transition_map() {
+ int index = transitions()->Search(GetHeap()->elements_transition_symbol());
+ return transitions()->GetTarget(index);
+}
+
+
+bool Map::CanHaveMoreTransitions() {
+ if (!HasTransitionArray()) return true;
+ return FixedArray::SizeFor(transitions()->length() +
+ TransitionArray::kTransitionSize)
+ <= Page::kMaxNonCodeHeapObjectSize;
+}
+
+
+MaybeObject* Map::AddTransition(Name* key,
+ Map* target,
+ SimpleTransitionFlag flag) {
+ if (HasTransitionArray()) return transitions()->CopyInsert(key, target);
+ return TransitionArray::NewWith(flag, key, target, GetBackPointer());
+}
+
+
+void Map::SetTransition(int transition_index, Map* target) {
+ transitions()->SetTarget(transition_index, target);
+}
+
+
+Map* Map::GetTransition(int transition_index) {
+ return transitions()->GetTarget(transition_index);
+}
+
+
+MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) {
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions = AddTransition(
+ GetHeap()->elements_transition_symbol(),
+ transitioned_map,
+ FULL_TRANSITION);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ set_transitions(transitions);
+ return transitions;
+}
+
+
+FixedArray* Map::GetPrototypeTransitions() {
+ if (!HasTransitionArray()) return GetHeap()->empty_fixed_array();
+ if (!transitions()->HasPrototypeTransitions()) {
+ return GetHeap()->empty_fixed_array();
+ }
+ return transitions()->GetPrototypeTransitions();
+}
+
+
+MaybeObject* Map::SetPrototypeTransitions(FixedArray* proto_transitions) {
+ MaybeObject* allow_prototype = EnsureHasTransitionArray(this);
+ if (allow_prototype->IsFailure()) return allow_prototype;
+ int old_number_of_transitions = NumberOfProtoTransitions();
+#ifdef DEBUG
+ if (HasPrototypeTransitions()) {
+ ASSERT(GetPrototypeTransitions() != proto_transitions);
+ ZapPrototypeTransitions();
+ }
+#endif
+ transitions()->SetPrototypeTransitions(proto_transitions);
+ SetNumberOfProtoTransitions(old_number_of_transitions);
+ return this;
+}
+
+
+bool Map::HasPrototypeTransitions() {
+ return HasTransitionArray() && transitions()->HasPrototypeTransitions();
+}
+
+
+TransitionArray* Map::transitions() {
+ ASSERT(HasTransitionArray());
+ Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset);
+ return TransitionArray::cast(object);
+}
+
+
+void Map::set_transitions(TransitionArray* transition_array,
+ WriteBarrierMode mode) {
+ // Transition arrays are not shared. When one is replaced, it should not
+ // keep referenced objects alive, so we zap it.
+ // When there is another reference to the array somewhere (e.g. a handle),
+ // not zapping turns from a waste of memory into a source of crashes.
+ if (HasTransitionArray()) {
+ ASSERT(transitions() != transition_array);
+ ZapTransitions();
+ }
+
+ WRITE_FIELD(this, kTransitionsOrBackPointerOffset, transition_array);
+ CONDITIONAL_WRITE_BARRIER(
+ GetHeap(), this, kTransitionsOrBackPointerOffset, transition_array, mode);
+}
+
+
+void Map::init_back_pointer(Object* undefined) {
+ ASSERT(undefined->IsUndefined());
+ WRITE_FIELD(this, kTransitionsOrBackPointerOffset, undefined);
+}
+
+
+void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
+ ASSERT(instance_type() >= FIRST_JS_RECEIVER_TYPE);
+ ASSERT((value->IsUndefined() && GetBackPointer()->IsMap()) ||
+ (value->IsMap() && GetBackPointer()->IsUndefined()));
+ Object* object = READ_FIELD(this, kTransitionsOrBackPointerOffset);
+ if (object->IsTransitionArray()) {
+ TransitionArray::cast(object)->set_back_pointer_storage(value);
+ } else {
+ WRITE_FIELD(this, kTransitionsOrBackPointerOffset, value);
+ CONDITIONAL_WRITE_BARRIER(
+ GetHeap(), this, kTransitionsOrBackPointerOffset, value, mode);
+ }
+}
+
+
+// Can either be Smi (no transitions), normal transition array, or a transition
+// array with the header overwritten as a Smi (thus iterating).
+TransitionArray* Map::unchecked_transition_array() {
+ Object* object = *HeapObject::RawField(this,
+ Map::kTransitionsOrBackPointerOffset);
+ TransitionArray* transition_array = static_cast<TransitionArray*>(object);
+ return transition_array;
+}
+
+
+HeapObject* Map::UncheckedPrototypeTransitions() {
+ ASSERT(HasTransitionArray());
+ ASSERT(unchecked_transition_array()->HasPrototypeTransitions());
+ return unchecked_transition_array()->UncheckedPrototypeTransitions();
+}
+
+
+ACCESSORS(Map, code_cache, Object, kCodeCacheOffset)
+ACCESSORS(Map, dependent_code, DependentCode, kDependentCodeOffset)
+ACCESSORS(Map, constructor, Object, kConstructorOffset)
+
+ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset)
+ACCESSORS(JSFunction, literals_or_bindings, FixedArray, kLiteralsOffset)
+ACCESSORS(JSFunction, next_function_link, Object, kNextFunctionLinkOffset)
+
+ACCESSORS(GlobalObject, builtins, JSBuiltinsObject, kBuiltinsOffset)
+ACCESSORS(GlobalObject, native_context, Context, kNativeContextOffset)
+ACCESSORS(GlobalObject, global_context, Context, kGlobalContextOffset)
+ACCESSORS(GlobalObject, global_receiver, JSObject, kGlobalReceiverOffset)
+
+ACCESSORS(JSGlobalProxy, native_context, Object, kNativeContextOffset)
+
+ACCESSORS(AccessorInfo, name, Object, kNameOffset)
+ACCESSORS_TO_SMI(AccessorInfo, flag, kFlagOffset)
+ACCESSORS(AccessorInfo, expected_receiver_type, Object,
+ kExpectedReceiverTypeOffset)
+
+ACCESSORS(DeclaredAccessorDescriptor, serialized_data, ByteArray,
+ kSerializedDataOffset)
+
+ACCESSORS(DeclaredAccessorInfo, descriptor, DeclaredAccessorDescriptor,
+ kDescriptorOffset)
+
+ACCESSORS(ExecutableAccessorInfo, getter, Object, kGetterOffset)
+ACCESSORS(ExecutableAccessorInfo, setter, Object, kSetterOffset)
+ACCESSORS(ExecutableAccessorInfo, data, Object, kDataOffset)
+
+ACCESSORS(Box, value, Object, kValueOffset)
+
+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)
+
+ACCESSORS(InterceptorInfo, getter, Object, kGetterOffset)
+ACCESSORS(InterceptorInfo, setter, Object, kSetterOffset)
+ACCESSORS(InterceptorInfo, query, Object, kQueryOffset)
+ACCESSORS(InterceptorInfo, deleter, Object, kDeleterOffset)
+ACCESSORS(InterceptorInfo, enumerator, Object, kEnumeratorOffset)
+ACCESSORS(InterceptorInfo, data, Object, kDataOffset)
+
+ACCESSORS(CallHandlerInfo, callback, Object, kCallbackOffset)
+ACCESSORS(CallHandlerInfo, data, Object, kDataOffset)
+
+ACCESSORS(TemplateInfo, tag, Object, kTagOffset)
+ACCESSORS(TemplateInfo, property_list, Object, kPropertyListOffset)
+
+ACCESSORS(FunctionTemplateInfo, serial_number, Object, kSerialNumberOffset)
+ACCESSORS(FunctionTemplateInfo, call_code, Object, kCallCodeOffset)
+ACCESSORS(FunctionTemplateInfo, property_accessors, Object,
+ kPropertyAccessorsOffset)
+ACCESSORS(FunctionTemplateInfo, prototype_template, Object,
+ kPrototypeTemplateOffset)
+ACCESSORS(FunctionTemplateInfo, parent_template, Object, kParentTemplateOffset)
+ACCESSORS(FunctionTemplateInfo, named_property_handler, Object,
+ kNamedPropertyHandlerOffset)
+ACCESSORS(FunctionTemplateInfo, indexed_property_handler, Object,
+ kIndexedPropertyHandlerOffset)
+ACCESSORS(FunctionTemplateInfo, instance_template, Object,
+ kInstanceTemplateOffset)
+ACCESSORS(FunctionTemplateInfo, class_name, Object, kClassNameOffset)
+ACCESSORS(FunctionTemplateInfo, signature, Object, kSignatureOffset)
+ACCESSORS(FunctionTemplateInfo, instance_call_handler, Object,
+ kInstanceCallHandlerOffset)
+ACCESSORS(FunctionTemplateInfo, access_check_info, Object,
+ kAccessCheckInfoOffset)
+ACCESSORS_TO_SMI(FunctionTemplateInfo, flag, kFlagOffset)
+
+ACCESSORS(ObjectTemplateInfo, constructor, Object, kConstructorOffset)
+ACCESSORS(ObjectTemplateInfo, internal_field_count, Object,
+ kInternalFieldCountOffset)
+
+ACCESSORS(SignatureInfo, receiver, Object, kReceiverOffset)
+ACCESSORS(SignatureInfo, args, Object, kArgsOffset)
+
+ACCESSORS(TypeSwitchInfo, types, Object, kTypesOffset)
+
+ACCESSORS(AllocationSite, transition_info, Object, kTransitionInfoOffset)
+ACCESSORS(AllocationSite, weak_next, Object, kWeakNextOffset)
+ACCESSORS(AllocationMemento, allocation_site, Object, kAllocationSiteOffset)
+
+ACCESSORS(Script, source, Object, kSourceOffset)
+ACCESSORS(Script, name, Object, kNameOffset)
+ACCESSORS(Script, id, Smi, kIdOffset)
+ACCESSORS_TO_SMI(Script, line_offset, kLineOffsetOffset)
+ACCESSORS_TO_SMI(Script, column_offset, kColumnOffsetOffset)
+ACCESSORS(Script, data, Object, kDataOffset)
+ACCESSORS(Script, context_data, Object, kContextOffset)
+ACCESSORS(Script, wrapper, Foreign, kWrapperOffset)
+ACCESSORS_TO_SMI(Script, type, kTypeOffset)
+ACCESSORS(Script, line_ends, Object, kLineEndsOffset)
+ACCESSORS(Script, eval_from_shared, Object, kEvalFromSharedOffset)
+ACCESSORS_TO_SMI(Script, eval_from_instructions_offset,
+ kEvalFrominstructionsOffsetOffset)
+ACCESSORS_TO_SMI(Script, flags, kFlagsOffset)
+BOOL_ACCESSORS(Script, flags, is_shared_cross_origin, kIsSharedCrossOriginBit)
+
+Script::CompilationType Script::compilation_type() {
+ return BooleanBit::get(flags(), kCompilationTypeBit) ?
+ COMPILATION_TYPE_EVAL : COMPILATION_TYPE_HOST;
+}
+void Script::set_compilation_type(CompilationType type) {
+ set_flags(BooleanBit::set(flags(), kCompilationTypeBit,
+ type == COMPILATION_TYPE_EVAL));
+}
+Script::CompilationState Script::compilation_state() {
+ return BooleanBit::get(flags(), kCompilationStateBit) ?
+ COMPILATION_STATE_COMPILED : COMPILATION_STATE_INITIAL;
+}
+void Script::set_compilation_state(CompilationState state) {
+ set_flags(BooleanBit::set(flags(), kCompilationStateBit,
+ state == COMPILATION_STATE_COMPILED));
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ACCESSORS(DebugInfo, shared, SharedFunctionInfo, kSharedFunctionInfoIndex)
+ACCESSORS(DebugInfo, original_code, Code, kOriginalCodeIndex)
+ACCESSORS(DebugInfo, code, Code, kPatchedCodeIndex)
+ACCESSORS(DebugInfo, break_points, FixedArray, kBreakPointsStateIndex)
+
+ACCESSORS_TO_SMI(BreakPointInfo, code_position, kCodePositionIndex)
+ACCESSORS_TO_SMI(BreakPointInfo, source_position, kSourcePositionIndex)
+ACCESSORS_TO_SMI(BreakPointInfo, statement_position, kStatementPositionIndex)
+ACCESSORS(BreakPointInfo, break_point_objects, Object, kBreakPointObjectsIndex)
+#endif
+
+ACCESSORS(SharedFunctionInfo, name, Object, kNameOffset)
+ACCESSORS(SharedFunctionInfo, optimized_code_map, Object,
+ kOptimizedCodeMapOffset)
+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)
+ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
+ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
+ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
+SMI_ACCESSORS(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset)
+
+
+SMI_ACCESSORS(FunctionTemplateInfo, length, kLengthOffset)
+BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype,
+ kHiddenPrototypeBit)
+BOOL_ACCESSORS(FunctionTemplateInfo, flag, undetectable, kUndetectableBit)
+BOOL_ACCESSORS(FunctionTemplateInfo, flag, needs_access_check,
+ kNeedsAccessCheckBit)
+BOOL_ACCESSORS(FunctionTemplateInfo, flag, read_only_prototype,
+ kReadOnlyPrototypeBit)
+BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_expression,
+ kIsExpressionBit)
+BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_toplevel,
+ kIsTopLevelBit)
+
+BOOL_ACCESSORS(SharedFunctionInfo,
+ compiler_hints,
+ allows_lazy_compilation,
+ kAllowLazyCompilation)
+BOOL_ACCESSORS(SharedFunctionInfo,
+ compiler_hints,
+ allows_lazy_compilation_without_context,
+ kAllowLazyCompilationWithoutContext)
+BOOL_ACCESSORS(SharedFunctionInfo,
+ compiler_hints,
+ uses_arguments,
+ kUsesArguments)
+BOOL_ACCESSORS(SharedFunctionInfo,
+ compiler_hints,
+ has_duplicate_parameters,
+ kHasDuplicateParameters)
+
+
+#if V8_HOST_ARCH_32_BIT
+SMI_ACCESSORS(SharedFunctionInfo, length, kLengthOffset)
+SMI_ACCESSORS(SharedFunctionInfo, formal_parameter_count,
+ kFormalParameterCountOffset)
+SMI_ACCESSORS(SharedFunctionInfo, expected_nof_properties,
+ kExpectedNofPropertiesOffset)
+SMI_ACCESSORS(SharedFunctionInfo, num_literals, kNumLiteralsOffset)
+SMI_ACCESSORS(SharedFunctionInfo, start_position_and_type,
+ kStartPositionAndTypeOffset)
+SMI_ACCESSORS(SharedFunctionInfo, end_position, kEndPositionOffset)
+SMI_ACCESSORS(SharedFunctionInfo, function_token_position,
+ kFunctionTokenPositionOffset)
+SMI_ACCESSORS(SharedFunctionInfo, compiler_hints,
+ kCompilerHintsOffset)
+SMI_ACCESSORS(SharedFunctionInfo, opt_count, kOptCountOffset)
+SMI_ACCESSORS(SharedFunctionInfo, counters, kCountersOffset)
+
+#else
+
+#define PSEUDO_SMI_ACCESSORS_LO(holder, name, offset) \
+ STATIC_ASSERT(holder::offset % kPointerSize == 0); \
+ int holder::name() { \
+ int value = READ_INT_FIELD(this, offset); \
+ ASSERT(kHeapObjectTag == 1); \
+ ASSERT((value & kHeapObjectTag) == 0); \
+ return value >> 1; \
+ } \
+ void holder::set_##name(int value) { \
+ ASSERT(kHeapObjectTag == 1); \
+ ASSERT((value & 0xC0000000) == 0xC0000000 || \
+ (value & 0xC0000000) == 0x000000000); \
+ WRITE_INT_FIELD(this, \
+ offset, \
+ (value << 1) & ~kHeapObjectTag); \
+ }
+
+#define PSEUDO_SMI_ACCESSORS_HI(holder, name, offset) \
+ STATIC_ASSERT(holder::offset % kPointerSize == kIntSize); \
+ INT_ACCESSORS(holder, name, offset)
+
+
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, length, kLengthOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
+ formal_parameter_count,
+ kFormalParameterCountOffset)
+
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
+ expected_nof_properties,
+ kExpectedNofPropertiesOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, num_literals, kNumLiteralsOffset)
+
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, end_position, kEndPositionOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
+ start_position_and_type,
+ kStartPositionAndTypeOffset)
+
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
+ function_token_position,
+ kFunctionTokenPositionOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
+ compiler_hints,
+ kCompilerHintsOffset)
+
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, opt_count, kOptCountOffset)
+
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, counters, kCountersOffset)
+
+#endif
+
+
+int SharedFunctionInfo::construction_count() {
+ return READ_BYTE_FIELD(this, kConstructionCountOffset);
+}
+
+
+void SharedFunctionInfo::set_construction_count(int value) {
+ ASSERT(0 <= value && value < 256);
+ WRITE_BYTE_FIELD(this, kConstructionCountOffset, static_cast<byte>(value));
+}
+
+
+BOOL_ACCESSORS(SharedFunctionInfo,
+ compiler_hints,
+ live_objects_may_exist,
+ kLiveObjectsMayExist)
+
+
+bool SharedFunctionInfo::IsInobjectSlackTrackingInProgress() {
+ return initial_map() != GetHeap()->undefined_value();
+}
+
+
+BOOL_GETTER(SharedFunctionInfo,
+ compiler_hints,
+ optimization_disabled,
+ kOptimizationDisabled)
+
+
+void SharedFunctionInfo::set_optimization_disabled(bool disable) {
+ set_compiler_hints(BooleanBit::set(compiler_hints(),
+ kOptimizationDisabled,
+ disable));
+ // If disabling optimizations we reflect that in the code object so
+ // it will not be counted as optimizable code.
+ if ((code()->kind() == Code::FUNCTION) && disable) {
+ code()->set_optimizable(false);
+ }
+}
+
+
+int SharedFunctionInfo::profiler_ticks() {
+ if (code()->kind() != Code::FUNCTION) return 0;
+ return code()->profiler_ticks();
+}
+
+
+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, is_function, kIsFunction)
+BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_optimize,
+ kDontOptimize)
+BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_inline, kDontInline)
+BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_cache, kDontCache)
+BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, dont_flush, kDontFlush)
+BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_generator, kIsGenerator)
+
+void SharedFunctionInfo::BeforeVisitingPointers() {
+ if (IsInobjectSlackTrackingInProgress()) DetachInitialMap();
+}
+
+
+ACCESSORS(CodeCache, default_cache, FixedArray, kDefaultCacheOffset)
+ACCESSORS(CodeCache, normal_type_cache, Object, kNormalTypeCacheOffset)
+
+ACCESSORS(PolymorphicCodeCache, cache, Object, kCacheOffset)
+
+bool Script::HasValidSource() {
+ Object* src = this->source();
+ if (!src->IsString()) return true;
+ String* src_str = String::cast(src);
+ if (!StringShape(src_str).IsExternal()) return true;
+ if (src_str->IsOneByteRepresentation()) {
+ return ExternalAsciiString::cast(src)->resource() != NULL;
+ } else if (src_str->IsTwoByteRepresentation()) {
+ return ExternalTwoByteString::cast(src)->resource() != NULL;
+ }
+ return true;
+}
+
+
+void SharedFunctionInfo::DontAdaptArguments() {
+ ASSERT(code()->kind() == Code::BUILTIN);
+ set_formal_parameter_count(kDontAdaptArgumentsSentinel);
+}
+
+
+int SharedFunctionInfo::start_position() {
+ return start_position_and_type() >> kStartPositionShift;
+}
+
+
+void SharedFunctionInfo::set_start_position(int start_position) {
+ set_start_position_and_type((start_position << kStartPositionShift)
+ | (start_position_and_type() & ~kStartPositionMask));
+}
+
+
+Code* SharedFunctionInfo::code() {
+ return Code::cast(READ_FIELD(this, kCodeOffset));
+}
+
+
+void SharedFunctionInfo::set_code(Code* value, WriteBarrierMode mode) {
+ WRITE_FIELD(this, kCodeOffset, value);
+ CONDITIONAL_WRITE_BARRIER(value->GetHeap(), this, kCodeOffset, value, mode);
+}
+
+
+void SharedFunctionInfo::ReplaceCode(Code* value) {
+ // If the GC metadata field is already used then the function was
+ // enqueued as a code flushing candidate and we remove it now.
+ if (code()->gc_metadata() != NULL) {
+ CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
+ flusher->EvictCandidate(this);
+ }
+
+ ASSERT(code()->gc_metadata() == NULL && value->gc_metadata() == NULL);
+ set_code(value);
+}
+
+
+ScopeInfo* SharedFunctionInfo::scope_info() {
+ return reinterpret_cast<ScopeInfo*>(READ_FIELD(this, kScopeInfoOffset));
+}
+
+
+void SharedFunctionInfo::set_scope_info(ScopeInfo* value,
+ WriteBarrierMode mode) {
+ WRITE_FIELD(this, kScopeInfoOffset, reinterpret_cast<Object*>(value));
+ CONDITIONAL_WRITE_BARRIER(GetHeap(),
+ this,
+ kScopeInfoOffset,
+ reinterpret_cast<Object*>(value),
+ mode);
+}
+
+
+bool SharedFunctionInfo::is_compiled() {
+ return code() !=
+ Isolate::Current()->builtins()->builtin(Builtins::kLazyCompile);
+}
+
+
+bool SharedFunctionInfo::IsApiFunction() {
+ return function_data()->IsFunctionTemplateInfo();
+}
+
+
+FunctionTemplateInfo* SharedFunctionInfo::get_api_func_data() {
+ ASSERT(IsApiFunction());
+ return FunctionTemplateInfo::cast(function_data());
+}
+
+
+bool SharedFunctionInfo::HasBuiltinFunctionId() {
+ return function_data()->IsSmi();
+}
+
+
+BuiltinFunctionId SharedFunctionInfo::builtin_function_id() {
+ ASSERT(HasBuiltinFunctionId());
+ return static_cast<BuiltinFunctionId>(Smi::cast(function_data())->value());
+}
+
+
+int SharedFunctionInfo::ic_age() {
+ return ICAgeBits::decode(counters());
+}
+
+
+void SharedFunctionInfo::set_ic_age(int ic_age) {
+ set_counters(ICAgeBits::update(counters(), ic_age));
+}
+
+
+int SharedFunctionInfo::deopt_count() {
+ return DeoptCountBits::decode(counters());
+}
+
+
+void SharedFunctionInfo::set_deopt_count(int deopt_count) {
+ set_counters(DeoptCountBits::update(counters(), deopt_count));
+}
+
+
+void SharedFunctionInfo::increment_deopt_count() {
+ int value = counters();
+ int deopt_count = DeoptCountBits::decode(value);
+ deopt_count = (deopt_count + 1) & DeoptCountBits::kMax;
+ set_counters(DeoptCountBits::update(value, deopt_count));
+}
+
+
+int SharedFunctionInfo::opt_reenable_tries() {
+ return OptReenableTriesBits::decode(counters());
+}
+
+
+void SharedFunctionInfo::set_opt_reenable_tries(int tries) {
+ set_counters(OptReenableTriesBits::update(counters(), tries));
+}
+
+
+bool SharedFunctionInfo::has_deoptimization_support() {
+ Code* code = this->code();
+ return code->kind() == Code::FUNCTION && code->has_deoptimization_support();
+}
+
+
+void SharedFunctionInfo::TryReenableOptimization() {
+ int tries = opt_reenable_tries();
+ set_opt_reenable_tries((tries + 1) & OptReenableTriesBits::kMax);
+ // We reenable optimization whenever the number of tries is a large
+ // enough power of 2.
+ if (tries >= 16 && (((tries - 1) & tries) == 0)) {
+ set_optimization_disabled(false);
+ set_opt_count(0);
+ set_deopt_count(0);
+ code()->set_optimizable(true);
+ }
+}
+
+
+bool JSFunction::IsBuiltin() {
+ return context()->global_object()->IsJSBuiltinsObject();
+}
+
+
+bool JSFunction::NeedsArgumentsAdaption() {
+ return shared()->formal_parameter_count() !=
+ SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+}
+
+
+bool JSFunction::IsOptimized() {
+ return code()->kind() == Code::OPTIMIZED_FUNCTION;
+}
+
+
+bool JSFunction::IsOptimizable() {
+ return code()->kind() == Code::FUNCTION && code()->optimizable();
+}
+
+
+bool JSFunction::IsMarkedForLazyRecompilation() {
+ return code() == GetIsolate()->builtins()->builtin(Builtins::kLazyRecompile);
+}
+
+
+bool JSFunction::IsMarkedForInstallingRecompiledCode() {
+ return code() == GetIsolate()->builtins()->builtin(
+ Builtins::kInstallRecompiledCode);
+}
+
+
+bool JSFunction::IsMarkedForParallelRecompilation() {
+ return code() == GetIsolate()->builtins()->builtin(
+ Builtins::kParallelRecompile);
+}
+
+
+bool JSFunction::IsInRecompileQueue() {
+ return code() == GetIsolate()->builtins()->builtin(
+ Builtins::kInRecompileQueue);
+}
+
+
+Code* JSFunction::code() {
+ return Code::cast(
+ Code::GetObjectFromEntryAddress(FIELD_ADDR(this, kCodeEntryOffset)));
+}
+
+
+void JSFunction::set_code(Code* value) {
+ 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);
+}
+
+
+void JSFunction::set_code_no_write_barrier(Code* value) {
+ ASSERT(!HEAP->InNewSpace(value));
+ Address entry = value->entry();
+ WRITE_INTPTR_FIELD(this, kCodeEntryOffset, reinterpret_cast<intptr_t>(entry));
+}
+
+
+void JSFunction::ReplaceCode(Code* code) {
+ bool was_optimized = IsOptimized();
+ bool is_optimized = code->kind() == Code::OPTIMIZED_FUNCTION;
+
+ set_code(code);
+
+ // Add/remove the function from the list of optimized functions for this
+ // context based on the state change.
+ if (!was_optimized && is_optimized) {
+ context()->native_context()->AddOptimizedFunction(this);
+ }
+ if (was_optimized && !is_optimized) {
+ context()->native_context()->RemoveOptimizedFunction(this);
+ }
+}
+
+
+Context* JSFunction::context() {
+ return Context::cast(READ_FIELD(this, kContextOffset));
+}
+
+
+void JSFunction::set_context(Object* value) {
+ ASSERT(value->IsUndefined() || value->IsContext());
+ WRITE_FIELD(this, kContextOffset, value);
+ WRITE_BARRIER(GetHeap(), this, kContextOffset, value);
+}
+
+ACCESSORS(JSFunction, prototype_or_initial_map, Object,
+ kPrototypeOrInitialMapOffset)
+
+
+Map* JSFunction::initial_map() {
+ return Map::cast(prototype_or_initial_map());
+}
+
+
+void JSFunction::set_initial_map(Map* value) {
+ set_prototype_or_initial_map(value);
+}
+
+
+bool JSFunction::has_initial_map() {
+ return prototype_or_initial_map()->IsMap();
+}
+
+
+bool JSFunction::has_instance_prototype() {
+ return has_initial_map() || !prototype_or_initial_map()->IsTheHole();
+}
+
+
+bool JSFunction::has_prototype() {
+ return map()->has_non_instance_prototype() || has_instance_prototype();
+}
+
+
+Object* JSFunction::instance_prototype() {
+ ASSERT(has_instance_prototype());
+ if (has_initial_map()) return initial_map()->prototype();
+ // When there is no initial map and the prototype is a JSObject, the
+ // initial map field is used for the prototype field.
+ return prototype_or_initial_map();
+}
+
+
+Object* JSFunction::prototype() {
+ ASSERT(has_prototype());
+ // If the function's prototype property has been set to a non-JSObject
+ // value, that value is stored in the constructor field of the map.
+ if (map()->has_non_instance_prototype()) return map()->constructor();
+ return instance_prototype();
+}
+
+
+bool JSFunction::should_have_prototype() {
+ return map()->function_with_prototype();
+}
+
+
+bool JSFunction::is_compiled() {
+ return code() != GetIsolate()->builtins()->builtin(Builtins::kLazyCompile);
+}
+
+
+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();
+}
+
+
+Object* JSBuiltinsObject::javascript_builtin(Builtins::JavaScript id) {
+ ASSERT(id < kJSBuiltinsCount); // id is unsigned.
+ return READ_FIELD(this, OffsetOfFunctionWithId(id));
+}
+
+
+void JSBuiltinsObject::set_javascript_builtin(Builtins::JavaScript id,
+ Object* value) {
+ ASSERT(id < kJSBuiltinsCount); // id is unsigned.
+ WRITE_FIELD(this, OffsetOfFunctionWithId(id), value);
+ WRITE_BARRIER(GetHeap(), this, OffsetOfFunctionWithId(id), value);
+}
+
+
+Code* JSBuiltinsObject::javascript_builtin_code(Builtins::JavaScript id) {
+ ASSERT(id < kJSBuiltinsCount); // id is unsigned.
+ return Code::cast(READ_FIELD(this, OffsetOfCodeWithId(id)));
+}
+
+
+void JSBuiltinsObject::set_javascript_builtin_code(Builtins::JavaScript id,
+ Code* value) {
+ ASSERT(id < kJSBuiltinsCount); // id is unsigned.
+ WRITE_FIELD(this, OffsetOfCodeWithId(id), value);
+ ASSERT(!HEAP->InNewSpace(value));
+}
+
+
+ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
+ACCESSORS(JSProxy, hash, Object, kHashOffset)
+ACCESSORS(JSFunctionProxy, call_trap, Object, kCallTrapOffset)
+ACCESSORS(JSFunctionProxy, construct_trap, Object, kConstructTrapOffset)
+
+
+void JSProxy::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);
+ }
+}
+
+
+ACCESSORS(JSSet, table, Object, kTableOffset)
+ACCESSORS(JSMap, table, Object, kTableOffset)
+ACCESSORS(JSWeakCollection, table, Object, kTableOffset)
+ACCESSORS(JSWeakCollection, next, Object, kNextOffset)
+
+
+Address Foreign::foreign_address() {
+ return AddressFrom<Address>(READ_INTPTR_FIELD(this, kForeignAddressOffset));
+}
+
+
+void Foreign::set_foreign_address(Address value) {
+ WRITE_INTPTR_FIELD(this, kForeignAddressOffset, OffsetFrom(value));
+}
+
+
+ACCESSORS(JSGeneratorObject, function, JSFunction, kFunctionOffset)
+ACCESSORS(JSGeneratorObject, context, Context, kContextOffset)
+ACCESSORS(JSGeneratorObject, receiver, Object, kReceiverOffset)
+SMI_ACCESSORS(JSGeneratorObject, continuation, kContinuationOffset)
+ACCESSORS(JSGeneratorObject, operand_stack, FixedArray, kOperandStackOffset)
+SMI_ACCESSORS(JSGeneratorObject, stack_handler_index, kStackHandlerIndexOffset)
+
+
+JSGeneratorObject* JSGeneratorObject::cast(Object* obj) {
+ ASSERT(obj->IsJSGeneratorObject());
+ ASSERT(HeapObject::cast(obj)->Size() == JSGeneratorObject::kSize);
+ return reinterpret_cast<JSGeneratorObject*>(obj);
+}
+
+
+ACCESSORS(JSModule, context, Object, kContextOffset)
+ACCESSORS(JSModule, scope_info, ScopeInfo, kScopeInfoOffset)
+
+
+JSModule* JSModule::cast(Object* obj) {
+ ASSERT(obj->IsJSModule());
+ ASSERT(HeapObject::cast(obj)->Size() == JSModule::kSize);
+ return reinterpret_cast<JSModule*>(obj);
+}
+
+
+ACCESSORS(JSValue, value, Object, kValueOffset)
+
+
+JSValue* JSValue::cast(Object* obj) {
+ ASSERT(obj->IsJSValue());
+ ASSERT(HeapObject::cast(obj)->Size() == JSValue::kSize);
+ return reinterpret_cast<JSValue*>(obj);
+}
+
+
+ACCESSORS(JSDate, value, Object, kValueOffset)
+ACCESSORS(JSDate, cache_stamp, Object, kCacheStampOffset)
+ACCESSORS(JSDate, year, Object, kYearOffset)
+ACCESSORS(JSDate, month, Object, kMonthOffset)
+ACCESSORS(JSDate, day, Object, kDayOffset)
+ACCESSORS(JSDate, weekday, Object, kWeekdayOffset)
+ACCESSORS(JSDate, hour, Object, kHourOffset)
+ACCESSORS(JSDate, min, Object, kMinOffset)
+ACCESSORS(JSDate, sec, Object, kSecOffset)
+
+
+JSDate* JSDate::cast(Object* obj) {
+ ASSERT(obj->IsJSDate());
+ ASSERT(HeapObject::cast(obj)->Size() == JSDate::kSize);
+ return reinterpret_cast<JSDate*>(obj);
+}
+
+
+ACCESSORS(JSMessageObject, type, String, kTypeOffset)
+ACCESSORS(JSMessageObject, arguments, JSArray, kArgumentsOffset)
+ACCESSORS(JSMessageObject, script, Object, kScriptOffset)
+ACCESSORS(JSMessageObject, stack_trace, Object, kStackTraceOffset)
+ACCESSORS(JSMessageObject, stack_frames, Object, kStackFramesOffset)
+SMI_ACCESSORS(JSMessageObject, start_position, kStartPositionOffset)
+SMI_ACCESSORS(JSMessageObject, end_position, kEndPositionOffset)
+
+
+JSMessageObject* JSMessageObject::cast(Object* obj) {
+ ASSERT(obj->IsJSMessageObject());
+ ASSERT(HeapObject::cast(obj)->Size() == JSMessageObject::kSize);
+ return reinterpret_cast<JSMessageObject*>(obj);
+}
+
+
+INT_ACCESSORS(Code, instruction_size, kInstructionSizeOffset)
+INT_ACCESSORS(Code, prologue_offset, kPrologueOffset)
+ACCESSORS(Code, relocation_info, ByteArray, kRelocationInfoOffset)
+ACCESSORS(Code, handler_table, FixedArray, kHandlerTableOffset)
+ACCESSORS(Code, deoptimization_data, FixedArray, kDeoptimizationDataOffset)
+
+
+// Type feedback slot: type_feedback_info for FUNCTIONs, stub_info for STUBs.
+void Code::InitializeTypeFeedbackInfoNoWriteBarrier(Object* value) {
+ WRITE_FIELD(this, kTypeFeedbackInfoOffset, value);
+}
+
+
+Object* Code::type_feedback_info() {
+ ASSERT(kind() == FUNCTION);
+ return Object::cast(READ_FIELD(this, kTypeFeedbackInfoOffset));
+}
+
+
+void Code::set_type_feedback_info(Object* value, WriteBarrierMode mode) {
+ ASSERT(kind() == FUNCTION);
+ WRITE_FIELD(this, kTypeFeedbackInfoOffset, value);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kTypeFeedbackInfoOffset,
+ value, mode);
+}
+
+
+int Code::stub_info() {
+ ASSERT(kind() == COMPARE_IC || kind() == COMPARE_NIL_IC ||
+ kind() == BINARY_OP_IC || kind() == LOAD_IC);
+ Object* value = READ_FIELD(this, kTypeFeedbackInfoOffset);
+ return Smi::cast(value)->value();
+}
+
+
+void Code::set_stub_info(int value) {
+ ASSERT(kind() == COMPARE_IC ||
+ kind() == COMPARE_NIL_IC ||
+ kind() == BINARY_OP_IC ||
+ kind() == STUB ||
+ kind() == LOAD_IC ||
+ kind() == KEYED_LOAD_IC ||
+ kind() == STORE_IC ||
+ kind() == KEYED_STORE_IC);
+ WRITE_FIELD(this, kTypeFeedbackInfoOffset, Smi::FromInt(value));
+}
+
+
+Object* Code::code_to_deoptimize_link() {
+ // Optimized code should not have type feedback.
+ ASSERT(kind() == OPTIMIZED_FUNCTION);
+ return READ_FIELD(this, kTypeFeedbackInfoOffset);
+}
+
+
+void Code::set_code_to_deoptimize_link(Object* value) {
+ ASSERT(kind() == OPTIMIZED_FUNCTION);
+ WRITE_FIELD(this, kTypeFeedbackInfoOffset, value);
+}
+
+
+Object** Code::code_to_deoptimize_link_slot() {
+ ASSERT(kind() == OPTIMIZED_FUNCTION);
+ return HeapObject::RawField(this, kTypeFeedbackInfoOffset);
+}
+
+
+ACCESSORS(Code, gc_metadata, Object, kGCMetadataOffset)
+INT_ACCESSORS(Code, ic_age, kICAgeOffset)
+
+
+byte* Code::instruction_start() {
+ return FIELD_ADDR(this, kHeaderSize);
+}
+
+
+byte* Code::instruction_end() {
+ return instruction_start() + instruction_size();
+}
+
+
+int Code::body_size() {
+ return RoundUp(instruction_size(), kObjectAlignment);
+}
+
+
+ByteArray* Code::unchecked_relocation_info() {
+ return reinterpret_cast<ByteArray*>(READ_FIELD(this, kRelocationInfoOffset));
+}
+
+
+byte* Code::relocation_start() {
+ return unchecked_relocation_info()->GetDataStartAddress();
+}
+
+
+int Code::relocation_size() {
+ return unchecked_relocation_info()->length();
+}
+
+
+byte* Code::entry() {
+ return instruction_start();
+}
+
+
+bool Code::contains(byte* inner_pointer) {
+ return (address() <= inner_pointer) && (inner_pointer <= address() + Size());
+}
+
+
+ACCESSORS(JSArray, length, Object, kLengthOffset)
+
+
+void* JSArrayBuffer::backing_store() {
+ intptr_t ptr = READ_INTPTR_FIELD(this, kBackingStoreOffset);
+ return reinterpret_cast<void*>(ptr);
+}
+
+
+void JSArrayBuffer::set_backing_store(void* value, WriteBarrierMode mode) {
+ intptr_t ptr = reinterpret_cast<intptr_t>(value);
+ WRITE_INTPTR_FIELD(this, kBackingStoreOffset, ptr);
+}
+
+
+ACCESSORS(JSArrayBuffer, byte_length, Object, kByteLengthOffset)
+ACCESSORS_TO_SMI(JSArrayBuffer, flag, kFlagOffset)
+
+
+bool JSArrayBuffer::is_external() {
+ return BooleanBit::get(flag(), kIsExternalBit);
+}
+
+
+void JSArrayBuffer::set_is_external(bool value) {
+ set_flag(BooleanBit::set(flag(), kIsExternalBit, value));
+}
+
+
+ACCESSORS(JSArrayBuffer, weak_next, Object, kWeakNextOffset)
+ACCESSORS(JSArrayBuffer, weak_first_view, Object, kWeakFirstViewOffset)
+
+
+ACCESSORS(JSArrayBufferView, buffer, Object, kBufferOffset)
+ACCESSORS(JSArrayBufferView, byte_offset, Object, kByteOffsetOffset)
+ACCESSORS(JSArrayBufferView, byte_length, Object, kByteLengthOffset)
+ACCESSORS(JSArrayBufferView, weak_next, Object, kWeakNextOffset)
+ACCESSORS(JSTypedArray, length, Object, kLengthOffset)
+
+ACCESSORS(JSRegExp, data, Object, kDataOffset)
+
+
+JSRegExp::Type JSRegExp::TypeTag() {
+ Object* data = this->data();
+ if (data->IsUndefined()) return JSRegExp::NOT_COMPILED;
+ Smi* smi = Smi::cast(FixedArray::cast(data)->get(kTagIndex));
+ return static_cast<JSRegExp::Type>(smi->value());
+}
+
+
+int JSRegExp::CaptureCount() {
+ switch (TypeTag()) {
+ case ATOM:
+ return 0;
+ case IRREGEXP:
+ return Smi::cast(DataAt(kIrregexpCaptureCountIndex))->value();
+ default:
+ UNREACHABLE();
+ return -1;
+ }
+}
+
+
+JSRegExp::Flags JSRegExp::GetFlags() {
+ ASSERT(this->data()->IsFixedArray());
+ Object* data = this->data();
+ Smi* smi = Smi::cast(FixedArray::cast(data)->get(kFlagsIndex));
+ return Flags(smi->value());
+}
+
+
+String* JSRegExp::Pattern() {
+ ASSERT(this->data()->IsFixedArray());
+ Object* data = this->data();
+ String* pattern= String::cast(FixedArray::cast(data)->get(kSourceIndex));
+ return pattern;
+}
+
+
+Object* JSRegExp::DataAt(int index) {
+ ASSERT(TypeTag() != NOT_COMPILED);
+ return FixedArray::cast(data())->get(index);
+}
+
+
+void JSRegExp::SetDataAt(int index, Object* value) {
+ ASSERT(TypeTag() != NOT_COMPILED);
+ ASSERT(index >= kDataIndex); // Only implementation data can be set this way.
+ FixedArray::cast(data())->set(index, value);
+}
+
+
+ElementsKind JSObject::GetElementsKind() {
+ ElementsKind kind = map()->elements_kind();
+#if DEBUG
+ FixedArrayBase* fixed_array =
+ reinterpret_cast<FixedArrayBase*>(READ_FIELD(this, kElementsOffset));
+ Map* map = fixed_array->map();
+ ASSERT((IsFastSmiOrObjectElementsKind(kind) &&
+ (map == GetHeap()->fixed_array_map() ||
+ map == GetHeap()->fixed_cow_array_map())) ||
+ (IsFastDoubleElementsKind(kind) &&
+ (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;
+}
+
+
+ElementsAccessor* JSObject::GetElementsAccessor() {
+ return ElementsAccessor::ForKind(GetElementsKind());
+}
+
+
+bool JSObject::HasFastObjectElements() {
+ return IsFastObjectElementsKind(GetElementsKind());
+}
+
+
+bool JSObject::HasFastSmiElements() {
+ return IsFastSmiElementsKind(GetElementsKind());
+}
+
+
+bool JSObject::HasFastSmiOrObjectElements() {
+ return IsFastSmiOrObjectElementsKind(GetElementsKind());
+}
+
+
+bool JSObject::HasFastDoubleElements() {
+ return IsFastDoubleElementsKind(GetElementsKind());
+}
+
+
+bool JSObject::HasFastHoleyElements() {
+ return IsFastHoleyElementsKind(GetElementsKind());
+}
+
+
+bool JSObject::HasFastElements() {
+ return IsFastElementsKind(GetElementsKind());
+}
+
+
+bool JSObject::HasDictionaryElements() {
+ return GetElementsKind() == DICTIONARY_ELEMENTS;
+}
+
+
+bool JSObject::HasNonStrictArgumentsElements() {
+ return GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS;
+}
+
+
+bool JSObject::HasExternalArrayElements() {
+ HeapObject* array = elements();
+ ASSERT(array != NULL);
+ return array->IsExternalArray();
+}
+
+
+#define EXTERNAL_ELEMENTS_CHECK(name, type) \
+bool JSObject::HasExternal##name##Elements() { \
+ HeapObject* array = elements(); \
+ ASSERT(array != NULL); \
+ if (!array->IsHeapObject()) \
+ return false; \
+ return array->map()->instance_type() == type; \
+}
+
+
+EXTERNAL_ELEMENTS_CHECK(Byte, EXTERNAL_BYTE_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(UnsignedByte, EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(Short, EXTERNAL_SHORT_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(UnsignedShort,
+ EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(Int, EXTERNAL_INT_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(UnsignedInt,
+ EXTERNAL_UNSIGNED_INT_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(Float,
+ EXTERNAL_FLOAT_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(Double,
+ EXTERNAL_DOUBLE_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(Pixel, EXTERNAL_PIXEL_ARRAY_TYPE)
+
+
+bool JSObject::HasNamedInterceptor() {
+ return map()->has_named_interceptor();
+}
+
+
+bool JSObject::HasIndexedInterceptor() {
+ return map()->has_indexed_interceptor();
+}
+
+
+MaybeObject* JSObject::EnsureWritableFastElements() {
+ ASSERT(HasFastSmiOrObjectElements());
+ FixedArray* elems = FixedArray::cast(elements());
+ Isolate* isolate = GetIsolate();
+ if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems;
+ Object* writable_elems;
+ { MaybeObject* maybe_writable_elems = isolate->heap()->CopyFixedArrayWithMap(
+ elems, isolate->heap()->fixed_array_map());
+ if (!maybe_writable_elems->ToObject(&writable_elems)) {
+ return maybe_writable_elems;
+ }
+ }
+ set_elements(FixedArray::cast(writable_elems));
+ isolate->counters()->cow_arrays_converted()->Increment();
+ return writable_elems;
+}
+
+
+NameDictionary* JSObject::property_dictionary() {
+ ASSERT(!HasFastProperties());
+ return NameDictionary::cast(properties());
+}
+
+
+SeededNumberDictionary* JSObject::element_dictionary() {
+ ASSERT(HasDictionaryElements());
+ return SeededNumberDictionary::cast(elements());
+}
+
+
+bool Name::IsHashFieldComputed(uint32_t field) {
+ return (field & kHashNotComputedMask) == 0;
+}
+
+
+bool Name::HasHashCode() {
+ return IsHashFieldComputed(hash_field());
+}
+
+
+uint32_t Name::Hash() {
+ // Fast case: has hash code already been computed?
+ uint32_t field = hash_field();
+ if (IsHashFieldComputed(field)) return field >> kHashShift;
+ // Slow case: compute hash code and set it. Has to be a string.
+ return String::cast(this)->ComputeAndSetHash();
+}
+
+
+StringHasher::StringHasher(int length, uint32_t seed)
+ : length_(length),
+ raw_running_hash_(seed),
+ array_index_(0),
+ is_array_index_(0 < length_ && length_ <= String::kMaxArrayIndexSize),
+ is_first_char_(true) {
+ ASSERT(FLAG_randomize_hashes || raw_running_hash_ == 0);
+}
+
+
+bool StringHasher::has_trivial_hash() {
+ return length_ > String::kMaxHashCalcLength;
+}
+
+
+uint32_t StringHasher::AddCharacterCore(uint32_t running_hash, uint16_t c) {
+ running_hash += c;
+ running_hash += (running_hash << 10);
+ running_hash ^= (running_hash >> 6);
+ return running_hash;
+}
+
+
+uint32_t StringHasher::GetHashCore(uint32_t running_hash) {
+ running_hash += (running_hash << 3);
+ running_hash ^= (running_hash >> 11);
+ running_hash += (running_hash << 15);
+ if ((running_hash & String::kHashBitMask) == 0) {
+ return kZeroHash;
+ }
+ return running_hash;
+}
+
+
+void StringHasher::AddCharacter(uint16_t c) {
+ // Use the Jenkins one-at-a-time hash function to update the hash
+ // for the given character.
+ raw_running_hash_ = AddCharacterCore(raw_running_hash_, c);
+}
+
+
+bool StringHasher::UpdateIndex(uint16_t c) {
+ ASSERT(is_array_index_);
+ if (c < '0' || c > '9') {
+ is_array_index_ = false;
+ return false;
+ }
+ int d = c - '0';
+ if (is_first_char_) {
+ is_first_char_ = false;
+ if (c == '0' && length_ > 1) {
+ is_array_index_ = false;
+ return false;
+ }
+ }
+ if (array_index_ > 429496729U - ((d + 2) >> 3)) {
+ is_array_index_ = false;
+ return false;
+ }
+ array_index_ = array_index_ * 10 + d;
+ return true;
+}
+
+
+template<typename Char>
+inline void StringHasher::AddCharacters(const Char* chars, int length) {
+ ASSERT(sizeof(Char) == 1 || sizeof(Char) == 2);
+ int i = 0;
+ if (is_array_index_) {
+ for (; i < length; i++) {
+ AddCharacter(chars[i]);
+ if (!UpdateIndex(chars[i])) {
+ i++;
+ break;
+ }
+ }
+ }
+ for (; i < length; i++) {
+ ASSERT(!is_array_index_);
+ AddCharacter(chars[i]);
+ }
+}
+
+
+template <typename schar>
+uint32_t StringHasher::HashSequentialString(const schar* chars,
+ int length,
+ uint32_t seed) {
+ StringHasher hasher(length, seed);
+ if (!hasher.has_trivial_hash()) hasher.AddCharacters(chars, length);
+ return hasher.GetHashField();
+}
+
+
+bool Name::AsArrayIndex(uint32_t* index) {
+ return IsString() && String::cast(this)->AsArrayIndex(index);
+}
+
+
+bool String::AsArrayIndex(uint32_t* index) {
+ uint32_t field = hash_field();
+ if (IsHashFieldComputed(field) && (field & kIsNotArrayIndexMask)) {
+ return false;
+ }
+ return SlowAsArrayIndex(index);
+}
+
+
+Object* JSReceiver::GetPrototype() {
+ return map()->prototype();
+}
+
+
+Object* JSReceiver::GetConstructor() {
+ return map()->constructor();
+}
+
+
+bool JSReceiver::HasProperty(Name* name) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->HasPropertyWithHandler(name);
+ }
+ return GetPropertyAttribute(name) != ABSENT;
+}
+
+
+bool JSReceiver::HasLocalProperty(Name* name) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->HasPropertyWithHandler(name);
+ }
+ return GetLocalPropertyAttribute(name) != ABSENT;
+}
+
+
+PropertyAttributes JSReceiver::GetPropertyAttribute(Name* key) {
+ uint32_t index;
+ if (IsJSObject() && key->AsArrayIndex(&index)) {
+ return GetElementAttribute(index);
+ }
+ return GetPropertyAttributeWithReceiver(this, key);
+}
+
+
+PropertyAttributes JSReceiver::GetElementAttribute(uint32_t index) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->GetElementAttributeWithHandler(this, index);
+ }
+ return JSObject::cast(this)->GetElementAttributeWithReceiver(
+ this, index, true);
+}
+
+
+// TODO(504): this may be useful in other places too where JSGlobalProxy
+// is used.
+Object* JSObject::BypassGlobalProxy() {
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return GetHeap()->undefined_value();
+ ASSERT(proto->IsJSGlobalObject());
+ return proto;
+ }
+ return this;
+}
+
+
+MaybeObject* JSReceiver::GetIdentityHash(CreationFlag flag) {
+ return IsJSProxy()
+ ? JSProxy::cast(this)->GetIdentityHash(flag)
+ : JSObject::cast(this)->GetIdentityHash(flag);
+}
+
+
+bool JSReceiver::HasElement(uint32_t index) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->HasElementWithHandler(index);
+ }
+ return JSObject::cast(this)->GetElementAttributeWithReceiver(
+ this, index, true) != ABSENT;
+}
+
+
+bool JSReceiver::HasLocalElement(uint32_t index) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->HasElementWithHandler(index);
+ }
+ return JSObject::cast(this)->GetElementAttributeWithReceiver(
+ this, index, false) != ABSENT;
+}
+
+
+PropertyAttributes JSReceiver::GetLocalElementAttribute(uint32_t index) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->GetElementAttributeWithHandler(this, index);
+ }
+ return JSObject::cast(this)->GetElementAttributeWithReceiver(
+ this, index, false);
+}
+
+
+bool AccessorInfo::all_can_read() {
+ return BooleanBit::get(flag(), kAllCanReadBit);
+}
+
+
+void AccessorInfo::set_all_can_read(bool value) {
+ set_flag(BooleanBit::set(flag(), kAllCanReadBit, value));
+}
+
+
+bool AccessorInfo::all_can_write() {
+ return BooleanBit::get(flag(), kAllCanWriteBit);
+}
+
+
+void AccessorInfo::set_all_can_write(bool value) {
+ set_flag(BooleanBit::set(flag(), kAllCanWriteBit, value));
+}
+
+
+bool AccessorInfo::prohibits_overwriting() {
+ return BooleanBit::get(flag(), kProhibitsOverwritingBit);
+}
+
+
+void AccessorInfo::set_prohibits_overwriting(bool value) {
+ set_flag(BooleanBit::set(flag(), kProhibitsOverwritingBit, value));
+}
+
+
+PropertyAttributes AccessorInfo::property_attributes() {
+ return AttributesField::decode(static_cast<uint32_t>(flag()->value()));
+}
+
+
+void AccessorInfo::set_property_attributes(PropertyAttributes attributes) {
+ set_flag(Smi::FromInt(AttributesField::update(flag()->value(), attributes)));
+}
+
+
+bool AccessorInfo::IsCompatibleReceiver(Object* receiver) {
+ Object* function_template = expected_receiver_type();
+ if (!function_template->IsFunctionTemplateInfo()) return true;
+ return receiver->IsInstanceOf(FunctionTemplateInfo::cast(function_template));
+}
+
+
+template<typename Shape, typename Key>
+void Dictionary<Shape, Key>::SetEntry(int entry,
+ Object* key,
+ Object* value) {
+ SetEntry(entry, key, value, PropertyDetails(Smi::FromInt(0)));
+}
+
+
+template<typename Shape, typename Key>
+void Dictionary<Shape, Key>::SetEntry(int entry,
+ Object* key,
+ Object* value,
+ PropertyDetails details) {
+ ASSERT(!key->IsName() ||
+ details.IsDeleted() ||
+ details.dictionary_index() > 0);
+ int index = HashTable<Shape, Key>::EntryToIndex(entry);
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = FixedArray::GetWriteBarrierMode(no_gc);
+ FixedArray::set(index, key, mode);
+ FixedArray::set(index+1, value, mode);
+ FixedArray::set(index+2, details.AsSmi());
+}
+
+
+bool NumberDictionaryShape::IsMatch(uint32_t key, Object* other) {
+ ASSERT(other->IsNumber());
+ return key == static_cast<uint32_t>(other->Number());
+}
+
+
+uint32_t UnseededNumberDictionaryShape::Hash(uint32_t key) {
+ return ComputeIntegerHash(key, 0);
+}
+
+
+uint32_t UnseededNumberDictionaryShape::HashForObject(uint32_t key,
+ Object* other) {
+ ASSERT(other->IsNumber());
+ return ComputeIntegerHash(static_cast<uint32_t>(other->Number()), 0);
+}
+
+uint32_t SeededNumberDictionaryShape::SeededHash(uint32_t key, uint32_t seed) {
+ return ComputeIntegerHash(key, seed);
+}
+
+uint32_t SeededNumberDictionaryShape::SeededHashForObject(uint32_t key,
+ uint32_t seed,
+ Object* other) {
+ ASSERT(other->IsNumber());
+ return ComputeIntegerHash(static_cast<uint32_t>(other->Number()), seed);
+}
+
+MaybeObject* NumberDictionaryShape::AsObject(Heap* heap, uint32_t key) {
+ return heap->NumberFromUint32(key);
+}
+
+
+bool NameDictionaryShape::IsMatch(Name* key, Object* other) {
+ // We know that all entries in a hash table had their hash keys created.
+ // Use that knowledge to have fast failure.
+ if (key->Hash() != Name::cast(other)->Hash()) return false;
+ return key->Equals(Name::cast(other));
+}
+
+
+uint32_t NameDictionaryShape::Hash(Name* key) {
+ return key->Hash();
+}
+
+
+uint32_t NameDictionaryShape::HashForObject(Name* key, Object* other) {
+ return Name::cast(other)->Hash();
+}
+
+
+MaybeObject* NameDictionaryShape::AsObject(Heap* heap, Name* key) {
+ return key;
+}
+
+
+template <int entrysize>
+bool ObjectHashTableShape<entrysize>::IsMatch(Object* key, Object* other) {
+ return key->SameValue(other);
+}
+
+
+template <int entrysize>
+uint32_t ObjectHashTableShape<entrysize>::Hash(Object* key) {
+ MaybeObject* maybe_hash = key->GetHash(OMIT_CREATION);
+ return Smi::cast(maybe_hash->ToObjectChecked())->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();
+}
+
+
+template <int entrysize>
+MaybeObject* ObjectHashTableShape<entrysize>::AsObject(Heap* heap,
+ Object* key) {
+ return key;
+}
+
+
+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:
+ // - MarkCompactCollector::MarkUnmarkedObject
+ // - IncrementalMarking::Step
+ ASSERT(!heap->InNewSpace(heap->empty_fixed_array()));
+ WRITE_FIELD(this, kCodeCacheOffset, heap->empty_fixed_array());
+}
+
+
+void JSArray::EnsureSize(int required_size) {
+ ASSERT(HasFastSmiOrObjectElements());
+ FixedArray* elts = FixedArray::cast(elements());
+ const int kArraySizeThatFitsComfortablyInNewSpace = 128;
+ if (elts->length() < required_size) {
+ // Doubling in size would be overkill, but leave some slack to avoid
+ // constantly growing.
+ Expand(required_size + (required_size >> 3));
+ // It's a performance benefit to keep a frequently used array in new-space.
+ } else if (!GetHeap()->new_space()->Contains(elts) &&
+ required_size < kArraySizeThatFitsComfortablyInNewSpace) {
+ // Expand will allocate a new backing store in new space even if the size
+ // we asked for isn't larger than what we had before.
+ Expand(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);
+}
+
+
+bool JSArray::AllowsSetElementsLength() {
+ bool result = elements()->IsFixedArray() || elements()->IsFixedDoubleArray();
+ ASSERT(result == !HasExternalArrayElements());
+ return result;
+}
+
+
+MaybeObject* JSArray::SetContent(FixedArrayBase* storage) {
+ MaybeObject* maybe_result = EnsureCanContainElements(
+ storage, storage->length(), ALLOW_COPIED_DOUBLE_ELEMENTS);
+ if (maybe_result->IsFailure()) return maybe_result;
+ ASSERT((storage->map() == GetHeap()->fixed_double_array_map() &&
+ IsFastDoubleElementsKind(GetElementsKind())) ||
+ ((storage->map() != GetHeap()->fixed_double_array_map()) &&
+ (IsFastObjectElementsKind(GetElementsKind()) ||
+ (IsFastSmiElementsKind(GetElementsKind()) &&
+ FixedArray::cast(storage)->ContainsOnlySmisOrHoles()))));
+ set_elements(storage);
+ set_length(Smi::FromInt(storage->length()));
+ return this;
+}
+
+
+MaybeObject* FixedArray::Copy() {
+ if (length() == 0) return this;
+ return GetHeap()->CopyFixedArray(this);
+}
+
+
+MaybeObject* FixedDoubleArray::Copy() {
+ if (length() == 0) return this;
+ return GetHeap()->CopyFixedDoubleArray(this);
+}
+
+
+void TypeFeedbackCells::SetAstId(int index, TypeFeedbackId id) {
+ set(1 + index * 2, Smi::FromInt(id.ToInt()));
+}
+
+
+TypeFeedbackId TypeFeedbackCells::AstId(int index) {
+ return TypeFeedbackId(Smi::cast(get(1 + index * 2))->value());
+}
+
+
+void TypeFeedbackCells::SetCell(int index, Cell* cell) {
+ set(index * 2, cell);
+}
+
+
+Cell* TypeFeedbackCells::GetCell(int index) {
+ return Cell::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();
+}
+
+
+Handle<Object> TypeFeedbackCells::MonomorphicArraySentinel(Isolate* isolate,
+ ElementsKind elements_kind) {
+ return Handle<Object>(Smi::FromInt(static_cast<int>(elements_kind)), isolate);
+}
+
+
+Object* TypeFeedbackCells::RawUninitializedSentinel(Heap* heap) {
+ return heap->the_hole_value();
+}
+
+
+int TypeFeedbackInfo::ic_total_count() {
+ int current = Smi::cast(READ_FIELD(this, kStorage1Offset))->value();
+ return ICTotalCountField::decode(current);
+}
+
+
+void TypeFeedbackInfo::set_ic_total_count(int count) {
+ int value = Smi::cast(READ_FIELD(this, kStorage1Offset))->value();
+ value = ICTotalCountField::update(value,
+ ICTotalCountField::decode(count));
+ WRITE_FIELD(this, kStorage1Offset, Smi::FromInt(value));
+}
+
+
+int TypeFeedbackInfo::ic_with_type_info_count() {
+ int current = Smi::cast(READ_FIELD(this, kStorage2Offset))->value();
+ return ICsWithTypeInfoCountField::decode(current);
+}
+
+
+void TypeFeedbackInfo::change_ic_with_type_info_count(int delta) {
+ int value = Smi::cast(READ_FIELD(this, kStorage2Offset))->value();
+ int new_count = ICsWithTypeInfoCountField::decode(value) + delta;
+ // We can get negative count here when the type-feedback info is
+ // shared between two code objects. The can only happen when
+ // the debugger made a shallow copy of code object (see Heap::CopyCode).
+ // Since we do not optimize when the debugger is active, we can skip
+ // this counter update.
+ if (new_count >= 0) {
+ new_count &= ICsWithTypeInfoCountField::kMask;
+ value = ICsWithTypeInfoCountField::update(value, new_count);
+ WRITE_FIELD(this, kStorage2Offset, Smi::FromInt(value));
+ }
+}
+
+
+void TypeFeedbackInfo::initialize_storage() {
+ WRITE_FIELD(this, kStorage1Offset, Smi::FromInt(0));
+ WRITE_FIELD(this, kStorage2Offset, Smi::FromInt(0));
+}
+
+
+void TypeFeedbackInfo::change_own_type_change_checksum() {
+ int value = Smi::cast(READ_FIELD(this, kStorage1Offset))->value();
+ int checksum = OwnTypeChangeChecksum::decode(value);
+ checksum = (checksum + 1) % (1 << kTypeChangeChecksumBits);
+ value = OwnTypeChangeChecksum::update(value, checksum);
+ // Ensure packed bit field is in Smi range.
+ if (value > Smi::kMaxValue) value |= Smi::kMinValue;
+ if (value < Smi::kMinValue) value &= ~Smi::kMinValue;
+ WRITE_FIELD(this, kStorage1Offset, Smi::FromInt(value));
+}
+
+
+void TypeFeedbackInfo::set_inlined_type_change_checksum(int checksum) {
+ int value = Smi::cast(READ_FIELD(this, kStorage2Offset))->value();
+ int mask = (1 << kTypeChangeChecksumBits) - 1;
+ value = InlinedTypeChangeChecksum::update(value, checksum & mask);
+ // Ensure packed bit field is in Smi range.
+ if (value > Smi::kMaxValue) value |= Smi::kMinValue;
+ if (value < Smi::kMinValue) value &= ~Smi::kMinValue;
+ WRITE_FIELD(this, kStorage2Offset, Smi::FromInt(value));
+}
+
+
+int TypeFeedbackInfo::own_type_change_checksum() {
+ int value = Smi::cast(READ_FIELD(this, kStorage1Offset))->value();
+ return OwnTypeChangeChecksum::decode(value);
+}
+
+
+bool TypeFeedbackInfo::matches_inlined_type_change_checksum(int checksum) {
+ int value = Smi::cast(READ_FIELD(this, kStorage2Offset))->value();
+ int mask = (1 << kTypeChangeChecksumBits) - 1;
+ return InlinedTypeChangeChecksum::decode(value) == (checksum & mask);
+}
+
+
+ACCESSORS(TypeFeedbackInfo, type_feedback_cells, TypeFeedbackCells,
+ kTypeFeedbackCellsOffset)
+
+
+SMI_ACCESSORS(AliasedArgumentsEntry, aliased_context_slot, kAliasedContextSlot)
+
+
+Relocatable::Relocatable(Isolate* isolate) {
+ ASSERT(isolate == Isolate::Current());
+ isolate_ = isolate;
+ prev_ = isolate->relocatable_top();
+ isolate->set_relocatable_top(this);
+}
+
+
+Relocatable::~Relocatable() {
+ ASSERT(isolate_ == Isolate::Current());
+ ASSERT_EQ(isolate_->relocatable_top(), this);
+ isolate_->set_relocatable_top(prev_);
+}
+
+
+int JSObject::BodyDescriptor::SizeOf(Map* map, HeapObject* object) {
+ return map->instance_size();
+}
+
+
+void Foreign::ForeignIterateBody(ObjectVisitor* v) {
+ v->VisitExternalReference(
+ reinterpret_cast<Address*>(FIELD_ADDR(this, kForeignAddressOffset)));
+}
+
+
+template<typename StaticVisitor>
+void Foreign::ForeignIterateBody() {
+ StaticVisitor::VisitExternalReference(
+ reinterpret_cast<Address*>(FIELD_ADDR(this, kForeignAddressOffset)));
+}
+
+
+void ExternalAsciiString::ExternalAsciiStringIterateBody(ObjectVisitor* v) {
+ typedef v8::String::ExternalAsciiStringResource Resource;
+ v->VisitExternalAsciiString(
+ reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
+}
+
+
+template<typename StaticVisitor>
+void ExternalAsciiString::ExternalAsciiStringIterateBody() {
+ typedef v8::String::ExternalAsciiStringResource Resource;
+ StaticVisitor::VisitExternalAsciiString(
+ reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
+}
+
+
+void ExternalTwoByteString::ExternalTwoByteStringIterateBody(ObjectVisitor* v) {
+ typedef v8::String::ExternalStringResource Resource;
+ v->VisitExternalTwoByteString(
+ reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
+}
+
+
+template<typename StaticVisitor>
+void ExternalTwoByteString::ExternalTwoByteStringIterateBody() {
+ typedef v8::String::ExternalStringResource Resource;
+ StaticVisitor::VisitExternalTwoByteString(
+ reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset)));
+}
+
+
+template<int start_offset, int end_offset, int size>
+void FixedBodyDescriptor<start_offset, end_offset, size>::IterateBody(
+ HeapObject* obj,
+ ObjectVisitor* v) {
+ v->VisitPointers(HeapObject::RawField(obj, start_offset),
+ HeapObject::RawField(obj, end_offset));
+}
+
+
+template<int start_offset>
+void FlexibleBodyDescriptor<start_offset>::IterateBody(HeapObject* obj,
+ int object_size,
+ ObjectVisitor* v) {
+ v->VisitPointers(HeapObject::RawField(obj, start_offset),
+ HeapObject::RawField(obj, object_size));
+}
+
+
+#undef TYPE_CHECKER
+#undef CAST_ACCESSOR
+#undef INT_ACCESSORS
+#undef ACCESSORS
+#undef ACCESSORS_TO_SMI
+#undef SMI_ACCESSORS
+#undef BOOL_GETTER
+#undef BOOL_ACCESSORS
+#undef FIELD_ADDR
+#undef READ_FIELD
+#undef WRITE_FIELD
+#undef WRITE_BARRIER
+#undef CONDITIONAL_WRITE_BARRIER
+#undef READ_DOUBLE_FIELD
+#undef WRITE_DOUBLE_FIELD
+#undef READ_INT_FIELD
+#undef WRITE_INT_FIELD
+#undef READ_INTPTR_FIELD
+#undef WRITE_INTPTR_FIELD
+#undef READ_UINT32_FIELD
+#undef WRITE_UINT32_FIELD
+#undef READ_SHORT_FIELD
+#undef WRITE_SHORT_FIELD
+#undef READ_BYTE_FIELD
+#undef WRITE_BYTE_FIELD
+
+
+} } // namespace v8::internal
+
+#endif // V8_OBJECTS_INL_H_
diff --git a/chromium/v8/src/objects-printer.cc b/chromium/v8/src/objects-printer.cc
new file mode 100644
index 00000000000..7b6f7a478ce
--- /dev/null
+++ b/chromium/v8/src/objects-printer.cc
@@ -0,0 +1,1239 @@
+// Copyright 2012 the V8 project authors. 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 "disassembler.h"
+#include "disasm.h"
+#include "jsregexp.h"
+#include "objects-visiting.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef OBJECT_PRINT
+
+void MaybeObject::Print() {
+ Print(stdout);
+}
+
+
+void MaybeObject::Print(FILE* out) {
+ Object* this_as_object;
+ if (ToObject(&this_as_object)) {
+ if (this_as_object->IsSmi()) {
+ Smi::cast(this_as_object)->SmiPrint(out);
+ } else {
+ HeapObject::cast(this_as_object)->HeapObjectPrint(out);
+ }
+ } else {
+ Failure::cast(this)->FailurePrint(out);
+ }
+ Flush(out);
+}
+
+
+void MaybeObject::PrintLn() {
+ PrintLn(stdout);
+}
+
+
+void MaybeObject::PrintLn(FILE* out) {
+ Print(out);
+ PrintF(out, "\n");
+}
+
+
+void HeapObject::PrintHeader(FILE* out, const char* id) {
+ PrintF(out, "%p: [%s]\n", reinterpret_cast<void*>(this), id);
+}
+
+
+void HeapObject::HeapObjectPrint(FILE* out) {
+ InstanceType instance_type = map()->instance_type();
+
+ HandleScope scope(GetIsolate());
+ if (instance_type < FIRST_NONSTRING_TYPE) {
+ String::cast(this)->StringPrint(out);
+ return;
+ }
+
+ switch (instance_type) {
+ case SYMBOL_TYPE:
+ Symbol::cast(this)->SymbolPrint(out);
+ break;
+ case MAP_TYPE:
+ Map::cast(this)->MapPrint(out);
+ break;
+ 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;
+ case EXTERNAL_BYTE_ARRAY_TYPE:
+ ExternalByteArray::cast(this)->ExternalByteArrayPrint(out);
+ break;
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ ExternalUnsignedByteArray::cast(this)
+ ->ExternalUnsignedByteArrayPrint(out);
+ break;
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ ExternalShortArray::cast(this)->ExternalShortArrayPrint(out);
+ break;
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ ExternalUnsignedShortArray::cast(this)
+ ->ExternalUnsignedShortArrayPrint(out);
+ break;
+ case EXTERNAL_INT_ARRAY_TYPE:
+ ExternalIntArray::cast(this)->ExternalIntArrayPrint(out);
+ break;
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ ExternalUnsignedIntArray::cast(this)->ExternalUnsignedIntArrayPrint(out);
+ break;
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ ExternalFloatArray::cast(this)->ExternalFloatArrayPrint(out);
+ break;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ ExternalDoubleArray::cast(this)->ExternalDoubleArrayPrint(out);
+ break;
+ case FILLER_TYPE:
+ PrintF(out, "filler");
+ break;
+ case JS_OBJECT_TYPE: // fall through
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
+ case JS_ARRAY_TYPE:
+ case JS_GENERATOR_OBJECT_TYPE:
+ case JS_REGEXP_TYPE:
+ JSObject::cast(this)->JSObjectPrint(out);
+ break;
+ case ODDBALL_TYPE:
+ Oddball::cast(this)->to_string()->Print(out);
+ break;
+ case JS_MODULE_TYPE:
+ JSModule::cast(this)->JSModulePrint(out);
+ break;
+ case JS_FUNCTION_TYPE:
+ JSFunction::cast(this)->JSFunctionPrint(out);
+ break;
+ case JS_GLOBAL_PROXY_TYPE:
+ JSGlobalProxy::cast(this)->JSGlobalProxyPrint(out);
+ break;
+ case JS_GLOBAL_OBJECT_TYPE:
+ JSGlobalObject::cast(this)->JSGlobalObjectPrint(out);
+ break;
+ case JS_BUILTINS_OBJECT_TYPE:
+ JSBuiltinsObject::cast(this)->JSBuiltinsObjectPrint(out);
+ break;
+ case JS_VALUE_TYPE:
+ PrintF(out, "Value wrapper around:");
+ JSValue::cast(this)->value()->Print(out);
+ break;
+ case JS_DATE_TYPE:
+ JSDate::cast(this)->JSDatePrint(out);
+ break;
+ case CODE_TYPE:
+ Code::cast(this)->CodePrint(out);
+ break;
+ case JS_PROXY_TYPE:
+ JSProxy::cast(this)->JSProxyPrint(out);
+ break;
+ case JS_FUNCTION_PROXY_TYPE:
+ JSFunctionProxy::cast(this)->JSFunctionProxyPrint(out);
+ break;
+ case JS_SET_TYPE:
+ JSSet::cast(this)->JSSetPrint(out);
+ break;
+ case JS_MAP_TYPE:
+ JSMap::cast(this)->JSMapPrint(out);
+ break;
+ case JS_WEAK_MAP_TYPE:
+ JSWeakMap::cast(this)->JSWeakMapPrint(out);
+ break;
+ case JS_WEAK_SET_TYPE:
+ JSWeakSet::cast(this)->JSWeakSetPrint(out);
+ break;
+ case FOREIGN_TYPE:
+ Foreign::cast(this)->ForeignPrint(out);
+ break;
+ case SHARED_FUNCTION_INFO_TYPE:
+ SharedFunctionInfo::cast(this)->SharedFunctionInfoPrint(out);
+ break;
+ case JS_MESSAGE_OBJECT_TYPE:
+ JSMessageObject::cast(this)->JSMessageObjectPrint(out);
+ break;
+ case CELL_TYPE:
+ Cell::cast(this)->CellPrint(out);
+ break;
+ case PROPERTY_CELL_TYPE:
+ PropertyCell::cast(this)->PropertyCellPrint(out);
+ break;
+ case JS_ARRAY_BUFFER_TYPE:
+ JSArrayBuffer::cast(this)->JSArrayBufferPrint(out);
+ break;
+ case JS_TYPED_ARRAY_TYPE:
+ JSTypedArray::cast(this)->JSTypedArrayPrint(out);
+ break;
+ case JS_DATA_VIEW_TYPE:
+ JSDataView::cast(this)->JSDataViewPrint(out);
+ break;
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE: \
+ Name::cast(this)->Name##Print(out); \
+ break;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+
+ default:
+ PrintF(out, "UNKNOWN TYPE %d", map()->instance_type());
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void ByteArray::ByteArrayPrint(FILE* out) {
+ PrintF(out, "byte array, data starts at %p", GetDataStartAddress());
+}
+
+
+void FreeSpace::FreeSpacePrint(FILE* out) {
+ PrintF(out, "free space, size %d", Size());
+}
+
+
+void ExternalPixelArray::ExternalPixelArrayPrint(FILE* out) {
+ PrintF(out, "external pixel array");
+}
+
+
+void ExternalByteArray::ExternalByteArrayPrint(FILE* out) {
+ PrintF(out, "external byte array");
+}
+
+
+void ExternalUnsignedByteArray::ExternalUnsignedByteArrayPrint(FILE* out) {
+ PrintF(out, "external unsigned byte array");
+}
+
+
+void ExternalShortArray::ExternalShortArrayPrint(FILE* out) {
+ PrintF(out, "external short array");
+}
+
+
+void ExternalUnsignedShortArray::ExternalUnsignedShortArrayPrint(FILE* out) {
+ PrintF(out, "external unsigned short array");
+}
+
+
+void ExternalIntArray::ExternalIntArrayPrint(FILE* out) {
+ PrintF(out, "external int array");
+}
+
+
+void ExternalUnsignedIntArray::ExternalUnsignedIntArrayPrint(FILE* out) {
+ PrintF(out, "external unsigned int array");
+}
+
+
+void ExternalFloatArray::ExternalFloatArrayPrint(FILE* out) {
+ PrintF(out, "external float array");
+}
+
+
+void ExternalDoubleArray::ExternalDoubleArrayPrint(FILE* out) {
+ PrintF(out, "external double array");
+}
+
+
+void JSObject::PrintProperties(FILE* out) {
+ if (HasFastProperties()) {
+ DescriptorArray* descs = map()->instance_descriptors();
+ for (int i = 0; i < map()->NumberOfOwnDescriptors(); i++) {
+ PrintF(out, " ");
+ descs->GetKey(i)->NamePrint(out);
+ PrintF(out, ": ");
+ switch (descs->GetType(i)) {
+ case FIELD: {
+ int index = descs->GetFieldIndex(i);
+ RawFastPropertyAt(index)->ShortPrint(out);
+ PrintF(out, " (field at offset %d)\n", index);
+ break;
+ }
+ case CONSTANT:
+ descs->GetConstant(i)->ShortPrint(out);
+ PrintF(out, " (constant)\n");
+ break;
+ case CALLBACKS:
+ descs->GetCallbacksObject(i)->ShortPrint(out);
+ PrintF(out, " (callback)\n");
+ 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
+ // There are no transitions in the descriptor array.
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+ } else {
+ property_dictionary()->Print(out);
+ }
+}
+
+
+void JSObject::PrintElements(FILE* out) {
+ // Don't call GetElementsKind, its validation code can cause the printer to
+ // fail when debugging.
+ switch (map()->elements_kind()) {
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_ELEMENTS: {
+ // Print in array notation for non-sparse arrays.
+ FixedArray* p = FixedArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: ", i);
+ p->get(i)->ShortPrint(out);
+ PrintF(out, "\n");
+ }
+ break;
+ }
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS: {
+ // Print in array notation for non-sparse arrays.
+ if (elements()->length() > 0) {
+ FixedDoubleArray* p = FixedDoubleArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ if (p->is_the_hole(i)) {
+ PrintF(out, " %d: <the hole>", i);
+ } else {
+ PrintF(out, " %d: %g", i, p->get_scalar(i));
+ }
+ PrintF(out, "\n");
+ }
+ }
+ break;
+ }
+ case EXTERNAL_PIXEL_ELEMENTS: {
+ ExternalPixelArray* p = ExternalPixelArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, p->get_scalar(i));
+ }
+ break;
+ }
+ case EXTERNAL_BYTE_ELEMENTS: {
+ ExternalByteArray* p = ExternalByteArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get_scalar(i)));
+ }
+ break;
+ }
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
+ ExternalUnsignedByteArray* p =
+ ExternalUnsignedByteArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get_scalar(i)));
+ }
+ break;
+ }
+ case EXTERNAL_SHORT_ELEMENTS: {
+ ExternalShortArray* p = ExternalShortArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get_scalar(i)));
+ }
+ break;
+ }
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
+ ExternalUnsignedShortArray* p =
+ ExternalUnsignedShortArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get_scalar(i)));
+ }
+ break;
+ }
+ case EXTERNAL_INT_ELEMENTS: {
+ ExternalIntArray* p = ExternalIntArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get_scalar(i)));
+ }
+ break;
+ }
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
+ ExternalUnsignedIntArray* p =
+ ExternalUnsignedIntArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %d\n", i, static_cast<int>(p->get_scalar(i)));
+ }
+ break;
+ }
+ case EXTERNAL_FLOAT_ELEMENTS: {
+ ExternalFloatArray* p = ExternalFloatArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %f\n", i, p->get_scalar(i));
+ }
+ break;
+ }
+ case EXTERNAL_DOUBLE_ELEMENTS: {
+ ExternalDoubleArray* p = ExternalDoubleArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %f\n", i, p->get_scalar(i));
+ }
+ break;
+ }
+ case DICTIONARY_ELEMENTS:
+ elements()->Print(out);
+ break;
+ case NON_STRICT_ARGUMENTS_ELEMENTS: {
+ FixedArray* p = FixedArray::cast(elements());
+ PrintF(out, " parameter map:");
+ for (int i = 2; i < p->length(); i++) {
+ PrintF(out, " %d:", i - 2);
+ p->get(i)->ShortPrint(out);
+ }
+ PrintF(out, "\n context: ");
+ p->get(0)->ShortPrint(out);
+ PrintF(out, "\n arguments: ");
+ p->get(1)->ShortPrint(out);
+ PrintF(out, "\n");
+ break;
+ }
+ }
+}
+
+
+void JSObject::PrintTransitions(FILE* out) {
+ if (!map()->HasTransitionArray()) return;
+ TransitionArray* transitions = map()->transitions();
+ for (int i = 0; i < transitions->number_of_transitions(); i++) {
+ PrintF(out, " ");
+ transitions->GetKey(i)->NamePrint(out);
+ PrintF(out, ": ");
+ switch (transitions->GetTargetDetails(i).type()) {
+ case FIELD: {
+ PrintF(out, " (transition to field)\n");
+ break;
+ }
+ case CONSTANT:
+ PrintF(out, " (transition to constant)\n");
+ break;
+ case CALLBACKS:
+ PrintF(out, " (transition to callback)\n");
+ break;
+ // Values below are never in the target descriptor array.
+ case NORMAL:
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void JSObject::JSObjectPrint(FILE* out) {
+ PrintF(out, "%p: [JSObject]\n", reinterpret_cast<void*>(this));
+ 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);
+ PrintTransitions(out);
+ PrintElements(out);
+ PrintF(out, " }\n");
+}
+
+
+void JSModule::JSModulePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSModule");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - context = ");
+ context()->Print(out);
+ PrintF(out, " - scope_info = ");
+ scope_info()->ShortPrint(out);
+ PrintElementsKind(out, this->map()->elements_kind());
+ PrintF(out, " {\n");
+ PrintProperties(out);
+ PrintElements(out);
+ PrintF(out, " }\n");
+}
+
+
+static const char* TypeToString(InstanceType type) {
+ switch (type) {
+#define TYPE_TO_STRING(TYPE) case TYPE: return #TYPE;
+ INSTANCE_TYPE_LIST(TYPE_TO_STRING)
+#undef TYPE_TO_STRING
+ }
+ UNREACHABLE();
+ return "UNKNOWN"; // Keep the compiler happy.
+}
+
+
+void Symbol::SymbolPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Symbol");
+ PrintF(out, " - hash: %d\n", Hash());
+ PrintF(out, " - name: ");
+ name()->ShortPrint();
+ PrintF(out, "\n");
+}
+
+
+void Map::MapPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Map");
+ 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, " - 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()) {
+ PrintF(out, " - hidden_prototype\n");
+ }
+ if (has_named_interceptor()) {
+ PrintF(out, " - named_interceptor\n");
+ }
+ if (has_indexed_interceptor()) {
+ PrintF(out, " - indexed_interceptor\n");
+ }
+ if (is_undetectable()) {
+ PrintF(out, " - undetectable\n");
+ }
+ if (has_instance_call_handler()) {
+ PrintF(out, " - instance_call_handler\n");
+ }
+ if (is_access_check_needed()) {
+ PrintF(out, " - access_check_needed\n");
+ }
+ PrintF(out, " - back pointer: ");
+ GetBackPointer()->ShortPrint(out);
+ PrintF(out, "\n - instance descriptors %s#%i: ",
+ owns_descriptors() ? "(own) " : "",
+ NumberOfOwnDescriptors());
+ instance_descriptors()->ShortPrint(out);
+ if (HasTransitionArray()) {
+ PrintF(out, "\n - transitions: ");
+ transitions()->ShortPrint(out);
+ }
+ PrintF(out, "\n - prototype: ");
+ prototype()->ShortPrint(out);
+ PrintF(out, "\n - constructor: ");
+ constructor()->ShortPrint(out);
+ PrintF(out, "\n - code cache: ");
+ code_cache()->ShortPrint(out);
+ PrintF(out, "\n - dependent code: ");
+ dependent_code()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void CodeCache::CodeCachePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "CodeCache");
+ PrintF(out, "\n - default_cache: ");
+ default_cache()->ShortPrint(out);
+ PrintF(out, "\n - normal_type_cache: ");
+ normal_type_cache()->ShortPrint(out);
+}
+
+
+void PolymorphicCodeCache::PolymorphicCodeCachePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "PolymorphicCodeCache");
+ PrintF(out, "\n - cache: ");
+ cache()->ShortPrint(out);
+}
+
+
+void TypeFeedbackInfo::TypeFeedbackInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "TypeFeedbackInfo");
+ PrintF(out, " - ic_total_count: %d, ic_with_type_info_count: %d\n",
+ ic_total_count(), ic_with_type_info_count());
+ PrintF(out, " - type_feedback_cells: ");
+ type_feedback_cells()->FixedArrayPrint(out);
+}
+
+
+void AliasedArgumentsEntry::AliasedArgumentsEntryPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "AliasedArgumentsEntry");
+ PrintF(out, "\n - aliased_context_slot: %d", aliased_context_slot());
+}
+
+
+void FixedArray::FixedArrayPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "FixedArray");
+ PrintF(out, " - length: %d", length());
+ for (int i = 0; i < length(); i++) {
+ PrintF(out, "\n [%d]: ", i);
+ get(i)->ShortPrint(out);
+ }
+ PrintF(out, "\n");
+}
+
+
+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);
+}
+
+
+void JSMessageObject::JSMessageObjectPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSMessageObject");
+ PrintF(out, " - type: ");
+ type()->ShortPrint(out);
+ PrintF(out, "\n - arguments: ");
+ arguments()->ShortPrint(out);
+ PrintF(out, "\n - start_position: %d", start_position());
+ PrintF(out, "\n - end_position: %d", end_position());
+ PrintF(out, "\n - script: ");
+ script()->ShortPrint(out);
+ PrintF(out, "\n - stack_trace: ");
+ stack_trace()->ShortPrint(out);
+ PrintF(out, "\n - stack_frames: ");
+ stack_frames()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void String::StringPrint(FILE* out) {
+ if (StringShape(this).IsInternalized()) {
+ PrintF(out, "#");
+ } else if (StringShape(this).IsCons()) {
+ PrintF(out, "c\"");
+ } else {
+ PrintF(out, "\"");
+ }
+
+ const char truncated_epilogue[] = "...<truncated>";
+ int len = length();
+ if (!FLAG_use_verbose_printer) {
+ if (len > 100) {
+ len = 100 - sizeof(truncated_epilogue);
+ }
+ }
+ for (int i = 0; i < len; i++) {
+ PrintF(out, "%c", Get(i));
+ }
+ if (len != length()) {
+ PrintF(out, "%s", truncated_epilogue);
+ }
+
+ if (!StringShape(this).IsInternalized()) PrintF(out, "\"");
+}
+
+
+void Name::NamePrint(FILE* out) {
+ if (IsString())
+ String::cast(this)->StringPrint(out);
+ else
+ ShortPrint();
+}
+
+
+// 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
+// will be ignored in the output.
+char* String::ToAsciiArray() {
+ // Static so that subsequent calls frees previously allocated space.
+ // This also means that previous results will be overwritten.
+ static char* buffer = NULL;
+ if (buffer != NULL) free(buffer);
+ buffer = new char[length()+1];
+ WriteToFlat(this, reinterpret_cast<uint8_t*>(buffer), 0, length());
+ buffer[length()] = 0;
+ return buffer;
+}
+
+
+static const char* const weekdays[] = {
+ "???", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+
+void JSDate::JSDatePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSDate");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - value = ");
+ value()->Print(out);
+ if (!year()->IsSmi()) {
+ PrintF(out, " - time = NaN\n");
+ } else {
+ PrintF(out, " - time = %s %04d/%02d/%02d %02d:%02d:%02d\n",
+ weekdays[weekday()->IsSmi() ? Smi::cast(weekday())->value() + 1 : 0],
+ year()->IsSmi() ? Smi::cast(year())->value() : -1,
+ month()->IsSmi() ? Smi::cast(month())->value() : -1,
+ day()->IsSmi() ? Smi::cast(day())->value() : -1,
+ hour()->IsSmi() ? Smi::cast(hour())->value() : -1,
+ min()->IsSmi() ? Smi::cast(min())->value() : -1,
+ sec()->IsSmi() ? Smi::cast(sec())->value() : -1);
+ }
+}
+
+
+void JSProxy::JSProxyPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSProxy");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - handler = ");
+ handler()->Print(out);
+ PrintF(out, " - hash = ");
+ hash()->Print(out);
+ PrintF(out, "\n");
+}
+
+
+void JSFunctionProxy::JSFunctionProxyPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSFunctionProxy");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - handler = ");
+ handler()->Print(out);
+ PrintF(out, " - call_trap = ");
+ call_trap()->Print(out);
+ PrintF(out, " - construct_trap = ");
+ construct_trap()->Print(out);
+ PrintF(out, "\n");
+}
+
+
+void JSSet::JSSetPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSSet");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - table = ");
+ table()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSMap::JSMapPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSMap");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - table = ");
+ table()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSWeakMap::JSWeakMapPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSWeakMap");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - table = ");
+ table()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSWeakSet::JSWeakSetPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSWeakSet");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - table = ");
+ table()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSArrayBuffer::JSArrayBufferPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSArrayBuffer");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - backing_store = %p\n", backing_store());
+ PrintF(out, " - byte_length = ");
+ byte_length()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSTypedArray::JSTypedArrayPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSTypedArray");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - buffer =");
+ buffer()->ShortPrint(out);
+ PrintF(out, "\n - byte_offset = ");
+ byte_offset()->ShortPrint(out);
+ PrintF(out, "\n - byte_length = ");
+ byte_length()->ShortPrint(out);
+ PrintF(out, "\n - length = ");
+ length()->ShortPrint(out);
+ PrintF("\n");
+ PrintElements(out);
+}
+
+
+void JSDataView::JSDataViewPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSDataView");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - buffer =");
+ buffer()->ShortPrint(out);
+ PrintF(out, "\n - byte_offset = ");
+ byte_offset()->ShortPrint(out);
+ PrintF(out, "\n - byte_length = ");
+ byte_length()->ShortPrint(out);
+ PrintF("\n");
+}
+
+
+void JSFunction::JSFunctionPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Function");
+ PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - initial_map = ");
+ if (has_initial_map()) {
+ initial_map()->ShortPrint(out);
+ }
+ PrintF(out, "\n - shared_info = ");
+ shared()->ShortPrint(out);
+ PrintF(out, "\n - name = ");
+ shared()->name()->Print(out);
+ PrintF(out, "\n - context = ");
+ context()->ShortPrint(out);
+ PrintF(out, "\n - literals = ");
+ literals()->ShortPrint(out);
+ PrintF(out, "\n - code = ");
+ code()->ShortPrint(out);
+ PrintF(out, "\n");
+
+ PrintProperties(out);
+ PrintElements(out);
+
+ PrintF(out, "\n");
+}
+
+
+void SharedFunctionInfo::SharedFunctionInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "SharedFunctionInfo");
+ PrintF(out, " - name: ");
+ name()->ShortPrint(out);
+ PrintF(out, "\n - expected_nof_properties: %d", expected_nof_properties());
+ PrintF(out, "\n - instance class name = ");
+ instance_class_name()->Print(out);
+ PrintF(out, "\n - code = ");
+ code()->ShortPrint(out);
+ if (HasSourceCode()) {
+ PrintF(out, "\n - source code = ");
+ String* source = String::cast(Script::cast(script())->source());
+ int start = start_position();
+ int length = end_position() - start;
+ SmartArrayPointer<char> source_string =
+ source->ToCString(DISALLOW_NULLS,
+ FAST_STRING_TRAVERSAL,
+ start, length, NULL);
+ PrintF(out, "%s", *source_string);
+ }
+ // Script files are often large, hard to read.
+ // PrintF(out, "\n - script =");
+ // script()->Print(out);
+ PrintF(out, "\n - function token position = %d", function_token_position());
+ PrintF(out, "\n - start position = %d", start_position());
+ PrintF(out, "\n - end position = %d", end_position());
+ PrintF(out, "\n - is expression = %d", is_expression());
+ PrintF(out, "\n - debug info = ");
+ debug_info()->ShortPrint(out);
+ PrintF(out, "\n - length = %d", length());
+ PrintF(out, "\n - optimized_code_map = ");
+ optimized_code_map()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSGlobalProxy::JSGlobalProxyPrint(FILE* out) {
+ PrintF(out, "global_proxy ");
+ JSObjectPrint(out);
+ PrintF(out, "native context : ");
+ native_context()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSGlobalObject::JSGlobalObjectPrint(FILE* out) {
+ PrintF(out, "global ");
+ JSObjectPrint(out);
+ PrintF(out, "native context : ");
+ native_context()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void JSBuiltinsObject::JSBuiltinsObjectPrint(FILE* out) {
+ PrintF(out, "builtins ");
+ JSObjectPrint(out);
+}
+
+
+void Cell::CellPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Cell");
+}
+
+
+void PropertyCell::PropertyCellPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "PropertyCell");
+}
+
+
+void Code::CodePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Code");
+#ifdef ENABLE_DISASSEMBLER
+ if (FLAG_use_verbose_printer) {
+ Disassemble(NULL, out);
+ }
+#endif
+}
+
+
+void Foreign::ForeignPrint(FILE* out) {
+ PrintF(out, "foreign address : %p", foreign_address());
+}
+
+
+void ExecutableAccessorInfo::ExecutableAccessorInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "ExecutableAccessorInfo");
+ PrintF(out, "\n - name: ");
+ name()->ShortPrint(out);
+ PrintF(out, "\n - flag: ");
+ flag()->ShortPrint(out);
+ PrintF(out, "\n - getter: ");
+ getter()->ShortPrint(out);
+ PrintF(out, "\n - setter: ");
+ setter()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+}
+
+
+void DeclaredAccessorInfo::DeclaredAccessorInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "DeclaredAccessorInfo");
+ PrintF(out, "\n - name: ");
+ name()->ShortPrint(out);
+ PrintF(out, "\n - flag: ");
+ flag()->ShortPrint(out);
+ PrintF(out, "\n - descriptor: ");
+ descriptor()->ShortPrint(out);
+}
+
+
+void DeclaredAccessorDescriptor::DeclaredAccessorDescriptorPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "DeclaredAccessorDescriptor");
+ PrintF(out, "\n - internal field: ");
+ serialized_data()->ShortPrint(out);
+}
+
+
+void Box::BoxPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Box");
+ PrintF(out, "\n - value: ");
+ value()->ShortPrint(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: ");
+ named_callback()->ShortPrint(out);
+ PrintF(out, "\n - indexed_callback: ");
+ indexed_callback()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+}
+
+
+void InterceptorInfo::InterceptorInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "InterceptorInfo");
+ PrintF(out, "\n - getter: ");
+ getter()->ShortPrint(out);
+ PrintF(out, "\n - setter: ");
+ setter()->ShortPrint(out);
+ PrintF(out, "\n - query: ");
+ query()->ShortPrint(out);
+ PrintF(out, "\n - deleter: ");
+ deleter()->ShortPrint(out);
+ PrintF(out, "\n - enumerator: ");
+ enumerator()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+}
+
+
+void CallHandlerInfo::CallHandlerInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "CallHandlerInfo");
+ PrintF(out, "\n - callback: ");
+ callback()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+ PrintF(out, "\n - call_stub_cache: ");
+}
+
+
+void FunctionTemplateInfo::FunctionTemplateInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "FunctionTemplateInfo");
+ PrintF(out, "\n - class name: ");
+ class_name()->ShortPrint(out);
+ PrintF(out, "\n - tag: ");
+ tag()->ShortPrint(out);
+ PrintF(out, "\n - property_list: ");
+ property_list()->ShortPrint(out);
+ PrintF(out, "\n - serial_number: ");
+ serial_number()->ShortPrint(out);
+ PrintF(out, "\n - call_code: ");
+ call_code()->ShortPrint(out);
+ PrintF(out, "\n - property_accessors: ");
+ property_accessors()->ShortPrint(out);
+ PrintF(out, "\n - prototype_template: ");
+ prototype_template()->ShortPrint(out);
+ PrintF(out, "\n - parent_template: ");
+ parent_template()->ShortPrint(out);
+ PrintF(out, "\n - named_property_handler: ");
+ named_property_handler()->ShortPrint(out);
+ PrintF(out, "\n - indexed_property_handler: ");
+ indexed_property_handler()->ShortPrint(out);
+ PrintF(out, "\n - instance_template: ");
+ instance_template()->ShortPrint(out);
+ PrintF(out, "\n - signature: ");
+ signature()->ShortPrint(out);
+ PrintF(out, "\n - access_check_info: ");
+ access_check_info()->ShortPrint(out);
+ PrintF(out, "\n - hidden_prototype: %s",
+ hidden_prototype() ? "true" : "false");
+ PrintF(out, "\n - undetectable: %s", undetectable() ? "true" : "false");
+ PrintF(out, "\n - need_access_check: %s",
+ needs_access_check() ? "true" : "false");
+}
+
+
+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");
+}
+
+
+void SignatureInfo::SignatureInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "SignatureInfo");
+ PrintF(out, "\n - receiver: ");
+ receiver()->ShortPrint(out);
+ PrintF(out, "\n - args: ");
+ args()->ShortPrint(out);
+}
+
+
+void TypeSwitchInfo::TypeSwitchInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "TypeSwitchInfo");
+ PrintF(out, "\n - types: ");
+ types()->ShortPrint(out);
+}
+
+
+void AllocationSite::AllocationSitePrint(FILE* out) {
+ HeapObject::PrintHeader(out, "AllocationSite");
+ PrintF(out, " - weak_next: ");
+ weak_next()->ShortPrint(out);
+ PrintF(out, "\n");
+
+ PrintF(out, " - transition_info: ");
+ if (transition_info()->IsCell()) {
+ Cell* cell = Cell::cast(transition_info());
+ Object* cell_contents = cell->value();
+ if (cell_contents->IsSmi()) {
+ ElementsKind kind = static_cast<ElementsKind>(
+ Smi::cast(cell_contents)->value());
+ PrintF(out, "Array allocation with ElementsKind ");
+ PrintElementsKind(out, kind);
+ PrintF(out, "\n");
+ return;
+ }
+ } else if (transition_info()->IsJSArray()) {
+ PrintF(out, "Array literal ");
+ transition_info()->ShortPrint(out);
+ PrintF(out, "\n");
+ return;
+ }
+
+ PrintF(out, "unknown transition_info");
+ transition_info()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+void AllocationMemento::AllocationMementoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "AllocationMemento");
+ PrintF(out, " - allocation site: ");
+ if (IsValid()) {
+ GetAllocationSite()->Print();
+ } else {
+ PrintF(out, "<invalid>\n");
+ }
+}
+
+
+void Script::ScriptPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "Script");
+ PrintF(out, "\n - source: ");
+ source()->ShortPrint(out);
+ PrintF(out, "\n - name: ");
+ name()->ShortPrint(out);
+ PrintF(out, "\n - line_offset: ");
+ line_offset()->ShortPrint(out);
+ PrintF(out, "\n - column_offset: ");
+ column_offset()->ShortPrint(out);
+ PrintF(out, "\n - type: ");
+ type()->ShortPrint(out);
+ PrintF(out, "\n - id: ");
+ id()->ShortPrint(out);
+ PrintF(out, "\n - data: ");
+ data()->ShortPrint(out);
+ PrintF(out, "\n - context data: ");
+ context_data()->ShortPrint(out);
+ PrintF(out, "\n - wrapper: ");
+ wrapper()->ShortPrint(out);
+ PrintF(out, "\n - compilation type: %d", compilation_type());
+ PrintF(out, "\n - line ends: ");
+ line_ends()->ShortPrint(out);
+ PrintF(out, "\n - eval from shared: ");
+ eval_from_shared()->ShortPrint(out);
+ PrintF(out, "\n - eval from instructions offset: ");
+ eval_from_instructions_offset()->ShortPrint(out);
+ PrintF(out, "\n");
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void DebugInfo::DebugInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "DebugInfo");
+ PrintF(out, "\n - shared: ");
+ shared()->ShortPrint(out);
+ PrintF(out, "\n - original_code: ");
+ original_code()->ShortPrint(out);
+ PrintF(out, "\n - code: ");
+ code()->ShortPrint(out);
+ PrintF(out, "\n - break_points: ");
+ break_points()->Print(out);
+}
+
+
+void BreakPointInfo::BreakPointInfoPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "BreakPointInfo");
+ PrintF(out, "\n - code_position: %d", code_position()->value());
+ PrintF(out, "\n - source_position: %d", source_position()->value());
+ PrintF(out, "\n - statement_position: %d", statement_position()->value());
+ PrintF(out, "\n - break_point_objects: ");
+ break_point_objects()->ShortPrint(out);
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+void DescriptorArray::PrintDescriptors(FILE* out) {
+ PrintF(out, "Descriptor array %d\n", number_of_descriptors());
+ for (int i = 0; i < number_of_descriptors(); i++) {
+ PrintF(out, " %d: ", i);
+ Descriptor desc;
+ Get(i, &desc);
+ desc.Print(out);
+ }
+ PrintF(out, "\n");
+}
+
+
+void TransitionArray::PrintTransitions(FILE* out) {
+ PrintF(out, "Transition array %d\n", number_of_transitions());
+ for (int i = 0; i < number_of_transitions(); i++) {
+ PrintF(out, " %d: ", i);
+ GetKey(i)->NamePrint(out);
+ PrintF(out, ": ");
+ switch (GetTargetDetails(i).type()) {
+ case FIELD: {
+ PrintF(out, " (transition to field)\n");
+ break;
+ }
+ case CONSTANT:
+ PrintF(out, " (transition to constant)\n");
+ break;
+ case CALLBACKS:
+ PrintF(out, " (transition to callback)\n");
+ break;
+ // Values below are never in the target descriptor array.
+ case NORMAL:
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+ PrintF(out, "\n");
+}
+
+
+#endif // OBJECT_PRINT
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/objects-visiting-inl.h b/chromium/v8/src/objects-visiting-inl.h
new file mode 100644
index 00000000000..9398d6dfeaa
--- /dev/null
+++ b/chromium/v8/src/objects-visiting-inl.h
@@ -0,0 +1,891 @@
+// Copyright 2012 the V8 project authors. 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(kVisitSymbol,
+ &FixedBodyVisitor<StaticVisitor,
+ Symbol::BodyDescriptor,
+ int>::Visit);
+
+ table_.Register(kVisitFixedArray,
+ &FlexibleBodyVisitor<StaticVisitor,
+ FixedArray::BodyDescriptor,
+ int>::Visit);
+
+ table_.Register(kVisitFixedDoubleArray, &VisitFixedDoubleArray);
+
+ table_.Register(kVisitNativeContext,
+ &FixedBodyVisitor<StaticVisitor,
+ Context::ScavengeBodyDescriptor,
+ int>::Visit);
+
+ table_.Register(kVisitByteArray, &VisitByteArray);
+
+ table_.Register(kVisitSharedFunctionInfo,
+ &FixedBodyVisitor<StaticVisitor,
+ SharedFunctionInfo::BodyDescriptor,
+ int>::Visit);
+
+ table_.Register(kVisitSeqOneByteString, &VisitSeqOneByteString);
+
+ table_.Register(kVisitSeqTwoByteString, &VisitSeqTwoByteString);
+
+ table_.Register(kVisitJSFunction, &VisitJSFunction);
+
+ table_.Register(kVisitJSArrayBuffer, &VisitJSArrayBuffer);
+
+ table_.Register(kVisitJSTypedArray, &VisitJSTypedArray);
+
+ table_.Register(kVisitJSDataView, &VisitJSDataView);
+
+ table_.Register(kVisitFreeSpace, &VisitFreeSpace);
+
+ table_.Register(kVisitJSWeakMap, &JSObjectVisitor::Visit);
+
+ table_.Register(kVisitJSWeakSet, &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>();
+}
+
+
+template<typename StaticVisitor>
+int StaticNewSpaceVisitor<StaticVisitor>::VisitJSArrayBuffer(
+ Map* map, HeapObject* object) {
+ Heap* heap = map->GetHeap();
+
+ STATIC_ASSERT(
+ JSArrayBuffer::kWeakFirstViewOffset ==
+ JSArrayBuffer::kWeakNextOffset + kPointerSize);
+ VisitPointers(
+ heap,
+ HeapObject::RawField(object, JSArrayBuffer::BodyDescriptor::kStartOffset),
+ HeapObject::RawField(object, JSArrayBuffer::kWeakNextOffset));
+ VisitPointers(
+ heap,
+ HeapObject::RawField(object,
+ JSArrayBuffer::kWeakNextOffset + 2 * kPointerSize),
+ HeapObject::RawField(object, JSArrayBuffer::kSizeWithInternalFields));
+ return JSArrayBuffer::kSizeWithInternalFields;
+}
+
+
+template<typename StaticVisitor>
+int StaticNewSpaceVisitor<StaticVisitor>::VisitJSTypedArray(
+ Map* map, HeapObject* object) {
+ VisitPointers(
+ map->GetHeap(),
+ HeapObject::RawField(object, JSTypedArray::BodyDescriptor::kStartOffset),
+ HeapObject::RawField(object, JSTypedArray::kWeakNextOffset));
+ VisitPointers(
+ map->GetHeap(),
+ HeapObject::RawField(object,
+ JSTypedArray::kWeakNextOffset + kPointerSize),
+ HeapObject::RawField(object, JSTypedArray::kSizeWithInternalFields));
+ return JSTypedArray::kSizeWithInternalFields;
+}
+
+
+template<typename StaticVisitor>
+int StaticNewSpaceVisitor<StaticVisitor>::VisitJSDataView(
+ Map* map, HeapObject* object) {
+ VisitPointers(
+ map->GetHeap(),
+ HeapObject::RawField(object, JSDataView::BodyDescriptor::kStartOffset),
+ HeapObject::RawField(object, JSDataView::kWeakNextOffset));
+ VisitPointers(
+ map->GetHeap(),
+ HeapObject::RawField(object,
+ JSDataView::kWeakNextOffset + kPointerSize),
+ HeapObject::RawField(object, JSDataView::kSizeWithInternalFields));
+ return JSDataView::kSizeWithInternalFields;
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::Initialize() {
+ table_.Register(kVisitShortcutCandidate,
+ &FixedBodyVisitor<StaticVisitor,
+ ConsString::BodyDescriptor,
+ void>::Visit);
+
+ table_.Register(kVisitConsString,
+ &FixedBodyVisitor<StaticVisitor,
+ ConsString::BodyDescriptor,
+ void>::Visit);
+
+ table_.Register(kVisitSlicedString,
+ &FixedBodyVisitor<StaticVisitor,
+ SlicedString::BodyDescriptor,
+ void>::Visit);
+
+ table_.Register(kVisitSymbol,
+ &FixedBodyVisitor<StaticVisitor,
+ Symbol::BodyDescriptor,
+ void>::Visit);
+
+ table_.Register(kVisitFixedArray, &FixedArrayVisitor::Visit);
+
+ table_.Register(kVisitFixedDoubleArray, &DataObjectVisitor::Visit);
+
+ table_.Register(kVisitNativeContext, &VisitNativeContext);
+
+ table_.Register(kVisitAllocationSite,
+ &FixedBodyVisitor<StaticVisitor,
+ AllocationSite::BodyDescriptor,
+ void>::Visit);
+
+ table_.Register(kVisitByteArray, &DataObjectVisitor::Visit);
+
+ table_.Register(kVisitFreeSpace, &DataObjectVisitor::Visit);
+
+ table_.Register(kVisitSeqOneByteString, &DataObjectVisitor::Visit);
+
+ table_.Register(kVisitSeqTwoByteString, &DataObjectVisitor::Visit);
+
+ table_.Register(kVisitJSWeakMap, &StaticVisitor::VisitWeakCollection);
+
+ table_.Register(kVisitJSWeakSet, &StaticVisitor::VisitWeakCollection);
+
+ table_.Register(kVisitOddball,
+ &FixedBodyVisitor<StaticVisitor,
+ Oddball::BodyDescriptor,
+ void>::Visit);
+
+ table_.Register(kVisitMap, &VisitMap);
+
+ table_.Register(kVisitCode, &VisitCode);
+
+ table_.Register(kVisitSharedFunctionInfo, &VisitSharedFunctionInfo);
+
+ table_.Register(kVisitJSFunction, &VisitJSFunction);
+
+ table_.Register(kVisitJSArrayBuffer, &VisitJSArrayBuffer);
+
+ table_.Register(kVisitJSTypedArray, &VisitJSTypedArray);
+
+ table_.Register(kVisitJSDataView, &VisitJSDataView);
+
+ // Registration for kVisitJSRegExp is done by StaticVisitor.
+
+ table_.Register(kVisitCell,
+ &FixedBodyVisitor<StaticVisitor,
+ Cell::BodyDescriptor,
+ void>::Visit);
+
+ table_.Register(kVisitPropertyCell, &VisitPropertyCell);
+
+ table_.template RegisterSpecializations<DataObjectVisitor,
+ kVisitDataObject,
+ kVisitDataObjectGeneric>();
+
+ table_.template RegisterSpecializations<JSObjectVisitor,
+ kVisitJSObject,
+ kVisitJSObjectGeneric>();
+
+ table_.template RegisterSpecializations<StructObjectVisitor,
+ kVisitStruct,
+ kVisitStructGeneric>();
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitCodeEntry(
+ Heap* heap, Address entry_address) {
+ Code* code = Code::cast(Code::GetObjectFromEntryAddress(entry_address));
+ heap->mark_compact_collector()->RecordCodeEntrySlot(entry_address, code);
+ StaticVisitor::MarkObject(heap, code);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitEmbeddedPointer(
+ Heap* heap, RelocInfo* rinfo) {
+ ASSERT(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT);
+ ASSERT(!rinfo->target_object()->IsConsString());
+ HeapObject* object = HeapObject::cast(rinfo->target_object());
+ if (!FLAG_weak_embedded_maps_in_optimized_code || !FLAG_collect_maps ||
+ rinfo->host()->kind() != Code::OPTIMIZED_FUNCTION ||
+ !object->IsMap() || !Map::cast(object)->CanTransition()) {
+ heap->mark_compact_collector()->RecordRelocSlot(rinfo, object);
+ StaticVisitor::MarkObject(heap, object);
+ }
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitCell(
+ Heap* heap, RelocInfo* rinfo) {
+ ASSERT(rinfo->rmode() == RelocInfo::CELL);
+ Cell* cell = rinfo->target_cell();
+ StaticVisitor::MarkObject(heap, cell);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitDebugTarget(
+ Heap* heap, RelocInfo* rinfo) {
+ ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+ rinfo->IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence()));
+ Code* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
+ heap->mark_compact_collector()->RecordRelocSlot(rinfo, target);
+ StaticVisitor::MarkObject(heap, target);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitCodeTarget(
+ Heap* heap, RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
+ Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ // 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.
+ if (FLAG_cleanup_code_caches_at_gc && target->is_inline_cache_stub()
+ && (target->ic_state() == MEGAMORPHIC || target->ic_state() == GENERIC ||
+ target->ic_state() == POLYMORPHIC || heap->flush_monomorphic_ics() ||
+ Serializer::enabled() || target->ic_age() != heap->global_ic_age())) {
+ IC::Clear(rinfo->pc());
+ target = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ }
+ heap->mark_compact_collector()->RecordRelocSlot(rinfo, target);
+ StaticVisitor::MarkObject(heap, target);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitCodeAgeSequence(
+ Heap* heap, RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsCodeAgeSequence(rinfo->rmode()));
+ Code* target = rinfo->code_age_stub();
+ ASSERT(target != NULL);
+ heap->mark_compact_collector()->RecordRelocSlot(rinfo, target);
+ StaticVisitor::MarkObject(heap, target);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitNativeContext(
+ Map* map, HeapObject* object) {
+ FixedBodyVisitor<StaticVisitor,
+ Context::MarkCompactBodyDescriptor,
+ void>::Visit(map, object);
+
+ MarkCompactCollector* collector = map->GetHeap()->mark_compact_collector();
+ for (int idx = Context::FIRST_WEAK_SLOT;
+ idx < Context::NATIVE_CONTEXT_SLOTS;
+ ++idx) {
+ Object** slot =
+ HeapObject::RawField(object, FixedArray::OffsetOfElementAt(idx));
+ collector->RecordSlot(slot, slot, *slot);
+ }
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitMap(
+ Map* map, HeapObject* object) {
+ Heap* heap = map->GetHeap();
+ Map* map_object = Map::cast(object);
+
+ // Clears the cache of ICs related to this map.
+ if (FLAG_cleanup_code_caches_at_gc) {
+ map_object->ClearCodeCache(heap);
+ }
+
+ // When map collection is enabled we have to mark through map's transitions
+ // and back pointers in a special way to make these links weak.
+ if (FLAG_collect_maps && map_object->CanTransition()) {
+ MarkMapContents(heap, map_object);
+ } else {
+ StaticVisitor::VisitPointers(heap,
+ HeapObject::RawField(object, Map::kPointerFieldsBeginOffset),
+ HeapObject::RawField(object, Map::kPointerFieldsEndOffset));
+ }
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitPropertyCell(
+ Map* map, HeapObject* object) {
+ Heap* heap = map->GetHeap();
+
+ Object** slot =
+ HeapObject::RawField(object, PropertyCell::kDependentCodeOffset);
+ if (FLAG_collect_maps) {
+ // Mark property cell dependent codes array but do not push it onto marking
+ // stack, this will make references from it weak. We will clean dead
+ // codes when we iterate over property cells in ClearNonLiveReferences.
+ HeapObject* obj = HeapObject::cast(*slot);
+ heap->mark_compact_collector()->RecordSlot(slot, slot, obj);
+ StaticVisitor::MarkObjectWithoutPush(heap, obj);
+ } else {
+ StaticVisitor::VisitPointer(heap, slot);
+ }
+
+ StaticVisitor::VisitPointers(heap,
+ HeapObject::RawField(object, PropertyCell::kPointerFieldsBeginOffset),
+ HeapObject::RawField(object, PropertyCell::kPointerFieldsEndOffset));
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitCode(
+ Map* map, HeapObject* object) {
+ Heap* heap = map->GetHeap();
+ Code* code = Code::cast(object);
+ if (FLAG_cleanup_code_caches_at_gc) {
+ code->ClearTypeFeedbackCells(heap);
+ }
+ if (FLAG_age_code && !Serializer::enabled()) {
+ code->MakeOlder(heap->mark_compact_collector()->marking_parity());
+ }
+ code->CodeIterateBody<StaticVisitor>(heap);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitSharedFunctionInfo(
+ Map* map, HeapObject* object) {
+ Heap* heap = map->GetHeap();
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
+ if (shared->ic_age() != heap->global_ic_age()) {
+ shared->ResetForNewContext(heap->global_ic_age());
+ }
+ if (FLAG_cache_optimized_code &&
+ FLAG_flush_optimized_code_cache &&
+ !shared->optimized_code_map()->IsSmi()) {
+ // Always flush the optimized code map if requested by flag.
+ shared->ClearOptimizedCodeMap();
+ }
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+ if (collector->is_code_flushing_enabled()) {
+ if (FLAG_cache_optimized_code && !shared->optimized_code_map()->IsSmi()) {
+ // Add the shared function info holding an optimized code map to
+ // the code flusher for processing of code maps after marking.
+ collector->code_flusher()->AddOptimizedCodeMap(shared);
+ // Treat all references within the code map weakly by marking the
+ // code map itself but not pushing it onto the marking deque.
+ FixedArray* code_map = FixedArray::cast(shared->optimized_code_map());
+ StaticVisitor::MarkObjectWithoutPush(heap, code_map);
+ }
+ if (IsFlushable(heap, shared)) {
+ // This function's code looks flushable. But we have to postpone
+ // the decision until we see all functions that point to the same
+ // SharedFunctionInfo because some of them might be optimized.
+ // That would also make the non-optimized version of the code
+ // non-flushable, because it is required for bailing out from
+ // optimized code.
+ collector->code_flusher()->AddCandidate(shared);
+ // Treat the reference to the code object weakly.
+ VisitSharedFunctionInfoWeakCode(heap, object);
+ return;
+ }
+ } else {
+ if (FLAG_cache_optimized_code && !shared->optimized_code_map()->IsSmi()) {
+ // Flush optimized code map on major GCs without code flushing,
+ // needed because cached code doesn't contain breakpoints.
+ shared->ClearOptimizedCodeMap();
+ }
+ }
+ VisitSharedFunctionInfoStrongCode(heap, object);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitJSFunction(
+ Map* map, HeapObject* object) {
+ Heap* heap = map->GetHeap();
+ JSFunction* function = JSFunction::cast(object);
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+ if (collector->is_code_flushing_enabled()) {
+ if (IsFlushable(heap, function)) {
+ // This function's code looks flushable. But we have to postpone
+ // the decision until we see all functions that point to the same
+ // SharedFunctionInfo because some of them might be optimized.
+ // That would also make the non-optimized version of the code
+ // non-flushable, because it is required for bailing out from
+ // optimized code.
+ collector->code_flusher()->AddCandidate(function);
+ // Visit shared function info immediately to avoid double checking
+ // of its flushability later. This is just an optimization because
+ // the shared function info would eventually be visited.
+ SharedFunctionInfo* shared = function->shared();
+ if (StaticVisitor::MarkObjectWithoutPush(heap, shared)) {
+ StaticVisitor::MarkObject(heap, shared->map());
+ VisitSharedFunctionInfoWeakCode(heap, shared);
+ }
+ // Treat the reference to the code object weakly.
+ VisitJSFunctionWeakCode(heap, object);
+ return;
+ } else {
+ // Visit all unoptimized code objects to prevent flushing them.
+ StaticVisitor::MarkObject(heap, function->shared()->code());
+ if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) {
+ MarkInlinedFunctionsCode(heap, function->code());
+ }
+ }
+ }
+ VisitJSFunctionStrongCode(heap, object);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitJSRegExp(
+ Map* map, HeapObject* object) {
+ int last_property_offset =
+ JSRegExp::kSize + kPointerSize * map->inobject_properties();
+ StaticVisitor::VisitPointers(map->GetHeap(),
+ HeapObject::RawField(object, JSRegExp::kPropertiesOffset),
+ HeapObject::RawField(object, last_property_offset));
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitJSArrayBuffer(
+ Map* map, HeapObject* object) {
+ Heap* heap = map->GetHeap();
+
+ STATIC_ASSERT(
+ JSArrayBuffer::kWeakFirstViewOffset ==
+ JSArrayBuffer::kWeakNextOffset + kPointerSize);
+ StaticVisitor::VisitPointers(
+ heap,
+ HeapObject::RawField(object, JSArrayBuffer::BodyDescriptor::kStartOffset),
+ HeapObject::RawField(object, JSArrayBuffer::kWeakNextOffset));
+ StaticVisitor::VisitPointers(
+ heap,
+ HeapObject::RawField(object,
+ JSArrayBuffer::kWeakNextOffset + 2 * kPointerSize),
+ HeapObject::RawField(object, JSArrayBuffer::kSizeWithInternalFields));
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitJSTypedArray(
+ Map* map, HeapObject* object) {
+ StaticVisitor::VisitPointers(
+ map->GetHeap(),
+ HeapObject::RawField(object, JSTypedArray::BodyDescriptor::kStartOffset),
+ HeapObject::RawField(object, JSTypedArray::kWeakNextOffset));
+ StaticVisitor::VisitPointers(
+ map->GetHeap(),
+ HeapObject::RawField(object,
+ JSTypedArray::kWeakNextOffset + kPointerSize),
+ HeapObject::RawField(object, JSTypedArray::kSizeWithInternalFields));
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitJSDataView(
+ Map* map, HeapObject* object) {
+ StaticVisitor::VisitPointers(
+ map->GetHeap(),
+ HeapObject::RawField(object, JSDataView::BodyDescriptor::kStartOffset),
+ HeapObject::RawField(object, JSDataView::kWeakNextOffset));
+ StaticVisitor::VisitPointers(
+ map->GetHeap(),
+ HeapObject::RawField(object,
+ JSDataView::kWeakNextOffset + kPointerSize),
+ HeapObject::RawField(object, JSDataView::kSizeWithInternalFields));
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::MarkMapContents(
+ Heap* heap, Map* map) {
+ // Make sure that the back pointer stored either in the map itself or
+ // inside its transitions array is marked. Skip recording the back
+ // pointer slot since map space is not compacted.
+ StaticVisitor::MarkObject(heap, HeapObject::cast(map->GetBackPointer()));
+
+ // Treat pointers in the transitions array as weak and also mark that
+ // array to prevent visiting it later. Skip recording the transition
+ // array slot, since it will be implicitly recorded when the pointer
+ // fields of this map are visited.
+ TransitionArray* transitions = map->unchecked_transition_array();
+ if (transitions->IsTransitionArray()) {
+ MarkTransitionArray(heap, transitions);
+ } else {
+ // Already marked by marking map->GetBackPointer() above.
+ ASSERT(transitions->IsMap() || transitions->IsUndefined());
+ }
+
+ // Since descriptor arrays are potentially shared, ensure that only the
+ // descriptors that belong to this map are marked. The first time a
+ // non-empty descriptor array is marked, its header is also visited. The slot
+ // holding the descriptor array will be implicitly recorded when the pointer
+ // fields of this map are visited.
+ DescriptorArray* descriptors = map->instance_descriptors();
+ if (StaticVisitor::MarkObjectWithoutPush(heap, descriptors) &&
+ descriptors->length() > 0) {
+ StaticVisitor::VisitPointers(heap,
+ descriptors->GetFirstElementAddress(),
+ descriptors->GetDescriptorEndSlot(0));
+ }
+ int start = 0;
+ int end = map->NumberOfOwnDescriptors();
+ if (start < end) {
+ StaticVisitor::VisitPointers(heap,
+ descriptors->GetDescriptorStartSlot(start),
+ descriptors->GetDescriptorEndSlot(end));
+ }
+
+ // Mark prototype dependent codes array but do not push it onto marking
+ // stack, this will make references from it weak. We will clean dead
+ // codes when we iterate over maps in ClearNonLiveTransitions.
+ Object** slot = HeapObject::RawField(map, Map::kDependentCodeOffset);
+ HeapObject* obj = HeapObject::cast(*slot);
+ heap->mark_compact_collector()->RecordSlot(slot, slot, obj);
+ StaticVisitor::MarkObjectWithoutPush(heap, obj);
+
+ // Mark the pointer fields of the Map. Since the transitions array has
+ // been marked already, it is fine that one of these fields contains a
+ // pointer to it.
+ StaticVisitor::VisitPointers(heap,
+ HeapObject::RawField(map, Map::kPointerFieldsBeginOffset),
+ HeapObject::RawField(map, Map::kPointerFieldsEndOffset));
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::MarkTransitionArray(
+ Heap* heap, TransitionArray* transitions) {
+ if (!StaticVisitor::MarkObjectWithoutPush(heap, transitions)) return;
+
+ // Simple transitions do not have keys nor prototype transitions.
+ if (transitions->IsSimpleTransition()) return;
+
+ if (transitions->HasPrototypeTransitions()) {
+ // Mark prototype transitions array but do not push it onto marking
+ // stack, this will make references from it weak. We will clean dead
+ // prototype transitions in ClearNonLiveTransitions.
+ Object** slot = transitions->GetPrototypeTransitionsSlot();
+ HeapObject* obj = HeapObject::cast(*slot);
+ heap->mark_compact_collector()->RecordSlot(slot, slot, obj);
+ StaticVisitor::MarkObjectWithoutPush(heap, obj);
+ }
+
+ for (int i = 0; i < transitions->number_of_transitions(); ++i) {
+ StaticVisitor::VisitPointer(heap, transitions->GetKeySlot(i));
+ }
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::MarkInlinedFunctionsCode(
+ Heap* heap, Code* code) {
+ // For optimized functions we should retain both non-optimized version
+ // of its code and non-optimized version of all inlined functions.
+ // This is required to support bailing out from inlined code.
+ DeoptimizationInputData* data =
+ DeoptimizationInputData::cast(code->deoptimization_data());
+ FixedArray* literals = data->LiteralArray();
+ for (int i = 0, count = data->InlinedFunctionCount()->value();
+ i < count;
+ i++) {
+ JSFunction* inlined = JSFunction::cast(literals->get(i));
+ StaticVisitor::MarkObject(heap, inlined->shared()->code());
+ }
+}
+
+
+inline static bool IsValidNonBuiltinContext(Object* context) {
+ return context->IsContext() &&
+ !Context::cast(context)->global_object()->IsJSBuiltinsObject();
+}
+
+
+inline static bool HasSourceCode(Heap* heap, SharedFunctionInfo* info) {
+ Object* undefined = heap->undefined_value();
+ return (info->script() != undefined) &&
+ (reinterpret_cast<Script*>(info->script())->source() != undefined);
+}
+
+
+template<typename StaticVisitor>
+bool StaticMarkingVisitor<StaticVisitor>::IsFlushable(
+ Heap* heap, JSFunction* function) {
+ SharedFunctionInfo* shared_info = function->shared();
+
+ // Code is either on stack, in compilation cache or referenced
+ // by optimized version of function.
+ MarkBit code_mark = Marking::MarkBitFrom(function->code());
+ if (code_mark.Get()) {
+ return false;
+ }
+
+ // The function must have a valid context and not be a builtin.
+ if (!IsValidNonBuiltinContext(function->context())) {
+ return false;
+ }
+
+ // We do not (yet) flush code for optimized functions.
+ if (function->code() != shared_info->code()) {
+ return false;
+ }
+
+ // Check age of optimized code.
+ if (FLAG_age_code && !function->code()->IsOld()) {
+ return false;
+ }
+
+ return IsFlushable(heap, shared_info);
+}
+
+
+template<typename StaticVisitor>
+bool StaticMarkingVisitor<StaticVisitor>::IsFlushable(
+ Heap* heap, SharedFunctionInfo* shared_info) {
+ // Code is either on stack, in compilation cache or referenced
+ // by optimized version of function.
+ MarkBit code_mark = Marking::MarkBitFrom(shared_info->code());
+ if (code_mark.Get()) {
+ return false;
+ }
+
+ // The function must be compiled and have the source code available,
+ // to be able to recompile it in case we need the function again.
+ if (!(shared_info->is_compiled() && HasSourceCode(heap, shared_info))) {
+ return false;
+ }
+
+ // We never flush code for API functions.
+ Object* function_data = shared_info->function_data();
+ if (function_data->IsFunctionTemplateInfo()) {
+ return false;
+ }
+
+ // Only flush code for functions.
+ if (shared_info->code()->kind() != Code::FUNCTION) {
+ return false;
+ }
+
+ // Function must be lazy compilable.
+ if (!shared_info->allows_lazy_compilation()) {
+ return false;
+ }
+
+ // We do not (yet?) flush code for generator functions, because we don't know
+ // if there are still live activations (generator objects) on the heap.
+ if (shared_info->is_generator()) {
+ return false;
+ }
+
+ // If this is a full script wrapped in a function we do not flush the code.
+ if (shared_info->is_toplevel()) {
+ return false;
+ }
+
+ // If this is a function initialized with %SetCode then the one-to-one
+ // relation between SharedFunctionInfo and Code is broken.
+ if (shared_info->dont_flush()) {
+ return false;
+ }
+
+ // Check age of code. If code aging is disabled we never flush.
+ if (!FLAG_age_code || !shared_info->code()->IsOld()) {
+ return false;
+ }
+
+ return true;
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitSharedFunctionInfoStrongCode(
+ Heap* heap, HeapObject* object) {
+ StaticVisitor::BeforeVisitingSharedFunctionInfo(object);
+ Object** start_slot =
+ HeapObject::RawField(object,
+ SharedFunctionInfo::BodyDescriptor::kStartOffset);
+ Object** end_slot =
+ HeapObject::RawField(object,
+ SharedFunctionInfo::BodyDescriptor::kEndOffset);
+ StaticVisitor::VisitPointers(heap, start_slot, end_slot);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitSharedFunctionInfoWeakCode(
+ Heap* heap, HeapObject* object) {
+ StaticVisitor::BeforeVisitingSharedFunctionInfo(object);
+ Object** name_slot =
+ HeapObject::RawField(object, SharedFunctionInfo::kNameOffset);
+ StaticVisitor::VisitPointer(heap, name_slot);
+
+ // Skip visiting kCodeOffset as it is treated weakly here.
+ STATIC_ASSERT(SharedFunctionInfo::kNameOffset + kPointerSize ==
+ SharedFunctionInfo::kCodeOffset);
+ STATIC_ASSERT(SharedFunctionInfo::kCodeOffset + kPointerSize ==
+ SharedFunctionInfo::kOptimizedCodeMapOffset);
+
+ Object** start_slot =
+ HeapObject::RawField(object,
+ SharedFunctionInfo::kOptimizedCodeMapOffset);
+ Object** end_slot =
+ HeapObject::RawField(object,
+ SharedFunctionInfo::BodyDescriptor::kEndOffset);
+ StaticVisitor::VisitPointers(heap, start_slot, end_slot);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitJSFunctionStrongCode(
+ Heap* heap, HeapObject* object) {
+ Object** start_slot =
+ HeapObject::RawField(object, JSFunction::kPropertiesOffset);
+ Object** end_slot =
+ HeapObject::RawField(object, JSFunction::kCodeEntryOffset);
+ StaticVisitor::VisitPointers(heap, start_slot, end_slot);
+
+ VisitCodeEntry(heap, object->address() + JSFunction::kCodeEntryOffset);
+ STATIC_ASSERT(JSFunction::kCodeEntryOffset + kPointerSize ==
+ JSFunction::kPrototypeOrInitialMapOffset);
+
+ start_slot =
+ HeapObject::RawField(object, JSFunction::kPrototypeOrInitialMapOffset);
+ end_slot =
+ HeapObject::RawField(object, JSFunction::kNonWeakFieldsEndOffset);
+ StaticVisitor::VisitPointers(heap, start_slot, end_slot);
+}
+
+
+template<typename StaticVisitor>
+void StaticMarkingVisitor<StaticVisitor>::VisitJSFunctionWeakCode(
+ Heap* heap, HeapObject* object) {
+ Object** start_slot =
+ HeapObject::RawField(object, JSFunction::kPropertiesOffset);
+ Object** end_slot =
+ HeapObject::RawField(object, JSFunction::kCodeEntryOffset);
+ StaticVisitor::VisitPointers(heap, start_slot, end_slot);
+
+ // Skip visiting kCodeEntryOffset as it is treated weakly here.
+ STATIC_ASSERT(JSFunction::kCodeEntryOffset + kPointerSize ==
+ JSFunction::kPrototypeOrInitialMapOffset);
+
+ start_slot =
+ HeapObject::RawField(object, JSFunction::kPrototypeOrInitialMapOffset);
+ end_slot =
+ HeapObject::RawField(object, JSFunction::kNonWeakFieldsEndOffset);
+ StaticVisitor::VisitPointers(heap, start_slot, end_slot);
+}
+
+
+void Code::CodeIterateBody(ObjectVisitor* v) {
+ int mode_mask = RelocInfo::kCodeTargetMask |
+ RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
+ RelocInfo::ModeMask(RelocInfo::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, kTypeFeedbackInfoOffset);
+
+ 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::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() + kTypeFeedbackInfoOffset));
+
+ 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/chromium/v8/src/objects-visiting.cc b/chromium/v8/src/objects-visiting.cc
new file mode 100644
index 00000000000..cd46013398a
--- /dev/null
+++ b/chromium/v8/src/objects-visiting.cc
@@ -0,0 +1,202 @@
+// 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 "ic-inl.h"
+#include "objects-visiting.h"
+
+namespace v8 {
+namespace internal {
+
+
+static inline bool IsShortcutCandidate(int type) {
+ return ((type & kShortcutTypeMask) == kShortcutTypeTag);
+}
+
+
+StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
+ int instance_type,
+ int instance_size) {
+ if (instance_type < FIRST_NONSTRING_TYPE) {
+ switch (instance_type & kStringRepresentationMask) {
+ case kSeqStringTag:
+ if ((instance_type & kStringEncodingMask) == kOneByteStringTag) {
+ return kVisitSeqOneByteString;
+ } else {
+ return kVisitSeqTwoByteString;
+ }
+
+ case kConsStringTag:
+ if (IsShortcutCandidate(instance_type)) {
+ return kVisitShortcutCandidate;
+ } else {
+ return kVisitConsString;
+ }
+
+ case kSlicedStringTag:
+ return kVisitSlicedString;
+
+ case kExternalStringTag:
+ return GetVisitorIdForSize(kVisitDataObject,
+ kVisitDataObjectGeneric,
+ instance_size);
+ }
+ UNREACHABLE();
+ }
+
+ switch (instance_type) {
+ case BYTE_ARRAY_TYPE:
+ return kVisitByteArray;
+
+ case FREE_SPACE_TYPE:
+ return kVisitFreeSpace;
+
+ case FIXED_ARRAY_TYPE:
+ return kVisitFixedArray;
+
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ return kVisitFixedDoubleArray;
+
+ case ODDBALL_TYPE:
+ return kVisitOddball;
+
+ case MAP_TYPE:
+ return kVisitMap;
+
+ case CODE_TYPE:
+ return kVisitCode;
+
+ case CELL_TYPE:
+ return kVisitCell;
+
+ case 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;
+
+ case JS_WEAK_SET_TYPE:
+ return kVisitJSWeakSet;
+
+ case JS_REGEXP_TYPE:
+ return kVisitJSRegExp;
+
+ case SHARED_FUNCTION_INFO_TYPE:
+ return kVisitSharedFunctionInfo;
+
+ case JS_PROXY_TYPE:
+ return GetVisitorIdForSize(kVisitStruct,
+ kVisitStructGeneric,
+ JSProxy::kSize);
+
+ case JS_FUNCTION_PROXY_TYPE:
+ return GetVisitorIdForSize(kVisitStruct,
+ kVisitStructGeneric,
+ JSFunctionProxy::kSize);
+
+ case FOREIGN_TYPE:
+ return GetVisitorIdForSize(kVisitDataObject,
+ kVisitDataObjectGeneric,
+ Foreign::kSize);
+
+ case SYMBOL_TYPE:
+ return kVisitSymbol;
+
+ case FILLER_TYPE:
+ return kVisitDataObjectGeneric;
+
+ case JS_ARRAY_BUFFER_TYPE:
+ return kVisitJSArrayBuffer;
+
+ case JS_TYPED_ARRAY_TYPE:
+ return kVisitJSTypedArray;
+
+ case JS_DATA_VIEW_TYPE:
+ return kVisitJSDataView;
+
+ case JS_OBJECT_TYPE:
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
+ case JS_GENERATOR_OBJECT_TYPE:
+ case JS_MODULE_TYPE:
+ case JS_VALUE_TYPE:
+ case JS_DATE_TYPE:
+ case JS_ARRAY_TYPE:
+ case JS_GLOBAL_PROXY_TYPE:
+ case JS_GLOBAL_OBJECT_TYPE:
+ case JS_BUILTINS_OBJECT_TYPE:
+ case JS_MESSAGE_OBJECT_TYPE:
+ return GetVisitorIdForSize(kVisitJSObject,
+ kVisitJSObjectGeneric,
+ instance_size);
+
+ case JS_FUNCTION_TYPE:
+ return kVisitJSFunction;
+
+ case HEAP_NUMBER_TYPE:
+ case EXTERNAL_PIXEL_ARRAY_TYPE:
+ case EXTERNAL_BYTE_ARRAY_TYPE:
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ case EXTERNAL_INT_ARRAY_TYPE:
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ return GetVisitorIdForSize(kVisitDataObject,
+ kVisitDataObjectGeneric,
+ instance_size);
+
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE:
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ if (instance_type == ALLOCATION_SITE_TYPE) {
+ return kVisitAllocationSite;
+ }
+
+ return GetVisitorIdForSize(kVisitStruct,
+ kVisitStructGeneric,
+ instance_size);
+
+ default:
+ UNREACHABLE();
+ return kVisitorIdCount;
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/objects-visiting.h b/chromium/v8/src/objects-visiting.h
new file mode 100644
index 00000000000..21757377a4f
--- /dev/null
+++ b/chromium/v8/src/objects-visiting.h
@@ -0,0 +1,477 @@
+// Copyright 2012 the V8 project authors. 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_H_
+#define V8_OBJECTS_VISITING_H_
+
+#include "allocation.h"
+
+// 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
+// two switches on object's instance type to determine object size and layout
+// and one or more virtual method calls on visitor itself.
+// Static visitor is different: it provides a dispatch table which contains
+// pointers to specialized visit functions. Each map has the visitor_id
+// field which contains an index of specialized visitor to use.
+
+namespace v8 {
+namespace internal {
+
+
+// Base class for all static visitors.
+class StaticVisitorBase : public AllStatic {
+ public:
+#define VISITOR_ID_LIST(V) \
+ V(SeqOneByteString) \
+ V(SeqTwoByteString) \
+ V(ShortcutCandidate) \
+ V(ByteArray) \
+ V(FreeSpace) \
+ V(FixedArray) \
+ V(FixedDoubleArray) \
+ V(NativeContext) \
+ V(AllocationSite) \
+ V(DataObject2) \
+ V(DataObject3) \
+ V(DataObject4) \
+ V(DataObject5) \
+ V(DataObject6) \
+ V(DataObject7) \
+ V(DataObject8) \
+ V(DataObject9) \
+ V(DataObjectGeneric) \
+ V(JSObject2) \
+ V(JSObject3) \
+ V(JSObject4) \
+ V(JSObject5) \
+ V(JSObject6) \
+ V(JSObject7) \
+ V(JSObject8) \
+ V(JSObject9) \
+ V(JSObjectGeneric) \
+ V(Struct2) \
+ V(Struct3) \
+ V(Struct4) \
+ V(Struct5) \
+ V(Struct6) \
+ V(Struct7) \
+ V(Struct8) \
+ V(Struct9) \
+ V(StructGeneric) \
+ V(ConsString) \
+ V(SlicedString) \
+ V(Symbol) \
+ V(Oddball) \
+ V(Code) \
+ V(Map) \
+ V(Cell) \
+ V(PropertyCell) \
+ V(SharedFunctionInfo) \
+ V(JSFunction) \
+ V(JSWeakMap) \
+ V(JSWeakSet) \
+ V(JSArrayBuffer) \
+ V(JSTypedArray) \
+ V(JSDataView) \
+ V(JSRegExp)
+
+ // For data objects, JS objects and structs along with generic visitor which
+ // can visit object of any size we provide visitors specialized by
+ // object size in words.
+ // Ids of specialized visitors are declared in a linear order (without
+ // holes) starting from the id of visitor specialized for 2 words objects
+ // (base visitor id) and ending with the id of generic visitor.
+ // Method GetVisitorIdForSize depends on this ordering to calculate visitor
+ // id of specialized visitor from given instance size, base visitor id and
+ // generic visitor's id.
+ enum VisitorId {
+#define VISITOR_ID_ENUM_DECL(id) kVisit##id,
+ VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL)
+#undef VISITOR_ID_ENUM_DECL
+ kVisitorIdCount,
+ kVisitDataObject = kVisitDataObject2,
+ kVisitJSObject = kVisitJSObject2,
+ kVisitStruct = kVisitStruct2,
+ kMinObjectSizeInWords = 2
+ };
+
+ // Visitor ID should fit in one byte.
+ STATIC_ASSERT(kVisitorIdCount <= 256);
+
+ // Determine which specialized visitor should be used for given instance type
+ // and instance type.
+ static VisitorId GetVisitorId(int instance_type, int instance_size);
+
+ static VisitorId GetVisitorId(Map* map) {
+ return GetVisitorId(map->instance_type(), map->instance_size());
+ }
+
+ // For visitors that allow specialization by size calculate VisitorId based
+ // on size, base visitor id and generic visitor id.
+ static VisitorId GetVisitorIdForSize(VisitorId base,
+ VisitorId generic,
+ int object_size) {
+ ASSERT((base == kVisitDataObject) ||
+ (base == kVisitStruct) ||
+ (base == kVisitJSObject));
+ ASSERT(IsAligned(object_size, kPointerSize));
+ ASSERT(kMinObjectSizeInWords * kPointerSize <= object_size);
+ ASSERT(object_size <= Page::kMaxNonCodeHeapObjectSize);
+
+ const VisitorId specialization = static_cast<VisitorId>(
+ base + (object_size >> kPointerSizeLog2) - kMinObjectSizeInWords);
+
+ return Min(specialization, generic);
+ }
+};
+
+
+template<typename Callback>
+class VisitorDispatchTable {
+ public:
+ void CopyFrom(VisitorDispatchTable* other) {
+ // We are not using memcpy to guarantee that during update
+ // every element of callbacks_ array will remain correct
+ // pointer (memcpy might be implemented as a byte copying loop).
+ for (int i = 0; i < StaticVisitorBase::kVisitorIdCount; i++) {
+ NoBarrier_Store(&callbacks_[i], other->callbacks_[i]);
+ }
+ }
+
+ 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()]);
+ }
+
+ void Register(StaticVisitorBase::VisitorId id, Callback callback) {
+ ASSERT(id < StaticVisitorBase::kVisitorIdCount); // id is unsigned.
+ callbacks_[id] = reinterpret_cast<AtomicWord>(callback);
+ }
+
+ template<typename Visitor,
+ StaticVisitorBase::VisitorId base,
+ StaticVisitorBase::VisitorId generic,
+ int object_size_in_words>
+ void RegisterSpecialization() {
+ static const int size = object_size_in_words * kPointerSize;
+ Register(StaticVisitorBase::GetVisitorIdForSize(base, generic, size),
+ &Visitor::template VisitSpecialized<size>);
+ }
+
+
+ template<typename Visitor,
+ StaticVisitorBase::VisitorId base,
+ StaticVisitorBase::VisitorId generic>
+ void RegisterSpecializations() {
+ STATIC_ASSERT(
+ (generic - base + StaticVisitorBase::kMinObjectSizeInWords) == 10);
+ RegisterSpecialization<Visitor, base, generic, 2>();
+ RegisterSpecialization<Visitor, base, generic, 3>();
+ RegisterSpecialization<Visitor, base, generic, 4>();
+ RegisterSpecialization<Visitor, base, generic, 5>();
+ RegisterSpecialization<Visitor, base, generic, 6>();
+ RegisterSpecialization<Visitor, base, generic, 7>();
+ RegisterSpecialization<Visitor, base, generic, 8>();
+ RegisterSpecialization<Visitor, base, generic, 9>();
+ Register(generic, &Visitor::Visit);
+ }
+
+ private:
+ AtomicWord callbacks_[StaticVisitorBase::kVisitorIdCount];
+};
+
+
+template<typename StaticVisitor>
+class BodyVisitorBase : public AllStatic {
+ public:
+ INLINE(static void IteratePointers(Heap* heap,
+ HeapObject* object,
+ int start_offset,
+ int end_offset)) {
+ Object** start_slot = reinterpret_cast<Object**>(object->address() +
+ start_offset);
+ Object** end_slot = reinterpret_cast<Object**>(object->address() +
+ end_offset);
+ StaticVisitor::VisitPointers(heap, start_slot, end_slot);
+ }
+};
+
+
+template<typename StaticVisitor, typename BodyDescriptor, typename ReturnType>
+class FlexibleBodyVisitor : public BodyVisitorBase<StaticVisitor> {
+ public:
+ INLINE(static ReturnType Visit(Map* map, HeapObject* object)) {
+ int object_size = BodyDescriptor::SizeOf(map, object);
+ BodyVisitorBase<StaticVisitor>::IteratePointers(
+ map->GetHeap(),
+ object,
+ BodyDescriptor::kStartOffset,
+ object_size);
+ return static_cast<ReturnType>(object_size);
+ }
+
+ template<int object_size>
+ static inline ReturnType VisitSpecialized(Map* map, HeapObject* object) {
+ ASSERT(BodyDescriptor::SizeOf(map, object) == object_size);
+ BodyVisitorBase<StaticVisitor>::IteratePointers(
+ map->GetHeap(),
+ object,
+ BodyDescriptor::kStartOffset,
+ object_size);
+ return static_cast<ReturnType>(object_size);
+ }
+};
+
+
+template<typename StaticVisitor, typename BodyDescriptor, typename ReturnType>
+class FixedBodyVisitor : public BodyVisitorBase<StaticVisitor> {
+ public:
+ INLINE(static ReturnType Visit(Map* map, HeapObject* object)) {
+ BodyVisitorBase<StaticVisitor>::IteratePointers(
+ map->GetHeap(),
+ object,
+ BodyDescriptor::kStartOffset,
+ BodyDescriptor::kEndOffset);
+ return static_cast<ReturnType>(BodyDescriptor::kSize);
+ }
+};
+
+
+// Base class for visitors used for a linear new space iteration.
+// IterateBody returns size of visited object.
+// Certain types of objects (i.e. Code objects) are not handled
+// by dispatch table of this visitor because they cannot appear
+// in the new space.
+//
+// This class is intended to be used in the following way:
+//
+// class SomeVisitor : public StaticNewSpaceVisitor<SomeVisitor> {
+// ...
+// }
+//
+// This is an example of Curiously recurring template pattern
+// (see http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).
+// We use CRTP to guarantee aggressive compile time optimizations (i.e.
+// inlining and specialization of StaticVisitor::VisitPointers methods).
+template<typename StaticVisitor>
+class StaticNewSpaceVisitor : public StaticVisitorBase {
+ public:
+ static void Initialize();
+
+ INLINE(static int IterateBody(Map* map, HeapObject* obj)) {
+ return table_.GetVisitor(map)(map, obj);
+ }
+
+ INLINE(static void VisitPointers(Heap* heap, Object** start, Object** end)) {
+ for (Object** p = start; p < end; p++) StaticVisitor::VisitPointer(heap, p);
+ }
+
+ private:
+ INLINE(static int VisitJSFunction(Map* map, HeapObject* object)) {
+ Heap* heap = map->GetHeap();
+ VisitPointers(heap,
+ HeapObject::RawField(object, JSFunction::kPropertiesOffset),
+ HeapObject::RawField(object, JSFunction::kCodeEntryOffset));
+
+ // Don't visit code entry. We are using this visitor only during scavenges.
+
+ VisitPointers(
+ heap,
+ HeapObject::RawField(object,
+ JSFunction::kCodeEntryOffset + kPointerSize),
+ HeapObject::RawField(object,
+ JSFunction::kNonWeakFieldsEndOffset));
+ return JSFunction::kSize;
+ }
+
+ INLINE(static int VisitByteArray(Map* map, HeapObject* object)) {
+ return reinterpret_cast<ByteArray*>(object)->ByteArraySize();
+ }
+
+ INLINE(static int VisitFixedDoubleArray(Map* map, HeapObject* object)) {
+ int length = reinterpret_cast<FixedDoubleArray*>(object)->length();
+ return FixedDoubleArray::SizeFor(length);
+ }
+
+ INLINE(static int VisitJSObject(Map* map, HeapObject* object)) {
+ return JSObjectVisitor::Visit(map, object);
+ }
+
+ INLINE(static int VisitSeqOneByteString(Map* map, HeapObject* object)) {
+ return SeqOneByteString::cast(object)->
+ SeqOneByteStringSize(map->instance_type());
+ }
+
+ INLINE(static int VisitSeqTwoByteString(Map* map, HeapObject* object)) {
+ return SeqTwoByteString::cast(object)->
+ SeqTwoByteStringSize(map->instance_type());
+ }
+
+ INLINE(static int VisitFreeSpace(Map* map, HeapObject* object)) {
+ return FreeSpace::cast(object)->Size();
+ }
+
+ INLINE(static int VisitJSArrayBuffer(Map* map, HeapObject* object));
+ INLINE(static int VisitJSTypedArray(Map* map, HeapObject* object));
+ INLINE(static int VisitJSDataView(Map* map, HeapObject* object));
+
+ class DataObjectVisitor {
+ public:
+ template<int object_size>
+ static inline int VisitSpecialized(Map* map, HeapObject* object) {
+ return object_size;
+ }
+
+ INLINE(static int Visit(Map* map, HeapObject* object)) {
+ return map->instance_size();
+ }
+ };
+
+ typedef FlexibleBodyVisitor<StaticVisitor,
+ StructBodyDescriptor,
+ int> StructVisitor;
+
+ typedef FlexibleBodyVisitor<StaticVisitor,
+ JSObject::BodyDescriptor,
+ int> JSObjectVisitor;
+
+ typedef int (*Callback)(Map* map, HeapObject* object);
+
+ static VisitorDispatchTable<Callback> table_;
+};
+
+
+template<typename StaticVisitor>
+VisitorDispatchTable<typename StaticNewSpaceVisitor<StaticVisitor>::Callback>
+ StaticNewSpaceVisitor<StaticVisitor>::table_;
+
+
+// Base class for visitors used to transitively mark the entire heap.
+// IterateBody returns nothing.
+// Certain types of objects might not be handled by this base class and
+// no visitor function is registered by the generic initialization. A
+// specialized visitor function needs to be provided by the inheriting
+// class itself for those cases.
+//
+// This class is intended to be used in the following way:
+//
+// class SomeVisitor : public StaticMarkingVisitor<SomeVisitor> {
+// ...
+// }
+//
+// This is an example of Curiously recurring template pattern.
+template<typename StaticVisitor>
+class StaticMarkingVisitor : public StaticVisitorBase {
+ public:
+ static void Initialize();
+
+ INLINE(static void IterateBody(Map* map, HeapObject* obj)) {
+ table_.GetVisitor(map)(map, obj);
+ }
+
+ INLINE(static void VisitPropertyCell(Map* map, HeapObject* object));
+ INLINE(static void VisitCodeEntry(Heap* heap, Address entry_address));
+ INLINE(static void VisitEmbeddedPointer(Heap* heap, RelocInfo* rinfo));
+ INLINE(static void VisitCell(Heap* heap, RelocInfo* rinfo));
+ INLINE(static void VisitDebugTarget(Heap* heap, RelocInfo* rinfo));
+ INLINE(static void VisitCodeTarget(Heap* heap, RelocInfo* rinfo));
+ INLINE(static void VisitCodeAgeSequence(Heap* heap, RelocInfo* rinfo));
+ INLINE(static void VisitExternalReference(RelocInfo* rinfo)) { }
+ INLINE(static void VisitRuntimeEntry(RelocInfo* rinfo)) { }
+
+ // TODO(mstarzinger): This should be made protected once refactoring is done.
+ // Mark non-optimize code for functions inlined into the given optimized
+ // code. This will prevent it from being flushed.
+ static void MarkInlinedFunctionsCode(Heap* heap, Code* code);
+
+ protected:
+ INLINE(static void VisitMap(Map* map, HeapObject* object));
+ INLINE(static void VisitCode(Map* map, HeapObject* object));
+ INLINE(static void VisitSharedFunctionInfo(Map* map, HeapObject* object));
+ INLINE(static void VisitJSFunction(Map* map, HeapObject* object));
+ INLINE(static void VisitJSRegExp(Map* map, HeapObject* object));
+ INLINE(static void VisitJSArrayBuffer(Map* map, HeapObject* object));
+ INLINE(static void VisitJSTypedArray(Map* map, HeapObject* object));
+ INLINE(static void VisitJSDataView(Map* map, HeapObject* object));
+ INLINE(static void VisitNativeContext(Map* map, HeapObject* object));
+
+ // Mark pointers in a Map and its TransitionArray together, possibly
+ // treating transitions or back pointers weak.
+ static void MarkMapContents(Heap* heap, Map* map);
+ static void MarkTransitionArray(Heap* heap, TransitionArray* transitions);
+
+ // Code flushing support.
+ INLINE(static bool IsFlushable(Heap* heap, JSFunction* function));
+ INLINE(static bool IsFlushable(Heap* heap, SharedFunctionInfo* shared_info));
+
+ // Helpers used by code flushing support that visit pointer fields and treat
+ // references to code objects either strongly or weakly.
+ static void VisitSharedFunctionInfoStrongCode(Heap* heap, HeapObject* object);
+ static void VisitSharedFunctionInfoWeakCode(Heap* heap, HeapObject* object);
+ static void VisitJSFunctionStrongCode(Heap* heap, HeapObject* object);
+ static void VisitJSFunctionWeakCode(Heap* heap, HeapObject* object);
+
+ class DataObjectVisitor {
+ public:
+ template<int size>
+ static inline void VisitSpecialized(Map* map, HeapObject* object) {
+ }
+
+ INLINE(static void Visit(Map* map, HeapObject* object)) {
+ }
+ };
+
+ typedef FlexibleBodyVisitor<StaticVisitor,
+ FixedArray::BodyDescriptor,
+ void> FixedArrayVisitor;
+
+ typedef FlexibleBodyVisitor<StaticVisitor,
+ JSObject::BodyDescriptor,
+ void> JSObjectVisitor;
+
+ typedef FlexibleBodyVisitor<StaticVisitor,
+ StructBodyDescriptor,
+ void> StructObjectVisitor;
+
+ typedef void (*Callback)(Map* map, HeapObject* object);
+
+ static VisitorDispatchTable<Callback> table_;
+};
+
+
+template<typename StaticVisitor>
+VisitorDispatchTable<typename StaticMarkingVisitor<StaticVisitor>::Callback>
+ StaticMarkingVisitor<StaticVisitor>::table_;
+
+
+} } // namespace v8::internal
+
+#endif // V8_OBJECTS_VISITING_H_
diff --git a/chromium/v8/src/objects.cc b/chromium/v8/src/objects.cc
new file mode 100644
index 00000000000..995ed36f022
--- /dev/null
+++ b/chromium/v8/src/objects.cc
@@ -0,0 +1,15986 @@
+// Copyright 2013 the V8 project authors. 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 "accessors.h"
+#include "api.h"
+#include "arguments.h"
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "date.h"
+#include "elements.h"
+#include "execution.h"
+#include "full-codegen.h"
+#include "hydrogen.h"
+#include "isolate-inl.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"
+
+#ifdef ENABLE_DISASSEMBLER
+#include "disasm.h"
+#include "disassembler.h"
+#endif
+
+namespace v8 {
+namespace internal {
+
+
+MUST_USE_RESULT static MaybeObject* CreateJSValue(JSFunction* constructor,
+ Object* value) {
+ Object* result;
+ { MaybeObject* maybe_result =
+ constructor->GetHeap()->AllocateJSObject(constructor);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ JSValue::cast(result)->set_value(value);
+ return result;
+}
+
+
+MaybeObject* Object::ToObject(Context* native_context) {
+ if (IsNumber()) {
+ return CreateJSValue(native_context->number_function(), this);
+ } else if (IsBoolean()) {
+ return CreateJSValue(native_context->boolean_function(), this);
+ } else if (IsString()) {
+ return CreateJSValue(native_context->string_function(), this);
+ }
+ ASSERT(IsJSObject());
+ return this;
+}
+
+
+MaybeObject* Object::ToObject() {
+ if (IsJSReceiver()) {
+ return this;
+ } else if (IsNumber()) {
+ Isolate* isolate = Isolate::Current();
+ Context* native_context = isolate->context()->native_context();
+ return CreateJSValue(native_context->number_function(), this);
+ } else if (IsBoolean()) {
+ Isolate* isolate = HeapObject::cast(this)->GetIsolate();
+ Context* native_context = isolate->context()->native_context();
+ return CreateJSValue(native_context->boolean_function(), this);
+ } else if (IsString()) {
+ Isolate* isolate = HeapObject::cast(this)->GetIsolate();
+ Context* native_context = isolate->context()->native_context();
+ return CreateJSValue(native_context->string_function(), this);
+ } else if (IsSymbol()) {
+ Isolate* isolate = HeapObject::cast(this)->GetIsolate();
+ Context* native_context = isolate->context()->native_context();
+ return CreateJSValue(native_context->symbol_function(), this);
+ }
+
+ // Throw a type error.
+ return Failure::InternalError();
+}
+
+
+bool Object::BooleanValue() {
+ if (IsBoolean()) return IsTrue();
+ if (IsSmi()) return Smi::cast(this)->value() != 0;
+ if (IsUndefined() || IsNull()) return false;
+ if (IsUndetectableObject()) return false; // Undetectable object is false.
+ if (IsString()) return String::cast(this)->length() != 0;
+ if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue();
+ return true;
+}
+
+
+void Object::Lookup(Name* name, LookupResult* result) {
+ Object* holder = NULL;
+ if (IsJSReceiver()) {
+ holder = this;
+ } else {
+ Context* native_context = result->isolate()->context()->native_context();
+ if (IsNumber()) {
+ holder = native_context->number_function()->instance_prototype();
+ } else if (IsString()) {
+ holder = native_context->string_function()->instance_prototype();
+ } else if (IsSymbol()) {
+ holder = native_context->symbol_function()->instance_prototype();
+ } else if (IsBoolean()) {
+ holder = native_context->boolean_function()->instance_prototype();
+ } else {
+ Isolate::Current()->PushStackTraceAndDie(
+ 0xDEAD0000, this, JSReceiver::cast(this)->map(), 0xDEAD0001);
+ }
+ }
+ ASSERT(holder != NULL); // Cannot handle null or undefined.
+ JSReceiver::cast(holder)->Lookup(name, result);
+}
+
+
+MaybeObject* Object::GetPropertyWithReceiver(Object* receiver,
+ Name* name,
+ PropertyAttributes* attributes) {
+ LookupResult result(name->GetIsolate());
+ Lookup(name, &result);
+ MaybeObject* value = GetProperty(receiver, &result, name, attributes);
+ ASSERT(*attributes <= ABSENT);
+ return value;
+}
+
+
+bool Object::ToInt32(int32_t* value) {
+ if (IsSmi()) {
+ *value = Smi::cast(this)->value();
+ return true;
+ }
+ if (IsHeapNumber()) {
+ double num = HeapNumber::cast(this)->value();
+ if (FastI2D(FastD2I(num)) == num) {
+ *value = FastD2I(num);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Object::ToUint32(uint32_t* value) {
+ if (IsSmi()) {
+ int num = Smi::cast(this)->value();
+ if (num >= 0) {
+ *value = static_cast<uint32_t>(num);
+ return true;
+ }
+ }
+ if (IsHeapNumber()) {
+ double num = HeapNumber::cast(this)->value();
+ if (num >= 0 && FastUI2D(FastD2UI(num)) == num) {
+ *value = FastD2UI(num);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+template<typename To>
+static inline To* CheckedCast(void *from) {
+ uintptr_t temp = reinterpret_cast<uintptr_t>(from);
+ ASSERT(temp % sizeof(To) == 0);
+ return reinterpret_cast<To*>(temp);
+}
+
+
+static MaybeObject* PerformCompare(const BitmaskCompareDescriptor& descriptor,
+ char* ptr,
+ Heap* heap) {
+ uint32_t bitmask = descriptor.bitmask;
+ uint32_t compare_value = descriptor.compare_value;
+ uint32_t value;
+ switch (descriptor.size) {
+ case 1:
+ value = static_cast<uint32_t>(*CheckedCast<uint8_t>(ptr));
+ compare_value &= 0xff;
+ bitmask &= 0xff;
+ break;
+ case 2:
+ value = static_cast<uint32_t>(*CheckedCast<uint16_t>(ptr));
+ compare_value &= 0xffff;
+ bitmask &= 0xffff;
+ break;
+ case 4:
+ value = *CheckedCast<uint32_t>(ptr);
+ break;
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+ return heap->ToBoolean((bitmask & value) == (bitmask & compare_value));
+}
+
+
+static MaybeObject* PerformCompare(const PointerCompareDescriptor& descriptor,
+ char* ptr,
+ Heap* heap) {
+ uintptr_t compare_value =
+ reinterpret_cast<uintptr_t>(descriptor.compare_value);
+ uintptr_t value = *CheckedCast<uintptr_t>(ptr);
+ return heap->ToBoolean(compare_value == value);
+}
+
+
+static MaybeObject* GetPrimitiveValue(
+ const PrimitiveValueDescriptor& descriptor,
+ char* ptr,
+ Heap* heap) {
+ int32_t int32_value = 0;
+ switch (descriptor.data_type) {
+ case kDescriptorInt8Type:
+ int32_value = *CheckedCast<int8_t>(ptr);
+ break;
+ case kDescriptorUint8Type:
+ int32_value = *CheckedCast<uint8_t>(ptr);
+ break;
+ case kDescriptorInt16Type:
+ int32_value = *CheckedCast<int16_t>(ptr);
+ break;
+ case kDescriptorUint16Type:
+ int32_value = *CheckedCast<uint16_t>(ptr);
+ break;
+ case kDescriptorInt32Type:
+ int32_value = *CheckedCast<int32_t>(ptr);
+ break;
+ case kDescriptorUint32Type: {
+ uint32_t value = *CheckedCast<uint32_t>(ptr);
+ return heap->NumberFromUint32(value);
+ }
+ case kDescriptorBoolType: {
+ uint8_t byte = *CheckedCast<uint8_t>(ptr);
+ return heap->ToBoolean(byte & (0x1 << descriptor.bool_offset));
+ }
+ case kDescriptorFloatType: {
+ float value = *CheckedCast<float>(ptr);
+ return heap->NumberFromDouble(value);
+ }
+ case kDescriptorDoubleType: {
+ double value = *CheckedCast<double>(ptr);
+ return heap->NumberFromDouble(value);
+ }
+ }
+ return heap->NumberFromInt32(int32_value);
+}
+
+
+static MaybeObject* GetDeclaredAccessorProperty(Object* receiver,
+ DeclaredAccessorInfo* info,
+ Isolate* isolate) {
+ char* current = reinterpret_cast<char*>(receiver);
+ DeclaredAccessorDescriptorIterator iterator(info->descriptor());
+ while (true) {
+ const DeclaredAccessorDescriptorData* data = iterator.Next();
+ switch (data->type) {
+ case kDescriptorReturnObject: {
+ ASSERT(iterator.Complete());
+ current = *CheckedCast<char*>(current);
+ return *CheckedCast<Object*>(current);
+ }
+ case kDescriptorPointerDereference:
+ ASSERT(!iterator.Complete());
+ current = *reinterpret_cast<char**>(current);
+ break;
+ case kDescriptorPointerShift:
+ ASSERT(!iterator.Complete());
+ current += data->pointer_shift_descriptor.byte_offset;
+ break;
+ case kDescriptorObjectDereference: {
+ ASSERT(!iterator.Complete());
+ Object* object = CheckedCast<Object>(current);
+ int field = data->object_dereference_descriptor.internal_field;
+ Object* smi = JSObject::cast(object)->GetInternalField(field);
+ ASSERT(smi->IsSmi());
+ current = reinterpret_cast<char*>(smi);
+ break;
+ }
+ case kDescriptorBitmaskCompare:
+ ASSERT(iterator.Complete());
+ return PerformCompare(data->bitmask_compare_descriptor,
+ current,
+ isolate->heap());
+ case kDescriptorPointerCompare:
+ ASSERT(iterator.Complete());
+ return PerformCompare(data->pointer_compare_descriptor,
+ current,
+ isolate->heap());
+ case kDescriptorPrimitiveValue:
+ ASSERT(iterator.Complete());
+ return GetPrimitiveValue(data->primitive_value_descriptor,
+ current,
+ isolate->heap());
+ }
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+MaybeObject* JSObject::GetPropertyWithCallback(Object* receiver,
+ Object* structure,
+ Name* 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
+ // callbacks should be phased out.
+ if (structure->IsForeign()) {
+ AccessorDescriptor* callback =
+ reinterpret_cast<AccessorDescriptor*>(
+ Foreign::cast(structure)->foreign_address());
+ MaybeObject* value = (callback->getter)(receiver, callback->data);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return value;
+ }
+
+ // api style callbacks.
+ if (structure->IsAccessorInfo()) {
+ if (!AccessorInfo::cast(structure)->IsCompatibleReceiver(receiver)) {
+ Handle<Object> name_handle(name, isolate);
+ Handle<Object> receiver_handle(receiver, isolate);
+ Handle<Object> args[2] = { name_handle, receiver_handle };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("incompatible_method_receiver",
+ HandleVector(args,
+ ARRAY_SIZE(args)));
+ return isolate->Throw(*error);
+ }
+ // TODO(rossberg): Handling symbols in the API requires changing the API,
+ // so we do not support it for now.
+ if (name->IsSymbol()) return isolate->heap()->undefined_value();
+ if (structure->IsDeclaredAccessorInfo()) {
+ return GetDeclaredAccessorProperty(receiver,
+ DeclaredAccessorInfo::cast(structure),
+ isolate);
+ }
+ ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(structure);
+ Object* fun_obj = data->getter();
+ v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
+ if (call_fun == NULL) return isolate->heap()->undefined_value();
+ HandleScope scope(isolate);
+ JSObject* self = JSObject::cast(receiver);
+ Handle<String> key(String::cast(name));
+ LOG(isolate, ApiNamedPropertyAccess("load", self, name));
+ PropertyCallbackArguments args(isolate, data->data(), self, this);
+ v8::Handle<v8::Value> result =
+ args.Call(call_fun, v8::Utils::ToLocal(key));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (result.IsEmpty()) {
+ return isolate->heap()->undefined_value();
+ }
+ Object* return_value = *v8::Utils::OpenHandle(*result);
+ return_value->VerifyApiCallResultType();
+ return return_value;
+ }
+
+ // __defineGetter__ callback
+ 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();
+ }
+
+ UNREACHABLE();
+ return NULL;
+}
+
+
+MaybeObject* JSProxy::GetPropertyWithHandler(Object* receiver_raw,
+ Name* name_raw) {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> receiver(receiver_raw, isolate);
+ Handle<Object> name(name_raw, isolate);
+
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (name->IsSymbol()) return isolate->heap()->undefined_value();
+
+ 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();
+
+ return *result;
+}
+
+
+Handle<Object> Object::GetProperty(Handle<Object> object, Handle<Name> name) {
+ // TODO(rossberg): The index test should not be here but in the GetProperty
+ // method (or somewhere else entirely). Needs more global clean-up.
+ uint32_t index;
+ if (name->AsArrayIndex(&index))
+ return GetElement(object, index);
+ Isolate* isolate = object->IsHeapObject()
+ ? Handle<HeapObject>::cast(object)->GetIsolate()
+ : Isolate::Current();
+ CALL_HEAP_FUNCTION(isolate, object->GetProperty(*name), Object);
+}
+
+
+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(JSReceiver* receiver,
+ uint32_t index,
+ Object* value,
+ StrictModeFlag strict_mode) {
+ String* name;
+ MaybeObject* maybe = GetHeap()->Uint32ToString(index);
+ if (!maybe->To<String>(&name)) return maybe;
+ return SetPropertyWithHandler(receiver, 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,
+ JSReceiver* getter) {
+ Isolate* isolate = getter->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSReceiver> fun(getter);
+ Handle<Object> self(receiver, isolate);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Debug* debug = isolate->debug();
+ // Handle stepping into a getter if step into is active.
+ // 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, true);
+ // Check for pending exception and return the result.
+ if (has_pending_exception) return Failure::Exception();
+ return *result;
+}
+
+
+// Only deal with CALLBACKS and INTERCEPTOR
+MaybeObject* JSObject::GetPropertyWithFailedAccessCheck(
+ Object* receiver,
+ LookupResult* result,
+ Name* name,
+ PropertyAttributes* attributes) {
+ if (result->IsProperty()) {
+ switch (result->type()) {
+ case CALLBACKS: {
+ // Only allow API accessors.
+ Object* obj = result->GetCallbackObject();
+ if (obj->IsAccessorInfo()) {
+ AccessorInfo* info = AccessorInfo::cast(obj);
+ if (info->all_can_read()) {
+ *attributes = result->GetAttributes();
+ return result->holder()->GetPropertyWithCallback(
+ receiver, result->GetCallbackObject(), name);
+ }
+ }
+ break;
+ }
+ case NORMAL:
+ case FIELD:
+ case CONSTANT: {
+ // Search ALL_CAN_READ accessors in prototype chain.
+ LookupResult r(GetIsolate());
+ result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
+ if (r.IsProperty()) {
+ return GetPropertyWithFailedAccessCheck(receiver,
+ &r,
+ name,
+ attributes);
+ }
+ break;
+ }
+ case INTERCEPTOR: {
+ // If the object has an interceptor, try real named properties.
+ // No access check in GetPropertyAttributeWithInterceptor.
+ LookupResult r(GetIsolate());
+ result->holder()->LookupRealNamedProperty(name, &r);
+ if (r.IsProperty()) {
+ return GetPropertyWithFailedAccessCheck(receiver,
+ &r,
+ name,
+ attributes);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ // No accessible property found.
+ *attributes = ABSENT;
+ Heap* heap = name->GetHeap();
+ Isolate* isolate = heap->isolate();
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_GET);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return heap->undefined_value();
+}
+
+
+PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
+ Object* receiver,
+ LookupResult* result,
+ Name* name,
+ bool continue_search) {
+ if (result->IsProperty()) {
+ switch (result->type()) {
+ case CALLBACKS: {
+ // Only allow API accessors.
+ Object* obj = result->GetCallbackObject();
+ if (obj->IsAccessorInfo()) {
+ AccessorInfo* info = AccessorInfo::cast(obj);
+ if (info->all_can_read()) {
+ return result->GetAttributes();
+ }
+ }
+ break;
+ }
+
+ case NORMAL:
+ case FIELD:
+ case CONSTANT: {
+ if (!continue_search) break;
+ // Search ALL_CAN_READ accessors in prototype chain.
+ LookupResult r(GetIsolate());
+ result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
+ if (r.IsProperty()) {
+ return GetPropertyAttributeWithFailedAccessCheck(receiver,
+ &r,
+ name,
+ continue_search);
+ }
+ break;
+ }
+
+ case INTERCEPTOR: {
+ // If the object has an interceptor, try real named properties.
+ // No access check in GetPropertyAttributeWithInterceptor.
+ LookupResult r(GetIsolate());
+ if (continue_search) {
+ result->holder()->LookupRealNamedProperty(name, &r);
+ } else {
+ result->holder()->LocalLookupRealNamedProperty(name, &r);
+ }
+ if (!r.IsFound()) break;
+ return GetPropertyAttributeWithFailedAccessCheck(receiver,
+ &r,
+ name,
+ continue_search);
+ }
+
+ case HANDLER:
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ }
+ }
+
+ GetIsolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return ABSENT;
+}
+
+
+Object* JSObject::GetNormalizedProperty(LookupResult* result) {
+ ASSERT(!HasFastProperties());
+ Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
+ if (IsGlobalObject()) {
+ value = PropertyCell::cast(value)->value();
+ }
+ ASSERT(!value->IsPropertyCell() && !value->IsCell());
+ return value;
+}
+
+
+Handle<Object> JSObject::SetNormalizedProperty(Handle<JSObject> object,
+ LookupResult* result,
+ Handle<Object> value) {
+ CALL_HEAP_FUNCTION(object->GetIsolate(),
+ object->SetNormalizedProperty(result, *value),
+ Object);
+}
+
+
+MaybeObject* JSObject::SetNormalizedProperty(LookupResult* result,
+ Object* value) {
+ ASSERT(!HasFastProperties());
+ if (IsGlobalObject()) {
+ PropertyCell* cell = PropertyCell::cast(
+ property_dictionary()->ValueAt(result->GetDictionaryEntry()));
+ MaybeObject* maybe_type = cell->SetValueInferType(value);
+ if (maybe_type->IsFailure()) return maybe_type;
+ } else {
+ property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value);
+ }
+ return value;
+}
+
+
+Handle<Object> JSObject::SetNormalizedProperty(Handle<JSObject> object,
+ Handle<Name> key,
+ Handle<Object> value,
+ PropertyDetails details) {
+ CALL_HEAP_FUNCTION(object->GetIsolate(),
+ object->SetNormalizedProperty(*key, *value, details),
+ Object);
+}
+
+
+MaybeObject* JSObject::SetNormalizedProperty(Name* name,
+ Object* value,
+ PropertyDetails details) {
+ ASSERT(!HasFastProperties());
+ int entry = property_dictionary()->FindEntry(name);
+ if (entry == NameDictionary::kNotFound) {
+ Object* store_value = value;
+ if (IsGlobalObject()) {
+ Heap* heap = name->GetHeap();
+ MaybeObject* maybe_store_value = heap->AllocatePropertyCell(value);
+ if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
+ }
+ Object* dict;
+ { MaybeObject* maybe_dict =
+ property_dictionary()->Add(name, store_value, details);
+ if (!maybe_dict->ToObject(&dict)) return maybe_dict;
+ }
+ set_properties(NameDictionary::cast(dict));
+ return value;
+ }
+
+ PropertyDetails original_details = property_dictionary()->DetailsAt(entry);
+ int enumeration_index;
+ // Preserve the enumeration index unless the property was deleted.
+ if (original_details.IsDeleted()) {
+ enumeration_index = property_dictionary()->NextEnumerationIndex();
+ property_dictionary()->SetNextEnumerationIndex(enumeration_index + 1);
+ } else {
+ enumeration_index = original_details.dictionary_index();
+ ASSERT(enumeration_index > 0);
+ }
+
+ details = PropertyDetails(
+ details.attributes(), details.type(), enumeration_index);
+
+ if (IsGlobalObject()) {
+ PropertyCell* cell =
+ PropertyCell::cast(property_dictionary()->ValueAt(entry));
+ MaybeObject* maybe_type = cell->SetValueInferType(value);
+ if (maybe_type->IsFailure()) return maybe_type;
+ // Please note we have to update the property details.
+ property_dictionary()->DetailsAtPut(entry, details);
+ } else {
+ property_dictionary()->SetEntry(entry, name, value, details);
+ }
+ return value;
+}
+
+
+// TODO(mstarzinger): Temporary wrapper until target is handlified.
+Handle<NameDictionary> NameDictionaryShrink(Handle<NameDictionary> dict,
+ Handle<Name> name) {
+ CALL_HEAP_FUNCTION(dict->GetIsolate(), dict->Shrink(*name), NameDictionary);
+}
+
+
+static void CellSetValueInferType(Handle<PropertyCell> cell,
+ Handle<Object> value) {
+ CALL_HEAP_FUNCTION_VOID(cell->GetIsolate(), cell->SetValueInferType(*value));
+}
+
+
+Handle<Object> JSObject::DeleteNormalizedProperty(Handle<JSObject> object,
+ Handle<Name> name,
+ DeleteMode mode) {
+ ASSERT(!object->HasFastProperties());
+ Isolate* isolate = object->GetIsolate();
+ Handle<NameDictionary> dictionary(object->property_dictionary());
+ int entry = dictionary->FindEntry(*name);
+ if (entry != NameDictionary::kNotFound) {
+ // If we have a global object set the cell to the hole.
+ if (object->IsGlobalObject()) {
+ PropertyDetails details = dictionary->DetailsAt(entry);
+ if (details.IsDontDelete()) {
+ if (mode != FORCE_DELETION) return isolate->factory()->false_value();
+ // When forced to delete global properties, we have to make a
+ // map change to invalidate any ICs that think they can load
+ // from the DontDelete cell without checking if it contains
+ // the hole value.
+ Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
+ ASSERT(new_map->is_dictionary_map());
+ object->set_map(*new_map);
+ }
+ Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry)));
+ CellSetValueInferType(cell, isolate->factory()->the_hole_value());
+ dictionary->DetailsAtPut(entry, details.AsDeleted());
+ } else {
+ Handle<Object> deleted(dictionary->DeleteProperty(entry, mode), isolate);
+ if (*deleted == isolate->heap()->true_value()) {
+ Handle<NameDictionary> new_properties =
+ NameDictionaryShrink(dictionary, name);
+ object->set_properties(*new_properties);
+ }
+ return deleted;
+ }
+ }
+ return isolate->factory()->true_value();
+}
+
+
+bool JSObject::IsDirty() {
+ Object* cons_obj = map()->constructor();
+ if (!cons_obj->IsJSFunction())
+ return true;
+ JSFunction* fun = JSFunction::cast(cons_obj);
+ if (!fun->shared()->IsApiFunction())
+ return true;
+ // If the object is fully fast case and has the same map it was
+ // created with then no changes can have been made to it.
+ return map() != fun->initial_map()
+ || !HasFastObjectElements()
+ || !HasFastProperties();
+}
+
+
+Handle<Object> Object::GetProperty(Handle<Object> object,
+ Handle<Object> receiver,
+ LookupResult* result,
+ Handle<Name> 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::GetPropertyOrFail(Handle<Object> object,
+ Handle<Object> receiver,
+ LookupResult* result,
+ Handle<Name> key,
+ PropertyAttributes* attributes) {
+ Isolate* isolate = object->IsHeapObject()
+ ? Handle<HeapObject>::cast(object)->GetIsolate()
+ : Isolate::Current();
+ CALL_HEAP_FUNCTION_PASS_EXCEPTION(
+ isolate,
+ object->GetProperty(*receiver, result, *key, attributes));
+}
+
+
+MaybeObject* Object::GetProperty(Object* receiver,
+ LookupResult* result,
+ Name* name,
+ PropertyAttributes* attributes) {
+ // Make sure that the top context does not change when doing
+ // callbacks or interceptor calls.
+ AssertNoContextChange ncc;
+ Isolate* isolate = name->GetIsolate();
+ Heap* heap = isolate->heap();
+
+ // Traverse the prototype chain from the current object (this) to
+ // the holder and check for access rights. This avoids traversing the
+ // objects more than once in case of interceptors, because the
+ // holder will always be the interceptor holder and the search may
+ // only continue with a current object just after the interceptor
+ // 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()
+ : Object::cast(heap->null_value());
+ ASSERT(this != this->GetPrototype(isolate));
+ for (Object* current = this;
+ true;
+ current = current->GetPrototype(isolate)) {
+ if (current->IsAccessCheckNeeded()) {
+ // Check if we're allowed to read from the current object. Note
+ // that even though we may not actually end up loading the named
+ // property from the current object, we still check that we have
+ // access to it.
+ JSObject* checked = JSObject::cast(current);
+ if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) {
+ return checked->GetPropertyWithFailedAccessCheck(receiver,
+ result,
+ name,
+ attributes);
+ }
+ }
+ // Stop traversing the chain once we reach the last object in the
+ // chain; either the holder of the result or null in case of an
+ // absent property.
+ if (current == last) break;
+ }
+ }
+
+ if (!result->IsProperty()) {
+ *attributes = ABSENT;
+ return heap->undefined_value();
+ }
+ *attributes = result->GetAttributes();
+ Object* value;
+ switch (result->type()) {
+ case NORMAL:
+ value = result->holder()->GetNormalizedProperty(result);
+ ASSERT(!value->IsTheHole() || result->IsReadOnly());
+ return value->IsTheHole() ? heap->undefined_value() : value;
+ case FIELD: {
+ MaybeObject* maybe_result = result->holder()->FastPropertyAt(
+ result->representation(),
+ result->GetFieldIndex().field_index());
+ if (!maybe_result->To(&value)) return maybe_result;
+ ASSERT(!value->IsTheHole() || result->IsReadOnly());
+ return value->IsTheHole() ? heap->undefined_value() : value;
+ }
+ case CONSTANT:
+ return result->GetConstant();
+ case CALLBACKS:
+ return result->holder()->GetPropertyWithCallback(
+ receiver, result->GetCallbackObject(), name);
+ case HANDLER:
+ return result->proxy()->GetPropertyWithHandler(receiver, name);
+ case INTERCEPTOR:
+ return result->holder()->GetPropertyWithInterceptor(
+ receiver, name, attributes);
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
+ Isolate* isolate = IsSmi()
+ ? Isolate::Current()
+ : HeapObject::cast(this)->GetIsolate();
+ Heap* heap = isolate->heap();
+ Object* holder = this;
+
+ // Iterate up the prototype chain until an element is found or the null
+ // prototype is encountered.
+ for (holder = this;
+ holder != heap->null_value();
+ holder = holder->GetPrototype(isolate)) {
+ if (!holder->IsJSObject()) {
+ Context* native_context = isolate->context()->native_context();
+ if (holder->IsNumber()) {
+ holder = native_context->number_function()->instance_prototype();
+ } else if (holder->IsString()) {
+ holder = native_context->string_function()->instance_prototype();
+ } else if (holder->IsSymbol()) {
+ holder = native_context->symbol_function()->instance_prototype();
+ } else if (holder->IsBoolean()) {
+ holder = native_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();
+ }
+ }
+
+ // Inline the case for JSObjects. Doing so significantly improves the
+ // performance of fetching elements where checking the prototype chain is
+ // necessary.
+ JSObject* js_object = JSObject::cast(holder);
+
+ // Check access rights if needed.
+ if (js_object->IsAccessCheckNeeded()) {
+ Isolate* isolate = heap->isolate();
+ if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) {
+ isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_GET);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return heap->undefined_value();
+ }
+ }
+
+ if (js_object->HasIndexedInterceptor()) {
+ return js_object->GetElementWithInterceptor(receiver, index);
+ }
+
+ if (js_object->elements() != heap->empty_fixed_array()) {
+ MaybeObject* result = js_object->GetElementsAccessor()->Get(
+ receiver, js_object, index);
+ if (result != heap->the_hole_value()) return result;
+ }
+ }
+
+ return heap->undefined_value();
+}
+
+
+Object* Object::GetPrototype(Isolate* isolate) {
+ if (IsSmi()) {
+ Context* context = isolate->context()->native_context();
+ return context->number_function()->instance_prototype();
+ }
+
+ HeapObject* heap_object = HeapObject::cast(this);
+
+ // The object is either a number, a string, a boolean,
+ // a real JS object, or a Harmony proxy.
+ if (heap_object->IsJSReceiver()) {
+ return heap_object->map()->prototype();
+ }
+ Context* context = isolate->context()->native_context();
+
+ if (heap_object->IsHeapNumber()) {
+ return context->number_function()->instance_prototype();
+ }
+ if (heap_object->IsString()) {
+ return context->string_function()->instance_prototype();
+ }
+ if (heap_object->IsSymbol()) {
+ return context->symbol_function()->instance_prototype();
+ }
+ if (heap_object->IsBoolean()) {
+ return context->boolean_function()->instance_prototype();
+ } else {
+ return isolate->heap()->null_value();
+ }
+}
+
+
+MaybeObject* Object::GetHash(CreationFlag flag) {
+ // The object is either a number, a name, 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 (IsName()) {
+ uint32_t hash = Name::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;
+
+ // The object is either a number, a name, 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) ||
+ (std::isnan(this_value) && std::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);
+ ShortPrint(&accumulator);
+ accumulator.OutputToFile(out);
+}
+
+
+void Object::ShortPrint(StringStream* accumulator) {
+ if (IsSmi()) {
+ Smi::cast(this)->SmiPrint(accumulator);
+ } else if (IsFailure()) {
+ Failure::cast(this)->FailurePrint(accumulator);
+ } else {
+ HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
+ }
+}
+
+
+void Smi::SmiPrint(FILE* out) {
+ PrintF(out, "%d", value());
+}
+
+
+void Smi::SmiPrint(StringStream* accumulator) {
+ accumulator->Add("%d", value());
+}
+
+
+void Failure::FailurePrint(StringStream* accumulator) {
+ accumulator->Add("Failure(%p)", reinterpret_cast<void*>(value()));
+}
+
+
+void Failure::FailurePrint(FILE* out) {
+ PrintF(out, "Failure(%p)", reinterpret_cast<void*>(value()));
+}
+
+
+// Should a word be prefixed by 'a' or 'an' in order to read naturally in
+// English? Returns false for non-ASCII or words that don't start with
+// a capital letter. The a/an rule follows pronunciation in English.
+// We don't use the BBC's overcorrect "an historic occasion" though if
+// you speak a dialect you may well say "an 'istoric occasion".
+static bool AnWord(String* str) {
+ if (str->length() == 0) return false; // A nothing.
+ int c0 = str->Get(0);
+ int c1 = str->length() > 1 ? str->Get(1) : 0;
+ if (c0 == 'U') {
+ if (c1 > 'Z') {
+ return true; // An Umpire, but a UTF8String, a U.
+ }
+ } else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
+ return true; // An Ape, an ABCBook.
+ } else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
+ (c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
+ c0 == 'S' || c0 == 'X')) {
+ return true; // An MP3File, an M.
+ }
+ return false;
+}
+
+
+MaybeObject* String::SlowTryFlatten(PretenureFlag pretenure) {
+#ifdef DEBUG
+ // Do not attempt to flatten in debug mode when allocation is not
+ // allowed. This is to avoid an assertion failure when allocating.
+ // Flattening strings is the only case where we always allow
+ // allocation because no GC is performed if the allocation fails.
+ if (!AllowHeapAllocation::IsAllowed()) return this;
+#endif
+
+ Heap* heap = GetHeap();
+ switch (StringShape(this).representation_tag()) {
+ case kConsStringTag: {
+ ConsString* cs = ConsString::cast(this);
+ if (cs->second()->length() == 0) {
+ return cs->first();
+ }
+ // There's little point in putting the flat string in new space if the
+ // cons string is in old space. It can never get GCed until there is
+ // an old space GC.
+ PretenureFlag tenure = heap->InNewSpace(this) ? pretenure : TENURED;
+ int len = length();
+ Object* object;
+ String* result;
+ if (IsOneByteRepresentation()) {
+ { MaybeObject* maybe_object =
+ heap->AllocateRawOneByteString(len, tenure);
+ if (!maybe_object->ToObject(&object)) return maybe_object;
+ }
+ result = String::cast(object);
+ String* first = cs->first();
+ int first_length = first->length();
+ uint8_t* dest = SeqOneByteString::cast(result)->GetChars();
+ WriteToFlat(first, dest, 0, first_length);
+ String* second = cs->second();
+ WriteToFlat(second,
+ dest + first_length,
+ 0,
+ len - first_length);
+ } else {
+ { MaybeObject* maybe_object =
+ heap->AllocateRawTwoByteString(len, tenure);
+ if (!maybe_object->ToObject(&object)) return maybe_object;
+ }
+ result = String::cast(object);
+ uc16* dest = SeqTwoByteString::cast(result)->GetChars();
+ String* first = cs->first();
+ int first_length = first->length();
+ WriteToFlat(first, dest, 0, first_length);
+ String* second = cs->second();
+ WriteToFlat(second,
+ dest + first_length,
+ 0,
+ len - first_length);
+ }
+ cs->set_first(result);
+ cs->set_second(heap->empty_string(), SKIP_WRITE_BARRIER);
+ return result;
+ }
+ default:
+ return this;
+ }
+}
+
+
+bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
+ // Externalizing twice leaks the external resource, so it's
+ // prohibited by the API.
+ ASSERT(!this->IsExternalString());
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ // Assert that the resource and the string are equivalent.
+ ASSERT(static_cast<size_t>(this->length()) == resource->length());
+ ScopedVector<uc16> smart_chars(this->length());
+ String::WriteToFlat(this, smart_chars.start(), 0, this->length());
+ ASSERT(memcmp(smart_chars.start(),
+ resource->data(),
+ resource->length() * sizeof(smart_chars[0])) == 0);
+ }
+#endif // DEBUG
+ Heap* heap = GetHeap();
+ int size = this->Size(); // Byte size of the original string.
+ if (size < ExternalString::kShortSize) {
+ return false;
+ }
+ bool is_ascii = this->IsOneByteRepresentation();
+ bool is_internalized = this->IsInternalizedString();
+
+ // Morph the object to an external string by adjusting the map and
+ // reinitializing the fields.
+ if (size >= ExternalString::kSize) {
+ this->set_map_no_write_barrier(
+ is_internalized
+ ? (is_ascii
+ ? heap->external_internalized_string_with_one_byte_data_map()
+ : heap->external_internalized_string_map())
+ : (is_ascii
+ ? heap->external_string_with_one_byte_data_map()
+ : heap->external_string_map()));
+ } else {
+ this->set_map_no_write_barrier(
+ is_internalized
+ ? (is_ascii
+ ? heap->
+ short_external_internalized_string_with_one_byte_data_map()
+ : heap->short_external_internalized_string_map())
+ : (is_ascii
+ ? heap->short_external_string_with_one_byte_data_map()
+ : heap->short_external_string_map()));
+ }
+ ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
+ self->set_resource(resource);
+ if (is_internalized) 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;
+}
+
+
+bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ // Assert that the resource and the string are equivalent.
+ ASSERT(static_cast<size_t>(this->length()) == resource->length());
+ if (this->IsTwoByteRepresentation()) {
+ ScopedVector<uint16_t> smart_chars(this->length());
+ String::WriteToFlat(this, smart_chars.start(), 0, this->length());
+ ASSERT(String::IsOneByte(smart_chars.start(), this->length()));
+ }
+ ScopedVector<char> smart_chars(this->length());
+ String::WriteToFlat(this, smart_chars.start(), 0, this->length());
+ ASSERT(memcmp(smart_chars.start(),
+ resource->data(),
+ resource->length() * sizeof(smart_chars[0])) == 0);
+ }
+#endif // DEBUG
+ Heap* heap = GetHeap();
+ int size = this->Size(); // Byte size of the original string.
+ if (size < ExternalString::kShortSize) {
+ return false;
+ }
+ bool is_internalized = this->IsInternalizedString();
+
+ // Morph the object to an external string by adjusting the map and
+ // reinitializing the fields. Use short version if space is limited.
+ if (size >= ExternalString::kSize) {
+ this->set_map_no_write_barrier(
+ is_internalized ? heap->external_ascii_internalized_string_map()
+ : heap->external_ascii_string_map());
+ } else {
+ this->set_map_no_write_barrier(
+ is_internalized ? heap->short_external_ascii_internalized_string_map()
+ : heap->short_external_ascii_string_map());
+ }
+ ExternalAsciiString* self = ExternalAsciiString::cast(this);
+ self->set_resource(resource);
+ if (is_internalized) 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;
+}
+
+
+void String::StringShortPrint(StringStream* accumulator) {
+ int len = length();
+ if (len > kMaxShortPrintLength) {
+ accumulator->Add("<Very long string[%u]>", len);
+ return;
+ }
+
+ if (!LooksValid()) {
+ accumulator->Add("<Invalid String>");
+ return;
+ }
+
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(this, &op);
+
+ bool truncated = false;
+ if (len > kMaxShortPrintLength) {
+ len = kMaxShortPrintLength;
+ truncated = true;
+ }
+ bool ascii = true;
+ for (int i = 0; i < len; i++) {
+ uint16_t c = stream.GetNext();
+
+ if (c < 32 || c >= 127) {
+ ascii = false;
+ }
+ }
+ stream.Reset(this);
+ if (ascii) {
+ accumulator->Add("<String[%u]: ", length());
+ for (int i = 0; i < len; i++) {
+ accumulator->Put(static_cast<char>(stream.GetNext()));
+ }
+ accumulator->Put('>');
+ } else {
+ // Backslash indicates that the string contains control
+ // characters and that backslashes are therefore escaped.
+ accumulator->Add("<String[%u]\\: ", length());
+ for (int i = 0; i < len; i++) {
+ uint16_t c = stream.GetNext();
+ if (c == '\n') {
+ accumulator->Add("\\n");
+ } else if (c == '\r') {
+ accumulator->Add("\\r");
+ } else if (c == '\\') {
+ accumulator->Add("\\\\");
+ } else if (c < 32 || c > 126) {
+ accumulator->Add("\\x%02x", c);
+ } else {
+ accumulator->Put(static_cast<char>(c));
+ }
+ }
+ if (truncated) {
+ accumulator->Put('.');
+ accumulator->Put('.');
+ accumulator->Put('.');
+ }
+ accumulator->Put('>');
+ }
+ return;
+}
+
+
+void JSObject::JSObjectShortPrint(StringStream* accumulator) {
+ switch (map()->instance_type()) {
+ case JS_ARRAY_TYPE: {
+ double length = JSArray::cast(this)->length()->IsUndefined()
+ ? 0
+ : JSArray::cast(this)->length()->Number();
+ accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length));
+ break;
+ }
+ case JS_WEAK_MAP_TYPE: {
+ accumulator->Add("<JS WeakMap>");
+ break;
+ }
+ case JS_WEAK_SET_TYPE: {
+ accumulator->Add("<JS WeakSet>");
+ break;
+ }
+ case JS_REGEXP_TYPE: {
+ accumulator->Add("<JS RegExp>");
+ break;
+ }
+ case JS_FUNCTION_TYPE: {
+ JSFunction* function = JSFunction::cast(this);
+ Object* fun_name = function->shared()->DebugName();
+ bool printed = false;
+ if (fun_name->IsString()) {
+ String* str = String::cast(fun_name);
+ if (str->length() > 0) {
+ accumulator->Add("<JS Function ");
+ accumulator->Put(str);
+ printed = true;
+ }
+ }
+ if (!printed) {
+ accumulator->Add("<JS Function");
+ }
+ accumulator->Add(" (SharedFunctionInfo %p)",
+ reinterpret_cast<void*>(function->shared()));
+ accumulator->Put('>');
+ break;
+ }
+ case JS_GENERATOR_OBJECT_TYPE: {
+ accumulator->Add("<JS Generator>");
+ break;
+ }
+ case JS_MODULE_TYPE: {
+ accumulator->Add("<JS Module>");
+ break;
+ }
+ // All other JSObjects are rather similar to each other (JSObject,
+ // JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
+ default: {
+ Map* map_of_this = map();
+ Heap* heap = GetHeap();
+ Object* constructor = map_of_this->constructor();
+ bool printed = false;
+ if (constructor->IsHeapObject() &&
+ !heap->Contains(HeapObject::cast(constructor))) {
+ accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
+ } else {
+ bool global_object = IsJSGlobalProxy();
+ if (constructor->IsJSFunction()) {
+ if (!heap->Contains(JSFunction::cast(constructor)->shared())) {
+ accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
+ } else {
+ Object* constructor_name =
+ JSFunction::cast(constructor)->shared()->name();
+ if (constructor_name->IsString()) {
+ String* str = String::cast(constructor_name);
+ if (str->length() > 0) {
+ bool vowel = AnWord(str);
+ accumulator->Add("<%sa%s ",
+ global_object ? "Global Object: " : "",
+ vowel ? "n" : "");
+ accumulator->Put(str);
+ accumulator->Add(" with %smap %p",
+ map_of_this->is_deprecated() ? "deprecated " : "",
+ map_of_this);
+ printed = true;
+ }
+ }
+ }
+ }
+ if (!printed) {
+ accumulator->Add("<JS %sObject", global_object ? "Global " : "");
+ }
+ }
+ if (IsJSValue()) {
+ accumulator->Add(" value = ");
+ JSValue::cast(this)->value()->ShortPrint(accumulator);
+ }
+ accumulator->Put('>');
+ break;
+ }
+ }
+}
+
+
+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(GetIsolate(), 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) {
+ Heap* heap = GetHeap();
+ if (!heap->Contains(this)) {
+ accumulator->Add("!!!INVALID POINTER!!!");
+ return;
+ }
+ if (!heap->Contains(map())) {
+ accumulator->Add("!!!INVALID MAP!!!");
+ return;
+ }
+
+ accumulator->Add("%p ", this);
+
+ if (IsString()) {
+ String::cast(this)->StringShortPrint(accumulator);
+ return;
+ }
+ if (IsJSObject()) {
+ JSObject::cast(this)->JSObjectShortPrint(accumulator);
+ return;
+ }
+ switch (map()->instance_type()) {
+ case MAP_TYPE:
+ 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());
+ break;
+ case EXTERNAL_BYTE_ARRAY_TYPE:
+ accumulator->Add("<ExternalByteArray[%u]>",
+ ExternalByteArray::cast(this)->length());
+ break;
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ accumulator->Add("<ExternalUnsignedByteArray[%u]>",
+ ExternalUnsignedByteArray::cast(this)->length());
+ break;
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ accumulator->Add("<ExternalShortArray[%u]>",
+ ExternalShortArray::cast(this)->length());
+ break;
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ accumulator->Add("<ExternalUnsignedShortArray[%u]>",
+ ExternalUnsignedShortArray::cast(this)->length());
+ break;
+ case EXTERNAL_INT_ARRAY_TYPE:
+ accumulator->Add("<ExternalIntArray[%u]>",
+ ExternalIntArray::cast(this)->length());
+ break;
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ accumulator->Add("<ExternalUnsignedIntArray[%u]>",
+ ExternalUnsignedIntArray::cast(this)->length());
+ break;
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ accumulator->Add("<ExternalFloatArray[%u]>",
+ ExternalFloatArray::cast(this)->length());
+ break;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ accumulator->Add("<ExternalDoubleArray[%u]>",
+ ExternalDoubleArray::cast(this)->length());
+ break;
+ case SHARED_FUNCTION_INFO_TYPE: {
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(this);
+ SmartArrayPointer<char> debug_name =
+ shared->DebugName()->ToCString();
+ if (debug_name[0] != 0) {
+ accumulator->Add("<SharedFunctionInfo %s>", *debug_name);
+ } else {
+ accumulator->Add("<SharedFunctionInfo>");
+ }
+ break;
+ }
+ case JS_MESSAGE_OBJECT_TYPE:
+ accumulator->Add("<JSMessageObject>");
+ break;
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE: \
+ accumulator->Put('<'); \
+ accumulator->Add(#Name); \
+ accumulator->Put('>'); \
+ break;
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ case CODE_TYPE:
+ accumulator->Add("<Code>");
+ break;
+ case ODDBALL_TYPE: {
+ if (IsUndefined())
+ accumulator->Add("<undefined>");
+ else if (IsTheHole())
+ accumulator->Add("<the hole>");
+ else if (IsNull())
+ accumulator->Add("<null>");
+ else if (IsTrue())
+ accumulator->Add("<true>");
+ else if (IsFalse())
+ accumulator->Add("<false>");
+ else
+ accumulator->Add("<Odd Oddball>");
+ break;
+ }
+ case SYMBOL_TYPE: {
+ Symbol* symbol = Symbol::cast(this);
+ accumulator->Add("<Symbol: %d", symbol->Hash());
+ if (!symbol->name()->IsUndefined()) {
+ accumulator->Add(" ");
+ String::cast(symbol->name())->StringShortPrint(accumulator);
+ }
+ accumulator->Add(">");
+ break;
+ }
+ case HEAP_NUMBER_TYPE:
+ accumulator->Add("<Number: ");
+ HeapNumber::cast(this)->HeapNumberPrint(accumulator);
+ accumulator->Put('>');
+ break;
+ case JS_PROXY_TYPE:
+ accumulator->Add("<JSProxy>");
+ break;
+ case JS_FUNCTION_PROXY_TYPE:
+ accumulator->Add("<JSFunctionProxy>");
+ break;
+ case FOREIGN_TYPE:
+ accumulator->Add("<Foreign>");
+ break;
+ case CELL_TYPE:
+ accumulator->Add("Cell for ");
+ Cell::cast(this)->value()->ShortPrint(accumulator);
+ break;
+ case PROPERTY_CELL_TYPE:
+ accumulator->Add("PropertyCell for ");
+ PropertyCell::cast(this)->value()->ShortPrint(accumulator);
+ break;
+ default:
+ accumulator->Add("<Other heap object (%d)>", map()->instance_type());
+ break;
+ }
+}
+
+
+void HeapObject::Iterate(ObjectVisitor* v) {
+ // Handle header
+ IteratePointer(v, kMapOffset);
+ // Handle object body
+ Map* m = map();
+ IterateBody(m->instance_type(), SizeFromMap(m), v);
+}
+
+
+void HeapObject::IterateBody(InstanceType type, int object_size,
+ ObjectVisitor* v) {
+ // Avoiding <Type>::cast(this) because it accesses the map pointer field.
+ // During GC, the map pointer field is encoded.
+ if (type < FIRST_NONSTRING_TYPE) {
+ switch (type & kStringRepresentationMask) {
+ case kSeqStringTag:
+ break;
+ case kConsStringTag:
+ ConsString::BodyDescriptor::IterateBody(this, v);
+ break;
+ case kSlicedStringTag:
+ SlicedString::BodyDescriptor::IterateBody(this, v);
+ break;
+ case kExternalStringTag:
+ if ((type & kStringEncodingMask) == kOneByteStringTag) {
+ reinterpret_cast<ExternalAsciiString*>(this)->
+ ExternalAsciiStringIterateBody(v);
+ } else {
+ reinterpret_cast<ExternalTwoByteString*>(this)->
+ ExternalTwoByteStringIterateBody(v);
+ }
+ break;
+ }
+ return;
+ }
+
+ switch (type) {
+ case FIXED_ARRAY_TYPE:
+ FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
+ break;
+ case FIXED_DOUBLE_ARRAY_TYPE:
+ break;
+ case JS_OBJECT_TYPE:
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
+ case JS_GENERATOR_OBJECT_TYPE:
+ case JS_MODULE_TYPE:
+ case JS_VALUE_TYPE:
+ case JS_DATE_TYPE:
+ case JS_ARRAY_TYPE:
+ case JS_ARRAY_BUFFER_TYPE:
+ case JS_TYPED_ARRAY_TYPE:
+ case JS_DATA_VIEW_TYPE:
+ case JS_SET_TYPE:
+ case JS_MAP_TYPE:
+ case JS_WEAK_MAP_TYPE:
+ case JS_WEAK_SET_TYPE:
+ case JS_REGEXP_TYPE:
+ case JS_GLOBAL_PROXY_TYPE:
+ case JS_GLOBAL_OBJECT_TYPE:
+ case JS_BUILTINS_OBJECT_TYPE:
+ case JS_MESSAGE_OBJECT_TYPE:
+ JSObject::BodyDescriptor::IterateBody(this, object_size, v);
+ break;
+ case JS_FUNCTION_TYPE:
+ reinterpret_cast<JSFunction*>(this)
+ ->JSFunctionIterateBody(object_size, v);
+ break;
+ case ODDBALL_TYPE:
+ Oddball::BodyDescriptor::IterateBody(this, v);
+ break;
+ case JS_PROXY_TYPE:
+ JSProxy::BodyDescriptor::IterateBody(this, v);
+ break;
+ case JS_FUNCTION_PROXY_TYPE:
+ JSFunctionProxy::BodyDescriptor::IterateBody(this, v);
+ break;
+ case FOREIGN_TYPE:
+ reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
+ break;
+ case MAP_TYPE:
+ Map::BodyDescriptor::IterateBody(this, v);
+ break;
+ case CODE_TYPE:
+ reinterpret_cast<Code*>(this)->CodeIterateBody(v);
+ break;
+ case CELL_TYPE:
+ Cell::BodyDescriptor::IterateBody(this, v);
+ break;
+ case PROPERTY_CELL_TYPE:
+ PropertyCell::BodyDescriptor::IterateBody(this, v);
+ break;
+ case SYMBOL_TYPE:
+ Symbol::BodyDescriptor::IterateBody(this, v);
+ break;
+ 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:
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ case EXTERNAL_INT_ARRAY_TYPE:
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ break;
+ case SHARED_FUNCTION_INFO_TYPE: {
+ SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
+ break;
+ }
+
+#define MAKE_STRUCT_CASE(NAME, Name, name) \
+ case NAME##_TYPE:
+ STRUCT_LIST(MAKE_STRUCT_CASE)
+#undef MAKE_STRUCT_CASE
+ if (type == ALLOCATION_SITE_TYPE) {
+ AllocationSite::BodyDescriptor::IterateBody(this, v);
+ } else {
+ StructBodyDescriptor::IterateBody(this, object_size, v);
+ }
+ break;
+ default:
+ PrintF("Unknown type: %d\n", type);
+ UNREACHABLE();
+ }
+}
+
+
+bool HeapNumber::HeapNumberBooleanValue() {
+ // NaN, +0, and -0 should return the false object
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ union IeeeDoubleLittleEndianArchType u;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ union IeeeDoubleBigEndianArchType u;
+#endif
+ u.d = value();
+ if (u.bits.exp == 2047) {
+ // Detect NaN for IEEE double precision floating point.
+ if ((u.bits.man_low | u.bits.man_high) != 0) return false;
+ }
+ if (u.bits.exp == 0) {
+ // Detect +0, and -0 for IEEE double precision floating point.
+ if ((u.bits.man_low | u.bits.man_high) == 0) return false;
+ }
+ return true;
+}
+
+
+void HeapNumber::HeapNumberPrint(FILE* out) {
+ PrintF(out, "%.16g", Number());
+}
+
+
+void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
+ // The Windows version of vsnprintf can allocate when printing a %g string
+ // into a buffer that may not be big enough. We don't want random memory
+ // allocation when producing post-crash stack traces, so we print into a
+ // buffer that is plenty big enough for any floating point number, then
+ // print that using vsnprintf (which may truncate but never allocate if
+ // there is no more space in the buffer).
+ EmbeddedVector<char, 100> buffer;
+ OS::SNPrintF(buffer, "%.16g", Number());
+ accumulator->Add("%s", buffer.start());
+}
+
+
+String* JSReceiver::class_name() {
+ if (IsJSFunction() && IsJSFunctionProxy()) {
+ return GetHeap()->function_class_string();
+ }
+ if (map()->constructor()->IsJSFunction()) {
+ JSFunction* constructor = JSFunction::cast(map()->constructor());
+ return String::cast(constructor->shared()->instance_class_name());
+ }
+ // If the constructor is not present, return "Object".
+ return GetHeap()->Object_string();
+}
+
+
+String* JSReceiver::constructor_name() {
+ if (map()->constructor()->IsJSFunction()) {
+ JSFunction* constructor = JSFunction::cast(map()->constructor());
+ String* name = String::cast(constructor->shared()->name());
+ if (name->length() > 0) return name;
+ String* inferred_name = constructor->shared()->inferred_name();
+ if (inferred_name->length() > 0) return inferred_name;
+ Object* proto = GetPrototype();
+ if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
+ }
+ // TODO(rossberg): what about proxies?
+ // If the constructor is not present, return "Object".
+ return GetHeap()->Object_string();
+}
+
+
+MaybeObject* JSObject::AddFastPropertyUsingMap(Map* new_map,
+ Name* name,
+ Object* value,
+ int field_index,
+ Representation representation) {
+ // This method is used to transition to a field. If we are transitioning to a
+ // double field, allocate new storage.
+ Object* storage;
+ MaybeObject* maybe_storage =
+ value->AllocateNewStorageFor(GetHeap(), representation);
+ if (!maybe_storage->To(&storage)) return maybe_storage;
+
+ if (map()->unused_property_fields() == 0) {
+ int new_unused = new_map->unused_property_fields();
+ FixedArray* values;
+ MaybeObject* maybe_values =
+ properties()->CopySize(properties()->length() + new_unused + 1);
+ if (!maybe_values->To(&values)) return maybe_values;
+
+ set_properties(values);
+ }
+
+ set_map(new_map);
+
+ FastPropertyAtPut(field_index, storage);
+ return value;
+}
+
+
+static bool IsIdentifier(UnicodeCache* cache, Name* name) {
+ // Checks whether the buffer contains an identifier (no escape).
+ if (!name->IsString()) return false;
+ String* string = String::cast(name);
+ if (string->length() == 0) return false;
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(string, &op);
+ if (!cache->IsIdentifierStart(stream.GetNext())) {
+ return false;
+ }
+ while (stream.HasMore()) {
+ if (!cache->IsIdentifierPart(stream.GetNext())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+MaybeObject* JSObject::AddFastProperty(Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StoreFromKeyed store_mode,
+ ValueType value_type) {
+ ASSERT(!IsJSGlobalProxy());
+ ASSERT(DescriptorArray::kNotFound ==
+ map()->instance_descriptors()->Search(
+ name, map()->NumberOfOwnDescriptors()));
+
+ // Normalize the object if the name is an actual name (not the
+ // hidden strings) and is not a real identifier.
+ // Normalize the object if it will have too many fast properties.
+ Isolate* isolate = GetHeap()->isolate();
+ if ((!name->IsSymbol() && !IsIdentifier(isolate->unicode_cache(), name)
+ && name != isolate->heap()->hidden_string()) ||
+ (map()->unused_property_fields() == 0 &&
+ TooManyFastProperties(properties()->length(), store_mode))) {
+ Object* obj;
+ MaybeObject* maybe_obj =
+ NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+
+ return AddSlowProperty(name, value, attributes);
+ }
+
+ // Compute the new index for new field.
+ int index = map()->NextFreePropertyIndex();
+
+ // Allocate new instance descriptors with (name, index) added
+ if (IsJSContextExtensionObject()) value_type = FORCE_TAGGED;
+ Representation representation = value->OptimalRepresentation(value_type);
+
+ FieldDescriptor new_field(name, index, attributes, representation);
+
+ ASSERT(index < map()->inobject_properties() ||
+ (index - map()->inobject_properties()) < properties()->length() ||
+ map()->unused_property_fields() == 0);
+
+ FixedArray* values = NULL;
+
+ // TODO(verwaest): Merge with AddFastPropertyUsingMap.
+ if (map()->unused_property_fields() == 0) {
+ // Make room for the new value
+ MaybeObject* maybe_values =
+ properties()->CopySize(properties()->length() + kFieldsAdded);
+ if (!maybe_values->To(&values)) return maybe_values;
+ }
+
+ TransitionFlag flag = INSERT_TRANSITION;
+
+ Heap* heap = isolate->heap();
+
+ Object* storage;
+ MaybeObject* maybe_storage =
+ value->AllocateNewStorageFor(heap, representation);
+ if (!maybe_storage->To(&storage)) return maybe_storage;
+
+ // Note that Map::CopyAddDescriptor has side-effects, the new map is already
+ // inserted in the transition tree. No more allocations that might fail are
+ // allowed after this point.
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyAddDescriptor(&new_field, flag);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ if (map()->unused_property_fields() == 0) {
+ ASSERT(values != NULL);
+ set_properties(values);
+ new_map->set_unused_property_fields(kFieldsAdded - 1);
+ } else {
+ new_map->set_unused_property_fields(map()->unused_property_fields() - 1);
+ }
+
+ set_map(new_map);
+
+ FastPropertyAtPut(index, storage);
+ return value;
+}
+
+
+MaybeObject* JSObject::AddConstantProperty(
+ Name* name,
+ Object* constant,
+ PropertyAttributes attributes) {
+ // Allocate new instance descriptors with (name, constant) added
+ ConstantDescriptor d(name, constant, attributes);
+
+ TransitionFlag flag =
+ // Do not add transitions to global objects.
+ (IsGlobalObject() ||
+ // Don't add transitions to special properties with non-trivial
+ // attributes.
+ attributes != NONE)
+ ? OMIT_TRANSITION
+ : INSERT_TRANSITION;
+
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyAddDescriptor(&d, flag);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ set_map(new_map);
+ return constant;
+}
+
+
+// Add property in slow mode
+MaybeObject* JSObject::AddSlowProperty(Name* name,
+ Object* value,
+ PropertyAttributes attributes) {
+ ASSERT(!HasFastProperties());
+ NameDictionary* dict = property_dictionary();
+ Object* store_value = value;
+ if (IsGlobalObject()) {
+ // In case name is an orphaned property reuse the cell.
+ int entry = dict->FindEntry(name);
+ if (entry != NameDictionary::kNotFound) {
+ store_value = dict->ValueAt(entry);
+ MaybeObject* maybe_type =
+ PropertyCell::cast(store_value)->SetValueInferType(value);
+ if (maybe_type->IsFailure()) return maybe_type;
+ // Assign an enumeration index to the property and update
+ // SetNextEnumerationIndex.
+ int index = dict->NextEnumerationIndex();
+ PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
+ dict->SetNextEnumerationIndex(index + 1);
+ dict->SetEntry(entry, name, store_value, details);
+ return value;
+ }
+ Heap* heap = GetHeap();
+ { MaybeObject* maybe_store_value =
+ heap->AllocatePropertyCell(value);
+ if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value;
+ }
+ MaybeObject* maybe_type =
+ PropertyCell::cast(store_value)->SetValueInferType(value);
+ if (maybe_type->IsFailure()) return maybe_type;
+ }
+ PropertyDetails details = PropertyDetails(attributes, NORMAL, 0);
+ Object* result;
+ { MaybeObject* maybe_result = dict->Add(name, store_value, details);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ if (dict != result) set_properties(NameDictionary::cast(result));
+ return value;
+}
+
+
+MaybeObject* JSObject::AddProperty(Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ JSReceiver::StoreFromKeyed store_mode,
+ ExtensibilityCheck extensibility_check,
+ ValueType value_type,
+ StoreMode mode) {
+ ASSERT(!IsJSGlobalProxy());
+ Map* map_of_this = map();
+ Heap* heap = GetHeap();
+ Isolate* isolate = heap->isolate();
+ MaybeObject* result;
+ if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK &&
+ !map_of_this->is_extensible()) {
+ if (strict_mode == kNonStrictMode) {
+ return value;
+ } else {
+ Handle<Object> args[1] = {Handle<Name>(name)};
+ return isolate->Throw(
+ *isolate->factory()->NewTypeError("object_not_extensible",
+ HandleVector(args, 1)));
+ }
+ }
+
+ if (HasFastProperties()) {
+ // Ensure the descriptor array does not get too big.
+ if (map_of_this->NumberOfOwnDescriptors() <
+ DescriptorArray::kMaxNumberOfDescriptors) {
+ // TODO(verwaest): Support other constants.
+ // if (mode == ALLOW_AS_CONSTANT &&
+ // !value->IsTheHole() &&
+ // !value->IsConsString()) {
+ if (value->IsJSFunction()) {
+ result = AddConstantProperty(name, value, attributes);
+ } else {
+ result = AddFastProperty(
+ name, value, attributes, store_mode, value_type);
+ }
+ } else {
+ // Normalize the object to prevent very large instance descriptors.
+ // This eliminates unwanted N^2 allocation and lookup behavior.
+ Object* obj;
+ MaybeObject* maybe = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (!maybe->To(&obj)) return maybe;
+ result = AddSlowProperty(name, value, attributes);
+ }
+ } else {
+ result = AddSlowProperty(name, value, attributes);
+ }
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (FLAG_harmony_observation && map()->is_observed()) {
+ EnqueueChangeRecord(handle(this, isolate),
+ "new",
+ handle(name, isolate),
+ handle(heap->the_hole_value(), isolate));
+ }
+
+ return *hresult;
+}
+
+
+void JSObject::EnqueueChangeRecord(Handle<JSObject> object,
+ const char* type_str,
+ Handle<Name> name,
+ Handle<Object> old_value) {
+ Isolate* isolate = object->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<String> type = isolate->factory()->InternalizeUtf8String(type_str);
+ if (object->IsJSGlobalObject()) {
+ object = handle(JSGlobalObject::cast(*object)->global_receiver(), isolate);
+ }
+ Handle<Object> args[] = { type, object, name, old_value };
+ bool threw;
+ Execution::Call(Handle<JSFunction>(isolate->observers_notify_change()),
+ isolate->factory()->undefined_value(),
+ old_value->IsTheHole() ? 3 : 4, args,
+ &threw);
+ ASSERT(!threw);
+}
+
+
+void JSObject::DeliverChangeRecords(Isolate* isolate) {
+ ASSERT(isolate->observer_delivery_pending());
+ bool threw = false;
+ Execution::Call(
+ isolate->observers_deliver_changes(),
+ isolate->factory()->undefined_value(),
+ 0,
+ NULL,
+ &threw);
+ ASSERT(!threw);
+ isolate->set_observer_delivery_pending(false);
+}
+
+
+MaybeObject* JSObject::SetPropertyPostInterceptor(
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ ExtensibilityCheck extensibility_check,
+ StoreMode mode) {
+ // Check local property, ignore interceptor.
+ LookupResult result(GetIsolate());
+ LocalLookupRealNamedProperty(name, &result);
+ if (!result.IsFound()) map()->LookupTransition(this, name, &result);
+ if (result.IsFound()) {
+ // An existing property or a map transition was found. Use set property to
+ // handle all these cases.
+ return SetProperty(&result, name, value, attributes, strict_mode);
+ }
+ bool done = false;
+ MaybeObject* result_object;
+ result_object =
+ SetPropertyViaPrototypes(name, value, attributes, strict_mode, &done);
+ if (done) return result_object;
+ // Add a new real property.
+ return AddProperty(name, value, attributes, strict_mode,
+ MAY_BE_STORE_FROM_KEYED, extensibility_check,
+ OPTIMAL_REPRESENTATION, mode);
+}
+
+
+MaybeObject* JSObject::ReplaceSlowProperty(Name* name,
+ Object* value,
+ PropertyAttributes attributes) {
+ NameDictionary* dictionary = property_dictionary();
+ int old_index = dictionary->FindEntry(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.
+ new_enumeration_index = dictionary->DetailsAt(old_index).dictionary_index();
+ }
+
+ PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
+ return SetNormalizedProperty(name, value, new_details);
+}
+
+
+MaybeObject* JSObject::ConvertTransitionToMapTransition(
+ int transition_index,
+ Name* name,
+ Object* new_value,
+ PropertyAttributes attributes) {
+ Map* old_map = map();
+ Map* old_target = old_map->GetTransition(transition_index);
+ Object* result;
+
+ MaybeObject* maybe_result = ConvertDescriptorToField(
+ name, new_value, attributes, OMIT_TRANSITION_KEEP_REPRESENTATIONS);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ if (!HasFastProperties()) return result;
+
+ // This method should only be used to convert existing transitions.
+ Map* new_map = map();
+
+ // TODO(verwaest): From here on we lose existing map transitions, causing
+ // invalid back pointers. This will change once we can store multiple
+ // transitions with the same key.
+ bool owned_descriptors = old_map->owns_descriptors();
+ if (owned_descriptors ||
+ old_target->instance_descriptors() == old_map->instance_descriptors()) {
+ // Since the conversion above generated a new fast map with an additional
+ // property which can be shared as well, install this descriptor pointer
+ // along the entire chain of smaller maps.
+ Map* map;
+ DescriptorArray* new_descriptors = new_map->instance_descriptors();
+ DescriptorArray* old_descriptors = old_map->instance_descriptors();
+ for (Object* current = old_map;
+ !current->IsUndefined();
+ current = map->GetBackPointer()) {
+ map = Map::cast(current);
+ if (map->instance_descriptors() != old_descriptors) break;
+ map->SetEnumLength(Map::kInvalidEnumCache);
+ map->set_instance_descriptors(new_descriptors);
+ }
+ old_map->set_owns_descriptors(false);
+ }
+
+ old_target->DeprecateTransitionTree();
+
+ old_map->SetTransition(transition_index, new_map);
+ new_map->SetBackPointer(old_map);
+ return result;
+}
+
+
+MaybeObject* JSObject::ConvertDescriptorToField(Name* name,
+ Object* new_value,
+ PropertyAttributes attributes,
+ TransitionFlag flag) {
+ if (map()->unused_property_fields() == 0 &&
+ TooManyFastProperties(properties()->length(), MAY_BE_STORE_FROM_KEYED)) {
+ Object* obj;
+ MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ return ReplaceSlowProperty(name, new_value, attributes);
+ }
+
+ Representation representation = IsJSContextExtensionObject()
+ ? Representation::Tagged() : new_value->OptimalRepresentation();
+ int index = map()->NextFreePropertyIndex();
+ FieldDescriptor new_field(name, index, attributes, representation);
+
+ // Make a new map for the object.
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyInsertDescriptor(&new_field, flag);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ // Make new properties array if necessary.
+ FixedArray* new_properties = NULL;
+ int new_unused_property_fields = map()->unused_property_fields() - 1;
+ if (map()->unused_property_fields() == 0) {
+ new_unused_property_fields = kFieldsAdded - 1;
+ MaybeObject* maybe_new_properties =
+ properties()->CopySize(properties()->length() + kFieldsAdded);
+ if (!maybe_new_properties->To(&new_properties)) return maybe_new_properties;
+ }
+
+ Heap* heap = GetHeap();
+ Object* storage;
+ MaybeObject* maybe_storage =
+ new_value->AllocateNewStorageFor(heap, representation);
+ if (!maybe_storage->To(&storage)) return maybe_storage;
+
+ // Update pointers to commit changes.
+ // Object points to the new map.
+ new_map->set_unused_property_fields(new_unused_property_fields);
+ set_map(new_map);
+ if (new_properties != NULL) {
+ set_properties(new_properties);
+ }
+ FastPropertyAtPut(index, new_value);
+ return new_value;
+}
+
+
+const char* Representation::Mnemonic() const {
+ switch (kind_) {
+ case kNone: return "v";
+ case kTagged: return "t";
+ case kSmi: return "s";
+ case kDouble: return "d";
+ case kInteger32: return "i";
+ case kHeapObject: return "h";
+ case kExternal: return "x";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+enum RightTrimMode { FROM_GC, FROM_MUTATOR };
+
+
+static void ZapEndOfFixedArray(Address new_end, int to_trim) {
+ // If we are doing a big trim in old space then we zap the space.
+ Object** zap = reinterpret_cast<Object**>(new_end);
+ zap++; // Header of filler must be at least one word so skip that.
+ for (int i = 1; i < to_trim; i++) {
+ *zap++ = Smi::FromInt(0);
+ }
+}
+
+
+template<RightTrimMode trim_mode>
+static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
+ ASSERT(elms->map() != HEAP->fixed_cow_array_map());
+ // For now this trick is only applied to fixed arrays in new and paged space.
+ ASSERT(!HEAP->lo_space()->Contains(elms));
+
+ const int len = elms->length();
+
+ ASSERT(to_trim < len);
+
+ Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim);
+
+ if (trim_mode != FROM_GC || Heap::ShouldZapGarbage()) {
+ ZapEndOfFixedArray(new_end, to_trim);
+ }
+
+ int size_delta = to_trim * kPointerSize;
+
+ // Technically in new space this write might be omitted (except for
+ // debug mode which iterates through the heap), but to play safer
+ // we still do it.
+ heap->CreateFillerObjectAt(new_end, size_delta);
+
+ elms->set_length(len - to_trim);
+
+ // Maintain marking consistency for IncrementalMarking.
+ if (Marking::IsBlack(Marking::MarkBitFrom(elms))) {
+ if (trim_mode == FROM_GC) {
+ MemoryChunk::IncrementLiveBytesFromGC(elms->address(), -size_delta);
+ } else {
+ MemoryChunk::IncrementLiveBytesFromMutator(elms->address(), -size_delta);
+ }
+ }
+}
+
+
+bool Map::InstancesNeedRewriting(Map* target,
+ int target_number_of_fields,
+ int target_inobject,
+ int target_unused) {
+ // If fields were added (or removed), rewrite the instance.
+ int number_of_fields = NumberOfFields();
+ ASSERT(target_number_of_fields >= number_of_fields);
+ if (target_number_of_fields != number_of_fields) return true;
+
+ if (FLAG_track_double_fields) {
+ // If smi descriptors were replaced by double descriptors, rewrite.
+ DescriptorArray* old_desc = instance_descriptors();
+ DescriptorArray* new_desc = target->instance_descriptors();
+ int limit = NumberOfOwnDescriptors();
+ for (int i = 0; i < limit; i++) {
+ if (new_desc->GetDetails(i).representation().IsDouble() &&
+ !old_desc->GetDetails(i).representation().IsDouble()) {
+ return true;
+ }
+ }
+ }
+
+ // If no fields were added, and no inobject properties were removed, setting
+ // the map is sufficient.
+ if (target_inobject == inobject_properties()) return false;
+ // In-object slack tracking may have reduced the object size of the new map.
+ // In that case, succeed if all existing fields were inobject, and they still
+ // fit within the new inobject size.
+ ASSERT(target_inobject < inobject_properties());
+ if (target_number_of_fields <= target_inobject) {
+ ASSERT(target_number_of_fields + target_unused == target_inobject);
+ return false;
+ }
+ // Otherwise, properties will need to be moved to the backing store.
+ return true;
+}
+
+
+// To migrate an instance to a map:
+// - First check whether the instance needs to be rewritten. If not, simply
+// change the map.
+// - Otherwise, allocate a fixed array large enough to hold all fields, in
+// addition to unused space.
+// - Copy all existing properties in, in the following order: backing store
+// properties, unused fields, inobject properties.
+// - If all allocation succeeded, commit the state atomically:
+// * Copy inobject properties from the backing store back into the object.
+// * Trim the difference in instance size of the object. This also cleanly
+// frees inobject properties that moved to the backing store.
+// * If there are properties left in the backing store, trim of the space used
+// to temporarily store the inobject properties.
+// * If there are properties left in the backing store, install the backing
+// store.
+MaybeObject* JSObject::MigrateToMap(Map* new_map) {
+ Heap* heap = GetHeap();
+ Map* old_map = map();
+ int number_of_fields = new_map->NumberOfFields();
+ int inobject = new_map->inobject_properties();
+ int unused = new_map->unused_property_fields();
+
+ // Nothing to do if no functions were converted to fields.
+ if (!old_map->InstancesNeedRewriting(
+ new_map, number_of_fields, inobject, unused)) {
+ set_map(new_map);
+ return this;
+ }
+
+ int total_size = number_of_fields + unused;
+ int external = total_size - inobject;
+ FixedArray* array;
+ MaybeObject* maybe_array = heap->AllocateFixedArray(total_size);
+ if (!maybe_array->To(&array)) return maybe_array;
+
+ DescriptorArray* old_descriptors = old_map->instance_descriptors();
+ DescriptorArray* new_descriptors = new_map->instance_descriptors();
+ int descriptors = new_map->NumberOfOwnDescriptors();
+
+ for (int i = 0; i < descriptors; i++) {
+ PropertyDetails details = new_descriptors->GetDetails(i);
+ if (details.type() != FIELD) continue;
+ PropertyDetails old_details = old_descriptors->GetDetails(i);
+ ASSERT(old_details.type() == CONSTANT ||
+ old_details.type() == FIELD);
+ Object* value = old_details.type() == CONSTANT
+ ? old_descriptors->GetValue(i)
+ : RawFastPropertyAt(old_descriptors->GetFieldIndex(i));
+ if (FLAG_track_double_fields &&
+ !old_details.representation().IsDouble() &&
+ details.representation().IsDouble()) {
+ if (old_details.representation().IsNone()) value = Smi::FromInt(0);
+ // Objects must be allocated in the old object space, since the
+ // overall number of HeapNumbers needed for the conversion might
+ // exceed the capacity of new space, and we would fail repeatedly
+ // trying to migrate the instance.
+ MaybeObject* maybe_storage =
+ value->AllocateNewStorageFor(heap, details.representation(), TENURED);
+ if (!maybe_storage->To(&value)) return maybe_storage;
+ }
+ ASSERT(!(FLAG_track_double_fields &&
+ details.representation().IsDouble() &&
+ value->IsSmi()));
+ int target_index = new_descriptors->GetFieldIndex(i) - inobject;
+ if (target_index < 0) target_index += total_size;
+ array->set(target_index, value);
+ }
+
+ // From here on we cannot fail anymore.
+
+ // Copy (real) inobject properties. If necessary, stop at number_of_fields to
+ // avoid overwriting |one_pointer_filler_map|.
+ int limit = Min(inobject, number_of_fields);
+ for (int i = 0; i < limit; i++) {
+ FastPropertyAtPut(i, array->get(external + i));
+ }
+
+ // Create filler object past the new instance size.
+ int new_instance_size = new_map->instance_size();
+ int instance_size_delta = old_map->instance_size() - new_instance_size;
+ ASSERT(instance_size_delta >= 0);
+ Address address = this->address() + new_instance_size;
+ heap->CreateFillerObjectAt(address, instance_size_delta);
+
+ // If there are properties in the new backing store, trim it to the correct
+ // size and install the backing store into the object.
+ if (external > 0) {
+ RightTrimFixedArray<FROM_MUTATOR>(heap, array, inobject);
+ set_properties(array);
+ }
+
+ set_map(new_map);
+
+ return this;
+}
+
+
+MaybeObject* JSObject::GeneralizeFieldRepresentation(
+ int modify_index,
+ Representation new_representation) {
+ Map* new_map;
+ MaybeObject* maybe_new_map =
+ map()->GeneralizeRepresentation(modify_index, new_representation);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ if (map() == new_map) return this;
+
+ return MigrateToMap(new_map);
+}
+
+
+int Map::NumberOfFields() {
+ DescriptorArray* descriptors = instance_descriptors();
+ int result = 0;
+ for (int i = 0; i < NumberOfOwnDescriptors(); i++) {
+ if (descriptors->GetDetails(i).type() == FIELD) result++;
+ }
+ return result;
+}
+
+
+MaybeObject* Map::CopyGeneralizeAllRepresentations() {
+ Map* new_map;
+ MaybeObject* maybe_map = this->Copy();
+ if (!maybe_map->To(&new_map)) return maybe_map;
+
+ new_map->instance_descriptors()->InitializeRepresentations(
+ Representation::Tagged());
+ if (FLAG_trace_generalization) {
+ PrintF("failed generalization %p -> %p\n",
+ static_cast<void*>(this), static_cast<void*>(new_map));
+ }
+ return new_map;
+}
+
+
+void Map::DeprecateTransitionTree() {
+ if (!FLAG_track_fields) return;
+ if (is_deprecated()) return;
+ if (HasTransitionArray()) {
+ TransitionArray* transitions = this->transitions();
+ for (int i = 0; i < transitions->number_of_transitions(); i++) {
+ transitions->GetTarget(i)->DeprecateTransitionTree();
+ }
+ }
+ deprecate();
+ dependent_code()->DeoptimizeDependentCodeGroup(
+ GetIsolate(), DependentCode::kTransitionGroup);
+ NotifyLeafMapLayoutChange();
+}
+
+
+// Invalidates a transition target at |key|, and installs |new_descriptors| over
+// the current instance_descriptors to ensure proper sharing of descriptor
+// arrays.
+void Map::DeprecateTarget(Name* key, DescriptorArray* new_descriptors) {
+ if (HasTransitionArray()) {
+ TransitionArray* transitions = this->transitions();
+ int transition = transitions->Search(key);
+ if (transition != TransitionArray::kNotFound) {
+ transitions->GetTarget(transition)->DeprecateTransitionTree();
+ }
+ }
+
+ // Don't overwrite the empty descriptor array.
+ if (NumberOfOwnDescriptors() == 0) return;
+
+ DescriptorArray* to_replace = instance_descriptors();
+ Map* current = this;
+ while (current->instance_descriptors() == to_replace) {
+ current->SetEnumLength(Map::kInvalidEnumCache);
+ current->set_instance_descriptors(new_descriptors);
+ Object* next = current->GetBackPointer();
+ if (next->IsUndefined()) break;
+ current = Map::cast(next);
+ }
+
+ set_owns_descriptors(false);
+}
+
+
+Map* Map::FindRootMap() {
+ Map* result = this;
+ while (true) {
+ Object* back = result->GetBackPointer();
+ if (back->IsUndefined()) return result;
+ result = Map::cast(back);
+ }
+}
+
+
+// Returns NULL if the updated map is incompatible.
+Map* Map::FindUpdatedMap(int verbatim,
+ int length,
+ DescriptorArray* descriptors) {
+ // This can only be called on roots of transition trees.
+ ASSERT(GetBackPointer()->IsUndefined());
+
+ Map* current = this;
+
+ for (int i = verbatim; i < length; i++) {
+ if (!current->HasTransitionArray()) break;
+ Name* name = descriptors->GetKey(i);
+ TransitionArray* transitions = current->transitions();
+ int transition = transitions->Search(name);
+ if (transition == TransitionArray::kNotFound) break;
+ current = transitions->GetTarget(transition);
+ PropertyDetails details = descriptors->GetDetails(i);
+ PropertyDetails target_details =
+ current->instance_descriptors()->GetDetails(i);
+ if (details.attributes() != target_details.attributes()) return NULL;
+ if (details.type() == CALLBACKS) {
+ if (target_details.type() != CALLBACKS) return NULL;
+ if (descriptors->GetValue(i) !=
+ current->instance_descriptors()->GetValue(i)) {
+ return NULL;
+ }
+ }
+ }
+
+ return current;
+}
+
+
+Map* Map::FindLastMatchMap(int verbatim,
+ int length,
+ DescriptorArray* descriptors) {
+ // This can only be called on roots of transition trees.
+ ASSERT(GetBackPointer()->IsUndefined());
+
+ Map* current = this;
+
+ for (int i = verbatim; i < length; i++) {
+ if (!current->HasTransitionArray()) break;
+ Name* name = descriptors->GetKey(i);
+ TransitionArray* transitions = current->transitions();
+ int transition = transitions->Search(name);
+ if (transition == TransitionArray::kNotFound) break;
+
+ Map* next = transitions->GetTarget(transition);
+ DescriptorArray* next_descriptors = next->instance_descriptors();
+
+ if (next_descriptors->GetValue(i) != descriptors->GetValue(i)) break;
+
+ PropertyDetails details = descriptors->GetDetails(i);
+ PropertyDetails next_details = next_descriptors->GetDetails(i);
+ if (details.type() != next_details.type()) break;
+ if (details.attributes() != next_details.attributes()) break;
+ if (!details.representation().Equals(next_details.representation())) break;
+
+ current = next;
+ }
+ return current;
+}
+
+
+// Generalize the representation of the descriptor at |modify_index|.
+// This method rewrites the transition tree to reflect the new change. To avoid
+// high degrees over polymorphism, and to stabilize quickly, on every rewrite
+// the new type is deduced by merging the current type with any potential new
+// (partial) version of the type in the transition tree.
+// To do this, on each rewrite:
+// - Search the root of the transition tree using FindRootMap.
+// - Find |updated|, the newest matching version of this map using
+// FindUpdatedMap. This uses the keys in the own map's descriptor array to
+// walk the transition tree.
+// - Merge/generalize the descriptor array of the current map and |updated|.
+// - Generalize the |modify_index| descriptor using |new_representation|.
+// - Walk the tree again starting from the root towards |updated|. Stop at
+// |split_map|, the first map who's descriptor array does not match the merged
+// descriptor array.
+// - If |updated| == |split_map|, |updated| is in the expected state. Return it.
+// - Otherwise, invalidate the outdated transition target from |updated|, and
+// replace its transition tree with a new branch for the updated descriptors.
+MaybeObject* Map::GeneralizeRepresentation(int modify_index,
+ Representation new_representation) {
+ Map* old_map = this;
+ DescriptorArray* old_descriptors = old_map->instance_descriptors();
+ Representation old_representation =
+ old_descriptors->GetDetails(modify_index).representation();
+
+ // It's fine to transition from None to anything but double without any
+ // modification to the object, because the default uninitialized value for
+ // representation None can be overwritten by both smi and tagged values.
+ // Doubles, however, would require a box allocation.
+ if (old_representation.IsNone() &&
+ !new_representation.IsNone() &&
+ !new_representation.IsDouble()) {
+ if (FLAG_trace_generalization) {
+ PrintF("initializing representation %i: %p -> %s\n",
+ modify_index,
+ static_cast<void*>(this),
+ new_representation.Mnemonic());
+ }
+ old_descriptors->SetRepresentation(modify_index, new_representation);
+ return old_map;
+ }
+
+ int descriptors = old_map->NumberOfOwnDescriptors();
+ Map* root_map = old_map->FindRootMap();
+
+ // Check the state of the root map.
+ if (!old_map->EquivalentToForTransition(root_map)) {
+ return CopyGeneralizeAllRepresentations();
+ }
+
+ int verbatim = root_map->NumberOfOwnDescriptors();
+
+ Map* updated = root_map->FindUpdatedMap(
+ verbatim, descriptors, old_descriptors);
+ if (updated == NULL) return CopyGeneralizeAllRepresentations();
+
+ DescriptorArray* updated_descriptors = updated->instance_descriptors();
+
+ int valid = updated->NumberOfOwnDescriptors();
+ if (updated_descriptors->IsMoreGeneralThan(
+ verbatim, valid, descriptors, old_descriptors)) {
+ Representation updated_representation =
+ updated_descriptors->GetDetails(modify_index).representation();
+ if (new_representation.fits_into(updated_representation)) {
+ if (FLAG_trace_generalization &&
+ !(modify_index == 0 && new_representation.IsNone())) {
+ PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
+ PrintF("migrating to existing map %p(%s) -> %p(%s)\n",
+ static_cast<void*>(this),
+ old_details.representation().Mnemonic(),
+ static_cast<void*>(updated),
+ updated_representation.Mnemonic());
+ }
+ return updated;
+ }
+ }
+
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors = updated_descriptors->Merge(
+ verbatim, valid, descriptors, old_descriptors);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ old_representation =
+ new_descriptors->GetDetails(modify_index).representation();
+ Representation updated_representation =
+ new_representation.generalize(old_representation);
+ if (!updated_representation.Equals(old_representation)) {
+ new_descriptors->SetRepresentation(modify_index, updated_representation);
+ }
+
+ Map* split_map = root_map->FindLastMatchMap(
+ verbatim, descriptors, new_descriptors);
+
+ int split_descriptors = split_map->NumberOfOwnDescriptors();
+ // This is shadowed by |updated_descriptors| being more general than
+ // |old_descriptors|.
+ ASSERT(descriptors != split_descriptors);
+
+ int descriptor = split_descriptors;
+ split_map->DeprecateTarget(
+ old_descriptors->GetKey(descriptor), new_descriptors);
+
+ if (FLAG_trace_generalization &&
+ !(modify_index == 0 && new_representation.IsNone())) {
+ PrintF("migrating to new map %i: %p(%s) -> %p(%s) (%i steps)\n",
+ modify_index,
+ static_cast<void*>(this),
+ old_representation.Mnemonic(),
+ static_cast<void*>(new_descriptors),
+ updated_representation.Mnemonic(),
+ descriptors - descriptor);
+ }
+
+ Map* new_map = split_map;
+ // Add missing transitions.
+ for (; descriptor < descriptors; descriptor++) {
+ MaybeObject* maybe_map = new_map->CopyInstallDescriptors(
+ descriptor, new_descriptors);
+ if (!maybe_map->To(&new_map)) {
+ // Create a handle for the last created map to ensure it stays alive
+ // during GC. Its descriptor array is too large, but it will be
+ // overwritten during retry anyway.
+ Handle<Map>(new_map);
+ return maybe_map;
+ }
+ new_map->set_migration_target(true);
+ }
+
+ new_map->set_owns_descriptors(true);
+ return new_map;
+}
+
+
+Map* Map::CurrentMapForDeprecated() {
+ DisallowHeapAllocation no_allocation;
+ if (!is_deprecated()) return this;
+
+ DescriptorArray* old_descriptors = instance_descriptors();
+
+ int descriptors = NumberOfOwnDescriptors();
+ Map* root_map = FindRootMap();
+
+ // Check the state of the root map.
+ if (!EquivalentToForTransition(root_map)) return NULL;
+ int verbatim = root_map->NumberOfOwnDescriptors();
+
+ Map* updated = root_map->FindUpdatedMap(
+ verbatim, descriptors, old_descriptors);
+ if (updated == NULL) return NULL;
+
+ DescriptorArray* updated_descriptors = updated->instance_descriptors();
+ int valid = updated->NumberOfOwnDescriptors();
+ if (!updated_descriptors->IsMoreGeneralThan(
+ verbatim, valid, descriptors, old_descriptors)) {
+ return NULL;
+ }
+
+ return updated;
+}
+
+
+MaybeObject* JSObject::SetPropertyWithInterceptor(
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode) {
+ // TODO(rossberg): Support symbols in the API.
+ if (name->IsSymbol()) return value;
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSObject> this_handle(this);
+ Handle<String> name_handle(String::cast(name));
+ Handle<Object> value_handle(value, isolate);
+ Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
+ if (!interceptor->setter()->IsUndefined()) {
+ LOG(isolate, ApiNamedPropertyAccess("interceptor-named-set", this, name));
+ PropertyCallbackArguments args(isolate, interceptor->data(), this, this);
+ v8::NamedPropertySetter setter =
+ v8::ToCData<v8::NamedPropertySetter>(interceptor->setter());
+ Handle<Object> value_unhole(value->IsTheHole() ?
+ isolate->heap()->undefined_value() :
+ value,
+ isolate);
+ v8::Handle<v8::Value> result = args.Call(setter,
+ v8::Utils::ToLocal(name_handle),
+ v8::Utils::ToLocal(value_unhole));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!result.IsEmpty()) return *value_handle;
+ }
+ MaybeObject* raw_result =
+ this_handle->SetPropertyPostInterceptor(*name_handle,
+ *value_handle,
+ attributes,
+ strict_mode,
+ PERFORM_EXTENSIBILITY_CHECK);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return raw_result;
+}
+
+
+Handle<Object> JSReceiver::SetProperty(Handle<JSReceiver> object,
+ Handle<Name> key,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode) {
+ CALL_HEAP_FUNCTION(object->GetIsolate(),
+ object->SetProperty(*key, *value, attributes, strict_mode),
+ Object);
+}
+
+
+MaybeObject* JSReceiver::SetPropertyOrFail(
+ Handle<JSReceiver> object,
+ Handle<Name> key,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ JSReceiver::StoreFromKeyed store_mode) {
+ CALL_HEAP_FUNCTION_PASS_EXCEPTION(
+ object->GetIsolate(),
+ object->SetProperty(*key, *value, attributes, strict_mode, store_mode));
+}
+
+
+MaybeObject* JSReceiver::SetProperty(Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ JSReceiver::StoreFromKeyed store_mode) {
+ LookupResult result(GetIsolate());
+ LocalLookup(name, &result, true);
+ if (!result.IsFound()) {
+ map()->LookupTransition(JSObject::cast(this), name, &result);
+ }
+ return SetProperty(&result, name, value, attributes, strict_mode, store_mode);
+}
+
+
+MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
+ Name* name,
+ Object* value,
+ JSObject* holder,
+ StrictModeFlag strict_mode) {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+
+ // We should never get here to initialize a const with the hole
+ // value since a const declaration would conflict with the setter.
+ ASSERT(!value->IsTheHole());
+ Handle<Object> value_handle(value, isolate);
+
+ // To accommodate both the old and the new api we switch on the
+ // data structure used to store the callbacks. Eventually foreign
+ // callbacks should be phased out.
+ if (structure->IsForeign()) {
+ AccessorDescriptor* callback =
+ reinterpret_cast<AccessorDescriptor*>(
+ Foreign::cast(structure)->foreign_address());
+ MaybeObject* obj = (callback->setter)(this, value, callback->data);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (obj->IsFailure()) return obj;
+ return *value_handle;
+ }
+
+ if (structure->IsExecutableAccessorInfo()) {
+ // api style callbacks
+ ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(structure);
+ if (!data->IsCompatibleReceiver(this)) {
+ Handle<Object> name_handle(name, isolate);
+ Handle<Object> receiver_handle(this, isolate);
+ Handle<Object> args[2] = { name_handle, receiver_handle };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("incompatible_method_receiver",
+ HandleVector(args,
+ ARRAY_SIZE(args)));
+ return isolate->Throw(*error);
+ }
+ // TODO(rossberg): Support symbols in the API.
+ if (name->IsSymbol()) return value;
+ Object* call_obj = data->setter();
+ v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
+ if (call_fun == NULL) return value;
+ Handle<String> key(String::cast(name));
+ LOG(isolate, ApiNamedPropertyAccess("store", this, name));
+ PropertyCallbackArguments args(
+ isolate, data->data(), this, JSObject::cast(holder));
+ args.Call(call_fun,
+ v8::Utils::ToLocal(key),
+ v8::Utils::ToLocal(value_handle));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return *value_handle;
+ }
+
+ 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;
+ }
+ Handle<Name> key(name);
+ Handle<Object> holder_handle(holder, isolate);
+ Handle<Object> args[2] = { key, holder_handle };
+ return isolate->Throw(
+ *isolate->factory()->NewTypeError("no_setter_in_callback",
+ HandleVector(args, 2)));
+ }
+ }
+
+ // TODO(dcarney): Handle correctly.
+ if (structure->IsDeclaredAccessorInfo()) {
+ return value;
+ }
+
+ UNREACHABLE();
+ return NULL;
+}
+
+
+MaybeObject* JSReceiver::SetPropertyWithDefinedSetter(JSReceiver* setter,
+ Object* value) {
+ Isolate* isolate = GetIsolate();
+ Handle<Object> value_handle(value, 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.
+ // 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> 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;
+}
+
+
+MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes(
+ uint32_t index,
+ Object* value,
+ bool* found,
+ StrictModeFlag strict_mode) {
+ Heap* heap = GetHeap();
+ for (Object* pt = GetPrototype();
+ pt != heap->null_value();
+ pt = pt->GetPrototype(GetIsolate())) {
+ if (pt->IsJSProxy()) {
+ String* name;
+ MaybeObject* maybe = heap->Uint32ToString(index);
+ if (!maybe->To<String>(&name)) {
+ *found = true; // Force abort
+ return maybe;
+ }
+ return JSProxy::cast(pt)->SetPropertyViaPrototypesWithHandler(
+ this, name, value, NONE, strict_mode, found);
+ }
+ if (!JSObject::cast(pt)->HasDictionaryElements()) {
+ continue;
+ }
+ SeededNumberDictionary* dictionary =
+ JSObject::cast(pt)->element_dictionary();
+ int entry = dictionary->FindEntry(index);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ PropertyDetails details = dictionary->DetailsAt(entry);
+ if (details.type() == CALLBACKS) {
+ *found = true;
+ return SetElementWithCallback(dictionary->ValueAt(entry),
+ index,
+ value,
+ JSObject::cast(pt),
+ strict_mode);
+ }
+ }
+ }
+ *found = false;
+ return heap->the_hole_value();
+}
+
+MaybeObject* JSObject::SetPropertyViaPrototypes(
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool* done) {
+ Heap* heap = GetHeap();
+ Isolate* isolate = heap->isolate();
+
+ *done = false;
+ // We could not find a local property so let's check whether there is an
+ // accessor that wants to handle the property, or whether the property is
+ // read-only on the prototype chain.
+ LookupResult result(isolate);
+ LookupRealNamedPropertyInPrototypes(name, &result);
+ if (result.IsFound()) {
+ switch (result.type()) {
+ case NORMAL:
+ case FIELD:
+ case CONSTANT:
+ *done = result.IsReadOnly();
+ break;
+ case INTERCEPTOR: {
+ PropertyAttributes attr =
+ result.holder()->GetPropertyAttributeWithInterceptor(
+ this, name, true);
+ *done = !!(attr & READ_ONLY);
+ break;
+ }
+ case CALLBACKS: {
+ if (!FLAG_es5_readonly && result.IsReadOnly()) break;
+ *done = true;
+ return SetPropertyWithCallback(result.GetCallbackObject(),
+ name, value, result.holder(), strict_mode);
+ }
+ case HANDLER: {
+ return result.proxy()->SetPropertyViaPrototypesWithHandler(
+ this, name, value, attributes, strict_mode, done);
+ }
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ // If we get here with *done true, we have encountered a read-only property.
+ if (!FLAG_es5_readonly) *done = false;
+ if (*done) {
+ if (strict_mode == kNonStrictMode) return value;
+ Handle<Object> args[] = { Handle<Object>(name, isolate),
+ Handle<Object>(this, isolate)};
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args))));
+ }
+ return heap->the_hole_value();
+}
+
+
+void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) {
+ Handle<DescriptorArray> descriptors(map->instance_descriptors());
+ if (slack <= descriptors->NumberOfSlackDescriptors()) return;
+ int number_of_descriptors = descriptors->number_of_descriptors();
+ Isolate* isolate = map->GetIsolate();
+ Handle<DescriptorArray> new_descriptors =
+ isolate->factory()->NewDescriptorArray(number_of_descriptors, slack);
+ DescriptorArray::WhitenessWitness witness(*new_descriptors);
+
+ for (int i = 0; i < number_of_descriptors; ++i) {
+ new_descriptors->CopyFrom(i, *descriptors, i, witness);
+ }
+
+ map->set_instance_descriptors(*new_descriptors);
+}
+
+
+void Map::AppendCallbackDescriptors(Handle<Map> map,
+ Handle<Object> descriptors) {
+ Isolate* isolate = map->GetIsolate();
+ Handle<DescriptorArray> array(map->instance_descriptors());
+ NeanderArray callbacks(descriptors);
+ int nof_callbacks = callbacks.length();
+
+ ASSERT(array->NumberOfSlackDescriptors() >= nof_callbacks);
+
+ // Ensure the keys are unique names before writing them into the
+ // instance descriptor. Since it may cause a GC, it has to be done before we
+ // temporarily put the heap in an invalid state while appending descriptors.
+ for (int i = 0; i < nof_callbacks; ++i) {
+ Handle<AccessorInfo> entry(AccessorInfo::cast(callbacks.get(i)));
+ if (!entry->name()->IsUniqueName()) {
+ Handle<String> key =
+ isolate->factory()->InternalizedStringFromString(
+ Handle<String>(String::cast(entry->name())));
+ entry->set_name(*key);
+ }
+ }
+
+ int nof = map->NumberOfOwnDescriptors();
+
+ // Fill in new callback descriptors. Process the callbacks from
+ // back to front so that the last callback with a given name takes
+ // precedence over previously added callbacks with that name.
+ for (int i = nof_callbacks - 1; i >= 0; i--) {
+ AccessorInfo* entry = AccessorInfo::cast(callbacks.get(i));
+ Name* key = Name::cast(entry->name());
+ // Check if a descriptor with this name already exists before writing.
+ if (array->Search(key, nof) == DescriptorArray::kNotFound) {
+ CallbacksDescriptor desc(key, entry, entry->property_attributes());
+ array->Append(&desc);
+ nof += 1;
+ }
+ }
+
+ map->SetNumberOfOwnDescriptors(nof);
+}
+
+
+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 kind = elements_kind();
+ Handle<Map> transitioned_map = Handle<Map>::null();
+ Handle<Map> current_map(this);
+ bool packed = IsFastPackedElementsKind(kind);
+ if (IsTransitionableFastElementsKind(kind)) {
+ while (CanTransitionToMoreGeneralFastElementsKind(kind, false)) {
+ kind = GetNextMoreGeneralFastElementsKind(kind, false);
+ Handle<Map> maybe_transitioned_map =
+ MaybeNull(current_map->LookupElementsTransitionMap(kind));
+ if (maybe_transitioned_map.is_null()) break;
+ if (ContainsMap(candidates, maybe_transitioned_map) &&
+ (packed || !IsFastPackedElementsKind(kind))) {
+ transitioned_map = maybe_transitioned_map;
+ if (!IsFastPackedElementsKind(kind)) packed = false;
+ }
+ current_map = maybe_transitioned_map;
+ }
+ }
+ return transitioned_map;
+}
+
+
+static Map* FindClosestElementsTransition(Map* map, ElementsKind to_kind) {
+ Map* current_map = map;
+ int index = GetSequenceIndexFromFastElementsKind(map->elements_kind());
+ int to_index = IsFastElementsKind(to_kind)
+ ? GetSequenceIndexFromFastElementsKind(to_kind)
+ : GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND);
+
+ ASSERT(index <= to_index);
+
+ for (; index < to_index; ++index) {
+ if (!current_map->HasElementsTransition()) return current_map;
+ current_map = current_map->elements_transition_map();
+ }
+ if (!IsFastElementsKind(to_kind) && current_map->HasElementsTransition()) {
+ Map* next_map = current_map->elements_transition_map();
+ if (next_map->elements_kind() == to_kind) return next_map;
+ }
+ ASSERT(IsFastElementsKind(to_kind)
+ ? current_map->elements_kind() == to_kind
+ : current_map->elements_kind() == TERMINAL_FAST_ELEMENTS_KIND);
+ return current_map;
+}
+
+
+Map* Map::LookupElementsTransitionMap(ElementsKind to_kind) {
+ Map* to_map = FindClosestElementsTransition(this, to_kind);
+ if (to_map->elements_kind() == to_kind) return to_map;
+ return NULL;
+}
+
+
+bool Map::IsMapInArrayPrototypeChain() {
+ Isolate* isolate = GetIsolate();
+ if (isolate->initial_array_prototype()->map() == this) {
+ return true;
+ }
+
+ if (isolate->initial_object_prototype()->map() == this) {
+ return true;
+ }
+
+ return false;
+}
+
+
+static MaybeObject* AddMissingElementsTransitions(Map* map,
+ ElementsKind to_kind) {
+ ASSERT(IsFastElementsKind(map->elements_kind()));
+ int index = GetSequenceIndexFromFastElementsKind(map->elements_kind());
+ int to_index = IsFastElementsKind(to_kind)
+ ? GetSequenceIndexFromFastElementsKind(to_kind)
+ : GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND);
+
+ ASSERT(index <= to_index);
+
+ Map* current_map = map;
+
+ for (; index < to_index; ++index) {
+ ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(index + 1);
+ MaybeObject* maybe_next_map =
+ current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION);
+ if (!maybe_next_map->To(&current_map)) return maybe_next_map;
+ }
+
+ // In case we are exiting the fast elements kind system, just add the map in
+ // the end.
+ if (!IsFastElementsKind(to_kind)) {
+ MaybeObject* maybe_next_map =
+ current_map->CopyAsElementsKind(to_kind, INSERT_TRANSITION);
+ if (!maybe_next_map->To(&current_map)) return maybe_next_map;
+ }
+
+ ASSERT(current_map->elements_kind() == to_kind);
+ return current_map;
+}
+
+
+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* start_map = map();
+ ElementsKind from_kind = start_map->elements_kind();
+
+ if (from_kind == to_kind) {
+ return start_map;
+ }
+
+ bool allow_store_transition =
+ // Only remember the map transition if there is not an already existing
+ // non-matching element transition.
+ !start_map->IsUndefined() && !start_map->is_shared() &&
+ IsFastElementsKind(from_kind);
+
+ // Only store fast element maps in ascending generality.
+ if (IsFastElementsKind(to_kind)) {
+ allow_store_transition &=
+ IsTransitionableFastElementsKind(from_kind) &&
+ IsMoreGeneralElementsKindTransition(from_kind, to_kind);
+ }
+
+ if (!allow_store_transition) {
+ return start_map->CopyAsElementsKind(to_kind, OMIT_TRANSITION);
+ }
+
+ return start_map->AsElementsKind(to_kind);
+}
+
+
+MaybeObject* Map::AsElementsKind(ElementsKind kind) {
+ Map* closest_map = FindClosestElementsTransition(this, kind);
+
+ if (closest_map->elements_kind() == kind) {
+ return closest_map;
+ }
+
+ return AddMissingElementsTransitions(closest_map, kind);
+}
+
+
+void JSObject::LocalLookupRealNamedProperty(Name* name, LookupResult* result) {
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return result->NotFound();
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->LocalLookupRealNamedProperty(name, result);
+ }
+
+ if (HasFastProperties()) {
+ map()->LookupDescriptor(this, name, result);
+ // A property or a map transition was found. We return all of these result
+ // types because LocalLookupRealNamedProperty is used when setting
+ // properties where map transitions are handled.
+ ASSERT(!result->IsFound() ||
+ (result->holder() == this && result->IsFastPropertyType()));
+ // Disallow caching for uninitialized constants. These can only
+ // occur as fields.
+ if (result->IsField() &&
+ result->IsReadOnly() &&
+ RawFastPropertyAt(result->GetFieldIndex().field_index())->IsTheHole()) {
+ result->DisallowCaching();
+ }
+ return;
+ }
+
+ int entry = property_dictionary()->FindEntry(name);
+ if (entry != NameDictionary::kNotFound) {
+ Object* value = property_dictionary()->ValueAt(entry);
+ if (IsGlobalObject()) {
+ PropertyDetails d = property_dictionary()->DetailsAt(entry);
+ if (d.IsDeleted()) {
+ result->NotFound();
+ return;
+ }
+ value = PropertyCell::cast(value)->value();
+ }
+ // Make sure to disallow caching for uninitialized constants
+ // found in the dictionary-mode objects.
+ if (value->IsTheHole()) result->DisallowCaching();
+ result->DictionaryResult(this, entry);
+ return;
+ }
+
+ result->NotFound();
+}
+
+
+void JSObject::LookupRealNamedProperty(Name* name, LookupResult* result) {
+ LocalLookupRealNamedProperty(name, result);
+ if (result->IsFound()) return;
+
+ LookupRealNamedPropertyInPrototypes(name, result);
+}
+
+
+void JSObject::LookupRealNamedPropertyInPrototypes(Name* name,
+ LookupResult* result) {
+ Isolate* isolate = GetIsolate();
+ Heap* heap = isolate->heap();
+ for (Object* pt = GetPrototype();
+ pt != heap->null_value();
+ pt = pt->GetPrototype(isolate)) {
+ if (pt->IsJSProxy()) {
+ return result->HandlerResult(JSProxy::cast(pt));
+ }
+ JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
+ ASSERT(!(result->IsFound() && result->type() == INTERCEPTOR));
+ if (result->IsFound()) return;
+ }
+ result->NotFound();
+}
+
+
+// We only need to deal with CALLBACKS and INTERCEPTORS
+MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(
+ LookupResult* result,
+ Name* name,
+ Object* value,
+ bool check_prototype,
+ StrictModeFlag strict_mode) {
+ if (check_prototype && !result->IsProperty()) {
+ LookupRealNamedPropertyInPrototypes(name, result);
+ }
+
+ if (result->IsProperty()) {
+ if (!result->IsReadOnly()) {
+ switch (result->type()) {
+ case CALLBACKS: {
+ Object* obj = result->GetCallbackObject();
+ if (obj->IsAccessorInfo()) {
+ AccessorInfo* info = AccessorInfo::cast(obj);
+ if (info->all_can_write()) {
+ return SetPropertyWithCallback(result->GetCallbackObject(),
+ name,
+ value,
+ result->holder(),
+ strict_mode);
+ }
+ }
+ break;
+ }
+ 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(GetIsolate());
+ LookupRealNamedProperty(name, &r);
+ if (r.IsProperty()) {
+ return SetPropertyWithFailedAccessCheck(&r,
+ name,
+ value,
+ check_prototype,
+ strict_mode);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> value_handle(value, isolate);
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return *value_handle;
+}
+
+
+MaybeObject* JSReceiver::SetProperty(LookupResult* result,
+ Name* key,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ JSReceiver::StoreFromKeyed store_mode) {
+ if (result->IsHandler()) {
+ return result->proxy()->SetPropertyWithHandler(
+ this, key, value, attributes, strict_mode);
+ } else {
+ return JSObject::cast(this)->SetPropertyForResult(
+ result, key, value, attributes, strict_mode, store_mode);
+ }
+}
+
+
+bool JSProxy::HasPropertyWithHandler(Name* name_raw) {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> receiver(this, isolate);
+ Handle<Object> name(name_raw, isolate);
+
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (name->IsSymbol()) return false;
+
+ Handle<Object> args[] = { name };
+ Handle<Object> result = CallTrap(
+ "has", isolate->derived_has_trap(), ARRAY_SIZE(args), args);
+ if (isolate->has_pending_exception()) return false;
+
+ return result->BooleanValue();
+}
+
+
+MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler(
+ JSReceiver* receiver_raw,
+ Name* name_raw,
+ Object* value_raw,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode) {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSReceiver> receiver(receiver_raw);
+ Handle<Object> name(name_raw, isolate);
+ Handle<Object> value(value_raw, isolate);
+
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (name->IsSymbol()) return *value;
+
+ Handle<Object> args[] = { receiver, name, value };
+ CallTrap("set", isolate->derived_set_trap(), ARRAY_SIZE(args), args);
+ if (isolate->has_pending_exception()) return Failure::Exception();
+
+ return *value;
+}
+
+
+MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyViaPrototypesWithHandler(
+ JSReceiver* receiver_raw,
+ Name* name_raw,
+ Object* value_raw,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool* done) {
+ Isolate* isolate = GetIsolate();
+ Handle<JSProxy> proxy(this);
+ Handle<JSReceiver> receiver(receiver_raw);
+ Handle<Name> name(name_raw);
+ Handle<Object> value(value_raw, isolate);
+ Handle<Object> handler(this->handler(), isolate); // Trap might morph proxy.
+
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (name->IsSymbol()) {
+ *done = false;
+ return isolate->heap()->the_hole_value();
+ }
+
+ *done = true; // except where redefined...
+ 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()) {
+ *done = false;
+ return isolate->heap()->the_hole_value();
+ }
+
+ // 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();
+
+ // [[GetProperty]] requires to check that all properties are configurable.
+ Handle<String> configurable_name =
+ isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("configurable_"));
+ Handle<Object> configurable(
+ v8::internal::GetProperty(isolate, desc, configurable_name));
+ ASSERT(!isolate->has_pending_exception());
+ ASSERT(configurable->IsTrue() || configurable->IsFalse());
+ if (configurable->IsFalse()) {
+ Handle<String> trap =
+ isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("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 DataDescriptor.
+ Handle<String> hasWritable_name =
+ isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("hasWritable_"));
+ Handle<Object> hasWritable(
+ v8::internal::GetProperty(isolate, desc, hasWritable_name));
+ ASSERT(!isolate->has_pending_exception());
+ ASSERT(hasWritable->IsTrue() || hasWritable->IsFalse());
+ if (hasWritable->IsTrue()) {
+ Handle<String> writable_name =
+ isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("writable_"));
+ Handle<Object> writable(
+ v8::internal::GetProperty(isolate, desc, writable_name));
+ ASSERT(!isolate->has_pending_exception());
+ ASSERT(writable->IsTrue() || writable->IsFalse());
+ *done = writable->IsFalse();
+ if (!*done) return GetHeap()->the_hole_value();
+ if (strict_mode == kNonStrictMode) return *value;
+ Handle<Object> args[] = { name, receiver };
+ Handle<Object> error = isolate->factory()->NewTypeError(
+ "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args)));
+ return isolate->Throw(*error);
+ }
+
+ // We have an AccessorDescriptor.
+ Handle<String> set_name = isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("set_"));
+ Handle<Object> setter(v8::internal::GetProperty(isolate, desc, set_name));
+ ASSERT(!isolate->has_pending_exception());
+ if (!setter->IsUndefined()) {
+ // TODO(rossberg): nicer would be to cast to some JSCallable here...
+ return receiver->SetPropertyWithDefinedSetter(
+ JSReceiver::cast(*setter), *value);
+ }
+
+ if (strict_mode == kNonStrictMode) return *value;
+ Handle<Object> args2[] = { name, proxy };
+ Handle<Object> error = isolate->factory()->NewTypeError(
+ "no_setter_in_callback", HandleVector(args2, ARRAY_SIZE(args2)));
+ return isolate->Throw(*error);
+}
+
+
+Handle<Object> JSProxy::DeletePropertyWithHandler(
+ Handle<JSProxy> object, Handle<Name> name, DeleteMode mode) {
+ Isolate* isolate = object->GetIsolate();
+
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (name->IsSymbol()) return isolate->factory()->false_value();
+
+ Handle<Object> args[] = { name };
+ Handle<Object> result = object->CallTrap(
+ "delete", Handle<Object>(), ARRAY_SIZE(args), args);
+ if (isolate->has_pending_exception()) return Handle<Object>();
+
+ bool result_bool = result->BooleanValue();
+ if (mode == STRICT_DELETION && !result_bool) {
+ Handle<Object> handler(object->handler(), isolate);
+ Handle<String> trap_name = isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("delete"));
+ Handle<Object> args[] = { handler, trap_name };
+ Handle<Object> error = isolate->factory()->NewTypeError(
+ "handler_failed", HandleVector(args, ARRAY_SIZE(args)));
+ isolate->Throw(*error);
+ return Handle<Object>();
+ }
+ return isolate->factory()->ToBoolean(result_bool);
+}
+
+
+Handle<Object> JSProxy::DeleteElementWithHandler(
+ Handle<JSProxy> object, uint32_t index, DeleteMode mode) {
+ Isolate* isolate = object->GetIsolate();
+ Handle<String> name = isolate->factory()->Uint32ToString(index);
+ return JSProxy::DeletePropertyWithHandler(object, name, mode);
+}
+
+
+MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler(
+ JSReceiver* receiver_raw,
+ Name* name_raw) {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSProxy> proxy(this);
+ Handle<Object> handler(this->handler(), isolate); // Trap might morph proxy.
+ Handle<JSReceiver> receiver(receiver_raw);
+ Handle<Object> name(name_raw, isolate);
+
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (name->IsSymbol()) return ABSENT;
+
+ Handle<Object> args[] = { name };
+ Handle<Object> result = CallTrap(
+ "getPropertyDescriptor", Handle<Object>(), ARRAY_SIZE(args), args);
+ if (isolate->has_pending_exception()) return NONE;
+
+ 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()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("enumerable_"));
+ Handle<Object> enumerable(v8::internal::GetProperty(isolate, desc, enum_n));
+ if (isolate->has_pending_exception()) return NONE;
+ Handle<String> conf_n = isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("configurable_"));
+ Handle<Object> configurable(v8::internal::GetProperty(isolate, desc, conf_n));
+ if (isolate->has_pending_exception()) return NONE;
+ Handle<String> writ_n = isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("writable_"));
+ Handle<Object> writable(v8::internal::GetProperty(isolate, desc, writ_n));
+ if (isolate->has_pending_exception()) return NONE;
+ if (!writable->BooleanValue()) {
+ Handle<String> set_n = isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("set_"));
+ Handle<Object> setter(v8::internal::GetProperty(isolate, desc, set_n));
+ if (isolate->has_pending_exception()) return NONE;
+ writable = isolate->factory()->ToBoolean(!setter->IsUndefined());
+ }
+
+ if (configurable->IsFalse()) {
+ Handle<String> trap = isolate->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR("getPropertyDescriptor"));
+ Handle<Object> args[] = { handler, trap, name };
+ Handle<Object> error = isolate->factory()->NewTypeError(
+ "proxy_prop_not_configurable", HandleVector(args, ARRAY_SIZE(args)));
+ isolate->Throw(*error);
+ return NONE;
+ }
+
+ int attributes = NONE;
+ if (!enumerable->BooleanValue()) attributes |= DONT_ENUM;
+ if (!configurable->BooleanValue()) attributes |= DONT_DELETE;
+ if (!writable->BooleanValue()) attributes |= READ_ONLY;
+ return static_cast<PropertyAttributes>(attributes);
+}
+
+
+MUST_USE_RESULT PropertyAttributes JSProxy::GetElementAttributeWithHandler(
+ JSReceiver* receiver_raw,
+ uint32_t index) {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSProxy> proxy(this);
+ Handle<JSReceiver> receiver(receiver_raw);
+ Handle<String> name = isolate->factory()->Uint32ToString(index);
+ return proxy->GetPropertyAttributeWithHandler(*receiver, *name);
+}
+
+
+void JSProxy::Fix() {
+ Isolate* isolate = GetIsolate();
+ 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.
+ } else {
+ 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, Smi::cast(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(), isolate);
+
+ Handle<String> trap_name = isolate->factory()->InternalizeUtf8String(name);
+ Handle<Object> trap(v8::internal::GetProperty(isolate, 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);
+}
+
+
+void JSObject::AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map) {
+ CALL_HEAP_FUNCTION_VOID(
+ object->GetIsolate(),
+ object->AllocateStorageForMap(*map));
+}
+
+
+void JSObject::MigrateInstance(Handle<JSObject> object) {
+ if (FLAG_trace_migration) {
+ PrintF("migrating instance %p (%p)\n",
+ static_cast<void*>(*object),
+ static_cast<void*>(object->map()));
+ }
+ CALL_HEAP_FUNCTION_VOID(
+ object->GetIsolate(),
+ object->MigrateInstance());
+}
+
+
+Handle<Object> JSObject::TryMigrateInstance(Handle<JSObject> object) {
+ if (FLAG_trace_migration) {
+ PrintF("migrating instance (no new maps) %p (%p)\n",
+ static_cast<void*>(*object),
+ static_cast<void*>(object->map()));
+ }
+ CALL_HEAP_FUNCTION(
+ object->GetIsolate(),
+ object->MigrateInstance(),
+ Object);
+}
+
+
+Handle<Map> Map::GeneralizeRepresentation(Handle<Map> map,
+ int modify_index,
+ Representation representation) {
+ CALL_HEAP_FUNCTION(
+ map->GetIsolate(),
+ map->GeneralizeRepresentation(modify_index, representation),
+ Map);
+}
+
+
+MaybeObject* JSObject::SetPropertyForResult(LookupResult* lookup,
+ Name* name_raw,
+ Object* value_raw,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ StoreFromKeyed store_mode) {
+ Heap* heap = GetHeap();
+ Isolate* isolate = heap->isolate();
+ // Make sure that the top context does not change when doing callbacks or
+ // interceptor calls.
+ AssertNoContextChange ncc;
+
+ // Optimization for 2-byte strings often used as keys in a decompression
+ // dictionary. We internalize these short keys to avoid constantly
+ // reallocating them.
+ if (name_raw->IsString() && !name_raw->IsInternalizedString() &&
+ String::cast(name_raw)->length() <= 2) {
+ Object* internalized_version;
+ { MaybeObject* maybe_string_version =
+ heap->InternalizeString(String::cast(name_raw));
+ if (maybe_string_version->ToObject(&internalized_version)) {
+ name_raw = String::cast(internalized_version);
+ }
+ }
+ }
+
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
+ return SetPropertyWithFailedAccessCheck(
+ lookup, name_raw, value_raw, true, strict_mode);
+ }
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return value_raw;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->SetPropertyForResult(
+ lookup, name_raw, value_raw, attributes, strict_mode, store_mode);
+ }
+
+ ASSERT(!lookup->IsFound() || lookup->holder() == this ||
+ lookup->holder()->map()->is_hidden_prototype());
+
+ // From this point on everything needs to be handlified, because
+ // SetPropertyViaPrototypes might call back into JavaScript.
+ HandleScope scope(isolate);
+ Handle<JSObject> self(this);
+ Handle<Name> name(name_raw);
+ Handle<Object> value(value_raw, isolate);
+
+ if (!lookup->IsProperty() && !self->IsJSContextExtensionObject()) {
+ bool done = false;
+ MaybeObject* result_object = self->SetPropertyViaPrototypes(
+ *name, *value, attributes, strict_mode, &done);
+ if (done) return result_object;
+ }
+
+ if (!lookup->IsFound()) {
+ // Neither properties nor transitions found.
+ return self->AddProperty(
+ *name, *value, attributes, strict_mode, store_mode);
+ }
+
+ if (lookup->IsProperty() && lookup->IsReadOnly()) {
+ if (strict_mode == kStrictMode) {
+ Handle<Object> args[] = { name, self };
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_read_only_property", HandleVector(args, ARRAY_SIZE(args))));
+ } else {
+ return *value;
+ }
+ }
+
+ Handle<Object> old_value(heap->the_hole_value(), isolate);
+ if (FLAG_harmony_observation &&
+ map()->is_observed() && lookup->IsDataProperty()) {
+ old_value = Object::GetProperty(self, name);
+ }
+
+ // This is a real property that is not read-only, or it is a
+ // transition or null descriptor and there are no setters in the prototypes.
+ MaybeObject* result = *value;
+ switch (lookup->type()) {
+ case NORMAL:
+ result = lookup->holder()->SetNormalizedProperty(lookup, *value);
+ break;
+ case FIELD: {
+ Representation representation = lookup->representation();
+ if (!value->FitsRepresentation(representation)) {
+ MaybeObject* maybe_failure =
+ lookup->holder()->GeneralizeFieldRepresentation(
+ lookup->GetDescriptorIndex(), value->OptimalRepresentation());
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ DescriptorArray* desc = lookup->holder()->map()->instance_descriptors();
+ int descriptor = lookup->GetDescriptorIndex();
+ representation = desc->GetDetails(descriptor).representation();
+ }
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ HeapNumber* storage =
+ HeapNumber::cast(lookup->holder()->RawFastPropertyAt(
+ lookup->GetFieldIndex().field_index()));
+ storage->set_value(value->Number());
+ result = *value;
+ break;
+ }
+ lookup->holder()->FastPropertyAtPut(
+ lookup->GetFieldIndex().field_index(), *value);
+ result = *value;
+ break;
+ }
+ case CONSTANT:
+ // Only replace the constant if necessary.
+ if (*value == lookup->GetConstant()) return *value;
+ // Preserve the attributes of this existing property.
+ attributes = lookup->GetAttributes();
+ result = lookup->holder()->ConvertDescriptorToField(
+ *name, *value, attributes);
+ break;
+ case CALLBACKS: {
+ Object* callback_object = lookup->GetCallbackObject();
+ return self->SetPropertyWithCallback(
+ callback_object, *name, *value, lookup->holder(), strict_mode);
+ }
+ case INTERCEPTOR:
+ result = lookup->holder()->SetPropertyWithInterceptor(
+ *name, *value, attributes, strict_mode);
+ break;
+ case TRANSITION: {
+ Map* transition_map = lookup->GetTransitionTarget();
+ int descriptor = transition_map->LastAdded();
+
+ DescriptorArray* descriptors = transition_map->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+
+ if (details.type() == FIELD) {
+ if (attributes == details.attributes()) {
+ Representation representation = details.representation();
+ if (!value->FitsRepresentation(representation)) {
+ MaybeObject* maybe_map = transition_map->GeneralizeRepresentation(
+ descriptor, value->OptimalRepresentation());
+ if (!maybe_map->To(&transition_map)) return maybe_map;
+ Object* back = transition_map->GetBackPointer();
+ if (back->IsMap()) {
+ MaybeObject* maybe_failure =
+ lookup->holder()->MigrateToMap(Map::cast(back));
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ }
+ DescriptorArray* desc = transition_map->instance_descriptors();
+ int descriptor = transition_map->LastAdded();
+ representation = desc->GetDetails(descriptor).representation();
+ }
+ int field_index = descriptors->GetFieldIndex(descriptor);
+ result = lookup->holder()->AddFastPropertyUsingMap(
+ transition_map, *name, *value, field_index, representation);
+ } else {
+ result = lookup->holder()->ConvertDescriptorToField(
+ *name, *value, attributes);
+ }
+ } else if (details.type() == CALLBACKS) {
+ result = lookup->holder()->ConvertDescriptorToField(
+ *name, *value, attributes);
+ } else {
+ ASSERT(details.type() == CONSTANT);
+
+ Object* constant = descriptors->GetValue(descriptor);
+ if (constant == *value) {
+ // If the same constant function is being added we can simply
+ // transition to the target map.
+ lookup->holder()->set_map(transition_map);
+ result = constant;
+ } else {
+ // Otherwise, replace with a map transition to a new map with a FIELD,
+ // even if the value is a constant function.
+ result = lookup->holder()->ConvertTransitionToMapTransition(
+ lookup->GetTransitionIndex(), *name, *value, attributes);
+ }
+ }
+ break;
+ }
+ case HANDLER:
+ case NONEXISTENT:
+ UNREACHABLE();
+ }
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (FLAG_harmony_observation && self->map()->is_observed()) {
+ if (lookup->IsTransition()) {
+ EnqueueChangeRecord(self, "new", name, old_value);
+ } else {
+ LookupResult new_lookup(isolate);
+ self->LocalLookup(*name, &new_lookup, true);
+ if (new_lookup.IsDataProperty()) {
+ Handle<Object> new_value = Object::GetProperty(self, name);
+ if (!new_value->SameValue(*old_value)) {
+ EnqueueChangeRecord(self, "updated", name, old_value);
+ }
+ }
+ }
+ }
+
+ return *hresult;
+}
+
+
+// Set a real local property, even if it is READ_ONLY. If the property is not
+// present, add it with attributes NONE. This code is an exact clone of
+// SetProperty, with the check for IsReadOnly and the check for a
+// callback setter removed. The two lines looking up the LookupResult
+// result are also added. If one of the functions is changed, the other
+// should be.
+// 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<Name> key,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ ValueType value_type,
+ StoreMode mode) {
+ CALL_HEAP_FUNCTION(
+ object->GetIsolate(),
+ object->SetLocalPropertyIgnoreAttributes(
+ *key, *value, attributes, value_type, mode),
+ Object);
+}
+
+
+MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
+ Name* name_raw,
+ Object* value_raw,
+ PropertyAttributes attributes,
+ ValueType value_type,
+ StoreMode mode) {
+ // Make sure that the top context does not change when doing callbacks or
+ // interceptor calls.
+ AssertNoContextChange ncc;
+ Isolate* isolate = GetIsolate();
+ LookupResult lookup(isolate);
+ LocalLookup(name_raw, &lookup, true);
+ if (!lookup.IsFound()) map()->LookupTransition(this, name_raw, &lookup);
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ if (!isolate->MayNamedAccess(this, name_raw, v8::ACCESS_SET)) {
+ return SetPropertyWithFailedAccessCheck(&lookup,
+ name_raw,
+ value_raw,
+ false,
+ kNonStrictMode);
+ }
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return value_raw;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes(
+ name_raw,
+ value_raw,
+ attributes,
+ value_type,
+ mode);
+ }
+
+ // Check for accessor in prototype chain removed here in clone.
+ if (!lookup.IsFound()) {
+ // Neither properties nor transitions found.
+ return AddProperty(
+ name_raw, value_raw, attributes, kNonStrictMode,
+ MAY_BE_STORE_FROM_KEYED, PERFORM_EXTENSIBILITY_CHECK, value_type, mode);
+ }
+
+ // From this point on everything needs to be handlified.
+ HandleScope scope(isolate);
+ Handle<JSObject> self(this);
+ Handle<Name> name(name_raw);
+ Handle<Object> value(value_raw, isolate);
+
+ Handle<Object> old_value(isolate->heap()->the_hole_value(), isolate);
+ PropertyAttributes old_attributes = ABSENT;
+ bool is_observed = FLAG_harmony_observation && self->map()->is_observed();
+ if (is_observed && lookup.IsProperty()) {
+ if (lookup.IsDataProperty()) old_value = Object::GetProperty(self, name);
+ old_attributes = lookup.GetAttributes();
+ }
+
+ // Check of IsReadOnly removed from here in clone.
+ MaybeObject* result = *value;
+ switch (lookup.type()) {
+ case NORMAL: {
+ PropertyDetails details = PropertyDetails(attributes, NORMAL, 0);
+ result = self->SetNormalizedProperty(*name, *value, details);
+ break;
+ }
+ case FIELD: {
+ Representation representation = lookup.representation();
+ Representation value_representation =
+ value->OptimalRepresentation(value_type);
+ if (value_representation.IsNone()) break;
+ if (!value_representation.fits_into(representation)) {
+ MaybeObject* maybe_failure = self->GeneralizeFieldRepresentation(
+ lookup.GetDescriptorIndex(), value_representation);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ DescriptorArray* desc = self->map()->instance_descriptors();
+ int descriptor = lookup.GetDescriptorIndex();
+ representation = desc->GetDetails(descriptor).representation();
+ }
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ HeapNumber* storage =
+ HeapNumber::cast(self->RawFastPropertyAt(
+ lookup.GetFieldIndex().field_index()));
+ storage->set_value(value->Number());
+ result = *value;
+ break;
+ }
+ self->FastPropertyAtPut(lookup.GetFieldIndex().field_index(), *value);
+ result = *value;
+ break;
+ }
+ case CONSTANT:
+ // Only replace the function if necessary.
+ if (*value != lookup.GetConstant()) {
+ // Preserve the attributes of this existing property.
+ attributes = lookup.GetAttributes();
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ }
+ break;
+ case CALLBACKS:
+ case INTERCEPTOR:
+ // Override callback in clone
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ break;
+ case TRANSITION: {
+ Map* transition_map = lookup.GetTransitionTarget();
+ int descriptor = transition_map->LastAdded();
+
+ DescriptorArray* descriptors = transition_map->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+
+ if (details.type() == FIELD) {
+ if (attributes == details.attributes()) {
+ Representation representation = details.representation();
+ Representation value_representation =
+ value->OptimalRepresentation(value_type);
+ if (!value_representation.fits_into(representation)) {
+ MaybeObject* maybe_map = transition_map->GeneralizeRepresentation(
+ descriptor, value_representation);
+ if (!maybe_map->To(&transition_map)) return maybe_map;
+ Object* back = transition_map->GetBackPointer();
+ if (back->IsMap()) {
+ MaybeObject* maybe_failure = self->MigrateToMap(Map::cast(back));
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ }
+ DescriptorArray* desc = transition_map->instance_descriptors();
+ int descriptor = transition_map->LastAdded();
+ representation = desc->GetDetails(descriptor).representation();
+ }
+ int field_index = descriptors->GetFieldIndex(descriptor);
+ result = self->AddFastPropertyUsingMap(
+ transition_map, *name, *value, field_index, representation);
+ } else {
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ }
+ } else if (details.type() == CALLBACKS) {
+ result = self->ConvertDescriptorToField(*name, *value, attributes);
+ } else {
+ ASSERT(details.type() == CONSTANT);
+
+ // Replace transition to CONSTANT FUNCTION with a map transition to a
+ // new map with a FIELD, even if the value is a function.
+ result = self->ConvertTransitionToMapTransition(
+ lookup.GetTransitionIndex(), *name, *value, attributes);
+ }
+ break;
+ }
+ case HANDLER:
+ case NONEXISTENT:
+ UNREACHABLE();
+ }
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (is_observed) {
+ if (lookup.IsTransition()) {
+ EnqueueChangeRecord(self, "new", name, old_value);
+ } else if (old_value->IsTheHole()) {
+ EnqueueChangeRecord(self, "reconfigured", name, old_value);
+ } else {
+ LookupResult new_lookup(isolate);
+ self->LocalLookup(*name, &new_lookup, true);
+ bool value_changed = false;
+ if (new_lookup.IsDataProperty()) {
+ Handle<Object> new_value = Object::GetProperty(self, name);
+ value_changed = !old_value->SameValue(*new_value);
+ }
+ if (new_lookup.GetAttributes() != old_attributes) {
+ if (!value_changed) old_value = isolate->factory()->the_hole_value();
+ EnqueueChangeRecord(self, "reconfigured", name, old_value);
+ } else if (value_changed) {
+ EnqueueChangeRecord(self, "updated", name, old_value);
+ }
+ }
+ }
+
+ return *hresult;
+}
+
+
+PropertyAttributes JSObject::GetPropertyAttributePostInterceptor(
+ JSObject* receiver,
+ Name* name,
+ bool continue_search) {
+ // Check local property, ignore interceptor.
+ LookupResult result(GetIsolate());
+ LocalLookupRealNamedProperty(name, &result);
+ if (result.IsFound()) return result.GetAttributes();
+
+ if (continue_search) {
+ // Continue searching via the prototype chain.
+ Object* pt = GetPrototype();
+ if (!pt->IsNull()) {
+ return JSObject::cast(pt)->
+ GetPropertyAttributeWithReceiver(receiver, name);
+ }
+ }
+ return ABSENT;
+}
+
+
+PropertyAttributes JSObject::GetPropertyAttributeWithInterceptor(
+ JSObject* receiver,
+ Name* name,
+ bool continue_search) {
+ // TODO(rossberg): Support symbols in the API.
+ if (name->IsSymbol()) return ABSENT;
+
+ Isolate* isolate = GetIsolate();
+
+ // Make sure that the top context does not change when doing
+ // callbacks or interceptor calls.
+ AssertNoContextChange ncc;
+
+ HandleScope scope(isolate);
+ Handle<InterceptorInfo> interceptor(GetNamedInterceptor());
+ Handle<JSObject> receiver_handle(receiver);
+ Handle<JSObject> holder_handle(this);
+ Handle<String> name_handle(String::cast(name));
+ PropertyCallbackArguments args(isolate, interceptor->data(), receiver, this);
+ if (!interceptor->query()->IsUndefined()) {
+ v8::NamedPropertyQuery query =
+ v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
+ LOG(isolate,
+ ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
+ v8::Handle<v8::Integer> result =
+ args.Call(query, v8::Utils::ToLocal(name_handle));
+ if (!result.IsEmpty()) {
+ ASSERT(result->IsInt32());
+ return static_cast<PropertyAttributes>(result->Int32Value());
+ }
+ } else if (!interceptor->getter()->IsUndefined()) {
+ v8::NamedPropertyGetter getter =
+ v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
+ LOG(isolate,
+ ApiNamedPropertyAccess("interceptor-named-get-has", this, name));
+ v8::Handle<v8::Value> result =
+ args.Call(getter, v8::Utils::ToLocal(name_handle));
+ if (!result.IsEmpty()) return DONT_ENUM;
+ }
+ return holder_handle->GetPropertyAttributePostInterceptor(*receiver_handle,
+ *name_handle,
+ continue_search);
+}
+
+
+PropertyAttributes JSReceiver::GetPropertyAttributeWithReceiver(
+ JSReceiver* receiver,
+ Name* key) {
+ uint32_t index = 0;
+ if (IsJSObject() && key->AsArrayIndex(&index)) {
+ return JSObject::cast(this)->GetElementAttributeWithReceiver(
+ receiver, index, true);
+ }
+ // Named property.
+ LookupResult lookup(GetIsolate());
+ Lookup(key, &lookup);
+ return GetPropertyAttributeForResult(receiver, &lookup, key, true);
+}
+
+
+PropertyAttributes JSReceiver::GetPropertyAttributeForResult(
+ JSReceiver* receiver,
+ LookupResult* lookup,
+ Name* name,
+ bool continue_search) {
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ JSObject* this_obj = JSObject::cast(this);
+ Heap* heap = GetHeap();
+ if (!heap->isolate()->MayNamedAccess(this_obj, name, v8::ACCESS_HAS)) {
+ return this_obj->GetPropertyAttributeWithFailedAccessCheck(
+ receiver, lookup, name, continue_search);
+ }
+ }
+ if (lookup->IsFound()) {
+ switch (lookup->type()) {
+ case NORMAL: // fall through
+ case FIELD:
+ case CONSTANT:
+ case CALLBACKS:
+ return lookup->GetAttributes();
+ case HANDLER: {
+ return JSProxy::cast(lookup->proxy())->GetPropertyAttributeWithHandler(
+ receiver, name);
+ }
+ case INTERCEPTOR:
+ return lookup->holder()->GetPropertyAttributeWithInterceptor(
+ JSObject::cast(receiver), name, continue_search);
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ }
+ }
+ return ABSENT;
+}
+
+
+PropertyAttributes JSReceiver::GetLocalPropertyAttribute(Name* name) {
+ // Check whether the name is an array index.
+ uint32_t index = 0;
+ if (IsJSObject() && name->AsArrayIndex(&index)) {
+ return GetLocalElementAttribute(index);
+ }
+ // Named property.
+ LookupResult lookup(GetIsolate());
+ LocalLookup(name, &lookup, true);
+ return GetPropertyAttributeForResult(this, &lookup, name, false);
+}
+
+
+PropertyAttributes JSObject::GetElementAttributeWithReceiver(
+ JSReceiver* receiver, uint32_t index, bool continue_search) {
+ Isolate* isolate = GetIsolate();
+
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return ABSENT;
+ }
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return ABSENT;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->GetElementAttributeWithReceiver(
+ receiver, index, continue_search);
+ }
+
+ // Check for lookup interceptor except when bootstrapping.
+ if (HasIndexedInterceptor() && !isolate->bootstrapper()->IsActive()) {
+ return GetElementAttributeWithInterceptor(receiver, index, continue_search);
+ }
+
+ return GetElementAttributeWithoutInterceptor(
+ receiver, index, continue_search);
+}
+
+
+PropertyAttributes JSObject::GetElementAttributeWithInterceptor(
+ JSReceiver* receiver, uint32_t index, bool continue_search) {
+ Isolate* isolate = GetIsolate();
+ // Make sure that the top context does not change when doing
+ // callbacks or interceptor calls.
+ AssertNoContextChange ncc;
+ HandleScope scope(isolate);
+ Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
+ Handle<JSReceiver> hreceiver(receiver);
+ Handle<JSObject> holder(this);
+ PropertyCallbackArguments args(isolate, interceptor->data(), receiver, this);
+ if (!interceptor->query()->IsUndefined()) {
+ v8::IndexedPropertyQuery query =
+ v8::ToCData<v8::IndexedPropertyQuery>(interceptor->query());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-has", this, index));
+ v8::Handle<v8::Integer> result = args.Call(query, index);
+ if (!result.IsEmpty())
+ return static_cast<PropertyAttributes>(result->Int32Value());
+ } else if (!interceptor->getter()->IsUndefined()) {
+ v8::IndexedPropertyGetter getter =
+ v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-get-has", this, index));
+ v8::Handle<v8::Value> result = args.Call(getter, index);
+ if (!result.IsEmpty()) return NONE;
+ }
+
+ return holder->GetElementAttributeWithoutInterceptor(
+ *hreceiver, index, continue_search);
+}
+
+
+PropertyAttributes JSObject::GetElementAttributeWithoutInterceptor(
+ JSReceiver* receiver, uint32_t index, bool continue_search) {
+ PropertyAttributes attr = GetElementsAccessor()->GetAttributes(
+ receiver, this, index);
+ if (attr != ABSENT) return attr;
+
+ // Handle [] on String objects.
+ if (IsStringObjectWithCharacterAt(index)) {
+ return static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
+ }
+
+ if (!continue_search) return ABSENT;
+
+ Object* pt = GetPrototype();
+ if (pt->IsJSProxy()) {
+ // We need to follow the spec and simulate a call to [[GetOwnProperty]].
+ return JSProxy::cast(pt)->GetElementAttributeWithHandler(receiver, index);
+ }
+ if (pt->IsNull()) return ABSENT;
+ return JSObject::cast(pt)->GetElementAttributeWithReceiver(
+ receiver, index, true);
+}
+
+
+MaybeObject* NormalizedMapCache::Get(JSObject* obj,
+ PropertyNormalizationMode mode) {
+ Isolate* isolate = obj->GetIsolate();
+ Map* fast = obj->map();
+ int index = fast->Hash() % kEntries;
+ Object* result = get(index);
+ if (result->IsMap() &&
+ Map::cast(result)->EquivalentToForNormalization(fast, mode)) {
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ Map::cast(result)->SharedMapVerify();
+ }
+#endif
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ // The cached map should match newly created normalized map bit-by-bit,
+ // except for the code cache, which can contain some ics which can be
+ // applied to the shared map.
+ Object* fresh;
+ MaybeObject* maybe_fresh =
+ fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
+ if (maybe_fresh->ToObject(&fresh)) {
+ ASSERT(memcmp(Map::cast(fresh)->address(),
+ Map::cast(result)->address(),
+ Map::kCodeCacheOffset) == 0);
+ STATIC_ASSERT(Map::kDependentCodeOffset ==
+ Map::kCodeCacheOffset + kPointerSize);
+ int offset = Map::kDependentCodeOffset + kPointerSize;
+ ASSERT(memcmp(Map::cast(fresh)->address() + offset,
+ Map::cast(result)->address() + offset,
+ Map::kSize - offset) == 0);
+ }
+ }
+#endif
+ return result;
+ }
+
+ { MaybeObject* maybe_result =
+ fast->CopyNormalized(mode, SHARED_NORMALIZED_MAP);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ ASSERT(Map::cast(result)->is_dictionary_map());
+ set(index, result);
+ isolate->counters()->normalized_maps()->Increment();
+
+ return result;
+}
+
+
+void NormalizedMapCache::Clear() {
+ int entries = length();
+ for (int i = 0; i != entries; i++) {
+ set_undefined(i);
+ }
+}
+
+
+void JSObject::UpdateMapCodeCache(Handle<JSObject> object,
+ Handle<Name> name,
+ Handle<Code> code) {
+ Isolate* isolate = object->GetIsolate();
+ CALL_HEAP_FUNCTION_VOID(isolate,
+ object->UpdateMapCodeCache(*name, *code));
+}
+
+
+MaybeObject* JSObject::UpdateMapCodeCache(Name* name, Code* code) {
+ if (map()->is_shared()) {
+ // Fast case maps are never marked as shared.
+ ASSERT(!HasFastProperties());
+ // Replace the map with an identical copy that can be safely modified.
+ Object* obj;
+ { MaybeObject* maybe_obj = map()->CopyNormalized(KEEP_INOBJECT_PROPERTIES,
+ UNIQUE_NORMALIZED_MAP);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ GetIsolate()->counters()->normalized_maps()->Increment();
+
+ set_map(Map::cast(obj));
+ }
+ return map()->UpdateCodeCache(name, 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;
+
+ // The global object is always normalized.
+ ASSERT(!IsGlobalObject());
+ // JSGlobalProxy must never be normalized
+ ASSERT(!IsJSGlobalProxy());
+
+ Map* map_of_this = map();
+
+ // Allocate new content.
+ int real_size = map_of_this->NumberOfOwnDescriptors();
+ int property_count = real_size;
+ if (expected_additional_properties > 0) {
+ property_count += expected_additional_properties;
+ } else {
+ property_count += 2; // Make space for two more properties.
+ }
+ NameDictionary* dictionary;
+ MaybeObject* maybe_dictionary =
+ NameDictionary::Allocate(GetHeap(), property_count);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+
+ DescriptorArray* descs = map_of_this->instance_descriptors();
+ for (int i = 0; i < real_size; i++) {
+ PropertyDetails details = descs->GetDetails(i);
+ switch (details.type()) {
+ case CONSTANT: {
+ PropertyDetails d = PropertyDetails(
+ details.attributes(), NORMAL, i + 1);
+ Object* value = descs->GetConstant(i);
+ MaybeObject* maybe_dictionary =
+ dictionary->Add(descs->GetKey(i), value, d);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+ break;
+ }
+ case FIELD: {
+ PropertyDetails d =
+ PropertyDetails(details.attributes(), NORMAL, i + 1);
+ Object* value = RawFastPropertyAt(descs->GetFieldIndex(i));
+ MaybeObject* maybe_dictionary =
+ dictionary->Add(descs->GetKey(i), value, d);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+ break;
+ }
+ case CALLBACKS: {
+ Object* value = descs->GetCallbacksObject(i);
+ PropertyDetails d = PropertyDetails(
+ details.attributes(), CALLBACKS, i + 1);
+ MaybeObject* maybe_dictionary =
+ dictionary->Add(descs->GetKey(i), value, d);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+ break;
+ }
+ case INTERCEPTOR:
+ break;
+ case HANDLER:
+ case NORMAL:
+ case TRANSITION:
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ Heap* current_heap = GetHeap();
+
+ // Copy the next enumeration index from instance descriptor.
+ dictionary->SetNextEnumerationIndex(real_size + 1);
+
+ Map* new_map;
+ MaybeObject* maybe_map =
+ current_heap->isolate()->context()->native_context()->
+ normalized_map_cache()->Get(this, mode);
+ if (!maybe_map->To(&new_map)) return maybe_map;
+ ASSERT(new_map->is_dictionary_map());
+
+ // We have now successfully allocated all the necessary objects.
+ // Changes can now be made with the guarantee that all of them take effect.
+
+ // Resize the object in the heap if necessary.
+ int new_instance_size = new_map->instance_size();
+ int instance_size_delta = map_of_this->instance_size() - new_instance_size;
+ 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);
+ map_of_this->NotifyLeafMapLayoutChange();
+
+ set_properties(dictionary);
+
+ current_heap->isolate()->counters()->props_to_dictionary()->Increment();
+
+#ifdef DEBUG
+ if (FLAG_trace_normalization) {
+ PrintF("Object properties have been normalized:\n");
+ Print();
+ }
+#endif
+ return this;
+}
+
+
+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());
+ return property_dictionary()->
+ TransformPropertiesToFastFor(this, unused_property_fields);
+}
+
+
+static MUST_USE_RESULT MaybeObject* CopyFastElementsToDictionary(
+ Isolate* isolate,
+ FixedArrayBase* array,
+ int length,
+ SeededNumberDictionary* dictionary) {
+ Heap* heap = isolate->heap();
+ bool has_double_elements = array->IsFixedDoubleArray();
+ for (int i = 0; i < length; i++) {
+ Object* value = NULL;
+ if (has_double_elements) {
+ FixedDoubleArray* double_array = FixedDoubleArray::cast(array);
+ if (double_array->is_the_hole(i)) {
+ value = isolate->heap()->the_hole_value();
+ } else {
+ // Objects must be allocated in the old object space, since the
+ // overall number of HeapNumbers needed for the conversion might
+ // exceed the capacity of new space, and we would fail repeatedly
+ // trying to convert the FixedDoubleArray.
+ MaybeObject* maybe_value_object =
+ heap->AllocateHeapNumber(double_array->get_scalar(i), TENURED);
+ if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
+ }
+ } else {
+ value = FixedArray::cast(array)->get(i);
+ }
+ if (!value->IsTheHole()) {
+ PropertyDetails details = PropertyDetails(NONE, NORMAL, 0);
+ MaybeObject* maybe_result =
+ dictionary->AddNumberEntry(i, value, details);
+ if (!maybe_result->To(&dictionary)) return maybe_result;
+ }
+ }
+ return dictionary;
+}
+
+
+Handle<SeededNumberDictionary> JSObject::NormalizeElements(
+ Handle<JSObject> object) {
+ CALL_HEAP_FUNCTION(object->GetIsolate(),
+ object->NormalizeElements(),
+ SeededNumberDictionary);
+}
+
+
+MaybeObject* JSObject::NormalizeElements() {
+ ASSERT(!HasExternalArrayElements());
+
+ // Find the backing store.
+ FixedArrayBase* array = FixedArrayBase::cast(elements());
+ Map* old_map = array->map();
+ bool is_arguments =
+ (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(HasFastSmiOrObjectElements() ||
+ HasFastDoubleElements() ||
+ HasFastArgumentsElements());
+ // Compute the effective length and allocate a new backing store.
+ int length = IsJSArray()
+ ? Smi::cast(JSArray::cast(this)->length())->value()
+ : array->length();
+ int old_capacity = 0;
+ int used_elements = 0;
+ GetElementsCapacityAndUsage(&old_capacity, &used_elements);
+ SeededNumberDictionary* dictionary;
+ MaybeObject* maybe_dictionary =
+ SeededNumberDictionary::Allocate(GetHeap(), used_elements);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+
+ maybe_dictionary = CopyFastElementsToDictionary(
+ GetIsolate(), array, length, dictionary);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+
+ // Switch to using the dictionary as the backing storage for elements.
+ if (is_arguments) {
+ FixedArray::cast(elements())->set(1, dictionary);
+ } else {
+ // Set the new map first to satify the elements type assert in
+ // set_elements().
+ Map* new_map;
+ MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(),
+ DICTIONARY_ELEMENTS);
+ if (!maybe->To(&new_map)) return maybe;
+ set_map(new_map);
+ set_elements(dictionary);
+ }
+
+ old_map->GetHeap()->isolate()->counters()->elements_to_dictionary()->
+ Increment();
+
+#ifdef DEBUG
+ if (FLAG_trace_normalization) {
+ PrintF("Object elements have been normalized:\n");
+ Print();
+ }
+#endif
+
+ ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
+ return dictionary;
+}
+
+
+Smi* JSReceiver::GenerateIdentityHash() {
+ Isolate* isolate = GetIsolate();
+
+ 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::RandomPrivate(isolate) & Smi::kMaxValue;
+ attempts++;
+ } while (hash_value == 0 && attempts < 30);
+ hash_value = hash_value != 0 ? hash_value : 1; // never return 0
+
+ return Smi::FromInt(hash_value);
+}
+
+
+MaybeObject* JSObject::SetIdentityHash(Smi* hash, CreationFlag flag) {
+ MaybeObject* maybe = SetHiddenProperty(GetHeap()->identity_hash_string(),
+ hash);
+ if (maybe->IsFailure()) return maybe;
+ return this;
+}
+
+
+int JSObject::GetIdentityHash(Handle<JSObject> obj) {
+ CALL_AND_RETRY_OR_DIE(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_string());
+ 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_string(),
+ 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(Name* key) {
+ ASSERT(key->IsUniqueName());
+ 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()->the_hole_value();
+ ASSERT(proxy_parent->IsJSGlobalObject());
+ return JSObject::cast(proxy_parent)->GetHiddenProperty(key);
+ }
+ ASSERT(!IsJSGlobalProxy());
+ MaybeObject* hidden_lookup =
+ GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
+ Object* inline_value = hidden_lookup->ToObjectUnchecked();
+
+ if (inline_value->IsSmi()) {
+ // Handle inline-stored identity hash.
+ if (key == GetHeap()->identity_hash_string()) {
+ return inline_value;
+ } else {
+ return GetHeap()->the_hole_value();
+ }
+ }
+
+ if (inline_value->IsUndefined()) return GetHeap()->the_hole_value();
+
+ ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value);
+ Object* entry = hashtable->Lookup(key);
+ return entry;
+}
+
+
+Handle<Object> JSObject::SetHiddenProperty(Handle<JSObject> obj,
+ Handle<Name> key,
+ Handle<Object> value) {
+ CALL_HEAP_FUNCTION(obj->GetIsolate(),
+ obj->SetHiddenProperty(*key, *value),
+ Object);
+}
+
+
+MaybeObject* JSObject::SetHiddenProperty(Name* key, Object* value) {
+ ASSERT(key->IsUniqueName());
+ 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 =
+ GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
+ Object* inline_value = hidden_lookup->ToObjectUnchecked();
+
+ // If there is no backing store yet, store the identity hash inline.
+ if (value->IsSmi() &&
+ key == GetHeap()->identity_hash_string() &&
+ (inline_value->IsUndefined() || inline_value->IsSmi())) {
+ return SetHiddenPropertiesHashTable(value);
+ }
+
+ hidden_lookup = GetHiddenPropertiesHashTable(CREATE_NEW_IF_ABSENT);
+ ObjectHashTable* hashtable;
+ if (!hidden_lookup->To(&hashtable)) return hidden_lookup;
+
+ // If it was found, check if the key is already in the dictionary.
+ MaybeObject* insert_result = hashtable->Put(key, value);
+ ObjectHashTable* new_table;
+ if (!insert_result->To(&new_table)) return insert_result;
+ if (new_table != hashtable) {
+ // If adding the key expanded the dictionary (i.e., Add returned a new
+ // dictionary), store it back to the object.
+ MaybeObject* store_result = SetHiddenPropertiesHashTable(new_table);
+ if (store_result->IsFailure()) return store_result;
+ }
+ // Return this to mark success.
+ return this;
+}
+
+
+void JSObject::DeleteHiddenProperty(Name* key) {
+ ASSERT(key->IsUniqueName());
+ 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;
+ }
+ ASSERT(!IsJSGlobalProxy());
+ MaybeObject* hidden_lookup =
+ GetHiddenPropertiesHashTable(ONLY_RETURN_INLINE_VALUE);
+ Object* inline_value = hidden_lookup->ToObjectUnchecked();
+
+ // We never delete (inline-stored) identity hashes.
+ ASSERT(key != GetHeap()->identity_hash_string());
+ if (inline_value->IsUndefined() || inline_value->IsSmi()) return;
+
+ ObjectHashTable* hashtable = ObjectHashTable::cast(inline_value);
+ MaybeObject* delete_result = hashtable->Put(key, GetHeap()->the_hole_value());
+ USE(delete_result);
+ ASSERT(!delete_result->IsFailure()); // Delete does not cause GC.
+}
+
+
+bool JSObject::HasHiddenProperties() {
+ return GetPropertyAttributePostInterceptor(this,
+ GetHeap()->hidden_string(),
+ false) != ABSENT;
+}
+
+
+MaybeObject* JSObject::GetHiddenPropertiesHashTable(
+ InitializeHiddenProperties init_option) {
+ ASSERT(!IsJSGlobalProxy());
+ Object* inline_value;
+ if (HasFastProperties()) {
+ // If the object has fast properties, check whether the first slot
+ // in the descriptor array matches the hidden string. Since the
+ // hidden strings hash code is zero (and no other name 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) {
+ int sorted_index = descriptors->GetSortedKeyIndex(0);
+ if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_string() &&
+ sorted_index < map()->NumberOfOwnDescriptors()) {
+ ASSERT(descriptors->GetType(sorted_index) == FIELD);
+ MaybeObject* maybe_value = this->FastPropertyAt(
+ descriptors->GetDetails(sorted_index).representation(),
+ descriptors->GetFieldIndex(sorted_index));
+ if (!maybe_value->To(&inline_value)) return maybe_value;
+ } else {
+ inline_value = GetHeap()->undefined_value();
+ }
+ } else {
+ inline_value = GetHeap()->undefined_value();
+ }
+ } else {
+ PropertyAttributes attributes;
+ // You can't install a getter on a property indexed by the hidden string,
+ // so we can be sure that GetLocalPropertyPostInterceptor returns a real
+ // object.
+ inline_value =
+ GetLocalPropertyPostInterceptor(this,
+ GetHeap()->hidden_string(),
+ &attributes)->ToObjectUnchecked();
+ }
+
+ if (init_option == ONLY_RETURN_INLINE_VALUE ||
+ inline_value->IsHashTable()) {
+ return inline_value;
+ }
+
+ ObjectHashTable* hashtable;
+ static const int kInitialCapacity = 4;
+ MaybeObject* maybe_obj =
+ ObjectHashTable::Allocate(GetHeap(),
+ kInitialCapacity,
+ ObjectHashTable::USE_CUSTOM_MINIMUM_CAPACITY);
+ if (!maybe_obj->To<ObjectHashTable>(&hashtable)) return maybe_obj;
+
+ if (inline_value->IsSmi()) {
+ // We were storing the identity hash inline and now allocated an actual
+ // dictionary. Put the identity hash into the new dictionary.
+ MaybeObject* insert_result =
+ hashtable->Put(GetHeap()->identity_hash_string(), inline_value);
+ ObjectHashTable* new_table;
+ if (!insert_result->To(&new_table)) return insert_result;
+ // We expect no resizing for the first insert.
+ ASSERT_EQ(hashtable, new_table);
+ }
+
+ MaybeObject* store_result =
+ SetPropertyPostInterceptor(GetHeap()->hidden_string(),
+ hashtable,
+ DONT_ENUM,
+ kNonStrictMode,
+ OMIT_EXTENSIBILITY_CHECK,
+ FORCE_FIELD);
+ if (store_result->IsFailure()) return store_result;
+ return hashtable;
+}
+
+
+MaybeObject* JSObject::SetHiddenPropertiesHashTable(Object* value) {
+ ASSERT(!IsJSGlobalProxy());
+ // We can store the identity hash inline iff there is no backing store
+ // for hidden properties yet.
+ ASSERT(HasHiddenProperties() != value->IsSmi());
+ if (HasFastProperties()) {
+ // If the object has fast properties, check whether the first slot
+ // in the descriptor array matches the hidden string. Since the
+ // hidden strings hash code is zero (and no other name 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) {
+ int sorted_index = descriptors->GetSortedKeyIndex(0);
+ if (descriptors->GetKey(sorted_index) == GetHeap()->hidden_string() &&
+ sorted_index < map()->NumberOfOwnDescriptors()) {
+ ASSERT(descriptors->GetType(sorted_index) == FIELD);
+ FastPropertyAtPut(descriptors->GetFieldIndex(sorted_index), value);
+ return this;
+ }
+ }
+ }
+ MaybeObject* store_result =
+ SetPropertyPostInterceptor(GetHeap()->hidden_string(),
+ value,
+ DONT_ENUM,
+ kNonStrictMode,
+ OMIT_EXTENSIBILITY_CHECK,
+ FORCE_FIELD);
+ if (store_result->IsFailure()) return store_result;
+ return this;
+}
+
+
+Handle<Object> JSObject::DeletePropertyPostInterceptor(Handle<JSObject> object,
+ Handle<Name> name,
+ DeleteMode mode) {
+ // Check local property, ignore interceptor.
+ Isolate* isolate = object->GetIsolate();
+ LookupResult result(isolate);
+ object->LocalLookupRealNamedProperty(*name, &result);
+ if (!result.IsFound()) return isolate->factory()->true_value();
+
+ // Normalize object if needed.
+ NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
+
+ return DeleteNormalizedProperty(object, name, mode);
+}
+
+
+Handle<Object> JSObject::DeletePropertyWithInterceptor(Handle<JSObject> object,
+ Handle<Name> name) {
+ Isolate* isolate = object->GetIsolate();
+
+ // TODO(rossberg): Support symbols in the API.
+ if (name->IsSymbol()) return isolate->factory()->false_value();
+
+ Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor());
+ if (!interceptor->deleter()->IsUndefined()) {
+ v8::NamedPropertyDeleter deleter =
+ v8::ToCData<v8::NamedPropertyDeleter>(interceptor->deleter());
+ LOG(isolate,
+ ApiNamedPropertyAccess("interceptor-named-delete", *object, *name));
+ PropertyCallbackArguments args(
+ isolate, interceptor->data(), *object, *object);
+ v8::Handle<v8::Boolean> result =
+ args.Call(deleter, v8::Utils::ToLocal(Handle<String>::cast(name)));
+ RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object);
+ if (!result.IsEmpty()) {
+ ASSERT(result->IsBoolean());
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ // Rebox CustomArguments::kReturnValueOffset before returning.
+ return handle(*result_internal, isolate);
+ }
+ }
+ Handle<Object> result =
+ DeletePropertyPostInterceptor(object, name, NORMAL_DELETION);
+ RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object);
+ return result;
+}
+
+
+MaybeObject* JSObject::DeleteElementWithInterceptor(uint32_t index) {
+ Isolate* isolate = GetIsolate();
+ Heap* heap = isolate->heap();
+ // Make sure that the top context does not change when doing
+ // callbacks or interceptor calls.
+ AssertNoContextChange ncc;
+ HandleScope scope(isolate);
+ Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
+ if (interceptor->deleter()->IsUndefined()) return heap->false_value();
+ v8::IndexedPropertyDeleter deleter =
+ v8::ToCData<v8::IndexedPropertyDeleter>(interceptor->deleter());
+ Handle<JSObject> this_handle(this);
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-delete", this, index));
+ PropertyCallbackArguments args(isolate, interceptor->data(), this, this);
+ v8::Handle<v8::Boolean> result = args.Call(deleter, index);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!result.IsEmpty()) {
+ ASSERT(result->IsBoolean());
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
+ }
+ MaybeObject* raw_result = this_handle->GetElementsAccessor()->Delete(
+ *this_handle,
+ index,
+ NORMAL_DELETION);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return raw_result;
+}
+
+
+Handle<Object> JSObject::DeleteElement(Handle<JSObject> obj,
+ uint32_t index,
+ DeleteMode mode) {
+ CALL_HEAP_FUNCTION(obj->GetIsolate(),
+ obj->DeleteElement(index, mode),
+ Object);
+}
+
+
+MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
+ Isolate* isolate = GetIsolate();
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded() &&
+ !isolate->MayIndexedAccess(this, index, v8::ACCESS_DELETE)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_DELETE);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return isolate->heap()->false_value();
+ }
+
+ if (IsStringObjectWithCharacterAt(index)) {
+ if (mode == STRICT_DELETION) {
+ // Deleting a non-configurable property in strict mode.
+ HandleScope scope(isolate);
+ Handle<Object> holder(this, isolate);
+ Handle<Object> name = isolate->factory()->NewNumberFromUint(index);
+ Handle<Object> args[2] = { name, holder };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("strict_delete_property",
+ HandleVector(args, 2));
+ return isolate->Throw(*error);
+ }
+ return isolate->heap()->false_value();
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return isolate->heap()->false_value();
+ ASSERT(proto->IsJSGlobalObject());
+ return JSGlobalObject::cast(proto)->DeleteElement(index, mode);
+ }
+
+ // From this point on everything needs to be handlified.
+ HandleScope scope(isolate);
+ Handle<JSObject> self(this);
+
+ Handle<Object> old_value;
+ bool should_enqueue_change_record = false;
+ if (FLAG_harmony_observation && self->map()->is_observed()) {
+ should_enqueue_change_record = self->HasLocalElement(index);
+ if (should_enqueue_change_record) {
+ old_value = self->GetLocalElementAccessorPair(index) != NULL
+ ? Handle<Object>::cast(isolate->factory()->the_hole_value())
+ : Object::GetElement(self, index);
+ }
+ }
+
+ MaybeObject* result;
+ // Skip interceptor if forcing deletion.
+ if (self->HasIndexedInterceptor() && mode != FORCE_DELETION) {
+ result = self->DeleteElementWithInterceptor(index);
+ } else {
+ result = self->GetElementsAccessor()->Delete(*self, index, mode);
+ }
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ if (should_enqueue_change_record && !self->HasLocalElement(index)) {
+ Handle<String> name = isolate->factory()->Uint32ToString(index);
+ EnqueueChangeRecord(self, "deleted", name, old_value);
+ }
+
+ return *hresult;
+}
+
+
+Handle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
+ Handle<Name> name,
+ DeleteMode mode) {
+ Isolate* isolate = object->GetIsolate();
+ // ECMA-262, 3rd, 8.6.2.5
+ ASSERT(name->IsName());
+
+ // Check access rights if needed.
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(*object, *name, v8::ACCESS_DELETE)) {
+ isolate->ReportFailedAccessCheck(*object, v8::ACCESS_DELETE);
+ RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object);
+ return isolate->factory()->false_value();
+ }
+
+ if (object->IsJSGlobalProxy()) {
+ Object* proto = object->GetPrototype();
+ if (proto->IsNull()) return isolate->factory()->false_value();
+ ASSERT(proto->IsJSGlobalObject());
+ return JSGlobalObject::DeleteProperty(
+ handle(JSGlobalObject::cast(proto)), name, mode);
+ }
+
+ uint32_t index = 0;
+ if (name->AsArrayIndex(&index)) {
+ return DeleteElement(object, index, mode);
+ }
+
+ LookupResult lookup(isolate);
+ object->LocalLookup(*name, &lookup, true);
+ if (!lookup.IsFound()) return isolate->factory()->true_value();
+ // Ignore attributes if forcing a deletion.
+ if (lookup.IsDontDelete() && mode != FORCE_DELETION) {
+ if (mode == STRICT_DELETION) {
+ // Deleting a non-configurable property in strict mode.
+ Handle<Object> args[2] = { name, object };
+ Handle<Object> error = isolate->factory()->NewTypeError(
+ "strict_delete_property", HandleVector(args, ARRAY_SIZE(args)));
+ isolate->Throw(*error);
+ return Handle<Object>();
+ }
+ return isolate->factory()->false_value();
+ }
+
+ Handle<Object> old_value = isolate->factory()->the_hole_value();
+ bool is_observed = FLAG_harmony_observation && object->map()->is_observed();
+ if (is_observed && lookup.IsDataProperty()) {
+ old_value = Object::GetProperty(object, name);
+ }
+ Handle<Object> result;
+
+ // Check for interceptor.
+ if (lookup.IsInterceptor()) {
+ // Skip interceptor if forcing a deletion.
+ if (mode == FORCE_DELETION) {
+ result = DeletePropertyPostInterceptor(object, name, mode);
+ } else {
+ result = DeletePropertyWithInterceptor(object, name);
+ }
+ } else {
+ // Normalize object if needed.
+ NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
+ // Make sure the properties are normalized before removing the entry.
+ result = DeleteNormalizedProperty(object, name, mode);
+ }
+
+ if (is_observed && !object->HasLocalProperty(*name)) {
+ EnqueueChangeRecord(object, "deleted", name, old_value);
+ }
+
+ return result;
+}
+
+
+Handle<Object> JSReceiver::DeleteElement(Handle<JSReceiver> object,
+ uint32_t index,
+ DeleteMode mode) {
+ if (object->IsJSProxy()) {
+ return JSProxy::DeleteElementWithHandler(
+ Handle<JSProxy>::cast(object), index, mode);
+ }
+ return JSObject::DeleteElement(Handle<JSObject>::cast(object), index, mode);
+}
+
+
+Handle<Object> JSReceiver::DeleteProperty(Handle<JSReceiver> object,
+ Handle<Name> name,
+ DeleteMode mode) {
+ if (object->IsJSProxy()) {
+ return JSProxy::DeletePropertyWithHandler(
+ Handle<JSProxy>::cast(object), name, mode);
+ }
+ return JSObject::DeleteProperty(Handle<JSObject>::cast(object), name, mode);
+}
+
+
+bool JSObject::ReferencesObjectFromElements(FixedArray* elements,
+ ElementsKind kind,
+ Object* object) {
+ ASSERT(IsFastObjectElementsKind(kind) ||
+ kind == DICTIONARY_ELEMENTS);
+ if (IsFastObjectElementsKind(kind)) {
+ int length = IsJSArray()
+ ? Smi::cast(JSArray::cast(this)->length())->value()
+ : elements->length();
+ for (int i = 0; i < length; ++i) {
+ Object* element = elements->get(i);
+ if (!element->IsTheHole() && element == object) return true;
+ }
+ } else {
+ Object* key =
+ SeededNumberDictionary::cast(elements)->SlowReverseLookup(object);
+ if (!key->IsUndefined()) return true;
+ }
+ return false;
+}
+
+
+// Check whether this object references another object.
+bool JSObject::ReferencesObject(Object* obj) {
+ Map* map_of_this = map();
+ Heap* heap = GetHeap();
+ DisallowHeapAllocation no_allocation;
+
+ // Is the object the constructor for this object?
+ if (map_of_this->constructor() == obj) {
+ return true;
+ }
+
+ // Is the object the prototype for this object?
+ if (map_of_this->prototype() == obj) {
+ return true;
+ }
+
+ // Check if the object is among the named properties.
+ Object* key = SlowReverseLookup(obj);
+ if (!key->IsUndefined()) {
+ return true;
+ }
+
+ // Check if the object is among the indexed properties.
+ ElementsKind kind = GetElementsKind();
+ switch (kind) {
+ case EXTERNAL_PIXEL_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 FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ // Raw pixels and external arrays do not reference other
+ // objects.
+ break;
+ case FAST_SMI_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ break;
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case DICTIONARY_ELEMENTS: {
+ FixedArray* elements = FixedArray::cast(this->elements());
+ if (ReferencesObjectFromElements(elements, kind, obj)) return true;
+ break;
+ }
+ case NON_STRICT_ARGUMENTS_ELEMENTS: {
+ FixedArray* parameter_map = FixedArray::cast(elements());
+ // Check the mapped parameters.
+ int length = parameter_map->length();
+ for (int i = 2; i < length; ++i) {
+ Object* value = parameter_map->get(i);
+ if (!value->IsTheHole() && value == obj) return true;
+ }
+ // Check the arguments.
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ kind = arguments->IsDictionary() ? DICTIONARY_ELEMENTS :
+ FAST_HOLEY_ELEMENTS;
+ if (ReferencesObjectFromElements(arguments, kind, obj)) return true;
+ break;
+ }
+ }
+
+ // For functions check the context.
+ if (IsJSFunction()) {
+ // Get the constructor function for arguments array.
+ JSObject* arguments_boilerplate =
+ heap->isolate()->context()->native_context()->
+ arguments_boilerplate();
+ JSFunction* arguments_function =
+ JSFunction::cast(arguments_boilerplate->map()->constructor());
+
+ // Get the context and don't check if it is the native context.
+ JSFunction* f = JSFunction::cast(this);
+ Context* context = f->context();
+ if (context->IsNativeContext()) {
+ return false;
+ }
+
+ // Check the non-special context slots.
+ for (int i = Context::MIN_CONTEXT_SLOTS; i < context->length(); i++) {
+ // Only check JS objects.
+ if (context->get(i)->IsJSObject()) {
+ JSObject* ctxobj = JSObject::cast(context->get(i));
+ // If it is an arguments array check the content.
+ if (ctxobj->map()->constructor() == arguments_function) {
+ if (ctxobj->ReferencesObject(obj)) {
+ return true;
+ }
+ } else if (ctxobj == obj) {
+ return true;
+ }
+ }
+ }
+
+ // Check the context extension (if any) if it can have references.
+ if (context->has_extension() && !context->IsCatchContext()) {
+ return JSObject::cast(context->extension())->ReferencesObject(obj);
+ }
+ }
+
+ // No references to object.
+ return false;
+}
+
+
+Handle<Object> JSObject::PreventExtensions(Handle<JSObject> object) {
+ CALL_HEAP_FUNCTION(object->GetIsolate(), object->PreventExtensions(), Object);
+}
+
+
+MaybeObject* JSObject::PreventExtensions() {
+ Isolate* isolate = GetIsolate();
+ if (IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(this,
+ isolate->heap()->undefined_value(),
+ v8::ACCESS_KEYS)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_KEYS);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return isolate->heap()->false_value();
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return this;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->PreventExtensions();
+ }
+
+ // It's not possible to seal objects with external array elements
+ if (HasExternalArrayElements()) {
+ HandleScope scope(isolate);
+ Handle<Object> object(this, isolate);
+ Handle<Object> error =
+ isolate->factory()->NewTypeError(
+ "cant_prevent_ext_external_array_elements",
+ HandleVector(&object, 1));
+ return isolate->Throw(*error);
+ }
+
+ // If there are fast elements we normalize.
+ SeededNumberDictionary* dictionary = NULL;
+ { MaybeObject* maybe = NormalizeElements();
+ if (!maybe->To<SeededNumberDictionary>(&dictionary)) return maybe;
+ }
+ ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
+ // Make sure that we never go back to fast case.
+ dictionary->set_requires_slow_elements();
+
+ // Do a map transition, other objects with this map may still
+ // be extensible.
+ // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
+ Map* new_map;
+ MaybeObject* maybe = map()->Copy();
+ if (!maybe->To(&new_map)) return maybe;
+
+ new_map->set_is_extensible(false);
+ set_map(new_map);
+ ASSERT(!map()->is_extensible());
+ return new_map;
+}
+
+
+template<typename Dictionary>
+static void FreezeDictionary(Dictionary* dictionary) {
+ int capacity = dictionary->Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Object* k = dictionary->KeyAt(i);
+ if (dictionary->IsKey(k)) {
+ PropertyDetails details = dictionary->DetailsAt(i);
+ int attrs = DONT_DELETE;
+ // READ_ONLY is an invalid attribute for JS setters/getters.
+ if (details.type() != CALLBACKS ||
+ !dictionary->ValueAt(i)->IsAccessorPair()) {
+ attrs |= READ_ONLY;
+ }
+ details = details.CopyAddAttributes(
+ static_cast<PropertyAttributes>(attrs));
+ dictionary->DetailsAtPut(i, details);
+ }
+ }
+}
+
+
+MUST_USE_RESULT MaybeObject* JSObject::Freeze(Isolate* isolate) {
+ // Freezing non-strict arguments should be handled elsewhere.
+ ASSERT(!HasNonStrictArgumentsElements());
+
+ Heap* heap = isolate->heap();
+
+ if (map()->is_frozen()) return this;
+
+ if (IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(this,
+ heap->undefined_value(),
+ v8::ACCESS_KEYS)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_KEYS);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return heap->false_value();
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return this;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->Freeze(isolate);
+ }
+
+ // It's not possible to freeze objects with external array elements
+ if (HasExternalArrayElements()) {
+ HandleScope scope(isolate);
+ Handle<Object> object(this, isolate);
+ Handle<Object> error =
+ isolate->factory()->NewTypeError(
+ "cant_prevent_ext_external_array_elements",
+ HandleVector(&object, 1));
+ return isolate->Throw(*error);
+ }
+
+ SeededNumberDictionary* new_element_dictionary = NULL;
+ if (!elements()->IsDictionary()) {
+ int length = IsJSArray()
+ ? Smi::cast(JSArray::cast(this)->length())->value()
+ : elements()->length();
+ if (length > 0) {
+ int capacity = 0;
+ int used = 0;
+ GetElementsCapacityAndUsage(&capacity, &used);
+ MaybeObject* maybe_dict = SeededNumberDictionary::Allocate(heap, used);
+ if (!maybe_dict->To(&new_element_dictionary)) return maybe_dict;
+
+ // Move elements to a dictionary; avoid calling NormalizeElements to avoid
+ // unnecessary transitions.
+ maybe_dict = CopyFastElementsToDictionary(isolate, elements(), length,
+ new_element_dictionary);
+ if (!maybe_dict->To(&new_element_dictionary)) return maybe_dict;
+ } else {
+ // No existing elements, use a pre-allocated empty backing store
+ new_element_dictionary = heap->empty_slow_element_dictionary();
+ }
+ }
+
+ LookupResult result(isolate);
+ map()->LookupTransition(this, heap->frozen_symbol(), &result);
+ if (result.IsTransition()) {
+ Map* transition_map = result.GetTransitionTarget();
+ ASSERT(transition_map->has_dictionary_elements());
+ ASSERT(transition_map->is_frozen());
+ ASSERT(!transition_map->is_extensible());
+ set_map(transition_map);
+ } else if (HasFastProperties() && map()->CanHaveMoreTransitions()) {
+ // Create a new descriptor array with fully-frozen properties
+ int num_descriptors = map()->NumberOfOwnDescriptors();
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors =
+ map()->instance_descriptors()->CopyUpToAddAttributes(num_descriptors,
+ FROZEN);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyReplaceDescriptors(
+ new_descriptors, INSERT_TRANSITION, heap->frozen_symbol());
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ new_map->freeze();
+ new_map->set_is_extensible(false);
+ new_map->set_elements_kind(DICTIONARY_ELEMENTS);
+ set_map(new_map);
+ } else {
+ // Slow path: need to normalize properties for safety
+ MaybeObject* maybe = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (maybe->IsFailure()) return maybe;
+
+ // Create a new map, since other objects with this map may be extensible.
+ // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps.
+ Map* new_map;
+ MaybeObject* maybe_copy = map()->Copy();
+ if (!maybe_copy->To(&new_map)) return maybe_copy;
+ new_map->freeze();
+ new_map->set_is_extensible(false);
+ new_map->set_elements_kind(DICTIONARY_ELEMENTS);
+ set_map(new_map);
+
+ // Freeze dictionary-mode properties
+ FreezeDictionary(property_dictionary());
+ }
+
+ ASSERT(map()->has_dictionary_elements());
+ if (new_element_dictionary != NULL) {
+ set_elements(new_element_dictionary);
+ }
+
+ if (elements() != heap->empty_slow_element_dictionary()) {
+ SeededNumberDictionary* dictionary = element_dictionary();
+ // Make sure we never go back to the fast case
+ dictionary->set_requires_slow_elements();
+ // Freeze all elements in the dictionary
+ FreezeDictionary(dictionary);
+ }
+
+ return this;
+}
+
+
+MUST_USE_RESULT MaybeObject* JSObject::SetObserved(Isolate* isolate) {
+ if (map()->is_observed())
+ return isolate->heap()->undefined_value();
+
+ Heap* heap = isolate->heap();
+
+ if (!HasExternalArrayElements()) {
+ // Go to dictionary mode, so that we don't skip map checks.
+ MaybeObject* maybe = NormalizeElements();
+ if (maybe->IsFailure()) return maybe;
+ ASSERT(!HasFastElements());
+ }
+
+ LookupResult result(isolate);
+ map()->LookupTransition(this, heap->observed_symbol(), &result);
+
+ Map* new_map;
+ if (result.IsTransition()) {
+ new_map = result.GetTransitionTarget();
+ ASSERT(new_map->is_observed());
+ } else if (map()->CanHaveMoreTransitions()) {
+ MaybeObject* maybe_new_map = map()->CopyForObserved();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ } else {
+ MaybeObject* maybe_copy = map()->Copy();
+ if (!maybe_copy->To(&new_map)) return maybe_copy;
+ new_map->set_is_observed(true);
+ }
+ set_map(new_map);
+
+ return heap->undefined_value();
+}
+
+
+MUST_USE_RESULT MaybeObject* JSObject::DeepCopy(Isolate* isolate) {
+ StackLimitCheck check(isolate);
+ if (check.HasOverflowed()) return isolate->StackOverflow();
+
+ if (map()->is_deprecated()) {
+ MaybeObject* maybe_failure = MigrateInstance();
+ if (maybe_failure->IsFailure()) return maybe_failure;
+ }
+
+ Heap* heap = isolate->heap();
+ Object* result;
+ { MaybeObject* maybe_result = heap->CopyJSObject(this);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ JSObject* copy = JSObject::cast(result);
+
+ // Deep copy local properties.
+ if (copy->HasFastProperties()) {
+ DescriptorArray* descriptors = copy->map()->instance_descriptors();
+ int limit = copy->map()->NumberOfOwnDescriptors();
+ for (int i = 0; i < limit; i++) {
+ PropertyDetails details = descriptors->GetDetails(i);
+ if (details.type() != FIELD) continue;
+ int index = descriptors->GetFieldIndex(i);
+ Object* value = RawFastPropertyAt(index);
+ if (value->IsJSObject()) {
+ JSObject* js_object = JSObject::cast(value);
+ MaybeObject* maybe_copy = js_object->DeepCopy(isolate);
+ if (!maybe_copy->To(&value)) return maybe_copy;
+ } else {
+ Representation representation = details.representation();
+ MaybeObject* maybe_storage =
+ value->AllocateNewStorageFor(heap, representation);
+ if (!maybe_storage->To(&value)) return maybe_storage;
+ }
+ copy->FastPropertyAtPut(index, value);
+ }
+ } else {
+ { MaybeObject* maybe_result =
+ heap->AllocateFixedArray(copy->NumberOfLocalProperties());
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ FixedArray* names = FixedArray::cast(result);
+ copy->GetLocalPropertyNames(names, 0);
+ for (int i = 0; i < names->length(); i++) {
+ ASSERT(names->get(i)->IsString());
+ String* key_string = String::cast(names->get(i));
+ PropertyAttributes attributes =
+ copy->GetLocalPropertyAttribute(key_string);
+ // Only deep copy fields from the object literal expression.
+ // In particular, don't try to copy the length attribute of
+ // an array.
+ if (attributes != NONE) continue;
+ Object* value =
+ copy->GetProperty(key_string, &attributes)->ToObjectUnchecked();
+ if (value->IsJSObject()) {
+ JSObject* js_object = JSObject::cast(value);
+ { MaybeObject* maybe_result = js_object->DeepCopy(isolate);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ { MaybeObject* maybe_result =
+ // Creating object copy for literals. No strict mode needed.
+ copy->SetProperty(key_string, result, NONE, kNonStrictMode);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ }
+ }
+ }
+
+ // Deep copy local elements.
+ // Pixel elements cannot be created using an object literal.
+ ASSERT(!copy->HasExternalArrayElements());
+ switch (copy->GetElementsKind()) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS: {
+ FixedArray* elements = FixedArray::cast(copy->elements());
+ if (elements->map() == heap->fixed_cow_array_map()) {
+ isolate->counters()->cow_arrays_created_runtime()->Increment();
+#ifdef DEBUG
+ for (int i = 0; i < elements->length(); i++) {
+ ASSERT(!elements->get(i)->IsJSObject());
+ }
+#endif
+ } else {
+ for (int i = 0; i < elements->length(); i++) {
+ Object* value = elements->get(i);
+ ASSERT(value->IsSmi() ||
+ value->IsTheHole() ||
+ (IsFastObjectElementsKind(copy->GetElementsKind())));
+ if (value->IsJSObject()) {
+ JSObject* js_object = JSObject::cast(value);
+ { MaybeObject* maybe_result = js_object->DeepCopy(isolate);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ elements->set(i, result);
+ }
+ }
+ }
+ break;
+ }
+ case DICTIONARY_ELEMENTS: {
+ SeededNumberDictionary* element_dictionary = copy->element_dictionary();
+ int capacity = element_dictionary->Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Object* k = element_dictionary->KeyAt(i);
+ if (element_dictionary->IsKey(k)) {
+ Object* value = element_dictionary->ValueAt(i);
+ if (value->IsJSObject()) {
+ JSObject* js_object = JSObject::cast(value);
+ { MaybeObject* maybe_result = js_object->DeepCopy(isolate);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ element_dictionary->ValueAtPut(i, result);
+ }
+ }
+ }
+ break;
+ }
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNIMPLEMENTED();
+ break;
+ case EXTERNAL_PIXEL_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 FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ // No contained objects, nothing to do.
+ break;
+ }
+ return copy;
+}
+
+
+// Tests for the fast common case for property enumeration:
+// - 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 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);
+ int enum_length = curr->map()->EnumLength();
+ if (enum_length == Map::kInvalidEnumCache) return false;
+ ASSERT(!curr->HasNamedInterceptor());
+ ASSERT(!curr->HasIndexedInterceptor());
+ ASSERT(!curr->IsAccessCheckNeeded());
+ if (curr->NumberOfEnumElements() > 0) return false;
+ if (curr != this && enum_length != 0) return false;
+ }
+ return true;
+}
+
+
+int Map::NumberOfDescribedProperties(DescriptorFlag which,
+ PropertyAttributes filter) {
+ int result = 0;
+ DescriptorArray* descs = instance_descriptors();
+ int limit = which == ALL_DESCRIPTORS
+ ? descs->number_of_descriptors()
+ : NumberOfOwnDescriptors();
+ for (int i = 0; i < limit; i++) {
+ if ((descs->GetDetails(i).attributes() & filter) == 0 &&
+ ((filter & SYMBOLIC) == 0 || !descs->GetKey(i)->IsSymbol())) {
+ result++;
+ }
+ }
+ return result;
+}
+
+
+int Map::NextFreePropertyIndex() {
+ int max_index = -1;
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ DescriptorArray* descs = instance_descriptors();
+ for (int i = 0; i < number_of_own_descriptors; i++) {
+ if (descs->GetType(i) == FIELD) {
+ int current_index = descs->GetFieldIndex(i);
+ if (current_index > max_index) max_index = current_index;
+ }
+ }
+ return max_index + 1;
+}
+
+
+AccessorDescriptor* Map::FindAccessor(Name* name) {
+ DescriptorArray* descs = instance_descriptors();
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ for (int i = 0; i < number_of_own_descriptors; i++) {
+ if (descs->GetType(i) == CALLBACKS && name->Equals(descs->GetKey(i))) {
+ return descs->GetCallbacks(i);
+ }
+ }
+ return NULL;
+}
+
+
+void JSReceiver::LocalLookup(
+ Name* name, LookupResult* result, bool search_hidden_prototypes) {
+ ASSERT(name->IsName());
+
+ Heap* heap = GetHeap();
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return result->NotFound();
+ ASSERT(proto->IsJSGlobalObject());
+ return JSReceiver::cast(proto)->LocalLookup(
+ name, result, search_hidden_prototypes);
+ }
+
+ 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 (IsAccessCheckNeeded()) {
+ result->DisallowCaching();
+ }
+
+ JSObject* js_object = JSObject::cast(this);
+
+ // Check for lookup interceptor except when bootstrapping.
+ if (js_object->HasNamedInterceptor() &&
+ !heap->isolate()->bootstrapper()->IsActive()) {
+ result->InterceptorResult(js_object);
+ return;
+ }
+
+ js_object->LocalLookupRealNamedProperty(name, result);
+ if (result->IsFound() || !search_hidden_prototypes) return;
+
+ Object* proto = js_object->GetPrototype();
+ if (!proto->IsJSReceiver()) return;
+ JSReceiver* receiver = JSReceiver::cast(proto);
+ if (receiver->map()->is_hidden_prototype()) {
+ receiver->LocalLookup(name, result, search_hidden_prototypes);
+ }
+}
+
+
+void JSReceiver::Lookup(Name* name, LookupResult* result) {
+ // Ecma-262 3rd 8.6.2.4
+ Heap* heap = GetHeap();
+ for (Object* current = this;
+ current != heap->null_value();
+ current = JSObject::cast(current)->GetPrototype()) {
+ JSReceiver::cast(current)->LocalLookup(name, result, false);
+ if (result->IsFound()) return;
+ }
+ result->NotFound();
+}
+
+
+// Search object and its prototype chain for callback properties.
+void JSObject::LookupCallbackProperty(Name* name, LookupResult* result) {
+ Heap* heap = GetHeap();
+ for (Object* current = this;
+ current != heap->null_value() && current->IsJSObject();
+ current = JSObject::cast(current)->GetPrototype()) {
+ JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
+ if (result->IsPropertyCallbacks()) return;
+ }
+ result->NotFound();
+}
+
+
+// Try to update an accessor in an elements dictionary. Return true if the
+// update succeeded, and false otherwise.
+static bool UpdateGetterSetterInDictionary(
+ SeededNumberDictionary* dictionary,
+ uint32_t index,
+ Object* getter,
+ Object* setter,
+ PropertyAttributes attributes) {
+ int entry = dictionary->FindEntry(index);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ Object* result = dictionary->ValueAt(entry);
+ PropertyDetails details = dictionary->DetailsAt(entry);
+ if (details.type() == CALLBACKS && result->IsAccessorPair()) {
+ ASSERT(!details.IsDontDelete());
+ if (details.attributes() != attributes) {
+ dictionary->DetailsAtPut(
+ entry,
+ PropertyDetails(attributes, CALLBACKS, index));
+ }
+ AccessorPair::cast(result)->SetComponents(getter, setter);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void JSObject::DefineElementAccessor(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes) {
+ switch (object->GetElementsKind()) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ break;
+ case EXTERNAL_PIXEL_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:
+ // Ignore getters and setters on pixel and external array elements.
+ return;
+ case DICTIONARY_ELEMENTS:
+ if (UpdateGetterSetterInDictionary(object->element_dictionary(),
+ index,
+ *getter,
+ *setter,
+ attributes)) {
+ return;
+ }
+ break;
+ case NON_STRICT_ARGUMENTS_ELEMENTS: {
+ // Ascertain whether we have read-only properties or an existing
+ // getter/setter pair in an arguments elements dictionary backing
+ // store.
+ FixedArray* parameter_map = FixedArray::cast(object->elements());
+ uint32_t length = parameter_map->length();
+ Object* probe =
+ index < (length - 2) ? parameter_map->get(index + 2) : NULL;
+ if (probe == NULL || probe->IsTheHole()) {
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ if (arguments->IsDictionary()) {
+ SeededNumberDictionary* dictionary =
+ SeededNumberDictionary::cast(arguments);
+ if (UpdateGetterSetterInDictionary(dictionary,
+ index,
+ *getter,
+ *setter,
+ attributes)) {
+ return;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ Isolate* isolate = object->GetIsolate();
+ Handle<AccessorPair> accessors = isolate->factory()->NewAccessorPair();
+ accessors->SetComponents(*getter, *setter);
+
+ CALL_HEAP_FUNCTION_VOID(
+ isolate, object->SetElementCallback(index, *accessors, attributes));
+}
+
+
+Handle<AccessorPair> JSObject::CreateAccessorPairFor(Handle<JSObject> object,
+ Handle<Name> name) {
+ Isolate* isolate = object->GetIsolate();
+ LookupResult result(isolate);
+ object->LocalLookupRealNamedProperty(*name, &result);
+ if (result.IsPropertyCallbacks()) {
+ // Note that the result can actually have IsDontDelete() == true when we
+ // e.g. have to fall back to the slow case while adding a setter after
+ // successfully reusing a map transition for a getter. Nevertheless, this is
+ // OK, because the assertion only holds for the whole addition of both
+ // accessors, not for the addition of each part. See first comment in
+ // DefinePropertyAccessor below.
+ Object* obj = result.GetCallbackObject();
+ if (obj->IsAccessorPair()) {
+ return AccessorPair::Copy(handle(AccessorPair::cast(obj), isolate));
+ }
+ }
+ return isolate->factory()->NewAccessorPair();
+}
+
+
+void JSObject::DefinePropertyAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes) {
+ // We could assert that the property is configurable here, but we would need
+ // to do a lookup, which seems to be a bit of overkill.
+ bool only_attribute_changes = getter->IsNull() && setter->IsNull();
+ if (object->HasFastProperties() && !only_attribute_changes &&
+ (object->map()->NumberOfOwnDescriptors() <
+ DescriptorArray::kMaxNumberOfDescriptors)) {
+ bool getterOk = getter->IsNull() ||
+ DefineFastAccessor(object, name, ACCESSOR_GETTER, getter, attributes);
+ bool setterOk = !getterOk || setter->IsNull() ||
+ DefineFastAccessor(object, name, ACCESSOR_SETTER, setter, attributes);
+ if (getterOk && setterOk) return;
+ }
+
+ Handle<AccessorPair> accessors = CreateAccessorPairFor(object, name);
+ accessors->SetComponents(*getter, *setter);
+
+ CALL_HEAP_FUNCTION_VOID(
+ object->GetIsolate(),
+ object->SetPropertyCallback(*name, *accessors, attributes));
+}
+
+
+bool JSObject::CanSetCallback(Name* name) {
+ 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 its prototype chain.
+ // This mechanism is needed for instance in a browser setting, where
+ // certain accessors such as window.location should not be allowed
+ // to be overwritten because allowing overwriting could potentially
+ // cause security problems.
+ LookupResult callback_result(GetIsolate());
+ LookupCallbackProperty(name, &callback_result);
+ if (callback_result.IsFound()) {
+ Object* obj = callback_result.GetCallbackObject();
+ if (obj->IsAccessorInfo() &&
+ AccessorInfo::cast(obj)->prohibits_overwriting()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+MaybeObject* JSObject::SetElementCallback(uint32_t index,
+ Object* structure,
+ PropertyAttributes attributes) {
+ PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0);
+
+ // Normalize elements to make this operation simple.
+ SeededNumberDictionary* dictionary;
+ { MaybeObject* maybe_dictionary = NormalizeElements();
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+ }
+ ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
+
+ // Update the dictionary with the new CALLBACKS property.
+ { MaybeObject* maybe_dictionary = dictionary->Set(index, structure, details);
+ if (!maybe_dictionary->To(&dictionary)) return maybe_dictionary;
+ }
+
+ dictionary->set_requires_slow_elements();
+ // Update the dictionary backing store on the object.
+ if (elements()->map() == GetHeap()->non_strict_arguments_elements_map()) {
+ // Also delete any parameter alias.
+ //
+ // TODO(kmillikin): when deleting the last parameter alias we could
+ // switch to a direct backing store without the parameter map. This
+ // would allow GC of the context.
+ FixedArray* parameter_map = FixedArray::cast(elements());
+ if (index < static_cast<uint32_t>(parameter_map->length()) - 2) {
+ parameter_map->set(index + 2, GetHeap()->the_hole_value());
+ }
+ parameter_map->set(1, dictionary);
+ } else {
+ set_elements(dictionary);
+ }
+
+ return GetHeap()->undefined_value();
+}
+
+
+MaybeObject* JSObject::SetPropertyCallback(Name* name,
+ Object* structure,
+ PropertyAttributes attributes) {
+ // Normalize object to make this operation simple.
+ MaybeObject* maybe_ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (maybe_ok->IsFailure()) return maybe_ok;
+
+ // For the global object allocate a new map to invalidate the global inline
+ // caches which have a global property cell reference directly in the code.
+ if (IsGlobalObject()) {
+ Map* new_map;
+ MaybeObject* maybe_new_map = map()->CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ ASSERT(new_map->is_dictionary_map());
+
+ set_map(new_map);
+ // When running crankshaft, changing the map is not enough. We
+ // need to deoptimize all functions that rely on this global
+ // object.
+ Deoptimizer::DeoptimizeGlobalObject(this);
+ }
+
+ // Update the dictionary with the new CALLBACKS property.
+ PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0);
+ maybe_ok = SetNormalizedProperty(name, structure, details);
+ if (maybe_ok->IsFailure()) return maybe_ok;
+
+ return GetHeap()->undefined_value();
+}
+
+
+void JSObject::DefineAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes) {
+ Isolate* isolate = object->GetIsolate();
+ // Check access rights if needed.
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(*object, *name, v8::ACCESS_SET)) {
+ isolate->ReportFailedAccessCheck(*object, v8::ACCESS_SET);
+ return;
+ }
+
+ if (object->IsJSGlobalProxy()) {
+ Handle<Object> proto(object->GetPrototype(), isolate);
+ if (proto->IsNull()) return;
+ ASSERT(proto->IsJSGlobalObject());
+ DefineAccessor(
+ Handle<JSObject>::cast(proto), name, getter, setter, attributes);
+ return;
+ }
+
+ // Make sure that the top context does not change when doing callbacks or
+ // interceptor calls.
+ AssertNoContextChange ncc;
+
+ // Try to flatten before operating on the string.
+ if (name->IsString()) String::cast(*name)->TryFlatten();
+
+ if (!object->CanSetCallback(*name)) return;
+
+ uint32_t index = 0;
+ bool is_element = name->AsArrayIndex(&index);
+
+ Handle<Object> old_value = isolate->factory()->the_hole_value();
+ bool is_observed = FLAG_harmony_observation && object->map()->is_observed();
+ bool preexists = false;
+ if (is_observed) {
+ if (is_element) {
+ preexists = object->HasLocalElement(index);
+ if (preexists && object->GetLocalElementAccessorPair(index) == NULL) {
+ old_value = Object::GetElement(object, index);
+ }
+ } else {
+ LookupResult lookup(isolate);
+ object->LocalLookup(*name, &lookup, true);
+ preexists = lookup.IsProperty();
+ if (preexists && lookup.IsDataProperty()) {
+ old_value = Object::GetProperty(object, name);
+ }
+ }
+ }
+
+ if (is_element) {
+ DefineElementAccessor(object, index, getter, setter, attributes);
+ } else {
+ DefinePropertyAccessor(object, name, getter, setter, attributes);
+ }
+
+ if (is_observed) {
+ const char* type = preexists ? "reconfigured" : "new";
+ EnqueueChangeRecord(object, type, name, old_value);
+ }
+}
+
+
+static bool TryAccessorTransition(JSObject* self,
+ Map* transitioned_map,
+ int target_descriptor,
+ AccessorComponent component,
+ Object* accessor,
+ PropertyAttributes attributes) {
+ DescriptorArray* descs = transitioned_map->instance_descriptors();
+ PropertyDetails details = descs->GetDetails(target_descriptor);
+
+ // If the transition target was not callbacks, fall back to the slow case.
+ if (details.type() != CALLBACKS) return false;
+ Object* descriptor = descs->GetCallbacksObject(target_descriptor);
+ if (!descriptor->IsAccessorPair()) return false;
+
+ Object* target_accessor = AccessorPair::cast(descriptor)->get(component);
+ PropertyAttributes target_attributes = details.attributes();
+
+ // Reuse transition if adding same accessor with same attributes.
+ if (target_accessor == accessor && target_attributes == attributes) {
+ self->set_map(transitioned_map);
+ return true;
+ }
+
+ // If either not the same accessor, or not the same attributes, fall back to
+ // the slow case.
+ return false;
+}
+
+
+static MaybeObject* CopyInsertDescriptor(Map* map,
+ Name* name,
+ AccessorPair* accessors,
+ PropertyAttributes attributes) {
+ CallbacksDescriptor new_accessors_desc(name, accessors, attributes);
+ return map->CopyInsertDescriptor(&new_accessors_desc, INSERT_TRANSITION);
+}
+
+
+static Handle<Map> CopyInsertDescriptor(Handle<Map> map,
+ Handle<Name> name,
+ Handle<AccessorPair> accessors,
+ PropertyAttributes attributes) {
+ CALL_HEAP_FUNCTION(map->GetIsolate(),
+ CopyInsertDescriptor(*map, *name, *accessors, attributes),
+ Map);
+}
+
+
+bool JSObject::DefineFastAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ AccessorComponent component,
+ Handle<Object> accessor,
+ PropertyAttributes attributes) {
+ ASSERT(accessor->IsSpecFunction() || accessor->IsUndefined());
+ Isolate* isolate = object->GetIsolate();
+ LookupResult result(isolate);
+ object->LocalLookup(*name, &result);
+
+ if (result.IsFound() && !result.IsPropertyCallbacks()) {
+ return false;
+ }
+
+ // Return success if the same accessor with the same attributes already exist.
+ AccessorPair* source_accessors = NULL;
+ if (result.IsPropertyCallbacks()) {
+ Object* callback_value = result.GetCallbackObject();
+ if (callback_value->IsAccessorPair()) {
+ source_accessors = AccessorPair::cast(callback_value);
+ Object* entry = source_accessors->get(component);
+ if (entry == *accessor && result.GetAttributes() == attributes) {
+ return true;
+ }
+ } else {
+ return false;
+ }
+
+ int descriptor_number = result.GetDescriptorIndex();
+
+ object->map()->LookupTransition(*object, *name, &result);
+
+ if (result.IsFound()) {
+ Map* target = result.GetTransitionTarget();
+ ASSERT(target->NumberOfOwnDescriptors() ==
+ object->map()->NumberOfOwnDescriptors());
+ // This works since descriptors are sorted in order of addition.
+ ASSERT(object->map()->instance_descriptors()->
+ GetKey(descriptor_number) == *name);
+ return TryAccessorTransition(*object, target, descriptor_number,
+ component, *accessor, attributes);
+ }
+ } else {
+ // If not, lookup a transition.
+ object->map()->LookupTransition(*object, *name, &result);
+
+ // If there is a transition, try to follow it.
+ if (result.IsFound()) {
+ Map* target = result.GetTransitionTarget();
+ int descriptor_number = target->LastAdded();
+ ASSERT(target->instance_descriptors()->GetKey(descriptor_number)
+ ->Equals(*name));
+ return TryAccessorTransition(*object, target, descriptor_number,
+ component, *accessor, attributes);
+ }
+ }
+
+ // If there is no transition yet, add a transition to the a new accessor pair
+ // containing the accessor. Allocate a new pair if there were no source
+ // accessors. Otherwise, copy the pair and modify the accessor.
+ Handle<AccessorPair> accessors = source_accessors != NULL
+ ? AccessorPair::Copy(Handle<AccessorPair>(source_accessors))
+ : isolate->factory()->NewAccessorPair();
+ accessors->set(component, *accessor);
+ Handle<Map> new_map = CopyInsertDescriptor(Handle<Map>(object->map()),
+ name, accessors, attributes);
+ object->set_map(*new_map);
+ return true;
+}
+
+
+MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
+ Isolate* isolate = GetIsolate();
+ Name* name = Name::cast(info->name());
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(this, name, v8::ACCESS_SET)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return isolate->heap()->undefined_value();
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return this;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->DefineAccessor(info);
+ }
+
+ // Make sure that the top context does not change when doing callbacks or
+ // interceptor calls.
+ AssertNoContextChange ncc;
+
+ // Try to flatten before operating on the string.
+ if (name->IsString()) String::cast(name)->TryFlatten();
+
+ if (!CanSetCallback(name)) return isolate->heap()->undefined_value();
+
+ uint32_t index = 0;
+ bool is_element = name->AsArrayIndex(&index);
+
+ if (is_element) {
+ if (IsJSArray()) return isolate->heap()->undefined_value();
+
+ // Accessors overwrite previous callbacks (cf. with getters/setters).
+ switch (GetElementsKind()) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ break;
+ case EXTERNAL_PIXEL_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:
+ // Ignore getters and setters on pixel and external array
+ // elements.
+ return isolate->heap()->undefined_value();
+ case DICTIONARY_ELEMENTS:
+ break;
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNIMPLEMENTED();
+ break;
+ }
+
+ MaybeObject* maybe_ok =
+ SetElementCallback(index, info, info->property_attributes());
+ if (maybe_ok->IsFailure()) return maybe_ok;
+ } else {
+ // Lookup the name.
+ LookupResult result(isolate);
+ LocalLookup(name, &result, true);
+ // 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).
+ if (result.IsFound() && (result.IsReadOnly() || result.IsDontDelete())) {
+ return isolate->heap()->undefined_value();
+ }
+
+ MaybeObject* maybe_ok =
+ SetPropertyCallback(name, info, info->property_attributes());
+ if (maybe_ok->IsFailure()) return maybe_ok;
+ }
+
+ return this;
+}
+
+
+MaybeObject* JSObject::LookupAccessor(Name* name, AccessorComponent component) {
+ Heap* heap = GetHeap();
+
+ // Make sure that the top context does not change when doing callbacks or
+ // interceptor calls.
+ AssertNoContextChange ncc;
+
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded() &&
+ !heap->isolate()->MayNamedAccess(this, name, v8::ACCESS_HAS)) {
+ heap->isolate()->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ RETURN_IF_SCHEDULED_EXCEPTION(heap->isolate());
+ return heap->undefined_value();
+ }
+
+ // Make the lookup and include prototypes.
+ uint32_t index = 0;
+ if (name->AsArrayIndex(&index)) {
+ for (Object* obj = this;
+ obj != heap->null_value();
+ obj = JSReceiver::cast(obj)->GetPrototype()) {
+ if (obj->IsJSObject() && JSObject::cast(obj)->HasDictionaryElements()) {
+ JSObject* js_object = JSObject::cast(obj);
+ SeededNumberDictionary* dictionary = js_object->element_dictionary();
+ int entry = dictionary->FindEntry(index);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ Object* element = dictionary->ValueAt(entry);
+ if (dictionary->DetailsAt(entry).type() == CALLBACKS &&
+ element->IsAccessorPair()) {
+ return AccessorPair::cast(element)->GetComponent(component);
+ }
+ }
+ }
+ }
+ } else {
+ for (Object* obj = this;
+ obj != heap->null_value();
+ obj = JSReceiver::cast(obj)->GetPrototype()) {
+ LookupResult result(heap->isolate());
+ JSReceiver::cast(obj)->LocalLookup(name, &result);
+ if (result.IsFound()) {
+ if (result.IsReadOnly()) return heap->undefined_value();
+ if (result.IsPropertyCallbacks()) {
+ Object* obj = result.GetCallbackObject();
+ if (obj->IsAccessorPair()) {
+ return AccessorPair::cast(obj)->GetComponent(component);
+ }
+ }
+ }
+ }
+ }
+ return heap->undefined_value();
+}
+
+
+Object* JSObject::SlowReverseLookup(Object* value) {
+ if (HasFastProperties()) {
+ int number_of_own_descriptors = map()->NumberOfOwnDescriptors();
+ DescriptorArray* descs = map()->instance_descriptors();
+ for (int i = 0; i < number_of_own_descriptors; i++) {
+ if (descs->GetType(i) == FIELD) {
+ Object* property = RawFastPropertyAt(descs->GetFieldIndex(i));
+ if (FLAG_track_double_fields &&
+ descs->GetDetails(i).representation().IsDouble()) {
+ ASSERT(property->IsHeapNumber());
+ if (value->IsNumber() && property->Number() == value->Number()) {
+ return descs->GetKey(i);
+ }
+ } else if (property == value) {
+ return descs->GetKey(i);
+ }
+ } else if (descs->GetType(i) == CONSTANT) {
+ if (descs->GetConstant(i) == value) {
+ return descs->GetKey(i);
+ }
+ }
+ }
+ return GetHeap()->undefined_value();
+ } else {
+ return property_dictionary()->SlowReverseLookup(value);
+ }
+}
+
+
+MaybeObject* Map::RawCopy(int instance_size) {
+ Map* result;
+ MaybeObject* maybe_result =
+ GetHeap()->AllocateMap(instance_type(), instance_size);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ result->set_prototype(prototype());
+ result->set_constructor(constructor());
+ result->set_bit_field(bit_field());
+ result->set_bit_field2(bit_field2());
+ int new_bit_field3 = bit_field3();
+ new_bit_field3 = OwnsDescriptors::update(new_bit_field3, true);
+ new_bit_field3 = NumberOfOwnDescriptorsBits::update(new_bit_field3, 0);
+ new_bit_field3 = EnumLengthBits::update(new_bit_field3, kInvalidEnumCache);
+ new_bit_field3 = Deprecated::update(new_bit_field3, false);
+ new_bit_field3 = IsUnstable::update(new_bit_field3, false);
+ result->set_bit_field3(new_bit_field3);
+ return result;
+}
+
+
+MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
+ NormalizedMapSharingMode sharing) {
+ int new_instance_size = instance_size();
+ if (mode == CLEAR_INOBJECT_PROPERTIES) {
+ new_instance_size -= inobject_properties() * kPointerSize;
+ }
+
+ Map* result;
+ MaybeObject* maybe_result = RawCopy(new_instance_size);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ if (mode != CLEAR_INOBJECT_PROPERTIES) {
+ result->set_inobject_properties(inobject_properties());
+ }
+
+ result->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
+ result->set_dictionary_map(true);
+ result->set_migration_target(false);
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap && result->is_shared()) {
+ result->SharedMapVerify();
+ }
+#endif
+
+ return result;
+}
+
+
+Handle<Map> Map::CopyDropDescriptors(Handle<Map> map) {
+ CALL_HEAP_FUNCTION(map->GetIsolate(), map->CopyDropDescriptors(), Map);
+}
+
+
+MaybeObject* Map::CopyDropDescriptors() {
+ Map* result;
+ MaybeObject* maybe_result = RawCopy(instance_size());
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ // Please note instance_type and instance_size are set when allocated.
+ result->set_inobject_properties(inobject_properties());
+ result->set_unused_property_fields(unused_property_fields());
+
+ result->set_pre_allocated_property_fields(pre_allocated_property_fields());
+ result->set_is_shared(false);
+ result->ClearCodeCache(GetHeap());
+ NotifyLeafMapLayoutChange();
+ return result;
+}
+
+
+MaybeObject* Map::ShareDescriptor(DescriptorArray* descriptors,
+ Descriptor* descriptor) {
+ // Sanity check. This path is only to be taken if the map owns its descriptor
+ // array, implying that its NumberOfOwnDescriptors equals the number of
+ // descriptors in the descriptor array.
+ ASSERT(NumberOfOwnDescriptors() ==
+ instance_descriptors()->number_of_descriptors());
+ Map* result;
+ MaybeObject* maybe_result = CopyDropDescriptors();
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ Name* name = descriptor->GetKey();
+
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions =
+ AddTransition(name, result, SIMPLE_TRANSITION);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+
+ int old_size = descriptors->number_of_descriptors();
+
+ DescriptorArray* new_descriptors;
+
+ if (descriptors->NumberOfSlackDescriptors() > 0) {
+ new_descriptors = descriptors;
+ new_descriptors->Append(descriptor);
+ } else {
+ // Descriptor arrays grow by 50%.
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(
+ old_size, old_size < 4 ? 1 : old_size / 2);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ DescriptorArray::WhitenessWitness witness(new_descriptors);
+
+ // Copy the descriptors, inserting a descriptor.
+ for (int i = 0; i < old_size; ++i) {
+ new_descriptors->CopyFrom(i, descriptors, i, witness);
+ }
+
+ new_descriptors->Append(descriptor, witness);
+
+ if (old_size > 0) {
+ // If the source descriptors had an enum cache we copy it. This ensures
+ // that the maps to which we push the new descriptor array back can rely
+ // on a cache always being available once it is set. If the map has more
+ // enumerated descriptors than available in the original cache, the cache
+ // will be lazily replaced by the extended cache when needed.
+ if (descriptors->HasEnumCache()) {
+ new_descriptors->CopyEnumCacheFrom(descriptors);
+ }
+
+ Map* map;
+ // Replace descriptors by new_descriptors in all maps that share it.
+ for (Object* current = GetBackPointer();
+ !current->IsUndefined();
+ current = map->GetBackPointer()) {
+ map = Map::cast(current);
+ if (map->instance_descriptors() != descriptors) break;
+ map->set_instance_descriptors(new_descriptors);
+ }
+
+ set_instance_descriptors(new_descriptors);
+ }
+ }
+
+ result->SetBackPointer(this);
+ result->InitializeDescriptors(new_descriptors);
+ ASSERT(result->NumberOfOwnDescriptors() == NumberOfOwnDescriptors() + 1);
+
+ set_transitions(transitions);
+ set_owns_descriptors(false);
+
+ return result;
+}
+
+
+MaybeObject* Map::CopyReplaceDescriptors(DescriptorArray* descriptors,
+ TransitionFlag flag,
+ Name* name,
+ SimpleTransitionFlag simple_flag) {
+ ASSERT(descriptors->IsSortedNoDuplicates());
+
+ Map* result;
+ MaybeObject* maybe_result = CopyDropDescriptors();
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ result->InitializeDescriptors(descriptors);
+
+ if (flag == INSERT_TRANSITION && CanHaveMoreTransitions()) {
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions = AddTransition(name, result, simple_flag);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ set_transitions(transitions);
+ result->SetBackPointer(this);
+ } else if (flag != OMIT_TRANSITION_KEEP_REPRESENTATIONS) {
+ descriptors->InitializeRepresentations(Representation::Tagged());
+ }
+
+ return result;
+}
+
+
+// Since this method is used to rewrite an existing transition tree, it can
+// always insert transitions without checking.
+MaybeObject* Map::CopyInstallDescriptors(int new_descriptor,
+ DescriptorArray* descriptors) {
+ ASSERT(descriptors->IsSortedNoDuplicates());
+
+ Map* result;
+ MaybeObject* maybe_result = CopyDropDescriptors();
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ result->InitializeDescriptors(descriptors);
+ result->SetNumberOfOwnDescriptors(new_descriptor + 1);
+
+ int unused_property_fields = this->unused_property_fields();
+ if (descriptors->GetDetails(new_descriptor).type() == FIELD) {
+ unused_property_fields = this->unused_property_fields() - 1;
+ if (unused_property_fields < 0) {
+ unused_property_fields += JSObject::kFieldsAdded;
+ }
+ }
+
+ result->set_unused_property_fields(unused_property_fields);
+ result->set_owns_descriptors(false);
+
+ Name* name = descriptors->GetKey(new_descriptor);
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions =
+ AddTransition(name, result, SIMPLE_TRANSITION);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+
+ set_transitions(transitions);
+ result->SetBackPointer(this);
+
+ return result;
+}
+
+
+MaybeObject* Map::CopyAsElementsKind(ElementsKind kind, TransitionFlag flag) {
+ if (flag == INSERT_TRANSITION) {
+ ASSERT(!HasElementsTransition() ||
+ ((elements_transition_map()->elements_kind() == DICTIONARY_ELEMENTS ||
+ IsExternalArrayElementsKind(
+ elements_transition_map()->elements_kind())) &&
+ (kind == DICTIONARY_ELEMENTS ||
+ IsExternalArrayElementsKind(kind))));
+ ASSERT(!IsFastElementsKind(kind) ||
+ IsMoreGeneralElementsKindTransition(elements_kind(), kind));
+ ASSERT(kind != elements_kind());
+ }
+
+ bool insert_transition =
+ flag == INSERT_TRANSITION && !HasElementsTransition();
+
+ if (insert_transition && owns_descriptors()) {
+ // In case the map owned its own descriptors, share the descriptors and
+ // transfer ownership to the new map.
+ Map* new_map;
+ MaybeObject* maybe_new_map = CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ MaybeObject* added_elements = set_elements_transition_map(new_map);
+ if (added_elements->IsFailure()) return added_elements;
+
+ new_map->set_elements_kind(kind);
+ new_map->InitializeDescriptors(instance_descriptors());
+ new_map->SetBackPointer(this);
+ set_owns_descriptors(false);
+ return new_map;
+ }
+
+ // In case the map did not own its own descriptors, a split is forced by
+ // copying the map; creating a new descriptor array cell.
+ // Create a new free-floating map only if we are not allowed to store it.
+ Map* new_map;
+ MaybeObject* maybe_new_map = Copy();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ new_map->set_elements_kind(kind);
+
+ if (insert_transition) {
+ MaybeObject* added_elements = set_elements_transition_map(new_map);
+ if (added_elements->IsFailure()) return added_elements;
+ new_map->SetBackPointer(this);
+ }
+
+ return new_map;
+}
+
+
+MaybeObject* Map::CopyForObserved() {
+ ASSERT(!is_observed());
+
+ // In case the map owned its own descriptors, share the descriptors and
+ // transfer ownership to the new map.
+ Map* new_map;
+ MaybeObject* maybe_new_map;
+ if (owns_descriptors()) {
+ maybe_new_map = CopyDropDescriptors();
+ } else {
+ maybe_new_map = Copy();
+ }
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ TransitionArray* transitions;
+ MaybeObject* maybe_transitions = AddTransition(GetHeap()->observed_symbol(),
+ new_map,
+ FULL_TRANSITION);
+ if (!maybe_transitions->To(&transitions)) return maybe_transitions;
+ set_transitions(transitions);
+
+ new_map->set_is_observed(true);
+
+ if (owns_descriptors()) {
+ new_map->InitializeDescriptors(instance_descriptors());
+ set_owns_descriptors(false);
+ }
+
+ new_map->SetBackPointer(this);
+ return new_map;
+}
+
+
+MaybeObject* Map::CopyWithPreallocatedFieldDescriptors() {
+ if (pre_allocated_property_fields() == 0) return CopyDropDescriptors();
+
+ // If the map has pre-allocated properties always start out with a descriptor
+ // array describing these properties.
+ ASSERT(constructor()->IsJSFunction());
+ JSFunction* ctor = JSFunction::cast(constructor());
+ Map* map = ctor->initial_map();
+ DescriptorArray* descriptors = map->instance_descriptors();
+
+ int number_of_own_descriptors = map->NumberOfOwnDescriptors();
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors =
+ descriptors->CopyUpTo(number_of_own_descriptors);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ return CopyReplaceDescriptors(new_descriptors, OMIT_TRANSITION);
+}
+
+
+Handle<Map> Map::Copy(Handle<Map> map) {
+ CALL_HEAP_FUNCTION(map->GetIsolate(), map->Copy(), Map);
+}
+
+
+MaybeObject* Map::Copy() {
+ DescriptorArray* descriptors = instance_descriptors();
+ DescriptorArray* new_descriptors;
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ MaybeObject* maybe_descriptors =
+ descriptors->CopyUpTo(number_of_own_descriptors);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ return CopyReplaceDescriptors(new_descriptors, OMIT_TRANSITION);
+}
+
+
+MaybeObject* Map::CopyAddDescriptor(Descriptor* descriptor,
+ TransitionFlag flag) {
+ DescriptorArray* descriptors = instance_descriptors();
+
+ // Ensure the key is unique.
+ MaybeObject* maybe_failure = descriptor->KeyToUniqueName();
+ if (maybe_failure->IsFailure()) return maybe_failure;
+
+ int old_size = NumberOfOwnDescriptors();
+ int new_size = old_size + 1;
+
+ if (flag == INSERT_TRANSITION &&
+ owns_descriptors() &&
+ CanHaveMoreTransitions()) {
+ return ShareDescriptor(descriptors, descriptor);
+ }
+
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(old_size, 1);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+
+ DescriptorArray::WhitenessWitness witness(new_descriptors);
+
+ // Copy the descriptors, inserting a descriptor.
+ for (int i = 0; i < old_size; ++i) {
+ new_descriptors->CopyFrom(i, descriptors, i, witness);
+ }
+
+ if (old_size != descriptors->number_of_descriptors()) {
+ new_descriptors->SetNumberOfDescriptors(new_size);
+ new_descriptors->Set(old_size, descriptor, witness);
+ new_descriptors->Sort();
+ } else {
+ new_descriptors->Append(descriptor, witness);
+ }
+
+ Name* key = descriptor->GetKey();
+ return CopyReplaceDescriptors(new_descriptors, flag, key, SIMPLE_TRANSITION);
+}
+
+
+MaybeObject* Map::CopyInsertDescriptor(Descriptor* descriptor,
+ TransitionFlag flag) {
+ DescriptorArray* old_descriptors = instance_descriptors();
+
+ // Ensure the key is unique.
+ MaybeObject* maybe_result = descriptor->KeyToUniqueName();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // We replace the key if it is already present.
+ int index = old_descriptors->SearchWithCache(descriptor->GetKey(), this);
+ if (index != DescriptorArray::kNotFound) {
+ return CopyReplaceDescriptor(old_descriptors, descriptor, index, flag);
+ }
+ return CopyAddDescriptor(descriptor, flag);
+}
+
+
+MaybeObject* DescriptorArray::CopyUpToAddAttributes(
+ int enumeration_index, PropertyAttributes attributes) {
+ if (enumeration_index == 0) return GetHeap()->empty_descriptor_array();
+
+ int size = enumeration_index;
+
+ DescriptorArray* descriptors;
+ MaybeObject* maybe_descriptors = Allocate(size);
+ if (!maybe_descriptors->To(&descriptors)) return maybe_descriptors;
+ DescriptorArray::WhitenessWitness witness(descriptors);
+
+ if (attributes != NONE) {
+ for (int i = 0; i < size; ++i) {
+ Object* value = GetValue(i);
+ PropertyDetails details = GetDetails(i);
+ int mask = DONT_DELETE | DONT_ENUM;
+ // READ_ONLY is an invalid attribute for JS setters/getters.
+ if (details.type() != CALLBACKS || !value->IsAccessorPair()) {
+ mask |= READ_ONLY;
+ }
+ details = details.CopyAddAttributes(
+ static_cast<PropertyAttributes>(attributes & mask));
+ Descriptor desc(GetKey(i), value, details);
+ descriptors->Set(i, &desc, witness);
+ }
+ } else {
+ for (int i = 0; i < size; ++i) {
+ descriptors->CopyFrom(i, this, i, witness);
+ }
+ }
+
+ if (number_of_descriptors() != enumeration_index) descriptors->Sort();
+
+ return descriptors;
+}
+
+
+MaybeObject* Map::CopyReplaceDescriptor(DescriptorArray* descriptors,
+ Descriptor* descriptor,
+ int insertion_index,
+ TransitionFlag flag) {
+ // Ensure the key is unique.
+ MaybeObject* maybe_failure = descriptor->KeyToUniqueName();
+ if (maybe_failure->IsFailure()) return maybe_failure;
+
+ Name* key = descriptor->GetKey();
+ ASSERT(key == descriptors->GetKey(insertion_index));
+
+ int new_size = NumberOfOwnDescriptors();
+ ASSERT(0 <= insertion_index && insertion_index < new_size);
+
+ ASSERT_LT(insertion_index, new_size);
+
+ DescriptorArray* new_descriptors;
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(new_size);
+ if (!maybe_descriptors->To(&new_descriptors)) return maybe_descriptors;
+ DescriptorArray::WhitenessWitness witness(new_descriptors);
+
+ for (int i = 0; i < new_size; ++i) {
+ if (i == insertion_index) {
+ new_descriptors->Set(i, descriptor, witness);
+ } else {
+ new_descriptors->CopyFrom(i, descriptors, i, witness);
+ }
+ }
+
+ // Re-sort if descriptors were removed.
+ if (new_size != descriptors->length()) new_descriptors->Sort();
+
+ SimpleTransitionFlag simple_flag =
+ (insertion_index == descriptors->number_of_descriptors() - 1)
+ ? SIMPLE_TRANSITION
+ : FULL_TRANSITION;
+ return CopyReplaceDescriptors(new_descriptors, flag, key, simple_flag);
+}
+
+
+void Map::UpdateCodeCache(Handle<Map> map,
+ Handle<Name> name,
+ Handle<Code> code) {
+ Isolate* isolate = map->GetIsolate();
+ CALL_HEAP_FUNCTION_VOID(isolate,
+ map->UpdateCodeCache(*name, *code));
+}
+
+
+MaybeObject* Map::UpdateCodeCache(Name* name, Code* code) {
+ ASSERT(!is_shared() || code->allowed_in_shared_map_code_cache());
+
+ // Allocate the code cache if not present.
+ if (code_cache()->IsFixedArray()) {
+ Object* result;
+ { MaybeObject* maybe_result = GetHeap()->AllocateCodeCache();
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ set_code_cache(result);
+ }
+
+ // Update the code cache.
+ return CodeCache::cast(code_cache())->Update(name, code);
+}
+
+
+Object* Map::FindInCodeCache(Name* name, Code::Flags flags) {
+ // Do a lookup if a code cache exists.
+ if (!code_cache()->IsFixedArray()) {
+ return CodeCache::cast(code_cache())->Lookup(name, flags);
+ } else {
+ return GetHeap()->undefined_value();
+ }
+}
+
+
+int Map::IndexInCodeCache(Object* name, Code* code) {
+ // Get the internal index if a code cache exists.
+ if (!code_cache()->IsFixedArray()) {
+ return CodeCache::cast(code_cache())->GetIndex(name, code);
+ }
+ return -1;
+}
+
+
+void Map::RemoveFromCodeCache(Name* name, Code* code, int index) {
+ // No GC is supposed to happen between a call to IndexInCodeCache and
+ // RemoveFromCodeCache so the code cache must be there.
+ ASSERT(!code_cache()->IsFixedArray());
+ CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
+}
+
+
+// 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(TransitionArray* transition_array)
+ : transition_array_(transition_array) { }
+
+ void Start() {
+ ASSERT(!IsIterating());
+ *TransitionArrayHeader() = Smi::FromInt(0);
+ }
+
+ bool IsIterating() {
+ return (*TransitionArrayHeader())->IsSmi();
+ }
+
+ Map* Next() {
+ ASSERT(IsIterating());
+ int index = Smi::cast(*TransitionArrayHeader())->value();
+ int number_of_transitions = transition_array_->number_of_transitions();
+ while (index < number_of_transitions) {
+ *TransitionArrayHeader() = Smi::FromInt(index + 1);
+ return transition_array_->GetTarget(index);
+ }
+
+ *TransitionArrayHeader() = transition_array_->GetHeap()->fixed_array_map();
+ return NULL;
+ }
+
+ private:
+ Object** TransitionArrayHeader() {
+ return HeapObject::RawField(transition_array_, TransitionArray::kMapOffset);
+ }
+
+ TransitionArray* transition_array_;
+};
+
+
+// An iterator over all prototype transitions, reusing the map field of the
+// underlying array while it is running.
+class IntrusivePrototypeTransitionIterator {
+ public:
+ explicit IntrusivePrototypeTransitionIterator(HeapObject* proto_trans)
+ : proto_trans_(proto_trans) { }
+
+ void Start() {
+ ASSERT(!IsIterating());
+ *Header() = Smi::FromInt(0);
+ }
+
+ bool IsIterating() {
+ return (*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:
+ Object** Header() {
+ return HeapObject::RawField(proto_trans_, FixedArray::kMapOffset);
+ }
+
+ int NumberOfTransitions() {
+ FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
+ Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset);
+ return Smi::cast(num)->value();
+ }
+
+ Map* GetTransition(int transitionNumber) {
+ FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
+ return Map::cast(proto_trans->get(IndexFor(transitionNumber)));
+ }
+
+ int IndexFor(int transitionNumber) {
+ return Map::kProtoTransitionHeaderSize +
+ Map::kProtoTransitionMapOffset +
+ transitionNumber * Map::kProtoTransitionElementsPerEntry;
+ }
+
+ HeapObject* 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() {
+ if (HasTransitionArray()) {
+ if (HasPrototypeTransitions()) {
+ IntrusivePrototypeTransitionIterator(GetPrototypeTransitions()).Start();
+ }
+
+ IntrusiveMapTransitionIterator(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() {
+ TransitionArray* transition_array = unchecked_transition_array();
+ if (!transition_array->map()->IsSmi() &&
+ !transition_array->IsTransitionArray()) {
+ return NULL;
+ }
+
+ if (transition_array->HasPrototypeTransitions()) {
+ HeapObject* proto_transitions =
+ transition_array->UncheckedPrototypeTransitions();
+ IntrusivePrototypeTransitionIterator proto_iterator(proto_transitions);
+ if (proto_iterator.IsIterating()) {
+ Map* next = proto_iterator.Next();
+ if (next != NULL) return static_cast<TraversableMap*>(next);
+ }
+ }
+
+ IntrusiveMapTransitionIterator transition_iterator(transition_array);
+ if (transition_iterator.IsIterating()) {
+ Map* next = transition_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;
+ }
+ }
+}
+
+
+MaybeObject* CodeCache::Update(Name* name, Code* code) {
+ // The number of monomorphic stubs for normal load/store/call IC's can grow to
+ // a large number and therefore they need to go into a hash table. They are
+ // used to load global properties from cells.
+ if (code->type() == Code::NORMAL) {
+ // Make sure that a hash table is allocated for the normal load code cache.
+ if (normal_type_cache()->IsUndefined()) {
+ Object* result;
+ { MaybeObject* maybe_result =
+ CodeCacheHashTable::Allocate(GetHeap(),
+ CodeCacheHashTable::kInitialSize);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ set_normal_type_cache(result);
+ }
+ return UpdateNormalTypeCache(name, code);
+ } else {
+ ASSERT(default_cache()->IsFixedArray());
+ return UpdateDefaultCache(name, code);
+ }
+}
+
+
+MaybeObject* CodeCache::UpdateDefaultCache(Name* name, Code* code) {
+ // When updating the default code cache we disregard the type encoded in the
+ // flags. This allows call constant stubs to overwrite call field
+ // stubs, etc.
+ Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
+
+ // First check whether we can update existing code cache without
+ // extending it.
+ FixedArray* cache = default_cache();
+ int length = cache->length();
+ int deleted_index = -1;
+ for (int i = 0; i < length; i += kCodeCacheEntrySize) {
+ Object* key = cache->get(i);
+ if (key->IsNull()) {
+ if (deleted_index < 0) deleted_index = i;
+ continue;
+ }
+ if (key->IsUndefined()) {
+ if (deleted_index >= 0) i = deleted_index;
+ cache->set(i + kCodeCacheEntryNameOffset, name);
+ cache->set(i + kCodeCacheEntryCodeOffset, code);
+ return this;
+ }
+ if (name->Equals(Name::cast(key))) {
+ Code::Flags found =
+ Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
+ if (Code::RemoveTypeFromFlags(found) == flags) {
+ cache->set(i + kCodeCacheEntryCodeOffset, code);
+ return this;
+ }
+ }
+ }
+
+ // Reached the end of the code cache. If there were deleted
+ // elements, reuse the space for the first of them.
+ if (deleted_index >= 0) {
+ cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
+ cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
+ return this;
+ }
+
+ // Extend the code cache with some new entries (at least one). Must be a
+ // multiple of the entry size.
+ int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
+ new_length = new_length - new_length % kCodeCacheEntrySize;
+ ASSERT((new_length % kCodeCacheEntrySize) == 0);
+ Object* result;
+ { MaybeObject* maybe_result = cache->CopySize(new_length);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // Add the (name, code) pair to the new cache.
+ cache = FixedArray::cast(result);
+ cache->set(length + kCodeCacheEntryNameOffset, name);
+ cache->set(length + kCodeCacheEntryCodeOffset, code);
+ set_default_cache(cache);
+ return this;
+}
+
+
+MaybeObject* CodeCache::UpdateNormalTypeCache(Name* name, Code* code) {
+ // Adding a new entry can cause a new cache to be allocated.
+ CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
+ Object* new_cache;
+ { MaybeObject* maybe_new_cache = cache->Put(name, code);
+ if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
+ }
+ set_normal_type_cache(new_cache);
+ return this;
+}
+
+
+Object* CodeCache::Lookup(Name* name, Code::Flags flags) {
+ if (Code::ExtractTypeFromFlags(flags) == Code::NORMAL) {
+ return LookupNormalTypeCache(name, flags);
+ } else {
+ return LookupDefaultCache(name, flags);
+ }
+}
+
+
+Object* CodeCache::LookupDefaultCache(Name* name, Code::Flags flags) {
+ FixedArray* cache = default_cache();
+ int length = cache->length();
+ for (int i = 0; i < length; i += kCodeCacheEntrySize) {
+ Object* key = cache->get(i + kCodeCacheEntryNameOffset);
+ // Skip deleted elements.
+ if (key->IsNull()) continue;
+ if (key->IsUndefined()) return key;
+ if (name->Equals(Name::cast(key))) {
+ Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
+ if (code->flags() == flags) {
+ return code;
+ }
+ }
+ }
+ return GetHeap()->undefined_value();
+}
+
+
+Object* CodeCache::LookupNormalTypeCache(Name* name, Code::Flags flags) {
+ if (!normal_type_cache()->IsUndefined()) {
+ CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
+ return cache->Lookup(name, flags);
+ } else {
+ return GetHeap()->undefined_value();
+ }
+}
+
+
+int CodeCache::GetIndex(Object* name, Code* code) {
+ if (code->type() == Code::NORMAL) {
+ if (normal_type_cache()->IsUndefined()) return -1;
+ CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
+ return cache->GetIndex(Name::cast(name), code->flags());
+ }
+
+ FixedArray* array = default_cache();
+ int len = array->length();
+ for (int i = 0; i < len; i += kCodeCacheEntrySize) {
+ if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
+ }
+ return -1;
+}
+
+
+void CodeCache::RemoveByIndex(Object* name, Code* code, int index) {
+ if (code->type() == Code::NORMAL) {
+ ASSERT(!normal_type_cache()->IsUndefined());
+ CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
+ ASSERT(cache->GetIndex(Name::cast(name), code->flags()) == index);
+ cache->RemoveByIndex(index);
+ } else {
+ FixedArray* array = default_cache();
+ ASSERT(array->length() >= index && array->get(index)->IsCode());
+ // Use null instead of undefined for deleted elements to distinguish
+ // deleted elements from unused elements. This distinction is used
+ // when looking up in the cache and when updating the cache.
+ ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
+ array->set_null(index - 1); // Name.
+ array->set_null(index); // Code.
+ }
+}
+
+
+// The key in the code cache hash table consists of the property name and the
+// code object. The actual match is on the name and the code flags. If a key
+// is created using the flags and not a code object it can only be used for
+// lookup not to create a new entry.
+class CodeCacheHashTableKey : public HashTableKey {
+ public:
+ CodeCacheHashTableKey(Name* name, Code::Flags flags)
+ : name_(name), flags_(flags), code_(NULL) { }
+
+ CodeCacheHashTableKey(Name* name, Code* code)
+ : name_(name),
+ flags_(code->flags()),
+ code_(code) { }
+
+
+ bool IsMatch(Object* other) {
+ if (!other->IsFixedArray()) return false;
+ FixedArray* pair = FixedArray::cast(other);
+ Name* name = Name::cast(pair->get(0));
+ Code::Flags flags = Code::cast(pair->get(1))->flags();
+ if (flags != flags_) {
+ return false;
+ }
+ return name_->Equals(name);
+ }
+
+ static uint32_t NameFlagsHashHelper(Name* name, Code::Flags flags) {
+ return name->Hash() ^ flags;
+ }
+
+ uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
+
+ uint32_t HashForObject(Object* obj) {
+ FixedArray* pair = FixedArray::cast(obj);
+ Name* name = Name::cast(pair->get(0));
+ Code* code = Code::cast(pair->get(1));
+ return NameFlagsHashHelper(name, code->flags());
+ }
+
+ MUST_USE_RESULT MaybeObject* AsObject(Heap* heap) {
+ ASSERT(code_ != NULL);
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateFixedArray(2);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedArray* pair = FixedArray::cast(obj);
+ pair->set(0, name_);
+ pair->set(1, code_);
+ return pair;
+ }
+
+ private:
+ Name* name_;
+ Code::Flags flags_;
+ // TODO(jkummerow): We should be able to get by without this.
+ Code* code_;
+};
+
+
+Object* CodeCacheHashTable::Lookup(Name* name, Code::Flags flags) {
+ CodeCacheHashTableKey key(name, flags);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) return GetHeap()->undefined_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
+MaybeObject* CodeCacheHashTable::Put(Name* name, Code* code) {
+ CodeCacheHashTableKey key(name, code);
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ // Don't use |this|, as the table might have grown.
+ CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
+
+ int entry = cache->FindInsertionEntry(key.Hash());
+ Object* k;
+ { MaybeObject* maybe_k = key.AsObject(GetHeap());
+ if (!maybe_k->ToObject(&k)) return maybe_k;
+ }
+
+ cache->set(EntryToIndex(entry), k);
+ cache->set(EntryToIndex(entry) + 1, code);
+ cache->ElementAdded();
+ return cache;
+}
+
+
+int CodeCacheHashTable::GetIndex(Name* name, Code::Flags flags) {
+ CodeCacheHashTableKey key(name, flags);
+ int entry = FindEntry(&key);
+ return (entry == kNotFound) ? -1 : entry;
+}
+
+
+void CodeCacheHashTable::RemoveByIndex(int index) {
+ ASSERT(index >= 0);
+ Heap* heap = GetHeap();
+ set(EntryToIndex(index), heap->the_hole_value());
+ set(EntryToIndex(index) + 1, heap->the_hole_value());
+ ElementRemoved();
+}
+
+
+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.
+ if (cache()->IsUndefined()) {
+ Object* result;
+ { MaybeObject* maybe_result =
+ PolymorphicCodeCacheHashTable::Allocate(
+ GetHeap(),
+ PolymorphicCodeCacheHashTable::kInitialSize);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ set_cache(result);
+ } else {
+ // This entry shouldn't be contained in the cache yet.
+ ASSERT(PolymorphicCodeCacheHashTable::cast(cache())
+ ->Lookup(maps, flags)->IsUndefined());
+ }
+ PolymorphicCodeCacheHashTable* hash_table =
+ PolymorphicCodeCacheHashTable::cast(cache());
+ Object* new_cache;
+ { MaybeObject* maybe_new_cache = hash_table->Put(maps, flags, code);
+ if (!maybe_new_cache->ToObject(&new_cache)) return maybe_new_cache;
+ }
+ set_cache(new_cache);
+ return this;
+}
+
+
+Handle<Object> PolymorphicCodeCache::Lookup(MapHandleList* maps,
+ Code::Flags flags) {
+ if (!cache()->IsUndefined()) {
+ PolymorphicCodeCacheHashTable* hash_table =
+ PolymorphicCodeCacheHashTable::cast(cache());
+ return Handle<Object>(hash_table->Lookup(maps, flags), GetIsolate());
+ } else {
+ return GetIsolate()->factory()->undefined_value();
+ }
+}
+
+
+// Despite their name, object of this class are not stored in the actual
+// hash table; instead they're temporarily used for lookups. It is therefore
+// safe to have a weak (non-owning) pointer to a MapList as a member field.
+class PolymorphicCodeCacheHashTableKey : public HashTableKey {
+ public:
+ // Callers must ensure that |maps| outlives the newly constructed object.
+ PolymorphicCodeCacheHashTableKey(MapHandleList* maps, int code_flags)
+ : maps_(maps),
+ code_flags_(code_flags) {}
+
+ bool IsMatch(Object* other) {
+ MapHandleList other_maps(kDefaultListAllocationSize);
+ int other_flags;
+ FromObject(other, &other_flags, &other_maps);
+ if (code_flags_ != other_flags) return false;
+ if (maps_->length() != other_maps.length()) return false;
+ // Compare just the hashes first because it's faster.
+ int this_hash = MapsHashHelper(maps_, code_flags_);
+ int other_hash = MapsHashHelper(&other_maps, other_flags);
+ if (this_hash != other_hash) return false;
+
+ // Full comparison: for each map in maps_, look for an equivalent map in
+ // other_maps. This implementation is slow, but probably good enough for
+ // now because the lists are short (<= 4 elements currently).
+ 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)) == *(other_maps.at(j))) {
+ match_found = true;
+ break;
+ }
+ }
+ if (!match_found) return false;
+ }
+ return true;
+ }
+
+ 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();
+ }
+ return hash;
+ }
+
+ uint32_t Hash() {
+ return MapsHashHelper(maps_, code_flags_);
+ }
+
+ uint32_t HashForObject(Object* obj) {
+ MapHandleList other_maps(kDefaultListAllocationSize);
+ int other_flags;
+ FromObject(obj, &other_flags, &other_maps);
+ return MapsHashHelper(&other_maps, other_flags);
+ }
+
+ MUST_USE_RESULT MaybeObject* AsObject(Heap* heap) {
+ Object* obj;
+ // The maps in |maps_| must be copied to a newly allocated FixedArray,
+ // both because the referenced MapList is short-lived, and because C++
+ // objects can't be stored in the heap anyway.
+ { MaybeObject* maybe_obj =
+ heap->AllocateUninitializedFixedArray(maps_->length() + 1);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ 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));
+ }
+ return list;
+ }
+
+ private:
+ 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(Handle<Map>(Map::cast(list->get(i))));
+ }
+ return maps;
+ }
+
+ MapHandleList* maps_; // weak.
+ int code_flags_;
+ static const int kDefaultListAllocationSize = kMaxKeyedPolymorphism + 1;
+};
+
+
+Object* PolymorphicCodeCacheHashTable::Lookup(MapHandleList* maps,
+ int code_flags) {
+ PolymorphicCodeCacheHashTableKey key(maps, code_flags);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) return GetHeap()->undefined_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
+MaybeObject* PolymorphicCodeCacheHashTable::Put(MapHandleList* maps,
+ int code_flags,
+ Code* code) {
+ PolymorphicCodeCacheHashTableKey key(maps, code_flags);
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ PolymorphicCodeCacheHashTable* cache =
+ reinterpret_cast<PolymorphicCodeCacheHashTable*>(obj);
+ int entry = cache->FindInsertionEntry(key.Hash());
+ { MaybeObject* maybe_obj = key.AsObject(GetHeap());
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ cache->set(EntryToIndex(entry), obj);
+ cache->set(EntryToIndex(entry) + 1, code);
+ cache->ElementAdded();
+ return cache;
+}
+
+
+MaybeObject* FixedArray::AddKeysFromJSArray(JSArray* array) {
+ ElementsAccessor* accessor = array->GetElementsAccessor();
+ MaybeObject* maybe_result =
+ accessor->AddElementsToFixedArray(array, array, this);
+ FixedArray* result;
+ if (!maybe_result->To<FixedArray>(&result)) return maybe_result;
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ for (int i = 0; i < result->length(); i++) {
+ Object* current = result->get(i);
+ ASSERT(current->IsNumber() || current->IsName());
+ }
+ }
+#endif
+ return result;
+}
+
+
+MaybeObject* FixedArray::UnionOfKeys(FixedArray* other) {
+ ElementsAccessor* accessor = ElementsAccessor::ForArray(other);
+ MaybeObject* maybe_result =
+ accessor->AddElementsToFixedArray(NULL, NULL, this, other);
+ FixedArray* result;
+ if (!maybe_result->To(&result)) return maybe_result;
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ for (int i = 0; i < result->length(); i++) {
+ Object* current = result->get(i);
+ ASSERT(current->IsNumber() || current->IsName());
+ }
+ }
+#endif
+ return result;
+}
+
+
+MaybeObject* FixedArray::CopySize(int new_length) {
+ Heap* heap = GetHeap();
+ if (new_length == 0) return heap->empty_fixed_array();
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateFixedArray(new_length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedArray* result = FixedArray::cast(obj);
+ // Copy the content
+ DisallowHeapAllocation no_gc;
+ int len = length();
+ if (new_length < len) len = new_length;
+ // 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);
+ }
+ return result;
+}
+
+
+void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) {
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc);
+ for (int index = 0; index < len; index++) {
+ dest->set(dest_pos+index, get(pos+index), mode);
+ }
+}
+
+
+#ifdef DEBUG
+bool FixedArray::IsEqualTo(FixedArray* other) {
+ if (length() != other->length()) return false;
+ for (int i = 0 ; i < length(); ++i) {
+ if (get(i) != other->get(i)) return false;
+ }
+ return true;
+}
+#endif
+
+
+MaybeObject* DescriptorArray::Allocate(int number_of_descriptors, int slack) {
+ Heap* heap = Isolate::Current()->heap();
+ // Do not use DescriptorArray::cast on incomplete object.
+ int size = number_of_descriptors + slack;
+ if (size == 0) return heap->empty_descriptor_array();
+ FixedArray* result;
+ // Allocate the array of keys.
+ MaybeObject* maybe_array = heap->AllocateFixedArray(LengthFor(size));
+ if (!maybe_array->To(&result)) return maybe_array;
+
+ result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors));
+ result->set(kEnumCacheIndex, Smi::FromInt(0));
+ return result;
+}
+
+
+void DescriptorArray::ClearEnumCache() {
+ set(kEnumCacheIndex, Smi::FromInt(0));
+}
+
+
+void DescriptorArray::SetEnumCache(FixedArray* bridge_storage,
+ FixedArray* new_cache,
+ Object* new_index_cache) {
+ ASSERT(bridge_storage->length() >= kEnumCacheBridgeLength);
+ ASSERT(new_index_cache->IsSmi() || new_index_cache->IsFixedArray());
+ ASSERT(!IsEmpty());
+ ASSERT(!HasEnumCache() || new_cache->length() > GetEnumCache()->length());
+ FixedArray::cast(bridge_storage)->
+ set(kEnumCacheBridgeCacheIndex, new_cache);
+ FixedArray::cast(bridge_storage)->
+ set(kEnumCacheBridgeIndicesCacheIndex, new_index_cache);
+ set(kEnumCacheIndex, bridge_storage);
+}
+
+
+void DescriptorArray::CopyFrom(int dst_index,
+ DescriptorArray* src,
+ int src_index,
+ const WhitenessWitness& witness) {
+ Object* value = src->GetValue(src_index);
+ PropertyDetails details = src->GetDetails(src_index);
+ Descriptor desc(src->GetKey(src_index), value, details);
+ Set(dst_index, &desc, witness);
+}
+
+
+// Generalize the |other| descriptor array by merging it into the (at least
+// partly) updated |this| descriptor array.
+// The method merges two descriptor array in three parts. Both descriptor arrays
+// are identical up to |verbatim|. They also overlap in keys up to |valid|.
+// Between |verbatim| and |valid|, the resulting descriptor type as well as the
+// representation are generalized from both |this| and |other|. Beyond |valid|,
+// the descriptors are copied verbatim from |other| up to |new_size|.
+// In case of incompatible types, the type and representation of |other| is
+// used.
+MaybeObject* DescriptorArray::Merge(int verbatim,
+ int valid,
+ int new_size,
+ DescriptorArray* other) {
+ ASSERT(verbatim <= valid);
+ ASSERT(valid <= new_size);
+
+ DescriptorArray* result;
+ // Allocate a new descriptor array large enough to hold the required
+ // descriptors, with minimally the exact same size as this descriptor array.
+ MaybeObject* maybe_descriptors = DescriptorArray::Allocate(
+ new_size, Max(new_size, other->number_of_descriptors()) - new_size);
+ if (!maybe_descriptors->To(&result)) return maybe_descriptors;
+ ASSERT(result->length() > length() ||
+ result->NumberOfSlackDescriptors() > 0 ||
+ result->number_of_descriptors() == other->number_of_descriptors());
+ ASSERT(result->number_of_descriptors() == new_size);
+
+ DescriptorArray::WhitenessWitness witness(result);
+
+ int descriptor;
+
+ // 0 -> |verbatim|
+ int current_offset = 0;
+ for (descriptor = 0; descriptor < verbatim; descriptor++) {
+ if (GetDetails(descriptor).type() == FIELD) current_offset++;
+ result->CopyFrom(descriptor, this, descriptor, witness);
+ }
+
+ // |verbatim| -> |valid|
+ for (; descriptor < valid; descriptor++) {
+ Name* key = GetKey(descriptor);
+ PropertyDetails details = GetDetails(descriptor);
+ PropertyDetails other_details = other->GetDetails(descriptor);
+
+ if (details.type() == FIELD || other_details.type() == FIELD ||
+ (details.type() == CONSTANT &&
+ other_details.type() == CONSTANT &&
+ GetValue(descriptor) != other->GetValue(descriptor))) {
+ Representation representation =
+ details.representation().generalize(other_details.representation());
+ FieldDescriptor d(key,
+ current_offset++,
+ other_details.attributes(),
+ representation);
+ result->Set(descriptor, &d, witness);
+ } else {
+ result->CopyFrom(descriptor, other, descriptor, witness);
+ }
+ }
+
+ // |valid| -> |new_size|
+ for (; descriptor < new_size; descriptor++) {
+ PropertyDetails details = other->GetDetails(descriptor);
+ if (details.type() == FIELD) {
+ Name* key = other->GetKey(descriptor);
+ FieldDescriptor d(key,
+ current_offset++,
+ details.attributes(),
+ details.representation());
+ result->Set(descriptor, &d, witness);
+ } else {
+ result->CopyFrom(descriptor, other, descriptor, witness);
+ }
+ }
+
+ result->Sort();
+ return result;
+}
+
+
+// Checks whether a merge of |other| into |this| would return a copy of |this|.
+bool DescriptorArray::IsMoreGeneralThan(int verbatim,
+ int valid,
+ int new_size,
+ DescriptorArray* other) {
+ ASSERT(verbatim <= valid);
+ ASSERT(valid <= new_size);
+ if (valid != new_size) return false;
+
+ for (int descriptor = verbatim; descriptor < valid; descriptor++) {
+ PropertyDetails details = GetDetails(descriptor);
+ PropertyDetails other_details = other->GetDetails(descriptor);
+ if (!other_details.representation().fits_into(details.representation())) {
+ return false;
+ }
+ if (details.type() == CONSTANT) {
+ if (other_details.type() != CONSTANT) return false;
+ if (GetValue(descriptor) != other->GetValue(descriptor)) return false;
+ }
+ }
+
+ return true;
+}
+
+
+// We need the whiteness witness since sort will reshuffle the entries in the
+// descriptor array. If the descriptor array were to be black, the shuffling
+// would move a slot that was already recorded as pointing into an evacuation
+// candidate. This would result in missing updates upon evacuation.
+void DescriptorArray::Sort() {
+ // In-place heap sort.
+ int len = number_of_descriptors();
+ // Reset sorting since the descriptor array might contain invalid pointers.
+ for (int i = 0; i < len; ++i) SetSortedKey(i, i);
+ // Bottom-up max-heap construction.
+ // Index of the last node with children
+ const int max_parent_index = (len / 2) - 1;
+ for (int i = max_parent_index; i >= 0; --i) {
+ int parent_index = i;
+ const uint32_t parent_hash = GetSortedKey(i)->Hash();
+ while (parent_index <= max_parent_index) {
+ int child_index = 2 * parent_index + 1;
+ uint32_t child_hash = GetSortedKey(child_index)->Hash();
+ if (child_index + 1 < len) {
+ uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash();
+ if (right_child_hash > child_hash) {
+ child_index++;
+ child_hash = right_child_hash;
+ }
+ }
+ if (child_hash <= parent_hash) break;
+ SwapSortedKeys(parent_index, child_index);
+ // Now element at child_index could be < its children.
+ parent_index = child_index; // parent_hash remains correct.
+ }
+ }
+
+ // Extract elements and create sorted array.
+ for (int i = len - 1; i > 0; --i) {
+ // Put max element at the back of the array.
+ SwapSortedKeys(0, i);
+ // Shift down the new top element.
+ int parent_index = 0;
+ const uint32_t parent_hash = GetSortedKey(parent_index)->Hash();
+ const int max_parent_index = (i / 2) - 1;
+ while (parent_index <= max_parent_index) {
+ int child_index = parent_index * 2 + 1;
+ uint32_t child_hash = GetSortedKey(child_index)->Hash();
+ if (child_index + 1 < i) {
+ uint32_t right_child_hash = GetSortedKey(child_index + 1)->Hash();
+ if (right_child_hash > child_hash) {
+ child_index++;
+ child_hash = right_child_hash;
+ }
+ }
+ if (child_hash <= parent_hash) break;
+ SwapSortedKeys(parent_index, child_index);
+ parent_index = child_index;
+ }
+ }
+ ASSERT(IsSortedNoDuplicates());
+}
+
+
+Handle<AccessorPair> AccessorPair::Copy(Handle<AccessorPair> pair) {
+ Handle<AccessorPair> copy = pair->GetIsolate()->factory()->NewAccessorPair();
+ copy->set_getter(pair->getter());
+ copy->set_setter(pair->setter());
+ return copy;
+}
+
+
+Object* AccessorPair::GetComponent(AccessorComponent component) {
+ Object* accessor = get(component);
+ return accessor->IsTheHole() ? GetHeap()->undefined_value() : accessor;
+}
+
+
+MaybeObject* DeoptimizationInputData::Allocate(int deopt_entry_count,
+ PretenureFlag pretenure) {
+ ASSERT(deopt_entry_count > 0);
+ return HEAP->AllocateFixedArray(LengthFor(deopt_entry_count),
+ pretenure);
+}
+
+
+MaybeObject* DeoptimizationOutputData::Allocate(int number_of_deopt_points,
+ PretenureFlag pretenure) {
+ if (number_of_deopt_points == 0) return HEAP->empty_fixed_array();
+ return HEAP->AllocateFixedArray(LengthOfFixedArray(number_of_deopt_points),
+ pretenure);
+}
+
+
+#ifdef DEBUG
+bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
+ if (IsEmpty()) return other->IsEmpty();
+ if (other->IsEmpty()) return false;
+ if (length() != other->length()) return false;
+ for (int i = 0; i < length(); ++i) {
+ if (get(i) != other->get(i)) return false;
+ }
+ return true;
+}
+#endif
+
+
+bool String::LooksValid() {
+ if (!Isolate::Current()->heap()->Contains(this)) return false;
+ return true;
+}
+
+
+String::FlatContent String::GetFlatContent() {
+ ASSERT(!AllowHeapAllocation::IsAllowed());
+ int length = this->length();
+ StringShape shape(this);
+ String* string = this;
+ int offset = 0;
+ if (shape.representation_tag() == kConsStringTag) {
+ ConsString* cons = ConsString::cast(string);
+ if (cons->second()->length() != 0) {
+ return FlatContent();
+ }
+ string = cons->first();
+ shape = StringShape(string);
+ }
+ if (shape.representation_tag() == kSlicedStringTag) {
+ SlicedString* slice = SlicedString::cast(string);
+ offset = slice->offset();
+ string = slice->parent();
+ shape = StringShape(string);
+ ASSERT(shape.representation_tag() != kConsStringTag &&
+ shape.representation_tag() != kSlicedStringTag);
+ }
+ if (shape.encoding_tag() == kOneByteStringTag) {
+ const uint8_t* start;
+ if (shape.representation_tag() == kSeqStringTag) {
+ start = SeqOneByteString::cast(string)->GetChars();
+ } else {
+ start = ExternalAsciiString::cast(string)->GetChars();
+ }
+ return FlatContent(Vector<const uint8_t>(start + offset, length));
+ } else {
+ ASSERT(shape.encoding_tag() == kTwoByteStringTag);
+ const uc16* start;
+ if (shape.representation_tag() == kSeqStringTag) {
+ start = SeqTwoByteString::cast(string)->GetChars();
+ } else {
+ start = ExternalTwoByteString::cast(string)->GetChars();
+ }
+ return FlatContent(Vector<const uc16>(start + offset, length));
+ }
+}
+
+
+SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
+ RobustnessFlag robust_flag,
+ int offset,
+ int length,
+ int* length_return) {
+ if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
+ return SmartArrayPointer<char>(NULL);
+ }
+ Heap* heap = GetHeap();
+
+ // Negative length means the to the end of the string.
+ if (length < 0) length = kMaxInt - offset;
+
+ // Compute the size of the UTF-8 string. Start at the specified offset.
+ Access<ConsStringIteratorOp> op(
+ heap->isolate()->objects_string_iterator());
+ StringCharacterStream stream(this, op.value(), offset);
+ int character_position = offset;
+ int utf8_bytes = 0;
+ int last = unibrow::Utf16::kNoPreviousCharacter;
+ while (stream.HasMore() && character_position++ < offset + length) {
+ uint16_t character = stream.GetNext();
+ utf8_bytes += unibrow::Utf8::Length(character, last);
+ last = character;
+ }
+
+ if (length_return) {
+ *length_return = utf8_bytes;
+ }
+
+ char* result = NewArray<char>(utf8_bytes + 1);
+
+ // Convert the UTF-16 string to a UTF-8 buffer. Start at the specified offset.
+ stream.Reset(this, offset);
+ character_position = offset;
+ int utf8_byte_position = 0;
+ last = unibrow::Utf16::kNoPreviousCharacter;
+ while (stream.HasMore() && character_position++ < offset + length) {
+ uint16_t character = stream.GetNext();
+ if (allow_nulls == DISALLOW_NULLS && character == 0) {
+ character = ' ';
+ }
+ utf8_byte_position +=
+ unibrow::Utf8::Encode(result + utf8_byte_position, character, last);
+ last = character;
+ }
+ result[utf8_byte_position] = 0;
+ return SmartArrayPointer<char>(result);
+}
+
+
+SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls,
+ RobustnessFlag robust_flag,
+ int* length_return) {
+ return ToCString(allow_nulls, robust_flag, 0, -1, length_return);
+}
+
+
+const uc16* String::GetTwoByteData() {
+ return GetTwoByteData(0);
+}
+
+
+const uc16* String::GetTwoByteData(unsigned start) {
+ ASSERT(!IsOneByteRepresentationUnderneath());
+ switch (StringShape(this).representation_tag()) {
+ case kSeqStringTag:
+ return SeqTwoByteString::cast(this)->SeqTwoByteStringGetData(start);
+ case kExternalStringTag:
+ return ExternalTwoByteString::cast(this)->
+ ExternalTwoByteStringGetData(start);
+ case kSlicedStringTag: {
+ SlicedString* slice = SlicedString::cast(this);
+ return slice->parent()->GetTwoByteData(start + slice->offset());
+ }
+ case kConsStringTag:
+ UNREACHABLE();
+ return NULL;
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+SmartArrayPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) {
+ if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) {
+ return SmartArrayPointer<uc16>();
+ }
+ Heap* heap = GetHeap();
+
+ Access<ConsStringIteratorOp> op(
+ heap->isolate()->objects_string_iterator());
+ StringCharacterStream stream(this, op.value());
+
+ uc16* result = NewArray<uc16>(length() + 1);
+
+ int i = 0;
+ while (stream.HasMore()) {
+ uint16_t character = stream.GetNext();
+ result[i++] = character;
+ }
+ result[i] = 0;
+ return SmartArrayPointer<uc16>(result);
+}
+
+
+const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) {
+ return reinterpret_cast<uc16*>(
+ reinterpret_cast<char*>(this) - kHeapObjectTag + kHeaderSize) + start;
+}
+
+
+void Relocatable::PostGarbageCollectionProcessing() {
+ Isolate* isolate = Isolate::Current();
+ Relocatable* current = isolate->relocatable_top();
+ while (current != NULL) {
+ current->PostGarbageCollection();
+ current = current->prev_;
+ }
+}
+
+
+// Reserve space for statics needing saving and restoring.
+int Relocatable::ArchiveSpacePerThread() {
+ return sizeof(Isolate::Current()->relocatable_top());
+}
+
+
+// Archive statics that are thread local.
+char* Relocatable::ArchiveState(Isolate* isolate, char* to) {
+ *reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top();
+ isolate->set_relocatable_top(NULL);
+ return to + ArchiveSpacePerThread();
+}
+
+
+// Restore statics that are thread local.
+char* Relocatable::RestoreState(Isolate* isolate, char* from) {
+ isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from));
+ return from + ArchiveSpacePerThread();
+}
+
+
+char* Relocatable::Iterate(ObjectVisitor* v, char* thread_storage) {
+ Relocatable* top = *reinterpret_cast<Relocatable**>(thread_storage);
+ Iterate(v, top);
+ return thread_storage + ArchiveSpacePerThread();
+}
+
+
+void Relocatable::Iterate(ObjectVisitor* v) {
+ Isolate* isolate = Isolate::Current();
+ Iterate(v, isolate->relocatable_top());
+}
+
+
+void Relocatable::Iterate(ObjectVisitor* v, Relocatable* top) {
+ Relocatable* current = top;
+ while (current != NULL) {
+ current->IterateInstance(v);
+ current = current->prev_;
+ }
+}
+
+
+FlatStringReader::FlatStringReader(Isolate* isolate, Handle<String> str)
+ : Relocatable(isolate),
+ str_(str.location()),
+ length_(str->length()) {
+ PostGarbageCollection();
+}
+
+
+FlatStringReader::FlatStringReader(Isolate* isolate, Vector<const char> input)
+ : Relocatable(isolate),
+ str_(0),
+ is_ascii_(true),
+ length_(input.length()),
+ start_(input.start()) { }
+
+
+void FlatStringReader::PostGarbageCollection() {
+ if (str_ == NULL) return;
+ Handle<String> str(str_);
+ ASSERT(str->IsFlat());
+ DisallowHeapAllocation no_gc;
+ // This does not actually prevent the vector from being relocated later.
+ String::FlatContent content = str->GetFlatContent();
+ ASSERT(content.IsFlat());
+ is_ascii_ = content.IsAscii();
+ if (is_ascii_) {
+ start_ = content.ToOneByteVector().start();
+ } else {
+ start_ = content.ToUC16Vector().start();
+ }
+}
+
+
+String* ConsStringIteratorOp::Operate(String* string,
+ unsigned* offset_out,
+ int32_t* type_out,
+ unsigned* length_out) {
+ ASSERT(string->IsConsString());
+ ConsString* cons_string = ConsString::cast(string);
+ // Set up search data.
+ root_ = cons_string;
+ consumed_ = *offset_out;
+ // Now search.
+ return Search(offset_out, type_out, length_out);
+}
+
+
+String* ConsStringIteratorOp::Search(unsigned* offset_out,
+ int32_t* type_out,
+ unsigned* length_out) {
+ ConsString* cons_string = root_;
+ // Reset the stack, pushing the root string.
+ depth_ = 1;
+ maximum_depth_ = 1;
+ frames_[0] = cons_string;
+ const unsigned consumed = consumed_;
+ unsigned offset = 0;
+ while (true) {
+ // Loop until the string is found which contains the target offset.
+ String* string = cons_string->first();
+ unsigned length = string->length();
+ int32_t type;
+ if (consumed < offset + length) {
+ // Target offset is in the left branch.
+ // Keep going if we're still in a ConString.
+ type = string->map()->instance_type();
+ if ((type & kStringRepresentationMask) == kConsStringTag) {
+ cons_string = ConsString::cast(string);
+ PushLeft(cons_string);
+ continue;
+ }
+ // Tell the stack we're done decending.
+ AdjustMaximumDepth();
+ } else {
+ // Descend right.
+ // Update progress through the string.
+ offset += length;
+ // Keep going if we're still in a ConString.
+ string = cons_string->second();
+ type = string->map()->instance_type();
+ if ((type & kStringRepresentationMask) == kConsStringTag) {
+ cons_string = ConsString::cast(string);
+ PushRight(cons_string);
+ // TODO(dcarney) Add back root optimization.
+ continue;
+ }
+ // Need this to be updated for the current string.
+ length = string->length();
+ // Account for the possibility of an empty right leaf.
+ // This happens only if we have asked for an offset outside the string.
+ if (length == 0) {
+ // Reset depth so future operations will return null immediately.
+ Reset();
+ return NULL;
+ }
+ // Tell the stack we're done decending.
+ AdjustMaximumDepth();
+ // Pop stack so next iteration is in correct place.
+ Pop();
+ }
+ ASSERT(length != 0);
+ // Adjust return values and exit.
+ consumed_ = offset + length;
+ *offset_out = consumed - offset;
+ *type_out = type;
+ *length_out = length;
+ return string;
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+String* ConsStringIteratorOp::NextLeaf(bool* blew_stack,
+ int32_t* type_out,
+ unsigned* length_out) {
+ while (true) {
+ // Tree traversal complete.
+ if (depth_ == 0) {
+ *blew_stack = false;
+ return NULL;
+ }
+ // We've lost track of higher nodes.
+ if (maximum_depth_ - depth_ == kStackSize) {
+ *blew_stack = true;
+ return NULL;
+ }
+ // Go right.
+ ConsString* cons_string = frames_[OffsetForDepth(depth_ - 1)];
+ String* string = cons_string->second();
+ int32_t type = string->map()->instance_type();
+ if ((type & kStringRepresentationMask) != kConsStringTag) {
+ // Pop stack so next iteration is in correct place.
+ Pop();
+ unsigned length = static_cast<unsigned>(string->length());
+ // Could be a flattened ConsString.
+ if (length == 0) continue;
+ *length_out = length;
+ *type_out = type;
+ consumed_ += length;
+ return string;
+ }
+ cons_string = ConsString::cast(string);
+ // TODO(dcarney) Add back root optimization.
+ PushRight(cons_string);
+ // Need to traverse all the way left.
+ while (true) {
+ // Continue left.
+ string = cons_string->first();
+ type = string->map()->instance_type();
+ if ((type & kStringRepresentationMask) != kConsStringTag) {
+ AdjustMaximumDepth();
+ unsigned length = static_cast<unsigned>(string->length());
+ ASSERT(length != 0);
+ *length_out = length;
+ *type_out = type;
+ consumed_ += length;
+ return string;
+ }
+ cons_string = ConsString::cast(string);
+ PushLeft(cons_string);
+ }
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+uint16_t ConsString::ConsStringGet(int index) {
+ ASSERT(index >= 0 && index < this->length());
+
+ // Check for a flattened cons string
+ if (second()->length() == 0) {
+ String* left = first();
+ return left->Get(index);
+ }
+
+ String* string = String::cast(this);
+
+ while (true) {
+ if (StringShape(string).IsCons()) {
+ ConsString* cons_string = ConsString::cast(string);
+ String* left = cons_string->first();
+ if (left->length() > index) {
+ string = left;
+ } else {
+ index -= left->length();
+ string = cons_string->second();
+ }
+ } else {
+ return string->Get(index);
+ }
+ }
+
+ UNREACHABLE();
+ return 0;
+}
+
+
+uint16_t SlicedString::SlicedStringGet(int index) {
+ return parent()->Get(offset() + index);
+}
+
+
+template <typename sinkchar>
+void String::WriteToFlat(String* src,
+ sinkchar* sink,
+ int f,
+ int t) {
+ String* source = src;
+ int from = f;
+ int to = t;
+ while (true) {
+ ASSERT(0 <= from && from <= to && to <= source->length());
+ switch (StringShape(source).full_representation_tag()) {
+ case kOneByteStringTag | kExternalStringTag: {
+ CopyChars(sink,
+ ExternalAsciiString::cast(source)->GetChars() + from,
+ to - from);
+ return;
+ }
+ case kTwoByteStringTag | kExternalStringTag: {
+ const uc16* data =
+ ExternalTwoByteString::cast(source)->GetChars();
+ CopyChars(sink,
+ data + from,
+ to - from);
+ return;
+ }
+ case kOneByteStringTag | kSeqStringTag: {
+ CopyChars(sink,
+ SeqOneByteString::cast(source)->GetChars() + from,
+ to - from);
+ return;
+ }
+ case kTwoByteStringTag | kSeqStringTag: {
+ CopyChars(sink,
+ SeqTwoByteString::cast(source)->GetChars() + from,
+ to - from);
+ return;
+ }
+ case kOneByteStringTag | kConsStringTag:
+ case kTwoByteStringTag | kConsStringTag: {
+ ConsString* cons_string = ConsString::cast(source);
+ String* first = cons_string->first();
+ int boundary = first->length();
+ if (to - boundary >= boundary - from) {
+ // Right hand side is longer. Recurse over left.
+ if (from < boundary) {
+ WriteToFlat(first, sink, from, boundary);
+ sink += boundary - from;
+ from = 0;
+ } else {
+ from -= boundary;
+ }
+ to -= boundary;
+ source = cons_string->second();
+ } else {
+ // Left hand side is longer. Recurse over right.
+ if (to > boundary) {
+ String* second = cons_string->second();
+ // When repeatedly appending to a string, we get a cons string that
+ // is unbalanced to the left, a list, essentially. We inline the
+ // common case of sequential ascii right child.
+ if (to - boundary == 1) {
+ sink[boundary - from] = static_cast<sinkchar>(second->Get(0));
+ } else if (second->IsSeqOneByteString()) {
+ CopyChars(sink + boundary - from,
+ SeqOneByteString::cast(second)->GetChars(),
+ to - boundary);
+ } else {
+ WriteToFlat(second,
+ sink + boundary - from,
+ 0,
+ to - boundary);
+ }
+ to = boundary;
+ }
+ source = first;
+ }
+ break;
+ }
+ case kOneByteStringTag | kSlicedStringTag:
+ case kTwoByteStringTag | kSlicedStringTag: {
+ SlicedString* slice = SlicedString::cast(source);
+ unsigned offset = slice->offset();
+ WriteToFlat(slice->parent(), sink, from + offset, to + offset);
+ return;
+ }
+ }
+ }
+}
+
+
+// Compares the contents of two strings by reading and comparing
+// int-sized blocks of characters.
+template <typename Char>
+static inline bool CompareRawStringContents(const Char* const a,
+ const Char* const b,
+ int length) {
+ int i = 0;
+#ifndef V8_HOST_CAN_READ_UNALIGNED
+ // If this architecture isn't comfortable reading unaligned ints
+ // then we have to check that the strings are aligned before
+ // comparing them blockwise.
+ const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
+ uint32_t pa_addr = reinterpret_cast<uint32_t>(a);
+ uint32_t pb_addr = reinterpret_cast<uint32_t>(b);
+ if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
+#endif
+ const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
+ int endpoint = length - kStepSize;
+ // Compare blocks until we reach near the end of the string.
+ for (; i <= endpoint; i += kStepSize) {
+ uint32_t wa = *reinterpret_cast<const uint32_t*>(a + i);
+ uint32_t wb = *reinterpret_cast<const uint32_t*>(b + i);
+ if (wa != wb) {
+ return false;
+ }
+ }
+#ifndef V8_HOST_CAN_READ_UNALIGNED
+ }
+#endif
+ // Compare the remaining characters that didn't fit into a block.
+ for (; i < length; i++) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+template<typename Chars1, typename Chars2>
+class RawStringComparator : public AllStatic {
+ public:
+ static inline bool compare(const Chars1* a, const Chars2* b, int len) {
+ ASSERT(sizeof(Chars1) != sizeof(Chars2));
+ for (int i = 0; i < len; i++) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+
+template<>
+class RawStringComparator<uint16_t, uint16_t> {
+ public:
+ static inline bool compare(const uint16_t* a, const uint16_t* b, int len) {
+ return CompareRawStringContents(a, b, len);
+ }
+};
+
+
+template<>
+class RawStringComparator<uint8_t, uint8_t> {
+ public:
+ static inline bool compare(const uint8_t* a, const uint8_t* b, int len) {
+ return CompareRawStringContents(a, b, len);
+ }
+};
+
+
+class StringComparator {
+ class State {
+ public:
+ explicit inline State(ConsStringIteratorOp* op)
+ : op_(op), is_one_byte_(true), length_(0), buffer8_(NULL) {}
+
+ inline void Init(String* string, unsigned len) {
+ op_->Reset();
+ int32_t type = string->map()->instance_type();
+ String::Visit(string, 0, *this, *op_, type, len);
+ }
+
+ inline void VisitOneByteString(const uint8_t* chars, unsigned length) {
+ is_one_byte_ = true;
+ buffer8_ = chars;
+ length_ = length;
+ }
+
+ inline void VisitTwoByteString(const uint16_t* chars, unsigned length) {
+ is_one_byte_ = false;
+ buffer16_ = chars;
+ length_ = length;
+ }
+
+ void Advance(unsigned consumed) {
+ ASSERT(consumed <= length_);
+ // Still in buffer.
+ if (length_ != consumed) {
+ if (is_one_byte_) {
+ buffer8_ += consumed;
+ } else {
+ buffer16_ += consumed;
+ }
+ length_ -= consumed;
+ return;
+ }
+ // Advance state.
+ ASSERT(op_->HasMore());
+ int32_t type = 0;
+ unsigned length = 0;
+ String* next = op_->ContinueOperation(&type, &length);
+ ASSERT(next != NULL);
+ ConsStringNullOp null_op;
+ String::Visit(next, 0, *this, null_op, type, length);
+ }
+
+ ConsStringIteratorOp* const op_;
+ bool is_one_byte_;
+ unsigned length_;
+ union {
+ const uint8_t* buffer8_;
+ const uint16_t* buffer16_;
+ };
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(State);
+ };
+
+ public:
+ inline StringComparator(ConsStringIteratorOp* op_1,
+ ConsStringIteratorOp* op_2)
+ : state_1_(op_1),
+ state_2_(op_2) {
+ }
+
+ template<typename Chars1, typename Chars2>
+ static inline bool Equals(State* state_1, State* state_2, unsigned to_check) {
+ const Chars1* a = reinterpret_cast<const Chars1*>(state_1->buffer8_);
+ const Chars2* b = reinterpret_cast<const Chars2*>(state_2->buffer8_);
+ return RawStringComparator<Chars1, Chars2>::compare(a, b, to_check);
+ }
+
+ bool Equals(unsigned length, String* string_1, String* string_2) {
+ ASSERT(length != 0);
+ state_1_.Init(string_1, length);
+ state_2_.Init(string_2, length);
+ while (true) {
+ unsigned to_check = Min(state_1_.length_, state_2_.length_);
+ ASSERT(to_check > 0 && to_check <= length);
+ bool is_equal;
+ if (state_1_.is_one_byte_) {
+ if (state_2_.is_one_byte_) {
+ is_equal = Equals<uint8_t, uint8_t>(&state_1_, &state_2_, to_check);
+ } else {
+ is_equal = Equals<uint8_t, uint16_t>(&state_1_, &state_2_, to_check);
+ }
+ } else {
+ if (state_2_.is_one_byte_) {
+ is_equal = Equals<uint16_t, uint8_t>(&state_1_, &state_2_, to_check);
+ } else {
+ is_equal = Equals<uint16_t, uint16_t>(&state_1_, &state_2_, to_check);
+ }
+ }
+ // Looping done.
+ if (!is_equal) return false;
+ length -= to_check;
+ // Exit condition. Strings are equal.
+ if (length == 0) return true;
+ state_1_.Advance(to_check);
+ state_2_.Advance(to_check);
+ }
+ }
+
+ private:
+ State state_1_;
+ State state_2_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringComparator);
+};
+
+
+bool String::SlowEquals(String* other) {
+ // Fast check: negative check with lengths.
+ int len = length();
+ if (len != other->length()) return false;
+ if (len == 0) return true;
+
+ // Fast check: if hash code is computed for both strings
+ // a fast negative check can be performed.
+ if (HasHashCode() && other->HasHashCode()) {
+#ifdef DEBUG
+ if (FLAG_enable_slow_asserts) {
+ if (Hash() != other->Hash()) {
+ bool found_difference = false;
+ for (int i = 0; i < len; i++) {
+ if (Get(i) != other->Get(i)) {
+ found_difference = true;
+ break;
+ }
+ }
+ ASSERT(found_difference);
+ }
+ }
+#endif
+ if (Hash() != other->Hash()) return false;
+ }
+
+ // We know the strings are both non-empty. Compare the first chars
+ // before we try to flatten the strings.
+ if (this->Get(0) != other->Get(0)) return false;
+
+ String* lhs = this->TryFlattenGetString();
+ String* rhs = other->TryFlattenGetString();
+
+ // TODO(dcarney): Compare all types of flat strings with a Visitor.
+ if (StringShape(lhs).IsSequentialAscii() &&
+ StringShape(rhs).IsSequentialAscii()) {
+ const uint8_t* str1 = SeqOneByteString::cast(lhs)->GetChars();
+ const uint8_t* str2 = SeqOneByteString::cast(rhs)->GetChars();
+ return CompareRawStringContents(str1, str2, len);
+ }
+
+ Isolate* isolate = GetIsolate();
+ StringComparator comparator(isolate->objects_string_compare_iterator_a(),
+ isolate->objects_string_compare_iterator_b());
+
+ return comparator.Equals(static_cast<unsigned>(len), lhs, rhs);
+}
+
+
+bool String::MarkAsUndetectable() {
+ if (StringShape(this).IsInternalized()) return false;
+
+ Map* map = this->map();
+ Heap* heap = GetHeap();
+ if (map == heap->string_map()) {
+ this->set_map(heap->undetectable_string_map());
+ return true;
+ } else if (map == heap->ascii_string_map()) {
+ this->set_map(heap->undetectable_ascii_string_map());
+ return true;
+ }
+ // Rest cannot be marked as undetectable
+ return false;
+}
+
+
+bool String::IsUtf8EqualTo(Vector<const char> str, bool allow_prefix_match) {
+ int slen = length();
+ // Can't check exact length equality, but we can check bounds.
+ int str_len = str.length();
+ if (!allow_prefix_match &&
+ (str_len < slen ||
+ str_len > slen*static_cast<int>(unibrow::Utf8::kMaxEncodedSize))) {
+ return false;
+ }
+ int i;
+ unsigned remaining_in_str = static_cast<unsigned>(str_len);
+ const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(str.start());
+ for (i = 0; i < slen && remaining_in_str > 0; i++) {
+ unsigned cursor = 0;
+ uint32_t r = unibrow::Utf8::ValueOf(utf8_data, remaining_in_str, &cursor);
+ ASSERT(cursor > 0 && cursor <= remaining_in_str);
+ if (r > unibrow::Utf16::kMaxNonSurrogateCharCode) {
+ if (i > slen - 1) return false;
+ if (Get(i++) != unibrow::Utf16::LeadSurrogate(r)) return false;
+ if (Get(i) != unibrow::Utf16::TrailSurrogate(r)) return false;
+ } else {
+ if (Get(i) != r) return false;
+ }
+ utf8_data += cursor;
+ remaining_in_str -= cursor;
+ }
+ return (allow_prefix_match || i == slen) && remaining_in_str == 0;
+}
+
+
+bool String::IsOneByteEqualTo(Vector<const uint8_t> str) {
+ int slen = length();
+ if (str.length() != slen) return false;
+ DisallowHeapAllocation no_gc;
+ FlatContent content = GetFlatContent();
+ if (content.IsAscii()) {
+ return CompareChars(content.ToOneByteVector().start(),
+ str.start(), slen) == 0;
+ }
+ for (int i = 0; i < slen; i++) {
+ if (Get(i) != static_cast<uint16_t>(str[i])) return false;
+ }
+ return true;
+}
+
+
+bool String::IsTwoByteEqualTo(Vector<const uc16> str) {
+ int slen = length();
+ if (str.length() != slen) return false;
+ DisallowHeapAllocation no_gc;
+ FlatContent content = GetFlatContent();
+ if (content.IsTwoByte()) {
+ return CompareChars(content.ToUC16Vector().start(), str.start(), slen) == 0;
+ }
+ for (int i = 0; i < slen; i++) {
+ if (Get(i) != str[i]) return false;
+ }
+ return true;
+}
+
+
+class IteratingStringHasher: public StringHasher {
+ public:
+ static inline uint32_t Hash(String* string, uint32_t seed) {
+ const unsigned len = static_cast<unsigned>(string->length());
+ IteratingStringHasher hasher(len, seed);
+ if (hasher.has_trivial_hash()) {
+ return hasher.GetHashField();
+ }
+ int32_t type = string->map()->instance_type();
+ ConsStringNullOp null_op;
+ String::Visit(string, 0, hasher, null_op, type, len);
+ // Flat strings terminate immediately.
+ if (hasher.consumed_ == len) {
+ ASSERT(!string->IsConsString());
+ return hasher.GetHashField();
+ }
+ ASSERT(string->IsConsString());
+ // This is a ConsString, iterate across it.
+ ConsStringIteratorOp op;
+ unsigned offset = 0;
+ unsigned leaf_length = len;
+ string = op.Operate(string, &offset, &type, &leaf_length);
+ while (true) {
+ ASSERT(hasher.consumed_ < len);
+ String::Visit(string, 0, hasher, null_op, type, leaf_length);
+ if (hasher.consumed_ == len) break;
+ string = op.ContinueOperation(&type, &leaf_length);
+ // This should be taken care of by the length check.
+ ASSERT(string != NULL);
+ }
+ return hasher.GetHashField();
+ }
+ inline void VisitOneByteString(const uint8_t* chars, unsigned length) {
+ AddCharacters(chars, static_cast<int>(length));
+ consumed_ += length;
+ }
+ inline void VisitTwoByteString(const uint16_t* chars, unsigned length) {
+ AddCharacters(chars, static_cast<int>(length));
+ consumed_ += length;
+ }
+
+ private:
+ inline IteratingStringHasher(int len, uint32_t seed)
+ : StringHasher(len, seed),
+ consumed_(0) {}
+ unsigned consumed_;
+ DISALLOW_COPY_AND_ASSIGN(IteratingStringHasher);
+};
+
+
+uint32_t String::ComputeAndSetHash() {
+ // Should only be called if hash code has not yet been computed.
+ ASSERT(!HasHashCode());
+
+ // Store the hash code in the object.
+ uint32_t field = IteratingStringHasher::Hash(this, GetHeap()->HashSeed());
+ set_hash_field(field);
+
+ // Check the hash code is there.
+ ASSERT(HasHashCode());
+ uint32_t result = field >> kHashShift;
+ ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
+ return result;
+}
+
+
+bool String::ComputeArrayIndex(uint32_t* index) {
+ int length = this->length();
+ if (length == 0 || length > kMaxArrayIndexSize) return false;
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(this, &op);
+ uint16_t ch = stream.GetNext();
+
+ // If the string begins with a '0' character, it must only consist
+ // of it to be a legal array index.
+ if (ch == '0') {
+ *index = 0;
+ return length == 1;
+ }
+
+ // Convert string to uint32 array index; character by character.
+ int d = ch - '0';
+ if (d < 0 || d > 9) return false;
+ uint32_t result = d;
+ while (stream.HasMore()) {
+ d = stream.GetNext() - '0';
+ if (d < 0 || d > 9) return false;
+ // Check that the new result is below the 32 bit limit.
+ if (result > 429496729U - ((d > 5) ? 1 : 0)) return false;
+ result = (result * 10) + d;
+ }
+
+ *index = result;
+ return true;
+}
+
+
+bool String::SlowAsArrayIndex(uint32_t* index) {
+ if (length() <= kMaxCachedArrayIndexLength) {
+ Hash(); // force computation of hash code
+ uint32_t field = hash_field();
+ if ((field & kIsNotArrayIndexMask) != 0) return false;
+ // Isolate the array index form the full hash field.
+ *index = (kArrayIndexHashMask & field) >> kHashShift;
+ return true;
+ } else {
+ return ComputeArrayIndex(index);
+ }
+}
+
+
+Handle<String> SeqString::Truncate(Handle<SeqString> string, int new_length) {
+ int new_size, old_size;
+ int old_length = string->length();
+ if (old_length <= new_length) return string;
+
+ if (string->IsSeqOneByteString()) {
+ old_size = SeqOneByteString::SizeFor(old_length);
+ new_size = SeqOneByteString::SizeFor(new_length);
+ } else {
+ ASSERT(string->IsSeqTwoByteString());
+ old_size = SeqTwoByteString::SizeFor(old_length);
+ new_size = SeqTwoByteString::SizeFor(new_length);
+ }
+
+ int delta = old_size - new_size;
+ string->set_length(new_length);
+
+ Address start_of_string = string->address();
+ ASSERT_OBJECT_ALIGNED(start_of_string);
+ ASSERT_OBJECT_ALIGNED(start_of_string + new_size);
+
+ Heap* heap = string->GetHeap();
+ NewSpace* newspace = heap->new_space();
+ if (newspace->Contains(start_of_string) &&
+ newspace->top() == start_of_string + old_size) {
+ // Last allocated object in new space. Simply lower allocation top.
+ *(newspace->allocation_top_address()) = start_of_string + new_size;
+ } else {
+ // Sizes are pointer size aligned, so that we can use filler objects
+ // that are a multiple of pointer size.
+ heap->CreateFillerObjectAt(start_of_string + new_size, delta);
+ }
+ if (Marking::IsBlack(Marking::MarkBitFrom(start_of_string))) {
+ MemoryChunk::IncrementLiveBytesFromMutator(start_of_string, -delta);
+ }
+
+
+ if (new_length == 0) return heap->isolate()->factory()->empty_string();
+ return string;
+}
+
+
+AllocationMemento* AllocationMemento::FindForJSObject(JSObject* object) {
+ // Currently, AllocationMemento objects are only allocated immediately
+ // after JSArrays in NewSpace, and detecting whether a JSArray has one
+ // involves carefully checking the object immediately after the JSArray
+ // (if there is one) to see if it's an AllocationMemento.
+ if (FLAG_track_allocation_sites && object->GetHeap()->InNewSpace(object)) {
+ Address ptr_end = (reinterpret_cast<Address>(object) - kHeapObjectTag) +
+ object->Size();
+ if ((ptr_end + AllocationMemento::kSize) <=
+ object->GetHeap()->NewSpaceTop()) {
+ // There is room in newspace for allocation info. Do we have some?
+ Map** possible_allocation_memento_map =
+ reinterpret_cast<Map**>(ptr_end);
+ if (*possible_allocation_memento_map ==
+ object->GetHeap()->allocation_memento_map()) {
+ AllocationMemento* memento = AllocationMemento::cast(
+ reinterpret_cast<Object*>(ptr_end + 1));
+ return memento;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
+ // For array indexes mix the length into the hash as an array index could
+ // be zero.
+ ASSERT(length > 0);
+ ASSERT(length <= String::kMaxArrayIndexSize);
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+
+ value <<= String::kHashShift;
+ value |= length << String::kArrayIndexHashLengthShift;
+
+ ASSERT((value & String::kIsNotArrayIndexMask) == 0);
+ ASSERT((length > String::kMaxCachedArrayIndexLength) ||
+ (value & String::kContainsCachedArrayIndexMask) == 0);
+ return value;
+}
+
+
+uint32_t StringHasher::GetHashField() {
+ if (length_ <= String::kMaxHashCalcLength) {
+ if (is_array_index_) {
+ return MakeArrayIndexHash(array_index_, length_);
+ }
+ return (GetHashCore(raw_running_hash_) << String::kHashShift) |
+ String::kIsNotArrayIndexMask;
+ } else {
+ return (length_ << String::kHashShift) | String::kIsNotArrayIndexMask;
+ }
+}
+
+
+uint32_t StringHasher::ComputeUtf8Hash(Vector<const char> chars,
+ uint32_t seed,
+ int* utf16_length_out) {
+ int vector_length = chars.length();
+ // Handle some edge cases
+ if (vector_length <= 1) {
+ ASSERT(vector_length == 0 ||
+ static_cast<uint8_t>(chars.start()[0]) <=
+ unibrow::Utf8::kMaxOneByteChar);
+ *utf16_length_out = vector_length;
+ return HashSequentialString(chars.start(), vector_length, seed);
+ }
+ // Start with a fake length which won't affect computation.
+ // It will be updated later.
+ StringHasher hasher(String::kMaxArrayIndexSize, seed);
+ unsigned remaining = static_cast<unsigned>(vector_length);
+ const uint8_t* stream = reinterpret_cast<const uint8_t*>(chars.start());
+ int utf16_length = 0;
+ bool is_index = true;
+ ASSERT(hasher.is_array_index_);
+ while (remaining > 0) {
+ unsigned consumed = 0;
+ uint32_t c = unibrow::Utf8::ValueOf(stream, remaining, &consumed);
+ ASSERT(consumed > 0 && consumed <= remaining);
+ stream += consumed;
+ remaining -= consumed;
+ bool is_two_characters = c > unibrow::Utf16::kMaxNonSurrogateCharCode;
+ utf16_length += is_two_characters ? 2 : 1;
+ // No need to keep hashing. But we do need to calculate utf16_length.
+ if (utf16_length > String::kMaxHashCalcLength) continue;
+ if (is_two_characters) {
+ uint16_t c1 = unibrow::Utf16::LeadSurrogate(c);
+ uint16_t c2 = unibrow::Utf16::TrailSurrogate(c);
+ hasher.AddCharacter(c1);
+ hasher.AddCharacter(c2);
+ if (is_index) is_index = hasher.UpdateIndex(c1);
+ if (is_index) is_index = hasher.UpdateIndex(c2);
+ } else {
+ hasher.AddCharacter(c);
+ if (is_index) is_index = hasher.UpdateIndex(c);
+ }
+ }
+ *utf16_length_out = static_cast<int>(utf16_length);
+ // Must set length here so that hash computation is correct.
+ hasher.length_ = utf16_length;
+ return hasher.GetHashField();
+}
+
+
+MaybeObject* String::SubString(int start, int end, PretenureFlag pretenure) {
+ Heap* heap = GetHeap();
+ if (start == 0 && end == length()) return this;
+ MaybeObject* result = heap->AllocateSubString(this, start, end, pretenure);
+ return result;
+}
+
+
+void String::PrintOn(FILE* file) {
+ int length = this->length();
+ for (int i = 0; i < length; i++) {
+ PrintF(file, "%c", Get(i));
+ }
+}
+
+
+static void TrimEnumCache(Heap* heap, Map* map, DescriptorArray* descriptors) {
+ int live_enum = map->EnumLength();
+ if (live_enum == Map::kInvalidEnumCache) {
+ live_enum = map->NumberOfDescribedProperties(OWN_DESCRIPTORS, DONT_ENUM);
+ }
+ if (live_enum == 0) return descriptors->ClearEnumCache();
+
+ FixedArray* enum_cache = descriptors->GetEnumCache();
+
+ int to_trim = enum_cache->length() - live_enum;
+ if (to_trim <= 0) return;
+ RightTrimFixedArray<FROM_GC>(heap, descriptors->GetEnumCache(), to_trim);
+
+ if (!descriptors->HasEnumIndicesCache()) return;
+ FixedArray* enum_indices_cache = descriptors->GetEnumIndicesCache();
+ RightTrimFixedArray<FROM_GC>(heap, enum_indices_cache, to_trim);
+}
+
+
+static void TrimDescriptorArray(Heap* heap,
+ Map* map,
+ DescriptorArray* descriptors,
+ int number_of_own_descriptors) {
+ int number_of_descriptors = descriptors->number_of_descriptors_storage();
+ int to_trim = number_of_descriptors - number_of_own_descriptors;
+ if (to_trim == 0) return;
+
+ RightTrimFixedArray<FROM_GC>(
+ heap, descriptors, to_trim * DescriptorArray::kDescriptorSize);
+ descriptors->SetNumberOfDescriptors(number_of_own_descriptors);
+
+ if (descriptors->HasEnumCache()) TrimEnumCache(heap, map, descriptors);
+ descriptors->Sort();
+}
+
+
+// Clear a possible back pointer in case the transition leads to a dead map.
+// Return true in case a back pointer has been cleared and false otherwise.
+static bool ClearBackPointer(Heap* heap, Map* target) {
+ if (Marking::MarkBitFrom(target).Get()) return false;
+ target->SetBackPointer(heap->undefined_value(), SKIP_WRITE_BARRIER);
+ return true;
+}
+
+
+// TODO(mstarzinger): This method should be moved into MarkCompactCollector,
+// because it cannot be called from outside the GC and we already have methods
+// depending on the transitions layout in the GC anyways.
+void Map::ClearNonLiveTransitions(Heap* heap) {
+ // If there are no transitions to be cleared, return.
+ // TODO(verwaest) Should be an assert, otherwise back pointers are not
+ // properly cleared.
+ if (!HasTransitionArray()) return;
+
+ TransitionArray* t = transitions();
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+
+ int transition_index = 0;
+
+ DescriptorArray* descriptors = instance_descriptors();
+ bool descriptors_owner_died = false;
+
+ // Compact all live descriptors to the left.
+ for (int i = 0; i < t->number_of_transitions(); ++i) {
+ Map* target = t->GetTarget(i);
+ if (ClearBackPointer(heap, target)) {
+ if (target->instance_descriptors() == descriptors) {
+ descriptors_owner_died = true;
+ }
+ } else {
+ if (i != transition_index) {
+ Name* key = t->GetKey(i);
+ t->SetKey(transition_index, key);
+ Object** key_slot = t->GetKeySlot(transition_index);
+ collector->RecordSlot(key_slot, key_slot, key);
+ // Target slots do not need to be recorded since maps are not compacted.
+ t->SetTarget(transition_index, t->GetTarget(i));
+ }
+ transition_index++;
+ }
+ }
+
+ // If there are no transitions to be cleared, return.
+ // TODO(verwaest) Should be an assert, otherwise back pointers are not
+ // properly cleared.
+ if (transition_index == t->number_of_transitions()) return;
+
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+
+ if (descriptors_owner_died) {
+ if (number_of_own_descriptors > 0) {
+ TrimDescriptorArray(heap, this, descriptors, number_of_own_descriptors);
+ ASSERT(descriptors->number_of_descriptors() == number_of_own_descriptors);
+ } else {
+ ASSERT(descriptors == GetHeap()->empty_descriptor_array());
+ }
+ }
+
+ int trim = t->number_of_transitions() - transition_index;
+ if (trim > 0) {
+ RightTrimFixedArray<FROM_GC>(heap, t, t->IsSimpleTransition()
+ ? trim : trim * TransitionArray::kTransitionSize);
+ }
+}
+
+
+int Map::Hash() {
+ // For performance reasons we only hash the 3 most variable fields of a map:
+ // constructor, prototype and bit_field2.
+
+ // Shift away the tag.
+ int hash = (static_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(constructor())) >> 2);
+
+ // XOR-ing the prototype and constructor directly yields too many zero bits
+ // when the two pointers are close (which is fairly common).
+ // To avoid this we shift the prototype 4 bits relatively to the constructor.
+ hash ^= (static_cast<uint32_t>(
+ reinterpret_cast<uintptr_t>(prototype())) << 2);
+
+ return hash ^ (hash >> 16) ^ bit_field2();
+}
+
+
+static bool CheckEquivalent(Map* first, Map* second) {
+ return
+ first->constructor() == second->constructor() &&
+ first->prototype() == second->prototype() &&
+ first->instance_type() == second->instance_type() &&
+ first->bit_field() == second->bit_field() &&
+ first->bit_field2() == second->bit_field2() &&
+ first->is_observed() == second->is_observed() &&
+ first->function_with_prototype() == second->function_with_prototype();
+}
+
+
+bool Map::EquivalentToForTransition(Map* other) {
+ return CheckEquivalent(this, other);
+}
+
+
+bool Map::EquivalentToForNormalization(Map* other,
+ PropertyNormalizationMode mode) {
+ int properties = mode == CLEAR_INOBJECT_PROPERTIES
+ ? 0 : other->inobject_properties();
+ return CheckEquivalent(this, other) && inobject_properties() == properties;
+}
+
+
+void JSFunction::JSFunctionIterateBody(int object_size, ObjectVisitor* v) {
+ // Iterate over all fields in the body but take care in dealing with
+ // the code entry.
+ IteratePointers(v, kPropertiesOffset, kCodeEntryOffset);
+ v->VisitCodeEntry(this->address() + kCodeEntryOffset);
+ IteratePointers(v, kCodeEntryOffset + kPointerSize, object_size);
+}
+
+
+void JSFunction::MarkForLazyRecompilation() {
+ ASSERT(is_compiled() || GetIsolate()->DebuggerHasBreakPoints());
+ ASSERT(!IsOptimized());
+ ASSERT(shared()->allows_lazy_compilation() ||
+ code()->optimizable());
+ ASSERT(!shared()->is_generator());
+ set_code_no_write_barrier(
+ GetIsolate()->builtins()->builtin(Builtins::kLazyRecompile));
+ // No write barrier required, since the builtin is part of the root set.
+}
+
+
+void JSFunction::MarkForParallelRecompilation() {
+ ASSERT(is_compiled() || GetIsolate()->DebuggerHasBreakPoints());
+ ASSERT(!IsOptimized());
+ ASSERT(shared()->allows_lazy_compilation() || code()->optimizable());
+ ASSERT(!shared()->is_generator());
+ ASSERT(FLAG_parallel_recompilation);
+ if (FLAG_trace_parallel_recompilation) {
+ PrintF(" ** Marking ");
+ PrintName();
+ PrintF(" for parallel recompilation.\n");
+ }
+ set_code_no_write_barrier(
+ GetIsolate()->builtins()->builtin(Builtins::kParallelRecompile));
+ // No write barrier required, since the builtin is part of the root set.
+}
+
+
+void JSFunction::MarkForInstallingRecompiledCode() {
+ // The debugger could have switched the builtin to lazy compile.
+ // In that case, simply carry on. It will be dealt with later.
+ ASSERT(!IsOptimized());
+ ASSERT(shared()->allows_lazy_compilation() || code()->optimizable());
+ ASSERT(FLAG_parallel_recompilation);
+ set_code_no_write_barrier(
+ GetIsolate()->builtins()->builtin(Builtins::kInstallRecompiledCode));
+ // No write barrier required, since the builtin is part of the root set.
+}
+
+
+void JSFunction::MarkInRecompileQueue() {
+ // We can only arrive here via the parallel-recompilation builtin. If
+ // break points were set, the code would point to the lazy-compile builtin.
+ ASSERT(!GetIsolate()->DebuggerHasBreakPoints());
+ ASSERT(IsMarkedForParallelRecompilation() && !IsOptimized());
+ ASSERT(shared()->allows_lazy_compilation() || code()->optimizable());
+ ASSERT(FLAG_parallel_recompilation);
+ if (FLAG_trace_parallel_recompilation) {
+ PrintF(" ** Queueing ");
+ PrintName();
+ PrintF(" for parallel recompilation.\n");
+ }
+ set_code_no_write_barrier(
+ GetIsolate()->builtins()->builtin(Builtins::kInRecompileQueue));
+ // No write barrier required, since the builtin is part of the root set.
+}
+
+
+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) {
+ ASSERT(shared->allows_lazy_compilation_without_context());
+ CompilationInfoWithZone info(shared);
+ return CompileLazyHelper(&info, flag);
+}
+
+
+void SharedFunctionInfo::AddToOptimizedCodeMap(
+ Handle<SharedFunctionInfo> shared,
+ Handle<Context> native_context,
+ Handle<Code> code,
+ Handle<FixedArray> literals) {
+ CALL_HEAP_FUNCTION_VOID(
+ shared->GetIsolate(),
+ shared->AddToOptimizedCodeMap(*native_context, *code, *literals));
+}
+
+
+MaybeObject* SharedFunctionInfo::AddToOptimizedCodeMap(Context* native_context,
+ Code* code,
+ FixedArray* literals) {
+ ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION);
+ ASSERT(native_context->IsNativeContext());
+ STATIC_ASSERT(kEntryLength == 3);
+ Heap* heap = GetHeap();
+ FixedArray* new_code_map;
+ Object* value = optimized_code_map();
+ if (value->IsSmi()) {
+ // No optimized code map.
+ ASSERT_EQ(0, Smi::cast(value)->value());
+ // Crate 3 entries per context {context, code, literals}.
+ MaybeObject* maybe = heap->AllocateFixedArray(kInitialLength);
+ if (!maybe->To(&new_code_map)) return maybe;
+ new_code_map->set(kEntriesStart + 0, native_context);
+ new_code_map->set(kEntriesStart + 1, code);
+ new_code_map->set(kEntriesStart + 2, literals);
+ } else {
+ // Copy old map and append one new entry.
+ FixedArray* old_code_map = FixedArray::cast(value);
+ ASSERT_EQ(-1, SearchOptimizedCodeMap(native_context));
+ int old_length = old_code_map->length();
+ int new_length = old_length + kEntryLength;
+ MaybeObject* maybe = old_code_map->CopySize(new_length);
+ if (!maybe->To(&new_code_map)) return maybe;
+ new_code_map->set(old_length + 0, native_context);
+ new_code_map->set(old_length + 1, code);
+ new_code_map->set(old_length + 2, literals);
+ // Zap the old map for the sake of the heap verifier.
+ if (Heap::ShouldZapGarbage()) {
+ Object** data = old_code_map->data_start();
+ MemsetPointer(data, heap->the_hole_value(), old_length);
+ }
+ }
+#ifdef DEBUG
+ for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) {
+ ASSERT(new_code_map->get(i)->IsNativeContext());
+ ASSERT(new_code_map->get(i + 1)->IsCode());
+ ASSERT(Code::cast(new_code_map->get(i + 1))->kind() ==
+ Code::OPTIMIZED_FUNCTION);
+ ASSERT(new_code_map->get(i + 2)->IsFixedArray());
+ }
+#endif
+ set_optimized_code_map(new_code_map);
+ return new_code_map;
+}
+
+
+void SharedFunctionInfo::InstallFromOptimizedCodeMap(JSFunction* function,
+ int index) {
+ ASSERT(index > kEntriesStart);
+ FixedArray* code_map = FixedArray::cast(optimized_code_map());
+ if (!bound()) {
+ FixedArray* cached_literals = FixedArray::cast(code_map->get(index + 1));
+ ASSERT(cached_literals != NULL);
+ function->set_literals(cached_literals);
+ }
+ Code* code = Code::cast(code_map->get(index));
+ ASSERT(code != NULL);
+ ASSERT(function->context()->native_context() == code_map->get(index - 1));
+ function->ReplaceCode(code);
+}
+
+
+void SharedFunctionInfo::ClearOptimizedCodeMap() {
+ FixedArray* code_map = FixedArray::cast(optimized_code_map());
+
+ // If the next map link slot is already used then the function was
+ // enqueued with code flushing and we remove it now.
+ if (!code_map->get(kNextMapIndex)->IsUndefined()) {
+ CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
+ flusher->EvictOptimizedCodeMap(this);
+ }
+
+ ASSERT(code_map->get(kNextMapIndex)->IsUndefined());
+ set_optimized_code_map(Smi::FromInt(0));
+}
+
+
+void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code,
+ const char* reason) {
+ if (optimized_code_map()->IsSmi()) return;
+
+ int i;
+ bool removed_entry = false;
+ FixedArray* code_map = FixedArray::cast(optimized_code_map());
+ for (i = kEntriesStart; i < code_map->length(); i += kEntryLength) {
+ ASSERT(code_map->get(i)->IsNativeContext());
+ if (Code::cast(code_map->get(i + 1)) == optimized_code) {
+ if (FLAG_trace_opt) {
+ PrintF("[evicting entry from optimizing code map (%s) for ", reason);
+ ShortPrint();
+ PrintF("]\n");
+ }
+ removed_entry = true;
+ break;
+ }
+ }
+ while (i < (code_map->length() - kEntryLength)) {
+ code_map->set(i, code_map->get(i + kEntryLength));
+ code_map->set(i + 1, code_map->get(i + 1 + kEntryLength));
+ code_map->set(i + 2, code_map->get(i + 2 + kEntryLength));
+ i += kEntryLength;
+ }
+ if (removed_entry) {
+ // Always trim even when array is cleared because of heap verifier.
+ RightTrimFixedArray<FROM_MUTATOR>(GetHeap(), code_map, kEntryLength);
+ if (code_map->length() == kEntriesStart) {
+ ClearOptimizedCodeMap();
+ }
+ }
+}
+
+
+void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) {
+ FixedArray* code_map = FixedArray::cast(optimized_code_map());
+ ASSERT(shrink_by % kEntryLength == 0);
+ ASSERT(shrink_by <= code_map->length() - kEntriesStart);
+ // Always trim even when array is cleared because of heap verifier.
+ RightTrimFixedArray<FROM_GC>(GetHeap(), code_map, shrink_by);
+ if (code_map->length() == kEntriesStart) {
+ ClearOptimizedCodeMap();
+ }
+}
+
+
+bool JSFunction::CompileLazy(Handle<JSFunction> function,
+ ClearExceptionFlag flag) {
+ bool result = true;
+ if (function->shared()->is_compiled()) {
+ function->ReplaceCode(function->shared()->code());
+ } else {
+ ASSERT(function->shared()->allows_lazy_compilation());
+ CompilationInfoWithZone info(function);
+ result = CompileLazyHelper(&info, flag);
+ ASSERT(!result || function->is_compiled());
+ }
+ return result;
+}
+
+
+bool JSFunction::CompileOptimized(Handle<JSFunction> function,
+ BailoutId osr_ast_id,
+ ClearExceptionFlag flag) {
+ CompilationInfoWithZone info(function);
+ info.SetOptimizing(osr_ast_id);
+ return CompileLazyHelper(&info, flag);
+}
+
+
+bool JSFunction::EnsureCompiled(Handle<JSFunction> function,
+ ClearExceptionFlag flag) {
+ return function->is_compiled() || CompileLazy(function, flag);
+}
+
+
+bool JSFunction::IsInlineable() {
+ if (IsBuiltin()) return false;
+ SharedFunctionInfo* shared_info = shared();
+ // Check that the function has a script associated with it.
+ if (!shared_info->script()->IsScript()) return false;
+ if (shared_info->optimization_disabled()) return false;
+ Code* code = shared_info->code();
+ if (code->kind() == Code::OPTIMIZED_FUNCTION) return true;
+ // If we never ran this (unlikely) then lets try to optimize it.
+ if (code->kind() != Code::FUNCTION) return true;
+ return code->optimizable();
+}
+
+
+void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
+ CALL_HEAP_FUNCTION_VOID(object->GetIsolate(), object->OptimizeAsPrototype());
+}
+
+
+MaybeObject* JSObject::OptimizeAsPrototype() {
+ if (IsGlobalObject()) return this;
+
+ // Make sure prototypes are fast objects and their maps have the bit set
+ // so they remain fast.
+ if (!HasFastProperties()) {
+ MaybeObject* new_proto = TransformToFastProperties(0);
+ if (new_proto->IsFailure()) return new_proto;
+ ASSERT(new_proto == this);
+ }
+ return this;
+}
+
+
+static MUST_USE_RESULT MaybeObject* CacheInitialJSArrayMaps(
+ Context* native_context, Map* initial_map) {
+ // Replace all of the cached initial array maps in the native context with
+ // the appropriate transitioned elements kind maps.
+ Heap* heap = native_context->GetHeap();
+ MaybeObject* maybe_maps =
+ heap->AllocateFixedArrayWithHoles(kElementsKindCount, TENURED);
+ FixedArray* maps;
+ if (!maybe_maps->To(&maps)) return maybe_maps;
+
+ Map* current_map = initial_map;
+ ElementsKind kind = current_map->elements_kind();
+ ASSERT(kind == GetInitialFastElementsKind());
+ maps->set(kind, current_map);
+ for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1;
+ i < kFastElementsKindCount; ++i) {
+ Map* new_map;
+ ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
+ if (current_map->HasElementsTransition()) {
+ new_map = current_map->elements_transition_map();
+ ASSERT(new_map->elements_kind() == next_kind);
+ } else {
+ MaybeObject* maybe_new_map =
+ current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ }
+ maps->set(next_kind, new_map);
+ current_map = new_map;
+ }
+ native_context->set_js_array_maps(maps);
+ return initial_map;
+}
+
+
+Handle<Object> CacheInitialJSArrayMaps(Handle<Context> native_context,
+ Handle<Map> initial_map) {
+ CALL_HEAP_FUNCTION(native_context->GetIsolate(),
+ CacheInitialJSArrayMaps(*native_context, *initial_map),
+ Object);
+}
+
+
+void JSFunction::SetInstancePrototype(Handle<JSFunction> function,
+ Handle<Object> value) {
+ ASSERT(value->IsJSReceiver());
+
+ // First some logic for the map of the prototype to make sure it is in fast
+ // mode.
+ if (value->IsJSObject()) {
+ JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
+ }
+
+ // Now some logic for the maps of the objects that are created by using this
+ // function as a constructor.
+ if (function->has_initial_map()) {
+ // If the function has allocated the initial map replace it with a
+ // copy containing the new prototype. Also complete any in-object
+ // slack tracking that is in progress at this point because it is
+ // still tracking the old copy.
+ if (function->shared()->IsInobjectSlackTrackingInProgress()) {
+ function->shared()->CompleteInobjectSlackTracking();
+ }
+ Handle<Map> new_map = Map::Copy(handle(function->initial_map()));
+ new_map->set_prototype(*value);
+
+ // If the function is used as the global Array function, cache the
+ // initial map (and transitioned versions) in the native context.
+ Context* native_context = function->context()->native_context();
+ Object* array_function = native_context->get(Context::ARRAY_FUNCTION_INDEX);
+ if (array_function->IsJSFunction() &&
+ *function == JSFunction::cast(array_function)) {
+ CacheInitialJSArrayMaps(handle(native_context), new_map);
+ }
+
+ function->set_initial_map(*new_map);
+ } 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
+ // prototype is put into the initial map where it belongs.
+ function->set_prototype_or_initial_map(*value);
+ }
+ function->GetHeap()->ClearInstanceofCache();
+}
+
+
+void JSFunction::SetPrototype(Handle<JSFunction> function,
+ Handle<Object> value) {
+ ASSERT(function->should_have_prototype());
+ Handle<Object> construct_prototype = value;
+
+ // If the value is not a JSReceiver, store the value in the map's
+ // constructor field so it can be accessed. Also, set the prototype
+ // used for constructing objects to the original object prototype.
+ // See ECMA-262 13.2.2.
+ if (!value->IsJSReceiver()) {
+ // Copy the map so this does not affect unrelated functions.
+ // Remove map transitions because they point to maps with a
+ // different prototype.
+ Handle<Map> new_map = Map::Copy(handle(function->map()));
+
+ function->set_map(*new_map);
+ new_map->set_constructor(*value);
+ new_map->set_non_instance_prototype(true);
+ Isolate* isolate = new_map->GetIsolate();
+ construct_prototype = handle(
+ isolate->context()->native_context()->initial_object_prototype(),
+ isolate);
+ } else {
+ function->map()->set_non_instance_prototype(false);
+ }
+
+ return SetInstancePrototype(function, construct_prototype);
+}
+
+
+void JSFunction::RemovePrototype() {
+ Context* native_context = context()->native_context();
+ Map* no_prototype_map = shared()->is_classic_mode()
+ ? native_context->function_without_prototype_map()
+ : native_context->strict_mode_function_without_prototype_map();
+
+ if (map() == no_prototype_map) return;
+
+ ASSERT(map() == (shared()->is_classic_mode()
+ ? native_context->function_map()
+ : native_context->strict_mode_function_map()));
+
+ set_map(no_prototype_map);
+ set_prototype_or_initial_map(no_prototype_map->GetHeap()->the_hole_value());
+}
+
+
+void JSFunction::SetInstanceClassName(String* name) {
+ shared()->set_instance_class_name(name);
+}
+
+
+void JSFunction::PrintName(FILE* out) {
+ SmartArrayPointer<char> name = shared()->DebugName()->ToCString();
+ PrintF(out, "%s", *name);
+}
+
+
+Context* JSFunction::NativeContextFromLiterals(FixedArray* literals) {
+ return Context::cast(literals->get(JSFunction::kLiteralNativeContextIndex));
+}
+
+
+bool JSFunction::PassesHydrogenFilter() {
+ String* name = shared()->DebugName();
+ // The filter string is a pattern that matches functions in this way:
+ // "*" all; the default
+ // "-" all but the top-level function
+ // "-name" all but the function "name"
+ // "" only the top-level function
+ // "name" only the function "name"
+ // "name*" only functions starting with "name"
+ if (*FLAG_hydrogen_filter != '*') {
+ Vector<const char> filter = CStrVector(FLAG_hydrogen_filter);
+ if (filter.length() == 0) return name->length() == 0;
+ if (filter[0] != '-' && name->IsUtf8EqualTo(filter)) return true;
+ if (filter[0] == '-' &&
+ !name->IsUtf8EqualTo(filter.SubVector(1, filter.length()))) {
+ return true;
+ }
+ if (filter[filter.length() - 1] == '*' &&
+ name->IsUtf8EqualTo(filter.SubVector(0, filter.length() - 1), true)) {
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+MaybeObject* Oddball::Initialize(const char* to_string,
+ Object* to_number,
+ byte kind) {
+ String* internalized_to_string;
+ { MaybeObject* maybe_string =
+ Isolate::Current()->heap()->InternalizeUtf8String(
+ CStrVector(to_string));
+ if (!maybe_string->To(&internalized_to_string)) return maybe_string;
+ }
+ set_to_string(internalized_to_string);
+ set_to_number(to_number);
+ set_kind(kind);
+ return this;
+}
+
+
+String* SharedFunctionInfo::DebugName() {
+ Object* n = name();
+ if (!n->IsString() || String::cast(n)->length() == 0) return inferred_name();
+ return String::cast(n);
+}
+
+
+bool SharedFunctionInfo::HasSourceCode() {
+ return !script()->IsUndefined() &&
+ !reinterpret_cast<Script*>(script())->source()->IsUndefined();
+}
+
+
+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());
+}
+
+
+int SharedFunctionInfo::SourceSize() {
+ return end_position() - start_position();
+}
+
+
+int SharedFunctionInfo::CalculateInstanceSize() {
+ int instance_size =
+ JSObject::kHeaderSize +
+ expected_nof_properties() * kPointerSize;
+ if (instance_size > JSObject::kMaxInstanceSize) {
+ instance_size = JSObject::kMaxInstanceSize;
+ }
+ return instance_size;
+}
+
+
+int SharedFunctionInfo::CalculateInObjectProperties() {
+ return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
+}
+
+
+// Support function for printing the source code to a StringStream
+// without any allocation in the heap.
+void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
+ int max_length) {
+ // For some native functions there is no source.
+ if (!HasSourceCode()) {
+ accumulator->Add("<No Source>");
+ return;
+ }
+
+ // Get the source for the script which this function came from.
+ // Don't use String::cast because we don't want more assertion errors while
+ // we are already creating a stack dump.
+ String* script_source =
+ reinterpret_cast<String*>(Script::cast(script())->source());
+
+ if (!script_source->LooksValid()) {
+ accumulator->Add("<Invalid Source>");
+ return;
+ }
+
+ if (!is_toplevel()) {
+ accumulator->Add("function ");
+ Object* name = this->name();
+ if (name->IsString() && String::cast(name)->length() > 0) {
+ accumulator->PrintName(name);
+ }
+ }
+
+ int len = end_position() - start_position();
+ if (len <= max_length || max_length < 0) {
+ accumulator->Put(script_source, start_position(), end_position());
+ } else {
+ accumulator->Put(script_source,
+ start_position(),
+ start_position() + max_length);
+ accumulator->Add("...\n");
+ }
+}
+
+
+static bool IsCodeEquivalent(Code* code, Code* recompiled) {
+ if (code->instruction_size() != recompiled->instruction_size()) return false;
+ ByteArray* code_relocation = code->relocation_info();
+ ByteArray* recompiled_relocation = recompiled->relocation_info();
+ int length = code_relocation->length();
+ if (length != recompiled_relocation->length()) return false;
+ int compare = memcmp(code_relocation->GetDataStartAddress(),
+ recompiled_relocation->GetDataStartAddress(),
+ length);
+ return compare == 0;
+}
+
+
+void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
+ ASSERT(!has_deoptimization_support());
+ DisallowHeapAllocation no_allocation;
+ Code* code = this->code();
+ if (IsCodeEquivalent(code, recompiled)) {
+ // Copy the deoptimization data from the recompiled code.
+ code->set_deoptimization_data(recompiled->deoptimization_data());
+ code->set_has_deoptimization_support(true);
+ } else {
+ // TODO(3025757): In case the recompiled isn't equivalent to the
+ // old code, we have to replace it. We should try to avoid this
+ // altogether because it flushes valuable type feedback by
+ // effectively resetting all IC state.
+ ReplaceCode(recompiled);
+ }
+ ASSERT(has_deoptimization_support());
+}
+
+
+void SharedFunctionInfo::DisableOptimization(BailoutReason reason) {
+ // Disable optimization for the shared function info and mark the
+ // code as non-optimizable. The marker on the shared function info
+ // is there because we flush non-optimized code thereby loosing the
+ // non-optimizable information for the code. When the code is
+ // regenerated and set on the shared function info it is marked as
+ // non-optimizable if optimization is disabled for the shared
+ // function info.
+ set_optimization_disabled(true);
+ // Code should be the lazy compilation stub or else unoptimized. If the
+ // latter, disable optimization for the code too.
+ ASSERT(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN);
+ if (code()->kind() == Code::FUNCTION) {
+ code()->set_optimizable(false);
+ }
+ if (FLAG_trace_opt) {
+ PrintF("[disabled optimization for ");
+ ShortPrint();
+ PrintF(", reason: %s]\n", GetBailoutReason(reason));
+ }
+}
+
+
+bool SharedFunctionInfo::VerifyBailoutId(BailoutId id) {
+ ASSERT(!id.IsNone());
+ Code* unoptimized = code();
+ DeoptimizationOutputData* data =
+ DeoptimizationOutputData::cast(unoptimized->deoptimization_data());
+ unsigned ignore = Deoptimizer::GetOutputInfo(data, id, this);
+ USE(ignore);
+ return true; // Return true if there was no ASSERT.
+}
+
+
+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);
+
+ // No tracking during the snapshot construction phase.
+ if (Serializer::enabled()) return;
+
+ if (map->unused_property_fields() == 0) return;
+
+ // Nonzero counter is a leftover from the previous attempt interrupted
+ // by GC, keep it.
+ if (construction_count() == 0) {
+ set_construction_count(kGenerousAllocationCount);
+ }
+ set_initial_map(map);
+ Builtins* builtins = map->GetHeap()->isolate()->builtins();
+ ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric),
+ construct_stub());
+ set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown));
+}
+
+
+// Called from GC, hence reinterpret_cast and unchecked accessors.
+void SharedFunctionInfo::DetachInitialMap() {
+ Map* map = reinterpret_cast<Map*>(initial_map());
+
+ // Make the map remember to restore the link if it survives the GC.
+ map->set_bit_field2(
+ map->bit_field2() | (1 << Map::kAttachedToSharedFunctionInfo));
+
+ // Undo state changes made by StartInobjectTracking (except the
+ // construction_count). This way if the initial map does not survive the GC
+ // 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.
+ Heap* heap = map->GetHeap();
+ set_initial_map(heap->undefined_value());
+ Builtins* builtins = heap->isolate()->builtins();
+ ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown),
+ *RawField(this, kConstructStubOffset));
+ set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric));
+ // It is safe to clear the flag: it will be set again if the map is live.
+ set_live_objects_may_exist(false);
+}
+
+
+// Called from GC, hence reinterpret_cast and unchecked accessors.
+void SharedFunctionInfo::AttachInitialMap(Map* map) {
+ map->set_bit_field2(
+ map->bit_field2() & ~(1 << Map::kAttachedToSharedFunctionInfo));
+
+ // Resume inobject slack tracking.
+ set_initial_map(map);
+ Builtins* builtins = map->GetHeap()->isolate()->builtins();
+ ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubGeneric),
+ *RawField(this, kConstructStubOffset));
+ set_construct_stub(builtins->builtin(Builtins::kJSConstructStubCountdown));
+ // The map survived the gc, so there may be objects referencing it.
+ set_live_objects_may_exist(true);
+}
+
+
+void SharedFunctionInfo::ResetForNewContext(int new_ic_age) {
+ code()->ClearInlineCaches();
+ set_ic_age(new_ic_age);
+ if (code()->kind() == Code::FUNCTION) {
+ code()->set_profiler_ticks(0);
+ if (optimization_disabled() &&
+ opt_count() >= FLAG_max_opt_count) {
+ // Re-enable optimizations if they were disabled due to opt_count limit.
+ set_optimization_disabled(false);
+ code()->set_optimizable(true);
+ }
+ set_opt_count(0);
+ set_deopt_count(0);
+ }
+}
+
+
+static void GetMinInobjectSlack(Map* map, void* data) {
+ int slack = map->unused_property_fields();
+ if (*reinterpret_cast<int*>(data) > slack) {
+ *reinterpret_cast<int*>(data) = slack;
+ }
+}
+
+
+static void ShrinkInstanceSize(Map* map, void* data) {
+ int slack = *reinterpret_cast<int*>(data);
+ map->set_inobject_properties(map->inobject_properties() - slack);
+ map->set_unused_property_fields(map->unused_property_fields() - slack);
+ map->set_instance_size(map->instance_size() - slack * kPointerSize);
+
+ // Visitor id might depend on the instance size, recalculate it.
+ map->set_visitor_id(StaticVisitorBase::GetVisitorId(map));
+}
+
+
+void SharedFunctionInfo::CompleteInobjectSlackTracking() {
+ ASSERT(live_objects_may_exist() && IsInobjectSlackTrackingInProgress());
+ Map* map = Map::cast(initial_map());
+
+ Heap* heap = map->GetHeap();
+ set_initial_map(heap->undefined_value());
+ Builtins* builtins = heap->isolate()->builtins();
+ ASSERT_EQ(builtins->builtin(Builtins::kJSConstructStubCountdown),
+ construct_stub());
+ set_construct_stub(builtins->builtin(Builtins::kJSConstructStubGeneric));
+
+ int slack = map->unused_property_fields();
+ map->TraverseTransitionTree(&GetMinInobjectSlack, &slack);
+ if (slack != 0) {
+ // Resize the initial map and all maps in its transition tree.
+ map->TraverseTransitionTree(&ShrinkInstanceSize, &slack);
+
+ // Give the correct expected_nof_properties to initial maps created later.
+ ASSERT(expected_nof_properties() >= slack);
+ set_expected_nof_properties(expected_nof_properties() - slack);
+ }
+}
+
+
+int SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context) {
+ ASSERT(native_context->IsNativeContext());
+ if (!FLAG_cache_optimized_code) return -1;
+ Object* value = optimized_code_map();
+ if (!value->IsSmi()) {
+ FixedArray* optimized_code_map = FixedArray::cast(value);
+ int length = optimized_code_map->length();
+ for (int i = kEntriesStart; i < length; i += kEntryLength) {
+ if (optimized_code_map->get(i) == native_context) {
+ return i + 1;
+ }
+ }
+ if (FLAG_trace_opt) {
+ PrintF("[didn't find optimized code in optimized code map for ");
+ ShortPrint();
+ PrintF("]\n");
+ }
+ }
+ return -1;
+}
+
+
+#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());
+ Object* old_target = target;
+ VisitPointer(&target);
+ CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
+}
+
+
+void ObjectVisitor::VisitCodeAgeSequence(RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsCodeAgeSequence(rinfo->rmode()));
+ Object* stub = rinfo->code_age_stub();
+ if (stub) {
+ VisitPointer(&stub);
+ }
+}
+
+
+void ObjectVisitor::VisitCodeEntry(Address entry_address) {
+ Object* code = Code::GetObjectFromEntryAddress(entry_address);
+ Object* old_code = code;
+ VisitPointer(&code);
+ if (code != old_code) {
+ Memory::Address_at(entry_address) = reinterpret_cast<Code*>(code)->entry();
+ }
+}
+
+
+void ObjectVisitor::VisitCell(RelocInfo* rinfo) {
+ ASSERT(rinfo->rmode() == RelocInfo::CELL);
+ Object* cell = rinfo->target_cell();
+ Object* old_cell = cell;
+ VisitPointer(&cell);
+ if (cell != old_cell) {
+ rinfo->set_target_cell(reinterpret_cast<Cell*>(cell));
+ }
+}
+
+
+void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
+ ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+ rinfo->IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence()));
+ Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
+ Object* old_target = target;
+ VisitPointer(&target);
+ CHECK_EQ(target, old_target); // VisitPointer doesn't change Code* *target.
+}
+
+
+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(GetHeap()->empty_byte_array());
+}
+
+
+void Code::Relocate(intptr_t delta) {
+ for (RelocIterator it(this, RelocInfo::kApplyMask); !it.done(); it.next()) {
+ it.rinfo()->apply(delta);
+ }
+ CPU::FlushICache(instruction_start(), instruction_size());
+}
+
+
+void Code::CopyFrom(const CodeDesc& desc) {
+ ASSERT(Marking::Color(this) == Marking::WHITE_OBJECT);
+
+ // copy code
+ CopyBytes(instruction_start(), desc.buffer,
+ static_cast<size_t>(desc.instr_size));
+
+ // copy reloc info
+ CopyBytes(relocation_start(),
+ desc.buffer + desc.buffer_size - desc.reloc_size,
+ static_cast<size_t>(desc.reloc_size));
+
+ // unbox handles and relocate
+ intptr_t delta = instruction_start() - desc.buffer;
+ int mode_mask = RelocInfo::kCodeTargetMask |
+ RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
+ RelocInfo::ModeMask(RelocInfo::CELL) |
+ RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) |
+ RelocInfo::kApplyMask;
+ // Needed to find target_object and runtime_entry on X64
+ Assembler* origin = desc.origin;
+ AllowDeferredHandleDereference embedding_raw_address;
+ for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
+ 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, SKIP_WRITE_BARRIER);
+ } else if (mode == RelocInfo::CELL) {
+ Handle<Cell> 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(),
+ SKIP_WRITE_BARRIER);
+ } else if (RelocInfo::IsRuntimeEntry(mode)) {
+ Address p = it.rinfo()->target_runtime_entry(origin);
+ it.rinfo()->set_target_runtime_entry(p, SKIP_WRITE_BARRIER);
+ } else {
+ it.rinfo()->apply(delta);
+ }
+ }
+ CPU::FlushICache(instruction_start(), instruction_size());
+}
+
+
+// Locate the source position which is closest to the address in the code. This
+// is using the source position information embedded in the relocation info.
+// The position returned is relative to the beginning of the script where the
+// source for this function is found.
+int Code::SourcePosition(Address pc) {
+ int distance = kMaxInt;
+ int position = RelocInfo::kNoPosition; // Initially no position found.
+ // Run through all the relocation info to find the best matching source
+ // position. All the code needs to be considered as the sequence of the
+ // instructions in the code does not necessarily follow the same order as the
+ // source.
+ RelocIterator it(this, RelocInfo::kPositionMask);
+ while (!it.done()) {
+ // Only look at positions after the current pc.
+ if (it.rinfo()->pc() < pc) {
+ // Get position and distance.
+
+ int dist = static_cast<int>(pc - it.rinfo()->pc());
+ int pos = static_cast<int>(it.rinfo()->data());
+ // If this position is closer than the current candidate or if it has the
+ // same distance as the current candidate and the position is higher then
+ // this position is the new candidate.
+ if ((dist < distance) ||
+ (dist == distance && pos > position)) {
+ position = pos;
+ distance = dist;
+ }
+ }
+ it.next();
+ }
+ return position;
+}
+
+
+// Same as Code::SourcePosition above except it only looks for statement
+// positions.
+int Code::SourceStatementPosition(Address pc) {
+ // First find the position as close as possible using all position
+ // information.
+ int position = SourcePosition(pc);
+ // Now find the closest statement position before the position.
+ int statement_position = 0;
+ RelocIterator it(this, RelocInfo::kPositionMask);
+ while (!it.done()) {
+ if (RelocInfo::IsStatementPosition(it.rinfo()->rmode())) {
+ int p = static_cast<int>(it.rinfo()->data());
+ if (statement_position < p && p <= position) {
+ statement_position = p;
+ }
+ }
+ it.next();
+ }
+ return statement_position;
+}
+
+
+SafepointEntry Code::GetSafepointEntry(Address pc) {
+ SafepointTable table(this);
+ return table.FindEntry(pc);
+}
+
+
+Object* Code::FindNthObject(int n, Map* match_map) {
+ ASSERT(is_inline_cache_stub());
+ DisallowHeapAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Object* object = info->target_object();
+ if (object->IsHeapObject()) {
+ if (HeapObject::cast(object)->map() == match_map) {
+ if (--n == 0) return object;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+Map* Code::FindFirstMap() {
+ Object* result = FindNthObject(1, GetHeap()->meta_map());
+ return (result != NULL) ? Map::cast(result) : NULL;
+}
+
+
+void Code::ReplaceNthObject(int n,
+ Map* match_map,
+ Object* replace_with) {
+ ASSERT(is_inline_cache_stub());
+ DisallowHeapAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Object* object = info->target_object();
+ if (object->IsHeapObject()) {
+ if (HeapObject::cast(object)->map() == match_map) {
+ if (--n == 0) {
+ info->set_target_object(replace_with);
+ return;
+ }
+ }
+ }
+ }
+ UNREACHABLE();
+}
+
+
+void Code::FindAllMaps(MapHandleList* maps) {
+ ASSERT(is_inline_cache_stub());
+ DisallowHeapAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Object* object = info->target_object();
+ if (object->IsMap()) maps->Add(Handle<Map>(Map::cast(object)));
+ }
+}
+
+
+void Code::ReplaceFirstMap(Map* replace_with) {
+ ReplaceNthObject(1, GetHeap()->meta_map(), replace_with);
+}
+
+
+Code* Code::FindFirstCode() {
+ ASSERT(is_inline_cache_stub());
+ DisallowHeapAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ return Code::GetCodeFromTargetAddress(info->target_address());
+ }
+ return NULL;
+}
+
+
+void Code::FindAllCode(CodeHandleList* code_list, int length) {
+ ASSERT(is_inline_cache_stub());
+ DisallowHeapAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET);
+ int i = 0;
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ if (i++ == length) return;
+ RelocInfo* info = it.rinfo();
+ Code* code = Code::GetCodeFromTargetAddress(info->target_address());
+ ASSERT(code->kind() == Code::STUB);
+ code_list->Add(Handle<Code>(code));
+ }
+ UNREACHABLE();
+}
+
+
+Name* Code::FindFirstName() {
+ ASSERT(is_inline_cache_stub());
+ DisallowHeapAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Object* object = info->target_object();
+ if (object->IsName()) return Name::cast(object);
+ }
+ return NULL;
+}
+
+
+void Code::ReplaceNthCell(int n, Cell* replace_with) {
+ ASSERT(is_inline_cache_stub());
+ DisallowHeapAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::CELL);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ if (--n == 0) {
+ info->set_target_cell(replace_with);
+ return;
+ }
+ }
+ UNREACHABLE();
+}
+
+
+void Code::ClearInlineCaches() {
+ int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
+ RelocInfo::ModeMask(RelocInfo::CONSTRUCT_CALL) |
+ RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID) |
+ RelocInfo::ModeMask(RelocInfo::CODE_TARGET_CONTEXT);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Code* target(Code::GetCodeFromTargetAddress(info->target_address()));
+ if (target->is_inline_cache_stub()) {
+ IC::Clear(info->pc());
+ }
+ }
+}
+
+
+void Code::ClearTypeFeedbackCells(Heap* heap) {
+ if (kind() != FUNCTION) return;
+ Object* raw_info = type_feedback_info();
+ if (raw_info->IsTypeFeedbackInfo()) {
+ TypeFeedbackCells* type_feedback_cells =
+ TypeFeedbackInfo::cast(raw_info)->type_feedback_cells();
+ for (int i = 0; i < type_feedback_cells->CellCount(); i++) {
+ Cell* cell = type_feedback_cells->GetCell(i);
+ // Don't clear AllocationSites
+ Object* value = cell->value();
+ if (value == NULL || !value->IsAllocationSite()) {
+ cell->set_value(TypeFeedbackCells::RawUninitializedSentinel(heap));
+ }
+ }
+ }
+}
+
+
+bool Code::allowed_in_shared_map_code_cache() {
+ return is_keyed_load_stub() || is_keyed_store_stub() ||
+ (is_compare_ic_stub() &&
+ ICCompareStub::CompareState(stub_info()) == CompareIC::KNOWN_OBJECT);
+}
+
+
+void Code::MakeCodeAgeSequenceYoung(byte* sequence) {
+ PatchPlatformCodeAge(sequence, kNoAge, NO_MARKING_PARITY);
+}
+
+
+void Code::MakeOlder(MarkingParity current_parity) {
+ byte* sequence = FindCodeAgeSequence();
+ if (sequence != NULL) {
+ Age age;
+ MarkingParity code_parity;
+ GetCodeAgeAndParity(sequence, &age, &code_parity);
+ if (age != kLastCodeAge && code_parity != current_parity) {
+ PatchPlatformCodeAge(sequence, static_cast<Age>(age + 1),
+ current_parity);
+ }
+ }
+}
+
+
+bool Code::IsOld() {
+ byte* sequence = FindCodeAgeSequence();
+ if (sequence == NULL) return false;
+ Age age;
+ MarkingParity parity;
+ GetCodeAgeAndParity(sequence, &age, &parity);
+ return age >= kSexagenarianCodeAge;
+}
+
+
+byte* Code::FindCodeAgeSequence() {
+ return FLAG_age_code &&
+ prologue_offset() != kPrologueOffsetNotSet &&
+ (kind() == OPTIMIZED_FUNCTION ||
+ (kind() == FUNCTION && !has_debug_break_slots()))
+ ? instruction_start() + prologue_offset()
+ : NULL;
+}
+
+
+int Code::GetAge() {
+ byte* sequence = FindCodeAgeSequence();
+ if (sequence == NULL) {
+ return Code::kNoAge;
+ }
+ Age age;
+ MarkingParity parity;
+ GetCodeAgeAndParity(sequence, &age, &parity);
+ return age;
+}
+
+
+void Code::GetCodeAgeAndParity(Code* code, Age* age,
+ MarkingParity* parity) {
+ Isolate* isolate = Isolate::Current();
+ Builtins* builtins = isolate->builtins();
+ Code* stub = NULL;
+#define HANDLE_CODE_AGE(AGE) \
+ stub = *builtins->Make##AGE##CodeYoungAgainEvenMarking(); \
+ if (code == stub) { \
+ *age = k##AGE##CodeAge; \
+ *parity = EVEN_MARKING_PARITY; \
+ return; \
+ } \
+ stub = *builtins->Make##AGE##CodeYoungAgainOddMarking(); \
+ if (code == stub) { \
+ *age = k##AGE##CodeAge; \
+ *parity = ODD_MARKING_PARITY; \
+ return; \
+ }
+ CODE_AGE_LIST(HANDLE_CODE_AGE)
+#undef HANDLE_CODE_AGE
+ UNREACHABLE();
+}
+
+
+Code* Code::GetCodeAgeStub(Age age, MarkingParity parity) {
+ Isolate* isolate = Isolate::Current();
+ Builtins* builtins = isolate->builtins();
+ switch (age) {
+#define HANDLE_CODE_AGE(AGE) \
+ case k##AGE##CodeAge: { \
+ Code* stub = parity == EVEN_MARKING_PARITY \
+ ? *builtins->Make##AGE##CodeYoungAgainEvenMarking() \
+ : *builtins->Make##AGE##CodeYoungAgainOddMarking(); \
+ return stub; \
+ }
+ CODE_AGE_LIST(HANDLE_CODE_AGE)
+#undef HANDLE_CODE_AGE
+ default:
+ UNREACHABLE();
+ break;
+ }
+ return NULL;
+}
+
+
+void Code::PrintDeoptLocation(int bailout_id) {
+ const char* last_comment = NULL;
+ int mask = RelocInfo::ModeMask(RelocInfo::COMMENT)
+ | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
+ for (RelocIterator it(this, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ if (info->rmode() == RelocInfo::COMMENT) {
+ last_comment = reinterpret_cast<const char*>(info->data());
+ } else if (last_comment != NULL) {
+ if ((bailout_id == Deoptimizer::GetDeoptimizationId(
+ GetIsolate(), info->target_address(), Deoptimizer::EAGER)) ||
+ (bailout_id == Deoptimizer::GetDeoptimizationId(
+ GetIsolate(), info->target_address(), Deoptimizer::SOFT))) {
+ CHECK(RelocInfo::IsRuntimeEntry(info->rmode()));
+ PrintF(" %s\n", last_comment);
+ return;
+ }
+ }
+ }
+}
+
+
+bool Code::CanDeoptAt(Address pc) {
+ DeoptimizationInputData* deopt_data =
+ DeoptimizationInputData::cast(deoptimization_data());
+ Address code_start_address = instruction_start();
+ for (int i = 0; i < deopt_data->DeoptCount(); i++) {
+ if (deopt_data->Pc(i)->value() == -1) continue;
+ Address address = code_start_address + deopt_data->Pc(i)->value();
+ if (address == pc) return true;
+ }
+ return false;
+}
+
+
+// Identify kind of code.
+const char* Code::Kind2String(Kind kind) {
+ switch (kind) {
+#define CASE(name) case name: return #name;
+ CODE_KIND_LIST(CASE)
+#undef CASE
+ case NUMBER_OF_KINDS: break;
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+#ifdef ENABLE_DISASSEMBLER
+
+void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
+ disasm::NameConverter converter;
+ int deopt_count = DeoptCount();
+ PrintF(out, "Deoptimization Input Data (deopt points = %d)\n", deopt_count);
+ if (0 == deopt_count) return;
+
+ PrintF(out, "%6s %6s %6s %6s %12s\n", "index", "ast id", "argc", "pc",
+ FLAG_print_code_verbose ? "commands" : "");
+ for (int i = 0; i < deopt_count; i++) {
+ PrintF(out, "%6d %6d %6d %6d",
+ i,
+ AstId(i).ToInt(),
+ ArgumentsStackHeight(i)->value(),
+ Pc(i)->value());
+
+ if (!FLAG_print_code_verbose) {
+ PrintF(out, "\n");
+ continue;
+ }
+ // Print details of the frame translation.
+ int translation_index = TranslationIndex(i)->value();
+ TranslationIterator iterator(TranslationByteArray(), translation_index);
+ Translation::Opcode opcode =
+ static_cast<Translation::Opcode>(iterator.Next());
+ ASSERT(Translation::BEGIN == opcode);
+ int frame_count = iterator.Next();
+ 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 !=
+ (opcode = static_cast<Translation::Opcode>(iterator.Next()))) {
+ PrintF(out, "%24s %s ", "", Translation::StringFor(opcode));
+
+ switch (opcode) {
+ case Translation::BEGIN:
+ UNREACHABLE();
+ break;
+
+ case Translation::JS_FRAME: {
+ int ast_id = iterator.Next();
+ int function_id = iterator.Next();
+ unsigned height = iterator.Next();
+ PrintF(out, "{ast_id=%d, function=", ast_id);
+ if (function_id != Translation::kSelfLiteralId) {
+ Object* function = LiteralArray()->get(function_id);
+ JSFunction::cast(function)->PrintName(out);
+ } else {
+ PrintF(out, "<self>");
+ }
+ PrintF(out, ", height=%u}", height);
+ break;
+ }
+
+ case Translation::COMPILED_STUB_FRAME: {
+ Code::Kind stub_kind = static_cast<Code::Kind>(iterator.Next());
+ PrintF(out, "{kind=%d}", stub_kind);
+ break;
+ }
+
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
+ case Translation::CONSTRUCT_STUB_FRAME: {
+ int function_id = iterator.Next();
+ JSFunction* function =
+ JSFunction::cast(LiteralArray()->get(function_id));
+ unsigned height = iterator.Next();
+ PrintF(out, "{function=");
+ function->PrintName(out);
+ PrintF(out, ", height=%u}", height);
+ break;
+ }
+
+ case Translation::GETTER_STUB_FRAME:
+ case Translation::SETTER_STUB_FRAME: {
+ int function_id = iterator.Next();
+ JSFunction* function =
+ JSFunction::cast(LiteralArray()->get(function_id));
+ PrintF(out, "{function=");
+ function->PrintName(out);
+ PrintF(out, "}");
+ break;
+ }
+
+ case Translation::REGISTER: {
+ int reg_code = iterator.Next();
+ PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
+ break;
+ }
+
+ case Translation::INT32_REGISTER: {
+ int reg_code = iterator.Next();
+ PrintF(out, "{input=%s}", converter.NameOfCPURegister(reg_code));
+ break;
+ }
+
+ case Translation::UINT32_REGISTER: {
+ int reg_code = iterator.Next();
+ PrintF(out, "{input=%s (unsigned)}",
+ converter.NameOfCPURegister(reg_code));
+ break;
+ }
+
+ case Translation::DOUBLE_REGISTER: {
+ int reg_code = iterator.Next();
+ PrintF(out, "{input=%s}",
+ DoubleRegister::AllocationIndexToString(reg_code));
+ break;
+ }
+
+ case Translation::STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ PrintF(out, "{input=%d}", input_slot_index);
+ break;
+ }
+
+ case Translation::INT32_STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ PrintF(out, "{input=%d}", input_slot_index);
+ break;
+ }
+
+ case Translation::UINT32_STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ PrintF(out, "{input=%d (unsigned)}", input_slot_index);
+ break;
+ }
+
+ case Translation::DOUBLE_STACK_SLOT: {
+ int input_slot_index = iterator.Next();
+ PrintF(out, "{input=%d}", input_slot_index);
+ break;
+ }
+
+ case Translation::LITERAL: {
+ unsigned literal_index = iterator.Next();
+ PrintF(out, "{literal_id=%u}", literal_index);
+ break;
+ }
+
+ case Translation::DUPLICATED_OBJECT: {
+ int object_index = iterator.Next();
+ PrintF(out, "{object_index=%d}", object_index);
+ break;
+ }
+
+ case Translation::ARGUMENTS_OBJECT:
+ case Translation::CAPTURED_OBJECT: {
+ int args_length = iterator.Next();
+ PrintF(out, "{length=%d}", args_length);
+ break;
+ }
+ }
+ PrintF(out, "\n");
+ }
+ }
+}
+
+
+void DeoptimizationOutputData::DeoptimizationOutputDataPrint(FILE* out) {
+ PrintF(out, "Deoptimization Output Data (deopt points = %d)\n",
+ this->DeoptPoints());
+ if (this->DeoptPoints() == 0) return;
+
+ PrintF("%6s %8s %s\n", "ast id", "pc", "state");
+ for (int i = 0; i < this->DeoptPoints(); i++) {
+ int pc_and_state = this->PcAndState(i)->value();
+ PrintF("%6d %8d %s\n",
+ this->AstId(i).ToInt(),
+ FullCodeGenerator::PcField::decode(pc_and_state),
+ FullCodeGenerator::State2String(
+ FullCodeGenerator::StateField::decode(pc_and_state)));
+ }
+}
+
+
+const char* Code::ICState2String(InlineCacheState state) {
+ switch (state) {
+ case UNINITIALIZED: return "UNINITIALIZED";
+ case PREMONOMORPHIC: return "PREMONOMORPHIC";
+ case MONOMORPHIC: return "MONOMORPHIC";
+ case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
+ case POLYMORPHIC: return "POLYMORPHIC";
+ case MEGAMORPHIC: return "MEGAMORPHIC";
+ case GENERIC: return "GENERIC";
+ case DEBUG_STUB: return "DEBUG_STUB";
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+const char* Code::StubType2String(StubType type) {
+ switch (type) {
+ case NORMAL: return "NORMAL";
+ case FIELD: return "FIELD";
+ case CONSTANT: return "CONSTANT";
+ case CALLBACKS: return "CALLBACKS";
+ case INTERCEPTOR: return "INTERCEPTOR";
+ case MAP_TRANSITION: return "MAP_TRANSITION";
+ case NONEXISTENT: return "NONEXISTENT";
+ }
+ UNREACHABLE(); // keep the compiler happy
+ return NULL;
+}
+
+
+void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) {
+ PrintF(out, "extra_ic_state = ");
+ const char* name = NULL;
+ switch (kind) {
+ case CALL_IC:
+ if (extra == STRING_INDEX_OUT_OF_BOUNDS) {
+ name = "STRING_INDEX_OUT_OF_BOUNDS";
+ }
+ break;
+ case STORE_IC:
+ case KEYED_STORE_IC:
+ if (extra == kStrictMode) {
+ name = "STRICT";
+ }
+ break;
+ default:
+ break;
+ }
+ if (name != NULL) {
+ PrintF(out, "%s\n", name);
+ } else {
+ PrintF(out, "%d\n", extra);
+ }
+}
+
+
+void Code::Disassemble(const char* name, FILE* out) {
+ PrintF(out, "kind = %s\n", Kind2String(kind()));
+ if (is_inline_cache_stub()) {
+ PrintF(out, "ic_state = %s\n", ICState2String(ic_state()));
+ PrintExtraICState(out, kind(), needs_extended_extra_ic_state(kind()) ?
+ extended_extra_ic_state() : extra_ic_state());
+ if (ic_state() == MONOMORPHIC) {
+ PrintF(out, "type = %s\n", StubType2String(type()));
+ }
+ if (is_call_stub() || is_keyed_call_stub()) {
+ PrintF(out, "argc = %d\n", arguments_count());
+ }
+ if (is_compare_ic_stub()) {
+ ASSERT(major_key() == CodeStub::CompareIC);
+ CompareIC::State left_state, right_state, handler_state;
+ Token::Value op;
+ ICCompareStub::DecodeMinorKey(stub_info(), &left_state, &right_state,
+ &handler_state, &op);
+ PrintF(out, "compare_state = %s*%s -> %s\n",
+ CompareIC::GetStateName(left_state),
+ CompareIC::GetStateName(right_state),
+ CompareIC::GetStateName(handler_state));
+ PrintF(out, "compare_operation = %s\n", Token::Name(op));
+ }
+ }
+ if ((name != NULL) && (name[0] != '\0')) {
+ PrintF(out, "name = %s\n", name);
+ }
+ if (kind() == OPTIMIZED_FUNCTION) {
+ PrintF(out, "stack_slots = %d\n", stack_slots());
+ }
+
+ PrintF(out, "Instructions (size = %d)\n", instruction_size());
+ Disassembler::Decode(out, this);
+ PrintF(out, "\n");
+
+ if (kind() == FUNCTION) {
+ DeoptimizationOutputData* data =
+ DeoptimizationOutputData::cast(this->deoptimization_data());
+ data->DeoptimizationOutputDataPrint(out);
+ } else if (kind() == OPTIMIZED_FUNCTION) {
+ DeoptimizationInputData* data =
+ DeoptimizationInputData::cast(this->deoptimization_data());
+ data->DeoptimizationInputDataPrint(out);
+ }
+ PrintF("\n");
+
+ if (is_crankshafted()) {
+ SafepointTable table(this);
+ PrintF(out, "Safepoints (size = %u)\n", table.size());
+ for (unsigned i = 0; i < table.length(); i++) {
+ unsigned pc_offset = table.GetPcOffset(i);
+ PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset);
+ table.PrintEntry(i);
+ PrintF(out, " (sp -> fp)");
+ SafepointEntry entry = table.GetEntry(i);
+ if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
+ PrintF(out, " %6d", entry.deoptimization_index());
+ } else {
+ PrintF(out, " <none>");
+ }
+ if (entry.argument_count() > 0) {
+ PrintF(out, " argc: %d", entry.argument_count());
+ }
+ PrintF(out, "\n");
+ }
+ PrintF(out, "\n");
+ } else if (kind() == FUNCTION) {
+ unsigned offset = back_edge_table_offset();
+ // If there is no back edge table, the "table start" will be at or after
+ // (due to alignment) the end of the instruction stream.
+ if (static_cast<int>(offset) < instruction_size()) {
+ FullCodeGenerator::BackEdgeTableIterator back_edges(this);
+
+ PrintF(out, "Back edges (size = %u)\n", back_edges.table_length());
+ PrintF(out, "ast_id pc_offset loop_depth\n");
+
+ for ( ; !back_edges.Done(); back_edges.Next()) {
+ PrintF(out, "%6d %9u %10u\n", back_edges.ast_id().ToInt(),
+ back_edges.pc_offset(),
+ back_edges.loop_depth());
+ }
+
+ PrintF(out, "\n");
+ }
+#ifdef OBJECT_PRINT
+ if (!type_feedback_info()->IsUndefined()) {
+ TypeFeedbackInfo::cast(type_feedback_info())->TypeFeedbackInfoPrint(out);
+ PrintF(out, "\n");
+ }
+#endif
+ }
+
+ PrintF("RelocInfo (size = %d)\n", relocation_size());
+ for (RelocIterator it(this); !it.done(); it.next()) {
+ it.rinfo()->Print(GetIsolate(), out);
+ }
+ PrintF(out, "\n");
+}
+#endif // ENABLE_DISASSEMBLER
+
+
+MaybeObject* JSObject::SetFastElementsCapacityAndLength(
+ int capacity,
+ int length,
+ SetFastElementsCapacitySmiMode smi_mode) {
+ Heap* heap = GetHeap();
+ // We should never end in here with a pixel or external array.
+ ASSERT(!HasExternalArrayElements());
+ ASSERT(!map()->is_observed());
+
+ // Allocate a new fast elements backing store.
+ FixedArray* new_elements;
+ MaybeObject* maybe = heap->AllocateUninitializedFixedArray(capacity);
+ if (!maybe->To(&new_elements)) return maybe;
+
+ ElementsKind elements_kind = GetElementsKind();
+ ElementsKind new_elements_kind;
+ // The resized array has FAST_*_SMI_ELEMENTS if the capacity mode forces it,
+ // or if it's allowed and the old elements array contained only SMIs.
+ bool has_fast_smi_elements =
+ (smi_mode == kForceSmiElements) ||
+ ((smi_mode == kAllowSmiElements) && HasFastSmiElements());
+ if (has_fast_smi_elements) {
+ if (IsHoleyElementsKind(elements_kind)) {
+ new_elements_kind = FAST_HOLEY_SMI_ELEMENTS;
+ } else {
+ new_elements_kind = FAST_SMI_ELEMENTS;
+ }
+ } else {
+ if (IsHoleyElementsKind(elements_kind)) {
+ new_elements_kind = FAST_HOLEY_ELEMENTS;
+ } else {
+ new_elements_kind = FAST_ELEMENTS;
+ }
+ }
+ FixedArrayBase* old_elements = elements();
+ ElementsAccessor* accessor = ElementsAccessor::ForKind(new_elements_kind);
+ MaybeObject* maybe_obj =
+ accessor->CopyElements(this, new_elements, elements_kind);
+ if (maybe_obj->IsFailure()) return maybe_obj;
+
+ if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) {
+ Map* new_map = map();
+ if (new_elements_kind != elements_kind) {
+ MaybeObject* maybe =
+ GetElementsTransitionMap(GetIsolate(), new_elements_kind);
+ if (!maybe->To(&new_map)) return maybe;
+ }
+ ValidateElements();
+ set_map_and_elements(new_map, new_elements);
+ } else {
+ FixedArray* parameter_map = FixedArray::cast(old_elements);
+ parameter_map->set(1, new_elements);
+ }
+
+ if (FLAG_trace_elements_transitions) {
+ PrintElementsTransition(stdout, elements_kind, old_elements,
+ GetElementsKind(), new_elements);
+ }
+
+ if (IsJSArray()) {
+ JSArray::cast(this)->set_length(Smi::FromInt(length));
+ }
+ return new_elements;
+}
+
+
+MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
+ int capacity,
+ int length) {
+ Heap* heap = GetHeap();
+ // We should never end in here with a pixel or external array.
+ ASSERT(!HasExternalArrayElements());
+ ASSERT(!map()->is_observed());
+
+ FixedArrayBase* elems;
+ { MaybeObject* maybe_obj =
+ heap->AllocateUninitializedFixedDoubleArray(capacity);
+ if (!maybe_obj->To(&elems)) return maybe_obj;
+ }
+
+ ElementsKind elements_kind = GetElementsKind();
+ ElementsKind new_elements_kind = elements_kind;
+ if (IsHoleyElementsKind(elements_kind)) {
+ new_elements_kind = FAST_HOLEY_DOUBLE_ELEMENTS;
+ } else {
+ new_elements_kind = FAST_DOUBLE_ELEMENTS;
+ }
+
+ Map* new_map;
+ { MaybeObject* maybe_obj =
+ GetElementsTransitionMap(heap->isolate(), new_elements_kind);
+ if (!maybe_obj->To(&new_map)) return maybe_obj;
+ }
+
+ FixedArrayBase* old_elements = elements();
+ ElementsAccessor* accessor = ElementsAccessor::ForKind(FAST_DOUBLE_ELEMENTS);
+ { MaybeObject* maybe_obj =
+ accessor->CopyElements(this, elems, elements_kind);
+ if (maybe_obj->IsFailure()) return maybe_obj;
+ }
+ if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) {
+ ValidateElements();
+ set_map_and_elements(new_map, elems);
+ } else {
+ FixedArray* parameter_map = FixedArray::cast(old_elements);
+ parameter_map->set(1, elems);
+ }
+
+ if (FLAG_trace_elements_transitions) {
+ PrintElementsTransition(stdout, elements_kind, old_elements,
+ GetElementsKind(), elems);
+ }
+
+ if (IsJSArray()) {
+ JSArray::cast(this)->set_length(Smi::FromInt(length));
+ }
+
+ return this;
+}
+
+
+MaybeObject* JSArray::Initialize(int capacity, int length) {
+ ASSERT(capacity >= 0);
+ return GetHeap()->AllocateJSArrayStorage(this, length, capacity,
+ INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
+}
+
+
+void JSArray::Expand(int required_size) {
+ GetIsolate()->factory()->SetElementsCapacityAndLength(
+ Handle<JSArray>(this), required_size, required_size);
+}
+
+
+// Returns false if the passed-in index is marked non-configurable,
+// which will cause the ES5 truncation operation to halt, and thus
+// no further old values need be collected.
+static bool GetOldValue(Isolate* isolate,
+ Handle<JSObject> object,
+ uint32_t index,
+ List<Handle<Object> >* old_values,
+ List<uint32_t>* indices) {
+ PropertyAttributes attributes = object->GetLocalElementAttribute(index);
+ ASSERT(attributes != ABSENT);
+ if (attributes == DONT_DELETE) return false;
+ old_values->Add(object->GetLocalElementAccessorPair(index) == NULL
+ ? Object::GetElement(object, index)
+ : Handle<Object>::cast(isolate->factory()->the_hole_value()));
+ indices->Add(index);
+ return true;
+}
+
+static void EnqueueSpliceRecord(Handle<JSArray> object,
+ uint32_t index,
+ Handle<JSArray> deleted,
+ uint32_t add_count) {
+ Isolate* isolate = object->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> index_object = isolate->factory()->NewNumberFromUint(index);
+ Handle<Object> add_count_object =
+ isolate->factory()->NewNumberFromUint(add_count);
+
+ Handle<Object> args[] =
+ { object, index_object, deleted, add_count_object };
+
+ bool threw;
+ Execution::Call(Handle<JSFunction>(isolate->observers_enqueue_splice()),
+ isolate->factory()->undefined_value(), ARRAY_SIZE(args), args,
+ &threw);
+ ASSERT(!threw);
+}
+
+
+static void BeginPerformSplice(Handle<JSArray> object) {
+ Isolate* isolate = object->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> args[] = { object };
+
+ bool threw;
+ Execution::Call(Handle<JSFunction>(isolate->observers_begin_perform_splice()),
+ isolate->factory()->undefined_value(), ARRAY_SIZE(args), args,
+ &threw);
+ ASSERT(!threw);
+}
+
+
+static void EndPerformSplice(Handle<JSArray> object) {
+ Isolate* isolate = object->GetIsolate();
+ HandleScope scope(isolate);
+ Handle<Object> args[] = { object };
+
+ bool threw;
+ Execution::Call(Handle<JSFunction>(isolate->observers_end_perform_splice()),
+ isolate->factory()->undefined_value(), ARRAY_SIZE(args), args,
+ &threw);
+ ASSERT(!threw);
+}
+
+
+MaybeObject* JSArray::SetElementsLength(Object* len) {
+ // We should never end in here with a pixel or external array.
+ ASSERT(AllowsSetElementsLength());
+ if (!(FLAG_harmony_observation && map()->is_observed()))
+ return GetElementsAccessor()->SetLength(this, len);
+
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSArray> self(this);
+ List<uint32_t> indices;
+ List<Handle<Object> > old_values;
+ Handle<Object> old_length_handle(self->length(), isolate);
+ Handle<Object> new_length_handle(len, isolate);
+ uint32_t old_length = 0;
+ CHECK(old_length_handle->ToArrayIndex(&old_length));
+ uint32_t new_length = 0;
+ if (!new_length_handle->ToArrayIndex(&new_length))
+ return Failure::InternalError();
+
+ // Observed arrays should always be in dictionary mode;
+ // if they were in fast mode, the below is slower than necessary
+ // as it iterates over the array backing store multiple times.
+ ASSERT(self->HasDictionaryElements());
+ static const PropertyAttributes kNoAttrFilter = NONE;
+ int num_elements = self->NumberOfLocalElements(kNoAttrFilter);
+ if (num_elements > 0) {
+ if (old_length == static_cast<uint32_t>(num_elements)) {
+ // Simple case for arrays without holes.
+ for (uint32_t i = old_length - 1; i + 1 > new_length; --i) {
+ if (!GetOldValue(isolate, self, i, &old_values, &indices)) break;
+ }
+ } else {
+ // For sparse arrays, only iterate over existing elements.
+ Handle<FixedArray> keys = isolate->factory()->NewFixedArray(num_elements);
+ self->GetLocalElementKeys(*keys, kNoAttrFilter);
+ while (num_elements-- > 0) {
+ uint32_t index = NumberToUint32(keys->get(num_elements));
+ if (index < new_length) break;
+ if (!GetOldValue(isolate, self, index, &old_values, &indices)) break;
+ }
+ }
+ }
+
+ MaybeObject* result =
+ self->GetElementsAccessor()->SetLength(*self, *new_length_handle);
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ CHECK(self->length()->ToArrayIndex(&new_length));
+ if (old_length == new_length) return *hresult;
+
+ BeginPerformSplice(self);
+
+ for (int i = 0; i < indices.length(); ++i) {
+ JSObject::EnqueueChangeRecord(
+ self, "deleted", isolate->factory()->Uint32ToString(indices[i]),
+ old_values[i]);
+ }
+ JSObject::EnqueueChangeRecord(
+ self, "updated", isolate->factory()->length_string(),
+ old_length_handle);
+
+ EndPerformSplice(self);
+
+ uint32_t index = Min(old_length, new_length);
+ uint32_t add_count = new_length > old_length ? new_length - old_length : 0;
+ uint32_t delete_count = new_length < old_length ? old_length - new_length : 0;
+ Handle<JSArray> deleted = isolate->factory()->NewJSArray(0);
+ if (delete_count > 0) {
+ for (int i = indices.length() - 1; i >= 0; i--) {
+ JSObject::SetElement(deleted, indices[i] - index, old_values[i], NONE,
+ kNonStrictMode);
+ }
+
+ SetProperty(deleted, isolate->factory()->length_string(),
+ isolate->factory()->NewNumberFromUint(delete_count),
+ NONE, kNonStrictMode);
+ }
+
+ EnqueueSpliceRecord(self, index, deleted, add_count);
+
+ return *hresult;
+}
+
+
+Handle<Map> Map::GetPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype) {
+ FixedArray* cache = map->GetPrototypeTransitions();
+ int number_of_transitions = map->NumberOfProtoTransitions();
+ const int proto_offset =
+ kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset;
+ const int map_offset = kProtoTransitionHeaderSize + kProtoTransitionMapOffset;
+ const int step = kProtoTransitionElementsPerEntry;
+ for (int i = 0; i < number_of_transitions; i++) {
+ if (cache->get(proto_offset + i * step) == *prototype) {
+ Object* result = cache->get(map_offset + i * step);
+ return Handle<Map>(Map::cast(result));
+ }
+ }
+ return Handle<Map>();
+}
+
+
+Handle<Map> Map::PutPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype,
+ Handle<Map> target_map) {
+ ASSERT(target_map->IsMap());
+ ASSERT(HeapObject::cast(*prototype)->map()->IsMap());
+ // Don't cache prototype transition if this map is shared.
+ if (map->is_shared() || !FLAG_cache_prototype_transitions) return map;
+
+ const int step = kProtoTransitionElementsPerEntry;
+ const int header = kProtoTransitionHeaderSize;
+
+ Handle<FixedArray> cache(map->GetPrototypeTransitions());
+ int capacity = (cache->length() - header) / step;
+ int transitions = map->NumberOfProtoTransitions() + 1;
+
+ if (transitions > capacity) {
+ if (capacity > kMaxCachedPrototypeTransitions) return map;
+
+ // Grow array by factor 2 over and above what we need.
+ Factory* factory = map->GetIsolate()->factory();
+ cache = factory->CopySizeFixedArray(cache, transitions * 2 * step + header);
+
+ CALL_AND_RETRY_OR_DIE(map->GetIsolate(),
+ map->SetPrototypeTransitions(*cache),
+ break,
+ return Handle<Map>());
+ }
+
+ // Reload number of transitions as GC might shrink them.
+ int last = map->NumberOfProtoTransitions();
+ int entry = header + last * step;
+
+ cache->set(entry + kProtoTransitionPrototypeOffset, *prototype);
+ cache->set(entry + kProtoTransitionMapOffset, *target_map);
+ map->SetNumberOfProtoTransitions(transitions);
+
+ return map;
+}
+
+
+void Map::ZapTransitions() {
+ TransitionArray* transition_array = transitions();
+ // TODO(mstarzinger): Temporarily use a slower version instead of the faster
+ // MemsetPointer to investigate a crasher. Switch back to MemsetPointer.
+ Object** data = transition_array->data_start();
+ Object* the_hole = GetHeap()->the_hole_value();
+ int length = transition_array->length();
+ for (int i = 0; i < length; i++) {
+ data[i] = the_hole;
+ }
+}
+
+
+void Map::ZapPrototypeTransitions() {
+ FixedArray* proto_transitions = GetPrototypeTransitions();
+ MemsetPointer(proto_transitions->data_start(),
+ GetHeap()->the_hole_value(),
+ proto_transitions->length());
+}
+
+
+void Map::AddDependentCompilationInfo(DependentCode::DependencyGroup group,
+ CompilationInfo* info) {
+ Handle<DependentCode> dep(dependent_code());
+ Handle<DependentCode> codes =
+ DependentCode::Insert(dep, group, info->object_wrapper());
+ if (*codes != dependent_code()) set_dependent_code(*codes);
+ info->dependencies(group)->Add(Handle<HeapObject>(this), info->zone());
+}
+
+
+void Map::AddDependentCode(DependentCode::DependencyGroup group,
+ Handle<Code> code) {
+ Handle<DependentCode> codes = DependentCode::Insert(
+ Handle<DependentCode>(dependent_code()), group, code);
+ if (*codes != dependent_code()) set_dependent_code(*codes);
+}
+
+
+DependentCode::GroupStartIndexes::GroupStartIndexes(DependentCode* entries) {
+ Recompute(entries);
+}
+
+
+void DependentCode::GroupStartIndexes::Recompute(DependentCode* entries) {
+ start_indexes_[0] = 0;
+ for (int g = 1; g <= kGroupCount; g++) {
+ int count = entries->number_of_entries(static_cast<DependencyGroup>(g - 1));
+ start_indexes_[g] = start_indexes_[g - 1] + count;
+ }
+}
+
+
+DependentCode* DependentCode::ForObject(Handle<HeapObject> object,
+ DependencyGroup group) {
+ AllowDeferredHandleDereference dependencies_are_safe;
+ if (group == DependentCode::kPropertyCellChangedGroup) {
+ return Handle<PropertyCell>::cast(object)->dependent_code();
+ }
+ return Handle<Map>::cast(object)->dependent_code();
+}
+
+
+Handle<DependentCode> DependentCode::Insert(Handle<DependentCode> entries,
+ DependencyGroup group,
+ Handle<Object> object) {
+ GroupStartIndexes starts(*entries);
+ int start = starts.at(group);
+ int end = starts.at(group + 1);
+ int number_of_entries = starts.number_of_entries();
+ if (start < end && entries->object_at(end - 1) == *object) {
+ // Do not append the compilation info if it is already in the array.
+ // It is sufficient to just check only the last element because
+ // we process embedded maps of an optimized code in one batch.
+ return entries;
+ }
+ if (entries->length() < kCodesStartIndex + number_of_entries + 1) {
+ Factory* factory = entries->GetIsolate()->factory();
+ int capacity = kCodesStartIndex + number_of_entries + 1;
+ if (capacity > 5) capacity = capacity * 5 / 4;
+ Handle<DependentCode> new_entries = Handle<DependentCode>::cast(
+ factory->CopySizeFixedArray(entries, capacity));
+ // The number of codes can change after GC.
+ starts.Recompute(*entries);
+ start = starts.at(group);
+ end = starts.at(group + 1);
+ number_of_entries = starts.number_of_entries();
+ for (int i = 0; i < number_of_entries; i++) {
+ entries->clear_at(i);
+ }
+ // If the old fixed array was empty, we need to reset counters of the
+ // new array.
+ if (number_of_entries == 0) {
+ for (int g = 0; g < kGroupCount; g++) {
+ new_entries->set_number_of_entries(static_cast<DependencyGroup>(g), 0);
+ }
+ }
+ entries = new_entries;
+ }
+ entries->ExtendGroup(group);
+ entries->set_object_at(end, *object);
+ entries->set_number_of_entries(group, end + 1 - start);
+ return entries;
+}
+
+
+void DependentCode::UpdateToFinishedCode(DependencyGroup group,
+ CompilationInfo* info,
+ Code* code) {
+ DisallowHeapAllocation no_gc;
+ AllowDeferredHandleDereference get_object_wrapper;
+ Foreign* info_wrapper = *info->object_wrapper();
+ GroupStartIndexes starts(this);
+ int start = starts.at(group);
+ int end = starts.at(group + 1);
+ for (int i = start; i < end; i++) {
+ if (object_at(i) == info_wrapper) {
+ set_object_at(i, code);
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ for (int i = start; i < end; i++) {
+ ASSERT(is_code_at(i) || compilation_info_at(i) != info);
+ }
+#endif
+}
+
+
+void DependentCode::RemoveCompilationInfo(DependentCode::DependencyGroup group,
+ CompilationInfo* info) {
+ DisallowHeapAllocation no_allocation;
+ AllowDeferredHandleDereference get_object_wrapper;
+ Foreign* info_wrapper = *info->object_wrapper();
+ GroupStartIndexes starts(this);
+ int start = starts.at(group);
+ int end = starts.at(group + 1);
+ // Find compilation info wrapper.
+ int info_pos = -1;
+ for (int i = start; i < end; i++) {
+ if (object_at(i) == info_wrapper) {
+ info_pos = i;
+ break;
+ }
+ }
+ if (info_pos == -1) return; // Not found.
+ int gap = info_pos;
+ // Use the last of each group to fill the gap in the previous group.
+ for (int i = group; i < kGroupCount; i++) {
+ int last_of_group = starts.at(i + 1) - 1;
+ ASSERT(last_of_group >= gap);
+ if (last_of_group == gap) continue;
+ copy(last_of_group, gap);
+ gap = last_of_group;
+ }
+ ASSERT(gap == starts.number_of_entries() - 1);
+ clear_at(gap); // Clear last gap.
+ set_number_of_entries(group, end - start - 1);
+
+#ifdef DEBUG
+ for (int i = start; i < end - 1; i++) {
+ ASSERT(is_code_at(i) || compilation_info_at(i) != info);
+ }
+#endif
+}
+
+
+bool DependentCode::Contains(DependencyGroup group, Code* code) {
+ GroupStartIndexes starts(this);
+ int number_of_entries = starts.number_of_entries();
+ for (int i = 0; i < number_of_entries; i++) {
+ if (object_at(i) == code) return true;
+ }
+ return false;
+}
+
+
+void DependentCode::DeoptimizeDependentCodeGroup(
+ Isolate* isolate,
+ DependentCode::DependencyGroup group) {
+ ASSERT(AllowCodeDependencyChange::IsAllowed());
+ DisallowHeapAllocation no_allocation_scope;
+ DependentCode::GroupStartIndexes starts(this);
+ int start = starts.at(group);
+ int end = starts.at(group + 1);
+ int code_entries = starts.number_of_entries();
+ if (start == end) return;
+
+ // Collect all the code to deoptimize.
+ Zone zone(isolate);
+ ZoneList<Code*> codes(end - start, &zone);
+ for (int i = start; i < end; i++) {
+ if (is_code_at(i)) {
+ Code* code = code_at(i);
+ if (!code->marked_for_deoptimization()) codes.Add(code, &zone);
+ } else {
+ CompilationInfo* info = compilation_info_at(i);
+ info->AbortDueToDependencyChange();
+ }
+ }
+ // Compact the array by moving all subsequent groups to fill in the new holes.
+ for (int src = end, dst = start; src < code_entries; src++, dst++) {
+ copy(src, dst);
+ }
+ // Now the holes are at the end of the array, zap them for heap-verifier.
+ int removed = end - start;
+ for (int i = code_entries - removed; i < code_entries; i++) {
+ clear_at(i);
+ }
+ set_number_of_entries(group, 0);
+ Deoptimizer::DeoptimizeCodeList(isolate, &codes);
+}
+
+
+Handle<Object> JSObject::SetPrototype(Handle<JSObject> object,
+ Handle<Object> value,
+ bool skip_hidden_prototypes) {
+#ifdef DEBUG
+ int size = object->Size();
+#endif
+
+ Isolate* isolate = object->GetIsolate();
+ Heap* heap = isolate->heap();
+ // Silently ignore the change if value is not a JSObject or null.
+ // SpiderMonkey behaves this way.
+ if (!value->IsJSReceiver() && !value->IsNull()) return value;
+
+ // From 8.6.2 Object Internal Methods
+ // ...
+ // In addition, if [[Extensible]] is false the value of the [[Class]] and
+ // [[Prototype]] internal properties of the object may not be modified.
+ // ...
+ // Implementation specific extensions that modify [[Class]], [[Prototype]]
+ // or [[Extensible]] must not violate the invariants defined in the preceding
+ // paragraph.
+ if (!object->map()->is_extensible()) {
+ Handle<Object> args[] = { object };
+ Handle<Object> error = isolate->factory()->NewTypeError(
+ "non_extensible_proto", HandleVector(args, ARRAY_SIZE(args)));
+ isolate->Throw(*error);
+ return Handle<Object>();
+ }
+
+ // Before we can set the prototype we need to be sure
+ // prototype cycles are prevented.
+ // 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(isolate)) {
+ if (JSReceiver::cast(pt) == *object) {
+ // Cycle detected.
+ Handle<Object> error = isolate->factory()->NewError(
+ "cyclic_proto", HandleVector<Object>(NULL, 0));
+ isolate->Throw(*error);
+ return Handle<Object>();
+ }
+ }
+
+ Handle<JSObject> real_receiver = object;
+
+ if (skip_hidden_prototypes) {
+ // Find the first object in the chain whose prototype object is not
+ // 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 = handle(JSObject::cast(current_proto), isolate);
+ current_proto = current_proto->GetPrototype(isolate);
+ }
+ }
+
+ // Set the new prototype of the object.
+ Handle<Map> map(real_receiver->map());
+
+ // Nothing to do if prototype is already set.
+ if (map->prototype() == *value) return value;
+
+ if (value->IsJSObject()) {
+ JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
+ }
+
+ Handle<Map> new_map = Map::GetPrototypeTransition(map, value);
+ if (new_map.is_null()) {
+ new_map = Map::Copy(map);
+ Map::PutPrototypeTransition(map, value, new_map);
+ new_map->set_prototype(*value);
+ }
+ ASSERT(new_map->prototype() == *value);
+ real_receiver->set_map(*new_map);
+
+ heap->ClearInstanceofCache();
+ ASSERT(size == object->Size());
+ return 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);
+}
+
+
+PropertyType JSObject::GetLocalPropertyType(Name* name) {
+ uint32_t index = 0;
+ if (name->AsArrayIndex(&index)) {
+ return GetLocalElementType(index);
+ }
+ LookupResult lookup(GetIsolate());
+ LocalLookup(name, &lookup, true);
+ return lookup.type();
+}
+
+
+PropertyType JSObject::GetLocalElementType(uint32_t index) {
+ return GetElementsAccessor()->GetType(this, this, index);
+}
+
+
+AccessorPair* JSObject::GetLocalPropertyAccessorPair(Name* name) {
+ uint32_t index = 0;
+ if (name->AsArrayIndex(&index)) {
+ return GetLocalElementAccessorPair(index);
+ }
+
+ LookupResult lookup(GetIsolate());
+ LocalLookupRealNamedProperty(name, &lookup);
+
+ if (lookup.IsPropertyCallbacks() &&
+ lookup.GetCallbackObject()->IsAccessorPair()) {
+ return AccessorPair::cast(lookup.GetCallbackObject());
+ }
+ return NULL;
+}
+
+
+AccessorPair* JSObject::GetLocalElementAccessorPair(uint32_t index) {
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return NULL;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->GetLocalElementAccessorPair(index);
+ }
+
+ // Check for lookup interceptor.
+ if (HasIndexedInterceptor()) return NULL;
+
+ return GetElementsAccessor()->GetAccessorPair(this, this, index);
+}
+
+
+MaybeObject* JSObject::SetElementWithInterceptor(uint32_t index,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool check_prototype,
+ SetPropertyMode set_mode) {
+ Isolate* isolate = GetIsolate();
+ // Make sure that the top context does not change when doing
+ // callbacks or interceptor calls.
+ AssertNoContextChange ncc;
+ HandleScope scope(isolate);
+ Handle<InterceptorInfo> interceptor(GetIndexedInterceptor());
+ Handle<JSObject> this_handle(this);
+ Handle<Object> value_handle(value, isolate);
+ if (!interceptor->setter()->IsUndefined()) {
+ v8::IndexedPropertySetter setter =
+ v8::ToCData<v8::IndexedPropertySetter>(interceptor->setter());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-set", this, index));
+ PropertyCallbackArguments args(isolate, interceptor->data(), this, this);
+ v8::Handle<v8::Value> result =
+ args.Call(setter, index, v8::Utils::ToLocal(value_handle));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!result.IsEmpty()) return *value_handle;
+ }
+ MaybeObject* raw_result =
+ this_handle->SetElementWithoutInterceptor(index,
+ *value_handle,
+ attributes,
+ strict_mode,
+ check_prototype,
+ set_mode);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return raw_result;
+}
+
+
+MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
+ Object* structure,
+ uint32_t index,
+ Object* holder) {
+ Isolate* isolate = GetIsolate();
+ ASSERT(!structure->IsForeign());
+
+ // api style callbacks.
+ if (structure->IsExecutableAccessorInfo()) {
+ Handle<ExecutableAccessorInfo> data(
+ ExecutableAccessorInfo::cast(structure));
+ Object* fun_obj = data->getter();
+ v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
+ if (call_fun == NULL) return isolate->heap()->undefined_value();
+ HandleScope scope(isolate);
+ Handle<JSObject> self(JSObject::cast(receiver));
+ Handle<JSObject> holder_handle(JSObject::cast(holder));
+ Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
+ Handle<String> key = isolate->factory()->NumberToString(number);
+ LOG(isolate, ApiNamedPropertyAccess("load", *self, *key));
+ PropertyCallbackArguments
+ args(isolate, data->data(), *self, *holder_handle);
+ v8::Handle<v8::Value> result = args.Call(call_fun, v8::Utils::ToLocal(key));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (result.IsEmpty()) return isolate->heap()->undefined_value();
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
+ }
+
+ // __defineGetter__ callback
+ 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();
+ }
+
+ if (structure->IsDeclaredAccessorInfo()) {
+ return GetDeclaredAccessorProperty(receiver,
+ DeclaredAccessorInfo::cast(structure),
+ isolate);
+ }
+
+ UNREACHABLE();
+ return NULL;
+}
+
+
+MaybeObject* JSObject::SetElementWithCallback(Object* structure,
+ uint32_t index,
+ Object* value,
+ JSObject* holder,
+ StrictModeFlag strict_mode) {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+
+ // We should never get here to initialize a const with the hole
+ // value since a const declaration would conflict with the setter.
+ ASSERT(!value->IsTheHole());
+ Handle<Object> value_handle(value, isolate);
+
+ // To accommodate both the old and the new api we switch on the
+ // data structure used to store the callbacks. Eventually foreign
+ // callbacks should be phased out.
+ ASSERT(!structure->IsForeign());
+
+ if (structure->IsExecutableAccessorInfo()) {
+ // api style callbacks
+ Handle<JSObject> self(this);
+ Handle<JSObject> holder_handle(JSObject::cast(holder));
+ Handle<ExecutableAccessorInfo> data(
+ ExecutableAccessorInfo::cast(structure));
+ Object* call_obj = data->setter();
+ v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
+ if (call_fun == NULL) return value;
+ Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
+ Handle<String> key(isolate->factory()->NumberToString(number));
+ LOG(isolate, ApiNamedPropertyAccess("store", *self, *key));
+ PropertyCallbackArguments
+ args(isolate, data->data(), *self, *holder_handle);
+ args.Call(call_fun,
+ v8::Utils::ToLocal(key),
+ v8::Utils::ToLocal(value_handle));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return *value_handle;
+ }
+
+ if (structure->IsAccessorPair()) {
+ Handle<Object> setter(AccessorPair::cast(structure)->setter(), isolate);
+ 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;
+ }
+ Handle<Object> holder_handle(holder, isolate);
+ Handle<Object> key(isolate->factory()->NewNumberFromUint(index));
+ Handle<Object> args[2] = { key, holder_handle };
+ return isolate->Throw(
+ *isolate->factory()->NewTypeError("no_setter_in_callback",
+ HandleVector(args, 2)));
+ }
+ }
+
+ // TODO(dcarney): Handle correctly.
+ if (structure->IsDeclaredAccessorInfo()) return value;
+
+ UNREACHABLE();
+ return NULL;
+}
+
+
+bool JSObject::HasFastArgumentsElements() {
+ Heap* heap = GetHeap();
+ if (!elements()->IsFixedArray()) return false;
+ FixedArray* elements = FixedArray::cast(this->elements());
+ if (elements->map() != heap->non_strict_arguments_elements_map()) {
+ return false;
+ }
+ FixedArray* arguments = FixedArray::cast(elements->get(1));
+ return !arguments->IsDictionary();
+}
+
+
+bool JSObject::HasDictionaryArgumentsElements() {
+ Heap* heap = GetHeap();
+ if (!elements()->IsFixedArray()) return false;
+ FixedArray* elements = FixedArray::cast(this->elements());
+ if (elements->map() != heap->non_strict_arguments_elements_map()) {
+ return false;
+ }
+ FixedArray* arguments = FixedArray::cast(elements->get(1));
+ return arguments->IsDictionary();
+}
+
+
+// Adding n elements in fast case is O(n*n).
+// Note: revisit design to have dual undefined values to capture absent
+// elements.
+MaybeObject* JSObject::SetFastElement(uint32_t index,
+ Object* value,
+ StrictModeFlag strict_mode,
+ bool check_prototype) {
+ ASSERT(HasFastSmiOrObjectElements() ||
+ HasFastArgumentsElements());
+
+ // Array optimizations rely on the prototype lookups of Array objects always
+ // returning undefined. If there is a store to the initial prototype object,
+ // make sure all of these optimizations are invalidated.
+ Isolate* isolate(GetIsolate());
+ if (isolate->is_initial_object_prototype(this) ||
+ isolate->is_initial_array_prototype(this)) {
+ HandleScope scope(GetIsolate());
+ map()->dependent_code()->DeoptimizeDependentCodeGroup(
+ GetIsolate(),
+ DependentCode::kElementsCantBeAddedGroup);
+ }
+
+ FixedArray* backing_store = FixedArray::cast(elements());
+ if (backing_store->map() == GetHeap()->non_strict_arguments_elements_map()) {
+ backing_store = FixedArray::cast(backing_store->get(1));
+ } else {
+ MaybeObject* maybe = EnsureWritableFastElements();
+ if (!maybe->To(&backing_store)) return maybe;
+ }
+ uint32_t capacity = static_cast<uint32_t>(backing_store->length());
+
+ if (check_prototype &&
+ (index >= capacity || backing_store->get(index)->IsTheHole())) {
+ bool found;
+ MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
+ value,
+ &found,
+ strict_mode);
+ if (found) return result;
+ }
+
+ 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;
+ bool introduces_holes = true;
+ if (IsJSArray()) {
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_length));
+ introduces_holes = index > array_length;
+ if (index >= array_length) {
+ must_update_array_length = true;
+ array_length = index + 1;
+ }
+ } else {
+ introduces_holes = index >= capacity;
+ }
+
+ // If the array is growing, and it's not growth by a single element at the
+ // end, make sure that the ElementsKind is HOLEY.
+ ElementsKind elements_kind = GetElementsKind();
+ if (introduces_holes &&
+ IsFastElementsKind(elements_kind) &&
+ !IsFastHoleyElementsKind(elements_kind)) {
+ ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind);
+ MaybeObject* maybe = TransitionElementsKind(transitioned_kind);
+ if (maybe->IsFailure()) return maybe;
+ }
+
+ // 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, NONE, strict_mode,
+ check_prototype);
+ }
+ }
+ // Convert to fast double elements if appropriate.
+ if (HasFastSmiElements() && !value->IsSmi() && value->IsNumber()) {
+ // Consider fixing the boilerplate as well if we have one.
+ ElementsKind to_kind = IsHoleyElementsKind(elements_kind)
+ ? FAST_HOLEY_DOUBLE_ELEMENTS
+ : FAST_DOUBLE_ELEMENTS;
+
+ MaybeObject* maybe_failure = UpdateAllocationSite(to_kind);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+
+ MaybeObject* maybe =
+ SetFastDoubleElementsCapacityAndLength(new_capacity, array_length);
+ if (maybe->IsFailure()) return maybe;
+ FixedDoubleArray::cast(elements())->set(index, value->Number());
+ ValidateElements();
+ return value;
+ }
+ // Change elements kind from Smi-only to generic FAST if necessary.
+ if (HasFastSmiElements() && !value->IsSmi()) {
+ Map* new_map;
+ ElementsKind kind = HasFastHoleyElements()
+ ? FAST_HOLEY_ELEMENTS
+ : FAST_ELEMENTS;
+
+ MaybeObject* maybe_failure = UpdateAllocationSite(kind);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+
+ MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(),
+ kind);
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ set_map(new_map);
+ }
+ // Increase backing store capacity if that's been decided previously.
+ if (new_capacity != capacity) {
+ FixedArray* new_elements;
+ SetFastElementsCapacitySmiMode smi_mode =
+ value->IsSmi() && HasFastSmiElements()
+ ? kAllowSmiElements
+ : kDontAllowSmiElements;
+ { MaybeObject* maybe =
+ SetFastElementsCapacityAndLength(new_capacity,
+ array_length,
+ smi_mode);
+ if (!maybe->To(&new_elements)) return maybe;
+ }
+ new_elements->set(index, value);
+ ValidateElements();
+ return value;
+ }
+
+ // 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;
+}
+
+
+MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
+ Object* value_raw,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool check_prototype,
+ SetPropertyMode set_mode) {
+ ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
+ Isolate* isolate = GetIsolate();
+ Heap* heap = isolate->heap();
+ Handle<JSObject> self(this);
+ Handle<Object> value(value_raw, isolate);
+
+ // Insert element in the dictionary.
+ Handle<FixedArray> elements(FixedArray::cast(this->elements()));
+ bool is_arguments =
+ (elements->map() == heap->non_strict_arguments_elements_map());
+ Handle<SeededNumberDictionary> dictionary(is_arguments
+ ? SeededNumberDictionary::cast(elements->get(1))
+ : SeededNumberDictionary::cast(*elements));
+
+ int entry = dictionary->FindEntry(index);
+ if (entry != SeededNumberDictionary::kNotFound) {
+ Object* element = dictionary->ValueAt(entry);
+ PropertyDetails details = dictionary->DetailsAt(entry);
+ if (details.type() == CALLBACKS && set_mode == SET_PROPERTY) {
+ return SetElementWithCallback(element, index, *value, this, strict_mode);
+ } else {
+ dictionary->UpdateMaxNumberKey(index);
+ // If a value has not been initialized we allow writing to it even if it
+ // is read-only (a declared const that has not been initialized). If a
+ // value is being defined we skip attribute checks completely.
+ if (set_mode == DEFINE_PROPERTY) {
+ details = PropertyDetails(
+ attributes, NORMAL, details.dictionary_index());
+ dictionary->DetailsAtPut(entry, details);
+ } else if (details.IsReadOnly() && !element->IsTheHole()) {
+ if (strict_mode == kNonStrictMode) {
+ return isolate->heap()->undefined_value();
+ } else {
+ Handle<Object> holder(this, isolate);
+ Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
+ Handle<Object> args[2] = { number, holder };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("strict_read_only_property",
+ HandleVector(args, 2));
+ return isolate->Throw(*error);
+ }
+ }
+ // Elements of the arguments object in slow mode might be slow aliases.
+ if (is_arguments && element->IsAliasedArgumentsEntry()) {
+ AliasedArgumentsEntry* entry = AliasedArgumentsEntry::cast(element);
+ Context* context = Context::cast(elements->get(0));
+ int context_index = entry->aliased_context_slot();
+ ASSERT(!context->get(context_index)->IsTheHole());
+ context->set(context_index, *value);
+ // For elements that are still writable we keep slow aliasing.
+ if (!details.IsReadOnly()) value = handle(element, isolate);
+ }
+ dictionary->ValueAtPut(entry, *value);
+ }
+ } else {
+ // Index not already used. Look for an accessor in the prototype chain.
+ // Can cause GC!
+ if (check_prototype) {
+ bool found;
+ MaybeObject* result = SetElementWithCallbackSetterInPrototypes(
+ index, *value, &found, strict_mode);
+ if (found) return result;
+ }
+ // When we set the is_extensible flag to false we always force the
+ // element into dictionary mode (and force them to stay there).
+ if (!self->map()->is_extensible()) {
+ if (strict_mode == kNonStrictMode) {
+ return isolate->heap()->undefined_value();
+ } else {
+ Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
+ Handle<String> name = isolate->factory()->NumberToString(number);
+ Handle<Object> args[1] = { name };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("object_not_extensible",
+ HandleVector(args, 1));
+ return isolate->Throw(*error);
+ }
+ }
+ FixedArrayBase* new_dictionary;
+ PropertyDetails details = PropertyDetails(attributes, NORMAL, 0);
+ MaybeObject* maybe = dictionary->AddNumberEntry(index, *value, details);
+ if (!maybe->To(&new_dictionary)) return maybe;
+ if (*dictionary != SeededNumberDictionary::cast(new_dictionary)) {
+ if (is_arguments) {
+ elements->set(1, new_dictionary);
+ } else {
+ self->set_elements(new_dictionary);
+ }
+ dictionary =
+ handle(SeededNumberDictionary::cast(new_dictionary), isolate);
+ }
+ }
+
+ // Update the array length if this JSObject is an array.
+ if (self->IsJSArray()) {
+ MaybeObject* result =
+ JSArray::cast(*self)->JSArrayUpdateLengthFromIndex(index, *value);
+ if (result->IsFailure()) return result;
+ }
+
+ // Attempt to put this object back in fast case.
+ if (self->ShouldConvertToFastElements()) {
+ uint32_t new_length = 0;
+ if (self->IsJSArray()) {
+ CHECK(JSArray::cast(*self)->length()->ToArrayIndex(&new_length));
+ } else {
+ new_length = dictionary->max_number_key() + 1;
+ }
+ SetFastElementsCapacitySmiMode smi_mode = FLAG_smi_only_arrays
+ ? kAllowSmiElements
+ : kDontAllowSmiElements;
+ bool has_smi_only_elements = false;
+ bool should_convert_to_fast_double_elements =
+ self->ShouldConvertToFastDoubleElements(&has_smi_only_elements);
+ if (has_smi_only_elements) {
+ smi_mode = kForceSmiElements;
+ }
+ MaybeObject* result = should_convert_to_fast_double_elements
+ ? self->SetFastDoubleElementsCapacityAndLength(new_length, new_length)
+ : self->SetFastElementsCapacityAndLength(
+ new_length, new_length, smi_mode);
+ self->ValidateElements();
+ if (result->IsFailure()) return result;
+#ifdef DEBUG
+ if (FLAG_trace_normalization) {
+ PrintF("Object elements are fast case again:\n");
+ Print();
+ }
+#endif
+ }
+ return *value;
+}
+
+
+MUST_USE_RESULT MaybeObject* JSObject::SetFastDoubleElement(
+ uint32_t index,
+ Object* value,
+ StrictModeFlag strict_mode,
+ bool check_prototype) {
+ ASSERT(HasFastDoubleElements());
+
+ FixedArrayBase* base_elms = FixedArrayBase::cast(elements());
+ uint32_t elms_length = static_cast<uint32_t>(base_elms->length());
+
+ // If storing to an element that isn't in the array, pass the store request
+ // up the prototype chain before storing in the receiver's elements.
+ if (check_prototype &&
+ (index >= elms_length ||
+ FixedDoubleArray::cast(base_elms)->is_the_hole(index))) {
+ bool found;
+ MaybeObject* result = SetElementWithCallbackSetterInPrototypes(index,
+ value,
+ &found,
+ strict_mode);
+ if (found) return result;
+ }
+
+ // If the value object is not a heap number, switch to fast elements and try
+ // again.
+ bool value_is_smi = value->IsSmi();
+ bool introduces_holes = true;
+ uint32_t length = elms_length;
+ if (IsJSArray()) {
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
+ introduces_holes = index > length;
+ } else {
+ introduces_holes = index >= elms_length;
+ }
+
+ if (!value->IsNumber()) {
+ MaybeObject* maybe_obj = SetFastElementsCapacityAndLength(
+ elms_length,
+ length,
+ kDontAllowSmiElements);
+ if (maybe_obj->IsFailure()) return maybe_obj;
+ maybe_obj = SetFastElement(index, value, strict_mode, check_prototype);
+ if (maybe_obj->IsFailure()) return maybe_obj;
+ ValidateElements();
+ return maybe_obj;
+ }
+
+ double double_value = value_is_smi
+ ? static_cast<double>(Smi::cast(value)->value())
+ : HeapNumber::cast(value)->value();
+
+ // If the array is growing, and it's not growth by a single element at the
+ // end, make sure that the ElementsKind is HOLEY.
+ ElementsKind elements_kind = GetElementsKind();
+ if (introduces_holes && !IsFastHoleyElementsKind(elements_kind)) {
+ ElementsKind transitioned_kind = GetHoleyElementsKind(elements_kind);
+ MaybeObject* maybe = TransitionElementsKind(transitioned_kind);
+ if (maybe->IsFailure()) return maybe;
+ }
+
+ // Check whether there is extra space in the fixed array.
+ if (index < elms_length) {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
+ elms->set(index, double_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));
+ }
+ }
+ return value;
+ }
+
+ // Allow gap in fast case.
+ if ((index - elms_length) < kMaxGap) {
+ // Try allocating extra space.
+ int new_capacity = NewElementsCapacity(index+1);
+ if (!ShouldConvertToSlowElements(new_capacity)) {
+ ASSERT(static_cast<uint32_t>(new_capacity) > index);
+ MaybeObject* maybe_obj =
+ SetFastDoubleElementsCapacityAndLength(new_capacity, index + 1);
+ if (maybe_obj->IsFailure()) return maybe_obj;
+ FixedDoubleArray::cast(elements())->set(index, double_value);
+ ValidateElements();
+ return value;
+ }
+ }
+
+ // Otherwise default to slow case.
+ ASSERT(HasFastDoubleElements());
+ ASSERT(map()->has_fast_double_elements());
+ ASSERT(elements()->IsFixedDoubleArray());
+ Object* obj;
+ { MaybeObject* maybe_obj = NormalizeElements();
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ ASSERT(HasDictionaryElements());
+ return SetElement(index, value, NONE, strict_mode, check_prototype);
+}
+
+
+MaybeObject* JSReceiver::SetElement(uint32_t index,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool check_proto) {
+ if (IsJSProxy()) {
+ return JSProxy::cast(this)->SetElementWithHandler(
+ this, index, value, strict_mode);
+ } else {
+ return JSObject::cast(this)->SetElement(
+ index, value, attributes, 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, NONE, strict_mode, false),
+ Object);
+}
+
+
+Handle<Object> JSObject::SetElement(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> value,
+ PropertyAttributes attr,
+ StrictModeFlag strict_mode,
+ SetPropertyMode set_mode) {
+ if (object->HasExternalArrayElements()) {
+ if (!value->IsNumber() && !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, attr, strict_mode, true, set_mode),
+ Object);
+}
+
+
+MaybeObject* JSObject::SetElement(uint32_t index,
+ Object* value_raw,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool check_prototype,
+ SetPropertyMode set_mode) {
+ Isolate* isolate = GetIsolate();
+
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_SET)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_SET);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return value_raw;
+ }
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return value_raw;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->SetElement(index,
+ value_raw,
+ attributes,
+ strict_mode,
+ check_prototype,
+ set_mode);
+ }
+
+ // Don't allow element properties to be redefined for external arrays.
+ if (HasExternalArrayElements() && set_mode == DEFINE_PROPERTY) {
+ Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
+ Handle<Object> args[] = { handle(this, isolate), number };
+ Handle<Object> error = isolate->factory()->NewTypeError(
+ "redef_external_array_element", HandleVector(args, ARRAY_SIZE(args)));
+ return isolate->Throw(*error);
+ }
+
+ // Normalize the elements to enable attributes on the property.
+ if ((attributes & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) {
+ SeededNumberDictionary* dictionary;
+ MaybeObject* maybe_object = NormalizeElements();
+ if (!maybe_object->To(&dictionary)) return maybe_object;
+ // Make sure that we never go back to fast case.
+ dictionary->set_requires_slow_elements();
+ }
+
+ if (!(FLAG_harmony_observation && map()->is_observed())) {
+ return HasIndexedInterceptor()
+ ? SetElementWithInterceptor(
+ index, value_raw, attributes, strict_mode, check_prototype, set_mode)
+ : SetElementWithoutInterceptor(
+ index, value_raw, attributes, strict_mode, check_prototype, set_mode);
+ }
+
+ // From here on, everything has to be handlified.
+ Handle<JSObject> self(this);
+ Handle<Object> value(value_raw, isolate);
+ PropertyAttributes old_attributes = self->GetLocalElementAttribute(index);
+ Handle<Object> old_value = isolate->factory()->the_hole_value();
+ Handle<Object> old_length_handle;
+ Handle<Object> new_length_handle;
+
+ if (old_attributes != ABSENT) {
+ if (self->GetLocalElementAccessorPair(index) == NULL)
+ old_value = Object::GetElement(self, index);
+ } else if (self->IsJSArray()) {
+ // Store old array length in case adding an element grows the array.
+ old_length_handle = handle(Handle<JSArray>::cast(self)->length(), isolate);
+ }
+
+ // Check for lookup interceptor
+ MaybeObject* result = self->HasIndexedInterceptor()
+ ? self->SetElementWithInterceptor(
+ index, *value, attributes, strict_mode, check_prototype, set_mode)
+ : self->SetElementWithoutInterceptor(
+ index, *value, attributes, strict_mode, check_prototype, set_mode);
+
+ Handle<Object> hresult;
+ if (!result->ToHandle(&hresult, isolate)) return result;
+
+ Handle<String> name = isolate->factory()->Uint32ToString(index);
+ PropertyAttributes new_attributes = self->GetLocalElementAttribute(index);
+ if (old_attributes == ABSENT) {
+ if (self->IsJSArray() &&
+ !old_length_handle->SameValue(Handle<JSArray>::cast(self)->length())) {
+ new_length_handle = handle(Handle<JSArray>::cast(self)->length(),
+ isolate);
+ uint32_t old_length = 0;
+ uint32_t new_length = 0;
+ CHECK(old_length_handle->ToArrayIndex(&old_length));
+ CHECK(new_length_handle->ToArrayIndex(&new_length));
+
+ BeginPerformSplice(Handle<JSArray>::cast(self));
+ EnqueueChangeRecord(self, "new", name, old_value);
+ EnqueueChangeRecord(self, "updated", isolate->factory()->length_string(),
+ old_length_handle);
+ EndPerformSplice(Handle<JSArray>::cast(self));
+ Handle<JSArray> deleted = isolate->factory()->NewJSArray(0);
+ EnqueueSpliceRecord(Handle<JSArray>::cast(self), old_length, deleted,
+ new_length - old_length);
+ } else {
+ EnqueueChangeRecord(self, "new", name, old_value);
+ }
+ } else if (old_value->IsTheHole()) {
+ EnqueueChangeRecord(self, "reconfigured", name, old_value);
+ } else {
+ Handle<Object> new_value = Object::GetElement(self, index);
+ bool value_changed = !old_value->SameValue(*new_value);
+ if (old_attributes != new_attributes) {
+ if (!value_changed) old_value = isolate->factory()->the_hole_value();
+ EnqueueChangeRecord(self, "reconfigured", name, old_value);
+ } else if (value_changed) {
+ EnqueueChangeRecord(self, "updated", name, old_value);
+ }
+ }
+
+ return *hresult;
+}
+
+
+MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
+ Object* value,
+ PropertyAttributes attr,
+ StrictModeFlag strict_mode,
+ bool check_prototype,
+ SetPropertyMode set_mode) {
+ ASSERT(HasDictionaryElements() ||
+ HasDictionaryArgumentsElements() ||
+ (attr & (DONT_DELETE | DONT_ENUM | READ_ONLY)) == 0);
+ Isolate* isolate = GetIsolate();
+ if (FLAG_trace_external_array_abuse &&
+ IsExternalArrayElementsKind(GetElementsKind())) {
+ CheckArrayAbuse(this, "external elements write", index);
+ }
+ if (FLAG_trace_js_array_abuse &&
+ !IsExternalArrayElementsKind(GetElementsKind())) {
+ if (IsJSArray()) {
+ CheckArrayAbuse(this, "elements write", index, true);
+ }
+ }
+ switch (GetElementsKind()) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ return SetFastElement(index, value, strict_mode, check_prototype);
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ return SetFastDoubleElement(index, value, strict_mode, check_prototype);
+ case EXTERNAL_PIXEL_ELEMENTS: {
+ ExternalPixelArray* pixels = ExternalPixelArray::cast(elements());
+ return pixels->SetValue(index, value);
+ }
+ case EXTERNAL_BYTE_ELEMENTS: {
+ ExternalByteArray* array = ExternalByteArray::cast(elements());
+ return array->SetValue(index, value);
+ }
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
+ ExternalUnsignedByteArray* array =
+ ExternalUnsignedByteArray::cast(elements());
+ return array->SetValue(index, value);
+ }
+ case EXTERNAL_SHORT_ELEMENTS: {
+ ExternalShortArray* array = ExternalShortArray::cast(elements());
+ return array->SetValue(index, value);
+ }
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
+ ExternalUnsignedShortArray* array =
+ ExternalUnsignedShortArray::cast(elements());
+ return array->SetValue(index, value);
+ }
+ case EXTERNAL_INT_ELEMENTS: {
+ ExternalIntArray* array = ExternalIntArray::cast(elements());
+ return array->SetValue(index, value);
+ }
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
+ ExternalUnsignedIntArray* array =
+ ExternalUnsignedIntArray::cast(elements());
+ return array->SetValue(index, value);
+ }
+ case EXTERNAL_FLOAT_ELEMENTS: {
+ ExternalFloatArray* array = ExternalFloatArray::cast(elements());
+ return array->SetValue(index, value);
+ }
+ case EXTERNAL_DOUBLE_ELEMENTS: {
+ ExternalDoubleArray* array = ExternalDoubleArray::cast(elements());
+ return array->SetValue(index, value);
+ }
+ case DICTIONARY_ELEMENTS:
+ return SetDictionaryElement(index, value, attr, strict_mode,
+ check_prototype, set_mode);
+ case NON_STRICT_ARGUMENTS_ELEMENTS: {
+ FixedArray* parameter_map = FixedArray::cast(elements());
+ uint32_t length = parameter_map->length();
+ Object* probe =
+ (index < length - 2) ? parameter_map->get(index + 2) : NULL;
+ if (probe != NULL && !probe->IsTheHole()) {
+ Context* context = Context::cast(parameter_map->get(0));
+ int context_index = Smi::cast(probe)->value();
+ ASSERT(!context->get(context_index)->IsTheHole());
+ context->set(context_index, value);
+ // Redefining attributes of an aliased element destroys fast aliasing.
+ if (set_mode == SET_PROPERTY || attr == NONE) return value;
+ parameter_map->set_the_hole(index + 2);
+ // For elements that are still writable we re-establish slow aliasing.
+ if ((attr & READ_ONLY) == 0) {
+ MaybeObject* maybe_entry =
+ isolate->heap()->AllocateAliasedArgumentsEntry(context_index);
+ if (!maybe_entry->ToObject(&value)) return maybe_entry;
+ }
+ }
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ if (arguments->IsDictionary()) {
+ return SetDictionaryElement(index, value, attr, strict_mode,
+ check_prototype, set_mode);
+ } else {
+ return SetFastElement(index, value, strict_mode, check_prototype);
+ }
+ }
+ }
+ // All possible cases have been handled above. Add a return to avoid the
+ // complaints from the compiler.
+ UNREACHABLE();
+ return isolate->heap()->null_value();
+}
+
+
+Handle<Object> JSObject::TransitionElementsKind(Handle<JSObject> object,
+ ElementsKind to_kind) {
+ CALL_HEAP_FUNCTION(object->GetIsolate(),
+ object->TransitionElementsKind(to_kind),
+ Object);
+}
+
+
+MaybeObject* JSObject::UpdateAllocationSite(ElementsKind to_kind) {
+ if (!FLAG_track_allocation_sites || !IsJSArray()) {
+ return this;
+ }
+
+ AllocationMemento* memento = AllocationMemento::FindForJSObject(this);
+ if (memento == NULL || !memento->IsValid()) {
+ return this;
+ }
+
+ // Walk through to the Allocation Site
+ AllocationSite* site = memento->GetAllocationSite();
+ if (site->IsLiteralSite()) {
+ JSArray* transition_info = JSArray::cast(site->transition_info());
+ ElementsKind kind = transition_info->GetElementsKind();
+ // if kind is holey ensure that to_kind is as well.
+ if (IsHoleyElementsKind(kind)) {
+ to_kind = GetHoleyElementsKind(to_kind);
+ }
+ if (AllocationSite::GetMode(kind, to_kind) == TRACK_ALLOCATION_SITE) {
+ // If the array is huge, it's not likely to be defined in a local
+ // function, so we shouldn't make new instances of it very often.
+ uint32_t length = 0;
+ CHECK(transition_info->length()->ToArrayIndex(&length));
+ if (length <= AllocationSite::kMaximumArrayBytesToPretransition) {
+ if (FLAG_trace_track_allocation_sites) {
+ PrintF(
+ "AllocationSite: JSArray %p boilerplate updated %s->%s\n",
+ reinterpret_cast<void*>(this),
+ ElementsKindToString(kind),
+ ElementsKindToString(to_kind));
+ }
+ return transition_info->TransitionElementsKind(to_kind);
+ }
+ }
+ } else {
+ ElementsKind kind = site->GetElementsKind();
+ // if kind is holey ensure that to_kind is as well.
+ if (IsHoleyElementsKind(kind)) {
+ to_kind = GetHoleyElementsKind(to_kind);
+ }
+ if (AllocationSite::GetMode(kind, to_kind) == TRACK_ALLOCATION_SITE) {
+ if (FLAG_trace_track_allocation_sites) {
+ PrintF("AllocationSite: JSArray %p site updated %s->%s\n",
+ reinterpret_cast<void*>(this),
+ ElementsKindToString(kind),
+ ElementsKindToString(to_kind));
+ }
+ site->set_transition_info(Smi::FromInt(to_kind));
+ }
+ }
+ return this;
+}
+
+
+MaybeObject* JSObject::TransitionElementsKind(ElementsKind to_kind) {
+ ASSERT(!map()->is_observed());
+ ElementsKind from_kind = map()->elements_kind();
+
+ if (IsFastHoleyElementsKind(from_kind)) {
+ to_kind = GetHoleyElementsKind(to_kind);
+ }
+
+ if (from_kind == to_kind) return this;
+
+ MaybeObject* maybe_failure = UpdateAllocationSite(to_kind);
+ if (maybe_failure->IsFailure()) return maybe_failure;
+
+ Isolate* isolate = GetIsolate();
+ if (elements() == isolate->heap()->empty_fixed_array() ||
+ (IsFastSmiOrObjectElementsKind(from_kind) &&
+ IsFastSmiOrObjectElementsKind(to_kind)) ||
+ (from_kind == FAST_DOUBLE_ELEMENTS &&
+ to_kind == FAST_HOLEY_DOUBLE_ELEMENTS)) {
+ ASSERT(from_kind != TERMINAL_FAST_ELEMENTS_KIND);
+ // No change is needed to the elements() buffer, the transition
+ // only requires a map change.
+ 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 (IsFastSmiElementsKind(from_kind) &&
+ IsFastDoubleElementsKind(to_kind)) {
+ MaybeObject* maybe_result =
+ SetFastDoubleElementsCapacityAndLength(capacity, length);
+ if (maybe_result->IsFailure()) return maybe_result;
+ ValidateElements();
+ return this;
+ }
+
+ if (IsFastDoubleElementsKind(from_kind) &&
+ IsFastObjectElementsKind(to_kind)) {
+ MaybeObject* maybe_result = SetFastElementsCapacityAndLength(
+ capacity, length, kDontAllowSmiElements);
+ if (maybe_result->IsFailure()) return maybe_result;
+ ValidateElements();
+ 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) {
+ // Transitions can't go backwards.
+ if (!IsMoreGeneralElementsKindTransition(from_kind, to_kind)) {
+ return false;
+ }
+
+ // Transitions from HOLEY -> PACKED are not allowed.
+ return !IsFastHoleyElementsKind(from_kind) ||
+ IsFastHoleyElementsKind(to_kind);
+}
+
+
+MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
+ Object* value) {
+ uint32_t old_len = 0;
+ CHECK(length()->ToArrayIndex(&old_len));
+ // Check to see if we need to update the length. For now, we make
+ // sure that the length stays within 32-bits (unsigned).
+ if (index >= old_len && index != 0xffffffff) {
+ Object* len;
+ { MaybeObject* maybe_len =
+ GetHeap()->NumberFromDouble(static_cast<double>(index) + 1);
+ if (!maybe_len->ToObject(&len)) return maybe_len;
+ }
+ set_length(len);
+ }
+ return value;
+}
+
+
+MaybeObject* JSObject::GetElementWithInterceptor(Object* receiver,
+ uint32_t index) {
+ Isolate* isolate = GetIsolate();
+ // Make sure that the top context does not change when doing
+ // callbacks or interceptor calls.
+ AssertNoContextChange ncc;
+ HandleScope scope(isolate);
+ Handle<InterceptorInfo> interceptor(GetIndexedInterceptor(), isolate);
+ Handle<Object> this_handle(receiver, isolate);
+ Handle<JSObject> holder_handle(this, isolate);
+ if (!interceptor->getter()->IsUndefined()) {
+ v8::IndexedPropertyGetter getter =
+ v8::ToCData<v8::IndexedPropertyGetter>(interceptor->getter());
+ LOG(isolate,
+ ApiIndexedPropertyAccess("interceptor-indexed-get", this, index));
+ PropertyCallbackArguments
+ args(isolate, interceptor->data(), receiver, this);
+ v8::Handle<v8::Value> result = args.Call(getter, index);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!result.IsEmpty()) {
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
+ }
+ }
+
+ Heap* heap = holder_handle->GetHeap();
+ ElementsAccessor* handler = holder_handle->GetElementsAccessor();
+ MaybeObject* raw_result = handler->Get(*this_handle,
+ *holder_handle,
+ index);
+ if (raw_result != heap->the_hole_value()) return raw_result;
+
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+
+ Object* pt = holder_handle->GetPrototype();
+ if (pt == heap->null_value()) return heap->undefined_value();
+ return pt->GetElementWithReceiver(*this_handle, index);
+}
+
+
+bool JSObject::HasDenseElements() {
+ int capacity = 0;
+ int used = 0;
+ GetElementsCapacityAndUsage(&capacity, &used);
+ return (capacity == 0) || (used > (capacity / 2));
+}
+
+
+void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
+ *capacity = 0;
+ *used = 0;
+
+ FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements());
+ FixedArray* backing_store = NULL;
+ switch (GetElementsKind()) {
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ backing_store_base =
+ FixedArray::cast(FixedArray::cast(backing_store_base)->get(1));
+ backing_store = FixedArray::cast(backing_store_base);
+ if (backing_store->IsDictionary()) {
+ SeededNumberDictionary* dictionary =
+ SeededNumberDictionary::cast(backing_store);
+ *capacity = dictionary->Capacity();
+ *used = dictionary->NumberOfElements();
+ break;
+ }
+ // Fall through.
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ if (IsJSArray()) {
+ *capacity = backing_store_base->length();
+ *used = Smi::cast(JSArray::cast(this)->length())->value();
+ break;
+ }
+ // Fall through if packing is not guaranteed.
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ backing_store = FixedArray::cast(backing_store_base);
+ *capacity = backing_store->length();
+ for (int i = 0; i < *capacity; ++i) {
+ if (!backing_store->get(i)->IsTheHole()) ++(*used);
+ }
+ break;
+ case DICTIONARY_ELEMENTS: {
+ SeededNumberDictionary* dictionary =
+ SeededNumberDictionary::cast(FixedArray::cast(elements()));
+ *capacity = dictionary->Capacity();
+ *used = dictionary->NumberOfElements();
+ break;
+ }
+ case FAST_DOUBLE_ELEMENTS:
+ if (IsJSArray()) {
+ *capacity = backing_store_base->length();
+ *used = Smi::cast(JSArray::cast(this)->length())->value();
+ break;
+ }
+ // Fall through if packing is not guaranteed.
+ case FAST_HOLEY_DOUBLE_ELEMENTS: {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
+ *capacity = elms->length();
+ for (int i = 0; i < *capacity; i++) {
+ if (!elms->is_the_hole(i)) ++(*used);
+ }
+ 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:
+ // External arrays are considered 100% used.
+ ExternalArray* external_array = ExternalArray::cast(elements());
+ *capacity = external_array->length();
+ *used = external_array->length();
+ break;
+ }
+}
+
+
+bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
+ STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
+ kMaxUncheckedFastElementsLength);
+ if (new_capacity <= kMaxUncheckedOldFastElementsLength ||
+ (new_capacity <= kMaxUncheckedFastElementsLength &&
+ GetHeap()->InNewSpace(this))) {
+ return false;
+ }
+ // If the fast-case backing storage takes up roughly three times as
+ // much space (in machine words) as a dictionary backing storage
+ // would, the object should have slow elements.
+ int old_capacity = 0;
+ int used_elements = 0;
+ GetElementsCapacityAndUsage(&old_capacity, &used_elements);
+ int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) *
+ SeededNumberDictionary::kEntrySize;
+ return 3 * dictionary_size <= new_capacity;
+}
+
+
+bool JSObject::ShouldConvertToFastElements() {
+ ASSERT(HasDictionaryElements() || HasDictionaryArgumentsElements());
+ // If the elements are sparse, we should not go back to fast case.
+ if (!HasDenseElements()) return false;
+ // An object requiring access checks is never allowed to have fast
+ // elements. If it had fast elements we would skip security checks.
+ if (IsAccessCheckNeeded()) return false;
+ // Observed objects may not go to fast mode because they rely on map checks,
+ // and for fast element accesses we sometimes check element kinds only.
+ if (FLAG_harmony_observation && map()->is_observed()) return false;
+
+ FixedArray* elements = FixedArray::cast(this->elements());
+ SeededNumberDictionary* dictionary = NULL;
+ if (elements->map() == GetHeap()->non_strict_arguments_elements_map()) {
+ dictionary = SeededNumberDictionary::cast(elements->get(1));
+ } else {
+ dictionary = SeededNumberDictionary::cast(elements);
+ }
+ // If an element has been added at a very high index in the elements
+ // dictionary, we cannot go back to fast case.
+ if (dictionary->requires_slow_elements()) return false;
+ // If the dictionary backing storage takes up roughly half as much
+ // space (in machine words) as a fast-case backing storage would,
+ // the object should have fast elements.
+ uint32_t array_size = 0;
+ if (IsJSArray()) {
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size));
+ } else {
+ array_size = dictionary->max_number_key();
+ }
+ uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
+ SeededNumberDictionary::kEntrySize;
+ return 2 * dictionary_size >= array_size;
+}
+
+
+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()) {
+ Object* value = dictionary->ValueAt(i);
+ if (!value->IsNumber()) return false;
+ if (!value->IsSmi()) {
+ found_double = true;
+ }
+ }
+ }
+ *has_smi_only_elements = !found_double;
+ return found_double;
+ } else {
+ return false;
+ }
+}
+
+
+// Certain compilers request function template instantiation when they
+// see the definition of the other template functions in the
+// class. This requires us to have the template functions put
+// together, so even though this function belongs in objects-debug.cc,
+// we keep it here instead to satisfy certain compilers.
+#ifdef OBJECT_PRINT
+template<typename Shape, typename Key>
+void Dictionary<Shape, Key>::Print(FILE* out) {
+ int capacity = HashTable<Shape, Key>::Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Object* k = HashTable<Shape, Key>::KeyAt(i);
+ if (HashTable<Shape, Key>::IsKey(k)) {
+ PrintF(out, " ");
+ if (k->IsString()) {
+ String::cast(k)->StringPrint(out);
+ } else {
+ k->ShortPrint(out);
+ }
+ PrintF(out, ": ");
+ ValueAt(i)->ShortPrint(out);
+ PrintF(out, "\n");
+ }
+ }
+}
+#endif
+
+
+template<typename Shape, typename Key>
+void Dictionary<Shape, Key>::CopyValuesTo(FixedArray* elements) {
+ int pos = 0;
+ int capacity = HashTable<Shape, Key>::Capacity();
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc);
+ for (int i = 0; i < capacity; i++) {
+ Object* k = Dictionary<Shape, Key>::KeyAt(i);
+ if (Dictionary<Shape, Key>::IsKey(k)) {
+ elements->set(pos++, ValueAt(i), mode);
+ }
+ }
+ ASSERT(pos == elements->length());
+}
+
+
+InterceptorInfo* JSObject::GetNamedInterceptor() {
+ ASSERT(map()->has_named_interceptor());
+ JSFunction* constructor = JSFunction::cast(map()->constructor());
+ ASSERT(constructor->shared()->IsApiFunction());
+ Object* result =
+ constructor->shared()->get_api_func_data()->named_property_handler();
+ return InterceptorInfo::cast(result);
+}
+
+
+InterceptorInfo* JSObject::GetIndexedInterceptor() {
+ ASSERT(map()->has_indexed_interceptor());
+ JSFunction* constructor = JSFunction::cast(map()->constructor());
+ ASSERT(constructor->shared()->IsApiFunction());
+ Object* result =
+ constructor->shared()->get_api_func_data()->indexed_property_handler();
+ return InterceptorInfo::cast(result);
+}
+
+
+MaybeObject* JSObject::GetPropertyPostInterceptor(
+ Object* receiver,
+ Name* name,
+ PropertyAttributes* attributes) {
+ // Check local property in holder, ignore interceptor.
+ LookupResult result(GetIsolate());
+ LocalLookupRealNamedProperty(name, &result);
+ if (result.IsFound()) {
+ return GetProperty(receiver, &result, name, attributes);
+ }
+ // Continue searching via the prototype chain.
+ Object* pt = GetPrototype();
+ *attributes = ABSENT;
+ if (pt->IsNull()) return GetHeap()->undefined_value();
+ return pt->GetPropertyWithReceiver(receiver, name, attributes);
+}
+
+
+MaybeObject* JSObject::GetLocalPropertyPostInterceptor(
+ Object* receiver,
+ Name* name,
+ PropertyAttributes* attributes) {
+ // Check local property in holder, ignore interceptor.
+ LookupResult result(GetIsolate());
+ LocalLookupRealNamedProperty(name, &result);
+ if (result.IsFound()) {
+ return GetProperty(receiver, &result, name, attributes);
+ }
+ return GetHeap()->undefined_value();
+}
+
+
+MaybeObject* JSObject::GetPropertyWithInterceptor(
+ Object* receiver,
+ Name* name,
+ PropertyAttributes* attributes) {
+ // TODO(rossberg): Support symbols in the API.
+ if (name->IsSymbol()) return GetHeap()->undefined_value();
+
+ Isolate* isolate = GetIsolate();
+ InterceptorInfo* interceptor = GetNamedInterceptor();
+ HandleScope scope(isolate);
+ Handle<Object> receiver_handle(receiver, isolate);
+ Handle<JSObject> holder_handle(this);
+ Handle<String> name_handle(String::cast(name));
+
+ if (!interceptor->getter()->IsUndefined()) {
+ v8::NamedPropertyGetter getter =
+ v8::ToCData<v8::NamedPropertyGetter>(interceptor->getter());
+ LOG(isolate,
+ ApiNamedPropertyAccess("interceptor-named-get", *holder_handle, name));
+ PropertyCallbackArguments
+ args(isolate, interceptor->data(), receiver, this);
+ v8::Handle<v8::Value> result =
+ args.Call(getter, v8::Utils::ToLocal(name_handle));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!result.IsEmpty()) {
+ *attributes = NONE;
+ Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
+ result_internal->VerifyApiCallResultType();
+ return *result_internal;
+ }
+ }
+
+ MaybeObject* result = holder_handle->GetPropertyPostInterceptor(
+ *receiver_handle,
+ *name_handle,
+ attributes);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return result;
+}
+
+
+bool JSObject::HasRealNamedProperty(Isolate* isolate, Name* key) {
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ if (!isolate->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return false;
+ }
+ }
+
+ LookupResult result(isolate);
+ LocalLookupRealNamedProperty(key, &result);
+ return result.IsFound() && !result.IsInterceptor();
+}
+
+
+bool JSObject::HasRealElementProperty(Isolate* isolate, uint32_t index) {
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ if (!isolate->MayIndexedAccess(this, index, v8::ACCESS_HAS)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return false;
+ }
+ }
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return false;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->HasRealElementProperty(isolate, index);
+ }
+
+ return GetElementAttributeWithoutInterceptor(this, index, false) != ABSENT;
+}
+
+
+bool JSObject::HasRealNamedCallbackProperty(Isolate* isolate, Name* key) {
+ // Check access rights if needed.
+ if (IsAccessCheckNeeded()) {
+ if (!isolate->MayNamedAccess(this, key, v8::ACCESS_HAS)) {
+ isolate->ReportFailedAccessCheck(this, v8::ACCESS_HAS);
+ return false;
+ }
+ }
+
+ LookupResult result(isolate);
+ LocalLookupRealNamedProperty(key, &result);
+ return result.IsPropertyCallbacks();
+}
+
+
+int JSObject::NumberOfLocalProperties(PropertyAttributes filter) {
+ if (HasFastProperties()) {
+ Map* map = this->map();
+ if (filter == NONE) return map->NumberOfOwnDescriptors();
+ if (filter & DONT_ENUM) {
+ int result = map->EnumLength();
+ if (result != Map::kInvalidEnumCache) return result;
+ }
+ return map->NumberOfDescribedProperties(OWN_DESCRIPTORS, filter);
+ }
+ return property_dictionary()->NumberOfElementsFilterAttributes(filter);
+}
+
+
+void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
+ Object* temp = get(i);
+ set(i, get(j));
+ set(j, temp);
+ if (this != numbers) {
+ temp = numbers->get(i);
+ numbers->set(i, Smi::cast(numbers->get(j)));
+ numbers->set(j, Smi::cast(temp));
+ }
+}
+
+
+static void InsertionSortPairs(FixedArray* content,
+ FixedArray* numbers,
+ int len) {
+ for (int i = 1; i < len; i++) {
+ int j = i;
+ while (j > 0 &&
+ (NumberToUint32(numbers->get(j - 1)) >
+ NumberToUint32(numbers->get(j)))) {
+ content->SwapPairs(numbers, j - 1, j);
+ j--;
+ }
+ }
+}
+
+
+void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
+ // In-place heap sort.
+ ASSERT(content->length() == numbers->length());
+
+ // Bottom-up max-heap construction.
+ for (int i = 1; i < len; ++i) {
+ int child_index = i;
+ while (child_index > 0) {
+ int parent_index = ((child_index + 1) >> 1) - 1;
+ uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
+ uint32_t child_value = NumberToUint32(numbers->get(child_index));
+ if (parent_value < child_value) {
+ content->SwapPairs(numbers, parent_index, child_index);
+ } else {
+ break;
+ }
+ child_index = parent_index;
+ }
+ }
+
+ // Extract elements and create sorted array.
+ for (int i = len - 1; i > 0; --i) {
+ // Put max element at the back of the array.
+ content->SwapPairs(numbers, 0, i);
+ // Sift down the new top element.
+ int parent_index = 0;
+ while (true) {
+ int child_index = ((parent_index + 1) << 1) - 1;
+ if (child_index >= i) break;
+ uint32_t child1_value = NumberToUint32(numbers->get(child_index));
+ uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
+ uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
+ if (child_index + 1 >= i || child1_value > child2_value) {
+ if (parent_value > child1_value) break;
+ content->SwapPairs(numbers, parent_index, child_index);
+ parent_index = child_index;
+ } else {
+ if (parent_value > child2_value) break;
+ content->SwapPairs(numbers, parent_index, child_index + 1);
+ parent_index = child_index + 1;
+ }
+ }
+ }
+}
+
+
+// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
+void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
+ ASSERT(this->length() == numbers->length());
+ // For small arrays, simply use insertion sort.
+ if (len <= 10) {
+ InsertionSortPairs(this, numbers, len);
+ return;
+ }
+ // Check the range of indices.
+ uint32_t min_index = NumberToUint32(numbers->get(0));
+ uint32_t max_index = min_index;
+ uint32_t i;
+ for (i = 1; i < len; i++) {
+ if (NumberToUint32(numbers->get(i)) < min_index) {
+ min_index = NumberToUint32(numbers->get(i));
+ } else if (NumberToUint32(numbers->get(i)) > max_index) {
+ max_index = NumberToUint32(numbers->get(i));
+ }
+ }
+ if (max_index - min_index + 1 == len) {
+ // Indices form a contiguous range, unless there are duplicates.
+ // Do an in-place linear time sort assuming distinct numbers, but
+ // avoid hanging in case they are not.
+ for (i = 0; i < len; i++) {
+ uint32_t p;
+ uint32_t j = 0;
+ // While the current element at i is not at its correct position p,
+ // swap the elements at these two positions.
+ while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
+ j++ < len) {
+ SwapPairs(numbers, i, p);
+ }
+ }
+ } else {
+ HeapSortPairs(this, numbers, len);
+ return;
+ }
+}
+
+
+// Fill in the names of local properties into the supplied storage. The main
+// purpose of this function is to provide reflection information for the object
+// mirrors.
+void JSObject::GetLocalPropertyNames(
+ FixedArray* storage, int index, PropertyAttributes filter) {
+ ASSERT(storage->length() >= (NumberOfLocalProperties(filter) - index));
+ if (HasFastProperties()) {
+ int real_size = map()->NumberOfOwnDescriptors();
+ DescriptorArray* descs = map()->instance_descriptors();
+ for (int i = 0; i < real_size; i++) {
+ if ((descs->GetDetails(i).attributes() & filter) == 0 &&
+ ((filter & SYMBOLIC) == 0 || !descs->GetKey(i)->IsSymbol())) {
+ storage->set(index++, descs->GetKey(i));
+ }
+ }
+ } else {
+ property_dictionary()->CopyKeysTo(storage,
+ index,
+ filter,
+ NameDictionary::UNSORTED);
+ }
+}
+
+
+int JSObject::NumberOfLocalElements(PropertyAttributes filter) {
+ return GetLocalElementKeys(NULL, filter);
+}
+
+
+int JSObject::NumberOfEnumElements() {
+ // Fast case for objects with no elements.
+ if (!IsJSValue() && HasFastObjectElements()) {
+ uint32_t length = IsJSArray() ?
+ static_cast<uint32_t>(
+ Smi::cast(JSArray::cast(this)->length())->value()) :
+ static_cast<uint32_t>(FixedArray::cast(elements())->length());
+ if (length == 0) return 0;
+ }
+ // Compute the number of enumerable elements.
+ return NumberOfLocalElements(static_cast<PropertyAttributes>(DONT_ENUM));
+}
+
+
+int JSObject::GetLocalElementKeys(FixedArray* storage,
+ PropertyAttributes filter) {
+ int counter = 0;
+ switch (GetElementsKind()) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS: {
+ int length = IsJSArray() ?
+ Smi::cast(JSArray::cast(this)->length())->value() :
+ FixedArray::cast(elements())->length();
+ for (int i = 0; i < length; i++) {
+ if (!FixedArray::cast(elements())->get(i)->IsTheHole()) {
+ if (storage != NULL) {
+ storage->set(counter, Smi::FromInt(i));
+ }
+ counter++;
+ }
+ }
+ ASSERT(!storage || storage->length() >= counter);
+ break;
+ }
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS: {
+ int length = IsJSArray() ?
+ Smi::cast(JSArray::cast(this)->length())->value() :
+ FixedDoubleArray::cast(elements())->length();
+ for (int i = 0; i < length; i++) {
+ if (!FixedDoubleArray::cast(elements())->is_the_hole(i)) {
+ if (storage != NULL) {
+ storage->set(counter, Smi::FromInt(i));
+ }
+ counter++;
+ }
+ }
+ ASSERT(!storage || storage->length() >= counter);
+ break;
+ }
+ case EXTERNAL_PIXEL_ELEMENTS: {
+ int length = ExternalPixelArray::cast(elements())->length();
+ while (counter < length) {
+ if (storage != NULL) {
+ storage->set(counter, Smi::FromInt(counter));
+ }
+ counter++;
+ }
+ ASSERT(!storage || storage->length() >= counter);
+ 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: {
+ int length = ExternalArray::cast(elements())->length();
+ while (counter < length) {
+ if (storage != NULL) {
+ storage->set(counter, Smi::FromInt(counter));
+ }
+ counter++;
+ }
+ ASSERT(!storage || storage->length() >= counter);
+ break;
+ }
+ case DICTIONARY_ELEMENTS: {
+ if (storage != NULL) {
+ element_dictionary()->CopyKeysTo(storage,
+ filter,
+ SeededNumberDictionary::SORTED);
+ }
+ counter += element_dictionary()->NumberOfElementsFilterAttributes(filter);
+ break;
+ }
+ case NON_STRICT_ARGUMENTS_ELEMENTS: {
+ FixedArray* parameter_map = FixedArray::cast(elements());
+ int mapped_length = parameter_map->length() - 2;
+ FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+ if (arguments->IsDictionary()) {
+ // Copy the keys from arguments first, because Dictionary::CopyKeysTo
+ // will insert in storage starting at index 0.
+ SeededNumberDictionary* dictionary =
+ SeededNumberDictionary::cast(arguments);
+ if (storage != NULL) {
+ dictionary->CopyKeysTo(
+ storage, filter, SeededNumberDictionary::UNSORTED);
+ }
+ counter += dictionary->NumberOfElementsFilterAttributes(filter);
+ for (int i = 0; i < mapped_length; ++i) {
+ if (!parameter_map->get(i + 2)->IsTheHole()) {
+ if (storage != NULL) storage->set(counter, Smi::FromInt(i));
+ ++counter;
+ }
+ }
+ if (storage != NULL) storage->SortPairs(storage, counter);
+
+ } else {
+ int backing_length = arguments->length();
+ int i = 0;
+ for (; i < mapped_length; ++i) {
+ if (!parameter_map->get(i + 2)->IsTheHole()) {
+ if (storage != NULL) storage->set(counter, Smi::FromInt(i));
+ ++counter;
+ } else if (i < backing_length && !arguments->get(i)->IsTheHole()) {
+ if (storage != NULL) storage->set(counter, Smi::FromInt(i));
+ ++counter;
+ }
+ }
+ for (; i < backing_length; ++i) {
+ if (storage != NULL) storage->set(counter, Smi::FromInt(i));
+ ++counter;
+ }
+ }
+ break;
+ }
+ }
+
+ if (this->IsJSValue()) {
+ Object* val = JSValue::cast(this)->value();
+ if (val->IsString()) {
+ String* str = String::cast(val);
+ if (storage) {
+ for (int i = 0; i < str->length(); i++) {
+ storage->set(counter + i, Smi::FromInt(i));
+ }
+ }
+ counter += str->length();
+ }
+ }
+ ASSERT(!storage || storage->length() == counter);
+ return counter;
+}
+
+
+int JSObject::GetEnumElementKeys(FixedArray* storage) {
+ return GetLocalElementKeys(storage,
+ static_cast<PropertyAttributes>(DONT_ENUM));
+}
+
+
+// StringKey simply carries a string object as key.
+class StringKey : public HashTableKey {
+ public:
+ explicit StringKey(String* string) :
+ string_(string),
+ hash_(HashForObject(string)) { }
+
+ bool IsMatch(Object* string) {
+ // We know that all entries in a hash table had their hash keys created.
+ // Use that knowledge to have fast failure.
+ if (hash_ != HashForObject(string)) {
+ return false;
+ }
+ return string_->Equals(String::cast(string));
+ }
+
+ uint32_t Hash() { return hash_; }
+
+ uint32_t HashForObject(Object* other) { return String::cast(other)->Hash(); }
+
+ Object* AsObject(Heap* heap) { return string_; }
+
+ String* string_;
+ uint32_t hash_;
+};
+
+
+// StringSharedKeys are used as keys in the eval cache.
+class StringSharedKey : public HashTableKey {
+ public:
+ StringSharedKey(String* source,
+ SharedFunctionInfo* shared,
+ LanguageMode language_mode,
+ int scope_position)
+ : source_(source),
+ shared_(shared),
+ language_mode_(language_mode),
+ scope_position_(scope_position) { }
+
+ bool IsMatch(Object* other) {
+ if (!other->IsFixedArray()) return false;
+ FixedArray* other_array = FixedArray::cast(other);
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(other_array->get(0));
+ if (shared != shared_) return false;
+ 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,
+ 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 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 (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_, language_mode_, scope_position_);
+ }
+
+ uint32_t HashForObject(Object* obj) {
+ 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(Heap* heap) {
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateFixedArray(4);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ 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_;
+ LanguageMode language_mode_;
+ int scope_position_;
+};
+
+
+// RegExpKey carries the source and flags of a regular expression as key.
+class RegExpKey : public HashTableKey {
+ public:
+ RegExpKey(String* string, JSRegExp::Flags flags)
+ : string_(string),
+ flags_(Smi::FromInt(flags.value())) { }
+
+ // Rather than storing the key in the hash table, a pointer to the
+ // stored value is stored where the key should be. IsMatch then
+ // compares the search key to the found object, rather than comparing
+ // a key to a key.
+ bool IsMatch(Object* obj) {
+ FixedArray* val = FixedArray::cast(obj);
+ return string_->Equals(String::cast(val->get(JSRegExp::kSourceIndex)))
+ && (flags_ == val->get(JSRegExp::kFlagsIndex));
+ }
+
+ uint32_t Hash() { return RegExpHash(string_, flags_); }
+
+ Object* AsObject(Heap* heap) {
+ // Plain hash maps, which is where regexp keys are used, don't
+ // use this function.
+ UNREACHABLE();
+ return NULL;
+ }
+
+ uint32_t HashForObject(Object* obj) {
+ FixedArray* val = FixedArray::cast(obj);
+ return RegExpHash(String::cast(val->get(JSRegExp::kSourceIndex)),
+ Smi::cast(val->get(JSRegExp::kFlagsIndex)));
+ }
+
+ static uint32_t RegExpHash(String* string, Smi* flags) {
+ return string->Hash() + flags->value();
+ }
+
+ String* string_;
+ Smi* flags_;
+};
+
+
+// Utf8StringKey carries a vector of chars as key.
+class Utf8StringKey : public HashTableKey {
+ public:
+ explicit Utf8StringKey(Vector<const char> string, uint32_t seed)
+ : string_(string), hash_field_(0), seed_(seed) { }
+
+ bool IsMatch(Object* string) {
+ return String::cast(string)->IsUtf8EqualTo(string_);
+ }
+
+ uint32_t Hash() {
+ if (hash_field_ != 0) return hash_field_ >> String::kHashShift;
+ hash_field_ = StringHasher::ComputeUtf8Hash(string_, seed_, &chars_);
+ uint32_t result = hash_field_ >> String::kHashShift;
+ ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
+ return result;
+ }
+
+ uint32_t HashForObject(Object* other) {
+ return String::cast(other)->Hash();
+ }
+
+ MaybeObject* AsObject(Heap* heap) {
+ if (hash_field_ == 0) Hash();
+ return heap->AllocateInternalizedStringFromUtf8(string_,
+ chars_,
+ hash_field_);
+ }
+
+ Vector<const char> string_;
+ uint32_t hash_field_;
+ int chars_; // Caches the number of characters when computing the hash code.
+ uint32_t seed_;
+};
+
+
+template <typename Char>
+class SequentialStringKey : public HashTableKey {
+ public:
+ explicit SequentialStringKey(Vector<const Char> string, uint32_t seed)
+ : string_(string), hash_field_(0), seed_(seed) { }
+
+ uint32_t Hash() {
+ hash_field_ = StringHasher::HashSequentialString<Char>(string_.start(),
+ string_.length(),
+ seed_);
+
+ uint32_t result = hash_field_ >> String::kHashShift;
+ ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
+ return result;
+ }
+
+
+ uint32_t HashForObject(Object* other) {
+ return String::cast(other)->Hash();
+ }
+
+ Vector<const Char> string_;
+ uint32_t hash_field_;
+ uint32_t seed_;
+};
+
+
+
+class OneByteStringKey : public SequentialStringKey<uint8_t> {
+ public:
+ OneByteStringKey(Vector<const uint8_t> str, uint32_t seed)
+ : SequentialStringKey<uint8_t>(str, seed) { }
+
+ bool IsMatch(Object* string) {
+ return String::cast(string)->IsOneByteEqualTo(string_);
+ }
+
+ MaybeObject* AsObject(Heap* heap) {
+ if (hash_field_ == 0) Hash();
+ return heap->AllocateOneByteInternalizedString(string_, hash_field_);
+ }
+};
+
+
+class SubStringOneByteStringKey : public HashTableKey {
+ public:
+ explicit SubStringOneByteStringKey(Handle<SeqOneByteString> string,
+ int from,
+ int length)
+ : string_(string), from_(from), length_(length) { }
+
+ uint32_t Hash() {
+ ASSERT(length_ >= 0);
+ ASSERT(from_ + length_ <= string_->length());
+ uint8_t* chars = string_->GetChars() + from_;
+ hash_field_ = StringHasher::HashSequentialString(
+ chars, length_, string_->GetHeap()->HashSeed());
+ uint32_t result = hash_field_ >> String::kHashShift;
+ ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
+ return result;
+ }
+
+
+ uint32_t HashForObject(Object* other) {
+ return String::cast(other)->Hash();
+ }
+
+ bool IsMatch(Object* string) {
+ Vector<const uint8_t> chars(string_->GetChars() + from_, length_);
+ return String::cast(string)->IsOneByteEqualTo(chars);
+ }
+
+ MaybeObject* AsObject(Heap* heap) {
+ if (hash_field_ == 0) Hash();
+ Vector<const uint8_t> chars(string_->GetChars() + from_, length_);
+ return heap->AllocateOneByteInternalizedString(chars, hash_field_);
+ }
+
+ private:
+ Handle<SeqOneByteString> string_;
+ int from_;
+ int length_;
+ uint32_t hash_field_;
+};
+
+
+class TwoByteStringKey : public SequentialStringKey<uc16> {
+ public:
+ explicit TwoByteStringKey(Vector<const uc16> str, uint32_t seed)
+ : SequentialStringKey<uc16>(str, seed) { }
+
+ bool IsMatch(Object* string) {
+ return String::cast(string)->IsTwoByteEqualTo(string_);
+ }
+
+ MaybeObject* AsObject(Heap* heap) {
+ if (hash_field_ == 0) Hash();
+ return heap->AllocateTwoByteInternalizedString(string_, hash_field_);
+ }
+};
+
+
+// InternalizedStringKey carries a string/internalized-string object as key.
+class InternalizedStringKey : public HashTableKey {
+ public:
+ explicit InternalizedStringKey(String* string)
+ : string_(string) { }
+
+ bool IsMatch(Object* string) {
+ return String::cast(string)->Equals(string_);
+ }
+
+ uint32_t Hash() { return string_->Hash(); }
+
+ uint32_t HashForObject(Object* other) {
+ return String::cast(other)->Hash();
+ }
+
+ MaybeObject* AsObject(Heap* heap) {
+ // Attempt to flatten the string, so that internalized strings will most
+ // often be flat strings.
+ string_ = string_->TryFlattenGetString();
+ // Internalize the string if possible.
+ Map* map = heap->InternalizedStringMapForString(string_);
+ if (map != NULL) {
+ string_->set_map_no_write_barrier(map);
+ ASSERT(string_->IsInternalizedString());
+ return string_;
+ }
+ // Otherwise allocate a new internalized string.
+ return heap->AllocateInternalizedStringImpl(
+ string_, string_->length(), string_->hash_field());
+ }
+
+ static uint32_t StringHash(Object* obj) {
+ return String::cast(obj)->Hash();
+ }
+
+ String* string_;
+};
+
+
+template<typename Shape, typename Key>
+void HashTable<Shape, Key>::IteratePrefix(ObjectVisitor* v) {
+ IteratePointers(v, 0, kElementsStartOffset);
+}
+
+
+template<typename Shape, typename Key>
+void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
+ IteratePointers(v,
+ kElementsStartOffset,
+ kHeaderSize + length() * kPointerSize);
+}
+
+
+template<typename Shape, typename Key>
+MaybeObject* HashTable<Shape, Key>::Allocate(Heap* heap,
+ int at_least_space_for,
+ MinimumCapacity capacity_option,
+ PretenureFlag pretenure) {
+ ASSERT(!capacity_option || IS_POWER_OF_TWO(at_least_space_for));
+ int capacity = (capacity_option == USE_CUSTOM_MINIMUM_CAPACITY)
+ ? at_least_space_for
+ : ComputeCapacity(at_least_space_for);
+ if (capacity > HashTable::kMaxCapacity) {
+ return Failure::OutOfMemoryException(0x10);
+ }
+
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ heap-> AllocateHashTable(EntryToIndex(capacity), pretenure);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ HashTable::cast(obj)->SetNumberOfElements(0);
+ HashTable::cast(obj)->SetNumberOfDeletedElements(0);
+ HashTable::cast(obj)->SetCapacity(capacity);
+ return obj;
+}
+
+
+// Find entry for key otherwise return kNotFound.
+int NameDictionary::FindEntry(Name* key) {
+ if (!key->IsUniqueName()) {
+ return HashTable<NameDictionaryShape, Name*>::FindEntry(key);
+ }
+
+ // Optimized for unique names. Knowledge of the key type allows:
+ // 1. Move the check if the key is unique out of the loop.
+ // 2. Avoid comparing hash codes in unique-to-unique comparison.
+ // 3. Detect a case when a dictionary key is not unique but the key is.
+ // In case of positive result the dictionary key may be replaced by the
+ // internalized string with minimal performance penalty. It gives a chance
+ // to perform further lookups in code stubs (and significant performance
+ // boost a certain style of code).
+
+ // EnsureCapacity will guarantee the hash table is never full.
+ uint32_t capacity = Capacity();
+ uint32_t entry = FirstProbe(key->Hash(), capacity);
+ uint32_t count = 1;
+
+ while (true) {
+ int index = EntryToIndex(entry);
+ Object* element = get(index);
+ if (element->IsUndefined()) break; // Empty entry.
+ if (key == element) return entry;
+ if (!element->IsUniqueName() &&
+ !element->IsTheHole() &&
+ Name::cast(element)->Equals(key)) {
+ // Replace a key that is a non-internalized string by the equivalent
+ // internalized string for faster further lookups.
+ set(index, key);
+ return entry;
+ }
+ ASSERT(element->IsTheHole() || !Name::cast(element)->Equals(key));
+ entry = NextProbe(entry, count++, capacity);
+ }
+ return kNotFound;
+}
+
+
+template<typename Shape, typename Key>
+MaybeObject* HashTable<Shape, Key>::Rehash(HashTable* new_table, Key key) {
+ ASSERT(NumberOfElements() < new_table->Capacity());
+
+ DisallowHeapAllocation no_gc;
+ WriteBarrierMode mode = new_table->GetWriteBarrierMode(no_gc);
+
+ // Copy prefix to new array.
+ for (int i = kPrefixStartIndex;
+ i < kPrefixStartIndex + Shape::kPrefixSize;
+ i++) {
+ new_table->set(i, get(i), mode);
+ }
+
+ // Rehash the elements.
+ int capacity = Capacity();
+ for (int i = 0; i < capacity; i++) {
+ uint32_t from_index = EntryToIndex(i);
+ Object* k = get(from_index);
+ if (IsKey(k)) {
+ uint32_t hash = HashTable<Shape, Key>::HashForObject(key, k);
+ uint32_t insertion_index =
+ EntryToIndex(new_table->FindInsertionEntry(hash));
+ for (int j = 0; j < Shape::kEntrySize; j++) {
+ new_table->set(insertion_index + j, get(from_index + j), mode);
+ }
+ }
+ }
+ new_table->SetNumberOfElements(NumberOfElements());
+ new_table->SetNumberOfDeletedElements(0);
+ return new_table;
+}
+
+
+template<typename Shape, typename Key>
+MaybeObject* HashTable<Shape, Key>::EnsureCapacity(int n, Key key) {
+ int capacity = Capacity();
+ int nof = NumberOfElements() + n;
+ int nod = NumberOfDeletedElements();
+ // Return if:
+ // 50% is still free after adding n elements and
+ // at most 50% of the free elements are deleted elements.
+ if (nod <= (capacity - nof) >> 1) {
+ int needed_free = nof >> 1;
+ if (nof + needed_free <= capacity) return this;
+ }
+
+ const int kMinCapacityForPretenure = 256;
+ bool pretenure =
+ (capacity > kMinCapacityForPretenure) && !GetHeap()->InNewSpace(this);
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ Allocate(GetHeap(),
+ nof * 2,
+ USE_DEFAULT_MINIMUM_CAPACITY,
+ pretenure ? TENURED : NOT_TENURED);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ return Rehash(HashTable::cast(obj), key);
+}
+
+
+template<typename Shape, typename Key>
+MaybeObject* HashTable<Shape, Key>::Shrink(Key key) {
+ int capacity = Capacity();
+ int nof = NumberOfElements();
+
+ // Shrink to fit the number of elements if only a quarter of the
+ // capacity is filled with elements.
+ if (nof > (capacity >> 2)) return this;
+ // Allocate a new dictionary with room for at least the current
+ // number of elements. The allocation method will make sure that
+ // there is extra room in the dictionary for additions. Don't go
+ // lower than room for 16 elements.
+ int at_least_room_for = nof;
+ if (at_least_room_for < 16) return this;
+
+ const int kMinCapacityForPretenure = 256;
+ bool pretenure =
+ (at_least_room_for > kMinCapacityForPretenure) &&
+ !GetHeap()->InNewSpace(this);
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ Allocate(GetHeap(),
+ at_least_room_for,
+ USE_DEFAULT_MINIMUM_CAPACITY,
+ pretenure ? TENURED : NOT_TENURED);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ return Rehash(HashTable::cast(obj), key);
+}
+
+
+template<typename Shape, typename Key>
+uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
+ uint32_t capacity = Capacity();
+ uint32_t entry = FirstProbe(hash, capacity);
+ uint32_t count = 1;
+ // EnsureCapacity will guarantee the hash table is never full.
+ while (true) {
+ Object* element = KeyAt(entry);
+ if (element->IsUndefined() || element->IsTheHole()) break;
+ entry = NextProbe(entry, count++, capacity);
+ }
+ return entry;
+}
+
+
+// Force instantiation of template instances class.
+// Please note this list is compiler dependent.
+
+template class HashTable<StringTableShape, HashTableKey*>;
+
+template class HashTable<CompilationCacheShape, HashTableKey*>;
+
+template class HashTable<MapCacheShape, HashTableKey*>;
+
+template class HashTable<ObjectHashTableShape<1>, Object*>;
+
+template class HashTable<ObjectHashTableShape<2>, Object*>;
+
+template class Dictionary<NameDictionaryShape, Name*>;
+
+template class Dictionary<SeededNumberDictionaryShape, uint32_t>;
+
+template class Dictionary<UnseededNumberDictionaryShape, uint32_t>;
+
+template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::
+ Allocate(Heap* heap, int at_least_space_for, PretenureFlag pretenure);
+
+template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>::
+ Allocate(Heap* heap, int at_least_space_for, PretenureFlag pretenure);
+
+template MaybeObject* Dictionary<NameDictionaryShape, Name*>::
+ Allocate(Heap* heap, int n, PretenureFlag pretenure);
+
+template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::AtPut(
+ uint32_t, Object*);
+
+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);
+
+template Object* Dictionary<NameDictionaryShape, Name*>::SlowReverseLookup(
+ Object*);
+
+template void Dictionary<SeededNumberDictionaryShape, uint32_t>::CopyKeysTo(
+ FixedArray*,
+ PropertyAttributes,
+ Dictionary<SeededNumberDictionaryShape, uint32_t>::SortMode);
+
+template Object* Dictionary<NameDictionaryShape, Name*>::DeleteProperty(
+ int, JSObject::DeleteMode);
+
+template Object* Dictionary<SeededNumberDictionaryShape, uint32_t>::
+ DeleteProperty(int, JSObject::DeleteMode);
+
+template MaybeObject* Dictionary<NameDictionaryShape, Name*>::Shrink(Name* n);
+
+template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::Shrink(
+ uint32_t);
+
+template void Dictionary<NameDictionaryShape, Name*>::CopyKeysTo(
+ FixedArray*,
+ int,
+ PropertyAttributes,
+ Dictionary<NameDictionaryShape, Name*>::SortMode);
+
+template int
+Dictionary<NameDictionaryShape, Name*>::NumberOfElementsFilterAttributes(
+ PropertyAttributes);
+
+template MaybeObject* Dictionary<NameDictionaryShape, Name*>::Add(
+ Name*, Object*, PropertyDetails);
+
+template MaybeObject*
+Dictionary<NameDictionaryShape, Name*>::GenerateNewEnumerationIndices();
+
+template int
+Dictionary<SeededNumberDictionaryShape, uint32_t>::
+ NumberOfElementsFilterAttributes(PropertyAttributes);
+
+template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::Add(
+ uint32_t, Object*, PropertyDetails);
+
+template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>::Add(
+ uint32_t, Object*, PropertyDetails);
+
+template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::
+ EnsureCapacity(int, uint32_t);
+
+template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>::
+ EnsureCapacity(int, uint32_t);
+
+template MaybeObject* Dictionary<NameDictionaryShape, Name*>::
+ EnsureCapacity(int, Name*);
+
+template MaybeObject* Dictionary<SeededNumberDictionaryShape, uint32_t>::
+ AddEntry(uint32_t, Object*, PropertyDetails, uint32_t);
+
+template MaybeObject* Dictionary<UnseededNumberDictionaryShape, uint32_t>::
+ AddEntry(uint32_t, Object*, PropertyDetails, uint32_t);
+
+template MaybeObject* Dictionary<NameDictionaryShape, Name*>::AddEntry(
+ Name*, Object*, PropertyDetails, uint32_t);
+
+template
+int Dictionary<SeededNumberDictionaryShape, uint32_t>::NumberOfEnumElements();
+
+template
+int Dictionary<NameDictionaryShape, Name*>::NumberOfEnumElements();
+
+template
+int HashTable<SeededNumberDictionaryShape, uint32_t>::FindEntry(uint32_t);
+
+
+// Collates undefined and unexisting elements below limit from position
+// zero of the elements. The object stays in Dictionary mode.
+MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) {
+ ASSERT(HasDictionaryElements());
+ // Must stay in dictionary mode, either because of requires_slow_elements,
+ // or because we are not going to sort (and therefore compact) all of the
+ // elements.
+ SeededNumberDictionary* dict = element_dictionary();
+ HeapNumber* result_double = NULL;
+ if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
+ // Allocate space for result before we start mutating the object.
+ Object* new_double;
+ { MaybeObject* maybe_new_double = GetHeap()->AllocateHeapNumber(0.0);
+ if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
+ }
+ result_double = HeapNumber::cast(new_double);
+ }
+
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ SeededNumberDictionary::Allocate(GetHeap(), dict->NumberOfElements());
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ SeededNumberDictionary* new_dict = SeededNumberDictionary::cast(obj);
+
+ DisallowHeapAllocation no_alloc;
+
+ uint32_t pos = 0;
+ uint32_t undefs = 0;
+ int capacity = dict->Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Object* k = dict->KeyAt(i);
+ if (dict->IsKey(k)) {
+ ASSERT(k->IsNumber());
+ ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0);
+ ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0);
+ ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32);
+ Object* value = dict->ValueAt(i);
+ PropertyDetails details = dict->DetailsAt(i);
+ if (details.type() == CALLBACKS || details.IsReadOnly()) {
+ // Bail out and do the sorting of undefineds and array holes in JS.
+ // Also bail out if the element is not supposed to be moved.
+ return Smi::FromInt(-1);
+ }
+ uint32_t key = NumberToUint32(k);
+ // In the following we assert that adding the entry to the new dictionary
+ // does not cause GC. This is the case because we made sure to allocate
+ // the dictionary big enough above, so it need not grow.
+ if (key < limit) {
+ if (value->IsUndefined()) {
+ undefs++;
+ } else {
+ if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
+ // Adding an entry with the key beyond smi-range requires
+ // allocation. Bailout.
+ return Smi::FromInt(-1);
+ }
+ new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked();
+ pos++;
+ }
+ } else {
+ if (key > static_cast<uint32_t>(Smi::kMaxValue)) {
+ // Adding an entry with the key beyond smi-range requires
+ // allocation. Bailout.
+ return Smi::FromInt(-1);
+ }
+ new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked();
+ }
+ }
+ }
+
+ uint32_t result = pos;
+ PropertyDetails no_details = PropertyDetails(NONE, NORMAL, 0);
+ Heap* heap = GetHeap();
+ while (undefs > 0) {
+ if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
+ // Adding an entry with the key beyond smi-range requires
+ // allocation. Bailout.
+ return Smi::FromInt(-1);
+ }
+ new_dict->AddNumberEntry(pos, heap->undefined_value(), no_details)->
+ ToObjectUnchecked();
+ pos++;
+ undefs--;
+ }
+
+ set_elements(new_dict);
+
+ if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
+ return Smi::FromInt(static_cast<int>(result));
+ }
+
+ ASSERT_NE(NULL, result_double);
+ result_double->set_value(static_cast<double>(result));
+ return result_double;
+}
+
+
+// Collects all defined (non-hole) and non-undefined (array) elements at
+// the start of the elements array.
+// If the object is in dictionary mode, it is converted to fast elements
+// mode.
+MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
+ Heap* heap = GetHeap();
+
+ ASSERT(!map()->is_observed());
+ if (HasDictionaryElements()) {
+ // Convert to fast elements containing only the existing properties.
+ // Ordering is irrelevant, since we are going to sort anyway.
+ SeededNumberDictionary* dict = element_dictionary();
+ if (IsJSArray() || dict->requires_slow_elements() ||
+ dict->max_number_key() >= limit) {
+ return PrepareSlowElementsForSort(limit);
+ }
+ // Convert to fast elements.
+
+ Object* obj;
+ MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(),
+ FAST_HOLEY_ELEMENTS);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ Map* new_map = Map::cast(obj);
+
+ PretenureFlag tenure = heap->InNewSpace(this) ? NOT_TENURED: TENURED;
+ Object* new_array;
+ { MaybeObject* maybe_new_array =
+ heap->AllocateFixedArray(dict->NumberOfElements(), tenure);
+ if (!maybe_new_array->ToObject(&new_array)) return maybe_new_array;
+ }
+ FixedArray* fast_elements = FixedArray::cast(new_array);
+ dict->CopyValuesTo(fast_elements);
+ ValidateElements();
+
+ set_map_and_elements(new_map, 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(HasFastSmiOrObjectElements() || 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.
+
+ FixedArrayBase* elements_base = FixedArrayBase::cast(this->elements());
+ uint32_t elements_length = static_cast<uint32_t>(elements_base->length());
+ if (limit > elements_length) {
+ limit = elements_length ;
+ }
+ if (limit == 0) {
+ return Smi::FromInt(0);
+ }
+
+ HeapNumber* result_double = NULL;
+ if (limit > static_cast<uint32_t>(Smi::kMaxValue)) {
+ // Pessimistically allocate space for return value before
+ // we start mutating the array.
+ Object* new_double;
+ { MaybeObject* maybe_new_double = heap->AllocateHeapNumber(0.0);
+ if (!maybe_new_double->ToObject(&new_double)) return maybe_new_double;
+ }
+ result_double = HeapNumber::cast(new_double);
+ }
+
+ uint32_t result = 0;
+ if (elements_base->map() == heap->fixed_double_array_map()) {
+ FixedDoubleArray* elements = FixedDoubleArray::cast(elements_base);
+ // Split elements into defined and the_hole, in that order.
+ unsigned int holes = limit;
+ // Assume most arrays contain no holes and undefined values, so minimize the
+ // number of stores of non-undefined, non-the-hole values.
+ for (unsigned int i = 0; i < holes; i++) {
+ if (elements->is_the_hole(i)) {
+ holes--;
+ } else {
+ continue;
+ }
+ // Position i needs to be filled.
+ while (holes > i) {
+ if (elements->is_the_hole(holes)) {
+ holes--;
+ } else {
+ elements->set(i, elements->get_scalar(holes));
+ break;
+ }
+ }
+ }
+ result = holes;
+ while (holes < limit) {
+ elements->set_the_hole(holes);
+ holes++;
+ }
+ } else {
+ FixedArray* elements = FixedArray::cast(elements_base);
+ DisallowHeapAllocation no_gc;
+
+ // Split elements into defined, undefined and the_hole, in that order. Only
+ // count locations for undefined and the hole, and fill them afterwards.
+ WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(no_gc);
+ unsigned int undefs = limit;
+ unsigned int holes = limit;
+ // Assume most arrays contain no holes and undefined values, so minimize the
+ // number of stores of non-undefined, non-the-hole values.
+ for (unsigned int i = 0; i < undefs; i++) {
+ Object* current = elements->get(i);
+ if (current->IsTheHole()) {
+ holes--;
+ undefs--;
+ } else if (current->IsUndefined()) {
+ undefs--;
+ } else {
+ continue;
+ }
+ // Position i needs to be filled.
+ while (undefs > i) {
+ current = elements->get(undefs);
+ if (current->IsTheHole()) {
+ holes--;
+ undefs--;
+ } else if (current->IsUndefined()) {
+ undefs--;
+ } else {
+ elements->set(i, current, write_barrier);
+ break;
+ }
+ }
+ }
+ result = undefs;
+ while (undefs < holes) {
+ elements->set_undefined(undefs);
+ undefs++;
+ }
+ while (holes < limit) {
+ elements->set_the_hole(holes);
+ holes++;
+ }
+ }
+
+ if (result <= static_cast<uint32_t>(Smi::kMaxValue)) {
+ return Smi::FromInt(static_cast<int>(result));
+ }
+ ASSERT_NE(NULL, result_double);
+ result_double->set_value(static_cast<double>(result));
+ return result_double;
+}
+
+
+ExternalArrayType JSTypedArray::type() {
+ switch (elements()->map()->instance_type()) {
+ case EXTERNAL_BYTE_ARRAY_TYPE:
+ return kExternalByteArray;
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ return kExternalUnsignedByteArray;
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ return kExternalShortArray;
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ return kExternalUnsignedShortArray;
+ case EXTERNAL_INT_ARRAY_TYPE:
+ return kExternalIntArray;
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ return kExternalUnsignedIntArray;
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ return kExternalFloatArray;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ return kExternalDoubleArray;
+ case EXTERNAL_PIXEL_ARRAY_TYPE:
+ return kExternalPixelArray;
+ default:
+ return static_cast<ExternalArrayType>(-1);
+ }
+}
+
+
+size_t JSTypedArray::element_size() {
+ switch (elements()->map()->instance_type()) {
+ case EXTERNAL_BYTE_ARRAY_TYPE:
+ return 1;
+ case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ return 1;
+ case EXTERNAL_SHORT_ARRAY_TYPE:
+ return 2;
+ case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ return 2;
+ case EXTERNAL_INT_ARRAY_TYPE:
+ return 4;
+ case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ return 4;
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ return 4;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ return 8;
+ case EXTERNAL_PIXEL_ARRAY_TYPE:
+ return 1;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+}
+
+
+Object* ExternalPixelArray::SetValue(uint32_t index, Object* value) {
+ uint8_t clamped_value = 0;
+ if (index < static_cast<uint32_t>(length())) {
+ if (value->IsSmi()) {
+ int int_value = Smi::cast(value)->value();
+ if (int_value < 0) {
+ clamped_value = 0;
+ } else if (int_value > 255) {
+ clamped_value = 255;
+ } else {
+ clamped_value = static_cast<uint8_t>(int_value);
+ }
+ } else if (value->IsHeapNumber()) {
+ double double_value = HeapNumber::cast(value)->value();
+ if (!(double_value > 0)) {
+ // NaN and less than zero clamp to zero.
+ clamped_value = 0;
+ } else if (double_value > 255) {
+ // Greater than 255 clamp to 255.
+ clamped_value = 255;
+ } else {
+ // Other doubles are rounded to the nearest integer.
+ clamped_value = static_cast<uint8_t>(lrint(double_value));
+ }
+ } else {
+ // Clamp undefined to zero (default). All other types have been
+ // converted to a number type further up in the call chain.
+ ASSERT(value->IsUndefined());
+ }
+ set(index, clamped_value);
+ }
+ return Smi::FromInt(clamped_value);
+}
+
+
+template<typename ExternalArrayClass, typename ValueType>
+static MaybeObject* ExternalArrayIntSetter(Heap* heap,
+ ExternalArrayClass* receiver,
+ uint32_t index,
+ Object* value) {
+ ValueType cast_value = 0;
+ if (index < static_cast<uint32_t>(receiver->length())) {
+ if (value->IsSmi()) {
+ int int_value = Smi::cast(value)->value();
+ cast_value = static_cast<ValueType>(int_value);
+ } else if (value->IsHeapNumber()) {
+ double double_value = HeapNumber::cast(value)->value();
+ cast_value = static_cast<ValueType>(DoubleToInt32(double_value));
+ } else {
+ // Clamp undefined to zero (default). All other types have been
+ // converted to a number type further up in the call chain.
+ ASSERT(value->IsUndefined());
+ }
+ receiver->set(index, cast_value);
+ }
+ return heap->NumberFromInt32(cast_value);
+}
+
+
+MaybeObject* ExternalByteArray::SetValue(uint32_t index, Object* value) {
+ return ExternalArrayIntSetter<ExternalByteArray, int8_t>
+ (GetHeap(), this, index, value);
+}
+
+
+MaybeObject* ExternalUnsignedByteArray::SetValue(uint32_t index,
+ Object* value) {
+ return ExternalArrayIntSetter<ExternalUnsignedByteArray, uint8_t>
+ (GetHeap(), this, index, value);
+}
+
+
+MaybeObject* ExternalShortArray::SetValue(uint32_t index,
+ Object* value) {
+ return ExternalArrayIntSetter<ExternalShortArray, int16_t>
+ (GetHeap(), this, index, value);
+}
+
+
+MaybeObject* ExternalUnsignedShortArray::SetValue(uint32_t index,
+ Object* value) {
+ return ExternalArrayIntSetter<ExternalUnsignedShortArray, uint16_t>
+ (GetHeap(), this, index, value);
+}
+
+
+MaybeObject* ExternalIntArray::SetValue(uint32_t index, Object* value) {
+ return ExternalArrayIntSetter<ExternalIntArray, int32_t>
+ (GetHeap(), this, index, value);
+}
+
+
+MaybeObject* ExternalUnsignedIntArray::SetValue(uint32_t index, Object* value) {
+ uint32_t cast_value = 0;
+ Heap* heap = GetHeap();
+ if (index < static_cast<uint32_t>(length())) {
+ if (value->IsSmi()) {
+ int int_value = Smi::cast(value)->value();
+ cast_value = static_cast<uint32_t>(int_value);
+ } else if (value->IsHeapNumber()) {
+ double double_value = HeapNumber::cast(value)->value();
+ cast_value = static_cast<uint32_t>(DoubleToUint32(double_value));
+ } else {
+ // Clamp undefined to zero (default). All other types have been
+ // converted to a number type further up in the call chain.
+ ASSERT(value->IsUndefined());
+ }
+ set(index, cast_value);
+ }
+ return heap->NumberFromUint32(cast_value);
+}
+
+
+MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
+ float cast_value = static_cast<float>(OS::nan_value());
+ Heap* heap = GetHeap();
+ if (index < static_cast<uint32_t>(length())) {
+ if (value->IsSmi()) {
+ int int_value = Smi::cast(value)->value();
+ cast_value = static_cast<float>(int_value);
+ } else if (value->IsHeapNumber()) {
+ double double_value = HeapNumber::cast(value)->value();
+ cast_value = static_cast<float>(double_value);
+ } else {
+ // Clamp undefined to NaN (default). All other types have been
+ // converted to a number type further up in the call chain.
+ ASSERT(value->IsUndefined());
+ }
+ set(index, cast_value);
+ }
+ return heap->AllocateHeapNumber(cast_value);
+}
+
+
+MaybeObject* ExternalDoubleArray::SetValue(uint32_t index, Object* value) {
+ double double_value = OS::nan_value();
+ Heap* heap = GetHeap();
+ if (index < static_cast<uint32_t>(length())) {
+ if (value->IsSmi()) {
+ int int_value = Smi::cast(value)->value();
+ double_value = static_cast<double>(int_value);
+ } else if (value->IsHeapNumber()) {
+ double_value = HeapNumber::cast(value)->value();
+ } else {
+ // Clamp undefined to NaN (default). All other types have been
+ // converted to a number type further up in the call chain.
+ ASSERT(value->IsUndefined());
+ }
+ set(index, double_value);
+ }
+ return heap->AllocateHeapNumber(double_value);
+}
+
+
+PropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) {
+ ASSERT(!HasFastProperties());
+ Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
+ return PropertyCell::cast(value);
+}
+
+
+// TODO(mstarzinger): Temporary wrapper until handlified.
+static Handle<NameDictionary> NameDictionaryAdd(Handle<NameDictionary> dict,
+ Handle<Name> name,
+ Handle<Object> value,
+ PropertyDetails details) {
+ CALL_HEAP_FUNCTION(dict->GetIsolate(),
+ dict->Add(*name, *value, details),
+ NameDictionary);
+}
+
+
+Handle<PropertyCell> GlobalObject::EnsurePropertyCell(
+ Handle<GlobalObject> global,
+ Handle<Name> name) {
+ ASSERT(!global->HasFastProperties());
+ int entry = global->property_dictionary()->FindEntry(*name);
+ if (entry == NameDictionary::kNotFound) {
+ Isolate* isolate = global->GetIsolate();
+ Handle<PropertyCell> cell = isolate->factory()->NewPropertyCell(
+ isolate->factory()->the_hole_value());
+ PropertyDetails details(NONE, NORMAL, 0);
+ details = details.AsDeleted();
+ Handle<NameDictionary> dictionary = NameDictionaryAdd(
+ handle(global->property_dictionary()), name, cell, details);
+ global->set_properties(*dictionary);
+ return cell;
+ } else {
+ Object* value = global->property_dictionary()->ValueAt(entry);
+ ASSERT(value->IsPropertyCell());
+ return handle(PropertyCell::cast(value));
+ }
+}
+
+
+MaybeObject* StringTable::LookupString(String* string, Object** s) {
+ InternalizedStringKey key(string);
+ return LookupKey(&key, s);
+}
+
+
+// This class is used for looking up two character strings in the string table.
+// If we don't have a hit we don't want to waste much time so we unroll the
+// string hash calculation loop here for speed. Doesn't work if the two
+// characters form a decimal integer, since such strings have a different hash
+// algorithm.
+class TwoCharHashTableKey : public HashTableKey {
+ public:
+ TwoCharHashTableKey(uint16_t c1, uint16_t c2, uint32_t seed)
+ : c1_(c1), c2_(c2) {
+ // Char 1.
+ uint32_t hash = seed;
+ hash += c1;
+ hash += hash << 10;
+ hash ^= hash >> 6;
+ // Char 2.
+ hash += c2;
+ hash += hash << 10;
+ hash ^= hash >> 6;
+ // GetHash.
+ hash += hash << 3;
+ hash ^= hash >> 11;
+ hash += hash << 15;
+ if ((hash & String::kHashBitMask) == 0) hash = StringHasher::kZeroHash;
+ hash_ = hash;
+#ifdef DEBUG
+ // If this assert fails then we failed to reproduce the two-character
+ // version of the string hashing algorithm above. One reason could be
+ // that we were passed two digits as characters, since the hash
+ // algorithm is different in that case.
+ uint16_t chars[2] = {c1, c2};
+ uint32_t check_hash = StringHasher::HashSequentialString(chars, 2, seed);
+ hash = (hash << String::kHashShift) | String::kIsNotArrayIndexMask;
+ ASSERT_EQ(static_cast<int32_t>(hash), static_cast<int32_t>(check_hash));
+#endif
+ }
+
+ bool IsMatch(Object* o) {
+ if (!o->IsString()) return false;
+ String* other = String::cast(o);
+ if (other->length() != 2) return false;
+ if (other->Get(0) != c1_) return false;
+ return other->Get(1) == c2_;
+ }
+
+ uint32_t Hash() { return hash_; }
+ uint32_t HashForObject(Object* key) {
+ if (!key->IsString()) return 0;
+ return String::cast(key)->Hash();
+ }
+
+ Object* AsObject(Heap* heap) {
+ // The TwoCharHashTableKey is only used for looking in the string
+ // table, not for adding to it.
+ UNREACHABLE();
+ return NULL;
+ }
+
+ private:
+ uint16_t c1_;
+ uint16_t c2_;
+ uint32_t hash_;
+};
+
+
+bool StringTable::LookupStringIfExists(String* string, String** result) {
+ InternalizedStringKey key(string);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) {
+ return false;
+ } else {
+ *result = String::cast(KeyAt(entry));
+ ASSERT(StringShape(*result).IsInternalized());
+ return true;
+ }
+}
+
+
+bool StringTable::LookupTwoCharsStringIfExists(uint16_t c1,
+ uint16_t c2,
+ String** result) {
+ TwoCharHashTableKey key(c1, c2, GetHeap()->HashSeed());
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) {
+ return false;
+ } else {
+ *result = String::cast(KeyAt(entry));
+ ASSERT(StringShape(*result).IsInternalized());
+ return true;
+ }
+}
+
+
+MaybeObject* StringTable::LookupUtf8String(Vector<const char> str,
+ Object** s) {
+ Utf8StringKey key(str, GetHeap()->HashSeed());
+ return LookupKey(&key, s);
+}
+
+
+MaybeObject* StringTable::LookupOneByteString(Vector<const uint8_t> str,
+ Object** s) {
+ OneByteStringKey key(str, GetHeap()->HashSeed());
+ return LookupKey(&key, s);
+}
+
+
+MaybeObject* StringTable::LookupSubStringOneByteString(
+ Handle<SeqOneByteString> str,
+ int from,
+ int length,
+ Object** s) {
+ SubStringOneByteStringKey key(str, from, length);
+ return LookupKey(&key, s);
+}
+
+
+MaybeObject* StringTable::LookupTwoByteString(Vector<const uc16> str,
+ Object** s) {
+ TwoByteStringKey key(str, GetHeap()->HashSeed());
+ return LookupKey(&key, s);
+}
+
+
+MaybeObject* StringTable::LookupKey(HashTableKey* key, Object** s) {
+ int entry = FindEntry(key);
+
+ // String already in table.
+ if (entry != kNotFound) {
+ *s = KeyAt(entry);
+ return this;
+ }
+
+ // Adding new string. Grow table if needed.
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ // Create string object.
+ Object* string;
+ { MaybeObject* maybe_string = key->AsObject(GetHeap());
+ if (!maybe_string->ToObject(&string)) return maybe_string;
+ }
+
+ // If the string table grew as part of EnsureCapacity, obj is not
+ // the current string table and therefore we cannot use
+ // StringTable::cast here.
+ StringTable* table = reinterpret_cast<StringTable*>(obj);
+
+ // Add the new string and return it along with the string table.
+ entry = table->FindInsertionEntry(key->Hash());
+ table->set(EntryToIndex(entry), string);
+ table->ElementAdded();
+ *s = string;
+ return table;
+}
+
+
+// The key for the script compilation cache is dependent on the mode flags,
+// because they change the global language mode and thus binding behaviour.
+// If flags change at some point, we must ensure that we do not hit the cache
+// for code compiled with different settings.
+static LanguageMode CurrentGlobalLanguageMode() {
+ return FLAG_use_strict
+ ? (FLAG_harmony_scoping ? EXTENDED_MODE : STRICT_MODE)
+ : CLASSIC_MODE;
+}
+
+
+Object* CompilationCacheTable::Lookup(String* src, Context* context) {
+ SharedFunctionInfo* shared = context->closure()->shared();
+ StringSharedKey key(src,
+ shared,
+ CurrentGlobalLanguageMode(),
+ RelocInfo::kNoPosition);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) return GetHeap()->undefined_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
+Object* CompilationCacheTable::LookupEval(String* src,
+ Context* context,
+ 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);
+}
+
+
+Object* CompilationCacheTable::LookupRegExp(String* src,
+ JSRegExp::Flags flags) {
+ RegExpKey key(src, flags);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) return GetHeap()->undefined_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
+MaybeObject* CompilationCacheTable::Put(String* src,
+ Context* context,
+ Object* value) {
+ SharedFunctionInfo* shared = context->closure()->shared();
+ StringSharedKey key(src,
+ shared,
+ CurrentGlobalLanguageMode(),
+ RelocInfo::kNoPosition);
+ CompilationCacheTable* cache;
+ MaybeObject* maybe_cache = EnsureCapacity(1, &key);
+ if (!maybe_cache->To(&cache)) return maybe_cache;
+
+ Object* k;
+ MaybeObject* maybe_k = key.AsObject(GetHeap());
+ if (!maybe_k->To(&k)) return maybe_k;
+
+ int entry = cache->FindInsertionEntry(key.Hash());
+ cache->set(EntryToIndex(entry), k);
+ cache->set(EntryToIndex(entry) + 1, value);
+ cache->ElementAdded();
+ return cache;
+}
+
+
+MaybeObject* CompilationCacheTable::PutEval(String* src,
+ Context* context,
+ SharedFunctionInfo* value,
+ int scope_position) {
+ StringSharedKey key(src,
+ context->closure()->shared(),
+ value->language_mode(),
+ scope_position);
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ CompilationCacheTable* cache =
+ reinterpret_cast<CompilationCacheTable*>(obj);
+ int entry = cache->FindInsertionEntry(key.Hash());
+
+ Object* k;
+ { MaybeObject* maybe_k = key.AsObject(GetHeap());
+ if (!maybe_k->ToObject(&k)) return maybe_k;
+ }
+
+ cache->set(EntryToIndex(entry), k);
+ cache->set(EntryToIndex(entry) + 1, value);
+ cache->ElementAdded();
+ return cache;
+}
+
+
+MaybeObject* CompilationCacheTable::PutRegExp(String* src,
+ JSRegExp::Flags flags,
+ FixedArray* value) {
+ RegExpKey key(src, flags);
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ CompilationCacheTable* cache =
+ reinterpret_cast<CompilationCacheTable*>(obj);
+ int entry = cache->FindInsertionEntry(key.Hash());
+ // We store the value in the key slot, and compare the search key
+ // to the stored value with a custon IsMatch function during lookups.
+ cache->set(EntryToIndex(entry), value);
+ cache->set(EntryToIndex(entry) + 1, value);
+ cache->ElementAdded();
+ return cache;
+}
+
+
+void CompilationCacheTable::Remove(Object* 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) {
+ NoWriteBarrierSet(this, entry_index, the_hole_value);
+ NoWriteBarrierSet(this, value_index, the_hole_value);
+ ElementRemoved();
+ }
+ }
+ return;
+}
+
+
+// StringsKey used for HashTable where key is array of internalized strings.
+class StringsKey : public HashTableKey {
+ public:
+ explicit StringsKey(FixedArray* strings) : strings_(strings) { }
+
+ bool IsMatch(Object* strings) {
+ FixedArray* o = FixedArray::cast(strings);
+ int len = strings_->length();
+ if (o->length() != len) return false;
+ for (int i = 0; i < len; i++) {
+ if (o->get(i) != strings_->get(i)) return false;
+ }
+ return true;
+ }
+
+ uint32_t Hash() { return HashForObject(strings_); }
+
+ uint32_t HashForObject(Object* obj) {
+ FixedArray* strings = FixedArray::cast(obj);
+ int len = strings->length();
+ uint32_t hash = 0;
+ for (int i = 0; i < len; i++) {
+ hash ^= String::cast(strings->get(i))->Hash();
+ }
+ return hash;
+ }
+
+ Object* AsObject(Heap* heap) { return strings_; }
+
+ private:
+ FixedArray* strings_;
+};
+
+
+Object* MapCache::Lookup(FixedArray* array) {
+ StringsKey key(array);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) return GetHeap()->undefined_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
+MaybeObject* MapCache::Put(FixedArray* array, Map* value) {
+ StringsKey key(array);
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ MapCache* cache = reinterpret_cast<MapCache*>(obj);
+ int entry = cache->FindInsertionEntry(key.Hash());
+ cache->set(EntryToIndex(entry), array);
+ cache->set(EntryToIndex(entry) + 1, value);
+ cache->ElementAdded();
+ return cache;
+}
+
+
+template<typename Shape, typename Key>
+MaybeObject* Dictionary<Shape, Key>::Allocate(Heap* heap,
+ int at_least_space_for,
+ PretenureFlag pretenure) {
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ HashTable<Shape, Key>::Allocate(
+ heap,
+ at_least_space_for,
+ HashTable<Shape, Key>::USE_DEFAULT_MINIMUM_CAPACITY,
+ pretenure);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ // Initialize the next enumeration index.
+ Dictionary<Shape, Key>::cast(obj)->
+ SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
+ return obj;
+}
+
+
+void NameDictionary::DoGenerateNewEnumerationIndices(
+ Handle<NameDictionary> dictionary) {
+ CALL_HEAP_FUNCTION_VOID(dictionary->GetIsolate(),
+ dictionary->GenerateNewEnumerationIndices());
+}
+
+template<typename Shape, typename Key>
+MaybeObject* Dictionary<Shape, Key>::GenerateNewEnumerationIndices() {
+ Heap* heap = Dictionary<Shape, Key>::GetHeap();
+ int length = HashTable<Shape, Key>::NumberOfElements();
+
+ // Allocate and initialize iteration order array.
+ Object* obj;
+ { MaybeObject* maybe_obj = heap->AllocateFixedArray(length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedArray* iteration_order = FixedArray::cast(obj);
+ for (int i = 0; i < length; i++) {
+ iteration_order->set(i, Smi::FromInt(i));
+ }
+
+ // Allocate array with enumeration order.
+ { MaybeObject* maybe_obj = heap->AllocateFixedArray(length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ FixedArray* enumeration_order = FixedArray::cast(obj);
+
+ // Fill the enumeration order array with property details.
+ int capacity = HashTable<Shape, Key>::Capacity();
+ int pos = 0;
+ for (int i = 0; i < capacity; i++) {
+ if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
+ int index = DetailsAt(i).dictionary_index();
+ enumeration_order->set(pos++, Smi::FromInt(index));
+ }
+ }
+
+ // Sort the arrays wrt. enumeration order.
+ iteration_order->SortPairs(enumeration_order, enumeration_order->length());
+
+ // Overwrite the enumeration_order with the enumeration indices.
+ for (int i = 0; i < length; i++) {
+ int index = Smi::cast(iteration_order->get(i))->value();
+ int enum_index = PropertyDetails::kInitialIndex + i;
+ enumeration_order->set(index, Smi::FromInt(enum_index));
+ }
+
+ // Update the dictionary with new indices.
+ capacity = HashTable<Shape, Key>::Capacity();
+ pos = 0;
+ for (int i = 0; i < capacity; i++) {
+ if (Dictionary<Shape, Key>::IsKey(Dictionary<Shape, Key>::KeyAt(i))) {
+ int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
+ PropertyDetails details = DetailsAt(i);
+ PropertyDetails new_details = PropertyDetails(
+ details.attributes(), details.type(), enum_index);
+ DetailsAtPut(i, new_details);
+ }
+ }
+
+ // Set the next enumeration index.
+ SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
+ return this;
+}
+
+template<typename Shape, typename Key>
+MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
+ // Check whether there are enough enumeration indices to add n elements.
+ if (Shape::kIsEnumerable &&
+ !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
+ // If not, we generate new indices for the properties.
+ Object* result;
+ { MaybeObject* maybe_result = GenerateNewEnumerationIndices();
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ }
+ return HashTable<Shape, Key>::EnsureCapacity(n, key);
+}
+
+
+template<typename Shape, typename Key>
+Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
+ JSReceiver::DeleteMode mode) {
+ Heap* heap = Dictionary<Shape, Key>::GetHeap();
+ PropertyDetails details = DetailsAt(entry);
+ // Ignore attributes if forcing a deletion.
+ if (details.IsDontDelete() && mode != JSReceiver::FORCE_DELETION) {
+ return heap->false_value();
+ }
+ SetEntry(entry, heap->the_hole_value(), heap->the_hole_value());
+ HashTable<Shape, Key>::ElementRemoved();
+ return heap->true_value();
+}
+
+
+template<typename Shape, typename Key>
+MaybeObject* Dictionary<Shape, Key>::Shrink(Key key) {
+ return HashTable<Shape, Key>::Shrink(key);
+}
+
+
+template<typename Shape, typename Key>
+MaybeObject* Dictionary<Shape, Key>::AtPut(Key key, Object* value) {
+ int entry = this->FindEntry(key);
+
+ // If the entry is present set the value;
+ if (entry != Dictionary<Shape, Key>::kNotFound) {
+ ValueAtPut(entry, value);
+ return this;
+ }
+
+ // Check whether the dictionary should be extended.
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ Object* k;
+ { MaybeObject* maybe_k = Shape::AsObject(this->GetHeap(), key);
+ if (!maybe_k->ToObject(&k)) return maybe_k;
+ }
+ PropertyDetails details = PropertyDetails(NONE, NORMAL, 0);
+
+ return Dictionary<Shape, Key>::cast(obj)->AddEntry(key, value, details,
+ Dictionary<Shape, Key>::Hash(key));
+}
+
+
+template<typename Shape, typename Key>
+MaybeObject* Dictionary<Shape, Key>::Add(Key key,
+ Object* value,
+ PropertyDetails details) {
+ // Valdate key is absent.
+ SLOW_ASSERT((this->FindEntry(key) == Dictionary<Shape, Key>::kNotFound));
+ // Check whether the dictionary should be extended.
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ return Dictionary<Shape, Key>::cast(obj)->AddEntry(key, value, details,
+ Dictionary<Shape, Key>::Hash(key));
+}
+
+
+// Add a key, value pair to the dictionary.
+template<typename Shape, typename Key>
+MaybeObject* Dictionary<Shape, Key>::AddEntry(Key key,
+ Object* value,
+ PropertyDetails details,
+ uint32_t hash) {
+ // Compute the key object.
+ Object* k;
+ { MaybeObject* maybe_k = Shape::AsObject(this->GetHeap(), key);
+ if (!maybe_k->ToObject(&k)) return maybe_k;
+ }
+
+ uint32_t entry = Dictionary<Shape, Key>::FindInsertionEntry(hash);
+ // Insert element at empty or deleted entry
+ if (!details.IsDeleted() &&
+ details.dictionary_index() == 0 &&
+ Shape::kIsEnumerable) {
+ // Assign an enumeration index to the property and update
+ // SetNextEnumerationIndex.
+ int index = NextEnumerationIndex();
+ details = PropertyDetails(details.attributes(), details.type(), index);
+ SetNextEnumerationIndex(index + 1);
+ }
+ SetEntry(entry, k, value, details);
+ ASSERT((Dictionary<Shape, Key>::KeyAt(entry)->IsNumber() ||
+ Dictionary<Shape, Key>::KeyAt(entry)->IsName()));
+ HashTable<Shape, Key>::ElementAdded();
+ return this;
+}
+
+
+void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key) {
+ // If the dictionary requires slow elements an element has already
+ // been added at a high index.
+ if (requires_slow_elements()) return;
+ // Check if this index is high enough that we should require slow
+ // elements.
+ if (key > kRequiresSlowElementsLimit) {
+ set_requires_slow_elements();
+ return;
+ }
+ // Update max key value.
+ Object* max_index_object = get(kMaxNumberKeyIndex);
+ if (!max_index_object->IsSmi() || max_number_key() < key) {
+ FixedArray::set(kMaxNumberKeyIndex,
+ Smi::FromInt(key << kRequiresSlowElementsTagSize));
+ }
+}
+
+
+MaybeObject* SeededNumberDictionary::AddNumberEntry(uint32_t key,
+ Object* value,
+ PropertyDetails details) {
+ UpdateMaxNumberKey(key);
+ SLOW_ASSERT(this->FindEntry(key) == kNotFound);
+ return Add(key, value, details);
+}
+
+
+MaybeObject* UnseededNumberDictionary::AddNumberEntry(uint32_t key,
+ Object* value) {
+ SLOW_ASSERT(this->FindEntry(key) == kNotFound);
+ return Add(key, value, PropertyDetails(NONE, NORMAL, 0));
+}
+
+
+MaybeObject* SeededNumberDictionary::AtNumberPut(uint32_t key, Object* value) {
+ UpdateMaxNumberKey(key);
+ return AtPut(key, value);
+}
+
+
+MaybeObject* UnseededNumberDictionary::AtNumberPut(uint32_t key,
+ Object* value) {
+ return AtPut(key, value);
+}
+
+
+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) {
+ int entry = FindEntry(key);
+ if (entry == kNotFound) return AddNumberEntry(key, value, details);
+ // Preserve enumeration index.
+ details = PropertyDetails(details.attributes(),
+ details.type(),
+ DetailsAt(entry).dictionary_index());
+ MaybeObject* maybe_object_key =
+ SeededNumberDictionaryShape::AsObject(GetHeap(), key);
+ Object* object_key;
+ if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
+ SetEntry(entry, object_key, value, details);
+ return this;
+}
+
+
+MaybeObject* UnseededNumberDictionary::Set(uint32_t key,
+ Object* value) {
+ int entry = FindEntry(key);
+ if (entry == kNotFound) return AddNumberEntry(key, value);
+ MaybeObject* maybe_object_key =
+ UnseededNumberDictionaryShape::AsObject(GetHeap(), key);
+ Object* object_key;
+ if (!maybe_object_key->ToObject(&object_key)) return maybe_object_key;
+ SetEntry(entry, object_key, value);
+ return this;
+}
+
+
+
+template<typename Shape, typename Key>
+int Dictionary<Shape, Key>::NumberOfElementsFilterAttributes(
+ PropertyAttributes filter) {
+ int capacity = HashTable<Shape, Key>::Capacity();
+ int result = 0;
+ for (int i = 0; i < capacity; i++) {
+ Object* k = HashTable<Shape, Key>::KeyAt(i);
+ if (HashTable<Shape, Key>::IsKey(k) &&
+ ((filter & SYMBOLIC) == 0 || !k->IsSymbol())) {
+ PropertyDetails details = DetailsAt(i);
+ if (details.IsDeleted()) continue;
+ PropertyAttributes attr = details.attributes();
+ if ((attr & filter) == 0) result++;
+ }
+ }
+ return result;
+}
+
+
+template<typename Shape, typename Key>
+int Dictionary<Shape, Key>::NumberOfEnumElements() {
+ return NumberOfElementsFilterAttributes(
+ static_cast<PropertyAttributes>(DONT_ENUM));
+}
+
+
+template<typename Shape, typename Key>
+void Dictionary<Shape, Key>::CopyKeysTo(
+ FixedArray* storage,
+ PropertyAttributes filter,
+ typename Dictionary<Shape, Key>::SortMode sort_mode) {
+ ASSERT(storage->length() >= NumberOfEnumElements());
+ int capacity = HashTable<Shape, Key>::Capacity();
+ int index = 0;
+ for (int i = 0; i < capacity; i++) {
+ Object* k = HashTable<Shape, Key>::KeyAt(i);
+ if (HashTable<Shape, Key>::IsKey(k)) {
+ PropertyDetails details = DetailsAt(i);
+ if (details.IsDeleted()) continue;
+ PropertyAttributes attr = details.attributes();
+ if ((attr & filter) == 0) storage->set(index++, k);
+ }
+ }
+ if (sort_mode == Dictionary<Shape, Key>::SORTED) {
+ storage->SortPairs(storage, index);
+ }
+ ASSERT(storage->length() >= index);
+}
+
+
+FixedArray* NameDictionary::CopyEnumKeysTo(FixedArray* storage) {
+ int length = storage->length();
+ ASSERT(length >= NumberOfEnumElements());
+ Heap* heap = GetHeap();
+ Object* undefined_value = heap->undefined_value();
+ int capacity = Capacity();
+ int properties = 0;
+
+ // Fill in the enumeration array by assigning enumerable keys at their
+ // enumeration index. This will leave holes in the array if there are keys
+ // that are deleted or not enumerable.
+ for (int i = 0; i < capacity; i++) {
+ Object* k = KeyAt(i);
+ if (IsKey(k) && !k->IsSymbol()) {
+ PropertyDetails details = DetailsAt(i);
+ if (details.IsDeleted() || details.IsDontEnum()) continue;
+ properties++;
+ storage->set(details.dictionary_index() - 1, k);
+ if (properties == length) break;
+ }
+ }
+
+ // There are holes in the enumeration array if less properties were assigned
+ // than the length of the array. If so, crunch all the existing properties
+ // together by shifting them to the left (maintaining the enumeration order),
+ // and trimming of the right side of the array.
+ if (properties < length) {
+ if (properties == 0) return heap->empty_fixed_array();
+ properties = 0;
+ for (int i = 0; i < length; ++i) {
+ Object* value = storage->get(i);
+ if (value != undefined_value) {
+ storage->set(properties, value);
+ ++properties;
+ }
+ }
+ RightTrimFixedArray<FROM_MUTATOR>(heap, storage, length - properties);
+ }
+ return storage;
+}
+
+
+template<typename Shape, typename Key>
+void Dictionary<Shape, Key>::CopyKeysTo(
+ FixedArray* storage,
+ int index,
+ PropertyAttributes filter,
+ typename Dictionary<Shape, Key>::SortMode sort_mode) {
+ ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
+ static_cast<PropertyAttributes>(NONE)));
+ int capacity = HashTable<Shape, Key>::Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Object* k = HashTable<Shape, Key>::KeyAt(i);
+ if (HashTable<Shape, Key>::IsKey(k)) {
+ PropertyDetails details = DetailsAt(i);
+ if (details.IsDeleted()) continue;
+ PropertyAttributes attr = details.attributes();
+ if ((attr & filter) == 0) storage->set(index++, k);
+ }
+ }
+ if (sort_mode == Dictionary<Shape, Key>::SORTED) {
+ storage->SortPairs(storage, index);
+ }
+ ASSERT(storage->length() >= index);
+}
+
+
+// Backwards lookup (slow).
+template<typename Shape, typename Key>
+Object* Dictionary<Shape, Key>::SlowReverseLookup(Object* value) {
+ int capacity = HashTable<Shape, Key>::Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Object* k = HashTable<Shape, Key>::KeyAt(i);
+ if (Dictionary<Shape, Key>::IsKey(k)) {
+ Object* e = ValueAt(i);
+ if (e->IsPropertyCell()) {
+ e = PropertyCell::cast(e)->value();
+ }
+ if (e == value) return k;
+ }
+ }
+ Heap* heap = Dictionary<Shape, Key>::GetHeap();
+ return heap->undefined_value();
+}
+
+
+MaybeObject* NameDictionary::TransformPropertiesToFastFor(
+ JSObject* obj, int unused_property_fields) {
+ // Make sure we preserve dictionary representation if there are too many
+ // descriptors.
+ int number_of_elements = NumberOfElements();
+ if (number_of_elements > DescriptorArray::kMaxNumberOfDescriptors) return obj;
+
+ if (number_of_elements != NextEnumerationIndex()) {
+ MaybeObject* maybe_result = GenerateNewEnumerationIndices();
+ if (maybe_result->IsFailure()) return maybe_result;
+ }
+
+ int instance_descriptor_length = 0;
+ int number_of_fields = 0;
+
+ Heap* heap = GetHeap();
+
+ // Compute the length of the instance descriptor.
+ int capacity = Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Object* k = KeyAt(i);
+ if (IsKey(k)) {
+ Object* value = ValueAt(i);
+ PropertyType type = DetailsAt(i).type();
+ ASSERT(type != FIELD);
+ instance_descriptor_length++;
+ if (type == NORMAL && !value->IsJSFunction()) {
+ number_of_fields += 1;
+ }
+ }
+ }
+
+ int inobject_props = obj->map()->inobject_properties();
+
+ // Allocate new map.
+ Map* new_map;
+ MaybeObject* maybe_new_map = obj->map()->CopyDropDescriptors();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ new_map->set_dictionary_map(false);
+
+ if (instance_descriptor_length == 0) {
+ ASSERT_LE(unused_property_fields, inobject_props);
+ // Transform the object.
+ new_map->set_unused_property_fields(inobject_props);
+ obj->set_map(new_map);
+ obj->set_properties(heap->empty_fixed_array());
+ // Check that it really works.
+ ASSERT(obj->HasFastProperties());
+ return obj;
+ }
+
+ // Allocate the instance descriptor.
+ DescriptorArray* descriptors;
+ MaybeObject* maybe_descriptors =
+ DescriptorArray::Allocate(instance_descriptor_length);
+ if (!maybe_descriptors->To(&descriptors)) {
+ return maybe_descriptors;
+ }
+
+ DescriptorArray::WhitenessWitness witness(descriptors);
+
+ int number_of_allocated_fields =
+ number_of_fields + unused_property_fields - inobject_props;
+ if (number_of_allocated_fields < 0) {
+ // There is enough inobject space for all fields (including unused).
+ number_of_allocated_fields = 0;
+ unused_property_fields = inobject_props - number_of_fields;
+ }
+
+ // Allocate the fixed array for the fields.
+ FixedArray* fields;
+ MaybeObject* maybe_fields =
+ heap->AllocateFixedArray(number_of_allocated_fields);
+ if (!maybe_fields->To(&fields)) return maybe_fields;
+
+ // Fill in the instance descriptor and the fields.
+ int current_offset = 0;
+ for (int i = 0; i < capacity; i++) {
+ Object* k = KeyAt(i);
+ if (IsKey(k)) {
+ Object* value = ValueAt(i);
+ Name* key;
+ if (k->IsSymbol()) {
+ key = Symbol::cast(k);
+ } else {
+ // Ensure the key is a unique name before writing into the
+ // instance descriptor.
+ MaybeObject* maybe_key = heap->InternalizeString(String::cast(k));
+ if (!maybe_key->To(&key)) return maybe_key;
+ }
+
+ PropertyDetails details = DetailsAt(i);
+ int enumeration_index = details.dictionary_index();
+ PropertyType type = details.type();
+
+ if (value->IsJSFunction()) {
+ ConstantDescriptor d(key, value, details.attributes());
+ descriptors->Set(enumeration_index - 1, &d, witness);
+ } else if (type == NORMAL) {
+ if (current_offset < inobject_props) {
+ obj->InObjectPropertyAtPut(current_offset,
+ value,
+ UPDATE_WRITE_BARRIER);
+ } else {
+ int offset = current_offset - inobject_props;
+ fields->set(offset, value);
+ }
+ FieldDescriptor d(key,
+ current_offset++,
+ details.attributes(),
+ // TODO(verwaest): value->OptimalRepresentation();
+ Representation::Tagged());
+ descriptors->Set(enumeration_index - 1, &d, witness);
+ } else if (type == CALLBACKS) {
+ CallbacksDescriptor d(key,
+ value,
+ details.attributes());
+ descriptors->Set(enumeration_index - 1, &d, witness);
+ } else {
+ UNREACHABLE();
+ }
+ }
+ }
+ ASSERT(current_offset == number_of_fields);
+
+ descriptors->Sort();
+
+ new_map->InitializeDescriptors(descriptors);
+ new_map->set_unused_property_fields(unused_property_fields);
+
+ // Transform the object.
+ obj->set_map(new_map);
+
+ obj->set_properties(fields);
+ ASSERT(obj->IsJSObject());
+
+ // Check that it really works.
+ ASSERT(obj->HasFastProperties());
+
+ return obj;
+}
+
+
+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->GetHash(OMIT_CREATION);
+ if (maybe_hash->ToObjectUnchecked()->IsUndefined()) {
+ return GetHeap()->the_hole_value();
+ }
+ }
+ int entry = FindEntry(key);
+ if (entry == kNotFound) return GetHeap()->the_hole_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
+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->GetHash(ALLOW_CREATION);
+ if (maybe_hash->IsFailure()) return maybe_hash;
+ hash = Smi::cast(maybe_hash->ToObjectUnchecked())->value();
+ }
+ int entry = FindEntry(key);
+
+ // Check whether to perform removal operation.
+ if (value->IsTheHole()) {
+ if (entry == kNotFound) return this;
+ RemoveEntry(entry);
+ return Shrink(key);
+ }
+
+ // Key is already in table, just overwrite value.
+ if (entry != kNotFound) {
+ set(EntryToIndex(entry) + 1, value);
+ return this;
+ }
+
+ // Check whether the hash table should be extended.
+ Object* obj;
+ { MaybeObject* maybe_obj = EnsureCapacity(1, key);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ ObjectHashTable* table = ObjectHashTable::cast(obj);
+ table->AddEntry(table->FindInsertionEntry(hash), key, value);
+ return table;
+}
+
+
+void ObjectHashTable::AddEntry(int entry, Object* key, Object* value) {
+ set(EntryToIndex(entry), key);
+ set(EntryToIndex(entry) + 1, value);
+ ElementAdded();
+}
+
+
+void ObjectHashTable::RemoveEntry(int entry) {
+ set_the_hole(EntryToIndex(entry));
+ set_the_hole(EntryToIndex(entry) + 1);
+ ElementRemoved();
+}
+
+
+DeclaredAccessorDescriptorIterator::DeclaredAccessorDescriptorIterator(
+ DeclaredAccessorDescriptor* descriptor)
+ : array_(descriptor->serialized_data()->GetDataStartAddress()),
+ length_(descriptor->serialized_data()->length()),
+ offset_(0) {
+}
+
+
+const DeclaredAccessorDescriptorData*
+ DeclaredAccessorDescriptorIterator::Next() {
+ ASSERT(offset_ < length_);
+ uint8_t* ptr = &array_[offset_];
+ ASSERT(reinterpret_cast<uintptr_t>(ptr) % sizeof(uintptr_t) == 0);
+ const DeclaredAccessorDescriptorData* data =
+ reinterpret_cast<const DeclaredAccessorDescriptorData*>(ptr);
+ offset_ += sizeof(*data);
+ ASSERT(offset_ <= length_);
+ return data;
+}
+
+
+Handle<DeclaredAccessorDescriptor> DeclaredAccessorDescriptor::Create(
+ Isolate* isolate,
+ const DeclaredAccessorDescriptorData& descriptor,
+ Handle<DeclaredAccessorDescriptor> previous) {
+ int previous_length =
+ previous.is_null() ? 0 : previous->serialized_data()->length();
+ int length = sizeof(descriptor) + previous_length;
+ Handle<ByteArray> serialized_descriptor =
+ isolate->factory()->NewByteArray(length);
+ Handle<DeclaredAccessorDescriptor> value =
+ isolate->factory()->NewDeclaredAccessorDescriptor();
+ value->set_serialized_data(*serialized_descriptor);
+ // Copy in the data.
+ {
+ DisallowHeapAllocation no_allocation;
+ uint8_t* array = serialized_descriptor->GetDataStartAddress();
+ if (previous_length != 0) {
+ uint8_t* previous_array =
+ previous->serialized_data()->GetDataStartAddress();
+ OS::MemCopy(array, previous_array, previous_length);
+ array += previous_length;
+ }
+ ASSERT(reinterpret_cast<uintptr_t>(array) % sizeof(uintptr_t) == 0);
+ DeclaredAccessorDescriptorData* data =
+ reinterpret_cast<DeclaredAccessorDescriptorData*>(array);
+ *data = descriptor;
+ }
+ return value;
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+// Check if there is a break point at this code position.
+bool DebugInfo::HasBreakPoint(int code_position) {
+ // Get the break point info object for this code position.
+ Object* break_point_info = GetBreakPointInfo(code_position);
+
+ // If there is no break point info object or no break points in the break
+ // point info object there is no break point at this code position.
+ if (break_point_info->IsUndefined()) return false;
+ return BreakPointInfo::cast(break_point_info)->GetBreakPointCount() > 0;
+}
+
+
+// Get the break point info object for this code position.
+Object* DebugInfo::GetBreakPointInfo(int code_position) {
+ // Find the index of the break point info object for this code position.
+ int index = GetBreakPointInfoIndex(code_position);
+
+ // Return the break point info object if any.
+ if (index == kNoBreakPointInfo) return GetHeap()->undefined_value();
+ return BreakPointInfo::cast(break_points()->get(index));
+}
+
+
+// Clear a break point at the specified code position.
+void DebugInfo::ClearBreakPoint(Handle<DebugInfo> debug_info,
+ int code_position,
+ Handle<Object> break_point_object) {
+ Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position),
+ Isolate::Current());
+ if (break_point_info->IsUndefined()) return;
+ BreakPointInfo::ClearBreakPoint(
+ Handle<BreakPointInfo>::cast(break_point_info),
+ break_point_object);
+}
+
+
+void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
+ int code_position,
+ int source_position,
+ int statement_position,
+ Handle<Object> break_point_object) {
+ Isolate* isolate = Isolate::Current();
+ Handle<Object> break_point_info(debug_info->GetBreakPointInfo(code_position),
+ isolate);
+ if (!break_point_info->IsUndefined()) {
+ BreakPointInfo::SetBreakPoint(
+ Handle<BreakPointInfo>::cast(break_point_info),
+ break_point_object);
+ return;
+ }
+
+ // Adding a new break point for a code position which did not have any
+ // break points before. Try to find a free slot.
+ int index = kNoBreakPointInfo;
+ for (int i = 0; i < debug_info->break_points()->length(); i++) {
+ if (debug_info->break_points()->get(i)->IsUndefined()) {
+ index = i;
+ break;
+ }
+ }
+ if (index == kNoBreakPointInfo) {
+ // No free slot - extend break point info array.
+ Handle<FixedArray> old_break_points =
+ Handle<FixedArray>(FixedArray::cast(debug_info->break_points()));
+ Handle<FixedArray> new_break_points =
+ isolate->factory()->NewFixedArray(
+ old_break_points->length() +
+ Debug::kEstimatedNofBreakPointsInFunction);
+
+ debug_info->set_break_points(*new_break_points);
+ for (int i = 0; i < old_break_points->length(); i++) {
+ new_break_points->set(i, old_break_points->get(i));
+ }
+ index = old_break_points->length();
+ }
+ ASSERT(index != kNoBreakPointInfo);
+
+ // Allocate new BreakPointInfo object and set the break point.
+ Handle<BreakPointInfo> new_break_point_info = Handle<BreakPointInfo>::cast(
+ isolate->factory()->NewStruct(BREAK_POINT_INFO_TYPE));
+ new_break_point_info->set_code_position(Smi::FromInt(code_position));
+ new_break_point_info->set_source_position(Smi::FromInt(source_position));
+ new_break_point_info->
+ set_statement_position(Smi::FromInt(statement_position));
+ new_break_point_info->set_break_point_objects(
+ isolate->heap()->undefined_value());
+ BreakPointInfo::SetBreakPoint(new_break_point_info, break_point_object);
+ debug_info->break_points()->set(index, *new_break_point_info);
+}
+
+
+// Get the break point objects for a code position.
+Object* DebugInfo::GetBreakPointObjects(int code_position) {
+ Object* break_point_info = GetBreakPointInfo(code_position);
+ if (break_point_info->IsUndefined()) {
+ return GetHeap()->undefined_value();
+ }
+ return BreakPointInfo::cast(break_point_info)->break_point_objects();
+}
+
+
+// Get the total number of break points.
+int DebugInfo::GetBreakPointCount() {
+ if (break_points()->IsUndefined()) return 0;
+ int count = 0;
+ for (int i = 0; i < break_points()->length(); i++) {
+ if (!break_points()->get(i)->IsUndefined()) {
+ BreakPointInfo* break_point_info =
+ BreakPointInfo::cast(break_points()->get(i));
+ count += break_point_info->GetBreakPointCount();
+ }
+ }
+ return count;
+}
+
+
+Object* DebugInfo::FindBreakPointInfo(Handle<DebugInfo> debug_info,
+ Handle<Object> break_point_object) {
+ Heap* heap = debug_info->GetHeap();
+ if (debug_info->break_points()->IsUndefined()) return heap->undefined_value();
+ for (int i = 0; i < debug_info->break_points()->length(); i++) {
+ if (!debug_info->break_points()->get(i)->IsUndefined()) {
+ Handle<BreakPointInfo> break_point_info =
+ Handle<BreakPointInfo>(BreakPointInfo::cast(
+ debug_info->break_points()->get(i)));
+ if (BreakPointInfo::HasBreakPointObject(break_point_info,
+ break_point_object)) {
+ return *break_point_info;
+ }
+ }
+ }
+ return heap->undefined_value();
+}
+
+
+// Find the index of the break point info object for the specified code
+// position.
+int DebugInfo::GetBreakPointInfoIndex(int code_position) {
+ if (break_points()->IsUndefined()) return kNoBreakPointInfo;
+ for (int i = 0; i < break_points()->length(); i++) {
+ if (!break_points()->get(i)->IsUndefined()) {
+ BreakPointInfo* break_point_info =
+ BreakPointInfo::cast(break_points()->get(i));
+ if (break_point_info->code_position()->value() == code_position) {
+ return i;
+ }
+ }
+ }
+ return kNoBreakPointInfo;
+}
+
+
+// Remove the specified break point object.
+void BreakPointInfo::ClearBreakPoint(Handle<BreakPointInfo> break_point_info,
+ Handle<Object> break_point_object) {
+ Isolate* isolate = Isolate::Current();
+ // If there are no break points just ignore.
+ if (break_point_info->break_point_objects()->IsUndefined()) return;
+ // If there is a single break point clear it if it is the same.
+ if (!break_point_info->break_point_objects()->IsFixedArray()) {
+ if (break_point_info->break_point_objects() == *break_point_object) {
+ break_point_info->set_break_point_objects(
+ isolate->heap()->undefined_value());
+ }
+ return;
+ }
+ // If there are multiple break points shrink the array
+ ASSERT(break_point_info->break_point_objects()->IsFixedArray());
+ Handle<FixedArray> old_array =
+ Handle<FixedArray>(
+ FixedArray::cast(break_point_info->break_point_objects()));
+ Handle<FixedArray> new_array =
+ isolate->factory()->NewFixedArray(old_array->length() - 1);
+ int found_count = 0;
+ for (int i = 0; i < old_array->length(); i++) {
+ if (old_array->get(i) == *break_point_object) {
+ ASSERT(found_count == 0);
+ found_count++;
+ } else {
+ new_array->set(i - found_count, old_array->get(i));
+ }
+ }
+ // If the break point was found in the list change it.
+ if (found_count > 0) break_point_info->set_break_point_objects(*new_array);
+}
+
+
+// Add the specified break point object.
+void BreakPointInfo::SetBreakPoint(Handle<BreakPointInfo> break_point_info,
+ Handle<Object> break_point_object) {
+ Isolate* isolate = break_point_info->GetIsolate();
+
+ // If there was no break point objects before just set it.
+ if (break_point_info->break_point_objects()->IsUndefined()) {
+ break_point_info->set_break_point_objects(*break_point_object);
+ return;
+ }
+ // If the break point object is the same as before just ignore.
+ if (break_point_info->break_point_objects() == *break_point_object) return;
+ // If there was one break point object before replace with array.
+ if (!break_point_info->break_point_objects()->IsFixedArray()) {
+ Handle<FixedArray> array = isolate->factory()->NewFixedArray(2);
+ array->set(0, break_point_info->break_point_objects());
+ array->set(1, *break_point_object);
+ break_point_info->set_break_point_objects(*array);
+ return;
+ }
+ // If there was more than one break point before extend array.
+ Handle<FixedArray> old_array =
+ Handle<FixedArray>(
+ FixedArray::cast(break_point_info->break_point_objects()));
+ Handle<FixedArray> new_array =
+ isolate->factory()->NewFixedArray(old_array->length() + 1);
+ for (int i = 0; i < old_array->length(); i++) {
+ // If the break point was there before just ignore.
+ if (old_array->get(i) == *break_point_object) return;
+ new_array->set(i, old_array->get(i));
+ }
+ // Add the new break point.
+ new_array->set(old_array->length(), *break_point_object);
+ break_point_info->set_break_point_objects(*new_array);
+}
+
+
+bool BreakPointInfo::HasBreakPointObject(
+ Handle<BreakPointInfo> break_point_info,
+ Handle<Object> break_point_object) {
+ // No break point.
+ if (break_point_info->break_point_objects()->IsUndefined()) return false;
+ // Single break point.
+ if (!break_point_info->break_point_objects()->IsFixedArray()) {
+ return break_point_info->break_point_objects() == *break_point_object;
+ }
+ // Multiple break points.
+ FixedArray* array = FixedArray::cast(break_point_info->break_point_objects());
+ for (int i = 0; i < array->length(); i++) {
+ if (array->get(i) == *break_point_object) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Get the number of break points.
+int BreakPointInfo::GetBreakPointCount() {
+ // No break point.
+ if (break_point_objects()->IsUndefined()) return 0;
+ // Single break point.
+ if (!break_point_objects()->IsFixedArray()) return 1;
+ // Multiple break points.
+ return FixedArray::cast(break_point_objects())->length();
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+Object* JSDate::GetField(Object* object, Smi* index) {
+ return JSDate::cast(object)->DoGetField(
+ static_cast<FieldIndex>(index->value()));
+}
+
+
+Object* JSDate::DoGetField(FieldIndex index) {
+ ASSERT(index != kDateValue);
+
+ DateCache* date_cache = GetIsolate()->date_cache();
+
+ if (index < kFirstUncachedField) {
+ Object* stamp = cache_stamp();
+ if (stamp != date_cache->stamp() && stamp->IsSmi()) {
+ // Since the stamp is not NaN, the value is also not NaN.
+ int64_t local_time_ms =
+ date_cache->ToLocal(static_cast<int64_t>(value()->Number()));
+ SetLocalFields(local_time_ms, date_cache);
+ }
+ switch (index) {
+ case kYear: return year();
+ case kMonth: return month();
+ case kDay: return day();
+ case kWeekday: return weekday();
+ case kHour: return hour();
+ case kMinute: return min();
+ case kSecond: return sec();
+ default: UNREACHABLE();
+ }
+ }
+
+ if (index >= kFirstUTCField) {
+ return GetUTCField(index, value()->Number(), date_cache);
+ }
+
+ double time = value()->Number();
+ if (std::isnan(time)) return GetIsolate()->heap()->nan_value();
+
+ int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(time));
+ int days = DateCache::DaysFromTime(local_time_ms);
+
+ if (index == kDays) return Smi::FromInt(days);
+
+ int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
+ if (index == kMillisecond) return Smi::FromInt(time_in_day_ms % 1000);
+ ASSERT(index == kTimeInDay);
+ return Smi::FromInt(time_in_day_ms);
+}
+
+
+Object* JSDate::GetUTCField(FieldIndex index,
+ double value,
+ DateCache* date_cache) {
+ ASSERT(index >= kFirstUTCField);
+
+ if (std::isnan(value)) return GetIsolate()->heap()->nan_value();
+
+ int64_t time_ms = static_cast<int64_t>(value);
+
+ if (index == kTimezoneOffset) {
+ return Smi::FromInt(date_cache->TimezoneOffset(time_ms));
+ }
+
+ int days = DateCache::DaysFromTime(time_ms);
+
+ if (index == kWeekdayUTC) return Smi::FromInt(date_cache->Weekday(days));
+
+ if (index <= kDayUTC) {
+ int year, month, day;
+ date_cache->YearMonthDayFromDays(days, &year, &month, &day);
+ if (index == kYearUTC) return Smi::FromInt(year);
+ if (index == kMonthUTC) return Smi::FromInt(month);
+ ASSERT(index == kDayUTC);
+ return Smi::FromInt(day);
+ }
+
+ int time_in_day_ms = DateCache::TimeInDay(time_ms, days);
+ switch (index) {
+ case kHourUTC: return Smi::FromInt(time_in_day_ms / (60 * 60 * 1000));
+ case kMinuteUTC: return Smi::FromInt((time_in_day_ms / (60 * 1000)) % 60);
+ case kSecondUTC: return Smi::FromInt((time_in_day_ms / 1000) % 60);
+ case kMillisecondUTC: return Smi::FromInt(time_in_day_ms % 1000);
+ case kDaysUTC: return Smi::FromInt(days);
+ case kTimeInDayUTC: return Smi::FromInt(time_in_day_ms);
+ default: UNREACHABLE();
+ }
+
+ UNREACHABLE();
+ return NULL;
+}
+
+
+void JSDate::SetValue(Object* value, bool is_value_nan) {
+ set_value(value);
+ if (is_value_nan) {
+ HeapNumber* nan = GetIsolate()->heap()->nan_value();
+ set_cache_stamp(nan, SKIP_WRITE_BARRIER);
+ set_year(nan, SKIP_WRITE_BARRIER);
+ set_month(nan, SKIP_WRITE_BARRIER);
+ set_day(nan, SKIP_WRITE_BARRIER);
+ set_hour(nan, SKIP_WRITE_BARRIER);
+ set_min(nan, SKIP_WRITE_BARRIER);
+ set_sec(nan, SKIP_WRITE_BARRIER);
+ set_weekday(nan, SKIP_WRITE_BARRIER);
+ } else {
+ set_cache_stamp(Smi::FromInt(DateCache::kInvalidStamp), SKIP_WRITE_BARRIER);
+ }
+}
+
+
+void JSDate::SetLocalFields(int64_t local_time_ms, DateCache* date_cache) {
+ int days = DateCache::DaysFromTime(local_time_ms);
+ int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
+ int year, month, day;
+ date_cache->YearMonthDayFromDays(days, &year, &month, &day);
+ int weekday = date_cache->Weekday(days);
+ int hour = time_in_day_ms / (60 * 60 * 1000);
+ int min = (time_in_day_ms / (60 * 1000)) % 60;
+ int sec = (time_in_day_ms / 1000) % 60;
+ set_cache_stamp(date_cache->stamp());
+ set_year(Smi::FromInt(year), SKIP_WRITE_BARRIER);
+ set_month(Smi::FromInt(month), SKIP_WRITE_BARRIER);
+ set_day(Smi::FromInt(day), SKIP_WRITE_BARRIER);
+ set_weekday(Smi::FromInt(weekday), SKIP_WRITE_BARRIER);
+ set_hour(Smi::FromInt(hour), SKIP_WRITE_BARRIER);
+ set_min(Smi::FromInt(min), SKIP_WRITE_BARRIER);
+ set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER);
+}
+
+
+void JSArrayBuffer::Neuter() {
+ ASSERT(is_external());
+ set_backing_store(NULL);
+ set_byte_length(Smi::FromInt(0));
+}
+
+
+void JSArrayBufferView::NeuterView() {
+ set_byte_offset(Smi::FromInt(0));
+ set_byte_length(Smi::FromInt(0));
+}
+
+
+void JSDataView::Neuter() {
+ NeuterView();
+}
+
+
+void JSTypedArray::Neuter() {
+ NeuterView();
+ set_length(Smi::FromInt(0));
+ set_elements(GetHeap()->EmptyExternalArrayForMap(map()));
+}
+
+
+Type* PropertyCell::type() {
+ return static_cast<Type*>(type_raw());
+}
+
+
+void PropertyCell::set_type(Type* type, WriteBarrierMode ignored) {
+ ASSERT(IsPropertyCell());
+ set_type_raw(type, ignored);
+}
+
+
+Type* PropertyCell::UpdateType(Handle<PropertyCell> cell,
+ Handle<Object> value) {
+ Isolate* isolate = cell->GetIsolate();
+ Handle<Type> old_type(cell->type(), isolate);
+ // TODO(2803): Do not track ConsString as constant because they cannot be
+ // embedded into code.
+ Handle<Type> new_type(value->IsConsString() || value->IsTheHole()
+ ? Type::Any()
+ : Type::Constant(value, isolate), isolate);
+
+ if (new_type->Is(old_type)) {
+ return *old_type;
+ }
+
+ cell->dependent_code()->DeoptimizeDependentCodeGroup(
+ isolate, DependentCode::kPropertyCellChangedGroup);
+
+ if (old_type->Is(Type::None()) || old_type->Is(Type::Undefined())) {
+ return *new_type;
+ }
+
+ return Type::Any();
+}
+
+
+MaybeObject* PropertyCell::SetValueInferType(Object* value,
+ WriteBarrierMode ignored) {
+ set_value(value, ignored);
+ if (!Type::Any()->Is(type())) {
+ IdempotentPointerToHandleCodeTrampoline trampoline(GetIsolate());
+ MaybeObject* maybe_type = trampoline.CallWithReturnValue(
+ &PropertyCell::UpdateType,
+ Handle<PropertyCell>(this),
+ Handle<Object>(value, GetIsolate()));
+ Type* new_type = NULL;
+ if (!maybe_type->To(&new_type)) return maybe_type;
+ set_type(new_type);
+ }
+ return value;
+}
+
+
+void PropertyCell::AddDependentCompilationInfo(CompilationInfo* info) {
+ Handle<DependentCode> dep(dependent_code());
+ Handle<DependentCode> codes =
+ DependentCode::Insert(dep, DependentCode::kPropertyCellChangedGroup,
+ info->object_wrapper());
+ if (*codes != dependent_code()) set_dependent_code(*codes);
+ info->dependencies(DependentCode::kPropertyCellChangedGroup)->Add(
+ Handle<HeapObject>(this), info->zone());
+}
+
+
+void PropertyCell::AddDependentCode(Handle<Code> code) {
+ Handle<DependentCode> codes = DependentCode::Insert(
+ Handle<DependentCode>(dependent_code()),
+ DependentCode::kPropertyCellChangedGroup, code);
+ if (*codes != dependent_code()) set_dependent_code(*codes);
+}
+
+
+const char* GetBailoutReason(BailoutReason reason) {
+ ASSERT(reason < kLastErrorMessage);
+#define ERROR_MESSAGES_TEXTS(C, T) T,
+ static const char* error_messages_[] = {
+ ERROR_MESSAGES_LIST(ERROR_MESSAGES_TEXTS)
+ };
+#undef ERROR_MESSAGES_TEXTS
+ return error_messages_[reason];
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/objects.h b/chromium/v8/src/objects.h
new file mode 100644
index 00000000000..f800c5d580e
--- /dev/null
+++ b/chromium/v8/src/objects.h
@@ -0,0 +1,10180 @@
+// Copyright 2012 the V8 project authors. 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_H_
+#define V8_OBJECTS_H_
+
+#include "allocation.h"
+#include "assert-scope.h"
+#include "builtins.h"
+#include "elements-kind.h"
+#include "flags.h"
+#include "list.h"
+#include "property-details.h"
+#include "smart-pointers.h"
+#include "unicode-inl.h"
+#if V8_TARGET_ARCH_ARM
+#include "arm/constants-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/constants-mips.h"
+#endif
+#include "v8checks.h"
+#include "zone.h"
+
+
+//
+// Most object types in the V8 JavaScript are described in this file.
+//
+// Inheritance hierarchy:
+// - MaybeObject (an object or a failure)
+// - Failure (immediate for marking failed operation)
+// - Object
+// - Smi (immediate small integer)
+// - HeapObject (superclass for everything allocated in the heap)
+// - JSReceiver (suitable for property access)
+// - JSObject
+// - JSArray
+// - JSArrayBuffer
+// - JSArrayBufferView
+// - JSTypedArray
+// - JSDataView
+// - JSSet
+// - JSMap
+// - JSWeakCollection
+// - JSWeakMap
+// - JSWeakSet
+// - JSRegExp
+// - JSFunction
+// - JSGeneratorObject
+// - JSModule
+// - GlobalObject
+// - JSGlobalObject
+// - JSBuiltinsObject
+// - JSGlobalProxy
+// - JSValue
+// - JSDate
+// - JSMessageObject
+// - JSProxy
+// - JSFunctionProxy
+// - FixedArrayBase
+// - ByteArray
+// - FixedArray
+// - DescriptorArray
+// - HashTable
+// - Dictionary
+// - StringTable
+// - CompilationCacheTable
+// - CodeCacheHashTable
+// - MapCache
+// - Context
+// - JSFunctionResultCache
+// - ScopeInfo
+// - TransitionArray
+// - FixedDoubleArray
+// - ExternalArray
+// - ExternalPixelArray
+// - ExternalByteArray
+// - ExternalUnsignedByteArray
+// - ExternalShortArray
+// - ExternalUnsignedShortArray
+// - ExternalIntArray
+// - ExternalUnsignedIntArray
+// - ExternalFloatArray
+// - Name
+// - String
+// - SeqString
+// - SeqOneByteString
+// - SeqTwoByteString
+// - SlicedString
+// - ConsString
+// - ExternalString
+// - ExternalAsciiString
+// - ExternalTwoByteString
+// - InternalizedString
+// - SeqInternalizedString
+// - SeqOneByteInternalizedString
+// - SeqTwoByteInternalizedString
+// - ConsInternalizedString
+// - ExternalInternalizedString
+// - ExternalAsciiInternalizedString
+// - ExternalTwoByteInternalizedString
+// - Symbol
+// - HeapNumber
+// - Cell
+// - PropertyCell
+// - Code
+// - Map
+// - Oddball
+// - Foreign
+// - SharedFunctionInfo
+// - Struct
+// - Box
+// - DeclaredAccessorDescriptor
+// - AccessorInfo
+// - DeclaredAccessorInfo
+// - ExecutableAccessorInfo
+// - AccessorPair
+// - AccessCheckInfo
+// - InterceptorInfo
+// - CallHandlerInfo
+// - TemplateInfo
+// - FunctionTemplateInfo
+// - ObjectTemplateInfo
+// - Script
+// - SignatureInfo
+// - TypeSwitchInfo
+// - DebugInfo
+// - BreakPointInfo
+// - CodeCache
+//
+// Formats of Object*:
+// Smi: [31 bit signed int] 0
+// HeapObject: [32 bit direct pointer] (4 byte aligned) | 01
+// Failure: [30 bit signed int] 11
+
+namespace v8 {
+namespace internal {
+
+enum KeyedAccessStoreMode {
+ STANDARD_STORE,
+ STORE_TRANSITION_SMI_TO_OBJECT,
+ STORE_TRANSITION_SMI_TO_DOUBLE,
+ STORE_TRANSITION_DOUBLE_TO_OBJECT,
+ STORE_TRANSITION_HOLEY_SMI_TO_OBJECT,
+ STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE,
+ STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT,
+ STORE_AND_GROW_NO_TRANSITION,
+ STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT,
+ STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE,
+ STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT,
+ STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT,
+ STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE,
+ STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT,
+ STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS,
+ STORE_NO_TRANSITION_HANDLE_COW
+};
+
+
+static const int kGrowICDelta = STORE_AND_GROW_NO_TRANSITION -
+ STANDARD_STORE;
+STATIC_ASSERT(STANDARD_STORE == 0);
+STATIC_ASSERT(kGrowICDelta ==
+ STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT -
+ STORE_TRANSITION_SMI_TO_OBJECT);
+STATIC_ASSERT(kGrowICDelta ==
+ STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE -
+ STORE_TRANSITION_SMI_TO_DOUBLE);
+STATIC_ASSERT(kGrowICDelta ==
+ STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT -
+ STORE_TRANSITION_DOUBLE_TO_OBJECT);
+
+
+static inline KeyedAccessStoreMode GetGrowStoreMode(
+ KeyedAccessStoreMode store_mode) {
+ if (store_mode < STORE_AND_GROW_NO_TRANSITION) {
+ store_mode = static_cast<KeyedAccessStoreMode>(
+ static_cast<int>(store_mode) + kGrowICDelta);
+ }
+ return store_mode;
+}
+
+
+static inline bool IsTransitionStoreMode(KeyedAccessStoreMode store_mode) {
+ return store_mode > STANDARD_STORE &&
+ store_mode <= STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT &&
+ store_mode != STORE_AND_GROW_NO_TRANSITION;
+}
+
+
+static inline KeyedAccessStoreMode GetNonTransitioningStoreMode(
+ KeyedAccessStoreMode store_mode) {
+ if (store_mode >= STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
+ return store_mode;
+ }
+ if (store_mode >= STORE_AND_GROW_NO_TRANSITION) {
+ return STORE_AND_GROW_NO_TRANSITION;
+ }
+ return STANDARD_STORE;
+}
+
+
+static inline bool IsGrowStoreMode(KeyedAccessStoreMode store_mode) {
+ return store_mode >= STORE_AND_GROW_NO_TRANSITION &&
+ store_mode <= STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT;
+}
+
+
+// Setter that skips the write barrier if mode is SKIP_WRITE_BARRIER.
+enum WriteBarrierMode { SKIP_WRITE_BARRIER, UPDATE_WRITE_BARRIER };
+
+
+// PropertyNormalizationMode is used to specify whether to keep
+// inobject properties when normalizing properties of a JSObject.
+enum PropertyNormalizationMode {
+ CLEAR_INOBJECT_PROPERTIES,
+ KEEP_INOBJECT_PROPERTIES
+};
+
+
+// NormalizedMapSharingMode is used to specify whether a map may be shared
+// by different objects with normalized properties.
+enum NormalizedMapSharingMode {
+ UNIQUE_NORMALIZED_MAP,
+ SHARED_NORMALIZED_MAP
+};
+
+
+// Indicates whether a get method should implicitly create the object looked up.
+enum CreationFlag {
+ ALLOW_CREATION,
+ OMIT_CREATION
+};
+
+
+// Indicates whether transitions can be added to a source map or not.
+enum TransitionFlag {
+ INSERT_TRANSITION,
+ OMIT_TRANSITION_KEEP_REPRESENTATIONS,
+ OMIT_TRANSITION
+};
+
+
+enum DebugExtraICState {
+ DEBUG_BREAK,
+ DEBUG_PREPARE_STEP_IN
+};
+
+
+// Indicates whether the transition is simple: the target map of the transition
+// either extends the current map with a new property, or it modifies the
+// property that was added last to the current map.
+enum SimpleTransitionFlag {
+ SIMPLE_TRANSITION,
+ FULL_TRANSITION
+};
+
+
+// Indicates whether we are only interested in the descriptors of a particular
+// map, or in all descriptors in the descriptor array.
+enum DescriptorFlag {
+ ALL_DESCRIPTORS,
+ OWN_DESCRIPTORS
+};
+
+// The GC maintains a bit of information, the MarkingParity, which toggles
+// from odd to even and back every time marking is completed. Incremental
+// marking can visit an object twice during a marking phase, so algorithms that
+// that piggy-back on marking can use the parity to ensure that they only
+// perform an operation on an object once per marking phase: they record the
+// MarkingParity when they visit an object, and only re-visit the object when it
+// is marked again and the MarkingParity changes.
+enum MarkingParity {
+ NO_MARKING_PARITY,
+ ODD_MARKING_PARITY,
+ EVEN_MARKING_PARITY
+};
+
+// Instance size sentinel for objects of variable size.
+const int kVariableSizeSentinel = 0;
+
+const int kStubMajorKeyBits = 6;
+const int kStubMinorKeyBits = kBitsPerInt - kSmiTagSize - kStubMajorKeyBits;
+
+// All Maps have a field instance_type containing a InstanceType.
+// It describes the type of the instances.
+//
+// As an example, a JavaScript object is a heap object and its map
+// instance_type is JS_OBJECT_TYPE.
+//
+// The names of the string instance types are intended to systematically
+// mirror their encoding in the instance_type field of the map. The default
+// 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 (e.g. CONS, EXTERNAL) are explicitly
+// mentioned. Finally, the string is either a STRING_TYPE (if it is a normal
+// string) or a INTERNALIZED_STRING_TYPE (if it is a internalized string).
+//
+// NOTE: The following things are some that depend on the string types having
+// instance_types that are less than those of all other types:
+// HeapObject::Size, HeapObject::IterateBody, the typeof operator, and
+// Object::IsString.
+//
+// NOTE: Everything following JS_VALUE_TYPE is considered a
+// JSObject for GC purposes. The first four entries here have typeof
+// 'object', whereas JS_FUNCTION_TYPE has typeof 'function'.
+#define INSTANCE_TYPE_LIST_ALL(V) \
+ V(STRING_TYPE) \
+ V(ASCII_STRING_TYPE) \
+ V(CONS_STRING_TYPE) \
+ V(CONS_ASCII_STRING_TYPE) \
+ V(SLICED_STRING_TYPE) \
+ V(SLICED_ASCII_STRING_TYPE) \
+ V(EXTERNAL_STRING_TYPE) \
+ V(EXTERNAL_ASCII_STRING_TYPE) \
+ V(EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE) \
+ V(SHORT_EXTERNAL_STRING_TYPE) \
+ V(SHORT_EXTERNAL_ASCII_STRING_TYPE) \
+ V(SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE) \
+ \
+ V(INTERNALIZED_STRING_TYPE) \
+ V(ASCII_INTERNALIZED_STRING_TYPE) \
+ V(CONS_INTERNALIZED_STRING_TYPE) \
+ V(CONS_ASCII_INTERNALIZED_STRING_TYPE) \
+ V(EXTERNAL_INTERNALIZED_STRING_TYPE) \
+ V(EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE) \
+ V(EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE) \
+ V(SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE) \
+ V(SHORT_EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE) \
+ V(SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE) \
+ \
+ V(SYMBOL_TYPE) \
+ V(MAP_TYPE) \
+ V(CODE_TYPE) \
+ V(ODDBALL_TYPE) \
+ V(CELL_TYPE) \
+ V(PROPERTY_CELL_TYPE) \
+ \
+ 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(). */ \
+ V(EXTERNAL_BYTE_ARRAY_TYPE) \
+ V(EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE) \
+ V(EXTERNAL_SHORT_ARRAY_TYPE) \
+ V(EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE) \
+ V(EXTERNAL_INT_ARRAY_TYPE) \
+ V(EXTERNAL_UNSIGNED_INT_ARRAY_TYPE) \
+ V(EXTERNAL_FLOAT_ARRAY_TYPE) \
+ V(EXTERNAL_DOUBLE_ARRAY_TYPE) \
+ V(EXTERNAL_PIXEL_ARRAY_TYPE) \
+ V(FILLER_TYPE) \
+ \
+ V(DECLARED_ACCESSOR_DESCRIPTOR_TYPE) \
+ V(DECLARED_ACCESSOR_INFO_TYPE) \
+ V(EXECUTABLE_ACCESSOR_INFO_TYPE) \
+ V(ACCESSOR_PAIR_TYPE) \
+ V(ACCESS_CHECK_INFO_TYPE) \
+ V(INTERCEPTOR_INFO_TYPE) \
+ V(CALL_HANDLER_INFO_TYPE) \
+ V(FUNCTION_TEMPLATE_INFO_TYPE) \
+ V(OBJECT_TEMPLATE_INFO_TYPE) \
+ V(SIGNATURE_INFO_TYPE) \
+ V(TYPE_SWITCH_INFO_TYPE) \
+ V(ALLOCATION_MEMENTO_TYPE) \
+ V(ALLOCATION_SITE_TYPE) \
+ V(SCRIPT_TYPE) \
+ V(CODE_CACHE_TYPE) \
+ V(POLYMORPHIC_CODE_CACHE_TYPE) \
+ V(TYPE_FEEDBACK_INFO_TYPE) \
+ V(ALIASED_ARGUMENTS_ENTRY_TYPE) \
+ V(BOX_TYPE) \
+ \
+ V(FIXED_ARRAY_TYPE) \
+ V(FIXED_DOUBLE_ARRAY_TYPE) \
+ V(SHARED_FUNCTION_INFO_TYPE) \
+ \
+ V(JS_MESSAGE_OBJECT_TYPE) \
+ \
+ V(JS_VALUE_TYPE) \
+ V(JS_DATE_TYPE) \
+ V(JS_OBJECT_TYPE) \
+ V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \
+ V(JS_GENERATOR_OBJECT_TYPE) \
+ V(JS_MODULE_TYPE) \
+ V(JS_GLOBAL_OBJECT_TYPE) \
+ V(JS_BUILTINS_OBJECT_TYPE) \
+ V(JS_GLOBAL_PROXY_TYPE) \
+ V(JS_ARRAY_TYPE) \
+ V(JS_ARRAY_BUFFER_TYPE) \
+ V(JS_TYPED_ARRAY_TYPE) \
+ V(JS_DATA_VIEW_TYPE) \
+ V(JS_PROXY_TYPE) \
+ V(JS_SET_TYPE) \
+ V(JS_MAP_TYPE) \
+ V(JS_WEAK_MAP_TYPE) \
+ V(JS_WEAK_SET_TYPE) \
+ V(JS_REGEXP_TYPE) \
+ \
+ V(JS_FUNCTION_TYPE) \
+ V(JS_FUNCTION_PROXY_TYPE) \
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#define INSTANCE_TYPE_LIST_DEBUGGER(V) \
+ V(DEBUG_INFO_TYPE) \
+ V(BREAK_POINT_INFO_TYPE)
+#else
+#define INSTANCE_TYPE_LIST_DEBUGGER(V)
+#endif
+
+#define INSTANCE_TYPE_LIST(V) \
+ INSTANCE_TYPE_LIST_ALL(V) \
+ INSTANCE_TYPE_LIST_DEBUGGER(V)
+
+
+// Since string types are not consecutive, this macro is used to
+// iterate over them.
+#define STRING_TYPE_LIST(V) \
+ V(STRING_TYPE, \
+ kVariableSizeSentinel, \
+ string, \
+ String) \
+ V(ASCII_STRING_TYPE, \
+ kVariableSizeSentinel, \
+ ascii_string, \
+ AsciiString) \
+ V(CONS_STRING_TYPE, \
+ ConsString::kSize, \
+ cons_string, \
+ ConsString) \
+ V(CONS_ASCII_STRING_TYPE, \
+ ConsString::kSize, \
+ cons_ascii_string, \
+ ConsAsciiString) \
+ V(SLICED_STRING_TYPE, \
+ SlicedString::kSize, \
+ sliced_string, \
+ SlicedString) \
+ V(SLICED_ASCII_STRING_TYPE, \
+ SlicedString::kSize, \
+ sliced_ascii_string, \
+ SlicedAsciiString) \
+ V(EXTERNAL_STRING_TYPE, \
+ ExternalTwoByteString::kSize, \
+ external_string, \
+ ExternalString) \
+ V(EXTERNAL_ASCII_STRING_TYPE, \
+ ExternalAsciiString::kSize, \
+ external_ascii_string, \
+ ExternalAsciiString) \
+ V(EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE, \
+ ExternalTwoByteString::kSize, \
+ external_string_with_one_bytei_data, \
+ ExternalStringWithOneByteData) \
+ V(SHORT_EXTERNAL_STRING_TYPE, \
+ ExternalTwoByteString::kShortSize, \
+ short_external_string, \
+ ShortExternalString) \
+ V(SHORT_EXTERNAL_ASCII_STRING_TYPE, \
+ ExternalAsciiString::kShortSize, \
+ short_external_ascii_string, \
+ ShortExternalAsciiString) \
+ V(SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE, \
+ ExternalTwoByteString::kShortSize, \
+ short_external_string_with_one_byte_data, \
+ ShortExternalStringWithOneByteData) \
+ \
+ V(INTERNALIZED_STRING_TYPE, \
+ kVariableSizeSentinel, \
+ internalized_string, \
+ InternalizedString) \
+ V(ASCII_INTERNALIZED_STRING_TYPE, \
+ kVariableSizeSentinel, \
+ ascii_internalized_string, \
+ AsciiInternalizedString) \
+ V(CONS_INTERNALIZED_STRING_TYPE, \
+ ConsString::kSize, \
+ cons_internalized_string, \
+ ConsInternalizedString) \
+ V(CONS_ASCII_INTERNALIZED_STRING_TYPE, \
+ ConsString::kSize, \
+ cons_ascii_internalized_string, \
+ ConsAsciiInternalizedString) \
+ V(EXTERNAL_INTERNALIZED_STRING_TYPE, \
+ ExternalTwoByteString::kSize, \
+ external_internalized_string, \
+ ExternalInternalizedString) \
+ V(EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE, \
+ ExternalAsciiString::kSize, \
+ external_ascii_internalized_string, \
+ ExternalAsciiInternalizedString) \
+ V(EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE, \
+ ExternalTwoByteString::kSize, \
+ external_internalized_string_with_one_byte_data, \
+ ExternalInternalizedStringWithOneByteData) \
+ V(SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE, \
+ ExternalTwoByteString::kShortSize, \
+ short_external_internalized_string, \
+ ShortExternalInternalizedString) \
+ V(SHORT_EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE, \
+ ExternalAsciiString::kShortSize, \
+ short_external_ascii_internalized_string, \
+ ShortExternalAsciiInternalizedString) \
+ V(SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE, \
+ ExternalTwoByteString::kShortSize, \
+ short_external_internalized_string_with_one_byte_data, \
+ ShortExternalInternalizedStringWithOneByteData) \
+
+// 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
+// code for the class including allocation and garbage collection routines,
+// casts and predicates. All you need to define is the class, methods and
+// object verification routines. Easy, no?
+//
+// Note that for subtle reasons related to the ordering or numerical values of
+// type tags, elements in this list have to be added to the INSTANCE_TYPE_LIST
+// manually.
+#define STRUCT_LIST_ALL(V) \
+ V(BOX, Box, box) \
+ V(DECLARED_ACCESSOR_DESCRIPTOR, \
+ DeclaredAccessorDescriptor, \
+ declared_accessor_descriptor) \
+ V(DECLARED_ACCESSOR_INFO, DeclaredAccessorInfo, declared_accessor_info) \
+ V(EXECUTABLE_ACCESSOR_INFO, ExecutableAccessorInfo, executable_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) \
+ V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \
+ V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
+ V(SIGNATURE_INFO, SignatureInfo, signature_info) \
+ V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
+ V(SCRIPT, Script, script) \
+ V(ALLOCATION_SITE, AllocationSite, allocation_site) \
+ V(ALLOCATION_MEMENTO, AllocationMemento, allocation_memento) \
+ V(CODE_CACHE, CodeCache, code_cache) \
+ V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache) \
+ V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info) \
+ V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry)
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#define STRUCT_LIST_DEBUGGER(V) \
+ V(DEBUG_INFO, DebugInfo, debug_info) \
+ V(BREAK_POINT_INFO, BreakPointInfo, break_point_info)
+#else
+#define STRUCT_LIST_DEBUGGER(V)
+#endif
+
+#define STRUCT_LIST(V) \
+ STRUCT_LIST_ALL(V) \
+ STRUCT_LIST_DEBUGGER(V)
+
+// We use the full 8 bits of the instance_type field to encode heap object
+// instance types. The high-order bit (bit 7) is set if the object is not a
+// string, and cleared if it is a string.
+const uint32_t kIsNotStringMask = 0x80;
+const uint32_t kStringTag = 0x0;
+const uint32_t kNotStringTag = 0x80;
+
+// Bit 6 indicates that the object is an internalized string (if set) or not.
+// Bit 7 has to be clear as well.
+const uint32_t kIsNotInternalizedMask = 0x40;
+const uint32_t kNotInternalizedTag = 0x40;
+const uint32_t kInternalizedTag = 0x0;
+
+// If bit 7 is clear then bit 2 indicates whether the string consists of
+// two-byte characters or one-byte characters.
+const uint32_t kStringEncodingMask = 0x4;
+const uint32_t kTwoByteStringTag = 0x0;
+const uint32_t kOneByteStringTag = 0x4;
+
+// If bit 7 is clear, the low-order 2 bits indicate the representation
+// of the string.
+const uint32_t kStringRepresentationMask = 0x03;
+enum StringRepresentationTag {
+ kSeqStringTag = 0x0,
+ kConsStringTag = 0x1,
+ kExternalStringTag = 0x2,
+ kSlicedStringTag = 0x3
+};
+const uint32_t kIsIndirectStringMask = 0x1;
+const uint32_t kIsIndirectStringTag = 0x1;
+STATIC_ASSERT((kSeqStringTag & kIsIndirectStringMask) == 0);
+STATIC_ASSERT((kExternalStringTag & kIsIndirectStringMask) == 0);
+STATIC_ASSERT(
+ (kConsStringTag & kIsIndirectStringMask) == kIsIndirectStringTag);
+STATIC_ASSERT(
+ (kSlicedStringTag & kIsIndirectStringMask) == kIsIndirectStringTag);
+
+// Use this mask to distinguish between cons and slice only after making
+// sure that the string is one of the two (an indirect string).
+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 one byte data.
+const uint32_t kOneByteDataHintMask = 0x08;
+const uint32_t kOneByteDataHintTag = 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 internalized.
+// It's not common to have non-flat internalized strings, so we do not
+// shortcut them thereby avoiding turning internalized strings into strings.
+// See heap.cc and mark-compact.cc.
+const uint32_t kShortcutTypeMask =
+ kIsNotStringMask |
+ kIsNotInternalizedMask |
+ kStringRepresentationMask;
+const uint32_t kShortcutTypeTag = kConsStringTag | kNotInternalizedTag;
+
+
+enum InstanceType {
+ // String types.
+ INTERNALIZED_STRING_TYPE = kTwoByteStringTag | kSeqStringTag
+ | kInternalizedTag,
+ ASCII_INTERNALIZED_STRING_TYPE = kOneByteStringTag | kSeqStringTag
+ | kInternalizedTag,
+ CONS_INTERNALIZED_STRING_TYPE = kTwoByteStringTag | kConsStringTag
+ | kInternalizedTag,
+ CONS_ASCII_INTERNALIZED_STRING_TYPE = kOneByteStringTag | kConsStringTag
+ | kInternalizedTag,
+ EXTERNAL_INTERNALIZED_STRING_TYPE = kTwoByteStringTag | kExternalStringTag
+ | kInternalizedTag,
+ EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE = kOneByteStringTag
+ | kExternalStringTag | kInternalizedTag,
+ EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE =
+ EXTERNAL_INTERNALIZED_STRING_TYPE | kOneByteDataHintTag
+ | kInternalizedTag,
+ SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE =
+ EXTERNAL_INTERNALIZED_STRING_TYPE | kShortExternalStringTag
+ | kInternalizedTag,
+ SHORT_EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE =
+ EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE | kShortExternalStringTag
+ | kInternalizedTag,
+ SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE =
+ EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE
+ | kShortExternalStringTag | kInternalizedTag,
+
+ STRING_TYPE = INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
+ ASCII_STRING_TYPE = ASCII_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
+ CONS_STRING_TYPE = CONS_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
+ CONS_ASCII_STRING_TYPE =
+ CONS_ASCII_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
+
+ SLICED_STRING_TYPE =
+ kTwoByteStringTag | kSlicedStringTag | kNotInternalizedTag,
+ SLICED_ASCII_STRING_TYPE =
+ kOneByteStringTag | kSlicedStringTag | kNotInternalizedTag,
+ EXTERNAL_STRING_TYPE =
+ EXTERNAL_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
+ EXTERNAL_ASCII_STRING_TYPE =
+ EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
+ EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE =
+ EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE
+ | kNotInternalizedTag,
+ SHORT_EXTERNAL_STRING_TYPE =
+ SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
+ SHORT_EXTERNAL_ASCII_STRING_TYPE =
+ SHORT_EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE | kNotInternalizedTag,
+ SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE =
+ SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE
+ | kNotInternalizedTag,
+
+ // Non-string names
+ SYMBOL_TYPE = kNotStringTag, // LAST_NAME_TYPE, FIRST_NONSTRING_TYPE
+
+ // Objects allocated in their own spaces (never in new space).
+ MAP_TYPE,
+ CODE_TYPE,
+ ODDBALL_TYPE,
+ CELL_TYPE,
+ PROPERTY_CELL_TYPE,
+
+ // "Data", objects that cannot contain non-map-word pointers to heap
+ // objects.
+ 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,
+ EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE,
+ EXTERNAL_INT_ARRAY_TYPE,
+ EXTERNAL_UNSIGNED_INT_ARRAY_TYPE,
+ EXTERNAL_FLOAT_ARRAY_TYPE,
+ EXTERNAL_DOUBLE_ARRAY_TYPE,
+ EXTERNAL_PIXEL_ARRAY_TYPE, // LAST_EXTERNAL_ARRAY_TYPE
+ FIXED_DOUBLE_ARRAY_TYPE,
+ FILLER_TYPE, // LAST_DATA_TYPE
+
+ // Structs.
+ DECLARED_ACCESSOR_DESCRIPTOR_TYPE,
+ DECLARED_ACCESSOR_INFO_TYPE,
+ EXECUTABLE_ACCESSOR_INFO_TYPE,
+ ACCESSOR_PAIR_TYPE,
+ ACCESS_CHECK_INFO_TYPE,
+ INTERCEPTOR_INFO_TYPE,
+ CALL_HANDLER_INFO_TYPE,
+ FUNCTION_TEMPLATE_INFO_TYPE,
+ OBJECT_TEMPLATE_INFO_TYPE,
+ SIGNATURE_INFO_TYPE,
+ TYPE_SWITCH_INFO_TYPE,
+ ALLOCATION_SITE_TYPE,
+ ALLOCATION_MEMENTO_TYPE,
+ SCRIPT_TYPE,
+ CODE_CACHE_TYPE,
+ POLYMORPHIC_CODE_CACHE_TYPE,
+ TYPE_FEEDBACK_INFO_TYPE,
+ ALIASED_ARGUMENTS_ENTRY_TYPE,
+ BOX_TYPE,
+ // The following two instance types are only used when ENABLE_DEBUGGER_SUPPORT
+ // is defined. However as include/v8.h contain some of the instance type
+ // constants always having them avoids them getting different numbers
+ // depending on whether ENABLE_DEBUGGER_SUPPORT is defined or not.
+ DEBUG_INFO_TYPE,
+ BREAK_POINT_INFO_TYPE,
+
+ FIXED_ARRAY_TYPE,
+ SHARED_FUNCTION_INFO_TYPE,
+
+ JS_MESSAGE_OBJECT_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_DATE_TYPE,
+ JS_OBJECT_TYPE,
+ JS_CONTEXT_EXTENSION_OBJECT_TYPE,
+ JS_GENERATOR_OBJECT_TYPE,
+ JS_MODULE_TYPE,
+ JS_GLOBAL_OBJECT_TYPE,
+ JS_BUILTINS_OBJECT_TYPE,
+ JS_GLOBAL_PROXY_TYPE,
+ JS_ARRAY_TYPE,
+ JS_ARRAY_BUFFER_TYPE,
+ JS_TYPED_ARRAY_TYPE,
+ JS_DATA_VIEW_TYPE,
+ JS_SET_TYPE,
+ JS_MAP_TYPE,
+ JS_WEAK_MAP_TYPE,
+ JS_WEAK_SET_TYPE,
+
+ JS_REGEXP_TYPE,
+
+ JS_FUNCTION_TYPE, // LAST_JS_OBJECT_TYPE, LAST_JS_RECEIVER_TYPE
+
+ // Pseudo-types
+ FIRST_TYPE = 0x0,
+ LAST_TYPE = JS_FUNCTION_TYPE,
+ FIRST_NAME_TYPE = FIRST_TYPE,
+ LAST_NAME_TYPE = SYMBOL_TYPE,
+ FIRST_UNIQUE_NAME_TYPE = INTERNALIZED_STRING_TYPE,
+ LAST_UNIQUE_NAME_TYPE = SYMBOL_TYPE,
+ FIRST_NONSTRING_TYPE = SYMBOL_TYPE,
+ // Boundaries for testing for an external array.
+ FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE,
+ LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_PIXEL_ARRAY_TYPE,
+ // Boundary for promotion to old data space/old pointer space.
+ LAST_DATA_TYPE = FILLER_TYPE,
+ // Boundary for objects represented as JSReceiver (i.e. JSObject or JSProxy).
+ // Note that there is no range for JSObject or JSProxy, since their subtypes
+ // 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_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_PROXY_TYPE,
+ LAST_NONCALLABLE_SPEC_OBJECT_TYPE = JS_REGEXP_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
+};
+
+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);
+STATIC_CHECK(ODDBALL_TYPE == Internals::kOddballType);
+STATIC_CHECK(FOREIGN_TYPE == Internals::kForeignType);
+
+
+#define FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(V) \
+ V(FAST_ELEMENTS_SUB_TYPE) \
+ V(DICTIONARY_ELEMENTS_SUB_TYPE) \
+ V(FAST_PROPERTIES_SUB_TYPE) \
+ V(DICTIONARY_PROPERTIES_SUB_TYPE) \
+ V(MAP_CODE_CACHE_SUB_TYPE) \
+ V(SCOPE_INFO_SUB_TYPE) \
+ V(STRING_TABLE_SUB_TYPE) \
+ V(DESCRIPTOR_ARRAY_SUB_TYPE) \
+ V(TRANSITION_ARRAY_SUB_TYPE)
+
+enum FixedArraySubInstanceType {
+#define DEFINE_FIXED_ARRAY_SUB_INSTANCE_TYPE(name) name,
+ FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(DEFINE_FIXED_ARRAY_SUB_INSTANCE_TYPE)
+#undef DEFINE_FIXED_ARRAY_SUB_INSTANCE_TYPE
+ LAST_FIXED_ARRAY_SUB_TYPE = TRANSITION_ARRAY_SUB_TYPE
+};
+
+
+enum CompareResult {
+ LESS = -1,
+ EQUAL = 0,
+ GREATER = 1,
+
+ NOT_EQUAL = GREATER
+};
+
+
+#define DECL_BOOLEAN_ACCESSORS(name) \
+ inline bool name(); \
+ inline void set_##name(bool value); \
+
+
+#define DECL_ACCESSORS(name, type) \
+ inline type* name(); \
+ inline void set_##name(type* value, \
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER); \
+
+
+class AccessorPair;
+class DictionaryElementsAccessor;
+class ElementsAccessor;
+class Failure;
+class FixedArrayBase;
+class ObjectVisitor;
+class StringStream;
+class Type;
+
+struct ValueInfo : public Malloced {
+ ValueInfo() : type(FIRST_TYPE), ptr(NULL), str(NULL), number(0) { }
+ InstanceType type;
+ Object* ptr;
+ const char* str;
+ double number;
+};
+
+
+// A template-ized version of the IsXXX functions.
+template <class C> inline bool Is(Object* obj);
+
+#ifdef VERIFY_HEAP
+#define DECLARE_VERIFIER(Name) void Name##Verify();
+#else
+#define DECLARE_VERIFIER(Name)
+#endif
+
+#ifdef OBJECT_PRINT
+#define DECLARE_PRINTER(Name) void Name##Print(FILE* out = stdout);
+#else
+#define DECLARE_PRINTER(Name)
+#endif
+
+class MaybeObject BASE_EMBEDDED {
+ public:
+ inline bool IsFailure();
+ inline bool IsRetryAfterGC();
+ inline bool IsOutOfMemory();
+ inline bool IsException();
+ INLINE(bool IsTheHole());
+ INLINE(bool IsUninitialized());
+ inline bool ToObject(Object** obj) {
+ if (IsFailure()) return false;
+ *obj = reinterpret_cast<Object*>(this);
+ return true;
+ }
+ inline Failure* ToFailureUnchecked() {
+ ASSERT(IsFailure());
+ return reinterpret_cast<Failure*>(this);
+ }
+ inline Object* ToObjectUnchecked() {
+ // TODO(jkummerow): Turn this back into an ASSERT when we can be certain
+ // that it never fires in Release mode in the wild.
+ CHECK(!IsFailure());
+ return reinterpret_cast<Object*>(this);
+ }
+ inline Object* ToObjectChecked() {
+ CHECK(!IsFailure());
+ return reinterpret_cast<Object*>(this);
+ }
+
+ template<typename T>
+ inline bool To(T** obj) {
+ if (IsFailure()) return false;
+ *obj = T::cast(reinterpret_cast<Object*>(this));
+ return true;
+ }
+
+ template<typename T>
+ inline bool ToHandle(Handle<T>* obj, Isolate* isolate) {
+ if (IsFailure()) return false;
+ *obj = handle(T::cast(reinterpret_cast<Object*>(this)), isolate);
+ return true;
+ }
+
+#ifdef OBJECT_PRINT
+ // Prints this object with details.
+ void Print();
+ void Print(FILE* out);
+ void PrintLn();
+ void PrintLn(FILE* out);
+#endif
+#ifdef VERIFY_HEAP
+ // Verifies the object.
+ void Verify();
+#endif
+};
+
+
+#define OBJECT_TYPE_LIST(V) \
+ V(Smi) \
+ V(HeapObject) \
+ V(Number) \
+
+#define HEAP_OBJECT_TYPE_LIST(V) \
+ V(HeapNumber) \
+ V(Name) \
+ V(UniqueName) \
+ V(String) \
+ V(SeqString) \
+ V(ExternalString) \
+ V(ConsString) \
+ V(SlicedString) \
+ V(ExternalTwoByteString) \
+ V(ExternalAsciiString) \
+ V(SeqTwoByteString) \
+ V(SeqOneByteString) \
+ V(InternalizedString) \
+ V(Symbol) \
+ \
+ V(ExternalArray) \
+ V(ExternalByteArray) \
+ V(ExternalUnsignedByteArray) \
+ V(ExternalShortArray) \
+ V(ExternalUnsignedShortArray) \
+ V(ExternalIntArray) \
+ V(ExternalUnsignedIntArray) \
+ V(ExternalFloatArray) \
+ V(ExternalDoubleArray) \
+ V(ExternalPixelArray) \
+ V(ByteArray) \
+ V(FreeSpace) \
+ V(JSReceiver) \
+ V(JSObject) \
+ V(JSContextExtensionObject) \
+ V(JSGeneratorObject) \
+ V(JSModule) \
+ V(Map) \
+ V(DescriptorArray) \
+ V(TransitionArray) \
+ V(DeoptimizationInputData) \
+ V(DeoptimizationOutputData) \
+ V(DependentCode) \
+ V(TypeFeedbackCells) \
+ V(FixedArray) \
+ V(FixedDoubleArray) \
+ V(Context) \
+ V(NativeContext) \
+ V(ScopeInfo) \
+ V(JSFunction) \
+ V(Code) \
+ V(Oddball) \
+ V(SharedFunctionInfo) \
+ V(JSValue) \
+ V(JSDate) \
+ V(JSMessageObject) \
+ V(StringWrapper) \
+ V(Foreign) \
+ V(Boolean) \
+ V(JSArray) \
+ V(JSArrayBuffer) \
+ V(JSArrayBufferView) \
+ V(JSTypedArray) \
+ V(JSDataView) \
+ V(JSProxy) \
+ V(JSFunctionProxy) \
+ V(JSSet) \
+ V(JSMap) \
+ V(JSWeakCollection) \
+ V(JSWeakMap) \
+ V(JSWeakSet) \
+ V(JSRegExp) \
+ V(HashTable) \
+ V(Dictionary) \
+ V(StringTable) \
+ V(JSFunctionResultCache) \
+ V(NormalizedMapCache) \
+ V(CompilationCacheTable) \
+ V(CodeCacheHashTable) \
+ V(PolymorphicCodeCacheHashTable) \
+ V(MapCache) \
+ V(Primitive) \
+ V(GlobalObject) \
+ V(JSGlobalObject) \
+ V(JSBuiltinsObject) \
+ V(JSGlobalProxy) \
+ V(UndetectableObject) \
+ V(AccessCheckNeeded) \
+ V(Cell) \
+ V(PropertyCell) \
+ V(ObjectHashTable)
+
+
+#define ERROR_MESSAGES_LIST(V) \
+ V(kNoReason, "no reason") \
+ \
+ V(k32BitValueInRegisterIsNotZeroExtended, \
+ "32 bit value in register is not zero-extended") \
+ V(kAlignmentMarkerExpected, "alignment marker expected") \
+ V(kAllocationIsNotDoubleAligned, "Allocation is not double aligned") \
+ V(kAPICallReturnedInvalidObject, "API call returned invalid object") \
+ V(kArgumentsObjectValueInATestContext, \
+ "arguments object value in a test context") \
+ V(kArrayBoilerplateCreationFailed, "array boilerplate creation failed") \
+ V(kArrayIndexConstantValueTooBig, "array index constant value too big") \
+ V(kAssignmentToArguments, "assignment to arguments") \
+ V(kAssignmentToLetVariableBeforeInitialization, \
+ "assignment to let variable before initialization") \
+ V(kAssignmentToLOOKUPVariable, "assignment to LOOKUP variable") \
+ V(kAssignmentToParameterFunctionUsesArgumentsObject, \
+ "assignment to parameter, function uses arguments object") \
+ V(kAssignmentToParameterInArgumentsObject, \
+ "assignment to parameter in arguments object") \
+ V(kAttemptToUseUndefinedCache, "Attempt to use undefined cache") \
+ V(kBadValueContextForArgumentsObjectValue, \
+ "bad value context for arguments object value") \
+ V(kBadValueContextForArgumentsValue, \
+ "bad value context for arguments value") \
+ V(kBailedOutDueToDependencyChange, "bailed out due to dependency change") \
+ V(kBailoutWasNotPrepared, "bailout was not prepared") \
+ V(kBinaryStubGenerateFloatingPointCode, \
+ "BinaryStub_GenerateFloatingPointCode") \
+ V(kBothRegistersWereSmisInSelectNonSmi, \
+ "Both registers were smis in SelectNonSmi") \
+ V(kCallToAJavaScriptRuntimeFunction, \
+ "call to a JavaScript runtime function") \
+ V(kCannotTranslatePositionInChangedArea, \
+ "Cannot translate position in changed area") \
+ V(kCodeGenerationFailed, "code generation failed") \
+ V(kCodeObjectNotProperlyPatched, "code object not properly patched") \
+ V(kCompoundAssignmentToLookupSlot, "compound assignment to lookup slot") \
+ V(kContextAllocatedArguments, "context-allocated arguments") \
+ V(kDebuggerIsActive, "debugger is active") \
+ V(kDebuggerStatement, "DebuggerStatement") \
+ V(kDeclarationInCatchContext, "Declaration in catch context") \
+ V(kDeclarationInWithContext, "Declaration in with context") \
+ V(kDefaultNaNModeNotSet, "Default NaN mode not set") \
+ V(kDeleteWithGlobalVariable, "delete with global variable") \
+ V(kDeleteWithNonGlobalVariable, "delete with non-global variable") \
+ V(kDestinationOfCopyNotAligned, "Destination of copy not aligned") \
+ V(kDontDeleteCellsCannotContainTheHole, \
+ "DontDelete cells can't contain the hole") \
+ V(kDoPushArgumentNotImplementedForDoubleType, \
+ "DoPushArgument not implemented for double type") \
+ V(kEmitLoadRegisterUnsupportedDoubleImmediate, \
+ "EmitLoadRegister: Unsupported double immediate") \
+ V(kEval, "eval") \
+ V(kExpected0AsASmiSentinel, "Expected 0 as a Smi sentinel") \
+ V(kExpectedAlignmentMarker, "expected alignment marker") \
+ V(kExpectedPropertyCellInRegisterA2, \
+ "Expected property cell in register a2") \
+ V(kExpectedPropertyCellInRegisterEbx, \
+ "Expected property cell in register ebx") \
+ V(kExpectedPropertyCellInRegisterRbx, \
+ "Expected property cell in register rbx") \
+ V(kExpectingAlignmentForCopyBytes, \
+ "Expecting alignment for CopyBytes") \
+ V(kExternalStringExpectedButNotFound, \
+ "external string expected, but not found") \
+ V(kFailedBailedOutLastTime, "failed/bailed out last time") \
+ V(kForInStatementIsNotFastCase, "ForInStatement is not fast case") \
+ V(kForInStatementOptimizationIsDisabled, \
+ "ForInStatement optimization is disabled") \
+ V(kForInStatementWithNonLocalEachVariable, \
+ "ForInStatement with non-local each variable") \
+ V(kForOfStatement, "ForOfStatement") \
+ V(kFrameIsExpectedToBeAligned, "frame is expected to be aligned") \
+ V(kFunctionCallsEval, "function calls eval") \
+ V(kFunctionIsAGenerator, "function is a generator") \
+ V(kFunctionWithIllegalRedeclaration, "function with illegal redeclaration") \
+ V(kGeneratedCodeIsTooLarge, "Generated code is too large") \
+ V(kGeneratorFailedToResume, "Generator failed to resume") \
+ V(kGenerator, "generator") \
+ V(kGlobalFunctionsMustHaveInitialMap, \
+ "Global functions must have initial map") \
+ V(kHeapNumberMapRegisterClobbered, "HeapNumberMap register clobbered") \
+ V(kImproperObjectOnPrototypeChainForStore, \
+ "improper object on prototype chain for store") \
+ V(kIndexIsNegative, "Index is negative") \
+ V(kIndexIsTooLarge, "Index is too large") \
+ V(kInlinedRuntimeFunctionClassOf, "inlined runtime function: ClassOf") \
+ V(kInlinedRuntimeFunctionFastAsciiArrayJoin, \
+ "inlined runtime function: FastAsciiArrayJoin") \
+ V(kInlinedRuntimeFunctionGeneratorNext, \
+ "inlined runtime function: GeneratorNext") \
+ V(kInlinedRuntimeFunctionGeneratorThrow, \
+ "inlined runtime function: GeneratorThrow") \
+ V(kInlinedRuntimeFunctionGetFromCache, \
+ "inlined runtime function: GetFromCache") \
+ V(kInlinedRuntimeFunctionIsNonNegativeSmi, \
+ "inlined runtime function: IsNonNegativeSmi") \
+ V(kInlinedRuntimeFunctionIsRegExpEquivalent, \
+ "inlined runtime function: IsRegExpEquivalent") \
+ V(kInlinedRuntimeFunctionIsStringWrapperSafeForDefaultValueOf, \
+ "inlined runtime function: IsStringWrapperSafeForDefaultValueOf") \
+ V(kInliningBailedOut, "inlining bailed out") \
+ V(kInputGPRIsExpectedToHaveUpper32Cleared, \
+ "input GPR is expected to have upper32 cleared") \
+ V(kInstanceofStubUnexpectedCallSiteCacheCheck, \
+ "InstanceofStub unexpected call site cache (check)") \
+ V(kInstanceofStubUnexpectedCallSiteCacheCmp1, \
+ "InstanceofStub unexpected call site cache (cmp 1)") \
+ V(kInstanceofStubUnexpectedCallSiteCacheCmp2, \
+ "InstanceofStub unexpected call site cache (cmp 2)") \
+ V(kInstanceofStubUnexpectedCallSiteCacheMov, \
+ "InstanceofStub unexpected call site cache (mov)") \
+ V(kInteger32ToSmiFieldWritingToNonSmiLocation, \
+ "Integer32ToSmiField writing to non-smi location") \
+ V(kInvalidCaptureReferenced, "Invalid capture referenced") \
+ V(kInvalidElementsKindForInternalArrayOrInternalPackedArray, \
+ "Invalid ElementsKind for InternalArray or InternalPackedArray") \
+ V(kInvalidHandleScopeLevel, "Invalid HandleScope level") \
+ V(kInvalidLeftHandSideInAssignment, "invalid left-hand side in assignment") \
+ V(kInvalidLhsInCompoundAssignment, "invalid lhs in compound assignment") \
+ V(kInvalidLhsInCountOperation, "invalid lhs in count operation") \
+ V(kInvalidMinLength, "Invalid min_length") \
+ V(kJSGlobalObjectNativeContextShouldBeANativeContext, \
+ "JSGlobalObject::native_context should be a native context") \
+ V(kJSGlobalProxyContextShouldNotBeNull, \
+ "JSGlobalProxy::context() should not be null") \
+ V(kJSObjectWithFastElementsMapHasSlowElements, \
+ "JSObject with fast elements map has slow elements") \
+ V(kLetBindingReInitialization, "Let binding re-initialization") \
+ V(kLiveBytesCountOverflowChunkSize, "Live Bytes Count overflow chunk size") \
+ V(kLiveEditFrameDroppingIsNotSupportedOnArm, \
+ "LiveEdit frame dropping is not supported on arm") \
+ V(kLiveEditFrameDroppingIsNotSupportedOnMips, \
+ "LiveEdit frame dropping is not supported on mips") \
+ V(kLiveEdit, "LiveEdit") \
+ V(kLookupVariableInCountOperation, \
+ "lookup variable in count operation") \
+ V(kMapIsNoLongerInEax, "Map is no longer in eax") \
+ V(kNoCasesLeft, "no cases left") \
+ V(kNoEmptyArraysHereInEmitFastAsciiArrayJoin, \
+ "No empty arrays here in EmitFastAsciiArrayJoin") \
+ V(kNonInitializerAssignmentToConst, \
+ "non-initializer assignment to const") \
+ V(kNonSmiIndex, "Non-smi index") \
+ V(kNonSmiKeyInArrayLiteral, "Non-smi key in array literal") \
+ V(kNonSmiValue, "Non-smi value") \
+ V(kNotEnoughVirtualRegistersForValues, \
+ "not enough virtual registers for values") \
+ V(kNotEnoughVirtualRegistersRegalloc, \
+ "not enough virtual registers (regalloc)") \
+ V(kObjectFoundInSmiOnlyArray, "object found in smi-only array") \
+ V(kObjectLiteralWithComplexProperty, \
+ "Object literal with complex property") \
+ V(kOddballInStringTableIsNotUndefinedOrTheHole, \
+ "oddball in string table is not undefined or the hole") \
+ V(kOperandIsASmiAndNotAName, "Operand is a smi and not a name") \
+ V(kOperandIsASmiAndNotAString, "Operand is a smi and not a string") \
+ V(kOperandIsASmi, "Operand is a smi") \
+ V(kOperandIsNotAName, "Operand is not a name") \
+ V(kOperandIsNotANumber, "Operand is not a number") \
+ V(kOperandIsNotASmi, "Operand is not a smi") \
+ V(kOperandIsNotAString, "Operand is not a string") \
+ V(kOperandIsNotSmi, "Operand is not smi") \
+ V(kOperandNotANumber, "Operand not a number") \
+ V(kOptimizedTooManyTimes, "optimized too many times") \
+ V(kOutOfVirtualRegistersWhileTryingToAllocateTempRegister, \
+ "Out of virtual registers while trying to allocate temp register") \
+ V(kParseScopeError, "parse/scope error") \
+ V(kPossibleDirectCallToEval, "possible direct call to eval") \
+ V(kPropertyAllocationCountFailed, "Property allocation count failed") \
+ V(kReceivedInvalidReturnAddress, "Received invalid return address") \
+ V(kReferenceToAVariableWhichRequiresDynamicLookup, \
+ "reference to a variable which requires dynamic lookup") \
+ V(kReferenceToGlobalLexicalVariable, \
+ "reference to global lexical variable") \
+ V(kReferenceToUninitializedVariable, "reference to uninitialized variable") \
+ V(kRegisterDidNotMatchExpectedRoot, "Register did not match expected root") \
+ V(kRegisterWasClobbered, "register was clobbered") \
+ V(kScopedBlock, "ScopedBlock") \
+ V(kSharedFunctionInfoLiteral, "SharedFunctionInfoLiteral") \
+ V(kSmiAdditionOverflow, "Smi addition overflow") \
+ V(kSmiSubtractionOverflow, "Smi subtraction overflow") \
+ V(kStackFrameTypesMustMatch, "stack frame types must match") \
+ V(kSwitchStatementMixedOrNonLiteralSwitchLabels, \
+ "SwitchStatement: mixed or non-literal switch labels") \
+ V(kSwitchStatementTooManyClauses, "SwitchStatement: too many clauses") \
+ V(kTheInstructionShouldBeALui, "The instruction should be a lui") \
+ V(kTheInstructionShouldBeAnOri, "The instruction should be an ori") \
+ V(kTheInstructionToPatchShouldBeALoadFromPc, \
+ "The instruction to patch should be a load from pc") \
+ V(kTheInstructionToPatchShouldBeALui, \
+ "The instruction to patch should be a lui") \
+ V(kTheInstructionToPatchShouldBeAnOri, \
+ "The instruction to patch should be an ori") \
+ V(kTooManyParametersLocals, "too many parameters/locals") \
+ V(kTooManyParameters, "too many parameters") \
+ V(kTooManySpillSlotsNeededForOSR, "Too many spill slots needed for OSR") \
+ V(kToOperandIsDoubleRegisterUnimplemented, \
+ "ToOperand IsDoubleRegister unimplemented") \
+ V(kToOperandUnsupportedDoubleImmediate, \
+ "ToOperand Unsupported double immediate") \
+ V(kTryCatchStatement, "TryCatchStatement") \
+ V(kTryFinallyStatement, "TryFinallyStatement") \
+ V(kUnableToEncodeValueAsSmi, "Unable to encode value as smi") \
+ V(kUnalignedAllocationInNewSpace, "Unaligned allocation in new space") \
+ V(kUndefinedValueNotLoaded, "Undefined value not loaded") \
+ V(kUndoAllocationOfNonAllocatedMemory, \
+ "Undo allocation of non allocated memory") \
+ V(kUnexpectedAllocationTop, "Unexpected allocation top") \
+ V(kUnexpectedElementsKindInArrayConstructor, \
+ "Unexpected ElementsKind in array constructor") \
+ V(kUnexpectedFallthroughFromCharCodeAtSlowCase, \
+ "Unexpected fallthrough from CharCodeAt slow case") \
+ V(kUnexpectedFallthroughFromCharFromCodeSlowCase, \
+ "Unexpected fallthrough from CharFromCode slow case") \
+ V(kUnexpectedFallThroughFromStringComparison, \
+ "Unexpected fall-through from string comparison") \
+ V(kUnexpectedFallThroughInBinaryStubGenerateFloatingPointCode, \
+ "Unexpected fall-through in BinaryStub_GenerateFloatingPointCode") \
+ V(kUnexpectedFallthroughToCharCodeAtSlowCase, \
+ "Unexpected fallthrough to CharCodeAt slow case") \
+ V(kUnexpectedFallthroughToCharFromCodeSlowCase, \
+ "Unexpected fallthrough to CharFromCode slow case") \
+ V(kUnexpectedFPUStackDepthAfterInstruction, \
+ "Unexpected FPU stack depth after instruction") \
+ V(kUnexpectedInitialMapForArrayFunction1, \
+ "Unexpected initial map for Array function (1)") \
+ V(kUnexpectedInitialMapForArrayFunction2, \
+ "Unexpected initial map for Array function (2)") \
+ V(kUnexpectedInitialMapForArrayFunction, \
+ "Unexpected initial map for Array function") \
+ V(kUnexpectedInitialMapForInternalArrayFunction, \
+ "Unexpected initial map for InternalArray function") \
+ V(kUnexpectedLevelAfterReturnFromApiCall, \
+ "Unexpected level after return from api call") \
+ V(kUnexpectedNumberOfPreAllocatedPropertyFields, \
+ "Unexpected number of pre-allocated property fields") \
+ V(kUnexpectedStringFunction, "Unexpected String function") \
+ V(kUnexpectedStringType, "Unexpected string type") \
+ V(kUnexpectedStringWrapperInstanceSize, \
+ "Unexpected string wrapper instance size") \
+ V(kUnexpectedTypeForRegExpDataFixedArrayExpected, \
+ "Unexpected type for RegExp data, FixedArray expected") \
+ V(kUnexpectedUnusedPropertiesOfStringWrapper, \
+ "Unexpected unused properties of string wrapper") \
+ V(kUninitializedKSmiConstantRegister, "Uninitialized kSmiConstantRegister") \
+ V(kUnknown, "unknown") \
+ V(kUnsupportedConstCompoundAssignment, \
+ "unsupported const compound assignment") \
+ V(kUnsupportedCountOperationWithConst, \
+ "unsupported count operation with const") \
+ V(kUnsupportedDoubleImmediate, "unsupported double immediate") \
+ V(kUnsupportedLetCompoundAssignment, "unsupported let compound assignment") \
+ V(kUnsupportedLookupSlotInDeclaration, \
+ "unsupported lookup slot in declaration") \
+ V(kUnsupportedNonPrimitiveCompare, "Unsupported non-primitive compare") \
+ V(kUnsupportedPhiUseOfArguments, "Unsupported phi use of arguments") \
+ V(kUnsupportedPhiUseOfConstVariable, \
+ "Unsupported phi use of const variable") \
+ V(kUnsupportedTaggedImmediate, "unsupported tagged immediate") \
+ V(kVariableResolvedToWithContext, "Variable resolved to with context") \
+ V(kWeShouldNotHaveAnEmptyLexicalContext, \
+ "we should not have an empty lexical context") \
+ V(kWithStatement, "WithStatement") \
+ V(kWrongAddressOrValuePassedToRecordWrite, \
+ "Wrong address or value passed to RecordWrite")
+
+
+#define ERROR_MESSAGES_CONSTANTS(C, T) C,
+enum BailoutReason {
+ ERROR_MESSAGES_LIST(ERROR_MESSAGES_CONSTANTS)
+ kLastErrorMessage
+};
+#undef ERROR_MESSAGES_CONSTANTS
+
+
+const char* GetBailoutReason(BailoutReason reason);
+
+
+// Object is the abstract superclass for all classes in the
+// object hierarchy.
+// Object does not use any virtual functions to avoid the
+// allocation of the C++ vtable.
+// Since Smi and Failure are subclasses of Object no
+// data members can be present in Object.
+class Object : public MaybeObject {
+ public:
+ // Type testing.
+ bool IsObject() { return true; }
+
+#define IS_TYPE_FUNCTION_DECL(type_) inline bool Is##type_();
+ OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL)
+ HEAP_OBJECT_TYPE_LIST(IS_TYPE_FUNCTION_DECL)
+#undef IS_TYPE_FUNCTION_DECL
+
+ inline bool IsFixedArrayBase();
+ inline bool IsExternal();
+ inline bool IsAccessorInfo();
+
+ // Returns true if this object is an instance of the specified
+ // function template.
+ inline bool IsInstanceOf(FunctionTemplateInfo* type);
+
+ inline bool IsStruct();
+#define DECLARE_STRUCT_PREDICATE(NAME, Name, name) inline bool Is##Name();
+ STRUCT_LIST(DECLARE_STRUCT_PREDICATE)
+#undef DECLARE_STRUCT_PREDICATE
+
+ INLINE(bool IsSpecObject());
+ INLINE(bool IsSpecFunction());
+
+ // Oddball testing.
+ INLINE(bool IsUndefined());
+ INLINE(bool IsNull());
+ INLINE(bool IsTheHole()); // Shadows MaybeObject's implementation.
+ INLINE(bool IsUninitialized());
+ 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();
+ inline bool IsNaN();
+ bool ToInt32(int32_t* value);
+ bool ToUint32(uint32_t* value);
+
+ // Indicates whether OptimalRepresentation can do its work, or whether it
+ // always has to return Representation::Tagged().
+ enum ValueType {
+ OPTIMAL_REPRESENTATION,
+ FORCE_TAGGED
+ };
+
+ inline Representation OptimalRepresentation(
+ ValueType type = OPTIMAL_REPRESENTATION) {
+ if (!FLAG_track_fields) return Representation::Tagged();
+ if (type == FORCE_TAGGED) return Representation::Tagged();
+ if (IsSmi()) {
+ return Representation::Smi();
+ } else if (FLAG_track_double_fields && IsHeapNumber()) {
+ return Representation::Double();
+ } else if (FLAG_track_computed_fields && IsUninitialized()) {
+ return Representation::None();
+ } else if (FLAG_track_heap_object_fields) {
+ ASSERT(IsHeapObject());
+ return Representation::HeapObject();
+ } else {
+ return Representation::Tagged();
+ }
+ }
+
+ inline bool FitsRepresentation(Representation representation) {
+ if (FLAG_track_fields && representation.IsNone()) {
+ return false;
+ } else if (FLAG_track_fields && representation.IsSmi()) {
+ return IsSmi();
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ return IsNumber();
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ return IsHeapObject();
+ }
+ return true;
+ }
+
+ inline MaybeObject* AllocateNewStorageFor(Heap* heap,
+ Representation representation,
+ PretenureFlag tenure = NOT_TENURED);
+
+ // Returns true if the object is of the correct type to be used as a
+ // implementation of a JSObject's elements.
+ inline bool HasValidElements();
+
+ inline bool HasSpecificClassOf(String* name);
+
+ MUST_USE_RESULT MaybeObject* ToObject(); // ECMA-262 9.9.
+ bool BooleanValue(); // ECMA-262 9.2.
+
+ // Convert to a JSObject if needed.
+ // native_context is used when creating wrapper object.
+ MUST_USE_RESULT MaybeObject* ToObject(Context* native_context);
+
+ // Converts this to a Smi if possible.
+ // Failure is returned otherwise.
+ MUST_USE_RESULT inline MaybeObject* ToSmi();
+
+ void Lookup(Name* name, LookupResult* result);
+
+ // Property access.
+ MUST_USE_RESULT inline MaybeObject* GetProperty(Name* key);
+ MUST_USE_RESULT inline MaybeObject* GetProperty(
+ Name* key,
+ PropertyAttributes* attributes);
+ MUST_USE_RESULT MaybeObject* GetPropertyWithReceiver(
+ Object* receiver,
+ Name* key,
+ PropertyAttributes* attributes);
+
+ static Handle<Object> GetProperty(Handle<Object> object, Handle<Name> key);
+ static Handle<Object> GetProperty(Handle<Object> object,
+ Handle<Object> receiver,
+ LookupResult* result,
+ Handle<Name> key,
+ PropertyAttributes* attributes);
+
+ MUST_USE_RESULT static MaybeObject* GetPropertyOrFail(
+ Handle<Object> object,
+ Handle<Object> receiver,
+ LookupResult* result,
+ Handle<Name> key,
+ PropertyAttributes* attributes);
+
+ MUST_USE_RESULT MaybeObject* GetProperty(Object* receiver,
+ LookupResult* result,
+ Name* key,
+ PropertyAttributes* attributes);
+
+ MUST_USE_RESULT MaybeObject* GetPropertyWithDefinedGetter(Object* receiver,
+ JSReceiver* getter);
+
+ static Handle<Object> GetElement(Handle<Object> object, uint32_t index);
+ MUST_USE_RESULT inline MaybeObject* GetElement(uint32_t index);
+ // For use when we know that no exception can be thrown.
+ inline Object* GetElementNoExceptionThrown(uint32_t index);
+ MUST_USE_RESULT MaybeObject* GetElementWithReceiver(Object* receiver,
+ uint32_t index);
+
+ // Return the object's prototype (might be Heap::null_value()).
+ Object* GetPrototype(Isolate* isolate);
+
+ // 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);
+
+ // Returns true if this is a JSValue containing a string and the index is
+ // < the length of the string. Used to implement [] on strings.
+ inline bool IsStringObjectWithCharacterAt(uint32_t index);
+
+#ifdef VERIFY_HEAP
+ // Verify a pointer is a valid object pointer.
+ static void VerifyPointer(Object* p);
+#endif
+
+ inline void VerifyApiCallResultType();
+
+ // Prints this object without details.
+ inline void ShortPrint() {
+ ShortPrint(stdout);
+ }
+ void ShortPrint(FILE* out);
+
+ // Prints this object without details to a message accumulator.
+ void ShortPrint(StringStream* accumulator);
+
+ // Casting: This cast is only needed to satisfy macros in objects-inl.h.
+ static Object* cast(Object* value) { return value; }
+
+ // Layout description.
+ static const int kHeaderSize = 0; // Object does not take up any space.
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Object);
+};
+
+
+// Smi represents integer Numbers that can be stored in 31 bits.
+// Smis are immediate which means they are NOT allocated in the heap.
+// The this pointer has the following format: [31 bit signed int] 0
+// For long smis it has the following format:
+// [32 bit signed int] [31 bits zero padding] 0
+// Smi stands for small integer.
+class Smi: public Object {
+ public:
+ // Returns the integer value.
+ inline int value();
+
+ // Convert a value to a Smi object.
+ static inline Smi* FromInt(int value);
+
+ static inline Smi* FromIntptr(intptr_t value);
+
+ // Returns whether value can be represented in a Smi.
+ static inline bool IsValid(intptr_t value);
+
+ // Casting.
+ static inline Smi* cast(Object* object);
+
+ // Dispatched behavior.
+ inline void SmiPrint() {
+ SmiPrint(stdout);
+ }
+ void SmiPrint(FILE* out);
+ void SmiPrint(StringStream* accumulator);
+
+ DECLARE_VERIFIER(Smi)
+
+ static const int kMinValue =
+ (static_cast<unsigned int>(-1)) << (kSmiValueSize - 1);
+ static const int kMaxValue = -(kMinValue + 1);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Smi);
+};
+
+
+// Failure is used for reporting out of memory situations and
+// propagating exceptions through the runtime system. Failure objects
+// are transient and cannot occur as part of the object graph.
+//
+// Failures are a single word, encoded as follows:
+// +-------------------------+---+--+--+
+// |.........unused..........|sss|tt|11|
+// +-------------------------+---+--+--+
+// 7 6 4 32 10
+//
+//
+// The low two bits, 0-1, are the failure tag, 11. The next two bits,
+// 2-3, are a failure type tag 'tt' with possible values:
+// 00 RETRY_AFTER_GC
+// 01 EXCEPTION
+// 10 INTERNAL_ERROR
+// 11 OUT_OF_MEMORY_EXCEPTION
+//
+// The next three bits, 4-6, are an allocation space tag 'sss'. The
+// allocation space tag is 000 for all failure types except
+// RETRY_AFTER_GC. For RETRY_AFTER_GC, the possible values are the
+// allocation spaces (the encoding is found in globals.h).
+
+// Failure type tag info.
+const int kFailureTypeTagSize = 2;
+const int kFailureTypeTagMask = (1 << kFailureTypeTagSize) - 1;
+
+class Failure: public MaybeObject {
+ public:
+ // RuntimeStubs assumes EXCEPTION = 1 in the compiler-generated code.
+ enum Type {
+ RETRY_AFTER_GC = 0,
+ EXCEPTION = 1, // Returning this marker tells the real exception
+ // is in Isolate::pending_exception.
+ INTERNAL_ERROR = 2,
+ OUT_OF_MEMORY_EXCEPTION = 3
+ };
+
+ inline Type type() const;
+
+ // Returns the space that needs to be collected for RetryAfterGC failures.
+ inline AllocationSpace allocation_space() const;
+
+ inline bool IsInternalError() const;
+ inline bool IsOutOfMemoryException() const;
+
+ static inline Failure* RetryAfterGC(AllocationSpace space);
+ static inline Failure* RetryAfterGC(); // NEW_SPACE
+ static inline Failure* Exception();
+ static inline Failure* InternalError();
+ // TODO(jkummerow): The value is temporary instrumentation. Remove it
+ // when it has served its purpose.
+ static inline Failure* OutOfMemoryException(intptr_t value);
+ // Casting.
+ static inline Failure* cast(MaybeObject* object);
+
+ // Dispatched behavior.
+ inline void FailurePrint() {
+ FailurePrint(stdout);
+ }
+ void FailurePrint(FILE* out);
+ void FailurePrint(StringStream* accumulator);
+
+ DECLARE_VERIFIER(Failure)
+
+ private:
+ inline intptr_t value() const;
+ static inline Failure* Construct(Type type, intptr_t value = 0);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Failure);
+};
+
+
+// Heap objects typically have a map pointer in their first word. However,
+// 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 {
+ public:
+ // Normal state: the map word contains a map pointer.
+
+ // Create a map word from a map pointer.
+ static inline MapWord FromMap(Map* map);
+
+ // View this map word as a map pointer.
+ inline Map* ToMap();
+
+
+ // Scavenge collection: the map word of live objects in the from space
+ // contains a forwarding address (a heap object pointer in the to space).
+
+ // 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, i.e. not during a full GC).
+ inline bool IsForwardingAddress();
+
+ // Create a map word from a forwarding address.
+ static inline MapWord FromForwardingAddress(HeapObject* object);
+
+ // View this map word as a forwarding address.
+ inline HeapObject* ToForwardingAddress();
+
+ static inline MapWord FromRawValue(uintptr_t value) {
+ return MapWord(value);
+ }
+
+ inline uintptr_t ToRawValue() {
+ return value_;
+ }
+
+ private:
+ // HeapObject calls the private constructor and directly reads the value.
+ friend class HeapObject;
+
+ explicit MapWord(uintptr_t value) : value_(value) {}
+
+ uintptr_t value_;
+};
+
+
+// HeapObject is the superclass for all classes describing heap allocated
+// objects.
+class HeapObject: public Object {
+ public:
+ // [map]: Contains a map which contains the object's reflective
+ // 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.
+ inline MapWord map_word();
+ inline void set_map_word(MapWord map_word);
+
+ // The Heap the object was allocated in. Used also to access Isolate.
+ 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.
+ inline Isolate* GetIsolate();
+
+ // Converts an address to a HeapObject pointer.
+ static inline HeapObject* FromAddress(Address address);
+
+ // Returns the address of this HeapObject.
+ inline Address address();
+
+ // Iterates over pointers contained in the object (including the Map)
+ void Iterate(ObjectVisitor* v);
+
+ // Iterates over all pointers contained in the object except the
+ // first map pointer. The object type is given in the first
+ // parameter. This function does not access the map pointer in the
+ // object, and so is safe to call while the map pointer is modified.
+ void IterateBody(InstanceType type, int object_size, ObjectVisitor* v);
+
+ // Returns the heap object's size in bytes
+ inline int Size();
+
+ // Given a heap object's map pointer, returns the heap size in bytes
+ // Useful when the map pointer field is used for other purposes.
+ // GC internal.
+ inline int SizeFromMap(Map* map);
+
+ // 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
+ // during marking GC.
+ static inline Object** RawField(HeapObject* obj, int offset);
+
+ // Casting.
+ static inline HeapObject* cast(Object* obj);
+
+ // Return the write barrier mode for this. Callers of this function
+ // must be able to present a reference to an DisallowHeapAllocation
+ // object as a sign that they are not going to use this function
+ // from code that allocates and thus invalidates the returned write
+ // barrier mode.
+ inline WriteBarrierMode GetWriteBarrierMode(
+ const DisallowHeapAllocation& promise);
+
+ // Dispatched behavior.
+ void HeapObjectShortPrint(StringStream* accumulator);
+#ifdef OBJECT_PRINT
+ inline void HeapObjectPrint() {
+ HeapObjectPrint(stdout);
+ }
+ void HeapObjectPrint(FILE* out);
+ void PrintHeader(FILE* out, const char* id);
+#endif
+ DECLARE_VERIFIER(HeapObject)
+#ifdef VERIFY_HEAP
+ inline void VerifyObjectField(int offset);
+ inline void VerifySmiField(int offset);
+
+ // Verify a pointer is a valid HeapObject pointer that points to object
+ // areas in the heap.
+ static void VerifyHeapPointer(Object* p);
+#endif
+
+ // Layout description.
+ // First field in a heap object is map.
+ static const int kMapOffset = Object::kHeaderSize;
+ static const int kHeaderSize = kMapOffset + kPointerSize;
+
+ STATIC_CHECK(kMapOffset == Internals::kHeapObjectMapOffset);
+
+ protected:
+ // helpers for calling an ObjectVisitor to iterate over pointers in the
+ // half-open range [start, end) specified as integer offsets
+ inline void IteratePointers(ObjectVisitor* v, int start, int end);
+ // as above, for the single element at "offset"
+ inline void IteratePointer(ObjectVisitor* v, int offset);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(HeapObject);
+};
+
+
+// This class describes a body of an object of a fixed size
+// in which all pointer fields are located in the [start_offset, end_offset)
+// interval.
+template<int start_offset, int end_offset, int size>
+class FixedBodyDescriptor {
+ public:
+ static const int kStartOffset = start_offset;
+ static const int kEndOffset = end_offset;
+ static const int kSize = size;
+
+ static inline void IterateBody(HeapObject* obj, ObjectVisitor* v);
+
+ template<typename StaticVisitor>
+ static inline void IterateBody(HeapObject* obj) {
+ StaticVisitor::VisitPointers(HeapObject::RawField(obj, start_offset),
+ HeapObject::RawField(obj, end_offset));
+ }
+};
+
+
+// This class describes a body of an object of a variable size
+// in which all pointer fields are located in the [start_offset, object_size)
+// interval.
+template<int start_offset>
+class FlexibleBodyDescriptor {
+ public:
+ static const int kStartOffset = start_offset;
+
+ static inline void IterateBody(HeapObject* obj,
+ int object_size,
+ ObjectVisitor* v);
+
+ template<typename StaticVisitor>
+ static inline void IterateBody(HeapObject* obj, int object_size) {
+ StaticVisitor::VisitPointers(HeapObject::RawField(obj, start_offset),
+ HeapObject::RawField(obj, object_size));
+ }
+};
+
+
+// The HeapNumber class describes heap allocated numbers that cannot be
+// represented in a Smi (small integer)
+class HeapNumber: public HeapObject {
+ public:
+ // [value]: number value.
+ inline double value();
+ inline void set_value(double value);
+
+ // Casting.
+ static inline HeapNumber* cast(Object* obj);
+
+ // Dispatched behavior.
+ bool HeapNumberBooleanValue();
+
+ inline void HeapNumberPrint() {
+ HeapNumberPrint(stdout);
+ }
+ void HeapNumberPrint(FILE* out);
+ void HeapNumberPrint(StringStream* accumulator);
+ DECLARE_VERIFIER(HeapNumber)
+
+ inline int get_exponent();
+ inline int get_sign();
+
+ // Layout description.
+ static const int kValueOffset = HeapObject::kHeaderSize;
+ // IEEE doubles are two 32 bit words. The first is just mantissa, the second
+ // is a mixture of sign, exponent and mantissa. Our current platforms are all
+ // little endian apart from non-EABI arm which is little endian with big
+ // endian floating point word ordering!
+ static const int kMantissaOffset = kValueOffset;
+ static const int kExponentOffset = kValueOffset + 4;
+
+ static const int kSize = kValueOffset + kDoubleSize;
+ static const uint32_t kSignMask = 0x80000000u;
+ static const uint32_t kExponentMask = 0x7ff00000u;
+ static const uint32_t kMantissaMask = 0xfffffu;
+ static const int kMantissaBits = 52;
+ static const int kExponentBits = 11;
+ static const int kExponentBias = 1023;
+ static const int kExponentShift = 20;
+ static const int kInfinityOrNanExponent =
+ (kExponentMask >> kExponentShift) - kExponentBias;
+ static const int kMantissaBitsInTopWord = 20;
+ static const int kNonMantissaBitsInTopWord = 12;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(HeapNumber);
+};
+
+
+enum EnsureElementsMode {
+ DONT_ALLOW_DOUBLE_ELEMENTS,
+ ALLOW_COPIED_DOUBLE_ELEMENTS,
+ ALLOW_CONVERTED_DOUBLE_ELEMENTS
+};
+
+
+// Indicates whether a property should be set or (re)defined. Setting of a
+// property causes attributes to remain unchanged, writability to be checked
+// and callbacks to be called. Defining of a property causes attributes to
+// be updated and callbacks to be overridden.
+enum SetPropertyMode {
+ SET_PROPERTY,
+ DEFINE_PROPERTY
+};
+
+
+// Indicator for one component of an AccessorPair.
+enum AccessorComponent {
+ ACCESSOR_GETTER,
+ ACCESSOR_SETTER
+};
+
+
+// JSReceiver includes types on which properties can be defined, i.e.,
+// JSObject and JSProxy.
+class JSReceiver: public HeapObject {
+ public:
+ enum DeleteMode {
+ NORMAL_DELETION,
+ STRICT_DELETION,
+ FORCE_DELETION
+ };
+
+ // A non-keyed store is of the form a.x = foo or a["x"] = foo whereas
+ // a keyed store is of the form a[expression] = foo.
+ enum StoreFromKeyed {
+ MAY_BE_STORE_FROM_KEYED,
+ CERTAINLY_NOT_STORE_FROM_KEYED
+ };
+
+ // Indicates whether a value can be loaded as a constant.
+ enum StoreMode {
+ ALLOW_AS_CONSTANT,
+ FORCE_FIELD
+ };
+
+ // Internal properties (e.g. the hidden properties dictionary) might
+ // be added even though the receiver is non-extensible.
+ enum ExtensibilityCheck {
+ PERFORM_EXTENSIBILITY_CHECK,
+ OMIT_EXTENSIBILITY_CHECK
+ };
+
+ // Casting.
+ static inline JSReceiver* cast(Object* obj);
+
+ static Handle<Object> SetProperty(Handle<JSReceiver> object,
+ Handle<Name> key,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode);
+
+ MUST_USE_RESULT static MaybeObject* SetPropertyOrFail(
+ Handle<JSReceiver> object,
+ Handle<Name> key,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ StoreFromKeyed store_from_keyed = MAY_BE_STORE_FROM_KEYED);
+
+ // Can cause GC.
+ MUST_USE_RESULT MaybeObject* SetProperty(
+ Name* key,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ StoreFromKeyed store_from_keyed = MAY_BE_STORE_FROM_KEYED);
+ MUST_USE_RESULT MaybeObject* SetProperty(
+ LookupResult* result,
+ Name* key,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ StoreFromKeyed store_from_keyed = MAY_BE_STORE_FROM_KEYED);
+ MUST_USE_RESULT MaybeObject* SetPropertyWithDefinedSetter(JSReceiver* setter,
+ Object* value);
+
+ static Handle<Object> DeleteProperty(Handle<JSReceiver> object,
+ Handle<Name> name,
+ DeleteMode mode = NORMAL_DELETION);
+ static Handle<Object> DeleteElement(Handle<JSReceiver> object,
+ 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,
+ PropertyAttributes attributes,
+ 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();
+
+ // Returns the constructor name (the name (possibly, inferred name) of the
+ // function that was used to instantiate the object).
+ String* constructor_name();
+
+ inline PropertyAttributes GetPropertyAttribute(Name* name);
+ PropertyAttributes GetPropertyAttributeWithReceiver(JSReceiver* receiver,
+ Name* name);
+ PropertyAttributes GetLocalPropertyAttribute(Name* name);
+
+ inline PropertyAttributes GetElementAttribute(uint32_t index);
+ inline PropertyAttributes GetLocalElementAttribute(uint32_t index);
+
+ // Can cause a GC.
+ inline bool HasProperty(Name* name);
+ inline bool HasLocalProperty(Name* name);
+ inline bool HasElement(uint32_t index);
+ inline bool HasLocalElement(uint32_t index);
+
+ // Return the object's prototype (might be Heap::null_value()).
+ inline Object* GetPrototype();
+
+ // Return the constructor function (may be Heap::null_value()).
+ inline Object* GetConstructor();
+
+ // 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(Name* name, LookupResult* result,
+ bool search_hidden_prototypes = false);
+ void Lookup(Name* name, LookupResult* result);
+
+ protected:
+ Smi* GenerateIdentityHash();
+
+ private:
+ PropertyAttributes GetPropertyAttributeForResult(JSReceiver* receiver,
+ LookupResult* result,
+ Name* name,
+ bool continue_search);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSReceiver);
+};
+
+// The JSObject describes real heap allocated JavaScript objects with
+// properties.
+// Note that the map of JSObject changes during execution to enable inline
+// caching.
+class JSObject: public JSReceiver {
+ public:
+ // [properties]: Backing storage for properties.
+ // properties is a FixedArray in the fast case and a Dictionary in the
+ // slow case.
+ DECL_ACCESSORS(properties, FixedArray) // Get and set fast properties.
+ inline void initialize_properties();
+ inline bool HasFastProperties();
+ inline NameDictionary* property_dictionary(); // Gets slow properties.
+
+ // [elements]: The elements (properties with names that are integers).
+ //
+ // Elements can be in two general modes: fast and slow. Each mode
+ // corrensponds to a set of object representations of elements that
+ // have something in common.
+ //
+ // In the fast mode elements is a FixedArray and so each element can
+ // be quickly accessed. This fact is used in the generated code. The
+ // elements array can have one of three maps in this mode:
+ // fixed_array_map, non_strict_arguments_elements_map or
+ // fixed_cow_array_map (for copy-on-write arrays). In the latter case
+ // the elements array may be shared by a few objects and so before
+ // writing to any element the array must be copied. Use
+ // EnsureWritableFastElements in this case.
+ //
+ // In the slow mode the elements is either a NumberDictionary, an
+ // ExternalArray, or a FixedArray parameter map for a (non-strict)
+ // arguments object.
+ DECL_ACCESSORS(elements, FixedArrayBase)
+ inline void initialize_elements();
+ MUST_USE_RESULT inline MaybeObject* ResetElements();
+ inline ElementsKind GetElementsKind();
+ inline ElementsAccessor* GetElementsAccessor();
+ // Returns true if an object has elements of FAST_SMI_ELEMENTS ElementsKind.
+ inline bool HasFastSmiElements();
+ // Returns true if an object has elements of FAST_ELEMENTS ElementsKind.
+ inline bool HasFastObjectElements();
+ // Returns true if an object has elements of FAST_ELEMENTS or
+ // FAST_SMI_ONLY_ELEMENTS.
+ inline bool HasFastSmiOrObjectElements();
+ // Returns true if an object has any of the fast elements kinds.
+ inline bool HasFastElements();
+ // Returns true if an object has elements of FAST_DOUBLE_ELEMENTS
+ // ElementsKind.
+ inline bool HasFastDoubleElements();
+ // Returns true if an object has elements of FAST_HOLEY_*_ELEMENTS
+ // ElementsKind.
+ inline bool HasFastHoleyElements();
+ inline bool HasNonStrictArgumentsElements();
+ inline bool HasDictionaryElements();
+ inline bool HasExternalPixelElements();
+ inline bool HasExternalArrayElements();
+ inline bool HasExternalByteElements();
+ inline bool HasExternalUnsignedByteElements();
+ inline bool HasExternalShortElements();
+ inline bool HasExternalUnsignedShortElements();
+ inline bool HasExternalIntElements();
+ inline bool HasExternalUnsignedIntElements();
+ inline bool HasExternalFloatElements();
+ inline bool HasExternalDoubleElements();
+ bool HasFastArgumentsElements();
+ bool HasDictionaryArgumentsElements();
+ inline SeededNumberDictionary* element_dictionary(); // Gets slow elements.
+
+ inline bool ShouldTrackAllocationInfo();
+
+ inline void set_map_and_elements(
+ Map* map,
+ FixedArrayBase* value,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+
+ // Requires: HasFastElements().
+ MUST_USE_RESULT inline MaybeObject* EnsureWritableFastElements();
+
+ // Collects elements starting at index 0.
+ // Undefined values are placed after non-undefined values.
+ // Returns the number of non-undefined values.
+ MUST_USE_RESULT MaybeObject* PrepareElementsForSort(uint32_t limit);
+ // As PrepareElementsForSort, but only on objects where elements is
+ // 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,
+ Name* name);
+
+ // Can cause GC.
+ MUST_USE_RESULT MaybeObject* SetPropertyForResult(LookupResult* result,
+ Name* key,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ StoreFromKeyed store_mode);
+ MUST_USE_RESULT MaybeObject* SetPropertyWithFailedAccessCheck(
+ LookupResult* result,
+ Name* name,
+ Object* value,
+ bool check_prototype,
+ StrictModeFlag strict_mode);
+ MUST_USE_RESULT MaybeObject* SetPropertyWithCallback(
+ Object* structure,
+ Name* name,
+ Object* value,
+ JSObject* holder,
+ StrictModeFlag strict_mode);
+ MUST_USE_RESULT MaybeObject* SetPropertyWithInterceptor(
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode);
+ MUST_USE_RESULT MaybeObject* SetPropertyPostInterceptor(
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ ExtensibilityCheck extensibility_check,
+ StoreMode mode = ALLOW_AS_CONSTANT);
+
+ static Handle<Object> SetLocalPropertyIgnoreAttributes(
+ Handle<JSObject> object,
+ Handle<Name> key,
+ Handle<Object> value,
+ PropertyAttributes attributes,
+ ValueType value_type = OPTIMAL_REPRESENTATION,
+ StoreMode mode = ALLOW_AS_CONSTANT);
+
+ static inline Handle<String> ExpectedTransitionKey(Handle<Map> map);
+ static inline Handle<Map> ExpectedTransitionTarget(Handle<Map> map);
+
+ // Try to follow an existing transition to a field with attributes NONE. The
+ // return value indicates whether the transition was successful.
+ static inline Handle<Map> FindTransitionToField(Handle<Map> map,
+ Handle<Name> key);
+
+ inline int LastAddedFieldIndex();
+
+ // Extend the receiver with a single fast property appeared first in the
+ // passed map. This also extends the property backing store if necessary.
+ static void AllocateStorageForMap(Handle<JSObject> object, Handle<Map> map);
+ inline MUST_USE_RESULT MaybeObject* AllocateStorageForMap(Map* map);
+
+ static void MigrateInstance(Handle<JSObject> instance);
+ inline MUST_USE_RESULT MaybeObject* MigrateInstance();
+
+ static Handle<Object> TryMigrateInstance(Handle<JSObject> instance);
+ inline MUST_USE_RESULT MaybeObject* TryMigrateInstance();
+
+ // Can cause GC.
+ MUST_USE_RESULT MaybeObject* SetLocalPropertyIgnoreAttributes(
+ Name* key,
+ Object* value,
+ PropertyAttributes attributes,
+ ValueType value_type = OPTIMAL_REPRESENTATION,
+ StoreMode mode = ALLOW_AS_CONSTANT);
+
+ // Retrieve a value in a normalized object given a lookup result.
+ // Handles the special representation of JS global objects.
+ Object* GetNormalizedProperty(LookupResult* result);
+
+ // Sets the property value in a normalized object given (key, value).
+ // Handles the special representation of JS global objects.
+ static Handle<Object> SetNormalizedProperty(Handle<JSObject> object,
+ LookupResult* result,
+ Handle<Object> value);
+
+ // Sets the property value in a normalized object given a lookup result.
+ // Handles the special representation of JS global objects.
+ MUST_USE_RESULT MaybeObject* SetNormalizedProperty(LookupResult* result,
+ Object* value);
+
+ // 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<Name> key,
+ Handle<Object> value,
+ PropertyDetails details);
+
+ MUST_USE_RESULT MaybeObject* SetNormalizedProperty(Name* name,
+ Object* value,
+ PropertyDetails details);
+
+ static void OptimizeAsPrototype(Handle<JSObject> object);
+ MUST_USE_RESULT MaybeObject* OptimizeAsPrototype();
+
+ // Retrieve interceptors.
+ InterceptorInfo* GetNamedInterceptor();
+ InterceptorInfo* GetIndexedInterceptor();
+
+ // Used from JSReceiver.
+ PropertyAttributes GetPropertyAttributePostInterceptor(JSObject* receiver,
+ Name* name,
+ bool continue_search);
+ PropertyAttributes GetPropertyAttributeWithInterceptor(JSObject* receiver,
+ Name* name,
+ bool continue_search);
+ PropertyAttributes GetPropertyAttributeWithFailedAccessCheck(
+ Object* receiver,
+ LookupResult* result,
+ Name* name,
+ bool continue_search);
+ PropertyAttributes GetElementAttributeWithReceiver(JSReceiver* receiver,
+ uint32_t index,
+ bool continue_search);
+
+ static void DefineAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes);
+
+ MaybeObject* LookupAccessor(Name* name, AccessorComponent component);
+
+ MUST_USE_RESULT MaybeObject* DefineAccessor(AccessorInfo* info);
+
+ // Used from Object::GetProperty().
+ MUST_USE_RESULT MaybeObject* GetPropertyWithFailedAccessCheck(
+ Object* receiver,
+ LookupResult* result,
+ Name* name,
+ PropertyAttributes* attributes);
+ MUST_USE_RESULT MaybeObject* GetPropertyWithInterceptor(
+ Object* receiver,
+ Name* name,
+ PropertyAttributes* attributes);
+ MUST_USE_RESULT MaybeObject* GetPropertyPostInterceptor(
+ Object* receiver,
+ Name* name,
+ PropertyAttributes* attributes);
+ MUST_USE_RESULT MaybeObject* GetLocalPropertyPostInterceptor(
+ Object* receiver,
+ Name* name,
+ PropertyAttributes* attributes);
+
+ // Returns true if this is an instance of an api function and has
+ // been modified since it was created. May give false positives.
+ bool IsDirty();
+
+ // If the receiver is a JSGlobalProxy this method will return its prototype,
+ // otherwise the result is the receiver itself.
+ inline Object* BypassGlobalProxy();
+
+ // Accessors for hidden properties object.
+ //
+ // Hidden properties are not local properties of the object itself.
+ // Instead they are stored in an auxiliary structure kept as a local
+ // property with a special name Heap::hidden_string(). But if the
+ // receiver is a JSGlobalProxy then the auxiliary object is a property
+ // 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<Name> key,
+ Handle<Object> value);
+ // Returns a failure if a GC is required.
+ MUST_USE_RESULT MaybeObject* SetHiddenProperty(Name* key, Object* value);
+ // Gets the value of a hidden property with the given key. Returns the hole
+ // if the property doesn't exist (or if called on a detached proxy),
+ // otherwise returns the value set for the key.
+ Object* GetHiddenProperty(Name* key);
+ // Deletes a hidden property. Deleting a non-existing property is
+ // considered successful.
+ void DeleteHiddenProperty(Name* key);
+ // Returns true if the object has a property with the hidden string as name.
+ bool HasHiddenProperties();
+
+ static int GetIdentityHash(Handle<JSObject> obj);
+ MUST_USE_RESULT MaybeObject* GetIdentityHash(CreationFlag flag);
+ MUST_USE_RESULT MaybeObject* SetIdentityHash(Smi* hash, CreationFlag flag);
+
+ static Handle<Object> DeleteElement(Handle<JSObject> obj,
+ uint32_t index,
+ DeleteMode mode = NORMAL_DELETION);
+ MUST_USE_RESULT MaybeObject* DeleteElement(uint32_t index, DeleteMode mode);
+
+ inline void ValidateElements();
+
+ // Makes sure that this object can contain HeapObject as elements.
+ MUST_USE_RESULT inline MaybeObject* EnsureCanContainHeapObjectElements();
+
+ // Makes sure that this object can contain the specified elements.
+ MUST_USE_RESULT inline MaybeObject* EnsureCanContainElements(
+ Object** elements,
+ uint32_t count,
+ EnsureElementsMode mode);
+ MUST_USE_RESULT inline MaybeObject* EnsureCanContainElements(
+ FixedArrayBase* elements,
+ uint32_t length,
+ EnsureElementsMode mode);
+ MUST_USE_RESULT 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?
+ bool ShouldConvertToSlowElements(int new_capacity);
+ // Returns true if the backing storage for the slow-case elements of
+ // this object takes up nearly as much space as a fast-case backing
+ // storage would. In that case the JSObject should have fast
+ // elements.
+ bool ShouldConvertToFastElements();
+ // Returns true if the elements of JSObject contains only values that can be
+ // 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);
+
+ // Computes the new capacity when expanding the elements of a JSObject.
+ static int NewElementsCapacity(int old_capacity) {
+ // (old_capacity + 50%) + 16
+ return old_capacity + (old_capacity >> 1) + 16;
+ }
+
+ PropertyType GetLocalPropertyType(Name* name);
+ PropertyType GetLocalElementType(uint32_t index);
+
+ // These methods do not perform access checks!
+ AccessorPair* GetLocalPropertyAccessorPair(Name* name);
+ AccessorPair* GetLocalElementAccessorPair(uint32_t index);
+
+ MUST_USE_RESULT MaybeObject* SetFastElement(uint32_t index,
+ Object* value,
+ StrictModeFlag strict_mode,
+ bool check_prototype);
+
+ MUST_USE_RESULT MaybeObject* SetDictionaryElement(
+ uint32_t index,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool check_prototype,
+ SetPropertyMode set_mode = SET_PROPERTY);
+
+ MUST_USE_RESULT MaybeObject* SetFastDoubleElement(
+ uint32_t index,
+ Object* value,
+ StrictModeFlag strict_mode,
+ bool check_prototype = true);
+
+ 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 Handle<Object> SetElement(
+ Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> value,
+ PropertyAttributes attr,
+ StrictModeFlag strict_mode,
+ SetPropertyMode set_mode = SET_PROPERTY);
+
+ // A Failure object is returned if GC is needed.
+ MUST_USE_RESULT MaybeObject* SetElement(
+ uint32_t index,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool check_prototype = true,
+ SetPropertyMode set_mode = SET_PROPERTY);
+
+ // Returns the index'th element.
+ // The undefined object if index is out of bounds.
+ MUST_USE_RESULT MaybeObject* GetElementWithInterceptor(Object* receiver,
+ uint32_t index);
+
+ enum SetFastElementsCapacitySmiMode {
+ kAllowSmiElements,
+ kForceSmiElements,
+ kDontAllowSmiElements
+ };
+
+ // 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,
+ SetFastElementsCapacitySmiMode smi_mode);
+ MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength(
+ int capacity,
+ int length);
+
+ // Lookup interceptors are used for handling properties controlled by host
+ // objects.
+ inline bool HasNamedInterceptor();
+ inline bool HasIndexedInterceptor();
+
+ // Support functions for v8 api (needed for correct interceptor behavior).
+ bool HasRealNamedProperty(Isolate* isolate, Name* key);
+ bool HasRealElementProperty(Isolate* isolate, uint32_t index);
+ bool HasRealNamedCallbackProperty(Isolate* isolate, Name* key);
+
+ // 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();
+
+ inline int GetInternalFieldCount();
+ inline int GetInternalFieldOffset(int index);
+ inline Object* GetInternalField(int index);
+ inline void SetInternalField(int index, Object* value);
+ inline void SetInternalField(int index, Smi* value);
+
+ // The following lookup functions skip interceptors.
+ void LocalLookupRealNamedProperty(Name* name, LookupResult* result);
+ void LookupRealNamedProperty(Name* name, LookupResult* result);
+ void LookupRealNamedPropertyInPrototypes(Name* name, LookupResult* result);
+ MUST_USE_RESULT MaybeObject* SetElementWithCallbackSetterInPrototypes(
+ uint32_t index, Object* value, bool* found, StrictModeFlag strict_mode);
+ void LookupCallbackProperty(Name* name, LookupResult* result);
+
+ // Returns the number of properties on this object filtering out properties
+ // with the specified attributes (ignoring interceptors).
+ int NumberOfLocalProperties(PropertyAttributes filter = NONE);
+ // Fill in details for properties into storage starting at the specified
+ // index.
+ void GetLocalPropertyNames(
+ FixedArray* storage, int index, PropertyAttributes filter = NONE);
+
+ // Returns the number of properties on this object filtering out properties
+ // with the specified attributes (ignoring interceptors).
+ int NumberOfLocalElements(PropertyAttributes filter);
+ // Returns the number of enumerable elements (ignoring interceptors).
+ int NumberOfEnumElements();
+ // Returns the number of elements on this object filtering out elements
+ // with the specified attributes (ignoring interceptors).
+ int GetLocalElementKeys(FixedArray* storage, PropertyAttributes filter);
+ // Count and fill in the enumerable elements into storage.
+ // (storage->length() == NumberOfEnumElements()).
+ // If storage is NULL, will count the elements without adding
+ // them to any storage.
+ // Returns the number of enumerable elements.
+ int GetEnumElementKeys(FixedArray* storage);
+
+ // Add a property to a fast-case object using a map transition to
+ // new_map.
+ MUST_USE_RESULT MaybeObject* AddFastPropertyUsingMap(
+ Map* new_map,
+ Name* name,
+ Object* value,
+ int field_index,
+ Representation representation);
+
+ // Add a constant function property to a fast-case object.
+ // This leaves a CONSTANT_TRANSITION in the old map, and
+ // if it is called on a second object with this map, a
+ // normal property is added instead, with a map transition.
+ // This avoids the creation of many maps with the same constant
+ // function, all orphaned.
+ MUST_USE_RESULT MaybeObject* AddConstantProperty(
+ Name* name,
+ Object* constant,
+ PropertyAttributes attributes);
+
+ MUST_USE_RESULT MaybeObject* ReplaceSlowProperty(
+ Name* name,
+ 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);
+ MUST_USE_RESULT MaybeObject* UpdateAllocationSite(ElementsKind to_kind);
+
+ // Replaces an existing transition with a transition to a map with a FIELD.
+ MUST_USE_RESULT MaybeObject* ConvertTransitionToMapTransition(
+ int transition_index,
+ Name* name,
+ Object* new_value,
+ PropertyAttributes attributes);
+
+ // Converts a descriptor of any other type to a real field, backed by the
+ // properties array.
+ MUST_USE_RESULT MaybeObject* ConvertDescriptorToField(
+ Name* name,
+ Object* new_value,
+ PropertyAttributes attributes,
+ TransitionFlag flag = OMIT_TRANSITION);
+
+ MUST_USE_RESULT MaybeObject* MigrateToMap(Map* new_map);
+ MUST_USE_RESULT MaybeObject* GeneralizeFieldRepresentation(
+ int modify_index,
+ Representation new_representation);
+
+ // Add a property to a fast-case object.
+ MUST_USE_RESULT MaybeObject* AddFastProperty(
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StoreFromKeyed store_mode = MAY_BE_STORE_FROM_KEYED,
+ ValueType value_type = OPTIMAL_REPRESENTATION);
+
+ // Add a property to a slow-case object.
+ MUST_USE_RESULT MaybeObject* AddSlowProperty(Name* name,
+ Object* value,
+ PropertyAttributes attributes);
+
+ // Add a property to an object. May cause GC.
+ MUST_USE_RESULT MaybeObject* AddProperty(
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ StoreFromKeyed store_mode = MAY_BE_STORE_FROM_KEYED,
+ ExtensibilityCheck extensibility_check = PERFORM_EXTENSIBILITY_CHECK,
+ ValueType value_type = OPTIMAL_REPRESENTATION,
+ StoreMode mode = ALLOW_AS_CONSTANT);
+
+ // Convert the object to use the canonical dictionary
+ // 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<Name> name,
+ Handle<Code> code);
+
+ MUST_USE_RESULT MaybeObject* UpdateMapCodeCache(Name* 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);
+
+ // Access fast-case object properties at index.
+ MUST_USE_RESULT inline MaybeObject* FastPropertyAt(
+ Representation representation,
+ int index);
+ inline Object* RawFastPropertyAt(int index);
+ inline void FastPropertyAtPut(int index, Object* value);
+
+ // Access to in object properties.
+ inline int GetInObjectPropertyOffset(int index);
+ inline Object* InObjectPropertyAt(int index);
+ inline Object* InObjectPropertyAtPut(int index,
+ Object* value,
+ WriteBarrierMode mode
+ = UPDATE_WRITE_BARRIER);
+
+ // Set the object's prototype (only JSReceiver and null are allowed values).
+ static Handle<Object> SetPrototype(Handle<JSObject> object,
+ Handle<Object> value,
+ bool skip_hidden_prototypes = false);
+
+ // 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);
+
+ // Casting.
+ 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();
+
+ // ES5 Object.freeze
+ MUST_USE_RESULT MaybeObject* Freeze(Isolate* isolate);
+
+
+ // Called the first time an object is observed with ES7 Object.observe.
+ MUST_USE_RESULT MaybeObject* SetObserved(Isolate* isolate);
+
+ // Copy object
+ MUST_USE_RESULT MaybeObject* DeepCopy(Isolate* isolate);
+
+ // Dispatched behavior.
+ void JSObjectShortPrint(StringStream* accumulator);
+ DECLARE_PRINTER(JSObject)
+ DECLARE_VERIFIER(JSObject)
+#ifdef OBJECT_PRINT
+ inline void PrintProperties() {
+ PrintProperties(stdout);
+ }
+ void PrintProperties(FILE* out);
+
+ inline void PrintElements() {
+ PrintElements(stdout);
+ }
+ void PrintElements(FILE* out);
+ inline void PrintTransitions() {
+ PrintTransitions(stdout);
+ }
+ void PrintTransitions(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 {
+ public:
+ void Clear();
+ void Print();
+ int number_of_objects_;
+ int number_of_objects_with_fast_properties_;
+ int number_of_objects_with_fast_elements_;
+ int number_of_fast_used_fields_;
+ int number_of_fast_unused_fields_;
+ int number_of_slow_used_properties_;
+ int number_of_slow_unused_properties_;
+ int number_of_fast_used_elements_;
+ int number_of_fast_unused_elements_;
+ int number_of_slow_used_elements_;
+ int number_of_slow_unused_elements_;
+ };
+
+ void IncrementSpillStatistics(SpillInformation* info);
+#endif
+ Object* SlowReverseLookup(Object* value);
+
+ // Maximal number of fast properties for the JSObject. Used to
+ // restrict the number of map transitions to avoid an explosion in
+ // the number of maps for objects used as dictionaries.
+ inline bool TooManyFastProperties(int properties, StoreFromKeyed store_mode);
+
+ // Maximal number of elements (numbered 0 .. kMaxElementCount - 1).
+ // Also maximal value of JSArray's length property.
+ static const uint32_t kMaxElementCount = 0xffffffffu;
+
+ // Constants for heuristics controlling conversion of fast elements
+ // to slow elements.
+
+ // Maximal gap that can be introduced by adding an element beyond
+ // the current elements length.
+ static const uint32_t kMaxGap = 1024;
+
+ // Maximal length of fast elements array that won't be checked for
+ // being dense enough on expansion.
+ static const int kMaxUncheckedFastElementsLength = 5000;
+
+ // Same as above but for old arrays. This limit is more strict. We
+ // don't want to be wasteful with long lived objects.
+ static const int kMaxUncheckedOldFastElementsLength = 500;
+
+ static const int kInitialMaxFastElementArray = 100000;
+ static const int kFastPropertiesSoftLimit = 12;
+ static const int kMaxFastProperties = 64;
+ static const int kMaxInstanceSize = 255 * kPointerSize;
+ // When extending the backing storage for property values, we increase
+ // its size by more than the 1 entry necessary, so sequentially adding fields
+ // to the same object requires fewer allocations and copies.
+ static const int kFieldsAdded = 3;
+
+ // Layout description.
+ static const int kPropertiesOffset = HeapObject::kHeaderSize;
+ static const int kElementsOffset = kPropertiesOffset + kPointerSize;
+ static const int kHeaderSize = kElementsOffset + kPointerSize;
+
+ STATIC_CHECK(kHeaderSize == Internals::kJSObjectHeaderSize);
+
+ class BodyDescriptor : public FlexibleBodyDescriptor<kPropertiesOffset> {
+ public:
+ static inline int SizeOf(Map* map, HeapObject* object);
+ };
+
+ // Enqueue change record for Object.observe. May cause GC.
+ static void EnqueueChangeRecord(Handle<JSObject> object,
+ const char* type,
+ Handle<Name> name,
+ Handle<Object> old_value);
+
+ // Deliver change records to observers. May cause GC.
+ static void DeliverChangeRecords(Isolate* isolate);
+
+ private:
+ friend class DictionaryElementsAccessor;
+ friend class JSReceiver;
+
+ MUST_USE_RESULT MaybeObject* GetElementWithCallback(Object* receiver,
+ Object* structure,
+ uint32_t index,
+ Object* holder);
+ MUST_USE_RESULT PropertyAttributes GetElementAttributeWithInterceptor(
+ JSReceiver* receiver,
+ uint32_t index,
+ bool continue_search);
+ MUST_USE_RESULT PropertyAttributes GetElementAttributeWithoutInterceptor(
+ JSReceiver* receiver,
+ uint32_t index,
+ bool continue_search);
+ MUST_USE_RESULT MaybeObject* SetElementWithCallback(
+ Object* structure,
+ uint32_t index,
+ Object* value,
+ JSObject* holder,
+ StrictModeFlag strict_mode);
+ MUST_USE_RESULT MaybeObject* SetElementWithInterceptor(
+ uint32_t index,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool check_prototype,
+ SetPropertyMode set_mode);
+ MUST_USE_RESULT MaybeObject* SetElementWithoutInterceptor(
+ uint32_t index,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool check_prototype,
+ SetPropertyMode set_mode);
+
+ // Searches the prototype chain for property 'name'. If it is found and
+ // has a setter, invoke it and set '*done' to true. If it is found and is
+ // read-only, reject and set '*done' to true. Otherwise, set '*done' to
+ // false. Can cause GC and can return a failure result with '*done==true'.
+ MUST_USE_RESULT MaybeObject* SetPropertyViaPrototypes(
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool* done);
+
+ static Handle<Object> DeleteProperty(Handle<JSObject> object,
+ Handle<Name> name,
+ DeleteMode mode);
+ static Handle<Object> DeletePropertyPostInterceptor(Handle<JSObject> object,
+ Handle<Name> name,
+ DeleteMode mode);
+ static Handle<Object> DeletePropertyWithInterceptor(Handle<JSObject> object,
+ Handle<Name> name);
+
+ // Deletes the named property in a normalized object.
+ static Handle<Object> DeleteNormalizedProperty(Handle<JSObject> object,
+ Handle<Name> name,
+ DeleteMode mode);
+
+ MUST_USE_RESULT MaybeObject* DeleteElementWithInterceptor(uint32_t index);
+
+ MUST_USE_RESULT MaybeObject* DeleteFastElement(uint32_t index);
+ MUST_USE_RESULT MaybeObject* DeleteDictionaryElement(uint32_t index,
+ DeleteMode mode);
+
+ bool ReferencesObjectFromElements(FixedArray* elements,
+ ElementsKind kind,
+ Object* object);
+
+ // Returns true if most of the elements backing storage is used.
+ bool HasDenseElements();
+
+ // Gets the current elements capacity and the number of used elements.
+ void GetElementsCapacityAndUsage(int* capacity, int* used);
+
+ bool CanSetCallback(Name* name);
+ MUST_USE_RESULT MaybeObject* SetElementCallback(
+ uint32_t index,
+ Object* structure,
+ PropertyAttributes attributes);
+ MUST_USE_RESULT MaybeObject* SetPropertyCallback(
+ Name* name,
+ Object* structure,
+ PropertyAttributes attributes);
+ static void DefineElementAccessor(Handle<JSObject> object,
+ uint32_t index,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes);
+ static Handle<AccessorPair> CreateAccessorPairFor(Handle<JSObject> object,
+ Handle<Name> name);
+ static void DefinePropertyAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ Handle<Object> getter,
+ Handle<Object> setter,
+ PropertyAttributes attributes);
+
+ // Try to define a single accessor paying attention to map transitions.
+ // Returns false if this was not possible and we have to use the slow case.
+ static bool DefineFastAccessor(Handle<JSObject> object,
+ Handle<Name> name,
+ AccessorComponent component,
+ Handle<Object> accessor,
+ PropertyAttributes attributes);
+
+ enum InitializeHiddenProperties {
+ CREATE_NEW_IF_ABSENT,
+ ONLY_RETURN_INLINE_VALUE
+ };
+
+ // If create_if_absent is true, return the hash table backing store
+ // for hidden properties. If there is no backing store, allocate one.
+ // If create_if_absent is false, return the hash table backing store
+ // or the inline stored identity hash, whatever is found.
+ MUST_USE_RESULT MaybeObject* GetHiddenPropertiesHashTable(
+ InitializeHiddenProperties init_option);
+ // Set the hidden property backing store to either a hash table or
+ // the inline-stored identity hash.
+ MUST_USE_RESULT MaybeObject* SetHiddenPropertiesHashTable(
+ Object* value);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSObject);
+};
+
+
+// Common superclass for FixedArrays that allow implementations to share
+// common accessors and some code paths.
+class FixedArrayBase: public HeapObject {
+ public:
+ // [length]: length of the array.
+ inline int length();
+ inline void set_length(int value);
+
+ inline static FixedArrayBase* cast(Object* object);
+
+ // Layout description.
+ // Length is smi tagged when it is stored.
+ static const int kLengthOffset = HeapObject::kHeaderSize;
+ static const int kHeaderSize = kLengthOffset + kPointerSize;
+};
+
+
+class FixedDoubleArray;
+class IncrementalMarking;
+
+
+// FixedArray describes fixed-sized arrays with element type Object*.
+class FixedArray: public FixedArrayBase {
+ public:
+ // Setter and getter for elements.
+ inline Object* get(int index);
+ // Setter that uses write barrier.
+ inline void set(int index, Object* value);
+ inline bool is_the_hole(int index);
+
+ // Setter that doesn't need write barrier.
+ inline void set(int index, Smi* value);
+ // Setter with explicit barrier mode.
+ inline void set(int index, Object* value, WriteBarrierMode mode);
+
+ // Setters for frequently used oddballs located in old space.
+ inline void set_undefined(int index);
+ // TODO(isolates): duplicate.
+ inline void set_undefined(Heap* heap, int index);
+ inline void set_null(int index);
+ // TODO(isolates): duplicate.
+ inline void set_null(Heap* heap, int index);
+ inline void set_the_hole(int index);
+
+ inline Object** GetFirstElementAddress();
+ inline bool ContainsOnlySmisOrHoles();
+
+ // Gives access to raw memory which stores the array's data.
+ inline Object** data_start();
+
+ // Copy operations.
+ MUST_USE_RESULT inline MaybeObject* Copy();
+ MUST_USE_RESULT MaybeObject* CopySize(int new_length);
+
+ // Add the elements of a JSArray to this FixedArray.
+ MUST_USE_RESULT MaybeObject* AddKeysFromJSArray(JSArray* array);
+
+ // Compute the union of this and other.
+ MUST_USE_RESULT MaybeObject* UnionOfKeys(FixedArray* other);
+
+ // Copy a sub array from the receiver to dest.
+ void CopyTo(int pos, FixedArray* dest, int dest_pos, int len);
+
+ // Garbage collection support.
+ static int SizeFor(int length) { return kHeaderSize + length * kPointerSize; }
+
+ // Code Generation support.
+ static int OffsetOfElementAt(int index) { return SizeFor(index); }
+
+ // Casting.
+ static inline FixedArray* cast(Object* obj);
+
+ // Maximal allowed size, in bytes, of a single FixedArray.
+ // Prevents overflowing size computations, as well as extreme memory
+ // consumption.
+ static const int kMaxSize = 128 * MB * kPointerSize;
+ // Maximally allowed length of a FixedArray.
+ static const int kMaxLength = (kMaxSize - kHeaderSize) / kPointerSize;
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(FixedArray)
+ DECLARE_VERIFIER(FixedArray)
+#ifdef DEBUG
+ // Checks if two FixedArrays have identical contents.
+ bool IsEqualTo(FixedArray* other);
+#endif
+
+ // Swap two elements in a pair of arrays. If this array and the
+ // numbers array are the same object, the elements are only swapped
+ // once.
+ void SwapPairs(FixedArray* numbers, int i, int j);
+
+ // Sort prefix of this array and the numbers array as pairs wrt. the
+ // numbers. If the numbers array and the this array are the same
+ // object, the prefix of this array is sorted.
+ void SortPairs(FixedArray* numbers, uint32_t len);
+
+ class BodyDescriptor : public FlexibleBodyDescriptor<kHeaderSize> {
+ public:
+ static inline int SizeOf(Map* map, HeapObject* object) {
+ return SizeFor(reinterpret_cast<FixedArray*>(object)->length());
+ }
+ };
+
+ protected:
+ // Set operation on FixedArray without using write barriers. Can
+ // only be used for storing old space objects or smis.
+ 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:
+ STATIC_CHECK(kHeaderSize == Internals::kFixedArrayHeaderSize);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FixedArray);
+};
+
+
+// FixedDoubleArray describes fixed-sized arrays with element type double.
+class FixedDoubleArray: public FixedArrayBase {
+ public:
+ // Setter and getter for elements.
+ inline double get_scalar(int index);
+ inline int64_t get_representation(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, double value);
+ inline void set_the_hole(int index);
+
+ // 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;
+ }
+
+ // Gives access to raw memory which stores the array's data.
+ inline double* data_start();
+
+ // Code Generation support.
+ static int OffsetOfElementAt(int index) { return SizeFor(index); }
+
+ inline static bool is_the_hole_nan(double value);
+ inline static double hole_nan_as_double();
+ inline static double canonical_not_the_hole_nan_as_double();
+
+ // Casting.
+ static inline FixedDoubleArray* cast(Object* obj);
+
+ // Maximal allowed size, in bytes, of a single FixedDoubleArray.
+ // Prevents overflowing size computations, as well as extreme memory
+ // consumption.
+ static const int kMaxSize = 512 * MB;
+ // Maximally allowed length of a FixedArray.
+ static const int kMaxLength = (kMaxSize - kHeaderSize) / kDoubleSize;
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(FixedDoubleArray)
+ DECLARE_VERIFIER(FixedDoubleArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FixedDoubleArray);
+};
+
+
+// DescriptorArrays are fixed arrays used to hold instance descriptors.
+// The format of the these objects is:
+// [0]: Number of descriptors
+// [1]: Either Smi(0) if uninitialized, or a pointer to small fixed array:
+// [0]: pointer to fixed array with enum cache
+// [1]: either Smi(0) or pointer to fixed array with indices
+// [2]: first key
+// [2 + number of descriptors * kDescriptorSize]: start of slack
+class DescriptorArray: public FixedArray {
+ public:
+ // WhitenessWitness is used to prove that a descriptor array is white
+ // (unmarked), so incremental write barriers can be skipped because the
+ // marking invariant cannot be broken and slots pointing into evacuation
+ // candidates will be discovered when the object is scanned. A witness is
+ // always stack-allocated right after creating an array. By allocating a
+ // witness, incremental marking is globally disabled. The witness is then
+ // passed along wherever needed to statically prove that the array is known to
+ // be white.
+ class WhitenessWitness {
+ public:
+ inline explicit WhitenessWitness(FixedArray* array);
+ inline ~WhitenessWitness();
+
+ private:
+ IncrementalMarking* marking_;
+ };
+
+ // Returns true for both shared empty_descriptor_array and for smis, which the
+ // map uses to encode additional bit fields when the descriptor array is not
+ // yet used.
+ inline bool IsEmpty();
+
+ // Returns the number of descriptors in the array.
+ int number_of_descriptors() {
+ ASSERT(length() >= kFirstIndex || IsEmpty());
+ int len = length();
+ return len == 0 ? 0 : Smi::cast(get(kDescriptorLengthIndex))->value();
+ }
+
+ int number_of_descriptors_storage() {
+ int len = length();
+ return len == 0 ? 0 : (len - kFirstIndex) / kDescriptorSize;
+ }
+
+ int NumberOfSlackDescriptors() {
+ return number_of_descriptors_storage() - number_of_descriptors();
+ }
+
+ inline void SetNumberOfDescriptors(int number_of_descriptors);
+ inline int number_of_entries() { return number_of_descriptors(); }
+
+ bool HasEnumCache() {
+ return !IsEmpty() && !get(kEnumCacheIndex)->IsSmi();
+ }
+
+ void CopyEnumCacheFrom(DescriptorArray* array) {
+ set(kEnumCacheIndex, array->get(kEnumCacheIndex));
+ }
+
+ FixedArray* GetEnumCache() {
+ ASSERT(HasEnumCache());
+ FixedArray* bridge = FixedArray::cast(get(kEnumCacheIndex));
+ return FixedArray::cast(bridge->get(kEnumCacheBridgeCacheIndex));
+ }
+
+ bool HasEnumIndicesCache() {
+ if (IsEmpty()) return false;
+ Object* object = get(kEnumCacheIndex);
+ if (object->IsSmi()) return false;
+ FixedArray* bridge = FixedArray::cast(object);
+ return !bridge->get(kEnumCacheBridgeIndicesCacheIndex)->IsSmi();
+ }
+
+ FixedArray* GetEnumIndicesCache() {
+ ASSERT(HasEnumIndicesCache());
+ FixedArray* bridge = FixedArray::cast(get(kEnumCacheIndex));
+ return FixedArray::cast(bridge->get(kEnumCacheBridgeIndicesCacheIndex));
+ }
+
+ Object** GetEnumCacheSlot() {
+ ASSERT(HasEnumCache());
+ return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
+ kEnumCacheOffset);
+ }
+
+ void ClearEnumCache();
+
+ // Initialize or change the enum cache,
+ // using the supplied storage for the small "bridge".
+ void SetEnumCache(FixedArray* bridge_storage,
+ FixedArray* new_cache,
+ Object* new_index_cache);
+
+ // Accessors for fetching instance descriptor at descriptor number.
+ inline Name* GetKey(int descriptor_number);
+ inline Object** GetKeySlot(int descriptor_number);
+ inline Object* GetValue(int descriptor_number);
+ inline Object** GetValueSlot(int descriptor_number);
+ inline Object** GetDescriptorStartSlot(int descriptor_number);
+ inline Object** GetDescriptorEndSlot(int descriptor_number);
+ inline PropertyDetails GetDetails(int descriptor_number);
+ inline PropertyType GetType(int descriptor_number);
+ inline int GetFieldIndex(int descriptor_number);
+ inline Object* GetConstant(int descriptor_number);
+ inline Object* GetCallbacksObject(int descriptor_number);
+ inline AccessorDescriptor* GetCallbacks(int descriptor_number);
+
+ inline Name* GetSortedKey(int descriptor_number);
+ inline int GetSortedKeyIndex(int descriptor_number);
+ inline void SetSortedKey(int pointer, int descriptor_number);
+ inline void InitializeRepresentations(Representation representation);
+ inline void SetRepresentation(int descriptor_number,
+ Representation representation);
+
+ // Accessor for complete descriptor.
+ inline void Get(int descriptor_number, Descriptor* desc);
+ inline void Set(int descriptor_number,
+ Descriptor* desc,
+ const WhitenessWitness&);
+ inline void Set(int descriptor_number, Descriptor* desc);
+
+ // Append automatically sets the enumeration index. This should only be used
+ // to add descriptors in bulk at the end, followed by sorting the descriptor
+ // array.
+ inline void Append(Descriptor* desc, const WhitenessWitness&);
+ inline void Append(Descriptor* desc);
+
+ // Transfer a complete descriptor from the src descriptor array to this
+ // descriptor array.
+ void CopyFrom(int dst_index,
+ DescriptorArray* src,
+ int src_index,
+ const WhitenessWitness&);
+ MUST_USE_RESULT MaybeObject* Merge(int verbatim,
+ int valid,
+ int new_size,
+ DescriptorArray* other);
+
+ bool IsMoreGeneralThan(int verbatim,
+ int valid,
+ int new_size,
+ DescriptorArray* other);
+
+ MUST_USE_RESULT MaybeObject* CopyUpTo(int enumeration_index) {
+ return CopyUpToAddAttributes(enumeration_index, NONE);
+ }
+
+ MUST_USE_RESULT MaybeObject* CopyUpToAddAttributes(
+ int enumeration_index,
+ PropertyAttributes attributes);
+
+ // Sort the instance descriptors by the hash codes of their keys.
+ void Sort();
+
+ // Search the instance descriptors for given name.
+ INLINE(int Search(Name* name, int number_of_own_descriptors));
+
+ // As the above, but uses DescriptorLookupCache and updates it when
+ // necessary.
+ INLINE(int SearchWithCache(Name* name, Map* map));
+
+ // Allocates a DescriptorArray, but returns the singleton
+ // empty descriptor array object if number_of_descriptors is 0.
+ MUST_USE_RESULT static MaybeObject* Allocate(int number_of_descriptors,
+ int slack = 0);
+
+ // Casting.
+ static inline DescriptorArray* cast(Object* obj);
+
+ // Constant for denoting key was not found.
+ static const int kNotFound = -1;
+
+ static const int kDescriptorLengthIndex = 0;
+ static const int kEnumCacheIndex = 1;
+ static const int kFirstIndex = 2;
+
+ // The length of the "bridge" to the enum cache.
+ static const int kEnumCacheBridgeLength = 2;
+ static const int kEnumCacheBridgeCacheIndex = 0;
+ static const int kEnumCacheBridgeIndicesCacheIndex = 1;
+
+ // Layout description.
+ static const int kDescriptorLengthOffset = FixedArray::kHeaderSize;
+ static const int kEnumCacheOffset = kDescriptorLengthOffset + kPointerSize;
+ static const int kFirstOffset = kEnumCacheOffset + kPointerSize;
+
+ // Layout description for the bridge array.
+ static const int kEnumCacheBridgeCacheOffset = FixedArray::kHeaderSize;
+
+ // Layout of descriptor.
+ static const int kDescriptorKey = 0;
+ static const int kDescriptorDetails = 1;
+ static const int kDescriptorValue = 2;
+ static const int kDescriptorSize = 3;
+
+#ifdef OBJECT_PRINT
+ // Print all the descriptors.
+ inline void PrintDescriptors() {
+ PrintDescriptors(stdout);
+ }
+ void PrintDescriptors(FILE* out);
+#endif
+
+#ifdef DEBUG
+ // Is the descriptor array sorted and without duplicates?
+ bool IsSortedNoDuplicates(int valid_descriptors = -1);
+
+ // Is the descriptor array consistent with the back pointers in targets?
+ bool IsConsistentWithBackPointers(Map* current_map);
+
+ // Are two DescriptorArrays equal?
+ bool IsEqualTo(DescriptorArray* other);
+#endif
+
+ // The maximum number of descriptors we want in a descriptor array (should
+ // fit in a page).
+ static const int kMaxNumberOfDescriptors = 1024 + 512;
+
+ // Returns the fixed array length required to hold number_of_descriptors
+ // descriptors.
+ static int LengthFor(int number_of_descriptors) {
+ return ToKeyIndex(number_of_descriptors);
+ }
+
+ private:
+ // An entry in a DescriptorArray, represented as an (array, index) pair.
+ class Entry {
+ public:
+ inline explicit Entry(DescriptorArray* descs, int index) :
+ descs_(descs), index_(index) { }
+
+ inline PropertyType type() { return descs_->GetType(index_); }
+ inline Object* GetCallbackObject() { return descs_->GetValue(index_); }
+
+ private:
+ DescriptorArray* descs_;
+ int index_;
+ };
+
+ // Conversion from descriptor number to array indices.
+ static int ToKeyIndex(int descriptor_number) {
+ return kFirstIndex +
+ (descriptor_number * kDescriptorSize) +
+ kDescriptorKey;
+ }
+
+ static int ToDetailsIndex(int descriptor_number) {
+ return kFirstIndex +
+ (descriptor_number * kDescriptorSize) +
+ kDescriptorDetails;
+ }
+
+ static int ToValueIndex(int descriptor_number) {
+ return kFirstIndex +
+ (descriptor_number * kDescriptorSize) +
+ kDescriptorValue;
+ }
+
+ // Swap first and second descriptor.
+ inline void SwapSortedKeys(int first, int second);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DescriptorArray);
+};
+
+
+enum SearchMode { ALL_ENTRIES, VALID_ENTRIES };
+
+template<SearchMode search_mode, typename T>
+inline int LinearSearch(T* array, Name* name, int len, int valid_entries);
+
+
+template<SearchMode search_mode, typename T>
+inline int Search(T* array, Name* name, int valid_entries = 0);
+
+
+// HashTable is a subclass of FixedArray that implements a hash table
+// that uses open addressing and quadratic probing.
+//
+// In order for the quadratic probing to work, elements that have not
+// yet been used and elements that have been deleted are
+// distinguished. Probing continues when deleted elements are
+// encountered and stops when unused elements are encountered.
+//
+// - Elements with key == undefined have not been used yet.
+// - 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:
+// class ExampleShape {
+// public:
+// // Tells whether key matches other.
+// static bool IsMatch(Key key, Object* other);
+// // Returns the hash value for key.
+// static uint32_t Hash(Key key);
+// // Returns the hash value for object.
+// static uint32_t HashForObject(Key key, Object* object);
+// // Convert key to an object.
+// static inline Object* AsObject(Heap* heap, Key key);
+// // The prefix size indicates number of elements in the beginning
+// // of the backing storage.
+// static const int kPrefixSize = ..;
+// // The Element size indicates number of elements per entry.
+// static const int kEntrySize = ..;
+// };
+// The prefix size indicates an amount of memory in the
+// beginning of the backing storage that can be used for non-element
+// information by subclasses.
+
+template<typename Key>
+class BaseShape {
+ public:
+ static const bool UsesSeed = false;
+ static uint32_t Hash(Key key) { return 0; }
+ static uint32_t SeededHash(Key key, uint32_t seed) {
+ ASSERT(UsesSeed);
+ return Hash(key);
+ }
+ static uint32_t HashForObject(Key key, Object* object) { return 0; }
+ static uint32_t SeededHashForObject(Key key, uint32_t seed, Object* object) {
+ ASSERT(UsesSeed);
+ return HashForObject(key, object);
+ }
+};
+
+template<typename Shape, typename Key>
+class HashTable: public FixedArray {
+ public:
+ enum MinimumCapacity {
+ USE_DEFAULT_MINIMUM_CAPACITY,
+ USE_CUSTOM_MINIMUM_CAPACITY
+ };
+
+ // Wrapper methods
+ inline uint32_t Hash(Key key) {
+ if (Shape::UsesSeed) {
+ return Shape::SeededHash(key,
+ GetHeap()->HashSeed());
+ } else {
+ return Shape::Hash(key);
+ }
+ }
+
+ inline uint32_t HashForObject(Key key, Object* object) {
+ if (Shape::UsesSeed) {
+ return Shape::SeededHashForObject(key,
+ GetHeap()->HashSeed(), object);
+ } else {
+ return Shape::HashForObject(key, object);
+ }
+ }
+
+ // Returns the number of elements in the hash table.
+ int NumberOfElements() {
+ return Smi::cast(get(kNumberOfElementsIndex))->value();
+ }
+
+ // Returns the number of deleted elements in the hash table.
+ int NumberOfDeletedElements() {
+ return Smi::cast(get(kNumberOfDeletedElementsIndex))->value();
+ }
+
+ // Returns the capacity of the hash table.
+ int Capacity() {
+ return Smi::cast(get(kCapacityIndex))->value();
+ }
+
+ // ElementAdded should be called whenever an element is added to a
+ // hash table.
+ void ElementAdded() { SetNumberOfElements(NumberOfElements() + 1); }
+
+ // ElementRemoved should be called whenever an element is removed from
+ // a hash table.
+ void ElementRemoved() {
+ SetNumberOfElements(NumberOfElements() - 1);
+ SetNumberOfDeletedElements(NumberOfDeletedElements() + 1);
+ }
+ void ElementsRemoved(int n) {
+ SetNumberOfElements(NumberOfElements() - n);
+ SetNumberOfDeletedElements(NumberOfDeletedElements() + n);
+ }
+
+ // Returns a new HashTable object. Might return Failure.
+ MUST_USE_RESULT static MaybeObject* Allocate(
+ Heap* heap,
+ int at_least_space_for,
+ MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Computes the required capacity for a table holding the given
+ // number of elements. May be more than HashTable::kMaxCapacity.
+ static int ComputeCapacity(int at_least_space_for);
+
+ // Returns the key at entry.
+ Object* KeyAt(int entry) { return get(EntryToIndex(entry)); }
+
+ // 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->IsTheHole() && !k->IsUndefined();
+ }
+
+ // Garbage collection support.
+ void IteratePrefix(ObjectVisitor* visitor);
+ void IterateElements(ObjectVisitor* visitor);
+
+ // Casting.
+ static inline HashTable* cast(Object* obj);
+
+ // Compute the probe offset (quadratic probing).
+ INLINE(static uint32_t GetProbeOffset(uint32_t n)) {
+ return (n + n * n) >> 1;
+ }
+
+ static const int kNumberOfElementsIndex = 0;
+ static const int kNumberOfDeletedElementsIndex = 1;
+ static const int kCapacityIndex = 2;
+ static const int kPrefixStartIndex = 3;
+ static const int kElementsStartIndex =
+ kPrefixStartIndex + Shape::kPrefixSize;
+ static const int kEntrySize = Shape::kEntrySize;
+ static const int kElementsStartOffset =
+ kHeaderSize + kElementsStartIndex * kPointerSize;
+ static const int kCapacityOffset =
+ kHeaderSize + kCapacityIndex * kPointerSize;
+
+ // Constant used for denoting a absent entry.
+ static const int kNotFound = -1;
+
+ // Maximal capacity of HashTable. Based on maximal length of underlying
+ // FixedArray. Staying below kMaxCapacity also ensures that EntryToIndex
+ // cannot overflow.
+ static const int kMaxCapacity =
+ (FixedArray::kMaxLength - kElementsStartOffset) / kEntrySize;
+
+ // Find entry for key otherwise return kNotFound.
+ inline int FindEntry(Key key);
+ int FindEntry(Isolate* isolate, Key key);
+
+ protected:
+ // Find the entry at which to insert element with the given key that
+ // has the given hash value.
+ uint32_t FindInsertionEntry(uint32_t hash);
+
+ // Returns the index for an entry (of the key)
+ static inline int EntryToIndex(int entry) {
+ return (entry * kEntrySize) + kElementsStartIndex;
+ }
+
+ // Update the number of elements in the hash table.
+ void SetNumberOfElements(int nof) {
+ set(kNumberOfElementsIndex, Smi::FromInt(nof));
+ }
+
+ // Update the number of deleted elements in the hash table.
+ void SetNumberOfDeletedElements(int nod) {
+ set(kNumberOfDeletedElementsIndex, Smi::FromInt(nod));
+ }
+
+ // Sets the capacity of the hash table.
+ void SetCapacity(int capacity) {
+ // To scale a computed hash code to fit within the hash table, we
+ // use bit-wise AND with a mask, so the capacity must be positive
+ // and non-zero.
+ ASSERT(capacity > 0);
+ ASSERT(capacity <= kMaxCapacity);
+ set(kCapacityIndex, Smi::FromInt(capacity));
+ }
+
+
+ // Returns probe entry.
+ static uint32_t GetProbe(uint32_t hash, uint32_t number, uint32_t size) {
+ ASSERT(IsPowerOf2(size));
+ return (hash + GetProbeOffset(number)) & (size - 1);
+ }
+
+ inline static uint32_t FirstProbe(uint32_t hash, uint32_t size) {
+ return hash & (size - 1);
+ }
+
+ inline static uint32_t NextProbe(
+ uint32_t last, uint32_t number, uint32_t size) {
+ return (last + number) & (size - 1);
+ }
+
+ // Rehashes this hash-table into the new table.
+ MUST_USE_RESULT MaybeObject* Rehash(HashTable* new_table, Key key);
+
+ // Attempt to shrink hash table after removal of key.
+ MUST_USE_RESULT MaybeObject* Shrink(Key key);
+
+ // Ensure enough space for n additional elements.
+ MUST_USE_RESULT MaybeObject* EnsureCapacity(int n, Key key);
+};
+
+
+// HashTableKey is an abstract superclass for virtual key behavior.
+class HashTableKey {
+ public:
+ // Returns whether the other object matches this key.
+ virtual bool IsMatch(Object* other) = 0;
+ // Returns the hash value for this key.
+ virtual uint32_t Hash() = 0;
+ // Returns the hash value for object.
+ virtual uint32_t HashForObject(Object* key) = 0;
+ // Returns the key object for storing into the hash table.
+ // If allocations fails a failure object is returned.
+ MUST_USE_RESULT virtual MaybeObject* AsObject(Heap* heap) = 0;
+ // Required.
+ virtual ~HashTableKey() {}
+};
+
+
+class StringTableShape : public BaseShape<HashTableKey*> {
+ public:
+ static inline bool IsMatch(HashTableKey* key, Object* value) {
+ return key->IsMatch(value);
+ }
+ static inline uint32_t Hash(HashTableKey* key) {
+ return key->Hash();
+ }
+ static inline uint32_t HashForObject(HashTableKey* key, Object* object) {
+ return key->HashForObject(object);
+ }
+ MUST_USE_RESULT static inline MaybeObject* AsObject(Heap* heap,
+ HashTableKey* key) {
+ return key->AsObject(heap);
+ }
+
+ static const int kPrefixSize = 0;
+ static const int kEntrySize = 1;
+};
+
+class SeqOneByteString;
+
+// StringTable.
+//
+// No special elements in the prefix and the element size is 1
+// because only the string itself (the key) needs to be stored.
+class StringTable: public HashTable<StringTableShape, HashTableKey*> {
+ public:
+ // Find string in the string table. If it is not there yet, it is
+ // added. The return value is the string table which might have
+ // been enlarged. If the return value is not a failure, the string
+ // pointer *s is set to the string found.
+ MUST_USE_RESULT MaybeObject* LookupUtf8String(
+ Vector<const char> str,
+ Object** s);
+ MUST_USE_RESULT MaybeObject* LookupOneByteString(
+ Vector<const uint8_t> str,
+ Object** s);
+ MUST_USE_RESULT MaybeObject* LookupSubStringOneByteString(
+ Handle<SeqOneByteString> str,
+ int from,
+ int length,
+ Object** s);
+ MUST_USE_RESULT MaybeObject* LookupTwoByteString(
+ Vector<const uc16> str,
+ Object** s);
+ MUST_USE_RESULT MaybeObject* LookupString(String* key, Object** s);
+
+ // Looks up a string that is equal to the given string and returns
+ // true if it is found, assigning the string to the given output
+ // parameter.
+ bool LookupStringIfExists(String* str, String** result);
+ bool LookupTwoCharsStringIfExists(uint16_t c1, uint16_t c2, String** result);
+
+ // Casting.
+ static inline StringTable* cast(Object* obj);
+
+ private:
+ MUST_USE_RESULT MaybeObject* LookupKey(HashTableKey* key, Object** s);
+
+ template <bool seq_ascii> friend class JsonParser;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringTable);
+};
+
+
+class MapCacheShape : public BaseShape<HashTableKey*> {
+ public:
+ static inline bool IsMatch(HashTableKey* key, Object* value) {
+ return key->IsMatch(value);
+ }
+ static inline uint32_t Hash(HashTableKey* key) {
+ return key->Hash();
+ }
+
+ static inline uint32_t HashForObject(HashTableKey* key, Object* object) {
+ return key->HashForObject(object);
+ }
+
+ MUST_USE_RESULT static inline MaybeObject* AsObject(Heap* heap,
+ HashTableKey* key) {
+ return key->AsObject(heap);
+ }
+
+ static const int kPrefixSize = 0;
+ static const int kEntrySize = 2;
+};
+
+
+// MapCache.
+//
+// Maps keys that are a fixed array of unique names to a map.
+// Used for canonicalize maps for object literals.
+class MapCache: public HashTable<MapCacheShape, HashTableKey*> {
+ public:
+ // Find cached value for a name key, otherwise return null.
+ Object* Lookup(FixedArray* key);
+ MUST_USE_RESULT MaybeObject* Put(FixedArray* key, Map* value);
+ static inline MapCache* cast(Object* obj);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MapCache);
+};
+
+
+template <typename Shape, typename Key>
+class Dictionary: public HashTable<Shape, Key> {
+ public:
+ static inline Dictionary<Shape, Key>* cast(Object* obj) {
+ return reinterpret_cast<Dictionary<Shape, Key>*>(obj);
+ }
+
+ // Returns the value at entry.
+ Object* ValueAt(int entry) {
+ return this->get(HashTable<Shape, Key>::EntryToIndex(entry) + 1);
+ }
+
+ // Set the value for entry.
+ void ValueAtPut(int entry, Object* value) {
+ this->set(HashTable<Shape, Key>::EntryToIndex(entry) + 1, value);
+ }
+
+ // Returns the property details for the property at entry.
+ PropertyDetails DetailsAt(int entry) {
+ ASSERT(entry >= 0); // Not found is -1, which is not caught by get().
+ return PropertyDetails(
+ Smi::cast(this->get(HashTable<Shape, Key>::EntryToIndex(entry) + 2)));
+ }
+
+ // Set the details for entry.
+ void DetailsAtPut(int entry, PropertyDetails value) {
+ this->set(HashTable<Shape, Key>::EntryToIndex(entry) + 2, value.AsSmi());
+ }
+
+ // Sorting support
+ void CopyValuesTo(FixedArray* elements);
+
+ // Delete a property from the dictionary.
+ Object* DeleteProperty(int entry, JSObject::DeleteMode mode);
+
+ // Attempt to shrink the dictionary after deletion of key.
+ MUST_USE_RESULT MaybeObject* Shrink(Key key);
+
+ // Returns the number of elements in the dictionary filtering out properties
+ // with the specified attributes.
+ int NumberOfElementsFilterAttributes(PropertyAttributes filter);
+
+ // Returns the number of enumerable elements in the dictionary.
+ int NumberOfEnumElements();
+
+ enum SortMode { UNSORTED, SORTED };
+ // Copies keys to preallocated fixed array.
+ void CopyKeysTo(FixedArray* storage,
+ PropertyAttributes filter,
+ SortMode sort_mode);
+ // Fill in details for properties into storage.
+ void CopyKeysTo(FixedArray* storage,
+ int index,
+ PropertyAttributes filter,
+ SortMode sort_mode);
+
+ // Accessors for next enumeration index.
+ void SetNextEnumerationIndex(int index) {
+ ASSERT(index != 0);
+ this->set(kNextEnumerationIndexIndex, Smi::FromInt(index));
+ }
+
+ int NextEnumerationIndex() {
+ return Smi::cast(FixedArray::get(kNextEnumerationIndexIndex))->value();
+ }
+
+ // Returns a new array for dictionary usage. Might return Failure.
+ MUST_USE_RESULT static MaybeObject* Allocate(
+ Heap* heap,
+ int at_least_space_for,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // Ensure enough space for n additional elements.
+ MUST_USE_RESULT MaybeObject* EnsureCapacity(int n, Key key);
+
+#ifdef OBJECT_PRINT
+ inline void Print() {
+ Print(stdout);
+ }
+ void Print(FILE* out);
+#endif
+ // Returns the key (slow).
+ Object* SlowReverseLookup(Object* value);
+
+ // Sets the entry to (key, value) pair.
+ inline void SetEntry(int entry,
+ Object* key,
+ Object* value);
+ inline void SetEntry(int entry,
+ Object* key,
+ Object* value,
+ PropertyDetails details);
+
+ MUST_USE_RESULT MaybeObject* Add(Key key,
+ Object* value,
+ PropertyDetails details);
+
+ protected:
+ // Generic at put operation.
+ MUST_USE_RESULT MaybeObject* AtPut(Key key, Object* value);
+
+ // Add entry to dictionary.
+ MUST_USE_RESULT MaybeObject* AddEntry(Key key,
+ Object* value,
+ PropertyDetails details,
+ uint32_t hash);
+
+ // Generate new enumeration indices to avoid enumeration index overflow.
+ MUST_USE_RESULT MaybeObject* GenerateNewEnumerationIndices();
+ static const int kMaxNumberKeyIndex =
+ HashTable<Shape, Key>::kPrefixStartIndex;
+ static const int kNextEnumerationIndexIndex = kMaxNumberKeyIndex + 1;
+};
+
+
+class NameDictionaryShape : public BaseShape<Name*> {
+ public:
+ static inline bool IsMatch(Name* key, Object* other);
+ static inline uint32_t Hash(Name* key);
+ static inline uint32_t HashForObject(Name* key, Object* object);
+ MUST_USE_RESULT static inline MaybeObject* AsObject(Heap* heap,
+ Name* key);
+ static const int kPrefixSize = 2;
+ static const int kEntrySize = 3;
+ static const bool kIsEnumerable = true;
+};
+
+
+class NameDictionary: public Dictionary<NameDictionaryShape, Name*> {
+ public:
+ static inline NameDictionary* cast(Object* obj) {
+ ASSERT(obj->IsDictionary());
+ return reinterpret_cast<NameDictionary*>(obj);
+ }
+
+ // Copies enumerable keys to preallocated fixed array.
+ FixedArray* CopyEnumKeysTo(FixedArray* storage);
+ static void DoGenerateNewEnumerationIndices(
+ Handle<NameDictionary> dictionary);
+
+ // For transforming properties of a JSObject.
+ MUST_USE_RESULT MaybeObject* TransformPropertiesToFastFor(
+ JSObject* obj,
+ int unused_property_fields);
+
+ // Find entry for key, otherwise return kNotFound. Optimized version of
+ // HashTable::FindEntry.
+ int FindEntry(Name* key);
+};
+
+
+class NumberDictionaryShape : public BaseShape<uint32_t> {
+ public:
+ static inline bool IsMatch(uint32_t key, Object* other);
+ MUST_USE_RESULT static inline MaybeObject* AsObject(Heap* heap,
+ uint32_t key);
+ static const int kEntrySize = 3;
+ static const bool kIsEnumerable = false;
+};
+
+
+class SeededNumberDictionaryShape : public NumberDictionaryShape {
+ public:
+ static const bool UsesSeed = true;
+ static const int kPrefixSize = 2;
+
+ static inline uint32_t SeededHash(uint32_t key, uint32_t seed);
+ static inline uint32_t SeededHashForObject(uint32_t key,
+ uint32_t seed,
+ Object* object);
+};
+
+
+class UnseededNumberDictionaryShape : public NumberDictionaryShape {
+ public:
+ static const int kPrefixSize = 0;
+
+ static inline uint32_t Hash(uint32_t key);
+ static inline uint32_t HashForObject(uint32_t key, Object* object);
+};
+
+
+class SeededNumberDictionary
+ : public Dictionary<SeededNumberDictionaryShape, uint32_t> {
+ public:
+ static SeededNumberDictionary* cast(Object* obj) {
+ ASSERT(obj->IsDictionary());
+ return reinterpret_cast<SeededNumberDictionary*>(obj);
+ }
+
+ // Type specific at put (default NONE attributes is used when adding).
+ MUST_USE_RESULT MaybeObject* AtNumberPut(uint32_t key, Object* value);
+ MUST_USE_RESULT MaybeObject* AddNumberEntry(uint32_t key,
+ Object* value,
+ 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);
+
+ void UpdateMaxNumberKey(uint32_t key);
+
+ // If slow elements are required we will never go back to fast-case
+ // for the elements kept in this dictionary. We require slow
+ // elements if an element has been added at an index larger than
+ // kRequiresSlowElementsLimit or set_requires_slow_elements() has been called
+ // when defining a getter or setter with a number key.
+ inline bool requires_slow_elements();
+ inline void set_requires_slow_elements();
+
+ // Get the value of the max number key that has been added to this
+ // dictionary. max_number_key can only be called if
+ // requires_slow_elements returns false.
+ inline uint32_t max_number_key();
+
+ // Bit masks.
+ static const int kRequiresSlowElementsMask = 1;
+ static const int kRequiresSlowElementsTagSize = 1;
+ static const uint32_t kRequiresSlowElementsLimit = (1 << 29) - 1;
+};
+
+
+class UnseededNumberDictionary
+ : public Dictionary<UnseededNumberDictionaryShape, uint32_t> {
+ public:
+ static UnseededNumberDictionary* cast(Object* obj) {
+ ASSERT(obj->IsDictionary());
+ return reinterpret_cast<UnseededNumberDictionary*>(obj);
+ }
+
+ // Type specific at put (default NONE attributes is used when adding).
+ MUST_USE_RESULT MaybeObject* AtNumberPut(uint32_t key, Object* value);
+ 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(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(Heap* heap,
+ Object* key);
+ static const int kPrefixSize = 0;
+ static const int kEntrySize = entrysize;
+};
+
+
+// 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<2>, Object*> {
+ public:
+ static inline ObjectHashTable* cast(Object* obj) {
+ ASSERT(obj->IsHashTable());
+ return reinterpret_cast<ObjectHashTable*>(obj);
+ }
+
+ // Looks up the value associated with the given key. The hole value is
+ // returned in case the key is not present.
+ Object* Lookup(Object* key);
+
+ // Adds (or overwrites) the value associated with the given key. Mapping a
+ // key to the hole value causes removal of the whole entry.
+ MUST_USE_RESULT MaybeObject* Put(Object* key, Object* value);
+
+ private:
+ friend class MarkCompactCollector;
+
+ 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) {
+ return EntryToIndex(entry) + 1;
+ }
+};
+
+
+// JSFunctionResultCache caches results of some JSFunction invocation.
+// It is a fixed array with fixed structure:
+// [0]: factory function
+// [1]: finger index
+// [2]: current cache size
+// [3]: dummy field.
+// The rest of array are key/value pairs.
+class JSFunctionResultCache: public FixedArray {
+ public:
+ static const int kFactoryIndex = 0;
+ static const int kFingerIndex = kFactoryIndex + 1;
+ static const int kCacheSizeIndex = kFingerIndex + 1;
+ static const int kDummyIndex = kCacheSizeIndex + 1;
+ static const int kEntriesIndex = kDummyIndex + 1;
+
+ static const int kEntrySize = 2; // key + value
+
+ static const int kFactoryOffset = kHeaderSize;
+ static const int kFingerOffset = kFactoryOffset + kPointerSize;
+ static const int kCacheSizeOffset = kFingerOffset + kPointerSize;
+
+ inline void MakeZeroSize();
+ inline void Clear();
+
+ inline int size();
+ inline void set_size(int size);
+ inline int finger_index();
+ inline void set_finger_index(int finger_index);
+
+ // Casting
+ static inline JSFunctionResultCache* cast(Object* obj);
+
+ DECLARE_VERIFIER(JSFunctionResultCache)
+};
+
+
+// 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 scope_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 an internalized
+ // string.
+ 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 an internalized string.
+ // 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 an internalized string.
+ int ParameterIndex(String* name);
+
+ // Lookup support for serialized scope info. Returns the function context
+ // slot index if the function name is present and context-allocated (named
+ // function expressions, only), otherwise returns a value < 0. The name
+ // must be an internalized string.
+ int FunctionContextSlotIndex(String* name, VariableMode* mode);
+
+
+ // Copies all the context locals into an object used to materialize a scope.
+ bool CopyContextLocalsToScopeObject(Isolate* isolate,
+ Handle<Context> context,
+ Handle<JSObject> scope_object);
+
+
+ static Handle<ScopeInfo> Create(Scope* scope, Zone* zone);
+
+ // Serializes empty scope info.
+ static ScopeInfo* Empty(Isolate* isolate);
+
+#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 ScopeTypeField: 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.
+class NormalizedMapCache: public FixedArray {
+ public:
+ static const int kEntries = 64;
+
+ MUST_USE_RESULT MaybeObject* Get(JSObject* object,
+ PropertyNormalizationMode mode);
+
+ void Clear();
+
+ // Casting
+ static inline NormalizedMapCache* cast(Object* obj);
+
+ DECLARE_VERIFIER(NormalizedMapCache)
+};
+
+
+// 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);
+
+ // Treat contents as an int array.
+ inline int get_int(int index);
+
+ static int SizeFor(int length) {
+ return OBJECT_POINTER_ALIGN(kHeaderSize + length);
+ }
+ // We use byte arrays for free blocks in the heap. Given a desired size in
+ // bytes that is a multiple of the word size and big enough to hold a byte
+ // array, this function returns the number of elements a byte array should
+ // have.
+ static int LengthFor(int size_in_bytes) {
+ ASSERT(IsAligned(size_in_bytes, kPointerSize));
+ ASSERT(size_in_bytes >= kHeaderSize);
+ return size_in_bytes - kHeaderSize;
+ }
+
+ // Returns data start address.
+ inline Address GetDataStartAddress();
+
+ // Returns a pointer to the ByteArray object for a given data start address.
+ static inline ByteArray* FromDataStartAddress(Address address);
+
+ // Casting.
+ static inline ByteArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ inline int ByteArraySize() {
+ return SizeFor(this->length());
+ }
+ DECLARE_PRINTER(ByteArray)
+ DECLARE_VERIFIER(ByteArray)
+
+ // Layout description.
+ static const int kAlignedSize = OBJECT_POINTER_ALIGN(kHeaderSize);
+
+ // Maximal memory consumption for a single ByteArray.
+ static const int kMaxSize = 512 * MB;
+ // Maximal length of a single ByteArray.
+ static const int kMaxLength = kMaxSize - kHeaderSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArray);
+};
+
+
+// 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);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(FreeSpace)
+ DECLARE_VERIFIER(FreeSpace)
+
+ // 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
+// specification. As of this writing the first public draft is not yet
+// available, but Khronos members can access the draft at:
+// https://cvs.khronos.org/svn/repos/3dweb/trunk/doc/spec/WebGL-spec.html
+//
+// The semantics of these arrays differ from CanvasPixelArray.
+// Out-of-range values passed to the setter are converted via a C
+// cast, not clamping. Out-of-range indices cause exceptions to be
+// raised rather than being silently ignored.
+class ExternalArray: public FixedArrayBase {
+ public:
+ inline bool is_the_hole(int index) { return false; }
+
+ // [external_pointer]: The pointer to the external memory area backing this
+ // external array.
+ DECL_ACCESSORS(external_pointer, void) // Pointer to the data store.
+
+ // Casting.
+ static inline ExternalArray* cast(Object* obj);
+
+ // Maximal acceptable length for an external array.
+ static const int kMaxLength = 0x3fffffff;
+
+ // ExternalArray headers are not quadword aligned.
+ static const int kExternalPointerOffset =
+ POINTER_SIZE_ALIGN(FixedArrayBase::kLengthOffset + kPointerSize);
+ static const int kHeaderSize = kExternalPointerOffset + kPointerSize;
+ static const int kAlignedSize = OBJECT_POINTER_ALIGN(kHeaderSize);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalArray);
+};
+
+
+// A ExternalPixelArray represents a fixed-size byte array with special
+// semantics used for implementing the CanvasPixelArray object. Please see the
+// specification at:
+
+// http://www.whatwg.org/specs/web-apps/current-work/
+// multipage/the-canvas-element.html#canvaspixelarray
+// In particular, write access clamps the value written to 0 or 255 if the
+// value written is outside this range.
+class ExternalPixelArray: public ExternalArray {
+ public:
+ inline uint8_t* external_pixel_pointer();
+
+ // Setter and getter.
+ inline uint8_t get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, uint8_t value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber and
+ // undefined and clamps the converted value between 0 and 255.
+ Object* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalPixelArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalPixelArray)
+ DECLARE_VERIFIER(ExternalPixelArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalPixelArray);
+};
+
+
+class ExternalByteArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline int8_t get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, int8_t value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalByteArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalByteArray)
+ DECLARE_VERIFIER(ExternalByteArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalByteArray);
+};
+
+
+class ExternalUnsignedByteArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline uint8_t get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, uint8_t value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalUnsignedByteArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalUnsignedByteArray)
+ DECLARE_VERIFIER(ExternalUnsignedByteArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalUnsignedByteArray);
+};
+
+
+class ExternalShortArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline int16_t get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, int16_t value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalShortArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalShortArray)
+ DECLARE_VERIFIER(ExternalShortArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalShortArray);
+};
+
+
+class ExternalUnsignedShortArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline uint16_t get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, uint16_t value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalUnsignedShortArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalUnsignedShortArray)
+ DECLARE_VERIFIER(ExternalUnsignedShortArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalUnsignedShortArray);
+};
+
+
+class ExternalIntArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline int32_t get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, int32_t value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalIntArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalIntArray)
+ DECLARE_VERIFIER(ExternalIntArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalIntArray);
+};
+
+
+class ExternalUnsignedIntArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline uint32_t get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, uint32_t value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalUnsignedIntArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalUnsignedIntArray)
+ DECLARE_VERIFIER(ExternalUnsignedIntArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalUnsignedIntArray);
+};
+
+
+class ExternalFloatArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline float get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, float value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalFloatArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalFloatArray)
+ DECLARE_VERIFIER(ExternalFloatArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalFloatArray);
+};
+
+
+class ExternalDoubleArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline double get_scalar(int index);
+ MUST_USE_RESULT inline MaybeObject* get(int index);
+ inline void set(int index, double value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MUST_USE_RESULT MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalDoubleArray* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExternalDoubleArray)
+ DECLARE_VERIFIER(ExternalDoubleArray)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalDoubleArray);
+};
+
+
+// DeoptimizationInputData is a fixed array used to hold the deoptimization
+// data for code generated by the Hydrogen/Lithium compiler. It also
+// contains information about functions that were inlined. If N different
+// functions were inlined then first N elements of the literal array will
+// contain these functions.
+//
+// It can be empty.
+class DeoptimizationInputData: public FixedArray {
+ public:
+ // Layout description. Indices in the array.
+ static const int kTranslationByteArrayIndex = 0;
+ static const int kInlinedFunctionCountIndex = 1;
+ static const int kLiteralArrayIndex = 2;
+ static const int kOsrAstIdIndex = 3;
+ static const int kOsrPcOffsetIndex = 4;
+ static const int kFirstDeoptEntryIndex = 5;
+
+ // Offsets of deopt entry elements relative to the start of the entry.
+ static const int kAstIdRawOffset = 0;
+ static const int kTranslationIndexOffset = 1;
+ static const int kArgumentsStackHeightOffset = 2;
+ static const int kPcOffset = 3;
+ static const int kDeoptEntrySize = 4;
+
+ // Simple element accessors.
+#define DEFINE_ELEMENT_ACCESSORS(name, type) \
+ type* name() { \
+ return type::cast(get(k##name##Index)); \
+ } \
+ void Set##name(type* value) { \
+ set(k##name##Index, value); \
+ }
+
+ DEFINE_ELEMENT_ACCESSORS(TranslationByteArray, ByteArray)
+ DEFINE_ELEMENT_ACCESSORS(InlinedFunctionCount, Smi)
+ DEFINE_ELEMENT_ACCESSORS(LiteralArray, FixedArray)
+ DEFINE_ELEMENT_ACCESSORS(OsrAstId, Smi)
+ DEFINE_ELEMENT_ACCESSORS(OsrPcOffset, Smi)
+
+#undef DEFINE_ELEMENT_ACCESSORS
+
+ // Accessors for elements of the ith deoptimization entry.
+#define DEFINE_ENTRY_ACCESSORS(name, type) \
+ type* name(int i) { \
+ return type::cast(get(IndexForEntry(i) + k##name##Offset)); \
+ } \
+ void Set##name(int i, type* value) { \
+ set(IndexForEntry(i) + k##name##Offset, value); \
+ }
+
+ DEFINE_ENTRY_ACCESSORS(AstIdRaw, Smi)
+ DEFINE_ENTRY_ACCESSORS(TranslationIndex, Smi)
+ DEFINE_ENTRY_ACCESSORS(ArgumentsStackHeight, Smi)
+ DEFINE_ENTRY_ACCESSORS(Pc, Smi)
+
+#undef DEFINE_ENTRY_ACCESSORS
+
+ BailoutId AstId(int i) {
+ return BailoutId(AstIdRaw(i)->value());
+ }
+
+ void SetAstId(int i, BailoutId value) {
+ SetAstIdRaw(i, Smi::FromInt(value.ToInt()));
+ }
+
+ int DeoptCount() {
+ return (length() - kFirstDeoptEntryIndex) / kDeoptEntrySize;
+ }
+
+ // Allocates a DeoptimizationInputData.
+ MUST_USE_RESULT static MaybeObject* Allocate(int deopt_entry_count,
+ PretenureFlag pretenure);
+
+ // Casting.
+ static inline DeoptimizationInputData* cast(Object* obj);
+
+#ifdef ENABLE_DISASSEMBLER
+ void DeoptimizationInputDataPrint(FILE* out);
+#endif
+
+ private:
+ static int IndexForEntry(int i) {
+ return kFirstDeoptEntryIndex + (i * kDeoptEntrySize);
+ }
+
+ static int LengthFor(int entry_count) {
+ return IndexForEntry(entry_count);
+ }
+};
+
+
+// DeoptimizationOutputData is a fixed array used to hold the deoptimization
+// data for code generated by the full compiler.
+// The format of the these objects is
+// [i * 2]: Ast ID for ith deoptimization.
+// [i * 2 + 1]: PC and state of ith deoptimization
+class DeoptimizationOutputData: public FixedArray {
+ public:
+ int DeoptPoints() { return length() / 2; }
+
+ BailoutId AstId(int index) {
+ return BailoutId(Smi::cast(get(index * 2))->value());
+ }
+
+ void SetAstId(int index, BailoutId id) {
+ set(index * 2, Smi::FromInt(id.ToInt()));
+ }
+
+ Smi* PcAndState(int index) { return Smi::cast(get(1 + index * 2)); }
+ void SetPcAndState(int index, Smi* offset) { set(1 + index * 2, offset); }
+
+ static int LengthOfFixedArray(int deopt_points) {
+ return deopt_points * 2;
+ }
+
+ // Allocates a DeoptimizationOutputData.
+ MUST_USE_RESULT static MaybeObject* Allocate(int number_of_deopt_points,
+ PretenureFlag pretenure);
+
+ // Casting.
+ static inline DeoptimizationOutputData* cast(Object* obj);
+
+#if defined(OBJECT_PRINT) || defined(ENABLE_DISASSEMBLER)
+ void DeoptimizationOutputDataPrint(FILE* out);
+#endif
+};
+
+
+// Forward declaration.
+class Cell;
+class PropertyCell;
+
+// 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 TypeFeedbackId AstId(int index);
+ inline void SetAstId(int index, TypeFeedbackId id);
+
+ // Accessors for global property cells holding the cache values.
+ inline Cell* GetCell(int index);
+ inline void SetCell(int index, Cell* 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);
+
+ // The object that indicates a monomorphic state of Array with
+ // ElementsKind
+ static inline Handle<Object> MonomorphicArraySentinel(Isolate* isolate,
+ ElementsKind elements_kind);
+
+ // 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);
+
+ static const int kForInFastCaseMarker = 0;
+ static const int kForInSlowCaseMarker = 1;
+};
+
+
+// Forward declaration.
+class SafepointEntry;
+class TypeFeedbackInfo;
+
+// Code describes objects with on-the-fly generated machine code.
+class Code: public HeapObject {
+ public:
+ // Opaque data type for encapsulating code flags like kind, inline
+ // cache state, and arguments count.
+ typedef uint32_t Flags;
+
+#define NON_IC_KIND_LIST(V) \
+ V(FUNCTION) \
+ V(OPTIMIZED_FUNCTION) \
+ V(STUB) \
+ V(BUILTIN) \
+ V(REGEXP)
+
+#define IC_KIND_LIST(V) \
+ V(LOAD_IC) \
+ V(KEYED_LOAD_IC) \
+ V(CALL_IC) \
+ V(KEYED_CALL_IC) \
+ V(STORE_IC) \
+ V(KEYED_STORE_IC) \
+ V(BINARY_OP_IC) \
+ V(COMPARE_IC) \
+ V(COMPARE_NIL_IC) \
+ V(TO_BOOLEAN_IC)
+
+#define CODE_KIND_LIST(V) \
+ NON_IC_KIND_LIST(V) \
+ IC_KIND_LIST(V)
+
+ enum Kind {
+#define DEFINE_CODE_KIND_ENUM(name) name,
+ CODE_KIND_LIST(DEFINE_CODE_KIND_ENUM)
+#undef DEFINE_CODE_KIND_ENUM
+ NUMBER_OF_KINDS
+ };
+
+ // No more than 16 kinds. The value is currently encoded in four bits in
+ // Flags.
+ STATIC_ASSERT(NUMBER_OF_KINDS <= 16);
+
+ static const char* Kind2String(Kind kind);
+
+ // Types of stubs.
+ enum StubType {
+ NORMAL,
+ FIELD,
+ CONSTANT,
+ CALLBACKS,
+ INTERCEPTOR,
+ MAP_TRANSITION,
+ NONEXISTENT
+ };
+
+ enum StubHolder {
+ OWN_STUB,
+ PROTOTYPE_STUB
+ };
+
+ typedef int ExtraICState;
+
+ static const ExtraICState kNoExtraICState = 0;
+
+#ifdef ENABLE_DISASSEMBLER
+ // Printing
+ static const char* ICState2String(InlineCacheState state);
+ static const char* StubType2String(StubType type);
+ static void PrintExtraICState(FILE* out, Kind kind, ExtraICState extra);
+ inline void Disassemble(const char* name) {
+ Disassemble(name, stdout);
+ }
+ void Disassemble(const char* name, FILE* out);
+#endif // ENABLE_DISASSEMBLER
+
+ // [instruction_size]: Size of the native instructions
+ inline int instruction_size();
+ inline void set_instruction_size(int value);
+
+ // [relocation_info]: Code relocation information
+ 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)
+
+ // [type_feedback_info]: Struct containing type feedback information for
+ // unoptimized code. Optimized code can temporarily store the head of
+ // the list of code to be deoptimized during mark-compact GC.
+ // STUBs can use this slot to store arbitrary information as a Smi.
+ // Will contain either a TypeFeedbackInfo object, or JSFunction object,
+ // or undefined, or a Smi.
+ DECL_ACCESSORS(type_feedback_info, Object)
+ inline void InitializeTypeFeedbackInfoNoWriteBarrier(Object* value);
+ inline int stub_info();
+ inline void set_stub_info(int info);
+
+ // Used during GC to code a list of code objects to deoptimize.
+ inline Object* code_to_deoptimize_link();
+ inline void set_code_to_deoptimize_link(Object* value);
+ inline Object** code_to_deoptimize_link_slot();
+
+ // [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(gc_metadata, Object)
+
+ // [ic_age]: Inline caching age: the value of the Heap::global_ic_age
+ // at the moment when this object was created.
+ inline void set_ic_age(int count);
+ inline int ic_age();
+
+ // [prologue_offset]: Offset of the function prologue, used for aging
+ // FUNCTIONs and OPTIMIZED_FUNCTIONs.
+ inline int prologue_offset();
+ inline void set_prologue_offset(int offset);
+
+ // Unchecked accessors to be used during GC.
+ inline ByteArray* unchecked_relocation_info();
+
+ inline int relocation_size();
+
+ // [flags]: Various code flags.
+ inline Flags flags();
+ inline void set_flags(Flags flags);
+
+ // [flags]: Access to specific code flags.
+ inline Kind kind();
+ inline InlineCacheState ic_state(); // Only valid for IC stubs.
+ inline ExtraICState extra_ic_state(); // Only valid for IC stubs.
+
+ inline ExtraICState extended_extra_ic_state(); // Only valid for
+ // non-call IC stubs.
+ static bool needs_extended_extra_ic_state(Kind kind) {
+ // TODO(danno): This is a bit of a hack right now since there are still
+ // clients of this API that pass "extra" values in for argc. These clients
+ // should be retrofitted to used ExtendedExtraICState.
+ return kind == COMPARE_NIL_IC || kind == TO_BOOLEAN_IC;
+ }
+
+ inline StubType type(); // Only valid for monomorphic IC stubs.
+ inline int arguments_count(); // Only valid for call IC stubs.
+
+ // Testers for IC stub kinds.
+ inline bool is_inline_cache_stub();
+ inline bool is_debug_break();
+ inline bool is_load_stub() { return kind() == LOAD_IC; }
+ inline bool is_keyed_load_stub() { return kind() == KEYED_LOAD_IC; }
+ inline bool is_store_stub() { return kind() == STORE_IC; }
+ inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
+ inline bool is_call_stub() { return kind() == CALL_IC; }
+ inline bool is_keyed_call_stub() { return kind() == KEYED_CALL_IC; }
+ inline bool is_binary_op_stub() { return kind() == BINARY_OP_IC; }
+ inline bool is_compare_ic_stub() { return kind() == COMPARE_IC; }
+ inline bool is_compare_nil_ic_stub() { return kind() == COMPARE_NIL_IC; }
+ inline bool is_to_boolean_ic_stub() { return kind() == TO_BOOLEAN_IC; }
+
+ // [major_key]: For kind STUB or BINARY_OP_IC, the major key.
+ inline int major_key();
+ inline void set_major_key(int value);
+
+ // For kind STUB or ICs, tells whether or not a code object was generated by
+ // the optimizing compiler (but it may not be an optimized function).
+ bool is_crankshafted();
+ inline void set_is_crankshafted(bool 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);
+
+ // [has_deoptimization_support]: For FUNCTION kind, tells if it has
+ // deoptimization support.
+ inline bool has_deoptimization_support();
+ inline void set_has_deoptimization_support(bool value);
+
+ // [has_debug_break_slots]: For FUNCTION kind, tells if it has
+ // been compiled with debug break slots.
+ 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
+ // for.
+ inline void set_allow_osr_at_loop_nesting_level(int level);
+ inline int allow_osr_at_loop_nesting_level();
+
+ // [profiler_ticks]: For FUNCTION kind, tells for how many profiler ticks
+ // the code object was seen on the stack with no IC patching going on.
+ inline int profiler_ticks();
+ inline void set_profiler_ticks(int ticks);
+
+ // [stack_slots]: For kind OPTIMIZED_FUNCTION, the number of stack slots
+ // reserved in the code prologue.
+ inline unsigned stack_slots();
+ inline void set_stack_slots(unsigned slots);
+
+ // [safepoint_table_start]: For kind OPTIMIZED_CODE, the offset in
+ // the instruction stream where the safepoint table starts.
+ inline unsigned safepoint_table_offset();
+ inline void set_safepoint_table_offset(unsigned offset);
+
+ // [back_edge_table_start]: For kind FUNCTION, the offset in the
+ // instruction stream where the back edge table starts.
+ inline unsigned back_edge_table_offset();
+ inline void set_back_edge_table_offset(unsigned offset);
+
+ inline bool back_edges_patched_for_osr();
+ inline void set_back_edges_patched_for_osr(bool value);
+
+ // [check type]: For kind CALL_IC, tells how to check if the
+ // receiver is valid for the given call.
+ inline CheckType check_type();
+ inline void set_check_type(CheckType value);
+
+ // [to_boolean_foo]: For kind TO_BOOLEAN_IC tells what state the stub is in.
+ inline byte to_boolean_state();
+
+ // [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);
+
+
+ // [marked_for_deoptimization]: For kind OPTIMIZED_FUNCTION tells whether
+ // the code is going to be deoptimized because of dead embedded maps.
+ inline bool marked_for_deoptimization();
+ inline void set_marked_for_deoptimization(bool flag);
+
+ bool allowed_in_shared_map_code_cache();
+
+ // Get the safepoint entry for the given pc.
+ SafepointEntry GetSafepointEntry(Address pc);
+
+ // Find an object in a stub with a specified map
+ Object* FindNthObject(int n, Map* match_map);
+ void ReplaceNthObject(int n, Map* match_map, Object* replace_with);
+
+ // Find the first map in an IC stub.
+ Map* FindFirstMap();
+ void FindAllMaps(MapHandleList* maps);
+ void ReplaceFirstMap(Map* replace);
+
+ // Find the first code in an IC stub.
+ Code* FindFirstCode();
+ void FindAllCode(CodeHandleList* code_list, int length);
+
+ // Find the first name in an IC stub.
+ Name* FindFirstName();
+
+ void ReplaceNthCell(int n, Cell* replace_with);
+
+ class ExtraICStateStrictMode: public BitField<StrictModeFlag, 0, 1> {};
+ class ExtraICStateKeyedAccessStoreMode:
+ public BitField<KeyedAccessStoreMode, 1, 4> {}; // NOLINT
+
+ class ExtraICStateStubHolder: public BitField<StubHolder, 0, 1> {};
+
+ static inline StrictModeFlag GetStrictMode(ExtraICState extra_ic_state) {
+ return ExtraICStateStrictMode::decode(extra_ic_state);
+ }
+
+ static inline KeyedAccessStoreMode GetKeyedAccessStoreMode(
+ ExtraICState extra_ic_state) {
+ return ExtraICStateKeyedAccessStoreMode::decode(extra_ic_state);
+ }
+
+ static inline ExtraICState ComputeExtraICState(
+ KeyedAccessStoreMode store_mode,
+ StrictModeFlag strict_mode) {
+ return ExtraICStateKeyedAccessStoreMode::encode(store_mode) |
+ ExtraICStateStrictMode::encode(strict_mode);
+ }
+
+ static inline ExtraICState ComputeExtraICState(StubHolder stub_holder) {
+ return ExtraICStateStubHolder::encode(stub_holder);
+ }
+
+ // Flags operations.
+ static inline Flags ComputeFlags(
+ Kind kind,
+ InlineCacheState ic_state = UNINITIALIZED,
+ ExtraICState extra_ic_state = kNoExtraICState,
+ StubType type = NORMAL,
+ int argc = -1,
+ InlineCacheHolderFlag holder = OWN_MAP);
+
+ static inline Flags ComputeMonomorphicFlags(
+ Kind kind,
+ ExtraICState extra_ic_state = kNoExtraICState,
+ StubType type = NORMAL,
+ int argc = -1,
+ InlineCacheHolderFlag holder = OWN_MAP);
+
+ static inline InlineCacheState ExtractICStateFromFlags(Flags flags);
+ static inline StubType ExtractTypeFromFlags(Flags flags);
+ static inline Kind ExtractKindFromFlags(Flags flags);
+ static inline InlineCacheHolderFlag ExtractCacheHolderFromFlags(Flags flags);
+ static inline ExtraICState ExtractExtraICStateFromFlags(Flags flags);
+ static inline ExtraICState ExtractExtendedExtraICStateFromFlags(Flags flags);
+ static inline int ExtractArgumentsCountFromFlags(Flags flags);
+
+ static inline Flags RemoveTypeFromFlags(Flags flags);
+
+ // Convert a target address into a code object.
+ static inline Code* GetCodeFromTargetAddress(Address address);
+
+ // Convert an entry address into an object.
+ static inline Object* GetObjectFromEntryAddress(Address location_of_address);
+
+ // Returns the address of the first instruction.
+ inline byte* instruction_start();
+
+ // Returns the address right after the last instruction.
+ inline byte* instruction_end();
+
+ // Returns the size of the instructions, padding, and relocation information.
+ inline int body_size();
+
+ // Returns the address of the first relocation info (read backwards!).
+ inline byte* relocation_start();
+
+ // Code entry point.
+ inline byte* entry();
+
+ // Returns true if pc is inside this object's instructions.
+ inline bool contains(byte* pc);
+
+ // Relocate the code by delta bytes. Called to signal that this code
+ // object has been moved by delta bytes.
+ void Relocate(intptr_t delta);
+
+ // Migrate code described by desc.
+ void CopyFrom(const CodeDesc& desc);
+
+ // Returns the object size for a given body (used for allocation).
+ static int SizeFor(int body_size) {
+ ASSERT_SIZE_TAG_ALIGNED(body_size);
+ return RoundUp(kHeaderSize + body_size, kCodeAlignment);
+ }
+
+ // Calculate the size of the code object to report for log events. This takes
+ // the layout of the code object into account.
+ int ExecutableSize() {
+ // Check that the assumptions about the layout of the code object holds.
+ ASSERT_EQ(static_cast<int>(instruction_start() - address()),
+ Code::kHeaderSize);
+ return instruction_size() + Code::kHeaderSize;
+ }
+
+ // Locating source position.
+ int SourcePosition(Address pc);
+ int SourceStatementPosition(Address pc);
+
+ // Casting.
+ static inline Code* cast(Object* obj);
+
+ // Dispatched behavior.
+ int CodeSize() { return SizeFor(body_size()); }
+ inline void CodeIterateBody(ObjectVisitor* v);
+
+ template<typename StaticVisitor>
+ inline void CodeIterateBody(Heap* heap);
+
+ DECLARE_PRINTER(Code)
+ DECLARE_VERIFIER(Code)
+
+ void ClearInlineCaches();
+ void ClearTypeFeedbackCells(Heap* heap);
+
+#define DECLARE_CODE_AGE_ENUM(X) k##X##CodeAge,
+ enum Age {
+ kNoAge = 0,
+ CODE_AGE_LIST(DECLARE_CODE_AGE_ENUM)
+ kAfterLastCodeAge,
+ kLastCodeAge = kAfterLastCodeAge - 1,
+ kCodeAgeCount = kAfterLastCodeAge - 1
+ };
+#undef DECLARE_CODE_AGE_ENUM
+
+ // Code aging. Indicates how many full GCs this code has survived without
+ // being entered through the prologue. Used to determine when it is
+ // relatively safe to flush this code object and replace it with the lazy
+ // compilation stub.
+ static void MakeCodeAgeSequenceYoung(byte* sequence);
+ void MakeOlder(MarkingParity);
+ static bool IsYoungSequence(byte* sequence);
+ bool IsOld();
+ int GetAge();
+
+ void PrintDeoptLocation(int bailout_id);
+ bool CanDeoptAt(Address pc);
+
+#ifdef VERIFY_HEAP
+ void VerifyEmbeddedMapsDependency();
+#endif
+
+ // 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;
+
+ // Layout description.
+ static const int kInstructionSizeOffset = HeapObject::kHeaderSize;
+ static const int kRelocationInfoOffset = kInstructionSizeOffset + kIntSize;
+ static const int kHandlerTableOffset = kRelocationInfoOffset + kPointerSize;
+ static const int kDeoptimizationDataOffset =
+ kHandlerTableOffset + kPointerSize;
+ static const int kTypeFeedbackInfoOffset =
+ kDeoptimizationDataOffset + kPointerSize;
+ static const int kGCMetadataOffset = kTypeFeedbackInfoOffset + kPointerSize;
+ static const int kICAgeOffset =
+ kGCMetadataOffset + kPointerSize;
+ static const int kFlagsOffset = kICAgeOffset + kIntSize;
+ static const int kKindSpecificFlags1Offset = kFlagsOffset + kIntSize;
+ static const int kKindSpecificFlags2Offset =
+ kKindSpecificFlags1Offset + kIntSize;
+ // Note: We might be able to squeeze this into the flags above.
+ static const int kPrologueOffset = kKindSpecificFlags2Offset + kIntSize;
+
+ static const int kHeaderPaddingStart = kPrologueOffset + kIntSize;
+
+ // Add padding to align the instruction start following right after
+ // the Code object header.
+ static const int kHeaderSize =
+ (kHeaderPaddingStart + kCodeAlignmentMask) & ~kCodeAlignmentMask;
+
+ // Byte offsets within kKindSpecificFlags1Offset.
+ static const int kOptimizableOffset = kKindSpecificFlags1Offset;
+ static const int kCheckTypeOffset = kKindSpecificFlags1Offset;
+
+ 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 kAllowOSRAtLoopNestingLevelOffset = kFullCodeFlags + 1;
+ static const int kProfilerTicksOffset = kAllowOSRAtLoopNestingLevelOffset + 1;
+
+ // Flags layout. BitField<type, shift, size>.
+ class ICStateField: public BitField<InlineCacheState, 0, 3> {};
+ class TypeField: public BitField<StubType, 3, 3> {};
+ class CacheHolderField: public BitField<InlineCacheHolderFlag, 6, 1> {};
+ class KindField: public BitField<Kind, 7, 4> {};
+ class IsPregeneratedField: public BitField<bool, 11, 1> {};
+ class ExtraICStateField: public BitField<ExtraICState, 12, 5> {};
+ class ExtendedExtraICStateField: public BitField<ExtraICState, 12,
+ PlatformSmiTagging::kSmiValueSize - 12 + 1> {}; // NOLINT
+ STATIC_ASSERT(ExtraICStateField::kShift == ExtendedExtraICStateField::kShift);
+
+ // KindSpecificFlags1 layout (STUB and OPTIMIZED_FUNCTION)
+ static const int kStackSlotsFirstBit = 0;
+ static const int kStackSlotsBitCount = 24;
+ static const int kHasFunctionCacheFirstBit =
+ kStackSlotsFirstBit + kStackSlotsBitCount;
+ static const int kHasFunctionCacheBitCount = 1;
+ static const int kMarkedForDeoptimizationFirstBit =
+ kStackSlotsFirstBit + kStackSlotsBitCount + 1;
+ static const int kMarkedForDeoptimizationBitCount = 1;
+
+ STATIC_ASSERT(kStackSlotsFirstBit + kStackSlotsBitCount <= 32);
+ STATIC_ASSERT(kHasFunctionCacheFirstBit + kHasFunctionCacheBitCount <= 32);
+ STATIC_ASSERT(kMarkedForDeoptimizationFirstBit +
+ kMarkedForDeoptimizationBitCount <= 32);
+
+ class StackSlotsField: public BitField<int,
+ kStackSlotsFirstBit, kStackSlotsBitCount> {}; // NOLINT
+ class HasFunctionCacheField: public BitField<bool,
+ kHasFunctionCacheFirstBit, kHasFunctionCacheBitCount> {}; // NOLINT
+ class MarkedForDeoptimizationField: public BitField<bool,
+ kMarkedForDeoptimizationFirstBit,
+ kMarkedForDeoptimizationBitCount> {}; // NOLINT
+
+ // KindSpecificFlags2 layout (ALL)
+ static const int kIsCrankshaftedBit = 0;
+ class IsCrankshaftedField: public BitField<bool,
+ kIsCrankshaftedBit, 1> {}; // NOLINT
+
+ // KindSpecificFlags2 layout (STUB and OPTIMIZED_FUNCTION)
+ static const int kStubMajorKeyFirstBit = kIsCrankshaftedBit + 1;
+ static const int kSafepointTableOffsetFirstBit =
+ kStubMajorKeyFirstBit + kStubMajorKeyBits;
+ static const int kSafepointTableOffsetBitCount = 25;
+
+ STATIC_ASSERT(kStubMajorKeyFirstBit + kStubMajorKeyBits <= 32);
+ STATIC_ASSERT(kSafepointTableOffsetFirstBit +
+ kSafepointTableOffsetBitCount <= 32);
+ STATIC_ASSERT(1 + kStubMajorKeyBits +
+ kSafepointTableOffsetBitCount <= 32);
+
+ class SafepointTableOffsetField: public BitField<int,
+ kSafepointTableOffsetFirstBit,
+ kSafepointTableOffsetBitCount> {}; // NOLINT
+ class StubMajorKeyField: public BitField<int,
+ kStubMajorKeyFirstBit, kStubMajorKeyBits> {}; // NOLINT
+
+ // KindSpecificFlags2 layout (FUNCTION)
+ class BackEdgeTableOffsetField: public BitField<int,
+ kIsCrankshaftedBit + 1, 29> {}; // NOLINT
+ class BackEdgesPatchedForOSRField: public BitField<bool,
+ kIsCrankshaftedBit + 1 + 29, 1> {}; // NOLINT
+
+ // Signed field cannot be encoded using the BitField class.
+ static const int kArgumentsCountShift = 17;
+ static const int kArgumentsCountMask = ~((1 << kArgumentsCountShift) - 1);
+ static const int kArgumentsBits =
+ PlatformSmiTagging::kSmiValueSize - Code::kArgumentsCountShift + 1;
+ static const int kMaxArguments = (1 << kArgumentsBits) - 1;
+
+ // ICs can use either argument count or ExtendedExtraIC, since their storage
+ // overlaps.
+ STATIC_ASSERT(ExtraICStateField::kShift +
+ ExtraICStateField::kSize + kArgumentsBits ==
+ ExtendedExtraICStateField::kShift +
+ ExtendedExtraICStateField::kSize);
+
+ // This constant should be encodable in an ARM instruction.
+ static const int kFlagsNotUsedInLookup =
+ TypeField::kMask | CacheHolderField::kMask;
+
+ private:
+ friend class RelocIterator;
+
+ // Code aging
+ byte* FindCodeAgeSequence();
+ static void GetCodeAgeAndParity(Code* code, Age* age,
+ MarkingParity* parity);
+ static void GetCodeAgeAndParity(byte* sequence, Age* age,
+ MarkingParity* parity);
+ static Code* GetCodeAgeStub(Age age, MarkingParity parity);
+
+ // Code aging -- platform-specific
+ static void PatchPlatformCodeAge(byte* sequence, Age age,
+ MarkingParity parity);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Code);
+};
+
+
+class CompilationInfo;
+
+// This class describes the layout of dependent codes array of a map. The
+// array is partitioned into several groups of dependent codes. Each group
+// contains codes with the same dependency on the map. The array has the
+// following layout for n dependency groups:
+//
+// +----+----+-----+----+---------+----------+-----+---------+-----------+
+// | C1 | C2 | ... | Cn | group 1 | group 2 | ... | group n | undefined |
+// +----+----+-----+----+---------+----------+-----+---------+-----------+
+//
+// The first n elements are Smis, each of them specifies the number of codes
+// in the corresponding group. The subsequent elements contain grouped code
+// objects. The suffix of the array can be filled with the undefined value if
+// the number of codes is less than the length of the array. The order of the
+// code objects within a group is not preserved.
+//
+// All code indexes used in the class are counted starting from the first
+// code object of the first group. In other words, code index 0 corresponds
+// to array index n = kCodesStartIndex.
+
+class DependentCode: public FixedArray {
+ public:
+ enum DependencyGroup {
+ // Group of code that weakly embed this map and depend on being
+ // deoptimized when the map is garbage collected.
+ kWeaklyEmbeddedGroup,
+ // Group of code that embed a transition to this map, and depend on being
+ // deoptimized when the transition is replaced by a new version.
+ kTransitionGroup,
+ // Group of code that omit run-time prototype checks for prototypes
+ // described by this map. The group is deoptimized whenever an object
+ // described by this map changes shape (and transitions to a new map),
+ // possibly invalidating the assumptions embedded in the code.
+ kPrototypeCheckGroup,
+ // Group of code that depends on elements not being added to objects with
+ // this map.
+ kElementsCantBeAddedGroup,
+ // Group of code that depends on global property values in property cells
+ // not being changed.
+ kPropertyCellChangedGroup,
+ kGroupCount = kPropertyCellChangedGroup + 1
+ };
+
+ // Array for holding the index of the first code object of each group.
+ // The last element stores the total number of code objects.
+ class GroupStartIndexes {
+ public:
+ explicit GroupStartIndexes(DependentCode* entries);
+ void Recompute(DependentCode* entries);
+ int at(int i) { return start_indexes_[i]; }
+ int number_of_entries() { return start_indexes_[kGroupCount]; }
+ private:
+ int start_indexes_[kGroupCount + 1];
+ };
+
+ bool Contains(DependencyGroup group, Code* code);
+ static Handle<DependentCode> Insert(Handle<DependentCode> entries,
+ DependencyGroup group,
+ Handle<Object> object);
+ void UpdateToFinishedCode(DependencyGroup group,
+ CompilationInfo* info,
+ Code* code);
+ void RemoveCompilationInfo(DependentCode::DependencyGroup group,
+ CompilationInfo* info);
+
+ void DeoptimizeDependentCodeGroup(Isolate* isolate,
+ DependentCode::DependencyGroup group);
+
+ // The following low-level accessors should only be used by this class
+ // and the mark compact collector.
+ inline int number_of_entries(DependencyGroup group);
+ inline void set_number_of_entries(DependencyGroup group, int value);
+ inline bool is_code_at(int i);
+ inline Code* code_at(int i);
+ inline CompilationInfo* compilation_info_at(int i);
+ inline void set_object_at(int i, Object* object);
+ inline Object** slot_at(int i);
+ inline Object* object_at(int i);
+ inline void clear_at(int i);
+ inline void copy(int from, int to);
+ static inline DependentCode* cast(Object* object);
+
+ static DependentCode* ForObject(Handle<HeapObject> object,
+ DependencyGroup group);
+
+ private:
+ // Make a room at the end of the given group by moving out the first
+ // code objects of the subsequent groups.
+ inline void ExtendGroup(DependencyGroup group);
+ static const int kCodesStartIndex = kGroupCount;
+};
+
+
+// All heap objects have a Map that describes their structure.
+// A Map contains information about:
+// - Size information about the object
+// - How to iterate over an object (for garbage collection)
+class Map: public HeapObject {
+ public:
+ // Instance size.
+ // Size in bytes or kVariableSizeSentinel if instances do not have
+ // a fixed size.
+ inline int instance_size();
+ inline void set_instance_size(int value);
+
+ // Count of properties allocated in the object.
+ inline int inobject_properties();
+ inline void set_inobject_properties(int value);
+
+ // Count of property fields pre-allocated in the object when first allocated.
+ inline int pre_allocated_property_fields();
+ inline void set_pre_allocated_property_fields(int value);
+
+ // Instance type.
+ inline InstanceType instance_type();
+ inline void set_instance_type(InstanceType value);
+
+ // Tells how many unused property fields are available in the
+ // instance (only used for JSObject in fast mode).
+ inline int unused_property_fields();
+ inline void set_unused_property_fields(int value);
+
+ // Bit field.
+ inline byte bit_field();
+ inline void set_bit_field(byte value);
+
+ // Bit field 2.
+ inline byte bit_field2();
+ inline void set_bit_field2(byte value);
+
+ // Bit field 3.
+ inline uint32_t bit_field3();
+ inline void set_bit_field3(uint32_t bits);
+
+ class EnumLengthBits: public BitField<int, 0, 11> {};
+ class NumberOfOwnDescriptorsBits: public BitField<int, 11, 11> {};
+ class IsShared: public BitField<bool, 22, 1> {};
+ class FunctionWithPrototype: public BitField<bool, 23, 1> {};
+ class DictionaryMap: public BitField<bool, 24, 1> {};
+ class OwnsDescriptors: public BitField<bool, 25, 1> {};
+ class IsObserved: public BitField<bool, 26, 1> {};
+ class Deprecated: public BitField<bool, 27, 1> {};
+ class IsFrozen: public BitField<bool, 28, 1> {};
+ class IsUnstable: public BitField<bool, 29, 1> {};
+ class IsMigrationTarget: public BitField<bool, 30, 1> {};
+
+ // Tells whether the object in the prototype property will be used
+ // for instances created from this function. If the prototype
+ // property is set to a value that is not a JSObject, the prototype
+ // property will not be used to create instances of the function.
+ // See ECMA-262, 13.2.2.
+ inline void set_non_instance_prototype(bool value);
+ inline bool has_non_instance_prototype();
+
+ // Tells whether function has special prototype property. If not, prototype
+ // property will not be created when accessed (will return undefined),
+ // and construction from this function will not be allowed.
+ inline void set_function_with_prototype(bool value);
+ inline bool function_with_prototype();
+
+ // Tells whether the instance with this map should be ignored by the
+ // Object.getPrototypeOf() function and the __proto__ accessor.
+ inline void set_is_hidden_prototype() {
+ set_bit_field(bit_field() | (1 << kIsHiddenPrototype));
+ }
+
+ inline bool is_hidden_prototype() {
+ return ((1 << kIsHiddenPrototype) & bit_field()) != 0;
+ }
+
+ // Records and queries whether the instance has a named interceptor.
+ inline void set_has_named_interceptor() {
+ set_bit_field(bit_field() | (1 << kHasNamedInterceptor));
+ }
+
+ inline bool has_named_interceptor() {
+ return ((1 << kHasNamedInterceptor) & bit_field()) != 0;
+ }
+
+ // Records and queries whether the instance has an indexed interceptor.
+ inline void set_has_indexed_interceptor() {
+ set_bit_field(bit_field() | (1 << kHasIndexedInterceptor));
+ }
+
+ inline bool has_indexed_interceptor() {
+ return ((1 << kHasIndexedInterceptor) & bit_field()) != 0;
+ }
+
+ // Tells whether the instance is undetectable.
+ // An undetectable object is a special class of JSObject: 'typeof' operator
+ // returns undefined, ToBoolean returns false. Otherwise it behaves like
+ // a normal JS object. It is useful for implementing undetectable
+ // document.all in Firefox & Safari.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=248549.
+ inline void set_is_undetectable() {
+ set_bit_field(bit_field() | (1 << kIsUndetectable));
+ }
+
+ inline bool is_undetectable() {
+ return ((1 << kIsUndetectable) & bit_field()) != 0;
+ }
+
+ // Tells whether the instance has a call-as-function handler.
+ inline void set_has_instance_call_handler() {
+ set_bit_field(bit_field() | (1 << kHasInstanceCallHandler));
+ }
+
+ inline bool has_instance_call_handler() {
+ return ((1 << kHasInstanceCallHandler) & bit_field()) != 0;
+ }
+
+ inline void set_is_extensible(bool value);
+ inline bool is_extensible();
+
+ inline void set_elements_kind(ElementsKind elements_kind) {
+ ASSERT(elements_kind < kElementsKindCount);
+ ASSERT(kElementsKindCount <= (1 << kElementsKindBitCount));
+ ASSERT(!is_observed() ||
+ elements_kind == DICTIONARY_ELEMENTS ||
+ elements_kind == NON_STRICT_ARGUMENTS_ELEMENTS ||
+ IsExternalArrayElementsKind(elements_kind));
+ set_bit_field2((bit_field2() & ~kElementsKindMask) |
+ (elements_kind << kElementsKindShift));
+ ASSERT(this->elements_kind() == elements_kind);
+ }
+
+ inline ElementsKind elements_kind() {
+ return static_cast<ElementsKind>(
+ (bit_field2() & kElementsKindMask) >> kElementsKindShift);
+ }
+
+ // Tells whether the instance has fast elements that are only Smis.
+ inline bool has_fast_smi_elements() {
+ return IsFastSmiElementsKind(elements_kind());
+ }
+
+ // Tells whether the instance has fast elements.
+ inline bool has_fast_object_elements() {
+ return IsFastObjectElementsKind(elements_kind());
+ }
+
+ inline bool has_fast_smi_or_object_elements() {
+ return IsFastSmiOrObjectElementsKind(elements_kind());
+ }
+
+ inline bool has_fast_double_elements() {
+ return IsFastDoubleElementsKind(elements_kind());
+ }
+
+ inline bool has_fast_elements() {
+ return IsFastElementsKind(elements_kind());
+ }
+
+ inline bool has_non_strict_arguments_elements() {
+ return elements_kind() == NON_STRICT_ARGUMENTS_ELEMENTS;
+ }
+
+ inline bool has_external_array_elements() {
+ return IsExternalArrayElementsKind(elements_kind());
+ }
+
+ inline bool has_dictionary_elements() {
+ return IsDictionaryElementsKind(elements_kind());
+ }
+
+ 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);
+
+ inline bool HasTransitionArray();
+ inline bool HasElementsTransition();
+ inline Map* elements_transition_map();
+ MUST_USE_RESULT inline MaybeObject* set_elements_transition_map(
+ Map* transitioned_map);
+ inline void SetTransition(int transition_index, Map* target);
+ inline Map* GetTransition(int transition_index);
+ MUST_USE_RESULT inline MaybeObject* AddTransition(Name* key,
+ Map* target,
+ SimpleTransitionFlag flag);
+ DECL_ACCESSORS(transitions, TransitionArray)
+ inline void ClearTransitions(Heap* heap,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+
+ void DeprecateTransitionTree();
+ void DeprecateTarget(Name* key, DescriptorArray* new_descriptors);
+
+ Map* FindRootMap();
+ Map* FindUpdatedMap(int verbatim, int length, DescriptorArray* descriptors);
+ Map* FindLastMatchMap(int verbatim, int length, DescriptorArray* descriptors);
+
+ int NumberOfFields();
+
+ bool InstancesNeedRewriting(Map* target,
+ int target_number_of_fields,
+ int target_inobject,
+ int target_unused);
+ static Handle<Map> GeneralizeRepresentation(
+ Handle<Map> map,
+ int modify_index,
+ Representation new_representation);
+ MUST_USE_RESULT MaybeObject* GeneralizeRepresentation(
+ int modify_index,
+ Representation representation);
+ MUST_USE_RESULT MaybeObject* CopyGeneralizeAllRepresentations();
+
+ // Tells whether the map is attached to SharedFunctionInfo
+ // (for inobject slack tracking).
+ inline void set_attached_to_shared_function_info(bool value);
+
+ inline bool attached_to_shared_function_info();
+
+ // Tells whether the map is shared between objects that may have different
+ // behavior. If true, the map should never be modified, instead a clone
+ // should be created and modified.
+ inline void set_is_shared(bool value);
+ inline bool is_shared();
+
+ // Tells whether the map is used for JSObjects in dictionary mode (ie
+ // normalized objects, ie objects for which HasFastProperties returns false).
+ // A map can never be used for both dictionary mode and fast mode JSObjects.
+ // False by default and for HeapObjects that are not JSObjects.
+ inline void set_dictionary_map(bool value);
+ inline bool is_dictionary_map();
+
+ // Tells whether the instance needs security checks when accessing its
+ // properties.
+ inline void set_is_access_check_needed(bool access_check_needed);
+ inline bool is_access_check_needed();
+
+ // Returns true if map has a non-empty stub code cache.
+ inline bool has_code_cache();
+
+ // [prototype]: implicit prototype object.
+ DECL_ACCESSORS(prototype, Object)
+
+ // [constructor]: points back to the function responsible for this map.
+ DECL_ACCESSORS(constructor, Object)
+
+ // [instance descriptors]: describes the object.
+ DECL_ACCESSORS(instance_descriptors, DescriptorArray)
+ inline void InitializeDescriptors(DescriptorArray* descriptors);
+
+ // [stub cache]: contains stubs compiled for this map.
+ DECL_ACCESSORS(code_cache, Object)
+
+ // [dependent code]: list of optimized codes that have this map embedded.
+ DECL_ACCESSORS(dependent_code, DependentCode)
+
+ // [back pointer]: points back to the parent map from which a transition
+ // leads to this map. The field overlaps with prototype transitions and the
+ // back pointer will be moved into the prototype transitions array if
+ // required.
+ inline Object* GetBackPointer();
+ inline void SetBackPointer(Object* value,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+ inline void init_back_pointer(Object* undefined);
+
+ // [prototype transitions]: cache of prototype transitions.
+ // Prototype transition is a transition that happens
+ // when we change object's prototype to a new one.
+ // Cache format:
+ // 0: finger - index of the first free cell in the cache
+ // 1: back pointer that overlaps with prototype transitions field.
+ // 2 + 2 * i: prototype
+ // 3 + 2 * i: target map
+ inline FixedArray* GetPrototypeTransitions();
+ MUST_USE_RESULT inline MaybeObject* SetPrototypeTransitions(
+ FixedArray* prototype_transitions);
+ inline bool HasPrototypeTransitions();
+
+ inline HeapObject* UncheckedPrototypeTransitions();
+ inline TransitionArray* unchecked_transition_array();
+
+ static const int kProtoTransitionHeaderSize = 1;
+ static const int kProtoTransitionNumberOfEntriesOffset = 0;
+ static const int kProtoTransitionElementsPerEntry = 2;
+ static const int kProtoTransitionPrototypeOffset = 0;
+ static const int kProtoTransitionMapOffset = 1;
+
+ inline int NumberOfProtoTransitions() {
+ FixedArray* cache = GetPrototypeTransitions();
+ if (cache->length() == 0) return 0;
+ return
+ Smi::cast(cache->get(kProtoTransitionNumberOfEntriesOffset))->value();
+ }
+
+ inline void SetNumberOfProtoTransitions(int value) {
+ FixedArray* cache = GetPrototypeTransitions();
+ ASSERT(cache->length() != 0);
+ cache->set(kProtoTransitionNumberOfEntriesOffset, Smi::FromInt(value));
+ }
+
+ // Lookup in the map's instance descriptors and fill out the result
+ // with the given holder if the name is found. The holder may be
+ // NULL when this function is used from the compiler.
+ inline void LookupDescriptor(JSObject* holder,
+ Name* name,
+ LookupResult* result);
+
+ inline void LookupTransition(JSObject* holder,
+ Name* name,
+ LookupResult* result);
+
+ // The size of transition arrays are limited so they do not end up in large
+ // object space. Otherwise ClearNonLiveTransitions would leak memory while
+ // applying in-place right trimming.
+ inline bool CanHaveMoreTransitions();
+
+ int LastAdded() {
+ int number_of_own_descriptors = NumberOfOwnDescriptors();
+ ASSERT(number_of_own_descriptors > 0);
+ return number_of_own_descriptors - 1;
+ }
+
+ int NumberOfOwnDescriptors() {
+ return NumberOfOwnDescriptorsBits::decode(bit_field3());
+ }
+
+ void SetNumberOfOwnDescriptors(int number) {
+ ASSERT(number <= instance_descriptors()->number_of_descriptors());
+ set_bit_field3(NumberOfOwnDescriptorsBits::update(bit_field3(), number));
+ }
+
+ inline Cell* RetrieveDescriptorsPointer();
+
+ int EnumLength() {
+ return EnumLengthBits::decode(bit_field3());
+ }
+
+ void SetEnumLength(int length) {
+ if (length != kInvalidEnumCache) {
+ ASSERT(length >= 0);
+ ASSERT(length == 0 || instance_descriptors()->HasEnumCache());
+ ASSERT(length <= NumberOfOwnDescriptors());
+ }
+ set_bit_field3(EnumLengthBits::update(bit_field3(), length));
+ }
+
+ inline bool CanTrackAllocationSite();
+ inline bool owns_descriptors();
+ inline void set_owns_descriptors(bool is_shared);
+ inline bool is_observed();
+ inline void set_is_observed(bool is_observed);
+ inline void freeze();
+ inline bool is_frozen();
+ inline void mark_unstable();
+ inline bool is_stable();
+ inline void set_migration_target(bool value);
+ inline bool is_migration_target();
+ inline void deprecate();
+ inline bool is_deprecated();
+ inline bool CanBeDeprecated();
+ // Returns a non-deprecated version of the input. If the input was not
+ // deprecated, it is directly returned. Otherwise, the non-deprecated version
+ // is found by re-transitioning from the root of the transition tree using the
+ // descriptor array of the map. Returns NULL if no updated map is found.
+ Map* CurrentMapForDeprecated();
+
+ MUST_USE_RESULT MaybeObject* RawCopy(int instance_size);
+ MUST_USE_RESULT MaybeObject* CopyWithPreallocatedFieldDescriptors();
+ static Handle<Map> CopyDropDescriptors(Handle<Map> map);
+ MUST_USE_RESULT MaybeObject* CopyDropDescriptors();
+ MUST_USE_RESULT MaybeObject* CopyReplaceDescriptors(
+ DescriptorArray* descriptors,
+ TransitionFlag flag,
+ Name* name = NULL,
+ SimpleTransitionFlag simple_flag = FULL_TRANSITION);
+ MUST_USE_RESULT MaybeObject* CopyInstallDescriptors(
+ int new_descriptor,
+ DescriptorArray* descriptors);
+ MUST_USE_RESULT MaybeObject* ShareDescriptor(DescriptorArray* descriptors,
+ Descriptor* descriptor);
+ MUST_USE_RESULT MaybeObject* CopyAddDescriptor(Descriptor* descriptor,
+ TransitionFlag flag);
+ MUST_USE_RESULT MaybeObject* CopyInsertDescriptor(Descriptor* descriptor,
+ TransitionFlag flag);
+ MUST_USE_RESULT MaybeObject* CopyReplaceDescriptor(
+ DescriptorArray* descriptors,
+ Descriptor* descriptor,
+ int index,
+ TransitionFlag flag);
+ MUST_USE_RESULT MaybeObject* AsElementsKind(ElementsKind kind);
+
+ MUST_USE_RESULT MaybeObject* CopyAsElementsKind(ElementsKind kind,
+ TransitionFlag flag);
+ MUST_USE_RESULT MaybeObject* CopyForObserved();
+
+ MUST_USE_RESULT MaybeObject* CopyNormalized(PropertyNormalizationMode mode,
+ NormalizedMapSharingMode sharing);
+
+ inline void AppendDescriptor(Descriptor* desc,
+ const DescriptorArray::WhitenessWitness&);
+
+ // Returns a copy of the map, with all transitions dropped from the
+ // instance descriptors.
+ static Handle<Map> Copy(Handle<Map> map);
+ MUST_USE_RESULT MaybeObject* Copy();
+
+ // Returns the next free property index (only valid for FAST MODE).
+ int NextFreePropertyIndex();
+
+ // Returns the number of properties described in instance_descriptors
+ // filtering out properties with the specified attributes.
+ int NumberOfDescribedProperties(DescriptorFlag which = OWN_DESCRIPTORS,
+ PropertyAttributes filter = NONE);
+
+ // Returns the number of slots allocated for the initial properties
+ // backing storage for instances of this map.
+ int InitialPropertiesLength() {
+ return pre_allocated_property_fields() + unused_property_fields() -
+ inobject_properties();
+ }
+
+ // Casting.
+ static inline Map* cast(Object* obj);
+
+ // Locate an accessor in the instance descriptor.
+ AccessorDescriptor* FindAccessor(Name* name);
+
+ // Code cache operations.
+
+ // Clears the code cache.
+ inline void ClearCodeCache(Heap* heap);
+
+ // Update code cache.
+ static void UpdateCodeCache(Handle<Map> map,
+ Handle<Name> name,
+ Handle<Code> code);
+ MUST_USE_RESULT MaybeObject* UpdateCodeCache(Name* name, Code* code);
+
+ // Extend the descriptor array of the map with the list of descriptors.
+ // In case of duplicates, the latest descriptor is used.
+ static void AppendCallbackDescriptors(Handle<Map> map,
+ Handle<Object> descriptors);
+
+ static void EnsureDescriptorSlack(Handle<Map> map, int slack);
+
+ // Returns the found code or undefined if absent.
+ Object* FindInCodeCache(Name* name, Code::Flags flags);
+
+ // Returns the non-negative index of the code object if it is in the
+ // cache and -1 otherwise.
+ int IndexInCodeCache(Object* name, Code* code);
+
+ // Removes a code object from the code cache at the given index.
+ void RemoveFromCodeCache(Name* name, Code* code, int index);
+
+ // Set all map transitions from this map to dead maps to null. Also clear
+ // back pointers in transition targets so that we do not process this map
+ // again while following back pointers.
+ void ClearNonLiveTransitions(Heap* heap);
+
+ // Computes a hash value for this map, to be used in HashTables and such.
+ int Hash();
+
+ bool EquivalentToForTransition(Map* other);
+
+ // Compares this map to another to see if they describe equivalent objects.
+ // If |mode| is set to CLEAR_INOBJECT_PROPERTIES, |other| is treated as if
+ // it had exactly zero inobject properties.
+ // The "shared" flags of both this map and |other| are ignored.
+ bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode mode);
+
+ // 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);
+
+ // 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);
+
+ // Zaps the contents of backing data structures. Note that the
+ // heap verifier (i.e. VerifyMarkingVisitor) relies on zapping of objects
+ // holding weak references when incremental marking is used, because it also
+ // iterates over objects that are otherwise unreachable.
+ // In general we only want to call these functions in release mode when
+ // heap verification is turned on.
+ void ZapPrototypeTransitions();
+ void ZapTransitions();
+
+ bool CanTransition() {
+ // Only JSObject and subtypes have map transitions and back pointers.
+ STATIC_ASSERT(LAST_TYPE == LAST_JS_OBJECT_TYPE);
+ return instance_type() >= FIRST_JS_OBJECT_TYPE;
+ }
+
+ // Fires when the layout of an object with a leaf map changes.
+ // This includes adding transitions to the leaf map or changing
+ // the descriptor array.
+ inline void NotifyLeafMapLayoutChange();
+
+ inline bool CanOmitMapChecks();
+
+ void AddDependentCompilationInfo(DependentCode::DependencyGroup group,
+ CompilationInfo* info);
+
+ void AddDependentCode(DependentCode::DependencyGroup group,
+ Handle<Code> code);
+
+ bool IsMapInArrayPrototypeChain();
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(Map)
+ DECLARE_VERIFIER(Map)
+
+#ifdef VERIFY_HEAP
+ void SharedMapVerify();
+ void VerifyOmittedMapChecks();
+#endif
+
+ inline int visitor_id();
+ inline void set_visitor_id(int visitor_id);
+
+ typedef void (*TraverseCallback)(Map* map, void* data);
+
+ void TraverseTransitionTree(TraverseCallback callback, void* data);
+
+ // When you set the prototype of an object using the __proto__ accessor you
+ // need a new map for the object (the prototype is stored in the map). In
+ // order not to multiply maps unnecessarily we store these as transitions in
+ // the original map. That way we can transition to the same map if the same
+ // prototype is set, rather than creating a new map every time. The
+ // transitions are in the form of a map where the keys are prototype objects
+ // and the values are the maps the are transitioned to.
+ static const int kMaxCachedPrototypeTransitions = 256;
+ static Handle<Map> GetPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype);
+ static Handle<Map> PutPrototypeTransition(Handle<Map> map,
+ Handle<Object> prototype,
+ Handle<Map> target_map);
+
+ static const int kMaxPreAllocatedPropertyFields = 255;
+
+ // Constant for denoting that the enum cache is not yet initialized.
+ static const int kInvalidEnumCache = EnumLengthBits::kMax;
+
+ // Layout description.
+ static const int kInstanceSizesOffset = HeapObject::kHeaderSize;
+ static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize;
+ static const int kPrototypeOffset = kInstanceAttributesOffset + kIntSize;
+ static const int kConstructorOffset = kPrototypeOffset + kPointerSize;
+ // Storage for the transition array is overloaded to directly contain a back
+ // pointer if unused. When the map has transitions, the back pointer is
+ // transferred to the transition array and accessed through an extra
+ // indirection.
+ static const int kTransitionsOrBackPointerOffset =
+ kConstructorOffset + kPointerSize;
+ static const int kDescriptorsOffset =
+ kTransitionsOrBackPointerOffset + kPointerSize;
+ static const int kCodeCacheOffset = kDescriptorsOffset + kPointerSize;
+ static const int kDependentCodeOffset = kCodeCacheOffset + kPointerSize;
+ static const int kBitField3Offset = kDependentCodeOffset + kPointerSize;
+ static const int kSize = kBitField3Offset + kPointerSize;
+
+ // Layout of pointer fields. Heap iteration code relies on them
+ // being continuously allocated.
+ static const int kPointerFieldsBeginOffset = Map::kPrototypeOffset;
+ static const int kPointerFieldsEndOffset = kBitField3Offset + kPointerSize;
+
+ // Byte offsets within kInstanceSizesOffset.
+ static const int kInstanceSizeOffset = kInstanceSizesOffset + 0;
+ static const int kInObjectPropertiesByte = 1;
+ static const int kInObjectPropertiesOffset =
+ kInstanceSizesOffset + kInObjectPropertiesByte;
+ static const int kPreAllocatedPropertyFieldsByte = 2;
+ static const int kPreAllocatedPropertyFieldsOffset =
+ kInstanceSizesOffset + kPreAllocatedPropertyFieldsByte;
+ static const int kVisitorIdByte = 3;
+ static const int kVisitorIdOffset = kInstanceSizesOffset + kVisitorIdByte;
+
+ // Byte offsets within kInstanceAttributesOffset attributes.
+ static const int kInstanceTypeOffset = kInstanceAttributesOffset + 0;
+ static const int kUnusedPropertyFieldsOffset = kInstanceAttributesOffset + 1;
+ static const int kBitFieldOffset = kInstanceAttributesOffset + 2;
+ static const int kBitField2Offset = kInstanceAttributesOffset + 3;
+
+ STATIC_CHECK(kInstanceTypeOffset == Internals::kMapInstanceTypeOffset);
+
+ // Bit positions for bit field.
+ static const int kUnused = 0; // To be used for marking recently used maps.
+ static const int kHasNonInstancePrototype = 1;
+ static const int kIsHiddenPrototype = 2;
+ static const int kHasNamedInterceptor = 3;
+ static const int kHasIndexedInterceptor = 4;
+ static const int kIsUndetectable = 5;
+ static const int kHasInstanceCallHandler = 6;
+ static const int kIsAccessCheckNeeded = 7;
+
+ // Bit positions for bit field 2
+ static const int kIsExtensible = 0;
+ static const int kStringWrapperSafeForDefaultValueOf = 1;
+ static const int kAttachedToSharedFunctionInfo = 2;
+ // No bits can be used after kElementsKindFirstBit, they are all reserved for
+ // storing ElementKind.
+ static const int kElementsKindShift = 3;
+ static const int kElementsKindBitCount = 5;
+
+ // Derived values from bit field 2
+ static const int kElementsKindMask = (-1 << kElementsKindShift) &
+ ((1 << (kElementsKindShift + kElementsKindBitCount)) - 1);
+ static const int8_t kMaximumBitField2FastElementValue = static_cast<int8_t>(
+ (FAST_ELEMENTS + 1) << Map::kElementsKindShift) - 1;
+ static const int8_t kMaximumBitField2FastSmiElementValue =
+ static_cast<int8_t>((FAST_SMI_ELEMENTS + 1) <<
+ Map::kElementsKindShift) - 1;
+ static const int8_t kMaximumBitField2FastHoleyElementValue =
+ static_cast<int8_t>((FAST_HOLEY_ELEMENTS + 1) <<
+ Map::kElementsKindShift) - 1;
+ static const int8_t kMaximumBitField2FastHoleySmiElementValue =
+ static_cast<int8_t>((FAST_HOLEY_SMI_ELEMENTS + 1) <<
+ Map::kElementsKindShift) - 1;
+
+ typedef FixedBodyDescriptor<kPointerFieldsBeginOffset,
+ kPointerFieldsEndOffset,
+ kSize> BodyDescriptor;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Map);
+};
+
+
+// An abstract superclass, a marker class really, for simple structure classes.
+// It doesn't carry much functionality but allows struct classes to be
+// identified in the type system.
+class Struct: public HeapObject {
+ public:
+ inline void InitializeBody(int object_size);
+ static inline Struct* cast(Object* that);
+};
+
+
+// A simple one-element struct, useful where smis need to be boxed.
+class Box : public Struct {
+ public:
+ // [value]: the boxed contents.
+ DECL_ACCESSORS(value, Object)
+
+ static inline Box* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(Box)
+ DECLARE_VERIFIER(Box)
+
+ static const int kValueOffset = HeapObject::kHeaderSize;
+ static const int kSize = kValueOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Box);
+};
+
+
+// Script describes a script which has been added to the VM.
+class Script: public Struct {
+ public:
+ // Script types.
+ enum Type {
+ TYPE_NATIVE = 0,
+ TYPE_EXTENSION = 1,
+ TYPE_NORMAL = 2
+ };
+
+ // Script compilation types.
+ enum CompilationType {
+ COMPILATION_TYPE_HOST = 0,
+ COMPILATION_TYPE_EVAL = 1
+ };
+
+ // Script compilation state.
+ enum CompilationState {
+ COMPILATION_STATE_INITIAL = 0,
+ COMPILATION_STATE_COMPILED = 1
+ };
+
+ // [source]: the script source.
+ DECL_ACCESSORS(source, Object)
+
+ // [name]: the script name.
+ DECL_ACCESSORS(name, Object)
+
+ // [id]: the script id.
+ DECL_ACCESSORS(id, Smi)
+
+ // [line_offset]: script line offset in resource from where it was extracted.
+ DECL_ACCESSORS(line_offset, Smi)
+
+ // [column_offset]: script column offset in resource from where it was
+ // extracted.
+ DECL_ACCESSORS(column_offset, Smi)
+
+ // [data]: additional data associated with this script.
+ DECL_ACCESSORS(data, Object)
+
+ // [context_data]: context data for the context this script was compiled in.
+ DECL_ACCESSORS(context_data, Object)
+
+ // [wrapper]: the wrapper cache.
+ DECL_ACCESSORS(wrapper, Foreign)
+
+ // [type]: the script type.
+ DECL_ACCESSORS(type, Smi)
+
+ // [line_ends]: FixedArray of line ends positions.
+ DECL_ACCESSORS(line_ends, Object)
+
+ // [eval_from_shared]: for eval scripts the shared funcion info for the
+ // function from which eval was called.
+ DECL_ACCESSORS(eval_from_shared, Object)
+
+ // [eval_from_instructions_offset]: the instruction offset in the code for the
+ // function from which eval was called where eval was called.
+ DECL_ACCESSORS(eval_from_instructions_offset, Smi)
+
+ // [flags]: Holds an exciting bitfield.
+ DECL_ACCESSORS(flags, Smi)
+
+ // [compilation_type]: how the the script was compiled. Encoded in the
+ // 'flags' field.
+ inline CompilationType compilation_type();
+ inline void set_compilation_type(CompilationType type);
+
+ // [compilation_state]: determines whether the script has already been
+ // compiled. Encoded in the 'flags' field.
+ inline CompilationState compilation_state();
+ inline void set_compilation_state(CompilationState state);
+
+ // [is_shared_cross_origin]: An opaque boolean set by the embedder via
+ // ScriptOrigin, and used by the embedder to make decisions about the
+ // script's level of privilege. V8 just passes this through. Encoded in
+ // the 'flags' field.
+ DECL_BOOLEAN_ACCESSORS(is_shared_cross_origin)
+
+ static inline Script* cast(Object* obj);
+
+ // If script source is an external string, check that the underlying
+ // resource is accessible. Otherwise, always return true.
+ inline bool HasValidSource();
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(Script)
+ DECLARE_VERIFIER(Script)
+
+ static const int kSourceOffset = HeapObject::kHeaderSize;
+ static const int kNameOffset = kSourceOffset + kPointerSize;
+ static const int kLineOffsetOffset = kNameOffset + kPointerSize;
+ static const int kColumnOffsetOffset = kLineOffsetOffset + kPointerSize;
+ static const int kDataOffset = kColumnOffsetOffset + kPointerSize;
+ static const int kContextOffset = kDataOffset + kPointerSize;
+ static const int kWrapperOffset = kContextOffset + kPointerSize;
+ static const int kTypeOffset = kWrapperOffset + kPointerSize;
+ static const int kLineEndsOffset = kTypeOffset + kPointerSize;
+ static const int kIdOffset = kLineEndsOffset + kPointerSize;
+ static const int kEvalFromSharedOffset = kIdOffset + kPointerSize;
+ static const int kEvalFrominstructionsOffsetOffset =
+ kEvalFromSharedOffset + kPointerSize;
+ static const int kFlagsOffset =
+ kEvalFrominstructionsOffsetOffset + kPointerSize;
+ static const int kSize = kFlagsOffset + kPointerSize;
+
+ private:
+ // Bit positions in the flags field.
+ static const int kCompilationTypeBit = 0;
+ static const int kCompilationStateBit = 1;
+ static const int kIsSharedCrossOriginBit = 2;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Script);
+};
+
+
+// List of builtin functions we want to identify to improve code
+// generation.
+//
+// Each entry has a name of a global object property holding an object
+// optionally followed by ".prototype", a name of a builtin function
+// on the object (the one the id is set for), and a label.
+//
+// Installation of ids for the selected builtin functions is handled
+// by the bootstrapper.
+//
+// NOTE: Order is important: math functions should be at the end of
+// the list and MathFloor should be the first math function.
+#define FUNCTIONS_WITH_ID_LIST(V) \
+ V(Array.prototype, push, ArrayPush) \
+ V(Array.prototype, pop, ArrayPop) \
+ V(Function.prototype, apply, FunctionApply) \
+ V(String.prototype, charCodeAt, StringCharCodeAt) \
+ V(String.prototype, charAt, StringCharAt) \
+ V(String, fromCharCode, StringFromCharCode) \
+ V(Math, floor, MathFloor) \
+ V(Math, round, MathRound) \
+ V(Math, ceil, MathCeil) \
+ V(Math, abs, MathAbs) \
+ V(Math, log, MathLog) \
+ V(Math, sin, MathSin) \
+ V(Math, cos, MathCos) \
+ V(Math, tan, MathTan) \
+ V(Math, asin, MathASin) \
+ V(Math, acos, MathACos) \
+ V(Math, atan, MathATan) \
+ V(Math, exp, MathExp) \
+ V(Math, sqrt, MathSqrt) \
+ V(Math, pow, MathPow) \
+ V(Math, random, MathRandom) \
+ V(Math, max, MathMax) \
+ V(Math, min, MathMin) \
+ V(Math, imul, MathImul)
+
+enum BuiltinFunctionId {
+ kArrayCode,
+#define DECLARE_FUNCTION_ID(ignored1, ignore2, name) \
+ k##name,
+ FUNCTIONS_WITH_ID_LIST(DECLARE_FUNCTION_ID)
+#undef DECLARE_FUNCTION_ID
+ // Fake id for a special case of Math.pow. Note, it continues the
+ // list of math functions.
+ kMathPowHalf,
+ kFirstMathFunctionId = kMathFloor
+};
+
+
+// SharedFunctionInfo describes the JSFunction information that can be
+// shared by multiple instances of the function.
+class SharedFunctionInfo: public HeapObject {
+ public:
+ // [name]: Function name.
+ DECL_ACCESSORS(name, Object)
+
+ // [code]: Function code.
+ DECL_ACCESSORS(code, Code)
+ inline void ReplaceCode(Code* code);
+
+ // [optimized_code_map]: Map from native context to optimized code
+ // and a shared literals array or Smi(0) if none.
+ DECL_ACCESSORS(optimized_code_map, Object)
+
+ // Returns index i of the entry with the specified context. At position
+ // i - 1 is the context, position i the code, and i + 1 the literals array.
+ // Returns -1 when no matching entry is found.
+ int SearchOptimizedCodeMap(Context* native_context);
+
+ // Installs optimized code from the code map on the given closure. The
+ // index has to be consistent with a search result as defined above.
+ void InstallFromOptimizedCodeMap(JSFunction* function, int index);
+
+ // Clear optimized code map.
+ void ClearOptimizedCodeMap();
+
+ // Removed a specific optimized code object from the optimized code map.
+ void EvictFromOptimizedCodeMap(Code* optimized_code, const char* reason);
+
+ // Trims the optimized code map after entries have been removed.
+ void TrimOptimizedCodeMap(int shrink_by);
+
+ // Add a new entry to the optimized code map.
+ MUST_USE_RESULT MaybeObject* AddToOptimizedCodeMap(Context* native_context,
+ Code* code,
+ FixedArray* literals);
+ static void AddToOptimizedCodeMap(Handle<SharedFunctionInfo> shared,
+ Handle<Context> native_context,
+ Handle<Code> code,
+ Handle<FixedArray> literals);
+
+ // Layout description of the optimized code map.
+ static const int kNextMapIndex = 0;
+ static const int kEntriesStart = 1;
+ static const int kEntryLength = 3;
+ static const int kFirstContextSlot = FixedArray::kHeaderSize + kPointerSize;
+ static const int kFirstCodeSlot = FixedArray::kHeaderSize + 2 * kPointerSize;
+ static const int kSecondEntryIndex = kEntryLength + kEntriesStart;
+ static const int kInitialLength = kEntriesStart + kEntryLength;
+
+ // [scope_info]: Scope info.
+ DECL_ACCESSORS(scope_info, ScopeInfo)
+
+ // [construct stub]: Code stub for constructing instances of this function.
+ DECL_ACCESSORS(construct_stub, Code)
+
+ // Returns if this function has been compiled to native code yet.
+ inline bool is_compiled();
+
+ // [length]: The function length - usually the number of declared parameters.
+ // Use up to 2^30 parameters.
+ inline int length();
+ inline void set_length(int value);
+
+ // [formal parameter count]: The declared number of parameters.
+ inline int formal_parameter_count();
+ inline void set_formal_parameter_count(int value);
+
+ // Set the formal parameter count so the function code will be
+ // called without using argument adaptor frames.
+ inline void DontAdaptArguments();
+
+ // [expected_nof_properties]: Expected number of properties for the function.
+ inline int expected_nof_properties();
+ inline void set_expected_nof_properties(int value);
+
+ // Inobject slack tracking is the way to reclaim unused inobject space.
+ //
+ // The instance size is initially determined by adding some slack to
+ // expected_nof_properties (to allow for a few extra properties added
+ // after the constructor). There is no guarantee that the extra space
+ // will not be wasted.
+ //
+ // Here is the algorithm to reclaim the unused inobject space:
+ // - Detect the first constructor call for this SharedFunctionInfo.
+ // When it happens enter the "in progress" state: remember the
+ // constructor's initial_map and install a special construct stub that
+ // counts constructor calls.
+ // - While the tracking is in progress create objects filled with
+ // one_pointer_filler_map instead of undefined_value. This way they can be
+ // resized quickly and safely.
+ // - Once enough (kGenerousAllocationCount) objects have been created
+ // compute the 'slack' (traverse the map transition tree starting from the
+ // initial_map and find the lowest value of unused_property_fields).
+ // - Traverse the transition tree again and decrease the instance size
+ // of every map. Existing objects will resize automatically (they are
+ // filled with one_pointer_filler_map). All further allocations will
+ // use the adjusted instance size.
+ // - Decrease expected_nof_properties so that an allocations made from
+ // another context will use the adjusted instance size too.
+ // - Exit "in progress" state by clearing the reference to the initial_map
+ // and setting the regular construct stub (generic or inline).
+ //
+ // The above is the main event sequence. Some special cases are possible
+ // while the tracking is in progress:
+ //
+ // - GC occurs.
+ // Check if the initial_map is referenced by any live objects (except this
+ // SharedFunctionInfo). If it is, continue tracking as usual.
+ // If it is not, clear the reference and reset the tracking state. The
+ // tracking will be initiated again on the next constructor call.
+ //
+ // - The constructor is called from another context.
+ // Immediately complete the tracking, perform all the necessary changes
+ // to maps. This is necessary because there is no efficient way to track
+ // multiple initial_maps.
+ // Proceed to create an object in the current context (with the adjusted
+ // size).
+ //
+ // - A different constructor function sharing the same SharedFunctionInfo is
+ // called in the same context. This could be another closure in the same
+ // context, or the first function could have been disposed.
+ // This is handled the same way as the previous case.
+ //
+ // Important: inobject slack tracking is not attempted during the snapshot
+ // creation.
+
+ static const int kGenerousAllocationCount = 8;
+
+ // [construction_count]: Counter for constructor calls made during
+ // the tracking phase.
+ inline int construction_count();
+ inline void set_construction_count(int value);
+
+ // [initial_map]: initial map of the first function called as a constructor.
+ // Saved for the duration of the tracking phase.
+ // This is a weak link (GC resets it to undefined_value if no other live
+ // object reference this map).
+ DECL_ACCESSORS(initial_map, Object)
+
+ // True if the initial_map is not undefined and the countdown stub is
+ // installed.
+ inline bool IsInobjectSlackTrackingInProgress();
+
+ // Starts the tracking.
+ // Stores the initial map and installs the countdown stub.
+ // IsInobjectSlackTrackingInProgress is normally true after this call,
+ // except when tracking have not been started (e.g. the map has no unused
+ // properties or the snapshot is being built).
+ void StartInobjectSlackTracking(Map* map);
+
+ // Completes the tracking.
+ // IsInobjectSlackTrackingInProgress is false after this call.
+ void CompleteInobjectSlackTracking();
+
+ // Invoked before pointers in SharedFunctionInfo are being marked.
+ // Also clears the optimized code map.
+ inline void BeforeVisitingPointers();
+
+ // Clears the initial_map before the GC marking phase to ensure the reference
+ // is weak. IsInobjectSlackTrackingInProgress is false after this call.
+ void DetachInitialMap();
+
+ // Restores the link to the initial map after the GC marking phase.
+ // IsInobjectSlackTrackingInProgress is true after this call.
+ void AttachInitialMap(Map* map);
+
+ // False if there are definitely no live objects created from this function.
+ // True if live objects _may_ exist (existence not guaranteed).
+ // May go back from true to false after GC.
+ DECL_BOOLEAN_ACCESSORS(live_objects_may_exist)
+
+ // [instance class name]: class name for instances.
+ DECL_ACCESSORS(instance_class_name, Object)
+
+ // [function data]: This field holds some additional data for function.
+ // Currently it either has FunctionTemplateInfo to make benefit the API
+ // or Smi identifying a builtin function.
+ // In the long run we don't want all functions to have this field but
+ // we can fix that when we have a better model for storing hidden data
+ // on objects.
+ DECL_ACCESSORS(function_data, Object)
+
+ inline bool IsApiFunction();
+ inline FunctionTemplateInfo* get_api_func_data();
+ inline bool HasBuiltinFunctionId();
+ inline BuiltinFunctionId builtin_function_id();
+
+ // [script info]: Script from which the function originates.
+ DECL_ACCESSORS(script, Object)
+
+ // [num_literals]: Number of literals used by this function.
+ inline int num_literals();
+ inline void set_num_literals(int value);
+
+ // [start_position_and_type]: Field used to store both the source code
+ // position, whether or not the function is a function expression,
+ // and whether or not the function is a toplevel function. The two
+ // least significants bit indicates whether the function is an
+ // expression and the rest contains the source code position.
+ inline int start_position_and_type();
+ inline void set_start_position_and_type(int value);
+
+ // [debug info]: Debug information.
+ DECL_ACCESSORS(debug_info, Object)
+
+ // [inferred name]: Name inferred from variable or property
+ // assignment of this function. Used to facilitate debugging and
+ // profiling of JavaScript code written in OO style, where almost
+ // all functions are anonymous but are assigned to object
+ // properties.
+ DECL_ACCESSORS(inferred_name, String)
+
+ // The function's name if it is non-empty, otherwise the inferred name.
+ String* DebugName();
+
+ // Position of the 'function' token in the script source.
+ inline int function_token_position();
+ inline void set_function_token_position(int function_token_position);
+
+ // Position of this function in the script source.
+ inline int start_position();
+ inline void set_start_position(int start_position);
+
+ // End position of this function in the script source.
+ inline int end_position();
+ inline void set_end_position(int end_position);
+
+ // Is this function a function expression in the source code.
+ DECL_BOOLEAN_ACCESSORS(is_expression)
+
+ // Is this function a top-level function (scripts, evals).
+ DECL_BOOLEAN_ACCESSORS(is_toplevel)
+
+ // Bit field containing various information collected by the compiler to
+ // drive optimization.
+ inline int compiler_hints();
+ inline void set_compiler_hints(int value);
+
+ inline int ast_node_count();
+ inline void set_ast_node_count(int count);
+
+ inline int profiler_ticks();
+
+ // Inline cache age is used to infer whether the function survived a context
+ // disposal or not. In the former case we reset the opt_count.
+ inline int ic_age();
+ inline void set_ic_age(int age);
+
+ // Indicates if this function can be lazy compiled.
+ // This is used to determine if we can safely flush code from a function
+ // when doing GC if we expect that the function will no longer be used.
+ DECL_BOOLEAN_ACCESSORS(allows_lazy_compilation)
+
+ // Indicates if this function can be lazy compiled without a context.
+ // This is used to determine if we can force compilation without reaching
+ // the function through program execution but through other means (e.g. heap
+ // iteration by the debugger).
+ DECL_BOOLEAN_ACCESSORS(allows_lazy_compilation_without_context)
+
+ // Indicates whether optimizations have been disabled for this
+ // shared function info. If a function is repeatedly optimized or if
+ // we cannot optimize the function we disable optimization to avoid
+ // spending time attempting to optimize it again.
+ DECL_BOOLEAN_ACCESSORS(optimization_disabled)
+
+ // 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)
+
+ // True if the function has any duplicated parameter names.
+ DECL_BOOLEAN_ACCESSORS(has_duplicate_parameters)
+
+ // Indicates whether the function is a native function.
+ // These needs special treatment in .call and .apply since
+ // null passed as the receiver should not be translated to the
+ // global object.
+ DECL_BOOLEAN_ACCESSORS(native)
+
+ // Indicates that the function was created by the Function function.
+ // Though it's anonymous, toString should treat it as if it had the name
+ // "anonymous". We don't set the name itself so that the system does not
+ // see a binding for it.
+ DECL_BOOLEAN_ACCESSORS(name_should_print_as_anonymous)
+
+ // Indicates whether the function is a bound function created using
+ // the bind function.
+ DECL_BOOLEAN_ACCESSORS(bound)
+
+ // Indicates that the function is anonymous (the name field can be set
+ // through the API, which does not change this flag).
+ DECL_BOOLEAN_ACCESSORS(is_anonymous)
+
+ // Is this a function or top-level/eval code.
+ DECL_BOOLEAN_ACCESSORS(is_function)
+
+ // Indicates that the function cannot be optimized.
+ DECL_BOOLEAN_ACCESSORS(dont_optimize)
+
+ // Indicates that the function cannot be inlined.
+ DECL_BOOLEAN_ACCESSORS(dont_inline)
+
+ // Indicates that code for this function cannot be cached.
+ DECL_BOOLEAN_ACCESSORS(dont_cache)
+
+ // Indicates that code for this function cannot be flushed.
+ DECL_BOOLEAN_ACCESSORS(dont_flush)
+
+ // Indicates that this function is a generator.
+ DECL_BOOLEAN_ACCESSORS(is_generator)
+
+ // Indicates whether or not the code in the shared function support
+ // deoptimization.
+ inline bool has_deoptimization_support();
+
+ // Enable deoptimization support through recompiled code.
+ void EnableDeoptimizationSupport(Code* recompiled);
+
+ // Disable (further) attempted optimization of all functions sharing this
+ // shared function info.
+ void DisableOptimization(BailoutReason reason);
+
+ // Lookup the bailout ID and ASSERT that it exists in the non-optimized
+ // code, returns whether it asserted (i.e., always true if assertions are
+ // disabled).
+ bool VerifyBailoutId(BailoutId id);
+
+ // [source code]: Source code for the function.
+ bool HasSourceCode();
+ Handle<Object> GetSourceCode();
+
+ // Number of times the function was optimized.
+ inline int opt_count();
+ inline void set_opt_count(int opt_count);
+
+ // Number of times the function was deoptimized.
+ inline void set_deopt_count(int value);
+ inline int deopt_count();
+ inline void increment_deopt_count();
+
+ // Number of time we tried to re-enable optimization after it
+ // was disabled due to high number of deoptimizations.
+ inline void set_opt_reenable_tries(int value);
+ inline int opt_reenable_tries();
+
+ inline void TryReenableOptimization();
+
+ // Stores deopt_count, opt_reenable_tries and ic_age as bit-fields.
+ inline void set_counters(int value);
+ inline int counters();
+
+ // Source size of this function.
+ int SourceSize();
+
+ // Calculate the instance size.
+ int CalculateInstanceSize();
+
+ // Calculate the number of in-object properties.
+ int CalculateInObjectProperties();
+
+ // Dispatched behavior.
+ // Set max_length to -1 for unlimited length.
+ void SourceCodePrint(StringStream* accumulator, int max_length);
+ DECLARE_PRINTER(SharedFunctionInfo)
+ DECLARE_VERIFIER(SharedFunctionInfo)
+
+ void ResetForNewContext(int new_ic_age);
+
+ // Helper to compile the shared code. Returns true on success, false on
+ // failure (e.g., stack overflow during compilation). This is only used by
+ // the debugger, it is not possible to compile without a context otherwise.
+ static bool CompileLazy(Handle<SharedFunctionInfo> shared,
+ ClearExceptionFlag flag);
+
+ // Casting.
+ static inline SharedFunctionInfo* cast(Object* obj);
+
+ // Constants.
+ static const int kDontAdaptArgumentsSentinel = -1;
+
+ // Layout description.
+ // Pointer fields.
+ static const int kNameOffset = HeapObject::kHeaderSize;
+ static const int kCodeOffset = kNameOffset + kPointerSize;
+ static const int kOptimizedCodeMapOffset = kCodeOffset + kPointerSize;
+ static const int kScopeInfoOffset = kOptimizedCodeMapOffset + kPointerSize;
+ static const int kConstructStubOffset = kScopeInfoOffset + kPointerSize;
+ static const int kInstanceClassNameOffset =
+ kConstructStubOffset + kPointerSize;
+ static const int kFunctionDataOffset =
+ kInstanceClassNameOffset + kPointerSize;
+ static const int kScriptOffset = kFunctionDataOffset + kPointerSize;
+ static const int kDebugInfoOffset = kScriptOffset + kPointerSize;
+ static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
+ static const int kInitialMapOffset =
+ kInferredNameOffset + kPointerSize;
+ // ast_node_count is a Smi field. It could be grouped with another Smi field
+ // into a PSEUDO_SMI_ACCESSORS pair (on x64), if one becomes available.
+ static const int kAstNodeCountOffset =
+ kInitialMapOffset + kPointerSize;
+#if V8_HOST_ARCH_32_BIT
+ // Smi fields.
+ static const int kLengthOffset =
+ kAstNodeCountOffset + kPointerSize;
+ static const int kFormalParameterCountOffset = kLengthOffset + kPointerSize;
+ static const int kExpectedNofPropertiesOffset =
+ kFormalParameterCountOffset + kPointerSize;
+ static const int kNumLiteralsOffset =
+ kExpectedNofPropertiesOffset + kPointerSize;
+ static const int kStartPositionAndTypeOffset =
+ kNumLiteralsOffset + kPointerSize;
+ static const int kEndPositionOffset =
+ kStartPositionAndTypeOffset + kPointerSize;
+ static const int kFunctionTokenPositionOffset =
+ kEndPositionOffset + kPointerSize;
+ static const int kCompilerHintsOffset =
+ kFunctionTokenPositionOffset + kPointerSize;
+ static const int kOptCountOffset = kCompilerHintsOffset + kPointerSize;
+ static const int kCountersOffset = kOptCountOffset + kPointerSize;
+
+ // Total size.
+ static const int kSize = kCountersOffset + kPointerSize;
+#else
+ // The only reason to use smi fields instead of int fields
+ // is to allow iteration without maps decoding during
+ // garbage collections.
+ // To avoid wasting space on 64-bit architectures we use
+ // the following trick: we group integer fields into pairs
+ // First integer in each pair is shifted left by 1.
+ // By doing this we guarantee that LSB of each kPointerSize aligned
+ // word is not set and thus this word cannot be treated as pointer
+ // to HeapObject during old space traversal.
+ static const int kLengthOffset =
+ kAstNodeCountOffset + kPointerSize;
+ static const int kFormalParameterCountOffset =
+ kLengthOffset + kIntSize;
+
+ static const int kExpectedNofPropertiesOffset =
+ kFormalParameterCountOffset + kIntSize;
+ static const int kNumLiteralsOffset =
+ kExpectedNofPropertiesOffset + kIntSize;
+
+ static const int kEndPositionOffset =
+ kNumLiteralsOffset + kIntSize;
+ static const int kStartPositionAndTypeOffset =
+ kEndPositionOffset + kIntSize;
+
+ static const int kFunctionTokenPositionOffset =
+ kStartPositionAndTypeOffset + kIntSize;
+ static const int kCompilerHintsOffset =
+ kFunctionTokenPositionOffset + kIntSize;
+
+ static const int kOptCountOffset = kCompilerHintsOffset + kIntSize;
+
+ static const int kCountersOffset = kOptCountOffset + kIntSize;
+
+ // Total size.
+ static const int kSize = kCountersOffset + kIntSize;
+
+#endif
+
+ // The construction counter for inobject slack tracking is stored in the
+ // most significant byte of compiler_hints which is otherwise unused.
+ // Its offset depends on the endian-ness of the architecture.
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ static const int kConstructionCountOffset = kCompilerHintsOffset + 3;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ static const int kConstructionCountOffset = kCompilerHintsOffset + 0;
+#else
+#error Unknown byte ordering
+#endif
+
+ static const int kAlignedSize = POINTER_SIZE_ALIGN(kSize);
+
+ typedef FixedBodyDescriptor<kNameOffset,
+ kInitialMapOffset + kPointerSize,
+ kSize> BodyDescriptor;
+
+ // Bit positions in start_position_and_type.
+ // The source code start position is in the 30 most significant bits of
+ // the start_position_and_type field.
+ static const int kIsExpressionBit = 0;
+ static const int kIsTopLevelBit = 1;
+ static const int kStartPositionShift = 2;
+ static const int kStartPositionMask = ~((1 << kStartPositionShift) - 1);
+
+ // Bit positions in compiler_hints.
+ enum CompilerHints {
+ kAllowLazyCompilation,
+ kAllowLazyCompilationWithoutContext,
+ kLiveObjectsMayExist,
+ kOptimizationDisabled,
+ kStrictModeFunction,
+ kExtendedModeFunction,
+ kUsesArguments,
+ kHasDuplicateParameters,
+ kNative,
+ kBoundFunction,
+ kIsAnonymous,
+ kNameShouldPrintAsAnonymous,
+ kIsFunction,
+ kDontOptimize,
+ kDontInline,
+ kDontCache,
+ kDontFlush,
+ kIsGenerator,
+ kCompilerHintsCount // Pseudo entry
+ };
+
+ class DeoptCountBits: public BitField<int, 0, 4> {};
+ class OptReenableTriesBits: public BitField<int, 4, 18> {};
+ class ICAgeBits: public BitField<int, 22, 8> {};
+
+ private:
+#if V8_HOST_ARCH_32_BIT
+ // On 32 bit platforms, compiler hints is a smi.
+ static const int kCompilerHintsSmiTagSize = kSmiTagSize;
+ static const int kCompilerHintsSize = kPointerSize;
+#else
+ // On 64 bit platforms, compiler hints is not a smi, see comment above.
+ static const int kCompilerHintsSmiTagSize = 0;
+ static const int kCompilerHintsSize = kIntSize;
+#endif
+
+ STATIC_ASSERT(SharedFunctionInfo::kCompilerHintsCount <=
+ SharedFunctionInfo::kCompilerHintsSize * kBitsPerByte);
+
+ public:
+ // Constants for optimizing codegen for strict mode function and
+ // native tests.
+ // 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);
+#else
+#error Unknown byte ordering
+#endif
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
+};
+
+
+class JSGeneratorObject: public JSObject {
+ public:
+ // [function]: The function corresponding to this generator object.
+ DECL_ACCESSORS(function, JSFunction)
+
+ // [context]: The context of the suspended computation.
+ DECL_ACCESSORS(context, Context)
+
+ // [receiver]: The receiver of the suspended computation.
+ DECL_ACCESSORS(receiver, Object)
+
+ // [continuation]: Offset into code of continuation.
+ //
+ // A positive offset indicates a suspended generator. The special
+ // kGeneratorExecuting and kGeneratorClosed values indicate that a generator
+ // cannot be resumed.
+ inline int continuation();
+ inline void set_continuation(int continuation);
+
+ // [operand_stack]: Saved operand stack.
+ DECL_ACCESSORS(operand_stack, FixedArray)
+
+ // [stack_handler_index]: Index of first stack handler in operand_stack, or -1
+ // if the captured activation had no stack handler.
+ inline int stack_handler_index();
+ inline void set_stack_handler_index(int stack_handler_index);
+
+ // Casting.
+ static inline JSGeneratorObject* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSGeneratorObject)
+ DECLARE_VERIFIER(JSGeneratorObject)
+
+ // Magic sentinel values for the continuation.
+ static const int kGeneratorExecuting = -1;
+ static const int kGeneratorClosed = 0;
+
+ // Layout description.
+ static const int kFunctionOffset = JSObject::kHeaderSize;
+ static const int kContextOffset = kFunctionOffset + kPointerSize;
+ static const int kReceiverOffset = kContextOffset + kPointerSize;
+ static const int kContinuationOffset = kReceiverOffset + kPointerSize;
+ static const int kOperandStackOffset = kContinuationOffset + kPointerSize;
+ static const int kStackHandlerIndexOffset =
+ kOperandStackOffset + kPointerSize;
+ static const int kSize = kStackHandlerIndexOffset + kPointerSize;
+
+ // Resume mode, for use by runtime functions.
+ enum ResumeMode { NEXT, THROW };
+
+ // Yielding from a generator returns an object with the following inobject
+ // properties. See Context::generator_result_map() for the map.
+ static const int kResultValuePropertyIndex = 0;
+ static const int kResultDonePropertyIndex = 1;
+ static const int kResultPropertyCount = 2;
+
+ static const int kResultValuePropertyOffset = JSObject::kHeaderSize;
+ static const int kResultDonePropertyOffset =
+ kResultValuePropertyOffset + kPointerSize;
+ static const int kResultSize = kResultDonePropertyOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSGeneratorObject);
+};
+
+
+// Representation for module instance objects.
+class JSModule: public JSObject {
+ public:
+ // [context]: the context holding the module's locals, or undefined if none.
+ DECL_ACCESSORS(context, Object)
+
+ // [scope_info]: Scope info.
+ DECL_ACCESSORS(scope_info, ScopeInfo)
+
+ // Casting.
+ static inline JSModule* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSModule)
+ DECLARE_VERIFIER(JSModule)
+
+ // Layout description.
+ static const int kContextOffset = JSObject::kHeaderSize;
+ static const int kScopeInfoOffset = kContextOffset + kPointerSize;
+ static const int kSize = kScopeInfoOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSModule);
+};
+
+
+// JSFunction describes JavaScript functions.
+class JSFunction: public JSObject {
+ public:
+ // [prototype_or_initial_map]:
+ DECL_ACCESSORS(prototype_or_initial_map, Object)
+
+ // [shared]: The information about the function that
+ // can be shared by instances.
+ DECL_ACCESSORS(shared, SharedFunctionInfo)
+
+ // [context]: The context for this function.
+ inline Context* context();
+ inline void set_context(Object* context);
+
+ // [code]: The generated code object for this function. Executed
+ // when the function is invoked, e.g. foo() or new foo(). See
+ // [[Call]] and [[Construct]] description in ECMA-262, section
+ // 8.6.2, page 27.
+ inline Code* code();
+ inline void set_code(Code* code);
+ inline void set_code_no_write_barrier(Code* code);
+ inline void ReplaceCode(Code* code);
+
+ // Tells whether this function is builtin.
+ inline bool IsBuiltin();
+
+ // Tells whether or not the function needs arguments adaption.
+ inline bool NeedsArgumentsAdaption();
+
+ // Tells whether or not this function has been optimized.
+ inline bool IsOptimized();
+
+ // Tells whether or not this function can be optimized.
+ inline bool IsOptimizable();
+
+ // Mark this function for lazy recompilation. The function will be
+ // recompiled the next time it is executed.
+ void MarkForLazyRecompilation();
+ void MarkForParallelRecompilation();
+ void MarkForInstallingRecompiledCode();
+ void MarkInRecompileQueue();
+
+ // Helpers to compile this function. Returns true on success, false on
+ // failure (e.g., stack overflow during compilation).
+ static bool EnsureCompiled(Handle<JSFunction> function,
+ ClearExceptionFlag flag);
+ static bool CompileLazy(Handle<JSFunction> function,
+ ClearExceptionFlag flag);
+ static bool CompileOptimized(Handle<JSFunction> function,
+ BailoutId osr_ast_id,
+ ClearExceptionFlag flag);
+
+ // Tells whether or not the function is already marked for lazy
+ // recompilation.
+ inline bool IsMarkedForLazyRecompilation();
+ inline bool IsMarkedForParallelRecompilation();
+ inline bool IsMarkedForInstallingRecompiledCode();
+
+ // Tells whether or not the function is on the parallel
+ // recompilation queue.
+ inline bool IsInRecompileQueue();
+
+ // Check whether or not this function is inlineable.
+ bool IsInlineable();
+
+ // [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
+ // function to be used when creating these literals. This is
+ // necessary so that we do not dynamically lookup the object, regexp
+ // 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.
+ //
+ // 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 bool has_initial_map();
+
+ // Get and set the prototype property on a JSFunction. If the
+ // function has an initial map the prototype is set on the initial
+ // map. Otherwise, the prototype is put in the initial map field
+ // until an initial map is needed.
+ inline bool has_prototype();
+ inline bool has_instance_prototype();
+ inline Object* prototype();
+ inline Object* instance_prototype();
+ static void SetPrototype(Handle<JSFunction> function,
+ Handle<Object> value);
+ static void SetInstancePrototype(Handle<JSFunction> function,
+ Handle<Object> value);
+
+ // After prototype is removed, it will not be created when accessed, and
+ // [[Construct]] from this function will not be allowed.
+ void RemovePrototype();
+ inline bool should_have_prototype();
+
+ // Accessor for this function's initial map's [[class]]
+ // property. This is primarily used by ECMA native functions. This
+ // method sets the class_name field of this function's initial map
+ // to a given value. It creates an initial map if this function does
+ // not have one. Note that this method does not copy the initial map
+ // if it has one already, but simply replaces it with the new value.
+ // Instances created afterwards will have a map whose [[class]] is
+ // set to 'value', but there is no guarantees on instances created
+ // before.
+ void SetInstanceClassName(String* name);
+
+ // Returns if this function has been compiled to native code yet.
+ inline bool is_compiled();
+
+ // [next_function_link]: Field for linking functions. This list is treated as
+ // a weak list by the GC.
+ DECL_ACCESSORS(next_function_link, Object)
+
+ // Prints the name of the function using PrintF.
+ inline void PrintName() {
+ PrintName(stdout);
+ }
+ void PrintName(FILE* out);
+
+ // Casting.
+ static inline JSFunction* cast(Object* obj);
+
+ // Iterates the objects, including code objects indirectly referenced
+ // through pointers to the first instruction in the code object.
+ void JSFunctionIterateBody(int object_size, ObjectVisitor* v);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSFunction)
+ DECLARE_VERIFIER(JSFunction)
+
+ // Returns the number of allocated literals.
+ inline int NumberOfLiterals();
+
+ // Retrieve the native context from a function's literal array.
+ static Context* NativeContextFromLiterals(FixedArray* literals);
+
+ bool PassesHydrogenFilter();
+
+ // Layout descriptors. The last property (from kNonWeakFieldsEndOffset to
+ // kSize) is weak and has special handling during garbage collection.
+ static const int kCodeEntryOffset = JSObject::kHeaderSize;
+ static const int kPrototypeOrInitialMapOffset =
+ kCodeEntryOffset + kPointerSize;
+ static const int kSharedFunctionInfoOffset =
+ kPrototypeOrInitialMapOffset + kPointerSize;
+ static const int kContextOffset = kSharedFunctionInfoOffset + kPointerSize;
+ static const int kLiteralsOffset = kContextOffset + kPointerSize;
+ static const int kNonWeakFieldsEndOffset = kLiteralsOffset + kPointerSize;
+ static const int kNextFunctionLinkOffset = kNonWeakFieldsEndOffset;
+ static const int kSize = kNextFunctionLinkOffset + kPointerSize;
+
+ // Layout of the literals array.
+ static const int kLiteralsPrefixSize = 1;
+ static const int kLiteralNativeContextIndex = 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);
+};
+
+
+// JSGlobalProxy's prototype must be a JSGlobalObject or null,
+// and the prototype is hidden. JSGlobalProxy always delegates
+// property accesses to its prototype if the prototype is not null.
+//
+// A JSGlobalProxy can be reinitialized which will preserve its identity.
+//
+// Accessing a JSGlobalProxy requires security check.
+
+class JSGlobalProxy : public JSObject {
+ public:
+ // [native_context]: the owner native context of this global proxy object.
+ // It is null value if this object is not used by any context.
+ DECL_ACCESSORS(native_context, Object)
+
+ // Casting.
+ static inline JSGlobalProxy* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSGlobalProxy)
+ DECLARE_VERIFIER(JSGlobalProxy)
+
+ // Layout description.
+ static const int kNativeContextOffset = JSObject::kHeaderSize;
+ static const int kSize = kNativeContextOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSGlobalProxy);
+};
+
+
+// Forward declaration.
+class JSBuiltinsObject;
+
+// Common super class for JavaScript global objects and the special
+// builtins global objects.
+class GlobalObject: public JSObject {
+ public:
+ // [builtins]: the object holding the runtime routines written in JS.
+ DECL_ACCESSORS(builtins, JSBuiltinsObject)
+
+ // [native context]: the natives corresponding to this global object.
+ DECL_ACCESSORS(native_context, Context)
+
+ // [global context]: the most recent (i.e. innermost) global context.
+ DECL_ACCESSORS(global_context, Context)
+
+ // [global receiver]: the global receiver object of the context
+ DECL_ACCESSORS(global_receiver, JSObject)
+
+ // Retrieve the property cell used to store a property.
+ PropertyCell* GetPropertyCell(LookupResult* result);
+
+ // This is like GetProperty, but is used when you know the lookup won't fail
+ // by throwing an exception. This is for the debug and builtins global
+ // objects, where it is known which properties can be expected to be present
+ // on the object.
+ Object* GetPropertyNoExceptionThrown(Name* key) {
+ Object* answer = GetProperty(key)->ToObjectUnchecked();
+ return answer;
+ }
+
+ // Ensure that the global object has a cell for the given property name.
+ static Handle<PropertyCell> EnsurePropertyCell(Handle<GlobalObject> global,
+ Handle<Name> name);
+
+ // Casting.
+ static inline GlobalObject* cast(Object* obj);
+
+ // Layout description.
+ static const int kBuiltinsOffset = JSObject::kHeaderSize;
+ static const int kNativeContextOffset = kBuiltinsOffset + kPointerSize;
+ static const int kGlobalContextOffset = kNativeContextOffset + kPointerSize;
+ static const int kGlobalReceiverOffset = kGlobalContextOffset + kPointerSize;
+ static const int kHeaderSize = kGlobalReceiverOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(GlobalObject);
+};
+
+
+// JavaScript global object.
+class JSGlobalObject: public GlobalObject {
+ public:
+ // Casting.
+ static inline JSGlobalObject* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSGlobalObject)
+ DECLARE_VERIFIER(JSGlobalObject)
+
+ // Layout description.
+ static const int kSize = GlobalObject::kHeaderSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSGlobalObject);
+};
+
+
+// Builtins global object which holds the runtime routines written in
+// JavaScript.
+class JSBuiltinsObject: public GlobalObject {
+ public:
+ // Accessors for the runtime routines written in JavaScript.
+ inline Object* javascript_builtin(Builtins::JavaScript id);
+ inline void set_javascript_builtin(Builtins::JavaScript id, Object* value);
+
+ // Accessors for code of the runtime routines written in JavaScript.
+ inline Code* javascript_builtin_code(Builtins::JavaScript id);
+ inline void set_javascript_builtin_code(Builtins::JavaScript id, Code* value);
+
+ // Casting.
+ static inline JSBuiltinsObject* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSBuiltinsObject)
+ DECLARE_VERIFIER(JSBuiltinsObject)
+
+ // Layout description. The size of the builtins object includes
+ // room for two pointers per runtime routine written in javascript
+ // (function and code object).
+ static const int kJSBuiltinsCount = Builtins::id_count;
+ static const int kJSBuiltinsOffset = GlobalObject::kHeaderSize;
+ static const int kJSBuiltinsCodeOffset =
+ GlobalObject::kHeaderSize + (kJSBuiltinsCount * kPointerSize);
+ static const int kSize =
+ kJSBuiltinsCodeOffset + (kJSBuiltinsCount * kPointerSize);
+
+ static int OffsetOfFunctionWithId(Builtins::JavaScript id) {
+ return kJSBuiltinsOffset + id * kPointerSize;
+ }
+
+ static int OffsetOfCodeWithId(Builtins::JavaScript id) {
+ return kJSBuiltinsCodeOffset + id * kPointerSize;
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSBuiltinsObject);
+};
+
+
+// Representation for JS Wrapper objects, String, Number, Boolean, etc.
+class JSValue: public JSObject {
+ public:
+ // [value]: the object being wrapped.
+ DECL_ACCESSORS(value, Object)
+
+ // Casting.
+ static inline JSValue* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSValue)
+ DECLARE_VERIFIER(JSValue)
+
+ // Layout description.
+ static const int kValueOffset = JSObject::kHeaderSize;
+ static const int kSize = kValueOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSValue);
+};
+
+
+class DateCache;
+
+// Representation for JS date objects.
+class JSDate: public JSObject {
+ public:
+ // If one component is NaN, all of them are, indicating a NaN time value.
+ // [value]: the time value.
+ DECL_ACCESSORS(value, Object)
+ // [year]: caches year. Either undefined, smi, or NaN.
+ DECL_ACCESSORS(year, Object)
+ // [month]: caches month. Either undefined, smi, or NaN.
+ DECL_ACCESSORS(month, Object)
+ // [day]: caches day. Either undefined, smi, or NaN.
+ DECL_ACCESSORS(day, Object)
+ // [weekday]: caches day of week. Either undefined, smi, or NaN.
+ DECL_ACCESSORS(weekday, Object)
+ // [hour]: caches hours. Either undefined, smi, or NaN.
+ DECL_ACCESSORS(hour, Object)
+ // [min]: caches minutes. Either undefined, smi, or NaN.
+ DECL_ACCESSORS(min, Object)
+ // [sec]: caches seconds. Either undefined, smi, or NaN.
+ DECL_ACCESSORS(sec, Object)
+ // [cache stamp]: sample of the date cache stamp at the
+ // moment when local fields were cached.
+ DECL_ACCESSORS(cache_stamp, Object)
+
+ // Casting.
+ static inline JSDate* cast(Object* obj);
+
+ // Returns the date field with the specified index.
+ // See FieldIndex for the list of date fields.
+ static Object* GetField(Object* date, Smi* index);
+
+ void SetValue(Object* value, bool is_value_nan);
+
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSDate)
+ DECLARE_VERIFIER(JSDate)
+
+ // The order is important. It must be kept in sync with date macros
+ // in macros.py.
+ enum FieldIndex {
+ kDateValue,
+ kYear,
+ kMonth,
+ kDay,
+ kWeekday,
+ kHour,
+ kMinute,
+ kSecond,
+ kFirstUncachedField,
+ kMillisecond = kFirstUncachedField,
+ kDays,
+ kTimeInDay,
+ kFirstUTCField,
+ kYearUTC = kFirstUTCField,
+ kMonthUTC,
+ kDayUTC,
+ kWeekdayUTC,
+ kHourUTC,
+ kMinuteUTC,
+ kSecondUTC,
+ kMillisecondUTC,
+ kDaysUTC,
+ kTimeInDayUTC,
+ kTimezoneOffset
+ };
+
+ // Layout description.
+ static const int kValueOffset = JSObject::kHeaderSize;
+ static const int kYearOffset = kValueOffset + kPointerSize;
+ static const int kMonthOffset = kYearOffset + kPointerSize;
+ static const int kDayOffset = kMonthOffset + kPointerSize;
+ static const int kWeekdayOffset = kDayOffset + kPointerSize;
+ static const int kHourOffset = kWeekdayOffset + kPointerSize;
+ static const int kMinOffset = kHourOffset + kPointerSize;
+ static const int kSecOffset = kMinOffset + kPointerSize;
+ static const int kCacheStampOffset = kSecOffset + kPointerSize;
+ static const int kSize = kCacheStampOffset + kPointerSize;
+
+ private:
+ inline Object* DoGetField(FieldIndex index);
+
+ Object* GetUTCField(FieldIndex index, double value, DateCache* date_cache);
+
+ // Computes and caches the cacheable fields of the date.
+ inline void SetLocalFields(int64_t local_time_ms, DateCache* date_cache);
+
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSDate);
+};
+
+
+// Representation of message objects used for error reporting through
+// the API. The messages are formatted in JavaScript so this object is
+// a real JavaScript object. The information used for formatting the
+// error messages are not directly accessible from JavaScript to
+// prevent leaking information to user code called during error
+// formatting.
+class JSMessageObject: public JSObject {
+ public:
+ // [type]: the type of error message.
+ DECL_ACCESSORS(type, String)
+
+ // [arguments]: the arguments for formatting the error message.
+ DECL_ACCESSORS(arguments, JSArray)
+
+ // [script]: the script from which the error message originated.
+ DECL_ACCESSORS(script, Object)
+
+ // [stack_trace]: the stack trace for this error message.
+ DECL_ACCESSORS(stack_trace, Object)
+
+ // [stack_frames]: an array of stack frames for this error object.
+ DECL_ACCESSORS(stack_frames, Object)
+
+ // [start_position]: the start position in the script for the error message.
+ inline int start_position();
+ inline void set_start_position(int value);
+
+ // [end_position]: the end position in the script for the error message.
+ inline int end_position();
+ inline void set_end_position(int value);
+
+ // Casting.
+ static inline JSMessageObject* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSMessageObject)
+ DECLARE_VERIFIER(JSMessageObject)
+
+ // Layout description.
+ static const int kTypeOffset = JSObject::kHeaderSize;
+ static const int kArgumentsOffset = kTypeOffset + kPointerSize;
+ static const int kScriptOffset = kArgumentsOffset + kPointerSize;
+ static const int kStackTraceOffset = kScriptOffset + kPointerSize;
+ static const int kStackFramesOffset = kStackTraceOffset + kPointerSize;
+ static const int kStartPositionOffset = kStackFramesOffset + kPointerSize;
+ static const int kEndPositionOffset = kStartPositionOffset + kPointerSize;
+ static const int kSize = kEndPositionOffset + kPointerSize;
+
+ typedef FixedBodyDescriptor<HeapObject::kMapOffset,
+ kStackFramesOffset + kPointerSize,
+ kSize> BodyDescriptor;
+};
+
+
+// Regular expressions
+// The regular expression holds a single reference to a FixedArray in
+// the kDataOffset field.
+// The FixedArray contains the following data:
+// - tag : type of regexp implementation (not compiled yet, atom or irregexp)
+// - reference to the original source string
+// - reference to the original flag string
+// If it is an atom regexp
+// - a reference to a literal string to search for
+// If it is an irregexp regexp:
+// - a reference to code for ASCII inputs (bytecode or compiled), or a smi
+// used for tracking the last usage (used for code flushing).
+// - a reference to code for UC16 inputs (bytecode or compiled), or a smi
+// used for tracking the last usage (used for code flushing)..
+// - max number of registers used by irregexp implementations.
+// - number of capture registers (output values) of the regexp.
+class JSRegExp: public JSObject {
+ public:
+ // Meaning of Type:
+ // NOT_COMPILED: Initial value. No data has been stored in the JSRegExp yet.
+ // ATOM: A simple string to match against using an indexOf operation.
+ // IRREGEXP: Compiled with Irregexp.
+ // IRREGEXP_NATIVE: Compiled to native code with Irregexp.
+ enum Type { NOT_COMPILED, ATOM, IRREGEXP };
+ enum Flag { NONE = 0, GLOBAL = 1, IGNORE_CASE = 2, MULTILINE = 4 };
+
+ class Flags {
+ public:
+ explicit Flags(uint32_t value) : value_(value) { }
+ bool is_global() { return (value_ & GLOBAL) != 0; }
+ bool is_ignore_case() { return (value_ & IGNORE_CASE) != 0; }
+ bool is_multiline() { return (value_ & MULTILINE) != 0; }
+ uint32_t value() { return value_; }
+ private:
+ uint32_t value_;
+ };
+
+ DECL_ACCESSORS(data, Object)
+
+ inline Type TypeTag();
+ inline int CaptureCount();
+ inline Flags GetFlags();
+ inline String* Pattern();
+ inline Object* DataAt(int index);
+ // Set implementation data after the object has been prepared.
+ inline void SetDataAt(int index, Object* value);
+
+ static int code_index(bool is_ascii) {
+ if (is_ascii) {
+ return kIrregexpASCIICodeIndex;
+ } else {
+ return kIrregexpUC16CodeIndex;
+ }
+ }
+
+ static int saved_code_index(bool is_ascii) {
+ if (is_ascii) {
+ return kIrregexpASCIICodeSavedIndex;
+ } else {
+ return kIrregexpUC16CodeSavedIndex;
+ }
+ }
+
+ static inline JSRegExp* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_VERIFIER(JSRegExp)
+
+ static const int kDataOffset = JSObject::kHeaderSize;
+ static const int kSize = kDataOffset + kPointerSize;
+
+ // Indices in the data array.
+ static const int kTagIndex = 0;
+ static const int kSourceIndex = kTagIndex + 1;
+ static const int kFlagsIndex = kSourceIndex + 1;
+ static const int kDataIndex = kFlagsIndex + 1;
+ // The data fields are used in different ways depending on the
+ // value of the tag.
+ // Atom regexps (literal strings).
+ static const int kAtomPatternIndex = kDataIndex;
+
+ static const int kAtomDataSize = kAtomPatternIndex + 1;
+
+ // Irregexp compiled code or bytecode for ASCII. If compilation
+ // fails, this fields hold an exception object that should be
+ // thrown if the regexp is used again.
+ static const int kIrregexpASCIICodeIndex = kDataIndex;
+ // Irregexp compiled code or bytecode for UC16. If compilation
+ // fails, this fields hold an exception object that should be
+ // thrown if the regexp is used again.
+ static const int kIrregexpUC16CodeIndex = kDataIndex + 1;
+
+ // Saved instance of Irregexp compiled code or bytecode for ASCII that
+ // is a potential candidate for flushing.
+ static const int kIrregexpASCIICodeSavedIndex = kDataIndex + 2;
+ // Saved instance of Irregexp compiled code or bytecode for UC16 that is
+ // a potential candidate for flushing.
+ static const int kIrregexpUC16CodeSavedIndex = kDataIndex + 3;
+
+ // Maximal number of registers used by either ASCII or UC16.
+ // Only used to check that there is enough stack space
+ static const int kIrregexpMaxRegisterCountIndex = kDataIndex + 4;
+ // Number of captures in the compiled regexp.
+ static const int kIrregexpCaptureCountIndex = kDataIndex + 5;
+
+ static const int kIrregexpDataSize = kIrregexpCaptureCountIndex + 1;
+
+ // Offsets directly into the data fixed array.
+ static const int kDataTagOffset =
+ FixedArray::kHeaderSize + kTagIndex * kPointerSize;
+ static const int kDataAsciiCodeOffset =
+ FixedArray::kHeaderSize + kIrregexpASCIICodeIndex * kPointerSize;
+ static const int kDataUC16CodeOffset =
+ FixedArray::kHeaderSize + kIrregexpUC16CodeIndex * kPointerSize;
+ static const int kIrregexpCaptureCountOffset =
+ FixedArray::kHeaderSize + kIrregexpCaptureCountIndex * kPointerSize;
+
+ // In-object fields.
+ static const int kSourceFieldIndex = 0;
+ static const int kGlobalFieldIndex = 1;
+ static const int kIgnoreCaseFieldIndex = 2;
+ static const int kMultilineFieldIndex = 3;
+ static const int kLastIndexFieldIndex = 4;
+ static const int kInObjectFieldCount = 5;
+
+ // The uninitialized value for a regexp code object.
+ static const int kUninitializedValue = -1;
+
+ // The compilation error value for the regexp code object. The real error
+ // object is in the saved code field.
+ static const int kCompilationErrorValue = -2;
+
+ // When we store the sweep generation at which we moved the code from the
+ // code index to the saved code index we mask it of to be in the [0:255]
+ // range.
+ static const int kCodeAgeMask = 0xff;
+};
+
+
+class CompilationCacheShape : public BaseShape<HashTableKey*> {
+ public:
+ static inline bool IsMatch(HashTableKey* key, Object* value) {
+ return key->IsMatch(value);
+ }
+
+ static inline uint32_t Hash(HashTableKey* key) {
+ return key->Hash();
+ }
+
+ static inline uint32_t HashForObject(HashTableKey* key, Object* object) {
+ return key->HashForObject(object);
+ }
+
+ MUST_USE_RESULT static MaybeObject* AsObject(Heap* heap,
+ HashTableKey* key) {
+ return key->AsObject(heap);
+ }
+
+ static const int kPrefixSize = 0;
+ static const int kEntrySize = 2;
+};
+
+
+class CompilationCacheTable: public HashTable<CompilationCacheShape,
+ HashTableKey*> {
+ public:
+ // Find cached value for a string key, otherwise return null.
+ Object* Lookup(String* src, Context* context);
+ Object* LookupEval(String* src,
+ Context* context,
+ LanguageMode language_mode,
+ int scope_position);
+ Object* LookupRegExp(String* source, JSRegExp::Flags flags);
+ MUST_USE_RESULT MaybeObject* Put(String* src,
+ Context* context,
+ Object* value);
+ MUST_USE_RESULT MaybeObject* PutEval(String* src,
+ Context* context,
+ SharedFunctionInfo* value,
+ int scope_position);
+ MUST_USE_RESULT MaybeObject* PutRegExp(String* src,
+ JSRegExp::Flags flags,
+ FixedArray* value);
+
+ // Remove given value from cache.
+ void Remove(Object* value);
+
+ static inline CompilationCacheTable* cast(Object* obj);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheTable);
+};
+
+
+class CodeCache: public Struct {
+ public:
+ DECL_ACCESSORS(default_cache, FixedArray)
+ DECL_ACCESSORS(normal_type_cache, Object)
+
+ // Add the code object to the cache.
+ MUST_USE_RESULT MaybeObject* Update(Name* name, Code* code);
+
+ // Lookup code object in the cache. Returns code object if found and undefined
+ // if not.
+ Object* Lookup(Name* name, Code::Flags flags);
+
+ // Get the internal index of a code object in the cache. Returns -1 if the
+ // code object is not in that cache. This index can be used to later call
+ // RemoveByIndex. The cache cannot be modified between a call to GetIndex and
+ // RemoveByIndex.
+ int GetIndex(Object* name, Code* code);
+
+ // Remove an object from the cache with the provided internal index.
+ void RemoveByIndex(Object* name, Code* code, int index);
+
+ static inline CodeCache* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(CodeCache)
+ DECLARE_VERIFIER(CodeCache)
+
+ static const int kDefaultCacheOffset = HeapObject::kHeaderSize;
+ static const int kNormalTypeCacheOffset =
+ kDefaultCacheOffset + kPointerSize;
+ static const int kSize = kNormalTypeCacheOffset + kPointerSize;
+
+ private:
+ MUST_USE_RESULT MaybeObject* UpdateDefaultCache(Name* name, Code* code);
+ MUST_USE_RESULT MaybeObject* UpdateNormalTypeCache(Name* name, Code* code);
+ Object* LookupDefaultCache(Name* name, Code::Flags flags);
+ Object* LookupNormalTypeCache(Name* name, Code::Flags flags);
+
+ // Code cache layout of the default cache. Elements are alternating name and
+ // code objects for non normal load/store/call IC's.
+ static const int kCodeCacheEntrySize = 2;
+ static const int kCodeCacheEntryNameOffset = 0;
+ static const int kCodeCacheEntryCodeOffset = 1;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CodeCache);
+};
+
+
+class CodeCacheHashTableShape : public BaseShape<HashTableKey*> {
+ public:
+ static inline bool IsMatch(HashTableKey* key, Object* value) {
+ return key->IsMatch(value);
+ }
+
+ static inline uint32_t Hash(HashTableKey* key) {
+ return key->Hash();
+ }
+
+ static inline uint32_t HashForObject(HashTableKey* key, Object* object) {
+ return key->HashForObject(object);
+ }
+
+ MUST_USE_RESULT static MaybeObject* AsObject(Heap* heap,
+ HashTableKey* key) {
+ return key->AsObject(heap);
+ }
+
+ static const int kPrefixSize = 0;
+ static const int kEntrySize = 2;
+};
+
+
+class CodeCacheHashTable: public HashTable<CodeCacheHashTableShape,
+ HashTableKey*> {
+ public:
+ Object* Lookup(Name* name, Code::Flags flags);
+ MUST_USE_RESULT MaybeObject* Put(Name* name, Code* code);
+
+ int GetIndex(Name* name, Code::Flags flags);
+ void RemoveByIndex(int index);
+
+ static inline CodeCacheHashTable* cast(Object* obj);
+
+ // Initial size of the fixed array backing the hash table.
+ static const int kInitialSize = 64;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CodeCacheHashTable);
+};
+
+
+class PolymorphicCodeCache: public Struct {
+ public:
+ DECL_ACCESSORS(cache, Object)
+
+ 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);
+
+ // Returns an undefined value if the entry is not found.
+ Handle<Object> Lookup(MapHandleList* maps, Code::Flags flags);
+
+ static inline PolymorphicCodeCache* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(PolymorphicCodeCache)
+ DECLARE_VERIFIER(PolymorphicCodeCache)
+
+ static const int kCacheOffset = HeapObject::kHeaderSize;
+ static const int kSize = kCacheOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PolymorphicCodeCache);
+};
+
+
+class PolymorphicCodeCacheHashTable
+ : public HashTable<CodeCacheHashTableShape, HashTableKey*> {
+ public:
+ 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);
+
+ static const int kInitialSize = 64;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PolymorphicCodeCacheHashTable);
+};
+
+
+class TypeFeedbackInfo: public Struct {
+ public:
+ inline int ic_total_count();
+ inline void set_ic_total_count(int count);
+
+ inline int ic_with_type_info_count();
+ inline void change_ic_with_type_info_count(int count);
+
+ inline void initialize_storage();
+
+ inline void change_own_type_change_checksum();
+ inline int own_type_change_checksum();
+
+ inline void set_inlined_type_change_checksum(int checksum);
+ inline bool matches_inlined_type_change_checksum(int checksum);
+
+ DECL_ACCESSORS(type_feedback_cells, TypeFeedbackCells)
+
+ static inline TypeFeedbackInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(TypeFeedbackInfo)
+ DECLARE_VERIFIER(TypeFeedbackInfo)
+
+ static const int kStorage1Offset = HeapObject::kHeaderSize;
+ static const int kStorage2Offset = kStorage1Offset + kPointerSize;
+ static const int kTypeFeedbackCellsOffset = kStorage2Offset + kPointerSize;
+ static const int kSize = kTypeFeedbackCellsOffset + kPointerSize;
+
+ private:
+ static const int kTypeChangeChecksumBits = 7;
+
+ class ICTotalCountField: public BitField<int, 0,
+ kSmiValueSize - kTypeChangeChecksumBits> {}; // NOLINT
+ class OwnTypeChangeChecksum: public BitField<int,
+ kSmiValueSize - kTypeChangeChecksumBits,
+ kTypeChangeChecksumBits> {}; // NOLINT
+ class ICsWithTypeInfoCountField: public BitField<int, 0,
+ kSmiValueSize - kTypeChangeChecksumBits> {}; // NOLINT
+ class InlinedTypeChangeChecksum: public BitField<int,
+ kSmiValueSize - kTypeChangeChecksumBits,
+ kTypeChangeChecksumBits> {}; // NOLINT
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TypeFeedbackInfo);
+};
+
+
+enum AllocationSiteMode {
+ DONT_TRACK_ALLOCATION_SITE,
+ TRACK_ALLOCATION_SITE,
+ LAST_ALLOCATION_SITE_MODE = TRACK_ALLOCATION_SITE
+};
+
+
+class AllocationSite: public Struct {
+ public:
+ static const uint32_t kMaximumArrayBytesToPretransition = 8 * 1024;
+
+ DECL_ACCESSORS(transition_info, Object)
+ DECL_ACCESSORS(weak_next, Object)
+
+ void Initialize() {
+ SetElementsKind(GetInitialFastElementsKind());
+ }
+
+ ElementsKind GetElementsKind() {
+ ASSERT(!IsLiteralSite());
+ return static_cast<ElementsKind>(Smi::cast(transition_info())->value());
+ }
+
+ void SetElementsKind(ElementsKind kind) {
+ set_transition_info(Smi::FromInt(static_cast<int>(kind)));
+ }
+
+ bool IsLiteralSite() {
+ // If transition_info is a smi, then it represents an ElementsKind
+ // for a constructed array. Otherwise, it must be a boilerplate
+ // for an array literal
+ return transition_info()->IsJSArray();
+ }
+
+ DECLARE_PRINTER(AllocationSite)
+ DECLARE_VERIFIER(AllocationSite)
+
+ static inline AllocationSite* cast(Object* obj);
+ static inline AllocationSiteMode GetMode(
+ ElementsKind boilerplate_elements_kind);
+ static inline AllocationSiteMode GetMode(ElementsKind from, ElementsKind to);
+
+ static const int kTransitionInfoOffset = HeapObject::kHeaderSize;
+ static const int kWeakNextOffset = kTransitionInfoOffset + kPointerSize;
+ static const int kSize = kWeakNextOffset + kPointerSize;
+
+ typedef FixedBodyDescriptor<HeapObject::kHeaderSize,
+ kTransitionInfoOffset + kPointerSize,
+ kSize> BodyDescriptor;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AllocationSite);
+};
+
+
+class AllocationMemento: public Struct {
+ public:
+ static const int kAllocationSiteOffset = HeapObject::kHeaderSize;
+ static const int kSize = kAllocationSiteOffset + kPointerSize;
+
+ DECL_ACCESSORS(allocation_site, Object)
+
+ bool IsValid() { return allocation_site()->IsAllocationSite(); }
+ AllocationSite* GetAllocationSite() {
+ ASSERT(IsValid());
+ return AllocationSite::cast(allocation_site());
+ }
+
+ DECLARE_PRINTER(AllocationMemento)
+ DECLARE_VERIFIER(AllocationMemento)
+
+ // Returns NULL if no AllocationMemento is available for object.
+ static AllocationMemento* FindForJSObject(JSObject* object);
+ static inline AllocationMemento* cast(Object* obj);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AllocationMemento);
+};
+
+
+// Representation of a slow alias as part of a non-strict arguments objects.
+// For fast aliases (if HasNonStrictArgumentsElements()):
+// - the parameter map contains an index into the context
+// - all attributes of the element have default values
+// For slow aliases (if HasDictionaryArgumentsElements()):
+// - the parameter map contains no fast alias mapping (i.e. the hole)
+// - this struct (in the slow backing store) contains an index into the context
+// - all attributes are available as part if the property details
+class AliasedArgumentsEntry: public Struct {
+ public:
+ inline int aliased_context_slot();
+ inline void set_aliased_context_slot(int count);
+
+ static inline AliasedArgumentsEntry* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(AliasedArgumentsEntry)
+ DECLARE_VERIFIER(AliasedArgumentsEntry)
+
+ static const int kAliasedContextSlot = HeapObject::kHeaderSize;
+ static const int kSize = kAliasedContextSlot + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AliasedArgumentsEntry);
+};
+
+
+enum AllowNullsFlag {ALLOW_NULLS, DISALLOW_NULLS};
+enum RobustnessFlag {ROBUST_STRING_TRAVERSAL, FAST_STRING_TRAVERSAL};
+
+
+class StringHasher {
+ public:
+ explicit inline StringHasher(int length, uint32_t seed);
+
+ template <typename schar>
+ static inline uint32_t HashSequentialString(const schar* chars,
+ int length,
+ uint32_t seed);
+
+ // Reads all the data, even for long strings and computes the utf16 length.
+ static uint32_t ComputeUtf8Hash(Vector<const char> chars,
+ uint32_t seed,
+ int* utf16_length_out);
+
+ // Calculated hash value for a string consisting of 1 to
+ // String::kMaxArrayIndexSize digits with no leading zeros (except "0").
+ // value is represented decimal value.
+ static uint32_t MakeArrayIndexHash(uint32_t value, int length);
+
+ // No string is allowed to have a hash of zero. That value is reserved
+ // for internal properties. If the hash calculation yields zero then we
+ // use 27 instead.
+ static const int kZeroHash = 27;
+
+ // Reusable parts of the hashing algorithm.
+ INLINE(static uint32_t AddCharacterCore(uint32_t running_hash, uint16_t c));
+ INLINE(static uint32_t GetHashCore(uint32_t running_hash));
+
+ protected:
+ // Returns the value to store in the hash field of a string with
+ // the given length and contents.
+ uint32_t GetHashField();
+ // Returns true if the hash of this string can be computed without
+ // looking at the contents.
+ inline bool has_trivial_hash();
+ // Adds a block of characters to the hash.
+ template<typename Char>
+ inline void AddCharacters(const Char* chars, int len);
+
+ private:
+ // Add a character to the hash.
+ inline void AddCharacter(uint16_t c);
+ // Update index. Returns true if string is still an index.
+ inline bool UpdateIndex(uint16_t c);
+
+ int length_;
+ uint32_t raw_running_hash_;
+ uint32_t array_index_;
+ bool is_array_index_;
+ bool is_first_char_;
+ DISALLOW_COPY_AND_ASSIGN(StringHasher);
+};
+
+
+// The characteristics of a string are stored in its map. Retrieving these
+// few bits of information is moderately expensive, involving two memory
+// loads where the second is dependent on the first. To improve efficiency
+// the shape of the string is given its own class so that it can be retrieved
+// once and used for several string operations. A StringShape is small enough
+// to be passed by value and is immutable, but be aware that flattening a
+// string can potentially alter its shape. Also be aware that a GC caused by
+// something else can alter the shape of a string due to ConsString
+// shortcutting. Keeping these restrictions in mind has proven to be error-
+// prone and so we no longer put StringShapes in variables unless there is a
+// concrete performance benefit at that particular point in the code.
+class StringShape BASE_EMBEDDED {
+ public:
+ inline explicit StringShape(String* s);
+ inline explicit StringShape(Map* s);
+ inline explicit StringShape(InstanceType t);
+ inline bool IsSequential();
+ inline bool IsExternal();
+ inline bool IsCons();
+ inline bool IsSliced();
+ inline bool IsIndirect();
+ inline bool IsExternalAscii();
+ inline bool IsExternalTwoByte();
+ inline bool IsSequentialAscii();
+ inline bool IsSequentialTwoByte();
+ inline bool IsInternalized();
+ inline StringRepresentationTag representation_tag();
+ inline uint32_t encoding_tag();
+ inline uint32_t full_representation_tag();
+ inline uint32_t size_tag();
+#ifdef DEBUG
+ inline uint32_t type() { return type_; }
+ inline void invalidate() { valid_ = false; }
+ inline bool valid() { return valid_; }
+#else
+ inline void invalidate() { }
+#endif
+
+ private:
+ uint32_t type_;
+#ifdef DEBUG
+ inline void set_valid() { valid_ = true; }
+ bool valid_;
+#else
+ inline void set_valid() { }
+#endif
+};
+
+
+// The Name abstract class captures anything that can be used as a property
+// name, i.e., strings and symbols. All names store a hash value.
+class Name: public HeapObject {
+ public:
+ // Get and set the hash field of the name.
+ inline uint32_t hash_field();
+ inline void set_hash_field(uint32_t value);
+
+ // Tells whether the hash code has been computed.
+ inline bool HasHashCode();
+
+ // Returns a hash value used for the property table
+ inline uint32_t Hash();
+
+ // Equality operations.
+ inline bool Equals(Name* other);
+
+ // Conversion.
+ inline bool AsArrayIndex(uint32_t* index);
+
+ // Casting.
+ static inline Name* cast(Object* obj);
+
+ DECLARE_PRINTER(Name)
+
+ // Layout description.
+ static const int kHashFieldOffset = HeapObject::kHeaderSize;
+ static const int kSize = kHashFieldOffset + kPointerSize;
+
+ // Mask constant for checking if a name has a computed hash code
+ // and if it is a string that is an array index. The least significant bit
+ // indicates whether a hash code has been computed. If the hash code has
+ // been computed the 2nd bit tells whether the string can be used as an
+ // array index.
+ static const int kHashNotComputedMask = 1;
+ static const int kIsNotArrayIndexMask = 1 << 1;
+ static const int kNofHashBitFields = 2;
+
+ // Shift constant retrieving hash code from hash field.
+ static const int kHashShift = kNofHashBitFields;
+
+ // Only these bits are relevant in the hash, since the top two are shifted
+ // out.
+ static const uint32_t kHashBitMask = 0xffffffffu >> kHashShift;
+
+ // Array index strings this short can keep their index in the hash field.
+ static const int kMaxCachedArrayIndexLength = 7;
+
+ // For strings which are array indexes the hash value has the string length
+ // mixed into the hash, mainly to avoid a hash value of zero which would be
+ // the case for the string '0'. 24 bits are used for the array index value.
+ static const int kArrayIndexValueBits = 24;
+ static const int kArrayIndexLengthBits =
+ kBitsPerInt - kArrayIndexValueBits - kNofHashBitFields;
+
+ STATIC_CHECK((kArrayIndexLengthBits > 0));
+
+ static const int kArrayIndexHashLengthShift =
+ kArrayIndexValueBits + kNofHashBitFields;
+
+ static const int kArrayIndexHashMask = (1 << kArrayIndexHashLengthShift) - 1;
+
+ static const int kArrayIndexValueMask =
+ ((1 << kArrayIndexValueBits) - 1) << kHashShift;
+
+ // Check that kMaxCachedArrayIndexLength + 1 is a power of two so we
+ // could use a mask to test if the length of string is less than or equal to
+ // kMaxCachedArrayIndexLength.
+ STATIC_CHECK(IS_POWER_OF_TWO(kMaxCachedArrayIndexLength + 1));
+
+ static const int kContainsCachedArrayIndexMask =
+ (~kMaxCachedArrayIndexLength << kArrayIndexHashLengthShift) |
+ kIsNotArrayIndexMask;
+
+ // Value of empty hash field indicating that the hash is not computed.
+ static const int kEmptyHashField =
+ kIsNotArrayIndexMask | kHashNotComputedMask;
+
+ protected:
+ static inline bool IsHashFieldComputed(uint32_t field);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Name);
+};
+
+
+// ES6 symbols.
+class Symbol: public Name {
+ public:
+ // [name]: the print name of a symbol, or undefined if none.
+ DECL_ACCESSORS(name, Object)
+
+ // Casting.
+ static inline Symbol* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(Symbol)
+ DECLARE_VERIFIER(Symbol)
+
+ // Layout description.
+ static const int kNameOffset = Name::kSize;
+ static const int kSize = kNameOffset + kPointerSize;
+
+ typedef FixedBodyDescriptor<kNameOffset, kNameOffset + kPointerSize, kSize>
+ BodyDescriptor;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Symbol);
+};
+
+
+class ConsString;
+
+// The String abstract class captures JavaScript string values:
+//
+// Ecma-262:
+// 4.3.16 String Value
+// A string value is a member of the type String and is a finite
+// ordered sequence of zero or more 16-bit unsigned integer values.
+//
+// All string values have a length field.
+class String: public Name {
+ public:
+ enum Encoding { ONE_BYTE_ENCODING, TWO_BYTE_ENCODING };
+
+ // Representation of the flat content of a String.
+ // A non-flat string doesn't have flat content.
+ // A flat string has content that's encoded as a sequence of either
+ // ASCII chars or two-byte UC16.
+ // Returned by String::GetFlatContent().
+ class FlatContent {
+ public:
+ // Returns true if the string is flat and this structure contains content.
+ bool IsFlat() { return state_ != NON_FLAT; }
+ // Returns true if the structure contains ASCII content.
+ bool IsAscii() { return state_ == ASCII; }
+ // Returns true if the structure contains two-byte content.
+ bool IsTwoByte() { return state_ == TWO_BYTE; }
+
+ // Return the one byte content of the string. Only use if IsAscii() returns
+ // true.
+ Vector<const uint8_t> ToOneByteVector() {
+ ASSERT_EQ(ASCII, state_);
+ return buffer_;
+ }
+ // Return the two-byte content of the string. Only use if IsTwoByte()
+ // returns true.
+ Vector<const uc16> ToUC16Vector() {
+ ASSERT_EQ(TWO_BYTE, state_);
+ return Vector<const uc16>::cast(buffer_);
+ }
+
+ private:
+ enum State { NON_FLAT, ASCII, TWO_BYTE };
+
+ // Constructors only used by String::GetFlatContent().
+ explicit FlatContent(Vector<const uint8_t> chars)
+ : buffer_(chars),
+ state_(ASCII) { }
+ explicit FlatContent(Vector<const uc16> chars)
+ : buffer_(Vector<const byte>::cast(chars)),
+ state_(TWO_BYTE) { }
+ FlatContent() : buffer_(), state_(NON_FLAT) { }
+
+ Vector<const uint8_t> buffer_;
+ State state_;
+
+ friend class String;
+ };
+
+ // Get and set the length of the string.
+ inline int length();
+ inline void set_length(int value);
+
+ // Returns whether this string has only ASCII chars, i.e. all of them can
+ // be ASCII encoded. This might be the case even if the string is
+ // two-byte. Such strings may appear when the embedder prefers
+ // two-byte external representations even for ASCII data.
+ inline bool IsOneByteRepresentation();
+ inline bool IsTwoByteRepresentation();
+
+ // Cons and slices have an encoding flag that may not represent the actual
+ // encoding of the underlying string. This is taken into account here.
+ // Requires: this->IsFlat()
+ inline bool IsOneByteRepresentationUnderneath();
+ inline bool IsTwoByteRepresentationUnderneath();
+
+ // NOTE: this should be considered only a hint. False negatives are
+ // possible.
+ inline bool HasOnlyOneByteChars();
+
+ // Get and set individual two byte chars in the string.
+ inline void Set(int index, uint16_t value);
+ // Get individual two byte char in the string. Repeated calls
+ // to this method are not efficient unless the string is flat.
+ INLINE(uint16_t Get(int index));
+
+ // Try to flatten the string. Checks first inline to see if it is
+ // necessary. Does nothing if the string is not a cons string.
+ // Flattening allocates a sequential string with the same data as
+ // the given string and mutates the cons string to a degenerate
+ // form, where the first component is the new sequential string and
+ // the second component is the empty string. If allocation fails,
+ // this function returns a failure. If flattening succeeds, this
+ // function returns the sequential string that is now the first
+ // component of the cons string.
+ //
+ // Degenerate cons strings are handled specially by the garbage
+ // collector (see IsShortcutCandidate).
+ //
+ // Use FlattenString from Handles.cc to flatten even in case an
+ // allocation failure happens.
+ inline MaybeObject* TryFlatten(PretenureFlag pretenure = NOT_TENURED);
+
+ // Convenience function. Has exactly the same behavior as
+ // TryFlatten(), except in the case of failure returns the original
+ // string.
+ inline String* TryFlattenGetString(PretenureFlag pretenure = NOT_TENURED);
+
+ // Tries to return the content of a flat string as a structure holding either
+ // a flat vector of char or of uc16.
+ // If the string isn't flat, and therefore doesn't have flat content, the
+ // returned structure will report so, and can't provide a vector of either
+ // kind.
+ FlatContent GetFlatContent();
+
+ // Returns the parent of a sliced string or first part of a flat cons string.
+ // Requires: StringShape(this).IsIndirect() && this->IsFlat()
+ inline String* GetUnderlying();
+
+ // Mark the string as an undetectable object. It only applies to
+ // ASCII and two byte string types.
+ bool MarkAsUndetectable();
+
+ // Return a substring.
+ MUST_USE_RESULT MaybeObject* SubString(int from,
+ int to,
+ PretenureFlag pretenure = NOT_TENURED);
+
+ // String equality operations.
+ inline bool Equals(String* other);
+ bool IsUtf8EqualTo(Vector<const char> str, bool allow_prefix_match = false);
+ bool IsOneByteEqualTo(Vector<const uint8_t> str);
+ bool IsTwoByteEqualTo(Vector<const uc16> str);
+
+ // Return a UTF8 representation of the string. The string is null
+ // terminated but may optionally contain nulls. Length is returned
+ // in length_output if length_output is not a null pointer The string
+ // should be nearly flat, otherwise the performance of this method may
+ // be very slow (quadratic in the length). Setting robustness_flag to
+ // ROBUST_STRING_TRAVERSAL invokes behaviour that is robust This means it
+ // handles unexpected data without causing assert failures and it does not
+ // do any heap allocations. This is useful when printing stack traces.
+ SmartArrayPointer<char> ToCString(AllowNullsFlag allow_nulls,
+ RobustnessFlag robustness_flag,
+ int offset,
+ int length,
+ int* length_output = 0);
+ SmartArrayPointer<char> ToCString(
+ AllowNullsFlag allow_nulls = DISALLOW_NULLS,
+ RobustnessFlag robustness_flag = FAST_STRING_TRAVERSAL,
+ int* length_output = 0);
+
+ // Return a 16 bit Unicode representation of the string.
+ // The string should be nearly flat, otherwise the performance of
+ // of this method may be very bad. Setting robustness_flag to
+ // ROBUST_STRING_TRAVERSAL invokes behaviour that is robust This means it
+ // handles unexpected data without causing assert failures and it does not
+ // do any heap allocations. This is useful when printing stack traces.
+ SmartArrayPointer<uc16> ToWideCString(
+ RobustnessFlag robustness_flag = FAST_STRING_TRAVERSAL);
+
+ bool ComputeArrayIndex(uint32_t* index);
+
+ // Externalization.
+ bool MakeExternal(v8::String::ExternalStringResource* resource);
+ bool MakeExternal(v8::String::ExternalAsciiStringResource* resource);
+
+ // Conversion.
+ inline bool AsArrayIndex(uint32_t* index);
+
+ // Casting.
+ static inline String* cast(Object* obj);
+
+ void PrintOn(FILE* out);
+
+ // For use during stack traces. Performs rudimentary sanity check.
+ bool LooksValid();
+
+ // Dispatched behavior.
+ void StringShortPrint(StringStream* accumulator);
+#ifdef OBJECT_PRINT
+ inline void StringPrint() {
+ StringPrint(stdout);
+ }
+ void StringPrint(FILE* out);
+
+ char* ToAsciiArray();
+#endif
+ DECLARE_VERIFIER(String)
+
+ inline bool IsFlat();
+
+ // Layout description.
+ static const int kLengthOffset = Name::kSize;
+ static const int kSize = kLengthOffset + kPointerSize;
+
+ // Maximum number of characters to consider when trying to convert a string
+ // value into an array index.
+ static const int kMaxArrayIndexSize = 10;
+ STATIC_CHECK(kMaxArrayIndexSize < (1 << kArrayIndexLengthBits));
+
+ // Max char codes.
+ static const int32_t kMaxOneByteCharCode = unibrow::Latin1::kMaxChar;
+ static const uint32_t kMaxOneByteCharCodeU = unibrow::Latin1::kMaxChar;
+ static const int kMaxUtf16CodeUnit = 0xffff;
+
+ // Value of hash field containing computed hash equal to zero.
+ static const int kEmptyStringHash = kIsNotArrayIndexMask;
+
+ // Maximal string length.
+ static const int kMaxLength = (1 << (32 - 2)) - 1;
+
+ // Max length for computing hash. For strings longer than this limit the
+ // string length is used as the hash value.
+ static const int kMaxHashCalcLength = 16383;
+
+ // Limit for truncation in short printing.
+ static const int kMaxShortPrintLength = 1024;
+
+ // Support for regular expressions.
+ const uc16* GetTwoByteData();
+ const uc16* GetTwoByteData(unsigned start);
+
+ // Helper function for flattening strings.
+ template <typename sinkchar>
+ static void WriteToFlat(String* source,
+ sinkchar* sink,
+ int from,
+ int to);
+
+ // The return value may point to the first aligned word containing the
+ // first non-ascii character, rather than directly to the non-ascii character.
+ // If the return value is >= the passed length, the entire string was ASCII.
+ static inline int NonAsciiStart(const char* chars, int length) {
+ const char* start = chars;
+ const char* limit = chars + length;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ ASSERT(unibrow::Utf8::kMaxOneByteChar == 0x7F);
+ const uintptr_t non_ascii_mask = kUintptrAllBitsSet / 0xFF * 0x80;
+ while (chars + sizeof(uintptr_t) <= limit) {
+ if (*reinterpret_cast<const uintptr_t*>(chars) & non_ascii_mask) {
+ return static_cast<int>(chars - start);
+ }
+ chars += sizeof(uintptr_t);
+ }
+#endif
+ while (chars < limit) {
+ if (static_cast<uint8_t>(*chars) > unibrow::Utf8::kMaxOneByteChar) {
+ return static_cast<int>(chars - start);
+ }
+ ++chars;
+ }
+ return static_cast<int>(chars - start);
+ }
+
+ static inline bool IsAscii(const char* chars, int length) {
+ return NonAsciiStart(chars, length) >= length;
+ }
+
+ static inline bool IsAscii(const uint8_t* chars, int length) {
+ return
+ NonAsciiStart(reinterpret_cast<const char*>(chars), length) >= length;
+ }
+
+ static inline int NonOneByteStart(const uc16* chars, int length) {
+ const uc16* limit = chars + length;
+ const uc16* start = chars;
+ while (chars < limit) {
+ if (*chars > kMaxOneByteCharCodeU) return static_cast<int>(chars - start);
+ ++chars;
+ }
+ return static_cast<int>(chars - start);
+ }
+
+ static inline bool IsOneByte(const uc16* chars, int length) {
+ return NonOneByteStart(chars, length) >= length;
+ }
+
+ // TODO(dcarney): Replace all instances of this with VisitFlat.
+ template<class Visitor, class ConsOp>
+ static inline void Visit(String* string,
+ unsigned offset,
+ Visitor& visitor,
+ ConsOp& cons_op,
+ int32_t type,
+ unsigned length);
+
+ template<class Visitor>
+ static inline ConsString* VisitFlat(Visitor* visitor,
+ String* string,
+ int offset,
+ int length,
+ int32_t type);
+
+ template<class Visitor>
+ static inline ConsString* VisitFlat(Visitor* visitor,
+ String* string,
+ int offset = 0) {
+ int32_t type = string->map()->instance_type();
+ return VisitFlat(visitor, string, offset, string->length(), type);
+ }
+
+ private:
+ friend class Name;
+
+ // Try to flatten the top level ConsString that is hiding behind this
+ // string. This is a no-op unless the string is a ConsString. Flatten
+ // mutates the ConsString and might return a failure.
+ MUST_USE_RESULT MaybeObject* SlowTryFlatten(PretenureFlag pretenure);
+
+ // Slow case of String::Equals. This implementation works on any strings
+ // but it is most efficient on strings that are almost flat.
+ bool SlowEquals(String* other);
+
+ // Slow case of AsArrayIndex.
+ bool SlowAsArrayIndex(uint32_t* index);
+
+ // Compute and set the hash code.
+ uint32_t ComputeAndSetHash();
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(String);
+};
+
+
+// The SeqString abstract class captures sequential string values.
+class SeqString: public String {
+ public:
+ // Casting.
+ static inline SeqString* cast(Object* obj);
+
+ // Layout description.
+ static const int kHeaderSize = String::kSize;
+
+ // Truncate the string in-place if possible and return the result.
+ // In case of new_length == 0, the empty string is returned without
+ // truncating the original string.
+ MUST_USE_RESULT static Handle<String> Truncate(Handle<SeqString> string,
+ int new_length);
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SeqString);
+};
+
+
+// The AsciiString class captures sequential ASCII string objects.
+// Each character in the AsciiString is an ASCII character.
+class SeqOneByteString: public SeqString {
+ public:
+ static const bool kHasAsciiEncoding = true;
+
+ // Dispatched behavior.
+ inline uint16_t SeqOneByteStringGet(int index);
+ inline void SeqOneByteStringSet(int index, uint16_t value);
+
+ // Get the address of the characters in this string.
+ inline Address GetCharsAddress();
+
+ inline uint8_t* GetChars();
+
+ // Casting
+ static inline SeqOneByteString* cast(Object* obj);
+
+ // Garbage collection support. This method is called by the
+ // garbage collector to compute the actual size of an AsciiString
+ // instance.
+ inline int SeqOneByteStringSize(InstanceType instance_type);
+
+ // Computes the size for an AsciiString instance of a given length.
+ static int SizeFor(int length) {
+ return OBJECT_POINTER_ALIGN(kHeaderSize + length * kCharSize);
+ }
+
+ // Maximal memory usage for a single sequential ASCII string.
+ 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);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SeqOneByteString);
+};
+
+
+// The TwoByteString class captures sequential unicode string objects.
+// Each character in the TwoByteString is a two-byte uint16_t.
+class SeqTwoByteString: public SeqString {
+ public:
+ static const bool kHasAsciiEncoding = false;
+
+ // Dispatched behavior.
+ inline uint16_t SeqTwoByteStringGet(int index);
+ inline void SeqTwoByteStringSet(int index, uint16_t value);
+
+ // Get the address of the characters in this string.
+ inline Address GetCharsAddress();
+
+ inline uc16* GetChars();
+
+ // For regexp code.
+ const uint16_t* SeqTwoByteStringGetData(unsigned start);
+
+ // Casting
+ static inline SeqTwoByteString* cast(Object* obj);
+
+ // Garbage collection support. This method is called by the
+ // garbage collector to compute the actual size of a TwoByteString
+ // instance.
+ inline int SeqTwoByteStringSize(InstanceType instance_type);
+
+ // Computes the size for a TwoByteString instance of a given length.
+ static int SizeFor(int length) {
+ return OBJECT_POINTER_ALIGN(kHeaderSize + length * kShortSize);
+ }
+
+ // Maximal memory usage for a single sequential two-byte string.
+ 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);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SeqTwoByteString);
+};
+
+
+// The ConsString class describes string values built by using the
+// addition operator on strings. A ConsString is a pair where the
+// first and second components are pointers to other string values.
+// One or both components of a ConsString can be pointers to other
+// ConsStrings, creating a binary tree of ConsStrings where the leaves
+// are non-ConsString string values. The string value represented by
+// a ConsString can be obtained by concatenating the leaf string
+// values in a left-to-right depth-first traversal of the tree.
+class ConsString: public String {
+ public:
+ // First string of the cons cell.
+ inline String* first();
+ // Doesn't check that the result is a string, even in debug mode. This is
+ // useful during GC where the mark bits confuse the checks.
+ inline Object* unchecked_first();
+ inline void set_first(String* first,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+
+ // Second string of the cons cell.
+ inline String* second();
+ // Doesn't check that the result is a string, even in debug mode. This is
+ // useful during GC where the mark bits confuse the checks.
+ inline Object* unchecked_second();
+ inline void set_second(String* second,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+
+ // Dispatched behavior.
+ uint16_t ConsStringGet(int index);
+
+ // Casting.
+ static inline ConsString* cast(Object* obj);
+
+ // Layout description.
+ static const int kFirstOffset = POINTER_SIZE_ALIGN(String::kSize);
+ static const int kSecondOffset = kFirstOffset + kPointerSize;
+ static const int kSize = kSecondOffset + kPointerSize;
+
+ // Minimum length for a cons string.
+ static const int kMinLength = 13;
+
+ typedef FixedBodyDescriptor<kFirstOffset, kSecondOffset + kPointerSize, kSize>
+ BodyDescriptor;
+
+ DECLARE_VERIFIER(ConsString)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ConsString);
+};
+
+
+// The Sliced String class describes strings that are substrings of another
+// sequential string. The motivation is to save time and memory when creating
+// a substring. A Sliced String is described as a pointer to the parent,
+// the offset from the start of the parent string and the length. Using
+// a Sliced String therefore requires unpacking of the parent string and
+// adding the offset to the start address. A substring of a Sliced String
+// are not nested since the double indirection is simplified when creating
+// such a substring.
+// Currently missing features are:
+// - handling externalized parent strings
+// - external strings as parent
+// - truncating sliced string to enable otherwise unneeded parent to be GC'ed.
+class SlicedString: public String {
+ public:
+ inline String* parent();
+ inline void set_parent(String* parent,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+ inline int offset();
+ inline void set_offset(int offset);
+
+ // Dispatched behavior.
+ uint16_t SlicedStringGet(int index);
+
+ // Casting.
+ static inline SlicedString* cast(Object* obj);
+
+ // Layout description.
+ static const int kParentOffset = POINTER_SIZE_ALIGN(String::kSize);
+ static const int kOffsetOffset = kParentOffset + kPointerSize;
+ static const int kSize = kOffsetOffset + kPointerSize;
+
+ // Minimum length for a sliced string.
+ static const int kMinLength = 13;
+
+ typedef FixedBodyDescriptor<kParentOffset,
+ kOffsetOffset + kPointerSize, kSize>
+ BodyDescriptor;
+
+ DECLARE_VERIFIER(SlicedString)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SlicedString);
+};
+
+
+// The ExternalString class describes string values that are backed by
+// a string resource that lies outside the V8 heap. ExternalStrings
+// consist of the length field common to all strings, a pointer to the
+// external resource. It is important to ensure (externally) that the
+// resource is not deallocated while the ExternalString is live in the
+// V8 heap.
+//
+// The API expects that all ExternalStrings are created through the
+// API. Therefore, ExternalStrings should not be used internally.
+class ExternalString: public String {
+ public:
+ // Casting
+ static inline ExternalString* cast(Object* obj);
+
+ // Layout description.
+ static const int kResourceOffset = POINTER_SIZE_ALIGN(String::kSize);
+ static const int kShortSize = kResourceOffset + kPointerSize;
+ static const int kResourceDataOffset = kResourceOffset + kPointerSize;
+ static const int kSize = kResourceDataOffset + kPointerSize;
+
+ static const int kMaxShortLength =
+ (kShortSize - SeqString::kHeaderSize) / kCharSize;
+
+ // Return whether external string is short (data pointer is not cached).
+ inline bool is_short();
+
+ STATIC_CHECK(kResourceOffset == Internals::kStringResourceOffset);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalString);
+};
+
+
+// The ExternalAsciiString class is an external string backed by an
+// ASCII string.
+class ExternalAsciiString: public ExternalString {
+ public:
+ static const bool kHasAsciiEncoding = true;
+
+ typedef v8::String::ExternalAsciiStringResource Resource;
+
+ // The underlying resource.
+ 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 uint8_t* GetChars();
+
+ // Dispatched behavior.
+ inline uint16_t ExternalAsciiStringGet(int index);
+
+ // Casting.
+ static inline ExternalAsciiString* cast(Object* obj);
+
+ // Garbage collection support.
+ inline void ExternalAsciiStringIterateBody(ObjectVisitor* v);
+
+ template<typename StaticVisitor>
+ inline void ExternalAsciiStringIterateBody();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalAsciiString);
+};
+
+
+// The ExternalTwoByteString class is an external string backed by a UTF-16
+// encoded string.
+class ExternalTwoByteString: public ExternalString {
+ public:
+ static const bool kHasAsciiEncoding = false;
+
+ typedef v8::String::ExternalStringResource Resource;
+
+ // The underlying string resource.
+ 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.
+ inline uint16_t ExternalTwoByteStringGet(int index);
+
+ // For regexp code.
+ inline const uint16_t* ExternalTwoByteStringGetData(unsigned start);
+
+ // Casting.
+ static inline ExternalTwoByteString* cast(Object* obj);
+
+ // Garbage collection support.
+ inline void ExternalTwoByteStringIterateBody(ObjectVisitor* v);
+
+ template<typename StaticVisitor>
+ inline void ExternalTwoByteStringIterateBody();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalTwoByteString);
+};
+
+
+// Utility superclass for stack-allocated objects that must be updated
+// on gc. It provides two ways for the gc to update instances, either
+// iterating or updating after gc.
+class Relocatable BASE_EMBEDDED {
+ public:
+ explicit inline Relocatable(Isolate* isolate);
+ inline virtual ~Relocatable();
+ virtual void IterateInstance(ObjectVisitor* v) { }
+ virtual void PostGarbageCollection() { }
+
+ static void PostGarbageCollectionProcessing();
+ static int ArchiveSpacePerThread();
+ static char* ArchiveState(Isolate* isolate, char* to);
+ static char* RestoreState(Isolate* isolate, char* from);
+ static void Iterate(ObjectVisitor* v);
+ static void Iterate(ObjectVisitor* v, Relocatable* top);
+ static char* Iterate(ObjectVisitor* v, char* t);
+ private:
+ Isolate* isolate_;
+ Relocatable* prev_;
+};
+
+
+// A flat string reader provides random access to the contents of a
+// string independent of the character width of the string. The handle
+// must be valid as long as the reader is being used.
+class FlatStringReader : public Relocatable {
+ public:
+ FlatStringReader(Isolate* isolate, Handle<String> str);
+ FlatStringReader(Isolate* isolate, Vector<const char> input);
+ void PostGarbageCollection();
+ inline uc32 Get(int index);
+ int length() { return length_; }
+ private:
+ String** str_;
+ bool is_ascii_;
+ int length_;
+ const void* start_;
+};
+
+
+// A ConsStringOp that returns null.
+// Useful when the operation to apply on a ConsString
+// requires an expensive data structure.
+class ConsStringNullOp {
+ public:
+ inline ConsStringNullOp() {}
+ static inline String* Operate(String*, unsigned*, int32_t*, unsigned*);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConsStringNullOp);
+};
+
+
+// This maintains an off-stack representation of the stack frames required
+// to traverse a ConsString, allowing an entirely iterative and restartable
+// traversal of the entire string
+// Note: this class is not GC-safe.
+class ConsStringIteratorOp {
+ public:
+ inline ConsStringIteratorOp() {}
+ String* Operate(String* string,
+ unsigned* offset_out,
+ int32_t* type_out,
+ unsigned* length_out);
+ inline String* ContinueOperation(int32_t* type_out, unsigned* length_out);
+ inline void Reset();
+ inline bool HasMore();
+
+ private:
+ // TODO(dcarney): Templatize this out for different stack sizes.
+ static const unsigned kStackSize = 32;
+ // Use a mask instead of doing modulo operations for stack wrapping.
+ static const unsigned kDepthMask = kStackSize-1;
+ STATIC_ASSERT(IS_POWER_OF_TWO(kStackSize));
+ static inline unsigned OffsetForDepth(unsigned depth);
+
+ inline void PushLeft(ConsString* string);
+ inline void PushRight(ConsString* string);
+ inline void AdjustMaximumDepth();
+ inline void Pop();
+ String* NextLeaf(bool* blew_stack, int32_t* type_out, unsigned* length_out);
+ String* Search(unsigned* offset_out,
+ int32_t* type_out,
+ unsigned* length_out);
+
+ unsigned depth_;
+ unsigned maximum_depth_;
+ // Stack must always contain only frames for which right traversal
+ // has not yet been performed.
+ ConsString* frames_[kStackSize];
+ unsigned consumed_;
+ ConsString* root_;
+ DISALLOW_COPY_AND_ASSIGN(ConsStringIteratorOp);
+};
+
+
+// Note: this class is not GC-safe.
+class StringCharacterStream {
+ public:
+ inline StringCharacterStream(String* string,
+ ConsStringIteratorOp* op,
+ unsigned offset = 0);
+ inline uint16_t GetNext();
+ inline bool HasMore();
+ inline void Reset(String* string, unsigned offset = 0);
+ inline void VisitOneByteString(const uint8_t* chars, unsigned length);
+ inline void VisitTwoByteString(const uint16_t* chars, unsigned length);
+
+ private:
+ bool is_one_byte_;
+ union {
+ const uint8_t* buffer8_;
+ const uint16_t* buffer16_;
+ };
+ const uint8_t* end_;
+ ConsStringIteratorOp* op_;
+ DISALLOW_COPY_AND_ASSIGN(StringCharacterStream);
+};
+
+
+template <typename T>
+class VectorIterator {
+ public:
+ VectorIterator(T* d, int l) : data_(Vector<const T>(d, l)), index_(0) { }
+ explicit VectorIterator(Vector<const T> data) : data_(data), index_(0) { }
+ T GetNext() { return data_[index_++]; }
+ bool has_more() { return index_ < data_.length(); }
+ private:
+ Vector<const T> data_;
+ int index_;
+};
+
+
+// The Oddball describes objects null, undefined, true, and false.
+class Oddball: public HeapObject {
+ public:
+ // [to_string]: Cached to_string computed at startup.
+ DECL_ACCESSORS(to_string, String)
+
+ // [to_number]: Cached to_number computed at startup.
+ DECL_ACCESSORS(to_number, Object)
+
+ inline byte kind();
+ inline void set_kind(byte kind);
+
+ // Casting.
+ static inline Oddball* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_VERIFIER(Oddball)
+
+ // Initialize the fields.
+ MUST_USE_RESULT MaybeObject* Initialize(const char* to_string,
+ Object* to_number,
+ byte kind);
+
+ // Layout description.
+ static const int kToStringOffset = HeapObject::kHeaderSize;
+ static const int kToNumberOffset = kToStringOffset + kPointerSize;
+ static const int kKindOffset = kToNumberOffset + kPointerSize;
+ static const int kSize = kKindOffset + kPointerSize;
+
+ static const byte kFalse = 0;
+ static const byte kTrue = 1;
+ static const byte kNotBooleanMask = ~1;
+ static const byte kTheHole = 2;
+ static const byte kNull = 3;
+ static const byte kArgumentMarker = 4;
+ static const byte kUndefined = 5;
+ static const byte kUninitialized = 6;
+ static const byte kOther = 7;
+
+ typedef FixedBodyDescriptor<kToStringOffset,
+ kToNumberOffset + kPointerSize,
+ kSize> BodyDescriptor;
+
+ STATIC_CHECK(kKindOffset == Internals::kOddballKindOffset);
+ STATIC_CHECK(kNull == Internals::kNullOddballKind);
+ STATIC_CHECK(kUndefined == Internals::kUndefinedOddballKind);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Oddball);
+};
+
+
+class Cell: public HeapObject {
+ public:
+ // [value]: value of the global property.
+ DECL_ACCESSORS(value, Object)
+
+ // Casting.
+ static inline Cell* cast(Object* obj);
+
+ static inline Cell* FromValueAddress(Address value) {
+ Object* result = FromAddress(value - kValueOffset);
+ ASSERT(result->IsCell() || result->IsPropertyCell());
+ return static_cast<Cell*>(result);
+ }
+
+ inline Address ValueAddress() {
+ return address() + kValueOffset;
+ }
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(Cell)
+ DECLARE_VERIFIER(Cell)
+
+ // Layout description.
+ static const int kValueOffset = HeapObject::kHeaderSize;
+ static const int kSize = kValueOffset + kPointerSize;
+
+ typedef FixedBodyDescriptor<kValueOffset,
+ kValueOffset + kPointerSize,
+ kSize> BodyDescriptor;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Cell);
+};
+
+
+class PropertyCell: public Cell {
+ public:
+ // [type]: type of the global property.
+ Type* type();
+ void set_type(Type* value, WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+
+ // [dependent_code]: dependent code that depends on the type of the global
+ // property.
+ DECL_ACCESSORS(dependent_code, DependentCode)
+
+ // Sets the value of the cell and updates the type field to be the union
+ // of the cell's current type and the value's type. If the change causes
+ // a change of the type of the cell's contents, code dependent on the cell
+ // will be deoptimized.
+ MUST_USE_RESULT MaybeObject* SetValueInferType(
+ Object* value,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+
+ // Casting.
+ static inline PropertyCell* cast(Object* obj);
+
+ inline Address TypeAddress() {
+ return address() + kTypeOffset;
+ }
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(PropertyCell)
+ DECLARE_VERIFIER(PropertyCell)
+
+ // Layout description.
+ static const int kTypeOffset = kValueOffset + kPointerSize;
+ static const int kDependentCodeOffset = kTypeOffset + kPointerSize;
+ static const int kSize = kDependentCodeOffset + kPointerSize;
+
+ static const int kPointerFieldsBeginOffset = kValueOffset;
+ static const int kPointerFieldsEndOffset = kDependentCodeOffset;
+
+ typedef FixedBodyDescriptor<kValueOffset,
+ kSize,
+ kSize> BodyDescriptor;
+
+ void AddDependentCompilationInfo(CompilationInfo* info);
+
+ void AddDependentCode(Handle<Code> code);
+
+ static Type* UpdateType(Handle<PropertyCell> cell,
+ Handle<Object> value);
+
+ private:
+ DECL_ACCESSORS(type_raw, Object)
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PropertyCell);
+};
+
+
+// The JSProxy describes EcmaScript Harmony proxies
+class JSProxy: public JSReceiver {
+ public:
+ // [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(Name* name);
+ bool HasElementWithHandler(uint32_t index);
+
+ MUST_USE_RESULT MaybeObject* GetPropertyWithHandler(
+ Object* receiver,
+ Name* name);
+ MUST_USE_RESULT MaybeObject* GetElementWithHandler(
+ Object* receiver,
+ uint32_t index);
+
+ MUST_USE_RESULT MaybeObject* SetPropertyWithHandler(
+ JSReceiver* receiver,
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode);
+ MUST_USE_RESULT MaybeObject* SetElementWithHandler(
+ JSReceiver* receiver,
+ uint32_t index,
+ Object* value,
+ StrictModeFlag strict_mode);
+
+ // If the handler defines an accessor property with a setter, invoke it.
+ // If it defines an accessor property without a setter, or a data property
+ // that is read-only, throw. In all these cases set '*done' to true,
+ // otherwise set it to false.
+ MUST_USE_RESULT MaybeObject* SetPropertyViaPrototypesWithHandler(
+ JSReceiver* receiver,
+ Name* name,
+ Object* value,
+ PropertyAttributes attributes,
+ StrictModeFlag strict_mode,
+ bool* done);
+
+ MUST_USE_RESULT PropertyAttributes GetPropertyAttributeWithHandler(
+ JSReceiver* receiver,
+ Name* 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();
+
+ // 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.
+ DECLARE_PRINTER(JSProxy)
+ DECLARE_VERIFIER(JSProxy)
+
+ // Layout description. We add padding so that a proxy has the same
+ // size as a virgin JSObject. This is essential for becoming a JSObject
+ // upon freeze.
+ static const int kHandlerOffset = HeapObject::kHeaderSize;
+ 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;
+
+ STATIC_CHECK(kPaddingSize >= 0);
+
+ typedef FixedBodyDescriptor<kHandlerOffset,
+ kPaddingOffset,
+ kSize> BodyDescriptor;
+
+ private:
+ friend class JSReceiver;
+
+ static Handle<Object> DeletePropertyWithHandler(Handle<JSProxy> object,
+ Handle<Name> name,
+ DeleteMode mode);
+ static Handle<Object> DeleteElementWithHandler(Handle<JSProxy> object,
+ uint32_t index,
+ DeleteMode mode);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSProxy);
+};
+
+
+class JSFunctionProxy: public JSProxy {
+ public:
+ // [call_trap]: The call trap.
+ DECL_ACCESSORS(call_trap, Object)
+
+ // [construct_trap]: The construct trap.
+ DECL_ACCESSORS(construct_trap, Object)
+
+ // Casting.
+ static inline JSFunctionProxy* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSFunctionProxy)
+ DECLARE_VERIFIER(JSFunctionProxy)
+
+ // Layout description.
+ static const int kCallTrapOffset = JSProxy::kPaddingOffset;
+ static const int kConstructTrapOffset = kCallTrapOffset + kPointerSize;
+ static const int kPaddingOffset = kConstructTrapOffset + kPointerSize;
+ static const int kSize = JSFunction::kSize;
+ static const int kPaddingSize = kSize - kPaddingOffset;
+
+ STATIC_CHECK(kPaddingSize >= 0);
+
+ typedef FixedBodyDescriptor<kHandlerOffset,
+ kConstructTrapOffset + kPointerSize,
+ kSize> BodyDescriptor;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSFunctionProxy);
+};
+
+
+// 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);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSSet)
+ DECLARE_VERIFIER(JSSet)
+
+ 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);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSMap)
+ DECLARE_VERIFIER(JSMap)
+
+ static const int kTableOffset = JSObject::kHeaderSize;
+ static const int kSize = kTableOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSMap);
+};
+
+
+// Base class for both JSWeakMap and JSWeakSet
+class JSWeakCollection: public JSObject {
+ public:
+ // [table]: the backing hash table mapping keys to values.
+ DECL_ACCESSORS(table, Object)
+
+ // [next]: linked list of encountered weak maps during GC.
+ DECL_ACCESSORS(next, Object)
+
+ static const int kTableOffset = JSObject::kHeaderSize;
+ static const int kNextOffset = kTableOffset + kPointerSize;
+ static const int kSize = kNextOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakCollection);
+};
+
+
+// The JSWeakMap describes EcmaScript Harmony weak maps
+class JSWeakMap: public JSWeakCollection {
+ public:
+ // Casting.
+ static inline JSWeakMap* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSWeakMap)
+ DECLARE_VERIFIER(JSWeakMap)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakMap);
+};
+
+
+// The JSWeakSet describes EcmaScript Harmony weak sets
+class JSWeakSet: public JSWeakCollection {
+ public:
+ // Casting.
+ static inline JSWeakSet* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSWeakSet)
+ DECLARE_VERIFIER(JSWeakSet)
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSWeakSet);
+};
+
+
+class JSArrayBuffer: public JSObject {
+ public:
+ // [backing_store]: backing memory for this array
+ DECL_ACCESSORS(backing_store, void)
+
+ // [byte_length]: length in bytes
+ DECL_ACCESSORS(byte_length, Object)
+
+ // [flags]
+ DECL_ACCESSORS(flag, Smi)
+
+ inline bool is_external();
+ inline void set_is_external(bool value);
+
+ // [weak_next]: linked list of array buffers.
+ DECL_ACCESSORS(weak_next, Object)
+
+ // [weak_first_array]: weak linked list of views.
+ DECL_ACCESSORS(weak_first_view, Object)
+
+ // Casting.
+ static inline JSArrayBuffer* cast(Object* obj);
+
+ // Neutering. Only neuters the buffer, not associated typed arrays.
+ void Neuter();
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSArrayBuffer)
+ DECLARE_VERIFIER(JSArrayBuffer)
+
+ static const int kBackingStoreOffset = JSObject::kHeaderSize;
+ static const int kByteLengthOffset = kBackingStoreOffset + kPointerSize;
+ static const int kFlagOffset = kByteLengthOffset + kPointerSize;
+ static const int kWeakNextOffset = kFlagOffset + kPointerSize;
+ static const int kWeakFirstViewOffset = kWeakNextOffset + kPointerSize;
+ static const int kSize = kWeakFirstViewOffset + kPointerSize;
+
+ static const int kSizeWithInternalFields =
+ kSize + v8::ArrayBuffer::kInternalFieldCount * kPointerSize;
+
+ private:
+ // Bit position in a flag
+ static const int kIsExternalBit = 0;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBuffer);
+};
+
+
+class JSArrayBufferView: public JSObject {
+ public:
+ // [buffer]: ArrayBuffer that this typed array views.
+ DECL_ACCESSORS(buffer, Object)
+
+ // [byte_length]: offset of typed array in bytes.
+ DECL_ACCESSORS(byte_offset, Object)
+
+ // [byte_length]: length of typed array in bytes.
+ DECL_ACCESSORS(byte_length, Object)
+
+ // [weak_next]: linked list of typed arrays over the same array buffer.
+ DECL_ACCESSORS(weak_next, Object)
+
+ // Casting.
+ static inline JSArrayBufferView* cast(Object* obj);
+
+ DECLARE_VERIFIER(JSArrayBufferView)
+
+ static const int kBufferOffset = JSObject::kHeaderSize;
+ static const int kByteOffsetOffset = kBufferOffset + kPointerSize;
+ static const int kByteLengthOffset = kByteOffsetOffset + kPointerSize;
+ static const int kWeakNextOffset = kByteLengthOffset + kPointerSize;
+ static const int kViewSize = kWeakNextOffset + kPointerSize;
+
+ protected:
+ void NeuterView();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBufferView);
+};
+
+
+class JSTypedArray: public JSArrayBufferView {
+ public:
+ // [length]: length of typed array in elements.
+ DECL_ACCESSORS(length, Object)
+
+ // Neutering. Only neuters this typed array.
+ void Neuter();
+
+ // Casting.
+ static inline JSTypedArray* cast(Object* obj);
+
+ ExternalArrayType type();
+ size_t element_size();
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSTypedArray)
+ DECLARE_VERIFIER(JSTypedArray)
+
+ static const int kLengthOffset = kViewSize + kPointerSize;
+ static const int kSize = kLengthOffset + kPointerSize;
+
+ static const int kSizeWithInternalFields =
+ kSize + v8::ArrayBufferView::kInternalFieldCount * kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSTypedArray);
+};
+
+
+class JSDataView: public JSArrayBufferView {
+ public:
+ // Only neuters this DataView
+ void Neuter();
+
+ // Casting.
+ static inline JSDataView* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSDataView)
+ DECLARE_VERIFIER(JSDataView)
+
+ static const int kSize = kViewSize;
+
+ static const int kSizeWithInternalFields =
+ kSize + v8::ArrayBufferView::kInternalFieldCount * kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSDataView);
+};
+
+
+// Foreign describes objects pointing from JavaScript to C structures.
+// Since they cannot contain references to JS HeapObjects they can be
+// placed in old_data_space.
+class Foreign: public HeapObject {
+ public:
+ // [address]: field containing the address.
+ inline Address foreign_address();
+ inline void set_foreign_address(Address value);
+
+ // Casting.
+ static inline Foreign* cast(Object* obj);
+
+ // Dispatched behavior.
+ inline void ForeignIterateBody(ObjectVisitor* v);
+
+ template<typename StaticVisitor>
+ inline void ForeignIterateBody();
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(Foreign)
+ DECLARE_VERIFIER(Foreign)
+
+ // Layout description.
+
+ static const int kForeignAddressOffset = HeapObject::kHeaderSize;
+ static const int kSize = kForeignAddressOffset + kPointerSize;
+
+ STATIC_CHECK(kForeignAddressOffset == Internals::kForeignAddressOffset);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Foreign);
+};
+
+
+// The JSArray describes JavaScript Arrays
+// Such an array can be in one of two modes:
+// - fast, backing storage is a FixedArray and length <= elements.length();
+// Please note: push and pop can be used to grow and shrink the array.
+// - slow, backing storage is a HashTable with numbers as keys.
+class JSArray: public JSObject {
+ public:
+ // [length]: The length property.
+ DECL_ACCESSORS(length, Object)
+
+ // Overload the length setter to skip write barrier when the length
+ // is set to a smi. This matches the set function on FixedArray.
+ inline void set_length(Smi* length);
+
+ MUST_USE_RESULT MaybeObject* JSArrayUpdateLengthFromIndex(uint32_t index,
+ Object* value);
+
+ // Initialize the array with the given capacity. The function may
+ // fail due to out-of-memory situations, but only if the requested
+ // capacity is non-zero.
+ MUST_USE_RESULT MaybeObject* Initialize(int capacity, int length = 0);
+
+ // Initializes the array to a certain length.
+ inline bool AllowsSetElementsLength();
+ // Can cause GC.
+ MUST_USE_RESULT MaybeObject* SetElementsLength(Object* length);
+
+ // Set the content of the array to the content of storage.
+ MUST_USE_RESULT inline MaybeObject* SetContent(FixedArrayBase* storage);
+
+ // Casting.
+ static inline JSArray* cast(Object* obj);
+
+ // Uses handles. Ensures that the fixed array backing the JSArray has at
+ // least the stated size.
+ inline void EnsureSize(int minimum_size_of_backing_fixed_array);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(JSArray)
+ DECLARE_VERIFIER(JSArray)
+
+ // Number of element slots to pre-allocate for an empty array.
+ static const int kPreallocatedArrayElements = 4;
+
+ // Layout description.
+ static const int kLengthOffset = JSObject::kHeaderSize;
+ static const int kSize = kLengthOffset + kPointerSize;
+
+ private:
+ // Expand the fixed array backing of a fast-case JSArray to at least
+ // the requested size.
+ void Expand(int minimum_size_of_backing_fixed_array);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSArray);
+};
+
+
+Handle<Object> CacheInitialJSArrayMaps(Handle<Context> native_context,
+ Handle<Map> initial_map);
+
+
+// JSRegExpResult is just a JSArray with a specific initial map.
+// This initial map adds in-object properties for "index" and "input"
+// properties, as assigned by RegExp.prototype.exec, which allows
+// faster creation of RegExp exec results.
+// This class just holds constants used when creating the result.
+// After creation the result must be treated as a JSArray in all regards.
+class JSRegExpResult: public JSArray {
+ public:
+ // Offsets of object fields.
+ static const int kIndexOffset = JSArray::kSize;
+ static const int kInputOffset = kIndexOffset + kPointerSize;
+ static const int kSize = kInputOffset + kPointerSize;
+ // Indices of in-object properties.
+ static const int kIndexIndex = 0;
+ static const int kInputIndex = 1;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSRegExpResult);
+};
+
+
+class AccessorInfo: public Struct {
+ public:
+ DECL_ACCESSORS(name, Object)
+ DECL_ACCESSORS(flag, Smi)
+ DECL_ACCESSORS(expected_receiver_type, Object)
+
+ inline bool all_can_read();
+ inline void set_all_can_read(bool value);
+
+ inline bool all_can_write();
+ inline void set_all_can_write(bool value);
+
+ inline bool prohibits_overwriting();
+ inline void set_prohibits_overwriting(bool value);
+
+ inline PropertyAttributes property_attributes();
+ inline void set_property_attributes(PropertyAttributes attributes);
+
+ // Checks whether the given receiver is compatible with this accessor.
+ inline bool IsCompatibleReceiver(Object* receiver);
+
+ static inline AccessorInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_VERIFIER(AccessorInfo)
+
+
+ static const int kNameOffset = HeapObject::kHeaderSize;
+ static const int kFlagOffset = kNameOffset + kPointerSize;
+ static const int kExpectedReceiverTypeOffset = kFlagOffset + kPointerSize;
+ static const int kSize = kExpectedReceiverTypeOffset + kPointerSize;
+
+ private:
+ // Bit positions in flag.
+ static const int kAllCanReadBit = 0;
+ static const int kAllCanWriteBit = 1;
+ static const int kProhibitsOverwritingBit = 2;
+ class AttributesField: public BitField<PropertyAttributes, 3, 3> {};
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AccessorInfo);
+};
+
+
+enum AccessorDescriptorType {
+ kDescriptorBitmaskCompare,
+ kDescriptorPointerCompare,
+ kDescriptorPrimitiveValue,
+ kDescriptorObjectDereference,
+ kDescriptorPointerDereference,
+ kDescriptorPointerShift,
+ kDescriptorReturnObject
+};
+
+
+struct BitmaskCompareDescriptor {
+ uint32_t bitmask;
+ uint32_t compare_value;
+ uint8_t size; // Must be in {1,2,4}.
+};
+
+
+struct PointerCompareDescriptor {
+ void* compare_value;
+};
+
+
+struct PrimitiveValueDescriptor {
+ v8::DeclaredAccessorDescriptorDataType data_type;
+ uint8_t bool_offset; // Must be in [0,7], used for kDescriptorBoolType.
+};
+
+
+struct ObjectDerefenceDescriptor {
+ uint8_t internal_field;
+};
+
+
+struct PointerShiftDescriptor {
+ int16_t byte_offset;
+};
+
+
+struct DeclaredAccessorDescriptorData {
+ AccessorDescriptorType type;
+ union {
+ struct BitmaskCompareDescriptor bitmask_compare_descriptor;
+ struct PointerCompareDescriptor pointer_compare_descriptor;
+ struct PrimitiveValueDescriptor primitive_value_descriptor;
+ struct ObjectDerefenceDescriptor object_dereference_descriptor;
+ struct PointerShiftDescriptor pointer_shift_descriptor;
+ };
+};
+
+
+class DeclaredAccessorDescriptor;
+
+
+class DeclaredAccessorDescriptorIterator {
+ public:
+ explicit DeclaredAccessorDescriptorIterator(
+ DeclaredAccessorDescriptor* descriptor);
+ const DeclaredAccessorDescriptorData* Next();
+ bool Complete() const { return length_ == offset_; }
+ private:
+ uint8_t* array_;
+ const int length_;
+ int offset_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DeclaredAccessorDescriptorIterator);
+};
+
+
+class DeclaredAccessorDescriptor: public Struct {
+ public:
+ DECL_ACCESSORS(serialized_data, ByteArray)
+
+ static inline DeclaredAccessorDescriptor* cast(Object* obj);
+
+ static Handle<DeclaredAccessorDescriptor> Create(
+ Isolate* isolate,
+ const DeclaredAccessorDescriptorData& data,
+ Handle<DeclaredAccessorDescriptor> previous);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(DeclaredAccessorDescriptor)
+ DECLARE_VERIFIER(DeclaredAccessorDescriptor)
+
+ static const int kSerializedDataOffset = HeapObject::kHeaderSize;
+ static const int kSize = kSerializedDataOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DeclaredAccessorDescriptor);
+};
+
+
+class DeclaredAccessorInfo: public AccessorInfo {
+ public:
+ DECL_ACCESSORS(descriptor, DeclaredAccessorDescriptor)
+
+ static inline DeclaredAccessorInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(DeclaredAccessorInfo)
+ DECLARE_VERIFIER(DeclaredAccessorInfo)
+
+ static const int kDescriptorOffset = AccessorInfo::kSize;
+ static const int kSize = kDescriptorOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DeclaredAccessorInfo);
+};
+
+
+// An accessor must have a getter, but can have no setter.
+//
+// When setting a property, V8 searches accessors in prototypes.
+// If an accessor was found and it does not have a setter,
+// the request is ignored.
+//
+// If the accessor in the prototype has the READ_ONLY property attribute, then
+// a new value is added to the local object when the property is set.
+// This shadows the accessor in the prototype.
+class ExecutableAccessorInfo: public AccessorInfo {
+ public:
+ DECL_ACCESSORS(getter, Object)
+ DECL_ACCESSORS(setter, Object)
+ DECL_ACCESSORS(data, Object)
+
+ static inline ExecutableAccessorInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ExecutableAccessorInfo)
+ DECLARE_VERIFIER(ExecutableAccessorInfo)
+
+ static const int kGetterOffset = AccessorInfo::kSize;
+ static const int kSetterOffset = kGetterOffset + kPointerSize;
+ static const int kDataOffset = kSetterOffset + kPointerSize;
+ static const int kSize = kDataOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExecutableAccessorInfo);
+};
+
+
+// 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);
+
+ static Handle<AccessorPair> Copy(Handle<AccessorPair> pair);
+
+ Object* get(AccessorComponent component) {
+ return component == ACCESSOR_GETTER ? getter() : setter();
+ }
+
+ void set(AccessorComponent component, Object* value) {
+ if (component == ACCESSOR_GETTER) {
+ set_getter(value);
+ } else {
+ set_setter(value);
+ }
+ }
+
+ // Note: Returns undefined instead in case of a hole.
+ Object* GetComponent(AccessorComponent component);
+
+ // Set both components, skipping arguments which are a JavaScript null.
+ void SetComponents(Object* getter, Object* setter) {
+ if (!getter->IsNull()) set_getter(getter);
+ if (!setter->IsNull()) set_setter(setter);
+ }
+
+ bool ContainsAccessor() {
+ return IsJSAccessor(getter()) || IsJSAccessor(setter());
+ }
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(AccessorPair)
+ DECLARE_VERIFIER(AccessorPair)
+
+ static const int kGetterOffset = HeapObject::kHeaderSize;
+ static const int kSetterOffset = kGetterOffset + kPointerSize;
+ static const int kSize = kSetterOffset + kPointerSize;
+
+ private:
+ // Strangely enough, in addition to functions and harmony proxies, the spec
+ // requires us to consider undefined as a kind of accessor, too:
+ // var obj = {};
+ // Object.defineProperty(obj, "foo", {get: undefined});
+ // assertTrue("foo" in obj);
+ bool IsJSAccessor(Object* obj) {
+ return obj->IsSpecFunction() || obj->IsUndefined();
+ }
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AccessorPair);
+};
+
+
+class AccessCheckInfo: public Struct {
+ public:
+ DECL_ACCESSORS(named_callback, Object)
+ DECL_ACCESSORS(indexed_callback, Object)
+ DECL_ACCESSORS(data, Object)
+
+ static inline AccessCheckInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(AccessCheckInfo)
+ DECLARE_VERIFIER(AccessCheckInfo)
+
+ static const int kNamedCallbackOffset = HeapObject::kHeaderSize;
+ static const int kIndexedCallbackOffset = kNamedCallbackOffset + kPointerSize;
+ static const int kDataOffset = kIndexedCallbackOffset + kPointerSize;
+ static const int kSize = kDataOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AccessCheckInfo);
+};
+
+
+class InterceptorInfo: public Struct {
+ public:
+ DECL_ACCESSORS(getter, Object)
+ DECL_ACCESSORS(setter, Object)
+ DECL_ACCESSORS(query, Object)
+ DECL_ACCESSORS(deleter, Object)
+ DECL_ACCESSORS(enumerator, Object)
+ DECL_ACCESSORS(data, Object)
+
+ static inline InterceptorInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(InterceptorInfo)
+ DECLARE_VERIFIER(InterceptorInfo)
+
+ static const int kGetterOffset = HeapObject::kHeaderSize;
+ static const int kSetterOffset = kGetterOffset + kPointerSize;
+ static const int kQueryOffset = kSetterOffset + kPointerSize;
+ static const int kDeleterOffset = kQueryOffset + kPointerSize;
+ static const int kEnumeratorOffset = kDeleterOffset + kPointerSize;
+ static const int kDataOffset = kEnumeratorOffset + kPointerSize;
+ static const int kSize = kDataOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(InterceptorInfo);
+};
+
+
+class CallHandlerInfo: public Struct {
+ public:
+ DECL_ACCESSORS(callback, Object)
+ DECL_ACCESSORS(data, Object)
+
+ static inline CallHandlerInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(CallHandlerInfo)
+ DECLARE_VERIFIER(CallHandlerInfo)
+
+ static const int kCallbackOffset = HeapObject::kHeaderSize;
+ static const int kDataOffset = kCallbackOffset + kPointerSize;
+ static const int kSize = kDataOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CallHandlerInfo);
+};
+
+
+class TemplateInfo: public Struct {
+ public:
+ DECL_ACCESSORS(tag, Object)
+ DECL_ACCESSORS(property_list, Object)
+
+ DECLARE_VERIFIER(TemplateInfo)
+
+ static const int kTagOffset = HeapObject::kHeaderSize;
+ static const int kPropertyListOffset = kTagOffset + kPointerSize;
+ static const int kHeaderSize = kPropertyListOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TemplateInfo);
+};
+
+
+class FunctionTemplateInfo: public TemplateInfo {
+ public:
+ DECL_ACCESSORS(serial_number, Object)
+ DECL_ACCESSORS(call_code, Object)
+ DECL_ACCESSORS(property_accessors, Object)
+ DECL_ACCESSORS(prototype_template, Object)
+ DECL_ACCESSORS(parent_template, Object)
+ DECL_ACCESSORS(named_property_handler, Object)
+ DECL_ACCESSORS(indexed_property_handler, Object)
+ DECL_ACCESSORS(instance_template, Object)
+ DECL_ACCESSORS(class_name, Object)
+ DECL_ACCESSORS(signature, Object)
+ DECL_ACCESSORS(instance_call_handler, Object)
+ DECL_ACCESSORS(access_check_info, Object)
+ DECL_ACCESSORS(flag, Smi)
+
+ inline int length();
+ inline void set_length(int value);
+
+ // Following properties use flag bits.
+ DECL_BOOLEAN_ACCESSORS(hidden_prototype)
+ DECL_BOOLEAN_ACCESSORS(undetectable)
+ // If the bit is set, object instances created by this function
+ // requires access check.
+ DECL_BOOLEAN_ACCESSORS(needs_access_check)
+ DECL_BOOLEAN_ACCESSORS(read_only_prototype)
+
+ static inline FunctionTemplateInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(FunctionTemplateInfo)
+ DECLARE_VERIFIER(FunctionTemplateInfo)
+
+ static const int kSerialNumberOffset = TemplateInfo::kHeaderSize;
+ static const int kCallCodeOffset = kSerialNumberOffset + kPointerSize;
+ static const int kPropertyAccessorsOffset = kCallCodeOffset + kPointerSize;
+ static const int kPrototypeTemplateOffset =
+ kPropertyAccessorsOffset + kPointerSize;
+ static const int kParentTemplateOffset =
+ kPrototypeTemplateOffset + kPointerSize;
+ static const int kNamedPropertyHandlerOffset =
+ kParentTemplateOffset + kPointerSize;
+ static const int kIndexedPropertyHandlerOffset =
+ kNamedPropertyHandlerOffset + kPointerSize;
+ static const int kInstanceTemplateOffset =
+ kIndexedPropertyHandlerOffset + kPointerSize;
+ static const int kClassNameOffset = kInstanceTemplateOffset + kPointerSize;
+ static const int kSignatureOffset = kClassNameOffset + kPointerSize;
+ static const int kInstanceCallHandlerOffset = kSignatureOffset + kPointerSize;
+ static const int kAccessCheckInfoOffset =
+ kInstanceCallHandlerOffset + kPointerSize;
+ static const int kFlagOffset = kAccessCheckInfoOffset + kPointerSize;
+ static const int kLengthOffset = kFlagOffset + kPointerSize;
+ static const int kSize = kLengthOffset + kPointerSize;
+
+ private:
+ // Bit position in the flag, from least significant bit position.
+ static const int kHiddenPrototypeBit = 0;
+ static const int kUndetectableBit = 1;
+ static const int kNeedsAccessCheckBit = 2;
+ static const int kReadOnlyPrototypeBit = 3;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FunctionTemplateInfo);
+};
+
+
+class ObjectTemplateInfo: public TemplateInfo {
+ public:
+ DECL_ACCESSORS(constructor, Object)
+ DECL_ACCESSORS(internal_field_count, Object)
+
+ static inline ObjectTemplateInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(ObjectTemplateInfo)
+ DECLARE_VERIFIER(ObjectTemplateInfo)
+
+ static const int kConstructorOffset = TemplateInfo::kHeaderSize;
+ static const int kInternalFieldCountOffset =
+ kConstructorOffset + kPointerSize;
+ static const int kSize = kInternalFieldCountOffset + kPointerSize;
+};
+
+
+class SignatureInfo: public Struct {
+ public:
+ DECL_ACCESSORS(receiver, Object)
+ DECL_ACCESSORS(args, Object)
+
+ static inline SignatureInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(SignatureInfo)
+ DECLARE_VERIFIER(SignatureInfo)
+
+ static const int kReceiverOffset = Struct::kHeaderSize;
+ static const int kArgsOffset = kReceiverOffset + kPointerSize;
+ static const int kSize = kArgsOffset + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SignatureInfo);
+};
+
+
+class TypeSwitchInfo: public Struct {
+ public:
+ DECL_ACCESSORS(types, Object)
+
+ static inline TypeSwitchInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(TypeSwitchInfo)
+ DECLARE_VERIFIER(TypeSwitchInfo)
+
+ static const int kTypesOffset = Struct::kHeaderSize;
+ static const int kSize = kTypesOffset + kPointerSize;
+};
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+// The DebugInfo class holds additional information for a function being
+// debugged.
+class DebugInfo: public Struct {
+ public:
+ // The shared function info for the source being debugged.
+ DECL_ACCESSORS(shared, SharedFunctionInfo)
+ // Code object for the original code.
+ DECL_ACCESSORS(original_code, Code)
+ // Code object for the patched code. This code object is the code object
+ // currently active for the function.
+ DECL_ACCESSORS(code, Code)
+ // Fixed array holding status information for each active break point.
+ DECL_ACCESSORS(break_points, FixedArray)
+
+ // Check if there is a break point at a code position.
+ bool HasBreakPoint(int code_position);
+ // Get the break point info object for a code position.
+ Object* GetBreakPointInfo(int code_position);
+ // Clear a break point.
+ static void ClearBreakPoint(Handle<DebugInfo> debug_info,
+ int code_position,
+ Handle<Object> break_point_object);
+ // Set a break point.
+ static void SetBreakPoint(Handle<DebugInfo> debug_info, int code_position,
+ int source_position, int statement_position,
+ Handle<Object> break_point_object);
+ // Get the break point objects for a code position.
+ Object* GetBreakPointObjects(int code_position);
+ // Find the break point info holding this break point object.
+ static Object* FindBreakPointInfo(Handle<DebugInfo> debug_info,
+ Handle<Object> break_point_object);
+ // Get the number of break points for this function.
+ int GetBreakPointCount();
+
+ static inline DebugInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(DebugInfo)
+ DECLARE_VERIFIER(DebugInfo)
+
+ static const int kSharedFunctionInfoIndex = Struct::kHeaderSize;
+ static const int kOriginalCodeIndex = kSharedFunctionInfoIndex + kPointerSize;
+ static const int kPatchedCodeIndex = kOriginalCodeIndex + kPointerSize;
+ static const int kActiveBreakPointsCountIndex =
+ kPatchedCodeIndex + kPointerSize;
+ static const int kBreakPointsStateIndex =
+ kActiveBreakPointsCountIndex + kPointerSize;
+ static const int kSize = kBreakPointsStateIndex + kPointerSize;
+
+ private:
+ static const int kNoBreakPointInfo = -1;
+
+ // Lookup the index in the break_points array for a code position.
+ int GetBreakPointInfoIndex(int code_position);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DebugInfo);
+};
+
+
+// The BreakPointInfo class holds information for break points set in a
+// function. The DebugInfo object holds a BreakPointInfo object for each code
+// position with one or more break points.
+class BreakPointInfo: public Struct {
+ public:
+ // The position in the code for the break point.
+ DECL_ACCESSORS(code_position, Smi)
+ // The position in the source for the break position.
+ DECL_ACCESSORS(source_position, Smi)
+ // The position in the source for the last statement before this break
+ // position.
+ DECL_ACCESSORS(statement_position, Smi)
+ // List of related JavaScript break points.
+ DECL_ACCESSORS(break_point_objects, Object)
+
+ // Removes a break point.
+ static void ClearBreakPoint(Handle<BreakPointInfo> info,
+ Handle<Object> break_point_object);
+ // Set a break point.
+ static void SetBreakPoint(Handle<BreakPointInfo> info,
+ Handle<Object> break_point_object);
+ // Check if break point info has this break point object.
+ static bool HasBreakPointObject(Handle<BreakPointInfo> info,
+ Handle<Object> break_point_object);
+ // Get the number of break points for this code position.
+ int GetBreakPointCount();
+
+ static inline BreakPointInfo* cast(Object* obj);
+
+ // Dispatched behavior.
+ DECLARE_PRINTER(BreakPointInfo)
+ DECLARE_VERIFIER(BreakPointInfo)
+
+ static const int kCodePositionIndex = Struct::kHeaderSize;
+ static const int kSourcePositionIndex = kCodePositionIndex + kPointerSize;
+ static const int kStatementPositionIndex =
+ kSourcePositionIndex + kPointerSize;
+ static const int kBreakPointObjectsIndex =
+ kStatementPositionIndex + kPointerSize;
+ static const int kSize = kBreakPointObjectsIndex + kPointerSize;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BreakPointInfo);
+};
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+#undef DECL_BOOLEAN_ACCESSORS
+#undef DECL_ACCESSORS
+#undef DECLARE_VERIFIER
+
+#define VISITOR_SYNCHRONIZATION_TAGS_LIST(V) \
+ V(kStringTable, "string_table", "(Internalized strings)") \
+ V(kExternalStringsTable, "external_strings_table", "(External strings)") \
+ V(kStrongRootList, "strong_root_list", "(Strong roots)") \
+ V(kInternalizedString, "internalized_string", "(Internal string)") \
+ 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(kEternalHandles, "eternalhandles", "(Eternal 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.
+class ObjectVisitor BASE_EMBEDDED {
+ public:
+ virtual ~ObjectVisitor() {}
+
+ // Visits a contiguous arrays of pointers in the half-open range
+ // [start, end). Any or all of the values may be modified on return.
+ virtual void VisitPointers(Object** start, Object** end) = 0;
+
+ // To allow lazy clearing of inline caches the visitor has
+ // a rich interface for iterating over Code objects..
+
+ // Visits a code target in the instruction stream.
+ virtual void VisitCodeTarget(RelocInfo* rinfo);
+
+ // Visits a code entry in a JS function.
+ virtual void VisitCodeEntry(Address entry_address);
+
+ // Visits a global property cell reference in the instruction stream.
+ virtual void VisitCell(RelocInfo* rinfo);
+
+ // Visits a runtime entry in the instruction stream.
+ virtual void VisitRuntimeEntry(RelocInfo* rinfo) {}
+
+ // Visits the resource of an ASCII or two-byte string.
+ virtual void VisitExternalAsciiString(
+ v8::String::ExternalAsciiStringResource** resource) {}
+ virtual void VisitExternalTwoByteString(
+ v8::String::ExternalStringResource** resource) {}
+
+ // Visits a debug call target in the instruction stream.
+ virtual void VisitDebugTarget(RelocInfo* rinfo);
+
+ // Visits the byte sequence in a function's prologue that contains information
+ // about the code's age.
+ virtual void VisitCodeAgeSequence(RelocInfo* rinfo);
+
+ // 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);
+ }
+
+ // Visits a handle that has an embedder-assigned class ID.
+ virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {}
+
+ // Intended for serialization/deserialization checking: insert, or
+ // check for the presence of, a tag at this position in the stream.
+ // Also used for marking up GC roots in heap snapshots.
+ virtual void Synchronize(VisitorSynchronization::SyncTag tag) {}
+};
+
+
+class StructBodyDescriptor : public
+ FlexibleBodyDescriptor<HeapObject::kHeaderSize> {
+ public:
+ static inline int SizeOf(Map* map, HeapObject* object) {
+ return map->instance_size();
+ }
+};
+
+
+// BooleanBit is a helper class for setting and getting a bit in an
+// integer or Smi.
+class BooleanBit : public AllStatic {
+ public:
+ static inline bool get(Smi* smi, int bit_position) {
+ return get(smi->value(), bit_position);
+ }
+
+ static inline bool get(int value, int bit_position) {
+ return (value & (1 << bit_position)) != 0;
+ }
+
+ static inline Smi* set(Smi* smi, int bit_position, bool v) {
+ return Smi::FromInt(set(smi->value(), bit_position, v));
+ }
+
+ static inline int set(int value, int bit_position, bool v) {
+ if (v) {
+ value |= (1 << bit_position);
+ } else {
+ value &= ~(1 << bit_position);
+ }
+ return value;
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_OBJECTS_H_
diff --git a/chromium/v8/src/once.cc b/chromium/v8/src/once.cc
new file mode 100644
index 00000000000..37fe369fb67
--- /dev/null
+++ b/chromium/v8/src/once.cc
@@ -0,0 +1,77 @@
+// Copyright 2012 the V8 project authors. 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 "once.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sched.h>
+#endif
+
+#include "atomicops.h"
+#include "checks.h"
+
+namespace v8 {
+namespace internal {
+
+void CallOnceImpl(OnceType* once, PointerArgFunction init_func, void* arg) {
+ AtomicWord state = Acquire_Load(once);
+ // Fast path. The provided function was already executed.
+ if (state == ONCE_STATE_DONE) {
+ return;
+ }
+
+ // The function execution did not complete yet. The once object can be in one
+ // of the two following states:
+ // - UNINITIALIZED: We are the first thread calling this function.
+ // - EXECUTING_FUNCTION: Another thread is already executing the function.
+ //
+ // First, try to change the state from UNINITIALIZED to EXECUTING_FUNCTION
+ // atomically.
+ state = Acquire_CompareAndSwap(
+ once, ONCE_STATE_UNINITIALIZED, ONCE_STATE_EXECUTING_FUNCTION);
+ if (state == ONCE_STATE_UNINITIALIZED) {
+ // We are the first thread to call this function, so we have to call the
+ // function.
+ init_func(arg);
+ Release_Store(once, ONCE_STATE_DONE);
+ } else {
+ // Another thread has already started executing the function. We need to
+ // wait until it completes the initialization.
+ while (state == ONCE_STATE_EXECUTING_FUNCTION) {
+#ifdef _WIN32
+ ::Sleep(0);
+#else
+ sched_yield();
+#endif
+ state = Acquire_Load(once);
+ }
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/once.h b/chromium/v8/src/once.h
new file mode 100644
index 00000000000..a44b8fafbf5
--- /dev/null
+++ b/chromium/v8/src/once.h
@@ -0,0 +1,123 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// emulates google3/base/once.h
+//
+// This header is intended to be included only by v8's internal code. Users
+// should not use this directly.
+//
+// This is basically a portable version of pthread_once().
+//
+// This header declares:
+// * A type called OnceType.
+// * A macro V8_DECLARE_ONCE() which declares a (global) variable of type
+// OnceType.
+// * A function CallOnce(OnceType* once, void (*init_func)()).
+// This function, when invoked multiple times given the same OnceType object,
+// will invoke init_func on the first call only, and will make sure none of
+// the calls return before that first call to init_func has finished.
+//
+// Additionally, the following features are supported:
+// * A macro V8_ONCE_INIT which is expanded into the expression used to
+// initialize a OnceType. This is only useful when clients embed a OnceType
+// into a structure of their own and want to initialize it statically.
+// * The user can provide a parameter which CallOnce() forwards to the
+// user-provided function when it is called. Usage example:
+// CallOnce(&my_once, &MyFunctionExpectingIntArgument, 10);
+// * This implementation guarantees that OnceType is a POD (i.e. no static
+// initializer generated).
+//
+// This implements a way to perform lazy initialization. It's more efficient
+// than using mutexes as no lock is needed if initialization has already
+// happened.
+//
+// Example usage:
+// void Init();
+// V8_DECLARE_ONCE(once_init);
+//
+// // Calls Init() exactly once.
+// void InitOnce() {
+// CallOnce(&once_init, &Init);
+// }
+//
+// Note that if CallOnce() is called before main() has begun, it must
+// only be called by the thread that will eventually call main() -- that is,
+// the thread that performs dynamic initialization. In general this is a safe
+// assumption since people don't usually construct threads before main() starts,
+// but it is technically not guaranteed. Unfortunately, Win32 provides no way
+// whatsoever to statically-initialize its synchronization primitives, so our
+// only choice is to assume that dynamic initialization is single-threaded.
+
+#ifndef V8_ONCE_H_
+#define V8_ONCE_H_
+
+#include "atomicops.h"
+
+namespace v8 {
+namespace internal {
+
+typedef AtomicWord OnceType;
+
+#define V8_ONCE_INIT 0
+
+#define V8_DECLARE_ONCE(NAME) ::v8::internal::OnceType NAME
+
+enum {
+ ONCE_STATE_UNINITIALIZED = 0,
+ ONCE_STATE_EXECUTING_FUNCTION = 1,
+ ONCE_STATE_DONE = 2
+};
+
+typedef void (*NoArgFunction)();
+typedef void (*PointerArgFunction)(void* arg);
+
+template <typename T>
+struct OneArgFunction {
+ typedef void (*type)(T);
+};
+
+void CallOnceImpl(OnceType* once, PointerArgFunction init_func, void* arg);
+
+inline void CallOnce(OnceType* once, NoArgFunction init_func) {
+ if (Acquire_Load(once) != ONCE_STATE_DONE) {
+ CallOnceImpl(once, reinterpret_cast<PointerArgFunction>(init_func), NULL);
+ }
+}
+
+
+template <typename Arg>
+inline void CallOnce(OnceType* once,
+ typename OneArgFunction<Arg*>::type init_func, Arg* arg) {
+ if (Acquire_Load(once) != ONCE_STATE_DONE) {
+ CallOnceImpl(once, reinterpret_cast<PointerArgFunction>(init_func),
+ static_cast<void*>(arg));
+ }
+}
+
+} } // namespace v8::internal
+
+#endif // V8_ONCE_H_
diff --git a/chromium/v8/src/optimizing-compiler-thread.cc b/chromium/v8/src/optimizing-compiler-thread.cc
new file mode 100644
index 00000000000..337ddd4b2d2
--- /dev/null
+++ b/chromium/v8/src/optimizing-compiler-thread.cc
@@ -0,0 +1,222 @@
+// Copyright 2012 the V8 project authors. 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 "optimizing-compiler-thread.h"
+
+#include "v8.h"
+
+#include "hydrogen.h"
+#include "isolate.h"
+#include "v8threads.h"
+
+namespace v8 {
+namespace internal {
+
+
+void OptimizingCompilerThread::Run() {
+#ifdef DEBUG
+ { ScopedLock lock(thread_id_mutex_);
+ thread_id_ = ThreadId::Current().ToInteger();
+ }
+#endif
+ Isolate::SetIsolateThreadLocals(isolate_, NULL);
+ DisallowHeapAllocation no_allocation;
+ DisallowHandleAllocation no_handles;
+ DisallowHandleDereference no_deref;
+
+ int64_t epoch = 0;
+ if (FLAG_trace_parallel_recompilation) epoch = OS::Ticks();
+
+ while (true) {
+ input_queue_semaphore_->Wait();
+ Logger::TimerEventScope timer(
+ isolate_, Logger::TimerEventScope::v8_recompile_parallel);
+
+ if (FLAG_parallel_recompilation_delay != 0) {
+ OS::Sleep(FLAG_parallel_recompilation_delay);
+ }
+
+ switch (static_cast<StopFlag>(Acquire_Load(&stop_thread_))) {
+ case CONTINUE:
+ break;
+ case STOP:
+ if (FLAG_trace_parallel_recompilation) {
+ time_spent_total_ = OS::Ticks() - epoch;
+ }
+ stop_semaphore_->Signal();
+ return;
+ case FLUSH:
+ // The main thread is blocked, waiting for the stop semaphore.
+ { AllowHandleDereference allow_handle_dereference;
+ FlushInputQueue(true);
+ }
+ Release_Store(&queue_length_, static_cast<AtomicWord>(0));
+ Release_Store(&stop_thread_, static_cast<AtomicWord>(CONTINUE));
+ stop_semaphore_->Signal();
+ // Return to start of consumer loop.
+ continue;
+ }
+
+ int64_t compiling_start = 0;
+ if (FLAG_trace_parallel_recompilation) compiling_start = OS::Ticks();
+
+ CompileNext();
+
+ if (FLAG_trace_parallel_recompilation) {
+ time_spent_compiling_ += OS::Ticks() - compiling_start;
+ }
+ }
+}
+
+
+void OptimizingCompilerThread::CompileNext() {
+ OptimizingCompiler* optimizing_compiler = NULL;
+ bool result = input_queue_.Dequeue(&optimizing_compiler);
+ USE(result);
+ ASSERT(result);
+ Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(-1));
+
+ // The function may have already been optimized by OSR. Simply continue.
+ OptimizingCompiler::Status status = optimizing_compiler->OptimizeGraph();
+ USE(status); // Prevent an unused-variable error in release mode.
+ ASSERT(status != OptimizingCompiler::FAILED);
+
+ // The function may have already been optimized by OSR. Simply continue.
+ // Use a mutex to make sure that functions marked for install
+ // are always also queued.
+ ScopedLock mark_and_queue(install_mutex_);
+ { Heap::RelocationLock relocation_lock(isolate_->heap());
+ AllowHandleDereference ahd;
+ optimizing_compiler->info()->closure()->MarkForInstallingRecompiledCode();
+ }
+ output_queue_.Enqueue(optimizing_compiler);
+}
+
+
+void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) {
+ OptimizingCompiler* optimizing_compiler;
+ // The optimizing compiler is allocated in the CompilationInfo's zone.
+ while (input_queue_.Dequeue(&optimizing_compiler)) {
+ // This should not block, since we have one signal on the input queue
+ // semaphore corresponding to each element in the input queue.
+ input_queue_semaphore_->Wait();
+ CompilationInfo* info = optimizing_compiler->info();
+ if (restore_function_code) {
+ Handle<JSFunction> function = info->closure();
+ function->ReplaceCode(function->shared()->code());
+ }
+ delete info;
+ }
+}
+
+
+void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) {
+ OptimizingCompiler* optimizing_compiler;
+ // The optimizing compiler is allocated in the CompilationInfo's zone.
+ while (output_queue_.Dequeue(&optimizing_compiler)) {
+ CompilationInfo* info = optimizing_compiler->info();
+ if (restore_function_code) {
+ Handle<JSFunction> function = info->closure();
+ function->ReplaceCode(function->shared()->code());
+ }
+ delete info;
+ }
+}
+
+
+void OptimizingCompilerThread::Flush() {
+ ASSERT(!IsOptimizerThread());
+ Release_Store(&stop_thread_, static_cast<AtomicWord>(FLUSH));
+ input_queue_semaphore_->Signal();
+ stop_semaphore_->Wait();
+ FlushOutputQueue(true);
+}
+
+
+void OptimizingCompilerThread::Stop() {
+ ASSERT(!IsOptimizerThread());
+ Release_Store(&stop_thread_, static_cast<AtomicWord>(STOP));
+ input_queue_semaphore_->Signal();
+ stop_semaphore_->Wait();
+
+ if (FLAG_parallel_recompilation_delay != 0) {
+ // Barrier when loading queue length is not necessary since the write
+ // happens in CompileNext on the same thread.
+ // This is used only for testing.
+ while (NoBarrier_Load(&queue_length_) > 0) CompileNext();
+ InstallOptimizedFunctions();
+ } else {
+ FlushInputQueue(false);
+ FlushOutputQueue(false);
+ }
+
+ if (FLAG_trace_parallel_recompilation) {
+ double compile_time = static_cast<double>(time_spent_compiling_);
+ double total_time = static_cast<double>(time_spent_total_);
+ double percentage = (compile_time * 100) / total_time;
+ PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage);
+ }
+
+ Join();
+}
+
+
+void OptimizingCompilerThread::InstallOptimizedFunctions() {
+ ASSERT(!IsOptimizerThread());
+ HandleScope handle_scope(isolate_);
+ OptimizingCompiler* compiler;
+ while (true) {
+ { // Memory barrier to ensure marked functions are queued.
+ ScopedLock marked_and_queued(install_mutex_);
+ if (!output_queue_.Dequeue(&compiler)) return;
+ }
+ Compiler::InstallOptimizedCode(compiler);
+ }
+}
+
+
+void OptimizingCompilerThread::QueueForOptimization(
+ OptimizingCompiler* optimizing_compiler) {
+ ASSERT(IsQueueAvailable());
+ ASSERT(!IsOptimizerThread());
+ Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(1));
+ optimizing_compiler->info()->closure()->MarkInRecompileQueue();
+ input_queue_.Enqueue(optimizing_compiler);
+ input_queue_semaphore_->Signal();
+}
+
+
+#ifdef DEBUG
+bool OptimizingCompilerThread::IsOptimizerThread() {
+ if (!FLAG_parallel_recompilation) return false;
+ ScopedLock lock(thread_id_mutex_);
+ return ThreadId::Current().ToInteger() == thread_id_;
+}
+#endif
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/optimizing-compiler-thread.h b/chromium/v8/src/optimizing-compiler-thread.h
new file mode 100644
index 00000000000..cbd4d0e4872
--- /dev/null
+++ b/chromium/v8/src/optimizing-compiler-thread.h
@@ -0,0 +1,121 @@
+// Copyright 2012 the V8 project authors. 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_OPTIMIZING_COMPILER_THREAD_H_
+#define V8_OPTIMIZING_COMPILER_THREAD_H_
+
+#include "atomicops.h"
+#include "flags.h"
+#include "platform.h"
+#include "unbound-queue-inl.h"
+
+namespace v8 {
+namespace internal {
+
+class HOptimizedGraphBuilder;
+class OptimizingCompiler;
+class SharedFunctionInfo;
+
+class OptimizingCompilerThread : public Thread {
+ public:
+ explicit OptimizingCompilerThread(Isolate *isolate) :
+ Thread("OptimizingCompilerThread"),
+#ifdef DEBUG
+ thread_id_(0),
+ thread_id_mutex_(OS::CreateMutex()),
+#endif
+ isolate_(isolate),
+ stop_semaphore_(OS::CreateSemaphore(0)),
+ input_queue_semaphore_(OS::CreateSemaphore(0)),
+ install_mutex_(OS::CreateMutex()),
+ time_spent_compiling_(0),
+ time_spent_total_(0) {
+ NoBarrier_Store(&stop_thread_, static_cast<AtomicWord>(CONTINUE));
+ NoBarrier_Store(&queue_length_, static_cast<AtomicWord>(0));
+ }
+
+ void Run();
+ void Stop();
+ void Flush();
+ void QueueForOptimization(OptimizingCompiler* optimizing_compiler);
+ void InstallOptimizedFunctions();
+
+ inline bool IsQueueAvailable() {
+ // We don't need a barrier since we have a data dependency right
+ // after.
+ Atomic32 current_length = NoBarrier_Load(&queue_length_);
+
+ // This can be queried only from the execution thread.
+ ASSERT(!IsOptimizerThread());
+ // Since only the execution thread increments queue_length_ and
+ // only one thread can run inside an Isolate at one time, a direct
+ // doesn't introduce a race -- queue_length_ may decreased in
+ // meantime, but not increased.
+ return (current_length < FLAG_parallel_recompilation_queue_length);
+ }
+
+#ifdef DEBUG
+ bool IsOptimizerThread();
+#endif
+
+ ~OptimizingCompilerThread() {
+ delete install_mutex_;
+ delete input_queue_semaphore_;
+ delete stop_semaphore_;
+#ifdef DEBUG
+ delete thread_id_mutex_;
+#endif
+ }
+
+ private:
+ enum StopFlag { CONTINUE, STOP, FLUSH };
+
+ void FlushInputQueue(bool restore_function_code);
+ void FlushOutputQueue(bool restore_function_code);
+
+ void CompileNext();
+
+#ifdef DEBUG
+ int thread_id_;
+ Mutex* thread_id_mutex_;
+#endif
+
+ Isolate* isolate_;
+ Semaphore* stop_semaphore_;
+ Semaphore* input_queue_semaphore_;
+ UnboundQueue<OptimizingCompiler*> input_queue_;
+ UnboundQueue<OptimizingCompiler*> output_queue_;
+ Mutex* install_mutex_;
+ volatile AtomicWord stop_thread_;
+ volatile Atomic32 queue_length_;
+ int64_t time_spent_compiling_;
+ int64_t time_spent_total_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_OPTIMIZING_COMPILER_THREAD_H_
diff --git a/chromium/v8/src/parser.cc b/chromium/v8/src/parser.cc
new file mode 100644
index 00000000000..4947790395f
--- /dev/null
+++ b/chromium/v8/src/parser.cc
@@ -0,0 +1,5942 @@
+// Copyright 2012 the V8 project authors. 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 "api.h"
+#include "ast.h"
+#include "bootstrapper.h"
+#include "char-predicates-inl.h"
+#include "codegen.h"
+#include "compiler.h"
+#include "func-name-inferrer.h"
+#include "messages.h"
+#include "parser.h"
+#include "platform.h"
+#include "preparser.h"
+#include "runtime.h"
+#include "scanner-character-streams.h"
+#include "scopeinfo.h"
+#include "string-stream.h"
+
+namespace v8 {
+namespace internal {
+
+// PositionStack is used for on-stack allocation of token positions for
+// new expressions. Please look at ParseNewExpression.
+
+class PositionStack {
+ public:
+ explicit PositionStack(bool* ok) : top_(NULL), ok_(ok) {}
+ ~PositionStack() {
+ ASSERT(!*ok_ || is_empty());
+ USE(ok_);
+ }
+
+ class Element {
+ public:
+ Element(PositionStack* stack, int value) {
+ previous_ = stack->top();
+ value_ = value;
+ stack->set_top(this);
+ }
+
+ private:
+ Element* previous() { return previous_; }
+ int value() { return value_; }
+ friend class PositionStack;
+ Element* previous_;
+ int value_;
+ };
+
+ bool is_empty() { return top_ == NULL; }
+ int pop() {
+ ASSERT(!is_empty());
+ int result = top_->value();
+ top_ = top_->previous();
+ return result;
+ }
+
+ private:
+ Element* top() { return top_; }
+ void set_top(Element* value) { top_ = value; }
+ Element* top_;
+ bool* ok_;
+};
+
+
+RegExpBuilder::RegExpBuilder(Zone* zone)
+ : zone_(zone),
+ pending_empty_(false),
+ characters_(NULL),
+ terms_(),
+ alternatives_()
+#ifdef DEBUG
+ , last_added_(ADD_NONE)
+#endif
+ {}
+
+
+void RegExpBuilder::FlushCharacters() {
+ pending_empty_ = false;
+ if (characters_ != NULL) {
+ RegExpTree* atom = new(zone()) RegExpAtom(characters_->ToConstVector());
+ characters_ = NULL;
+ text_.Add(atom, zone());
+ LAST(ADD_ATOM);
+ }
+}
+
+
+void RegExpBuilder::FlushText() {
+ FlushCharacters();
+ int num_text = text_.length();
+ if (num_text == 0) {
+ return;
+ } else if (num_text == 1) {
+ terms_.Add(text_.last(), zone());
+ } else {
+ RegExpText* text = new(zone()) RegExpText(zone());
+ for (int i = 0; i < num_text; i++)
+ text_.Get(i)->AppendToText(text, zone());
+ terms_.Add(text, zone());
+ }
+ text_.Clear();
+}
+
+
+void RegExpBuilder::AddCharacter(uc16 c) {
+ pending_empty_ = false;
+ if (characters_ == NULL) {
+ characters_ = new(zone()) ZoneList<uc16>(4, zone());
+ }
+ characters_->Add(c, zone());
+ LAST(ADD_CHAR);
+}
+
+
+void RegExpBuilder::AddEmpty() {
+ pending_empty_ = true;
+}
+
+
+void RegExpBuilder::AddAtom(RegExpTree* term) {
+ if (term->IsEmpty()) {
+ AddEmpty();
+ return;
+ }
+ if (term->IsTextElement()) {
+ FlushCharacters();
+ text_.Add(term, zone());
+ } else {
+ FlushText();
+ terms_.Add(term, zone());
+ }
+ LAST(ADD_ATOM);
+}
+
+
+void RegExpBuilder::AddAssertion(RegExpTree* assert) {
+ FlushText();
+ terms_.Add(assert, zone());
+ LAST(ADD_ASSERT);
+}
+
+
+void RegExpBuilder::NewAlternative() {
+ FlushTerms();
+}
+
+
+void RegExpBuilder::FlushTerms() {
+ FlushText();
+ int num_terms = terms_.length();
+ RegExpTree* alternative;
+ if (num_terms == 0) {
+ alternative = RegExpEmpty::GetInstance();
+ } else if (num_terms == 1) {
+ alternative = terms_.last();
+ } else {
+ alternative = new(zone()) RegExpAlternative(terms_.GetList(zone()));
+ }
+ alternatives_.Add(alternative, zone());
+ terms_.Clear();
+ LAST(ADD_NONE);
+}
+
+
+RegExpTree* RegExpBuilder::ToRegExp() {
+ FlushTerms();
+ int num_alternatives = alternatives_.length();
+ if (num_alternatives == 0) {
+ return RegExpEmpty::GetInstance();
+ }
+ if (num_alternatives == 1) {
+ return alternatives_.last();
+ }
+ return new(zone()) RegExpDisjunction(alternatives_.GetList(zone()));
+}
+
+
+void RegExpBuilder::AddQuantifierToAtom(
+ int min, int max, RegExpQuantifier::QuantifierType quantifier_type) {
+ if (pending_empty_) {
+ pending_empty_ = false;
+ return;
+ }
+ RegExpTree* atom;
+ if (characters_ != NULL) {
+ ASSERT(last_added_ == ADD_CHAR);
+ // Last atom was character.
+ Vector<const uc16> char_vector = characters_->ToConstVector();
+ int num_chars = char_vector.length();
+ if (num_chars > 1) {
+ Vector<const uc16> prefix = char_vector.SubVector(0, num_chars - 1);
+ text_.Add(new(zone()) RegExpAtom(prefix), zone());
+ char_vector = char_vector.SubVector(num_chars - 1, num_chars);
+ }
+ characters_ = NULL;
+ atom = new(zone()) RegExpAtom(char_vector);
+ FlushText();
+ } else if (text_.length() > 0) {
+ ASSERT(last_added_ == ADD_ATOM);
+ atom = text_.RemoveLast();
+ FlushText();
+ } else if (terms_.length() > 0) {
+ ASSERT(last_added_ == ADD_ATOM);
+ atom = terms_.RemoveLast();
+ if (atom->max_match() == 0) {
+ // Guaranteed to only match an empty string.
+ LAST(ADD_TERM);
+ if (min == 0) {
+ return;
+ }
+ terms_.Add(atom, zone());
+ return;
+ }
+ } else {
+ // Only call immediately after adding an atom or character!
+ UNREACHABLE();
+ return;
+ }
+ terms_.Add(
+ new(zone()) RegExpQuantifier(min, max, quantifier_type, atom), zone());
+ LAST(ADD_TERM);
+}
+
+
+Handle<String> Parser::LookupSymbol(int symbol_id) {
+ // Length of symbol cache is the number of identified symbols.
+ // If we are larger than that, or negative, it's not a cached symbol.
+ // This might also happen if there is no preparser symbol data, even
+ // if there is some preparser data.
+ if (static_cast<unsigned>(symbol_id)
+ >= static_cast<unsigned>(symbol_cache_.length())) {
+ if (scanner().is_literal_ascii()) {
+ return isolate()->factory()->InternalizeOneByteString(
+ Vector<const uint8_t>::cast(scanner().literal_ascii_string()));
+ } else {
+ return isolate()->factory()->InternalizeTwoByteString(
+ scanner().literal_utf16_string());
+ }
+ }
+ return LookupCachedSymbol(symbol_id);
+}
+
+
+Handle<String> Parser::LookupCachedSymbol(int symbol_id) {
+ // Make sure the cache is large enough to hold the symbol identifier.
+ if (symbol_cache_.length() <= symbol_id) {
+ // Increase length to index + 1.
+ symbol_cache_.AddBlock(Handle<String>::null(),
+ symbol_id + 1 - symbol_cache_.length(), zone());
+ }
+ Handle<String> result = symbol_cache_.at(symbol_id);
+ if (result.is_null()) {
+ if (scanner().is_literal_ascii()) {
+ result = isolate()->factory()->InternalizeOneByteString(
+ Vector<const uint8_t>::cast(scanner().literal_ascii_string()));
+ } else {
+ result = isolate()->factory()->InternalizeTwoByteString(
+ scanner().literal_utf16_string());
+ }
+ symbol_cache_.at(symbol_id) = result;
+ return result;
+ }
+ isolate()->counters()->total_preparse_symbols_skipped()->Increment();
+ return result;
+}
+
+
+FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) {
+ // The current pre-data entry must be a FunctionEntry with the given
+ // start position.
+ if ((function_index_ + FunctionEntry::kSize <= store_.length())
+ && (static_cast<int>(store_[function_index_]) == start)) {
+ int index = function_index_;
+ function_index_ += FunctionEntry::kSize;
+ return FunctionEntry(store_.SubVector(index,
+ index + FunctionEntry::kSize));
+ }
+ return FunctionEntry();
+}
+
+
+int ScriptDataImpl::GetSymbolIdentifier() {
+ return ReadNumber(&symbol_data_);
+}
+
+
+bool ScriptDataImpl::SanityCheck() {
+ // Check that the header data is valid and doesn't specify
+ // point to positions outside the store.
+ if (store_.length() < PreparseDataConstants::kHeaderSize) return false;
+ if (magic() != PreparseDataConstants::kMagicNumber) return false;
+ if (version() != PreparseDataConstants::kCurrentVersion) return false;
+ if (has_error()) {
+ // Extra sane sanity check for error message encoding.
+ if (store_.length() <= PreparseDataConstants::kHeaderSize
+ + PreparseDataConstants::kMessageTextPos) {
+ return false;
+ }
+ if (Read(PreparseDataConstants::kMessageStartPos) >
+ Read(PreparseDataConstants::kMessageEndPos)) {
+ return false;
+ }
+ unsigned arg_count = Read(PreparseDataConstants::kMessageArgCountPos);
+ int pos = PreparseDataConstants::kMessageTextPos;
+ for (unsigned int i = 0; i <= arg_count; i++) {
+ if (store_.length() <= PreparseDataConstants::kHeaderSize + pos) {
+ return false;
+ }
+ int length = static_cast<int>(Read(pos));
+ if (length < 0) return false;
+ pos += 1 + length;
+ }
+ if (store_.length() < PreparseDataConstants::kHeaderSize + pos) {
+ return false;
+ }
+ return true;
+ }
+ // Check that the space allocated for function entries is sane.
+ int functions_size =
+ static_cast<int>(store_[PreparseDataConstants::kFunctionsSizeOffset]);
+ if (functions_size < 0) return false;
+ if (functions_size % FunctionEntry::kSize != 0) return false;
+ // Check that the count of symbols is non-negative.
+ int symbol_count =
+ static_cast<int>(store_[PreparseDataConstants::kSymbolCountOffset]);
+ if (symbol_count < 0) return false;
+ // Check that the total size has room for header and function entries.
+ int minimum_size =
+ PreparseDataConstants::kHeaderSize + functions_size;
+ if (store_.length() < minimum_size) return false;
+ return true;
+}
+
+
+
+const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) {
+ int length = start[0];
+ char* result = NewArray<char>(length + 1);
+ for (int i = 0; i < length; i++) {
+ result[i] = start[i + 1];
+ }
+ result[length] = '\0';
+ if (chars != NULL) *chars = length;
+ return result;
+}
+
+
+Scanner::Location ScriptDataImpl::MessageLocation() {
+ int beg_pos = Read(PreparseDataConstants::kMessageStartPos);
+ int end_pos = Read(PreparseDataConstants::kMessageEndPos);
+ return Scanner::Location(beg_pos, end_pos);
+}
+
+
+const char* ScriptDataImpl::BuildMessage() {
+ unsigned* start = ReadAddress(PreparseDataConstants::kMessageTextPos);
+ return ReadString(start, NULL);
+}
+
+
+Vector<const char*> ScriptDataImpl::BuildArgs() {
+ int arg_count = Read(PreparseDataConstants::kMessageArgCountPos);
+ const char** array = NewArray<const char*>(arg_count);
+ // Position after text found by skipping past length field and
+ // length field content words.
+ int pos = PreparseDataConstants::kMessageTextPos + 1
+ + Read(PreparseDataConstants::kMessageTextPos);
+ for (int i = 0; i < arg_count; i++) {
+ int count = 0;
+ array[i] = ReadString(ReadAddress(pos), &count);
+ pos += count + 1;
+ }
+ return Vector<const char*>(array, arg_count);
+}
+
+
+unsigned ScriptDataImpl::Read(int position) {
+ return store_[PreparseDataConstants::kHeaderSize + position];
+}
+
+
+unsigned* ScriptDataImpl::ReadAddress(int position) {
+ return &store_[PreparseDataConstants::kHeaderSize + position];
+}
+
+
+Scope* Parser::NewScope(Scope* parent, ScopeType scope_type) {
+ Scope* result = new(zone()) Scope(parent, scope_type, zone());
+ result->Initialize();
+ return result;
+}
+
+
+// ----------------------------------------------------------------------------
+// Target is a support class to facilitate manipulation of the
+// Parser's target_stack_ (the stack of potential 'break' and
+// 'continue' statement targets). Upon construction, a new target is
+// added; it is removed upon destruction.
+
+class Target BASE_EMBEDDED {
+ public:
+ Target(Target** variable, AstNode* node)
+ : variable_(variable), node_(node), previous_(*variable) {
+ *variable = this;
+ }
+
+ ~Target() {
+ *variable_ = previous_;
+ }
+
+ Target* previous() { return previous_; }
+ AstNode* node() { return node_; }
+
+ private:
+ Target** variable_;
+ AstNode* node_;
+ Target* previous_;
+};
+
+
+class TargetScope BASE_EMBEDDED {
+ public:
+ explicit TargetScope(Target** variable)
+ : variable_(variable), previous_(*variable) {
+ *variable = NULL;
+ }
+
+ ~TargetScope() {
+ *variable_ = previous_;
+ }
+
+ private:
+ Target** variable_;
+ Target* previous_;
+};
+
+
+// ----------------------------------------------------------------------------
+// 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 Parser::BlockState BASE_EMBEDDED {
+ public:
+ BlockState(Parser* parser, Scope* scope)
+ : parser_(parser),
+ outer_scope_(parser->top_scope_) {
+ parser->top_scope_ = scope;
+ }
+
+ ~BlockState() { parser_->top_scope_ = outer_scope_; }
+
+ private:
+ Parser* parser_;
+ Scope* outer_scope_;
+};
+
+
+Parser::FunctionState::FunctionState(Parser* parser,
+ Scope* scope,
+ Isolate* isolate)
+ : next_materialized_literal_index_(JSFunction::kLiteralsPrefixSize),
+ next_handler_index_(0),
+ expected_property_count_(0),
+ generator_object_variable_(NULL),
+ 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->zone()) {
+ parser->top_scope_ = scope;
+ parser->current_function_state_ = this;
+ isolate->set_ast_node_id(BailoutId::FirstUsable().ToInt());
+}
+
+
+Parser::FunctionState::~FunctionState() {
+ parser_->top_scope_ = outer_scope_;
+ parser_->current_function_state_ = outer_function_state_;
+ if (outer_function_state_ != NULL) {
+ parser_->isolate()->set_ast_node_id(saved_ast_node_id_);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// The CHECK_OK macro is a convenient macro to enforce error
+// handling for functions that may fail (by returning !*ok).
+//
+// CAUTION: This macro appends extra statements after a call,
+// thus it must never be used where only a single statement
+// is correct (e.g. an if statement branch w/o braces)!
+
+#define CHECK_OK ok); \
+ if (!*ok) return NULL; \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+#define CHECK_FAILED /**/); \
+ if (failed_) return NULL; \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+// ----------------------------------------------------------------------------
+// Implementation of Parser
+
+Parser::Parser(CompilationInfo* info)
+ : isolate_(info->isolate()),
+ symbol_cache_(0, info->zone()),
+ script_(info->script()),
+ scanner_(isolate_->unicode_cache()),
+ reusable_preparser_(NULL),
+ top_scope_(NULL),
+ current_function_state_(NULL),
+ target_stack_(NULL),
+ extension_(info->extension()),
+ pre_parse_data_(NULL),
+ fni_(NULL),
+ allow_natives_syntax_(false),
+ allow_lazy_(false),
+ allow_generators_(false),
+ allow_for_of_(false),
+ stack_overflow_(false),
+ parenthesized_function_(false),
+ zone_(info->zone()),
+ info_(info) {
+ ASSERT(!script_.is_null());
+ isolate_->set_ast_node_id(0);
+ set_allow_harmony_scoping(!info->is_native() && FLAG_harmony_scoping);
+ set_allow_modules(!info->is_native() && FLAG_harmony_modules);
+ set_allow_natives_syntax(FLAG_allow_natives_syntax || info->is_native());
+ set_allow_lazy(false); // Must be explicitly enabled.
+ set_allow_generators(FLAG_harmony_generators);
+ set_allow_for_of(FLAG_harmony_iteration);
+ set_allow_harmony_numeric_literals(FLAG_harmony_numeric_literals);
+}
+
+
+FunctionLiteral* Parser::ParseProgram() {
+ HistogramTimerScope timer(isolate()->counters()->parse());
+ Handle<String> source(String::cast(script_->source()));
+ isolate()->counters()->total_parse_size()->Increment(source->length());
+ int64_t start = FLAG_trace_parse ? OS::Ticks() : 0;
+ fni_ = new(zone()) FuncNameInferrer(isolate(), zone());
+
+ // Initialize parser state.
+ source->TryFlatten();
+ FunctionLiteral* result;
+ if (source->IsExternalTwoByteString()) {
+ // Notice that the stream is destroyed at the end of the branch block.
+ // The last line of the blocks can't be moved outside, even though they're
+ // identical calls.
+ ExternalTwoByteStringUtf16CharacterStream stream(
+ Handle<ExternalTwoByteString>::cast(source), 0, source->length());
+ scanner_.Initialize(&stream);
+ result = DoParseProgram(info(), source);
+ } else {
+ GenericStringUtf16CharacterStream stream(source, 0, source->length());
+ scanner_.Initialize(&stream);
+ result = DoParseProgram(info(), source);
+ }
+
+ if (FLAG_trace_parse && result != NULL) {
+ double ms = static_cast<double>(OS::Ticks() - start) / 1000;
+ if (info()->is_eval()) {
+ PrintF("[parsing eval");
+ } else if (info()->script()->name()->IsString()) {
+ String* name = String::cast(info()->script()->name());
+ SmartArrayPointer<char> name_chars = name->ToCString();
+ PrintF("[parsing script: %s", *name_chars);
+ } else {
+ PrintF("[parsing script");
+ }
+ PrintF(" - took %0.3f ms]\n", ms);
+ }
+ return result;
+}
+
+
+FunctionLiteral* Parser::DoParseProgram(CompilationInfo* info,
+ Handle<String> source) {
+ ASSERT(top_scope_ == NULL);
+ ASSERT(target_stack_ == NULL);
+ if (pre_parse_data_ != NULL) pre_parse_data_->Initialize();
+
+ Handle<String> no_name = isolate()->factory()->empty_string();
+
+ FunctionLiteral* result = NULL;
+ { Scope* scope = NewScope(top_scope_, GLOBAL_SCOPE);
+ info->SetGlobalScope(scope);
+ if (!info->context().is_null()) {
+ scope = Scope::DeserializeScopeChain(*info->context(), scope, zone());
+ }
+ if (info->is_eval()) {
+ if (!scope->is_global_scope() || info->language_mode() != CLASSIC_MODE) {
+ scope = NewScope(scope, EVAL_SCOPE);
+ }
+ } else if (info->is_global()) {
+ scope = NewScope(scope, GLOBAL_SCOPE);
+ }
+ scope->set_start_position(0);
+ scope->set_end_position(source->length());
+
+ // Compute the parsing mode.
+ Mode mode = (FLAG_lazy && allow_lazy()) ? PARSE_LAZILY : PARSE_EAGERLY;
+ if (allow_natives_syntax() ||
+ extension_ != NULL ||
+ scope->is_eval_scope()) {
+ mode = PARSE_EAGERLY;
+ }
+ ParsingModeScope parsing_mode(this, mode);
+
+ // Enters 'scope'.
+ FunctionState function_state(this, scope, isolate());
+
+ top_scope_->SetLanguageMode(info->language_mode());
+ ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone());
+ bool ok = true;
+ int beg_loc = scanner().location().beg_pos;
+ ParseSourceElements(body, Token::EOS, info->is_eval(), true, &ok);
+ if (ok && !top_scope_->is_classic_mode()) {
+ CheckOctalLiteral(beg_loc, scanner().location().end_pos, &ok);
+ }
+
+ if (ok && is_extended_mode()) {
+ CheckConflictingVarDeclarations(top_scope_, &ok);
+ }
+
+ if (ok && info->parse_restriction() == ONLY_SINGLE_FUNCTION_LITERAL) {
+ if (body->length() != 1 ||
+ !body->at(0)->IsExpressionStatement() ||
+ !body->at(0)->AsExpressionStatement()->
+ expression()->IsFunctionLiteral()) {
+ ReportMessage("single_function_literal", Vector<const char*>::empty());
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ result = factory()->NewFunctionLiteral(
+ no_name,
+ top_scope_,
+ body,
+ function_state.materialized_literal_count(),
+ function_state.expected_property_count(),
+ function_state.handler_count(),
+ 0,
+ FunctionLiteral::kNoDuplicateParameters,
+ FunctionLiteral::ANONYMOUS_EXPRESSION,
+ FunctionLiteral::kGlobalOrEval,
+ FunctionLiteral::kNotParenthesized,
+ FunctionLiteral::kNotGenerator);
+ result->set_ast_properties(factory()->visitor()->ast_properties());
+ } else if (stack_overflow_) {
+ isolate()->StackOverflow();
+ }
+ }
+
+ // Make sure the target stack is empty.
+ ASSERT(target_stack_ == NULL);
+
+ return result;
+}
+
+
+FunctionLiteral* Parser::ParseLazy() {
+ HistogramTimerScope timer(isolate()->counters()->parse_lazy());
+ Handle<String> source(String::cast(script_->source()));
+ isolate()->counters()->total_parse_size()->Increment(source->length());
+ int64_t start = FLAG_trace_parse ? OS::Ticks() : 0;
+ Handle<SharedFunctionInfo> shared_info = info()->shared_info();
+
+ // Initialize parser state.
+ source->TryFlatten();
+ FunctionLiteral* result;
+ if (source->IsExternalTwoByteString()) {
+ ExternalTwoByteStringUtf16CharacterStream stream(
+ Handle<ExternalTwoByteString>::cast(source),
+ shared_info->start_position(),
+ shared_info->end_position());
+ result = ParseLazy(&stream);
+ } else {
+ GenericStringUtf16CharacterStream stream(source,
+ shared_info->start_position(),
+ shared_info->end_position());
+ result = ParseLazy(&stream);
+ }
+
+ if (FLAG_trace_parse && result != NULL) {
+ double ms = static_cast<double>(OS::Ticks() - start) / 1000;
+ SmartArrayPointer<char> name_chars = result->debug_name()->ToCString();
+ PrintF("[parsing function: %s - took %0.3f ms]\n", *name_chars, ms);
+ }
+ return result;
+}
+
+
+FunctionLiteral* Parser::ParseLazy(Utf16CharacterStream* source) {
+ 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()));
+ fni_ = new(zone()) FuncNameInferrer(isolate(), zone());
+ fni_->PushEnclosingName(name);
+
+ ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
+
+ // Place holder for the result.
+ FunctionLiteral* result = NULL;
+
+ {
+ // Parse the function literal.
+ Scope* scope = NewScope(top_scope_, GLOBAL_SCOPE);
+ info()->SetGlobalScope(scope);
+ if (!info()->closure().is_null()) {
+ scope = Scope::DeserializeScopeChain(info()->closure()->context(), scope,
+ zone());
+ }
+ 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::FunctionType function_type = shared_info->is_expression()
+ ? (shared_info->is_anonymous()
+ ? FunctionLiteral::ANONYMOUS_EXPRESSION
+ : FunctionLiteral::NAMED_EXPRESSION)
+ : FunctionLiteral::DECLARATION;
+ bool ok = true;
+ result = ParseFunctionLiteral(name,
+ false, // Strict mode name already checked.
+ shared_info->is_generator(),
+ RelocInfo::kNoPosition,
+ function_type,
+ &ok);
+ // Make sure the results agree.
+ ASSERT(ok == (result != NULL));
+ }
+
+ // Make sure the target stack is empty.
+ ASSERT(target_stack_ == NULL);
+
+ if (result == NULL) {
+ if (stack_overflow_) isolate()->StackOverflow();
+ } else {
+ Handle<String> inferred_name(shared_info->inferred_name());
+ result->set_inferred_name(inferred_name);
+ }
+ return result;
+}
+
+
+Handle<String> Parser::GetSymbol() {
+ int symbol_id = -1;
+ if (pre_parse_data() != NULL) {
+ symbol_id = pre_parse_data()->GetSymbolIdentifier();
+ }
+ return LookupSymbol(symbol_id);
+}
+
+
+void Parser::ReportMessage(const char* message, Vector<const char*> args) {
+ Scanner::Location source_location = scanner().location();
+ ReportMessageAt(source_location, message, args);
+}
+
+
+void Parser::ReportMessage(const char* message, Vector<Handle<String> > args) {
+ Scanner::Location source_location = scanner().location();
+ ReportMessageAt(source_location, message, args);
+}
+
+
+void Parser::ReportMessageAt(Scanner::Location source_location,
+ const char* message,
+ Vector<const char*> args) {
+ MessageLocation location(script_,
+ source_location.beg_pos,
+ source_location.end_pos);
+ Factory* factory = isolate()->factory();
+ Handle<FixedArray> elements = factory->NewFixedArray(args.length());
+ for (int i = 0; i < args.length(); i++) {
+ Handle<String> arg_string = factory->NewStringFromUtf8(CStrVector(args[i]));
+ elements->set(i, *arg_string);
+ }
+ Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
+ Handle<Object> result = factory->NewSyntaxError(message, array);
+ isolate()->Throw(*result, &location);
+}
+
+
+void Parser::ReportMessageAt(Scanner::Location source_location,
+ const char* message,
+ Vector<Handle<String> > args) {
+ MessageLocation location(script_,
+ source_location.beg_pos,
+ source_location.end_pos);
+ Factory* factory = isolate()->factory();
+ Handle<FixedArray> elements = factory->NewFixedArray(args.length());
+ for (int i = 0; i < args.length(); i++) {
+ elements->set(i, *args[i]);
+ }
+ Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
+ Handle<Object> result = factory->NewSyntaxError(message, array);
+ isolate()->Throw(*result, &location);
+}
+
+
+void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
+ int end_token,
+ bool is_eval,
+ bool is_global,
+ bool* ok) {
+ // SourceElements ::
+ // (ModuleElement)* <end_token>
+
+ // Allocate a target stack to use for this set of source
+ // elements. This way, all scripts and functions get their own
+ // target stack thus avoiding illegal breaks and continues across
+ // functions.
+ TargetScope scope(&this->target_stack_);
+
+ ASSERT(processor != NULL);
+ bool directive_prologue = true; // Parsing directive prologue.
+
+ while (peek() != end_token) {
+ if (directive_prologue && peek() != Token::STRING) {
+ directive_prologue = false;
+ }
+
+ Scanner::Location token_loc = scanner().peek_location();
+ Statement* stat;
+ if (is_global && !is_eval) {
+ stat = ParseModuleElement(NULL, CHECK_OK);
+ } else {
+ stat = ParseBlockElement(NULL, CHECK_OK);
+ }
+ if (stat == NULL || stat->IsEmpty()) {
+ directive_prologue = false; // End of directive prologue.
+ continue;
+ }
+
+ if (directive_prologue) {
+ // A shot at a directive.
+ ExpressionStatement* e_stat;
+ Literal* literal;
+ // Still processing directive prologue?
+ if ((e_stat = stat->AsExpressionStatement()) != NULL &&
+ (literal = e_stat->expression()->AsLiteral()) != NULL &&
+ literal->value()->IsString()) {
+ Handle<String> directive = Handle<String>::cast(literal->value());
+
+ // Check "use strict" directive (ES5 14.1).
+ if (top_scope_->is_classic_mode() &&
+ directive->Equals(isolate()->heap()->use_strict_string()) &&
+ token_loc.end_pos - token_loc.beg_pos ==
+ isolate()->heap()->use_strict_string()->length() + 2) {
+ // TODO(mstarzinger): Global strict eval calls, need their own scope
+ // as specified in ES5 10.4.2(3). The correct fix would be to always
+ // add this scope in DoParseProgram(), but that requires adaptations
+ // all over the code base, so we go with a quick-fix for now.
+ // In the same manner, we have to patch the parsing mode.
+ if (is_eval && !top_scope_->is_eval_scope()) {
+ ASSERT(top_scope_->is_global_scope());
+ Scope* scope = NewScope(top_scope_, EVAL_SCOPE);
+ scope->set_start_position(top_scope_->start_position());
+ scope->set_end_position(top_scope_->end_position());
+ top_scope_ = scope;
+ mode_ = PARSE_EAGERLY;
+ }
+ // TODO(ES6): Fix entering extended mode, once it is specified.
+ top_scope_->SetLanguageMode(allow_harmony_scoping()
+ ? EXTENDED_MODE : STRICT_MODE);
+ // "use strict" is the only directive for now.
+ directive_prologue = false;
+ }
+ } else {
+ // End of the directive prologue.
+ directive_prologue = false;
+ }
+ }
+
+ processor->Add(stat, zone());
+ }
+
+ return 0;
+}
+
+
+Statement* Parser::ParseModuleElement(ZoneStringList* labels,
+ bool* ok) {
+ // (Ecma 262 5th Edition, clause 14):
+ // SourceElement:
+ // Statement
+ // FunctionDeclaration
+ //
+ // In harmony mode we allow additionally the following productions
+ // ModuleElement:
+ // LetDeclaration
+ // ConstDeclaration
+ // ModuleDeclaration
+ // ImportDeclaration
+ // ExportDeclaration
+ // GeneratorDeclaration
+
+ switch (peek()) {
+ case Token::FUNCTION:
+ return ParseFunctionDeclaration(NULL, ok);
+ case Token::LET:
+ case Token::CONST:
+ return ParseVariableStatement(kModuleElement, NULL, ok);
+ case Token::IMPORT:
+ return ParseImportDeclaration(ok);
+ case Token::EXPORT:
+ return ParseExportDeclaration(ok);
+ default: {
+ Statement* stmt = ParseStatement(labels, CHECK_OK);
+ // Handle 'module' as a context-sensitive keyword.
+ if (FLAG_harmony_modules &&
+ peek() == Token::IDENTIFIER &&
+ !scanner().HasAnyLineTerminatorBeforeNext() &&
+ stmt != NULL) {
+ ExpressionStatement* estmt = stmt->AsExpressionStatement();
+ if (estmt != NULL &&
+ estmt->expression()->AsVariableProxy() != NULL &&
+ estmt->expression()->AsVariableProxy()->name()->Equals(
+ isolate()->heap()->module_string()) &&
+ !scanner().literal_contains_escapes()) {
+ return ParseModuleDeclaration(NULL, ok);
+ }
+ }
+ return stmt;
+ }
+ }
+}
+
+
+Statement* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
+ // ModuleDeclaration:
+ // 'module' Identifier Module
+
+ Handle<String> name = ParseIdentifier(CHECK_OK);
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Module %s...\n", name->ToAsciiArray());
+#endif
+
+ Module* module = ParseModule(CHECK_OK);
+ VariableProxy* proxy = NewUnresolved(name, MODULE, module->interface());
+ Declaration* declaration =
+ factory()->NewModuleDeclaration(proxy, module, top_scope_);
+ Declare(declaration, true, CHECK_OK);
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Module %s.\n", name->ToAsciiArray());
+
+ if (FLAG_print_interfaces) {
+ PrintF("module %s : ", name->ToAsciiArray());
+ module->interface()->Print();
+ }
+#endif
+
+ if (names) names->Add(name, zone());
+ if (module->body() == NULL)
+ return factory()->NewEmptyStatement();
+ else
+ return factory()->NewModuleStatement(proxy, module->body());
+}
+
+
+Module* Parser::ParseModule(bool* ok) {
+ // Module:
+ // '{' ModuleElement '}'
+ // '=' ModulePath ';'
+ // 'at' String ';'
+
+ switch (peek()) {
+ case Token::LBRACE:
+ return ParseModuleLiteral(ok);
+
+ case Token::ASSIGN: {
+ Expect(Token::ASSIGN, CHECK_OK);
+ Module* result = ParseModulePath(CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ return result;
+ }
+
+ default: {
+ ExpectContextualKeyword(CStrVector("at"), CHECK_OK);
+ Module* result = ParseModuleUrl(CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ return result;
+ }
+ }
+}
+
+
+Module* Parser::ParseModuleLiteral(bool* ok) {
+ // Module:
+ // '{' ModuleElement '}'
+
+ // Construct block expecting 16 statements.
+ Block* body = factory()->NewBlock(NULL, 16, false);
+#ifdef DEBUG
+ if (FLAG_print_interface_details) PrintF("# Literal ");
+#endif
+ Scope* scope = NewScope(top_scope_, MODULE_SCOPE);
+
+ Expect(Token::LBRACE, CHECK_OK);
+ scope->set_start_position(scanner().location().beg_pos);
+ scope->SetLanguageMode(EXTENDED_MODE);
+
+ {
+ BlockState block_state(this, scope);
+ TargetCollector collector(zone());
+ Target target(&this->target_stack_, &collector);
+ Target target_body(&this->target_stack_, body);
+
+ while (peek() != Token::RBRACE) {
+ Statement* stat = ParseModuleElement(NULL, CHECK_OK);
+ if (stat && !stat->IsEmpty()) {
+ body->AddStatement(stat, zone());
+ }
+ }
+ }
+
+ Expect(Token::RBRACE, CHECK_OK);
+ scope->set_end_position(scanner().location().end_pos);
+ body->set_scope(scope);
+
+ // Check that all exports are bound.
+ Interface* interface = scope->interface();
+ for (Interface::Iterator it = interface->iterator();
+ !it.done(); it.Advance()) {
+ if (scope->LocalLookup(it.name()) == NULL) {
+ Handle<String> name(it.name());
+ ReportMessage("module_export_undefined",
+ Vector<Handle<String> >(&name, 1));
+ *ok = false;
+ return NULL;
+ }
+ }
+
+ interface->MakeModule(ok);
+ ASSERT(*ok);
+ interface->Freeze(ok);
+ ASSERT(*ok);
+ return factory()->NewModuleLiteral(body, interface);
+}
+
+
+Module* Parser::ParseModulePath(bool* ok) {
+ // ModulePath:
+ // Identifier
+ // ModulePath '.' Identifier
+
+ Module* result = ParseModuleVariable(CHECK_OK);
+ while (Check(Token::PERIOD)) {
+ Handle<String> name = ParseIdentifierName(CHECK_OK);
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Path .%s ", name->ToAsciiArray());
+#endif
+ Module* member = factory()->NewModulePath(result, name);
+ result->interface()->Add(name, member->interface(), zone(), ok);
+ if (!*ok) {
+#ifdef DEBUG
+ if (FLAG_print_interfaces) {
+ PrintF("PATH TYPE ERROR at '%s'\n", name->ToAsciiArray());
+ PrintF("result: ");
+ result->interface()->Print();
+ PrintF("member: ");
+ member->interface()->Print();
+ }
+#endif
+ ReportMessage("invalid_module_path", Vector<Handle<String> >(&name, 1));
+ return NULL;
+ }
+ result = member;
+ }
+
+ return result;
+}
+
+
+Module* Parser::ParseModuleVariable(bool* ok) {
+ // ModulePath:
+ // Identifier
+
+ Handle<String> name = ParseIdentifier(CHECK_OK);
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Module variable %s ", name->ToAsciiArray());
+#endif
+ VariableProxy* proxy = top_scope_->NewUnresolved(
+ factory(), name, Interface::NewModule(zone()),
+ scanner().location().beg_pos);
+
+ return factory()->NewModuleVariable(proxy);
+}
+
+
+Module* Parser::ParseModuleUrl(bool* ok) {
+ // Module:
+ // String
+
+ Expect(Token::STRING, CHECK_OK);
+ Handle<String> symbol = GetSymbol();
+
+ // TODO(ES6): Request JS resource from environment...
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details) PrintF("# Url ");
+#endif
+
+ // Create an empty literal as long as the feature isn't finished.
+ USE(symbol);
+ Scope* scope = NewScope(top_scope_, MODULE_SCOPE);
+ Block* body = factory()->NewBlock(NULL, 1, false);
+ body->set_scope(scope);
+ Interface* interface = scope->interface();
+ Module* result = factory()->NewModuleLiteral(body, interface);
+ interface->Freeze(ok);
+ ASSERT(*ok);
+ interface->Unify(scope->interface(), zone(), ok);
+ ASSERT(*ok);
+ return result;
+}
+
+
+Module* Parser::ParseModuleSpecifier(bool* ok) {
+ // ModuleSpecifier:
+ // String
+ // ModulePath
+
+ if (peek() == Token::STRING) {
+ return ParseModuleUrl(ok);
+ } else {
+ return ParseModulePath(ok);
+ }
+}
+
+
+Block* Parser::ParseImportDeclaration(bool* ok) {
+ // ImportDeclaration:
+ // 'import' IdentifierName (',' IdentifierName)* 'from' ModuleSpecifier ';'
+ //
+ // TODO(ES6): implement destructuring ImportSpecifiers
+
+ Expect(Token::IMPORT, CHECK_OK);
+ ZoneStringList names(1, zone());
+
+ Handle<String> name = ParseIdentifierName(CHECK_OK);
+ names.Add(name, zone());
+ while (peek() == Token::COMMA) {
+ Consume(Token::COMMA);
+ name = ParseIdentifierName(CHECK_OK);
+ names.Add(name, zone());
+ }
+
+ ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
+ Module* module = ParseModuleSpecifier(CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+
+ // Generate a separate declaration for each identifier.
+ // TODO(ES6): once we implement destructuring, make that one declaration.
+ Block* block = factory()->NewBlock(NULL, 1, true);
+ for (int i = 0; i < names.length(); ++i) {
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Import %s ", names[i]->ToAsciiArray());
+#endif
+ Interface* interface = Interface::NewUnknown(zone());
+ module->interface()->Add(names[i], interface, zone(), ok);
+ if (!*ok) {
+#ifdef DEBUG
+ if (FLAG_print_interfaces) {
+ PrintF("IMPORT TYPE ERROR at '%s'\n", names[i]->ToAsciiArray());
+ PrintF("module: ");
+ module->interface()->Print();
+ }
+#endif
+ ReportMessage("invalid_module_path", Vector<Handle<String> >(&name, 1));
+ return NULL;
+ }
+ VariableProxy* proxy = NewUnresolved(names[i], LET, interface);
+ Declaration* declaration =
+ factory()->NewImportDeclaration(proxy, module, top_scope_);
+ Declare(declaration, true, CHECK_OK);
+ }
+
+ return block;
+}
+
+
+Statement* Parser::ParseExportDeclaration(bool* ok) {
+ // ExportDeclaration:
+ // 'export' Identifier (',' Identifier)* ';'
+ // 'export' VariableDeclaration
+ // 'export' FunctionDeclaration
+ // 'export' GeneratorDeclaration
+ // 'export' ModuleDeclaration
+ //
+ // TODO(ES6): implement structuring ExportSpecifiers
+
+ Expect(Token::EXPORT, CHECK_OK);
+
+ Statement* result = NULL;
+ ZoneStringList names(1, zone());
+ switch (peek()) {
+ case Token::IDENTIFIER: {
+ Handle<String> name = ParseIdentifier(CHECK_OK);
+ // Handle 'module' as a context-sensitive keyword.
+ if (!name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("module"))) {
+ names.Add(name, zone());
+ while (peek() == Token::COMMA) {
+ Consume(Token::COMMA);
+ name = ParseIdentifier(CHECK_OK);
+ names.Add(name, zone());
+ }
+ ExpectSemicolon(CHECK_OK);
+ result = factory()->NewEmptyStatement();
+ } else {
+ result = ParseModuleDeclaration(&names, CHECK_OK);
+ }
+ break;
+ }
+
+ case Token::FUNCTION:
+ result = ParseFunctionDeclaration(&names, CHECK_OK);
+ break;
+
+ case Token::VAR:
+ case Token::LET:
+ case Token::CONST:
+ result = ParseVariableStatement(kModuleElement, &names, CHECK_OK);
+ break;
+
+ default:
+ *ok = false;
+ ReportUnexpectedToken(scanner().current_token());
+ return NULL;
+ }
+
+ // Extract declared names into export declarations and interface.
+ Interface* interface = top_scope_->interface();
+ for (int i = 0; i < names.length(); ++i) {
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Export %s ", names[i]->ToAsciiArray());
+#endif
+ Interface* inner = Interface::NewUnknown(zone());
+ interface->Add(names[i], inner, zone(), CHECK_OK);
+ if (!*ok)
+ return NULL;
+ VariableProxy* proxy = NewUnresolved(names[i], LET, inner);
+ USE(proxy);
+ // TODO(rossberg): Rethink whether we actually need to store export
+ // declarations (for compilation?).
+ // ExportDeclaration* declaration =
+ // factory()->NewExportDeclaration(proxy, top_scope_);
+ // top_scope_->AddDeclaration(declaration);
+ }
+
+ ASSERT(result != NULL);
+ return result;
+}
+
+
+Statement* Parser::ParseBlockElement(ZoneStringList* labels,
+ bool* ok) {
+ // (Ecma 262 5th Edition, clause 14):
+ // SourceElement:
+ // Statement
+ // FunctionDeclaration
+ //
+ // In harmony mode we allow additionally the following productions
+ // BlockElement (aka SourceElement):
+ // LetDeclaration
+ // ConstDeclaration
+ // GeneratorDeclaration
+
+ switch (peek()) {
+ case Token::FUNCTION:
+ return ParseFunctionDeclaration(NULL, ok);
+ case Token::LET:
+ case Token::CONST:
+ return ParseVariableStatement(kModuleElement, NULL, ok);
+ default:
+ return ParseStatement(labels, ok);
+ }
+}
+
+
+Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
+ // Statement ::
+ // Block
+ // VariableStatement
+ // EmptyStatement
+ // ExpressionStatement
+ // IfStatement
+ // IterationStatement
+ // ContinueStatement
+ // BreakStatement
+ // ReturnStatement
+ // WithStatement
+ // LabelledStatement
+ // SwitchStatement
+ // ThrowStatement
+ // TryStatement
+ // DebuggerStatement
+
+ // Note: Since labels can only be used by 'break' and 'continue'
+ // statements, which themselves are only valid within blocks,
+ // iterations or 'switch' statements (i.e., BreakableStatements),
+ // labels can be simply ignored in all other cases; except for
+ // trivial labeled break statements 'label: break label' which is
+ // parsed into an empty statement.
+
+ // Keep the source position of the statement
+ int statement_pos = scanner().peek_location().beg_pos;
+ Statement* stmt = NULL;
+ switch (peek()) {
+ case Token::LBRACE:
+ return ParseBlock(labels, ok);
+
+ case Token::CONST: // fall through
+ case Token::LET:
+ case Token::VAR:
+ stmt = ParseVariableStatement(kStatement, NULL, ok);
+ break;
+
+ case Token::SEMICOLON:
+ Next();
+ return factory()->NewEmptyStatement();
+
+ case Token::IF:
+ stmt = ParseIfStatement(labels, ok);
+ break;
+
+ case Token::DO:
+ stmt = ParseDoWhileStatement(labels, ok);
+ break;
+
+ case Token::WHILE:
+ stmt = ParseWhileStatement(labels, ok);
+ break;
+
+ case Token::FOR:
+ stmt = ParseForStatement(labels, ok);
+ break;
+
+ case Token::CONTINUE:
+ stmt = ParseContinueStatement(ok);
+ break;
+
+ case Token::BREAK:
+ stmt = ParseBreakStatement(labels, ok);
+ break;
+
+ case Token::RETURN:
+ stmt = ParseReturnStatement(ok);
+ break;
+
+ case Token::WITH:
+ stmt = ParseWithStatement(labels, ok);
+ break;
+
+ case Token::SWITCH:
+ stmt = ParseSwitchStatement(labels, ok);
+ break;
+
+ case Token::THROW:
+ stmt = ParseThrowStatement(ok);
+ break;
+
+ case Token::TRY: {
+ // NOTE: It is somewhat complicated to have labels on
+ // try-statements. When breaking out of a try-finally statement,
+ // 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 = factory()->NewBlock(labels, 1, false);
+ Target target(&this->target_stack_, result);
+ TryStatement* statement = ParseTryStatement(CHECK_OK);
+ if (statement) {
+ statement->set_statement_pos(statement_pos);
+ }
+ if (result) result->AddStatement(statement, zone());
+ return result;
+ }
+
+ case 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.
+ //
+ // In Harmony mode, this case also handles the extension:
+ // Statement:
+ // GeneratorDeclaration
+ if (!top_scope_->is_classic_mode()) {
+ ReportMessageAt(scanner().peek_location(), "strict_function",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ return ParseFunctionDeclaration(NULL, ok);
+ }
+
+ case Token::DEBUGGER:
+ stmt = ParseDebuggerStatement(ok);
+ break;
+
+ default:
+ stmt = ParseExpressionOrLabelledStatement(labels, ok);
+ }
+
+ // Store the source position of the statement
+ if (stmt != NULL) stmt->set_statement_pos(statement_pos);
+ return stmt;
+}
+
+
+VariableProxy* Parser::NewUnresolved(
+ Handle<String> name, VariableMode mode, Interface* interface) {
+ // 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.
+ return DeclarationScope(mode)->NewUnresolved(
+ factory(), name, interface, scanner().location().beg_pos);
+}
+
+
+void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
+ VariableProxy* proxy = declaration->proxy();
+ Handle<String> name = proxy->name();
+ VariableMode mode = declaration->mode();
+ Scope* declaration_scope = DeclarationScope(mode);
+ Variable* var = NULL;
+
+ // If a suitable scope exists, then we can statically declare this
+ // variable and also set its mode. In any case, a Declaration node
+ // will be added to the scope so that the declaration can be added
+ // to the corresponding activation frame at runtime if necessary.
+ // For instance declarations inside an eval scope need to be added
+ // 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.
+ if (declaration_scope->is_function_scope() ||
+ declaration_scope->is_strict_or_extended_eval_scope() ||
+ declaration_scope->is_block_scope() ||
+ declaration_scope->is_module_scope() ||
+ declaration_scope->is_global_scope()) {
+ // Declare the variable in the declaration scope.
+ // For the global scope, we have to check for collisions with earlier
+ // (i.e., enclosing) global scopes, to maintain the illusion of a single
+ // global scope.
+ var = declaration_scope->is_global_scope()
+ ? declaration_scope->Lookup(name)
+ : declaration_scope->LocalLookup(name);
+ if (var == NULL) {
+ // Declare the name.
+ var = declaration_scope->DeclareLocal(
+ name, mode, declaration->initialization(), proxy->interface());
+ } else if ((mode != VAR || var->mode() != VAR) &&
+ (!declaration_scope->is_global_scope() ||
+ IsLexicalVariableMode(mode) ||
+ IsLexicalVariableMode(var->mode()))) {
+ // The name was declared in this scope before; check for conflicting
+ // re-declarations. We have a conflict if either of the declarations is
+ // not a var (in the global scope, we also have to ignore legacy const for
+ // compatibility). There is similar code in runtime.cc in the Declare
+ // functions. The function CheckNonConflictingScope checks for conflicting
+ // var and let bindings from different scopes whereas this is a check for
+ // conflicting declarations within the same scope. This check also covers
+ // the special case
+ //
+ // function () { let x; { var x; } }
+ //
+ // because the var declaration is hoisted to the function scope where 'x'
+ // is already bound.
+ ASSERT(IsDeclaredVariableMode(var->mode()));
+ 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);
+ const char* elms[2] = { "Variable", *c_string };
+ Vector<const char*> args(elms, 2);
+ ReportMessage("redeclaration", args);
+ *ok = false;
+ return;
+ }
+ Handle<String> message_string =
+ isolate()->factory()->NewStringFromUtf8(CStrVector("Variable"),
+ TENURED);
+ Expression* expression =
+ NewThrowTypeError(isolate()->factory()->redeclaration_string(),
+ message_string, name);
+ declaration_scope->SetIllegalRedeclaration(expression);
+ }
+ }
+
+ // We add a declaration node for every declaration. The compiler
+ // will only generate code if necessary. In particular, declarations
+ // for inner local variables that do not represent functions won't
+ // result in any generated code.
+ //
+ // Note that we always add an unresolved proxy even if it's not
+ // used, simply because we don't know in this method (w/o extra
+ // parameters) if the proxy is needed or not. The proxy will be
+ // bound during variable resolution time unless it was pre-bound
+ // below.
+ //
+ // WARNING: This will lead to multiple declaration nodes for the
+ // same variable if it is declared several times. This is not a
+ // semantic issue as long as we keep the source order, but it may be
+ // a performance issue since it may lead to repeated
+ // Runtime::DeclareContextSlot() calls.
+ declaration_scope->AddDeclaration(declaration);
+
+ if (mode == CONST && 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, mode, true, kind,
+ kNeedsInitialization, proxy->interface());
+ } 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,
+ declaration->initialization(), proxy->interface());
+ var->AllocateTo(Variable::LOOKUP, -1);
+ resolve = true;
+ }
+
+ // If requested and we have a local variable, bind the proxy to the variable
+ // at parse-time. This is used for functions (and consts) declared inside
+ // statements: the corresponding function (or const) variable must be in the
+ // function scope and not a statement-local scope, e.g. as provided with a
+ // 'with' statement:
+ //
+ // with (obj) {
+ // function f() {}
+ // }
+ //
+ // which is translated into:
+ //
+ // with (obj) {
+ // // in this case this is not: 'var f; f = function () {};'
+ // var f = function () {};
+ // }
+ //
+ // Note that if 'f' is accessed from inside the 'with' statement, it
+ // will be allocated in the context (because we must be able to look
+ // it up dynamically) but it will also be accessed statically, i.e.,
+ // with a context slot index and a context chain length for this
+ // initialization code. Thus, inside the 'with' statement, we need
+ // both access to the static and the dynamic context chain; the
+ // runtime needs to provide both.
+ if (resolve && var != NULL) {
+ proxy->BindTo(var);
+
+ if (FLAG_harmony_modules) {
+ bool ok;
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Declare %s\n", var->name()->ToAsciiArray());
+#endif
+ proxy->interface()->Unify(var->interface(), zone(), &ok);
+ if (!ok) {
+#ifdef DEBUG
+ if (FLAG_print_interfaces) {
+ PrintF("DECLARE TYPE ERROR\n");
+ PrintF("proxy: ");
+ proxy->interface()->Print();
+ PrintF("var: ");
+ var->interface()->Print();
+ }
+#endif
+ ReportMessage("module_type_error", Vector<Handle<String> >(&name, 1));
+ }
+ }
+ }
+}
+
+
+// Language extension which is only enabled for source files loaded
+// through the API's extension mechanism. A native function
+// declaration is resolved by looking up the function through a
+// callback provided by the extension.
+Statement* Parser::ParseNativeDeclaration(bool* ok) {
+ Expect(Token::FUNCTION, CHECK_OK);
+ Handle<String> name = ParseIdentifier(CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+ bool done = (peek() == Token::RPAREN);
+ while (!done) {
+ ParseIdentifier(CHECK_OK);
+ done = (peek() == Token::RPAREN);
+ if (!done) {
+ Expect(Token::COMMA, CHECK_OK);
+ }
+ }
+ Expect(Token::RPAREN, CHECK_OK);
+ Expect(Token::SEMICOLON, CHECK_OK);
+
+ // Make sure that the function containing the native declaration
+ // isn't lazily compiled. The extension structures are only
+ // accessible while parsing the first time not when reparsing
+ // because of lazy compilation.
+ DeclarationScope(VAR)->ForceEagerCompilation();
+
+ // Compute the function template for the native function.
+ v8::Handle<v8::FunctionTemplate> fun_template =
+ extension_->GetNativeFunction(v8::Utils::ToLocal(name));
+ ASSERT(!fun_template.IsEmpty());
+
+ // Instantiate the function and create a shared function info from it.
+ Handle<JSFunction> fun = Utils::OpenHandle(*fun_template->GetFunction());
+ const int literals = fun->NumberOfLiterals();
+ Handle<Code> code = Handle<Code>(fun->shared()->code());
+ Handle<Code> construct_stub = Handle<Code>(fun->shared()->construct_stub());
+ bool is_generator = false;
+ Handle<SharedFunctionInfo> shared =
+ isolate()->factory()->NewSharedFunctionInfo(name, literals, is_generator,
+ code, Handle<ScopeInfo>(fun->shared()->scope_info()));
+ shared->set_construct_stub(*construct_stub);
+
+ // Copy the function data to the shared function info.
+ shared->set_function_data(fun->shared()->function_data());
+ int parameters = fun->shared()->formal_parameter_count();
+ shared->set_formal_parameter_count(parameters);
+
+ // TODO(1240846): It's weird that native function declarations are
+ // introduced dynamically when we meet their declarations, whereas
+ // other functions are set up when entering the surrounding scope.
+ VariableProxy* proxy = NewUnresolved(name, VAR, Interface::NewValue());
+ Declaration* declaration =
+ factory()->NewVariableDeclaration(proxy, VAR, top_scope_);
+ Declare(declaration, true, CHECK_OK);
+ SharedFunctionInfoLiteral* lit =
+ factory()->NewSharedFunctionInfoLiteral(shared);
+ return factory()->NewExpressionStatement(
+ factory()->NewAssignment(
+ Token::INIT_VAR, proxy, lit, RelocInfo::kNoPosition));
+}
+
+
+Statement* Parser::ParseFunctionDeclaration(ZoneStringList* names, bool* ok) {
+ // FunctionDeclaration ::
+ // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
+ // GeneratorDeclaration ::
+ // 'function' '*' Identifier '(' FormalParameterListopt ')'
+ // '{' FunctionBody '}'
+ Expect(Token::FUNCTION, CHECK_OK);
+ int function_token_position = scanner().location().beg_pos;
+ bool is_generator = allow_generators() && Check(Token::MUL);
+ bool is_strict_reserved = false;
+ Handle<String> name = ParseIdentifierOrStrictReservedWord(
+ &is_strict_reserved, CHECK_OK);
+ FunctionLiteral* fun = ParseFunctionLiteral(name,
+ is_strict_reserved,
+ is_generator,
+ function_token_position,
+ FunctionLiteral::DECLARATION,
+ CHECK_OK);
+ // Even if we're not at the top-level of the global or a function
+ // scope, we treat it as such and introduce the function with its
+ // initial value upon entering the corresponding scope.
+ // In extended mode, a function behaves as a lexical binding, except in the
+ // global scope.
+ VariableMode mode =
+ is_extended_mode() && !top_scope_->is_global_scope() ? LET : VAR;
+ VariableProxy* proxy = NewUnresolved(name, mode, Interface::NewValue());
+ Declaration* declaration =
+ factory()->NewFunctionDeclaration(proxy, mode, fun, top_scope_);
+ Declare(declaration, true, CHECK_OK);
+ if (names) names->Add(name, zone());
+ return factory()->NewEmptyStatement();
+}
+
+
+Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) {
+ if (top_scope_->is_extended_mode()) return ParseScopedBlock(labels, ok);
+
+ // Block ::
+ // '{' Statement* '}'
+
+ // Note that a Block does not introduce a new execution scope!
+ // (ECMA-262, 3rd, 12.2)
+ //
+ // Construct block expecting 16 statements.
+ Block* result = factory()->NewBlock(labels, 16, false);
+ Target target(&this->target_stack_, result);
+ Expect(Token::LBRACE, CHECK_OK);
+ while (peek() != Token::RBRACE) {
+ Statement* stat = ParseStatement(NULL, CHECK_OK);
+ if (stat && !stat->IsEmpty()) {
+ result->AddStatement(stat, zone());
+ }
+ }
+ Expect(Token::RBRACE, CHECK_OK);
+ return result;
+}
+
+
+Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) {
+ // The harmony mode uses block elements instead of statements.
+ //
+ // Block ::
+ // '{' BlockElement* '}'
+
+ // Construct block expecting 16 statements.
+ Block* body = factory()->NewBlock(labels, 16, false);
+ Scope* block_scope = NewScope(top_scope_, BLOCK_SCOPE);
+
+ // Parse the statements and collect escaping labels.
+ Expect(Token::LBRACE, CHECK_OK);
+ block_scope->set_start_position(scanner().location().beg_pos);
+ { BlockState block_state(this, block_scope);
+ TargetCollector collector(zone());
+ Target target(&this->target_stack_, &collector);
+ Target target_body(&this->target_stack_, body);
+
+ while (peek() != Token::RBRACE) {
+ Statement* stat = ParseBlockElement(NULL, CHECK_OK);
+ if (stat && !stat->IsEmpty()) {
+ body->AddStatement(stat, zone());
+ }
+ }
+ }
+ Expect(Token::RBRACE, CHECK_OK);
+ block_scope->set_end_position(scanner().location().end_pos);
+ block_scope = block_scope->FinalizeBlockScope();
+ body->set_scope(block_scope);
+ return body;
+}
+
+
+Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
+ ZoneStringList* names,
+ bool* ok) {
+ // VariableStatement ::
+ // VariableDeclarations ';'
+
+ Handle<String> ignore;
+ Block* result =
+ ParseVariableDeclarations(var_context, NULL, names, &ignore, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ return result;
+}
+
+
+bool Parser::IsEvalOrArguments(Handle<String> string) {
+ return string.is_identical_to(isolate()->factory()->eval_string()) ||
+ string.is_identical_to(isolate()->factory()->arguments_string());
+}
+
+
+// If the variable declaration declares exactly one non-const
+// 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,
+ VariableDeclarationProperties* decl_props,
+ ZoneStringList* names,
+ Handle<String>* out,
+ bool* ok) {
+ // VariableDeclarations ::
+ // ('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
+ // immediately by their declaration nodes.
+ bool needs_init = false;
+ bool is_const = false;
+ Token::Value init_op = Token::INIT_VAR;
+ 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);
+ 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 == kStatement) {
+ // 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;
+ }
+ is_const = true;
+ needs_init = true;
+ } 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 == kStatement) {
+ // Let declarations are only allowed in source element positions.
+ ReportMessage("unprotected_let", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ mode = LET;
+ needs_init = true;
+ init_op = Token::INIT_LET;
+ } else {
+ UNREACHABLE(); // by current callers
+ }
+
+ Scope* declaration_scope = DeclarationScope(mode);
+
+ // 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
+ // transform a source-level var/const declaration into a (Function)
+ // Scope declaration, and rewrite the source-level initialization into an
+ // assignment statement. We use a block to collect multiple assignments.
+ //
+ // We mark the block as initializer block because we don't want the
+ // rewriter to add a '.result' assignment to such a block (to get compliant
+ // behavior for code such as print(eval('var x = 7')), and for cosmetic
+ // reasons when pretty-printing. Also, unless an assignment (initialization)
+ // is inside an initializer block, it is ignored.
+ //
+ // Create new block with one expected declaration.
+ Block* block = factory()->NewBlock(NULL, 1, true);
+ int nvars = 0; // the number of variables declared
+ Handle<String> name;
+ do {
+ if (fni_ != NULL) fni_->Enter();
+
+ // Parse variable name.
+ if (nvars > 0) Consume(Token::COMMA);
+ name = ParseIdentifier(CHECK_OK);
+ if (fni_ != NULL) fni_->PushVariableName(name);
+
+ // Strict mode variables may not be named eval or arguments
+ if (!declaration_scope->is_classic_mode() && IsEvalOrArguments(name)) {
+ ReportMessage("strict_var_name", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+
+ // Declare variable.
+ // Note that we *always* must treat the initial value via a separate init
+ // assignment for variables and constants because the value must be assigned
+ // when the variable is encountered in the source. But the variable/constant
+ // is declared (and set to 'undefined') upon entering the function within
+ // which the variable or constant is declared. Only function variables have
+ // an initial value in the declaration (because they are initialized upon
+ // entering the function).
+ //
+ // 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).
+ // 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.
+ Interface* interface =
+ is_const ? Interface::NewConst() : Interface::NewValue();
+ VariableProxy* proxy = NewUnresolved(name, mode, interface);
+ Declaration* declaration =
+ factory()->NewVariableDeclaration(proxy, mode, top_scope_);
+ Declare(declaration, mode != VAR, CHECK_OK);
+ nvars++;
+ if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
+ ReportMessageAt(scanner().location(), "too_many_variables",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ if (names) names->Add(name, zone());
+
+ // Parse initialization expression if present and/or needed. A
+ // declaration of the form:
+ //
+ // var v = x;
+ //
+ // is syntactic sugar for:
+ //
+ // var v; v = x;
+ //
+ // In particular, we need to re-lookup 'v' (in top_scope_, not
+ // declaration_scope) as it may be a different 'v' than the 'v' in the
+ // declaration (e.g., if we are inside a 'with' statement or 'catch'
+ // block).
+ //
+ // However, note that const declarations are different! A const
+ // declaration of the form:
+ //
+ // const c = x;
+ //
+ // is *not* syntactic sugar for:
+ //
+ // const c; c = x;
+ //
+ // The "variable" c initialized to x is the same as the declared
+ // one - there is no re-lookup (see the last parameter of the
+ // Declare() call above).
+
+ Scope* initialization_scope = is_const ? declaration_scope : top_scope_;
+ Expression* value = NULL;
+ int position = -1;
+ // 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);
+ // Don't infer if it is "a = function(){...}();"-like expression.
+ if (fni_ != NULL &&
+ value->AsCall() == NULL &&
+ value->AsCallNew() == NULL) {
+ fni_->Infer();
+ } 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.
+ if (value == NULL && needs_init) {
+ value = GetLiteralUndefined();
+ }
+
+ // Global variable declarations must be compiled in a specific
+ // way. When the script containing the global variable declaration
+ // is entered, the global variable must be declared, so that if it
+ // doesn't exist (on the global object itself, see ES5 errata) it
+ // gets created with an initial undefined value. This is handled
+ // by the declarations part of the function representing the
+ // top-level global code; see Runtime::DeclareGlobalVariable. If
+ // it already exists (in the object or in a prototype), it is
+ // *not* touched until the variable declaration statement is
+ // executed.
+ //
+ // Executing the variable declaration statement will always
+ // guarantee to give the global object a "local" variable; a
+ // variable defined in the global object and not in any
+ // prototype. This way, global variable declarations can shadow
+ // properties in the prototype chain, but only after the variable
+ // 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() &&
+ !IsLexicalVariableMode(mode)) {
+ // Compute the arguments for the runtime call.
+ ZoneList<Expression*>* arguments =
+ new(zone()) ZoneList<Expression*>(3, zone());
+ // We have at least 1 parameter.
+ arguments->Add(factory()->NewLiteral(name), zone());
+ CallRuntime* initialize;
+
+ if (is_const) {
+ arguments->Add(value, zone());
+ value = NULL; // zap the value to avoid the unnecessary assignment
+
+ // Construct the call to Runtime_InitializeConstGlobal
+ // 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 = factory()->NewCallRuntime(
+ isolate()->factory()->InitializeConstGlobal_string(),
+ Runtime::FunctionForId(Runtime::kInitializeConstGlobal),
+ arguments);
+ } else {
+ // Add strict mode.
+ // We may want to pass singleton to avoid Literal allocations.
+ LanguageMode language_mode = initialization_scope->language_mode();
+ arguments->Add(factory()->NewNumberLiteral(language_mode), zone());
+
+ // Be careful not to assign a value to the global variable if
+ // we're in a with. The initialization value should not
+ // necessarily be stored in the global object in that case,
+ // which is why we need to generate a separate assignment node.
+ if (value != NULL && !inside_with()) {
+ arguments->Add(value, zone());
+ value = NULL; // zap the value to avoid the unnecessary assignment
+ }
+
+ // Construct the call to Runtime_InitializeVarGlobal
+ // 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 = factory()->NewCallRuntime(
+ isolate()->factory()->InitializeVarGlobal_string(),
+ Runtime::FunctionForId(Runtime::kInitializeVarGlobal),
+ arguments);
+ }
+
+ block->AddStatement(factory()->NewExpressionStatement(initialize),
+ zone());
+ } 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),
+ zone());
+ value = NULL;
+ }
+
+ // Add an assignment node to the initialization statement block if we still
+ // have a pending initialization value.
+ if (value != NULL) {
+ 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(factory(), name, interface);
+ Assignment* assignment =
+ factory()->NewAssignment(init_op, proxy, value, position);
+ block->AddStatement(factory()->NewExpressionStatement(assignment),
+ zone());
+ }
+
+ if (fni_ != NULL) fni_->Leave();
+ } while (peek() == Token::COMMA);
+
+ // If there was a single non-const declaration, return it in the output
+ // parameter for possible use by for/in.
+ if (nvars == 1 && !is_const) {
+ *out = name;
+ }
+
+ return block;
+}
+
+
+static bool ContainsLabel(ZoneStringList* labels, Handle<String> label) {
+ ASSERT(!label.is_null());
+ if (labels != NULL)
+ for (int i = labels->length(); i-- > 0; )
+ if (labels->at(i).is_identical_to(label))
+ return true;
+
+ return false;
+}
+
+
+Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels,
+ bool* ok) {
+ // ExpressionStatement | LabelledStatement ::
+ // Expression ';'
+ // Identifier ':' Statement
+ bool starts_with_idenfifier = peek_any_identifier();
+ Expression* expr = ParseExpression(true, CHECK_OK);
+ if (peek() == Token::COLON && starts_with_idenfifier && expr != NULL &&
+ expr->AsVariableProxy() != NULL &&
+ !expr->AsVariableProxy()->is_this()) {
+ // Expression is a single identifier, and not, e.g., a parenthesized
+ // identifier.
+ VariableProxy* var = expr->AsVariableProxy();
+ Handle<String> label = var->name();
+ // TODO(1240780): We don't check for redeclaration of labels
+ // during preparsing since keeping track of the set of active
+ // labels requires nontrivial changes to the way scopes are
+ // structured. However, these are probably changes we want to
+ // make later anyway so we should go back and fix this then.
+ if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
+ SmartArrayPointer<char> c_string = label->ToCString(DISALLOW_NULLS);
+ const char* elms[2] = { "Label", *c_string };
+ Vector<const char*> args(elms, 2);
+ ReportMessage("redeclaration", args);
+ *ok = false;
+ return NULL;
+ }
+ if (labels == NULL) {
+ labels = new(zone()) ZoneStringList(4, zone());
+ }
+ labels->Add(label, zone());
+ // Remove the "ghost" variable that turned out to be a label
+ // from the top scope. This way, we don't try to resolve it
+ // during the scope processing.
+ top_scope_->RemoveUnresolved(var);
+ Expect(Token::COLON, CHECK_OK);
+ return ParseStatement(labels, ok);
+ }
+
+ // If we have an extension, we allow a native function declaration.
+ // A native function declaration starts with "native function" with
+ // no line-terminator between the two words.
+ if (extension_ != NULL &&
+ peek() == Token::FUNCTION &&
+ !scanner().HasAnyLineTerminatorBeforeNext() &&
+ expr != NULL &&
+ expr->AsVariableProxy() != NULL &&
+ expr->AsVariableProxy()->name()->Equals(
+ isolate()->heap()->native_string()) &&
+ !scanner().literal_contains_escapes()) {
+ return ParseNativeDeclaration(ok);
+ }
+
+ // Parsed expression statement, or the context-sensitive 'module' keyword.
+ // Only expect semicolon in the former case.
+ if (!FLAG_harmony_modules ||
+ peek() != Token::IDENTIFIER ||
+ scanner().HasAnyLineTerminatorBeforeNext() ||
+ expr->AsVariableProxy() == NULL ||
+ !expr->AsVariableProxy()->name()->Equals(
+ isolate()->heap()->module_string()) ||
+ scanner().literal_contains_escapes()) {
+ ExpectSemicolon(CHECK_OK);
+ }
+ return factory()->NewExpressionStatement(expr);
+}
+
+
+IfStatement* Parser::ParseIfStatement(ZoneStringList* labels, bool* ok) {
+ // IfStatement ::
+ // 'if' '(' Expression ')' Statement ('else' Statement)?
+
+ Expect(Token::IF, CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+ Expression* condition = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+ Statement* then_statement = ParseStatement(labels, CHECK_OK);
+ Statement* else_statement = NULL;
+ if (peek() == Token::ELSE) {
+ Next();
+ else_statement = ParseStatement(labels, CHECK_OK);
+ } else {
+ else_statement = factory()->NewEmptyStatement();
+ }
+ return factory()->NewIfStatement(condition, then_statement, else_statement);
+}
+
+
+Statement* Parser::ParseContinueStatement(bool* ok) {
+ // ContinueStatement ::
+ // 'continue' Identifier? ';'
+
+ Expect(Token::CONTINUE, CHECK_OK);
+ Handle<String> label = Handle<String>::null();
+ Token::Value tok = peek();
+ if (!scanner().HasAnyLineTerminatorBeforeNext() &&
+ tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
+ label = ParseIdentifier(CHECK_OK);
+ }
+ IterationStatement* target = NULL;
+ target = LookupContinueTarget(label, CHECK_OK);
+ if (target == NULL) {
+ // Illegal continue statement.
+ const char* message = "illegal_continue";
+ Vector<Handle<String> > args;
+ if (!label.is_null()) {
+ message = "unknown_label";
+ args = Vector<Handle<String> >(&label, 1);
+ }
+ ReportMessageAt(scanner().location(), message, args);
+ *ok = false;
+ return NULL;
+ }
+ ExpectSemicolon(CHECK_OK);
+ return factory()->NewContinueStatement(target);
+}
+
+
+Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) {
+ // BreakStatement ::
+ // 'break' Identifier? ';'
+
+ Expect(Token::BREAK, CHECK_OK);
+ Handle<String> label;
+ Token::Value tok = peek();
+ if (!scanner().HasAnyLineTerminatorBeforeNext() &&
+ tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
+ label = ParseIdentifier(CHECK_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)) {
+ ExpectSemicolon(CHECK_OK);
+ return factory()->NewEmptyStatement();
+ }
+ BreakableStatement* target = NULL;
+ target = LookupBreakTarget(label, CHECK_OK);
+ if (target == NULL) {
+ // Illegal break statement.
+ const char* message = "illegal_break";
+ Vector<Handle<String> > args;
+ if (!label.is_null()) {
+ message = "unknown_label";
+ args = Vector<Handle<String> >(&label, 1);
+ }
+ ReportMessageAt(scanner().location(), message, args);
+ *ok = false;
+ return NULL;
+ }
+ ExpectSemicolon(CHECK_OK);
+ return factory()->NewBreakStatement(target);
+}
+
+
+Statement* Parser::ParseReturnStatement(bool* ok) {
+ // ReturnStatement ::
+ // 'return' Expression? ';'
+
+ // Consume the return token. It is necessary to do the before
+ // reporting any errors on it, because of the way errors are
+ // reported (underlining).
+ Expect(Token::RETURN, CHECK_OK);
+
+ Token::Value tok = peek();
+ Statement* result;
+ Expression* return_value;
+ if (scanner().HasAnyLineTerminatorBeforeNext() ||
+ tok == Token::SEMICOLON ||
+ tok == Token::RBRACE ||
+ tok == Token::EOS) {
+ return_value = GetLiteralUndefined();
+ } else {
+ return_value = ParseExpression(true, CHECK_OK);
+ }
+ ExpectSemicolon(CHECK_OK);
+ if (is_generator()) {
+ Expression* generator = factory()->NewVariableProxy(
+ current_function_state_->generator_object_variable());
+ Expression* yield = factory()->NewYield(
+ generator, return_value, Yield::FINAL, RelocInfo::kNoPosition);
+ result = factory()->NewExpressionStatement(yield);
+ } else {
+ result = factory()->NewReturnStatement(return_value);
+ }
+
+ // An ECMAScript program is considered syntactically incorrect if it
+ // contains a return statement that is not within the body of a
+ // function. See ECMA-262, section 12.9, page 67.
+ //
+ // To be consistent with KJS we report the syntax error at runtime.
+ Scope* declaration_scope = top_scope_->DeclarationScope();
+ if (declaration_scope->is_global_scope() ||
+ declaration_scope->is_eval_scope()) {
+ Handle<String> message = isolate()->factory()->illegal_return_string();
+ Expression* throw_error =
+ NewThrowSyntaxError(message, Handle<Object>::null());
+ return factory()->NewExpressionStatement(throw_error);
+ }
+ return result;
+}
+
+
+Statement* Parser::ParseWithStatement(ZoneStringList* labels, bool* ok) {
+ // WithStatement ::
+ // 'with' '(' Expression ')' Statement
+
+ Expect(Token::WITH, CHECK_OK);
+
+ if (!top_scope_->is_classic_mode()) {
+ ReportMessage("strict_mode_with", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+
+ Expect(Token::LPAREN, CHECK_OK);
+ Expression* expr = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+
+ top_scope_->DeclarationScope()->RecordWithStatement();
+ 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(with_scope, expr, stmt);
+}
+
+
+CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) {
+ // CaseClause ::
+ // 'case' Expression ':' Statement*
+ // 'default' ':' Statement*
+
+ Expression* label = NULL; // NULL expression indicates default case
+ if (peek() == Token::CASE) {
+ Expect(Token::CASE, CHECK_OK);
+ label = ParseExpression(true, CHECK_OK);
+ } else {
+ Expect(Token::DEFAULT, CHECK_OK);
+ if (*default_seen_ptr) {
+ ReportMessage("multiple_defaults_in_switch",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ *default_seen_ptr = true;
+ }
+ Expect(Token::COLON, CHECK_OK);
+ int pos = scanner().location().beg_pos;
+ ZoneList<Statement*>* statements =
+ new(zone()) ZoneList<Statement*>(5, zone());
+ while (peek() != Token::CASE &&
+ peek() != Token::DEFAULT &&
+ peek() != Token::RBRACE) {
+ Statement* stat = ParseStatement(NULL, CHECK_OK);
+ statements->Add(stat, zone());
+ }
+
+ return new(zone()) CaseClause(isolate(), label, statements, pos);
+}
+
+
+SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels,
+ bool* ok) {
+ // SwitchStatement ::
+ // 'switch' '(' Expression ')' '{' CaseClause* '}'
+
+ SwitchStatement* statement = factory()->NewSwitchStatement(labels);
+ Target target(&this->target_stack_, statement);
+
+ Expect(Token::SWITCH, CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+ Expression* tag = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+
+ bool default_seen = false;
+ ZoneList<CaseClause*>* cases = new(zone()) ZoneList<CaseClause*>(4, zone());
+ Expect(Token::LBRACE, CHECK_OK);
+ while (peek() != Token::RBRACE) {
+ CaseClause* clause = ParseCaseClause(&default_seen, CHECK_OK);
+ cases->Add(clause, zone());
+ }
+ Expect(Token::RBRACE, CHECK_OK);
+
+ if (statement) statement->Initialize(tag, cases);
+ return statement;
+}
+
+
+Statement* Parser::ParseThrowStatement(bool* ok) {
+ // ThrowStatement ::
+ // 'throw' Expression ';'
+
+ Expect(Token::THROW, CHECK_OK);
+ int pos = scanner().location().beg_pos;
+ if (scanner().HasAnyLineTerminatorBeforeNext()) {
+ ReportMessage("newline_after_throw", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ Expression* exception = ParseExpression(true, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+
+ return factory()->NewExpressionStatement(factory()->NewThrow(exception, pos));
+}
+
+
+TryStatement* Parser::ParseTryStatement(bool* ok) {
+ // TryStatement ::
+ // 'try' Block Catch
+ // 'try' Block Finally
+ // 'try' Block Catch Finally
+ //
+ // Catch ::
+ // 'catch' '(' Identifier ')' Block
+ //
+ // Finally ::
+ // 'finally' Block
+
+ Expect(Token::TRY, CHECK_OK);
+
+ TargetCollector try_collector(zone());
+ Block* try_block;
+
+ { Target target(&this->target_stack_, &try_collector);
+ try_block = ParseBlock(NULL, CHECK_OK);
+ }
+
+ Token::Value tok = peek();
+ if (tok != Token::CATCH && tok != Token::FINALLY) {
+ ReportMessage("no_catch_or_finally", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+
+ // If we can break out from the catch block and there is a finally block,
+ // then we will need to collect escaping targets from the catch
+ // block. Since we don't know yet if there will be a finally block, we
+ // always collect the targets.
+ TargetCollector catch_collector(zone());
+ Scope* catch_scope = NULL;
+ Variable* catch_variable = NULL;
+ Block* catch_block = NULL;
+ Handle<String> name;
+ if (tok == Token::CATCH) {
+ 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_classic_mode() && IsEvalOrArguments(name)) {
+ ReportMessage("strict_catch_variable", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+
+ Expect(Token::RPAREN, CHECK_OK);
+
+ if (peek() == Token::LBRACE) {
+ Target target(&this->target_stack_, &catch_collector);
+ VariableMode mode = is_extended_mode() ? LET : VAR;
+ catch_variable =
+ catch_scope->DeclareLocal(name, mode, kCreatedInitialized);
+
+ BlockState block_state(this, catch_scope);
+ catch_block = ParseBlock(NULL, CHECK_OK);
+ } else {
+ Expect(Token::LBRACE, CHECK_OK);
+ }
+ catch_scope->set_end_position(scanner().location().end_pos);
+ tok = peek();
+ }
+
+ Block* finally_block = NULL;
+ if (tok == Token::FINALLY || catch_block == NULL) {
+ Consume(Token::FINALLY);
+ finally_block = ParseBlock(NULL, CHECK_OK);
+ }
+
+ // Simplify the AST nodes by converting:
+ // 'try B0 catch B1 finally B2'
+ // to:
+ // 'try { try B0 catch B1 } finally B2'
+
+ if (catch_block != NULL && finally_block != NULL) {
+ // If we have both, create an inner try/catch.
+ ASSERT(catch_scope != NULL && catch_variable != NULL);
+ 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 = factory()->NewBlock(NULL, 1, false);
+ try_block->AddStatement(statement, zone());
+ catch_block = NULL; // Clear to indicate it's been handled.
+ }
+
+ TryStatement* result = NULL;
+ if (catch_block != NULL) {
+ ASSERT(finally_block == NULL);
+ ASSERT(catch_scope != NULL && catch_variable != NULL);
+ int index = current_function_state_->NextHandlerIndex();
+ result = factory()->NewTryCatchStatement(
+ index, try_block, catch_scope, catch_variable, catch_block);
+ } else {
+ ASSERT(finally_block != NULL);
+ 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(), zone());
+ }
+
+ result->set_escaping_targets(try_collector.targets());
+ return result;
+}
+
+
+DoWhileStatement* Parser::ParseDoWhileStatement(ZoneStringList* labels,
+ bool* ok) {
+ // DoStatement ::
+ // 'do' Statement 'while' '(' Expression ')' ';'
+
+ DoWhileStatement* loop = factory()->NewDoWhileStatement(labels);
+ Target target(&this->target_stack_, loop);
+
+ Expect(Token::DO, CHECK_OK);
+ Statement* body = ParseStatement(NULL, CHECK_OK);
+ Expect(Token::WHILE, CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+
+ if (loop != NULL) {
+ int position = scanner().location().beg_pos;
+ loop->set_condition_position(position);
+ }
+
+ Expression* cond = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+
+ // Allow do-statements to be terminated with and without
+ // semi-colons. This allows code such as 'do;while(0)return' to
+ // parse, which would not be the case if we had used the
+ // ExpectSemicolon() functionality here.
+ if (peek() == Token::SEMICOLON) Consume(Token::SEMICOLON);
+
+ if (loop != NULL) loop->Initialize(cond, body);
+ return loop;
+}
+
+
+WhileStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) {
+ // WhileStatement ::
+ // 'while' '(' Expression ')' Statement
+
+ WhileStatement* loop = factory()->NewWhileStatement(labels);
+ Target target(&this->target_stack_, loop);
+
+ Expect(Token::WHILE, CHECK_OK);
+ Expect(Token::LPAREN, CHECK_OK);
+ Expression* cond = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+ Statement* body = ParseStatement(NULL, CHECK_OK);
+
+ if (loop != NULL) loop->Initialize(cond, body);
+ return loop;
+}
+
+
+bool Parser::CheckInOrOf(bool accept_OF,
+ ForEachStatement::VisitMode* visit_mode) {
+ if (Check(Token::IN)) {
+ *visit_mode = ForEachStatement::ENUMERATE;
+ return true;
+ } else if (allow_for_of() && accept_OF &&
+ CheckContextualKeyword(CStrVector("of"))) {
+ *visit_mode = ForEachStatement::ITERATE;
+ return true;
+ }
+ return false;
+}
+
+
+void Parser::InitializeForEachStatement(ForEachStatement* stmt,
+ Expression* each,
+ Expression* subject,
+ Statement* body) {
+ ForOfStatement* for_of = stmt->AsForOfStatement();
+
+ if (for_of != NULL) {
+ Factory* heap_factory = isolate()->factory();
+ Handle<String> iterator_str = heap_factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR(".iterator"));
+ Handle<String> result_str = heap_factory->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR(".result"));
+ Variable* iterator =
+ top_scope_->DeclarationScope()->NewTemporary(iterator_str);
+ Variable* result = top_scope_->DeclarationScope()->NewTemporary(result_str);
+
+ Expression* assign_iterator;
+ Expression* next_result;
+ Expression* result_done;
+ Expression* assign_each;
+
+ // var iterator = iterable;
+ {
+ Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
+ assign_iterator = factory()->NewAssignment(
+ Token::ASSIGN, iterator_proxy, subject, RelocInfo::kNoPosition);
+ }
+
+ // var result = iterator.next();
+ {
+ Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
+ Expression* next_literal =
+ factory()->NewLiteral(heap_factory->next_string());
+ Expression* next_property = factory()->NewProperty(
+ iterator_proxy, next_literal, RelocInfo::kNoPosition);
+ ZoneList<Expression*>* next_arguments =
+ new(zone()) ZoneList<Expression*>(0, zone());
+ Expression* next_call = factory()->NewCall(
+ next_property, next_arguments, RelocInfo::kNoPosition);
+ Expression* result_proxy = factory()->NewVariableProxy(result);
+ next_result = factory()->NewAssignment(
+ Token::ASSIGN, result_proxy, next_call, RelocInfo::kNoPosition);
+ }
+
+ // result.done
+ {
+ Expression* done_literal =
+ factory()->NewLiteral(heap_factory->done_string());
+ Expression* result_proxy = factory()->NewVariableProxy(result);
+ result_done = factory()->NewProperty(
+ result_proxy, done_literal, RelocInfo::kNoPosition);
+ }
+
+ // each = result.value
+ {
+ Expression* value_literal =
+ factory()->NewLiteral(heap_factory->value_string());
+ Expression* result_proxy = factory()->NewVariableProxy(result);
+ Expression* result_value = factory()->NewProperty(
+ result_proxy, value_literal, RelocInfo::kNoPosition);
+ assign_each = factory()->NewAssignment(
+ Token::ASSIGN, each, result_value, RelocInfo::kNoPosition);
+ }
+
+ for_of->Initialize(each, subject, body,
+ assign_iterator, next_result, result_done, assign_each);
+ } else {
+ stmt->Initialize(each, subject, body);
+ }
+}
+
+
+Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
+ // ForStatement ::
+ // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
+
+ 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) {
+ bool is_const = peek() == Token::CONST;
+ Handle<String> name;
+ VariableDeclarationProperties decl_props = kHasNoInitializers;
+ Block* variable_statement =
+ ParseVariableDeclarations(kForStatement, &decl_props, NULL, &name,
+ CHECK_OK);
+ bool accept_OF = decl_props == kHasNoInitializers;
+ ForEachStatement::VisitMode mode;
+
+ if (!name.is_null() && CheckInOrOf(accept_OF, &mode)) {
+ Interface* interface =
+ is_const ? Interface::NewConst() : Interface::NewValue();
+ ForEachStatement* loop = factory()->NewForEachStatement(mode, labels);
+ Target target(&this->target_stack_, loop);
+
+ Expression* enumerable = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+
+ VariableProxy* each =
+ top_scope_->NewUnresolved(factory(), name, interface);
+ Statement* body = ParseStatement(NULL, CHECK_OK);
+ InitializeForEachStatement(loop, each, enumerable, body);
+ Block* result = factory()->NewBlock(NULL, 2, false);
+ result->AddStatement(variable_statement, zone());
+ result->AddStatement(loop, zone());
+ 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, NULL, &name,
+ CHECK_OK);
+ bool accept_IN = !name.is_null() && decl_props != kHasInitializers;
+ bool accept_OF = decl_props == kHasNoInitializers;
+ ForEachStatement::VisitMode mode;
+
+ if (accept_IN && CheckInOrOf(accept_OF, &mode)) {
+ // 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.
+ Factory* heap_factory = isolate()->factory();
+ Handle<String> tempstr =
+ heap_factory->NewConsString(heap_factory->dot_for_string(), name);
+ Handle<String> tempname = heap_factory->InternalizeString(tempstr);
+ Variable* temp = top_scope_->DeclarationScope()->NewTemporary(tempname);
+ VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
+ ForEachStatement* loop = factory()->NewForEachStatement(mode, labels);
+ Target target(&this->target_stack_, loop);
+
+ // The expression does not see the loop variable.
+ top_scope_ = saved_scope;
+ Expression* enumerable = ParseExpression(true, CHECK_OK);
+ top_scope_ = for_scope;
+ Expect(Token::RPAREN, CHECK_OK);
+
+ VariableProxy* each =
+ top_scope_->NewUnresolved(factory(), name, Interface::NewValue());
+ 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, zone());
+ body_block->AddStatement(assignment_statement, zone());
+ body_block->AddStatement(body, zone());
+ InitializeForEachStatement(loop, 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_scope(for_scope);
+ // Parsed for-in loop w/ let declaration.
+ return loop;
+
+ } else {
+ init = variable_statement;
+ }
+ } else {
+ Expression* expression = ParseExpression(false, CHECK_OK);
+ ForEachStatement::VisitMode mode;
+ bool accept_OF = expression->AsVariableProxy();
+
+ if (CheckInOrOf(accept_OF, &mode)) {
+ // Signal a reference error if the expression is an invalid
+ // left-hand side expression. We could report this as a syntax
+ // error here but for compatibility with JSC we choose to report
+ // the error at runtime.
+ if (expression == NULL || !expression->IsValidLeftHandSide()) {
+ Handle<String> message =
+ isolate()->factory()->invalid_lhs_in_for_in_string();
+ expression = NewThrowReferenceError(message);
+ }
+ ForEachStatement* loop = factory()->NewForEachStatement(mode, labels);
+ Target target(&this->target_stack_, loop);
+
+ Expression* enumerable = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+
+ Statement* body = ParseStatement(NULL, CHECK_OK);
+ InitializeForEachStatement(loop, 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 = factory()->NewExpressionStatement(expression);
+ }
+ }
+ }
+
+ // Standard 'for' loop
+ ForStatement* loop = factory()->NewForStatement(labels);
+ Target target(&this->target_stack_, loop);
+
+ // Parsed initializer at this point.
+ Expect(Token::SEMICOLON, CHECK_OK);
+
+ Expression* cond = NULL;
+ if (peek() != Token::SEMICOLON) {
+ cond = ParseExpression(true, CHECK_OK);
+ }
+ Expect(Token::SEMICOLON, CHECK_OK);
+
+ Statement* next = NULL;
+ if (peek() != Token::RPAREN) {
+ Expression* exp = ParseExpression(true, CHECK_OK);
+ next = factory()->NewExpressionStatement(exp);
+ }
+ Expect(Token::RPAREN, CHECK_OK);
+
+ Statement* body = ParseStatement(NULL, CHECK_OK);
+ 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, zone());
+ result->AddStatement(loop, zone());
+ result->set_scope(for_scope);
+ loop->Initialize(NULL, cond, next, body);
+ return result;
+ } else {
+ loop->Initialize(init, cond, next, body);
+ return loop;
+ }
+}
+
+
+// Precedence = 1
+Expression* Parser::ParseExpression(bool accept_IN, bool* ok) {
+ // Expression ::
+ // AssignmentExpression
+ // Expression ',' AssignmentExpression
+
+ Expression* result = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ while (peek() == Token::COMMA) {
+ Expect(Token::COMMA, CHECK_OK);
+ int position = scanner().location().beg_pos;
+ Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ result =
+ factory()->NewBinaryOperation(Token::COMMA, result, right, position);
+ }
+ return result;
+}
+
+
+// Precedence = 2
+Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) {
+ // AssignmentExpression ::
+ // ConditionalExpression
+ // YieldExpression
+ // LeftHandSideExpression AssignmentOperator AssignmentExpression
+
+ if (peek() == Token::YIELD && is_generator()) {
+ return ParseYieldExpression(ok);
+ }
+
+ if (fni_ != NULL) fni_->Enter();
+ Expression* expression = ParseConditionalExpression(accept_IN, CHECK_OK);
+
+ if (!Token::IsAssignmentOp(peek())) {
+ if (fni_ != NULL) fni_->Leave();
+ // Parsed conditional expression only (no assignment).
+ return expression;
+ }
+
+ // Signal a reference error if the expression is an invalid left-hand
+ // side expression. We could report this as a syntax error here but
+ // for compatibility with JSC we choose to report the error at
+ // runtime.
+ // TODO(ES5): Should change parsing for spec conformance.
+ if (expression == NULL || !expression->IsValidLeftHandSide()) {
+ Handle<String> message =
+ isolate()->factory()->invalid_lhs_in_assignment_string();
+ expression = NewThrowReferenceError(message);
+ }
+
+ 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;
+ Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
+
+ // TODO(1231235): We try to estimate the set of properties set by
+ // constructors. We define a new property whenever there is an
+ // assignment to a property of 'this'. We should probably only add
+ // properties if we haven't seen them before. Otherwise we'll
+ // probably overestimate the number of properties.
+ Property* property = expression ? expression->AsProperty() : NULL;
+ if (op == Token::ASSIGN &&
+ property != NULL &&
+ property->obj()->AsVariableProxy() != NULL &&
+ property->obj()->AsVariableProxy()->is_this()) {
+ 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();
+ }
+
+ if (fni_ != NULL) {
+ // Check if the right hand side is a call to avoid inferring a
+ // name if we're dealing with "a = function(){...}();"-like
+ // expression.
+ if ((op == Token::INIT_VAR
+ || op == Token::INIT_CONST
+ || op == Token::ASSIGN)
+ && (right->AsCall() == NULL && right->AsCallNew() == NULL)) {
+ fni_->Infer();
+ } else {
+ fni_->RemoveLastFunction();
+ }
+ fni_->Leave();
+ }
+
+ return factory()->NewAssignment(op, expression, right, pos);
+}
+
+
+Expression* Parser::ParseYieldExpression(bool* ok) {
+ // YieldExpression ::
+ // 'yield' '*'? AssignmentExpression
+ int position = scanner().peek_location().beg_pos;
+ Expect(Token::YIELD, CHECK_OK);
+ Yield::Kind kind =
+ Check(Token::MUL) ? Yield::DELEGATING : Yield::SUSPEND;
+ Expression* generator_object = factory()->NewVariableProxy(
+ current_function_state_->generator_object_variable());
+ Expression* expression = ParseAssignmentExpression(false, CHECK_OK);
+ Yield* yield =
+ factory()->NewYield(generator_object, expression, kind, position);
+ if (kind == Yield::DELEGATING) {
+ yield->set_index(current_function_state_->NextHandlerIndex());
+ }
+ return yield;
+}
+
+
+// Precedence = 3
+Expression* Parser::ParseConditionalExpression(bool accept_IN, bool* ok) {
+ // ConditionalExpression ::
+ // LogicalOrExpression
+ // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression
+
+ // We start using the binary expression parser for prec >= 4 only!
+ Expression* expression = ParseBinaryExpression(4, accept_IN, CHECK_OK);
+ if (peek() != Token::CONDITIONAL) return expression;
+ Consume(Token::CONDITIONAL);
+ // In parsing the first assignment expression in conditional
+ // expressions we always accept the 'in' keyword; see ECMA-262,
+ // section 11.12, page 58.
+ int left_position = scanner().peek_location().beg_pos;
+ Expression* left = ParseAssignmentExpression(true, CHECK_OK);
+ Expect(Token::COLON, CHECK_OK);
+ int right_position = scanner().peek_location().beg_pos;
+ Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ return factory()->NewConditional(
+ expression, left, right, left_position, right_position);
+}
+
+
+static int Precedence(Token::Value tok, bool accept_IN) {
+ if (tok == Token::IN && !accept_IN)
+ return 0; // 0 precedence will terminate binary expression parsing
+
+ return Token::Precedence(tok);
+}
+
+
+// Precedence >= 4
+Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) {
+ ASSERT(prec >= 4);
+ Expression* x = ParseUnaryExpression(CHECK_OK);
+ for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) {
+ // prec1 >= 4
+ while (Precedence(peek(), accept_IN) == prec1) {
+ Token::Value op = Next();
+ int position = scanner().location().beg_pos;
+ Expression* y = ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK);
+
+ // Compute some expressions involving only number literals.
+ if (x && x->AsLiteral() && x->AsLiteral()->value()->IsNumber() &&
+ y && y->AsLiteral() && y->AsLiteral()->value()->IsNumber()) {
+ double x_val = x->AsLiteral()->value()->Number();
+ double y_val = y->AsLiteral()->value()->Number();
+
+ switch (op) {
+ case Token::ADD:
+ x = factory()->NewNumberLiteral(x_val + y_val);
+ continue;
+ case Token::SUB:
+ x = factory()->NewNumberLiteral(x_val - y_val);
+ continue;
+ case Token::MUL:
+ x = factory()->NewNumberLiteral(x_val * y_val);
+ continue;
+ case Token::DIV:
+ x = factory()->NewNumberLiteral(x_val / y_val);
+ continue;
+ case Token::BIT_OR: {
+ int value = DoubleToInt32(x_val) | DoubleToInt32(y_val);
+ x = factory()->NewNumberLiteral(value);
+ continue;
+ }
+ case Token::BIT_AND: {
+ int value = DoubleToInt32(x_val) & DoubleToInt32(y_val);
+ x = factory()->NewNumberLiteral(value);
+ continue;
+ }
+ 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 = factory()->NewNumberLiteral(value);
+ continue;
+ }
+ case Token::SHR: {
+ uint32_t shift = DoubleToInt32(y_val) & 0x1f;
+ uint32_t value = DoubleToUint32(x_val) >> shift;
+ x = factory()->NewNumberLiteral(value);
+ continue;
+ }
+ case Token::SAR: {
+ uint32_t shift = DoubleToInt32(y_val) & 0x1f;
+ int value = ArithmeticShiftRight(DoubleToInt32(x_val), shift);
+ x = factory()->NewNumberLiteral(value);
+ continue;
+ }
+ default:
+ break;
+ }
+ }
+
+ // For now we distinguish between comparisons and other binary
+ // operations. (We could combine the two and get rid of this
+ // code and AST node eventually.)
+ if (Token::IsCompareOp(op)) {
+ // We have a comparison.
+ Token::Value cmp = op;
+ switch (op) {
+ case Token::NE: cmp = Token::EQ; break;
+ case Token::NE_STRICT: cmp = Token::EQ_STRICT; break;
+ default: break;
+ }
+ x = factory()->NewCompareOperation(cmp, x, y, position);
+ if (cmp != op) {
+ // The comparison was negated - add a NOT.
+ x = factory()->NewUnaryOperation(Token::NOT, x, position);
+ }
+
+ } else {
+ // We have a "normal" binary operation.
+ x = factory()->NewBinaryOperation(op, x, y, position);
+ }
+ }
+ }
+ return x;
+}
+
+
+Expression* Parser::ParseUnaryExpression(bool* ok) {
+ // UnaryExpression ::
+ // PostfixExpression
+ // 'delete' UnaryExpression
+ // 'void' UnaryExpression
+ // 'typeof' UnaryExpression
+ // '++' UnaryExpression
+ // '--' UnaryExpression
+ // '+' UnaryExpression
+ // '-' UnaryExpression
+ // '~' UnaryExpression
+ // '!' UnaryExpression
+
+ Token::Value op = peek();
+ if (Token::IsUnaryOp(op)) {
+ op = Next();
+ int position = scanner().location().beg_pos;
+ Expression* expression = ParseUnaryExpression(CHECK_OK);
+
+ if (expression != NULL && (expression->AsLiteral() != NULL)) {
+ Handle<Object> literal = expression->AsLiteral()->value();
+ if (op == Token::NOT) {
+ // Convert the literal to a boolean condition and negate it.
+ bool condition = literal->BooleanValue();
+ Handle<Object> result(isolate()->heap()->ToBoolean(!condition),
+ isolate());
+ return factory()->NewLiteral(result);
+ } else if (literal->IsNumber()) {
+ // Compute some expressions involving only number literals.
+ double value = literal->Number();
+ switch (op) {
+ case Token::ADD:
+ return expression;
+ case Token::SUB:
+ return factory()->NewNumberLiteral(-value);
+ case Token::BIT_NOT:
+ return factory()->NewNumberLiteral(~DoubleToInt32(value));
+ default:
+ break;
+ }
+ }
+ }
+
+ // "delete identifier" is a syntax error in 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());
+ *ok = false;
+ return NULL;
+ }
+ }
+
+ // Desugar '+foo' into 'foo*1', this enables the collection of type feedback
+ // without any special stub and the multiplication is removed later in
+ // Crankshaft's canonicalization pass.
+ if (op == Token::ADD) {
+ return factory()->NewBinaryOperation(Token::MUL,
+ expression,
+ factory()->NewNumberLiteral(1),
+ position);
+ }
+ // The same idea for '-foo' => 'foo*(-1)'.
+ if (op == Token::SUB) {
+ return factory()->NewBinaryOperation(Token::MUL,
+ expression,
+ factory()->NewNumberLiteral(-1),
+ position);
+ }
+ // ...and one more time for '~foo' => 'foo^(~0)'.
+ if (op == Token::BIT_NOT) {
+ return factory()->NewBinaryOperation(Token::BIT_XOR,
+ expression,
+ factory()->NewNumberLiteral(~0),
+ position);
+ }
+
+ return factory()->NewUnaryOperation(op, expression, position);
+
+ } else if (Token::IsCountOp(op)) {
+ op = Next();
+ Expression* expression = ParseUnaryExpression(CHECK_OK);
+ // Signal a reference error if the expression is an invalid
+ // left-hand side expression. We could report this as a syntax
+ // error here but for compatibility with JSC we choose to report the
+ // error at runtime.
+ if (expression == NULL || !expression->IsValidLeftHandSide()) {
+ Handle<String> message =
+ isolate()->factory()->invalid_lhs_in_prefix_op_string();
+ expression = NewThrowReferenceError(message);
+ }
+
+ 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 factory()->NewCountOperation(op,
+ true /* prefix */,
+ expression,
+ position);
+
+ } else {
+ return ParsePostfixExpression(ok);
+ }
+}
+
+
+Expression* Parser::ParsePostfixExpression(bool* ok) {
+ // PostfixExpression ::
+ // LeftHandSideExpression ('++' | '--')?
+
+ Expression* expression = ParseLeftHandSideExpression(CHECK_OK);
+ if (!scanner().HasAnyLineTerminatorBeforeNext() &&
+ Token::IsCountOp(peek())) {
+ // Signal a reference error if the expression is an invalid
+ // left-hand side expression. We could report this as a syntax
+ // error here but for compatibility with JSC we choose to report the
+ // error at runtime.
+ if (expression == NULL || !expression->IsValidLeftHandSide()) {
+ Handle<String> message =
+ isolate()->factory()->invalid_lhs_in_postfix_op_string();
+ expression = NewThrowReferenceError(message);
+ }
+
+ 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 =
+ factory()->NewCountOperation(next,
+ false /* postfix */,
+ expression,
+ position);
+ }
+ return expression;
+}
+
+
+Expression* Parser::ParseLeftHandSideExpression(bool* ok) {
+ // LeftHandSideExpression ::
+ // (NewExpression | MemberExpression) ...
+
+ Expression* result;
+ if (peek() == Token::NEW) {
+ result = ParseNewExpression(CHECK_OK);
+ } else {
+ result = ParseMemberExpression(CHECK_OK);
+ }
+
+ while (true) {
+ switch (peek()) {
+ case Token::LBRACK: {
+ Consume(Token::LBRACK);
+ int pos = scanner().location().beg_pos;
+ Expression* index = ParseExpression(true, CHECK_OK);
+ result = factory()->NewProperty(result, index, pos);
+ Expect(Token::RBRACK, CHECK_OK);
+ break;
+ }
+
+ case Token::LPAREN: {
+ 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;
+ // Also the trailing parenthesis are a hint that the function will
+ // be called immediately. If we happen to have parsed a preceding
+ // function literal eagerly, we can also compile it eagerly.
+ if (result->IsFunctionLiteral() && mode() == PARSE_EAGERLY) {
+ result->AsFunctionLiteral()->set_parenthesized();
+ }
+ }
+ 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 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.
+ VariableProxy* callee = result->AsVariableProxy();
+ if (callee != NULL &&
+ callee->IsVariable(isolate()->factory()->eval_string())) {
+ top_scope_->DeclarationScope()->RecordEvalCall();
+ }
+ result = factory()->NewCall(result, args, pos);
+ if (fni_ != NULL) fni_->RemoveLastFunction();
+ break;
+ }
+
+ case Token::PERIOD: {
+ Consume(Token::PERIOD);
+ int pos = scanner().location().beg_pos;
+ Handle<String> name = ParseIdentifierName(CHECK_OK);
+ result =
+ factory()->NewProperty(result, factory()->NewLiteral(name), pos);
+ if (fni_ != NULL) fni_->PushLiteralName(name);
+ break;
+ }
+
+ default:
+ return result;
+ }
+ }
+}
+
+
+Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) {
+ // NewExpression ::
+ // ('new')+ MemberExpression
+
+ // The grammar for new expressions is pretty warped. The keyword
+ // 'new' can either be a part of the new expression (where it isn't
+ // followed by an argument list) or a part of the member expression,
+ // where it must be followed by an argument list. To accommodate
+ // this, we parse the 'new' keywords greedily and keep track of how
+ // many we have parsed. This information is then passed on to the
+ // member expression parser, which is only allowed to match argument
+ // lists as long as it has 'new' prefixes left
+ Expect(Token::NEW, CHECK_OK);
+ PositionStack::Element pos(stack, scanner().location().beg_pos);
+
+ Expression* result;
+ if (peek() == Token::NEW) {
+ result = ParseNewPrefix(stack, CHECK_OK);
+ } else {
+ result = ParseMemberWithNewPrefixesExpression(stack, CHECK_OK);
+ }
+
+ if (!stack->is_empty()) {
+ int last = stack->pop();
+ result = factory()->NewCallNew(
+ result, new(zone()) ZoneList<Expression*>(0, zone()), last);
+ }
+ return result;
+}
+
+
+Expression* Parser::ParseNewExpression(bool* ok) {
+ PositionStack stack(ok);
+ return ParseNewPrefix(&stack, ok);
+}
+
+
+Expression* Parser::ParseMemberExpression(bool* ok) {
+ return ParseMemberWithNewPrefixesExpression(NULL, ok);
+}
+
+
+Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack,
+ bool* ok) {
+ // MemberExpression ::
+ // (PrimaryExpression | FunctionLiteral)
+ // ('[' Expression ']' | '.' Identifier | Arguments)*
+
+ // Parse the initial primary or function expression.
+ Expression* result = NULL;
+ if (peek() == Token::FUNCTION) {
+ Expect(Token::FUNCTION, CHECK_OK);
+ int function_token_position = scanner().location().beg_pos;
+ bool is_generator = allow_generators() && Check(Token::MUL);
+ Handle<String> name;
+ bool is_strict_reserved_name = false;
+ if (peek_any_identifier()) {
+ name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name,
+ CHECK_OK);
+ }
+ FunctionLiteral::FunctionType function_type = name.is_null()
+ ? FunctionLiteral::ANONYMOUS_EXPRESSION
+ : FunctionLiteral::NAMED_EXPRESSION;
+ result = ParseFunctionLiteral(name,
+ is_strict_reserved_name,
+ is_generator,
+ function_token_position,
+ function_type,
+ CHECK_OK);
+ } else {
+ result = ParsePrimaryExpression(CHECK_OK);
+ }
+
+ while (true) {
+ switch (peek()) {
+ case Token::LBRACK: {
+ Consume(Token::LBRACK);
+ int pos = scanner().location().beg_pos;
+ Expression* index = ParseExpression(true, CHECK_OK);
+ result = factory()->NewProperty(result, index, pos);
+ if (fni_ != NULL) {
+ if (index->IsPropertyName()) {
+ fni_->PushLiteralName(index->AsLiteral()->AsPropertyName());
+ } else {
+ fni_->PushLiteralName(
+ isolate()->factory()->anonymous_function_string());
+ }
+ }
+ Expect(Token::RBRACK, CHECK_OK);
+ break;
+ }
+ case Token::PERIOD: {
+ Consume(Token::PERIOD);
+ int pos = scanner().location().beg_pos;
+ Handle<String> name = ParseIdentifierName(CHECK_OK);
+ result =
+ factory()->NewProperty(result, factory()->NewLiteral(name), pos);
+ if (fni_ != NULL) fni_->PushLiteralName(name);
+ break;
+ }
+ case Token::LPAREN: {
+ if ((stack == NULL) || stack->is_empty()) return result;
+ // Consume one of the new prefixes (already parsed).
+ ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
+ int last = stack->pop();
+ result = factory()->NewCallNew(result, args, last);
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+
+DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) {
+ // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
+ // contexts this is used as a statement which invokes the debugger as i a
+ // break point is present.
+ // DebuggerStatement ::
+ // 'debugger' ';'
+
+ Expect(Token::DEBUGGER, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ return factory()->NewDebuggerStatement();
+}
+
+
+void Parser::ReportUnexpectedToken(Token::Value token) {
+ // We don't report stack overflows here, to avoid increasing the
+ // stack depth even further. Instead we report it after parsing is
+ // over, in ParseProgram/ParseJson.
+ if (token == Token::ILLEGAL && stack_overflow_) return;
+ // Four of the tokens are treated specially
+ switch (token) {
+ case Token::EOS:
+ return ReportMessage("unexpected_eos", Vector<const char*>::empty());
+ case Token::NUMBER:
+ return ReportMessage("unexpected_token_number",
+ Vector<const char*>::empty());
+ case Token::STRING:
+ return ReportMessage("unexpected_token_string",
+ Vector<const char*>::empty());
+ case Token::IDENTIFIER:
+ return ReportMessage("unexpected_token_identifier",
+ Vector<const char*>::empty());
+ case Token::FUTURE_RESERVED_WORD:
+ return ReportMessage("unexpected_reserved",
+ Vector<const char*>::empty());
+ case Token::YIELD:
+ case Token::FUTURE_STRICT_RESERVED_WORD:
+ return ReportMessage(top_scope_->is_classic_mode() ?
+ "unexpected_token_identifier" :
+ "unexpected_strict_reserved",
+ Vector<const char*>::empty());
+ default:
+ const char* name = Token::String(token);
+ ASSERT(name != NULL);
+ ReportMessage("unexpected_token", Vector<const char*>(&name, 1));
+ }
+}
+
+
+void Parser::ReportInvalidPreparseData(Handle<String> name, bool* ok) {
+ SmartArrayPointer<char> name_string = name->ToCString(DISALLOW_NULLS);
+ const char* element[1] = { *name_string };
+ ReportMessage("invalid_preparser_data",
+ Vector<const char*>(element, 1));
+ *ok = false;
+}
+
+
+Expression* Parser::ParsePrimaryExpression(bool* ok) {
+ // PrimaryExpression ::
+ // 'this'
+ // 'null'
+ // 'true'
+ // 'false'
+ // Identifier
+ // Number
+ // String
+ // ArrayLiteral
+ // ObjectLiteral
+ // RegExpLiteral
+ // '(' Expression ')'
+
+ Expression* result = NULL;
+ switch (peek()) {
+ case Token::THIS: {
+ Consume(Token::THIS);
+ result = factory()->NewVariableProxy(top_scope_->receiver());
+ break;
+ }
+
+ case Token::NULL_LITERAL:
+ Consume(Token::NULL_LITERAL);
+ result = factory()->NewLiteral(isolate()->factory()->null_value());
+ break;
+
+ case Token::TRUE_LITERAL:
+ Consume(Token::TRUE_LITERAL);
+ result = factory()->NewLiteral(isolate()->factory()->true_value());
+ break;
+
+ case Token::FALSE_LITERAL:
+ Consume(Token::FALSE_LITERAL);
+ result = factory()->NewLiteral(isolate()->factory()->false_value());
+ break;
+
+ case Token::IDENTIFIER:
+ case Token::YIELD:
+ case Token::FUTURE_STRICT_RESERVED_WORD: {
+ Handle<String> name = ParseIdentifier(CHECK_OK);
+ if (fni_ != NULL) fni_->PushVariableName(name);
+ // The name may refer to a module instance object, so its type is unknown.
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Variable %s ", name->ToAsciiArray());
+#endif
+ Interface* interface = Interface::NewUnknown(zone());
+ result = top_scope_->NewUnresolved(
+ factory(), name, interface, scanner().location().beg_pos);
+ break;
+ }
+
+ case Token::NUMBER: {
+ Consume(Token::NUMBER);
+ ASSERT(scanner().is_literal_ascii());
+ double value = StringToDouble(isolate()->unicode_cache(),
+ scanner().literal_ascii_string(),
+ ALLOW_HEX | ALLOW_OCTAL |
+ ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY);
+ result = factory()->NewNumberLiteral(value);
+ break;
+ }
+
+ case Token::STRING: {
+ Consume(Token::STRING);
+ Handle<String> symbol = GetSymbol();
+ result = factory()->NewLiteral(symbol);
+ if (fni_ != NULL) fni_->PushLiteralName(symbol);
+ break;
+ }
+
+ case Token::ASSIGN_DIV:
+ result = ParseRegExpLiteral(true, CHECK_OK);
+ break;
+
+ case Token::DIV:
+ result = ParseRegExpLiteral(false, CHECK_OK);
+ break;
+
+ case Token::LBRACK:
+ result = ParseArrayLiteral(CHECK_OK);
+ break;
+
+ case Token::LBRACE:
+ result = ParseObjectLiteral(CHECK_OK);
+ break;
+
+ case Token::LPAREN:
+ Consume(Token::LPAREN);
+ // Heuristically try to detect immediately called functions before
+ // seeing the call parentheses.
+ parenthesized_function_ = (peek() == Token::FUNCTION);
+ result = ParseExpression(true, CHECK_OK);
+ Expect(Token::RPAREN, CHECK_OK);
+ break;
+
+ case Token::MOD:
+ if (allow_natives_syntax() || extension_ != NULL) {
+ result = ParseV8Intrinsic(CHECK_OK);
+ break;
+ }
+ // If we're not allowing special syntax we fall-through to the
+ // default case.
+
+ default: {
+ Token::Value tok = Next();
+ ReportUnexpectedToken(tok);
+ *ok = false;
+ return NULL;
+ }
+ }
+
+ return result;
+}
+
+
+Expression* Parser::ParseArrayLiteral(bool* ok) {
+ // ArrayLiteral ::
+ // '[' Expression? (',' Expression?)* ']'
+
+ ZoneList<Expression*>* values = new(zone()) ZoneList<Expression*>(4, zone());
+ Expect(Token::LBRACK, CHECK_OK);
+ while (peek() != Token::RBRACK) {
+ Expression* elem;
+ if (peek() == Token::COMMA) {
+ elem = GetLiteralTheHole();
+ } else {
+ elem = ParseAssignmentExpression(true, CHECK_OK);
+ }
+ values->Add(elem, zone());
+ if (peek() != Token::RBRACK) {
+ Expect(Token::COMMA, CHECK_OK);
+ }
+ }
+ Expect(Token::RBRACK, CHECK_OK);
+
+ // Update the scope information before the pre-parsing bailout.
+ int literal_index = current_function_state_->NextMaterializedLiteralIndex();
+
+ // Allocate a fixed array to hold all the object literals.
+ Handle<JSArray> array =
+ isolate()->factory()->NewJSArray(0, FAST_HOLEY_SMI_ELEMENTS);
+ isolate()->factory()->SetElementsCapacityAndLength(
+ array, values->length(), values->length());
+
+ // Fill in the literals.
+ Heap* heap = isolate()->heap();
+ bool is_simple = true;
+ int depth = 1;
+ bool is_holey = false;
+ for (int i = 0, n = values->length(); i < n; i++) {
+ MaterializedLiteral* m_literal = values->at(i)->AsMaterializedLiteral();
+ if (m_literal != NULL && m_literal->depth() + 1 > depth) {
+ depth = m_literal->depth() + 1;
+ }
+ Handle<Object> boilerplate_value = GetBoilerplateValue(values->at(i));
+ if (boilerplate_value->IsTheHole()) {
+ is_holey = true;
+ } else if (boilerplate_value->IsUninitialized()) {
+ is_simple = false;
+ JSObject::SetOwnElement(
+ array, i, handle(Smi::FromInt(0), isolate()), kNonStrictMode);
+ } else {
+ JSObject::SetOwnElement(array, i, boilerplate_value, kNonStrictMode);
+ }
+ }
+
+ Handle<FixedArrayBase> element_values(array->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 &&
+ array->HasFastSmiOrObjectElements()) {
+ element_values->set_map(heap->fixed_cow_array_map());
+ }
+
+ // 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);
+
+ ElementsKind kind = array->GetElementsKind();
+ kind = is_holey ? GetHoleyElementsKind(kind) : GetPackedElementsKind(kind);
+
+ literals->set(0, Smi::FromInt(kind));
+ literals->set(1, *element_values);
+
+ return factory()->NewArrayLiteral(
+ literals, values, literal_index, is_simple, depth);
+}
+
+
+bool Parser::IsBoilerplateProperty(ObjectLiteral::Property* property) {
+ return property != NULL &&
+ property->kind() != ObjectLiteral::Property::PROTOTYPE;
+}
+
+
+bool CompileTimeValue::IsCompileTimeValue(Expression* expression) {
+ if (expression->AsLiteral() != NULL) return true;
+ MaterializedLiteral* lit = expression->AsMaterializedLiteral();
+ return lit != NULL && lit->is_simple();
+}
+
+
+Handle<FixedArray> CompileTimeValue::GetValue(Expression* expression) {
+ Factory* factory = Isolate::Current()->factory();
+ ASSERT(IsCompileTimeValue(expression));
+ Handle<FixedArray> result = factory->NewFixedArray(2, TENURED);
+ ObjectLiteral* object_literal = expression->AsObjectLiteral();
+ if (object_literal != NULL) {
+ ASSERT(object_literal->is_simple());
+ if (object_literal->fast_elements()) {
+ result->set(kLiteralTypeSlot, Smi::FromInt(OBJECT_LITERAL_FAST_ELEMENTS));
+ } else {
+ result->set(kLiteralTypeSlot, Smi::FromInt(OBJECT_LITERAL_SLOW_ELEMENTS));
+ }
+ result->set(kElementsSlot, *object_literal->constant_properties());
+ } else {
+ ArrayLiteral* array_literal = expression->AsArrayLiteral();
+ ASSERT(array_literal != NULL && array_literal->is_simple());
+ result->set(kLiteralTypeSlot, Smi::FromInt(ARRAY_LITERAL));
+ result->set(kElementsSlot, *array_literal->constant_elements());
+ }
+ return result;
+}
+
+
+CompileTimeValue::LiteralType CompileTimeValue::GetLiteralType(
+ Handle<FixedArray> value) {
+ Smi* literal_type = Smi::cast(value->get(kLiteralTypeSlot));
+ return static_cast<LiteralType>(literal_type->value());
+}
+
+
+Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
+ return Handle<FixedArray>(FixedArray::cast(value->get(kElementsSlot)));
+}
+
+
+Handle<Object> Parser::GetBoilerplateValue(Expression* expression) {
+ if (expression->AsLiteral() != NULL) {
+ return expression->AsLiteral()->value();
+ }
+ if (CompileTimeValue::IsCompileTimeValue(expression)) {
+ return CompileTimeValue::GetValue(expression);
+ }
+ return isolate()->factory()->uninitialized_value();
+}
+
+
+// Validation per 11.1.5 Object Initialiser
+class ObjectLiteralPropertyChecker {
+ public:
+ ObjectLiteralPropertyChecker(Parser* parser, LanguageMode language_mode) :
+ props_(Literal::Match),
+ parser_(parser),
+ language_mode_(language_mode) {
+ }
+
+ void CheckProperty(
+ ObjectLiteral::Property* property,
+ Scanner::Location loc,
+ bool* ok);
+
+ private:
+ enum PropertyKind {
+ kGetAccessor = 0x01,
+ kSetAccessor = 0x02,
+ kAccessor = kGetAccessor | kSetAccessor,
+ kData = 0x04
+ };
+
+ static intptr_t GetPropertyKind(ObjectLiteral::Property* property) {
+ switch (property->kind()) {
+ case ObjectLiteral::Property::GETTER:
+ return kGetAccessor;
+ case ObjectLiteral::Property::SETTER:
+ return kSetAccessor;
+ default:
+ return kData;
+ }
+ }
+
+ HashMap props_;
+ Parser* parser_;
+ LanguageMode language_mode_;
+};
+
+
+void ObjectLiteralPropertyChecker::CheckProperty(
+ ObjectLiteral::Property* property,
+ Scanner::Location loc,
+ bool* ok) {
+ ASSERT(property != NULL);
+ Literal* literal = property->key();
+ HashMap::Entry* entry = props_.Lookup(literal, literal->Hash(), true);
+ intptr_t prev = reinterpret_cast<intptr_t> (entry->value);
+ intptr_t curr = GetPropertyKind(property);
+
+ // 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;
+ return;
+ }
+ // Data property conflicting with an accessor.
+ if (((curr & kData) && (prev & kAccessor)) ||
+ ((prev & kData) && (curr & kAccessor))) {
+ parser_->ReportMessageAt(loc, "accessor_data_property",
+ Vector<const char*>::empty());
+ *ok = false;
+ return;
+ }
+ // Two accessors of the same type conflicting
+ if ((curr & prev & kAccessor) != 0) {
+ parser_->ReportMessageAt(loc, "accessor_get_set",
+ Vector<const char*>::empty());
+ *ok = false;
+ return;
+ }
+
+ // Update map
+ entry->value = reinterpret_cast<void*> (prev | curr);
+ *ok = true;
+}
+
+
+void Parser::BuildObjectLiteralConstantProperties(
+ ZoneList<ObjectLiteral::Property*>* properties,
+ Handle<FixedArray> constant_properties,
+ bool* is_simple,
+ bool* fast_elements,
+ int* depth,
+ bool* may_store_doubles) {
+ int position = 0;
+ // Accumulate the value in local variables and store it at the end.
+ bool is_simple_acc = true;
+ int depth_acc = 1;
+ uint32_t max_element_index = 0;
+ uint32_t elements = 0;
+ for (int i = 0; i < properties->length(); i++) {
+ ObjectLiteral::Property* property = properties->at(i);
+ if (!IsBoilerplateProperty(property)) {
+ is_simple_acc = false;
+ continue;
+ }
+ MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral();
+ if (m_literal != NULL && m_literal->depth() >= depth_acc) {
+ depth_acc = m_literal->depth() + 1;
+ }
+
+ // Add CONSTANT and COMPUTED properties to boilerplate. Use undefined
+ // value for COMPUTED properties, the real value is filled in at
+ // runtime. The enumeration order is maintained.
+ Handle<Object> key = property->key()->value();
+ Handle<Object> value = GetBoilerplateValue(property->value());
+
+ // Ensure objects that may, at any point in time, contain fields with double
+ // representation are always treated as nested objects. This is true for
+ // computed fields (value is undefined), and smi and double literals
+ // (value->IsNumber()).
+ // TODO(verwaest): Remove once we can store them inline.
+ if (FLAG_track_double_fields &&
+ (value->IsNumber() || value->IsUninitialized())) {
+ *may_store_doubles = true;
+ }
+
+ is_simple_acc = is_simple_acc && !value->IsUninitialized();
+
+ // Keep track of the number of elements in the object literal and
+ // the largest element index. If the largest element index is
+ // much larger than the number of elements, creating an object
+ // literal with fast elements will be a waste of space.
+ uint32_t element_index = 0;
+ if (key->IsString()
+ && Handle<String>::cast(key)->AsArrayIndex(&element_index)
+ && element_index > max_element_index) {
+ max_element_index = element_index;
+ elements++;
+ } else if (key->IsSmi()) {
+ int key_value = Smi::cast(*key)->value();
+ if (key_value > 0
+ && static_cast<uint32_t>(key_value) > max_element_index) {
+ max_element_index = key_value;
+ }
+ elements++;
+ }
+
+ // Add name, value pair to the fixed array.
+ constant_properties->set(position++, *key);
+ constant_properties->set(position++, *value);
+ }
+ *fast_elements =
+ (max_element_index <= 32) || ((2 * elements) >= max_element_index);
+ *is_simple = is_simple_acc;
+ *depth = depth_acc;
+}
+
+
+ObjectLiteral::Property* Parser::ParseObjectLiteralGetSet(bool is_getter,
+ bool* ok) {
+ // Special handling of getter and setter syntax:
+ // { ... , get foo() { ... }, ... , set foo(v) { ... v ... } , ... }
+ // We have already read the "get" or "set" keyword.
+ Token::Value next = Next();
+ bool is_keyword = Token::IsKeyword(next);
+ if (next == Token::IDENTIFIER || next == Token::NUMBER ||
+ next == Token::FUTURE_RESERVED_WORD ||
+ next == Token::FUTURE_STRICT_RESERVED_WORD ||
+ next == Token::STRING || is_keyword) {
+ Handle<String> name;
+ if (is_keyword) {
+ name = isolate_->factory()->InternalizeUtf8String(Token::String(next));
+ } else {
+ name = GetSymbol();
+ }
+ FunctionLiteral* value =
+ ParseFunctionLiteral(name,
+ false, // reserved words are allowed here
+ false, // not a generator
+ RelocInfo::kNoPosition,
+ FunctionLiteral::ANONYMOUS_EXPRESSION,
+ CHECK_OK);
+ // Allow any number of parameters for compatibilty with JSC.
+ // Specification only allows zero parameters for get and one for set.
+ return factory()->NewObjectLiteralProperty(is_getter, value);
+ } else {
+ ReportUnexpectedToken(next);
+ *ok = false;
+ return NULL;
+ }
+}
+
+
+Expression* Parser::ParseObjectLiteral(bool* ok) {
+ // ObjectLiteral ::
+ // '{' (
+ // ((IdentifierName | String | Number) ':' AssignmentExpression)
+ // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral)
+ // )*[','] '}'
+
+ ZoneList<ObjectLiteral::Property*>* properties =
+ new(zone()) ZoneList<ObjectLiteral::Property*>(4, zone());
+ int number_of_boilerplate_properties = 0;
+ bool has_function = false;
+
+ ObjectLiteralPropertyChecker checker(this, top_scope_->language_mode());
+
+ Expect(Token::LBRACE, CHECK_OK);
+
+ while (peek() != Token::RBRACE) {
+ if (fni_ != NULL) fni_->Enter();
+
+ Literal* key = NULL;
+ Token::Value next = peek();
+
+ // Location of the property name token
+ Scanner::Location loc = scanner().peek_location();
+
+ switch (next) {
+ case Token::FUTURE_RESERVED_WORD:
+ case Token::FUTURE_STRICT_RESERVED_WORD:
+ case Token::IDENTIFIER: {
+ bool is_getter = false;
+ bool is_setter = false;
+ Handle<String> id =
+ ParseIdentifierNameOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
+ if (fni_ != NULL) fni_->PushLiteralName(id);
+
+ if ((is_getter || is_setter) && peek() != Token::COLON) {
+ // Update loc to point to the identifier
+ loc = scanner().peek_location();
+ ObjectLiteral::Property* property =
+ ParseObjectLiteralGetSet(is_getter, CHECK_OK);
+ if (IsBoilerplateProperty(property)) {
+ number_of_boilerplate_properties++;
+ }
+ // Validate the property.
+ checker.CheckProperty(property, loc, CHECK_OK);
+ properties->Add(property, zone());
+ if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK);
+
+ if (fni_ != NULL) {
+ fni_->Infer();
+ fni_->Leave();
+ }
+ continue; // restart the while
+ }
+ // Failed to parse as get/set property, so it's just a property
+ // called "get" or "set".
+ key = factory()->NewLiteral(id);
+ break;
+ }
+ case Token::STRING: {
+ Consume(Token::STRING);
+ Handle<String> string = GetSymbol();
+ if (fni_ != NULL) fni_->PushLiteralName(string);
+ uint32_t index;
+ if (!string.is_null() && string->AsArrayIndex(&index)) {
+ key = factory()->NewNumberLiteral(index);
+ break;
+ }
+ key = factory()->NewLiteral(string);
+ break;
+ }
+ case Token::NUMBER: {
+ Consume(Token::NUMBER);
+ ASSERT(scanner().is_literal_ascii());
+ double value = StringToDouble(isolate()->unicode_cache(),
+ scanner().literal_ascii_string(),
+ ALLOW_HEX | ALLOW_OCTAL |
+ ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY);
+ key = factory()->NewNumberLiteral(value);
+ break;
+ }
+ default:
+ if (Token::IsKeyword(next)) {
+ Consume(next);
+ Handle<String> string = GetSymbol();
+ key = factory()->NewLiteral(string);
+ } else {
+ // Unexpected token.
+ Token::Value next = Next();
+ ReportUnexpectedToken(next);
+ *ok = false;
+ return NULL;
+ }
+ }
+
+ Expect(Token::COLON, CHECK_OK);
+ Expression* value = ParseAssignmentExpression(true, CHECK_OK);
+
+ ObjectLiteral::Property* property =
+ new(zone()) ObjectLiteral::Property(key, value, isolate());
+
+ // 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();
+ }
+
+ // Count CONSTANT or COMPUTED properties to maintain the enumeration order.
+ if (IsBoilerplateProperty(property)) number_of_boilerplate_properties++;
+ // Validate the property
+ checker.CheckProperty(property, loc, CHECK_OK);
+ properties->Add(property, zone());
+
+ // TODO(1240767): Consider allowing trailing comma.
+ if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK);
+
+ if (fni_ != NULL) {
+ fni_->Infer();
+ fni_->Leave();
+ }
+ }
+ Expect(Token::RBRACE, CHECK_OK);
+
+ // Computation of literal_index must happen before pre parse bailout.
+ int literal_index = current_function_state_->NextMaterializedLiteralIndex();
+
+ Handle<FixedArray> constant_properties = isolate()->factory()->NewFixedArray(
+ number_of_boilerplate_properties * 2, TENURED);
+
+ bool is_simple = true;
+ bool fast_elements = true;
+ int depth = 1;
+ bool may_store_doubles = false;
+ BuildObjectLiteralConstantProperties(properties,
+ constant_properties,
+ &is_simple,
+ &fast_elements,
+ &depth,
+ &may_store_doubles);
+ return factory()->NewObjectLiteral(constant_properties,
+ properties,
+ literal_index,
+ is_simple,
+ fast_elements,
+ depth,
+ may_store_doubles,
+ has_function);
+}
+
+
+Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) {
+ if (!scanner().ScanRegExpPattern(seen_equal)) {
+ Next();
+ ReportMessage("unterminated_regexp", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+
+ int literal_index = current_function_state_->NextMaterializedLiteralIndex();
+
+ Handle<String> js_pattern = NextLiteralString(TENURED);
+ scanner().ScanRegExpFlags();
+ Handle<String> js_flags = NextLiteralString(TENURED);
+ Next();
+
+ return factory()->NewRegExpLiteral(js_pattern, js_flags, literal_index);
+}
+
+
+ZoneList<Expression*>* Parser::ParseArguments(bool* ok) {
+ // Arguments ::
+ // '(' (AssignmentExpression)*[','] ')'
+
+ ZoneList<Expression*>* result = new(zone()) ZoneList<Expression*>(4, zone());
+ Expect(Token::LPAREN, CHECK_OK);
+ bool done = (peek() == Token::RPAREN);
+ while (!done) {
+ Expression* argument = ParseAssignmentExpression(true, CHECK_OK);
+ result->Add(argument, zone());
+ if (result->length() > Code::kMaxArguments) {
+ ReportMessageAt(scanner().location(), "too_many_arguments",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ done = (peek() == Token::RPAREN);
+ if (!done) Expect(Token::COMMA, CHECK_OK);
+ }
+ Expect(Token::RPAREN, CHECK_OK);
+ return result;
+}
+
+
+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 LogUtf16Symbol(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) {
+ if (has_error_) return;
+ 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,
+ bool is_generator,
+ int function_token_position,
+ FunctionLiteral::FunctionType function_type,
+ bool* ok) {
+ // Function ::
+ // '(' FormalParameterList? ')' '{' FunctionBody '}'
+
+ // Anonymous functions were passed either the empty symbol or a null
+ // handle as the function name. Remember if we were passed a non-empty
+ // handle to decide whether to invoke function name inference.
+ bool should_infer_name = function_name.is_null();
+
+ // We want a non-null handle as the function name.
+ if (should_infer_name) {
+ function_name = isolate()->factory()->empty_string();
+ }
+
+ int num_parameters = 0;
+ // 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 =
+ (function_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;
+ FunctionLiteral::ParameterFlag duplicate_parameters =
+ FunctionLiteral::kNoDuplicateParameters;
+ FunctionLiteral::IsParenthesizedFlag parenthesized = parenthesized_function_
+ ? FunctionLiteral::kIsParenthesized
+ : FunctionLiteral::kNotParenthesized;
+ FunctionLiteral::IsGeneratorFlag generator = is_generator
+ ? FunctionLiteral::kIsGenerator
+ : FunctionLiteral::kNotGenerator;
+ AstProperties ast_properties;
+ // Parse function body.
+ { FunctionState function_state(this, scope, isolate());
+ top_scope_->SetScopeName(function_name);
+
+ if (is_generator) {
+ // For generators, allocating variables in contexts is currently a win
+ // because it minimizes the work needed to suspend and resume an
+ // activation.
+ top_scope_->ForceContextAllocation();
+
+ // Calling a generator returns a generator object. That object is stored
+ // in a temporary variable, a definition that is used by "yield"
+ // expressions. Presence of a variable for the generator object in the
+ // FunctionState indicates that this function is a generator.
+ Handle<String> tempname = isolate()->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR(".generator_object"));
+ Variable* temp = top_scope_->DeclarationScope()->NewTemporary(tempname);
+ function_state.set_generator_object_variable(temp);
+ }
+
+ // FormalParameterList ::
+ // '(' (Identifier)*[','] ')'
+ Expect(Token::LPAREN, CHECK_OK);
+ 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();
+
+ bool done = (peek() == Token::RPAREN);
+ while (!done) {
+ bool is_strict_reserved = false;
+ Handle<String> param_name =
+ ParseIdentifierOrStrictReservedWord(&is_strict_reserved,
+ CHECK_OK);
+
+ // Store locations for possible future error reports.
+ if (!name_loc.IsValid() && IsEvalOrArguments(param_name)) {
+ name_loc = scanner().location();
+ }
+ if (!dupe_loc.IsValid() && top_scope_->IsDeclared(param_name)) {
+ duplicate_parameters = FunctionLiteral::kHasDuplicateParameters;
+ dupe_loc = scanner().location();
+ }
+ if (!reserved_loc.IsValid() && is_strict_reserved) {
+ reserved_loc = scanner().location();
+ }
+
+ top_scope_->DeclareParameter(param_name, VAR);
+ num_parameters++;
+ if (num_parameters > Code::kMaxArguments) {
+ ReportMessageAt(scanner().location(), "too_many_parameters",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ done = (peek() == Token::RPAREN);
+ if (!done) Expect(Token::COMMA, CHECK_OK);
+ }
+ Expect(Token::RPAREN, CHECK_OK);
+
+ Expect(Token::LBRACE, CHECK_OK);
+
+ // If we have a named function expression, we add a local variable
+ // declaration to the body of the function with the name of the
+ // function and let it refer to the function itself (closure).
+ // 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 (function_type == FunctionLiteral::NAMED_EXPRESSION) {
+ if (is_extended_mode()) fvar_init_op = Token::INIT_CONST_HARMONY;
+ VariableMode fvar_mode = is_extended_mode() ? CONST_HARMONY : CONST;
+ fvar = new(zone()) Variable(top_scope_,
+ function_name, fvar_mode, true /* is valid LHS */,
+ Variable::NORMAL, kCreatedInitialized, Interface::NewConst());
+ VariableProxy* proxy = factory()->NewVariableProxy(fvar);
+ VariableDeclaration* fvar_declaration =
+ factory()->NewVariableDeclaration(proxy, fvar_mode, top_scope_);
+ top_scope_->DeclareFunctionVar(fvar_declaration);
+ }
+
+ // 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 allow lazy compilation of inner functions.
+ // - 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_->AllowsLazyCompilation() &&
+ !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;
+ if (pre_parse_data_ != NULL) {
+ // If we have pre_parse_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_parse_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());
+ } else {
+ is_lazily_compiled = false;
+ }
+ } else {
+ // 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(
+ scope->end_position() - function_block_pos);
+ materialized_literal_count = logger.literals();
+ expected_property_count = logger.properties();
+ top_scope_->SetLanguageMode(logger.language_mode());
+ }
+ }
+
+ if (!is_lazily_compiled) {
+ ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
+ body = new(zone()) ZoneList<Statement*>(8, zone());
+ if (fvar != NULL) {
+ VariableProxy* fproxy = top_scope_->NewUnresolved(
+ factory(), function_name, Interface::NewConst());
+ fproxy->BindTo(fvar);
+ body->Add(factory()->NewExpressionStatement(
+ factory()->NewAssignment(fvar_init_op,
+ fproxy,
+ factory()->NewThisFunction(),
+ RelocInfo::kNoPosition)),
+ zone());
+ }
+
+ // For generators, allocate and yield an iterator on function entry.
+ if (is_generator) {
+ ZoneList<Expression*>* arguments =
+ new(zone()) ZoneList<Expression*>(0, zone());
+ CallRuntime* allocation = factory()->NewCallRuntime(
+ isolate()->factory()->empty_string(),
+ Runtime::FunctionForId(Runtime::kCreateJSGeneratorObject),
+ arguments);
+ VariableProxy* init_proxy = factory()->NewVariableProxy(
+ current_function_state_->generator_object_variable());
+ Assignment* assignment = factory()->NewAssignment(
+ Token::INIT_VAR, init_proxy, allocation, RelocInfo::kNoPosition);
+ VariableProxy* get_proxy = factory()->NewVariableProxy(
+ current_function_state_->generator_object_variable());
+ Yield* yield = factory()->NewYield(
+ get_proxy, assignment, Yield::INITIAL, RelocInfo::kNoPosition);
+ body->Add(factory()->NewExpressionStatement(yield), zone());
+ }
+
+ ParseSourceElements(body, Token::RBRACE, false, false, CHECK_OK);
+
+ if (is_generator) {
+ VariableProxy* get_proxy = factory()->NewVariableProxy(
+ current_function_state_->generator_object_variable());
+ Expression *undefined = factory()->NewLiteral(
+ isolate()->factory()->undefined_value());
+ Yield* yield = factory()->NewYield(
+ get_proxy, undefined, Yield::FINAL, RelocInfo::kNoPosition);
+ body->Add(factory()->NewExpressionStatement(yield), zone());
+ }
+
+ materialized_literal_count = function_state.materialized_literal_count();
+ expected_property_count = function_state.expected_property_count();
+ handler_count = function_state.handler_count();
+
+ Expect(Token::RBRACE, CHECK_OK);
+ scope->set_end_position(scanner().location().end_pos);
+ }
+
+ // Validate 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);
+ Scanner::Location location = Scanner::Location(position, start_pos);
+ ReportMessageAt(location,
+ "strict_function_name", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ if (name_loc.IsValid()) {
+ ReportMessageAt(name_loc, "strict_param_name",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ if (dupe_loc.IsValid()) {
+ ReportMessageAt(dupe_loc, "strict_param_dupe",
+ Vector<const char*>::empty());
+ *ok = false;
+ 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);
+ Scanner::Location location = Scanner::Location(position, start_pos);
+ ReportMessageAt(location, "strict_reserved_word",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ if (reserved_loc.IsValid()) {
+ ReportMessageAt(reserved_loc, "strict_reserved_word",
+ Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ CheckOctalLiteral(scope->start_position(),
+ scope->end_position(),
+ CHECK_OK);
+ }
+ ast_properties = *factory()->visitor()->ast_properties();
+ }
+
+ if (is_extended_mode()) {
+ CheckConflictingVarDeclarations(scope, CHECK_OK);
+ }
+
+ FunctionLiteral* function_literal =
+ factory()->NewFunctionLiteral(function_name,
+ scope,
+ body,
+ materialized_literal_count,
+ expected_property_count,
+ handler_count,
+ num_parameters,
+ duplicate_parameters,
+ function_type,
+ FunctionLiteral::kIsFunction,
+ parenthesized,
+ generator);
+ 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();
+ reusable_preparser_ = new preparser::PreParser(&scanner_,
+ NULL,
+ stack_limit);
+ reusable_preparser_->set_allow_harmony_scoping(allow_harmony_scoping());
+ reusable_preparser_->set_allow_modules(allow_modules());
+ reusable_preparser_->set_allow_natives_syntax(allow_natives_syntax());
+ reusable_preparser_->set_allow_lazy(true);
+ reusable_preparser_->set_allow_generators(allow_generators());
+ reusable_preparser_->set_allow_for_of(allow_for_of());
+ reusable_preparser_->set_allow_harmony_numeric_literals(
+ allow_harmony_numeric_literals());
+ }
+ preparser::PreParser::PreParseResult result =
+ reusable_preparser_->PreParseLazyFunction(top_scope_->language_mode(),
+ is_generator(),
+ logger);
+ return result;
+}
+
+
+Expression* Parser::ParseV8Intrinsic(bool* ok) {
+ // CallRuntime ::
+ // '%' Identifier Arguments
+
+ Expect(Token::MOD, CHECK_OK);
+ Handle<String> name = ParseIdentifier(CHECK_OK);
+ ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
+
+ if (extension_ != NULL) {
+ // The extension structures are only accessible while parsing the
+ // very first time not when reparsing because of lazy compilation.
+ top_scope_->DeclarationScope()->ForceEagerCompilation();
+ }
+
+ const Runtime::Function* function = Runtime::FunctionForName(name);
+
+ // Check for built-in IS_VAR macro.
+ if (function != NULL &&
+ function->intrinsic_type == Runtime::RUNTIME &&
+ function->function_id == Runtime::kIS_VAR) {
+ // %IS_VAR(x) evaluates to x if x is a variable,
+ // leads to a parse error otherwise. Could be implemented as an
+ // inline function %_IS_VAR(x) to eliminate this special case.
+ if (args->length() == 1 && args->at(0)->AsVariableProxy() != NULL) {
+ return args->at(0);
+ } else {
+ ReportMessage("not_isvar", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+ }
+
+ // Check that the expected number of arguments are being passed.
+ if (function != NULL &&
+ function->nargs != -1 &&
+ function->nargs != args->length()) {
+ ReportMessage("illegal_access", Vector<const char*>::empty());
+ *ok = false;
+ return NULL;
+ }
+
+ // Check that the function is defined if it's an inline runtime call.
+ if (function == NULL && name->Get(0) == '_') {
+ ReportMessage("not_defined", Vector<Handle<String> >(&name, 1));
+ *ok = false;
+ return NULL;
+ }
+
+ // We have a valid intrinsics call or a call to a builtin.
+ return factory()->NewCallRuntime(name, function, args);
+}
+
+
+bool Parser::peek_any_identifier() {
+ Token::Value next = peek();
+ return next == Token::IDENTIFIER ||
+ next == Token::FUTURE_RESERVED_WORD ||
+ next == Token::FUTURE_STRICT_RESERVED_WORD ||
+ next == Token::YIELD;
+}
+
+
+void Parser::Consume(Token::Value token) {
+ Token::Value next = Next();
+ USE(next);
+ USE(token);
+ ASSERT(next == token);
+}
+
+
+void Parser::Expect(Token::Value token, bool* ok) {
+ Token::Value next = Next();
+ if (next == token) return;
+ ReportUnexpectedToken(next);
+ *ok = false;
+}
+
+
+bool Parser::Check(Token::Value token) {
+ Token::Value next = peek();
+ if (next == token) {
+ Consume(next);
+ return true;
+ }
+ return false;
+}
+
+
+bool Parser::CheckContextualKeyword(Vector<const char> keyword) {
+ if (peek() == Token::IDENTIFIER &&
+ scanner().is_next_contextual_keyword(keyword)) {
+ Consume(Token::IDENTIFIER);
+ return true;
+ }
+ return false;
+}
+
+
+void Parser::ExpectSemicolon(bool* ok) {
+ // Check for automatic semicolon insertion according to
+ // the rules given in ECMA-262, section 7.9, page 21.
+ Token::Value tok = peek();
+ if (tok == Token::SEMICOLON) {
+ Next();
+ return;
+ }
+ if (scanner().HasAnyLineTerminatorBeforeNext() ||
+ tok == Token::RBRACE ||
+ tok == Token::EOS) {
+ return;
+ }
+ Expect(Token::SEMICOLON, ok);
+}
+
+
+void Parser::ExpectContextualKeyword(Vector<const char> keyword, bool* ok) {
+ Expect(Token::IDENTIFIER, ok);
+ if (!*ok) return;
+ if (!scanner().is_literal_contextual_keyword(keyword)) {
+ *ok = false;
+ ReportUnexpectedToken(scanner().current_token());
+ }
+}
+
+
+Literal* Parser::GetLiteralUndefined() {
+ return factory()->NewLiteral(isolate()->factory()->undefined_value());
+}
+
+
+Literal* Parser::GetLiteralTheHole() {
+ 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) {
+ Token::Value next = Next();
+ if (next == Token::IDENTIFIER ||
+ (top_scope_->is_classic_mode() &&
+ (next == Token::FUTURE_STRICT_RESERVED_WORD ||
+ (next == Token::YIELD && !is_generator())))) {
+ return GetSymbol();
+ } else {
+ ReportUnexpectedToken(next);
+ *ok = false;
+ return Handle<String>();
+ }
+}
+
+
+// Parses and identifier or a strict mode future reserved word, and indicate
+// whether it is strict mode future reserved.
+Handle<String> Parser::ParseIdentifierOrStrictReservedWord(
+ bool* is_strict_reserved, bool* ok) {
+ Token::Value next = Next();
+ if (next == Token::IDENTIFIER) {
+ *is_strict_reserved = false;
+ } else if (next == Token::FUTURE_STRICT_RESERVED_WORD ||
+ (next == Token::YIELD && !is_generator())) {
+ *is_strict_reserved = true;
+ } else {
+ ReportUnexpectedToken(next);
+ *ok = false;
+ return Handle<String>();
+ }
+ return GetSymbol();
+}
+
+
+Handle<String> Parser::ParseIdentifierName(bool* ok) {
+ Token::Value next = Next();
+ if (next != Token::IDENTIFIER &&
+ next != Token::FUTURE_RESERVED_WORD &&
+ next != Token::FUTURE_STRICT_RESERVED_WORD &&
+ !Token::IsKeyword(next)) {
+ ReportUnexpectedToken(next);
+ *ok = false;
+ return Handle<String>();
+ }
+ return GetSymbol();
+}
+
+
+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_classic_mode());
+ VariableProxy* lhs = expression != NULL
+ ? expression->AsVariableProxy()
+ : NULL;
+
+ if (lhs != NULL && !lhs->is_this() && IsEvalOrArguments(lhs->name())) {
+ ReportMessage(error, Vector<const char*>::empty());
+ *ok = false;
+ }
+}
+
+
+// Checks whether an octal literal was last seen between beg_pos and end_pos.
+// If so, reports an error. Only called for strict mode.
+void Parser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) {
+ Scanner::Location octal = scanner().octal_position();
+ if (octal.IsValid() &&
+ beg_pos <= octal.beg_pos &&
+ octal.end_pos <= end_pos) {
+ ReportMessageAt(octal, "strict_octal_literal",
+ Vector<const char*>::empty());
+ scanner().clear_octal_position();
+ *ok = false;
+ }
+}
+
+
+void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
+ Declaration* decl = scope->CheckConflictingVarDeclarations();
+ if (decl != NULL) {
+ // In harmony mode we treat conflicting variable bindinds as early
+ // errors. See ES5 16 for a definition of early errors.
+ Handle<String> name = decl->proxy()->name();
+ SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS);
+ const char* elms[2] = { "Variable", *c_string };
+ Vector<const char*> args(elms, 2);
+ int position = decl->proxy()->position();
+ Scanner::Location location = position == RelocInfo::kNoPosition
+ ? Scanner::Location::invalid()
+ : Scanner::Location(position, position + 1);
+ ReportMessageAt(location, "redeclaration", args);
+ *ok = false;
+ }
+}
+
+
+// This function reads an identifier name and determines whether or not it
+// is 'get' or 'set'.
+Handle<String> Parser::ParseIdentifierNameOrGetOrSet(bool* is_get,
+ bool* is_set,
+ bool* ok) {
+ Handle<String> result = ParseIdentifierName(ok);
+ if (!*ok) return Handle<String>();
+ if (scanner().is_literal_ascii() && scanner().literal_length() == 3) {
+ const char* token = scanner().literal_ascii_string().start();
+ *is_get = strncmp(token, "get", 3) == 0;
+ *is_set = !*is_get && strncmp(token, "set", 3) == 0;
+ }
+ return result;
+}
+
+
+// ----------------------------------------------------------------------------
+// Parser support
+
+
+bool Parser::TargetStackContainsLabel(Handle<String> label) {
+ for (Target* t = target_stack_; t != NULL; t = t->previous()) {
+ BreakableStatement* stat = t->node()->AsBreakableStatement();
+ if (stat != NULL && ContainsLabel(stat->labels(), label))
+ return true;
+ }
+ return false;
+}
+
+
+BreakableStatement* Parser::LookupBreakTarget(Handle<String> label, bool* ok) {
+ bool anonymous = label.is_null();
+ for (Target* t = target_stack_; t != NULL; t = t->previous()) {
+ BreakableStatement* stat = t->node()->AsBreakableStatement();
+ if (stat == NULL) continue;
+ if ((anonymous && stat->is_target_for_anonymous()) ||
+ (!anonymous && ContainsLabel(stat->labels(), label))) {
+ RegisterTargetUse(stat->break_target(), t->previous());
+ return stat;
+ }
+ }
+ return NULL;
+}
+
+
+IterationStatement* Parser::LookupContinueTarget(Handle<String> label,
+ bool* ok) {
+ bool anonymous = label.is_null();
+ for (Target* t = target_stack_; t != NULL; t = t->previous()) {
+ IterationStatement* stat = t->node()->AsIterationStatement();
+ if (stat == NULL) continue;
+
+ ASSERT(stat->is_target_for_anonymous());
+ if (anonymous || ContainsLabel(stat->labels(), label)) {
+ RegisterTargetUse(stat->continue_target(), t->previous());
+ return stat;
+ }
+ }
+ return NULL;
+}
+
+
+void Parser::RegisterTargetUse(Label* target, Target* stop) {
+ // Register that a break target found at the given stop in the
+ // target stack has been used from the top of the target stack. Add
+ // the break target to any TargetCollectors passed on the stack.
+ for (Target* t = target_stack_; t != stop; t = t->previous()) {
+ TargetCollector* collector = t->node()->AsTargetCollector();
+ if (collector != NULL) collector->AddTarget(target, zone());
+ }
+}
+
+
+Expression* Parser::NewThrowReferenceError(Handle<String> message) {
+ return NewThrowError(isolate()->factory()->MakeReferenceError_string(),
+ message, HandleVector<Object>(NULL, 0));
+}
+
+
+Expression* Parser::NewThrowSyntaxError(Handle<String> message,
+ Handle<Object> first) {
+ int argc = first.is_null() ? 0 : 1;
+ Vector< Handle<Object> > arguments = HandleVector<Object>(&first, argc);
+ return NewThrowError(
+ isolate()->factory()->MakeSyntaxError_string(), message, arguments);
+}
+
+
+Expression* Parser::NewThrowTypeError(Handle<String> message,
+ Handle<Object> first,
+ Handle<Object> second) {
+ ASSERT(!first.is_null() && !second.is_null());
+ Handle<Object> elements[] = { first, second };
+ Vector< Handle<Object> > arguments =
+ HandleVector<Object>(elements, ARRAY_SIZE(elements));
+ return NewThrowError(
+ isolate()->factory()->MakeTypeError_string(), message, arguments);
+}
+
+
+Expression* Parser::NewThrowError(Handle<String> constructor,
+ Handle<String> message,
+ Vector< Handle<Object> > arguments) {
+ int argc = arguments.length();
+ Handle<FixedArray> elements = isolate()->factory()->NewFixedArray(argc,
+ TENURED);
+ for (int i = 0; i < argc; i++) {
+ Handle<Object> element = arguments[i];
+ if (!element.is_null()) {
+ elements->set(i, *element);
+ }
+ }
+ Handle<JSArray> array = isolate()->factory()->NewJSArrayWithElements(
+ elements, FAST_ELEMENTS, TENURED);
+
+ ZoneList<Expression*>* args = new(zone()) ZoneList<Expression*>(2, zone());
+ args->Add(factory()->NewLiteral(message), zone());
+ args->Add(factory()->NewLiteral(array), zone());
+ CallRuntime* call_constructor =
+ factory()->NewCallRuntime(constructor, NULL, args);
+ return factory()->NewThrow(call_constructor, scanner().location().beg_pos);
+}
+
+
+// ----------------------------------------------------------------------------
+// Regular expressions
+
+
+RegExpParser::RegExpParser(FlatStringReader* in,
+ Handle<String>* error,
+ bool multiline,
+ Zone* zone)
+ : isolate_(Isolate::Current()),
+ zone_(zone),
+ error_(error),
+ captures_(NULL),
+ in_(in),
+ current_(kEndMarker),
+ next_pos_(0),
+ capture_count_(0),
+ has_more_(true),
+ multiline_(multiline),
+ simple_(false),
+ contains_anchor_(false),
+ is_scanned_for_captures_(false),
+ failed_(false) {
+ Advance();
+}
+
+
+uc32 RegExpParser::Next() {
+ if (has_next()) {
+ return in()->Get(next_pos_);
+ } else {
+ return kEndMarker;
+ }
+}
+
+
+void RegExpParser::Advance() {
+ if (next_pos_ < in()->length()) {
+ StackLimitCheck check(isolate());
+ if (check.HasOverflowed()) {
+ ReportError(CStrVector(Isolate::kStackOverflowMessage));
+ } else if (zone()->excess_allocation()) {
+ ReportError(CStrVector("Regular expression too large"));
+ } else {
+ current_ = in()->Get(next_pos_);
+ next_pos_++;
+ }
+ } else {
+ current_ = kEndMarker;
+ has_more_ = false;
+ }
+}
+
+
+void RegExpParser::Reset(int pos) {
+ next_pos_ = pos;
+ has_more_ = (pos < in()->length());
+ Advance();
+}
+
+
+void RegExpParser::Advance(int dist) {
+ next_pos_ += dist - 1;
+ Advance();
+}
+
+
+bool RegExpParser::simple() {
+ return simple_;
+}
+
+
+RegExpTree* RegExpParser::ReportError(Vector<const char> message) {
+ failed_ = true;
+ *error_ = isolate()->factory()->NewStringFromAscii(message, NOT_TENURED);
+ // Zip to the end to make sure the no more input is read.
+ current_ = kEndMarker;
+ next_pos_ = in()->length();
+ return NULL;
+}
+
+
+// Pattern ::
+// Disjunction
+RegExpTree* RegExpParser::ParsePattern() {
+ RegExpTree* result = ParseDisjunction(CHECK_FAILED);
+ ASSERT(!has_more());
+ // If the result of parsing is a literal string atom, and it has the
+ // same length as the input, then the atom is identical to the input.
+ if (result->IsAtom() && result->AsAtom()->length() == in()->length()) {
+ simple_ = true;
+ }
+ return result;
+}
+
+
+// Disjunction ::
+// Alternative
+// Alternative | Disjunction
+// Alternative ::
+// [empty]
+// Term Alternative
+// Term ::
+// Assertion
+// Atom
+// Atom Quantifier
+RegExpTree* RegExpParser::ParseDisjunction() {
+ // Used to store current state while parsing subexpressions.
+ RegExpParserState initial_state(NULL, INITIAL, 0, zone());
+ RegExpParserState* stored_state = &initial_state;
+ // Cache the builder in a local variable for quick access.
+ RegExpBuilder* builder = initial_state.builder();
+ while (true) {
+ switch (current()) {
+ case kEndMarker:
+ if (stored_state->IsSubexpression()) {
+ // Inside a parenthesized group when hitting end of input.
+ ReportError(CStrVector("Unterminated group") CHECK_FAILED);
+ }
+ ASSERT_EQ(INITIAL, stored_state->group_type());
+ // Parsing completed successfully.
+ return builder->ToRegExp();
+ case ')': {
+ if (!stored_state->IsSubexpression()) {
+ ReportError(CStrVector("Unmatched ')'") CHECK_FAILED);
+ }
+ ASSERT_NE(INITIAL, stored_state->group_type());
+
+ Advance();
+ // End disjunction parsing and convert builder content to new single
+ // regexp atom.
+ RegExpTree* body = builder->ToRegExp();
+
+ int end_capture_index = captures_started();
+
+ int capture_index = stored_state->capture_index();
+ SubexpressionType group_type = stored_state->group_type();
+
+ // Restore previous state.
+ stored_state = stored_state->previous_state();
+ builder = stored_state->builder();
+
+ // Build result of subexpression.
+ if (group_type == CAPTURE) {
+ RegExpCapture* capture = new(zone()) RegExpCapture(body, capture_index);
+ captures_->at(capture_index - 1) = capture;
+ body = capture;
+ } else if (group_type != GROUPING) {
+ ASSERT(group_type == POSITIVE_LOOKAHEAD ||
+ group_type == NEGATIVE_LOOKAHEAD);
+ bool is_positive = (group_type == POSITIVE_LOOKAHEAD);
+ body = new(zone()) RegExpLookahead(body,
+ is_positive,
+ end_capture_index - capture_index,
+ capture_index);
+ }
+ builder->AddAtom(body);
+ // For compatability with JSC and ES3, we allow quantifiers after
+ // lookaheads, and break in all cases.
+ break;
+ }
+ case '|': {
+ Advance();
+ builder->NewAlternative();
+ continue;
+ }
+ case '*':
+ case '+':
+ case '?':
+ return ReportError(CStrVector("Nothing to repeat"));
+ case '^': {
+ Advance();
+ if (multiline_) {
+ builder->AddAssertion(
+ new(zone()) RegExpAssertion(RegExpAssertion::START_OF_LINE));
+ } else {
+ builder->AddAssertion(
+ new(zone()) RegExpAssertion(RegExpAssertion::START_OF_INPUT));
+ set_contains_anchor();
+ }
+ continue;
+ }
+ case '$': {
+ Advance();
+ RegExpAssertion::AssertionType assertion_type =
+ multiline_ ? RegExpAssertion::END_OF_LINE :
+ RegExpAssertion::END_OF_INPUT;
+ builder->AddAssertion(new(zone()) RegExpAssertion(assertion_type));
+ continue;
+ }
+ case '.': {
+ Advance();
+ // everything except \x0a, \x0d, \u2028 and \u2029
+ ZoneList<CharacterRange>* ranges =
+ new(zone()) ZoneList<CharacterRange>(2, zone());
+ CharacterRange::AddClassEscape('.', ranges, zone());
+ RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false);
+ builder->AddAtom(atom);
+ break;
+ }
+ case '(': {
+ SubexpressionType subexpr_type = CAPTURE;
+ Advance();
+ if (current() == '?') {
+ switch (Next()) {
+ case ':':
+ subexpr_type = GROUPING;
+ break;
+ case '=':
+ subexpr_type = POSITIVE_LOOKAHEAD;
+ break;
+ case '!':
+ subexpr_type = NEGATIVE_LOOKAHEAD;
+ break;
+ default:
+ ReportError(CStrVector("Invalid group") CHECK_FAILED);
+ break;
+ }
+ Advance(2);
+ } else {
+ if (captures_ == NULL) {
+ captures_ = new(zone()) ZoneList<RegExpCapture*>(2, zone());
+ }
+ if (captures_started() >= kMaxCaptures) {
+ ReportError(CStrVector("Too many captures") CHECK_FAILED);
+ }
+ captures_->Add(NULL, zone());
+ }
+ // Store current state and begin new disjunction parsing.
+ stored_state = new(zone()) RegExpParserState(stored_state, subexpr_type,
+ captures_started(), zone());
+ builder = stored_state->builder();
+ continue;
+ }
+ case '[': {
+ RegExpTree* atom = ParseCharacterClass(CHECK_FAILED);
+ builder->AddAtom(atom);
+ break;
+ }
+ // Atom ::
+ // \ AtomEscape
+ case '\\':
+ switch (Next()) {
+ case kEndMarker:
+ return ReportError(CStrVector("\\ at end of pattern"));
+ case 'b':
+ Advance(2);
+ builder->AddAssertion(
+ new(zone()) RegExpAssertion(RegExpAssertion::BOUNDARY));
+ continue;
+ case 'B':
+ Advance(2);
+ builder->AddAssertion(
+ new(zone()) RegExpAssertion(RegExpAssertion::NON_BOUNDARY));
+ continue;
+ // AtomEscape ::
+ // CharacterClassEscape
+ //
+ // CharacterClassEscape :: one of
+ // d D s S w W
+ case 'd': case 'D': case 's': case 'S': case 'w': case 'W': {
+ uc32 c = Next();
+ Advance(2);
+ ZoneList<CharacterRange>* ranges =
+ new(zone()) ZoneList<CharacterRange>(2, zone());
+ CharacterRange::AddClassEscape(c, ranges, zone());
+ RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false);
+ builder->AddAtom(atom);
+ break;
+ }
+ case '1': case '2': case '3': case '4': case '5': case '6':
+ case '7': case '8': case '9': {
+ int index = 0;
+ if (ParseBackReferenceIndex(&index)) {
+ RegExpCapture* capture = NULL;
+ if (captures_ != NULL && index <= captures_->length()) {
+ capture = captures_->at(index - 1);
+ }
+ if (capture == NULL) {
+ builder->AddEmpty();
+ break;
+ }
+ RegExpTree* atom = new(zone()) RegExpBackReference(capture);
+ builder->AddAtom(atom);
+ break;
+ }
+ uc32 first_digit = Next();
+ if (first_digit == '8' || first_digit == '9') {
+ // Treat as identity escape
+ builder->AddCharacter(first_digit);
+ Advance(2);
+ break;
+ }
+ }
+ // FALLTHROUGH
+ case '0': {
+ Advance();
+ uc32 octal = ParseOctalLiteral();
+ builder->AddCharacter(octal);
+ break;
+ }
+ // ControlEscape :: one of
+ // f n r t v
+ case 'f':
+ Advance(2);
+ builder->AddCharacter('\f');
+ break;
+ case 'n':
+ Advance(2);
+ builder->AddCharacter('\n');
+ break;
+ case 'r':
+ Advance(2);
+ builder->AddCharacter('\r');
+ break;
+ case 't':
+ Advance(2);
+ builder->AddCharacter('\t');
+ break;
+ case 'v':
+ Advance(2);
+ builder->AddCharacter('\v');
+ break;
+ case 'c': {
+ Advance();
+ uc32 controlLetter = Next();
+ // Special case if it is an ASCII letter.
+ // Convert lower case letters to uppercase.
+ uc32 letter = controlLetter & ~('a' ^ 'A');
+ if (letter < 'A' || 'Z' < letter) {
+ // controlLetter is not in range 'A'-'Z' or 'a'-'z'.
+ // This is outside the specification. We match JSC in
+ // reading the backslash as a literal character instead
+ // of as starting an escape.
+ builder->AddCharacter('\\');
+ } else {
+ Advance(2);
+ builder->AddCharacter(controlLetter & 0x1f);
+ }
+ break;
+ }
+ case 'x': {
+ Advance(2);
+ uc32 value;
+ if (ParseHexEscape(2, &value)) {
+ builder->AddCharacter(value);
+ } else {
+ builder->AddCharacter('x');
+ }
+ break;
+ }
+ case 'u': {
+ Advance(2);
+ uc32 value;
+ if (ParseHexEscape(4, &value)) {
+ builder->AddCharacter(value);
+ } else {
+ builder->AddCharacter('u');
+ }
+ break;
+ }
+ default:
+ // Identity escape.
+ builder->AddCharacter(Next());
+ Advance(2);
+ break;
+ }
+ break;
+ case '{': {
+ int dummy;
+ if (ParseIntervalQuantifier(&dummy, &dummy)) {
+ ReportError(CStrVector("Nothing to repeat") CHECK_FAILED);
+ }
+ // fallthrough
+ }
+ default:
+ builder->AddCharacter(current());
+ Advance();
+ break;
+ } // end switch(current())
+
+ int min;
+ int max;
+ switch (current()) {
+ // QuantifierPrefix ::
+ // *
+ // +
+ // ?
+ // {
+ case '*':
+ min = 0;
+ max = RegExpTree::kInfinity;
+ Advance();
+ break;
+ case '+':
+ min = 1;
+ max = RegExpTree::kInfinity;
+ Advance();
+ break;
+ case '?':
+ min = 0;
+ max = 1;
+ Advance();
+ break;
+ case '{':
+ if (ParseIntervalQuantifier(&min, &max)) {
+ if (max < min) {
+ ReportError(CStrVector("numbers out of order in {} quantifier.")
+ CHECK_FAILED);
+ }
+ break;
+ } else {
+ continue;
+ }
+ default:
+ continue;
+ }
+ RegExpQuantifier::QuantifierType quantifier_type = RegExpQuantifier::GREEDY;
+ if (current() == '?') {
+ quantifier_type = RegExpQuantifier::NON_GREEDY;
+ Advance();
+ } else if (FLAG_regexp_possessive_quantifier && current() == '+') {
+ // FLAG_regexp_possessive_quantifier is a debug-only flag.
+ quantifier_type = RegExpQuantifier::POSSESSIVE;
+ Advance();
+ }
+ builder->AddQuantifierToAtom(min, max, quantifier_type);
+ }
+}
+
+
+#ifdef DEBUG
+// Currently only used in an ASSERT.
+static bool IsSpecialClassEscape(uc32 c) {
+ switch (c) {
+ case 'd': case 'D':
+ case 's': case 'S':
+ case 'w': case 'W':
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+
+// In order to know whether an escape is a backreference or not we have to scan
+// the entire regexp and find the number of capturing parentheses. However we
+// don't want to scan the regexp twice unless it is necessary. This mini-parser
+// is called when needed. It can see the difference between capturing and
+// noncapturing parentheses and can skip character classes and backslash-escaped
+// characters.
+void RegExpParser::ScanForCaptures() {
+ // Start with captures started previous to current position
+ int capture_count = captures_started();
+ // Add count of captures after this position.
+ int n;
+ while ((n = current()) != kEndMarker) {
+ Advance();
+ switch (n) {
+ case '\\':
+ Advance();
+ break;
+ case '[': {
+ int c;
+ while ((c = current()) != kEndMarker) {
+ Advance();
+ if (c == '\\') {
+ Advance();
+ } else {
+ if (c == ']') break;
+ }
+ }
+ break;
+ }
+ case '(':
+ if (current() != '?') capture_count++;
+ break;
+ }
+ }
+ capture_count_ = capture_count;
+ is_scanned_for_captures_ = true;
+}
+
+
+bool RegExpParser::ParseBackReferenceIndex(int* index_out) {
+ ASSERT_EQ('\\', current());
+ ASSERT('1' <= Next() && Next() <= '9');
+ // Try to parse a decimal literal that is no greater than the total number
+ // of left capturing parentheses in the input.
+ int start = position();
+ int value = Next() - '0';
+ Advance(2);
+ while (true) {
+ uc32 c = current();
+ if (IsDecimalDigit(c)) {
+ value = 10 * value + (c - '0');
+ if (value > kMaxCaptures) {
+ Reset(start);
+ return false;
+ }
+ Advance();
+ } else {
+ break;
+ }
+ }
+ if (value > captures_started()) {
+ if (!is_scanned_for_captures_) {
+ int saved_position = position();
+ ScanForCaptures();
+ Reset(saved_position);
+ }
+ if (value > capture_count_) {
+ Reset(start);
+ return false;
+ }
+ }
+ *index_out = value;
+ return true;
+}
+
+
+// QuantifierPrefix ::
+// { DecimalDigits }
+// { DecimalDigits , }
+// { DecimalDigits , DecimalDigits }
+//
+// Returns true if parsing succeeds, and set the min_out and max_out
+// values. Values are truncated to RegExpTree::kInfinity if they overflow.
+bool RegExpParser::ParseIntervalQuantifier(int* min_out, int* max_out) {
+ ASSERT_EQ(current(), '{');
+ int start = position();
+ Advance();
+ int min = 0;
+ if (!IsDecimalDigit(current())) {
+ Reset(start);
+ return false;
+ }
+ while (IsDecimalDigit(current())) {
+ int next = current() - '0';
+ if (min > (RegExpTree::kInfinity - next) / 10) {
+ // Overflow. Skip past remaining decimal digits and return -1.
+ do {
+ Advance();
+ } while (IsDecimalDigit(current()));
+ min = RegExpTree::kInfinity;
+ break;
+ }
+ min = 10 * min + next;
+ Advance();
+ }
+ int max = 0;
+ if (current() == '}') {
+ max = min;
+ Advance();
+ } else if (current() == ',') {
+ Advance();
+ if (current() == '}') {
+ max = RegExpTree::kInfinity;
+ Advance();
+ } else {
+ while (IsDecimalDigit(current())) {
+ int next = current() - '0';
+ if (max > (RegExpTree::kInfinity - next) / 10) {
+ do {
+ Advance();
+ } while (IsDecimalDigit(current()));
+ max = RegExpTree::kInfinity;
+ break;
+ }
+ max = 10 * max + next;
+ Advance();
+ }
+ if (current() != '}') {
+ Reset(start);
+ return false;
+ }
+ Advance();
+ }
+ } else {
+ Reset(start);
+ return false;
+ }
+ *min_out = min;
+ *max_out = max;
+ return true;
+}
+
+
+uc32 RegExpParser::ParseOctalLiteral() {
+ ASSERT('0' <= current() && current() <= '7');
+ // For compatibility with some other browsers (not all), we parse
+ // up to three octal digits with a value below 256.
+ uc32 value = current() - '0';
+ Advance();
+ if ('0' <= current() && current() <= '7') {
+ value = value * 8 + current() - '0';
+ Advance();
+ if (value < 32 && '0' <= current() && current() <= '7') {
+ value = value * 8 + current() - '0';
+ Advance();
+ }
+ }
+ return value;
+}
+
+
+bool RegExpParser::ParseHexEscape(int length, uc32 *value) {
+ int start = position();
+ uc32 val = 0;
+ bool done = false;
+ for (int i = 0; !done; i++) {
+ uc32 c = current();
+ int d = HexValue(c);
+ if (d < 0) {
+ Reset(start);
+ return false;
+ }
+ val = val * 16 + d;
+ Advance();
+ if (i == length - 1) {
+ done = true;
+ }
+ }
+ *value = val;
+ return true;
+}
+
+
+uc32 RegExpParser::ParseClassCharacterEscape() {
+ ASSERT(current() == '\\');
+ ASSERT(has_next() && !IsSpecialClassEscape(Next()));
+ Advance();
+ switch (current()) {
+ case 'b':
+ Advance();
+ return '\b';
+ // ControlEscape :: one of
+ // f n r t v
+ case 'f':
+ Advance();
+ return '\f';
+ case 'n':
+ Advance();
+ return '\n';
+ case 'r':
+ Advance();
+ return '\r';
+ case 't':
+ Advance();
+ return '\t';
+ case 'v':
+ Advance();
+ return '\v';
+ case 'c': {
+ uc32 controlLetter = Next();
+ uc32 letter = controlLetter & ~('A' ^ 'a');
+ // For compatibility with JSC, inside a character class
+ // we also accept digits and underscore as control characters.
+ if ((controlLetter >= '0' && controlLetter <= '9') ||
+ controlLetter == '_' ||
+ (letter >= 'A' && letter <= 'Z')) {
+ Advance(2);
+ // Control letters mapped to ASCII control characters in the range
+ // 0x00-0x1f.
+ return controlLetter & 0x1f;
+ }
+ // We match JSC in reading the backslash as a literal
+ // character instead of as starting an escape.
+ return '\\';
+ }
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7':
+ // For compatibility, we interpret a decimal escape that isn't
+ // a back reference (and therefore either \0 or not valid according
+ // to the specification) as a 1..3 digit octal character code.
+ return ParseOctalLiteral();
+ case 'x': {
+ Advance();
+ uc32 value;
+ if (ParseHexEscape(2, &value)) {
+ return value;
+ }
+ // If \x is not followed by a two-digit hexadecimal, treat it
+ // as an identity escape.
+ return 'x';
+ }
+ case 'u': {
+ Advance();
+ uc32 value;
+ if (ParseHexEscape(4, &value)) {
+ return value;
+ }
+ // If \u is not followed by a four-digit hexadecimal, treat it
+ // as an identity escape.
+ return 'u';
+ }
+ default: {
+ // Extended identity escape. We accept any character that hasn't
+ // been matched by a more specific case, not just the subset required
+ // by the ECMAScript specification.
+ uc32 result = current();
+ Advance();
+ return result;
+ }
+ }
+ return 0;
+}
+
+
+CharacterRange RegExpParser::ParseClassAtom(uc16* char_class) {
+ ASSERT_EQ(0, *char_class);
+ uc32 first = current();
+ if (first == '\\') {
+ switch (Next()) {
+ case 'w': case 'W': case 'd': case 'D': case 's': case 'S': {
+ *char_class = Next();
+ Advance(2);
+ return CharacterRange::Singleton(0); // Return dummy value.
+ }
+ case kEndMarker:
+ return ReportError(CStrVector("\\ at end of pattern"));
+ default:
+ uc32 c = ParseClassCharacterEscape(CHECK_FAILED);
+ return CharacterRange::Singleton(c);
+ }
+ } else {
+ Advance();
+ return CharacterRange::Singleton(first);
+ }
+}
+
+
+static const uc16 kNoCharClass = 0;
+
+// Adds range or pre-defined character class to character ranges.
+// If char_class is not kInvalidClass, it's interpreted as a class
+// escape (i.e., 's' means whitespace, from '\s').
+static inline void AddRangeOrEscape(ZoneList<CharacterRange>* ranges,
+ uc16 char_class,
+ CharacterRange range,
+ Zone* zone) {
+ if (char_class != kNoCharClass) {
+ CharacterRange::AddClassEscape(char_class, ranges, zone);
+ } else {
+ ranges->Add(range, zone);
+ }
+}
+
+
+RegExpTree* RegExpParser::ParseCharacterClass() {
+ static const char* kUnterminated = "Unterminated character class";
+ static const char* kRangeOutOfOrder = "Range out of order in character class";
+
+ ASSERT_EQ(current(), '[');
+ Advance();
+ bool is_negated = false;
+ if (current() == '^') {
+ is_negated = true;
+ Advance();
+ }
+ ZoneList<CharacterRange>* ranges =
+ new(zone()) ZoneList<CharacterRange>(2, zone());
+ while (has_more() && current() != ']') {
+ uc16 char_class = kNoCharClass;
+ CharacterRange first = ParseClassAtom(&char_class CHECK_FAILED);
+ if (current() == '-') {
+ Advance();
+ if (current() == kEndMarker) {
+ // If we reach the end we break out of the loop and let the
+ // following code report an error.
+ break;
+ } else if (current() == ']') {
+ AddRangeOrEscape(ranges, char_class, first, zone());
+ ranges->Add(CharacterRange::Singleton('-'), zone());
+ break;
+ }
+ uc16 char_class_2 = kNoCharClass;
+ CharacterRange next = ParseClassAtom(&char_class_2 CHECK_FAILED);
+ if (char_class != kNoCharClass || char_class_2 != kNoCharClass) {
+ // Either end is an escaped character class. Treat the '-' verbatim.
+ AddRangeOrEscape(ranges, char_class, first, zone());
+ ranges->Add(CharacterRange::Singleton('-'), zone());
+ AddRangeOrEscape(ranges, char_class_2, next, zone());
+ continue;
+ }
+ if (first.from() > next.to()) {
+ return ReportError(CStrVector(kRangeOutOfOrder) CHECK_FAILED);
+ }
+ ranges->Add(CharacterRange::Range(first.from(), next.to()), zone());
+ } else {
+ AddRangeOrEscape(ranges, char_class, first, zone());
+ }
+ }
+ if (!has_more()) {
+ return ReportError(CStrVector(kUnterminated) CHECK_FAILED);
+ }
+ Advance();
+ if (ranges->length() == 0) {
+ ranges->Add(CharacterRange::Everything(), zone());
+ is_negated = !is_negated;
+ }
+ return new(zone()) RegExpCharacterClass(ranges, is_negated);
+}
+
+
+// ----------------------------------------------------------------------------
+// The Parser interface.
+
+ParserMessage::~ParserMessage() {
+ for (int i = 0; i < args().length(); i++)
+ DeleteArray(args()[i]);
+ DeleteArray(args().start());
+}
+
+
+ScriptDataImpl::~ScriptDataImpl() {
+ if (owns_store_) store_.Dispose();
+}
+
+
+int ScriptDataImpl::Length() {
+ return store_.length() * sizeof(unsigned);
+}
+
+
+const char* ScriptDataImpl::Data() {
+ return reinterpret_cast<const char*>(store_.start());
+}
+
+
+bool ScriptDataImpl::HasError() {
+ return has_error();
+}
+
+
+void ScriptDataImpl::Initialize() {
+ // Prepares state for use.
+ if (store_.length() >= PreparseDataConstants::kHeaderSize) {
+ function_index_ = PreparseDataConstants::kHeaderSize;
+ int symbol_data_offset = PreparseDataConstants::kHeaderSize
+ + store_[PreparseDataConstants::kFunctionsSizeOffset];
+ if (store_.length() > symbol_data_offset) {
+ symbol_data_ = reinterpret_cast<byte*>(&store_[symbol_data_offset]);
+ } else {
+ // Partial preparse causes no symbol information.
+ symbol_data_ = reinterpret_cast<byte*>(&store_[0] + store_.length());
+ }
+ symbol_data_end_ = reinterpret_cast<byte*>(&store_[0] + store_.length());
+ }
+}
+
+
+int ScriptDataImpl::ReadNumber(byte** source) {
+ // Reads a number from symbol_data_ in base 128. The most significant
+ // bit marks that there are more digits.
+ // If the first byte is 0x80 (kNumberTerminator), it would normally
+ // represent a leading zero. Since that is useless, and therefore won't
+ // appear as the first digit of any actual value, it is used to
+ // mark the end of the input stream.
+ byte* data = *source;
+ if (data >= symbol_data_end_) return -1;
+ byte input = *data;
+ if (input == PreparseDataConstants::kNumberTerminator) {
+ // End of stream marker.
+ return -1;
+ }
+ int result = input & 0x7f;
+ data++;
+ while ((input & 0x80u) != 0) {
+ if (data >= symbol_data_end_) return -1;
+ input = *data;
+ result = (result << 7) | (input & 0x7f);
+ data++;
+ }
+ *source = data;
+ return result;
+}
+
+
+// Create a Scanner for the preparser to use as input, and preparse the source.
+ScriptDataImpl* PreParserApi::PreParse(Utf16CharacterStream* source) {
+ CompleteParserRecorder recorder;
+ Isolate* isolate = Isolate::Current();
+ HistogramTimerScope timer(isolate->counters()->pre_parse());
+ Scanner scanner(isolate->unicode_cache());
+ intptr_t stack_limit = isolate->stack_guard()->real_climit();
+ preparser::PreParser preparser(&scanner, &recorder, stack_limit);
+ preparser.set_allow_lazy(true);
+ preparser.set_allow_generators(FLAG_harmony_generators);
+ preparser.set_allow_for_of(FLAG_harmony_iteration);
+ preparser.set_allow_harmony_scoping(FLAG_harmony_scoping);
+ preparser.set_allow_harmony_numeric_literals(FLAG_harmony_numeric_literals);
+ scanner.Initialize(source);
+ preparser::PreParser::PreParseResult result = preparser.PreParseProgram();
+ if (result == preparser::PreParser::kPreParseStackOverflow) {
+ isolate->StackOverflow();
+ return NULL;
+ }
+
+ // Extract the accumulated data from the recorder as a single
+ // contiguous vector that we are responsible for disposing.
+ Vector<unsigned> store = recorder.ExtractData();
+ return new ScriptDataImpl(store);
+}
+
+
+bool RegExpParser::ParseRegExp(FlatStringReader* input,
+ bool multiline,
+ RegExpCompileData* result,
+ Zone* zone) {
+ ASSERT(result != NULL);
+ RegExpParser parser(input, &result->error, multiline, zone);
+ RegExpTree* tree = parser.ParsePattern();
+ if (parser.failed()) {
+ ASSERT(tree == NULL);
+ ASSERT(!result->error.is_null());
+ } else {
+ ASSERT(tree != NULL);
+ ASSERT(result->error.is_null());
+ result->tree = tree;
+ int capture_count = parser.captures_started();
+ result->simple = tree->IsAtom() && parser.simple() && capture_count == 0;
+ result->contains_anchor = parser.contains_anchor();
+ result->capture_count = capture_count;
+ }
+ return !parser.failed();
+}
+
+
+bool Parser::Parse() {
+ ASSERT(info()->function() == NULL);
+ FunctionLiteral* result = NULL;
+ if (info()->is_lazy()) {
+ ASSERT(!info()->is_eval());
+ if (info()->shared_info()->is_function()) {
+ result = ParseLazy();
+ } else {
+ result = ParseProgram();
+ }
+ } else {
+ ScriptDataImpl* pre_parse_data = info()->pre_parse_data();
+ set_pre_parse_data(pre_parse_data);
+ if (pre_parse_data != NULL && pre_parse_data->has_error()) {
+ Scanner::Location loc = pre_parse_data->MessageLocation();
+ const char* message = pre_parse_data->BuildMessage();
+ Vector<const char*> args = pre_parse_data->BuildArgs();
+ ReportMessageAt(loc, message, args);
+ DeleteArray(message);
+ for (int i = 0; i < args.length(); i++) {
+ DeleteArray(args[i]);
+ }
+ DeleteArray(args.start());
+ ASSERT(info()->isolate()->has_pending_exception());
+ } else {
+ result = ParseProgram();
+ }
+ }
+ info()->SetFunction(result);
+ return (result != NULL);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/parser.h b/chromium/v8/src/parser.h
new file mode 100644
index 00000000000..68a74b78a9a
--- /dev/null
+++ b/chromium/v8/src/parser.h
@@ -0,0 +1,913 @@
+// Copyright 2012 the V8 project authors. 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_PARSER_H_
+#define V8_PARSER_H_
+
+#include "allocation.h"
+#include "ast.h"
+#include "preparse-data-format.h"
+#include "preparse-data.h"
+#include "scopes.h"
+#include "preparser.h"
+
+namespace v8 {
+namespace internal {
+
+class CompilationInfo;
+class FuncNameInferrer;
+class ParserLog;
+class PositionStack;
+class Target;
+
+template <typename T> class ZoneListWrapper;
+
+
+class ParserMessage : public Malloced {
+ public:
+ ParserMessage(Scanner::Location loc, const char* message,
+ Vector<const char*> args)
+ : loc_(loc),
+ message_(message),
+ args_(args) { }
+ ~ParserMessage();
+ Scanner::Location location() { return loc_; }
+ const char* message() { return message_; }
+ Vector<const char*> args() { return args_; }
+ private:
+ Scanner::Location loc_;
+ const char* message_;
+ Vector<const char*> args_;
+};
+
+
+class FunctionEntry BASE_EMBEDDED {
+ public:
+ enum {
+ kStartPositionIndex,
+ kEndPositionIndex,
+ kLiteralCountIndex,
+ kPropertyCountIndex,
+ kLanguageModeIndex,
+ kSize
+ };
+
+ explicit FunctionEntry(Vector<unsigned> backing)
+ : backing_(backing) { }
+
+ FunctionEntry() : backing_() { }
+
+ 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]);
+ }
+
+ bool is_valid() { return !backing_.is_empty(); }
+
+ private:
+ Vector<unsigned> backing_;
+};
+
+
+class ScriptDataImpl : public ScriptData {
+ public:
+ explicit ScriptDataImpl(Vector<unsigned> store)
+ : store_(store),
+ owns_store_(true) { }
+
+ // Create an empty ScriptDataImpl that is guaranteed to not satisfy
+ // a SanityCheck.
+ ScriptDataImpl() : owns_store_(false) { }
+
+ virtual ~ScriptDataImpl();
+ virtual int Length();
+ virtual const char* Data();
+ virtual bool HasError();
+
+ void Initialize();
+ void ReadNextSymbolPosition();
+
+ FunctionEntry GetFunctionEntry(int start);
+ int GetSymbolIdentifier();
+ bool SanityCheck();
+
+ Scanner::Location MessageLocation();
+ const char* BuildMessage();
+ Vector<const char*> BuildArgs();
+
+ int symbol_count() {
+ return (store_.length() > PreparseDataConstants::kHeaderSize)
+ ? store_[PreparseDataConstants::kSymbolCountOffset]
+ : 0;
+ }
+ // The following functions should only be called if SanityCheck has
+ // returned true.
+ bool has_error() { return store_[PreparseDataConstants::kHasErrorOffset]; }
+ unsigned magic() { return store_[PreparseDataConstants::kMagicOffset]; }
+ unsigned version() { return store_[PreparseDataConstants::kVersionOffset]; }
+
+ private:
+ Vector<unsigned> store_;
+ unsigned char* symbol_data_;
+ unsigned char* symbol_data_end_;
+ int function_index_;
+ bool owns_store_;
+
+ unsigned Read(int position);
+ unsigned* ReadAddress(int position);
+ // Reads a number from the current symbols
+ int ReadNumber(byte** source);
+
+ ScriptDataImpl(const char* backing_store, int length)
+ : store_(reinterpret_cast<unsigned*>(const_cast<char*>(backing_store)),
+ length / static_cast<int>(sizeof(unsigned))),
+ owns_store_(false) {
+ ASSERT_EQ(0, static_cast<int>(
+ reinterpret_cast<intptr_t>(backing_store) % sizeof(unsigned)));
+ }
+
+ // Read strings written by ParserRecorder::WriteString.
+ static const char* ReadString(unsigned* start, int* chars);
+
+ friend class ScriptData;
+};
+
+
+class PreParserApi {
+ public:
+ // Pre-parse a character stream and return full preparse data.
+ //
+ // This interface is here instead of in preparser.h because it instantiates a
+ // preparser recorder object that is suited to the parser's purposes. Also,
+ // the preparser doesn't know about ScriptDataImpl.
+ static ScriptDataImpl* PreParse(Utf16CharacterStream* source);
+};
+
+
+// ----------------------------------------------------------------------------
+// REGEXP PARSING
+
+// 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
+// allocated.
+// Elements must not be NULL pointers.
+template <typename T, int initial_size>
+class BufferedZoneList {
+ public:
+ BufferedZoneList() : list_(NULL), last_(NULL) {}
+
+ // Adds element at end of list. This element is buffered and can
+ // be read using last() or removed using RemoveLast until a new Add or until
+ // RemoveLast or GetList has been called.
+ void Add(T* value, Zone* zone) {
+ if (last_ != NULL) {
+ if (list_ == NULL) {
+ list_ = new(zone) ZoneList<T*>(initial_size, zone);
+ }
+ list_->Add(last_, zone);
+ }
+ last_ = value;
+ }
+
+ T* last() {
+ ASSERT(last_ != NULL);
+ return last_;
+ }
+
+ T* RemoveLast() {
+ ASSERT(last_ != NULL);
+ T* result = last_;
+ if ((list_ != NULL) && (list_->length() > 0))
+ last_ = list_->RemoveLast();
+ else
+ last_ = NULL;
+ return result;
+ }
+
+ T* Get(int i) {
+ ASSERT((0 <= i) && (i < length()));
+ if (list_ == NULL) {
+ ASSERT_EQ(0, i);
+ return last_;
+ } else {
+ if (i == list_->length()) {
+ ASSERT(last_ != NULL);
+ return last_;
+ } else {
+ return list_->at(i);
+ }
+ }
+ }
+
+ void Clear() {
+ list_ = NULL;
+ last_ = NULL;
+ }
+
+ int length() {
+ int length = (list_ == NULL) ? 0 : list_->length();
+ return length + ((last_ == NULL) ? 0 : 1);
+ }
+
+ ZoneList<T*>* GetList(Zone* zone) {
+ if (list_ == NULL) {
+ list_ = new(zone) ZoneList<T*>(initial_size, zone);
+ }
+ if (last_ != NULL) {
+ list_->Add(last_, zone);
+ last_ = NULL;
+ }
+ return list_;
+ }
+
+ private:
+ ZoneList<T*>* list_;
+ T* last_;
+};
+
+
+// Accumulates RegExp atoms and assertions into lists of terms and alternatives.
+class RegExpBuilder: public ZoneObject {
+ public:
+ explicit RegExpBuilder(Zone* zone);
+ void AddCharacter(uc16 character);
+ // "Adds" an empty expression. Does nothing except consume a
+ // following quantifier
+ void AddEmpty();
+ void AddAtom(RegExpTree* tree);
+ void AddAssertion(RegExpTree* tree);
+ void NewAlternative(); // '|'
+ void AddQuantifierToAtom(
+ int min, int max, RegExpQuantifier::QuantifierType type);
+ RegExpTree* ToRegExp();
+
+ private:
+ void FlushCharacters();
+ void FlushText();
+ void FlushTerms();
+ Zone* zone() const { return zone_; }
+
+ Zone* zone_;
+ bool pending_empty_;
+ ZoneList<uc16>* characters_;
+ BufferedZoneList<RegExpTree, 2> terms_;
+ BufferedZoneList<RegExpTree, 2> text_;
+ BufferedZoneList<RegExpTree, 2> alternatives_;
+#ifdef DEBUG
+ enum {ADD_NONE, ADD_CHAR, ADD_TERM, ADD_ASSERT, ADD_ATOM} last_added_;
+#define LAST(x) last_added_ = x;
+#else
+#define LAST(x)
+#endif
+};
+
+
+class RegExpParser BASE_EMBEDDED {
+ public:
+ RegExpParser(FlatStringReader* in,
+ Handle<String>* error,
+ bool multiline_mode,
+ Zone* zone);
+
+ static bool ParseRegExp(FlatStringReader* input,
+ bool multiline,
+ RegExpCompileData* result,
+ Zone* zone);
+
+ RegExpTree* ParsePattern();
+ RegExpTree* ParseDisjunction();
+ RegExpTree* ParseGroup();
+ RegExpTree* ParseCharacterClass();
+
+ // Parses a {...,...} quantifier and stores the range in the given
+ // out parameters.
+ bool ParseIntervalQuantifier(int* min_out, int* max_out);
+
+ // Parses and returns a single escaped character. The character
+ // must not be 'b' or 'B' since they are usually handle specially.
+ uc32 ParseClassCharacterEscape();
+
+ // Checks whether the following is a length-digit hexadecimal number,
+ // and sets the value if it is.
+ bool ParseHexEscape(int length, uc32* value);
+
+ uc32 ParseOctalLiteral();
+
+ // Tries to parse the input as a back reference. If successful it
+ // stores the result in the output parameter and returns true. If
+ // it fails it will push back the characters read so the same characters
+ // can be reparsed.
+ bool ParseBackReferenceIndex(int* index_out);
+
+ CharacterRange ParseClassAtom(uc16* char_class);
+ RegExpTree* ReportError(Vector<const char> message);
+ void Advance();
+ void Advance(int dist);
+ void Reset(int pos);
+
+ // Reports whether the pattern might be used as a literal search string.
+ // Only use if the result of the parse is a single atom node.
+ bool simple();
+ bool contains_anchor() { return contains_anchor_; }
+ void set_contains_anchor() { contains_anchor_ = true; }
+ int captures_started() { return captures_ == NULL ? 0 : captures_->length(); }
+ int position() { return next_pos_ - 1; }
+ bool failed() { return failed_; }
+
+ static const int kMaxCaptures = 1 << 16;
+ static const uc32 kEndMarker = (1 << 21);
+
+ private:
+ enum SubexpressionType {
+ INITIAL,
+ CAPTURE, // All positive values represent captures.
+ POSITIVE_LOOKAHEAD,
+ NEGATIVE_LOOKAHEAD,
+ GROUPING
+ };
+
+ class RegExpParserState : public ZoneObject {
+ public:
+ RegExpParserState(RegExpParserState* previous_state,
+ SubexpressionType group_type,
+ int disjunction_capture_index,
+ Zone* zone)
+ : previous_state_(previous_state),
+ builder_(new(zone) RegExpBuilder(zone)),
+ group_type_(group_type),
+ disjunction_capture_index_(disjunction_capture_index) {}
+ // Parser state of containing expression, if any.
+ RegExpParserState* previous_state() { return previous_state_; }
+ bool IsSubexpression() { return previous_state_ != NULL; }
+ // RegExpBuilder building this regexp's AST.
+ RegExpBuilder* builder() { return builder_; }
+ // Type of regexp being parsed (parenthesized group or entire regexp).
+ SubexpressionType group_type() { return group_type_; }
+ // Index in captures array of first capture in this sub-expression, if any.
+ // Also the capture index of this sub-expression itself, if group_type
+ // is CAPTURE.
+ int capture_index() { return disjunction_capture_index_; }
+
+ private:
+ // Linked list implementation of stack of states.
+ RegExpParserState* previous_state_;
+ // Builder for the stored disjunction.
+ RegExpBuilder* builder_;
+ // Stored disjunction type (capture, look-ahead or grouping), if any.
+ SubexpressionType group_type_;
+ // Stored disjunction's capture index (if any).
+ int disjunction_capture_index_;
+ };
+
+ Isolate* isolate() { return isolate_; }
+ Zone* zone() const { return zone_; }
+
+ uc32 current() { return current_; }
+ bool has_more() { return has_more_; }
+ bool has_next() { return next_pos_ < in()->length(); }
+ uc32 Next();
+ FlatStringReader* in() { return in_; }
+ void ScanForCaptures();
+
+ Isolate* isolate_;
+ Zone* zone_;
+ Handle<String>* error_;
+ ZoneList<RegExpCapture*>* captures_;
+ FlatStringReader* in_;
+ uc32 current_;
+ int next_pos_;
+ // The capture count is only valid after we have scanned for captures.
+ int capture_count_;
+ bool has_more_;
+ bool multiline_;
+ bool simple_;
+ bool contains_anchor_;
+ bool is_scanned_for_captures_;
+ bool failed_;
+};
+
+// ----------------------------------------------------------------------------
+// JAVASCRIPT PARSING
+
+// Forward declaration.
+class SingletonLogger;
+
+class Parser BASE_EMBEDDED {
+ public:
+ explicit Parser(CompilationInfo* info);
+ ~Parser() {
+ delete reusable_preparser_;
+ reusable_preparser_ = NULL;
+ }
+
+ bool allow_natives_syntax() const { return allow_natives_syntax_; }
+ bool allow_lazy() const { return allow_lazy_; }
+ bool allow_modules() { return scanner().HarmonyModules(); }
+ bool allow_harmony_scoping() { return scanner().HarmonyScoping(); }
+ bool allow_generators() const { return allow_generators_; }
+ bool allow_for_of() const { return allow_for_of_; }
+ bool allow_harmony_numeric_literals() {
+ return scanner().HarmonyNumericLiterals();
+ }
+
+ void set_allow_natives_syntax(bool allow) { allow_natives_syntax_ = allow; }
+ void set_allow_lazy(bool allow) { allow_lazy_ = allow; }
+ void set_allow_modules(bool allow) { scanner().SetHarmonyModules(allow); }
+ void set_allow_harmony_scoping(bool allow) {
+ scanner().SetHarmonyScoping(allow);
+ }
+ void set_allow_generators(bool allow) { allow_generators_ = allow; }
+ void set_allow_for_of(bool allow) { allow_for_of_ = allow; }
+ void set_allow_harmony_numeric_literals(bool allow) {
+ scanner().SetHarmonyNumericLiterals(allow);
+ }
+
+ // 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) { return Parser(info).Parse(); }
+ bool Parse();
+
+ // Returns NULL if parsing failed.
+ FunctionLiteral* ParseProgram();
+
+ void ReportMessageAt(Scanner::Location loc,
+ const char* message,
+ Vector<const char*> args);
+ void ReportMessageAt(Scanner::Location loc,
+ const char* message,
+ Vector<Handle<String> > args);
+
+ private:
+ static const int kMaxNumFunctionLocals = 131071; // 2^17-1
+
+ enum Mode {
+ PARSE_LAZILY,
+ PARSE_EAGERLY
+ };
+
+ enum VariableDeclarationContext {
+ kModuleElement,
+ kBlockElement,
+ kStatement,
+ 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 AddProperty() { expected_property_count_++; }
+ int expected_property_count() { return expected_property_count_; }
+
+ void set_generator_object_variable(Variable *variable) {
+ ASSERT(variable != NULL);
+ ASSERT(!is_generator());
+ generator_object_variable_ = variable;
+ }
+ Variable* generator_object_variable() const {
+ return generator_object_variable_;
+ }
+ bool is_generator() const {
+ return generator_object_variable_ != NULL;
+ }
+
+ 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_;
+
+ // For generators, the variable that holds the generator object. This
+ // variable is used by yield expressions and return statements. NULL
+ // indicates that this function is not a generator.
+ Variable* generator_object_variable_;
+
+ Parser* parser_;
+ FunctionState* outer_function_state_;
+ Scope* outer_scope_;
+ int saved_ast_node_id_;
+ AstNodeFactory<AstConstructionVisitor> factory_;
+ };
+
+ class ParsingModeScope BASE_EMBEDDED {
+ public:
+ ParsingModeScope(Parser* parser, Mode mode)
+ : parser_(parser),
+ old_mode_(parser->mode()) {
+ parser_->mode_ = mode;
+ }
+ ~ParsingModeScope() {
+ parser_->mode_ = old_mode_;
+ }
+
+ private:
+ Parser* parser_;
+ Mode old_mode_;
+ };
+
+ FunctionLiteral* ParseLazy();
+ FunctionLiteral* ParseLazy(Utf16CharacterStream* source);
+
+ Isolate* isolate() { return isolate_; }
+ Zone* zone() const { return zone_; }
+ CompilationInfo* info() const { return info_; }
+
+ // Called by ParseProgram after setting up the scanner.
+ FunctionLiteral* DoParseProgram(CompilationInfo* info,
+ Handle<String> source);
+
+ // Report syntax error
+ void ReportUnexpectedToken(Token::Value token);
+ void ReportInvalidPreparseData(Handle<String> name, bool* ok);
+ void ReportMessage(const char* message, Vector<const char*> args);
+ void ReportMessage(const char* message, Vector<Handle<String> > args);
+
+ void set_pre_parse_data(ScriptDataImpl *data) {
+ pre_parse_data_ = data;
+ symbol_cache_.Initialize(data ? data->symbol_count() : 0, zone());
+ }
+
+ bool inside_with() const { return top_scope_->inside_with(); }
+ Scanner& scanner() { return scanner_; }
+ Mode mode() const { return mode_; }
+ ScriptDataImpl* pre_parse_data() const { return pre_parse_data_; }
+ bool is_extended_mode() {
+ ASSERT(top_scope_ != NULL);
+ return top_scope_->is_extended_mode();
+ }
+ Scope* DeclarationScope(VariableMode mode) {
+ return IsLexicalVariableMode(mode)
+ ? top_scope_ : top_scope_->DeclarationScope();
+ }
+
+ // Check if the given string is 'eval' or 'arguments'.
+ bool IsEvalOrArguments(Handle<String> string);
+
+ // All ParseXXX functions take as the last argument an *ok parameter
+ // which is set to false if parsing failed; it is unchanged otherwise.
+ // By making the 'exception handling' explicit, we are forced to check
+ // for failure at the call sites.
+ void* ParseSourceElements(ZoneList<Statement*>* processor, int end_token,
+ bool is_eval, bool is_global, bool* ok);
+ Statement* ParseModuleElement(ZoneStringList* labels, bool* ok);
+ Statement* ParseModuleDeclaration(ZoneStringList* names, bool* ok);
+ Module* ParseModule(bool* ok);
+ Module* ParseModuleLiteral(bool* ok);
+ Module* ParseModulePath(bool* ok);
+ Module* ParseModuleVariable(bool* ok);
+ Module* ParseModuleUrl(bool* ok);
+ Module* ParseModuleSpecifier(bool* ok);
+ Block* ParseImportDeclaration(bool* ok);
+ Statement* ParseExportDeclaration(bool* ok);
+ Statement* ParseBlockElement(ZoneStringList* labels, bool* ok);
+ Statement* ParseStatement(ZoneStringList* labels, bool* ok);
+ Statement* ParseFunctionDeclaration(ZoneStringList* names, bool* ok);
+ Statement* ParseNativeDeclaration(bool* ok);
+ Block* ParseBlock(ZoneStringList* labels, bool* ok);
+ Block* ParseVariableStatement(VariableDeclarationContext var_context,
+ ZoneStringList* names,
+ bool* ok);
+ Block* ParseVariableDeclarations(VariableDeclarationContext var_context,
+ VariableDeclarationProperties* decl_props,
+ ZoneStringList* names,
+ Handle<String>* out,
+ bool* ok);
+ Statement* ParseExpressionOrLabelledStatement(ZoneStringList* labels,
+ bool* ok);
+ IfStatement* ParseIfStatement(ZoneStringList* labels, bool* ok);
+ Statement* ParseContinueStatement(bool* ok);
+ Statement* ParseBreakStatement(ZoneStringList* labels, bool* ok);
+ Statement* ParseReturnStatement(bool* ok);
+ Statement* ParseWithStatement(ZoneStringList* labels, bool* ok);
+ CaseClause* ParseCaseClause(bool* default_seen_ptr, bool* ok);
+ SwitchStatement* ParseSwitchStatement(ZoneStringList* labels, bool* ok);
+ DoWhileStatement* ParseDoWhileStatement(ZoneStringList* labels, bool* ok);
+ WhileStatement* ParseWhileStatement(ZoneStringList* labels, bool* ok);
+ Statement* ParseForStatement(ZoneStringList* labels, bool* ok);
+ Statement* ParseThrowStatement(bool* ok);
+ Expression* MakeCatchContext(Handle<String> id, VariableProxy* value);
+ 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* ParseYieldExpression(bool* ok);
+ Expression* ParseConditionalExpression(bool accept_IN, bool* ok);
+ Expression* ParseBinaryExpression(int prec, bool accept_IN, bool* ok);
+ Expression* ParseUnaryExpression(bool* ok);
+ Expression* ParsePostfixExpression(bool* ok);
+ Expression* ParseLeftHandSideExpression(bool* ok);
+ Expression* ParseNewExpression(bool* ok);
+ Expression* ParseMemberExpression(bool* ok);
+ Expression* ParseNewPrefix(PositionStack* stack, bool* ok);
+ Expression* ParseMemberWithNewPrefixesExpression(PositionStack* stack,
+ bool* ok);
+ Expression* ParsePrimaryExpression(bool* ok);
+ Expression* ParseArrayLiteral(bool* ok);
+ Expression* ParseObjectLiteral(bool* ok);
+ ObjectLiteral::Property* ParseObjectLiteralGetSet(bool is_getter, bool* ok);
+ Expression* ParseRegExpLiteral(bool seen_equal, bool* ok);
+
+ // Populate the constant properties fixed array for a materialized object
+ // literal.
+ void BuildObjectLiteralConstantProperties(
+ ZoneList<ObjectLiteral::Property*>* properties,
+ Handle<FixedArray> constants,
+ bool* is_simple,
+ bool* fast_elements,
+ int* depth,
+ bool* may_store_doubles);
+
+ // Decide if a property should be in the object boilerplate.
+ bool IsBoilerplateProperty(ObjectLiteral::Property* property);
+ // If the expression is a literal, return the literal value;
+ // if the expression is a materialized literal and is simple return a
+ // compile time value as encoded by CompileTimeValue::GetValue().
+ // Otherwise, return undefined literal as the placeholder
+ // in the object literal boilerplate.
+ Handle<Object> GetBoilerplateValue(Expression* expression);
+
+ // Initialize the components of a for-in / for-of statement.
+ void InitializeForEachStatement(ForEachStatement* stmt,
+ Expression* each,
+ Expression* subject,
+ Statement* body);
+
+ ZoneList<Expression*>* ParseArguments(bool* ok);
+ FunctionLiteral* ParseFunctionLiteral(Handle<String> var_name,
+ bool name_is_reserved,
+ bool is_generator,
+ int function_token_position,
+ FunctionLiteral::FunctionType type,
+ bool* ok);
+
+
+ // Magical syntax support.
+ Expression* ParseV8Intrinsic(bool* ok);
+
+ INLINE(Token::Value peek()) {
+ if (stack_overflow_) return Token::ILLEGAL;
+ return scanner().peek();
+ }
+
+ INLINE(Token::Value Next()) {
+ // BUG 1215673: Find a thread safe way to set a stack limit in
+ // pre-parse mode. Otherwise, we cannot safely pre-parse from other
+ // threads.
+ if (stack_overflow_) {
+ return Token::ILLEGAL;
+ }
+ if (StackLimitCheck(isolate()).HasOverflowed()) {
+ // Any further calls to Next or peek will return the illegal token.
+ // The current call must return the next token, which might already
+ // have been peek'ed.
+ stack_overflow_ = true;
+ }
+ return scanner().Next();
+ }
+
+ bool is_generator() const { return current_function_state_->is_generator(); }
+
+ bool CheckInOrOf(bool accept_OF, ForEachStatement::VisitMode* visit_mode);
+
+ bool peek_any_identifier();
+
+ INLINE(void Consume(Token::Value token));
+ void Expect(Token::Value token, bool* ok);
+ bool Check(Token::Value token);
+ void ExpectSemicolon(bool* ok);
+ bool CheckContextualKeyword(Vector<const char> keyword);
+ void ExpectContextualKeyword(Vector<const char> keyword, bool* ok);
+
+ Handle<String> LiteralString(PretenureFlag tenured) {
+ if (scanner().is_literal_ascii()) {
+ return isolate_->factory()->NewStringFromAscii(
+ scanner().literal_ascii_string(), tenured);
+ } else {
+ return isolate_->factory()->NewStringFromTwoByte(
+ scanner().literal_utf16_string(), tenured);
+ }
+ }
+
+ Handle<String> NextLiteralString(PretenureFlag tenured) {
+ if (scanner().is_next_literal_ascii()) {
+ return isolate_->factory()->NewStringFromAscii(
+ scanner().next_literal_ascii_string(), tenured);
+ } else {
+ return isolate_->factory()->NewStringFromTwoByte(
+ scanner().next_literal_utf16_string(), tenured);
+ }
+ }
+
+ Handle<String> GetSymbol();
+
+ // Get odd-ball literals.
+ Literal* GetLiteralUndefined();
+ Literal* GetLiteralTheHole();
+
+ Handle<String> ParseIdentifier(bool* ok);
+ Handle<String> ParseIdentifierOrStrictReservedWord(
+ bool* is_strict_reserved, bool* ok);
+ Handle<String> ParseIdentifierName(bool* ok);
+ Handle<String> ParseIdentifierNameOrGetOrSet(bool* is_get,
+ 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,
+ bool* ok);
+
+ // Strict mode octal literal validation.
+ void CheckOctalLiteral(int beg_pos, int end_pos, bool* ok);
+
+ // For harmony block scoping mode: Check if the scope has conflicting var/let
+ // declarations from different scopes. It covers for example
+ //
+ // function f() { { { var x; } let x; } }
+ // function g() { { var x; let x; } }
+ //
+ // The var declarations are hoisted to the function scope, but originate from
+ // a scope where the name has also been let bound or the var declaration is
+ // hoisted over such a scope.
+ void CheckConflictingVarDeclarations(Scope* scope, bool* ok);
+
+ // Parser support
+ VariableProxy* NewUnresolved(Handle<String> name,
+ VariableMode mode,
+ Interface* interface);
+ void Declare(Declaration* declaration, bool resolve, bool* ok);
+
+ bool TargetStackContainsLabel(Handle<String> label);
+ BreakableStatement* LookupBreakTarget(Handle<String> label, bool* ok);
+ IterationStatement* LookupContinueTarget(Handle<String> label, bool* ok);
+
+ void RegisterTargetUse(Label* target, Target* stop);
+
+ // Factory methods.
+
+ Scope* NewScope(Scope* parent, ScopeType type);
+
+ Handle<String> LookupSymbol(int symbol_id);
+
+ Handle<String> LookupCachedSymbol(int symbol_id);
+
+ // Generate AST node that throw a ReferenceError with the given type.
+ Expression* NewThrowReferenceError(Handle<String> type);
+
+ // Generate AST node that throw a SyntaxError with the given
+ // type. The first argument may be null (in the handle sense) in
+ // which case no arguments are passed to the constructor.
+ Expression* NewThrowSyntaxError(Handle<String> type, Handle<Object> first);
+
+ // Generate AST node that throw a TypeError with the given
+ // type. Both arguments must be non-null (in the handle sense).
+ Expression* NewThrowTypeError(Handle<String> type,
+ Handle<Object> first,
+ Handle<Object> second);
+
+ // Generic AST generator for throwing errors from compiled code.
+ Expression* NewThrowError(Handle<String> constructor,
+ 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_;
+ Scanner scanner_;
+ preparser::PreParser* reusable_preparser_;
+ Scope* top_scope_;
+ FunctionState* current_function_state_;
+ Target* target_stack_; // for break, continue statements
+ v8::Extension* extension_;
+ ScriptDataImpl* pre_parse_data_;
+ FuncNameInferrer* fni_;
+
+ Mode mode_;
+ bool allow_natives_syntax_;
+ bool allow_lazy_;
+ bool allow_generators_;
+ bool allow_for_of_;
+ 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_;
+
+ Zone* zone_;
+ CompilationInfo* info_;
+ friend class BlockState;
+ friend class FunctionState;
+};
+
+
+// Support for handling complex values (array and object literals) that
+// can be fully handled at compile time.
+class CompileTimeValue: public AllStatic {
+ public:
+ enum LiteralType {
+ OBJECT_LITERAL_FAST_ELEMENTS,
+ OBJECT_LITERAL_SLOW_ELEMENTS,
+ ARRAY_LITERAL
+ };
+
+ static bool IsCompileTimeValue(Expression* expression);
+
+ // Get the value as a compile time value.
+ static Handle<FixedArray> GetValue(Expression* expression);
+
+ // Get the type of a compile time value returned by GetValue().
+ static LiteralType GetLiteralType(Handle<FixedArray> value);
+
+ // Get the elements array of a compile time value returned by GetValue().
+ static Handle<FixedArray> GetElements(Handle<FixedArray> value);
+
+ private:
+ static const int kLiteralTypeSlot = 0;
+ static const int kElementsSlot = 1;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CompileTimeValue);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_PARSER_H_
diff --git a/chromium/v8/src/platform-cygwin.cc b/chromium/v8/src/platform-cygwin.cc
new file mode 100644
index 00000000000..4c7b0175923
--- /dev/null
+++ b/chromium/v8/src/platform-cygwin.cc
@@ -0,0 +1,483 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Platform specific code for Cygwin goes here. For the POSIX comaptible parts
+// the implementation is in platform-posix.cc.
+
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdarg.h>
+#include <strings.h> // index
+#include <sys/time.h>
+#include <sys/mman.h> // mmap & munmap
+#include <unistd.h> // sysconf
+
+#undef MAP_TYPE
+
+#include "v8.h"
+
+#include "platform-posix.h"
+#include "platform.h"
+#include "simulator.h"
+#include "v8threads.h"
+#include "vm-state-inl.h"
+#include "win32-headers.h"
+
+namespace v8 {
+namespace internal {
+
+
+static Mutex* limit_mutex = NULL;
+
+
+const char* OS::LocalTimezone(double time) {
+ if (std::isnan(time)) return "";
+ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
+ struct tm* t = localtime(&tv);
+ if (NULL == t) return "";
+ return tzname[0]; // The location of the timezone string on Cygwin.
+}
+
+
+double OS::LocalTimeOffset() {
+ // On Cygwin, struct tm does not contain a tm_gmtoff field.
+ time_t utc = time(NULL);
+ ASSERT(utc != -1);
+ struct tm* loc = localtime(&utc);
+ ASSERT(loc != NULL);
+ // time - localtime includes any daylight savings offset, so subtract it.
+ return static_cast<double>((mktime(loc) - utc) * msPerSecond -
+ (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0));
+}
+
+
+// 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, 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);
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
+
+
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ ASSERT(limit_mutex != NULL);
+ ScopedLock lock(limit_mutex);
+
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
+ highest_ever_allocated =
+ Max(highest_ever_allocated,
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* address) {
+ return address < lowest_ever_allocated || address >= highest_ever_allocated;
+}
+
+
+void* OS::Allocate(const size_t requested,
+ size_t* allocated,
+ bool is_executable) {
+ const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
+ void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (mbase == MAP_FAILED) {
+ LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
+ return NULL;
+ }
+ *allocated = msize;
+ UpdateAllocatedSpaceLimits(mbase, msize);
+ return mbase;
+}
+
+
+void OS::DumpBacktrace() {
+ // Currently unsupported.
+}
+
+
+class PosixMemoryMappedFile : public OS::MemoryMappedFile {
+ public:
+ PosixMemoryMappedFile(FILE* file, void* memory, int size)
+ : file_(file), memory_(memory), size_(size) { }
+ virtual ~PosixMemoryMappedFile();
+ virtual void* memory() { return memory_; }
+ virtual int size() { return size_; }
+ private:
+ FILE* file_;
+ void* memory_;
+ int size_;
+};
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
+ FILE* file = fopen(name, "r+");
+ if (file == NULL) return NULL;
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ FILE* file = fopen(name, "w+");
+ if (file == NULL) return NULL;
+ int result = fwrite(initial, size, 1, file);
+ if (result < 1) {
+ fclose(file);
+ return NULL;
+ }
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+PosixMemoryMappedFile::~PosixMemoryMappedFile() {
+ if (memory_) munmap(memory_, size_);
+ fclose(file_);
+}
+
+
+void OS::LogSharedLibraryAddresses() {
+ // 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) {
+ 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;
+ }
+ }
+ free(lib_name);
+ fclose(fp);
+}
+
+
+void OS::SignalCodeMovingGC() {
+ // Nothing to do on Cygwin.
+}
+
+
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
+ // Not supported on Cygwin.
+ return 0;
+}
+
+
+// The VirtualMemory implementation is taken from platform-win32.cc.
+// The mmap-based virtual memory implementation as it is used on most posix
+// platforms does not work well because Cygwin does not support MAP_FIXED.
+// This causes VirtualMemory::Commit to not always commit the memory region
+// specified.
+
+static void* GetRandomAddr() {
+ 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) {
+ // The address range used to randomize RWX allocations in OS::Allocate
+ // Try not to map pages into the default range that windows loads DLLs
+ // Use a multiple of 64k to prevent committing unused memory.
+ // Note: This does not guarantee RWX regions will be within the
+ // range kAllocationRandomAddressMin to kAllocationRandomAddressMax
+#ifdef V8_HOST_ARCH_64_BIT
+ static const intptr_t kAllocationRandomAddressMin = 0x0000000080000000;
+ static const intptr_t kAllocationRandomAddressMax = 0x000003FFFFFF0000;
+#else
+ static const intptr_t kAllocationRandomAddressMin = 0x04000000;
+ static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000;
+#endif
+ uintptr_t address = (V8::RandomPrivate(isolate) << kPageSizeBits)
+ | kAllocationRandomAddressMin;
+ address &= kAllocationRandomAddressMax;
+ return reinterpret_cast<void *>(address);
+ }
+ return NULL;
+}
+
+
+static void* RandomizedVirtualAlloc(size_t size, int action, int protection) {
+ LPVOID base = NULL;
+
+ if (protection == PAGE_EXECUTE_READWRITE || protection == PAGE_NOACCESS) {
+ // For exectutable pages try and randomize the allocation address
+ for (size_t attempts = 0; base == NULL && attempts < 3; ++attempts) {
+ base = VirtualAlloc(GetRandomAddr(), size, action, protection);
+ }
+ }
+
+ // After three attempts give up and let the OS find an address to use.
+ if (base == NULL) base = VirtualAlloc(NULL, size, action, protection);
+
+ return base;
+}
+
+
+VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
+
+
+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()) {
+ 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) {
+ return CommitRegion(address, size, is_executable);
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ ASSERT(IsReserved());
+ return UncommitRegion(address, size);
+}
+
+
+void* VirtualMemory::ReserveRegion(size_t size) {
+ return RandomizedVirtualAlloc(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(base, size, MEM_COMMIT, prot)) {
+ return false;
+ }
+
+ UpdateAllocatedSpaceLimits(base, static_cast<int>(size));
+ return true;
+}
+
+
+bool VirtualMemory::Guard(void* address) {
+ if (NULL == VirtualAlloc(address,
+ OS::CommitPageSize(),
+ MEM_COMMIT,
+ PAGE_READONLY | PAGE_GUARD)) {
+ return false;
+ }
+ return true;
+}
+
+
+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;
+}
+
+
+bool VirtualMemory::HasLazyCommits() {
+ // TODO(alph): implement for the platform.
+ return false;
+}
+
+
+class CygwinSemaphore : public Semaphore {
+ public:
+ explicit CygwinSemaphore(int count) { sem_init(&sem_, 0, count); }
+ virtual ~CygwinSemaphore() { sem_destroy(&sem_); }
+
+ virtual void Wait();
+ virtual bool Wait(int timeout);
+ virtual void Signal() { sem_post(&sem_); }
+ private:
+ sem_t sem_;
+};
+
+
+void CygwinSemaphore::Wait() {
+ while (true) {
+ int result = sem_wait(&sem_);
+ if (result == 0) return; // Successfully got semaphore.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+#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 CygwinSemaphore::Wait(int timeout) {
+ const long kOneSecondMicros = 1000000; // NOLINT
+
+ // Split timeout into second and nanosecond parts.
+ struct timeval delta;
+ delta.tv_usec = timeout % kOneSecondMicros;
+ delta.tv_sec = timeout / kOneSecondMicros;
+
+ struct timeval current_time;
+ // Get the current time.
+ if (gettimeofday(&current_time, NULL) == -1) {
+ return false;
+ }
+
+ // Calculate time for end of timeout.
+ struct timeval end_time;
+ timeradd(&current_time, &delta, &end_time);
+
+ struct timespec ts;
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
+ // Wait for semaphore signalled or timeout.
+ while (true) {
+ int result = sem_timedwait(&sem_, &ts);
+ if (result == 0) return true; // Successfully got semaphore.
+ if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ return new CygwinSemaphore(count);
+}
+
+
+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();
+}
+
+
+void OS::TearDown() {
+ delete limit_mutex;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform-freebsd.cc b/chromium/v8/src/platform-freebsd.cc
new file mode 100644
index 00000000000..e0917fa567a
--- /dev/null
+++ b/chromium/v8/src/platform-freebsd.cc
@@ -0,0 +1,448 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Platform specific code for FreeBSD 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/types.h>
+#include <sys/ucontext.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
+// If you don't have execinfo.h then you need devel/libexecinfo from ports.
+#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-posix.h"
+#include "platform.h"
+#include "vm-state-inl.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+static Mutex* limit_mutex = NULL;
+
+
+const char* OS::LocalTimezone(double time) {
+ if (std::isnan(time)) return "";
+ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
+ struct tm* t = localtime(&tv);
+ if (NULL == t) return "";
+ return t->tm_zone;
+}
+
+
+double OS::LocalTimeOffset() {
+ time_t tv = time(NULL);
+ struct tm* t = localtime(&tv);
+ // tm_gmtoff includes any daylight savings offset, so subtract it.
+ return static_cast<double>(t->tm_gmtoff * msPerSecond -
+ (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
+}
+
+
+// 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, 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);
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
+
+
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ ASSERT(limit_mutex != NULL);
+ ScopedLock lock(limit_mutex);
+
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
+ highest_ever_allocated =
+ Max(highest_ever_allocated,
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* address) {
+ return address < lowest_ever_allocated || address >= highest_ever_allocated;
+}
+
+
+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);
+
+ if (mbase == MAP_FAILED) {
+ LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
+ return NULL;
+ }
+ *allocated = msize;
+ UpdateAllocatedSpaceLimits(mbase, msize);
+ return mbase;
+}
+
+
+void OS::DumpBacktrace() {
+ POSIXBacktraceHelper<backtrace, backtrace_symbols>::DumpBacktrace();
+}
+
+
+class PosixMemoryMappedFile : public OS::MemoryMappedFile {
+ public:
+ PosixMemoryMappedFile(FILE* file, void* memory, int size)
+ : file_(file), memory_(memory), size_(size) { }
+ virtual ~PosixMemoryMappedFile();
+ virtual void* memory() { return memory_; }
+ virtual int size() { return size_; }
+ private:
+ FILE* file_;
+ void* memory_;
+ int size_;
+};
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
+ FILE* file = fopen(name, "r+");
+ if (file == NULL) return NULL;
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ FILE* file = fopen(name, "w+");
+ if (file == NULL) return NULL;
+ int result = fwrite(initial, size, 1, file);
+ if (result < 1) {
+ fclose(file);
+ return NULL;
+ }
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+PosixMemoryMappedFile::~PosixMemoryMappedFile() {
+ if (memory_) munmap(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;
+ 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));
+ }
+ close(fd);
+}
+
+
+void OS::SignalCodeMovingGC() {
+}
+
+
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
+ return POSIXBacktraceHelper<backtrace, backtrace_symbols>::StackWalk(frames);
+}
+
+
+// Constants used for mmap.
+static const int kMmapFd = -1;
+static const int kMmapFdOffset = 0;
+
+
+VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
+
+
+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()) {
+ 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) {
+ return CommitRegion(address, size, is_executable);
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ return UncommitRegion(address, size);
+}
+
+
+bool VirtualMemory::Guard(void* address) {
+ OS::Guard(address, OS::CommitPageSize());
+ return true;
+}
+
+
+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)) {
+ return false;
+ }
+
+ UpdateAllocatedSpaceLimits(base, size);
+ return true;
+}
+
+
+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;
+}
+
+
+bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
+ return munmap(base, size) == 0;
+}
+
+
+bool VirtualMemory::HasLazyCommits() {
+ // TODO(alph): implement for the platform.
+ return false;
+}
+
+
+class FreeBSDSemaphore : public Semaphore {
+ public:
+ explicit FreeBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
+ virtual ~FreeBSDSemaphore() { sem_destroy(&sem_); }
+
+ virtual void Wait();
+ virtual bool Wait(int timeout);
+ virtual void Signal() { sem_post(&sem_); }
+ private:
+ sem_t sem_;
+};
+
+
+void FreeBSDSemaphore::Wait() {
+ while (true) {
+ int result = sem_wait(&sem_);
+ if (result == 0) return; // Successfully got semaphore.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+bool FreeBSDSemaphore::Wait(int timeout) {
+ const long kOneSecondMicros = 1000000; // NOLINT
+
+ // Split timeout into second and nanosecond parts.
+ struct timeval delta;
+ delta.tv_usec = timeout % kOneSecondMicros;
+ delta.tv_sec = timeout / kOneSecondMicros;
+
+ struct timeval current_time;
+ // Get the current time.
+ if (gettimeofday(&current_time, NULL) == -1) {
+ return false;
+ }
+
+ // Calculate time for end of timeout.
+ struct timeval end_time;
+ timeradd(&current_time, &delta, &end_time);
+
+ struct timespec ts;
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
+ while (true) {
+ int result = sem_timedwait(&sem_, &ts);
+ if (result == 0) return true; // Successfully got semaphore.
+ if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ return new FreeBSDSemaphore(count);
+}
+
+
+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();
+}
+
+
+void OS::TearDown() {
+ delete limit_mutex;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform-linux.cc b/chromium/v8/src/platform-linux.cc
new file mode 100644
index 00000000000..885683398e2
--- /dev/null
+++ b/chromium/v8/src/platform-linux.cc
@@ -0,0 +1,770 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Platform specific code for Linux 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/prctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+#include <execinfo.h>
+#include <cxxabi.h>
+#endif
+
+// Ubuntu Dapper requires memory pages to be marked as
+// executable. Otherwise, OS raises an exception when executing code
+// in that page.
+#include <sys/types.h> // mmap & munmap
+#include <sys/mman.h> // mmap & munmap
+#include <sys/stat.h> // open
+#include <fcntl.h> // open
+#include <unistd.h> // sysconf
+#include <strings.h> // index
+#include <errno.h>
+#include <stdarg.h>
+
+// GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'.
+// Old versions of the C library <signal.h> didn't define the type.
+#if defined(__ANDROID__) && !defined(__BIONIC_HAVE_UCONTEXT_T) && \
+ defined(__arm__) && !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT)
+#include <asm/sigcontext.h>
+#endif
+
+#undef MAP_TYPE
+
+#include "v8.h"
+
+#include "platform-posix.h"
+#include "platform.h"
+#include "v8threads.h"
+#include "vm-state-inl.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+static Mutex* limit_mutex = NULL;
+
+
+#ifdef __arm__
+static bool CPUInfoContainsString(const char * search_string) {
+ const char* file_name = "/proc/cpuinfo";
+ // This is written as a straight shot one pass parser
+ // and not using STL string and ifstream because,
+ // on Linux, it's reading from a (non-mmap-able)
+ // character special device.
+ FILE* f = NULL;
+ const char* what = search_string;
+
+ if (NULL == (f = fopen(file_name, "r"))) {
+ OS::PrintError("Failed to open /proc/cpuinfo\n");
+ return false;
+ }
+
+ int k;
+ while (EOF != (k = fgetc(f))) {
+ if (k == *what) {
+ ++what;
+ while ((*what != '\0') && (*what == fgetc(f))) {
+ ++what;
+ }
+ if (*what == '\0') {
+ fclose(f);
+ return true;
+ } else {
+ what = search_string;
+ }
+ }
+ }
+ fclose(f);
+
+ // Did not find string in the proc file.
+ return false;
+}
+
+
+bool OS::ArmCpuHasFeature(CpuFeature feature) {
+ const char* search_string = NULL;
+ // Simple detection of VFP at runtime for Linux.
+ // It is based on /proc/cpuinfo, which reveals hardware configuration
+ // to user-space applications. According to ARM (mid 2009), no similar
+ // facility is universally available on the ARM architectures,
+ // so it's up to individual OSes to provide such.
+ switch (feature) {
+ case VFP3:
+ search_string = "vfpv3";
+ break;
+ case NEON:
+ search_string = "neon";
+ break;
+ case ARMv7:
+ search_string = "ARMv7";
+ break;
+ case SUDIV:
+ search_string = "idiva";
+ break;
+ case VFP32DREGS:
+ // This case is handled specially below.
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (feature == VFP32DREGS) {
+ return ArmCpuHasFeature(VFP3) && !CPUInfoContainsString("d16");
+ }
+
+ if (CPUInfoContainsString(search_string)) {
+ return true;
+ }
+
+ if (feature == VFP3) {
+ // Some old kernels will report vfp not vfpv3. Here we make a last attempt
+ // to detect vfpv3 by checking for vfp *and* neon, since neon is only
+ // available on architectures with vfpv3.
+ // Checking neon on its own is not enough as it is possible to have neon
+ // without vfp.
+ if (CPUInfoContainsString("vfp") && CPUInfoContainsString("neon")) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+CpuImplementer OS::GetCpuImplementer() {
+ static bool use_cached_value = false;
+ static CpuImplementer cached_value = UNKNOWN_IMPLEMENTER;
+ if (use_cached_value) {
+ return cached_value;
+ }
+ if (CPUInfoContainsString("CPU implementer\t: 0x41")) {
+ cached_value = ARM_IMPLEMENTER;
+ } else if (CPUInfoContainsString("CPU implementer\t: 0x51")) {
+ cached_value = QUALCOMM_IMPLEMENTER;
+ } else {
+ cached_value = UNKNOWN_IMPLEMENTER;
+ }
+ use_cached_value = true;
+ return cached_value;
+}
+
+
+CpuPart OS::GetCpuPart(CpuImplementer implementer) {
+ static bool use_cached_value = false;
+ static CpuPart cached_value = CPU_UNKNOWN;
+ if (use_cached_value) {
+ return cached_value;
+ }
+ if (implementer == ARM_IMPLEMENTER) {
+ if (CPUInfoContainsString("CPU part\t: 0xc0f")) {
+ cached_value = CORTEX_A15;
+ } else if (CPUInfoContainsString("CPU part\t: 0xc0c")) {
+ cached_value = CORTEX_A12;
+ } else if (CPUInfoContainsString("CPU part\t: 0xc09")) {
+ cached_value = CORTEX_A9;
+ } else if (CPUInfoContainsString("CPU part\t: 0xc08")) {
+ cached_value = CORTEX_A8;
+ } else if (CPUInfoContainsString("CPU part\t: 0xc07")) {
+ cached_value = CORTEX_A7;
+ } else if (CPUInfoContainsString("CPU part\t: 0xc05")) {
+ cached_value = CORTEX_A5;
+ } else {
+ cached_value = CPU_UNKNOWN;
+ }
+ } else {
+ cached_value = CPU_UNKNOWN;
+ }
+ use_cached_value = true;
+ return cached_value;
+}
+
+
+bool OS::ArmUsingHardFloat() {
+ // GCC versions 4.6 and above define __ARM_PCS or __ARM_PCS_VFP to specify
+ // the Floating Point ABI used (PCS stands for Procedure Call Standard).
+ // We use these as well as a couple of other defines to statically determine
+ // what FP ABI used.
+ // GCC versions 4.4 and below don't support hard-fp.
+ // GCC versions 4.5 may support hard-fp without defining __ARM_PCS or
+ // __ARM_PCS_VFP.
+
+#define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION >= 40600
+#if defined(__ARM_PCS_VFP)
+ return true;
+#else
+ return false;
+#endif
+
+#elif GCC_VERSION < 40500
+ return false;
+
+#else
+#if defined(__ARM_PCS_VFP)
+ return true;
+#elif defined(__ARM_PCS) || defined(__SOFTFP__) || defined(__SOFTFP) || \
+ !defined(__VFP_FP__)
+ return false;
+#else
+#error "Your version of GCC does not report the FP ABI compiled for." \
+ "Please report it on this issue" \
+ "http://code.google.com/p/v8/issues/detail?id=2140"
+
+#endif
+#endif
+#undef GCC_VERSION
+}
+
+#endif // def __arm__
+
+
+#ifdef __mips__
+bool OS::MipsCpuHasFeature(CpuFeature feature) {
+ const char* search_string = NULL;
+ const char* file_name = "/proc/cpuinfo";
+ // Simple detection of FPU at runtime for Linux.
+ // It is based on /proc/cpuinfo, which reveals hardware configuration
+ // to user-space applications. According to MIPS (early 2010), no similar
+ // facility is universally available on the MIPS architectures,
+ // so it's up to individual OSes to provide such.
+ //
+ // This is written as a straight shot one pass parser
+ // and not using STL string and ifstream because,
+ // on Linux, it's reading from a (non-mmap-able)
+ // character special device.
+
+ switch (feature) {
+ case FPU:
+ search_string = "FPU";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ FILE* f = NULL;
+ const char* what = search_string;
+
+ if (NULL == (f = fopen(file_name, "r"))) {
+ OS::PrintError("Failed to open /proc/cpuinfo\n");
+ return false;
+ }
+
+ int k;
+ while (EOF != (k = fgetc(f))) {
+ if (k == *what) {
+ ++what;
+ while ((*what != '\0') && (*what == fgetc(f))) {
+ ++what;
+ }
+ if (*what == '\0') {
+ fclose(f);
+ return true;
+ } else {
+ what = search_string;
+ }
+ }
+ }
+ fclose(f);
+
+ // Did not find string in the proc file.
+ return false;
+}
+#endif // def __mips__
+
+
+const char* OS::LocalTimezone(double time) {
+ if (std::isnan(time)) return "";
+ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
+ struct tm* t = localtime(&tv);
+ if (NULL == t) return "";
+ return t->tm_zone;
+}
+
+
+double OS::LocalTimeOffset() {
+ time_t tv = time(NULL);
+ struct tm* t = localtime(&tv);
+ // tm_gmtoff includes any daylight savings offset, so subtract it.
+ return static_cast<double>(t->tm_gmtoff * msPerSecond -
+ (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
+}
+
+
+// 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, 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);
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
+
+
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ ASSERT(limit_mutex != NULL);
+ ScopedLock lock(limit_mutex);
+
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
+ highest_ever_allocated =
+ Max(highest_ever_allocated,
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* address) {
+ return address < lowest_ever_allocated || address >= highest_ever_allocated;
+}
+
+
+void* OS::Allocate(const size_t requested,
+ size_t* allocated,
+ bool is_executable) {
+ const size_t msize = RoundUp(requested, AllocateAlignment());
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
+ void* addr = OS::GetRandomMmapAddr();
+ void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (mbase == MAP_FAILED) {
+ LOG(i::Isolate::Current(),
+ StringEvent("OS::Allocate", "mmap failed"));
+ return NULL;
+ }
+ *allocated = msize;
+ UpdateAllocatedSpaceLimits(mbase, msize);
+ return mbase;
+}
+
+
+void OS::DumpBacktrace() {
+ // backtrace is a glibc extension.
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+ POSIXBacktraceHelper<backtrace, backtrace_symbols>::DumpBacktrace();
+#endif
+}
+
+
+class PosixMemoryMappedFile : public OS::MemoryMappedFile {
+ public:
+ PosixMemoryMappedFile(FILE* file, void* memory, int size)
+ : file_(file), memory_(memory), size_(size) { }
+ virtual ~PosixMemoryMappedFile();
+ virtual void* memory() { return memory_; }
+ virtual int size() { return size_; }
+ private:
+ FILE* file_;
+ void* memory_;
+ int size_;
+};
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
+ FILE* file = fopen(name, "r+");
+ if (file == NULL) return NULL;
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+
+ void* memory =
+ mmap(OS::GetRandomMmapAddr(),
+ size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ fileno(file),
+ 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ FILE* file = fopen(name, "w+");
+ if (file == NULL) return NULL;
+ int result = fwrite(initial, size, 1, file);
+ if (result < 1) {
+ fclose(file);
+ return NULL;
+ }
+ void* memory =
+ mmap(OS::GetRandomMmapAddr(),
+ size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ fileno(file),
+ 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+PosixMemoryMappedFile::~PosixMemoryMappedFile() {
+ if (memory_) OS::Free(memory_, size_);
+ fclose(file_);
+}
+
+
+void OS::LogSharedLibraryAddresses() {
+ // 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) {
+ 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 != '/') && (c != '['));
+ if (c == EOF) break; // EOF: Was unexpected, just exit.
+
+ // Process the filename if found.
+ if ((c == '/') || (c == '[')) {
+ // Push the '/' or '[' back into the stream to be read below.
+ ungetc(c, fp);
+
+ // 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
+ // '/' or '[' 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;
+ }
+ }
+ free(lib_name);
+ fclose(fp);
+}
+
+
+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(FLAG_gc_fake_mmap, "w+");
+ if (f == NULL) {
+ OS::PrintError("Failed to open %s\n", FLAG_gc_fake_mmap);
+ OS::Abort();
+ }
+ void* addr = mmap(OS::GetRandomMmapAddr(),
+ size,
+#if defined(__native_client__)
+ // The Native Client port of V8 uses an interpreter,
+ // so code pages don't need PROT_EXEC.
+ PROT_READ,
+#else
+ PROT_READ | PROT_EXEC,
+#endif
+ 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.
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+ return POSIXBacktraceHelper<backtrace, backtrace_symbols>::StackWalk(frames);
+#else
+ return 0;
+#endif
+}
+
+
+// Constants used for mmap.
+static const int kMmapFd = -1;
+static const int kMmapFdOffset = 0;
+
+
+VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
+
+
+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_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()) {
+ 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) {
+ return CommitRegion(address, size, is_executable);
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ return UncommitRegion(address, size);
+}
+
+
+bool VirtualMemory::Guard(void* address) {
+ OS::Guard(address, OS::CommitPageSize());
+ return true;
+}
+
+
+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) {
+#if defined(__native_client__)
+ // The Native Client port of V8 uses an interpreter,
+ // so code pages don't need PROT_EXEC.
+ int prot = PROT_READ | PROT_WRITE;
+#else
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
+#endif
+ if (MAP_FAILED == mmap(base,
+ size,
+ prot,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ kMmapFd,
+ kMmapFdOffset)) {
+ return false;
+ }
+
+ UpdateAllocatedSpaceLimits(base, size);
+ return true;
+}
+
+
+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;
+}
+
+
+bool VirtualMemory::HasLazyCommits() {
+ return true;
+}
+
+
+class LinuxSemaphore : public Semaphore {
+ public:
+ explicit LinuxSemaphore(int count) { sem_init(&sem_, 0, count); }
+ virtual ~LinuxSemaphore() { sem_destroy(&sem_); }
+
+ virtual void Wait();
+ virtual bool Wait(int timeout);
+ virtual void Signal() { sem_post(&sem_); }
+ private:
+ sem_t sem_;
+};
+
+
+void LinuxSemaphore::Wait() {
+ while (true) {
+ int result = sem_wait(&sem_);
+ if (result == 0) return; // Successfully got semaphore.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+#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 LinuxSemaphore::Wait(int timeout) {
+ const long kOneSecondMicros = 1000000; // NOLINT
+
+ // Split timeout into second and nanosecond parts.
+ struct timeval delta;
+ delta.tv_usec = timeout % kOneSecondMicros;
+ delta.tv_sec = timeout / kOneSecondMicros;
+
+ struct timeval current_time;
+ // Get the current time.
+ if (gettimeofday(&current_time, NULL) == -1) {
+ return false;
+ }
+
+ // Calculate time for end of timeout.
+ struct timeval end_time;
+ timeradd(&current_time, &delta, &end_time);
+
+ struct timespec ts;
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
+ // Wait for semaphore signalled or timeout.
+ while (true) {
+ int result = sem_timedwait(&sem_, &ts);
+ if (result == 0) return true; // Successfully got semaphore.
+ if (result > 0) {
+ // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
+ errno = result;
+ result = -1;
+ }
+ if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ return new LinuxSemaphore(count);
+}
+
+
+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();
+}
+
+
+void OS::TearDown() {
+ delete limit_mutex;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform-macos.cc b/chromium/v8/src/platform-macos.cc
new file mode 100644
index 00000000000..6135cd13740
--- /dev/null
+++ b/chromium/v8/src/platform-macos.cc
@@ -0,0 +1,455 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Platform specific code for MacOS goes here. For the POSIX comaptible parts
+// the implementation is in platform-posix.cc.
+
+#include <dlfcn.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <mach/mach_init.h>
+#include <mach-o/dyld.h>
+#include <mach-o/getsect.h>
+
+#include <AvailabilityMacros.h>
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <libkern/OSAtomic.h>
+#include <mach/mach.h>
+#include <mach/semaphore.h>
+#include <mach/task.h>
+#include <mach/vm_statistics.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <cxxabi.h>
+
+#undef MAP_TYPE
+
+#include "v8.h"
+
+#include "platform-posix.h"
+#include "platform.h"
+#include "simulator.h"
+#include "vm-state-inl.h"
+
+// Manually define these here as weak imports, rather than including execinfo.h.
+// This lets us launch on 10.4 which does not have these calls.
+extern "C" {
+ extern int backtrace(void**, int) __attribute__((weak_import));
+ extern char** backtrace_symbols(void* const*, int)
+ __attribute__((weak_import));
+ extern void backtrace_symbols_fd(void* const*, int, int)
+ __attribute__((weak_import));
+}
+
+
+namespace v8 {
+namespace internal {
+
+
+static Mutex* limit_mutex = NULL;
+
+
+// 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, 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);
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
+
+
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ ASSERT(limit_mutex != NULL);
+ ScopedLock lock(limit_mutex);
+
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
+ highest_ever_allocated =
+ Max(highest_ever_allocated,
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* address) {
+ return address < lowest_ever_allocated || address >= highest_ever_allocated;
+}
+
+
+// Constants used for mmap.
+// kMmapFd is used to pass vm_alloc flags to tag the region with the user
+// defined tag 255 This helps identify V8-allocated regions in memory analysis
+// tools like vmmap(1).
+static const int kMmapFd = VM_MAKE_TAG(255);
+static const off_t kMmapFdOffset = 0;
+
+
+void* OS::Allocate(const size_t requested,
+ size_t* allocated,
+ bool is_executable) {
+ const size_t msize = RoundUp(requested, getpagesize());
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
+ void* mbase = mmap(OS::GetRandomMmapAddr(),
+ msize,
+ prot,
+ MAP_PRIVATE | MAP_ANON,
+ kMmapFd,
+ kMmapFdOffset);
+ if (mbase == MAP_FAILED) {
+ LOG(Isolate::Current(), StringEvent("OS::Allocate", "mmap failed"));
+ return NULL;
+ }
+ *allocated = msize;
+ UpdateAllocatedSpaceLimits(mbase, msize);
+ return mbase;
+}
+
+
+void OS::DumpBacktrace() {
+ // If weak link to execinfo lib has failed, ie because we are on 10.4, abort.
+ if (backtrace == NULL) return;
+
+ POSIXBacktraceHelper<backtrace, backtrace_symbols>::DumpBacktrace();
+}
+
+
+class PosixMemoryMappedFile : public OS::MemoryMappedFile {
+ public:
+ PosixMemoryMappedFile(FILE* file, void* memory, int size)
+ : file_(file), memory_(memory), size_(size) { }
+ virtual ~PosixMemoryMappedFile();
+ virtual void* memory() { return memory_; }
+ virtual int size() { return size_; }
+ private:
+ FILE* file_;
+ void* memory_;
+ int size_;
+};
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
+ FILE* file = fopen(name, "r+");
+ if (file == NULL) return NULL;
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+
+ void* memory =
+ mmap(OS::GetRandomMmapAddr(),
+ size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ fileno(file),
+ 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ FILE* file = fopen(name, "w+");
+ if (file == NULL) return NULL;
+ int result = fwrite(initial, size, 1, file);
+ if (result < 1) {
+ fclose(file);
+ return NULL;
+ }
+ void* memory =
+ mmap(OS::GetRandomMmapAddr(),
+ size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ fileno(file),
+ 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+PosixMemoryMappedFile::~PosixMemoryMappedFile() {
+ if (memory_) OS::Free(memory_, size_);
+ fclose(file_);
+}
+
+
+void OS::LogSharedLibraryAddresses() {
+ unsigned int images_count = _dyld_image_count();
+ for (unsigned int i = 0; i < images_count; ++i) {
+ const mach_header* header = _dyld_get_image_header(i);
+ if (header == NULL) continue;
+#if V8_HOST_ARCH_X64
+ uint64_t size;
+ char* code_ptr = getsectdatafromheader_64(
+ reinterpret_cast<const mach_header_64*>(header),
+ SEG_TEXT,
+ SECT_TEXT,
+ &size);
+#else
+ unsigned int size;
+ char* code_ptr = getsectdatafromheader(header, SEG_TEXT, SECT_TEXT, &size);
+#endif
+ if (code_ptr == NULL) continue;
+ const uintptr_t slide = _dyld_get_image_vmaddr_slide(i);
+ const uintptr_t start = reinterpret_cast<uintptr_t>(code_ptr) + slide;
+ LOG(Isolate::Current(),
+ SharedLibraryEvent(_dyld_get_image_name(i), start, start + size));
+ }
+}
+
+
+void OS::SignalCodeMovingGC() {
+}
+
+
+const char* OS::LocalTimezone(double time) {
+ if (std::isnan(time)) return "";
+ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
+ struct tm* t = localtime(&tv);
+ if (NULL == t) return "";
+ return t->tm_zone;
+}
+
+
+double OS::LocalTimeOffset() {
+ time_t tv = time(NULL);
+ struct tm* t = localtime(&tv);
+ // tm_gmtoff includes any daylight savings offset, so subtract it.
+ return static_cast<double>(t->tm_gmtoff * msPerSecond -
+ (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
+}
+
+
+int OS::StackWalk(Vector<StackFrame> frames) {
+ // If weak link to execinfo lib has failed, ie because we are on 10.4, abort.
+ if (backtrace == NULL) return 0;
+
+ return POSIXBacktraceHelper<backtrace, backtrace_symbols>::StackWalk(frames);
+}
+
+
+VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
+
+
+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()) {
+ 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) {
+ return CommitRegion(address, size, is_executable);
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ return UncommitRegion(address, size);
+}
+
+
+bool VirtualMemory::Guard(void* address) {
+ OS::Guard(address, OS::CommitPageSize());
+ return true;
+}
+
+
+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* 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,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+ kMmapFd,
+ kMmapFdOffset)) {
+ return false;
+ }
+
+ UpdateAllocatedSpaceLimits(address, size);
+ return true;
+}
+
+
+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;
+}
+
+
+bool VirtualMemory::ReleaseRegion(void* address, size_t size) {
+ return munmap(address, size) == 0;
+}
+
+
+bool VirtualMemory::HasLazyCommits() {
+ return false;
+}
+
+
+class MacOSSemaphore : public Semaphore {
+ public:
+ explicit MacOSSemaphore(int count) {
+ int r;
+ r = semaphore_create(mach_task_self(),
+ &semaphore_,
+ SYNC_POLICY_FIFO,
+ count);
+ ASSERT(r == KERN_SUCCESS);
+ }
+
+ ~MacOSSemaphore() {
+ int r;
+ r = semaphore_destroy(mach_task_self(), semaphore_);
+ ASSERT(r == KERN_SUCCESS);
+ }
+
+ void Wait() {
+ int r;
+ do {
+ r = semaphore_wait(semaphore_);
+ ASSERT(r == KERN_SUCCESS || r == KERN_ABORTED);
+ } while (r == KERN_ABORTED);
+ }
+
+ bool Wait(int timeout);
+
+ void Signal() { semaphore_signal(semaphore_); }
+
+ private:
+ semaphore_t semaphore_;
+};
+
+
+bool MacOSSemaphore::Wait(int timeout) {
+ mach_timespec_t ts;
+ ts.tv_sec = timeout / 1000000;
+ ts.tv_nsec = (timeout % 1000000) * 1000;
+ return semaphore_timedwait(semaphore_, ts) != KERN_OPERATION_TIMED_OUT;
+}
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ return new MacOSSemaphore(count);
+}
+
+
+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();
+}
+
+
+void OS::TearDown() {
+ delete limit_mutex;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform-nullos.cc b/chromium/v8/src/platform-nullos.cc
new file mode 100644
index 00000000000..dd5a3ddb32f
--- /dev/null
+++ b/chromium/v8/src/platform-nullos.cc
@@ -0,0 +1,573 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Platform specific code for NULLOS goes here
+
+// Minimal include to get access to abort, fprintf and friends for bootstrapping
+// messages.
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "platform.h"
+#include "vm-state-inl.h"
+
+
+namespace v8 {
+namespace internal {
+
+// Give V8 the opportunity to override the default ceil behaviour.
+double ceiling(double x) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+// Give V8 the opportunity to override the default fmod behavior.
+double modulo(double x, double y) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+double fast_sin(double x) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+double fast_cos(double x) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+double fast_tan(double x) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+double fast_log(double x) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+// Initialize OS class early in the V8 startup.
+void OS::SetUp() {
+ // Seed the random number generator.
+ UNIMPLEMENTED();
+}
+
+
+void OS::PostSetUp() {
+ UNIMPLEMENTED();
+}
+
+
+void OS::TearDown() {
+ UNIMPLEMENTED();
+}
+
+
+// Returns the accumulated user time for thread.
+int OS::GetUserTime(uint32_t* secs, uint32_t* usecs) {
+ UNIMPLEMENTED();
+ *secs = 0;
+ *usecs = 0;
+ return 0;
+}
+
+
+// Returns current time as the number of milliseconds since
+// 00:00:00 UTC, January 1, 1970.
+double OS::TimeCurrentMillis() {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+// Returns ticks in microsecond resolution.
+int64_t OS::Ticks() {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+// Returns a string identifying the current timezone taking into
+// account daylight saving.
+const char* OS::LocalTimezone(double time) {
+ UNIMPLEMENTED();
+ return "<none>";
+}
+
+
+// Returns the daylight savings offset in milliseconds for the given time.
+double OS::DaylightSavingsOffset(double time) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+int OS::GetLastError() {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+// Returns the local time offset in milliseconds east of UTC without
+// taking daylight savings time into account.
+double OS::LocalTimeOffset() {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+// Print (debug) message to console.
+void OS::Print(const char* format, ...) {
+ UNIMPLEMENTED();
+}
+
+
+// Print (debug) message to console.
+void OS::VPrint(const char* format, va_list args) {
+ // Minimalistic implementation for bootstrapping.
+ vfprintf(stdout, format, args);
+}
+
+
+void OS::FPrint(FILE* out, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ VFPrint(out, format, args);
+ va_end(args);
+}
+
+
+void OS::VFPrint(FILE* out, const char* format, va_list args) {
+ vfprintf(out, format, args);
+}
+
+
+// Print error message to console.
+void OS::PrintError(const char* format, ...) {
+ // Minimalistic implementation for bootstrapping.
+ va_list args;
+ va_start(args, format);
+ VPrintError(format, args);
+ va_end(args);
+}
+
+
+// Print error message to console.
+void OS::VPrintError(const char* format, va_list args) {
+ // Minimalistic implementation for bootstrapping.
+ vfprintf(stderr, format, args);
+}
+
+
+int OS::SNPrintF(char* str, size_t size, const char* format, ...) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+int OS::VSNPrintF(char* str, size_t size, const char* format, va_list args) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+uint64_t OS::CpuFeaturesImpliedByPlatform() {
+ return 0;
+}
+
+
+double OS::nan_value() {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+CpuImplementer OS::GetCpuImplementer() {
+ UNIMPLEMENTED();
+}
+
+
+CpuPart OS::GetCpuPart(CpuImplementer implementer) {
+ UNIMPLEMENTED();
+}
+
+
+bool OS::ArmCpuHasFeature(CpuFeature feature) {
+ UNIMPLEMENTED();
+}
+
+
+bool OS::ArmUsingHardFloat() {
+ UNIMPLEMENTED();
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* address) {
+ UNIMPLEMENTED();
+ return false;
+}
+
+
+size_t OS::AllocateAlignment() {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+void* OS::Allocate(const size_t requested,
+ size_t* allocated,
+ bool executable) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+
+void OS::Free(void* buf, const size_t length) {
+ // TODO(1240712): potential system call return value which is ignored here.
+ UNIMPLEMENTED();
+}
+
+
+void OS::Guard(void* address, const size_t size) {
+ UNIMPLEMENTED();
+}
+
+
+void OS::Sleep(int milliseconds) {
+ UNIMPLEMENTED();
+}
+
+
+int OS::NumberOfCores() {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+void OS::Abort() {
+ // Minimalistic implementation for bootstrapping.
+ abort();
+}
+
+
+void OS::DebugBreak() {
+ UNIMPLEMENTED();
+}
+
+
+void OS::DumpBacktrace() {
+ // Currently unsupported.
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+
+void OS::LogSharedLibraryAddresses() {
+ UNIMPLEMENTED();
+}
+
+
+void OS::SignalCodeMovingGC() {
+ UNIMPLEMENTED();
+}
+
+
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+
+VirtualMemory::VirtualMemory() {
+ UNIMPLEMENTED();
+}
+
+
+VirtualMemory::VirtualMemory(size_t size) {
+ UNIMPLEMENTED();
+}
+
+
+VirtualMemory::VirtualMemory(size_t size, void* address_hint) {
+ UNIMPLEMENTED();
+}
+
+
+VirtualMemory::~VirtualMemory() {
+ UNIMPLEMENTED();
+}
+
+
+bool VirtualMemory::IsReserved() {
+ UNIMPLEMENTED();
+ return false;
+}
+
+
+void VirtualMemory::Reset() {
+ UNIMPLEMENTED();
+}
+
+
+bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
+ UNIMPLEMENTED();
+ return false;
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ UNIMPLEMENTED();
+ return false;
+}
+
+
+bool VirtualMemory::Guard(void* address) {
+ UNIMPLEMENTED();
+ return false;
+}
+
+
+void* VirtualMemory::ReserveRegion(size_t size) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+
+bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
+ UNIMPLEMENTED();
+ return false;
+}
+
+
+bool VirtualMemory::UncommitRegion(void* base, size_t size) {
+ UNIMPLEMENTED();
+ return false;
+}
+
+
+bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
+ UNIMPLEMENTED();
+ return false;
+}
+
+
+bool VirtualMemory::HasLazyCommits() {
+ // TODO(alph): implement for the platform.
+ return false;
+}
+
+
+class Thread::PlatformData : public Malloced {
+ public:
+ PlatformData() {
+ UNIMPLEMENTED();
+ }
+
+ void* pd_data_;
+};
+
+
+Thread::Thread(const Options& options)
+ : data_(new PlatformData()),
+ stack_size_(options.stack_size),
+ start_semaphore_(NULL) {
+ set_name(options.name);
+ UNIMPLEMENTED();
+}
+
+
+Thread::Thread(const char* name)
+ : data_(new PlatformData()),
+ stack_size_(0) {
+ set_name(name);
+ UNIMPLEMENTED();
+}
+
+
+Thread::~Thread() {
+ delete data_;
+ UNIMPLEMENTED();
+}
+
+
+void Thread::set_name(const char* name) {
+ strncpy(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
+void Thread::Start() {
+ UNIMPLEMENTED();
+}
+
+
+void Thread::Join() {
+ UNIMPLEMENTED();
+}
+
+
+Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
+ UNIMPLEMENTED();
+ return static_cast<LocalStorageKey>(0);
+}
+
+
+void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
+ UNIMPLEMENTED();
+}
+
+
+void* Thread::GetThreadLocal(LocalStorageKey key) {
+ UNIMPLEMENTED();
+ return NULL;
+}
+
+
+void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ UNIMPLEMENTED();
+}
+
+
+void Thread::YieldCPU() {
+ UNIMPLEMENTED();
+}
+
+
+class NullMutex : public Mutex {
+ public:
+ NullMutex() : data_(NULL) {
+ UNIMPLEMENTED();
+ }
+
+ virtual ~NullMutex() {
+ UNIMPLEMENTED();
+ }
+
+ virtual int Lock() {
+ UNIMPLEMENTED();
+ return 0;
+ }
+
+ virtual int Unlock() {
+ UNIMPLEMENTED();
+ return 0;
+ }
+
+ private:
+ void* data_;
+};
+
+
+Mutex* OS::CreateMutex() {
+ UNIMPLEMENTED();
+ return new NullMutex();
+}
+
+
+class NullSemaphore : public Semaphore {
+ public:
+ explicit NullSemaphore(int count) : data_(NULL) {
+ UNIMPLEMENTED();
+ }
+
+ virtual ~NullSemaphore() {
+ UNIMPLEMENTED();
+ }
+
+ virtual void Wait() {
+ UNIMPLEMENTED();
+ }
+
+ virtual void Signal() {
+ UNIMPLEMENTED();
+ }
+ private:
+ void* data_;
+};
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ UNIMPLEMENTED();
+ return new NullSemaphore(count);
+}
+
+
+class ProfileSampler::PlatformData : public Malloced {
+ public:
+ PlatformData() {
+ UNIMPLEMENTED();
+ }
+};
+
+
+ProfileSampler::ProfileSampler(int interval) {
+ UNIMPLEMENTED();
+ // Shared setup follows.
+ data_ = new PlatformData();
+ interval_ = interval;
+ active_ = false;
+}
+
+
+ProfileSampler::~ProfileSampler() {
+ UNIMPLEMENTED();
+ // Shared tear down follows.
+ delete data_;
+}
+
+
+void ProfileSampler::Start() {
+ UNIMPLEMENTED();
+}
+
+
+void ProfileSampler::Stop() {
+ UNIMPLEMENTED();
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform-openbsd.cc b/chromium/v8/src/platform-openbsd.cc
new file mode 100644
index 00000000000..e59160109f0
--- /dev/null
+++ b/chromium/v8/src/platform-openbsd.cc
@@ -0,0 +1,514 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// 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 <fcntl.h> // open
+#include <unistd.h> // sysconf
+#include <execinfo.h> // backtrace, backtrace_symbols
+#include <strings.h> // index
+#include <errno.h>
+#include <stdarg.h>
+
+#undef MAP_TYPE
+
+#include "v8.h"
+
+#include "platform-posix.h"
+#include "platform.h"
+#include "v8threads.h"
+#include "vm-state-inl.h"
+
+
+namespace v8 {
+namespace internal {
+
+
+static Mutex* limit_mutex = NULL;
+
+
+const char* OS::LocalTimezone(double time) {
+ if (std::isnan(time)) return "";
+ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
+ struct tm* t = localtime(&tv);
+ if (NULL == t) return "";
+ return t->tm_zone;
+}
+
+
+double OS::LocalTimeOffset() {
+ time_t tv = time(NULL);
+ struct tm* t = localtime(&tv);
+ // tm_gmtoff includes any daylight savings offset, so subtract it.
+ return static_cast<double>(t->tm_gmtoff * msPerSecond -
+ (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
+}
+
+
+// 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, 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);
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
+
+
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ ASSERT(limit_mutex != NULL);
+ ScopedLock lock(limit_mutex);
+
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
+ highest_ever_allocated =
+ Max(highest_ever_allocated,
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* address) {
+ return address < lowest_ever_allocated || address >= highest_ever_allocated;
+}
+
+
+void* OS::Allocate(const size_t requested,
+ size_t* allocated,
+ bool is_executable) {
+ const size_t msize = RoundUp(requested, AllocateAlignment());
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
+ void* addr = OS::GetRandomMmapAddr();
+ void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (mbase == MAP_FAILED) {
+ LOG(i::Isolate::Current(),
+ StringEvent("OS::Allocate", "mmap failed"));
+ return NULL;
+ }
+ *allocated = msize;
+ UpdateAllocatedSpaceLimits(mbase, msize);
+ return mbase;
+}
+
+
+void OS::DumpBacktrace() {
+ // Currently unsupported.
+}
+
+
+class PosixMemoryMappedFile : public OS::MemoryMappedFile {
+ public:
+ PosixMemoryMappedFile(FILE* file, void* memory, int size)
+ : file_(file), memory_(memory), size_(size) { }
+ virtual ~PosixMemoryMappedFile();
+ virtual void* memory() { return memory_; }
+ virtual int size() { return size_; }
+ private:
+ FILE* file_;
+ void* memory_;
+ int size_;
+};
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
+ FILE* file = fopen(name, "r+");
+ if (file == NULL) return NULL;
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ FILE* file = fopen(name, "w+");
+ if (file == NULL) return NULL;
+ int result = fwrite(initial, size, 1, file);
+ if (result < 1) {
+ fclose(file);
+ return NULL;
+ }
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+PosixMemoryMappedFile::~PosixMemoryMappedFile() {
+ if (memory_) OS::Free(memory_, size_);
+ fclose(file_);
+}
+
+
+void OS::LogSharedLibraryAddresses() {
+ // 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) {
+ 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;
+ }
+ }
+ free(lib_name);
+ fclose(fp);
+}
+
+
+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(FLAG_gc_fake_mmap, "w+");
+ if (f == NULL) {
+ OS::PrintError("Failed to open %s\n", FLAG_gc_fake_mmap);
+ OS::Abort();
+ }
+ 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);
+
+ int frames_count = backtrace(addresses.start(), frames_size);
+
+ char** symbols = backtrace_symbols(addresses.start(), frames_count);
+ if (symbols == NULL) {
+ return kStackWalkError;
+ }
+
+ for (int i = 0; i < frames_count; i++) {
+ frames[i].address = addresses[i];
+ // Format a text representation of the frame based on the information
+ // available.
+ SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
+ "%s",
+ symbols[i]);
+ // Make sure line termination is in place.
+ frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
+ }
+
+ free(symbols);
+
+ return frames_count;
+}
+
+
+// Constants used for mmap.
+static const int kMmapFd = -1;
+static const int kMmapFdOffset = 0;
+
+
+VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
+
+
+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()) {
+ 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) {
+ return CommitRegion(address, size, is_executable);
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ return UncommitRegion(address, size);
+}
+
+
+bool VirtualMemory::Guard(void* address) {
+ OS::Guard(address, OS::CommitPageSize());
+ return true;
+}
+
+
+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)) {
+ return false;
+ }
+
+ UpdateAllocatedSpaceLimits(base, size);
+ return true;
+}
+
+
+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;
+}
+
+
+bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
+ return munmap(base, size) == 0;
+}
+
+
+bool VirtualMemory::HasLazyCommits() {
+ // TODO(alph): implement for the platform.
+ return false;
+}
+
+
+class OpenBSDSemaphore : public Semaphore {
+ public:
+ explicit OpenBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
+ virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); }
+
+ virtual void Wait();
+ virtual bool Wait(int timeout);
+ virtual void Signal() { sem_post(&sem_); }
+ private:
+ sem_t sem_;
+};
+
+
+void OpenBSDSemaphore::Wait() {
+ while (true) {
+ int result = sem_wait(&sem_);
+ if (result == 0) return; // Successfully got semaphore.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+#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
+
+ // Split timeout into second and nanosecond parts.
+ struct timeval delta;
+ delta.tv_usec = timeout % kOneSecondMicros;
+ delta.tv_sec = timeout / kOneSecondMicros;
+
+ struct timeval current_time;
+ // Get the current time.
+ if (gettimeofday(&current_time, NULL) == -1) {
+ return false;
+ }
+
+ // Calculate time for end of timeout.
+ struct timeval end_time;
+ timeradd(&current_time, &delta, &end_time);
+
+ struct timespec ts;
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
+
+ int to = ts.tv_sec;
+
+ while (true) {
+ int result = sem_trywait(&sem_);
+ if (result == 0) return true; // Successfully got semaphore.
+ if (!to) return false; // Timeout.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ usleep(ts.tv_nsec / 1000);
+ to--;
+ }
+}
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ return new OpenBSDSemaphore(count);
+}
+
+
+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();
+}
+
+
+void OS::TearDown() {
+ delete limit_mutex;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform-posix.cc b/chromium/v8/src/platform-posix.cc
new file mode 100644
index 00000000000..13b819bd1e7
--- /dev/null
+++ b/chromium/v8/src/platform-posix.cc
@@ -0,0 +1,999 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Platform specific code for POSIX goes here. This is not a platform on its
+// own but contains the parts which are the same across POSIX platforms Linux,
+// Mac OS, FreeBSD and OpenBSD.
+
+#include "platform-posix.h"
+
+#include <dlfcn.h>
+#include <pthread.h>
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+#include <pthread_np.h> // for pthread_set_name_np
+#endif
+#include <sched.h> // for sched_yield
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined(__linux__)
+#include <sys/prctl.h> // for prctl
+#endif
+#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
+ defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/sysctl.h> // for sysctl
+#endif
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#undef MAP_TYPE
+
+#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
+#define LOG_TAG "v8"
+#include <android/log.h>
+#endif
+
+#include "v8.h"
+
+#include "codegen.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+// 0 is never a valid thread id.
+static const pthread_t kNoThread = (pthread_t) 0;
+
+
+uint64_t OS::CpuFeaturesImpliedByPlatform() {
+#if defined(__APPLE__)
+ // Mac OS X requires all these to install so we can assume they are present.
+ // These constants are defined by the CPUid instructions.
+ const uint64_t one = 1;
+ return (one << SSE2) | (one << CMOV) | (one << RDTSC) | (one << CPUID);
+#else
+ return 0; // Nothing special about the other systems.
+#endif
+}
+
+
+// Maximum size of the virtual memory. 0 means there is no artificial
+// limit.
+
+intptr_t OS::MaxVirtualMemory() {
+ struct rlimit limit;
+ int result = getrlimit(RLIMIT_DATA, &limit);
+ if (result != 0) return 0;
+ return limit.rlim_cur;
+}
+
+
+int OS::ActivationFrameAlignment() {
+#if V8_TARGET_ARCH_ARM
+ // On EABI ARM targets this is required for fp correctness in the
+ // runtime system.
+ return 8;
+#elif V8_TARGET_ARCH_MIPS
+ return 8;
+#else
+ // Otherwise we just assume 16 byte alignment, i.e.:
+ // - With gcc 4.4 the tree vectorization optimizer can generate code
+ // that requires 16 byte alignment such as movdqa on x86.
+ // - Mac OS X and Solaris (64-bit) activation frames must be 16 byte-aligned;
+ // see "Mac OS X ABI Function Call Guide"
+ return 16;
+#endif
+}
+
+
+intptr_t OS::CommitPageSize() {
+ static intptr_t page_size = getpagesize();
+ return page_size;
+}
+
+
+void OS::Free(void* address, const size_t size) {
+ // TODO(1240712): munmap has a return value which is ignored here.
+ int result = munmap(address, size);
+ USE(result);
+ ASSERT(result == 0);
+}
+
+
+// Get rid of writable permission on code allocations.
+void OS::ProtectCode(void* address, const size_t size) {
+#if defined(__CYGWIN__)
+ DWORD old_protect;
+ VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
+#elif defined(__native_client__)
+ // The Native Client port of V8 uses an interpreter, so
+ // code pages don't need PROT_EXEC.
+ mprotect(address, size, PROT_READ);
+#else
+ mprotect(address, size, PROT_READ | PROT_EXEC);
+#endif
+}
+
+
+// Create guard pages.
+void OS::Guard(void* address, const size_t size) {
+#if defined(__CYGWIN__)
+ DWORD oldprotect;
+ VirtualProtect(address, size, PAGE_READONLY | PAGE_GUARD, &oldprotect);
+#else
+ mprotect(address, size, PROT_NONE);
+#endif
+}
+
+
+void* OS::GetRandomMmapAddr() {
+#if defined(__native_client__)
+ // TODO(bradchen): restore randomization once Native Client gets
+ // smarter about using mmap address hints.
+ // See http://code.google.com/p/nativeclient/issues/3341
+ return NULL;
+#endif
+ 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) {
+#if 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);
+
+ raw_addr &= 0x3ffff000;
+
+# ifdef __sun
+ // For our Solaris/illumos mmap hint, we pick a random address in the bottom
+ // half of the top half of the address space (that is, the third quarter).
+ // Because we do not MAP_FIXED, this will be treated only as a hint -- the
+ // system will not fail to mmap() because something else happens to already
+ // be mapped at our random address. We deliberately set the hint high enough
+ // to get well above the system's break (that is, the heap); Solaris and
+ // illumos will try the hint and if that fails allocate as if there were
+ // no hint at all. The high hint prevents the break from getting hemmed in
+ // at low values, ceding half of the address space to the system heap.
+ raw_addr += 0x80000000;
+# else
+ // 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 += 0x20000000;
+# endif
+#endif
+ return reinterpret_cast<void*>(raw_addr);
+ }
+ return NULL;
+}
+
+
+size_t OS::AllocateAlignment() {
+ return getpagesize();
+}
+
+
+void OS::Sleep(int milliseconds) {
+ useconds_t ms = static_cast<useconds_t>(milliseconds);
+ usleep(1000 * ms);
+}
+
+
+int OS::NumberOfCores() {
+ return sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+
+void OS::Abort() {
+ // Redirect to std abort to signal abnormal program termination.
+ if (FLAG_break_on_abort) {
+ DebugBreak();
+ }
+ abort();
+}
+
+
+void OS::DebugBreak() {
+#if V8_HOST_ARCH_ARM
+ asm("bkpt 0");
+#elif V8_HOST_ARCH_MIPS
+ asm("break");
+#elif V8_HOST_ARCH_IA32
+#if defined(__native_client__)
+ asm("hlt");
+#else
+ asm("int $3");
+#endif // __native_client__
+#elif V8_HOST_ARCH_X64
+ asm("int $3");
+#else
+#error Unsupported host architecture.
+#endif
+}
+
+
+// ----------------------------------------------------------------------------
+// Math functions
+
+double ceiling(double x) {
+ // Correct buggy 'ceil' on some systems (i.e. FreeBSD, OS X 10.5)
+ return (-1.0 < x && x < 0.0) ? -0.0 : ceil(x);
+}
+
+
+double modulo(double x, double y) {
+ return fmod(x, y);
+}
+
+
+#define UNARY_MATH_FUNCTION(name, generator) \
+static UnaryMathFunction fast_##name##_function = NULL; \
+void init_fast_##name##_function() { \
+ fast_##name##_function = generator; \
+} \
+double fast_##name(double x) { \
+ return (*fast_##name##_function)(x); \
+}
+
+UNARY_MATH_FUNCTION(sin, CreateTranscendentalFunction(TranscendentalCache::SIN))
+UNARY_MATH_FUNCTION(cos, CreateTranscendentalFunction(TranscendentalCache::COS))
+UNARY_MATH_FUNCTION(tan, CreateTranscendentalFunction(TranscendentalCache::TAN))
+UNARY_MATH_FUNCTION(log, CreateTranscendentalFunction(TranscendentalCache::LOG))
+UNARY_MATH_FUNCTION(exp, CreateExpFunction())
+UNARY_MATH_FUNCTION(sqrt, CreateSqrtFunction())
+
+#undef UNARY_MATH_FUNCTION
+
+
+void lazily_initialize_fast_exp() {
+ if (fast_exp_function == NULL) {
+ init_fast_exp_function();
+ }
+}
+
+
+double OS::nan_value() {
+ // NAN from math.h is defined in C99 and not in POSIX.
+ return NAN;
+}
+
+
+int OS::GetCurrentProcessId() {
+ return static_cast<int>(getpid());
+}
+
+
+// ----------------------------------------------------------------------------
+// POSIX date/time support.
+//
+
+int OS::GetUserTime(uint32_t* secs, uint32_t* usecs) {
+ struct rusage usage;
+
+ if (getrusage(RUSAGE_SELF, &usage) < 0) return -1;
+ *secs = usage.ru_utime.tv_sec;
+ *usecs = usage.ru_utime.tv_usec;
+ return 0;
+}
+
+
+double OS::TimeCurrentMillis() {
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0) return 0.0;
+ return (static_cast<double>(tv.tv_sec) * 1000) +
+ (static_cast<double>(tv.tv_usec) / 1000);
+}
+
+
+int64_t OS::Ticks() {
+ // gettimeofday has microsecond resolution.
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0)
+ return 0;
+ return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
+}
+
+
+double OS::DaylightSavingsOffset(double time) {
+ if (std::isnan(time)) return nan_value();
+ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
+ struct tm* t = localtime(&tv);
+ if (NULL == t) return nan_value();
+ return t->tm_isdst > 0 ? 3600 * msPerSecond : 0;
+}
+
+
+int OS::GetLastError() {
+ return errno;
+}
+
+
+// ----------------------------------------------------------------------------
+// POSIX stdio support.
+//
+
+FILE* OS::FOpen(const char* path, const char* mode) {
+ FILE* file = fopen(path, mode);
+ if (file == NULL) return NULL;
+ struct stat file_stat;
+ if (fstat(fileno(file), &file_stat) != 0) return NULL;
+ bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
+ if (is_regular_file) return file;
+ fclose(file);
+ return NULL;
+}
+
+
+bool OS::Remove(const char* path) {
+ return (remove(path) == 0);
+}
+
+
+FILE* OS::OpenTemporaryFile() {
+ return tmpfile();
+}
+
+
+const char* const OS::LogFileOpenMode = "w";
+
+
+void OS::Print(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ VPrint(format, args);
+ va_end(args);
+}
+
+
+void OS::VPrint(const char* format, va_list args) {
+#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
+ __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
+#else
+ vprintf(format, args);
+#endif
+}
+
+
+void OS::FPrint(FILE* out, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ VFPrint(out, format, args);
+ va_end(args);
+}
+
+
+void OS::VFPrint(FILE* out, const char* format, va_list args) {
+#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
+ __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, format, args);
+#else
+ vfprintf(out, format, args);
+#endif
+}
+
+
+void OS::PrintError(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ VPrintError(format, args);
+ va_end(args);
+}
+
+
+void OS::VPrintError(const char* format, va_list args) {
+#if defined(ANDROID) && !defined(V8_ANDROID_LOG_STDOUT)
+ __android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, format, args);
+#else
+ vfprintf(stderr, format, args);
+#endif
+}
+
+
+int OS::SNPrintF(Vector<char> str, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = VSNPrintF(str, format, args);
+ va_end(args);
+ return result;
+}
+
+
+int OS::VSNPrintF(Vector<char> str,
+ const char* format,
+ va_list args) {
+ int n = vsnprintf(str.start(), str.length(), format, args);
+ if (n < 0 || n >= str.length()) {
+ // If the length is zero, the assignment fails.
+ if (str.length() > 0)
+ str[str.length() - 1] = '\0';
+ return -1;
+ } else {
+ return n;
+ }
+}
+
+
+#if V8_TARGET_ARCH_IA32
+static void MemMoveWrapper(void* dest, const void* src, size_t size) {
+ memmove(dest, src, size);
+}
+
+
+// Initialize to library version so we can call this at any time during startup.
+static OS::MemMoveFunction memmove_function = &MemMoveWrapper;
+
+// Defined in codegen-ia32.cc.
+OS::MemMoveFunction CreateMemMoveFunction();
+
+// Copy memory area. No restrictions.
+void OS::MemMove(void* dest, const void* src, size_t size) {
+ if (size == 0) return;
+ // Note: here we rely on dependent reads being ordered. This is true
+ // on all architectures we currently support.
+ (*memmove_function)(dest, src, size);
+}
+
+#elif defined(V8_HOST_ARCH_ARM)
+void OS::MemCopyUint16Uint8Wrapper(uint16_t* dest,
+ const uint8_t* src,
+ size_t chars) {
+ uint16_t *limit = dest + chars;
+ while (dest < limit) {
+ *dest++ = static_cast<uint16_t>(*src++);
+ }
+}
+
+
+OS::MemCopyUint8Function OS::memcopy_uint8_function = &OS::MemCopyUint8Wrapper;
+OS::MemCopyUint16Uint8Function OS::memcopy_uint16_uint8_function =
+ &OS::MemCopyUint16Uint8Wrapper;
+// Defined in codegen-arm.cc.
+OS::MemCopyUint8Function CreateMemCopyUint8Function(
+ OS::MemCopyUint8Function stub);
+OS::MemCopyUint16Uint8Function CreateMemCopyUint16Uint8Function(
+ OS::MemCopyUint16Uint8Function stub);
+#endif
+
+
+void OS::PostSetUp() {
+#if V8_TARGET_ARCH_IA32
+ OS::MemMoveFunction generated_memmove = CreateMemMoveFunction();
+ if (generated_memmove != NULL) {
+ memmove_function = generated_memmove;
+ }
+#elif defined(V8_HOST_ARCH_ARM)
+ OS::memcopy_uint8_function =
+ CreateMemCopyUint8Function(&OS::MemCopyUint8Wrapper);
+ OS::memcopy_uint16_uint8_function =
+ CreateMemCopyUint16Uint8Function(&OS::MemCopyUint16Uint8Wrapper);
+#endif
+ init_fast_sin_function();
+ init_fast_cos_function();
+ init_fast_tan_function();
+ init_fast_log_function();
+ // fast_exp is initialized lazily.
+ init_fast_sqrt_function();
+}
+
+
+// ----------------------------------------------------------------------------
+// POSIX string support.
+//
+
+char* OS::StrChr(char* str, int c) {
+ return strchr(str, c);
+}
+
+
+void OS::StrNCpy(Vector<char> dest, const char* src, size_t n) {
+ strncpy(dest.start(), src, n);
+}
+
+
+// ----------------------------------------------------------------------------
+// POSIX thread support.
+//
+
+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()),
+ start_semaphore_(NULL) {
+ set_name(options.name());
+}
+
+
+Thread::~Thread() {
+ delete data_;
+}
+
+
+static void SetThreadName(const char* name) {
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ pthread_set_name_np(pthread_self(), name);
+#elif defined(__NetBSD__)
+ STATIC_ASSERT(Thread::kMaxThreadNameLength <= PTHREAD_MAX_NAMELEN_NP);
+ pthread_setname_np(pthread_self(), "%s", name);
+#elif defined(__APPLE__)
+ // pthread_setname_np is only available in 10.6 or later, so test
+ // for it at runtime.
+ int (*dynamic_pthread_setname_np)(const char*);
+ *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
+ dlsym(RTLD_DEFAULT, "pthread_setname_np");
+ if (dynamic_pthread_setname_np == NULL)
+ return;
+
+ // Mac OS X does not expose the length limit of the name, so hardcode it.
+ static const int kMaxNameLength = 63;
+ STATIC_ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
+ dynamic_pthread_setname_np(name);
+#elif defined(PR_SET_NAME)
+ prctl(PR_SET_NAME,
+ reinterpret_cast<unsigned long>(name), // NOLINT
+ 0, 0, 0);
+#endif
+}
+
+
+static void* ThreadEntry(void* arg) {
+ Thread* thread = reinterpret_cast<Thread*>(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.
+ thread->data()->thread_ = pthread_self();
+ SetThreadName(thread->name());
+ ASSERT(thread->data()->thread_ != kNoThread);
+ thread->NotifyStartedAndRun();
+ return NULL;
+}
+
+
+void Thread::set_name(const char* name) {
+ strncpy(name_, name, sizeof(name_));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
+void Thread::Start() {
+ int result;
+ pthread_attr_t attr;
+ memset(&attr, 0, sizeof(attr));
+ result = pthread_attr_init(&attr);
+ ASSERT_EQ(0, result);
+ // Native client uses default stack size.
+#if !defined(__native_client__)
+ if (stack_size_ > 0) {
+ result = pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
+ ASSERT_EQ(0, result);
+ }
+#endif
+ result = pthread_create(&data_->thread_, &attr, ThreadEntry, this);
+ ASSERT_EQ(0, result);
+ result = pthread_attr_destroy(&attr);
+ ASSERT_EQ(0, result);
+ ASSERT(data_->thread_ != kNoThread);
+ USE(result);
+}
+
+
+void Thread::Join() {
+ pthread_join(data_->thread_, NULL);
+}
+
+
+void Thread::YieldCPU() {
+ int result = sched_yield();
+ ASSERT_EQ(0, result);
+ USE(result);
+}
+
+
+static Thread::LocalStorageKey PthreadKeyToLocalKey(pthread_key_t pthread_key) {
+#if defined(__CYGWIN__)
+ // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
+ // because pthread_key_t is a pointer type on Cygwin. This will probably not
+ // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
+ STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
+ intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
+ return static_cast<Thread::LocalStorageKey>(ptr_key);
+#else
+ return static_cast<Thread::LocalStorageKey>(pthread_key);
+#endif
+}
+
+
+static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
+#if defined(__CYGWIN__)
+ STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
+ intptr_t ptr_key = static_cast<intptr_t>(local_key);
+ return reinterpret_cast<pthread_key_t>(ptr_key);
+#else
+ return static_cast<pthread_key_t>(local_key);
+#endif
+}
+
+
+#ifdef V8_FAST_TLS_SUPPORTED
+
+static Atomic32 tls_base_offset_initialized = 0;
+intptr_t kMacTlsBaseOffset = 0;
+
+// It's safe to do the initialization more that once, but it has to be
+// done at least once.
+static void InitializeTlsBaseOffset() {
+ const size_t kBufferSize = 128;
+ char buffer[kBufferSize];
+ size_t buffer_size = kBufferSize;
+ int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
+ if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
+ V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
+ }
+ // The buffer now contains a string of the form XX.YY.ZZ, where
+ // XX is the major kernel version component.
+ // Make sure the buffer is 0-terminated.
+ buffer[kBufferSize - 1] = '\0';
+ char* period_pos = strchr(buffer, '.');
+ *period_pos = '\0';
+ int kernel_version_major =
+ static_cast<int>(strtol(buffer, NULL, 10)); // NOLINT
+ // The constants below are taken from pthreads.s from the XNU kernel
+ // sources archive at www.opensource.apple.com.
+ if (kernel_version_major < 11) {
+ // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
+ // same offsets.
+#if V8_HOST_ARCH_IA32
+ kMacTlsBaseOffset = 0x48;
+#else
+ kMacTlsBaseOffset = 0x60;
+#endif
+ } else {
+ // 11.x.x (Lion) changed the offset.
+ kMacTlsBaseOffset = 0;
+ }
+
+ Release_Store(&tls_base_offset_initialized, 1);
+}
+
+
+static void CheckFastTls(Thread::LocalStorageKey key) {
+ void* expected = reinterpret_cast<void*>(0x1234CAFE);
+ Thread::SetThreadLocal(key, expected);
+ void* actual = Thread::GetExistingThreadLocal(key);
+ if (expected != actual) {
+ V8_Fatal(__FILE__, __LINE__,
+ "V8 failed to initialize fast TLS on current kernel");
+ }
+ Thread::SetThreadLocal(key, NULL);
+}
+
+#endif // V8_FAST_TLS_SUPPORTED
+
+
+Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
+#ifdef V8_FAST_TLS_SUPPORTED
+ bool check_fast_tls = false;
+ if (tls_base_offset_initialized == 0) {
+ check_fast_tls = true;
+ InitializeTlsBaseOffset();
+ }
+#endif
+ pthread_key_t key;
+ int result = pthread_key_create(&key, NULL);
+ ASSERT_EQ(0, result);
+ USE(result);
+ LocalStorageKey local_key = PthreadKeyToLocalKey(key);
+#ifdef V8_FAST_TLS_SUPPORTED
+ // If we just initialized fast TLS support, make sure it works.
+ if (check_fast_tls) CheckFastTls(local_key);
+#endif
+ return local_key;
+}
+
+
+void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
+ pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
+ int result = pthread_key_delete(pthread_key);
+ ASSERT_EQ(0, result);
+ USE(result);
+}
+
+
+void* Thread::GetThreadLocal(LocalStorageKey key) {
+ pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
+ return pthread_getspecific(pthread_key);
+}
+
+
+void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
+ int result = pthread_setspecific(pthread_key, value);
+ ASSERT_EQ(0, result);
+ USE(result);
+}
+
+
+class POSIXMutex : public Mutex {
+ public:
+ POSIXMutex() {
+ pthread_mutexattr_t attr;
+ memset(&attr, 0, sizeof(attr));
+ int result = pthread_mutexattr_init(&attr);
+ ASSERT(result == 0);
+ result = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ ASSERT(result == 0);
+ result = pthread_mutex_init(&mutex_, &attr);
+ ASSERT(result == 0);
+ result = pthread_mutexattr_destroy(&attr);
+ ASSERT(result == 0);
+ USE(result);
+ }
+
+ virtual ~POSIXMutex() { pthread_mutex_destroy(&mutex_); }
+
+ virtual int Lock() { return pthread_mutex_lock(&mutex_); }
+
+ virtual int Unlock() { return pthread_mutex_unlock(&mutex_); }
+
+ virtual bool TryLock() {
+ int result = pthread_mutex_trylock(&mutex_);
+ // Return false if the lock is busy and locking failed.
+ if (result == EBUSY) {
+ return false;
+ }
+ ASSERT(result == 0); // Verify no other errors.
+ return true;
+ }
+
+ private:
+ pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
+};
+
+
+Mutex* OS::CreateMutex() {
+ return new POSIXMutex();
+}
+
+
+// ----------------------------------------------------------------------------
+// POSIX socket support.
+//
+
+class POSIXSocket : public Socket {
+ public:
+ explicit POSIXSocket() {
+ // Create the socket.
+ socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (IsValid()) {
+ // Allow rapid reuse.
+ static const int kOn = 1;
+ int ret = setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
+ &kOn, sizeof(kOn));
+ ASSERT(ret == 0);
+ USE(ret);
+ }
+ }
+ explicit POSIXSocket(int socket): socket_(socket) { }
+ virtual ~POSIXSocket() { Shutdown(); }
+
+ // Server initialization.
+ bool Bind(const int port);
+ bool Listen(int backlog) const;
+ Socket* Accept() const;
+
+ // Client initialization.
+ bool Connect(const char* host, const char* port);
+
+ // Shutdown socket for both read and write.
+ bool Shutdown();
+
+ // Data Transimission
+ int Send(const char* data, int len) const;
+ int Receive(char* data, int len) const;
+
+ bool SetReuseAddress(bool reuse_address);
+
+ bool IsValid() const { return socket_ != -1; }
+
+ private:
+ int socket_;
+};
+
+
+bool POSIXSocket::Bind(const int port) {
+ if (!IsValid()) {
+ return false;
+ }
+
+ sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_port = htons(port);
+ int status = bind(socket_,
+ BitCast<struct sockaddr *>(&addr),
+ sizeof(addr));
+ return status == 0;
+}
+
+
+bool POSIXSocket::Listen(int backlog) const {
+ if (!IsValid()) {
+ return false;
+ }
+
+ int status = listen(socket_, backlog);
+ return status == 0;
+}
+
+
+Socket* POSIXSocket::Accept() const {
+ if (!IsValid()) {
+ return NULL;
+ }
+
+ int socket;
+ do {
+ socket = accept(socket_, NULL, NULL);
+ } while (socket == -1 && errno == EINTR);
+
+ if (socket == -1) {
+ return NULL;
+ } else {
+ return new POSIXSocket(socket);
+ }
+}
+
+
+bool POSIXSocket::Connect(const char* host, const char* port) {
+ if (!IsValid()) {
+ return false;
+ }
+
+ // Lookup host and port.
+ struct addrinfo *result = NULL;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(addrinfo));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ int status = getaddrinfo(host, port, &hints, &result);
+ if (status != 0) {
+ return false;
+ }
+
+ // Connect.
+ do {
+ status = connect(socket_, result->ai_addr, result->ai_addrlen);
+ } while (status == -1 && errno == EINTR);
+ freeaddrinfo(result);
+ return status == 0;
+}
+
+
+bool POSIXSocket::Shutdown() {
+ if (IsValid()) {
+ // Shutdown socket for both read and write.
+ int status = shutdown(socket_, SHUT_RDWR);
+ close(socket_);
+ socket_ = -1;
+ return status == 0;
+ }
+ return true;
+}
+
+
+int POSIXSocket::Send(const char* data, int len) const {
+ if (len <= 0) return 0;
+ int written = 0;
+ while (written < len) {
+ int status = send(socket_, data + written, len - written, 0);
+ if (status == 0) {
+ break;
+ } else if (status > 0) {
+ written += status;
+ } else if (errno != EINTR) {
+ return 0;
+ }
+ }
+ return written;
+}
+
+
+int POSIXSocket::Receive(char* data, int len) const {
+ if (len <= 0) return 0;
+ int status;
+ do {
+ status = recv(socket_, data, len, 0);
+ } while (status == -1 && errno == EINTR);
+ return (status < 0) ? 0 : status;
+}
+
+
+bool POSIXSocket::SetReuseAddress(bool reuse_address) {
+ int on = reuse_address ? 1 : 0;
+ int status = setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ return status == 0;
+}
+
+
+bool Socket::SetUp() {
+ // Nothing to do on POSIX.
+ return true;
+}
+
+
+int Socket::LastError() {
+ return errno;
+}
+
+
+uint16_t Socket::HToN(uint16_t value) {
+ return htons(value);
+}
+
+
+uint16_t Socket::NToH(uint16_t value) {
+ return ntohs(value);
+}
+
+
+uint32_t Socket::HToN(uint32_t value) {
+ return htonl(value);
+}
+
+
+uint32_t Socket::NToH(uint32_t value) {
+ return ntohl(value);
+}
+
+
+Socket* OS::CreateSocket() {
+ return new POSIXSocket();
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform-posix.h b/chromium/v8/src/platform-posix.h
new file mode 100644
index 00000000000..6b73387cd79
--- /dev/null
+++ b/chromium/v8/src/platform-posix.h
@@ -0,0 +1,106 @@
+// Copyright 2012 the V8 project authors. 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_PLATFORM_POSIX_H_
+#define V8_PLATFORM_POSIX_H_
+
+#if !defined(ANDROID)
+#include <cxxabi.h>
+#endif
+#include <stdio.h>
+
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+// Used by platform implementation files during OS::DumpBacktrace()
+// and OS::StackWalk().
+template<int (*backtrace)(void**, int),
+ char** (*backtrace_symbols)(void* const*, int)>
+struct POSIXBacktraceHelper {
+ static void DumpBacktrace() {
+ void* trace[100];
+ int size = backtrace(trace, ARRAY_SIZE(trace));
+ char** symbols = backtrace_symbols(trace, size);
+ fprintf(stderr, "\n==== C stack trace ===============================\n\n");
+ if (size == 0) {
+ fprintf(stderr, "(empty)\n");
+ } else if (symbols == NULL) {
+ fprintf(stderr, "(no symbols)\n");
+ } else {
+ for (int i = 1; i < size; ++i) {
+ fprintf(stderr, "%2d: ", i);
+ char mangled[201];
+ if (sscanf(symbols[i], "%*[^(]%*[(]%200[^)+]", mangled) == 1) {// NOLINT
+ char* demangled = NULL;
+#if !defined(ANDROID)
+ int status;
+ size_t length;
+ demangled = abi::__cxa_demangle(mangled, NULL, &length, &status);
+#endif
+ fprintf(stderr, "%s\n", demangled != NULL ? demangled : mangled);
+ free(demangled);
+ } else {
+ fprintf(stderr, "??\n");
+ }
+ }
+ }
+ fflush(stderr);
+ free(symbols);
+ }
+
+ static int StackWalk(Vector<OS::StackFrame> frames) {
+ int frames_size = frames.length();
+ ScopedVector<void*> addresses(frames_size);
+
+ int frames_count = backtrace(addresses.start(), frames_size);
+
+ char** symbols = backtrace_symbols(addresses.start(), frames_count);
+ if (symbols == NULL) {
+ return OS::kStackWalkError;
+ }
+
+ for (int i = 0; i < frames_count; i++) {
+ frames[i].address = addresses[i];
+ // Format a text representation of the frame based on the information
+ // available.
+ OS::SNPrintF(MutableCStrVector(frames[i].text, OS::kStackWalkMaxTextLen),
+ "%s", symbols[i]);
+ // Make sure line termination is in place.
+ frames[i].text[OS::kStackWalkMaxTextLen - 1] = '\0';
+ }
+
+ free(symbols);
+
+ return frames_count;
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_PLATFORM_POSIX_H_
diff --git a/chromium/v8/src/platform-solaris.cc b/chromium/v8/src/platform-solaris.cc
new file mode 100644
index 00000000000..b1d88af2939
--- /dev/null
+++ b/chromium/v8/src/platform-solaris.cc
@@ -0,0 +1,491 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Platform specific code for Solaris 10 goes here. For the POSIX comaptible
+// parts the implementation is in platform-posix.cc.
+
+#ifdef __sparc
+# error "V8 does not support the SPARC CPU architecture."
+#endif
+
+#include <sys/stack.h> // for stack alignment
+#include <unistd.h> // getpagesize(), usleep()
+#include <sys/mman.h> // mmap()
+#include <ucontext.h> // walkstack(), getcontext()
+#include <dlfcn.h> // dladdr
+#include <pthread.h>
+#include <semaphore.h>
+#include <time.h>
+#include <sys/time.h> // gettimeofday(), timeradd()
+#include <errno.h>
+#include <ieeefp.h> // finite()
+#include <signal.h> // sigemptyset(), etc
+#include <sys/regset.h>
+
+
+#undef MAP_TYPE
+
+#include "v8.h"
+
+#include "platform-posix.h"
+#include "platform.h"
+#include "v8threads.h"
+#include "vm-state-inl.h"
+
+
+// It seems there is a bug in some Solaris distributions (experienced in
+// SunOS 5.10 Generic_141445-09) which make it difficult or impossible to
+// access signbit() despite the availability of other C99 math functions.
+#ifndef signbit
+namespace std {
+// Test sign - usually defined in math.h
+int signbit(double x) {
+ // We need to take care of the special case of both positive and negative
+ // versions of zero.
+ if (x == 0) {
+ return fpclass(x) & FP_NZERO;
+ } else {
+ // This won't detect negative NaN but that should be okay since we don't
+ // assume that behavior.
+ return x < 0;
+ }
+}
+} // namespace std
+#endif // signbit
+
+namespace v8 {
+namespace internal {
+
+
+static Mutex* limit_mutex = NULL;
+
+
+const char* OS::LocalTimezone(double time) {
+ if (std::isnan(time)) return "";
+ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
+ struct tm* t = localtime(&tv);
+ if (NULL == t) return "";
+ return tzname[0]; // The location of the timezone string on Solaris.
+}
+
+
+double OS::LocalTimeOffset() {
+ tzset();
+ return -static_cast<double>(timezone * msPerSecond);
+}
+
+
+// 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, 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);
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
+
+
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ ASSERT(limit_mutex != NULL);
+ ScopedLock lock(limit_mutex);
+
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
+ highest_ever_allocated =
+ Max(highest_ever_allocated,
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* address) {
+ return address < lowest_ever_allocated || address >= highest_ever_allocated;
+}
+
+
+void* OS::Allocate(const size_t requested,
+ size_t* allocated,
+ 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, MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ if (mbase == MAP_FAILED) {
+ LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
+ return NULL;
+ }
+ *allocated = msize;
+ UpdateAllocatedSpaceLimits(mbase, msize);
+ return mbase;
+}
+
+
+void OS::DumpBacktrace() {
+ // Currently unsupported.
+}
+
+
+class PosixMemoryMappedFile : public OS::MemoryMappedFile {
+ public:
+ PosixMemoryMappedFile(FILE* file, void* memory, int size)
+ : file_(file), memory_(memory), size_(size) { }
+ virtual ~PosixMemoryMappedFile();
+ virtual void* memory() { return memory_; }
+ virtual int size() { return size_; }
+ private:
+ FILE* file_;
+ void* memory_;
+ int size_;
+};
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
+ FILE* file = fopen(name, "r+");
+ if (file == NULL) return NULL;
+
+ fseek(file, 0, SEEK_END);
+ int size = ftell(file);
+
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ FILE* file = fopen(name, "w+");
+ if (file == NULL) return NULL;
+ int result = fwrite(initial, size, 1, file);
+ if (result < 1) {
+ fclose(file);
+ return NULL;
+ }
+ void* memory =
+ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
+ return new PosixMemoryMappedFile(file, memory, size);
+}
+
+
+PosixMemoryMappedFile::~PosixMemoryMappedFile() {
+ if (memory_) munmap(memory_, size_);
+ fclose(file_);
+}
+
+
+void OS::LogSharedLibraryAddresses() {
+}
+
+
+void OS::SignalCodeMovingGC() {
+}
+
+
+struct StackWalker {
+ Vector<OS::StackFrame>& frames;
+ int index;
+};
+
+
+static int StackWalkCallback(uintptr_t pc, int signo, void* data) {
+ struct StackWalker* walker = static_cast<struct StackWalker*>(data);
+ Dl_info info;
+
+ int i = walker->index;
+
+ walker->frames[i].address = reinterpret_cast<void*>(pc);
+
+ // Make sure line termination is in place.
+ walker->frames[i].text[OS::kStackWalkMaxTextLen - 1] = '\0';
+
+ Vector<char> text = MutableCStrVector(walker->frames[i].text,
+ OS::kStackWalkMaxTextLen);
+
+ if (dladdr(reinterpret_cast<void*>(pc), &info) == 0) {
+ OS::SNPrintF(text, "[0x%p]", pc);
+ } else if ((info.dli_fname != NULL && info.dli_sname != NULL)) {
+ // We have symbol info.
+ OS::SNPrintF(text, "%s'%s+0x%x", info.dli_fname, info.dli_sname, pc);
+ } else {
+ // No local symbol info.
+ OS::SNPrintF(text,
+ "%s'0x%p [0x%p]",
+ info.dli_fname,
+ pc - reinterpret_cast<uintptr_t>(info.dli_fbase),
+ pc);
+ }
+ walker->index++;
+ return 0;
+}
+
+
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
+ ucontext_t ctx;
+ struct StackWalker walker = { frames, 0 };
+
+ if (getcontext(&ctx) < 0) return kStackWalkError;
+
+ if (!walkcontext(&ctx, StackWalkCallback, &walker)) {
+ return kStackWalkError;
+ }
+
+ return walker.index;
+}
+
+
+// Constants used for mmap.
+static const int kMmapFd = -1;
+static const int kMmapFdOffset = 0;
+
+
+VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
+
+
+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_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()) {
+ 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) {
+ return CommitRegion(address, size, is_executable);
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ return UncommitRegion(address, size);
+}
+
+
+bool VirtualMemory::Guard(void* address) {
+ OS::Guard(address, OS::CommitPageSize());
+ return true;
+}
+
+
+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(base,
+ size,
+ prot,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ kMmapFd,
+ kMmapFdOffset)) {
+ return false;
+ }
+
+ UpdateAllocatedSpaceLimits(base, size);
+ return true;
+}
+
+
+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;
+}
+
+
+bool VirtualMemory::HasLazyCommits() {
+ // TODO(alph): implement for the platform.
+ return false;
+}
+
+
+class SolarisSemaphore : public Semaphore {
+ public:
+ explicit SolarisSemaphore(int count) { sem_init(&sem_, 0, count); }
+ virtual ~SolarisSemaphore() { sem_destroy(&sem_); }
+
+ virtual void Wait();
+ virtual bool Wait(int timeout);
+ virtual void Signal() { sem_post(&sem_); }
+ private:
+ sem_t sem_;
+};
+
+
+void SolarisSemaphore::Wait() {
+ while (true) {
+ int result = sem_wait(&sem_);
+ if (result == 0) return; // Successfully got semaphore.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+#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
+
+
+#ifndef timeradd
+#define timeradd(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
+ if ((result)->tv_usec >= 1000000) { \
+ ++(result)->tv_sec; \
+ (result)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#endif
+
+
+bool SolarisSemaphore::Wait(int timeout) {
+ const long kOneSecondMicros = 1000000; // NOLINT
+
+ // Split timeout into second and nanosecond parts.
+ struct timeval delta;
+ delta.tv_usec = timeout % kOneSecondMicros;
+ delta.tv_sec = timeout / kOneSecondMicros;
+
+ struct timeval current_time;
+ // Get the current time.
+ if (gettimeofday(&current_time, NULL) == -1) {
+ return false;
+ }
+
+ // Calculate time for end of timeout.
+ struct timeval end_time;
+ timeradd(&current_time, &delta, &end_time);
+
+ struct timespec ts;
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
+ // Wait for semaphore signalled or timeout.
+ while (true) {
+ int result = sem_timedwait(&sem_, &ts);
+ if (result == 0) return true; // Successfully got semaphore.
+ if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
+ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
+ }
+}
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ return new SolarisSemaphore(count);
+}
+
+
+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());
+ srandom(static_cast<unsigned int>(seed));
+ limit_mutex = CreateMutex();
+}
+
+
+void OS::TearDown() {
+ delete limit_mutex;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform-win32.cc b/chromium/v8/src/platform-win32.cc
new file mode 100644
index 00000000000..292c24a3dac
--- /dev/null
+++ b/chromium/v8/src/platform-win32.cc
@@ -0,0 +1,1999 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// Platform specific code for Win32.
+
+// Secure API functions are not available using MinGW with msvcrt.dll
+// on Windows XP. Make sure MINGW_HAS_SECURE_API is not defined to
+// disable definition of secure API functions in standard headers that
+// would conflict with our own implementation.
+#ifdef __MINGW32__
+#include <_mingw.h>
+#ifdef MINGW_HAS_SECURE_API
+#undef MINGW_HAS_SECURE_API
+#endif // MINGW_HAS_SECURE_API
+#endif // __MINGW32__
+
+#define V8_WIN32_HEADERS_FULL
+#include "win32-headers.h"
+
+#include "v8.h"
+
+#include "codegen.h"
+#include "platform.h"
+#include "simulator.h"
+#include "vm-state-inl.h"
+
+#ifdef _MSC_VER
+
+// Case-insensitive bounded string comparisons. Use stricmp() on Win32. Usually
+// defined in strings.h.
+int strncasecmp(const char* s1, const char* s2, int n) {
+ return _strnicmp(s1, s2, n);
+}
+
+#endif // _MSC_VER
+
+
+// Extra functions for MinGW. Most of these are the _s functions which are in
+// the Microsoft Visual Studio C++ CRT.
+#ifdef __MINGW32__
+
+
+#ifndef __MINGW64_VERSION_MAJOR
+
+#define _TRUNCATE 0
+#define STRUNCATE 80
+
+inline void MemoryBarrier() {
+ int barrier = 0;
+ __asm__ __volatile__("xchgl %%eax,%0 ":"=r" (barrier));
+}
+
+#endif // __MINGW64_VERSION_MAJOR
+
+
+int localtime_s(tm* out_tm, const time_t* time) {
+ tm* posix_local_time_struct = localtime(time);
+ if (posix_local_time_struct == NULL) return 1;
+ *out_tm = *posix_local_time_struct;
+ return 0;
+}
+
+
+int fopen_s(FILE** pFile, const char* filename, const char* mode) {
+ *pFile = fopen(filename, mode);
+ return *pFile != NULL ? 0 : 1;
+}
+
+int _vsnprintf_s(char* buffer, size_t sizeOfBuffer, size_t count,
+ const char* format, va_list argptr) {
+ ASSERT(count == _TRUNCATE);
+ return _vsnprintf(buffer, sizeOfBuffer, format, argptr);
+}
+
+
+int strncpy_s(char* dest, size_t dest_size, const char* source, size_t count) {
+ CHECK(source != NULL);
+ CHECK(dest != NULL);
+ CHECK_GT(dest_size, 0);
+
+ if (count == _TRUNCATE) {
+ while (dest_size > 0 && *source != 0) {
+ *(dest++) = *(source++);
+ --dest_size;
+ }
+ if (dest_size == 0) {
+ *(dest - 1) = 0;
+ return STRUNCATE;
+ }
+ } else {
+ while (dest_size > 0 && count > 0 && *source != 0) {
+ *(dest++) = *(source++);
+ --dest_size;
+ --count;
+ }
+ }
+ CHECK_GT(dest_size, 0);
+ *dest = 0;
+ return 0;
+}
+
+#endif // __MINGW32__
+
+// Generate a pseudo-random number in the range 0-2^31-1. Usually
+// defined in stdlib.h. Missing in both Microsoft Visual Studio C++ and MinGW.
+int random() {
+ return rand();
+}
+
+
+namespace v8 {
+namespace internal {
+
+intptr_t OS::MaxVirtualMemory() {
+ return 0;
+}
+
+
+double ceiling(double x) {
+ return ceil(x);
+}
+
+
+static Mutex* limit_mutex = NULL;
+
+#if V8_TARGET_ARCH_IA32
+static void MemMoveWrapper(void* dest, const void* src, size_t size) {
+ memmove(dest, src, size);
+}
+
+
+// Initialize to library version so we can call this at any time during startup.
+static OS::MemMoveFunction memmove_function = &MemMoveWrapper;
+
+// Defined in codegen-ia32.cc.
+OS::MemMoveFunction CreateMemMoveFunction();
+
+// Copy memory area to disjoint memory area.
+void OS::MemMove(void* dest, const void* src, size_t size) {
+ if (size == 0) return;
+ // Note: here we rely on dependent reads being ordered. This is true
+ // on all architectures we currently support.
+ (*memmove_function)(dest, src, size);
+}
+
+#endif // V8_TARGET_ARCH_IA32
+
+#ifdef _WIN64
+typedef double (*ModuloFunction)(double, double);
+static ModuloFunction modulo_function = NULL;
+// Defined in codegen-x64.cc.
+ModuloFunction CreateModuloFunction();
+
+void init_modulo_function() {
+ modulo_function = CreateModuloFunction();
+}
+
+
+double modulo(double x, double y) {
+ // Note: here we rely on dependent reads being ordered. This is true
+ // on all architectures we currently support.
+ return (*modulo_function)(x, y);
+}
+#else // Win32
+
+double modulo(double x, double y) {
+ // Workaround MS fmod bugs. ECMA-262 says:
+ // dividend is finite and divisor is an infinity => result equals dividend
+ // dividend is a zero and divisor is nonzero finite => result equals dividend
+ if (!(std::isfinite(x) && (!std::isfinite(y) && !std::isnan(y))) &&
+ !(x == 0 && (y != 0 && std::isfinite(y)))) {
+ x = fmod(x, y);
+ }
+ return x;
+}
+
+#endif // _WIN64
+
+
+#define UNARY_MATH_FUNCTION(name, generator) \
+static UnaryMathFunction fast_##name##_function = NULL; \
+void init_fast_##name##_function() { \
+ fast_##name##_function = generator; \
+} \
+double fast_##name(double x) { \
+ return (*fast_##name##_function)(x); \
+}
+
+UNARY_MATH_FUNCTION(sin, CreateTranscendentalFunction(TranscendentalCache::SIN))
+UNARY_MATH_FUNCTION(cos, CreateTranscendentalFunction(TranscendentalCache::COS))
+UNARY_MATH_FUNCTION(tan, CreateTranscendentalFunction(TranscendentalCache::TAN))
+UNARY_MATH_FUNCTION(log, CreateTranscendentalFunction(TranscendentalCache::LOG))
+UNARY_MATH_FUNCTION(exp, CreateExpFunction())
+UNARY_MATH_FUNCTION(sqrt, CreateSqrtFunction())
+
+#undef UNARY_MATH_FUNCTION
+
+
+void lazily_initialize_fast_exp() {
+ if (fast_exp_function == NULL) {
+ init_fast_exp_function();
+ }
+}
+
+
+void MathSetup() {
+#ifdef _WIN64
+ init_modulo_function();
+#endif
+ init_fast_sin_function();
+ init_fast_cos_function();
+ init_fast_tan_function();
+ init_fast_log_function();
+ // fast_exp is initialized lazily.
+ init_fast_sqrt_function();
+}
+
+
+// ----------------------------------------------------------------------------
+// The Time class represents time on win32. A timestamp is represented as
+// 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.
+
+class Time {
+ public:
+ // Constructors.
+ Time();
+ explicit Time(double jstime);
+ Time(int year, int mon, int day, int hour, int min, int sec);
+
+ // Convert timestamp to JavaScript representation.
+ double ToJSTime();
+
+ // Set timestamp to current time.
+ void SetToCurrentTime();
+
+ // Returns the local timezone offset in milliseconds east of UTC. This is
+ // the number of milliseconds you must add to UTC to get local time, i.e.
+ // LocalOffset(CET) = 3600000 and LocalOffset(PST) = -28800000. This
+ // routine also takes into account whether daylight saving is effect
+ // at the time.
+ int64_t LocalOffset();
+
+ // Returns the daylight savings time offset for the time in milliseconds.
+ int64_t DaylightSavingsOffset();
+
+ // Returns a string identifying the current timezone for the
+ // timestamp taking into account daylight saving.
+ char* LocalTimezone();
+
+ private:
+ // Constants for time conversion.
+ static const int64_t kTimeEpoc = 116444736000000000LL;
+ static const int64_t kTimeScaler = 10000;
+ static const int64_t kMsPerMinute = 60000;
+
+ // Constants for timezone information.
+ static const int kTzNameSize = 128;
+ static const bool kShortTzNames = false;
+
+ // Timezone information. We need to have static buffers for the
+ // timezone names because we return pointers to these in
+ // LocalTimezone().
+ static bool tz_initialized_;
+ static TIME_ZONE_INFORMATION tzinfo_;
+ static char std_tz_name_[kTzNameSize];
+ static char dst_tz_name_[kTzNameSize];
+
+ // Initialize the timezone information (if not already done).
+ static void TzSet();
+
+ // Guess the name of the timezone from the bias.
+ static const char* GuessTimezoneNameFromBias(int bias);
+
+ // Return whether or not daylight savings time is in effect at this time.
+ bool InDST();
+
+ // Return the difference (in milliseconds) between this timestamp and
+ // another timestamp.
+ int64_t Diff(Time* other);
+
+ // Accessor for FILETIME representation.
+ FILETIME& ft() { return time_.ft_; }
+
+ // Accessor for integer representation.
+ int64_t& t() { return time_.t_; }
+
+ // Although win32 uses 64-bit integers for representing timestamps,
+ // these are packed into a FILETIME structure. The FILETIME structure
+ // is just a struct representing a 64-bit integer. The TimeStamp union
+ // allows access to both a FILETIME and an integer representation of
+ // the timestamp.
+ union TimeStamp {
+ FILETIME ft_;
+ int64_t t_;
+ };
+
+ TimeStamp time_;
+};
+
+
+// Static variables.
+bool Time::tz_initialized_ = false;
+TIME_ZONE_INFORMATION Time::tzinfo_;
+char Time::std_tz_name_[kTzNameSize];
+char Time::dst_tz_name_[kTzNameSize];
+
+
+// Initialize timestamp to start of epoc.
+Time::Time() {
+ t() = 0;
+}
+
+
+// Initialize timestamp from a JavaScript timestamp.
+Time::Time(double jstime) {
+ t() = static_cast<int64_t>(jstime) * kTimeScaler + kTimeEpoc;
+}
+
+
+// Initialize timestamp from date/time components.
+Time::Time(int year, int mon, int day, int hour, int min, int sec) {
+ SYSTEMTIME st;
+ st.wYear = year;
+ st.wMonth = mon;
+ st.wDay = day;
+ st.wHour = hour;
+ st.wMinute = min;
+ st.wSecond = sec;
+ st.wMilliseconds = 0;
+ SystemTimeToFileTime(&st, &ft());
+}
+
+
+// Convert timestamp to JavaScript timestamp.
+double Time::ToJSTime() {
+ return static_cast<double>((t() - kTimeEpoc) / kTimeScaler);
+}
+
+
+// Guess the name of the timezone from the bias.
+// The guess is very biased towards the northern hemisphere.
+const char* Time::GuessTimezoneNameFromBias(int bias) {
+ static const int kHour = 60;
+ switch (-bias) {
+ case -9*kHour: return "Alaska";
+ case -8*kHour: return "Pacific";
+ case -7*kHour: return "Mountain";
+ case -6*kHour: return "Central";
+ case -5*kHour: return "Eastern";
+ case -4*kHour: return "Atlantic";
+ case 0*kHour: return "GMT";
+ case +1*kHour: return "Central Europe";
+ case +2*kHour: return "Eastern Europe";
+ case +3*kHour: return "Russia";
+ case +5*kHour + 30: return "India";
+ case +8*kHour: return "China";
+ case +9*kHour: return "Japan";
+ case +12*kHour: return "New Zealand";
+ default: return "Local";
+ }
+}
+
+
+// Initialize timezone information. The timezone information is obtained from
+// windows. If we cannot get the timezone information we fall back to CET.
+// Please notice that this code is not thread-safe.
+void Time::TzSet() {
+ // Just return if timezone information has already been initialized.
+ if (tz_initialized_) return;
+
+ // Initialize POSIX time zone data.
+ _tzset();
+ // Obtain timezone information from operating system.
+ memset(&tzinfo_, 0, sizeof(tzinfo_));
+ if (GetTimeZoneInformation(&tzinfo_) == TIME_ZONE_ID_INVALID) {
+ // If we cannot get timezone information we fall back to CET.
+ tzinfo_.Bias = -60;
+ tzinfo_.StandardDate.wMonth = 10;
+ tzinfo_.StandardDate.wDay = 5;
+ tzinfo_.StandardDate.wHour = 3;
+ tzinfo_.StandardBias = 0;
+ tzinfo_.DaylightDate.wMonth = 3;
+ tzinfo_.DaylightDate.wDay = 5;
+ tzinfo_.DaylightDate.wHour = 2;
+ tzinfo_.DaylightBias = -60;
+ }
+
+ // Make standard and DST timezone names.
+ WideCharToMultiByte(CP_UTF8, 0, tzinfo_.StandardName, -1,
+ std_tz_name_, kTzNameSize, NULL, NULL);
+ std_tz_name_[kTzNameSize - 1] = '\0';
+ WideCharToMultiByte(CP_UTF8, 0, tzinfo_.DaylightName, -1,
+ dst_tz_name_, kTzNameSize, NULL, NULL);
+ dst_tz_name_[kTzNameSize - 1] = '\0';
+
+ // If OS returned empty string or resource id (like "@tzres.dll,-211")
+ // simply guess the name from the UTC bias of the timezone.
+ // To properly resolve the resource identifier requires a library load,
+ // which is not possible in a sandbox.
+ if (std_tz_name_[0] == '\0' || std_tz_name_[0] == '@') {
+ OS::SNPrintF(Vector<char>(std_tz_name_, kTzNameSize - 1),
+ "%s Standard Time",
+ GuessTimezoneNameFromBias(tzinfo_.Bias));
+ }
+ if (dst_tz_name_[0] == '\0' || dst_tz_name_[0] == '@') {
+ OS::SNPrintF(Vector<char>(dst_tz_name_, kTzNameSize - 1),
+ "%s Daylight Time",
+ GuessTimezoneNameFromBias(tzinfo_.Bias));
+ }
+
+ // Timezone information initialized.
+ tz_initialized_ = true;
+}
+
+
+// Return the difference in milliseconds between this and another timestamp.
+int64_t Time::Diff(Time* other) {
+ return (t() - other->t()) / kTimeScaler;
+}
+
+
+// Set timestamp to current time.
+void Time::SetToCurrentTime() {
+ // The default GetSystemTimeAsFileTime has a ~15.5ms resolution.
+ // Because we're fast, we like fast timers which have at least a
+ // 1ms resolution.
+ //
+ // timeGetTime() provides 1ms granularity when combined with
+ // timeBeginPeriod(). If the host application for v8 wants fast
+ // timers, it can use timeBeginPeriod to increase the resolution.
+ //
+ // Using timeGetTime() has a drawback because it is a 32bit value
+ // and hence rolls-over every ~49days.
+ //
+ // To use the clock, we use GetSystemTimeAsFileTime as our base;
+ // and then use timeGetTime to extrapolate current time from the
+ // start time. To deal with rollovers, we resync the clock
+ // any time when more than kMaxClockElapsedTime has passed or
+ // whenever timeGetTime creates a rollover.
+
+ static bool initialized = false;
+ static TimeStamp init_time;
+ static DWORD init_ticks;
+ static const int64_t kHundredNanosecondsPerSecond = 10000000;
+ static const int64_t kMaxClockElapsedTime =
+ 60*kHundredNanosecondsPerSecond; // 1 minute
+
+ // If we are uninitialized, we need to resync the clock.
+ bool needs_resync = !initialized;
+
+ // Get the current time.
+ TimeStamp time_now;
+ GetSystemTimeAsFileTime(&time_now.ft_);
+ DWORD ticks_now = timeGetTime();
+
+ // Check if we need to resync due to clock rollover.
+ needs_resync |= ticks_now < init_ticks;
+
+ // Check if we need to resync due to elapsed time.
+ needs_resync |= (time_now.t_ - init_time.t_) > kMaxClockElapsedTime;
+
+ // Check if we need to resync due to backwards time change.
+ needs_resync |= time_now.t_ < init_time.t_;
+
+ // Resync the clock if necessary.
+ if (needs_resync) {
+ GetSystemTimeAsFileTime(&init_time.ft_);
+ init_ticks = ticks_now = timeGetTime();
+ initialized = true;
+ }
+
+ // Finally, compute the actual time. Why is this so hard.
+ DWORD elapsed = ticks_now - init_ticks;
+ this->time_.t_ = init_time.t_ + (static_cast<int64_t>(elapsed) * 10000);
+}
+
+
+// Return the local timezone offset in milliseconds east of UTC. This
+// takes into account whether daylight saving is in effect at the time.
+// Only times in the 32-bit Unix range may be passed to this function.
+// Also, adding the time-zone offset to the input must not overflow.
+// The function EquivalentTime() in date.js guarantees this.
+int64_t Time::LocalOffset() {
+ // Initialize timezone information, if needed.
+ TzSet();
+
+ Time rounded_to_second(*this);
+ rounded_to_second.t() = rounded_to_second.t() / 1000 / kTimeScaler *
+ 1000 * kTimeScaler;
+ // Convert to local time using POSIX localtime function.
+ // Windows XP Service Pack 3 made SystemTimeToTzSpecificLocalTime()
+ // very slow. Other browsers use localtime().
+
+ // Convert from JavaScript milliseconds past 1/1/1970 0:00:00 to
+ // POSIX seconds past 1/1/1970 0:00:00.
+ double unchecked_posix_time = rounded_to_second.ToJSTime() / 1000;
+ if (unchecked_posix_time > INT_MAX || unchecked_posix_time < 0) {
+ return 0;
+ }
+ // Because _USE_32BIT_TIME_T is defined, time_t is a 32-bit int.
+ time_t posix_time = static_cast<time_t>(unchecked_posix_time);
+
+ // Convert to local time, as struct with fields for day, hour, year, etc.
+ tm posix_local_time_struct;
+ if (localtime_s(&posix_local_time_struct, &posix_time)) return 0;
+
+ if (posix_local_time_struct.tm_isdst > 0) {
+ return (tzinfo_.Bias + tzinfo_.DaylightBias) * -kMsPerMinute;
+ } else if (posix_local_time_struct.tm_isdst == 0) {
+ return (tzinfo_.Bias + tzinfo_.StandardBias) * -kMsPerMinute;
+ } else {
+ return tzinfo_.Bias * -kMsPerMinute;
+ }
+}
+
+
+// Return whether or not daylight savings time is in effect at this time.
+bool Time::InDST() {
+ // Initialize timezone information, if needed.
+ TzSet();
+
+ // Determine if DST is in effect at the specified time.
+ bool in_dst = false;
+ if (tzinfo_.StandardDate.wMonth != 0 || tzinfo_.DaylightDate.wMonth != 0) {
+ // Get the local timezone offset for the timestamp in milliseconds.
+ int64_t offset = LocalOffset();
+
+ // Compute the offset for DST. The bias parameters in the timezone info
+ // are specified in minutes. These must be converted to milliseconds.
+ int64_t dstofs = -(tzinfo_.Bias + tzinfo_.DaylightBias) * kMsPerMinute;
+
+ // If the local time offset equals the timezone bias plus the daylight
+ // bias then DST is in effect.
+ in_dst = offset == dstofs;
+ }
+
+ return in_dst;
+}
+
+
+// Return the daylight savings time offset for this time.
+int64_t Time::DaylightSavingsOffset() {
+ return InDST() ? 60 * kMsPerMinute : 0;
+}
+
+
+// Returns a string identifying the current timezone for the
+// timestamp taking into account daylight saving.
+char* Time::LocalTimezone() {
+ // Return the standard or DST time zone name based on whether daylight
+ // saving is in effect at the given time.
+ return InDST() ? dst_tz_name_ : std_tz_name_;
+}
+
+
+void OS::PostSetUp() {
+ // Math functions depend on CPU features therefore they are initialized after
+ // CPU.
+ MathSetup();
+#if V8_TARGET_ARCH_IA32
+ OS::MemMoveFunction generated_memmove = CreateMemMoveFunction();
+ if (generated_memmove != NULL) {
+ memmove_function = generated_memmove;
+ }
+#endif
+}
+
+
+// Returns the accumulated user time for thread.
+int OS::GetUserTime(uint32_t* secs, uint32_t* usecs) {
+ FILETIME dummy;
+ uint64_t usertime;
+
+ // Get the amount of time that the thread has executed in user mode.
+ if (!GetThreadTimes(GetCurrentThread(), &dummy, &dummy, &dummy,
+ reinterpret_cast<FILETIME*>(&usertime))) return -1;
+
+ // Adjust the resolution to micro-seconds.
+ usertime /= 10;
+
+ // Convert to seconds and microseconds
+ *secs = static_cast<uint32_t>(usertime / 1000000);
+ *usecs = static_cast<uint32_t>(usertime % 1000000);
+ return 0;
+}
+
+
+// Returns current time as the number of milliseconds since
+// 00:00:00 UTC, January 1, 1970.
+double OS::TimeCurrentMillis() {
+ Time t;
+ t.SetToCurrentTime();
+ return t.ToJSTime();
+}
+
+
+// Returns the tickcounter based on timeGetTime.
+int64_t OS::Ticks() {
+ return timeGetTime() * 1000; // Convert to microseconds.
+}
+
+
+// Returns a string identifying the current timezone taking into
+// account daylight saving.
+const char* OS::LocalTimezone(double time) {
+ return Time(time).LocalTimezone();
+}
+
+
+// Returns the local time offset in milliseconds east of UTC without
+// taking daylight savings time into account.
+double OS::LocalTimeOffset() {
+ // Use current time, rounded to the millisecond.
+ Time t(TimeCurrentMillis());
+ // Time::LocalOffset inlcudes any daylight savings offset, so subtract it.
+ return static_cast<double>(t.LocalOffset() - t.DaylightSavingsOffset());
+}
+
+
+// Returns the daylight savings offset in milliseconds for the given
+// time.
+double OS::DaylightSavingsOffset(double time) {
+ int64_t offset = Time(time).DaylightSavingsOffset();
+ return static_cast<double>(offset);
+}
+
+
+int OS::GetLastError() {
+ return ::GetLastError();
+}
+
+
+int OS::GetCurrentProcessId() {
+ return static_cast<int>(::GetCurrentProcessId());
+}
+
+
+// ----------------------------------------------------------------------------
+// Win32 console output.
+//
+// If a Win32 application is linked as a console application it has a normal
+// standard output and standard error. In this case normal printf works fine
+// for output. However, if the application is linked as a GUI application,
+// the process doesn't have a console, and therefore (debugging) output is lost.
+// This is the case if we are embedded in a windows program (like a browser).
+// In order to be able to get debug output in this case the the debugging
+// facility using OutputDebugString. This output goes to the active debugger
+// for the process (if any). Else the output can be monitored using DBMON.EXE.
+
+enum OutputMode {
+ UNKNOWN, // Output method has not yet been determined.
+ CONSOLE, // Output is written to stdout.
+ ODS // Output is written to debug facility.
+};
+
+static OutputMode output_mode = UNKNOWN; // Current output mode.
+
+
+// Determine if the process has a console for output.
+static bool HasConsole() {
+ // Only check the first time. Eventual race conditions are not a problem,
+ // because all threads will eventually determine the same mode.
+ if (output_mode == UNKNOWN) {
+ // We cannot just check that the standard output is attached to a console
+ // because this would fail if output is redirected to a file. Therefore we
+ // say that a process does not have an output console if either the
+ // standard output handle is invalid or its file type is unknown.
+ if (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE &&
+ GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) != FILE_TYPE_UNKNOWN)
+ output_mode = CONSOLE;
+ else
+ output_mode = ODS;
+ }
+ return output_mode == CONSOLE;
+}
+
+
+static void VPrintHelper(FILE* stream, const char* format, va_list args) {
+ if (HasConsole()) {
+ vfprintf(stream, format, args);
+ } else {
+ // It is important to use safe print here in order to avoid
+ // overflowing the buffer. We might truncate the output, but this
+ // does not crash.
+ EmbeddedVector<char, 4096> buffer;
+ OS::VSNPrintF(buffer, format, args);
+ OutputDebugStringA(buffer.start());
+ }
+}
+
+
+FILE* OS::FOpen(const char* path, const char* mode) {
+ FILE* result;
+ if (fopen_s(&result, path, mode) == 0) {
+ return result;
+ } else {
+ return NULL;
+ }
+}
+
+
+bool OS::Remove(const char* path) {
+ return (DeleteFileA(path) != 0);
+}
+
+
+FILE* OS::OpenTemporaryFile() {
+ // tmpfile_s tries to use the root dir, don't use it.
+ char tempPathBuffer[MAX_PATH];
+ DWORD path_result = 0;
+ path_result = GetTempPathA(MAX_PATH, tempPathBuffer);
+ if (path_result > MAX_PATH || path_result == 0) return NULL;
+ UINT name_result = 0;
+ char tempNameBuffer[MAX_PATH];
+ name_result = GetTempFileNameA(tempPathBuffer, "", 0, tempNameBuffer);
+ if (name_result == 0) return NULL;
+ FILE* result = FOpen(tempNameBuffer, "w+"); // Same mode as tmpfile uses.
+ if (result != NULL) {
+ Remove(tempNameBuffer); // Delete on close.
+ }
+ return result;
+}
+
+
+// Open log file in binary mode to avoid /n -> /r/n conversion.
+const char* const OS::LogFileOpenMode = "wb";
+
+
+// Print (debug) message to console.
+void OS::Print(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ VPrint(format, args);
+ va_end(args);
+}
+
+
+void OS::VPrint(const char* format, va_list args) {
+ VPrintHelper(stdout, format, args);
+}
+
+
+void OS::FPrint(FILE* out, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ VFPrint(out, format, args);
+ va_end(args);
+}
+
+
+void OS::VFPrint(FILE* out, const char* format, va_list args) {
+ VPrintHelper(out, format, args);
+}
+
+
+// Print error message to console.
+void OS::PrintError(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ VPrintError(format, args);
+ va_end(args);
+}
+
+
+void OS::VPrintError(const char* format, va_list args) {
+ VPrintHelper(stderr, format, args);
+}
+
+
+int OS::SNPrintF(Vector<char> str, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = VSNPrintF(str, format, args);
+ va_end(args);
+ return result;
+}
+
+
+int OS::VSNPrintF(Vector<char> str, const char* format, va_list args) {
+ int n = _vsnprintf_s(str.start(), str.length(), _TRUNCATE, format, args);
+ // Make sure to zero-terminate the string if the output was
+ // truncated or if there was an error.
+ if (n < 0 || n >= str.length()) {
+ if (str.length() > 0)
+ str[str.length() - 1] = '\0';
+ return -1;
+ } else {
+ return n;
+ }
+}
+
+
+char* OS::StrChr(char* str, int c) {
+ return const_cast<char*>(strchr(str, c));
+}
+
+
+void OS::StrNCpy(Vector<char> dest, const char* src, size_t n) {
+ // Use _TRUNCATE or strncpy_s crashes (by design) if buffer is too small.
+ size_t buffer_size = static_cast<size_t>(dest.length());
+ if (n + 1 > buffer_size) // count for trailing '\0'
+ n = _TRUNCATE;
+ int result = strncpy_s(dest.start(), dest.length(), src, n);
+ USE(result);
+ ASSERT(result == 0 || (n == _TRUNCATE && result == STRUNCATE));
+}
+
+
+#undef _TRUNCATE
+#undef STRUNCATE
+
+// 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, 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);
+static void* highest_ever_allocated = reinterpret_cast<void*>(0);
+
+
+static void UpdateAllocatedSpaceLimits(void* address, int size) {
+ ASSERT(limit_mutex != NULL);
+ ScopedLock lock(limit_mutex);
+
+ lowest_ever_allocated = Min(lowest_ever_allocated, address);
+ highest_ever_allocated =
+ Max(highest_ever_allocated,
+ reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
+}
+
+
+bool OS::IsOutsideAllocatedSpace(void* pointer) {
+ if (pointer < lowest_ever_allocated || pointer >= highest_ever_allocated)
+ return true;
+ // Ask the Windows API
+ if (IsBadWritePtr(pointer, 1))
+ return true;
+ return false;
+}
+
+
+// Get the system's page size used by VirtualAlloc() or the next power
+// of two. The reason for always returning a power of two is that the
+// rounding up in OS::Allocate expects that.
+static size_t GetPageSize() {
+ static size_t page_size = 0;
+ if (page_size == 0) {
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ page_size = RoundUpToPowerOf2(info.dwPageSize);
+ }
+ return page_size;
+}
+
+
+// The allocation alignment is the guaranteed alignment for
+// VirtualAlloc'ed blocks of memory.
+size_t OS::AllocateAlignment() {
+ static size_t allocate_alignment = 0;
+ if (allocate_alignment == 0) {
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ allocate_alignment = info.dwAllocationGranularity;
+ }
+ return allocate_alignment;
+}
+
+
+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) {
+ // The address range used to randomize RWX allocations in OS::Allocate
+ // Try not to map pages into the default range that windows loads DLLs
+ // Use a multiple of 64k to prevent committing unused memory.
+ // Note: This does not guarantee RWX regions will be within the
+ // range kAllocationRandomAddressMin to kAllocationRandomAddressMax
+#ifdef V8_HOST_ARCH_64_BIT
+ static const intptr_t kAllocationRandomAddressMin = 0x0000000080000000;
+ static const intptr_t kAllocationRandomAddressMax = 0x000003FFFFFF0000;
+#else
+ static const intptr_t kAllocationRandomAddressMin = 0x04000000;
+ static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000;
+#endif
+ uintptr_t address = (V8::RandomPrivate(isolate) << kPageSizeBits)
+ | kAllocationRandomAddressMin;
+ address &= kAllocationRandomAddressMax;
+ return reinterpret_cast<void *>(address);
+ }
+ return NULL;
+}
+
+
+static void* RandomizedVirtualAlloc(size_t size, int action, int protection) {
+ LPVOID base = NULL;
+
+ if (protection == PAGE_EXECUTE_READWRITE || protection == PAGE_NOACCESS) {
+ // For exectutable pages try and randomize the allocation address
+ for (size_t attempts = 0; base == NULL && attempts < 3; ++attempts) {
+ base = VirtualAlloc(OS::GetRandomMmapAddr(), size, action, protection);
+ }
+ }
+
+ // After three attempts give up and let the OS find an address to use.
+ if (base == NULL) base = VirtualAlloc(NULL, size, action, protection);
+
+ return base;
+}
+
+
+void* OS::Allocate(const size_t requested,
+ size_t* allocated,
+ bool is_executable) {
+ // VirtualAlloc rounds allocated size to page size automatically.
+ size_t msize = RoundUp(requested, static_cast<int>(GetPageSize()));
+
+ // Windows XP SP2 allows Data Excution Prevention (DEP).
+ int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
+
+ LPVOID mbase = RandomizedVirtualAlloc(msize,
+ MEM_COMMIT | MEM_RESERVE,
+ prot);
+
+ if (mbase == NULL) {
+ LOG(ISOLATE, StringEvent("OS::Allocate", "VirtualAlloc failed"));
+ return NULL;
+ }
+
+ ASSERT(IsAligned(reinterpret_cast<size_t>(mbase), OS::AllocateAlignment()));
+
+ *allocated = msize;
+ UpdateAllocatedSpaceLimits(mbase, static_cast<int>(msize));
+ return mbase;
+}
+
+
+void OS::Free(void* address, const size_t size) {
+ // TODO(1240712): VirtualFree has a return value which is ignored here.
+ VirtualFree(address, 0, MEM_RELEASE);
+ USE(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);
+}
+
+
+void OS::Guard(void* address, const size_t size) {
+ DWORD oldprotect;
+ VirtualProtect(address, size, PAGE_READONLY | PAGE_GUARD, &oldprotect);
+}
+
+
+void OS::Sleep(int milliseconds) {
+ ::Sleep(milliseconds);
+}
+
+
+int OS::NumberOfCores() {
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ return info.dwNumberOfProcessors;
+}
+
+
+void OS::Abort() {
+ if (IsDebuggerPresent() || FLAG_break_on_abort) {
+ DebugBreak();
+ } else {
+ // Make the MSVCRT do a silent abort.
+ raise(SIGABRT);
+ }
+}
+
+
+void OS::DebugBreak() {
+#ifdef _MSC_VER
+ __debugbreak();
+#else
+ ::DebugBreak();
+#endif
+}
+
+
+void OS::DumpBacktrace() {
+ // Currently unsupported.
+}
+
+
+class Win32MemoryMappedFile : public OS::MemoryMappedFile {
+ public:
+ Win32MemoryMappedFile(HANDLE file,
+ HANDLE file_mapping,
+ void* memory,
+ int size)
+ : file_(file),
+ file_mapping_(file_mapping),
+ memory_(memory),
+ size_(size) { }
+ virtual ~Win32MemoryMappedFile();
+ virtual void* memory() { return memory_; }
+ virtual int size() { return size_; }
+ private:
+ HANDLE file_;
+ HANDLE file_mapping_;
+ void* memory_;
+ int size_;
+};
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
+ // Open a physical file
+ HANDLE file = CreateFileA(name, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (file == INVALID_HANDLE_VALUE) return NULL;
+
+ int size = static_cast<int>(GetFileSize(file, NULL));
+
+ // Create a file mapping for the physical file
+ HANDLE file_mapping = CreateFileMapping(file, NULL,
+ PAGE_READWRITE, 0, static_cast<DWORD>(size), NULL);
+ if (file_mapping == NULL) return NULL;
+
+ // Map a view of the file into memory
+ void* memory = MapViewOfFile(file_mapping, FILE_MAP_ALL_ACCESS, 0, 0, size);
+ return new Win32MemoryMappedFile(file, file_mapping, memory, size);
+}
+
+
+OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
+ void* initial) {
+ // Open a physical file
+ HANDLE file = CreateFileA(name, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
+ if (file == NULL) return NULL;
+ // Create a file mapping for the physical file
+ HANDLE file_mapping = CreateFileMapping(file, NULL,
+ PAGE_READWRITE, 0, static_cast<DWORD>(size), NULL);
+ if (file_mapping == NULL) return NULL;
+ // Map a view of the file into memory
+ void* memory = MapViewOfFile(file_mapping, FILE_MAP_ALL_ACCESS, 0, 0, size);
+ if (memory) OS::MemMove(memory, initial, size);
+ return new Win32MemoryMappedFile(file, file_mapping, memory, size);
+}
+
+
+Win32MemoryMappedFile::~Win32MemoryMappedFile() {
+ if (memory_ != NULL)
+ UnmapViewOfFile(memory_);
+ CloseHandle(file_mapping_);
+ CloseHandle(file_);
+}
+
+
+// The following code loads functions defined in DbhHelp.h and TlHelp32.h
+// dynamically. This is to avoid being depending on dbghelp.dll and
+// tlhelp32.dll when running (the functions in tlhelp32.dll have been moved to
+// kernel32.dll at some point so loading functions defines in TlHelp32.h
+// dynamically might not be necessary any more - for some versions of Windows?).
+
+// Function pointers to functions dynamically loaded from dbghelp.dll.
+#define DBGHELP_FUNCTION_LIST(V) \
+ V(SymInitialize) \
+ V(SymGetOptions) \
+ V(SymSetOptions) \
+ V(SymGetSearchPath) \
+ V(SymLoadModule64) \
+ V(StackWalk64) \
+ V(SymGetSymFromAddr64) \
+ V(SymGetLineFromAddr64) \
+ V(SymFunctionTableAccess64) \
+ V(SymGetModuleBase64)
+
+// Function pointers to functions dynamically loaded from dbghelp.dll.
+#define TLHELP32_FUNCTION_LIST(V) \
+ V(CreateToolhelp32Snapshot) \
+ V(Module32FirstW) \
+ V(Module32NextW)
+
+// Define the decoration to use for the type and variable name used for
+// dynamically loaded DLL function..
+#define DLL_FUNC_TYPE(name) _##name##_
+#define DLL_FUNC_VAR(name) _##name
+
+// Define the type for each dynamically loaded DLL function. The function
+// definitions are copied from DbgHelp.h and TlHelp32.h. The IN and VOID macros
+// from the Windows include files are redefined here to have the function
+// definitions to be as close to the ones in the original .h files as possible.
+#ifndef IN
+#define IN
+#endif
+#ifndef VOID
+#define VOID void
+#endif
+
+// DbgHelp isn't supported on MinGW yet
+#ifndef __MINGW32__
+// DbgHelp.h functions.
+typedef BOOL (__stdcall *DLL_FUNC_TYPE(SymInitialize))(IN HANDLE hProcess,
+ IN PSTR UserSearchPath,
+ IN BOOL fInvadeProcess);
+typedef DWORD (__stdcall *DLL_FUNC_TYPE(SymGetOptions))(VOID);
+typedef DWORD (__stdcall *DLL_FUNC_TYPE(SymSetOptions))(IN DWORD SymOptions);
+typedef BOOL (__stdcall *DLL_FUNC_TYPE(SymGetSearchPath))(
+ IN HANDLE hProcess,
+ OUT PSTR SearchPath,
+ IN DWORD SearchPathLength);
+typedef DWORD64 (__stdcall *DLL_FUNC_TYPE(SymLoadModule64))(
+ IN HANDLE hProcess,
+ IN HANDLE hFile,
+ IN PSTR ImageName,
+ IN PSTR ModuleName,
+ IN DWORD64 BaseOfDll,
+ IN DWORD SizeOfDll);
+typedef BOOL (__stdcall *DLL_FUNC_TYPE(StackWalk64))(
+ DWORD MachineType,
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPSTACKFRAME64 StackFrame,
+ PVOID ContextRecord,
+ PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
+ PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
+ PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
+ PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
+typedef BOOL (__stdcall *DLL_FUNC_TYPE(SymGetSymFromAddr64))(
+ IN HANDLE hProcess,
+ IN DWORD64 qwAddr,
+ OUT PDWORD64 pdwDisplacement,
+ OUT PIMAGEHLP_SYMBOL64 Symbol);
+typedef BOOL (__stdcall *DLL_FUNC_TYPE(SymGetLineFromAddr64))(
+ IN HANDLE hProcess,
+ IN DWORD64 qwAddr,
+ OUT PDWORD pdwDisplacement,
+ OUT PIMAGEHLP_LINE64 Line64);
+// DbgHelp.h typedefs. Implementation found in dbghelp.dll.
+typedef PVOID (__stdcall *DLL_FUNC_TYPE(SymFunctionTableAccess64))(
+ HANDLE hProcess,
+ DWORD64 AddrBase); // DbgHelp.h typedef PFUNCTION_TABLE_ACCESS_ROUTINE64
+typedef DWORD64 (__stdcall *DLL_FUNC_TYPE(SymGetModuleBase64))(
+ HANDLE hProcess,
+ DWORD64 AddrBase); // DbgHelp.h typedef PGET_MODULE_BASE_ROUTINE64
+
+// TlHelp32.h functions.
+typedef HANDLE (__stdcall *DLL_FUNC_TYPE(CreateToolhelp32Snapshot))(
+ DWORD dwFlags,
+ DWORD th32ProcessID);
+typedef BOOL (__stdcall *DLL_FUNC_TYPE(Module32FirstW))(HANDLE hSnapshot,
+ LPMODULEENTRY32W lpme);
+typedef BOOL (__stdcall *DLL_FUNC_TYPE(Module32NextW))(HANDLE hSnapshot,
+ LPMODULEENTRY32W lpme);
+
+#undef IN
+#undef VOID
+
+// Declare a variable for each dynamically loaded DLL function.
+#define DEF_DLL_FUNCTION(name) DLL_FUNC_TYPE(name) DLL_FUNC_VAR(name) = NULL;
+DBGHELP_FUNCTION_LIST(DEF_DLL_FUNCTION)
+TLHELP32_FUNCTION_LIST(DEF_DLL_FUNCTION)
+#undef DEF_DLL_FUNCTION
+
+// Load the functions. This function has a lot of "ugly" macros in order to
+// keep down code duplication.
+
+static bool LoadDbgHelpAndTlHelp32() {
+ static bool dbghelp_loaded = false;
+
+ if (dbghelp_loaded) return true;
+
+ HMODULE module;
+
+ // Load functions from the dbghelp.dll module.
+ module = LoadLibrary(TEXT("dbghelp.dll"));
+ if (module == NULL) {
+ return false;
+ }
+
+#define LOAD_DLL_FUNC(name) \
+ DLL_FUNC_VAR(name) = \
+ reinterpret_cast<DLL_FUNC_TYPE(name)>(GetProcAddress(module, #name));
+
+DBGHELP_FUNCTION_LIST(LOAD_DLL_FUNC)
+
+#undef LOAD_DLL_FUNC
+
+ // Load functions from the kernel32.dll module (the TlHelp32.h function used
+ // to be in tlhelp32.dll but are now moved to kernel32.dll).
+ module = LoadLibrary(TEXT("kernel32.dll"));
+ if (module == NULL) {
+ return false;
+ }
+
+#define LOAD_DLL_FUNC(name) \
+ DLL_FUNC_VAR(name) = \
+ reinterpret_cast<DLL_FUNC_TYPE(name)>(GetProcAddress(module, #name));
+
+TLHELP32_FUNCTION_LIST(LOAD_DLL_FUNC)
+
+#undef LOAD_DLL_FUNC
+
+ // Check that all functions where loaded.
+ bool result =
+#define DLL_FUNC_LOADED(name) (DLL_FUNC_VAR(name) != NULL) &&
+
+DBGHELP_FUNCTION_LIST(DLL_FUNC_LOADED)
+TLHELP32_FUNCTION_LIST(DLL_FUNC_LOADED)
+
+#undef DLL_FUNC_LOADED
+ true;
+
+ dbghelp_loaded = result;
+ return result;
+ // NOTE: The modules are never unloaded and will stay around until the
+ // application is closed.
+}
+
+#undef DBGHELP_FUNCTION_LIST
+#undef TLHELP32_FUNCTION_LIST
+#undef DLL_FUNC_VAR
+#undef DLL_FUNC_TYPE
+
+
+// Load the symbols for generating stack traces.
+static bool LoadSymbols(HANDLE process_handle) {
+ static bool symbols_loaded = false;
+
+ if (symbols_loaded) return true;
+
+ BOOL ok;
+
+ // Initialize the symbol engine.
+ ok = _SymInitialize(process_handle, // hProcess
+ NULL, // UserSearchPath
+ false); // fInvadeProcess
+ if (!ok) return false;
+
+ DWORD options = _SymGetOptions();
+ options |= SYMOPT_LOAD_LINES;
+ options |= SYMOPT_FAIL_CRITICAL_ERRORS;
+ options = _SymSetOptions(options);
+
+ char buf[OS::kStackWalkMaxNameLen] = {0};
+ ok = _SymGetSearchPath(process_handle, buf, OS::kStackWalkMaxNameLen);
+ if (!ok) {
+ int err = GetLastError();
+ PrintF("%d\n", err);
+ return false;
+ }
+
+ HANDLE snapshot = _CreateToolhelp32Snapshot(
+ TH32CS_SNAPMODULE, // dwFlags
+ GetCurrentProcessId()); // th32ProcessId
+ if (snapshot == INVALID_HANDLE_VALUE) return false;
+ MODULEENTRY32W module_entry;
+ module_entry.dwSize = sizeof(module_entry); // Set the size of the structure.
+ BOOL cont = _Module32FirstW(snapshot, &module_entry);
+ while (cont) {
+ DWORD64 base;
+ // NOTE the SymLoadModule64 function has the peculiarity of accepting a
+ // both unicode and ASCII strings even though the parameter is PSTR.
+ base = _SymLoadModule64(
+ process_handle, // hProcess
+ 0, // hFile
+ reinterpret_cast<PSTR>(module_entry.szExePath), // ImageName
+ reinterpret_cast<PSTR>(module_entry.szModule), // ModuleName
+ reinterpret_cast<DWORD64>(module_entry.modBaseAddr), // BaseOfDll
+ module_entry.modBaseSize); // SizeOfDll
+ if (base == 0) {
+ int err = GetLastError();
+ if (err != ERROR_MOD_NOT_FOUND &&
+ err != ERROR_INVALID_HANDLE) return false;
+ }
+ LOG(i::Isolate::Current(),
+ SharedLibraryEvent(
+ module_entry.szExePath,
+ reinterpret_cast<unsigned int>(module_entry.modBaseAddr),
+ reinterpret_cast<unsigned int>(module_entry.modBaseAddr +
+ module_entry.modBaseSize)));
+ cont = _Module32NextW(snapshot, &module_entry);
+ }
+ CloseHandle(snapshot);
+
+ symbols_loaded = true;
+ return true;
+}
+
+
+void OS::LogSharedLibraryAddresses() {
+ // SharedLibraryEvents are logged when loading symbol information.
+ // Only the shared libraries loaded at the time of the call to
+ // LogSharedLibraryAddresses are logged. DLLs loaded after
+ // initialization are not accounted for.
+ if (!LoadDbgHelpAndTlHelp32()) return;
+ HANDLE process_handle = GetCurrentProcess();
+ LoadSymbols(process_handle);
+}
+
+
+void OS::SignalCodeMovingGC() {
+}
+
+
+// Walk the stack using the facilities in dbghelp.dll and tlhelp32.dll
+
+// Switch off warning 4748 (/GS can not protect parameters and local variables
+// from local buffer overrun because optimizations are disabled in function) as
+// it is triggered by the use of inline assembler.
+#pragma warning(push)
+#pragma warning(disable : 4748)
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
+ BOOL ok;
+
+ // Load the required functions from DLL's.
+ if (!LoadDbgHelpAndTlHelp32()) return kStackWalkError;
+
+ // Get the process and thread handles.
+ HANDLE process_handle = GetCurrentProcess();
+ HANDLE thread_handle = GetCurrentThread();
+
+ // Read the symbols.
+ if (!LoadSymbols(process_handle)) return kStackWalkError;
+
+ // Capture current context.
+ CONTEXT context;
+ RtlCaptureContext(&context);
+
+ // Initialize the stack walking
+ STACKFRAME64 stack_frame;
+ memset(&stack_frame, 0, sizeof(stack_frame));
+#ifdef _WIN64
+ stack_frame.AddrPC.Offset = context.Rip;
+ stack_frame.AddrFrame.Offset = context.Rbp;
+ stack_frame.AddrStack.Offset = context.Rsp;
+#else
+ stack_frame.AddrPC.Offset = context.Eip;
+ stack_frame.AddrFrame.Offset = context.Ebp;
+ stack_frame.AddrStack.Offset = context.Esp;
+#endif
+ stack_frame.AddrPC.Mode = AddrModeFlat;
+ stack_frame.AddrFrame.Mode = AddrModeFlat;
+ stack_frame.AddrStack.Mode = AddrModeFlat;
+ int frames_count = 0;
+
+ // Collect stack frames.
+ int frames_size = frames.length();
+ while (frames_count < frames_size) {
+ ok = _StackWalk64(
+ IMAGE_FILE_MACHINE_I386, // MachineType
+ process_handle, // hProcess
+ thread_handle, // hThread
+ &stack_frame, // StackFrame
+ &context, // ContextRecord
+ NULL, // ReadMemoryRoutine
+ _SymFunctionTableAccess64, // FunctionTableAccessRoutine
+ _SymGetModuleBase64, // GetModuleBaseRoutine
+ NULL); // TranslateAddress
+ if (!ok) break;
+
+ // Store the address.
+ ASSERT((stack_frame.AddrPC.Offset >> 32) == 0); // 32-bit address.
+ frames[frames_count].address =
+ reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
+
+ // Try to locate a symbol for this frame.
+ DWORD64 symbol_displacement;
+ SmartArrayPointer<IMAGEHLP_SYMBOL64> symbol(
+ NewArray<IMAGEHLP_SYMBOL64>(kStackWalkMaxNameLen));
+ if (symbol.is_empty()) return kStackWalkError; // Out of memory.
+ memset(*symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + kStackWalkMaxNameLen);
+ (*symbol)->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ (*symbol)->MaxNameLength = kStackWalkMaxNameLen;
+ ok = _SymGetSymFromAddr64(process_handle, // hProcess
+ stack_frame.AddrPC.Offset, // Address
+ &symbol_displacement, // Displacement
+ *symbol); // Symbol
+ if (ok) {
+ // Try to locate more source information for the symbol.
+ IMAGEHLP_LINE64 Line;
+ memset(&Line, 0, sizeof(Line));
+ Line.SizeOfStruct = sizeof(Line);
+ DWORD line_displacement;
+ ok = _SymGetLineFromAddr64(
+ process_handle, // hProcess
+ stack_frame.AddrPC.Offset, // dwAddr
+ &line_displacement, // pdwDisplacement
+ &Line); // Line
+ // Format a text representation of the frame based on the information
+ // available.
+ if (ok) {
+ SNPrintF(MutableCStrVector(frames[frames_count].text,
+ kStackWalkMaxTextLen),
+ "%s %s:%d:%d",
+ (*symbol)->Name, Line.FileName, Line.LineNumber,
+ line_displacement);
+ } else {
+ SNPrintF(MutableCStrVector(frames[frames_count].text,
+ kStackWalkMaxTextLen),
+ "%s",
+ (*symbol)->Name);
+ }
+ // Make sure line termination is in place.
+ frames[frames_count].text[kStackWalkMaxTextLen - 1] = '\0';
+ } else {
+ // No text representation of this frame
+ frames[frames_count].text[0] = '\0';
+
+ // Continue if we are just missing a module (for non C/C++ frames a
+ // module will never be found).
+ int err = GetLastError();
+ if (err != ERROR_MOD_NOT_FOUND) {
+ break;
+ }
+ }
+
+ frames_count++;
+ }
+
+ // Return the number of frames filled in.
+ return frames_count;
+}
+
+
+// Restore warnings to previous settings.
+#pragma warning(pop)
+
+#else // __MINGW32__
+void OS::LogSharedLibraryAddresses() { }
+void OS::SignalCodeMovingGC() { }
+int OS::StackWalk(Vector<OS::StackFrame> frames) { return 0; }
+#endif // __MINGW32__
+
+
+uint64_t OS::CpuFeaturesImpliedByPlatform() {
+ return 0; // Windows runs on anything.
+}
+
+
+double OS::nan_value() {
+#ifdef _MSC_VER
+ // Positive Quiet NaN with no payload (aka. Indeterminate) has all bits
+ // in mask set, so value equals mask.
+ static const __int64 nanval = kQuietNaNMask;
+ return *reinterpret_cast<const double*>(&nanval);
+#else // _MSC_VER
+ return NAN;
+#endif // _MSC_VER
+}
+
+
+int OS::ActivationFrameAlignment() {
+#ifdef _WIN64
+ return 16; // Windows 64-bit ABI requires the stack to be 16-byte aligned.
+#elif defined(__MINGW32__)
+ // With gcc 4.4 the tree vectorization optimizer can generate code
+ // that requires 16 byte alignment such as movdqa on x86.
+ return 16;
+#else
+ return 8; // Floating-point math runs faster with 8-byte alignment.
+#endif
+}
+
+
+VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
+
+
+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()) {
+ 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) {
+ return CommitRegion(address, size, is_executable);
+}
+
+
+bool VirtualMemory::Uncommit(void* address, size_t size) {
+ ASSERT(IsReserved());
+ return UncommitRegion(address, size);
+}
+
+
+bool VirtualMemory::Guard(void* address) {
+ if (NULL == VirtualAlloc(address,
+ OS::CommitPageSize(),
+ MEM_COMMIT,
+ PAGE_READONLY | PAGE_GUARD)) {
+ return false;
+ }
+ return true;
+}
+
+
+void* VirtualMemory::ReserveRegion(size_t size) {
+ return RandomizedVirtualAlloc(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(base, size, MEM_COMMIT, prot)) {
+ return false;
+ }
+
+ UpdateAllocatedSpaceLimits(base, static_cast<int>(size));
+ return true;
+}
+
+
+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;
+}
+
+
+bool VirtualMemory::HasLazyCommits() {
+ // TODO(alph): implement for the platform.
+ return false;
+}
+
+
+// ----------------------------------------------------------------------------
+// Win32 thread support.
+
+// Definition of invalid thread handle and id.
+static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
+
+// Entry point for threads. The supplied argument is a pointer to the thread
+// object. The entry function dispatches to the run method in the thread
+// object. It is important that this function has __stdcall calling
+// convention.
+static unsigned int __stdcall ThreadEntry(void* arg) {
+ Thread* thread = reinterpret_cast<Thread*>(arg);
+ thread->NotifyStartedAndRun();
+ return 0;
+}
+
+
+class Thread::PlatformData : public Malloced {
+ public:
+ explicit PlatformData(HANDLE thread) : thread_(thread) {}
+ HANDLE thread_;
+ unsigned thread_id_;
+};
+
+
+// Initialize a Win32 thread object. The thread has an invalid thread
+// handle until it is started.
+
+Thread::Thread(const Options& options)
+ : stack_size_(options.stack_size()),
+ start_semaphore_(NULL) {
+ data_ = new PlatformData(kNoThread);
+ set_name(options.name());
+}
+
+
+void Thread::set_name(const char* name) {
+ OS::StrNCpy(Vector<char>(name_, sizeof(name_)), name, strlen(name));
+ name_[sizeof(name_) - 1] = '\0';
+}
+
+
+// Close our own handle for the thread.
+Thread::~Thread() {
+ if (data_->thread_ != kNoThread) CloseHandle(data_->thread_);
+ delete data_;
+}
+
+
+// Create a new thread. It is important to use _beginthreadex() instead of
+// the Win32 function CreateThread(), because the CreateThread() does not
+// initialize thread specific structures in the C runtime library.
+void Thread::Start() {
+ data_->thread_ = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL,
+ static_cast<unsigned>(stack_size_),
+ ThreadEntry,
+ this,
+ 0,
+ &data_->thread_id_));
+}
+
+
+// Wait for thread to terminate.
+void Thread::Join() {
+ if (data_->thread_id_ != GetCurrentThreadId()) {
+ WaitForSingleObject(data_->thread_, INFINITE);
+ }
+}
+
+
+Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
+ DWORD result = TlsAlloc();
+ ASSERT(result != TLS_OUT_OF_INDEXES);
+ return static_cast<LocalStorageKey>(result);
+}
+
+
+void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
+ BOOL result = TlsFree(static_cast<DWORD>(key));
+ USE(result);
+ ASSERT(result);
+}
+
+
+void* Thread::GetThreadLocal(LocalStorageKey key) {
+ return TlsGetValue(static_cast<DWORD>(key));
+}
+
+
+void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ BOOL result = TlsSetValue(static_cast<DWORD>(key), value);
+ USE(result);
+ ASSERT(result);
+}
+
+
+
+void Thread::YieldCPU() {
+ Sleep(0);
+}
+
+
+// ----------------------------------------------------------------------------
+// Win32 mutex support.
+//
+// On Win32 mutexes are implemented using CRITICAL_SECTION objects. These are
+// faster than Win32 Mutex objects because they are implemented using user mode
+// atomic instructions. Therefore we only do ring transitions if there is lock
+// contention.
+
+class Win32Mutex : public Mutex {
+ public:
+ Win32Mutex() { InitializeCriticalSection(&cs_); }
+
+ virtual ~Win32Mutex() { DeleteCriticalSection(&cs_); }
+
+ virtual int Lock() {
+ EnterCriticalSection(&cs_);
+ return 0;
+ }
+
+ virtual int Unlock() {
+ LeaveCriticalSection(&cs_);
+ return 0;
+ }
+
+
+ virtual bool TryLock() {
+ // Returns non-zero if critical section is entered successfully entered.
+ return TryEnterCriticalSection(&cs_);
+ }
+
+ private:
+ CRITICAL_SECTION cs_; // Critical section used for mutex
+};
+
+
+Mutex* OS::CreateMutex() {
+ return new Win32Mutex();
+}
+
+
+// ----------------------------------------------------------------------------
+// Win32 semaphore support.
+//
+// On Win32 semaphores are implemented using Win32 Semaphore objects. The
+// semaphores are anonymous. Also, the semaphores are initialized to have
+// no upper limit on count.
+
+
+class Win32Semaphore : public Semaphore {
+ public:
+ explicit Win32Semaphore(int count) {
+ sem = ::CreateSemaphoreA(NULL, count, 0x7fffffff, NULL);
+ }
+
+ ~Win32Semaphore() {
+ CloseHandle(sem);
+ }
+
+ void Wait() {
+ WaitForSingleObject(sem, INFINITE);
+ }
+
+ bool Wait(int timeout) {
+ // Timeout in Windows API is in milliseconds.
+ DWORD millis_timeout = timeout / 1000;
+ return WaitForSingleObject(sem, millis_timeout) != WAIT_TIMEOUT;
+ }
+
+ void Signal() {
+ LONG dummy;
+ ReleaseSemaphore(sem, 1, &dummy);
+ }
+
+ private:
+ HANDLE sem;
+};
+
+
+Semaphore* OS::CreateSemaphore(int count) {
+ return new Win32Semaphore(count);
+}
+
+
+// ----------------------------------------------------------------------------
+// Win32 socket support.
+//
+
+class Win32Socket : public Socket {
+ public:
+ explicit Win32Socket() {
+ // Create the socket.
+ socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ }
+ explicit Win32Socket(SOCKET socket): socket_(socket) { }
+ virtual ~Win32Socket() { Shutdown(); }
+
+ // Server initialization.
+ bool Bind(const int port);
+ bool Listen(int backlog) const;
+ Socket* Accept() const;
+
+ // Client initialization.
+ bool Connect(const char* host, const char* port);
+
+ // Shutdown socket for both read and write.
+ bool Shutdown();
+
+ // Data Transimission
+ int Send(const char* data, int len) const;
+ int Receive(char* data, int len) const;
+
+ bool SetReuseAddress(bool reuse_address);
+
+ bool IsValid() const { return socket_ != INVALID_SOCKET; }
+
+ private:
+ SOCKET socket_;
+};
+
+
+bool Win32Socket::Bind(const int port) {
+ if (!IsValid()) {
+ return false;
+ }
+
+ sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr.sin_port = htons(port);
+ int status = bind(socket_,
+ reinterpret_cast<struct sockaddr *>(&addr),
+ sizeof(addr));
+ return status == 0;
+}
+
+
+bool Win32Socket::Listen(int backlog) const {
+ if (!IsValid()) {
+ return false;
+ }
+
+ int status = listen(socket_, backlog);
+ return status == 0;
+}
+
+
+Socket* Win32Socket::Accept() const {
+ if (!IsValid()) {
+ return NULL;
+ }
+
+ SOCKET socket = accept(socket_, NULL, NULL);
+ if (socket == INVALID_SOCKET) {
+ return NULL;
+ } else {
+ return new Win32Socket(socket);
+ }
+}
+
+
+bool Win32Socket::Connect(const char* host, const char* port) {
+ if (!IsValid()) {
+ return false;
+ }
+
+ // Lookup host and port.
+ struct addrinfo *result = NULL;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(addrinfo));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ int status = getaddrinfo(host, port, &hints, &result);
+ if (status != 0) {
+ return false;
+ }
+
+ // Connect.
+ status = connect(socket_,
+ result->ai_addr,
+ static_cast<int>(result->ai_addrlen));
+ freeaddrinfo(result);
+ return status == 0;
+}
+
+
+bool Win32Socket::Shutdown() {
+ if (IsValid()) {
+ // Shutdown socket for both read and write.
+ int status = shutdown(socket_, SD_BOTH);
+ closesocket(socket_);
+ socket_ = INVALID_SOCKET;
+ return status == SOCKET_ERROR;
+ }
+ return true;
+}
+
+
+int Win32Socket::Send(const char* data, int len) const {
+ if (len <= 0) return 0;
+ int written = 0;
+ while (written < len) {
+ int status = send(socket_, data + written, len - written, 0);
+ if (status == 0) {
+ break;
+ } else if (status > 0) {
+ written += status;
+ } else {
+ return 0;
+ }
+ }
+ return written;
+}
+
+
+int Win32Socket::Receive(char* data, int len) const {
+ if (len <= 0) return 0;
+ int status = recv(socket_, data, len, 0);
+ return (status == SOCKET_ERROR) ? 0 : status;
+}
+
+
+bool Win32Socket::SetReuseAddress(bool reuse_address) {
+ BOOL on = reuse_address ? true : false;
+ int status = setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
+ reinterpret_cast<char*>(&on), sizeof(on));
+ return status == SOCKET_ERROR;
+}
+
+
+bool Socket::SetUp() {
+ // Initialize Winsock32
+ int err;
+ WSADATA winsock_data;
+ WORD version_requested = MAKEWORD(1, 0);
+ err = WSAStartup(version_requested, &winsock_data);
+ if (err != 0) {
+ PrintF("Unable to initialize Winsock, err = %d\n", Socket::LastError());
+ }
+
+ return err == 0;
+}
+
+
+int Socket::LastError() {
+ return WSAGetLastError();
+}
+
+
+uint16_t Socket::HToN(uint16_t value) {
+ return htons(value);
+}
+
+
+uint16_t Socket::NToH(uint16_t value) {
+ return ntohs(value);
+}
+
+
+uint32_t Socket::HToN(uint32_t value) {
+ return htonl(value);
+}
+
+
+uint32_t Socket::NToH(uint32_t value) {
+ return ntohl(value);
+}
+
+
+Socket* OS::CreateSocket() {
+ return new Win32Socket();
+}
+
+
+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());
+ srand(static_cast<unsigned int>(seed));
+ limit_mutex = CreateMutex();
+}
+
+
+void OS::TearDown() {
+ delete limit_mutex;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/platform.h b/chromium/v8/src/platform.h
new file mode 100644
index 00000000000..8b27c19a65f
--- /dev/null
+++ b/chromium/v8/src/platform.h
@@ -0,0 +1,826 @@
+// Copyright 2012 the V8 project authors. 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 module contains the platform-specific code. This make the rest of the
+// code less dependent on operating system, compilers and runtime libraries.
+// This module does specifically not deal with differences between different
+// processor architecture.
+// The platform classes have the same definition for all platforms. The
+// implementation for a particular platform is put in platform_<os>.cc.
+// The build system then uses the implementation for the target platform.
+//
+// This design has been chosen because it is simple and fast. Alternatively,
+// the platform dependent classes could have been implemented using abstract
+// superclasses with virtual methods and having specializations for each
+// platform. This design was rejected because it was more complicated and
+// slower. It would require factory methods for selecting the right
+// implementation and the overhead of virtual methods for performance
+// sensitive like mutex locking/unlocking.
+
+#ifndef V8_PLATFORM_H_
+#define V8_PLATFORM_H_
+
+#ifdef __sun
+# ifndef signbit
+namespace std {
+int signbit(double x);
+}
+# endif
+#endif
+
+// GCC specific stuff
+#ifdef __GNUC__
+
+// Needed for va_list on at least MinGW and Android.
+#include <stdarg.h>
+
+#define __GNUC_VERSION__ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100)
+
+#endif // __GNUC__
+
+
+// Windows specific stuff.
+#ifdef WIN32
+
+// Microsoft Visual C++ specific stuff.
+#ifdef _MSC_VER
+
+#include "win32-headers.h"
+#include "win32-math.h"
+
+int strncasecmp(const char* s1, const char* s2, int n);
+
+inline int lrint(double flt) {
+ int intgr;
+#if defined(V8_TARGET_ARCH_IA32)
+ __asm {
+ fld flt
+ fistp intgr
+ };
+#else
+ intgr = static_cast<int>(flt + 0.5);
+ if ((intgr & 1) != 0 && intgr - flt == 0.5) {
+ // If the number is halfway between two integers, round to the even one.
+ intgr--;
+ }
+#endif
+ return intgr;
+}
+
+#endif // _MSC_VER
+
+#ifndef __CYGWIN__
+// Random is missing on both Visual Studio and MinGW.
+int random();
+#endif
+
+#endif // WIN32
+
+#include "lazy-instance.h"
+#include "utils.h"
+#include "v8globals.h"
+
+namespace v8 {
+namespace internal {
+
+class Semaphore;
+class Mutex;
+
+double ceiling(double x);
+double modulo(double x, double y);
+
+// Custom implementation of math functions.
+double fast_sin(double input);
+double fast_cos(double input);
+double fast_tan(double input);
+double fast_log(double input);
+double fast_exp(double input);
+double fast_sqrt(double input);
+// The custom exp implementation needs 16KB of lookup data; initialize it
+// on demand.
+void lazily_initialize_fast_exp();
+
+// Forward declarations.
+class Socket;
+
+// ----------------------------------------------------------------------------
+// Fast TLS support
+
+#ifndef V8_NO_FAST_TLS
+
+#if defined(_MSC_VER) && V8_HOST_ARCH_IA32
+
+#define V8_FAST_TLS_SUPPORTED 1
+
+INLINE(intptr_t InternalGetExistingThreadLocal(intptr_t index));
+
+inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
+ const intptr_t kTibInlineTlsOffset = 0xE10;
+ const intptr_t kTibExtraTlsOffset = 0xF94;
+ const intptr_t kMaxInlineSlots = 64;
+ const intptr_t kMaxSlots = kMaxInlineSlots + 1024;
+ ASSERT(0 <= index && index < kMaxSlots);
+ if (index < kMaxInlineSlots) {
+ return static_cast<intptr_t>(__readfsdword(kTibInlineTlsOffset +
+ kPointerSize * index));
+ }
+ intptr_t extra = static_cast<intptr_t>(__readfsdword(kTibExtraTlsOffset));
+ ASSERT(extra != 0);
+ return *reinterpret_cast<intptr_t*>(extra +
+ kPointerSize * (index - kMaxInlineSlots));
+}
+
+#elif defined(__APPLE__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)
+
+#define V8_FAST_TLS_SUPPORTED 1
+
+extern intptr_t kMacTlsBaseOffset;
+
+INLINE(intptr_t InternalGetExistingThreadLocal(intptr_t index));
+
+inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
+ intptr_t result;
+#if V8_HOST_ARCH_IA32
+ asm("movl %%gs:(%1,%2,4), %0;"
+ :"=r"(result) // Output must be a writable register.
+ :"r"(kMacTlsBaseOffset), "r"(index));
+#else
+ asm("movq %%gs:(%1,%2,8), %0;"
+ :"=r"(result)
+ :"r"(kMacTlsBaseOffset), "r"(index));
+#endif
+ return result;
+}
+
+#endif
+
+#endif // V8_NO_FAST_TLS
+
+
+// ----------------------------------------------------------------------------
+// OS
+//
+// This class has static methods for the different platform specific
+// functions. Add methods here to cope with differences between the
+// supported platforms.
+
+class OS {
+ public:
+ // Initializes the platform OS support. Called once at VM startup.
+ static void SetUp();
+
+ // Initializes the platform OS support that depend on CPU features. This is
+ // called after CPU initialization.
+ static void PostSetUp();
+
+ // Clean up platform-OS-related things. Called once at VM shutdown.
+ static void TearDown();
+
+ // Returns the accumulated user time for thread. This routine
+ // can be used for profiling. The implementation should
+ // strive for high-precision timer resolution, preferable
+ // micro-second resolution.
+ static int GetUserTime(uint32_t* secs, uint32_t* usecs);
+
+ // Get a tick counter normalized to one tick per microsecond.
+ // Used for calculating time intervals.
+ static int64_t Ticks();
+
+ // Returns current time as the number of milliseconds since
+ // 00:00:00 UTC, January 1, 1970.
+ static double TimeCurrentMillis();
+
+ // Returns a string identifying the current time zone. The
+ // timestamp is used for determining if DST is in effect.
+ static const char* LocalTimezone(double time);
+
+ // Returns the local time offset in milliseconds east of UTC without
+ // taking daylight savings time into account.
+ static double LocalTimeOffset();
+
+ // Returns the daylight savings offset for the given time.
+ static double DaylightSavingsOffset(double time);
+
+ // Returns last OS error.
+ static int GetLastError();
+
+ static FILE* FOpen(const char* path, const char* mode);
+ static bool Remove(const char* path);
+
+ // Opens a temporary file, the file is auto removed on close.
+ static FILE* OpenTemporaryFile();
+
+ // Log file open mode is platform-dependent due to line ends issues.
+ static const char* const LogFileOpenMode;
+
+ // Print output to console. This is mostly used for debugging output.
+ // On platforms that has standard terminal output, the output
+ // should go to stdout.
+ static void Print(const char* format, ...);
+ static void VPrint(const char* format, va_list args);
+
+ // Print output to a file. This is mostly used for debugging output.
+ static void FPrint(FILE* out, const char* format, ...);
+ static void VFPrint(FILE* out, const char* format, va_list args);
+
+ // Print error output to console. This is mostly used for error message
+ // output. On platforms that has standard terminal output, the output
+ // should go to stderr.
+ static void PrintError(const char* format, ...);
+ static void VPrintError(const char* format, va_list args);
+
+ // Allocate/Free memory used by JS heap. Pages are readable/writable, but
+ // they are not guaranteed to be executable unless 'executable' is true.
+ // Returns the address of allocated memory, or NULL if failed.
+ static void* Allocate(const size_t requested,
+ size_t* allocated,
+ 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();
+
+ // Returns an indication of whether a pointer is in a space that
+ // has been allocated by Allocate(). This method may conservatively
+ // always return false, but giving more accurate information may
+ // improve the robustness of the stack dump code in the presence of
+ // heap corruption.
+ static bool IsOutsideAllocatedSpace(void* pointer);
+
+ // Sleep for a number of milliseconds.
+ static void Sleep(const int milliseconds);
+
+ static int NumberOfCores();
+
+ // Abort the current process.
+ static void Abort();
+
+ // Debug break.
+ static void DebugBreak();
+
+ // Dump C++ current stack trace (only functional on Linux).
+ static void DumpBacktrace();
+
+ // Walk the stack.
+ static const int kStackWalkError = -1;
+ static const int kStackWalkMaxNameLen = 256;
+ static const int kStackWalkMaxTextLen = 256;
+ struct StackFrame {
+ void* address;
+ char text[kStackWalkMaxTextLen];
+ };
+
+ static int StackWalk(Vector<StackFrame> frames);
+
+ // Factory method for creating platform dependent Mutex.
+ // Please use delete to reclaim the storage for the returned Mutex.
+ static Mutex* CreateMutex();
+
+ // Factory method for creating platform dependent Semaphore.
+ // Please use delete to reclaim the storage for the returned Semaphore.
+ static Semaphore* CreateSemaphore(int count);
+
+ // Factory method for creating platform dependent Socket.
+ // Please use delete to reclaim the storage for the returned Socket.
+ static Socket* CreateSocket();
+
+ class MemoryMappedFile {
+ public:
+ static MemoryMappedFile* open(const char* name);
+ static MemoryMappedFile* create(const char* name, int size, void* initial);
+ virtual ~MemoryMappedFile() { }
+ virtual void* memory() = 0;
+ virtual int size() = 0;
+ };
+
+ // Safe formatting print. Ensures that str is always null-terminated.
+ // Returns the number of chars written, or -1 if output was truncated.
+ static int SNPrintF(Vector<char> str, const char* format, ...);
+ static int VSNPrintF(Vector<char> str,
+ const char* format,
+ va_list args);
+
+ static char* StrChr(char* str, int c);
+ static void StrNCpy(Vector<char> dest, const char* src, size_t n);
+
+ // Support for the profiler. Can do nothing, in which case ticks
+ // occuring in shared libraries will not be properly accounted for.
+ static void LogSharedLibraryAddresses();
+
+ // Support for the profiler. Notifies the external profiling
+ // process that a code moving garbage collection starts. Can do
+ // nothing, in which case the code objects must not move (e.g., by
+ // using --never-compact) if accurate profiling is desired.
+ static void SignalCodeMovingGC();
+
+ // The return value indicates the CPU features we are sure of because of the
+ // OS. For example MacOSX doesn't run on any x86 CPUs that don't have SSE2
+ // instructions.
+ // This is a little messy because the interpretation is subject to the cross
+ // of the CPU and the OS. The bits in the answer correspond to the bit
+ // positions indicated by the members of the CpuFeature enum from globals.h
+ static uint64_t CpuFeaturesImpliedByPlatform();
+
+ // Maximum size of the virtual memory. 0 means there is no artificial
+ // limit.
+ static intptr_t MaxVirtualMemory();
+
+ // Returns the double constant NAN
+ static double nan_value();
+
+ // Support runtime detection of Cpu implementer
+ static CpuImplementer GetCpuImplementer();
+
+ // Support runtime detection of Cpu implementer
+ static CpuPart GetCpuPart(CpuImplementer implementer);
+
+ // Support runtime detection of VFP3 on ARM CPUs.
+ static bool ArmCpuHasFeature(CpuFeature feature);
+
+ // Support runtime detection of whether the hard float option of the
+ // EABI is used.
+ static bool ArmUsingHardFloat();
+
+ // Support runtime detection of FPU on MIPS CPUs.
+ static bool MipsCpuHasFeature(CpuFeature feature);
+
+ // Returns the activation frame alignment constraint or zero if
+ // the platform doesn't care. Guaranteed to be a power of two.
+ static int ActivationFrameAlignment();
+
+#if defined(V8_TARGET_ARCH_IA32)
+ // Limit below which the extra overhead of the MemCopy function is likely
+ // to outweigh the benefits of faster copying.
+ static const int kMinComplexMemCopy = 64;
+
+ // Copy memory area. No restrictions.
+ static void MemMove(void* dest, const void* src, size_t size);
+ typedef void (*MemMoveFunction)(void* dest, const void* src, size_t size);
+
+ // Keep the distinction of "move" vs. "copy" for the benefit of other
+ // architectures.
+ static void MemCopy(void* dest, const void* src, size_t size) {
+ MemMove(dest, src, size);
+ }
+#elif defined(V8_HOST_ARCH_ARM)
+ typedef void (*MemCopyUint8Function)(uint8_t* dest,
+ const uint8_t* src,
+ size_t size);
+ static MemCopyUint8Function memcopy_uint8_function;
+ static void MemCopyUint8Wrapper(uint8_t* dest,
+ const uint8_t* src,
+ size_t chars) {
+ memcpy(dest, src, chars);
+ }
+ // For values < 16, the assembler function is slower than the inlined C code.
+ static const int kMinComplexMemCopy = 16;
+ static void MemCopy(void* dest, const void* src, size_t size) {
+ (*memcopy_uint8_function)(reinterpret_cast<uint8_t*>(dest),
+ reinterpret_cast<const uint8_t*>(src),
+ size);
+ }
+ static void MemMove(void* dest, const void* src, size_t size) {
+ memmove(dest, src, size);
+ }
+
+ typedef void (*MemCopyUint16Uint8Function)(uint16_t* dest,
+ const uint8_t* src,
+ size_t size);
+ static MemCopyUint16Uint8Function memcopy_uint16_uint8_function;
+ static void MemCopyUint16Uint8Wrapper(uint16_t* dest,
+ const uint8_t* src,
+ size_t chars);
+ // For values < 12, the assembler function is slower than the inlined C code.
+ static const int kMinComplexConvertMemCopy = 12;
+ static void MemCopyUint16Uint8(uint16_t* dest,
+ const uint8_t* src,
+ size_t size) {
+ (*memcopy_uint16_uint8_function)(dest, src, size);
+ }
+#else
+ // Copy memory area to disjoint memory area.
+ static void MemCopy(void* dest, const void* src, size_t size) {
+ memcpy(dest, src, size);
+ }
+ static void MemMove(void* dest, const void* src, size_t size) {
+ memmove(dest, src, size);
+ }
+ static const int kMinComplexMemCopy = 16 * kPointerSize;
+#endif // V8_TARGET_ARCH_IA32
+
+ static int GetCurrentProcessId();
+
+ private:
+ static const int msPerSecond = 1000;
+
+ 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. 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.
+ bool Commit(void* address, size_t size, bool is_executable);
+
+ // Uncommit real memory. Returns whether the operation succeeded.
+ bool Uncommit(void* address, size_t size);
+
+ // Creates a single guard page at the given address.
+ bool Guard(void* address);
+
+ 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);
+
+ // Returns true if OS performs lazy commits, i.e. the memory allocation call
+ // defers actual physical memory allocation till the first memory access.
+ // Otherwise returns false.
+ static bool HasLazyCommits();
+
+ private:
+ void* address_; // Start address of the virtual memory.
+ size_t size_; // Size of the virtual memory.
+};
+
+
+// ----------------------------------------------------------------------------
+// Semaphore
+//
+// A semaphore object is a synchronization object that maintains a count. The
+// count is decremented each time a thread completes a wait for the semaphore
+// object and incremented each time a thread signals the semaphore. When the
+// count reaches zero, threads waiting for the semaphore blocks until the
+// count becomes non-zero.
+
+class Semaphore {
+ public:
+ virtual ~Semaphore() {}
+
+ // Suspends the calling thread until the semaphore counter is non zero
+ // and then decrements the semaphore counter.
+ virtual void Wait() = 0;
+
+ // Suspends the calling thread until the counter is non zero or the timeout
+ // 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;
+
+ // Increments the semaphore counter.
+ virtual void Signal() = 0;
+};
+
+template <int InitialValue>
+struct CreateSemaphoreTrait {
+ static Semaphore* Create() {
+ return OS::CreateSemaphore(InitialValue);
+ }
+};
+
+// POD Semaphore initialized lazily (i.e. the first time Pointer() is called).
+// Usage:
+// // The following semaphore starts at 0.
+// static LazySemaphore<0>::type my_semaphore = LAZY_SEMAPHORE_INITIALIZER;
+//
+// void my_function() {
+// // Do something with my_semaphore.Pointer().
+// }
+//
+template <int InitialValue>
+struct LazySemaphore {
+ typedef typename LazyDynamicInstance<
+ Semaphore, CreateSemaphoreTrait<InitialValue>,
+ ThreadSafeInitOnceTrait>::type type;
+};
+
+#define LAZY_SEMAPHORE_INITIALIZER LAZY_DYNAMIC_INSTANCE_INITIALIZER
+
+
+// ----------------------------------------------------------------------------
+// Thread
+//
+// Thread objects are used for creating and running threads. When the start()
+// method is called the new thread starts running the run() method in the new
+// thread. The Thread object should not be deallocated before the thread has
+// terminated.
+
+class Thread {
+ public:
+ // Opaque data type for thread-local storage keys.
+ // LOCAL_STORAGE_KEY_MIN_VALUE and LOCAL_STORAGE_KEY_MAX_VALUE are specified
+ // to ensure that enumeration type has correct value range (see Issue 830 for
+ // more details).
+ enum LocalStorageKey {
+ LOCAL_STORAGE_KEY_MIN_VALUE = kMinInt,
+ LOCAL_STORAGE_KEY_MAX_VALUE = kMaxInt
+ };
+
+ 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_; }
+
+ private:
+ const char* name_;
+ int stack_size_;
+ };
+
+ // Create new thread.
+ explicit Thread(const Options& options);
+ virtual ~Thread();
+
+ // Start new thread by calling the Run() method on the new thread.
+ void Start();
+
+ // Start new thread and wait until Run() method is called on the new thread.
+ void StartSynchronously() {
+ start_semaphore_ = OS::CreateSemaphore(0);
+ Start();
+ start_semaphore_->Wait();
+ delete start_semaphore_;
+ start_semaphore_ = NULL;
+ }
+
+ // Wait until thread terminates.
+ void Join();
+
+ inline const char* name() const {
+ return name_;
+ }
+
+ // Abstract method for run handler.
+ virtual void Run() = 0;
+
+ // Thread-local storage.
+ static LocalStorageKey CreateThreadLocalKey();
+ static void DeleteThreadLocalKey(LocalStorageKey key);
+ static void* GetThreadLocal(LocalStorageKey key);
+ static int GetThreadLocalInt(LocalStorageKey key) {
+ return static_cast<int>(reinterpret_cast<intptr_t>(GetThreadLocal(key)));
+ }
+ static void SetThreadLocal(LocalStorageKey key, void* value);
+ static void SetThreadLocalInt(LocalStorageKey key, int value) {
+ SetThreadLocal(key, reinterpret_cast<void*>(static_cast<intptr_t>(value)));
+ }
+ static bool HasThreadLocal(LocalStorageKey key) {
+ return GetThreadLocal(key) != NULL;
+ }
+
+#ifdef V8_FAST_TLS_SUPPORTED
+ static inline void* GetExistingThreadLocal(LocalStorageKey key) {
+ void* result = reinterpret_cast<void*>(
+ InternalGetExistingThreadLocal(static_cast<intptr_t>(key)));
+ ASSERT(result == GetThreadLocal(key));
+ return result;
+ }
+#else
+ static inline void* GetExistingThreadLocal(LocalStorageKey key) {
+ return GetThreadLocal(key);
+ }
+#endif
+
+ // A hint to the scheduler to let another thread run.
+ static void YieldCPU();
+
+
+ // The thread name length is limited to 16 based on Linux's implementation of
+ // prctl().
+ static const int kMaxThreadNameLength = 16;
+
+ class PlatformData;
+ PlatformData* data() { return data_; }
+
+ void NotifyStartedAndRun() {
+ if (start_semaphore_) start_semaphore_->Signal();
+ Run();
+ }
+
+ private:
+ void set_name(const char* name);
+
+ PlatformData* data_;
+
+ char name_[kMaxThreadNameLength];
+ int stack_size_;
+ Semaphore* start_semaphore_;
+
+ DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+
+// ----------------------------------------------------------------------------
+// Mutex
+//
+// Mutexes are used for serializing access to non-reentrant sections of code.
+// The implementations of mutex should allow for nested/recursive locking.
+
+class Mutex {
+ public:
+ virtual ~Mutex() {}
+
+ // Locks the given mutex. If the mutex is currently unlocked, it becomes
+ // locked and owned by the calling thread, and immediately. If the mutex
+ // is already locked by another thread, suspends the calling thread until
+ // the mutex is unlocked.
+ virtual int Lock() = 0;
+
+ // Unlocks the given mutex. The mutex is assumed to be locked and owned by
+ // the calling thread on entrance.
+ virtual int Unlock() = 0;
+
+ // Tries to lock the given mutex. Returns whether the mutex was
+ // successfully locked.
+ virtual bool TryLock() = 0;
+};
+
+struct CreateMutexTrait {
+ static Mutex* Create() {
+ return OS::CreateMutex();
+ }
+};
+
+// POD Mutex initialized lazily (i.e. the first time Pointer() is called).
+// Usage:
+// static LazyMutex my_mutex = LAZY_MUTEX_INITIALIZER;
+//
+// void my_function() {
+// ScopedLock my_lock(my_mutex.Pointer());
+// // Do something.
+// }
+//
+typedef LazyDynamicInstance<
+ Mutex, CreateMutexTrait, ThreadSafeInitOnceTrait>::type LazyMutex;
+
+#define LAZY_MUTEX_INITIALIZER LAZY_DYNAMIC_INSTANCE_INITIALIZER
+
+// ----------------------------------------------------------------------------
+// ScopedLock
+//
+// Stack-allocated ScopedLocks provide block-scoped locking and
+// unlocking of a mutex.
+class ScopedLock {
+ public:
+ explicit ScopedLock(Mutex* mutex): mutex_(mutex) {
+ ASSERT(mutex_ != NULL);
+ mutex_->Lock();
+ }
+ ~ScopedLock() {
+ mutex_->Unlock();
+ }
+
+ private:
+ Mutex* mutex_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedLock);
+};
+
+
+// ----------------------------------------------------------------------------
+// Socket
+//
+
+class Socket {
+ public:
+ virtual ~Socket() {}
+
+ // Server initialization.
+ virtual bool Bind(const int port) = 0;
+ virtual bool Listen(int backlog) const = 0;
+ virtual Socket* Accept() const = 0;
+
+ // Client initialization.
+ virtual bool Connect(const char* host, const char* port) = 0;
+
+ // Shutdown socket for both read and write. This causes blocking Send and
+ // Receive calls to exit. After Shutdown the Socket object cannot be used for
+ // any communication.
+ virtual bool Shutdown() = 0;
+
+ // Data Transimission
+ // Return 0 on failure.
+ virtual int Send(const char* data, int len) const = 0;
+ virtual int Receive(char* data, int len) const = 0;
+
+ // Set the value of the SO_REUSEADDR socket option.
+ virtual bool SetReuseAddress(bool reuse_address) = 0;
+
+ virtual bool IsValid() const = 0;
+
+ static bool SetUp();
+ static int LastError();
+ static uint16_t HToN(uint16_t value);
+ static uint16_t NToH(uint16_t value);
+ static uint32_t HToN(uint32_t value);
+ static uint32_t NToH(uint32_t value);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_PLATFORM_H_
diff --git a/chromium/v8/src/preparse-data-format.h b/chromium/v8/src/preparse-data-format.h
new file mode 100644
index 00000000000..e64326e578c
--- /dev/null
+++ b/chromium/v8/src/preparse-data-format.h
@@ -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.
+
+#ifndef V8_PREPARSE_DATA_FORMAT_H_
+#define V8_PREPARSE_DATA_FORMAT_H_
+
+namespace v8 {
+namespace internal {
+
+// Generic and general data used by preparse data recorders and readers.
+
+struct PreparseDataConstants {
+ public:
+ // Layout and constants of the preparse data exchange format.
+ static const unsigned kMagicNumber = 0xBadDead;
+ static const unsigned kCurrentVersion = 7;
+
+ static const int kMagicOffset = 0;
+ static const int kVersionOffset = 1;
+ static const int kHasErrorOffset = 2;
+ static const int kFunctionsSizeOffset = 3;
+ static const int kSymbolCountOffset = 4;
+ static const int kSizeOffset = 5;
+ static const int kHeaderSize = 6;
+
+ // If encoding a message, the following positions are fixed.
+ static const int kMessageStartPos = 0;
+ static const int kMessageEndPos = 1;
+ static const int kMessageArgCountPos = 2;
+ static const int kMessageTextPos = 3;
+
+ static const unsigned char kNumberTerminator = 0x80u;
+};
+
+
+} } // namespace v8::internal.
+
+#endif // V8_PREPARSE_DATA_FORMAT_H_
diff --git a/chromium/v8/src/preparse-data.cc b/chromium/v8/src/preparse-data.cc
new file mode 100644
index 00000000000..8e088482850
--- /dev/null
+++ b/chromium/v8/src/preparse-data.cc
@@ -0,0 +1,184 @@
+// 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.
+
+#include "../include/v8stdint.h"
+
+#include "preparse-data-format.h"
+#include "preparse-data.h"
+
+#include "checks.h"
+#include "globals.h"
+#include "hashmap.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// FunctionLoggingParserRecorder
+
+FunctionLoggingParserRecorder::FunctionLoggingParserRecorder()
+ : function_store_(0),
+ is_recording_(true),
+ pause_count_(0) {
+ preamble_[PreparseDataConstants::kMagicOffset] =
+ PreparseDataConstants::kMagicNumber;
+ preamble_[PreparseDataConstants::kVersionOffset] =
+ PreparseDataConstants::kCurrentVersion;
+ preamble_[PreparseDataConstants::kHasErrorOffset] = false;
+ preamble_[PreparseDataConstants::kFunctionsSizeOffset] = 0;
+ preamble_[PreparseDataConstants::kSymbolCountOffset] = 0;
+ preamble_[PreparseDataConstants::kSizeOffset] = 0;
+ ASSERT_EQ(6, PreparseDataConstants::kHeaderSize);
+#ifdef DEBUG
+ prev_start_ = -1;
+#endif
+}
+
+
+void FunctionLoggingParserRecorder::LogMessage(int start_pos,
+ int end_pos,
+ const char* message,
+ const char* arg_opt) {
+ if (has_error()) return;
+ preamble_[PreparseDataConstants::kHasErrorOffset] = true;
+ function_store_.Reset();
+ STATIC_ASSERT(PreparseDataConstants::kMessageStartPos == 0);
+ function_store_.Add(start_pos);
+ STATIC_ASSERT(PreparseDataConstants::kMessageEndPos == 1);
+ function_store_.Add(end_pos);
+ STATIC_ASSERT(PreparseDataConstants::kMessageArgCountPos == 2);
+ function_store_.Add((arg_opt == NULL) ? 0 : 1);
+ STATIC_ASSERT(PreparseDataConstants::kMessageTextPos == 3);
+ WriteString(CStrVector(message));
+ if (arg_opt != NULL) WriteString(CStrVector(arg_opt));
+ is_recording_ = false;
+}
+
+
+void FunctionLoggingParserRecorder::WriteString(Vector<const char> str) {
+ function_store_.Add(str.length());
+ for (int i = 0; i < str.length(); i++) {
+ function_store_.Add(str[i]);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// PartialParserRecorder - Record both function entries and symbols.
+
+Vector<unsigned> PartialParserRecorder::ExtractData() {
+ int function_size = function_store_.size();
+ int total_size = PreparseDataConstants::kHeaderSize + function_size;
+ Vector<unsigned> data = Vector<unsigned>::New(total_size);
+ preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size;
+ preamble_[PreparseDataConstants::kSymbolCountOffset] = 0;
+ OS::MemCopy(data.start(), preamble_, sizeof(preamble_));
+ int symbol_start = PreparseDataConstants::kHeaderSize + function_size;
+ if (function_size > 0) {
+ function_store_.WriteTo(data.SubVector(PreparseDataConstants::kHeaderSize,
+ symbol_start));
+ }
+ return data;
+}
+
+
+// ----------------------------------------------------------------------------
+// CompleteParserRecorder - Record both function entries and symbols.
+
+CompleteParserRecorder::CompleteParserRecorder()
+ : FunctionLoggingParserRecorder(),
+ literal_chars_(0),
+ symbol_store_(0),
+ symbol_keys_(0),
+ string_table_(vector_compare),
+ symbol_id_(0) {
+}
+
+
+void CompleteParserRecorder::LogSymbol(int start,
+ int hash,
+ bool is_ascii,
+ Vector<const byte> literal_bytes) {
+ Key key = { is_ascii, literal_bytes };
+ HashMap::Entry* entry = string_table_.Lookup(&key, hash, true);
+ int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
+ if (id == 0) {
+ // Copy literal contents for later comparison.
+ key.literal_bytes =
+ Vector<const byte>::cast(literal_chars_.AddBlock(literal_bytes));
+ // Put (symbol_id_ + 1) into entry and increment it.
+ id = ++symbol_id_;
+ entry->value = reinterpret_cast<void*>(id);
+ Vector<Key> symbol = symbol_keys_.AddBlock(1, key);
+ entry->key = &symbol[0];
+ }
+ WriteNumber(id - 1);
+}
+
+
+Vector<unsigned> CompleteParserRecorder::ExtractData() {
+ int function_size = function_store_.size();
+ // Add terminator to symbols, then pad to unsigned size.
+ int symbol_size = symbol_store_.size();
+ int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned));
+ symbol_store_.AddBlock(padding, PreparseDataConstants::kNumberTerminator);
+ symbol_size += padding;
+ int total_size = PreparseDataConstants::kHeaderSize + function_size
+ + (symbol_size / sizeof(unsigned));
+ Vector<unsigned> data = Vector<unsigned>::New(total_size);
+ preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size;
+ preamble_[PreparseDataConstants::kSymbolCountOffset] = symbol_id_;
+ OS::MemCopy(data.start(), preamble_, sizeof(preamble_));
+ int symbol_start = PreparseDataConstants::kHeaderSize + function_size;
+ if (function_size > 0) {
+ function_store_.WriteTo(data.SubVector(PreparseDataConstants::kHeaderSize,
+ symbol_start));
+ }
+ if (!has_error()) {
+ symbol_store_.WriteTo(
+ Vector<byte>::cast(data.SubVector(symbol_start, total_size)));
+ }
+ return data;
+}
+
+
+void CompleteParserRecorder::WriteNumber(int number) {
+ ASSERT(number >= 0);
+
+ int mask = (1 << 28) - 1;
+ for (int i = 28; i > 0; i -= 7) {
+ if (number > mask) {
+ symbol_store_.Add(static_cast<byte>(number >> i) | 0x80u);
+ number &= mask;
+ }
+ mask >>= 7;
+ }
+ symbol_store_.Add(static_cast<byte>(number));
+}
+
+
+} } // namespace v8::internal.
diff --git a/chromium/v8/src/preparse-data.h b/chromium/v8/src/preparse-data.h
new file mode 100644
index 00000000000..3a1e99d5d18
--- /dev/null
+++ b/chromium/v8/src/preparse-data.h
@@ -0,0 +1,231 @@
+// 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_PREPARSE_DATA_H_
+#define V8_PREPARSE_DATA_H_
+
+#include "allocation.h"
+#include "hashmap.h"
+#include "utils-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// ParserRecorder - Logging of preparser data.
+
+// Abstract interface for preparse data recorder.
+class ParserRecorder {
+ public:
+ ParserRecorder() { }
+ virtual ~ParserRecorder() { }
+
+ // Logs the scope and some details of a function literal in the source.
+ virtual void LogFunction(int start,
+ int end,
+ int literals,
+ int properties,
+ LanguageMode language_mode) = 0;
+
+ // Logs a symbol creation of a literal or identifier.
+ virtual void LogAsciiSymbol(int start, Vector<const char> literal) { }
+ virtual void LogUtf16Symbol(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) = 0;
+
+ virtual int function_position() = 0;
+
+ virtual int symbol_position() = 0;
+
+ virtual int symbol_ids() = 0;
+
+ virtual Vector<unsigned> ExtractData() = 0;
+
+ virtual void PauseRecording() = 0;
+
+ virtual void ResumeRecording() = 0;
+};
+
+
+// ----------------------------------------------------------------------------
+// FunctionLoggingParserRecorder - Record only function entries
+
+class FunctionLoggingParserRecorder : public ParserRecorder {
+ public:
+ FunctionLoggingParserRecorder();
+ virtual ~FunctionLoggingParserRecorder() {}
+
+ virtual void LogFunction(int start,
+ int end,
+ int literals,
+ int properties,
+ LanguageMode language_mode) {
+ function_store_.Add(start);
+ function_store_.Add(end);
+ function_store_.Add(literals);
+ function_store_.Add(properties);
+ function_store_.Add(language_mode);
+ }
+
+ // 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);
+
+ virtual int function_position() { return function_store_.size(); }
+
+
+ virtual Vector<unsigned> ExtractData() = 0;
+
+ virtual void PauseRecording() {
+ pause_count_++;
+ is_recording_ = false;
+ }
+
+ virtual void ResumeRecording() {
+ ASSERT(pause_count_ > 0);
+ if (--pause_count_ == 0) is_recording_ = !has_error();
+ }
+
+ protected:
+ bool has_error() {
+ return static_cast<bool>(preamble_[PreparseDataConstants::kHasErrorOffset]);
+ }
+
+ bool is_recording() {
+ return is_recording_;
+ }
+
+ void WriteString(Vector<const char> str);
+
+ Collector<unsigned> function_store_;
+ unsigned preamble_[PreparseDataConstants::kHeaderSize];
+ bool is_recording_;
+ int pause_count_;
+
+#ifdef DEBUG
+ int prev_start_;
+#endif
+};
+
+
+// ----------------------------------------------------------------------------
+// PartialParserRecorder - Record only function entries
+
+class PartialParserRecorder : public FunctionLoggingParserRecorder {
+ public:
+ PartialParserRecorder() : FunctionLoggingParserRecorder() { }
+ virtual void LogAsciiSymbol(int start, Vector<const char> literal) { }
+ virtual void LogUtf16Symbol(int start, Vector<const uc16> literal) { }
+ virtual ~PartialParserRecorder() { }
+ virtual Vector<unsigned> ExtractData();
+ virtual int symbol_position() { return 0; }
+ virtual int symbol_ids() { return 0; }
+};
+
+
+// ----------------------------------------------------------------------------
+// CompleteParserRecorder - Record both function entries and symbols.
+
+class CompleteParserRecorder: public FunctionLoggingParserRecorder {
+ public:
+ CompleteParserRecorder();
+ virtual ~CompleteParserRecorder() { }
+
+ virtual void LogAsciiSymbol(int start, Vector<const char> literal) {
+ if (!is_recording_) return;
+ int hash = vector_hash(literal);
+ LogSymbol(start, hash, true, Vector<const byte>::cast(literal));
+ }
+
+ virtual void LogUtf16Symbol(int start, Vector<const uc16> literal) {
+ if (!is_recording_) return;
+ int hash = vector_hash(literal);
+ LogSymbol(start, hash, false, Vector<const byte>::cast(literal));
+ }
+
+ virtual Vector<unsigned> ExtractData();
+
+ virtual int symbol_position() { return symbol_store_.size(); }
+ virtual int symbol_ids() { return symbol_id_; }
+
+ private:
+ struct Key {
+ bool is_ascii;
+ Vector<const byte> literal_bytes;
+ };
+
+ virtual void LogSymbol(int start,
+ int hash,
+ bool is_ascii,
+ Vector<const byte> literal);
+
+ template <typename Char>
+ static int vector_hash(Vector<const Char> string) {
+ int hash = 0;
+ for (int i = 0; i < string.length(); i++) {
+ int c = static_cast<int>(string[i]);
+ hash += c;
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ return hash;
+ }
+
+ static bool vector_compare(void* a, void* b) {
+ Key* string1 = reinterpret_cast<Key*>(a);
+ Key* string2 = reinterpret_cast<Key*>(b);
+ if (string1->is_ascii != string2->is_ascii) return false;
+ int length = string1->literal_bytes.length();
+ if (string2->literal_bytes.length() != length) return false;
+ return memcmp(string1->literal_bytes.start(),
+ string2->literal_bytes.start(), length) == 0;
+ }
+
+ // Write a non-negative number to the symbol store.
+ void WriteNumber(int number);
+
+ Collector<byte> literal_chars_;
+ Collector<byte> symbol_store_;
+ Collector<Key> symbol_keys_;
+ HashMap string_table_;
+ int symbol_id_;
+};
+
+
+} } // namespace v8::internal.
+
+#endif // V8_PREPARSE_DATA_H_
diff --git a/chromium/v8/src/preparser-api.cc b/chromium/v8/src/preparser-api.cc
new file mode 100644
index 00000000000..462dfe2290f
--- /dev/null
+++ b/chromium/v8/src/preparser-api.cc
@@ -0,0 +1,196 @@
+// 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.
+
+#ifdef _MSC_VER
+#define V8_WIN32_LEAN_AND_MEAN
+#include "win32-headers.h"
+#endif
+
+#include "../include/v8-preparser.h"
+
+#include "globals.h"
+#include "checks.h"
+#include "allocation.h"
+#include "utils.h"
+#include "list.h"
+#include "hashmap.h"
+#include "preparse-data-format.h"
+#include "preparse-data.h"
+#include "preparser.h"
+
+namespace v8 {
+namespace internal {
+
+// UTF16Buffer based on a v8::UnicodeInputStream.
+class InputStreamUtf16Buffer : public Utf16CharacterStream {
+ public:
+ /* The InputStreamUtf16Buffer maintains an internal buffer
+ * that is filled in chunks from the Utf16CharacterStream.
+ * It also maintains unlimited pushback capability, but optimized
+ * for small pushbacks.
+ * The pushback_buffer_ pointer points to the limit of pushbacks
+ * in the current buffer. There is room for a few pushback'ed chars before
+ * the buffer containing the most recently read chunk. If this is overflowed,
+ * an external buffer is allocated/reused to hold further pushbacks, and
+ * pushback_buffer_ and buffer_cursor_/buffer_end_ now points to the
+ * new buffer. When this buffer is read to the end again, the cursor is
+ * switched back to the internal buffer
+ */
+ explicit InputStreamUtf16Buffer(v8::UnicodeInputStream* stream)
+ : Utf16CharacterStream(),
+ stream_(stream),
+ pushback_buffer_(buffer_),
+ pushback_buffer_end_cache_(NULL),
+ pushback_buffer_backing_(NULL),
+ pushback_buffer_backing_size_(0) {
+ buffer_cursor_ = buffer_end_ = buffer_ + kPushBackSize;
+ }
+
+ virtual ~InputStreamUtf16Buffer() {
+ if (pushback_buffer_backing_ != NULL) {
+ DeleteArray(pushback_buffer_backing_);
+ }
+ }
+
+ virtual void PushBack(uc32 ch) {
+ ASSERT(pos_ > 0);
+ if (ch == kEndOfInput) {
+ pos_--;
+ return;
+ }
+ if (buffer_cursor_ <= pushback_buffer_) {
+ // No more room in the current buffer to do pushbacks.
+ if (pushback_buffer_end_cache_ == NULL) {
+ // We have overflowed the pushback space at the beginning of buffer_.
+ // Switch to using a separate allocated pushback buffer.
+ if (pushback_buffer_backing_ == NULL) {
+ // Allocate a buffer the first time we need it.
+ pushback_buffer_backing_ = NewArray<uc16>(kPushBackSize);
+ pushback_buffer_backing_size_ = kPushBackSize;
+ }
+ pushback_buffer_ = pushback_buffer_backing_;
+ pushback_buffer_end_cache_ = buffer_end_;
+ buffer_end_ = pushback_buffer_backing_ + pushback_buffer_backing_size_;
+ buffer_cursor_ = buffer_end_ - 1;
+ } else {
+ // Hit the bottom of the allocated pushback buffer.
+ // Double the buffer and continue.
+ uc16* new_buffer = NewArray<uc16>(pushback_buffer_backing_size_ * 2);
+ OS::MemCopy(new_buffer + pushback_buffer_backing_size_,
+ pushback_buffer_backing_,
+ pushback_buffer_backing_size_);
+ DeleteArray(pushback_buffer_backing_);
+ buffer_cursor_ = new_buffer + pushback_buffer_backing_size_;
+ pushback_buffer_backing_ = pushback_buffer_ = new_buffer;
+ buffer_end_ = pushback_buffer_backing_ + pushback_buffer_backing_size_;
+ }
+ }
+ pushback_buffer_[buffer_cursor_ - pushback_buffer_- 1] =
+ static_cast<uc16>(ch);
+ pos_--;
+ }
+
+ protected:
+ virtual bool ReadBlock() {
+ if (pushback_buffer_end_cache_ != NULL) {
+ buffer_cursor_ = buffer_;
+ buffer_end_ = pushback_buffer_end_cache_;
+ pushback_buffer_end_cache_ = NULL;
+ return buffer_end_ > buffer_cursor_;
+ }
+ // Copy the top of the buffer into the pushback area.
+ int32_t value;
+ uc16* buffer_start = buffer_ + kPushBackSize;
+ buffer_cursor_ = buffer_end_ = buffer_start;
+ while ((value = stream_->Next()) >= 0) {
+ if (value >
+ static_cast<int32_t>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
+ buffer_start[buffer_end_++ - buffer_start] =
+ unibrow::Utf16::LeadSurrogate(value);
+ buffer_start[buffer_end_++ - buffer_start] =
+ unibrow::Utf16::TrailSurrogate(value);
+ } else {
+ // buffer_end_ is a const pointer, but buffer_ is writable.
+ buffer_start[buffer_end_++ - buffer_start] = static_cast<uc16>(value);
+ }
+ // Stop one before the end of the buffer in case we get a surrogate pair.
+ if (buffer_end_ <= buffer_ + 1 + kPushBackSize + kBufferSize) break;
+ }
+ return buffer_end_ > buffer_start;
+ }
+
+ virtual unsigned SlowSeekForward(unsigned pos) {
+ // Seeking in the input is not used by preparsing.
+ // It's only used by the real parser based on preparser data.
+ UNIMPLEMENTED();
+ return 0;
+ }
+
+ private:
+ static const unsigned kBufferSize = 512;
+ static const unsigned kPushBackSize = 16;
+ v8::UnicodeInputStream* const stream_;
+ // Buffer holding first kPushBackSize characters of pushback buffer,
+ // then kBufferSize chars of read-ahead.
+ // The pushback buffer is only used if pushing back characters past
+ // the start of a block.
+ uc16 buffer_[kPushBackSize + kBufferSize];
+ // Limit of pushbacks before new allocation is necessary.
+ uc16* pushback_buffer_;
+ // Only if that pushback buffer at the start of buffer_ isn't sufficient
+ // is the following used.
+ const uc16* pushback_buffer_end_cache_;
+ uc16* pushback_buffer_backing_;
+ unsigned pushback_buffer_backing_size_;
+};
+
+} // namespace internal.
+
+
+UnicodeInputStream::~UnicodeInputStream() { }
+
+
+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::Scanner scanner(&unicode_cache);
+ scanner.Initialize(&buffer);
+ internal::CompleteParserRecorder recorder;
+ preparser::PreParser preparser(&scanner, &recorder, stack_limit);
+ preparser.set_allow_lazy(true);
+ preparser::PreParser::PreParseResult result = preparser.PreParseProgram();
+ if (result == preparser::PreParser::kPreParseStackOverflow) {
+ return PreParserData::StackOverflow();
+ }
+ internal::Vector<unsigned> pre_data = recorder.ExtractData();
+ size_t size = pre_data.length() * sizeof(pre_data[0]);
+ unsigned char* data = reinterpret_cast<unsigned char*>(pre_data.start());
+ return PreParserData(size, data);
+}
+
+} // namespace v8.
diff --git a/chromium/v8/src/preparser.cc b/chromium/v8/src/preparser.cc
new file mode 100644
index 00000000000..36a94a3315b
--- /dev/null
+++ b/chromium/v8/src/preparser.cc
@@ -0,0 +1,1832 @@
+// 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 <cmath>
+
+#include "../include/v8stdint.h"
+
+#include "allocation.h"
+#include "checks.h"
+#include "conversions.h"
+#include "conversions-inl.h"
+#include "globals.h"
+#include "hashmap.h"
+#include "list.h"
+#include "preparse-data-format.h"
+#include "preparse-data.h"
+#include "preparser.h"
+#include "unicode.h"
+#include "utils.h"
+
+#ifdef _MSC_VER
+namespace std {
+
+// Usually defined in math.h, but not in MSVC.
+// Abstracted to work
+int isfinite(double value);
+
+} // namespace std
+#endif
+
+namespace v8 {
+
+namespace preparser {
+
+PreParser::PreParseResult PreParser::PreParseLazyFunction(
+ i::LanguageMode mode, bool is_generator, 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);
+ function_scope.set_is_generator(is_generator);
+ 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.
+
+// The PreParser checks that the syntax follows the grammar for JavaScript,
+// and collects some information about the program along the way.
+// The grammar check is only performed in order to understand the program
+// sufficiently to deduce some information about it, that can be used
+// to speed up later parsing. Finding errors is not the goal of pre-parsing,
+// rather it is to speed up properly written and correct programs.
+// That means that contextual checks (like a label being declared where
+// it is used) are generally omitted.
+
+void PreParser::ReportUnexpectedToken(i::Token::Value token) {
+ // We don't report stack overflows here, to avoid increasing the
+ // stack depth even further. Instead we report it after parsing is
+ // over, in ParseProgram.
+ if (token == i::Token::ILLEGAL && stack_overflow_) {
+ return;
+ }
+ i::Scanner::Location source_location = scanner_->location();
+
+ // Four of the tokens are treated specially
+ switch (token) {
+ case i::Token::EOS:
+ return ReportMessageAt(source_location, "unexpected_eos", NULL);
+ case i::Token::NUMBER:
+ return ReportMessageAt(source_location, "unexpected_token_number", NULL);
+ case i::Token::STRING:
+ return ReportMessageAt(source_location, "unexpected_token_string", NULL);
+ case i::Token::IDENTIFIER:
+ return ReportMessageAt(source_location,
+ "unexpected_token_identifier", NULL);
+ case i::Token::FUTURE_RESERVED_WORD:
+ return ReportMessageAt(source_location, "unexpected_reserved", NULL);
+ case i::Token::FUTURE_STRICT_RESERVED_WORD:
+ return ReportMessageAt(source_location,
+ "unexpected_strict_reserved", NULL);
+ default:
+ const char* name = i::Token::String(token);
+ ReportMessageAt(source_location, "unexpected_token", name);
+ }
+}
+
+
+// Checks whether octal literal last seen is between beg_pos and end_pos.
+// If so, reports an error.
+void PreParser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) {
+ i::Scanner::Location octal = scanner_->octal_position();
+ if (beg_pos <= octal.beg_pos && octal.end_pos <= end_pos) {
+ ReportMessageAt(octal, "strict_octal_literal", NULL);
+ scanner_->clear_octal_position();
+ *ok = false;
+ }
+}
+
+
+#define CHECK_OK ok); \
+ if (!*ok) return kUnknownSourceElements; \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+
+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
+ // GeneratorDeclaration
+
+ 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);
+ }
+}
+
+
+PreParser::SourceElements PreParser::ParseSourceElements(int end_token,
+ bool* ok) {
+ // SourceElements ::
+ // (Statement)* <end_token>
+
+ bool allow_directive_prologue = true;
+ while (peek() != end_token) {
+ Statement statement = ParseSourceElement(CHECK_OK);
+ if (allow_directive_prologue) {
+ if (statement.IsUseStrictLiteral()) {
+ set_language_mode(allow_harmony_scoping() ?
+ i::EXTENDED_MODE : i::STRICT_MODE);
+ } else if (!statement.IsStringLiteral()) {
+ allow_directive_prologue = false;
+ }
+ }
+ }
+ return kUnknownSourceElements;
+}
+
+
+#undef CHECK_OK
+#define CHECK_OK ok); \
+ if (!*ok) return Statement::Default(); \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+
+PreParser::Statement PreParser::ParseStatement(bool* ok) {
+ // Statement ::
+ // Block
+ // VariableStatement
+ // EmptyStatement
+ // ExpressionStatement
+ // IfStatement
+ // IterationStatement
+ // ContinueStatement
+ // BreakStatement
+ // ReturnStatement
+ // WithStatement
+ // LabelledStatement
+ // SwitchStatement
+ // ThrowStatement
+ // TryStatement
+ // DebuggerStatement
+
+ // Note: Since labels can only be used by 'break' and 'continue'
+ // statements, which themselves are only valid within blocks,
+ // iterations or 'switch' statements (i.e., BreakableStatements),
+ // labels can be simply ignored in all other cases; except for
+ // trivial labeled break statements 'label: break label' which is
+ // parsed into an empty statement.
+
+ // Keep the source position of the statement
+ switch (peek()) {
+ case i::Token::LBRACE:
+ return ParseBlock(ok);
+
+ case i::Token::CONST:
+ case i::Token::LET:
+ case i::Token::VAR:
+ return ParseVariableStatement(kStatement, ok);
+
+ case i::Token::SEMICOLON:
+ Next();
+ return Statement::Default();
+
+ case i::Token::IF:
+ return ParseIfStatement(ok);
+
+ case i::Token::DO:
+ return ParseDoWhileStatement(ok);
+
+ case i::Token::WHILE:
+ return ParseWhileStatement(ok);
+
+ case i::Token::FOR:
+ return ParseForStatement(ok);
+
+ case i::Token::CONTINUE:
+ return ParseContinueStatement(ok);
+
+ case i::Token::BREAK:
+ return ParseBreakStatement(ok);
+
+ case i::Token::RETURN:
+ return ParseReturnStatement(ok);
+
+ case i::Token::WITH:
+ return ParseWithStatement(ok);
+
+ case i::Token::SWITCH:
+ return ParseSwitchStatement(ok);
+
+ case i::Token::THROW:
+ return ParseThrowStatement(ok);
+
+ case i::Token::TRY:
+ return ParseTryStatement(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);
+
+ default:
+ return ParseExpressionOrLabelledStatement(ok);
+ }
+}
+
+
+PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
+ // FunctionDeclaration ::
+ // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
+ // GeneratorDeclaration ::
+ // 'function' '*' Identifier '(' FormalParameterListopt ')'
+ // '{' FunctionBody '}'
+ Expect(i::Token::FUNCTION, CHECK_OK);
+
+ bool is_generator = allow_generators_ && Check(i::Token::MUL);
+ Identifier identifier = ParseIdentifier(CHECK_OK);
+ i::Scanner::Location location = scanner_->location();
+
+ Expression function_value = ParseFunctionLiteral(is_generator, CHECK_OK);
+
+ if (function_value.IsStrictFunction() &&
+ !identifier.IsValidStrictVariable()) {
+ // Strict mode violation, using either reserved word or eval/arguments
+ // as name of strict function.
+ const char* type = "strict_function_name";
+ if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
+ type = "strict_reserved_word";
+ }
+ ReportMessageAt(location, type, NULL);
+ *ok = false;
+ }
+ return Statement::FunctionDeclaration();
+}
+
+
+PreParser::Statement PreParser::ParseBlock(bool* ok) {
+ // Block ::
+ // '{' Statement* '}'
+
+ // Note that a Block does not introduce a new execution scope!
+ // (ECMA-262, 3rd, 12.2)
+ //
+ Expect(i::Token::LBRACE, CHECK_OK);
+ while (peek() != i::Token::RBRACE) {
+ if (is_extended_mode()) {
+ ParseSourceElement(CHECK_OK);
+ } else {
+ ParseStatement(CHECK_OK);
+ }
+ }
+ Expect(i::Token::RBRACE, ok);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseVariableStatement(
+ VariableDeclarationContext var_context,
+ bool* ok) {
+ // VariableStatement ::
+ // VariableDeclarations ';'
+
+ Statement result = ParseVariableDeclarations(var_context,
+ NULL,
+ NULL,
+ CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ return result;
+}
+
+
+// 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
+// to initialize it properly. This mechanism is also used for the parsing
+// 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) {
+ // 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.beg_pos, location.end_pos,
+ "illegal_let", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
+ Consume(i::Token::LET);
+ if (var_context != kSourceElement &&
+ var_context != kForStatement) {
+ i::Scanner::Location location = scanner_->peek_location();
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ "unprotected_let", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
+ } else {
+ *ok = false;
+ return Statement::Default();
+ }
+
+ // 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). The scope
+ // of a let declared variable is the scope of the immediately enclosing
+ // block.
+ int nvars = 0; // the number of variables declared
+ do {
+ // Parse variable name.
+ if (nvars > 0) Consume(i::Token::COMMA);
+ Identifier identifier = ParseIdentifier(CHECK_OK);
+ if (!is_classic_mode() && !identifier.IsValidStrictVariable()) {
+ StrictModeIdentifierViolation(scanner_->location(),
+ "strict_var_name",
+ identifier,
+ ok);
+ return Statement::Default();
+ }
+ nvars++;
+ 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);
+
+ if (num_decl != NULL) *num_decl = nvars;
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
+ // ExpressionStatement | LabelledStatement ::
+ // Expression ';'
+ // Identifier ':' Statement
+
+ Expression expr = ParseExpression(true, CHECK_OK);
+ if (expr.IsRawIdentifier()) {
+ ASSERT(!expr.AsIdentifier().IsFutureReserved());
+ ASSERT(is_classic_mode() ||
+ (!expr.AsIdentifier().IsFutureStrictReserved() &&
+ !expr.AsIdentifier().IsYield()));
+ if (peek() == i::Token::COLON) {
+ Consume(i::Token::COLON);
+ return ParseStatement(ok);
+ }
+ // Preparsing is disabled for extensions (because the extension details
+ // aren't passed to lazily compiled functions), so we don't
+ // accept "native function" in the preparser.
+ }
+ // Parsed expression statement.
+ ExpectSemicolon(CHECK_OK);
+ return Statement::ExpressionStatement(expr);
+}
+
+
+PreParser::Statement PreParser::ParseIfStatement(bool* ok) {
+ // IfStatement ::
+ // 'if' '(' Expression ')' Statement ('else' Statement)?
+
+ Expect(i::Token::IF, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+ ParseStatement(CHECK_OK);
+ if (peek() == i::Token::ELSE) {
+ Next();
+ ParseStatement(CHECK_OK);
+ }
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
+ // ContinueStatement ::
+ // 'continue' [no line terminator] Identifier? ';'
+
+ Expect(i::Token::CONTINUE, CHECK_OK);
+ i::Token::Value tok = peek();
+ if (!scanner_->HasAnyLineTerminatorBeforeNext() &&
+ tok != i::Token::SEMICOLON &&
+ tok != i::Token::RBRACE &&
+ tok != i::Token::EOS) {
+ ParseIdentifier(CHECK_OK);
+ }
+ ExpectSemicolon(CHECK_OK);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
+ // BreakStatement ::
+ // 'break' [no line terminator] Identifier? ';'
+
+ Expect(i::Token::BREAK, CHECK_OK);
+ i::Token::Value tok = peek();
+ if (!scanner_->HasAnyLineTerminatorBeforeNext() &&
+ tok != i::Token::SEMICOLON &&
+ tok != i::Token::RBRACE &&
+ tok != i::Token::EOS) {
+ ParseIdentifier(CHECK_OK);
+ }
+ ExpectSemicolon(CHECK_OK);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
+ // ReturnStatement ::
+ // 'return' [no line terminator] Expression? ';'
+
+ // Consume the return token. It is necessary to do the before
+ // reporting any errors on it, because of the way errors are
+ // reported (underlining).
+ Expect(i::Token::RETURN, CHECK_OK);
+
+ // An ECMAScript program is considered syntactically incorrect if it
+ // contains a return statement that is not within the body of a
+ // function. See ECMA-262, section 12.9, page 67.
+ // This is not handled during preparsing.
+
+ i::Token::Value tok = peek();
+ if (!scanner_->HasAnyLineTerminatorBeforeNext() &&
+ tok != i::Token::SEMICOLON &&
+ tok != i::Token::RBRACE &&
+ tok != i::Token::EOS) {
+ ParseExpression(true, CHECK_OK);
+ }
+ ExpectSemicolon(CHECK_OK);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseWithStatement(bool* ok) {
+ // WithStatement ::
+ // 'with' '(' Expression ')' Statement
+ Expect(i::Token::WITH, CHECK_OK);
+ if (!is_classic_mode()) {
+ i::Scanner::Location location = scanner_->location();
+ ReportMessageAt(location, "strict_mode_with", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ Scope::InsideWith iw(scope_);
+ ParseStatement(CHECK_OK);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) {
+ // SwitchStatement ::
+ // 'switch' '(' Expression ')' '{' CaseClause* '}'
+
+ Expect(i::Token::SWITCH, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ Expect(i::Token::LBRACE, CHECK_OK);
+ i::Token::Value token = peek();
+ while (token != i::Token::RBRACE) {
+ if (token == i::Token::CASE) {
+ Expect(i::Token::CASE, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ } else {
+ Expect(i::Token::DEFAULT, CHECK_OK);
+ }
+ Expect(i::Token::COLON, CHECK_OK);
+ token = peek();
+ while (token != i::Token::CASE &&
+ token != i::Token::DEFAULT &&
+ token != i::Token::RBRACE) {
+ ParseStatement(CHECK_OK);
+ token = peek();
+ }
+ }
+ Expect(i::Token::RBRACE, ok);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) {
+ // DoStatement ::
+ // 'do' Statement 'while' '(' Expression ')' ';'
+
+ Expect(i::Token::DO, CHECK_OK);
+ ParseStatement(CHECK_OK);
+ Expect(i::Token::WHILE, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, ok);
+ if (peek() == i::Token::SEMICOLON) Consume(i::Token::SEMICOLON);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseWhileStatement(bool* ok) {
+ // WhileStatement ::
+ // 'while' '(' Expression ')' Statement
+
+ Expect(i::Token::WHILE, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+ ParseStatement(ok);
+ return Statement::Default();
+}
+
+
+bool PreParser::CheckInOrOf(bool accept_OF) {
+ if (peek() == i::Token::IN ||
+ (allow_for_of() && accept_OF && peek() == i::Token::IDENTIFIER &&
+ scanner_->is_next_contextual_keyword(v8::internal::CStrVector("of")))) {
+ Next();
+ return true;
+ }
+ return false;
+}
+
+
+PreParser::Statement PreParser::ParseForStatement(bool* ok) {
+ // ForStatement ::
+ // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
+
+ Expect(i::Token::FOR, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_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;
+ VariableDeclarationProperties decl_props = kHasNoInitializers;
+ ParseVariableDeclarations(
+ kForStatement, &decl_props, &decl_count, CHECK_OK);
+ bool has_initializers = decl_props == kHasInitializers;
+ bool accept_IN = decl_count == 1 && !(is_let && has_initializers);
+ bool accept_OF = !has_initializers;
+ if (accept_IN && CheckInOrOf(accept_OF)) {
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ ParseStatement(CHECK_OK);
+ return Statement::Default();
+ }
+ } else {
+ Expression lhs = ParseExpression(false, CHECK_OK);
+ if (CheckInOrOf(lhs.IsIdentifier())) {
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ ParseStatement(CHECK_OK);
+ return Statement::Default();
+ }
+ }
+ }
+
+ // Parsed initializer at this point.
+ Expect(i::Token::SEMICOLON, CHECK_OK);
+
+ if (peek() != i::Token::SEMICOLON) {
+ ParseExpression(true, CHECK_OK);
+ }
+ Expect(i::Token::SEMICOLON, CHECK_OK);
+
+ if (peek() != i::Token::RPAREN) {
+ ParseExpression(true, CHECK_OK);
+ }
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ ParseStatement(ok);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseThrowStatement(bool* ok) {
+ // ThrowStatement ::
+ // 'throw' [no line terminator] Expression ';'
+
+ Expect(i::Token::THROW, CHECK_OK);
+ if (scanner_->HasAnyLineTerminatorBeforeNext()) {
+ i::Scanner::Location pos = scanner_->location();
+ ReportMessageAt(pos, "newline_after_throw", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
+ ParseExpression(true, CHECK_OK);
+ ExpectSemicolon(ok);
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
+ // TryStatement ::
+ // 'try' Block Catch
+ // 'try' Block Finally
+ // 'try' Block Catch Finally
+ //
+ // Catch ::
+ // 'catch' '(' Identifier ')' Block
+ //
+ // Finally ::
+ // 'finally' Block
+
+ // In preparsing, allow any number of catch/finally blocks, including zero
+ // of both.
+
+ Expect(i::Token::TRY, CHECK_OK);
+
+ ParseBlock(CHECK_OK);
+
+ bool catch_or_finally_seen = false;
+ if (peek() == i::Token::CATCH) {
+ Consume(i::Token::CATCH);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ Identifier id = ParseIdentifier(CHECK_OK);
+ if (!is_classic_mode() && !id.IsValidStrictVariable()) {
+ StrictModeIdentifierViolation(scanner_->location(),
+ "strict_catch_variable",
+ id,
+ ok);
+ return Statement::Default();
+ }
+ Expect(i::Token::RPAREN, CHECK_OK);
+ { Scope::InsideWith iw(scope_);
+ ParseBlock(CHECK_OK);
+ }
+ catch_or_finally_seen = true;
+ }
+ if (peek() == i::Token::FINALLY) {
+ Consume(i::Token::FINALLY);
+ ParseBlock(CHECK_OK);
+ catch_or_finally_seen = true;
+ }
+ if (!catch_or_finally_seen) {
+ *ok = false;
+ }
+ return Statement::Default();
+}
+
+
+PreParser::Statement PreParser::ParseDebuggerStatement(bool* ok) {
+ // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
+ // contexts this is used as a statement which invokes the debugger as if a
+ // break point is present.
+ // DebuggerStatement ::
+ // 'debugger' ';'
+
+ Expect(i::Token::DEBUGGER, CHECK_OK);
+ ExpectSemicolon(ok);
+ return Statement::Default();
+}
+
+
+#undef CHECK_OK
+#define CHECK_OK ok); \
+ if (!*ok) return Expression::Default(); \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+
+// Precedence = 1
+PreParser::Expression PreParser::ParseExpression(bool accept_IN, bool* ok) {
+ // Expression ::
+ // AssignmentExpression
+ // Expression ',' AssignmentExpression
+
+ Expression result = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ while (peek() == i::Token::COMMA) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ ParseAssignmentExpression(accept_IN, CHECK_OK);
+ result = Expression::Default();
+ }
+ return result;
+}
+
+
+// Precedence = 2
+PreParser::Expression PreParser::ParseAssignmentExpression(bool accept_IN,
+ bool* ok) {
+ // AssignmentExpression ::
+ // ConditionalExpression
+ // YieldExpression
+ // LeftHandSideExpression AssignmentOperator AssignmentExpression
+
+ if (scope_->is_generator() && peek() == i::Token::YIELD) {
+ return ParseYieldExpression(ok);
+ }
+
+ i::Scanner::Location before = scanner_->peek_location();
+ Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK);
+
+ if (!i::Token::IsAssignmentOp(peek())) {
+ // Parsed conditional expression only (no assignment).
+ return expression;
+ }
+
+ if (!is_classic_mode() &&
+ expression.IsIdentifier() &&
+ expression.AsIdentifier().IsEvalOrArguments()) {
+ i::Scanner::Location after = scanner_->location();
+ ReportMessageAt(before.beg_pos, after.end_pos,
+ "strict_lhs_assignment", NULL);
+ *ok = false;
+ return Expression::Default();
+ }
+
+ i::Token::Value op = Next(); // Get assignment operator.
+ ParseAssignmentExpression(accept_IN, CHECK_OK);
+
+ if ((op == i::Token::ASSIGN) && expression.IsThisProperty()) {
+ scope_->AddProperty();
+ }
+
+ return Expression::Default();
+}
+
+
+// Precedence = 3
+PreParser::Expression PreParser::ParseYieldExpression(bool* ok) {
+ // YieldExpression ::
+ // 'yield' '*'? AssignmentExpression
+ Consume(i::Token::YIELD);
+ Check(i::Token::MUL);
+
+ ParseAssignmentExpression(false, CHECK_OK);
+
+ return Expression::Default();
+}
+
+
+// Precedence = 3
+PreParser::Expression PreParser::ParseConditionalExpression(bool accept_IN,
+ bool* ok) {
+ // ConditionalExpression ::
+ // LogicalOrExpression
+ // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression
+
+ // We start using the binary expression parser for prec >= 4 only!
+ Expression expression = ParseBinaryExpression(4, accept_IN, CHECK_OK);
+ if (peek() != i::Token::CONDITIONAL) return expression;
+ Consume(i::Token::CONDITIONAL);
+ // In parsing the first assignment expression in conditional
+ // expressions we always accept the 'in' keyword; see ECMA-262,
+ // section 11.12, page 58.
+ ParseAssignmentExpression(true, CHECK_OK);
+ Expect(i::Token::COLON, CHECK_OK);
+ ParseAssignmentExpression(accept_IN, CHECK_OK);
+ return Expression::Default();
+}
+
+
+int PreParser::Precedence(i::Token::Value tok, bool accept_IN) {
+ if (tok == i::Token::IN && !accept_IN)
+ return 0; // 0 precedence will terminate binary expression parsing
+
+ return i::Token::Precedence(tok);
+}
+
+
+// Precedence >= 4
+PreParser::Expression PreParser::ParseBinaryExpression(int prec,
+ bool accept_IN,
+ bool* ok) {
+ Expression result = ParseUnaryExpression(CHECK_OK);
+ for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) {
+ // prec1 >= 4
+ while (Precedence(peek(), accept_IN) == prec1) {
+ Next();
+ ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK);
+ result = Expression::Default();
+ }
+ }
+ return result;
+}
+
+
+PreParser::Expression PreParser::ParseUnaryExpression(bool* ok) {
+ // UnaryExpression ::
+ // PostfixExpression
+ // 'delete' UnaryExpression
+ // 'void' UnaryExpression
+ // 'typeof' UnaryExpression
+ // '++' UnaryExpression
+ // '--' UnaryExpression
+ // '+' UnaryExpression
+ // '-' UnaryExpression
+ // '~' UnaryExpression
+ // '!' UnaryExpression
+
+ i::Token::Value op = peek();
+ if (i::Token::IsUnaryOp(op)) {
+ op = Next();
+ ParseUnaryExpression(ok);
+ return Expression::Default();
+ } else if (i::Token::IsCountOp(op)) {
+ op = Next();
+ i::Scanner::Location before = scanner_->peek_location();
+ Expression expression = ParseUnaryExpression(CHECK_OK);
+ if (!is_classic_mode() &&
+ expression.IsIdentifier() &&
+ expression.AsIdentifier().IsEvalOrArguments()) {
+ i::Scanner::Location after = scanner_->location();
+ ReportMessageAt(before.beg_pos, after.end_pos,
+ "strict_lhs_prefix", NULL);
+ *ok = false;
+ }
+ return Expression::Default();
+ } else {
+ return ParsePostfixExpression(ok);
+ }
+}
+
+
+PreParser::Expression PreParser::ParsePostfixExpression(bool* ok) {
+ // PostfixExpression ::
+ // LeftHandSideExpression ('++' | '--')?
+
+ i::Scanner::Location before = scanner_->peek_location();
+ Expression expression = ParseLeftHandSideExpression(CHECK_OK);
+ if (!scanner_->HasAnyLineTerminatorBeforeNext() &&
+ i::Token::IsCountOp(peek())) {
+ if (!is_classic_mode() &&
+ expression.IsIdentifier() &&
+ expression.AsIdentifier().IsEvalOrArguments()) {
+ i::Scanner::Location after = scanner_->location();
+ ReportMessageAt(before.beg_pos, after.end_pos,
+ "strict_lhs_postfix", NULL);
+ *ok = false;
+ return Expression::Default();
+ }
+ Next();
+ return Expression::Default();
+ }
+ return expression;
+}
+
+
+PreParser::Expression PreParser::ParseLeftHandSideExpression(bool* ok) {
+ // LeftHandSideExpression ::
+ // (NewExpression | MemberExpression) ...
+
+ Expression result = Expression::Default();
+ if (peek() == i::Token::NEW) {
+ result = ParseNewExpression(CHECK_OK);
+ } else {
+ result = ParseMemberExpression(CHECK_OK);
+ }
+
+ while (true) {
+ switch (peek()) {
+ case i::Token::LBRACK: {
+ Consume(i::Token::LBRACK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RBRACK, CHECK_OK);
+ if (result.IsThis()) {
+ result = Expression::ThisProperty();
+ } else {
+ result = Expression::Default();
+ }
+ break;
+ }
+
+ case i::Token::LPAREN: {
+ ParseArguments(CHECK_OK);
+ result = Expression::Default();
+ break;
+ }
+
+ case i::Token::PERIOD: {
+ Consume(i::Token::PERIOD);
+ ParseIdentifierName(CHECK_OK);
+ if (result.IsThis()) {
+ result = Expression::ThisProperty();
+ } else {
+ result = Expression::Default();
+ }
+ break;
+ }
+
+ default:
+ return result;
+ }
+ }
+}
+
+
+PreParser::Expression PreParser::ParseNewExpression(bool* ok) {
+ // NewExpression ::
+ // ('new')+ MemberExpression
+
+ // The grammar for new expressions is pretty warped. The keyword
+ // 'new' can either be a part of the new expression (where it isn't
+ // followed by an argument list) or a part of the member expression,
+ // where it must be followed by an argument list. To accommodate
+ // this, we parse the 'new' keywords greedily and keep track of how
+ // many we have parsed. This information is then passed on to the
+ // member expression parser, which is only allowed to match argument
+ // lists as long as it has 'new' prefixes left
+ unsigned new_count = 0;
+ do {
+ Consume(i::Token::NEW);
+ new_count++;
+ } while (peek() == i::Token::NEW);
+
+ return ParseMemberWithNewPrefixesExpression(new_count, ok);
+}
+
+
+PreParser::Expression PreParser::ParseMemberExpression(bool* ok) {
+ return ParseMemberWithNewPrefixesExpression(0, ok);
+}
+
+
+PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
+ unsigned new_count, bool* ok) {
+ // MemberExpression ::
+ // (PrimaryExpression | FunctionLiteral)
+ // ('[' Expression ']' | '.' Identifier | Arguments)*
+
+ // Parse the initial primary or function expression.
+ Expression result = Expression::Default();
+ if (peek() == i::Token::FUNCTION) {
+ Consume(i::Token::FUNCTION);
+
+ bool is_generator = allow_generators_ && Check(i::Token::MUL);
+ Identifier identifier = Identifier::Default();
+ if (peek_any_identifier()) {
+ identifier = ParseIdentifier(CHECK_OK);
+ }
+ result = ParseFunctionLiteral(is_generator, CHECK_OK);
+ if (result.IsStrictFunction() && !identifier.IsValidStrictVariable()) {
+ StrictModeIdentifierViolation(scanner_->location(),
+ "strict_function_name",
+ identifier,
+ ok);
+ return Expression::Default();
+ }
+ } else {
+ result = ParsePrimaryExpression(CHECK_OK);
+ }
+
+ while (true) {
+ switch (peek()) {
+ case i::Token::LBRACK: {
+ Consume(i::Token::LBRACK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RBRACK, CHECK_OK);
+ if (result.IsThis()) {
+ result = Expression::ThisProperty();
+ } else {
+ result = Expression::Default();
+ }
+ break;
+ }
+ case i::Token::PERIOD: {
+ Consume(i::Token::PERIOD);
+ ParseIdentifierName(CHECK_OK);
+ if (result.IsThis()) {
+ result = Expression::ThisProperty();
+ } else {
+ result = Expression::Default();
+ }
+ break;
+ }
+ case i::Token::LPAREN: {
+ if (new_count == 0) return result;
+ // Consume one of the new prefixes (already parsed).
+ ParseArguments(CHECK_OK);
+ new_count--;
+ result = Expression::Default();
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+
+PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
+ // PrimaryExpression ::
+ // 'this'
+ // 'null'
+ // 'true'
+ // 'false'
+ // Identifier
+ // Number
+ // String
+ // ArrayLiteral
+ // ObjectLiteral
+ // RegExpLiteral
+ // '(' Expression ')'
+
+ Expression result = Expression::Default();
+ switch (peek()) {
+ case i::Token::THIS: {
+ Next();
+ result = Expression::This();
+ break;
+ }
+
+ case i::Token::FUTURE_RESERVED_WORD:
+ case i::Token::FUTURE_STRICT_RESERVED_WORD:
+ case i::Token::YIELD:
+ case i::Token::IDENTIFIER: {
+ Identifier id = ParseIdentifier(CHECK_OK);
+ result = Expression::FromIdentifier(id);
+ break;
+ }
+
+ case i::Token::NULL_LITERAL:
+ case i::Token::TRUE_LITERAL:
+ case i::Token::FALSE_LITERAL:
+ case i::Token::NUMBER: {
+ Next();
+ break;
+ }
+ case i::Token::STRING: {
+ Next();
+ result = GetStringSymbol();
+ break;
+ }
+
+ case i::Token::ASSIGN_DIV:
+ result = ParseRegExpLiteral(true, CHECK_OK);
+ break;
+
+ case i::Token::DIV:
+ result = ParseRegExpLiteral(false, CHECK_OK);
+ break;
+
+ case i::Token::LBRACK:
+ result = ParseArrayLiteral(CHECK_OK);
+ break;
+
+ case i::Token::LBRACE:
+ result = ParseObjectLiteral(CHECK_OK);
+ break;
+
+ case i::Token::LPAREN:
+ Consume(i::Token::LPAREN);
+ parenthesized_function_ = (peek() == i::Token::FUNCTION);
+ result = ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+ result = result.Parenthesize();
+ break;
+
+ case i::Token::MOD:
+ result = ParseV8Intrinsic(CHECK_OK);
+ break;
+
+ default: {
+ Next();
+ *ok = false;
+ return Expression::Default();
+ }
+ }
+
+ return result;
+}
+
+
+PreParser::Expression PreParser::ParseArrayLiteral(bool* ok) {
+ // ArrayLiteral ::
+ // '[' Expression? (',' Expression?)* ']'
+ Expect(i::Token::LBRACK, CHECK_OK);
+ while (peek() != i::Token::RBRACK) {
+ if (peek() != i::Token::COMMA) {
+ ParseAssignmentExpression(true, CHECK_OK);
+ }
+ if (peek() != i::Token::RBRACK) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ }
+ }
+ Expect(i::Token::RBRACK, CHECK_OK);
+
+ scope_->NextMaterializedLiteralIndex();
+ return Expression::Default();
+}
+
+void PreParser::CheckDuplicate(DuplicateFinder* finder,
+ i::Token::Value property,
+ int type,
+ bool* ok) {
+ int old_type;
+ if (property == i::Token::NUMBER) {
+ old_type = finder->AddNumber(scanner_->literal_ascii_string(), type);
+ } else if (scanner_->is_literal_ascii()) {
+ old_type = finder->AddAsciiSymbol(scanner_->literal_ascii_string(),
+ type);
+ } else {
+ old_type = finder->AddUtf16Symbol(scanner_->literal_utf16_string(), type);
+ }
+ if (HasConflict(old_type, type)) {
+ if (IsDataDataConflict(old_type, type)) {
+ // Both are data properties.
+ if (is_classic_mode()) return;
+ ReportMessageAt(scanner_->location(),
+ "strict_duplicate_property", NULL);
+ } else if (IsDataAccessorConflict(old_type, type)) {
+ // Both a data and an accessor property with the same name.
+ ReportMessageAt(scanner_->location(),
+ "accessor_data_property", NULL);
+ } else {
+ ASSERT(IsAccessorAccessorConflict(old_type, type));
+ // Both accessors of the same type.
+ ReportMessageAt(scanner_->location(),
+ "accessor_get_set", NULL);
+ }
+ *ok = false;
+ }
+}
+
+
+PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
+ // ObjectLiteral ::
+ // '{' (
+ // ((IdentifierName | String | Number) ':' AssignmentExpression)
+ // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral)
+ // )*[','] '}'
+
+ Expect(i::Token::LBRACE, CHECK_OK);
+ DuplicateFinder duplicate_finder(scanner_->unicode_cache());
+ while (peek() != i::Token::RBRACE) {
+ i::Token::Value next = peek();
+ switch (next) {
+ case i::Token::IDENTIFIER:
+ case i::Token::FUTURE_RESERVED_WORD:
+ case i::Token::FUTURE_STRICT_RESERVED_WORD: {
+ bool is_getter = false;
+ bool is_setter = false;
+ ParseIdentifierNameOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
+ if ((is_getter || is_setter) && peek() != i::Token::COLON) {
+ i::Token::Value name = Next();
+ bool is_keyword = i::Token::IsKeyword(name);
+ if (name != i::Token::IDENTIFIER &&
+ name != i::Token::FUTURE_RESERVED_WORD &&
+ name != i::Token::FUTURE_STRICT_RESERVED_WORD &&
+ name != i::Token::NUMBER &&
+ name != i::Token::STRING &&
+ !is_keyword) {
+ *ok = false;
+ return Expression::Default();
+ }
+ if (!is_keyword) {
+ LogSymbol();
+ }
+ PropertyType type = is_getter ? kGetterProperty : kSetterProperty;
+ CheckDuplicate(&duplicate_finder, name, type, CHECK_OK);
+ ParseFunctionLiteral(false, CHECK_OK);
+ if (peek() != i::Token::RBRACE) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ }
+ continue; // restart the while
+ }
+ CheckDuplicate(&duplicate_finder, next, kValueProperty, CHECK_OK);
+ break;
+ }
+ case i::Token::STRING:
+ Consume(next);
+ CheckDuplicate(&duplicate_finder, next, kValueProperty, CHECK_OK);
+ GetStringSymbol();
+ break;
+ case i::Token::NUMBER:
+ Consume(next);
+ CheckDuplicate(&duplicate_finder, next, kValueProperty, CHECK_OK);
+ break;
+ default:
+ if (i::Token::IsKeyword(next)) {
+ Consume(next);
+ CheckDuplicate(&duplicate_finder, next, kValueProperty, CHECK_OK);
+ } else {
+ // Unexpected token.
+ *ok = false;
+ return Expression::Default();
+ }
+ }
+
+ Expect(i::Token::COLON, CHECK_OK);
+ ParseAssignmentExpression(true, CHECK_OK);
+
+ // TODO(1240767): Consider allowing trailing comma.
+ if (peek() != i::Token::RBRACE) Expect(i::Token::COMMA, CHECK_OK);
+ }
+ Expect(i::Token::RBRACE, CHECK_OK);
+
+ scope_->NextMaterializedLiteralIndex();
+ return Expression::Default();
+}
+
+
+PreParser::Expression PreParser::ParseRegExpLiteral(bool seen_equal,
+ bool* ok) {
+ if (!scanner_->ScanRegExpPattern(seen_equal)) {
+ Next();
+ ReportMessageAt(scanner_->location(), "unterminated_regexp", NULL);
+ *ok = false;
+ return Expression::Default();
+ }
+
+ scope_->NextMaterializedLiteralIndex();
+
+ if (!scanner_->ScanRegExpFlags()) {
+ Next();
+ ReportMessageAt(scanner_->location(), "invalid_regexp_flags", NULL);
+ *ok = false;
+ return Expression::Default();
+ }
+ Next();
+ return Expression::Default();
+}
+
+
+PreParser::Arguments PreParser::ParseArguments(bool* ok) {
+ // Arguments ::
+ // '(' (AssignmentExpression)*[','] ')'
+
+ Expect(i::Token::LPAREN, ok);
+ if (!*ok) return -1;
+ bool done = (peek() == i::Token::RPAREN);
+ int argc = 0;
+ while (!done) {
+ ParseAssignmentExpression(true, ok);
+ if (!*ok) return -1;
+ argc++;
+ done = (peek() == i::Token::RPAREN);
+ if (!done) {
+ Expect(i::Token::COMMA, ok);
+ if (!*ok) return -1;
+ }
+ }
+ Expect(i::Token::RPAREN, ok);
+ return argc;
+}
+
+
+PreParser::Expression PreParser::ParseFunctionLiteral(bool is_generator,
+ bool* ok) {
+ // Function ::
+ // '(' FormalParameterList? ')' '{' FunctionBody '}'
+
+ // Parse function body.
+ ScopeType outer_scope_type = scope_->type();
+ bool inside_with = scope_->IsInsideWith();
+ Scope function_scope(&scope_, kFunctionScope);
+ function_scope.set_is_generator(is_generator);
+ // FormalParameterList ::
+ // '(' (Identifier)*[','] ')'
+ Expect(i::Token::LPAREN, CHECK_OK);
+ int start_position = scanner_->location().beg_pos;
+ bool done = (peek() == i::Token::RPAREN);
+ DuplicateFinder duplicate_finder(scanner_->unicode_cache());
+ while (!done) {
+ Identifier id = ParseIdentifier(CHECK_OK);
+ if (!id.IsValidStrictVariable()) {
+ StrictModeIdentifierViolation(scanner_->location(),
+ "strict_param_name",
+ id,
+ CHECK_OK);
+ }
+ int prev_value;
+ if (scanner_->is_literal_ascii()) {
+ prev_value =
+ duplicate_finder.AddAsciiSymbol(scanner_->literal_ascii_string(), 1);
+ } else {
+ prev_value =
+ duplicate_finder.AddUtf16Symbol(scanner_->literal_utf16_string(), 1);
+ }
+
+ if (prev_value != 0) {
+ SetStrictModeViolation(scanner_->location(),
+ "strict_param_dupe",
+ CHECK_OK);
+ }
+ done = (peek() == i::Token::RPAREN);
+ if (!done) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ }
+ }
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ // 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.
+ bool is_lazily_compiled = (outer_scope_type == kTopLevelScope &&
+ !inside_with && allow_lazy_ &&
+ !parenthesized_function_);
+ parenthesized_function_ = false;
+
+ Expect(i::Token::LBRACE, CHECK_OK);
+ if (is_lazily_compiled) {
+ ParseLazyFunctionLiteralBody(CHECK_OK);
+ } else {
+ ParseSourceElements(i::Token::RBRACE, ok);
+ }
+ Expect(i::Token::RBRACE, CHECK_OK);
+
+ 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);
+ return Expression::StrictFunction();
+ }
+
+ return Expression::Default();
+}
+
+
+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);
+
+ return Expression::Default();
+}
+
+#undef CHECK_OK
+
+
+void PreParser::ExpectSemicolon(bool* ok) {
+ // Check for automatic semicolon insertion according to
+ // the rules given in ECMA-262, section 7.9, page 21.
+ i::Token::Value tok = peek();
+ if (tok == i::Token::SEMICOLON) {
+ Next();
+ return;
+ }
+ if (scanner_->HasAnyLineTerminatorBeforeNext() ||
+ tok == i::Token::RBRACE ||
+ tok == i::Token::EOS) {
+ return;
+ }
+ Expect(i::Token::SEMICOLON, ok);
+}
+
+
+void PreParser::LogSymbol() {
+ int identifier_pos = scanner_->location().beg_pos;
+ if (scanner_->is_literal_ascii()) {
+ log_->LogAsciiSymbol(identifier_pos, scanner_->literal_ascii_string());
+ } else {
+ log_->LogUtf16Symbol(identifier_pos, scanner_->literal_utf16_string());
+ }
+}
+
+
+PreParser::Expression PreParser::GetStringSymbol() {
+ const int kUseStrictLength = 10;
+ const char* kUseStrictChars = "use strict";
+ LogSymbol();
+ if (scanner_->is_literal_ascii() &&
+ scanner_->literal_length() == kUseStrictLength &&
+ !scanner_->literal_contains_escapes() &&
+ !strncmp(scanner_->literal_ascii_string().start(), kUseStrictChars,
+ kUseStrictLength)) {
+ return Expression::UseStrictStringLiteral();
+ }
+ return Expression::StringLiteral();
+}
+
+
+PreParser::Identifier PreParser::GetIdentifierSymbol() {
+ LogSymbol();
+ if (scanner_->current_token() == i::Token::FUTURE_RESERVED_WORD) {
+ return Identifier::FutureReserved();
+ } else if (scanner_->current_token() ==
+ i::Token::FUTURE_STRICT_RESERVED_WORD) {
+ return Identifier::FutureStrictReserved();
+ } else if (scanner_->current_token() == i::Token::YIELD) {
+ return Identifier::Yield();
+ }
+ if (scanner_->is_literal_ascii()) {
+ // Detect strict-mode poison words.
+ if (scanner_->literal_length() == 4 &&
+ !strncmp(scanner_->literal_ascii_string().start(), "eval", 4)) {
+ return Identifier::Eval();
+ }
+ if (scanner_->literal_length() == 9 &&
+ !strncmp(scanner_->literal_ascii_string().start(), "arguments", 9)) {
+ return Identifier::Arguments();
+ }
+ }
+ return Identifier::Default();
+}
+
+
+PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
+ i::Token::Value next = Next();
+ switch (next) {
+ case i::Token::FUTURE_RESERVED_WORD: {
+ i::Scanner::Location location = scanner_->location();
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ "reserved_word", NULL);
+ *ok = false;
+ return GetIdentifierSymbol();
+ }
+ case i::Token::YIELD:
+ if (scope_->is_generator()) {
+ // 'yield' in a generator is only valid as part of a YieldExpression.
+ ReportMessageAt(scanner_->location(), "unexpected_token", "yield");
+ *ok = false;
+ return Identifier::Yield();
+ }
+ // 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:
+ *ok = false;
+ return Identifier::Default();
+ }
+}
+
+
+void PreParser::SetStrictModeViolation(i::Scanner::Location location,
+ const char* type,
+ bool* ok) {
+ if (!is_classic_mode()) {
+ ReportMessageAt(location, type, NULL);
+ *ok = false;
+ return;
+ }
+ // Delay report in case this later turns out to be strict code
+ // (i.e., for function names and parameters prior to a "use strict"
+ // directive).
+ // It's safe to overwrite an existing violation.
+ // It's either from a function that turned out to be non-strict,
+ // or it's in the current function (and we just need to report
+ // one error), or it's in a unclosed nesting function that wasn't
+ // strict (otherwise we would already be in strict mode).
+ strict_mode_violation_location_ = location;
+ strict_mode_violation_type_ = type;
+}
+
+
+void PreParser::CheckDelayedStrictModeViolation(int beg_pos,
+ int end_pos,
+ bool* ok) {
+ i::Scanner::Location location = strict_mode_violation_location_;
+ if (location.IsValid() &&
+ location.beg_pos > beg_pos && location.end_pos < end_pos) {
+ ReportMessageAt(location, strict_mode_violation_type_, NULL);
+ *ok = false;
+ }
+}
+
+
+void PreParser::StrictModeIdentifierViolation(i::Scanner::Location location,
+ const char* eval_args_type,
+ Identifier identifier,
+ bool* ok) {
+ const char* type = eval_args_type;
+ if (identifier.IsFutureReserved()) {
+ type = "reserved_word";
+ } else if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
+ type = "strict_reserved_word";
+ }
+ if (!is_classic_mode()) {
+ ReportMessageAt(location, type, NULL);
+ *ok = false;
+ return;
+ }
+ strict_mode_violation_location_ = location;
+ strict_mode_violation_type_ = type;
+}
+
+
+PreParser::Identifier PreParser::ParseIdentifierName(bool* ok) {
+ i::Token::Value next = Next();
+ if (i::Token::IsKeyword(next)) {
+ int pos = scanner_->location().beg_pos;
+ const char* keyword = i::Token::String(next);
+ log_->LogAsciiSymbol(pos, i::Vector<const char>(keyword,
+ i::StrLength(keyword)));
+ return Identifier::Default();
+ }
+ if (next == i::Token::IDENTIFIER ||
+ next == i::Token::FUTURE_RESERVED_WORD ||
+ next == i::Token::FUTURE_STRICT_RESERVED_WORD) {
+ return GetIdentifierSymbol();
+ }
+ *ok = false;
+ return Identifier::Default();
+}
+
+#undef CHECK_OK
+
+
+// This function reads an identifier and determines whether or not it
+// is 'get' or 'set'.
+PreParser::Identifier PreParser::ParseIdentifierNameOrGetOrSet(bool* is_get,
+ bool* is_set,
+ bool* ok) {
+ Identifier result = ParseIdentifierName(ok);
+ if (!*ok) return Identifier::Default();
+ if (scanner_->is_literal_ascii() &&
+ scanner_->literal_length() == 3) {
+ const char* token = scanner_->literal_ascii_string().start();
+ *is_get = strncmp(token, "get", 3) == 0;
+ *is_set = !*is_get && strncmp(token, "set", 3) == 0;
+ }
+ return result;
+}
+
+
+bool PreParser::peek_any_identifier() {
+ i::Token::Value next = peek();
+ return next == i::Token::IDENTIFIER ||
+ next == i::Token::FUTURE_RESERVED_WORD ||
+ next == i::Token::FUTURE_STRICT_RESERVED_WORD ||
+ next == i::Token::YIELD;
+}
+
+
+int DuplicateFinder::AddAsciiSymbol(i::Vector<const char> key, int value) {
+ return AddSymbol(i::Vector<const byte>::cast(key), true, value);
+}
+
+
+int DuplicateFinder::AddUtf16Symbol(i::Vector<const uint16_t> key, int value) {
+ return AddSymbol(i::Vector<const byte>::cast(key), false, value);
+}
+
+int DuplicateFinder::AddSymbol(i::Vector<const byte> key,
+ bool is_ascii,
+ int value) {
+ uint32_t hash = Hash(key, is_ascii);
+ byte* encoding = BackupKey(key, is_ascii);
+ i::HashMap::Entry* entry = map_.Lookup(encoding, hash, true);
+ int old_value = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
+ entry->value =
+ reinterpret_cast<void*>(static_cast<intptr_t>(value | old_value));
+ return old_value;
+}
+
+
+int DuplicateFinder::AddNumber(i::Vector<const char> key, int value) {
+ ASSERT(key.length() > 0);
+ // Quick check for already being in canonical form.
+ if (IsNumberCanonical(key)) {
+ return AddAsciiSymbol(key, value);
+ }
+
+ int flags = i::ALLOW_HEX | i::ALLOW_OCTAL | i::ALLOW_IMPLICIT_OCTAL |
+ i::ALLOW_BINARY;
+ double double_value = StringToDouble(unicode_constants_, key, flags, 0.0);
+ int length;
+ const char* string;
+ if (!std::isfinite(double_value)) {
+ string = "Infinity";
+ length = 8; // strlen("Infinity");
+ } else {
+ string = DoubleToCString(double_value,
+ i::Vector<char>(number_buffer_, kBufferSize));
+ length = i::StrLength(string);
+ }
+ return AddSymbol(i::Vector<const byte>(reinterpret_cast<const byte*>(string),
+ length), true, value);
+}
+
+
+bool DuplicateFinder::IsNumberCanonical(i::Vector<const char> number) {
+ // Test for a safe approximation of number literals that are already
+ // in canonical form: max 15 digits, no leading zeroes, except an
+ // integer part that is a single zero, and no trailing zeros below
+ // the decimal point.
+ int pos = 0;
+ int length = number.length();
+ if (number.length() > 15) return false;
+ if (number[pos] == '0') {
+ pos++;
+ } else {
+ while (pos < length &&
+ static_cast<unsigned>(number[pos] - '0') <= ('9' - '0')) pos++;
+ }
+ if (length == pos) return true;
+ if (number[pos] != '.') return false;
+ pos++;
+ bool invalid_last_digit = true;
+ while (pos < length) {
+ byte digit = number[pos] - '0';
+ if (digit > '9' - '0') return false;
+ invalid_last_digit = (digit == 0);
+ pos++;
+ }
+ return !invalid_last_digit;
+}
+
+
+uint32_t DuplicateFinder::Hash(i::Vector<const byte> key, bool is_ascii) {
+ // Primitive hash function, almost identical to the one used
+ // for strings (except that it's seeded by the length and ASCII-ness).
+ int length = key.length();
+ uint32_t hash = (length << 1) | (is_ascii ? 1 : 0) ;
+ for (int i = 0; i < length; i++) {
+ uint32_t c = key[i];
+ hash = (hash + c) * 1025;
+ hash ^= (hash >> 6);
+ }
+ return hash;
+}
+
+
+bool DuplicateFinder::Match(void* first, void* second) {
+ // Decode lengths.
+ // Length + ASCII-bit is encoded as base 128, most significant heptet first,
+ // with a 8th bit being non-zero while there are more heptets.
+ // The value encodes the number of bytes following, and whether the original
+ // was ASCII.
+ byte* s1 = reinterpret_cast<byte*>(first);
+ byte* s2 = reinterpret_cast<byte*>(second);
+ uint32_t length_ascii_field = 0;
+ byte c1;
+ do {
+ c1 = *s1;
+ if (c1 != *s2) return false;
+ length_ascii_field = (length_ascii_field << 7) | (c1 & 0x7f);
+ s1++;
+ s2++;
+ } while ((c1 & 0x80) != 0);
+ int length = static_cast<int>(length_ascii_field >> 1);
+ return memcmp(s1, s2, length) == 0;
+}
+
+
+byte* DuplicateFinder::BackupKey(i::Vector<const byte> bytes,
+ bool is_ascii) {
+ uint32_t ascii_length = (bytes.length() << 1) | (is_ascii ? 1 : 0);
+ backing_store_.StartSequence();
+ // Emit ascii_length as base-128 encoded number, with the 7th bit set
+ // on the byte of every heptet except the last, least significant, one.
+ if (ascii_length >= (1 << 7)) {
+ if (ascii_length >= (1 << 14)) {
+ if (ascii_length >= (1 << 21)) {
+ if (ascii_length >= (1 << 28)) {
+ backing_store_.Add(static_cast<byte>((ascii_length >> 28) | 0x80));
+ }
+ backing_store_.Add(static_cast<byte>((ascii_length >> 21) | 0x80u));
+ }
+ backing_store_.Add(static_cast<byte>((ascii_length >> 14) | 0x80u));
+ }
+ backing_store_.Add(static_cast<byte>((ascii_length >> 7) | 0x80u));
+ }
+ backing_store_.Add(static_cast<byte>(ascii_length & 0x7f));
+
+ backing_store_.AddBlock(bytes);
+ return backing_store_.EndSequence().start();
+}
+} } // v8::preparser
diff --git a/chromium/v8/src/preparser.h b/chromium/v8/src/preparser.h
new file mode 100644
index 00000000000..faddecc5622
--- /dev/null
+++ b/chromium/v8/src/preparser.h
@@ -0,0 +1,697 @@
+// Copyright 2012 the V8 project authors. 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_PREPARSER_H
+#define V8_PREPARSER_H
+
+#include "hashmap.h"
+#include "token.h"
+#include "scanner.h"
+
+namespace v8 {
+
+namespace internal {
+class UnicodeCache;
+}
+
+namespace preparser {
+
+typedef uint8_t byte;
+
+// Preparsing checks a JavaScript program and emits preparse-data that helps
+// a later parsing to be faster.
+// See preparse-data-format.h for the data format.
+
+// The PreParser checks that the syntax follows the grammar for JavaScript,
+// and collects some information about the program along the way.
+// The grammar check is only performed in order to understand the program
+// sufficiently to deduce some information about it, that can be used
+// to speed up later parsing. Finding errors is not the goal of pre-parsing,
+// rather it is to speed up properly written and correct programs.
+// That means that contextual checks (like a label being declared where
+// it is used) are generally omitted.
+
+namespace i = v8::internal;
+
+class DuplicateFinder {
+ public:
+ explicit DuplicateFinder(i::UnicodeCache* constants)
+ : unicode_constants_(constants),
+ backing_store_(16),
+ map_(&Match) { }
+
+ int AddAsciiSymbol(i::Vector<const char> key, int value);
+ int AddUtf16Symbol(i::Vector<const uint16_t> key, int value);
+ // Add a a number literal by converting it (if necessary)
+ // to the string that ToString(ToNumber(literal)) would generate.
+ // and then adding that string with AddAsciiSymbol.
+ // This string is the actual value used as key in an object literal,
+ // and the one that must be different from the other keys.
+ int AddNumber(i::Vector<const char> key, int value);
+
+ private:
+ int AddSymbol(i::Vector<const byte> key, bool is_ascii, int value);
+ // Backs up the key and its length in the backing store.
+ // The backup is stored with a base 127 encoding of the
+ // length (plus a bit saying whether the string is ASCII),
+ // followed by the bytes of the key.
+ byte* BackupKey(i::Vector<const byte> key, bool is_ascii);
+
+ // Compare two encoded keys (both pointing into the backing store)
+ // for having the same base-127 encoded lengths and ASCII-ness,
+ // and then having the same 'length' bytes following.
+ static bool Match(void* first, void* second);
+ // Creates a hash from a sequence of bytes.
+ static uint32_t Hash(i::Vector<const byte> key, bool is_ascii);
+ // Checks whether a string containing a JS number is its canonical
+ // form.
+ static bool IsNumberCanonical(i::Vector<const char> key);
+
+ // Size of buffer. Sufficient for using it to call DoubleToCString in
+ // from conversions.h.
+ static const int kBufferSize = 100;
+
+ i::UnicodeCache* unicode_constants_;
+ // Backing store used to store strings used as hashmap keys.
+ i::SequenceCollector<unsigned char> backing_store_;
+ i::HashMap map_;
+ // Buffer used for string->number->canonical string conversions.
+ char number_buffer_[kBufferSize];
+};
+
+
+#ifdef WIN32
+#undef Yield
+#endif
+
+
+class PreParser {
+ public:
+ enum PreParseResult {
+ kPreParseStackOverflow,
+ kPreParseSuccess
+ };
+
+
+ PreParser(i::Scanner* scanner,
+ i::ParserRecorder* log,
+ uintptr_t stack_limit)
+ : 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_(false),
+ allow_natives_syntax_(false),
+ allow_generators_(false),
+ allow_for_of_(false),
+ parenthesized_function_(false) { }
+
+ ~PreParser() {}
+
+ bool allow_natives_syntax() const { return allow_natives_syntax_; }
+ bool allow_lazy() const { return allow_lazy_; }
+ bool allow_modules() const { return scanner_->HarmonyModules(); }
+ bool allow_harmony_scoping() const { return scanner_->HarmonyScoping(); }
+ bool allow_generators() const { return allow_generators_; }
+ bool allow_for_of() const { return allow_for_of_; }
+ bool allow_harmony_numeric_literals() const {
+ return scanner_->HarmonyNumericLiterals();
+ }
+
+ void set_allow_natives_syntax(bool allow) { allow_natives_syntax_ = allow; }
+ void set_allow_lazy(bool allow) { allow_lazy_ = allow; }
+ void set_allow_modules(bool allow) { scanner_->SetHarmonyModules(allow); }
+ void set_allow_harmony_scoping(bool allow) {
+ scanner_->SetHarmonyScoping(allow);
+ }
+ void set_allow_generators(bool allow) { allow_generators_ = allow; }
+ void set_allow_for_of(bool allow) { allow_for_of_ = allow; }
+ void set_allow_harmony_numeric_literals(bool allow) {
+ scanner_->SetHarmonyNumericLiterals(allow);
+ }
+
+ // 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.
+ PreParseResult PreParseProgram() {
+ Scope top_scope(&scope_, kTopLevelScope);
+ bool ok = true;
+ int start_position = scanner_->peek_location().beg_pos;
+ ParseSourceElements(i::Token::EOS, &ok);
+ if (stack_overflow_) return kPreParseStackOverflow;
+ if (!ok) {
+ ReportUnexpectedToken(scanner_->current_token());
+ } else if (!scope_->is_classic_mode()) {
+ CheckOctalLiteral(start_position, scanner_->location().end_pos, &ok);
+ }
+ return kPreParseSuccess;
+ }
+
+ // 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 function in enough
+ // detail that it can be lazily compiled.
+ // The scanner is expected to have matched the "function" or "function*"
+ // keyword and parameters, and have consumed the initial '{'.
+ // At return, unless an error occurred, the scanner is positioned before the
+ // the final '}'.
+ PreParseResult PreParseLazyFunction(i::LanguageMode mode,
+ bool is_generator,
+ i::ParserRecorder* log);
+
+ private:
+ // Used to detect duplicates in object literals. Each of the values
+ // kGetterProperty, kSetterProperty and kValueProperty represents
+ // a type of object literal property. When parsing a property, its
+ // type value is stored in the DuplicateFinder for the property name.
+ // Values are chosen so that having intersection bits means the there is
+ // an incompatibility.
+ // I.e., you can add a getter to a property that already has a setter, since
+ // kGetterProperty and kSetterProperty doesn't intersect, but not if it
+ // already has a getter or a value. Adding the getter to an existing
+ // setter will store the value (kGetterProperty | kSetterProperty), which
+ // is incompatible with adding any further properties.
+ enum PropertyType {
+ kNone = 0,
+ // Bit patterns representing different object literal property types.
+ kGetterProperty = 1,
+ kSetterProperty = 2,
+ kValueProperty = 7,
+ // Helper constants.
+ kValueFlag = 4
+ };
+
+ // Checks the type of conflict based on values coming from PropertyType.
+ bool HasConflict(int type1, int type2) { return (type1 & type2) != 0; }
+ bool IsDataDataConflict(int type1, int type2) {
+ return ((type1 & type2) & kValueFlag) != 0;
+ }
+ bool IsDataAccessorConflict(int type1, int type2) {
+ return ((type1 ^ type2) & kValueFlag) != 0;
+ }
+ bool IsAccessorAccessorConflict(int type1, int type2) {
+ return ((type1 | type2) & kValueFlag) == 0;
+ }
+
+
+ void CheckDuplicate(DuplicateFinder* finder,
+ i::Token::Value property,
+ int type,
+ bool* ok);
+
+ // These types form an algebra over syntactic categories that is just
+ // rich enough to let us recognize and propagate the constructs that
+ // are either being counted in the preparser data, or is important
+ // to throw the correct syntax error exceptions.
+
+ enum ScopeType {
+ kTopLevelScope,
+ kFunctionScope
+ };
+
+ enum VariableDeclarationContext {
+ kSourceElement,
+ kStatement,
+ kForStatement
+ };
+
+ // If a list of variable declarations includes any initializers.
+ enum VariableDeclarationProperties {
+ kHasInitializers,
+ kHasNoInitializers
+ };
+
+ class Expression;
+
+ class Identifier {
+ public:
+ static Identifier Default() {
+ return Identifier(kUnknownIdentifier);
+ }
+ static Identifier Eval() {
+ return Identifier(kEvalIdentifier);
+ }
+ static Identifier Arguments() {
+ return Identifier(kArgumentsIdentifier);
+ }
+ static Identifier FutureReserved() {
+ return Identifier(kFutureReservedIdentifier);
+ }
+ static Identifier FutureStrictReserved() {
+ return Identifier(kFutureStrictReservedIdentifier);
+ }
+ static Identifier Yield() {
+ return Identifier(kYieldIdentifier);
+ }
+ bool IsEval() { return type_ == kEvalIdentifier; }
+ bool IsArguments() { return type_ == kArgumentsIdentifier; }
+ bool IsEvalOrArguments() { return type_ >= kEvalIdentifier; }
+ bool IsYield() { return type_ == kYieldIdentifier; }
+ bool IsFutureReserved() { return type_ == kFutureReservedIdentifier; }
+ bool IsFutureStrictReserved() {
+ return type_ == kFutureStrictReservedIdentifier;
+ }
+ bool IsValidStrictVariable() { return type_ == kUnknownIdentifier; }
+
+ private:
+ enum Type {
+ kUnknownIdentifier,
+ kFutureReservedIdentifier,
+ kFutureStrictReservedIdentifier,
+ kYieldIdentifier,
+ kEvalIdentifier,
+ kArgumentsIdentifier
+ };
+ explicit Identifier(Type type) : type_(type) { }
+ Type type_;
+
+ friend class Expression;
+ };
+
+ // Bits 0 and 1 are used to identify the type of expression:
+ // If bit 0 is set, it's an identifier.
+ // if bit 1 is set, it's a string literal.
+ // If neither is set, it's no particular type, and both set isn't
+ // use yet.
+ // Bit 2 is used to mark the expression as being parenthesized,
+ // so "(foo)" isn't recognized as a pure identifier (and possible label).
+ class Expression {
+ public:
+ static Expression Default() {
+ return Expression(kUnknownExpression);
+ }
+
+ static Expression FromIdentifier(Identifier id) {
+ return Expression(kIdentifierFlag | (id.type_ << kIdentifierShift));
+ }
+
+ static Expression StringLiteral() {
+ return Expression(kUnknownStringLiteral);
+ }
+
+ static Expression UseStrictStringLiteral() {
+ return Expression(kUseStrictString);
+ }
+
+ static Expression This() {
+ return Expression(kThisExpression);
+ }
+
+ static Expression ThisProperty() {
+ return Expression(kThisPropertyExpression);
+ }
+
+ static Expression StrictFunction() {
+ return Expression(kStrictFunctionExpression);
+ }
+
+ bool IsIdentifier() {
+ return (code_ & kIdentifierFlag) != 0;
+ }
+
+ // Only works corretly if it is actually an identifier expression.
+ PreParser::Identifier AsIdentifier() {
+ return PreParser::Identifier(
+ static_cast<PreParser::Identifier::Type>(code_ >> kIdentifierShift));
+ }
+
+ bool IsParenthesized() {
+ // If bit 0 or 1 is set, we interpret bit 2 as meaning parenthesized.
+ return (code_ & 7) > 4;
+ }
+
+ bool IsRawIdentifier() {
+ return !IsParenthesized() && IsIdentifier();
+ }
+
+ bool IsStringLiteral() { return (code_ & kStringLiteralFlag) != 0; }
+
+ bool IsRawStringLiteral() {
+ return !IsParenthesized() && IsStringLiteral();
+ }
+
+ bool IsUseStrictLiteral() {
+ return (code_ & kStringLiteralMask) == kUseStrictString;
+ }
+
+ bool IsThis() {
+ return code_ == kThisExpression;
+ }
+
+ bool IsThisProperty() {
+ return code_ == kThisPropertyExpression;
+ }
+
+ bool IsStrictFunction() {
+ return code_ == kStrictFunctionExpression;
+ }
+
+ Expression Parenthesize() {
+ int type = code_ & 3;
+ if (type != 0) {
+ // Identifiers and string literals can be parenthesized.
+ // They no longer work as labels or directive prologues,
+ // but are still recognized in other contexts.
+ return Expression(code_ | kParenthesizedExpressionFlag);
+ }
+ // For other types of expressions, it's not important to remember
+ // the parentheses.
+ return *this;
+ }
+
+ private:
+ // First two/three bits are used as flags.
+ // Bit 0 and 1 represent identifiers or strings literals, and are
+ // mutually exclusive, but can both be absent.
+ // If bit 0 or 1 are set, bit 2 marks that the expression has
+ // been wrapped in parentheses (a string literal can no longer
+ // be a directive prologue, and an identifier can no longer be
+ // a label.
+ enum {
+ kUnknownExpression = 0,
+ // Identifiers
+ kIdentifierFlag = 1, // Used to detect labels.
+ kIdentifierShift = 3,
+
+ kStringLiteralFlag = 2, // Used to detect directive prologue.
+ kUnknownStringLiteral = kStringLiteralFlag,
+ kUseStrictString = kStringLiteralFlag | 8,
+ kStringLiteralMask = kUseStrictString,
+
+ // Only if identifier or string literal.
+ kParenthesizedExpressionFlag = 4,
+
+ // Below here applies if neither identifier nor string literal.
+ kThisExpression = 4,
+ kThisPropertyExpression = 8,
+ kStrictFunctionExpression = 12
+ };
+
+ explicit Expression(int expression_code) : code_(expression_code) { }
+
+ int code_;
+ };
+
+ class Statement {
+ public:
+ static Statement Default() {
+ return Statement(kUnknownStatement);
+ }
+
+ static Statement FunctionDeclaration() {
+ return Statement(kFunctionDeclaration);
+ }
+
+ // Creates expression statement from expression.
+ // Preserves being an unparenthesized string literal, possibly
+ // "use strict".
+ static Statement ExpressionStatement(Expression expression) {
+ if (!expression.IsParenthesized()) {
+ if (expression.IsUseStrictLiteral()) {
+ return Statement(kUseStrictExpressionStatement);
+ }
+ if (expression.IsStringLiteral()) {
+ return Statement(kStringLiteralExpressionStatement);
+ }
+ }
+ return Default();
+ }
+
+ bool IsStringLiteral() {
+ return code_ != kUnknownStatement;
+ }
+
+ bool IsUseStrictLiteral() {
+ return code_ == kUseStrictExpressionStatement;
+ }
+
+ bool IsFunctionDeclaration() {
+ return code_ == kFunctionDeclaration;
+ }
+
+ private:
+ enum Type {
+ kUnknownStatement,
+ kStringLiteralExpressionStatement,
+ kUseStrictExpressionStatement,
+ kFunctionDeclaration
+ };
+
+ explicit Statement(Type code) : code_(code) {}
+ Type code_;
+ };
+
+ enum SourceElements {
+ kUnknownSourceElements
+ };
+
+ typedef int Arguments;
+
+ class Scope {
+ public:
+ Scope(Scope** variable, ScopeType type)
+ : variable_(variable),
+ prev_(*variable),
+ type_(type),
+ materialized_literal_count_(0),
+ expected_properties_(0),
+ with_nesting_count_(0),
+ language_mode_(
+ (prev_ != NULL) ? prev_->language_mode() : i::CLASSIC_MODE),
+ is_generator_(false) {
+ *variable = this;
+ }
+ ~Scope() { *variable_ = prev_; }
+ void NextMaterializedLiteralIndex() { materialized_literal_count_++; }
+ void AddProperty() { expected_properties_++; }
+ ScopeType type() { return type_; }
+ int expected_properties() { return expected_properties_; }
+ int materialized_literal_count() { return materialized_literal_count_; }
+ bool IsInsideWith() { return with_nesting_count_ != 0; }
+ bool is_generator() { return is_generator_; }
+ void set_is_generator(bool is_generator) { is_generator_ = is_generator; }
+ 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;
+ }
+
+ class InsideWith {
+ public:
+ explicit InsideWith(Scope* scope) : scope_(scope) {
+ scope->with_nesting_count_++;
+ }
+
+ ~InsideWith() { scope_->with_nesting_count_--; }
+
+ private:
+ Scope* scope_;
+ DISALLOW_COPY_AND_ASSIGN(InsideWith);
+ };
+
+ private:
+ Scope** const variable_;
+ Scope* const prev_;
+ const ScopeType type_;
+ int materialized_literal_count_;
+ int expected_properties_;
+ int with_nesting_count_;
+ i::LanguageMode language_mode_;
+ bool is_generator_;
+ };
+
+ // Report syntax error
+ void ReportUnexpectedToken(i::Token::Value token);
+ void ReportMessageAt(i::Scanner::Location location,
+ const char* type,
+ const char* name_opt) {
+ log_->LogMessage(location.beg_pos, location.end_pos, type, name_opt);
+ }
+ void ReportMessageAt(int start_pos,
+ int end_pos,
+ const char* type,
+ const char* name_opt) {
+ log_->LogMessage(start_pos, end_pos, type, name_opt);
+ }
+
+ void CheckOctalLiteral(int beg_pos, int end_pos, bool* ok);
+
+ // All ParseXXX functions take as the last argument an *ok parameter
+ // which is set to false if parsing failed; it is unchanged otherwise.
+ // By making the 'exception handling' explicit, we are forced to check
+ // for failure at the call sites.
+ Statement ParseSourceElement(bool* ok);
+ SourceElements ParseSourceElements(int end_token, bool* ok);
+ Statement ParseStatement(bool* ok);
+ Statement ParseFunctionDeclaration(bool* ok);
+ Statement ParseBlock(bool* ok);
+ Statement ParseVariableStatement(VariableDeclarationContext var_context,
+ bool* ok);
+ Statement ParseVariableDeclarations(VariableDeclarationContext var_context,
+ VariableDeclarationProperties* decl_props,
+ int* num_decl,
+ bool* ok);
+ Statement ParseExpressionOrLabelledStatement(bool* ok);
+ Statement ParseIfStatement(bool* ok);
+ Statement ParseContinueStatement(bool* ok);
+ Statement ParseBreakStatement(bool* ok);
+ Statement ParseReturnStatement(bool* ok);
+ Statement ParseWithStatement(bool* ok);
+ Statement ParseSwitchStatement(bool* ok);
+ Statement ParseDoWhileStatement(bool* ok);
+ Statement ParseWhileStatement(bool* ok);
+ Statement ParseForStatement(bool* ok);
+ Statement ParseThrowStatement(bool* ok);
+ Statement ParseTryStatement(bool* ok);
+ Statement ParseDebuggerStatement(bool* ok);
+
+ Expression ParseExpression(bool accept_IN, bool* ok);
+ Expression ParseAssignmentExpression(bool accept_IN, bool* ok);
+ Expression ParseYieldExpression(bool* ok);
+ Expression ParseConditionalExpression(bool accept_IN, bool* ok);
+ Expression ParseBinaryExpression(int prec, bool accept_IN, bool* ok);
+ Expression ParseUnaryExpression(bool* ok);
+ Expression ParsePostfixExpression(bool* ok);
+ Expression ParseLeftHandSideExpression(bool* ok);
+ Expression ParseNewExpression(bool* ok);
+ Expression ParseMemberExpression(bool* ok);
+ Expression ParseMemberWithNewPrefixesExpression(unsigned new_count, bool* ok);
+ Expression ParsePrimaryExpression(bool* ok);
+ Expression ParseArrayLiteral(bool* ok);
+ Expression ParseObjectLiteral(bool* ok);
+ Expression ParseRegExpLiteral(bool seen_equal, bool* ok);
+ Expression ParseV8Intrinsic(bool* ok);
+
+ Arguments ParseArguments(bool* ok);
+ Expression ParseFunctionLiteral(bool is_generator, bool* ok);
+ void ParseLazyFunctionLiteralBody(bool* ok);
+
+ Identifier ParseIdentifier(bool* ok);
+ Identifier ParseIdentifierName(bool* ok);
+ Identifier ParseIdentifierNameOrGetOrSet(bool* is_get,
+ bool* is_set,
+ bool* ok);
+
+ // Logs the currently parsed literal as a symbol in the preparser data.
+ void LogSymbol();
+ // Log the currently parsed identifier.
+ Identifier GetIdentifierSymbol();
+ // Log the currently parsed string literal.
+ Expression GetStringSymbol();
+
+ i::Token::Value peek() {
+ if (stack_overflow_) return i::Token::ILLEGAL;
+ return scanner_->peek();
+ }
+
+ i::Token::Value Next() {
+ if (stack_overflow_) return i::Token::ILLEGAL;
+ {
+ int marker;
+ if (reinterpret_cast<uintptr_t>(&marker) < stack_limit_) {
+ // Further calls to peek/Next will return illegal token.
+ // The current one will still be returned. It might already
+ // have been seen using peek.
+ stack_overflow_ = true;
+ }
+ }
+ return scanner_->Next();
+ }
+
+ bool peek_any_identifier();
+
+ 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;
+ }
+
+ i::LanguageMode language_mode() { return scope_->language_mode(); }
+
+ void Consume(i::Token::Value token) { Next(); }
+
+ void Expect(i::Token::Value token, bool* ok) {
+ if (Next() != token) {
+ *ok = false;
+ }
+ }
+
+ bool Check(i::Token::Value token) {
+ i::Token::Value next = peek();
+ if (next == token) {
+ Consume(next);
+ return true;
+ }
+ return false;
+ }
+ void ExpectSemicolon(bool* ok);
+
+ bool CheckInOrOf(bool accept_OF);
+
+ static int Precedence(i::Token::Value tok, bool accept_IN);
+
+ void SetStrictModeViolation(i::Scanner::Location,
+ const char* type,
+ bool* ok);
+
+ void CheckDelayedStrictModeViolation(int beg_pos, int end_pos, bool* ok);
+
+ void StrictModeIdentifierViolation(i::Scanner::Location,
+ const char* eval_args_type,
+ Identifier identifier,
+ bool* ok);
+
+ i::Scanner* scanner_;
+ i::ParserRecorder* log_;
+ Scope* scope_;
+ uintptr_t stack_limit_;
+ i::Scanner::Location strict_mode_violation_location_;
+ const char* strict_mode_violation_type_;
+ bool stack_overflow_;
+ bool allow_lazy_;
+ bool allow_natives_syntax_;
+ bool allow_generators_;
+ bool allow_for_of_;
+ bool parenthesized_function_;
+};
+} } // v8::preparser
+
+#endif // V8_PREPARSER_H
diff --git a/chromium/v8/src/prettyprinter.cc b/chromium/v8/src/prettyprinter.cc
new file mode 100644
index 00000000000..1824efa7f59
--- /dev/null
+++ b/chromium/v8/src/prettyprinter.cc
@@ -0,0 +1,1169 @@
+// Copyright 2012 the V8 project authors. 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 <stdarg.h>
+
+#include "v8.h"
+
+#include "prettyprinter.h"
+#include "scopes.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef DEBUG
+
+PrettyPrinter::PrettyPrinter() {
+ output_ = NULL;
+ size_ = 0;
+ pos_ = 0;
+ InitializeAstVisitor();
+}
+
+
+PrettyPrinter::~PrettyPrinter() {
+ DeleteArray(output_);
+}
+
+
+void PrettyPrinter::VisitBlock(Block* node) {
+ if (!node->is_initializer_block()) Print("{ ");
+ PrintStatements(node->statements());
+ if (node->statements()->length() > 0) Print(" ");
+ if (!node->is_initializer_block()) Print("}");
+}
+
+
+void PrettyPrinter::VisitVariableDeclaration(VariableDeclaration* node) {
+ Print("var ");
+ PrintLiteral(node->proxy()->name(), false);
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitFunctionDeclaration(FunctionDeclaration* node) {
+ Print("function ");
+ PrintLiteral(node->proxy()->name(), false);
+ Print(" = ");
+ PrintFunctionLiteral(node->fun());
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitModuleDeclaration(ModuleDeclaration* node) {
+ Print("module ");
+ PrintLiteral(node->proxy()->name(), false);
+ Print(" = ");
+ Visit(node->module());
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitImportDeclaration(ImportDeclaration* node) {
+ Print("import ");
+ PrintLiteral(node->proxy()->name(), false);
+ Print(" from ");
+ Visit(node->module());
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitExportDeclaration(ExportDeclaration* node) {
+ Print("export ");
+ PrintLiteral(node->proxy()->name(), false);
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitModuleLiteral(ModuleLiteral* node) {
+ VisitBlock(node->body());
+}
+
+
+void PrettyPrinter::VisitModuleVariable(ModuleVariable* node) {
+ Visit(node->proxy());
+}
+
+
+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::VisitModuleStatement(ModuleStatement* node) {
+ Print("module ");
+ PrintLiteral(node->proxy()->name(), false);
+ Print(" ");
+ Visit(node->body());
+}
+
+
+void PrettyPrinter::VisitExpressionStatement(ExpressionStatement* node) {
+ Visit(node->expression());
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitEmptyStatement(EmptyStatement* node) {
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitIfStatement(IfStatement* node) {
+ Print("if (");
+ Visit(node->condition());
+ Print(") ");
+ Visit(node->then_statement());
+ if (node->HasElseStatement()) {
+ Print(" else ");
+ Visit(node->else_statement());
+ }
+}
+
+
+void PrettyPrinter::VisitContinueStatement(ContinueStatement* node) {
+ Print("continue");
+ ZoneStringList* labels = node->target()->labels();
+ if (labels != NULL) {
+ Print(" ");
+ ASSERT(labels->length() > 0); // guaranteed to have at least one entry
+ PrintLiteral(labels->at(0), false); // any label from the list is fine
+ }
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitBreakStatement(BreakStatement* node) {
+ Print("break");
+ ZoneStringList* labels = node->target()->labels();
+ if (labels != NULL) {
+ Print(" ");
+ ASSERT(labels->length() > 0); // guaranteed to have at least one entry
+ PrintLiteral(labels->at(0), false); // any label from the list is fine
+ }
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitReturnStatement(ReturnStatement* node) {
+ Print("return ");
+ Visit(node->expression());
+ Print(";");
+}
+
+
+void PrettyPrinter::VisitWithStatement(WithStatement* node) {
+ Print("with (");
+ Visit(node->expression());
+ Print(") ");
+ Visit(node->statement());
+}
+
+
+void PrettyPrinter::VisitSwitchStatement(SwitchStatement* node) {
+ PrintLabels(node->labels());
+ Print("switch (");
+ Visit(node->tag());
+ Print(") { ");
+ ZoneList<CaseClause*>* cases = node->cases();
+ for (int i = 0; i < cases->length(); i++)
+ PrintCaseClause(cases->at(i));
+ Print("}");
+}
+
+
+void PrettyPrinter::VisitDoWhileStatement(DoWhileStatement* node) {
+ PrintLabels(node->labels());
+ Print("do ");
+ Visit(node->body());
+ Print(" while (");
+ Visit(node->cond());
+ Print(");");
+}
+
+
+void PrettyPrinter::VisitWhileStatement(WhileStatement* node) {
+ PrintLabels(node->labels());
+ Print("while (");
+ Visit(node->cond());
+ Print(") ");
+ Visit(node->body());
+}
+
+
+void PrettyPrinter::VisitForStatement(ForStatement* node) {
+ PrintLabels(node->labels());
+ Print("for (");
+ if (node->init() != NULL) {
+ Visit(node->init());
+ Print(" ");
+ } else {
+ Print("; ");
+ }
+ if (node->cond() != NULL) Visit(node->cond());
+ Print("; ");
+ if (node->next() != NULL) {
+ Visit(node->next()); // prints extra ';', unfortunately
+ // to fix: should use Expression for next
+ }
+ Print(") ");
+ Visit(node->body());
+}
+
+
+void PrettyPrinter::VisitForInStatement(ForInStatement* node) {
+ PrintLabels(node->labels());
+ Print("for (");
+ Visit(node->each());
+ Print(" in ");
+ Visit(node->enumerable());
+ Print(") ");
+ Visit(node->body());
+}
+
+
+void PrettyPrinter::VisitForOfStatement(ForOfStatement* node) {
+ PrintLabels(node->labels());
+ Print("for (");
+ Visit(node->each());
+ Print(" of ");
+ Visit(node->iterable());
+ Print(") ");
+ Visit(node->body());
+}
+
+
+void PrettyPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
+ Print("try ");
+ Visit(node->try_block());
+ Print(" catch (");
+ const bool quote = false;
+ PrintLiteral(node->variable()->name(), quote);
+ Print(") ");
+ Visit(node->catch_block());
+}
+
+
+void PrettyPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) {
+ Print("try ");
+ Visit(node->try_block());
+ Print(" finally ");
+ Visit(node->finally_block());
+}
+
+
+void PrettyPrinter::VisitDebuggerStatement(DebuggerStatement* node) {
+ Print("debugger ");
+}
+
+
+void PrettyPrinter::VisitFunctionLiteral(FunctionLiteral* node) {
+ Print("(");
+ PrintFunctionLiteral(node);
+ Print(")");
+}
+
+
+void PrettyPrinter::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* node) {
+ Print("(");
+ PrintLiteral(node->shared_function_info(), true);
+ Print(")");
+}
+
+
+void PrettyPrinter::VisitConditional(Conditional* node) {
+ Visit(node->condition());
+ Print(" ? ");
+ Visit(node->then_expression());
+ Print(" : ");
+ Visit(node->else_expression());
+}
+
+
+void PrettyPrinter::VisitLiteral(Literal* node) {
+ PrintLiteral(node->value(), true);
+}
+
+
+void PrettyPrinter::VisitRegExpLiteral(RegExpLiteral* node) {
+ Print(" RegExp(");
+ PrintLiteral(node->pattern(), false);
+ Print(",");
+ PrintLiteral(node->flags(), false);
+ Print(") ");
+}
+
+
+void PrettyPrinter::VisitObjectLiteral(ObjectLiteral* node) {
+ Print("{ ");
+ for (int i = 0; i < node->properties()->length(); i++) {
+ if (i != 0) Print(",");
+ ObjectLiteral::Property* property = node->properties()->at(i);
+ Print(" ");
+ Visit(property->key());
+ Print(": ");
+ Visit(property->value());
+ }
+ Print(" }");
+}
+
+
+void PrettyPrinter::VisitArrayLiteral(ArrayLiteral* node) {
+ Print("[ ");
+ for (int i = 0; i < node->values()->length(); i++) {
+ if (i != 0) Print(",");
+ Visit(node->values()->at(i));
+ }
+ Print(" ]");
+}
+
+
+void PrettyPrinter::VisitVariableProxy(VariableProxy* node) {
+ PrintLiteral(node->name(), false);
+}
+
+
+void PrettyPrinter::VisitAssignment(Assignment* node) {
+ Visit(node->target());
+ Print(" %s ", Token::String(node->op()));
+ Visit(node->value());
+}
+
+
+void PrettyPrinter::VisitYield(Yield* node) {
+ Print("yield ");
+ Visit(node->expression());
+}
+
+
+void PrettyPrinter::VisitThrow(Throw* node) {
+ Print("throw ");
+ Visit(node->exception());
+}
+
+
+void PrettyPrinter::VisitProperty(Property* node) {
+ Expression* key = node->key();
+ Literal* literal = key->AsLiteral();
+ if (literal != NULL && literal->value()->IsInternalizedString()) {
+ Print("(");
+ Visit(node->obj());
+ Print(").");
+ PrintLiteral(literal->value(), false);
+ } else {
+ Visit(node->obj());
+ Print("[");
+ Visit(key);
+ Print("]");
+ }
+}
+
+
+void PrettyPrinter::VisitCall(Call* node) {
+ Visit(node->expression());
+ PrintArguments(node->arguments());
+}
+
+
+void PrettyPrinter::VisitCallNew(CallNew* node) {
+ Print("new (");
+ Visit(node->expression());
+ Print(")");
+ PrintArguments(node->arguments());
+}
+
+
+void PrettyPrinter::VisitCallRuntime(CallRuntime* node) {
+ Print("%%");
+ PrintLiteral(node->name(), false);
+ PrintArguments(node->arguments());
+}
+
+
+void PrettyPrinter::VisitUnaryOperation(UnaryOperation* node) {
+ Token::Value op = node->op();
+ bool needsSpace =
+ op == Token::DELETE || op == Token::TYPEOF || op == Token::VOID;
+ Print("(%s%s", Token::String(op), needsSpace ? " " : "");
+ Visit(node->expression());
+ Print(")");
+}
+
+
+void PrettyPrinter::VisitCountOperation(CountOperation* node) {
+ Print("(");
+ if (node->is_prefix()) Print("%s", Token::String(node->op()));
+ Visit(node->expression());
+ if (node->is_postfix()) Print("%s", Token::String(node->op()));
+ Print(")");
+}
+
+
+void PrettyPrinter::VisitBinaryOperation(BinaryOperation* node) {
+ Print("(");
+ Visit(node->left());
+ Print(" %s ", Token::String(node->op()));
+ Visit(node->right());
+ Print(")");
+}
+
+
+void PrettyPrinter::VisitCompareOperation(CompareOperation* node) {
+ Print("(");
+ Visit(node->left());
+ Print(" %s ", Token::String(node->op()));
+ Visit(node->right());
+ Print(")");
+}
+
+
+void PrettyPrinter::VisitThisFunction(ThisFunction* node) {
+ Print("<this-function>");
+}
+
+
+const char* PrettyPrinter::Print(AstNode* node) {
+ Init();
+ Visit(node);
+ return output_;
+}
+
+
+const char* PrettyPrinter::PrintExpression(FunctionLiteral* program) {
+ Init();
+ ExpressionStatement* statement =
+ program->body()->at(0)->AsExpressionStatement();
+ Visit(statement->expression());
+ return output_;
+}
+
+
+const char* PrettyPrinter::PrintProgram(FunctionLiteral* program) {
+ Init();
+ PrintStatements(program->body());
+ Print("\n");
+ return output_;
+}
+
+
+void PrettyPrinter::PrintOut(AstNode* node) {
+ PrettyPrinter printer;
+ PrintF("%s", printer.Print(node));
+}
+
+
+void PrettyPrinter::Init() {
+ if (size_ == 0) {
+ ASSERT(output_ == NULL);
+ const int initial_size = 256;
+ output_ = NewArray<char>(initial_size);
+ size_ = initial_size;
+ }
+ output_[0] = '\0';
+ pos_ = 0;
+}
+
+
+void PrettyPrinter::Print(const char* format, ...) {
+ for (;;) {
+ va_list arguments;
+ va_start(arguments, format);
+ int n = OS::VSNPrintF(Vector<char>(output_, size_) + pos_,
+ format,
+ arguments);
+ va_end(arguments);
+
+ if (n >= 0) {
+ // there was enough space - we are done
+ pos_ += n;
+ return;
+ } else {
+ // there was not enough space - allocate more and try again
+ const int slack = 32;
+ int new_size = size_ + (size_ >> 1) + slack;
+ char* new_output = NewArray<char>(new_size);
+ OS::MemCopy(new_output, output_, pos_);
+ DeleteArray(output_);
+ output_ = new_output;
+ size_ = new_size;
+ }
+ }
+}
+
+
+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));
+ }
+}
+
+
+void PrettyPrinter::PrintLabels(ZoneStringList* labels) {
+ if (labels != NULL) {
+ for (int i = 0; i < labels->length(); i++) {
+ PrintLiteral(labels->at(i), false);
+ Print(": ");
+ }
+ }
+}
+
+
+void PrettyPrinter::PrintArguments(ZoneList<Expression*>* arguments) {
+ Print("(");
+ for (int i = 0; i < arguments->length(); i++) {
+ if (i != 0) Print(", ");
+ Visit(arguments->at(i));
+ }
+ Print(")");
+}
+
+
+void PrettyPrinter::PrintLiteral(Handle<Object> value, bool quote) {
+ Object* object = *value;
+ if (object->IsString()) {
+ String* string = String::cast(object);
+ if (quote) Print("\"");
+ for (int i = 0; i < string->length(); i++) {
+ Print("%c", string->Get(i));
+ }
+ if (quote) Print("\"");
+ } else if (object->IsNull()) {
+ Print("null");
+ } else if (object->IsTrue()) {
+ Print("true");
+ } else if (object->IsFalse()) {
+ Print("false");
+ } else if (object->IsUndefined()) {
+ Print("undefined");
+ } else if (object->IsNumber()) {
+ Print("%g", object->Number());
+ } else if (object->IsJSObject()) {
+ // regular expression
+ if (object->IsJSFunction()) {
+ Print("JS-Function");
+ } else if (object->IsJSArray()) {
+ Print("JS-array[%u]", JSArray::cast(object)->length());
+ } else if (object->IsJSObject()) {
+ Print("JS-Object");
+ } else {
+ Print("?UNKNOWN?");
+ }
+ } else if (object->IsFixedArray()) {
+ Print("FixedArray");
+ } else {
+ Print("<unknown literal %p>", object);
+ }
+}
+
+
+void PrettyPrinter::PrintParameters(Scope* scope) {
+ Print("(");
+ for (int i = 0; i < scope->num_parameters(); i++) {
+ if (i > 0) Print(", ");
+ PrintLiteral(scope->parameter(i)->name(), false);
+ }
+ Print(")");
+}
+
+
+void PrettyPrinter::PrintDeclarations(ZoneList<Declaration*>* declarations) {
+ for (int i = 0; i < declarations->length(); i++) {
+ if (i > 0) Print(" ");
+ Visit(declarations->at(i));
+ }
+}
+
+
+void PrettyPrinter::PrintFunctionLiteral(FunctionLiteral* function) {
+ Print("function ");
+ PrintLiteral(function->name(), false);
+ PrintParameters(function->scope());
+ Print(" { ");
+ PrintDeclarations(function->scope()->declarations());
+ PrintStatements(function->body());
+ Print(" }");
+}
+
+
+void PrettyPrinter::PrintCaseClause(CaseClause* clause) {
+ if (clause->is_default()) {
+ Print("default");
+ } else {
+ Print("case ");
+ Visit(clause->label());
+ }
+ Print(": ");
+ PrintStatements(clause->statements());
+ if (clause->statements()->length() > 0)
+ Print(" ");
+}
+
+
+//-----------------------------------------------------------------------------
+
+class IndentedScope BASE_EMBEDDED {
+ public:
+ IndentedScope(AstPrinter* printer, const char* txt)
+ : ast_printer_(printer) {
+ ast_printer_->PrintIndented(txt);
+ ast_printer_->Print("\n");
+ ast_printer_->inc_indent();
+ }
+
+ virtual ~IndentedScope() {
+ ast_printer_->dec_indent();
+ }
+
+ private:
+ AstPrinter* ast_printer_;
+};
+
+
+//-----------------------------------------------------------------------------
+
+
+AstPrinter::AstPrinter() : indent_(0) {
+}
+
+
+AstPrinter::~AstPrinter() {
+ ASSERT(indent_ == 0);
+}
+
+
+void AstPrinter::PrintIndented(const char* txt) {
+ for (int i = 0; i < indent_; i++) {
+ Print(". ");
+ }
+ Print(txt);
+}
+
+
+void AstPrinter::PrintLiteralIndented(const char* info,
+ Handle<Object> value,
+ bool quote) {
+ PrintIndented(info);
+ Print(" ");
+ PrintLiteral(value, quote);
+ Print("\n");
+}
+
+
+void AstPrinter::PrintLiteralWithModeIndented(const char* info,
+ Variable* var,
+ Handle<Object> value) {
+ if (var == NULL) {
+ PrintLiteralIndented(info, value, true);
+ } else {
+ EmbeddedVector<char, 256> buf;
+ int pos = OS::SNPrintF(buf, "%s (mode = %s", info,
+ Variable::Mode2String(var->mode()));
+ OS::SNPrintF(buf + pos, ")");
+ PrintLiteralIndented(buf.start(), value, true);
+ }
+}
+
+
+void AstPrinter::PrintLabelsIndented(ZoneStringList* labels) {
+ if (labels == NULL || labels->length() == 0) return;
+ PrintIndented("LABELS ");
+ PrintLabels(labels);
+ Print("\n");
+}
+
+
+void AstPrinter::PrintIndentedVisit(const char* s, AstNode* node) {
+ IndentedScope indent(this, s);
+ Visit(node);
+}
+
+
+const char* AstPrinter::PrintProgram(FunctionLiteral* program) {
+ Init();
+ { IndentedScope indent(this, "FUNC");
+ PrintLiteralIndented("NAME", program->name(), true);
+ PrintLiteralIndented("INFERRED NAME", program->inferred_name(), true);
+ PrintParameters(program->scope());
+ PrintDeclarations(program->scope()->declarations());
+ PrintStatements(program->body());
+ }
+ return Output();
+}
+
+
+void AstPrinter::PrintDeclarations(ZoneList<Declaration*>* declarations) {
+ if (declarations->length() > 0) {
+ IndentedScope indent(this, "DECLS");
+ for (int i = 0; i < declarations->length(); i++) {
+ Visit(declarations->at(i));
+ }
+ }
+}
+
+
+void AstPrinter::PrintParameters(Scope* scope) {
+ if (scope->num_parameters() > 0) {
+ IndentedScope indent(this, "PARAMS");
+ for (int i = 0; i < scope->num_parameters(); i++) {
+ PrintLiteralWithModeIndented("VAR", scope->parameter(i),
+ scope->parameter(i)->name());
+ }
+ }
+}
+
+
+void AstPrinter::PrintStatements(ZoneList<Statement*>* statements) {
+ for (int i = 0; i < statements->length(); i++) {
+ Visit(statements->at(i));
+ }
+}
+
+
+void AstPrinter::PrintArguments(ZoneList<Expression*>* arguments) {
+ for (int i = 0; i < arguments->length(); i++) {
+ Visit(arguments->at(i));
+ }
+}
+
+
+void AstPrinter::PrintCaseClause(CaseClause* clause) {
+ if (clause->is_default()) {
+ IndentedScope indent(this, "DEFAULT");
+ PrintStatements(clause->statements());
+ } else {
+ IndentedScope indent(this, "CASE");
+ Visit(clause->label());
+ PrintStatements(clause->statements());
+ }
+}
+
+
+void AstPrinter::VisitBlock(Block* node) {
+ const char* block_txt = node->is_initializer_block() ? "BLOCK INIT" : "BLOCK";
+ IndentedScope indent(this, block_txt);
+ PrintStatements(node->statements());
+}
+
+
+// TODO(svenpanne) Start with IndentedScope.
+void AstPrinter::VisitVariableDeclaration(VariableDeclaration* node) {
+ PrintLiteralWithModeIndented(Variable::Mode2String(node->mode()),
+ node->proxy()->var(),
+ node->proxy()->name());
+}
+
+
+// TODO(svenpanne) Start with IndentedScope.
+void AstPrinter::VisitFunctionDeclaration(FunctionDeclaration* node) {
+ PrintIndented("FUNCTION ");
+ PrintLiteral(node->proxy()->name(), true);
+ Print(" = function ");
+ PrintLiteral(node->fun()->name(), false);
+ Print("\n");
+}
+
+
+void AstPrinter::VisitModuleDeclaration(ModuleDeclaration* node) {
+ IndentedScope indent(this, "MODULE");
+ PrintLiteralIndented("NAME", node->proxy()->name(), true);
+ Visit(node->module());
+}
+
+
+void AstPrinter::VisitImportDeclaration(ImportDeclaration* node) {
+ IndentedScope indent(this, "IMPORT");
+ PrintLiteralIndented("NAME", node->proxy()->name(), true);
+ Visit(node->module());
+}
+
+
+void AstPrinter::VisitExportDeclaration(ExportDeclaration* node) {
+ IndentedScope indent(this, "EXPORT ");
+ PrintLiteral(node->proxy()->name(), true);
+}
+
+
+void AstPrinter::VisitModuleLiteral(ModuleLiteral* node) {
+ IndentedScope indent(this, "MODULE LITERAL");
+ VisitBlock(node->body());
+}
+
+
+void AstPrinter::VisitModuleVariable(ModuleVariable* node) {
+ IndentedScope indent(this, "MODULE VARIABLE");
+ Visit(node->proxy());
+}
+
+
+void AstPrinter::VisitModulePath(ModulePath* node) {
+ IndentedScope indent(this, "MODULE PATH");
+ PrintIndentedVisit("MODULE PATH PARENT", node->module());
+ PrintLiteralIndented("NAME", node->name(), true);
+}
+
+
+void AstPrinter::VisitModuleUrl(ModuleUrl* node) {
+ PrintLiteralIndented("URL", node->url(), true);
+}
+
+
+void AstPrinter::VisitModuleStatement(ModuleStatement* node) {
+ IndentedScope indent(this, "MODULE STATEMENT");
+ PrintLiteralIndented("NAME", node->proxy()->name(), true);
+ PrintStatements(node->body()->statements());
+}
+
+
+void AstPrinter::VisitExpressionStatement(ExpressionStatement* node) {
+ IndentedScope indent(this, "EXPRESSION STATEMENT");
+ Visit(node->expression());
+}
+
+
+void AstPrinter::VisitEmptyStatement(EmptyStatement* node) {
+ IndentedScope indent(this, "EMPTY");
+}
+
+
+void AstPrinter::VisitIfStatement(IfStatement* node) {
+ IndentedScope indent(this, "IF");
+ PrintIndentedVisit("CONDITION", node->condition());
+ PrintIndentedVisit("THEN", node->then_statement());
+ if (node->HasElseStatement()) {
+ PrintIndentedVisit("ELSE", node->else_statement());
+ }
+}
+
+
+void AstPrinter::VisitContinueStatement(ContinueStatement* node) {
+ IndentedScope indent(this, "CONTINUE");
+ PrintLabelsIndented(node->target()->labels());
+}
+
+
+void AstPrinter::VisitBreakStatement(BreakStatement* node) {
+ IndentedScope indent(this, "BREAK");
+ PrintLabelsIndented(node->target()->labels());
+}
+
+
+void AstPrinter::VisitReturnStatement(ReturnStatement* node) {
+ IndentedScope indent(this, "RETURN");
+ Visit(node->expression());
+}
+
+
+void AstPrinter::VisitWithStatement(WithStatement* node) {
+ IndentedScope indent(this, "WITH");
+ PrintIndentedVisit("OBJECT", node->expression());
+ PrintIndentedVisit("BODY", node->statement());
+}
+
+
+void AstPrinter::VisitSwitchStatement(SwitchStatement* node) {
+ IndentedScope indent(this, "SWITCH");
+ PrintLabelsIndented(node->labels());
+ PrintIndentedVisit("TAG", node->tag());
+ for (int i = 0; i < node->cases()->length(); i++) {
+ PrintCaseClause(node->cases()->at(i));
+ }
+}
+
+
+void AstPrinter::VisitDoWhileStatement(DoWhileStatement* node) {
+ IndentedScope indent(this, "DO");
+ PrintLabelsIndented(node->labels());
+ PrintIndentedVisit("BODY", node->body());
+ PrintIndentedVisit("COND", node->cond());
+}
+
+
+void AstPrinter::VisitWhileStatement(WhileStatement* node) {
+ IndentedScope indent(this, "WHILE");
+ PrintLabelsIndented(node->labels());
+ PrintIndentedVisit("COND", node->cond());
+ PrintIndentedVisit("BODY", node->body());
+}
+
+
+void AstPrinter::VisitForStatement(ForStatement* node) {
+ IndentedScope indent(this, "FOR");
+ PrintLabelsIndented(node->labels());
+ if (node->init()) PrintIndentedVisit("INIT", node->init());
+ if (node->cond()) PrintIndentedVisit("COND", node->cond());
+ PrintIndentedVisit("BODY", node->body());
+ if (node->next()) PrintIndentedVisit("NEXT", node->next());
+}
+
+
+void AstPrinter::VisitForInStatement(ForInStatement* node) {
+ IndentedScope indent(this, "FOR IN");
+ PrintIndentedVisit("FOR", node->each());
+ PrintIndentedVisit("IN", node->enumerable());
+ PrintIndentedVisit("BODY", node->body());
+}
+
+
+void AstPrinter::VisitForOfStatement(ForOfStatement* node) {
+ IndentedScope indent(this, "FOR OF");
+ PrintIndentedVisit("FOR", node->each());
+ PrintIndentedVisit("OF", node->iterable());
+ PrintIndentedVisit("BODY", node->body());
+}
+
+
+void AstPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
+ IndentedScope indent(this, "TRY CATCH");
+ PrintIndentedVisit("TRY", node->try_block());
+ PrintLiteralWithModeIndented("CATCHVAR",
+ node->variable(),
+ node->variable()->name());
+ PrintIndentedVisit("CATCH", node->catch_block());
+}
+
+
+void AstPrinter::VisitTryFinallyStatement(TryFinallyStatement* node) {
+ IndentedScope indent(this, "TRY FINALLY");
+ PrintIndentedVisit("TRY", node->try_block());
+ PrintIndentedVisit("FINALLY", node->finally_block());
+}
+
+
+void AstPrinter::VisitDebuggerStatement(DebuggerStatement* node) {
+ IndentedScope indent(this, "DEBUGGER");
+}
+
+
+void AstPrinter::VisitFunctionLiteral(FunctionLiteral* node) {
+ IndentedScope indent(this, "FUNC LITERAL");
+ PrintLiteralIndented("NAME", node->name(), false);
+ PrintLiteralIndented("INFERRED NAME", node->inferred_name(), false);
+ PrintParameters(node->scope());
+ // We don't want to see the function literal in this case: it
+ // will be printed via PrintProgram when the code for it is
+ // generated.
+ // PrintStatements(node->body());
+}
+
+
+void AstPrinter::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* node) {
+ IndentedScope indent(this, "FUNC LITERAL");
+ PrintLiteralIndented("SHARED INFO", node->shared_function_info(), true);
+}
+
+
+void AstPrinter::VisitConditional(Conditional* node) {
+ IndentedScope indent(this, "CONDITIONAL");
+ PrintIndentedVisit("CONDITION", node->condition());
+ PrintIndentedVisit("THEN", node->then_expression());
+ PrintIndentedVisit("ELSE", node->else_expression());
+}
+
+
+// TODO(svenpanne) Start with IndentedScope.
+void AstPrinter::VisitLiteral(Literal* node) {
+ PrintLiteralIndented("LITERAL", node->value(), true);
+}
+
+
+void AstPrinter::VisitRegExpLiteral(RegExpLiteral* node) {
+ IndentedScope indent(this, "REGEXP LITERAL");
+ PrintLiteralIndented("PATTERN", node->pattern(), false);
+ PrintLiteralIndented("FLAGS", node->flags(), false);
+}
+
+
+void AstPrinter::VisitObjectLiteral(ObjectLiteral* node) {
+ IndentedScope indent(this, "OBJ LITERAL");
+ for (int i = 0; i < node->properties()->length(); i++) {
+ const char* prop_kind = NULL;
+ switch (node->properties()->at(i)->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ prop_kind = "PROPERTY - CONSTANT";
+ break;
+ case ObjectLiteral::Property::COMPUTED:
+ prop_kind = "PROPERTY - COMPUTED";
+ break;
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+ prop_kind = "PROPERTY - MATERIALIZED_LITERAL";
+ break;
+ case ObjectLiteral::Property::PROTOTYPE:
+ prop_kind = "PROPERTY - PROTOTYPE";
+ break;
+ case ObjectLiteral::Property::GETTER:
+ prop_kind = "PROPERTY - GETTER";
+ break;
+ case ObjectLiteral::Property::SETTER:
+ prop_kind = "PROPERTY - SETTER";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ IndentedScope prop(this, prop_kind);
+ PrintIndentedVisit("KEY", node->properties()->at(i)->key());
+ PrintIndentedVisit("VALUE", node->properties()->at(i)->value());
+ }
+}
+
+
+void AstPrinter::VisitArrayLiteral(ArrayLiteral* node) {
+ IndentedScope indent(this, "ARRAY LITERAL");
+ if (node->values()->length() > 0) {
+ IndentedScope indent(this, "VALUES");
+ for (int i = 0; i < node->values()->length(); i++) {
+ Visit(node->values()->at(i));
+ }
+ }
+}
+
+
+// TODO(svenpanne) Start with IndentedScope.
+void AstPrinter::VisitVariableProxy(VariableProxy* node) {
+ Variable* var = node->var();
+ EmbeddedVector<char, 128> buf;
+ int pos = OS::SNPrintF(buf, "VAR PROXY");
+ switch (var->location()) {
+ case Variable::UNALLOCATED:
+ break;
+ case Variable::PARAMETER:
+ OS::SNPrintF(buf + pos, " parameter[%d]", var->index());
+ break;
+ case Variable::LOCAL:
+ OS::SNPrintF(buf + pos, " local[%d]", var->index());
+ break;
+ case Variable::CONTEXT:
+ OS::SNPrintF(buf + pos, " context[%d]", var->index());
+ break;
+ case Variable::LOOKUP:
+ OS::SNPrintF(buf + pos, " lookup");
+ break;
+ }
+ PrintLiteralWithModeIndented(buf.start(), var, node->name());
+}
+
+
+void AstPrinter::VisitAssignment(Assignment* node) {
+ IndentedScope indent(this, Token::Name(node->op()));
+ Visit(node->target());
+ Visit(node->value());
+}
+
+
+void AstPrinter::VisitYield(Yield* node) {
+ IndentedScope indent(this, "YIELD");
+ Visit(node->expression());
+}
+
+
+void AstPrinter::VisitThrow(Throw* node) {
+ IndentedScope indent(this, "THROW");
+ Visit(node->exception());
+}
+
+
+void AstPrinter::VisitProperty(Property* node) {
+ IndentedScope indent(this, "PROPERTY");
+ Visit(node->obj());
+ Literal* literal = node->key()->AsLiteral();
+ if (literal != NULL && literal->value()->IsInternalizedString()) {
+ PrintLiteralIndented("NAME", literal->value(), false);
+ } else {
+ PrintIndentedVisit("KEY", node->key());
+ }
+}
+
+
+void AstPrinter::VisitCall(Call* node) {
+ IndentedScope indent(this, "CALL");
+ Visit(node->expression());
+ PrintArguments(node->arguments());
+}
+
+
+void AstPrinter::VisitCallNew(CallNew* node) {
+ IndentedScope indent(this, "CALL NEW");
+ Visit(node->expression());
+ PrintArguments(node->arguments());
+}
+
+
+void AstPrinter::VisitCallRuntime(CallRuntime* node) {
+ IndentedScope indent(this, "CALL RUNTIME");
+ PrintLiteralIndented("NAME", node->name(), false);
+ PrintArguments(node->arguments());
+}
+
+
+void AstPrinter::VisitUnaryOperation(UnaryOperation* node) {
+ IndentedScope indent(this, Token::Name(node->op()));
+ Visit(node->expression());
+}
+
+
+void AstPrinter::VisitCountOperation(CountOperation* node) {
+ EmbeddedVector<char, 128> buf;
+ OS::SNPrintF(buf, "%s %s", (node->is_prefix() ? "PRE" : "POST"),
+ Token::Name(node->op()));
+ IndentedScope indent(this, buf.start());
+ Visit(node->expression());
+}
+
+
+void AstPrinter::VisitBinaryOperation(BinaryOperation* node) {
+ IndentedScope indent(this, Token::Name(node->op()));
+ Visit(node->left());
+ Visit(node->right());
+}
+
+
+void AstPrinter::VisitCompareOperation(CompareOperation* node) {
+ IndentedScope indent(this, Token::Name(node->op()));
+ Visit(node->left());
+ Visit(node->right());
+}
+
+
+void AstPrinter::VisitThisFunction(ThisFunction* node) {
+ IndentedScope indent(this, "THIS-FUNCTION");
+}
+
+#endif // DEBUG
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/prettyprinter.h b/chromium/v8/src/prettyprinter.h
new file mode 100644
index 00000000000..6657ecd1458
--- /dev/null
+++ b/chromium/v8/src/prettyprinter.h
@@ -0,0 +1,121 @@
+// Copyright 2012 the V8 project authors. 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_PRETTYPRINTER_H_
+#define V8_PRETTYPRINTER_H_
+
+#include "allocation.h"
+#include "ast.h"
+
+namespace v8 {
+namespace internal {
+
+#ifdef DEBUG
+
+class PrettyPrinter: public AstVisitor {
+ public:
+ PrettyPrinter();
+ virtual ~PrettyPrinter();
+
+ // The following routines print a node into a string.
+ // The result string is alive as long as the PrettyPrinter is alive.
+ const char* Print(AstNode* node);
+ const char* PrintExpression(FunctionLiteral* program);
+ const char* PrintProgram(FunctionLiteral* program);
+
+ void Print(const char* format, ...);
+
+ // Print a node to stdout.
+ static void PrintOut(AstNode* node);
+
+ // Individual nodes
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ private:
+ char* output_; // output string buffer
+ int size_; // output_ size
+ int pos_; // current printing position
+
+ protected:
+ void Init();
+ const char* Output() const { return output_; }
+
+ virtual void PrintStatements(ZoneList<Statement*>* statements);
+ void PrintLabels(ZoneStringList* labels);
+ virtual void PrintArguments(ZoneList<Expression*>* arguments);
+ void PrintLiteral(Handle<Object> value, bool quote);
+ void PrintParameters(Scope* scope);
+ void PrintDeclarations(ZoneList<Declaration*>* declarations);
+ void PrintFunctionLiteral(FunctionLiteral* function);
+ void PrintCaseClause(CaseClause* clause);
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+};
+
+
+// Prints the AST structure
+class AstPrinter: public PrettyPrinter {
+ public:
+ AstPrinter();
+ virtual ~AstPrinter();
+
+ const char* PrintProgram(FunctionLiteral* program);
+
+ // Individual nodes
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ private:
+ friend class IndentedScope;
+ void PrintIndented(const char* txt);
+ void PrintIndentedVisit(const char* s, AstNode* node);
+
+ void PrintStatements(ZoneList<Statement*>* statements);
+ void PrintDeclarations(ZoneList<Declaration*>* declarations);
+ void PrintParameters(Scope* scope);
+ void PrintArguments(ZoneList<Expression*>* arguments);
+ void PrintCaseClause(CaseClause* clause);
+ void PrintLiteralIndented(const char* info, Handle<Object> value, bool quote);
+ void PrintLiteralWithModeIndented(const char* info,
+ Variable* var,
+ Handle<Object> value);
+ void PrintLabelsIndented(ZoneStringList* labels);
+
+ void inc_indent() { indent_++; }
+ void dec_indent() { indent_--; }
+
+ int indent_;
+};
+
+#endif // DEBUG
+
+} } // namespace v8::internal
+
+#endif // V8_PRETTYPRINTER_H_
diff --git a/chromium/v8/src/profile-generator-inl.h b/chromium/v8/src/profile-generator-inl.h
new file mode 100644
index 00000000000..6791c88c56e
--- /dev/null
+++ b/chromium/v8/src/profile-generator-inl.h
@@ -0,0 +1,103 @@
+// 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.
+
+#ifndef V8_PROFILE_GENERATOR_INL_H_
+#define V8_PROFILE_GENERATOR_INL_H_
+
+#include "profile-generator.h"
+
+namespace v8 {
+namespace internal {
+
+const char* StringsStorage::GetFunctionName(Name* name) {
+ return GetFunctionName(GetName(name));
+}
+
+
+const char* StringsStorage::GetFunctionName(const char* name) {
+ return strlen(name) > 0 ? name : ProfileGenerator::kAnonymousFunctionName;
+}
+
+
+CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
+ const char* name,
+ const char* name_prefix,
+ const char* resource_name,
+ int line_number)
+ : tag_(tag),
+ builtin_id_(Builtins::builtin_count),
+ name_prefix_(name_prefix),
+ name_(name),
+ resource_name_(resource_name),
+ line_number_(line_number),
+ shared_id_(0),
+ script_id_(v8::Script::kNoScriptId),
+ no_frame_ranges_(NULL) {
+}
+
+
+bool CodeEntry::is_js_function_tag(Logger::LogEventsAndTags tag) {
+ return tag == Logger::FUNCTION_TAG
+ || tag == Logger::LAZY_COMPILE_TAG
+ || tag == Logger::SCRIPT_TAG
+ || tag == Logger::NATIVE_FUNCTION_TAG
+ || tag == Logger::NATIVE_LAZY_COMPILE_TAG
+ || tag == Logger::NATIVE_SCRIPT_TAG;
+}
+
+
+ProfileNode::ProfileNode(ProfileTree* tree, CodeEntry* entry)
+ : tree_(tree),
+ entry_(entry),
+ total_ticks_(0),
+ self_ticks_(0),
+ children_(CodeEntriesMatch),
+ id_(tree->next_node_id()) {
+}
+
+
+CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
+ switch (tag) {
+ case GC:
+ return gc_entry_;
+ case JS:
+ case COMPILER:
+ // DOM events handlers are reported as OTHER / EXTERNAL entries.
+ // To avoid confusing people, let's put all these entries into
+ // one bucket.
+ case OTHER:
+ case EXTERNAL:
+ return program_entry_;
+ case IDLE:
+ return idle_entry_;
+ default: return NULL;
+ }
+}
+
+} } // namespace v8::internal
+
+#endif // V8_PROFILE_GENERATOR_INL_H_
diff --git a/chromium/v8/src/profile-generator.cc b/chromium/v8/src/profile-generator.cc
new file mode 100644
index 00000000000..86bd17b70a0
--- /dev/null
+++ b/chromium/v8/src/profile-generator.cc
@@ -0,0 +1,716 @@
+// Copyright 2012 the V8 project authors. 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 "profile-generator-inl.h"
+
+#include "compiler.h"
+#include "debug.h"
+#include "sampler.h"
+#include "global-handles.h"
+#include "scopeinfo.h"
+#include "unicode.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+StringsStorage::StringsStorage()
+ : names_(StringsMatch) {
+}
+
+
+StringsStorage::~StringsStorage() {
+ for (HashMap::Entry* p = names_.Start();
+ p != NULL;
+ p = names_.Next(p)) {
+ DeleteArray(reinterpret_cast<const char*>(p->value));
+ }
+}
+
+
+const char* StringsStorage::GetCopy(const char* src) {
+ int len = static_cast<int>(strlen(src));
+ Vector<char> dst = Vector<char>::New(len + 1);
+ OS::StrNCpy(dst, src, len);
+ dst[len] = '\0';
+ uint32_t hash =
+ StringHasher::HashSequentialString(dst.start(), len, HEAP->HashSeed());
+ return AddOrDisposeString(dst.start(), hash);
+}
+
+
+const char* StringsStorage::GetFormatted(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ const char* result = GetVFormatted(format, args);
+ va_end(args);
+ return result;
+}
+
+
+const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) {
+ HashMap::Entry* cache_entry = names_.Lookup(str, hash, true);
+ if (cache_entry->value == NULL) {
+ // New entry added.
+ cache_entry->value = str;
+ } else {
+ DeleteArray(str);
+ }
+ return reinterpret_cast<const char*>(cache_entry->value);
+}
+
+
+const char* StringsStorage::GetVFormatted(const char* format, va_list args) {
+ Vector<char> str = Vector<char>::New(1024);
+ int len = OS::VSNPrintF(str, format, args);
+ if (len == -1) {
+ DeleteArray(str.start());
+ return format;
+ }
+ uint32_t hash = StringHasher::HashSequentialString(
+ str.start(), len, HEAP->HashSeed());
+ return AddOrDisposeString(str.start(), hash);
+}
+
+
+const char* StringsStorage::GetName(Name* name) {
+ if (name->IsString()) {
+ String* str = String::cast(name);
+ int length = Min(kMaxNameSize, str->length());
+ SmartArrayPointer<char> data =
+ str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length);
+ uint32_t hash = StringHasher::HashSequentialString(
+ *data, length, name->GetHeap()->HashSeed());
+ return AddOrDisposeString(data.Detach(), hash);
+ } else if (name->IsSymbol()) {
+ return "<symbol>";
+ }
+ return "";
+}
+
+
+const char* StringsStorage::GetName(int index) {
+ return GetFormatted("%d", index);
+}
+
+
+size_t StringsStorage::GetUsedMemorySize() const {
+ size_t size = sizeof(*this);
+ size += sizeof(HashMap::Entry) * names_.capacity();
+ for (HashMap::Entry* p = names_.Start(); p != NULL; p = names_.Next(p)) {
+ size += strlen(reinterpret_cast<const char*>(p->value)) + 1;
+ }
+ return size;
+}
+
+
+const char* const CodeEntry::kEmptyNamePrefix = "";
+const char* const CodeEntry::kEmptyResourceName = "";
+
+
+CodeEntry::~CodeEntry() {
+ delete no_frame_ranges_;
+}
+
+
+void CodeEntry::CopyData(const CodeEntry& source) {
+ tag_ = source.tag_;
+ name_prefix_ = source.name_prefix_;
+ name_ = source.name_;
+ resource_name_ = source.resource_name_;
+ line_number_ = source.line_number_;
+}
+
+
+uint32_t CodeEntry::GetCallUid() const {
+ uint32_t hash = ComputeIntegerHash(tag_, v8::internal::kZeroHashSeed);
+ if (shared_id_ != 0) {
+ hash ^= ComputeIntegerHash(static_cast<uint32_t>(shared_id_),
+ v8::internal::kZeroHashSeed);
+ } else {
+ hash ^= ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)),
+ v8::internal::kZeroHashSeed);
+ hash ^= ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)),
+ v8::internal::kZeroHashSeed);
+ hash ^= ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)),
+ v8::internal::kZeroHashSeed);
+ hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed);
+ }
+ return hash;
+}
+
+
+bool CodeEntry::IsSameAs(CodeEntry* entry) const {
+ return this == entry
+ || (tag_ == entry->tag_
+ && shared_id_ == entry->shared_id_
+ && (shared_id_ != 0
+ || (name_prefix_ == entry->name_prefix_
+ && name_ == entry->name_
+ && resource_name_ == entry->resource_name_
+ && line_number_ == entry->line_number_)));
+}
+
+
+void CodeEntry::SetBuiltinId(Builtins::Name id) {
+ tag_ = Logger::BUILTIN_TAG;
+ builtin_id_ = id;
+}
+
+
+ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
+ HashMap::Entry* map_entry =
+ children_.Lookup(entry, CodeEntryHash(entry), false);
+ return map_entry != NULL ?
+ reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
+}
+
+
+ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
+ HashMap::Entry* map_entry =
+ children_.Lookup(entry, CodeEntryHash(entry), true);
+ if (map_entry->value == NULL) {
+ // New node added.
+ ProfileNode* new_node = new ProfileNode(tree_, entry);
+ map_entry->value = new_node;
+ children_list_.Add(new_node);
+ }
+ return reinterpret_cast<ProfileNode*>(map_entry->value);
+}
+
+
+double ProfileNode::GetSelfMillis() const {
+ return tree_->TicksToMillis(self_ticks_);
+}
+
+
+double ProfileNode::GetTotalMillis() const {
+ return tree_->TicksToMillis(total_ticks_);
+}
+
+
+void ProfileNode::Print(int indent) {
+ OS::Print("%5u %5u %*c %s%s %d #%d",
+ total_ticks_, self_ticks_,
+ indent, ' ',
+ entry_->name_prefix(),
+ entry_->name(),
+ entry_->script_id(),
+ id());
+ if (entry_->resource_name()[0] != '\0')
+ OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
+ OS::Print("\n");
+ for (HashMap::Entry* p = children_.Start();
+ p != NULL;
+ p = children_.Next(p)) {
+ reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
+ }
+}
+
+
+class DeleteNodesCallback {
+ public:
+ void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
+
+ void AfterAllChildrenTraversed(ProfileNode* node) {
+ delete node;
+ }
+
+ void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
+};
+
+
+ProfileTree::ProfileTree()
+ : root_entry_(Logger::FUNCTION_TAG, "(root)"),
+ next_node_id_(1),
+ root_(new ProfileNode(this, &root_entry_)) {
+}
+
+
+ProfileTree::~ProfileTree() {
+ DeleteNodesCallback cb;
+ TraverseDepthFirst(&cb);
+}
+
+
+ProfileNode* ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) {
+ ProfileNode* node = root_;
+ for (CodeEntry** entry = path.start() + path.length() - 1;
+ entry != path.start() - 1;
+ --entry) {
+ if (*entry != NULL) {
+ node = node->FindOrAddChild(*entry);
+ }
+ }
+ node->IncrementSelfTicks();
+ return node;
+}
+
+
+void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
+ ProfileNode* node = root_;
+ for (CodeEntry** entry = path.start();
+ entry != path.start() + path.length();
+ ++entry) {
+ if (*entry != NULL) {
+ node = node->FindOrAddChild(*entry);
+ }
+ }
+ node->IncrementSelfTicks();
+}
+
+
+struct NodesPair {
+ NodesPair(ProfileNode* src, ProfileNode* dst)
+ : src(src), dst(dst) { }
+ ProfileNode* src;
+ ProfileNode* dst;
+};
+
+
+void ProfileTree::SetTickRatePerMs(double ticks_per_ms) {
+ ms_to_ticks_scale_ = ticks_per_ms > 0 ? 1.0 / ticks_per_ms : 1.0;
+}
+
+
+class Position {
+ public:
+ explicit Position(ProfileNode* node)
+ : node(node), child_idx_(0) { }
+ INLINE(ProfileNode* current_child()) {
+ return node->children()->at(child_idx_);
+ }
+ INLINE(bool has_current_child()) {
+ return child_idx_ < node->children()->length();
+ }
+ INLINE(void next_child()) { ++child_idx_; }
+
+ ProfileNode* node;
+ private:
+ int child_idx_;
+};
+
+
+// Non-recursive implementation of a depth-first post-order tree traversal.
+template <typename Callback>
+void ProfileTree::TraverseDepthFirst(Callback* callback) {
+ List<Position> stack(10);
+ stack.Add(Position(root_));
+ while (stack.length() > 0) {
+ Position& current = stack.last();
+ if (current.has_current_child()) {
+ callback->BeforeTraversingChild(current.node, current.current_child());
+ stack.Add(Position(current.current_child()));
+ } else {
+ callback->AfterAllChildrenTraversed(current.node);
+ if (stack.length() > 1) {
+ Position& parent = stack[stack.length() - 2];
+ callback->AfterChildTraversed(parent.node, current.node);
+ parent.next_child();
+ }
+ // Remove child from the stack.
+ stack.RemoveLast();
+ }
+ }
+}
+
+
+class CalculateTotalTicksCallback {
+ public:
+ void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
+
+ void AfterAllChildrenTraversed(ProfileNode* node) {
+ node->IncreaseTotalTicks(node->self_ticks());
+ }
+
+ void AfterChildTraversed(ProfileNode* parent, ProfileNode* child) {
+ parent->IncreaseTotalTicks(child->total_ticks());
+ }
+};
+
+
+void ProfileTree::CalculateTotalTicks() {
+ CalculateTotalTicksCallback cb;
+ TraverseDepthFirst(&cb);
+}
+
+
+void ProfileTree::ShortPrint() {
+ OS::Print("root: %u %u %.2fms %.2fms\n",
+ root_->total_ticks(), root_->self_ticks(),
+ root_->GetTotalMillis(), root_->GetSelfMillis());
+}
+
+
+CpuProfile::CpuProfile(const char* title, unsigned uid, bool record_samples)
+ : title_(title),
+ uid_(uid),
+ record_samples_(record_samples),
+ start_time_us_(OS::Ticks()),
+ end_time_us_(0) {
+}
+
+
+void CpuProfile::AddPath(const Vector<CodeEntry*>& path) {
+ ProfileNode* top_frame_node = top_down_.AddPathFromEnd(path);
+ if (record_samples_) samples_.Add(top_frame_node);
+}
+
+
+void CpuProfile::CalculateTotalTicksAndSamplingRate() {
+ end_time_us_ = OS::Ticks();
+ top_down_.CalculateTotalTicks();
+
+ double duration_ms = (end_time_us_ - start_time_us_) / 1000.;
+ if (duration_ms < 1) duration_ms = 1;
+ unsigned ticks = top_down_.root()->total_ticks();
+ double rate = ticks / duration_ms;
+ top_down_.SetTickRatePerMs(rate);
+}
+
+
+void CpuProfile::ShortPrint() {
+ OS::Print("top down ");
+ top_down_.ShortPrint();
+}
+
+
+void CpuProfile::Print() {
+ OS::Print("[Top down]:\n");
+ top_down_.Print();
+}
+
+
+CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL;
+const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
+
+
+void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
+ DeleteAllCoveredCode(addr, addr + size);
+ CodeTree::Locator locator;
+ tree_.Insert(addr, &locator);
+ locator.set_value(CodeEntryInfo(entry, size));
+}
+
+
+void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
+ List<Address> to_delete;
+ Address addr = end - 1;
+ while (addr >= start) {
+ CodeTree::Locator locator;
+ if (!tree_.FindGreatestLessThan(addr, &locator)) break;
+ Address start2 = locator.key(), end2 = start2 + locator.value().size;
+ if (start2 < end && start < end2) to_delete.Add(start2);
+ addr = start2 - 1;
+ }
+ for (int i = 0; i < to_delete.length(); ++i) tree_.Remove(to_delete[i]);
+}
+
+
+CodeEntry* CodeMap::FindEntry(Address addr, Address* start) {
+ CodeTree::Locator locator;
+ if (tree_.FindGreatestLessThan(addr, &locator)) {
+ // locator.key() <= addr. Need to check that addr is within entry.
+ const CodeEntryInfo& entry = locator.value();
+ if (addr < (locator.key() + entry.size)) {
+ if (start) {
+ *start = locator.key();
+ }
+ return entry.entry;
+ }
+ }
+ return NULL;
+}
+
+
+int CodeMap::GetSharedId(Address addr) {
+ CodeTree::Locator locator;
+ // For shared function entries, 'size' field is used to store their IDs.
+ if (tree_.Find(addr, &locator)) {
+ const CodeEntryInfo& entry = locator.value();
+ ASSERT(entry.entry == kSharedFunctionCodeEntry);
+ return entry.size;
+ } else {
+ tree_.Insert(addr, &locator);
+ int id = next_shared_id_++;
+ locator.set_value(CodeEntryInfo(kSharedFunctionCodeEntry, id));
+ return id;
+ }
+}
+
+
+void CodeMap::MoveCode(Address from, Address to) {
+ if (from == to) return;
+ CodeTree::Locator locator;
+ if (!tree_.Find(from, &locator)) return;
+ CodeEntryInfo entry = locator.value();
+ tree_.Remove(from);
+ AddCode(to, entry.entry, entry.size);
+}
+
+
+void CodeMap::CodeTreePrinter::Call(
+ const Address& key, const CodeMap::CodeEntryInfo& value) {
+ // For shared function entries, 'size' field is used to store their IDs.
+ if (value.entry == kSharedFunctionCodeEntry) {
+ OS::Print("%p SharedFunctionInfo %d\n", key, value.size);
+ } else {
+ OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
+ }
+}
+
+
+void CodeMap::Print() {
+ CodeTreePrinter printer;
+ tree_.ForEach(&printer);
+}
+
+
+CpuProfilesCollection::CpuProfilesCollection()
+ : current_profiles_semaphore_(OS::CreateSemaphore(1)) {
+}
+
+
+static void DeleteCodeEntry(CodeEntry** entry_ptr) {
+ delete *entry_ptr;
+}
+
+
+static void DeleteCpuProfile(CpuProfile** profile_ptr) {
+ delete *profile_ptr;
+}
+
+
+CpuProfilesCollection::~CpuProfilesCollection() {
+ delete current_profiles_semaphore_;
+ finished_profiles_.Iterate(DeleteCpuProfile);
+ current_profiles_.Iterate(DeleteCpuProfile);
+ code_entries_.Iterate(DeleteCodeEntry);
+}
+
+
+bool CpuProfilesCollection::StartProfiling(const char* title, unsigned uid,
+ bool record_samples) {
+ ASSERT(uid > 0);
+ current_profiles_semaphore_->Wait();
+ if (current_profiles_.length() >= kMaxSimultaneousProfiles) {
+ current_profiles_semaphore_->Signal();
+ return false;
+ }
+ for (int i = 0; i < current_profiles_.length(); ++i) {
+ if (strcmp(current_profiles_[i]->title(), title) == 0) {
+ // Ignore attempts to start profile with the same title.
+ current_profiles_semaphore_->Signal();
+ return false;
+ }
+ }
+ current_profiles_.Add(new CpuProfile(title, uid, record_samples));
+ current_profiles_semaphore_->Signal();
+ return true;
+}
+
+
+CpuProfile* CpuProfilesCollection::StopProfiling(const char* title) {
+ const int title_len = StrLength(title);
+ CpuProfile* profile = NULL;
+ current_profiles_semaphore_->Wait();
+ for (int i = current_profiles_.length() - 1; i >= 0; --i) {
+ if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) {
+ profile = current_profiles_.Remove(i);
+ break;
+ }
+ }
+ current_profiles_semaphore_->Signal();
+
+ if (profile == NULL) return NULL;
+ profile->CalculateTotalTicksAndSamplingRate();
+ finished_profiles_.Add(profile);
+ return profile;
+}
+
+
+bool CpuProfilesCollection::IsLastProfile(const char* title) {
+ // Called from VM thread, and only it can mutate the list,
+ // so no locking is needed here.
+ if (current_profiles_.length() != 1) return false;
+ return StrLength(title) == 0
+ || strcmp(current_profiles_[0]->title(), title) == 0;
+}
+
+
+void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
+ // Called from VM thread for a completed profile.
+ unsigned uid = profile->uid();
+ for (int i = 0; i < finished_profiles_.length(); i++) {
+ if (uid == finished_profiles_[i]->uid()) {
+ finished_profiles_.Remove(i);
+ return;
+ }
+ }
+ UNREACHABLE();
+}
+
+
+void CpuProfilesCollection::AddPathToCurrentProfiles(
+ const Vector<CodeEntry*>& path) {
+ // As starting / stopping profiles is rare relatively to this
+ // method, we don't bother minimizing the duration of lock holding,
+ // e.g. copying contents of the list to a local vector.
+ current_profiles_semaphore_->Wait();
+ for (int i = 0; i < current_profiles_.length(); ++i) {
+ current_profiles_[i]->AddPath(path);
+ }
+ current_profiles_semaphore_->Signal();
+}
+
+
+CodeEntry* CpuProfilesCollection::NewCodeEntry(
+ Logger::LogEventsAndTags tag,
+ const char* name,
+ const char* name_prefix,
+ const char* resource_name,
+ int line_number) {
+ CodeEntry* code_entry = new CodeEntry(tag,
+ name,
+ name_prefix,
+ resource_name,
+ line_number);
+ code_entries_.Add(code_entry);
+ return code_entry;
+}
+
+
+const char* const ProfileGenerator::kAnonymousFunctionName =
+ "(anonymous function)";
+const char* const ProfileGenerator::kProgramEntryName =
+ "(program)";
+const char* const ProfileGenerator::kIdleEntryName =
+ "(idle)";
+const char* const ProfileGenerator::kGarbageCollectorEntryName =
+ "(garbage collector)";
+const char* const ProfileGenerator::kUnresolvedFunctionName =
+ "(unresolved function)";
+
+
+ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
+ : profiles_(profiles),
+ program_entry_(
+ profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)),
+ idle_entry_(
+ profiles->NewCodeEntry(Logger::FUNCTION_TAG, kIdleEntryName)),
+ gc_entry_(
+ profiles->NewCodeEntry(Logger::BUILTIN_TAG,
+ kGarbageCollectorEntryName)),
+ unresolved_entry_(
+ profiles->NewCodeEntry(Logger::FUNCTION_TAG,
+ kUnresolvedFunctionName)) {
+}
+
+
+void ProfileGenerator::RecordTickSample(const TickSample& sample) {
+ // Allocate space for stack frames + pc + function + vm-state.
+ ScopedVector<CodeEntry*> entries(sample.frames_count + 3);
+ // As actual number of decoded code entries may vary, initialize
+ // entries vector with NULL values.
+ CodeEntry** entry = entries.start();
+ memset(entry, 0, entries.length() * sizeof(*entry));
+ if (sample.pc != NULL) {
+ if (sample.has_external_callback && sample.state == EXTERNAL &&
+ sample.top_frame_type == StackFrame::EXIT) {
+ // Don't use PC when in external callback code, as it can point
+ // inside callback's code, and we will erroneously report
+ // that a callback calls itself.
+ *entry++ = code_map_.FindEntry(sample.external_callback);
+ } else {
+ Address start;
+ CodeEntry* pc_entry = code_map_.FindEntry(sample.pc, &start);
+ // If pc is in the function code before it set up stack frame or after the
+ // frame was destroyed SafeStackFrameIterator incorrectly thinks that
+ // ebp contains return address of the current function and skips caller's
+ // frame. Check for this case and just skip such samples.
+ if (pc_entry) {
+ List<OffsetRange>* ranges = pc_entry->no_frame_ranges();
+ if (ranges) {
+ Code* code = Code::cast(HeapObject::FromAddress(start));
+ int pc_offset = static_cast<int>(
+ sample.pc - code->instruction_start());
+ for (int i = 0; i < ranges->length(); i++) {
+ OffsetRange& range = ranges->at(i);
+ if (range.from <= pc_offset && pc_offset < range.to) {
+ return;
+ }
+ }
+ }
+ *entry++ = pc_entry;
+
+ if (pc_entry->builtin_id() == Builtins::kFunctionCall ||
+ pc_entry->builtin_id() == Builtins::kFunctionApply) {
+ // When current function is FunctionCall or FunctionApply builtin the
+ // top frame is either frame of the calling JS function or internal
+ // frame. In the latter case we know the caller for sure but in the
+ // former case we don't so we simply replace the frame with
+ // 'unresolved' entry.
+ if (sample.top_frame_type == StackFrame::JAVA_SCRIPT) {
+ *entry++ = unresolved_entry_;
+ }
+ }
+ }
+ }
+
+ for (const Address* stack_pos = sample.stack,
+ *stack_end = stack_pos + sample.frames_count;
+ stack_pos != stack_end;
+ ++stack_pos) {
+ *entry++ = code_map_.FindEntry(*stack_pos);
+ }
+ }
+
+ if (FLAG_prof_browser_mode) {
+ bool no_symbolized_entries = true;
+ for (CodeEntry** e = entries.start(); e != entry; ++e) {
+ if (*e != NULL) {
+ no_symbolized_entries = false;
+ break;
+ }
+ }
+ // If no frames were symbolized, put the VM state entry in.
+ if (no_symbolized_entries) {
+ *entry++ = EntryForVMState(sample.state);
+ }
+ }
+
+ profiles_->AddPathToCurrentProfiles(entries);
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/profile-generator.h b/chromium/v8/src/profile-generator.h
new file mode 100644
index 00000000000..99aeb1f5c7f
--- /dev/null
+++ b/chromium/v8/src/profile-generator.h
@@ -0,0 +1,367 @@
+// 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_PROFILE_GENERATOR_H_
+#define V8_PROFILE_GENERATOR_H_
+
+#include "allocation.h"
+#include "hashmap.h"
+#include "../include/v8-profiler.h"
+
+namespace v8 {
+namespace internal {
+
+struct OffsetRange;
+
+// Provides a storage of strings allocated in C++ heap, to hold them
+// forever, even if they disappear from JS heap or external storage.
+class StringsStorage {
+ public:
+ StringsStorage();
+ ~StringsStorage();
+
+ const char* GetCopy(const char* src);
+ const char* GetFormatted(const char* format, ...);
+ const char* GetVFormatted(const char* format, va_list args);
+ const char* GetName(Name* name);
+ const char* GetName(int index);
+ inline const char* GetFunctionName(Name* name);
+ inline const char* GetFunctionName(const char* name);
+ size_t GetUsedMemorySize() const;
+
+ 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;
+ }
+ const char* AddOrDisposeString(char* str, uint32_t hash);
+
+ // Mapping of strings by String::Hash to const char* strings.
+ HashMap names_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringsStorage);
+};
+
+
+class CodeEntry {
+ public:
+ // CodeEntry doesn't own name strings, just references them.
+ INLINE(CodeEntry(Logger::LogEventsAndTags tag,
+ const char* name,
+ const char* name_prefix = CodeEntry::kEmptyNamePrefix,
+ const char* resource_name = CodeEntry::kEmptyResourceName,
+ int line_number = v8::CpuProfileNode::kNoLineNumberInfo));
+ ~CodeEntry();
+
+ INLINE(bool is_js_function() const) { return is_js_function_tag(tag_); }
+ INLINE(const char* name_prefix() const) { return name_prefix_; }
+ INLINE(bool has_name_prefix() const) { return name_prefix_[0] != '\0'; }
+ INLINE(const char* name() const) { return name_; }
+ INLINE(const char* resource_name() const) { return resource_name_; }
+ INLINE(int line_number() const) { return line_number_; }
+ INLINE(void set_shared_id(int shared_id)) { shared_id_ = shared_id; }
+ INLINE(int script_id() const) { return script_id_; }
+ INLINE(void set_script_id(int script_id)) { script_id_ = script_id; }
+
+ INLINE(static bool is_js_function_tag(Logger::LogEventsAndTags tag));
+
+ List<OffsetRange>* no_frame_ranges() const { return no_frame_ranges_; }
+ void set_no_frame_ranges(List<OffsetRange>* ranges) {
+ no_frame_ranges_ = ranges;
+ }
+
+ void SetBuiltinId(Builtins::Name id);
+ Builtins::Name builtin_id() const { return builtin_id_; }
+
+ void CopyData(const CodeEntry& source);
+ uint32_t GetCallUid() const;
+ bool IsSameAs(CodeEntry* entry) const;
+
+ static const char* const kEmptyNamePrefix;
+ static const char* const kEmptyResourceName;
+
+ private:
+ Logger::LogEventsAndTags tag_ : 8;
+ Builtins::Name builtin_id_ : 8;
+ const char* name_prefix_;
+ const char* name_;
+ const char* resource_name_;
+ int line_number_;
+ int shared_id_;
+ int script_id_;
+ List<OffsetRange>* no_frame_ranges_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodeEntry);
+};
+
+
+class ProfileTree;
+
+class ProfileNode {
+ public:
+ INLINE(ProfileNode(ProfileTree* tree, CodeEntry* entry));
+
+ ProfileNode* FindChild(CodeEntry* entry);
+ ProfileNode* FindOrAddChild(CodeEntry* entry);
+ INLINE(void IncrementSelfTicks()) { ++self_ticks_; }
+ INLINE(void IncreaseSelfTicks(unsigned amount)) { self_ticks_ += amount; }
+ INLINE(void IncreaseTotalTicks(unsigned amount)) { total_ticks_ += amount; }
+
+ INLINE(CodeEntry* entry() const) { return entry_; }
+ INLINE(unsigned self_ticks() const) { return self_ticks_; }
+ INLINE(unsigned total_ticks() const) { return total_ticks_; }
+ INLINE(const List<ProfileNode*>* children() const) { return &children_list_; }
+ double GetSelfMillis() const;
+ double GetTotalMillis() const;
+ unsigned id() const { return id_; }
+
+ void Print(int indent);
+
+ private:
+ INLINE(static bool CodeEntriesMatch(void* entry1, void* entry2)) {
+ return reinterpret_cast<CodeEntry*>(entry1)->IsSameAs(
+ reinterpret_cast<CodeEntry*>(entry2));
+ }
+
+ INLINE(static uint32_t CodeEntryHash(CodeEntry* entry)) {
+ return entry->GetCallUid();
+ }
+
+ ProfileTree* tree_;
+ CodeEntry* entry_;
+ unsigned total_ticks_;
+ unsigned self_ticks_;
+ // Mapping from CodeEntry* to ProfileNode*
+ HashMap children_;
+ List<ProfileNode*> children_list_;
+ unsigned id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileNode);
+};
+
+
+class ProfileTree {
+ public:
+ ProfileTree();
+ ~ProfileTree();
+
+ ProfileNode* AddPathFromEnd(const Vector<CodeEntry*>& path);
+ void AddPathFromStart(const Vector<CodeEntry*>& path);
+ void CalculateTotalTicks();
+
+ double TicksToMillis(unsigned ticks) const {
+ return ticks * ms_to_ticks_scale_;
+ }
+ ProfileNode* root() const { return root_; }
+ void SetTickRatePerMs(double ticks_per_ms);
+
+ unsigned next_node_id() { return next_node_id_++; }
+
+ void ShortPrint();
+ void Print() {
+ root_->Print(0);
+ }
+
+ private:
+ template <typename Callback>
+ void TraverseDepthFirst(Callback* callback);
+
+ CodeEntry root_entry_;
+ unsigned next_node_id_;
+ ProfileNode* root_;
+ double ms_to_ticks_scale_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileTree);
+};
+
+
+class CpuProfile {
+ public:
+ CpuProfile(const char* title, unsigned uid, bool record_samples);
+
+ // Add pc -> ... -> main() call path to the profile.
+ void AddPath(const Vector<CodeEntry*>& path);
+ void CalculateTotalTicksAndSamplingRate();
+
+ const char* title() const { return title_; }
+ unsigned uid() const { return uid_; }
+ const ProfileTree* top_down() const { return &top_down_; }
+
+ int samples_count() const { return samples_.length(); }
+ ProfileNode* sample(int index) const { return samples_.at(index); }
+
+ int64_t start_time_us() const { return start_time_us_; }
+ int64_t end_time_us() const { return end_time_us_; }
+
+ void UpdateTicksScale();
+
+ void ShortPrint();
+ void Print();
+
+ private:
+ const char* title_;
+ unsigned uid_;
+ bool record_samples_;
+ int64_t start_time_us_;
+ int64_t end_time_us_;
+ List<ProfileNode*> samples_;
+ ProfileTree top_down_;
+
+ DISALLOW_COPY_AND_ASSIGN(CpuProfile);
+};
+
+
+class CodeMap {
+ public:
+ CodeMap() : next_shared_id_(1) { }
+ void AddCode(Address addr, CodeEntry* entry, unsigned size);
+ void MoveCode(Address from, Address to);
+ CodeEntry* FindEntry(Address addr, Address* start = NULL);
+ int GetSharedId(Address addr);
+
+ void Print();
+
+ private:
+ struct CodeEntryInfo {
+ CodeEntryInfo(CodeEntry* an_entry, unsigned a_size)
+ : entry(an_entry), size(a_size) { }
+ CodeEntry* entry;
+ unsigned size;
+ };
+
+ struct CodeTreeConfig {
+ typedef Address Key;
+ typedef CodeEntryInfo Value;
+ static const Key kNoKey;
+ 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);
+ }
+ };
+ typedef SplayTree<CodeTreeConfig> CodeTree;
+
+ class CodeTreePrinter {
+ public:
+ void Call(const Address& key, const CodeEntryInfo& value);
+ };
+
+ void DeleteAllCoveredCode(Address start, Address end);
+
+ // Fake CodeEntry pointer to distinguish shared function entries.
+ static CodeEntry* const kSharedFunctionCodeEntry;
+
+ CodeTree tree_;
+ int next_shared_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodeMap);
+};
+
+
+class CpuProfilesCollection {
+ public:
+ CpuProfilesCollection();
+ ~CpuProfilesCollection();
+
+ bool StartProfiling(const char* title, unsigned uid, bool record_samples);
+ CpuProfile* StopProfiling(const char* title);
+ List<CpuProfile*>* profiles() { return &finished_profiles_; }
+ const char* GetName(Name* name) {
+ return function_and_resource_names_.GetName(name);
+ }
+ const char* GetName(int args_count) {
+ return function_and_resource_names_.GetName(args_count);
+ }
+ const char* GetFunctionName(Name* name) {
+ return function_and_resource_names_.GetFunctionName(name);
+ }
+ const char* GetFunctionName(const char* name) {
+ return function_and_resource_names_.GetFunctionName(name);
+ }
+ bool IsLastProfile(const char* title);
+ void RemoveProfile(CpuProfile* profile);
+
+ CodeEntry* NewCodeEntry(
+ Logger::LogEventsAndTags tag,
+ const char* name,
+ const char* name_prefix = CodeEntry::kEmptyNamePrefix,
+ const char* resource_name = CodeEntry::kEmptyResourceName,
+ int line_number = v8::CpuProfileNode::kNoLineNumberInfo);
+
+ // Called from profile generator thread.
+ void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path);
+
+ // Limits the number of profiles that can be simultaneously collected.
+ static const int kMaxSimultaneousProfiles = 100;
+
+ private:
+ StringsStorage function_and_resource_names_;
+ List<CodeEntry*> code_entries_;
+ List<CpuProfile*> finished_profiles_;
+
+ // Accessed by VM thread and profile generator thread.
+ List<CpuProfile*> current_profiles_;
+ Semaphore* current_profiles_semaphore_;
+
+ DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection);
+};
+
+
+class ProfileGenerator {
+ public:
+ explicit ProfileGenerator(CpuProfilesCollection* profiles);
+
+ void RecordTickSample(const TickSample& sample);
+
+ INLINE(CodeMap* code_map()) { return &code_map_; }
+
+ static const char* const kAnonymousFunctionName;
+ static const char* const kProgramEntryName;
+ static const char* const kIdleEntryName;
+ static const char* const kGarbageCollectorEntryName;
+ // Used to represent frames for which we have no reliable way to
+ // detect function.
+ static const char* const kUnresolvedFunctionName;
+
+ private:
+ INLINE(CodeEntry* EntryForVMState(StateTag tag));
+
+ CpuProfilesCollection* profiles_;
+ CodeMap code_map_;
+ CodeEntry* program_entry_;
+ CodeEntry* idle_entry_;
+ CodeEntry* gc_entry_;
+ CodeEntry* unresolved_entry_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_PROFILE_GENERATOR_H_
diff --git a/chromium/v8/src/property-details.h b/chromium/v8/src/property-details.h
new file mode 100644
index 00000000000..6b62ddb18e5
--- /dev/null
+++ b/chromium/v8/src/property-details.h
@@ -0,0 +1,278 @@
+// Copyright 2012 the V8 project authors. 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,
+
+ SEALED = DONT_DELETE,
+ FROZEN = SEALED | READ_ONLY,
+
+ SYMBOLIC = 8, // Used to filter symbol names
+ DONT_SHOW = DONT_ENUM | SYMBOLIC,
+ 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;
+class Type;
+class TypeInfo;
+
+// 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 {
+ // Only in slow mode.
+ NORMAL = 0,
+ // Only in fast mode.
+ FIELD = 1,
+ CONSTANT = 2,
+ CALLBACKS = 3,
+ // Only in lookup results, not in descriptors.
+ HANDLER = 4,
+ INTERCEPTOR = 5,
+ TRANSITION = 6,
+ // Only used as a marker in LookupResult.
+ NONEXISTENT = 7
+};
+
+
+class Representation {
+ public:
+ enum Kind {
+ kNone,
+ kSmi,
+ kInteger32,
+ kDouble,
+ kHeapObject,
+ kTagged,
+ kExternal,
+ kNumRepresentations
+ };
+
+ Representation() : kind_(kNone) { }
+
+ static Representation None() { return Representation(kNone); }
+ static Representation Tagged() { return Representation(kTagged); }
+ static Representation Smi() { return Representation(kSmi); }
+ static Representation Integer32() { return Representation(kInteger32); }
+ static Representation Double() { return Representation(kDouble); }
+ static Representation HeapObject() { return Representation(kHeapObject); }
+ static Representation External() { return Representation(kExternal); }
+
+ static Representation FromKind(Kind kind) { return Representation(kind); }
+
+ // TODO(rossberg): this should die eventually.
+ static Representation FromType(TypeInfo info);
+ static Representation FromType(Handle<Type> type);
+
+ bool Equals(const Representation& other) const {
+ return kind_ == other.kind_;
+ }
+
+ bool IsCompatibleForLoad(const Representation& other) const {
+ return (IsDouble() && other.IsDouble()) ||
+ (!IsDouble() && !other.IsDouble());
+ }
+
+ bool IsCompatibleForStore(const Representation& other) const {
+ return Equals(other);
+ }
+
+ bool is_more_general_than(const Representation& other) const {
+ ASSERT(kind_ != kExternal);
+ ASSERT(other.kind_ != kExternal);
+ if (IsHeapObject()) return other.IsDouble() || other.IsNone();
+ return kind_ > other.kind_;
+ }
+
+ bool fits_into(const Representation& other) const {
+ return other.is_more_general_than(*this) || other.Equals(*this);
+ }
+
+ Representation generalize(Representation other) {
+ if (other.fits_into(*this)) return *this;
+ if (other.is_more_general_than(*this)) return other;
+ return Representation::Tagged();
+ }
+
+ Kind kind() const { return static_cast<Kind>(kind_); }
+ bool IsNone() const { return kind_ == kNone; }
+ bool IsTagged() const { return kind_ == kTagged; }
+ bool IsSmi() const { return kind_ == kSmi; }
+ bool IsSmiOrTagged() const { return IsSmi() || IsTagged(); }
+ bool IsInteger32() const { return kind_ == kInteger32; }
+ bool IsSmiOrInteger32() const { return IsSmi() || IsInteger32(); }
+ bool IsDouble() const { return kind_ == kDouble; }
+ bool IsHeapObject() const { return kind_ == kHeapObject; }
+ bool IsExternal() const { return kind_ == kExternal; }
+ bool IsSpecialization() const {
+ return kind_ == kInteger32 || kind_ == kDouble;
+ }
+ const char* Mnemonic() const;
+
+ private:
+ explicit Representation(Kind k) : kind_(k) { }
+
+ // Make sure kind fits in int8.
+ STATIC_ASSERT(kNumRepresentations <= (1 << kBitsPerByte));
+
+ int8_t kind_;
+};
+
+
+// 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) {
+ value_ = TypeField::encode(type)
+ | AttributesField::encode(attributes)
+ | DictionaryStorageField::encode(index);
+
+ ASSERT(type == this->type());
+ ASSERT(attributes == this->attributes());
+ }
+
+ PropertyDetails(PropertyAttributes attributes,
+ PropertyType type,
+ Representation representation,
+ int field_index = 0) {
+ value_ = TypeField::encode(type)
+ | AttributesField::encode(attributes)
+ | RepresentationField::encode(EncodeRepresentation(representation))
+ | FieldIndexField::encode(field_index);
+ }
+
+ int pointer() { return DescriptorPointer::decode(value_); }
+
+ PropertyDetails set_pointer(int i) { return PropertyDetails(value_, i); }
+
+ PropertyDetails CopyWithRepresentation(Representation representation) {
+ return PropertyDetails(value_, representation);
+ }
+ PropertyDetails CopyAddAttributes(PropertyAttributes new_attributes) {
+ new_attributes =
+ static_cast<PropertyAttributes>(attributes() | new_attributes);
+ return PropertyDetails(value_, new_attributes);
+ }
+
+ // Conversion for storing details as Object*.
+ explicit inline PropertyDetails(Smi* smi);
+ inline Smi* AsSmi();
+
+ static uint8_t EncodeRepresentation(Representation representation) {
+ return representation.kind();
+ }
+
+ static Representation DecodeRepresentation(uint32_t bits) {
+ return Representation::FromKind(static_cast<Representation::Kind>(bits));
+ }
+
+ PropertyType type() { return TypeField::decode(value_); }
+
+ PropertyAttributes attributes() const {
+ return AttributesField::decode(value_);
+ }
+
+ int dictionary_index() {
+ return DictionaryStorageField::decode(value_);
+ }
+
+ Representation representation() {
+ ASSERT(type() != NORMAL);
+ return DecodeRepresentation(RepresentationField::decode(value_));
+ }
+
+ int field_index() {
+ return FieldIndexField::decode(value_);
+ }
+
+ inline PropertyDetails AsDeleted();
+
+ static bool IsValidIndex(int index) {
+ return DictionaryStorageField::is_valid(index);
+ }
+
+ bool IsReadOnly() const { return (attributes() & READ_ONLY) != 0; }
+ bool IsDontDelete() const { return (attributes() & DONT_DELETE) != 0; }
+ bool IsDontEnum() const { return (attributes() & DONT_ENUM) != 0; }
+ bool IsDeleted() const { 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, 3> {};
+ class AttributesField: public BitField<PropertyAttributes, 3, 3> {};
+
+ // Bit fields for normalized objects.
+ class DeletedField: public BitField<uint32_t, 6, 1> {};
+ class DictionaryStorageField: public BitField<uint32_t, 7, 24> {};
+
+ // Bit fields for fast objects.
+ class DescriptorPointer: public BitField<uint32_t, 6, 11> {};
+ class RepresentationField: public BitField<uint32_t, 17, 3> {};
+ class FieldIndexField: public BitField<uint32_t, 20, 11> {};
+
+ static const int kInitialIndex = 1;
+
+ private:
+ PropertyDetails(int value, int pointer) {
+ value_ = DescriptorPointer::update(value, pointer);
+ }
+ PropertyDetails(int value, Representation representation) {
+ value_ = RepresentationField::update(
+ value, EncodeRepresentation(representation));
+ }
+ PropertyDetails(int value, PropertyAttributes attributes) {
+ value_ = AttributesField::update(value, attributes);
+ }
+
+ uint32_t value_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_PROPERTY_DETAILS_H_
diff --git a/chromium/v8/src/property.cc b/chromium/v8/src/property.cc
new file mode 100644
index 00000000000..83a6a365b87
--- /dev/null
+++ b/chromium/v8/src/property.cc
@@ -0,0 +1,121 @@
+// Copyright 2012 the V8 project authors. 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"
+
+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()) {
+ PrintF(out, "Not Found\n");
+ return;
+ }
+
+ PrintF(out, "LookupResult:\n");
+ PrintF(out, " -cacheable = %s\n", IsCacheable() ? "true" : "false");
+ PrintF(out, " -attributes = %x\n", GetAttributes());
+ switch (type()) {
+ case NORMAL:
+ PrintF(out, " -type = normal\n");
+ PrintF(out, " -entry = %d", GetDictionaryEntry());
+ break;
+ case CONSTANT:
+ PrintF(out, " -type = constant\n");
+ PrintF(out, " -value:\n");
+ GetConstant()->Print(out);
+ PrintF(out, "\n");
+ break;
+ case FIELD:
+ PrintF(out, " -type = field\n");
+ PrintF(out, " -index = %d", GetFieldIndex().field_index());
+ PrintF(out, "\n");
+ break;
+ case CALLBACKS:
+ PrintF(out, " -type = call backs\n");
+ PrintF(out, " -callback object:\n");
+ GetCallbackObject()->Print(out);
+ break;
+ case HANDLER:
+ PrintF(out, " -type = lookup proxy\n");
+ break;
+ case INTERCEPTOR:
+ PrintF(out, " -type = lookup interceptor\n");
+ break;
+ case TRANSITION:
+ switch (GetTransitionDetails().type()) {
+ case FIELD:
+ PrintF(out, " -type = map transition\n");
+ PrintF(out, " -map:\n");
+ GetTransitionMap()->Print(out);
+ PrintF(out, "\n");
+ return;
+ case CONSTANT:
+ PrintF(out, " -type = constant property transition\n");
+ PrintF(out, " -map:\n");
+ GetTransitionMap()->Print(out);
+ PrintF(out, "\n");
+ return;
+ case CALLBACKS:
+ PrintF(out, " -type = callbacks transition\n");
+ PrintF(out, " -callback object:\n");
+ GetCallbackObject()->Print(out);
+ return;
+ default:
+ UNREACHABLE();
+ return;
+ }
+ case NONEXISTENT:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void Descriptor::Print(FILE* out) {
+ PrintF(out, "Descriptor ");
+ GetKey()->ShortPrint(out);
+ PrintF(out, " @ ");
+ GetValue()->ShortPrint(out);
+}
+
+
+#endif
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/property.h b/chromium/v8/src/property.h
new file mode 100644
index 00000000000..d109de91d10
--- /dev/null
+++ b/chromium/v8/src/property.h
@@ -0,0 +1,513 @@
+// Copyright 2012 the V8 project authors. 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_H_
+#define V8_PROPERTY_H_
+
+#include "allocation.h"
+#include "transitions.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Abstraction for elements in instance-descriptor arrays.
+//
+// Each descriptor has a key, property attributes, property type,
+// property index (in the actual instance-descriptor array) and
+// optionally a piece of data.
+//
+
+class Descriptor BASE_EMBEDDED {
+ public:
+ MUST_USE_RESULT MaybeObject* KeyToUniqueName() {
+ if (!key_->IsUniqueName()) {
+ MaybeObject* maybe_result = HEAP->InternalizeString(String::cast(key_));
+ if (!maybe_result->To(&key_)) return maybe_result;
+ }
+ return key_;
+ }
+
+ Name* GetKey() { return key_; }
+ Object* GetValue() { return value_; }
+ PropertyDetails GetDetails() { return details_; }
+
+#ifdef OBJECT_PRINT
+ void Print(FILE* out);
+#endif
+
+ void SetSortedKeyIndex(int index) { details_ = details_.set_pointer(index); }
+
+ private:
+ Name* key_;
+ Object* value_;
+ PropertyDetails details_;
+
+ protected:
+ Descriptor() : details_(Smi::FromInt(0)) {}
+
+ void Init(Name* key, Object* value, PropertyDetails details) {
+ key_ = key;
+ value_ = value;
+ details_ = details;
+ }
+
+ Descriptor(Name* key, Object* value, PropertyDetails details)
+ : key_(key),
+ value_(value),
+ details_(details) { }
+
+ Descriptor(Name* key,
+ Object* value,
+ PropertyAttributes attributes,
+ PropertyType type,
+ Representation representation,
+ int field_index = 0)
+ : key_(key),
+ value_(value),
+ details_(attributes, type, representation, field_index) { }
+
+ friend class DescriptorArray;
+};
+
+
+class FieldDescriptor: public Descriptor {
+ public:
+ FieldDescriptor(Name* key,
+ int field_index,
+ PropertyAttributes attributes,
+ Representation representation)
+ : Descriptor(key, Smi::FromInt(0), attributes,
+ FIELD, representation, field_index) {}
+};
+
+
+class ConstantDescriptor: public Descriptor {
+ public:
+ ConstantDescriptor(Name* key,
+ Object* value,
+ PropertyAttributes attributes)
+ : Descriptor(key, value, attributes, CONSTANT,
+ value->OptimalRepresentation()) {}
+};
+
+
+class CallbacksDescriptor: public Descriptor {
+ public:
+ CallbacksDescriptor(Name* key,
+ Object* foreign,
+ PropertyAttributes attributes)
+ : Descriptor(key, foreign, attributes, CALLBACKS,
+ Representation::Tagged()) {}
+};
+
+
+// Holds a property index value distinguishing if it is a field index or an
+// index inside the object header.
+class PropertyIndex {
+ public:
+ static PropertyIndex NewFieldIndex(int index) {
+ return PropertyIndex(index, false);
+ }
+ static PropertyIndex NewHeaderIndex(int index) {
+ return PropertyIndex(index, true);
+ }
+
+ bool is_field_index() { return (index_ & kHeaderIndexBit) == 0; }
+ bool is_header_index() { return (index_ & kHeaderIndexBit) != 0; }
+
+ int field_index() {
+ ASSERT(is_field_index());
+ return value();
+ }
+ int header_index() {
+ ASSERT(is_header_index());
+ return value();
+ }
+
+ bool is_inobject(Handle<JSObject> holder) {
+ if (is_header_index()) return true;
+ return field_index() < holder->map()->inobject_properties();
+ }
+
+ int translate(Handle<JSObject> holder) {
+ if (is_header_index()) return header_index();
+ int index = field_index() - holder->map()->inobject_properties();
+ if (index >= 0) return index;
+ return index + holder->map()->instance_size() / kPointerSize;
+ }
+
+ private:
+ static const int kHeaderIndexBit = 1 << 31;
+ static const int kIndexMask = ~kHeaderIndexBit;
+
+ int value() { return index_ & kIndexMask; }
+
+ PropertyIndex(int index, bool is_header_based)
+ : index_(index | (is_header_based ? kHeaderIndexBit : 0)) {
+ ASSERT(index <= kIndexMask);
+ }
+
+ int index_;
+};
+
+
+class LookupResult BASE_EMBEDDED {
+ public:
+ explicit LookupResult(Isolate* isolate)
+ : isolate_(isolate),
+ next_(isolate->top_lookup_result()),
+ lookup_type_(NOT_FOUND),
+ holder_(NULL),
+ cacheable_(true),
+ details_(NONE, NONEXISTENT, Representation::None()) {
+ isolate->SetTopLookupResult(this);
+ }
+
+ ~LookupResult() {
+ ASSERT(isolate()->top_lookup_result() == this);
+ isolate()->SetTopLookupResult(next_);
+ }
+
+ Isolate* isolate() const { return isolate_; }
+
+ void DescriptorResult(JSObject* holder, PropertyDetails details, int number) {
+ lookup_type_ = DESCRIPTOR_TYPE;
+ holder_ = holder;
+ details_ = details;
+ number_ = number;
+ }
+
+ bool CanHoldValue(Handle<Object> value) {
+ if (IsNormal()) return true;
+ ASSERT(!IsTransition());
+ return value->FitsRepresentation(details_.representation());
+ }
+
+ void TransitionResult(JSObject* holder, int number) {
+ lookup_type_ = TRANSITION_TYPE;
+ details_ = PropertyDetails(NONE, TRANSITION, Representation::None());
+ holder_ = holder;
+ number_ = number;
+ }
+
+ void DictionaryResult(JSObject* holder, int entry) {
+ lookup_type_ = DICTIONARY_TYPE;
+ holder_ = holder;
+ details_ = holder->property_dictionary()->DetailsAt(entry);
+ number_ = entry;
+ }
+
+ void HandlerResult(JSProxy* proxy) {
+ lookup_type_ = HANDLER_TYPE;
+ holder_ = proxy;
+ details_ = PropertyDetails(NONE, HANDLER, Representation::None());
+ cacheable_ = false;
+ }
+
+ void InterceptorResult(JSObject* holder) {
+ lookup_type_ = INTERCEPTOR_TYPE;
+ holder_ = holder;
+ details_ = PropertyDetails(NONE, INTERCEPTOR, Representation::None());
+ }
+
+ void NotFound() {
+ lookup_type_ = NOT_FOUND;
+ details_ = PropertyDetails(NONE, NONEXISTENT, Representation::None());
+ holder_ = NULL;
+ }
+
+ JSObject* holder() {
+ ASSERT(IsFound());
+ return JSObject::cast(holder_);
+ }
+
+ JSProxy* proxy() {
+ ASSERT(IsFound());
+ return JSProxy::cast(holder_);
+ }
+
+ PropertyType type() {
+ ASSERT(IsFound());
+ return details_.type();
+ }
+
+ Representation representation() {
+ ASSERT(IsFound());
+ ASSERT(!IsTransition());
+ ASSERT(details_.type() != NONEXISTENT);
+ return details_.representation();
+ }
+
+ PropertyAttributes GetAttributes() {
+ ASSERT(!IsTransition());
+ ASSERT(IsFound());
+ ASSERT(details_.type() != NONEXISTENT);
+ return details_.attributes();
+ }
+
+ PropertyDetails GetPropertyDetails() {
+ ASSERT(!IsTransition());
+ return details_;
+ }
+
+ bool IsFastPropertyType() {
+ ASSERT(IsFound());
+ return IsTransition() || type() != NORMAL;
+ }
+
+ // Property callbacks does not include transitions to callbacks.
+ bool IsPropertyCallbacks() {
+ ASSERT(!(details_.type() == CALLBACKS && !IsFound()));
+ return details_.type() == CALLBACKS;
+ }
+
+ bool IsReadOnly() {
+ ASSERT(IsFound());
+ ASSERT(!IsTransition());
+ ASSERT(details_.type() != NONEXISTENT);
+ return details_.IsReadOnly();
+ }
+
+ bool IsField() {
+ ASSERT(!(details_.type() == FIELD && !IsFound()));
+ return details_.type() == FIELD;
+ }
+
+ bool IsNormal() {
+ ASSERT(!(details_.type() == NORMAL && !IsFound()));
+ return details_.type() == NORMAL;
+ }
+
+ bool IsConstant() {
+ ASSERT(!(details_.type() == CONSTANT && !IsFound()));
+ return details_.type() == CONSTANT;
+ }
+
+ bool IsConstantFunction() {
+ return IsConstant() && GetValue()->IsJSFunction();
+ }
+
+ bool IsDontDelete() { return details_.IsDontDelete(); }
+ bool IsDontEnum() { return details_.IsDontEnum(); }
+ bool IsFound() { return lookup_type_ != NOT_FOUND; }
+ bool IsTransition() { return lookup_type_ == TRANSITION_TYPE; }
+ bool IsHandler() { return lookup_type_ == HANDLER_TYPE; }
+ bool IsInterceptor() { return lookup_type_ == INTERCEPTOR_TYPE; }
+
+ // Is the result is a property excluding transitions and the null descriptor?
+ bool IsProperty() {
+ return IsFound() && !IsTransition();
+ }
+
+ bool IsDataProperty() {
+ switch (type()) {
+ case FIELD:
+ case NORMAL:
+ case CONSTANT:
+ return true;
+ case CALLBACKS: {
+ Object* callback = GetCallbackObject();
+ return callback->IsAccessorInfo() || callback->IsForeign();
+ }
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ return false;
+ }
+ UNREACHABLE();
+ return false;
+ }
+
+ bool IsCacheable() { return cacheable_; }
+ void DisallowCaching() { cacheable_ = false; }
+
+ Object* GetLazyValue() {
+ switch (type()) {
+ case FIELD:
+ return holder()->RawFastPropertyAt(GetFieldIndex().field_index());
+ case NORMAL: {
+ Object* value;
+ value = holder()->property_dictionary()->ValueAt(GetDictionaryEntry());
+ if (holder()->IsGlobalObject()) {
+ value = PropertyCell::cast(value)->value();
+ }
+ return value;
+ }
+ case CONSTANT:
+ return GetConstant();
+ case CALLBACKS:
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ case NONEXISTENT:
+ return isolate()->heap()->the_hole_value();
+ }
+ UNREACHABLE();
+ return NULL;
+ }
+
+ Map* GetTransitionTarget(Map* map) {
+ ASSERT(IsTransition());
+ TransitionArray* transitions = map->transitions();
+ return transitions->GetTarget(number_);
+ }
+
+ Map* GetTransitionTarget() {
+ return GetTransitionTarget(holder()->map());
+ }
+
+ PropertyDetails GetTransitionDetails(Map* map) {
+ ASSERT(IsTransition());
+ TransitionArray* transitions = map->transitions();
+ return transitions->GetTargetDetails(number_);
+ }
+
+ PropertyDetails GetTransitionDetails() {
+ return GetTransitionDetails(holder()->map());
+ }
+
+ bool IsTransitionToField(Map* map) {
+ return IsTransition() && GetTransitionDetails(map).type() == FIELD;
+ }
+
+ bool IsTransitionToConstant(Map* map) {
+ return IsTransition() && GetTransitionDetails(map).type() == CONSTANT;
+ }
+
+ Map* GetTransitionMap() {
+ ASSERT(IsTransition());
+ return Map::cast(GetValue());
+ }
+
+ Map* GetTransitionMapFromMap(Map* map) {
+ ASSERT(IsTransition());
+ return map->transitions()->GetTarget(number_);
+ }
+
+ int GetTransitionIndex() {
+ ASSERT(IsTransition());
+ return number_;
+ }
+
+ int GetDescriptorIndex() {
+ ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
+ return number_;
+ }
+
+ PropertyIndex GetFieldIndex() {
+ ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
+ ASSERT(IsField());
+ return PropertyIndex::NewFieldIndex(GetFieldIndexFromMap(holder()->map()));
+ }
+
+ int GetLocalFieldIndexFromMap(Map* map) {
+ ASSERT(IsField());
+ return GetFieldIndexFromMap(map) - map->inobject_properties();
+ }
+
+ int GetDictionaryEntry() {
+ ASSERT(lookup_type_ == DICTIONARY_TYPE);
+ return number_;
+ }
+
+ JSFunction* GetConstantFunction() {
+ ASSERT(type() == CONSTANT);
+ return JSFunction::cast(GetValue());
+ }
+
+ Object* GetConstantFromMap(Map* map) {
+ ASSERT(type() == CONSTANT);
+ return GetValueFromMap(map);
+ }
+
+ JSFunction* GetConstantFunctionFromMap(Map* map) {
+ return JSFunction::cast(GetConstantFromMap(map));
+ }
+
+ Object* GetConstant() {
+ ASSERT(type() == CONSTANT);
+ return GetValue();
+ }
+
+ Object* GetCallbackObject() {
+ ASSERT(type() == CALLBACKS && !IsTransition());
+ return GetValue();
+ }
+
+#ifdef OBJECT_PRINT
+ void Print(FILE* out);
+#endif
+
+ Object* GetValue() {
+ if (lookup_type_ == DESCRIPTOR_TYPE) {
+ return GetValueFromMap(holder()->map());
+ }
+ // In the dictionary case, the data is held in the value field.
+ ASSERT(lookup_type_ == DICTIONARY_TYPE);
+ return holder()->GetNormalizedProperty(this);
+ }
+
+ Object* GetValueFromMap(Map* map) const {
+ ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
+ ASSERT(number_ < map->NumberOfOwnDescriptors());
+ return map->instance_descriptors()->GetValue(number_);
+ }
+
+ int GetFieldIndexFromMap(Map* map) const {
+ ASSERT(lookup_type_ == DESCRIPTOR_TYPE);
+ ASSERT(number_ < map->NumberOfOwnDescriptors());
+ return map->instance_descriptors()->GetFieldIndex(number_);
+ }
+
+ void Iterate(ObjectVisitor* visitor);
+
+ private:
+ Isolate* isolate_;
+ LookupResult* next_;
+
+ // Where did we find the result;
+ enum {
+ NOT_FOUND,
+ DESCRIPTOR_TYPE,
+ TRANSITION_TYPE,
+ DICTIONARY_TYPE,
+ HANDLER_TYPE,
+ INTERCEPTOR_TYPE
+ } lookup_type_;
+
+ JSReceiver* holder_;
+ int number_;
+ bool cacheable_;
+ PropertyDetails details_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_PROPERTY_H_
diff --git a/chromium/v8/src/proxy.js b/chromium/v8/src/proxy.js
new file mode 100644
index 00000000000..de9be50ddca
--- /dev/null
+++ b/chromium/v8/src/proxy.js
@@ -0,0 +1,213 @@
+// 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.
+
+"use strict";
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js:
+// var $Object = global.Object;
+
+var $Proxy = new $Object();
+
+// -------------------------------------------------------------------
+
+function ProxyCreate(handler, proto) {
+ if (!IS_SPEC_OBJECT(handler))
+ throw MakeTypeError("handler_non_object", ["create"])
+ if (IS_UNDEFINED(proto))
+ proto = null
+ else if (!(IS_SPEC_OBJECT(proto) || proto === null))
+ throw MakeTypeError("proto_non_object", ["create"])
+ return %CreateJSProxy(handler, proto)
+}
+
+function ProxyCreateFunction(handler, callTrap, constructTrap) {
+ if (!IS_SPEC_OBJECT(handler))
+ throw MakeTypeError("handler_non_object", ["create"])
+ if (!IS_SPEC_FUNCTION(callTrap))
+ throw MakeTypeError("trap_function_expected", ["createFunction", "call"])
+ if (IS_UNDEFINED(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"])
+ }
+ return %CreateJSFunctionProxy(
+ handler, callTrap, constructTrap, $Function.prototype)
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpProxy() {
+ %CheckIsBootstrapping()
+
+ global.Proxy = $Proxy;
+
+ // Set up non-enumerable properties of the Proxy object.
+ InstallFunctions($Proxy, DONT_ENUM, [
+ "create", ProxyCreate,
+ "createFunction", ProxyCreateFunction
+ ])
+}
+
+SetUpProxy();
+
+
+// -------------------------------------------------------------------
+// Proxy Builtins
+
+function DerivedConstructTrap(callTrap) {
+ return function() {
+ var proto = this.prototype
+ if (!IS_SPEC_OBJECT(proto)) proto = $Object.prototype
+ var 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,
+ this, arguments, 0, %_ArgumentsLength())
+ }
+}
+
+function DerivedGetTrap(receiver, name) {
+ var desc = this.getPropertyDescriptor(name)
+ if (IS_UNDEFINED(desc)) { return desc }
+ if ('value' in desc) {
+ return desc.value
+ } else {
+ if (IS_UNDEFINED(desc.get)) { return desc.get }
+ // The proposal says: desc.get.call(receiver)
+ return %_CallFunction(receiver, desc.get)
+ }
+}
+
+function DerivedSetTrap(receiver, name, val) {
+ var desc = this.getOwnPropertyDescriptor(name)
+ if (desc) {
+ if ('writable' in desc) {
+ if (desc.writable) {
+ desc.value = val
+ this.defineProperty(name, desc)
+ return true
+ } else {
+ return false
+ }
+ } else { // accessor
+ if (desc.set) {
+ // The proposal says: desc.set.call(receiver, val)
+ %_CallFunction(receiver, val, desc.set)
+ return true
+ } else {
+ return false
+ }
+ }
+ }
+ desc = this.getPropertyDescriptor(name)
+ if (desc) {
+ if ('writable' in desc) {
+ if (desc.writable) {
+ // fall through
+ } else {
+ return false
+ }
+ } else { // accessor
+ if (desc.set) {
+ // The proposal says: desc.set.call(receiver, val)
+ %_CallFunction(receiver, val, desc.set)
+ return true
+ } else {
+ return false
+ }
+ }
+ }
+ this.defineProperty(name, {
+ value: val,
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ return true;
+}
+
+function DerivedHasTrap(name) {
+ return !!this.getPropertyDescriptor(name)
+}
+
+function DerivedHasOwnTrap(name) {
+ return !!this.getOwnPropertyDescriptor(name)
+}
+
+function DerivedKeysTrap() {
+ var names = this.getOwnPropertyNames()
+ var enumerableNames = []
+ for (var i = 0, count = 0; i < names.length; ++i) {
+ var name = names[i]
+ if (IS_SYMBOL(name)) continue
+ 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]
+ if (IS_SYMBOL(name)) continue
+ var desc = this.getPropertyDescriptor(TO_STRING_INLINE(name))
+ if (!IS_UNDEFINED(desc)) {
+ if (!desc.configurable) {
+ throw MakeTypeError("proxy_prop_not_configurable",
+ [this, "getPropertyDescriptor", name])
+ }
+ if (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 ToNameArray(handler.enumerate(), "enumerate", false)
+ }
+}
diff --git a/chromium/v8/src/regexp-macro-assembler-irregexp-inl.h b/chromium/v8/src/regexp-macro-assembler-irregexp-inl.h
new file mode 100644
index 00000000000..a767ec0089f
--- /dev/null
+++ b/chromium/v8/src/regexp-macro-assembler-irregexp-inl.h
@@ -0,0 +1,88 @@
+// Copyright 2008-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.
+
+// A light-weight assembler for the Irregexp byte code.
+
+
+#include "v8.h"
+#include "ast.h"
+#include "bytecodes-irregexp.h"
+
+#ifndef V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_INL_H_
+#define V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_INL_H_
+
+namespace v8 {
+namespace internal {
+
+#ifdef V8_INTERPRETED_REGEXP
+
+void RegExpMacroAssemblerIrregexp::Emit(uint32_t byte,
+ uint32_t twenty_four_bits) {
+ uint32_t word = ((twenty_four_bits << BYTECODE_SHIFT) | byte);
+ ASSERT(pc_ <= buffer_.length());
+ if (pc_ + 3 >= buffer_.length()) {
+ Expand();
+ }
+ *reinterpret_cast<uint32_t*>(buffer_.start() + pc_) = word;
+ pc_ += 4;
+}
+
+
+void RegExpMacroAssemblerIrregexp::Emit16(uint32_t word) {
+ ASSERT(pc_ <= buffer_.length());
+ if (pc_ + 1 >= buffer_.length()) {
+ Expand();
+ }
+ *reinterpret_cast<uint16_t*>(buffer_.start() + pc_) = word;
+ pc_ += 2;
+}
+
+
+void RegExpMacroAssemblerIrregexp::Emit8(uint32_t word) {
+ ASSERT(pc_ <= buffer_.length());
+ if (pc_ == buffer_.length()) {
+ Expand();
+ }
+ *reinterpret_cast<unsigned char*>(buffer_.start() + pc_) = word;
+ pc_ += 1;
+}
+
+
+void RegExpMacroAssemblerIrregexp::Emit32(uint32_t word) {
+ ASSERT(pc_ <= buffer_.length());
+ if (pc_ + 3 >= buffer_.length()) {
+ Expand();
+ }
+ *reinterpret_cast<uint32_t*>(buffer_.start() + pc_) = word;
+ pc_ += 4;
+}
+
+#endif // V8_INTERPRETED_REGEXP
+
+} } // namespace v8::internal
+
+#endif // V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_INL_H_
diff --git a/chromium/v8/src/regexp-macro-assembler-irregexp.cc b/chromium/v8/src/regexp-macro-assembler-irregexp.cc
new file mode 100644
index 00000000000..3b9a2f66033
--- /dev/null
+++ b/chromium/v8/src/regexp-macro-assembler-irregexp.cc
@@ -0,0 +1,477 @@
+// Copyright 2008-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.
+
+#include "v8.h"
+#include "ast.h"
+#include "bytecodes-irregexp.h"
+#include "regexp-macro-assembler.h"
+#include "regexp-macro-assembler-irregexp.h"
+#include "regexp-macro-assembler-irregexp-inl.h"
+
+
+namespace v8 {
+namespace internal {
+
+#ifdef V8_INTERPRETED_REGEXP
+
+RegExpMacroAssemblerIrregexp::RegExpMacroAssemblerIrregexp(Vector<byte> buffer,
+ Zone* zone)
+ : RegExpMacroAssembler(zone),
+ buffer_(buffer),
+ pc_(0),
+ own_buffer_(false),
+ advance_current_end_(kInvalidPC),
+ isolate_(zone->isolate()) { }
+
+
+RegExpMacroAssemblerIrregexp::~RegExpMacroAssemblerIrregexp() {
+ if (backtrack_.is_linked()) backtrack_.Unuse();
+ if (own_buffer_) buffer_.Dispose();
+}
+
+
+RegExpMacroAssemblerIrregexp::IrregexpImplementation
+RegExpMacroAssemblerIrregexp::Implementation() {
+ return kBytecodeImplementation;
+}
+
+
+void RegExpMacroAssemblerIrregexp::Bind(Label* l) {
+ advance_current_end_ = kInvalidPC;
+ ASSERT(!l->is_bound());
+ if (l->is_linked()) {
+ int pos = l->pos();
+ while (pos != 0) {
+ int fixup = pos;
+ pos = *reinterpret_cast<int32_t*>(buffer_.start() + fixup);
+ *reinterpret_cast<uint32_t*>(buffer_.start() + fixup) = pc_;
+ }
+ }
+ l->bind_to(pc_);
+}
+
+
+void RegExpMacroAssemblerIrregexp::EmitOrLink(Label* l) {
+ if (l == NULL) l = &backtrack_;
+ if (l->is_bound()) {
+ Emit32(l->pos());
+ } else {
+ int pos = 0;
+ if (l->is_linked()) {
+ pos = l->pos();
+ }
+ l->link_to(pc_);
+ Emit32(pos);
+ }
+}
+
+
+void RegExpMacroAssemblerIrregexp::PopRegister(int register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_POP_REGISTER, register_index);
+}
+
+
+void RegExpMacroAssemblerIrregexp::PushRegister(
+ int register_index,
+ StackCheckFlag check_stack_limit) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_PUSH_REGISTER, register_index);
+}
+
+
+void RegExpMacroAssemblerIrregexp::WriteCurrentPositionToRegister(
+ int register_index, int cp_offset) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER_TO_CP, register_index);
+ Emit32(cp_offset); // Current position offset.
+}
+
+
+void RegExpMacroAssemblerIrregexp::ClearRegisters(int reg_from, int reg_to) {
+ ASSERT(reg_from <= reg_to);
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ SetRegister(reg, -1);
+ }
+}
+
+
+void RegExpMacroAssemblerIrregexp::ReadCurrentPositionFromRegister(
+ int register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_CP_TO_REGISTER, register_index);
+}
+
+
+void RegExpMacroAssemblerIrregexp::WriteStackPointerToRegister(
+ int register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER_TO_SP, register_index);
+}
+
+
+void RegExpMacroAssemblerIrregexp::ReadStackPointerFromRegister(
+ int register_index) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_SP_TO_REGISTER, register_index);
+}
+
+
+void RegExpMacroAssemblerIrregexp::SetCurrentPositionFromEnd(int by) {
+ ASSERT(is_uint24(by));
+ Emit(BC_SET_CURRENT_POSITION_FROM_END, by);
+}
+
+
+void RegExpMacroAssemblerIrregexp::SetRegister(int register_index, int to) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER, register_index);
+ Emit32(to);
+}
+
+
+void RegExpMacroAssemblerIrregexp::AdvanceRegister(int register_index, int by) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_ADVANCE_REGISTER, register_index);
+ Emit32(by);
+}
+
+
+void RegExpMacroAssemblerIrregexp::PopCurrentPosition() {
+ Emit(BC_POP_CP, 0);
+}
+
+
+void RegExpMacroAssemblerIrregexp::PushCurrentPosition() {
+ Emit(BC_PUSH_CP, 0);
+}
+
+
+void RegExpMacroAssemblerIrregexp::Backtrack() {
+ Emit(BC_POP_BT, 0);
+}
+
+
+void RegExpMacroAssemblerIrregexp::GoTo(Label* l) {
+ if (advance_current_end_ == pc_) {
+ // Combine advance current and goto.
+ pc_ = advance_current_start_;
+ Emit(BC_ADVANCE_CP_AND_GOTO, advance_current_offset_);
+ EmitOrLink(l);
+ advance_current_end_ = kInvalidPC;
+ } else {
+ // Regular goto.
+ Emit(BC_GOTO, 0);
+ EmitOrLink(l);
+ }
+}
+
+
+void RegExpMacroAssemblerIrregexp::PushBacktrack(Label* l) {
+ Emit(BC_PUSH_BT, 0);
+ EmitOrLink(l);
+}
+
+
+bool RegExpMacroAssemblerIrregexp::Succeed() {
+ Emit(BC_SUCCEED, 0);
+ return false; // Restart matching for global regexp not supported.
+}
+
+
+void RegExpMacroAssemblerIrregexp::Fail() {
+ Emit(BC_FAIL, 0);
+}
+
+
+void RegExpMacroAssemblerIrregexp::AdvanceCurrentPosition(int by) {
+ ASSERT(by >= kMinCPOffset);
+ ASSERT(by <= kMaxCPOffset);
+ advance_current_start_ = pc_;
+ advance_current_offset_ = by;
+ Emit(BC_ADVANCE_CP, by);
+ advance_current_end_ = pc_;
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckGreedyLoop(
+ Label* on_tos_equals_current_position) {
+ Emit(BC_CHECK_GREEDY, 0);
+ EmitOrLink(on_tos_equals_current_position);
+}
+
+
+void RegExpMacroAssemblerIrregexp::LoadCurrentCharacter(int cp_offset,
+ Label* on_failure,
+ bool check_bounds,
+ int characters) {
+ ASSERT(cp_offset >= kMinCPOffset);
+ ASSERT(cp_offset <= kMaxCPOffset);
+ int bytecode;
+ if (check_bounds) {
+ if (characters == 4) {
+ bytecode = BC_LOAD_4_CURRENT_CHARS;
+ } else if (characters == 2) {
+ bytecode = BC_LOAD_2_CURRENT_CHARS;
+ } else {
+ ASSERT(characters == 1);
+ bytecode = BC_LOAD_CURRENT_CHAR;
+ }
+ } else {
+ if (characters == 4) {
+ bytecode = BC_LOAD_4_CURRENT_CHARS_UNCHECKED;
+ } else if (characters == 2) {
+ bytecode = BC_LOAD_2_CURRENT_CHARS_UNCHECKED;
+ } else {
+ ASSERT(characters == 1);
+ bytecode = BC_LOAD_CURRENT_CHAR_UNCHECKED;
+ }
+ }
+ Emit(bytecode, cp_offset);
+ if (check_bounds) EmitOrLink(on_failure);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckCharacterLT(uc16 limit,
+ Label* on_less) {
+ Emit(BC_CHECK_LT, limit);
+ EmitOrLink(on_less);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckCharacterGT(uc16 limit,
+ Label* on_greater) {
+ Emit(BC_CHECK_GT, limit);
+ EmitOrLink(on_greater);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckCharacter(uint32_t c, Label* on_equal) {
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_CHECK_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_CHECK_CHAR, c);
+ }
+ EmitOrLink(on_equal);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckAtStart(Label* on_at_start) {
+ Emit(BC_CHECK_AT_START, 0);
+ EmitOrLink(on_at_start);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckNotAtStart(Label* on_not_at_start) {
+ Emit(BC_CHECK_NOT_AT_START, 0);
+ EmitOrLink(on_not_at_start);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckNotCharacter(uint32_t c,
+ Label* on_not_equal) {
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_CHECK_NOT_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_CHECK_NOT_CHAR, c);
+ }
+ EmitOrLink(on_not_equal);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckCharacterAfterAnd(
+ uint32_t c,
+ uint32_t mask,
+ Label* on_equal) {
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_AND_CHECK_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_AND_CHECK_CHAR, c);
+ }
+ Emit32(mask);
+ EmitOrLink(on_equal);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckNotCharacterAfterAnd(
+ uint32_t c,
+ uint32_t mask,
+ Label* on_not_equal) {
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_AND_CHECK_NOT_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_AND_CHECK_NOT_CHAR, c);
+ }
+ Emit32(mask);
+ EmitOrLink(on_not_equal);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckNotCharacterAfterMinusAnd(
+ uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal) {
+ Emit(BC_MINUS_AND_CHECK_NOT_CHAR, c);
+ Emit16(minus);
+ Emit16(mask);
+ EmitOrLink(on_not_equal);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckCharacterInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_in_range) {
+ Emit(BC_CHECK_CHAR_IN_RANGE, 0);
+ Emit16(from);
+ Emit16(to);
+ EmitOrLink(on_in_range);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckCharacterNotInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_not_in_range) {
+ Emit(BC_CHECK_CHAR_NOT_IN_RANGE, 0);
+ Emit16(from);
+ Emit16(to);
+ EmitOrLink(on_not_in_range);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckBitInTable(
+ Handle<ByteArray> table, Label* on_bit_set) {
+ Emit(BC_CHECK_BIT_IN_TABLE, 0);
+ EmitOrLink(on_bit_set);
+ for (int i = 0; i < kTableSize; i += kBitsPerByte) {
+ int byte = 0;
+ for (int j = 0; j < kBitsPerByte; j++) {
+ if (table->get(i + j) != 0) byte |= 1 << j;
+ }
+ Emit8(byte);
+ }
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckNotBackReference(int start_reg,
+ Label* on_not_equal) {
+ ASSERT(start_reg >= 0);
+ ASSERT(start_reg <= kMaxRegister);
+ Emit(BC_CHECK_NOT_BACK_REF, start_reg);
+ EmitOrLink(on_not_equal);
+}
+
+
+void RegExpMacroAssemblerIrregexp::CheckNotBackReferenceIgnoreCase(
+ int start_reg,
+ Label* on_not_equal) {
+ ASSERT(start_reg >= 0);
+ ASSERT(start_reg <= kMaxRegister);
+ Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
+ EmitOrLink(on_not_equal);
+}
+
+
+void RegExpMacroAssemblerIrregexp::IfRegisterLT(int register_index,
+ int comparand,
+ Label* on_less_than) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_LT, register_index);
+ Emit32(comparand);
+ EmitOrLink(on_less_than);
+}
+
+
+void RegExpMacroAssemblerIrregexp::IfRegisterGE(int register_index,
+ int comparand,
+ Label* on_greater_or_equal) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_GE, register_index);
+ Emit32(comparand);
+ EmitOrLink(on_greater_or_equal);
+}
+
+
+void RegExpMacroAssemblerIrregexp::IfRegisterEqPos(int register_index,
+ Label* on_eq) {
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_EQ_POS, register_index);
+ EmitOrLink(on_eq);
+}
+
+
+Handle<HeapObject> RegExpMacroAssemblerIrregexp::GetCode(
+ Handle<String> source) {
+ Bind(&backtrack_);
+ Emit(BC_POP_BT, 0);
+ Handle<ByteArray> array = isolate_->factory()->NewByteArray(length());
+ Copy(array->GetDataStartAddress());
+ return array;
+}
+
+
+int RegExpMacroAssemblerIrregexp::length() {
+ return pc_;
+}
+
+
+void RegExpMacroAssemblerIrregexp::Copy(Address a) {
+ OS::MemCopy(a, buffer_.start(), length());
+}
+
+
+void RegExpMacroAssemblerIrregexp::Expand() {
+ bool old_buffer_was_our_own = own_buffer_;
+ Vector<byte> old_buffer = buffer_;
+ buffer_ = Vector<byte>::New(old_buffer.length() * 2);
+ own_buffer_ = true;
+ OS::MemCopy(buffer_.start(), old_buffer.start(), old_buffer.length());
+ if (old_buffer_was_our_own) {
+ old_buffer.Dispose();
+ }
+}
+
+#endif // V8_INTERPRETED_REGEXP
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/regexp-macro-assembler-irregexp.h b/chromium/v8/src/regexp-macro-assembler-irregexp.h
new file mode 100644
index 00000000000..f8a412d4f86
--- /dev/null
+++ b/chromium/v8/src/regexp-macro-assembler-irregexp.h
@@ -0,0 +1,148 @@
+// Copyright 2012 the V8 project authors. 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_REGEXP_MACRO_ASSEMBLER_IRREGEXP_H_
+#define V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_H_
+
+namespace v8 {
+namespace internal {
+
+#ifdef V8_INTERPRETED_REGEXP
+
+class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler {
+ public:
+ // Create an assembler. Instructions and relocation information are emitted
+ // into a buffer, with the instructions starting from the beginning and the
+ // relocation information starting from the end of the buffer. See CodeDesc
+ // for a detailed comment on the layout (globals.h).
+ //
+ // If the provided buffer is NULL, the assembler allocates and grows its own
+ // buffer, and buffer_size determines the initial buffer size. The buffer is
+ // owned by the assembler and deallocated upon destruction of the assembler.
+ //
+ // If the provided buffer is not NULL, the assembler uses the provided buffer
+ // for code generation and assumes its size to be buffer_size. If the buffer
+ // is too small, a fatal error occurs. No deallocation of the buffer is done
+ // upon destruction of the assembler.
+ RegExpMacroAssemblerIrregexp(Vector<byte>, Zone* zone);
+ virtual ~RegExpMacroAssemblerIrregexp();
+ // The byte-code interpreter checks on each push anyway.
+ virtual int stack_limit_slack() { return 1; }
+ virtual void Bind(Label* label);
+ virtual void AdvanceCurrentPosition(int by); // Signed cp change.
+ virtual void PopCurrentPosition();
+ virtual void PushCurrentPosition();
+ virtual void Backtrack();
+ virtual void GoTo(Label* label);
+ virtual void PushBacktrack(Label* label);
+ virtual bool Succeed();
+ virtual void Fail();
+ virtual void PopRegister(int register_index);
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
+ virtual void AdvanceRegister(int reg, int by); // r[reg] += by.
+ virtual void SetCurrentPositionFromEnd(int by);
+ virtual void SetRegister(int register_index, int to);
+ virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+ virtual void ClearRegisters(int reg_from, int reg_to);
+ virtual void ReadCurrentPositionFromRegister(int reg);
+ virtual void WriteStackPointerToRegister(int reg);
+ virtual void ReadStackPointerFromRegister(int reg);
+ virtual void LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds = true,
+ int characters = 1);
+ virtual void CheckCharacter(unsigned c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned mask,
+ Label* on_equal);
+ virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
+ virtual void CheckCharacterLT(uc16 limit, Label* on_less);
+ virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
+ virtual void CheckAtStart(Label* on_at_start);
+ virtual void CheckNotAtStart(Label* on_not_at_start);
+ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned mask,
+ Label* on_not_equal);
+ virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal);
+ virtual void CheckCharacterInRange(uc16 from,
+ uc16 to,
+ Label* on_in_range);
+ virtual void CheckCharacterNotInRange(uc16 from,
+ uc16 to,
+ Label* on_not_in_range);
+ virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
+ virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
+ virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
+ Label* on_no_match);
+ virtual void IfRegisterLT(int register_index, int comparand, Label* if_lt);
+ virtual void IfRegisterGE(int register_index, int comparand, Label* if_ge);
+ virtual void IfRegisterEqPos(int register_index, Label* if_eq);
+
+ virtual IrregexpImplementation Implementation();
+ virtual Handle<HeapObject> GetCode(Handle<String> source);
+
+ private:
+ void Expand();
+ // Code and bitmap emission.
+ inline void EmitOrLink(Label* label);
+ inline void Emit32(uint32_t x);
+ inline void Emit16(uint32_t x);
+ inline void Emit8(uint32_t x);
+ inline void Emit(uint32_t bc, uint32_t arg);
+ // Bytecode buffer.
+ int length();
+ void Copy(Address a);
+
+ // The buffer into which code and relocation info are generated.
+ Vector<byte> buffer_;
+ // The program counter.
+ int pc_;
+ // True if the assembler owns the buffer, false if buffer is external.
+ bool own_buffer_;
+ Label backtrack_;
+
+ int advance_current_start_;
+ int advance_current_offset_;
+ int advance_current_end_;
+
+ Isolate* isolate_;
+
+ static const int kInvalidPC = -1;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(RegExpMacroAssemblerIrregexp);
+};
+
+#endif // V8_INTERPRETED_REGEXP
+
+} } // namespace v8::internal
+
+#endif // V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_H_
diff --git a/chromium/v8/src/regexp-macro-assembler-tracer.cc b/chromium/v8/src/regexp-macro-assembler-tracer.cc
new file mode 100644
index 00000000000..1ce1fa4b24a
--- /dev/null
+++ b/chromium/v8/src/regexp-macro-assembler-tracer.cc
@@ -0,0 +1,434 @@
+// Copyright 2012 the V8 project authors. 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 "ast.h"
+#include "regexp-macro-assembler.h"
+#include "regexp-macro-assembler-tracer.h"
+
+namespace v8 {
+namespace internal {
+
+RegExpMacroAssemblerTracer::RegExpMacroAssemblerTracer(
+ RegExpMacroAssembler* assembler) :
+ RegExpMacroAssembler(assembler->zone()),
+ assembler_(assembler) {
+ unsigned int type = assembler->Implementation();
+ ASSERT(type < 5);
+ const char* impl_names[] = {"IA32", "ARM", "MIPS", "X64", "Bytecode"};
+ PrintF("RegExpMacroAssembler%s();\n", impl_names[type]);
+}
+
+
+RegExpMacroAssemblerTracer::~RegExpMacroAssemblerTracer() {
+}
+
+
+// This is used for printing out debugging information. It makes an integer
+// that is closely related to the address of an object.
+static int LabelToInt(Label* label) {
+ return static_cast<int>(reinterpret_cast<intptr_t>(label));
+}
+
+
+void RegExpMacroAssemblerTracer::Bind(Label* label) {
+ PrintF("label[%08x]: (Bind)\n", LabelToInt(label));
+ assembler_->Bind(label);
+}
+
+
+void RegExpMacroAssemblerTracer::AdvanceCurrentPosition(int by) {
+ PrintF(" AdvanceCurrentPosition(by=%d);\n", by);
+ assembler_->AdvanceCurrentPosition(by);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckGreedyLoop(Label* label) {
+ PrintF(" CheckGreedyLoop(label[%08x]);\n\n", LabelToInt(label));
+ assembler_->CheckGreedyLoop(label);
+}
+
+
+void RegExpMacroAssemblerTracer::PopCurrentPosition() {
+ PrintF(" PopCurrentPosition();\n");
+ assembler_->PopCurrentPosition();
+}
+
+
+void RegExpMacroAssemblerTracer::PushCurrentPosition() {
+ PrintF(" PushCurrentPosition();\n");
+ assembler_->PushCurrentPosition();
+}
+
+
+void RegExpMacroAssemblerTracer::Backtrack() {
+ PrintF(" Backtrack();\n");
+ assembler_->Backtrack();
+}
+
+
+void RegExpMacroAssemblerTracer::GoTo(Label* label) {
+ PrintF(" GoTo(label[%08x]);\n\n", LabelToInt(label));
+ assembler_->GoTo(label);
+}
+
+
+void RegExpMacroAssemblerTracer::PushBacktrack(Label* label) {
+ PrintF(" PushBacktrack(label[%08x]);\n", LabelToInt(label));
+ assembler_->PushBacktrack(label);
+}
+
+
+bool RegExpMacroAssemblerTracer::Succeed() {
+ bool restart = assembler_->Succeed();
+ PrintF(" Succeed();%s\n", restart ? " [restart for global match]" : "");
+ return restart;
+}
+
+
+void RegExpMacroAssemblerTracer::Fail() {
+ PrintF(" Fail();");
+ assembler_->Fail();
+}
+
+
+void RegExpMacroAssemblerTracer::PopRegister(int register_index) {
+ PrintF(" PopRegister(register=%d);\n", register_index);
+ assembler_->PopRegister(register_index);
+}
+
+
+void RegExpMacroAssemblerTracer::PushRegister(
+ int register_index,
+ StackCheckFlag check_stack_limit) {
+ PrintF(" PushRegister(register=%d, %s);\n",
+ register_index,
+ check_stack_limit ? "check stack limit" : "");
+ assembler_->PushRegister(register_index, check_stack_limit);
+}
+
+
+void RegExpMacroAssemblerTracer::AdvanceRegister(int reg, int by) {
+ PrintF(" AdvanceRegister(register=%d, by=%d);\n", reg, by);
+ assembler_->AdvanceRegister(reg, by);
+}
+
+
+void RegExpMacroAssemblerTracer::SetCurrentPositionFromEnd(int by) {
+ PrintF(" SetCurrentPositionFromEnd(by=%d);\n", by);
+ assembler_->SetCurrentPositionFromEnd(by);
+}
+
+
+void RegExpMacroAssemblerTracer::SetRegister(int register_index, int to) {
+ PrintF(" SetRegister(register=%d, to=%d);\n", register_index, to);
+ assembler_->SetRegister(register_index, to);
+}
+
+
+void RegExpMacroAssemblerTracer::WriteCurrentPositionToRegister(int reg,
+ int cp_offset) {
+ PrintF(" WriteCurrentPositionToRegister(register=%d,cp_offset=%d);\n",
+ reg,
+ cp_offset);
+ assembler_->WriteCurrentPositionToRegister(reg, cp_offset);
+}
+
+
+void RegExpMacroAssemblerTracer::ClearRegisters(int reg_from, int reg_to) {
+ PrintF(" ClearRegister(from=%d, to=%d);\n", reg_from, reg_to);
+ assembler_->ClearRegisters(reg_from, reg_to);
+}
+
+
+void RegExpMacroAssemblerTracer::ReadCurrentPositionFromRegister(int reg) {
+ PrintF(" ReadCurrentPositionFromRegister(register=%d);\n", reg);
+ assembler_->ReadCurrentPositionFromRegister(reg);
+}
+
+
+void RegExpMacroAssemblerTracer::WriteStackPointerToRegister(int reg) {
+ PrintF(" WriteStackPointerToRegister(register=%d);\n", reg);
+ assembler_->WriteStackPointerToRegister(reg);
+}
+
+
+void RegExpMacroAssemblerTracer::ReadStackPointerFromRegister(int reg) {
+ PrintF(" ReadStackPointerFromRegister(register=%d);\n", reg);
+ assembler_->ReadStackPointerFromRegister(reg);
+}
+
+
+void RegExpMacroAssemblerTracer::LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds,
+ int characters) {
+ const char* check_msg = check_bounds ? "" : " (unchecked)";
+ PrintF(" LoadCurrentCharacter(cp_offset=%d, label[%08x]%s (%d chars));\n",
+ cp_offset,
+ LabelToInt(on_end_of_input),
+ check_msg,
+ characters);
+ assembler_->LoadCurrentCharacter(cp_offset,
+ on_end_of_input,
+ check_bounds,
+ characters);
+}
+
+
+class PrintablePrinter {
+ public:
+ explicit PrintablePrinter(uc16 character) : character_(character) { }
+
+ const char* operator*() {
+ if (character_ >= ' ' && character_ <= '~') {
+ buffer_[0] = '(';
+ buffer_[1] = static_cast<char>(character_);
+ buffer_[2] = ')';
+ buffer_[3] = '\0';
+ } else {
+ buffer_[0] = '\0';
+ }
+ return &buffer_[0];
+ };
+
+ private:
+ uc16 character_;
+ char buffer_[4];
+};
+
+
+void RegExpMacroAssemblerTracer::CheckCharacterLT(uc16 limit, Label* on_less) {
+ PrintablePrinter printable(limit);
+ PrintF(" CheckCharacterLT(c=0x%04x%s, label[%08x]);\n",
+ limit,
+ *printable,
+ LabelToInt(on_less));
+ assembler_->CheckCharacterLT(limit, on_less);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckCharacterGT(uc16 limit,
+ Label* on_greater) {
+ PrintablePrinter printable(limit);
+ PrintF(" CheckCharacterGT(c=0x%04x%s, label[%08x]);\n",
+ limit,
+ *printable,
+ LabelToInt(on_greater));
+ assembler_->CheckCharacterGT(limit, on_greater);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckCharacter(unsigned c, Label* on_equal) {
+ PrintablePrinter printable(c);
+ PrintF(" CheckCharacter(c=0x%04x%s, label[%08x]);\n",
+ c,
+ *printable,
+ LabelToInt(on_equal));
+ assembler_->CheckCharacter(c, on_equal);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckAtStart(Label* on_at_start) {
+ PrintF(" CheckAtStart(label[%08x]);\n", LabelToInt(on_at_start));
+ assembler_->CheckAtStart(on_at_start);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckNotAtStart(Label* on_not_at_start) {
+ PrintF(" CheckNotAtStart(label[%08x]);\n", LabelToInt(on_not_at_start));
+ assembler_->CheckNotAtStart(on_not_at_start);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckNotCharacter(unsigned c,
+ Label* on_not_equal) {
+ PrintablePrinter printable(c);
+ PrintF(" CheckNotCharacter(c=0x%04x%s, label[%08x]);\n",
+ c,
+ *printable,
+ LabelToInt(on_not_equal));
+ assembler_->CheckNotCharacter(c, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckCharacterAfterAnd(
+ unsigned c,
+ unsigned mask,
+ Label* on_equal) {
+ PrintablePrinter printable(c);
+ PrintF(" CheckCharacterAfterAnd(c=0x%04x%s, mask=0x%04x, label[%08x]);\n",
+ c,
+ *printable,
+ mask,
+ LabelToInt(on_equal));
+ assembler_->CheckCharacterAfterAnd(c, mask, on_equal);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckNotCharacterAfterAnd(
+ unsigned c,
+ unsigned mask,
+ Label* on_not_equal) {
+ PrintablePrinter printable(c);
+ PrintF(" CheckNotCharacterAfterAnd(c=0x%04x%s, mask=0x%04x, label[%08x]);\n",
+ c,
+ *printable,
+ mask,
+ LabelToInt(on_not_equal));
+ assembler_->CheckNotCharacterAfterAnd(c, mask, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckNotCharacterAfterMinusAnd(
+ uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal) {
+ PrintF(" CheckNotCharacterAfterMinusAnd(c=0x%04x, minus=%04x, mask=0x%04x, "
+ "label[%08x]);\n",
+ c,
+ minus,
+ mask,
+ LabelToInt(on_not_equal));
+ assembler_->CheckNotCharacterAfterMinusAnd(c, minus, mask, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckCharacterInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_not_in_range) {
+ PrintablePrinter printable_from(from);
+ PrintablePrinter printable_to(to);
+ PrintF(" CheckCharacterInRange(from=0x%04x%s, to=0x%04x%s, label[%08x]);\n",
+ from,
+ *printable_from,
+ to,
+ *printable_to,
+ LabelToInt(on_not_in_range));
+ assembler_->CheckCharacterInRange(from, to, on_not_in_range);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckCharacterNotInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_in_range) {
+ PrintablePrinter printable_from(from);
+ PrintablePrinter printable_to(to);
+ PrintF(
+ " CheckCharacterNotInRange(from=0x%04x%s," " to=%04x%s, label[%08x]);\n",
+ from,
+ *printable_from,
+ to,
+ *printable_to,
+ LabelToInt(on_in_range));
+ assembler_->CheckCharacterNotInRange(from, to, on_in_range);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckBitInTable(
+ Handle<ByteArray> table, Label* on_bit_set) {
+ PrintF(" CheckBitInTable(label[%08x] ", LabelToInt(on_bit_set));
+ for (int i = 0; i < kTableSize; i++) {
+ PrintF("%c", table->get(i) != 0 ? 'X' : '.');
+ if (i % 32 == 31 && i != kTableMask) {
+ PrintF("\n ");
+ }
+ }
+ PrintF(");\n");
+ assembler_->CheckBitInTable(table, on_bit_set);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckNotBackReference(int start_reg,
+ Label* on_no_match) {
+ PrintF(" CheckNotBackReference(register=%d, label[%08x]);\n", start_reg,
+ LabelToInt(on_no_match));
+ assembler_->CheckNotBackReference(start_reg, on_no_match);
+}
+
+
+void RegExpMacroAssemblerTracer::CheckNotBackReferenceIgnoreCase(
+ int start_reg,
+ Label* on_no_match) {
+ PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, label[%08x]);\n",
+ start_reg, LabelToInt(on_no_match));
+ assembler_->CheckNotBackReferenceIgnoreCase(start_reg, on_no_match);
+}
+
+
+bool RegExpMacroAssemblerTracer::CheckSpecialCharacterClass(
+ uc16 type,
+ Label* on_no_match) {
+ bool supported = assembler_->CheckSpecialCharacterClass(type,
+ on_no_match);
+ PrintF(" CheckSpecialCharacterClass(type='%c', label[%08x]): %s;\n",
+ type,
+ LabelToInt(on_no_match),
+ supported ? "true" : "false");
+ return supported;
+}
+
+
+void RegExpMacroAssemblerTracer::IfRegisterLT(int register_index,
+ int comparand, Label* if_lt) {
+ PrintF(" IfRegisterLT(register=%d, number=%d, label[%08x]);\n",
+ register_index, comparand, LabelToInt(if_lt));
+ assembler_->IfRegisterLT(register_index, comparand, if_lt);
+}
+
+
+void RegExpMacroAssemblerTracer::IfRegisterEqPos(int register_index,
+ Label* if_eq) {
+ PrintF(" IfRegisterEqPos(register=%d, label[%08x]);\n",
+ register_index, LabelToInt(if_eq));
+ assembler_->IfRegisterEqPos(register_index, if_eq);
+}
+
+
+void RegExpMacroAssemblerTracer::IfRegisterGE(int register_index,
+ int comparand, Label* if_ge) {
+ PrintF(" IfRegisterGE(register=%d, number=%d, label[%08x]);\n",
+ register_index, comparand, LabelToInt(if_ge));
+ assembler_->IfRegisterGE(register_index, comparand, if_ge);
+}
+
+
+RegExpMacroAssembler::IrregexpImplementation
+ RegExpMacroAssemblerTracer::Implementation() {
+ return assembler_->Implementation();
+}
+
+
+Handle<HeapObject> RegExpMacroAssemblerTracer::GetCode(Handle<String> source) {
+ PrintF(" GetCode(%s);\n", *(source->ToCString()));
+ return assembler_->GetCode(source);
+}
+
+}} // namespace v8::internal
diff --git a/chromium/v8/src/regexp-macro-assembler-tracer.h b/chromium/v8/src/regexp-macro-assembler-tracer.h
new file mode 100644
index 00000000000..852fb80417b
--- /dev/null
+++ b/chromium/v8/src/regexp-macro-assembler-tracer.h
@@ -0,0 +1,106 @@
+// Copyright 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_REGEXP_MACRO_ASSEMBLER_TRACER_H_
+#define V8_REGEXP_MACRO_ASSEMBLER_TRACER_H_
+
+namespace v8 {
+namespace internal {
+
+// Decorator on a RegExpMacroAssembler that write all calls.
+class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
+ public:
+ explicit RegExpMacroAssemblerTracer(RegExpMacroAssembler* assembler);
+ virtual ~RegExpMacroAssemblerTracer();
+ virtual int stack_limit_slack() { return assembler_->stack_limit_slack(); }
+ virtual bool CanReadUnaligned() { return assembler_->CanReadUnaligned(); }
+ virtual void AdvanceCurrentPosition(int by); // Signed cp change.
+ virtual void AdvanceRegister(int reg, int by); // r[reg] += by.
+ virtual void Backtrack();
+ virtual void Bind(Label* label);
+ virtual void CheckAtStart(Label* on_at_start);
+ virtual void CheckCharacter(unsigned c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned and_with,
+ Label* on_equal);
+ virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
+ virtual void CheckCharacterLT(uc16 limit, Label* on_less);
+ virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
+ virtual void CheckNotAtStart(Label* on_not_at_start);
+ virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
+ virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
+ Label* on_no_match);
+ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned and_with,
+ Label* on_not_equal);
+ virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
+ uc16 minus,
+ uc16 and_with,
+ Label* on_not_equal);
+ virtual void CheckCharacterInRange(uc16 from,
+ uc16 to,
+ Label* on_in_range);
+ virtual void CheckCharacterNotInRange(uc16 from,
+ uc16 to,
+ Label* on_not_in_range);
+ virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
+ virtual bool CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match);
+ virtual void Fail();
+ virtual Handle<HeapObject> GetCode(Handle<String> source);
+ virtual void GoTo(Label* label);
+ virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
+ virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
+ virtual void IfRegisterEqPos(int reg, Label* if_eq);
+ virtual IrregexpImplementation Implementation();
+ virtual void LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds = true,
+ int characters = 1);
+ virtual void PopCurrentPosition();
+ virtual void PopRegister(int register_index);
+ virtual void PushBacktrack(Label* label);
+ virtual void PushCurrentPosition();
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
+ virtual void ReadCurrentPositionFromRegister(int reg);
+ virtual void ReadStackPointerFromRegister(int reg);
+ virtual void SetCurrentPositionFromEnd(int by);
+ virtual void SetRegister(int register_index, int to);
+ virtual bool Succeed();
+ virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+ virtual void ClearRegisters(int reg_from, int reg_to);
+ virtual void WriteStackPointerToRegister(int reg);
+
+ private:
+ RegExpMacroAssembler* assembler_;
+};
+
+}} // namespace v8::internal
+
+#endif // V8_REGEXP_MACRO_ASSEMBLER_TRACER_H_
diff --git a/chromium/v8/src/regexp-macro-assembler.cc b/chromium/v8/src/regexp-macro-assembler.cc
new file mode 100644
index 00000000000..fa792768bc3
--- /dev/null
+++ b/chromium/v8/src/regexp-macro-assembler.cc
@@ -0,0 +1,292 @@
+// Copyright 2012 the V8 project authors. 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 "ast.h"
+#include "assembler.h"
+#include "regexp-stack.h"
+#include "regexp-macro-assembler.h"
+#include "simulator.h"
+
+namespace v8 {
+namespace internal {
+
+RegExpMacroAssembler::RegExpMacroAssembler(Zone* zone)
+ : slow_safe_compiler_(false),
+ global_mode_(NOT_GLOBAL),
+ zone_(zone) {
+}
+
+
+RegExpMacroAssembler::~RegExpMacroAssembler() {
+}
+
+
+bool RegExpMacroAssembler::CanReadUnaligned() {
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ return true;
+#else
+ return false;
+#endif
+}
+
+
+#ifndef V8_INTERPRETED_REGEXP // Avoid unused code, e.g., on ARM.
+
+NativeRegExpMacroAssembler::NativeRegExpMacroAssembler(Zone* zone)
+ : RegExpMacroAssembler(zone) {
+}
+
+
+NativeRegExpMacroAssembler::~NativeRegExpMacroAssembler() {
+}
+
+
+bool NativeRegExpMacroAssembler::CanReadUnaligned() {
+ return FLAG_enable_unaligned_accesses && !slow_safe();
+}
+
+const byte* NativeRegExpMacroAssembler::StringCharacterPosition(
+ String* subject,
+ int start_index) {
+ // Not just flat, but ultra flat.
+ ASSERT(subject->IsExternalString() || subject->IsSeqString());
+ ASSERT(start_index >= 0);
+ ASSERT(start_index <= subject->length());
+ if (subject->IsOneByteRepresentation()) {
+ const byte* address;
+ if (StringShape(subject).IsExternal()) {
+ const uint8_t* data = ExternalAsciiString::cast(subject)->GetChars();
+ address = reinterpret_cast<const byte*>(data);
+ } else {
+ ASSERT(subject->IsSeqOneByteString());
+ const uint8_t* data = SeqOneByteString::cast(subject)->GetChars();
+ address = reinterpret_cast<const byte*>(data);
+ }
+ return address + start_index;
+ }
+ const uc16* data;
+ if (StringShape(subject).IsExternal()) {
+ data = ExternalTwoByteString::cast(subject)->GetChars();
+ } else {
+ ASSERT(subject->IsSeqTwoByteString());
+ data = SeqTwoByteString::cast(subject)->GetChars();
+ }
+ return reinterpret_cast<const byte*>(data + start_index);
+}
+
+
+NativeRegExpMacroAssembler::Result NativeRegExpMacroAssembler::Match(
+ Handle<Code> regexp_code,
+ Handle<String> subject,
+ int* offsets_vector,
+ int offsets_vector_length,
+ int previous_index,
+ Isolate* isolate) {
+
+ ASSERT(subject->IsFlat());
+ ASSERT(previous_index >= 0);
+ ASSERT(previous_index <= subject->length());
+
+ // No allocations before calling the regexp, but we can't use
+ // DisallowHeapAllocation, since regexps might be preempted, and another
+ // thread might do allocation anyway.
+
+ String* subject_ptr = *subject;
+ // Character offsets into string.
+ int start_offset = previous_index;
+ int char_length = subject_ptr->length() - start_offset;
+ int slice_offset = 0;
+
+ // The string has been flattened, so if it is a cons string it contains the
+ // full string in the first part.
+ if (StringShape(subject_ptr).IsCons()) {
+ ASSERT_EQ(0, ConsString::cast(subject_ptr)->second()->length());
+ subject_ptr = ConsString::cast(subject_ptr)->first();
+ } else if (StringShape(subject_ptr).IsSliced()) {
+ SlicedString* slice = SlicedString::cast(subject_ptr);
+ subject_ptr = slice->parent();
+ slice_offset = slice->offset();
+ }
+ // Ensure that an underlying string has the same ASCII-ness.
+ bool is_ascii = subject_ptr->IsOneByteRepresentation();
+ ASSERT(subject_ptr->IsExternalString() || subject_ptr->IsSeqString());
+ // String is now either Sequential or External
+ int char_size_shift = is_ascii ? 0 : 1;
+
+ const byte* input_start =
+ StringCharacterPosition(subject_ptr, start_offset + slice_offset);
+ int byte_length = char_length << char_size_shift;
+ const byte* input_end = input_start + byte_length;
+ Result res = Execute(*regexp_code,
+ *subject,
+ start_offset,
+ input_start,
+ input_end,
+ offsets_vector,
+ offsets_vector_length,
+ isolate);
+ return res;
+}
+
+
+NativeRegExpMacroAssembler::Result NativeRegExpMacroAssembler::Execute(
+ Code* code,
+ String* input, // This needs to be the unpacked (sliced, cons) string.
+ int start_offset,
+ const byte* input_start,
+ const byte* input_end,
+ int* output,
+ int output_size,
+ Isolate* isolate) {
+ ASSERT(isolate == Isolate::Current());
+ // Ensure that the minimum stack has been allocated.
+ RegExpStackScope stack_scope(isolate);
+ Address stack_base = stack_scope.stack()->stack_base();
+
+ int direct_call = 0;
+ int result = CALL_GENERATED_REGEXP_CODE(code->entry(),
+ input,
+ start_offset,
+ input_start,
+ input_end,
+ output,
+ output_size,
+ stack_base,
+ direct_call,
+ isolate);
+ ASSERT(result >= RETRY);
+
+ if (result == EXCEPTION && !isolate->has_pending_exception()) {
+ // We detected a stack overflow (on the backtrack stack) in RegExp code,
+ // but haven't created the exception yet.
+ isolate->StackOverflow();
+ }
+ return static_cast<Result>(result);
+}
+
+
+const byte NativeRegExpMacroAssembler::word_character_map[] = {
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // '0' - '7'
+ 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // '8' - '9'
+
+ 0x00u, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'A' - 'G'
+ 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'H' - 'O'
+ 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'P' - 'W'
+ 0xffu, 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0xffu, // 'X' - 'Z', '_'
+
+ 0x00u, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'a' - 'g'
+ 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'h' - 'o'
+ 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, 0xffu, // 'p' - 'w'
+ 0xffu, 0xffu, 0xffu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // 'x' - 'z'
+ // Latin-1 range
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+ 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
+};
+
+
+int NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16(
+ Address byte_offset1,
+ Address byte_offset2,
+ size_t byte_length,
+ Isolate* isolate) {
+ ASSERT(isolate == Isolate::Current());
+ unibrow::Mapping<unibrow::Ecma262Canonicalize>* canonicalize =
+ isolate->regexp_macro_assembler_canonicalize();
+ // This function is not allowed to cause a garbage collection.
+ // A GC might move the calling generated code and invalidate the
+ // return address on the stack.
+ ASSERT(byte_length % 2 == 0);
+ uc16* substring1 = reinterpret_cast<uc16*>(byte_offset1);
+ uc16* substring2 = reinterpret_cast<uc16*>(byte_offset2);
+ size_t length = byte_length >> 1;
+
+ for (size_t i = 0; i < length; i++) {
+ unibrow::uchar c1 = substring1[i];
+ unibrow::uchar c2 = substring2[i];
+ if (c1 != c2) {
+ unibrow::uchar s1[1] = { c1 };
+ canonicalize->get(c1, '\0', s1);
+ if (s1[0] != c2) {
+ unibrow::uchar s2[1] = { c2 };
+ canonicalize->get(c2, '\0', s2);
+ if (s1[0] != s2[0]) {
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+
+Address NativeRegExpMacroAssembler::GrowStack(Address stack_pointer,
+ Address* stack_base,
+ Isolate* isolate) {
+ ASSERT(isolate == Isolate::Current());
+ RegExpStack* regexp_stack = isolate->regexp_stack();
+ size_t size = regexp_stack->stack_capacity();
+ Address old_stack_base = regexp_stack->stack_base();
+ ASSERT(old_stack_base == *stack_base);
+ ASSERT(stack_pointer <= old_stack_base);
+ ASSERT(static_cast<size_t>(old_stack_base - stack_pointer) <= size);
+ Address new_stack_base = regexp_stack->EnsureCapacity(size * 2);
+ if (new_stack_base == NULL) {
+ return NULL;
+ }
+ *stack_base = new_stack_base;
+ intptr_t stack_content_size = old_stack_base - stack_pointer;
+ return new_stack_base - stack_content_size;
+}
+
+#endif // V8_INTERPRETED_REGEXP
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/regexp-macro-assembler.h b/chromium/v8/src/regexp-macro-assembler.h
new file mode 100644
index 00000000000..1ff8bd97972
--- /dev/null
+++ b/chromium/v8/src/regexp-macro-assembler.h
@@ -0,0 +1,259 @@
+// Copyright 2012 the V8 project authors. 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_REGEXP_MACRO_ASSEMBLER_H_
+#define V8_REGEXP_MACRO_ASSEMBLER_H_
+
+#include "ast.h"
+
+namespace v8 {
+namespace internal {
+
+struct DisjunctDecisionRow {
+ RegExpCharacterClass cc;
+ Label* on_match;
+};
+
+
+class RegExpMacroAssembler {
+ public:
+ // The implementation must be able to handle at least:
+ static const int kMaxRegister = (1 << 16) - 1;
+ static const int kMaxCPOffset = (1 << 15) - 1;
+ static const int kMinCPOffset = -(1 << 15);
+
+ static const int kTableSizeBits = 7;
+ static const int kTableSize = 1 << kTableSizeBits;
+ static const int kTableMask = kTableSize - 1;
+
+ enum IrregexpImplementation {
+ kIA32Implementation,
+ kARMImplementation,
+ kMIPSImplementation,
+ kX64Implementation,
+ kBytecodeImplementation
+ };
+
+ enum StackCheckFlag {
+ kNoStackLimitCheck = false,
+ kCheckStackLimit = true
+ };
+
+ explicit RegExpMacroAssembler(Zone* zone);
+ virtual ~RegExpMacroAssembler();
+ // The maximal number of pushes between stack checks. Users must supply
+ // kCheckStackLimit flag to push operations (instead of kNoStackLimitCheck)
+ // at least once for every stack_limit() pushes that are executed.
+ virtual int stack_limit_slack() = 0;
+ virtual bool CanReadUnaligned();
+ virtual void AdvanceCurrentPosition(int by) = 0; // Signed cp change.
+ virtual void AdvanceRegister(int reg, int by) = 0; // r[reg] += by.
+ // Continues execution from the position pushed on the top of the backtrack
+ // stack by an earlier PushBacktrack(Label*).
+ virtual void Backtrack() = 0;
+ virtual void Bind(Label* label) = 0;
+ virtual void CheckAtStart(Label* on_at_start) = 0;
+ // Dispatch after looking the current character up in a 2-bits-per-entry
+ // map. The destinations vector has up to 4 labels.
+ virtual void CheckCharacter(unsigned c, Label* on_equal) = 0;
+ // Bitwise and the current character with the given constant and then
+ // check for a match with c.
+ virtual void CheckCharacterAfterAnd(unsigned c,
+ unsigned and_with,
+ Label* on_equal) = 0;
+ virtual void CheckCharacterGT(uc16 limit, Label* on_greater) = 0;
+ virtual void CheckCharacterLT(uc16 limit, Label* on_less) = 0;
+ virtual void CheckGreedyLoop(Label* on_tos_equals_current_position) = 0;
+ virtual void CheckNotAtStart(Label* on_not_at_start) = 0;
+ virtual void CheckNotBackReference(int start_reg, Label* on_no_match) = 0;
+ virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
+ Label* on_no_match) = 0;
+ // Check the current character for a match with a literal character. If we
+ // fail to match then goto the on_failure label. End of input always
+ // matches. If the label is NULL then we should pop a backtrack address off
+ // the stack and go to that.
+ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal) = 0;
+ virtual void CheckNotCharacterAfterAnd(unsigned c,
+ unsigned and_with,
+ Label* on_not_equal) = 0;
+ // Subtract a constant from the current character, then and with the given
+ // constant and then check for a match with c.
+ virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
+ uc16 minus,
+ uc16 and_with,
+ Label* on_not_equal) = 0;
+ virtual void CheckCharacterInRange(uc16 from,
+ uc16 to, // Both inclusive.
+ Label* on_in_range) = 0;
+ virtual void CheckCharacterNotInRange(uc16 from,
+ uc16 to, // Both inclusive.
+ Label* on_not_in_range) = 0;
+
+ // The current character (modulus the kTableSize) is looked up in the byte
+ // array, and if the found byte is non-zero, we jump to the on_bit_set label.
+ virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set) = 0;
+
+ // Checks whether the given offset from the current position is before
+ // the end of the string. May overwrite the current character.
+ virtual void CheckPosition(int cp_offset, Label* on_outside_input) {
+ LoadCurrentCharacter(cp_offset, on_outside_input, true);
+ }
+ // Check whether a standard/default character class matches the current
+ // character. Returns false if the type of special character class does
+ // not have custom support.
+ // May clobber the current loaded character.
+ virtual bool CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match) {
+ return false;
+ }
+ virtual void Fail() = 0;
+ virtual Handle<HeapObject> GetCode(Handle<String> source) = 0;
+ virtual void GoTo(Label* label) = 0;
+ // Check whether a register is >= a given constant and go to a label if it
+ // is. Backtracks instead if the label is NULL.
+ virtual void IfRegisterGE(int reg, int comparand, Label* if_ge) = 0;
+ // Check whether a register is < a given constant and go to a label if it is.
+ // Backtracks instead if the label is NULL.
+ virtual void IfRegisterLT(int reg, int comparand, Label* if_lt) = 0;
+ // Check whether a register is == to the current position and go to a
+ // label if it is.
+ virtual void IfRegisterEqPos(int reg, Label* if_eq) = 0;
+ virtual IrregexpImplementation Implementation() = 0;
+ virtual void LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds = true,
+ int characters = 1) = 0;
+ virtual void PopCurrentPosition() = 0;
+ virtual void PopRegister(int register_index) = 0;
+ // Pushes the label on the backtrack stack, so that a following Backtrack
+ // will go to this label. Always checks the backtrack stack limit.
+ virtual void PushBacktrack(Label* label) = 0;
+ virtual void PushCurrentPosition() = 0;
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit) = 0;
+ virtual void ReadCurrentPositionFromRegister(int reg) = 0;
+ virtual void ReadStackPointerFromRegister(int reg) = 0;
+ virtual void SetCurrentPositionFromEnd(int by) = 0;
+ virtual void SetRegister(int register_index, int to) = 0;
+ // Return whether the matching (with a global regexp) will be restarted.
+ virtual bool Succeed() = 0;
+ virtual void WriteCurrentPositionToRegister(int reg, int cp_offset) = 0;
+ virtual void ClearRegisters(int reg_from, int reg_to) = 0;
+ virtual void WriteStackPointerToRegister(int reg) = 0;
+
+ // Controls the generation of large inlined constants in the code.
+ void set_slow_safe(bool ssc) { slow_safe_compiler_ = ssc; }
+ bool slow_safe() { return slow_safe_compiler_; }
+
+ enum GlobalMode { NOT_GLOBAL, GLOBAL, GLOBAL_NO_ZERO_LENGTH_CHECK };
+ // Set whether the regular expression has the global flag. Exiting due to
+ // a failure in a global regexp may still mean success overall.
+ inline void set_global_mode(GlobalMode mode) { global_mode_ = mode; }
+ inline bool global() { return global_mode_ != NOT_GLOBAL; }
+ inline bool global_with_zero_length_check() {
+ return global_mode_ == GLOBAL;
+ }
+
+ Zone* zone() const { return zone_; }
+
+ private:
+ bool slow_safe_compiler_;
+ bool global_mode_;
+ Zone* zone_;
+};
+
+
+#ifndef V8_INTERPRETED_REGEXP // Avoid compiling unused code.
+
+class NativeRegExpMacroAssembler: public RegExpMacroAssembler {
+ public:
+ // Type of input string to generate code for.
+ enum Mode { ASCII = 1, UC16 = 2 };
+
+ // Result of calling generated native RegExp code.
+ // RETRY: Something significant changed during execution, and the matching
+ // should be retried from scratch.
+ // EXCEPTION: Something failed during execution. If no exception has been
+ // thrown, it's an internal out-of-memory, and the caller should
+ // throw the exception.
+ // FAILURE: Matching failed.
+ // SUCCESS: Matching succeeded, and the output array has been filled with
+ // capture positions.
+ enum Result { RETRY = -2, EXCEPTION = -1, FAILURE = 0, SUCCESS = 1 };
+
+ explicit NativeRegExpMacroAssembler(Zone* zone);
+ virtual ~NativeRegExpMacroAssembler();
+ virtual bool CanReadUnaligned();
+
+ static Result Match(Handle<Code> regexp,
+ Handle<String> subject,
+ int* offsets_vector,
+ int offsets_vector_length,
+ int previous_index,
+ Isolate* isolate);
+
+ // Compares two-byte strings case insensitively.
+ // Called from generated RegExp code.
+ static int CaseInsensitiveCompareUC16(Address byte_offset1,
+ Address byte_offset2,
+ size_t byte_length,
+ Isolate* isolate);
+
+ // Called from RegExp if the backtrack stack limit is hit.
+ // Tries to expand the stack. Returns the new stack-pointer if
+ // successful, and updates the stack_top address, or returns 0 if unable
+ // to grow the stack.
+ // This function must not trigger a garbage collection.
+ static Address GrowStack(Address stack_pointer, Address* stack_top,
+ Isolate* isolate);
+
+ static const byte* StringCharacterPosition(String* subject, int start_index);
+
+ // Byte map of one byte characters with a 0xff if the character is a word
+ // character (digit, letter or underscore) and 0x00 otherwise.
+ // Used by generated RegExp code.
+ static const byte word_character_map[256];
+
+ static Address word_character_map_address() {
+ return const_cast<Address>(&word_character_map[0]);
+ }
+
+ static Result Execute(Code* code,
+ String* input,
+ int start_offset,
+ const byte* input_start,
+ const byte* input_end,
+ int* output,
+ int output_size,
+ Isolate* isolate);
+};
+
+#endif // V8_INTERPRETED_REGEXP
+
+} } // namespace v8::internal
+
+#endif // V8_REGEXP_MACRO_ASSEMBLER_H_
diff --git a/chromium/v8/src/regexp-stack.cc b/chromium/v8/src/regexp-stack.cc
new file mode 100644
index 00000000000..fc4114af5de
--- /dev/null
+++ b/chromium/v8/src/regexp-stack.cc
@@ -0,0 +1,111 @@
+// 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.
+
+#include "v8.h"
+#include "regexp-stack.h"
+
+namespace v8 {
+namespace internal {
+
+RegExpStackScope::RegExpStackScope(Isolate* isolate)
+ : regexp_stack_(isolate->regexp_stack()) {
+ // Initialize, if not already initialized.
+ regexp_stack_->EnsureCapacity(0);
+}
+
+
+RegExpStackScope::~RegExpStackScope() {
+ ASSERT(Isolate::Current() == regexp_stack_->isolate_);
+ // Reset the buffer if it has grown.
+ regexp_stack_->Reset();
+}
+
+
+RegExpStack::RegExpStack()
+ : isolate_(NULL) {
+}
+
+
+RegExpStack::~RegExpStack() {
+ thread_local_.Free();
+}
+
+
+char* RegExpStack::ArchiveStack(char* to) {
+ size_t size = sizeof(thread_local_);
+ OS::MemCopy(reinterpret_cast<void*>(to), &thread_local_, size);
+ thread_local_ = ThreadLocal();
+ return to + size;
+}
+
+
+char* RegExpStack::RestoreStack(char* from) {
+ size_t size = sizeof(thread_local_);
+ OS::MemCopy(&thread_local_, reinterpret_cast<void*>(from), size);
+ return from + size;
+}
+
+
+void RegExpStack::Reset() {
+ if (thread_local_.memory_size_ > kMinimumStackSize) {
+ DeleteArray(thread_local_.memory_);
+ thread_local_ = ThreadLocal();
+ }
+}
+
+
+void RegExpStack::ThreadLocal::Free() {
+ if (memory_size_ > 0) {
+ DeleteArray(memory_);
+ Clear();
+ }
+}
+
+
+Address RegExpStack::EnsureCapacity(size_t size) {
+ if (size > kMaximumStackSize) return NULL;
+ if (size < kMinimumStackSize) size = kMinimumStackSize;
+ if (thread_local_.memory_size_ < size) {
+ Address new_memory = NewArray<byte>(static_cast<int>(size));
+ if (thread_local_.memory_size_ > 0) {
+ // Copy original memory into top of new memory.
+ OS::MemCopy(
+ reinterpret_cast<void*>(
+ new_memory + size - thread_local_.memory_size_),
+ reinterpret_cast<void*>(thread_local_.memory_),
+ thread_local_.memory_size_);
+ DeleteArray(thread_local_.memory_);
+ }
+ thread_local_.memory_ = new_memory;
+ thread_local_.memory_size_ = size;
+ thread_local_.limit_ = new_memory + kStackLimitSlack * kPointerSize;
+ }
+ return thread_local_.memory_ + thread_local_.memory_size_;
+}
+
+
+}} // namespace v8::internal
diff --git a/chromium/v8/src/regexp-stack.h b/chromium/v8/src/regexp-stack.h
new file mode 100644
index 00000000000..5684239f9fa
--- /dev/null
+++ b/chromium/v8/src/regexp-stack.h
@@ -0,0 +1,148 @@
+// 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.
+
+#ifndef V8_REGEXP_STACK_H_
+#define V8_REGEXP_STACK_H_
+
+namespace v8 {
+namespace internal {
+
+class RegExpStack;
+
+// Maintains a per-v8thread stack area that can be used by irregexp
+// implementation for its backtracking stack.
+// Since there is only one stack area, the Irregexp implementation is not
+// re-entrant. I.e., no regular expressions may be executed in the same thread
+// during a preempted Irregexp execution.
+class RegExpStackScope {
+ public:
+ // Create and delete an instance to control the life-time of a growing stack.
+
+ // Initializes the stack memory area if necessary.
+ explicit RegExpStackScope(Isolate* isolate);
+ ~RegExpStackScope(); // Releases the stack if it has grown.
+
+ RegExpStack* stack() const { return regexp_stack_; }
+
+ private:
+ RegExpStack* regexp_stack_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegExpStackScope);
+};
+
+
+class RegExpStack {
+ public:
+ // Number of allocated locations on the stack below the limit.
+ // No sequence of pushes must be longer that this without doing a stack-limit
+ // check.
+ static const int kStackLimitSlack = 32;
+
+ // Gives the top of the memory used as stack.
+ Address stack_base() {
+ ASSERT(thread_local_.memory_size_ != 0);
+ return thread_local_.memory_ + thread_local_.memory_size_;
+ }
+
+ // The total size of the memory allocated for the stack.
+ size_t stack_capacity() { return thread_local_.memory_size_; }
+
+ // If the stack pointer gets below the limit, we should react and
+ // either grow the stack or report an out-of-stack exception.
+ // There is only a limited number of locations below the stack limit,
+ // so users of the stack should check the stack limit during any
+ // sequence of pushes longer that this.
+ Address* limit_address() { return &(thread_local_.limit_); }
+
+ // Ensures that there is a memory area with at least the specified size.
+ // If passing zero, the default/minimum size buffer is allocated.
+ Address EnsureCapacity(size_t size);
+
+ // Thread local archiving.
+ static int ArchiveSpacePerThread() {
+ return static_cast<int>(sizeof(ThreadLocal));
+ }
+ char* ArchiveStack(char* to);
+ char* RestoreStack(char* from);
+ void FreeThreadResources() { thread_local_.Free(); }
+
+ private:
+ RegExpStack();
+ ~RegExpStack();
+
+ // Artificial limit used when no memory has been allocated.
+ static const uintptr_t kMemoryTop = static_cast<uintptr_t>(-1);
+
+ // Minimal size of allocated stack area.
+ static const size_t kMinimumStackSize = 1 * KB;
+
+ // Maximal size of allocated stack area.
+ static const size_t kMaximumStackSize = 64 * MB;
+
+ // Structure holding the allocated memory, size and limit.
+ struct ThreadLocal {
+ ThreadLocal() { Clear(); }
+ // If memory_size_ > 0 then memory_ must be non-NULL.
+ Address memory_;
+ size_t memory_size_;
+ Address limit_;
+ void Clear() {
+ memory_ = NULL;
+ memory_size_ = 0;
+ limit_ = reinterpret_cast<Address>(kMemoryTop);
+ }
+ void Free();
+ };
+
+ // Address of allocated memory.
+ Address memory_address() {
+ return reinterpret_cast<Address>(&thread_local_.memory_);
+ }
+
+ // Address of size of allocated memory.
+ Address memory_size_address() {
+ return reinterpret_cast<Address>(&thread_local_.memory_size_);
+ }
+
+ // Resets the buffer if it has grown beyond the default/minimum size.
+ // After this, the buffer is either the default size, or it is empty, so
+ // you have to call EnsureCapacity before using it again.
+ void Reset();
+
+ ThreadLocal thread_local_;
+ Isolate* isolate_;
+
+ friend class ExternalReference;
+ friend class Isolate;
+ friend class RegExpStackScope;
+
+ DISALLOW_COPY_AND_ASSIGN(RegExpStack);
+};
+
+}} // namespace v8::internal
+
+#endif // V8_REGEXP_STACK_H_
diff --git a/chromium/v8/src/regexp.js b/chromium/v8/src/regexp.js
new file mode 100644
index 00000000000..cb11ad107cf
--- /dev/null
+++ b/chromium/v8/src/regexp.js
@@ -0,0 +1,485 @@
+// Copyright 2012 the V8 project authors. 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:
+// var $Object = global.Object;
+// var $Array = global.Array;
+
+var $RegExp = global.RegExp;
+
+// -------------------------------------------------------------------
+
+// A recursive descent parser for Patterns according to the grammar of
+// ECMA-262 15.10.1, with deviations noted below.
+function DoConstructRegExp(object, pattern, flags) {
+ // RegExp : Called as constructor; see ECMA-262, section 15.10.4.
+ if (IS_REGEXP(pattern)) {
+ if (!IS_UNDEFINED(flags)) {
+ throw MakeTypeError('regexp_flags', []);
+ }
+ flags = (pattern.global ? 'g' : '')
+ + (pattern.ignoreCase ? 'i' : '')
+ + (pattern.multiline ? 'm' : '');
+ pattern = pattern.source;
+ }
+
+ pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern);
+ flags = IS_UNDEFINED(flags) ? '' : ToString(flags);
+
+ var global = false;
+ var ignoreCase = false;
+ var multiline = false;
+ for (var i = 0; i < flags.length; i++) {
+ var c = %_CallFunction(flags, i, StringCharAt);
+ switch (c) {
+ case 'g':
+ if (global) {
+ throw MakeSyntaxError("invalid_regexp_flags", [flags]);
+ }
+ global = true;
+ break;
+ case 'i':
+ if (ignoreCase) {
+ throw MakeSyntaxError("invalid_regexp_flags", [flags]);
+ }
+ ignoreCase = true;
+ break;
+ case 'm':
+ if (multiline) {
+ throw MakeSyntaxError("invalid_regexp_flags", [flags]);
+ }
+ multiline = true;
+ break;
+ default:
+ throw MakeSyntaxError("invalid_regexp_flags", [flags]);
+ }
+ }
+
+ %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
+
+ // Call internal function to compile the pattern.
+ %RegExpCompile(object, pattern, flags);
+}
+
+
+function RegExpConstructor(pattern, flags) {
+ if (%_IsConstructCall()) {
+ DoConstructRegExp(this, pattern, flags);
+ } else {
+ // RegExp : Called as function; see ECMA-262, section 15.10.3.1.
+ if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) {
+ return pattern;
+ }
+ return new $RegExp(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 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
+ // here, treating undefined values differently in
+ // 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 {
+ DoConstructRegExp(this, pattern, flags);
+ }
+}
+
+
+function DoRegExpExec(regexp, string, index) {
+ var result = %_RegExpExec(regexp, string, index, lastMatchInfo);
+ if (result !== null) lastMatchInfoOverride = null;
+ return result;
+}
+
+
+function BuildResultFromMatchInfo(lastMatchInfo, s) {
+ var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
+ var start = lastMatchInfo[CAPTURE0];
+ var end = lastMatchInfo[CAPTURE1];
+ var result = %_RegExpConstructResult(numResults, start, s);
+ result[0] = %_SubString(s, start, end);
+ var j = REGEXP_FIRST_CAPTURE + 2;
+ for (var i = 1; i < numResults; i++) {
+ start = lastMatchInfo[j++];
+ if (start != -1) {
+ end = lastMatchInfo[j];
+ result[i] = %_SubString(s, start, end);
+ }
+ j++;
+ }
+ return result;
+}
+
+
+function RegExpExecNoTests(regexp, string, start) {
+ // Must be called with RegExp, string and positive integer as arguments.
+ var matchInfo = %_RegExpExec(regexp, string, start, lastMatchInfo);
+ if (matchInfo !== null) {
+ lastMatchInfoOverride = null;
+ return BuildResultFromMatchInfo(matchInfo, string);
+ }
+ regexp.lastIndex = 0;
+ return null;
+}
+
+
+function RegExpExec(string) {
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['RegExp.prototype.exec', this]);
+ }
+
+ string = TO_STRING_INLINE(string);
+ var lastIndex = this.lastIndex;
+
+ // Conversion is required by the ES5 specification (RegExp.prototype.exec
+ // algorithm, step 5) even if the value is discarded for non-global RegExps.
+ var i = TO_INTEGER(lastIndex);
+
+ var global = this.global;
+ if (global) {
+ if (i < 0 || i > string.length) {
+ this.lastIndex = 0;
+ return null;
+ }
+ } else {
+ i = 0;
+ }
+
+ %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
+ // matchIndices is either null or the lastMatchInfo array.
+ var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
+
+ if (matchIndices === null) {
+ this.lastIndex = 0;
+ return null;
+ }
+
+ // Successful match.
+ lastMatchInfoOverride = null;
+ if (global) {
+ this.lastIndex = lastMatchInfo[CAPTURE1];
+ }
+ return BuildResultFromMatchInfo(matchIndices, string);
+}
+
+
+// One-element cache for the simplified test regexp.
+var regexp_key;
+var regexp_val;
+
+// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
+// that test is defined in terms of String.prototype.exec. However, it probably
+// means the original value of String.prototype.exec, which is what everybody
+// else implements.
+function RegExpTest(string) {
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['RegExp.prototype.test', this]);
+ }
+ string = TO_STRING_INLINE(string);
+
+ var lastIndex = this.lastIndex;
+
+ // Conversion is required by the ES5 specification (RegExp.prototype.exec
+ // algorithm, step 5) even if the value is discarded for non-global RegExps.
+ var i = TO_INTEGER(lastIndex);
+
+ if (this.global) {
+ if (i < 0 || i > string.length) {
+ this.lastIndex = 0;
+ return false;
+ }
+ %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
+ // matchIndices is either null or the lastMatchInfo array.
+ var matchIndices = %_RegExpExec(this, string, i, lastMatchInfo);
+ if (matchIndices === null) {
+ this.lastIndex = 0;
+ return false;
+ }
+ lastMatchInfoOverride = null;
+ this.lastIndex = lastMatchInfo[CAPTURE1];
+ return true;
+ } else {
+ // Non-global regexp.
+ // Remove irrelevant preceeding '.*' in a non-global test regexp.
+ // The expression checks whether this.source starts with '.*' and
+ // that the third char is not a '?'.
+ var regexp = this;
+ if (%_StringCharCodeAt(regexp.source, 0) == 46 && // '.'
+ %_StringCharCodeAt(regexp.source, 1) == 42 && // '*'
+ %_StringCharCodeAt(regexp.source, 2) != 63) { // '?'
+ regexp = TrimRegExp(regexp);
+ }
+ %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [regexp, string, lastIndex]);
+ // matchIndices is either null or the lastMatchInfo array.
+ var matchIndices = %_RegExpExec(regexp, string, 0, lastMatchInfo);
+ if (matchIndices === null) {
+ this.lastIndex = 0;
+ return false;
+ }
+ lastMatchInfoOverride = null;
+ return true;
+ }
+}
+
+function TrimRegExp(regexp) {
+ if (!%_ObjectEquals(regexp_key, regexp)) {
+ regexp_key = regexp;
+ regexp_val =
+ new $RegExp(%_SubString(regexp.source, 2, regexp.source.length),
+ (regexp.ignoreCase ? regexp.multiline ? "im" : "i"
+ : regexp.multiline ? "m" : ""));
+ }
+ return regexp_val;
+}
+
+
+function RegExpToString() {
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['RegExp.prototype.toString', this]);
+ }
+ var result = '/' + this.source + '/';
+ if (this.global) result += 'g';
+ if (this.ignoreCase) result += 'i';
+ if (this.multiline) result += 'm';
+ return result;
+}
+
+
+// Getters for the static properties lastMatch, lastParen, leftContext, and
+// rightContext of the RegExp constructor. The properties are computed based
+// on the captures array of the last successful match and the subject string
+// of the last successful match.
+function RegExpGetLastMatch() {
+ if (lastMatchInfoOverride !== null) {
+ return OVERRIDE_MATCH(lastMatchInfoOverride);
+ }
+ var regExpSubject = LAST_SUBJECT(lastMatchInfo);
+ return %_SubString(regExpSubject,
+ lastMatchInfo[CAPTURE0],
+ lastMatchInfo[CAPTURE1]);
+}
+
+
+function RegExpGetLastParen() {
+ if (lastMatchInfoOverride) {
+ var override = lastMatchInfoOverride;
+ if (override.length <= 3) return '';
+ return override[override.length - 3];
+ }
+ var length = NUMBER_OF_CAPTURES(lastMatchInfo);
+ if (length <= 2) return ''; // There were no captures.
+ // We match the SpiderMonkey behavior: return the substring defined by the
+ // last pair (after the first pair) of elements of the capture array even if
+ // it is empty.
+ var regExpSubject = LAST_SUBJECT(lastMatchInfo);
+ var start = lastMatchInfo[CAPTURE(length - 2)];
+ var end = lastMatchInfo[CAPTURE(length - 1)];
+ if (start != -1 && end != -1) {
+ return %_SubString(regExpSubject, start, end);
+ }
+ return "";
+}
+
+
+function RegExpGetLeftContext() {
+ var start_index;
+ var subject;
+ if (!lastMatchInfoOverride) {
+ start_index = lastMatchInfo[CAPTURE0];
+ subject = LAST_SUBJECT(lastMatchInfo);
+ } else {
+ var override = lastMatchInfoOverride;
+ start_index = OVERRIDE_POS(override);
+ subject = OVERRIDE_SUBJECT(override);
+ }
+ return %_SubString(subject, 0, start_index);
+}
+
+
+function RegExpGetRightContext() {
+ var start_index;
+ var subject;
+ if (!lastMatchInfoOverride) {
+ start_index = lastMatchInfo[CAPTURE1];
+ subject = LAST_SUBJECT(lastMatchInfo);
+ } else {
+ var override = lastMatchInfoOverride;
+ subject = OVERRIDE_SUBJECT(override);
+ var match = OVERRIDE_MATCH(override);
+ start_index = OVERRIDE_POS(override) + match.length;
+ }
+ return %_SubString(subject, start_index, subject.length);
+}
+
+
+// The properties $1..$9 are the first nine capturing substrings of the last
+// successful match, or ''. The function RegExpMakeCaptureGetter will be
+// called with indices from 1 to 9.
+function RegExpMakeCaptureGetter(n) {
+ return function() {
+ if (lastMatchInfoOverride) {
+ if (n < lastMatchInfoOverride.length - 2) {
+ return OVERRIDE_CAPTURE(lastMatchInfoOverride, n);
+ }
+ return '';
+ }
+ var index = n * 2;
+ if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
+ var matchStart = lastMatchInfo[CAPTURE(index)];
+ var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
+ if (matchStart == -1 || matchEnd == -1) return '';
+ return %_SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
+ };
+}
+
+
+// Property of the builtins object for recording the result of the last
+// regexp match. The property lastMatchInfo includes the matchIndices
+// array of the last successful regexp match (an array of start/end index
+// pairs for the match and all the captured substrings), the invariant is
+// that there are at least two capture indeces. The array also contains
+// the subject string for the last successful match.
+var lastMatchInfo = new InternalPackedArray(
+ 2, // REGEXP_NUMBER_OF_CAPTURES
+ "", // Last subject.
+ void 0, // Last input - settable with RegExpSetInput.
+ 0, // REGEXP_FIRST_CAPTURE + 0
+ 0 // REGEXP_FIRST_CAPTURE + 1
+);
+
+// Override last match info with an array of actual substrings.
+// Used internally by replace regexp with function.
+// The array has the format of an "apply" argument for a replacement
+// function.
+var lastMatchInfoOverride = null;
+
+// -------------------------------------------------------------------
+
+function SetUpRegExp() {
+ %CheckIsBootstrapping();
+ %FunctionSetInstanceClassName($RegExp, 'RegExp');
+ %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
+ %SetCode($RegExp, RegExpConstructor);
+
+ InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
+ "exec", RegExpExec,
+ "test", RegExpTest,
+ "toString", RegExpToString,
+ "compile", RegExpCompile
+ ));
+
+ // The length of compile is 1 in SpiderMonkey.
+ %FunctionSetLength($RegExp.prototype.compile, 1);
+
+ // The properties input, $input, and $_ are aliases for each other. When this
+ // value is set the value it is set to is coerced to a string.
+ // Getter and setter for the input.
+ var RegExpGetInput = function() {
+ var regExpInput = LAST_INPUT(lastMatchInfo);
+ return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
+ };
+ var RegExpSetInput = function(string) {
+ LAST_INPUT(lastMatchInfo) = ToString(string);
+ };
+
+ %OptimizeObjectForAddingMultipleProperties($RegExp, 22);
+ %DefineOrRedefineAccessorProperty($RegExp, 'input', RegExpGetInput,
+ RegExpSetInput, DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, '$_', RegExpGetInput,
+ RegExpSetInput, DONT_ENUM | DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, '$input', RegExpGetInput,
+ 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 (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;
+ var RegExpGetMultiline = function() { return multiline; };
+ var RegExpSetMultiline = function(flag) { multiline = flag ? true : false; };
+
+ %DefineOrRedefineAccessorProperty($RegExp, 'multiline', RegExpGetMultiline,
+ RegExpSetMultiline, DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, '$*', RegExpGetMultiline,
+ RegExpSetMultiline,
+ DONT_ENUM | DONT_DELETE);
+
+
+ var NoOpSetter = function(ignored) {};
+
+
+ // Static properties set by a successful match.
+ %DefineOrRedefineAccessorProperty($RegExp, 'lastMatch', RegExpGetLastMatch,
+ NoOpSetter, DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, '$&', RegExpGetLastMatch,
+ NoOpSetter, DONT_ENUM | DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, 'lastParen', RegExpGetLastParen,
+ NoOpSetter, DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, '$+', RegExpGetLastParen,
+ NoOpSetter, DONT_ENUM | DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, 'leftContext',
+ RegExpGetLeftContext, NoOpSetter,
+ DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, '$`', RegExpGetLeftContext,
+ NoOpSetter, DONT_ENUM | DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, 'rightContext',
+ RegExpGetRightContext, NoOpSetter,
+ DONT_DELETE);
+ %DefineOrRedefineAccessorProperty($RegExp, "$'", RegExpGetRightContext,
+ NoOpSetter, DONT_ENUM | DONT_DELETE);
+
+ for (var i = 1; i < 10; ++i) {
+ %DefineOrRedefineAccessorProperty($RegExp, '$' + i,
+ RegExpMakeCaptureGetter(i), NoOpSetter,
+ DONT_DELETE);
+ }
+ %ToFastProperties($RegExp);
+}
+
+SetUpRegExp();
diff --git a/chromium/v8/src/rewriter.cc b/chromium/v8/src/rewriter.cc
new file mode 100644
index 00000000000..df5c353415d
--- /dev/null
+++ b/chromium/v8/src/rewriter.cc
@@ -0,0 +1,289 @@
+// Copyright 2012 the V8 project authors. 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 "rewriter.h"
+
+#include "ast.h"
+#include "compiler.h"
+#include "scopes.h"
+
+namespace v8 {
+namespace internal {
+
+class Processor: public AstVisitor {
+ public:
+ Processor(Variable* result, Zone* zone)
+ : result_(result),
+ result_assigned_(false),
+ is_set_(false),
+ in_try_(false),
+ factory_(Isolate::Current(), zone) {
+ InitializeAstVisitor();
+ }
+
+ virtual ~Processor() { }
+
+ void Process(ZoneList<Statement*>* statements);
+ bool result_assigned() const { return result_assigned_; }
+
+ AstNodeFactory<AstNullVisitor>* factory() {
+ return &factory_;
+ }
+
+ private:
+ Variable* result_;
+
+ // We are not tracking result usage via the result_'s use
+ // counts (we leave the accurate computation to the
+ // usage analyzer). Instead we simple remember if
+ // there was ever an assignment to result_.
+ bool result_assigned_;
+
+ // To avoid storing to .result all the time, we eliminate some of
+ // the stores by keeping track of whether or not we're sure .result
+ // will be overwritten anyway. This is a bit more tricky than what I
+ // was hoping for
+ bool is_set_;
+ bool in_try_;
+
+ AstNodeFactory<AstNullVisitor> factory_;
+
+ Expression* SetResult(Expression* value) {
+ result_assigned_ = true;
+ VariableProxy* result_proxy = factory()->NewVariableProxy(result_);
+ return factory()->NewAssignment(
+ Token::ASSIGN, result_proxy, value, RelocInfo::kNoPosition);
+ }
+
+ // Node visitors.
+#define DEF_VISIT(type) \
+ virtual void Visit##type(type* node);
+ AST_NODE_LIST(DEF_VISIT)
+#undef DEF_VISIT
+
+ void VisitIterationStatement(IterationStatement* stmt);
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+};
+
+
+void Processor::Process(ZoneList<Statement*>* statements) {
+ for (int i = statements->length() - 1; i >= 0; --i) {
+ Visit(statements->at(i));
+ }
+}
+
+
+void Processor::VisitBlock(Block* node) {
+ // An initializer block is the rewritten form of a variable declaration
+ // with initialization expressions. The initializer block contains the
+ // list of assignments corresponding to the initialization expressions.
+ // While unclear from the spec (ECMA-262, 3rd., 12.2), the value of
+ // a variable declaration with initialization expression is 'undefined'
+ // with some JS VMs: For instance, using smjs, print(eval('var x = 7'))
+ // returns 'undefined'. To obtain the same behavior with v8, we need
+ // to prevent rewriting in that case.
+ if (!node->is_initializer_block()) Process(node->statements());
+}
+
+
+void Processor::VisitModuleStatement(ModuleStatement* node) {
+ bool set_after_body = is_set_;
+ Visit(node->body());
+ is_set_ = is_set_ && set_after_body;
+}
+
+
+void Processor::VisitExpressionStatement(ExpressionStatement* node) {
+ // Rewrite : <x>; -> .result = <x>;
+ if (!is_set_ && !node->expression()->IsThrow()) {
+ node->set_expression(SetResult(node->expression()));
+ if (!in_try_) is_set_ = true;
+ }
+}
+
+
+void Processor::VisitIfStatement(IfStatement* node) {
+ // Rewrite both then and else parts (reversed).
+ bool save = is_set_;
+ Visit(node->else_statement());
+ bool set_after_then = is_set_;
+ is_set_ = save;
+ Visit(node->then_statement());
+ is_set_ = is_set_ && set_after_then;
+}
+
+
+void Processor::VisitIterationStatement(IterationStatement* node) {
+ // Rewrite the body.
+ bool set_after_loop = is_set_;
+ Visit(node->body());
+ is_set_ = is_set_ && set_after_loop;
+}
+
+
+void Processor::VisitDoWhileStatement(DoWhileStatement* node) {
+ VisitIterationStatement(node);
+}
+
+
+void Processor::VisitWhileStatement(WhileStatement* node) {
+ VisitIterationStatement(node);
+}
+
+
+void Processor::VisitForStatement(ForStatement* node) {
+ VisitIterationStatement(node);
+}
+
+
+void Processor::VisitForInStatement(ForInStatement* node) {
+ VisitIterationStatement(node);
+}
+
+
+void Processor::VisitForOfStatement(ForOfStatement* node) {
+ VisitIterationStatement(node);
+}
+
+
+void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
+ // Rewrite both try and catch blocks (reversed order).
+ bool set_after_catch = is_set_;
+ Visit(node->catch_block());
+ is_set_ = is_set_ && set_after_catch;
+ bool save = in_try_;
+ in_try_ = true;
+ Visit(node->try_block());
+ in_try_ = save;
+}
+
+
+void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
+ // Rewrite both try and finally block (reversed order).
+ Visit(node->finally_block());
+ bool save = in_try_;
+ in_try_ = true;
+ Visit(node->try_block());
+ in_try_ = save;
+}
+
+
+void Processor::VisitSwitchStatement(SwitchStatement* node) {
+ // Rewrite statements in all case clauses in reversed order.
+ ZoneList<CaseClause*>* clauses = node->cases();
+ bool set_after_switch = is_set_;
+ for (int i = clauses->length() - 1; i >= 0; --i) {
+ CaseClause* clause = clauses->at(i);
+ Process(clause->statements());
+ }
+ is_set_ = is_set_ && set_after_switch;
+}
+
+
+void Processor::VisitContinueStatement(ContinueStatement* node) {
+ is_set_ = false;
+}
+
+
+void Processor::VisitBreakStatement(BreakStatement* node) {
+ is_set_ = false;
+}
+
+
+void Processor::VisitWithStatement(WithStatement* node) {
+ bool set_after_body = is_set_;
+ Visit(node->statement());
+ is_set_ = is_set_ && set_after_body;
+}
+
+
+// Do nothing:
+void Processor::VisitVariableDeclaration(VariableDeclaration* node) {}
+void Processor::VisitFunctionDeclaration(FunctionDeclaration* node) {}
+void Processor::VisitModuleDeclaration(ModuleDeclaration* node) {}
+void Processor::VisitImportDeclaration(ImportDeclaration* node) {}
+void Processor::VisitExportDeclaration(ExportDeclaration* 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) {}
+
+
+// Expressions are never visited yet.
+#define DEF_VISIT(type) \
+ void Processor::Visit##type(type* expr) { UNREACHABLE(); }
+EXPRESSION_NODE_LIST(DEF_VISIT)
+#undef DEF_VISIT
+
+
+// Assumes code has been parsed. Mutates the AST, so the AST should not
+// continue to be used in the case of failure.
+bool Rewriter::Rewrite(CompilationInfo* info) {
+ FunctionLiteral* function = info->function();
+ ASSERT(function != NULL);
+ Scope* scope = function->scope();
+ ASSERT(scope != NULL);
+ if (!scope->is_global_scope() && !scope->is_eval_scope()) return true;
+
+ ZoneList<Statement*>* body = function->body();
+ if (!body->is_empty()) {
+ Variable* result = scope->NewTemporary(
+ info->isolate()->factory()->result_string());
+ Processor processor(result, info->zone());
+ processor.Process(body);
+ if (processor.HasStackOverflow()) return false;
+
+ if (processor.result_assigned()) {
+ 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, result->interface(), position);
+ result_proxy->BindTo(result);
+ Statement* result_statement =
+ processor.factory()->NewReturnStatement(result_proxy);
+ result_statement->set_statement_pos(position);
+ body->Add(result_statement, info->zone());
+ }
+ }
+
+ return true;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/rewriter.h b/chromium/v8/src/rewriter.h
new file mode 100644
index 00000000000..59914d97f91
--- /dev/null
+++ b/chromium/v8/src/rewriter.h
@@ -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.
+
+#ifndef V8_REWRITER_H_
+#define V8_REWRITER_H_
+
+namespace v8 {
+namespace internal {
+
+class CompilationInfo;
+
+class Rewriter {
+ public:
+ // Rewrite top-level code (ECMA 262 "programs") so as to conservatively
+ // include an assignment of the value of the last statement in the code to
+ // a compiler-generated temporary variable wherever needed.
+ //
+ // Assumes code has been parsed and scopes have been analyzed. Mutates the
+ // AST, so the AST should not continue to be used in the case of failure.
+ static bool Rewrite(CompilationInfo* info);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_REWRITER_H_
diff --git a/chromium/v8/src/runtime-profiler.cc b/chromium/v8/src/runtime-profiler.cc
new file mode 100644
index 00000000000..0e99650ed73
--- /dev/null
+++ b/chromium/v8/src/runtime-profiler.cc
@@ -0,0 +1,445 @@
+// Copyright 2012 the V8 project authors. 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 "runtime-profiler.h"
+
+#include "assembler.h"
+#include "bootstrapper.h"
+#include "code-stubs.h"
+#include "compilation-cache.h"
+#include "deoptimizer.h"
+#include "execution.h"
+#include "full-codegen.h"
+#include "global-handles.h"
+#include "isolate-inl.h"
+#include "mark-compact.h"
+#include "platform.h"
+#include "scopeinfo.h"
+
+namespace v8 {
+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;
+
+static const int kSamplerThresholdInit = 3;
+static const int kSamplerThresholdMin = 1;
+static const int kSamplerThresholdDelta = 1;
+
+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;
+// If the function optimization was disabled due to high deoptimization count,
+// but the function is hot and has been seen on the stack this number of times,
+// then we try to reenable optimization for this function.
+static const int kProfilerTicksBeforeReenablingOptimization = 250;
+// If a function does not have enough type info (according to
+// FLAG_type_info_threshold), but has seen a huge number of ticks,
+// optimize it as it is.
+static const int kTicksWhenNotEnoughTypeInfo = 100;
+// We only have one byte to store the number of ticks.
+STATIC_ASSERT(kProfilerTicksBeforeOptimization < 256);
+STATIC_ASSERT(kProfilerTicksBeforeReenablingOptimization < 256);
+STATIC_ASSERT(kTicksWhenNotEnoughTypeInfo < 256);
+
+// Maximum size in bytes of generate code for a function to allow OSR.
+static const int kOSRCodeSizeAllowanceBase =
+ 100 * FullCodeGenerator::kCodeSizeMultiplier;
+
+static const int kOSRCodeSizeAllowancePerTick =
+ 3 * FullCodeGenerator::kCodeSizeMultiplier;
+
+// 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 =
+ 5 * FullCodeGenerator::kCodeSizeMultiplier;
+
+
+RuntimeProfiler::RuntimeProfiler(Isolate* isolate)
+ : isolate_(isolate),
+ sampler_threshold_(kSamplerThresholdInit),
+ sampler_threshold_size_factor_(kSamplerThresholdSizeFactorInit),
+ sampler_ticks_until_threshold_adjustment_(
+ kSamplerTicksBetweenThresholdAdjustment),
+ sampler_window_position_(0),
+ any_ic_changed_(false),
+ code_generated_(false) {
+ ClearSampleBuffer();
+}
+
+
+static void GetICCounts(Code* shared_code,
+ int* ic_with_type_info_count,
+ int* ic_total_count,
+ int* percentage) {
+ *ic_total_count = 0;
+ *ic_with_type_info_count = 0;
+ Object* raw_info = shared_code->type_feedback_info();
+ if (raw_info->IsTypeFeedbackInfo()) {
+ TypeFeedbackInfo* info = TypeFeedbackInfo::cast(raw_info);
+ *ic_with_type_info_count = info->ic_with_type_info_count();
+ *ic_total_count = info->ic_total_count();
+ }
+ *percentage = *ic_total_count > 0
+ ? 100 * *ic_with_type_info_count / *ic_total_count
+ : 100;
+}
+
+
+void RuntimeProfiler::Optimize(JSFunction* function, const char* reason) {
+ ASSERT(function->IsOptimizable());
+
+ if (FLAG_trace_opt && function->PassesHydrogenFilter()) {
+ PrintF("[marking ");
+ function->ShortPrint();
+ PrintF(" for recompilation, reason: %s", reason);
+ if (FLAG_type_info_threshold > 0) {
+ int typeinfo, total, percentage;
+ GetICCounts(function->shared()->code(), &typeinfo, &total, &percentage);
+ PrintF(", ICs with typeinfo: %d/%d (%d%%)", typeinfo, total, percentage);
+ }
+ PrintF("]\n");
+ }
+
+ if (FLAG_parallel_recompilation && !isolate_->bootstrapper()->IsActive()) {
+ ASSERT(!function->IsMarkedForInstallingRecompiledCode());
+ ASSERT(!function->IsInRecompileQueue());
+ function->MarkForParallelRecompilation();
+ } else {
+ // The next call to the function will trigger optimization.
+ function->MarkForLazyRecompilation();
+ }
+}
+
+
+void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) {
+ // See AlwaysFullCompiler (in compiler.cc) comment on why we need
+ // Debug::has_break_points().
+ if (!FLAG_use_osr ||
+ isolate_->DebuggerHasBreakPoints() ||
+ function->IsBuiltin()) {
+ return;
+ }
+
+ SharedFunctionInfo* shared = function->shared();
+ // If the code is not optimizable, don't try OSR.
+ if (!shared->code()->optimizable()) return;
+
+ // We are not prepared to do OSR for a function that already has an
+ // allocated arguments object. The optimized code would bypass it for
+ // arguments accesses, which is unsound. Don't try OSR.
+ if (shared->uses_arguments()) return;
+
+ // We're using on-stack replacement: patch the unoptimized code so that
+ // any back edge in any unoptimized frame will trigger on-stack
+ // replacement for that frame.
+ if (FLAG_trace_osr) {
+ PrintF("[patching back edges in ");
+ function->PrintName();
+ PrintF(" for on-stack replacement]\n");
+ }
+
+ // Get the interrupt stub code object to match against. We aren't
+ // prepared to generate it, but we don't expect to have to.
+ Code* interrupt_code = NULL;
+ InterruptStub interrupt_stub;
+ bool found_code = interrupt_stub.FindCodeInCache(&interrupt_code, isolate_);
+ if (found_code) {
+ Code* replacement_code =
+ isolate_->builtins()->builtin(Builtins::kOnStackReplacement);
+ Code* unoptimized_code = shared->code();
+ Deoptimizer::PatchInterruptCode(
+ unoptimized_code, interrupt_code, replacement_code);
+ }
+}
+
+
+void RuntimeProfiler::ClearSampleBuffer() {
+ memset(sampler_window_, 0, sizeof(sampler_window_));
+ memset(sampler_window_weight_, 0, sizeof(sampler_window_weight_));
+}
+
+
+int RuntimeProfiler::LookupSample(JSFunction* function) {
+ int weight = 0;
+ for (int i = 0; i < kSamplerWindowSize; i++) {
+ Object* sample = sampler_window_[i];
+ if (sample != NULL) {
+ bool fits = FLAG_lookup_sample_by_shared
+ ? (function->shared() == JSFunction::cast(sample)->shared())
+ : (function == JSFunction::cast(sample));
+ if (fits) {
+ weight += sampler_window_weight_[i];
+ }
+ }
+ }
+ return weight;
+}
+
+
+void RuntimeProfiler::AddSample(JSFunction* function, int weight) {
+ ASSERT(IsPowerOf2(kSamplerWindowSize));
+ sampler_window_[sampler_window_position_] = function;
+ sampler_window_weight_[sampler_window_position_] = weight;
+ sampler_window_position_ = (sampler_window_position_ + 1) &
+ (kSamplerWindowSize - 1);
+}
+
+
+void RuntimeProfiler::OptimizeNow() {
+ HandleScope scope(isolate_);
+
+ if (isolate_->DebuggerHasBreakPoints()) return;
+
+ if (FLAG_parallel_recompilation) {
+ // Take this as opportunity to process the optimizing compiler thread's
+ // output queue so that it does not unnecessarily keep objects alive.
+ isolate_->optimizing_compiler_thread()->InstallOptimizedFunctions();
+ }
+
+ // Run through the JavaScript frames and collect them. If we already
+ // have a sample of the function, we mark it for optimizations
+ // (eagerly or lazily).
+ JSFunction* samples[kSamplerFrameCount];
+ int sample_count = 0;
+ int frame_count = 0;
+ int frame_count_limit = FLAG_watch_ic_patching ? FLAG_frame_count
+ : kSamplerFrameCount;
+ for (JavaScriptFrameIterator it(isolate_);
+ frame_count++ < frame_count_limit && !it.done();
+ it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+ JSFunction* function = frame->function();
+
+ 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;
+ }
+ }
+ }
+ }
+
+ SharedFunctionInfo* shared = function->shared();
+ Code* shared_code = shared->code();
+
+ if (shared_code->kind() != Code::FUNCTION) continue;
+ if (function->IsInRecompileQueue()) continue;
+
+ if (FLAG_always_osr &&
+ shared_code->allow_osr_at_loop_nesting_level() == 0) {
+ // Testing mode: always try an OSR compile for every function.
+ for (int i = 0; i < Code::kMaxLoopNestingMarker; i++) {
+ // TODO(titzer): fix AttemptOnStackReplacement to avoid this dumb loop.
+ shared_code->set_allow_osr_at_loop_nesting_level(i);
+ AttemptOnStackReplacement(function);
+ }
+ // Fall through and do a normal optimized compile as well.
+ } else if (!frame->is_optimized() &&
+ (function->IsMarkedForLazyRecompilation() ||
+ function->IsMarkedForParallelRecompilation() ||
+ function->IsOptimized())) {
+ // Attempt OSR if we are still running unoptimized code even though the
+ // the function has long been marked or even already been optimized.
+ int ticks = shared_code->profiler_ticks();
+ int allowance = kOSRCodeSizeAllowanceBase +
+ ticks * kOSRCodeSizeAllowancePerTick;
+ if (shared_code->CodeSize() > allowance) {
+ if (ticks < 255) shared_code->set_profiler_ticks(ticks + 1);
+ } else {
+ int nesting = shared_code->allow_osr_at_loop_nesting_level();
+ if (nesting < Code::kMaxLoopNestingMarker) {
+ int new_nesting = nesting + 1;
+ shared_code->set_allow_osr_at_loop_nesting_level(new_nesting);
+ AttemptOnStackReplacement(function);
+ }
+ }
+ continue;
+ }
+
+ // Only record top-level code on top of the execution stack and
+ // avoid optimizing excessively large scripts since top-level code
+ // will be executed only once.
+ const int kMaxToplevelSourceSize = 10 * 1024;
+ if (shared->is_toplevel() &&
+ (frame_count > 1 || shared->SourceSize() > kMaxToplevelSourceSize)) {
+ continue;
+ }
+
+ // Do not record non-optimizable functions.
+ if (shared->optimization_disabled()) {
+ if (shared->deopt_count() >= FLAG_max_opt_count) {
+ // If optimization was disabled due to many deoptimizations,
+ // then check if the function is hot and try to reenable optimization.
+ int ticks = shared_code->profiler_ticks();
+ if (ticks >= kProfilerTicksBeforeReenablingOptimization) {
+ shared_code->set_profiler_ticks(0);
+ shared->TryReenableOptimization();
+ } else {
+ shared_code->set_profiler_ticks(ticks + 1);
+ }
+ }
+ continue;
+ }
+ if (!function->IsOptimizable()) continue;
+
+ if (FLAG_watch_ic_patching) {
+ int ticks = shared_code->profiler_ticks();
+
+ if (ticks >= kProfilerTicksBeforeOptimization) {
+ int typeinfo, total, percentage;
+ GetICCounts(shared_code, &typeinfo, &total, &percentage);
+ if (percentage >= FLAG_type_info_threshold) {
+ // If this particular function hasn't had any ICs patched for enough
+ // ticks, optimize it now.
+ Optimize(function, "hot and stable");
+ } else if (ticks >= kTicksWhenNotEnoughTypeInfo) {
+ Optimize(function, "not much type info but very hot");
+ } else {
+ shared_code->set_profiler_ticks(ticks + 1);
+ if (FLAG_trace_opt_verbose) {
+ PrintF("[not yet optimizing ");
+ function->PrintName();
+ PrintF(", not enough type info: %d/%d (%d%%)]\n",
+ typeinfo, total, percentage);
+ }
+ }
+ } else if (!any_ic_changed_ &&
+ 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 {
+ shared_code->set_profiler_ticks(ticks + 1);
+ }
+ } else { // !FLAG_watch_ic_patching
+ 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;
+
+ if (LookupSample(function) >= threshold) {
+ Optimize(function, "sampler window lookup");
+ }
+ }
+ }
+ if (FLAG_watch_ic_patching) {
+ any_ic_changed_ = false;
+ } else { // !FLAG_watch_ic_patching
+ // 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]);
+ }
+ }
+}
+
+
+void RuntimeProfiler::SetUp() {
+ if (!FLAG_watch_ic_patching) {
+ ClearSampleBuffer();
+ }
+}
+
+
+void RuntimeProfiler::Reset() {
+ if (!FLAG_watch_ic_patching) {
+ sampler_threshold_ = kSamplerThresholdInit;
+ sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit;
+ sampler_ticks_until_threshold_adjustment_ =
+ kSamplerTicksBetweenThresholdAdjustment;
+ }
+}
+
+
+void RuntimeProfiler::TearDown() {
+ // Nothing to do.
+}
+
+
+// Update the pointers in the sampler window after a GC.
+void RuntimeProfiler::UpdateSamplesAfterScavenge() {
+ for (int i = 0; i < kSamplerWindowSize; i++) {
+ Object* function = sampler_window_[i];
+ if (function != NULL && isolate_->heap()->InNewSpace(function)) {
+ MapWord map_word = HeapObject::cast(function)->map_word();
+ if (map_word.IsForwardingAddress()) {
+ sampler_window_[i] = map_word.ToForwardingAddress();
+ } else {
+ sampler_window_[i] = NULL;
+ }
+ }
+ }
+}
+
+
+void RuntimeProfiler::RemoveDeadSamples() {
+ for (int i = 0; i < kSamplerWindowSize; i++) {
+ Object* function = sampler_window_[i];
+ if (function != NULL &&
+ !Marking::MarkBitFrom(HeapObject::cast(function)).Get()) {
+ sampler_window_[i] = NULL;
+ }
+ }
+}
+
+
+void RuntimeProfiler::UpdateSamplesAfterCompact(ObjectVisitor* visitor) {
+ for (int i = 0; i < kSamplerWindowSize; i++) {
+ visitor->VisitPointer(&sampler_window_[i]);
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/runtime-profiler.h b/chromium/v8/src/runtime-profiler.h
new file mode 100644
index 00000000000..28d6d322fd5
--- /dev/null
+++ b/chromium/v8/src/runtime-profiler.h
@@ -0,0 +1,93 @@
+// Copyright 2012 the V8 project authors. 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_RUNTIME_PROFILER_H_
+#define V8_RUNTIME_PROFILER_H_
+
+#include "allocation.h"
+#include "atomicops.h"
+
+namespace v8 {
+namespace internal {
+
+class Isolate;
+class JSFunction;
+class Object;
+class Semaphore;
+
+class RuntimeProfiler {
+ public:
+ explicit RuntimeProfiler(Isolate* isolate);
+
+ void OptimizeNow();
+
+ void SetUp();
+ void Reset();
+ void TearDown();
+
+ void NotifyICChanged() { any_ic_changed_ = true; }
+
+ // Rate limiting support.
+
+ void UpdateSamplesAfterScavenge();
+ void RemoveDeadSamples();
+ void UpdateSamplesAfterCompact(ObjectVisitor* visitor);
+
+ void AttemptOnStackReplacement(JSFunction* function);
+
+ private:
+ static const int kSamplerWindowSize = 16;
+
+ void Optimize(JSFunction* function, const char* reason);
+
+ void ClearSampleBuffer();
+
+ void ClearSampleBufferNewSpaceEntries();
+
+ int LookupSample(JSFunction* function);
+
+ void AddSample(JSFunction* function, int weight);
+
+ bool CodeSizeOKForOSR(Code* shared_code);
+
+ Isolate* isolate_;
+
+ int sampler_threshold_;
+ int sampler_threshold_size_factor_;
+ int sampler_ticks_until_threshold_adjustment_;
+
+ Object* sampler_window_[kSamplerWindowSize];
+ int sampler_window_position_;
+ int sampler_window_weight_[kSamplerWindowSize];
+
+ bool any_ic_changed_;
+ bool code_generated_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_RUNTIME_PROFILER_H_
diff --git a/chromium/v8/src/runtime.cc b/chromium/v8/src/runtime.cc
new file mode 100644
index 00000000000..10de6f9e5ec
--- /dev/null
+++ b/chromium/v8/src/runtime.cc
@@ -0,0 +1,14490 @@
+// Copyright 2012 the V8 project authors. 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 <limits>
+
+#include "v8.h"
+
+#include "accessors.h"
+#include "api.h"
+#include "arguments.h"
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "compilation-cache.h"
+#include "compiler.h"
+#include "cpu.h"
+#include "cpu-profiler.h"
+#include "dateparser-inl.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "date.h"
+#include "execution.h"
+#include "full-codegen.h"
+#include "global-handles.h"
+#include "isolate-inl.h"
+#include "jsregexp.h"
+#include "jsregexp-inl.h"
+#include "json-parser.h"
+#include "json-stringifier.h"
+#include "liveedit.h"
+#include "misc-intrinsics.h"
+#include "parser.h"
+#include "platform.h"
+#include "runtime-profiler.h"
+#include "runtime.h"
+#include "scopeinfo.h"
+#include "smart-pointers.h"
+#include "string-search.h"
+#include "stub-cache.h"
+#include "uri.h"
+#include "v8conversions.h"
+#include "v8threads.h"
+#include "vm-state-inl.h"
+
+#ifdef V8_I18N_SUPPORT
+#include "i18n.h"
+#include "unicode/brkiter.h"
+#include "unicode/calendar.h"
+#include "unicode/coll.h"
+#include "unicode/curramt.h"
+#include "unicode/datefmt.h"
+#include "unicode/dcfmtsym.h"
+#include "unicode/decimfmt.h"
+#include "unicode/dtfmtsym.h"
+#include "unicode/dtptngen.h"
+#include "unicode/locid.h"
+#include "unicode/numfmt.h"
+#include "unicode/numsys.h"
+#include "unicode/smpdtfmt.h"
+#include "unicode/timezone.h"
+#include "unicode/uchar.h"
+#include "unicode/ucol.h"
+#include "unicode/ucurr.h"
+#include "unicode/uloc.h"
+#include "unicode/unum.h"
+#include "unicode/uversion.h"
+#endif
+
+#ifndef _STLP_VENDOR_CSTD
+// STLPort doesn't import fpclassify and isless into the std namespace.
+using std::fpclassify;
+using std::isless;
+#endif
+
+namespace v8 {
+namespace internal {
+
+
+#define RUNTIME_ASSERT(value) \
+ if (!(value)) return isolate->ThrowIllegalOperation();
+
+// Cast the given object to a value of the specified type and store
+// it in a variable with the given name. If the object is not of the
+// expected type call IllegalOperation and return.
+#define CONVERT_ARG_CHECKED(Type, name, index) \
+ RUNTIME_ASSERT(args[index]->Is##Type()); \
+ Type* name = Type::cast(args[index]);
+
+#define CONVERT_ARG_HANDLE_CHECKED(Type, name, index) \
+ RUNTIME_ASSERT(args[index]->Is##Type()); \
+ Handle<Type> name = args.at<Type>(index);
+
+// Cast the given object to a boolean and store it in a variable with
+// the given name. If the object is not a boolean call IllegalOperation
+// and return.
+#define CONVERT_BOOLEAN_ARG_CHECKED(name, index) \
+ RUNTIME_ASSERT(args[index]->IsBoolean()); \
+ bool name = args[index]->IsTrue();
+
+// Cast the given argument to a Smi and store its value in an int variable
+// with the given name. If the argument is not a Smi call IllegalOperation
+// and return.
+#define CONVERT_SMI_ARG_CHECKED(name, index) \
+ RUNTIME_ASSERT(args[index]->IsSmi()); \
+ int name = args.smi_at(index);
+
+// Cast the given argument to a double and store it in a variable with
+// the given name. If the argument is not a number (as opposed to
+// the number not-a-number) call IllegalOperation and return.
+#define CONVERT_DOUBLE_ARG_CHECKED(name, index) \
+ RUNTIME_ASSERT(args[index]->IsNumber()); \
+ double name = args.number_at(index);
+
+// Call the specified converter on the object *comand store the result in
+// a variable of the specified type with the given name. If the
+// object is not a Number call IllegalOperation and return.
+#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
+ RUNTIME_ASSERT(obj->IsNumber()); \
+ type name = NumberTo##Type(obj);
+
+
+// Cast the given argument to PropertyDetails and store its value in a
+// variable with the given name. If the argument is not a Smi call
+// IllegalOperation and return.
+#define CONVERT_PROPERTY_DETAILS_CHECKED(name, index) \
+ RUNTIME_ASSERT(args[index]->IsSmi()); \
+ PropertyDetails name = PropertyDetails(Smi::cast(args[index]));
+
+
+// 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_CHECKED(name, index) \
+ RUNTIME_ASSERT(args[index]->IsSmi()); \
+ RUNTIME_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));
+
+
+static Handle<Map> ComputeObjectLiteralMap(
+ Handle<Context> context,
+ Handle<FixedArray> constant_properties,
+ bool* is_result_from_cache) {
+ Isolate* isolate = context->GetIsolate();
+ int properties_length = constant_properties->length();
+ int number_of_properties = properties_length / 2;
+ // Check that there are only internal strings and array indices among keys.
+ int number_of_string_keys = 0;
+ for (int p = 0; p != properties_length; p += 2) {
+ Object* key = constant_properties->get(p);
+ uint32_t element_index = 0;
+ if (key->IsInternalizedString()) {
+ number_of_string_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-internalized-string non-index key makes caching
+ // impossible.
+ // ASSERT to make sure that the if condition after the loop is false.
+ ASSERT(number_of_string_keys != number_of_properties);
+ break;
+ }
+ }
+ // If we only have internalized strings and array indices among keys then we
+ // can use the map cache in the native context.
+ const int kMaxKeys = 10;
+ if ((number_of_string_keys == number_of_properties) &&
+ (number_of_string_keys < kMaxKeys)) {
+ // Create the fixed array with the key.
+ Handle<FixedArray> keys =
+ isolate->factory()->NewFixedArray(number_of_string_keys);
+ if (number_of_string_keys > 0) {
+ int index = 0;
+ for (int p = 0; p < properties_length; p += 2) {
+ Object* key = constant_properties->get(p);
+ if (key->IsInternalizedString()) {
+ keys->set(index++, key);
+ }
+ }
+ ASSERT(index == number_of_string_keys);
+ }
+ *is_result_from_cache = true;
+ return isolate->factory()->ObjectLiteralMapFromCache(context, keys);
+ }
+ *is_result_from_cache = false;
+ return isolate->factory()->CopyMap(
+ Handle<Map>(context->object_function()->initial_map()),
+ number_of_properties);
+}
+
+
+static Handle<Object> CreateLiteralBoilerplate(
+ Isolate* isolate,
+ Handle<FixedArray> literals,
+ Handle<FixedArray> constant_properties);
+
+
+static Handle<Object> CreateObjectLiteralBoilerplate(
+ Isolate* isolate,
+ Handle<FixedArray> literals,
+ Handle<FixedArray> constant_properties,
+ bool should_have_fast_elements,
+ bool has_function_literal) {
+ // Get the native context from the literals array. This is the
+ // context in which the function was created and we use the object
+ // function from this context to create the object literal. We do
+ // not use the object function from the current native context
+ // because this might be the object function from another context
+ // which we should not have access to.
+ Handle<Context> context =
+ Handle<Context>(JSFunction::NativeContextFromLiterals(*literals));
+
+ // In case we have function literals, we want the object to be in
+ // slow properties mode for now. We don't go in the map cache because
+ // maps with constant functions can't be shared if the functions are
+ // not the same (which is the common case).
+ bool is_result_from_cache = false;
+ Handle<Map> map = has_function_literal
+ ? Handle<Map>(context->object_function()->initial_map())
+ : ComputeObjectLiteralMap(context,
+ constant_properties,
+ &is_result_from_cache);
+
+ Handle<JSObject> boilerplate =
+ isolate->factory()->NewJSObjectFromMap(
+ map, isolate->heap()->GetPretenureMode());
+
+ // Normalize the elements of the boilerplate to save space if needed.
+ if (!should_have_fast_elements) JSObject::NormalizeElements(boilerplate);
+
+ // Add the constant properties to the boilerplate.
+ int length = constant_properties->length();
+ bool should_transform =
+ !is_result_from_cache && boilerplate->HasFastProperties();
+ if (should_transform || has_function_literal) {
+ // Normalize the properties of object to avoid n^2 behavior
+ // when extending the object multiple properties. Indicate the number of
+ // properties to be added.
+ JSObject::NormalizeProperties(
+ boilerplate, KEEP_INOBJECT_PROPERTIES, length / 2);
+ }
+
+ // TODO(verwaest): Support tracking representations in the boilerplate.
+ for (int index = 0; index < length; index +=2) {
+ Handle<Object> key(constant_properties->get(index+0), isolate);
+ Handle<Object> value(constant_properties->get(index+1), isolate);
+ if (value->IsFixedArray()) {
+ // The value contains the constant_properties of a
+ // simple object or array literal.
+ Handle<FixedArray> array = Handle<FixedArray>::cast(value);
+ value = CreateLiteralBoilerplate(isolate, literals, array);
+ if (value.is_null()) return value;
+ }
+ Handle<Object> result;
+ uint32_t element_index = 0;
+ JSReceiver::StoreMode mode = value->IsJSObject()
+ ? JSReceiver::FORCE_FIELD
+ : JSReceiver::ALLOW_AS_CONSTANT;
+ if (key->IsInternalizedString()) {
+ if (Handle<String>::cast(key)->AsArrayIndex(&element_index)) {
+ // Array index as string (uint32).
+ result = JSObject::SetOwnElement(
+ boilerplate, element_index, value, kNonStrictMode);
+ } else {
+ Handle<String> name(String::cast(*key));
+ ASSERT(!name->AsArrayIndex(&element_index));
+ result = JSObject::SetLocalPropertyIgnoreAttributes(
+ boilerplate, name, value, NONE,
+ Object::OPTIMAL_REPRESENTATION, mode);
+ }
+ } else if (key->ToArrayIndex(&element_index)) {
+ // Array index (uint32).
+ result = JSObject::SetOwnElement(
+ boilerplate, element_index, value, kNonStrictMode);
+ } else {
+ // Non-uint32 number.
+ ASSERT(key->IsNumber());
+ double num = key->Number();
+ char arr[100];
+ Vector<char> buffer(arr, ARRAY_SIZE(arr));
+ const char* str = DoubleToCString(num, buffer);
+ Handle<String> name =
+ isolate->factory()->NewStringFromAscii(CStrVector(str));
+ result = JSObject::SetLocalPropertyIgnoreAttributes(
+ boilerplate, name, value, NONE,
+ Object::OPTIMAL_REPRESENTATION, mode);
+ }
+ // If setting the property on the boilerplate throws an
+ // exception, the exception is converted to an empty handle in
+ // the handle based operations. In that case, we need to
+ // convert back to an exception.
+ if (result.is_null()) return result;
+ }
+
+ // Transform to fast properties if necessary. For object literals with
+ // containing function literals we defer this operation until after all
+ // computed properties have been assigned so that we can generate
+ // constant function properties.
+ if (should_transform && !has_function_literal) {
+ JSObject::TransformToFastProperties(
+ boilerplate, boilerplate->map()->unused_property_fields());
+ }
+
+ return boilerplate;
+}
+
+
+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 kSmiLiteralMinimumLength = 1024;
+
+
+Handle<Object> Runtime::CreateArrayLiteralBoilerplate(
+ Isolate* isolate,
+ Handle<FixedArray> literals,
+ Handle<FixedArray> elements) {
+ // Create the JSArray.
+ Handle<JSFunction> constructor(
+ JSFunction::NativeContextFromLiterals(*literals)->array_function());
+
+ Handle<JSArray> object = Handle<JSArray>::cast(
+ isolate->factory()->NewJSObject(
+ constructor, isolate->heap()->GetPretenureMode()));
+
+ ElementsKind constant_elements_kind =
+ static_cast<ElementsKind>(Smi::cast(elements->get(0))->value());
+ Handle<FixedArrayBase> constant_elements_values(
+ FixedArrayBase::cast(elements->get(1)));
+
+ ASSERT(IsFastElementsKind(constant_elements_kind));
+ Context* native_context = isolate->context()->native_context();
+ Object* maybe_maps_array = native_context->js_array_maps();
+ ASSERT(!maybe_maps_array->IsUndefined());
+ Object* maybe_map = FixedArray::cast(maybe_maps_array)->get(
+ constant_elements_kind);
+ ASSERT(maybe_map->IsMap());
+ object->set_map(Map::cast(maybe_map));
+
+ Handle<FixedArrayBase> copied_elements_values;
+ if (IsFastDoubleElementsKind(constant_elements_kind)) {
+ ASSERT(FLAG_smi_only_arrays);
+ copied_elements_values = isolate->factory()->CopyFixedDoubleArray(
+ Handle<FixedDoubleArray>::cast(constant_elements_values));
+ } else {
+ ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind));
+ 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() < kSmiLiteralMinimumLength) {
+ ElementsKind elements_kind = object->GetElementsKind();
+ if (!IsFastObjectElementsKind(elements_kind)) {
+ if (IsFastHoleyElementsKind(elements_kind)) {
+ CHECK(!TransitionElements(object, FAST_HOLEY_ELEMENTS,
+ isolate)->IsFailure());
+ } else {
+ CHECK(!TransitionElements(object, FAST_ELEMENTS, isolate)->IsFailure());
+ }
+ }
+ }
+
+ object->ValidateElements();
+ return object;
+}
+
+
+static Handle<Object> CreateLiteralBoilerplate(
+ Isolate* isolate,
+ Handle<FixedArray> literals,
+ Handle<FixedArray> array) {
+ Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
+ const bool kHasNoFunctionLiteral = false;
+ switch (CompileTimeValue::GetLiteralType(array)) {
+ case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
+ return CreateObjectLiteralBoilerplate(isolate,
+ literals,
+ elements,
+ true,
+ kHasNoFunctionLiteral);
+ case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
+ return CreateObjectLiteralBoilerplate(isolate,
+ literals,
+ elements,
+ false,
+ kHasNoFunctionLiteral);
+ case CompileTimeValue::ARRAY_LITERAL:
+ return Runtime::CreateArrayLiteralBoilerplate(
+ isolate, literals, elements);
+ default:
+ UNREACHABLE();
+ return Handle<Object>::null();
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateObjectLiteral) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
+ CONVERT_SMI_ARG_CHECKED(literals_index, 1);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, constant_properties, 2);
+ CONVERT_SMI_ARG_CHECKED(flags, 3);
+ bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0;
+ bool has_function_literal = (flags & ObjectLiteral::kHasFunction) != 0;
+
+ // Check if boilerplate exists. If not, create it first.
+ Handle<Object> boilerplate(literals->get(literals_index), isolate);
+ if (*boilerplate == isolate->heap()->undefined_value()) {
+ boilerplate = CreateObjectLiteralBoilerplate(isolate,
+ literals,
+ constant_properties,
+ should_have_fast_elements,
+ has_function_literal);
+ RETURN_IF_EMPTY_HANDLE(isolate, boilerplate);
+ // Update the functions literal and return the boilerplate.
+ literals->set(literals_index, *boilerplate);
+ }
+ return JSObject::cast(*boilerplate)->DeepCopy(isolate);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateObjectLiteralShallow) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
+ CONVERT_SMI_ARG_CHECKED(literals_index, 1);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, constant_properties, 2);
+ CONVERT_SMI_ARG_CHECKED(flags, 3);
+ bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0;
+ bool has_function_literal = (flags & ObjectLiteral::kHasFunction) != 0;
+
+ // Check if boilerplate exists. If not, create it first.
+ Handle<Object> boilerplate(literals->get(literals_index), isolate);
+ if (*boilerplate == isolate->heap()->undefined_value()) {
+ boilerplate = CreateObjectLiteralBoilerplate(isolate,
+ literals,
+ constant_properties,
+ should_have_fast_elements,
+ has_function_literal);
+ RETURN_IF_EMPTY_HANDLE(isolate, boilerplate);
+ // Update the functions literal and return the boilerplate.
+ literals->set(literals_index, *boilerplate);
+ }
+ return isolate->heap()->CopyJSObject(JSObject::cast(*boilerplate));
+}
+
+
+static Handle<AllocationSite> GetLiteralAllocationSite(
+ Isolate* isolate,
+ Handle<FixedArray> literals,
+ int literals_index,
+ Handle<FixedArray> elements) {
+ // Check if boilerplate exists. If not, create it first.
+ Handle<Object> literal_site(literals->get(literals_index), isolate);
+ Handle<AllocationSite> site;
+ if (*literal_site == isolate->heap()->undefined_value()) {
+ ASSERT(*elements != isolate->heap()->empty_fixed_array());
+ Handle<Object> boilerplate =
+ Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements);
+ if (boilerplate.is_null()) return site;
+ site = isolate->factory()->NewAllocationSite();
+ site->set_transition_info(*boilerplate);
+ literals->set(literals_index, *site);
+ } else {
+ site = Handle<AllocationSite>::cast(literal_site);
+ }
+
+ return site;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteral) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
+ CONVERT_SMI_ARG_CHECKED(literals_index, 1);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2);
+
+ Handle<AllocationSite> site = GetLiteralAllocationSite(isolate, literals,
+ literals_index, elements);
+ RETURN_IF_EMPTY_HANDLE(isolate, site);
+
+ JSObject* boilerplate = JSObject::cast(site->transition_info());
+ return boilerplate->DeepCopy(isolate);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
+ CONVERT_SMI_ARG_CHECKED(literals_index, 1);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2);
+
+ Handle<AllocationSite> site = GetLiteralAllocationSite(isolate, literals,
+ literals_index, elements);
+ RETURN_IF_EMPTY_HANDLE(isolate, site);
+
+ JSObject* boilerplate = JSObject::cast(site->transition_info());
+ if (boilerplate->elements()->map() ==
+ isolate->heap()->fixed_cow_array_map()) {
+ isolate->counters()->cow_arrays_created_runtime()->Increment();
+ }
+
+ AllocationSiteMode mode = AllocationSite::GetMode(
+ boilerplate->GetElementsKind());
+ if (mode == TRACK_ALLOCATION_SITE) {
+ return isolate->heap()->CopyJSObjectWithAllocationSite(
+ boilerplate, *site);
+ }
+
+ return isolate->heap()->CopyJSObject(boilerplate);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateSymbol) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ Handle<Object> name(args[0], isolate);
+ RUNTIME_ASSERT(name->IsString() || name->IsUndefined());
+ Symbol* symbol;
+ MaybeObject* maybe = isolate->heap()->AllocateSymbol();
+ if (!maybe->To(&symbol)) return maybe;
+ if (name->IsString()) symbol->set_name(*name);
+ return symbol;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SymbolName) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(Symbol, symbol, 0);
+ return symbol->name();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSReceiver, handler, 0);
+ Object* prototype = args[1];
+ Object* used_prototype =
+ prototype->IsJSReceiver() ? prototype : isolate->heap()->null_value();
+ return isolate->heap()->AllocateJSProxy(handler, used_prototype);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSFunctionProxy) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_CHECKED(JSReceiver, handler, 0);
+ Object* call_trap = args[1];
+ RUNTIME_ASSERT(call_trap->IsJSFunction() || call_trap->IsJSFunctionProxy());
+ CONVERT_ARG_CHECKED(JSFunction, construct_trap, 2);
+ Object* prototype = args[3];
+ Object* used_prototype =
+ prototype->IsJSReceiver() ? prototype : isolate->heap()->null_value();
+ return isolate->heap()->AllocateJSFunctionProxy(
+ handler, call_trap, construct_trap, used_prototype);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSProxy) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ Object* obj = args[0];
+ return isolate->heap()->ToBoolean(obj->IsJSProxy());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSFunctionProxy) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ Object* obj = args[0];
+ return isolate->heap()->ToBoolean(obj->IsJSFunctionProxy());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSProxy, proxy, 0);
+ return proxy->handler();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetCallTrap) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunctionProxy, proxy, 0);
+ return proxy->call_trap();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetConstructTrap) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunctionProxy, proxy, 0);
+ return proxy->construct_trap();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSProxy, proxy, 0);
+ proxy->Fix();
+ return isolate->heap()->undefined_value();
+}
+
+
+void Runtime::FreeArrayBuffer(Isolate* isolate,
+ JSArrayBuffer* phantom_array_buffer) {
+ if (phantom_array_buffer->is_external()) return;
+
+ size_t allocated_length = NumberToSize(
+ isolate, phantom_array_buffer->byte_length());
+
+ isolate->heap()->AdjustAmountOfExternalAllocatedMemory(
+ -static_cast<intptr_t>(allocated_length));
+ CHECK(V8::ArrayBufferAllocator() != NULL);
+ V8::ArrayBufferAllocator()->Free(
+ phantom_array_buffer->backing_store(),
+ allocated_length);
+}
+
+
+void Runtime::SetupArrayBuffer(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ bool is_external,
+ void* data,
+ size_t allocated_length) {
+ ASSERT(array_buffer->GetInternalFieldCount() ==
+ v8::ArrayBuffer::kInternalFieldCount);
+ for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) {
+ array_buffer->SetInternalField(i, Smi::FromInt(0));
+ }
+ array_buffer->set_backing_store(data);
+ array_buffer->set_flag(Smi::FromInt(0));
+ array_buffer->set_is_external(is_external);
+
+ Handle<Object> byte_length =
+ isolate->factory()->NewNumberFromSize(allocated_length);
+ CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
+ array_buffer->set_byte_length(*byte_length);
+
+ array_buffer->set_weak_next(isolate->heap()->array_buffers_list());
+ isolate->heap()->set_array_buffers_list(*array_buffer);
+ array_buffer->set_weak_first_view(isolate->heap()->undefined_value());
+}
+
+
+bool Runtime::SetupArrayBufferAllocatingData(
+ Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ size_t allocated_length,
+ bool initialize) {
+ void* data;
+ CHECK(V8::ArrayBufferAllocator() != NULL);
+ if (allocated_length != 0) {
+ if (initialize) {
+ data = V8::ArrayBufferAllocator()->Allocate(allocated_length);
+ } else {
+ data =
+ V8::ArrayBufferAllocator()->AllocateUninitialized(allocated_length);
+ }
+ if (data == NULL) return false;
+ } else {
+ data = NULL;
+ }
+
+ SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length);
+
+ isolate->heap()->AdjustAmountOfExternalAllocatedMemory(allocated_length);
+
+ return true;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferInitialize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, byteLength, 1);
+ size_t allocated_length;
+ if (byteLength->IsSmi()) {
+ allocated_length = Smi::cast(*byteLength)->value();
+ } else {
+ ASSERT(byteLength->IsHeapNumber());
+ double value = HeapNumber::cast(*byteLength)->value();
+
+ ASSERT(value >= 0);
+
+ if (value > std::numeric_limits<size_t>::max()) {
+ return isolate->Throw(
+ *isolate->factory()->NewRangeError("invalid_array_buffer_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+
+ allocated_length = static_cast<size_t>(value);
+ }
+
+ if (!Runtime::SetupArrayBufferAllocatingData(isolate,
+ holder, allocated_length)) {
+ return isolate->Throw(*isolate->factory()->
+ NewRangeError("invalid_array_buffer_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+
+ return *holder;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferGetByteLength) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0);
+ return holder->byte_length();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferSliceImpl) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1);
+ CONVERT_DOUBLE_ARG_CHECKED(first, 2);
+ size_t start = static_cast<size_t>(first);
+ size_t target_length = NumberToSize(isolate, target->byte_length());
+
+ if (target_length == 0) return isolate->heap()->undefined_value();
+
+ ASSERT(NumberToSize(isolate, source->byte_length()) - target_length >= start);
+ uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
+ uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
+ CopyBytes(target_data, source_data + start, target_length);
+ return isolate->heap()->undefined_value();
+}
+
+
+enum TypedArrayId {
+ // arrayIds below should be synchromized with typedarray.js natives.
+ ARRAY_ID_UINT8 = 1,
+ ARRAY_ID_INT8 = 2,
+ ARRAY_ID_UINT16 = 3,
+ ARRAY_ID_INT16 = 4,
+ ARRAY_ID_UINT32 = 5,
+ ARRAY_ID_INT32 = 6,
+ ARRAY_ID_FLOAT32 = 7,
+ ARRAY_ID_FLOAT64 = 8,
+ ARRAY_ID_UINT8C = 9
+};
+
+static void ArrayIdToTypeAndSize(
+ int arrayId, ExternalArrayType* array_type, size_t* element_size) {
+ switch (arrayId) {
+ case ARRAY_ID_UINT8:
+ *array_type = kExternalUnsignedByteArray;
+ *element_size = 1;
+ break;
+ case ARRAY_ID_INT8:
+ *array_type = kExternalByteArray;
+ *element_size = 1;
+ break;
+ case ARRAY_ID_UINT16:
+ *array_type = kExternalUnsignedShortArray;
+ *element_size = 2;
+ break;
+ case ARRAY_ID_INT16:
+ *array_type = kExternalShortArray;
+ *element_size = 2;
+ break;
+ case ARRAY_ID_UINT32:
+ *array_type = kExternalUnsignedIntArray;
+ *element_size = 4;
+ break;
+ case ARRAY_ID_INT32:
+ *array_type = kExternalIntArray;
+ *element_size = 4;
+ break;
+ case ARRAY_ID_FLOAT32:
+ *array_type = kExternalFloatArray;
+ *element_size = 4;
+ break;
+ case ARRAY_ID_FLOAT64:
+ *array_type = kExternalDoubleArray;
+ *element_size = 8;
+ break;
+ case ARRAY_ID_UINT8C:
+ *array_type = kExternalPixelArray;
+ *element_size = 1;
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 5);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
+ CONVERT_SMI_ARG_CHECKED(arrayId, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 2);
+ CONVERT_ARG_HANDLE_CHECKED(Object, byte_offset_object, 3);
+ CONVERT_ARG_HANDLE_CHECKED(Object, byte_length_object, 4);
+
+ ASSERT(holder->GetInternalFieldCount() ==
+ v8::ArrayBufferView::kInternalFieldCount);
+ for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
+ holder->SetInternalField(i, Smi::FromInt(0));
+ }
+
+ ExternalArrayType array_type = kExternalByteArray; // Bogus initialization.
+ size_t element_size = 1; // Bogus initialization.
+ ArrayIdToTypeAndSize(arrayId, &array_type, &element_size);
+
+ holder->set_buffer(*buffer);
+ holder->set_byte_offset(*byte_offset_object);
+ holder->set_byte_length(*byte_length_object);
+
+ size_t byte_offset = NumberToSize(isolate, *byte_offset_object);
+ size_t byte_length = NumberToSize(isolate, *byte_length_object);
+ ASSERT(byte_length % element_size == 0);
+ size_t length = byte_length / element_size;
+
+ Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(length);
+ holder->set_length(*length_obj);
+ holder->set_weak_next(buffer->weak_first_view());
+ buffer->set_weak_first_view(*holder);
+
+ Handle<ExternalArray> elements =
+ isolate->factory()->NewExternalArray(
+ static_cast<int>(length), array_type,
+ static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
+ holder->set_elements(*elements);
+ return isolate->heap()->undefined_value();
+}
+
+
+// Initializes a typed array from an array-like object.
+// If an array-like object happens to be a typed array of the same type,
+// initializes backing store using memove.
+//
+// Returns true if backing store was initialized or false otherwise.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitializeFromArrayLike) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
+ CONVERT_SMI_ARG_CHECKED(arrayId, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, source, 2);
+ CONVERT_ARG_HANDLE_CHECKED(Object, length_obj, 3);
+
+ ASSERT(holder->GetInternalFieldCount() ==
+ v8::ArrayBufferView::kInternalFieldCount);
+ for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
+ holder->SetInternalField(i, Smi::FromInt(0));
+ }
+
+ ExternalArrayType array_type = kExternalByteArray; // Bogus initialization.
+ size_t element_size = 1; // Bogus initialization.
+ ArrayIdToTypeAndSize(arrayId, &array_type, &element_size);
+
+ Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
+ size_t length = NumberToSize(isolate, *length_obj);
+ size_t byte_length = length * element_size;
+ if (byte_length < length) { // Overflow
+ return isolate->Throw(*isolate->factory()->
+ NewRangeError("invalid_array_buffer_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+
+ // We assume that the caller of this function will initialize holder
+ // with the loop
+ // for(i = 0; i < length; i++) { holder[i] = source[i]; }
+ // If source is a typed array, this loop will always run to completion,
+ // so we are sure that the backing store will be initialized.
+ // Otherwise, we do not know (the indexing operation might throw).
+ // Hence we require zero initialization unless our source is a typed array.
+ bool should_zero_initialize = !source->IsJSTypedArray();
+
+ if (!Runtime::SetupArrayBufferAllocatingData(
+ isolate, buffer, byte_length, should_zero_initialize)) {
+ return isolate->Throw(*isolate->factory()->
+ NewRangeError("invalid_array_buffer_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+
+ holder->set_buffer(*buffer);
+ holder->set_byte_offset(Smi::FromInt(0));
+ Handle<Object> byte_length_obj(
+ isolate->factory()->NewNumberFromSize(byte_length));
+ holder->set_byte_length(*byte_length_obj);
+ holder->set_length(*length_obj);
+ holder->set_weak_next(buffer->weak_first_view());
+ buffer->set_weak_first_view(*holder);
+
+ Handle<ExternalArray> elements =
+ isolate->factory()->NewExternalArray(
+ static_cast<int>(length), array_type,
+ static_cast<uint8_t*>(buffer->backing_store()));
+ holder->set_elements(*elements);
+
+ if (source->IsJSTypedArray()) {
+ Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
+
+ if (typed_array->type() == holder->type()) {
+ uint8_t* backing_store =
+ static_cast<uint8_t*>(
+ JSArrayBuffer::cast(typed_array->buffer())->backing_store());
+ size_t source_byte_offset =
+ NumberToSize(isolate, typed_array->byte_offset());
+ OS::MemCopy(
+ buffer->backing_store(),
+ backing_store + source_byte_offset,
+ byte_length);
+ return *isolate->factory()->true_value();
+ } else {
+ return *isolate->factory()->false_value();
+ }
+ }
+
+ return *isolate->factory()->false_value();
+}
+
+
+#define TYPED_ARRAY_GETTER(getter, accessor) \
+ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayGet##getter) { \
+ HandleScope scope(isolate); \
+ ASSERT(args.length() == 1); \
+ CONVERT_ARG_HANDLE_CHECKED(Object, holder, 0); \
+ if (!holder->IsJSTypedArray()) \
+ return isolate->Throw(*isolate->factory()->NewTypeError( \
+ "not_typed_array", HandleVector<Object>(NULL, 0))); \
+ Handle<JSTypedArray> typed_array(JSTypedArray::cast(*holder)); \
+ return typed_array->accessor(); \
+ }
+
+TYPED_ARRAY_GETTER(Buffer, buffer)
+TYPED_ARRAY_GETTER(ByteLength, byte_length)
+TYPED_ARRAY_GETTER(ByteOffset, byte_offset)
+TYPED_ARRAY_GETTER(Length, length)
+
+#undef TYPED_ARRAY_GETTER
+
+// Return codes for Runtime_TypedArraySetFastCases.
+// Should be synchronized with typedarray.js natives.
+enum TypedArraySetResultCodes {
+ // Set from typed array of the same type.
+ // This is processed by TypedArraySetFastCases
+ TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0,
+ // Set from typed array of the different type, overlapping in memory.
+ TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1,
+ // Set from typed array of the different type, non-overlapping.
+ TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2,
+ // Set from non-typed array.
+ TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3
+};
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArraySetFastCases) {
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(Object, target_obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, source_obj, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, offset_obj, 2);
+
+ if (!target_obj->IsJSTypedArray())
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "not_typed_array", HandleVector<Object>(NULL, 0)));
+
+ if (!source_obj->IsJSTypedArray())
+ return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY);
+
+ Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj));
+ Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj));
+ size_t offset = NumberToSize(isolate, *offset_obj);
+ size_t target_length = NumberToSize(isolate, target->length());
+ size_t source_length = NumberToSize(isolate, source->length());
+ size_t target_byte_length = NumberToSize(isolate, target->byte_length());
+ size_t source_byte_length = NumberToSize(isolate, source->byte_length());
+ if (offset > target_length ||
+ offset + source_length > target_length ||
+ offset + source_length < offset) // overflow
+ return isolate->Throw(*isolate->factory()->NewRangeError(
+ "typed_array_set_source_too_large", HandleVector<Object>(NULL, 0)));
+
+ size_t target_offset = NumberToSize(isolate, target->byte_offset());
+ size_t source_offset = NumberToSize(isolate, source->byte_offset());
+ uint8_t* target_base =
+ static_cast<uint8_t*>(
+ JSArrayBuffer::cast(target->buffer())->backing_store()) + target_offset;
+ uint8_t* source_base =
+ static_cast<uint8_t*>(
+ JSArrayBuffer::cast(source->buffer())->backing_store()) + source_offset;
+
+ // Typed arrays of the same type: use memmove.
+ if (target->type() == source->type()) {
+ memmove(target_base + offset * target->element_size(),
+ source_base, source_byte_length);
+ return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE);
+ }
+
+ // Typed arrays of different types over the same backing store
+ if ((source_base <= target_base &&
+ source_base + source_byte_length > target_base) ||
+ (target_base <= source_base &&
+ target_base + target_byte_length > source_base)) {
+ // We do not support overlapping ArrayBuffers
+ ASSERT(
+ JSArrayBuffer::cast(target->buffer())->backing_store() ==
+ JSArrayBuffer::cast(source->buffer())->backing_store());
+ return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING);
+ } else { // Non-overlapping typed arrays
+ return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING);
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DataViewInitialize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, byte_offset, 2);
+ CONVERT_ARG_HANDLE_CHECKED(Object, byte_length, 3);
+
+ ASSERT(holder->GetInternalFieldCount() ==
+ v8::ArrayBufferView::kInternalFieldCount);
+ for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
+ holder->SetInternalField(i, Smi::FromInt(0));
+ }
+
+ holder->set_buffer(*buffer);
+ ASSERT(byte_offset->IsNumber());
+ ASSERT(
+ NumberToSize(isolate, buffer->byte_length()) >=
+ NumberToSize(isolate, *byte_offset)
+ + NumberToSize(isolate, *byte_length));
+ holder->set_byte_offset(*byte_offset);
+ ASSERT(byte_length->IsNumber());
+ holder->set_byte_length(*byte_length);
+
+ holder->set_weak_next(buffer->weak_first_view());
+ buffer->set_weak_first_view(*holder);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DataViewGetBuffer) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, data_view, 0);
+ return data_view->buffer();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DataViewGetByteOffset) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, data_view, 0);
+ return data_view->byte_offset();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DataViewGetByteLength) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, data_view, 0);
+ return data_view->byte_length();
+}
+
+
+inline static bool NeedToFlipBytes(bool is_little_endian) {
+#ifdef V8_TARGET_LITTLE_ENDIAN
+ return !is_little_endian;
+#else
+ return is_little_endian;
+#endif
+}
+
+
+template<int n>
+inline void CopyBytes(uint8_t* target, uint8_t* source) {
+ for (int i = 0; i < n; i++) {
+ *(target++) = *(source++);
+ }
+}
+
+
+template<int n>
+inline void FlipBytes(uint8_t* target, uint8_t* source) {
+ source = source + (n-1);
+ for (int i = 0; i < n; i++) {
+ *(target++) = *(source--);
+ }
+}
+
+
+template<typename T>
+inline static bool DataViewGetValue(
+ Isolate* isolate,
+ Handle<JSDataView> data_view,
+ Handle<Object> byte_offset_obj,
+ bool is_little_endian,
+ T* result) {
+ size_t byte_offset = NumberToSize(isolate, *byte_offset_obj);
+ Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
+
+ size_t data_view_byte_offset =
+ NumberToSize(isolate, data_view->byte_offset());
+ size_t data_view_byte_length =
+ NumberToSize(isolate, data_view->byte_length());
+ if (byte_offset + sizeof(T) > data_view_byte_length ||
+ byte_offset + sizeof(T) < byte_offset) { // overflow
+ return false;
+ }
+
+ union Value {
+ T data;
+ uint8_t bytes[sizeof(T)];
+ };
+
+ Value value;
+ size_t buffer_offset = data_view_byte_offset + byte_offset;
+ ASSERT(
+ NumberToSize(isolate, buffer->byte_length())
+ >= buffer_offset + sizeof(T));
+ uint8_t* source =
+ static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
+ if (NeedToFlipBytes(is_little_endian)) {
+ FlipBytes<sizeof(T)>(value.bytes, source);
+ } else {
+ CopyBytes<sizeof(T)>(value.bytes, source);
+ }
+ *result = value.data;
+ return true;
+}
+
+
+template<typename T>
+static bool DataViewSetValue(
+ Isolate* isolate,
+ Handle<JSDataView> data_view,
+ Handle<Object> byte_offset_obj,
+ bool is_little_endian,
+ T data) {
+ size_t byte_offset = NumberToSize(isolate, *byte_offset_obj);
+ Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(data_view->buffer()));
+
+ size_t data_view_byte_offset =
+ NumberToSize(isolate, data_view->byte_offset());
+ size_t data_view_byte_length =
+ NumberToSize(isolate, data_view->byte_length());
+ if (byte_offset + sizeof(T) > data_view_byte_length ||
+ byte_offset + sizeof(T) < byte_offset) { // overflow
+ return false;
+ }
+
+ union Value {
+ T data;
+ uint8_t bytes[sizeof(T)];
+ };
+
+ Value value;
+ value.data = data;
+ size_t buffer_offset = data_view_byte_offset + byte_offset;
+ ASSERT(
+ NumberToSize(isolate, buffer->byte_length())
+ >= buffer_offset + sizeof(T));
+ uint8_t* target =
+ static_cast<uint8_t*>(buffer->backing_store()) + buffer_offset;
+ if (NeedToFlipBytes(is_little_endian)) {
+ FlipBytes<sizeof(T)>(target, value.bytes);
+ } else {
+ CopyBytes<sizeof(T)>(target, value.bytes);
+ }
+ return true;
+}
+
+
+#define DATA_VIEW_GETTER(TypeName, Type, Converter) \
+ RUNTIME_FUNCTION(MaybeObject*, Runtime_DataViewGet##TypeName) { \
+ HandleScope scope(isolate); \
+ ASSERT(args.length() == 3); \
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
+ CONVERT_ARG_HANDLE_CHECKED(Object, offset, 1); \
+ CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 2); \
+ Type result; \
+ if (DataViewGetValue( \
+ isolate, holder, offset, is_little_endian, &result)) { \
+ return isolate->heap()->Converter(result); \
+ } else { \
+ return isolate->Throw(*isolate->factory()->NewRangeError( \
+ "invalid_data_view_accessor_offset", \
+ HandleVector<Object>(NULL, 0))); \
+ } \
+ }
+
+DATA_VIEW_GETTER(Uint8, uint8_t, NumberFromUint32)
+DATA_VIEW_GETTER(Int8, int8_t, NumberFromInt32)
+DATA_VIEW_GETTER(Uint16, uint16_t, NumberFromUint32)
+DATA_VIEW_GETTER(Int16, int16_t, NumberFromInt32)
+DATA_VIEW_GETTER(Uint32, uint32_t, NumberFromUint32)
+DATA_VIEW_GETTER(Int32, int32_t, NumberFromInt32)
+DATA_VIEW_GETTER(Float32, float, NumberFromDouble)
+DATA_VIEW_GETTER(Float64, double, NumberFromDouble)
+
+#undef DATA_VIEW_GETTER
+
+
+template <typename T>
+static T DataViewConvertValue(double value);
+
+
+template <>
+int8_t DataViewConvertValue<int8_t>(double value) {
+ return static_cast<int8_t>(DoubleToInt32(value));
+}
+
+
+template <>
+int16_t DataViewConvertValue<int16_t>(double value) {
+ return static_cast<int16_t>(DoubleToInt32(value));
+}
+
+
+template <>
+int32_t DataViewConvertValue<int32_t>(double value) {
+ return DoubleToInt32(value);
+}
+
+
+template <>
+uint8_t DataViewConvertValue<uint8_t>(double value) {
+ return static_cast<uint8_t>(DoubleToUint32(value));
+}
+
+
+template <>
+uint16_t DataViewConvertValue<uint16_t>(double value) {
+ return static_cast<uint16_t>(DoubleToUint32(value));
+}
+
+
+template <>
+uint32_t DataViewConvertValue<uint32_t>(double value) {
+ return DoubleToUint32(value);
+}
+
+
+template <>
+float DataViewConvertValue<float>(double value) {
+ return static_cast<float>(value);
+}
+
+
+template <>
+double DataViewConvertValue<double>(double value) {
+ return value;
+}
+
+
+#define DATA_VIEW_SETTER(TypeName, Type) \
+ RUNTIME_FUNCTION(MaybeObject*, Runtime_DataViewSet##TypeName) { \
+ HandleScope scope(isolate); \
+ ASSERT(args.length() == 4); \
+ CONVERT_ARG_HANDLE_CHECKED(JSDataView, holder, 0); \
+ CONVERT_ARG_HANDLE_CHECKED(Object, offset, 1); \
+ CONVERT_ARG_HANDLE_CHECKED(Object, value, 2); \
+ CONVERT_BOOLEAN_ARG_CHECKED(is_little_endian, 3); \
+ Type v = DataViewConvertValue<Type>(value->Number()); \
+ if (DataViewSetValue( \
+ isolate, holder, offset, is_little_endian, v)) { \
+ return isolate->heap()->undefined_value(); \
+ } else { \
+ return isolate->Throw(*isolate->factory()->NewRangeError( \
+ "invalid_data_view_accessor_offset", \
+ HandleVector<Object>(NULL, 0))); \
+ } \
+ }
+
+DATA_VIEW_SETTER(Uint8, uint8_t)
+DATA_VIEW_SETTER(Int8, int8_t)
+DATA_VIEW_SETTER(Uint16, uint16_t)
+DATA_VIEW_SETTER(Int16, int16_t)
+DATA_VIEW_SETTER(Uint32, uint32_t)
+DATA_VIEW_SETTER(Int32, int32_t)
+DATA_VIEW_SETTER(Float32, float)
+DATA_VIEW_SETTER(Float64, double)
+
+#undef DATA_VIEW_SETTER
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetInitialize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_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_HANDLE_CHECKED(JSSet, holder, 0);
+ Handle<Object> key(args[1], isolate);
+ Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table()));
+ table = ObjectHashSetAdd(table, key);
+ holder->set_table(*table);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetHas) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
+ Handle<Object> key(args[1], isolate);
+ 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_HANDLE_CHECKED(JSSet, holder, 0);
+ Handle<Object> key(args[1], isolate);
+ Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table()));
+ table = ObjectHashSetRemove(table, key);
+ holder->set_table(*table);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetGetSize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0);
+ Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table()));
+ return Smi::FromInt(table->NumberOfElements());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MapInitialize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_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_HANDLE_CHECKED(JSMap, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
+ Handle<Object> lookup(table->Lookup(*key), isolate);
+ return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MapHas) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
+ Handle<Object> lookup(table->Lookup(*key), isolate);
+ return isolate->heap()->ToBoolean(!lookup->IsTheHole());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MapDelete) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
+ Handle<Object> lookup(table->Lookup(*key), isolate);
+ Handle<ObjectHashTable> new_table =
+ PutIntoObjectHashTable(table, key, isolate->factory()->the_hole_value());
+ holder->set_table(*new_table);
+ return isolate->heap()->ToBoolean(!lookup->IsTheHole());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MapSet) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
+ Handle<ObjectHashTable> new_table = PutIntoObjectHashTable(table, key, value);
+ holder->set_table(*new_table);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGetSize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0);
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table()));
+ return Smi::FromInt(table->NumberOfElements());
+}
+
+
+static JSWeakCollection* WeakCollectionInitialize(Isolate* isolate,
+ Handle<JSWeakCollection> weak_collection) {
+ ASSERT(weak_collection->map()->inobject_properties() == 0);
+ Handle<ObjectHashTable> table = isolate->factory()->NewObjectHashTable(0);
+ weak_collection->set_table(*table);
+ weak_collection->set_next(Smi::FromInt(0));
+ return *weak_collection;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionInitialize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ return WeakCollectionInitialize(isolate, weak_collection);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionGet) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<ObjectHashTable> table(
+ ObjectHashTable::cast(weak_collection->table()));
+ Handle<Object> lookup(table->Lookup(*key), isolate);
+ return lookup->IsTheHole() ? isolate->heap()->undefined_value() : *lookup;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionHas) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<ObjectHashTable> table(
+ ObjectHashTable::cast(weak_collection->table()));
+ Handle<Object> lookup(table->Lookup(*key), isolate);
+ return isolate->heap()->ToBoolean(!lookup->IsTheHole());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionDelete) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(
+ weak_collection->table()));
+ Handle<Object> lookup(table->Lookup(*key), isolate);
+ Handle<ObjectHashTable> new_table =
+ PutIntoObjectHashTable(table, key, isolate->factory()->the_hole_value());
+ weak_collection->set_table(*new_table);
+ return isolate->heap()->ToBoolean(!lookup->IsTheHole());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakCollectionSet) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSWeakCollection, weak_collection, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
+ Handle<Object> value(args[2], isolate);
+ Handle<ObjectHashTable> table(
+ ObjectHashTable::cast(weak_collection->table()));
+ Handle<ObjectHashTable> new_table = PutIntoObjectHashTable(table, key, value);
+ weak_collection->set_table(*new_table);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ Object* obj = args[0];
+ if (!obj->IsJSObject()) return isolate->heap()->null_value();
+ return JSObject::cast(obj)->class_name();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPrototype) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(Object, obj, 0);
+ // We don't expect access checks to be needed on JSProxy objects.
+ ASSERT(!obj->IsAccessCheckNeeded() || obj->IsJSObject());
+ do {
+ if (obj->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(JSObject::cast(obj),
+ isolate->heap()->proto_string(),
+ v8::ACCESS_GET)) {
+ isolate->ReportFailedAccessCheck(JSObject::cast(obj), v8::ACCESS_GET);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return isolate->heap()->undefined_value();
+ }
+ obj = obj->GetPrototype(isolate);
+ } while (obj->IsJSObject() &&
+ JSObject::cast(obj)->map()->is_hidden_prototype());
+ return obj;
+}
+
+
+static inline Object* GetPrototypeSkipHiddenPrototypes(Isolate* isolate,
+ Object* receiver) {
+ Object* current = receiver->GetPrototype(isolate);
+ while (current->IsJSObject() &&
+ JSObject::cast(current)->map()->is_hidden_prototype()) {
+ current = current->GetPrototype(isolate);
+ }
+ return current;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetPrototype) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1);
+ if (FLAG_harmony_observation && obj->map()->is_observed()) {
+ Handle<Object> old_value(
+ GetPrototypeSkipHiddenPrototypes(isolate, *obj), isolate);
+
+ Handle<Object> result = JSObject::SetPrototype(obj, prototype, true);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+
+ Handle<Object> new_value(
+ GetPrototypeSkipHiddenPrototypes(isolate, *obj), isolate);
+ if (!new_value->SameValue(*old_value)) {
+ JSObject::EnqueueChangeRecord(obj, "prototype",
+ isolate->factory()->proto_string(),
+ old_value);
+ }
+ return *result;
+ }
+ Handle<Object> result = JSObject::SetPrototype(obj, prototype, true);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsInPrototypeChain) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
+ Object* O = args[0];
+ Object* V = args[1];
+ while (true) {
+ Object* prototype = V->GetPrototype(isolate);
+ if (prototype->IsNull()) return isolate->heap()->false_value();
+ if (O == prototype) return isolate->heap()->true_value();
+ V = prototype;
+ }
+}
+
+
+static bool CheckAccessException(Object* callback,
+ v8::AccessType access_type) {
+ if (callback->IsAccessorInfo()) {
+ AccessorInfo* info = AccessorInfo::cast(callback);
+ return
+ (access_type == v8::ACCESS_HAS &&
+ (info->all_can_read() || info->all_can_write())) ||
+ (access_type == v8::ACCESS_GET && info->all_can_read()) ||
+ (access_type == v8::ACCESS_SET && info->all_can_write());
+ }
+ return false;
+}
+
+
+template<class Key>
+static bool CheckGenericAccess(
+ JSObject* receiver,
+ JSObject* holder,
+ Key key,
+ v8::AccessType access_type,
+ bool (Isolate::*mayAccess)(JSObject*, Key, v8::AccessType)) {
+ Isolate* isolate = receiver->GetIsolate();
+ for (JSObject* current = receiver;
+ true;
+ current = JSObject::cast(current->GetPrototype())) {
+ if (current->IsAccessCheckNeeded() &&
+ !(isolate->*mayAccess)(current, key, access_type)) {
+ return false;
+ }
+ if (current == holder) break;
+ }
+ return true;
+}
+
+
+enum AccessCheckResult {
+ ACCESS_FORBIDDEN,
+ ACCESS_ALLOWED,
+ ACCESS_ABSENT
+};
+
+
+static AccessCheckResult CheckPropertyAccess(
+ JSObject* obj,
+ Name* name,
+ v8::AccessType access_type) {
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ // TODO(1095): we should traverse hidden prototype hierachy as well.
+ if (CheckGenericAccess(
+ obj, obj, index, access_type, &Isolate::MayIndexedAccess)) {
+ return ACCESS_ALLOWED;
+ }
+
+ obj->GetIsolate()->ReportFailedAccessCheck(obj, access_type);
+ return ACCESS_FORBIDDEN;
+ }
+
+ LookupResult lookup(obj->GetIsolate());
+ obj->LocalLookup(name, &lookup, true);
+
+ if (!lookup.IsProperty()) return ACCESS_ABSENT;
+ if (CheckGenericAccess<Object*>(
+ obj, lookup.holder(), name, access_type, &Isolate::MayNamedAccess)) {
+ return ACCESS_ALLOWED;
+ }
+
+ // Access check callback denied the access, but some properties
+ // can have a special permissions which override callbacks descision
+ // (currently see v8::AccessControl).
+ // API callbacks can have per callback access exceptions.
+ switch (lookup.type()) {
+ case CALLBACKS:
+ if (CheckAccessException(lookup.GetCallbackObject(), access_type)) {
+ return ACCESS_ALLOWED;
+ }
+ break;
+ case INTERCEPTOR:
+ // If the object has an interceptor, try real named properties.
+ // Overwrite the result to fetch the correct property later.
+ lookup.holder()->LookupRealNamedProperty(name, &lookup);
+ if (lookup.IsProperty() && lookup.IsPropertyCallbacks()) {
+ if (CheckAccessException(lookup.GetCallbackObject(), access_type)) {
+ return ACCESS_ALLOWED;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ obj->GetIsolate()->ReportFailedAccessCheck(obj, access_type);
+ return ACCESS_FORBIDDEN;
+}
+
+
+// Enumerator used as indices into the array returned from GetOwnProperty
+enum PropertyDescriptorIndices {
+ IS_ACCESSOR_INDEX,
+ VALUE_INDEX,
+ GETTER_INDEX,
+ SETTER_INDEX,
+ WRITABLE_INDEX,
+ ENUMERABLE_INDEX,
+ CONFIGURABLE_INDEX,
+ DESCRIPTOR_SIZE
+};
+
+
+static MaybeObject* GetOwnProperty(Isolate* isolate,
+ Handle<JSObject> obj,
+ Handle<Name> name) {
+ Heap* heap = isolate->heap();
+ // Due to some WebKit tests, we want to make sure that we do not log
+ // more than one access failure here.
+ AccessCheckResult access_check_result =
+ CheckPropertyAccess(*obj, *name, v8::ACCESS_HAS);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ switch (access_check_result) {
+ case ACCESS_FORBIDDEN: return heap->false_value();
+ case ACCESS_ALLOWED: break;
+ case ACCESS_ABSENT: return heap->undefined_value();
+ }
+
+ PropertyAttributes attrs = obj->GetLocalPropertyAttribute(*name);
+ if (attrs == ABSENT) {
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return heap->undefined_value();
+ }
+ ASSERT(!isolate->has_scheduled_exception());
+ AccessorPair* raw_accessors = obj->GetLocalPropertyAccessorPair(*name);
+ Handle<AccessorPair> accessors(raw_accessors, isolate);
+
+ Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE);
+ elms->set(ENUMERABLE_INDEX, heap->ToBoolean((attrs & DONT_ENUM) == 0));
+ elms->set(CONFIGURABLE_INDEX, heap->ToBoolean((attrs & DONT_DELETE) == 0));
+ elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(raw_accessors != NULL));
+
+ if (raw_accessors == NULL) {
+ elms->set(WRITABLE_INDEX, heap->ToBoolean((attrs & READ_ONLY) == 0));
+ // GetProperty does access check.
+ Handle<Object> value = GetProperty(isolate, obj, name);
+ RETURN_IF_EMPTY_HANDLE(isolate, value);
+ elms->set(VALUE_INDEX, *value);
+ } else {
+ // Access checks are performed for both accessors separately.
+ // When they fail, the respective field is not set in the descriptor.
+ Object* getter = accessors->GetComponent(ACCESSOR_GETTER);
+ Object* setter = accessors->GetComponent(ACCESSOR_SETTER);
+ if (!getter->IsMap() && CheckPropertyAccess(*obj, *name, v8::ACCESS_GET)) {
+ ASSERT(!isolate->has_scheduled_exception());
+ elms->set(GETTER_INDEX, getter);
+ } else {
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ }
+ if (!setter->IsMap() && CheckPropertyAccess(*obj, *name, v8::ACCESS_SET)) {
+ ASSERT(!isolate->has_scheduled_exception());
+ elms->set(SETTER_INDEX, setter);
+ } else {
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ }
+ }
+
+ return *isolate->factory()->NewJSArrayWithElements(elms);
+}
+
+
+// Returns an array with the property description:
+// if args[1] is not a property on args[0]
+// returns undefined
+// if args[1] is a data property on args[0]
+// [false, value, Writeable, Enumerable, Configurable]
+// if args[1] is an accessor on args[0]
+// [true, GetFunction, SetFunction, Enumerable, Configurable]
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+ return GetOwnProperty(isolate, obj, name);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PreventExtensions) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSObject, obj, 0);
+ return obj->PreventExtensions();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsExtensible) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSObject, obj, 0);
+ if (obj->IsJSGlobalProxy()) {
+ Object* proto = obj->GetPrototype();
+ if (proto->IsNull()) return isolate->heap()->false_value();
+ ASSERT(proto->IsJSGlobalObject());
+ obj = JSObject::cast(proto);
+ }
+ return isolate->heap()->ToBoolean(obj->map()->is_extensible());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpCompile) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, re, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, flags, 2);
+ Handle<Object> result =
+ RegExpImpl::Compile(re, pattern, flags);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateApiFunction) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(FunctionTemplateInfo, data, 0);
+ return *isolate->factory()->CreateApiFunction(data);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsTemplate) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ Object* arg = args[0];
+ bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
+ return isolate->heap()->ToBoolean(result);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetTemplateField) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(HeapObject, templ, 0);
+ CONVERT_SMI_ARG_CHECKED(index, 1)
+ int offset = index * kPointerSize + HeapObject::kHeaderSize;
+ InstanceType type = templ->map()->instance_type();
+ RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
+ type == OBJECT_TEMPLATE_INFO_TYPE);
+ RUNTIME_ASSERT(offset > 0);
+ if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
+ RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
+ } else {
+ RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
+ }
+ return *HeapObject::RawField(templ, offset);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DisableAccessChecks) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(HeapObject, object, 0);
+ Map* old_map = object->map();
+ bool needs_access_checks = old_map->is_access_check_needed();
+ if (needs_access_checks) {
+ // Copy map so it won't interfere constructor's initial map.
+ Map* new_map;
+ MaybeObject* maybe_new_map = old_map->Copy();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ new_map->set_is_access_check_needed(false);
+ object->set_map(new_map);
+ }
+ return isolate->heap()->ToBoolean(needs_access_checks);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_EnableAccessChecks) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(HeapObject, object, 0);
+ Map* old_map = object->map();
+ if (!old_map->is_access_check_needed()) {
+ // Copy map so it won't interfere constructor's initial map.
+ Map* new_map;
+ MaybeObject* maybe_new_map = old_map->Copy();
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+
+ new_map->set_is_access_check_needed(true);
+ object->set_map(new_map);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+static Failure* ThrowRedeclarationError(Isolate* isolate,
+ const char* type,
+ Handle<String> name) {
+ HandleScope scope(isolate);
+ Handle<Object> type_handle =
+ isolate->factory()->NewStringFromAscii(CStrVector(type));
+ Handle<Object> args[2] = { type_handle, name };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("redeclaration", HandleVector(args, 2));
+ return isolate->Throw(*error);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ Handle<GlobalObject> global = Handle<GlobalObject>(
+ isolate->context()->global_object());
+
+ Handle<Context> context = args.at<Context>(0);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, pairs, 1);
+ CONVERT_SMI_ARG_CHECKED(flags, 2);
+
+ // Traverse the name/value pairs and set the properties.
+ int length = pairs->length();
+ for (int i = 0; i < length; i += 2) {
+ HandleScope scope(isolate);
+ Handle<String> name(String::cast(pairs->get(i)));
+ Handle<Object> value(pairs->get(i + 1), isolate);
+
+ // We have to declare a global const property. To capture we only
+ // assign to it when evaluating the assignment for "const x =
+ // <expr>" the initial value is the hole.
+ bool is_var = value->IsUndefined();
+ bool is_const = value->IsTheHole();
+ bool is_function = value->IsSharedFunctionInfo();
+ ASSERT(is_var + is_const + is_function == 1);
+
+ if (is_var || is_const) {
+ // Lookup the property in the global object, and don't set the
+ // value of the variable if the property is already there.
+ // Do the lookup locally only, see ES5 erratum.
+ LookupResult lookup(isolate);
+ if (FLAG_es52_globals) {
+ global->LocalLookup(*name, &lookup, true);
+ } else {
+ global->Lookup(*name, &lookup);
+ }
+ if (lookup.IsFound()) {
+ // We found an existing property. Unless it was an interceptor
+ // that claims the property is absent, skip this declaration.
+ if (!lookup.IsInterceptor()) continue;
+ PropertyAttributes attributes = global->GetPropertyAttribute(*name);
+ if (attributes != ABSENT) continue;
+ // Fall-through and introduce the absent property by using
+ // SetProperty.
+ }
+ } else if (is_function) {
+ // Copy the function and update its context. Use it as value.
+ Handle<SharedFunctionInfo> shared =
+ Handle<SharedFunctionInfo>::cast(value);
+ Handle<JSFunction> function =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ shared, context, TENURED);
+ value = function;
+ }
+
+ LookupResult lookup(isolate);
+ global->LocalLookup(*name, &lookup, true);
+
+ // Compute the property attributes. According to ECMA-262,
+ // the property must be non-configurable except in eval.
+ int attr = NONE;
+ bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
+ if (!is_eval) {
+ attr |= DONT_DELETE;
+ }
+ bool is_native = DeclareGlobalsNativeFlag::decode(flags);
+ if (is_const || (is_native && is_function)) {
+ attr |= READ_ONLY;
+ }
+
+ LanguageMode language_mode = DeclareGlobalsLanguageMode::decode(flags);
+
+ if (!lookup.IsFound() || is_function) {
+ // If the local property exists, check that we can reconfigure it
+ // as required for function declarations.
+ if (lookup.IsFound() && lookup.IsDontDelete()) {
+ if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
+ lookup.IsPropertyCallbacks()) {
+ return ThrowRedeclarationError(isolate, "function", name);
+ }
+ // If the existing property is not configurable, keep its attributes.
+ attr = lookup.GetAttributes();
+ }
+ // Define or redefine own property.
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ global, name, value, static_cast<PropertyAttributes>(attr)));
+ } else {
+ // Do a [[Put]] on the existing (own) property.
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetProperty(
+ global, name, value, static_cast<PropertyAttributes>(attr),
+ language_mode == CLASSIC_MODE ? kNonStrictMode : kStrictMode));
+ }
+ }
+
+ ASSERT(!isolate->has_pending_exception());
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+
+ // Declarations are always made in a function or native 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);
+
+ int index;
+ PropertyAttributes attributes;
+ ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
+ BindingFlags binding_flags;
+ Handle<Object> holder =
+ context->Lookup(name, flags, &index, &attributes, &binding_flags);
+
+ if (attributes != ABSENT) {
+ // The name was declared before; check for conflicting re-declarations.
+ // Note: this is actually inconsistent with what happens for globals (where
+ // we silently ignore such declarations).
+ if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
+ // Functions are not read-only.
+ ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
+ const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
+ return ThrowRedeclarationError(isolate, type, name);
+ }
+
+ // Initialize it if necessary.
+ if (*initial_value != NULL) {
+ if (index >= 0) {
+ 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 in the context extension object of a
+ // function context or the global object of a native context.
+ Handle<JSObject> object = Handle<JSObject>::cast(holder);
+ RETURN_IF_EMPTY_HANDLE(
+ isolate,
+ 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 as a
+ // property of the the global object.
+ Handle<JSObject> object;
+ if (context->has_extension()) {
+ object = Handle<JSObject>(JSObject::cast(context->extension()));
+ } else {
+ // Context extension objects are allocated lazily.
+ ASSERT(context->IsFunctionContext());
+ object = isolate->factory()->NewJSObject(
+ isolate->context_extension_function());
+ context->set_extension(*object);
+ }
+ 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(!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
+ // there is a callback with that name in a prototype. It is
+ // allowed to introduce const variables in
+ // JSContextExtensionObjects. They are treated specially in
+ // SetProperty and no setters are invoked for those since they are
+ // not real JSObjects.
+ if (initial_value->IsTheHole() &&
+ !object->IsJSContextExtensionObject()) {
+ LookupResult lookup(isolate);
+ object->Lookup(*name, &lookup);
+ if (lookup.IsPropertyCallbacks()) {
+ return ThrowRedeclarationError(isolate, "const", name);
+ }
+ }
+ if (object->IsJSGlobalObject()) {
+ // Define own property on the global object.
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(object, name, value, mode));
+ } else {
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSReceiver::SetProperty(object, name, value, mode, kNonStrictMode));
+ }
+ }
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) {
+ SealHandleScope shs(isolate);
+ // args[0] == name
+ // args[1] == language_mode
+ // args[2] == value (optional)
+
+ // Determine if we need to assign to the variable if it already
+ // exists (based on the number of arguments).
+ RUNTIME_ASSERT(args.length() == 2 || args.length() == 3);
+ bool assign = args.length() == 3;
+
+ CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
+ GlobalObject* global = isolate->context()->global_object();
+ RUNTIME_ASSERT(args[1]->IsSmi());
+ 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.
+ PropertyAttributes attributes = DONT_DELETE;
+
+ // Lookup the property locally in the global object. If it isn't
+ // there, there is a property with this name in the prototype chain.
+ // We follow Safari and Firefox behavior and only set the property
+ // locally if there is an explicit initialization value that we have
+ // 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.
+ Object* object = global;
+ LookupResult lookup(isolate);
+ JSObject::cast(object)->LocalLookup(*name, &lookup, true);
+ if (lookup.IsInterceptor()) {
+ HandleScope handle_scope(isolate);
+ PropertyAttributes intercepted =
+ lookup.holder()->GetPropertyAttribute(*name);
+ if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
+ // Found an interceptor that's not read only.
+ if (assign) {
+ return lookup.holder()->SetProperty(
+ &lookup, *name, args[2], attributes, strict_mode_flag);
+ } else {
+ return isolate->heap()->undefined_value();
+ }
+ }
+ }
+
+ // Reload global in case the loop above performed a GC.
+ global = isolate->context()->global_object();
+ if (assign) {
+ return global->SetProperty(*name, args[2], attributes, strict_mode_flag);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) {
+ SealHandleScope shs(isolate);
+ // All constants are declared with an initial value. The name
+ // of the constant is the first argument and the initial value
+ // is the second.
+ RUNTIME_ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
+ Handle<Object> value = args.at<Object>(1);
+
+ // Get the current global object from top.
+ GlobalObject* global = isolate->context()->global_object();
+
+ // According to ECMA-262, section 12.2, page 62, the property must
+ // not be deletable. Since it's a const, it must be READ_ONLY too.
+ PropertyAttributes attributes =
+ static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
+
+ // Lookup the property locally in the global object. If it isn't
+ // there, we add the property and take special precautions to always
+ // 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(isolate);
+ global->LocalLookup(*name, &lookup);
+ if (!lookup.IsFound()) {
+ return global->SetLocalPropertyIgnoreAttributes(*name,
+ *value,
+ attributes);
+ }
+
+ if (!lookup.IsReadOnly()) {
+ // Restore global object from context (in case of GC) and continue
+ // with setting the value.
+ HandleScope handle_scope(isolate);
+ Handle<GlobalObject> global(isolate->context()->global_object());
+
+ // BUG 1213575: Handle the case where we have to set a read-only
+ // 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,
+ JSReceiver::SetProperty(global, name, value, attributes,
+ kNonStrictMode));
+ return *value;
+ }
+
+ // 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 is disallowed in strict mode).
+ if (lookup.IsField()) {
+ FixedArray* properties = global->properties();
+ int index = lookup.GetFieldIndex().field_index();
+ if (properties->get(index)->IsTheHole() || !lookup.IsReadOnly()) {
+ properties->set(index, *value);
+ }
+ } else if (lookup.IsNormal()) {
+ if (global->GetNormalizedProperty(&lookup)->IsTheHole() ||
+ !lookup.IsReadOnly()) {
+ HandleScope scope(isolate);
+ JSObject::SetNormalizedProperty(Handle<JSObject>(global), &lookup, value);
+ }
+ } else {
+ // Ignore re-initialization of constants that have already been
+ // assigned a constant value.
+ ASSERT(lookup.IsReadOnly() && lookup.IsConstant());
+ }
+
+ // Use the set value as the result of the operation.
+ return *value;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstContextSlot) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+
+ Handle<Object> value(args[0], isolate);
+ ASSERT(!value->IsTheHole());
+
+ // Initializations are always done in a function or native 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;
+ ContextLookupFlags flags = FOLLOW_CHAINS;
+ BindingFlags binding_flags;
+ Handle<Object> holder =
+ context->Lookup(name, flags, &index, &attributes, &binding_flags);
+
+ if (index >= 0) {
+ 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 as a property of the
+ // global object.
+ if (attributes == ABSENT) {
+ Handle<JSObject> global = Handle<JSObject>(
+ isolate->context()->global_object());
+ // Strict mode not needed (const disallowed in strict mode).
+ RETURN_IF_EMPTY_HANDLE(
+ isolate,
+ JSReceiver::SetProperty(global, name, value, NONE, kNonStrictMode));
+ return *value;
+ }
+
+ // 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 (*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
+
+ if (lookup.IsField()) {
+ FixedArray* properties = object->properties();
+ int index = lookup.GetFieldIndex().field_index();
+ if (properties->get(index)->IsTheHole()) {
+ properties->set(index, *value);
+ }
+ } else if (lookup.IsNormal()) {
+ if (object->GetNormalizedProperty(&lookup)->IsTheHole()) {
+ JSObject::SetNormalizedProperty(object, &lookup, value);
+ }
+ } else {
+ // We should not reach here. Any real, named property should be
+ // either a field or a dictionary slot.
+ UNREACHABLE();
+ }
+ } else {
+ // 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,
+ JSReceiver::SetProperty(object, name, value, attributes,
+ kNonStrictMode));
+ }
+ }
+
+ return *value;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*,
+ Runtime_OptimizeObjectForAddingMultipleProperties) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+ CONVERT_SMI_ARG_CHECKED(properties, 1);
+ if (object->HasFastProperties()) {
+ JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
+ }
+ return *object;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
+ // Due to the way the JS calls are constructed this must be less than the
+ // length of a string, i.e. it is always a Smi. We check anyway for security.
+ CONVERT_SMI_ARG_CHECKED(index, 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3);
+ RUNTIME_ASSERT(index >= 0);
+ RUNTIME_ASSERT(index <= subject->length());
+ isolate->counters()->regexp_entry_runtime()->Increment();
+ Handle<Object> result = RegExpImpl::Exec(regexp,
+ subject,
+ index,
+ last_match_info);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpConstructResult) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_SMI_ARG_CHECKED(elements_count, 0);
+ if (elements_count < 0 ||
+ elements_count > FixedArray::kMaxLength ||
+ !Smi::IsValid(elements_count)) {
+ return isolate->ThrowIllegalOperation();
+ }
+ Object* new_object;
+ { MaybeObject* maybe_new_object =
+ isolate->heap()->AllocateFixedArrayWithHoles(elements_count);
+ if (!maybe_new_object->ToObject(&new_object)) return maybe_new_object;
+ }
+ FixedArray* elements = FixedArray::cast(new_object);
+ { MaybeObject* maybe_new_object = isolate->heap()->AllocateRaw(
+ JSRegExpResult::kSize, NEW_SPACE, OLD_POINTER_SPACE);
+ if (!maybe_new_object->ToObject(&new_object)) return maybe_new_object;
+ }
+ {
+ DisallowHeapAllocation no_gc;
+ HandleScope scope(isolate);
+ reinterpret_cast<HeapObject*>(new_object)->
+ set_map(isolate->native_context()->regexp_result_map());
+ }
+ JSArray* array = JSArray::cast(new_object);
+ array->set_properties(isolate->heap()->empty_fixed_array());
+ array->set_elements(elements);
+ array->set_length(Smi::FromInt(elements_count));
+ // Write in-object properties after the length of the array.
+ array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]);
+ array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]);
+ return array;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpInitializeObject) {
+ SealHandleScope shs(isolate);
+ DisallowHeapAllocation no_allocation;
+ ASSERT(args.length() == 5);
+ CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
+ CONVERT_ARG_CHECKED(String, source, 1);
+ // If source is the empty string we set it to "(?:)" instead as
+ // suggested by ECMA-262, 5th, section 15.10.4.1.
+ if (source->length() == 0) source = isolate->heap()->query_colon_string();
+
+ Object* global = args[2];
+ if (!global->IsTrue()) global = isolate->heap()->false_value();
+
+ Object* ignoreCase = args[3];
+ if (!ignoreCase->IsTrue()) ignoreCase = isolate->heap()->false_value();
+
+ Object* multiline = args[4];
+ if (!multiline->IsTrue()) multiline = isolate->heap()->false_value();
+
+ Map* map = regexp->map();
+ Object* constructor = map->constructor();
+ if (constructor->IsJSFunction() &&
+ JSFunction::cast(constructor)->initial_map() == map) {
+ // If we still have the original map, set in-object properties directly.
+ regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source);
+ // 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);
+ return regexp;
+ }
+
+ // Map has changed, so use generic, but slower, method.
+ PropertyAttributes final =
+ static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE);
+ PropertyAttributes writable =
+ static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
+ Heap* heap = isolate->heap();
+ MaybeObject* result;
+ result = regexp->SetLocalPropertyIgnoreAttributes(heap->source_string(),
+ source,
+ final);
+ // TODO(jkummerow): Turn these back into ASSERTs when we can be certain
+ // that it never fires in Release mode in the wild.
+ CHECK(!result->IsFailure());
+ result = regexp->SetLocalPropertyIgnoreAttributes(heap->global_string(),
+ global,
+ final);
+ CHECK(!result->IsFailure());
+ result =
+ regexp->SetLocalPropertyIgnoreAttributes(heap->ignore_case_string(),
+ ignoreCase,
+ final);
+ CHECK(!result->IsFailure());
+ result = regexp->SetLocalPropertyIgnoreAttributes(heap->multiline_string(),
+ multiline,
+ final);
+ CHECK(!result->IsFailure());
+ result =
+ regexp->SetLocalPropertyIgnoreAttributes(heap->last_index_string(),
+ Smi::FromInt(0),
+ writable);
+ CHECK(!result->IsFailure());
+ USE(result);
+ return regexp;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FinishArrayPrototypeSetup) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0);
+ // This is necessary to enable fast checks for absence of elements
+ // on Array.prototype and below.
+ prototype->set_elements(isolate->heap()->empty_fixed_array());
+ return Smi::FromInt(0);
+}
+
+
+static Handle<JSFunction> InstallBuiltin(Isolate* isolate,
+ Handle<JSObject> holder,
+ const char* name,
+ Builtins::Name builtin_name) {
+ Handle<String> key = isolate->factory()->InternalizeUtf8String(name);
+ Handle<Code> code(isolate->builtins()->builtin(builtin_name));
+ Handle<JSFunction> optimized =
+ isolate->factory()->NewFunction(key,
+ JS_OBJECT_TYPE,
+ JSObject::kHeaderSize,
+ code,
+ false);
+ optimized->shared()->DontAdaptArguments();
+ JSReceiver::SetProperty(holder, key, optimized, NONE, kStrictMode);
+ return optimized;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SpecialArrayFunctions) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, holder, 0);
+
+ InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop);
+ InstallBuiltin(isolate, holder, "push", Builtins::kArrayPush);
+ InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift);
+ InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift);
+ InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
+ InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
+ InstallBuiltin(isolate, holder, "concat", Builtins::kArrayConcat);
+
+ return *holder;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsClassicModeFunction) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSReceiver, callable, 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();
+ return isolate->heap()->ToBoolean(shared->is_classic_mode());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetDefaultReceiver) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSReceiver, callable, 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->is_classic_mode()) {
+ return isolate->heap()->undefined_value();
+ }
+ // Returns undefined for strict or native functions, or
+ // the associated global receiver for "normal" functions.
+
+ Context* native_context =
+ function->context()->global_object()->native_context();
+ return native_context->global_object()->global_receiver();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MaterializeRegExpLiteral) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0);
+ int index = args.smi_at(1);
+ Handle<String> pattern = args.at<String>(2);
+ Handle<String> flags = args.at<String>(3);
+
+ // Get the RegExp function from the context in the literals array.
+ // This is the RegExp function from the context in which the
+ // function was created. We do not use the RegExp function from the
+ // current native context because this might be the RegExp function
+ // from another context which we should not have access to.
+ Handle<JSFunction> constructor =
+ Handle<JSFunction>(
+ JSFunction::NativeContextFromLiterals(*literals)->regexp_function());
+ // Compute the regular expression literal.
+ bool has_pending_exception;
+ Handle<Object> regexp =
+ RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
+ &has_pending_exception);
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+ literals->set(index, *regexp);
+ return *regexp;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetName) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ return f->shared()->name();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetName) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ CONVERT_ARG_CHECKED(String, name, 1);
+ f->shared()->set_name(name);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionNameShouldPrintAsAnonymous) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ return isolate->heap()->ToBoolean(
+ f->shared()->name_should_print_as_anonymous());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionMarkNameShouldPrintAsAnonymous) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ f->shared()->set_name_should_print_as_anonymous(true);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionIsGenerator) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ return isolate->heap()->ToBoolean(f->shared()->is_generator());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionRemovePrototype) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ f->RemovePrototype();
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetScript) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, fun, 0);
+ Handle<Object> script = Handle<Object>(fun->shared()->script(), isolate);
+ if (!script->IsScript()) return isolate->heap()->undefined_value();
+
+ return *GetScriptWrapper(Handle<Script>::cast(script));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetSourceCode) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, f, 0);
+ Handle<SharedFunctionInfo> shared(f->shared());
+ return *shared->GetSourceCode();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetScriptSourcePosition) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, fun, 0);
+ int pos = fun->shared()->start_position();
+ return Smi::FromInt(pos);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetPositionForOffset) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(Code, code, 0);
+ CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
+
+ RUNTIME_ASSERT(0 <= offset && offset < code->Size());
+
+ Address pc = code->address() + offset;
+ return Smi::FromInt(code->SourcePosition(pc));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetInstanceClassName) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(JSFunction, fun, 0);
+ CONVERT_ARG_CHECKED(String, name, 1);
+ fun->SetInstanceClassName(name);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetLength) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(JSFunction, fun, 0);
+ CONVERT_SMI_ARG_CHECKED(length, 1);
+ fun->shared()->set_length(length);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetPrototype) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(JSFunction, fun, 0);
+ ASSERT(fun->should_have_prototype());
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ Accessors::FunctionSetPrototype(fun, args[1], NULL);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ return args[0]; // return TOS
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetReadOnlyPrototype) {
+ SealHandleScope shs(isolate);
+ RUNTIME_ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+
+ String* name = isolate->heap()->prototype_string();
+
+ if (function->HasFastProperties()) {
+ // Construct a new field descriptor with updated attributes.
+ DescriptorArray* instance_desc = function->map()->instance_descriptors();
+
+ int index = instance_desc->SearchWithCache(name, function->map());
+ ASSERT(index != DescriptorArray::kNotFound);
+ PropertyDetails details = instance_desc->GetDetails(index);
+
+ CallbacksDescriptor new_desc(name,
+ instance_desc->GetValue(index),
+ static_cast<PropertyAttributes>(details.attributes() | READ_ONLY));
+
+ // Create a new map featuring the new field descriptors array.
+ Map* new_map;
+ MaybeObject* maybe_map =
+ function->map()->CopyReplaceDescriptor(
+ instance_desc, &new_desc, index, OMIT_TRANSITION);
+ if (!maybe_map->To(&new_map)) return maybe_map;
+
+ function->set_map(new_map);
+ } else { // Dictionary properties.
+ // Directly manipulate the property details.
+ int entry = function->property_dictionary()->FindEntry(name);
+ ASSERT(entry != NameDictionary::kNotFound);
+ PropertyDetails details = function->property_dictionary()->DetailsAt(entry);
+ PropertyDetails new_details(
+ static_cast<PropertyAttributes>(details.attributes() | READ_ONLY),
+ details.type(),
+ details.dictionary_index());
+ function->property_dictionary()->DetailsAtPut(entry, new_details);
+ }
+ return function;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionIsAPIFunction) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ return isolate->heap()->ToBoolean(f->shared()->IsApiFunction());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionIsBuiltin) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ return isolate->heap()->ToBoolean(f->IsBuiltin());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, target, 0);
+ Handle<Object> code = args.at<Object>(1);
+
+ if (code->IsNull()) return *target;
+ RUNTIME_ASSERT(code->IsJSFunction());
+ Handle<JSFunction> source = Handle<JSFunction>::cast(code);
+ Handle<SharedFunctionInfo> target_shared(target->shared());
+ Handle<SharedFunctionInfo> source_shared(source->shared());
+
+ if (!JSFunction::EnsureCompiled(source, KEEP_EXCEPTION)) {
+ return Failure::Exception();
+ }
+
+ // Mark both, the source and the target, as un-flushable because the
+ // shared unoptimized code makes them impossible to enqueue in a list.
+ ASSERT(target_shared->code()->gc_metadata() == NULL);
+ ASSERT(source_shared->code()->gc_metadata() == NULL);
+ target_shared->set_dont_flush(true);
+ source_shared->set_dont_flush(true);
+
+ // Set the code, scope info, formal parameter count, and the length
+ // of the target shared function info. Set the source code of the
+ // target function to undefined. SetCode is only used for built-in
+ // constructors like String, Array, and Object, and some web code
+ // doesn't like seeing source code for constructors.
+ target_shared->ReplaceCode(source_shared->code());
+ target_shared->set_scope_info(source_shared->scope_info());
+ target_shared->set_length(source_shared->length());
+ target_shared->set_formal_parameter_count(
+ source_shared->formal_parameter_count());
+ target_shared->set_script(isolate->heap()->undefined_value());
+
+ // Since we don't store the source we should never optimize this.
+ target_shared->code()->set_optimizable(false);
+
+ // Set the code of the target function.
+ target->ReplaceCode(source_shared->code());
+ ASSERT(target->next_function_link()->IsUndefined());
+
+ // Make sure we get a fresh copy of the literal vector to avoid cross
+ // context contamination.
+ Handle<Context> context(source->context());
+ int number_of_literals = source->NumberOfLiterals();
+ Handle<FixedArray> literals =
+ isolate->factory()->NewFixedArray(number_of_literals, TENURED);
+ if (number_of_literals > 0) {
+ literals->set(JSFunction::kLiteralNativeContextIndex,
+ context->native_context());
+ }
+ target->set_context(*context);
+ target->set_literals(*literals);
+
+ if (isolate->logger()->is_logging_code_events() ||
+ isolate->cpu_profiler()->is_profiling()) {
+ isolate->logger()->LogExistingFunction(
+ source_shared, Handle<Code>(source_shared->code()));
+ }
+
+ return *target;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetExpectedNumberOfProperties) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_SMI_ARG_CHECKED(num, 1);
+ RUNTIME_ASSERT(num >= 0);
+ SetExpectedNofProperties(function, num);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSGeneratorObject) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+
+ JavaScriptFrameIterator it(isolate);
+ JavaScriptFrame* frame = it.frame();
+ JSFunction* function = frame->function();
+ RUNTIME_ASSERT(function->shared()->is_generator());
+
+ JSGeneratorObject* generator;
+ if (frame->IsConstructor()) {
+ generator = JSGeneratorObject::cast(frame->receiver());
+ } else {
+ MaybeObject* maybe_generator =
+ isolate->heap()->AllocateJSGeneratorObject(function);
+ if (!maybe_generator->To(&generator)) return maybe_generator;
+ }
+ generator->set_function(function);
+ generator->set_context(Context::cast(frame->context()));
+ generator->set_receiver(frame->receiver());
+ generator->set_continuation(0);
+ generator->set_operand_stack(isolate->heap()->empty_fixed_array());
+ generator->set_stack_handler_index(-1);
+
+ return generator;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SuspendJSGeneratorObject) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSGeneratorObject, generator_object, 0);
+
+ JavaScriptFrameIterator stack_iterator(isolate);
+ JavaScriptFrame* frame = stack_iterator.frame();
+ RUNTIME_ASSERT(frame->function()->shared()->is_generator());
+ ASSERT_EQ(frame->function(), generator_object->function());
+
+ // The caller should have saved the context and continuation already.
+ ASSERT_EQ(generator_object->context(), Context::cast(frame->context()));
+ ASSERT_LT(0, generator_object->continuation());
+
+ // We expect there to be at least two values on the operand stack: the return
+ // value of the yield expression, and the argument to this runtime call.
+ // Neither of those should be saved.
+ int operands_count = frame->ComputeOperandsCount();
+ ASSERT_GE(operands_count, 2);
+ operands_count -= 2;
+
+ if (operands_count == 0) {
+ // Although it's semantically harmless to call this function with an
+ // operands_count of zero, it is also unnecessary.
+ ASSERT_EQ(generator_object->operand_stack(),
+ isolate->heap()->empty_fixed_array());
+ ASSERT_EQ(generator_object->stack_handler_index(), -1);
+ // If there are no operands on the stack, there shouldn't be a handler
+ // active either.
+ ASSERT(!frame->HasHandler());
+ } else {
+ int stack_handler_index = -1;
+ MaybeObject* alloc = isolate->heap()->AllocateFixedArray(operands_count);
+ FixedArray* operand_stack;
+ if (!alloc->To(&operand_stack)) return alloc;
+ frame->SaveOperandStack(operand_stack, &stack_handler_index);
+ generator_object->set_operand_stack(operand_stack);
+ generator_object->set_stack_handler_index(stack_handler_index);
+ }
+
+ return isolate->heap()->undefined_value();
+}
+
+
+// Note that this function is the slow path for resuming generators. It is only
+// called if the suspended activation had operands on the stack, stack handlers
+// needing rewinding, or if the resume should throw an exception. The fast path
+// is handled directly in FullCodeGenerator::EmitGeneratorResume(), which is
+// inlined into GeneratorNext and GeneratorThrow. EmitGeneratorResumeResume is
+// called in any case, as it needs to reconstruct the stack frame and make space
+// for arguments and operands.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ResumeJSGeneratorObject) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_CHECKED(JSGeneratorObject, generator_object, 0);
+ CONVERT_ARG_CHECKED(Object, value, 1);
+ CONVERT_SMI_ARG_CHECKED(resume_mode_int, 2);
+ JavaScriptFrameIterator stack_iterator(isolate);
+ JavaScriptFrame* frame = stack_iterator.frame();
+
+ ASSERT_EQ(frame->function(), generator_object->function());
+ ASSERT(frame->function()->is_compiled());
+
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+
+ Address pc = generator_object->function()->code()->instruction_start();
+ int offset = generator_object->continuation();
+ ASSERT(offset > 0);
+ frame->set_pc(pc + offset);
+ generator_object->set_continuation(JSGeneratorObject::kGeneratorExecuting);
+
+ FixedArray* operand_stack = generator_object->operand_stack();
+ int operands_count = operand_stack->length();
+ if (operands_count != 0) {
+ frame->RestoreOperandStack(operand_stack,
+ generator_object->stack_handler_index());
+ generator_object->set_operand_stack(isolate->heap()->empty_fixed_array());
+ generator_object->set_stack_handler_index(-1);
+ }
+
+ JSGeneratorObject::ResumeMode resume_mode =
+ static_cast<JSGeneratorObject::ResumeMode>(resume_mode_int);
+ switch (resume_mode) {
+ case JSGeneratorObject::NEXT:
+ return value;
+ case JSGeneratorObject::THROW:
+ return isolate->Throw(value);
+ }
+
+ UNREACHABLE();
+ return isolate->ThrowIllegalOperation();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ThrowGeneratorStateError) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
+ int continuation = generator->continuation();
+ const char* message = continuation == JSGeneratorObject::kGeneratorClosed ?
+ "generator_finished" : "generator_running";
+ Vector< Handle<Object> > argv = HandleVector<Object>(NULL, 0);
+ Handle<Object> error = isolate->factory()->NewError(message, argv);
+ return isolate->Throw(*error);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ObjectFreeze) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSObject, object, 0);
+ return object->Freeze(isolate);
+}
+
+
+MUST_USE_RESULT static MaybeObject* CharFromCode(Isolate* isolate,
+ Object* char_code) {
+ if (char_code->IsNumber()) {
+ return isolate->heap()->LookupSingleCharacterStringFromCode(
+ NumberToUint32(char_code) & 0xffff);
+ }
+ return isolate->heap()->empty_string();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringCharCodeAt) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(String, subject, 0);
+ CONVERT_NUMBER_CHECKED(uint32_t, i, Uint32, args[1]);
+
+ // Flatten the string. If someone wants to get a char at an index
+ // in a cons string, it is likely that more indices will be
+ // accessed.
+ Object* flat;
+ { MaybeObject* maybe_flat = subject->TryFlatten();
+ if (!maybe_flat->ToObject(&flat)) return maybe_flat;
+ }
+ subject = String::cast(flat);
+
+ if (i >= static_cast<uint32_t>(subject->length())) {
+ return isolate->heap()->nan_value();
+ }
+
+ return Smi::FromInt(subject->Get(i));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CharFromCode) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ return CharFromCode(isolate, args[0]);
+}
+
+
+class FixedArrayBuilder {
+ public:
+ explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity)
+ : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)),
+ 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);
+ }
+
+ explicit FixedArrayBuilder(Handle<FixedArray> backing_store)
+ : array_(backing_store),
+ 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);
+ }
+
+ bool HasCapacity(int elements) {
+ int length = array_->length();
+ int required_length = length_ + elements;
+ return (length >= required_length);
+ }
+
+ void EnsureCapacity(int elements) {
+ int length = array_->length();
+ int required_length = length_ + elements;
+ if (length < required_length) {
+ int new_length = length;
+ do {
+ new_length *= 2;
+ } while (new_length < required_length);
+ Handle<FixedArray> extended_array =
+ array_->GetIsolate()->factory()->NewFixedArrayWithHoles(new_length);
+ array_->CopyTo(0, *extended_array, 0, length_);
+ array_ = extended_array;
+ }
+ }
+
+ 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_++;
+ }
+
+ Handle<FixedArray> array() {
+ return array_;
+ }
+
+ int length() {
+ return length_;
+ }
+
+ int capacity() {
+ return array_->length();
+ }
+
+ Handle<JSArray> ToJSArray(Handle<JSArray> target_array) {
+ Factory* factory = target_array->GetIsolate()->factory();
+ factory->SetContent(target_array, array_);
+ target_array->set_length(Smi::FromInt(length_));
+ return target_array;
+ }
+
+
+ private:
+ Handle<FixedArray> array_;
+ int length_;
+ bool has_non_smi_elements_;
+};
+
+
+// Forward declarations.
+const int kStringBuilderConcatHelperLengthBits = 11;
+const int kStringBuilderConcatHelperPositionBits = 19;
+
+template <typename schar>
+static inline void StringBuilderConcatHelper(String*,
+ schar*,
+ FixedArray*,
+ int);
+
+typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits>
+ StringBuilderSubstringLength;
+typedef BitField<int,
+ kStringBuilderConcatHelperLengthBits,
+ kStringBuilderConcatHelperPositionBits>
+ StringBuilderSubstringPosition;
+
+
+class ReplacementStringBuilder {
+ public:
+ ReplacementStringBuilder(Heap* heap,
+ Handle<String> subject,
+ int estimated_part_count)
+ : heap_(heap),
+ array_builder_(heap->isolate(), estimated_part_count),
+ subject_(subject),
+ character_count_(0),
+ is_ascii_(subject->IsOneByteRepresentation()) {
+ // Require a non-zero initial size. Ensures that doubling the size to
+ // extend the array will work.
+ ASSERT(estimated_part_count > 0);
+ }
+
+ static inline void AddSubjectSlice(FixedArrayBuilder* builder,
+ int from,
+ int to) {
+ ASSERT(from >= 0);
+ int length = to - from;
+ ASSERT(length > 0);
+ if (StringBuilderSubstringLength::is_valid(length) &&
+ StringBuilderSubstringPosition::is_valid(from)) {
+ int encoded_slice = StringBuilderSubstringLength::encode(length) |
+ StringBuilderSubstringPosition::encode(from);
+ builder->Add(Smi::FromInt(encoded_slice));
+ } else {
+ // Otherwise encode as two smis.
+ builder->Add(Smi::FromInt(-length));
+ builder->Add(Smi::FromInt(from));
+ }
+ }
+
+
+ void EnsureCapacity(int elements) {
+ array_builder_.EnsureCapacity(elements);
+ }
+
+
+ void AddSubjectSlice(int from, int to) {
+ AddSubjectSlice(&array_builder_, from, to);
+ IncrementCharacterCount(to - from);
+ }
+
+
+ void AddString(Handle<String> string) {
+ int length = string->length();
+ ASSERT(length > 0);
+ AddElement(*string);
+ if (!string->IsOneByteRepresentation()) {
+ is_ascii_ = false;
+ }
+ IncrementCharacterCount(length);
+ }
+
+
+ Handle<String> ToString() {
+ if (array_builder_.length() == 0) {
+ return heap_->isolate()->factory()->empty_string();
+ }
+
+ Handle<String> joined_string;
+ if (is_ascii_) {
+ Handle<SeqOneByteString> seq = NewRawOneByteString(character_count_);
+ DisallowHeapAllocation no_gc;
+ uint8_t* char_buffer = seq->GetChars();
+ StringBuilderConcatHelper(*subject_,
+ char_buffer,
+ *array_builder_.array(),
+ array_builder_.length());
+ joined_string = Handle<String>::cast(seq);
+ } else {
+ // Non-ASCII.
+ Handle<SeqTwoByteString> seq = NewRawTwoByteString(character_count_);
+ DisallowHeapAllocation no_gc;
+ uc16* char_buffer = seq->GetChars();
+ StringBuilderConcatHelper(*subject_,
+ char_buffer,
+ *array_builder_.array(),
+ array_builder_.length());
+ joined_string = Handle<String>::cast(seq);
+ }
+ return joined_string;
+ }
+
+
+ void IncrementCharacterCount(int by) {
+ if (character_count_ > String::kMaxLength - by) {
+ V8::FatalProcessOutOfMemory("String.replace result too large.");
+ }
+ character_count_ += by;
+ }
+
+ private:
+ Handle<SeqOneByteString> NewRawOneByteString(int length) {
+ return heap_->isolate()->factory()->NewRawOneByteString(length);
+ }
+
+
+ Handle<SeqTwoByteString> NewRawTwoByteString(int length) {
+ return heap_->isolate()->factory()->NewRawTwoByteString(length);
+ }
+
+
+ void AddElement(Object* element) {
+ ASSERT(element->IsSmi() || element->IsString());
+ ASSERT(array_builder_.capacity() > array_builder_.length());
+ array_builder_.Add(element);
+ }
+
+ Heap* heap_;
+ FixedArrayBuilder array_builder_;
+ Handle<String> subject_;
+ int character_count_;
+ bool is_ascii_;
+};
+
+
+class CompiledReplacement {
+ public:
+ explicit CompiledReplacement(Zone* zone)
+ : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {}
+
+ // Return whether the replacement is simple.
+ bool Compile(Handle<String> replacement,
+ int capture_count,
+ int subject_length);
+
+ // Use Apply only if Compile returned false.
+ void Apply(ReplacementStringBuilder* builder,
+ int match_from,
+ int match_to,
+ int32_t* match);
+
+ // Number of distinct parts of the replacement pattern.
+ int parts() {
+ return parts_.length();
+ }
+
+ Zone* zone() const { return zone_; }
+
+ private:
+ enum PartType {
+ SUBJECT_PREFIX = 1,
+ SUBJECT_SUFFIX,
+ SUBJECT_CAPTURE,
+ REPLACEMENT_SUBSTRING,
+ REPLACEMENT_STRING,
+
+ NUMBER_OF_PART_TYPES
+ };
+
+ struct ReplacementPart {
+ static inline ReplacementPart SubjectMatch() {
+ return ReplacementPart(SUBJECT_CAPTURE, 0);
+ }
+ static inline ReplacementPart SubjectCapture(int capture_index) {
+ return ReplacementPart(SUBJECT_CAPTURE, capture_index);
+ }
+ static inline ReplacementPart SubjectPrefix() {
+ return ReplacementPart(SUBJECT_PREFIX, 0);
+ }
+ static inline ReplacementPart SubjectSuffix(int subject_length) {
+ return ReplacementPart(SUBJECT_SUFFIX, subject_length);
+ }
+ static inline ReplacementPart ReplacementString() {
+ return ReplacementPart(REPLACEMENT_STRING, 0);
+ }
+ static inline ReplacementPart ReplacementSubString(int from, int to) {
+ ASSERT(from >= 0);
+ ASSERT(to > from);
+ return ReplacementPart(-from, to);
+ }
+
+ // If tag <= 0 then it is the negation of a start index of a substring of
+ // the replacement pattern, otherwise it's a value from PartType.
+ ReplacementPart(int tag, int data)
+ : tag(tag), data(data) {
+ // Must be non-positive or a PartType value.
+ ASSERT(tag < NUMBER_OF_PART_TYPES);
+ }
+ // Either a value of PartType or a non-positive number that is
+ // the negation of an index into the replacement string.
+ int tag;
+ // The data value's interpretation depends on the value of tag:
+ // tag == SUBJECT_PREFIX ||
+ // tag == SUBJECT_SUFFIX: data is unused.
+ // tag == SUBJECT_CAPTURE: data is the number of the capture.
+ // tag == REPLACEMENT_SUBSTRING ||
+ // tag == REPLACEMENT_STRING: data is index into array of substrings
+ // of the replacement string.
+ // tag <= 0: Temporary representation of the substring of the replacement
+ // string ranging over -tag .. data.
+ // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
+ // substring objects.
+ int data;
+ };
+
+ template<typename Char>
+ bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
+ Vector<Char> characters,
+ int capture_count,
+ int subject_length,
+ Zone* zone) {
+ int length = characters.length();
+ int last = 0;
+ for (int i = 0; i < length; i++) {
+ Char c = characters[i];
+ if (c == '$') {
+ int next_index = i + 1;
+ if (next_index == length) { // No next character!
+ break;
+ }
+ Char c2 = characters[next_index];
+ switch (c2) {
+ case '$':
+ if (i > last) {
+ // There is a substring before. Include the first "$".
+ parts->Add(ReplacementPart::ReplacementSubString(last, next_index),
+ zone);
+ last = next_index + 1; // Continue after the second "$".
+ } else {
+ // Let the next substring start with the second "$".
+ last = next_index;
+ }
+ i = next_index;
+ break;
+ case '`':
+ if (i > last) {
+ parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
+ }
+ parts->Add(ReplacementPart::SubjectPrefix(), zone);
+ i = next_index;
+ last = i + 1;
+ break;
+ case '\'':
+ if (i > last) {
+ parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
+ }
+ parts->Add(ReplacementPart::SubjectSuffix(subject_length), zone);
+ i = next_index;
+ last = i + 1;
+ break;
+ case '&':
+ if (i > last) {
+ parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
+ }
+ parts->Add(ReplacementPart::SubjectMatch(), zone);
+ i = next_index;
+ last = i + 1;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ int capture_ref = c2 - '0';
+ if (capture_ref > capture_count) {
+ i = next_index;
+ continue;
+ }
+ int second_digit_index = next_index + 1;
+ if (second_digit_index < length) {
+ // Peek ahead to see if we have two digits.
+ Char c3 = characters[second_digit_index];
+ if ('0' <= c3 && c3 <= '9') { // Double digits.
+ int double_digit_ref = capture_ref * 10 + c3 - '0';
+ if (double_digit_ref <= capture_count) {
+ next_index = second_digit_index;
+ capture_ref = double_digit_ref;
+ }
+ }
+ }
+ if (capture_ref > 0) {
+ if (i > last) {
+ parts->Add(ReplacementPart::ReplacementSubString(last, i), zone);
+ }
+ ASSERT(capture_ref <= capture_count);
+ parts->Add(ReplacementPart::SubjectCapture(capture_ref), zone);
+ last = next_index + 1;
+ }
+ i = next_index;
+ break;
+ }
+ default:
+ i = next_index;
+ break;
+ }
+ }
+ }
+ if (length > last) {
+ if (last == 0) {
+ // Replacement is simple. Do not use Apply to do the replacement.
+ return true;
+ } else {
+ parts->Add(ReplacementPart::ReplacementSubString(last, length), zone);
+ }
+ }
+ return false;
+ }
+
+ ZoneList<ReplacementPart> parts_;
+ ZoneList<Handle<String> > replacement_substrings_;
+ Zone* zone_;
+};
+
+
+bool CompiledReplacement::Compile(Handle<String> replacement,
+ int capture_count,
+ int subject_length) {
+ {
+ DisallowHeapAllocation no_gc;
+ String::FlatContent content = replacement->GetFlatContent();
+ ASSERT(content.IsFlat());
+ bool simple = false;
+ if (content.IsAscii()) {
+ simple = ParseReplacementPattern(&parts_,
+ content.ToOneByteVector(),
+ capture_count,
+ subject_length,
+ zone());
+ } else {
+ ASSERT(content.IsTwoByte());
+ simple = ParseReplacementPattern(&parts_,
+ content.ToUC16Vector(),
+ capture_count,
+ subject_length,
+ zone());
+ }
+ if (simple) return true;
+ }
+
+ Isolate* isolate = replacement->GetIsolate();
+ // Find substrings of replacement string and create them as String objects.
+ int substring_index = 0;
+ for (int i = 0, n = parts_.length(); i < n; i++) {
+ int tag = parts_[i].tag;
+ if (tag <= 0) { // A replacement string slice.
+ int from = -tag;
+ int to = parts_[i].data;
+ replacement_substrings_.Add(
+ isolate->factory()->NewSubString(replacement, from, to), zone());
+ parts_[i].tag = REPLACEMENT_SUBSTRING;
+ parts_[i].data = substring_index;
+ substring_index++;
+ } else if (tag == REPLACEMENT_STRING) {
+ replacement_substrings_.Add(replacement, zone());
+ parts_[i].data = substring_index;
+ substring_index++;
+ }
+ }
+ return false;
+}
+
+
+void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
+ int match_from,
+ int match_to,
+ int32_t* match) {
+ ASSERT_LT(0, parts_.length());
+ for (int i = 0, n = parts_.length(); i < n; i++) {
+ ReplacementPart part = parts_[i];
+ switch (part.tag) {
+ case SUBJECT_PREFIX:
+ if (match_from > 0) builder->AddSubjectSlice(0, match_from);
+ break;
+ case SUBJECT_SUFFIX: {
+ int subject_length = part.data;
+ if (match_to < subject_length) {
+ builder->AddSubjectSlice(match_to, subject_length);
+ }
+ break;
+ }
+ case SUBJECT_CAPTURE: {
+ int capture = part.data;
+ int from = match[capture * 2];
+ int to = match[capture * 2 + 1];
+ if (from >= 0 && to > from) {
+ builder->AddSubjectSlice(from, to);
+ }
+ break;
+ }
+ case REPLACEMENT_SUBSTRING:
+ case REPLACEMENT_STRING:
+ builder->AddString(replacement_substrings_[part.data]);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+
+void FindAsciiStringIndices(Vector<const uint8_t> subject,
+ char pattern,
+ ZoneList<int>* indices,
+ unsigned int limit,
+ Zone* zone) {
+ ASSERT(limit > 0);
+ // Collect indices of pattern in subject using memchr.
+ // Stop after finding at most limit values.
+ const uint8_t* subject_start = subject.start();
+ const uint8_t* subject_end = subject_start + subject.length();
+ const uint8_t* pos = subject_start;
+ while (limit > 0) {
+ pos = reinterpret_cast<const uint8_t*>(
+ memchr(pos, pattern, subject_end - pos));
+ if (pos == NULL) return;
+ indices->Add(static_cast<int>(pos - subject_start), zone);
+ pos++;
+ limit--;
+ }
+}
+
+
+void FindTwoByteStringIndices(const Vector<const uc16> subject,
+ uc16 pattern,
+ ZoneList<int>* indices,
+ unsigned int limit,
+ Zone* zone) {
+ ASSERT(limit > 0);
+ const uc16* subject_start = subject.start();
+ const uc16* subject_end = subject_start + subject.length();
+ for (const uc16* pos = subject_start; pos < subject_end && limit > 0; pos++) {
+ if (*pos == pattern) {
+ indices->Add(static_cast<int>(pos - subject_start), zone);
+ limit--;
+ }
+ }
+}
+
+
+template <typename SubjectChar, typename PatternChar>
+void FindStringIndices(Isolate* isolate,
+ Vector<const SubjectChar> subject,
+ Vector<const PatternChar> pattern,
+ ZoneList<int>* indices,
+ unsigned int limit,
+ Zone* zone) {
+ ASSERT(limit > 0);
+ // Collect indices of pattern in subject.
+ // Stop after finding at most limit values.
+ int pattern_length = pattern.length();
+ int index = 0;
+ StringSearch<PatternChar, SubjectChar> search(isolate, pattern);
+ while (limit > 0) {
+ index = search.Search(subject, index);
+ if (index < 0) return;
+ indices->Add(index, zone);
+ index += pattern_length;
+ limit--;
+ }
+}
+
+
+void FindStringIndicesDispatch(Isolate* isolate,
+ String* subject,
+ String* pattern,
+ ZoneList<int>* indices,
+ unsigned int limit,
+ Zone* zone) {
+ {
+ DisallowHeapAllocation no_gc;
+ String::FlatContent subject_content = subject->GetFlatContent();
+ String::FlatContent pattern_content = pattern->GetFlatContent();
+ ASSERT(subject_content.IsFlat());
+ ASSERT(pattern_content.IsFlat());
+ if (subject_content.IsAscii()) {
+ Vector<const uint8_t> subject_vector = subject_content.ToOneByteVector();
+ if (pattern_content.IsAscii()) {
+ Vector<const uint8_t> pattern_vector =
+ pattern_content.ToOneByteVector();
+ if (pattern_vector.length() == 1) {
+ FindAsciiStringIndices(subject_vector,
+ pattern_vector[0],
+ indices,
+ limit,
+ zone);
+ } else {
+ FindStringIndices(isolate,
+ subject_vector,
+ pattern_vector,
+ indices,
+ limit,
+ zone);
+ }
+ } else {
+ FindStringIndices(isolate,
+ subject_vector,
+ pattern_content.ToUC16Vector(),
+ indices,
+ limit,
+ zone);
+ }
+ } else {
+ Vector<const uc16> subject_vector = subject_content.ToUC16Vector();
+ if (pattern_content.IsAscii()) {
+ Vector<const uint8_t> pattern_vector =
+ pattern_content.ToOneByteVector();
+ if (pattern_vector.length() == 1) {
+ FindTwoByteStringIndices(subject_vector,
+ pattern_vector[0],
+ indices,
+ limit,
+ zone);
+ } else {
+ FindStringIndices(isolate,
+ subject_vector,
+ pattern_vector,
+ indices,
+ limit,
+ zone);
+ }
+ } else {
+ Vector<const uc16> pattern_vector = pattern_content.ToUC16Vector();
+ if (pattern_vector.length() == 1) {
+ FindTwoByteStringIndices(subject_vector,
+ pattern_vector[0],
+ indices,
+ limit,
+ zone);
+ } else {
+ FindStringIndices(isolate,
+ subject_vector,
+ pattern_vector,
+ indices,
+ limit,
+ zone);
+ }
+ }
+ }
+ }
+}
+
+
+template<typename ResultSeqString>
+MUST_USE_RESULT static MaybeObject* StringReplaceGlobalAtomRegExpWithString(
+ Isolate* isolate,
+ Handle<String> subject,
+ Handle<JSRegExp> pattern_regexp,
+ Handle<String> replacement,
+ Handle<JSArray> last_match_info) {
+ ASSERT(subject->IsFlat());
+ ASSERT(replacement->IsFlat());
+
+ ZoneScope zone_scope(isolate->runtime_zone());
+ ZoneList<int> indices(8, zone_scope.zone());
+ ASSERT_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag());
+ String* pattern =
+ String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex));
+ int subject_len = subject->length();
+ int pattern_len = pattern->length();
+ int replacement_len = replacement->length();
+
+ FindStringIndicesDispatch(
+ isolate, *subject, pattern, &indices, 0xffffffff, zone_scope.zone());
+
+ int matches = indices.length();
+ if (matches == 0) return *subject;
+
+ // Detect integer overflow.
+ int64_t result_len_64 =
+ (static_cast<int64_t>(replacement_len) -
+ static_cast<int64_t>(pattern_len)) *
+ static_cast<int64_t>(matches) +
+ static_cast<int64_t>(subject_len);
+ if (result_len_64 > INT_MAX) return Failure::OutOfMemoryException(0x11);
+ int result_len = static_cast<int>(result_len_64);
+
+ int subject_pos = 0;
+ int result_pos = 0;
+
+ Handle<ResultSeqString> result;
+ if (ResultSeqString::kHasAsciiEncoding) {
+ result = Handle<ResultSeqString>::cast(
+ isolate->factory()->NewRawOneByteString(result_len));
+ } else {
+ result = Handle<ResultSeqString>::cast(
+ isolate->factory()->NewRawTwoByteString(result_len));
+ }
+
+ for (int i = 0; i < matches; i++) {
+ // Copy non-matched subject content.
+ if (subject_pos < indices.at(i)) {
+ String::WriteToFlat(*subject,
+ result->GetChars() + result_pos,
+ subject_pos,
+ indices.at(i));
+ result_pos += indices.at(i) - subject_pos;
+ }
+
+ // Replace match.
+ if (replacement_len > 0) {
+ String::WriteToFlat(*replacement,
+ result->GetChars() + result_pos,
+ 0,
+ replacement_len);
+ result_pos += replacement_len;
+ }
+
+ subject_pos = indices.at(i) + pattern_len;
+ }
+ // Add remaining subject content at the end.
+ if (subject_pos < subject_len) {
+ String::WriteToFlat(*subject,
+ result->GetChars() + result_pos,
+ subject_pos,
+ subject_len);
+ }
+
+ int32_t match_indices[] = { indices.at(matches - 1),
+ indices.at(matches - 1) + pattern_len };
+ RegExpImpl::SetLastMatchInfo(last_match_info, subject, 0, match_indices);
+
+ return *result;
+}
+
+
+MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithString(
+ Isolate* isolate,
+ Handle<String> subject,
+ Handle<JSRegExp> regexp,
+ Handle<String> replacement,
+ Handle<JSArray> last_match_info) {
+ ASSERT(subject->IsFlat());
+ ASSERT(replacement->IsFlat());
+
+ int capture_count = regexp->CaptureCount();
+ int subject_length = subject->length();
+
+ // CompiledReplacement uses zone allocation.
+ ZoneScope zone_scope(isolate->runtime_zone());
+ CompiledReplacement compiled_replacement(zone_scope.zone());
+ bool simple_replace = compiled_replacement.Compile(replacement,
+ capture_count,
+ subject_length);
+
+ // Shortcut for simple non-regexp global replacements
+ if (regexp->TypeTag() == JSRegExp::ATOM && simple_replace) {
+ if (subject->HasOnlyOneByteChars() &&
+ replacement->HasOnlyOneByteChars()) {
+ return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>(
+ isolate, subject, regexp, replacement, last_match_info);
+ } else {
+ return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>(
+ isolate, subject, regexp, replacement, last_match_info);
+ }
+ }
+
+ RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
+ if (global_cache.HasException()) return Failure::Exception();
+
+ int32_t* current_match = global_cache.FetchNext();
+ if (current_match == NULL) {
+ if (global_cache.HasException()) return Failure::Exception();
+ return *subject;
+ }
+
+ // Guessing the number of parts that the final result string is built
+ // from. Global regexps can match any number of times, so we guess
+ // conservatively.
+ int expected_parts = (compiled_replacement.parts() + 1) * 4 + 1;
+ ReplacementStringBuilder builder(isolate->heap(),
+ subject,
+ expected_parts);
+
+ // Number of parts added by compiled replacement plus preceeding
+ // string and possibly suffix after last match. It is possible for
+ // all components to use two elements when encoded as two smis.
+ const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
+
+ int prev = 0;
+
+ do {
+ builder.EnsureCapacity(parts_added_per_loop);
+
+ int start = current_match[0];
+ int end = current_match[1];
+
+ if (prev < start) {
+ builder.AddSubjectSlice(prev, start);
+ }
+
+ if (simple_replace) {
+ builder.AddString(replacement);
+ } else {
+ compiled_replacement.Apply(&builder,
+ start,
+ end,
+ current_match);
+ }
+ prev = end;
+
+ current_match = global_cache.FetchNext();
+ } while (current_match != NULL);
+
+ if (global_cache.HasException()) return Failure::Exception();
+
+ if (prev < subject_length) {
+ builder.EnsureCapacity(2);
+ builder.AddSubjectSlice(prev, subject_length);
+ }
+
+ RegExpImpl::SetLastMatchInfo(last_match_info,
+ subject,
+ capture_count,
+ global_cache.LastSuccessfulMatch());
+
+ return *(builder.ToString());
+}
+
+
+template <typename ResultSeqString>
+MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithEmptyString(
+ Isolate* isolate,
+ Handle<String> subject,
+ Handle<JSRegExp> regexp,
+ Handle<JSArray> last_match_info) {
+ ASSERT(subject->IsFlat());
+
+ // Shortcut for simple non-regexp global replacements
+ if (regexp->TypeTag() == JSRegExp::ATOM) {
+ Handle<String> empty_string = isolate->factory()->empty_string();
+ if (subject->IsOneByteRepresentation()) {
+ return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>(
+ isolate, subject, regexp, empty_string, last_match_info);
+ } else {
+ return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>(
+ isolate, subject, regexp, empty_string, last_match_info);
+ }
+ }
+
+ RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
+ if (global_cache.HasException()) return Failure::Exception();
+
+ int32_t* current_match = global_cache.FetchNext();
+ if (current_match == NULL) {
+ if (global_cache.HasException()) return Failure::Exception();
+ return *subject;
+ }
+
+ int start = current_match[0];
+ int end = current_match[1];
+ int capture_count = regexp->CaptureCount();
+ int subject_length = subject->length();
+
+ int new_length = subject_length - (end - start);
+ if (new_length == 0) return isolate->heap()->empty_string();
+
+ Handle<ResultSeqString> answer;
+ if (ResultSeqString::kHasAsciiEncoding) {
+ answer = Handle<ResultSeqString>::cast(
+ isolate->factory()->NewRawOneByteString(new_length));
+ } else {
+ answer = Handle<ResultSeqString>::cast(
+ isolate->factory()->NewRawTwoByteString(new_length));
+ }
+
+ int prev = 0;
+ int position = 0;
+
+ do {
+ start = current_match[0];
+ end = current_match[1];
+ if (prev < start) {
+ // Add substring subject[prev;start] to answer string.
+ String::WriteToFlat(*subject, answer->GetChars() + position, prev, start);
+ position += start - prev;
+ }
+ prev = end;
+
+ current_match = global_cache.FetchNext();
+ } while (current_match != NULL);
+
+ if (global_cache.HasException()) return Failure::Exception();
+
+ RegExpImpl::SetLastMatchInfo(last_match_info,
+ subject,
+ capture_count,
+ global_cache.LastSuccessfulMatch());
+
+ if (prev < subject_length) {
+ // Add substring subject[prev;length] to answer string.
+ String::WriteToFlat(
+ *subject, answer->GetChars() + position, prev, subject_length);
+ position += subject_length - prev;
+ }
+
+ if (position == 0) return isolate->heap()->empty_string();
+
+ // Shorten string and fill
+ int string_size = ResultSeqString::SizeFor(position);
+ int allocated_string_size = ResultSeqString::SizeFor(new_length);
+ int delta = allocated_string_size - string_size;
+
+ answer->set_length(position);
+ if (delta == 0) return *answer;
+
+ 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;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceGlobalRegExpWithString) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, replacement, 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3);
+
+ ASSERT(regexp->GetFlags().is_global());
+
+ if (!subject->IsFlat()) subject = FlattenGetString(subject);
+
+ if (replacement->length() == 0) {
+ if (subject->HasOnlyOneByteChars()) {
+ return StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>(
+ isolate, subject, regexp, last_match_info);
+ } else {
+ return StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>(
+ isolate, subject, regexp, last_match_info);
+ }
+ }
+
+ if (!replacement->IsFlat()) replacement = FlattenGetString(replacement);
+
+ return StringReplaceGlobalRegExpWithString(
+ isolate, subject, regexp, replacement, last_match_info);
+}
+
+
+Handle<String> 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 = Runtime::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) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, search, 1);
+ CONVERT_ARG_HANDLE_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 = StringReplaceOneCharWithString(isolate,
+ subject,
+ search,
+ replace,
+ &found,
+ kRecursionLimit);
+ if (!result.is_null()) return *result;
+ return *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().
+int Runtime::StringMatch(Isolate* isolate,
+ Handle<String> sub,
+ Handle<String> pat,
+ int start_index) {
+ ASSERT(0 <= start_index);
+ ASSERT(start_index <= sub->length());
+
+ int pattern_length = pat->length();
+ if (pattern_length == 0) return start_index;
+
+ int subject_length = sub->length();
+ if (start_index + pattern_length > subject_length) return -1;
+
+ if (!sub->IsFlat()) FlattenString(sub);
+ if (!pat->IsFlat()) FlattenString(pat);
+
+ DisallowHeapAllocation no_gc; // ensure vectors stay valid
+ // Extract flattened substrings of cons strings before determining asciiness.
+ String::FlatContent seq_sub = sub->GetFlatContent();
+ String::FlatContent seq_pat = pat->GetFlatContent();
+
+ // dispatch on type of strings
+ if (seq_pat.IsAscii()) {
+ Vector<const uint8_t> pat_vector = seq_pat.ToOneByteVector();
+ if (seq_sub.IsAscii()) {
+ return SearchString(isolate,
+ seq_sub.ToOneByteVector(),
+ pat_vector,
+ start_index);
+ }
+ return SearchString(isolate,
+ seq_sub.ToUC16Vector(),
+ pat_vector,
+ start_index);
+ }
+ Vector<const uc16> pat_vector = seq_pat.ToUC16Vector();
+ if (seq_sub.IsAscii()) {
+ return SearchString(isolate,
+ seq_sub.ToOneByteVector(),
+ pat_vector,
+ start_index);
+ }
+ return SearchString(isolate,
+ seq_sub.ToUC16Vector(),
+ pat_vector,
+ start_index);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringIndexOf) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, sub, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, pat, 1);
+
+ Object* index = args[2];
+ uint32_t start_index;
+ if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
+
+ RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
+ int position =
+ Runtime::StringMatch(isolate, sub, pat, start_index);
+ return Smi::FromInt(position);
+}
+
+
+template <typename schar, typename pchar>
+static int StringMatchBackwards(Vector<const schar> subject,
+ Vector<const pchar> pattern,
+ int idx) {
+ int pattern_length = pattern.length();
+ ASSERT(pattern_length >= 1);
+ ASSERT(idx + pattern_length <= subject.length());
+
+ if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
+ for (int i = 0; i < pattern_length; i++) {
+ uc16 c = pattern[i];
+ if (c > String::kMaxOneByteCharCode) {
+ return -1;
+ }
+ }
+ }
+
+ pchar pattern_first_char = pattern[0];
+ for (int i = idx; i >= 0; i--) {
+ if (subject[i] != pattern_first_char) continue;
+ int j = 1;
+ while (j < pattern_length) {
+ if (pattern[j] != subject[i+j]) {
+ break;
+ }
+ j++;
+ }
+ if (j == pattern_length) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringLastIndexOf) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, sub, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, pat, 1);
+
+ Object* index = args[2];
+ uint32_t start_index;
+ if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1);
+
+ uint32_t pat_length = pat->length();
+ uint32_t sub_length = sub->length();
+
+ if (start_index + pat_length > sub_length) {
+ start_index = sub_length - pat_length;
+ }
+
+ if (pat_length == 0) {
+ return Smi::FromInt(start_index);
+ }
+
+ if (!sub->IsFlat()) FlattenString(sub);
+ if (!pat->IsFlat()) FlattenString(pat);
+
+ int position = -1;
+ DisallowHeapAllocation no_gc; // ensure vectors stay valid
+
+ String::FlatContent sub_content = sub->GetFlatContent();
+ String::FlatContent pat_content = pat->GetFlatContent();
+
+ if (pat_content.IsAscii()) {
+ Vector<const uint8_t> pat_vector = pat_content.ToOneByteVector();
+ if (sub_content.IsAscii()) {
+ position = StringMatchBackwards(sub_content.ToOneByteVector(),
+ pat_vector,
+ start_index);
+ } else {
+ position = StringMatchBackwards(sub_content.ToUC16Vector(),
+ pat_vector,
+ start_index);
+ }
+ } else {
+ Vector<const uc16> pat_vector = pat_content.ToUC16Vector();
+ if (sub_content.IsAscii()) {
+ position = StringMatchBackwards(sub_content.ToOneByteVector(),
+ pat_vector,
+ start_index);
+ } else {
+ position = StringMatchBackwards(sub_content.ToUC16Vector(),
+ pat_vector,
+ start_index);
+ }
+ }
+
+ return Smi::FromInt(position);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringLocaleCompare) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(String, str1, 0);
+ CONVERT_ARG_CHECKED(String, str2, 1);
+
+ if (str1 == str2) return Smi::FromInt(0); // Equal.
+ int str1_length = str1->length();
+ int str2_length = str2->length();
+
+ // Decide trivial cases without flattening.
+ if (str1_length == 0) {
+ if (str2_length == 0) return Smi::FromInt(0); // Equal.
+ return Smi::FromInt(-str2_length);
+ } else {
+ if (str2_length == 0) return Smi::FromInt(str1_length);
+ }
+
+ int end = str1_length < str2_length ? str1_length : str2_length;
+
+ // No need to flatten if we are going to find the answer on the first
+ // character. At this point we know there is at least one character
+ // in each string, due to the trivial case handling above.
+ int d = str1->Get(0) - str2->Get(0);
+ if (d != 0) return Smi::FromInt(d);
+
+ str1->TryFlatten();
+ str2->TryFlatten();
+
+ ConsStringIteratorOp* op1 =
+ isolate->runtime_state()->string_locale_compare_it1();
+ ConsStringIteratorOp* op2 =
+ isolate->runtime_state()->string_locale_compare_it2();
+ // TODO(dcarney) Can do array compares here more efficiently.
+ StringCharacterStream stream1(str1, op1);
+ StringCharacterStream stream2(str2, op2);
+
+ for (int i = 0; i < end; i++) {
+ uint16_t char1 = stream1.GetNext();
+ uint16_t char2 = stream2.GetNext();
+ if (char1 != char2) return Smi::FromInt(char1 - char2);
+ }
+
+ return Smi::FromInt(str1_length - str2_length);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SubString) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_CHECKED(String, value, 0);
+ int start, end;
+ // We have a fast integer-only case here to avoid a conversion to double in
+ // the common case where from and to are Smis.
+ if (args[1]->IsSmi() && args[2]->IsSmi()) {
+ CONVERT_SMI_ARG_CHECKED(from_number, 1);
+ CONVERT_SMI_ARG_CHECKED(to_number, 2);
+ start = from_number;
+ end = to_number;
+ } else {
+ CONVERT_DOUBLE_ARG_CHECKED(from_number, 1);
+ CONVERT_DOUBLE_ARG_CHECKED(to_number, 2);
+ start = FastD2IChecked(from_number);
+ end = FastD2IChecked(to_number);
+ }
+ RUNTIME_ASSERT(end >= start);
+ RUNTIME_ASSERT(start >= 0);
+ RUNTIME_ASSERT(end <= value->length());
+ isolate->counters()->sub_string_runtime()->Increment();
+ if (end - start == 1) {
+ return isolate->heap()->LookupSingleCharacterStringFromCode(
+ value->Get(start));
+ }
+ return value->SubString(start, end);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringMatch) {
+ HandleScope handles(isolate);
+ ASSERT_EQ(3, args.length());
+
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, regexp_info, 2);
+
+ RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
+ if (global_cache.HasException()) return Failure::Exception();
+
+ int capture_count = regexp->CaptureCount();
+
+ ZoneScope zone_scope(isolate->runtime_zone());
+ ZoneList<int> offsets(8, zone_scope.zone());
+
+ while (true) {
+ int32_t* match = global_cache.FetchNext();
+ if (match == NULL) break;
+ offsets.Add(match[0], zone_scope.zone()); // start
+ offsets.Add(match[1], zone_scope.zone()); // end
+ }
+
+ if (global_cache.HasException()) return Failure::Exception();
+
+ if (offsets.length() == 0) {
+ // Not a single match.
+ return isolate->heap()->null_value();
+ }
+
+ RegExpImpl::SetLastMatchInfo(regexp_info,
+ subject,
+ capture_count,
+ global_cache.LastSuccessfulMatch());
+
+ int matches = offsets.length() / 2;
+ Handle<FixedArray> elements = isolate->factory()->NewFixedArray(matches);
+ Handle<String> substring =
+ isolate->factory()->NewSubString(subject, offsets.at(0), offsets.at(1));
+ elements->set(0, *substring);
+ for (int i = 1; i < matches; i++) {
+ HandleScope temp_scope(isolate);
+ int from = offsets.at(i * 2);
+ int to = offsets.at(i * 2 + 1);
+ Handle<String> substring =
+ isolate->factory()->NewProperSubString(subject, from, to);
+ elements->set(i, *substring);
+ }
+ Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(elements);
+ result->set_length(Smi::FromInt(matches));
+ return *result;
+}
+
+
+// Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain
+// separate last match info. See comment on that function.
+template<bool has_capture>
+static MaybeObject* SearchRegExpMultiple(
+ Isolate* isolate,
+ Handle<String> subject,
+ Handle<JSRegExp> regexp,
+ Handle<JSArray> last_match_array,
+ Handle<JSArray> result_array) {
+ ASSERT(subject->IsFlat());
+ ASSERT_NE(has_capture, regexp->CaptureCount() == 0);
+
+ int capture_count = regexp->CaptureCount();
+ int subject_length = subject->length();
+
+ static const int kMinLengthToCache = 0x1000;
+
+ if (subject_length > kMinLengthToCache) {
+ Handle<Object> cached_answer(RegExpResultsCache::Lookup(
+ isolate->heap(),
+ *subject,
+ regexp->data(),
+ RegExpResultsCache::REGEXP_MULTIPLE_INDICES), isolate);
+ if (*cached_answer != Smi::FromInt(0)) {
+ Handle<FixedArray> cached_fixed_array =
+ Handle<FixedArray>(FixedArray::cast(*cached_answer));
+ // The cache FixedArray is a COW-array and can therefore be reused.
+ isolate->factory()->SetContent(result_array, cached_fixed_array);
+ // The actual length of the result array is stored in the last element of
+ // the backing store (the backing FixedArray may have a larger capacity).
+ Object* cached_fixed_array_last_element =
+ cached_fixed_array->get(cached_fixed_array->length() - 1);
+ Smi* js_array_length = Smi::cast(cached_fixed_array_last_element);
+ result_array->set_length(js_array_length);
+ RegExpImpl::SetLastMatchInfo(
+ last_match_array, subject, capture_count, NULL);
+ return *result_array;
+ }
+ }
+
+ RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
+ if (global_cache.HasException()) return Failure::Exception();
+
+ Handle<FixedArray> result_elements;
+ if (result_array->HasFastObjectElements()) {
+ result_elements =
+ Handle<FixedArray>(FixedArray::cast(result_array->elements()));
+ }
+ if (result_elements.is_null() || result_elements->length() < 16) {
+ result_elements = isolate->factory()->NewFixedArrayWithHoles(16);
+ }
+
+ FixedArrayBuilder builder(result_elements);
+
+ // Position to search from.
+ int match_start = -1;
+ int match_end = 0;
+ bool first = true;
+
+ // Two smis before and after the match, for very long strings.
+ static const int kMaxBuilderEntriesPerRegExpMatch = 5;
+
+ while (true) {
+ int32_t* current_match = global_cache.FetchNext();
+ if (current_match == NULL) break;
+ match_start = current_match[0];
+ builder.EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
+ if (match_end < match_start) {
+ ReplacementStringBuilder::AddSubjectSlice(&builder,
+ match_end,
+ match_start);
+ }
+ match_end = current_match[1];
+ {
+ // Avoid accumulating new handles inside loop.
+ HandleScope temp_scope(isolate);
+ Handle<String> match;
+ if (!first) {
+ match = isolate->factory()->NewProperSubString(subject,
+ match_start,
+ match_end);
+ } else {
+ match = isolate->factory()->NewSubString(subject,
+ match_start,
+ match_end);
+ first = false;
+ }
+
+ if (has_capture) {
+ // Arguments array to replace function is match, captures, index and
+ // subject, i.e., 3 + capture count in total.
+ Handle<FixedArray> elements =
+ isolate->factory()->NewFixedArray(3 + capture_count);
+
+ elements->set(0, *match);
+ for (int i = 1; i <= capture_count; i++) {
+ int start = current_match[i * 2];
+ if (start >= 0) {
+ int end = current_match[i * 2 + 1];
+ ASSERT(start <= end);
+ Handle<String> substring =
+ isolate->factory()->NewSubString(subject, start, end);
+ elements->set(i, *substring);
+ } else {
+ ASSERT(current_match[i * 2 + 1] < 0);
+ elements->set(i, isolate->heap()->undefined_value());
+ }
+ }
+ elements->set(capture_count + 1, Smi::FromInt(match_start));
+ elements->set(capture_count + 2, *subject);
+ builder.Add(*isolate->factory()->NewJSArrayWithElements(elements));
+ } else {
+ builder.Add(*match);
+ }
+ }
+ }
+
+ if (global_cache.HasException()) return Failure::Exception();
+
+ if (match_start >= 0) {
+ // Finished matching, with at least one match.
+ if (match_end < subject_length) {
+ ReplacementStringBuilder::AddSubjectSlice(&builder,
+ match_end,
+ subject_length);
+ }
+
+ RegExpImpl::SetLastMatchInfo(
+ last_match_array, subject, capture_count, NULL);
+
+ if (subject_length > kMinLengthToCache) {
+ // Store the length of the result array into the last element of the
+ // backing FixedArray.
+ builder.EnsureCapacity(1);
+ Handle<FixedArray> fixed_array = builder.array();
+ fixed_array->set(fixed_array->length() - 1,
+ Smi::FromInt(builder.length()));
+ // Cache the result and turn the FixedArray into a COW array.
+ RegExpResultsCache::Enter(isolate->heap(),
+ *subject,
+ regexp->data(),
+ *fixed_array,
+ RegExpResultsCache::REGEXP_MULTIPLE_INDICES);
+ }
+ return *builder.ToJSArray(result_array);
+ } else {
+ return isolate->heap()->null_value(); // No matches at all.
+ }
+}
+
+
+// This is only called for StringReplaceGlobalRegExpWithFunction. This sets
+// lastMatchInfoOverride to maintain the last match info, so we don't need to
+// set any other last match array info.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExecMultiple) {
+ HandleScope handles(isolate);
+ ASSERT(args.length() == 4);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
+ if (!subject->IsFlat()) FlattenString(subject);
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3);
+
+ ASSERT(regexp->GetFlags().is_global());
+
+ if (regexp->CaptureCount() == 0) {
+ return SearchRegExpMultiple<false>(
+ isolate, subject, regexp, last_match_info, result_array);
+ } else {
+ return SearchRegExpMultiple<true>(
+ isolate, subject, regexp, last_match_info, result_array);
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToRadixString) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_SMI_ARG_CHECKED(radix, 1);
+ RUNTIME_ASSERT(2 <= radix && radix <= 36);
+
+ // Fast case where the result is a one character string.
+ if (args[0]->IsSmi()) {
+ int value = args.smi_at(0);
+ if (value >= 0 && value < radix) {
+ // Character array used for conversion.
+ static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ return isolate->heap()->
+ LookupSingleCharacterStringFromCode(kCharTable[value]);
+ }
+ }
+
+ // Slow case.
+ CONVERT_DOUBLE_ARG_CHECKED(value, 0);
+ if (std::isnan(value)) {
+ return *isolate->factory()->nan_string();
+ }
+ if (std::isinf(value)) {
+ if (value < 0) {
+ return *isolate->factory()->minus_infinity_string();
+ }
+ return *isolate->factory()->infinity_string();
+ }
+ char* str = DoubleToRadixCString(value, radix);
+ MaybeObject* result =
+ isolate->heap()->AllocateStringFromOneByte(CStrVector(str));
+ DeleteArray(str);
+ return result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToFixed) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(value, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
+ int f = FastD2IChecked(f_number);
+ RUNTIME_ASSERT(f >= 0);
+ char* str = DoubleToFixedCString(value, f);
+ MaybeObject* res =
+ isolate->heap()->AllocateStringFromOneByte(CStrVector(str));
+ DeleteArray(str);
+ return res;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToExponential) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(value, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
+ int f = FastD2IChecked(f_number);
+ RUNTIME_ASSERT(f >= -1 && f <= 20);
+ char* str = DoubleToExponentialCString(value, f);
+ MaybeObject* res =
+ isolate->heap()->AllocateStringFromOneByte(CStrVector(str));
+ DeleteArray(str);
+ return res;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToPrecision) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(value, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(f_number, 1);
+ int f = FastD2IChecked(f_number);
+ RUNTIME_ASSERT(f >= 1 && f <= 21);
+ char* str = DoubleToPrecisionCString(value, f);
+ MaybeObject* res =
+ isolate->heap()->AllocateStringFromOneByte(CStrVector(str));
+ DeleteArray(str);
+ return res;
+}
+
+
+// Returns a single character string where first character equals
+// string->Get(index).
+static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
+ if (index < static_cast<uint32_t>(string->length())) {
+ string->TryFlatten();
+ return LookupSingleCharacterStringFromCode(
+ string->GetIsolate(),
+ string->Get(index));
+ }
+ return Execution::CharAt(string, index);
+}
+
+
+MaybeObject* Runtime::GetElementOrCharAtOrFail(Isolate* isolate,
+ Handle<Object> object,
+ uint32_t index) {
+ CALL_HEAP_FUNCTION_PASS_EXCEPTION(isolate,
+ GetElementOrCharAt(isolate, object, index));
+}
+
+
+MaybeObject* Runtime::GetElementOrCharAt(Isolate* isolate,
+ Handle<Object> object,
+ uint32_t index) {
+ // Handle [] indexing on Strings
+ if (object->IsString()) {
+ Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
+ if (!result->IsUndefined()) return *result;
+ }
+
+ // Handle [] indexing on String objects
+ if (object->IsStringObjectWithCharacterAt(index)) {
+ Handle<JSValue> js_value = Handle<JSValue>::cast(object);
+ Handle<Object> result =
+ GetCharAt(Handle<String>(String::cast(js_value->value())), index);
+ if (!result->IsUndefined()) return *result;
+ }
+
+ if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
+ return object->GetPrototype(isolate)->GetElement(index);
+ }
+
+ return object->GetElement(index);
+}
+
+
+MaybeObject* Runtime::HasObjectProperty(Isolate* isolate,
+ Handle<JSReceiver> object,
+ Handle<Object> key) {
+ HandleScope scope(isolate);
+
+ // Check if the given key is an array index.
+ uint32_t index;
+ if (key->ToArrayIndex(&index)) {
+ return isolate->heap()->ToBoolean(object->HasElement(index));
+ }
+
+ // Convert the key to a name - possibly by calling back into JavaScript.
+ Handle<Name> name;
+ if (key->IsName()) {
+ name = Handle<Name>::cast(key);
+ } else {
+ bool has_pending_exception = false;
+ Handle<Object> converted =
+ Execution::ToString(key, &has_pending_exception);
+ if (has_pending_exception) return Failure::Exception();
+ name = Handle<Name>::cast(converted);
+ }
+
+ return isolate->heap()->ToBoolean(object->HasProperty(*name));
+}
+
+MaybeObject* Runtime::GetObjectPropertyOrFail(
+ Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key) {
+ CALL_HEAP_FUNCTION_PASS_EXCEPTION(isolate,
+ GetObjectProperty(isolate, object, key));
+}
+
+MaybeObject* Runtime::GetObjectProperty(Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key) {
+ HandleScope scope(isolate);
+
+ if (object->IsUndefined() || object->IsNull()) {
+ Handle<Object> args[2] = { key, object };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("non_object_property_load",
+ HandleVector(args, 2));
+ return isolate->Throw(*error);
+ }
+
+ // Check if the given key is an array index.
+ uint32_t index;
+ if (key->ToArrayIndex(&index)) {
+ return GetElementOrCharAt(isolate, object, index);
+ }
+
+ // Convert the key to a name - possibly by calling back into JavaScript.
+ Handle<Name> name;
+ if (key->IsName()) {
+ name = Handle<Name>::cast(key);
+ } else {
+ bool has_pending_exception = false;
+ Handle<Object> converted =
+ Execution::ToString(key, &has_pending_exception);
+ if (has_pending_exception) return Failure::Exception();
+ name = Handle<Name>::cast(converted);
+ }
+
+ // Check if the name is trivially convertible to an index and get
+ // the element if so.
+ if (name->AsArrayIndex(&index)) {
+ return GetElementOrCharAt(isolate, object, index);
+ } else {
+ return object->GetProperty(*name);
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetProperty) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ Handle<Object> object = args.at<Object>(0);
+ Handle<Object> key = args.at<Object>(1);
+
+ return Runtime::GetObjectProperty(isolate, object, key);
+}
+
+
+// KeyedGetProperty is called from KeyedLoadIC::GenerateGeneric.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_KeyedGetProperty) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ // Fast cases for getting named properties of the receiver JSObject
+ // itself.
+ //
+ // The global proxy objects has to be excluded since LocalLookup on
+ // the global proxy object can return a valid result even though the
+ // global proxy object never has properties. This is the case
+ // because the global proxy object forwards everything to its hidden
+ // prototype including local lookups.
+ //
+ // Additionally, we need to make sure that we do not cache results
+ // for objects that require access checks.
+ if (args[0]->IsJSObject()) {
+ if (!args[0]->IsJSGlobalProxy() &&
+ !args[0]->IsAccessCheckNeeded() &&
+ args[1]->IsName()) {
+ JSObject* receiver = JSObject::cast(args[0]);
+ Name* key = Name::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) {
+ // Doubles are not cached, so raw read the value.
+ Object* value = receiver->RawFastPropertyAt(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.IsField()) {
+ int offset = result.GetFieldIndex().field_index();
+ // Do not track double fields in the keyed lookup cache. Reading
+ // double values requires boxing.
+ if (!FLAG_track_double_fields ||
+ !result.representation().IsDouble()) {
+ keyed_lookup_cache->Update(receiver_map, key, offset);
+ }
+ return receiver->FastPropertyAt(result.representation(), offset);
+ }
+ } else {
+ // Attempt dictionary lookup.
+ NameDictionary* dictionary = receiver->property_dictionary();
+ int entry = dictionary->FindEntry(key);
+ if ((entry != NameDictionary::kNotFound) &&
+ (dictionary->DetailsAt(entry).type() == NORMAL)) {
+ Object* value = dictionary->ValueAt(entry);
+ if (!receiver->IsGlobalObject()) return value;
+ value = PropertyCell::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 name 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 (IsFastDoubleElementsKind(elements_kind)) {
+ FixedArrayBase* elements = js_object->elements();
+ if (args.at<Smi>(1)->value() >= elements->length()) {
+ if (IsFastHoleyElementsKind(elements_kind)) {
+ elements_kind = FAST_HOLEY_ELEMENTS;
+ } else {
+ elements_kind = FAST_ELEMENTS;
+ }
+ MaybeObject* maybe_object = TransitionElements(js_object,
+ elements_kind,
+ isolate);
+ if (maybe_object->IsFailure()) return maybe_object;
+ }
+ } else {
+ ASSERT(IsFastSmiOrObjectElementsKind(elements_kind) ||
+ !IsFastElementsKind(elements_kind));
+ }
+ }
+ } else if (args[0]->IsString() && args[1]->IsSmi()) {
+ // Fast case for string indexing using [] with a smi index.
+ HandleScope scope(isolate);
+ Handle<String> str = args.at<String>(0);
+ int index = args.smi_at(1);
+ if (index >= 0 && index < str->length()) {
+ Handle<Object> result = GetCharAt(str, index);
+ return *result;
+ }
+ }
+
+ // Fall back to GetObjectProperty.
+ return Runtime::GetObjectProperty(isolate,
+ args.at<Object>(0),
+ args.at<Object>(1));
+}
+
+
+static bool IsValidAccessor(Handle<Object> obj) {
+ return obj->IsUndefined() || obj->IsSpecFunction() || obj->IsNull();
+}
+
+
+// Implements part of 8.12.9 DefineOwnProperty.
+// There are 3 cases that lead here:
+// Step 4b - define a new accessor property.
+// Steps 9c & 12 - replace an existing data property with an accessor property.
+// Step 12 - update an existing accessor property with an accessor or generic
+// descriptor.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineAccessorProperty) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 5);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ RUNTIME_ASSERT(!obj->IsNull());
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, getter, 2);
+ RUNTIME_ASSERT(IsValidAccessor(getter));
+ CONVERT_ARG_HANDLE_CHECKED(Object, setter, 3);
+ RUNTIME_ASSERT(IsValidAccessor(setter));
+ CONVERT_SMI_ARG_CHECKED(unchecked, 4);
+ RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
+ PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
+
+ bool fast = obj->HasFastProperties();
+ JSObject::DefineAccessor(obj, name, getter, setter, attr);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (fast) JSObject::TransformToFastProperties(obj, 0);
+ return isolate->heap()->undefined_value();
+}
+
+
+// Implements part of 8.12.9 DefineOwnProperty.
+// There are 3 cases that lead here:
+// Step 4a - define a new data property.
+// Steps 9b & 12 - replace an existing accessor property with a data property.
+// Step 12 - update an existing data property with a data or generic
+// descriptor.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, js_object, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, obj_value, 2);
+ CONVERT_SMI_ARG_CHECKED(unchecked, 3);
+ RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
+ PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
+
+ LookupResult result(isolate);
+ js_object->LocalLookupRealNamedProperty(*name, &result);
+
+ // Special case for callback properties.
+ if (result.IsPropertyCallbacks()) {
+ 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
+ // a property. For simplicity we normalize the property which enables us
+ // to not worry about changing the instance_descriptor and creating a new
+ // map. The current version of SetObjectProperty does not handle attributes
+ // correctly in the case where a property is a field and is reset with
+ // new attributes.
+ if (result.IsFound() &&
+ (attr != result.GetAttributes() || result.IsPropertyCallbacks())) {
+ // New attributes - normalize to avoid writing to instance descriptor
+ if (js_object->IsJSGlobalProxy()) {
+ // Since the result is a property, the prototype will exist so
+ // we don't have to check for null.
+ js_object = Handle<JSObject>(JSObject::cast(js_object->GetPrototype()));
+ }
+ 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,
+ *obj_value,
+ attr);
+ }
+
+ return Runtime::ForceSetObjectProperty(isolate,
+ js_object,
+ name,
+ obj_value,
+ attr);
+}
+
+
+// Return property without being observable by accessors or interceptors.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetDataProperty) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);
+ LookupResult lookup(isolate);
+ object->LookupRealNamedProperty(*key, &lookup);
+ if (!lookup.IsFound()) return isolate->heap()->undefined_value();
+ switch (lookup.type()) {
+ case NORMAL:
+ return lookup.holder()->GetNormalizedProperty(&lookup);
+ case FIELD:
+ return lookup.holder()->FastPropertyAt(
+ lookup.representation(),
+ lookup.GetFieldIndex().field_index());
+ case CONSTANT:
+ return lookup.GetConstant();
+ case CALLBACKS:
+ case HANDLER:
+ case INTERCEPTOR:
+ case TRANSITION:
+ return isolate->heap()->undefined_value();
+ case NONEXISTENT:
+ UNREACHABLE();
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+MaybeObject* Runtime::SetObjectPropertyOrFail(
+ Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr,
+ StrictModeFlag strict_mode) {
+ CALL_HEAP_FUNCTION_PASS_EXCEPTION(isolate,
+ SetObjectProperty(isolate, object, key, value, attr, strict_mode));
+}
+
+
+MaybeObject* Runtime::SetObjectProperty(Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr,
+ StrictModeFlag strict_mode) {
+ SetPropertyMode set_mode = attr == NONE ? SET_PROPERTY : DEFINE_PROPERTY;
+ HandleScope scope(isolate);
+
+ if (object->IsUndefined() || object->IsNull()) {
+ Handle<Object> args[2] = { key, object };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("non_object_property_store",
+ HandleVector(args, 2));
+ return isolate->Throw(*error);
+ }
+
+ if (object->IsJSProxy()) {
+ bool has_pending_exception = false;
+ Handle<Object> name = key->IsSymbol()
+ ? key : Execution::ToString(key, &has_pending_exception);
+ if (has_pending_exception) return Failure::Exception();
+ return JSProxy::cast(*object)->SetProperty(
+ Name::cast(*name), *value, attr, strict_mode);
+ }
+
+ // If the object isn't a JavaScript object, we ignore the store.
+ if (!object->IsJSObject()) return *value;
+
+ Handle<JSObject> js_object = Handle<JSObject>::cast(object);
+
+ // Check if the given key is an array index.
+ uint32_t index;
+ if (key->ToArrayIndex(&index)) {
+ // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
+ // of a string using [] notation. We need to support this too in
+ // JavaScript.
+ // In the case of a String object we just need to redirect the assignment to
+ // the underlying string if the index is in range. Since the underlying
+ // string does nothing with the assignment then we can ignore such
+ // assignments.
+ if (js_object->IsStringObjectWithCharacterAt(index)) {
+ return *value;
+ }
+
+ js_object->ValidateElements();
+ if (js_object->HasExternalArrayElements()) {
+ if (!value->IsNumber() && !value->IsUndefined()) {
+ bool has_exception;
+ Handle<Object> number = Execution::ToNumber(value, &has_exception);
+ if (has_exception) return Failure::Exception();
+ value = number;
+ }
+ }
+ MaybeObject* result = js_object->SetElement(
+ index, *value, attr, strict_mode, true, set_mode);
+ js_object->ValidateElements();
+ if (result->IsFailure()) return result;
+ return *value;
+ }
+
+ if (key->IsName()) {
+ MaybeObject* result;
+ Handle<Name> name = Handle<Name>::cast(key);
+ if (name->AsArrayIndex(&index)) {
+ if (js_object->HasExternalArrayElements()) {
+ if (!value->IsNumber() && !value->IsUndefined()) {
+ bool has_exception;
+ Handle<Object> number = Execution::ToNumber(value, &has_exception);
+ if (has_exception) return Failure::Exception();
+ value = number;
+ }
+ }
+ result = js_object->SetElement(
+ index, *value, attr, strict_mode, true, set_mode);
+ } else {
+ if (name->IsString()) Handle<String>::cast(name)->TryFlatten();
+ result = js_object->SetProperty(*name, *value, attr, strict_mode);
+ }
+ if (result->IsFailure()) return result;
+ return *value;
+ }
+
+ // Call-back into JavaScript to convert the key to a string.
+ bool has_pending_exception = false;
+ Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
+ if (has_pending_exception) return Failure::Exception();
+ Handle<String> name = Handle<String>::cast(converted);
+
+ if (name->AsArrayIndex(&index)) {
+ return js_object->SetElement(
+ index, *value, attr, strict_mode, true, set_mode);
+ } else {
+ return js_object->SetProperty(*name, *value, attr, strict_mode);
+ }
+}
+
+
+MaybeObject* Runtime::ForceSetObjectProperty(Isolate* isolate,
+ Handle<JSObject> js_object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr) {
+ HandleScope scope(isolate);
+
+ // Check if the given key is an array index.
+ uint32_t index;
+ if (key->ToArrayIndex(&index)) {
+ // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
+ // of a string using [] notation. We need to support this too in
+ // JavaScript.
+ // In the case of a String object we just need to redirect the assignment to
+ // the underlying string if the index is in range. Since the underlying
+ // string does nothing with the assignment then we can ignore such
+ // assignments.
+ if (js_object->IsStringObjectWithCharacterAt(index)) {
+ return *value;
+ }
+
+ return js_object->SetElement(
+ index, *value, attr, kNonStrictMode, false, DEFINE_PROPERTY);
+ }
+
+ if (key->IsName()) {
+ Handle<Name> name = Handle<Name>::cast(key);
+ if (name->AsArrayIndex(&index)) {
+ return js_object->SetElement(
+ index, *value, attr, kNonStrictMode, false, DEFINE_PROPERTY);
+ } else {
+ if (name->IsString()) Handle<String>::cast(name)->TryFlatten();
+ return js_object->SetLocalPropertyIgnoreAttributes(*name, *value, attr);
+ }
+ }
+
+ // Call-back into JavaScript to convert the key to a string.
+ bool has_pending_exception = false;
+ Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
+ if (has_pending_exception) return Failure::Exception();
+ Handle<String> name = Handle<String>::cast(converted);
+
+ if (name->AsArrayIndex(&index)) {
+ return js_object->SetElement(
+ index, *value, attr, kNonStrictMode, false, DEFINE_PROPERTY);
+ } else {
+ return js_object->SetLocalPropertyIgnoreAttributes(*name, *value, attr);
+ }
+}
+
+
+MaybeObject* Runtime::DeleteObjectProperty(Isolate* isolate,
+ Handle<JSReceiver> receiver,
+ Handle<Object> key,
+ JSReceiver::DeleteMode mode) {
+ HandleScope scope(isolate);
+
+ // Check if the given key is an array index.
+ uint32_t 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
+ // underlying string if the index is in range. Since the
+ // underlying string does nothing with the deletion, we can ignore
+ // such deletions.
+ if (receiver->IsStringObjectWithCharacterAt(index)) {
+ return isolate->heap()->true_value();
+ }
+
+ Handle<Object> result = JSReceiver::DeleteElement(receiver, index, mode);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+ return *result;
+ }
+
+ Handle<Name> name;
+ if (key->IsName()) {
+ name = Handle<Name>::cast(key);
+ } else {
+ // Call-back into JavaScript to convert the key to a string.
+ bool has_pending_exception = false;
+ Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
+ if (has_pending_exception) return Failure::Exception();
+ name = Handle<String>::cast(converted);
+ }
+
+ if (name->IsString()) Handle<String>::cast(name)->TryFlatten();
+ Handle<Object> result = JSReceiver::DeleteProperty(receiver, name, mode);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetProperty) {
+ SealHandleScope shs(isolate);
+ RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
+
+ Handle<Object> object = args.at<Object>(0);
+ Handle<Object> key = args.at<Object>(1);
+ Handle<Object> value = args.at<Object>(2);
+ CONVERT_SMI_ARG_CHECKED(unchecked_attributes, 3);
+ RUNTIME_ASSERT(
+ (unchecked_attributes & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
+ // Compute attributes.
+ PropertyAttributes attributes =
+ static_cast<PropertyAttributes>(unchecked_attributes);
+
+ StrictModeFlag strict_mode = kNonStrictMode;
+ if (args.length() == 5) {
+ CONVERT_STRICT_MODE_ARG_CHECKED(strict_mode_flag, 4);
+ strict_mode = strict_mode_flag;
+ }
+
+ return Runtime::SetObjectProperty(isolate,
+ object,
+ key,
+ value,
+ attributes,
+ strict_mode);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsKind) {
+ HandleScope scope(isolate);
+ RUNTIME_ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Map, map, 1);
+ JSObject::TransitionElementsKind(array, map->elements_kind());
+ return *array;
+}
+
+
+// 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.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetNativeFlag) {
+ SealHandleScope shs(isolate);
+ RUNTIME_ASSERT(args.length() == 1);
+
+ Handle<Object> object = args.at<Object>(0);
+
+ if (object->IsJSFunction()) {
+ JSFunction* func = JSFunction::cast(*object);
+ func->shared()->set_native(true);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreArrayLiteralElement) {
+ HandleScope scope(isolate);
+ RUNTIME_ASSERT(args.length() == 5);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+ CONVERT_SMI_ARG_CHECKED(store_index, 1);
+ Handle<Object> value = args.at<Object>(2);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 3);
+ CONVERT_SMI_ARG_CHECKED(literal_index, 4);
+
+ Object* raw_literal_cell = literals->get(literal_index);
+ JSArray* boilerplate = NULL;
+ if (raw_literal_cell->IsAllocationSite()) {
+ AllocationSite* site = AllocationSite::cast(raw_literal_cell);
+ boilerplate = JSArray::cast(site->transition_info());
+ } else {
+ boilerplate = JSArray::cast(raw_literal_cell);
+ }
+ Handle<JSArray> boilerplate_object(boilerplate);
+ ElementsKind elements_kind = object->GetElementsKind();
+ ASSERT(IsFastElementsKind(elements_kind));
+ // Smis should never trigger transitions.
+ ASSERT(!value->IsSmi());
+
+ if (value->IsNumber()) {
+ ASSERT(IsFastSmiElementsKind(elements_kind));
+ ElementsKind transitioned_kind = IsFastHoleyElementsKind(elements_kind)
+ ? FAST_HOLEY_DOUBLE_ELEMENTS
+ : FAST_DOUBLE_ELEMENTS;
+ if (IsMoreGeneralElementsKindTransition(
+ boilerplate_object->GetElementsKind(),
+ transitioned_kind)) {
+ JSObject::TransitionElementsKind(boilerplate_object, transitioned_kind);
+ }
+ JSObject::TransitionElementsKind(object, transitioned_kind);
+ ASSERT(IsFastDoubleElementsKind(object->GetElementsKind()));
+ FixedDoubleArray* double_array = FixedDoubleArray::cast(object->elements());
+ HeapNumber* number = HeapNumber::cast(*value);
+ double_array->set(store_index, number->Number());
+ } else {
+ ASSERT(IsFastSmiElementsKind(elements_kind) ||
+ IsFastDoubleElementsKind(elements_kind));
+ ElementsKind transitioned_kind = IsFastHoleyElementsKind(elements_kind)
+ ? FAST_HOLEY_ELEMENTS
+ : FAST_ELEMENTS;
+ JSObject::TransitionElementsKind(object, transitioned_kind);
+ if (IsMoreGeneralElementsKindTransition(
+ boilerplate_object->GetElementsKind(),
+ transitioned_kind)) {
+ JSObject::TransitionElementsKind(boilerplate_object, transitioned_kind);
+ }
+ FixedArray* object_array = FixedArray::cast(object->elements());
+ object_array->set(store_index, *value);
+ }
+ return *object;
+}
+
+
+// Check whether debugger and is about to step into the callback that is passed
+// to a built-in function such as Array.forEach.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugCallbackSupportsStepping) {
+ SealHandleScope shs(isolate);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (!isolate->IsDebuggerActive() || !isolate->debug()->StepInActive()) {
+ return isolate->heap()->false_value();
+ }
+ CONVERT_ARG_CHECKED(Object, callback, 0);
+ // We do not step into the callback if it's a builtin or not even a function.
+ if (!callback->IsJSFunction() || JSFunction::cast(callback)->IsBuiltin()) {
+ return isolate->heap()->false_value();
+ }
+ return isolate->heap()->true_value();
+#else
+ return isolate->heap()->false_value();
+#endif // ENABLE_DEBUGGER_SUPPORT
+}
+
+
+// Set one shot breakpoints for the callback function that is passed to a
+// built-in function such as Array.forEach to enable stepping into the callback.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrepareStepInIfStepping) {
+ SealHandleScope shs(isolate);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Debug* debug = isolate->debug();
+ if (!debug->IsStepping()) return isolate->heap()->undefined_value();
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, callback, 0);
+ HandleScope scope(isolate);
+ // When leaving the callback, step out has been activated, but not performed
+ // if we do not leave the builtin. To be able to step into the callback
+ // again, we need to clear the step out at this point.
+ debug->ClearStepOut();
+ debug->FloodWithOneShot(callback);
+#endif // ENABLE_DEBUGGER_SUPPORT
+ return isolate->heap()->undefined_value();
+}
+
+
+// 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) {
+ SealHandleScope shs(isolate);
+ RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
+ CONVERT_ARG_CHECKED(JSObject, object, 0);
+ CONVERT_ARG_CHECKED(Name, name, 1);
+ // Compute attributes.
+ PropertyAttributes attributes = NONE;
+ if (args.length() == 4) {
+ CONVERT_SMI_ARG_CHECKED(unchecked_value, 3);
+ // Only attribute bits should be set.
+ RUNTIME_ASSERT(
+ (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
+ attributes = static_cast<PropertyAttributes>(unchecked_value);
+ }
+
+ return object->
+ SetLocalPropertyIgnoreAttributes(name, args[2], attributes);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteProperty) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Name, key, 1);
+ CONVERT_STRICT_MODE_ARG_CHECKED(strict_mode, 2);
+ JSReceiver::DeleteMode delete_mode = (strict_mode == kStrictMode)
+ ? JSReceiver::STRICT_DELETION : JSReceiver::NORMAL_DELETION;
+ Handle<Object> result = JSReceiver::DeleteProperty(object, key, delete_mode);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+ return *result;
+}
+
+
+static MaybeObject* HasLocalPropertyImplementation(Isolate* isolate,
+ Handle<JSObject> object,
+ Handle<Name> key) {
+ if (object->HasLocalProperty(*key)) return isolate->heap()->true_value();
+ // Handle hidden prototypes. If there's a hidden prototype above this thing
+ // then we have to check it for properties, because they are supposed to
+ // look like they are on this object.
+ Handle<Object> proto(object->GetPrototype(), isolate);
+ if (proto->IsJSObject() &&
+ Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
+ return HasLocalPropertyImplementation(isolate,
+ Handle<JSObject>::cast(proto),
+ key);
+ }
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return isolate->heap()->false_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_HasLocalProperty) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(Name, key, 1);
+
+ uint32_t index;
+ const bool key_is_array_index = key->AsArrayIndex(&index);
+
+ Object* obj = args[0];
+ // Only JS objects can have properties.
+ if (obj->IsJSObject()) {
+ JSObject* object = JSObject::cast(obj);
+ // Fast case: either the key is a real named property or it is not
+ // an array index and there are no interceptors or hidden
+ // prototypes.
+ if (object->HasRealNamedProperty(isolate, key)) {
+ ASSERT(!isolate->has_scheduled_exception());
+ return isolate->heap()->true_value();
+ } else {
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ }
+ Map* map = object->map();
+ if (!key_is_array_index &&
+ !map->has_named_interceptor() &&
+ !HeapObject::cast(map->prototype())->map()->is_hidden_prototype()) {
+ return isolate->heap()->false_value();
+ }
+ // Slow case.
+ HandleScope scope(isolate);
+ return HasLocalPropertyImplementation(isolate,
+ Handle<JSObject>(object),
+ Handle<Name>(key));
+ } else if (obj->IsString() && key_is_array_index) {
+ // Well, there is one exception: Handle [] on strings.
+ String* string = String::cast(obj);
+ if (index < static_cast<uint32_t>(string->length())) {
+ return isolate->heap()->true_value();
+ }
+ }
+ return isolate->heap()->false_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_HasProperty) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSReceiver, receiver, 0);
+ CONVERT_ARG_CHECKED(Name, key, 1);
+
+ bool result = receiver->HasProperty(key);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (isolate->has_pending_exception()) return Failure::Exception();
+ return isolate->heap()->ToBoolean(result);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_HasElement) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSReceiver, receiver, 0);
+ CONVERT_SMI_ARG_CHECKED(index, 1);
+
+ bool result = receiver->HasElement(index);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (isolate->has_pending_exception()) return Failure::Exception();
+ return isolate->heap()->ToBoolean(result);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsPropertyEnumerable) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(JSObject, object, 0);
+ CONVERT_ARG_CHECKED(Name, key, 1);
+
+ PropertyAttributes att = object->GetLocalPropertyAttribute(key);
+ if (att == ABSENT || (att & DONT_ENUM) != 0) {
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return isolate->heap()->false_value();
+ }
+ ASSERT(!isolate->has_scheduled_exception());
+ return isolate->heap()->true_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNames) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0);
+ bool threw = false;
+ Handle<JSArray> result = GetKeysFor(object, &threw);
+ if (threw) return Failure::Exception();
+ return *result;
+}
+
+
+// Returns either a FixedArray as Runtime_GetPropertyNames,
+// or, if the given object has an enum cache that contains
+// all enumerable properties of the object and its prototypes
+// have none, the map of the object. This is used to speed up
+// the check for deletions during a for-in.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNamesFast) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSReceiver, raw_object, 0);
+
+ if (raw_object->IsSimpleEnum()) return raw_object->map();
+
+ HandleScope scope(isolate);
+ 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();
+
+ return *content;
+}
+
+
+// Find the length of the prototype chain that is to to handled as one. If a
+// prototype object is hidden it is to be viewed as part of the the object it
+// is prototype for.
+static int LocalPrototypeChainLength(JSObject* obj) {
+ int count = 1;
+ Object* proto = obj->GetPrototype();
+ while (proto->IsJSObject() &&
+ JSObject::cast(proto)->map()->is_hidden_prototype()) {
+ count++;
+ proto = JSObject::cast(proto)->GetPrototype();
+ }
+ return count;
+}
+
+
+// Return the names of the local named properties.
+// args[0]: object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalPropertyNames) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ if (!args[0]->IsJSObject()) {
+ return isolate->heap()->undefined_value();
+ }
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(include_symbols, 1);
+ PropertyAttributes filter = include_symbols ? NONE : SYMBOLIC;
+
+ // Skip the global proxy as it has no properties and always delegates to the
+ // real global object.
+ if (obj->IsJSGlobalProxy()) {
+ // Only collect names if access is permitted.
+ if (obj->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(*obj,
+ isolate->heap()->undefined_value(),
+ v8::ACCESS_KEYS)) {
+ isolate->ReportFailedAccessCheck(*obj, v8::ACCESS_KEYS);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return *isolate->factory()->NewJSArray(0);
+ }
+ obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
+ }
+
+ // Find the number of objects making up this.
+ int length = LocalPrototypeChainLength(*obj);
+
+ // Find the number of local properties for each of the objects.
+ ScopedVector<int> local_property_count(length);
+ int total_property_count = 0;
+ Handle<JSObject> jsproto = obj;
+ for (int i = 0; i < length; i++) {
+ // Only collect names if access is permitted.
+ if (jsproto->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(*jsproto,
+ isolate->heap()->undefined_value(),
+ v8::ACCESS_KEYS)) {
+ isolate->ReportFailedAccessCheck(*jsproto, v8::ACCESS_KEYS);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return *isolate->factory()->NewJSArray(0);
+ }
+ int n;
+ n = jsproto->NumberOfLocalProperties(filter);
+ local_property_count[i] = n;
+ total_property_count += n;
+ if (i < length - 1) {
+ jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
+ }
+ }
+
+ // Allocate an array with storage for all the property names.
+ Handle<FixedArray> names =
+ isolate->factory()->NewFixedArray(total_property_count);
+
+ // Get the property names.
+ jsproto = obj;
+ int proto_with_hidden_properties = 0;
+ int next_copy_index = 0;
+ for (int i = 0; i < length; i++) {
+ jsproto->GetLocalPropertyNames(*names, next_copy_index, filter);
+ next_copy_index += local_property_count[i];
+ if (jsproto->HasHiddenProperties()) {
+ proto_with_hidden_properties++;
+ }
+ if (i < length - 1) {
+ jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
+ }
+ }
+
+ // Filter out name of hidden properties object.
+ if (proto_with_hidden_properties > 0) {
+ Handle<FixedArray> old_names = names;
+ names = isolate->factory()->NewFixedArray(
+ names->length() - proto_with_hidden_properties);
+ int dest_pos = 0;
+ for (int i = 0; i < total_property_count; i++) {
+ Object* name = old_names->get(i);
+ if (name == isolate->heap()->hidden_string()) {
+ continue;
+ }
+ names->set(dest_pos++, name);
+ }
+ }
+
+ return *isolate->factory()->NewJSArrayWithElements(names);
+}
+
+
+// Return the names of the local indexed properties.
+// args[0]: object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLocalElementNames) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ if (!args[0]->IsJSObject()) {
+ return isolate->heap()->undefined_value();
+ }
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+
+ int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
+ Handle<FixedArray> names = isolate->factory()->NewFixedArray(n);
+ obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
+ return *isolate->factory()->NewJSArrayWithElements(names);
+}
+
+
+// Return information on whether an object has a named or indexed interceptor.
+// args[0]: object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetInterceptorInfo) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ if (!args[0]->IsJSObject()) {
+ return Smi::FromInt(0);
+ }
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+
+ int result = 0;
+ if (obj->HasNamedInterceptor()) result |= 2;
+ if (obj->HasIndexedInterceptor()) result |= 1;
+
+ return Smi::FromInt(result);
+}
+
+
+// Return property names from named interceptor.
+// args[0]: object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetNamedInterceptorPropertyNames) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+
+ if (obj->HasNamedInterceptor()) {
+ v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
+ if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+// Return element names from indexed interceptor.
+// args[0]: object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetIndexedInterceptorElementNames) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+
+ if (obj->HasIndexedInterceptor()) {
+ v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
+ if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LocalKeys) {
+ HandleScope scope(isolate);
+ ASSERT_EQ(args.length(), 1);
+ CONVERT_ARG_CHECKED(JSObject, raw_object, 0);
+ Handle<JSObject> object(raw_object);
+
+ if (object->IsJSGlobalProxy()) {
+ // Do access checks before going to the global object.
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(*object, isolate->heap()->undefined_value(),
+ v8::ACCESS_KEYS)) {
+ isolate->ReportFailedAccessCheck(*object, v8::ACCESS_KEYS);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return *isolate->factory()->NewJSArray(0);
+ }
+
+ Handle<Object> proto(object->GetPrototype(), isolate);
+ // If proxy is detached we simply return an empty array.
+ if (proto->IsNull()) return *isolate->factory()->NewJSArray(0);
+ object = Handle<JSObject>::cast(proto);
+ }
+
+ 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.
+ int length = contents->length();
+ Handle<FixedArray> copy = isolate->factory()->NewFixedArray(length);
+ for (int i = 0; i < length; i++) {
+ Object* entry = contents->get(i);
+ if (entry->IsString()) {
+ copy->set(i, entry);
+ } else {
+ ASSERT(entry->IsNumber());
+ HandleScope scope(isolate);
+ Handle<Object> entry_handle(entry, isolate);
+ Handle<Object> entry_str =
+ isolate->factory()->NumberToString(entry_handle);
+ copy->set(i, *entry_str);
+ }
+ }
+ return *isolate->factory()->NewJSArrayWithElements(copy);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArgumentsProperty) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ // Compute the frame holding the arguments.
+ JavaScriptFrameIterator it(isolate);
+ it.AdvanceToArgumentsFrame();
+ JavaScriptFrame* frame = it.frame();
+
+ // Get the actual number of provided arguments.
+ const uint32_t n = frame->ComputeParametersCount();
+
+ // Try to convert the key to an index. If successful and within
+ // index return the the argument from the frame.
+ uint32_t index;
+ if (args[0]->ToArrayIndex(&index) && index < n) {
+ return frame->GetParameter(index);
+ }
+
+ if (args[0]->IsSymbol()) {
+ // Lookup in the initial Object.prototype object.
+ return isolate->initial_object_prototype()->GetProperty(
+ Symbol::cast(args[0]));
+ }
+
+ // Convert the key to a string.
+ HandleScope scope(isolate);
+ bool exception = false;
+ Handle<Object> converted =
+ Execution::ToString(args.at<Object>(0), &exception);
+ if (exception) return Failure::Exception();
+ Handle<String> key = Handle<String>::cast(converted);
+
+ // Try to convert the string key into an array index.
+ if (key->AsArrayIndex(&index)) {
+ if (index < n) {
+ return frame->GetParameter(index);
+ } else {
+ return isolate->initial_object_prototype()->GetElement(index);
+ }
+ }
+
+ // Handle special arguments properties.
+ if (key->Equals(isolate->heap()->length_string())) return Smi::FromInt(n);
+ if (key->Equals(isolate->heap()->callee_string())) {
+ JSFunction* function = frame->function();
+ if (!function->shared()->is_classic_mode()) {
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "strict_arguments_callee", HandleVector<Object>(NULL, 0)));
+ }
+ return function;
+ }
+
+ // Lookup in the initial Object.prototype object.
+ return isolate->initial_object_prototype()->GetProperty(*key);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ToFastProperties) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ Object* object = args[0];
+ return (object->IsJSObject() && !object->IsGlobalObject())
+ ? JSObject::cast(object)->TransformToFastProperties(0)
+ : object;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ToBool) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ return isolate->heap()->ToBoolean(args[0]->BooleanValue());
+}
+
+
+// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
+// Possible optimizations: put the type string into the oddballs.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Typeof) {
+ SealHandleScope shs(isolate);
+
+ Object* obj = args[0];
+ if (obj->IsNumber()) return isolate->heap()->number_string();
+ HeapObject* heap_obj = HeapObject::cast(obj);
+
+ // typeof an undetectable object is 'undefined'
+ if (heap_obj->map()->is_undetectable()) {
+ return isolate->heap()->undefined_string();
+ }
+
+ InstanceType instance_type = heap_obj->map()->instance_type();
+ if (instance_type < FIRST_NONSTRING_TYPE) {
+ return isolate->heap()->string_string();
+ }
+
+ switch (instance_type) {
+ case ODDBALL_TYPE:
+ if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
+ return isolate->heap()->boolean_string();
+ }
+ if (heap_obj->IsNull()) {
+ return FLAG_harmony_typeof
+ ? isolate->heap()->null_string()
+ : isolate->heap()->object_string();
+ }
+ ASSERT(heap_obj->IsUndefined());
+ return isolate->heap()->undefined_string();
+ case SYMBOL_TYPE:
+ return isolate->heap()->symbol_string();
+ case JS_FUNCTION_TYPE:
+ case JS_FUNCTION_PROXY_TYPE:
+ return isolate->heap()->function_string();
+ default:
+ // For any kind of object not handled above, the spec rule for
+ // host objects gives that it is okay to return "object"
+ return isolate->heap()->object_string();
+ }
+}
+
+
+static bool AreDigits(const uint8_t*s, int from, int to) {
+ for (int i = from; i < to; i++) {
+ if (s[i] < '0' || s[i] > '9') return false;
+ }
+
+ return true;
+}
+
+
+static int ParseDecimalInteger(const uint8_t*s, int from, int to) {
+ ASSERT(to - from < 10); // Overflow is not possible.
+ ASSERT(from < to);
+ int d = s[from] - '0';
+
+ for (int i = from + 1; i < to; i++) {
+ d = 10 * d + (s[i] - '0');
+ }
+
+ return d;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToNumber) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(String, subject, 0);
+ subject->TryFlatten();
+
+ // Fast case: short integer or some sorts of junk values.
+ int len = subject->length();
+ if (subject->IsSeqOneByteString()) {
+ if (len == 0) return Smi::FromInt(0);
+
+ uint8_t const* data = SeqOneByteString::cast(subject)->GetChars();
+ bool minus = (data[0] == '-');
+ int start_pos = (minus ? 1 : 0);
+
+ if (start_pos == len) {
+ return isolate->heap()->nan_value();
+ } else if (data[start_pos] > '9') {
+ // Fast check for a junk value. A valid string may start from a
+ // whitespace, a sign ('+' or '-'), the decimal point, a decimal digit or
+ // the 'I' character ('Infinity'). All of that have codes not greater than
+ // '9' except 'I' and &nbsp;.
+ if (data[start_pos] != 'I' && data[start_pos] != 0xa0) {
+ return isolate->heap()->nan_value();
+ }
+ } else if (len - start_pos < 10 && AreDigits(data, start_pos, len)) {
+ // The maximal/minimal smi has 10 digits. If the string has less digits we
+ // know it will fit into the smi-data type.
+ int d = ParseDecimalInteger(data, start_pos, len);
+ if (minus) {
+ if (d == 0) return isolate->heap()->minus_zero_value();
+ d = -d;
+ } else if (!subject->HasHashCode() &&
+ len <= String::kMaxArrayIndexSize &&
+ (len == 1 || data[0] != '0')) {
+ // String hash is not calculated yet but all the data are present.
+ // Update the hash field to speed up sequential convertions.
+ uint32_t hash = StringHasher::MakeArrayIndexHash(d, len);
+#ifdef DEBUG
+ subject->Hash(); // Force hash calculation.
+ ASSERT_EQ(static_cast<int>(subject->hash_field()),
+ static_cast<int>(hash));
+#endif
+ subject->set_hash_field(hash);
+ }
+ return Smi::FromInt(d);
+ }
+ }
+
+ // Slower case.
+ int flags = ALLOW_HEX;
+ if (FLAG_harmony_numeric_literals) {
+ // The current spec draft has not updated "ToNumber Applied to the String
+ // Type", https://bugs.ecmascript.org/show_bug.cgi?id=1584
+ flags |= ALLOW_OCTAL | ALLOW_BINARY;
+ }
+ return isolate->heap()->NumberFromDouble(
+ StringToDouble(isolate->unicode_cache(), subject, flags));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewString) {
+ SealHandleScope shs(isolate);
+ CONVERT_SMI_ARG_CHECKED(length, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(is_one_byte, 1);
+ if (length == 0) return isolate->heap()->empty_string();
+ if (is_one_byte) {
+ return isolate->heap()->AllocateRawOneByteString(length);
+ } else {
+ return isolate->heap()->AllocateRawTwoByteString(length);
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TruncateString) {
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(SeqString, string, 0);
+ CONVERT_SMI_ARG_CHECKED(new_length, 1);
+ return *SeqString::Truncate(string, new_length);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_URIEscape) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
+ Handle<String> string = FlattenGetString(source);
+ ASSERT(string->IsFlat());
+ Handle<String> result = string->IsOneByteRepresentationUnderneath()
+ ? URIEscape::Escape<uint8_t>(isolate, source)
+ : URIEscape::Escape<uc16>(isolate, source);
+ if (result.is_null()) return Failure::OutOfMemoryException(0x12);
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_URIUnescape) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
+ Handle<String> string = FlattenGetString(source);
+ ASSERT(string->IsFlat());
+ return string->IsOneByteRepresentationUnderneath()
+ ? *URIUnescape::Unescape<uint8_t>(isolate, source)
+ : *URIUnescape::Unescape<uc16>(isolate, source);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_QuoteJSONString) {
+ HandleScope scope(isolate);
+ CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
+ ASSERT(args.length() == 1);
+ return BasicJsonStringifier::StringifyString(isolate, string);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_BasicJSONStringify) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ BasicJsonStringifier stringifier(isolate);
+ return stringifier.Stringify(Handle<Object>(args[0], isolate));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringParseInt) {
+ SealHandleScope shs(isolate);
+
+ CONVERT_ARG_CHECKED(String, s, 0);
+ CONVERT_SMI_ARG_CHECKED(radix, 1);
+
+ s->TryFlatten();
+
+ RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
+ double value = StringToInt(isolate->unicode_cache(), s, radix);
+ return isolate->heap()->NumberFromDouble(value);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringParseFloat) {
+ SealHandleScope shs(isolate);
+ CONVERT_ARG_CHECKED(String, str, 0);
+
+ // ECMA-262 section 15.1.2.3, empty string is NaN
+ double value = StringToDouble(isolate->unicode_cache(),
+ str, ALLOW_TRAILING_JUNK, OS::nan_value());
+
+ // Create a number object from the value.
+ return isolate->heap()->NumberFromDouble(value);
+}
+
+
+template <class Converter>
+MUST_USE_RESULT static MaybeObject* ConvertCaseHelper(
+ Isolate* isolate,
+ String* s,
+ int length,
+ int input_string_length,
+ unibrow::Mapping<Converter, 128>* mapping) {
+ // We try this twice, once with the assumption that the result is no longer
+ // than the input and, if that assumption breaks, again with the exact
+ // length. This may not be pretty, but it is nicer than what was here before
+ // and I hereby claim my vaffel-is.
+ //
+ // 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
+ // might break in the future if we implement more context and locale
+ // dependent upper/lower conversions.
+ Object* o;
+ { MaybeObject* maybe_o = s->IsOneByteRepresentation()
+ ? isolate->heap()->AllocateRawOneByteString(length)
+ : isolate->heap()->AllocateRawTwoByteString(length);
+ if (!maybe_o->ToObject(&o)) return maybe_o;
+ }
+ String* result = String::cast(o);
+ bool has_changed_character = false;
+
+ // Convert all characters to upper case, assuming that they will fit
+ // in the buffer
+ Access<ConsStringIteratorOp> op(
+ isolate->runtime_state()->string_iterator());
+ StringCharacterStream stream(s, op.value());
+ unibrow::uchar chars[Converter::kMaxWidth];
+ // We can assume that the string is not empty
+ uc32 current = stream.GetNext();
+ for (int i = 0; i < length;) {
+ bool has_next = stream.HasMore();
+ uc32 next = has_next ? stream.GetNext() : 0;
+ int char_length = mapping->get(current, next, chars);
+ if (char_length == 0) {
+ // The case conversion of this character is the character itself.
+ result->Set(i, current);
+ i++;
+ } else if (char_length == 1) {
+ // Common case: converting the letter resulted in one character.
+ ASSERT(static_cast<uc32>(chars[0]) != current);
+ result->Set(i, chars[0]);
+ has_changed_character = true;
+ i++;
+ } else if (length == input_string_length) {
+ // We've assumed that the result would be as long as the
+ // input but here is a character that converts to several
+ // characters. No matter, we calculate the exact length
+ // of the result and try the whole thing again.
+ //
+ // Note that this leaves room for optimization. We could just
+ // memcpy what we already have to the result string. Also,
+ // the result string is the last object allocated we could
+ // "realloc" it and probably, in the vast majority of cases,
+ // extend the existing string to be able to hold the full
+ // result.
+ int next_length = 0;
+ if (has_next) {
+ next_length = mapping->get(next, 0, chars);
+ if (next_length == 0) next_length = 1;
+ }
+ int current_length = i + char_length + next_length;
+ while (stream.HasMore()) {
+ current = stream.GetNext();
+ // NOTE: we use 0 as the next character here because, while
+ // the next character may affect what a character converts to,
+ // it does not in any case affect the length of what it convert
+ // to.
+ int char_length = mapping->get(current, 0, chars);
+ if (char_length == 0) char_length = 1;
+ current_length += char_length;
+ if (current_length > Smi::kMaxValue) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x13);
+ }
+ }
+ // Try again with the real length.
+ return Smi::FromInt(current_length);
+ } else {
+ for (int j = 0; j < char_length; j++) {
+ result->Set(i, chars[j]);
+ i++;
+ }
+ has_changed_character = true;
+ }
+ current = next;
+ }
+ if (has_changed_character) {
+ return result;
+ } else {
+ // If we didn't actually change anything in doing the conversion
+ // we simple return the result and let the converted string
+ // become garbage; there is no reason to keep two identical strings
+ // alive.
+ return s;
+ }
+}
+
+
+namespace {
+
+static const uintptr_t kOneInEveryByte = kUintptrAllBitsSet / 0xFF;
+static const uintptr_t kAsciiMask = kOneInEveryByte << 7;
+
+// Given a word and two range boundaries returns a word with high bit
+// set in every byte iff the corresponding input byte was strictly in
+// the range (m, n). All the other bits in the result are cleared.
+// 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).
+static inline uintptr_t AsciiRangeMask(uintptr_t w, char m, char n) {
+ // Use strict inequalities since in edge cases the function could be
+ // further simplified.
+ ASSERT(0 < m && m < n);
+ // Has high bit set in every w byte less than n.
+ uintptr_t tmp1 = kOneInEveryByte * (0x7F + n) - w;
+ // Has high bit set in every w byte greater than m.
+ uintptr_t tmp2 = w + kOneInEveryByte * (0x7F - m);
+ return (tmp1 & tmp2 & (kOneInEveryByte * 0x80));
+}
+
+
+enum AsciiCaseConversion {
+ ASCII_TO_LOWER,
+ ASCII_TO_UPPER
+};
+
+
+template <AsciiCaseConversion dir>
+struct FastAsciiConverter {
+ static bool Convert(char* dst, char* src, int length, bool* changed_out) {
+#ifdef DEBUG
+ char* saved_dst = dst;
+ char* saved_src = src;
+#endif
+ // We rely on the distance between upper and lower case letters
+ // being a known power of 2.
+ ASSERT('a' - 'A' == (1 << 5));
+ // Boundaries for the range of input characters than require conversion.
+ const char lo = (dir == ASCII_TO_LOWER) ? 'A' - 1 : 'a' - 1;
+ const char hi = (dir == ASCII_TO_LOWER) ? 'Z' + 1 : 'z' + 1;
+ bool changed = false;
+ uintptr_t or_acc = 0;
+ char* const limit = src + length;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ // Process the prefix of the input that requires no conversion one
+ // (machine) word at a time.
+ while (src <= limit - sizeof(uintptr_t)) {
+ uintptr_t w = *reinterpret_cast<uintptr_t*>(src);
+ or_acc |= w;
+ if (AsciiRangeMask(w, lo, hi) != 0) {
+ changed = true;
+ break;
+ }
+ *reinterpret_cast<uintptr_t*>(dst) = w;
+ src += sizeof(uintptr_t);
+ dst += sizeof(uintptr_t);
+ }
+ // Process the remainder of the input performing conversion when
+ // required one word at a time.
+ while (src <= limit - sizeof(uintptr_t)) {
+ uintptr_t w = *reinterpret_cast<uintptr_t*>(src);
+ or_acc |= w;
+ uintptr_t m = AsciiRangeMask(w, lo, hi);
+ // The mask has high (7th) bit set in every byte that needs
+ // conversion and we know that the distance between cases is
+ // 1 << 5.
+ *reinterpret_cast<uintptr_t*>(dst) = w ^ (m >> 2);
+ src += sizeof(uintptr_t);
+ dst += sizeof(uintptr_t);
+ }
+#endif
+ // Process the last few bytes of the input (or the whole input if
+ // unaligned access is not supported).
+ while (src < limit) {
+ char c = *src;
+ or_acc |= c;
+ if (lo < c && c < hi) {
+ c ^= (1 << 5);
+ changed = true;
+ }
+ *dst = c;
+ ++src;
+ ++dst;
+ }
+ if ((or_acc & kAsciiMask) != 0) {
+ return false;
+ }
+#ifdef DEBUG
+ CheckConvert(saved_dst, saved_src, length, changed);
+#endif
+ *changed_out = changed;
+ return true;
+ }
+
+#ifdef DEBUG
+ static void CheckConvert(char* dst, char* src, int length, bool changed) {
+ bool expected_changed = false;
+ for (int i = 0; i < length; i++) {
+ if (dst[i] == src[i]) continue;
+ expected_changed = true;
+ if (dir == ASCII_TO_LOWER) {
+ ASSERT('A' <= src[i] && src[i] <= 'Z');
+ ASSERT(dst[i] == src[i] + ('a' - 'A'));
+ } else {
+ ASSERT(dir == ASCII_TO_UPPER);
+ ASSERT('a' <= src[i] && src[i] <= 'z');
+ ASSERT(dst[i] == src[i] - ('a' - 'A'));
+ }
+ }
+ ASSERT(expected_changed == changed);
+ }
+#endif
+};
+
+
+struct ToLowerTraits {
+ typedef unibrow::ToLowercase UnibrowConverter;
+
+ typedef FastAsciiConverter<ASCII_TO_LOWER> AsciiConverter;
+};
+
+
+struct ToUpperTraits {
+ typedef unibrow::ToUppercase UnibrowConverter;
+
+ typedef FastAsciiConverter<ASCII_TO_UPPER> AsciiConverter;
+};
+
+} // namespace
+
+
+template <typename ConvertTraits>
+MUST_USE_RESULT static MaybeObject* ConvertCase(
+ Arguments args,
+ Isolate* isolate,
+ unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
+ SealHandleScope shs(isolate);
+ CONVERT_ARG_CHECKED(String, s, 0);
+ s = s->TryFlattenGetString();
+
+ const int length = s->length();
+ // Assume that the string is not empty; we need this assumption later
+ if (length == 0) return s;
+
+ // 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
+ // might break in the future if we implement more context and locale
+ // dependent upper/lower conversions.
+ if (s->IsSeqOneByteString()) {
+ Object* o;
+ { MaybeObject* maybe_o = isolate->heap()->AllocateRawOneByteString(length);
+ if (!maybe_o->ToObject(&o)) return maybe_o;
+ }
+ SeqOneByteString* result = SeqOneByteString::cast(o);
+ bool has_changed_character;
+ bool is_ascii = ConvertTraits::AsciiConverter::Convert(
+ reinterpret_cast<char*>(result->GetChars()),
+ reinterpret_cast<char*>(SeqOneByteString::cast(s)->GetChars()),
+ length,
+ &has_changed_character);
+ // If not ASCII, we discard the result and take the 2 byte path.
+ if (is_ascii) {
+ return has_changed_character ? result : s;
+ }
+ }
+
+ Object* answer;
+ { MaybeObject* maybe_answer =
+ ConvertCaseHelper(isolate, s, length, length, mapping);
+ if (!maybe_answer->ToObject(&answer)) return maybe_answer;
+ }
+ if (answer->IsSmi()) {
+ // Retry with correct length.
+ { MaybeObject* maybe_answer =
+ ConvertCaseHelper(isolate,
+ s, Smi::cast(answer)->value(), length, mapping);
+ if (!maybe_answer->ToObject(&answer)) return maybe_answer;
+ }
+ }
+ return answer;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToLowerCase) {
+ return ConvertCase<ToLowerTraits>(
+ args, isolate, isolate->runtime_state()->to_lower_mapping());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToUpperCase) {
+ return ConvertCase<ToUpperTraits>(
+ args, isolate, isolate->runtime_state()->to_upper_mapping());
+}
+
+
+static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
+ return unibrow::WhiteSpace::Is(c) || c == 0x200b || c == 0xfeff;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringTrim) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_CHECKED(String, s, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(trimLeft, 1);
+ CONVERT_BOOLEAN_ARG_CHECKED(trimRight, 2);
+
+ s->TryFlatten();
+ int length = s->length();
+
+ int left = 0;
+ if (trimLeft) {
+ while (left < length && IsTrimWhiteSpace(s->Get(left))) {
+ left++;
+ }
+ }
+
+ int right = length;
+ if (trimRight) {
+ while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
+ right--;
+ }
+ }
+ return s->SubString(left, right);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) {
+ HandleScope handle_scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1);
+ CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]);
+
+ int subject_length = subject->length();
+ int pattern_length = pattern->length();
+ RUNTIME_ASSERT(pattern_length > 0);
+
+ if (limit == 0xffffffffu) {
+ Handle<Object> cached_answer(
+ RegExpResultsCache::Lookup(isolate->heap(),
+ *subject,
+ *pattern,
+ RegExpResultsCache::STRING_SPLIT_SUBSTRINGS),
+ isolate);
+ if (*cached_answer != Smi::FromInt(0)) {
+ // The cache FixedArray is a COW-array and can therefore be reused.
+ Handle<JSArray> result =
+ isolate->factory()->NewJSArrayWithElements(
+ Handle<FixedArray>::cast(cached_answer));
+ return *result;
+ }
+ }
+
+ // The limit can be very large (0xffffffffu), but since the pattern
+ // isn't empty, we can never create more parts than ~half the length
+ // of the subject.
+
+ if (!subject->IsFlat()) FlattenString(subject);
+
+ static const int kMaxInitialListCapacity = 16;
+
+ ZoneScope zone_scope(isolate->runtime_zone());
+
+ // Find (up to limit) indices of separator and end-of-string in subject
+ int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
+ ZoneList<int> indices(initial_capacity, zone_scope.zone());
+ if (!pattern->IsFlat()) FlattenString(pattern);
+
+ FindStringIndicesDispatch(isolate, *subject, *pattern,
+ &indices, limit, zone_scope.zone());
+
+ if (static_cast<uint32_t>(indices.length()) < limit) {
+ indices.Add(subject_length, zone_scope.zone());
+ }
+
+ // The list indices now contains the end of each part to create.
+
+ // Create JSArray of substrings separated by separator.
+ 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->HasFastObjectElements());
+
+ if (part_count == 1 && indices.at(0) == subject_length) {
+ FixedArray::cast(result->elements())->set(0, *subject);
+ return *result;
+ }
+
+ Handle<FixedArray> elements(FixedArray::cast(result->elements()));
+ int part_start = 0;
+ for (int i = 0; i < part_count; i++) {
+ HandleScope local_loop_handle(isolate);
+ int part_end = indices.at(i);
+ Handle<String> substring =
+ isolate->factory()->NewProperSubString(subject, part_start, part_end);
+ elements->set(i, *substring);
+ part_start = part_end + pattern_length;
+ }
+
+ if (limit == 0xffffffffu) {
+ if (result->HasFastObjectElements()) {
+ RegExpResultsCache::Enter(isolate->heap(),
+ *subject,
+ *pattern,
+ *elements,
+ RegExpResultsCache::STRING_SPLIT_SUBSTRINGS);
+ }
+ }
+
+ return *result;
+}
+
+
+// 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.
+static int CopyCachedAsciiCharsToArray(Heap* heap,
+ const uint8_t* chars,
+ FixedArray* elements,
+ int length) {
+ DisallowHeapAllocation no_gc;
+ 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;
+ elements->set(i, value, mode);
+ }
+ if (i < length) {
+ ASSERT(Smi::FromInt(0) == 0);
+ memset(elements->data_start() + i, 0, kPointerSize * (length - i));
+ }
+#ifdef DEBUG
+ for (int j = 0; j < length; ++j) {
+ Object* element = elements->get(j);
+ ASSERT(element == Smi::FromInt(0) ||
+ (element->IsString() && String::cast(element)->LooksValid()));
+ }
+#endif
+ return i;
+}
+
+
+// Converts a String to JSArray.
+// For example, "foo" => ["f", "o", "o"].
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringToArray) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
+ CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
+
+ s = FlattenGetString(s);
+ const int length = static_cast<int>(Min<uint32_t>(s->length(), limit));
+
+ Handle<FixedArray> elements;
+ int position = 0;
+ if (s->IsFlat() && s->IsOneByteRepresentation()) {
+ // Try using cached chars where possible.
+ Object* obj;
+ { MaybeObject* maybe_obj =
+ isolate->heap()->AllocateUninitializedFixedArray(length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ elements = Handle<FixedArray>(FixedArray::cast(obj), isolate);
+ DisallowHeapAllocation no_gc;
+ String::FlatContent content = s->GetFlatContent();
+ if (content.IsAscii()) {
+ Vector<const uint8_t> chars = content.ToOneByteVector();
+ // Note, this will initialize all elements (not only the prefix)
+ // to prevent GC from seeing partially initialized array.
+ position = CopyCachedAsciiCharsToArray(isolate->heap(),
+ chars.start(),
+ *elements,
+ length);
+ } else {
+ MemsetPointer(elements->data_start(),
+ isolate->heap()->undefined_value(),
+ length);
+ }
+ } else {
+ elements = isolate->factory()->NewFixedArray(length);
+ }
+ for (int i = position; i < length; ++i) {
+ Handle<Object> str =
+ LookupSingleCharacterStringFromCode(isolate, s->Get(i));
+ elements->set(i, *str);
+ }
+
+#ifdef DEBUG
+ for (int i = 0; i < length; ++i) {
+ ASSERT(String::cast(elements->get(i))->length() == 1);
+ }
+#endif
+
+ return *isolate->factory()->NewJSArrayWithElements(elements);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewStringWrapper) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(String, value, 0);
+ return value->ToObject();
+}
+
+
+bool Runtime::IsUpperCaseChar(RuntimeState* runtime_state, uint16_t ch) {
+ unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
+ int char_length = runtime_state->to_upper_mapping()->get(ch, 0, chars);
+ return char_length == 0;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToString) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ Object* number = args[0];
+ RUNTIME_ASSERT(number->IsNumber());
+
+ return isolate->heap()->NumberToString(number);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToStringSkipCache) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ Object* number = args[0];
+ RUNTIME_ASSERT(number->IsNumber());
+
+ return isolate->heap()->NumberToString(
+ number, false, isolate->heap()->GetPretenureMode());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToInteger) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(number, 0);
+
+ // We do not include 0 so that we don't have to treat +0 / -0 cases.
+ if (number > 0 && number <= Smi::kMaxValue) {
+ return Smi::FromInt(static_cast<int>(number));
+ }
+ return isolate->heap()->NumberFromDouble(DoubleToInteger(number));
+}
+
+
+// ES6 draft 9.1.11
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToPositiveInteger) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(number, 0);
+
+ // We do not include 0 so that we don't have to treat +0 / -0 cases.
+ if (number > 0 && number <= Smi::kMaxValue) {
+ return Smi::FromInt(static_cast<int>(number));
+ }
+ if (number <= 0) {
+ return Smi::FromInt(0);
+ }
+ return isolate->heap()->NumberFromDouble(DoubleToInteger(number));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToIntegerMapMinusZero) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(number, 0);
+
+ // We do not include 0 so that we don't have to treat +0 / -0 cases.
+ if (number > 0 && number <= Smi::kMaxValue) {
+ return Smi::FromInt(static_cast<int>(number));
+ }
+
+ double double_value = DoubleToInteger(number);
+ // Map both -0 and +0 to +0.
+ if (double_value == 0) double_value = 0;
+
+ return isolate->heap()->NumberFromDouble(double_value);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToJSUint32) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
+ return isolate->heap()->NumberFromUint32(number);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToJSInt32) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(number, 0);
+
+ // We do not include 0 so that we don't have to treat +0 / -0 cases.
+ if (number > 0 && number <= Smi::kMaxValue) {
+ return Smi::FromInt(static_cast<int>(number));
+ }
+ return isolate->heap()->NumberFromInt32(DoubleToInt32(number));
+}
+
+
+// Converts a Number to a Smi, if possible. Returns NaN if the number is not
+// a small integer.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberToSmi) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ Object* obj = args[0];
+ if (obj->IsSmi()) {
+ return obj;
+ }
+ if (obj->IsHeapNumber()) {
+ double value = HeapNumber::cast(obj)->value();
+ int int_value = FastD2I(value);
+ if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
+ return Smi::FromInt(int_value);
+ }
+ }
+ return isolate->heap()->nan_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_AllocateHeapNumber) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ return isolate->heap()->AllocateHeapNumber(0);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberAdd) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return isolate->heap()->NumberFromDouble(x + y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberSub) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return isolate->heap()->NumberFromDouble(x - y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberMul) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return isolate->heap()->NumberFromDouble(x * y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberUnaryMinus) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->heap()->NumberFromDouble(-x);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberAlloc) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+
+ return isolate->heap()->NumberFromDouble(9876543210.0);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberDiv) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ return isolate->heap()->NumberFromDouble(x / y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberMod) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+
+ x = modulo(x, y);
+ // NumberFromDouble may return a Smi instead of a Number object
+ return isolate->heap()->NumberFromDouble(x);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberImul) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return isolate->heap()->NumberFromInt32(x * y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringAdd) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(String, str1, 0);
+ CONVERT_ARG_CHECKED(String, str2, 1);
+ isolate->counters()->string_add_runtime()->Increment();
+ return isolate->heap()->AllocateConsString(str1, str2);
+}
+
+
+template <typename sinkchar>
+static inline void StringBuilderConcatHelper(String* special,
+ sinkchar* sink,
+ FixedArray* fixed_array,
+ int array_length) {
+ int position = 0;
+ for (int i = 0; i < array_length; i++) {
+ Object* element = fixed_array->get(i);
+ if (element->IsSmi()) {
+ // Smi encoding of position and length.
+ int encoded_slice = Smi::cast(element)->value();
+ int pos;
+ int len;
+ if (encoded_slice > 0) {
+ // Position and length encoded in one smi.
+ pos = StringBuilderSubstringPosition::decode(encoded_slice);
+ len = StringBuilderSubstringLength::decode(encoded_slice);
+ } else {
+ // Position and length encoded in two smis.
+ Object* obj = fixed_array->get(++i);
+ ASSERT(obj->IsSmi());
+ pos = Smi::cast(obj)->value();
+ len = -encoded_slice;
+ }
+ String::WriteToFlat(special,
+ sink + position,
+ pos,
+ pos + len);
+ position += len;
+ } else {
+ String* string = String::cast(element);
+ int element_length = string->length();
+ String::WriteToFlat(string, sink + position, 0, element_length);
+ position += element_length;
+ }
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_CHECKED(JSArray, array, 0);
+ if (!args[1]->IsSmi()) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x14);
+ }
+ int array_length = args.smi_at(1);
+ CONVERT_ARG_CHECKED(String, special, 2);
+
+ // 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->HasFastObjectElements()) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+ FixedArray* fixed_array = FixedArray::cast(array->elements());
+ if (fixed_array->length() < array_length) {
+ array_length = fixed_array->length();
+ }
+
+ if (array_length == 0) {
+ return isolate->heap()->empty_string();
+ } else if (array_length == 1) {
+ Object* first = fixed_array->get(0);
+ if (first->IsString()) return first;
+ }
+
+ bool one_byte = special->HasOnlyOneByteChars();
+ int position = 0;
+ for (int i = 0; i < array_length; i++) {
+ int increment = 0;
+ Object* elt = fixed_array->get(i);
+ if (elt->IsSmi()) {
+ // Smi encoding of position and length.
+ int smi_value = Smi::cast(elt)->value();
+ int pos;
+ int len;
+ if (smi_value > 0) {
+ // Position and length encoded in one smi.
+ pos = StringBuilderSubstringPosition::decode(smi_value);
+ len = StringBuilderSubstringLength::decode(smi_value);
+ } else {
+ // Position and length encoded in two smis.
+ len = -smi_value;
+ // Get the position and check that it is a positive smi.
+ i++;
+ if (i >= array_length) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+ Object* next_smi = fixed_array->get(i);
+ if (!next_smi->IsSmi()) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+ pos = Smi::cast(next_smi)->value();
+ if (pos < 0) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+ }
+ ASSERT(pos >= 0);
+ ASSERT(len >= 0);
+ if (pos > special_length || len > special_length - pos) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+ increment = len;
+ } else if (elt->IsString()) {
+ String* element = String::cast(elt);
+ int element_length = element->length();
+ increment = element_length;
+ if (one_byte && !element->HasOnlyOneByteChars()) {
+ one_byte = false;
+ }
+ } else {
+ ASSERT(!elt->IsTheHole());
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+ if (increment > String::kMaxLength - position) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x15);
+ }
+ position += increment;
+ }
+
+ int length = position;
+ Object* object;
+
+ if (one_byte) {
+ { MaybeObject* maybe_object =
+ isolate->heap()->AllocateRawOneByteString(length);
+ if (!maybe_object->ToObject(&object)) return maybe_object;
+ }
+ SeqOneByteString* answer = SeqOneByteString::cast(object);
+ StringBuilderConcatHelper(special,
+ answer->GetChars(),
+ fixed_array,
+ array_length);
+ return answer;
+ } else {
+ { MaybeObject* maybe_object =
+ isolate->heap()->AllocateRawTwoByteString(length);
+ if (!maybe_object->ToObject(&object)) return maybe_object;
+ }
+ SeqTwoByteString* answer = SeqTwoByteString::cast(object);
+ StringBuilderConcatHelper(special,
+ answer->GetChars(),
+ fixed_array,
+ array_length);
+ return answer;
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_CHECKED(JSArray, array, 0);
+ if (!args[1]->IsSmi()) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x16);
+ }
+ int array_length = args.smi_at(1);
+ CONVERT_ARG_CHECKED(String, separator, 2);
+
+ if (!array->HasFastObjectElements()) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+ FixedArray* fixed_array = FixedArray::cast(array->elements());
+ if (fixed_array->length() < array_length) {
+ array_length = fixed_array->length();
+ }
+
+ if (array_length == 0) {
+ return isolate->heap()->empty_string();
+ } else if (array_length == 1) {
+ Object* first = fixed_array->get(0);
+ if (first->IsString()) return first;
+ }
+
+ int separator_length = separator->length();
+ int max_nof_separators =
+ (String::kMaxLength + separator_length - 1) / separator_length;
+ if (max_nof_separators < (array_length - 1)) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x17);
+ }
+ int length = (array_length - 1) * separator_length;
+ for (int i = 0; i < array_length; i++) {
+ Object* element_obj = fixed_array->get(i);
+ if (!element_obj->IsString()) {
+ // TODO(1161): handle this case.
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+ String* element = String::cast(element_obj);
+ int increment = element->length();
+ if (increment > String::kMaxLength - length) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException(0x18);
+ }
+ length += increment;
+ }
+
+ Object* object;
+ { MaybeObject* maybe_object =
+ isolate->heap()->AllocateRawTwoByteString(length);
+ if (!maybe_object->ToObject(&object)) return maybe_object;
+ }
+ SeqTwoByteString* answer = SeqTwoByteString::cast(object);
+
+ uc16* sink = answer->GetChars();
+#ifdef DEBUG
+ uc16* end = sink + length;
+#endif
+
+ String* first = String::cast(fixed_array->get(0));
+ int first_length = first->length();
+ String::WriteToFlat(first, sink, 0, first_length);
+ sink += first_length;
+
+ for (int i = 1; i < array_length; i++) {
+ ASSERT(sink + separator_length <= end);
+ String::WriteToFlat(separator, sink, 0, separator_length);
+ sink += separator_length;
+
+ String* element = String::cast(fixed_array->get(i));
+ int element_length = element->length();
+ ASSERT(sink + element_length <= end);
+ String::WriteToFlat(element, sink, 0, element_length);
+ sink += element_length;
+ }
+ ASSERT(sink == end);
+
+ // Use %_FastAsciiArrayJoin instead.
+ ASSERT(!answer->IsOneByteRepresentation());
+ return answer;
+}
+
+template <typename Char>
+static void JoinSparseArrayWithSeparator(FixedArray* elements,
+ int elements_length,
+ uint32_t array_length,
+ String* separator,
+ Vector<Char> buffer) {
+ int previous_separator_position = 0;
+ int separator_length = separator->length();
+ int cursor = 0;
+ for (int i = 0; i < elements_length; i += 2) {
+ int position = NumberToInt32(elements->get(i));
+ String* string = String::cast(elements->get(i + 1));
+ int string_length = string->length();
+ if (string->length() > 0) {
+ while (previous_separator_position < position) {
+ String::WriteToFlat<Char>(separator, &buffer[cursor],
+ 0, separator_length);
+ cursor += separator_length;
+ previous_separator_position++;
+ }
+ String::WriteToFlat<Char>(string, &buffer[cursor],
+ 0, string_length);
+ cursor += string->length();
+ }
+ }
+ if (separator_length > 0) {
+ // Array length must be representable as a signed 32-bit number,
+ // otherwise the total string length would have been too large.
+ ASSERT(array_length <= 0x7fffffff); // Is int32_t.
+ int last_array_index = static_cast<int>(array_length - 1);
+ while (previous_separator_position < last_array_index) {
+ String::WriteToFlat<Char>(separator, &buffer[cursor],
+ 0, separator_length);
+ cursor += separator_length;
+ previous_separator_position++;
+ }
+ }
+ ASSERT(cursor <= buffer.length());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_CHECKED(JSArray, elements_array, 0);
+ RUNTIME_ASSERT(elements_array->HasFastSmiOrObjectElements());
+ CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]);
+ CONVERT_ARG_CHECKED(String, separator, 2);
+ // elements_array is fast-mode JSarray of alternating positions
+ // (increasing order) and strings.
+ // array_length is length of original array (used to add separators);
+ // separator is string to put between elements. Assumed to be non-empty.
+
+ // Find total length of join result.
+ int string_length = 0;
+ bool is_ascii = separator->IsOneByteRepresentation();
+ int max_string_length;
+ if (is_ascii) {
+ max_string_length = SeqOneByteString::kMaxLength;
+ } else {
+ max_string_length = SeqTwoByteString::kMaxLength;
+ }
+ bool overflow = false;
+ CONVERT_NUMBER_CHECKED(int, elements_length,
+ Int32, elements_array->length());
+ RUNTIME_ASSERT((elements_length & 1) == 0); // Even length.
+ FixedArray* elements = FixedArray::cast(elements_array->elements());
+ for (int i = 0; i < elements_length; i += 2) {
+ RUNTIME_ASSERT(elements->get(i)->IsNumber());
+ RUNTIME_ASSERT(elements->get(i + 1)->IsString());
+ String* string = String::cast(elements->get(i + 1));
+ int length = string->length();
+ if (is_ascii && !string->IsOneByteRepresentation()) {
+ is_ascii = false;
+ max_string_length = SeqTwoByteString::kMaxLength;
+ }
+ if (length > max_string_length ||
+ max_string_length - length < string_length) {
+ overflow = true;
+ break;
+ }
+ string_length += length;
+ }
+ int separator_length = separator->length();
+ if (!overflow && separator_length > 0) {
+ if (array_length <= 0x7fffffffu) {
+ int separator_count = static_cast<int>(array_length) - 1;
+ int remaining_length = max_string_length - string_length;
+ if ((remaining_length / separator_length) >= separator_count) {
+ string_length += separator_length * (array_length - 1);
+ } else {
+ // Not room for the separators within the maximal string length.
+ overflow = true;
+ }
+ } else {
+ // Nonempty separator and at least 2^31-1 separators necessary
+ // means that the string is too large to create.
+ STATIC_ASSERT(String::kMaxLength < 0x7fffffff);
+ overflow = true;
+ }
+ }
+ if (overflow) {
+ // Throw OutOfMemory exception for creating too large a string.
+ V8::FatalProcessOutOfMemory("Array join result too large.");
+ }
+
+ if (is_ascii) {
+ MaybeObject* result_allocation =
+ isolate->heap()->AllocateRawOneByteString(string_length);
+ if (result_allocation->IsFailure()) return result_allocation;
+ SeqOneByteString* result_string =
+ SeqOneByteString::cast(result_allocation->ToObjectUnchecked());
+ JoinSparseArrayWithSeparator<uint8_t>(elements,
+ elements_length,
+ array_length,
+ separator,
+ Vector<uint8_t>(
+ result_string->GetChars(),
+ string_length));
+ return result_string;
+ } else {
+ MaybeObject* result_allocation =
+ isolate->heap()->AllocateRawTwoByteString(string_length);
+ if (result_allocation->IsFailure()) return result_allocation;
+ SeqTwoByteString* result_string =
+ SeqTwoByteString::cast(result_allocation->ToObjectUnchecked());
+ JoinSparseArrayWithSeparator<uc16>(elements,
+ elements_length,
+ array_length,
+ separator,
+ Vector<uc16>(result_string->GetChars(),
+ string_length));
+ return result_string;
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberOr) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return isolate->heap()->NumberFromInt32(x | y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberAnd) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return isolate->heap()->NumberFromInt32(x & y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberXor) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return isolate->heap()->NumberFromInt32(x ^ y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberShl) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return isolate->heap()->NumberFromInt32(x << (y & 0x1f));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberShr) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return isolate->heap()->NumberFromUint32(x >> (y & 0x1f));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberSar) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
+ CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
+ return isolate->heap()->NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberEquals) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ if (std::isnan(x)) return Smi::FromInt(NOT_EQUAL);
+ if (std::isnan(y)) return Smi::FromInt(NOT_EQUAL);
+ if (x == y) return Smi::FromInt(EQUAL);
+ Object* result;
+ if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
+ result = Smi::FromInt(EQUAL);
+ } else {
+ result = Smi::FromInt(NOT_EQUAL);
+ }
+ return result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringEquals) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(String, x, 0);
+ CONVERT_ARG_CHECKED(String, y, 1);
+
+ bool not_equal = !x->Equals(y);
+ // This is slightly convoluted because the value that signifies
+ // equality is 0 and inequality is 1 so we have to negate the result
+ // from String::Equals.
+ ASSERT(not_equal == 0 || not_equal == 1);
+ STATIC_CHECK(EQUAL == 0);
+ STATIC_CHECK(NOT_EQUAL == 1);
+ return Smi::FromInt(not_equal);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberCompare) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ if (std::isnan(x) || std::isnan(y)) return args[2];
+ if (x == y) return Smi::FromInt(EQUAL);
+ if (isless(x, y)) return Smi::FromInt(LESS);
+ return Smi::FromInt(GREATER);
+}
+
+
+// Compare two Smis as if they were converted to strings and then
+// compared lexicographically.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SmiLexicographicCompare) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_SMI_ARG_CHECKED(x_value, 0);
+ CONVERT_SMI_ARG_CHECKED(y_value, 1);
+
+ // If the integers are equal so are the string representations.
+ if (x_value == y_value) return Smi::FromInt(EQUAL);
+
+ // If one of the integers is zero the normal integer order is the
+ // same as the lexicographic order of the string representations.
+ if (x_value == 0 || y_value == 0)
+ return Smi::FromInt(x_value < y_value ? LESS : GREATER);
+
+ // If only one of the integers is negative the negative number is
+ // smallest because the char code of '-' is less than the char code
+ // of any digit. Otherwise, we make both values positive.
+
+ // Use unsigned values otherwise the logic is incorrect for -MIN_INT on
+ // architectures using 32-bit Smis.
+ uint32_t x_scaled = x_value;
+ uint32_t y_scaled = y_value;
+ if (x_value < 0 || y_value < 0) {
+ if (y_value >= 0) return Smi::FromInt(LESS);
+ if (x_value >= 0) return Smi::FromInt(GREATER);
+ x_scaled = -x_value;
+ y_scaled = -y_value;
+ }
+
+ static const uint32_t kPowersOf10[] = {
+ 1, 10, 100, 1000, 10*1000, 100*1000,
+ 1000*1000, 10*1000*1000, 100*1000*1000,
+ 1000*1000*1000
+ };
+
+ // If the integers have the same number of decimal digits they can be
+ // compared directly as the numeric order is the same as the
+ // lexicographic order. If one integer has fewer digits, it is scaled
+ // by some power of 10 to have the same number of digits as the longer
+ // integer. If the scaled integers are equal it means the shorter
+ // integer comes first in the lexicographic order.
+
+ // From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+ int x_log2 = IntegerLog2(x_scaled);
+ int x_log10 = ((x_log2 + 1) * 1233) >> 12;
+ x_log10 -= x_scaled < kPowersOf10[x_log10];
+
+ int y_log2 = IntegerLog2(y_scaled);
+ int y_log10 = ((y_log2 + 1) * 1233) >> 12;
+ y_log10 -= y_scaled < kPowersOf10[y_log10];
+
+ int tie = EQUAL;
+
+ if (x_log10 < y_log10) {
+ // X has fewer digits. We would like to simply scale up X but that
+ // might overflow, e.g when comparing 9 with 1_000_000_000, 9 would
+ // be scaled up to 9_000_000_000. So we scale up by the next
+ // smallest power and scale down Y to drop one digit. It is OK to
+ // drop one digit from the longer integer since the final digit is
+ // past the length of the shorter integer.
+ x_scaled *= kPowersOf10[y_log10 - x_log10 - 1];
+ y_scaled /= 10;
+ tie = LESS;
+ } else if (y_log10 < x_log10) {
+ y_scaled *= kPowersOf10[x_log10 - y_log10 - 1];
+ x_scaled /= 10;
+ tie = GREATER;
+ }
+
+ if (x_scaled < y_scaled) return Smi::FromInt(LESS);
+ if (x_scaled > y_scaled) return Smi::FromInt(GREATER);
+ return Smi::FromInt(tie);
+}
+
+
+static Object* StringCharacterStreamCompare(RuntimeState* state,
+ String* x,
+ String* y) {
+ StringCharacterStream stream_x(x, state->string_iterator_compare_x());
+ StringCharacterStream stream_y(y, state->string_iterator_compare_y());
+ while (stream_x.HasMore() && stream_y.HasMore()) {
+ int d = stream_x.GetNext() - stream_y.GetNext();
+ if (d < 0) return Smi::FromInt(LESS);
+ else if (d > 0) return Smi::FromInt(GREATER);
+ }
+
+ // x is (non-trivial) prefix of y:
+ if (stream_y.HasMore()) return Smi::FromInt(LESS);
+ // y is prefix of x:
+ return Smi::FromInt(stream_x.HasMore() ? GREATER : EQUAL);
+}
+
+
+static Object* FlatStringCompare(String* x, String* y) {
+ ASSERT(x->IsFlat());
+ ASSERT(y->IsFlat());
+ Object* equal_prefix_result = Smi::FromInt(EQUAL);
+ int prefix_length = x->length();
+ if (y->length() < prefix_length) {
+ prefix_length = y->length();
+ equal_prefix_result = Smi::FromInt(GREATER);
+ } else if (y->length() > prefix_length) {
+ equal_prefix_result = Smi::FromInt(LESS);
+ }
+ int r;
+ DisallowHeapAllocation no_gc;
+ String::FlatContent x_content = x->GetFlatContent();
+ String::FlatContent y_content = y->GetFlatContent();
+ if (x_content.IsAscii()) {
+ Vector<const uint8_t> x_chars = x_content.ToOneByteVector();
+ if (y_content.IsAscii()) {
+ Vector<const uint8_t> y_chars = y_content.ToOneByteVector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ } else {
+ Vector<const uc16> y_chars = y_content.ToUC16Vector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ }
+ } else {
+ Vector<const uc16> x_chars = x_content.ToUC16Vector();
+ if (y_content.IsAscii()) {
+ Vector<const uint8_t> y_chars = y_content.ToOneByteVector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ } else {
+ Vector<const uc16> y_chars = y_content.ToUC16Vector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ }
+ }
+ Object* result;
+ if (r == 0) {
+ result = equal_prefix_result;
+ } else {
+ result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
+ }
+ ASSERT(result ==
+ StringCharacterStreamCompare(Isolate::Current()->runtime_state(), x, y));
+ return result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StringCompare) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(String, x, 0);
+ CONVERT_ARG_CHECKED(String, y, 1);
+
+ isolate->counters()->string_compare_runtime()->Increment();
+
+ // A few fast case tests before we flatten.
+ if (x == y) return Smi::FromInt(EQUAL);
+ if (y->length() == 0) {
+ if (x->length() == 0) return Smi::FromInt(EQUAL);
+ return Smi::FromInt(GREATER);
+ } else if (x->length() == 0) {
+ return Smi::FromInt(LESS);
+ }
+
+ int d = x->Get(0) - y->Get(0);
+ if (d < 0) return Smi::FromInt(LESS);
+ else if (d > 0) return Smi::FromInt(GREATER);
+
+ Object* obj;
+ { MaybeObject* maybe_obj = isolate->heap()->PrepareForCompare(x);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ { MaybeObject* maybe_obj = isolate->heap()->PrepareForCompare(y);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
+ : StringCharacterStreamCompare(isolate->runtime_state(), x, y);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_acos) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_acos()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->transcendental_cache()->Get(TranscendentalCache::ACOS, x);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_asin) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_asin()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->transcendental_cache()->Get(TranscendentalCache::ASIN, x);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_atan) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_atan()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->transcendental_cache()->Get(TranscendentalCache::ATAN, x);
+}
+
+
+static const double kPiDividedBy4 = 0.78539816339744830962;
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_atan2) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ isolate->counters()->math_atan2()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ double result;
+ if (std::isinf(x) && std::isinf(y)) {
+ // Make sure that the result in case of two infinite arguments
+ // is a multiple of Pi / 4. The sign of the result is determined
+ // by the first argument (x) and the sign of the second argument
+ // determines the multiplier: one or three.
+ int multiplier = (x < 0) ? -1 : 1;
+ if (y < 0) multiplier *= 3;
+ result = multiplier * kPiDividedBy4;
+ } else {
+ result = atan2(x, y);
+ }
+ return isolate->heap()->AllocateHeapNumber(result);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_ceil) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_ceil()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->heap()->NumberFromDouble(ceiling(x));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_cos) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_cos()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->transcendental_cache()->Get(TranscendentalCache::COS, x);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_exp) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_exp()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ lazily_initialize_fast_exp();
+ return isolate->heap()->NumberFromDouble(fast_exp(x));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_floor) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_floor()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->heap()->NumberFromDouble(floor(x));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_log) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_log()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ 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) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ isolate->counters()->math_pow()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+
+ // If the second argument is a smi, it is much faster to call the
+ // custom powi() function than the generic pow().
+ if (args[1]->IsSmi()) {
+ int y = args.smi_at(1);
+ return isolate->heap()->NumberFromDouble(power_double_int(x, y));
+ }
+
+ CONVERT_DOUBLE_ARG_CHECKED(y, 1);
+ double result = power_helper(x, y);
+ if (std::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 slow case from full codegen.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_pow_cfunction) {
+ SealHandleScope shs(isolate);
+ 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 {
+ double result = power_double_double(x, y);
+ if (std::isnan(result)) return isolate->heap()->nan_value();
+ return isolate->heap()->AllocateHeapNumber(result);
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RoundNumber) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_round()->Increment();
+
+ if (!args[0]->IsHeapNumber()) {
+ // Must be smi. Return the argument unchanged for all the other types
+ // to make fuzz-natives test happy.
+ return args[0];
+ }
+
+ HeapNumber* number = reinterpret_cast<HeapNumber*>(args[0]);
+
+ double value = number->value();
+ int exponent = number->get_exponent();
+ int sign = number->get_sign();
+
+ if (exponent < -1) {
+ // Number in range ]-0.5..0.5[. These always round to +/-zero.
+ if (sign) return isolate->heap()->minus_zero_value();
+ return Smi::FromInt(0);
+ }
+
+ // 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
+ // argument holds for 32-bit smis).
+ if (!sign && exponent < kSmiValueSize - 2) {
+ return Smi::FromInt(static_cast<int>(value + 0.5));
+ }
+
+ // If the magnitude is big enough, there's no place for fraction part. If we
+ // try to add 0.5 to this number, 1.0 will be added instead.
+ if (exponent >= 52) {
+ return number;
+ }
+
+ if (sign && value >= -0.5) return isolate->heap()->minus_zero_value();
+
+ // Do not call NumberFromDouble() to avoid extra checks.
+ return isolate->heap()->AllocateHeapNumber(floor(value + 0.5));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_sin) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_sin()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->transcendental_cache()->Get(TranscendentalCache::SIN, x);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_sqrt) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_sqrt()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->heap()->AllocateHeapNumber(fast_sqrt(x));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Math_tan) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ isolate->counters()->math_tan()->Increment();
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ return isolate->transcendental_cache()->Get(TranscendentalCache::TAN, x);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_SMI_ARG_CHECKED(year, 0);
+ CONVERT_SMI_ARG_CHECKED(month, 1);
+
+ return Smi::FromInt(isolate->date_cache()->DaysFromYearMonth(year, month));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateSetValue) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 0);
+ CONVERT_DOUBLE_ARG_CHECKED(time, 1);
+ CONVERT_SMI_ARG_CHECKED(is_utc, 2);
+
+ DateCache* date_cache = isolate->date_cache();
+
+ Object* value = NULL;
+ bool is_value_nan = false;
+ if (std::isnan(time)) {
+ value = isolate->heap()->nan_value();
+ is_value_nan = true;
+ } else if (!is_utc &&
+ (time < -DateCache::kMaxTimeBeforeUTCInMs ||
+ time > DateCache::kMaxTimeBeforeUTCInMs)) {
+ value = isolate->heap()->nan_value();
+ is_value_nan = true;
+ } else {
+ time = is_utc ? time : date_cache->ToUTC(static_cast<int64_t>(time));
+ if (time < -DateCache::kMaxTimeInMs ||
+ time > DateCache::kMaxTimeInMs) {
+ value = isolate->heap()->nan_value();
+ is_value_nan = true;
+ } else {
+ MaybeObject* maybe_result =
+ isolate->heap()->AllocateHeapNumber(DoubleToInteger(time));
+ if (!maybe_result->ToObject(&value)) return maybe_result;
+ }
+ }
+ date->SetValue(value, is_value_nan);
+ return value;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewArgumentsFast) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+
+ Handle<JSFunction> callee = args.at<JSFunction>(0);
+ Object** parameters = reinterpret_cast<Object**>(args[1]);
+ const int argument_count = Smi::cast(args[2])->value();
+
+ Handle<JSObject> result =
+ isolate->factory()->NewArgumentsObject(callee, argument_count);
+ // Allocate the elements if needed.
+ int parameter_count = callee->shared()->formal_parameter_count();
+ if (argument_count > 0) {
+ if (parameter_count > 0) {
+ int mapped_count = Min(argument_count, parameter_count);
+ Handle<FixedArray> parameter_map =
+ isolate->factory()->NewFixedArray(mapped_count + 2, NOT_TENURED);
+ parameter_map->set_map(
+ isolate->heap()->non_strict_arguments_elements_map());
+
+ Handle<Map> old_map(result->map());
+ Handle<Map> new_map = isolate->factory()->CopyMap(old_map);
+ new_map->set_elements_kind(NON_STRICT_ARGUMENTS_ELEMENTS);
+
+ result->set_map(*new_map);
+ result->set_elements(*parameter_map);
+
+ // Store the context and the arguments array at the beginning of the
+ // parameter map.
+ Handle<Context> context(isolate->context());
+ Handle<FixedArray> arguments =
+ isolate->factory()->NewFixedArray(argument_count, NOT_TENURED);
+ parameter_map->set(0, *context);
+ parameter_map->set(1, *arguments);
+
+ // Loop over the actual parameters backwards.
+ int index = argument_count - 1;
+ while (index >= mapped_count) {
+ // These go directly in the arguments array and have no
+ // corresponding slot in the parameter map.
+ arguments->set(index, *(parameters - index - 1));
+ --index;
+ }
+
+ 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->ParameterName(index));
+ int context_local_count = scope_info->ContextLocalCount();
+ bool duplicate = false;
+ for (int j = index + 1; j < parameter_count; ++j) {
+ if (scope_info->ParameterName(j) == *name) {
+ duplicate = true;
+ break;
+ }
+ }
+
+ if (duplicate) {
+ // This goes directly in the arguments array with a hole in the
+ // parameter map.
+ arguments->set(index, *(parameters - index - 1));
+ parameter_map->set_the_hole(index + 2);
+ } else {
+ // The context index goes in the parameter map with a hole in the
+ // arguments array.
+ int context_index = -1;
+ 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::MIN_CONTEXT_SLOTS + context_index));
+ }
+
+ --index;
+ }
+ } else {
+ // If there is no aliasing, the arguments object elements are not
+ // special in any way.
+ Handle<FixedArray> elements =
+ isolate->factory()->NewFixedArray(argument_count, NOT_TENURED);
+ result->set_elements(*elements);
+ for (int i = 0; i < argument_count; ++i) {
+ elements->set(i, *(parameters - i - 1));
+ }
+ }
+ }
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewStrictArgumentsFast) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+
+ JSFunction* callee = JSFunction::cast(args[0]);
+ Object** parameters = reinterpret_cast<Object**>(args[1]);
+ const int length = args.smi_at(2);
+
+ Object* result;
+ { MaybeObject* maybe_result =
+ isolate->heap()->AllocateArgumentsObject(callee, length);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ // Allocate the elements if needed.
+ if (length > 0) {
+ // Allocate the fixed array.
+ Object* obj;
+ { MaybeObject* maybe_obj = isolate->heap()->AllocateRawFixedArray(length);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+
+ DisallowHeapAllocation no_gc;
+ FixedArray* array = reinterpret_cast<FixedArray*>(obj);
+ array->set_map_no_write_barrier(isolate->heap()->fixed_array_map());
+ array->set_length(length);
+
+ WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
+ for (int i = 0; i < length; i++) {
+ array->set(i, *--parameters, mode);
+ }
+ JSObject::cast(result)->set_elements(FixedArray::cast(obj));
+ }
+ return result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewClosure) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(Context, context, 0);
+ CONVERT_ARG_HANDLE_CHECKED(SharedFunctionInfo, shared, 1);
+ CONVERT_BOOLEAN_ARG_CHECKED(pretenure, 2);
+
+ // The caller ensures that we pretenure closures that are assigned
+ // directly to properties.
+ PretenureFlag pretenure_flag = pretenure ? TENURED : NOT_TENURED;
+ Handle<JSFunction> result =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(shared,
+ context,
+ pretenure_flag);
+ return *result;
+}
+
+
+// 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(
+ Isolate* isolate,
+ int prefix_argc,
+ int* total_argc) {
+ // Find frame containing arguments passed to the caller.
+ JavaScriptFrameIterator it(isolate);
+ JavaScriptFrame* frame = it.frame();
+ List<JSFunction*> functions(2);
+ frame->GetFunctions(&functions);
+ if (functions.length() > 1) {
+ 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(isolate);
+ 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 = 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), isolate);
+ param_data[prefix_argc + i] = val;
+ }
+ return param_data;
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionBindArguments) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_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(isolate, 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),
+ isolate);
+ 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_string = isolate->factory()->length_string();
+ Handle<Object> new_length(args.at<Object>(3));
+ PropertyAttributes attr =
+ static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY);
+ ForceSetProperty(bound_function, length_string, new_length, attr);
+ return *bound_function;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_BoundFunctionGetBindings) {
+ HandleScope handles(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_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() == 1);
+ // First argument is a function to use as a constructor.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ 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)),
+ isolate);
+ ASSERT(!bound_function->IsJSFunction() ||
+ !Handle<JSFunction>::cast(bound_function)->shared()->bound());
+
+ int total_argc = 0;
+ SmartArrayPointer<Handle<Object> > param_data =
+ GetCallerArguments(isolate, bound_argc, &total_argc);
+ for (int i = 0; i < bound_argc; i++) {
+ param_data[i] = Handle<Object>(bound_args->get(
+ JSFunction::kBoundArgumentsStartIndex + i), isolate);
+ }
+
+ 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(Handle<JSFunction>::cast(bound_function),
+ total_argc, *param_data, &exception);
+ if (exception) {
+ return Failure::Exception();
+ }
+ ASSERT(!result.is_null());
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObject) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ Handle<Object> constructor = args.at<Object>(0);
+
+ // If the constructor isn't a proper function we throw a type error.
+ if (!constructor->IsJSFunction()) {
+ Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
+ Handle<Object> type_error =
+ isolate->factory()->NewTypeError("not_constructor", arguments);
+ return isolate->Throw(*type_error);
+ }
+
+ Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
+
+ // If function should not have prototype, construction is not allowed. In this
+ // case generated code bailouts here, since function has no initial_map.
+ if (!function->should_have_prototype() && !function->shared()->bound()) {
+ Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
+ Handle<Object> type_error =
+ isolate->factory()->NewTypeError("not_constructor", arguments);
+ return isolate->Throw(*type_error);
+ }
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Debug* debug = isolate->debug();
+ // Handle stepping into constructors if step into is active.
+ if (debug->StepInActive()) {
+ debug->HandleStepIn(function, Handle<Object>::null(), 0, true);
+ }
+#endif
+
+ if (function->has_initial_map()) {
+ if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
+ // The 'Function' function ignores the receiver object when
+ // called using 'new' and creates a new JSFunction object that
+ // is returned. The receiver object is only used for error
+ // reporting if an error occurs when constructing the new
+ // JSFunction. Factory::NewJSObject() should not be used to
+ // allocate JSFunctions since it does not properly initialize
+ // the shared part of the function. Since the receiver is
+ // ignored anyway, we use the global object as the receiver
+ // instead of a new JSFunction object. This way, errors are
+ // reported the same way whether or not 'Function' is called
+ // using 'new'.
+ return isolate->context()->global_object();
+ }
+ }
+
+ // The function should be compiled for the optimization hints to be
+ // available.
+ JSFunction::EnsureCompiled(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
+ // track one initial_map at a time, so we force the completion before the
+ // function is called as a constructor for the first time.
+ shared->CompleteInobjectSlackTracking();
+ }
+
+ Handle<JSObject> result = isolate->factory()->NewJSObject(function);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+
+ isolate->counters()->constructed_objects()->Increment();
+ isolate->counters()->constructed_objects_runtime()->Increment();
+
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FinalizeInstanceSize) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ function->shared()->CompleteInobjectSlackTracking();
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyCompile) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ Handle<JSFunction> function = args.at<JSFunction>(0);
+#ifdef DEBUG
+ if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
+ PrintF("[lazy: ");
+ function->PrintName();
+ PrintF("]\n");
+ }
+#endif
+
+ // Compile the target function.
+ ASSERT(!function->is_compiled());
+ if (!JSFunction::CompileLazy(function, KEEP_EXCEPTION)) {
+ return Failure::Exception();
+ }
+
+ // All done. Return the compiled code.
+ ASSERT(function->is_compiled());
+ return function->code();
+}
+
+
+bool AllowOptimization(Isolate* isolate, Handle<JSFunction> function) {
+ // 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.
+ if (!function->shared()->is_compiled()) return false;
+
+ // If the function is not optimizable or debugger is active continue using the
+ // code from the full compiler.
+ if (!FLAG_crankshaft ||
+ function->shared()->optimization_disabled() ||
+ isolate->DebuggerHasBreakPoints()) {
+ if (FLAG_trace_opt) {
+ PrintF("[failed to optimize ");
+ function->PrintName();
+ PrintF(": is code optimizable: %s, is debugger enabled: %s]\n",
+ function->shared()->optimization_disabled() ? "F" : "T",
+ isolate->DebuggerHasBreakPoints() ? "T" : "F");
+ }
+ return false;
+ }
+ return true;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ Handle<JSFunction> function = args.at<JSFunction>(0);
+
+ if (!AllowOptimization(isolate, function)) {
+ function->ReplaceCode(function->shared()->code());
+ return function->code();
+ }
+ function->shared()->code()->set_profiler_ticks(0);
+ if (JSFunction::CompileOptimized(function,
+ BailoutId::None(),
+ CLEAR_EXCEPTION)) {
+ return function->code();
+ }
+ if (FLAG_trace_opt) {
+ PrintF("[failed to optimize ");
+ function->PrintName();
+ PrintF(": optimized compilation failed]\n");
+ }
+ function->ReplaceCode(function->shared()->code());
+ return function->code();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ParallelRecompile) {
+ HandleScope handle_scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ if (!AllowOptimization(isolate, function)) {
+ function->ReplaceCode(function->shared()->code());
+ return isolate->heap()->undefined_value();
+ }
+ function->shared()->code()->set_profiler_ticks(0);
+ ASSERT(FLAG_parallel_recompilation);
+ Compiler::RecompileParallel(function);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InstallRecompiledCode) {
+ HandleScope handle_scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ ASSERT(V8::UseCrankshaft() && FLAG_parallel_recompilation);
+ isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
+ return function->code();
+}
+
+
+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_NotifyStubFailure) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 0);
+ Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
+ ASSERT(AllowHeapAllocation::IsAllowed());
+ delete deoptimizer;
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ RUNTIME_ASSERT(args[0]->IsSmi());
+ Deoptimizer::BailoutType type =
+ static_cast<Deoptimizer::BailoutType>(args.smi_at(0));
+ Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
+ ASSERT(AllowHeapAllocation::IsAllowed());
+
+ ASSERT(deoptimizer->compiled_code_kind() == Code::OPTIMIZED_FUNCTION);
+
+ // Make sure to materialize objects before causing any allocation.
+ JavaScriptFrameIterator it(isolate);
+ deoptimizer->MaterializeHeapObjects(&it);
+ delete deoptimizer;
+
+ JavaScriptFrame* frame = it.frame();
+ RUNTIME_ASSERT(frame->function()->IsJSFunction());
+ Handle<JSFunction> function(frame->function(), isolate);
+ Handle<Code> optimized_code(function->code());
+ RUNTIME_ASSERT((type != Deoptimizer::EAGER &&
+ type != Deoptimizer::SOFT) || function->IsOptimized());
+
+ // Avoid doing too much work when running with --always-opt and keep
+ // the optimized code around.
+ if (FLAG_always_opt || type == Deoptimizer::LAZY) {
+ return isolate->heap()->undefined_value();
+ }
+
+ // Find other optimized activations of the function or functions that
+ // share the same optimized code.
+ bool has_other_activations = false;
+ while (!it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ JSFunction* other_function = frame->function();
+ if (frame->is_optimized() && other_function->code() == function->code()) {
+ has_other_activations = true;
+ break;
+ }
+ it.Advance();
+ }
+
+ 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();
+ PrintF("]\n");
+ }
+ function->ReplaceCode(function->shared()->code());
+ } else {
+ Deoptimizer::DeoptimizeFunction(*function);
+ }
+ // Evict optimized code for this function from the cache so that it doesn't
+ // get used for new closures.
+ function->shared()->EvictFromOptimizedCodeMap(*optimized_code,
+ "notify deoptimized");
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyOSR) {
+ SealHandleScope shs(isolate);
+ Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
+ delete deoptimizer;
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeoptimizeFunction) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ if (!function->IsOptimized()) return isolate->heap()->undefined_value();
+
+ Deoptimizer::DeoptimizeFunction(*function);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearFunctionTypeFeedback) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ Code* unoptimized = function->shared()->code();
+ if (unoptimized->kind() == Code::FUNCTION) {
+ unoptimized->ClearInlineCaches();
+ unoptimized->ClearTypeFeedbackCells(isolate->heap());
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RunningInSimulator) {
+ SealHandleScope shs(isolate);
+#if defined(USE_SIMULATOR)
+ return isolate->heap()->true_value();
+#else
+ return isolate->heap()->false_value();
+#endif
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsParallelRecompilationSupported) {
+ HandleScope scope(isolate);
+ return FLAG_parallel_recompilation
+ ? isolate->heap()->true_value() : isolate->heap()->false_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) {
+ HandleScope scope(isolate);
+ RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+
+ if (!function->IsOptimizable()) return isolate->heap()->undefined_value();
+ function->MarkForLazyRecompilation();
+
+ Code* unoptimized = function->shared()->code();
+ if (args.length() == 2 &&
+ unoptimized->kind() == Code::FUNCTION) {
+ CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
+ if (type->IsOneByteEqualTo(STATIC_ASCII_VECTOR("osr"))) {
+ for (int i = 0; i <= Code::kMaxLoopNestingMarker; i++) {
+ unoptimized->set_allow_osr_at_loop_nesting_level(i);
+ isolate->runtime_profiler()->AttemptOnStackReplacement(*function);
+ }
+ } else if (type->IsOneByteEqualTo(STATIC_ASCII_VECTOR("parallel"))) {
+ function->MarkForParallelRecompilation();
+ }
+ }
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NeverOptimizeFunction) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+ ASSERT(!function->IsOptimized());
+ function->shared()->set_optimization_disabled(true);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationStatus) {
+ HandleScope scope(isolate);
+ RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
+ if (!V8::UseCrankshaft()) {
+ return Smi::FromInt(4); // 4 == "never".
+ }
+ bool sync_with_compiler_thread = true;
+ if (args.length() == 2) {
+ CONVERT_ARG_HANDLE_CHECKED(String, sync, 1);
+ if (sync->IsOneByteEqualTo(STATIC_ASCII_VECTOR("no sync"))) {
+ sync_with_compiler_thread = false;
+ }
+ }
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ if (FLAG_parallel_recompilation && sync_with_compiler_thread) {
+ while (function->IsInRecompileQueue() ||
+ function->IsMarkedForInstallingRecompiledCode()) {
+ isolate->optimizing_compiler_thread()->InstallOptimizedFunctions();
+ OS::Sleep(50);
+ }
+ }
+ if (FLAG_always_opt) {
+ // We may have always opt, but that is more best-effort than a real
+ // promise, so we still say "no" if it is not optimized.
+ return function->IsOptimized() ? Smi::FromInt(3) // 3 == "always".
+ : Smi::FromInt(2); // 2 == "no".
+ }
+ if (FLAG_deopt_every_n_times) {
+ return Smi::FromInt(6); // 6 == "maybe deopted".
+ }
+ return function->IsOptimized() ? Smi::FromInt(1) // 1 == "yes".
+ : Smi::FromInt(2); // 2 == "no".
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationCount) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ return Smi::FromInt(function->shared()->opt_count());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileForOnStackReplacement) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+
+ // We're not prepared to handle a function with arguments object.
+ ASSERT(!function->shared()->uses_arguments());
+
+ // We have hit a back edge in an unoptimized frame for a function that was
+ // selected for on-stack replacement. Find the unoptimized code object.
+ Handle<Code> unoptimized(function->shared()->code(), isolate);
+ // Keep track of whether we've succeeded in optimizing.
+ bool succeeded = unoptimized->optimizable();
+ if (succeeded) {
+ // If we are trying to do OSR when there are already optimized
+ // activations of the function, it means (a) the function is directly or
+ // indirectly recursive and (b) an optimized invocation has been
+ // deoptimized so that we are currently in an unoptimized activation.
+ // Check for optimized activations of this function.
+ JavaScriptFrameIterator it(isolate);
+ while (succeeded && !it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ succeeded = !frame->is_optimized() || frame->function() != *function;
+ it.Advance();
+ }
+ }
+
+ BailoutId ast_id = BailoutId::None();
+ if (succeeded) {
+ // The top JS function is this one, the PC is somewhere in the
+ // unoptimized code.
+ JavaScriptFrameIterator it(isolate);
+ JavaScriptFrame* frame = it.frame();
+ ASSERT(frame->function() == *function);
+ ASSERT(frame->LookupCode() == *unoptimized);
+ ASSERT(unoptimized->contains(frame->pc()));
+
+ // Use linear search of the unoptimized code's back edge table to find
+ // the AST id matching the PC.
+ uint32_t target_pc_offset =
+ static_cast<uint32_t>(frame->pc() - unoptimized->instruction_start());
+ uint32_t loop_depth = 0;
+
+ for (FullCodeGenerator::BackEdgeTableIterator back_edges(*unoptimized);
+ !back_edges.Done();
+ back_edges.Next()) {
+ if (back_edges.pc_offset() == target_pc_offset) {
+ ast_id = back_edges.ast_id();
+ loop_depth = back_edges.loop_depth();
+ break;
+ }
+ }
+ ASSERT(!ast_id.IsNone());
+
+ if (FLAG_trace_osr) {
+ PrintF("[replacing on-stack at AST id %d, loop depth %d in ",
+ ast_id.ToInt(), loop_depth);
+ function->PrintName();
+ PrintF("]\n");
+ }
+
+ // Try to compile the optimized code. A true return value from
+ // CompileOptimized means that compilation succeeded, not necessarily
+ // that optimization succeeded.
+ if (JSFunction::CompileOptimized(function, ast_id, CLEAR_EXCEPTION) &&
+ function->IsOptimized()) {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ function->code()->deoptimization_data());
+ if (data->OsrPcOffset()->value() >= 0) {
+ if (FLAG_trace_osr) {
+ PrintF("[on-stack replacement offset %d in optimized code]\n",
+ data->OsrPcOffset()->value());
+ }
+ ASSERT(BailoutId(data->OsrAstId()->value()) == ast_id);
+ } else {
+ // We may never generate the desired OSR entry if we emit an
+ // early deoptimize.
+ succeeded = false;
+ }
+ } else {
+ succeeded = false;
+ }
+ }
+
+ // Revert to the original interrupt calls in the original unoptimized code.
+ if (FLAG_trace_osr) {
+ PrintF("[restoring original interrupt calls in ");
+ function->PrintName();
+ PrintF("]\n");
+ }
+ InterruptStub interrupt_stub;
+ Handle<Code> interrupt_code = interrupt_stub.GetCode(isolate);
+ Handle<Code> replacement_code = isolate->builtins()->OnStackReplacement();
+ Deoptimizer::RevertInterruptCode(*unoptimized,
+ *interrupt_code,
+ *replacement_code);
+
+ // If the optimization attempt succeeded, return the AST id tagged as a
+ // smi. This tells the builtin that we need to translate the unoptimized
+ // frame to an optimized one.
+ if (succeeded) {
+ ASSERT(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
+ return Smi::FromInt(ast_id.ToInt());
+ } else {
+ if (function->IsMarkedForLazyRecompilation()) {
+ function->ReplaceCode(function->shared()->code());
+ }
+ return Smi::FromInt(-1);
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CheckIsBootstrapping) {
+ SealHandleScope shs(isolate);
+ RUNTIME_ASSERT(isolate->bootstrapper()->IsActive());
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetRootNaN) {
+ SealHandleScope shs(isolate);
+ RUNTIME_ASSERT(isolate->bootstrapper()->IsActive());
+ return isolate->heap()->nan_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Call) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() >= 2);
+ int argc = args.length() - 2;
+ CONVERT_ARG_CHECKED(JSReceiver, fun, argc + 1);
+ Object* receiver = args[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) {
+ MaybeObject* maybe = args[1 + i];
+ Object* object;
+ if (!maybe->To<Object>(&object)) return maybe;
+ argv[i] = Handle<Object>(object, isolate);
+ }
+
+ bool threw;
+ Handle<JSReceiver> hfun(fun);
+ Handle<Object> hreceiver(receiver, isolate);
+ 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_HANDLE_CHECKED(JSReceiver, fun, 0);
+ Handle<Object> receiver = args.at<Object>(1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, arguments, 2);
+ CONVERT_SMI_ARG_CHECKED(offset, 3);
+ CONVERT_SMI_ARG_CHECKED(argc, 4);
+ RUNTIME_ASSERT(offset >= 0);
+ RUNTIME_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;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFunctionDelegate) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ RUNTIME_ASSERT(!args[0]->IsJSFunction());
+ return *Execution::GetFunctionDelegate(args.at<Object>(0));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetConstructorDelegate) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ RUNTIME_ASSERT(!args[0]->IsJSFunction());
+ return *Execution::GetConstructorDelegate(args.at<Object>(0));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewGlobalContext) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+ CONVERT_ARG_CHECKED(ScopeInfo, scope_info, 1);
+ Context* result;
+ MaybeObject* maybe_result =
+ isolate->heap()->AllocateGlobalContext(function, scope_info);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ ASSERT(function->context() == isolate->context());
+ ASSERT(function->context()->global_object() == result->global_object());
+ isolate->set_context(result);
+ result->global_object()->set_global_context(result);
+
+ return result; // non-failure
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_NewFunctionContext) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+ int length = function->shared()->scope_info()->ContextLength();
+ Context* result;
+ MaybeObject* maybe_result =
+ isolate->heap()->AllocateFunctionContext(length, function);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ isolate->set_context(result);
+
+ return result; // non-failure
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushWithContext) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ JSReceiver* extension_object;
+ if (args[0]->IsJSReceiver()) {
+ extension_object = JSReceiver::cast(args[0]);
+ } else {
+ // Convert the object to a proper JavaScript object.
+ MaybeObject* maybe_js_object = args[0]->ToObject();
+ if (!maybe_js_object->To(&extension_object)) {
+ if (Failure::cast(maybe_js_object)->IsInternalError()) {
+ HandleScope scope(isolate);
+ Handle<Object> handle = args.at<Object>(0);
+ Handle<Object> result =
+ isolate->factory()->NewTypeError("with_expression",
+ HandleVector(&handle, 1));
+ return isolate->Throw(*result);
+ } else {
+ return maybe_js_object;
+ }
+ }
+ }
+
+ JSFunction* function;
+ if (args[1]->IsSmi()) {
+ // A smi sentinel indicates a context nested inside global code rather
+ // than some function. There is a canonical empty function that can be
+ // gotten from the native context.
+ function = isolate->context()->native_context()->closure();
+ } else {
+ function = JSFunction::cast(args[1]);
+ }
+
+ Context* context;
+ MaybeObject* maybe_context =
+ isolate->heap()->AllocateWithContext(function,
+ isolate->context(),
+ extension_object);
+ if (!maybe_context->To(&context)) return maybe_context;
+ isolate->set_context(context);
+ return context;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushCatchContext) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ String* name = String::cast(args[0]);
+ Object* thrown_object = args[1];
+ JSFunction* function;
+ if (args[2]->IsSmi()) {
+ // A smi sentinel indicates a context nested inside global code rather
+ // than some function. There is a canonical empty function that can be
+ // gotten from the native context.
+ function = isolate->context()->native_context()->closure();
+ } else {
+ function = JSFunction::cast(args[2]);
+ }
+ Context* context;
+ MaybeObject* maybe_context =
+ isolate->heap()->AllocateCatchContext(function,
+ isolate->context(),
+ name,
+ thrown_object);
+ if (!maybe_context->To(&context)) return maybe_context;
+ isolate->set_context(context);
+ return context;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushBlockContext) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ ScopeInfo* scope_info = ScopeInfo::cast(args[0]);
+ JSFunction* function;
+ if (args[1]->IsSmi()) {
+ // A smi sentinel indicates a context nested inside global code rather
+ // than some function. There is a canonical empty function that can be
+ // gotten from the native context.
+ function = isolate->context()->native_context()->closure();
+ } else {
+ function = JSFunction::cast(args[1]);
+ }
+ Context* context;
+ MaybeObject* maybe_context =
+ isolate->heap()->AllocateBlockContext(function,
+ isolate->context(),
+ scope_info);
+ if (!maybe_context->To(&context)) return maybe_context;
+ isolate->set_context(context);
+ return context;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSModule) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ Object* obj = args[0];
+ return isolate->heap()->ToBoolean(obj->IsJSModule());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushModuleContext) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_SMI_ARG_CHECKED(index, 0);
+
+ if (!args[1]->IsScopeInfo()) {
+ // Module already initialized. Find hosting context and retrieve context.
+ Context* host = Context::cast(isolate->context())->global_context();
+ Context* context = Context::cast(host->get(index));
+ ASSERT(context->previous() == isolate->context());
+ isolate->set_context(context);
+ return context;
+ }
+
+ CONVERT_ARG_HANDLE_CHECKED(ScopeInfo, scope_info, 1);
+
+ // Allocate module context.
+ HandleScope scope(isolate);
+ Factory* factory = isolate->factory();
+ Handle<Context> context = factory->NewModuleContext(scope_info);
+ Handle<JSModule> module = factory->NewJSModule(context, scope_info);
+ context->set_module(*module);
+ Context* previous = isolate->context();
+ context->set_previous(previous);
+ context->set_closure(previous->closure());
+ context->set_global_object(previous->global_object());
+ isolate->set_context(*context);
+
+ // Find hosting scope and initialize internal variable holding module there.
+ previous->global_context()->set(index, *context);
+
+ return *context;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareModules) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(FixedArray, descriptions, 0);
+ Context* host_context = isolate->context();
+
+ for (int i = 0; i < descriptions->length(); ++i) {
+ Handle<ModuleInfo> description(ModuleInfo::cast(descriptions->get(i)));
+ int host_index = description->host_index();
+ Handle<Context> context(Context::cast(host_context->get(host_index)));
+ Handle<JSModule> module(context->module());
+
+ for (int j = 0; j < description->length(); ++j) {
+ Handle<String> name(description->name(j));
+ VariableMode mode = description->mode(j);
+ int index = description->index(j);
+ switch (mode) {
+ case VAR:
+ case LET:
+ case CONST:
+ case CONST_HARMONY: {
+ PropertyAttributes attr =
+ IsImmutableVariableMode(mode) ? FROZEN : SEALED;
+ Handle<AccessorInfo> info =
+ Accessors::MakeModuleExport(name, index, attr);
+ Handle<Object> result = SetAccessor(module, info);
+ ASSERT(!(result.is_null() || result->IsUndefined()));
+ USE(result);
+ break;
+ }
+ case MODULE: {
+ Object* referenced_context = Context::cast(host_context)->get(index);
+ Handle<JSModule> value(Context::cast(referenced_context)->module());
+ JSReceiver::SetProperty(module, name, value, FROZEN, kStrictMode);
+ break;
+ }
+ case INTERNAL:
+ case TEMPORARY:
+ case DYNAMIC:
+ case DYNAMIC_GLOBAL:
+ case DYNAMIC_LOCAL:
+ UNREACHABLE();
+ }
+ }
+
+ JSObject::PreventExtensions(module);
+ }
+
+ ASSERT(!isolate->has_pending_exception());
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteContextSlot) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(Context, context, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
+
+ int index;
+ PropertyAttributes attributes;
+ ContextLookupFlags flags = FOLLOW_CHAINS;
+ BindingFlags binding_flags;
+ Handle<Object> holder = context->Lookup(name,
+ flags,
+ &index,
+ &attributes,
+ &binding_flags);
+
+ // If the slot was not found the result is true.
+ if (holder.is_null()) {
+ return isolate->heap()->true_value();
+ }
+
+ // If the slot was found in a context, it should be DONT_DELETE.
+ if (holder->IsContext()) {
+ return isolate->heap()->false_value();
+ }
+
+ // The slot was found in a JSObject, either a context extension object,
+ // the global object, or the subject of a with. Try to delete it
+ // (respecting DONT_DELETE).
+ Handle<JSObject> object = Handle<JSObject>::cast(holder);
+ Handle<Object> result = JSReceiver::DeleteProperty(object, name);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
+ return *result;
+}
+
+
+// A mechanism to return a pair of Object pointers in registers (if possible).
+// How this is achieved is calling convention-dependent.
+// All currently supported x86 compiles uses calling conventions that are cdecl
+// variants where a 64-bit value is returned in two 32-bit registers
+// (edx:eax on ia32, r1:r0 on ARM).
+// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
+// In Win64 calling convention, a struct of two pointers is returned in memory,
+// allocated by the caller, and passed as a pointer in a hidden first parameter.
+#ifdef V8_HOST_ARCH_64_BIT
+struct ObjectPair {
+ MaybeObject* x;
+ MaybeObject* y;
+};
+
+
+static inline ObjectPair MakePair(MaybeObject* x, MaybeObject* y) {
+ ObjectPair result = {x, y};
+ // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
+ // In Win64 they are assigned to a hidden first argument.
+ return result;
+}
+#else
+typedef uint64_t ObjectPair;
+static inline ObjectPair MakePair(MaybeObject* x, MaybeObject* y) {
+ return reinterpret_cast<uint32_t>(x) |
+ (reinterpret_cast<ObjectPair>(y) << 32);
+}
+#endif
+
+
+static inline MaybeObject* Unhole(Heap* heap,
+ MaybeObject* x,
+ PropertyAttributes attributes) {
+ ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
+ USE(attributes);
+ return x->IsTheHole() ? heap->undefined_value() : x;
+}
+
+
+static Object* ComputeReceiverForNonGlobal(Isolate* isolate,
+ JSObject* holder) {
+ ASSERT(!holder->IsGlobalObject());
+ Context* top = isolate->context();
+ // Get the context extension function.
+ JSFunction* context_extension_function =
+ top->native_context()->context_extension_function();
+ // If the holder isn't a context extension object, we just return it
+ // as the receiver. This allows arguments objects to be used as
+ // receivers, but only if they are put in the context scope chain
+ // explicitly via a with-statement.
+ Object* constructor = holder->map()->constructor();
+ if (constructor != context_extension_function) return holder;
+ // Fall back to using the global object as the implicit receiver if
+ // the property turns out to be a local variable allocated in a
+ // context extension object - introduced via eval. Implicit global
+ // receivers are indicated with the hole value.
+ return isolate->heap()->the_hole_value();
+}
+
+
+static ObjectPair LoadContextSlotHelper(Arguments args,
+ Isolate* isolate,
+ bool throw_error) {
+ HandleScope scope(isolate);
+ ASSERT_EQ(2, args.length());
+
+ if (!args[0]->IsContext() || !args[1]->IsString()) {
+ return MakePair(isolate->ThrowIllegalOperation(), NULL);
+ }
+ Handle<Context> context = args.at<Context>(0);
+ Handle<String> name = args.at<String>(1);
+
+ int index;
+ PropertyAttributes attributes;
+ ContextLookupFlags flags = FOLLOW_CHAINS;
+ BindingFlags binding_flags;
+ Handle<Object> holder = context->Lookup(name,
+ flags,
+ &index,
+ &attributes,
+ &binding_flags);
+ if (isolate->has_pending_exception()) {
+ return MakePair(Failure::Exception(), NULL);
+ }
+
+ // If the index is non-negative, the slot has been found in a context.
+ if (index >= 0) {
+ 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 (as distinguished from an
+ // explicit receiver that happens to be a global object).
+ Handle<Object> receiver = isolate->factory()->the_hole_value();
+ Object* value = Context::cast(*holder)->get(index);
+ // Check for uninitialized bindings.
+ 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);
+ }
+ }
+
+ // 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<JSReceiver> object = Handle<JSReceiver>::cast(holder);
+ ASSERT(object->IsJSProxy() || object->HasProperty(*name));
+ // GetProperty below can cause GC.
+ Handle<Object> receiver_handle(
+ object->IsGlobalObject()
+ ? GlobalObject::cast(*object)->global_receiver()
+ : object->IsJSProxy() ? static_cast<Object*>(*object)
+ : ComputeReceiverForNonGlobal(isolate, JSObject::cast(*object)),
+ isolate);
+
+ // 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);
+ }
+
+ if (throw_error) {
+ // The property doesn't exist - throw exception.
+ Handle<Object> reference_error =
+ isolate->factory()->NewReferenceError("not_defined",
+ HandleVector(&name, 1));
+ return MakePair(isolate->Throw(*reference_error), NULL);
+ } else {
+ // The property doesn't exist - return undefined.
+ return MakePair(isolate->heap()->undefined_value(),
+ isolate->heap()->undefined_value());
+ }
+}
+
+
+RUNTIME_FUNCTION(ObjectPair, Runtime_LoadContextSlot) {
+ return LoadContextSlotHelper(args, isolate, true);
+}
+
+
+RUNTIME_FUNCTION(ObjectPair, Runtime_LoadContextSlotNoReferenceError) {
+ return LoadContextSlotHelper(args, isolate, false);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreContextSlot) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+
+ Handle<Object> value(args[0], isolate);
+ CONVERT_ARG_HANDLE_CHECKED(Context, context, 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, name, 2);
+ CONVERT_LANGUAGE_MODE_ARG(language_mode, 3);
+ StrictModeFlag strict_mode = (language_mode == CLASSIC_MODE)
+ ? kNonStrictMode : kStrictMode;
+
+ int index;
+ PropertyAttributes attributes;
+ ContextLookupFlags flags = FOLLOW_CHAINS;
+ BindingFlags binding_flags;
+ Handle<Object> holder = context->Lookup(name,
+ flags,
+ &index,
+ &attributes,
+ &binding_flags);
+ if (isolate->has_pending_exception()) return Failure::Exception();
+
+ if (index >= 0) {
+ // 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 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<JSReceiver> object;
+
+ if (!holder.is_null()) {
+ // The property exists on the holder.
+ object = Handle<JSReceiver>::cast(holder);
+ } else {
+ // The property was not found.
+ ASSERT(attributes == ABSENT);
+
+ if (strict_mode == kStrictMode) {
+ // Throw in strict mode (assignment to undefined variable).
+ Handle<Object> error =
+ isolate->factory()->NewReferenceError(
+ "not_defined", HandleVector(&name, 1));
+ return isolate->Throw(*error);
+ }
+ // In non-strict mode, the property is added to the global object.
+ attributes = NONE;
+ object = Handle<JSReceiver>(isolate->context()->global_object());
+ }
+
+ // Set the property if it's not read only or doesn't yet exist.
+ if ((attributes & READ_ONLY) == 0 ||
+ (object->GetLocalPropertyAttribute(*name) == ABSENT)) {
+ RETURN_IF_EMPTY_HANDLE(
+ isolate,
+ 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 =
+ isolate->factory()->NewTypeError(
+ "strict_cannot_assign", HandleVector(&name, 1));
+ return isolate->Throw(*error);
+ }
+ return *value;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Throw) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ return isolate->Throw(args[0]);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ReThrow) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ return isolate->ReThrow(args[0]);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PromoteScheduledException) {
+ SealHandleScope shs(isolate);
+ ASSERT_EQ(0, args.length());
+ return isolate->PromoteScheduledException();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ThrowReferenceError) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ Handle<Object> name(args[0], isolate);
+ Handle<Object> reference_error =
+ isolate->factory()->NewReferenceError("not_defined",
+ HandleVector(&name, 1));
+ return isolate->Throw(*reference_error);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ThrowNotDateError) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 0);
+ return isolate->Throw(*isolate->factory()->NewTypeError(
+ "not_date_object", HandleVector<Object>(NULL, 0)));
+}
+
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_StackGuard) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+
+ // First check if this is a real stack overflow.
+ if (isolate->stack_guard()->IsStackOverflow()) {
+ SealHandleScope shs(isolate);
+ return isolate->StackOverflow();
+ }
+
+ return Execution::HandleStackGuardInterrupt(isolate);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Interrupt) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ return Execution::HandleStackGuardInterrupt(isolate);
+}
+
+
+static int StackSize(Isolate* isolate) {
+ int n = 0;
+ for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) n++;
+ return n;
+}
+
+
+static void PrintTransition(Isolate* isolate, Object* result) {
+ // indentation
+ { const int nmax = 80;
+ int n = StackSize(isolate);
+ if (n <= nmax)
+ PrintF("%4d:%*s", n, n, "");
+ else
+ PrintF("%4d:%*s", n, nmax, "...");
+ }
+
+ if (result == NULL) {
+ JavaScriptFrame::PrintTop(isolate, stdout, true, false);
+ PrintF(" {\n");
+ } else {
+ // function result
+ PrintF("} -> ");
+ result->ShortPrint();
+ PrintF("\n");
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TraceEnter) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ PrintTransition(isolate, NULL);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TraceExit) {
+ SealHandleScope shs(isolate);
+ PrintTransition(isolate, args[0]);
+ return args[0]; // return TOS
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrint) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+#ifdef DEBUG
+ if (args[0]->IsString()) {
+ // If we have a string, assume it's a code "marker"
+ // and print some interesting cpu debugging info.
+ JavaScriptFrameIterator it(isolate);
+ JavaScriptFrame* frame = it.frame();
+ PrintF("fp = %p, sp = %p, caller_sp = %p: ",
+ frame->fp(), frame->sp(), frame->caller_sp());
+ } else {
+ PrintF("DebugPrint: ");
+ }
+ args[0]->Print();
+ if (args[0]->IsHeapObject()) {
+ PrintF("\n");
+ HeapObject::cast(args[0])->map()->Print();
+ }
+#else
+ // ShortPrint is available in release mode. Print is not.
+ args[0]->ShortPrint();
+#endif
+ PrintF("\n");
+ Flush();
+
+ return args[0]; // return TOS
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugTrace) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ isolate->PrintStack(stdout);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateCurrentTime) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+
+ // According to ECMA-262, section 15.9.1, page 117, the precision of
+ // the number in a Date object representing a particular instant in
+ // time is milliseconds. Therefore, we floor the result of getting
+ // the OS time.
+ double millis = floor(OS::TimeCurrentMillis());
+ return isolate->heap()->NumberFromDouble(millis);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateParseString) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, str, 0);
+ FlattenString(str);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, output, 1);
+
+ MaybeObject* maybe_result_array =
+ output->EnsureCanContainHeapObjectElements();
+ if (maybe_result_array->IsFailure()) return maybe_result_array;
+ RUNTIME_ASSERT(output->HasFastObjectElements());
+
+ DisallowHeapAllocation no_gc;
+
+ FixedArray* output_array = FixedArray::cast(output->elements());
+ RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
+ bool result;
+ String::FlatContent str_content = str->GetFlatContent();
+ if (str_content.IsAscii()) {
+ result = DateParser::Parse(str_content.ToOneByteVector(),
+ output_array,
+ isolate->unicode_cache());
+ } else {
+ ASSERT(str_content.IsTwoByte());
+ result = DateParser::Parse(str_content.ToUC16Vector(),
+ output_array,
+ isolate->unicode_cache());
+ }
+
+ if (result) {
+ return *output;
+ } else {
+ return isolate->heap()->null_value();
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateLocalTimezone) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ int64_t time = isolate->date_cache()->EquivalentTime(static_cast<int64_t>(x));
+ const char* zone = OS::LocalTimezone(static_cast<double>(time));
+ return isolate->heap()->AllocateStringFromUtf8(CStrVector(zone));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateToUTC) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_DOUBLE_ARG_CHECKED(x, 0);
+ int64_t time = isolate->date_cache()->ToUTC(static_cast<int64_t>(x));
+
+ return isolate->heap()->NumberFromDouble(static_cast<double>(time));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GlobalReceiver) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ Object* global = args[0];
+ if (!global->IsJSGlobalObject()) return isolate->heap()->null_value();
+ return JSGlobalObject::cast(global)->global_receiver();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ParseJson) {
+ HandleScope scope(isolate);
+ ASSERT_EQ(1, args.length());
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
+
+ source = Handle<String>(FlattenGetString(source));
+ // Optimized fast case where we only have ASCII characters.
+ Handle<Object> result;
+ if (source->IsSeqOneByteString()) {
+ result = JsonParser<true>::Parse(source);
+ } else {
+ result = JsonParser<false>::Parse(source);
+ }
+ if (result.is_null()) {
+ // Syntax error or stack overflow in scanner.
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+ return *result;
+}
+
+
+bool CodeGenerationFromStringsAllowed(Isolate* isolate,
+ Handle<Context> 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<EXTERNAL> state(isolate);
+ return callback(v8::Utils::ToLocal(context));
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileString) {
+ HandleScope scope(isolate);
+ ASSERT_EQ(2, args.length());
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(function_literal_only, 1);
+
+ // Extract native context.
+ Handle<Context> context(isolate->context()->native_context());
+
+ // Check if native context allows code generation from
+ // strings. Throw an exception if it doesn't.
+ if (context->allow_code_gen_from_strings()->IsFalse() &&
+ !CodeGenerationFromStringsAllowed(isolate, context)) {
+ Handle<Object> error_message =
+ context->ErrorMessageForCodeGenerationFromStrings();
+ return isolate->Throw(*isolate->factory()->NewEvalError(
+ "code_gen_from_strings", HandleVector<Object>(&error_message, 1)));
+ }
+
+ // Compile source string in the native context.
+ ParseRestriction restriction = function_literal_only
+ ? ONLY_SINGLE_FUNCTION_LITERAL : NO_PARSE_RESTRICTION;
+ Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
+ source, context, true, CLASSIC_MODE, restriction, RelocInfo::kNoPosition);
+ RETURN_IF_EMPTY_HANDLE(isolate, shared);
+ Handle<JSFunction> fun =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(shared,
+ context,
+ NOT_TENURED);
+ return *fun;
+}
+
+
+static ObjectPair CompileGlobalEval(Isolate* isolate,
+ Handle<String> source,
+ Handle<Object> receiver,
+ LanguageMode language_mode,
+ int scope_position) {
+ Handle<Context> context = Handle<Context>(isolate->context());
+ Handle<Context> native_context = Handle<Context>(context->native_context());
+
+ // Check if native context allows code generation from
+ // strings. Throw an exception if it doesn't.
+ if (native_context->allow_code_gen_from_strings()->IsFalse() &&
+ !CodeGenerationFromStringsAllowed(isolate, native_context)) {
+ Handle<Object> error_message =
+ native_context->ErrorMessageForCodeGenerationFromStrings();
+ isolate->Throw(*isolate->factory()->NewEvalError(
+ "code_gen_from_strings", HandleVector<Object>(&error_message, 1)));
+ return MakePair(Failure::Exception(), NULL);
+ }
+
+ // Deal with a normal eval call with a string argument. Compile it
+ // and return the compiled function bound in the local context.
+ Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
+ source,
+ context,
+ context->IsNativeContext(),
+ language_mode,
+ NO_PARSE_RESTRICTION,
+ scope_position);
+ RETURN_IF_EMPTY_HANDLE_VALUE(isolate, shared,
+ MakePair(Failure::Exception(), NULL));
+ Handle<JSFunction> compiled =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ shared, context, NOT_TENURED);
+ return MakePair(*compiled, *receiver);
+}
+
+
+RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEval) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 5);
+
+ Handle<Object> callee = args.at<Object>(0);
+
+ // 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->native_context()->global_eval_fun() ||
+ !args[1]->IsString()) {
+ return MakePair(*callee, isolate->heap()->the_hole_value());
+ }
+
+ CONVERT_LANGUAGE_MODE_ARG(language_mode, 3);
+ ASSERT(args[4]->IsSmi());
+ return CompileGlobalEval(isolate,
+ args.at<String>(1),
+ args.at<Object>(2),
+ language_mode,
+ args.smi_at(4));
+}
+
+
+static MaybeObject* Allocate(Isolate* isolate,
+ int size,
+ AllocationSpace space) {
+ // Allocate a block of memory in the given space (filled with a filler).
+ // Use as fallback for allocation in generated code when the space
+ // is full.
+ SealHandleScope shs(isolate);
+ RUNTIME_ASSERT(IsAligned(size, kPointerSize));
+ RUNTIME_ASSERT(size > 0);
+ Heap* heap = isolate->heap();
+ RUNTIME_ASSERT(size <= heap->MaxRegularSpaceAllocationSize());
+ Object* allocation;
+ { MaybeObject* maybe_allocation;
+ if (space == NEW_SPACE) {
+ maybe_allocation = heap->new_space()->AllocateRaw(size);
+ } else {
+ ASSERT(space == OLD_POINTER_SPACE || space == OLD_DATA_SPACE);
+ maybe_allocation = heap->paged_space(space)->AllocateRaw(size);
+ }
+ if (maybe_allocation->ToObject(&allocation)) {
+ heap->CreateFillerObjectAt(HeapObject::cast(allocation)->address(), size);
+ }
+ return maybe_allocation;
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_AllocateInNewSpace) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(Smi, size_smi, 0);
+ return Allocate(isolate, size_smi->value(), NEW_SPACE);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_AllocateInOldPointerSpace) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(Smi, size_smi, 0);
+ return Allocate(isolate, size_smi->value(), OLD_POINTER_SPACE);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_AllocateInOldDataSpace) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(Smi, size_smi, 0);
+ return Allocate(isolate, size_smi->value(), OLD_DATA_SPACE);
+}
+
+
+// Push an object unto an array of objects if it is not already in the
+// array. Returns true if the element was pushed on the stack and
+// false otherwise.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PushIfAbsent) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSArray, array, 0);
+ CONVERT_ARG_CHECKED(JSReceiver, element, 1);
+ RUNTIME_ASSERT(array->HasFastSmiOrObjectElements());
+ int length = Smi::cast(array->length())->value();
+ FixedArray* elements = FixedArray::cast(array->elements());
+ for (int i = 0; i < length; i++) {
+ if (elements->get(i) == element) return isolate->heap()->false_value();
+ }
+ Object* obj;
+ // Strict not needed. Used for cycle detection in Array join implementation.
+ { MaybeObject* maybe_obj =
+ array->SetFastElement(length, element, kNonStrictMode, true);
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ return isolate->heap()->true_value();
+}
+
+
+/**
+ * A simple visitor visits every element of Array's.
+ * The backend storage can be a fixed array for fast elements case,
+ * or a dictionary for sparse array. Since Dictionary is a subtype
+ * of FixedArray, the class can be used by both fast and slow cases.
+ * The second parameter of the constructor, fast_elements, specifies
+ * whether the storage is a FixedArray or Dictionary.
+ *
+ * An index limit is used to deal with the situation that a result array
+ * length overflows 32-bit non-negative integer.
+ */
+class ArrayConcatVisitor {
+ public:
+ ArrayConcatVisitor(Isolate* isolate,
+ Handle<FixedArray> storage,
+ bool fast_elements) :
+ isolate_(isolate),
+ storage_(Handle<FixedArray>::cast(
+ isolate->global_handles()->Create(*storage))),
+ index_offset_(0u),
+ fast_elements_(fast_elements),
+ exceeds_array_limit_(false) { }
+
+ ~ArrayConcatVisitor() {
+ clear_storage();
+ }
+
+ void visit(uint32_t i, Handle<Object> elm) {
+ if (i > JSObject::kMaxElementCount - index_offset_) {
+ exceeds_array_limit_ = true;
+ return;
+ }
+ uint32_t index = index_offset_ + i;
+
+ if (fast_elements_) {
+ if (index < static_cast<uint32_t>(storage_->length())) {
+ storage_->set(index, *elm);
+ return;
+ }
+ // Our initial estimate of length was foiled, possibly by
+ // getters on the arrays increasing the length of later arrays
+ // during iteration.
+ // This shouldn't happen in anything but pathological cases.
+ SetDictionaryMode(index);
+ // Fall-through to dictionary mode.
+ }
+ ASSERT(!fast_elements_);
+ Handle<SeededNumberDictionary> dict(
+ SeededNumberDictionary::cast(*storage_));
+ Handle<SeededNumberDictionary> result =
+ isolate_->factory()->DictionaryAtNumberPut(dict, index, elm);
+ if (!result.is_identical_to(dict)) {
+ // Dictionary needed to grow.
+ clear_storage();
+ set_storage(*result);
+ }
+ }
+
+ void increase_index_offset(uint32_t delta) {
+ if (JSObject::kMaxElementCount - index_offset_ < delta) {
+ index_offset_ = JSObject::kMaxElementCount;
+ } else {
+ index_offset_ += delta;
+ }
+ }
+
+ bool exceeds_array_limit() {
+ return exceeds_array_limit_;
+ }
+
+ Handle<JSArray> ToArray() {
+ Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
+ Handle<Object> length =
+ isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
+ Handle<Map> map;
+ if (fast_elements_) {
+ map = isolate_->factory()->GetElementsTransitionMap(array,
+ FAST_HOLEY_ELEMENTS);
+ } else {
+ map = isolate_->factory()->GetElementsTransitionMap(array,
+ DICTIONARY_ELEMENTS);
+ }
+ array->set_map(*map);
+ array->set_length(*length);
+ array->set_elements(*storage_);
+ return array;
+ }
+
+ private:
+ // Convert storage to dictionary mode.
+ void SetDictionaryMode(uint32_t index) {
+ ASSERT(fast_elements_);
+ Handle<FixedArray> current_storage(*storage_);
+ Handle<SeededNumberDictionary> slow_storage(
+ isolate_->factory()->NewSeededNumberDictionary(
+ current_storage->length()));
+ uint32_t current_length = static_cast<uint32_t>(current_storage->length());
+ for (uint32_t i = 0; i < current_length; i++) {
+ HandleScope loop_scope(isolate_);
+ Handle<Object> element(current_storage->get(i), isolate_);
+ if (!element->IsTheHole()) {
+ Handle<SeededNumberDictionary> new_storage =
+ isolate_->factory()->DictionaryAtNumberPut(slow_storage, i, element);
+ if (!new_storage.is_identical_to(slow_storage)) {
+ slow_storage = loop_scope.CloseAndEscape(new_storage);
+ }
+ }
+ }
+ clear_storage();
+ set_storage(*slow_storage);
+ fast_elements_ = false;
+ }
+
+ inline void clear_storage() {
+ isolate_->global_handles()->Destroy(
+ Handle<Object>::cast(storage_).location());
+ }
+
+ inline void set_storage(FixedArray* storage) {
+ storage_ = Handle<FixedArray>::cast(
+ isolate_->global_handles()->Create(storage));
+ }
+
+ Isolate* isolate_;
+ Handle<FixedArray> storage_; // Always a global handle.
+ // Index after last seen index. Always less than or equal to
+ // JSObject::kMaxElementCount.
+ uint32_t index_offset_;
+ bool fast_elements_ : 1;
+ bool exceeds_array_limit_ : 1;
+};
+
+
+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_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS: {
+ // Fast elements can't have lengths that are not representable by
+ // a 32-bit signed integer.
+ ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0);
+ int fast_length = static_cast<int>(length);
+ Handle<FixedArray> elements(FixedArray::cast(array->elements()));
+ for (int i = 0; i < fast_length; i++) {
+ if (!elements->get(i)->IsTheHole()) element_count++;
+ }
+ break;
+ }
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS: {
+ // Fast elements can't have lengths that are not representable by
+ // a 32-bit signed integer.
+ ASSERT(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0);
+ int fast_length = static_cast<int>(length);
+ if (array->elements()->IsFixedArray()) {
+ ASSERT(FixedArray::cast(array->elements())->length() == 0);
+ break;
+ }
+ Handle<FixedDoubleArray> elements(
+ FixedDoubleArray::cast(array->elements()));
+ for (int i = 0; i < fast_length; i++) {
+ if (!elements->is_the_hole(i)) element_count++;
+ }
+ break;
+ }
+ case DICTIONARY_ELEMENTS: {
+ Handle<SeededNumberDictionary> dictionary(
+ SeededNumberDictionary::cast(array->elements()));
+ int capacity = dictionary->Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Handle<Object> key(dictionary->KeyAt(i), array->GetIsolate());
+ if (dictionary->IsKey(*key)) {
+ element_count++;
+ }
+ }
+ break;
+ }
+ 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;
+ }
+ // As an estimate, we assume that the prototype doesn't contain any
+ // inherited elements.
+ return element_count;
+}
+
+
+
+template<class ExternalArrayClass, class ElementType>
+static void IterateExternalArrayElements(Isolate* isolate,
+ Handle<JSObject> receiver,
+ bool elements_are_ints,
+ bool elements_are_guaranteed_smis,
+ ArrayConcatVisitor* visitor) {
+ Handle<ExternalArrayClass> array(
+ ExternalArrayClass::cast(receiver->elements()));
+ uint32_t len = static_cast<uint32_t>(array->length());
+
+ ASSERT(visitor != NULL);
+ if (elements_are_ints) {
+ if (elements_are_guaranteed_smis) {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope(isolate);
+ Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get_scalar(j))),
+ isolate);
+ visitor->visit(j, e);
+ }
+ } else {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope(isolate);
+ int64_t val = static_cast<int64_t>(array->get_scalar(j));
+ if (Smi::IsValid(static_cast<intptr_t>(val))) {
+ Handle<Smi> e(Smi::FromInt(static_cast<int>(val)), isolate);
+ visitor->visit(j, e);
+ } else {
+ Handle<Object> e =
+ isolate->factory()->NewNumber(static_cast<ElementType>(val));
+ visitor->visit(j, e);
+ }
+ }
+ }
+ } else {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope(isolate);
+ Handle<Object> e = isolate->factory()->NewNumber(array->get_scalar(j));
+ visitor->visit(j, e);
+ }
+ }
+}
+
+
+// Used for sorting indices in a List<uint32_t>.
+static int compareUInt32(const uint32_t* ap, const uint32_t* bp) {
+ uint32_t a = *ap;
+ uint32_t b = *bp;
+ return (a == b) ? 0 : (a < b) ? -1 : 1;
+}
+
+
+static void CollectElementIndices(Handle<JSObject> object,
+ uint32_t range,
+ List<uint32_t>* indices) {
+ Isolate* isolate = object->GetIsolate();
+ ElementsKind kind = object->GetElementsKind();
+ switch (kind) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS: {
+ Handle<FixedArray> elements(FixedArray::cast(object->elements()));
+ uint32_t length = static_cast<uint32_t>(elements->length());
+ if (range < length) length = range;
+ for (uint32_t i = 0; i < length; i++) {
+ if (!elements->get(i)->IsTheHole()) {
+ indices->Add(i);
+ }
+ }
+ break;
+ }
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ 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()));
+ uint32_t capacity = dict->Capacity();
+ for (uint32_t j = 0; j < capacity; j++) {
+ HandleScope loop_scope(isolate);
+ Handle<Object> k(dict->KeyAt(j), isolate);
+ if (dict->IsKey(*k)) {
+ ASSERT(k->IsNumber());
+ uint32_t index = static_cast<uint32_t>(k->Number());
+ if (index < range) {
+ indices->Add(index);
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ int dense_elements_length;
+ switch (kind) {
+ case EXTERNAL_PIXEL_ELEMENTS: {
+ dense_elements_length =
+ ExternalPixelArray::cast(object->elements())->length();
+ break;
+ }
+ case EXTERNAL_BYTE_ELEMENTS: {
+ dense_elements_length =
+ ExternalByteArray::cast(object->elements())->length();
+ break;
+ }
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedByteArray::cast(object->elements())->length();
+ break;
+ }
+ case EXTERNAL_SHORT_ELEMENTS: {
+ dense_elements_length =
+ ExternalShortArray::cast(object->elements())->length();
+ break;
+ }
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedShortArray::cast(object->elements())->length();
+ break;
+ }
+ case EXTERNAL_INT_ELEMENTS: {
+ dense_elements_length =
+ ExternalIntArray::cast(object->elements())->length();
+ break;
+ }
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedIntArray::cast(object->elements())->length();
+ break;
+ }
+ case EXTERNAL_FLOAT_ELEMENTS: {
+ dense_elements_length =
+ ExternalFloatArray::cast(object->elements())->length();
+ break;
+ }
+ case EXTERNAL_DOUBLE_ELEMENTS: {
+ dense_elements_length =
+ ExternalDoubleArray::cast(object->elements())->length();
+ break;
+ }
+ default:
+ UNREACHABLE();
+ dense_elements_length = 0;
+ break;
+ }
+ uint32_t length = static_cast<uint32_t>(dense_elements_length);
+ if (range <= length) {
+ length = range;
+ // We will add all indices, so we might as well clear it first
+ // and avoid duplicates.
+ indices->Clear();
+ }
+ for (uint32_t i = 0; i < length; i++) {
+ indices->Add(i);
+ }
+ if (length == range) return; // All indices accounted for already.
+ break;
+ }
+ }
+
+ Handle<Object> prototype(object->GetPrototype(), isolate);
+ if (prototype->IsJSObject()) {
+ // The prototype will usually have no inherited element indices,
+ // but we have to check.
+ CollectElementIndices(Handle<JSObject>::cast(prototype), range, indices);
+ }
+}
+
+
+/**
+ * A helper function that visits elements of a JSArray in numerical
+ * order.
+ *
+ * The visitor argument called for each existing element in the array
+ * with the element index and the element's value.
+ * Afterwards it increments the base-index of the visitor by the array
+ * length.
+ * Returns false if any access threw an exception, otherwise true.
+ */
+static bool IterateElements(Isolate* isolate,
+ Handle<JSArray> receiver,
+ ArrayConcatVisitor* visitor) {
+ uint32_t length = static_cast<uint32_t>(receiver->length()->Number());
+ switch (receiver->GetElementsKind()) {
+ case FAST_SMI_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS: {
+ // Run through the elements FixedArray and use HasElement and GetElement
+ // to check the prototype for missing elements.
+ Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
+ int fast_length = static_cast<int>(length);
+ ASSERT(fast_length <= elements->length());
+ for (int j = 0; j < fast_length; j++) {
+ HandleScope loop_scope(isolate);
+ Handle<Object> element_value(elements->get(j), isolate);
+ if (!element_value->IsTheHole()) {
+ visitor->visit(j, element_value);
+ } else if (receiver->HasElement(j)) {
+ // Call GetElement on receiver, not its prototype, or getters won't
+ // have the correct receiver.
+ element_value = Object::GetElement(receiver, j);
+ RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element_value, false);
+ visitor->visit(j, element_value);
+ }
+ }
+ break;
+ }
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS: {
+ // Run through the elements FixedArray and use HasElement and GetElement
+ // to check the prototype for missing elements.
+ Handle<FixedDoubleArray> elements(
+ FixedDoubleArray::cast(receiver->elements()));
+ int fast_length = static_cast<int>(length);
+ ASSERT(fast_length <= elements->length());
+ for (int j = 0; j < fast_length; j++) {
+ HandleScope loop_scope(isolate);
+ if (!elements->is_the_hole(j)) {
+ double double_value = elements->get_scalar(j);
+ Handle<Object> element_value =
+ isolate->factory()->NewNumber(double_value);
+ visitor->visit(j, element_value);
+ } else if (receiver->HasElement(j)) {
+ // Call GetElement on receiver, not its prototype, or getters won't
+ // have the correct receiver.
+ Handle<Object> element_value = Object::GetElement(receiver, j);
+ RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element_value, false);
+ visitor->visit(j, element_value);
+ }
+ }
+ break;
+ }
+ case DICTIONARY_ELEMENTS: {
+ Handle<SeededNumberDictionary> dict(receiver->element_dictionary());
+ List<uint32_t> indices(dict->Capacity() / 2);
+ // Collect all indices in the object and the prototypes less
+ // than length. This might introduce duplicates in the indices list.
+ CollectElementIndices(receiver, length, &indices);
+ indices.Sort(&compareUInt32);
+ int j = 0;
+ int n = indices.length();
+ while (j < n) {
+ HandleScope loop_scope(isolate);
+ uint32_t index = indices[j];
+ 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 {
+ j++;
+ } while (j < n && indices[j] == index);
+ }
+ break;
+ }
+ case EXTERNAL_PIXEL_ELEMENTS: {
+ Handle<ExternalPixelArray> pixels(ExternalPixelArray::cast(
+ receiver->elements()));
+ for (uint32_t j = 0; j < length; j++) {
+ Handle<Smi> e(Smi::FromInt(pixels->get_scalar(j)), isolate);
+ visitor->visit(j, e);
+ }
+ break;
+ }
+ case EXTERNAL_BYTE_ELEMENTS: {
+ IterateExternalArrayElements<ExternalByteArray, int8_t>(
+ isolate, receiver, true, true, visitor);
+ break;
+ }
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
+ IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
+ isolate, receiver, true, true, visitor);
+ break;
+ }
+ case EXTERNAL_SHORT_ELEMENTS: {
+ IterateExternalArrayElements<ExternalShortArray, int16_t>(
+ isolate, receiver, true, true, visitor);
+ break;
+ }
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
+ IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
+ isolate, receiver, true, true, visitor);
+ break;
+ }
+ case EXTERNAL_INT_ELEMENTS: {
+ IterateExternalArrayElements<ExternalIntArray, int32_t>(
+ isolate, receiver, true, false, visitor);
+ break;
+ }
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS: {
+ IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
+ isolate, receiver, true, false, visitor);
+ break;
+ }
+ case EXTERNAL_FLOAT_ELEMENTS: {
+ IterateExternalArrayElements<ExternalFloatArray, float>(
+ isolate, receiver, false, false, visitor);
+ break;
+ }
+ case EXTERNAL_DOUBLE_ELEMENTS: {
+ IterateExternalArrayElements<ExternalDoubleArray, double>(
+ isolate, receiver, false, false, visitor);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ visitor->increase_index_offset(length);
+ return true;
+}
+
+
+/**
+ * Array::concat implementation.
+ * See ECMAScript 262, 15.4.4.4.
+ * TODO(581): Fix non-compliance for very large concatenations and update to
+ * following the ECMAScript 5 specification.
+ */
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConcat) {
+ HandleScope handle_scope(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0);
+ int argument_count = static_cast<int>(arguments->length()->Number());
+ RUNTIME_ASSERT(arguments->HasFastObjectElements());
+ Handle<FixedArray> elements(FixedArray::cast(arguments->elements()));
+
+ // Pass 1: estimate the length and number of elements of the result.
+ // The actual length can be larger if any of the arguments have getters
+ // that mutate other arguments (but will otherwise be precise).
+ // The number of elements is precise if there are no inherited elements.
+
+ ElementsKind kind = FAST_SMI_ELEMENTS;
+
+ uint32_t estimate_result_length = 0;
+ uint32_t estimate_nof_elements = 0;
+ for (int i = 0; i < argument_count; i++) {
+ HandleScope loop_scope(isolate);
+ Handle<Object> obj(elements->get(i), isolate);
+ uint32_t length_estimate;
+ uint32_t element_estimate;
+ if (obj->IsJSArray()) {
+ Handle<JSArray> array(Handle<JSArray>::cast(obj));
+ length_estimate = static_cast<uint32_t>(array->length()->Number());
+ if (length_estimate != 0) {
+ ElementsKind array_kind =
+ GetPackedElementsKind(array->map()->elements_kind());
+ if (IsMoreGeneralElementsKindTransition(kind, array_kind)) {
+ kind = array_kind;
+ }
+ }
+ element_estimate = EstimateElementCount(array);
+ } else {
+ if (obj->IsHeapObject()) {
+ if (obj->IsNumber()) {
+ if (IsMoreGeneralElementsKindTransition(kind, FAST_DOUBLE_ELEMENTS)) {
+ kind = FAST_DOUBLE_ELEMENTS;
+ }
+ } else if (IsMoreGeneralElementsKindTransition(kind, FAST_ELEMENTS)) {
+ kind = FAST_ELEMENTS;
+ }
+ }
+ length_estimate = 1;
+ element_estimate = 1;
+ }
+ // Avoid overflows by capping at kMaxElementCount.
+ if (JSObject::kMaxElementCount - estimate_result_length <
+ length_estimate) {
+ estimate_result_length = JSObject::kMaxElementCount;
+ } else {
+ estimate_result_length += length_estimate;
+ }
+ if (JSObject::kMaxElementCount - estimate_nof_elements <
+ element_estimate) {
+ estimate_nof_elements = JSObject::kMaxElementCount;
+ } else {
+ estimate_nof_elements += element_estimate;
+ }
+ }
+
+ // If estimated number of elements is more than half of length, a
+ // fixed array (fast case) is more time and space-efficient than a
+ // dictionary.
+ bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length;
+
+ Handle<FixedArray> storage;
+ if (fast_case) {
+ if (kind == FAST_DOUBLE_ELEMENTS) {
+ Handle<FixedDoubleArray> double_storage =
+ isolate->factory()->NewFixedDoubleArray(estimate_result_length);
+ int j = 0;
+ bool failure = false;
+ for (int i = 0; i < argument_count; i++) {
+ Handle<Object> obj(elements->get(i), isolate);
+ if (obj->IsSmi()) {
+ double_storage->set(j, Smi::cast(*obj)->value());
+ j++;
+ } else if (obj->IsNumber()) {
+ double_storage->set(j, obj->Number());
+ j++;
+ } else {
+ JSArray* array = JSArray::cast(*obj);
+ uint32_t length = static_cast<uint32_t>(array->length()->Number());
+ switch (array->map()->elements_kind()) {
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS: {
+ // Empty fixed array indicates that there are no elements.
+ if (array->elements()->IsFixedArray()) break;
+ FixedDoubleArray* elements =
+ FixedDoubleArray::cast(array->elements());
+ for (uint32_t i = 0; i < length; i++) {
+ if (elements->is_the_hole(i)) {
+ failure = true;
+ break;
+ }
+ double double_value = elements->get_scalar(i);
+ double_storage->set(j, double_value);
+ j++;
+ }
+ break;
+ }
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_SMI_ELEMENTS: {
+ FixedArray* elements(
+ FixedArray::cast(array->elements()));
+ for (uint32_t i = 0; i < length; i++) {
+ Object* element = elements->get(i);
+ if (element->IsTheHole()) {
+ failure = true;
+ break;
+ }
+ int32_t int_value = Smi::cast(element)->value();
+ double_storage->set(j, int_value);
+ j++;
+ }
+ break;
+ }
+ case FAST_HOLEY_ELEMENTS:
+ ASSERT_EQ(0, length);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ if (failure) break;
+ }
+ Handle<JSArray> array = isolate->factory()->NewJSArray(0);
+ Smi* length = Smi::FromInt(j);
+ Handle<Map> map;
+ map = isolate->factory()->GetElementsTransitionMap(array, kind);
+ array->set_map(*map);
+ array->set_length(length);
+ array->set_elements(*double_storage);
+ return *array;
+ }
+ // The backing storage array must have non-existing elements to preserve
+ // holes across concat operations.
+ storage = isolate->factory()->NewFixedArrayWithHoles(
+ estimate_result_length);
+ } else {
+ // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
+ uint32_t at_least_space_for = estimate_nof_elements +
+ (estimate_nof_elements >> 2);
+ storage = Handle<FixedArray>::cast(
+ isolate->factory()->NewSeededNumberDictionary(at_least_space_for));
+ }
+
+ ArrayConcatVisitor visitor(isolate, storage, fast_case);
+
+ for (int i = 0; i < argument_count; i++) {
+ Handle<Object> obj(elements->get(i), isolate);
+ if (obj->IsJSArray()) {
+ Handle<JSArray> array = Handle<JSArray>::cast(obj);
+ if (!IterateElements(isolate, array, &visitor)) {
+ return Failure::Exception();
+ }
+ } else {
+ visitor.visit(0, obj);
+ visitor.increase_index_offset(1);
+ }
+ }
+
+ if (visitor.exceeds_array_limit()) {
+ return isolate->Throw(
+ *isolate->factory()->NewRangeError("invalid_array_length",
+ HandleVector<Object>(NULL, 0)));
+ }
+ return *visitor.ToArray();
+}
+
+
+// This will not allocate (flatten the string), but it may run
+// very slowly for very deeply nested ConsStrings. For debugging use only.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GlobalPrint) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(String, string, 0);
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(string, &op);
+ while (stream.HasMore()) {
+ uint16_t character = stream.GetNext();
+ PrintF("%c", character);
+ }
+ return string;
+}
+
+
+// Moves all own elements of an object, that are below a limit, to positions
+// starting at zero. All undefined values are placed after non-undefined values,
+// and are followed by non-existing element. Does not change the length
+// property.
+// Returns the number of non-undefined elements collected.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_RemoveArrayHoles) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSObject, object, 0);
+ CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
+ return object->PrepareElementsForSort(limit);
+}
+
+
+// Move contents of argument 0 (an array) to argument 1 (an array)
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MoveArrayContents) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSArray, from, 0);
+ CONVERT_ARG_CHECKED(JSArray, to, 1);
+ from->ValidateElements();
+ to->ValidateElements();
+ FixedArrayBase* new_elements = from->elements();
+ ElementsKind from_kind = from->GetElementsKind();
+ MaybeObject* maybe_new_map;
+ maybe_new_map = to->GetElementsTransitionMap(isolate, from_kind);
+ Object* new_map;
+ if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
+ to->set_map_and_elements(Map::cast(new_map), new_elements);
+ to->set_length(from->length());
+ Object* obj;
+ { MaybeObject* maybe_obj = from->ResetElements();
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ }
+ from->set_length(Smi::FromInt(0));
+ to->ValidateElements();
+ return to;
+}
+
+
+// How many elements does this object/array have?
+RUNTIME_FUNCTION(MaybeObject*, Runtime_EstimateNumberOfElements) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSObject, object, 0);
+ HeapObject* elements = object->elements();
+ if (elements->IsDictionary()) {
+ int result = SeededNumberDictionary::cast(elements)->NumberOfElements();
+ return Smi::FromInt(result);
+ } else if (object->IsJSArray()) {
+ return JSArray::cast(object)->length();
+ } else {
+ return Smi::FromInt(FixedArray::cast(elements)->length());
+ }
+}
+
+
+// Returns an array that tells you where in the [0, length) interval an array
+// might have elements. Can either return an array of keys (positive integers
+// or undefined) or a number representing the positive length of an interval
+// starting at index 0.
+// Intervals can span over some keys that are not in the object.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetArrayKeys) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
+ CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
+ if (array->elements()->IsDictionary()) {
+ Handle<FixedArray> keys = isolate->factory()->empty_fixed_array();
+ for (Handle<Object> p = array;
+ !p->IsNull();
+ p = Handle<Object>(p->GetPrototype(isolate), isolate)) {
+ if (p->IsJSProxy() || JSObject::cast(*p)->HasIndexedInterceptor()) {
+ // Bail out if we find a proxy or interceptor, likely not worth
+ // collecting keys in that case.
+ return *isolate->factory()->NewNumberFromUint(length);
+ }
+ Handle<JSObject> current = Handle<JSObject>::cast(p);
+ Handle<FixedArray> current_keys =
+ isolate->factory()->NewFixedArray(
+ current->NumberOfLocalElements(NONE));
+ current->GetLocalElementKeys(*current_keys, NONE);
+ keys = UnionOfKeys(keys, current_keys);
+ }
+ // Erase any keys >= length.
+ // TODO(adamk): Remove this step when the contract of %GetArrayKeys
+ // is changed to let this happen on the JS side.
+ for (int i = 0; i < keys->length(); i++) {
+ if (NumberToUint32(keys->get(i)) >= length) keys->set_undefined(i);
+ }
+ return *isolate->factory()->NewJSArrayWithElements(keys);
+ } else {
+ ASSERT(array->HasFastSmiOrObjectElements() ||
+ array->HasFastDoubleElements());
+ uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
+ return *isolate->factory()->NewNumberFromUint(Min(actual_length, length));
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LookupAccessor) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_CHECKED(JSReceiver, receiver, 0);
+ CONVERT_ARG_CHECKED(Name, name, 1);
+ CONVERT_SMI_ARG_CHECKED(flag, 2);
+ AccessorComponent component = flag == 0 ? ACCESSOR_GETTER : ACCESSOR_SETTER;
+ if (!receiver->IsJSObject()) return isolate->heap()->undefined_value();
+ return JSObject::cast(receiver)->LookupAccessor(name, component);
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugBreak) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ return Execution::DebugBreakHelper();
+}
+
+
+// Helper functions for wrapping and unwrapping stack frame ids.
+static Smi* WrapFrameId(StackFrame::Id id) {
+ ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
+ return Smi::FromInt(id >> 2);
+}
+
+
+static StackFrame::Id UnwrapFrameId(int wrapped) {
+ return static_cast<StackFrame::Id>(wrapped << 2);
+}
+
+
+// Adds a JavaScript function as a debug event listener.
+// args[0]: debug event listener function to set or null or undefined for
+// clearing the event listener function
+// args[1]: object supplied during callback
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDebugEventListener) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ RUNTIME_ASSERT(args[0]->IsJSFunction() ||
+ args[0]->IsUndefined() ||
+ args[0]->IsNull());
+ Handle<Object> callback = args.at<Object>(0);
+ Handle<Object> data = args.at<Object>(1);
+ isolate->debugger()->SetEventListener(callback, data);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Break) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ isolate->stack_guard()->DebugBreak();
+ return isolate->heap()->undefined_value();
+}
+
+
+static MaybeObject* DebugLookupResultValue(Heap* heap,
+ Object* receiver,
+ Name* name,
+ LookupResult* result,
+ bool* caught_exception) {
+ Object* value;
+ switch (result->type()) {
+ case NORMAL:
+ value = result->holder()->GetNormalizedProperty(result);
+ if (value->IsTheHole()) {
+ return heap->undefined_value();
+ }
+ return value;
+ case FIELD: {
+ Object* value;
+ MaybeObject* maybe_value =
+ JSObject::cast(result->holder())->FastPropertyAt(
+ result->representation(),
+ result->GetFieldIndex().field_index());
+ if (!maybe_value->To(&value)) return maybe_value;
+ if (value->IsTheHole()) {
+ return heap->undefined_value();
+ }
+ return value;
+ }
+ case CONSTANT:
+ return result->GetConstant();
+ case CALLBACKS: {
+ Object* structure = result->GetCallbackObject();
+ if (structure->IsForeign() || structure->IsAccessorInfo()) {
+ 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());
+ maybe_value = heap->isolate()->pending_exception();
+ heap->isolate()->clear_pending_exception();
+ if (caught_exception != NULL) {
+ *caught_exception = true;
+ }
+ return maybe_value;
+ }
+ return value;
+ } else {
+ return heap->undefined_value();
+ }
+ }
+ case INTERCEPTOR:
+ case TRANSITION:
+ return heap->undefined_value();
+ case HANDLER:
+ case NONEXISTENT:
+ UNREACHABLE();
+ return heap->undefined_value();
+ }
+ UNREACHABLE(); // keep the compiler happy
+ return heap->undefined_value();
+}
+
+
+// Get debugger related details for an object property.
+// args[0]: object holding property
+// args[1]: name of the property
+//
+// The array returned contains the following information:
+// 0: Property value
+// 1: Property details
+// 2: Property value is exception
+// 3: Getter function if defined
+// 4: Setter function if defined
+// Items 2-4 are only filled if the property has either a getter or a setter
+// defined through __defineGetter__ and/or __defineSetter__.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetPropertyDetails) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+
+ // Make sure to set the current context to the context before the debugger was
+ // entered (if the debugger is entered). The reason for switching context here
+ // is that for some property lookups (accessors and interceptors) callbacks
+ // into the embedding application can occour, and the embedding application
+ // could have the assumption that its own native context is the current
+ // context and not some internal debugger context.
+ SaveContext save(isolate);
+ if (isolate->debug()->InDebugger()) {
+ isolate->set_context(*isolate->debug()->debugger_entry()->GetContext());
+ }
+
+ // Skip the global proxy as it has no properties and always delegates to the
+ // real global object.
+ if (obj->IsJSGlobalProxy()) {
+ obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
+ }
+
+
+ // Check if the name is trivially convertible to an index and get the element
+ // if so.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ Handle<FixedArray> details = isolate->factory()->NewFixedArray(2);
+ Object* element_or_char;
+ { MaybeObject* maybe_element_or_char =
+ Runtime::GetElementOrCharAt(isolate, obj, index);
+ if (!maybe_element_or_char->ToObject(&element_or_char)) {
+ return maybe_element_or_char;
+ }
+ }
+ details->set(0, element_or_char);
+ details->set(
+ 1, PropertyDetails(NONE, NORMAL, Representation::None()).AsSmi());
+ return *isolate->factory()->NewJSArrayWithElements(details);
+ }
+
+ // Find the number of objects making up this.
+ int length = LocalPrototypeChainLength(*obj);
+
+ // Try local lookup on each of the objects.
+ Handle<JSObject> jsproto = obj;
+ for (int i = 0; i < length; i++) {
+ LookupResult result(isolate);
+ jsproto->LocalLookup(*name, &result);
+ if (result.IsFound()) {
+ // LookupResult is not GC safe as it holds raw object pointers.
+ // GC can happen later in this code so put the required fields into
+ // local variables using handles when required for later use.
+ Handle<Object> result_callback_obj;
+ if (result.IsPropertyCallbacks()) {
+ result_callback_obj = Handle<Object>(result.GetCallbackObject(),
+ isolate);
+ }
+ Smi* property_details = result.GetPropertyDetails().AsSmi();
+ // DebugLookupResultValue can cause GC so details from LookupResult needs
+ // to be copied to handles before this.
+ bool caught_exception = false;
+ Object* raw_value;
+ { MaybeObject* maybe_raw_value =
+ DebugLookupResultValue(isolate->heap(), *obj, *name,
+ &result, &caught_exception);
+ if (!maybe_raw_value->ToObject(&raw_value)) return maybe_raw_value;
+ }
+ Handle<Object> value(raw_value, isolate);
+
+ // If the callback object is a fixed array then it contains JavaScript
+ // getter and/or setter.
+ bool hasJavaScriptAccessors = result.IsPropertyCallbacks() &&
+ result_callback_obj->IsAccessorPair();
+ Handle<FixedArray> details =
+ isolate->factory()->NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
+ details->set(0, *value);
+ details->set(1, property_details);
+ if (hasJavaScriptAccessors) {
+ AccessorPair* accessors = AccessorPair::cast(*result_callback_obj);
+ details->set(2, isolate->heap()->ToBoolean(caught_exception));
+ details->set(3, accessors->GetComponent(ACCESSOR_GETTER));
+ details->set(4, accessors->GetComponent(ACCESSOR_SETTER));
+ }
+
+ return *isolate->factory()->NewJSArrayWithElements(details);
+ }
+ if (i < length - 1) {
+ jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
+ }
+ }
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetProperty) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+
+ LookupResult result(isolate);
+ obj->Lookup(*name, &result);
+ if (result.IsFound()) {
+ return DebugLookupResultValue(isolate->heap(), *obj, *name, &result, NULL);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
+// Return the property type calculated from the property details.
+// args[0]: smi with property details.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPropertyTypeFromDetails) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_PROPERTY_DETAILS_CHECKED(details, 0);
+ return Smi::FromInt(static_cast<int>(details.type()));
+}
+
+
+// Return the property attribute calculated from the property details.
+// args[0]: smi with property details.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPropertyAttributesFromDetails) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_PROPERTY_DETAILS_CHECKED(details, 0);
+ return Smi::FromInt(static_cast<int>(details.attributes()));
+}
+
+
+// Return the property insertion index calculated from the property details.
+// args[0]: smi with property details.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPropertyIndexFromDetails) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_PROPERTY_DETAILS_CHECKED(details, 0);
+ // TODO(verwaest): Depends on the type of details.
+ return Smi::FromInt(details.dictionary_index());
+}
+
+
+// Return property value from named interceptor.
+// args[0]: object
+// args[1]: property name
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugNamedInterceptorPropertyValue) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ RUNTIME_ASSERT(obj->HasNamedInterceptor());
+ CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
+
+ PropertyAttributes attributes;
+ return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
+}
+
+
+// Return element value from indexed interceptor.
+// args[0]: object
+// args[1]: index
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugIndexedInterceptorElementValue) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0);
+ RUNTIME_ASSERT(obj->HasIndexedInterceptor());
+ CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
+
+ return obj->GetElementWithInterceptor(*obj, index);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CheckExecutionState) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() >= 1);
+ CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
+ // Check that the break id is valid.
+ if (isolate->debug()->break_id() == 0 ||
+ break_id != isolate->debug()->break_id()) {
+ return isolate->Throw(
+ isolate->heap()->illegal_execution_state_string());
+ }
+
+ return isolate->heap()->true_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameCount) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ // Check arguments.
+ Object* result;
+ { MaybeObject* maybe_result = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // Count all frames which are relevant to debugging stack trace.
+ int n = 0;
+ StackFrame::Id id = isolate->debug()->break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there is no JavaScript stack frame count is 0.
+ return Smi::FromInt(0);
+ }
+
+ for (JavaScriptFrameIterator it(isolate, id); !it.done(); it.Advance()) {
+ n += it.frame()->GetInlineCount();
+ }
+ return Smi::FromInt(n);
+}
+
+
+class FrameInspector {
+ public:
+ FrameInspector(JavaScriptFrame* frame,
+ 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_jsframe_index, isolate);
+ }
+ has_adapted_arguments_ = frame_->has_adapted_arguments();
+ is_bottommost_ = inlined_jsframe_index == 0;
+ is_optimized_ = frame_->is_optimized();
+ }
+
+ ~FrameInspector() {
+ // Get rid of the calculated deoptimized frame if any.
+ if (deoptimized_frame_ != NULL) {
+ Deoptimizer::DeleteDebuggerInspectableFrame(deoptimized_frame_,
+ isolate_);
+ }
+ }
+
+ int GetParametersCount() {
+ return is_optimized_
+ ? deoptimized_frame_->parameters_count()
+ : frame_->ComputeParametersCount();
+ }
+ int expression_count() { return deoptimized_frame_->expression_count(); }
+ Object* GetFunction() {
+ return is_optimized_
+ ? deoptimized_frame_->GetFunction()
+ : frame_->function();
+ }
+ Object* GetParameter(int index) {
+ return is_optimized_
+ ? deoptimized_frame_->GetParameter(index)
+ : frame_->GetParameter(index);
+ }
+ Object* GetExpression(int index) {
+ return is_optimized_
+ ? deoptimized_frame_->GetExpression(index)
+ : frame_->GetExpression(index);
+ }
+ int GetSourcePosition() {
+ return is_optimized_
+ ? deoptimized_frame_->GetSourcePosition()
+ : frame_->LookupCode()->SourcePosition(frame_->pc());
+ }
+ bool IsConstructor() {
+ return is_optimized_ && !is_bottommost_
+ ? deoptimized_frame_->HasConstructStub()
+ : frame_->IsConstructor();
+ }
+
+ // To inspect all the provided arguments the frame might need to be
+ // replaced with the arguments frame.
+ void SetArgumentsFrame(JavaScriptFrame* frame) {
+ ASSERT(has_adapted_arguments_);
+ frame_ = frame;
+ is_optimized_ = frame_->is_optimized();
+ ASSERT(!is_optimized_);
+ }
+
+ private:
+ JavaScriptFrame* frame_;
+ DeoptimizedFrameInfo* deoptimized_frame_;
+ Isolate* isolate_;
+ bool is_optimized_;
+ bool is_bottommost_;
+ bool has_adapted_arguments_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameInspector);
+};
+
+
+static const int kFrameDetailsFrameIdIndex = 0;
+static const int kFrameDetailsReceiverIndex = 1;
+static const int kFrameDetailsFunctionIndex = 2;
+static const int kFrameDetailsArgumentCountIndex = 3;
+static const int kFrameDetailsLocalCountIndex = 4;
+static const int kFrameDetailsSourcePositionIndex = 5;
+static const int kFrameDetailsConstructCallIndex = 6;
+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
+//
+// The array returned contains the following information:
+// 0: Frame id
+// 1: Receiver
+// 2: Function
+// 3: Argument count
+// 4: Local count
+// 5: Source position
+// 6: Constructor call
+// 7: Is at return
+// 8: Flags
+// Arguments name, value
+// Locals name, value
+// Return value if any
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ // Check arguments.
+ Object* check;
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_check->ToObject(&check)) return maybe_check;
+ }
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
+ Heap* heap = isolate->heap();
+
+ // Find the relevant frame with the requested index.
+ StackFrame::Id id = isolate->debug()->break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there are no JavaScript stack frames return undefined.
+ return heap->undefined_value();
+ }
+
+ int count = 0;
+ JavaScriptFrameIterator it(isolate, id);
+ for (; !it.done(); it.Advance()) {
+ if (index < count + it.frame()->GetInlineCount()) break;
+ count += it.frame()->GetInlineCount();
+ }
+ if (it.done()) return heap->undefined_value();
+
+ 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_jsframe_index, isolate);
+
+ // Traverse the saved contexts chain to find the active context for the
+ // selected frame.
+ SaveContext* save = FindSavedContextForFrame(isolate, it.frame());
+
+ // Get the frame id.
+ Handle<Object> frame_id(WrapFrameId(it.frame()->id()), isolate);
+
+ // Find source position in unoptimized code.
+ int position = frame_inspector.GetSourcePosition();
+
+ // Check for constructor frame.
+ bool constructor = frame_inspector.IsConstructor();
+
+ // Get scope info and read from it for local variable information.
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+ ASSERT(*scope_info != ScopeInfo::Empty(isolate));
+
+ // Get the locals names and values into a temporary array.
+ //
+ // TODO(1240907): Hide compiler-introduced stack variables
+ // (e.g. .result)? For users of the debugger, they will probably be
+ // confusing.
+ Handle<FixedArray> locals =
+ isolate->factory()->NewFixedArray(scope_info->LocalCount() * 2);
+
+ // Fill in the values of the locals.
+ int i = 0;
+ for (; i < scope_info->StackLocalCount(); ++i) {
+ // Use the value from the stack.
+ locals->set(i * 2, scope_info->LocalName(i));
+ locals->set(i * 2 + 1, frame_inspector.GetExpression(i));
+ }
+ if (i < scope_info->LocalCount()) {
+ // Get the context containing declarations.
+ Handle<Context> context(
+ Context::cast(it.frame()->context())->declaration_context());
+ 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, &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 (!is_optimized && index == 0) {
+ at_return = isolate->debug()->IsBreakAtReturn(it.frame());
+ }
+
+ // If positioned just before return find the value to be returned and add it
+ // to the frame information.
+ Handle<Object> return_value = isolate->factory()->undefined_value();
+ if (at_return) {
+ StackFrameIterator it2(isolate);
+ Address internal_frame_sp = NULL;
+ while (!it2.done()) {
+ if (it2.frame()->is_internal()) {
+ internal_frame_sp = it2.frame()->sp();
+ } else {
+ if (it2.frame()->is_java_script()) {
+ if (it2.frame()->id() == it.frame()->id()) {
+ // The internal frame just before the JavaScript frame contains the
+ // value to return on top. A debug break at return will create an
+ // internal frame to store the return value (eax/rax/r0) before
+ // entering the debug break exit frame.
+ if (internal_frame_sp != NULL) {
+ return_value =
+ Handle<Object>(Memory::Object_at(internal_frame_sp),
+ isolate);
+ break;
+ }
+ }
+ }
+
+ // Indicate that the previous frame was not an internal frame.
+ internal_frame_sp = NULL;
+ }
+ it2.Advance();
+ }
+ }
+
+ // Now advance to the arguments adapter frame (if any). It contains all
+ // 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 ((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 = scope_info->ParameterCount();
+ if (argument_count < frame_inspector.GetParametersCount()) {
+ argument_count = frame_inspector.GetParametersCount();
+ }
+
+ // Calculate the size of the result.
+ int details_size = kFrameDetailsFirstDynamicIndex +
+ 2 * (argument_count + scope_info->LocalCount()) +
+ (at_return ? 1 : 0);
+ Handle<FixedArray> details = isolate->factory()->NewFixedArray(details_size);
+
+ // Add the frame id.
+ details->set(kFrameDetailsFrameIdIndex, *frame_id);
+
+ // Add the function (same as in function frame).
+ details->set(kFrameDetailsFunctionIndex, frame_inspector.GetFunction());
+
+ // Add the arguments count.
+ details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
+
+ // Add the locals count
+ details->set(kFrameDetailsLocalCountIndex,
+ Smi::FromInt(scope_info->LocalCount()));
+
+ // Add the source position.
+ if (position != RelocInfo::kNoPosition) {
+ details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
+ } else {
+ details->set(kFrameDetailsSourcePositionIndex, heap->undefined_value());
+ }
+
+ // Add the constructor information.
+ details->set(kFrameDetailsConstructCallIndex, heap->ToBoolean(constructor));
+
+ // Add the at return information.
+ details->set(kFrameDetailsAtReturnIndex, heap->ToBoolean(at_return));
+
+ // Add flags to indicate information on whether this frame is
+ // bit 0: invoked in the debugger context.
+ // bit 1: optimized frame.
+ // bit 2: inlined in optimized frame
+ int flags = 0;
+ if (*save->context() == *isolate->debug()->debug_context()) {
+ flags |= 1 << 0;
+ }
+ if (is_optimized) {
+ flags |= 1 << 1;
+ flags |= inlined_jsframe_index << 2;
+ }
+ details->set(kFrameDetailsFlagsIndex, Smi::FromInt(flags));
+
+ // Fill the dynamic part.
+ int details_index = kFrameDetailsFirstDynamicIndex;
+
+ // Add arguments name and value.
+ for (int i = 0; i < argument_count; i++) {
+ // Name of the argument.
+ 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 < frame_inspector.GetParametersCount()) {
+ // Get the value from the stack.
+ details->set(details_index++, frame_inspector.GetParameter(i));
+ } else {
+ details->set(details_index++, heap->undefined_value());
+ }
+ }
+
+ // Add locals name and value from the temporary copy from the function frame.
+ for (int i = 0; i < scope_info->LocalCount() * 2; i++) {
+ details->set(details_index++, locals->get(i));
+ }
+
+ // Add the value being returned.
+ if (at_return) {
+ details->set(details_index++, *return_value);
+ }
+
+ // Add the receiver (same as in function frame).
+ // 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->is_classic_mode() &&
+ !function->IsBuiltin()) {
+ // 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
+ // hide this optimization from the debugger, we wrap the receiver
+ // by creating correct wrapper object based on the calling frame's
+ // native context.
+ it.Advance();
+ Handle<Context> calling_frames_native_context(
+ Context::cast(Context::cast(it.frame()->context())->native_context()));
+ ASSERT(!receiver->IsUndefined() && !receiver->IsNull());
+ receiver =
+ isolate->factory()->ToObject(receiver, calling_frames_native_context);
+ }
+ details->set(kFrameDetailsReceiverIndex, *receiver);
+
+ ASSERT_EQ(details_size, details_index);
+ return *isolate->factory()->NewJSArrayWithElements(details);
+}
+
+
+// Create a plain JSObject which materializes the local scope for the specified
+// frame.
+static Handle<JSObject> MaterializeStackLocalsWithFrameInspector(
+ Isolate* isolate,
+ Handle<JSObject> target,
+ Handle<JSFunction> function,
+ FrameInspector* frame_inspector) {
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ // First fill all parameters.
+ for (int i = 0; i < scope_info->ParameterCount(); ++i) {
+ Handle<Object> value(i < frame_inspector->GetParametersCount()
+ ? frame_inspector->GetParameter(i)
+ : isolate->heap()->undefined_value(),
+ isolate);
+ ASSERT(!value->IsTheHole());
+
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(isolate,
+ target,
+ Handle<String>(scope_info->ParameterName(i)),
+ value,
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
+ }
+
+ // Second fill all stack locals.
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ Handle<Object> value(frame_inspector->GetExpression(i), isolate);
+ if (value->IsTheHole()) continue;
+
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(isolate,
+ target,
+ Handle<String>(scope_info->StackLocalName(i)),
+ value,
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
+ }
+
+ return target;
+}
+
+
+static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate,
+ Handle<JSObject> target,
+ Handle<JSFunction> function,
+ JavaScriptFrame* frame,
+ int inlined_jsframe_index) {
+ if (inlined_jsframe_index != 0 || frame->is_optimized()) {
+ // Optimized frames are not supported.
+ // TODO(yangguo): make sure all code deoptimized when debugger is active
+ // and assert that this cannot happen.
+ return;
+ }
+
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ // Parameters.
+ for (int i = 0; i < scope_info->ParameterCount(); ++i) {
+ ASSERT(!frame->GetParameter(i)->IsTheHole());
+ HandleScope scope(isolate);
+ Handle<Object> value = GetProperty(
+ isolate, target, Handle<String>(scope_info->ParameterName(i)));
+ frame->SetParameterValue(i, *value);
+ }
+
+ // Stack locals.
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ if (frame->GetExpression(i)->IsTheHole()) continue;
+ HandleScope scope(isolate);
+ Handle<Object> value = GetProperty(
+ isolate, target, Handle<String>(scope_info->StackLocalName(i)));
+ frame->SetExpression(i, *value);
+ }
+}
+
+
+static Handle<JSObject> MaterializeLocalContext(Isolate* isolate,
+ Handle<JSObject> target,
+ Handle<JSFunction> function,
+ JavaScriptFrame* frame) {
+ HandleScope scope(isolate);
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ if (!scope_info->HasContext()) return target;
+
+ // Third fill all context locals.
+ Handle<Context> frame_context(Context::cast(frame->context()));
+ Handle<Context> function_context(frame_context->declaration_context());
+ if (!scope_info->CopyContextLocalsToScopeObject(
+ isolate, function_context, target)) {
+ return Handle<JSObject>();
+ }
+
+ // Finally copy any properties from the function context extension.
+ // These will be variables introduced by eval.
+ if (function_context->closure() == *function) {
+ if (function_context->has_extension() &&
+ !function_context->IsNativeContext()) {
+ Handle<JSObject> ext(JSObject::cast(function_context->extension()));
+ 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());
+ Handle<String> key(String::cast(keys->get(i)));
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(isolate,
+ target,
+ key,
+ GetProperty(isolate, ext, key),
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
+ }
+ }
+ }
+
+ return target;
+}
+
+
+static Handle<JSObject> MaterializeLocalScope(
+ Isolate* isolate,
+ JavaScriptFrame* frame,
+ int inlined_jsframe_index) {
+ FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
+
+ Handle<JSObject> local_scope =
+ isolate->factory()->NewJSObject(isolate->object_function());
+ local_scope = MaterializeStackLocalsWithFrameInspector(
+ isolate, local_scope, function, &frame_inspector);
+ RETURN_IF_EMPTY_HANDLE_VALUE(isolate, local_scope, Handle<JSObject>());
+
+ return MaterializeLocalContext(isolate, local_scope, function, frame);
+}
+
+
+// Set the context local variable value.
+static bool SetContextLocalValue(Isolate* isolate,
+ Handle<ScopeInfo> scope_info,
+ Handle<Context> context,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ for (int i = 0; i < scope_info->ContextLocalCount(); i++) {
+ Handle<String> next_name(scope_info->ContextLocalName(i));
+ if (variable_name->Equals(*next_name)) {
+ VariableMode mode;
+ InitializationFlag init_flag;
+ int context_index =
+ scope_info->ContextSlotIndex(*next_name, &mode, &init_flag);
+ context->set(context_index, *new_value);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+static bool SetLocalVariableValue(Isolate* isolate,
+ JavaScriptFrame* frame,
+ int inlined_jsframe_index,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ if (inlined_jsframe_index != 0 || frame->is_optimized()) {
+ // Optimized frames are not supported.
+ return false;
+ }
+
+ Handle<JSFunction> function(frame->function());
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ bool default_result = false;
+
+ // Parameters.
+ for (int i = 0; i < scope_info->ParameterCount(); ++i) {
+ if (scope_info->ParameterName(i)->Equals(*variable_name)) {
+ frame->SetParameterValue(i, *new_value);
+ // Argument might be shadowed in heap context, don't stop here.
+ default_result = true;
+ }
+ }
+
+ // Stack locals.
+ for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
+ if (scope_info->StackLocalName(i)->Equals(*variable_name)) {
+ frame->SetExpression(i, *new_value);
+ return true;
+ }
+ }
+
+ if (scope_info->HasContext()) {
+ // Context locals.
+ Handle<Context> frame_context(Context::cast(frame->context()));
+ Handle<Context> function_context(frame_context->declaration_context());
+ if (SetContextLocalValue(
+ isolate, scope_info, function_context, variable_name, new_value)) {
+ return true;
+ }
+
+ // Function context extension. These are variables introduced by eval.
+ if (function_context->closure() == *function) {
+ if (function_context->has_extension() &&
+ !function_context->IsNativeContext()) {
+ Handle<JSObject> ext(JSObject::cast(function_context->extension()));
+
+ if (ext->HasProperty(*variable_name)) {
+ // We don't expect this to do anything except replacing
+ // property value.
+ SetProperty(isolate,
+ ext,
+ variable_name,
+ new_value,
+ NONE,
+ kNonStrictMode);
+ return true;
+ }
+ }
+ }
+ }
+
+ return default_result;
+}
+
+
+// Create a plain JSObject which materializes the closure content for the
+// context.
+static Handle<JSObject> MaterializeClosure(Isolate* isolate,
+ Handle<Context> context) {
+ ASSERT(context->IsFunctionContext());
+
+ Handle<SharedFunctionInfo> shared(context->closure()->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ // 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 (!scope_info->CopyContextLocalsToScopeObject(
+ isolate, context, closure_scope)) {
+ return Handle<JSObject>();
+ }
+
+ // Finally copy any properties from the function context extension. This will
+ // be variables introduced by eval.
+ if (context->has_extension()) {
+ Handle<JSObject> ext(JSObject::cast(context->extension()));
+ 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());
+ Handle<String> key(String::cast(keys->get(i)));
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(isolate,
+ closure_scope,
+ key,
+ GetProperty(isolate, ext, key),
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
+ }
+ }
+
+ return closure_scope;
+}
+
+
+// This method copies structure of MaterializeClosure method above.
+static bool SetClosureVariableValue(Isolate* isolate,
+ Handle<Context> context,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ ASSERT(context->IsFunctionContext());
+
+ Handle<SharedFunctionInfo> shared(context->closure()->shared());
+ Handle<ScopeInfo> scope_info(shared->scope_info());
+
+ // Context locals to the context extension.
+ if (SetContextLocalValue(
+ isolate, scope_info, context, variable_name, new_value)) {
+ return true;
+ }
+
+ // Properties from the function context extension. This will
+ // be variables introduced by eval.
+ if (context->has_extension()) {
+ Handle<JSObject> ext(JSObject::cast(context->extension()));
+ if (ext->HasProperty(*variable_name)) {
+ // We don't expect this to do anything except replacing property value.
+ SetProperty(isolate,
+ ext,
+ variable_name,
+ new_value,
+ NONE,
+ kNonStrictMode);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// Create a plain JSObject which materializes the scope for the specified
+// catch context.
+static Handle<JSObject> MaterializeCatchScope(Isolate* isolate,
+ Handle<Context> context) {
+ ASSERT(context->IsCatchContext());
+ Handle<String> name(String::cast(context->extension()));
+ Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
+ isolate);
+ Handle<JSObject> catch_scope =
+ isolate->factory()->NewJSObject(isolate->object_function());
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(isolate,
+ catch_scope,
+ name,
+ thrown_object,
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
+ return catch_scope;
+}
+
+
+static bool SetCatchVariableValue(Isolate* isolate,
+ Handle<Context> context,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ ASSERT(context->IsCatchContext());
+ Handle<String> name(String::cast(context->extension()));
+ if (!name->Equals(*variable_name)) {
+ return false;
+ }
+ context->set(Context::THROWN_OBJECT_INDEX, *new_value);
+ return true;
+}
+
+
+// Create a plain JSObject which materializes the block scope for the specified
+// block context.
+static Handle<JSObject> MaterializeBlockScope(
+ Isolate* isolate,
+ Handle<Context> context) {
+ ASSERT(context->IsBlockContext());
+ 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.
+ Handle<JSObject> block_scope =
+ isolate->factory()->NewJSObject(isolate->object_function());
+
+ // Fill all context locals.
+ if (!scope_info->CopyContextLocalsToScopeObject(
+ isolate, context, block_scope)) {
+ return Handle<JSObject>();
+ }
+
+ return block_scope;
+}
+
+
+// Create a plain JSObject which materializes the module scope for the specified
+// module context.
+static Handle<JSObject> MaterializeModuleScope(
+ Isolate* isolate,
+ Handle<Context> context) {
+ ASSERT(context->IsModuleContext());
+ Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+
+ // Allocate and initialize a JSObject with all the members of the debugged
+ // module.
+ Handle<JSObject> module_scope =
+ isolate->factory()->NewJSObject(isolate->object_function());
+
+ // Fill all context locals.
+ if (!scope_info->CopyContextLocalsToScopeObject(
+ isolate, context, module_scope)) {
+ return Handle<JSObject>();
+ }
+
+ return module_scope;
+}
+
+
+// Iterate over the actual scopes visible from a stack frame or from a closure.
+// 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 "artificially" in the context chain.
+class ScopeIterator {
+ public:
+ enum ScopeType {
+ ScopeTypeGlobal = 0,
+ ScopeTypeLocal,
+ ScopeTypeWith,
+ ScopeTypeClosure,
+ ScopeTypeCatch,
+ ScopeTypeBlock,
+ ScopeTypeModule
+ };
+
+ ScopeIterator(Isolate* isolate,
+ JavaScriptFrame* frame,
+ int inlined_jsframe_index)
+ : isolate_(isolate),
+ frame_(frame),
+ inlined_jsframe_index_(inlined_jsframe_index),
+ function_(frame->function()),
+ context_(Context::cast(frame->context())),
+ nested_scope_chain_(4),
+ failed_(false) {
+
+ // 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, function_)) {
+ // 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);
+ // pc points to the instruction after the current one, possibly a break
+ // location as well. So the "- 1" to exclude it from the search.
+ break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1);
+ 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->scope_type() != EVAL_SCOPE) {
+ nested_scope_chain_.Add(scope_info);
+ }
+ } else {
+ // Reparse the code and analyze the scopes.
+ 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->scope_type() != FUNCTION_SCOPE) {
+ // Global or eval code.
+ CompilationInfoWithZone info(script);
+ if (scope_info->scope_type() == GLOBAL_SCOPE) {
+ info.MarkAsGlobal();
+ } else {
+ ASSERT(scope_info->scope_type() == EVAL_SCOPE);
+ info.MarkAsEval();
+ info.SetContext(Handle<Context>(function_->context()));
+ }
+ if (Parser::Parse(&info) && Scope::Analyze(&info)) {
+ scope = info.function()->scope();
+ }
+ RetrieveScopeChain(scope, shared_info);
+ } else {
+ // Function code
+ CompilationInfoWithZone info(shared_info);
+ if (Parser::Parse(&info) && Scope::Analyze(&info)) {
+ scope = info.function()->scope();
+ }
+ RetrieveScopeChain(scope, shared_info);
+ }
+ }
+ }
+
+ ScopeIterator(Isolate* isolate,
+ Handle<JSFunction> function)
+ : isolate_(isolate),
+ frame_(NULL),
+ inlined_jsframe_index_(0),
+ function_(function),
+ context_(function->context()),
+ failed_(false) {
+ if (function->IsBuiltin()) {
+ context_ = Handle<Context>();
+ }
+ }
+
+ // More scopes?
+ bool Done() {
+ ASSERT(!failed_);
+ return context_.is_null();
+ }
+
+ bool Failed() { return failed_; }
+
+ // Move to the next scope.
+ void Next() {
+ ASSERT(!failed_);
+ ScopeType scope_type = Type();
+ if (scope_type == ScopeTypeGlobal) {
+ // The global scope is always the last in the chain.
+ ASSERT(context_->IsNativeContext());
+ context_ = Handle<Context>();
+ return;
+ }
+ 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() {
+ ASSERT(!failed_);
+ if (!nested_scope_chain_.is_empty()) {
+ Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
+ switch (scope_info->scope_type()) {
+ case FUNCTION_SCOPE:
+ ASSERT(context_->IsFunctionContext() ||
+ !scope_info->HasContext());
+ return ScopeTypeLocal;
+ case MODULE_SCOPE:
+ ASSERT(context_->IsModuleContext());
+ return ScopeTypeModule;
+ case GLOBAL_SCOPE:
+ ASSERT(context_->IsNativeContext());
+ 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_->IsNativeContext()) {
+ ASSERT(context_->global_object()->IsGlobalObject());
+ return ScopeTypeGlobal;
+ }
+ if (context_->IsFunctionContext()) {
+ return ScopeTypeClosure;
+ }
+ if (context_->IsCatchContext()) {
+ return ScopeTypeCatch;
+ }
+ if (context_->IsBlockContext()) {
+ return ScopeTypeBlock;
+ }
+ if (context_->IsModuleContext()) {
+ return ScopeTypeModule;
+ }
+ ASSERT(context_->IsWithContext());
+ return ScopeTypeWith;
+ }
+
+ // Return the JavaScript object with the content of the current scope.
+ Handle<JSObject> ScopeObject() {
+ ASSERT(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ return Handle<JSObject>(CurrentContext()->global_object());
+ case ScopeIterator::ScopeTypeLocal:
+ // Materialize the content of the local scope into a JSObject.
+ 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()));
+ case ScopeIterator::ScopeTypeCatch:
+ return MaterializeCatchScope(isolate_, CurrentContext());
+ case ScopeIterator::ScopeTypeClosure:
+ // Materialize the content of the closure scope into a JSObject.
+ return MaterializeClosure(isolate_, CurrentContext());
+ case ScopeIterator::ScopeTypeBlock:
+ return MaterializeBlockScope(isolate_, CurrentContext());
+ case ScopeIterator::ScopeTypeModule:
+ return MaterializeModuleScope(isolate_, CurrentContext());
+ }
+ UNREACHABLE();
+ return Handle<JSObject>();
+ }
+
+ bool SetVariableValue(Handle<String> variable_name,
+ Handle<Object> new_value) {
+ ASSERT(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ break;
+ case ScopeIterator::ScopeTypeLocal:
+ return SetLocalVariableValue(isolate_, frame_, inlined_jsframe_index_,
+ variable_name, new_value);
+ case ScopeIterator::ScopeTypeWith:
+ break;
+ case ScopeIterator::ScopeTypeCatch:
+ return SetCatchVariableValue(isolate_, CurrentContext(),
+ variable_name, new_value);
+ case ScopeIterator::ScopeTypeClosure:
+ return SetClosureVariableValue(isolate_, CurrentContext(),
+ variable_name, new_value);
+ case ScopeIterator::ScopeTypeBlock:
+ // TODO(2399): should we implement it?
+ break;
+ case ScopeIterator::ScopeTypeModule:
+ // TODO(2399): should we implement it?
+ break;
+ }
+ return false;
+ }
+
+ Handle<ScopeInfo> CurrentScopeInfo() {
+ ASSERT(!failed_);
+ 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() {
+ ASSERT(!failed_);
+ if (Type() == ScopeTypeGlobal ||
+ nested_scope_chain_.is_empty()) {
+ return context_;
+ } else if (nested_scope_chain_.last()->HasContext()) {
+ return context_;
+ } else {
+ return Handle<Context>();
+ }
+ }
+
+#ifdef DEBUG
+ // Debug print of the content of the current scope.
+ void DebugPrint() {
+ ASSERT(!failed_);
+ switch (Type()) {
+ case ScopeIterator::ScopeTypeGlobal:
+ PrintF("Global:\n");
+ CurrentContext()->Print();
+ break;
+
+ case ScopeIterator::ScopeTypeLocal: {
+ PrintF("Local:\n");
+ function_->shared()->scope_info()->Print();
+ if (!CurrentContext().is_null()) {
+ CurrentContext()->Print();
+ if (CurrentContext()->has_extension()) {
+ Handle<Object> extension(CurrentContext()->extension(), isolate_);
+ if (extension->IsJSContextExtensionObject()) {
+ extension->Print();
+ }
+ }
+ }
+ break;
+ }
+
+ case ScopeIterator::ScopeTypeWith:
+ PrintF("With:\n");
+ CurrentContext()->extension()->Print();
+ break;
+
+ case ScopeIterator::ScopeTypeCatch:
+ PrintF("Catch:\n");
+ CurrentContext()->extension()->Print();
+ CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print();
+ break;
+
+ case ScopeIterator::ScopeTypeClosure:
+ PrintF("Closure:\n");
+ CurrentContext()->Print();
+ if (CurrentContext()->has_extension()) {
+ Handle<Object> extension(CurrentContext()->extension(), isolate_);
+ if (extension->IsJSContextExtensionObject()) {
+ extension->Print();
+ }
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ PrintF("\n");
+ }
+#endif
+
+ private:
+ Isolate* isolate_;
+ JavaScriptFrame* frame_;
+ int inlined_jsframe_index_;
+ Handle<JSFunction> function_;
+ Handle<Context> context_;
+ List<Handle<ScopeInfo> > nested_scope_chain_;
+ bool failed_;
+
+ void RetrieveScopeChain(Scope* scope,
+ Handle<SharedFunctionInfo> shared_info) {
+ 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.
+ // Or it could be due to stack overflow.
+ ASSERT(isolate_->has_pending_exception());
+ failed_ = true;
+ }
+ }
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
+};
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeCount) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ // Check arguments.
+ Object* check;
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_check->ToObject(&check)) return maybe_check;
+ }
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator it(isolate, id);
+ JavaScriptFrame* frame = it.frame();
+
+ // Count the visible scopes.
+ int n = 0;
+ for (ScopeIterator it(isolate, frame, 0);
+ !it.Done();
+ it.Next()) {
+ n++;
+ }
+
+ return Smi::FromInt(n);
+}
+
+
+// Returns the list of step-in positions (text offset) in a function of the
+// stack frame in a range from the current debug break position to the end
+// of the corresponding statement.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetStepInPositions) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ // Check arguments.
+ Object* check;
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_check->ToObject(&check)) return maybe_check;
+ }
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator frame_it(isolate, id);
+ JavaScriptFrame* frame = frame_it.frame();
+
+ Handle<JSFunction> fun =
+ Handle<JSFunction>(frame->function());
+ Handle<SharedFunctionInfo> shared =
+ Handle<SharedFunctionInfo>(fun->shared());
+
+ if (!isolate->debug()->EnsureDebugInfo(shared, fun)) {
+ return isolate->heap()->undefined_value();
+ }
+
+ Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared);
+
+ int len = 0;
+ Handle<JSArray> array(isolate->factory()->NewJSArray(10));
+ // Find the break point where execution has stopped.
+ BreakLocationIterator break_location_iterator(debug_info,
+ ALL_BREAK_LOCATIONS);
+
+ break_location_iterator.FindBreakLocationFromAddress(frame->pc());
+ int current_statement_pos = break_location_iterator.statement_position();
+
+ while (!break_location_iterator.Done()) {
+ if (break_location_iterator.pc() > frame->pc()) {
+ if (break_location_iterator.IsStepInLocation(isolate)) {
+ Smi* position_value = Smi::FromInt(break_location_iterator.position());
+ JSObject::SetElement(array, len,
+ Handle<Object>(position_value, isolate),
+ NONE, kNonStrictMode);
+ len++;
+ }
+ }
+ // Advance iterator.
+ break_location_iterator.Next();
+ if (current_statement_pos !=
+ break_location_iterator.statement_position()) {
+ break;
+ }
+ }
+ return *array;
+}
+
+
+static const int kScopeDetailsTypeIndex = 0;
+static const int kScopeDetailsObjectIndex = 1;
+static const int kScopeDetailsSize = 2;
+
+
+static MaybeObject* MaterializeScopeDetails(Isolate* isolate,
+ ScopeIterator* it) {
+ // Calculate the size of the result.
+ int details_size = kScopeDetailsSize;
+ Handle<FixedArray> details = isolate->factory()->NewFixedArray(details_size);
+
+ // Fill in scope details.
+ details->set(kScopeDetailsTypeIndex, Smi::FromInt(it->Type()));
+ Handle<JSObject> scope_object = it->ScopeObject();
+ RETURN_IF_EMPTY_HANDLE(isolate, scope_object);
+ details->set(kScopeDetailsObjectIndex, *scope_object);
+
+ return *isolate->factory()->NewJSArrayWithElements(details);
+}
+
+
+// Return an array with scope details
+// args[0]: number: break id
+// args[1]: number: frame index
+// args[2]: number: inlined frame index
+// args[3]: number: scope index
+//
+// The array returned contains the following information:
+// 0: Scope type
+// 1: Scope object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+
+ // Check arguments.
+ Object* check;
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_check->ToObject(&check)) return maybe_check;
+ }
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+ 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.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator frame_it(isolate, id);
+ JavaScriptFrame* frame = frame_it.frame();
+
+ // Find the requested scope.
+ int n = 0;
+ ScopeIterator it(isolate, frame, inlined_jsframe_index);
+ for (; !it.Done() && n < index; it.Next()) {
+ n++;
+ }
+ if (it.Done()) {
+ return isolate->heap()->undefined_value();
+ }
+ return MaterializeScopeDetails(isolate, &it);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFunctionScopeCount) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ // Check arguments.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+
+ // Count the visible scopes.
+ int n = 0;
+ for (ScopeIterator it(isolate, fun); !it.Done(); it.Next()) {
+ n++;
+ }
+
+ return Smi::FromInt(n);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFunctionScopeDetails) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ // Check arguments.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
+
+ // Find the requested scope.
+ int n = 0;
+ ScopeIterator it(isolate, fun);
+ for (; !it.Done() && n < index; it.Next()) {
+ n++;
+ }
+ if (it.Done()) {
+ return isolate->heap()->undefined_value();
+ }
+
+ return MaterializeScopeDetails(isolate, &it);
+}
+
+
+static bool SetScopeVariableValue(ScopeIterator* it, int index,
+ Handle<String> variable_name,
+ Handle<Object> new_value) {
+ for (int n = 0; !it->Done() && n < index; it->Next()) {
+ n++;
+ }
+ if (it->Done()) {
+ return false;
+ }
+ return it->SetVariableValue(variable_name, new_value);
+}
+
+
+// Change variable value in closure or local scope
+// args[0]: number or JsFunction: break id or function
+// args[1]: number: frame index (when arg[0] is break id)
+// args[2]: number: inlined frame index (when arg[0] is break id)
+// args[3]: number: scope index
+// args[4]: string: variable name
+// args[5]: object: new value
+//
+// Return true if success and false otherwise
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetScopeVariableValue) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 6);
+
+ // Check arguments.
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]);
+ CONVERT_ARG_HANDLE_CHECKED(String, variable_name, 4);
+ Handle<Object> new_value = args.at<Object>(5);
+
+ bool res;
+ if (args[0]->IsNumber()) {
+ Object* check;
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_check->ToObject(&check)) return maybe_check;
+ }
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+ CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator frame_it(isolate, id);
+ JavaScriptFrame* frame = frame_it.frame();
+
+ ScopeIterator it(isolate, frame, inlined_jsframe_index);
+ res = SetScopeVariableValue(&it, index, variable_name, new_value);
+ } else {
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+ ScopeIterator it(isolate, fun);
+ res = SetScopeVariableValue(&it, index, variable_name, new_value);
+ }
+
+ return isolate->heap()->ToBoolean(res);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrintScopes) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 0);
+
+#ifdef DEBUG
+ // Print the scopes for the top frame.
+ StackFrameLocator locator(isolate);
+ JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
+ for (ScopeIterator it(isolate, frame, 0);
+ !it.Done();
+ it.Next()) {
+ it.DebugPrint();
+ }
+#endif
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetThreadCount) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+
+ // Check arguments.
+ Object* result;
+ { MaybeObject* maybe_result = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // Count all archived V8 threads.
+ int n = 0;
+ for (ThreadState* thread =
+ isolate->thread_manager()->FirstThreadStateInUse();
+ thread != NULL;
+ thread = thread->Next()) {
+ n++;
+ }
+
+ // Total number of threads is current thread and archived threads.
+ return Smi::FromInt(n + 1);
+}
+
+
+static const int kThreadDetailsCurrentThreadIndex = 0;
+static const int kThreadDetailsThreadIdIndex = 1;
+static const int kThreadDetailsSize = 2;
+
+// Return an array with thread details
+// args[0]: number: break id
+// args[1]: number: thread index
+//
+// The array returned contains the following information:
+// 0: Is current thread?
+// 1: Thread id
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetThreadDetails) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ // Check arguments.
+ Object* check;
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_check->ToObject(&check)) return maybe_check;
+ }
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
+
+ // Allocate array for result.
+ Handle<FixedArray> details =
+ isolate->factory()->NewFixedArray(kThreadDetailsSize);
+
+ // Thread index 0 is current thread.
+ if (index == 0) {
+ // Fill the details.
+ details->set(kThreadDetailsCurrentThreadIndex,
+ isolate->heap()->true_value());
+ details->set(kThreadDetailsThreadIdIndex,
+ Smi::FromInt(ThreadId::Current().ToInteger()));
+ } else {
+ // Find the thread with the requested index.
+ int n = 1;
+ ThreadState* thread =
+ isolate->thread_manager()->FirstThreadStateInUse();
+ while (index != n && thread != NULL) {
+ thread = thread->Next();
+ n++;
+ }
+ if (thread == NULL) {
+ return isolate->heap()->undefined_value();
+ }
+
+ // Fill the details.
+ details->set(kThreadDetailsCurrentThreadIndex,
+ isolate->heap()->false_value());
+ details->set(kThreadDetailsThreadIdIndex,
+ Smi::FromInt(thread->id().ToInteger()));
+ }
+
+ // Convert to JS array and return.
+ return *isolate->factory()->NewJSArrayWithElements(details);
+}
+
+
+// Sets the disable break state
+// args[0]: disable break state
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDisableBreak) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_BOOLEAN_ARG_CHECKED(disable_break, 0);
+ isolate->debug()->set_disable_break(disable_break);
+ return isolate->heap()->undefined_value();
+}
+
+
+static bool IsPositionAlignmentCodeCorrect(int alignment) {
+ return alignment == STATEMENT_ALIGNED || alignment == BREAK_POSITION_ALIGNED;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetBreakLocations) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, statement_aligned_code, Int32, args[1]);
+
+ if (!IsPositionAlignmentCodeCorrect(statement_aligned_code)) {
+ return isolate->ThrowIllegalOperation();
+ }
+ BreakPositionAlignment alignment =
+ static_cast<BreakPositionAlignment>(statement_aligned_code);
+
+ Handle<SharedFunctionInfo> shared(fun->shared());
+ // Find the number of break points
+ Handle<Object> break_locations =
+ Debug::GetSourceBreakLocations(shared, alignment);
+ if (break_locations->IsUndefined()) return isolate->heap()->undefined_value();
+ // Return array as JS array
+ return *isolate->factory()->NewJSArrayWithElements(
+ Handle<FixedArray>::cast(break_locations));
+}
+
+
+// Set a break point in a function.
+// args[0]: function
+// args[1]: number: break source position (within the function source)
+// args[2]: number: break point object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFunctionBreakPoint) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
+ RUNTIME_ASSERT(source_position >= 0);
+ Handle<Object> break_point_object_arg = args.at<Object>(2);
+
+ // Set break point.
+ isolate->debug()->SetBreakPoint(function, break_point_object_arg,
+ &source_position);
+
+ return Smi::FromInt(source_position);
+}
+
+
+// Changes the state of a break point in a script and returns source position
+// where break point was set. NOTE: Regarding performance see the NOTE for
+// GetScriptFromScriptData.
+// args[0]: script to set break point in
+// args[1]: number: break source position (within the script source)
+// args[2]: number, breakpoint position alignment
+// args[3]: number: break point object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetScriptBreakPoint) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 4);
+ CONVERT_ARG_HANDLE_CHECKED(JSValue, wrapper, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
+ RUNTIME_ASSERT(source_position >= 0);
+ CONVERT_NUMBER_CHECKED(int32_t, statement_aligned_code, Int32, args[2]);
+ Handle<Object> break_point_object_arg = args.at<Object>(3);
+
+ if (!IsPositionAlignmentCodeCorrect(statement_aligned_code)) {
+ return isolate->ThrowIllegalOperation();
+ }
+ BreakPositionAlignment alignment =
+ static_cast<BreakPositionAlignment>(statement_aligned_code);
+
+ // Get the script from the script wrapper.
+ RUNTIME_ASSERT(wrapper->value()->IsScript());
+ Handle<Script> script(Script::cast(wrapper->value()));
+
+ // Set break point.
+ if (!isolate->debug()->SetBreakPointForScript(script, break_point_object_arg,
+ &source_position,
+ alignment)) {
+ return isolate->heap()->undefined_value();
+ }
+
+ return Smi::FromInt(source_position);
+}
+
+
+// Clear a break point
+// args[0]: number: break point object
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearBreakPoint) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ Handle<Object> break_point_object_arg = args.at<Object>(0);
+
+ // Clear break point.
+ isolate->debug()->ClearBreakPoint(break_point_object_arg);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+// Change the state of break on exceptions.
+// args[0]: Enum value indicating whether to affect caught/uncaught exceptions.
+// args[1]: Boolean indicating on/off.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ChangeBreakOnException) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ RUNTIME_ASSERT(args[0]->IsNumber());
+ CONVERT_BOOLEAN_ARG_CHECKED(enable, 1);
+
+ // If the number doesn't match an enum value, the ChangeBreakOnException
+ // function will default to affecting caught exceptions.
+ ExceptionBreakType type =
+ static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
+ // Update break point state.
+ isolate->debug()->ChangeBreakOnException(type, enable);
+ return isolate->heap()->undefined_value();
+}
+
+
+// Returns the state of break on exceptions
+// args[0]: boolean indicating uncaught exceptions
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsBreakOnException) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ RUNTIME_ASSERT(args[0]->IsNumber());
+
+ ExceptionBreakType type =
+ static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
+ bool result = isolate->debug()->IsBreakOnException(type);
+ return Smi::FromInt(result);
+}
+
+
+// Prepare for stepping
+// args[0]: break id for checking execution state
+// args[1]: step action from the enumeration StepAction
+// args[2]: number of times to perform the step, for step out it is the number
+// of frames to step down.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ // Check arguments.
+ Object* check;
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_check->ToObject(&check)) return maybe_check;
+ }
+ if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ // Get the step action and check validity.
+ StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
+ if (step_action != StepIn &&
+ step_action != StepNext &&
+ step_action != StepOut &&
+ step_action != StepInMin &&
+ step_action != StepMin) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ // Get the number of steps.
+ int step_count = NumberToInt32(args[2]);
+ if (step_count < 1) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ // Clear all current stepping setup.
+ isolate->debug()->ClearStepping();
+
+ // Prepare step.
+ isolate->debug()->PrepareStep(static_cast<StepAction>(step_action),
+ step_count);
+ return isolate->heap()->undefined_value();
+}
+
+
+// Clear all stepping set by PrepareStep.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearStepping) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 0);
+ isolate->debug()->ClearStepping();
+ return isolate->heap()->undefined_value();
+}
+
+
+// Helper function to find or create the arguments object for
+// Runtime_DebugEvaluate.
+static Handle<JSObject> MaterializeArgumentsObject(
+ Isolate* isolate,
+ Handle<JSObject> target,
+ Handle<JSFunction> function) {
+ // Do not materialize the arguments object for eval or top-level code.
+ // Skip if "arguments" is already taken.
+ if (!function->shared()->is_function() ||
+ target->HasLocalProperty(isolate->heap()->arguments_string())) {
+ return target;
+ }
+
+ // FunctionGetArguments can't throw an exception.
+ Handle<JSObject> arguments = Handle<JSObject>::cast(
+ Accessors::FunctionGetArguments(function));
+ SetProperty(isolate,
+ target,
+ isolate->factory()->arguments_string(),
+ arguments,
+ ::NONE,
+ kNonStrictMode);
+ return target;
+}
+
+
+// Compile and evaluate source for the given context.
+static MaybeObject* DebugEvaluate(Isolate* isolate,
+ Handle<Context> context,
+ Handle<Object> context_extension,
+ Handle<Object> receiver,
+ Handle<String> source) {
+ if (context_extension->IsJSObject()) {
+ Handle<JSObject> extension = Handle<JSObject>::cast(context_extension);
+ Handle<JSFunction> closure(context->closure(), isolate);
+ context = isolate->factory()->NewWithContext(closure, context, extension);
+ }
+
+ Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
+ source,
+ context,
+ context->IsNativeContext(),
+ CLASSIC_MODE,
+ NO_PARSE_RESTRICTION,
+ RelocInfo::kNoPosition);
+ RETURN_IF_EMPTY_HANDLE(isolate, shared);
+
+ Handle<JSFunction> eval_fun =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ shared, context, NOT_TENURED);
+ bool pending_exception;
+ Handle<Object> result = Execution::Call(
+ eval_fun, receiver, 0, NULL, &pending_exception);
+
+ if (pending_exception) return Failure::Exception();
+
+ // Skip the global proxy as it has no properties and always delegates to the
+ // real global object.
+ if (result->IsJSGlobalProxy()) {
+ result = Handle<JSObject>(JSObject::cast(result->GetPrototype(isolate)));
+ }
+
+ // Clear the oneshot breakpoints so that the debugger does not step further.
+ isolate->debug()->ClearStepping();
+ return *result;
+}
+
+
+// Evaluate a piece of JavaScript in the context of a stack frame for
+// debugging. Things that need special attention are:
+// - Parameters and stack-allocated locals need to be materialized. Altered
+// values need to be written back to the stack afterwards.
+// - The arguments object needs to materialized.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
+ HandleScope scope(isolate);
+
+ // Check the execution state and decode arguments frame and source to be
+ // evaluated.
+ ASSERT(args.length() == 6);
+ Object* check_result;
+ { MaybeObject* maybe_result = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_result->ToObject(&check_result)) return maybe_result;
+ }
+ CONVERT_SMI_ARG_CHECKED(wrapped_id, 1);
+ CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 3);
+ CONVERT_BOOLEAN_ARG_CHECKED(disable_break, 4);
+ Handle<Object> context_extension(args[5], isolate);
+
+ // Handle the processing of break.
+ DisableBreak disable_break_save(disable_break);
+
+ // Get the frame where the debugging is performed.
+ StackFrame::Id id = UnwrapFrameId(wrapped_id);
+ JavaScriptFrameIterator it(isolate, id);
+ JavaScriptFrame* frame = it.frame();
+ FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
+ Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
+
+ // Traverse the saved contexts chain to find the active context for the
+ // selected frame.
+ SaveContext* save = FindSavedContextForFrame(isolate, frame);
+
+ SaveContext savex(isolate);
+ isolate->set_context(*(save->context()));
+
+ // Evaluate on the context of the frame.
+ Handle<Context> context(Context::cast(frame->context()));
+ ASSERT(!context.is_null());
+
+ // Materialize stack locals and the arguments object.
+ Handle<JSObject> materialized =
+ isolate->factory()->NewJSObject(isolate->object_function());
+
+ materialized = MaterializeStackLocalsWithFrameInspector(
+ isolate, materialized, function, &frame_inspector);
+ RETURN_IF_EMPTY_HANDLE(isolate, materialized);
+
+ materialized = MaterializeArgumentsObject(isolate, materialized, function);
+ RETURN_IF_EMPTY_HANDLE(isolate, materialized);
+
+ // Add the materialized object in a with-scope to shadow the stack locals.
+ context = isolate->factory()->NewWithContext(function, context, materialized);
+
+ Handle<Object> receiver(frame->receiver(), isolate);
+ Object* evaluate_result_object;
+ { MaybeObject* maybe_result =
+ DebugEvaluate(isolate, context, context_extension, receiver, source);
+ if (!maybe_result->ToObject(&evaluate_result_object)) return maybe_result;
+ }
+
+ Handle<Object> result(evaluate_result_object, isolate);
+
+ // Write back potential changes to materialized stack locals to the stack.
+ UpdateStackLocalsFromMaterializedObject(
+ isolate, materialized, function, frame, inlined_jsframe_index);
+
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluateGlobal) {
+ HandleScope scope(isolate);
+
+ // Check the execution state and decode arguments frame and source to be
+ // evaluated.
+ ASSERT(args.length() == 4);
+ Object* check_result;
+ { MaybeObject* maybe_result = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_result->ToObject(&check_result)) return maybe_result;
+ }
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 1);
+ CONVERT_BOOLEAN_ARG_CHECKED(disable_break, 2);
+ Handle<Object> context_extension(args[3], isolate);
+
+ // Handle the processing of break.
+ DisableBreak disable_break_save(disable_break);
+
+ // Enter the top context from before the debugger was invoked.
+ SaveContext save(isolate);
+ SaveContext* top = &save;
+ while (top != NULL && *top->context() == *isolate->debug()->debug_context()) {
+ top = top->prev();
+ }
+ if (top != NULL) {
+ isolate->set_context(*top->context());
+ }
+
+ // Get the native context now set to the top context from before the
+ // debugger was invoked.
+ Handle<Context> context = isolate->native_context();
+ Handle<Object> receiver = isolate->global_object();
+ return DebugEvaluate(isolate, context, context_extension, receiver, source);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetLoadedScripts) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 0);
+
+ // Fill the script objects.
+ Handle<FixedArray> instances = isolate->debug()->GetLoadedScripts();
+
+ // Convert the script objects to proper JS objects.
+ for (int i = 0; i < instances->length(); i++) {
+ Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
+ // Get the script wrapper in a local handle before calling GetScriptWrapper,
+ // because using
+ // instances->set(i, *GetScriptWrapper(script))
+ // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
+ // already have dereferenced the instances handle.
+ Handle<JSValue> wrapper = GetScriptWrapper(script);
+ instances->set(i, *wrapper);
+ }
+
+ // Return result as a JS array.
+ Handle<JSObject> result =
+ isolate->factory()->NewJSObject(isolate->array_function());
+ isolate->factory()->SetContent(Handle<JSArray>::cast(result), instances);
+ return *result;
+}
+
+
+// Helper function used by Runtime_DebugReferencedBy below.
+static int DebugReferencedBy(HeapIterator* iterator,
+ JSObject* target,
+ Object* instance_filter, int max_references,
+ FixedArray* instances, int instances_size,
+ JSFunction* arguments_function) {
+ Isolate* isolate = target->GetIsolate();
+ SealHandleScope shs(isolate);
+ DisallowHeapAllocation no_allocation;
+
+ // Iterate the heap.
+ int count = 0;
+ JSObject* last = NULL;
+ HeapObject* heap_obj = NULL;
+ while (((heap_obj = iterator->next()) != NULL) &&
+ (max_references == 0 || count < max_references)) {
+ // Only look at all JSObjects.
+ if (heap_obj->IsJSObject()) {
+ // Skip context extension objects and argument arrays as these are
+ // checked in the context of functions using them.
+ JSObject* obj = JSObject::cast(heap_obj);
+ if (obj->IsJSContextExtensionObject() ||
+ obj->map()->constructor() == arguments_function) {
+ continue;
+ }
+
+ // Check if the JS object has a reference to the object looked for.
+ if (obj->ReferencesObject(target)) {
+ // Check instance filter if supplied. This is normally used to avoid
+ // references from mirror objects (see Runtime_IsInPrototypeChain).
+ if (!instance_filter->IsUndefined()) {
+ Object* V = obj;
+ while (true) {
+ Object* prototype = V->GetPrototype(isolate);
+ if (prototype->IsNull()) {
+ break;
+ }
+ if (instance_filter == prototype) {
+ obj = NULL; // Don't add this object.
+ break;
+ }
+ V = prototype;
+ }
+ }
+
+ if (obj != NULL) {
+ // Valid reference found add to instance array if supplied an update
+ // count.
+ if (instances != NULL && count < instances_size) {
+ instances->set(count, obj);
+ }
+ last = obj;
+ count++;
+ }
+ }
+ }
+ }
+
+ // Check for circular reference only. This can happen when the object is only
+ // referenced from mirrors and has a circular reference in which case the
+ // object is not really alive and would have been garbage collected if not
+ // referenced from the mirror.
+ if (count == 1 && last == target) {
+ count = 0;
+ }
+
+ // Return the number of referencing objects found.
+ return count;
+}
+
+
+// Scan the heap for objects with direct references to an object
+// args[0]: the object to find references to
+// args[1]: constructor function for instances to exclude (Mirror)
+// args[2]: the the maximum number of objects to return
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugReferencedBy) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 3);
+
+ // First perform a full GC in order to avoid references from dead objects.
+ 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_ARG_CHECKED(JSObject, target, 0);
+ Object* instance_filter = args[1];
+ RUNTIME_ASSERT(instance_filter->IsUndefined() ||
+ instance_filter->IsJSObject());
+ 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()->native_context()->arguments_boilerplate();
+ JSFunction* arguments_function =
+ JSFunction::cast(arguments_boilerplate->map()->constructor());
+
+ // Get the number of referencing objects.
+ int count;
+ Heap* heap = isolate->heap();
+ HeapIterator heap_iterator(heap);
+ count = DebugReferencedBy(&heap_iterator,
+ target, instance_filter, max_references,
+ NULL, 0, arguments_function);
+
+ // Allocate an array to hold the result.
+ Object* object;
+ { MaybeObject* maybe_object = heap->AllocateFixedArray(count);
+ if (!maybe_object->ToObject(&object)) return maybe_object;
+ }
+ FixedArray* instances = FixedArray::cast(object);
+
+ // Fill the referencing objects.
+ // AllocateFixedArray above does not make the heap non-iterable.
+ ASSERT(heap->IsHeapIterable());
+ HeapIterator heap_iterator2(heap);
+ count = DebugReferencedBy(&heap_iterator2,
+ target, instance_filter, max_references,
+ instances, count, arguments_function);
+
+ // Return result as JS array.
+ Object* result;
+ MaybeObject* maybe_result = heap->AllocateJSObject(
+ isolate->context()->native_context()->array_function());
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ return JSArray::cast(result)->SetContent(instances);
+}
+
+
+// Helper function used by Runtime_DebugConstructedBy below.
+static int DebugConstructedBy(HeapIterator* iterator,
+ JSFunction* constructor,
+ int max_references,
+ FixedArray* instances,
+ int instances_size) {
+ DisallowHeapAllocation no_allocation;
+
+ // Iterate the heap.
+ int count = 0;
+ HeapObject* heap_obj = NULL;
+ while (((heap_obj = iterator->next()) != NULL) &&
+ (max_references == 0 || count < max_references)) {
+ // Only look at all JSObjects.
+ if (heap_obj->IsJSObject()) {
+ JSObject* obj = JSObject::cast(heap_obj);
+ if (obj->map()->constructor() == constructor) {
+ // Valid reference found add to instance array if supplied an update
+ // count.
+ if (instances != NULL && count < instances_size) {
+ instances->set(count, obj);
+ }
+ count++;
+ }
+ }
+ }
+
+ // Return the number of referencing objects found.
+ return count;
+}
+
+
+// Scan the heap for objects constructed by a specific function.
+// args[0]: the constructor to find instances of
+// args[1]: the the maximum number of objects to return
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugConstructedBy) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+
+ // First perform a full GC in order to avoid dead objects.
+ Heap* heap = isolate->heap();
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "%DebugConstructedBy");
+
+ // Check parameters.
+ CONVERT_ARG_CHECKED(JSFunction, constructor, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
+ RUNTIME_ASSERT(max_references >= 0);
+
+ // Get the number of referencing objects.
+ int count;
+ HeapIterator heap_iterator(heap);
+ count = DebugConstructedBy(&heap_iterator,
+ constructor,
+ max_references,
+ NULL,
+ 0);
+
+ // Allocate an array to hold the result.
+ Object* object;
+ { MaybeObject* maybe_object = heap->AllocateFixedArray(count);
+ if (!maybe_object->ToObject(&object)) return maybe_object;
+ }
+ FixedArray* instances = FixedArray::cast(object);
+
+ ASSERT(HEAP->IsHeapIterable());
+ // Fill the referencing objects.
+ HeapIterator heap_iterator2(heap);
+ count = DebugConstructedBy(&heap_iterator2,
+ constructor,
+ max_references,
+ instances,
+ count);
+
+ // Return result as JS array.
+ Object* result;
+ { MaybeObject* maybe_result = isolate->heap()->AllocateJSObject(
+ isolate->context()->native_context()->array_function());
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ return JSArray::cast(result)->SetContent(instances);
+}
+
+
+// Find the effective prototype object as returned by __proto__.
+// args[0]: the object to find the prototype for.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugGetPrototype) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSObject, obj, 0);
+ return GetPrototypeSkipHiddenPrototypes(isolate, obj);
+}
+
+
+// Patches script source (should be called upon BeforeCompile event).
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugSetScriptSource) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSValue, script_wrapper, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 1);
+
+ RUNTIME_ASSERT(script_wrapper->value()->IsScript());
+ Handle<Script> script(Script::cast(script_wrapper->value()));
+
+ int compilation_state = script->compilation_state();
+ RUNTIME_ASSERT(compilation_state == Script::COMPILATION_STATE_INITIAL);
+ script->set_source(*source);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SystemBreak) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ CPU::DebugBreak();
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugDisassembleFunction) {
+ HandleScope scope(isolate);
+#ifdef DEBUG
+ ASSERT(args.length() == 1);
+ // Get the function and make sure it is compiled.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
+ if (!JSFunction::EnsureCompiled(func, KEEP_EXCEPTION)) {
+ return Failure::Exception();
+ }
+ func->code()->PrintLn();
+#endif // DEBUG
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugDisassembleConstructor) {
+ HandleScope scope(isolate);
+#ifdef DEBUG
+ ASSERT(args.length() == 1);
+ // Get the function and make sure it is compiled.
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, func, 0);
+ if (!JSFunction::EnsureCompiled(func, KEEP_EXCEPTION)) {
+ return Failure::Exception();
+ }
+ func->shared()->construct_stub()->PrintLn();
+#endif // DEBUG
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetInferredName) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(JSFunction, f, 0);
+ return f->shared()->inferred_name();
+}
+
+
+static int FindSharedFunctionInfosForScript(HeapIterator* iterator,
+ Script* script,
+ FixedArray* buffer) {
+ DisallowHeapAllocation no_allocation;
+ int counter = 0;
+ int buffer_size = buffer->length();
+ for (HeapObject* obj = iterator->next();
+ obj != NULL;
+ obj = iterator->next()) {
+ ASSERT(obj != NULL);
+ if (!obj->IsSharedFunctionInfo()) {
+ continue;
+ }
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
+ if (shared->script() != script) {
+ continue;
+ }
+ if (counter < buffer_size) {
+ buffer->set(counter, shared);
+ }
+ counter++;
+ }
+ return counter;
+}
+
+
+// For a script finds all SharedFunctionInfo's in the heap that points
+// to this script. Returns JSArray of SharedFunctionInfo wrapped
+// in OpaqueReferences.
+RUNTIME_FUNCTION(MaybeObject*,
+ Runtime_LiveEditFindSharedFunctionInfosForScript) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSValue, script_value, 0);
+
+ RUNTIME_ASSERT(script_value->value()->IsScript());
+ Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
+
+ const int kBufferSize = 32;
+
+ Handle<FixedArray> array;
+ array = isolate->factory()->NewFixedArray(kBufferSize);
+ int number;
+ Heap* heap = isolate->heap();
+ {
+ heap->EnsureHeapIsIterable();
+ DisallowHeapAllocation no_allocation;
+ HeapIterator heap_iterator(heap);
+ Script* scr = *script;
+ FixedArray* arr = *array;
+ number = FindSharedFunctionInfosForScript(&heap_iterator, scr, arr);
+ }
+ if (number > kBufferSize) {
+ array = isolate->factory()->NewFixedArray(number);
+ heap->EnsureHeapIsIterable();
+ DisallowHeapAllocation no_allocation;
+ HeapIterator heap_iterator(heap);
+ Script* scr = *script;
+ FixedArray* arr = *array;
+ FindSharedFunctionInfosForScript(&heap_iterator, scr, arr);
+ }
+
+ Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(array);
+ result->set_length(Smi::FromInt(number));
+
+ LiveEdit::WrapSharedFunctionInfos(result);
+
+ return *result;
+}
+
+
+// For a script calculates compilation information about all its functions.
+// The script source is explicitly specified by the second argument.
+// The source of the actual script is not used, however it is important that
+// all generated code keeps references to this particular instance of script.
+// Returns a JSArray of compilation infos. The array is ordered so that
+// each function with all its descendant is always stored in a continues range
+// with the function itself going first. The root function is a script function.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditGatherCompileInfo) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSValue, script, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, source, 1);
+
+ RUNTIME_ASSERT(script->value()->IsScript());
+ Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
+
+ JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
+
+ if (isolate->has_pending_exception()) {
+ return Failure::Exception();
+ }
+
+ return result;
+}
+
+
+// Changes the source of the script to a new_source.
+// If old_script_name is provided (i.e. is a String), also creates a copy of
+// the script with its original source and sends notification to debugger.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditReplaceScript) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_CHECKED(JSValue, original_script_value, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, new_source, 1);
+ Handle<Object> old_script_name(args[2], isolate);
+
+ RUNTIME_ASSERT(original_script_value->value()->IsScript());
+ Handle<Script> original_script(Script::cast(original_script_value->value()));
+
+ Object* old_script = LiveEdit::ChangeScriptSource(original_script,
+ new_source,
+ old_script_name);
+
+ if (old_script->IsScript()) {
+ Handle<Script> script_handle(Script::cast(old_script));
+ return *(GetScriptWrapper(script_handle));
+ } else {
+ return isolate->heap()->null_value();
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditFunctionSourceUpdated) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_info, 0);
+ return LiveEdit::FunctionSourceUpdated(shared_info);
+}
+
+
+// Replaces code of SharedFunctionInfo with a new one.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditReplaceFunctionCode) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, new_compile_info, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_info, 1);
+
+ return LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
+}
+
+
+// Connects SharedFunctionInfo to another script.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditFunctionSetScript) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 2);
+ Handle<Object> function_object(args[0], isolate);
+ Handle<Object> script_object(args[1], isolate);
+
+ if (function_object->IsJSValue()) {
+ Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
+ if (script_object->IsJSValue()) {
+ RUNTIME_ASSERT(JSValue::cast(*script_object)->value()->IsScript());
+ Script* script = Script::cast(JSValue::cast(*script_object)->value());
+ script_object = Handle<Object>(script, isolate);
+ }
+
+ LiveEdit::SetFunctionScript(function_wrapper, script_object);
+ } else {
+ // Just ignore this. We may not have a SharedFunctionInfo for some functions
+ // and we check it in this function.
+ }
+
+ return isolate->heap()->undefined_value();
+}
+
+
+// In a code of a parent function replaces original function as embedded object
+// with a substitution one.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditReplaceRefToNestedFunction) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSValue, parent_wrapper, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSValue, orig_wrapper, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSValue, subst_wrapper, 2);
+
+ LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
+ subst_wrapper);
+
+ return isolate->heap()->undefined_value();
+}
+
+
+// Updates positions of a shared function info (first parameter) according
+// to script source change. Text change is described in second parameter as
+// array of groups of 3 numbers:
+// (change_begin, change_end, change_end_new_position).
+// Each group describes a change in text; groups are sorted by change_begin.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditPatchFunctionPositions) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_array, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, position_change_array, 1);
+
+ return LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
+}
+
+
+// For array of SharedFunctionInfo's (each wrapped in JSValue)
+// checks that none of them have activations on stacks (of any thread).
+// Returns array of the same length with corresponding results of
+// LiveEdit::FunctionPatchabilityStatus type.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditCheckAndDropActivations) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, shared_array, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(do_drop, 1);
+
+ return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
+}
+
+
+// Compares 2 strings line-by-line, then token-wise and returns diff in form
+// of JSArray of triplets (pos1, pos1_end, pos2_end) describing list
+// of diff chunks.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditCompareStrings) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(String, s1, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, s2, 1);
+
+ return *LiveEdit::CompareStrings(s1, s2);
+}
+
+
+// Restarts a call frame and completely drops all frames above.
+// Returns true if successful. Otherwise returns undefined or an error message.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_LiveEditRestartFrame) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 2);
+
+ // Check arguments.
+ Object* check;
+ { MaybeObject* maybe_check = Runtime_CheckExecutionState(
+ RUNTIME_ARGUMENTS(isolate, args));
+ if (!maybe_check->ToObject(&check)) return maybe_check;
+ }
+ CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
+ Heap* heap = isolate->heap();
+
+ // Find the relevant frame with the requested index.
+ StackFrame::Id id = isolate->debug()->break_frame_id();
+ if (id == StackFrame::NO_ID) {
+ // If there are no JavaScript stack frames return undefined.
+ return heap->undefined_value();
+ }
+
+ int count = 0;
+ JavaScriptFrameIterator it(isolate, id);
+ for (; !it.done(); it.Advance()) {
+ if (index < count + it.frame()->GetInlineCount()) break;
+ count += it.frame()->GetInlineCount();
+ }
+ if (it.done()) return heap->undefined_value();
+
+ const char* error_message = LiveEdit::RestartFrame(it.frame());
+ if (error_message) {
+ return *(isolate->factory()->InternalizeUtf8String(error_message));
+ }
+ return heap->true_value();
+}
+
+
+// A testing entry. Returns statement position which is the closest to
+// source_position.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFunctionCodePositionFromSource) {
+ HandleScope scope(isolate);
+ CHECK(isolate->debugger()->live_edit_enabled());
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
+
+ Handle<Code> code(function->code(), isolate);
+
+ if (code->kind() != Code::FUNCTION &&
+ code->kind() != Code::OPTIMIZED_FUNCTION) {
+ return isolate->heap()->undefined_value();
+ }
+
+ RelocIterator it(*code, RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION));
+ int closest_pc = 0;
+ int distance = kMaxInt;
+ while (!it.done()) {
+ int statement_position = static_cast<int>(it.rinfo()->data());
+ // Check if this break point is closer that what was previously found.
+ if (source_position <= statement_position &&
+ statement_position - source_position < distance) {
+ closest_pc =
+ static_cast<int>(it.rinfo()->pc() - code->instruction_start());
+ distance = statement_position - source_position;
+ // Check whether we can't get any closer.
+ if (distance == 0) break;
+ }
+ it.next();
+ }
+
+ return Smi::FromInt(closest_pc);
+}
+
+
+// Calls specified function with or without entering the debugger.
+// This is used in unit tests to run code as if debugger is entered or simply
+// to have a stack with C++ frame in the middle.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ExecuteInDebugContext) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
+ CONVERT_BOOLEAN_ARG_CHECKED(without_debugger, 1);
+
+ Handle<Object> result;
+ bool pending_exception;
+ {
+ if (without_debugger) {
+ result = Execution::Call(function, isolate->global_object(), 0, NULL,
+ &pending_exception);
+ } else {
+ EnterDebugger enter_debugger;
+ result = Execution::Call(function, isolate->global_object(), 0, NULL,
+ &pending_exception);
+ }
+ }
+ if (!pending_exception) {
+ return *result;
+ } else {
+ return Failure::Exception();
+ }
+}
+
+
+// Sets a v8 flag.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFlags) {
+ SealHandleScope shs(isolate);
+ CONVERT_ARG_CHECKED(String, arg, 0);
+ SmartArrayPointer<char> flags =
+ arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
+ FlagList::SetFlagsFromString(*flags, StrLength(*flags));
+ return isolate->heap()->undefined_value();
+}
+
+
+// Performs a GC.
+// Presently, it only does a full GC.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CollectGarbage) {
+ SealHandleScope shs(isolate);
+ isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, "%CollectGarbage");
+ return isolate->heap()->undefined_value();
+}
+
+
+// Gets the current heap usage.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHeapUsage) {
+ SealHandleScope shs(isolate);
+ int usage = static_cast<int>(isolate->heap()->SizeOfObjects());
+ if (!Smi::IsValid(usage)) {
+ return *isolate->factory()->NewNumberFromInt(usage);
+ }
+ return Smi::FromInt(usage);
+}
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+#ifdef V8_I18N_SUPPORT
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CanonicalizeLanguageTag) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, locale_id_str, 0);
+
+ v8::String::Utf8Value locale_id(v8::Utils::ToLocal(locale_id_str));
+
+ // Return value which denotes invalid language tag.
+ const char* const kInvalidTag = "invalid-tag";
+
+ UErrorCode error = U_ZERO_ERROR;
+ char icu_result[ULOC_FULLNAME_CAPACITY];
+ int icu_length = 0;
+
+ uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY,
+ &icu_length, &error);
+ if (U_FAILURE(error) || icu_length == 0) {
+ return isolate->heap()->AllocateStringFromOneByte(CStrVector(kInvalidTag));
+ }
+
+ char result[ULOC_FULLNAME_CAPACITY];
+
+ // Force strict BCP47 rules.
+ uloc_toLanguageTag(icu_result, result, ULOC_FULLNAME_CAPACITY, TRUE, &error);
+
+ if (U_FAILURE(error)) {
+ return isolate->heap()->AllocateStringFromOneByte(CStrVector(kInvalidTag));
+ }
+
+ return isolate->heap()->AllocateStringFromOneByte(CStrVector(result));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_AvailableLocalesOf) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, service, 0);
+
+ const icu::Locale* available_locales = NULL;
+ int32_t count = 0;
+
+ if (service->IsUtf8EqualTo(CStrVector("collator"))) {
+ available_locales = icu::Collator::getAvailableLocales(count);
+ } else if (service->IsUtf8EqualTo(CStrVector("numberformat"))) {
+ available_locales = icu::NumberFormat::getAvailableLocales(count);
+ } else if (service->IsUtf8EqualTo(CStrVector("dateformat"))) {
+ available_locales = icu::DateFormat::getAvailableLocales(count);
+ } else if (service->IsUtf8EqualTo(CStrVector("breakiterator"))) {
+ available_locales = icu::BreakIterator::getAvailableLocales(count);
+ }
+
+ UErrorCode error = U_ZERO_ERROR;
+ char result[ULOC_FULLNAME_CAPACITY];
+ Handle<JSObject> locales =
+ isolate->factory()->NewJSObject(isolate->object_function());
+
+ for (int32_t i = 0; i < count; ++i) {
+ const char* icu_name = available_locales[i].getName();
+
+ error = U_ZERO_ERROR;
+ // No need to force strict BCP47 rules.
+ uloc_toLanguageTag(icu_name, result, ULOC_FULLNAME_CAPACITY, FALSE, &error);
+ if (U_FAILURE(error)) {
+ // This shouldn't happen, but lets not break the user.
+ continue;
+ }
+
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ locales,
+ isolate->factory()->NewStringFromAscii(CStrVector(result)),
+ isolate->factory()->NewNumber(i),
+ NONE));
+ }
+
+ return *locales;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetDefaultICULocale) {
+ SealHandleScope shs(isolate);
+
+ ASSERT(args.length() == 0);
+
+ icu::Locale default_locale;
+
+ // Set the locale
+ char result[ULOC_FULLNAME_CAPACITY];
+ UErrorCode status = U_ZERO_ERROR;
+ uloc_toLanguageTag(
+ default_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+ if (U_SUCCESS(status)) {
+ return isolate->heap()->AllocateStringFromOneByte(CStrVector(result));
+ }
+
+ return isolate->heap()->AllocateStringFromOneByte(CStrVector("und"));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLanguageTagVariants) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, input, 0);
+
+ uint32_t length = static_cast<uint32_t>(input->length()->Number());
+ Handle<FixedArray> output = isolate->factory()->NewFixedArray(length);
+ Handle<Name> maximized =
+ isolate->factory()->NewStringFromAscii(CStrVector("maximized"));
+ Handle<Name> base =
+ isolate->factory()->NewStringFromAscii(CStrVector("base"));
+ for (unsigned int i = 0; i < length; ++i) {
+ MaybeObject* maybe_string = input->GetElement(i);
+ Object* locale_id;
+ if (!maybe_string->ToObject(&locale_id) || !locale_id->IsString()) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ v8::String::Utf8Value utf8_locale_id(
+ v8::Utils::ToLocal(Handle<String>(String::cast(locale_id))));
+
+ UErrorCode error = U_ZERO_ERROR;
+
+ // Convert from BCP47 to ICU format.
+ // de-DE-u-co-phonebk -> de_DE@collation=phonebook
+ char icu_locale[ULOC_FULLNAME_CAPACITY];
+ int icu_locale_length = 0;
+ uloc_forLanguageTag(*utf8_locale_id, icu_locale, ULOC_FULLNAME_CAPACITY,
+ &icu_locale_length, &error);
+ if (U_FAILURE(error) || icu_locale_length == 0) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ // Maximize the locale.
+ // de_DE@collation=phonebook -> de_Latn_DE@collation=phonebook
+ char icu_max_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_addLikelySubtags(
+ icu_locale, icu_max_locale, ULOC_FULLNAME_CAPACITY, &error);
+
+ // Remove extensions from maximized locale.
+ // de_Latn_DE@collation=phonebook -> de_Latn_DE
+ char icu_base_max_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_getBaseName(
+ icu_max_locale, icu_base_max_locale, ULOC_FULLNAME_CAPACITY, &error);
+
+ // Get original name without extensions.
+ // de_DE@collation=phonebook -> de_DE
+ char icu_base_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_getBaseName(
+ icu_locale, icu_base_locale, ULOC_FULLNAME_CAPACITY, &error);
+
+ // Convert from ICU locale format to BCP47 format.
+ // de_Latn_DE -> de-Latn-DE
+ char base_max_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_toLanguageTag(icu_base_max_locale, base_max_locale,
+ ULOC_FULLNAME_CAPACITY, FALSE, &error);
+
+ // de_DE -> de-DE
+ char base_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_toLanguageTag(
+ icu_base_locale, base_locale, ULOC_FULLNAME_CAPACITY, FALSE, &error);
+
+ if (U_FAILURE(error)) {
+ return isolate->Throw(isolate->heap()->illegal_argument_string());
+ }
+
+ Handle<JSObject> result =
+ isolate->factory()->NewJSObject(isolate->object_function());
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ result,
+ maximized,
+ isolate->factory()->NewStringFromAscii(CStrVector(base_max_locale)),
+ NONE));
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ result,
+ base,
+ isolate->factory()->NewStringFromAscii(CStrVector(base_locale)),
+ NONE));
+ output->set(i, *result);
+ }
+
+ Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(output);
+ result->set_length(Smi::FromInt(length));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateDateTimeFormat) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
+
+ Handle<ObjectTemplateInfo> date_format_template =
+ I18N::GetTemplate(isolate);
+
+ // Create an empty object wrapper.
+ bool has_pending_exception = false;
+ Handle<JSObject> local_object = Execution::InstantiateObject(
+ date_format_template, &has_pending_exception);
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+
+ // Set date time formatter as internal field of the resulting JS object.
+ icu::SimpleDateFormat* date_format = DateFormat::InitializeDateTimeFormat(
+ isolate, locale, options, resolved);
+
+ if (!date_format) return isolate->ThrowIllegalOperation();
+
+ local_object->SetInternalField(0, reinterpret_cast<Smi*>(date_format));
+
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ local_object,
+ isolate->factory()->NewStringFromAscii(CStrVector("dateFormat")),
+ isolate->factory()->NewStringFromAscii(CStrVector("valid")),
+ NONE));
+
+ Persistent<v8::Object> wrapper(reinterpret_cast<v8::Isolate*>(isolate),
+ v8::Utils::ToLocal(local_object));
+ // Make object handle weak so we can delete the data format once GC kicks in.
+ wrapper.MakeWeak<void>(NULL, &DateFormat::DeleteDateFormat);
+ wrapper.ClearAndLeak();
+ return *local_object;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalDateFormat) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 1);
+
+ bool has_pending_exception = false;
+ double millis = Execution::ToNumber(date, &has_pending_exception)->Number();
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+
+ icu::SimpleDateFormat* date_format =
+ DateFormat::UnpackDateFormat(isolate, date_format_holder);
+ if (!date_format) return isolate->ThrowIllegalOperation();
+
+ icu::UnicodeString result;
+ date_format->format(millis, result);
+
+ return *isolate->factory()->NewStringFromTwoByte(
+ Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(result.getBuffer()),
+ result.length()));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalDateParse) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, date_string, 1);
+
+ v8::String::Utf8Value utf8_date(v8::Utils::ToLocal(date_string));
+ icu::UnicodeString u_date(icu::UnicodeString::fromUTF8(*utf8_date));
+ icu::SimpleDateFormat* date_format =
+ DateFormat::UnpackDateFormat(isolate, date_format_holder);
+ if (!date_format) return isolate->ThrowIllegalOperation();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UDate date = date_format->parse(u_date, status);
+ if (U_FAILURE(status)) return isolate->heap()->undefined_value();
+
+ bool has_pending_exception = false;
+ Handle<JSDate> result = Handle<JSDate>::cast(
+ Execution::NewDate(static_cast<double>(date), &has_pending_exception));
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateNumberFormat) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
+
+ Handle<ObjectTemplateInfo> number_format_template =
+ I18N::GetTemplate(isolate);
+
+ // Create an empty object wrapper.
+ bool has_pending_exception = false;
+ Handle<JSObject> local_object = Execution::InstantiateObject(
+ number_format_template, &has_pending_exception);
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+
+ // Set number formatter as internal field of the resulting JS object.
+ icu::DecimalFormat* number_format = NumberFormat::InitializeNumberFormat(
+ isolate, locale, options, resolved);
+
+ if (!number_format) return isolate->ThrowIllegalOperation();
+
+ local_object->SetInternalField(0, reinterpret_cast<Smi*>(number_format));
+
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ local_object,
+ isolate->factory()->NewStringFromAscii(CStrVector("numberFormat")),
+ isolate->factory()->NewStringFromAscii(CStrVector("valid")),
+ NONE));
+
+ Persistent<v8::Object> wrapper(reinterpret_cast<v8::Isolate*>(isolate),
+ v8::Utils::ToLocal(local_object));
+ // Make object handle weak so we can delete the number format once GC kicks
+ // in.
+ wrapper.MakeWeak<void>(NULL, &NumberFormat::DeleteNumberFormat);
+ wrapper.ClearAndLeak();
+ return *local_object;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalNumberFormat) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);
+
+ bool has_pending_exception = false;
+ double value = Execution::ToNumber(number, &has_pending_exception)->Number();
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+
+ icu::DecimalFormat* number_format =
+ NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
+ if (!number_format) return isolate->ThrowIllegalOperation();
+
+ icu::UnicodeString result;
+ number_format->format(value, result);
+
+ return *isolate->factory()->NewStringFromTwoByte(
+ Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(result.getBuffer()),
+ result.length()));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalNumberParse) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, number_string, 1);
+
+ v8::String::Utf8Value utf8_number(v8::Utils::ToLocal(number_string));
+ icu::UnicodeString u_number(icu::UnicodeString::fromUTF8(*utf8_number));
+ icu::DecimalFormat* number_format =
+ NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
+ if (!number_format) return isolate->ThrowIllegalOperation();
+
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Formattable result;
+ // ICU 4.6 doesn't support parseCurrency call. We need to wait for ICU49
+ // to be part of Chrome.
+ // TODO(cira): Include currency parsing code using parseCurrency call.
+ // We need to check if the formatter parses all currencies or only the
+ // one it was constructed with (it will impact the API - how to return ISO
+ // code and the value).
+ number_format->parse(u_number, result, status);
+ if (U_FAILURE(status)) return isolate->heap()->undefined_value();
+
+ switch (result.getType()) {
+ case icu::Formattable::kDouble:
+ return *isolate->factory()->NewNumber(result.getDouble());
+ case icu::Formattable::kLong:
+ return *isolate->factory()->NewNumberFromInt(result.getLong());
+ case icu::Formattable::kInt64:
+ return *isolate->factory()->NewNumber(
+ static_cast<double>(result.getInt64()));
+ default:
+ return isolate->heap()->undefined_value();
+ }
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateCollator) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
+
+ Handle<ObjectTemplateInfo> collator_template = I18N::GetTemplate(isolate);
+
+ // Create an empty object wrapper.
+ bool has_pending_exception = false;
+ Handle<JSObject> local_object = Execution::InstantiateObject(
+ collator_template, &has_pending_exception);
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+
+ // Set collator as internal field of the resulting JS object.
+ icu::Collator* collator = Collator::InitializeCollator(
+ isolate, locale, options, resolved);
+
+ if (!collator) return isolate->ThrowIllegalOperation();
+
+ local_object->SetInternalField(0, reinterpret_cast<Smi*>(collator));
+
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ local_object,
+ isolate->factory()->NewStringFromAscii(CStrVector("collator")),
+ isolate->factory()->NewStringFromAscii(CStrVector("valid")),
+ NONE));
+
+ Persistent<v8::Object> wrapper(reinterpret_cast<v8::Isolate*>(isolate),
+ v8::Utils::ToLocal(local_object));
+ // Make object handle weak so we can delete the collator once GC kicks in.
+ wrapper.MakeWeak<void>(NULL, &Collator::DeleteCollator);
+ wrapper.ClearAndLeak();
+ return *local_object;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalCompare) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 3);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, collator_holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, string1, 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, string2, 2);
+
+ icu::Collator* collator = Collator::UnpackCollator(isolate, collator_holder);
+ if (!collator) return isolate->ThrowIllegalOperation();
+
+ v8::String::Value string_value1(v8::Utils::ToLocal(string1));
+ v8::String::Value string_value2(v8::Utils::ToLocal(string2));
+ const UChar* u_string1 = reinterpret_cast<const UChar*>(*string_value1);
+ const UChar* u_string2 = reinterpret_cast<const UChar*>(*string_value2);
+ UErrorCode status = U_ZERO_ERROR;
+ UCollationResult result = collator->compare(u_string1,
+ string_value1.length(),
+ u_string2,
+ string_value2.length(),
+ status);
+ if (U_FAILURE(status)) return isolate->ThrowIllegalOperation();
+
+ return *isolate->factory()->NewNumberFromInt(result);
+}
+#endif // V8_I18N_SUPPORT
+
+
+// Finds the script object from the script data. NOTE: This operation uses
+// heap traversal to find the function generated for the source position
+// for the requested break point. For lazily compiled functions several heap
+// traversals might be required rendering this operation as a rather slow
+// operation. However for setting break points which is normally done through
+// some kind of user interaction the performance is not crucial.
+static Handle<Object> Runtime_GetScriptFromScriptName(
+ Handle<String> script_name) {
+ // Scan the heap for Script objects to find the script with the requested
+ // script data.
+ Handle<Script> script;
+ Factory* factory = script_name->GetIsolate()->factory();
+ Heap* heap = script_name->GetHeap();
+ heap->EnsureHeapIsIterable();
+ DisallowHeapAllocation no_allocation_during_heap_iteration;
+ HeapIterator iterator(heap);
+ HeapObject* obj = NULL;
+ while (script.is_null() && ((obj = iterator.next()) != NULL)) {
+ // If a script is found check if it has the script data requested.
+ if (obj->IsScript()) {
+ if (Script::cast(obj)->name()->IsString()) {
+ if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
+ script = Handle<Script>(Script::cast(obj));
+ }
+ }
+ }
+ }
+
+ // If no script with the requested script data is found return undefined.
+ if (script.is_null()) return factory->undefined_value();
+
+ // Return the script found.
+ return GetScriptWrapper(script);
+}
+
+
+// Get the script object from script data. NOTE: Regarding performance
+// see the NOTE for GetScriptFromScriptData.
+// args[0]: script data for the script to find the source for
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScript) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 1);
+
+ CONVERT_ARG_CHECKED(String, script_name, 0);
+
+ // Find the requested script.
+ Handle<Object> result =
+ Runtime_GetScriptFromScriptName(Handle<String>(script_name));
+ return *result;
+}
+
+
+// Collect the raw data for a stack trace. Returns an array of 4
+// element segments each containing a receiver, function, code and
+// native code offset.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CollectStackTrace) {
+ HandleScope scope(isolate);
+ ASSERT_EQ(args.length(), 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, error_object, 0);
+ Handle<Object> caller = args.at<Object>(1);
+ CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[2]);
+
+ // Optionally capture a more detailed stack trace for the message.
+ isolate->CaptureAndSetDetailedStackTrace(error_object);
+ // Capture a simple stack trace for the stack property.
+ return *isolate->CaptureSimpleStackTrace(error_object, caller, limit);
+}
+
+
+// Retrieve the stack trace. This is the raw stack trace that yet has to
+// be formatted. Since we only need this once, clear it afterwards.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetAndClearOverflowedStackTrace) {
+ HandleScope scope(isolate);
+ ASSERT_EQ(args.length(), 1);
+ CONVERT_ARG_CHECKED(JSObject, error_object, 0);
+ String* key = isolate->heap()->hidden_stack_trace_string();
+ Object* result = error_object->GetHiddenProperty(key);
+ if (result->IsTheHole()) return isolate->heap()->undefined_value();
+ RUNTIME_ASSERT(result->IsJSArray() || result->IsUndefined());
+ error_object->DeleteHiddenProperty(key);
+ return result;
+}
+
+
+// Returns V8 version as a string.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetV8Version) {
+ SealHandleScope shs(isolate);
+ ASSERT_EQ(args.length(), 0);
+
+ const char* version_string = v8::V8::GetVersion();
+
+ return isolate->heap()->AllocateStringFromOneByte(CStrVector(version_string),
+ NOT_TENURED);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Abort) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ OS::PrintError("abort: %s\n",
+ reinterpret_cast<char*>(args[0]) + args.smi_at(1));
+ isolate->PrintStack(stderr);
+ OS::Abort();
+ UNREACHABLE();
+ return NULL;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FlattenString) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(String, str, 0);
+ FlattenString(str);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MigrateInstance) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
+ if (!object->IsJSObject()) return Smi::FromInt(0);
+ Handle<JSObject> js_object = Handle<JSObject>::cast(object);
+ if (!js_object->map()->is_deprecated()) return Smi::FromInt(0);
+ JSObject::MigrateInstance(js_object);
+ return *object;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFromCache) {
+ SealHandleScope shs(isolate);
+ // This is only called from codegen, so checks might be more lax.
+ CONVERT_ARG_CHECKED(JSFunctionResultCache, cache, 0);
+ Object* key = args[1];
+
+ int finger_index = cache->finger_index();
+ Object* o = cache->get(finger_index);
+ if (o == key) {
+ // The fastest case: hit the same place again.
+ return cache->get(finger_index + 1);
+ }
+
+ for (int i = finger_index - 2;
+ i >= JSFunctionResultCache::kEntriesIndex;
+ i -= 2) {
+ o = cache->get(i);
+ if (o == key) {
+ cache->set_finger_index(i);
+ return cache->get(i + 1);
+ }
+ }
+
+ int size = cache->size();
+ ASSERT(size <= cache->length());
+
+ for (int i = size - 2; i > finger_index; i -= 2) {
+ o = cache->get(i);
+ if (o == key) {
+ cache->set_finger_index(i);
+ return cache->get(i + 1);
+ }
+ }
+
+ // There is no value in the cache. Invoke the function and cache result.
+ HandleScope scope(isolate);
+
+ Handle<JSFunctionResultCache> cache_handle(cache);
+ Handle<Object> key_handle(key, isolate);
+ Handle<Object> value;
+ {
+ Handle<JSFunction> factory(JSFunction::cast(
+ cache_handle->get(JSFunctionResultCache::kFactoryIndex)));
+ // TODO(antonm): consider passing a receiver when constructing a cache.
+ Handle<Object> receiver(isolate->native_context()->global_object(),
+ isolate);
+ // This handle is nor shared, nor used later, so it's safe.
+ Handle<Object> argv[] = { key_handle };
+ bool pending_exception;
+ value = Execution::Call(factory,
+ receiver,
+ ARRAY_SIZE(argv),
+ argv,
+ &pending_exception);
+ if (pending_exception) return Failure::Exception();
+ }
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ cache_handle->JSFunctionResultCacheVerify();
+ }
+#endif
+
+ // Function invocation may have cleared the cache. Reread all the data.
+ finger_index = cache_handle->finger_index();
+ size = cache_handle->size();
+
+ // If we have spare room, put new data into it, otherwise evict post finger
+ // entry which is likely to be the least recently used.
+ int index = -1;
+ if (size < cache_handle->length()) {
+ cache_handle->set_size(size + JSFunctionResultCache::kEntrySize);
+ index = size;
+ } else {
+ index = finger_index + JSFunctionResultCache::kEntrySize;
+ if (index == cache_handle->length()) {
+ index = JSFunctionResultCache::kEntriesIndex;
+ }
+ }
+
+ ASSERT(index % 2 == 0);
+ ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
+ ASSERT(index < cache_handle->length());
+
+ cache_handle->set(index, *key_handle);
+ cache_handle->set(index + 1, *value);
+ cache_handle->set_finger_index(index);
+
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ cache_handle->JSFunctionResultCacheVerify();
+ }
+#endif
+
+ return *value;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MessageGetStartPosition) {
+ SealHandleScope shs(isolate);
+ CONVERT_ARG_CHECKED(JSMessageObject, message, 0);
+ return Smi::FromInt(message->start_position());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MessageGetScript) {
+ SealHandleScope shs(isolate);
+ CONVERT_ARG_CHECKED(JSMessageObject, message, 0);
+ return message->script();
+}
+
+
+#ifdef DEBUG
+// ListNatives is ONLY used by the fuzz-natives.js in debug mode
+// Exclude the code in release mode.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ListNatives) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 0);
+#define COUNT_ENTRY(Name, argc, ressize) + 1
+ int entry_count = 0
+ RUNTIME_FUNCTION_LIST(COUNT_ENTRY)
+ INLINE_FUNCTION_LIST(COUNT_ENTRY)
+ INLINE_RUNTIME_FUNCTION_LIST(COUNT_ENTRY);
+#undef COUNT_ENTRY
+ Factory* factory = isolate->factory();
+ Handle<FixedArray> elements = factory->NewFixedArray(entry_count);
+ int index = 0;
+ bool inline_runtime_functions = false;
+#define ADD_ENTRY(Name, argc, ressize) \
+ { \
+ HandleScope inner(isolate); \
+ Handle<String> name; \
+ /* Inline runtime functions have an underscore in front of the name. */ \
+ if (inline_runtime_functions) { \
+ name = factory->NewStringFromAscii( \
+ Vector<const char>("_" #Name, StrLength("_" #Name))); \
+ } else { \
+ name = factory->NewStringFromAscii( \
+ Vector<const char>(#Name, StrLength(#Name))); \
+ } \
+ Handle<FixedArray> pair_elements = factory->NewFixedArray(2); \
+ pair_elements->set(0, *name); \
+ pair_elements->set(1, Smi::FromInt(argc)); \
+ Handle<JSArray> pair = factory->NewJSArrayWithElements(pair_elements); \
+ elements->set(index++, *pair); \
+ }
+ inline_runtime_functions = false;
+ RUNTIME_FUNCTION_LIST(ADD_ENTRY)
+ inline_runtime_functions = true;
+ INLINE_FUNCTION_LIST(ADD_ENTRY)
+ INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
+#undef ADD_ENTRY
+ ASSERT_EQ(index, entry_count);
+ Handle<JSArray> result = factory->NewJSArrayWithElements(elements);
+ return *result;
+}
+#endif
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Log) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(String, format, 0);
+ CONVERT_ARG_CHECKED(JSArray, elms, 1);
+ DisallowHeapAllocation no_gc;
+ String::FlatContent format_content = format->GetFlatContent();
+ RUNTIME_ASSERT(format_content.IsAscii());
+ Vector<const uint8_t> chars = format_content.ToOneByteVector();
+ isolate->logger()->LogRuntime(Vector<const char>::cast(chars), elms);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IS_VAR) {
+ UNREACHABLE(); // implemented as macro in the parser
+ return NULL;
+}
+
+
+#define ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(Name) \
+ RUNTIME_FUNCTION(MaybeObject*, Runtime_Has##Name) { \
+ CONVERT_ARG_CHECKED(JSObject, obj, 0); \
+ return isolate->heap()->ToBoolean(obj->Has##Name()); \
+ }
+
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastObjectElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastSmiOrObjectElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastDoubleElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastHoleyElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(DictionaryElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(NonStrictArgumentsElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalPixelElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalArrayElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalByteElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalUnsignedByteElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalShortElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalUnsignedShortElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalIntElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalUnsignedIntElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalFloatElements)
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalDoubleElements)
+// Properties test sitting with elements tests - not fooling anyone.
+ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(FastProperties)
+
+#undef ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_HaveSameMap) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSObject, obj1, 0);
+ CONVERT_ARG_CHECKED(JSObject, obj2, 1);
+ return isolate->heap()->ToBoolean(obj1->map() == obj2->map());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsObserved) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+
+ if (!args[0]->IsJSReceiver()) return isolate->heap()->false_value();
+ JSReceiver* obj = JSReceiver::cast(args[0]);
+ if (obj->IsJSGlobalProxy()) {
+ Object* proto = obj->GetPrototype();
+ if (proto->IsNull()) return isolate->heap()->false_value();
+ ASSERT(proto->IsJSGlobalObject());
+ obj = JSReceiver::cast(proto);
+ }
+ return isolate->heap()->ToBoolean(obj->map()->is_observed());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetIsObserved) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSReceiver, obj, 0);
+ if (obj->IsJSGlobalProxy()) {
+ Object* proto = obj->GetPrototype();
+ if (proto->IsNull()) return isolate->heap()->undefined_value();
+ ASSERT(proto->IsJSGlobalObject());
+ obj = JSReceiver::cast(proto);
+ }
+ if (obj->IsJSProxy())
+ return isolate->heap()->undefined_value();
+
+ ASSERT(!(obj->map()->is_observed() && obj->IsJSObject() &&
+ JSObject::cast(obj)->HasFastElements()));
+ ASSERT(obj->IsJSObject());
+ return JSObject::cast(obj)->SetObserved(isolate);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetObserverDeliveryPending) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ isolate->set_observer_delivery_pending(true);
+ return isolate->heap()->undefined_value();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetObservationState) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 0);
+ return isolate->heap()->observation_state();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ObservationWeakMapCreate) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 0);
+ // TODO(adamk): Currently this runtime function is only called three times per
+ // isolate. If it's called more often, the map should be moved into the
+ // strong root list.
+ Handle<Map> map =
+ isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
+ Handle<JSWeakMap> weakmap =
+ Handle<JSWeakMap>::cast(isolate->factory()->NewJSObjectFromMap(map));
+ return WeakCollectionInitialize(isolate, weakmap);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_UnwrapGlobalProxy) {
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 1);
+ Object* object = args[0];
+ if (object->IsJSGlobalProxy()) {
+ object = object->GetPrototype(isolate);
+ if (object->IsNull()) return isolate->heap()->undefined_value();
+ }
+ return object;
+}
+
+
+static MaybeObject* ArrayConstructorCommon(Isolate* isolate,
+ Handle<JSFunction> constructor,
+ Handle<Object> type_info,
+ Arguments* caller_args) {
+ bool holey = false;
+ bool can_use_type_feedback = true;
+ if (caller_args->length() == 1) {
+ Object* argument_one = (*caller_args)[0];
+ if (argument_one->IsSmi()) {
+ int value = Smi::cast(argument_one)->value();
+ if (value < 0 || value >= JSObject::kInitialMaxFastElementArray) {
+ // the array is a dictionary in this case.
+ can_use_type_feedback = false;
+ } else if (value != 0) {
+ holey = true;
+ }
+ } else {
+ // Non-smi length argument produces a dictionary
+ can_use_type_feedback = false;
+ }
+ }
+
+ JSArray* array;
+ MaybeObject* maybe_array;
+ if (!type_info.is_null() &&
+ *type_info != isolate->heap()->undefined_value() &&
+ Cell::cast(*type_info)->value()->IsAllocationSite() &&
+ can_use_type_feedback) {
+ Handle<Cell> cell = Handle<Cell>::cast(type_info);
+ Handle<AllocationSite> site = Handle<AllocationSite>(
+ AllocationSite::cast(cell->value()), isolate);
+ ASSERT(!site->IsLiteralSite());
+ ElementsKind to_kind = site->GetElementsKind();
+ if (holey && !IsFastHoleyElementsKind(to_kind)) {
+ to_kind = GetHoleyElementsKind(to_kind);
+ // Update the allocation site info to reflect the advice alteration.
+ site->SetElementsKind(to_kind);
+ }
+
+ maybe_array = isolate->heap()->AllocateJSObjectWithAllocationSite(
+ *constructor, site);
+ if (!maybe_array->To(&array)) return maybe_array;
+ } else {
+ maybe_array = isolate->heap()->AllocateJSObject(*constructor);
+ if (!maybe_array->To(&array)) return maybe_array;
+ // We might need to transition to holey
+ ElementsKind kind = constructor->initial_map()->elements_kind();
+ if (holey && !IsFastHoleyElementsKind(kind)) {
+ kind = GetHoleyElementsKind(kind);
+ maybe_array = array->TransitionElementsKind(kind);
+ if (maybe_array->IsFailure()) return maybe_array;
+ }
+ }
+
+ maybe_array = isolate->heap()->AllocateJSArrayStorage(array, 0, 0,
+ DONT_INITIALIZE_ARRAY_ELEMENTS);
+ if (maybe_array->IsFailure()) return maybe_array;
+ maybe_array = ArrayConstructInitializeElements(array, caller_args);
+ if (maybe_array->IsFailure()) return maybe_array;
+ return array;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayConstructor) {
+ HandleScope scope(isolate);
+ // If we get 2 arguments then they are the stub parameters (constructor, type
+ // info). If we get 3, then the first one is a pointer to the arguments
+ // passed by the caller.
+ Arguments empty_args(0, NULL);
+ bool no_caller_args = args.length() == 2;
+ ASSERT(no_caller_args || args.length() == 3);
+ int parameters_start = no_caller_args ? 0 : 1;
+ Arguments* caller_args = no_caller_args
+ ? &empty_args
+ : reinterpret_cast<Arguments*>(args[0]);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start);
+ CONVERT_ARG_HANDLE_CHECKED(Object, type_info, parameters_start + 1);
+
+ return ArrayConstructorCommon(isolate,
+ constructor,
+ type_info,
+ caller_args);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalArrayConstructor) {
+ HandleScope scope(isolate);
+ Arguments empty_args(0, NULL);
+ bool no_caller_args = args.length() == 1;
+ ASSERT(no_caller_args || args.length() == 2);
+ int parameters_start = no_caller_args ? 0 : 1;
+ Arguments* caller_args = no_caller_args
+ ? &empty_args
+ : reinterpret_cast<Arguments*>(args[0]);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start);
+
+ return ArrayConstructorCommon(isolate,
+ constructor,
+ Handle<Object>::null(),
+ caller_args);
+}
+
+
+// ----------------------------------------------------------------------------
+// Implementation of Runtime
+
+#define F(name, number_of_args, result_size) \
+ { Runtime::k##name, Runtime::RUNTIME, #name, \
+ FUNCTION_ADDR(Runtime_##name), number_of_args, result_size },
+
+
+#define I(name, number_of_args, result_size) \
+ { Runtime::kInline##name, Runtime::INLINE, \
+ "_" #name, NULL, number_of_args, result_size },
+
+static const Runtime::Function kIntrinsicFunctions[] = {
+ RUNTIME_FUNCTION_LIST(F)
+ INLINE_FUNCTION_LIST(I)
+ INLINE_RUNTIME_FUNCTION_LIST(I)
+};
+
+
+MaybeObject* Runtime::InitializeIntrinsicFunctionNames(Heap* heap,
+ Object* dictionary) {
+ ASSERT(Isolate::Current()->heap() == heap);
+ ASSERT(dictionary != NULL);
+ ASSERT(NameDictionary::cast(dictionary)->NumberOfElements() == 0);
+ for (int i = 0; i < kNumFunctions; ++i) {
+ Object* name_string;
+ { MaybeObject* maybe_name_string =
+ heap->InternalizeUtf8String(kIntrinsicFunctions[i].name);
+ if (!maybe_name_string->ToObject(&name_string)) return maybe_name_string;
+ }
+ NameDictionary* name_dictionary = NameDictionary::cast(dictionary);
+ { MaybeObject* maybe_dictionary = name_dictionary->Add(
+ String::cast(name_string),
+ Smi::FromInt(i),
+ PropertyDetails(NONE, NORMAL, Representation::None()));
+ if (!maybe_dictionary->ToObject(&dictionary)) {
+ // Non-recoverable failure. Calling code must restart heap
+ // initialization.
+ return maybe_dictionary;
+ }
+ }
+ }
+ return dictionary;
+}
+
+
+const Runtime::Function* Runtime::FunctionForName(Handle<String> name) {
+ Heap* heap = name->GetHeap();
+ int entry = heap->intrinsic_function_names()->FindEntry(*name);
+ if (entry != kNotFound) {
+ Object* smi_index = heap->intrinsic_function_names()->ValueAt(entry);
+ int function_index = Smi::cast(smi_index)->value();
+ return &(kIntrinsicFunctions[function_index]);
+ }
+ return NULL;
+}
+
+
+const Runtime::Function* Runtime::FunctionForId(Runtime::FunctionId id) {
+ return &(kIntrinsicFunctions[static_cast<int>(id)]);
+}
+
+
+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(),
+ "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(Heap::kNoGCFlags,
+ "Runtime::PerformGC");
+ }
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/runtime.h b/chromium/v8/src/runtime.h
new file mode 100644
index 00000000000..23e91f2ae9e
--- /dev/null
+++ b/chromium/v8/src/runtime.h
@@ -0,0 +1,845 @@
+// Copyright 2012 the V8 project authors. 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_RUNTIME_H_
+#define V8_RUNTIME_H_
+
+#include "allocation.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+// The interface to C++ runtime functions.
+
+// ----------------------------------------------------------------------------
+// RUNTIME_FUNCTION_LIST_ALWAYS defines runtime calls available in both
+// release and debug mode.
+// This macro should only be used by the macro RUNTIME_FUNCTION_LIST.
+
+// WARNING: RUNTIME_FUNCTION_LIST_ALWAYS_* is a very large macro that caused
+// MSVC Intellisense to crash. It was broken into two macros to work around
+// this problem. Please avoid large recursive macros whenever possible.
+#define RUNTIME_FUNCTION_LIST_ALWAYS_1(F) \
+ /* Property access */ \
+ F(GetProperty, 2, 1) \
+ F(KeyedGetProperty, 2, 1) \
+ F(DeleteProperty, 3, 1) \
+ F(HasLocalProperty, 2, 1) \
+ F(HasProperty, 2, 1) \
+ F(HasElement, 2, 1) \
+ F(IsPropertyEnumerable, 2, 1) \
+ F(GetPropertyNames, 1, 1) \
+ F(GetPropertyNamesFast, 1, 1) \
+ F(GetLocalPropertyNames, 2, 1) \
+ F(GetLocalElementNames, 1, 1) \
+ F(GetInterceptorInfo, 1, 1) \
+ F(GetNamedInterceptorPropertyNames, 1, 1) \
+ F(GetIndexedInterceptorElementNames, 1, 1) \
+ F(GetArgumentsProperty, 1, 1) \
+ F(ToFastProperties, 1, 1) \
+ F(FinishArrayPrototypeSetup, 1, 1) \
+ F(SpecialArrayFunctions, 1, 1) \
+ F(IsClassicModeFunction, 1, 1) \
+ F(GetDefaultReceiver, 1, 1) \
+ \
+ F(GetPrototype, 1, 1) \
+ F(SetPrototype, 2, 1) \
+ F(IsInPrototypeChain, 2, 1) \
+ \
+ F(GetOwnProperty, 2, 1) \
+ \
+ F(IsExtensible, 1, 1) \
+ F(PreventExtensions, 1, 1)\
+ \
+ /* Utilities */ \
+ F(CheckIsBootstrapping, 0, 1) \
+ F(GetRootNaN, 0, 1) \
+ F(Call, -1 /* >= 2 */, 1) \
+ F(Apply, 5, 1) \
+ F(GetFunctionDelegate, 1, 1) \
+ F(GetConstructorDelegate, 1, 1) \
+ F(NewArgumentsFast, 3, 1) \
+ F(NewStrictArgumentsFast, 3, 1) \
+ F(LazyCompile, 1, 1) \
+ F(LazyRecompile, 1, 1) \
+ F(ParallelRecompile, 1, 1) \
+ F(InstallRecompiledCode, 1, 1) \
+ F(NotifyDeoptimized, 1, 1) \
+ F(NotifyStubFailure, 0, 1) \
+ F(NotifyOSR, 0, 1) \
+ F(DeoptimizeFunction, 1, 1) \
+ F(ClearFunctionTypeFeedback, 1, 1) \
+ F(RunningInSimulator, 0, 1) \
+ F(IsParallelRecompilationSupported, 0, 1) \
+ F(OptimizeFunctionOnNextCall, -1, 1) \
+ F(NeverOptimizeFunction, 1, 1) \
+ F(GetOptimizationStatus, -1, 1) \
+ F(GetOptimizationCount, 1, 1) \
+ F(CompileForOnStackReplacement, 1, 1) \
+ F(AllocateInNewSpace, 1, 1) \
+ F(AllocateInOldPointerSpace, 1, 1) \
+ F(AllocateInOldDataSpace, 1, 1) \
+ F(SetNativeFlag, 1, 1) \
+ F(StoreArrayLiteralElement, 5, 1) \
+ F(DebugCallbackSupportsStepping, 1, 1) \
+ F(DebugPrepareStepInIfStepping, 1, 1) \
+ F(FlattenString, 1, 1) \
+ F(MigrateInstance, 1, 1) \
+ \
+ /* Array join support */ \
+ F(PushIfAbsent, 2, 1) \
+ F(ArrayConcat, 1, 1) \
+ \
+ /* Conversions */ \
+ F(ToBool, 1, 1) \
+ F(Typeof, 1, 1) \
+ \
+ F(StringToNumber, 1, 1) \
+ F(StringParseInt, 2, 1) \
+ F(StringParseFloat, 1, 1) \
+ F(StringToLowerCase, 1, 1) \
+ F(StringToUpperCase, 1, 1) \
+ F(StringSplit, 3, 1) \
+ F(CharFromCode, 1, 1) \
+ F(URIEscape, 1, 1) \
+ F(URIUnescape, 1, 1) \
+ \
+ F(NumberToString, 1, 1) \
+ F(NumberToStringSkipCache, 1, 1) \
+ F(NumberToInteger, 1, 1) \
+ F(NumberToPositiveInteger, 1, 1) \
+ F(NumberToIntegerMapMinusZero, 1, 1) \
+ F(NumberToJSUint32, 1, 1) \
+ F(NumberToJSInt32, 1, 1) \
+ F(NumberToSmi, 1, 1) \
+ F(AllocateHeapNumber, 0, 1) \
+ \
+ /* Arithmetic operations */ \
+ F(NumberAdd, 2, 1) \
+ F(NumberSub, 2, 1) \
+ F(NumberMul, 2, 1) \
+ F(NumberDiv, 2, 1) \
+ F(NumberMod, 2, 1) \
+ F(NumberUnaryMinus, 1, 1) \
+ F(NumberAlloc, 0, 1) \
+ F(NumberImul, 2, 1) \
+ \
+ F(StringAdd, 2, 1) \
+ F(StringBuilderConcat, 3, 1) \
+ F(StringBuilderJoin, 3, 1) \
+ F(SparseJoinWithSeparator, 3, 1) \
+ \
+ /* Bit operations */ \
+ F(NumberOr, 2, 1) \
+ F(NumberAnd, 2, 1) \
+ F(NumberXor, 2, 1) \
+ \
+ F(NumberShl, 2, 1) \
+ F(NumberShr, 2, 1) \
+ F(NumberSar, 2, 1) \
+ \
+ /* Comparisons */ \
+ F(NumberEquals, 2, 1) \
+ F(StringEquals, 2, 1) \
+ \
+ F(NumberCompare, 3, 1) \
+ F(SmiLexicographicCompare, 2, 1) \
+ F(StringCompare, 2, 1) \
+ \
+ /* Math */ \
+ F(Math_acos, 1, 1) \
+ F(Math_asin, 1, 1) \
+ F(Math_atan, 1, 1) \
+ F(Math_atan2, 2, 1) \
+ F(Math_ceil, 1, 1) \
+ F(Math_cos, 1, 1) \
+ F(Math_exp, 1, 1) \
+ F(Math_floor, 1, 1) \
+ F(Math_log, 1, 1) \
+ F(Math_pow, 2, 1) \
+ F(Math_pow_cfunction, 2, 1) \
+ F(RoundNumber, 1, 1) \
+ F(Math_sin, 1, 1) \
+ F(Math_sqrt, 1, 1) \
+ F(Math_tan, 1, 1) \
+ \
+ /* Regular expressions */ \
+ F(RegExpCompile, 3, 1) \
+ F(RegExpExec, 4, 1) \
+ F(RegExpExecMultiple, 4, 1) \
+ F(RegExpInitializeObject, 5, 1) \
+ F(RegExpConstructResult, 3, 1) \
+ \
+ /* JSON */ \
+ F(ParseJson, 1, 1) \
+ F(BasicJSONStringify, 1, 1) \
+ F(QuoteJSONString, 1, 1) \
+ \
+ /* Strings */ \
+ F(StringCharCodeAt, 2, 1) \
+ F(StringIndexOf, 3, 1) \
+ F(StringLastIndexOf, 3, 1) \
+ F(StringLocaleCompare, 2, 1) \
+ F(SubString, 3, 1) \
+ F(StringReplaceGlobalRegExpWithString, 4, 1) \
+ F(StringReplaceOneCharWithString, 3, 1) \
+ F(StringMatch, 3, 1) \
+ F(StringTrim, 3, 1) \
+ F(StringToArray, 2, 1) \
+ F(NewStringWrapper, 1, 1) \
+ F(NewString, 2, 1) \
+ F(TruncateString, 2, 1) \
+ \
+ /* Numbers */ \
+ F(NumberToRadixString, 2, 1) \
+ F(NumberToFixed, 2, 1) \
+ F(NumberToExponential, 2, 1) \
+ F(NumberToPrecision, 2, 1)
+
+
+#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \
+ /* Reflection */ \
+ F(FunctionSetInstanceClassName, 2, 1) \
+ F(FunctionSetLength, 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(FunctionIsGenerator, 1, 1) \
+ F(FunctionBindArguments, 4, 1) \
+ F(BoundFunctionGetBindings, 1, 1) \
+ F(FunctionRemovePrototype, 1, 1) \
+ F(FunctionGetSourceCode, 1, 1) \
+ F(FunctionGetScript, 1, 1) \
+ F(FunctionGetScriptSourcePosition, 1, 1) \
+ F(FunctionGetPositionForOffset, 2, 1) \
+ F(FunctionIsAPIFunction, 1, 1) \
+ F(FunctionIsBuiltin, 1, 1) \
+ F(GetScript, 1, 1) \
+ F(CollectStackTrace, 3, 1) \
+ F(GetAndClearOverflowedStackTrace, 1, 1) \
+ F(GetV8Version, 0, 1) \
+ \
+ F(ClassOf, 1, 1) \
+ F(SetCode, 2, 1) \
+ F(SetExpectedNumberOfProperties, 2, 1) \
+ \
+ F(CreateApiFunction, 1, 1) \
+ F(IsTemplate, 1, 1) \
+ F(GetTemplateField, 2, 1) \
+ F(DisableAccessChecks, 1, 1) \
+ F(EnableAccessChecks, 1, 1) \
+ \
+ /* Dates */ \
+ F(DateCurrentTime, 0, 1) \
+ F(DateParseString, 2, 1) \
+ F(DateLocalTimezone, 1, 1) \
+ F(DateToUTC, 1, 1) \
+ F(DateMakeDay, 2, 1) \
+ F(DateSetValue, 3, 1) \
+ \
+ /* Numbers */ \
+ \
+ /* Globals */ \
+ F(CompileString, 2, 1) \
+ F(GlobalPrint, 1, 1) \
+ \
+ /* Eval */ \
+ F(GlobalReceiver, 1, 1) \
+ F(ResolvePossiblyDirectEval, 5, 2) \
+ \
+ F(SetProperty, -1 /* 4 or 5 */, 1) \
+ F(DefineOrRedefineDataProperty, 4, 1) \
+ F(DefineOrRedefineAccessorProperty, 5, 1) \
+ F(IgnoreAttributesAndSetProperty, -1 /* 3 or 4 */, 1) \
+ F(GetDataProperty, 2, 1) \
+ \
+ /* Arrays */ \
+ F(RemoveArrayHoles, 2, 1) \
+ F(GetArrayKeys, 2, 1) \
+ F(MoveArrayContents, 2, 1) \
+ F(EstimateNumberOfElements, 1, 1) \
+ F(ArrayConstructor, -1, 1) \
+ F(InternalArrayConstructor, -1, 1) \
+ \
+ /* Getters and Setters */ \
+ F(LookupAccessor, 3, 1) \
+ \
+ /* Literals */ \
+ F(MaterializeRegExpLiteral, 4, 1)\
+ F(CreateObjectLiteral, 4, 1) \
+ F(CreateObjectLiteralShallow, 4, 1) \
+ F(CreateArrayLiteral, 3, 1) \
+ F(CreateArrayLiteralShallow, 3, 1) \
+ \
+ /* Harmony generators */ \
+ F(CreateJSGeneratorObject, 0, 1) \
+ F(SuspendJSGeneratorObject, 1, 1) \
+ F(ResumeJSGeneratorObject, 3, 1) \
+ F(ThrowGeneratorStateError, 1, 1) \
+ \
+ /* ES5 */ \
+ F(ObjectFreeze, 1, 1) \
+ \
+ /* Harmony modules */ \
+ F(IsJSModule, 1, 1) \
+ \
+ /* Harmony symbols */ \
+ F(CreateSymbol, 1, 1) \
+ F(SymbolName, 1, 1) \
+ \
+ /* Harmony proxies */ \
+ F(CreateJSProxy, 2, 1) \
+ F(CreateJSFunctionProxy, 4, 1) \
+ F(IsJSProxy, 1, 1) \
+ F(IsJSFunctionProxy, 1, 1) \
+ F(GetHandler, 1, 1) \
+ F(GetCallTrap, 1, 1) \
+ 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) \
+ F(SetGetSize, 1, 1) \
+ \
+ /* Harmony maps */ \
+ F(MapInitialize, 1, 1) \
+ F(MapGet, 2, 1) \
+ F(MapHas, 2, 1) \
+ F(MapDelete, 2, 1) \
+ F(MapSet, 3, 1) \
+ F(MapGetSize, 1, 1) \
+ \
+ /* Harmony weak maps and sets */ \
+ F(WeakCollectionInitialize, 1, 1) \
+ F(WeakCollectionGet, 2, 1) \
+ F(WeakCollectionHas, 2, 1) \
+ F(WeakCollectionDelete, 2, 1) \
+ F(WeakCollectionSet, 3, 1) \
+ \
+ /* Harmony observe */ \
+ F(IsObserved, 1, 1) \
+ F(SetIsObserved, 1, 1) \
+ F(SetObserverDeliveryPending, 0, 1) \
+ F(GetObservationState, 0, 1) \
+ F(ObservationWeakMapCreate, 0, 1) \
+ F(UnwrapGlobalProxy, 1, 1) \
+ \
+ /* Harmony typed arrays */ \
+ F(ArrayBufferInitialize, 2, 1)\
+ F(ArrayBufferGetByteLength, 1, 1)\
+ F(ArrayBufferSliceImpl, 3, 1) \
+ \
+ F(TypedArrayInitialize, 5, 1) \
+ F(TypedArrayInitializeFromArrayLike, 4, 1) \
+ F(TypedArrayGetBuffer, 1, 1) \
+ F(TypedArrayGetByteLength, 1, 1) \
+ F(TypedArrayGetByteOffset, 1, 1) \
+ F(TypedArrayGetLength, 1, 1) \
+ F(TypedArraySetFastCases, 3, 1) \
+ \
+ F(DataViewInitialize, 4, 1) \
+ F(DataViewGetBuffer, 1, 1) \
+ F(DataViewGetByteLength, 1, 1) \
+ F(DataViewGetByteOffset, 1, 1) \
+ F(DataViewGetInt8, 3, 1) \
+ F(DataViewGetUint8, 3, 1) \
+ F(DataViewGetInt16, 3, 1) \
+ F(DataViewGetUint16, 3, 1) \
+ F(DataViewGetInt32, 3, 1) \
+ F(DataViewGetUint32, 3, 1) \
+ F(DataViewGetFloat32, 3, 1) \
+ F(DataViewGetFloat64, 3, 1) \
+ \
+ F(DataViewSetInt8, 4, 1) \
+ F(DataViewSetUint8, 4, 1) \
+ F(DataViewSetInt16, 4, 1) \
+ F(DataViewSetUint16, 4, 1) \
+ F(DataViewSetInt32, 4, 1) \
+ F(DataViewSetUint32, 4, 1) \
+ F(DataViewSetFloat32, 4, 1) \
+ F(DataViewSetFloat64, 4, 1) \
+ \
+ /* Statements */ \
+ F(NewClosure, 3, 1) \
+ F(NewObject, 1, 1) \
+ F(NewObjectFromBound, 1, 1) \
+ F(FinalizeInstanceSize, 1, 1) \
+ F(Throw, 1, 1) \
+ F(ReThrow, 1, 1) \
+ F(ThrowReferenceError, 1, 1) \
+ F(ThrowNotDateError, 0, 1) \
+ F(StackGuard, 0, 1) \
+ F(Interrupt, 0, 1) \
+ F(PromoteScheduledException, 0, 1) \
+ \
+ /* Contexts */ \
+ F(NewGlobalContext, 2, 1) \
+ F(NewFunctionContext, 1, 1) \
+ F(PushWithContext, 2, 1) \
+ F(PushCatchContext, 3, 1) \
+ F(PushBlockContext, 2, 1) \
+ F(PushModuleContext, 2, 1) \
+ F(DeleteContextSlot, 2, 1) \
+ F(LoadContextSlot, 2, 2) \
+ F(LoadContextSlotNoReferenceError, 2, 2) \
+ F(StoreContextSlot, 4, 1) \
+ \
+ /* Declarations and initialization */ \
+ F(DeclareGlobals, 3, 1) \
+ F(DeclareModules, 1, 1) \
+ F(DeclareContextSlot, 4, 1) \
+ F(InitializeVarGlobal, -1 /* 2 or 3 */, 1) \
+ F(InitializeConstGlobal, 2, 1) \
+ F(InitializeConstContextSlot, 3, 1) \
+ F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
+ \
+ /* Debugging */ \
+ F(DebugPrint, 1, 1) \
+ F(DebugTrace, 0, 1) \
+ F(TraceEnter, 0, 1) \
+ F(TraceExit, 1, 1) \
+ F(Abort, 2, 1) \
+ /* Logging */ \
+ F(Log, 2, 1) \
+ /* ES5 */ \
+ F(LocalKeys, 1, 1) \
+ /* Cache suport */ \
+ F(GetFromCache, 2, 1) \
+ \
+ /* Message objects */ \
+ F(MessageGetStartPosition, 1, 1) \
+ F(MessageGetScript, 1, 1) \
+ \
+ /* Pseudo functions - handled as macros by parser */ \
+ F(IS_VAR, 1, 1) \
+ \
+ /* expose boolean functions from objects-inl.h */ \
+ F(HasFastSmiElements, 1, 1) \
+ F(HasFastSmiOrObjectElements, 1, 1) \
+ F(HasFastObjectElements, 1, 1) \
+ F(HasFastDoubleElements, 1, 1) \
+ F(HasFastHoleyElements, 1, 1) \
+ F(HasDictionaryElements, 1, 1) \
+ F(HasNonStrictArgumentsElements, 1, 1) \
+ F(HasExternalPixelElements, 1, 1) \
+ F(HasExternalArrayElements, 1, 1) \
+ F(HasExternalByteElements, 1, 1) \
+ F(HasExternalUnsignedByteElements, 1, 1) \
+ F(HasExternalShortElements, 1, 1) \
+ F(HasExternalUnsignedShortElements, 1, 1) \
+ F(HasExternalIntElements, 1, 1) \
+ F(HasExternalUnsignedIntElements, 1, 1) \
+ F(HasExternalFloatElements, 1, 1) \
+ F(HasExternalDoubleElements, 1, 1) \
+ F(HasFastProperties, 1, 1) \
+ F(TransitionElementsKind, 2, 1) \
+ F(HaveSameMap, 2, 1)
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) \
+ /* Debugger support*/ \
+ F(DebugBreak, 0, 1) \
+ F(SetDebugEventListener, 2, 1) \
+ F(Break, 0, 1) \
+ F(DebugGetPropertyDetails, 2, 1) \
+ F(DebugGetProperty, 2, 1) \
+ F(DebugPropertyTypeFromDetails, 1, 1) \
+ F(DebugPropertyAttributesFromDetails, 1, 1) \
+ F(DebugPropertyIndexFromDetails, 1, 1) \
+ F(DebugNamedInterceptorPropertyValue, 2, 1) \
+ F(DebugIndexedInterceptorElementValue, 2, 1) \
+ F(CheckExecutionState, 1, 1) \
+ F(GetFrameCount, 1, 1) \
+ F(GetFrameDetails, 2, 1) \
+ F(GetScopeCount, 2, 1) \
+ F(GetStepInPositions, 2, 1) \
+ F(GetScopeDetails, 4, 1) \
+ F(GetFunctionScopeCount, 1, 1) \
+ F(GetFunctionScopeDetails, 2, 1) \
+ F(SetScopeVariableValue, 6, 1) \
+ F(DebugPrintScopes, 0, 1) \
+ F(GetThreadCount, 1, 1) \
+ F(GetThreadDetails, 2, 1) \
+ F(SetDisableBreak, 1, 1) \
+ F(GetBreakLocations, 2, 1) \
+ F(SetFunctionBreakPoint, 3, 1) \
+ F(SetScriptBreakPoint, 4, 1) \
+ F(ClearBreakPoint, 1, 1) \
+ F(ChangeBreakOnException, 2, 1) \
+ F(IsBreakOnException, 1, 1) \
+ F(PrepareStep, 3, 1) \
+ F(ClearStepping, 0, 1) \
+ F(DebugEvaluate, 6, 1) \
+ F(DebugEvaluateGlobal, 4, 1) \
+ F(DebugGetLoadedScripts, 0, 1) \
+ F(DebugReferencedBy, 3, 1) \
+ F(DebugConstructedBy, 2, 1) \
+ F(DebugGetPrototype, 1, 1) \
+ F(DebugSetScriptSource, 2, 1) \
+ F(SystemBreak, 0, 1) \
+ F(DebugDisassembleFunction, 1, 1) \
+ F(DebugDisassembleConstructor, 1, 1) \
+ F(FunctionGetInferredName, 1, 1) \
+ F(LiveEditFindSharedFunctionInfosForScript, 1, 1) \
+ F(LiveEditGatherCompileInfo, 2, 1) \
+ F(LiveEditReplaceScript, 3, 1) \
+ F(LiveEditReplaceFunctionCode, 2, 1) \
+ F(LiveEditFunctionSourceUpdated, 1, 1) \
+ F(LiveEditFunctionSetScript, 2, 1) \
+ F(LiveEditReplaceRefToNestedFunction, 3, 1) \
+ F(LiveEditPatchFunctionPositions, 2, 1) \
+ F(LiveEditCheckAndDropActivations, 2, 1) \
+ F(LiveEditCompareStrings, 2, 1) \
+ F(LiveEditRestartFrame, 2, 1) \
+ F(GetFunctionCodePositionFromSource, 2, 1) \
+ F(ExecuteInDebugContext, 2, 1) \
+ \
+ F(SetFlags, 1, 1) \
+ F(CollectGarbage, 1, 1) \
+ F(GetHeapUsage, 0, 1) \
+
+#else
+#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
+#endif
+
+
+#ifdef V8_I18N_SUPPORT
+#define RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F) \
+ /* i18n support */ \
+ /* Standalone, helper methods. */ \
+ F(CanonicalizeLanguageTag, 1, 1) \
+ F(AvailableLocalesOf, 1, 1) \
+ F(GetDefaultICULocale, 0, 1) \
+ F(GetLanguageTagVariants, 1, 1) \
+ \
+ /* Date format and parse. */ \
+ F(CreateDateTimeFormat, 3, 1) \
+ F(InternalDateFormat, 2, 1) \
+ F(InternalDateParse, 2, 1) \
+ \
+ /* Number format and parse. */ \
+ F(CreateNumberFormat, 3, 1) \
+ F(InternalNumberFormat, 2, 1) \
+ F(InternalNumberParse, 2, 1) \
+ \
+ /* Collator. */ \
+ F(CreateCollator, 3, 1) \
+ F(InternalCompare, 3, 1) \
+
+#else
+#define RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F)
+#endif
+
+
+#ifdef DEBUG
+#define RUNTIME_FUNCTION_LIST_DEBUG(F) \
+ /* Testing */ \
+ F(ListNatives, 0, 1)
+#else
+#define RUNTIME_FUNCTION_LIST_DEBUG(F)
+#endif
+
+// ----------------------------------------------------------------------------
+// RUNTIME_FUNCTION_LIST defines all runtime functions accessed
+// either directly by id (via the code generator), or indirectly
+// via a native call by name (from within JS code).
+
+#define RUNTIME_FUNCTION_LIST(F) \
+ RUNTIME_FUNCTION_LIST_ALWAYS_1(F) \
+ RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \
+ RUNTIME_FUNCTION_LIST_DEBUG(F) \
+ RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) \
+ RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F)
+
+// ----------------------------------------------------------------------------
+// INLINE_FUNCTION_LIST defines all inlined functions accessed
+// with a native call of the form %_name from within JS code.
+// Entries have the form F(name, number of arguments, number of return values).
+#define INLINE_FUNCTION_LIST(F) \
+ F(IsSmi, 1, 1) \
+ F(IsNonNegativeSmi, 1, 1) \
+ F(IsArray, 1, 1) \
+ F(IsRegExp, 1, 1) \
+ F(IsConstructCall, 0, 1) \
+ F(CallFunction, -1 /* receiver + n args + function */, 1) \
+ F(ArgumentsLength, 0, 1) \
+ F(Arguments, 1, 1) \
+ F(ValueOf, 1, 1) \
+ F(SetValueOf, 2, 1) \
+ F(DateField, 2 /* date object, field index */, 1) \
+ F(StringCharFromCode, 1, 1) \
+ F(StringCharAt, 2, 1) \
+ F(OneByteSeqStringSetChar, 3, 1) \
+ F(TwoByteSeqStringSetChar, 3, 1) \
+ F(ObjectEquals, 2, 1) \
+ F(RandomHeapNumber, 0, 1) \
+ F(IsObject, 1, 1) \
+ F(IsFunction, 1, 1) \
+ F(IsUndetectableObject, 1, 1) \
+ F(IsSpecObject, 1, 1) \
+ F(IsStringWrapperSafeForDefaultValueOf, 1, 1) \
+ 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) \
+ F(HasCachedArrayIndex, 1, 1) \
+ F(GetCachedArrayIndex, 1, 1) \
+ F(FastAsciiArrayJoin, 2, 1) \
+ F(GeneratorNext, 2, 1) \
+ F(GeneratorThrow, 2, 1) \
+ F(DebugBreakInOptimizedCode, 0, 1)
+
+
+// ----------------------------------------------------------------------------
+// INLINE_RUNTIME_FUNCTION_LIST defines all inlined functions accessed
+// with a native call of the form %_name from within JS code that also have
+// a corresponding runtime function, that is called for slow cases.
+// Entries have the form F(name, number of arguments, number of return values).
+#define INLINE_RUNTIME_FUNCTION_LIST(F) \
+ F(ClassOf, 1, 1) \
+ F(StringCharCodeAt, 2, 1) \
+ F(Log, 3, 1) \
+ F(StringAdd, 2, 1) \
+ F(SubString, 3, 1) \
+ F(StringCompare, 2, 1) \
+ F(RegExpExec, 4, 1) \
+ F(RegExpConstructResult, 3, 1) \
+ F(GetFromCache, 2, 1) \
+ F(NumberToString, 1, 1)
+
+
+//---------------------------------------------------------------------------
+// Runtime provides access to all C++ runtime functions.
+
+class RuntimeState {
+ public:
+ StaticResource<ConsStringIteratorOp>* string_iterator() {
+ return &string_iterator_;
+ }
+ unibrow::Mapping<unibrow::ToUppercase, 128>* to_upper_mapping() {
+ return &to_upper_mapping_;
+ }
+ unibrow::Mapping<unibrow::ToLowercase, 128>* to_lower_mapping() {
+ return &to_lower_mapping_;
+ }
+ ConsStringIteratorOp* string_iterator_compare_x() {
+ return &string_iterator_compare_x_;
+ }
+ ConsStringIteratorOp* string_iterator_compare_y() {
+ return &string_iterator_compare_y_;
+ }
+ ConsStringIteratorOp* string_locale_compare_it1() {
+ return &string_locale_compare_it1_;
+ }
+ ConsStringIteratorOp* string_locale_compare_it2() {
+ return &string_locale_compare_it2_;
+ }
+
+ private:
+ RuntimeState() {}
+ // Non-reentrant string buffer for efficient general use in the runtime.
+ StaticResource<ConsStringIteratorOp> string_iterator_;
+ unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping_;
+ unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping_;
+ ConsStringIteratorOp string_iterator_compare_x_;
+ ConsStringIteratorOp string_iterator_compare_y_;
+ ConsStringIteratorOp string_locale_compare_it1_;
+ ConsStringIteratorOp string_locale_compare_it2_;
+
+ friend class Isolate;
+ friend class Runtime;
+
+ DISALLOW_COPY_AND_ASSIGN(RuntimeState);
+};
+
+
+class Runtime : public AllStatic {
+ public:
+ enum FunctionId {
+#define F(name, nargs, ressize) k##name,
+ RUNTIME_FUNCTION_LIST(F)
+#undef F
+#define F(name, nargs, ressize) kInline##name,
+ INLINE_FUNCTION_LIST(F)
+ INLINE_RUNTIME_FUNCTION_LIST(F)
+#undef F
+ kNumFunctions,
+ kFirstInlineFunction = kInlineIsSmi
+ };
+
+ enum IntrinsicType {
+ RUNTIME,
+ INLINE
+ };
+
+ // Intrinsic function descriptor.
+ struct Function {
+ FunctionId function_id;
+ IntrinsicType intrinsic_type;
+ // The JS name of the function.
+ const char* name;
+
+ // The C++ (native) entry point. NULL if the function is inlined.
+ byte* entry;
+
+ // The number of arguments expected. nargs is -1 if the function takes
+ // a variable number of arguments.
+ int nargs;
+ // Size of result. Most functions return a single pointer, size 1.
+ int result_size;
+ };
+
+ static const int kNotFound = -1;
+
+ // Add internalized strings for all the intrinsic function names to a
+ // StringDictionary.
+ // Returns failure if an allocation fails. In this case, it must be
+ // retried with a new, empty StringDictionary, not with the same one.
+ // Alternatively, heap initialization can be completely restarted.
+ MUST_USE_RESULT static MaybeObject* InitializeIntrinsicFunctionNames(
+ Heap* heap, Object* dictionary);
+
+ // Get the intrinsic function with the given name, which must be internalized.
+ static const Function* FunctionForName(Handle<String> name);
+
+ // Get the intrinsic function with the given FunctionId.
+ static const Function* FunctionForId(FunctionId id);
+
+ // General-purpose helper functions for runtime system.
+ static int StringMatch(Isolate* isolate,
+ Handle<String> sub,
+ Handle<String> pat,
+ int index);
+
+ static bool IsUpperCaseChar(RuntimeState* runtime_state, uint16_t ch);
+
+ // 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* GetElementOrCharAtOrFail(
+ Isolate* isolate,
+ Handle<Object> object,
+ uint32_t index);
+
+ MUST_USE_RESULT static MaybeObject* SetObjectProperty(
+ Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr,
+ StrictModeFlag strict_mode);
+
+ MUST_USE_RESULT static MaybeObject* SetObjectPropertyOrFail(
+ Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr,
+ StrictModeFlag strict_mode);
+
+ MUST_USE_RESULT static MaybeObject* ForceSetObjectProperty(
+ Isolate* isolate,
+ Handle<JSObject> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr);
+
+ MUST_USE_RESULT static MaybeObject* DeleteObjectProperty(
+ Isolate* isolate,
+ Handle<JSReceiver> object,
+ Handle<Object> key,
+ JSReceiver::DeleteMode mode);
+
+ MUST_USE_RESULT static MaybeObject* HasObjectProperty(
+ Isolate* isolate,
+ Handle<JSReceiver> object,
+ Handle<Object> key);
+
+ MUST_USE_RESULT static MaybeObject* GetObjectProperty(
+ Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key);
+
+ MUST_USE_RESULT static MaybeObject* GetObjectPropertyOrFail(
+ Isolate* isolate,
+ Handle<Object> object,
+ Handle<Object> key);
+
+ static void SetupArrayBuffer(Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ bool is_external,
+ void* data,
+ size_t allocated_length);
+
+ static bool SetupArrayBufferAllocatingData(
+ Isolate* isolate,
+ Handle<JSArrayBuffer> array_buffer,
+ size_t allocated_length,
+ bool initialize = true);
+
+ static void FreeArrayBuffer(
+ Isolate* isolate,
+ JSArrayBuffer* phantom_array_buffer);
+
+ // 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.
+
+class DeclareGlobalsEvalFlag: public BitField<bool, 0, 1> {};
+class DeclareGlobalsNativeFlag: public BitField<bool, 1, 1> {};
+class DeclareGlobalsLanguageMode: public BitField<LanguageMode, 2, 2> {};
+
+} } // namespace v8::internal
+
+#endif // V8_RUNTIME_H_
diff --git a/chromium/v8/src/runtime.js b/chromium/v8/src/runtime.js
new file mode 100644
index 00000000000..5339570ef6e
--- /dev/null
+++ b/chromium/v8/src/runtime.js
@@ -0,0 +1,676 @@
+// 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.
+
+// This files contains runtime support implemented in JavaScript.
+
+// CAUTION: Some of the functions specified in this file are called
+// directly from compiled code. These are the functions with names in
+// ALL CAPS. The compiled code passes the first argument in 'this' and
+// it does not push the function onto the stack. This means that you
+// cannot use contexts in all these functions.
+
+
+/* -----------------------------------
+ - - - C o m p a r i s o n - - -
+ -----------------------------------
+*/
+
+// The following declarations are shared with other native JS files.
+// They are all declared at this one spot to avoid redeclaration errors.
+var $Object = global.Object;
+var $Array = global.Array;
+var $String = global.String;
+var $Number = global.Number;
+var $Function = global.Function;
+var $Boolean = global.Boolean;
+var $NaN = %GetRootNaN();
+var builtins = this;
+
+// ECMA-262 Section 11.9.3.
+function EQUALS(y) {
+ if (IS_STRING(this) && IS_STRING(y)) return %StringEquals(this, y);
+ var x = this;
+
+ while (true) {
+ if (IS_NUMBER(x)) {
+ while (true) {
+ if (IS_NUMBER(y)) return %NumberEquals(x, y);
+ if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal
+ if (!IS_SPEC_OBJECT(y)) {
+ // String or boolean.
+ return %NumberEquals(x, %ToNumber(y));
+ }
+ y = %ToPrimitive(y, NO_HINT);
+ }
+ } else if (IS_STRING(x)) {
+ while (true) {
+ if (IS_STRING(y)) return %StringEquals(x, y);
+ if (IS_SYMBOL(y)) return 1; // not equal
+ if (IS_NUMBER(y)) return %NumberEquals(%ToNumber(x), y);
+ if (IS_BOOLEAN(y)) return %NumberEquals(%ToNumber(x), %ToNumber(y));
+ if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal
+ y = %ToPrimitive(y, NO_HINT);
+ }
+ } else if (IS_SYMBOL(x)) {
+ while (true) {
+ if (IS_SYMBOL(y)) return %_ObjectEquals(x, y) ? 0 : 1;
+ if (!IS_SPEC_OBJECT(y)) return 1; // not equal
+ y = %ToPrimitive(y, NO_HINT);
+ }
+ } else if (IS_BOOLEAN(x)) {
+ if (IS_BOOLEAN(y)) return %_ObjectEquals(x, y) ? 0 : 1;
+ if (IS_NULL_OR_UNDEFINED(y)) return 1;
+ if (IS_NUMBER(y)) return %NumberEquals(%ToNumber(x), y);
+ if (IS_STRING(y)) return %NumberEquals(%ToNumber(x), %ToNumber(y));
+ if (IS_SYMBOL(y)) return 1; // not equal
+ // y is object.
+ x = %ToNumber(x);
+ y = %ToPrimitive(y, NO_HINT);
+ } else if (IS_NULL_OR_UNDEFINED(x)) {
+ return IS_NULL_OR_UNDEFINED(y) ? 0 : 1;
+ } else {
+ // x is an object.
+ if (IS_SPEC_OBJECT(y)) {
+ return %_ObjectEquals(x, y) ? 0 : 1;
+ }
+ if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal
+ if (IS_BOOLEAN(y)) y = %ToNumber(y);
+ x = %ToPrimitive(x, NO_HINT);
+ }
+ }
+}
+
+// ECMA-262, section 11.9.4, page 56.
+function STRICT_EQUALS(x) {
+ if (IS_STRING(this)) {
+ if (!IS_STRING(x)) return 1; // not equal
+ return %StringEquals(this, x);
+ }
+
+ if (IS_NUMBER(this)) {
+ if (!IS_NUMBER(x)) return 1; // not equal
+ return %NumberEquals(this, x);
+ }
+
+ // If anything else gets here, we just do simple identity check.
+ // Objects (including functions), null, undefined and booleans were
+ // checked in the CompareStub, so there should be nothing left.
+ return %_ObjectEquals(this, x) ? 0 : 1;
+}
+
+
+// ECMA-262, section 11.8.5, page 53. The 'ncr' parameter is used as
+// the result when either (or both) the operands are NaN.
+function COMPARE(x, ncr) {
+ var left;
+ var right;
+ // Fast cases for string, numbers and undefined compares.
+ if (IS_STRING(this)) {
+ if (IS_STRING(x)) return %_StringCompare(this, x);
+ if (IS_UNDEFINED(x)) return ncr;
+ left = this;
+ } else if (IS_NUMBER(this)) {
+ if (IS_NUMBER(x)) return %NumberCompare(this, x, ncr);
+ if (IS_UNDEFINED(x)) return ncr;
+ left = this;
+ } else if (IS_UNDEFINED(this)) {
+ if (!IS_UNDEFINED(x)) {
+ %ToPrimitive(x, NUMBER_HINT);
+ }
+ return ncr;
+ } else if (IS_UNDEFINED(x)) {
+ %ToPrimitive(this, NUMBER_HINT);
+ return ncr;
+ } else {
+ left = %ToPrimitive(this, NUMBER_HINT);
+ }
+
+ right = %ToPrimitive(x, NUMBER_HINT);
+ if (IS_STRING(left) && IS_STRING(right)) {
+ return %_StringCompare(left, right);
+ } else {
+ var left_number = %ToNumber(left);
+ var right_number = %ToNumber(right);
+ if (NUMBER_IS_NAN(left_number) || NUMBER_IS_NAN(right_number)) return ncr;
+ return %NumberCompare(left_number, right_number, ncr);
+ }
+}
+
+
+
+/* -----------------------------------
+ - - - A r i t h m e t i c - - -
+ -----------------------------------
+*/
+
+// ECMA-262, section 11.6.1, page 50.
+function ADD(x) {
+ // Fast case: Check for number operands and do the addition.
+ if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x);
+ if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x);
+
+ // Default implementation.
+ var a = %ToPrimitive(this, NO_HINT);
+ var b = %ToPrimitive(x, NO_HINT);
+
+ if (IS_STRING(a)) {
+ return %_StringAdd(a, %ToString(b));
+ } else if (IS_STRING(b)) {
+ return %_StringAdd(%NonStringToString(a), b);
+ } else {
+ return %NumberAdd(%ToNumber(a), %ToNumber(b));
+ }
+}
+
+
+// Left operand (this) is already a string.
+function STRING_ADD_LEFT(y) {
+ if (!IS_STRING(y)) {
+ if (IS_STRING_WRAPPER(y) && %_IsStringWrapperSafeForDefaultValueOf(y)) {
+ y = %_ValueOf(y);
+ } else {
+ y = IS_NUMBER(y)
+ ? %_NumberToString(y)
+ : %ToString(%ToPrimitive(y, NO_HINT));
+ }
+ }
+ return %_StringAdd(this, y);
+}
+
+
+// Right operand (y) is already a string.
+function STRING_ADD_RIGHT(y) {
+ var x = this;
+ if (!IS_STRING(x)) {
+ if (IS_STRING_WRAPPER(x) && %_IsStringWrapperSafeForDefaultValueOf(x)) {
+ x = %_ValueOf(x);
+ } else {
+ x = IS_NUMBER(x)
+ ? %_NumberToString(x)
+ : %ToString(%ToPrimitive(x, NO_HINT));
+ }
+ }
+ return %_StringAdd(x, y);
+}
+
+
+// ECMA-262, section 11.6.2, page 50.
+function SUB(y) {
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ return %NumberSub(x, y);
+}
+
+
+// ECMA-262, section 11.5.1, page 48.
+function MUL(y) {
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ return %NumberMul(x, y);
+}
+
+
+// ECMA-262, section 11.5.2, page 49.
+function DIV(y) {
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ return %NumberDiv(x, y);
+}
+
+
+// ECMA-262, section 11.5.3, page 49.
+function MOD(y) {
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ return %NumberMod(x, y);
+}
+
+
+
+/* -------------------------------------------
+ - - - B i t o p e r a t i o n s - - -
+ -------------------------------------------
+*/
+
+// ECMA-262, section 11.10, page 57.
+function BIT_OR(y) {
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ return %NumberOr(x, y);
+}
+
+
+// ECMA-262, section 11.10, page 57.
+function BIT_AND(y) {
+ var x;
+ if (IS_NUMBER(this)) {
+ x = this;
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ } else {
+ x = %NonNumberToNumber(this);
+ // Make sure to convert the right operand to a number before
+ // bailing out in the fast case, but after converting the
+ // left operand. This ensures that valueOf methods on the right
+ // operand are always executed.
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ // Optimize for the case where we end up AND'ing a value
+ // that doesn't convert to a number. This is common in
+ // certain benchmarks.
+ if (NUMBER_IS_NAN(x)) return 0;
+ }
+ return %NumberAnd(x, y);
+}
+
+
+// ECMA-262, section 11.10, page 57.
+function BIT_XOR(y) {
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ return %NumberXor(x, y);
+}
+
+
+// ECMA-262, section 11.7.1, page 51.
+function SHL(y) {
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ return %NumberShl(x, y);
+}
+
+
+// ECMA-262, section 11.7.2, page 51.
+function SAR(y) {
+ var x;
+ if (IS_NUMBER(this)) {
+ x = this;
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ } else {
+ x = %NonNumberToNumber(this);
+ // Make sure to convert the right operand to a number before
+ // bailing out in the fast case, but after converting the
+ // left operand. This ensures that valueOf methods on the right
+ // operand are always executed.
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ // Optimize for the case where we end up shifting a value
+ // that doesn't convert to a number. This is common in
+ // certain benchmarks.
+ if (NUMBER_IS_NAN(x)) return 0;
+ }
+ return %NumberSar(x, y);
+}
+
+
+// ECMA-262, section 11.7.3, page 52.
+function SHR(y) {
+ var x = IS_NUMBER(this) ? this : %NonNumberToNumber(this);
+ if (!IS_NUMBER(y)) y = %NonNumberToNumber(y);
+ return %NumberShr(x, y);
+}
+
+
+
+/* -----------------------------
+ - - - H e l p e r s - - -
+ -----------------------------
+*/
+
+// ECMA-262, section 11.4.1, page 46.
+function DELETE(key, strict) {
+ return %DeleteProperty(%ToObject(this), %ToName(key), strict);
+}
+
+
+// ECMA-262, section 11.8.7, page 54.
+function IN(x) {
+ if (!IS_SPEC_OBJECT(x)) {
+ throw %MakeTypeError('invalid_in_operator_use', [this, x]);
+ }
+ return %_IsNonNegativeSmi(this) ?
+ %HasElement(x, this) : %HasProperty(x, %ToName(this));
+}
+
+
+// ECMA-262, section 11.8.6, page 54. To make the implementation more
+// efficient, the return value should be zero if the 'this' is an
+// instance of F, and non-zero if not. This makes it possible to avoid
+// an expensive ToBoolean conversion in the generated code.
+function INSTANCE_OF(F) {
+ var V = this;
+ if (!IS_SPEC_FUNCTION(F)) {
+ throw %MakeTypeError('instanceof_function_expected', [V]);
+ }
+
+ // If V is not an object, return false.
+ if (!IS_SPEC_OBJECT(V)) {
+ 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)) {
+ throw %MakeTypeError('instanceof_nonobject_proto', [O]);
+ }
+
+ // Return whether or not O is in the prototype chain of V.
+ return %IsInPrototypeChain(O, V) ? 0 : 1;
+}
+
+
+// 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.
+function FILTER_KEY(key) {
+ var string = %ToName(key);
+ if (%HasProperty(this, string)) return string;
+ return 0;
+}
+
+
+function CALL_NON_FUNCTION() {
+ var delegate = %GetFunctionDelegate(this);
+ if (!IS_FUNCTION(delegate)) {
+ throw %MakeTypeError('called_non_callable', [typeof this]);
+ }
+ return %Apply(delegate, this, arguments, 0, %_ArgumentsLength());
+}
+
+
+function CALL_NON_FUNCTION_AS_CONSTRUCTOR() {
+ var delegate = %GetConstructorDelegate(this);
+ if (!IS_FUNCTION(delegate)) {
+ throw %MakeTypeError('called_non_callable', [typeof this]);
+ }
+ return %Apply(delegate, this, arguments, 0, %_ArgumentsLength());
+}
+
+
+function CALL_FUNCTION_PROXY() {
+ var arity = %_ArgumentsLength() - 1;
+ var proxy = %_Arguments(arity); // The proxy comes in as an additional arg.
+ var trap = %GetCallTrap(proxy);
+ return %Apply(trap, this, arguments, 0, arity);
+}
+
+
+function CALL_FUNCTION_PROXY_AS_CONSTRUCTOR() {
+ var proxy = this;
+ var trap = %GetConstructTrap(proxy);
+ return %Apply(trap, this, arguments, 0, %_ArgumentsLength());
+}
+
+
+function APPLY_PREPARE(args) {
+ var length;
+ // First check whether length is a positive Smi and args is an
+ // array. This is the fast case. If this fails, we do the slow case
+ // that takes care of more eventualities.
+ if (IS_ARRAY(args)) {
+ length = args.length;
+ if (%_IsSmi(length) && length >= 0 && length < 0x800000 &&
+ IS_SPEC_FUNCTION(this)) {
+ return length;
+ }
+ }
+
+ length = (args == null) ? 0 : %ToUint32(args.length);
+
+ // We can handle any number of apply arguments if the stack is
+ // big enough, but sanity check the value to avoid overflow when
+ // multiplying with pointer size.
+ if (length > 0x800000) {
+ throw %MakeRangeError('stack_overflow', []);
+ }
+
+ if (!IS_SPEC_FUNCTION(this)) {
+ throw %MakeTypeError('apply_non_function',
+ [ %ToString(this), typeof this ]);
+ }
+
+ // Make sure the arguments list has the right type.
+ if (args != null && !IS_SPEC_OBJECT(args)) {
+ throw %MakeTypeError('apply_wrong_args', []);
+ }
+
+ // Return the length which is the number of arguments to copy to the
+ // stack. It is guaranteed to be a small integer at this point.
+ return length;
+}
+
+
+function APPLY_OVERFLOW(length) {
+ throw %MakeRangeError('stack_overflow', []);
+}
+
+
+// Convert the receiver to an object - forward to ToObject.
+function TO_OBJECT() {
+ return %ToObject(this);
+}
+
+
+// Convert the receiver to a number - forward to ToNumber.
+function TO_NUMBER() {
+ return %ToNumber(this);
+}
+
+
+// Convert the receiver to a string - forward to ToString.
+function TO_STRING() {
+ return %ToString(this);
+}
+
+
+/* -------------------------------------
+ - - - C o n v e r s i o n s - - -
+ -------------------------------------
+*/
+
+// ECMA-262, section 9.1, page 30. Use null/undefined for no hint,
+// (1) for number hint, and (2) for string hint.
+function ToPrimitive(x, hint) {
+ // Fast case check.
+ if (IS_STRING(x)) return x;
+ // Normal behavior.
+ if (!IS_SPEC_OBJECT(x)) return x;
+ if (IS_SYMBOL_WRAPPER(x)) return %_ValueOf(x);
+ if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT;
+ return (hint == NUMBER_HINT) ? %DefaultNumber(x) : %DefaultString(x);
+}
+
+
+// ECMA-262, section 9.2, page 30
+function ToBoolean(x) {
+ if (IS_BOOLEAN(x)) return x;
+ if (IS_STRING(x)) return x.length != 0;
+ if (x == null) return false;
+ if (IS_NUMBER(x)) return !((x == 0) || NUMBER_IS_NAN(x));
+ return true;
+}
+
+
+// ECMA-262, section 9.3, page 31.
+function ToNumber(x) {
+ if (IS_NUMBER(x)) return x;
+ if (IS_STRING(x)) {
+ return %_HasCachedArrayIndex(x) ? %_GetCachedArrayIndex(x)
+ : %StringToNumber(x);
+ }
+ if (IS_BOOLEAN(x)) return x ? 1 : 0;
+ if (IS_UNDEFINED(x)) return $NaN;
+ if (IS_SYMBOL(x)) return $NaN;
+ return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
+}
+
+function NonNumberToNumber(x) {
+ if (IS_STRING(x)) {
+ return %_HasCachedArrayIndex(x) ? %_GetCachedArrayIndex(x)
+ : %StringToNumber(x);
+ }
+ if (IS_BOOLEAN(x)) return x ? 1 : 0;
+ if (IS_UNDEFINED(x)) return $NaN;
+ if (IS_SYMBOL(x)) return $NaN;
+ return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
+}
+
+
+// ECMA-262, section 9.8, page 35.
+function ToString(x) {
+ if (IS_STRING(x)) return x;
+ if (IS_NUMBER(x)) return %_NumberToString(x);
+ if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
+ if (IS_UNDEFINED(x)) return 'undefined';
+ return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x));
+}
+
+function NonStringToString(x) {
+ if (IS_NUMBER(x)) return %_NumberToString(x);
+ if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
+ if (IS_UNDEFINED(x)) return 'undefined';
+ return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x));
+}
+
+
+// ES6 symbols
+function ToName(x) {
+ return IS_SYMBOL(x) ? x : %ToString(x);
+}
+
+
+// ECMA-262, section 9.9, page 36.
+function ToObject(x) {
+ if (IS_STRING(x)) return new $String(x);
+ if (IS_SYMBOL(x)) return new $Symbol(x);
+ if (IS_NUMBER(x)) return new $Number(x);
+ if (IS_BOOLEAN(x)) return new $Boolean(x);
+ if (IS_NULL_OR_UNDEFINED(x) && !IS_UNDETECTABLE(x)) {
+ throw %MakeTypeError('undefined_or_null_to_object', []);
+ }
+ return x;
+}
+
+
+// ECMA-262, section 9.4, page 34.
+function ToInteger(x) {
+ if (%_IsSmi(x)) return x;
+ return %NumberToInteger(ToNumber(x));
+}
+
+
+// ECMA-262, section 9.6, page 34.
+function ToUint32(x) {
+ if (%_IsSmi(x) && x >= 0) return x;
+ return %NumberToJSUint32(ToNumber(x));
+}
+
+
+// ECMA-262, section 9.5, page 34
+function ToInt32(x) {
+ if (%_IsSmi(x)) return x;
+ return %NumberToJSInt32(ToNumber(x));
+}
+
+
+// ES5, section 9.12
+function SameValue(x, y) {
+ if (typeof x != typeof y) return false;
+ if (IS_NUMBER(x)) {
+ if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true;
+ // x is +0 and y is -0 or vice versa.
+ if (x === 0 && y === 0 && (1 / x) != (1 / y)) return false;
+ }
+ return x === y;
+}
+
+
+/* ---------------------------------
+ - - - U t i l i t i e s - - -
+ ---------------------------------
+*/
+
+// Returns if the given x is a primitive value - not an object or a
+// function.
+function IsPrimitive(x) {
+ // Even though the type of null is "object", null is still
+ // considered a primitive value. IS_SPEC_OBJECT handles this correctly
+ // (i.e., it will return false if x is null).
+ return !IS_SPEC_OBJECT(x);
+}
+
+
+// ECMA-262, section 8.6.2.6, page 28.
+function DefaultNumber(x) {
+ var valueOf = x.valueOf;
+ if (IS_SPEC_FUNCTION(valueOf)) {
+ var v = %_CallFunction(x, valueOf);
+ if (%IsPrimitive(v)) return v;
+ }
+
+ var toString = x.toString;
+ if (IS_SPEC_FUNCTION(toString)) {
+ var s = %_CallFunction(x, toString);
+ if (%IsPrimitive(s)) return s;
+ }
+
+ throw %MakeTypeError('cannot_convert_to_primitive', []);
+}
+
+// ECMA-262, section 8.6.2.6, page 28.
+function DefaultString(x) {
+ var toString = x.toString;
+ if (IS_SPEC_FUNCTION(toString)) {
+ var s = %_CallFunction(x, toString);
+ if (%IsPrimitive(s)) return s;
+ }
+
+ var valueOf = x.valueOf;
+ if (IS_SPEC_FUNCTION(valueOf)) {
+ var v = %_CallFunction(x, valueOf);
+ if (%IsPrimitive(v)) return v;
+ }
+
+ throw %MakeTypeError('cannot_convert_to_primitive', []);
+}
+
+function ToPositiveInteger(x, rangeErrorName) {
+ var i = TO_INTEGER(x);
+ if (i < 0) throw %MakeRangeError(rangeErrorName);
+ return i;
+}
+
+
+// NOTE: Setting the prototype for Array must take place as early as
+// possible due to code generation for array literals. When
+// generating code for a array literal a boilerplate array is created
+// that is cloned when running the code. It is essential that the
+// boilerplate gets the right prototype.
+%FunctionSetPrototype($Array, new $Array(0));
diff --git a/chromium/v8/src/safepoint-table.cc b/chromium/v8/src/safepoint-table.cc
new file mode 100644
index 00000000000..b56556572eb
--- /dev/null
+++ b/chromium/v8/src/safepoint-table.cc
@@ -0,0 +1,237 @@
+// 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 "safepoint-table.h"
+
+#include "deoptimizer.h"
+#include "disasm.h"
+#include "macro-assembler.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+bool SafepointEntry::HasRegisters() const {
+ ASSERT(is_valid());
+ ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
+ const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
+ for (int i = 0; i < num_reg_bytes; i++) {
+ if (bits_[i] != SafepointTable::kNoRegisters) return true;
+ }
+ return false;
+}
+
+
+bool SafepointEntry::HasRegisterAt(int reg_index) const {
+ ASSERT(is_valid());
+ ASSERT(reg_index >= 0 && reg_index < kNumSafepointRegisters);
+ int byte_index = reg_index >> kBitsPerByteLog2;
+ int bit_index = reg_index & (kBitsPerByte - 1);
+ return (bits_[byte_index] & (1 << bit_index)) != 0;
+}
+
+
+SafepointTable::SafepointTable(Code* code) {
+ ASSERT(code->is_crankshafted());
+ code_ = code;
+ Address header = code->instruction_start() + code->safepoint_table_offset();
+ length_ = Memory::uint32_at(header + kLengthOffset);
+ entry_size_ = Memory::uint32_at(header + kEntrySizeOffset);
+ pc_and_deoptimization_indexes_ = header + kHeaderSize;
+ entries_ = pc_and_deoptimization_indexes_ +
+ (length_ * kPcAndDeoptimizationIndexSize);
+ ASSERT(entry_size_ > 0);
+ STATIC_ASSERT(SafepointEntry::DeoptimizationIndexField::kMax ==
+ Safepoint::kNoDeoptimizationIndex);
+}
+
+
+SafepointEntry SafepointTable::FindEntry(Address pc) const {
+ unsigned pc_offset = static_cast<unsigned>(pc - code_->instruction_start());
+ for (unsigned i = 0; i < length(); i++) {
+ // TODO(kasperl): Replace the linear search with binary search.
+ if (GetPcOffset(i) == pc_offset) return GetEntry(i);
+ }
+ return SafepointEntry();
+}
+
+
+void SafepointTable::PrintEntry(unsigned index) const {
+ disasm::NameConverter converter;
+ SafepointEntry entry = GetEntry(index);
+ uint8_t* bits = entry.bits();
+
+ // Print the stack slot bits.
+ if (entry_size_ > 0) {
+ ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
+ const int first = kNumSafepointRegisters >> kBitsPerByteLog2;
+ int last = entry_size_ - 1;
+ for (int i = first; i < last; i++) PrintBits(bits[i], kBitsPerByte);
+ int last_bits = code_->stack_slots() - ((last - first) * kBitsPerByte);
+ PrintBits(bits[last], last_bits);
+
+ // Print the registers (if any).
+ if (!entry.HasRegisters()) return;
+ for (int j = 0; j < kNumSafepointRegisters; j++) {
+ if (entry.HasRegisterAt(j)) {
+ PrintF(" | %s", converter.NameOfCPURegister(j));
+ }
+ }
+ }
+}
+
+
+void SafepointTable::PrintBits(uint8_t byte, int digits) {
+ ASSERT(digits >= 0 && digits <= kBitsPerByte);
+ for (int i = 0; i < digits; i++) {
+ PrintF("%c", ((byte & (1 << i)) == 0) ? '0' : '1');
+ }
+}
+
+
+void Safepoint::DefinePointerRegister(Register reg, Zone* zone) {
+ registers_->Add(reg.code(), zone);
+}
+
+
+Safepoint SafepointTableBuilder::DefineSafepoint(
+ Assembler* assembler,
+ Safepoint::Kind kind,
+ int arguments,
+ Safepoint::DeoptMode deopt_mode) {
+ ASSERT(arguments >= 0);
+ DeoptimizationInfo info;
+ info.pc = assembler->pc_offset();
+ info.arguments = arguments;
+ info.has_doubles = (kind & Safepoint::kWithDoubles);
+ deoptimization_info_.Add(info, zone_);
+ deopt_index_list_.Add(Safepoint::kNoDeoptimizationIndex, zone_);
+ if (deopt_mode == Safepoint::kNoLazyDeopt) {
+ last_lazy_safepoint_ = deopt_index_list_.length();
+ }
+ indexes_.Add(new(zone_) ZoneList<int>(8, zone_), zone_);
+ registers_.Add((kind & Safepoint::kWithRegisters)
+ ? new(zone_) ZoneList<int>(4, zone_)
+ : NULL,
+ zone_);
+ return Safepoint(indexes_.last(), registers_.last());
+}
+
+
+void SafepointTableBuilder::RecordLazyDeoptimizationIndex(int index) {
+ while (last_lazy_safepoint_ < deopt_index_list_.length()) {
+ deopt_index_list_[last_lazy_safepoint_++] = index;
+ }
+}
+
+unsigned SafepointTableBuilder::GetCodeOffset() const {
+ ASSERT(emitted_);
+ return offset_;
+}
+
+
+void SafepointTableBuilder::Emit(Assembler* assembler, int bits_per_entry) {
+ // Make sure the safepoint table is properly aligned. Pad with nops.
+ assembler->Align(kIntSize);
+ assembler->RecordComment(";;; Safepoint table.");
+ offset_ = assembler->pc_offset();
+
+ // Take the register bits into account.
+ bits_per_entry += kNumSafepointRegisters;
+
+ // Compute the number of bytes per safepoint entry.
+ int bytes_per_entry =
+ RoundUp(bits_per_entry, kBitsPerByte) >> kBitsPerByteLog2;
+
+ // Emit the table header.
+ int length = deoptimization_info_.length();
+ assembler->dd(length);
+ assembler->dd(bytes_per_entry);
+
+ // Emit sorted table of pc offsets together with deoptimization indexes.
+ for (int i = 0; i < length; i++) {
+ assembler->dd(deoptimization_info_[i].pc);
+ assembler->dd(EncodeExceptPC(deoptimization_info_[i],
+ deopt_index_list_[i]));
+ }
+
+ // Emit table of bitmaps.
+ ZoneList<uint8_t> bits(bytes_per_entry, zone_);
+ for (int i = 0; i < length; i++) {
+ ZoneList<int>* indexes = indexes_[i];
+ ZoneList<int>* registers = registers_[i];
+ bits.Clear();
+ bits.AddBlock(0, bytes_per_entry, zone_);
+
+ // Run through the registers (if any).
+ ASSERT(IsAligned(kNumSafepointRegisters, kBitsPerByte));
+ if (registers == NULL) {
+ const int num_reg_bytes = kNumSafepointRegisters >> kBitsPerByteLog2;
+ for (int j = 0; j < num_reg_bytes; j++) {
+ bits[j] = SafepointTable::kNoRegisters;
+ }
+ } else {
+ for (int j = 0; j < registers->length(); j++) {
+ int index = registers->at(j);
+ ASSERT(index >= 0 && index < kNumSafepointRegisters);
+ int byte_index = index >> kBitsPerByteLog2;
+ int bit_index = index & (kBitsPerByte - 1);
+ bits[byte_index] |= (1 << bit_index);
+ }
+ }
+
+ // Run through the indexes and build a bitmap.
+ for (int j = 0; j < indexes->length(); j++) {
+ int index = bits_per_entry - 1 - indexes->at(j);
+ int byte_index = index >> kBitsPerByteLog2;
+ int bit_index = index & (kBitsPerByte - 1);
+ bits[byte_index] |= (1U << bit_index);
+ }
+
+ // Emit the bitmap for the current entry.
+ for (int k = 0; k < bytes_per_entry; k++) {
+ assembler->db(bits[k]);
+ }
+ }
+ emitted_ = true;
+}
+
+
+uint32_t SafepointTableBuilder::EncodeExceptPC(const DeoptimizationInfo& info,
+ unsigned index) {
+ uint32_t encoding = SafepointEntry::DeoptimizationIndexField::encode(index);
+ encoding |= SafepointEntry::ArgumentsField::encode(info.arguments);
+ encoding |= SafepointEntry::SaveDoublesField::encode(info.has_doubles);
+ return encoding;
+}
+
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/safepoint-table.h b/chromium/v8/src/safepoint-table.h
new file mode 100644
index 00000000000..fc8bf7a411a
--- /dev/null
+++ b/chromium/v8/src/safepoint-table.h
@@ -0,0 +1,253 @@
+// 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_SAFEPOINT_TABLE_H_
+#define V8_SAFEPOINT_TABLE_H_
+
+#include "allocation.h"
+#include "heap.h"
+#include "v8memory.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+struct Register;
+
+class SafepointEntry BASE_EMBEDDED {
+ public:
+ SafepointEntry() : info_(0), bits_(NULL) {}
+
+ SafepointEntry(unsigned info, uint8_t* bits) : info_(info), bits_(bits) {
+ ASSERT(is_valid());
+ }
+
+ bool is_valid() const { return bits_ != NULL; }
+
+ bool Equals(const SafepointEntry& other) const {
+ return info_ == other.info_ && bits_ == other.bits_;
+ }
+
+ void Reset() {
+ info_ = 0;
+ bits_ = NULL;
+ }
+
+ int deoptimization_index() const {
+ ASSERT(is_valid());
+ return DeoptimizationIndexField::decode(info_);
+ }
+
+ static const int kArgumentsFieldBits = 3;
+ static const int kSaveDoublesFieldBits = 1;
+ static const int kDeoptIndexBits =
+ 32 - kArgumentsFieldBits - kSaveDoublesFieldBits;
+ class DeoptimizationIndexField:
+ public BitField<int, 0, kDeoptIndexBits> {}; // NOLINT
+ class ArgumentsField:
+ public BitField<unsigned,
+ kDeoptIndexBits,
+ kArgumentsFieldBits> {}; // NOLINT
+ class SaveDoublesField:
+ public BitField<bool,
+ kDeoptIndexBits + kArgumentsFieldBits,
+ kSaveDoublesFieldBits> { }; // NOLINT
+
+ int argument_count() const {
+ ASSERT(is_valid());
+ return ArgumentsField::decode(info_);
+ }
+
+ bool has_doubles() const {
+ ASSERT(is_valid());
+ return SaveDoublesField::decode(info_);
+ }
+
+ uint8_t* bits() {
+ ASSERT(is_valid());
+ return bits_;
+ }
+
+ bool HasRegisters() const;
+ bool HasRegisterAt(int reg_index) const;
+
+ private:
+ unsigned info_;
+ uint8_t* bits_;
+};
+
+
+class SafepointTable BASE_EMBEDDED {
+ public:
+ explicit SafepointTable(Code* code);
+
+ int size() const {
+ return kHeaderSize +
+ (length_ * (kPcAndDeoptimizationIndexSize + entry_size_)); }
+ unsigned length() const { return length_; }
+ unsigned entry_size() const { return entry_size_; }
+
+ unsigned GetPcOffset(unsigned index) const {
+ ASSERT(index < length_);
+ return Memory::uint32_at(GetPcOffsetLocation(index));
+ }
+
+ SafepointEntry GetEntry(unsigned index) const {
+ ASSERT(index < length_);
+ unsigned info = Memory::uint32_at(GetInfoLocation(index));
+ uint8_t* bits = &Memory::uint8_at(entries_ + (index * entry_size_));
+ return SafepointEntry(info, bits);
+ }
+
+ // Returns the entry for the given pc.
+ SafepointEntry FindEntry(Address pc) const;
+
+ void PrintEntry(unsigned index) const;
+
+ private:
+ static const uint8_t kNoRegisters = 0xFF;
+
+ static const int kLengthOffset = 0;
+ static const int kEntrySizeOffset = kLengthOffset + kIntSize;
+ static const int kHeaderSize = kEntrySizeOffset + kIntSize;
+
+ static const int kPcSize = kIntSize;
+ static const int kDeoptimizationIndexSize = kIntSize;
+ static const int kPcAndDeoptimizationIndexSize =
+ kPcSize + kDeoptimizationIndexSize;
+
+ Address GetPcOffsetLocation(unsigned index) const {
+ return pc_and_deoptimization_indexes_ +
+ (index * kPcAndDeoptimizationIndexSize);
+ }
+
+ Address GetInfoLocation(unsigned index) const {
+ return GetPcOffsetLocation(index) + kPcSize;
+ }
+
+ static void PrintBits(uint8_t byte, int digits);
+
+ DisallowHeapAllocation no_allocation_;
+ Code* code_;
+ unsigned length_;
+ unsigned entry_size_;
+
+ Address pc_and_deoptimization_indexes_;
+ Address entries_;
+
+ friend class SafepointTableBuilder;
+ friend class SafepointEntry;
+
+ DISALLOW_COPY_AND_ASSIGN(SafepointTable);
+};
+
+
+class Safepoint BASE_EMBEDDED {
+ public:
+ typedef enum {
+ kSimple = 0,
+ kWithRegisters = 1 << 0,
+ kWithDoubles = 1 << 1,
+ kWithRegistersAndDoubles = kWithRegisters | kWithDoubles
+ } Kind;
+
+ enum DeoptMode {
+ kNoLazyDeopt,
+ kLazyDeopt
+ };
+
+ static const int kNoDeoptimizationIndex =
+ (1 << (SafepointEntry::kDeoptIndexBits)) - 1;
+
+ void DefinePointerSlot(int index, Zone* zone) { indexes_->Add(index, zone); }
+ void DefinePointerRegister(Register reg, Zone* zone);
+
+ private:
+ Safepoint(ZoneList<int>* indexes, ZoneList<int>* registers) :
+ indexes_(indexes), registers_(registers) { }
+ ZoneList<int>* indexes_;
+ ZoneList<int>* registers_;
+
+ friend class SafepointTableBuilder;
+};
+
+
+class SafepointTableBuilder BASE_EMBEDDED {
+ public:
+ explicit SafepointTableBuilder(Zone* zone)
+ : deoptimization_info_(32, zone),
+ deopt_index_list_(32, zone),
+ indexes_(32, zone),
+ registers_(32, zone),
+ emitted_(false),
+ last_lazy_safepoint_(0),
+ zone_(zone) { }
+
+ // Get the offset of the emitted safepoint table in the code.
+ unsigned GetCodeOffset() const;
+
+ // Define a new safepoint for the current position in the body.
+ Safepoint DefineSafepoint(Assembler* assembler,
+ Safepoint::Kind kind,
+ int arguments,
+ Safepoint::DeoptMode mode);
+
+ // Record deoptimization index for lazy deoptimization for the last
+ // outstanding safepoints.
+ void RecordLazyDeoptimizationIndex(int index);
+
+ // Emit the safepoint table after the body. The number of bits per
+ // entry must be enough to hold all the pointer indexes.
+ void Emit(Assembler* assembler, int bits_per_entry);
+
+
+ private:
+ struct DeoptimizationInfo {
+ unsigned pc;
+ unsigned arguments;
+ bool has_doubles;
+ };
+
+ uint32_t EncodeExceptPC(const DeoptimizationInfo& info, unsigned index);
+
+ ZoneList<DeoptimizationInfo> deoptimization_info_;
+ ZoneList<unsigned> deopt_index_list_;
+ ZoneList<ZoneList<int>*> indexes_;
+ ZoneList<ZoneList<int>*> registers_;
+
+ unsigned offset_;
+ bool emitted_;
+ int last_lazy_safepoint_;
+
+ Zone* zone_;
+
+ DISALLOW_COPY_AND_ASSIGN(SafepointTableBuilder);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SAFEPOINT_TABLE_H_
diff --git a/chromium/v8/src/sampler.cc b/chromium/v8/src/sampler.cc
new file mode 100644
index 00000000000..1d0cdedd1fc
--- /dev/null
+++ b/chromium/v8/src/sampler.cc
@@ -0,0 +1,708 @@
+// Copyright 2013 the V8 project authors. 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 "sampler.h"
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \
+ || defined(__NetBSD__) || defined(__sun) || defined(__ANDROID__) \
+ || defined(__native_client__)
+
+#define USE_SIGNALS
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+// OpenBSD doesn't have <ucontext.h>. ucontext_t lives in <signal.h>
+// and is a typedef for struct sigcontext. There is no uc_mcontext.
+#if (!defined(__ANDROID__) || defined(__BIONIC_HAVE_UCONTEXT_T)) \
+ && !defined(__OpenBSD__)
+#include <ucontext.h>
+#endif
+#include <unistd.h>
+
+// GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'.
+// Old versions of the C library <signal.h> didn't define the type.
+#if defined(__ANDROID__) && !defined(__BIONIC_HAVE_UCONTEXT_T) && \
+ defined(__arm__) && !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT)
+#include <asm/sigcontext.h>
+#endif
+
+#elif defined(__MACH__)
+
+#include <mach/mach.h>
+
+#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
+
+#include "win32-headers.h"
+
+#endif
+
+#include "v8.h"
+
+#include "cpu-profiler.h"
+#include "flags.h"
+#include "frames-inl.h"
+#include "log.h"
+#include "platform.h"
+#include "simulator.h"
+#include "v8threads.h"
+#include "vm-state-inl.h"
+
+
+#if defined(__ANDROID__) && !defined(__BIONIC_HAVE_UCONTEXT_T)
+
+// Not all versions of Android's C library provide ucontext_t.
+// Detect this and provide custom but compatible definitions. Note that these
+// follow the GLibc naming convention to access register values from
+// mcontext_t.
+//
+// See http://code.google.com/p/android/issues/detail?id=34784
+
+#if defined(__arm__)
+
+typedef struct sigcontext mcontext_t;
+
+typedef struct ucontext {
+ uint32_t uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ // Other fields are not used by V8, don't define them here.
+} ucontext_t;
+
+#elif defined(__mips__)
+// MIPS version of sigcontext, for Android bionic.
+typedef struct {
+ 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;
+} mcontext_t;
+
+typedef struct ucontext {
+ uint32_t uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ // Other fields are not used by V8, don't define them here.
+} ucontext_t;
+
+#elif defined(__i386__)
+// x86 version for Android.
+typedef struct {
+ uint32_t gregs[19];
+ void* fpregs;
+ uint32_t oldmask;
+ uint32_t cr2;
+} mcontext_t;
+
+typedef uint32_t kernel_sigset_t[2]; // x86 kernel uses 64-bit signal masks
+typedef struct ucontext {
+ uint32_t uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ // Other fields are not used by V8, don't define them here.
+} ucontext_t;
+enum { REG_EBP = 6, REG_ESP = 7, REG_EIP = 14 };
+#endif
+
+#endif // __ANDROID__ && !defined(__BIONIC_HAVE_UCONTEXT_T)
+
+
+namespace v8 {
+namespace internal {
+
+namespace {
+
+class PlatformDataCommon : public Malloced {
+ public:
+ PlatformDataCommon() : profiled_thread_id_(ThreadId::Current()) {}
+ ThreadId profiled_thread_id() { return profiled_thread_id_; }
+
+ protected:
+ ~PlatformDataCommon() {}
+
+ private:
+ ThreadId profiled_thread_id_;
+};
+
+} // namespace
+
+#if defined(USE_SIGNALS)
+
+class Sampler::PlatformData : public PlatformDataCommon {
+ public:
+ PlatformData() : vm_tid_(pthread_self()) {}
+ pthread_t vm_tid() const { return vm_tid_; }
+
+ private:
+ pthread_t vm_tid_;
+};
+
+#elif defined(__MACH__)
+
+class Sampler::PlatformData : public PlatformDataCommon {
+ public:
+ PlatformData() : profiled_thread_(mach_thread_self()) {}
+
+ ~PlatformData() {
+ // Deallocate Mach port for thread.
+ mach_port_deallocate(mach_task_self(), profiled_thread_);
+ }
+
+ thread_act_t profiled_thread() { return profiled_thread_; }
+
+ private:
+ // Note: for profiled_thread_ Mach primitives are used instead of PThread's
+ // because the latter doesn't provide thread manipulation primitives required.
+ // For details, consult "Mac OS X Internals" book, Section 7.3.
+ thread_act_t profiled_thread_;
+};
+
+#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
+
+// ----------------------------------------------------------------------------
+// Win32 profiler support. On Cygwin we use the same sampler implementation as
+// on Win32.
+
+class Sampler::PlatformData : public PlatformDataCommon {
+ public:
+ // Get a handle to the calling thread. This is the thread that we are
+ // going to profile. We need to make a copy of the handle because we are
+ // going to use it in the sampler thread. Using GetThreadHandle() will
+ // not work in this case. We're using OpenThread because DuplicateHandle
+ // for some reason doesn't work in Chrome's sandbox.
+ PlatformData()
+ : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
+ THREAD_SUSPEND_RESUME |
+ THREAD_QUERY_INFORMATION,
+ false,
+ GetCurrentThreadId())) {}
+
+ ~PlatformData() {
+ if (profiled_thread_ != NULL) {
+ CloseHandle(profiled_thread_);
+ profiled_thread_ = NULL;
+ }
+ }
+
+ HANDLE profiled_thread() { return profiled_thread_; }
+
+ private:
+ HANDLE profiled_thread_;
+};
+#endif
+
+
+#if defined(USE_SIMULATOR)
+class SimulatorHelper {
+ public:
+ inline bool Init(Sampler* sampler, Isolate* isolate) {
+ ThreadId thread_id = sampler->platform_data()->profiled_thread_id();
+ Isolate::PerIsolateThreadData* per_thread_data = isolate->
+ FindPerThreadDataForThread(thread_id);
+ if (!per_thread_data) return false;
+ simulator_ = per_thread_data->simulator();
+ // Check if there is active simulator.
+ return simulator_ != NULL;
+ }
+
+ inline void FillRegisters(RegisterState* state) {
+ state->pc = reinterpret_cast<Address>(simulator_->get_pc());
+ state->sp = reinterpret_cast<Address>(simulator_->get_register(
+ Simulator::sp));
+#if V8_TARGET_ARCH_ARM
+ state->fp = reinterpret_cast<Address>(simulator_->get_register(
+ Simulator::r11));
+#elif V8_TARGET_ARCH_MIPS
+ state->fp = reinterpret_cast<Address>(simulator_->get_register(
+ Simulator::fp));
+#endif
+ }
+
+ private:
+ Simulator* simulator_;
+};
+#endif // USE_SIMULATOR
+
+
+#if defined(USE_SIGNALS)
+
+class SignalHandler : public AllStatic {
+ public:
+ static inline void EnsureInstalled() {
+ if (signal_handler_installed_) return;
+ struct sigaction sa;
+ sa.sa_sigaction = &HandleProfilerSignal;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ signal_handler_installed_ =
+ (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
+ }
+
+ static inline void Restore() {
+ if (signal_handler_installed_) {
+ sigaction(SIGPROF, &old_signal_handler_, 0);
+ signal_handler_installed_ = false;
+ }
+ }
+
+ static inline bool Installed() {
+ return signal_handler_installed_;
+ }
+
+ private:
+ static void HandleProfilerSignal(int signal, siginfo_t* info, void* context);
+ static bool signal_handler_installed_;
+ static struct sigaction old_signal_handler_;
+};
+
+struct sigaction SignalHandler::old_signal_handler_;
+bool SignalHandler::signal_handler_installed_ = false;
+
+
+void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info,
+ void* context) {
+#if defined(__native_client__)
+ // As Native Client does not support signal handling, profiling
+ // is disabled.
+ return;
+#else
+ USE(info);
+ if (signal != SIGPROF) return;
+ Isolate* isolate = Isolate::UncheckedCurrent();
+ if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
+ // We require a fully initialized and entered isolate.
+ return;
+ }
+ if (v8::Locker::IsActive() &&
+ !isolate->thread_manager()->IsLockedByCurrentThread()) {
+ return;
+ }
+
+ Sampler* sampler = isolate->logger()->sampler();
+ if (sampler == NULL || !sampler->IsActive()) return;
+
+ RegisterState state;
+
+#if defined(USE_SIMULATOR)
+ SimulatorHelper helper;
+ if (!helper.Init(sampler, isolate)) return;
+ helper.FillRegisters(&state);
+#else
+ // Extracting the sample from the context is extremely machine dependent.
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
+#if !defined(__OpenBSD__)
+ mcontext_t& mcontext = ucontext->uc_mcontext;
+#endif
+#if defined(__linux__) || defined(__ANDROID__)
+#if V8_HOST_ARCH_IA32
+ state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
+#elif V8_HOST_ARCH_X64
+ state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
+#elif V8_HOST_ARCH_ARM
+#if defined(__GLIBC__) && !defined(__UCLIBC__) && \
+ (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
+ // Old GLibc ARM versions used a gregs[] array to access the register
+ // values from mcontext_t.
+ state.pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
+#else
+ state.pc = reinterpret_cast<Address>(mcontext.arm_pc);
+ state.sp = reinterpret_cast<Address>(mcontext.arm_sp);
+ state.fp = reinterpret_cast<Address>(mcontext.arm_fp);
+#endif // defined(__GLIBC__) && !defined(__UCLIBC__) &&
+ // (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
+#elif V8_HOST_ARCH_MIPS
+ state.pc = reinterpret_cast<Address>(mcontext.pc);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[29]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[30]);
+#endif // V8_HOST_ARCH_*
+#elif defined(__FreeBSD__)
+#if V8_HOST_ARCH_IA32
+ state.pc = reinterpret_cast<Address>(mcontext.mc_eip);
+ state.sp = reinterpret_cast<Address>(mcontext.mc_esp);
+ state.fp = reinterpret_cast<Address>(mcontext.mc_ebp);
+#elif V8_HOST_ARCH_X64
+ state.pc = reinterpret_cast<Address>(mcontext.mc_rip);
+ state.sp = reinterpret_cast<Address>(mcontext.mc_rsp);
+ state.fp = reinterpret_cast<Address>(mcontext.mc_rbp);
+#elif V8_HOST_ARCH_ARM
+ state.pc = reinterpret_cast<Address>(mcontext.mc_r15);
+ state.sp = reinterpret_cast<Address>(mcontext.mc_r13);
+ state.fp = reinterpret_cast<Address>(mcontext.mc_r11);
+#endif // V8_HOST_ARCH_*
+#elif defined(__NetBSD__)
+#if V8_HOST_ARCH_IA32
+ state.pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_EIP]);
+ state.sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_ESP]);
+ state.fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_EBP]);
+#elif V8_HOST_ARCH_X64
+ state.pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_RIP]);
+ state.sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RSP]);
+ state.fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RBP]);
+#endif // V8_HOST_ARCH_*
+#elif defined(__OpenBSD__)
+#if V8_HOST_ARCH_IA32
+ state.pc = reinterpret_cast<Address>(ucontext->sc_eip);
+ state.sp = reinterpret_cast<Address>(ucontext->sc_esp);
+ state.fp = reinterpret_cast<Address>(ucontext->sc_ebp);
+#elif V8_HOST_ARCH_X64
+ state.pc = reinterpret_cast<Address>(ucontext->sc_rip);
+ state.sp = reinterpret_cast<Address>(ucontext->sc_rsp);
+ state.fp = reinterpret_cast<Address>(ucontext->sc_rbp);
+#endif // V8_HOST_ARCH_*
+#elif defined(__sun)
+ state.pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]);
+ state.sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]);
+ state.fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]);
+#endif // __sun
+#endif // USE_SIMULATOR
+ sampler->SampleStack(state);
+#endif // __native_client__
+}
+
+#endif
+
+
+class SamplerThread : public Thread {
+ public:
+ static const int kSamplerThreadStackSize = 64 * KB;
+
+ explicit SamplerThread(int interval)
+ : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)),
+ interval_(interval) {}
+
+ static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); }
+ static void TearDown() { delete mutex_; }
+
+ static void AddActiveSampler(Sampler* sampler) {
+ bool need_to_start = false;
+ ScopedLock lock(mutex_);
+ if (instance_ == NULL) {
+ // Start a thread that will send SIGPROF signal to VM threads,
+ // when CPU profiling will be enabled.
+ instance_ = new SamplerThread(sampler->interval());
+ need_to_start = true;
+ }
+
+ ASSERT(sampler->IsActive());
+ ASSERT(!instance_->active_samplers_.Contains(sampler));
+ ASSERT(instance_->interval_ == sampler->interval());
+ instance_->active_samplers_.Add(sampler);
+
+#if defined(USE_SIGNALS)
+ SignalHandler::EnsureInstalled();
+#endif
+ if (need_to_start) instance_->StartSynchronously();
+ }
+
+ static void RemoveActiveSampler(Sampler* sampler) {
+ SamplerThread* instance_to_remove = NULL;
+ {
+ ScopedLock lock(mutex_);
+
+ ASSERT(sampler->IsActive());
+ bool removed = instance_->active_samplers_.RemoveElement(sampler);
+ ASSERT(removed);
+ USE(removed);
+
+ // We cannot delete the instance immediately as we need to Join() the
+ // thread but we are holding mutex_ and the thread may try to acquire it.
+ if (instance_->active_samplers_.is_empty()) {
+ instance_to_remove = instance_;
+ instance_ = NULL;
+#if defined(USE_SIGNALS)
+ SignalHandler::Restore();
+#endif
+ }
+ }
+
+ if (!instance_to_remove) return;
+ instance_to_remove->Join();
+ delete instance_to_remove;
+ }
+
+ // Implement Thread::Run().
+ virtual void Run() {
+ while (true) {
+ {
+ ScopedLock lock(mutex_);
+ if (active_samplers_.is_empty()) break;
+ // When CPU profiling is enabled both JavaScript and C++ code is
+ // profiled. We must not suspend.
+ for (int i = 0; i < active_samplers_.length(); ++i) {
+ Sampler* sampler = active_samplers_.at(i);
+ if (!sampler->isolate()->IsInitialized()) continue;
+ if (!sampler->IsProfiling()) continue;
+ SampleContext(sampler);
+ }
+ }
+ OS::Sleep(interval_);
+ }
+ }
+
+ private:
+#if defined(USE_SIGNALS)
+
+ void SampleContext(Sampler* sampler) {
+ if (!SignalHandler::Installed()) return;
+ pthread_t tid = sampler->platform_data()->vm_tid();
+ pthread_kill(tid, SIGPROF);
+ }
+
+#elif defined(__MACH__)
+
+ void SampleContext(Sampler* sampler) {
+ thread_act_t profiled_thread = sampler->platform_data()->profiled_thread();
+
+#if defined(USE_SIMULATOR)
+ SimulatorHelper helper;
+ Isolate* isolate = sampler->isolate();
+ if (!helper.Init(sampler, isolate)) return;
+#endif
+
+ if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
+
+#if V8_HOST_ARCH_X64
+ thread_state_flavor_t flavor = x86_THREAD_STATE64;
+ x86_thread_state64_t thread_state;
+ mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
+#if __DARWIN_UNIX03
+#define REGISTER_FIELD(name) __r ## name
+#else
+#define REGISTER_FIELD(name) r ## name
+#endif // __DARWIN_UNIX03
+#elif V8_HOST_ARCH_IA32
+ thread_state_flavor_t flavor = i386_THREAD_STATE;
+ i386_thread_state_t thread_state;
+ mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
+#if __DARWIN_UNIX03
+#define REGISTER_FIELD(name) __e ## name
+#else
+#define REGISTER_FIELD(name) e ## name
+#endif // __DARWIN_UNIX03
+#else
+#error Unsupported Mac OS X host architecture.
+#endif // V8_HOST_ARCH
+
+ if (thread_get_state(profiled_thread,
+ flavor,
+ reinterpret_cast<natural_t*>(&thread_state),
+ &count) == KERN_SUCCESS) {
+ RegisterState state;
+#if defined(USE_SIMULATOR)
+ helper.FillRegisters(&state);
+#else
+ state.pc = reinterpret_cast<Address>(thread_state.REGISTER_FIELD(ip));
+ state.sp = reinterpret_cast<Address>(thread_state.REGISTER_FIELD(sp));
+ state.fp = reinterpret_cast<Address>(thread_state.REGISTER_FIELD(bp));
+#endif // USE_SIMULATOR
+#undef REGISTER_FIELD
+ sampler->SampleStack(state);
+ }
+ thread_resume(profiled_thread);
+ }
+
+#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
+
+ void SampleContext(Sampler* sampler) {
+ HANDLE profiled_thread = sampler->platform_data()->profiled_thread();
+ if (profiled_thread == NULL) return;
+
+ Isolate* isolate = sampler->isolate();
+#if defined(USE_SIMULATOR)
+ SimulatorHelper helper;
+ if (!helper.Init(sampler, isolate)) return;
+#endif
+
+ const DWORD kSuspendFailed = static_cast<DWORD>(-1);
+ if (SuspendThread(profiled_thread) == kSuspendFailed) return;
+
+ // Context used for sampling the register state of the profiled thread.
+ CONTEXT context;
+ memset(&context, 0, sizeof(context));
+ context.ContextFlags = CONTEXT_FULL;
+ if (GetThreadContext(profiled_thread, &context) != 0) {
+ RegisterState state;
+#if defined(USE_SIMULATOR)
+ helper.FillRegisters(&state);
+#else
+#if V8_HOST_ARCH_X64
+ state.pc = reinterpret_cast<Address>(context.Rip);
+ state.sp = reinterpret_cast<Address>(context.Rsp);
+ state.fp = reinterpret_cast<Address>(context.Rbp);
+#else
+ state.pc = reinterpret_cast<Address>(context.Eip);
+ state.sp = reinterpret_cast<Address>(context.Esp);
+ state.fp = reinterpret_cast<Address>(context.Ebp);
+#endif
+#endif // USE_SIMULATOR
+ sampler->SampleStack(state);
+ }
+ ResumeThread(profiled_thread);
+ }
+
+#endif // USE_SIGNALS
+
+
+ // Protects the process wide state below.
+ static Mutex* mutex_;
+ static SamplerThread* instance_;
+
+ const int interval_;
+ List<Sampler*> active_samplers_;
+
+ DISALLOW_COPY_AND_ASSIGN(SamplerThread);
+};
+
+
+Mutex* SamplerThread::mutex_ = NULL;
+SamplerThread* SamplerThread::instance_ = NULL;
+
+
+//
+// StackTracer implementation
+//
+DISABLE_ASAN void TickSample::Init(Isolate* isolate,
+ const RegisterState& regs) {
+ ASSERT(isolate->IsInitialized());
+ pc = regs.pc;
+ state = isolate->current_vm_state();
+
+ // Avoid collecting traces while doing GC.
+ if (state == GC) return;
+
+ Address js_entry_sp = isolate->js_entry_sp();
+ if (js_entry_sp == 0) {
+ // Not executing JS now.
+ return;
+ }
+
+ ExternalCallbackScope* scope = isolate->external_callback_scope();
+ Address handler = Isolate::handler(isolate->thread_local_top());
+ // If there is a handler on top of the external callback scope then
+ // we have already entrered JavaScript again and the external callback
+ // is not the top function.
+ if (scope && scope->scope_address() < handler) {
+ external_callback = scope->callback();
+ has_external_callback = true;
+ } else {
+ // Sample potential return address value for frameless invocation of
+ // stubs (we'll figure out later, if this value makes sense).
+ tos = Memory::Address_at(regs.sp);
+ has_external_callback = false;
+ }
+
+ SafeStackFrameIterator it(isolate, regs.fp, regs.sp, js_entry_sp);
+ top_frame_type = it.top_frame_type();
+ int i = 0;
+ while (!it.done() && i < TickSample::kMaxFramesCount) {
+ stack[i++] = it.frame()->pc();
+ it.Advance();
+ }
+ frames_count = i;
+}
+
+
+void Sampler::SetUp() {
+ SamplerThread::SetUp();
+}
+
+
+void Sampler::TearDown() {
+ SamplerThread::TearDown();
+}
+
+
+Sampler::Sampler(Isolate* isolate, int interval)
+ : isolate_(isolate),
+ interval_(interval),
+ profiling_(false),
+ active_(false),
+ is_counting_samples_(false),
+ js_and_external_sample_count_(0) {
+ data_ = new PlatformData;
+}
+
+
+Sampler::~Sampler() {
+ ASSERT(!IsActive());
+ delete data_;
+}
+
+
+void Sampler::Start() {
+ ASSERT(!IsActive());
+ SetActive(true);
+ SamplerThread::AddActiveSampler(this);
+}
+
+
+void Sampler::Stop() {
+ ASSERT(IsActive());
+ SamplerThread::RemoveActiveSampler(this);
+ SetActive(false);
+}
+
+
+void Sampler::SampleStack(const RegisterState& state) {
+ TickSample* sample = isolate_->cpu_profiler()->TickSampleEvent();
+ TickSample sample_obj;
+ if (sample == NULL) sample = &sample_obj;
+ sample->Init(isolate_, state);
+ if (is_counting_samples_) {
+ if (sample->state == JS || sample->state == EXTERNAL) {
+ ++js_and_external_sample_count_;
+ }
+ }
+ Tick(sample);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/sampler.h b/chromium/v8/src/sampler.h
new file mode 100644
index 00000000000..80ccc087cad
--- /dev/null
+++ b/chromium/v8/src/sampler.h
@@ -0,0 +1,139 @@
+// Copyright 2013 the V8 project authors. 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_SAMPLER_H_
+#define V8_SAMPLER_H_
+
+#include "atomicops.h"
+#include "frames.h"
+#include "v8globals.h"
+
+namespace v8 {
+namespace internal {
+
+class Isolate;
+
+// ----------------------------------------------------------------------------
+// Sampler
+//
+// A sampler periodically samples the state of the VM and optionally
+// (if used for profiling) the program counter and stack pointer for
+// the thread that created it.
+
+struct RegisterState {
+ RegisterState() : pc(NULL), sp(NULL), fp(NULL) {}
+ Address pc; // Instruction pointer.
+ Address sp; // Stack pointer.
+ Address fp; // Frame pointer.
+};
+
+// TickSample captures the information collected for each sample.
+struct TickSample {
+ TickSample()
+ : state(OTHER),
+ pc(NULL),
+ external_callback(NULL),
+ frames_count(0),
+ has_external_callback(false),
+ top_frame_type(StackFrame::NONE) {}
+ void Init(Isolate* isolate, const RegisterState& state);
+ StateTag state; // The state of the VM.
+ Address pc; // Instruction pointer.
+ union {
+ Address tos; // Top stack value (*sp).
+ Address external_callback;
+ };
+ static const int kMaxFramesCount = 64;
+ Address stack[kMaxFramesCount]; // Call stack.
+ int frames_count : 8; // Number of captured frames.
+ bool has_external_callback : 1;
+ StackFrame::Type top_frame_type : 4;
+};
+
+class Sampler {
+ public:
+ // Initializes the Sampler support. Called once at VM startup.
+ static void SetUp();
+ static void TearDown();
+
+ // Initialize sampler.
+ Sampler(Isolate* isolate, int interval);
+ virtual ~Sampler();
+
+ Isolate* isolate() const { return isolate_; }
+ int interval() const { return interval_; }
+
+ // Performs stack sampling.
+ void SampleStack(const RegisterState& regs);
+
+ // Start and stop sampler.
+ void Start();
+ void Stop();
+
+ // Is the sampler used for profiling?
+ bool IsProfiling() const { return NoBarrier_Load(&profiling_) > 0; }
+ void IncreaseProfilingDepth() { NoBarrier_AtomicIncrement(&profiling_, 1); }
+ void DecreaseProfilingDepth() { NoBarrier_AtomicIncrement(&profiling_, -1); }
+
+ // Whether the sampler is running (that is, consumes resources).
+ bool IsActive() const { return NoBarrier_Load(&active_); }
+
+ // Used in tests to make sure that stack sampling is performed.
+ unsigned js_and_external_sample_count() const {
+ return js_and_external_sample_count_;
+ }
+ void StartCountingSamples() {
+ is_counting_samples_ = true;
+ js_and_external_sample_count_ = 0;
+ }
+
+ class PlatformData;
+ PlatformData* platform_data() const { return data_; }
+
+ protected:
+ // This method is called for each sampling period with the current
+ // program counter.
+ virtual void Tick(TickSample* sample) = 0;
+
+ private:
+ void SetActive(bool value) { NoBarrier_Store(&active_, value); }
+
+ Isolate* isolate_;
+ const int interval_;
+ Atomic32 profiling_;
+ Atomic32 active_;
+ PlatformData* data_; // Platform specific data.
+ bool is_counting_samples_;
+ // Counts stack samples taken in JS VM state.
+ unsigned js_and_external_sample_count_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_SAMPLER_H_
diff --git a/chromium/v8/src/scanner-character-streams.cc b/chromium/v8/src/scanner-character-streams.cc
new file mode 100644
index 00000000000..fb503459f7f
--- /dev/null
+++ b/chromium/v8/src/scanner-character-streams.cc
@@ -0,0 +1,325 @@
+// 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 "scanner-character-streams.h"
+
+#include "handles.h"
+#include "unicode-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// BufferedUtf16CharacterStreams
+
+BufferedUtf16CharacterStream::BufferedUtf16CharacterStream()
+ : Utf16CharacterStream(),
+ pushback_limit_(NULL) {
+ // Initialize buffer as being empty. First read will fill the buffer.
+ buffer_cursor_ = buffer_;
+ buffer_end_ = buffer_;
+}
+
+
+BufferedUtf16CharacterStream::~BufferedUtf16CharacterStream() { }
+
+void BufferedUtf16CharacterStream::PushBack(uc32 character) {
+ if (character == kEndOfInput) {
+ pos_--;
+ return;
+ }
+ if (pushback_limit_ == NULL && buffer_cursor_ > buffer_) {
+ // buffer_ is writable, buffer_cursor_ is const pointer.
+ buffer_[--buffer_cursor_ - buffer_] = static_cast<uc16>(character);
+ pos_--;
+ return;
+ }
+ SlowPushBack(static_cast<uc16>(character));
+}
+
+
+void BufferedUtf16CharacterStream::SlowPushBack(uc16 character) {
+ // In pushback mode, the end of the buffer contains pushback,
+ // and the start of the buffer (from buffer start to pushback_limit_)
+ // contains valid data that comes just after the pushback.
+ // We NULL the pushback_limit_ if pushing all the way back to the
+ // start of the buffer.
+
+ if (pushback_limit_ == NULL) {
+ // Enter pushback mode.
+ pushback_limit_ = buffer_end_;
+ buffer_end_ = buffer_ + kBufferSize;
+ buffer_cursor_ = buffer_end_;
+ }
+ // Ensure that there is room for at least one pushback.
+ ASSERT(buffer_cursor_ > buffer_);
+ ASSERT(pos_ > 0);
+ buffer_[--buffer_cursor_ - buffer_] = character;
+ if (buffer_cursor_ == buffer_) {
+ pushback_limit_ = NULL;
+ } else if (buffer_cursor_ < pushback_limit_) {
+ pushback_limit_ = buffer_cursor_;
+ }
+ pos_--;
+}
+
+
+bool BufferedUtf16CharacterStream::ReadBlock() {
+ buffer_cursor_ = buffer_;
+ if (pushback_limit_ != NULL) {
+ // Leave pushback mode.
+ buffer_end_ = pushback_limit_;
+ pushback_limit_ = NULL;
+ // If there were any valid characters left at the
+ // start of the buffer, use those.
+ if (buffer_cursor_ < buffer_end_) return true;
+ // Otherwise read a new block.
+ }
+ unsigned length = FillBuffer(pos_, kBufferSize);
+ buffer_end_ = buffer_ + length;
+ return length > 0;
+}
+
+
+unsigned BufferedUtf16CharacterStream::SlowSeekForward(unsigned delta) {
+ // Leave pushback mode (i.e., ignore that there might be valid data
+ // in the buffer before the pushback_limit_ point).
+ pushback_limit_ = NULL;
+ return BufferSeekForward(delta);
+}
+
+
+// ----------------------------------------------------------------------------
+// GenericStringUtf16CharacterStream
+
+
+GenericStringUtf16CharacterStream::GenericStringUtf16CharacterStream(
+ Handle<String> data,
+ unsigned start_position,
+ unsigned end_position)
+ : string_(data),
+ length_(end_position) {
+ ASSERT(end_position >= start_position);
+ buffer_cursor_ = buffer_;
+ buffer_end_ = buffer_;
+ pos_ = start_position;
+}
+
+
+GenericStringUtf16CharacterStream::~GenericStringUtf16CharacterStream() { }
+
+
+unsigned GenericStringUtf16CharacterStream::BufferSeekForward(unsigned delta) {
+ unsigned old_pos = pos_;
+ pos_ = Min(pos_ + delta, length_);
+ ReadBlock();
+ return pos_ - old_pos;
+}
+
+
+unsigned GenericStringUtf16CharacterStream::FillBuffer(unsigned from_pos,
+ unsigned length) {
+ if (from_pos >= length_) return 0;
+ if (from_pos + length > length_) {
+ length = length_ - from_pos;
+ }
+ String::WriteToFlat<uc16>(*string_, buffer_, from_pos, from_pos + length);
+ return length;
+}
+
+
+// ----------------------------------------------------------------------------
+// Utf8ToUtf16CharacterStream
+Utf8ToUtf16CharacterStream::Utf8ToUtf16CharacterStream(const byte* data,
+ unsigned length)
+ : BufferedUtf16CharacterStream(),
+ raw_data_(data),
+ raw_data_length_(length),
+ raw_data_pos_(0),
+ raw_character_position_(0) {
+ ReadBlock();
+}
+
+
+Utf8ToUtf16CharacterStream::~Utf8ToUtf16CharacterStream() { }
+
+
+unsigned Utf8ToUtf16CharacterStream::BufferSeekForward(unsigned delta) {
+ unsigned old_pos = pos_;
+ unsigned target_pos = pos_ + delta;
+ SetRawPosition(target_pos);
+ pos_ = raw_character_position_;
+ ReadBlock();
+ return pos_ - old_pos;
+}
+
+
+unsigned Utf8ToUtf16CharacterStream::FillBuffer(unsigned char_position,
+ unsigned length) {
+ static const unibrow::uchar kMaxUtf16Character = 0xffff;
+ SetRawPosition(char_position);
+ if (raw_character_position_ != char_position) {
+ // char_position was not a valid position in the stream (hit the end
+ // while spooling to it).
+ return 0u;
+ }
+ unsigned i = 0;
+ while (i < length - 1) {
+ if (raw_data_pos_ == raw_data_length_) break;
+ unibrow::uchar c = raw_data_[raw_data_pos_];
+ if (c <= unibrow::Utf8::kMaxOneByteChar) {
+ raw_data_pos_++;
+ } else {
+ c = unibrow::Utf8::CalculateValue(raw_data_ + raw_data_pos_,
+ raw_data_length_ - raw_data_pos_,
+ &raw_data_pos_);
+ }
+ if (c > kMaxUtf16Character) {
+ buffer_[i++] = unibrow::Utf16::LeadSurrogate(c);
+ buffer_[i++] = unibrow::Utf16::TrailSurrogate(c);
+ } else {
+ buffer_[i++] = static_cast<uc16>(c);
+ }
+ }
+ raw_character_position_ = char_position + i;
+ return i;
+}
+
+
+static const byte kUtf8MultiByteMask = 0xC0;
+static const byte kUtf8MultiByteCharStart = 0xC0;
+static const byte kUtf8MultiByteCharFollower = 0x80;
+
+
+#ifdef DEBUG
+static bool IsUtf8MultiCharacterStart(byte first_byte) {
+ return (first_byte & kUtf8MultiByteMask) == kUtf8MultiByteCharStart;
+}
+#endif
+
+
+static bool IsUtf8MultiCharacterFollower(byte later_byte) {
+ return (later_byte & kUtf8MultiByteMask) == kUtf8MultiByteCharFollower;
+}
+
+
+// Move the cursor back to point at the preceding UTF-8 character start
+// in the buffer.
+static inline void Utf8CharacterBack(const byte* buffer, unsigned* cursor) {
+ byte character = buffer[--*cursor];
+ if (character > unibrow::Utf8::kMaxOneByteChar) {
+ ASSERT(IsUtf8MultiCharacterFollower(character));
+ // Last byte of a multi-byte character encoding. Step backwards until
+ // pointing to the first byte of the encoding, recognized by having the
+ // top two bits set.
+ while (IsUtf8MultiCharacterFollower(buffer[--*cursor])) { }
+ ASSERT(IsUtf8MultiCharacterStart(buffer[*cursor]));
+ }
+}
+
+
+// Move the cursor forward to point at the next following UTF-8 character start
+// in the buffer.
+static inline void Utf8CharacterForward(const byte* buffer, unsigned* cursor) {
+ byte character = buffer[(*cursor)++];
+ if (character > unibrow::Utf8::kMaxOneByteChar) {
+ // First character of a multi-byte character encoding.
+ // The number of most-significant one-bits determines the length of the
+ // encoding:
+ // 110..... - (0xCx, 0xDx) one additional byte (minimum).
+ // 1110.... - (0xEx) two additional bytes.
+ // 11110... - (0xFx) three additional bytes (maximum).
+ ASSERT(IsUtf8MultiCharacterStart(character));
+ // Additional bytes is:
+ // 1 if value in range 0xC0 .. 0xDF.
+ // 2 if value in range 0xE0 .. 0xEF.
+ // 3 if value in range 0xF0 .. 0xF7.
+ // Encode that in a single value.
+ unsigned additional_bytes =
+ ((0x3211u) >> (((character - 0xC0) >> 2) & 0xC)) & 0x03;
+ *cursor += additional_bytes;
+ ASSERT(!IsUtf8MultiCharacterFollower(buffer[1 + additional_bytes]));
+ }
+}
+
+
+// This can't set a raw position between two surrogate pairs, since there
+// is no position in the UTF8 stream that corresponds to that. This assumes
+// that the surrogate pair is correctly coded as a 4 byte UTF-8 sequence. If
+// it is illegally coded as two 3 byte sequences then there is no problem here.
+void Utf8ToUtf16CharacterStream::SetRawPosition(unsigned target_position) {
+ if (raw_character_position_ > target_position) {
+ // Spool backwards in utf8 buffer.
+ do {
+ int old_pos = raw_data_pos_;
+ Utf8CharacterBack(raw_data_, &raw_data_pos_);
+ raw_character_position_--;
+ ASSERT(old_pos - raw_data_pos_ <= 4);
+ // Step back over both code units for surrogate pairs.
+ if (old_pos - raw_data_pos_ == 4) raw_character_position_--;
+ } while (raw_character_position_ > target_position);
+ // No surrogate pair splitting.
+ ASSERT(raw_character_position_ == target_position);
+ return;
+ }
+ // Spool forwards in the utf8 buffer.
+ while (raw_character_position_ < target_position) {
+ if (raw_data_pos_ == raw_data_length_) return;
+ int old_pos = raw_data_pos_;
+ Utf8CharacterForward(raw_data_, &raw_data_pos_);
+ raw_character_position_++;
+ ASSERT(raw_data_pos_ - old_pos <= 4);
+ if (raw_data_pos_ - old_pos == 4) raw_character_position_++;
+ }
+ // No surrogate pair splitting.
+ ASSERT(raw_character_position_ == target_position);
+}
+
+
+// ----------------------------------------------------------------------------
+// ExternalTwoByteStringUtf16CharacterStream
+
+ExternalTwoByteStringUtf16CharacterStream::
+ ~ExternalTwoByteStringUtf16CharacterStream() { }
+
+
+ExternalTwoByteStringUtf16CharacterStream
+ ::ExternalTwoByteStringUtf16CharacterStream(
+ Handle<ExternalTwoByteString> data,
+ int start_position,
+ int end_position)
+ : Utf16CharacterStream(),
+ source_(data),
+ raw_data_(data->GetTwoByteData(start_position)) {
+ buffer_cursor_ = raw_data_,
+ buffer_end_ = raw_data_ + (end_position - start_position);
+ pos_ = start_position;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/scanner-character-streams.h b/chromium/v8/src/scanner-character-streams.h
new file mode 100644
index 00000000000..319ee8fc1c5
--- /dev/null
+++ b/chromium/v8/src/scanner-character-streams.h
@@ -0,0 +1,129 @@
+// 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_SCANNER_CHARACTER_STREAMS_H_
+#define V8_SCANNER_CHARACTER_STREAMS_H_
+
+#include "scanner.h"
+
+namespace v8 {
+namespace internal {
+
+// A buffered character stream based on a random access character
+// source (ReadBlock can be called with pos_ pointing to any position,
+// even positions before the current).
+class BufferedUtf16CharacterStream: public Utf16CharacterStream {
+ public:
+ BufferedUtf16CharacterStream();
+ virtual ~BufferedUtf16CharacterStream();
+
+ virtual void PushBack(uc32 character);
+
+ protected:
+ static const unsigned kBufferSize = 512;
+ static const unsigned kPushBackStepSize = 16;
+
+ virtual unsigned SlowSeekForward(unsigned delta);
+ virtual bool ReadBlock();
+ virtual void SlowPushBack(uc16 character);
+
+ virtual unsigned BufferSeekForward(unsigned delta) = 0;
+ virtual unsigned FillBuffer(unsigned position, unsigned length) = 0;
+
+ const uc16* pushback_limit_;
+ uc16 buffer_[kBufferSize];
+};
+
+
+// Generic string stream.
+class GenericStringUtf16CharacterStream: public BufferedUtf16CharacterStream {
+ public:
+ GenericStringUtf16CharacterStream(Handle<String> data,
+ unsigned start_position,
+ unsigned end_position);
+ virtual ~GenericStringUtf16CharacterStream();
+
+ protected:
+ virtual unsigned BufferSeekForward(unsigned delta);
+ virtual unsigned FillBuffer(unsigned position, unsigned length);
+
+ Handle<String> string_;
+ unsigned start_position_;
+ unsigned length_;
+};
+
+
+// Utf16 stream based on a literal UTF-8 string.
+class Utf8ToUtf16CharacterStream: public BufferedUtf16CharacterStream {
+ public:
+ Utf8ToUtf16CharacterStream(const byte* data, unsigned length);
+ virtual ~Utf8ToUtf16CharacterStream();
+
+ protected:
+ virtual unsigned BufferSeekForward(unsigned delta);
+ virtual unsigned FillBuffer(unsigned char_position, unsigned length);
+ void SetRawPosition(unsigned char_position);
+
+ const byte* raw_data_;
+ unsigned raw_data_length_; // Measured in bytes, not characters.
+ unsigned raw_data_pos_;
+ // The character position of the character at raw_data[raw_data_pos_].
+ // Not necessarily the same as pos_.
+ unsigned raw_character_position_;
+};
+
+
+// UTF16 buffer to read characters from an external string.
+class ExternalTwoByteStringUtf16CharacterStream: public Utf16CharacterStream {
+ public:
+ ExternalTwoByteStringUtf16CharacterStream(Handle<ExternalTwoByteString> data,
+ int start_position,
+ int end_position);
+ virtual ~ExternalTwoByteStringUtf16CharacterStream();
+
+ virtual void PushBack(uc32 character) {
+ ASSERT(buffer_cursor_ > raw_data_);
+ buffer_cursor_--;
+ pos_--;
+ }
+
+ protected:
+ virtual unsigned SlowSeekForward(unsigned delta) {
+ // Fast case always handles seeking.
+ return 0;
+ }
+ virtual bool ReadBlock() {
+ // Entire string is read at start.
+ return false;
+ }
+ Handle<ExternalTwoByteString> source_;
+ const uc16* raw_data_; // Pointer to the actual array of characters.
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SCANNER_CHARACTER_STREAMS_H_
diff --git a/chromium/v8/src/scanner.cc b/chromium/v8/src/scanner.cc
new file mode 100644
index 00000000000..8b7cb569bdd
--- /dev/null
+++ b/chromium/v8/src/scanner.cc
@@ -0,0 +1,1111 @@
+// 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.
+
+// Features shared by parsing and pre-parsing scanners.
+
+#include "scanner.h"
+
+#include "../include/v8stdint.h"
+#include "char-predicates-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// Scanner
+
+Scanner::Scanner(UnicodeCache* unicode_cache)
+ : unicode_cache_(unicode_cache),
+ octal_pos_(Location::invalid()),
+ harmony_scoping_(false),
+ harmony_modules_(false),
+ harmony_numeric_literals_(false) { }
+
+
+void Scanner::Initialize(Utf16CharacterStream* 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();
+}
+
+
+uc32 Scanner::ScanHexNumber(int expected_length) {
+ ASSERT(expected_length <= 4); // prevent overflow
+
+ uc32 digits[4] = { 0, 0, 0, 0 };
+ uc32 x = 0;
+ for (int i = 0; i < expected_length; i++) {
+ digits[i] = c0_;
+ int d = HexValue(c0_);
+ if (d < 0) {
+ // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes
+ // should be illegal, but other JS VMs just return the
+ // non-escaped version of the original character.
+
+ // Push back digits that we have advanced past.
+ for (int j = i-1; j >= 0; j--) {
+ PushBack(digits[j]);
+ }
+ return -1;
+ }
+ x = x * 16 + d;
+ Advance();
+ }
+
+ return x;
+}
+
+
+// Ensure that tokens can be stored in a byte.
+STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
+
+// Table of one-character tokens, by character (0x00..0x7f only).
+static const byte one_char_tokens[] = {
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::LPAREN, // 0x28
+ Token::RPAREN, // 0x29
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::COMMA, // 0x2c
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::COLON, // 0x3a
+ Token::SEMICOLON, // 0x3b
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::CONDITIONAL, // 0x3f
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::LBRACK, // 0x5b
+ Token::ILLEGAL,
+ Token::RBRACK, // 0x5d
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::ILLEGAL,
+ Token::LBRACE, // 0x7b
+ Token::ILLEGAL,
+ Token::RBRACE, // 0x7d
+ Token::BIT_NOT, // 0x7e
+ Token::ILLEGAL
+};
+
+
+Token::Value Scanner::Next() {
+ current_ = next_;
+ has_line_terminator_before_next_ = false;
+ has_multiline_comment_before_next_ = false;
+ if (static_cast<unsigned>(c0_) <= 0x7f) {
+ Token::Value token = static_cast<Token::Value>(one_char_tokens[c0_]);
+ if (token != Token::ILLEGAL) {
+ int pos = source_pos();
+ next_.token = token;
+ next_.location.beg_pos = pos;
+ next_.location.end_pos = pos + 1;
+ Advance();
+ return current_.token;
+ }
+ }
+ Scan();
+ return current_.token;
+}
+
+
+static inline bool IsByteOrderMark(uc32 c) {
+ // The Unicode value U+FFFE is guaranteed never to be assigned as a
+ // Unicode character; this implies that in a Unicode context the
+ // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF
+ // character expressed in little-endian byte order (since it could
+ // not be a U+FFFE character expressed in big-endian byte
+ // order). Nevertheless, we check for it to be compatible with
+ // Spidermonkey.
+ return c == 0xFEFF || c == 0xFFFE;
+}
+
+
+bool Scanner::SkipWhiteSpace() {
+ int start_position = source_pos();
+
+ while (true) {
+ // We treat byte-order marks (BOMs) as whitespace for better
+ // compatibility with Spidermonkey and other JavaScript engines.
+ while (unicode_cache_->IsWhiteSpace(c0_) || IsByteOrderMark(c0_)) {
+ // IsWhiteSpace() includes line terminators!
+ if (unicode_cache_->IsLineTerminator(c0_)) {
+ // Ignore line terminators, but remember them. This is necessary
+ // for automatic semicolon insertion.
+ has_line_terminator_before_next_ = true;
+ }
+ Advance();
+ }
+
+ // If there is an HTML comment end '-->' at the beginning of a
+ // line (with only whitespace in front of it), we treat the rest
+ // of the line as a comment. This is in line with the way
+ // SpiderMonkey handles it.
+ if (c0_ == '-' && has_line_terminator_before_next_) {
+ Advance();
+ if (c0_ == '-') {
+ Advance();
+ if (c0_ == '>') {
+ // Treat the rest of the line as a comment.
+ SkipSingleLineComment();
+ // Continue skipping white space after the comment.
+ continue;
+ }
+ PushBack('-'); // undo Advance()
+ }
+ PushBack('-'); // undo Advance()
+ }
+ // Return whether or not we skipped any characters.
+ return source_pos() != start_position;
+ }
+}
+
+
+Token::Value Scanner::SkipSingleLineComment() {
+ Advance();
+
+ // The line terminator at the end of the line is not considered
+ // to be part of the single-line comment; it is recognized
+ // separately by the lexical grammar and becomes part of the
+ // stream of input elements for the syntactic grammar (see
+ // ECMA-262, section 7.4).
+ while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) {
+ Advance();
+ }
+
+ return Token::WHITESPACE;
+}
+
+
+Token::Value Scanner::SkipMultiLineComment() {
+ ASSERT(c0_ == '*');
+ Advance();
+
+ while (c0_ >= 0) {
+ uc32 ch = c0_;
+ Advance();
+ if (unicode_cache_->IsLineTerminator(ch)) {
+ // Following ECMA-262, section 7.4, a comment containing
+ // a newline will make the comment count as a line-terminator.
+ has_multiline_comment_before_next_ = true;
+ }
+ // If we have reached the end of the multi-line comment, we
+ // consume the '/' and insert a whitespace. This way all
+ // multi-line comments are treated as whitespace.
+ if (ch == '*' && c0_ == '/') {
+ c0_ = ' ';
+ return Token::WHITESPACE;
+ }
+ }
+
+ // Unterminated multi-line comment.
+ return Token::ILLEGAL;
+}
+
+
+Token::Value Scanner::ScanHtmlComment() {
+ // Check for <!-- comments.
+ ASSERT(c0_ == '!');
+ Advance();
+ if (c0_ == '-') {
+ Advance();
+ if (c0_ == '-') return SkipSingleLineComment();
+ PushBack('-'); // undo Advance()
+ }
+ PushBack('!'); // undo Advance()
+ ASSERT(c0_ == '!');
+ return Token::LT;
+}
+
+
+void Scanner::Scan() {
+ next_.literal_chars = NULL;
+ Token::Value token;
+ do {
+ // Remember the position of the next token
+ next_.location.beg_pos = source_pos();
+
+ switch (c0_) {
+ case ' ':
+ case '\t':
+ Advance();
+ token = Token::WHITESPACE;
+ break;
+
+ case '\n':
+ Advance();
+ has_line_terminator_before_next_ = true;
+ token = Token::WHITESPACE;
+ break;
+
+ case '"': case '\'':
+ token = ScanString();
+ break;
+
+ case '<':
+ // < <= << <<= <!--
+ Advance();
+ if (c0_ == '=') {
+ token = Select(Token::LTE);
+ } else if (c0_ == '<') {
+ token = Select('=', Token::ASSIGN_SHL, Token::SHL);
+ } else if (c0_ == '!') {
+ token = ScanHtmlComment();
+ } else {
+ token = Token::LT;
+ }
+ break;
+
+ case '>':
+ // > >= >> >>= >>> >>>=
+ Advance();
+ if (c0_ == '=') {
+ token = Select(Token::GTE);
+ } else if (c0_ == '>') {
+ // >> >>= >>> >>>=
+ Advance();
+ if (c0_ == '=') {
+ token = Select(Token::ASSIGN_SAR);
+ } else if (c0_ == '>') {
+ token = Select('=', Token::ASSIGN_SHR, Token::SHR);
+ } else {
+ token = Token::SAR;
+ }
+ } else {
+ token = Token::GT;
+ }
+ break;
+
+ case '=':
+ // = == ===
+ Advance();
+ if (c0_ == '=') {
+ token = Select('=', Token::EQ_STRICT, Token::EQ);
+ } else {
+ token = Token::ASSIGN;
+ }
+ break;
+
+ case '!':
+ // ! != !==
+ Advance();
+ if (c0_ == '=') {
+ token = Select('=', Token::NE_STRICT, Token::NE);
+ } else {
+ token = Token::NOT;
+ }
+ break;
+
+ case '+':
+ // + ++ +=
+ Advance();
+ if (c0_ == '+') {
+ token = Select(Token::INC);
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_ADD);
+ } else {
+ token = Token::ADD;
+ }
+ break;
+
+ case '-':
+ // - -- --> -=
+ Advance();
+ if (c0_ == '-') {
+ Advance();
+ if (c0_ == '>' && has_line_terminator_before_next_) {
+ // For compatibility with SpiderMonkey, we skip lines that
+ // start with an HTML comment end '-->'.
+ token = SkipSingleLineComment();
+ } else {
+ token = Token::DEC;
+ }
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_SUB);
+ } else {
+ token = Token::SUB;
+ }
+ break;
+
+ case '*':
+ // * *=
+ token = Select('=', Token::ASSIGN_MUL, Token::MUL);
+ break;
+
+ case '%':
+ // % %=
+ token = Select('=', Token::ASSIGN_MOD, Token::MOD);
+ break;
+
+ case '/':
+ // / // /* /=
+ Advance();
+ if (c0_ == '/') {
+ token = SkipSingleLineComment();
+ } else if (c0_ == '*') {
+ token = SkipMultiLineComment();
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_DIV);
+ } else {
+ token = Token::DIV;
+ }
+ break;
+
+ case '&':
+ // & && &=
+ Advance();
+ if (c0_ == '&') {
+ token = Select(Token::AND);
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_BIT_AND);
+ } else {
+ token = Token::BIT_AND;
+ }
+ break;
+
+ case '|':
+ // | || |=
+ Advance();
+ if (c0_ == '|') {
+ token = Select(Token::OR);
+ } else if (c0_ == '=') {
+ token = Select(Token::ASSIGN_BIT_OR);
+ } else {
+ token = Token::BIT_OR;
+ }
+ break;
+
+ case '^':
+ // ^ ^=
+ token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
+ break;
+
+ case '.':
+ // . Number
+ Advance();
+ if (IsDecimalDigit(c0_)) {
+ token = ScanNumber(true);
+ } else {
+ token = Token::PERIOD;
+ }
+ break;
+
+ case ':':
+ token = Select(Token::COLON);
+ break;
+
+ case ';':
+ token = Select(Token::SEMICOLON);
+ break;
+
+ case ',':
+ token = Select(Token::COMMA);
+ break;
+
+ case '(':
+ token = Select(Token::LPAREN);
+ break;
+
+ case ')':
+ token = Select(Token::RPAREN);
+ break;
+
+ case '[':
+ token = Select(Token::LBRACK);
+ break;
+
+ case ']':
+ token = Select(Token::RBRACK);
+ break;
+
+ case '{':
+ token = Select(Token::LBRACE);
+ break;
+
+ case '}':
+ token = Select(Token::RBRACE);
+ break;
+
+ case '?':
+ token = Select(Token::CONDITIONAL);
+ break;
+
+ case '~':
+ token = Select(Token::BIT_NOT);
+ break;
+
+ default:
+ if (unicode_cache_->IsIdentifierStart(c0_)) {
+ token = ScanIdentifierOrKeyword();
+ } else if (IsDecimalDigit(c0_)) {
+ token = ScanNumber(false);
+ } else if (SkipWhiteSpace()) {
+ token = Token::WHITESPACE;
+ } else if (c0_ < 0) {
+ token = Token::EOS;
+ } else {
+ token = Select(Token::ILLEGAL);
+ }
+ break;
+ }
+
+ // Continue scanning for tokens as long as we're just skipping
+ // whitespace.
+ } while (token == Token::WHITESPACE);
+
+ next_.location.end_pos = source_pos();
+ next_.token = token;
+}
+
+
+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;
+ int current_pos = source_pos();
+ ASSERT_EQ(next_.location.end_pos, current_pos);
+ // Positions inside the lookahead token aren't supported.
+ ASSERT(pos >= current_pos);
+ if (pos != current_pos) {
+ source_->SeekForward(pos - source_->pos());
+ Advance();
+ // This function is only called to seek to the location
+ // of the end of a function (at the "}" token). It doesn't matter
+ // whether there was a line terminator in the part we skip.
+ has_line_terminator_before_next_ = false;
+ has_multiline_comment_before_next_ = false;
+ }
+ Scan();
+}
+
+
+bool Scanner::ScanEscape() {
+ uc32 c = c0_;
+ Advance();
+
+ // Skip escaped newlines.
+ if (unicode_cache_->IsLineTerminator(c)) {
+ // Allow CR+LF newlines in multiline string literals.
+ if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance();
+ // Allow LF+CR newlines in multiline string literals.
+ if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance();
+ return true;
+ }
+
+ switch (c) {
+ case '\'': // fall through
+ case '"' : // fall through
+ case '\\': break;
+ case 'b' : c = '\b'; break;
+ case 'f' : c = '\f'; break;
+ case 'n' : c = '\n'; break;
+ case 'r' : c = '\r'; break;
+ case 't' : c = '\t'; break;
+ case 'u' : {
+ c = ScanHexNumber(4);
+ if (c < 0) return false;
+ break;
+ }
+ case 'v' : c = '\v'; break;
+ case 'x' : {
+ c = ScanHexNumber(2);
+ if (c < 0) return false;
+ break;
+ }
+ case '0' : // fall through
+ case '1' : // fall through
+ case '2' : // fall through
+ case '3' : // fall through
+ case '4' : // fall through
+ case '5' : // fall through
+ case '6' : // fall through
+ case '7' : c = ScanOctalEscape(c, 2); break;
+ }
+
+ // According to ECMA-262, section 7.8.4, characters not covered by the
+ // above cases should be illegal, but they are commonly handled as
+ // non-escaped characters by JS VMs.
+ AddLiteralChar(c);
+ return true;
+}
+
+
+// Octal escapes of the forms '\0xx' and '\xxx' are not a part of
+// ECMA-262. Other JS VMs support them.
+uc32 Scanner::ScanOctalEscape(uc32 c, int length) {
+ uc32 x = c - '0';
+ int i = 0;
+ for (; i < length; i++) {
+ int d = c0_ - '0';
+ if (d < 0 || d > 7) break;
+ int nx = x * 8 + d;
+ if (nx >= 256) break;
+ x = nx;
+ Advance();
+ }
+ // Anything except '\0' is an octal escape sequence, illegal in strict mode.
+ // Remember the position of octal escape sequences so that an error
+ // can be reported later (in strict mode).
+ // We don't report the error immediately, because the octal escape can
+ // occur before the "use strict" directive.
+ if (c != '0' || i > 0) {
+ octal_pos_ = Location(source_pos() - i - 1, source_pos() - 1);
+ }
+ return x;
+}
+
+
+Token::Value Scanner::ScanString() {
+ uc32 quote = c0_;
+ Advance(); // consume quote
+
+ LiteralScope literal(this);
+ while (c0_ != quote && c0_ >= 0
+ && !unicode_cache_->IsLineTerminator(c0_)) {
+ uc32 c = c0_;
+ Advance();
+ if (c == '\\') {
+ if (c0_ < 0 || !ScanEscape()) return Token::ILLEGAL;
+ } else {
+ AddLiteralChar(c);
+ }
+ }
+ if (c0_ != quote) return Token::ILLEGAL;
+ literal.Complete();
+
+ Advance(); // consume quote
+ return Token::STRING;
+}
+
+
+void Scanner::ScanDecimalDigits() {
+ while (IsDecimalDigit(c0_))
+ AddLiteralCharAdvance();
+}
+
+
+Token::Value Scanner::ScanNumber(bool seen_period) {
+ ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction
+
+ enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL;
+
+ LiteralScope literal(this);
+ if (seen_period) {
+ // we have already seen a decimal point of the float
+ AddLiteralChar('.');
+ ScanDecimalDigits(); // we know we have at least one digit
+
+ } else {
+ // if the first character is '0' we must check for octals and hex
+ if (c0_ == '0') {
+ int start_pos = source_pos(); // For reporting octal positions.
+ AddLiteralCharAdvance();
+
+ // either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or
+ // an octal number.
+ if (c0_ == 'x' || c0_ == 'X') {
+ // hex number
+ kind = HEX;
+ AddLiteralCharAdvance();
+ if (!IsHexDigit(c0_)) {
+ // we must have at least one hex digit after 'x'/'X'
+ return Token::ILLEGAL;
+ }
+ while (IsHexDigit(c0_)) {
+ AddLiteralCharAdvance();
+ }
+ } else if (harmony_numeric_literals_ && (c0_ == 'o' || c0_ == 'O')) {
+ kind = OCTAL;
+ AddLiteralCharAdvance();
+ if (!IsOctalDigit(c0_)) {
+ // we must have at least one octal digit after 'o'/'O'
+ return Token::ILLEGAL;
+ }
+ while (IsOctalDigit(c0_)) {
+ AddLiteralCharAdvance();
+ }
+ } else if (harmony_numeric_literals_ && (c0_ == 'b' || c0_ == 'B')) {
+ kind = BINARY;
+ AddLiteralCharAdvance();
+ if (!IsBinaryDigit(c0_)) {
+ // we must have at least one binary digit after 'b'/'B'
+ return Token::ILLEGAL;
+ }
+ while (IsBinaryDigit(c0_)) {
+ AddLiteralCharAdvance();
+ }
+ } else if ('0' <= c0_ && c0_ <= '7') {
+ // (possible) octal number
+ kind = IMPLICIT_OCTAL;
+ while (true) {
+ if (c0_ == '8' || c0_ == '9') {
+ kind = DECIMAL;
+ break;
+ }
+ if (c0_ < '0' || '7' < c0_) {
+ // Octal literal finished.
+ octal_pos_ = Location(start_pos, source_pos());
+ break;
+ }
+ AddLiteralCharAdvance();
+ }
+ }
+ }
+
+ // Parse decimal digits and allow trailing fractional part.
+ if (kind == DECIMAL) {
+ ScanDecimalDigits(); // optional
+ if (c0_ == '.') {
+ AddLiteralCharAdvance();
+ ScanDecimalDigits(); // optional
+ }
+ }
+ }
+
+ // scan exponent, if any
+ if (c0_ == 'e' || c0_ == 'E') {
+ ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
+ if (kind != DECIMAL) return Token::ILLEGAL;
+ // scan exponent
+ AddLiteralCharAdvance();
+ if (c0_ == '+' || c0_ == '-')
+ AddLiteralCharAdvance();
+ if (!IsDecimalDigit(c0_)) {
+ // we must have at least one decimal digit after 'e'/'E'
+ return Token::ILLEGAL;
+ }
+ ScanDecimalDigits();
+ }
+
+ // The source character immediately following a numeric literal must
+ // not be an identifier start or a decimal digit; see ECMA-262
+ // section 7.8.3, page 17 (note that we read only one decimal digit
+ // if the value is 0).
+ if (IsDecimalDigit(c0_) || unicode_cache_->IsIdentifierStart(c0_))
+ return Token::ILLEGAL;
+
+ literal.Complete();
+
+ return Token::NUMBER;
+}
+
+
+uc32 Scanner::ScanIdentifierUnicodeEscape() {
+ Advance();
+ if (c0_ != 'u') return -1;
+ Advance();
+ uc32 result = ScanHexNumber(4);
+ if (result < 0) PushBack('u');
+ return result;
+}
+
+
+// ----------------------------------------------------------------------------
+// Keyword Matcher
+
+#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
+ KEYWORD_GROUP('b') \
+ KEYWORD("break", Token::BREAK) \
+ KEYWORD_GROUP('c') \
+ KEYWORD("case", Token::CASE) \
+ KEYWORD("catch", Token::CATCH) \
+ KEYWORD("class", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("const", Token::CONST) \
+ KEYWORD("continue", Token::CONTINUE) \
+ KEYWORD_GROUP('d') \
+ KEYWORD("debugger", Token::DEBUGGER) \
+ KEYWORD("default", Token::DEFAULT) \
+ KEYWORD("delete", Token::DELETE) \
+ KEYWORD("do", Token::DO) \
+ KEYWORD_GROUP('e') \
+ KEYWORD("else", Token::ELSE) \
+ KEYWORD("enum", 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) \
+ KEYWORD("finally", Token::FINALLY) \
+ KEYWORD("for", Token::FOR) \
+ KEYWORD("function", Token::FUNCTION) \
+ KEYWORD_GROUP('i') \
+ KEYWORD("if", Token::IF) \
+ KEYWORD("implements", Token::FUTURE_STRICT_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_scoping \
+ ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD_GROUP('n') \
+ KEYWORD("new", Token::NEW) \
+ KEYWORD("null", Token::NULL_LITERAL) \
+ KEYWORD_GROUP('p') \
+ KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD_GROUP('r') \
+ KEYWORD("return", Token::RETURN) \
+ KEYWORD_GROUP('s') \
+ KEYWORD("static", Token::FUTURE_STRICT_RESERVED_WORD) \
+ KEYWORD("super", Token::FUTURE_RESERVED_WORD) \
+ KEYWORD("switch", Token::SWITCH) \
+ KEYWORD_GROUP('t') \
+ KEYWORD("this", Token::THIS) \
+ KEYWORD("throw", Token::THROW) \
+ KEYWORD("true", Token::TRUE_LITERAL) \
+ KEYWORD("try", Token::TRY) \
+ KEYWORD("typeof", Token::TYPEOF) \
+ KEYWORD_GROUP('v') \
+ KEYWORD("var", Token::VAR) \
+ KEYWORD("void", Token::VOID) \
+ KEYWORD_GROUP('w') \
+ KEYWORD("while", Token::WHILE) \
+ KEYWORD("with", Token::WITH) \
+ KEYWORD_GROUP('y') \
+ KEYWORD("yield", Token::YIELD)
+
+
+static Token::Value KeywordOrIdentifierToken(const char* input,
+ int input_length,
+ bool harmony_scoping,
+ bool harmony_modules) {
+ ASSERT(input_length >= 1);
+ const int kMinLength = 2;
+ const int kMaxLength = 10;
+ if (input_length < kMinLength || input_length > kMaxLength) {
+ return Token::IDENTIFIER;
+ }
+ switch (input[0]) {
+ default:
+#define KEYWORD_GROUP_CASE(ch) \
+ break; \
+ case ch:
+#define KEYWORD(keyword, token) \
+ { \
+ /* 'keyword' is a char array, so sizeof(keyword) is */ \
+ /* strlen(keyword) plus 1 for the NUL char. */ \
+ const int keyword_length = sizeof(keyword) - 1; \
+ STATIC_ASSERT(keyword_length >= kMinLength); \
+ STATIC_ASSERT(keyword_length <= kMaxLength); \
+ if (input_length == keyword_length && \
+ input[1] == keyword[1] && \
+ (keyword_length <= 2 || input[2] == keyword[2]) && \
+ (keyword_length <= 3 || input[3] == keyword[3]) && \
+ (keyword_length <= 4 || input[4] == keyword[4]) && \
+ (keyword_length <= 5 || input[5] == keyword[5]) && \
+ (keyword_length <= 6 || input[6] == keyword[6]) && \
+ (keyword_length <= 7 || input[7] == keyword[7]) && \
+ (keyword_length <= 8 || input[8] == keyword[8]) && \
+ (keyword_length <= 9 || input[9] == keyword[9])) { \
+ return token; \
+ } \
+ }
+ KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
+ }
+ return Token::IDENTIFIER;
+}
+
+
+Token::Value Scanner::ScanIdentifierOrKeyword() {
+ ASSERT(unicode_cache_->IsIdentifierStart(c0_));
+ LiteralScope literal(this);
+ // Scan identifier start character.
+ if (c0_ == '\\') {
+ uc32 c = ScanIdentifierUnicodeEscape();
+ // Only allow legal identifier start characters.
+ if (c < 0 ||
+ c == '\\' || // No recursive escapes.
+ !unicode_cache_->IsIdentifierStart(c)) {
+ return Token::ILLEGAL;
+ }
+ AddLiteralChar(c);
+ return ScanIdentifierSuffix(&literal);
+ }
+
+ uc32 first_char = c0_;
+ Advance();
+ AddLiteralChar(first_char);
+
+ // Scan the rest of the identifier characters.
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
+ if (c0_ != '\\') {
+ uc32 next_char = c0_;
+ Advance();
+ AddLiteralChar(next_char);
+ continue;
+ }
+ // Fallthrough if no longer able to complete keyword.
+ return ScanIdentifierSuffix(&literal);
+ }
+
+ literal.Complete();
+
+ if (next_.literal_chars->is_ascii()) {
+ Vector<const char> chars = next_.literal_chars->ascii_literal();
+ return KeywordOrIdentifierToken(chars.start(),
+ chars.length(),
+ harmony_scoping_,
+ harmony_modules_);
+ }
+
+ return Token::IDENTIFIER;
+}
+
+
+Token::Value Scanner::ScanIdentifierSuffix(LiteralScope* literal) {
+ // Scan the rest of the identifier characters.
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
+ if (c0_ == '\\') {
+ uc32 c = ScanIdentifierUnicodeEscape();
+ // Only allow legal identifier part characters.
+ if (c < 0 ||
+ c == '\\' ||
+ !unicode_cache_->IsIdentifierPart(c)) {
+ return Token::ILLEGAL;
+ }
+ AddLiteralChar(c);
+ } else {
+ AddLiteralChar(c0_);
+ Advance();
+ }
+ }
+ literal->Complete();
+
+ return Token::IDENTIFIER;
+}
+
+
+bool Scanner::ScanRegExpPattern(bool seen_equal) {
+ // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags
+ bool in_character_class = false;
+
+ // Previous token is either '/' or '/=', in the second case, the
+ // pattern starts at =.
+ next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1);
+ next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0);
+
+ // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5,
+ // the scanner should pass uninterpreted bodies to the RegExp
+ // constructor.
+ LiteralScope literal(this);
+ if (seen_equal) {
+ AddLiteralChar('=');
+ }
+
+ while (c0_ != '/' || in_character_class) {
+ if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false;
+ if (c0_ == '\\') { // Escape sequence.
+ AddLiteralCharAdvance();
+ if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false;
+ AddLiteralCharAdvance();
+ // If the escape allows more characters, i.e., \x??, \u????, or \c?,
+ // only "safe" characters are allowed (letters, digits, underscore),
+ // otherwise the escape isn't valid and the invalid character has
+ // its normal meaning. I.e., we can just continue scanning without
+ // worrying whether the following characters are part of the escape
+ // or not, since any '/', '\\' or '[' is guaranteed to not be part
+ // of the escape sequence.
+
+ // TODO(896): At some point, parse RegExps more throughly to capture
+ // octal esacpes in strict mode.
+ } else { // Unescaped character.
+ if (c0_ == '[') in_character_class = true;
+ if (c0_ == ']') in_character_class = false;
+ AddLiteralCharAdvance();
+ }
+ }
+ Advance(); // consume '/'
+
+ literal.Complete();
+
+ return true;
+}
+
+
+bool Scanner::ScanLiteralUnicodeEscape() {
+ ASSERT(c0_ == '\\');
+ uc32 chars_read[6] = {'\\', 'u', 0, 0, 0, 0};
+ Advance();
+ int i = 1;
+ if (c0_ == 'u') {
+ i++;
+ while (i < 6) {
+ Advance();
+ if (!IsHexDigit(c0_)) break;
+ chars_read[i] = c0_;
+ i++;
+ }
+ }
+ if (i < 6) {
+ // Incomplete escape. Undo all advances and return false.
+ while (i > 0) {
+ i--;
+ PushBack(chars_read[i]);
+ }
+ return false;
+ }
+ // Complete escape. Add all chars to current literal buffer.
+ for (int i = 0; i < 6; i++) {
+ AddLiteralChar(chars_read[i]);
+ }
+ return true;
+}
+
+
+bool Scanner::ScanRegExpFlags() {
+ // Scan regular expression flags.
+ LiteralScope literal(this);
+ while (unicode_cache_->IsIdentifierPart(c0_)) {
+ if (c0_ != '\\') {
+ AddLiteralCharAdvance();
+ } else {
+ if (!ScanLiteralUnicodeEscape()) {
+ break;
+ }
+ Advance();
+ }
+ }
+ literal.Complete();
+
+ next_.location.end_pos = source_pos() - 1;
+ return true;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/scanner.h b/chromium/v8/src/scanner.h
new file mode 100644
index 00000000000..d7328085b79
--- /dev/null
+++ b/chromium/v8/src/scanner.h
@@ -0,0 +1,571 @@
+// 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.
+
+// Features shared by parsing and pre-parsing scanners.
+
+#ifndef V8_SCANNER_H_
+#define V8_SCANNER_H_
+
+#include "allocation.h"
+#include "char-predicates.h"
+#include "checks.h"
+#include "globals.h"
+#include "token.h"
+#include "unicode-inl.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+
+// 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) {
+ c -= '0';
+ if (static_cast<unsigned>(c) <= 9) return c;
+ c = (c | 0x20) - ('a' - '0'); // detect 0x11..0x16 and 0x31..0x36.
+ if (static_cast<unsigned>(c) <= 5) return c + 10;
+ return -1;
+}
+
+
+// ---------------------------------------------------------------------
+// Buffered stream of UTF-16 code units, using an internal UTF-16 buffer.
+// A code unit is a 16 bit value representing either a 16 bit code point
+// or one part of a surrogate pair that make a single 21 bit code point.
+
+class Utf16CharacterStream {
+ public:
+ Utf16CharacterStream() : pos_(0) { }
+ virtual ~Utf16CharacterStream() { }
+
+ // Returns and advances past the next UTF-16 code unit in the input
+ // stream. If there are no more code units, it returns a negative
+ // value.
+ inline uc32 Advance() {
+ if (buffer_cursor_ < buffer_end_ || ReadBlock()) {
+ pos_++;
+ return static_cast<uc32>(*(buffer_cursor_++));
+ }
+ // Note: currently the following increment is necessary to avoid a
+ // parser problem! The scanner treats the final kEndOfInput as
+ // a code unit with a position, and does math relative to that
+ // position.
+ pos_++;
+
+ return kEndOfInput;
+ }
+
+ // Return the current position in the code unit stream.
+ // Starts at zero.
+ inline unsigned pos() const { return pos_; }
+
+ // Skips forward past the next code_unit_count UTF-16 code units
+ // in the input, or until the end of input if that comes sooner.
+ // Returns the number of code units actually skipped. If less
+ // than code_unit_count,
+ inline unsigned SeekForward(unsigned code_unit_count) {
+ unsigned buffered_chars =
+ static_cast<unsigned>(buffer_end_ - buffer_cursor_);
+ if (code_unit_count <= buffered_chars) {
+ buffer_cursor_ += code_unit_count;
+ pos_ += code_unit_count;
+ return code_unit_count;
+ }
+ return SlowSeekForward(code_unit_count);
+ }
+
+ // Pushes back the most recently read UTF-16 code unit (or negative
+ // value if at end of input), i.e., the value returned by the most recent
+ // call to Advance.
+ // Must not be used right after calling SeekForward.
+ virtual void PushBack(int32_t code_unit) = 0;
+
+ protected:
+ static const uc32 kEndOfInput = -1;
+
+ // Ensures that the buffer_cursor_ points to the code_unit at
+ // position pos_ of the input, if possible. If the position
+ // is at or after the end of the input, return false. If there
+ // are more code_units available, return true.
+ virtual bool ReadBlock() = 0;
+ virtual unsigned SlowSeekForward(unsigned code_unit_count) = 0;
+
+ const uc16* buffer_cursor_;
+ const uc16* buffer_end_;
+ unsigned pos_;
+};
+
+
+class UnicodeCache {
+// ---------------------------------------------------------------------
+// Caching predicates used by scanners.
+ public:
+ UnicodeCache() {}
+ typedef unibrow::Utf8Decoder<512> Utf8Decoder;
+
+ StaticResource<Utf8Decoder>* utf8_decoder() {
+ return &utf8_decoder_;
+ }
+
+ bool IsIdentifierStart(unibrow::uchar c) { return kIsIdentifierStart.get(c); }
+ bool IsIdentifierPart(unibrow::uchar c) { return kIsIdentifierPart.get(c); }
+ bool IsLineTerminator(unibrow::uchar c) { return kIsLineTerminator.get(c); }
+ bool IsWhiteSpace(unibrow::uchar c) { return kIsWhiteSpace.get(c); }
+
+ private:
+ unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart;
+ unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart;
+ unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator;
+ unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace;
+ StaticResource<Utf8Decoder> utf8_decoder_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnicodeCache);
+};
+
+
+// ----------------------------------------------------------------------------
+// LiteralBuffer - Collector of chars of literals.
+
+class LiteralBuffer {
+ public:
+ LiteralBuffer() : is_ascii_(true), position_(0), backing_store_() { }
+
+ ~LiteralBuffer() {
+ if (backing_store_.length() > 0) {
+ backing_store_.Dispose();
+ }
+ }
+
+ INLINE(void AddChar(uint32_t code_unit)) {
+ if (position_ >= backing_store_.length()) ExpandBuffer();
+ if (is_ascii_) {
+ if (code_unit <= unibrow::Latin1::kMaxChar) {
+ backing_store_[position_] = static_cast<byte>(code_unit);
+ position_ += kOneByteSize;
+ return;
+ }
+ ConvertToUtf16();
+ }
+ ASSERT(code_unit < 0x10000u);
+ *reinterpret_cast<uc16*>(&backing_store_[position_]) = code_unit;
+ position_ += kUC16Size;
+ }
+
+ bool is_ascii() { return is_ascii_; }
+
+ bool is_contextual_keyword(Vector<const char> keyword) {
+ return is_ascii() && keyword.length() == position_ &&
+ (memcmp(keyword.start(), backing_store_.start(), position_) == 0);
+ }
+
+ Vector<const uc16> utf16_literal() {
+ ASSERT(!is_ascii_);
+ ASSERT((position_ & 0x1) == 0);
+ return Vector<const uc16>(
+ reinterpret_cast<const uc16*>(backing_store_.start()),
+ position_ >> 1);
+ }
+
+ Vector<const char> ascii_literal() {
+ ASSERT(is_ascii_);
+ return Vector<const char>(
+ reinterpret_cast<const char*>(backing_store_.start()),
+ position_);
+ }
+
+ int length() {
+ return is_ascii_ ? position_ : (position_ >> 1);
+ }
+
+ void Reset() {
+ position_ = 0;
+ is_ascii_ = true;
+ }
+
+ private:
+ static const int kInitialCapacity = 16;
+ static const int kGrowthFactory = 4;
+ static const int kMinConversionSlack = 256;
+ static const int kMaxGrowth = 1 * MB;
+ inline int NewCapacity(int min_capacity) {
+ int capacity = Max(min_capacity, backing_store_.length());
+ int new_capacity = Min(capacity * kGrowthFactory, capacity + kMaxGrowth);
+ return new_capacity;
+ }
+
+ void ExpandBuffer() {
+ Vector<byte> new_store = Vector<byte>::New(NewCapacity(kInitialCapacity));
+ OS::MemCopy(new_store.start(), backing_store_.start(), position_);
+ backing_store_.Dispose();
+ backing_store_ = new_store;
+ }
+
+ void ConvertToUtf16() {
+ ASSERT(is_ascii_);
+ Vector<byte> new_store;
+ int new_content_size = position_ * kUC16Size;
+ if (new_content_size >= backing_store_.length()) {
+ // Ensure room for all currently read code units as UC16 as well
+ // as the code unit about to be stored.
+ new_store = Vector<byte>::New(NewCapacity(new_content_size));
+ } else {
+ new_store = backing_store_;
+ }
+ uint8_t* src = backing_store_.start();
+ uc16* dst = reinterpret_cast<uc16*>(new_store.start());
+ for (int i = position_ - 1; i >= 0; i--) {
+ dst[i] = src[i];
+ }
+ if (new_store.start() != backing_store_.start()) {
+ backing_store_.Dispose();
+ backing_store_ = new_store;
+ }
+ position_ = new_content_size;
+ is_ascii_ = false;
+ }
+
+ bool is_ascii_;
+ int position_;
+ Vector<byte> backing_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(LiteralBuffer);
+};
+
+
+// ----------------------------------------------------------------------------
+// JavaScript Scanner.
+
+class Scanner {
+ public:
+ // Scoped helper for literal recording. Automatically drops the literal
+ // if aborting the scanning before it's complete.
+ class LiteralScope {
+ public:
+ 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_;
+ };
+
+ // 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) { }
+
+ bool IsValid() const {
+ return beg_pos >= 0 && end_pos >= beg_pos;
+ }
+
+ static Location invalid() { return Location(-1, -1); }
+
+ int beg_pos;
+ int end_pos;
+ };
+
+ // -1 is outside of the range of any real source code.
+ static const int kNoOctalLocation = -1;
+
+ explicit Scanner(UnicodeCache* scanner_contants);
+
+ void Initialize(Utf16CharacterStream* 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 last returned by Next()).
+ Location location() const { return current_.location; }
+ // Returns the literal string, if any, for the current token (the
+ // 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().
+ Vector<const char> literal_ascii_string() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->ascii_literal();
+ }
+ Vector<const uc16> literal_utf16_string() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->utf16_literal();
+ }
+ bool is_literal_ascii() {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->is_ascii();
+ }
+ bool is_literal_contextual_keyword(Vector<const char> keyword) {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->is_contextual_keyword(keyword);
+ }
+ int literal_length() const {
+ ASSERT_NOT_NULL(current_.literal_chars);
+ return current_.literal_chars->length();
+ }
+
+ bool literal_contains_escapes() const {
+ Location location = current_.location;
+ int source_length = (location.end_pos - location.beg_pos);
+ if (current_.token == Token::STRING) {
+ // Subtract delimiters.
+ source_length -= 2;
+ }
+ 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).
+ Vector<const char> next_literal_ascii_string() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->ascii_literal();
+ }
+ Vector<const uc16> next_literal_utf16_string() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->utf16_literal();
+ }
+ bool is_next_literal_ascii() {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->is_ascii();
+ }
+ bool is_next_contextual_keyword(Vector<const char> keyword) {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->is_contextual_keyword(keyword);
+ }
+ int next_literal_length() const {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ return next_.literal_chars->length();
+ }
+
+ UnicodeCache* unicode_cache() { return unicode_cache_; }
+
+ static const int kCharacterLookaheadBufferSize = 1;
+
+ // 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;
+ }
+ bool HarmonyNumericLiterals() const {
+ return harmony_numeric_literals_;
+ }
+ void SetHarmonyNumericLiterals(bool numeric_literals) {
+ harmony_numeric_literals_ = numeric_literals;
+ }
+
+ // 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();
+
+ private:
+ // The current and look-ahead token.
+ struct TokenDesc {
+ Token::Value token;
+ Location location;
+ LiteralBuffer* literal_chars;
+ };
+
+ // Call this after setting source_ to the input.
+ void Init() {
+ // Set c0_ (one character ahead)
+ STATIC_ASSERT(kCharacterLookaheadBufferSize == 1);
+ Advance();
+ // Initialize current_ to not refer to a literal.
+ current_.literal_chars = NULL;
+ }
+
+ // Literal buffer support
+ inline void StartLiteral() {
+ LiteralBuffer* free_buffer = (current_.literal_chars == &literal_buffer1_) ?
+ &literal_buffer2_ : &literal_buffer1_;
+ free_buffer->Reset();
+ next_.literal_chars = free_buffer;
+ }
+
+ INLINE(void AddLiteralChar(uc32 c)) {
+ ASSERT_NOT_NULL(next_.literal_chars);
+ next_.literal_chars->AddChar(c);
+ }
+
+ // Complete scanning of a literal.
+ inline void TerminateLiteral() {
+ // Does nothing in the current implementation.
+ }
+
+ // Stops scanning of a literal and drop the collected characters,
+ // e.g., due to an encountered error.
+ inline void DropLiteral() {
+ next_.literal_chars = NULL;
+ }
+
+ inline void AddLiteralCharAdvance() {
+ AddLiteralChar(c0_);
+ Advance();
+ }
+
+ // Low-level scanning support.
+ void Advance() { c0_ = source_->Advance(); }
+ void PushBack(uc32 ch) {
+ source_->PushBack(c0_);
+ c0_ = ch;
+ }
+
+ inline Token::Value Select(Token::Value tok) {
+ Advance();
+ return tok;
+ }
+
+ inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_) {
+ Advance();
+ if (c0_ == next) {
+ Advance();
+ return then;
+ } else {
+ return else_;
+ }
+ }
+
+ uc32 ScanHexNumber(int expected_length);
+
+ // Scans a single JavaScript token.
+ void Scan();
+
+ bool SkipWhiteSpace();
+ Token::Value SkipSingleLineComment();
+ Token::Value SkipMultiLineComment();
+ // Scans a possible HTML comment -- begins with '<!'.
+ Token::Value ScanHtmlComment();
+
+ void ScanDecimalDigits();
+ Token::Value ScanNumber(bool seen_period);
+ Token::Value ScanIdentifierOrKeyword();
+ Token::Value ScanIdentifierSuffix(LiteralScope* literal);
+
+ Token::Value ScanString();
+
+ // Scans an escape-sequence which is part of a string and adds the
+ // decoded character to the current literal. Returns true if a pattern
+ // is scanned.
+ bool ScanEscape();
+ // Decodes a Unicode escape-sequence which is part of an identifier.
+ // If the escape sequence cannot be decoded the result is kBadChar.
+ uc32 ScanIdentifierUnicodeEscape();
+ // Scans a Unicode escape-sequence and adds its characters,
+ // uninterpreted, to the current literal. Used for parsing RegExp
+ // 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 Utf16CharacterStream.
+ Utf16CharacterStream* 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.
+ bool has_line_terminator_before_next_;
+ // 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_scoping_;
+ // Whether we scan 'module', 'import', 'export' as keywords.
+ bool harmony_modules_;
+ // Whether we scan 0o777 and 0b111 as numbers.
+ bool harmony_numeric_literals_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SCANNER_H_
diff --git a/chromium/v8/src/scopeinfo.cc b/chromium/v8/src/scopeinfo.cc
new file mode 100644
index 00000000000..c9df1fb5801
--- /dev/null
+++ b/chromium/v8/src/scopeinfo.cc
@@ -0,0 +1,562 @@
+// 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 "scopeinfo.h"
+#include "scopes.h"
+
+#include "allocation-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+Handle<ScopeInfo> ScopeInfo::Create(Scope* scope, Zone* zone) {
+ // Collect stack and context locals.
+ ZoneList<Variable*> stack_locals(scope->StackLocalCount(), zone);
+ ZoneList<Variable*> context_locals(scope->ContextLocalCount(), zone);
+ 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()->proxy()->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 {
+ function_name_info = NONE;
+ function_variable_mode = VAR;
+ }
+
+ 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);
+
+ Factory* factory = Isolate::Current()->factory();
+ Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length);
+
+ // Encode the flags.
+ int flags = ScopeTypeField::encode(scope->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());
+ }
+
+ // 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());
+ }
+
+ // 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()->proxy()->var()->index();
+ scope_info->set(index++, *scope->function()->proxy()->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);
+ }
+
+ 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() ||
+ (scope->num_heap_slots() == kVariablePartIndex &&
+ scope_info->ContextLength() == 0));
+ return scope_info;
+}
+
+
+ScopeInfo* ScopeInfo::Empty(Isolate* isolate) {
+ return reinterpret_cast<ScopeInfo*>(isolate->heap()->empty_fixed_array());
+}
+
+
+ScopeType ScopeInfo::scope_type() {
+ ASSERT(length() > 0);
+ return ScopeTypeField::decode(Flags());
+}
+
+
+bool ScopeInfo::CallsEval() {
+ return length() > 0 && CallsEvalField::decode(Flags());
+}
+
+
+LanguageMode ScopeInfo::language_mode() {
+ return length() > 0 ? LanguageModeField::decode(Flags()) : CLASSIC_MODE;
+}
+
+
+int ScopeInfo::LocalCount() {
+ return StackLocalCount() + ContextLocalCount();
+}
+
+
+int ScopeInfo::StackSlotCount() {
+ if (length() > 0) {
+ bool function_name_stack_slot =
+ FunctionVariableField::decode(Flags()) == STACK;
+ return StackLocalCount() + (function_name_stack_slot ? 1 : 0);
+ }
+ return 0;
+}
+
+
+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 ||
+ scope_type() == WITH_SCOPE ||
+ (scope_type() == FUNCTION_SCOPE && CallsEval()) ||
+ scope_type() == MODULE_SCOPE;
+ if (has_context) {
+ return Context::MIN_CONTEXT_SLOTS + context_locals +
+ (function_name_context_slot ? 1 : 0);
+ }
+ }
+ return 0;
+}
+
+
+bool ScopeInfo::HasFunctionName() {
+ if (length() > 0) {
+ return NONE != FunctionVariableField::decode(Flags());
+ } else {
+ return false;
+ }
+}
+
+
+bool ScopeInfo::HasHeapAllocatedLocals() {
+ if (length() > 0) {
+ return ContextLocalCount() > 0;
+ } else {
+ return false;
+ }
+}
+
+
+bool ScopeInfo::HasContext() {
+ return ContextLength() > 0;
+}
+
+
+String* ScopeInfo::FunctionName() {
+ ASSERT(HasFunctionName());
+ return String::cast(get(FunctionNameEntryIndex()));
+}
+
+
+String* ScopeInfo::ParameterName(int var) {
+ ASSERT(0 <= var && var < ParameterCount());
+ int info_index = ParameterEntriesIndex() + var;
+ return String::cast(get(info_index));
+}
+
+
+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));
+}
+
+
+String* ScopeInfo::StackLocalName(int var) {
+ ASSERT(0 <= var && var < StackLocalCount());
+ int info_index = StackLocalEntriesIndex() + var;
+ return String::cast(get(info_index));
+}
+
+
+String* ScopeInfo::ContextLocalName(int var) {
+ ASSERT(0 <= var && var < ContextLocalCount());
+ int info_index = ContextLocalNameEntriesIndex() + var;
+ return String::cast(get(info_index));
+}
+
+
+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);
+}
+
+
+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 ScopeInfo::StackSlotIndex(String* name) {
+ ASSERT(name->IsInternalizedString());
+ if (length() > 0) {
+ int start = StackLocalEntriesIndex();
+ int end = StackLocalEntriesIndex() + StackLocalCount();
+ for (int i = start; i < end; ++i) {
+ if (name == get(i)) {
+ return i - start;
+ }
+ }
+ }
+ return -1;
+}
+
+
+int ScopeInfo::ContextSlotIndex(String* name,
+ VariableMode* mode,
+ InitializationFlag* init_flag) {
+ ASSERT(name->IsInternalizedString());
+ ASSERT(mode != NULL);
+ ASSERT(init_flag != NULL);
+ if (length() > 0) {
+ 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;
+ }
+ }
+ // Cache as not found. Mode and init flag don't matter.
+ context_slot_cache->Update(this, name, INTERNAL, kNeedsInitialization, -1);
+ }
+ return -1;
+}
+
+
+int ScopeInfo::ParameterIndex(String* name) {
+ ASSERT(name->IsInternalizedString());
+ if (length() > 0) {
+ // We must read parameters from the end since for
+ // multiply declared parameters the value of the
+ // last declaration of that parameter is used
+ // inside a function (and thus we need to look
+ // at the last index). Was bug# 1110337.
+ 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 ScopeInfo::FunctionContextSlotIndex(String* name, VariableMode* mode) {
+ ASSERT(name->IsInternalizedString());
+ ASSERT(mode != NULL);
+ if (length() > 0) {
+ if (FunctionVariableField::decode(Flags()) == CONTEXT &&
+ FunctionName() == name) {
+ *mode = FunctionVariableMode::decode(Flags());
+ return Smi::cast(get(FunctionNameEntryIndex() + 1))->value();
+ }
+ }
+ return -1;
+}
+
+
+bool ScopeInfo::CopyContextLocalsToScopeObject(
+ Isolate* isolate,
+ Handle<Context> context,
+ Handle<JSObject> scope_object) {
+ int local_count = ContextLocalCount();
+ if (local_count == 0) return true;
+ // Fill all context locals to the context extension.
+ int start = ContextLocalNameEntriesIndex();
+ int end = start + local_count;
+ for (int i = start; i < end; ++i) {
+ int context_index = Context::MIN_CONTEXT_SLOTS + i - start;
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(isolate,
+ scope_object,
+ Handle<String>(String::cast(get(i))),
+ Handle<Object>(context->get(context_index), isolate),
+ ::NONE,
+ kNonStrictMode),
+ false);
+ }
+ return true;
+}
+
+
+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 =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(data)) >> 2;
+ return static_cast<int>((addr_hash ^ name->Hash()) % kLength);
+}
+
+
+int ContextSlotCache::Lookup(Object* data,
+ String* name,
+ 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;
+}
+
+
+void ContextSlotCache::Update(Object* data,
+ String* name,
+ VariableMode mode,
+ InitializationFlag init_flag,
+ int slot_index) {
+ String* internalized_name;
+ ASSERT(slot_index > kNotFound);
+ if (HEAP->InternalizeStringIfExists(name, &internalized_name)) {
+ int index = Hash(data, internalized_name);
+ Key& key = keys_[index];
+ key.data = data;
+ key.name = internalized_name;
+ // Please note value only takes a uint as index.
+ values_[index] = Value(mode, init_flag, slot_index - kNotFound).raw();
+#ifdef DEBUG
+ ValidateEntry(data, name, mode, init_flag, slot_index);
+#endif
+ }
+}
+
+
+void ContextSlotCache::Clear() {
+ for (int index = 0; index < kLength; index++) keys_[index].data = NULL;
+}
+
+
+#ifdef DEBUG
+
+void ContextSlotCache::ValidateEntry(Object* data,
+ String* name,
+ VariableMode mode,
+ InitializationFlag init_flag,
+ int slot_index) {
+ String* internalized_name;
+ if (HEAP->InternalizeStringIfExists(name, &internalized_name)) {
+ int index = Hash(data, name);
+ Key& key = keys_[index];
+ ASSERT(key.data == 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);
+ }
+}
+
+
+static void PrintList(const char* list_name,
+ int nof_internal_slots,
+ 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 = nof_internal_slots; start < end; ++i, ++start) {
+ PrintF(" %2d ", i);
+ String::cast(scope_info->get(start))->ShortPrint();
+ PrintF("\n");
+ }
+ }
+}
+
+
+void ScopeInfo::Print() {
+ PrintF("ScopeInfo ");
+ if (HasFunctionName()) {
+ FunctionName()->ShortPrint();
+ } else {
+ PrintF("/* no function name */");
+ }
+ PrintF("{");
+
+ 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
+
+
+//---------------------------------------------------------------------------
+// ModuleInfo.
+
+Handle<ModuleInfo> ModuleInfo::Create(
+ Isolate* isolate, Interface* interface, Scope* scope) {
+ Handle<ModuleInfo> info = Allocate(isolate, interface->Length());
+ info->set_host_index(interface->Index());
+ int i = 0;
+ for (Interface::Iterator it = interface->iterator();
+ !it.done(); it.Advance(), ++i) {
+ Variable* var = scope->LocalLookup(it.name());
+ info->set_name(i, *it.name());
+ info->set_mode(i, var->mode());
+ ASSERT((var->mode() == MODULE) == (it.interface()->IsModule()));
+ if (var->mode() == MODULE) {
+ ASSERT(it.interface()->IsFrozen());
+ ASSERT(it.interface()->Index() >= 0);
+ info->set_index(i, it.interface()->Index());
+ } else {
+ ASSERT(var->index() >= 0);
+ info->set_index(i, var->index());
+ }
+ }
+ ASSERT(i == info->length());
+ return info;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/scopeinfo.h b/chromium/v8/src/scopeinfo.h
new file mode 100644
index 00000000000..a884b3b9ed8
--- /dev/null
+++ b/chromium/v8/src/scopeinfo.h
@@ -0,0 +1,196 @@
+// 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_SCOPEINFO_H_
+#define V8_SCOPEINFO_H_
+
+#include "allocation.h"
+#include "variables.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// 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.
+// Cleared at startup and prior to mark sweep collection.
+class ContextSlotCache {
+ public:
+ // Lookup context slot index for (data, name).
+ // If absent, kNotFound is returned.
+ int Lookup(Object* data,
+ String* name,
+ VariableMode* mode,
+ InitializationFlag* init_flag);
+
+ // Update an element in the cache.
+ void Update(Object* data,
+ String* name,
+ VariableMode mode,
+ InitializationFlag init_flag,
+ int slot_index);
+
+ // Clear the cache.
+ void Clear();
+
+ static const int kNotFound = -2;
+
+ private:
+ ContextSlotCache() {
+ for (int i = 0; i < kLength; ++i) {
+ keys_[i].data = NULL;
+ keys_[i].name = NULL;
+ values_[i] = kNotFound;
+ }
+ }
+
+ inline static int Hash(Object* data, String* name);
+
+#ifdef DEBUG
+ void ValidateEntry(Object* data,
+ String* name,
+ VariableMode mode,
+ InitializationFlag init_flag,
+ int slot_index);
+#endif
+
+ static const int kLength = 256;
+ struct Key {
+ Object* data;
+ String* name;
+ };
+
+ struct Value {
+ 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) |
+ InitField::encode(init_flag);
+ ASSERT(mode == this->mode());
+ ASSERT(init_flag == this->initialization_flag());
+ ASSERT(index == this->index());
+ }
+
+ explicit inline Value(uint32_t value) : value_(value) {}
+
+ uint32_t raw() { return 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<VariableMode, 0, 4> {};
+ class InitField: public BitField<InitializationFlag, 4, 1> {};
+ class IndexField: public BitField<int, 5, 32-5> {};
+
+ private:
+ uint32_t value_;
+ };
+
+ Key keys_[kLength];
+ uint32_t values_[kLength];
+
+ friend class Isolate;
+ DISALLOW_COPY_AND_ASSIGN(ContextSlotCache);
+};
+
+
+
+
+//---------------------------------------------------------------------------
+// Auxiliary class used for the description of module instances.
+// Used by Runtime_DeclareModules.
+
+class ModuleInfo: public FixedArray {
+ public:
+ static ModuleInfo* cast(Object* description) {
+ return static_cast<ModuleInfo*>(FixedArray::cast(description));
+ }
+
+ static Handle<ModuleInfo> Create(
+ Isolate* isolate, Interface* interface, Scope* scope);
+
+ // Index of module's context in host context.
+ int host_index() { return Smi::cast(get(HOST_OFFSET))->value(); }
+
+ // Name, mode, and index of the i-th export, respectively.
+ // For value exports, the index is the slot of the value in the module
+ // context, for exported modules it is the slot index of the
+ // referred module's context in the host context.
+ // TODO(rossberg): This format cannot yet handle exports of modules declared
+ // in earlier scripts.
+ String* name(int i) { return String::cast(get(name_offset(i))); }
+ VariableMode mode(int i) {
+ return static_cast<VariableMode>(Smi::cast(get(mode_offset(i)))->value());
+ }
+ int index(int i) { return Smi::cast(get(index_offset(i)))->value(); }
+
+ int length() { return (FixedArray::length() - HEADER_SIZE) / ITEM_SIZE; }
+
+ private:
+ // The internal format is: Index, (Name, VariableMode, Index)*
+ enum {
+ HOST_OFFSET,
+ NAME_OFFSET,
+ MODE_OFFSET,
+ INDEX_OFFSET,
+ HEADER_SIZE = NAME_OFFSET,
+ ITEM_SIZE = INDEX_OFFSET - NAME_OFFSET + 1
+ };
+ inline int name_offset(int i) { return NAME_OFFSET + i * ITEM_SIZE; }
+ inline int mode_offset(int i) { return MODE_OFFSET + i * ITEM_SIZE; }
+ inline int index_offset(int i) { return INDEX_OFFSET + i * ITEM_SIZE; }
+
+ static Handle<ModuleInfo> Allocate(Isolate* isolate, int length) {
+ return Handle<ModuleInfo>::cast(
+ isolate->factory()->NewFixedArray(HEADER_SIZE + ITEM_SIZE * length));
+ }
+ void set_host_index(int index) { set(HOST_OFFSET, Smi::FromInt(index)); }
+ void set_name(int i, String* name) { set(name_offset(i), name); }
+ void set_mode(int i, VariableMode mode) {
+ set(mode_offset(i), Smi::FromInt(mode));
+ }
+ void set_index(int i, int index) {
+ set(index_offset(i), Smi::FromInt(index));
+ }
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_SCOPEINFO_H_
diff --git a/chromium/v8/src/scopes.cc b/chromium/v8/src/scopes.cc
new file mode 100644
index 00000000000..e631332d5c3
--- /dev/null
+++ b/chromium/v8/src/scopes.cc
@@ -0,0 +1,1410 @@
+// Copyright 2012 the V8 project authors. 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 "scopes.h"
+
+#include "accessors.h"
+#include "bootstrapper.h"
+#include "compiler.h"
+#include "messages.h"
+#include "scopeinfo.h"
+
+#include "allocation-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// Implementation of LocalsMap
+//
+// Note: We are storing the handle locations as key values in the hash map.
+// When inserting a new variable via Declare(), we rely on the fact that
+// the handle location remains alive for the duration of that variable
+// use. Because a Variable holding a handle with the same location exists
+// this is ensured.
+
+static bool Match(void* key1, void* key2) {
+ String* name1 = *reinterpret_cast<String**>(key1);
+ String* name2 = *reinterpret_cast<String**>(key2);
+ ASSERT(name1->IsInternalizedString());
+ ASSERT(name2->IsInternalizedString());
+ return name1 == name2;
+}
+
+
+VariableMap::VariableMap(Zone* zone)
+ : ZoneHashMap(Match, 8, ZoneAllocationPolicy(zone)),
+ zone_(zone) {}
+VariableMap::~VariableMap() {}
+
+
+Variable* VariableMap::Declare(
+ Scope* scope,
+ Handle<String> name,
+ VariableMode mode,
+ bool is_valid_lhs,
+ Variable::Kind kind,
+ InitializationFlag initialization_flag,
+ Interface* interface) {
+ Entry* p = ZoneHashMap::Lookup(name.location(), name->Hash(), true,
+ ZoneAllocationPolicy(zone()));
+ if (p->value == NULL) {
+ // The variable has not been declared yet -> insert it.
+ ASSERT(p->key == name.location());
+ p->value = new(zone()) Variable(scope,
+ name,
+ mode,
+ is_valid_lhs,
+ kind,
+ initialization_flag,
+ interface);
+ }
+ return reinterpret_cast<Variable*>(p->value);
+}
+
+
+Variable* VariableMap::Lookup(Handle<String> name) {
+ Entry* p = ZoneHashMap::Lookup(name.location(), name->Hash(), false,
+ ZoneAllocationPolicy(NULL));
+ if (p != NULL) {
+ ASSERT(*reinterpret_cast<String**>(p->key) == *name);
+ ASSERT(p->value != NULL);
+ return reinterpret_cast<Variable*>(p->value);
+ }
+ return NULL;
+}
+
+
+// ----------------------------------------------------------------------------
+// Implementation of Scope
+
+Scope::Scope(Scope* outer_scope, ScopeType scope_type, Zone* zone)
+ : isolate_(zone->isolate()),
+ inner_scopes_(4, zone),
+ variables_(zone),
+ internals_(4, zone),
+ temps_(4, zone),
+ params_(4, zone),
+ unresolved_(16, zone),
+ decls_(4, zone),
+ interface_(FLAG_harmony_modules &&
+ (scope_type == MODULE_SCOPE || scope_type == GLOBAL_SCOPE)
+ ? Interface::NewModule(zone) : NULL),
+ already_resolved_(false),
+ zone_(zone) {
+ SetDefaults(scope_type, outer_scope, Handle<ScopeInfo>::null());
+ // The outermost scope must be a global scope.
+ ASSERT(scope_type == GLOBAL_SCOPE || outer_scope != NULL);
+ ASSERT(!HasIllegalRedeclaration());
+}
+
+
+Scope::Scope(Scope* inner_scope,
+ ScopeType scope_type,
+ Handle<ScopeInfo> scope_info,
+ Zone* zone)
+ : isolate_(Isolate::Current()),
+ inner_scopes_(4, zone),
+ variables_(zone),
+ internals_(4, zone),
+ temps_(4, zone),
+ params_(4, zone),
+ unresolved_(16, zone),
+ decls_(4, zone),
+ interface_(NULL),
+ already_resolved_(true),
+ zone_(zone) {
+ SetDefaults(scope_type, NULL, scope_info);
+ 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);
+}
+
+
+Scope::Scope(Scope* inner_scope, Handle<String> catch_variable_name, Zone* zone)
+ : isolate_(Isolate::Current()),
+ inner_scopes_(1, zone),
+ variables_(zone),
+ internals_(0, zone),
+ temps_(0, zone),
+ params_(0, zone),
+ unresolved_(0, zone),
+ decls_(0, zone),
+ interface_(NULL),
+ already_resolved_(true),
+ zone_(zone) {
+ 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,
+ VAR,
+ true, // Valid left-hand side.
+ Variable::NORMAL,
+ kCreatedInitialized);
+ AllocateHeapSlot(variable);
+}
+
+
+void Scope::SetDefaults(ScopeType scope_type,
+ Scope* outer_scope,
+ Handle<ScopeInfo> scope_info) {
+ outer_scope_ = outer_scope;
+ scope_type_ = scope_type;
+ scope_name_ = isolate_->factory()->empty_string();
+ dynamics_ = NULL;
+ receiver_ = NULL;
+ function_ = NULL;
+ arguments_ = NULL;
+ illegal_redecl_ = NULL;
+ scope_inside_with_ = false;
+ scope_contains_with_ = false;
+ scope_calls_eval_ = false;
+ // Inherit the strict mode from the parent scope.
+ language_mode_ = (outer_scope != NULL)
+ ? outer_scope->language_mode_ : CLASSIC_MODE;
+ outer_scope_calls_non_strict_eval_ = false;
+ inner_scope_calls_eval_ = false;
+ force_eager_compilation_ = false;
+ force_context_allocation_ = (outer_scope != NULL && !is_function_scope())
+ ? outer_scope->has_forced_context_allocation() : false;
+ num_var_or_const_ = 0;
+ num_stack_slots_ = 0;
+ num_heap_slots_ = 0;
+ num_modules_ = 0;
+ module_var_ = NULL,
+ 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(Context* context, Scope* global_scope,
+ Zone* zone) {
+ // Reconstruct the outer scope chain from a closure's context chain.
+ Scope* current_scope = NULL;
+ Scope* innermost_scope = NULL;
+ bool contains_with = false;
+ while (!context->IsNativeContext()) {
+ if (context->IsWithContext()) {
+ Scope* with_scope = new(zone) Scope(current_scope,
+ WITH_SCOPE,
+ Handle<ScopeInfo>::null(),
+ zone);
+ 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->IsGlobalContext()) {
+ ScopeInfo* scope_info = ScopeInfo::cast(context->extension());
+ current_scope = new(zone) Scope(current_scope,
+ GLOBAL_SCOPE,
+ Handle<ScopeInfo>(scope_info),
+ zone);
+ } else if (context->IsModuleContext()) {
+ ScopeInfo* scope_info = ScopeInfo::cast(context->module()->scope_info());
+ current_scope = new(zone) Scope(current_scope,
+ MODULE_SCOPE,
+ Handle<ScopeInfo>(scope_info),
+ zone);
+ } else if (context->IsFunctionContext()) {
+ ScopeInfo* scope_info = context->closure()->shared()->scope_info();
+ current_scope = new(zone) Scope(current_scope,
+ FUNCTION_SCOPE,
+ Handle<ScopeInfo>(scope_info),
+ zone);
+ } else if (context->IsBlockContext()) {
+ ScopeInfo* scope_info = ScopeInfo::cast(context->extension());
+ current_scope = new(zone) Scope(current_scope,
+ BLOCK_SCOPE,
+ Handle<ScopeInfo>(scope_info),
+ zone);
+ } else {
+ ASSERT(context->IsCatchContext());
+ String* name = String::cast(context->extension());
+ current_scope = new(zone) Scope(
+ current_scope, Handle<String>(name), zone);
+ }
+ 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()) {
+ contains_with = false;
+ }
+ context = context->previous();
+ }
+
+ 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* 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();
+ }
+
+ // Allocate the variables.
+ {
+ AstNodeFactory<AstNullVisitor> ast_node_factory(info->isolate(),
+ info->zone());
+ if (!top->AllocateVariables(info, &ast_node_factory)) return false;
+ }
+
+#ifdef DEBUG
+ if (info->isolate()->bootstrapper()->IsActive()
+ ? FLAG_print_builtin_scopes
+ : FLAG_print_scopes) {
+ scope->Print();
+ }
+
+ if (FLAG_harmony_modules && FLAG_print_interfaces && top->is_global_scope()) {
+ PrintF("global : ");
+ top->interface()->Print();
+ }
+#endif
+
+ info->SetScope(scope);
+ return true;
+}
+
+
+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, zone());
+ scope_inside_with_ = outer_scope_->scope_inside_with_ || is_with_scope();
+ } else {
+ scope_inside_with_ = is_with_scope();
+ }
+
+ // Declare convenience variables.
+ // Declare and allocate receiver (even for the global scope, and even
+ // if naccesses_ == 0).
+ // NOTE: When loading parameters in the global scope, we must take
+ // care not to access them as properties of the global object, but
+ // 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_declaration_scope()) {
+ Variable* var =
+ variables_.Declare(this,
+ isolate_->factory()->this_string(),
+ VAR,
+ false,
+ Variable::THIS,
+ kCreatedInitialized);
+ var->AllocateTo(Variable::PARAMETER, -1);
+ receiver_ = var;
+ } else {
+ ASSERT(outer_scope() != NULL);
+ receiver_ = outer_scope()->receiver();
+ }
+
+ if (is_function_scope()) {
+ // Declare 'arguments' variable which exists in all functions.
+ // Note that it might never be accessed, in which case it won't be
+ // allocated during variable allocation.
+ variables_.Declare(this,
+ isolate_->factory()->arguments_string(),
+ VAR,
+ true,
+ Variable::ARGUMENTS,
+ kCreatedInitialized);
+ }
+}
+
+
+Scope* Scope::FinalizeBlockScope() {
+ ASSERT(is_block_scope());
+ ASSERT(internals_.is_empty());
+ ASSERT(temps_.is_empty());
+ ASSERT(params_.is_empty());
+
+ if (num_var_or_const() > 0) return this;
+
+ // Remove this scope from outer scope.
+ for (int i = 0; i < outer_scope_->inner_scopes_.length(); i++) {
+ if (outer_scope_->inner_scopes_[i] == this) {
+ outer_scope_->inner_scopes_.Remove(i);
+ break;
+ }
+ }
+
+ // Reparent inner scopes.
+ for (int i = 0; i < inner_scopes_.length(); i++) {
+ outer_scope()->AddInnerScope(inner_scopes_[i]);
+ }
+
+ // Move unresolved variables
+ for (int i = 0; i < unresolved_.length(); i++) {
+ outer_scope()->unresolved_.Add(unresolved_[i], zone());
+ }
+
+ return NULL;
+}
+
+
+Variable* Scope::LocalLookup(Handle<String> name) {
+ Variable* result = variables_.Lookup(name);
+ if (result != NULL || scope_info_.is_null()) {
+ return result;
+ }
+ // If we have a serialized scope info, we might find the variable there.
+ // There should be no local slot with the given name.
+ ASSERT(scope_info_->StackSlotIndex(*name) < 0);
+
+ // Check context slot lookup.
+ VariableMode mode;
+ Variable::Location location = Variable::CONTEXT;
+ InitializationFlag init_flag;
+ int index = scope_info_->ContextSlotIndex(*name, &mode, &init_flag);
+ if (index < 0) {
+ // Check parameters.
+ index = scope_info_->ParameterIndex(*name);
+ if (index < 0) return NULL;
+
+ mode = DYNAMIC;
+ location = Variable::LOOKUP;
+ init_flag = kCreatedInitialized;
+ }
+
+ Variable* var = variables_.Declare(this, name, mode, true, Variable::NORMAL,
+ init_flag);
+ var->AllocateTo(location, index);
+ return var;
+}
+
+
+Variable* Scope::LookupFunctionVar(Handle<String> name,
+ AstNodeFactory<AstNullVisitor>* factory) {
+ if (function_ != NULL && function_->proxy()->name().is_identical_to(name)) {
+ return function_->proxy()->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 = new(zone()) Variable(
+ this, name, mode, true /* is valid LHS */,
+ Variable::NORMAL, kCreatedInitialized);
+ VariableProxy* proxy = factory->NewVariableProxy(var);
+ VariableDeclaration* declaration =
+ factory->NewVariableDeclaration(proxy, mode, this);
+ DeclareFunctionVar(declaration);
+ var->AllocateTo(Variable::CONTEXT, index);
+ return var;
+ } else {
+ return NULL;
+ }
+}
+
+
+Variable* Scope::Lookup(Handle<String> name) {
+ for (Scope* scope = this;
+ scope != NULL;
+ scope = scope->outer_scope()) {
+ Variable* var = scope->LocalLookup(name);
+ if (var != NULL) return var;
+ }
+ return NULL;
+}
+
+
+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,
+ kCreatedInitialized);
+ params_.Add(var, zone());
+}
+
+
+Variable* Scope::DeclareLocal(Handle<String> name,
+ VariableMode mode,
+ InitializationFlag init_flag,
+ Interface* interface) {
+ 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(IsDeclaredVariableMode(mode));
+ ++num_var_or_const_;
+ return variables_.Declare(
+ this, name, mode, true, Variable::NORMAL, init_flag, interface);
+}
+
+
+Variable* Scope::DeclareDynamicGlobal(Handle<String> name) {
+ ASSERT(is_global_scope());
+ return variables_.Declare(this,
+ name,
+ DYNAMIC_GLOBAL,
+ true,
+ Variable::NORMAL,
+ kCreatedInitialized);
+}
+
+
+void Scope::RemoveUnresolved(VariableProxy* var) {
+ // Most likely (always?) any variable we want to remove
+ // was just added before, so we search backwards.
+ for (int i = unresolved_.length(); i-- > 0;) {
+ if (unresolved_[i] == var) {
+ unresolved_.Remove(i);
+ return;
+ }
+ }
+}
+
+
+Variable* Scope::NewInternal(Handle<String> name) {
+ ASSERT(!already_resolved());
+ Variable* var = new(zone()) Variable(this,
+ name,
+ INTERNAL,
+ false,
+ Variable::NORMAL,
+ kCreatedInitialized);
+ internals_.Add(var, zone());
+ return var;
+}
+
+
+Variable* Scope::NewTemporary(Handle<String> name) {
+ ASSERT(!already_resolved());
+ Variable* var = new(zone()) Variable(this,
+ name,
+ TEMPORARY,
+ true,
+ Variable::NORMAL,
+ kCreatedInitialized);
+ temps_.Add(var, zone());
+ return var;
+}
+
+
+void Scope::AddDeclaration(Declaration* declaration) {
+ decls_.Add(declaration, zone());
+}
+
+
+void Scope::SetIllegalRedeclaration(Expression* expression) {
+ // Record only the first illegal redeclaration.
+ if (!HasIllegalRedeclaration()) {
+ illegal_redecl_ = expression;
+ }
+ ASSERT(HasIllegalRedeclaration());
+}
+
+
+void Scope::VisitIllegalRedeclaration(AstVisitor* visitor) {
+ ASSERT(HasIllegalRedeclaration());
+ illegal_redecl_->Accept(visitor);
+}
+
+
+Declaration* Scope::CheckConflictingVarDeclarations() {
+ int length = decls_.length();
+ for (int i = 0; i < length; i++) {
+ Declaration* decl = decls_[i];
+ if (decl->mode() != VAR) continue;
+ Handle<String> name = decl->proxy()->name();
+
+ // 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 = 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;
+}
+
+
+class VarAndOrder {
+ public:
+ VarAndOrder(Variable* var, int order) : var_(var), order_(order) { }
+ Variable* var() const { return var_; }
+ int order() const { return order_; }
+ static int Compare(const VarAndOrder* a, const VarAndOrder* b) {
+ return a->order_ - b->order_;
+ }
+
+ private:
+ Variable* var_;
+ int order_;
+};
+
+
+void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
+ ZoneList<Variable*>* context_locals) {
+ ASSERT(stack_locals != NULL);
+ ASSERT(context_locals != NULL);
+
+ // Collect internals which are always allocated on the heap.
+ for (int i = 0; i < internals_.length(); i++) {
+ Variable* var = internals_[i];
+ if (var->is_used()) {
+ ASSERT(var->IsContextSlot());
+ context_locals->Add(var, zone());
+ }
+ }
+
+ // Collect temporaries which are always allocated on the stack, unless the
+ // context as a whole has forced context allocation.
+ for (int i = 0; i < temps_.length(); i++) {
+ Variable* var = temps_[i];
+ if (var->is_used()) {
+ if (var->IsContextSlot()) {
+ ASSERT(has_forced_context_allocation());
+ context_locals->Add(var, zone());
+ } else {
+ ASSERT(var->IsStackLocal());
+ stack_locals->Add(var, zone());
+ }
+ }
+ }
+
+ // Collect declared local variables.
+ ZoneList<VarAndOrder> vars(variables_.occupancy(), zone());
+ for (VariableMap::Entry* p = variables_.Start();
+ p != NULL;
+ p = variables_.Next(p)) {
+ Variable* var = reinterpret_cast<Variable*>(p->value);
+ if (var->is_used()) {
+ vars.Add(VarAndOrder(var, p->order), zone());
+ }
+ }
+ vars.Sort(VarAndOrder::Compare);
+ int var_count = vars.length();
+ for (int i = 0; i < var_count; i++) {
+ Variable* var = vars[i].var();
+ if (var->IsStackLocal()) {
+ stack_locals->Add(var, zone());
+ } else if (var->IsContextSlot()) {
+ context_locals->Add(var, zone());
+ }
+ }
+}
+
+
+bool Scope::AllocateVariables(CompilationInfo* info,
+ AstNodeFactory<AstNullVisitor>* factory) {
+ // 1) Propagate scope information.
+ bool outer_scope_calls_non_strict_eval = false;
+ 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_non_strict_eval);
+
+ // 2) Allocate module instances.
+ if (FLAG_harmony_modules && (is_global_scope() || is_module_scope())) {
+ ASSERT(num_modules_ == 0);
+ AllocateModulesRecursively(this);
+ }
+
+ // 3) Resolve variables.
+ if (!ResolveVariablesRecursively(info, factory)) return false;
+
+ // 4) Allocate variables.
+ AllocateVariablesRecursively();
+
+ return true;
+}
+
+
+bool Scope::HasTrivialContext() const {
+ // A function scope has a trivial context if it always is the global
+ // context. We iteratively scan out the context chain to see if
+ // there is anything that makes this scope non-trivial; otherwise we
+ // return true.
+ for (const Scope* scope = this; scope != NULL; scope = scope->outer_scope_) {
+ if (scope->is_eval_scope()) return false;
+ if (scope->scope_inside_with_) return false;
+ if (scope->num_heap_slots_ > 0) return false;
+ }
+ return true;
+}
+
+
+bool Scope::HasTrivialOuterContext() const {
+ Scope* outer = outer_scope_;
+ if (outer == NULL) return true;
+ // Note that the outer context may be trivial in general, but the current
+ // scope may be inside a 'with' statement in which case the outer context
+ // for this scope is not trivial.
+ return !scope_inside_with_ && outer->HasTrivialContext();
+}
+
+
+bool Scope::HasLazyCompilableOuterContext() const {
+ Scope* outer = outer_scope_;
+ if (outer == NULL) return true;
+ // We have to prevent lazy compilation if this scope is inside a with scope
+ // and all declaration scopes between them have empty contexts. Such
+ // declaration scopes may become invisible during scope info deserialization.
+ outer = outer->DeclarationScope();
+ bool found_non_trivial_declarations = false;
+ for (const Scope* scope = outer; scope != NULL; scope = scope->outer_scope_) {
+ if (scope->is_with_scope() && !found_non_trivial_declarations) return false;
+ if (scope->is_declaration_scope() && scope->num_heap_slots() > 0) {
+ found_non_trivial_declarations = true;
+ }
+ }
+ return true;
+}
+
+
+bool Scope::AllowsLazyCompilation() const {
+ return !force_eager_compilation_ && HasLazyCompilableOuterContext();
+}
+
+
+bool Scope::AllowsLazyCompilationWithoutContext() const {
+ return !force_eager_compilation_ && HasTrivialOuterContext();
+}
+
+
+int Scope::ContextChainLength(Scope* scope) {
+ int n = 0;
+ for (Scope* s = this; s != scope; s = s->outer_scope_) {
+ ASSERT(s != NULL); // scope must be in the scope chain
+ if (s->is_with_scope() || s->num_heap_slots() > 0) n++;
+ // Catch and module scopes always have heap slots.
+ ASSERT(!s->is_catch_scope() || s->num_heap_slots() > 0);
+ ASSERT(!s->is_module_scope() || s->num_heap_slots() > 0);
+ }
+ return n;
+}
+
+
+Scope* Scope::GlobalScope() {
+ Scope* scope = this;
+ while (!scope->is_global_scope()) {
+ scope = scope->outer_scope();
+ }
+ return scope;
+}
+
+
+Scope* Scope::DeclarationScope() {
+ Scope* scope = this;
+ while (!scope->is_declaration_scope()) {
+ scope = scope->outer_scope();
+ }
+ return scope;
+}
+
+
+Handle<ScopeInfo> Scope::GetScopeInfo() {
+ if (scope_info_.is_null()) {
+ scope_info_ = ScopeInfo::Create(this, zone());
+ }
+ 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(ScopeType scope_type) {
+ switch (scope_type) {
+ case EVAL_SCOPE: return "eval";
+ case FUNCTION_SCOPE: return "function";
+ case MODULE_SCOPE: return "module";
+ case GLOBAL_SCOPE: return "global";
+ case CATCH_SCOPE: return "catch";
+ case BLOCK_SCOPE: return "block";
+ case WITH_SCOPE: return "with";
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+static void Indent(int n, const char* str) {
+ PrintF("%*s%s", n, "", str);
+}
+
+
+static void PrintName(Handle<String> name) {
+ SmartArrayPointer<char> s = name->ToCString(DISALLOW_NULLS);
+ PrintF("%s", *s);
+}
+
+
+static void PrintLocation(Variable* var) {
+ switch (var->location()) {
+ case Variable::UNALLOCATED:
+ break;
+ case Variable::PARAMETER:
+ PrintF("parameter[%d]", var->index());
+ break;
+ case Variable::LOCAL:
+ PrintF("local[%d]", var->index());
+ break;
+ case Variable::CONTEXT:
+ PrintF("context[%d]", var->index());
+ break;
+ case Variable::LOOKUP:
+ PrintF("lookup");
+ break;
+ }
+}
+
+
+static void PrintVar(int indent, Variable* var) {
+ if (var->is_used() || !var->IsUnallocated()) {
+ Indent(indent, Variable::Mode2String(var->mode()));
+ PrintF(" ");
+ PrintName(var->name());
+ PrintF("; // ");
+ PrintLocation(var);
+ if (var->has_forced_context_allocation()) {
+ if (!var->IsUnallocated()) PrintF(", ");
+ PrintF("forced context allocation");
+ }
+ PrintF("\n");
+ }
+}
+
+
+static void PrintMap(int indent, VariableMap* map) {
+ for (VariableMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) {
+ Variable* var = reinterpret_cast<Variable*>(p->value);
+ PrintVar(indent, var);
+ }
+}
+
+
+void Scope::Print(int n) {
+ int n0 = (n > 0 ? n : 0);
+ int n1 = n0 + 2; // indentation
+
+ // Print header.
+ Indent(n0, Header(scope_type_));
+ if (scope_name_->length() > 0) {
+ PrintF(" ");
+ PrintName(scope_name_);
+ }
+
+ // Print parameters, if any.
+ if (is_function_scope()) {
+ PrintF(" (");
+ for (int i = 0; i < params_.length(); i++) {
+ if (i > 0) PrintF(", ");
+ PrintName(params_[i]->name());
+ }
+ PrintF(")");
+ }
+
+ PrintF(" { // (%d, %d)\n", start_position(), end_position());
+
+ // Function name, if any (named function literals, only).
+ if (function_ != NULL) {
+ Indent(n1, "// (local) function name: ");
+ PrintName(function_->proxy()->name());
+ PrintF("\n");
+ }
+
+ // Scope info.
+ if (HasTrivialOuterContext()) {
+ Indent(n1, "// scope has trivial outer context\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_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 (num_stack_slots_ > 0) { Indent(n1, "// ");
+ PrintF("%d stack slots\n", num_stack_slots_); }
+ if (num_heap_slots_ > 0) { Indent(n1, "// ");
+ PrintF("%d heap slots\n", num_heap_slots_); }
+
+ // Print locals.
+ Indent(n1, "// function var\n");
+ if (function_ != NULL) {
+ PrintVar(n1, function_->proxy()->var());
+ }
+
+ Indent(n1, "// temporary vars\n");
+ for (int i = 0; i < temps_.length(); i++) {
+ PrintVar(n1, temps_[i]);
+ }
+
+ Indent(n1, "// internal vars\n");
+ for (int i = 0; i < internals_.length(); i++) {
+ PrintVar(n1, internals_[i]);
+ }
+
+ Indent(n1, "// local vars\n");
+ PrintMap(n1, &variables_);
+
+ Indent(n1, "// dynamic vars\n");
+ if (dynamics_ != NULL) {
+ 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).
+ if (n >= 0) {
+ for (int i = 0; i < inner_scopes_.length(); i++) {
+ PrintF("\n");
+ inner_scopes_[i]->Print(n1);
+ }
+ }
+
+ Indent(n0, "}\n");
+}
+#endif // DEBUG
+
+
+Variable* Scope::NonLocal(Handle<String> name, VariableMode mode) {
+ if (dynamics_ == NULL) dynamics_ = new(zone()) DynamicScopePart(zone());
+ VariableMap* map = dynamics_->GetMap(mode);
+ Variable* var = map->Lookup(name);
+ if (var == NULL) {
+ // Declare a new non-local.
+ 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);
+ }
+ return var;
+}
+
+
+Variable* Scope::LookupRecursive(Handle<String> name,
+ BindingKind* binding_kind,
+ AstNodeFactory<AstNullVisitor>* factory) {
+ ASSERT(binding_kind != NULL);
+ if (already_resolved() && is_with_scope()) {
+ // Short-cut: if the scope is deserialized from a scope info, variable
+ // allocation is already fixed. We can simply return with dynamic lookup.
+ *binding_kind = DYNAMIC_LOOKUP;
+ return 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) {
+ *binding_kind = BOUND;
+ return var;
+ }
+
+ // 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 (is_with_scope()) {
+ ASSERT(!already_resolved());
+ // 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;
+}
+
+
+bool Scope::ResolveVariable(CompilationInfo* info,
+ VariableProxy* proxy,
+ AstNodeFactory<AstNullVisitor>* factory) {
+ ASSERT(info->global_scope()->is_global_scope());
+
+ // If the proxy is already resolved there's nothing to do
+ // (functions and consts may be resolved by the parser).
+ if (proxy->var() != NULL) return true;
+
+ // Otherwise, try to resolve the variable.
+ 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 either found a variable binding that might be shadowed by eval or
+ // gave up on it (e.g. by encountering a local with the same in the outer
+ // scope which was not promoted to a context, this can happen if we use
+ // debugger to evaluate arbitrary expressions at a break point).
+ if (var->IsGlobalObjectProperty()) {
+ var = NonLocal(proxy->name(), DYNAMIC_GLOBAL);
+ } else if (var->is_dynamic()) {
+ var = NonLocal(proxy->name(), DYNAMIC);
+ } else {
+ 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 on the global object.
+ var = info->global_scope()->DeclareDynamicGlobal(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);
+
+ if (FLAG_harmony_scoping && is_extended_mode() &&
+ var->is_const_mode() && proxy->IsLValue()) {
+ // Assignment to const. Throw a syntax error.
+ MessageLocation location(
+ info->script(), proxy->position(), proxy->position());
+ Isolate* isolate = Isolate::Current();
+ 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;
+ }
+
+ if (FLAG_harmony_modules) {
+ bool ok;
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Resolve %s:\n", var->name()->ToAsciiArray());
+#endif
+ proxy->interface()->Unify(var->interface(), zone(), &ok);
+ if (!ok) {
+#ifdef DEBUG
+ if (FLAG_print_interfaces) {
+ PrintF("SCOPES TYPE ERROR\n");
+ PrintF("proxy: ");
+ proxy->interface()->Print();
+ PrintF("var: ");
+ var->interface()->Print();
+ }
+#endif
+
+ // Inconsistent use of module. Throw a syntax error.
+ // TODO(rossberg): generate more helpful error message.
+ MessageLocation location(
+ info->script(), proxy->position(), proxy->position());
+ Isolate* isolate = Isolate::Current();
+ Factory* factory = isolate->factory();
+ Handle<JSArray> array = factory->NewJSArray(1);
+ USE(JSObject::SetElement(array, 0, var->name(), NONE, kStrictMode));
+ Handle<Object> result =
+ factory->NewSyntaxError("module_type_error", array);
+ isolate->Throw(*result, &location);
+ return false;
+ }
+ }
+
+ proxy->BindTo(var);
+
+ return true;
+}
+
+
+bool Scope::ResolveVariablesRecursively(
+ CompilationInfo* info,
+ AstNodeFactory<AstNullVisitor>* factory) {
+ ASSERT(info->global_scope()->is_global_scope());
+
+ // Resolve unresolved variables for this scope.
+ for (int i = 0; i < unresolved_.length(); i++) {
+ if (!ResolveVariable(info, unresolved_[i], factory)) return false;
+ }
+
+ // Resolve unresolved variables for inner scopes.
+ for (int i = 0; i < inner_scopes_.length(); i++) {
+ if (!inner_scopes_[i]->ResolveVariablesRecursively(info, factory))
+ return false;
+ }
+
+ return 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;
+ }
+
+ bool 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_non_strict_eval)) {
+ inner_scope_calls_eval_ = true;
+ }
+ if (inner_scope->force_eager_compilation_) {
+ force_eager_compilation_ = true;
+ }
+ }
+
+ return scope_calls_eval_ || inner_scope_calls_eval_;
+}
+
+
+bool Scope::MustAllocate(Variable* var) {
+ // Give var a read/write use if there is a chance it might be accessed
+ // via an eval() call. This is only possible if the variable has a
+ // visible name.
+ if ((var->is_this() || var->name()->length() > 0) &&
+ (var->has_forced_context_allocation() ||
+ scope_calls_eval_ ||
+ inner_scope_calls_eval_ ||
+ scope_contains_with_ ||
+ is_catch_scope() ||
+ is_block_scope() ||
+ is_module_scope() ||
+ is_global_scope())) {
+ var->set_is_used(true);
+ }
+ // Global variables do not need to be allocated.
+ return !var->IsGlobalObjectProperty() && var->is_used();
+}
+
+
+bool Scope::MustAllocateInContext(Variable* var) {
+ // If var is accessed from an inner scope, or if there is a possibility
+ // that it might be accessed from the current or an inner scope (through
+ // an eval() call or a runtime with lookup), it must be allocated in the
+ // context.
+ //
+ // Exceptions: If the scope as a whole has forced context allocation, all
+ // variables will have context allocation, even temporaries. Otherwise
+ // temporary variables are always stack-allocated. Catch-bound variables are
+ // always context-allocated.
+ if (has_forced_context_allocation()) return true;
+ if (var->mode() == TEMPORARY) return false;
+ if (var->mode() == INTERNAL) return true;
+ if (is_catch_scope() || is_block_scope() || is_module_scope()) return true;
+ if (is_global_scope() && IsLexicalVariableMode(var->mode())) return true;
+ return var->has_forced_context_allocation() ||
+ scope_calls_eval_ ||
+ inner_scope_calls_eval_ ||
+ scope_contains_with_;
+}
+
+
+bool Scope::HasArgumentsParameter() {
+ for (int i = 0; i < params_.length(); i++) {
+ if (params_[i]->name().is_identical_to(
+ isolate_->factory()->arguments_string())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Scope::AllocateStackSlot(Variable* var) {
+ var->AllocateTo(Variable::LOCAL, num_stack_slots_++);
+}
+
+
+void Scope::AllocateHeapSlot(Variable* var) {
+ var->AllocateTo(Variable::CONTEXT, num_heap_slots_++);
+}
+
+
+void Scope::AllocateParameterLocals() {
+ ASSERT(is_function_scope());
+ Variable* arguments = LocalLookup(isolate_->factory()->arguments_string());
+ ASSERT(arguments != NULL); // functions have 'arguments' declared implicitly
+
+ bool uses_nonstrict_arguments = false;
+
+ if (MustAllocate(arguments) && !HasArgumentsParameter()) {
+ // 'arguments' is used. Unless there is also a parameter called
+ // 'arguments', we must be conservative and allocate all parameters to
+ // the context assuming they will be captured by the arguments object.
+ // If we have a parameter named 'arguments', a (new) value is always
+ // assigned to it via the function invocation. Then 'arguments' denotes
+ // that specific parameter value and cannot be used to access the
+ // parameters, which is why we don't need to allocate an arguments
+ // object in that case.
+
+ // We are using 'arguments'. Tell the code generator that is needs to
+ // allocate the arguments object by setting 'arguments_'.
+ arguments_ = arguments;
+
+ // 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_classic_mode();
+ }
+
+ // The same parameter may occur multiple times in the parameters_ list.
+ // If it does, and if it is not copied into the context object, it must
+ // receive the highest parameter index for that parameter; thus iteration
+ // order is relevant!
+ for (int i = params_.length() - 1; i >= 0; --i) {
+ Variable* var = params_[i];
+ ASSERT(var->scope() == this);
+ if (uses_nonstrict_arguments) {
+ // Force context allocation of the parameter.
+ var->ForceContextAllocation();
+ }
+
+ if (MustAllocate(var)) {
+ if (MustAllocateInContext(var)) {
+ ASSERT(var->IsUnallocated() || var->IsContextSlot());
+ if (var->IsUnallocated()) {
+ AllocateHeapSlot(var);
+ }
+ } else {
+ ASSERT(var->IsUnallocated() || var->IsParameter());
+ if (var->IsUnallocated()) {
+ var->AllocateTo(Variable::PARAMETER, i);
+ }
+ }
+ }
+ }
+}
+
+
+void Scope::AllocateNonParameterLocal(Variable* var) {
+ ASSERT(var->scope() == this);
+ ASSERT(!var->IsVariable(isolate_->factory()->result_string()) ||
+ !var->IsStackLocal());
+ if (var->IsUnallocated() && MustAllocate(var)) {
+ if (MustAllocateInContext(var)) {
+ AllocateHeapSlot(var);
+ } else {
+ AllocateStackSlot(var);
+ }
+ }
+}
+
+
+void Scope::AllocateNonParameterLocals() {
+ // All variables that have no rewrite yet are non-parameter locals.
+ for (int i = 0; i < temps_.length(); i++) {
+ AllocateNonParameterLocal(temps_[i]);
+ }
+
+ for (int i = 0; i < internals_.length(); i++) {
+ AllocateNonParameterLocal(internals_[i]);
+ }
+
+ ZoneList<VarAndOrder> vars(variables_.occupancy(), zone());
+ for (VariableMap::Entry* p = variables_.Start();
+ p != NULL;
+ p = variables_.Next(p)) {
+ Variable* var = reinterpret_cast<Variable*>(p->value);
+ vars.Add(VarAndOrder(var, p->order), zone());
+ }
+ vars.Sort(VarAndOrder::Compare);
+ int var_count = vars.length();
+ for (int i = 0; i < var_count; i++) {
+ AllocateNonParameterLocal(vars[i].var());
+ }
+
+ // For now, function_ must be allocated at the very end. If it gets
+ // allocated in the context, it must be the last slot in the context,
+ // because of the current ScopeInfo implementation (see
+ // ScopeInfo::ScopeInfo(FunctionScope* scope) constructor).
+ if (function_ != NULL) {
+ AllocateNonParameterLocal(function_->proxy()->var());
+ }
+}
+
+
+void Scope::AllocateVariablesRecursively() {
+ // Allocate variables for inner scopes.
+ for (int i = 0; i < inner_scopes_.length(); i++) {
+ inner_scopes_[i]->AllocateVariablesRecursively();
+ }
+
+ // If scope is already resolved, we still need to allocate
+ // variables in inner scopes which might not had been resolved yet.
+ if (already_resolved()) return;
+ // The number of slots required for variables.
+ num_stack_slots_ = 0;
+ num_heap_slots_ = Context::MIN_CONTEXT_SLOTS;
+
+ // Allocate variables for this scope.
+ // Parameters must be allocated first, if any.
+ if (is_function_scope()) AllocateParameterLocals();
+ AllocateNonParameterLocals();
+
+ // 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.
+ // Likewise for modules.
+ bool must_have_context = is_with_scope() || is_module_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 context.
+ if (num_heap_slots_ == Context::MIN_CONTEXT_SLOTS && !must_have_context) {
+ num_heap_slots_ = 0;
+ }
+
+ // Allocation done.
+ ASSERT(num_heap_slots_ == 0 || num_heap_slots_ >= Context::MIN_CONTEXT_SLOTS);
+}
+
+
+void Scope::AllocateModulesRecursively(Scope* host_scope) {
+ if (already_resolved()) return;
+ if (is_module_scope()) {
+ ASSERT(interface_->IsFrozen());
+ Handle<String> name = isolate_->factory()->InternalizeOneByteString(
+ STATIC_ASCII_VECTOR(".module"));
+ ASSERT(module_var_ == NULL);
+ module_var_ = host_scope->NewInternal(name);
+ ++host_scope->num_modules_;
+ }
+
+ for (int i = 0; i < inner_scopes_.length(); i++) {
+ Scope* inner_scope = inner_scopes_.at(i);
+ inner_scope->AllocateModulesRecursively(host_scope);
+ }
+}
+
+
+int Scope::StackLocalCount() const {
+ return num_stack_slots() -
+ (function_ != NULL && function_->proxy()->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_->proxy()->var()->IsContextSlot() ? 1 : 0);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/scopes.h b/chromium/v8/src/scopes.h
new file mode 100644
index 00000000000..06aaa902cf8
--- /dev/null
+++ b/chromium/v8/src/scopes.h
@@ -0,0 +1,647 @@
+// Copyright 2012 the V8 project authors. 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_SCOPES_H_
+#define V8_SCOPES_H_
+
+#include "ast.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+class CompilationInfo;
+
+
+// A hash map to support fast variable declaration and lookup.
+class VariableMap: public ZoneHashMap {
+ public:
+ explicit VariableMap(Zone* zone);
+
+ virtual ~VariableMap();
+
+ Variable* Declare(Scope* scope,
+ Handle<String> name,
+ VariableMode mode,
+ bool is_valid_lhs,
+ Variable::Kind kind,
+ InitializationFlag initialization_flag,
+ Interface* interface = Interface::NewValue());
+
+ Variable* Lookup(Handle<String> name);
+
+ Zone* zone() const { return zone_; }
+
+ private:
+ Zone* zone_;
+};
+
+
+// The dynamic scope part holds hash maps for the variables that will
+// be looked up dynamically from within eval and with scopes. The objects
+// are allocated on-demand from Scope::NonLocal to avoid wasting memory
+// and setup time for scopes that don't need them.
+class DynamicScopePart : public ZoneObject {
+ public:
+ explicit DynamicScopePart(Zone* zone) {
+ for (int i = 0; i < 3; i++)
+ maps_[i] = new(zone->New(sizeof(VariableMap))) VariableMap(zone);
+ }
+
+ VariableMap* GetMap(VariableMode mode) {
+ int index = mode - DYNAMIC;
+ ASSERT(index >= 0 && index < 3);
+ return maps_[index];
+ }
+
+ private:
+ VariableMap *maps_[3];
+};
+
+
+// Global invariants after AST construction: Each reference (i.e. identifier)
+// to a JavaScript variable (including global properties) is represented by a
+// VariableProxy node. Immediately after AST construction and before variable
+// allocation, most VariableProxy nodes are "unresolved", i.e. not bound to a
+// corresponding variable (though some are bound during parse time). Variable
+// allocation binds each unresolved VariableProxy to one Variable and assigns
+// a location. Note that many VariableProxy nodes may refer to the same Java-
+// Script variable.
+
+class Scope: public ZoneObject {
+ public:
+ // ---------------------------------------------------------------------------
+ // Construction
+
+ Scope(Scope* outer_scope, ScopeType scope_type, Zone* zone);
+
+ // 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(Context* context, Scope* global_scope,
+ Zone* zone);
+
+ // The scope name is only used for printing/debugging.
+ void SetScopeName(Handle<String> scope_name) { scope_name_ = scope_name; }
+
+ 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
+ // tree and its children are reparented.
+ Scope* FinalizeBlockScope();
+
+ Zone* zone() const { return zone_; }
+
+ // ---------------------------------------------------------------------------
+ // Declarations
+
+ // 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);
+
+ // 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.
+ void DeclareFunctionVar(VariableDeclaration* declaration) {
+ ASSERT(is_function_scope());
+ function_ = declaration;
+ }
+
+ // 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, 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,
+ VariableMode mode,
+ InitializationFlag init_flag,
+ Interface* interface = Interface::NewValue());
+
+ // Declare an implicit global variable in this scope which must be a
+ // global scope. The variable was introduced (possibly from an inner
+ // scope) by a reference to an unresolved variable with no intervening
+ // with statements or eval calls.
+ Variable* DeclareDynamicGlobal(Handle<String> name);
+
+ // Create a new unresolved variable.
+ template<class Visitor>
+ VariableProxy* NewUnresolved(AstNodeFactory<Visitor>* factory,
+ Handle<String> name,
+ Interface* interface = Interface::NewValue(),
+ 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, interface, position);
+ unresolved_.Add(proxy, zone_);
+ return proxy;
+ }
+
+ // Remove a unresolved variable. During parsing, an unresolved variable
+ // may have been added optimistically, but then only the variable name
+ // was used (typically for labels). If the variable was not declared, the
+ // addition introduced a new unresolved variable which may end up being
+ // allocated globally as a "ghost" variable. RemoveUnresolved removes
+ // such a variable again if it was added; otherwise this is a no-op.
+ void RemoveUnresolved(VariableProxy* var);
+
+ // Creates a new internal variable in this scope. The name is only used
+ // for printing and cannot be used to find the variable. In particular,
+ // the only way to get hold of the temporary is by keeping the Variable*
+ // around.
+ Variable* NewInternal(Handle<String> name);
+
+ // Creates a new temporary variable in this scope. The name is only used
+ // for printing and cannot be used to find the variable. In particular,
+ // the only way to get hold of the temporary is by keeping the Variable*
+ // around. The name should not clash with a legitimate variable names.
+ Variable* NewTemporary(Handle<String> name);
+
+ // Adds the specific declaration node to the list of declarations in
+ // this scope. The declarations are processed as part of entering
+ // the scope; see codegen.cc:ProcessDeclarations.
+ void AddDeclaration(Declaration* declaration);
+
+ // ---------------------------------------------------------------------------
+ // Illegal redeclaration support.
+
+ // Set an expression node that will be executed when the scope is
+ // entered. We only keep track of one illegal redeclaration node per
+ // scope - the first one - so if you try to set it multiple times
+ // the additional requests will be silently ignored.
+ void SetIllegalRedeclaration(Expression* expression);
+
+ // Visit the illegal redeclaration expression. Do not call if the
+ // scope doesn't have an illegal redeclaration node.
+ void VisitIllegalRedeclaration(AstVisitor* visitor);
+
+ // Check if the scope has (at least) one illegal redeclaration.
+ bool HasIllegalRedeclaration() const { return illegal_redecl_ != NULL; }
+
+ // For harmony block scoping mode: Check if the scope has conflicting var
+ // declarations, i.e. a var declaration that has been hoisted from a nested
+ // scope over a let binding of the same name.
+ Declaration* CheckConflictingVarDeclarations();
+
+ // ---------------------------------------------------------------------------
+ // Scope-specific info.
+
+ // Inform the scope that the corresponding code contains a with statement.
+ void RecordWithStatement() { scope_contains_with_ = true; }
+
+ // Inform the scope that the corresponding code contains an eval call.
+ 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;
+ }
+
+ // 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;
+ }
+
+ // In some cases we want to force context allocation for a whole scope.
+ void ForceContextAllocation() {
+ ASSERT(!already_resolved());
+ force_context_allocation_ = true;
+ }
+ bool has_forced_context_allocation() const {
+ return force_context_allocation_;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Predicates.
+
+ // Specific scope types.
+ bool is_eval_scope() const { return scope_type_ == EVAL_SCOPE; }
+ bool is_function_scope() const { return scope_type_ == FUNCTION_SCOPE; }
+ bool is_module_scope() const { return scope_type_ == MODULE_SCOPE; }
+ bool is_global_scope() const { return scope_type_ == GLOBAL_SCOPE; }
+ bool is_catch_scope() const { return scope_type_ == CATCH_SCOPE; }
+ bool is_block_scope() const { return scope_type_ == BLOCK_SCOPE; }
+ bool is_with_scope() const { return scope_type_ == WITH_SCOPE; }
+ bool is_declaration_scope() const {
+ return is_eval_scope() || is_function_scope() ||
+ is_module_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 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_;
+ }
+
+ // Is this scope inside a with statement.
+ bool inside_with() const { return scope_inside_with_; }
+ // Does this scope contain a with statement.
+ bool contains_with() const { return scope_contains_with_; }
+
+ // ---------------------------------------------------------------------------
+ // Accessors.
+
+ // The type of this scope.
+ ScopeType scope_type() const { return scope_type_; }
+
+ // The language mode of this scope.
+ LanguageMode language_mode() const { return language_mode_; }
+
+ // The variable corresponding the 'this' value.
+ Variable* receiver() { return receiver_; }
+
+ // The variable holding the function literal for named function
+ // literals, or NULL. Only valid for function scopes.
+ VariableDeclaration* function() const {
+ ASSERT(is_function_scope());
+ return function_;
+ }
+
+ // Parameters. The left-most parameter has index 0.
+ // Only valid for function scopes.
+ Variable* parameter(int index) const {
+ ASSERT(is_function_scope());
+ return params_[index];
+ }
+
+ int num_parameters() const { return params_.length(); }
+
+ // The local variable 'arguments' if we need to allocate it; NULL otherwise.
+ Variable* arguments() const { return arguments_; }
+
+ // Declarations list.
+ ZoneList<Declaration*>* declarations() { return &decls_; }
+
+ // Inner scope list.
+ ZoneList<Scope*>* inner_scopes() { return &inner_scopes_; }
+
+ // The scope immediately surrounding this scope, or NULL.
+ Scope* outer_scope() const { return outer_scope_; }
+
+ // The interface as inferred so far; only for module scopes.
+ Interface* interface() const { return interface_; }
+
+ // ---------------------------------------------------------------------------
+ // Variable allocation.
+
+ // 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);
+
+ // Current number of var or const locals.
+ int num_var_or_const() { return num_var_or_const_; }
+
+ // Result of variable allocation.
+ int num_stack_slots() const { return num_stack_slots_; }
+ int num_heap_slots() const { return num_heap_slots_; }
+
+ int StackLocalCount() const;
+ int ContextLocalCount() const;
+
+ // For global scopes, the number of module literals (including nested ones).
+ int num_modules() const { return num_modules_; }
+
+ // For module scopes, the host scope's internal variable binding this module.
+ Variable* module_var() const { return module_var_; }
+
+ // Make sure this scope and all outer scopes are eagerly compiled.
+ void ForceEagerCompilation() { force_eager_compilation_ = true; }
+
+ // Determine if we can use lazy compilation for this scope.
+ bool AllowsLazyCompilation() const;
+
+ // Determine if we can use lazy compilation for this scope without a context.
+ bool AllowsLazyCompilationWithoutContext() const;
+
+ // True if the outer context of this scope is always the native context.
+ bool HasTrivialOuterContext() const;
+
+ // True if the outer context allows lazy compilation of this scope.
+ bool HasLazyCompilableOuterContext() const;
+
+ // The number of contexts between this and scope; zero if this == scope.
+ int ContextChainLength(Scope* scope);
+
+ // Find the innermost global scope.
+ Scope* GlobalScope();
+
+ // Find the first function, global, or eval scope. This is the scope
+ // where var declarations will be hoisted to in the implementation.
+ Scope* DeclarationScope();
+
+ 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.
+ bool IsDeclared(Handle<String> name) {
+ // During formal parameter list parsing the scope only contains
+ // two variables inserted at initialization: "this" and "arguments".
+ // "this" is an invalid parameter name and "arguments" is invalid parameter
+ // name in strict mode. Therefore looking up with the map which includes
+ // "this" and "arguments" in addition to all formal parameters is safe.
+ return variables_.Lookup(name) != NULL;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Debugging.
+
+#ifdef DEBUG
+ void Print(int n = 0); // n = indentation; n < 0 => don't print recursively
+#endif
+
+ // ---------------------------------------------------------------------------
+ // Implementation.
+ protected:
+ friend class ParserFactory;
+
+ Isolate* const isolate_;
+
+ // Scope tree.
+ Scope* outer_scope_; // the immediately enclosing outer scope, or NULL
+ ZoneList<Scope*> inner_scopes_; // the immediately enclosed inner scopes
+
+ // The scope type.
+ ScopeType scope_type_;
+
+ // Debugging support.
+ Handle<String> scope_name_;
+
+ // The variables declared in this scope:
+ //
+ // All user-declared variables (incl. parameters). For global scopes
+ // variables may be implicitly 'declared' by being used (possibly in
+ // an inner scope) with no intervening with statements or eval calls.
+ VariableMap variables_;
+ // Compiler-allocated (user-invisible) internals.
+ ZoneList<Variable*> internals_;
+ // Compiler-allocated (user-invisible) temporaries.
+ ZoneList<Variable*> temps_;
+ // Parameter list in source order.
+ ZoneList<Variable*> params_;
+ // Variables that must be looked up dynamically.
+ DynamicScopePart* dynamics_;
+ // Unresolved variables referred to from this scope.
+ ZoneList<VariableProxy*> unresolved_;
+ // Declarations.
+ ZoneList<Declaration*> decls_;
+ // Convenience variable.
+ Variable* receiver_;
+ // Function variable, if any; function scopes only.
+ VariableDeclaration* function_;
+ // Convenience variable; function scopes only.
+ Variable* arguments_;
+ // Interface; module scopes only.
+ Interface* interface_;
+
+ // Illegal redeclaration.
+ Expression* illegal_redecl_;
+
+ // Scope-specific information computed during parsing.
+ //
+ // This scope is inside a 'with' of some outer scope.
+ bool scope_inside_with_;
+ // This scope contains a 'with' statement.
+ bool scope_contains_with_;
+ // 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_;
+ // The language mode of this scope.
+ LanguageMode language_mode_;
+ // Source positions.
+ int start_position_;
+ int end_position_;
+
+ // Computed via PropagateScopeInfo.
+ bool outer_scope_calls_non_strict_eval_;
+ bool inner_scope_calls_eval_;
+ bool force_eager_compilation_;
+ bool force_context_allocation_;
+
+ // True if it doesn't need scope resolution (e.g., if the scope was
+ // constructed based on a serialized scope info or a catch context).
+ bool already_resolved_;
+
+ // Computed as variables are declared.
+ int num_var_or_const_;
+
+ // Computed via AllocateVariables; function, block and catch scopes only.
+ int num_stack_slots_;
+ int num_heap_slots_;
+
+ // The number of modules (including nested ones).
+ int num_modules_;
+
+ // For module scopes, the host scope's internal variable binding this module.
+ Variable* module_var_;
+
+ // 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, 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,
+ BindingKind* binding_kind,
+ AstNodeFactory<AstNullVisitor>* factory);
+ MUST_USE_RESULT
+ bool ResolveVariable(CompilationInfo* info,
+ VariableProxy* proxy,
+ AstNodeFactory<AstNullVisitor>* factory);
+ MUST_USE_RESULT
+ bool ResolveVariablesRecursively(CompilationInfo* info,
+ AstNodeFactory<AstNullVisitor>* factory);
+
+ // Scope analysis.
+ bool PropagateScopeInfo(bool outer_scope_calls_non_strict_eval);
+ bool HasTrivialContext() const;
+
+ // Predicates.
+ bool MustAllocate(Variable* var);
+ bool MustAllocateInContext(Variable* var);
+ bool HasArgumentsParameter();
+
+ // Variable allocation.
+ void AllocateStackSlot(Variable* var);
+ void AllocateHeapSlot(Variable* var);
+ void AllocateParameterLocals();
+ void AllocateNonParameterLocal(Variable* var);
+ void AllocateNonParameterLocals();
+ void AllocateVariablesRecursively();
+ void AllocateModulesRecursively(Scope* host_scope);
+
+ // Resolve and fill in the allocation information for all variables
+ // in this scopes. Must be called *after* all scopes have been
+ // processed (parsed) to ensure that unresolved variables can be
+ // resolved properly.
+ //
+ // 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.
+ MUST_USE_RESULT
+ bool AllocateVariables(CompilationInfo* info,
+ AstNodeFactory<AstNullVisitor>* factory);
+
+ private:
+ // Construct a scope based on the scope info.
+ Scope(Scope* inner_scope, ScopeType type, Handle<ScopeInfo> scope_info,
+ Zone* zone);
+
+ // Construct a catch scope with a binding for the name.
+ Scope(Scope* inner_scope, Handle<String> catch_variable_name, Zone* zone);
+
+ void AddInnerScope(Scope* inner_scope) {
+ if (inner_scope != NULL) {
+ inner_scopes_.Add(inner_scope, zone_);
+ inner_scope->outer_scope_ = this;
+ }
+ }
+
+ void SetDefaults(ScopeType type,
+ Scope* outer_scope,
+ Handle<ScopeInfo> scope_info);
+
+ Zone* zone_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SCOPES_H_
diff --git a/chromium/v8/src/serialize.cc b/chromium/v8/src/serialize.cc
new file mode 100644
index 00000000000..746c926653d
--- /dev/null
+++ b/chromium/v8/src/serialize.cc
@@ -0,0 +1,1866 @@
+// Copyright 2012 the V8 project authors. 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 "accessors.h"
+#include "api.h"
+#include "bootstrapper.h"
+#include "deoptimizer.h"
+#include "execution.h"
+#include "global-handles.h"
+#include "ic-inl.h"
+#include "natives.h"
+#include "platform.h"
+#include "runtime.h"
+#include "serialize.h"
+#include "snapshot.h"
+#include "stub-cache.h"
+#include "v8threads.h"
+
+namespace v8 {
+namespace internal {
+
+
+// -----------------------------------------------------------------------------
+// Coding of external references.
+
+// The encoding of an external reference. The type is in the high word.
+// The id is in the low word.
+static uint32_t EncodeExternal(TypeCode type, uint16_t id) {
+ return static_cast<uint32_t>(type) << 16 | id;
+}
+
+
+static int* GetInternalPointer(StatsCounter* counter) {
+ // All counters refer to dummy_counter, if deserializing happens without
+ // setting up counters.
+ static int dummy_counter = 0;
+ return counter->Enabled() ? counter->GetInternalPointer() : &dummy_counter;
+}
+
+
+ExternalReferenceTable* ExternalReferenceTable::instance(Isolate* isolate) {
+ ExternalReferenceTable* external_reference_table =
+ isolate->external_reference_table();
+ if (external_reference_table == NULL) {
+ external_reference_table = new ExternalReferenceTable(isolate);
+ isolate->set_external_reference_table(external_reference_table);
+ }
+ return external_reference_table;
+}
+
+
+void ExternalReferenceTable::AddFromId(TypeCode type,
+ uint16_t id,
+ const char* name,
+ Isolate* isolate) {
+ Address address;
+ switch (type) {
+ case C_BUILTIN: {
+ ExternalReference ref(static_cast<Builtins::CFunctionId>(id), isolate);
+ address = ref.address();
+ break;
+ }
+ case BUILTIN: {
+ ExternalReference ref(static_cast<Builtins::Name>(id), isolate);
+ address = ref.address();
+ break;
+ }
+ case RUNTIME_FUNCTION: {
+ ExternalReference ref(static_cast<Runtime::FunctionId>(id), isolate);
+ address = ref.address();
+ break;
+ }
+ case IC_UTILITY: {
+ ExternalReference ref(IC_Utility(static_cast<IC::UtilityId>(id)),
+ isolate);
+ address = ref.address();
+ break;
+ }
+ default:
+ UNREACHABLE();
+ return;
+ }
+ Add(address, type, id, name);
+}
+
+
+void ExternalReferenceTable::Add(Address address,
+ TypeCode type,
+ uint16_t id,
+ const char* name) {
+ ASSERT_NE(NULL, address);
+ ExternalReferenceEntry entry;
+ entry.address = address;
+ entry.code = EncodeExternal(type, id);
+ entry.name = name;
+ ASSERT_NE(0, entry.code);
+ refs_.Add(entry);
+ if (id > max_id_[type]) max_id_[type] = id;
+}
+
+
+void ExternalReferenceTable::PopulateTable(Isolate* isolate) {
+ for (int type_code = 0; type_code < kTypeCodeCount; type_code++) {
+ max_id_[type_code] = 0;
+ }
+
+ // The following populates all of the different type of external references
+ // into the ExternalReferenceTable.
+ //
+ // NOTE: This function was originally 100k of code. It has since been
+ // rewritten to be mostly table driven, as the callback macro style tends to
+ // very easily cause code bloat. Please be careful in the future when adding
+ // new references.
+
+ struct RefTableEntry {
+ TypeCode type;
+ uint16_t id;
+ const char* name;
+ };
+
+ static const RefTableEntry ref_table[] = {
+ // Builtins
+#define DEF_ENTRY_C(name, ignored) \
+ { C_BUILTIN, \
+ Builtins::c_##name, \
+ "Builtins::" #name },
+
+ BUILTIN_LIST_C(DEF_ENTRY_C)
+#undef DEF_ENTRY_C
+
+#define DEF_ENTRY_C(name, ignored) \
+ { BUILTIN, \
+ Builtins::k##name, \
+ "Builtins::" #name },
+#define DEF_ENTRY_A(name, kind, state, extra) DEF_ENTRY_C(name, ignored)
+
+ BUILTIN_LIST_C(DEF_ENTRY_C)
+ BUILTIN_LIST_A(DEF_ENTRY_A)
+ BUILTIN_LIST_DEBUG_A(DEF_ENTRY_A)
+#undef DEF_ENTRY_C
+#undef DEF_ENTRY_A
+
+ // Runtime functions
+#define RUNTIME_ENTRY(name, nargs, ressize) \
+ { RUNTIME_FUNCTION, \
+ Runtime::k##name, \
+ "Runtime::" #name },
+
+ RUNTIME_FUNCTION_LIST(RUNTIME_ENTRY)
+#undef RUNTIME_ENTRY
+
+ // IC utilities
+#define IC_ENTRY(name) \
+ { IC_UTILITY, \
+ IC::k##name, \
+ "IC::" #name },
+
+ IC_UTIL_LIST(IC_ENTRY)
+#undef IC_ENTRY
+ }; // end of ref_table[].
+
+ for (size_t i = 0; i < ARRAY_SIZE(ref_table); ++i) {
+ AddFromId(ref_table[i].type,
+ ref_table[i].id,
+ ref_table[i].name,
+ isolate);
+ }
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Debug addresses
+ Add(Debug_Address(Debug::k_after_break_target_address).address(isolate),
+ DEBUG_ADDRESS,
+ Debug::k_after_break_target_address << kDebugIdShift,
+ "Debug::after_break_target_address()");
+ Add(Debug_Address(Debug::k_debug_break_slot_address).address(isolate),
+ DEBUG_ADDRESS,
+ Debug::k_debug_break_slot_address << kDebugIdShift,
+ "Debug::debug_break_slot_address()");
+ Add(Debug_Address(Debug::k_debug_break_return_address).address(isolate),
+ DEBUG_ADDRESS,
+ Debug::k_debug_break_return_address << kDebugIdShift,
+ "Debug::debug_break_return_address()");
+ Add(Debug_Address(Debug::k_restarter_frame_function_pointer).address(isolate),
+ DEBUG_ADDRESS,
+ Debug::k_restarter_frame_function_pointer << kDebugIdShift,
+ "Debug::restarter_frame_function_pointer_address()");
+#endif
+
+ // Stat counters
+ struct StatsRefTableEntry {
+ StatsCounter* (Counters::*counter)();
+ uint16_t id;
+ const char* name;
+ };
+
+ const StatsRefTableEntry stats_ref_table[] = {
+#define COUNTER_ENTRY(name, caption) \
+ { &Counters::name, \
+ Counters::k_##name, \
+ "Counters::" #name },
+
+ STATS_COUNTER_LIST_1(COUNTER_ENTRY)
+ STATS_COUNTER_LIST_2(COUNTER_ENTRY)
+#undef COUNTER_ENTRY
+ }; // end of stats_ref_table[].
+
+ Counters* counters = isolate->counters();
+ for (size_t i = 0; i < ARRAY_SIZE(stats_ref_table); ++i) {
+ Add(reinterpret_cast<Address>(GetInternalPointer(
+ (counters->*(stats_ref_table[i].counter))())),
+ STATS_COUNTER,
+ stats_ref_table[i].id,
+ stats_ref_table[i].name);
+ }
+
+ // Top addresses
+
+ const char* AddressNames[] = {
+#define BUILD_NAME_LITERAL(CamelName, hacker_name) \
+ "Isolate::" #hacker_name "_address",
+ FOR_EACH_ISOLATE_ADDRESS_NAME(BUILD_NAME_LITERAL)
+ NULL
+#undef BUILD_NAME_LITERAL
+ };
+
+ for (uint16_t i = 0; i < Isolate::kIsolateAddressCount; ++i) {
+ Add(isolate->get_address_from_id((Isolate::AddressId)i),
+ TOP_ADDRESS, i, AddressNames[i]);
+ }
+
+ // Accessors
+#define ACCESSOR_DESCRIPTOR_DECLARATION(name) \
+ Add((Address)&Accessors::name, \
+ ACCESSOR, \
+ Accessors::k##name, \
+ "Accessors::" #name);
+
+ ACCESSOR_DESCRIPTOR_LIST(ACCESSOR_DESCRIPTOR_DECLARATION)
+#undef ACCESSOR_DESCRIPTOR_DECLARATION
+
+ StubCache* stub_cache = isolate->stub_cache();
+
+ // Stub cache tables
+ Add(stub_cache->key_reference(StubCache::kPrimary).address(),
+ STUB_CACHE_TABLE,
+ 1,
+ "StubCache::primary_->key");
+ Add(stub_cache->value_reference(StubCache::kPrimary).address(),
+ STUB_CACHE_TABLE,
+ 2,
+ "StubCache::primary_->value");
+ Add(stub_cache->map_reference(StubCache::kPrimary).address(),
+ STUB_CACHE_TABLE,
+ 3,
+ "StubCache::primary_->map");
+ Add(stub_cache->key_reference(StubCache::kSecondary).address(),
+ STUB_CACHE_TABLE,
+ 4,
+ "StubCache::secondary_->key");
+ Add(stub_cache->value_reference(StubCache::kSecondary).address(),
+ STUB_CACHE_TABLE,
+ 5,
+ "StubCache::secondary_->value");
+ Add(stub_cache->map_reference(StubCache::kSecondary).address(),
+ STUB_CACHE_TABLE,
+ 6,
+ "StubCache::secondary_->map");
+
+ // Runtime entries
+ Add(ExternalReference::perform_gc_function(isolate).address(),
+ RUNTIME_ENTRY,
+ 1,
+ "Runtime::PerformGC");
+ Add(ExternalReference::fill_heap_number_with_random_function(
+ isolate).address(),
+ RUNTIME_ENTRY,
+ 2,
+ "V8::FillHeapNumberWithRandom");
+ Add(ExternalReference::random_uint32_function(isolate).address(),
+ RUNTIME_ENTRY,
+ 3,
+ "V8::Random");
+ Add(ExternalReference::delete_handle_scope_extensions(isolate).address(),
+ 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::roots_array_start(isolate).address(),
+ UNCLASSIFIED,
+ 3,
+ "Heap::roots_array_start()");
+ Add(ExternalReference::address_of_stack_limit(isolate).address(),
+ UNCLASSIFIED,
+ 4,
+ "StackGuard::address_of_jslimit()");
+ Add(ExternalReference::address_of_real_stack_limit(isolate).address(),
+ UNCLASSIFIED,
+ 5,
+ "StackGuard::address_of_real_jslimit()");
+#ifndef V8_INTERPRETED_REGEXP
+ Add(ExternalReference::address_of_regexp_stack_limit(isolate).address(),
+ UNCLASSIFIED,
+ 6,
+ "RegExpStack::limit_address()");
+ Add(ExternalReference::address_of_regexp_stack_memory_address(
+ isolate).address(),
+ UNCLASSIFIED,
+ 7,
+ "RegExpStack::memory_address()");
+ Add(ExternalReference::address_of_regexp_stack_memory_size(isolate).address(),
+ UNCLASSIFIED,
+ 8,
+ "RegExpStack::memory_size()");
+ Add(ExternalReference::address_of_static_offsets_vector(isolate).address(),
+ UNCLASSIFIED,
+ 9,
+ "OffsetsVector::static_offsets_vector");
+#endif // V8_INTERPRETED_REGEXP
+ Add(ExternalReference::new_space_start(isolate).address(),
+ UNCLASSIFIED,
+ 10,
+ "Heap::NewSpaceStart()");
+ Add(ExternalReference::new_space_mask(isolate).address(),
+ UNCLASSIFIED,
+ 11,
+ "Heap::NewSpaceMask()");
+ Add(ExternalReference::heap_always_allocate_scope_depth(isolate).address(),
+ UNCLASSIFIED,
+ 12,
+ "Heap::always_allocate_scope_depth()");
+ Add(ExternalReference::new_space_allocation_limit_address(isolate).address(),
+ UNCLASSIFIED,
+ 14,
+ "Heap::NewSpaceAllocationLimitAddress()");
+ Add(ExternalReference::new_space_allocation_top_address(isolate).address(),
+ UNCLASSIFIED,
+ 15,
+ "Heap::NewSpaceAllocationTopAddress()");
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Add(ExternalReference::debug_break(isolate).address(),
+ UNCLASSIFIED,
+ 16,
+ "Debug::Break()");
+ Add(ExternalReference::debug_step_in_fp_address(isolate).address(),
+ UNCLASSIFIED,
+ 17,
+ "Debug::step_in_fp_addr()");
+#endif
+ Add(ExternalReference::double_fp_operation(Token::ADD, isolate).address(),
+ UNCLASSIFIED,
+ 18,
+ "add_two_doubles");
+ Add(ExternalReference::double_fp_operation(Token::SUB, isolate).address(),
+ UNCLASSIFIED,
+ 19,
+ "sub_two_doubles");
+ Add(ExternalReference::double_fp_operation(Token::MUL, isolate).address(),
+ UNCLASSIFIED,
+ 20,
+ "mul_two_doubles");
+ Add(ExternalReference::double_fp_operation(Token::DIV, isolate).address(),
+ UNCLASSIFIED,
+ 21,
+ "div_two_doubles");
+ Add(ExternalReference::double_fp_operation(Token::MOD, isolate).address(),
+ UNCLASSIFIED,
+ 22,
+ "mod_two_doubles");
+ Add(ExternalReference::compare_doubles(isolate).address(),
+ UNCLASSIFIED,
+ 23,
+ "compare_doubles");
+#ifndef V8_INTERPRETED_REGEXP
+ Add(ExternalReference::re_case_insensitive_compare_uc16(isolate).address(),
+ UNCLASSIFIED,
+ 24,
+ "NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16()");
+ Add(ExternalReference::re_check_stack_guard_state(isolate).address(),
+ UNCLASSIFIED,
+ 25,
+ "RegExpMacroAssembler*::CheckStackGuardState()");
+ Add(ExternalReference::re_grow_stack(isolate).address(),
+ UNCLASSIFIED,
+ 26,
+ "NativeRegExpMacroAssembler::GrowStack()");
+ Add(ExternalReference::re_word_character_map().address(),
+ UNCLASSIFIED,
+ 27,
+ "NativeRegExpMacroAssembler::word_character_map");
+#endif // V8_INTERPRETED_REGEXP
+ // Keyed lookup cache.
+ Add(ExternalReference::keyed_lookup_cache_keys(isolate).address(),
+ UNCLASSIFIED,
+ 28,
+ "KeyedLookupCache::keys()");
+ Add(ExternalReference::keyed_lookup_cache_field_offsets(isolate).address(),
+ UNCLASSIFIED,
+ 29,
+ "KeyedLookupCache::field_offsets()");
+ Add(ExternalReference::transcendental_cache_array_address(isolate).address(),
+ UNCLASSIFIED,
+ 30,
+ "TranscendentalCache::caches()");
+ Add(ExternalReference::handle_scope_next_address(isolate).address(),
+ UNCLASSIFIED,
+ 31,
+ "HandleScope::next");
+ Add(ExternalReference::handle_scope_limit_address(isolate).address(),
+ UNCLASSIFIED,
+ 32,
+ "HandleScope::limit");
+ Add(ExternalReference::handle_scope_level_address(isolate).address(),
+ UNCLASSIFIED,
+ 33,
+ "HandleScope::level");
+ Add(ExternalReference::new_deoptimizer_function(isolate).address(),
+ UNCLASSIFIED,
+ 34,
+ "Deoptimizer::New()");
+ Add(ExternalReference::compute_output_frames_function(isolate).address(),
+ UNCLASSIFIED,
+ 35,
+ "Deoptimizer::ComputeOutputFrames()");
+ Add(ExternalReference::address_of_min_int().address(),
+ UNCLASSIFIED,
+ 36,
+ "LDoubleConstant::min_int");
+ Add(ExternalReference::address_of_one_half().address(),
+ UNCLASSIFIED,
+ 37,
+ "LDoubleConstant::one_half");
+ Add(ExternalReference::isolate_address(isolate).address(),
+ UNCLASSIFIED,
+ 38,
+ "isolate");
+ Add(ExternalReference::address_of_minus_zero().address(),
+ UNCLASSIFIED,
+ 39,
+ "LDoubleConstant::minus_zero");
+ Add(ExternalReference::address_of_negative_infinity().address(),
+ UNCLASSIFIED,
+ 40,
+ "LDoubleConstant::negative_infinity");
+ Add(ExternalReference::power_double_double_function(isolate).address(),
+ UNCLASSIFIED,
+ 41,
+ "power_double_double_function");
+ Add(ExternalReference::power_double_int_function(isolate).address(),
+ UNCLASSIFIED,
+ 42,
+ "power_double_int_function");
+ Add(ExternalReference::store_buffer_top(isolate).address(),
+ UNCLASSIFIED,
+ 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");
+ Add(ExternalReference::get_date_field_function(isolate).address(),
+ UNCLASSIFIED,
+ 46,
+ "JSDate::GetField");
+ Add(ExternalReference::date_cache_stamp(isolate).address(),
+ UNCLASSIFIED,
+ 47,
+ "date_cache_stamp");
+ Add(ExternalReference::address_of_pending_message_obj(isolate).address(),
+ UNCLASSIFIED,
+ 48,
+ "address_of_pending_message_obj");
+ Add(ExternalReference::address_of_has_pending_message(isolate).address(),
+ UNCLASSIFIED,
+ 49,
+ "address_of_has_pending_message");
+ Add(ExternalReference::address_of_pending_message_script(isolate).address(),
+ UNCLASSIFIED,
+ 50,
+ "pending_message_script");
+ Add(ExternalReference::get_make_code_young_function(isolate).address(),
+ UNCLASSIFIED,
+ 51,
+ "Code::MakeCodeYoung");
+ Add(ExternalReference::cpu_features().address(),
+ UNCLASSIFIED,
+ 52,
+ "cpu_features");
+ Add(ExternalReference::new_space_allocation_top_address(isolate).address(),
+ UNCLASSIFIED,
+ 53,
+ "Heap::NewSpaceAllocationTopAddress");
+ Add(ExternalReference::new_space_allocation_limit_address(isolate).address(),
+ UNCLASSIFIED,
+ 54,
+ "Heap::NewSpaceAllocationLimitAddress");
+ Add(ExternalReference(Runtime::kAllocateInNewSpace, isolate).address(),
+ UNCLASSIFIED,
+ 55,
+ "Runtime::AllocateInNewSpace");
+ Add(ExternalReference::old_pointer_space_allocation_top_address(
+ isolate).address(),
+ UNCLASSIFIED,
+ 56,
+ "Heap::OldPointerSpaceAllocationTopAddress");
+ Add(ExternalReference::old_pointer_space_allocation_limit_address(
+ isolate).address(),
+ UNCLASSIFIED,
+ 57,
+ "Heap::OldPointerSpaceAllocationLimitAddress");
+ Add(ExternalReference(Runtime::kAllocateInOldPointerSpace, isolate).address(),
+ UNCLASSIFIED,
+ 58,
+ "Runtime::AllocateInOldPointerSpace");
+ Add(ExternalReference::old_data_space_allocation_top_address(
+ isolate).address(),
+ UNCLASSIFIED,
+ 59,
+ "Heap::OldDataSpaceAllocationTopAddress");
+ Add(ExternalReference::old_data_space_allocation_limit_address(
+ isolate).address(),
+ UNCLASSIFIED,
+ 60,
+ "Heap::OldDataSpaceAllocationLimitAddress");
+ Add(ExternalReference(Runtime::kAllocateInOldDataSpace, isolate).address(),
+ UNCLASSIFIED,
+ 61,
+ "Runtime::AllocateInOldDataSpace");
+ Add(ExternalReference::new_space_high_promotion_mode_active_address(isolate).
+ address(),
+ UNCLASSIFIED,
+ 62,
+ "Heap::NewSpaceAllocationLimitAddress");
+ Add(ExternalReference::allocation_sites_list_address(isolate).address(),
+ UNCLASSIFIED,
+ 63,
+ "Heap::allocation_sites_list_address()");
+
+ // Add a small set of deopt entry addresses to encoder without generating the
+ // deopt table code, which isn't possible at deserialization time.
+ HandleScope scope(isolate);
+ for (int entry = 0; entry < kDeoptTableSerializeEntryCount; ++entry) {
+ Address address = Deoptimizer::GetDeoptimizationEntry(
+ isolate,
+ entry,
+ Deoptimizer::LAZY,
+ Deoptimizer::CALCULATE_ENTRY_ADDRESS);
+ Add(address, LAZY_DEOPTIMIZATION, 64 + entry, "lazy_deopt");
+ }
+}
+
+
+ExternalReferenceEncoder::ExternalReferenceEncoder()
+ : encodings_(Match),
+ isolate_(Isolate::Current()) {
+ ExternalReferenceTable* external_references =
+ ExternalReferenceTable::instance(isolate_);
+ for (int i = 0; i < external_references->size(); ++i) {
+ Put(external_references->address(i), i);
+ }
+}
+
+
+uint32_t ExternalReferenceEncoder::Encode(Address key) const {
+ int index = IndexOf(key);
+ ASSERT(key == NULL || index >= 0);
+ return index >=0 ?
+ ExternalReferenceTable::instance(isolate_)->code(index) : 0;
+}
+
+
+const char* ExternalReferenceEncoder::NameOfAddress(Address key) const {
+ int index = IndexOf(key);
+ return index >= 0 ?
+ ExternalReferenceTable::instance(isolate_)->name(index) : NULL;
+}
+
+
+int ExternalReferenceEncoder::IndexOf(Address key) const {
+ if (key == NULL) return -1;
+ HashMap::Entry* entry =
+ const_cast<HashMap&>(encodings_).Lookup(key, Hash(key), false);
+ return entry == NULL
+ ? -1
+ : static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
+}
+
+
+void ExternalReferenceEncoder::Put(Address key, int index) {
+ HashMap::Entry* entry = encodings_.Lookup(key, Hash(key), true);
+ entry->value = reinterpret_cast<void*>(index);
+}
+
+
+ExternalReferenceDecoder::ExternalReferenceDecoder()
+ : encodings_(NewArray<Address*>(kTypeCodeCount)),
+ isolate_(Isolate::Current()) {
+ ExternalReferenceTable* external_references =
+ ExternalReferenceTable::instance(isolate_);
+ for (int type = kFirstTypeCode; type < kTypeCodeCount; ++type) {
+ int max = external_references->max_id(type) + 1;
+ encodings_[type] = NewArray<Address>(max + 1);
+ }
+ for (int i = 0; i < external_references->size(); ++i) {
+ Put(external_references->code(i), external_references->address(i));
+ }
+}
+
+
+ExternalReferenceDecoder::~ExternalReferenceDecoder() {
+ for (int type = kFirstTypeCode; type < kTypeCodeCount; ++type) {
+ DeleteArray(encodings_[type]);
+ }
+ DeleteArray(encodings_);
+}
+
+
+bool Serializer::serialization_enabled_ = false;
+bool Serializer::too_late_to_enable_now_ = false;
+
+
+class CodeAddressMap: public CodeEventLogger {
+ public:
+ explicit CodeAddressMap(Isolate* isolate)
+ : isolate_(isolate) {
+ isolate->logger()->addCodeEventListener(this);
+ }
+
+ virtual ~CodeAddressMap() {
+ isolate_->logger()->removeCodeEventListener(this);
+ }
+
+ virtual void CodeMoveEvent(Address from, Address to) {
+ address_to_name_map_.Move(from, to);
+ }
+
+ virtual void CodeDeleteEvent(Address from) {
+ address_to_name_map_.Remove(from);
+ }
+
+ const char* Lookup(Address address) {
+ return address_to_name_map_.Lookup(address);
+ }
+
+ private:
+ class NameMap {
+ public:
+ NameMap() : impl_(&PointerEquals) {}
+
+ ~NameMap() {
+ for (HashMap::Entry* p = impl_.Start(); p != NULL; p = impl_.Next(p)) {
+ DeleteArray(static_cast<const char*>(p->value));
+ }
+ }
+
+ void Insert(Address code_address, const char* name, int name_size) {
+ HashMap::Entry* entry = FindOrCreateEntry(code_address);
+ if (entry->value == NULL) {
+ entry->value = CopyName(name, name_size);
+ }
+ }
+
+ const char* Lookup(Address code_address) {
+ HashMap::Entry* entry = FindEntry(code_address);
+ return (entry != NULL) ? static_cast<const char*>(entry->value) : NULL;
+ }
+
+ void Remove(Address code_address) {
+ HashMap::Entry* entry = FindEntry(code_address);
+ if (entry != NULL) {
+ DeleteArray(static_cast<char*>(entry->value));
+ RemoveEntry(entry);
+ }
+ }
+
+ void Move(Address from, Address to) {
+ if (from == to) return;
+ HashMap::Entry* from_entry = FindEntry(from);
+ ASSERT(from_entry != NULL);
+ void* value = from_entry->value;
+ RemoveEntry(from_entry);
+ HashMap::Entry* to_entry = FindOrCreateEntry(to);
+ ASSERT(to_entry->value == NULL);
+ to_entry->value = value;
+ }
+
+ private:
+ static bool PointerEquals(void* lhs, void* rhs) {
+ return lhs == rhs;
+ }
+
+ static char* CopyName(const char* name, int name_size) {
+ char* result = NewArray<char>(name_size + 1);
+ for (int i = 0; i < name_size; ++i) {
+ char c = name[i];
+ if (c == '\0') c = ' ';
+ result[i] = c;
+ }
+ result[name_size] = '\0';
+ return result;
+ }
+
+ HashMap::Entry* FindOrCreateEntry(Address code_address) {
+ return impl_.Lookup(code_address, ComputePointerHash(code_address), true);
+ }
+
+ HashMap::Entry* FindEntry(Address code_address) {
+ return impl_.Lookup(code_address,
+ ComputePointerHash(code_address),
+ false);
+ }
+
+ void RemoveEntry(HashMap::Entry* entry) {
+ impl_.Remove(entry->key, entry->hash);
+ }
+
+ HashMap impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(NameMap);
+ };
+
+ virtual void LogRecordedBuffer(Code* code,
+ SharedFunctionInfo*,
+ const char* name,
+ int length) {
+ address_to_name_map_.Insert(code->address(), name, length);
+ }
+
+ NameMap address_to_name_map_;
+ Isolate* isolate_;
+};
+
+
+CodeAddressMap* Serializer::code_address_map_ = NULL;
+
+
+void Serializer::Enable() {
+ if (!serialization_enabled_) {
+ ASSERT(!too_late_to_enable_now_);
+ }
+ if (serialization_enabled_) return;
+ serialization_enabled_ = true;
+ i::Isolate* isolate = Isolate::Current();
+ isolate->InitializeLoggingAndCounters();
+ code_address_map_ = new CodeAddressMap(isolate);
+}
+
+
+void Serializer::Disable() {
+ if (!serialization_enabled_) return;
+ serialization_enabled_ = false;
+ delete code_address_map_;
+ code_address_map_ = NULL;
+}
+
+
+Deserializer::Deserializer(SnapshotByteSource* source)
+ : isolate_(NULL),
+ source_(source),
+ external_reference_decoder_(NULL) {
+ for (int i = 0; i < LAST_SPACE + 1; i++) {
+ reservations_[i] = kUninitializedReservation;
+ }
+}
+
+
+void Deserializer::Deserialize() {
+ isolate_ = Isolate::Current();
+ ASSERT(isolate_ != NULL);
+ isolate_->heap()->ReserveSpace(reservations_, &high_water_[0]);
+ // No active threads.
+ ASSERT_EQ(NULL, isolate_->thread_manager()->FirstThreadStateInUse());
+ // No active handles.
+ ASSERT(isolate_->handle_scope_implementer()->blocks()->is_empty());
+ ASSERT_EQ(NULL, external_reference_decoder_);
+ external_reference_decoder_ = new ExternalReferenceDecoder();
+ isolate_->heap()->IterateStrongRoots(this, VISIT_ONLY_STRONG);
+ isolate_->heap()->RepairFreeListsAfterBoot();
+ isolate_->heap()->IterateWeakRoots(this, VISIT_ALL);
+
+ isolate_->heap()->set_native_contexts_list(
+ isolate_->heap()->undefined_value());
+ isolate_->heap()->set_array_buffers_list(
+ isolate_->heap()->undefined_value());
+
+ // The allocation site list is build during root iteration, but if no sites
+ // were encountered then it needs to be initialized to undefined.
+ if (isolate_->heap()->allocation_sites_list() == Smi::FromInt(0)) {
+ isolate_->heap()->set_allocation_sites_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();
+ }
+ }
+
+ // Issue code events for newly deserialized code objects.
+ LOG_CODE_EVENT(isolate_, LogCodeObjects());
+ LOG_CODE_EVENT(isolate_, LogCompiledFunctions());
+}
+
+
+void Deserializer::DeserializePartial(Object** root) {
+ isolate_ = Isolate::Current();
+ for (int i = NEW_SPACE; i < kNumberOfSpaces; i++) {
+ ASSERT(reservations_[i] != kUninitializedReservation);
+ }
+ isolate_->heap()->ReserveSpace(reservations_, &high_water_[0]);
+ if (external_reference_decoder_ == NULL) {
+ external_reference_decoder_ = new ExternalReferenceDecoder();
+ }
+
+ // Keep track of the code space start and end pointers in case new
+ // code objects were unserialized
+ OldSpace* code_space = isolate_->heap()->code_space();
+ Address start_address = code_space->top();
+ VisitPointer(root);
+
+ // There's no code deserialized here. If this assert fires
+ // then that's changed and logging should be added to notify
+ // the profiler et al of the new code.
+ CHECK_EQ(start_address, code_space->top());
+}
+
+
+Deserializer::~Deserializer() {
+ ASSERT(source_->AtEOF());
+ if (external_reference_decoder_) {
+ delete external_reference_decoder_;
+ external_reference_decoder_ = NULL;
+ }
+}
+
+
+// This is called on the roots. It is the driver of the deserialization
+// process. It is also called on the body of each function.
+void Deserializer::VisitPointers(Object** start, Object** end) {
+ // The space must be new space. Any other space would cause ReadChunk to try
+ // to update the remembered using NULL as the address.
+ ReadChunk(start, end, NEW_SPACE, NULL);
+}
+
+
+void Deserializer::RelinkAllocationSite(AllocationSite* site) {
+ if (isolate_->heap()->allocation_sites_list() == Smi::FromInt(0)) {
+ site->set_weak_next(isolate_->heap()->undefined_value());
+ } else {
+ site->set_weak_next(isolate_->heap()->allocation_sites_list());
+ }
+ isolate_->heap()->set_allocation_sites_list(site);
+}
+
+
+// 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 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,
+ Object** write_back) {
+ int size = source_->GetInt() << kObjectAlignmentBits;
+ Address address = Allocate(space_number, size);
+ HeapObject* obj = HeapObject::FromAddress(address);
+ *write_back = obj;
+ Object** current = reinterpret_cast<Object**>(address);
+ Object** limit = current + (size >> kPointerSizeLog2);
+ if (FLAG_log_snapshot_positions) {
+ LOG(isolate_, SnapshotPositionEvent(address, source_->position()));
+ }
+ ReadChunk(current, limit, space_number, address);
+
+ // TODO(mvstanton): consider treating the heap()->allocation_sites_list()
+ // as a (weak) root. If this root is relocated correctly,
+ // RelinkAllocationSite() isn't necessary.
+ if (obj->IsAllocationSite()) {
+ RelinkAllocationSite(AllocationSite::cast(obj));
+ }
+
+#ifdef DEBUG
+ bool is_codespace = (space_number == CODE_SPACE);
+ ASSERT(obj->IsCode() == is_codespace);
+#endif
+}
+
+void Deserializer::ReadChunk(Object** current,
+ Object** limit,
+ int source_space,
+ Address current_object_address) {
+ Isolate* const isolate = isolate_;
+ // Write barrier support costs around 1% in startup time. In fact there
+ // are no new space objects in current boot snapshots, so it's not needed,
+ // but that may change.
+ bool write_barrier_needed = (current_object_address != NULL &&
+ source_space != NEW_SPACE &&
+ source_space != CELL_SPACE &&
+ source_space != PROPERTY_CELL_SPACE &&
+ source_space != CODE_SPACE &&
+ source_space != OLD_DATA_SPACE);
+ while (current < limit) {
+ int data = source_->Get();
+ switch (data) {
+#define CASE_STATEMENT(where, how, within, space_number) \
+ case where + how + within + space_number: \
+ ASSERT((where & ~kPointedToMask) == 0); \
+ ASSERT((how & ~kHowToCodeMask) == 0); \
+ ASSERT((within & ~kWhereToPointMask) == 0); \
+ ASSERT((space_number & ~kSpaceMask) == 0);
+
+#define CASE_BODY(where, how, within, space_number_if_any) \
+ { \
+ bool emit_write_barrier = false; \
+ bool current_was_incremented = false; \
+ int space_number = space_number_if_any == kAnyOldSpace ? \
+ (data & kSpaceMask) : space_number_if_any; \
+ if (where == kNewObject && how == kPlain && within == kStartOfObject) {\
+ ReadObject(space_number, current); \
+ emit_write_barrier = (space_number == NEW_SPACE); \
+ } else { \
+ Object* new_object = NULL; /* May not be a real Object pointer. */ \
+ if (where == kNewObject) { \
+ ReadObject(space_number, &new_object); \
+ } else if (where == kRootArray) { \
+ int root_id = source_->GetInt(); \
+ 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 skip = source_->GetInt(); \
+ current = reinterpret_cast<Object**>(reinterpret_cast<Address>( \
+ current) + skip); \
+ 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); \
+ new_object = GetAddressFromEnd(data & kSpaceMask); \
+ } else { \
+ ASSERT(where == kBackrefWithSkip); \
+ int skip = source_->GetInt(); \
+ current = reinterpret_cast<Object**>( \
+ reinterpret_cast<Address>(current) + skip); \
+ emit_write_barrier = (space_number == NEW_SPACE); \
+ new_object = GetAddressFromEnd(data & kSpaceMask); \
+ } \
+ if (within == kInnerPointer) { \
+ if (space_number != CODE_SPACE || new_object->IsCode()) { \
+ Code* new_code_object = reinterpret_cast<Code*>(new_object); \
+ new_object = reinterpret_cast<Object*>( \
+ new_code_object->instruction_start()); \
+ } else { \
+ ASSERT(space_number == CODE_SPACE); \
+ Cell* cell = Cell::cast(new_object); \
+ new_object = reinterpret_cast<Object*>( \
+ cell->ValueAddress()); \
+ } \
+ } \
+ if (how == kFromCode) { \
+ Address location_of_branch_data = \
+ reinterpret_cast<Address>(current); \
+ Assembler::deserialization_set_special_target_at( \
+ location_of_branch_data, \
+ reinterpret_cast<Address>(new_object)); \
+ location_of_branch_data += Assembler::kSpecialTargetSize; \
+ current = reinterpret_cast<Object**>(location_of_branch_data); \
+ current_was_incremented = true; \
+ } else { \
+ *current = new_object; \
+ } \
+ } \
+ 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++; \
+ } \
+ break; \
+ } \
+
+// This generates a case and a body for the new space (which has to do extra
+// write barrier handling) and handles the other spaces with 8 fall-through
+// cases and one body.
+#define ALL_SPACES(where, how, within) \
+ CASE_STATEMENT(where, how, within, NEW_SPACE) \
+ CASE_BODY(where, how, within, NEW_SPACE) \
+ CASE_STATEMENT(where, how, within, OLD_DATA_SPACE) \
+ CASE_STATEMENT(where, how, within, OLD_POINTER_SPACE) \
+ CASE_STATEMENT(where, how, within, CODE_SPACE) \
+ CASE_STATEMENT(where, how, within, CELL_SPACE) \
+ CASE_STATEMENT(where, how, within, PROPERTY_CELL_SPACE) \
+ CASE_STATEMENT(where, how, within, MAP_SPACE) \
+ CASE_BODY(where, how, within, kAnyOldSpace)
+
+#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)
+
+#define COMMON_RAW_LENGTHS(f) \
+ f(1) \
+ f(2) \
+ f(3) \
+ f(4) \
+ f(5) \
+ f(6) \
+ f(7) \
+ f(8) \
+ f(9) \
+ f(10) \
+ f(11) \
+ f(12) \
+ f(13) \
+ f(14) \
+ f(15) \
+ f(16) \
+ f(17) \
+ f(18) \
+ f(19) \
+ f(20) \
+ f(21) \
+ f(22) \
+ f(23) \
+ f(24) \
+ f(25) \
+ f(26) \
+ f(27) \
+ f(28) \
+ f(29) \
+ f(30) \
+ f(31)
+
+ // We generate 15 cases and bodies that process special tags that combine
+ // the raw data tag and the length into one byte.
+#define RAW_CASE(index) \
+ case kRawData + index: { \
+ byte* raw_data_out = reinterpret_cast<byte*>(current); \
+ source_->CopyRaw(raw_data_out, index * kPointerSize); \
+ current = \
+ reinterpret_cast<Object**>(raw_data_out + index * kPointerSize); \
+ break; \
+ }
+ COMMON_RAW_LENGTHS(RAW_CASE)
+#undef RAW_CASE
+
+ // Deserialize a chunk of raw data that doesn't have one of the popular
+ // lengths.
+ case kRawData: {
+ int size = source_->GetInt();
+ byte* raw_data_out = reinterpret_cast<byte*>(current);
+ source_->CopyRaw(raw_data_out, size);
+ break;
+ }
+
+ SIXTEEN_CASES(kRootArrayConstants + kNoSkipDistance)
+ SIXTEEN_CASES(kRootArrayConstants + kNoSkipDistance + 16) {
+ int root_id = RootArrayConstantFromByteCode(data);
+ Object* object = isolate->heap()->roots_array_start()[root_id];
+ ASSERT(!isolate->heap()->InNewSpace(object));
+ *current++ = object;
+ break;
+ }
+
+ SIXTEEN_CASES(kRootArrayConstants + kHasSkipDistance)
+ SIXTEEN_CASES(kRootArrayConstants + kHasSkipDistance + 16) {
+ int root_id = RootArrayConstantFromByteCode(data);
+ int skip = source_->GetInt();
+ current = reinterpret_cast<Object**>(
+ reinterpret_cast<intptr_t>(current) + skip);
+ 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 == 13);
+ case kConstantRepeat:
+ FOUR_CASES(kConstantRepeat + 1)
+ FOUR_CASES(kConstantRepeat + 5)
+ FOUR_CASES(kConstantRepeat + 9) {
+ 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.
+ ALL_SPACES(kNewObject, kPlain, kStartOfObject)
+ // Support for direct instruction pointers in functions. It's an inner
+ // pointer because it points at the entry point, not at the start of the
+ // code object.
+ CASE_STATEMENT(kNewObject, kPlain, kInnerPointer, CODE_SPACE)
+ CASE_BODY(kNewObject, kPlain, kInnerPointer, CODE_SPACE)
+ // Deserialize a new code object and write a pointer to its first
+ // instruction to the current code object.
+ ALL_SPACES(kNewObject, kFromCode, kInnerPointer)
+ // Find a recently deserialized object using its offset from the current
+ // allocation point and write a pointer to it to the current object.
+ ALL_SPACES(kBackref, kPlain, kStartOfObject)
+ ALL_SPACES(kBackrefWithSkip, kPlain, kStartOfObject)
+#if V8_TARGET_ARCH_MIPS
+ // Deserialize a new object from pointer found in code and write
+ // a pointer to it to the current object. Required only for MIPS, and
+ // omitted on the other architectures because it is fully unrolled and
+ // would cause bloat.
+ ALL_SPACES(kNewObject, kFromCode, kStartOfObject)
+ // Find a recently deserialized code object using its offset from the
+ // current allocation point and write a pointer to it to the current
+ // object. Required only for MIPS.
+ ALL_SPACES(kBackref, kFromCode, kStartOfObject)
+ ALL_SPACES(kBackrefWithSkip, kFromCode, kStartOfObject)
+#endif
+ // Find a recently deserialized code object using its offset from the
+ // current allocation point and write a pointer to its first instruction
+ // to the current code object or the instruction pointer in a function
+ // object.
+ ALL_SPACES(kBackref, kFromCode, kInnerPointer)
+ ALL_SPACES(kBackrefWithSkip, kFromCode, kInnerPointer)
+ ALL_SPACES(kBackref, kPlain, kInnerPointer)
+ ALL_SPACES(kBackrefWithSkip, kPlain, kInnerPointer)
+ // Find an object in the roots array and write a pointer to it to the
+ // current object.
+ CASE_STATEMENT(kRootArray, kPlain, kStartOfObject, 0)
+ CASE_BODY(kRootArray, kPlain, kStartOfObject, 0)
+ // Find an object in the partial snapshots cache and write a pointer to it
+ // to the current object.
+ CASE_STATEMENT(kPartialSnapshotCache, kPlain, kStartOfObject, 0)
+ CASE_BODY(kPartialSnapshotCache,
+ kPlain,
+ kStartOfObject,
+ 0)
+ // Find an code entry in the partial snapshots cache and
+ // write a pointer to it to the current object.
+ CASE_STATEMENT(kPartialSnapshotCache, kPlain, kInnerPointer, 0)
+ CASE_BODY(kPartialSnapshotCache,
+ kPlain,
+ kInnerPointer,
+ 0)
+ // Find an external reference and write a pointer to it to the current
+ // object.
+ CASE_STATEMENT(kExternalReference, kPlain, kStartOfObject, 0)
+ CASE_BODY(kExternalReference,
+ kPlain,
+ kStartOfObject,
+ 0)
+ // Find an external reference and write a pointer to it in the current
+ // code object.
+ CASE_STATEMENT(kExternalReference, kFromCode, kStartOfObject, 0)
+ CASE_BODY(kExternalReference,
+ kFromCode,
+ kStartOfObject,
+ 0)
+
+#undef CASE_STATEMENT
+#undef CASE_BODY
+#undef ALL_SPACES
+
+ case kSkip: {
+ int size = source_->GetInt();
+ current = reinterpret_cast<Object**>(
+ reinterpret_cast<intptr_t>(current) + size);
+ break;
+ }
+
+ case kNativesStringResource: {
+ int index = source_->Get();
+ Vector<const char> source_vector = Natives::GetRawScriptSource(index);
+ NativesExternalStringResource* resource =
+ new NativesExternalStringResource(isolate->bootstrapper(),
+ source_vector.start(),
+ source_vector.length());
+ *current++ = reinterpret_cast<Object*>(resource);
+ break;
+ }
+
+ case kSynchronize: {
+ // If we get here then that indicates that you have a mismatch between
+ // the number of GC roots when serializing and deserializing.
+ UNREACHABLE();
+ }
+
+ default:
+ UNREACHABLE();
+ }
+ }
+ ASSERT_EQ(limit, current);
+}
+
+
+void SnapshotByteSink::PutInt(uintptr_t integer, const char* description) {
+ ASSERT(integer < 1 << 22);
+ integer <<= 2;
+ int bytes = 1;
+ if (integer > 0xff) bytes = 2;
+ if (integer > 0xffff) bytes = 3;
+ integer |= bytes;
+ Put(static_cast<int>(integer & 0xff), "IntPart1");
+ if (bytes > 1) Put(static_cast<int>((integer >> 8) & 0xff), "IntPart2");
+ if (bytes > 2) Put(static_cast<int>((integer >> 16) & 0xff), "IntPart3");
+}
+
+
+Serializer::Serializer(SnapshotByteSink* sink)
+ : sink_(sink),
+ current_root_index_(0),
+ external_reference_encoder_(new ExternalReferenceEncoder),
+ root_index_wave_front_(0) {
+ isolate_ = Isolate::Current();
+ // 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_->IsDefaultIsolate());
+ for (int i = 0; i <= LAST_SPACE; i++) {
+ fullness_[i] = 0;
+ }
+}
+
+
+Serializer::~Serializer() {
+ delete external_reference_encoder_;
+}
+
+
+void StartupSerializer::SerializeStrongReferences() {
+ Isolate* isolate = Isolate::Current();
+ // No active threads.
+ CHECK_EQ(NULL, Isolate::Current()->thread_manager()->FirstThreadStateInUse());
+ // No active or weak handles.
+ CHECK(isolate->handle_scope_implementer()->blocks()->is_empty());
+ CHECK_EQ(0, isolate->global_handles()->NumberOfWeakHandles());
+ CHECK_EQ(0, isolate->eternal_handles()->NumberOfHandles());
+ // We don't support serializing installed extensions.
+ CHECK(!isolate->has_installed_extensions());
+
+ HEAP->IterateStrongRoots(this, VISIT_ONLY_STRONG);
+}
+
+
+void PartialSerializer::Serialize(Object** object) {
+ this->VisitPointer(object);
+ Pad();
+}
+
+
+void Serializer::VisitPointers(Object** start, Object** end) {
+ Isolate* isolate = Isolate::Current();
+
+ for (Object** current = start; current < end; current++) {
+ 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");
+ sink_->PutInt(kPointerSize, "SkipOneWord");
+ } else if ((*current)->IsSmi()) {
+ sink_->Put(kRawData + 1, "Smi");
+ for (int i = 0; i < kPointerSize; i++) {
+ sink_->Put(reinterpret_cast<byte*>(current)[i], "Byte");
+ }
+ } else {
+ SerializeObject(*current, kPlain, kStartOfObject, 0);
+ }
+ }
+}
+
+
+// This ensures that the partial snapshot cache keeps things alive during GC and
+// tracks their movement. When it is called during serialization of the startup
+// snapshot nothing happens. When the partial (context) snapshot is created,
+// this array is populated with the pointers that the partial snapshot will
+// need. As that happens we emit serialized objects to the startup snapshot
+// that correspond to the elements of this cache array. On deserialization we
+// therefore need to visit the cache array. This fills it up with pointers to
+// deserialized objects.
+void SerializerDeserializer::Iterate(ObjectVisitor* visitor) {
+ if (Serializer::enabled()) return;
+ Isolate* isolate = Isolate::Current();
+ for (int i = 0; ; i++) {
+ if (isolate->serialize_partial_snapshot_cache_length() <= i) {
+ // Extend the array ready to get a value from the visitor when
+ // deserializing.
+ isolate->PushToPartialSnapshotCache(Smi::FromInt(0));
+ }
+ Object** cache = isolate->serialize_partial_snapshot_cache();
+ visitor->VisitPointers(&cache[i], &cache[i + 1]);
+ // Sentinel is the undefined object, which is a root so it will not normally
+ // be found in the cache.
+ if (cache[i] == isolate->heap()->undefined_value()) {
+ break;
+ }
+ }
+}
+
+
+int PartialSerializer::PartialSnapshotCacheIndex(HeapObject* heap_object) {
+ Isolate* isolate = Isolate::Current();
+
+ for (int i = 0;
+ i < isolate->serialize_partial_snapshot_cache_length();
+ i++) {
+ Object* entry = isolate->serialize_partial_snapshot_cache()[i];
+ if (entry == heap_object) return i;
+ }
+
+ // We didn't find the object in the cache. So we add it to the cache and
+ // then visit the pointer so that it becomes part of the startup snapshot
+ // and we can refer to it from the partial snapshot.
+ int length = isolate->serialize_partial_snapshot_cache_length();
+ isolate->PushToPartialSnapshotCache(heap_object);
+ startup_serializer_->VisitPointer(reinterpret_cast<Object**>(&heap_object));
+ // We don't recurse from the startup snapshot generator into the partial
+ // snapshot generator.
+ ASSERT(length == isolate->serialize_partial_snapshot_cache_length() - 1);
+ return length;
+}
+
+
+int Serializer::RootIndex(HeapObject* heap_object, HowToCode from) {
+ 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) {
+#if V8_TARGET_ARCH_MIPS
+ if (from == kFromCode) {
+ // In order to avoid code bloat in the deserializer we don't have
+ // support for the encoding that specifies a particular root should
+ // be written into the lui/ori instructions on MIPS. Therefore we
+ // should not generate such serialization data for MIPS.
+ return kInvalidRootIndex;
+ }
+#endif
+ return i;
+ }
+ }
+ return kInvalidRootIndex;
+}
+
+
+// Encode the location of an already deserialized object in order to write its
+// location into a later object. We can encode the location as an offset from
+// the start of the deserialized objects or as an offset backwards from the
+// current allocation pointer.
+void Serializer::SerializeReferenceToPreviousObject(
+ int space,
+ int address,
+ HowToCode how_to_code,
+ WhereToPoint where_to_point,
+ int skip) {
+ int offset = CurrentAllocationAddress(space) - address;
+ // Shift out the bits that are always 0.
+ offset >>= kObjectAlignmentBits;
+ if (skip == 0) {
+ sink_->Put(kBackref + how_to_code + where_to_point + space, "BackRefSer");
+ } else {
+ sink_->Put(kBackrefWithSkip + how_to_code + where_to_point + space,
+ "BackRefSerWithSkip");
+ sink_->PutInt(skip, "BackRefSkipDistance");
+ }
+ sink_->PutInt(offset, "offset");
+}
+
+
+void StartupSerializer::SerializeObject(
+ Object* o,
+ HowToCode how_to_code,
+ WhereToPoint where_to_point,
+ int skip) {
+ CHECK(o->IsHeapObject());
+ HeapObject* heap_object = HeapObject::cast(o);
+
+ int root_index;
+ if ((root_index = RootIndex(heap_object, how_to_code)) != kInvalidRootIndex) {
+ PutRoot(root_index, heap_object, how_to_code, where_to_point, skip);
+ return;
+ }
+
+ if (address_mapper_.IsMapped(heap_object)) {
+ int space = SpaceOfObject(heap_object);
+ int address = address_mapper_.MappedTo(heap_object);
+ SerializeReferenceToPreviousObject(space,
+ address,
+ how_to_code,
+ where_to_point,
+ skip);
+ } else {
+ if (skip != 0) {
+ sink_->Put(kSkip, "FlushPendingSkip");
+ sink_->PutInt(skip, "SkipDistance");
+ }
+
+ // Object has not yet been serialized. Serialize it here.
+ ObjectSerializer object_serializer(this,
+ heap_object,
+ sink_,
+ how_to_code,
+ where_to_point);
+ object_serializer.Serialize();
+ }
+}
+
+
+void StartupSerializer::SerializeWeakReferences() {
+ // This phase comes right after the partial serialization (of the snapshot).
+ // After we have done the partial serialization the partial snapshot cache
+ // will contain some references needed to decode the partial snapshot. We
+ // add one entry with 'undefined' which is the sentinel that the deserializer
+ // uses to know it is done deserializing the array.
+ Isolate* isolate = Isolate::Current();
+ Object* undefined = isolate->heap()->undefined_value();
+ VisitPointer(&undefined);
+ HEAP->IterateWeakRoots(this, VISIT_ALL);
+ Pad();
+}
+
+
+void Serializer::PutRoot(int root_index,
+ HeapObject* object,
+ SerializerDeserializer::HowToCode how_to_code,
+ SerializerDeserializer::WhereToPoint where_to_point,
+ int skip) {
+ if (how_to_code == kPlain &&
+ where_to_point == kStartOfObject &&
+ root_index < kRootArrayNumberOfConstantEncodings &&
+ !HEAP->InNewSpace(object)) {
+ if (skip == 0) {
+ sink_->Put(kRootArrayConstants + kNoSkipDistance + root_index,
+ "RootConstant");
+ } else {
+ sink_->Put(kRootArrayConstants + kHasSkipDistance + root_index,
+ "RootConstant");
+ sink_->PutInt(skip, "SkipInPutRoot");
+ }
+ } else {
+ if (skip != 0) {
+ sink_->Put(kSkip, "SkipFromPutRoot");
+ sink_->PutInt(skip, "SkipFromPutRootDistance");
+ }
+ 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,
+ WhereToPoint where_to_point,
+ int skip) {
+ 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()->empty_fixed_array());
+ }
+
+ int root_index;
+ if ((root_index = RootIndex(heap_object, how_to_code)) != kInvalidRootIndex) {
+ PutRoot(root_index, heap_object, how_to_code, where_to_point, skip);
+ return;
+ }
+
+ if (ShouldBeInThePartialSnapshotCache(heap_object)) {
+ if (skip != 0) {
+ sink_->Put(kSkip, "SkipFromSerializeObject");
+ sink_->PutInt(skip, "SkipDistanceFromSerializeObject");
+ }
+
+ int cache_index = PartialSnapshotCacheIndex(heap_object);
+ sink_->Put(kPartialSnapshotCache + how_to_code + where_to_point,
+ "PartialSnapshotCache");
+ sink_->PutInt(cache_index, "partial_snapshot_cache_index");
+ return;
+ }
+
+ // Pointers from the partial snapshot to the objects in the startup snapshot
+ // should go through the root array or through the partial snapshot cache.
+ // If this is not the case you may have to add something to the root array.
+ ASSERT(!startup_serializer_->address_mapper()->IsMapped(heap_object));
+ // All the internalized strings that the partial snapshot needs should be
+ // either in the root table or in the partial snapshot cache.
+ ASSERT(!heap_object->IsInternalizedString());
+
+ if (address_mapper_.IsMapped(heap_object)) {
+ int space = SpaceOfObject(heap_object);
+ int address = address_mapper_.MappedTo(heap_object);
+ SerializeReferenceToPreviousObject(space,
+ address,
+ how_to_code,
+ where_to_point,
+ skip);
+ } else {
+ if (skip != 0) {
+ sink_->Put(kSkip, "SkipFromSerializeObject");
+ sink_->PutInt(skip, "SkipDistanceFromSerializeObject");
+ }
+ // Object has not yet been serialized. Serialize it here.
+ ObjectSerializer serializer(this,
+ heap_object,
+ sink_,
+ how_to_code,
+ where_to_point);
+ serializer.Serialize();
+ }
+}
+
+
+void Serializer::ObjectSerializer::Serialize() {
+ int space = Serializer::SpaceOfObject(object_);
+ int size = object_->Size();
+
+ sink_->Put(kNewObject + reference_representation_ + space,
+ "ObjectSerialization");
+ sink_->PutInt(size >> kObjectAlignmentBits, "Size in words");
+
+ ASSERT(code_address_map_);
+ const char* code_name = code_address_map_->Lookup(object_->address());
+ LOG(serializer_->isolate_,
+ CodeNameEvent(object_->address(), sink_->Position(), code_name));
+ LOG(serializer_->isolate_,
+ SnapshotPositionEvent(object_->address(), sink_->Position()));
+
+ // Mark this object as already serialized.
+ int offset = serializer_->Allocate(space, size);
+ serializer_->address_mapper()->AddMapping(object_, offset);
+
+ // Serialize the map (first word of the object).
+ serializer_->SerializeObject(object_->map(), kPlain, kStartOfObject, 0);
+
+ // Serialize the rest of the object.
+ CHECK_EQ(0, bytes_processed_so_far_);
+ bytes_processed_so_far_ = kPointerSize;
+ object_->IterateBody(object_->map()->instance_type(), size, this);
+ OutputRawData(object_->address() + size);
+}
+
+
+void Serializer::ObjectSerializer::VisitPointers(Object** start,
+ Object** end) {
+ Object** current = start;
+ while (current < end) {
+ while (current < end && (*current)->IsSmi()) current++;
+ if (current < end) OutputRawData(reinterpret_cast<Address>(current));
+
+ while (current < end && !(*current)->IsSmi()) {
+ HeapObject* current_contents = HeapObject::cast(*current);
+ int root_index = serializer_->RootIndex(current_contents, kPlain);
+ // 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, 0);
+ bytes_processed_so_far_ += kPointerSize;
+ current++;
+ }
+ }
+ }
+}
+
+
+void Serializer::ObjectSerializer::VisitEmbeddedPointer(RelocInfo* rinfo) {
+ Object** current = rinfo->target_object_address();
+
+ int skip = OutputRawData(rinfo->target_address_address(),
+ kCanReturnSkipInsteadOfSkipping);
+ HowToCode representation = rinfo->IsCodedSpecially() ? kFromCode : kPlain;
+ serializer_->SerializeObject(*current, representation, kStartOfObject, skip);
+ bytes_processed_so_far_ += rinfo->target_address_size();
+}
+
+
+void Serializer::ObjectSerializer::VisitExternalReferences(Address* start,
+ Address* end) {
+ Address references_start = reinterpret_cast<Address>(start);
+ int skip = OutputRawData(references_start, kCanReturnSkipInsteadOfSkipping);
+
+ for (Address* current = start; current < end; current++) {
+ sink_->Put(kExternalReference + kPlain + kStartOfObject, "ExternalRef");
+ sink_->PutInt(skip, "SkipB4ExternalRef");
+ skip = 0;
+ int reference_id = serializer_->EncodeExternalReference(*current);
+ sink_->PutInt(reference_id, "reference id");
+ }
+ bytes_processed_so_far_ += static_cast<int>((end - start) * kPointerSize);
+}
+
+
+void Serializer::ObjectSerializer::VisitExternalReference(RelocInfo* rinfo) {
+ Address references_start = rinfo->target_address_address();
+ int skip = OutputRawData(references_start, kCanReturnSkipInsteadOfSkipping);
+
+ Address* current = rinfo->target_reference_address();
+ int representation = rinfo->IsCodedSpecially() ?
+ kFromCode + kStartOfObject : kPlain + kStartOfObject;
+ sink_->Put(kExternalReference + representation, "ExternalRef");
+ sink_->PutInt(skip, "SkipB4ExternalRef");
+ 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();
+ int skip = OutputRawData(target_start, kCanReturnSkipInsteadOfSkipping);
+ Address target = rinfo->target_address();
+ uint32_t encoding = serializer_->EncodeExternalReference(target);
+ CHECK(target == NULL ? encoding == 0 : encoding != 0);
+ int representation;
+ // Can't use a ternary operator because of gcc.
+ if (rinfo->IsCodedSpecially()) {
+ representation = kStartOfObject + kFromCode;
+ } else {
+ representation = kStartOfObject + kPlain;
+ }
+ sink_->Put(kExternalReference + representation, "ExternalReference");
+ sink_->PutInt(skip, "SkipB4ExternalRef");
+ sink_->PutInt(encoding, "reference id");
+ bytes_processed_so_far_ += rinfo->target_address_size();
+}
+
+
+void Serializer::ObjectSerializer::VisitCodeTarget(RelocInfo* rinfo) {
+ CHECK(RelocInfo::IsCodeTarget(rinfo->rmode()));
+ Address target_start = rinfo->target_address_address();
+ int skip = OutputRawData(target_start, kCanReturnSkipInsteadOfSkipping);
+ Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ serializer_->SerializeObject(target, kFromCode, kInnerPointer, skip);
+ bytes_processed_so_far_ += rinfo->target_address_size();
+}
+
+
+void Serializer::ObjectSerializer::VisitCodeEntry(Address entry_address) {
+ Code* target = Code::cast(Code::GetObjectFromEntryAddress(entry_address));
+ int skip = OutputRawData(entry_address, kCanReturnSkipInsteadOfSkipping);
+ serializer_->SerializeObject(target, kPlain, kInnerPointer, skip);
+ bytes_processed_so_far_ += kPointerSize;
+}
+
+
+void Serializer::ObjectSerializer::VisitCell(RelocInfo* rinfo) {
+ ASSERT(rinfo->rmode() == RelocInfo::CELL);
+ Cell* cell = Cell::cast(rinfo->target_cell());
+ int skip = OutputRawData(rinfo->pc(), kCanReturnSkipInsteadOfSkipping);
+ serializer_->SerializeObject(cell, kPlain, kInnerPointer, skip);
+}
+
+
+void Serializer::ObjectSerializer::VisitExternalAsciiString(
+ v8::String::ExternalAsciiStringResource** resource_pointer) {
+ Address references_start = reinterpret_cast<Address>(resource_pointer);
+ OutputRawData(references_start);
+ for (int i = 0; i < Natives::GetBuiltinsCount(); i++) {
+ Object* source = HEAP->natives_source_cache()->get(i);
+ if (!source->IsUndefined()) {
+ ExternalAsciiString* string = ExternalAsciiString::cast(source);
+ typedef v8::String::ExternalAsciiStringResource Resource;
+ const Resource* resource = string->resource();
+ if (resource == *resource_pointer) {
+ sink_->Put(kNativesStringResource, "NativesStringResource");
+ sink_->PutSection(i, "NativesStringResourceEnd");
+ bytes_processed_so_far_ += sizeof(resource);
+ return;
+ }
+ }
+ }
+ // One of the strings in the natives cache should match the resource. We
+ // can't serialize any other kinds of external strings.
+ UNREACHABLE();
+}
+
+
+int Serializer::ObjectSerializer::OutputRawData(
+ Address up_to, Serializer::ObjectSerializer::ReturnSkip return_skip) {
+ Address object_start = object_->address();
+ Address base = object_start + bytes_processed_so_far_;
+ int up_to_offset = static_cast<int>(up_to - object_start);
+ int to_skip = up_to_offset - bytes_processed_so_far_;
+ int bytes_to_output = to_skip;
+ bytes_processed_so_far_ += to_skip;
+ // This assert will fail if the reloc info gives us the target_address_address
+ // locations in a non-ascending order. Luckily that doesn't happen.
+ ASSERT(to_skip >= 0);
+ bool outputting_code = false;
+ if (to_skip != 0 && code_object_ && !code_has_been_output_) {
+ // Output the code all at once and fix later.
+ bytes_to_output = object_->Size() + to_skip - bytes_processed_so_far_;
+ outputting_code = true;
+ code_has_been_output_ = true;
+ }
+ if (bytes_to_output != 0 &&
+ (!code_object_ || outputting_code)) {
+#define RAW_CASE(index) \
+ if (!outputting_code && bytes_to_output == index * kPointerSize && \
+ index * kPointerSize == to_skip) { \
+ sink_->PutSection(kRawData + index, "RawDataFixed"); \
+ to_skip = 0; /* This insn already skips. */ \
+ } else /* NOLINT */
+ COMMON_RAW_LENGTHS(RAW_CASE)
+#undef RAW_CASE
+ { /* NOLINT */
+ // We always end up here if we are outputting the code of a code object.
+ sink_->Put(kRawData, "RawData");
+ sink_->PutInt(bytes_to_output, "length");
+ }
+ for (int i = 0; i < bytes_to_output; i++) {
+ unsigned int data = base[i];
+ sink_->PutSection(data, "Byte");
+ }
+ }
+ if (to_skip != 0 && return_skip == kIgnoringReturn) {
+ sink_->Put(kSkip, "Skip");
+ sink_->PutInt(to_skip, "SkipDistance");
+ to_skip = 0;
+ }
+ return to_skip;
+}
+
+
+int Serializer::SpaceOfObject(HeapObject* object) {
+ for (int i = FIRST_SPACE; i <= LAST_SPACE; i++) {
+ AllocationSpace s = static_cast<AllocationSpace>(i);
+ if (HEAP->InSpace(object, s)) {
+ ASSERT(i < kNumberOfSpaces);
+ return i;
+ }
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+
+int Serializer::Allocate(int space, int size) {
+ CHECK(space >= 0 && space < kNumberOfSpaces);
+ int allocation_address = fullness_[space];
+ fullness_[space] = allocation_address + size;
+ return allocation_address;
+}
+
+
+int Serializer::SpaceAreaSize(int space) {
+ if (space == CODE_SPACE) {
+ return isolate_->memory_allocator()->CodePageAreaSize();
+ } else {
+ return Page::kPageSize - Page::kObjectStartOffset;
+ }
+}
+
+
+void Serializer::Pad() {
+ // The non-branching GetInt will read up to 3 bytes too far, so we need
+ // to pad the snapshot to make sure we don't read over the end.
+ for (unsigned i = 0; i < sizeof(int32_t) - 1; i++) {
+ sink_->Put(kNop, "Padding");
+ }
+}
+
+
+bool SnapshotByteSource::AtEOF() {
+ if (0u + length_ - position_ > 2 * sizeof(uint32_t)) return false;
+ for (int x = position_; x < length_; x++) {
+ if (data_[x] != SerializerDeserializer::nop()) return false;
+ }
+ return true;
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/serialize.h b/chromium/v8/src/serialize.h
new file mode 100644
index 00000000000..563f0a06d02
--- /dev/null
+++ b/chromium/v8/src/serialize.h
@@ -0,0 +1,664 @@
+// Copyright 2012 the V8 project authors. 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_SERIALIZE_H_
+#define V8_SERIALIZE_H_
+
+#include "hashmap.h"
+
+namespace v8 {
+namespace internal {
+
+// A TypeCode is used to distinguish different kinds of external reference.
+// It is a single bit to make testing for types easy.
+enum TypeCode {
+ UNCLASSIFIED, // One-of-a-kind references.
+ BUILTIN,
+ RUNTIME_FUNCTION,
+ IC_UTILITY,
+ DEBUG_ADDRESS,
+ STATS_COUNTER,
+ TOP_ADDRESS,
+ C_BUILTIN,
+ EXTENSION,
+ ACCESSOR,
+ RUNTIME_ENTRY,
+ STUB_CACHE_TABLE,
+ LAZY_DEOPTIMIZATION
+};
+
+const int kTypeCodeCount = LAZY_DEOPTIMIZATION + 1;
+const int kFirstTypeCode = UNCLASSIFIED;
+
+const int kReferenceIdBits = 16;
+const int kReferenceIdMask = (1 << kReferenceIdBits) - 1;
+const int kReferenceTypeShift = kReferenceIdBits;
+const int kDebugRegisterBits = 4;
+const int kDebugIdShift = kDebugRegisterBits;
+
+const int kDeoptTableSerializeEntryCount = 8;
+
+// ExternalReferenceTable is a helper class that defines the relationship
+// between external references and their encodings. It is used to build
+// hashmaps in ExternalReferenceEncoder and ExternalReferenceDecoder.
+class ExternalReferenceTable {
+ public:
+ static ExternalReferenceTable* instance(Isolate* isolate);
+
+ ~ExternalReferenceTable() { }
+
+ int size() const { return refs_.length(); }
+
+ Address address(int i) { return refs_[i].address; }
+
+ uint32_t code(int i) { return refs_[i].code; }
+
+ const char* name(int i) { return refs_[i].name; }
+
+ int max_id(int code) { return max_id_[code]; }
+
+ private:
+ explicit ExternalReferenceTable(Isolate* isolate) : refs_(64) {
+ PopulateTable(isolate);
+ }
+
+ struct ExternalReferenceEntry {
+ Address address;
+ uint32_t code;
+ const char* name;
+ };
+
+ void PopulateTable(Isolate* isolate);
+
+ // For a few types of references, we can get their address from their id.
+ void AddFromId(TypeCode type,
+ uint16_t id,
+ const char* name,
+ Isolate* isolate);
+
+ // For other types of references, the caller will figure out the address.
+ void Add(Address address, TypeCode type, uint16_t id, const char* name);
+
+ List<ExternalReferenceEntry> refs_;
+ int max_id_[kTypeCodeCount];
+};
+
+
+class ExternalReferenceEncoder {
+ public:
+ ExternalReferenceEncoder();
+
+ uint32_t Encode(Address key) const;
+
+ const char* NameOfAddress(Address key) const;
+
+ private:
+ HashMap encodings_;
+ static uint32_t Hash(Address key) {
+ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key) >> 2);
+ }
+
+ int IndexOf(Address key) const;
+
+ static bool Match(void* key1, void* key2) { return key1 == key2; }
+
+ void Put(Address key, int index);
+
+ Isolate* isolate_;
+};
+
+
+class ExternalReferenceDecoder {
+ public:
+ ExternalReferenceDecoder();
+ ~ExternalReferenceDecoder();
+
+ Address Decode(uint32_t key) const {
+ if (key == 0) return NULL;
+ return *Lookup(key);
+ }
+
+ private:
+ Address** encodings_;
+
+ Address* Lookup(uint32_t key) const {
+ int type = key >> kReferenceTypeShift;
+ ASSERT(kFirstTypeCode <= type && type < kTypeCodeCount);
+ int id = key & kReferenceIdMask;
+ return &encodings_[type][id];
+ }
+
+ void Put(uint32_t key, Address value) {
+ *Lookup(key) = value;
+ }
+
+ Isolate* isolate_;
+};
+
+
+class SnapshotByteSource {
+ public:
+ SnapshotByteSource(const byte* array, int length)
+ : data_(array), length_(length), position_(0) { }
+
+ bool HasMore() { return position_ < length_; }
+
+ int Get() {
+ ASSERT(position_ < length_);
+ return data_[position_++];
+ }
+
+ int32_t GetUnalignedInt() {
+#if defined(V8_HOST_CAN_READ_UNALIGNED) && __BYTE_ORDER == __LITTLE_ENDIAN
+ int32_t answer;
+ ASSERT(position_ + sizeof(answer) <= length_ + 0u);
+ answer = *reinterpret_cast<const int32_t*>(data_ + position_);
+#else
+ int32_t answer = data_[position_];
+ answer |= data_[position_ + 1] << 8;
+ answer |= data_[position_ + 2] << 16;
+ answer |= data_[position_ + 3] << 24;
+#endif
+ return answer;
+ }
+
+ void Advance(int by) { position_ += by; }
+
+ inline void CopyRaw(byte* to, int number_of_bytes);
+
+ inline int GetInt();
+
+ bool AtEOF();
+
+ int position() { return position_; }
+
+ private:
+ const byte* data_;
+ int length_;
+ int position_;
+};
+
+
+// The Serializer/Deserializer class is a common superclass for Serializer and
+// Deserializer which is used to store common constants and methods used by
+// both.
+class SerializerDeserializer: public ObjectVisitor {
+ public:
+ static void Iterate(ObjectVisitor* visitor);
+
+ static int nop() { return kNop; }
+
+ protected:
+ // Where the pointed-to object can be found:
+ enum Where {
+ kNewObject = 0, // Object is next in snapshot.
+ // 1-6 One per space.
+ kRootArray = 0x9, // Object is found in root array.
+ kPartialSnapshotCache = 0xa, // Object is in the cache.
+ kExternalReference = 0xb, // Pointer to an external reference.
+ kSkip = 0xc, // Skip n bytes.
+ kNop = 0xd, // Does nothing, used to pad.
+ // 0xe-0xf Free.
+ kBackref = 0x10, // Object is described relative to end.
+ // 0x11-0x16 One per space.
+ kBackrefWithSkip = 0x18, // Object is described relative to end.
+ // 0x19-0x1e One per space.
+ // 0x20-0x3f Used by misc. tags below.
+ kPointedToMask = 0x3f
+ };
+
+ // How to code the pointer to the object.
+ enum HowToCode {
+ kPlain = 0, // Straight pointer.
+ // What this means depends on the architecture:
+ kFromCode = 0x40, // A pointer inlined in code.
+ kHowToCodeMask = 0x40
+ };
+
+ // For kRootArrayConstants
+ enum WithSkip {
+ kNoSkipDistance = 0,
+ kHasSkipDistance = 0x40,
+ kWithSkipMask = 0x40
+ };
+
+ // Where to point within the object.
+ enum WhereToPoint {
+ kStartOfObject = 0,
+ kInnerPointer = 0x80, // First insn in code object or payload of cell.
+ kWhereToPointMask = 0x80
+ };
+
+ // Misc.
+ // Raw data to be copied from the snapshot. This byte code does not advance
+ // the current pointer, which is used for code objects, where we write the
+ // entire code in one memcpy, then fix up stuff with kSkip and other byte
+ // codes that overwrite data.
+ static const int kRawData = 0x20;
+ // Some common raw lengths: 0x21-0x3f. These autoadvance the current pointer.
+ // A tag emitted at strategic points in the snapshot to delineate sections.
+ // If the deserializer does not find these at the expected moments then it
+ // is an indication that the snapshot and the VM do not fit together.
+ // Examine the build process for architecture, version or configuration
+ // mismatches.
+ static const int kSynchronize = 0x70;
+ // Used for the source code of the natives, which is in the executable, but
+ // is referred to from external strings in the snapshot.
+ static const int kNativesStringResource = 0x71;
+ static const int kRepeat = 0x72;
+ static const int kConstantRepeat = 0x73;
+ // 0x73-0x7f Repeat last word (subtract 0x72 to get the count).
+ static const int kMaxRepeats = 0x7f - 0x72;
+ static int CodeForRepeats(int repeats) {
+ ASSERT(repeats >= 1 && repeats <= kMaxRepeats);
+ return 0x72 + repeats;
+ }
+ static int RepeatsForCode(int byte_code) {
+ ASSERT(byte_code >= kConstantRepeat && byte_code <= 0x7f);
+ return byte_code - 0x72;
+ }
+ static const int kRootArrayConstants = 0xa0;
+ // 0xa0-0xbf Things from the first 32 elements of the root array.
+ static const int kRootArrayNumberOfConstantEncodings = 0x20;
+ static int RootArrayConstantFromByteCode(int byte_code) {
+ return byte_code & 0x1f;
+ }
+
+ static const int kNumberOfSpaces = LO_SPACE;
+ static const int kAnyOldSpace = -1;
+
+ // A bitmask for getting the space out of an instruction.
+ static const int kSpaceMask = 7;
+};
+
+
+int SnapshotByteSource::GetInt() {
+ // This way of variable-length encoding integers does not suffer from branch
+ // mispredictions.
+ uint32_t answer = GetUnalignedInt();
+ int bytes = answer & 3;
+ Advance(bytes);
+ uint32_t mask = 0xffffffffu;
+ mask >>= 32 - (bytes << 3);
+ answer &= mask;
+ answer >>= 2;
+ return answer;
+}
+
+
+void SnapshotByteSource::CopyRaw(byte* to, int number_of_bytes) {
+ OS::MemCopy(to, data_ + position_, number_of_bytes);
+ position_ += number_of_bytes;
+}
+
+
+// A Deserializer reads a snapshot and reconstructs the Object graph it defines.
+class Deserializer: public SerializerDeserializer {
+ public:
+ // Create a deserializer from a snapshot byte source.
+ explicit Deserializer(SnapshotByteSource* source);
+
+ virtual ~Deserializer();
+
+ // Deserialize the snapshot into an empty heap.
+ void Deserialize();
+
+ // Deserialize a single object and the objects reachable from it.
+ void DeserializePartial(Object** root);
+
+ void set_reservation(int space_number, int reservation) {
+ ASSERT(space_number >= 0);
+ ASSERT(space_number <= LAST_SPACE);
+ reservations_[space_number] = reservation;
+ }
+
+ private:
+ virtual void VisitPointers(Object** start, Object** end);
+
+ virtual void VisitExternalReferences(Address* start, Address* end) {
+ UNREACHABLE();
+ }
+
+ virtual void VisitRuntimeEntry(RelocInfo* rinfo) {
+ UNREACHABLE();
+ }
+
+ // Allocation sites are present in the snapshot, and must be linked into
+ // a list at deserialization time.
+ void RelinkAllocationSite(AllocationSite* site);
+
+ // 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);
+ void ReadObject(int space_number, Object** write_back);
+
+ // This routine both allocates a new object, and also keeps
+ // track of where objects have been allocated so that we can
+ // fix back references when deserializing.
+ Address Allocate(int space_index, int size) {
+ Address address = high_water_[space_index];
+ high_water_[space_index] = address + size;
+ return address;
+ }
+
+ // This returns the address of an object that has been described in the
+ // snapshot as being offset bytes back in a particular space.
+ HeapObject* GetAddressFromEnd(int space) {
+ int offset = source_->GetInt();
+ offset <<= kObjectAlignmentBits;
+ return HeapObject::FromAddress(high_water_[space] - offset);
+ }
+
+
+ // Cached current isolate.
+ Isolate* isolate_;
+
+ SnapshotByteSource* source_;
+ // This is the address of the next object that will be allocated in each
+ // space. It is used to calculate the addresses of back-references.
+ Address high_water_[LAST_SPACE + 1];
+
+ int reservations_[LAST_SPACE + 1];
+ static const intptr_t kUninitializedReservation = -1;
+
+ ExternalReferenceDecoder* external_reference_decoder_;
+
+ DISALLOW_COPY_AND_ASSIGN(Deserializer);
+};
+
+
+class SnapshotByteSink {
+ public:
+ virtual ~SnapshotByteSink() { }
+ virtual void Put(int byte, const char* description) = 0;
+ virtual void PutSection(int byte, const char* description) {
+ Put(byte, description);
+ }
+ void PutInt(uintptr_t integer, const char* description);
+ virtual int Position() = 0;
+};
+
+
+// Mapping objects to their location after deserialization.
+// This is used during building, but not at runtime by V8.
+class SerializationAddressMapper {
+ public:
+ SerializationAddressMapper()
+ : no_allocation_(),
+ serialization_map_(new HashMap(&SerializationMatchFun)) { }
+
+ ~SerializationAddressMapper() {
+ delete serialization_map_;
+ }
+
+ bool IsMapped(HeapObject* obj) {
+ return serialization_map_->Lookup(Key(obj), Hash(obj), false) != NULL;
+ }
+
+ int MappedTo(HeapObject* obj) {
+ ASSERT(IsMapped(obj));
+ return static_cast<int>(reinterpret_cast<intptr_t>(
+ serialization_map_->Lookup(Key(obj), Hash(obj), false)->value));
+ }
+
+ void AddMapping(HeapObject* obj, int to) {
+ ASSERT(!IsMapped(obj));
+ HashMap::Entry* entry =
+ serialization_map_->Lookup(Key(obj), Hash(obj), true);
+ entry->value = Value(to);
+ }
+
+ private:
+ static bool SerializationMatchFun(void* key1, void* key2) {
+ return key1 == key2;
+ }
+
+ static uint32_t Hash(HeapObject* obj) {
+ return static_cast<int32_t>(reinterpret_cast<intptr_t>(obj->address()));
+ }
+
+ static void* Key(HeapObject* obj) {
+ return reinterpret_cast<void*>(obj->address());
+ }
+
+ static void* Value(int v) {
+ return reinterpret_cast<void*>(v);
+ }
+
+ DisallowHeapAllocation no_allocation_;
+ HashMap* serialization_map_;
+ DISALLOW_COPY_AND_ASSIGN(SerializationAddressMapper);
+};
+
+
+class CodeAddressMap;
+
+// There can be only one serializer per V8 process.
+class Serializer : public SerializerDeserializer {
+ public:
+ explicit Serializer(SnapshotByteSink* sink);
+ ~Serializer();
+ void VisitPointers(Object** start, Object** end);
+ // You can call this after serialization to find out how much space was used
+ // in each space.
+ int CurrentAllocationAddress(int space) {
+ ASSERT(space < kNumberOfSpaces);
+ return fullness_[space];
+ }
+
+ static void Enable();
+ static void Disable();
+
+ // Call this when you have made use of the fact that there is no serialization
+ // going on.
+ static void TooLateToEnableNow() { too_late_to_enable_now_ = true; }
+ static bool enabled() { return serialization_enabled_; }
+ SerializationAddressMapper* address_mapper() { return &address_mapper_; }
+ void PutRoot(int index,
+ HeapObject* object,
+ HowToCode how,
+ WhereToPoint where,
+ int skip);
+
+ protected:
+ static const int kInvalidRootIndex = -1;
+
+ int RootIndex(HeapObject* heap_object, HowToCode from);
+ 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:
+ ObjectSerializer(Serializer* serializer,
+ Object* o,
+ SnapshotByteSink* sink,
+ HowToCode how_to_code,
+ WhereToPoint where_to_point)
+ : serializer_(serializer),
+ object_(HeapObject::cast(o)),
+ sink_(sink),
+ reference_representation_(how_to_code + where_to_point),
+ bytes_processed_so_far_(0),
+ code_object_(o->IsCode()),
+ code_has_been_output_(false) { }
+ 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 VisitCell(RelocInfo* rinfo);
+ void VisitRuntimeEntry(RelocInfo* reloc);
+ // Used for seralizing the external strings that hold the natives source.
+ void VisitExternalAsciiString(
+ v8::String::ExternalAsciiStringResource** resource);
+ // We can't serialize a heap with external two byte strings.
+ void VisitExternalTwoByteString(
+ v8::String::ExternalStringResource** resource) {
+ UNREACHABLE();
+ }
+
+ private:
+ enum ReturnSkip { kCanReturnSkipInsteadOfSkipping, kIgnoringReturn };
+ // This function outputs or skips the raw data between the last pointer and
+ // up to the current position. It optionally can just return the number of
+ // bytes to skip instead of performing a skip instruction, in case the skip
+ // can be merged into the next instruction.
+ int OutputRawData(Address up_to, ReturnSkip return_skip = kIgnoringReturn);
+
+ Serializer* serializer_;
+ HeapObject* object_;
+ SnapshotByteSink* sink_;
+ int reference_representation_;
+ int bytes_processed_so_far_;
+ bool code_object_;
+ bool code_has_been_output_;
+ };
+
+ virtual void SerializeObject(Object* o,
+ HowToCode how_to_code,
+ WhereToPoint where_to_point,
+ int skip) = 0;
+ void SerializeReferenceToPreviousObject(
+ int space,
+ int address,
+ HowToCode how_to_code,
+ WhereToPoint where_to_point,
+ int skip);
+ void InitializeAllocators();
+ // This will return the space for an object.
+ static int SpaceOfObject(HeapObject* object);
+ int Allocate(int space, int size);
+ int EncodeExternalReference(Address addr) {
+ return external_reference_encoder_->Encode(addr);
+ }
+
+ int SpaceAreaSize(int space);
+
+ Isolate* isolate_;
+ // Keep track of the fullness of each space in order to generate
+ // relative addresses for back references.
+ int fullness_[LAST_SPACE + 1];
+ SnapshotByteSink* sink_;
+ int current_root_index_;
+ ExternalReferenceEncoder* external_reference_encoder_;
+ static bool serialization_enabled_;
+ // Did we already make use of the fact that serialization was not enabled?
+ static bool too_late_to_enable_now_;
+ SerializationAddressMapper address_mapper_;
+ intptr_t root_index_wave_front_;
+ void Pad();
+
+ friend class ObjectSerializer;
+ friend class Deserializer;
+
+ private:
+ static CodeAddressMap* code_address_map_;
+ DISALLOW_COPY_AND_ASSIGN(Serializer);
+};
+
+
+class PartialSerializer : public Serializer {
+ public:
+ PartialSerializer(Serializer* startup_snapshot_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.
+ virtual void Serialize(Object** o);
+ virtual void SerializeObject(Object* o,
+ HowToCode how_to_code,
+ WhereToPoint where_to_point,
+ int skip);
+
+ protected:
+ virtual int PartialSnapshotCacheIndex(HeapObject* o);
+ virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) {
+ // Scripts should be referred only through shared function infos. We can't
+ // allow them to be part of the partial snapshot because they contain a
+ // unique ID, and deserializing several partial snapshots containing script
+ // would cause dupes.
+ ASSERT(!o->IsScript());
+ return o->IsName() || o->IsSharedFunctionInfo() ||
+ o->IsHeapNumber() || o->IsCode() ||
+ o->IsScopeInfo() ||
+ o->map() == HEAP->fixed_cow_array_map();
+ }
+
+ private:
+ Serializer* startup_serializer_;
+ DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
+};
+
+
+class StartupSerializer : public Serializer {
+ public:
+ 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 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 (e.g. the string table).
+ virtual void SerializeStrongReferences();
+ virtual void SerializeObject(Object* o,
+ HowToCode how_to_code,
+ WhereToPoint where_to_point,
+ int skip);
+ void SerializeWeakReferences();
+ void Serialize() {
+ SerializeStrongReferences();
+ SerializeWeakReferences();
+ Pad();
+ }
+
+ private:
+ virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) {
+ return false;
+ }
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_SERIALIZE_H_
diff --git a/chromium/v8/src/simulator.h b/chromium/v8/src/simulator.h
new file mode 100644
index 00000000000..485e9306458
--- /dev/null
+++ b/chromium/v8/src/simulator.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef V8_SIMULATOR_H_
+#define V8_SIMULATOR_H_
+
+#if V8_TARGET_ARCH_IA32
+#include "ia32/simulator-ia32.h"
+#elif V8_TARGET_ARCH_X64
+#include "x64/simulator-x64.h"
+#elif V8_TARGET_ARCH_ARM
+#include "arm/simulator-arm.h"
+#elif V8_TARGET_ARCH_MIPS
+#include "mips/simulator-mips.h"
+#else
+#error Unsupported target architecture.
+#endif
+
+#endif // V8_SIMULATOR_H_
diff --git a/chromium/v8/src/small-pointer-list.h b/chromium/v8/src/small-pointer-list.h
new file mode 100644
index 00000000000..295a06f26af
--- /dev/null
+++ b/chromium/v8/src/small-pointer-list.h
@@ -0,0 +1,198 @@
+// 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_SMALL_POINTER_LIST_H_
+#define V8_SMALL_POINTER_LIST_H_
+
+#include "checks.h"
+#include "v8globals.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+// SmallPointerList is a list optimized for storing no or just a
+// single value. When more values are given it falls back to ZoneList.
+//
+// The interface tries to be as close to List from list.h as possible.
+template <typename T>
+class SmallPointerList {
+ public:
+ SmallPointerList() : data_(kEmptyTag) {}
+
+ SmallPointerList(int capacity, Zone* zone) : data_(kEmptyTag) {
+ Reserve(capacity, zone);
+ }
+
+ void Reserve(int capacity, Zone* zone) {
+ if (capacity < 2) return;
+ if ((data_ & kTagMask) == kListTag) {
+ if (list()->capacity() >= capacity) return;
+ int old_length = list()->length();
+ list()->AddBlock(NULL, capacity - list()->capacity(), zone);
+ list()->Rewind(old_length);
+ return;
+ }
+ PointerList* list = new(zone) PointerList(capacity, zone);
+ if ((data_ & kTagMask) == kSingletonTag) {
+ list->Add(single_value(), zone);
+ }
+ ASSERT(IsAligned(reinterpret_cast<intptr_t>(list), kPointerAlignment));
+ data_ = reinterpret_cast<intptr_t>(list) | kListTag;
+ }
+
+ void Clear() {
+ data_ = kEmptyTag;
+ }
+
+ void Sort() {
+ if ((data_ & kTagMask) == kListTag) {
+ list()->Sort(compare_value);
+ }
+ }
+
+ bool is_empty() const { return length() == 0; }
+
+ int length() const {
+ if ((data_ & kTagMask) == kEmptyTag) return 0;
+ if ((data_ & kTagMask) == kSingletonTag) return 1;
+ return list()->length();
+ }
+
+ void Add(T* pointer, Zone* zone) {
+ ASSERT(IsAligned(reinterpret_cast<intptr_t>(pointer), kPointerAlignment));
+ if ((data_ & kTagMask) == kEmptyTag) {
+ data_ = reinterpret_cast<intptr_t>(pointer) | kSingletonTag;
+ return;
+ }
+ if ((data_ & kTagMask) == kSingletonTag) {
+ PointerList* list = new(zone) PointerList(2, zone);
+ list->Add(single_value(), zone);
+ list->Add(pointer, zone);
+ ASSERT(IsAligned(reinterpret_cast<intptr_t>(list), kPointerAlignment));
+ data_ = reinterpret_cast<intptr_t>(list) | kListTag;
+ return;
+ }
+ list()->Add(pointer, zone);
+ }
+
+ // Note: returns T* and not T*& (unlike List from list.h).
+ // This makes the implementation simpler and more const correct.
+ T* at(int i) const {
+ ASSERT((data_ & kTagMask) != kEmptyTag);
+ if ((data_ & kTagMask) == kSingletonTag) {
+ ASSERT(i == 0);
+ return single_value();
+ }
+ return list()->at(i);
+ }
+
+ // See the note above.
+ T* operator[](int i) const { return at(i); }
+
+ // Remove the given element from the list (if present).
+ void RemoveElement(T* pointer) {
+ if ((data_ & kTagMask) == kEmptyTag) return;
+ if ((data_ & kTagMask) == kSingletonTag) {
+ if (pointer == single_value()) {
+ data_ = kEmptyTag;
+ }
+ return;
+ }
+ list()->RemoveElement(pointer);
+ }
+
+ T* RemoveLast() {
+ ASSERT((data_ & kTagMask) != kEmptyTag);
+ if ((data_ & kTagMask) == kSingletonTag) {
+ T* result = single_value();
+ data_ = kEmptyTag;
+ return result;
+ }
+ return list()->RemoveLast();
+ }
+
+ void Rewind(int pos) {
+ if ((data_ & kTagMask) == kEmptyTag) {
+ ASSERT(pos == 0);
+ return;
+ }
+ if ((data_ & kTagMask) == kSingletonTag) {
+ ASSERT(pos == 0 || pos == 1);
+ if (pos == 0) {
+ data_ = kEmptyTag;
+ }
+ return;
+ }
+ list()->Rewind(pos);
+ }
+
+ int CountOccurrences(T* pointer, int start, int end) const {
+ if ((data_ & kTagMask) == kEmptyTag) return 0;
+ if ((data_ & kTagMask) == kSingletonTag) {
+ if (start == 0 && end >= 0) {
+ return (single_value() == pointer) ? 1 : 0;
+ }
+ return 0;
+ }
+ return list()->CountOccurrences(pointer, start, end);
+ }
+
+ private:
+ typedef ZoneList<T*> PointerList;
+
+ static int compare_value(T* const* a, T* const* b) {
+ return Compare<T>(**a, **b);
+ }
+
+ static const intptr_t kEmptyTag = 1;
+ static const intptr_t kSingletonTag = 0;
+ static const intptr_t kListTag = 2;
+ static const intptr_t kTagMask = 3;
+ static const intptr_t kValueMask = ~kTagMask;
+
+ STATIC_ASSERT(kTagMask + 1 <= kPointerAlignment);
+
+ T* single_value() const {
+ ASSERT((data_ & kTagMask) == kSingletonTag);
+ STATIC_ASSERT(kSingletonTag == 0);
+ return reinterpret_cast<T*>(data_);
+ }
+
+ PointerList* list() const {
+ ASSERT((data_ & kTagMask) == kListTag);
+ return reinterpret_cast<PointerList*>(data_ & kValueMask);
+ }
+
+ intptr_t data_;
+
+ DISALLOW_COPY_AND_ASSIGN(SmallPointerList);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SMALL_POINTER_LIST_H_
diff --git a/chromium/v8/src/smart-pointers.h b/chromium/v8/src/smart-pointers.h
new file mode 100644
index 00000000000..7c35b2aff27
--- /dev/null
+++ b/chromium/v8/src/smart-pointers.h
@@ -0,0 +1,150 @@
+// 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_SMART_POINTERS_H_
+#define V8_SMART_POINTERS_H_
+
+namespace v8 {
+namespace internal {
+
+
+template<typename Deallocator, typename T>
+class SmartPointerBase {
+ public:
+ // Default constructor. Constructs an empty scoped pointer.
+ inline SmartPointerBase() : p_(NULL) {}
+
+ // Constructs a scoped pointer from a plain one.
+ explicit inline SmartPointerBase(T* ptr) : p_(ptr) {}
+
+ // Copy constructor removes the pointer from the original to avoid double
+ // freeing.
+ inline SmartPointerBase(const SmartPointerBase<Deallocator, T>& rhs)
+ : p_(rhs.p_) {
+ const_cast<SmartPointerBase<Deallocator, T>&>(rhs).p_ = NULL;
+ }
+
+ // When the destructor of the scoped pointer is executed the plain pointer
+ // is deleted using DeleteArray. This implies that you must allocate with
+ // NewArray.
+ inline ~SmartPointerBase() { if (p_) Deallocator::Delete(p_); }
+
+ inline T* operator->() const { return p_; }
+
+ // You can get the underlying pointer out with the * operator.
+ inline T* operator*() { return p_; }
+
+ // You can use [n] to index as if it was a plain pointer.
+ inline T& operator[](size_t i) {
+ return p_[i];
+ }
+
+ // You can use [n] to index as if it was a plain pointer.
+ const inline T& operator[](size_t i) const {
+ return p_[i];
+ }
+
+ // We don't have implicit conversion to a T* since that hinders migration:
+ // You would not be able to change a method from returning a T* to
+ // returning an SmartArrayPointer<T> and then get errors wherever it is used.
+
+
+ // If you want to take out the plain pointer and don't want it automatically
+ // deleted then call Detach(). Afterwards, the smart pointer is empty
+ // (NULL).
+ inline T* Detach() {
+ T* temp = p_;
+ p_ = NULL;
+ return temp;
+ }
+
+ inline void Reset(T* new_value) {
+ if (p_) Deallocator::Delete(p_);
+ p_ = new_value;
+ }
+
+ // Assignment requires an empty (NULL) SmartArrayPointer as the receiver. Like
+ // the copy constructor it removes the pointer in the original to avoid
+ // double freeing.
+ inline SmartPointerBase<Deallocator, T>& operator=(
+ const SmartPointerBase<Deallocator, T>& rhs) {
+ ASSERT(is_empty());
+ T* tmp = rhs.p_; // swap to handle self-assignment
+ const_cast<SmartPointerBase<Deallocator, T>&>(rhs).p_ = NULL;
+ p_ = tmp;
+ return *this;
+ }
+
+ inline bool is_empty() { return p_ == NULL; }
+
+ private:
+ T* p_;
+};
+
+// A 'scoped array pointer' that calls DeleteArray on its pointer when the
+// destructor is called.
+
+template<typename T>
+struct ArrayDeallocator {
+ static void Delete(T* array) {
+ DeleteArray(array);
+ }
+};
+
+
+template<typename T>
+class SmartArrayPointer: public SmartPointerBase<ArrayDeallocator<T>, T> {
+ public:
+ inline SmartArrayPointer() { }
+ explicit inline SmartArrayPointer(T* ptr)
+ : SmartPointerBase<ArrayDeallocator<T>, T>(ptr) { }
+ inline SmartArrayPointer(const SmartArrayPointer<T>& rhs)
+ : SmartPointerBase<ArrayDeallocator<T>, T>(rhs) { }
+};
+
+
+template<typename T>
+struct ObjectDeallocator {
+ static void Delete(T* object) {
+ delete object;
+ }
+};
+
+
+template<typename T>
+class SmartPointer: public SmartPointerBase<ObjectDeallocator<T>, T> {
+ public:
+ inline SmartPointer() { }
+ explicit inline SmartPointer(T* ptr)
+ : SmartPointerBase<ObjectDeallocator<T>, T>(ptr) { }
+ inline SmartPointer(const SmartPointer<T>& rhs)
+ : SmartPointerBase<ObjectDeallocator<T>, T>(rhs) { }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SMART_POINTERS_H_
diff --git a/chromium/v8/src/snapshot-common.cc b/chromium/v8/src/snapshot-common.cc
new file mode 100644
index 00000000000..576269df9e4
--- /dev/null
+++ b/chromium/v8/src/snapshot-common.cc
@@ -0,0 +1,140 @@
+// 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.
+
+// The common functionality when building with or without snapshots.
+
+#include "v8.h"
+
+#include "api.h"
+#include "serialize.h"
+#include "snapshot.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+
+static void ReserveSpaceForSnapshot(Deserializer* deserializer,
+ const char* file_name) {
+ int file_name_length = StrLength(file_name) + 10;
+ Vector<char> name = Vector<char>::New(file_name_length + 1);
+ OS::SNPrintF(name, "%s.size", file_name);
+ FILE* fp = OS::FOpen(name.start(), "r");
+ CHECK_NE(NULL, fp);
+ int new_size, pointer_size, data_size, code_size, map_size, cell_size,
+ property_cell_size;
+#ifdef _MSC_VER
+ // Avoid warning about unsafe fscanf from MSVC.
+ // Please note that this is only fine if %c and %s are not being used.
+#define fscanf fscanf_s
+#endif
+ CHECK_EQ(1, fscanf(fp, "new %d\n", &new_size));
+ CHECK_EQ(1, fscanf(fp, "pointer %d\n", &pointer_size));
+ CHECK_EQ(1, fscanf(fp, "data %d\n", &data_size));
+ CHECK_EQ(1, fscanf(fp, "code %d\n", &code_size));
+ CHECK_EQ(1, fscanf(fp, "map %d\n", &map_size));
+ CHECK_EQ(1, fscanf(fp, "cell %d\n", &cell_size));
+ CHECK_EQ(1, fscanf(fp, "property cell %d\n", &property_cell_size));
+#ifdef _MSC_VER
+#undef fscanf
+#endif
+ fclose(fp);
+ deserializer->set_reservation(NEW_SPACE, new_size);
+ deserializer->set_reservation(OLD_POINTER_SPACE, pointer_size);
+ deserializer->set_reservation(OLD_DATA_SPACE, data_size);
+ deserializer->set_reservation(CODE_SPACE, code_size);
+ deserializer->set_reservation(MAP_SPACE, map_size);
+ deserializer->set_reservation(CELL_SPACE, cell_size);
+ deserializer->set_reservation(PROPERTY_CELL_SPACE,
+ property_cell_size);
+ name.Dispose();
+}
+
+
+void Snapshot::ReserveSpaceForLinkedInSnapshot(Deserializer* deserializer) {
+ deserializer->set_reservation(NEW_SPACE, new_space_used_);
+ deserializer->set_reservation(OLD_POINTER_SPACE, pointer_space_used_);
+ deserializer->set_reservation(OLD_DATA_SPACE, data_space_used_);
+ deserializer->set_reservation(CODE_SPACE, code_space_used_);
+ deserializer->set_reservation(MAP_SPACE, map_space_used_);
+ deserializer->set_reservation(CELL_SPACE, cell_space_used_);
+ deserializer->set_reservation(PROPERTY_CELL_SPACE,
+ property_cell_space_used_);
+}
+
+
+bool Snapshot::Initialize(const char* snapshot_file) {
+ if (snapshot_file) {
+ int len;
+ byte* str = ReadBytes(snapshot_file, &len);
+ if (!str) return false;
+ bool success;
+ {
+ SnapshotByteSource source(str, len);
+ Deserializer deserializer(&source);
+ ReserveSpaceForSnapshot(&deserializer, snapshot_file);
+ success = V8::Initialize(&deserializer);
+ }
+ DeleteArray(str);
+ return success;
+ } else if (size_ > 0) {
+ SnapshotByteSource source(raw_data_, raw_size_);
+ Deserializer deserializer(&source);
+ ReserveSpaceForLinkedInSnapshot(&deserializer);
+ return V8::Initialize(&deserializer);
+ }
+ return false;
+}
+
+
+bool Snapshot::HaveASnapshotToStartFrom() {
+ return size_ != 0;
+}
+
+
+Handle<Context> Snapshot::NewContextFromSnapshot() {
+ if (context_size_ == 0) {
+ return Handle<Context>();
+ }
+ SnapshotByteSource source(context_raw_data_,
+ context_raw_size_);
+ Deserializer deserializer(&source);
+ Object* root;
+ deserializer.set_reservation(NEW_SPACE, context_new_space_used_);
+ deserializer.set_reservation(OLD_POINTER_SPACE, context_pointer_space_used_);
+ deserializer.set_reservation(OLD_DATA_SPACE, context_data_space_used_);
+ deserializer.set_reservation(CODE_SPACE, context_code_space_used_);
+ deserializer.set_reservation(MAP_SPACE, context_map_space_used_);
+ deserializer.set_reservation(CELL_SPACE, context_cell_space_used_);
+ deserializer.set_reservation(PROPERTY_CELL_SPACE,
+ context_property_cell_space_used_);
+ deserializer.DeserializePartial(&root);
+ CHECK(root->IsContext());
+ return Handle<Context>(Context::cast(root));
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/snapshot-empty.cc b/chromium/v8/src/snapshot-empty.cc
new file mode 100644
index 00000000000..54236d82eca
--- /dev/null
+++ b/chromium/v8/src/snapshot-empty.cc
@@ -0,0 +1,62 @@
+// 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.
+
+// Used for building without snapshots.
+
+#include "v8.h"
+
+#include "snapshot.h"
+
+namespace v8 {
+namespace internal {
+
+const byte Snapshot::data_[] = { 0 };
+const byte* Snapshot::raw_data_ = NULL;
+const int Snapshot::size_ = 0;
+const int Snapshot::raw_size_ = 0;
+const byte Snapshot::context_data_[] = { 0 };
+const byte* Snapshot::context_raw_data_ = NULL;
+const int Snapshot::context_size_ = 0;
+const int Snapshot::context_raw_size_ = 0;
+
+const int Snapshot::new_space_used_ = 0;
+const int Snapshot::pointer_space_used_ = 0;
+const int Snapshot::data_space_used_ = 0;
+const int Snapshot::code_space_used_ = 0;
+const int Snapshot::map_space_used_ = 0;
+const int Snapshot::cell_space_used_ = 0;
+const int Snapshot::property_cell_space_used_ = 0;
+
+const int Snapshot::context_new_space_used_ = 0;
+const int Snapshot::context_pointer_space_used_ = 0;
+const int Snapshot::context_data_space_used_ = 0;
+const int Snapshot::context_code_space_used_ = 0;
+const int Snapshot::context_map_space_used_ = 0;
+const int Snapshot::context_cell_space_used_ = 0;
+const int Snapshot::context_property_cell_space_used_ = 0;
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/snapshot.h b/chromium/v8/src/snapshot.h
new file mode 100644
index 00000000000..149306e4421
--- /dev/null
+++ b/chromium/v8/src/snapshot.h
@@ -0,0 +1,100 @@
+// 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.
+
+#include "isolate.h"
+
+#ifndef V8_SNAPSHOT_H_
+#define V8_SNAPSHOT_H_
+
+namespace v8 {
+namespace internal {
+
+class Snapshot {
+ public:
+ // Initialize the VM from the given snapshot file. If snapshot_file is
+ // NULL, use the internal snapshot instead. Returns false if no snapshot
+ // could be found.
+ static bool Initialize(const char* snapshot_file = NULL);
+
+ static bool HaveASnapshotToStartFrom();
+
+ // Create a new context using the internal partial snapshot.
+ static Handle<Context> NewContextFromSnapshot();
+
+ // Returns whether or not the snapshot is enabled.
+ static bool IsEnabled() { return size_ != 0; }
+
+ // Write snapshot to the given file. Returns true if snapshot was written
+ // successfully.
+ static bool WriteToFile(const char* snapshot_file);
+
+ static const byte* data() { return data_; }
+ static int size() { return size_; }
+ static int raw_size() { return raw_size_; }
+ static void set_raw_data(const byte* raw_data) {
+ raw_data_ = raw_data;
+ }
+ static const byte* context_data() { return context_data_; }
+ static int context_size() { return context_size_; }
+ static int context_raw_size() { return context_raw_size_; }
+ static void set_context_raw_data(
+ const byte* context_raw_data) {
+ context_raw_data_ = context_raw_data;
+ }
+
+ private:
+ static const byte data_[];
+ static const byte* raw_data_;
+ static const byte context_data_[];
+ static const byte* context_raw_data_;
+ static const int new_space_used_;
+ static const int pointer_space_used_;
+ static const int data_space_used_;
+ static const int code_space_used_;
+ static const int map_space_used_;
+ static const int cell_space_used_;
+ static const int property_cell_space_used_;
+ static const int context_new_space_used_;
+ static const int context_pointer_space_used_;
+ static const int context_data_space_used_;
+ static const int context_code_space_used_;
+ static const int context_map_space_used_;
+ static const int context_cell_space_used_;
+ static const int context_property_cell_space_used_;
+ static const int size_;
+ static const int raw_size_;
+ static const int context_size_;
+ static const int context_raw_size_;
+
+ static void ReserveSpaceForLinkedInSnapshot(Deserializer* deserializer);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Snapshot);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SNAPSHOT_H_
diff --git a/chromium/v8/src/spaces-inl.h b/chromium/v8/src/spaces-inl.h
new file mode 100644
index 00000000000..77117b8a4e4
--- /dev/null
+++ b/chromium/v8/src/spaces-inl.h
@@ -0,0 +1,364 @@
+// 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_SPACES_INL_H_
+#define V8_SPACES_INL_H_
+
+#include "isolate.h"
+#include "spaces.h"
+#include "v8memory.h"
+
+namespace v8 {
+namespace internal {
+
+
+// -----------------------------------------------------------------------------
+// Bitmap
+
+void Bitmap::Clear(MemoryChunk* chunk) {
+ Bitmap* bitmap = chunk->markbits();
+ for (int i = 0; i < bitmap->CellsCount(); i++) bitmap->cells()[i] = 0;
+ chunk->ResetLiveBytes();
+}
+
+
+// -----------------------------------------------------------------------------
+// PageIterator
+
+
+PageIterator::PageIterator(PagedSpace* space)
+ : space_(space),
+ prev_page_(&space->anchor_),
+ next_page_(prev_page_->next_page()) { }
+
+
+bool PageIterator::has_next() {
+ return next_page_ != &space_->anchor_;
+}
+
+
+Page* PageIterator::next() {
+ ASSERT(has_next());
+ prev_page_ = next_page_;
+ next_page_ = next_page_->next_page();
+ return prev_page_;
+}
+
+
+// -----------------------------------------------------------------------------
+// NewSpacePageIterator
+
+
+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()) { }
+
+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);
+}
+
+
+bool NewSpacePageIterator::has_next() {
+ return prev_page_ != last_page_;
+}
+
+
+NewSpacePage* NewSpacePageIterator::next() {
+ ASSERT(has_next());
+ prev_page_ = next_page_;
+ next_page_ = next_page_->next_page();
+ return prev_page_;
+}
+
+
+// -----------------------------------------------------------------------------
+// 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;
+ }
+ }
+ return NULL;
+}
+
+
+// -----------------------------------------------------------------------------
+// MemoryAllocator
+
+#ifdef ENABLE_HEAP_PROTECTION
+
+void MemoryAllocator::Protect(Address start, size_t size) {
+ OS::Protect(start, size);
+}
+
+
+void MemoryAllocator::Unprotect(Address start,
+ size_t size,
+ Executability executable) {
+ OS::Unprotect(start, size, executable);
+}
+
+
+void MemoryAllocator::ProtectChunkFromPage(Page* page) {
+ int id = GetChunkId(page);
+ OS::Protect(chunks_[id].address(), chunks_[id].size());
+}
+
+
+void MemoryAllocator::UnprotectChunkFromPage(Page* page) {
+ int id = GetChunkId(page);
+ OS::Unprotect(chunks_[id].address(), chunks_[id].size(),
+ chunks_[id].owner()->executable() == EXECUTABLE);
+}
+
+#endif
+
+
+// --------------------------------------------------------------------------
+// PagedSpace
+Page* Page::Initialize(Heap* heap,
+ MemoryChunk* chunk,
+ Executability executable,
+ PagedSpace* owner) {
+ Page* page = reinterpret_cast<Page*>(chunk);
+ ASSERT(page->area_size() <= kNonCodeObjectAreaSize);
+ ASSERT(chunk->owner() == owner);
+ owner->IncreaseCapacity(page->area_size());
+ owner->Free(page->area_start(), page->area_size());
+
+ heap->incremental_marking()->SetOldSpacePageFlags(chunk);
+
+ return page;
+}
+
+
+bool PagedSpace::Contains(Address addr) {
+ Page* p = Page::FromAddress(addr);
+ if (!p->is_valid()) return false;
+ return p->owner() == this;
+}
+
+
+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 {
+ if (scan_on_scavenge()) heap_->decrement_scan_on_scavenge_pages();
+ ClearFlag(SCAN_ON_SCAVENGE);
+ }
+ 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;
+ }
+ }
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+void MemoryChunk::UpdateHighWaterMark(Address mark) {
+ if (mark == NULL) return;
+ // Need to subtract one from the mark because when a chunk is full the
+ // top points to the next address after the chunk, which effectively belongs
+ // to another chunk. See the comment to Page::FromAllocationTop.
+ MemoryChunk* chunk = MemoryChunk::FromAddress(mark - 1);
+ int new_mark = static_cast<int>(mark - chunk->address());
+ if (new_mark > chunk->high_water_mark_) {
+ chunk->high_water_mark_ = new_mark;
+ }
+}
+
+
+PointerChunkIterator::PointerChunkIterator(Heap* heap)
+ : state_(kOldPointerState),
+ old_pointer_iterator_(heap->old_pointer_space()),
+ map_iterator_(heap->map_space()),
+ lo_iterator_(heap->lo_space()) { }
+
+
+Page* Page::next_page() {
+ ASSERT(next_chunk()->owner() == owner());
+ return static_cast<Page*>(next_chunk());
+}
+
+
+Page* Page::prev_page() {
+ ASSERT(prev_chunk()->owner() == owner());
+ return static_cast<Page*>(prev_chunk());
+}
+
+
+void Page::set_next_page(Page* page) {
+ ASSERT(page->owner() == owner());
+ set_next_chunk(page);
+}
+
+
+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 (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(int size_in_bytes) {
+ Address current_top = allocation_info_.top;
+ Address new_top = current_top + size_in_bytes;
+ if (new_top > allocation_info_.limit) return NULL;
+
+ allocation_info_.top = new_top;
+ return HeapObject::FromAddress(current_top);
+}
+
+
+// Raw allocation.
+MaybeObject* PagedSpace::AllocateRaw(int size_in_bytes) {
+ HeapObject* object = AllocateLinearly(size_in_bytes);
+ if (object != NULL) {
+ if (identity() == CODE_SPACE) {
+ SkipList::Update(object->address(), size_in_bytes);
+ }
+ return object;
+ }
+
+ ASSERT(!heap()->linear_allocation() ||
+ (anchor_.next_chunk() == &anchor_ &&
+ anchor_.prev_chunk() == &anchor_));
+
+ 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) {
+ if (identity() == CODE_SPACE) {
+ SkipList::Update(object->address(), size_in_bytes);
+ }
+ return object;
+ }
+
+ return Failure::RetryAfterGC(identity());
+}
+
+
+// -----------------------------------------------------------------------------
+// NewSpace
+
+
+MaybeObject* NewSpace::AllocateRaw(int size_in_bytes) {
+ Address old_top = allocation_info_.top;
+#ifdef DEBUG
+ // If we are stressing compaction we waste some memory in new space
+ // in order to get more frequent GCs.
+ if (FLAG_stress_compaction && !HEAP->linear_allocation()) {
+ if (allocation_info_.limit - old_top >= size_in_bytes * 4) {
+ int filler_size = size_in_bytes * 4;
+ for (int i = 0; i < filler_size; i += kPointerSize) {
+ *(reinterpret_cast<Object**>(old_top + i)) =
+ HEAP->one_pointer_filler_map();
+ }
+ old_top += filler_size;
+ allocation_info_.top += filler_size;
+ }
+ }
+#endif
+
+ if (allocation_info_.limit - old_top < size_in_bytes) {
+ return SlowAllocateRaw(size_in_bytes);
+ }
+
+ Object* obj = HeapObject::FromAddress(old_top);
+ allocation_info_.top += size_in_bytes;
+ ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
+
+ return obj;
+}
+
+
+LargePage* LargePage::Initialize(Heap* heap, MemoryChunk* chunk) {
+ heap->incremental_marking()->SetOldSpacePageFlags(chunk);
+ return static_cast<LargePage*>(chunk);
+}
+
+
+intptr_t LargeObjectSpace::Available() {
+ return ObjectSizeFor(heap()->isolate()->memory_allocator()->Available());
+}
+
+
+bool FreeListNode::IsFreeListNode(HeapObject* object) {
+ 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
+
+#endif // V8_SPACES_INL_H_
diff --git a/chromium/v8/src/spaces.cc b/chromium/v8/src/spaces.cc
new file mode 100644
index 00000000000..5935c4a0ea9
--- /dev/null
+++ b/chromium/v8/src/spaces.cc
@@ -0,0 +1,3218 @@
+// 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 "macro-assembler.h"
+#include "mark-compact.h"
+#include "platform.h"
+
+namespace v8 {
+namespace internal {
+
+
+// ----------------------------------------------------------------------------
+// HeapObjectIterator
+
+HeapObjectIterator::HeapObjectIterator(PagedSpace* space) {
+ // 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) {
+ // 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) {
+ Space* owner = page->owner();
+ ASSERT(owner == page->heap()->old_pointer_space() ||
+ owner == page->heap()->old_data_space() ||
+ owner == page->heap()->map_space() ||
+ owner == page->heap()->cell_space() ||
+ owner == page->heap()->property_cell_space() ||
+ owner == page->heap()->code_space());
+ Initialize(reinterpret_cast<PagedSpace*>(owner),
+ page->area_start(),
+ page->area_end(),
+ 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;
+ cur_end_ = end;
+ page_mode_ = mode;
+ size_func_ = size_f;
+}
+
+
+// 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->area_end());
+ }
+ cur_page = cur_page->next_page();
+ if (cur_page == space_->anchor()) return false;
+ cur_addr_ = cur_page->area_start();
+ cur_end_ = cur_page->area_end();
+ ASSERT(cur_page->WasSweptPrecisely());
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+// CodeRange
+
+
+CodeRange::CodeRange(Isolate* isolate)
+ : isolate_(isolate),
+ code_range_(NULL),
+ free_list_(0),
+ allocation_list_(0),
+ current_allocation_block_index_(0) {
+}
+
+
+bool CodeRange::SetUp(const size_t requested) {
+ ASSERT(code_range_ == NULL);
+
+ code_range_ = new VirtualMemory(requested);
+ CHECK(code_range_ != NULL);
+ if (!code_range_->IsReserved()) {
+ delete code_range_;
+ code_range_ = NULL;
+ return false;
+ }
+
+ // 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));
+ 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;
+}
+
+
+int CodeRange::CompareFreeBlockAddress(const FreeBlock* left,
+ const FreeBlock* right) {
+ // The entire point of CodeRange is that the difference between two
+ // addresses in the range can be represented as a signed 32-bit int,
+ // so the cast is semantically correct.
+ return static_cast<int>(left->start - right->start);
+}
+
+
+void CodeRange::GetNextAllocationBlock(size_t requested) {
+ for (current_allocation_block_index_++;
+ current_allocation_block_index_ < allocation_list_.length();
+ current_allocation_block_index_++) {
+ if (requested <= allocation_list_[current_allocation_block_index_].size) {
+ return; // Found a large enough allocation block.
+ }
+ }
+
+ // Sort and merge the free blocks on the free list and the allocation list.
+ free_list_.AddAll(allocation_list_);
+ allocation_list_.Clear();
+ free_list_.Sort(&CompareFreeBlockAddress);
+ for (int i = 0; i < free_list_.length();) {
+ FreeBlock merged = free_list_[i];
+ i++;
+ // Add adjacent free blocks to the current merged block.
+ while (i < free_list_.length() &&
+ free_list_[i].start == merged.start + merged.size) {
+ merged.size += free_list_[i].size;
+ i++;
+ }
+ if (merged.size > 0) {
+ allocation_list_.Add(merged);
+ }
+ }
+ free_list_.Clear();
+
+ for (current_allocation_block_index_ = 0;
+ current_allocation_block_index_ < allocation_list_.length();
+ current_allocation_block_index_++) {
+ if (requested <= allocation_list_[current_allocation_block_index_].size) {
+ return; // Found a large enough allocation block.
+ }
+ }
+
+ // Code range is full or too fragmented.
+ V8::FatalProcessOutOfMemory("CodeRange::GetNextAllocationBlock");
+}
+
+
+Address CodeRange::AllocateRawMemory(const size_t requested_size,
+ const size_t commit_size,
+ size_t* allocated) {
+ ASSERT(commit_size <= requested_size);
+ ASSERT(current_allocation_block_index_ < allocation_list_.length());
+ if (requested_size > allocation_list_[current_allocation_block_index_].size) {
+ // Find an allocation block large enough. This function call may
+ // call V8::FatalProcessOutOfMemory if it cannot find a large enough block.
+ GetNextAllocationBlock(requested_size);
+ }
+ // Commit the requested memory at the start of the current allocation block.
+ size_t aligned_requested = RoundUp(requested_size, MemoryChunk::kAlignment);
+ FreeBlock current = allocation_list_[current_allocation_block_index_];
+ 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 (!MemoryAllocator::CommitExecutableMemory(code_range_,
+ current.start,
+ commit_size,
+ *allocated)) {
+ *allocated = 0;
+ return NULL;
+ }
+ allocation_list_[current_allocation_block_index_].start += *allocated;
+ allocation_list_[current_allocation_block_index_].size -= *allocated;
+ if (*allocated == current.size) {
+ GetNextAllocationBlock(0); // This block is used up, get the next one.
+ }
+ return current.start;
+}
+
+
+bool CodeRange::CommitRawMemory(Address start, size_t length) {
+ return code_range_->Commit(start, length, true);
+}
+
+
+bool CodeRange::UncommitRawMemory(Address start, size_t length) {
+ return code_range_->Uncommit(start, 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);
+}
+
+
+void CodeRange::TearDown() {
+ delete code_range_; // Frees all memory in the virtual memory range.
+ code_range_ = NULL;
+ free_list_.Free();
+ allocation_list_.Free();
+}
+
+
+// -----------------------------------------------------------------------------
+// MemoryAllocator
+//
+
+MemoryAllocator::MemoryAllocator(Isolate* isolate)
+ : isolate_(isolate),
+ capacity_(0),
+ capacity_executable_(0),
+ size_(0),
+ size_executable_(0) {
+}
+
+
+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_);
+
+ size_ = 0;
+ size_executable_ = 0;
+
+ return true;
+}
+
+
+void MemoryAllocator::TearDown() {
+ // 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;
+}
+
+
+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));
+
+ if (executable == EXECUTABLE) {
+ ASSERT(size_executable_ >= size);
+ size_executable_ -= size;
+ }
+ // 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::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 {
+ ASSERT(executable == NOT_EXECUTABLE || !isolate_->code_range()->exists());
+ bool result = VirtualMemory::ReleaseRegion(base, size);
+ USE(result);
+ ASSERT(result);
+ }
+}
+
+
+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;
+}
+
+
+Address MemoryAllocator::AllocateAlignedMemory(size_t reserve_size,
+ size_t commit_size,
+ size_t alignment,
+ Executability executable,
+ VirtualMemory* controller) {
+ ASSERT(commit_size <= reserve_size);
+ VirtualMemory reservation;
+ Address base = ReserveAlignedMemory(reserve_size, alignment, &reservation);
+ if (base == NULL) return NULL;
+
+ if (executable == EXECUTABLE) {
+ if (!CommitExecutableMemory(&reservation,
+ base,
+ commit_size,
+ reserve_size)) {
+ base = NULL;
+ }
+ } else {
+ if (!reservation.Commit(base, commit_size, false)) {
+ base = NULL;
+ }
+ }
+
+ if (base == NULL) {
+ // Failed to commit the body. Release the mapping and any partially
+ // commited regions inside it.
+ reservation.Release();
+ return NULL;
+ }
+
+ controller->TakeControl(&reservation);
+ return base;
+}
+
+
+void Page::InitializeAsAnchor(PagedSpace* owner) {
+ set_owner(owner);
+ set_prev_page(this);
+ set_next_page(this);
+}
+
+
+NewSpacePage* NewSpacePage::Initialize(Heap* heap,
+ Address start,
+ SemiSpace* semi_space) {
+ Address area_start = start + NewSpacePage::kObjectStartOffset;
+ Address area_end = start + Page::kPageSize;
+
+ MemoryChunk* chunk = MemoryChunk::Initialize(heap,
+ start,
+ Page::kPageSize,
+ area_start,
+ area_end,
+ 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 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);
+}
+
+
+MemoryChunk* MemoryChunk::Initialize(Heap* heap,
+ Address base,
+ size_t size,
+ Address area_start,
+ Address area_end,
+ Executability executable,
+ Space* owner) {
+ MemoryChunk* chunk = FromAddress(base);
+
+ ASSERT(base == chunk->address());
+
+ chunk->heap_ = heap;
+ chunk->size_ = size;
+ chunk->area_start_ = area_start;
+ chunk->area_end_ = area_end;
+ chunk->flags_ = 0;
+ chunk->set_owner(owner);
+ chunk->InitializeReservedMemory();
+ chunk->slots_buffer_ = NULL;
+ chunk->skip_list_ = NULL;
+ chunk->write_barrier_counter_ = kWriteBarrierCounterGranularity;
+ chunk->progress_bar_ = 0;
+ chunk->high_water_mark_ = static_cast<int>(area_start - base);
+ chunk->parallel_sweeping_ = 0;
+ chunk->available_in_small_free_list_ = 0;
+ chunk->available_in_medium_free_list_ = 0;
+ chunk->available_in_large_free_list_ = 0;
+ chunk->available_in_huge_free_list_ = 0;
+ chunk->non_available_small_blocks_ = 0;
+ 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);
+
+ if (executable == EXECUTABLE) {
+ chunk->SetFlag(IS_EXECUTABLE);
+ }
+
+ if (owner == heap->old_data_space()) {
+ chunk->SetFlag(CONTAINS_ONLY_DATA);
+ }
+
+ return chunk;
+}
+
+
+// Commit MemoryChunk area to the requested size.
+bool MemoryChunk::CommitArea(size_t requested) {
+ size_t guard_size = IsFlagSet(IS_EXECUTABLE) ?
+ MemoryAllocator::CodePageGuardSize() : 0;
+ size_t header_size = area_start() - address() - guard_size;
+ size_t commit_size = RoundUp(header_size + requested, OS::CommitPageSize());
+ size_t committed_size = RoundUp(header_size + (area_end() - area_start()),
+ OS::CommitPageSize());
+
+ if (commit_size > committed_size) {
+ // Commit size should be less or equal than the reserved size.
+ ASSERT(commit_size <= size() - 2 * guard_size);
+ // Append the committed area.
+ Address start = address() + committed_size + guard_size;
+ size_t length = commit_size - committed_size;
+ if (reservation_.IsReserved()) {
+ if (!reservation_.Commit(start, length, IsFlagSet(IS_EXECUTABLE))) {
+ return false;
+ }
+ } else {
+ CodeRange* code_range = heap_->isolate()->code_range();
+ ASSERT(code_range->exists() && IsFlagSet(IS_EXECUTABLE));
+ if (!code_range->CommitRawMemory(start, length)) return false;
+ }
+
+ if (Heap::ShouldZapGarbage()) {
+ heap_->isolate()->memory_allocator()->ZapBlock(start, length);
+ }
+ } else if (commit_size < committed_size) {
+ ASSERT(commit_size > 0);
+ // Shrink the committed area.
+ size_t length = committed_size - commit_size;
+ Address start = address() + committed_size + guard_size - length;
+ if (reservation_.IsReserved()) {
+ if (!reservation_.Uncommit(start, length)) return false;
+ } else {
+ CodeRange* code_range = heap_->isolate()->code_range();
+ ASSERT(code_range->exists() && IsFlagSet(IS_EXECUTABLE));
+ if (!code_range->UncommitRawMemory(start, length)) return false;
+ }
+ }
+
+ area_end_ = area_start_ + requested;
+ return true;
+}
+
+
+void MemoryChunk::InsertAfter(MemoryChunk* other) {
+ next_chunk_ = other->next_chunk_;
+ prev_chunk_ = other;
+
+ // This memory barrier is needed since concurrent sweeper threads may iterate
+ // over the list of pages while a new page is inserted.
+ // TODO(hpayer): find a cleaner way to guarantee that the page list can be
+ // expanded concurrently
+ MemoryBarrier();
+
+ // The following two write operations can take effect in arbitrary order
+ // since pages are always iterated by the sweeper threads in LIFO order, i.e,
+ // the inserted page becomes visible for the sweeper threads after
+ // other->next_chunk_ = this;
+ other->next_chunk_->prev_chunk_ = this;
+ other->next_chunk_ = this;
+}
+
+
+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;
+}
+
+
+MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t reserve_area_size,
+ intptr_t commit_area_size,
+ Executability executable,
+ Space* owner) {
+ ASSERT(commit_area_size <= reserve_area_size);
+
+ size_t chunk_size;
+ Heap* heap = isolate_->heap();
+ Address base = NULL;
+ VirtualMemory reservation;
+ Address area_start = NULL;
+ Address area_end = NULL;
+
+ //
+ // MemoryChunk layout:
+ //
+ // Executable
+ // +----------------------------+<- base aligned with MemoryChunk::kAlignment
+ // | Header |
+ // +----------------------------+<- base + CodePageGuardStartOffset
+ // | Guard |
+ // +----------------------------+<- area_start_
+ // | Area |
+ // +----------------------------+<- area_end_ (area_start + commit_area_size)
+ // | Committed but not used |
+ // +----------------------------+<- aligned at OS page boundary
+ // | Reserved but not committed |
+ // +----------------------------+<- aligned at OS page boundary
+ // | Guard |
+ // +----------------------------+<- base + chunk_size
+ //
+ // Non-executable
+ // +----------------------------+<- base aligned with MemoryChunk::kAlignment
+ // | Header |
+ // +----------------------------+<- area_start_ (base + kObjectStartOffset)
+ // | Area |
+ // +----------------------------+<- area_end_ (area_start + commit_area_size)
+ // | Committed but not used |
+ // +----------------------------+<- aligned at OS page boundary
+ // | Reserved but not committed |
+ // +----------------------------+<- base + chunk_size
+ //
+
+ if (executable == EXECUTABLE) {
+ chunk_size = RoundUp(CodePageAreaStartOffset() + reserve_area_size,
+ OS::CommitPageSize()) + CodePageGuardSize();
+
+ // Check executable memory limit.
+ if (size_executable_ + chunk_size > capacity_executable_) {
+ LOG(isolate_,
+ StringEvent("MemoryAllocator::AllocateRawMemory",
+ "V8 Executable Allocation capacity exceeded"));
+ return NULL;
+ }
+
+ // Size of header (not executable) plus area (executable).
+ size_t commit_size = RoundUp(CodePageGuardStartOffset() + commit_area_size,
+ OS::CommitPageSize());
+ // Allocate executable memory either from code range or from the
+ // OS.
+ if (isolate_->code_range()->exists()) {
+ base = isolate_->code_range()->AllocateRawMemory(chunk_size,
+ commit_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,
+ commit_size,
+ MemoryChunk::kAlignment,
+ executable,
+ &reservation);
+ if (base == NULL) return NULL;
+ // Update executable memory size.
+ size_executable_ += reservation.size();
+ }
+
+ if (Heap::ShouldZapGarbage()) {
+ ZapBlock(base, CodePageGuardStartOffset());
+ ZapBlock(base + CodePageAreaStartOffset(), commit_area_size);
+ }
+
+ area_start = base + CodePageAreaStartOffset();
+ area_end = area_start + commit_area_size;
+ } else {
+ chunk_size = RoundUp(MemoryChunk::kObjectStartOffset + reserve_area_size,
+ OS::CommitPageSize());
+ size_t commit_size = RoundUp(MemoryChunk::kObjectStartOffset +
+ commit_area_size, OS::CommitPageSize());
+ base = AllocateAlignedMemory(chunk_size,
+ commit_size,
+ MemoryChunk::kAlignment,
+ executable,
+ &reservation);
+
+ if (base == NULL) return NULL;
+
+ if (Heap::ShouldZapGarbage()) {
+ ZapBlock(base, Page::kObjectStartOffset + commit_area_size);
+ }
+
+ area_start = base + Page::kObjectStartOffset;
+ area_end = area_start + commit_area_size;
+ }
+
+ // Use chunk_size for statistics and callbacks because we assume that they
+ // treat reserved but not-yet committed memory regions of chunks as allocated.
+ 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);
+ }
+
+ MemoryChunk* result = MemoryChunk::Initialize(heap,
+ base,
+ chunk_size,
+ area_start,
+ area_end,
+ executable,
+ owner);
+ result->set_reserved_memory(&reservation);
+ return result;
+}
+
+
+void Page::ResetFreeListStatistics() {
+ non_available_small_blocks_ = 0;
+ available_in_small_free_list_ = 0;
+ available_in_medium_free_list_ = 0;
+ available_in_large_free_list_ = 0;
+ available_in_huge_free_list_ = 0;
+}
+
+
+Page* MemoryAllocator::AllocatePage(intptr_t size,
+ PagedSpace* owner,
+ Executability executable) {
+ MemoryChunk* chunk = AllocateChunk(size, size, executable, owner);
+
+ if (chunk == NULL) return NULL;
+
+ return Page::Initialize(isolate_->heap(), chunk, executable, owner);
+}
+
+
+LargePage* MemoryAllocator::AllocateLargePage(intptr_t object_size,
+ Space* owner,
+ Executability executable) {
+ MemoryChunk* chunk = AllocateChunk(object_size,
+ 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());
+ }
+
+ isolate_->heap()->RememberUnmappedPage(
+ reinterpret_cast<Address>(chunk), chunk->IsEvacuationCandidate());
+
+ 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) {
+ if (!VirtualMemory::CommitRegion(start, size, executable)) return false;
+
+ if (Heap::ShouldZapGarbage()) {
+ ZapBlock(start, size);
+ }
+
+ isolate_->counters()->memory_allocated()->Increment(static_cast<int>(size));
+ return true;
+}
+
+
+bool MemoryAllocator::UncommitBlock(Address start, size_t size) {
+ if (!VirtualMemory::UncommitRegion(start, size)) return false;
+ isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size));
+ return true;
+}
+
+
+void MemoryAllocator::ZapBlock(Address start, size_t size) {
+ for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) {
+ Memory::Address_at(start + s) = kZapValue;
+ }
+}
+
+
+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));
+ }
+}
+
+
+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 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::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();
+}
+
+
+#ifdef DEBUG
+void MemoryAllocator::ReportStatistics() {
+ float pct = static_cast<float>(capacity_ - size_) / capacity_;
+ PrintF(" capacity: %" V8_PTR_PREFIX "d"
+ ", used: %" V8_PTR_PREFIX "d"
+ ", available: %%%d\n\n",
+ capacity_, size_, static_cast<int>(pct*100));
+}
+#endif
+
+
+int MemoryAllocator::CodePageGuardStartOffset() {
+ // We are guarding code pages: the first OS page after the header
+ // will be protected as non-writable.
+ return RoundUp(Page::kObjectStartOffset, OS::CommitPageSize());
+}
+
+
+int MemoryAllocator::CodePageGuardSize() {
+ return static_cast<int>(OS::CommitPageSize());
+}
+
+
+int MemoryAllocator::CodePageAreaStartOffset() {
+ // We are guarding code pages: the first OS page after the header
+ // will be protected as non-writable.
+ return CodePageGuardStartOffset() + CodePageGuardSize();
+}
+
+
+int MemoryAllocator::CodePageAreaEndOffset() {
+ // We are guarding code pages: the last OS page will be protected as
+ // non-writable.
+ return Page::kPageSize - static_cast<int>(OS::CommitPageSize());
+}
+
+
+bool MemoryAllocator::CommitExecutableMemory(VirtualMemory* vm,
+ Address start,
+ size_t commit_size,
+ size_t reserved_size) {
+ // Commit page header (not executable).
+ if (!vm->Commit(start,
+ CodePageGuardStartOffset(),
+ false)) {
+ return false;
+ }
+
+ // Create guard page after the header.
+ if (!vm->Guard(start + CodePageGuardStartOffset())) {
+ return false;
+ }
+
+ // Commit page body (executable).
+ if (!vm->Commit(start + CodePageAreaStartOffset(),
+ commit_size - CodePageGuardStartOffset(),
+ true)) {
+ return false;
+ }
+
+ // Create guard page before the end.
+ if (!vm->Guard(start + reserved_size - CodePageGuardSize())) {
+ return false;
+ }
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+// MemoryChunk implementation
+
+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);
+ }
+ chunk->IncrementLiveBytes(by);
+}
+
+
+// -----------------------------------------------------------------------------
+// PagedSpace implementation
+
+PagedSpace::PagedSpace(Heap* heap,
+ intptr_t max_capacity,
+ AllocationSpace id,
+ Executability executable)
+ : Space(heap, id, executable),
+ free_list_(this),
+ was_swept_conservatively_(false),
+ first_unswept_page_(Page::FromAddress(NULL)),
+ unswept_free_bytes_(0) {
+ if (id == CODE_SPACE) {
+ area_size_ = heap->isolate()->memory_allocator()->
+ CodePageAreaSize();
+ } else {
+ area_size_ = Page::kPageSize - Page::kObjectStartOffset;
+ }
+ max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize)
+ * AreaSize();
+ accounting_stats_.Clear();
+
+ allocation_info_.top = NULL;
+ allocation_info_.limit = NULL;
+
+ anchor_.InitializeAsAnchor(this);
+}
+
+
+bool PagedSpace::SetUp() {
+ return true;
+}
+
+
+bool PagedSpace::HasBeenSetUp() {
+ return true;
+}
+
+
+void PagedSpace::TearDown() {
+ 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();
+}
+
+
+size_t PagedSpace::CommittedPhysicalMemory() {
+ if (!VirtualMemory::HasLazyCommits()) return CommittedMemory();
+ MemoryChunk::UpdateHighWaterMark(allocation_info_.top);
+ size_t size = 0;
+ PageIterator it(this);
+ while (it.has_next()) {
+ size += it.next()->CommittedPhysicalMemory();
+ }
+ return size;
+}
+
+
+MaybeObject* PagedSpace::FindObject(Address addr) {
+ // 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);
+ 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;
+ }
+
+ UNREACHABLE();
+ return Failure::Exception();
+}
+
+
+bool PagedSpace::CanExpand() {
+ ASSERT(max_capacity_ % AreaSize() == 0);
+
+ if (Capacity() == max_capacity_) return false;
+
+ ASSERT(Capacity() < max_capacity_);
+
+ // Are we going to exceed capacity for this space?
+ if ((Capacity() + Page::kPageSize) > max_capacity_) return false;
+
+ return true;
+}
+
+
+bool PagedSpace::Expand() {
+ if (!CanExpand()) return false;
+
+ intptr_t size = AreaSize();
+
+ if (anchor_.next_page() == &anchor_) {
+ size = SizeOfFirstPage();
+ }
+
+ Page* p = heap()->isolate()->memory_allocator()->AllocatePage(
+ size, this, executable());
+ if (p == NULL) return false;
+
+ ASSERT(Capacity() <= max_capacity_);
+
+ p->InsertAfter(anchor_.prev_page());
+
+ return true;
+}
+
+
+intptr_t PagedSpace::SizeOfFirstPage() {
+ int size = 0;
+ switch (identity()) {
+ case OLD_POINTER_SPACE:
+ size = 64 * kPointerSize * KB;
+ break;
+ case OLD_DATA_SPACE:
+ size = 192 * KB;
+ break;
+ case MAP_SPACE:
+ size = 16 * kPointerSize * KB;
+ break;
+ case CELL_SPACE:
+ size = 16 * kPointerSize * KB;
+ break;
+ case PROPERTY_CELL_SPACE:
+ size = 8 * kPointerSize * KB;
+ break;
+ case CODE_SPACE:
+ if (heap()->isolate()->code_range()->exists()) {
+ // When code range exists, code pages are allocated in a special way
+ // (from the reserved code range). That part of the code is not yet
+ // upgraded to handle small pages.
+ size = AreaSize();
+ } else {
+ size = 384 * KB;
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return Min(size, AreaSize());
+}
+
+
+int PagedSpace::CountTotalPages() {
+ PageIterator it(this);
+ int count = 0;
+ while (it.has_next()) {
+ it.next();
+ count++;
+ }
+ return count;
+}
+
+
+void PagedSpace::ObtainFreeListStatistics(Page* page, SizeStats* sizes) {
+ sizes->huge_size_ = page->available_in_huge_free_list();
+ sizes->small_size_ = page->available_in_small_free_list();
+ sizes->medium_size_ = page->available_in_medium_free_list();
+ sizes->large_size_ = page->available_in_large_free_list();
+}
+
+
+void PagedSpace::ResetFreeListStatistics() {
+ PageIterator page_iterator(this);
+ while (page_iterator.has_next()) {
+ Page* page = page_iterator.next();
+ page->ResetFreeListStatistics();
+ }
+}
+
+
+void PagedSpace::ReleasePage(Page* page, bool unlink) {
+ ASSERT(page->LiveBytes() == 0);
+ ASSERT(AreaSize() == page->area_size());
+
+ // 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);
+ }
+ }
+
+ if (page->WasSwept()) {
+ intptr_t size = free_list_.EvictFreeListItems(page);
+ accounting_stats_.AllocateBytes(size);
+ ASSERT_EQ(AreaSize(), static_cast<int>(size));
+ } else {
+ DecreaseUnsweptFreeBytes(page);
+ }
+
+ if (Page::FromAllocationTop(allocation_info_.top) == page) {
+ allocation_info_.top = allocation_info_.limit = NULL;
+ }
+
+ if (unlink) {
+ page->Unlink();
+ }
+ if (page->IsFlagSet(MemoryChunk::CONTAINS_ONLY_DATA)) {
+ heap()->isolate()->memory_allocator()->Free(page);
+ } else {
+ heap()->QueueMemoryChunkForFree(page);
+ }
+
+ ASSERT(Capacity() > 0);
+ accounting_stats_.ShrinkSpace(AreaSize());
+}
+
+
+#ifdef DEBUG
+void PagedSpace::Print() { }
+#endif
+
+#ifdef VERIFY_HEAP
+void PagedSpace::Verify(ObjectVisitor* visitor) {
+ // 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();
+ CHECK(page->owner() == this);
+ if (page == Page::FromAllocationTop(allocation_info_.top)) {
+ allocation_pointer_found_in_space = true;
+ }
+ CHECK(page->WasSweptPrecisely());
+ HeapObjectIterator it(page, NULL);
+ Address end_of_previous_object = page->area_start();
+ Address top = page->area_end();
+ int black_size = 0;
+ for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) {
+ CHECK(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();
+ CHECK(map->IsMap());
+ CHECK(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;
+ }
+
+ CHECK(object->address() + size <= top);
+ end_of_previous_object = object->address() + size;
+ }
+ CHECK_LE(black_size, page->LiveBytes());
+ }
+ CHECK(allocation_pointer_found_in_space);
+}
+#endif // VERIFY_HEAP
+
+// -----------------------------------------------------------------------------
+// NewSpace implementation
+
+
+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();
+
+ 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 set up the histogram arrays if necessary.
+ allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
+ promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
+
+#define SET_NAME(name) allocated_histogram_[name].set_name(#name); \
+ promoted_histogram_[name].set_name(#name);
+ INSTANCE_TYPE_LIST(SET_NAME)
+#undef SET_NAME
+
+ 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;
+ }
+ ASSERT(!from_space_.is_committed()); // No need to use memory yet.
+
+ start_ = chunk_base_;
+ address_mask_ = ~(2 * reserved_semispace_capacity - 1);
+ object_mask_ = address_mask_ | kHeapObjectTagMask;
+ object_expected_ = reinterpret_cast<uintptr_t>(start_) | kHeapObjectTag;
+
+ ResetAllocationInfo();
+
+ return true;
+}
+
+
+void NewSpace::TearDown() {
+ if (allocated_histogram_) {
+ DeleteArray(allocated_histogram_);
+ allocated_histogram_ = NULL;
+ }
+ if (promoted_histogram_) {
+ DeleteArray(promoted_histogram_);
+ promoted_histogram_ = NULL;
+ }
+
+ start_ = NULL;
+ allocation_info_.top = NULL;
+ allocation_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::Swap(&from_space_, &to_space_);
+}
+
+
+void NewSpace::Grow() {
+ // Double the semispace size but only up to maximum capacity.
+ ASSERT(Capacity() < MaximumCapacity());
+ 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.
+ V8::FatalProcessOutOfMemory("Failed to grow new space.");
+ }
+ }
+ }
+ 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, Page::kPageSize);
+ if (rounded_new_capacity < Capacity() &&
+ to_space_.ShrinkTo(rounded_new_capacity)) {
+ // 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 (!to_space_.GrowTo(from_space_.Capacity())) {
+ // We are in an inconsistent state because we could not
+ // commit/uncommit memory from new space.
+ V8::FatalProcessOutOfMemory("Failed to shrink new space.");
+ }
+ }
+ }
+ allocation_info_.limit = to_space_.page_high();
+ ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
+}
+
+
+void NewSpace::UpdateAllocationInfo() {
+ MemoryChunk::UpdateHighWaterMark(allocation_info_.top);
+ 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::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());
+ }
+}
+
+
+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)->area_end();
+ 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, IncrementalMarking::GC_VIA_STACK_GUARD);
+ 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, IncrementalMarking::GC_VIA_STACK_GUARD);
+ top_on_previous_step_ = to_space_.page_low();
+ return AllocateRaw(size_in_bytes);
+ } else {
+ return Failure::RetryAfterGC();
+ }
+}
+
+
+#ifdef VERIFY_HEAP
+// 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.
+ ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
+
+ // There should be objects packed in from the low address up to the
+ // allocation pointer.
+ Address current = to_space_.first_page()->area_start();
+ CHECK_EQ(current, to_space_.space_start());
+
+ 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());
+
+ 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();
+ CHECK(map->IsMap());
+ CHECK(heap()->map_space()->Contains(map));
+
+ // The object should not be code or a map.
+ CHECK(!object->IsMap());
+ CHECK(!object->IsCode());
+
+ // 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);
+
+ 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->area_start();
+ }
+ }
+
+ // Check semi-spaces.
+ CHECK_EQ(from_space_.id(), kFromSpace);
+ CHECK_EQ(to_space_.id(), kToSpace);
+ from_space_.Verify();
+ to_space_.Verify();
+}
+#endif
+
+// -----------------------------------------------------------------------------
+// SemiSpace implementation
+
+void SemiSpace::SetUp(Address start,
+ int initial_capacity,
+ int maximum_capacity) {
+ // Creates a space in the young generation. The constructor does not
+ // allocate memory from the OS. A SemiSpace is given a contiguous chunk of
+ // memory of size 'capacity' when set up, and does not grow or shrink
+ // otherwise. In the mark-compact collector, the memory region of the from
+ // space is used as the marking stack. It requires contiguous memory
+ // addresses.
+ ASSERT(maximum_capacity >= Page::kPageSize);
+ initial_capacity_ = RoundDown(initial_capacity, Page::kPageSize);
+ capacity_ = initial_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_;
+}
+
+
+void SemiSpace::TearDown() {
+ start_ = NULL;
+ capacity_ = 0;
+}
+
+
+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;
+ }
+
+ 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;
+}
+
+
+size_t SemiSpace::CommittedPhysicalMemory() {
+ if (!is_committed()) return 0;
+ size_t size = 0;
+ NewSpacePageIterator it(this);
+ while (it.has_next()) {
+ size += it.next()->CommittedPhysicalMemory();
+ }
+ return size;
+}
+
+
+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(
+ 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_);
+ 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() { }
+#endif
+
+#ifdef VERIFY_HEAP
+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();
+ }
+}
+#endif
+
+#ifdef DEBUG
+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->bottom(), space->top(), NULL);
+}
+
+
+SemiSpaceIterator::SemiSpaceIterator(NewSpace* space,
+ HeapObjectCallback size_func) {
+ Initialize(space->bottom(), space->top(), size_func);
+}
+
+
+SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, Address start) {
+ Initialize(start, space->top(), NULL);
+}
+
+
+SemiSpaceIterator::SemiSpaceIterator(Address from, Address to) {
+ Initialize(from, to, NULL);
+}
+
+
+void SemiSpaceIterator::Initialize(Address start,
+ Address end,
+ HeapObjectCallback size_func) {
+ SemiSpace::AssertValidRange(start, end);
+ current_ = start;
+ limit_ = end;
+ size_func_ = size_func;
+}
+
+
+#ifdef DEBUG
+// heap_histograms is shared, always clear it before using it.
+static void ClearHistograms() {
+ Isolate* isolate = Isolate::Current();
+ // We reset the name each time, though it hasn't changed.
+#define DEF_TYPE_NAME(name) isolate->heap_histograms()[name].set_name(#name);
+ INSTANCE_TYPE_LIST(DEF_TYPE_NAME)
+#undef DEF_TYPE_NAME
+
+#define CLEAR_HISTOGRAM(name) isolate->heap_histograms()[name].clear();
+ INSTANCE_TYPE_LIST(CLEAR_HISTOGRAM)
+#undef CLEAR_HISTOGRAM
+
+ isolate->js_spill_information()->Clear();
+}
+
+
+static void ClearCodeKindStatistics(int* code_kind_statistics) {
+ for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
+ code_kind_statistics[i] = 0;
+ }
+}
+
+
+static void ReportCodeKindStatistics(int* code_kind_statistics) {
+ PrintF("\n Code kind histograms: \n");
+ for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
+ if (code_kind_statistics[i] > 0) {
+ PrintF(" %-20s: %10d bytes\n",
+ Code::Kind2String(static_cast<Code::Kind>(i)),
+ code_kind_statistics[i]);
+ }
+ }
+ PrintF("\n");
+}
+
+
+static int CollectHistogramInfo(HeapObject* obj) {
+ Isolate* isolate = obj->GetIsolate();
+ InstanceType type = obj->map()->instance_type();
+ ASSERT(0 <= type && type <= LAST_TYPE);
+ ASSERT(isolate->heap_histograms()[type].name() != NULL);
+ isolate->heap_histograms()[type].increment_number(1);
+ isolate->heap_histograms()[type].increment_bytes(obj->Size());
+
+ if (FLAG_collect_heap_spill_statistics && obj->IsJSObject()) {
+ JSObject::cast(obj)->IncrementSpillStatistics(
+ isolate->js_spill_information());
+ }
+
+ return obj->Size();
+}
+
+
+static void ReportHistogram(bool print_spill) {
+ Isolate* isolate = Isolate::Current();
+ PrintF("\n Object Histogram:\n");
+ for (int i = 0; i <= LAST_TYPE; i++) {
+ if (isolate->heap_histograms()[i].number() > 0) {
+ PrintF(" %-34s%10d (%10d bytes)\n",
+ isolate->heap_histograms()[i].name(),
+ isolate->heap_histograms()[i].number(),
+ isolate->heap_histograms()[i].bytes());
+ }
+ }
+ PrintF("\n");
+
+ // Summarize string types.
+ int string_number = 0;
+ int string_bytes = 0;
+#define INCREMENT(type, size, name, camel_name) \
+ string_number += isolate->heap_histograms()[type].number(); \
+ string_bytes += isolate->heap_histograms()[type].bytes();
+ STRING_TYPE_LIST(INCREMENT)
+#undef INCREMENT
+ if (string_number > 0) {
+ PrintF(" %-34s%10d (%10d bytes)\n\n", "STRING_TYPE", string_number,
+ string_bytes);
+ }
+
+ if (FLAG_collect_heap_spill_statistics && print_spill) {
+ isolate->js_spill_information()->Print();
+ }
+}
+#endif // DEBUG
+
+
+// Support for statistics gathering for --heap-stats and --log-gc.
+void NewSpace::ClearHistograms() {
+ for (int i = 0; i <= LAST_TYPE; i++) {
+ allocated_histogram_[i].clear();
+ promoted_histogram_[i].clear();
+ }
+}
+
+
+// Because the copying collector does not touch garbage objects, we iterate
+// the new space before a collection to get a histogram of allocated objects.
+// This only happens when --log-gc flag is set.
+void NewSpace::CollectStatistics() {
+ ClearHistograms();
+ SemiSpaceIterator it(this);
+ for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next())
+ RecordAllocation(obj);
+}
+
+
+static void DoReportStatistics(Isolate* isolate,
+ HistogramInfo* info, const char* description) {
+ LOG(isolate, HeapSampleBeginEvent("NewSpace", description));
+ // Lump all the string types together.
+ int string_number = 0;
+ int string_bytes = 0;
+#define INCREMENT(type, size, name, camel_name) \
+ string_number += info[type].number(); \
+ string_bytes += info[type].bytes();
+ STRING_TYPE_LIST(INCREMENT)
+#undef INCREMENT
+ if (string_number > 0) {
+ LOG(isolate,
+ HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes));
+ }
+
+ // Then do the other types.
+ for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) {
+ if (info[i].number() > 0) {
+ LOG(isolate,
+ HeapSampleItemEvent(info[i].name(), info[i].number(),
+ info[i].bytes()));
+ }
+ }
+ LOG(isolate, HeapSampleEndEvent("NewSpace", description));
+}
+
+
+void NewSpace::ReportStatistics() {
+#ifdef DEBUG
+ if (FLAG_heap_stats) {
+ float pct = static_cast<float>(Available()) / Capacity();
+ PrintF(" capacity: %" V8_PTR_PREFIX "d"
+ ", available: %" V8_PTR_PREFIX "d, %%%d\n",
+ Capacity(), Available(), static_cast<int>(pct*100));
+ PrintF("\n Object Histogram:\n");
+ for (int i = 0; i <= LAST_TYPE; i++) {
+ if (allocated_histogram_[i].number() > 0) {
+ PrintF(" %-34s%10d (%10d bytes)\n",
+ allocated_histogram_[i].name(),
+ allocated_histogram_[i].number(),
+ allocated_histogram_[i].bytes());
+ }
+ }
+ PrintF("\n");
+ }
+#endif // DEBUG
+
+ if (FLAG_log_gc) {
+ Isolate* isolate = ISOLATE;
+ DoReportStatistics(isolate, allocated_histogram_, "allocated");
+ DoReportStatistics(isolate, promoted_histogram_, "promoted");
+ }
+}
+
+
+void NewSpace::RecordAllocation(HeapObject* obj) {
+ InstanceType type = obj->map()->instance_type();
+ ASSERT(0 <= type && type <= LAST_TYPE);
+ allocated_histogram_[type].increment_number(1);
+ allocated_histogram_[type].increment_bytes(obj->Size());
+}
+
+
+void NewSpace::RecordPromotion(HeapObject* obj) {
+ InstanceType type = obj->map()->instance_type();
+ ASSERT(0 <= type && type <= LAST_TYPE);
+ promoted_histogram_[type].increment_number(1);
+ promoted_histogram_[type].increment_bytes(obj->Size());
+}
+
+
+size_t NewSpace::CommittedPhysicalMemory() {
+ if (!VirtualMemory::HasLazyCommits()) return CommittedMemory();
+ MemoryChunk::UpdateHighWaterMark(allocation_info_.top);
+ size_t size = to_space_.CommittedPhysicalMemory();
+ if (from_space_.is_committed()) {
+ size += from_space_.CommittedPhysicalMemory();
+ }
+ return size;
+}
+
+
+// -----------------------------------------------------------------------------
+// Free lists for old object spaces implementation
+
+void FreeListNode::set_size(Heap* heap, int size_in_bytes) {
+ ASSERT(size_in_bytes > 0);
+ 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 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 > 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_no_write_barrier(heap->raw_unchecked_one_pointer_filler_map());
+ } else if (size_in_bytes == 2 * kPointerSize) {
+ 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 free space map is not done yet.
+}
+
+
+FreeListNode* FreeListNode::next() {
+ ASSERT(IsFreeListNode(this));
+ if (map() == GetHeap()->raw_unchecked_free_space_map()) {
+ ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize);
+ return reinterpret_cast<FreeListNode*>(
+ Memory::Address_at(address() + kNextOffset));
+ } else {
+ return reinterpret_cast<FreeListNode*>(
+ Memory::Address_at(address() + kPointerSize));
+ }
+}
+
+
+FreeListNode** FreeListNode::next_address() {
+ ASSERT(IsFreeListNode(this));
+ if (map() == GetHeap()->raw_unchecked_free_space_map()) {
+ ASSERT(Size() >= kNextOffset + kPointerSize);
+ return reinterpret_cast<FreeListNode**>(address() + kNextOffset);
+ } else {
+ return reinterpret_cast<FreeListNode**>(address() + kPointerSize);
+ }
+}
+
+
+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() == GetHeap()->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);
+ }
+}
+
+
+intptr_t FreeListCategory::Concatenate(FreeListCategory* category) {
+ intptr_t free_bytes = 0;
+ if (category->top_ != NULL) {
+ ASSERT(category->end_ != NULL);
+ // This is safe (not going to deadlock) since Concatenate operations
+ // are never performed on the same free lists at the same time in
+ // reverse order.
+ ScopedLock lock_target(mutex_);
+ ScopedLock lock_source(category->mutex());
+ free_bytes = category->available();
+ if (end_ == NULL) {
+ end_ = category->end();
+ } else {
+ category->end()->set_next(top_);
+ }
+ top_ = category->top();
+ available_ += category->available();
+ category->Reset();
+ }
+ return free_bytes;
+}
+
+
+void FreeListCategory::Reset() {
+ top_ = NULL;
+ end_ = NULL;
+ available_ = 0;
+}
+
+
+intptr_t FreeListCategory::EvictFreeListItemsInList(Page* p) {
+ int sum = 0;
+ FreeListNode** n = &top_;
+ 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();
+ }
+ }
+ if (top_ == NULL) {
+ end_ = NULL;
+ }
+ available_ -= sum;
+ return sum;
+}
+
+
+FreeListNode* FreeListCategory::PickNodeFromList(int *node_size) {
+ FreeListNode* node = top_;
+
+ if (node == NULL) return NULL;
+
+ while (node != NULL &&
+ Page::FromAddress(node->address())->IsEvacuationCandidate()) {
+ available_ -= reinterpret_cast<FreeSpace*>(node)->Size();
+ node = node->next();
+ }
+
+ if (node != NULL) {
+ set_top(node->next());
+ *node_size = reinterpret_cast<FreeSpace*>(node)->Size();
+ available_ -= *node_size;
+ } else {
+ set_top(NULL);
+ }
+
+ if (top() == NULL) {
+ set_end(NULL);
+ }
+
+ return node;
+}
+
+
+FreeListNode* FreeListCategory::PickNodeFromList(int size_in_bytes,
+ int *node_size) {
+ FreeListNode* node = PickNodeFromList(node_size);
+ if (node != NULL && *node_size < size_in_bytes) {
+ Free(node, *node_size);
+ *node_size = 0;
+ return NULL;
+ }
+ return node;
+}
+
+
+void FreeListCategory::Free(FreeListNode* node, int size_in_bytes) {
+ node->set_next(top_);
+ top_ = node;
+ if (end_ == NULL) {
+ end_ = node;
+ }
+ available_ += size_in_bytes;
+}
+
+
+void FreeListCategory::RepairFreeList(Heap* heap) {
+ FreeListNode* n = top_;
+ while (n != NULL) {
+ Map** map_location = reinterpret_cast<Map**>(n->address());
+ if (*map_location == NULL) {
+ *map_location = heap->free_space_map();
+ } else {
+ ASSERT(*map_location == heap->free_space_map());
+ }
+ n = n->next();
+ }
+}
+
+
+FreeList::FreeList(PagedSpace* owner)
+ : owner_(owner), heap_(owner->heap()) {
+ Reset();
+}
+
+
+intptr_t FreeList::Concatenate(FreeList* free_list) {
+ intptr_t free_bytes = 0;
+ free_bytes += small_list_.Concatenate(free_list->small_list());
+ free_bytes += medium_list_.Concatenate(free_list->medium_list());
+ free_bytes += large_list_.Concatenate(free_list->large_list());
+ free_bytes += huge_list_.Concatenate(free_list->huge_list());
+ return free_bytes;
+}
+
+
+void FreeList::Reset() {
+ small_list_.Reset();
+ medium_list_.Reset();
+ large_list_.Reset();
+ huge_list_.Reset();
+}
+
+
+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);
+ Page* page = Page::FromAddress(start);
+
+ // Early return to drop too-small blocks on the floor.
+ if (size_in_bytes < kSmallListMin) {
+ page->add_non_available_small_blocks(size_in_bytes);
+ return size_in_bytes;
+ }
+
+ // Insert other blocks at the head of a free list of the appropriate
+ // magnitude.
+ if (size_in_bytes <= kSmallListMax) {
+ small_list_.Free(node, size_in_bytes);
+ page->add_available_in_small_free_list(size_in_bytes);
+ } else if (size_in_bytes <= kMediumListMax) {
+ medium_list_.Free(node, size_in_bytes);
+ page->add_available_in_medium_free_list(size_in_bytes);
+ } else if (size_in_bytes <= kLargeListMax) {
+ large_list_.Free(node, size_in_bytes);
+ page->add_available_in_large_free_list(size_in_bytes);
+ } else {
+ huge_list_.Free(node, size_in_bytes);
+ page->add_available_in_huge_free_list(size_in_bytes);
+ }
+
+ ASSERT(IsVeryLong() || available() == SumFreeLists());
+ return 0;
+}
+
+
+FreeListNode* FreeList::FindNodeFor(int size_in_bytes, int* node_size) {
+ FreeListNode* node = NULL;
+ Page* page = NULL;
+
+ if (size_in_bytes <= kSmallAllocationMax) {
+ node = small_list_.PickNodeFromList(node_size);
+ if (node != NULL) {
+ ASSERT(size_in_bytes <= *node_size);
+ page = Page::FromAddress(node->address());
+ page->add_available_in_small_free_list(-(*node_size));
+ ASSERT(IsVeryLong() || available() == SumFreeLists());
+ return node;
+ }
+ }
+
+ if (size_in_bytes <= kMediumAllocationMax) {
+ node = medium_list_.PickNodeFromList(node_size);
+ if (node != NULL) {
+ ASSERT(size_in_bytes <= *node_size);
+ page = Page::FromAddress(node->address());
+ page->add_available_in_medium_free_list(-(*node_size));
+ ASSERT(IsVeryLong() || available() == SumFreeLists());
+ return node;
+ }
+ }
+
+ if (size_in_bytes <= kLargeAllocationMax) {
+ node = large_list_.PickNodeFromList(node_size);
+ if (node != NULL) {
+ ASSERT(size_in_bytes <= *node_size);
+ page = Page::FromAddress(node->address());
+ page->add_available_in_large_free_list(-(*node_size));
+ ASSERT(IsVeryLong() || available() == SumFreeLists());
+ return node;
+ }
+ }
+
+ int huge_list_available = huge_list_.available();
+ for (FreeListNode** cur = huge_list_.GetTopAddress();
+ *cur != NULL;
+ cur = (*cur)->next_address()) {
+ FreeListNode* cur_node = *cur;
+ while (cur_node != NULL &&
+ Page::FromAddress(cur_node->address())->IsEvacuationCandidate()) {
+ int size = reinterpret_cast<FreeSpace*>(cur_node)->Size();
+ huge_list_available -= size;
+ page = Page::FromAddress(cur_node->address());
+ page->add_available_in_huge_free_list(-size);
+ cur_node = cur_node->next();
+ }
+
+ *cur = cur_node;
+ if (cur_node == NULL) {
+ huge_list_.set_end(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;
+ *cur = node->next();
+ *node_size = size;
+ huge_list_available -= size;
+ page = Page::FromAddress(node->address());
+ page->add_available_in_huge_free_list(-size);
+ break;
+ }
+ }
+
+ if (huge_list_.top() == NULL) {
+ huge_list_.set_end(NULL);
+ }
+ huge_list_.set_available(huge_list_available);
+
+ if (node != NULL) {
+ ASSERT(IsVeryLong() || available() == SumFreeLists());
+ return node;
+ }
+
+ if (size_in_bytes <= kSmallListMax) {
+ node = small_list_.PickNodeFromList(size_in_bytes, node_size);
+ if (node != NULL) {
+ ASSERT(size_in_bytes <= *node_size);
+ page = Page::FromAddress(node->address());
+ page->add_available_in_small_free_list(-(*node_size));
+ }
+ } else if (size_in_bytes <= kMediumListMax) {
+ node = medium_list_.PickNodeFromList(size_in_bytes, node_size);
+ if (node != NULL) {
+ ASSERT(size_in_bytes <= *node_size);
+ page = Page::FromAddress(node->address());
+ page->add_available_in_medium_free_list(-(*node_size));
+ }
+ } else if (size_in_bytes <= kLargeListMax) {
+ node = large_list_.PickNodeFromList(size_in_bytes, node_size);
+ if (node != NULL) {
+ ASSERT(size_in_bytes <= *node_size);
+ page = Page::FromAddress(node->address());
+ page->add_available_in_large_free_list(-(*node_size));
+ }
+ }
+
+ ASSERT(IsVeryLong() || available() == SumFreeLists());
+ return node;
+}
+
+
+// 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 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);
+
+ owner_->heap()->incremental_marking()->OldSpaceStep(
+ size_in_bytes - old_linear_size);
+
+ int new_node_size = 0;
+ FreeListNode* new_node = FindNodeFor(size_in_bytes, &new_node_size);
+ if (new_node == NULL) {
+ owner_->SetTop(NULL, NULL);
+ return NULL;
+ }
+
+ int bytes_left = new_node_size - size_in_bytes;
+ ASSERT(bytes_left >= 0);
+
+#ifdef DEBUG
+ for (int i = 0; i < size_in_bytes / kPointerSize; i++) {
+ reinterpret_cast<Object**>(new_node->address())[i] =
+ Smi::FromInt(kCodeZapValue);
+ }
+#endif
+
+ // 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 {
+ // TODO(gc) Try not freeing linear allocation region when bytes_left
+ // are zero.
+ owner_->SetTop(NULL, NULL);
+ }
+
+ return new_node;
+}
+
+
+intptr_t FreeList::EvictFreeListItems(Page* p) {
+ intptr_t sum = huge_list_.EvictFreeListItemsInList(p);
+ p->set_available_in_huge_free_list(0);
+
+ if (sum < p->area_size()) {
+ sum += small_list_.EvictFreeListItemsInList(p) +
+ medium_list_.EvictFreeListItemsInList(p) +
+ large_list_.EvictFreeListItemsInList(p);
+ p->set_available_in_small_free_list(0);
+ p->set_available_in_medium_free_list(0);
+ p->set_available_in_large_free_list(0);
+ }
+
+ return sum;
+}
+
+
+void FreeList::RepairLists(Heap* heap) {
+ small_list_.RepairFreeList(heap);
+ medium_list_.RepairFreeList(heap);
+ large_list_.RepairFreeList(heap);
+ huge_list_.RepairFreeList(heap);
+}
+
+
+#ifdef DEBUG
+intptr_t FreeListCategory::SumFreeList() {
+ intptr_t sum = 0;
+ FreeListNode* cur = top_;
+ while (cur != NULL) {
+ ASSERT(cur->map() == cur->GetHeap()->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;
+}
+
+
+static const int kVeryLongFreeList = 500;
+
+
+int FreeListCategory::FreeListLength() {
+ int length = 0;
+ FreeListNode* cur = top_;
+ while (cur != NULL) {
+ length++;
+ cur = cur->next();
+ if (length == kVeryLongFreeList) return length;
+ }
+ return length;
+}
+
+
+bool FreeList::IsVeryLong() {
+ if (small_list_.FreeListLength() == kVeryLongFreeList) return true;
+ if (medium_list_.FreeListLength() == kVeryLongFreeList) return true;
+ if (large_list_.FreeListLength() == kVeryLongFreeList) return true;
+ if (huge_list_.FreeListLength() == 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 = small_list_.SumFreeList();
+ sum += medium_list_.SumFreeList();
+ sum += large_list_.SumFreeList();
+ sum += huge_list_.SumFreeList();
+ 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. 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());
+ MaybeObject* maybe = AllocateRaw(bytes);
+ Object* object = NULL;
+ if (!maybe->ToObject(&object)) return false;
+ HeapObject* allocation = HeapObject::cast(object);
+ Address top = allocation_info_.top;
+ 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();
+}
+
+
+bool PagedSpace::ReserveSpace(int size_in_bytes) {
+ ASSERT(size_in_bytes <= AreaSize());
+ 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;
+
+ 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;
+
+ 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);
+
+ SetTop(new_area->address(), new_area->address() + size_in_bytes);
+ return true;
+}
+
+
+intptr_t PagedSpace::SizeOfObjects() {
+ ASSERT(!heap()->IsSweepingComplete() || (unswept_free_bytes_ == 0));
+ return Size() - unswept_free_bytes_ - (limit() - top());
+}
+
+
+// After we have booted, we have created a map which represents free space
+// on the heap. If there was already a free list then the elements on it
+// were created with the wrong FreeSpaceMap (normally NULL), so we need to
+// fix them.
+void PagedSpace::RepairFreeListsAfterBoot() {
+ free_list_.RepairLists(heap());
+}
+
+
+// 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()->OldGenerationCapacityAvailable() >= bytes &&
+ (!heap()->incremental_marking()->IsStopped() ||
+ heap()->OldGenerationSpaceAvailable() >= bytes);
+}
+
+
+bool PagedSpace::AdvanceSweeper(intptr_t bytes_to_sweep) {
+ if (IsLazySweepingComplete()) return true;
+
+ 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<MarkCompactCollector::SWEEP_SEQUENTIALLY>(
+ this, NULL, p);
+ }
+ p = next_page;
+ } while (p != anchor() && freed_bytes < bytes_to_sweep);
+
+ if (p == anchor()) {
+ first_unswept_page_ = Page::FromAddress(NULL);
+ } else {
+ first_unswept_page_ = p;
+ }
+
+ heap()->FreeQueuedChunks();
+
+ return IsLazySweepingComplete();
+}
+
+
+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);
+
+ allocation_info_.top = NULL;
+ allocation_info_.limit = NULL;
+ }
+}
+
+
+bool PagedSpace::EnsureSweeperProgress(intptr_t size_in_bytes) {
+ MarkCompactCollector* collector = heap()->mark_compact_collector();
+ if (collector->AreSweeperThreadsActivated()) {
+ if (collector->IsConcurrentSweepingInProgress()) {
+ if (collector->StealMemoryFromSweeperThreads(this) < size_in_bytes) {
+ if (!collector->sequential_sweeping()) {
+ collector->WaitUntilSweepingCompleted();
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ } else {
+ return AdvanceSweeper(size_in_bytes);
+ }
+}
+
+
+HeapObject* PagedSpace::SlowAllocateRaw(int size_in_bytes) {
+ // Allocation in this space has failed.
+
+ // If there are unswept pages advance lazy sweeper a bounded number of times
+ // until we find a size_in_bytes contiguous piece of memory
+ const int kMaxSweepingTries = 5;
+ bool sweeping_complete = false;
+
+ for (int i = 0; i < kMaxSweepingTries && !sweeping_complete; i++) {
+ sweeping_complete = EnsureSweeperProgress(size_in_bytes);
+
+ // 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
+ // 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.
+ if (Expand()) {
+ return free_list_.Allocate(size_in_bytes);
+ }
+
+ // Last ditch, sweep all the remaining pages to try to find space. This may
+ // cause a pause.
+ if (!IsLazySweepingComplete()) {
+ EnsureSweeperProgress(kMaxInt);
+
+ // Retry the free list allocation.
+ HeapObject* object = free_list_.Allocate(size_in_bytes);
+ if (object != NULL) return object;
+ }
+
+ // Finally, fail.
+ return NULL;
+}
+
+
+#ifdef DEBUG
+void PagedSpace::ReportCodeStatistics() {
+ Isolate* isolate = Isolate::Current();
+ CommentStatistic* comments_statistics =
+ isolate->paged_space_comments_statistics();
+ ReportCodeKindStatistics(isolate->code_kind_statistics());
+ PrintF("Code comment statistics (\" [ comment-txt : size/ "
+ "count (average)\"):\n");
+ for (int i = 0; i <= CommentStatistic::kMaxComments; i++) {
+ const CommentStatistic& cs = comments_statistics[i];
+ if (cs.size > 0) {
+ PrintF(" %-30s: %10d/%6d (%d)\n", cs.comment, cs.size, cs.count,
+ cs.size/cs.count);
+ }
+ }
+ PrintF("\n");
+}
+
+
+void PagedSpace::ResetCodeStatistics() {
+ Isolate* isolate = Isolate::Current();
+ CommentStatistic* comments_statistics =
+ isolate->paged_space_comments_statistics();
+ ClearCodeKindStatistics(isolate->code_kind_statistics());
+ for (int i = 0; i < CommentStatistic::kMaxComments; i++) {
+ comments_statistics[i].Clear();
+ }
+ comments_statistics[CommentStatistic::kMaxComments].comment = "Unknown";
+ comments_statistics[CommentStatistic::kMaxComments].size = 0;
+ comments_statistics[CommentStatistic::kMaxComments].count = 0;
+}
+
+
+// Adds comment to 'comment_statistics' table. Performance OK as long as
+// 'kMaxComments' is small
+static void EnterComment(Isolate* isolate, const char* comment, int delta) {
+ CommentStatistic* comments_statistics =
+ isolate->paged_space_comments_statistics();
+ // Do not count empty comments
+ if (delta <= 0) return;
+ CommentStatistic* cs = &comments_statistics[CommentStatistic::kMaxComments];
+ // Search for a free or matching entry in 'comments_statistics': 'cs'
+ // points to result.
+ for (int i = 0; i < CommentStatistic::kMaxComments; i++) {
+ if (comments_statistics[i].comment == NULL) {
+ cs = &comments_statistics[i];
+ cs->comment = comment;
+ break;
+ } else if (strcmp(comments_statistics[i].comment, comment) == 0) {
+ cs = &comments_statistics[i];
+ break;
+ }
+ }
+ // Update entry for 'comment'
+ cs->size += delta;
+ cs->count += 1;
+}
+
+
+// Call for each nested comment start (start marked with '[ xxx', end marked
+// with ']'. RelocIterator 'it' must point to a comment reloc info.
+static void CollectCommentStatistics(Isolate* isolate, RelocIterator* it) {
+ ASSERT(!it->done());
+ ASSERT(it->rinfo()->rmode() == RelocInfo::COMMENT);
+ const char* tmp = reinterpret_cast<const char*>(it->rinfo()->data());
+ if (tmp[0] != '[') {
+ // Not a nested comment; skip
+ return;
+ }
+
+ // Search for end of nested comment or a new nested comment
+ const char* const comment_txt =
+ reinterpret_cast<const char*>(it->rinfo()->data());
+ const byte* prev_pc = it->rinfo()->pc();
+ int flat_delta = 0;
+ it->next();
+ while (true) {
+ // All nested comments must be terminated properly, and therefore exit
+ // from loop.
+ ASSERT(!it->done());
+ if (it->rinfo()->rmode() == RelocInfo::COMMENT) {
+ const char* const txt =
+ reinterpret_cast<const char*>(it->rinfo()->data());
+ flat_delta += static_cast<int>(it->rinfo()->pc() - prev_pc);
+ if (txt[0] == ']') break; // End of nested comment
+ // A new comment
+ CollectCommentStatistics(isolate, it);
+ // Skip code that was covered with previous comment
+ prev_pc = it->rinfo()->pc();
+ }
+ it->next();
+ }
+ EnterComment(isolate, comment_txt, flat_delta);
+}
+
+
+// Collects code size statistics:
+// - by code kind
+// - by code comment
+void PagedSpace::CollectCodeStatistics() {
+ Isolate* isolate = heap()->isolate();
+ HeapObjectIterator obj_it(this);
+ 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();
+ RelocIterator it(code);
+ int delta = 0;
+ const byte* prev_pc = code->instruction_start();
+ while (!it.done()) {
+ if (it.rinfo()->rmode() == RelocInfo::COMMENT) {
+ delta += static_cast<int>(it.rinfo()->pc() - prev_pc);
+ CollectCommentStatistics(isolate, &it);
+ prev_pc = it.rinfo()->pc();
+ }
+ it.next();
+ }
+
+ ASSERT(code->instruction_start() <= prev_pc &&
+ prev_pc <= code->instruction_end());
+ delta += static_cast<int>(code->instruction_end() - prev_pc);
+ EnterComment(isolate, "NoComment", delta);
+ }
+ }
+}
+
+
+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())
+ CollectHistogramInfo(obj);
+ ReportHistogram(true);
+}
+#endif
+
+// -----------------------------------------------------------------------------
+// FixedSpace implementation
+
+void FixedSpace::PrepareForMarkCompact() {
+ // Call prepare of the super class.
+ PagedSpace::PrepareForMarkCompact();
+
+ // 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();
+}
+
+
+// -----------------------------------------------------------------------------
+// MapSpace implementation
+// TODO(mvstanton): this is weird...the compiler can't make a vtable unless
+// there is at least one non-inlined virtual function. I would prefer to hide
+// the VerifyObject definition behind VERIFY_HEAP.
+
+void MapSpace::VerifyObject(HeapObject* object) {
+ // The object should be a map or a free-list node.
+ CHECK(object->IsMap() || object->IsFreeSpace());
+}
+
+
+// -----------------------------------------------------------------------------
+// CellSpace and PropertyCellSpace implementation
+// TODO(mvstanton): this is weird...the compiler can't make a vtable unless
+// there is at least one non-inlined virtual function. I would prefer to hide
+// the VerifyObject definition behind VERIFY_HEAP.
+
+void CellSpace::VerifyObject(HeapObject* object) {
+ // The object should be a global object property cell or a free-list node.
+ CHECK(object->IsCell() ||
+ object->map() == heap()->two_pointer_filler_map());
+}
+
+
+void PropertyCellSpace::VerifyObject(HeapObject* object) {
+ // The object should be a global object property cell or a free-list node.
+ CHECK(object->IsPropertyCell() ||
+ object->map() == heap()->two_pointer_filler_map());
+}
+
+
+// -----------------------------------------------------------------------------
+// LargeObjectIterator
+
+LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) {
+ current_ = space->first_page_;
+ size_func_ = NULL;
+}
+
+
+LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space,
+ HeapObjectCallback size_func) {
+ current_ = space->first_page_;
+ size_func_ = size_func;
+}
+
+
+HeapObject* LargeObjectIterator::Next() {
+ if (current_ == NULL) return NULL;
+
+ HeapObject* object = current_->GetObject();
+ current_ = current_->next_page();
+ return object;
+}
+
+
+// -----------------------------------------------------------------------------
+// LargeObjectSpace
+static bool ComparePointers(void* key1, void* key2) {
+ return key1 == key2;
+}
+
+
+LargeObjectSpace::LargeObjectSpace(Heap* heap,
+ intptr_t max_capacity,
+ AllocationSpace id)
+ : Space(heap, id, NOT_EXECUTABLE), // Managed on a per-allocation basis
+ max_capacity_(max_capacity),
+ first_page_(NULL),
+ size_(0),
+ page_count_(0),
+ objects_size_(0),
+ chunk_map_(ComparePointers, 1024) {}
+
+
+bool LargeObjectSpace::SetUp() {
+ first_page_ = NULL;
+ size_ = 0;
+ page_count_ = 0;
+ objects_size_ = 0;
+ chunk_map_.Clear();
+ return true;
+}
+
+
+void LargeObjectSpace::TearDown() {
+ 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();
+}
+
+
+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() &&
+ heap()->OldGenerationAllocationLimitReached()) {
+ return Failure::RetryAfterGC(identity());
+ }
+
+ if (Size() + object_size > max_capacity_) {
+ return Failure::RetryAfterGC(identity());
+ }
+
+ LargePage* page = heap()->isolate()->memory_allocator()->
+ AllocateLargePage(object_size, this, executable);
+ if (page == NULL) return Failure::RetryAfterGC(identity());
+ ASSERT(page->area_size() >= object_size);
+
+ size_ += static_cast<int>(page->size());
+ objects_size_ += object_size;
+ page_count_++;
+ page->set_next_page(first_page_);
+ first_page_ = page;
+
+ // Register all MemoryChunk::kAlignment-aligned chunks covered by
+ // this large page in the chunk map.
+ uintptr_t base = reinterpret_cast<uintptr_t>(page) / MemoryChunk::kAlignment;
+ uintptr_t limit = base + (page->size() - 1) / MemoryChunk::kAlignment;
+ for (uintptr_t key = base; key <= limit; key++) {
+ HashMap::Entry* entry = chunk_map_.Lookup(reinterpret_cast<void*>(key),
+ static_cast<uint32_t>(key),
+ true);
+ ASSERT(entry != NULL);
+ entry->value = page;
+ }
+
+ HeapObject* object = page->GetObject();
+
+ if (Heap::ShouldZapGarbage()) {
+ // Make the object consistent so the heap can be verified in OldSpaceStep.
+ // We only need to do this in debug builds or if verify_heap is on.
+ reinterpret_cast<Object**>(object->address())[0] =
+ heap()->fixed_array_map();
+ reinterpret_cast<Object**>(object->address())[1] = Smi::FromInt(0);
+ }
+
+ heap()->incremental_marking()->OldSpaceStep(object_size);
+ return object;
+}
+
+
+size_t LargeObjectSpace::CommittedPhysicalMemory() {
+ if (!VirtualMemory::HasLazyCommits()) return CommittedMemory();
+ size_t size = 0;
+ LargePage* current = first_page_;
+ while (current != NULL) {
+ size += current->CommittedPhysicalMemory();
+ current = current->next_page();
+ }
+ return size;
+}
+
+
+// GC support
+MaybeObject* LargeObjectSpace::FindObject(Address a) {
+ LargePage* page = FindPage(a);
+ if (page != NULL) {
+ return page->GetObject();
+ }
+ return Failure::Exception();
+}
+
+
+LargePage* LargeObjectSpace::FindPage(Address a) {
+ uintptr_t key = reinterpret_cast<uintptr_t>(a) / MemoryChunk::kAlignment;
+ HashMap::Entry* e = chunk_map_.Lookup(reinterpret_cast<void*>(key),
+ static_cast<uint32_t>(key),
+ false);
+ if (e != NULL) {
+ ASSERT(e->value != NULL);
+ LargePage* page = reinterpret_cast<LargePage*>(e->value);
+ ASSERT(page->is_valid());
+ if (page->Contains(a)) {
+ return page;
+ }
+ }
+ return NULL;
+}
+
+
+void LargeObjectSpace::FreeUnmarkedObjects() {
+ LargePage* previous = NULL;
+ LargePage* current = first_page_;
+ while (current != NULL) {
+ HeapObject* object = current->GetObject();
+ // 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();
+ Page::FromAddress(object->address())->ResetProgressBar();
+ Page::FromAddress(object->address())->ResetLiveBytes();
+ previous = current;
+ current = current->next_page();
+ } else {
+ LargePage* page = current;
+ // Cut the chunk out from the chunk list.
+ current = current->next_page();
+ if (previous == NULL) {
+ first_page_ = current;
+ } else {
+ previous->set_next_page(current);
+ }
+
+ // Free the chunk.
+ heap()->mark_compact_collector()->ReportDeleteIfNeeded(
+ object, heap()->isolate());
+ size_ -= static_cast<int>(page->size());
+ objects_size_ -= object->Size();
+ page_count_--;
+
+ // Remove entries belonging to this page.
+ // Use variable alignment to help pass length check (<= 80 characters)
+ // of single line in tools/presubmit.py.
+ const intptr_t alignment = MemoryChunk::kAlignment;
+ uintptr_t base = reinterpret_cast<uintptr_t>(page)/alignment;
+ uintptr_t limit = base + (page->size()-1)/alignment;
+ for (uintptr_t key = base; key <= limit; key++) {
+ chunk_map_.Remove(reinterpret_cast<void*>(key),
+ static_cast<uint32_t>(key));
+ }
+
+ 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();
+ MemoryChunk* chunk = MemoryChunk::FromAddress(address);
+
+ bool owned = (chunk->owner() == this);
+
+ SLOW_ASSERT(!owned || !FindObject(address)->IsFailure());
+
+ return owned;
+}
+
+
+#ifdef VERIFY_HEAP
+// 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 (LargePage* chunk = first_page_;
+ chunk != NULL;
+ chunk = chunk->next_page()) {
+ // Each chunk contains an object that starts at the large object page's
+ // object area start.
+ HeapObject* object = chunk->GetObject();
+ Page* page = Page::FromAddress(object->address());
+ CHECK(object->address() == page->area_start());
+
+ // 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));
+
+ // We have only code, sequential strings, external strings
+ // (sequential strings that have been morphed into external
+ // strings), fixed arrays, and byte arrays in large object space.
+ CHECK(object->IsCode() || object->IsSeqString() ||
+ object->IsExternalString() || object->IsFixedArray() ||
+ object->IsFixedDoubleArray() || object->IsByteArray());
+
+ // The object itself should look OK.
+ object->Verify();
+
+ // Byte arrays and strings don't have interior pointers.
+ if (object->IsCode()) {
+ VerifyPointersVisitor code_visitor;
+ object->IterateBody(map->instance_type(),
+ object->Size(),
+ &code_visitor);
+ } else if (object->IsFixedArray()) {
+ FixedArray* array = FixedArray::cast(object);
+ for (int j = 0; j < array->length(); j++) {
+ Object* element = array->get(j);
+ if (element->IsHeapObject()) {
+ HeapObject* element_object = HeapObject::cast(element);
+ CHECK(heap()->Contains(element_object));
+ CHECK(element_object->map()->IsMap());
+ }
+ }
+ }
+ }
+}
+#endif
+
+
+#ifdef DEBUG
+void LargeObjectSpace::Print() {
+ LargeObjectIterator it(this);
+ for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) {
+ obj->Print();
+ }
+}
+
+
+void LargeObjectSpace::ReportStatistics() {
+ PrintF(" size: %" V8_PTR_PREFIX "d\n", size_);
+ int num_objects = 0;
+ ClearHistograms();
+ LargeObjectIterator it(this);
+ for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) {
+ num_objects++;
+ CollectHistogramInfo(obj);
+ }
+
+ PrintF(" number of objects %d, "
+ "size of objects %" V8_PTR_PREFIX "d\n", num_objects, objects_size_);
+ if (num_objects > 0) ReportHistogram(false);
+}
+
+
+void LargeObjectSpace::CollectCodeStatistics() {
+ Isolate* isolate = heap()->isolate();
+ LargeObjectIterator obj_it(this);
+ 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/chromium/v8/src/spaces.h b/chromium/v8/src/spaces.h
new file mode 100644
index 00000000000..aa864b66ba5
--- /dev/null
+++ b/chromium/v8/src/spaces.h
@@ -0,0 +1,2887 @@
+// 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_SPACES_H_
+#define V8_SPACES_H_
+
+#include "allocation.h"
+#include "hashmap.h"
+#include "list.h"
+#include "log.h"
+#include "v8utils.h"
+
+namespace v8 {
+namespace internal {
+
+class Isolate;
+
+// -----------------------------------------------------------------------------
+// Heap structures:
+//
+// A JS heap consists of a young generation, an old generation, and a large
+// object space. The young generation is divided into two semispaces. A
+// scavenger implements Cheney's copying algorithm. The old generation is
+// separated into a map space and an old object space. The map space contains
+// all (and only) map objects, the rest of old objects go into the old space.
+// The old generation is collected by a mark-sweep-compact collector.
+//
+// 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.
+//
+// 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 the page size.
+//
+// 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 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 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.
+
+#define ASSERT_PAGE_ALIGNED(address) \
+ ASSERT((OffsetFrom(address) & Page::kPageAlignmentMask) == 0)
+
+#define ASSERT_OBJECT_ALIGNED(address) \
+ ASSERT((OffsetFrom(address) & kObjectAlignmentMask) == 0)
+
+#define ASSERT_OBJECT_SIZE(size) \
+ ASSERT((0 < size) && (size <= Page::kMaxNonCodeHeapObjectSize))
+
+#define ASSERT_PAGE_OFFSET(offset) \
+ ASSERT((Page::kObjectStartOffset <= offset) \
+ && (offset <= Page::kPageSize))
+
+#define ASSERT_MAP_PAGE_INDEX(index) \
+ ASSERT((0 <= index) && (index <= MapSpace::kMaxMapPageIndex))
+
+
+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*>(reinterpret_cast<intptr_t>(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;
+ }
+
+ bool Contains(Address addr) {
+ return addr >= area_start() && addr < area_end();
+ }
+
+ // 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 >= area_start() && addr <= area_end();
+ }
+
+ // Every n write barrier invocations we go to runtime even though
+ // we could have handled it in generated code. This lets us check
+ // whether we have hit the limit and should do some more marking.
+ static const int kWriteBarrierCounterGranularity = 500;
+
+ 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,
+
+ // Large objects can have a progress bar in their page header. These object
+ // are scanned in increments and will be kept black while being scanned.
+ // Even if the mutator writes to them they will be kept black and a white
+ // to grey transition is performed in the value.
+ HAS_PROGRESS_BAR,
+
+ // 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_; }
+
+ intptr_t parallel_sweeping() const {
+ return parallel_sweeping_;
+ }
+
+ void set_parallel_sweeping(intptr_t state) {
+ parallel_sweeping_ = state;
+ }
+
+ bool TryParallelSweeping() {
+ return NoBarrier_CompareAndSwap(&parallel_sweeping_, 1, 0) == 1;
+ }
+
+ // 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_;
+ }
+
+ int write_barrier_counter() {
+ return static_cast<int>(write_barrier_counter_);
+ }
+
+ void set_write_barrier_counter(int counter) {
+ write_barrier_counter_ = counter;
+ }
+
+ int progress_bar() {
+ ASSERT(IsFlagSet(HAS_PROGRESS_BAR));
+ return progress_bar_;
+ }
+
+ void set_progress_bar(int progress_bar) {
+ ASSERT(IsFlagSet(HAS_PROGRESS_BAR));
+ progress_bar_ = progress_bar;
+ }
+
+ void ResetProgressBar() {
+ if (IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR)) {
+ set_progress_bar(0);
+ ClearFlag(MemoryChunk::HAS_PROGRESS_BAR);
+ }
+ }
+
+ bool IsLeftOfProgressBar(Object** slot) {
+ Address slot_address = reinterpret_cast<Address>(slot);
+ ASSERT(slot_address > this->address());
+ return (slot_address - (this->address() + kObjectStartOffset)) <
+ progress_bar();
+ }
+
+ 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 + kPointerSize + kPointerSize + kIntSize;
+
+ static const size_t kSlotsBufferOffset = kLiveBytesOffset + kIntSize;
+
+ static const size_t kWriteBarrierCounterOffset =
+ kSlotsBufferOffset + kPointerSize + kPointerSize;
+
+ static const size_t kHeaderSize = kWriteBarrierCounterOffset + kPointerSize +
+ kIntSize + kIntSize + kPointerSize +
+ 5 * kPointerSize;
+
+ static const int kBodyOffset =
+ CODE_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;
+ }
+
+ void SetArea(Address area_start, Address area_end) {
+ area_start_ = area_start;
+ area_end_ = area_end;
+ }
+
+ 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);
+ }
+
+ Address area_start() { return area_start_; }
+ Address area_end() { return area_end_; }
+ int area_size() {
+ return static_cast<int>(area_end() - area_start());
+ }
+ bool CommitArea(size_t requested);
+
+ // Approximate amount of physical memory committed for this chunk.
+ size_t CommittedPhysicalMemory() {
+ return high_water_mark_;
+ }
+
+ static inline void UpdateHighWaterMark(Address mark);
+
+ protected:
+ MemoryChunk* next_chunk_;
+ MemoryChunk* prev_chunk_;
+ size_t size_;
+ intptr_t flags_;
+
+ // Start and end of allocatable memory on this chunk.
+ Address area_start_;
+ Address area_end_;
+
+ // 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_;
+ intptr_t write_barrier_counter_;
+ // Used by the incremental marker to keep track of the scanning progress in
+ // large objects that have a progress bar and are scanned in increments.
+ int progress_bar_;
+ // Assuming the initial allocation on a page is sequential,
+ // count highest number of bytes ever allocated on the page.
+ int high_water_mark_;
+
+ intptr_t parallel_sweeping_;
+
+ // PagedSpace free-list statistics.
+ intptr_t available_in_small_free_list_;
+ intptr_t available_in_medium_free_list_;
+ intptr_t available_in_large_free_list_;
+ intptr_t available_in_huge_free_list_;
+ intptr_t non_available_small_blocks_;
+
+ static MemoryChunk* Initialize(Heap* heap,
+ Address base,
+ size_t size,
+ Address area_start,
+ Address area_end,
+ Executability executable,
+ Space* owner);
+
+ friend class MemoryAllocator;
+};
+
+
+STATIC_CHECK(sizeof(MemoryChunk) <= MemoryChunk::kHeaderSize);
+
+
+// -----------------------------------------------------------------------------
+// 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 : public MemoryChunk {
+ public:
+ // Returns the page containing a given address. The address ranges
+ // from [page_addr .. page_addr + kPageSize[
+ // 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);
+ }
+
+ // Returns the page containing an allocation top. Because an allocation
+ // top address can be the upper bound of the page, we need to subtract
+ // it with kPointerSize first. The address ranges from
+ // [page_addr + kObjectStartOffset .. page_addr + kPageSize].
+ INLINE(static Page* FromAllocationTop(Address top)) {
+ Page* p = FromAddress(top - kPointerSize);
+ return p;
+ }
+
+ // Returns the next page in the chain of pages owned by a space.
+ inline Page* next_page();
+ inline Page* prev_page();
+ inline void set_next_page(Page* page);
+ inline void set_prev_page(Page* page);
+
+ // Checks whether an address is page aligned.
+ static bool IsAlignedToPageSize(Address a) {
+ return 0 == (OffsetFrom(a) & kPageAlignmentMask);
+ }
+
+ // Returns the offset of a given address to this page.
+ INLINE(int Offset(Address a)) {
+ int offset = static_cast<int>(a - address());
+ return offset;
+ }
+
+ // Returns the address for a given offset to the this page.
+ Address OffsetToAddress(int offset) {
+ ASSERT_PAGE_OFFSET(offset);
+ return address() + offset;
+ }
+
+ // ---------------------------------------------------------------------
+
+ // Page size in bytes. This must be a multiple of the OS page size.
+ static const int kPageSize = 1 << kPageSizeBits;
+
+ // Object area size in bytes.
+ static const int kNonCodeObjectAreaSize = kPageSize - kObjectStartOffset;
+
+ // Maximum object size that fits in a page. Objects larger than that size
+ // are allocated in large object space and are never moved in memory. This
+ // also applies to new space allocation, since objects are never migrated
+ // from new space to large object space. Takes double alignment into account.
+ static const int kMaxNonCodeHeapObjectSize =
+ kNonCodeObjectAreaSize - kPointerSize;
+
+ // Page size mask.
+ static const intptr_t kPageAlignmentMask = (1 << kPageSizeBits) - 1;
+
+ inline void ClearGCFields();
+
+ static inline Page* Initialize(Heap* heap,
+ MemoryChunk* chunk,
+ Executability executable,
+ PagedSpace* owner);
+
+ void InitializeAsAnchor(PagedSpace* owner);
+
+ bool WasSweptPrecisely() { return IsFlagSet(WAS_SWEPT_PRECISELY); }
+ bool WasSweptConservatively() { return IsFlagSet(WAS_SWEPT_CONSERVATIVELY); }
+ bool WasSwept() { return WasSweptPrecisely() || WasSweptConservatively(); }
+
+ void MarkSweptPrecisely() { SetFlag(WAS_SWEPT_PRECISELY); }
+ void MarkSweptConservatively() { SetFlag(WAS_SWEPT_CONSERVATIVELY); }
+
+ void ClearSweptPrecisely() { ClearFlag(WAS_SWEPT_PRECISELY); }
+ void ClearSweptConservatively() { ClearFlag(WAS_SWEPT_CONSERVATIVELY); }
+
+ void ResetFreeListStatistics();
+
+#define FRAGMENTATION_STATS_ACCESSORS(type, name) \
+ type name() { return name##_; } \
+ void set_##name(type name) { name##_ = name; } \
+ void add_##name(type name) { name##_ += name; }
+
+ FRAGMENTATION_STATS_ACCESSORS(intptr_t, non_available_small_blocks)
+ FRAGMENTATION_STATS_ACCESSORS(intptr_t, available_in_small_free_list)
+ FRAGMENTATION_STATS_ACCESSORS(intptr_t, available_in_medium_free_list)
+ FRAGMENTATION_STATS_ACCESSORS(intptr_t, available_in_large_free_list)
+ FRAGMENTATION_STATS_ACCESSORS(intptr_t, available_in_huge_free_list)
+
+#undef FRAGMENTATION_STATS_ACCESSORS
+
+#ifdef DEBUG
+ void Print();
+#endif // DEBUG
+
+ friend class MemoryAllocator;
+};
+
+
+STATIC_CHECK(sizeof(Page) <= MemoryChunk::kHeaderSize);
+
+
+class LargePage : public MemoryChunk {
+ public:
+ HeapObject* GetObject() {
+ return HeapObject::FromAddress(area_start());
+ }
+
+ 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.
+class Space : public Malloced {
+ public:
+ Space(Heap* heap, AllocationSpace id, Executability executable)
+ : heap_(heap), id_(id), executable_(executable) {}
+
+ virtual ~Space() {}
+
+ Heap* heap() const { return heap_; }
+
+ // Does the space need executable memory?
+ Executability executable() { return executable_; }
+
+ // Identity used in error reporting.
+ AllocationSpace identity() { return id_; }
+
+ // Returns allocated size.
+ virtual intptr_t Size() = 0;
+
+ // Returns size of objects. Can differ from the allocated size
+ // (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
+
+ private:
+ Heap* heap_;
+ AllocationSpace id_;
+ Executability executable_;
+};
+
+
+// ----------------------------------------------------------------------------
+// All heap objects containing executable code (code objects) must be allocated
+// from a 2 GB range of memory, so that they can call each other using 32-bit
+// displacements. This happens automatically on 32-bit platforms, where 32-bit
+// displacements cover the entire 4GB virtual address space. On 64-bit
+// platforms, we support this using the CodeRange object, which reserves and
+// manages a range of virtual memory.
+class CodeRange {
+ public:
+ explicit CodeRange(Isolate* isolate);
+ ~CodeRange() { TearDown(); }
+
+ // 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);
+
+ // Frees the range of virtual memory, and frees the data structures used to
+ // manage it.
+ void TearDown();
+
+ bool exists() { return this != NULL && code_range_ != NULL; }
+ Address start() {
+ if (this == NULL || code_range_ == NULL) return NULL;
+ return static_cast<Address>(code_range_->address());
+ }
+ bool contains(Address address) {
+ if (this == NULL || code_range_ == NULL) return false;
+ Address start = static_cast<Address>(code_range_->address());
+ return start <= address && address < start + code_range_->size();
+ }
+
+ // 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 Address AllocateRawMemory(const size_t requested_size,
+ const size_t commit_size,
+ size_t* allocated);
+ bool CommitRawMemory(Address start, size_t length);
+ bool UncommitRawMemory(Address start, size_t length);
+ void FreeRawMemory(Address buf, size_t length);
+
+ private:
+ Isolate* isolate_;
+
+ // The reserved range of virtual memory that all code objects are put in.
+ VirtualMemory* code_range_;
+ // Plain old data class, just a struct plus a constructor.
+ class FreeBlock {
+ public:
+ FreeBlock(Address start_arg, size_t 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) {
+ ASSERT(IsAddressAligned(start, MemoryChunk::kAlignment));
+ ASSERT(size >= static_cast<size_t>(Page::kPageSize));
+ }
+
+ Address start;
+ size_t size;
+ };
+
+ // Freed blocks of memory are added to the free list. When the allocation
+ // list is exhausted, the free list is sorted and merged to make the new
+ // allocation list.
+ List<FreeBlock> free_list_;
+ // Memory is allocated from the free blocks on the allocation list.
+ // The block at current_allocation_block_index_ is the current block.
+ List<FreeBlock> allocation_list_;
+ int current_allocation_block_index_;
+
+ // Finds a block on the allocation list that contains at least the
+ // requested amount of memory. If none is found, sorts and merges
+ // the existing free memory blocks, and searches again.
+ // If none can be found, terminates V8 with FatalProcessOutOfMemory.
+ void GetNextAllocationBlock(size_t requested);
+ // Compares the start addresses of two free blocks.
+ static int CompareFreeBlockAddress(const FreeBlock* left,
+ const FreeBlock* right);
+
+ DISALLOW_COPY_AND_ASSIGN(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 allocated and deallocates pages for the paged heap spaces and large
+// pages for large object space.
+//
+// Each space has to manage it's own pages.
+//
+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);
+
+ void TearDown();
+
+ Page* AllocatePage(
+ intptr_t size, PagedSpace* owner, Executability executable);
+
+ LargePage* AllocateLargePage(
+ intptr_t object_size, Space* owner, Executability executable);
+
+ void Free(MemoryChunk* chunk);
+
+ // Returns the maximum available bytes of heaps.
+ intptr_t Available() { return capacity_ < size_ ? 0 : capacity_ - size_; }
+
+ // Returns allocated spaces in bytes.
+ intptr_t Size() { return size_; }
+
+ // Returns the maximum available executable bytes of heaps.
+ intptr_t AvailableExecutable() {
+ if (capacity_executable_ < size_executable_) return 0;
+ return capacity_executable_ - size_executable_;
+ }
+
+ // Returns allocated executable spaces in bytes.
+ intptr_t SizeExecutable() { return size_executable_; }
+
+ // Returns maximum available bytes that the old space can have.
+ intptr_t MaxAvailable() {
+ return (Available() / Page::kPageSize) * Page::kMaxNonCodeHeapObjectSize;
+ }
+
+#ifdef DEBUG
+ // Reports statistic info of the space.
+ void ReportStatistics();
+#endif
+
+ // Returns a MemoryChunk in which the memory region from commit_area_size to
+ // reserve_area_size of the chunk area is reserved but not committed, it
+ // could be committed later by calling MemoryChunk::CommitArea.
+ MemoryChunk* AllocateChunk(intptr_t reserve_area_size,
+ intptr_t commit_area_size,
+ Executability executable,
+ Space* space);
+
+ Address ReserveAlignedMemory(size_t requested,
+ size_t alignment,
+ VirtualMemory* controller);
+ Address AllocateAlignedMemory(size_t reserve_size,
+ size_t commit_size,
+ size_t alignment,
+ Executability executable,
+ VirtualMemory* controller);
+
+ void FreeMemory(VirtualMemory* reservation, Executability executable);
+ void FreeMemory(Address addr, size_t size, Executability executable);
+
+ // 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);
+
+ // 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);
+
+ void PerformAllocationCallback(ObjectSpace space,
+ AllocationAction action,
+ size_t size);
+
+ void AddMemoryAllocationCallback(MemoryAllocationCallback callback,
+ ObjectSpace space,
+ AllocationAction action);
+
+ void RemoveMemoryAllocationCallback(
+ MemoryAllocationCallback callback);
+
+ bool MemoryAllocationCallbackRegistered(
+ MemoryAllocationCallback callback);
+
+ static int CodePageGuardStartOffset();
+
+ static int CodePageGuardSize();
+
+ static int CodePageAreaStartOffset();
+
+ static int CodePageAreaEndOffset();
+
+ static int CodePageAreaSize() {
+ return CodePageAreaEndOffset() - CodePageAreaStartOffset();
+ }
+
+ MUST_USE_RESULT static bool CommitExecutableMemory(VirtualMemory* vm,
+ Address start,
+ size_t commit_size,
+ size_t reserved_size);
+
+ private:
+ Isolate* isolate_;
+
+ // Maximum space size in bytes.
+ size_t capacity_;
+ // Maximum subset of capacity_ that can be executable
+ size_t capacity_executable_;
+
+ // Allocated space size in bytes.
+ size_t size_;
+ // Allocated executable space size in bytes.
+ size_t size_executable_;
+
+ struct MemoryAllocationCallbackRegistration {
+ MemoryAllocationCallbackRegistration(MemoryAllocationCallback callback,
+ ObjectSpace space,
+ AllocationAction action)
+ : callback(callback), space(space), action(action) {
+ }
+ MemoryAllocationCallback callback;
+ ObjectSpace space;
+ AllocationAction action;
+ };
+
+ // A List of callback that are triggered when memory is allocated or free'd
+ List<MemoryAllocationCallbackRegistration>
+ memory_allocation_callbacks_;
+
+ // 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
+ // used as a marking stack and its page headers are destroyed.
+ Page* InitializePagesInChunk(int chunk_id, int pages_in_chunk,
+ PagedSpace* owner);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MemoryAllocator);
+};
+
+
+// -----------------------------------------------------------------------------
+// Interface for heap object iterator to be implemented by all object space
+// object iterators.
+//
+// NOTE: The space specific object iterators also implements the own next()
+// method which is used to avoid using virtual functions
+// iterating a specific space.
+
+class ObjectIterator : public Malloced {
+ public:
+ virtual ~ObjectIterator() { }
+
+ virtual HeapObject* next_object() = 0;
+};
+
+
+// -----------------------------------------------------------------------------
+// Heap object iterator in new/old/map spaces.
+//
+// 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.
+//
+// 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 the size function is not given, the iterator calls the default
+ // Object::Size().
+ explicit HeapObjectIterator(PagedSpace* space);
+ HeapObjectIterator(PagedSpace* space, HeapObjectCallback size_func);
+ HeapObjectIterator(Page* page, HeapObjectCallback size_func);
+
+ // 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;
+ }
+
+ virtual HeapObject* next_object() {
+ return Next();
+ }
+
+ private:
+ enum PageMode { kOnePageOnly, kAllPagesInSpace };
+
+ Address cur_addr_; // Current iteration point.
+ Address cur_end_; // End iteration point.
+ HeapObjectCallback size_func_; // Size function or NULL.
+ PagedSpace* space_;
+ PageMode page_mode_;
+
+ // Fast (inlined) path of next().
+ inline HeapObject* FromCurrentPage();
+
+ // Slow path of next(), goes into the next page. Returns false if the
+ // iteration has ended.
+ bool AdvanceToNextPage();
+
+ // Initializes fields.
+ inline void Initialize(PagedSpace* owner,
+ Address start,
+ Address end,
+ PageMode mode,
+ HeapObjectCallback size_func);
+};
+
+
+// -----------------------------------------------------------------------------
+// A PageIterator iterates the pages in a paged space.
+
+class PageIterator BASE_EMBEDDED {
+ public:
+ explicit inline PageIterator(PagedSpace* space);
+
+ inline bool has_next();
+ inline Page* next();
+
+ private:
+ PagedSpace* space_;
+ Page* 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.
+ Page* next_page_;
+};
+
+
+// -----------------------------------------------------------------------------
+// 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:
+ AllocationInfo() : top(NULL), limit(NULL) {
+ }
+
+ Address top; // Current allocation top.
+ Address limit; // Current allocation limit.
+
+#ifdef DEBUG
+ bool VerifyPagedAllocation() {
+ return (Page::FromAllocationTop(top) == Page::FromAllocationTop(limit))
+ && (top <= limit);
+ }
+#endif
+};
+
+
+// An abstraction of the accounting statistics of a page-structured space.
+// 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 (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.
+//
+// The stats are only set by functions that ensure they stay balanced. These
+// functions increase or decrease one of the non-capacity stats in
+// conjunction with capacity, or else they always balance increases and
+// decreases to the non-capacity stats.
+class AllocationStats BASE_EMBEDDED {
+ public:
+ AllocationStats() { Clear(); }
+
+ // Zero out all the allocation statistics (i.e., no capacity).
+ void Clear() {
+ capacity_ = 0;
+ size_ = 0;
+ waste_ = 0;
+ }
+
+ void ClearSizeWaste() {
+ size_ = capacity_;
+ waste_ = 0;
+ }
+
+ // Reset the allocation statistics (i.e., available = capacity with no
+ // wasted or allocated bytes).
+ void Reset() {
+ size_ = 0;
+ waste_ = 0;
+ }
+
+ // Accessors for the allocation statistics.
+ intptr_t Capacity() { return capacity_; }
+ intptr_t Size() { return size_; }
+ intptr_t Waste() { return waste_; }
+
+ // 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;
+ size_ += size_in_bytes;
+ ASSERT(size_ >= 0);
+ }
+
+ // 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;
+ size_ -= size_in_bytes;
+ ASSERT(size_ >= 0);
+ }
+
+ // Allocate from available bytes (available -> size).
+ void AllocateBytes(intptr_t 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;
+ ASSERT(size_ >= 0);
+ }
+
+ // Waste free bytes (available -> waste).
+ void WasteBytes(int size_in_bytes) {
+ size_ -= size_in_bytes;
+ waste_ += size_in_bytes;
+ ASSERT(size_ >= 0);
+ }
+
+ private:
+ intptr_t capacity_;
+ 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();
+
+ static inline FreeListNode* cast(MaybeObject* maybe) {
+ ASSERT(!maybe->IsFailure());
+ return reinterpret_cast<FreeListNode*>(maybe);
+ }
+
+ private:
+ static const int kNextOffset = POINTER_SIZE_ALIGN(FreeSpace::kHeaderSize);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FreeListNode);
+};
+
+
+// The free list category holds a pointer to the top element and a pointer to
+// the end element of the linked list of free memory blocks.
+class FreeListCategory {
+ public:
+ FreeListCategory() :
+ top_(NULL),
+ end_(NULL),
+ mutex_(OS::CreateMutex()),
+ available_(0) {}
+
+ ~FreeListCategory() {
+ delete mutex_;
+ }
+
+ intptr_t Concatenate(FreeListCategory* category);
+
+ void Reset();
+
+ void Free(FreeListNode* node, int size_in_bytes);
+
+ FreeListNode* PickNodeFromList(int *node_size);
+ FreeListNode* PickNodeFromList(int size_in_bytes, int *node_size);
+
+ intptr_t EvictFreeListItemsInList(Page* p);
+
+ void RepairFreeList(Heap* heap);
+
+ FreeListNode** GetTopAddress() { return &top_; }
+ FreeListNode* top() const { return top_; }
+ void set_top(FreeListNode* top) { top_ = top; }
+
+ FreeListNode** GetEndAddress() { return &end_; }
+ FreeListNode* end() const { return end_; }
+ void set_end(FreeListNode* end) { end_ = end; }
+
+ int* GetAvailableAddress() { return &available_; }
+ int available() const { return available_; }
+ void set_available(int available) { available_ = available; }
+
+ Mutex* mutex() { return mutex_; }
+
+#ifdef DEBUG
+ intptr_t SumFreeList();
+ int FreeListLength();
+#endif
+
+ private:
+ FreeListNode* top_;
+ FreeListNode* end_;
+ Mutex* mutex_;
+
+ // Total available bytes in all blocks of this free list category.
+ int available_;
+};
+
+
+// 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);
+
+ intptr_t Concatenate(FreeList* free_list);
+
+ // Clear the free list.
+ void Reset();
+
+ // Return the number of bytes available on the free list.
+ intptr_t available() {
+ return small_list_.available() + medium_list_.available() +
+ large_list_.available() + huge_list_.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();
+ intptr_t SumFreeLists();
+ bool IsVeryLong();
+#endif
+
+ // Used after booting the VM.
+ void RepairLists(Heap* heap);
+
+ intptr_t EvictFreeListItems(Page* p);
+
+ FreeListCategory* small_list() { return &small_list_; }
+ FreeListCategory* medium_list() { return &medium_list_; }
+ FreeListCategory* large_list() { return &large_list_; }
+ FreeListCategory* huge_list() { return &huge_list_; }
+
+ private:
+ // The size range of blocks, in bytes.
+ static const int kMinBlockSize = 3 * kPointerSize;
+ static const int kMaxBlockSize = Page::kMaxNonCodeHeapObjectSize;
+
+ FreeListNode* FindNodeFor(int size_in_bytes, int* node_size);
+
+ PagedSpace* owner_;
+ Heap* heap_;
+
+ 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;
+ FreeListCategory small_list_;
+ FreeListCategory medium_list_;
+ FreeListCategory large_list_;
+ FreeListCategory huge_list_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FreeList);
+};
+
+
+class PagedSpace : public Space {
+ public:
+ // Creates a space with a maximum capacity, and an id.
+ PagedSpace(Heap* heap,
+ intptr_t max_capacity,
+ AllocationSpace id,
+ Executability executable);
+
+ virtual ~PagedSpace() {}
+
+ // Set up the space using the given address range of virtual memory (from
+ // 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();
+
+ // Returns true if the space has been successfully set up and not
+ // subsequently torn down.
+ 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.
+ void TearDown();
+
+ // Checks whether an object/address is in this space.
+ inline bool Contains(Address a);
+ bool Contains(HeapObject* o) { return Contains(o->address()); }
+
+ // 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
+ // iterates over objects in the page containing the address, the cost is
+ // linear in the number of objects in the page. It may be slow.
+ MUST_USE_RESULT MaybeObject* FindObject(Address addr);
+
+ // During boot the free_space_map is created, and afterwards we may need
+ // to write it into the free list nodes that were already created.
+ virtual void RepairFreeListsAfterBoot();
+
+ // Prepares for a mark-compact GC.
+ virtual void PrepareForMarkCompact();
+
+ // 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(); }
+
+ // Approximate amount of physical memory committed for this space.
+ size_t CommittedPhysicalMemory();
+
+ 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 ObtainFreeListStatistics(Page* p, SizeStats* sizes);
+ void ResetFreeListStatistics();
+
+ // 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();
+ ResetFreeListStatistics();
+ }
+
+ // Increases the number of available bytes of that space.
+ void AddToAccountingStats(intptr_t bytes) {
+ accounting_stats_.DeallocateBytes(bytes);
+ }
+
+ // 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. 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(); }
+
+ // 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();
+
+ // 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; }
+
+ // The allocation top and limit addresses.
+ Address* allocation_top_address() { return &allocation_info_.top; }
+ Address* allocation_limit_address() { 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);
+
+ virtual bool ReserveSpace(int bytes);
+
+ // 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;
+ }
+
+ void ResetFreeList() {
+ free_list_.Reset();
+ }
+
+ // Set space allocation info.
+ void SetTop(Address top, Address limit) {
+ ASSERT(top == limit ||
+ Page::FromAddress(top) == Page::FromAddress(limit - 1));
+ MemoryChunk::UpdateHighWaterMark(allocation_info_.top);
+ allocation_info_.top = top;
+ allocation_info_.limit = limit;
+ }
+
+ void Allocate(int bytes) {
+ accounting_stats_.AllocateBytes(bytes);
+ }
+
+ void IncreaseCapacity(int size) {
+ accounting_stats_.ExpandSpace(size);
+ }
+
+ // Releases an unused page and shrinks the space.
+ void ReleasePage(Page* page, bool unlink);
+
+ // The dummy page that anchors the linked list of pages.
+ Page* anchor() { return &anchor_; }
+
+#ifdef VERIFY_HEAP
+ // Verify integrity of this space.
+ virtual void Verify(ObjectVisitor* visitor);
+
+ // 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) {}
+#endif
+
+#ifdef DEBUG
+ // Print meta info and objects in this space.
+ virtual void Print();
+
+ // Reports statistics for the space
+ void ReportStatistics();
+
+ // Report code object related statistics
+ void CollectCodeStatistics();
+ static void ReportCodeStatistics();
+ static void ResetCodeStatistics();
+#endif
+
+ 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(intptr_t by) {
+ unswept_free_bytes_ += by;
+ }
+
+ void IncreaseUnsweptFreeBytes(Page* p) {
+ ASSERT(ShouldBeSweptLazily(p));
+ unswept_free_bytes_ += (p->area_size() - p->LiveBytes());
+ }
+
+ void DecrementUnsweptFreeBytes(intptr_t by) {
+ unswept_free_bytes_ -= by;
+ }
+
+ void DecreaseUnsweptFreeBytes(Page* p) {
+ ASSERT(ShouldBeSweptLazily(p));
+ unswept_free_bytes_ -= (p->area_size() - p->LiveBytes());
+ }
+
+ void ResetUnsweptFreeBytes() {
+ unswept_free_bytes_ = 0;
+ }
+
+ bool AdvanceSweeper(intptr_t bytes_to_sweep);
+
+ // When parallel sweeper threads are active and the main thread finished
+ // its sweeping phase, this function waits for them to complete, otherwise
+ // AdvanceSweeper with size_in_bytes is called.
+ bool EnsureSweeperProgress(intptr_t size_in_bytes);
+
+ bool IsLazySweepingComplete() {
+ return !first_unswept_page_->is_valid();
+ }
+
+ Page* FirstPage() { return anchor_.next_page(); }
+ Page* LastPage() { return anchor_.prev_page(); }
+
+ void EvictEvacuationCandidatesFromFreeLists();
+
+ bool CanExpand();
+
+ // Returns the number of total pages in this space.
+ int CountTotalPages();
+
+ // Return size of allocatable area on a page in this space.
+ inline int AreaSize() {
+ return area_size_;
+ }
+
+ protected:
+ FreeList* free_list() { return &free_list_; }
+
+ int area_size_;
+
+ // Maximum capacity of this space.
+ intptr_t max_capacity_;
+
+ intptr_t SizeOfFirstPage();
+
+ // Accounting information for this space.
+ AllocationStats accounting_stats_;
+
+ // The dummy page that anchors the double linked list of pages.
+ Page anchor_;
+
+ // The space's free list.
+ FreeList free_list_;
+
+ // Normal allocation information.
+ AllocationInfo allocation_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_;
+
+ bool was_swept_conservatively_;
+
+ // 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_;
+
+ // 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, or if the hard heap
+ // size limit has been hit.
+ bool Expand();
+
+ // 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);
+
+ friend class PageIterator;
+ friend class SweeperThread;
+};
+
+
+class NumberAndSizeInfo BASE_EMBEDDED {
+ public:
+ NumberAndSizeInfo() : number_(0), bytes_(0) {}
+
+ int number() const { return number_; }
+ void increment_number(int num) { number_ += num; }
+
+ int bytes() const { return bytes_; }
+ void increment_bytes(int size) { bytes_ += size; }
+
+ void clear() {
+ number_ = 0;
+ bytes_ = 0;
+ }
+
+ private:
+ int number_;
+ int bytes_;
+};
+
+
+// HistogramInfo class for recording a single "bar" of a histogram. This
+// class is used for collecting statistics to print to the log file.
+class HistogramInfo: public NumberAndSizeInfo {
+ public:
+ HistogramInfo() : NumberAndSizeInfo() {}
+
+ const char* name() { return name_; }
+ void set_name(const char* name) { name_ = name; }
+
+ private:
+ const char* name_;
+};
+
+
+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);
+
+ static const int kAreaSize = Page::kNonCodeObjectAreaSize;
+
+ 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 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.
+ 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.
+ 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 semispace to the new capacity. The new 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
+ // requested must be more than the amount of used memory in the
+ // semispace and less than the current capacity.
+ bool ShrinkTo(int new_capacity);
+
+ // Returns the start address of the first page of the space.
+ Address space_start() {
+ ASSERT(anchor_.next_page() != &anchor_);
+ return anchor_.next_page()->area_start();
+ }
+
+ // Returns the start address of the current page of the space.
+ Address page_low() {
+ return current_page_->area_start();
+ }
+
+ // Returns one past the end address of the space.
+ Address space_end() {
+ return anchor_.prev_page()->area_end();
+ }
+
+ // Returns one past the end address of the current page of the space.
+ Address page_high() {
+ return current_page_->area_end();
+ }
+
+ 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);
+
+ // True if the address is in the address range of this semispace (not
+ // necessarily below the allocation pointer).
+ bool Contains(Address a) {
+ return (reinterpret_cast<uintptr_t>(a) & address_mask_)
+ == reinterpret_cast<uintptr_t>(start_);
+ }
+
+ // True if the object is a heap object in the address range of this
+ // semispace (not necessarily below the allocation pointer).
+ bool Contains(Object* o) {
+ return (reinterpret_cast<uintptr_t>(o) & object_mask_) == object_expected_;
+ }
+
+ // If we don't have these here then SemiSpace will be abstract. However
+ // they should never be called.
+ virtual intptr_t Size() {
+ UNREACHABLE();
+ return 0;
+ }
+
+ virtual bool ReserveSpace(int bytes) {
+ UNREACHABLE();
+ return false;
+ }
+
+ bool is_committed() { return committed_; }
+ bool Commit();
+ bool Uncommit();
+
+ NewSpacePage* first_page() { return anchor_.next_page(); }
+ NewSpacePage* current_page() { return current_page_; }
+
+#ifdef VERIFY_HEAP
+ virtual void Verify();
+#endif
+
+#ifdef DEBUG
+ virtual void Print();
+ // 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.
+ int Capacity() { return capacity_; }
+
+ // Returns the maximum capacity of the semi space.
+ int MaximumCapacity() { return maximum_capacity_; }
+
+ // Returns the initial capacity of the semi space.
+ int InitialCapacity() { return initial_capacity_; }
+
+ SemiSpaceId id() { return id_; }
+
+ static void Swap(SemiSpace* from, SemiSpace* to);
+
+ // Approximate amount of physical memory committed for this space.
+ size_t CommittedPhysicalMemory();
+
+ 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_;
+ int initial_capacity_;
+
+ // The start address of the space.
+ Address start_;
+ // Used to govern object promotion during mark-compact collection.
+ Address age_mark_;
+
+ // Masks and comparison values to test for containment in this semispace.
+ uintptr_t address_mask_;
+ uintptr_t object_mask_;
+ uintptr_t object_expected_;
+
+ bool committed_;
+ SemiSpaceId id_;
+
+ NewSpacePage anchor_;
+ NewSpacePage* current_page_;
+
+ friend class SemiSpaceIterator;
+ friend class NewSpacePageIterator;
+ public:
+ TRACK_MEMORY("SemiSpace")
+};
+
+
+// A SemiSpaceIterator is an ObjectIterator that iterates over the active
+// semispace of the heap's new space. It iterates over the objects in the
+// semispace from a given start address (defaulting to the bottom of the
+// semispace) to the top of the semispace. New objects allocated after the
+// iterator is created are not iterated.
+class SemiSpaceIterator : public ObjectIterator {
+ public:
+ // 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() {
+ if (current_ == limit_) return NULL;
+ if (NewSpacePage::IsAtEnd(current_)) {
+ NewSpacePage* page = NewSpacePage::FromLimit(current_);
+ page = page->next_page();
+ ASSERT(!page->is_anchor());
+ current_ = page->area_start();
+ if (current_ == limit_) return NULL;
+ }
+
+ HeapObject* object = HeapObject::FromAddress(current_);
+ int size = (size_func_ == NULL) ? object->Size() : size_func_(object);
+
+ current_ += size;
+ return object;
+ }
+
+ // Implementation of the ObjectIterator functions.
+ virtual HeapObject* next_object() { return Next(); }
+
+ private:
+ void Initialize(Address start,
+ Address end,
+ HeapObjectCallback size_func);
+
+ // The current iteration point.
+ Address current_;
+ // The end of iteration.
+ Address limit_;
+ // The callback function.
+ HeapObjectCallback size_func_;
+};
+
+
+// -----------------------------------------------------------------------------
+// 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
+// forwards most functions to the appropriate semispace.
+
+class NewSpace : public Space {
+ public:
+ // Constructor.
+ explicit NewSpace(Heap* heap)
+ : Space(heap, NEW_SPACE, NOT_EXECUTABLE),
+ 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(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();
+ }
+
+ // Flip the pair of spaces.
+ void Flip();
+
+ // Grow the capacity of the semispaces. Assumes that they are not at
+ // their maximum capacity.
+ void Grow();
+
+ // Shrink the capacity of the semispaces.
+ void Shrink();
+
+ // True if the address or object lies in the address range of either
+ // semispace (not necessarily below the allocation pointer).
+ bool Contains(Address a) {
+ return (reinterpret_cast<uintptr_t>(a) & address_mask_)
+ == reinterpret_cast<uintptr_t>(start_);
+ }
+
+ bool Contains(Object* o) {
+ 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 pages_used_ * NewSpacePage::kAreaSize +
+ 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) * NewSpacePage::kAreaSize;
+ }
+
+ // Return the current capacity of a semispace.
+ intptr_t Capacity() {
+ ASSERT(to_space_.Capacity() == from_space_.Capacity());
+ return to_space_.Capacity();
+ }
+
+ // Return the total amount of memory committed for new space.
+ intptr_t CommittedMemory() {
+ if (from_space_.is_committed()) return 2 * Capacity();
+ return Capacity();
+ }
+
+ // Approximate amount of physical memory committed for this space.
+ size_t CommittedPhysicalMemory();
+
+ // Return the available bytes without growing.
+ intptr_t Available() {
+ return Capacity() - Size();
+ }
+
+ // Return the maximum capacity of a semispace.
+ int MaximumCapacity() {
+ ASSERT(to_space_.MaximumCapacity() == from_space_.MaximumCapacity());
+ return to_space_.MaximumCapacity();
+ }
+
+ // Returns the initial capacity of a semispace.
+ int InitialCapacity() {
+ ASSERT(to_space_.InitialCapacity() == from_space_.InitialCapacity());
+ return to_space_.InitialCapacity();
+ }
+
+ // Return the address of the allocation pointer in the active semispace.
+ 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_.space_start(); }
+
+ // Get the age mark of the inactive semispace.
+ Address age_mark() { return from_space_.age_mark(); }
+ // Set the age mark in the active semispace.
+ void set_age_mark(Address mark) { to_space_.set_age_mark(mark); }
+
+ // The start address of the space and a bit mask. Anding an address in the
+ // new space with the mask will result in the start address.
+ 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 INLINE(MaybeObject* AllocateRaw(int size_in_bytes));
+
+ // Reset the allocation pointer to the beginning of the active semispace.
+ void ResetAllocationInfo();
+
+ 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 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(); }
+
+ // 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);
+ }
+ 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).
+ inline bool ToSpaceContains(Object* o) { return to_space_.Contains(o); }
+ inline bool FromSpaceContains(Object* o) { return from_space_.Contains(o); }
+
+ // 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);
+
+#ifdef VERIFY_HEAP
+ // Verify the active semispace.
+ virtual void Verify();
+#endif
+
+#ifdef DEBUG
+ // Print the active semispace.
+ virtual void Print() { to_space_.Print(); }
+#endif
+
+ // Iterates the active semispace to collect statistics.
+ void CollectStatistics();
+ // Reports previously collected statistics of the active semispace.
+ void ReportStatistics();
+ // Clears previously collected statistics.
+ void ClearHistograms();
+
+ // Record the allocation or promotion of a heap object. Note that we don't
+ // record every single allocation, but only those that happen in the
+ // to space during a scavenge GC.
+ void RecordAllocation(HeapObject* obj);
+ void RecordPromotion(HeapObject* obj);
+
+ // Return whether the operation succeded.
+ bool CommitFromSpaceIfNeeded() {
+ if (from_space_.is_committed()) return true;
+ return from_space_.Commit();
+ }
+
+ bool UncommitFromSpace() {
+ if (!from_space_.is_committed()) return true;
+ 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_;
+ uintptr_t address_mask_;
+ uintptr_t object_mask_;
+ uintptr_t object_expected_;
+
+ // Allocation pointer and limit for normal allocation and allocation during
+ // mark-compact collection.
+ AllocationInfo allocation_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_;
+
+ MUST_USE_RESULT MaybeObject* SlowAllocateRaw(int size_in_bytes);
+
+ friend class SemiSpaceIterator;
+
+ public:
+ TRACK_MEMORY("NewSpace")
+};
+
+
+// -----------------------------------------------------------------------------
+// Old object space (excluding map objects)
+
+class OldSpace : public PagedSpace {
+ public:
+ // Creates an old space object with a given maximum capacity.
+ // The constructor does not allocate pages from OS.
+ OldSpace(Heap* heap,
+ intptr_t max_capacity,
+ AllocationSpace id,
+ Executability executable)
+ : PagedSpace(heap, max_capacity, id, executable) {
+ page_extra_ = 0;
+ }
+
+ // The limit of allocation for a page in this space.
+ virtual Address PageAllocationLimit(Page* page) {
+ return page->area_end();
+ }
+
+ 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
+
+class FixedSpace : public PagedSpace {
+ public:
+ FixedSpace(Heap* heap,
+ intptr_t max_capacity,
+ AllocationSpace id,
+ int object_size_in_bytes)
+ : PagedSpace(heap, max_capacity, id, NOT_EXECUTABLE),
+ object_size_in_bytes_(object_size_in_bytes) {
+ page_extra_ = Page::kNonCodeObjectAreaSize % object_size_in_bytes;
+ }
+
+ // The limit of allocation for a page in this space.
+ virtual Address PageAllocationLimit(Page* page) {
+ return page->area_end() - page_extra_;
+ }
+
+ int object_size_in_bytes() { return object_size_in_bytes_; }
+
+ // Prepares for a mark-compact GC.
+ virtual void PrepareForMarkCompact();
+
+ private:
+ // The size of objects in this space.
+ int object_size_in_bytes_;
+};
+
+
+// -----------------------------------------------------------------------------
+// Old space for all map objects
+
+class MapSpace : public FixedSpace {
+ public:
+ // Creates a map space object with a maximum capacity.
+ MapSpace(Heap* heap, intptr_t max_capacity, AllocationSpace id)
+ : FixedSpace(heap, max_capacity, id, Map::kSize),
+ max_map_space_pages_(kMaxMapPageIndex - 1) {
+ }
+
+ // Given an index, returns the page address.
+ // 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;
+ }
+ }
+
+ protected:
+ virtual void VerifyObject(HeapObject* obj);
+
+ private:
+ static const int kMapsPerPage = Page::kNonCodeObjectAreaSize / Map::kSize;
+
+ // Do map space compaction if there is a page gap.
+ int CompactionThreshold() {
+ return kMapsPerPage * (max_map_space_pages_ - 1);
+ }
+
+ const int max_map_space_pages_;
+
+ public:
+ TRACK_MEMORY("MapSpace")
+};
+
+
+// -----------------------------------------------------------------------------
+// Old space for simple property cell objects
+
+class CellSpace : public FixedSpace {
+ public:
+ // Creates a property cell space object with a maximum capacity.
+ CellSpace(Heap* heap, intptr_t max_capacity, AllocationSpace id)
+ : FixedSpace(heap, max_capacity, id, Cell::kSize)
+ {}
+
+ virtual int RoundSizeDownToObjectAlignment(int size) {
+ if (IsPowerOf2(Cell::kSize)) {
+ return RoundDown(size, Cell::kSize);
+ } else {
+ return (size / Cell::kSize) * Cell::kSize;
+ }
+ }
+
+ protected:
+ virtual void VerifyObject(HeapObject* obj);
+
+ public:
+ TRACK_MEMORY("CellSpace")
+};
+
+
+// -----------------------------------------------------------------------------
+// Old space for all global object property cell objects
+
+class PropertyCellSpace : public FixedSpace {
+ public:
+ // Creates a property cell space object with a maximum capacity.
+ PropertyCellSpace(Heap* heap, intptr_t max_capacity,
+ AllocationSpace id)
+ : FixedSpace(heap, max_capacity, id, PropertyCell::kSize)
+ {}
+
+ virtual int RoundSizeDownToObjectAlignment(int size) {
+ if (IsPowerOf2(PropertyCell::kSize)) {
+ return RoundDown(size, PropertyCell::kSize);
+ } else {
+ return (size / PropertyCell::kSize) * PropertyCell::kSize;
+ }
+ }
+
+ protected:
+ virtual void VerifyObject(HeapObject* obj);
+
+ public:
+ TRACK_MEMORY("PropertyCellSpace")
+};
+
+
+// -----------------------------------------------------------------------------
+// Large objects ( > Page::kMaxHeapObjectSize ) are allocated and managed by
+// the large object space. A large object is allocated from OS heap with
+// extra padding bytes (Page::kPageSize + Page::kObjectStartOffset).
+// A large object always starts at Page::kObjectStartOffset to a page.
+// Large objects do not move during garbage collections.
+
+class LargeObjectSpace : public Space {
+ public:
+ LargeObjectSpace(Heap* heap, intptr_t max_capacity, AllocationSpace id);
+ virtual ~LargeObjectSpace() {}
+
+ // Initializes internal data structures.
+ bool SetUp();
+
+ // Releases internal resources, frees objects in this space.
+ void TearDown();
+
+ 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();
+
+ virtual intptr_t Size() {
+ return size_;
+ }
+
+ virtual intptr_t SizeOfObjects() {
+ return objects_size_;
+ }
+
+ intptr_t CommittedMemory() {
+ return Size();
+ }
+
+ // Approximate amount of physical memory committed for this space.
+ size_t CommittedPhysicalMemory();
+
+ int PageCount() {
+ return page_count_;
+ }
+
+ // Finds an object for a given address, returns Failure::Exception()
+ // if it is not found. The function iterates through all objects in this
+ // space, may be slow.
+ MaybeObject* FindObject(Address a);
+
+ // Finds a large object page containing the given address, returns NULL
+ // if such a page doesn't exist.
+ LargePage* FindPage(Address a);
+
+ // Frees unmarked objects.
+ void FreeUnmarkedObjects();
+
+ // Checks whether a heap object is in this space; O(1).
+ bool Contains(HeapObject* obj);
+
+ // Checks whether the space is empty.
+ 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 VERIFY_HEAP
+ virtual void Verify();
+#endif
+
+#ifdef DEBUG
+ virtual void Print();
+ void ReportStatistics();
+ void CollectCodeStatistics();
+#endif
+ // Checks whether an address is in the object area in this space. It
+ // iterates all objects in the space. May be slow.
+ bool SlowContains(Address addr) { return !FindObject(addr)->IsFailure(); }
+
+ private:
+ intptr_t max_capacity_;
+ // The head of the linked list of large object chunks.
+ LargePage* first_page_;
+ intptr_t size_; // allocated bytes
+ int page_count_; // number of chunks
+ intptr_t objects_size_; // size of objects
+ // Map MemoryChunk::kAlignment-aligned chunks to large pages covering them
+ HashMap chunk_map_;
+
+ friend class LargeObjectIterator;
+
+ public:
+ TRACK_MEMORY("LargeObjectSpace")
+};
+
+
+class LargeObjectIterator: public ObjectIterator {
+ public:
+ explicit LargeObjectIterator(LargeObjectSpace* space);
+ LargeObjectIterator(LargeObjectSpace* space, HeapObjectCallback size_func);
+
+ HeapObject* Next();
+
+ // implementation of ObjectIterator.
+ virtual HeapObject* next_object() { return Next(); }
+
+ private:
+ 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;
+ int size;
+ int count;
+ void Clear() {
+ comment = NULL;
+ size = 0;
+ count = 0;
+ }
+ // Must be small, since an iteration is used for lookup.
+ static const int kMaxComments = 64;
+};
+#endif
+
+
+} } // namespace v8::internal
+
+#endif // V8_SPACES_H_
diff --git a/chromium/v8/src/splay-tree-inl.h b/chromium/v8/src/splay-tree-inl.h
new file mode 100644
index 00000000000..42024756e9b
--- /dev/null
+++ b/chromium/v8/src/splay-tree-inl.h
@@ -0,0 +1,318 @@
+// 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.
+
+#ifndef V8_SPLAY_TREE_INL_H_
+#define V8_SPLAY_TREE_INL_H_
+
+#include "splay-tree.h"
+
+namespace v8 {
+namespace internal {
+
+
+template<typename Config, class Allocator>
+SplayTree<Config, Allocator>::~SplayTree() {
+ NodeDeleter deleter;
+ ForEachNode(&deleter);
+}
+
+
+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(allocator_) 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.
+ Splay(key);
+ // Ignore repeated insertions with the same key.
+ int cmp = Config::Compare(key, root_->key_);
+ if (cmp == 0) {
+ locator->bind(root_);
+ return false;
+ }
+ // Insert the new node.
+ Node* node = new(allocator_) Node(key, Config::NoValue());
+ InsertInternal(cmp, node);
+ }
+ locator->bind(root_);
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+void SplayTree<Config, Allocator>::InsertInternal(int cmp, Node* node) {
+ if (cmp > 0) {
+ node->left_ = root_;
+ node->right_ = root_->right_;
+ root_->right_ = NULL;
+ } else {
+ node->right_ = root_;
+ node->left_ = root_->left_;
+ root_->left_ = NULL;
+ }
+ root_ = node;
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindInternal(const Key& key) {
+ if (is_empty())
+ return false;
+ Splay(key);
+ return Config::Compare(key, root_->key_) == 0;
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::Contains(const Key& key) {
+ return FindInternal(key);
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::Find(const Key& key, Locator* locator) {
+ if (FindInternal(key)) {
+ locator->bind(root_);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindGreatestLessThan(const Key& key,
+ Locator* locator) {
+ if (is_empty())
+ return false;
+ // 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.
+ Splay(key);
+ // Now the result is either the root node or the greatest node in
+ // the left subtree.
+ int cmp = Config::Compare(root_->key_, key);
+ if (cmp <= 0) {
+ locator->bind(root_);
+ return true;
+ } else {
+ Node* temp = root_;
+ root_ = root_->left_;
+ bool result = FindGreatest(locator);
+ root_ = temp;
+ return result;
+ }
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindLeastGreaterThan(const Key& key,
+ Locator* locator) {
+ if (is_empty())
+ return false;
+ // 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.
+ Splay(key);
+ // Now the result is either the root node or the least node in
+ // the right subtree.
+ int cmp = Config::Compare(root_->key_, key);
+ if (cmp >= 0) {
+ locator->bind(root_);
+ return true;
+ } else {
+ Node* temp = root_;
+ root_ = root_->right_;
+ bool result = FindLeast(locator);
+ root_ = temp;
+ return result;
+ }
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindGreatest(Locator* locator) {
+ if (is_empty())
+ return false;
+ Node* current = root_;
+ while (current->right_ != NULL)
+ current = current->right_;
+ locator->bind(current);
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindLeast(Locator* locator) {
+ if (is_empty())
+ return false;
+ Node* current = root_;
+ while (current->left_ != NULL)
+ current = current->left_;
+ locator->bind(current);
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::Move(const Key& old_key,
+ const Key& new_key) {
+ if (!FindInternal(old_key))
+ return false;
+ Node* node_to_move = root_;
+ RemoveRootNode(old_key);
+ Splay(new_key);
+ int cmp = Config::Compare(new_key, root_->key_);
+ if (cmp == 0) {
+ // A node with the target key already exists.
+ delete node_to_move;
+ return false;
+ }
+ node_to_move->key_ = new_key;
+ InsertInternal(cmp, node_to_move);
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::Remove(const Key& key) {
+ if (!FindInternal(key))
+ return false;
+ Node* node_to_remove = root_;
+ RemoveRootNode(key);
+ delete node_to_remove;
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+void SplayTree<Config, Allocator>::RemoveRootNode(const Key& key) {
+ if (root_->left_ == NULL) {
+ // No left child, so the new tree is just the right child.
+ root_ = root_->right_;
+ } else {
+ // Left child exists.
+ Node* right = root_->right_;
+ // Make the original left child the new root.
+ root_ = root_->left_;
+ // Splay to make sure that the new root has an empty right child.
+ Splay(key);
+ // Insert the original right child as the right child of the new
+ // root.
+ root_->right_ = right;
+ }
+}
+
+
+template<typename Config, class Allocator>
+void SplayTree<Config, Allocator>::Splay(const Key& key) {
+ if (is_empty())
+ return;
+ 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
+ // will hold the R tree of the algorithm. Using a dummy node, left
+ // and right will always be nodes and we avoid special cases.
+ Node* dummy = &dummy_node;
+ Node* left = dummy;
+ Node* right = dummy;
+ Node* current = root_;
+ while (true) {
+ int cmp = Config::Compare(key, current->key_);
+ if (cmp < 0) {
+ if (current->left_ == NULL)
+ break;
+ if (Config::Compare(key, current->left_->key_) < 0) {
+ // Rotate right.
+ Node* temp = current->left_;
+ current->left_ = temp->right_;
+ temp->right_ = current;
+ current = temp;
+ if (current->left_ == NULL)
+ break;
+ }
+ // Link right.
+ right->left_ = current;
+ right = current;
+ current = current->left_;
+ } else if (cmp > 0) {
+ if (current->right_ == NULL)
+ break;
+ if (Config::Compare(key, current->right_->key_) > 0) {
+ // Rotate left.
+ Node* temp = current->right_;
+ current->right_ = temp->left_;
+ temp->left_ = current;
+ current = temp;
+ if (current->right_ == NULL)
+ 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_;
+ root_ = current;
+}
+
+
+template <typename Config, class Allocator> template <class Callback>
+void SplayTree<Config, Allocator>::ForEach(Callback* callback) {
+ NodeToPairAdaptor<Callback> callback_adaptor(callback);
+ ForEachNode(&callback_adaptor);
+}
+
+
+template <typename Config, class Allocator> template <class Callback>
+void SplayTree<Config, Allocator>::ForEachNode(Callback* callback) {
+ if (root_ == NULL) return;
+ // Pre-allocate some space for tiny trees.
+ List<Node*, Allocator> nodes_to_visit(10, allocator_);
+ nodes_to_visit.Add(root_, allocator_);
+ int pos = 0;
+ while (pos < nodes_to_visit.length()) {
+ Node* node = nodes_to_visit[pos++];
+ if (node->left() != NULL) nodes_to_visit.Add(node->left(), allocator_);
+ if (node->right() != NULL) nodes_to_visit.Add(node->right(), allocator_);
+ callback->Call(node);
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_SPLAY_TREE_INL_H_
diff --git a/chromium/v8/src/splay-tree.h b/chromium/v8/src/splay-tree.h
new file mode 100644
index 00000000000..f393027a82c
--- /dev/null
+++ b/chromium/v8/src/splay-tree.h
@@ -0,0 +1,226 @@
+// 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.
+
+#ifndef V8_SPLAY_TREE_H_
+#define V8_SPLAY_TREE_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+
+// A splay tree. The config type parameter encapsulates the different
+// configurations of a concrete splay tree:
+//
+// typedef Key: the key type
+// typedef Value: the value type
+// static const Key kNoKey: the dummy key used when no key is set
+// static Value kNoValue(): the dummy value used to initialize nodes
+// static int (Compare)(Key& a, Key& b) -> {-1, 0, 1}: comparison function
+//
+// The tree is also parameterized by an allocation policy
+// (Allocator). The policy is used for allocating lists in the C free
+// store or the zone; see zone.h.
+
+// Forward defined as
+// template <typename Config, class Allocator = FreeStoreAllocationPolicy>
+// class SplayTree;
+template <typename Config, class AllocationPolicy>
+class SplayTree {
+ public:
+ typedef typename Config::Key Key;
+ typedef typename Config::Value Value;
+
+ class Locator;
+
+ SplayTree(AllocationPolicy allocator = AllocationPolicy())
+ : root_(NULL), allocator_(allocator) { }
+ ~SplayTree();
+
+ INLINE(void* operator new(size_t size,
+ AllocationPolicy allocator = AllocationPolicy())) {
+ return allocator.New(static_cast<int>(size));
+ }
+ INLINE(void operator delete(void* p)) {
+ AllocationPolicy::Delete(p);
+ }
+ // Please the MSVC compiler. We should never have to execute this.
+ INLINE(void operator delete(void* p, AllocationPolicy policy)) {
+ UNREACHABLE();
+ }
+
+ AllocationPolicy allocator() { return allocator_; }
+
+ // Checks if there is a mapping for the key.
+ bool Contains(const Key& key);
+
+ // Inserts the given key in this tree with the given value. Returns
+ // true if a node was inserted, otherwise false. If found the locator
+ // is enabled and provides access to the mapping for the key.
+ bool Insert(const Key& key, Locator* locator);
+
+ // Looks up the key in this tree and returns true if it was found,
+ // otherwise false. If the node is found the locator is enabled and
+ // provides access to the mapping for the key.
+ bool Find(const Key& key, Locator* locator);
+
+ // Finds the mapping with the greatest key less than or equal to the
+ // given key.
+ bool FindGreatestLessThan(const Key& key, Locator* locator);
+
+ // Find the mapping with the greatest key in this tree.
+ bool FindGreatest(Locator* locator);
+
+ // Finds the mapping with the least key greater than or equal to the
+ // given key.
+ bool FindLeastGreaterThan(const Key& key, Locator* locator);
+
+ // Find the mapping with the least key in this tree.
+ bool FindLeast(Locator* locator);
+
+ // Move the node from one key to another.
+ bool Move(const Key& old_key, const Key& new_key);
+
+ // Remove the node with the given key from the tree.
+ bool Remove(const Key& key);
+
+ // Remove all keys from the tree.
+ void Clear() { ResetRoot(); }
+
+ bool is_empty() { return root_ == NULL; }
+
+ // 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.
+ void Splay(const Key& key);
+
+ class Node {
+ public:
+ Node(const Key& key, const Value& value)
+ : key_(key),
+ value_(value),
+ left_(NULL),
+ right_(NULL) { }
+
+ INLINE(void* operator new(size_t size, AllocationPolicy allocator)) {
+ return allocator.New(static_cast<int>(size));
+ }
+ INLINE(void operator delete(void* p)) {
+ return AllocationPolicy::Delete(p);
+ }
+ // Please the MSVC compiler. We should never have to execute
+ // this.
+ INLINE(void operator delete(void* p, AllocationPolicy allocator)) {
+ UNREACHABLE();
+ }
+
+ Key key() { return key_; }
+ Value value() { return value_; }
+ Node* left() { return left_; }
+ Node* right() { return right_; }
+
+ private:
+ friend class SplayTree;
+ friend class Locator;
+ Key key_;
+ Value value_;
+ Node* left_;
+ Node* right_;
+ };
+
+ // A locator provides access to a node in the tree without actually
+ // exposing the node.
+ class Locator BASE_EMBEDDED {
+ public:
+ explicit Locator(Node* node) : node_(node) { }
+ Locator() : node_(NULL) { }
+ const Key& key() { return node_->key_; }
+ Value& value() { return node_->value_; }
+ void set_value(const Value& value) { node_->value_ = value; }
+ inline void bind(Node* node) { node_ = node; }
+
+ private:
+ Node* node_;
+ };
+
+ template <class Callback>
+ void ForEach(Callback* callback);
+
+ protected:
+ // Resets tree root. Existing nodes become unreachable.
+ void ResetRoot() { root_ = NULL; }
+
+ private:
+ // Search for a node with a given key. If found, root_ points
+ // to the node.
+ bool FindInternal(const Key& key);
+
+ // Inserts a node assuming that root_ is already set up.
+ void InsertInternal(int cmp, Node* node);
+
+ // Removes root_ node.
+ void RemoveRootNode(const Key& key);
+
+ template<class Callback>
+ class NodeToPairAdaptor BASE_EMBEDDED {
+ public:
+ explicit NodeToPairAdaptor(Callback* callback)
+ : callback_(callback) { }
+ void Call(Node* node) {
+ callback_->Call(node->key(), node->value());
+ }
+
+ private:
+ Callback* callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(NodeToPairAdaptor);
+ };
+
+ class NodeDeleter BASE_EMBEDDED {
+ public:
+ NodeDeleter() { }
+ void Call(Node* node) { AllocationPolicy::Delete(node); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NodeDeleter);
+ };
+
+ template <class Callback>
+ void ForEachNode(Callback* callback);
+
+ Node* root_;
+ AllocationPolicy allocator_;
+
+ DISALLOW_COPY_AND_ASSIGN(SplayTree);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_SPLAY_TREE_H_
diff --git a/chromium/v8/src/store-buffer-inl.h b/chromium/v8/src/store-buffer-inl.h
new file mode 100644
index 00000000000..bb386dbacf9
--- /dev/null
+++ b/chromium/v8/src/store-buffer-inl.h
@@ -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.
+
+#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);
+ }
+ }
+}
+
+
+void StoreBuffer::ClearDeadObject(HeapObject* object) {
+ Address& map_field = Memory::Address_at(object->address());
+ if (heap_->map_space()->Contains(map_field)) {
+ map_field = NULL;
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_STORE_BUFFER_INL_H_
diff --git a/chromium/v8/src/store-buffer.cc b/chromium/v8/src/store-buffer.cc
new file mode 100644
index 00000000000..9705b604898
--- /dev/null
+++ b/chromium/v8/src/store-buffer.cc
@@ -0,0 +1,727 @@
+// 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 "store-buffer.h"
+
+#include <algorithm>
+
+#include "v8.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();
+ isolate->counters()->store_buffer_overflows()->Increment();
+}
+
+
+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;
+}
+
+
+bool StoreBuffer::SpaceAvailable(intptr_t space_needed) {
+ return old_limit_ - old_top_ >= space_needed;
+}
+
+
+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 (SpaceAvailable(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 (SpaceAvailable(space_needed)) 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 = 0; i < kSampleFinenesses; 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 != (kSampleFinenesses - 1) || old_top_ == old_start_);
+ if (SpaceAvailable(space_needed)) 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;
+ std::sort(old_start_, old_top_);
+ 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.
+ EnsureSpace(kStoreBufferSize / 2);
+}
+
+
+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 VERIFY_HEAP
+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,
+ false);
+ }
+}
+
+
+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 VERIFY_HEAP
+ VerifyPointers(heap_->old_pointer_space(),
+ &StoreBuffer::FindPointersToNewSpaceInRegion);
+ VerifyPointers(heap_->map_space(),
+ &StoreBuffer::FindPointersToNewSpaceInMapsRegion);
+ VerifyPointers(heap_->lo_space());
+#endif
+}
+
+
+void StoreBuffer::GCEpilogue() {
+ during_gc_ = false;
+#ifdef VERIFY_HEAP
+ if (FLAG_verify_heap) {
+ Verify();
+ }
+#endif
+}
+
+
+void StoreBuffer::FindPointersToNewSpaceInRegion(
+ Address start,
+ Address end,
+ ObjectSlotCallback slot_callback,
+ bool clear_maps) {
+ 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());
+ // The new space object was not promoted if it still contains a map
+ // pointer. Clear the map field now lazily.
+ if (clear_maps) ClearDeadObject(object);
+ 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)->area_start();
+ 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)->area_start();
+ return page + ((addr - page) / Map::kSize * Map::kSize);
+}
+
+
+void StoreBuffer::FindPointersToNewSpaceInMaps(
+ Address start,
+ Address end,
+ ObjectSlotCallback slot_callback,
+ bool clear_maps) {
+ 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,
+ clear_maps);
+ map_address += Map::kSize;
+ }
+}
+
+
+void StoreBuffer::FindPointersToNewSpaceInMapsRegion(
+ Address start,
+ Address end,
+ ObjectSlotCallback slot_callback,
+ bool clear_maps) {
+ 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,
+ clear_maps);
+}
+
+
+// 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,
+ bool clear_maps) {
+ Address visitable_start = page->area_start();
+ Address end_of_page = page->area_end();
+
+ 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,
+ clear_maps);
+ 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,
+ clear_maps);
+ }
+}
+
+
+void StoreBuffer::IteratePointersInStoreBuffer(
+ ObjectSlotCallback slot_callback,
+ bool clear_maps) {
+ 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);
+ // The new space object was not promoted if it still contains a map
+ // pointer. Clear the map field now lazily.
+ if (clear_maps) ClearDeadObject(heap_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) {
+ IteratePointersToNewSpace(slot_callback, false);
+}
+
+
+void StoreBuffer::IteratePointersToNewSpaceAndClearMaps(
+ ObjectSlotCallback slot_callback) {
+ IteratePointersToNewSpace(slot_callback, true);
+}
+
+
+void StoreBuffer::IteratePointersToNewSpace(ObjectSlotCallback slot_callback,
+ bool clear_maps) {
+ // 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, clear_maps);
+
+ // 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, clear_maps);
+ } 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,
+ clear_maps);
+ }
+ }
+ }
+ 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;
+ // The upper part of an address is basically random because of ASLR and OS
+ // non-determinism, so we use only the bits within a page for hashing to
+ // make v8's behavior (more) deterministic.
+ uintptr_t hash_addr =
+ int_addr & (Page::kPageAlignmentMask >> kPointerSizeLog2);
+ int hash1 = ((hash_addr ^ (hash_addr >> kHashSetLengthLog2)) &
+ (kHashSetLength - 1));
+ if (hash_set_1_[hash1] == int_addr) continue;
+ uintptr_t hash2 = (hash_addr - (hash_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();
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/store-buffer.h b/chromium/v8/src/store-buffer.h
new file mode 100644
index 00000000000..01e7cbeb8d2
--- /dev/null
+++ b/chromium/v8/src/store-buffer.h
@@ -0,0 +1,270 @@
+// 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 Page;
+class PagedSpace;
+class StoreBuffer;
+
+typedef void (*ObjectSlotCallback)(HeapObject** from, HeapObject* to);
+
+typedef void (StoreBuffer::*RegionCallback)(Address start,
+ Address end,
+ ObjectSlotCallback slot_callback,
+ bool clear_maps);
+
+// 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);
+
+ // Same as IteratePointersToNewSpace but additonally clears maps in objects
+ // referenced from the store buffer that do not contain a forwarding pointer.
+ void IteratePointersToNewSpaceAndClearMaps(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();
+
+ bool SpaceAvailable(intptr_t space_needed);
+ void Uniq();
+ void ExemptPopularPages(int prime_sample_step, int threshold);
+
+ // Set the map field of the object to NULL if contains a map.
+ inline void ClearDeadObject(HeapObject *object);
+
+ void IteratePointersToNewSpace(ObjectSlotCallback callback, bool clear_maps);
+
+ void FindPointersToNewSpaceInRegion(Address start,
+ Address end,
+ ObjectSlotCallback slot_callback,
+ bool clear_maps);
+
+ // 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,
+ bool clear_maps);
+
+ void FindPointersToNewSpaceInMapsRegion(
+ Address start,
+ Address end,
+ ObjectSlotCallback slot_callback,
+ bool clear_maps);
+
+ void FindPointersToNewSpaceOnPage(
+ PagedSpace* space,
+ Page* page,
+ RegionCallback region_callback,
+ ObjectSlotCallback slot_callback,
+ bool clear_maps);
+
+ void IteratePointersInStoreBuffer(ObjectSlotCallback slot_callback,
+ bool clear_maps);
+
+#ifdef VERIFY_HEAP
+ 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)
+ : 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_;
+ }
+
+ private:
+ 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/chromium/v8/src/string-search.cc b/chromium/v8/src/string-search.cc
new file mode 100644
index 00000000000..3ae68b5d401
--- /dev/null
+++ b/chromium/v8/src/string-search.cc
@@ -0,0 +1,41 @@
+// 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.
+
+#include "v8.h"
+#include "string-search.h"
+
+namespace v8 {
+namespace internal {
+
+// Storage for constants used by string-search.
+
+// Now in Isolate:
+// bad_char_shift_table()
+// good_suffix_shift_table()
+// suffix_table()
+
+}} // namespace v8::internal
diff --git a/chromium/v8/src/string-search.h b/chromium/v8/src/string-search.h
new file mode 100644
index 00000000000..bc685ffe584
--- /dev/null
+++ b/chromium/v8/src/string-search.h
@@ -0,0 +1,580 @@
+// 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_STRING_SEARCH_H_
+#define V8_STRING_SEARCH_H_
+
+namespace v8 {
+namespace internal {
+
+
+//---------------------------------------------------------------------
+// String Search object.
+//---------------------------------------------------------------------
+
+// Class holding constants and methods that apply to all string search variants,
+// independently of subject and pattern char size.
+class StringSearchBase {
+ protected:
+ // Cap on the maximal shift in the Boyer-Moore implementation. By setting a
+ // limit, we can fix the size of tables. For a needle longer than this limit,
+ // search will not be optimal, since we only build tables for a suffix
+ // of the string, but it is a safe approximation.
+ static const int kBMMaxShift = Isolate::kBMMaxShift;
+
+ // Reduce alphabet to this size.
+ // One of the tables used by Boyer-Moore and Boyer-Moore-Horspool has size
+ // proportional to the input alphabet. We reduce the alphabet size by
+ // equating input characters modulo a smaller alphabet size. This gives
+ // a potentially less efficient searching, but is a safe approximation.
+ // For needles using only characters in the same Unicode 256-code point page,
+ // there is no search speed degradation.
+ static const int kAsciiAlphabetSize = 256;
+ static const int kUC16AlphabetSize = Isolate::kUC16AlphabetSize;
+
+ // Bad-char shift table stored in the state. It's length is the alphabet size.
+ // For patterns below this length, the skip length of Boyer-Moore is too short
+ // to compensate for the algorithmic overhead compared to simple brute force.
+ static const int kBMMinPatternLength = 7;
+
+ static inline bool IsOneByteString(Vector<const uint8_t> string) {
+ return true;
+ }
+
+ static inline bool IsOneByteString(Vector<const uc16> string) {
+ return String::IsOneByte(string.start(), string.length());
+ }
+
+ friend class Isolate;
+};
+
+
+template <typename PatternChar, typename SubjectChar>
+class StringSearch : private StringSearchBase {
+ public:
+ StringSearch(Isolate* isolate, Vector<const PatternChar> pattern)
+ : isolate_(isolate),
+ pattern_(pattern),
+ start_(Max(0, pattern.length() - kBMMaxShift)) {
+ if (sizeof(PatternChar) > sizeof(SubjectChar)) {
+ if (!IsOneByteString(pattern_)) {
+ strategy_ = &FailSearch;
+ return;
+ }
+ }
+ int pattern_length = pattern_.length();
+ if (pattern_length < kBMMinPatternLength) {
+ if (pattern_length == 1) {
+ strategy_ = &SingleCharSearch;
+ return;
+ }
+ strategy_ = &LinearSearch;
+ return;
+ }
+ strategy_ = &InitialSearch;
+ }
+
+ int Search(Vector<const SubjectChar> subject, int index) {
+ return strategy_(this, subject, index);
+ }
+
+ static inline int AlphabetSize() {
+ if (sizeof(PatternChar) == 1) {
+ // ASCII needle.
+ return kAsciiAlphabetSize;
+ } else {
+ ASSERT(sizeof(PatternChar) == 2);
+ // UC16 needle.
+ return kUC16AlphabetSize;
+ }
+ }
+
+ private:
+ typedef int (*SearchFunction)( // NOLINT - it's not a cast!
+ StringSearch<PatternChar, SubjectChar>*,
+ Vector<const SubjectChar>,
+ int);
+
+ static int FailSearch(StringSearch<PatternChar, SubjectChar>*,
+ Vector<const SubjectChar>,
+ int) {
+ return -1;
+ }
+
+ static int SingleCharSearch(StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int start_index);
+
+ static int LinearSearch(StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int start_index);
+
+ static int InitialSearch(StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int start_index);
+
+ static int BoyerMooreHorspoolSearch(
+ StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int start_index);
+
+ static int BoyerMooreSearch(StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int start_index);
+
+ void PopulateBoyerMooreHorspoolTable();
+
+ void PopulateBoyerMooreTable();
+
+ static inline bool exceedsOneByte(uint8_t c) {
+ return false;
+ }
+
+ static inline bool exceedsOneByte(uint16_t c) {
+ return c > String::kMaxOneByteCharCodeU;
+ }
+
+ static inline int CharOccurrence(int* bad_char_occurrence,
+ SubjectChar char_code) {
+ if (sizeof(SubjectChar) == 1) {
+ return bad_char_occurrence[static_cast<int>(char_code)];
+ }
+ if (sizeof(PatternChar) == 1) {
+ if (exceedsOneByte(char_code)) {
+ return -1;
+ }
+ return bad_char_occurrence[static_cast<unsigned int>(char_code)];
+ }
+ // Both pattern and subject are UC16. Reduce character to equivalence class.
+ int equiv_class = char_code % kUC16AlphabetSize;
+ return bad_char_occurrence[equiv_class];
+ }
+
+ // The following tables are shared by all searches.
+ // TODO(lrn): Introduce a way for a pattern to keep its tables
+ // between searches (e.g., for an Atom RegExp).
+
+ // Store for the BoyerMoore(Horspool) bad char shift table.
+ // Return a table covering the last kBMMaxShift+1 positions of
+ // pattern.
+ int* bad_char_table() {
+ return isolate_->bad_char_shift_table();
+ }
+
+ // Store for the BoyerMoore good suffix shift table.
+ int* good_suffix_shift_table() {
+ // Return biased pointer that maps the range [start_..pattern_.length()
+ // to the kGoodSuffixShiftTable array.
+ return isolate_->good_suffix_shift_table() - start_;
+ }
+
+ // Table used temporarily while building the BoyerMoore good suffix
+ // shift table.
+ int* suffix_table() {
+ // Return biased pointer that maps the range [start_..pattern_.length()
+ // to the kSuffixTable array.
+ return isolate_->suffix_table() - start_;
+ }
+
+ Isolate* isolate_;
+ // The pattern to search for.
+ Vector<const PatternChar> pattern_;
+ // Pointer to implementation of the search.
+ SearchFunction strategy_;
+ // Cache value of Max(0, pattern_length() - kBMMaxShift)
+ int start_;
+};
+
+
+//---------------------------------------------------------------------
+// Single Character Pattern Search Strategy
+//---------------------------------------------------------------------
+
+template <typename PatternChar, typename SubjectChar>
+int StringSearch<PatternChar, SubjectChar>::SingleCharSearch(
+ StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int index) {
+ ASSERT_EQ(1, search->pattern_.length());
+ PatternChar pattern_first_char = search->pattern_[0];
+ int i = index;
+ if (sizeof(SubjectChar) == 1 && sizeof(PatternChar) == 1) {
+ const SubjectChar* pos = reinterpret_cast<const SubjectChar*>(
+ memchr(subject.start() + i,
+ pattern_first_char,
+ subject.length() - i));
+ if (pos == NULL) return -1;
+ return static_cast<int>(pos - subject.start());
+ } else {
+ if (sizeof(PatternChar) > sizeof(SubjectChar)) {
+ if (exceedsOneByte(pattern_first_char)) {
+ return -1;
+ }
+ }
+ SubjectChar search_char = static_cast<SubjectChar>(pattern_first_char);
+ int n = subject.length();
+ while (i < n) {
+ if (subject[i++] == search_char) return i - 1;
+ }
+ return -1;
+ }
+}
+
+//---------------------------------------------------------------------
+// Linear Search Strategy
+//---------------------------------------------------------------------
+
+
+template <typename PatternChar, typename SubjectChar>
+inline bool CharCompare(const PatternChar* pattern,
+ const SubjectChar* subject,
+ int length) {
+ ASSERT(length > 0);
+ int pos = 0;
+ do {
+ if (pattern[pos] != subject[pos]) {
+ return false;
+ }
+ pos++;
+ } while (pos < length);
+ return true;
+}
+
+
+// Simple linear search for short patterns. Never bails out.
+template <typename PatternChar, typename SubjectChar>
+int StringSearch<PatternChar, SubjectChar>::LinearSearch(
+ StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int index) {
+ Vector<const PatternChar> pattern = search->pattern_;
+ ASSERT(pattern.length() > 1);
+ int pattern_length = pattern.length();
+ PatternChar pattern_first_char = pattern[0];
+ int i = index;
+ int n = subject.length() - pattern_length;
+ while (i <= n) {
+ if (sizeof(SubjectChar) == 1 && sizeof(PatternChar) == 1) {
+ const SubjectChar* pos = reinterpret_cast<const SubjectChar*>(
+ memchr(subject.start() + i,
+ pattern_first_char,
+ n - i + 1));
+ if (pos == NULL) return -1;
+ i = static_cast<int>(pos - subject.start()) + 1;
+ } else {
+ if (subject[i++] != pattern_first_char) continue;
+ }
+ // Loop extracted to separate function to allow using return to do
+ // a deeper break.
+ if (CharCompare(pattern.start() + 1,
+ subject.start() + i,
+ pattern_length - 1)) {
+ return i - 1;
+ }
+ }
+ return -1;
+}
+
+//---------------------------------------------------------------------
+// Boyer-Moore string search
+//---------------------------------------------------------------------
+
+template <typename PatternChar, typename SubjectChar>
+int StringSearch<PatternChar, SubjectChar>::BoyerMooreSearch(
+ StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int start_index) {
+ Vector<const PatternChar> pattern = search->pattern_;
+ int subject_length = subject.length();
+ int pattern_length = pattern.length();
+ // Only preprocess at most kBMMaxShift last characters of pattern.
+ int start = search->start_;
+
+ int* bad_char_occurence = search->bad_char_table();
+ int* good_suffix_shift = search->good_suffix_shift_table();
+
+ PatternChar last_char = pattern[pattern_length - 1];
+ int index = start_index;
+ // Continue search from i.
+ while (index <= subject_length - pattern_length) {
+ int j = pattern_length - 1;
+ int c;
+ while (last_char != (c = subject[index + j])) {
+ int shift =
+ j - CharOccurrence(bad_char_occurence, c);
+ index += shift;
+ if (index > subject_length - pattern_length) {
+ return -1;
+ }
+ }
+ while (j >= 0 && pattern[j] == (c = subject[index + j])) j--;
+ if (j < 0) {
+ return index;
+ } else if (j < start) {
+ // we have matched more than our tables allow us to be smart about.
+ // Fall back on BMH shift.
+ index += pattern_length - 1
+ - CharOccurrence(bad_char_occurence,
+ static_cast<SubjectChar>(last_char));
+ } else {
+ int gs_shift = good_suffix_shift[j + 1];
+ int bc_occ =
+ CharOccurrence(bad_char_occurence, c);
+ int shift = j - bc_occ;
+ if (gs_shift > shift) {
+ shift = gs_shift;
+ }
+ index += shift;
+ }
+ }
+
+ return -1;
+}
+
+
+template <typename PatternChar, typename SubjectChar>
+void StringSearch<PatternChar, SubjectChar>::PopulateBoyerMooreTable() {
+ int pattern_length = pattern_.length();
+ const PatternChar* pattern = pattern_.start();
+ // Only look at the last kBMMaxShift characters of pattern (from start_
+ // to pattern_length).
+ int start = start_;
+ int length = pattern_length - start;
+
+ // Biased tables so that we can use pattern indices as table indices,
+ // even if we only cover the part of the pattern from offset start.
+ int* shift_table = good_suffix_shift_table();
+ int* suffix_table = this->suffix_table();
+
+ // Initialize table.
+ for (int i = start; i < pattern_length; i++) {
+ shift_table[i] = length;
+ }
+ 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;
+ {
+ int i = pattern_length;
+ while (i > start) {
+ PatternChar c = pattern[i - 1];
+ while (suffix <= pattern_length && c != pattern[suffix - 1]) {
+ if (shift_table[suffix] == length) {
+ shift_table[suffix] = suffix - i;
+ }
+ suffix = suffix_table[suffix];
+ }
+ suffix_table[--i] = --suffix;
+ if (suffix == pattern_length) {
+ // No suffix to extend, so we check against last_char only.
+ while ((i > start) && (pattern[i - 1] != last_char)) {
+ if (shift_table[pattern_length] == length) {
+ shift_table[pattern_length] = pattern_length - i;
+ }
+ suffix_table[--i] = pattern_length;
+ }
+ if (i > start) {
+ suffix_table[--i] = --suffix;
+ }
+ }
+ }
+ }
+ // Build shift table using suffixes.
+ if (suffix < pattern_length) {
+ for (int i = start; i <= pattern_length; i++) {
+ if (shift_table[i] == length) {
+ shift_table[i] = suffix - start;
+ }
+ if (i == suffix) {
+ suffix = suffix_table[suffix];
+ }
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Boyer-Moore-Horspool string search.
+//---------------------------------------------------------------------
+
+template <typename PatternChar, typename SubjectChar>
+int StringSearch<PatternChar, SubjectChar>::BoyerMooreHorspoolSearch(
+ StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int start_index) {
+ Vector<const PatternChar> pattern = search->pattern_;
+ int subject_length = subject.length();
+ int pattern_length = pattern.length();
+ int* char_occurrences = search->bad_char_table();
+ int badness = -pattern_length;
+
+ // How bad we are doing without a good-suffix table.
+ PatternChar last_char = pattern[pattern_length - 1];
+ int last_char_shift = pattern_length - 1 -
+ CharOccurrence(char_occurrences, static_cast<SubjectChar>(last_char));
+ // Perform search
+ int index = start_index; // No matches found prior to this index.
+ while (index <= subject_length - pattern_length) {
+ int j = pattern_length - 1;
+ int subject_char;
+ while (last_char != (subject_char = subject[index + j])) {
+ int bc_occ = CharOccurrence(char_occurrences, subject_char);
+ int shift = j - bc_occ;
+ index += shift;
+ badness += 1 - shift; // at most zero, so badness cannot increase.
+ if (index > subject_length - pattern_length) {
+ return -1;
+ }
+ }
+ j--;
+ while (j >= 0 && pattern[j] == (subject[index + j])) j--;
+ if (j < 0) {
+ return index;
+ } else {
+ index += last_char_shift;
+ // Badness increases by the number of characters we have
+ // checked, and decreases by the number of characters we
+ // can skip by shifting. It's a measure of how we are doing
+ // compared to reading each character exactly once.
+ badness += (pattern_length - j) - last_char_shift;
+ if (badness > 0) {
+ search->PopulateBoyerMooreTable();
+ search->strategy_ = &BoyerMooreSearch;
+ return BoyerMooreSearch(search, subject, index);
+ }
+ }
+ }
+ return -1;
+}
+
+
+template <typename PatternChar, typename SubjectChar>
+void StringSearch<PatternChar, SubjectChar>::PopulateBoyerMooreHorspoolTable() {
+ int pattern_length = pattern_.length();
+
+ int* bad_char_occurrence = bad_char_table();
+
+ // Only preprocess at most kBMMaxShift last characters of pattern.
+ int start = start_;
+ // Run forwards to populate bad_char_table, so that *last* instance
+ // of character equivalence class is the one registered.
+ // Notice: Doesn't include the last character.
+ int table_size = AlphabetSize();
+ if (start == 0) { // All patterns less than kBMMaxShift in length.
+ memset(bad_char_occurrence,
+ -1,
+ table_size * sizeof(*bad_char_occurrence));
+ } else {
+ for (int i = 0; i < table_size; i++) {
+ bad_char_occurrence[i] = start - 1;
+ }
+ }
+ for (int i = start; i < pattern_length - 1; i++) {
+ PatternChar c = pattern_[i];
+ int bucket = (sizeof(PatternChar) == 1) ? c : c % AlphabetSize();
+ bad_char_occurrence[bucket] = i;
+ }
+}
+
+//---------------------------------------------------------------------
+// Linear string search with bailout to BMH.
+//---------------------------------------------------------------------
+
+// Simple linear search for short patterns, which bails out if the string
+// isn't found very early in the subject. Upgrades to BoyerMooreHorspool.
+template <typename PatternChar, typename SubjectChar>
+int StringSearch<PatternChar, SubjectChar>::InitialSearch(
+ StringSearch<PatternChar, SubjectChar>* search,
+ Vector<const SubjectChar> subject,
+ int index) {
+ Vector<const PatternChar> pattern = search->pattern_;
+ int pattern_length = pattern.length();
+ // Badness is a count of how much work we have done. When we have
+ // done enough work we decide it's probably worth switching to a better
+ // algorithm.
+ int badness = -10 - (pattern_length << 2);
+
+ // We know our pattern is at least 2 characters, we cache the first so
+ // the common case of the first character not matching is faster.
+ PatternChar pattern_first_char = pattern[0];
+ for (int i = index, n = subject.length() - pattern_length; i <= n; i++) {
+ badness++;
+ if (badness <= 0) {
+ if (sizeof(SubjectChar) == 1 && sizeof(PatternChar) == 1) {
+ const SubjectChar* pos = reinterpret_cast<const SubjectChar*>(
+ memchr(subject.start() + i,
+ pattern_first_char,
+ n - i + 1));
+ if (pos == NULL) {
+ return -1;
+ }
+ i = static_cast<int>(pos - subject.start());
+ } else {
+ if (subject[i] != pattern_first_char) continue;
+ }
+ int j = 1;
+ do {
+ if (pattern[j] != subject[i + j]) {
+ break;
+ }
+ j++;
+ } while (j < pattern_length);
+ if (j == pattern_length) {
+ return i;
+ }
+ badness += j;
+ } else {
+ search->PopulateBoyerMooreHorspoolTable();
+ search->strategy_ = &BoyerMooreHorspoolSearch;
+ return BoyerMooreHorspoolSearch(search, subject, i);
+ }
+ }
+ return -1;
+}
+
+
+// Perform a a single stand-alone search.
+// If searching multiple times for the same pattern, a search
+// object should be constructed once and the Search function then called
+// for each search.
+template <typename SubjectChar, typename PatternChar>
+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);
+}
+
+}} // namespace v8::internal
+
+#endif // V8_STRING_SEARCH_H_
diff --git a/chromium/v8/src/string-stream.cc b/chromium/v8/src/string-stream.cc
new file mode 100644
index 00000000000..9c4394ed7f0
--- /dev/null
+++ b/chromium/v8/src/string-stream.cc
@@ -0,0 +1,602 @@
+// Copyright 2012 the V8 project authors. 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 "factory.h"
+#include "string-stream.h"
+
+#include "allocation-inl.h"
+
+namespace v8 {
+namespace internal {
+
+static const int kMentionedObjectCacheMaxSize = 256;
+
+char* HeapStringAllocator::allocate(unsigned bytes) {
+ space_ = NewArray<char>(bytes);
+ return space_;
+}
+
+
+NoAllocationStringAllocator::NoAllocationStringAllocator(char* memory,
+ unsigned size) {
+ size_ = size;
+ space_ = memory;
+}
+
+
+bool StringStream::Put(char c) {
+ if (full()) return false;
+ ASSERT(length_ < capacity_);
+ // Since the trailing '\0' is not accounted for in length_ fullness is
+ // indicated by a difference of 1 between length_ and capacity_. Thus when
+ // reaching a difference of 2 we need to grow the buffer.
+ if (length_ == capacity_ - 2) {
+ unsigned new_capacity = capacity_;
+ char* new_buffer = allocator_->grow(&new_capacity);
+ if (new_capacity > capacity_) {
+ capacity_ = new_capacity;
+ buffer_ = new_buffer;
+ } else {
+ // Reached the end of the available buffer.
+ ASSERT(capacity_ >= 5);
+ length_ = capacity_ - 1; // Indicate fullness of the stream.
+ buffer_[length_ - 4] = '.';
+ buffer_[length_ - 3] = '.';
+ buffer_[length_ - 2] = '.';
+ buffer_[length_ - 1] = '\n';
+ buffer_[length_] = '\0';
+ return false;
+ }
+ }
+ buffer_[length_] = c;
+ buffer_[length_ + 1] = '\0';
+ length_++;
+ return true;
+}
+
+
+// A control character is one that configures a format element. For
+// instance, in %.5s, .5 are control characters.
+static bool IsControlChar(char c) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '.': case '-':
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
+ // If we already ran out of space then return immediately.
+ if (full()) return;
+ int offset = 0;
+ int elm = 0;
+ while (offset < format.length()) {
+ if (format[offset] != '%' || elm == elms.length()) {
+ Put(format[offset]);
+ offset++;
+ continue;
+ }
+ // Read this formatting directive into a temporary buffer
+ EmbeddedVector<char, 24> temp;
+ int format_length = 0;
+ // Skip over the whole control character sequence until the
+ // format element type
+ temp[format_length++] = format[offset++];
+ while (offset < format.length() && IsControlChar(format[offset]))
+ temp[format_length++] = format[offset++];
+ if (offset >= format.length())
+ return;
+ char type = format[offset];
+ temp[format_length++] = type;
+ temp[format_length] = '\0';
+ offset++;
+ FmtElm current = elms[elm++];
+ switch (type) {
+ case 's': {
+ ASSERT_EQ(FmtElm::C_STR, current.type_);
+ const char* value = current.data_.u_c_str_;
+ Add(value);
+ break;
+ }
+ case 'w': {
+ ASSERT_EQ(FmtElm::LC_STR, current.type_);
+ Vector<const uc16> value = *current.data_.u_lc_str_;
+ for (int i = 0; i < value.length(); i++)
+ Put(static_cast<char>(value[i]));
+ break;
+ }
+ case 'o': {
+ ASSERT_EQ(FmtElm::OBJ, current.type_);
+ Object* obj = current.data_.u_obj_;
+ PrintObject(obj);
+ break;
+ }
+ case 'k': {
+ ASSERT_EQ(FmtElm::INT, current.type_);
+ int value = current.data_.u_int_;
+ if (0x20 <= value && value <= 0x7F) {
+ Put(value);
+ } else if (value <= 0xff) {
+ Add("\\x%02x", value);
+ } else {
+ Add("\\u%04x", value);
+ }
+ break;
+ }
+ case 'i': case 'd': case 'u': case 'x': case 'c': case 'X': {
+ int value = current.data_.u_int_;
+ EmbeddedVector<char, 24> formatted;
+ int length = OS::SNPrintF(formatted, temp.start(), value);
+ Add(Vector<const char>(formatted.start(), length));
+ break;
+ }
+ case 'f': case 'g': case 'G': case 'e': case 'E': {
+ double value = current.data_.u_double_;
+ EmbeddedVector<char, 28> formatted;
+ OS::SNPrintF(formatted, temp.start(), value);
+ Add(formatted.start());
+ break;
+ }
+ case 'p': {
+ void* value = current.data_.u_pointer_;
+ EmbeddedVector<char, 20> formatted;
+ OS::SNPrintF(formatted, temp.start(), value);
+ Add(formatted.start());
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ // Verify that the buffer is 0-terminated
+ ASSERT(buffer_[length_] == '\0');
+}
+
+
+void StringStream::PrintObject(Object* o) {
+ o->ShortPrint(this);
+ if (o->IsString()) {
+ if (String::cast(o)->length() <= String::kMaxShortPrintLength) {
+ return;
+ }
+ } else if (o->IsNumber() || o->IsOddball()) {
+ return;
+ }
+ if (o->IsHeapObject()) {
+ DebugObjectCache* debug_object_cache = Isolate::Current()->
+ string_stream_debug_object_cache();
+ for (int i = 0; i < debug_object_cache->length(); i++) {
+ if ((*debug_object_cache)[i] == o) {
+ Add("#%d#", i);
+ return;
+ }
+ }
+ if (debug_object_cache->length() < kMentionedObjectCacheMaxSize) {
+ Add("#%d#", debug_object_cache->length());
+ debug_object_cache->Add(HeapObject::cast(o));
+ } else {
+ Add("@%p", o);
+ }
+ }
+}
+
+
+void StringStream::Add(const char* format) {
+ Add(CStrVector(format));
+}
+
+
+void StringStream::Add(Vector<const char> format) {
+ Add(format, Vector<FmtElm>::empty());
+}
+
+
+void StringStream::Add(const char* format, FmtElm arg0) {
+ const char argc = 1;
+ FmtElm argv[argc] = { arg0 };
+ Add(CStrVector(format), Vector<FmtElm>(argv, argc));
+}
+
+
+void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1) {
+ const char argc = 2;
+ FmtElm argv[argc] = { arg0, arg1 };
+ Add(CStrVector(format), Vector<FmtElm>(argv, argc));
+}
+
+
+void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
+ FmtElm arg2) {
+ const char argc = 3;
+ FmtElm argv[argc] = { arg0, arg1, arg2 };
+ Add(CStrVector(format), Vector<FmtElm>(argv, argc));
+}
+
+
+void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
+ FmtElm arg2, FmtElm arg3) {
+ const char argc = 4;
+ FmtElm argv[argc] = { arg0, arg1, arg2, arg3 };
+ Add(CStrVector(format), Vector<FmtElm>(argv, argc));
+}
+
+
+void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1,
+ FmtElm arg2, FmtElm arg3, FmtElm arg4) {
+ const char argc = 5;
+ FmtElm argv[argc] = { arg0, arg1, arg2, arg3, arg4 };
+ Add(CStrVector(format), Vector<FmtElm>(argv, argc));
+}
+
+
+SmartArrayPointer<const char> StringStream::ToCString() const {
+ char* str = NewArray<char>(length_ + 1);
+ OS::MemCopy(str, buffer_, length_);
+ str[length_] = '\0';
+ return SmartArrayPointer<const char>(str);
+}
+
+
+void StringStream::Log() {
+ LOG(ISOLATE, StringEvent("StackDump", buffer_));
+}
+
+
+void StringStream::OutputToFile(FILE* out) {
+ // Dump the output to stdout, but make sure to break it up into
+ // manageable chunks to avoid losing parts of the output in the OS
+ // printing code. This is a problem on Windows in particular; see
+ // the VPrint() function implementations in platform-win32.cc.
+ unsigned position = 0;
+ for (unsigned next; (next = position + 2048) < length_; position = next) {
+ char save = buffer_[next];
+ buffer_[next] = '\0';
+ internal::PrintF(out, "%s", &buffer_[position]);
+ buffer_[next] = save;
+ }
+ internal::PrintF(out, "%s", &buffer_[position]);
+}
+
+
+Handle<String> StringStream::ToString() {
+ Factory* factory = Isolate::Current()->factory();
+ return factory->NewStringFromUtf8(Vector<const char>(buffer_, length_));
+}
+
+
+void StringStream::ClearMentionedObjectCache() {
+ Isolate* isolate = Isolate::Current();
+ isolate->set_string_stream_current_security_token(NULL);
+ if (isolate->string_stream_debug_object_cache() == NULL) {
+ isolate->set_string_stream_debug_object_cache(
+ new List<HeapObject*, PreallocatedStorageAllocationPolicy>(0));
+ }
+ isolate->string_stream_debug_object_cache()->Clear();
+}
+
+
+#ifdef DEBUG
+bool StringStream::IsMentionedObjectCacheClear() {
+ return (
+ Isolate::Current()->string_stream_debug_object_cache()->length() == 0);
+}
+#endif
+
+
+bool StringStream::Put(String* str) {
+ return Put(str, 0, str->length());
+}
+
+
+bool StringStream::Put(String* str, int start, int end) {
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(str, &op, start);
+ for (int i = start; i < end && stream.HasMore(); i++) {
+ uint16_t c = stream.GetNext();
+ if (c >= 127 || c < 32) {
+ c = '?';
+ }
+ if (!Put(static_cast<char>(c))) {
+ return false; // Output was truncated.
+ }
+ }
+ return true;
+}
+
+
+void StringStream::PrintName(Object* name) {
+ if (name->IsString()) {
+ String* str = String::cast(name);
+ if (str->length() > 0) {
+ Put(str);
+ } else {
+ Add("/* anonymous */");
+ }
+ } else {
+ Add("%o", name);
+ }
+}
+
+
+void StringStream::PrintUsingMap(JSObject* js_object) {
+ Map* map = js_object->map();
+ if (!HEAP->Contains(map) ||
+ !map->IsHeapObject() ||
+ !map->IsMap()) {
+ Add("<Invalid map>\n");
+ return;
+ }
+ int real_size = map->NumberOfOwnDescriptors();
+ DescriptorArray* descs = map->instance_descriptors();
+ for (int i = 0; i < real_size; i++) {
+ PropertyDetails details = descs->GetDetails(i);
+ if (details.type() == 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->RawFastPropertyAt(descs->GetFieldIndex(i));
+ Add("%o\n", value);
+ }
+ }
+ }
+}
+
+
+void StringStream::PrintFixedArray(FixedArray* array, unsigned int limit) {
+ Heap* heap = HEAP;
+ for (unsigned int i = 0; i < 10 && i < limit; i++) {
+ Object* element = array->get(i);
+ if (element != heap->the_hole_value()) {
+ for (int len = 1; len < 18; len++)
+ Put(' ');
+ Add("%d: %o\n", i, array->get(i));
+ }
+ }
+ if (limit >= 10) {
+ Add(" ...\n");
+ }
+}
+
+
+void StringStream::PrintByteArray(ByteArray* byte_array) {
+ unsigned int limit = byte_array->length();
+ for (unsigned int i = 0; i < 10 && i < limit; i++) {
+ byte b = byte_array->get(i);
+ Add(" %d: %3d 0x%02x", i, b, b);
+ if (b >= ' ' && b <= '~') {
+ Add(" '%c'", b);
+ } else if (b == '\n') {
+ Add(" '\n'");
+ } else if (b == '\r') {
+ Add(" '\r'");
+ } else if (b >= 1 && b <= 26) {
+ Add(" ^%c", b + 'A' - 1);
+ }
+ Add("\n");
+ }
+ if (limit >= 10) {
+ Add(" ...\n");
+ }
+}
+
+
+void StringStream::PrintMentionedObjectCache() {
+ DebugObjectCache* debug_object_cache =
+ Isolate::Current()->string_stream_debug_object_cache();
+ Add("==== Key ============================================\n\n");
+ for (int i = 0; i < debug_object_cache->length(); i++) {
+ HeapObject* printee = (*debug_object_cache)[i];
+ Add(" #%d# %p: ", i, printee);
+ printee->ShortPrint(this);
+ Add("\n");
+ if (printee->IsJSObject()) {
+ if (printee->IsJSValue()) {
+ Add(" value(): %o\n", JSValue::cast(printee)->value());
+ }
+ PrintUsingMap(JSObject::cast(printee));
+ if (printee->IsJSArray()) {
+ JSArray* array = JSArray::cast(printee);
+ if (array->HasFastObjectElements()) {
+ unsigned int limit = FixedArray::cast(array->elements())->length();
+ unsigned int length =
+ static_cast<uint32_t>(JSArray::cast(array)->length()->Number());
+ if (length < limit) limit = length;
+ PrintFixedArray(FixedArray::cast(array->elements()), limit);
+ }
+ }
+ } else if (printee->IsByteArray()) {
+ PrintByteArray(ByteArray::cast(printee));
+ } else if (printee->IsFixedArray()) {
+ unsigned int limit = FixedArray::cast(printee)->length();
+ PrintFixedArray(FixedArray::cast(printee), limit);
+ }
+ }
+}
+
+
+void StringStream::PrintSecurityTokenIfChanged(Object* f) {
+ Isolate* isolate = Isolate::Current();
+ Heap* heap = isolate->heap();
+ if (!f->IsHeapObject() || !heap->Contains(HeapObject::cast(f))) {
+ return;
+ }
+ Map* map = HeapObject::cast(f)->map();
+ if (!map->IsHeapObject() ||
+ !heap->Contains(map) ||
+ !map->IsMap() ||
+ !f->IsJSFunction()) {
+ return;
+ }
+
+ JSFunction* fun = JSFunction::cast(f);
+ Object* perhaps_context = fun->context();
+ if (perhaps_context->IsHeapObject() &&
+ heap->Contains(HeapObject::cast(perhaps_context)) &&
+ perhaps_context->IsContext()) {
+ Context* context = fun->context();
+ if (!heap->Contains(context)) {
+ Add("(Function context is outside heap)\n");
+ return;
+ }
+ Object* token = context->native_context()->security_token();
+ if (token != isolate->string_stream_current_security_token()) {
+ Add("Security context: %o\n", token);
+ isolate->set_string_stream_current_security_token(token);
+ }
+ } else {
+ Add("(Function context is corrupt)\n");
+ }
+}
+
+
+void StringStream::PrintFunction(Object* f, Object* receiver, Code** code) {
+ if (f->IsHeapObject() &&
+ HEAP->Contains(HeapObject::cast(f)) &&
+ HEAP->Contains(HeapObject::cast(f)->map()) &&
+ HeapObject::cast(f)->map()->IsMap()) {
+ if (f->IsJSFunction()) {
+ JSFunction* fun = JSFunction::cast(f);
+ // Common case: on-stack function present and resolved.
+ PrintPrototype(fun, receiver);
+ *code = fun->code();
+ } else if (f->IsInternalizedString()) {
+ // Unresolved and megamorphic calls: Instead of the function
+ // we have the function name on the stack.
+ PrintName(f);
+ Add("/* unresolved */ ");
+ } else {
+ // Unless this is the frame of a built-in function, we should always have
+ // the callee function or name on the stack. If we don't, we have a
+ // problem or a change of the stack frame layout.
+ Add("%o", f);
+ Add("/* warning: no JSFunction object or function name found */ ");
+ }
+ /* } else if (is_trampoline()) {
+ Print("trampoline ");
+ */
+ } else {
+ if (!f->IsHeapObject()) {
+ Add("/* warning: 'function' was not a heap object */ ");
+ return;
+ }
+ if (!HEAP->Contains(HeapObject::cast(f))) {
+ Add("/* warning: 'function' was not on the heap */ ");
+ return;
+ }
+ if (!HEAP->Contains(HeapObject::cast(f)->map())) {
+ Add("/* warning: function's map was not on the heap */ ");
+ return;
+ }
+ if (!HeapObject::cast(f)->map()->IsMap()) {
+ Add("/* warning: function's map was not a valid map */ ");
+ return;
+ }
+ Add("/* warning: Invalid JSFunction object found */ ");
+ }
+}
+
+
+void StringStream::PrintPrototype(JSFunction* fun, Object* receiver) {
+ Object* name = fun->shared()->name();
+ bool print_name = false;
+ Isolate* isolate = fun->GetIsolate();
+ for (Object* p = receiver;
+ p != isolate->heap()->null_value();
+ p = p->GetPrototype(isolate)) {
+ if (p->IsJSObject()) {
+ Object* key = JSObject::cast(p)->SlowReverseLookup(fun);
+ if (key != isolate->heap()->undefined_value()) {
+ if (!name->IsString() ||
+ !key->IsString() ||
+ !String::cast(name)->Equals(String::cast(key))) {
+ print_name = true;
+ }
+ if (name->IsString() && String::cast(name)->length() == 0) {
+ print_name = false;
+ }
+ name = key;
+ }
+ } else {
+ print_name = true;
+ }
+ }
+ PrintName(name);
+ // Also known as - if the name in the function doesn't match the name under
+ // which it was looked up.
+ if (print_name) {
+ Add("(aka ");
+ PrintName(fun->shared()->name());
+ Put(')');
+ }
+}
+
+
+char* HeapStringAllocator::grow(unsigned* bytes) {
+ unsigned new_bytes = *bytes * 2;
+ // Check for overflow.
+ if (new_bytes <= *bytes) {
+ return space_;
+ }
+ char* new_space = NewArray<char>(new_bytes);
+ if (new_space == NULL) {
+ return space_;
+ }
+ OS::MemCopy(new_space, space_, *bytes);
+ *bytes = new_bytes;
+ DeleteArray(space_);
+ space_ = new_space;
+ return new_space;
+}
+
+
+// Only grow once to the maximum allowable size.
+char* NoAllocationStringAllocator::grow(unsigned* bytes) {
+ ASSERT(size_ >= *bytes);
+ *bytes = size_;
+ return space_;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/string-stream.h b/chromium/v8/src/string-stream.h
new file mode 100644
index 00000000000..2367994116e
--- /dev/null
+++ b/chromium/v8/src/string-stream.h
@@ -0,0 +1,223 @@
+// 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_STRING_STREAM_H_
+#define V8_STRING_STREAM_H_
+
+namespace v8 {
+namespace internal {
+
+
+class StringAllocator {
+ public:
+ virtual ~StringAllocator() {}
+ // Allocate a number of bytes.
+ virtual char* allocate(unsigned bytes) = 0;
+ // Allocate a larger number of bytes and copy the old buffer to the new one.
+ // bytes is an input and output parameter passing the old size of the buffer
+ // and returning the new size. If allocation fails then we return the old
+ // buffer and do not increase the size.
+ virtual char* grow(unsigned* bytes) = 0;
+};
+
+
+// Normal allocator uses new[] and delete[].
+class HeapStringAllocator: public StringAllocator {
+ public:
+ ~HeapStringAllocator() { DeleteArray(space_); }
+ char* allocate(unsigned bytes);
+ char* grow(unsigned* bytes);
+ private:
+ char* space_;
+};
+
+
+// Allocator for use when no new c++ heap allocation is allowed.
+// Given a preallocated buffer up front and does no allocation while
+// building message.
+class NoAllocationStringAllocator: public StringAllocator {
+ public:
+ NoAllocationStringAllocator(char* memory, unsigned size);
+ char* allocate(unsigned bytes) { return space_; }
+ char* grow(unsigned* bytes);
+ private:
+ unsigned size_;
+ char* space_;
+};
+
+
+class FmtElm {
+ public:
+ FmtElm(int value) : type_(INT) { // NOLINT
+ data_.u_int_ = value;
+ }
+ explicit FmtElm(double value) : type_(DOUBLE) {
+ data_.u_double_ = value;
+ }
+ FmtElm(const char* value) : type_(C_STR) { // NOLINT
+ data_.u_c_str_ = value;
+ }
+ FmtElm(const Vector<const uc16>& value) : type_(LC_STR) { // NOLINT
+ data_.u_lc_str_ = &value;
+ }
+ FmtElm(Object* value) : type_(OBJ) { // NOLINT
+ data_.u_obj_ = value;
+ }
+ FmtElm(Handle<Object> value) : type_(HANDLE) { // NOLINT
+ data_.u_handle_ = value.location();
+ }
+ FmtElm(void* value) : type_(POINTER) { // NOLINT
+ data_.u_pointer_ = value;
+ }
+
+ private:
+ friend class StringStream;
+ enum Type { INT, DOUBLE, C_STR, LC_STR, OBJ, HANDLE, POINTER };
+ Type type_;
+ union {
+ int u_int_;
+ double u_double_;
+ const char* u_c_str_;
+ const Vector<const uc16>* u_lc_str_;
+ Object* u_obj_;
+ Object** u_handle_;
+ void* u_pointer_;
+ } data_;
+};
+
+
+class StringStream {
+ public:
+ explicit StringStream(StringAllocator* allocator):
+ allocator_(allocator),
+ capacity_(kInitialCapacity),
+ length_(0),
+ buffer_(allocator_->allocate(kInitialCapacity)) {
+ buffer_[0] = 0;
+ }
+
+ ~StringStream() {
+ }
+
+ bool Put(char c);
+ bool Put(String* str);
+ bool Put(String* str, int start, int end);
+ void Add(Vector<const char> format, Vector<FmtElm> elms);
+ void Add(const char* format);
+ void Add(Vector<const char> format);
+ void Add(const char* format, FmtElm arg0);
+ void Add(const char* format, FmtElm arg0, FmtElm arg1);
+ void Add(const char* format, FmtElm arg0, FmtElm arg1, FmtElm arg2);
+ void Add(const char* format,
+ FmtElm arg0,
+ FmtElm arg1,
+ FmtElm arg2,
+ FmtElm arg3);
+ void Add(const char* format,
+ FmtElm arg0,
+ FmtElm arg1,
+ FmtElm arg2,
+ FmtElm arg3,
+ FmtElm arg4);
+
+ // Getting the message out.
+ void OutputToFile(FILE* out);
+ void OutputToStdOut() { OutputToFile(stdout); }
+ void Log();
+ Handle<String> ToString();
+ SmartArrayPointer<const char> ToCString() const;
+ int length() const { return length_; }
+
+ // Object printing support.
+ void PrintName(Object* o);
+ void PrintFixedArray(FixedArray* array, unsigned int limit);
+ void PrintByteArray(ByteArray* ba);
+ void PrintUsingMap(JSObject* js_object);
+ void PrintPrototype(JSFunction* fun, Object* receiver);
+ void PrintSecurityTokenIfChanged(Object* function);
+ // NOTE: Returns the code in the output parameter.
+ void PrintFunction(Object* function, Object* receiver, Code** code);
+
+ // Reset the stream.
+ void Reset() {
+ length_ = 0;
+ buffer_[0] = 0;
+ }
+
+ // Mentioned object cache support.
+ void PrintMentionedObjectCache();
+ static void ClearMentionedObjectCache();
+#ifdef DEBUG
+ static bool IsMentionedObjectCacheClear();
+#endif
+
+
+ static const int kInitialCapacity = 16;
+
+ private:
+ void PrintObject(Object* obj);
+
+ StringAllocator* allocator_;
+ unsigned capacity_;
+ unsigned length_; // does not include terminating 0-character
+ char* buffer_;
+
+ bool full() const { return (capacity_ - length_) == 1; }
+ int space() const { return capacity_ - length_; }
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringStream);
+};
+
+
+// Utility class to print a list of items to a stream, divided by a separator.
+class SimpleListPrinter {
+ public:
+ explicit SimpleListPrinter(StringStream* stream, char separator = ',') {
+ separator_ = separator;
+ stream_ = stream;
+ first_ = true;
+ }
+
+ void Add(const char* str) {
+ if (first_) {
+ first_ = false;
+ } else {
+ stream_->Put(separator_);
+ }
+ stream_->Add(str);
+ }
+
+ private:
+ bool first_;
+ char separator_;
+ StringStream* stream_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_STRING_STREAM_H_
diff --git a/chromium/v8/src/string.js b/chromium/v8/src/string.js
new file mode 100644
index 00000000000..cb82c166346
--- /dev/null
+++ b/chromium/v8/src/string.js
@@ -0,0 +1,1017 @@
+// Copyright 2012 the V8 project authors. 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:
+// var $String = global.String;
+// var $NaN = 0/0;
+
+// -------------------------------------------------------------------
+
+function StringConstructor(x) {
+ var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
+ if (%_IsConstructCall()) {
+ %_SetValueOf(this, value);
+ } else {
+ return value;
+ }
+}
+
+
+// ECMA-262 section 15.5.4.2
+function StringToString() {
+ 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)) {
+ throw new $TypeError('String.prototype.valueOf is not generic');
+ }
+ return %_ValueOf(this);
+}
+
+
+// ECMA-262, section 15.5.4.4
+function StringCharAt(pos) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.charAt"]);
+ }
+ var result = %_StringCharAt(this, pos);
+ if (%_IsSmi(result)) {
+ result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
+ }
+ return result;
+}
+
+
+// ECMA-262 section 15.5.4.5
+function StringCharCodeAt(pos) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.charCodeAt"]);
+ }
+ var result = %_StringCharCodeAt(this, pos);
+ if (!%_IsSmi(result)) {
+ result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
+ }
+ return result;
+}
+
+
+// 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"]);
+ }
+ var len = %_ArgumentsLength();
+ var this_as_string = TO_STRING_INLINE(this);
+ if (len === 1) {
+ return this_as_string + %_Arguments(0);
+ }
+ var parts = new InternalArray(len + 1);
+ parts[0] = this_as_string;
+ for (var i = 0; i < len; i++) {
+ var part = %_Arguments(i);
+ parts[i + 1] = TO_STRING_INLINE(part);
+ }
+ return %StringBuilderConcat(parts, len + 1, "");
+}
+
+// Match ES3 and Safari
+%FunctionSetLength(StringConcat, 1);
+
+
+// ECMA-262 section 15.5.4.7
+function StringIndexOf(pattern /* position */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.indexOf"]);
+ }
+ var subject = TO_STRING_INLINE(this);
+ pattern = TO_STRING_INLINE(pattern);
+ var index = 0;
+ if (%_ArgumentsLength() > 1) {
+ index = %_Arguments(1); // position
+ index = TO_INTEGER(index);
+ if (index < 0) index = 0;
+ if (index > subject.length) index = subject.length;
+ }
+ return %StringIndexOf(subject, pattern, index);
+}
+
+
+// ECMA-262 section 15.5.4.8
+function StringLastIndexOf(pat /* position */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.lastIndexOf"]);
+ }
+ var sub = TO_STRING_INLINE(this);
+ var subLength = sub.length;
+ var pat = TO_STRING_INLINE(pat);
+ var patLength = pat.length;
+ var index = subLength - patLength;
+ if (%_ArgumentsLength() > 1) {
+ var position = ToNumber(%_Arguments(1));
+ if (!NUMBER_IS_NAN(position)) {
+ position = TO_INTEGER(position);
+ if (position < 0) {
+ position = 0;
+ }
+ if (position + patLength < subLength) {
+ index = position;
+ }
+ }
+ }
+ if (index < 0) {
+ return -1;
+ }
+ return %StringLastIndexOf(sub, pat, index);
+}
+
+
+// ECMA-262 section 15.5.4.9
+//
+// This function is implementation specific. For now, we do not
+// do anything locale specific.
+function StringLocaleCompare(other) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.localeCompare"]);
+ }
+ return %StringLocaleCompare(TO_STRING_INLINE(this),
+ TO_STRING_INLINE(other));
+}
+
+
+// ECMA-262 section 15.5.4.10
+function StringMatch(regexp) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.match"]);
+ }
+ var subject = TO_STRING_INLINE(this);
+ if (IS_REGEXP(regexp)) {
+ // Emulate RegExp.prototype.exec's side effect in step 5, even though
+ // value is discarded.
+ var lastIndex = regexp.lastIndex;
+ TO_INTEGER_FOR_SIDE_EFFECT(lastIndex);
+ if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
+ %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
+ // lastMatchInfo is defined in regexp.js.
+ var result = %StringMatch(subject, regexp, lastMatchInfo);
+ if (result !== null) lastMatchInfoOverride = null;
+ regexp.lastIndex = 0;
+ return result;
+ }
+ // Non-regexp argument.
+ regexp = new $RegExp(regexp);
+ return RegExpExecNoTests(regexp, subject, 0);
+}
+
+
+// This has the same size as the lastMatchInfo array, and can be used for
+// functions that expect that structure to be returned. It is used when the
+// needle is a string rather than a regexp. In this case we can't update
+// lastMatchArray without erroneously affecting the properties on the global
+// RegExp object.
+var reusableMatchInfo = [2, "", "", -1, -1];
+
+
+// ECMA-262, section 15.5.4.11
+function StringReplace(search, replace) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.replace"]);
+ }
+ var subject = TO_STRING_INLINE(this);
+
+ // Decision tree for dispatch
+ // .. regexp search
+ // .... string replace
+ // ...... non-global search
+ // ........ empty string replace
+ // ........ non-empty string replace (with $-expansion)
+ // ...... global search
+ // ........ no need to circumvent last match info override
+ // ........ need to circument last match info override
+ // .... function replace
+ // ...... global search
+ // ...... non-global search
+ // .. string search
+ // .... special case that replaces with one single character
+ // ...... function replace
+ // ...... string replace (with $-expansion)
+
+ if (IS_REGEXP(search)) {
+ // Emulate RegExp.prototype.exec's side effect in step 5, even if
+ // value is discarded.
+ var lastIndex = search.lastIndex;
+ TO_INTEGER_FOR_SIDE_EFFECT(lastIndex);
+ %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
+
+ if (!IS_SPEC_FUNCTION(replace)) {
+ replace = TO_STRING_INLINE(replace);
+
+ if (!search.global) {
+ // Non-global regexp search, string replace.
+ var match = DoRegExpExec(search, subject, 0);
+ if (match == null) {
+ search.lastIndex = 0
+ return subject;
+ }
+ if (replace.length == 0) {
+ return %_SubString(subject, 0, match[CAPTURE0]) +
+ %_SubString(subject, match[CAPTURE1], subject.length)
+ }
+ return ExpandReplacement(replace, subject, lastMatchInfo,
+ %_SubString(subject, 0, match[CAPTURE0])) +
+ %_SubString(subject, match[CAPTURE1], subject.length);
+ }
+
+ // Global regexp search, string replace.
+ search.lastIndex = 0;
+ if (lastMatchInfoOverride == null) {
+ return %StringReplaceGlobalRegExpWithString(
+ subject, search, replace, lastMatchInfo);
+ } else {
+ // We use this hack to detect whether StringReplaceRegExpWithString
+ // found at least one hit. In that case we need to remove any
+ // override.
+ var saved_subject = lastMatchInfo[LAST_SUBJECT_INDEX];
+ lastMatchInfo[LAST_SUBJECT_INDEX] = 0;
+ var answer = %StringReplaceGlobalRegExpWithString(
+ subject, search, replace, lastMatchInfo);
+ if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) {
+ lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
+ } else {
+ lastMatchInfoOverride = null;
+ }
+ return answer;
+ }
+ }
+
+ if (search.global) {
+ // Global regexp search, function replace.
+ return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
+ }
+ // Non-global regexp search, function replace.
+ return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
+ }
+
+ 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;
+
+ var result = %_SubString(subject, 0, start);
+
+ // Compute the string to replace with.
+ if (IS_SPEC_FUNCTION(replace)) {
+ var receiver = %GetDefaultReceiver(replace);
+ result += %_CallFunction(receiver, search, start, subject, replace);
+ } else {
+ reusableMatchInfo[CAPTURE0] = start;
+ reusableMatchInfo[CAPTURE1] = end;
+ result = ExpandReplacement(TO_STRING_INLINE(replace),
+ subject,
+ reusableMatchInfo,
+ result);
+ }
+
+ return result + %_SubString(subject, end, subject.length);
+}
+
+
+// Expand the $-expressions in the string and return a new string with
+// the result.
+function ExpandReplacement(string, subject, matchInfo, result) {
+ var length = string.length;
+ var next = %StringIndexOf(string, '$', 0);
+ if (next < 0) {
+ if (length > 0) result += string;
+ return result;
+ }
+
+ if (next > 0) result += %_SubString(string, 0, next);
+
+ while (true) {
+ var expansion = '$';
+ var position = next + 1;
+ if (position < length) {
+ var peek = %_StringCharCodeAt(string, position);
+ if (peek == 36) { // $$
+ ++position;
+ result += '$';
+ } else if (peek == 38) { // $& - match
+ ++position;
+ result +=
+ %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
+ } else if (peek == 96) { // $` - prefix
+ ++position;
+ result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
+ } else if (peek == 39) { // $' - suffix
+ ++position;
+ result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
+ } else if (peek >= 48 && peek <= 57) {
+ // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
+ var scaled_index = (peek - 48) << 1;
+ var advance = 1;
+ var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
+ if (position + 1 < string.length) {
+ var next = %_StringCharCodeAt(string, position + 1);
+ if (next >= 48 && next <= 57) {
+ var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
+ if (new_scaled_index < number_of_captures) {
+ scaled_index = new_scaled_index;
+ advance = 2;
+ }
+ }
+ }
+ if (scaled_index != 0 && scaled_index < number_of_captures) {
+ var start = matchInfo[CAPTURE(scaled_index)];
+ if (start >= 0) {
+ result +=
+ %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
+ }
+ position += advance;
+ } else {
+ result += '$';
+ }
+ } else {
+ result += '$';
+ }
+ } else {
+ result += '$';
+ }
+
+ // Go the the next $ in the string.
+ next = %StringIndexOf(string, '$', position);
+
+ // Return if there are no more $ characters in the string. If we
+ // haven't reached the end, we need to append the suffix.
+ if (next < 0) {
+ if (position < length) {
+ result += %_SubString(string, position, length);
+ }
+ return result;
+ }
+
+ // Append substring between the previous and the next $ character.
+ if (next > position) {
+ result += %_SubString(string, position, next);
+ }
+ }
+ return result;
+}
+
+
+// Compute the string of a given regular expression capture.
+function CaptureString(string, lastCaptureInfo, index) {
+ // Scale the index.
+ var scaled = index << 1;
+ // Compute start and end.
+ var start = lastCaptureInfo[CAPTURE(scaled)];
+ // If start isn't valid, return undefined.
+ if (start < 0) return;
+ var end = lastCaptureInfo[CAPTURE(scaled + 1)];
+ return %_SubString(string, 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
+// in the finally block.
+var reusableReplaceArray = new InternalArray(16);
+
+// Helper function for replacing regular expressions with the result of a
+// function application in String.prototype.replace.
+function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
+ var resultArray = reusableReplaceArray;
+ if (resultArray) {
+ reusableReplaceArray = null;
+ } else {
+ // Inside a nested replace (replace called from the replacement function
+ // of another replace) or we have failed to set the reusable array
+ // back due to an exception in a replacement function. Create a new
+ // array to use in the future, or until the original is written back.
+ resultArray = new InternalArray(16);
+ }
+ var res = %RegExpExecMultiple(regexp,
+ subject,
+ lastMatchInfo,
+ resultArray);
+ regexp.lastIndex = 0;
+ if (IS_NULL(res)) {
+ // No matches at all.
+ reusableReplaceArray = resultArray;
+ return subject;
+ }
+ var len = res.length;
+ if (NUMBER_OF_CAPTURES(lastMatchInfo) == 2) {
+ // If the number of captures is two then there are no explicit captures in
+ // the regexp, just the implicit capture that captures the whole match. In
+ // this case we can simplify quite a bit and end up with something faster.
+ // The builder will consist of some integers that indicate slices of the
+ // input string and some replacements that were returned from the replace
+ // function.
+ var match_start = 0;
+ var override = new InternalPackedArray(null, 0, subject);
+ var receiver = %GetDefaultReceiver(replace);
+ for (var i = 0; i < len; i++) {
+ var elem = res[i];
+ if (%_IsSmi(elem)) {
+ // Integers represent slices of the original string. Use these to
+ // get the offsets we need for the override array (so things like
+ // RegExp.leftContext work during the callback function.
+ if (elem > 0) {
+ match_start = (elem >> 11) + (elem & 0x7ff);
+ } else {
+ match_start = res[++i] - elem;
+ }
+ } else {
+ override[0] = elem;
+ override[1] = match_start;
+ lastMatchInfoOverride = override;
+ var func_result =
+ %_CallFunction(receiver, elem, match_start, subject, replace);
+ // Overwrite the i'th element in the results with the string we got
+ // back from the callback function.
+ res[i] = TO_STRING_INLINE(func_result);
+ match_start += elem.length;
+ }
+ }
+ } else {
+ var receiver = %GetDefaultReceiver(replace);
+ for (var i = 0; i < len; i++) {
+ var elem = res[i];
+ if (!%_IsSmi(elem)) {
+ // elem must be an Array.
+ // Use the apply argument as backing for global RegExp properties.
+ lastMatchInfoOverride = elem;
+ var func_result = %Apply(replace, receiver, elem, 0, elem.length);
+ // Overwrite the i'th element in the results with the string we got
+ // back from the callback function.
+ res[i] = TO_STRING_INLINE(func_result);
+ }
+ }
+ }
+ var result = %StringBuilderConcat(res, res.length, subject);
+ resultArray.length = 0;
+ reusableReplaceArray = resultArray;
+ return result;
+}
+
+
+function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
+ var matchInfo = DoRegExpExec(regexp, subject, 0);
+ if (IS_NULL(matchInfo)) {
+ regexp.lastIndex = 0;
+ return subject;
+ }
+ var index = matchInfo[CAPTURE0];
+ var result = %_SubString(subject, 0, index);
+ var endOfMatch = matchInfo[CAPTURE1];
+ // Compute the parameter list consisting of the match, captures, index,
+ // and subject for the replace function invocation.
+ // The number of captures plus one for the match.
+ var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
+ var replacement;
+ var receiver = %GetDefaultReceiver(replace);
+ if (m == 1) {
+ // No captures, only the match, which is always valid.
+ var s = %_SubString(subject, index, endOfMatch);
+ // Don't call directly to avoid exposing the built-in global object.
+ replacement = %_CallFunction(receiver, s, index, subject, replace);
+ } else {
+ var parameters = new InternalArray(m + 2);
+ for (var j = 0; j < m; j++) {
+ parameters[j] = CaptureString(subject, matchInfo, j);
+ }
+ parameters[j] = index;
+ parameters[j + 1] = subject;
+
+ replacement = %Apply(replace, receiver, parameters, 0, j + 2);
+ }
+
+ result += replacement; // The add method converts to string if necessary.
+ // Can't use matchInfo any more from here, since the function could
+ // overwrite it.
+ return result + %_SubString(subject, endOfMatch, subject.length);
+}
+
+
+// ECMA-262 section 15.5.4.12
+function StringSearch(re) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.search"]);
+ }
+ var regexp;
+ if (IS_STRING(re)) {
+ regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
+ } else if (IS_REGEXP(re)) {
+ regexp = re;
+ } else {
+ regexp = new $RegExp(re);
+ }
+ var match = DoRegExpExec(regexp, TO_STRING_INLINE(this), 0);
+ if (match) {
+ return match[CAPTURE0];
+ }
+ return -1;
+}
+
+
+// ECMA-262 section 15.5.4.13
+function StringSlice(start, end) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.slice"]);
+ }
+ var s = TO_STRING_INLINE(this);
+ var s_len = s.length;
+ var start_i = TO_INTEGER(start);
+ var end_i = s_len;
+ if (end !== void 0) {
+ end_i = TO_INTEGER(end);
+ }
+
+ if (start_i < 0) {
+ start_i += s_len;
+ if (start_i < 0) {
+ start_i = 0;
+ }
+ } else {
+ if (start_i > s_len) {
+ return '';
+ }
+ }
+
+ if (end_i < 0) {
+ end_i += s_len;
+ if (end_i < 0) {
+ return '';
+ }
+ } else {
+ if (end_i > s_len) {
+ end_i = s_len;
+ }
+ }
+
+ if (end_i <= start_i) {
+ return '';
+ }
+
+ return %_SubString(s, start_i, end_i);
+}
+
+
+// ECMA-262 section 15.5.4.14
+function StringSplit(separator, limit) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.split"]);
+ }
+ var subject = TO_STRING_INLINE(this);
+ limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
+
+ // ECMA-262 says that if separator is undefined, the result should
+ // be an array of size 1 containing the entire string.
+ if (IS_UNDEFINED(separator)) {
+ return [subject];
+ }
+
+ 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.
+ if (separator_length === 0) return %StringToArray(subject, limit);
+
+ var result = %StringSplit(subject, separator, limit);
+
+ return result;
+ }
+
+ if (limit === 0) return [];
+
+ // Separator is a regular expression.
+ return StringSplitOnRegExp(subject, separator, limit, length);
+}
+
+
+var ArrayPushBuiltin = $Array.prototype.push;
+
+function StringSplitOnRegExp(subject, separator, limit, length) {
+ %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
+
+ if (length === 0) {
+ if (DoRegExpExec(separator, subject, 0, 0) != null) {
+ return [];
+ }
+ return [subject];
+ }
+
+ var currentIndex = 0;
+ var startIndex = 0;
+ var startMatch = 0;
+ var result = [];
+
+ outer_loop:
+ while (true) {
+
+ if (startIndex === length) {
+ %_CallFunction(result, %_SubString(subject, currentIndex, length),
+ ArrayPushBuiltin);
+ break;
+ }
+
+ var matchInfo = DoRegExpExec(separator, subject, startIndex);
+ if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) {
+ %_CallFunction(result, %_SubString(subject, currentIndex, length),
+ ArrayPushBuiltin);
+ break;
+ }
+ var endIndex = matchInfo[CAPTURE1];
+
+ // We ignore a zero-length match at the currentIndex.
+ if (startIndex === endIndex && endIndex === currentIndex) {
+ startIndex++;
+ continue;
+ }
+
+ %_CallFunction(result, %_SubString(subject, currentIndex, startMatch),
+ ArrayPushBuiltin);
+
+ if (result.length === limit) break;
+
+ var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
+ for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
+ var start = matchInfo[i++];
+ var end = matchInfo[i++];
+ if (end != -1) {
+ %_CallFunction(result, %_SubString(subject, start, end),
+ ArrayPushBuiltin);
+ } else {
+ %_CallFunction(result, void 0, ArrayPushBuiltin);
+ }
+ if (result.length === limit) break outer_loop;
+ }
+
+ startIndex = currentIndex = endIndex;
+ }
+ return result;
+}
+
+
+// ECMA-262 section 15.5.4.15
+function StringSubstring(start, end) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.subString"]);
+ }
+ var s = TO_STRING_INLINE(this);
+ var s_len = s.length;
+
+ var start_i = TO_INTEGER(start);
+ if (start_i < 0) {
+ start_i = 0;
+ } else if (start_i > s_len) {
+ start_i = s_len;
+ }
+
+ var end_i = s_len;
+ if (!IS_UNDEFINED(end)) {
+ end_i = TO_INTEGER(end);
+ if (end_i > s_len) {
+ end_i = s_len;
+ } else {
+ if (end_i < 0) end_i = 0;
+ if (start_i > end_i) {
+ var tmp = end_i;
+ end_i = start_i;
+ start_i = tmp;
+ }
+ }
+ }
+
+ return %_SubString(s, start_i, end_i);
+}
+
+
+// This is not a part of ECMA-262.
+function StringSubstr(start, n) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.substr"]);
+ }
+ var s = TO_STRING_INLINE(this);
+ var len;
+
+ // Correct n: If not given, set to string length; if explicitly
+ // set to undefined, zero, or negative, returns empty string.
+ if (n === void 0) {
+ len = s.length;
+ } else {
+ len = TO_INTEGER(n);
+ if (len <= 0) return '';
+ }
+
+ // Correct start: If not given (or undefined), set to zero; otherwise
+ // convert to integer and handle negative case.
+ if (start === void 0) {
+ start = 0;
+ } else {
+ start = TO_INTEGER(start);
+ // If positive, and greater than or equal to the string length,
+ // return empty string.
+ if (start >= s.length) return '';
+ // If negative and absolute value is larger than the string length,
+ // use zero.
+ if (start < 0) {
+ start += s.length;
+ if (start < 0) start = 0;
+ }
+ }
+
+ var end = start + len;
+ if (end > s.length) end = s.length;
+
+ return %_SubString(s, start, end);
+}
+
+
+// ECMA-262, 15.5.4.16
+function StringToLowerCase() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.toLowerCase"]);
+ }
+ return %StringToLowerCase(TO_STRING_INLINE(this));
+}
+
+
+// ECMA-262, 15.5.4.17
+function StringToLocaleLowerCase() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.toLocaleLowerCase"]);
+ }
+ return %StringToLowerCase(TO_STRING_INLINE(this));
+}
+
+
+// ECMA-262, 15.5.4.18
+function StringToUpperCase() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.toUpperCase"]);
+ }
+ return %StringToUpperCase(TO_STRING_INLINE(this));
+}
+
+
+// ECMA-262, 15.5.4.19
+function StringToLocaleUpperCase() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.toLocaleUpperCase"]);
+ }
+ return %StringToUpperCase(TO_STRING_INLINE(this));
+}
+
+// ES5, 15.5.4.20
+function StringTrim() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.trim"]);
+ }
+ return %StringTrim(TO_STRING_INLINE(this), true, true);
+}
+
+function StringTrimLeft() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.trimLeft"]);
+ }
+ return %StringTrim(TO_STRING_INLINE(this), true, false);
+}
+
+function StringTrimRight() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.trimRight"]);
+ }
+ return %StringTrim(TO_STRING_INLINE(this), false, true);
+}
+
+
+// ECMA-262, section 15.5.3.2
+function StringFromCharCode(code) {
+ var n = %_ArgumentsLength();
+ if (n == 1) {
+ if (!%_IsSmi(code)) code = ToNumber(code);
+ return %_StringCharFromCode(code & 0xffff);
+ }
+
+ var one_byte = %NewString(n, NEW_ONE_BYTE_STRING);
+ var i;
+ for (i = 0; i < n; i++) {
+ var code = %_Arguments(i);
+ if (!%_IsSmi(code)) code = ToNumber(code) & 0xffff;
+ if (code < 0) code = code & 0xffff;
+ if (code > 0xff) break;
+ %_OneByteSeqStringSetChar(one_byte, i, code);
+ }
+ if (i == n) return one_byte;
+ one_byte = %TruncateString(one_byte, i);
+
+ var two_byte = %NewString(n - i, NEW_TWO_BYTE_STRING);
+ for (var j = 0; i < n; i++, j++) {
+ var code = %_Arguments(i);
+ if (!%_IsSmi(code)) code = ToNumber(code) & 0xffff;
+ %_TwoByteSeqStringSetChar(two_byte, j, code);
+ }
+ return one_byte + two_byte;
+}
+
+
+// Helper function for very basic XSS protection.
+function HtmlEscape(str) {
+ return TO_STRING_INLINE(str).replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;")
+ .replace(/"/g, "&quot;")
+ .replace(/'/g, "&#039;");
+}
+
+
+// Compatibility support for KJS.
+// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
+function StringLink(s) {
+ return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
+}
+
+
+function StringAnchor(name) {
+ return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
+}
+
+
+function StringFontcolor(color) {
+ return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
+}
+
+
+function StringFontsize(size) {
+ return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
+}
+
+
+function StringBig() {
+ return "<big>" + this + "</big>";
+}
+
+
+function StringBlink() {
+ return "<blink>" + this + "</blink>";
+}
+
+
+function StringBold() {
+ return "<b>" + this + "</b>";
+}
+
+
+function StringFixed() {
+ return "<tt>" + this + "</tt>";
+}
+
+
+function StringItalics() {
+ return "<i>" + this + "</i>";
+}
+
+
+function StringSmall() {
+ return "<small>" + this + "</small>";
+}
+
+
+function StringStrike() {
+ return "<strike>" + this + "</strike>";
+}
+
+
+function StringSub() {
+ return "<sub>" + this + "</sub>";
+}
+
+
+function StringSup() {
+ return "<sup>" + this + "</sup>";
+}
+
+// -------------------------------------------------------------------
+
+function SetUpString() {
+ %CheckIsBootstrapping();
+
+ // Set the String function and constructor.
+ %SetCode($String, StringConstructor);
+ %FunctionSetPrototype($String, new $String());
+
+ // Set up the constructor property on the String prototype object.
+ %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
+
+ // Set up the non-enumerable functions on the String object.
+ InstallFunctions($String, DONT_ENUM, $Array(
+ "fromCharCode", StringFromCharCode
+ ));
+
+ // Set up the non-enumerable functions on the String prototype object.
+ InstallFunctions($String.prototype, DONT_ENUM, $Array(
+ "valueOf", StringValueOf,
+ "toString", StringToString,
+ "charAt", StringCharAt,
+ "charCodeAt", StringCharCodeAt,
+ "concat", StringConcat,
+ "indexOf", StringIndexOf,
+ "lastIndexOf", StringLastIndexOf,
+ "localeCompare", StringLocaleCompare,
+ "match", StringMatch,
+ "replace", StringReplace,
+ "search", StringSearch,
+ "slice", StringSlice,
+ "split", StringSplit,
+ "substring", StringSubstring,
+ "substr", StringSubstr,
+ "toLowerCase", StringToLowerCase,
+ "toLocaleLowerCase", StringToLocaleLowerCase,
+ "toUpperCase", StringToUpperCase,
+ "toLocaleUpperCase", StringToLocaleUpperCase,
+ "trim", StringTrim,
+ "trimLeft", StringTrimLeft,
+ "trimRight", StringTrimRight,
+ "link", StringLink,
+ "anchor", StringAnchor,
+ "fontcolor", StringFontcolor,
+ "fontsize", StringFontsize,
+ "big", StringBig,
+ "blink", StringBlink,
+ "bold", StringBold,
+ "fixed", StringFixed,
+ "italics", StringItalics,
+ "small", StringSmall,
+ "strike", StringStrike,
+ "sub", StringSub,
+ "sup", StringSup
+ ));
+}
+
+SetUpString();
diff --git a/chromium/v8/src/strtod.cc b/chromium/v8/src/strtod.cc
new file mode 100644
index 00000000000..d332fd2bc4d
--- /dev/null
+++ b/chromium/v8/src/strtod.cc
@@ -0,0 +1,442 @@
+// Copyright 2012 the V8 project authors. 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 <stdarg.h>
+#include <cmath>
+
+#include "globals.h"
+#include "utils.h"
+#include "strtod.h"
+#include "bignum.h"
+#include "cached-powers.h"
+#include "double.h"
+
+namespace v8 {
+namespace internal {
+
+// 2^53 = 9007199254740992.
+// Any integer with at most 15 decimal digits will hence fit into a double
+// (which has a 53bit significand) without loss of precision.
+static const int kMaxExactDoubleIntegerDecimalDigits = 15;
+// 2^64 = 18446744073709551616 > 10^19
+static const int kMaxUint64DecimalDigits = 19;
+
+// Max double: 1.7976931348623157 x 10^308
+// Min non-zero double: 4.9406564584124654 x 10^-324
+// Any x >= 10^309 is interpreted as +infinity.
+// Any x <= 10^-324 is interpreted as 0.
+// Note that 2.5e-324 (despite being smaller than the min double) will be read
+// as non-zero (equal to the min non-zero double).
+static const int kMaxDecimalPower = 309;
+static const int kMinDecimalPower = -324;
+
+// 2^64 = 18446744073709551616
+static const uint64_t kMaxUint64 = V8_2PART_UINT64_C(0xFFFFFFFF, FFFFFFFF);
+
+
+static const double exact_powers_of_ten[] = {
+ 1.0, // 10^0
+ 10.0,
+ 100.0,
+ 1000.0,
+ 10000.0,
+ 100000.0,
+ 1000000.0,
+ 10000000.0,
+ 100000000.0,
+ 1000000000.0,
+ 10000000000.0, // 10^10
+ 100000000000.0,
+ 1000000000000.0,
+ 10000000000000.0,
+ 100000000000000.0,
+ 1000000000000000.0,
+ 10000000000000000.0,
+ 100000000000000000.0,
+ 1000000000000000000.0,
+ 10000000000000000000.0,
+ 100000000000000000000.0, // 10^20
+ 1000000000000000000000.0,
+ // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22
+ 10000000000000000000000.0
+};
+static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten);
+
+// Maximum number of significant digits in the decimal representation.
+// In fact the value is 772 (see conversions.cc), but to give us some margin
+// we round up to 780.
+static const int kMaxSignificantDecimalDigits = 780;
+
+static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
+ for (int i = 0; i < buffer.length(); i++) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(i, buffer.length());
+ }
+ }
+ return Vector<const char>(buffer.start(), 0);
+}
+
+
+static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
+ for (int i = buffer.length() - 1; i >= 0; --i) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(0, i + 1);
+ }
+ }
+ return Vector<const char>(buffer.start(), 0);
+}
+
+
+static void TrimToMaxSignificantDigits(Vector<const char> buffer,
+ int exponent,
+ char* significant_buffer,
+ int* significant_exponent) {
+ for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) {
+ significant_buffer[i] = buffer[i];
+ }
+ // The input buffer has been trimmed. Therefore the last digit must be
+ // different from '0'.
+ ASSERT(buffer[buffer.length() - 1] != '0');
+ // Set the last digit to be non-zero. This is sufficient to guarantee
+ // correct rounding.
+ significant_buffer[kMaxSignificantDecimalDigits - 1] = '1';
+ *significant_exponent =
+ exponent + (buffer.length() - kMaxSignificantDecimalDigits);
+}
+
+
+// Reads digits from the buffer and converts them to a uint64.
+// Reads in as many digits as fit into a uint64.
+// When the string starts with "1844674407370955161" no further digit is read.
+// Since 2^64 = 18446744073709551616 it would still be possible read another
+// digit if it was less or equal than 6, but this would complicate the code.
+static uint64_t ReadUint64(Vector<const char> buffer,
+ int* number_of_read_digits) {
+ uint64_t result = 0;
+ int i = 0;
+ while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) {
+ int digit = buffer[i++] - '0';
+ ASSERT(0 <= digit && digit <= 9);
+ result = 10 * result + digit;
+ }
+ *number_of_read_digits = i;
+ return result;
+}
+
+
+// Reads a DiyFp from the buffer.
+// The returned DiyFp is not necessarily normalized.
+// If remaining_decimals is zero then the returned DiyFp is accurate.
+// Otherwise it has been rounded and has error of at most 1/2 ulp.
+static void ReadDiyFp(Vector<const char> buffer,
+ DiyFp* result,
+ int* remaining_decimals) {
+ int read_digits;
+ uint64_t significand = ReadUint64(buffer, &read_digits);
+ if (buffer.length() == read_digits) {
+ *result = DiyFp(significand, 0);
+ *remaining_decimals = 0;
+ } else {
+ // Round the significand.
+ if (buffer[read_digits] >= '5') {
+ significand++;
+ }
+ // Compute the binary exponent.
+ int exponent = 0;
+ *result = DiyFp(significand, exponent);
+ *remaining_decimals = buffer.length() - read_digits;
+ }
+}
+
+
+static bool DoubleStrtod(Vector<const char> trimmed,
+ int exponent,
+ double* result) {
+#if (V8_TARGET_ARCH_IA32 || defined(USE_SIMULATOR)) && !defined(_MSC_VER)
+ // On x86 the floating-point stack can be 64 or 80 bits wide. If it is
+ // 80 bits wide (as is the case on Linux) then double-rounding occurs and the
+ // result is not accurate.
+ // We know that Windows32 with MSVC, unlike with MinGW32, uses 64 bits and is
+ // therefore accurate.
+ // Note that the ARM and MIPS simulators are compiled for 32bits. They
+ // therefore exhibit the same problem.
+ return false;
+#endif
+ if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) {
+ int read_digits;
+ // The trimmed input fits into a double.
+ // If the 10^exponent (resp. 10^-exponent) fits into a double too then we
+ // can compute the result-double simply by multiplying (resp. dividing) the
+ // two numbers.
+ // This is possible because IEEE guarantees that floating-point operations
+ // return the best possible approximation.
+ if (exponent < 0 && -exponent < kExactPowersOfTenSize) {
+ // 10^-exponent fits into a double.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ ASSERT(read_digits == trimmed.length());
+ *result /= exact_powers_of_ten[-exponent];
+ return true;
+ }
+ if (0 <= exponent && exponent < kExactPowersOfTenSize) {
+ // 10^exponent fits into a double.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ ASSERT(read_digits == trimmed.length());
+ *result *= exact_powers_of_ten[exponent];
+ return true;
+ }
+ int remaining_digits =
+ kMaxExactDoubleIntegerDecimalDigits - trimmed.length();
+ if ((0 <= exponent) &&
+ (exponent - remaining_digits < kExactPowersOfTenSize)) {
+ // The trimmed string was short and we can multiply it with
+ // 10^remaining_digits. As a result the remaining exponent now fits
+ // into a double too.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ ASSERT(read_digits == trimmed.length());
+ *result *= exact_powers_of_ten[remaining_digits];
+ *result *= exact_powers_of_ten[exponent - remaining_digits];
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Returns 10^exponent as an exact DiyFp.
+// The given exponent must be in the range [1; kDecimalExponentDistance[.
+static DiyFp AdjustmentPowerOfTen(int exponent) {
+ ASSERT(0 < exponent);
+ ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance);
+ // Simply hardcode the remaining powers for the given decimal exponent
+ // distance.
+ ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8);
+ switch (exponent) {
+ case 1: return DiyFp(V8_2PART_UINT64_C(0xa0000000, 00000000), -60);
+ case 2: return DiyFp(V8_2PART_UINT64_C(0xc8000000, 00000000), -57);
+ case 3: return DiyFp(V8_2PART_UINT64_C(0xfa000000, 00000000), -54);
+ case 4: return DiyFp(V8_2PART_UINT64_C(0x9c400000, 00000000), -50);
+ case 5: return DiyFp(V8_2PART_UINT64_C(0xc3500000, 00000000), -47);
+ case 6: return DiyFp(V8_2PART_UINT64_C(0xf4240000, 00000000), -44);
+ case 7: return DiyFp(V8_2PART_UINT64_C(0x98968000, 00000000), -40);
+ default:
+ UNREACHABLE();
+ return DiyFp(0, 0);
+ }
+}
+
+
+// If the function returns true then the result is the correct double.
+// Otherwise it is either the correct double or the double that is just below
+// the correct double.
+static bool DiyFpStrtod(Vector<const char> buffer,
+ int exponent,
+ double* result) {
+ DiyFp input;
+ int remaining_decimals;
+ ReadDiyFp(buffer, &input, &remaining_decimals);
+ // Since we may have dropped some digits the input is not accurate.
+ // If remaining_decimals is different than 0 than the error is at most
+ // .5 ulp (unit in the last place).
+ // We don't want to deal with fractions and therefore keep a common
+ // denominator.
+ const int kDenominatorLog = 3;
+ const int kDenominator = 1 << kDenominatorLog;
+ // Move the remaining decimals into the exponent.
+ exponent += remaining_decimals;
+ int error = (remaining_decimals == 0 ? 0 : kDenominator / 2);
+
+ int old_e = input.e();
+ input.Normalize();
+ error <<= old_e - input.e();
+
+ ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent);
+ if (exponent < PowersOfTenCache::kMinDecimalExponent) {
+ *result = 0.0;
+ return true;
+ }
+ DiyFp cached_power;
+ int cached_decimal_exponent;
+ PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent,
+ &cached_power,
+ &cached_decimal_exponent);
+
+ if (cached_decimal_exponent != exponent) {
+ int adjustment_exponent = exponent - cached_decimal_exponent;
+ DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent);
+ input.Multiply(adjustment_power);
+ if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) {
+ // The product of input with the adjustment power fits into a 64 bit
+ // integer.
+ ASSERT(DiyFp::kSignificandSize == 64);
+ } else {
+ // The adjustment power is exact. There is hence only an error of 0.5.
+ error += kDenominator / 2;
+ }
+ }
+
+ input.Multiply(cached_power);
+ // The error introduced by a multiplication of a*b equals
+ // error_a + error_b + error_a*error_b/2^64 + 0.5
+ // Substituting a with 'input' and b with 'cached_power' we have
+ // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp),
+ // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64
+ int error_b = kDenominator / 2;
+ int error_ab = (error == 0 ? 0 : 1); // We round up to 1.
+ int fixed_error = kDenominator / 2;
+ error += error_b + error_ab + fixed_error;
+
+ old_e = input.e();
+ input.Normalize();
+ error <<= old_e - input.e();
+
+ // See if the double's significand changes if we add/subtract the error.
+ int order_of_magnitude = DiyFp::kSignificandSize + input.e();
+ int effective_significand_size =
+ Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude);
+ int precision_digits_count =
+ DiyFp::kSignificandSize - effective_significand_size;
+ if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) {
+ // This can only happen for very small denormals. In this case the
+ // half-way multiplied by the denominator exceeds the range of an uint64.
+ // Simply shift everything to the right.
+ int shift_amount = (precision_digits_count + kDenominatorLog) -
+ DiyFp::kSignificandSize + 1;
+ input.set_f(input.f() >> shift_amount);
+ input.set_e(input.e() + shift_amount);
+ // We add 1 for the lost precision of error, and kDenominator for
+ // the lost precision of input.f().
+ error = (error >> shift_amount) + 1 + kDenominator;
+ precision_digits_count -= shift_amount;
+ }
+ // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too.
+ ASSERT(DiyFp::kSignificandSize == 64);
+ ASSERT(precision_digits_count < 64);
+ uint64_t one64 = 1;
+ uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1;
+ uint64_t precision_bits = input.f() & precision_bits_mask;
+ uint64_t half_way = one64 << (precision_digits_count - 1);
+ precision_bits *= kDenominator;
+ half_way *= kDenominator;
+ DiyFp rounded_input(input.f() >> precision_digits_count,
+ input.e() + precision_digits_count);
+ if (precision_bits >= half_way + error) {
+ rounded_input.set_f(rounded_input.f() + 1);
+ }
+ // If the last_bits are too close to the half-way case than we are too
+ // inaccurate and round down. In this case we return false so that we can
+ // fall back to a more precise algorithm.
+
+ *result = Double(rounded_input).value();
+ if (half_way - error < precision_bits && precision_bits < half_way + error) {
+ // Too imprecise. The caller will have to fall back to a slower version.
+ // However the returned number is guaranteed to be either the correct
+ // double, or the next-lower double.
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+// Returns the correct double for the buffer*10^exponent.
+// The variable guess should be a close guess that is either the correct double
+// or its lower neighbor (the nearest double less than the correct one).
+// Preconditions:
+// buffer.length() + exponent <= kMaxDecimalPower + 1
+// buffer.length() + exponent > kMinDecimalPower
+// buffer.length() <= kMaxDecimalSignificantDigits
+static double BignumStrtod(Vector<const char> buffer,
+ int exponent,
+ double guess) {
+ if (guess == V8_INFINITY) {
+ return guess;
+ }
+
+ DiyFp upper_boundary = Double(guess).UpperBoundary();
+
+ ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1);
+ ASSERT(buffer.length() + exponent > kMinDecimalPower);
+ ASSERT(buffer.length() <= kMaxSignificantDecimalDigits);
+ // Make sure that the Bignum will be able to hold all our numbers.
+ // Our Bignum implementation has a separate field for exponents. Shifts will
+ // consume at most one bigit (< 64 bits).
+ // ln(10) == 3.3219...
+ ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits);
+ Bignum input;
+ Bignum boundary;
+ input.AssignDecimalString(buffer);
+ boundary.AssignUInt64(upper_boundary.f());
+ if (exponent >= 0) {
+ input.MultiplyByPowerOfTen(exponent);
+ } else {
+ boundary.MultiplyByPowerOfTen(-exponent);
+ }
+ if (upper_boundary.e() > 0) {
+ boundary.ShiftLeft(upper_boundary.e());
+ } else {
+ input.ShiftLeft(-upper_boundary.e());
+ }
+ int comparison = Bignum::Compare(input, boundary);
+ if (comparison < 0) {
+ return guess;
+ } else if (comparison > 0) {
+ return Double(guess).NextDouble();
+ } else if ((Double(guess).Significand() & 1) == 0) {
+ // Round towards even.
+ return guess;
+ } else {
+ return Double(guess).NextDouble();
+ }
+}
+
+
+double Strtod(Vector<const char> buffer, int exponent) {
+ Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
+ Vector<const char> trimmed = TrimTrailingZeros(left_trimmed);
+ exponent += left_trimmed.length() - trimmed.length();
+ if (trimmed.length() == 0) return 0.0;
+ if (trimmed.length() > kMaxSignificantDecimalDigits) {
+ char significant_buffer[kMaxSignificantDecimalDigits];
+ int significant_exponent;
+ TrimToMaxSignificantDigits(trimmed, exponent,
+ significant_buffer, &significant_exponent);
+ return Strtod(Vector<const char>(significant_buffer,
+ kMaxSignificantDecimalDigits),
+ significant_exponent);
+ }
+ if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) return V8_INFINITY;
+ if (exponent + trimmed.length() <= kMinDecimalPower) return 0.0;
+
+ double guess;
+ if (DoubleStrtod(trimmed, exponent, &guess) ||
+ DiyFpStrtod(trimmed, exponent, &guess)) {
+ return guess;
+ }
+ return BignumStrtod(trimmed, exponent, guess);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/strtod.h b/chromium/v8/src/strtod.h
new file mode 100644
index 00000000000..1a5a96c8ebe
--- /dev/null
+++ b/chromium/v8/src/strtod.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef V8_STRTOD_H_
+#define V8_STRTOD_H_
+
+namespace v8 {
+namespace internal {
+
+// The buffer must only contain digits in the range [0-9]. It must not
+// contain a dot or a sign. It must not start with '0', and must not be empty.
+double Strtod(Vector<const char> buffer, int exponent);
+
+} } // namespace v8::internal
+
+#endif // V8_STRTOD_H_
diff --git a/chromium/v8/src/stub-cache.cc b/chromium/v8/src/stub-cache.cc
new file mode 100644
index 00000000000..9e29a95ebf4
--- /dev/null
+++ b/chromium/v8/src/stub-cache.cc
@@ -0,0 +1,2200 @@
+// Copyright 2012 the V8 project authors. 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 "api.h"
+#include "arguments.h"
+#include "ast.h"
+#include "code-stubs.h"
+#include "cpu-profiler.h"
+#include "gdb-jit.h"
+#include "ic-inl.h"
+#include "stub-cache.h"
+#include "vm-state-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// -----------------------------------------------------------------------
+// StubCache implementation.
+
+
+StubCache::StubCache(Isolate* isolate)
+ : isolate_(isolate) {
+ ASSERT(isolate == Isolate::Current());
+}
+
+
+void StubCache::Initialize() {
+ ASSERT(IsPowerOf2(kPrimaryTableSize));
+ ASSERT(IsPowerOf2(kSecondaryTableSize));
+ Clear();
+}
+
+
+Code* StubCache::Set(Name* name, Map* map, Code* code) {
+ // Get the flags from the code.
+ Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
+
+ // Validate that the name does not move on scavenge, and that we
+ // can use identity checks instead of structural equality checks.
+ ASSERT(!heap()->InNewSpace(name));
+ ASSERT(name->IsUniqueName());
+
+ // The state bits are not important to the hash function because
+ // the stub cache only contains monomorphic stubs. Make sure that
+ // the bits are the least significant so they will be the ones
+ // masked out.
+ ASSERT(Code::ExtractICStateFromFlags(flags) == MONOMORPHIC);
+ STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1);
+
+ // Make sure that the code type is not included in the hash.
+ ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
+
+ // Compute the primary entry.
+ int primary_offset = PrimaryOffset(name, flags, map);
+ Entry* primary = entry(primary_, primary_offset);
+ Code* old_code = primary->value;
+
+ // If the primary entry has useful data in it, we retire it to the
+ // secondary cache before overwriting it.
+ if (old_code != isolate_->builtins()->builtin(Builtins::kIllegal)) {
+ Map* old_map = primary->map;
+ Code::Flags old_flags = Code::RemoveTypeFromFlags(old_code->flags());
+ int seed = PrimaryOffset(primary->key, old_flags, old_map);
+ int secondary_offset = SecondaryOffset(primary->key, old_flags, seed);
+ Entry* secondary = entry(secondary_, secondary_offset);
+ *secondary = *primary;
+ }
+
+ // Update primary cache.
+ primary->key = name;
+ primary->value = code;
+ primary->map = map;
+ isolate()->counters()->megamorphic_stub_cache_updates()->Increment();
+ return code;
+}
+
+
+Handle<JSObject> StubCache::StubHolder(Handle<JSObject> receiver,
+ Handle<JSObject> holder) {
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(*receiver, *holder);
+ return Handle<JSObject>(IC::GetCodeCacheHolder(
+ isolate_, *receiver, cache_holder));
+}
+
+
+Handle<Code> StubCache::FindIC(Handle<Name> name,
+ Handle<Map> stub_holder_map,
+ Code::Kind kind,
+ Code::StubType type,
+ Code::ExtraICState extra_state) {
+ Code::Flags flags = Code::ComputeMonomorphicFlags(kind, extra_state, type);
+ Handle<Object> probe(stub_holder_map->FindInCodeCache(*name, flags),
+ isolate_);
+ if (probe->IsCode()) return Handle<Code>::cast(probe);
+ return Handle<Code>::null();
+}
+
+
+Handle<Code> StubCache::FindIC(Handle<Name> name,
+ Handle<JSObject> stub_holder,
+ Code::Kind kind,
+ Code::StubType type,
+ Code::ExtraICState extra_ic_state) {
+ return FindIC(name, Handle<Map>(stub_holder->map()), kind,
+ type, extra_ic_state);
+}
+
+
+Handle<Code> StubCache::FindLoadHandler(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> stub_holder,
+ Code::Kind kind,
+ Code::StubType type) {
+ Code::ExtraICState extra_ic_state = Code::ComputeExtraICState(
+ receiver.is_identical_to(stub_holder) ? Code::OWN_STUB
+ : Code::PROTOTYPE_STUB);
+ ASSERT(type != Code::NORMAL);
+ Code::Flags flags = Code::ComputeMonomorphicFlags(
+ Code::STUB, extra_ic_state, type, kind);
+ Handle<Object> probe(stub_holder->map()->FindInCodeCache(*name, flags),
+ isolate_);
+ if (probe->IsCode()) return Handle<Code>::cast(probe);
+ return Handle<Code>::null();
+}
+
+
+Handle<Code> StubCache::FindStoreHandler(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Code::Kind kind,
+ Code::StubType type,
+ StrictModeFlag strict_mode) {
+ Code::ExtraICState extra_ic_state = Code::ComputeExtraICState(
+ STANDARD_STORE, strict_mode);
+ ASSERT(type != Code::NORMAL);
+ Code::Flags flags = Code::ComputeMonomorphicFlags(
+ Code::STUB, extra_ic_state, type, kind);
+ Handle<Object> probe(receiver->map()->FindInCodeCache(*name, flags),
+ isolate_);
+ if (probe->IsCode()) return Handle<Code>::cast(probe);
+ return Handle<Code>::null();
+}
+
+
+Handle<Code> StubCache::ComputeMonomorphicLoadIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<Name> name) {
+ Handle<Code> ic = FindIC(name, receiver, Code::LOAD_IC, handler->type());
+ if (!ic.is_null()) return ic;
+
+ LoadStubCompiler ic_compiler(isolate());
+ ic = ic_compiler.CompileMonomorphicIC(
+ Handle<Map>(receiver->map()), handler, name);
+
+ JSObject::UpdateMapCodeCache(receiver, name, ic);
+ return ic;
+}
+
+
+Handle<Code> StubCache::ComputeMonomorphicKeyedLoadIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<Name> name) {
+ Handle<Code> ic = FindIC(
+ name, receiver, Code::KEYED_LOAD_IC, handler->type());
+ if (!ic.is_null()) return ic;
+
+ KeyedLoadStubCompiler ic_compiler(isolate());
+ ic = ic_compiler.CompileMonomorphicIC(
+ Handle<Map>(receiver->map()), handler, name);
+
+ JSObject::UpdateMapCodeCache(receiver, name, ic);
+ return ic;
+}
+
+
+Handle<Code> StubCache::ComputeMonomorphicStoreIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<Name> name,
+ StrictModeFlag strict_mode) {
+ Handle<Code> ic = FindIC(
+ name, receiver, Code::STORE_IC, handler->type(), strict_mode);
+ if (!ic.is_null()) return ic;
+
+ StoreStubCompiler ic_compiler(isolate(), strict_mode);
+ ic = ic_compiler.CompileMonomorphicIC(
+ Handle<Map>(receiver->map()), handler, name);
+
+ JSObject::UpdateMapCodeCache(receiver, name, ic);
+ return ic;
+}
+
+
+Handle<Code> StubCache::ComputeMonomorphicKeyedStoreIC(
+ Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<Name> name,
+ StrictModeFlag strict_mode) {
+ Handle<Code> ic = FindIC(
+ name, receiver, Code::KEYED_STORE_IC, handler->type(), strict_mode);
+ if (!ic.is_null()) return ic;
+
+ KeyedStoreStubCompiler ic_compiler(isolate(), strict_mode, STANDARD_STORE);
+ ic = ic_compiler.CompileMonomorphicIC(
+ Handle<Map>(receiver->map()), handler, name);
+
+ JSObject::UpdateMapCodeCache(receiver, name, ic);
+ return ic;
+}
+
+
+Handle<Code> StubCache::ComputeLoadNonexistent(Handle<Name> name,
+ Handle<JSObject> receiver) {
+ // 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
+ // and we use the empty string for the map cache in that case. If
+ // 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.
+ Handle<Name> cache_name = factory()->empty_string();
+ Handle<JSObject> current;
+ Handle<Object> next = receiver;
+ Handle<GlobalObject> global;
+ do {
+ current = Handle<JSObject>::cast(next);
+ next = Handle<Object>(current->GetPrototype(), isolate_);
+ if (current->IsGlobalObject()) {
+ global = Handle<GlobalObject>::cast(current);
+ cache_name = name;
+ } else if (!current->HasFastProperties()) {
+ cache_name = name;
+ }
+ } while (!next->IsNull());
+
+ // Compile the stub that is either shared for all names or
+ // name specific if there are global objects involved.
+ Handle<Code> handler = FindLoadHandler(
+ cache_name, receiver, receiver, Code::LOAD_IC, Code::NONEXISTENT);
+ if (!handler.is_null()) return handler;
+
+ LoadStubCompiler compiler(isolate_);
+ handler =
+ compiler.CompileLoadNonexistent(receiver, current, cache_name, global);
+ JSObject::UpdateMapCodeCache(receiver, cache_name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeLoadField(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ PropertyIndex field,
+ Representation representation) {
+ if (receiver.is_identical_to(holder)) {
+ LoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ return stub.GetCode(isolate());
+ }
+
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> stub = FindLoadHandler(
+ name, receiver, stub_holder, Code::LOAD_IC, Code::FIELD);
+ if (!stub.is_null()) return stub;
+
+ LoadStubCompiler compiler(isolate_);
+ Handle<Code> handler =
+ compiler.CompileLoadField(receiver, holder, name, field, representation);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeLoadCallback(
+ Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ Handle<ExecutableAccessorInfo> callback) {
+ ASSERT(v8::ToCData<Address>(callback->getter()) != 0);
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> stub = FindLoadHandler(
+ name, receiver, stub_holder, Code::LOAD_IC, Code::CALLBACKS);
+ if (!stub.is_null()) return stub;
+
+ LoadStubCompiler compiler(isolate_);
+ Handle<Code> handler =
+ compiler.CompileLoadCallback(receiver, holder, name, callback);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeLoadViaGetter(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ Handle<JSFunction> getter) {
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> stub = FindLoadHandler(
+ name, receiver, stub_holder, Code::LOAD_IC, Code::CALLBACKS);
+ if (!stub.is_null()) return stub;
+
+ LoadStubCompiler compiler(isolate_);
+ Handle<Code> handler =
+ compiler.CompileLoadViaGetter(receiver, holder, name, getter);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeLoadConstant(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ Handle<Object> value) {
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> handler = FindLoadHandler(
+ name, receiver, stub_holder, Code::LOAD_IC, Code::CONSTANT);
+ if (!handler.is_null()) return handler;
+
+ LoadStubCompiler compiler(isolate_);
+ handler = compiler.CompileLoadConstant(receiver, holder, name, value);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeLoadInterceptor(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder) {
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> stub = FindLoadHandler(
+ name, receiver, stub_holder, Code::LOAD_IC, Code::INTERCEPTOR);
+ if (!stub.is_null()) return stub;
+
+ LoadStubCompiler compiler(isolate_);
+ Handle<Code> handler =
+ compiler.CompileLoadInterceptor(receiver, holder, name);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeLoadNormal(Handle<Name> name,
+ Handle<JSObject> receiver) {
+ return isolate_->builtins()->LoadIC_Normal();
+}
+
+
+Handle<Code> StubCache::ComputeLoadGlobal(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ bool is_dont_delete) {
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> stub = FindIC(name, stub_holder, Code::LOAD_IC, Code::NORMAL);
+ if (!stub.is_null()) return stub;
+
+ LoadStubCompiler compiler(isolate_);
+ Handle<Code> ic =
+ compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete);
+ JSObject::UpdateMapCodeCache(stub_holder, name, ic);
+ return ic;
+}
+
+
+Handle<Code> StubCache::ComputeKeyedLoadField(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ PropertyIndex field,
+ Representation representation) {
+ if (receiver.is_identical_to(holder)) {
+ // TODO(titzer): this should use an HObjectAccess
+ KeyedLoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ return stub.GetCode(isolate());
+ }
+
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> stub = FindLoadHandler(
+ name, receiver, stub_holder, Code::KEYED_LOAD_IC, Code::FIELD);
+ if (!stub.is_null()) return stub;
+
+ KeyedLoadStubCompiler compiler(isolate_);
+ Handle<Code> handler =
+ compiler.CompileLoadField(receiver, holder, name, field, representation);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeKeyedLoadConstant(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ Handle<Object> value) {
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> handler = FindLoadHandler(
+ name, receiver, stub_holder, Code::KEYED_LOAD_IC,
+ Code::CONSTANT);
+ if (!handler.is_null()) return handler;
+
+ KeyedLoadStubCompiler compiler(isolate_);
+ handler = compiler.CompileLoadConstant(receiver, holder, name, value);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeKeyedLoadInterceptor(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder) {
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> stub = FindLoadHandler(
+ name, receiver, stub_holder, Code::KEYED_LOAD_IC, Code::INTERCEPTOR);
+ if (!stub.is_null()) return stub;
+
+ KeyedLoadStubCompiler compiler(isolate_);
+ Handle<Code> handler =
+ compiler.CompileLoadInterceptor(receiver, holder, name);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeKeyedLoadCallback(
+ Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ Handle<ExecutableAccessorInfo> callback) {
+ Handle<JSObject> stub_holder = StubHolder(receiver, holder);
+ Handle<Code> stub = FindLoadHandler(
+ name, receiver, stub_holder, Code::KEYED_LOAD_IC, Code::CALLBACKS);
+ if (!stub.is_null()) return stub;
+
+ KeyedLoadStubCompiler compiler(isolate_);
+ Handle<Code> handler =
+ compiler.CompileLoadCallback(receiver, holder, name, callback);
+ JSObject::UpdateMapCodeCache(stub_holder, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeStoreField(Handle<Name> name,
+ Handle<JSObject> receiver,
+ LookupResult* lookup,
+ StrictModeFlag strict_mode) {
+ Handle<Code> stub = FindStoreHandler(
+ name, receiver, Code::STORE_IC, Code::FIELD, strict_mode);
+ if (!stub.is_null()) return stub;
+
+ StoreStubCompiler compiler(isolate_, strict_mode);
+ Handle<Code> handler = compiler.CompileStoreField(receiver, lookup, name);
+ JSObject::UpdateMapCodeCache(receiver, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeStoreTransition(Handle<Name> name,
+ Handle<JSObject> receiver,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ StrictModeFlag strict_mode) {
+ Handle<Code> stub = FindStoreHandler(
+ name, receiver, Code::STORE_IC, Code::MAP_TRANSITION, strict_mode);
+ if (!stub.is_null()) return stub;
+
+ StoreStubCompiler compiler(isolate_, strict_mode);
+ Handle<Code> handler =
+ compiler.CompileStoreTransition(receiver, lookup, transition, name);
+ JSObject::UpdateMapCodeCache(receiver, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeKeyedLoadElement(Handle<Map> receiver_map) {
+ Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC);
+ Handle<Name> name =
+ isolate()->factory()->KeyedLoadElementMonomorphic_string();
+
+ Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate_);
+ if (probe->IsCode()) return Handle<Code>::cast(probe);
+
+ KeyedLoadStubCompiler compiler(isolate());
+ Handle<Code> code = compiler.CompileLoadElement(receiver_map);
+
+ Map::UpdateCodeCache(receiver_map, name, code);
+ return code;
+}
+
+
+Handle<Code> StubCache::ComputeKeyedStoreElement(
+ Handle<Map> receiver_map,
+ StrictModeFlag strict_mode,
+ KeyedAccessStoreMode store_mode) {
+ Code::ExtraICState extra_state =
+ Code::ComputeExtraICState(store_mode, strict_mode);
+ Code::Flags flags = Code::ComputeMonomorphicFlags(
+ Code::KEYED_STORE_IC, extra_state);
+
+ ASSERT(store_mode == STANDARD_STORE ||
+ store_mode == STORE_AND_GROW_NO_TRANSITION ||
+ store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS ||
+ store_mode == STORE_NO_TRANSITION_HANDLE_COW);
+
+ Handle<String> name =
+ isolate()->factory()->KeyedStoreElementMonomorphic_string();
+ Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate_);
+ if (probe->IsCode()) return Handle<Code>::cast(probe);
+
+ KeyedStoreStubCompiler compiler(isolate(), strict_mode, store_mode);
+ Handle<Code> code = compiler.CompileStoreElement(receiver_map);
+
+ Map::UpdateCodeCache(receiver_map, name, code);
+ ASSERT(Code::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode);
+ return code;
+}
+
+
+Handle<Code> StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) {
+ return (strict_mode == kStrictMode)
+ ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict()
+ : isolate_->builtins()->Builtins::StoreIC_Normal();
+}
+
+
+Handle<Code> StubCache::ComputeStoreGlobal(Handle<Name> name,
+ Handle<GlobalObject> receiver,
+ Handle<PropertyCell> cell,
+ Handle<Object> value,
+ StrictModeFlag strict_mode) {
+ Isolate* isolate = cell->GetIsolate();
+ Handle<Type> union_type(PropertyCell::UpdateType(cell, value), isolate);
+ bool is_constant = union_type->IsConstant();
+ StoreGlobalStub stub(strict_mode, is_constant);
+
+ Handle<Code> code = FindIC(
+ name, Handle<JSObject>::cast(receiver),
+ Code::STORE_IC, Code::NORMAL, stub.GetExtraICState());
+ if (!code.is_null()) return code;
+
+ // Replace the placeholder cell and global object map with the actual global
+ // cell and receiver map.
+ Handle<Map> meta_map(isolate_->heap()->meta_map());
+ Handle<Object> receiver_map(receiver->map(), isolate_);
+ code = stub.GetCodeCopyFromTemplate(isolate_);
+ code->ReplaceNthObject(1, *meta_map, *receiver_map);
+ Handle<Map> cell_map(isolate_->heap()->global_property_cell_map());
+ code->ReplaceNthObject(1, *cell_map, *cell);
+
+ JSObject::UpdateMapCodeCache(receiver, name, code);
+
+ return code;
+}
+
+
+Handle<Code> StubCache::ComputeStoreCallback(
+ Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ Handle<ExecutableAccessorInfo> callback,
+ StrictModeFlag strict_mode) {
+ ASSERT(v8::ToCData<Address>(callback->setter()) != 0);
+ Handle<Code> stub = FindStoreHandler(
+ name, receiver, Code::STORE_IC, Code::CALLBACKS, strict_mode);
+ if (!stub.is_null()) return stub;
+
+ StoreStubCompiler compiler(isolate_, strict_mode);
+ Handle<Code> handler = compiler.CompileStoreCallback(
+ receiver, holder, name, callback);
+ JSObject::UpdateMapCodeCache(receiver, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeStoreViaSetter(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ Handle<JSFunction> setter,
+ StrictModeFlag strict_mode) {
+ Handle<Code> stub = FindStoreHandler(
+ name, receiver, Code::STORE_IC, Code::CALLBACKS, strict_mode);
+ if (!stub.is_null()) return stub;
+
+ StoreStubCompiler compiler(isolate_, strict_mode);
+ Handle<Code> handler = compiler.CompileStoreViaSetter(
+ receiver, holder, name, setter);
+ JSObject::UpdateMapCodeCache(receiver, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeStoreInterceptor(Handle<Name> name,
+ Handle<JSObject> receiver,
+ StrictModeFlag strict_mode) {
+ Handle<Code> stub = FindStoreHandler(
+ name, receiver, Code::STORE_IC, Code::INTERCEPTOR, strict_mode);
+ if (!stub.is_null()) return stub;
+
+ StoreStubCompiler compiler(isolate_, strict_mode);
+ Handle<Code> handler = compiler.CompileStoreInterceptor(receiver, name);
+ JSObject::UpdateMapCodeCache(receiver, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeKeyedStoreField(Handle<Name> name,
+ Handle<JSObject> receiver,
+ LookupResult* lookup,
+ StrictModeFlag strict_mode) {
+ Handle<Code> stub = FindStoreHandler(
+ name, receiver, Code::KEYED_STORE_IC, Code::FIELD, strict_mode);
+ if (!stub.is_null()) return stub;
+
+ KeyedStoreStubCompiler compiler(isolate(), strict_mode, STANDARD_STORE);
+ Handle<Code> handler = compiler.CompileStoreField(receiver, lookup, name);
+ JSObject::UpdateMapCodeCache(receiver, name, handler);
+ return handler;
+}
+
+
+Handle<Code> StubCache::ComputeKeyedStoreTransition(
+ Handle<Name> name,
+ Handle<JSObject> receiver,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ StrictModeFlag strict_mode) {
+ Handle<Code> stub = FindStoreHandler(
+ name, receiver, Code::KEYED_STORE_IC, Code::MAP_TRANSITION, strict_mode);
+ if (!stub.is_null()) return stub;
+
+ KeyedStoreStubCompiler compiler(isolate(), strict_mode, STANDARD_STORE);
+ Handle<Code> handler =
+ compiler.CompileStoreTransition(receiver, lookup, transition, name);
+ JSObject::UpdateMapCodeCache(receiver, name, handler);
+ return handler;
+}
+
+
+#define CALL_LOGGER_TAG(kind, type) \
+ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type)
+
+Handle<Code> StubCache::ComputeCallConstant(int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state,
+ Handle<Name> name,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<JSFunction> function) {
+ // Compute the check type and the map.
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(*object, *holder);
+ Handle<JSObject> stub_holder(IC::GetCodeCacheHolder(
+ isolate_, *object, cache_holder));
+
+ // Compute check type based on receiver/holder.
+ CheckType check = RECEIVER_MAP_CHECK;
+ if (object->IsString()) {
+ check = STRING_CHECK;
+ } else if (object->IsSymbol()) {
+ check = SYMBOL_CHECK;
+ } else if (object->IsNumber()) {
+ check = NUMBER_CHECK;
+ } else if (object->IsBoolean()) {
+ check = BOOLEAN_CHECK;
+ }
+
+ if (check != RECEIVER_MAP_CHECK &&
+ !function->IsBuiltin() &&
+ function->shared()->is_classic_mode()) {
+ // Calling non-strict non-builtins with a value as the receiver
+ // requires boxing.
+ return Handle<Code>::null();
+ }
+
+ Code::Flags flags = Code::ComputeMonomorphicFlags(
+ kind, extra_state, Code::CONSTANT, argc, cache_holder);
+ Handle<Object> probe(stub_holder->map()->FindInCodeCache(*name, flags),
+ isolate_);
+ if (probe->IsCode()) return Handle<Code>::cast(probe);
+
+ CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder);
+ Handle<Code> code =
+ compiler.CompileCallConstant(object, holder, name, check, function);
+ code->set_check_type(check);
+ ASSERT(flags == code->flags());
+ PROFILE(isolate_,
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name));
+ GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code));
+
+ if (CallStubCompiler::CanBeCached(function)) {
+ JSObject::UpdateMapCodeCache(stub_holder, name, code);
+ }
+ return code;
+}
+
+
+Handle<Code> StubCache::ComputeCallField(int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state,
+ Handle<Name> name,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ PropertyIndex index) {
+ // Compute the check type and the map.
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(*object, *holder);
+ Handle<JSObject> stub_holder(IC::GetCodeCacheHolder(
+ isolate_, *object, cache_holder));
+
+ // TODO(1233596): We cannot do receiver map check for non-JS objects
+ // because they may be represented as immediates without a
+ // map. Instead, we check against the map in the holder.
+ if (object->IsNumber() || object->IsSymbol() ||
+ object->IsBoolean() || object->IsString()) {
+ object = holder;
+ }
+
+ Code::Flags flags = Code::ComputeMonomorphicFlags(
+ kind, extra_state, Code::FIELD, argc, cache_holder);
+ Handle<Object> probe(stub_holder->map()->FindInCodeCache(*name, flags),
+ isolate_);
+ 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(flags == code->flags());
+ PROFILE(isolate_,
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name));
+ GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code));
+ JSObject::UpdateMapCodeCache(stub_holder, name, code);
+ return code;
+}
+
+
+Handle<Code> StubCache::ComputeCallInterceptor(int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state,
+ Handle<Name> name,
+ Handle<Object> object,
+ Handle<JSObject> holder) {
+ // Compute the check type and the map.
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(*object, *holder);
+ Handle<JSObject> stub_holder(IC::GetCodeCacheHolder(
+ isolate_, *object, cache_holder));
+
+ // TODO(1233596): We cannot do receiver map check for non-JS objects
+ // because they may be represented as immediates without a
+ // map. Instead, we check against the map in the holder.
+ if (object->IsNumber() || object->IsSymbol() ||
+ object->IsBoolean() || object->IsString()) {
+ object = holder;
+ }
+
+ Code::Flags flags = Code::ComputeMonomorphicFlags(
+ kind, extra_state, Code::INTERCEPTOR, argc, cache_holder);
+ Handle<Object> probe(stub_holder->map()->FindInCodeCache(*name, flags),
+ isolate_);
+ 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(flags == code->flags());
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name));
+ GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code));
+ JSObject::UpdateMapCodeCache(stub_holder, name, code);
+ return code;
+}
+
+
+Handle<Code> StubCache::ComputeCallGlobal(int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state,
+ Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ Handle<JSFunction> function) {
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(*receiver, *holder);
+ Handle<JSObject> stub_holder(IC::GetCodeCacheHolder(
+ isolate_, *receiver, cache_holder));
+ Code::Flags flags = Code::ComputeMonomorphicFlags(
+ kind, extra_state, Code::NORMAL, argc, cache_holder);
+ Handle<Object> probe(stub_holder->map()->FindInCodeCache(*name, flags),
+ isolate_);
+ 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(flags == code->flags());
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name));
+ GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code));
+ if (CallStubCompiler::CanBeCached(function)) {
+ JSObject::UpdateMapCodeCache(stub_holder, name, code);
+ }
+ return 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);
+}
+
+
+Code* StubCache::FindCallInitialize(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, Code::NORMAL, argc);
+ UnseededNumberDictionary* dictionary =
+ isolate()->heap()->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*>(code);
+}
+
+
+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, Code::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) {
+ return ComputeCallInitialize(argc, mode, Code::CALL_IC);
+}
+
+
+Handle<Code> StubCache::ComputeKeyedCallInitialize(int argc) {
+ return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET,
+ Code::KEYED_CALL_IC);
+}
+
+
+Handle<Code> StubCache::ComputeCallPreMonomorphic(
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state) {
+ Code::Flags flags =
+ Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::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;
+}
+
+
+Handle<Code> StubCache::ComputeCallNormal(int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state) {
+ Code::Flags flags =
+ Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::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;
+}
+
+
+Handle<Code> StubCache::ComputeCallArguments(int argc) {
+ Code::Flags flags =
+ Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC,
+ Code::kNoExtraICState, Code::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;
+}
+
+
+Handle<Code> StubCache::ComputeCallMegamorphic(
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state) {
+ Code::Flags flags =
+ Code::ComputeFlags(kind, MEGAMORPHIC, extra_state,
+ Code::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;
+}
+
+
+Handle<Code> StubCache::ComputeCallMiss(int argc,
+ Code::Kind kind,
+ 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_state,
+ Code::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;
+}
+
+
+Handle<Code> StubCache::ComputeCompareNil(Handle<Map> receiver_map,
+ CompareNilICStub& stub) {
+ Handle<String> name(isolate_->heap()->empty_string());
+ if (!receiver_map->is_shared()) {
+ Handle<Code> cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC,
+ Code::NORMAL, stub.GetExtraICState());
+ if (!cached_ic.is_null()) return cached_ic;
+ }
+
+ Handle<Code> ic = stub.GetCodeCopyFromTemplate(isolate_);
+ ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map);
+
+ if (!receiver_map->is_shared()) {
+ Map::UpdateCodeCache(receiver_map, name, ic);
+ }
+
+ return ic;
+}
+
+
+Handle<Code> StubCache::ComputeLoadElementPolymorphic(
+ MapHandleList* receiver_maps) {
+ Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC);
+ Handle<PolymorphicCodeCache> cache =
+ isolate_->factory()->polymorphic_code_cache();
+ Handle<Object> probe = cache->Lookup(receiver_maps, flags);
+ if (probe->IsCode()) return Handle<Code>::cast(probe);
+
+ CodeHandleList handlers(receiver_maps->length());
+ KeyedLoadStubCompiler compiler(isolate_);
+ compiler.CompileElementHandlers(receiver_maps, &handlers);
+ Handle<Code> code = compiler.CompilePolymorphicIC(
+ receiver_maps, &handlers, factory()->empty_string(),
+ Code::NORMAL, ELEMENT);
+
+ isolate()->counters()->keyed_load_polymorphic_stubs()->Increment();
+
+ PolymorphicCodeCache::Update(cache, receiver_maps, flags, code);
+ return code;
+}
+
+
+Handle<Code> StubCache::ComputePolymorphicLoadIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name) {
+ LoadStubCompiler ic_compiler(isolate_);
+ Code::StubType type = number_of_valid_maps == 1 ? handlers->at(0)->type()
+ : Code::NORMAL;
+ Handle<Code> ic = ic_compiler.CompilePolymorphicIC(
+ receiver_maps, handlers, name, type, PROPERTY);
+ return ic;
+}
+
+
+Handle<Code> StubCache::ComputePolymorphicStoreIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name,
+ StrictModeFlag strict_mode) {
+ StoreStubCompiler ic_compiler(isolate_, strict_mode);
+ Code::StubType type = number_of_valid_maps == 1 ? handlers->at(0)->type()
+ : Code::NORMAL;
+ Handle<Code> ic = ic_compiler.CompilePolymorphicIC(
+ receiver_maps, handlers, name, type, PROPERTY);
+ return ic;
+}
+
+
+Handle<Code> StubCache::ComputeStoreElementPolymorphic(
+ MapHandleList* receiver_maps,
+ KeyedAccessStoreMode store_mode,
+ StrictModeFlag strict_mode) {
+ ASSERT(store_mode == STANDARD_STORE ||
+ store_mode == STORE_AND_GROW_NO_TRANSITION ||
+ store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS ||
+ store_mode == STORE_NO_TRANSITION_HANDLE_COW);
+ Handle<PolymorphicCodeCache> cache =
+ isolate_->factory()->polymorphic_code_cache();
+ Code::ExtraICState extra_state = Code::ComputeExtraICState(store_mode,
+ strict_mode);
+ Code::Flags flags =
+ Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state);
+ Handle<Object> probe = cache->Lookup(receiver_maps, flags);
+ if (probe->IsCode()) return Handle<Code>::cast(probe);
+
+ KeyedStoreStubCompiler compiler(isolate_, strict_mode, store_mode);
+ Handle<Code> code = compiler.CompileStoreElementPolymorphic(receiver_maps);
+ PolymorphicCodeCache::Update(cache, receiver_maps, flags, code);
+ return code;
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+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_STUB, DEBUG_BREAK,
+ Code::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;
+}
+
+
+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_STUB, DEBUG_PREPARE_STEP_IN,
+ Code::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].map = NULL;
+ primary_[i].value = empty;
+ }
+ for (int j = 0; j < kSecondaryTableSize; j++) {
+ secondary_[j].key = heap()->empty_string();
+ secondary_[j].map = NULL;
+ secondary_[j].value = empty;
+ }
+}
+
+
+void StubCache::CollectMatchingMaps(SmallMapList* types,
+ Handle<Name> name,
+ Code::Flags flags,
+ Handle<Context> native_context,
+ Zone* zone) {
+ for (int i = 0; i < kPrimaryTableSize; i++) {
+ if (primary_[i].key == *name) {
+ Map* map = primary_[i].map;
+ // Map can be NULL, if the stub is constant function call
+ // with a primitive receiver.
+ if (map == NULL) continue;
+
+ int offset = PrimaryOffset(*name, flags, map);
+ if (entry(primary_, offset) == &primary_[i] &&
+ !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) {
+ types->AddMapIfMissing(Handle<Map>(map), zone);
+ }
+ }
+ }
+
+ for (int i = 0; i < kSecondaryTableSize; i++) {
+ if (secondary_[i].key == *name) {
+ Map* map = secondary_[i].map;
+ // Map can be NULL, if the stub is constant function call
+ // with a primitive receiver.
+ if (map == NULL) continue;
+
+ // Lookup in primary table and skip duplicates.
+ int primary_offset = PrimaryOffset(*name, flags, map);
+
+ // Lookup in secondary table and add matches.
+ int offset = SecondaryOffset(*name, flags, primary_offset);
+ if (entry(secondary_, offset) == &secondary_[i] &&
+ !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) {
+ types->AddMapIfMissing(Handle<Map>(map), zone);
+ }
+ }
+ }
+}
+
+
+// ------------------------------------------------------------------------
+// StubCompiler implementation.
+
+
+RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) {
+ JSObject* recv = JSObject::cast(args[0]);
+ ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]);
+ Address setter_address = v8::ToCData<Address>(callback->setter());
+ v8::AccessorSetter fun = FUNCTION_CAST<v8::AccessorSetter>(setter_address);
+ ASSERT(fun != NULL);
+ ASSERT(callback->IsCompatibleReceiver(recv));
+ Handle<Name> name = args.at<Name>(2);
+ Handle<Object> value = args.at<Object>(3);
+ HandleScope scope(isolate);
+
+ // TODO(rossberg): Support symbols in the API.
+ if (name->IsSymbol()) return *value;
+ Handle<String> str = Handle<String>::cast(name);
+
+ LOG(isolate, ApiNamedPropertyAccess("store", recv, *name));
+ PropertyCallbackArguments
+ custom_args(isolate, callback->data(), recv, recv);
+ custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return *value;
+}
+
+
+static const int kAccessorInfoOffsetInInterceptorArgs = 2;
+
+
+/**
+ * Attempts to load a property with an interceptor (which must be present),
+ * but doesn't search the prototype chain.
+ *
+ * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't
+ * provide any value for the given name.
+ */
+RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) {
+ typedef PropertyCallbackArguments PCA;
+ static const int kArgsOffset = kAccessorInfoOffsetInInterceptorArgs;
+ Handle<Name> name_handle = args.at<Name>(0);
+ Handle<InterceptorInfo> interceptor_info = args.at<InterceptorInfo>(1);
+ ASSERT(kArgsOffset == 2);
+ // No ReturnValue in interceptors.
+ ASSERT_EQ(kArgsOffset + PCA::kArgsLength - 2, args.length());
+
+ // TODO(rossberg): Support symbols in the API.
+ if (name_handle->IsSymbol())
+ return isolate->heap()->no_interceptor_result_sentinel();
+ Handle<String> name = Handle<String>::cast(name_handle);
+
+ Address getter_address = v8::ToCData<Address>(interceptor_info->getter());
+ v8::NamedPropertyGetter getter =
+ FUNCTION_CAST<v8::NamedPropertyGetter>(getter_address);
+ ASSERT(getter != NULL);
+
+ Handle<JSObject> receiver =
+ args.at<JSObject>(kArgsOffset - PCA::kThisIndex);
+ Handle<JSObject> holder =
+ args.at<JSObject>(kArgsOffset - PCA::kHolderIndex);
+ PropertyCallbackArguments callback_args(isolate,
+ interceptor_info->data(),
+ *receiver,
+ *holder);
+ {
+ // Use the interceptor getter.
+ HandleScope scope(isolate);
+ v8::Handle<v8::Value> r =
+ callback_args.Call(getter, v8::Utils::ToLocal(name));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!r.IsEmpty()) {
+ Handle<Object> result = v8::Utils::OpenHandle(*r);
+ result->VerifyApiCallResultType();
+ return *v8::Utils::OpenHandle(*r);
+ }
+ }
+
+ return isolate->heap()->no_interceptor_result_sentinel();
+}
+
+
+static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) {
+ // If the load is non-contextual, just return the undefined result.
+ // Note that both keyed and non-keyed loads may end up here, so we
+ // can't use either LoadIC or KeyedLoadIC constructors.
+ IC ic(IC::NO_EXTRA_FRAME, isolate);
+ ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub());
+ if (!ic.SlowIsUndeclaredGlobal()) return HEAP->undefined_value();
+
+ // Throw a reference error.
+ HandleScope scope(isolate);
+ Handle<Name> name_handle(name);
+ Handle<Object> error =
+ isolate->factory()->NewReferenceError("not_defined",
+ HandleVector(&name_handle, 1));
+ return isolate->Throw(*error);
+}
+
+
+static MaybeObject* LoadWithInterceptor(Arguments* args,
+ PropertyAttributes* attrs) {
+ typedef PropertyCallbackArguments PCA;
+ static const int kArgsOffset = kAccessorInfoOffsetInInterceptorArgs;
+ Handle<Name> name_handle = args->at<Name>(0);
+ Handle<InterceptorInfo> interceptor_info = args->at<InterceptorInfo>(1);
+ ASSERT(kArgsOffset == 2);
+ // No ReturnValue in interceptors.
+ ASSERT_EQ(kArgsOffset + PCA::kArgsLength - 2, args->length());
+ Handle<JSObject> receiver_handle =
+ args->at<JSObject>(kArgsOffset - PCA::kThisIndex);
+ Handle<JSObject> holder_handle =
+ args->at<JSObject>(kArgsOffset - PCA::kHolderIndex);
+
+ Isolate* isolate = receiver_handle->GetIsolate();
+
+ // TODO(rossberg): Support symbols in the API.
+ if (name_handle->IsSymbol())
+ return holder_handle->GetPropertyPostInterceptor(
+ *receiver_handle, *name_handle, attrs);
+ Handle<String> name = Handle<String>::cast(name_handle);
+
+ Address getter_address = v8::ToCData<Address>(interceptor_info->getter());
+ v8::NamedPropertyGetter getter =
+ FUNCTION_CAST<v8::NamedPropertyGetter>(getter_address);
+ ASSERT(getter != NULL);
+
+ PropertyCallbackArguments callback_args(isolate,
+ interceptor_info->data(),
+ *receiver_handle,
+ *holder_handle);
+ {
+ // Use the interceptor getter.
+ HandleScope scope(isolate);
+ v8::Handle<v8::Value> r =
+ callback_args.Call(getter, v8::Utils::ToLocal(name));
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ if (!r.IsEmpty()) {
+ *attrs = NONE;
+ Handle<Object> result = v8::Utils::OpenHandle(*r);
+ result->VerifyApiCallResultType();
+ return *result;
+ }
+ }
+
+ MaybeObject* result = holder_handle->GetPropertyPostInterceptor(
+ *receiver_handle,
+ *name_handle,
+ attrs);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ return result;
+}
+
+
+/**
+ * Loads a property with an interceptor performing post interceptor
+ * lookup if interceptor failed.
+ */
+RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) {
+ PropertyAttributes attr = NONE;
+ Object* result;
+ { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+
+ // If the property is present, return it.
+ if (attr != ABSENT) return result;
+ return ThrowReferenceError(isolate, Name::cast(args[0]));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) {
+ PropertyAttributes attr;
+ MaybeObject* result = LoadWithInterceptor(&args, &attr);
+ RETURN_IF_SCHEDULED_EXCEPTION(isolate);
+ // This is call IC. In this case, we simply return the undefined result which
+ // will lead to an exception when trying to invoke the result as a
+ // function.
+ return result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) {
+ ASSERT(args.length() == 4);
+ JSObject* recv = JSObject::cast(args[0]);
+ Name* name = Name::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(recv->HasNamedInterceptor());
+ PropertyAttributes attr = NONE;
+ MaybeObject* result = recv->SetPropertyWithInterceptor(
+ name, value, attr, strict_mode);
+ return result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) {
+ JSObject* receiver = JSObject::cast(args[0]);
+ ASSERT(args.smi_at(1) >= 0);
+ uint32_t index = args.smi_at(1);
+ return receiver->GetElementWithInterceptor(receiver, index);
+}
+
+
+Handle<Code> StubCompiler::CompileCallInitialize(Code::Flags flags) {
+ int argc = Code::ExtractArgumentsCountFromFlags(flags);
+ Code::Kind kind = Code::ExtractKindFromFlags(flags);
+ Code::ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags);
+ if (kind == Code::CALL_IC) {
+ CallIC::GenerateInitialize(masm(), argc, extra_state);
+ } else {
+ KeyedCallIC::GenerateInitialize(masm(), argc);
+ }
+ Handle<Code> code = GetCodeWithFlags(flags, "CompileCallInitialize");
+ isolate()->counters()->call_initialize_stubs()->Increment();
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG),
+ *code, code->arguments_count()));
+ GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code));
+ return code;
+}
+
+
+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_state = Code::ExtractExtraICStateFromFlags(flags);
+ if (kind == Code::CALL_IC) {
+ CallIC::GenerateInitialize(masm(), argc, extra_state);
+ } else {
+ KeyedCallIC::GenerateInitialize(masm(), argc);
+ }
+ Handle<Code> code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic");
+ isolate()->counters()->call_premonomorphic_stubs()->Increment();
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG),
+ *code, code->arguments_count()));
+ GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code));
+ return code;
+}
+
+
+Handle<Code> StubCompiler::CompileCallNormal(Code::Flags flags) {
+ int argc = Code::ExtractArgumentsCountFromFlags(flags);
+ Code::Kind kind = Code::ExtractKindFromFlags(flags);
+ if (kind == Code::CALL_IC) {
+ // Call normal is always with a explict receiver.
+ ASSERT(!CallIC::Contextual::decode(
+ Code::ExtractExtraICStateFromFlags(flags)));
+ CallIC::GenerateNormal(masm(), argc);
+ } else {
+ KeyedCallIC::GenerateNormal(masm(), argc);
+ }
+ Handle<Code> code = GetCodeWithFlags(flags, "CompileCallNormal");
+ isolate()->counters()->call_normal_stubs()->Increment();
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG),
+ *code, code->arguments_count()));
+ GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code));
+ return code;
+}
+
+
+Handle<Code> StubCompiler::CompileCallMegamorphic(Code::Flags flags) {
+ int argc = Code::ExtractArgumentsCountFromFlags(flags);
+ Code::Kind kind = Code::ExtractKindFromFlags(flags);
+ Code::ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags);
+ if (kind == Code::CALL_IC) {
+ CallIC::GenerateMegamorphic(masm(), argc, extra_state);
+ } else {
+ KeyedCallIC::GenerateMegamorphic(masm(), argc);
+ }
+ Handle<Code> code = GetCodeWithFlags(flags, "CompileCallMegamorphic");
+ isolate()->counters()->call_megamorphic_stubs()->Increment();
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG),
+ *code, code->arguments_count()));
+ GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code));
+ return code;
+}
+
+
+Handle<Code> StubCompiler::CompileCallArguments(Code::Flags flags) {
+ int argc = Code::ExtractArgumentsCountFromFlags(flags);
+ KeyedCallIC::GenerateNonStrictArguments(masm(), argc);
+ Handle<Code> code = GetCodeWithFlags(flags, "CompileCallArguments");
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags),
+ CALL_MEGAMORPHIC_TAG),
+ *code, code->arguments_count()));
+ GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code));
+ return code;
+}
+
+
+Handle<Code> StubCompiler::CompileCallMiss(Code::Flags flags) {
+ int argc = Code::ExtractArgumentsCountFromFlags(flags);
+ Code::Kind kind = Code::ExtractKindFromFlags(flags);
+ Code::ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags);
+ if (kind == Code::CALL_IC) {
+ CallIC::GenerateMiss(masm(), argc, extra_state);
+ } else {
+ KeyedCallIC::GenerateMiss(masm(), argc);
+ }
+ Handle<Code> code = GetCodeWithFlags(flags, "CompileCallMiss");
+ isolate()->counters()->call_megamorphic_stubs()->Increment();
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG),
+ *code, code->arguments_count()));
+ GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code));
+ return code;
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+Handle<Code> StubCompiler::CompileCallDebugBreak(Code::Flags flags) {
+ Debug::GenerateCallICDebugBreak(masm());
+ Handle<Code> code = GetCodeWithFlags(flags, "CompileCallDebugBreak");
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags),
+ CALL_DEBUG_BREAK_TAG),
+ *code, code->arguments_count()));
+ return code;
+}
+
+
+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) {
+ // For the debugger extra ic state is irrelevant.
+ CallIC::GenerateMiss(masm(), argc, Code::kNoExtraICState);
+ } else {
+ KeyedCallIC::GenerateMiss(masm(), argc);
+ }
+ Handle<Code> code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn");
+ PROFILE(isolate(),
+ CodeCreateEvent(
+ CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG),
+ *code,
+ code->arguments_count()));
+ return code;
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+#undef CALL_LOGGER_TAG
+
+
+Handle<Code> StubCompiler::GetCodeWithFlags(Code::Flags flags,
+ const char* name) {
+ // Create code object in the heap.
+ CodeDesc desc;
+ masm_.GetCode(&desc);
+ Handle<Code> code = factory()->NewCode(desc, flags, masm_.CodeObject());
+#ifdef ENABLE_DISASSEMBLER
+ if (FLAG_print_code_stubs) code->Disassemble(name);
+#endif
+ return code;
+}
+
+
+Handle<Code> StubCompiler::GetCodeWithFlags(Code::Flags flags,
+ Handle<Name> name) {
+ return (FLAG_print_code_stubs && !name.is_null() && name->IsString())
+ ? GetCodeWithFlags(flags, *Handle<String>::cast(name)->ToCString())
+ : GetCodeWithFlags(flags, NULL);
+}
+
+
+void StubCompiler::LookupPostInterceptor(Handle<JSObject> holder,
+ Handle<Name> name,
+ LookupResult* lookup) {
+ holder->LocalLookupRealNamedProperty(*name, lookup);
+ if (lookup->IsFound()) return;
+ if (holder->GetPrototype()->IsNull()) return;
+ holder->GetPrototype()->Lookup(*name, lookup);
+}
+
+
+#define __ ACCESS_MASM(masm())
+
+
+Register BaseLoadStubCompiler::HandlerFrontendHeader(
+ Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss) {
+ return CheckPrototypes(object, object_reg, holder,
+ scratch1(), scratch2(), scratch3(),
+ name, miss, SKIP_RECEIVER);
+}
+
+
+// HandlerFrontend for store uses the name register. It has to be restored
+// before a miss.
+Register BaseStoreStubCompiler::HandlerFrontendHeader(
+ Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss) {
+ return CheckPrototypes(object, object_reg, holder,
+ this->name(), scratch1(), scratch2(),
+ name, miss, SKIP_RECEIVER);
+}
+
+
+Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* success) {
+ Label miss;
+
+ Register reg = HandlerFrontendHeader(object, object_reg, holder, name, &miss);
+
+ HandlerFrontendFooter(name, success, &miss);
+ return reg;
+}
+
+
+Handle<Code> BaseLoadStubCompiler::CompileLoadField(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ PropertyIndex field,
+ Representation representation) {
+ Label miss;
+
+ Register reg = HandlerFrontendHeader(object, receiver(), holder, name, &miss);
+
+ GenerateLoadField(reg, holder, field, representation);
+
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetCode(kind(), Code::FIELD, name);
+}
+
+
+Handle<Code> BaseLoadStubCompiler::CompileLoadConstant(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<Object> value) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+ GenerateLoadConstant(value);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CONSTANT, name);
+}
+
+
+Handle<Code> BaseLoadStubCompiler::CompileLoadCallback(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label success;
+
+ Register reg = CallbackHandlerFrontend(
+ object, receiver(), holder, name, &success, callback);
+ __ bind(&success);
+ GenerateLoadCallback(reg, callback);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
+Handle<Code> BaseLoadStubCompiler::CompileLoadInterceptor(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name) {
+ Label success;
+
+ LookupResult lookup(isolate());
+ LookupPostInterceptor(holder, name, &lookup);
+
+ Register reg = HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+ // TODO(368): Compile in the whole chain: all the interceptors in
+ // prototypes and ultimate answer.
+ GenerateLoadInterceptor(reg, object, holder, &lookup, name);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::INTERCEPTOR, name);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadPostInterceptor(
+ Register interceptor_reg,
+ Handle<JSObject> interceptor_holder,
+ Handle<Name> name,
+ LookupResult* lookup) {
+ Label success;
+ Handle<JSObject> holder(lookup->holder());
+ if (lookup->IsField()) {
+ PropertyIndex field = lookup->GetFieldIndex();
+ if (interceptor_holder.is_identical_to(holder)) {
+ GenerateLoadField(
+ interceptor_reg, holder, field, lookup->representation());
+ } else {
+ // We found FIELD property in prototype chain of interceptor's holder.
+ // Retrieve a field from field's holder.
+ Register reg = HandlerFrontend(
+ interceptor_holder, interceptor_reg, holder, name, &success);
+ __ bind(&success);
+ GenerateLoadField(
+ reg, holder, field, lookup->representation());
+ }
+ } else {
+ // We found CALLBACKS property in prototype chain of interceptor's
+ // holder.
+ ASSERT(lookup->type() == CALLBACKS);
+ Handle<ExecutableAccessorInfo> callback(
+ ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
+ ASSERT(callback->getter() != NULL);
+
+ Register reg = CallbackHandlerFrontend(
+ interceptor_holder, interceptor_reg, holder, name, &success, callback);
+ __ bind(&success);
+ GenerateLoadCallback(reg, callback);
+ }
+}
+
+
+Handle<Code> BaseLoadStoreStubCompiler::CompileMonomorphicIC(
+ Handle<Map> receiver_map,
+ Handle<Code> handler,
+ Handle<Name> name) {
+ MapHandleList receiver_maps(1);
+ receiver_maps.Add(receiver_map);
+ CodeHandleList handlers(1);
+ handlers.Add(handler);
+ Code::StubType type = handler->type();
+ return CompilePolymorphicIC(&receiver_maps, &handlers, name, type, PROPERTY);
+}
+
+
+Handle<Code> LoadStubCompiler::CompileLoadViaGetter(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<JSFunction> getter) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+
+ __ bind(&success);
+ GenerateLoadViaGetter(masm(), getter);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
+Handle<Code> BaseStoreStubCompiler::CompileStoreTransition(
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ Handle<Name> name) {
+ Label miss, slow;
+
+ // Ensure no transitions to deprecated maps are followed.
+ __ CheckMapDeprecated(transition, scratch1(), &miss);
+
+ // Check that we are allowed to write this.
+ if (object->GetPrototype()->IsJSObject()) {
+ Handle<JSObject> holder;
+ // holder == object indicates that no property was found.
+ if (lookup->holder() != *object) {
+ holder = Handle<JSObject>(lookup->holder());
+ } else {
+ // Find the top object.
+ holder = object;
+ do {
+ holder = Handle<JSObject>(JSObject::cast(holder->GetPrototype()));
+ } while (holder->GetPrototype()->IsJSObject());
+ }
+
+ Register holder_reg =
+ HandlerFrontendHeader(object, receiver(), holder, name, &miss);
+
+ // If no property was found, and the holder (the last object in the
+ // prototype chain) is in slow mode, we need to do a negative lookup on the
+ // holder.
+ if (lookup->holder() == *object) {
+ GenerateNegativeHolderLookup(masm(), holder, holder_reg, name, &miss);
+ }
+ }
+
+ GenerateStoreTransition(masm(),
+ object,
+ lookup,
+ transition,
+ name,
+ receiver(), this->name(), value(),
+ scratch1(), scratch2(), scratch3(),
+ &miss,
+ &slow);
+
+ // Handle store cache miss.
+ GenerateRestoreName(masm(), &miss, name);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ GenerateRestoreName(masm(), &slow, name);
+ TailCallBuiltin(masm(), SlowBuiltin(kind()));
+
+ // Return the generated code.
+ return GetCode(kind(), Code::MAP_TRANSITION, name);
+}
+
+
+Handle<Code> BaseStoreStubCompiler::CompileStoreField(Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Name> name) {
+ Label miss;
+
+ HandlerFrontendHeader(object, receiver(), object, name, &miss);
+
+ // Generate store field code.
+ GenerateStoreField(masm(),
+ object,
+ lookup,
+ receiver(), this->name(), value(), scratch1(), scratch2(),
+ &miss);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetCode(kind(), Code::FIELD, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<JSFunction> setter) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+
+ __ bind(&success);
+ GenerateStoreViaSetter(masm(), setter);
+
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
+Handle<Code> KeyedLoadStubCompiler::CompileLoadElement(
+ Handle<Map> receiver_map) {
+ ElementsKind elements_kind = receiver_map->elements_kind();
+ if (receiver_map->has_fast_elements() ||
+ receiver_map->has_external_array_elements()) {
+ Handle<Code> stub = KeyedLoadFastElementStub(
+ receiver_map->instance_type() == JS_ARRAY_TYPE,
+ elements_kind).GetCode(isolate());
+ __ DispatchMap(receiver(), scratch1(), receiver_map, stub, DO_SMI_CHECK);
+ } else {
+ Handle<Code> stub =
+ KeyedLoadDictionaryElementStub().GetCode(isolate());
+ __ DispatchMap(receiver(), scratch1(), receiver_map, stub, DO_SMI_CHECK);
+ }
+
+ TailCallBuiltin(masm(), Builtins::kKeyedLoadIC_Miss);
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, factory()->empty_string());
+}
+
+
+Handle<Code> KeyedStoreStubCompiler::CompileStoreElement(
+ Handle<Map> receiver_map) {
+ ElementsKind elements_kind = receiver_map->elements_kind();
+ bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ Handle<Code> stub;
+ if (FLAG_compiled_keyed_stores &&
+ (receiver_map->has_fast_elements() ||
+ receiver_map->has_external_array_elements())) {
+ stub = KeyedStoreFastElementStub(
+ is_jsarray,
+ elements_kind,
+ store_mode_).GetCode(isolate());
+ } else {
+ stub = KeyedStoreElementStub(is_jsarray,
+ elements_kind,
+ store_mode_).GetCode(isolate());
+ }
+
+ __ DispatchMap(receiver(), scratch1(), receiver_map, stub, DO_SMI_CHECK);
+
+ TailCallBuiltin(masm(), Builtins::kKeyedStoreIC_Miss);
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, factory()->empty_string());
+}
+
+
+#undef __
+
+
+void StubCompiler::TailCallBuiltin(MacroAssembler* masm, Builtins::Name name) {
+ Handle<Code> code(masm->isolate()->builtins()->builtin(name));
+ GenerateTailCall(masm, code);
+}
+
+
+void LoadStubCompiler::JitEvent(Handle<Name> name, Handle<Code> code) {
+ GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code));
+}
+
+
+void KeyedLoadStubCompiler::JitEvent(Handle<Name> name, Handle<Code> code) {
+ GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code));
+}
+
+
+void StoreStubCompiler::JitEvent(Handle<Name> name, Handle<Code> code) {
+ GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code));
+}
+
+
+void KeyedStoreStubCompiler::JitEvent(Handle<Name> name, Handle<Code> code) {
+ GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code));
+}
+
+
+Handle<Code> BaseLoadStoreStubCompiler::GetICCode(Code::Kind kind,
+ Code::StubType type,
+ Handle<Name> name,
+ InlineCacheState state) {
+ Code::Flags flags = Code::ComputeFlags(
+ kind, state, extra_state(), type);
+ Handle<Code> code = GetCodeWithFlags(flags, name);
+ PROFILE(isolate(), CodeCreateEvent(log_kind(code), *code, *name));
+ JitEvent(name, code);
+ return code;
+}
+
+
+Handle<Code> BaseLoadStubCompiler::GetCode(Code::Kind kind,
+ Code::StubType type,
+ Handle<Name> name) {
+ ASSERT(type != Code::NORMAL);
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, Code::kNoExtraICState, type, kind);
+ Handle<Code> code = GetCodeWithFlags(flags, name);
+ PROFILE(isolate(), CodeCreateEvent(log_kind(code), *code, *name));
+ JitEvent(name, code);
+ return code;
+}
+
+
+Handle<Code> BaseStoreStubCompiler::GetCode(Code::Kind kind,
+ Code::StubType type,
+ Handle<Name> name) {
+ ASSERT(type != Code::NORMAL);
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, extra_state(), type, kind);
+ Handle<Code> code = GetCodeWithFlags(flags, name);
+ PROFILE(isolate(), CodeCreateEvent(log_kind(code), *code, *name));
+ JitEvent(name, code);
+ return code;
+}
+
+
+void KeyedLoadStubCompiler::CompileElementHandlers(MapHandleList* receiver_maps,
+ CodeHandleList* handlers) {
+ for (int i = 0; i < receiver_maps->length(); ++i) {
+ Handle<Map> receiver_map = receiver_maps->at(i);
+ Handle<Code> cached_stub;
+
+ if ((receiver_map->instance_type() & kNotStringTag) == 0) {
+ cached_stub = isolate()->builtins()->KeyedLoadIC_String();
+ } else {
+ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ ElementsKind elements_kind = receiver_map->elements_kind();
+
+ if (IsFastElementsKind(elements_kind) ||
+ IsExternalArrayElementsKind(elements_kind)) {
+ cached_stub =
+ KeyedLoadFastElementStub(is_js_array,
+ elements_kind).GetCode(isolate());
+ } else {
+ ASSERT(elements_kind == DICTIONARY_ELEMENTS);
+ cached_stub = KeyedLoadDictionaryElementStub().GetCode(isolate());
+ }
+ }
+
+ handlers->Add(cached_stub);
+ }
+}
+
+
+Handle<Code> KeyedStoreStubCompiler::CompileStoreElementPolymorphic(
+ MapHandleList* receiver_maps) {
+ // Collect MONOMORPHIC stubs for all |receiver_maps|.
+ CodeHandleList handlers(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);
+
+ // TODO(mvstanton): The code below is doing pessimistic elements
+ // transitions. I would like to stop doing that and rely on Allocation Site
+ // Tracking to do a better job of ensuring the data types are what they need
+ // to be. Not all the elements are in place yet, pessimistic elements
+ // transitions are still important for performance.
+ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ ElementsKind elements_kind = receiver_map->elements_kind();
+ if (!transitioned_map.is_null()) {
+ cached_stub = ElementsTransitionAndStoreStub(
+ elements_kind,
+ transitioned_map->elements_kind(),
+ is_js_array,
+ store_mode_).GetCode(isolate());
+ } else {
+ if (FLAG_compiled_keyed_stores &&
+ (receiver_map->has_fast_elements() ||
+ receiver_map->has_external_array_elements())) {
+ cached_stub = KeyedStoreFastElementStub(
+ is_js_array,
+ elements_kind,
+ store_mode_).GetCode(isolate());
+ } else {
+ cached_stub = KeyedStoreElementStub(
+ is_js_array,
+ elements_kind,
+ store_mode_).GetCode(isolate());
+ }
+ }
+ ASSERT(!cached_stub.is_null());
+ handlers.Add(cached_stub);
+ transitioned_maps.Add(transitioned_map);
+ }
+ Handle<Code> code =
+ CompileStorePolymorphic(receiver_maps, &handlers, &transitioned_maps);
+ isolate()->counters()->keyed_store_polymorphic_stubs()->Increment();
+ PROFILE(isolate(),
+ CodeCreateEvent(Logger::KEYED_STORE_POLYMORPHIC_IC_TAG, *code, 0));
+ return code;
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreDictionaryElement(
+ MacroAssembler* masm) {
+ KeyedStoreIC::GenerateSlow(masm);
+}
+
+
+CallStubCompiler::CallStubCompiler(Isolate* isolate,
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state,
+ InlineCacheHolderFlag cache_holder)
+ : StubCompiler(isolate),
+ arguments_(argc),
+ kind_(kind),
+ extra_state_(extra_state),
+ cache_holder_(cache_holder) {
+}
+
+
+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);
+ return optimization.is_simple_api_call();
+}
+
+
+bool CallStubCompiler::CanBeCached(Handle<JSFunction> function) {
+ if (function->shared()->HasBuiltinFunctionId()) {
+ BuiltinFunctionId id = function->shared()->builtin_function_id();
+#define CALL_GENERATOR_CASE(name) if (id == k##name) return false;
+ SITE_SPECIFIC_CALL_GENERATORS(CALL_GENERATOR_CASE)
+#undef CALL_GENERATOR_CASE
+ }
+
+ return true;
+}
+
+
+Handle<Code> CallStubCompiler::CompileCustomCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> fname,
+ Code::StubType type) {
+ ASSERT(HasCustomCallGenerator(function));
+
+ 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, \
+ type); \
+ }
+ CUSTOM_CALL_IC_GENERATORS(CALL_GENERATOR_CASE)
+#undef CALL_GENERATOR_CASE
+ }
+ CallOptimization optimization(function);
+ ASSERT(optimization.is_simple_api_call());
+ return CompileFastApiCall(optimization,
+ object,
+ holder,
+ cell,
+ function,
+ fname);
+}
+
+
+Handle<Code> CallStubCompiler::GetCode(Code::StubType type,
+ Handle<Name> name) {
+ int argc = arguments_.immediate();
+ Code::Flags flags = Code::ComputeMonomorphicFlags(kind_,
+ extra_state_,
+ type,
+ argc,
+ cache_holder_);
+ return GetCodeWithFlags(flags, name);
+}
+
+
+Handle<Code> CallStubCompiler::GetCode(Handle<JSFunction> function) {
+ Handle<String> function_name;
+ if (function->shared()->name()->IsString()) {
+ function_name = Handle<String>(String::cast(function->shared()->name()));
+ }
+ return GetCode(Code::CONSTANT, function_name);
+}
+
+
+CallOptimization::CallOptimization(LookupResult* lookup) {
+ if (lookup->IsFound() &&
+ lookup->IsCacheable() &&
+ lookup->IsConstantFunction()) {
+ // We only optimize constant function calls.
+ Initialize(Handle<JSFunction>(lookup->GetConstantFunction()));
+ } else {
+ Initialize(Handle<JSFunction>::null());
+ }
+}
+
+
+CallOptimization::CallOptimization(Handle<JSFunction> function) {
+ Initialize(function);
+}
+
+
+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.is_identical_to(holder)) {
+ if (object->IsInstanceOf(*expected_receiver_type_)) return depth;
+ object = Handle<JSObject>(JSObject::cast(object->GetPrototype()));
+ if (!object->map()->is_hidden_prototype()) return kInvalidProtoDepth;
+ ++depth;
+ }
+ if (holder->IsInstanceOf(*expected_receiver_type_)) return depth;
+ return kInvalidProtoDepth;
+}
+
+
+void CallOptimization::Initialize(Handle<JSFunction> function) {
+ constant_function_ = Handle<JSFunction>::null();
+ is_simple_api_call_ = false;
+ expected_receiver_type_ = Handle<FunctionTemplateInfo>::null();
+ api_call_info_ = Handle<CallHandlerInfo>::null();
+
+ if (function.is_null() || !function->is_compiled()) return;
+
+ constant_function_ = function;
+ AnalyzePossibleApiFunction(function);
+}
+
+
+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_ =
+ 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()) {
+ Handle<SignatureInfo> signature =
+ Handle<SignatureInfo>(SignatureInfo::cast(info->signature()));
+ if (!signature->args()->IsUndefined()) return;
+ if (!signature->receiver()->IsUndefined()) {
+ expected_receiver_type_ =
+ Handle<FunctionTemplateInfo>(
+ FunctionTemplateInfo::cast(signature->receiver()));
+ }
+ }
+
+ is_simple_api_call_ = true;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/stub-cache.h b/chromium/v8/src/stub-cache.h
new file mode 100644
index 00000000000..bd0678ed3a7
--- /dev/null
+++ b/chromium/v8/src/stub-cache.h
@@ -0,0 +1,1192 @@
+// Copyright 2012 the V8 project authors. 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_STUB_CACHE_H_
+#define V8_STUB_CACHE_H_
+
+#include "allocation.h"
+#include "arguments.h"
+#include "code-stubs.h"
+#include "ic-inl.h"
+#include "macro-assembler.h"
+#include "objects.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+// The stub cache is used for megamorphic calls and property accesses.
+// It maps (map, name, type)->Code*
+
+// The design of the table uses the inline cache stubs used for
+// mono-morphic calls. The beauty of this, we do not have to
+// invalidate the cache whenever a prototype map is changed. The stub
+// validates the map chain as in the mono-morphic case.
+
+class SmallMapList;
+class StubCache;
+
+
+class SCTableReference {
+ public:
+ Address address() const { return address_; }
+
+ private:
+ explicit SCTableReference(Address address) : address_(address) {}
+
+ Address address_;
+
+ friend class StubCache;
+};
+
+
+class StubCache {
+ public:
+ struct Entry {
+ Name* key;
+ Code* value;
+ Map* map;
+ };
+
+ void Initialize();
+
+ Handle<JSObject> StubHolder(Handle<JSObject> receiver,
+ Handle<JSObject> holder);
+
+ Handle<Code> FindIC(Handle<Name> name,
+ Handle<Map> stub_holder_map,
+ Code::Kind kind,
+ Code::StubType type,
+ Code::ExtraICState extra_state = Code::kNoExtraICState);
+
+ Handle<Code> FindIC(Handle<Name> name,
+ Handle<JSObject> stub_holder,
+ Code::Kind kind,
+ Code::StubType type,
+ Code::ExtraICState extra_state = Code::kNoExtraICState);
+
+ Handle<Code> FindLoadHandler(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> stub_holder,
+ Code::Kind kind,
+ Code::StubType type);
+
+ Handle<Code> FindStoreHandler(Handle<Name> name,
+ Handle<JSObject> receiver,
+ Code::Kind kind,
+ Code::StubType type,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeMonomorphicLoadIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<Name> name);
+
+ Handle<Code> ComputeMonomorphicKeyedLoadIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<Name> name);
+
+ Handle<Code> ComputeMonomorphicStoreIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<Name> name,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeMonomorphicKeyedStoreIC(Handle<JSObject> receiver,
+ Handle<Code> handler,
+ Handle<Name> name,
+ StrictModeFlag strict_mode);
+
+ // Computes the right stub matching. Inserts the result in the
+ // cache before returning. This might compile a stub if needed.
+ Handle<Code> ComputeLoadNonexistent(Handle<Name> name,
+ Handle<JSObject> object);
+
+ Handle<Code> ComputeLoadField(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ PropertyIndex field_index,
+ Representation representation);
+
+ Handle<Code> ComputeLoadCallback(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<ExecutableAccessorInfo> callback);
+
+ Handle<Code> ComputeLoadViaGetter(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<JSFunction> getter);
+
+ Handle<Code> ComputeLoadConstant(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Object> value);
+
+ Handle<Code> ComputeLoadInterceptor(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder);
+
+ Handle<Code> ComputeLoadNormal(Handle<Name> name,
+ Handle<JSObject> object);
+
+ Handle<Code> ComputeLoadGlobal(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ bool is_dont_delete);
+
+ // ---
+
+ Handle<Code> ComputeKeyedLoadField(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ PropertyIndex field_index,
+ Representation representation);
+
+ Handle<Code> ComputeKeyedLoadCallback(
+ Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<ExecutableAccessorInfo> callback);
+
+ Handle<Code> ComputeKeyedLoadConstant(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Object> value);
+
+ Handle<Code> ComputeKeyedLoadInterceptor(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder);
+
+ // ---
+
+ Handle<Code> ComputeStoreField(Handle<Name> name,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeStoreTransition(Handle<Name> name,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeStoreNormal(StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeStoreGlobal(Handle<Name> name,
+ Handle<GlobalObject> object,
+ Handle<PropertyCell> cell,
+ Handle<Object> value,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeStoreCallback(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<ExecutableAccessorInfo> callback,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeStoreViaSetter(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<JSFunction> setter,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeStoreInterceptor(Handle<Name> name,
+ Handle<JSObject> object,
+ StrictModeFlag strict_mode);
+
+ // ---
+
+ Handle<Code> ComputeKeyedStoreField(Handle<Name> name,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ StrictModeFlag strict_mode);
+ Handle<Code> ComputeKeyedStoreTransition(Handle<Name> name,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputeKeyedLoadElement(Handle<Map> receiver_map);
+
+ Handle<Code> ComputeKeyedStoreElement(Handle<Map> receiver_map,
+ StrictModeFlag strict_mode,
+ KeyedAccessStoreMode store_mode);
+
+ // ---
+
+ Handle<Code> ComputeCallField(int argc,
+ Code::Kind,
+ Code::ExtraICState extra_state,
+ Handle<Name> name,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ PropertyIndex index);
+
+ Handle<Code> ComputeCallConstant(int argc,
+ Code::Kind,
+ Code::ExtraICState extra_state,
+ Handle<Name> name,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<JSFunction> function);
+
+ Handle<Code> ComputeCallInterceptor(int argc,
+ Code::Kind,
+ Code::ExtraICState extra_state,
+ Handle<Name> name,
+ Handle<Object> object,
+ Handle<JSObject> holder);
+
+ Handle<Code> ComputeCallGlobal(int argc,
+ Code::Kind,
+ Code::ExtraICState extra_state,
+ Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ Handle<JSFunction> function);
+
+ // ---
+
+ Handle<Code> ComputeCallInitialize(int argc, RelocInfo::Mode mode);
+
+ Handle<Code> ComputeKeyedCallInitialize(int argc);
+
+ Handle<Code> ComputeCallPreMonomorphic(int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state);
+
+ Handle<Code> ComputeCallNormal(int argc,
+ Code::Kind kind,
+ Code::ExtraICState state);
+
+ Handle<Code> ComputeCallArguments(int argc);
+
+ Handle<Code> ComputeCallMegamorphic(int argc,
+ Code::Kind kind,
+ Code::ExtraICState state);
+
+ Handle<Code> ComputeCallMiss(int argc,
+ Code::Kind kind,
+ Code::ExtraICState state);
+
+ // ---
+
+ Handle<Code> ComputeCompareNil(Handle<Map> receiver_map,
+ CompareNilICStub& stub);
+
+ // ---
+
+ Handle<Code> ComputeLoadElementPolymorphic(MapHandleList* receiver_maps);
+ Handle<Code> ComputeStoreElementPolymorphic(MapHandleList* receiver_maps,
+ KeyedAccessStoreMode store_mode,
+ StrictModeFlag strict_mode);
+
+ Handle<Code> ComputePolymorphicLoadIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name);
+
+ Handle<Code> ComputePolymorphicStoreIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ int number_of_valid_maps,
+ Handle<Name> name,
+ StrictModeFlag strict_mode);
+
+ // Finds the Code object stored in the Heap::non_monomorphic_cache().
+ Code* FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Handle<Code> ComputeCallDebugBreak(int argc, Code::Kind kind);
+
+ Handle<Code> ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind);
+#endif
+
+ // Update cache for entry hash(name, map).
+ Code* Set(Name* name, Map* map, Code* code);
+
+ // Clear the lookup table (@ mark compact collection).
+ void Clear();
+
+ // Collect all maps that match the name and flags.
+ void CollectMatchingMaps(SmallMapList* types,
+ Handle<Name> name,
+ Code::Flags flags,
+ Handle<Context> native_context,
+ Zone* zone);
+
+ // Generate code for probing the stub cache table.
+ // Arguments extra, extra2 and extra3 may be used to pass additional scratch
+ // registers. Set to no_reg if not needed.
+ void GenerateProbe(MacroAssembler* masm,
+ Code::Flags flags,
+ Register receiver,
+ Register name,
+ Register scratch,
+ Register extra,
+ Register extra2 = no_reg,
+ Register extra3 = no_reg);
+
+ enum Table {
+ kPrimary,
+ kSecondary
+ };
+
+
+ SCTableReference key_reference(StubCache::Table table) {
+ return SCTableReference(
+ reinterpret_cast<Address>(&first_entry(table)->key));
+ }
+
+
+ SCTableReference map_reference(StubCache::Table table) {
+ return SCTableReference(
+ reinterpret_cast<Address>(&first_entry(table)->map));
+ }
+
+
+ SCTableReference value_reference(StubCache::Table table) {
+ return SCTableReference(
+ reinterpret_cast<Address>(&first_entry(table)->value));
+ }
+
+
+ StubCache::Entry* first_entry(StubCache::Table table) {
+ switch (table) {
+ case StubCache::kPrimary: return StubCache::primary_;
+ case StubCache::kSecondary: return StubCache::secondary_;
+ }
+ UNREACHABLE();
+ return NULL;
+ }
+
+ Isolate* isolate() { return isolate_; }
+ Heap* heap() { return isolate()->heap(); }
+ Factory* factory() { return isolate()->factory(); }
+
+ private:
+ explicit StubCache(Isolate* isolate);
+
+ Handle<Code> ComputeCallInitialize(int argc,
+ RelocInfo::Mode mode,
+ Code::Kind kind);
+
+ // The stub cache has a primary and secondary level. The two levels have
+ // different hashing algorithms in order to avoid simultaneous collisions
+ // in both caches. Unlike a probing strategy (quadratic or otherwise) the
+ // update strategy on updates is fairly clear and simple: Any existing entry
+ // in the primary cache is moved to the secondary cache, and secondary cache
+ // entries are overwritten.
+
+ // Hash algorithm for the primary table. This algorithm is replicated in
+ // assembler for every architecture. Returns an index into the table that
+ // is scaled by 1 << kHeapObjectTagSize.
+ static int PrimaryOffset(Name* name, Code::Flags flags, Map* map) {
+ // This works well because the heap object tag size and the hash
+ // shift are equal. Shifting down the length field to get the
+ // hash code would effectively throw away two bits of the hash
+ // code.
+ STATIC_ASSERT(kHeapObjectTagSize == Name::kHashShift);
+ // Compute the hash of the name (use entire hash field).
+ ASSERT(name->HasHashCode());
+ uint32_t field = name->hash_field();
+ // Using only the low bits in 64-bit mode is unlikely to increase the
+ // risk of collision even if the heap is spread over an area larger than
+ // 4Gb (and not at all if it isn't).
+ uint32_t map_low32bits =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map));
+ // We always set the in_loop bit to zero when generating the lookup code
+ // so do it here too so the hash codes match.
+ uint32_t iflags =
+ (static_cast<uint32_t>(flags) & ~Code::kFlagsNotUsedInLookup);
+ // Base the offset on a simple combination of name, flags, and map.
+ uint32_t key = (map_low32bits + field) ^ iflags;
+ return key & ((kPrimaryTableSize - 1) << kHeapObjectTagSize);
+ }
+
+ // Hash algorithm for the secondary table. This algorithm is replicated in
+ // assembler for every architecture. Returns an index into the table that
+ // is scaled by 1 << kHeapObjectTagSize.
+ static int SecondaryOffset(Name* name, Code::Flags flags, int seed) {
+ // Use the seed from the primary cache in the secondary cache.
+ uint32_t name_low32bits =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name));
+ // We always set the in_loop bit to zero when generating the lookup code
+ // so do it here too so the hash codes match.
+ uint32_t iflags =
+ (static_cast<uint32_t>(flags) & ~Code::kFlagsNotUsedInLookup);
+ uint32_t key = (seed - name_low32bits) + iflags;
+ return key & ((kSecondaryTableSize - 1) << kHeapObjectTagSize);
+ }
+
+ // Compute the entry for a given offset in exactly the same way as
+ // we do in generated code. We generate an hash code that already
+ // ends in Name::kHashShift 0s. Then we multiply it so it is a multiple
+ // of sizeof(Entry). This makes it easier to avoid making mistakes
+ // in the hashed offset computations.
+ static Entry* entry(Entry* table, int offset) {
+ const int multiplier = sizeof(*table) >> Name::kHashShift;
+ return reinterpret_cast<Entry*>(
+ reinterpret_cast<Address>(table) + offset * multiplier);
+ }
+
+ 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);
+};
+
+
+// ------------------------------------------------------------------------
+
+
+// Support functions for IC stubs for callbacks.
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty);
+
+
+// Support functions for IC stubs for interceptors.
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, CallInterceptorProperty);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor);
+
+
+enum PrototypeCheckType { CHECK_ALL_MAPS, SKIP_RECEIVER };
+enum IcCheckType { ELEMENT, PROPERTY };
+
+
+// The stub compilers compile stubs for the stub cache.
+class StubCompiler BASE_EMBEDDED {
+ public:
+ 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
+ Handle<Code> CompileCallDebugBreak(Code::Flags flags);
+ Handle<Code> CompileCallDebugPrepareStepIn(Code::Flags flags);
+#endif
+
+ // Static functions for generating parts of stubs.
+ static void GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
+ int index,
+ Register prototype);
+
+ // Generates prototype loading code that uses the objects from the
+ // context we were in when this function was called. If the context
+ // has changed, a jump to miss is performed. This ties the generated
+ // code to a particular context and so must not be used in cases
+ // where the generated code is not allowed to have references to
+ // objects from a context.
+ static void GenerateDirectLoadGlobalFunctionPrototype(MacroAssembler* masm,
+ int index,
+ Register prototype,
+ Label* miss);
+
+ static void GenerateFastPropertyLoad(MacroAssembler* masm,
+ Register dst,
+ Register src,
+ bool inobject,
+ int index,
+ Representation representation);
+
+ static void GenerateLoadArrayLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch,
+ Label* miss_label);
+
+ static void GenerateLoadStringLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label,
+ bool support_wrappers);
+
+ static void GenerateLoadFunctionPrototype(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label);
+
+ static void TailCallBuiltin(MacroAssembler* masm, Builtins::Name name);
+
+ // Generates code that verifies that the property holder has not changed
+ // (checking maps of objects in the prototype chain for fast and global
+ // objects or doing negative lookup for slow objects, ensures that the
+ // property cells for global objects are still empty) and checks that the map
+ // of the holder has not changed. If necessary the function also generates
+ // code for security check in case of global object holders. Helps to make
+ // sure that the current IC is still valid.
+ //
+ // The scratch and holder registers are always clobbered, but the object
+ // register is only clobbered if it the same as the holder register. The
+ // function returns a register containing the holder - either object_reg or
+ // holder_reg.
+ // The function can optionally (when save_at_depth !=
+ // kInvalidProtoDepth) save the object at the given depth by moving
+ // it to [esp + kPointerSize].
+ Register CheckPrototypes(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ Handle<Name> name,
+ Label* miss,
+ PrototypeCheckType check = CHECK_ALL_MAPS) {
+ return CheckPrototypes(object, object_reg, holder, holder_reg, scratch1,
+ scratch2, name, kInvalidProtoDepth, miss, check);
+ }
+
+ Register CheckPrototypes(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ Handle<Name> name,
+ int save_at_depth,
+ Label* miss,
+ PrototypeCheckType check = CHECK_ALL_MAPS);
+
+
+ protected:
+ Handle<Code> GetCodeWithFlags(Code::Flags flags, const char* name);
+ Handle<Code> GetCodeWithFlags(Code::Flags flags, Handle<Name> name);
+
+ MacroAssembler* masm() { return &masm_; }
+ void set_failure(Failure* failure) { failure_ = failure; }
+
+ static void LookupPostInterceptor(Handle<JSObject> holder,
+ Handle<Name> name,
+ LookupResult* lookup);
+
+ Isolate* isolate() { return isolate_; }
+ Heap* heap() { return isolate()->heap(); }
+ Factory* factory() { return isolate()->factory(); }
+
+ static void GenerateTailCall(MacroAssembler* masm, Handle<Code> code);
+
+ private:
+ Isolate* isolate_;
+ MacroAssembler masm_;
+ Failure* failure_;
+};
+
+
+enum FrontendCheckType { PERFORM_INITIAL_CHECKS, SKIP_INITIAL_CHECKS };
+
+
+class BaseLoadStoreStubCompiler: public StubCompiler {
+ public:
+ BaseLoadStoreStubCompiler(Isolate* isolate, Register* registers)
+ : StubCompiler(isolate), registers_(registers) { }
+ virtual ~BaseLoadStoreStubCompiler() { }
+
+ Handle<Code> CompileMonomorphicIC(Handle<Map> receiver_map,
+ Handle<Code> handler,
+ Handle<Name> name);
+
+ Handle<Code> CompilePolymorphicIC(MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ Handle<Name> name,
+ Code::StubType type,
+ IcCheckType check);
+
+ virtual void GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) { }
+
+ static Builtins::Name MissBuiltin(Code::Kind kind) {
+ switch (kind) {
+ case Code::LOAD_IC: return Builtins::kLoadIC_Miss;
+ case Code::STORE_IC: return Builtins::kStoreIC_Miss;
+ case Code::KEYED_LOAD_IC: return Builtins::kKeyedLoadIC_Miss;
+ case Code::KEYED_STORE_IC: return Builtins::kKeyedStoreIC_Miss;
+ default: UNREACHABLE();
+ }
+ return Builtins::kLoadIC_Miss;
+ }
+
+ protected:
+ virtual Register HandlerFrontendHeader(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss) = 0;
+
+ virtual void HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) = 0;
+
+ Register HandlerFrontend(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* success);
+
+ Handle<Code> GetICCode(Code::Kind kind,
+ Code::StubType type,
+ Handle<Name> name,
+ InlineCacheState state = MONOMORPHIC);
+
+ virtual Code::ExtraICState extra_state() { return Code::kNoExtraICState; }
+ virtual Logger::LogEventsAndTags log_kind(Handle<Code> code) = 0;
+ virtual void JitEvent(Handle<Name> name, Handle<Code> code) = 0;
+ virtual Code::Kind kind() = 0;
+ virtual Register receiver() = 0;
+ virtual Register name() = 0;
+ virtual Register scratch1() = 0;
+ virtual Register scratch2() = 0;
+ virtual Register scratch3() = 0;
+
+ Register* registers_;
+};
+
+
+class BaseLoadStubCompiler: public BaseLoadStoreStubCompiler {
+ public:
+ BaseLoadStubCompiler(Isolate* isolate, Register* registers)
+ : BaseLoadStoreStubCompiler(isolate, registers) { }
+ virtual ~BaseLoadStubCompiler() { }
+
+ Handle<Code> CompileLoadField(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ PropertyIndex index,
+ Representation representation);
+
+ Handle<Code> CompileLoadCallback(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<ExecutableAccessorInfo> callback);
+
+ Handle<Code> CompileLoadConstant(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<Object> value);
+
+ Handle<Code> CompileLoadInterceptor(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name);
+
+ protected:
+ virtual Register HandlerFrontendHeader(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss);
+
+ virtual void HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss);
+
+ Register CallbackHandlerFrontend(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* success,
+ Handle<ExecutableAccessorInfo> callback);
+ void NonexistentHandlerFrontend(Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Label* success,
+ Handle<GlobalObject> global);
+
+ void GenerateLoadField(Register reg,
+ Handle<JSObject> holder,
+ PropertyIndex field,
+ Representation representation);
+ void GenerateLoadConstant(Handle<Object> value);
+ void GenerateLoadCallback(Register reg,
+ Handle<ExecutableAccessorInfo> callback);
+ void GenerateLoadInterceptor(Register holder_reg,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ LookupResult* lookup,
+ Handle<Name> name);
+ void GenerateLoadPostInterceptor(Register reg,
+ Handle<JSObject> interceptor_holder,
+ Handle<Name> name,
+ LookupResult* lookup);
+
+ Handle<Code> GetCode(Code::Kind kind,
+ Code::StubType type,
+ Handle<Name> name);
+
+ virtual Register receiver() { return registers_[0]; }
+ virtual Register name() { return registers_[1]; }
+ virtual Register scratch1() { return registers_[2]; }
+ virtual Register scratch2() { return registers_[3]; }
+ virtual Register scratch3() { return registers_[4]; }
+ Register scratch4() { return registers_[5]; }
+};
+
+
+class LoadStubCompiler: public BaseLoadStubCompiler {
+ public:
+ explicit LoadStubCompiler(Isolate* isolate)
+ : BaseLoadStubCompiler(isolate, registers()) { }
+
+ Handle<Code> CompileLoadNonexistent(Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Handle<GlobalObject> global);
+
+ static void GenerateLoadViaGetter(MacroAssembler* masm,
+ Handle<JSFunction> getter);
+
+ Handle<Code> CompileLoadViaGetter(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<JSFunction> getter);
+
+ Handle<Code> CompileLoadGlobal(Handle<JSObject> object,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ Handle<Name> name,
+ bool is_dont_delete);
+
+ private:
+ static Register* registers();
+ virtual Code::Kind kind() { return Code::LOAD_IC; }
+ virtual Logger::LogEventsAndTags log_kind(Handle<Code> code) {
+ if (!code->is_inline_cache_stub()) return Logger::STUB_TAG;
+ return code->ic_state() == MONOMORPHIC
+ ? Logger::LOAD_IC_TAG : Logger::LOAD_POLYMORPHIC_IC_TAG;
+ }
+ virtual void JitEvent(Handle<Name> name, Handle<Code> code);
+};
+
+
+class KeyedLoadStubCompiler: public BaseLoadStubCompiler {
+ public:
+ explicit KeyedLoadStubCompiler(Isolate* isolate)
+ : BaseLoadStubCompiler(isolate, registers()) { }
+
+ Handle<Code> CompileLoadElement(Handle<Map> receiver_map);
+
+ void CompileElementHandlers(MapHandleList* receiver_maps,
+ CodeHandleList* handlers);
+
+ static void GenerateLoadDictionaryElement(MacroAssembler* masm);
+
+ private:
+ static Register* registers();
+ virtual Code::Kind kind() { return Code::KEYED_LOAD_IC; }
+ virtual Logger::LogEventsAndTags log_kind(Handle<Code> code) {
+ if (!code->is_inline_cache_stub()) return Logger::STUB_TAG;
+ return code->ic_state() == MONOMORPHIC
+ ? Logger::KEYED_LOAD_IC_TAG : Logger::KEYED_LOAD_POLYMORPHIC_IC_TAG;
+ }
+ virtual void JitEvent(Handle<Name> name, Handle<Code> code);
+ virtual void GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss);
+};
+
+
+class BaseStoreStubCompiler: public BaseLoadStoreStubCompiler {
+ public:
+ BaseStoreStubCompiler(Isolate* isolate,
+ StrictModeFlag strict_mode,
+ Register* registers)
+ : BaseLoadStoreStubCompiler(isolate, registers),
+ strict_mode_(strict_mode) { }
+
+ virtual ~BaseStoreStubCompiler() { }
+
+ Handle<Code> CompileStoreTransition(Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ Handle<Name> name);
+
+ Handle<Code> CompileStoreField(Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Name> name);
+
+ void GenerateNegativeHolderLookup(MacroAssembler* masm,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Handle<Name> name,
+ Label* miss);
+
+ void GenerateStoreTransition(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ Handle<Name> name,
+ Register receiver_reg,
+ Register name_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* miss_label,
+ Label* slow);
+
+ void GenerateStoreField(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Register receiver_reg,
+ Register name_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label);
+
+ static Builtins::Name MissBuiltin(Code::Kind kind) {
+ switch (kind) {
+ case Code::LOAD_IC: return Builtins::kLoadIC_Miss;
+ case Code::STORE_IC: return Builtins::kStoreIC_Miss;
+ case Code::KEYED_LOAD_IC: return Builtins::kKeyedLoadIC_Miss;
+ case Code::KEYED_STORE_IC: return Builtins::kKeyedStoreIC_Miss;
+ default: UNREACHABLE();
+ }
+ return Builtins::kLoadIC_Miss;
+ }
+ static Builtins::Name SlowBuiltin(Code::Kind kind) {
+ switch (kind) {
+ case Code::STORE_IC: return Builtins::kStoreIC_Slow;
+ case Code::KEYED_STORE_IC: return Builtins::kKeyedStoreIC_Slow;
+ default: UNREACHABLE();
+ }
+ return Builtins::kStoreIC_Slow;
+ }
+
+ protected:
+ virtual Register HandlerFrontendHeader(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss);
+
+ virtual void HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss);
+ Handle<Code> GetCode(Code::Kind kind,
+ Code::StubType type,
+ Handle<Name> name);
+
+ void GenerateRestoreName(MacroAssembler* masm,
+ Label* label,
+ Handle<Name> name);
+
+ virtual Register receiver() { return registers_[0]; }
+ virtual Register name() { return registers_[1]; }
+ Register value() { return registers_[2]; }
+ virtual Register scratch1() { return registers_[3]; }
+ virtual Register scratch2() { return registers_[4]; }
+ virtual Register scratch3() { return registers_[5]; }
+ StrictModeFlag strict_mode() { return strict_mode_; }
+ virtual Code::ExtraICState extra_state() { return strict_mode_; }
+
+ private:
+ StrictModeFlag strict_mode_;
+};
+
+
+class StoreStubCompiler: public BaseStoreStubCompiler {
+ public:
+ StoreStubCompiler(Isolate* isolate, StrictModeFlag strict_mode)
+ : BaseStoreStubCompiler(isolate, strict_mode, registers()) { }
+
+
+ Handle<Code> CompileStoreCallback(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<ExecutableAccessorInfo> callback);
+
+ static void GenerateStoreViaSetter(MacroAssembler* masm,
+ Handle<JSFunction> setter);
+
+ Handle<Code> CompileStoreViaSetter(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<JSFunction> setter);
+
+ Handle<Code> CompileStoreInterceptor(Handle<JSObject> object,
+ Handle<Name> name);
+
+ Handle<Code> CompileStoreGlobal(Handle<GlobalObject> object,
+ Handle<PropertyCell> holder,
+ Handle<Name> name);
+
+ private:
+ static Register* registers();
+ virtual Code::Kind kind() { return Code::STORE_IC; }
+ virtual Logger::LogEventsAndTags log_kind(Handle<Code> code) {
+ if (!code->is_inline_cache_stub()) return Logger::STUB_TAG;
+ return code->ic_state() == MONOMORPHIC
+ ? Logger::STORE_IC_TAG : Logger::STORE_POLYMORPHIC_IC_TAG;
+ }
+ virtual void JitEvent(Handle<Name> name, Handle<Code> code);
+};
+
+
+class KeyedStoreStubCompiler: public BaseStoreStubCompiler {
+ public:
+ KeyedStoreStubCompiler(Isolate* isolate,
+ StrictModeFlag strict_mode,
+ KeyedAccessStoreMode store_mode)
+ : BaseStoreStubCompiler(isolate, strict_mode, registers()),
+ store_mode_(store_mode) { }
+
+ Handle<Code> CompileStoreElement(Handle<Map> receiver_map);
+
+ Handle<Code> CompileStorePolymorphic(MapHandleList* receiver_maps,
+ CodeHandleList* handler_stubs,
+ MapHandleList* transitioned_maps);
+
+ Handle<Code> CompileStoreElementPolymorphic(MapHandleList* receiver_maps);
+
+ static void GenerateStoreFastElement(MacroAssembler* masm,
+ bool is_js_array,
+ ElementsKind element_kind,
+ KeyedAccessStoreMode store_mode);
+
+ static void GenerateStoreFastDoubleElement(MacroAssembler* masm,
+ bool is_js_array,
+ KeyedAccessStoreMode store_mode);
+
+ static void GenerateStoreExternalArray(MacroAssembler* masm,
+ ElementsKind elements_kind);
+
+ static void GenerateStoreDictionaryElement(MacroAssembler* masm);
+
+ protected:
+ virtual Code::ExtraICState extra_state() {
+ return Code::ComputeExtraICState(store_mode_, strict_mode());
+ }
+
+ private:
+ Register transition_map() {
+ return registers()[3];
+ }
+
+ static Register* registers();
+ virtual Code::Kind kind() { return Code::KEYED_STORE_IC; }
+ virtual Logger::LogEventsAndTags log_kind(Handle<Code> code) {
+ if (!code->is_inline_cache_stub()) return Logger::STUB_TAG;
+ return code->ic_state() == MONOMORPHIC
+ ? Logger::KEYED_STORE_IC_TAG : Logger::KEYED_STORE_POLYMORPHIC_IC_TAG;
+ }
+ virtual void JitEvent(Handle<Name> name, Handle<Code> code);
+ virtual void GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss);
+ KeyedAccessStoreMode store_mode_;
+};
+
+
+// Subset of FUNCTIONS_WITH_ID_LIST with custom constant/global call
+// IC stubs.
+#define CUSTOM_CALL_IC_GENERATORS(V) \
+ V(ArrayPush) \
+ V(ArrayPop) \
+ V(StringCharCodeAt) \
+ V(StringCharAt) \
+ V(StringFromCharCode) \
+ V(MathFloor) \
+ V(MathAbs) \
+ V(ArrayCode)
+
+
+#define SITE_SPECIFIC_CALL_GENERATORS(V) \
+ V(ArrayCode)
+
+
+class CallOptimization;
+
+class CallStubCompiler: public StubCompiler {
+ public:
+ CallStubCompiler(Isolate* isolate,
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state,
+ InlineCacheHolderFlag cache_holder);
+
+ Handle<Code> CompileCallField(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ PropertyIndex index,
+ Handle<Name> name);
+
+ void CompileHandlerFrontend(Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Label* success);
+
+ void CompileHandlerBackend(Handle<JSFunction> function);
+
+ Handle<Code> CompileCallConstant(Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Handle<JSFunction> function);
+
+ Handle<Code> CompileCallInterceptor(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name);
+
+ Handle<Code> CompileCallGlobal(Handle<JSObject> object,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ Handle<JSFunction> function,
+ Handle<Name> name);
+
+ static bool HasCustomCallGenerator(Handle<JSFunction> function);
+ static bool CanBeCached(Handle<JSFunction> function);
+
+ private:
+ // 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<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type);
+
+#define DECLARE_CALL_GENERATOR(name) \
+ Handle<Code> Compile##name##Call(Handle<Object> object, \
+ Handle<JSObject> holder, \
+ Handle<Cell> cell, \
+ Handle<JSFunction> function, \
+ Handle<String> fname, \
+ Code::StubType type);
+ CUSTOM_CALL_IC_GENERATORS(DECLARE_CALL_GENERATOR)
+#undef DECLARE_CALL_GENERATOR
+
+ Handle<Code> CompileFastApiCall(const CallOptimization& optimization,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name);
+
+ Handle<Code> GetCode(Code::StubType type, Handle<Name> name);
+ Handle<Code> GetCode(Handle<JSFunction> function);
+
+ const ParameterCount& arguments() { return arguments_; }
+
+ void GenerateNameCheck(Handle<Name> name, Label* miss);
+
+ void GenerateGlobalReceiverCheck(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss);
+
+ // Generates code to load the function from the cell checking that
+ // it still contains the same function.
+ void GenerateLoadFunctionFromCell(Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Label* miss);
+
+ // 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_;
+};
+
+
+// Holds information about possible function call optimizations.
+class CallOptimization BASE_EMBEDDED {
+ public:
+ explicit CallOptimization(LookupResult* lookup);
+
+ explicit CallOptimization(Handle<JSFunction> function);
+
+ bool is_constant_call() const {
+ return !constant_function_.is_null();
+ }
+
+ Handle<JSFunction> constant_function() const {
+ ASSERT(is_constant_call());
+ return constant_function_;
+ }
+
+ bool is_simple_api_call() const {
+ return is_simple_api_call_;
+ }
+
+ Handle<FunctionTemplateInfo> expected_receiver_type() const {
+ ASSERT(is_simple_api_call());
+ return expected_receiver_type_;
+ }
+
+ 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(Handle<JSObject> object,
+ Handle<JSObject> holder) const;
+
+ private:
+ void Initialize(Handle<JSFunction> function);
+
+ // Determines whether the given function can be called using the
+ // fast api call builtin.
+ void AnalyzePossibleApiFunction(Handle<JSFunction> function);
+
+ Handle<JSFunction> constant_function_;
+ bool is_simple_api_call_;
+ Handle<FunctionTemplateInfo> expected_receiver_type_;
+ Handle<CallHandlerInfo> api_call_info_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_STUB_CACHE_H_
diff --git a/chromium/v8/src/sweeper-thread.cc b/chromium/v8/src/sweeper-thread.cc
new file mode 100644
index 00000000000..ede567a4857
--- /dev/null
+++ b/chromium/v8/src/sweeper-thread.cc
@@ -0,0 +1,108 @@
+// Copyright 2012 the V8 project authors. 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 "sweeper-thread.h"
+
+#include "v8.h"
+
+#include "isolate.h"
+#include "v8threads.h"
+
+namespace v8 {
+namespace internal {
+
+static const int kSweeperThreadStackSize = 64 * KB;
+
+SweeperThread::SweeperThread(Isolate* isolate)
+ : Thread(Thread::Options("v8:SweeperThread", kSweeperThreadStackSize)),
+ isolate_(isolate),
+ heap_(isolate->heap()),
+ collector_(heap_->mark_compact_collector()),
+ start_sweeping_semaphore_(OS::CreateSemaphore(0)),
+ end_sweeping_semaphore_(OS::CreateSemaphore(0)),
+ stop_semaphore_(OS::CreateSemaphore(0)),
+ free_list_old_data_space_(heap_->paged_space(OLD_DATA_SPACE)),
+ free_list_old_pointer_space_(heap_->paged_space(OLD_POINTER_SPACE)),
+ private_free_list_old_data_space_(heap_->paged_space(OLD_DATA_SPACE)),
+ private_free_list_old_pointer_space_(
+ heap_->paged_space(OLD_POINTER_SPACE)) {
+ NoBarrier_Store(&stop_thread_, static_cast<AtomicWord>(false));
+}
+
+
+void SweeperThread::Run() {
+ Isolate::SetIsolateThreadLocals(isolate_, NULL);
+ DisallowHeapAllocation no_allocation;
+ DisallowHandleAllocation no_handles;
+ DisallowHandleDereference no_deref;
+
+ while (true) {
+ start_sweeping_semaphore_->Wait();
+
+ if (Acquire_Load(&stop_thread_)) {
+ stop_semaphore_->Signal();
+ return;
+ }
+
+ collector_->SweepInParallel(heap_->old_data_space(),
+ &private_free_list_old_data_space_,
+ &free_list_old_data_space_);
+ collector_->SweepInParallel(heap_->old_pointer_space(),
+ &private_free_list_old_pointer_space_,
+ &free_list_old_pointer_space_);
+ end_sweeping_semaphore_->Signal();
+ }
+}
+
+
+intptr_t SweeperThread::StealMemory(PagedSpace* space) {
+ if (space->identity() == OLD_POINTER_SPACE) {
+ return space->free_list()->Concatenate(&free_list_old_pointer_space_);
+ } else if (space->identity() == OLD_DATA_SPACE) {
+ return space->free_list()->Concatenate(&free_list_old_data_space_);
+ }
+ return 0;
+}
+
+
+void SweeperThread::Stop() {
+ Release_Store(&stop_thread_, static_cast<AtomicWord>(true));
+ start_sweeping_semaphore_->Signal();
+ stop_semaphore_->Wait();
+ Join();
+}
+
+
+void SweeperThread::StartSweeping() {
+ start_sweeping_semaphore_->Signal();
+}
+
+
+void SweeperThread::WaitForSweeperThread() {
+ end_sweeping_semaphore_->Wait();
+}
+} } // namespace v8::internal
diff --git a/chromium/v8/src/sweeper-thread.h b/chromium/v8/src/sweeper-thread.h
new file mode 100644
index 00000000000..a170982141d
--- /dev/null
+++ b/chromium/v8/src/sweeper-thread.h
@@ -0,0 +1,75 @@
+// Copyright 2012 the V8 project authors. 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_SWEEPER_THREAD_H_
+#define V8_SWEEPER_THREAD_H_
+
+#include "atomicops.h"
+#include "flags.h"
+#include "platform.h"
+#include "v8utils.h"
+
+#include "spaces.h"
+
+#include "heap.h"
+
+namespace v8 {
+namespace internal {
+
+class SweeperThread : public Thread {
+ public:
+ explicit SweeperThread(Isolate* isolate);
+
+ void Run();
+ void Stop();
+ void StartSweeping();
+ void WaitForSweeperThread();
+ intptr_t StealMemory(PagedSpace* space);
+
+ ~SweeperThread() {
+ delete start_sweeping_semaphore_;
+ delete end_sweeping_semaphore_;
+ delete stop_semaphore_;
+ }
+
+ private:
+ Isolate* isolate_;
+ Heap* heap_;
+ MarkCompactCollector* collector_;
+ Semaphore* start_sweeping_semaphore_;
+ Semaphore* end_sweeping_semaphore_;
+ Semaphore* stop_semaphore_;
+ FreeList free_list_old_data_space_;
+ FreeList free_list_old_pointer_space_;
+ FreeList private_free_list_old_data_space_;
+ FreeList private_free_list_old_pointer_space_;
+ volatile AtomicWord stop_thread_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_SWEEPER_THREAD_H_
diff --git a/chromium/v8/src/symbol.js b/chromium/v8/src/symbol.js
new file mode 100644
index 00000000000..050e7d918a0
--- /dev/null
+++ b/chromium/v8/src/symbol.js
@@ -0,0 +1,87 @@
+// Copyright 2013 the V8 project authors. 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.
+
+"use strict";
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js:
+// var $Array = global.Array;
+
+var $Symbol = global.Symbol;
+
+// -------------------------------------------------------------------
+
+function SymbolConstructor(x) {
+ var value =
+ IS_SYMBOL(x) ? x : %CreateSymbol(IS_UNDEFINED(x) ? x : ToString(x));
+ if (%_IsConstructCall()) {
+ %_SetValueOf(this, value);
+ } else {
+ return value;
+ }
+}
+
+function SymbolGetName() {
+ var symbol = IS_SYMBOL_WRAPPER(this) ? %_ValueOf(this) : this;
+ if (!IS_SYMBOL(symbol)) {
+ throw MakeTypeError(
+ 'incompatible_method_receiver', ["Symbol.prototype.name", this]);
+ }
+ return %SymbolName(symbol);
+}
+
+function SymbolToString() {
+ throw MakeTypeError('symbol_to_string');
+}
+
+function SymbolValueOf() {
+ // NOTE: Both Symbol objects and values can enter here as
+ // 'this'. This is not as dictated by ECMA-262.
+ if (!IS_SYMBOL(this) && !IS_SYMBOL_WRAPPER(this)) {
+ throw MakeTypeError(
+ 'incompatible_method_receiver', ["Symbol.prototype.valueOf", this]);
+ }
+ return %_ValueOf(this);
+}
+
+//-------------------------------------------------------------------
+
+function SetUpSymbol() {
+ %CheckIsBootstrapping();
+
+ %SetCode($Symbol, SymbolConstructor);
+ %FunctionSetPrototype($Symbol, new $Symbol());
+ %SetProperty($Symbol.prototype, "constructor", $Symbol, DONT_ENUM);
+
+ InstallGetter($Symbol.prototype, "name", SymbolGetName);
+ InstallFunctions($Symbol.prototype, DONT_ENUM, $Array(
+ "toString", SymbolToString,
+ "valueOf", SymbolValueOf
+ ));
+}
+
+SetUpSymbol();
diff --git a/chromium/v8/src/third_party/valgrind/valgrind.h b/chromium/v8/src/third_party/valgrind/valgrind.h
new file mode 100644
index 00000000000..7a3ee2f1fb2
--- /dev/null
+++ b/chromium/v8/src/third_party/valgrind/valgrind.h
@@ -0,0 +1,4033 @@
+/* -*- c -*-
+ ----------------------------------------------------------------
+
+ Notice that the following BSD-style license applies to this one
+ file (valgrind.h) only. The rest of Valgrind is licensed under the
+ terms of the GNU General Public License, version 2, unless
+ otherwise indicated. See the COPYING file in the source
+ distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2010 Julian Seward. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (valgrind.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+
+/* This file is for inclusion into client (your!) code.
+
+ You can use these macros to manipulate and query Valgrind's
+ execution inside your own programs.
+
+ The resulting executables will still run without Valgrind, just a
+ little bit more slowly than they otherwise would, but otherwise
+ unchanged. When not running on valgrind, each client request
+ consumes very few (eg. 7) instructions, so the resulting performance
+ loss is negligible unless you plan to execute client requests
+ millions of times per second. Nevertheless, if that is still a
+ problem, you can compile with the NVALGRIND symbol defined (gcc
+ -DNVALGRIND) so that client requests are not even compiled in. */
+
+#ifndef __VALGRIND_H
+#define __VALGRIND_H
+
+
+/* ------------------------------------------------------------------ */
+/* VERSION NUMBER OF VALGRIND */
+/* ------------------------------------------------------------------ */
+
+/* Specify Valgrind's version number, so that user code can
+ conditionally compile based on our version number. Note that these
+ were introduced at version 3.6 and so do not exist in version 3.5
+ or earlier. The recommended way to use them to check for "version
+ X.Y or later" is (eg)
+
+#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \
+ && (__VALGRIND_MAJOR__ > 3 \
+ || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6))
+*/
+#define __VALGRIND_MAJOR__ 3
+#define __VALGRIND_MINOR__ 6
+
+
+#include <stdarg.h>
+#include <stdint.h>
+
+/* Nb: this file might be included in a file compiled with -ansi. So
+ we can't use C++ style "//" comments nor the "asm" keyword (instead
+ use "__asm__"). */
+
+/* Derive some tags indicating what the target platform is. Note
+ that in this file we're using the compiler's CPP symbols for
+ identifying architectures, which are different to the ones we use
+ within the rest of Valgrind. Note, __powerpc__ is active for both
+ 32 and 64-bit PPC, whereas __powerpc64__ is only active for the
+ latter (on Linux, that is).
+
+ Misc note: how to find out what's predefined in gcc by default:
+ gcc -Wp,-dM somefile.c
+*/
+#undef PLAT_x86_darwin
+#undef PLAT_amd64_darwin
+#undef PLAT_x86_win32
+#undef PLAT_x86_linux
+#undef PLAT_amd64_linux
+#undef PLAT_ppc32_linux
+#undef PLAT_ppc64_linux
+#undef PLAT_arm_linux
+#undef PLAT_s390x_linux
+
+
+#if defined(__APPLE__) && defined(__i386__)
+# define PLAT_x86_darwin 1
+#elif defined(__APPLE__) && defined(__x86_64__)
+# define PLAT_amd64_darwin 1
+#elif defined(__MINGW32__) || defined(__CYGWIN32__) \
+ || (defined(_WIN32) && defined(_M_IX86))
+# define PLAT_x86_win32 1
+#elif defined(__linux__) && defined(__i386__)
+# define PLAT_x86_linux 1
+#elif defined(__linux__) && defined(__x86_64__)
+# define PLAT_amd64_linux 1
+#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__)
+# define PLAT_ppc32_linux 1
+#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__)
+# define PLAT_ppc64_linux 1
+#elif defined(__linux__) && defined(__arm__)
+# define PLAT_arm_linux 1
+#elif defined(__linux__) && defined(__s390__) && defined(__s390x__)
+# define PLAT_s390x_linux 1
+#else
+/* If we're not compiling for our target platform, don't generate
+ any inline asms. */
+# if !defined(NVALGRIND)
+# define NVALGRIND 1
+# endif
+#endif
+
+
+/* ------------------------------------------------------------------ */
+/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */
+/* in here of use to end-users -- skip to the next section. */
+/* ------------------------------------------------------------------ */
+
+/*
+ * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client
+ * request. Accepts both pointers and integers as arguments.
+ *
+ * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind
+ * client request and whose value equals the client request result. Accepts
+ * both pointers and integers as arguments.
+ */
+
+#define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \
+ _zzq_request, _zzq_arg1, _zzq_arg2, \
+ _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \
+ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \
+ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); }
+
+#if defined(NVALGRIND)
+
+/* Define NVALGRIND to completely remove the Valgrind magic sequence
+ from the compiled code (analogous to NDEBUG's effects on
+ assert()) */
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ (_zzq_default)
+
+#else /* ! NVALGRIND */
+
+/* The following defines the magic code sequences which the JITter
+ spots and handles magically. Don't look too closely at them as
+ they will rot your brain.
+
+ The assembly code sequences for all architectures is in this one
+ file. This is because this file must be stand-alone, and we don't
+ want to have multiple files.
+
+ For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default
+ value gets put in the return slot, so that everything works when
+ this is executed not under Valgrind. Args are passed in a memory
+ block, and so there's no intrinsic limit to the number that could
+ be passed, but it's currently five.
+
+ The macro args are:
+ _zzq_rlval result lvalue
+ _zzq_default default value (result returned when running on real CPU)
+ _zzq_request request code
+ _zzq_arg1..5 request params
+
+ The other two macros are used to support function wrapping, and are
+ a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the
+ guest's NRADDR pseudo-register and whatever other information is
+ needed to safely run the call original from the wrapper: on
+ ppc64-linux, the R2 value at the divert point is also needed. This
+ information is abstracted into a user-visible type, OrigFn.
+
+ VALGRIND_CALL_NOREDIR_* behaves the same as the following on the
+ guest, but guarantees that the branch instruction will not be
+ redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64:
+ branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a
+ complete inline asm, since it needs to be combined with more magic
+ inline asm stuff to be useful.
+*/
+
+/* ------------------------- x86-{linux,darwin} ---------------- */
+
+#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \
+ || (defined(PLAT_x86_win32) && defined(__GNUC__))
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "roll $3, %%edi ; roll $13, %%edi\n\t" \
+ "roll $29, %%edi ; roll $19, %%edi\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EDX = client_request ( %EAX ) */ \
+ "xchgl %%ebx,%%ebx" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "memory" \
+ ); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EAX = guest_NRADDR */ \
+ "xchgl %%ecx,%%ecx" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_EAX \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%EAX */ \
+ "xchgl %%edx,%%edx\n\t"
+#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */
+
+/* ------------------------- x86-Win32 ------------------------- */
+
+#if defined(PLAT_x86_win32) && !defined(__GNUC__)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#if defined(_MSC_VER)
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ __asm rol edi, 3 __asm rol edi, 13 \
+ __asm rol edi, 29 __asm rol edi, 19
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \
+ (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \
+ (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \
+ (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5))
+
+static __inline uintptr_t
+valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request,
+ uintptr_t _zzq_arg1, uintptr_t _zzq_arg2,
+ uintptr_t _zzq_arg3, uintptr_t _zzq_arg4,
+ uintptr_t _zzq_arg5)
+{
+ volatile uintptr_t _zzq_args[6];
+ volatile unsigned int _zzq_result;
+ _zzq_args[0] = (uintptr_t)(_zzq_request);
+ _zzq_args[1] = (uintptr_t)(_zzq_arg1);
+ _zzq_args[2] = (uintptr_t)(_zzq_arg2);
+ _zzq_args[3] = (uintptr_t)(_zzq_arg3);
+ _zzq_args[4] = (uintptr_t)(_zzq_arg4);
+ _zzq_args[5] = (uintptr_t)(_zzq_arg5);
+ __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default
+ __SPECIAL_INSTRUCTION_PREAMBLE
+ /* %EDX = client_request ( %EAX ) */
+ __asm xchg ebx,ebx
+ __asm mov _zzq_result, edx
+ }
+ return _zzq_result;
+}
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned int __addr; \
+ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EAX = guest_NRADDR */ \
+ __asm xchg ecx,ecx \
+ __asm mov __addr, eax \
+ } \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_EAX ERROR
+
+#else
+#error Unsupported compiler.
+#endif
+
+#endif /* PLAT_x86_win32 */
+
+/* ------------------------ amd64-{linux,darwin} --------------- */
+
+#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin)
+
+typedef
+ struct {
+ uint64_t nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \
+ "rolq $61, %%rdi ; rolq $51, %%rdi\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({ volatile uint64_t _zzq_args[6]; \
+ volatile uint64_t _zzq_result; \
+ _zzq_args[0] = (uint64_t)(_zzq_request); \
+ _zzq_args[1] = (uint64_t)(_zzq_arg1); \
+ _zzq_args[2] = (uint64_t)(_zzq_arg2); \
+ _zzq_args[3] = (uint64_t)(_zzq_arg3); \
+ _zzq_args[4] = (uint64_t)(_zzq_arg4); \
+ _zzq_args[5] = (uint64_t)(_zzq_arg5); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %RDX = client_request ( %RAX ) */ \
+ "xchgq %%rbx,%%rbx" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "memory" \
+ ); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile uint64_t __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %RAX = guest_NRADDR */ \
+ "xchgq %%rcx,%%rcx" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_RAX \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%RAX */ \
+ "xchgq %%rdx,%%rdx\n\t"
+#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */
+
+/* ------------------------ ppc32-linux ------------------------ */
+
+#if defined(PLAT_ppc32_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \
+ "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ __extension__ \
+ ({ unsigned int _zzq_args[6]; \
+ unsigned int _zzq_result; \
+ unsigned int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 3,%1\n\t" /*default*/ \
+ "mr 4,%2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" /*result*/ \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_default), "b" (_zzq_ptr) \
+ : "cc", "memory", "r3", "r4"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+#endif /* PLAT_ppc32_linux */
+
+/* ------------------------ ppc64-linux ------------------------ */
+
+#if defined(PLAT_ppc64_linux)
+
+typedef
+ struct {
+ uint64_t nraddr; /* where's the code? */
+ uint64_t r2; /* what tocptr do we need? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \
+ "rotldi 0,0,61 ; rotldi 0,0,51\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ __extension__ \
+ ({ uint64_t _zzq_args[6]; \
+ register uint64_t _zzq_result __asm__("r3"); \
+ register uint64_t* _zzq_ptr __asm__("r4"); \
+ _zzq_args[0] = (uint64_t)(_zzq_request); \
+ _zzq_args[1] = (uint64_t)(_zzq_arg1); \
+ _zzq_args[2] = (uint64_t)(_zzq_arg2); \
+ _zzq_args[3] = (uint64_t)(_zzq_arg3); \
+ _zzq_args[4] = (uint64_t)(_zzq_arg4); \
+ _zzq_args[5] = (uint64_t)(_zzq_arg5); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1" \
+ : "=r" (_zzq_result) \
+ : "0" (_zzq_default), "r" (_zzq_ptr) \
+ : "cc", "memory"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ register uint64_t __addr __asm__("r3"); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR_GPR2 */ \
+ "or 4,4,4" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->r2 = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#endif /* PLAT_ppc64_linux */
+
+/* ------------------------- arm-linux ------------------------- */
+
+#if defined(PLAT_arm_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \
+ "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ __extension__ \
+ ({volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile("mov r3, %1\n\t" /*default*/ \
+ "mov r4, %2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* R3 = client_request ( R4 ) */ \
+ "orr r10, r10, r10\n\t" \
+ "mov %0, r3" /*result*/ \
+ : "=r" (_zzq_result) \
+ : "r" (_zzq_default), "r" (&_zzq_args[0]) \
+ : "cc","memory", "r3", "r4"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* R3 = guest_NRADDR */ \
+ "orr r11, r11, r11\n\t" \
+ "mov %0, r3" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R4 */ \
+ "orr r12, r12, r12\n\t"
+
+#endif /* PLAT_arm_linux */
+
+/* ------------------------ s390x-linux ------------------------ */
+
+#if defined(PLAT_s390x_linux)
+
+typedef
+ struct {
+ uint64_t nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+/* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific
+ * code. This detection is implemented in platform specific toIR.c
+ * (e.g. VEX/priv/guest_s390_decoder.c).
+ */
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "lr 15,15\n\t" \
+ "lr 1,1\n\t" \
+ "lr 2,2\n\t" \
+ "lr 3,3\n\t"
+
+#define __CLIENT_REQUEST_CODE "lr 2,2\n\t"
+#define __GET_NR_CONTEXT_CODE "lr 3,3\n\t"
+#define __CALL_NO_REDIR_CODE "lr 4,4\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({volatile uint64_t _zzq_args[6]; \
+ volatile uint64_t _zzq_result; \
+ _zzq_args[0] = (uint64_t)(_zzq_request); \
+ _zzq_args[1] = (uint64_t)(_zzq_arg1); \
+ _zzq_args[2] = (uint64_t)(_zzq_arg2); \
+ _zzq_args[3] = (uint64_t)(_zzq_arg3); \
+ _zzq_args[4] = (uint64_t)(_zzq_arg4); \
+ _zzq_args[5] = (uint64_t)(_zzq_arg5); \
+ __asm__ volatile(/* r2 = args */ \
+ "lgr 2,%1\n\t" \
+ /* r3 = default */ \
+ "lgr 3,%2\n\t" \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ __CLIENT_REQUEST_CODE \
+ /* results = r3 */ \
+ "lgr %0, 3\n\t" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "2", "3", "memory" \
+ ); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile uint64_t __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ __GET_NR_CONTEXT_CODE \
+ "lgr %0, 3\n\t" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "3", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_R1 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ __CALL_NO_REDIR_CODE
+
+#endif /* PLAT_s390x_linux */
+
+/* Insert assembly code for other platforms here... */
+
+#endif /* NVALGRIND */
+
+
+/* ------------------------------------------------------------------ */
+/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */
+/* ugly. It's the least-worst tradeoff I can think of. */
+/* ------------------------------------------------------------------ */
+
+/* This section defines magic (a.k.a appalling-hack) macros for doing
+ guaranteed-no-redirection macros, so as to get from function
+ wrappers to the functions they are wrapping. The whole point is to
+ construct standard call sequences, but to do the call itself with a
+ special no-redirect call pseudo-instruction that the JIT
+ understands and handles specially. This section is long and
+ repetitious, and I can't see a way to make it shorter.
+
+ The naming scheme is as follows:
+
+ CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc}
+
+ 'W' stands for "word" and 'v' for "void". Hence there are
+ different macros for calling arity 0, 1, 2, 3, 4, etc, functions,
+ and for each, the possibility of returning a word-typed result, or
+ no result.
+*/
+
+/* Use these to write the name of your wrapper. NOTE: duplicates
+ VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */
+
+/* Use an extra level of macroisation so as to ensure the soname/fnname
+ args are fully macro-expanded before pasting them together. */
+#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd
+
+#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \
+ VG_CONCAT4(_vgwZU_,soname,_,fnname)
+
+#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \
+ VG_CONCAT4(_vgwZZ_,soname,_,fnname)
+
+/* Use this macro from within a wrapper function to collect the
+ context (address and possibly other info) of the original function.
+ Once you have that you can then use it in one of the CALL_FN_
+ macros. The type of the argument _lval is OrigFn. */
+#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval)
+
+/* Derivatives of the main macros below, for calling functions
+ returning void. */
+
+#define CALL_FN_v_v(fnptr) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_v(_junk,fnptr); } while (0)
+
+#define CALL_FN_v_W(fnptr, arg1) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_W(_junk,fnptr,arg1); } while (0)
+
+#define CALL_FN_v_WW(fnptr, arg1,arg2) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0)
+
+#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0)
+
+#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0)
+
+#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0)
+
+#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0)
+
+#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0)
+
+/* ------------------------- x86-{linux,darwin} ---------------- */
+
+#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin)
+
+/* These regs are trashed by the hidden call. No need to mention eax
+ as gcc can already see that, plus causes gcc to bomb. */
+#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx"
+
+/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "subl $12, %%esp\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "subl $8, %%esp\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "subl $4, %%esp\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $16, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "subl $12, %%esp\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "subl $8, %%esp\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "subl $4, %%esp\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $32, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "subl $12, %%esp\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "subl $8, %%esp\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "subl $4, %%esp\n\t" \
+ "pushl 44(%%eax)\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "pushl 48(%%eax)\n\t" \
+ "pushl 44(%%eax)\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ "addl $48, %%esp\n" \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_x86_linux || PLAT_x86_darwin */
+
+/* ------------------------ amd64-{linux,darwin} --------------- */
+
+#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin)
+
+/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \
+ "rdi", "r8", "r9", "r10", "r11"
+
+/* This is all pretty complex. It's so as to make stack unwinding
+ work reliably. See bug 243270. The basic problem is the sub and
+ add of 128 of %rsp in all of the following macros. If gcc believes
+ the CFA is in %rsp, then unwinding may fail, because what's at the
+ CFA is not what gcc "expected" when it constructs the CFIs for the
+ places where the macros are instantiated.
+
+ But we can't just add a CFI annotation to increase the CFA offset
+ by 128, to match the sub of 128 from %rsp, because we don't know
+ whether gcc has chosen %rsp as the CFA at that point, or whether it
+ has chosen some other register (eg, %rbp). In the latter case,
+ adding a CFI annotation to change the CFA offset is simply wrong.
+
+ So the solution is to get hold of the CFA using
+ __builtin_dwarf_cfa(), put it in a known register, and add a
+ CFI annotation to say what the register is. We choose %rbp for
+ this (perhaps perversely), because:
+
+ (1) %rbp is already subject to unwinding. If a new register was
+ chosen then the unwinder would have to unwind it in all stack
+ traces, which is expensive, and
+
+ (2) %rbp is already subject to precise exception updates in the
+ JIT. If a new register was chosen, we'd have to have precise
+ exceptions for it too, which reduces performance of the
+ generated code.
+
+ However .. one extra complication. We can't just whack the result
+ of __builtin_dwarf_cfa() into %rbp and then add %rbp to the
+ list of trashed registers at the end of the inline assembly
+ fragments; gcc won't allow %rbp to appear in that list. Hence
+ instead we need to stash %rbp in %r15 for the duration of the asm,
+ and say that %r15 is trashed instead. gcc seems happy to go with
+ that.
+
+ Oh .. and this all needs to be conditionalised so that it is
+ unchanged from before this commit, when compiled with older gccs
+ that don't support __builtin_dwarf_cfa. Furthermore, since
+ this header file is freestanding, it has to be independent of
+ config.h, and so the following conditionalisation cannot depend on
+ configure time checks.
+
+ Although it's not clear from
+ 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)',
+ this expression excludes Darwin.
+ .cfi directives in Darwin assembly appear to be completely
+ different and I haven't investigated how they work.
+
+ For even more entertainment value, note we have to use the
+ completely undocumented __builtin_dwarf_cfa(), which appears to
+ really compute the CFA, whereas __builtin_frame_address(0) claims
+ to but actually doesn't. See
+ https://bugs.kde.org/show_bug.cgi?id=243270#c47
+*/
+#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)
+# define __FRAME_POINTER \
+ ,"r"(__builtin_dwarf_cfa())
+# define VALGRIND_CFI_PROLOGUE \
+ "movq %%rbp, %%r15\n\t" \
+ "movq %2, %%rbp\n\t" \
+ ".cfi_remember_state\n\t" \
+ ".cfi_def_cfa rbp, 0\n\t"
+# define VALGRIND_CFI_EPILOGUE \
+ "movq %%r15, %%rbp\n\t" \
+ ".cfi_restore_state\n\t"
+#else
+# define __FRAME_POINTER
+# define VALGRIND_CFI_PROLOGUE
+# define VALGRIND_CFI_EPILOGUE
+#endif
+
+
+/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned
+ long) == 8. */
+
+/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_
+ macros. In order not to trash the stack redzone, we need to drop
+ %rsp by 128 before the hidden call, and restore afterwards. The
+ nastyness is that it is only by luck that the stack still appears
+ to be unwindable during the hidden call - since then the behaviour
+ of any routine using this macro does not match what the CFI data
+ says. Sigh.
+
+ Why is this important? Imagine that a wrapper has a stack
+ allocated local, and passes to the hidden call, a pointer to it.
+ Because gcc does not know about the hidden call, it may allocate
+ that local in the redzone. Unfortunately the hidden call may then
+ trash it before it comes to use it. So we must step clear of the
+ redzone, for the duration of the hidden call, to make it safe.
+
+ Probably the same problem afflicts the other redzone-style ABIs too
+ (ppc64-linux); but for those, the stack is
+ self describing (none of this CFI nonsense) so at least messing
+ with the stack pointer doesn't give a danger of non-unwindable
+ stack. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $136,%%rsp\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $8, %%rsp\n" \
+ "addq $136,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $16, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $136,%%rsp\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $24, %%rsp\n" \
+ "addq $136,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $32, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $136,%%rsp\n\t" \
+ "pushq 88(%%rax)\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $40, %%rsp\n" \
+ "addq $136,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "subq $128,%%rsp\n\t" \
+ "pushq 96(%%rax)\n\t" \
+ "pushq 88(%%rax)\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ "addq $48, %%rsp\n" \
+ "addq $128,%%rsp\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */
+
+/* ------------------------ ppc32-linux ------------------------ */
+
+#if defined(PLAT_ppc32_linux)
+
+/* This is useful for finding out about the on-stack stuff:
+
+ extern int f9 ( int,int,int,int,int,int,int,int,int );
+ extern int f10 ( int,int,int,int,int,int,int,int,int,int );
+ extern int f11 ( int,int,int,int,int,int,int,int,int,int,int );
+ extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int );
+
+ int g9 ( void ) {
+ return f9(11,22,33,44,55,66,77,88,99);
+ }
+ int g10 ( void ) {
+ return f10(11,22,33,44,55,66,77,88,99,110);
+ }
+ int g11 ( void ) {
+ return f11(11,22,33,44,55,66,77,88,99,110,121);
+ }
+ int g12 ( void ) {
+ return f12(11,22,33,44,55,66,77,88,99,110,121,132);
+ }
+*/
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* These CALL_FN_ macros assume that on ppc32-linux,
+ sizeof(unsigned long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-16\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,16\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-16\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,16\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-32\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,16(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,32\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ _argvec[12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-32\n\t" \
+ /* arg12 */ \
+ "lwz 3,48(11)\n\t" \
+ "stw 3,20(1)\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,16(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "addi 1,1,32\n\t" \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc32_linux */
+
+/* ------------------------ ppc64-linux ------------------------ */
+
+#if defined(PLAT_ppc64_linux)
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned
+ long) == 8. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+0]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+1]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+2]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+3]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+4]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+5]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+6]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+7]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+8]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)" /* restore tocptr */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+9]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-128\n\t" /* expand stack frame */ \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,128" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+10]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-128\n\t" /* expand stack frame */ \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,128" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+11]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-144\n\t" /* expand stack frame */ \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,144" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+12]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ _argvec[2+12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-144\n\t" /* expand stack frame */ \
+ /* arg12 */ \
+ "ld 3,96(11)\n\t" \
+ "std 3,136(1)\n\t" \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ "addi 1,1,144" /* restore frame */ \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc64_linux */
+
+/* ------------------------- arm-linux ------------------------- */
+
+#if defined(PLAT_arm_linux)
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14"
+
+/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "push {r0} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #4 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "push {r0, r1} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #8 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "push {r0, r1, r2} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #12 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "push {r0, r1, r2, r3} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #16 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #20 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #40] \n\t" \
+ "push {r0} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #24 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #40] \n\t" \
+ "ldr r1, [%1, #44] \n\t" \
+ "push {r0, r1} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #28 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory",__CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "ldr r0, [%1, #40] \n\t" \
+ "ldr r1, [%1, #44] \n\t" \
+ "ldr r2, [%1, #48] \n\t" \
+ "push {r0, r1, r2} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ "add sp, sp, #32 \n\t" \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_arm_linux */
+
+/* ------------------------- s390x-linux ------------------------- */
+
+#if defined(PLAT_s390x_linux)
+
+/* Similar workaround as amd64 (see above), but we use r11 as frame
+ pointer and save the old r11 in r7. r11 might be used for
+ argvec, therefore we copy argvec in r1 since r1 is clobbered
+ after the call anyway. */
+#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)
+# define __FRAME_POINTER \
+ ,"d"(__builtin_dwarf_cfa())
+# define VALGRIND_CFI_PROLOGUE \
+ ".cfi_remember_state\n\t" \
+ "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \
+ "lgr 7,11\n\t" \
+ "lgr 11,%2\n\t" \
+ ".cfi_def_cfa r11, 0\n\t"
+# define VALGRIND_CFI_EPILOGUE \
+ "lgr 11, 7\n\t" \
+ ".cfi_restore_state\n\t"
+#else
+# define __FRAME_POINTER
+# define VALGRIND_CFI_PROLOGUE \
+ "lgr 1,%1\n\t"
+# define VALGRIND_CFI_EPILOGUE
+#endif
+
+
+
+
+/* These regs are trashed by the hidden call. Note that we overwrite
+ r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the
+ function a proper return address. All others are ABI defined call
+ clobbers. */
+#define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \
+ "f0","f1","f2","f3","f4","f5","f6","f7"
+
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 1, 0(1)\n\t" /* target->r1 */ \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+/* The call abi has the arguments in r2-r6 and stack */
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1, arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-168\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,168\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-176\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,176\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-184\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,184\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8, arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-192\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "mvc 184(8,15), 72(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,192\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8, arg9, arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-200\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "mvc 184(8,15), 72(1)\n\t" \
+ "mvc 192(8,15), 80(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,200\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8, arg9, arg10, arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-208\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "mvc 184(8,15), 72(1)\n\t" \
+ "mvc 192(8,15), 80(1)\n\t" \
+ "mvc 200(8,15), 88(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,208\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ _argvec[12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-216\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "mvc 184(8,15), 72(1)\n\t" \
+ "mvc 192(8,15), 80(1)\n\t" \
+ "mvc 200(8,15), 88(1)\n\t" \
+ "mvc 208(8,15), 96(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,216\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+
+#endif /* PLAT_s390x_linux */
+
+
+/* ------------------------------------------------------------------ */
+/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */
+/* */
+/* ------------------------------------------------------------------ */
+
+/* Some request codes. There are many more of these, but most are not
+ exposed to end-user view. These are the public ones, all of the
+ form 0x1000 + small_number.
+
+ Core ones are in the range 0x00000000--0x0000ffff. The non-public
+ ones start at 0x2000.
+*/
+
+/* These macros are used by tools -- they must be public, but don't
+ embed them into other programs. */
+#define VG_USERREQ_TOOL_BASE(a,b) \
+ ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16))
+#define VG_IS_TOOL_USERREQ(a, b, v) \
+ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000))
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
+ This enum comprises an ABI exported by Valgrind to programs
+ which use client requests. DO NOT CHANGE THE ORDER OF THESE
+ ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+ enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001,
+ VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002,
+
+ /* These allow any function to be called from the simulated
+ CPU but run on the real CPU. Nb: the first arg passed to
+ the function is always the ThreadId of the running
+ thread! So CLIENT_CALL0 actually requires a 1 arg
+ function, etc. */
+ VG_USERREQ__CLIENT_CALL0 = 0x1101,
+ VG_USERREQ__CLIENT_CALL1 = 0x1102,
+ VG_USERREQ__CLIENT_CALL2 = 0x1103,
+ VG_USERREQ__CLIENT_CALL3 = 0x1104,
+
+ /* Can be useful in regression testing suites -- eg. can
+ send Valgrind's output to /dev/null and still count
+ errors. */
+ VG_USERREQ__COUNT_ERRORS = 0x1201,
+
+ /* Allows a string (gdb monitor command) to be passed to the tool
+ Used for interaction with vgdb/gdb */
+ VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202,
+
+ /* These are useful and can be interpreted by any tool that
+ tracks malloc() et al, by using vg_replace_malloc.c. */
+ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301,
+ VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b,
+ VG_USERREQ__FREELIKE_BLOCK = 0x1302,
+ /* Memory pool support. */
+ VG_USERREQ__CREATE_MEMPOOL = 0x1303,
+ VG_USERREQ__DESTROY_MEMPOOL = 0x1304,
+ VG_USERREQ__MEMPOOL_ALLOC = 0x1305,
+ VG_USERREQ__MEMPOOL_FREE = 0x1306,
+ VG_USERREQ__MEMPOOL_TRIM = 0x1307,
+ VG_USERREQ__MOVE_MEMPOOL = 0x1308,
+ VG_USERREQ__MEMPOOL_CHANGE = 0x1309,
+ VG_USERREQ__MEMPOOL_EXISTS = 0x130a,
+
+ /* Allow printfs to valgrind log. */
+ /* The first two pass the va_list argument by value, which
+ assumes it is the same size as or smaller than a UWord,
+ which generally isn't the case. Hence are deprecated.
+ The second two pass the vargs by reference and so are
+ immune to this problem. */
+ /* both :: char* fmt, va_list vargs (DEPRECATED) */
+ VG_USERREQ__PRINTF = 0x1401,
+ VG_USERREQ__PRINTF_BACKTRACE = 0x1402,
+ /* both :: char* fmt, va_list* vargs */
+ VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404,
+
+ /* Stack support. */
+ VG_USERREQ__STACK_REGISTER = 0x1501,
+ VG_USERREQ__STACK_DEREGISTER = 0x1502,
+ VG_USERREQ__STACK_CHANGE = 0x1503,
+
+ /* Wine support */
+ VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601,
+
+ /* Querying of debug info. */
+ VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701
+ } Vg_ClientRequest;
+
+#if !defined(__GNUC__)
+# define __extension__ /* */
+#endif
+
+
+/* Returns the number of Valgrinds this code is running under. That
+ is, 0 if running natively, 1 if running under Valgrind, 2 if
+ running under Valgrind which is running under another Valgrind,
+ etc. */
+#define RUNNING_ON_VALGRIND \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \
+ VG_USERREQ__RUNNING_ON_VALGRIND, \
+ 0, 0, 0, 0, 0) \
+
+
+/* Discard translation of code in the range [_qzz_addr .. _qzz_addr +
+ _qzz_len - 1]. Useful if you are debugging a JITter or some such,
+ since it provides a way to make sure valgrind will retranslate the
+ invalidated area. Returns no value. */
+#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__DISCARD_TRANSLATIONS, \
+ _qzz_addr, _qzz_len, 0, 0, 0)
+
+
+/* These requests are for getting Valgrind itself to print something.
+ Possibly with a backtrace. This is a really ugly hack. The return value
+ is the number of characters printed, excluding the "**<pid>** " part at the
+ start and the backtrace (if present). */
+
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+/* Modern GCC will optimize the static routine out if unused,
+ and unused attribute will shut down warnings about it. */
+static int VALGRIND_PRINTF(const char *format, ...)
+ __attribute__((format(__printf__, 1, 2), __unused__));
+#endif
+static int
+#if defined(_MSC_VER)
+__inline
+#endif
+VALGRIND_PRINTF(const char *format, ...)
+{
+#if defined(NVALGRIND)
+ return 0;
+#else /* NVALGRIND */
+#if defined(_MSC_VER)
+ uintptr_t _qzz_res;
+#else
+ unsigned long _qzz_res;
+#endif
+ va_list vargs;
+ va_start(vargs, format);
+#if defined(_MSC_VER)
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0,
+ VG_USERREQ__PRINTF_VALIST_BY_REF,
+ (uintptr_t)format,
+ (uintptr_t)&vargs,
+ 0, 0, 0);
+#else
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0,
+ VG_USERREQ__PRINTF_VALIST_BY_REF,
+ (unsigned long)format,
+ (unsigned long)&vargs,
+ 0, 0, 0);
+#endif
+ va_end(vargs);
+ return (int)_qzz_res;
+#endif /* NVALGRIND */
+}
+
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
+ __attribute__((format(__printf__, 1, 2), __unused__));
+#endif
+static int
+#if defined(_MSC_VER)
+__inline
+#endif
+VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
+{
+#if defined(NVALGRIND)
+ return 0;
+#else /* NVALGRIND */
+#if defined(_MSC_VER)
+ uintptr_t _qzz_res;
+#else
+ unsigned long _qzz_res;
+#endif
+ va_list vargs;
+ va_start(vargs, format);
+#if defined(_MSC_VER)
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF,
+ (uintptr_t)format,
+ (uintptr_t)&vargs,
+ 0, 0, 0);
+#else
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF,
+ (unsigned long)format,
+ (unsigned long)&vargs,
+ 0, 0, 0);
+#endif
+ va_end(vargs);
+ return (int)_qzz_res;
+#endif /* NVALGRIND */
+}
+
+
+/* These requests allow control to move from the simulated CPU to the
+ real CPU, calling an arbitary function.
+
+ Note that the current ThreadId is inserted as the first argument.
+ So this call:
+
+ VALGRIND_NON_SIMD_CALL2(f, arg1, arg2)
+
+ requires f to have this signature:
+
+ Word f(Word tid, Word arg1, Word arg2)
+
+ where "Word" is a word-sized type.
+
+ Note that these client requests are not entirely reliable. For example,
+ if you call a function with them that subsequently calls printf(),
+ there's a high chance Valgrind will crash. Generally, your prospects of
+ these working are made higher if the called function does not refer to
+ any global variables, and does not refer to any libc or other functions
+ (printf et al). Any kind of entanglement with libc or dynamic linking is
+ likely to have a bad outcome, for tricky reasons which we've grappled
+ with a lot in the past.
+*/
+#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL0, \
+ _qyy_fn, \
+ 0, 0, 0, 0)
+
+#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL1, \
+ _qyy_fn, \
+ _qyy_arg1, 0, 0, 0)
+
+#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL2, \
+ _qyy_fn, \
+ _qyy_arg1, _qyy_arg2, 0, 0)
+
+#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL3, \
+ _qyy_fn, \
+ _qyy_arg1, _qyy_arg2, \
+ _qyy_arg3, 0)
+
+
+/* Counts the number of errors that have been recorded by a tool. Nb:
+ the tool must record the errors with VG_(maybe_record_error)() or
+ VG_(unique_error)() for them to be counted. */
+#define VALGRIND_COUNT_ERRORS \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ 0 /* default return */, \
+ VG_USERREQ__COUNT_ERRORS, \
+ 0, 0, 0, 0, 0)
+
+/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing
+ when heap blocks are allocated in order to give accurate results. This
+ happens automatically for the standard allocator functions such as
+ malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete,
+ delete[], etc.
+
+ But if your program uses a custom allocator, this doesn't automatically
+ happen, and Valgrind will not do as well. For example, if you allocate
+ superblocks with mmap() and then allocates chunks of the superblocks, all
+ Valgrind's observations will be at the mmap() level and it won't know that
+ the chunks should be considered separate entities. In Memcheck's case,
+ that means you probably won't get heap block overrun detection (because
+ there won't be redzones marked as unaddressable) and you definitely won't
+ get any leak detection.
+
+ The following client requests allow a custom allocator to be annotated so
+ that it can be handled accurately by Valgrind.
+
+ VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated
+ by a malloc()-like function. For Memcheck (an illustrative case), this
+ does two things:
+
+ - It records that the block has been allocated. This means any addresses
+ within the block mentioned in error messages will be
+ identified as belonging to the block. It also means that if the block
+ isn't freed it will be detected by the leak checker.
+
+ - It marks the block as being addressable and undefined (if 'is_zeroed' is
+ not set), or addressable and defined (if 'is_zeroed' is set). This
+ controls how accesses to the block by the program are handled.
+
+ 'addr' is the start of the usable block (ie. after any
+ redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator
+ can apply redzones -- these are blocks of padding at the start and end of
+ each block. Adding redzones is recommended as it makes it much more likely
+ Valgrind will spot block overruns. `is_zeroed' indicates if the memory is
+ zeroed (or filled with another predictable value), as is the case for
+ calloc().
+
+ VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a
+ heap block -- that will be used by the client program -- is allocated.
+ It's best to put it at the outermost level of the allocator if possible;
+ for example, if you have a function my_alloc() which calls
+ internal_alloc(), and the client request is put inside internal_alloc(),
+ stack traces relating to the heap block will contain entries for both
+ my_alloc() and internal_alloc(), which is probably not what you want.
+
+ For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out
+ custom blocks from within a heap block, B, that has been allocated with
+ malloc/calloc/new/etc, then block B will be *ignored* during leak-checking
+ -- the custom blocks will take precedence.
+
+ VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For
+ Memcheck, it does two things:
+
+ - It records that the block has been deallocated. This assumes that the
+ block was annotated as having been allocated via
+ VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued.
+
+ - It marks the block as being unaddressable.
+
+ VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a
+ heap block is deallocated.
+
+ VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For
+ Memcheck, it does four things:
+
+ - It records that the size of a block has been changed. This assumes that
+ the block was annotated as having been allocated via
+ VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued.
+
+ - If the block shrunk, it marks the freed memory as being unaddressable.
+
+ - If the block grew, it marks the new area as undefined and defines a red
+ zone past the end of the new block.
+
+ - The V-bits of the overlap between the old and the new block are preserved.
+
+ VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block
+ and before deallocation of the old block.
+
+ In many cases, these three client requests will not be enough to get your
+ allocator working well with Memcheck. More specifically, if your allocator
+ writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call
+ will be necessary to mark the memory as addressable just before the zeroing
+ occurs, otherwise you'll get a lot of invalid write errors. For example,
+ you'll need to do this if your allocator recycles freed blocks, but it
+ zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK).
+ Alternatively, if your allocator reuses freed blocks for allocator-internal
+ data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary.
+
+ Really, what's happening is a blurring of the lines between the client
+ program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the
+ memory should be considered unaddressable to the client program, but the
+ allocator knows more than the rest of the client program and so may be able
+ to safely access it. Extra client requests are necessary for Valgrind to
+ understand the distinction between the allocator and the rest of the
+ program.
+
+ Ignored if addr == 0.
+*/
+#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MALLOCLIKE_BLOCK, \
+ addr, sizeB, rzB, is_zeroed, 0)
+
+/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details.
+ Ignored if addr == 0.
+*/
+#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__RESIZEINPLACE_BLOCK, \
+ addr, oldSizeB, newSizeB, rzB, 0)
+
+/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details.
+ Ignored if addr == 0.
+*/
+#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__FREELIKE_BLOCK, \
+ addr, rzB, 0, 0, 0)
+
+/* Create a memory pool. */
+#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__CREATE_MEMPOOL, \
+ pool, rzB, is_zeroed, 0, 0)
+
+/* Destroy a memory pool. */
+#define VALGRIND_DESTROY_MEMPOOL(pool) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__DESTROY_MEMPOOL, \
+ pool, 0, 0, 0, 0)
+
+/* Associate a piece of memory with a memory pool. */
+#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MEMPOOL_ALLOC, \
+ pool, addr, size, 0, 0)
+
+/* Disassociate a piece of memory from a memory pool. */
+#define VALGRIND_MEMPOOL_FREE(pool, addr) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MEMPOOL_FREE, \
+ pool, addr, 0, 0, 0)
+
+/* Disassociate any pieces outside a particular range. */
+#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MEMPOOL_TRIM, \
+ pool, addr, size, 0, 0)
+
+/* Resize and/or move a piece associated with a memory pool. */
+#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MOVE_MEMPOOL, \
+ poolA, poolB, 0, 0, 0)
+
+/* Resize and/or move a piece associated with a memory pool. */
+#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MEMPOOL_CHANGE, \
+ pool, addrA, addrB, size, 0)
+
+/* Return 1 if a mempool exists, else 0. */
+#define VALGRIND_MEMPOOL_EXISTS(pool) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MEMPOOL_EXISTS, \
+ pool, 0, 0, 0, 0)
+
+/* Mark a piece of memory as being a stack. Returns a stack id. */
+#define VALGRIND_STACK_REGISTER(start, end) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__STACK_REGISTER, \
+ start, end, 0, 0, 0)
+
+/* Unmark the piece of memory associated with a stack id as being a
+ stack. */
+#define VALGRIND_STACK_DEREGISTER(id) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__STACK_DEREGISTER, \
+ id, 0, 0, 0, 0)
+
+/* Change the start and end address of the stack id. */
+#define VALGRIND_STACK_CHANGE(id, start, end) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__STACK_CHANGE, \
+ id, start, end, 0, 0)
+
+/* Load PDB debug info for Wine PE image_map. */
+#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__LOAD_PDB_DEBUGINFO, \
+ fd, ptr, total_size, delta, 0)
+
+/* Map a code address to a source file name and line number. buf64
+ must point to a 64-byte buffer in the caller's address space. The
+ result will be dumped in there and is guaranteed to be zero
+ terminated. If no info is found, the first byte is set to zero. */
+#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MAP_IP_TO_SRCLOC, \
+ addr, buf64, 0, 0, 0)
+
+
+#undef PLAT_x86_darwin
+#undef PLAT_amd64_darwin
+#undef PLAT_x86_win32
+#undef PLAT_x86_linux
+#undef PLAT_amd64_linux
+#undef PLAT_ppc32_linux
+#undef PLAT_ppc64_linux
+#undef PLAT_arm_linux
+#undef PLAT_s390x_linux
+
+#endif /* __VALGRIND_H */
diff --git a/chromium/v8/src/third_party/vtune/ittnotify_config.h b/chromium/v8/src/third_party/vtune/ittnotify_config.h
new file mode 100644
index 00000000000..412e344628e
--- /dev/null
+++ b/chromium/v8/src/third_party/vtune/ittnotify_config.h
@@ -0,0 +1,484 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ The full GNU General Public License is included in this distribution
+ in the file called LICENSE.GPL.
+
+ Contact Information:
+ http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+ BSD LICENSE
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+ 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 Intel Corporation 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 _ITTNOTIFY_CONFIG_H_
+#define _ITTNOTIFY_CONFIG_H_
+
+/** @cond exclude_from_documentation */
+#ifndef ITT_OS_WIN
+# define ITT_OS_WIN 1
+#endif /* ITT_OS_WIN */
+
+#ifndef ITT_OS_LINUX
+# define ITT_OS_LINUX 2
+#endif /* ITT_OS_LINUX */
+
+#ifndef ITT_OS_MAC
+# define ITT_OS_MAC 3
+#endif /* ITT_OS_MAC */
+
+#ifndef ITT_OS
+# if defined WIN32 || defined _WIN32
+# define ITT_OS ITT_OS_WIN
+# elif defined( __APPLE__ ) && defined( __MACH__ )
+# define ITT_OS ITT_OS_MAC
+# else
+# define ITT_OS ITT_OS_LINUX
+# endif
+#endif /* ITT_OS */
+
+#ifndef ITT_PLATFORM_WIN
+# define ITT_PLATFORM_WIN 1
+#endif /* ITT_PLATFORM_WIN */
+
+#ifndef ITT_PLATFORM_POSIX
+# define ITT_PLATFORM_POSIX 2
+#endif /* ITT_PLATFORM_POSIX */
+
+#ifndef ITT_PLATFORM
+# if ITT_OS==ITT_OS_WIN
+# define ITT_PLATFORM ITT_PLATFORM_WIN
+# else
+# define ITT_PLATFORM ITT_PLATFORM_POSIX
+# endif /* _WIN32 */
+#endif /* ITT_PLATFORM */
+
+#if defined(_UNICODE) && !defined(UNICODE)
+#define UNICODE
+#endif
+
+#include <stddef.h>
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#include <tchar.h>
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#include <stdint.h>
+#if defined(UNICODE) || defined(_UNICODE)
+#include <wchar.h>
+#endif /* UNICODE || _UNICODE */
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+#ifndef CDECL
+# if ITT_PLATFORM==ITT_PLATFORM_WIN
+# define CDECL __cdecl
+# else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+# if defined _M_X64 || defined _M_AMD64 || defined __x86_64__
+# define CDECL /* not actual on x86_64 platform */
+# else /* _M_X64 || _M_AMD64 || __x86_64__ */
+# define CDECL __attribute__ ((cdecl))
+# endif /* _M_X64 || _M_AMD64 || __x86_64__ */
+# endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#endif /* CDECL */
+
+#ifndef STDCALL
+# if ITT_PLATFORM==ITT_PLATFORM_WIN
+# define STDCALL __stdcall
+# else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+# if defined _M_X64 || defined _M_AMD64 || defined __x86_64__
+# define STDCALL /* not supported on x86_64 platform */
+# else /* _M_X64 || _M_AMD64 || __x86_64__ */
+# define STDCALL __attribute__ ((stdcall))
+# endif /* _M_X64 || _M_AMD64 || __x86_64__ */
+# endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#endif /* STDCALL */
+
+#define ITTAPI CDECL
+#define LIBITTAPI CDECL
+
+/* TODO: Temporary for compatibility! */
+#define ITTAPI_CALL CDECL
+#define LIBITTAPI_CALL CDECL
+
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+/* use __forceinline (VC++ specific) */
+#define ITT_INLINE __forceinline
+#define ITT_INLINE_ATTRIBUTE /* nothing */
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+/*
+ * Generally, functions are not inlined unless optimization is specified.
+ * For functions declared inline, this attribute inlines the function even
+ * if no optimization level was specified.
+ */
+#ifdef __STRICT_ANSI__
+#define ITT_INLINE static
+#else /* __STRICT_ANSI__ */
+#define ITT_INLINE static inline
+#endif /* __STRICT_ANSI__ */
+#define ITT_INLINE_ATTRIBUTE __attribute__ ((always_inline))
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+/** @endcond */
+
+#ifndef ITT_ARCH_IA32
+# define ITT_ARCH_IA32 1
+#endif /* ITT_ARCH_IA32 */
+
+#ifndef ITT_ARCH_IA32E
+# define ITT_ARCH_IA32E 2
+#endif /* ITT_ARCH_IA32E */
+
+#ifndef ITT_ARCH_IA64
+# define ITT_ARCH_IA64 3
+#endif /* ITT_ARCH_IA64 */
+
+#ifndef ITT_ARCH
+# if defined _M_X64 || defined _M_AMD64 || defined __x86_64__
+# define ITT_ARCH ITT_ARCH_IA32E
+# elif defined _M_IA64 || defined __ia64
+# define ITT_ARCH ITT_ARCH_IA64
+# else
+# define ITT_ARCH ITT_ARCH_IA32
+# endif
+#endif
+
+#ifdef __cplusplus
+# define ITT_EXTERN_C extern "C"
+#else
+# define ITT_EXTERN_C /* nothing */
+#endif /* __cplusplus */
+
+#define ITT_TO_STR_AUX(x) #x
+#define ITT_TO_STR(x) ITT_TO_STR_AUX(x)
+
+#define __ITT_BUILD_ASSERT(expr, suffix) do { static char __itt_build_check_##suffix[(expr) ? 1 : -1]; __itt_build_check_##suffix[0] = 0; } while(0)
+#define _ITT_BUILD_ASSERT(expr, suffix) __ITT_BUILD_ASSERT((expr), suffix)
+#define ITT_BUILD_ASSERT(expr) _ITT_BUILD_ASSERT((expr), __LINE__)
+
+#define ITT_MAGIC { 0xED, 0xAB, 0xAB, 0xEC, 0x0D, 0xEE, 0xDA, 0x30 }
+
+/* Replace with snapshot date YYYYMMDD for promotion build. */
+#define API_VERSION_BUILD 20111111
+
+#ifndef API_VERSION_NUM
+#define API_VERSION_NUM 0.0.0
+#endif /* API_VERSION_NUM */
+
+#define API_VERSION "ITT-API-Version " ITT_TO_STR(API_VERSION_NUM) " (" ITT_TO_STR(API_VERSION_BUILD) ")"
+
+/* OS communication functions */
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#include <windows.h>
+typedef HMODULE lib_t;
+typedef DWORD TIDT;
+typedef CRITICAL_SECTION mutex_t;
+#define MUTEX_INITIALIZER { 0 }
+#define strong_alias(name, aliasname) /* empty for Windows */
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#include <dlfcn.h>
+#if defined(UNICODE) || defined(_UNICODE)
+#include <wchar.h>
+#endif /* UNICODE */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1 /* need for PTHREAD_MUTEX_RECURSIVE */
+#endif /* _GNU_SOURCE */
+#include <pthread.h>
+typedef void* lib_t;
+typedef pthread_t TIDT;
+typedef pthread_mutex_t mutex_t;
+#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#define _strong_alias(name, aliasname) extern __typeof (name) aliasname __attribute__ ((alias (#name)));
+#define strong_alias(name, aliasname) _strong_alias(name, aliasname)
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#define __itt_get_proc(lib, name) GetProcAddress(lib, name)
+#define __itt_mutex_init(mutex) InitializeCriticalSection(mutex)
+#define __itt_mutex_lock(mutex) EnterCriticalSection(mutex)
+#define __itt_mutex_unlock(mutex) LeaveCriticalSection(mutex)
+#define __itt_load_lib(name) LoadLibraryA(name)
+#define __itt_unload_lib(handle) FreeLibrary(handle)
+#define __itt_system_error() (int)GetLastError()
+#define __itt_fstrcmp(s1, s2) lstrcmpA(s1, s2)
+#define __itt_fstrlen(s) lstrlenA(s)
+#define __itt_fstrcpyn(s1, s2, l) lstrcpynA(s1, s2, l)
+#define __itt_fstrdup(s) _strdup(s)
+#define __itt_thread_id() GetCurrentThreadId()
+#define __itt_thread_yield() SwitchToThread()
+#ifndef ITT_SIMPLE_INIT
+ITT_INLINE int __itt_interlocked_increment(volatile long* ptr) ITT_INLINE_ATTRIBUTE;
+ITT_INLINE int __itt_interlocked_increment(volatile long* ptr)
+{
+ return InterlockedIncrement(ptr);
+}
+#endif /* ITT_SIMPLE_INIT */
+#else /* ITT_PLATFORM!=ITT_PLATFORM_WIN */
+#define __itt_get_proc(lib, name) dlsym(lib, name)
+#define __itt_mutex_init(mutex) \
+ { \
+ pthread_mutexattr_t mutex_attr; \
+ int error_code = pthread_mutexattr_init(&mutex_attr); \
+ if (error_code) \
+ __itt_report_error(__itt_error_system, "pthread_mutexattr_init", error_code); \
+ error_code = pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); \
+ if (error_code) \
+ __itt_report_error(__itt_error_system, "pthread_mutexattr_settype", error_code); \
+ error_code = pthread_mutex_init(mutex, &mutex_attr); \
+ if (error_code) \
+ __itt_report_error(__itt_error_system, "pthread_mutex_init", error_code); \
+ error_code = pthread_mutexattr_destroy(&mutex_attr); \
+ if (error_code) \
+ __itt_report_error(__itt_error_system, "pthread_mutexattr_destroy", error_code); \
+ }
+#define __itt_mutex_lock(mutex) pthread_mutex_lock(mutex)
+#define __itt_mutex_unlock(mutex) pthread_mutex_unlock(mutex)
+#define __itt_load_lib(name) dlopen(name, RTLD_LAZY)
+#define __itt_unload_lib(handle) dlclose(handle)
+#define __itt_system_error() errno
+#define __itt_fstrcmp(s1, s2) strcmp(s1, s2)
+#define __itt_fstrlen(s) strlen(s)
+#define __itt_fstrcpyn(s1, s2, l) strncpy(s1, s2, l)
+#define __itt_fstrdup(s) strdup(s)
+#define __itt_thread_id() pthread_self()
+#define __itt_thread_yield() sched_yield()
+#if ITT_ARCH==ITT_ARCH_IA64
+#ifdef __INTEL_COMPILER
+#define __TBB_machine_fetchadd4(addr, val) __fetchadd4_acq((void *)addr, val)
+#else /* __INTEL_COMPILER */
+/* TODO: Add Support for not Intel compilers for IA64 */
+#endif /* __INTEL_COMPILER */
+#else /* ITT_ARCH!=ITT_ARCH_IA64 */
+/*ITT_INLINE int __TBB_machine_fetchadd4(volatile void* ptr, long addend) ITT_INLINE_ATTRIBUTE;
+ITT_INLINE int __TBB_machine_fetchadd4(volatile void* ptr, long addend)
+{
+ int result;
+ __asm__ __volatile__("lock\nxaddl %0,%1"
+ : "=r"(result),"=m"(*(long*)ptr)
+ : "0"(addend), "m"(*(long*)ptr)
+ : "memory");
+ return result;
+}
+*/
+#endif /* ITT_ARCH==ITT_ARCH_IA64 */
+#ifndef ITT_SIMPLE_INIT
+/*ITT_INLINE int __itt_interlocked_increment(volatile long* ptr) ITT_INLINE_ATTRIBUTE;
+ITT_INLINE int __itt_interlocked_increment(volatile long* ptr)
+{
+ return __TBB_machine_fetchadd4(ptr, 1) + 1;
+}
+*/
+#endif /* ITT_SIMPLE_INIT */
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+typedef enum {
+ __itt_collection_normal = 0,
+ __itt_collection_paused = 1
+} __itt_collection_state;
+
+typedef enum {
+ __itt_thread_normal = 0,
+ __itt_thread_ignored = 1
+} __itt_thread_state;
+
+#pragma pack(push, 8)
+
+typedef struct ___itt_thread_info
+{
+ const char* nameA; /*!< Copy of original name in ASCII. */
+#if defined(UNICODE) || defined(_UNICODE)
+ const wchar_t* nameW; /*!< Copy of original name in UNICODE. */
+#else /* UNICODE || _UNICODE */
+ void* nameW;
+#endif /* UNICODE || _UNICODE */
+ TIDT tid;
+ __itt_thread_state state; /*!< Thread state (paused or normal) */
+ int extra1; /*!< Reserved to the runtime */
+ void* extra2; /*!< Reserved to the runtime */
+ struct ___itt_thread_info* next;
+} __itt_thread_info;
+
+#include "ittnotify_types.h" /* For __itt_group_id definition */
+
+typedef struct ___itt_api_info_20101001
+{
+ const char* name;
+ void** func_ptr;
+ void* init_func;
+ __itt_group_id group;
+} __itt_api_info_20101001;
+
+typedef struct ___itt_api_info
+{
+ const char* name;
+ void** func_ptr;
+ void* init_func;
+ void* null_func;
+ __itt_group_id group;
+} __itt_api_info;
+
+struct ___itt_domain;
+struct ___itt_string_handle;
+
+typedef struct ___itt_global
+{
+ unsigned char magic[8];
+ unsigned long version_major;
+ unsigned long version_minor;
+ unsigned long version_build;
+ volatile long api_initialized;
+ volatile long mutex_initialized;
+ volatile long atomic_counter;
+ mutex_t mutex;
+ lib_t lib;
+ void* error_handler;
+ const char** dll_path_ptr;
+ __itt_api_info* api_list_ptr;
+ struct ___itt_global* next;
+ /* Joinable structures below */
+ __itt_thread_info* thread_list;
+ struct ___itt_domain* domain_list;
+ struct ___itt_string_handle* string_list;
+ __itt_collection_state state;
+} __itt_global;
+
+#pragma pack(pop)
+
+#define NEW_THREAD_INFO_W(gptr,h,h_tail,t,s,n) { \
+ h = (__itt_thread_info*)malloc(sizeof(__itt_thread_info)); \
+ if (h != NULL) { \
+ h->tid = t; \
+ h->nameA = NULL; \
+ h->nameW = n ? _wcsdup(n) : NULL; \
+ h->state = s; \
+ h->extra1 = 0; /* reserved */ \
+ h->extra2 = NULL; /* reserved */ \
+ h->next = NULL; \
+ if (h_tail == NULL) \
+ (gptr)->thread_list = h; \
+ else \
+ h_tail->next = h; \
+ } \
+}
+
+#define NEW_THREAD_INFO_A(gptr,h,h_tail,t,s,n) { \
+ h = (__itt_thread_info*)malloc(sizeof(__itt_thread_info)); \
+ if (h != NULL) { \
+ h->tid = t; \
+ h->nameA = n ? __itt_fstrdup(n) : NULL; \
+ h->nameW = NULL; \
+ h->state = s; \
+ h->extra1 = 0; /* reserved */ \
+ h->extra2 = NULL; /* reserved */ \
+ h->next = NULL; \
+ if (h_tail == NULL) \
+ (gptr)->thread_list = h; \
+ else \
+ h_tail->next = h; \
+ } \
+}
+
+#define NEW_DOMAIN_W(gptr,h,h_tail,name) { \
+ h = (__itt_domain*)malloc(sizeof(__itt_domain)); \
+ if (h != NULL) { \
+ h->flags = 0; /* domain is disabled by default */ \
+ h->nameA = NULL; \
+ h->nameW = name ? _wcsdup(name) : NULL; \
+ h->extra1 = 0; /* reserved */ \
+ h->extra2 = NULL; /* reserved */ \
+ h->next = NULL; \
+ if (h_tail == NULL) \
+ (gptr)->domain_list = h; \
+ else \
+ h_tail->next = h; \
+ } \
+}
+
+#define NEW_DOMAIN_A(gptr,h,h_tail,name) { \
+ h = (__itt_domain*)malloc(sizeof(__itt_domain)); \
+ if (h != NULL) { \
+ h->flags = 0; /* domain is disabled by default */ \
+ h->nameA = name ? __itt_fstrdup(name) : NULL; \
+ h->nameW = NULL; \
+ h->extra1 = 0; /* reserved */ \
+ h->extra2 = NULL; /* reserved */ \
+ h->next = NULL; \
+ if (h_tail == NULL) \
+ (gptr)->domain_list = h; \
+ else \
+ h_tail->next = h; \
+ } \
+}
+
+#define NEW_STRING_HANDLE_W(gptr,h,h_tail,name) { \
+ h = (__itt_string_handle*)malloc(sizeof(__itt_string_handle)); \
+ if (h != NULL) { \
+ h->strA = NULL; \
+ h->strW = name ? _wcsdup(name) : NULL; \
+ h->extra1 = 0; /* reserved */ \
+ h->extra2 = NULL; /* reserved */ \
+ h->next = NULL; \
+ if (h_tail == NULL) \
+ (gptr)->string_list = h; \
+ else \
+ h_tail->next = h; \
+ } \
+}
+
+#define NEW_STRING_HANDLE_A(gptr,h,h_tail,name) { \
+ h = (__itt_string_handle*)malloc(sizeof(__itt_string_handle)); \
+ if (h != NULL) { \
+ h->strA = name ? __itt_fstrdup(name) : NULL; \
+ h->strW = NULL; \
+ h->extra1 = 0; /* reserved */ \
+ h->extra2 = NULL; /* reserved */ \
+ h->next = NULL; \
+ if (h_tail == NULL) \
+ (gptr)->string_list = h; \
+ else \
+ h_tail->next = h; \
+ } \
+}
+
+#endif /* _ITTNOTIFY_CONFIG_H_ */
+
diff --git a/chromium/v8/src/third_party/vtune/ittnotify_types.h b/chromium/v8/src/third_party/vtune/ittnotify_types.h
new file mode 100644
index 00000000000..736c1f5b5ec
--- /dev/null
+++ b/chromium/v8/src/third_party/vtune/ittnotify_types.h
@@ -0,0 +1,113 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ The full GNU General Public License is included in this distribution
+ in the file called LICENSE.GPL.
+
+ Contact Information:
+ http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+ BSD LICENSE
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+ 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 Intel Corporation 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 _ITTNOTIFY_TYPES_H_
+#define _ITTNOTIFY_TYPES_H_
+
+typedef enum ___itt_group_id
+{
+ __itt_group_none = 0,
+ __itt_group_legacy = 1<<0,
+ __itt_group_control = 1<<1,
+ __itt_group_thread = 1<<2,
+ __itt_group_mark = 1<<3,
+ __itt_group_sync = 1<<4,
+ __itt_group_fsync = 1<<5,
+ __itt_group_jit = 1<<6,
+ __itt_group_model = 1<<7,
+ __itt_group_splitter_min = 1<<7,
+ __itt_group_counter = 1<<8,
+ __itt_group_frame = 1<<9,
+ __itt_group_stitch = 1<<10,
+ __itt_group_heap = 1<<11,
+ __itt_group_splitter_max = 1<<12,
+ __itt_group_structure = 1<<12,
+ __itt_group_suppress = 1<<13,
+ __itt_group_all = -1
+} __itt_group_id;
+
+#pragma pack(push, 8)
+
+typedef struct ___itt_group_list
+{
+ __itt_group_id id;
+ const char* name;
+} __itt_group_list;
+
+#pragma pack(pop)
+
+#define ITT_GROUP_LIST(varname) \
+ static __itt_group_list varname[] = { \
+ { __itt_group_all, "all" }, \
+ { __itt_group_control, "control" }, \
+ { __itt_group_thread, "thread" }, \
+ { __itt_group_mark, "mark" }, \
+ { __itt_group_sync, "sync" }, \
+ { __itt_group_fsync, "fsync" }, \
+ { __itt_group_jit, "jit" }, \
+ { __itt_group_model, "model" }, \
+ { __itt_group_counter, "counter" }, \
+ { __itt_group_frame, "frame" }, \
+ { __itt_group_stitch, "stitch" }, \
+ { __itt_group_heap, "heap" }, \
+ { __itt_group_structure, "structure" }, \
+ { __itt_group_suppress, "suppress" }, \
+ { __itt_group_none, NULL } \
+ }
+
+#endif /* _ITTNOTIFY_TYPES_H_ */
+
diff --git a/chromium/v8/src/third_party/vtune/jitprofiling.cc b/chromium/v8/src/third_party/vtune/jitprofiling.cc
new file mode 100644
index 00000000000..b3952b32169
--- /dev/null
+++ b/chromium/v8/src/third_party/vtune/jitprofiling.cc
@@ -0,0 +1,499 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ The full GNU General Public License is included in this distribution
+ in the file called LICENSE.GPL.
+
+ Contact Information:
+ http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+ BSD LICENSE
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+ 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 Intel Corporation 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 "ittnotify_config.h"
+
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#include <windows.h>
+#pragma optimize("", off)
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#include <pthread.h>
+#include <dlfcn.h>
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#include <malloc.h>
+#include <stdlib.h>
+
+#include "jitprofiling.h"
+
+static const char rcsid[] = "\n@(#) $Revision: 234474 $\n";
+
+#define DLL_ENVIRONMENT_VAR "VS_PROFILER"
+
+#ifndef NEW_DLL_ENVIRONMENT_VAR
+#if ITT_ARCH==ITT_ARCH_IA32
+#define NEW_DLL_ENVIRONMENT_VAR "INTEL_JIT_PROFILER32"
+#else
+#define NEW_DLL_ENVIRONMENT_VAR "INTEL_JIT_PROFILER64"
+#endif
+#endif /* NEW_DLL_ENVIRONMENT_VAR */
+
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+#define DEFAULT_DLLNAME "JitPI.dll"
+HINSTANCE m_libHandle = NULL;
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+#define DEFAULT_DLLNAME "libJitPI.so"
+void* m_libHandle = NULL;
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+/* default location of JIT profiling agent on Android */
+#define ANDROID_JIT_AGENT_PATH "/data/intel/libittnotify.so"
+
+/* the function pointers */
+typedef unsigned int(*TPInitialize)(void);
+static TPInitialize FUNC_Initialize=NULL;
+
+typedef unsigned int(*TPNotify)(unsigned int, void*);
+static TPNotify FUNC_NotifyEvent=NULL;
+
+static iJIT_IsProfilingActiveFlags executionMode = iJIT_NOTHING_RUNNING;
+
+/* end collector dll part. */
+
+/* loadiJIT_Funcs() : this function is called just in the beginning and is responsible
+** to load the functions from BistroJavaCollector.dll
+** result:
+** on success: the functions loads, iJIT_DLL_is_missing=0, return value = 1.
+** on failure: the functions are NULL, iJIT_DLL_is_missing=1, return value = 0.
+*/
+static int loadiJIT_Funcs(void);
+
+/* global representing whether the BistroJavaCollector can't be loaded */
+static int iJIT_DLL_is_missing = 0;
+
+/* Virtual stack - the struct is used as a virtual stack for each thread.
+** Every thread initializes with a stack of size INIT_TOP_STACK.
+** Every method entry decreases from the current stack point,
+** and when a thread stack reaches its top of stack (return from the global function),
+** the top of stack and the current stack increase. Notice that when returning from a function
+** the stack pointer is the address of the function return.
+*/
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+static DWORD threadLocalStorageHandle = 0;
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+static pthread_key_t threadLocalStorageHandle = (pthread_key_t)0;
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+#define INIT_TOP_Stack 10000
+
+typedef struct
+{
+ unsigned int TopStack;
+ unsigned int CurrentStack;
+} ThreadStack, *pThreadStack;
+
+/* end of virtual stack. */
+
+/*
+** The function for reporting virtual-machine related events to VTune.
+** Note: when reporting iJVM_EVENT_TYPE_ENTER_NIDS, there is no need to fill in the stack_id
+** field in the iJIT_Method_NIDS structure, as VTune fills it.
+**
+** The return value in iJVM_EVENT_TYPE_ENTER_NIDS && iJVM_EVENT_TYPE_LEAVE_NIDS events
+** will be 0 in case of failure.
+** in iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED event it will be -1 if EventSpecificData == 0
+** otherwise it will be 0.
+*/
+
+ITT_EXTERN_C int JITAPI iJIT_NotifyEvent(iJIT_JVM_EVENT event_type, void *EventSpecificData)
+{
+ int ReturnValue;
+
+ /*******************************************************************************
+ ** This section is for debugging outside of VTune.
+ ** It creates the environment variables that indicates call graph mode.
+ ** If running outside of VTune remove the remark.
+ **
+
+ static int firstTime = 1;
+ char DoCallGraph[12] = "DoCallGraph";
+ if (firstTime)
+ {
+ firstTime = 0;
+ SetEnvironmentVariable( "BISTRO_COLLECTORS_DO_CALLGRAPH", DoCallGraph);
+ }
+
+ ** end of section.
+ *******************************************************************************/
+
+ /* initialization part - the functions have not been loaded yet. This part
+ ** will load the functions, and check if we are in Call Graph mode.
+ ** (for special treatment).
+ */
+ if (!FUNC_NotifyEvent)
+ {
+ if (iJIT_DLL_is_missing)
+ return 0;
+
+ // load the Function from the DLL
+ if (!loadiJIT_Funcs())
+ return 0;
+
+ /* Call Graph initialization. */
+ }
+
+ /* If the event is method entry/exit, check that in the current mode
+ ** VTune is allowed to receive it
+ */
+ if ((event_type == iJVM_EVENT_TYPE_ENTER_NIDS || event_type == iJVM_EVENT_TYPE_LEAVE_NIDS) &&
+ (executionMode != iJIT_CALLGRAPH_ON))
+ {
+ return 0;
+ }
+ /* This section is performed when method enter event occurs.
+ ** It updates the virtual stack, or creates it if this is the first
+ ** method entry in the thread. The stack pointer is decreased.
+ */
+ if (event_type == iJVM_EVENT_TYPE_ENTER_NIDS)
+ {
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ pThreadStack threadStack = (pThreadStack)TlsGetValue (threadLocalStorageHandle);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ pThreadStack threadStack = (pThreadStack)pthread_getspecific(threadLocalStorageHandle);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+ // check for use of reserved method IDs
+ if ( ((piJIT_Method_NIDS) EventSpecificData)->method_id <= 999 )
+ return 0;
+
+ if (!threadStack)
+ {
+ // initialize the stack.
+ threadStack = (pThreadStack) calloc (sizeof(ThreadStack), 1);
+ threadStack->TopStack = INIT_TOP_Stack;
+ threadStack->CurrentStack = INIT_TOP_Stack;
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ TlsSetValue(threadLocalStorageHandle,(void*)threadStack);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ pthread_setspecific(threadLocalStorageHandle,(void*)threadStack);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ }
+
+ // decrease the stack.
+ ((piJIT_Method_NIDS) EventSpecificData)->stack_id = (threadStack->CurrentStack)--;
+ }
+
+ /* This section is performed when method leave event occurs
+ ** It updates the virtual stack.
+ ** Increases the stack pointer.
+ ** If the stack pointer reached the top (left the global function)
+ ** increase the pointer and the top pointer.
+ */
+ if (event_type == iJVM_EVENT_TYPE_LEAVE_NIDS)
+ {
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ pThreadStack threadStack = (pThreadStack)TlsGetValue (threadLocalStorageHandle);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ pThreadStack threadStack = (pThreadStack)pthread_getspecific(threadLocalStorageHandle);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+ // check for use of reserved method IDs
+ if ( ((piJIT_Method_NIDS) EventSpecificData)->method_id <= 999 )
+ return 0;
+
+ if (!threadStack)
+ {
+ /* Error: first report in this thread is method exit */
+ exit (1);
+ }
+
+ ((piJIT_Method_NIDS) EventSpecificData)->stack_id = ++(threadStack->CurrentStack) + 1;
+
+ if (((piJIT_Method_NIDS) EventSpecificData)->stack_id > threadStack->TopStack)
+ ((piJIT_Method_NIDS) EventSpecificData)->stack_id = (unsigned int)-1;
+ }
+
+ if (event_type == iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED)
+ {
+ // check for use of reserved method IDs
+ if ( ((piJIT_Method_Load) EventSpecificData)->method_id <= 999 )
+ return 0;
+ }
+
+ ReturnValue = (int)FUNC_NotifyEvent(event_type, EventSpecificData);
+
+ return ReturnValue;
+}
+
+ITT_EXTERN_C void JITAPI iJIT_RegisterCallbackEx(void *userdata, iJIT_ModeChangedEx NewModeCallBackFuncEx) // The new mode call back routine
+{
+ // is it already missing... or the load of functions from the DLL failed
+ if (iJIT_DLL_is_missing || !loadiJIT_Funcs())
+ {
+ NewModeCallBackFuncEx(userdata, iJIT_NO_NOTIFICATIONS); // then do not bother with notifications
+ /* Error: could not load JIT functions. */
+ return;
+ }
+ // nothing to do with the callback
+}
+
+/*
+** This function allows the user to query in which mode, if at all, VTune is running
+*/
+ITT_EXTERN_C iJIT_IsProfilingActiveFlags JITAPI iJIT_IsProfilingActive()
+{
+ if (!iJIT_DLL_is_missing)
+ {
+ loadiJIT_Funcs();
+ }
+
+ return executionMode;
+}
+#include <stdio.h>
+/* this function loads the collector dll (BistroJavaCollector) and the relevant functions.
+** on success: all functions load, iJIT_DLL_is_missing = 0, return value = 1.
+** on failure: all functions are NULL, iJIT_DLL_is_missing = 1, return value = 0.
+*/
+static int loadiJIT_Funcs()
+{
+ static int bDllWasLoaded = 0;
+ char *dllName = (char*)rcsid; // !!! Just to avoid unused code elimination !!!
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ DWORD dNameLength = 0;
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+ if(bDllWasLoaded)
+ {// dll was already loaded, no need to do it for the second time
+ return 1;
+ }
+
+ // Assumes that the DLL will not be found
+ iJIT_DLL_is_missing = 1;
+ FUNC_NotifyEvent = NULL;
+
+ if (m_libHandle)
+ {
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ FreeLibrary(m_libHandle);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ dlclose(m_libHandle);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ m_libHandle = NULL;
+ }
+
+ // try to get the dll name from the environment
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ dNameLength = GetEnvironmentVariableA(NEW_DLL_ENVIRONMENT_VAR, NULL, 0);
+ if (dNameLength)
+ {
+ DWORD envret = 0;
+ dllName = (char*)malloc(sizeof(char) * (dNameLength + 1));
+ envret = GetEnvironmentVariableA(NEW_DLL_ENVIRONMENT_VAR, dllName, dNameLength);
+ if (envret)
+ {
+ // Try to load the dll from the PATH...
+ m_libHandle = LoadLibraryExA(dllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+ }
+ free(dllName);
+ } else {
+ // Try to use old VS_PROFILER variable
+ dNameLength = GetEnvironmentVariableA(DLL_ENVIRONMENT_VAR, NULL, 0);
+ if (dNameLength)
+ {
+ DWORD envret = 0;
+ dllName = (char*)malloc(sizeof(char) * (dNameLength + 1));
+ envret = GetEnvironmentVariableA(DLL_ENVIRONMENT_VAR, dllName, dNameLength);
+ if (envret)
+ {
+ // Try to load the dll from the PATH...
+ m_libHandle = LoadLibraryA(dllName);
+ }
+ free(dllName);
+ }
+ }
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ dllName = getenv(NEW_DLL_ENVIRONMENT_VAR);
+ if (!dllName) {
+ dllName = getenv(DLL_ENVIRONMENT_VAR);
+ }
+#ifdef ANDROID
+ if (!dllName)
+ dllName = ANDROID_JIT_AGENT_PATH;
+#endif
+ if (dllName)
+ {
+ // Try to load the dll from the PATH...
+ m_libHandle = dlopen(dllName, RTLD_LAZY);
+ }
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+
+ if (!m_libHandle)
+ {
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ m_libHandle = LoadLibraryA(DEFAULT_DLLNAME);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ m_libHandle = dlopen(DEFAULT_DLLNAME, RTLD_LAZY);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ }
+
+ // if the dll wasn't loaded - exit.
+ if (!m_libHandle)
+ {
+ iJIT_DLL_is_missing = 1; // don't try to initialize JIT agent the second time
+ return 0;
+ }
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ FUNC_NotifyEvent = (TPNotify)GetProcAddress(m_libHandle, "NotifyEvent");
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ FUNC_NotifyEvent = reinterpret_cast<TPNotify>(reinterpret_cast<intptr_t>(dlsym(m_libHandle, "NotifyEvent")));
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ if (!FUNC_NotifyEvent)
+ {
+ FUNC_Initialize = NULL;
+ return 0;
+ }
+
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ FUNC_Initialize = (TPInitialize)GetProcAddress(m_libHandle, "Initialize");
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ FUNC_Initialize = reinterpret_cast<TPInitialize>(reinterpret_cast<intptr_t>(dlsym(m_libHandle, "Initialize")));
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ if (!FUNC_Initialize)
+ {
+ FUNC_NotifyEvent = NULL;
+ return 0;
+ }
+
+ executionMode = (iJIT_IsProfilingActiveFlags)FUNC_Initialize();
+ if (executionMode != iJIT_SAMPLING_ON)
+ executionMode = iJIT_SAMPLING_ON;
+
+ bDllWasLoaded = 1;
+ iJIT_DLL_is_missing = 0; // DLL is ok.
+
+ /*
+ ** Call Graph mode: init the thread local storage
+ ** (need to store the virtual stack there).
+ */
+ if ( executionMode == iJIT_CALLGRAPH_ON )
+ {
+ // Allocate a thread local storage slot for the thread "stack"
+ if (!threadLocalStorageHandle)
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ threadLocalStorageHandle = TlsAlloc();
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ pthread_key_create(&threadLocalStorageHandle, NULL);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ }
+
+ return 1;
+}
+
+/*
+** This function should be called by the user whenever a thread ends, to free the thread
+** "virtual stack" storage
+*/
+ITT_EXTERN_C void JITAPI FinalizeThread()
+{
+ if (threadLocalStorageHandle)
+ {
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ pThreadStack threadStack = (pThreadStack)TlsGetValue (threadLocalStorageHandle);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ pThreadStack threadStack = (pThreadStack)pthread_getspecific(threadLocalStorageHandle);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ if (threadStack)
+ {
+ free (threadStack);
+ threadStack = NULL;
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ TlsSetValue (threadLocalStorageHandle, threadStack);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ pthread_setspecific(threadLocalStorageHandle, threadStack);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ }
+ }
+}
+
+/*
+** This function should be called by the user when the process ends, to free the local
+** storage index
+*/
+ITT_EXTERN_C void JITAPI FinalizeProcess()
+{
+ if (m_libHandle)
+ {
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ FreeLibrary(m_libHandle);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ dlclose(m_libHandle);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ m_libHandle = NULL;
+ }
+
+ if (threadLocalStorageHandle)
+#if ITT_PLATFORM==ITT_PLATFORM_WIN
+ TlsFree (threadLocalStorageHandle);
+#else /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+ pthread_key_delete(threadLocalStorageHandle);
+#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
+}
+
+/*
+** This function should be called by the user for any method once.
+** The function will return a unique method ID, the user should maintain the ID for each
+** method
+*/
+ITT_EXTERN_C unsigned int JITAPI iJIT_GetNewMethodID()
+{
+ static unsigned int methodID = 0x100000;
+
+ if (methodID == 0)
+ return 0; // ERROR : this is not a valid value
+
+ return methodID++;
+}
+
diff --git a/chromium/v8/src/third_party/vtune/jitprofiling.h b/chromium/v8/src/third_party/vtune/jitprofiling.h
new file mode 100644
index 00000000000..abd6d8ca786
--- /dev/null
+++ b/chromium/v8/src/third_party/vtune/jitprofiling.h
@@ -0,0 +1,298 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+
+ Copyright (c) 2005-2012 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ The full GNU General Public License is included in this distribution
+ in the file called LICENSE.GPL.
+
+ Contact Information:
+ http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+ BSD LICENSE
+
+ Copyright (c) 2005-2012 Intel Corporation. All rights reserved.
+ 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 Intel Corporation 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 __JITPROFILING_H__
+#define __JITPROFILING_H__
+
+/*
+ * Various constants used by functions
+ */
+
+/* event notification */
+typedef enum iJIT_jvm_event
+{
+
+ /* shutdown */
+
+ /*
+ * Program exiting EventSpecificData NA
+ */
+ iJVM_EVENT_TYPE_SHUTDOWN = 2,
+
+ /* JIT profiling */
+
+ /*
+ * issued after method code jitted into memory but before code is executed
+ * EventSpecificData is an iJIT_Method_Load
+ */
+ iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED=13,
+
+ /* issued before unload. Method code will no longer be executed, but code
+ * and info are still in memory. The VTune profiler may capture method
+ * code only at this point EventSpecificData is iJIT_Method_Id
+ */
+ iJVM_EVENT_TYPE_METHOD_UNLOAD_START,
+
+ /* Method Profiling */
+
+ /* method name, Id and stack is supplied
+ * issued when a method is about to be entered EventSpecificData is
+ * iJIT_Method_NIDS
+ */
+ iJVM_EVENT_TYPE_ENTER_NIDS = 19,
+
+ /* method name, Id and stack is supplied
+ * issued when a method is about to be left EventSpecificData is
+ * iJIT_Method_NIDS
+ */
+ iJVM_EVENT_TYPE_LEAVE_NIDS
+} iJIT_JVM_EVENT;
+
+typedef enum _iJIT_ModeFlags
+{
+ /* No need to Notify VTune, since VTune is not running */
+ iJIT_NO_NOTIFICATIONS = 0x0000,
+
+ /* when turned on the jit must call
+ * iJIT_NotifyEvent
+ * (
+ * iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED,
+ * )
+ * for all the method already jitted
+ */
+ iJIT_BE_NOTIFY_ON_LOAD = 0x0001,
+
+ /* when turned on the jit must call
+ * iJIT_NotifyEvent
+ * (
+ * iJVM_EVENT_TYPE_METHOD_UNLOAD_FINISHED,
+ * ) for all the method that are unloaded
+ */
+ iJIT_BE_NOTIFY_ON_UNLOAD = 0x0002,
+
+ /* when turned on the jit must instrument all
+ * the currently jited code with calls on
+ * method entries
+ */
+ iJIT_BE_NOTIFY_ON_METHOD_ENTRY = 0x0004,
+
+ /* when turned on the jit must instrument all
+ * the currently jited code with calls
+ * on method exit
+ */
+ iJIT_BE_NOTIFY_ON_METHOD_EXIT = 0x0008
+
+} iJIT_ModeFlags;
+
+
+ /* Flags used by iJIT_IsProfilingActive() */
+typedef enum _iJIT_IsProfilingActiveFlags
+{
+ /* No profiler is running. Currently not used */
+ iJIT_NOTHING_RUNNING = 0x0000,
+
+ /* Sampling is running. This is the default value
+ * returned by iJIT_IsProfilingActive()
+ */
+ iJIT_SAMPLING_ON = 0x0001,
+
+ /* Call Graph is running */
+ iJIT_CALLGRAPH_ON = 0x0002
+
+} iJIT_IsProfilingActiveFlags;
+
+/* Enumerator for the environment of methods*/
+typedef enum _iJDEnvironmentType
+{
+ iJDE_JittingAPI = 2
+} iJDEnvironmentType;
+
+/**********************************
+ * Data structures for the events *
+ **********************************/
+
+/* structure for the events:
+ * iJVM_EVENT_TYPE_METHOD_UNLOAD_START
+ */
+
+typedef struct _iJIT_Method_Id
+{
+ /* Id of the method (same as the one passed in
+ * the iJIT_Method_Load struct
+ */
+ unsigned int method_id;
+
+} *piJIT_Method_Id, iJIT_Method_Id;
+
+
+/* structure for the events:
+ * iJVM_EVENT_TYPE_ENTER_NIDS,
+ * iJVM_EVENT_TYPE_LEAVE_NIDS,
+ * iJVM_EVENT_TYPE_EXCEPTION_OCCURRED_NIDS
+ */
+
+typedef struct _iJIT_Method_NIDS
+{
+ /* unique method ID */
+ unsigned int method_id;
+
+ /* NOTE: no need to fill this field, it's filled by VTune */
+ unsigned int stack_id;
+
+ /* method name (just the method, without the class) */
+ char* method_name;
+} *piJIT_Method_NIDS, iJIT_Method_NIDS;
+
+/* structures for the events:
+ * iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED
+ */
+
+typedef struct _LineNumberInfo
+{
+ /* x86 Offset from the begining of the method*/
+ unsigned int Offset;
+
+ /* source line number from the begining of the source file */
+ unsigned int LineNumber;
+
+} *pLineNumberInfo, LineNumberInfo;
+
+typedef struct _iJIT_Method_Load
+{
+ /* unique method ID - can be any unique value, (except 0 - 999) */
+ unsigned int method_id;
+
+ /* method name (can be with or without the class and signature, in any case
+ * the class name will be added to it)
+ */
+ char* method_name;
+
+ /* virtual address of that method - This determines the method range for the
+ * iJVM_EVENT_TYPE_ENTER/LEAVE_METHOD_ADDR events
+ */
+ void* method_load_address;
+
+ /* Size in memory - Must be exact */
+ unsigned int method_size;
+
+ /* Line Table size in number of entries - Zero if none */
+ unsigned int line_number_size;
+
+ /* Pointer to the begining of the line numbers info array */
+ pLineNumberInfo line_number_table;
+
+ /* unique class ID */
+ unsigned int class_id;
+
+ /* class file name */
+ char* class_file_name;
+
+ /* source file name */
+ char* source_file_name;
+
+ /* bits supplied by the user for saving in the JIT file */
+ void* user_data;
+
+ /* the size of the user data buffer */
+ unsigned int user_data_size;
+
+ /* NOTE: no need to fill this field, it's filled by VTune */
+ iJDEnvironmentType env;
+
+} *piJIT_Method_Load, iJIT_Method_Load;
+
+/* API Functions */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CDECL
+# if defined WIN32 || defined _WIN32
+# define CDECL __cdecl
+# else /* defined WIN32 || defined _WIN32 */
+# if defined _M_X64 || defined _M_AMD64 || defined __x86_64__
+# define CDECL /* not actual on x86_64 platform */
+# else /* _M_X64 || _M_AMD64 || __x86_64__ */
+# define CDECL __attribute__ ((cdecl))
+# endif /* _M_X64 || _M_AMD64 || __x86_64__ */
+# endif /* defined WIN32 || defined _WIN32 */
+#endif /* CDECL */
+
+#define JITAPI CDECL
+
+/* called when the settings are changed with new settings */
+typedef void (*iJIT_ModeChangedEx)(void *UserData, iJIT_ModeFlags Flags);
+
+int JITAPI iJIT_NotifyEvent(iJIT_JVM_EVENT event_type, void *EventSpecificData);
+
+/* The new mode call back routine */
+void JITAPI iJIT_RegisterCallbackEx(void *userdata,
+ iJIT_ModeChangedEx NewModeCallBackFuncEx);
+
+iJIT_IsProfilingActiveFlags JITAPI iJIT_IsProfilingActive(void);
+
+void JITAPI FinalizeThread(void);
+
+void JITAPI FinalizeProcess(void);
+
+unsigned int JITAPI iJIT_GetNewMethodID(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __JITPROFILING_H__ */
diff --git a/chromium/v8/src/third_party/vtune/v8-vtune.h b/chromium/v8/src/third_party/vtune/v8-vtune.h
new file mode 100644
index 00000000000..29ea3eacd88
--- /dev/null
+++ b/chromium/v8/src/third_party/vtune/v8-vtune.h
@@ -0,0 +1,69 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ The full GNU General Public License is included in this distribution
+ in the file called LICENSE.GPL.
+
+ Contact Information:
+ http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+ BSD LICENSE
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+ 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 Intel Corporation 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_VTUNE_H_
+#define V8_VTUNE_H_
+
+namespace vTune {
+
+void InitializeVtuneForV8();
+
+} // namespace vTune
+
+
+#endif // V8_VTUNE_H_
+
diff --git a/chromium/v8/src/third_party/vtune/v8vtune.gyp b/chromium/v8/src/third_party/vtune/v8vtune.gyp
new file mode 100644
index 00000000000..6adf3656892
--- /dev/null
+++ b/chromium/v8/src/third_party/vtune/v8vtune.gyp
@@ -0,0 +1,59 @@
+# Copyright 2012 the V8 project authors. 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': {
+ 'v8_code': 1,
+ },
+ 'includes': ['../../../build/toolchain.gypi', '../../../build/features.gypi'],
+ 'targets': [
+ {
+ 'target_name': 'v8_vtune',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../../tools/gyp/v8.gyp:v8',
+ ],
+ 'sources': [
+ 'ittnotify_config.h',
+ 'ittnotify_types.h',
+ 'jitprofiling.cc',
+ 'jitprofiling.h',
+ 'v8-vtune.h',
+ 'vtune-jit.cc',
+ 'vtune-jit.h',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': ['ENABLE_VTUNE_JIT_INTERFACE',],
+ 'conditions': [
+ ['OS != "win"', {
+ 'libraries': ['-ldl',],
+ }],
+ ],
+ },
+ },
+ ],
+}
diff --git a/chromium/v8/src/third_party/vtune/vtune-jit.cc b/chromium/v8/src/third_party/vtune/vtune-jit.cc
new file mode 100644
index 00000000000..93de7efbb93
--- /dev/null
+++ b/chromium/v8/src/third_party/vtune/vtune-jit.cc
@@ -0,0 +1,283 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ The full GNU General Public License is included in this distribution
+ in the file called LICENSE.GPL.
+
+ Contact Information:
+ http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+ BSD LICENSE
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+ 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 Intel Corporation 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 <string.h>
+
+#ifdef WIN32
+#include <hash_map>
+using namespace std;
+#else
+// To avoid GCC 4.4 compilation warning about hash_map being deprecated.
+#define OLD_DEPRECATED __DEPRECATED
+#undef __DEPRECATED
+#if defined (ANDROID)
+#include <hash_map>
+using namespace std;
+#else
+#include <ext/hash_map>
+using namespace __gnu_cxx;
+#endif
+#define __DEPRECATED OLD_DEPRECATED
+#endif
+
+#include <list>
+
+#include "v8-vtune.h"
+#include "vtune-jit.h"
+
+namespace vTune {
+namespace internal {
+
+
+// This class is used to record the JITted code position info for JIT
+// code profiling.
+class JITCodeLineInfo {
+ public:
+ JITCodeLineInfo() { }
+
+ void SetPosition(intptr_t pc, int pos) {
+ AddCodeLineInfo(LineNumInfo(pc, pos));
+ }
+
+ struct LineNumInfo {
+ LineNumInfo(intptr_t pc, int pos)
+ : pc_(pc), pos_(pos) { }
+
+ intptr_t pc_;
+ int pos_;
+ };
+
+ std::list<LineNumInfo>* GetLineNumInfo() {
+ return &line_num_info_;
+ }
+
+ private:
+ void AddCodeLineInfo(const LineNumInfo& line_info) {
+ line_num_info_.push_back(line_info);
+ }
+ std::list<LineNumInfo> line_num_info_;
+};
+
+struct SameCodeObjects {
+ bool operator () (void* key1, void* key2) const {
+ return key1 == key2;
+ }
+};
+
+struct HashForCodeObject {
+ uint32_t operator () (void* code) const {
+ static const uintptr_t kGoldenRatio = 2654435761u;
+ uintptr_t hash = reinterpret_cast<uintptr_t>(code);
+ return static_cast<uint32_t>(hash * kGoldenRatio);
+ }
+};
+
+#ifdef WIN32
+typedef hash_map<void*, void*> JitInfoMap;
+#else
+typedef hash_map<void*, void*, HashForCodeObject, SameCodeObjects> JitInfoMap;
+#endif
+
+static JitInfoMap* GetEntries() {
+ static JitInfoMap* entries;
+ if (entries == NULL) {
+ entries = new JitInfoMap();
+ }
+ return entries;
+}
+
+static bool IsLineInfoTagged(void* ptr) {
+ return 0 != (reinterpret_cast<intptr_t>(ptr));
+}
+
+static JITCodeLineInfo* UntagLineInfo(void* ptr) {
+ return reinterpret_cast<JITCodeLineInfo*>(
+ reinterpret_cast<intptr_t>(ptr));
+}
+
+// The parameter str is a mixed pattern which contains the
+// function name and some other info. It comes from all the
+// Logger::CodeCreateEvent(...) function. This funtion get the
+// pure function name from the input parameter.
+static char* GetFunctionNameFromMixedName(const char* str, int length) {
+ int index = 0;
+ int count = 0;
+ char* start_ptr = NULL;
+
+ while (str[index++] != ':' && (index < length)) {}
+
+ if (str[index] == '*' || str[index] == '~' ) index++;
+ if (index >= length) return NULL;
+
+ start_ptr = const_cast<char*>(str + index);
+
+ while (index < length && str[index++] != ' ') {
+ count++;
+ }
+
+ char* result = new char[count + 1];
+ memcpy(result, start_ptr, count);
+ result[count] = '\0';
+
+ return result;
+}
+
+// The JitCodeEventHandler for Vtune.
+void VTUNEJITInterface::event_handler(const v8::JitCodeEvent* event) {
+ if (VTUNERUNNING && event != NULL) {
+ switch (event->type) {
+ case v8::JitCodeEvent::CODE_ADDED: {
+ char* temp_file_name = NULL;
+ char* temp_method_name =
+ GetFunctionNameFromMixedName(event->name.str,
+ static_cast<int>(event->name.len));
+ iJIT_Method_Load jmethod;
+ memset(&jmethod, 0, sizeof jmethod);
+ jmethod.method_id = iJIT_GetNewMethodID();
+ jmethod.method_load_address = event->code_start;
+ jmethod.method_size = static_cast<unsigned int>(event->code_len);
+ jmethod.method_name = temp_method_name;
+
+ Handle<Script> script = event->script;
+
+ if (*script != NULL) {
+ // Get the source file name and set it to jmethod.source_file_name
+ if ((*script->GetScriptName())->IsString()) {
+ Handle<String> script_name = script->GetScriptName()->ToString();
+ temp_file_name = new char[script_name->Utf8Length() + 1];
+ script_name->WriteUtf8(temp_file_name);
+ jmethod.source_file_name = temp_file_name;
+ }
+
+ JitInfoMap::iterator entry =
+ GetEntries()->find(event->code_start);
+ if (entry != GetEntries()->end() && IsLineInfoTagged(entry->first)) {
+ JITCodeLineInfo* line_info = UntagLineInfo(entry->second);
+ // Get the line_num_info and set it to jmethod.line_number_table
+ std::list<JITCodeLineInfo::LineNumInfo>* vtunelineinfo =
+ line_info->GetLineNumInfo();
+
+ jmethod.line_number_size = (unsigned int)vtunelineinfo->size();
+ jmethod.line_number_table =
+ reinterpret_cast<LineNumberInfo*>(
+ malloc(sizeof(LineNumberInfo)*jmethod.line_number_size));
+
+ std::list<JITCodeLineInfo::LineNumInfo>::iterator Iter;
+ int index = 0;
+ for (Iter = vtunelineinfo->begin();
+ Iter != vtunelineinfo->end();
+ Iter++) {
+ jmethod.line_number_table[index].Offset =
+ static_cast<unsigned int>(Iter->pc_);
+ jmethod.line_number_table[index++].LineNumber =
+ script->GetLineNumber(Iter->pos_)+1;
+ }
+ GetEntries()->erase(event->code_start);
+ }
+ }
+
+ iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED,
+ reinterpret_cast<void*>(&jmethod));
+ if (temp_method_name)
+ delete []temp_method_name;
+ if (temp_file_name)
+ delete []temp_file_name;
+ break;
+ }
+ // TODO(chunyang.dai@intel.com): code_move will be supported.
+ case v8::JitCodeEvent::CODE_MOVED:
+ break;
+ // Currently the CODE_REMOVED event is not issued.
+ case v8::JitCodeEvent::CODE_REMOVED:
+ break;
+ case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: {
+ JITCodeLineInfo* line_info =
+ reinterpret_cast<JITCodeLineInfo*>(event->user_data);
+ if (line_info != NULL) {
+ line_info->SetPosition(static_cast<intptr_t>(event->line_info.offset),
+ static_cast<int>(event->line_info.pos));
+ }
+ break;
+ }
+ case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: {
+ v8::JitCodeEvent* temp_event = const_cast<v8::JitCodeEvent*>(event);
+ temp_event->user_data = new JITCodeLineInfo();
+ break;
+ }
+ case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: {
+ GetEntries()->insert(std::pair <void*, void*>(event->code_start, event->user_data));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return;
+}
+
+} // namespace internal
+
+void InitializeVtuneForV8() {
+ if (v8::V8::Initialize()) {
+ v8::V8::SetFlagsFromString("--nocompact_code_space",
+ (int)strlen("--nocompact_code_space"));
+ v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault,
+ vTune::internal::VTUNEJITInterface::event_handler);
+ }
+}
+
+} // namespace vTune
diff --git a/chromium/v8/src/third_party/vtune/vtune-jit.h b/chromium/v8/src/third_party/vtune/vtune-jit.h
new file mode 100644
index 00000000000..42b8c3da1fb
--- /dev/null
+++ b/chromium/v8/src/third_party/vtune/vtune-jit.h
@@ -0,0 +1,82 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ The full GNU General Public License is included in this distribution
+ in the file called LICENSE.GPL.
+
+ Contact Information:
+ http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
+
+ BSD LICENSE
+
+ Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
+ 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 Intel Corporation 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 VTUNE_VTUNE_JIT_H_
+#define VTUNE_VTUNE_JIT_H_
+
+#include "jitprofiling.h"
+#include "../../../include/v8.h"
+
+#define VTUNERUNNING (iJIT_IsProfilingActive() == iJIT_SAMPLING_ON)
+
+namespace vTune {
+namespace internal {
+using namespace v8;
+class VTUNEJITInterface {
+ public:
+ static void event_handler(const v8::JitCodeEvent* event);
+
+ private:
+ //static Mutex* vtunemutex_;
+};
+
+
+} } // namespace vTune::internal
+
+
+#endif // VTUNE_VTUNE_JIT_H_
+
diff --git a/chromium/v8/src/token.cc b/chromium/v8/src/token.cc
new file mode 100644
index 00000000000..7ba7ed34205
--- /dev/null
+++ b/chromium/v8/src/token.cc
@@ -0,0 +1,63 @@
+// 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.
+
+#include "../include/v8stdint.h"
+#include "token.h"
+
+namespace v8 {
+namespace internal {
+
+#define T(name, string, precedence) #name,
+const char* const Token::name_[NUM_TOKENS] = {
+ TOKEN_LIST(T, T)
+};
+#undef T
+
+
+#define T(name, string, precedence) string,
+const char* const Token::string_[NUM_TOKENS] = {
+ TOKEN_LIST(T, T)
+};
+#undef T
+
+
+#define T(name, string, precedence) precedence,
+const int8_t Token::precedence_[NUM_TOKENS] = {
+ TOKEN_LIST(T, T)
+};
+#undef T
+
+
+#define KT(a, b, c) 'T',
+#define KK(a, b, c) 'K',
+const char Token::token_type[] = {
+ TOKEN_LIST(KT, KK)
+};
+#undef KT
+#undef KK
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/token.h b/chromium/v8/src/token.h
new file mode 100644
index 00000000000..992adaa77c2
--- /dev/null
+++ b/chromium/v8/src/token.h
@@ -0,0 +1,311 @@
+// Copyright 2012 the V8 project authors. 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_TOKEN_H_
+#define V8_TOKEN_H_
+
+#include "checks.h"
+
+namespace v8 {
+namespace internal {
+
+// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the
+// same signature M(name, string, precedence), where name is the
+// symbolic token name, string is the corresponding syntactic symbol
+// (or NULL, for literals), and precedence is the precedence (or 0).
+// The parameters are invoked for token categories as follows:
+//
+// T: Non-keyword tokens
+// K: Keyword tokens
+
+// IGNORE_TOKEN is a convenience macro that can be supplied as
+// an argument (at any position) for a TOKEN_LIST call. It does
+// nothing with tokens belonging to the respective category.
+
+#define IGNORE_TOKEN(name, string, precedence)
+
+#define TOKEN_LIST(T, K) \
+ /* End of source indicator. */ \
+ T(EOS, "EOS", 0) \
+ \
+ /* Punctuators (ECMA-262, section 7.7, page 15). */ \
+ T(LPAREN, "(", 0) \
+ T(RPAREN, ")", 0) \
+ T(LBRACK, "[", 0) \
+ T(RBRACK, "]", 0) \
+ T(LBRACE, "{", 0) \
+ T(RBRACE, "}", 0) \
+ T(COLON, ":", 0) \
+ T(SEMICOLON, ";", 0) \
+ T(PERIOD, ".", 0) \
+ T(CONDITIONAL, "?", 3) \
+ T(INC, "++", 0) \
+ T(DEC, "--", 0) \
+ \
+ /* Assignment operators. */ \
+ /* IsAssignmentOp() and Assignment::is_compound() relies on */ \
+ /* this block of enum values being contiguous and sorted in the */ \
+ /* same order! */ \
+ 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) \
+ T(ASSIGN_BIT_AND, "&=", 2) \
+ T(ASSIGN_SHL, "<<=", 2) \
+ T(ASSIGN_SAR, ">>=", 2) \
+ T(ASSIGN_SHR, ">>>=", 2) \
+ T(ASSIGN_ADD, "+=", 2) \
+ T(ASSIGN_SUB, "-=", 2) \
+ T(ASSIGN_MUL, "*=", 2) \
+ T(ASSIGN_DIV, "/=", 2) \
+ T(ASSIGN_MOD, "%=", 2) \
+ \
+ /* Binary operators sorted by precedence. */ \
+ /* IsBinaryOp() relies on this block of enum values */ \
+ /* being contiguous and sorted in the same order! */ \
+ T(COMMA, ",", 1) \
+ T(OR, "||", 4) \
+ T(AND, "&&", 5) \
+ T(BIT_OR, "|", 6) \
+ T(BIT_XOR, "^", 7) \
+ T(BIT_AND, "&", 8) \
+ T(SHL, "<<", 11) \
+ T(SAR, ">>", 11) \
+ T(SHR, ">>>", 11) \
+ T(ROR, "rotate right", 11) /* only used by Crankshaft */ \
+ T(ADD, "+", 12) \
+ T(SUB, "-", 12) \
+ T(MUL, "*", 13) \
+ T(DIV, "/", 13) \
+ T(MOD, "%", 13) \
+ \
+ /* Compare operators sorted by precedence. */ \
+ /* IsCompareOp() relies on this block of enum values */ \
+ /* being contiguous and sorted in the same order! */ \
+ T(EQ, "==", 9) \
+ T(NE, "!=", 9) \
+ T(EQ_STRICT, "===", 9) \
+ T(NE_STRICT, "!==", 9) \
+ T(LT, "<", 10) \
+ T(GT, ">", 10) \
+ T(LTE, "<=", 10) \
+ T(GTE, ">=", 10) \
+ K(INSTANCEOF, "instanceof", 10) \
+ K(IN, "in", 10) \
+ \
+ /* Unary operators. */ \
+ /* IsUnaryOp() relies on this block of enum values */ \
+ /* being contiguous and sorted in the same order! */ \
+ T(NOT, "!", 0) \
+ T(BIT_NOT, "~", 0) \
+ K(DELETE, "delete", 0) \
+ K(TYPEOF, "typeof", 0) \
+ K(VOID, "void", 0) \
+ \
+ /* Keywords (ECMA-262, section 7.5.2, page 13). */ \
+ K(BREAK, "break", 0) \
+ K(CASE, "case", 0) \
+ K(CATCH, "catch", 0) \
+ K(CONTINUE, "continue", 0) \
+ K(DEBUGGER, "debugger", 0) \
+ K(DEFAULT, "default", 0) \
+ /* DELETE */ \
+ K(DO, "do", 0) \
+ K(ELSE, "else", 0) \
+ K(FINALLY, "finally", 0) \
+ K(FOR, "for", 0) \
+ K(FUNCTION, "function", 0) \
+ K(IF, "if", 0) \
+ /* IN */ \
+ /* INSTANCEOF */ \
+ K(NEW, "new", 0) \
+ K(RETURN, "return", 0) \
+ K(SWITCH, "switch", 0) \
+ K(THIS, "this", 0) \
+ K(THROW, "throw", 0) \
+ K(TRY, "try", 0) \
+ /* TYPEOF */ \
+ K(VAR, "var", 0) \
+ /* VOID */ \
+ K(WHILE, "while", 0) \
+ K(WITH, "with", 0) \
+ \
+ /* Literals (ECMA-262, section 7.8, page 16). */ \
+ K(NULL_LITERAL, "null", 0) \
+ K(TRUE_LITERAL, "true", 0) \
+ K(FALSE_LITERAL, "false", 0) \
+ T(NUMBER, NULL, 0) \
+ T(STRING, NULL, 0) \
+ \
+ /* Identifiers (not keywords or future reserved words). */ \
+ T(IDENTIFIER, NULL, 0) \
+ \
+ /* Future reserved words (ECMA-262, section 7.6.1.2). */ \
+ 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(YIELD, "yield", 0) \
+ \
+ /* Illegal token - not able to scan. */ \
+ T(ILLEGAL, "ILLEGAL", 0) \
+ \
+ /* Scanner-internal use only. */ \
+ T(WHITESPACE, NULL, 0)
+
+
+class Token {
+ public:
+ // All token values.
+#define T(name, string, precedence) name,
+ enum Value {
+ TOKEN_LIST(T, T)
+ NUM_TOKENS
+ };
+#undef T
+
+ // Returns a string corresponding to the C++ token name
+ // (e.g. "LT" for the token LT).
+ static const char* Name(Value tok) {
+ ASSERT(tok < NUM_TOKENS); // tok is unsigned
+ return name_[tok];
+ }
+
+ // Predicates
+ static bool IsKeyword(Value tok) {
+ return token_type[tok] == 'K';
+ }
+
+ static bool IsAssignmentOp(Value tok) {
+ return INIT_VAR <= tok && tok <= ASSIGN_MOD;
+ }
+
+ static bool IsBinaryOp(Value op) {
+ return COMMA <= op && op <= MOD;
+ }
+
+ static bool IsCompareOp(Value op) {
+ return EQ <= op && op <= IN;
+ }
+
+ static bool IsOrderedRelationalCompareOp(Value op) {
+ return op == LT || op == LTE || op == GT || op == GTE;
+ }
+
+ static bool IsEqualityOp(Value op) {
+ return op == EQ || op == EQ_STRICT;
+ }
+
+ static bool IsInequalityOp(Value op) {
+ return op == NE || op == NE_STRICT;
+ }
+
+ static bool IsArithmeticCompareOp(Value op) {
+ return IsOrderedRelationalCompareOp(op) ||
+ IsEqualityOp(op) || IsInequalityOp(op);
+ }
+
+ static Value NegateCompareOp(Value op) {
+ ASSERT(IsArithmeticCompareOp(op));
+ switch (op) {
+ case EQ: return NE;
+ case NE: return EQ;
+ case EQ_STRICT: return NE_STRICT;
+ case NE_STRICT: return EQ_STRICT;
+ case LT: return GTE;
+ case GT: return LTE;
+ case LTE: return GT;
+ case GTE: return LT;
+ default:
+ UNREACHABLE();
+ return op;
+ }
+ }
+
+ static Value ReverseCompareOp(Value op) {
+ ASSERT(IsArithmeticCompareOp(op));
+ switch (op) {
+ case EQ: return EQ;
+ case NE: return NE;
+ case EQ_STRICT: return EQ_STRICT;
+ case NE_STRICT: return NE_STRICT;
+ case LT: return GT;
+ case GT: return LT;
+ case LTE: return GTE;
+ case GTE: return LTE;
+ default:
+ UNREACHABLE();
+ return op;
+ }
+ }
+
+ static bool IsBitOp(Value op) {
+ return (BIT_OR <= op && op <= SHR) || op == BIT_NOT;
+ }
+
+ static bool IsUnaryOp(Value op) {
+ return (NOT <= op && op <= VOID) || op == ADD || op == SUB;
+ }
+
+ static bool IsCountOp(Value op) {
+ return op == INC || op == DEC;
+ }
+
+ static bool IsShiftOp(Value op) {
+ return (SHL <= op) && (op <= SHR);
+ }
+
+ // Returns a string corresponding to the JS token string
+ // (.e., "<" for the token LT) or NULL if the token doesn't
+ // have a (unique) string (e.g. an IDENTIFIER).
+ static const char* String(Value tok) {
+ ASSERT(tok < NUM_TOKENS); // tok is unsigned.
+ return string_[tok];
+ }
+
+ // Returns the precedence > 0 for binary and compare
+ // operators; returns 0 otherwise.
+ static int Precedence(Value tok) {
+ ASSERT(tok < NUM_TOKENS); // tok is unsigned.
+ return precedence_[tok];
+ }
+
+ private:
+ static const char* const name_[NUM_TOKENS];
+ static const char* const string_[NUM_TOKENS];
+ static const int8_t precedence_[NUM_TOKENS];
+ static const char token_type[NUM_TOKENS];
+};
+
+} } // namespace v8::internal
+
+#endif // V8_TOKEN_H_
diff --git a/chromium/v8/src/transitions-inl.h b/chromium/v8/src/transitions-inl.h
new file mode 100644
index 00000000000..c4825fcf734
--- /dev/null
+++ b/chromium/v8/src/transitions-inl.h
@@ -0,0 +1,198 @@
+// Copyright 2012 the V8 project authors. 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_TRANSITIONS_INL_H_
+#define V8_TRANSITIONS_INL_H_
+
+#include "objects-inl.h"
+#include "transitions.h"
+
+namespace v8 {
+namespace internal {
+
+
+#define FIELD_ADDR(p, offset) \
+ (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
+
+#define WRITE_FIELD(p, offset, value) \
+ (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
+
+#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); \
+ } \
+ }
+
+
+TransitionArray* TransitionArray::cast(Object* object) {
+ ASSERT(object->IsTransitionArray());
+ return reinterpret_cast<TransitionArray*>(object);
+}
+
+
+bool TransitionArray::HasElementsTransition() {
+ return Search(GetHeap()->elements_transition_symbol()) != kNotFound;
+}
+
+
+Object* TransitionArray::back_pointer_storage() {
+ return get(kBackPointerStorageIndex);
+}
+
+
+void TransitionArray::set_back_pointer_storage(Object* back_pointer,
+ WriteBarrierMode mode) {
+ Heap* heap = GetHeap();
+ WRITE_FIELD(this, kBackPointerStorageOffset, back_pointer);
+ CONDITIONAL_WRITE_BARRIER(
+ heap, this, kBackPointerStorageOffset, back_pointer, mode);
+}
+
+
+bool TransitionArray::HasPrototypeTransitions() {
+ return IsFullTransitionArray() &&
+ get(kPrototypeTransitionsIndex) != Smi::FromInt(0);
+}
+
+
+FixedArray* TransitionArray::GetPrototypeTransitions() {
+ ASSERT(IsFullTransitionArray());
+ Object* prototype_transitions = get(kPrototypeTransitionsIndex);
+ return FixedArray::cast(prototype_transitions);
+}
+
+
+HeapObject* TransitionArray::UncheckedPrototypeTransitions() {
+ ASSERT(HasPrototypeTransitions());
+ return reinterpret_cast<HeapObject*>(get(kPrototypeTransitionsIndex));
+}
+
+
+void TransitionArray::SetPrototypeTransitions(FixedArray* transitions,
+ WriteBarrierMode mode) {
+ ASSERT(IsFullTransitionArray());
+ ASSERT(transitions->IsFixedArray());
+ Heap* heap = GetHeap();
+ WRITE_FIELD(this, kPrototypeTransitionsOffset, transitions);
+ CONDITIONAL_WRITE_BARRIER(
+ heap, this, kPrototypeTransitionsOffset, transitions, mode);
+}
+
+
+Object** TransitionArray::GetPrototypeTransitionsSlot() {
+ return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
+ kPrototypeTransitionsOffset);
+}
+
+
+Object** TransitionArray::GetKeySlot(int transition_number) {
+ ASSERT(!IsSimpleTransition());
+ ASSERT(transition_number < number_of_transitions());
+ return HeapObject::RawField(
+ reinterpret_cast<HeapObject*>(this),
+ OffsetOfElementAt(ToKeyIndex(transition_number)));
+}
+
+
+Name* TransitionArray::GetKey(int transition_number) {
+ if (IsSimpleTransition()) {
+ Map* target = GetTarget(kSimpleTransitionIndex);
+ int descriptor = target->LastAdded();
+ Name* key = target->instance_descriptors()->GetKey(descriptor);
+ return key;
+ }
+ ASSERT(transition_number < number_of_transitions());
+ return Name::cast(get(ToKeyIndex(transition_number)));
+}
+
+
+void TransitionArray::SetKey(int transition_number, Name* key) {
+ ASSERT(!IsSimpleTransition());
+ ASSERT(transition_number < number_of_transitions());
+ set(ToKeyIndex(transition_number), key);
+}
+
+
+Map* TransitionArray::GetTarget(int transition_number) {
+ if (IsSimpleTransition()) {
+ ASSERT(transition_number == kSimpleTransitionIndex);
+ return Map::cast(get(kSimpleTransitionTarget));
+ }
+ ASSERT(transition_number < number_of_transitions());
+ return Map::cast(get(ToTargetIndex(transition_number)));
+}
+
+
+void TransitionArray::SetTarget(int transition_number, Map* value) {
+ if (IsSimpleTransition()) {
+ ASSERT(transition_number == kSimpleTransitionIndex);
+ return set(kSimpleTransitionTarget, value);
+ }
+ ASSERT(transition_number < number_of_transitions());
+ set(ToTargetIndex(transition_number), value);
+}
+
+
+PropertyDetails TransitionArray::GetTargetDetails(int transition_number) {
+ Map* map = GetTarget(transition_number);
+ DescriptorArray* descriptors = map->instance_descriptors();
+ int descriptor = map->LastAdded();
+ return descriptors->GetDetails(descriptor);
+}
+
+
+int TransitionArray::Search(Name* name) {
+ if (IsSimpleTransition()) {
+ Name* key = GetKey(kSimpleTransitionIndex);
+ if (key->Equals(name)) return kSimpleTransitionIndex;
+ return kNotFound;
+ }
+ return internal::Search<ALL_ENTRIES>(this, name);
+}
+
+
+void TransitionArray::NoIncrementalWriteBarrierSet(int transition_number,
+ Name* key,
+ Map* target) {
+ FixedArray::NoIncrementalWriteBarrierSet(
+ this, ToKeyIndex(transition_number), key);
+ FixedArray::NoIncrementalWriteBarrierSet(
+ this, ToTargetIndex(transition_number), target);
+}
+
+
+#undef FIELD_ADDR
+#undef WRITE_FIELD
+#undef CONDITIONAL_WRITE_BARRIER
+
+
+} } // namespace v8::internal
+
+#endif // V8_TRANSITIONS_INL_H_
diff --git a/chromium/v8/src/transitions.cc b/chromium/v8/src/transitions.cc
new file mode 100644
index 00000000000..086edcb9948
--- /dev/null
+++ b/chromium/v8/src/transitions.cc
@@ -0,0 +1,156 @@
+// Copyright 2012 the V8 project authors. 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 "objects.h"
+#include "transitions-inl.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+
+static MaybeObject* AllocateRaw(int length) {
+ Heap* heap = Isolate::Current()->heap();
+
+ // Use FixedArray to not use TransitionArray::cast on incomplete object.
+ FixedArray* array;
+ MaybeObject* maybe_array = heap->AllocateFixedArray(length);
+ if (!maybe_array->To(&array)) return maybe_array;
+ return array;
+}
+
+
+MaybeObject* TransitionArray::Allocate(int number_of_transitions) {
+ FixedArray* array;
+ MaybeObject* maybe_array = AllocateRaw(ToKeyIndex(number_of_transitions));
+ if (!maybe_array->To(&array)) return maybe_array;
+ array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
+ return array;
+}
+
+
+void TransitionArray::NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin,
+ int origin_transition,
+ int target_transition) {
+ NoIncrementalWriteBarrierSet(target_transition,
+ origin->GetKey(origin_transition),
+ origin->GetTarget(origin_transition));
+}
+
+
+static bool InsertionPointFound(Name* key1, Name* key2) {
+ return key1->Hash() > key2->Hash();
+}
+
+
+MaybeObject* TransitionArray::NewWith(SimpleTransitionFlag flag,
+ Name* key,
+ Map* target,
+ Object* back_pointer) {
+ TransitionArray* result;
+ MaybeObject* maybe_result;
+
+ if (flag == SIMPLE_TRANSITION) {
+ maybe_result = AllocateRaw(kSimpleTransitionSize);
+ if (!maybe_result->To(&result)) return maybe_result;
+ result->set(kSimpleTransitionTarget, target);
+ } else {
+ maybe_result = Allocate(1);
+ if (!maybe_result->To(&result)) return maybe_result;
+ result->NoIncrementalWriteBarrierSet(0, key, target);
+ }
+ result->set_back_pointer_storage(back_pointer);
+ return result;
+}
+
+
+MaybeObject* TransitionArray::ExtendToFullTransitionArray() {
+ ASSERT(!IsFullTransitionArray());
+ int nof = number_of_transitions();
+ TransitionArray* result;
+ MaybeObject* maybe_result = Allocate(nof);
+ if (!maybe_result->To(&result)) return maybe_result;
+
+ if (nof == 1) {
+ result->NoIncrementalWriteBarrierCopyFrom(this, kSimpleTransitionIndex, 0);
+ }
+
+ result->set_back_pointer_storage(back_pointer_storage());
+ return result;
+}
+
+
+MaybeObject* TransitionArray::CopyInsert(Name* name, Map* target) {
+ TransitionArray* result;
+
+ int number_of_transitions = this->number_of_transitions();
+ int new_size = number_of_transitions;
+
+ int insertion_index = this->Search(name);
+ if (insertion_index == kNotFound) ++new_size;
+
+ MaybeObject* maybe_array;
+ maybe_array = TransitionArray::Allocate(new_size);
+ if (!maybe_array->To(&result)) return maybe_array;
+
+ if (HasPrototypeTransitions()) {
+ result->SetPrototypeTransitions(GetPrototypeTransitions());
+ }
+
+ if (insertion_index != kNotFound) {
+ for (int i = 0; i < number_of_transitions; ++i) {
+ if (i != insertion_index) {
+ result->NoIncrementalWriteBarrierCopyFrom(this, i, i);
+ }
+ }
+ result->NoIncrementalWriteBarrierSet(insertion_index, name, target);
+ result->set_back_pointer_storage(back_pointer_storage());
+ return result;
+ }
+
+ insertion_index = 0;
+ for (; insertion_index < number_of_transitions; ++insertion_index) {
+ if (InsertionPointFound(GetKey(insertion_index), name)) break;
+ result->NoIncrementalWriteBarrierCopyFrom(
+ this, insertion_index, insertion_index);
+ }
+
+ result->NoIncrementalWriteBarrierSet(insertion_index, name, target);
+
+ for (; insertion_index < number_of_transitions; ++insertion_index) {
+ result->NoIncrementalWriteBarrierCopyFrom(
+ this, insertion_index, insertion_index + 1);
+ }
+
+ result->set_back_pointer_storage(back_pointer_storage());
+ return result;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/transitions.h b/chromium/v8/src/transitions.h
new file mode 100644
index 00000000000..fde12798952
--- /dev/null
+++ b/chromium/v8/src/transitions.h
@@ -0,0 +1,211 @@
+// Copyright 2012 the V8 project authors. 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_TRANSITIONS_H_
+#define V8_TRANSITIONS_H_
+
+#include "elements-kind.h"
+#include "heap.h"
+#include "isolate.h"
+#include "objects.h"
+#include "v8checks.h"
+
+namespace v8 {
+namespace internal {
+
+
+// TransitionArrays are fixed arrays used to hold map transitions for property,
+// constant, and element changes. They can either be simple transition arrays
+// that store a single property transition, or a full transition array that has
+// prototype transitions and multiple property transitons. The details related
+// to property transitions are accessed in the descriptor array of the target
+// map. In the case of a simple transition, the key is also read from the
+// descriptor array of the target map.
+//
+// The simple format of the these objects is:
+// [0] Undefined or back pointer map
+// [1] Single transition
+//
+// The full format is:
+// [0] Undefined or back pointer map
+// [1] Smi(0) or fixed array of prototype transitions
+// [2] First transition
+// [length() - kTransitionSize] Last transition
+class TransitionArray: public FixedArray {
+ public:
+ // Accessors for fetching instance transition at transition number.
+ inline Name* GetKey(int transition_number);
+ inline void SetKey(int transition_number, Name* value);
+ inline Object** GetKeySlot(int transition_number);
+ int GetSortedKeyIndex(int transition_number) { return transition_number; }
+
+ Name* GetSortedKey(int transition_number) {
+ return GetKey(transition_number);
+ }
+
+ inline Map* GetTarget(int transition_number);
+ inline void SetTarget(int transition_number, Map* target);
+
+ inline PropertyDetails GetTargetDetails(int transition_number);
+
+ inline bool HasElementsTransition();
+
+ inline Object* back_pointer_storage();
+ inline void set_back_pointer_storage(
+ Object* back_pointer,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+
+ inline FixedArray* GetPrototypeTransitions();
+ inline void SetPrototypeTransitions(
+ FixedArray* prototype_transitions,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+ inline Object** GetPrototypeTransitionsSlot();
+ inline bool HasPrototypeTransitions();
+ inline HeapObject* UncheckedPrototypeTransitions();
+
+ // Returns the number of transitions in the array.
+ int number_of_transitions() {
+ if (IsSimpleTransition()) return 1;
+ int len = length();
+ return len <= kFirstIndex ? 0 : (len - kFirstIndex) / kTransitionSize;
+ }
+
+ inline int number_of_entries() { return number_of_transitions(); }
+
+ // Allocate a new transition array with a single entry.
+ static MUST_USE_RESULT MaybeObject* NewWith(
+ SimpleTransitionFlag flag,
+ Name* key,
+ Map* target,
+ Object* back_pointer);
+
+ MUST_USE_RESULT MaybeObject* ExtendToFullTransitionArray();
+
+ // Copy the transition array, inserting a new transition.
+ // TODO(verwaest): This should not cause an existing transition to be
+ // overwritten.
+ MUST_USE_RESULT MaybeObject* CopyInsert(Name* name, Map* target);
+
+ // Copy a single transition from the origin array.
+ inline void NoIncrementalWriteBarrierCopyFrom(TransitionArray* origin,
+ int origin_transition,
+ int target_transition);
+
+ // Search a transition for a given property name.
+ inline int Search(Name* name);
+
+ // Allocates a TransitionArray.
+ MUST_USE_RESULT static MaybeObject* Allocate(int number_of_transitions);
+
+ bool IsSimpleTransition() {
+ return length() == kSimpleTransitionSize &&
+ get(kSimpleTransitionTarget)->IsHeapObject() &&
+ // The IntrusivePrototypeTransitionIterator may have set the map of the
+ // prototype transitions array to a smi. In that case, there are
+ // prototype transitions, hence this transition array is a full
+ // transition array.
+ HeapObject::cast(get(kSimpleTransitionTarget))->map()->IsMap() &&
+ get(kSimpleTransitionTarget)->IsMap();
+ }
+
+ bool IsFullTransitionArray() {
+ return length() > kFirstIndex ||
+ (length() == kFirstIndex && !IsSimpleTransition());
+ }
+
+ // Casting.
+ static inline TransitionArray* cast(Object* obj);
+
+ // Constant for denoting key was not found.
+ static const int kNotFound = -1;
+
+ static const int kBackPointerStorageIndex = 0;
+
+ // Layout for full transition arrays.
+ static const int kPrototypeTransitionsIndex = 1;
+ static const int kFirstIndex = 2;
+
+ // Layout for simple transition arrays.
+ static const int kSimpleTransitionTarget = 1;
+ static const int kSimpleTransitionSize = 2;
+ static const int kSimpleTransitionIndex = 0;
+ STATIC_ASSERT(kSimpleTransitionIndex != kNotFound);
+
+ static const int kBackPointerStorageOffset = FixedArray::kHeaderSize;
+
+ // Layout for the full transition array header.
+ static const int kPrototypeTransitionsOffset = kBackPointerStorageOffset +
+ kPointerSize;
+
+ // Layout of map transition entries in full transition arrays.
+ static const int kTransitionKey = 0;
+ static const int kTransitionTarget = 1;
+ static const int kTransitionSize = 2;
+
+#ifdef OBJECT_PRINT
+ // Print all the transitions.
+ inline void PrintTransitions() {
+ PrintTransitions(stdout);
+ }
+ void PrintTransitions(FILE* out);
+#endif
+
+#ifdef DEBUG
+ bool IsSortedNoDuplicates(int valid_entries = -1);
+ bool IsConsistentWithBackPointers(Map* current_map);
+ bool IsEqualTo(TransitionArray* other);
+#endif
+
+ // The maximum number of transitions we want in a transition array (should
+ // fit in a page).
+ static const int kMaxNumberOfTransitions = 1024 + 512;
+
+ private:
+ // Conversion from transition number to array indices.
+ static int ToKeyIndex(int transition_number) {
+ return kFirstIndex +
+ (transition_number * kTransitionSize) +
+ kTransitionKey;
+ }
+
+ static int ToTargetIndex(int transition_number) {
+ return kFirstIndex +
+ (transition_number * kTransitionSize) +
+ kTransitionTarget;
+ }
+
+ inline void NoIncrementalWriteBarrierSet(int transition_number,
+ Name* key,
+ Map* target);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TransitionArray);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_TRANSITIONS_H_
diff --git a/chromium/v8/src/type-info.cc b/chromium/v8/src/type-info.cc
new file mode 100644
index 00000000000..336b459d6b7
--- /dev/null
+++ b/chromium/v8/src/type-info.cc
@@ -0,0 +1,708 @@
+// Copyright 2012 the V8 project authors. 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 "ast.h"
+#include "code-stubs.h"
+#include "compiler.h"
+#include "ic.h"
+#include "macro-assembler.h"
+#include "stub-cache.h"
+#include "type-info.h"
+
+#include "ic-inl.h"
+#include "objects-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+TypeInfo TypeInfo::FromValue(Handle<Object> value) {
+ if (value->IsSmi()) {
+ return TypeInfo::Smi();
+ } else if (value->IsHeapNumber()) {
+ return TypeInfo::IsInt32Double(HeapNumber::cast(*value)->value())
+ ? TypeInfo::Integer32()
+ : TypeInfo::Double();
+ } else if (value->IsString()) {
+ return TypeInfo::String();
+ }
+ return TypeInfo::Unknown();
+}
+
+
+TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code,
+ Handle<Context> native_context,
+ Isolate* isolate,
+ Zone* zone)
+ : native_context_(native_context),
+ isolate_(isolate),
+ zone_(zone) {
+ BuildDictionary(code);
+ ASSERT(dictionary_->IsDictionary());
+}
+
+
+static uint32_t IdToKey(TypeFeedbackId ast_id) {
+ return static_cast<uint32_t>(ast_id.ToInt());
+}
+
+
+Handle<Object> TypeFeedbackOracle::GetInfo(TypeFeedbackId ast_id) {
+ int entry = dictionary_->FindEntry(IdToKey(ast_id));
+ if (entry != UnseededNumberDictionary::kNotFound) {
+ Object* value = dictionary_->ValueAt(entry);
+ if (value->IsCell()) {
+ Cell* cell = Cell::cast(value);
+ return Handle<Object>(cell->value(), isolate_);
+ } else {
+ return Handle<Object>(value, isolate_);
+ }
+ }
+ return Handle<Object>::cast(isolate_->factory()->undefined_value());
+}
+
+
+Handle<Cell> TypeFeedbackOracle::GetInfoCell(
+ TypeFeedbackId ast_id) {
+ int entry = dictionary_->FindEntry(IdToKey(ast_id));
+ if (entry != UnseededNumberDictionary::kNotFound) {
+ Cell* cell = Cell::cast(dictionary_->ValueAt(entry));
+ return Handle<Cell>(cell, isolate_);
+ }
+ return Handle<Cell>::null();
+}
+
+
+bool TypeFeedbackOracle::LoadIsUninitialized(Property* expr) {
+ Handle<Object> map_or_code = GetInfo(expr->PropertyFeedbackId());
+ if (map_or_code->IsMap()) return false;
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ return code->is_inline_cache_stub() && code->ic_state() == UNINITIALIZED;
+ }
+ return false;
+}
+
+
+bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) {
+ Handle<Object> map_or_code = GetInfo(expr->PropertyFeedbackId());
+ if (map_or_code->IsMap()) return true;
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ bool preliminary_checks = code->is_keyed_load_stub() &&
+ code->ic_state() == MONOMORPHIC &&
+ Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL;
+ if (!preliminary_checks) return false;
+ Map* map = code->FindFirstMap();
+ if (map == NULL) return false;
+ map = map->CurrentMapForDeprecated();
+ return map != NULL && !CanRetainOtherContext(map, *native_context_);
+ }
+ return false;
+}
+
+
+bool TypeFeedbackOracle::LoadIsPolymorphic(Property* expr) {
+ Handle<Object> map_or_code = GetInfo(expr->PropertyFeedbackId());
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ return code->is_keyed_load_stub() && code->ic_state() == POLYMORPHIC;
+ }
+ return false;
+}
+
+
+bool TypeFeedbackOracle::StoreIsUninitialized(TypeFeedbackId ast_id) {
+ Handle<Object> map_or_code = GetInfo(ast_id);
+ if (map_or_code->IsMap()) return false;
+ if (!map_or_code->IsCode()) return false;
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ return code->ic_state() == UNINITIALIZED;
+}
+
+
+bool TypeFeedbackOracle::StoreIsMonomorphicNormal(TypeFeedbackId ast_id) {
+ Handle<Object> map_or_code = GetInfo(ast_id);
+ if (map_or_code->IsMap()) return true;
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ bool standard_store = FLAG_compiled_keyed_stores ||
+ (Code::GetKeyedAccessStoreMode(code->extra_ic_state()) ==
+ STANDARD_STORE);
+ bool preliminary_checks =
+ code->is_keyed_store_stub() &&
+ standard_store &&
+ code->ic_state() == MONOMORPHIC &&
+ Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL;
+ if (!preliminary_checks) return false;
+ Map* map = code->FindFirstMap();
+ if (map == NULL) return false;
+ map = map->CurrentMapForDeprecated();
+ return map != NULL && !CanRetainOtherContext(map, *native_context_);
+ }
+ return false;
+}
+
+
+bool TypeFeedbackOracle::StoreIsKeyedPolymorphic(TypeFeedbackId ast_id) {
+ Handle<Object> map_or_code = GetInfo(ast_id);
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ bool standard_store = FLAG_compiled_keyed_stores ||
+ (Code::GetKeyedAccessStoreMode(code->extra_ic_state()) ==
+ STANDARD_STORE);
+ return code->is_keyed_store_stub() && standard_store &&
+ code->ic_state() == POLYMORPHIC;
+ }
+ return false;
+}
+
+
+bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) {
+ Handle<Object> value = GetInfo(expr->CallFeedbackId());
+ return value->IsMap() || value->IsAllocationSite() || value->IsJSFunction() ||
+ value->IsSmi();
+}
+
+
+bool TypeFeedbackOracle::CallNewIsMonomorphic(CallNew* expr) {
+ Handle<Object> info = GetInfo(expr->CallNewFeedbackId());
+ return info->IsAllocationSite() || info->IsJSFunction();
+}
+
+
+bool TypeFeedbackOracle::ObjectLiteralStoreIsMonomorphic(
+ ObjectLiteral::Property* prop) {
+ Handle<Object> map_or_code = GetInfo(prop->key()->LiteralFeedbackId());
+ return map_or_code->IsMap();
+}
+
+
+byte TypeFeedbackOracle::ForInType(ForInStatement* stmt) {
+ Handle<Object> value = GetInfo(stmt->ForInFeedbackId());
+ return value->IsSmi() &&
+ Smi::cast(*value)->value() == TypeFeedbackCells::kForInFastCaseMarker
+ ? ForInStatement::FAST_FOR_IN : ForInStatement::SLOW_FOR_IN;
+}
+
+
+Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) {
+ ASSERT(LoadIsMonomorphicNormal(expr));
+ Handle<Object> map_or_code = GetInfo(expr->PropertyFeedbackId());
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ Map* map = code->FindFirstMap()->CurrentMapForDeprecated();
+ return map == NULL || CanRetainOtherContext(map, *native_context_)
+ ? Handle<Map>::null()
+ : Handle<Map>(map);
+ }
+ return Handle<Map>::cast(map_or_code);
+}
+
+
+Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(
+ TypeFeedbackId ast_id) {
+ ASSERT(StoreIsMonomorphicNormal(ast_id));
+ Handle<Object> map_or_code = GetInfo(ast_id);
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ Map* map = code->FindFirstMap()->CurrentMapForDeprecated();
+ return map == NULL || CanRetainOtherContext(map, *native_context_)
+ ? Handle<Map>::null()
+ : Handle<Map>(map);
+ }
+ return Handle<Map>::cast(map_or_code);
+}
+
+
+KeyedAccessStoreMode TypeFeedbackOracle::GetStoreMode(
+ TypeFeedbackId ast_id) {
+ Handle<Object> map_or_code = GetInfo(ast_id);
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ if (code->kind() == Code::KEYED_STORE_IC) {
+ return Code::GetKeyedAccessStoreMode(code->extra_ic_state());
+ }
+ }
+ return STANDARD_STORE;
+}
+
+
+void TypeFeedbackOracle::LoadReceiverTypes(Property* expr,
+ Handle<String> name,
+ SmallMapList* types) {
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, Code::kNoExtraICState,
+ Code::NORMAL, Code::LOAD_IC);
+ CollectReceiverTypes(expr->PropertyFeedbackId(), name, flags, types);
+}
+
+
+void TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr,
+ Handle<String> name,
+ SmallMapList* types) {
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, Code::kNoExtraICState,
+ Code::NORMAL, Code::STORE_IC);
+ CollectReceiverTypes(expr->AssignmentFeedbackId(), name, flags, types);
+}
+
+
+void TypeFeedbackOracle::CallReceiverTypes(Call* expr,
+ Handle<String> name,
+ CallKind call_kind,
+ SmallMapList* types) {
+ int arity = expr->arguments()->length();
+
+ // Note: Currently we do not take string extra ic data into account
+ // here.
+ Code::ExtraICState extra_ic_state =
+ CallIC::Contextual::encode(call_kind == CALL_AS_FUNCTION);
+
+ Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
+ extra_ic_state,
+ Code::NORMAL,
+ arity,
+ OWN_MAP);
+ CollectReceiverTypes(expr->CallFeedbackId(), name, flags, types);
+}
+
+
+CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) {
+ Handle<Object> value = GetInfo(expr->CallFeedbackId());
+ if (!value->IsSmi()) return RECEIVER_MAP_CHECK;
+ CheckType check = static_cast<CheckType>(Smi::cast(*value)->value());
+ ASSERT(check != RECEIVER_MAP_CHECK);
+ return check;
+}
+
+
+Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) {
+ Handle<Object> info = GetInfo(expr->CallFeedbackId());
+ if (info->IsAllocationSite()) {
+ return Handle<JSFunction>(isolate_->global_context()->array_function());
+ } else {
+ return Handle<JSFunction>::cast(info);
+ }
+}
+
+
+Handle<JSFunction> TypeFeedbackOracle::GetCallNewTarget(CallNew* expr) {
+ Handle<Object> info = GetInfo(expr->CallNewFeedbackId());
+ if (info->IsAllocationSite()) {
+ return Handle<JSFunction>(isolate_->global_context()->array_function());
+ } else {
+ return Handle<JSFunction>::cast(info);
+ }
+}
+
+
+Handle<Cell> TypeFeedbackOracle::GetCallNewAllocationInfoCell(CallNew* expr) {
+ return GetInfoCell(expr->CallNewFeedbackId());
+}
+
+
+Handle<Map> TypeFeedbackOracle::GetObjectLiteralStoreMap(
+ ObjectLiteral::Property* prop) {
+ ASSERT(ObjectLiteralStoreIsMonomorphic(prop));
+ return Handle<Map>::cast(GetInfo(prop->key()->LiteralFeedbackId()));
+}
+
+
+bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) {
+ return *GetInfo(expr->PropertyFeedbackId()) ==
+ isolate_->builtins()->builtin(id);
+}
+
+
+bool TypeFeedbackOracle::LoadIsStub(Property* expr, ICStub* stub) {
+ Handle<Object> object = GetInfo(expr->PropertyFeedbackId());
+ if (!object->IsCode()) return false;
+ Handle<Code> code = Handle<Code>::cast(object);
+ if (!code->is_load_stub()) return false;
+ if (code->ic_state() != MONOMORPHIC) return false;
+ return stub->Describes(*code);
+}
+
+
+void TypeFeedbackOracle::CompareType(TypeFeedbackId id,
+ Handle<Type>* left_type,
+ Handle<Type>* right_type,
+ Handle<Type>* combined_type) {
+ Handle<Object> info = GetInfo(id);
+ if (!info->IsCode()) {
+ // For some comparisons we don't have ICs, e.g. LiteralCompareTypeof.
+ *left_type = *right_type = *combined_type = handle(Type::None(), isolate_);
+ return;
+ }
+ Handle<Code> code = Handle<Code>::cast(info);
+
+ Handle<Map> map;
+ Map* raw_map = code->FindFirstMap();
+ if (raw_map != NULL) {
+ raw_map = raw_map->CurrentMapForDeprecated();
+ if (raw_map != NULL && !CanRetainOtherContext(raw_map, *native_context_)) {
+ map = handle(raw_map, isolate_);
+ }
+ }
+
+ if (code->is_compare_ic_stub()) {
+ int stub_minor_key = code->stub_info();
+ CompareIC::StubInfoToType(
+ stub_minor_key, left_type, right_type, combined_type, map, isolate());
+ } else if (code->is_compare_nil_ic_stub()) {
+ CompareNilICStub stub(code->extended_extra_ic_state());
+ *combined_type = stub.GetType(isolate_, map);
+ *left_type = *right_type = stub.GetInputType(isolate_, map);
+ }
+}
+
+
+void TypeFeedbackOracle::BinaryType(TypeFeedbackId id,
+ Handle<Type>* left,
+ Handle<Type>* right,
+ Handle<Type>* result,
+ Maybe<int>* fixed_right_arg) {
+ Handle<Object> object = GetInfo(id);
+ if (!object->IsCode()) {
+ // For some binary ops we don't have ICs, e.g. Token::COMMA.
+ *left = *right = *result = handle(Type::None(), isolate_);
+ return;
+ }
+ Handle<Code> code = Handle<Code>::cast(object);
+ ASSERT(code->is_binary_op_stub());
+
+ int minor_key = code->stub_info();
+ BinaryOpIC::StubInfoToType(minor_key, left, right, result, isolate());
+ *fixed_right_arg =
+ BinaryOpStub::decode_fixed_right_arg_from_minor_key(minor_key);
+}
+
+
+Handle<Type> TypeFeedbackOracle::ClauseType(TypeFeedbackId id) {
+ Handle<Object> info = GetInfo(id);
+ Handle<Type> result(Type::None(), isolate_);
+ if (info->IsCode() && Handle<Code>::cast(info)->is_compare_ic_stub()) {
+ Handle<Code> code = Handle<Code>::cast(info);
+ CompareIC::State state = ICCompareStub::CompareState(code->stub_info());
+ result = CompareIC::StateToType(isolate_, state);
+ }
+ return result;
+}
+
+
+TypeInfo TypeFeedbackOracle::IncrementType(CountOperation* expr) {
+ Handle<Object> object = GetInfo(expr->CountBinOpFeedbackId());
+ TypeInfo unknown = TypeInfo::Unknown();
+ if (!object->IsCode()) return unknown;
+ Handle<Code> code = Handle<Code>::cast(object);
+ if (!code->is_binary_op_stub()) return unknown;
+
+ BinaryOpIC::TypeInfo left_type, right_type, unused_result_type;
+ BinaryOpStub::decode_types_from_minor_key(code->stub_info(), &left_type,
+ &right_type, &unused_result_type);
+ // CountOperations should always have +1 or -1 as their right input.
+ ASSERT(right_type == BinaryOpIC::SMI ||
+ right_type == BinaryOpIC::UNINITIALIZED);
+
+ switch (left_type) {
+ case BinaryOpIC::UNINITIALIZED:
+ case BinaryOpIC::SMI:
+ return TypeInfo::Smi();
+ case BinaryOpIC::INT32:
+ return TypeInfo::Integer32();
+ case BinaryOpIC::NUMBER:
+ return TypeInfo::Double();
+ case BinaryOpIC::STRING:
+ case BinaryOpIC::GENERIC:
+ return unknown;
+ default:
+ return unknown;
+ }
+ UNREACHABLE();
+ return unknown;
+}
+
+
+void TypeFeedbackOracle::CollectPolymorphicMaps(Handle<Code> code,
+ SmallMapList* types) {
+ MapHandleList maps;
+ code->FindAllMaps(&maps);
+ types->Reserve(maps.length(), zone());
+ for (int i = 0; i < maps.length(); i++) {
+ Handle<Map> map(maps.at(i));
+ if (!CanRetainOtherContext(*map, *native_context_)) {
+ types->AddMapIfMissing(map, zone());
+ }
+ }
+}
+
+
+void TypeFeedbackOracle::CollectReceiverTypes(TypeFeedbackId ast_id,
+ Handle<String> name,
+ Code::Flags flags,
+ SmallMapList* types) {
+ Handle<Object> object = GetInfo(ast_id);
+ if (object->IsUndefined() || object->IsSmi()) return;
+
+ if (object.is_identical_to(isolate_->builtins()->StoreIC_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() == GENERIC);
+ } else if (object->IsMap()) {
+ types->AddMapIfMissing(Handle<Map>::cast(object), zone());
+ } else if (Handle<Code>::cast(object)->ic_state() == POLYMORPHIC ||
+ Handle<Code>::cast(object)->ic_state() == MONOMORPHIC) {
+ CollectPolymorphicMaps(Handle<Code>::cast(object), types);
+ } else if (FLAG_collect_megamorphic_maps_from_stub_cache &&
+ Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) {
+ types->Reserve(4, zone());
+ ASSERT(object->IsCode());
+ isolate_->stub_cache()->CollectMatchingMaps(types,
+ name,
+ flags,
+ native_context_,
+ zone());
+ }
+}
+
+
+// Check if a map originates from a given native 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* native_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 native context.
+ if (!constructor->IsJSFunction()) return true;
+ // Check if the constructor directly references a foreign context.
+ if (CanRetainOtherContext(JSFunction::cast(constructor),
+ native_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, native_context);
+}
+
+
+bool TypeFeedbackOracle::CanRetainOtherContext(JSFunction* function,
+ Context* native_context) {
+ return function->context()->global_object() != native_context->global_object()
+ && function->context()->global_object() != native_context->builtins();
+}
+
+
+void TypeFeedbackOracle::CollectKeyedReceiverTypes(TypeFeedbackId ast_id,
+ SmallMapList* types) {
+ Handle<Object> object = GetInfo(ast_id);
+ if (!object->IsCode()) return;
+ Handle<Code> code = Handle<Code>::cast(object);
+ if (code->kind() == Code::KEYED_LOAD_IC ||
+ code->kind() == Code::KEYED_STORE_IC) {
+ CollectPolymorphicMaps(code, types);
+ }
+}
+
+
+void TypeFeedbackOracle::CollectPolymorphicStoreReceiverTypes(
+ TypeFeedbackId ast_id,
+ SmallMapList* types) {
+ Handle<Object> object = GetInfo(ast_id);
+ if (!object->IsCode()) return;
+ Handle<Code> code = Handle<Code>::cast(object);
+ if (code->kind() == Code::STORE_IC && code->ic_state() == POLYMORPHIC) {
+ CollectPolymorphicMaps(code, types);
+ }
+}
+
+
+byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId id) {
+ Handle<Object> object = GetInfo(id);
+ return object->IsCode() ? Handle<Code>::cast(object)->to_boolean_state() : 0;
+}
+
+
+// Things are a bit tricky here: The iterator for the RelocInfos and the infos
+// themselves are not GC-safe, so we first get all infos, then we create the
+// dictionary (possibly triggering GC), and finally we relocate the collected
+// infos before we process them.
+void TypeFeedbackOracle::BuildDictionary(Handle<Code> code) {
+ DisallowHeapAllocation no_allocation;
+ ZoneList<RelocInfo> infos(16, zone());
+ HandleScope scope(isolate_);
+ GetRelocInfos(code, &infos);
+ CreateDictionary(code, &infos);
+ ProcessRelocInfos(&infos);
+ ProcessTypeFeedbackCells(code);
+ // Allocate handle in the parent scope.
+ dictionary_ = scope.CloseAndEscape(dictionary_);
+}
+
+
+void TypeFeedbackOracle::GetRelocInfos(Handle<Code> code,
+ ZoneList<RelocInfo>* infos) {
+ int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
+ for (RelocIterator it(*code, mask); !it.done(); it.next()) {
+ infos->Add(*it.rinfo(), zone());
+ }
+}
+
+
+void TypeFeedbackOracle::CreateDictionary(Handle<Code> code,
+ ZoneList<RelocInfo>* infos) {
+ AllowHeapAllocation allocation_allowed;
+ int cell_count = code->type_feedback_info()->IsTypeFeedbackInfo()
+ ? TypeFeedbackInfo::cast(code->type_feedback_info())->
+ type_feedback_cells()->CellCount()
+ : 0;
+ int length = infos->length() + cell_count;
+ byte* old_start = code->instruction_start();
+ dictionary_ = isolate()->factory()->NewUnseededNumberDictionary(length);
+ byte* new_start = code->instruction_start();
+ RelocateRelocInfos(infos, old_start, new_start);
+}
+
+
+void TypeFeedbackOracle::RelocateRelocInfos(ZoneList<RelocInfo>* infos,
+ byte* old_start,
+ byte* new_start) {
+ for (int i = 0; i < infos->length(); i++) {
+ RelocInfo* info = &(*infos)[i];
+ info->set_pc(new_start + (info->pc() - old_start));
+ }
+}
+
+
+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();
+ TypeFeedbackId ast_id =
+ TypeFeedbackId(static_cast<unsigned>((*infos)[i].data()));
+ 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),
+ *native_context_)) {
+ Map* feedback = Map::cast(map)->CurrentMapForDeprecated();
+ if (feedback != NULL) SetInfo(ast_id, feedback);
+ }
+ }
+ } else {
+ SetInfo(ast_id, target);
+ }
+ break;
+
+ case Code::KEYED_LOAD_IC:
+ case Code::KEYED_STORE_IC:
+ if (target->ic_state() == MONOMORPHIC ||
+ target->ic_state() == POLYMORPHIC) {
+ SetInfo(ast_id, target);
+ }
+ break;
+
+ case Code::BINARY_OP_IC:
+ case Code::COMPARE_IC:
+ case Code::TO_BOOLEAN_IC:
+ case Code::COMPARE_NIL_IC:
+ SetInfo(ast_id, target);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+void TypeFeedbackOracle::ProcessTypeFeedbackCells(Handle<Code> code) {
+ Object* raw_info = code->type_feedback_info();
+ if (!raw_info->IsTypeFeedbackInfo()) return;
+ Handle<TypeFeedbackCells> cache(
+ TypeFeedbackInfo::cast(raw_info)->type_feedback_cells());
+ for (int i = 0; i < cache->CellCount(); i++) {
+ TypeFeedbackId ast_id = cache->AstId(i);
+ Cell* cell = cache->GetCell(i);
+ Object* value = cell->value();
+ if (value->IsSmi() ||
+ value->IsAllocationSite() ||
+ (value->IsJSFunction() &&
+ !CanRetainOtherContext(JSFunction::cast(value),
+ *native_context_))) {
+ SetInfo(ast_id, cell);
+ }
+ }
+}
+
+
+void TypeFeedbackOracle::SetInfo(TypeFeedbackId ast_id, Object* target) {
+ ASSERT(dictionary_->FindEntry(IdToKey(ast_id)) ==
+ UnseededNumberDictionary::kNotFound);
+ MaybeObject* maybe_result = dictionary_->AtNumberPut(IdToKey(ast_id), target);
+ USE(maybe_result);
+#ifdef DEBUG
+ Object* result = NULL;
+ // Dictionary has been allocated with sufficient size for all elements.
+ ASSERT(maybe_result->ToObject(&result));
+ ASSERT(*dictionary_ == result);
+#endif
+}
+
+
+Representation Representation::FromType(TypeInfo info) {
+ if (info.IsUninitialized()) return Representation::None();
+ if (info.IsSmi()) return Representation::Smi();
+ if (info.IsInteger32()) return Representation::Integer32();
+ if (info.IsDouble()) return Representation::Double();
+ if (info.IsNumber()) return Representation::Double();
+ return Representation::Tagged();
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/type-info.h b/chromium/v8/src/type-info.h
new file mode 100644
index 00000000000..4b376c84bdc
--- /dev/null
+++ b/chromium/v8/src/type-info.h
@@ -0,0 +1,353 @@
+// Copyright 2012 the V8 project authors. 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_TYPE_INFO_H_
+#define V8_TYPE_INFO_H_
+
+#include "allocation.h"
+#include "globals.h"
+#include "types.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+const int kMaxKeyedPolymorphism = 4;
+
+// Unknown
+// | \____________
+// | |
+// Primitive Non-primitive
+// | \_______ |
+// | | |
+// Number String |
+// / \ | |
+// Double Integer32 | /
+// | | / /
+// | Smi / /
+// | | / __/
+// Uninitialized.
+
+class TypeInfo {
+ public:
+ TypeInfo() : type_(kUninitialized) { }
+
+ static TypeInfo Unknown() { return TypeInfo(kUnknown); }
+ // We know it's a primitive type.
+ static TypeInfo Primitive() { return TypeInfo(kPrimitive); }
+ // We know it's a number of some sort.
+ static TypeInfo Number() { return TypeInfo(kNumber); }
+ // We know it's a signed 32 bit integer.
+ static TypeInfo Integer32() { return TypeInfo(kInteger32); }
+ // We know it's a Smi.
+ static TypeInfo Smi() { return TypeInfo(kSmi); }
+ // We know it's a heap number.
+ static TypeInfo Double() { return TypeInfo(kDouble); }
+ // We know it's a string.
+ static TypeInfo String() { return TypeInfo(kString); }
+ // We know it's an internalized string.
+ static TypeInfo InternalizedString() { return TypeInfo(kInternalizedString); }
+ // We know it's a non-primitive (object) type.
+ static TypeInfo NonPrimitive() { return TypeInfo(kNonPrimitive); }
+ // We haven't started collecting info yet.
+ static TypeInfo Uninitialized() { return TypeInfo(kUninitialized); }
+
+ int ToInt() {
+ return type_;
+ }
+
+ static TypeInfo FromInt(int bit_representation) {
+ Type t = static_cast<Type>(bit_representation);
+ ASSERT(t == kUnknown ||
+ t == kPrimitive ||
+ t == kNumber ||
+ t == kInteger32 ||
+ t == kSmi ||
+ t == kDouble ||
+ t == kString ||
+ t == kNonPrimitive);
+ return TypeInfo(t);
+ }
+
+ // Return the weakest (least precise) common type.
+ static TypeInfo Combine(TypeInfo a, TypeInfo b) {
+ return TypeInfo(static_cast<Type>(a.type_ & b.type_));
+ }
+
+
+ // Integer32 is an integer that can be represented as a signed
+ // 32-bit integer. It has to be
+ // in the range [-2^31, 2^31 - 1]. We also have to check for negative 0
+ // as it is not an Integer32.
+ static inline bool IsInt32Double(double value) {
+ const DoubleRepresentation minus_zero(-0.0);
+ DoubleRepresentation rep(value);
+ if (rep.bits == minus_zero.bits) return false;
+ if (value >= kMinInt && value <= kMaxInt &&
+ value == static_cast<int32_t>(value)) {
+ return true;
+ }
+ return false;
+ }
+
+ static TypeInfo FromValue(Handle<Object> value);
+
+ bool Equals(const TypeInfo& other) {
+ return type_ == other.type_;
+ }
+
+ inline bool IsUnknown() {
+ ASSERT(type_ != kUninitialized);
+ return type_ == kUnknown;
+ }
+
+ inline bool IsPrimitive() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kPrimitive) == kPrimitive);
+ }
+
+ inline bool IsNumber() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kNumber) == kNumber);
+ }
+
+ inline bool IsSmi() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kSmi) == kSmi);
+ }
+
+ inline bool IsInternalizedString() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kInternalizedString) == kInternalizedString);
+ }
+
+ inline bool IsNonInternalizedString() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kInternalizedString) == kString);
+ }
+
+ inline bool IsInteger32() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kInteger32) == kInteger32);
+ }
+
+ inline bool IsDouble() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kDouble) == kDouble);
+ }
+
+ inline bool IsString() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kString) == kString);
+ }
+
+ inline bool IsNonPrimitive() {
+ ASSERT(type_ != kUninitialized);
+ return ((type_ & kNonPrimitive) == kNonPrimitive);
+ }
+
+ inline bool IsUninitialized() {
+ return type_ == kUninitialized;
+ }
+
+ const char* ToString() {
+ switch (type_) {
+ case kUnknown: return "Unknown";
+ case kPrimitive: return "Primitive";
+ case kNumber: return "Number";
+ case kInteger32: return "Integer32";
+ case kSmi: return "Smi";
+ case kInternalizedString: return "InternalizedString";
+ case kDouble: return "Double";
+ case kString: return "String";
+ case kNonPrimitive: return "Object";
+ case kUninitialized: return "Uninitialized";
+ }
+ UNREACHABLE();
+ return "Unreachable code";
+ }
+
+ private:
+ enum Type {
+ kUnknown = 0, // 0000000
+ kPrimitive = 0x10, // 0010000
+ kNumber = 0x11, // 0010001
+ kInteger32 = 0x13, // 0010011
+ kSmi = 0x17, // 0010111
+ kDouble = 0x19, // 0011001
+ kString = 0x30, // 0110000
+ kInternalizedString = 0x32, // 0110010
+ kNonPrimitive = 0x40, // 1000000
+ kUninitialized = 0x7f // 1111111
+ };
+
+ explicit inline TypeInfo(Type t) : type_(t) { }
+
+ Type type_;
+};
+
+
+enum StringStubFeedback {
+ DEFAULT_STRING_STUB = 0,
+ STRING_INDEX_OUT_OF_BOUNDS = 1
+};
+
+
+// Forward declarations.
+// TODO(rossberg): these should all go away eventually.
+class Assignment;
+class Call;
+class CallNew;
+class CaseClause;
+class CompilationInfo;
+class CountOperation;
+class Expression;
+class ForInStatement;
+class ICStub;
+class Property;
+class SmallMapList;
+class ObjectLiteral;
+class ObjectLiteralProperty;
+
+
+class TypeFeedbackOracle: public ZoneObject {
+ public:
+ TypeFeedbackOracle(Handle<Code> code,
+ Handle<Context> native_context,
+ Isolate* isolate,
+ Zone* zone);
+
+ bool LoadIsMonomorphicNormal(Property* expr);
+ bool LoadIsUninitialized(Property* expr);
+ bool LoadIsPolymorphic(Property* expr);
+ bool StoreIsUninitialized(TypeFeedbackId ast_id);
+ bool StoreIsMonomorphicNormal(TypeFeedbackId ast_id);
+ bool StoreIsKeyedPolymorphic(TypeFeedbackId ast_id);
+ bool CallIsMonomorphic(Call* expr);
+ bool CallNewIsMonomorphic(CallNew* expr);
+ bool ObjectLiteralStoreIsMonomorphic(ObjectLiteralProperty* prop);
+
+ // TODO(1571) We can't use ForInStatement::ForInType as the return value due
+ // to various cycles in our headers.
+ byte ForInType(ForInStatement* expr);
+
+ Handle<Map> LoadMonomorphicReceiverType(Property* expr);
+ Handle<Map> StoreMonomorphicReceiverType(TypeFeedbackId id);
+
+ KeyedAccessStoreMode GetStoreMode(TypeFeedbackId ast_id);
+
+ void LoadReceiverTypes(Property* expr,
+ Handle<String> name,
+ SmallMapList* types);
+ void StoreReceiverTypes(Assignment* expr,
+ Handle<String> name,
+ SmallMapList* types);
+ void CallReceiverTypes(Call* expr,
+ Handle<String> name,
+ CallKind call_kind,
+ SmallMapList* types);
+ void CollectKeyedReceiverTypes(TypeFeedbackId ast_id,
+ SmallMapList* types);
+ void CollectPolymorphicStoreReceiverTypes(TypeFeedbackId ast_id,
+ SmallMapList* types);
+
+ static bool CanRetainOtherContext(Map* map, Context* native_context);
+ static bool CanRetainOtherContext(JSFunction* function,
+ Context* native_context);
+
+ void CollectPolymorphicMaps(Handle<Code> code, SmallMapList* types);
+
+ CheckType GetCallCheckType(Call* expr);
+ Handle<JSFunction> GetCallTarget(Call* expr);
+ Handle<JSFunction> GetCallNewTarget(CallNew* expr);
+ Handle<Cell> GetCallNewAllocationInfoCell(CallNew* expr);
+
+ Handle<Map> GetObjectLiteralStoreMap(ObjectLiteralProperty* prop);
+
+ bool LoadIsBuiltin(Property* expr, Builtins::Name id);
+ bool LoadIsStub(Property* expr, ICStub* stub);
+
+ // TODO(1571) We can't use ToBooleanStub::Types as the return value because
+ // of various cycles in our headers. Death to tons of implementations in
+ // headers!! :-P
+ byte ToBooleanTypes(TypeFeedbackId id);
+
+ // Get type information for arithmetic operations and compares.
+ void BinaryType(TypeFeedbackId id,
+ Handle<Type>* left,
+ Handle<Type>* right,
+ Handle<Type>* result,
+ Maybe<int>* fixed_right_arg);
+
+ void CompareType(TypeFeedbackId id,
+ Handle<Type>* left,
+ Handle<Type>* right,
+ Handle<Type>* combined);
+
+ Handle<Type> ClauseType(TypeFeedbackId id);
+
+ TypeInfo IncrementType(CountOperation* expr);
+
+ Zone* zone() const { return zone_; }
+ Isolate* isolate() const { return isolate_; }
+
+ private:
+ void CollectReceiverTypes(TypeFeedbackId ast_id,
+ Handle<String> name,
+ Code::Flags flags,
+ SmallMapList* types);
+
+ void SetInfo(TypeFeedbackId ast_id, Object* target);
+
+ void BuildDictionary(Handle<Code> code);
+ void GetRelocInfos(Handle<Code> code, ZoneList<RelocInfo>* infos);
+ void CreateDictionary(Handle<Code> code, ZoneList<RelocInfo>* infos);
+ void RelocateRelocInfos(ZoneList<RelocInfo>* infos,
+ byte* old_start,
+ byte* new_start);
+ void ProcessRelocInfos(ZoneList<RelocInfo>* infos);
+ void ProcessTypeFeedbackCells(Handle<Code> code);
+
+ // Returns an element from the backing store. Returns undefined if
+ // there is no information.
+ Handle<Object> GetInfo(TypeFeedbackId ast_id);
+
+ // Return the cell that contains type feedback.
+ Handle<Cell> GetInfoCell(TypeFeedbackId ast_id);
+
+ private:
+ Handle<Context> native_context_;
+ Isolate* isolate_;
+ Zone* zone_;
+ Handle<UnseededNumberDictionary> dictionary_;
+
+ DISALLOW_COPY_AND_ASSIGN(TypeFeedbackOracle);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_TYPE_INFO_H_
diff --git a/chromium/v8/src/typedarray.js b/chromium/v8/src/typedarray.js
new file mode 100644
index 00000000000..7bd16f670b4
--- /dev/null
+++ b/chromium/v8/src/typedarray.js
@@ -0,0 +1,595 @@
+// Copyright 2013 the V8 project authors. 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.
+
+"use strict";
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js:
+// var $Array = global.Array;
+
+
+
+// --------------- Typed Arrays ---------------------
+
+function CreateTypedArrayConstructor(name, elementSize, arrayId, constructor) {
+ function ConstructByArrayBuffer(obj, buffer, byteOffset, length) {
+ var offset = ToPositiveInteger(byteOffset, "invalid_typed_array_length")
+
+ if (offset % elementSize !== 0) {
+ throw MakeRangeError("invalid_typed_array_alignment",
+ "start offset", name, elementSize);
+ }
+ var bufferByteLength = %ArrayBufferGetByteLength(buffer);
+ if (offset > bufferByteLength) {
+ throw MakeRangeError("invalid_typed_array_offset");
+ }
+
+ var newByteLength;
+ var newLength;
+ if (IS_UNDEFINED(length)) {
+ if (bufferByteLength % elementSize !== 0) {
+ throw MakeRangeError("invalid_typed_array_alignment",
+ "byte length", name, elementSize);
+ }
+ newByteLength = bufferByteLength - offset;
+ newLength = newByteLength / elementSize;
+ } else {
+ var newLength = ToPositiveInteger(length, "invalid_typed_array_length");
+ newByteLength = newLength * elementSize;
+ }
+ if (offset + newByteLength > bufferByteLength) {
+ throw MakeRangeError("invalid_typed_array_length");
+ }
+ %TypedArrayInitialize(obj, arrayId, buffer, offset, newByteLength);
+ }
+
+ function ConstructByLength(obj, length) {
+ var l = ToPositiveInteger(length, "invalid_typed_array_length");
+ var byteLength = l * elementSize;
+ var buffer = new global.ArrayBuffer(byteLength);
+ %TypedArrayInitialize(obj, arrayId, buffer, 0, byteLength);
+ }
+
+ function ConstructByArrayLike(obj, arrayLike) {
+ var length = arrayLike.length;
+ var l = ToPositiveInteger(length, "invalid_typed_array_length");
+ if(!%TypedArrayInitializeFromArrayLike(obj, arrayId, arrayLike, l)) {
+ for (var i = 0; i < l; i++) {
+ obj[i] = arrayLike[i];
+ }
+ }
+ }
+
+ return function (arg1, arg2, arg3) {
+ if (%_IsConstructCall()) {
+ if (IS_ARRAYBUFFER(arg1)) {
+ ConstructByArrayBuffer(this, arg1, arg2, arg3);
+ } else if (IS_NUMBER(arg1) || IS_STRING(arg1) ||
+ IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) {
+ ConstructByLength(this, arg1);
+ } else {
+ ConstructByArrayLike(this, arg1);
+ }
+ } else {
+ throw MakeTypeError("constructor_not_function", [name])
+ }
+ }
+}
+
+function TypedArrayGetBuffer() {
+ return %TypedArrayGetBuffer(this);
+}
+
+function TypedArrayGetByteLength() {
+ return %TypedArrayGetByteLength(this);
+}
+
+function TypedArrayGetByteOffset() {
+ return %TypedArrayGetByteOffset(this);
+}
+
+function TypedArrayGetLength() {
+ return %TypedArrayGetLength(this);
+}
+
+function CreateSubArray(elementSize, constructor) {
+ return function(begin, end) {
+ var srcLength = %TypedArrayGetLength(this);
+ var beginInt = TO_INTEGER(begin);
+ if (beginInt < 0) {
+ beginInt = MathMax(0, srcLength + beginInt);
+ } else {
+ beginInt = MathMin(srcLength, beginInt);
+ }
+
+ var endInt = IS_UNDEFINED(end) ? srcLength : TO_INTEGER(end);
+ if (endInt < 0) {
+ endInt = MathMax(0, srcLength + endInt);
+ } else {
+ endInt = MathMin(endInt, srcLength);
+ }
+ if (endInt < beginInt) {
+ endInt = beginInt;
+ }
+ var newLength = endInt - beginInt;
+ var beginByteOffset =
+ %TypedArrayGetByteOffset(this) + beginInt * elementSize;
+ return new constructor(%TypedArrayGetBuffer(this),
+ beginByteOffset, newLength);
+ }
+}
+
+function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
+ if (offset > 0) {
+ for (var i = 0; i < sourceLength; i++) {
+ target[offset + i] = source[i];
+ }
+ }
+ else {
+ for (var i = 0; i < sourceLength; i++) {
+ target[i] = source[i];
+ }
+ }
+}
+
+function TypedArraySetFromOverlappingTypedArray(target, source, offset) {
+ var sourceElementSize = source.BYTES_PER_ELEMENT;
+ var targetElementSize = target.BYTES_PER_ELEMENT;
+ var sourceLength = source.length;
+
+ // Copy left part.
+ function CopyLeftPart() {
+ // First un-mutated byte after the next write
+ var targetPtr = target.byteOffset + (offset + 1) * targetElementSize;
+ // Next read at sourcePtr. We do not care for memory changing before
+ // sourcePtr - we have already copied it.
+ var sourcePtr = source.byteOffset;
+ for (var leftIndex = 0;
+ leftIndex < sourceLength && targetPtr <= sourcePtr;
+ leftIndex++) {
+ target[offset + leftIndex] = source[leftIndex];
+ targetPtr += targetElementSize;
+ sourcePtr += sourceElementSize;
+ }
+ return leftIndex;
+ }
+ var leftIndex = CopyLeftPart();
+
+ // Copy rigth part;
+ function CopyRightPart() {
+ // First unmutated byte before the next write
+ var targetPtr =
+ target.byteOffset + (offset + sourceLength - 1) * targetElementSize;
+ // Next read before sourcePtr. We do not care for memory changing after
+ // sourcePtr - we have already copied it.
+ var sourcePtr =
+ source.byteOffset + sourceLength * sourceElementSize;
+ for(var rightIndex = sourceLength - 1;
+ rightIndex >= leftIndex && targetPtr >= sourcePtr;
+ rightIndex--) {
+ target[offset + rightIndex] = source[rightIndex];
+ targetPtr -= targetElementSize;
+ sourcePtr -= sourceElementSize;
+ }
+ return rightIndex;
+ }
+ var rightIndex = CopyRightPart();
+
+ var temp = new $Array(rightIndex + 1 - leftIndex);
+ for (var i = leftIndex; i <= rightIndex; i++) {
+ temp[i - leftIndex] = source[i];
+ }
+ for (i = leftIndex; i <= rightIndex; i++) {
+ target[offset + i] = temp[i - leftIndex];
+ }
+}
+
+function TypedArraySet(obj, offset) {
+ var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset);
+ if (intOffset < 0) {
+ throw MakeTypeError("typed_array_set_negative_offset");
+ }
+ switch (%TypedArraySetFastCases(this, obj, intOffset)) {
+ // These numbers should be synchronized with runtime.cc.
+ case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE
+ return;
+ case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING
+ TypedArraySetFromOverlappingTypedArray(this, obj, intOffset);
+ return;
+ case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING
+ TypedArraySetFromArrayLike(this, obj, obj.length, intOffset);
+ return;
+ case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY
+ var l = obj.length;
+ if (IS_UNDEFINED(l)) {
+ if (IS_NUMBER(obj)) {
+ // For number as a first argument, throw TypeError
+ // instead of silently ignoring the call, so that
+ // the user knows (s)he did something wrong.
+ // (Consistent with Firefox and Blink/WebKit)
+ throw MakeTypeError("invalid_argument");
+ }
+ return;
+ }
+ if (intOffset + l > this.length) {
+ throw MakeRangeError("typed_array_set_source_too_large");
+ }
+ TypedArraySetFromArrayLike(this, obj, l, intOffset);
+ return;
+ }
+}
+
+// -------------------------------------------------------------------
+
+function SetupTypedArray(arrayId, name, constructor, elementSize) {
+ %CheckIsBootstrapping();
+ var fun = CreateTypedArrayConstructor(name, elementSize,
+ arrayId, constructor);
+ %SetCode(constructor, fun);
+ %FunctionSetPrototype(constructor, new $Object());
+
+ %SetProperty(constructor, "BYTES_PER_ELEMENT", elementSize,
+ READ_ONLY | DONT_ENUM | DONT_DELETE);
+ %SetProperty(constructor.prototype,
+ "constructor", constructor, DONT_ENUM);
+ %SetProperty(constructor.prototype,
+ "BYTES_PER_ELEMENT", elementSize,
+ READ_ONLY | DONT_ENUM | DONT_DELETE);
+ InstallGetter(constructor.prototype, "buffer", TypedArrayGetBuffer);
+ InstallGetter(constructor.prototype, "byteOffset", TypedArrayGetByteOffset);
+ InstallGetter(constructor.prototype, "byteLength", TypedArrayGetByteLength);
+ InstallGetter(constructor.prototype, "length", TypedArrayGetLength);
+
+ InstallFunctions(constructor.prototype, DONT_ENUM, $Array(
+ "subarray", CreateSubArray(elementSize, constructor),
+ "set", TypedArraySet
+ ));
+}
+
+// arrayIds below should be synchronized with Runtime_TypedArrayInitialize.
+SetupTypedArray(1, "Uint8Array", global.Uint8Array, 1);
+SetupTypedArray(2, "Int8Array", global.Int8Array, 1);
+SetupTypedArray(3, "Uint16Array", global.Uint16Array, 2);
+SetupTypedArray(4, "Int16Array", global.Int16Array, 2);
+SetupTypedArray(5, "Uint32Array", global.Uint32Array, 4);
+SetupTypedArray(6, "Int32Array", global.Int32Array, 4);
+SetupTypedArray(7, "Float32Array", global.Float32Array, 4);
+SetupTypedArray(8, "Float64Array", global.Float64Array, 8);
+SetupTypedArray(9, "Uint8ClampedArray", global.Uint8ClampedArray, 1);
+
+
+// --------------------------- DataView -----------------------------
+
+var $DataView = global.DataView;
+
+function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
+ if (%_IsConstructCall()) {
+ if (!IS_ARRAYBUFFER(buffer)) {
+ throw MakeTypeError('data_view_not_array_buffer', []);
+ }
+ var bufferByteLength = %ArrayBufferGetByteLength(buffer);
+ var offset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
+ if (offset > bufferByteLength) {
+ throw MakeRangeError('invalid_data_view_offset');
+ }
+ var length = IS_UNDEFINED(byteLength) ?
+ bufferByteLength - offset : TO_INTEGER(byteLength);
+ if (length < 0 || offset + length > bufferByteLength) {
+ throw new MakeRangeError('invalid_data_view_length');
+ }
+ %DataViewInitialize(this, buffer, offset, length);
+ } else {
+ throw MakeTypeError('constructor_not_function', ["DataView"]);
+ }
+}
+
+function DataViewGetBuffer() {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.buffer', this]);
+ }
+ return %DataViewGetBuffer(this);
+}
+
+function DataViewGetByteOffset() {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.byteOffset', this]);
+ }
+ return %DataViewGetByteOffset(this);
+}
+
+function DataViewGetByteLength() {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.byteLength', this]);
+ }
+ return %DataViewGetByteLength(this);
+}
+
+function ToPositiveDataViewOffset(offset) {
+ return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset');
+}
+
+function DataViewGetInt8(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.getInt8', this]);
+ }
+ if (%_ArgumentsLength() < 1) {
+ throw MakeTypeError('invalid_argument');
+ }
+ return %DataViewGetInt8(this,
+ ToPositiveDataViewOffset(offset),
+ !!little_endian);
+}
+
+function DataViewSetInt8(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.setInt8', this]);
+ }
+ if (%_ArgumentsLength() < 2) {
+ throw MakeTypeError('invalid_argument');
+ }
+ %DataViewSetInt8(this,
+ ToPositiveDataViewOffset(offset),
+ TO_NUMBER_INLINE(value),
+ !!little_endian);
+}
+
+function DataViewGetUint8(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.getUint8', this]);
+ }
+ if (%_ArgumentsLength() < 1) {
+ throw MakeTypeError('invalid_argument');
+ }
+ return %DataViewGetUint8(this,
+ ToPositiveDataViewOffset(offset),
+ !!little_endian);
+}
+
+function DataViewSetUint8(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.setUint8', this]);
+ }
+ if (%_ArgumentsLength() < 2) {
+ throw MakeTypeError('invalid_argument');
+ }
+ %DataViewSetUint8(this,
+ ToPositiveDataViewOffset(offset),
+ TO_NUMBER_INLINE(value),
+ !!little_endian);
+}
+
+function DataViewGetInt16(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.getInt16', this]);
+ }
+ if (%_ArgumentsLength() < 1) {
+ throw MakeTypeError('invalid_argument');
+ }
+ return %DataViewGetInt16(this,
+ ToPositiveDataViewOffset(offset),
+ !!little_endian);
+}
+
+function DataViewSetInt16(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.setInt16', this]);
+ }
+ if (%_ArgumentsLength() < 2) {
+ throw MakeTypeError('invalid_argument');
+ }
+ %DataViewSetInt16(this,
+ ToPositiveDataViewOffset(offset),
+ TO_NUMBER_INLINE(value),
+ !!little_endian);
+}
+
+function DataViewGetUint16(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.getUint16', this]);
+ }
+ if (%_ArgumentsLength() < 1) {
+ throw MakeTypeError('invalid_argument');
+ }
+ return %DataViewGetUint16(this,
+ ToPositiveDataViewOffset(offset),
+ !!little_endian);
+}
+
+function DataViewSetUint16(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.setUint16', this]);
+ }
+ if (%_ArgumentsLength() < 2) {
+ throw MakeTypeError('invalid_argument');
+ }
+ %DataViewSetUint16(this,
+ ToPositiveDataViewOffset(offset),
+ TO_NUMBER_INLINE(value),
+ !!little_endian);
+}
+
+function DataViewGetInt32(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.getInt32', this]);
+ }
+ if (%_ArgumentsLength() < 1) {
+ throw MakeTypeError('invalid_argument');
+ }
+ return %DataViewGetInt32(this,
+ ToPositiveDataViewOffset(offset),
+ !!little_endian);
+}
+
+function DataViewSetInt32(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.setInt32', this]);
+ }
+ if (%_ArgumentsLength() < 2) {
+ throw MakeTypeError('invalid_argument');
+ }
+ %DataViewSetInt32(this,
+ ToPositiveDataViewOffset(offset),
+ TO_NUMBER_INLINE(value),
+ !!little_endian);
+}
+
+function DataViewGetUint32(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.getUint32', this]);
+ }
+ if (%_ArgumentsLength() < 1) {
+ throw MakeTypeError('invalid_argument');
+ }
+ return %DataViewGetUint32(this,
+ ToPositiveDataViewOffset(offset),
+ !!little_endian);
+}
+
+function DataViewSetUint32(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.setUint32', this]);
+ }
+ if (%_ArgumentsLength() < 2) {
+ throw MakeTypeError('invalid_argument');
+ }
+ %DataViewSetUint32(this,
+ ToPositiveDataViewOffset(offset),
+ TO_NUMBER_INLINE(value),
+ !!little_endian);
+}
+
+function DataViewGetFloat32(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.getFloat32', this]);
+ }
+ if (%_ArgumentsLength() < 1) {
+ throw MakeTypeError('invalid_argument');
+ }
+ return %DataViewGetFloat32(this,
+ ToPositiveDataViewOffset(offset),
+ !!little_endian);
+}
+
+function DataViewSetFloat32(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.setFloat32', this]);
+ }
+ if (%_ArgumentsLength() < 2) {
+ throw MakeTypeError('invalid_argument');
+ }
+ %DataViewSetFloat32(this,
+ ToPositiveDataViewOffset(offset),
+ TO_NUMBER_INLINE(value),
+ !!little_endian);
+}
+
+function DataViewGetFloat64(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.getFloat64', this]);
+ }
+ if (%_ArgumentsLength() < 1) {
+ throw MakeTypeError('invalid_argument');
+ }
+ return %DataViewGetFloat64(this,
+ ToPositiveDataViewOffset(offset),
+ !!little_endian);
+}
+
+function DataViewSetFloat64(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError('incompatible_method_receiver',
+ ['DataView.setFloat64', this]);
+ }
+ if (%_ArgumentsLength() < 2) {
+ throw MakeTypeError('invalid_argument');
+ }
+ %DataViewSetFloat64(this,
+ ToPositiveDataViewOffset(offset),
+ TO_NUMBER_INLINE(value),
+ !!little_endian);
+}
+
+function SetupDataView() {
+ %CheckIsBootstrapping();
+
+ // Setup the DataView constructor.
+ %SetCode($DataView, DataViewConstructor);
+ %FunctionSetPrototype($DataView, new $Object);
+
+ // Set up constructor property on the DataView prototype.
+ %SetProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM);
+
+ InstallGetter($DataView.prototype, "buffer", DataViewGetBuffer);
+ InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset);
+ InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength);
+
+ InstallFunctions($DataView.prototype, DONT_ENUM, $Array(
+ "getInt8", DataViewGetInt8,
+ "setInt8", DataViewSetInt8,
+
+ "getUint8", DataViewGetUint8,
+ "setUint8", DataViewSetUint8,
+
+ "getInt16", DataViewGetInt16,
+ "setInt16", DataViewSetInt16,
+
+ "getUint16", DataViewGetUint16,
+ "setUint16", DataViewSetUint16,
+
+ "getInt32", DataViewGetInt32,
+ "setInt32", DataViewSetInt32,
+
+ "getUint32", DataViewGetUint32,
+ "setUint32", DataViewSetUint32,
+
+ "getFloat32", DataViewGetFloat32,
+ "setFloat32", DataViewSetFloat32,
+
+ "getFloat64", DataViewGetFloat64,
+ "setFloat64", DataViewSetFloat64
+ ));
+}
+
+SetupDataView();
diff --git a/chromium/v8/src/types.cc b/chromium/v8/src/types.cc
new file mode 100644
index 00000000000..70ddccd6a74
--- /dev/null
+++ b/chromium/v8/src/types.cc
@@ -0,0 +1,538 @@
+// Copyright 2013 the V8 project authors. 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 "types.h"
+#include "string-stream.h"
+
+namespace v8 {
+namespace internal {
+
+int Type::NumClasses() {
+ if (is_class()) {
+ return 1;
+ } else if (is_union()) {
+ Handle<Unioned> unioned = as_union();
+ int result = 0;
+ for (int i = 0; i < unioned->length(); ++i) {
+ if (union_get(unioned, i)->is_class()) ++result;
+ }
+ return result;
+ } else {
+ return 0;
+ }
+}
+
+
+int Type::NumConstants() {
+ if (is_constant()) {
+ return 1;
+ } else if (is_union()) {
+ Handle<Unioned> unioned = as_union();
+ int result = 0;
+ for (int i = 0; i < unioned->length(); ++i) {
+ if (union_get(unioned, i)->is_constant()) ++result;
+ }
+ return result;
+ } else {
+ return 0;
+ }
+}
+
+
+template<class T>
+Handle<Type> Type::Iterator<T>::get_type() {
+ ASSERT(!Done());
+ return type_->is_union() ? union_get(type_->as_union(), index_) : type_;
+}
+
+template<>
+Handle<Map> Type::Iterator<Map>::Current() {
+ return get_type()->as_class();
+}
+
+template<>
+Handle<v8::internal::Object> Type::Iterator<v8::internal::Object>::Current() {
+ return get_type()->as_constant();
+}
+
+
+template<>
+bool Type::Iterator<Map>::matches(Handle<Type> type) {
+ return type->is_class();
+}
+
+template<>
+bool Type::Iterator<v8::internal::Object>::matches(Handle<Type> type) {
+ return type->is_constant();
+}
+
+
+template<class T>
+void Type::Iterator<T>::Advance() {
+ ++index_;
+ if (type_->is_union()) {
+ Handle<Unioned> unioned = type_->as_union();
+ for (; index_ < unioned->length(); ++index_) {
+ if (matches(union_get(unioned, index_))) return;
+ }
+ } else if (index_ == 0 && matches(type_)) {
+ return;
+ }
+ index_ = -1;
+}
+
+template class Type::Iterator<Map>;
+template class Type::Iterator<v8::internal::Object>;
+
+
+// Get the smallest bitset subsuming this type.
+int Type::LubBitset() {
+ if (this->is_bitset()) {
+ return this->as_bitset();
+ } else if (this->is_union()) {
+ Handle<Unioned> unioned = this->as_union();
+ int bitset = kNone;
+ for (int i = 0; i < unioned->length(); ++i) {
+ bitset |= union_get(unioned, i)->LubBitset();
+ }
+ return bitset;
+ } else {
+ Map* map = NULL;
+ if (this->is_class()) {
+ map = *this->as_class();
+ } else {
+ Handle<v8::internal::Object> value = this->as_constant();
+ if (value->IsSmi()) return kSmi;
+ map = HeapObject::cast(*value)->map();
+ if (map->instance_type() == ODDBALL_TYPE) {
+ if (value->IsUndefined()) return kUndefined;
+ if (value->IsNull()) return kNull;
+ if (value->IsTrue() || value->IsFalse()) return kBoolean;
+ if (value->IsTheHole()) return kAny;
+ }
+ }
+ switch (map->instance_type()) {
+ case STRING_TYPE:
+ case ASCII_STRING_TYPE:
+ case CONS_STRING_TYPE:
+ case CONS_ASCII_STRING_TYPE:
+ case SLICED_STRING_TYPE:
+ case SLICED_ASCII_STRING_TYPE:
+ case EXTERNAL_STRING_TYPE:
+ case EXTERNAL_ASCII_STRING_TYPE:
+ case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
+ case SHORT_EXTERNAL_STRING_TYPE:
+ case SHORT_EXTERNAL_ASCII_STRING_TYPE:
+ case SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
+ case INTERNALIZED_STRING_TYPE:
+ case ASCII_INTERNALIZED_STRING_TYPE:
+ case CONS_INTERNALIZED_STRING_TYPE:
+ case CONS_ASCII_INTERNALIZED_STRING_TYPE:
+ case EXTERNAL_INTERNALIZED_STRING_TYPE:
+ case EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE:
+ case EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE:
+ case SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE:
+ case SHORT_EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE:
+ case SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE:
+ return kString;
+ case SYMBOL_TYPE:
+ return kSymbol;
+ case ODDBALL_TYPE:
+ return kOddball;
+ case HEAP_NUMBER_TYPE:
+ return kDouble;
+ case JS_VALUE_TYPE:
+ case JS_DATE_TYPE:
+ case JS_OBJECT_TYPE:
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
+ case JS_GENERATOR_OBJECT_TYPE:
+ case JS_MODULE_TYPE:
+ case JS_GLOBAL_OBJECT_TYPE:
+ case JS_BUILTINS_OBJECT_TYPE:
+ case JS_GLOBAL_PROXY_TYPE:
+ case JS_ARRAY_BUFFER_TYPE:
+ case JS_TYPED_ARRAY_TYPE:
+ case JS_DATA_VIEW_TYPE:
+ case JS_SET_TYPE:
+ case JS_MAP_TYPE:
+ case JS_WEAK_MAP_TYPE:
+ case JS_WEAK_SET_TYPE:
+ if (map->is_undetectable()) return kUndetectable;
+ return kOtherObject;
+ case JS_ARRAY_TYPE:
+ return kArray;
+ case JS_FUNCTION_TYPE:
+ return kFunction;
+ case JS_REGEXP_TYPE:
+ return kRegExp;
+ case JS_PROXY_TYPE:
+ case JS_FUNCTION_PROXY_TYPE:
+ return kProxy;
+ case MAP_TYPE:
+ // When compiling stub templates, the meta map is used as a place holder
+ // for the actual map with which the template is later instantiated.
+ // We treat it as a kind of type variable whose upper bound is Any.
+ // TODO(rossberg): for caching of CompareNilIC stubs to work correctly,
+ // we must exclude Undetectable here. This makes no sense, really,
+ // because it means that the template isn't actually parametric.
+ // Also, it doesn't apply elsewhere. 8-(
+ // We ought to find a cleaner solution for compiling stubs parameterised
+ // over type or class variables, esp ones with bounds...
+ return kDetectable;
+ case DECLARED_ACCESSOR_INFO_TYPE:
+ case EXECUTABLE_ACCESSOR_INFO_TYPE:
+ case ACCESSOR_PAIR_TYPE:
+ case FIXED_ARRAY_TYPE:
+ return kInternal;
+ default:
+ UNREACHABLE();
+ return kNone;
+ }
+ }
+}
+
+
+// Get the largest bitset subsumed by this type.
+int Type::GlbBitset() {
+ if (this->is_bitset()) {
+ return this->as_bitset();
+ } else if (this->is_union()) {
+ // All but the first are non-bitsets and thus would yield kNone anyway.
+ return union_get(this->as_union(), 0)->GlbBitset();
+ } else {
+ return kNone;
+ }
+}
+
+
+// Check this <= that.
+bool Type::IsSlowCase(Type* that) {
+ // Fast path for bitsets.
+ if (that->is_bitset()) {
+ return (this->LubBitset() | that->as_bitset()) == that->as_bitset();
+ }
+
+ if (that->is_class()) {
+ return this->is_class() && *this->as_class() == *that->as_class();
+ }
+ if (that->is_constant()) {
+ return this->is_constant() && *this->as_constant() == *that->as_constant();
+ }
+
+ // (T1 \/ ... \/ Tn) <= T <=> (T1 <= T) /\ ... /\ (Tn <= T)
+ if (this->is_union()) {
+ Handle<Unioned> unioned = this->as_union();
+ for (int i = 0; i < unioned->length(); ++i) {
+ Handle<Type> this_i = union_get(unioned, i);
+ if (!this_i->Is(that)) return false;
+ }
+ return true;
+ }
+
+ // T <= (T1 \/ ... \/ Tn) <=> (T <= T1) \/ ... \/ (T <= Tn)
+ // (iff T is not a union)
+ ASSERT(!this->is_union());
+ if (that->is_union()) {
+ Handle<Unioned> unioned = that->as_union();
+ for (int i = 0; i < unioned->length(); ++i) {
+ Handle<Type> that_i = union_get(unioned, i);
+ if (this->Is(that_i)) return true;
+ if (this->is_bitset()) break; // Fast fail, no other field is a bitset.
+ }
+ return false;
+ }
+
+ return false;
+}
+
+
+// Check this overlaps that.
+bool Type::Maybe(Type* that) {
+ // Fast path for bitsets.
+ if (this->is_bitset()) {
+ return (this->as_bitset() & that->LubBitset()) != 0;
+ }
+ if (that->is_bitset()) {
+ return (this->LubBitset() & that->as_bitset()) != 0;
+ }
+
+ // (T1 \/ ... \/ Tn) overlaps T <=> (T1 overlaps T) \/ ... \/ (Tn overlaps T)
+ if (this->is_union()) {
+ Handle<Unioned> unioned = this->as_union();
+ for (int i = 0; i < unioned->length(); ++i) {
+ Handle<Type> this_i = union_get(unioned, i);
+ if (this_i->Maybe(that)) return true;
+ }
+ return false;
+ }
+
+ // T overlaps (T1 \/ ... \/ Tn) <=> (T overlaps T1) \/ ... \/ (T overlaps Tn)
+ if (that->is_union()) {
+ Handle<Unioned> unioned = that->as_union();
+ for (int i = 0; i < unioned->length(); ++i) {
+ Handle<Type> that_i = union_get(unioned, i);
+ if (this->Maybe(that_i)) return true;
+ }
+ return false;
+ }
+
+ ASSERT(!that->is_union());
+ if (this->is_class()) {
+ return that->is_class() && *this->as_class() == *that->as_class();
+ }
+ if (this->is_constant()) {
+ return that->is_constant() && *this->as_constant() == *that->as_constant();
+ }
+
+ return false;
+}
+
+
+bool Type::InUnion(Handle<Unioned> unioned, int current_size) {
+ ASSERT(!this->is_union());
+ for (int i = 0; i < current_size; ++i) {
+ Handle<Type> type = union_get(unioned, i);
+ if (this->Is(type)) return true;
+ }
+ return false;
+}
+
+
+// Get non-bitsets from this which are not subsumed by union, store at unioned,
+// starting at index. Returns updated index.
+int Type::ExtendUnion(Handle<Unioned> result, int current_size) {
+ int old_size = current_size;
+ if (this->is_class() || this->is_constant()) {
+ if (!this->InUnion(result, old_size)) result->set(current_size++, this);
+ } else if (this->is_union()) {
+ Handle<Unioned> unioned = this->as_union();
+ for (int i = 0; i < unioned->length(); ++i) {
+ Handle<Type> type = union_get(unioned, i);
+ ASSERT(i == 0 || !(type->is_bitset() || type->Is(union_get(unioned, 0))));
+ if (type->is_bitset()) continue;
+ if (!type->InUnion(result, old_size)) result->set(current_size++, *type);
+ }
+ }
+ return current_size;
+}
+
+
+// Union is O(1) on simple bit unions, but O(n*m) on structured unions.
+// TODO(rossberg): Should we use object sets somehow? Is it worth it?
+Type* Type::Union(Handle<Type> type1, Handle<Type> type2) {
+ // Fast case: bit sets.
+ if (type1->is_bitset() && type2->is_bitset()) {
+ return from_bitset(type1->as_bitset() | type2->as_bitset());
+ }
+
+ // Fast case: top or bottom types.
+ if (type1->SameValue(Type::Any())) return *type1;
+ if (type2->SameValue(Type::Any())) return *type2;
+ if (type1->SameValue(Type::None())) return *type2;
+ if (type2->SameValue(Type::None())) return *type1;
+
+ // Semi-fast case: Unioned objects are neither involved nor produced.
+ if (!(type1->is_union() || type2->is_union())) {
+ if (type1->Is(type2)) return *type2;
+ if (type2->Is(type1)) return *type1;
+ }
+
+ // Slow case: may need to produce a Unioned object.
+ Isolate* isolate = NULL;
+ int size = type1->is_bitset() || type2->is_bitset() ? 1 : 0;
+ if (!type1->is_bitset()) {
+ isolate = HeapObject::cast(*type1)->GetIsolate();
+ size += (type1->is_union() ? type1->as_union()->length() : 1);
+ }
+ if (!type2->is_bitset()) {
+ isolate = HeapObject::cast(*type2)->GetIsolate();
+ size += (type2->is_union() ? type2->as_union()->length() : 1);
+ }
+ ASSERT(isolate != NULL);
+ ASSERT(size >= 2);
+ Handle<Unioned> unioned = isolate->factory()->NewFixedArray(size);
+ size = 0;
+
+ int bitset = type1->GlbBitset() | type2->GlbBitset();
+ if (bitset != kNone) unioned->set(size++, from_bitset(bitset));
+ size = type1->ExtendUnion(unioned, size);
+ size = type2->ExtendUnion(unioned, size);
+
+ if (size == 1) {
+ return *union_get(unioned, 0);
+ } else if (size == unioned->length()) {
+ return from_handle(unioned);
+ }
+
+ // There was an overlap. Copy to smaller union.
+ Handle<Unioned> result = isolate->factory()->NewFixedArray(size);
+ for (int i = 0; i < size; ++i) result->set(i, unioned->get(i));
+ return from_handle(result);
+}
+
+
+// Get non-bitsets from this which are also in that, store at unioned,
+// starting at index. Returns updated index.
+int Type::ExtendIntersection(
+ Handle<Unioned> result, Handle<Type> that, int current_size) {
+ int old_size = current_size;
+ if (this->is_class() || this->is_constant()) {
+ if (this->Is(that) && !this->InUnion(result, old_size))
+ result->set(current_size++, this);
+ } else if (this->is_union()) {
+ Handle<Unioned> unioned = this->as_union();
+ for (int i = 0; i < unioned->length(); ++i) {
+ Handle<Type> type = union_get(unioned, i);
+ ASSERT(i == 0 || !(type->is_bitset() || type->Is(union_get(unioned, 0))));
+ if (type->is_bitset()) continue;
+ if (type->Is(that) && !type->InUnion(result, old_size))
+ result->set(current_size++, *type);
+ }
+ }
+ return current_size;
+}
+
+
+// Intersection is O(1) on simple bit unions, but O(n*m) on structured unions.
+// TODO(rossberg): Should we use object sets somehow? Is it worth it?
+Type* Type::Intersect(Handle<Type> type1, Handle<Type> type2) {
+ // Fast case: bit sets.
+ if (type1->is_bitset() && type2->is_bitset()) {
+ return from_bitset(type1->as_bitset() & type2->as_bitset());
+ }
+
+ // Fast case: top or bottom types.
+ if (type1->SameValue(Type::None())) return *type1;
+ if (type2->SameValue(Type::None())) return *type2;
+ if (type1->SameValue(Type::Any())) return *type2;
+ if (type2->SameValue(Type::Any())) return *type1;
+
+ // Semi-fast case: Unioned objects are neither involved nor produced.
+ if (!(type1->is_union() || type2->is_union())) {
+ if (type1->Is(type2)) return *type1;
+ if (type2->Is(type1)) return *type2;
+ }
+
+ // Slow case: may need to produce a Unioned object.
+ Isolate* isolate = NULL;
+ int size = 0;
+ if (!type1->is_bitset()) {
+ isolate = HeapObject::cast(*type1)->GetIsolate();
+ size = (type1->is_union() ? type1->as_union()->length() : 2);
+ }
+ if (!type2->is_bitset()) {
+ isolate = HeapObject::cast(*type2)->GetIsolate();
+ int size2 = (type2->is_union() ? type2->as_union()->length() : 2);
+ size = (size == 0 ? size2 : Min(size, size2));
+ }
+ ASSERT(isolate != NULL);
+ ASSERT(size >= 2);
+ Handle<Unioned> unioned = isolate->factory()->NewFixedArray(size);
+ size = 0;
+
+ int bitset = type1->GlbBitset() & type2->GlbBitset();
+ if (bitset != kNone) unioned->set(size++, from_bitset(bitset));
+ size = type1->ExtendIntersection(unioned, type2, size);
+ size = type2->ExtendIntersection(unioned, type1, size);
+
+ if (size == 0) {
+ return None();
+ } else if (size == 1) {
+ return *union_get(unioned, 0);
+ } else if (size == unioned->length()) {
+ return from_handle(unioned);
+ }
+
+ // There were dropped cases. Copy to smaller union.
+ Handle<Unioned> result = isolate->factory()->NewFixedArray(size);
+ for (int i = 0; i < size; ++i) result->set(i, unioned->get(i));
+ return from_handle(result);
+}
+
+
+Type* Type::Optional(Handle<Type> type) {
+ return type->is_bitset()
+ ? from_bitset(type->as_bitset() | kUndefined)
+ : Union(type, Undefined()->handle_via_isolate_of(*type));
+}
+
+
+Representation Representation::FromType(Handle<Type> type) {
+ if (type->Is(Type::None())) return Representation::None();
+ if (type->Is(Type::Smi())) return Representation::Smi();
+ if (type->Is(Type::Signed32())) return Representation::Integer32();
+ if (type->Is(Type::Number())) return Representation::Double();
+ return Representation::Tagged();
+}
+
+
+#ifdef OBJECT_PRINT
+void Type::TypePrint() {
+ TypePrint(stdout);
+ PrintF(stdout, "\n");
+ Flush(stdout);
+}
+
+
+void Type::TypePrint(FILE* out) {
+ if (is_bitset()) {
+ int val = as_bitset();
+ const char* composed_name = GetComposedName(val);
+ if (composed_name != NULL) {
+ PrintF(out, "%s", composed_name);
+ return;
+ }
+ bool first_entry = true;
+ PrintF(out, "{");
+ for (unsigned i = 0; i < sizeof(val)*8; ++i) {
+ int mask = (1 << i);
+ if ((val & mask) != 0) {
+ if (!first_entry) PrintF(out, ",");
+ first_entry = false;
+ PrintF(out, "%s", GetPrimitiveName(mask));
+ }
+ }
+ PrintF(out, "}");
+ } else if (is_constant()) {
+ PrintF(out, "Constant(%p)", static_cast<void*>(*as_constant()));
+ } else if (is_class()) {
+ PrintF(out, "Class(%p)", static_cast<void*>(*as_class()));
+ } else if (is_union()) {
+ PrintF(out, "{");
+ Handle<Unioned> unioned = as_union();
+ for (int i = 0; i < unioned->length(); ++i) {
+ Handle<Type> type_i = union_get(unioned, i);
+ if (i > 0) PrintF(out, ",");
+ type_i->TypePrint(out);
+ }
+ PrintF(out, "}");
+ }
+}
+#endif
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/types.h b/chromium/v8/src/types.h
new file mode 100644
index 00000000000..fc69c785295
--- /dev/null
+++ b/chromium/v8/src/types.h
@@ -0,0 +1,335 @@
+// Copyright 2013 the V8 project authors. 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_TYPES_H_
+#define V8_TYPES_H_
+
+#include "v8.h"
+
+#include "objects.h"
+
+namespace v8 {
+namespace internal {
+
+
+// A simple type system for compiler-internal use. It is based entirely on
+// union types, and all subtyping hence amounts to set inclusion. Besides the
+// obvious primitive types and some predefined unions, the type language also
+// can express class types (a.k.a. specific maps) and singleton types (i.e.,
+// concrete constants).
+//
+// The following equations and inequations hold:
+//
+// None <= T
+// T <= Any
+//
+// Oddball = Boolean \/ Null \/ Undefined
+// Number = Signed32 \/ Unsigned32 \/ Double
+// Smi <= Signed32
+// Name = String \/ Symbol
+// UniqueName = InternalizedString \/ Symbol
+// InternalizedString < String
+//
+// Allocated = Receiver \/ Number \/ Name
+// Detectable = Allocated - Undetectable
+// Undetectable < Object
+// Receiver = Object \/ Proxy
+// Array < Object
+// Function < Object
+// RegExp < Object
+//
+// Class(map) < T iff instance_type(map) < T
+// Constant(x) < T iff instance_type(map(x)) < T
+//
+// Note that Constant(x) < Class(map(x)) does _not_ hold, since x's map can
+// change! (Its instance type cannot, however.)
+// TODO(rossberg): the latter is not currently true for proxies, because of fix,
+// but will hold once we implement direct proxies.
+//
+// There are two main functions for testing types:
+//
+// T1->Is(T2) -- tests whether T1 is included in T2 (i.e., T1 <= T2)
+// T1->Maybe(T2) -- tests whether T1 and T2 overlap (i.e., T1 /\ T2 =/= 0)
+//
+// Typically, the former is to be used to select representations (e.g., via
+// T->Is(Integer31())), and the to check whether a specific case needs handling
+// (e.g., via T->Maybe(Number())).
+//
+// There is no functionality to discover whether a type is a leaf in the
+// lattice. That is intentional. It should always be possible to refine the
+// lattice (e.g., splitting up number types further) without invalidating any
+// existing assumptions or tests.
+//
+// Consequently, do not use pointer equality for type tests, always use Is!
+//
+// Internally, all 'primitive' types, and their unions, are represented as
+// bitsets via smis. Class is a heap pointer to the respective map. Only
+// Constant's, or unions containing Class'es or Constant's, require allocation.
+// Note that the bitset representation is closed under both Union and Intersect.
+//
+// The type representation is heap-allocated, so cannot (currently) be used in
+// a parallel compilation context.
+
+
+#define PRIMITIVE_TYPE_LIST(V) \
+ V(None, 0) \
+ V(Null, 1 << 0) \
+ V(Undefined, 1 << 1) \
+ V(Boolean, 1 << 2) \
+ V(Smi, 1 << 3) \
+ V(OtherSigned32, 1 << 4) \
+ V(Unsigned32, 1 << 5) \
+ V(Double, 1 << 6) \
+ V(Symbol, 1 << 7) \
+ V(InternalizedString, 1 << 8) \
+ V(OtherString, 1 << 9) \
+ V(Undetectable, 1 << 10) \
+ V(Array, 1 << 11) \
+ V(Function, 1 << 12) \
+ V(RegExp, 1 << 13) \
+ V(OtherObject, 1 << 14) \
+ V(Proxy, 1 << 15) \
+ V(Internal, 1 << 16)
+
+#define COMPOSED_TYPE_LIST(V) \
+ V(Oddball, kBoolean | kNull | kUndefined) \
+ V(Signed32, kSmi | kOtherSigned32) \
+ V(Number, kSigned32 | kUnsigned32 | kDouble) \
+ V(String, kInternalizedString | kOtherString) \
+ V(UniqueName, kSymbol | kInternalizedString) \
+ V(Name, kSymbol | kString) \
+ V(NumberOrString, kNumber | kString) \
+ V(Object, kUndetectable | kArray | kFunction | \
+ kRegExp | kOtherObject) \
+ V(Receiver, kObject | kProxy) \
+ V(Allocated, kDouble | kName | kReceiver) \
+ V(Any, kOddball | kNumber | kAllocated | kInternal) \
+ V(Detectable, kAllocated - kUndetectable)
+
+#define TYPE_LIST(V) \
+ PRIMITIVE_TYPE_LIST(V) \
+ COMPOSED_TYPE_LIST(V)
+
+
+
+class Type : public Object {
+ public:
+ #define DEFINE_TYPE_CONSTRUCTOR(type, value) \
+ static Type* type() { return from_bitset(k##type); }
+ TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR)
+ #undef DEFINE_TYPE_CONSTRUCTOR
+
+ static Type* Class(Handle<Map> map) { return from_handle(map); }
+ static Type* Constant(Handle<HeapObject> value) {
+ return Constant(value, value->GetIsolate());
+ }
+ static Type* Constant(Handle<v8::internal::Object> value, Isolate* isolate) {
+ return from_handle(isolate->factory()->NewBox(value));
+ }
+
+ static Type* Union(Handle<Type> type1, Handle<Type> type2);
+ static Type* Intersect(Handle<Type> type1, Handle<Type> type2);
+ static Type* Optional(Handle<Type> type); // type \/ Undefined
+
+ bool Is(Type* that) { return (this == that) ? true : IsSlowCase(that); }
+ bool Is(Handle<Type> that) { return this->Is(*that); }
+ bool Maybe(Type* that);
+ bool Maybe(Handle<Type> that) { return this->Maybe(*that); }
+
+ bool IsClass() { return is_class(); }
+ bool IsConstant() { return is_constant(); }
+ Handle<Map> AsClass() { return as_class(); }
+ Handle<v8::internal::Object> AsConstant() { return as_constant(); }
+
+ int NumClasses();
+ int NumConstants();
+
+ template<class T>
+ class Iterator {
+ public:
+ bool Done() const { return index_ < 0; }
+ Handle<T> Current();
+ void Advance();
+
+ private:
+ friend class Type;
+
+ Iterator() : index_(-1) {}
+ explicit Iterator(Handle<Type> type) : type_(type), index_(-1) {
+ Advance();
+ }
+
+ inline bool matches(Handle<Type> type);
+ inline Handle<Type> get_type();
+
+ Handle<Type> type_;
+ int index_;
+ };
+
+ Iterator<Map> Classes() {
+ if (this->is_bitset()) return Iterator<Map>();
+ return Iterator<Map>(this->handle());
+ }
+ Iterator<v8::internal::Object> Constants() {
+ if (this->is_bitset()) return Iterator<v8::internal::Object>();
+ return Iterator<v8::internal::Object>(this->handle());
+ }
+
+ static Type* cast(v8::internal::Object* object) {
+ Type* t = static_cast<Type*>(object);
+ ASSERT(t->is_bitset() || t->is_class() ||
+ t->is_constant() || t->is_union());
+ return t;
+ }
+
+#ifdef OBJECT_PRINT
+ void TypePrint();
+ void TypePrint(FILE* out);
+#endif
+
+ private:
+ // A union is a fixed array containing types. Invariants:
+ // - its length is at least 2
+ // - at most one field is a bitset, and it must go into index 0
+ // - no field is a union
+ typedef FixedArray Unioned;
+
+ enum {
+ #define DECLARE_TYPE(type, value) k##type = (value),
+ TYPE_LIST(DECLARE_TYPE)
+ #undef DECLARE_TYPE
+ kUnusedEOL = 0
+ };
+
+ bool is_bitset() { return this->IsSmi(); }
+ bool is_class() { return this->IsMap(); }
+ bool is_constant() { return this->IsBox(); }
+ bool is_union() { return this->IsFixedArray(); }
+
+ bool IsSlowCase(Type* that);
+
+ int as_bitset() { return Smi::cast(this)->value(); }
+ Handle<Map> as_class() { return Handle<Map>::cast(handle()); }
+ Handle<v8::internal::Object> as_constant() {
+ Handle<Box> box = Handle<Box>::cast(handle());
+ return v8::internal::handle(box->value(), box->GetIsolate());
+ }
+ Handle<Unioned> as_union() { return Handle<Unioned>::cast(handle()); }
+
+ Handle<Type> handle() { return handle_via_isolate_of(this); }
+ Handle<Type> handle_via_isolate_of(Type* type) {
+ ASSERT(type->IsHeapObject());
+ return v8::internal::handle(this, HeapObject::cast(type)->GetIsolate());
+ }
+
+ static Type* from_bitset(int bitset) {
+ return static_cast<Type*>(Object::cast(Smi::FromInt(bitset)));
+ }
+ static Type* from_handle(Handle<HeapObject> handle) {
+ return static_cast<Type*>(Object::cast(*handle));
+ }
+
+ static Handle<Type> union_get(Handle<Unioned> unioned, int i) {
+ Type* type = static_cast<Type*>(unioned->get(i));
+ ASSERT(!type->is_union());
+ return type->handle_via_isolate_of(from_handle(unioned));
+ }
+
+ int LubBitset(); // least upper bound that's a bitset
+ int GlbBitset(); // greatest lower bound that's a bitset
+ bool InUnion(Handle<Unioned> unioned, int current_size);
+ int ExtendUnion(Handle<Unioned> unioned, int current_size);
+ int ExtendIntersection(
+ Handle<Unioned> unioned, Handle<Type> type, int current_size);
+
+ static const char* GetComposedName(int type) {
+ switch (type) {
+ #define PRINT_COMPOSED_TYPE(type, value) \
+ case k##type: \
+ return # type;
+ COMPOSED_TYPE_LIST(PRINT_COMPOSED_TYPE)
+ #undef PRINT_COMPOSED_TYPE
+ }
+ return NULL;
+ }
+
+ static const char* GetPrimitiveName(int type) {
+ switch (type) {
+ #define PRINT_PRIMITIVE_TYPE(type, value) \
+ case k##type: \
+ return # type;
+ PRIMITIVE_TYPE_LIST(PRINT_PRIMITIVE_TYPE)
+ #undef PRINT_PRIMITIVE_TYPE
+ default:
+ UNREACHABLE();
+ return "InvalidType";
+ }
+ }
+};
+
+
+// A simple struct to represent a pair of lower/upper type bounds.
+struct Bounds {
+ Handle<Type> lower;
+ Handle<Type> upper;
+
+ Bounds() {}
+ Bounds(Handle<Type> l, Handle<Type> u) : lower(l), upper(u) {}
+ Bounds(Type* l, Type* u, Isolate* isl) : lower(l, isl), upper(u, isl) {}
+ explicit Bounds(Handle<Type> t) : lower(t), upper(t) {}
+ Bounds(Type* t, Isolate* isl) : lower(t, isl), upper(t, isl) {}
+
+ // Unrestricted bounds.
+ static Bounds Unbounded(Isolate* isl) {
+ return Bounds(Type::None(), Type::Any(), isl);
+ }
+
+ // Meet: both b1 and b2 are known to hold.
+ static Bounds Both(Bounds b1, Bounds b2, Isolate* isl) {
+ return Bounds(
+ handle(Type::Union(b1.lower, b2.lower), isl),
+ handle(Type::Intersect(b1.upper, b2.upper), isl));
+ }
+
+ // Join: either b1 or b2 is known to hold.
+ static Bounds Either(Bounds b1, Bounds b2, Isolate* isl) {
+ return Bounds(
+ handle(Type::Intersect(b1.lower, b2.lower), isl),
+ handle(Type::Union(b1.upper, b2.upper), isl));
+ }
+
+ static Bounds NarrowLower(Bounds b, Handle<Type> t, Isolate* isl) {
+ return Bounds(handle(Type::Union(b.lower, t), isl), b.upper);
+ }
+ static Bounds NarrowUpper(Bounds b, Handle<Type> t, Isolate* isl) {
+ return Bounds(b.lower, handle(Type::Intersect(b.upper, t), isl));
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_TYPES_H_
diff --git a/chromium/v8/src/typing.cc b/chromium/v8/src/typing.cc
new file mode 100644
index 00000000000..f8e2a7c206d
--- /dev/null
+++ b/chromium/v8/src/typing.cc
@@ -0,0 +1,704 @@
+// Copyright 2013 the V8 project authors. 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 "typing.h"
+
+#include "parser.h" // for CompileTimeValue; TODO(rossberg): should move
+#include "scopes.h"
+
+namespace v8 {
+namespace internal {
+
+
+AstTyper::AstTyper(CompilationInfo* info)
+ : info_(info),
+ oracle_(
+ Handle<Code>(info->closure()->shared()->code()),
+ Handle<Context>(info->closure()->context()->native_context()),
+ info->isolate(),
+ info->zone()),
+ store_(info->zone()) {
+ InitializeAstVisitor();
+}
+
+
+#define RECURSE(call) \
+ do { \
+ ASSERT(!visitor->HasStackOverflow()); \
+ call; \
+ if (visitor->HasStackOverflow()) return; \
+ } while (false)
+
+void AstTyper::Run(CompilationInfo* info) {
+ AstTyper* visitor = new(info->zone()) AstTyper(info);
+ Scope* scope = info->scope();
+
+ // Handle implicit declaration of the function name in named function
+ // expressions before other declarations.
+ if (scope->is_function_scope() && scope->function() != NULL) {
+ RECURSE(visitor->VisitVariableDeclaration(scope->function()));
+ }
+ RECURSE(visitor->VisitDeclarations(scope->declarations()));
+ RECURSE(visitor->VisitStatements(info->function()->body()));
+}
+
+#undef RECURSE
+
+#define RECURSE(call) \
+ do { \
+ ASSERT(!HasStackOverflow()); \
+ call; \
+ if (HasStackOverflow()) return; \
+ } while (false)
+
+
+void AstTyper::VisitStatements(ZoneList<Statement*>* stmts) {
+ for (int i = 0; i < stmts->length(); ++i) {
+ Statement* stmt = stmts->at(i);
+ RECURSE(Visit(stmt));
+ if (stmt->IsJump()) break;
+ }
+}
+
+
+void AstTyper::VisitBlock(Block* stmt) {
+ RECURSE(VisitStatements(stmt->statements()));
+ if (stmt->labels() != NULL) {
+ store_.Forget(); // Control may transfer here via 'break l'.
+ }
+}
+
+
+void AstTyper::VisitExpressionStatement(ExpressionStatement* stmt) {
+ RECURSE(Visit(stmt->expression()));
+}
+
+
+void AstTyper::VisitEmptyStatement(EmptyStatement* stmt) {
+}
+
+
+void AstTyper::VisitIfStatement(IfStatement* stmt) {
+ // Collect type feedback.
+ if (!stmt->condition()->ToBooleanIsTrue() &&
+ !stmt->condition()->ToBooleanIsFalse()) {
+ stmt->condition()->RecordToBooleanTypeFeedback(oracle());
+ }
+
+ RECURSE(Visit(stmt->condition()));
+ Effects then_effects = EnterEffects();
+ RECURSE(Visit(stmt->then_statement()));
+ ExitEffects();
+ Effects else_effects = EnterEffects();
+ RECURSE(Visit(stmt->else_statement()));
+ ExitEffects();
+ then_effects.Alt(else_effects);
+ store_.Seq(then_effects);
+}
+
+
+void AstTyper::VisitContinueStatement(ContinueStatement* stmt) {
+ // TODO(rossberg): is it worth having a non-termination effect?
+}
+
+
+void AstTyper::VisitBreakStatement(BreakStatement* stmt) {
+ // TODO(rossberg): is it worth having a non-termination effect?
+}
+
+
+void AstTyper::VisitReturnStatement(ReturnStatement* stmt) {
+ // Collect type feedback.
+ // TODO(rossberg): we only need this for inlining into test contexts...
+ stmt->expression()->RecordToBooleanTypeFeedback(oracle());
+
+ RECURSE(Visit(stmt->expression()));
+ // TODO(rossberg): is it worth having a non-termination effect?
+}
+
+
+void AstTyper::VisitWithStatement(WithStatement* stmt) {
+ RECURSE(stmt->expression());
+ RECURSE(stmt->statement());
+}
+
+
+void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) {
+ RECURSE(Visit(stmt->tag()));
+
+ ZoneList<CaseClause*>* clauses = stmt->cases();
+ SwitchStatement::SwitchType switch_type = stmt->switch_type();
+ Effects local_effects(zone());
+ bool complex_effects = false; // True for label effects or fall-through.
+
+ for (int i = 0; i < clauses->length(); ++i) {
+ CaseClause* clause = clauses->at(i);
+ Effects clause_effects = EnterEffects();
+
+ if (!clause->is_default()) {
+ Expression* label = clause->label();
+ SwitchStatement::SwitchType label_switch_type =
+ label->IsSmiLiteral() ? SwitchStatement::SMI_SWITCH :
+ label->IsStringLiteral() ? SwitchStatement::STRING_SWITCH :
+ SwitchStatement::GENERIC_SWITCH;
+ if (switch_type == SwitchStatement::UNKNOWN_SWITCH)
+ switch_type = label_switch_type;
+ else if (switch_type != label_switch_type)
+ switch_type = SwitchStatement::GENERIC_SWITCH;
+
+ RECURSE(Visit(label));
+ if (!clause_effects.IsEmpty()) complex_effects = true;
+ }
+
+ ZoneList<Statement*>* stmts = clause->statements();
+ RECURSE(VisitStatements(stmts));
+ ExitEffects();
+ if (stmts->is_empty() || stmts->last()->IsJump()) {
+ local_effects.Alt(clause_effects);
+ } else {
+ complex_effects = true;
+ }
+ }
+
+ if (complex_effects) {
+ store_.Forget(); // Reached this in unknown state.
+ } else {
+ store_.Seq(local_effects);
+ }
+
+ if (switch_type == SwitchStatement::UNKNOWN_SWITCH)
+ switch_type = SwitchStatement::GENERIC_SWITCH;
+ stmt->set_switch_type(switch_type);
+
+ // Collect type feedback.
+ // TODO(rossberg): can we eliminate this special case and extra loop?
+ if (switch_type == SwitchStatement::SMI_SWITCH) {
+ for (int i = 0; i < clauses->length(); ++i) {
+ CaseClause* clause = clauses->at(i);
+ if (!clause->is_default())
+ clause->RecordTypeFeedback(oracle());
+ }
+ }
+}
+
+
+void AstTyper::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ // Collect type feedback.
+ if (!stmt->cond()->ToBooleanIsTrue()) {
+ stmt->cond()->RecordToBooleanTypeFeedback(oracle());
+ }
+
+ // TODO(rossberg): refine the unconditional Forget (here and elsewhere) by
+ // computing the set of variables assigned in only some of the origins of the
+ // control transfer (such as the loop body here).
+ store_.Forget(); // Control may transfer here via looping or 'continue'.
+ RECURSE(Visit(stmt->body()));
+ RECURSE(Visit(stmt->cond()));
+ store_.Forget(); // Control may transfer here via 'break'.
+}
+
+
+void AstTyper::VisitWhileStatement(WhileStatement* stmt) {
+ // Collect type feedback.
+ if (!stmt->cond()->ToBooleanIsTrue()) {
+ stmt->cond()->RecordToBooleanTypeFeedback(oracle());
+ }
+
+ store_.Forget(); // Control may transfer here via looping or 'continue'.
+ RECURSE(Visit(stmt->cond()));
+ RECURSE(Visit(stmt->body()));
+ store_.Forget(); // Control may transfer here via termination or 'break'.
+}
+
+
+void AstTyper::VisitForStatement(ForStatement* stmt) {
+ if (stmt->init() != NULL) {
+ RECURSE(Visit(stmt->init()));
+ }
+ store_.Forget(); // Control may transfer here via looping.
+ if (stmt->cond() != NULL) {
+ // Collect type feedback.
+ stmt->cond()->RecordToBooleanTypeFeedback(oracle());
+
+ RECURSE(Visit(stmt->cond()));
+ }
+ RECURSE(Visit(stmt->body()));
+ store_.Forget(); // Control may transfer here via 'continue'.
+ if (stmt->next() != NULL) {
+ RECURSE(Visit(stmt->next()));
+ }
+ store_.Forget(); // Control may transfer here via termination or 'break'.
+}
+
+
+void AstTyper::VisitForInStatement(ForInStatement* stmt) {
+ // Collect type feedback.
+ stmt->RecordTypeFeedback(oracle());
+
+ RECURSE(Visit(stmt->enumerable()));
+ store_.Forget(); // Control may transfer here via looping or 'continue'.
+ RECURSE(Visit(stmt->body()));
+ store_.Forget(); // Control may transfer here via 'break'.
+}
+
+
+void AstTyper::VisitForOfStatement(ForOfStatement* stmt) {
+ RECURSE(Visit(stmt->iterable()));
+ store_.Forget(); // Control may transfer here via looping or 'continue'.
+ RECURSE(Visit(stmt->body()));
+ store_.Forget(); // Control may transfer here via 'break'.
+}
+
+
+void AstTyper::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ Effects try_effects = EnterEffects();
+ RECURSE(Visit(stmt->try_block()));
+ ExitEffects();
+ Effects catch_effects = EnterEffects();
+ store_.Forget(); // Control may transfer here via 'throw'.
+ RECURSE(Visit(stmt->catch_block()));
+ ExitEffects();
+ try_effects.Alt(catch_effects);
+ store_.Seq(try_effects);
+ // At this point, only variables that were reassigned in the catch block are
+ // still remembered.
+}
+
+
+void AstTyper::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ RECURSE(Visit(stmt->try_block()));
+ store_.Forget(); // Control may transfer here via 'throw'.
+ RECURSE(Visit(stmt->finally_block()));
+}
+
+
+void AstTyper::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ store_.Forget(); // May do whatever.
+}
+
+
+void AstTyper::VisitFunctionLiteral(FunctionLiteral* expr) {
+}
+
+
+void AstTyper::VisitSharedFunctionInfoLiteral(SharedFunctionInfoLiteral* expr) {
+}
+
+
+void AstTyper::VisitConditional(Conditional* expr) {
+ // Collect type feedback.
+ expr->condition()->RecordToBooleanTypeFeedback(oracle());
+
+ RECURSE(Visit(expr->condition()));
+ Effects then_effects = EnterEffects();
+ RECURSE(Visit(expr->then_expression()));
+ ExitEffects();
+ Effects else_effects = EnterEffects();
+ RECURSE(Visit(expr->else_expression()));
+ ExitEffects();
+ then_effects.Alt(else_effects);
+ store_.Seq(then_effects);
+
+ NarrowType(expr, Bounds::Either(
+ expr->then_expression()->bounds(),
+ expr->else_expression()->bounds(), isolate_));
+}
+
+
+void AstTyper::VisitVariableProxy(VariableProxy* expr) {
+ Variable* var = expr->var();
+ if (var->IsStackAllocated()) {
+ NarrowType(expr, store_.LookupBounds(variable_index(var)));
+ }
+}
+
+
+void AstTyper::VisitLiteral(Literal* expr) {
+ Type* type = Type::Constant(expr->value(), isolate_);
+ NarrowType(expr, Bounds(type, isolate_));
+}
+
+
+void AstTyper::VisitRegExpLiteral(RegExpLiteral* expr) {
+ NarrowType(expr, Bounds(Type::RegExp(), isolate_));
+}
+
+
+void AstTyper::VisitObjectLiteral(ObjectLiteral* expr) {
+ ZoneList<ObjectLiteral::Property*>* properties = expr->properties();
+ for (int i = 0; i < properties->length(); ++i) {
+ ObjectLiteral::Property* prop = properties->at(i);
+
+ // Collect type feedback.
+ if ((prop->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL &&
+ !CompileTimeValue::IsCompileTimeValue(prop->value())) ||
+ prop->kind() == ObjectLiteral::Property::COMPUTED) {
+ if (prop->key()->value()->IsInternalizedString() && prop->emit_store()) {
+ prop->RecordTypeFeedback(oracle());
+ }
+ }
+
+ RECURSE(Visit(prop->value()));
+ }
+
+ NarrowType(expr, Bounds(Type::Object(), isolate_));
+}
+
+
+void AstTyper::VisitArrayLiteral(ArrayLiteral* expr) {
+ ZoneList<Expression*>* values = expr->values();
+ for (int i = 0; i < values->length(); ++i) {
+ Expression* value = values->at(i);
+ RECURSE(Visit(value));
+ }
+
+ NarrowType(expr, Bounds(Type::Array(), isolate_));
+}
+
+
+void AstTyper::VisitAssignment(Assignment* expr) {
+ // TODO(rossberg): Can we clean this up?
+ if (expr->is_compound()) {
+ // Collect type feedback.
+ Expression* target = expr->target();
+ Property* prop = target->AsProperty();
+ if (prop != NULL) {
+ prop->RecordTypeFeedback(oracle(), zone());
+ expr->RecordTypeFeedback(oracle(), zone());
+ }
+
+ RECURSE(Visit(expr->binary_operation()));
+
+ NarrowType(expr, expr->binary_operation()->bounds());
+ } else {
+ // Collect type feedback.
+ if (expr->target()->IsProperty()) {
+ expr->RecordTypeFeedback(oracle(), zone());
+ }
+
+ RECURSE(Visit(expr->target()));
+ RECURSE(Visit(expr->value()));
+
+ NarrowType(expr, expr->value()->bounds());
+ }
+
+ VariableProxy* proxy = expr->target()->AsVariableProxy();
+ if (proxy != NULL && proxy->var()->IsStackAllocated()) {
+ store_.Seq(variable_index(proxy->var()), Effect(expr->bounds()));
+ }
+}
+
+
+void AstTyper::VisitYield(Yield* expr) {
+ RECURSE(Visit(expr->generator_object()));
+ RECURSE(Visit(expr->expression()));
+
+ // We don't know anything about the result type.
+}
+
+
+void AstTyper::VisitThrow(Throw* expr) {
+ RECURSE(Visit(expr->exception()));
+ // TODO(rossberg): is it worth having a non-termination effect?
+
+ NarrowType(expr, Bounds(Type::None(), isolate_));
+}
+
+
+void AstTyper::VisitProperty(Property* expr) {
+ // Collect type feedback.
+ expr->RecordTypeFeedback(oracle(), zone());
+
+ RECURSE(Visit(expr->obj()));
+ RECURSE(Visit(expr->key()));
+
+ // We don't know anything about the result type.
+}
+
+
+void AstTyper::VisitCall(Call* expr) {
+ // Collect type feedback.
+ Expression* callee = expr->expression();
+ Property* prop = callee->AsProperty();
+ if (prop != NULL) {
+ if (prop->key()->IsPropertyName())
+ expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
+ } else {
+ expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
+ }
+
+ RECURSE(Visit(expr->expression()));
+ ZoneList<Expression*>* args = expr->arguments();
+ for (int i = 0; i < args->length(); ++i) {
+ Expression* arg = args->at(i);
+ RECURSE(Visit(arg));
+ }
+
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+ if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
+ store_.Forget(); // Eval could do whatever to local variables.
+ }
+
+ // We don't know anything about the result type.
+}
+
+
+void AstTyper::VisitCallNew(CallNew* expr) {
+ // Collect type feedback.
+ expr->RecordTypeFeedback(oracle());
+
+ RECURSE(Visit(expr->expression()));
+ ZoneList<Expression*>* args = expr->arguments();
+ for (int i = 0; i < args->length(); ++i) {
+ Expression* arg = args->at(i);
+ RECURSE(Visit(arg));
+ }
+
+ // We don't know anything about the result type.
+}
+
+
+void AstTyper::VisitCallRuntime(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ for (int i = 0; i < args->length(); ++i) {
+ Expression* arg = args->at(i);
+ RECURSE(Visit(arg));
+ }
+
+ // We don't know anything about the result type.
+}
+
+
+void AstTyper::VisitUnaryOperation(UnaryOperation* expr) {
+ // Collect type feedback.
+ if (expr->op() == Token::NOT) {
+ // TODO(rossberg): only do in test or value context.
+ expr->expression()->RecordToBooleanTypeFeedback(oracle());
+ }
+
+ RECURSE(Visit(expr->expression()));
+
+ switch (expr->op()) {
+ case Token::NOT:
+ case Token::DELETE:
+ NarrowType(expr, Bounds(Type::Boolean(), isolate_));
+ break;
+ case Token::VOID:
+ NarrowType(expr, Bounds(Type::Undefined(), isolate_));
+ break;
+ case Token::TYPEOF:
+ NarrowType(expr, Bounds(Type::InternalizedString(), isolate_));
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void AstTyper::VisitCountOperation(CountOperation* expr) {
+ // Collect type feedback.
+ expr->RecordTypeFeedback(oracle(), zone());
+ Property* prop = expr->expression()->AsProperty();
+ if (prop != NULL) {
+ prop->RecordTypeFeedback(oracle(), zone());
+ }
+
+ RECURSE(Visit(expr->expression()));
+
+ NarrowType(expr, Bounds(Type::Smi(), Type::Number(), isolate_));
+
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+ if (proxy != NULL && proxy->var()->IsStackAllocated()) {
+ store_.Seq(variable_index(proxy->var()), Effect(expr->bounds()));
+ }
+}
+
+
+void AstTyper::VisitBinaryOperation(BinaryOperation* expr) {
+ // Collect type feedback.
+ Handle<Type> type, left_type, right_type;
+ Maybe<int> fixed_right_arg;
+ oracle()->BinaryType(expr->BinaryOperationFeedbackId(),
+ &left_type, &right_type, &type, &fixed_right_arg);
+ NarrowLowerType(expr, type);
+ NarrowLowerType(expr->left(), left_type);
+ NarrowLowerType(expr->right(), right_type);
+ expr->set_fixed_right_arg(fixed_right_arg);
+ if (expr->op() == Token::OR || expr->op() == Token::AND) {
+ expr->left()->RecordToBooleanTypeFeedback(oracle());
+ }
+
+ switch (expr->op()) {
+ case Token::COMMA:
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
+ NarrowType(expr, expr->right()->bounds());
+ break;
+ case Token::OR:
+ case Token::AND: {
+ Effects left_effects = EnterEffects();
+ RECURSE(Visit(expr->left()));
+ ExitEffects();
+ Effects right_effects = EnterEffects();
+ RECURSE(Visit(expr->right()));
+ ExitEffects();
+ left_effects.Alt(right_effects);
+ store_.Seq(left_effects);
+
+ NarrowType(expr, Bounds::Either(
+ expr->left()->bounds(), expr->right()->bounds(), isolate_));
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_AND: {
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
+ Type* upper = Type::Union(
+ expr->left()->bounds().upper, expr->right()->bounds().upper);
+ if (!upper->Is(Type::Signed32())) upper = Type::Signed32();
+ NarrowType(expr, Bounds(Type::Smi(), upper, isolate_));
+ break;
+ }
+ case Token::BIT_XOR:
+ case Token::SHL:
+ case Token::SAR:
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
+ NarrowType(expr, Bounds(Type::Smi(), Type::Signed32(), isolate_));
+ break;
+ case Token::SHR:
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
+ NarrowType(expr, Bounds(Type::Smi(), Type::Unsigned32(), isolate_));
+ break;
+ case Token::ADD: {
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
+ Bounds l = expr->left()->bounds();
+ Bounds r = expr->right()->bounds();
+ Type* lower =
+ l.lower->Is(Type::Number()) && r.lower->Is(Type::Number()) ?
+ Type::Smi() :
+ l.lower->Is(Type::String()) || r.lower->Is(Type::String()) ?
+ Type::String() : Type::None();
+ Type* upper =
+ l.upper->Is(Type::Number()) && r.upper->Is(Type::Number()) ?
+ Type::Number() :
+ l.upper->Is(Type::String()) || r.upper->Is(Type::String()) ?
+ Type::String() : Type::NumberOrString();
+ NarrowType(expr, Bounds(lower, upper, isolate_));
+ break;
+ }
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
+ NarrowType(expr, Bounds(Type::Smi(), Type::Number(), isolate_));
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void AstTyper::VisitCompareOperation(CompareOperation* expr) {
+ // Collect type feedback.
+ Handle<Type> left_type, right_type, combined_type;
+ oracle()->CompareType(expr->CompareOperationFeedbackId(),
+ &left_type, &right_type, &combined_type);
+ NarrowLowerType(expr->left(), left_type);
+ NarrowLowerType(expr->right(), right_type);
+ expr->set_combined_type(combined_type);
+
+ RECURSE(Visit(expr->left()));
+ RECURSE(Visit(expr->right()));
+
+ NarrowType(expr, Bounds(Type::Boolean(), isolate_));
+}
+
+
+void AstTyper::VisitThisFunction(ThisFunction* expr) {
+}
+
+
+void AstTyper::VisitDeclarations(ZoneList<Declaration*>* decls) {
+ for (int i = 0; i < decls->length(); ++i) {
+ Declaration* decl = decls->at(i);
+ RECURSE(Visit(decl));
+ }
+}
+
+
+void AstTyper::VisitVariableDeclaration(VariableDeclaration* declaration) {
+}
+
+
+void AstTyper::VisitFunctionDeclaration(FunctionDeclaration* declaration) {
+ RECURSE(Visit(declaration->fun()));
+}
+
+
+void AstTyper::VisitModuleDeclaration(ModuleDeclaration* declaration) {
+ RECURSE(Visit(declaration->module()));
+}
+
+
+void AstTyper::VisitImportDeclaration(ImportDeclaration* declaration) {
+ RECURSE(Visit(declaration->module()));
+}
+
+
+void AstTyper::VisitExportDeclaration(ExportDeclaration* declaration) {
+}
+
+
+void AstTyper::VisitModuleLiteral(ModuleLiteral* module) {
+ RECURSE(Visit(module->body()));
+}
+
+
+void AstTyper::VisitModuleVariable(ModuleVariable* module) {
+}
+
+
+void AstTyper::VisitModulePath(ModulePath* module) {
+ RECURSE(Visit(module->module()));
+}
+
+
+void AstTyper::VisitModuleUrl(ModuleUrl* module) {
+}
+
+
+void AstTyper::VisitModuleStatement(ModuleStatement* stmt) {
+ RECURSE(Visit(stmt->body()));
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/typing.h b/chromium/v8/src/typing.h
new file mode 100644
index 00000000000..c942b006327
--- /dev/null
+++ b/chromium/v8/src/typing.h
@@ -0,0 +1,102 @@
+// Copyright 2013 the V8 project authors. 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_TYPING_H_
+#define V8_TYPING_H_
+
+#include "v8.h"
+
+#include "allocation.h"
+#include "ast.h"
+#include "compiler.h"
+#include "type-info.h"
+#include "types.h"
+#include "effects.h"
+#include "zone.h"
+#include "scopes.h"
+
+namespace v8 {
+namespace internal {
+
+
+class AstTyper: public AstVisitor {
+ public:
+ static void Run(CompilationInfo* info);
+
+ void* operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+ }
+ void operator delete(void* pointer, Zone* zone) { }
+ void operator delete(void* pointer) { }
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+
+ private:
+ explicit AstTyper(CompilationInfo* info);
+
+ static const int kNoVar = INT_MIN;
+ typedef v8::internal::Effects<int, kNoVar> Effects;
+ typedef v8::internal::NestedEffects<int, kNoVar> Store;
+
+ CompilationInfo* info_;
+ TypeFeedbackOracle oracle_;
+ Store store_;
+
+ TypeFeedbackOracle* oracle() { return &oracle_; }
+ Zone* zone() const { return info_->zone(); }
+
+ void NarrowType(Expression* e, Bounds b) {
+ e->set_bounds(Bounds::Both(e->bounds(), b, isolate_));
+ }
+ void NarrowLowerType(Expression* e, Handle<Type> t) {
+ e->set_bounds(Bounds::NarrowLower(e->bounds(), t, isolate_));
+ }
+
+ Effects EnterEffects() {
+ store_ = store_.Push();
+ return store_.Top();
+ }
+ void ExitEffects() { store_ = store_.Pop(); }
+
+ int variable_index(Variable* var) {
+ return var->IsStackLocal() ? var->index() :
+ var->IsParameter() ? -var->index() : kNoVar;
+ }
+
+ void VisitDeclarations(ZoneList<Declaration*>* declarations);
+ void VisitStatements(ZoneList<Statement*>* statements);
+
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ DISALLOW_COPY_AND_ASSIGN(AstTyper);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_TYPING_H_
diff --git a/chromium/v8/src/unbound-queue-inl.h b/chromium/v8/src/unbound-queue-inl.h
new file mode 100644
index 00000000000..796ba401d58
--- /dev/null
+++ b/chromium/v8/src/unbound-queue-inl.h
@@ -0,0 +1,107 @@
+// 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.
+
+#ifndef V8_UNBOUND_QUEUE_INL_H_
+#define V8_UNBOUND_QUEUE_INL_H_
+
+#include "unbound-queue.h"
+
+#include "atomicops.h"
+
+namespace v8 {
+namespace internal {
+
+template<typename Record>
+struct UnboundQueue<Record>::Node: public Malloced {
+ explicit Node(const Record& value)
+ : value(value), next(NULL) {
+ }
+
+ Record value;
+ Node* next;
+};
+
+
+template<typename Record>
+UnboundQueue<Record>::UnboundQueue() {
+ first_ = new Node(Record());
+ divider_ = last_ = reinterpret_cast<AtomicWord>(first_);
+}
+
+
+template<typename Record>
+UnboundQueue<Record>::~UnboundQueue() {
+ while (first_ != NULL) DeleteFirst();
+}
+
+
+template<typename Record>
+void UnboundQueue<Record>::DeleteFirst() {
+ Node* tmp = first_;
+ first_ = tmp->next;
+ delete tmp;
+}
+
+
+template<typename Record>
+bool UnboundQueue<Record>::Dequeue(Record* rec) {
+ if (divider_ == Acquire_Load(&last_)) return false;
+ Node* next = reinterpret_cast<Node*>(divider_)->next;
+ *rec = next->value;
+ Release_Store(&divider_, reinterpret_cast<AtomicWord>(next));
+ return true;
+}
+
+
+template<typename Record>
+void UnboundQueue<Record>::Enqueue(const Record& rec) {
+ Node*& next = reinterpret_cast<Node*>(last_)->next;
+ next = new Node(rec);
+ Release_Store(&last_, reinterpret_cast<AtomicWord>(next));
+
+ while (first_ != reinterpret_cast<Node*>(Acquire_Load(&divider_))) {
+ DeleteFirst();
+ }
+}
+
+
+template<typename Record>
+bool UnboundQueue<Record>::IsEmpty() const {
+ return NoBarrier_Load(&divider_) == NoBarrier_Load(&last_);
+}
+
+
+template<typename Record>
+Record* UnboundQueue<Record>::Peek() const {
+ if (divider_ == Acquire_Load(&last_)) return NULL;
+ Node* next = reinterpret_cast<Node*>(divider_)->next;
+ return &next->value;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_UNBOUND_QUEUE_INL_H_
diff --git a/chromium/v8/src/unbound-queue.h b/chromium/v8/src/unbound-queue.h
new file mode 100644
index 00000000000..429e3c673e5
--- /dev/null
+++ b/chromium/v8/src/unbound-queue.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef V8_UNBOUND_QUEUE_
+#define V8_UNBOUND_QUEUE_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Lock-free unbound queue for small records. Intended for
+// transferring small records between a Single producer and a Single
+// consumer. Doesn't have restrictions on the number of queued
+// elements, so producer never blocks. Implemented after Herb
+// Sutter's article:
+// http://www.ddj.com/high-performance-computing/210604448
+template<typename Record>
+class UnboundQueue BASE_EMBEDDED {
+ public:
+ inline UnboundQueue();
+ inline ~UnboundQueue();
+
+ INLINE(bool Dequeue(Record* rec));
+ INLINE(void Enqueue(const Record& rec));
+ INLINE(bool IsEmpty() const);
+ INLINE(Record* Peek() const);
+
+ private:
+ INLINE(void DeleteFirst());
+
+ struct Node;
+
+ Node* first_;
+ AtomicWord divider_; // Node*
+ AtomicWord last_; // Node*
+
+ DISALLOW_COPY_AND_ASSIGN(UnboundQueue);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_UNBOUND_QUEUE_
diff --git a/chromium/v8/src/unicode-inl.h b/chromium/v8/src/unicode-inl.h
new file mode 100644
index 00000000000..f861f9f2d47
--- /dev/null
+++ b/chromium/v8/src/unicode-inl.h
@@ -0,0 +1,218 @@
+// Copyright 2007-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.
+
+#ifndef V8_UNICODE_INL_H_
+#define V8_UNICODE_INL_H_
+
+#include "unicode.h"
+#include "checks.h"
+#include "platform.h"
+
+namespace unibrow {
+
+template <class T, int s> bool Predicate<T, s>::get(uchar code_point) {
+ CacheEntry entry = entries_[code_point & kMask];
+ if (entry.code_point_ == code_point) return entry.value_;
+ return CalculateValue(code_point);
+}
+
+template <class T, int s> bool Predicate<T, s>::CalculateValue(
+ uchar code_point) {
+ bool result = T::Is(code_point);
+ entries_[code_point & kMask] = CacheEntry(code_point, result);
+ return result;
+}
+
+template <class T, int s> int Mapping<T, s>::get(uchar c, uchar n,
+ uchar* result) {
+ CacheEntry entry = entries_[c & kMask];
+ if (entry.code_point_ == c) {
+ if (entry.offset_ == 0) {
+ return 0;
+ } else {
+ result[0] = c + entry.offset_;
+ return 1;
+ }
+ } else {
+ return CalculateValue(c, n, result);
+ }
+}
+
+template <class T, int s> int Mapping<T, s>::CalculateValue(uchar c, uchar n,
+ uchar* result) {
+ bool allow_caching = true;
+ int length = T::Convert(c, n, result, &allow_caching);
+ if (allow_caching) {
+ if (length == 1) {
+ entries_[c & kMask] = CacheEntry(c, result[0] - c);
+ return 1;
+ } else {
+ entries_[c & kMask] = CacheEntry(c, 0);
+ return 0;
+ }
+ } else {
+ return length;
+ }
+}
+
+
+uint16_t Latin1::ConvertNonLatin1ToLatin1(uint16_t c) {
+ ASSERT(c > Latin1::kMaxChar);
+ switch (c) {
+ // This are equivalent characters in unicode.
+ case 0x39c:
+ case 0x3bc:
+ return 0xb5;
+ // This is an uppercase of a Latin-1 character
+ // outside of Latin-1.
+ case 0x178:
+ return 0xff;
+ }
+ return 0;
+}
+
+
+unsigned Utf8::EncodeOneByte(char* str, uint8_t c) {
+ static const int kMask = ~(1 << 6);
+ if (c <= kMaxOneByteChar) {
+ str[0] = c;
+ return 1;
+ }
+ str[0] = 0xC0 | (c >> 6);
+ str[1] = 0x80 | (c & kMask);
+ return 2;
+}
+
+
+unsigned Utf8::Encode(char* str, uchar c, int previous) {
+ static const int kMask = ~(1 << 6);
+ if (c <= kMaxOneByteChar) {
+ str[0] = c;
+ return 1;
+ } else if (c <= kMaxTwoByteChar) {
+ str[0] = 0xC0 | (c >> 6);
+ str[1] = 0x80 | (c & kMask);
+ return 2;
+ } else if (c <= kMaxThreeByteChar) {
+ if (Utf16::IsTrailSurrogate(c) &&
+ Utf16::IsLeadSurrogate(previous)) {
+ const int kUnmatchedSize = kSizeOfUnmatchedSurrogate;
+ return Encode(str - kUnmatchedSize,
+ Utf16::CombineSurrogatePair(previous, c),
+ Utf16::kNoPreviousCharacter) - kUnmatchedSize;
+ }
+ str[0] = 0xE0 | (c >> 12);
+ str[1] = 0x80 | ((c >> 6) & kMask);
+ str[2] = 0x80 | (c & kMask);
+ return 3;
+ } else {
+ str[0] = 0xF0 | (c >> 18);
+ str[1] = 0x80 | ((c >> 12) & kMask);
+ str[2] = 0x80 | ((c >> 6) & kMask);
+ str[3] = 0x80 | (c & kMask);
+ return 4;
+ }
+}
+
+
+uchar Utf8::ValueOf(const byte* bytes, unsigned length, unsigned* cursor) {
+ if (length <= 0) return kBadChar;
+ byte first = bytes[0];
+ // Characters between 0000 and 0007F are encoded as a single character
+ if (first <= kMaxOneByteChar) {
+ *cursor += 1;
+ return first;
+ }
+ return CalculateValue(bytes, length, cursor);
+}
+
+unsigned Utf8::Length(uchar c, int previous) {
+ if (c <= kMaxOneByteChar) {
+ return 1;
+ } else if (c <= kMaxTwoByteChar) {
+ return 2;
+ } else if (c <= kMaxThreeByteChar) {
+ if (Utf16::IsTrailSurrogate(c) &&
+ Utf16::IsLeadSurrogate(previous)) {
+ return kSizeOfUnmatchedSurrogate - kBytesSavedByCombiningSurrogates;
+ }
+ return 3;
+ } else {
+ return 4;
+ }
+}
+
+Utf8DecoderBase::Utf8DecoderBase()
+ : unbuffered_start_(NULL),
+ utf16_length_(0),
+ last_byte_of_buffer_unused_(false) {}
+
+Utf8DecoderBase::Utf8DecoderBase(uint16_t* buffer,
+ unsigned buffer_length,
+ const uint8_t* stream,
+ unsigned stream_length) {
+ Reset(buffer, buffer_length, stream, stream_length);
+}
+
+template<unsigned kBufferSize>
+Utf8Decoder<kBufferSize>::Utf8Decoder(const char* stream, unsigned length)
+ : Utf8DecoderBase(buffer_,
+ kBufferSize,
+ reinterpret_cast<const uint8_t*>(stream),
+ length) {
+}
+
+template<unsigned kBufferSize>
+void Utf8Decoder<kBufferSize>::Reset(const char* stream, unsigned length) {
+ Utf8DecoderBase::Reset(buffer_,
+ kBufferSize,
+ reinterpret_cast<const uint8_t*>(stream),
+ length);
+}
+
+template <unsigned kBufferSize>
+unsigned Utf8Decoder<kBufferSize>::WriteUtf16(uint16_t* data,
+ unsigned length) const {
+ ASSERT(length > 0);
+ if (length > utf16_length_) length = utf16_length_;
+ // memcpy everything in buffer.
+ unsigned buffer_length =
+ last_byte_of_buffer_unused_ ? kBufferSize - 1 : kBufferSize;
+ unsigned memcpy_length = length <= buffer_length ? length : buffer_length;
+ v8::internal::OS::MemCopy(data, buffer_, memcpy_length*sizeof(uint16_t));
+ if (length <= buffer_length) return length;
+ ASSERT(unbuffered_start_ != NULL);
+ // Copy the rest the slow way.
+ WriteUtf16Slow(unbuffered_start_,
+ data + buffer_length,
+ length - buffer_length);
+ return length;
+}
+
+} // namespace unibrow
+
+#endif // V8_UNICODE_INL_H_
diff --git a/chromium/v8/src/unicode.cc b/chromium/v8/src/unicode.cc
new file mode 100644
index 00000000000..bd32467786f
--- /dev/null
+++ b/chromium/v8/src/unicode.cc
@@ -0,0 +1,1873 @@
+// Copyright 2012 the V8 project authors. 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 was generated at 2012-03-06 09:55:58.934483
+
+#include "unicode-inl.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace unibrow {
+
+static const int kStartBit = (1 << 30);
+static const int kChunkBits = (1 << 13);
+static const uchar kSentinel = static_cast<uchar>(-1);
+
+/**
+ * \file
+ * Implementations of functions for working with unicode.
+ */
+
+typedef signed short int16_t; // NOLINT
+typedef unsigned short uint16_t; // NOLINT
+typedef int int32_t; // NOLINT
+
+// All access to the character table should go through this function.
+template <int D>
+static inline uchar TableGet(const int32_t* table, int index) {
+ return table[D * index];
+}
+
+
+static inline uchar GetEntry(int32_t entry) {
+ return entry & (kStartBit - 1);
+}
+
+
+static inline bool IsStart(int32_t entry) {
+ return (entry & kStartBit) != 0;
+}
+
+
+/**
+ * Look up a character in the unicode table using a mix of binary and
+ * interpolation search. For a uniformly distributed array
+ * interpolation search beats binary search by a wide margin. However,
+ * in this case interpolation search degenerates because of some very
+ * high values in the lower end of the table so this function uses a
+ * combination. The average number of steps to look up the information
+ * about a character is around 10, slightly higher if there is no
+ * information available about the character.
+ */
+static bool LookupPredicate(const int32_t* table, uint16_t size, uchar chr) {
+ static const int kEntryDist = 1;
+ uint16_t value = chr & (kChunkBits - 1);
+ unsigned int low = 0;
+ unsigned int high = size - 1;
+ while (high != low) {
+ unsigned int mid = low + ((high - low) >> 1);
+ uchar current_value = GetEntry(TableGet<kEntryDist>(table, mid));
+ // If we've found an entry less than or equal to this one, and the
+ // next one is not also less than this one, we've arrived.
+ if ((current_value <= value) &&
+ (mid + 1 == size ||
+ GetEntry(TableGet<kEntryDist>(table, mid + 1)) > value)) {
+ low = mid;
+ break;
+ } else if (current_value < value) {
+ low = mid + 1;
+ } else if (current_value > value) {
+ // If we've just checked the bottom-most value and it's not
+ // the one we're looking for, we're done.
+ if (mid == 0) break;
+ high = mid - 1;
+ }
+ }
+ int32_t field = TableGet<kEntryDist>(table, low);
+ uchar entry = GetEntry(field);
+ bool is_start = IsStart(field);
+ return (entry == value) || (entry < value && is_start);
+}
+
+template <int kW>
+struct MultiCharacterSpecialCase {
+ static const uchar kEndOfEncoding = kSentinel;
+ uchar chars[kW];
+};
+
+
+// Look up the mapping for the given character in the specified table,
+// which is of the specified length and uses the specified special case
+// mapping for multi-char mappings. The next parameter is the character
+// following the one to map. The result will be written in to the result
+// buffer and the number of characters written will be returned. Finally,
+// if the allow_caching_ptr is non-null then false will be stored in
+// it if the result contains multiple characters or depends on the
+// context.
+// If ranges are linear, a match between a start and end point is
+// offset by the distance between the match and the start. Otherwise
+// the result is the same as for the start point on the entire range.
+template <bool ranges_are_linear, int kW>
+static int LookupMapping(const int32_t* table,
+ uint16_t size,
+ const MultiCharacterSpecialCase<kW>* multi_chars,
+ uchar chr,
+ uchar next,
+ uchar* result,
+ bool* allow_caching_ptr) {
+ static const int kEntryDist = 2;
+ uint16_t key = chr & (kChunkBits - 1);
+ uint16_t chunk_start = chr - key;
+ unsigned int low = 0;
+ unsigned int high = size - 1;
+ while (high != low) {
+ unsigned int mid = low + ((high - low) >> 1);
+ uchar current_value = GetEntry(TableGet<kEntryDist>(table, mid));
+ // If we've found an entry less than or equal to this one, and the next one
+ // is not also less than this one, we've arrived.
+ if ((current_value <= key) &&
+ (mid + 1 == size ||
+ GetEntry(TableGet<kEntryDist>(table, mid + 1)) > key)) {
+ low = mid;
+ break;
+ } else if (current_value < key) {
+ low = mid + 1;
+ } else if (current_value > key) {
+ // If we've just checked the bottom-most value and it's not
+ // the one we're looking for, we're done.
+ if (mid == 0) break;
+ high = mid - 1;
+ }
+ }
+ int32_t field = TableGet<kEntryDist>(table, low);
+ uchar entry = GetEntry(field);
+ bool is_start = IsStart(field);
+ bool found = (entry == key) || (entry < key && is_start);
+ if (found) {
+ int32_t value = table[2 * low + 1];
+ if (value == 0) {
+ // 0 means not present
+ return 0;
+ } else if ((value & 3) == 0) {
+ // Low bits 0 means a constant offset from the given character.
+ if (ranges_are_linear) {
+ result[0] = chr + (value >> 2);
+ } else {
+ result[0] = entry + chunk_start + (value >> 2);
+ }
+ return 1;
+ } else if ((value & 3) == 1) {
+ // Low bits 1 means a special case mapping
+ if (allow_caching_ptr) *allow_caching_ptr = false;
+ const MultiCharacterSpecialCase<kW>& mapping = multi_chars[value >> 2];
+ int length = 0;
+ for (length = 0; length < kW; length++) {
+ uchar mapped = mapping.chars[length];
+ if (mapped == MultiCharacterSpecialCase<kW>::kEndOfEncoding) break;
+ if (ranges_are_linear) {
+ result[length] = mapped + (key - entry);
+ } else {
+ result[length] = mapped;
+ }
+ }
+ return length;
+ } else {
+ // Low bits 2 means a really really special case
+ if (allow_caching_ptr) *allow_caching_ptr = false;
+ // The cases of this switch are defined in unicode.py in the
+ // really_special_cases mapping.
+ switch (value >> 2) {
+ case 1:
+ // Really special case 1: upper case sigma. This letter
+ // converts to two different lower case sigmas depending on
+ // whether or not it occurs at the end of a word.
+ if (next != 0 && Letter::Is(next)) {
+ result[0] = 0x03C3;
+ } else {
+ result[0] = 0x03C2;
+ }
+ return 1;
+ default:
+ return 0;
+ }
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+}
+
+uchar Utf8::CalculateValue(const byte* str,
+ unsigned length,
+ unsigned* cursor) {
+ // We only get called for non-ASCII characters.
+ if (length == 1) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ byte first = str[0];
+ byte second = str[1] ^ 0x80;
+ if (second & 0xC0) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ if (first < 0xE0) {
+ if (first < 0xC0) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ uchar code_point = ((first << 6) | second) & kMaxTwoByteChar;
+ if (code_point <= kMaxOneByteChar) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ *cursor += 2;
+ return code_point;
+ }
+ if (length == 2) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ byte third = str[2] ^ 0x80;
+ if (third & 0xC0) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ if (first < 0xF0) {
+ uchar code_point = ((((first << 6) | second) << 6) | third)
+ & kMaxThreeByteChar;
+ if (code_point <= kMaxTwoByteChar) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ *cursor += 3;
+ return code_point;
+ }
+ if (length == 3) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ byte fourth = str[3] ^ 0x80;
+ if (fourth & 0xC0) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ if (first < 0xF8) {
+ uchar code_point = (((((first << 6 | second) << 6) | third) << 6) | fourth)
+ & kMaxFourByteChar;
+ if (code_point <= kMaxThreeByteChar) {
+ *cursor += 1;
+ return kBadChar;
+ }
+ *cursor += 4;
+ return code_point;
+ }
+ *cursor += 1;
+ return kBadChar;
+}
+
+
+void Utf8DecoderBase::Reset(uint16_t* buffer,
+ unsigned buffer_length,
+ const uint8_t* stream,
+ unsigned stream_length) {
+ // Assume everything will fit in the buffer and stream won't be needed.
+ last_byte_of_buffer_unused_ = false;
+ unbuffered_start_ = NULL;
+ bool writing_to_buffer = true;
+ // Loop until stream is read, writing to buffer as long as buffer has space.
+ unsigned utf16_length = 0;
+ while (stream_length != 0) {
+ unsigned cursor = 0;
+ uint32_t character = Utf8::ValueOf(stream, stream_length, &cursor);
+ ASSERT(cursor > 0 && cursor <= stream_length);
+ stream += cursor;
+ stream_length -= cursor;
+ bool is_two_characters = character > Utf16::kMaxNonSurrogateCharCode;
+ utf16_length += is_two_characters ? 2 : 1;
+ // Don't need to write to the buffer, but still need utf16_length.
+ if (!writing_to_buffer) continue;
+ // Write out the characters to the buffer.
+ // Must check for equality with buffer_length as we've already updated it.
+ if (utf16_length <= buffer_length) {
+ if (is_two_characters) {
+ *buffer++ = Utf16::LeadSurrogate(character);
+ *buffer++ = Utf16::TrailSurrogate(character);
+ } else {
+ *buffer++ = character;
+ }
+ if (utf16_length == buffer_length) {
+ // Just wrote last character of buffer
+ writing_to_buffer = false;
+ unbuffered_start_ = stream;
+ }
+ continue;
+ }
+ // Have gone over buffer.
+ // Last char of buffer is unused, set cursor back.
+ ASSERT(is_two_characters);
+ writing_to_buffer = false;
+ last_byte_of_buffer_unused_ = true;
+ unbuffered_start_ = stream - cursor;
+ }
+ utf16_length_ = utf16_length;
+}
+
+
+void Utf8DecoderBase::WriteUtf16Slow(const uint8_t* stream,
+ uint16_t* data,
+ unsigned data_length) {
+ while (data_length != 0) {
+ unsigned cursor = 0;
+ uint32_t character = Utf8::ValueOf(stream, Utf8::kMaxEncodedSize, &cursor);
+ // There's a total lack of bounds checking for stream
+ // as it was already done in Reset.
+ stream += cursor;
+ if (character > unibrow::Utf16::kMaxNonSurrogateCharCode) {
+ *data++ = Utf16::LeadSurrogate(character);
+ *data++ = Utf16::TrailSurrogate(character);
+ ASSERT(data_length > 1);
+ data_length -= 2;
+ } else {
+ *data++ = character;
+ data_length -= 1;
+ }
+ }
+}
+
+
+// Uppercase: point.category == 'Lu'
+
+static const uint16_t kUppercaseTable0Size = 450;
+static const int32_t kUppercaseTable0[450] = {
+ 1073741889, 90, 1073742016, 214, 1073742040, 222, 256, 258, // NOLINT
+ 260, 262, 264, 266, 268, 270, 272, 274, // NOLINT
+ 276, 278, 280, 282, 284, 286, 288, 290, // NOLINT
+ 292, 294, 296, 298, 300, 302, 304, 306, // NOLINT
+ 308, 310, 313, 315, 317, 319, 321, 323, // NOLINT
+ 325, 327, 330, 332, 334, 336, 338, 340, // NOLINT
+ 342, 344, 346, 348, 350, 352, 354, 356, // NOLINT
+ 358, 360, 362, 364, 366, 368, 370, 372, // NOLINT
+ 374, 1073742200, 377, 379, 381, 1073742209, 386, 388, // NOLINT
+ 1073742214, 391, 1073742217, 395, 1073742222, 401, 1073742227, 404, // NOLINT
+ 1073742230, 408, 1073742236, 413, 1073742239, 416, 418, 420, // NOLINT
+ 1073742246, 423, 425, 428, 1073742254, 431, 1073742257, 435, // NOLINT
+ 437, 1073742263, 440, 444, 452, 455, 458, 461, // NOLINT
+ 463, 465, 467, 469, 471, 473, 475, 478, // NOLINT
+ 480, 482, 484, 486, 488, 490, 492, 494, // NOLINT
+ 497, 500, 1073742326, 504, 506, 508, 510, 512, // NOLINT
+ 514, 516, 518, 520, 522, 524, 526, 528, // NOLINT
+ 530, 532, 534, 536, 538, 540, 542, 544, // NOLINT
+ 546, 548, 550, 552, 554, 556, 558, 560, // NOLINT
+ 562, 1073742394, 571, 1073742397, 574, 577, 1073742403, 582, // NOLINT
+ 584, 586, 588, 590, 880, 882, 886, 902, // NOLINT
+ 1073742728, 906, 908, 1073742734, 911, 1073742737, 929, 1073742755, // NOLINT
+ 939, 975, 1073742802, 980, 984, 986, 988, 990, // NOLINT
+ 992, 994, 996, 998, 1000, 1002, 1004, 1006, // NOLINT
+ 1012, 1015, 1073742841, 1018, 1073742845, 1071, 1120, 1122, // NOLINT
+ 1124, 1126, 1128, 1130, 1132, 1134, 1136, 1138, // NOLINT
+ 1140, 1142, 1144, 1146, 1148, 1150, 1152, 1162, // NOLINT
+ 1164, 1166, 1168, 1170, 1172, 1174, 1176, 1178, // NOLINT
+ 1180, 1182, 1184, 1186, 1188, 1190, 1192, 1194, // NOLINT
+ 1196, 1198, 1200, 1202, 1204, 1206, 1208, 1210, // NOLINT
+ 1212, 1214, 1073743040, 1217, 1219, 1221, 1223, 1225, // NOLINT
+ 1227, 1229, 1232, 1234, 1236, 1238, 1240, 1242, // NOLINT
+ 1244, 1246, 1248, 1250, 1252, 1254, 1256, 1258, // NOLINT
+ 1260, 1262, 1264, 1266, 1268, 1270, 1272, 1274, // NOLINT
+ 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, // NOLINT
+ 1292, 1294, 1296, 1298, 1300, 1302, 1304, 1306, // NOLINT
+ 1308, 1310, 1312, 1314, 1316, 1318, 1073743153, 1366, // NOLINT
+ 1073746080, 4293, 4295, 4301, 7680, 7682, 7684, 7686, // NOLINT
+ 7688, 7690, 7692, 7694, 7696, 7698, 7700, 7702, // NOLINT
+ 7704, 7706, 7708, 7710, 7712, 7714, 7716, 7718, // NOLINT
+ 7720, 7722, 7724, 7726, 7728, 7730, 7732, 7734, // NOLINT
+ 7736, 7738, 7740, 7742, 7744, 7746, 7748, 7750, // NOLINT
+ 7752, 7754, 7756, 7758, 7760, 7762, 7764, 7766, // NOLINT
+ 7768, 7770, 7772, 7774, 7776, 7778, 7780, 7782, // NOLINT
+ 7784, 7786, 7788, 7790, 7792, 7794, 7796, 7798, // NOLINT
+ 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, // NOLINT
+ 7816, 7818, 7820, 7822, 7824, 7826, 7828, 7838, // NOLINT
+ 7840, 7842, 7844, 7846, 7848, 7850, 7852, 7854, // NOLINT
+ 7856, 7858, 7860, 7862, 7864, 7866, 7868, 7870, // NOLINT
+ 7872, 7874, 7876, 7878, 7880, 7882, 7884, 7886, // NOLINT
+ 7888, 7890, 7892, 7894, 7896, 7898, 7900, 7902, // NOLINT
+ 7904, 7906, 7908, 7910, 7912, 7914, 7916, 7918, // NOLINT
+ 7920, 7922, 7924, 7926, 7928, 7930, 7932, 7934, // NOLINT
+ 1073749768, 7951, 1073749784, 7965, 1073749800, 7983, 1073749816, 7999, // NOLINT
+ 1073749832, 8013, 8025, 8027, 8029, 8031, 1073749864, 8047, // NOLINT
+ 1073749944, 8123, 1073749960, 8139, 1073749976, 8155, 1073749992, 8172, // NOLINT
+ 1073750008, 8187 }; // NOLINT
+static const uint16_t kUppercaseTable1Size = 86;
+static const int32_t kUppercaseTable1[86] = {
+ 258, 263, 1073742091, 269, 1073742096, 274, 277, 1073742105, // NOLINT
+ 285, 292, 294, 296, 1073742122, 301, 1073742128, 307, // NOLINT
+ 1073742142, 319, 325, 387, 1073744896, 3118, 3168, 1073744994, // NOLINT
+ 3172, 3175, 3177, 3179, 1073745005, 3184, 3186, 3189, // NOLINT
+ 1073745022, 3200, 3202, 3204, 3206, 3208, 3210, 3212, // NOLINT
+ 3214, 3216, 3218, 3220, 3222, 3224, 3226, 3228, // NOLINT
+ 3230, 3232, 3234, 3236, 3238, 3240, 3242, 3244, // NOLINT
+ 3246, 3248, 3250, 3252, 3254, 3256, 3258, 3260, // NOLINT
+ 3262, 3264, 3266, 3268, 3270, 3272, 3274, 3276, // NOLINT
+ 3278, 3280, 3282, 3284, 3286, 3288, 3290, 3292, // NOLINT
+ 3294, 3296, 3298, 3307, 3309, 3314 }; // NOLINT
+static const uint16_t kUppercaseTable5Size = 91;
+static const int32_t kUppercaseTable5[91] = {
+ 1600, 1602, 1604, 1606, 1608, 1610, 1612, 1614, // NOLINT
+ 1616, 1618, 1620, 1622, 1624, 1626, 1628, 1630, // NOLINT
+ 1632, 1634, 1636, 1638, 1640, 1642, 1644, 1664, // NOLINT
+ 1666, 1668, 1670, 1672, 1674, 1676, 1678, 1680, // NOLINT
+ 1682, 1684, 1686, 1826, 1828, 1830, 1832, 1834, // NOLINT
+ 1836, 1838, 1842, 1844, 1846, 1848, 1850, 1852, // NOLINT
+ 1854, 1856, 1858, 1860, 1862, 1864, 1866, 1868, // NOLINT
+ 1870, 1872, 1874, 1876, 1878, 1880, 1882, 1884, // NOLINT
+ 1886, 1888, 1890, 1892, 1894, 1896, 1898, 1900, // NOLINT
+ 1902, 1913, 1915, 1073743741, 1918, 1920, 1922, 1924, // NOLINT
+ 1926, 1931, 1933, 1936, 1938, 1952, 1954, 1956, // NOLINT
+ 1958, 1960, 1962 }; // NOLINT
+static const uint16_t kUppercaseTable7Size = 2;
+static const int32_t kUppercaseTable7[2] = {
+ 1073749793, 7994 }; // NOLINT
+bool Uppercase::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kUppercaseTable0,
+ kUppercaseTable0Size,
+ c);
+ case 1: return LookupPredicate(kUppercaseTable1,
+ kUppercaseTable1Size,
+ c);
+ case 5: return LookupPredicate(kUppercaseTable5,
+ kUppercaseTable5Size,
+ c);
+ case 7: return LookupPredicate(kUppercaseTable7,
+ kUppercaseTable7Size,
+ c);
+ default: return false;
+ }
+}
+
+
+// Lowercase: point.category == 'Ll'
+
+static const uint16_t kLowercaseTable0Size = 463;
+static const int32_t kLowercaseTable0[463] = {
+ 1073741921, 122, 181, 1073742047, 246, 1073742072, 255, 257, // NOLINT
+ 259, 261, 263, 265, 267, 269, 271, 273, // NOLINT
+ 275, 277, 279, 281, 283, 285, 287, 289, // NOLINT
+ 291, 293, 295, 297, 299, 301, 303, 305, // NOLINT
+ 307, 309, 1073742135, 312, 314, 316, 318, 320, // NOLINT
+ 322, 324, 326, 1073742152, 329, 331, 333, 335, // NOLINT
+ 337, 339, 341, 343, 345, 347, 349, 351, // NOLINT
+ 353, 355, 357, 359, 361, 363, 365, 367, // NOLINT
+ 369, 371, 373, 375, 378, 380, 1073742206, 384, // NOLINT
+ 387, 389, 392, 1073742220, 397, 402, 405, 1073742233, // NOLINT
+ 411, 414, 417, 419, 421, 424, 1073742250, 427, // NOLINT
+ 429, 432, 436, 438, 1073742265, 442, 1073742269, 447, // NOLINT
+ 454, 457, 460, 462, 464, 466, 468, 470, // NOLINT
+ 472, 474, 1073742300, 477, 479, 481, 483, 485, // NOLINT
+ 487, 489, 491, 493, 1073742319, 496, 499, 501, // NOLINT
+ 505, 507, 509, 511, 513, 515, 517, 519, // NOLINT
+ 521, 523, 525, 527, 529, 531, 533, 535, // NOLINT
+ 537, 539, 541, 543, 545, 547, 549, 551, // NOLINT
+ 553, 555, 557, 559, 561, 1073742387, 569, 572, // NOLINT
+ 1073742399, 576, 578, 583, 585, 587, 589, 1073742415, // NOLINT
+ 659, 1073742485, 687, 881, 883, 887, 1073742715, 893, // NOLINT
+ 912, 1073742764, 974, 1073742800, 977, 1073742805, 983, 985, // NOLINT
+ 987, 989, 991, 993, 995, 997, 999, 1001, // NOLINT
+ 1003, 1005, 1073742831, 1011, 1013, 1016, 1073742843, 1020, // NOLINT
+ 1073742896, 1119, 1121, 1123, 1125, 1127, 1129, 1131, // NOLINT
+ 1133, 1135, 1137, 1139, 1141, 1143, 1145, 1147, // NOLINT
+ 1149, 1151, 1153, 1163, 1165, 1167, 1169, 1171, // NOLINT
+ 1173, 1175, 1177, 1179, 1181, 1183, 1185, 1187, // NOLINT
+ 1189, 1191, 1193, 1195, 1197, 1199, 1201, 1203, // NOLINT
+ 1205, 1207, 1209, 1211, 1213, 1215, 1218, 1220, // NOLINT
+ 1222, 1224, 1226, 1228, 1073743054, 1231, 1233, 1235, // NOLINT
+ 1237, 1239, 1241, 1243, 1245, 1247, 1249, 1251, // NOLINT
+ 1253, 1255, 1257, 1259, 1261, 1263, 1265, 1267, // NOLINT
+ 1269, 1271, 1273, 1275, 1277, 1279, 1281, 1283, // NOLINT
+ 1285, 1287, 1289, 1291, 1293, 1295, 1297, 1299, // NOLINT
+ 1301, 1303, 1305, 1307, 1309, 1311, 1313, 1315, // NOLINT
+ 1317, 1319, 1073743201, 1415, 1073749248, 7467, 1073749355, 7543, // NOLINT
+ 1073749369, 7578, 7681, 7683, 7685, 7687, 7689, 7691, // NOLINT
+ 7693, 7695, 7697, 7699, 7701, 7703, 7705, 7707, // NOLINT
+ 7709, 7711, 7713, 7715, 7717, 7719, 7721, 7723, // NOLINT
+ 7725, 7727, 7729, 7731, 7733, 7735, 7737, 7739, // NOLINT
+ 7741, 7743, 7745, 7747, 7749, 7751, 7753, 7755, // NOLINT
+ 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, // NOLINT
+ 7773, 7775, 7777, 7779, 7781, 7783, 7785, 7787, // NOLINT
+ 7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, // NOLINT
+ 7805, 7807, 7809, 7811, 7813, 7815, 7817, 7819, // NOLINT
+ 7821, 7823, 7825, 7827, 1073749653, 7837, 7839, 7841, // NOLINT
+ 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, // NOLINT
+ 7859, 7861, 7863, 7865, 7867, 7869, 7871, 7873, // NOLINT
+ 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, // NOLINT
+ 7891, 7893, 7895, 7897, 7899, 7901, 7903, 7905, // NOLINT
+ 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, // NOLINT
+ 7923, 7925, 7927, 7929, 7931, 7933, 1073749759, 7943, // NOLINT
+ 1073749776, 7957, 1073749792, 7975, 1073749808, 7991, 1073749824, 8005, // NOLINT
+ 1073749840, 8023, 1073749856, 8039, 1073749872, 8061, 1073749888, 8071, // NOLINT
+ 1073749904, 8087, 1073749920, 8103, 1073749936, 8116, 1073749942, 8119, // NOLINT
+ 8126, 1073749954, 8132, 1073749958, 8135, 1073749968, 8147, 1073749974, // NOLINT
+ 8151, 1073749984, 8167, 1073750002, 8180, 1073750006, 8183 }; // NOLINT
+static const uint16_t kLowercaseTable1Size = 84;
+static const int32_t kLowercaseTable1[84] = {
+ 266, 1073742094, 271, 275, 303, 308, 313, 1073742140, // NOLINT
+ 317, 1073742150, 329, 334, 388, 1073744944, 3166, 3169, // NOLINT
+ 1073744997, 3174, 3176, 3178, 3180, 3185, 1073745011, 3188, // NOLINT
+ 1073745014, 3195, 3201, 3203, 3205, 3207, 3209, 3211, // NOLINT
+ 3213, 3215, 3217, 3219, 3221, 3223, 3225, 3227, // NOLINT
+ 3229, 3231, 3233, 3235, 3237, 3239, 3241, 3243, // NOLINT
+ 3245, 3247, 3249, 3251, 3253, 3255, 3257, 3259, // NOLINT
+ 3261, 3263, 3265, 3267, 3269, 3271, 3273, 3275, // NOLINT
+ 3277, 3279, 3281, 3283, 3285, 3287, 3289, 3291, // NOLINT
+ 3293, 3295, 3297, 1073745123, 3300, 3308, 3310, 3315, // NOLINT
+ 1073745152, 3365, 3367, 3373 }; // NOLINT
+static const uint16_t kLowercaseTable5Size = 93;
+static const int32_t kLowercaseTable5[93] = {
+ 1601, 1603, 1605, 1607, 1609, 1611, 1613, 1615, // NOLINT
+ 1617, 1619, 1621, 1623, 1625, 1627, 1629, 1631, // NOLINT
+ 1633, 1635, 1637, 1639, 1641, 1643, 1645, 1665, // NOLINT
+ 1667, 1669, 1671, 1673, 1675, 1677, 1679, 1681, // NOLINT
+ 1683, 1685, 1687, 1827, 1829, 1831, 1833, 1835, // NOLINT
+ 1837, 1073743663, 1841, 1843, 1845, 1847, 1849, 1851, // NOLINT
+ 1853, 1855, 1857, 1859, 1861, 1863, 1865, 1867, // NOLINT
+ 1869, 1871, 1873, 1875, 1877, 1879, 1881, 1883, // NOLINT
+ 1885, 1887, 1889, 1891, 1893, 1895, 1897, 1899, // NOLINT
+ 1901, 1903, 1073743729, 1912, 1914, 1916, 1919, 1921, // NOLINT
+ 1923, 1925, 1927, 1932, 1934, 1937, 1939, 1953, // NOLINT
+ 1955, 1957, 1959, 1961, 2042 }; // NOLINT
+static const uint16_t kLowercaseTable7Size = 6;
+static const int32_t kLowercaseTable7[6] = {
+ 1073748736, 6918, 1073748755, 6935, 1073749825, 8026 }; // NOLINT
+bool Lowercase::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kLowercaseTable0,
+ kLowercaseTable0Size,
+ c);
+ case 1: return LookupPredicate(kLowercaseTable1,
+ kLowercaseTable1Size,
+ c);
+ case 5: return LookupPredicate(kLowercaseTable5,
+ kLowercaseTable5Size,
+ c);
+ case 7: return LookupPredicate(kLowercaseTable7,
+ kLowercaseTable7Size,
+ c);
+ default: return false;
+ }
+}
+
+
+// Letter: point.category in ['Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl' ]
+
+static const uint16_t kLetterTable0Size = 435;
+static const int32_t kLetterTable0[435] = {
+ 1073741889, 90, 1073741921, 122, 170, 181, 186, 1073742016, // NOLINT
+ 214, 1073742040, 246, 1073742072, 705, 1073742534, 721, 1073742560, // NOLINT
+ 740, 748, 750, 1073742704, 884, 1073742710, 887, 1073742714, // NOLINT
+ 893, 902, 1073742728, 906, 908, 1073742734, 929, 1073742755, // NOLINT
+ 1013, 1073742839, 1153, 1073742986, 1319, 1073743153, 1366, 1369, // NOLINT
+ 1073743201, 1415, 1073743312, 1514, 1073743344, 1522, 1073743392, 1610, // NOLINT
+ 1073743470, 1647, 1073743473, 1747, 1749, 1073743589, 1766, 1073743598, // NOLINT
+ 1775, 1073743610, 1788, 1791, 1808, 1073743634, 1839, 1073743693, // NOLINT
+ 1957, 1969, 1073743818, 2026, 1073743860, 2037, 2042, 1073743872, // NOLINT
+ 2069, 2074, 2084, 2088, 1073743936, 2136, 2208, 1073744034, // NOLINT
+ 2220, 1073744132, 2361, 2365, 2384, 1073744216, 2401, 1073744241, // NOLINT
+ 2423, 1073744249, 2431, 1073744261, 2444, 1073744271, 2448, 1073744275, // NOLINT
+ 2472, 1073744298, 2480, 2482, 1073744310, 2489, 2493, 2510, // NOLINT
+ 1073744348, 2525, 1073744351, 2529, 1073744368, 2545, 1073744389, 2570, // NOLINT
+ 1073744399, 2576, 1073744403, 2600, 1073744426, 2608, 1073744434, 2611, // NOLINT
+ 1073744437, 2614, 1073744440, 2617, 1073744473, 2652, 2654, 1073744498, // NOLINT
+ 2676, 1073744517, 2701, 1073744527, 2705, 1073744531, 2728, 1073744554, // NOLINT
+ 2736, 1073744562, 2739, 1073744565, 2745, 2749, 2768, 1073744608, // NOLINT
+ 2785, 1073744645, 2828, 1073744655, 2832, 1073744659, 2856, 1073744682, // NOLINT
+ 2864, 1073744690, 2867, 1073744693, 2873, 2877, 1073744732, 2909, // NOLINT
+ 1073744735, 2913, 2929, 2947, 1073744773, 2954, 1073744782, 2960, // NOLINT
+ 1073744786, 2965, 1073744793, 2970, 2972, 1073744798, 2975, 1073744803, // NOLINT
+ 2980, 1073744808, 2986, 1073744814, 3001, 3024, 1073744901, 3084, // NOLINT
+ 1073744910, 3088, 1073744914, 3112, 1073744938, 3123, 1073744949, 3129, // NOLINT
+ 3133, 1073744984, 3161, 1073744992, 3169, 1073745029, 3212, 1073745038, // NOLINT
+ 3216, 1073745042, 3240, 1073745066, 3251, 1073745077, 3257, 3261, // NOLINT
+ 3294, 1073745120, 3297, 1073745137, 3314, 1073745157, 3340, 1073745166, // NOLINT
+ 3344, 1073745170, 3386, 3389, 3406, 1073745248, 3425, 1073745274, // NOLINT
+ 3455, 1073745285, 3478, 1073745306, 3505, 1073745331, 3515, 3517, // NOLINT
+ 1073745344, 3526, 1073745409, 3632, 1073745458, 3635, 1073745472, 3654, // NOLINT
+ 1073745537, 3714, 3716, 1073745543, 3720, 3722, 3725, 1073745556, // NOLINT
+ 3735, 1073745561, 3743, 1073745569, 3747, 3749, 3751, 1073745578, // NOLINT
+ 3755, 1073745581, 3760, 1073745586, 3763, 3773, 1073745600, 3780, // NOLINT
+ 3782, 1073745628, 3807, 3840, 1073745728, 3911, 1073745737, 3948, // NOLINT
+ 1073745800, 3980, 1073745920, 4138, 4159, 1073746000, 4181, 1073746010, // NOLINT
+ 4189, 4193, 1073746021, 4198, 1073746030, 4208, 1073746037, 4225, // NOLINT
+ 4238, 1073746080, 4293, 4295, 4301, 1073746128, 4346, 1073746172, // NOLINT
+ 4680, 1073746506, 4685, 1073746512, 4694, 4696, 1073746522, 4701, // NOLINT
+ 1073746528, 4744, 1073746570, 4749, 1073746576, 4784, 1073746610, 4789, // NOLINT
+ 1073746616, 4798, 4800, 1073746626, 4805, 1073746632, 4822, 1073746648, // NOLINT
+ 4880, 1073746706, 4885, 1073746712, 4954, 1073746816, 5007, 1073746848, // NOLINT
+ 5108, 1073746945, 5740, 1073747567, 5759, 1073747585, 5786, 1073747616, // NOLINT
+ 5866, 1073747694, 5872, 1073747712, 5900, 1073747726, 5905, 1073747744, // NOLINT
+ 5937, 1073747776, 5969, 1073747808, 5996, 1073747822, 6000, 1073747840, // NOLINT
+ 6067, 6103, 6108, 1073748000, 6263, 1073748096, 6312, 6314, // NOLINT
+ 1073748144, 6389, 1073748224, 6428, 1073748304, 6509, 1073748336, 6516, // NOLINT
+ 1073748352, 6571, 1073748417, 6599, 1073748480, 6678, 1073748512, 6740, // NOLINT
+ 6823, 1073748741, 6963, 1073748805, 6987, 1073748867, 7072, 1073748910, // NOLINT
+ 7087, 1073748922, 7141, 1073748992, 7203, 1073749069, 7247, 1073749082, // NOLINT
+ 7293, 1073749225, 7404, 1073749230, 7409, 1073749237, 7414, 1073749248, // NOLINT
+ 7615, 1073749504, 7957, 1073749784, 7965, 1073749792, 8005, 1073749832, // NOLINT
+ 8013, 1073749840, 8023, 8025, 8027, 8029, 1073749855, 8061, // NOLINT
+ 1073749888, 8116, 1073749942, 8124, 8126, 1073749954, 8132, 1073749958, // NOLINT
+ 8140, 1073749968, 8147, 1073749974, 8155, 1073749984, 8172, 1073750002, // NOLINT
+ 8180, 1073750006, 8188 }; // NOLINT
+static const uint16_t kLetterTable1Size = 87;
+static const int32_t kLetterTable1[87] = {
+ 113, 127, 1073741968, 156, 258, 263, 1073742090, 275, // NOLINT
+ 277, 1073742105, 285, 292, 294, 296, 1073742122, 301, // NOLINT
+ 1073742127, 313, 1073742140, 319, 1073742149, 329, 334, 1073742176, // NOLINT
+ 392, 1073744896, 3118, 1073744944, 3166, 1073744992, 3300, 1073745131, // NOLINT
+ 3310, 1073745138, 3315, 1073745152, 3365, 3367, 3373, 1073745200, // NOLINT
+ 3431, 3439, 1073745280, 3478, 1073745312, 3494, 1073745320, 3502, // NOLINT
+ 1073745328, 3510, 1073745336, 3518, 1073745344, 3526, 1073745352, 3534, // NOLINT
+ 1073745360, 3542, 1073745368, 3550, 3631, 1073745925, 4103, 1073745953, // NOLINT
+ 4137, 1073745969, 4149, 1073745976, 4156, 1073745985, 4246, 1073746077, // NOLINT
+ 4255, 1073746081, 4346, 1073746172, 4351, 1073746181, 4397, 1073746225, // NOLINT
+ 4494, 1073746336, 4538, 1073746416, 4607, 1073746944, 8191 }; // NOLINT
+static const uint16_t kLetterTable2Size = 4;
+static const int32_t kLetterTable2[4] = {
+ 1073741824, 3509, 1073745408, 8191 }; // NOLINT
+static const uint16_t kLetterTable3Size = 2;
+static const int32_t kLetterTable3[2] = {
+ 1073741824, 8191 }; // NOLINT
+static const uint16_t kLetterTable4Size = 2;
+static const int32_t kLetterTable4[2] = {
+ 1073741824, 8140 }; // NOLINT
+static const uint16_t kLetterTable5Size = 88;
+static const int32_t kLetterTable5[88] = {
+ 1073741824, 1164, 1073743056, 1277, 1073743104, 1548, 1073743376, 1567, // NOLINT
+ 1073743402, 1579, 1073743424, 1646, 1073743487, 1687, 1073743520, 1775, // NOLINT
+ 1073743639, 1823, 1073743650, 1928, 1073743755, 1934, 1073743760, 1939, // NOLINT
+ 1073743776, 1962, 1073743864, 2049, 1073743875, 2053, 1073743879, 2058, // NOLINT
+ 1073743884, 2082, 1073743936, 2163, 1073744002, 2227, 1073744114, 2295, // NOLINT
+ 2299, 1073744138, 2341, 1073744176, 2374, 1073744224, 2428, 1073744260, // NOLINT
+ 2482, 2511, 1073744384, 2600, 1073744448, 2626, 1073744452, 2635, // NOLINT
+ 1073744480, 2678, 2682, 1073744512, 2735, 2737, 1073744565, 2742, // NOLINT
+ 1073744569, 2749, 2752, 2754, 1073744603, 2781, 1073744608, 2794, // NOLINT
+ 1073744626, 2804, 1073744641, 2822, 1073744649, 2830, 1073744657, 2838, // NOLINT
+ 1073744672, 2854, 1073744680, 2862, 1073744832, 3042, 1073744896, 8191 }; // NOLINT
+static const uint16_t kLetterTable6Size = 6;
+static const int32_t kLetterTable6[6] = {
+ 1073741824, 6051, 1073747888, 6086, 1073747915, 6139 }; // NOLINT
+static const uint16_t kLetterTable7Size = 48;
+static const int32_t kLetterTable7[48] = {
+ 1073748224, 6765, 1073748592, 6873, 1073748736, 6918, 1073748755, 6935, // NOLINT
+ 6941, 1073748767, 6952, 1073748778, 6966, 1073748792, 6972, 6974, // NOLINT
+ 1073748800, 6977, 1073748803, 6980, 1073748806, 7089, 1073748947, 7485, // NOLINT
+ 1073749328, 7567, 1073749394, 7623, 1073749488, 7675, 1073749616, 7796, // NOLINT
+ 1073749622, 7932, 1073749793, 7994, 1073749825, 8026, 1073749862, 8126, // NOLINT
+ 1073749954, 8135, 1073749962, 8143, 1073749970, 8151, 1073749978, 8156 }; // NOLINT
+bool Letter::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kLetterTable0,
+ kLetterTable0Size,
+ c);
+ case 1: return LookupPredicate(kLetterTable1,
+ kLetterTable1Size,
+ c);
+ case 2: return LookupPredicate(kLetterTable2,
+ kLetterTable2Size,
+ c);
+ case 3: return LookupPredicate(kLetterTable3,
+ kLetterTable3Size,
+ c);
+ case 4: return LookupPredicate(kLetterTable4,
+ kLetterTable4Size,
+ c);
+ case 5: return LookupPredicate(kLetterTable5,
+ kLetterTable5Size,
+ c);
+ case 6: return LookupPredicate(kLetterTable6,
+ kLetterTable6Size,
+ c);
+ case 7: return LookupPredicate(kLetterTable7,
+ kLetterTable7Size,
+ c);
+ default: return false;
+ }
+}
+
+
+// Space: point.category == 'Zs'
+
+static const uint16_t kSpaceTable0Size = 4;
+static const int32_t kSpaceTable0[4] = {
+ 32, 160, 5760, 6158 }; // NOLINT
+static const uint16_t kSpaceTable1Size = 5;
+static const int32_t kSpaceTable1[5] = {
+ 1073741824, 10, 47, 95, 4096 }; // NOLINT
+bool Space::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kSpaceTable0,
+ kSpaceTable0Size,
+ c);
+ case 1: return LookupPredicate(kSpaceTable1,
+ kSpaceTable1Size,
+ c);
+ default: return false;
+ }
+}
+
+
+// Number: point.category == 'Nd'
+
+static const uint16_t kNumberTable0Size = 56;
+static const int32_t kNumberTable0[56] = {
+ 1073741872, 57, 1073743456, 1641, 1073743600, 1785, 1073743808, 1993, // NOLINT
+ 1073744230, 2415, 1073744358, 2543, 1073744486, 2671, 1073744614, 2799, // NOLINT
+ 1073744742, 2927, 1073744870, 3055, 1073744998, 3183, 1073745126, 3311, // NOLINT
+ 1073745254, 3439, 1073745488, 3673, 1073745616, 3801, 1073745696, 3881, // NOLINT
+ 1073745984, 4169, 1073746064, 4249, 1073747936, 6121, 1073747984, 6169, // NOLINT
+ 1073748294, 6479, 1073748432, 6617, 1073748608, 6793, 1073748624, 6809, // NOLINT
+ 1073748816, 7001, 1073748912, 7097, 1073749056, 7241, 1073749072, 7257 }; // NOLINT
+static const uint16_t kNumberTable5Size = 12;
+static const int32_t kNumberTable5[12] = {
+ 1073743392, 1577, 1073744080, 2265, 1073744128, 2313, 1073744336, 2521, // NOLINT
+ 1073744464, 2649, 1073744880, 3065 }; // NOLINT
+static const uint16_t kNumberTable7Size = 2;
+static const int32_t kNumberTable7[2] = {
+ 1073749776, 7961 }; // NOLINT
+bool Number::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kNumberTable0,
+ kNumberTable0Size,
+ c);
+ case 5: return LookupPredicate(kNumberTable5,
+ kNumberTable5Size,
+ c);
+ case 7: return LookupPredicate(kNumberTable7,
+ kNumberTable7Size,
+ c);
+ default: return false;
+ }
+}
+
+
+// WhiteSpace: 'Ws' in point.properties
+
+static const uint16_t kWhiteSpaceTable0Size = 7;
+static const int32_t kWhiteSpaceTable0[7] = {
+ 1073741833, 13, 32, 133, 160, 5760, 6158 }; // NOLINT
+static const uint16_t kWhiteSpaceTable1Size = 7;
+static const int32_t kWhiteSpaceTable1[7] = {
+ 1073741824, 10, 1073741864, 41, 47, 95, 4096 }; // NOLINT
+bool WhiteSpace::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kWhiteSpaceTable0,
+ kWhiteSpaceTable0Size,
+ c);
+ case 1: return LookupPredicate(kWhiteSpaceTable1,
+ kWhiteSpaceTable1Size,
+ c);
+ default: return false;
+ }
+}
+
+
+// LineTerminator: 'Lt' in point.properties
+
+static const uint16_t kLineTerminatorTable0Size = 2;
+static const int32_t kLineTerminatorTable0[2] = {
+ 10, 13 }; // NOLINT
+static const uint16_t kLineTerminatorTable1Size = 2;
+static const int32_t kLineTerminatorTable1[2] = {
+ 1073741864, 41 }; // NOLINT
+bool LineTerminator::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kLineTerminatorTable0,
+ kLineTerminatorTable0Size,
+ c);
+ case 1: return LookupPredicate(kLineTerminatorTable1,
+ kLineTerminatorTable1Size,
+ c);
+ default: return false;
+ }
+}
+
+
+// CombiningMark: point.category in ['Mn', 'Mc']
+
+static const uint16_t kCombiningMarkTable0Size = 258;
+static const int32_t kCombiningMarkTable0[258] = {
+ 1073742592, 879, 1073742979, 1159, 1073743249, 1469, 1471, 1073743297, // NOLINT
+ 1474, 1073743300, 1477, 1479, 1073743376, 1562, 1073743435, 1631, // NOLINT
+ 1648, 1073743574, 1756, 1073743583, 1764, 1073743591, 1768, 1073743594, // NOLINT
+ 1773, 1809, 1073743664, 1866, 1073743782, 1968, 1073743851, 2035, // NOLINT
+ 1073743894, 2073, 1073743899, 2083, 1073743909, 2087, 1073743913, 2093, // NOLINT
+ 1073743961, 2139, 1073744100, 2302, 1073744128, 2307, 1073744186, 2364, // NOLINT
+ 1073744190, 2383, 1073744209, 2391, 1073744226, 2403, 1073744257, 2435, // NOLINT
+ 2492, 1073744318, 2500, 1073744327, 2504, 1073744331, 2509, 2519, // NOLINT
+ 1073744354, 2531, 1073744385, 2563, 2620, 1073744446, 2626, 1073744455, // NOLINT
+ 2632, 1073744459, 2637, 2641, 1073744496, 2673, 2677, 1073744513, // NOLINT
+ 2691, 2748, 1073744574, 2757, 1073744583, 2761, 1073744587, 2765, // NOLINT
+ 1073744610, 2787, 1073744641, 2819, 2876, 1073744702, 2884, 1073744711, // NOLINT
+ 2888, 1073744715, 2893, 1073744726, 2903, 1073744738, 2915, 2946, // NOLINT
+ 1073744830, 3010, 1073744838, 3016, 1073744842, 3021, 3031, 1073744897, // NOLINT
+ 3075, 1073744958, 3140, 1073744966, 3144, 1073744970, 3149, 1073744981, // NOLINT
+ 3158, 1073744994, 3171, 1073745026, 3203, 3260, 1073745086, 3268, // NOLINT
+ 1073745094, 3272, 1073745098, 3277, 1073745109, 3286, 1073745122, 3299, // NOLINT
+ 1073745154, 3331, 1073745214, 3396, 1073745222, 3400, 1073745226, 3405, // NOLINT
+ 3415, 1073745250, 3427, 1073745282, 3459, 3530, 1073745359, 3540, // NOLINT
+ 3542, 1073745368, 3551, 1073745394, 3571, 3633, 1073745460, 3642, // NOLINT
+ 1073745479, 3662, 3761, 1073745588, 3769, 1073745595, 3772, 1073745608, // NOLINT
+ 3789, 1073745688, 3865, 3893, 3895, 3897, 1073745726, 3903, // NOLINT
+ 1073745777, 3972, 1073745798, 3975, 1073745805, 3991, 1073745817, 4028, // NOLINT
+ 4038, 1073745963, 4158, 1073746006, 4185, 1073746014, 4192, 1073746018, // NOLINT
+ 4196, 1073746023, 4205, 1073746033, 4212, 1073746050, 4237, 4239, // NOLINT
+ 1073746074, 4253, 1073746781, 4959, 1073747730, 5908, 1073747762, 5940, // NOLINT
+ 1073747794, 5971, 1073747826, 6003, 1073747892, 6099, 6109, 1073747979, // NOLINT
+ 6157, 6313, 1073748256, 6443, 1073748272, 6459, 1073748400, 6592, // NOLINT
+ 1073748424, 6601, 1073748503, 6683, 1073748565, 6750, 1073748576, 6780, // NOLINT
+ 6783, 1073748736, 6916, 1073748788, 6980, 1073748843, 7027, 1073748864, // NOLINT
+ 7042, 1073748897, 7085, 1073748966, 7155, 1073749028, 7223, 1073749200, // NOLINT
+ 7378, 1073749204, 7400, 7405, 1073749234, 7412, 1073749440, 7654, // NOLINT
+ 1073749500, 7679 }; // NOLINT
+static const uint16_t kCombiningMarkTable1Size = 14;
+static const int32_t kCombiningMarkTable1[14] = {
+ 1073742032, 220, 225, 1073742053, 240, 1073745135, 3313, 3455, // NOLINT
+ 1073745376, 3583, 1073745962, 4143, 1073746073, 4250 }; // NOLINT
+static const uint16_t kCombiningMarkTable5Size = 47;
+static const int32_t kCombiningMarkTable5[47] = {
+ 1647, 1073743476, 1661, 1695, 1073743600, 1777, 2050, 2054, // NOLINT
+ 2059, 1073743907, 2087, 1073744000, 2177, 1073744052, 2244, 1073744096, // NOLINT
+ 2289, 1073744166, 2349, 1073744199, 2387, 1073744256, 2435, 1073744307, // NOLINT
+ 2496, 1073744425, 2614, 2627, 1073744460, 2637, 2683, 2736, // NOLINT
+ 1073744562, 2740, 1073744567, 2744, 1073744574, 2751, 2753, 1073744619, // NOLINT
+ 2799, 1073744629, 2806, 1073744867, 3050, 1073744876, 3053 }; // NOLINT
+static const uint16_t kCombiningMarkTable7Size = 5;
+static const int32_t kCombiningMarkTable7[5] = {
+ 6942, 1073749504, 7695, 1073749536, 7718 }; // NOLINT
+bool CombiningMark::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kCombiningMarkTable0,
+ kCombiningMarkTable0Size,
+ c);
+ case 1: return LookupPredicate(kCombiningMarkTable1,
+ kCombiningMarkTable1Size,
+ c);
+ case 5: return LookupPredicate(kCombiningMarkTable5,
+ kCombiningMarkTable5Size,
+ c);
+ case 7: return LookupPredicate(kCombiningMarkTable7,
+ kCombiningMarkTable7Size,
+ c);
+ default: return false;
+ }
+}
+
+
+// ConnectorPunctuation: point.category == 'Pc'
+
+static const uint16_t kConnectorPunctuationTable0Size = 1;
+static const int32_t kConnectorPunctuationTable0[1] = {
+ 95 }; // NOLINT
+static const uint16_t kConnectorPunctuationTable1Size = 3;
+static const int32_t kConnectorPunctuationTable1[3] = {
+ 1073741887, 64, 84 }; // NOLINT
+static const uint16_t kConnectorPunctuationTable7Size = 5;
+static const int32_t kConnectorPunctuationTable7[5] = {
+ 1073749555, 7732, 1073749581, 7759, 7999 }; // NOLINT
+bool ConnectorPunctuation::Is(uchar c) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupPredicate(kConnectorPunctuationTable0,
+ kConnectorPunctuationTable0Size,
+ c);
+ case 1: return LookupPredicate(kConnectorPunctuationTable1,
+ kConnectorPunctuationTable1Size,
+ c);
+ case 7: return LookupPredicate(kConnectorPunctuationTable7,
+ kConnectorPunctuationTable7Size,
+ c);
+ default: return false;
+ }
+}
+
+static const MultiCharacterSpecialCase<2> kToLowercaseMultiStrings0[2] = { // NOLINT
+ {{105, 775}}, {{kSentinel}} }; // NOLINT
+static const uint16_t kToLowercaseTable0Size = 483; // NOLINT
+static const int32_t kToLowercaseTable0[966] = {
+ 1073741889, 128, 90, 128, 1073742016, 128, 214, 128, 1073742040, 128, 222, 128, 256, 4, 258, 4, // NOLINT
+ 260, 4, 262, 4, 264, 4, 266, 4, 268, 4, 270, 4, 272, 4, 274, 4, // NOLINT
+ 276, 4, 278, 4, 280, 4, 282, 4, 284, 4, 286, 4, 288, 4, 290, 4, // NOLINT
+ 292, 4, 294, 4, 296, 4, 298, 4, 300, 4, 302, 4, 304, 1, 306, 4, // NOLINT
+ 308, 4, 310, 4, 313, 4, 315, 4, 317, 4, 319, 4, 321, 4, 323, 4, // NOLINT
+ 325, 4, 327, 4, 330, 4, 332, 4, 334, 4, 336, 4, 338, 4, 340, 4, // NOLINT
+ 342, 4, 344, 4, 346, 4, 348, 4, 350, 4, 352, 4, 354, 4, 356, 4, // NOLINT
+ 358, 4, 360, 4, 362, 4, 364, 4, 366, 4, 368, 4, 370, 4, 372, 4, // NOLINT
+ 374, 4, 376, -484, 377, 4, 379, 4, 381, 4, 385, 840, 386, 4, 388, 4, // NOLINT
+ 390, 824, 391, 4, 1073742217, 820, 394, 820, 395, 4, 398, 316, 399, 808, 400, 812, // NOLINT
+ 401, 4, 403, 820, 404, 828, 406, 844, 407, 836, 408, 4, 412, 844, 413, 852, // NOLINT
+ 415, 856, 416, 4, 418, 4, 420, 4, 422, 872, 423, 4, 425, 872, 428, 4, // NOLINT
+ 430, 872, 431, 4, 1073742257, 868, 434, 868, 435, 4, 437, 4, 439, 876, 440, 4, // NOLINT
+ 444, 4, 452, 8, 453, 4, 455, 8, 456, 4, 458, 8, 459, 4, 461, 4, // NOLINT
+ 463, 4, 465, 4, 467, 4, 469, 4, 471, 4, 473, 4, 475, 4, 478, 4, // NOLINT
+ 480, 4, 482, 4, 484, 4, 486, 4, 488, 4, 490, 4, 492, 4, 494, 4, // NOLINT
+ 497, 8, 498, 4, 500, 4, 502, -388, 503, -224, 504, 4, 506, 4, 508, 4, // NOLINT
+ 510, 4, 512, 4, 514, 4, 516, 4, 518, 4, 520, 4, 522, 4, 524, 4, // NOLINT
+ 526, 4, 528, 4, 530, 4, 532, 4, 534, 4, 536, 4, 538, 4, 540, 4, // NOLINT
+ 542, 4, 544, -520, 546, 4, 548, 4, 550, 4, 552, 4, 554, 4, 556, 4, // NOLINT
+ 558, 4, 560, 4, 562, 4, 570, 43180, 571, 4, 573, -652, 574, 43168, 577, 4, // NOLINT
+ 579, -780, 580, 276, 581, 284, 582, 4, 584, 4, 586, 4, 588, 4, 590, 4, // NOLINT
+ 880, 4, 882, 4, 886, 4, 902, 152, 1073742728, 148, 906, 148, 908, 256, 1073742734, 252, // NOLINT
+ 911, 252, 1073742737, 128, 929, 128, 931, 6, 1073742756, 128, 939, 128, 975, 32, 984, 4, // NOLINT
+ 986, 4, 988, 4, 990, 4, 992, 4, 994, 4, 996, 4, 998, 4, 1000, 4, // NOLINT
+ 1002, 4, 1004, 4, 1006, 4, 1012, -240, 1015, 4, 1017, -28, 1018, 4, 1073742845, -520, // NOLINT
+ 1023, -520, 1073742848, 320, 1039, 320, 1073742864, 128, 1071, 128, 1120, 4, 1122, 4, 1124, 4, // NOLINT
+ 1126, 4, 1128, 4, 1130, 4, 1132, 4, 1134, 4, 1136, 4, 1138, 4, 1140, 4, // NOLINT
+ 1142, 4, 1144, 4, 1146, 4, 1148, 4, 1150, 4, 1152, 4, 1162, 4, 1164, 4, // NOLINT
+ 1166, 4, 1168, 4, 1170, 4, 1172, 4, 1174, 4, 1176, 4, 1178, 4, 1180, 4, // NOLINT
+ 1182, 4, 1184, 4, 1186, 4, 1188, 4, 1190, 4, 1192, 4, 1194, 4, 1196, 4, // NOLINT
+ 1198, 4, 1200, 4, 1202, 4, 1204, 4, 1206, 4, 1208, 4, 1210, 4, 1212, 4, // NOLINT
+ 1214, 4, 1216, 60, 1217, 4, 1219, 4, 1221, 4, 1223, 4, 1225, 4, 1227, 4, // NOLINT
+ 1229, 4, 1232, 4, 1234, 4, 1236, 4, 1238, 4, 1240, 4, 1242, 4, 1244, 4, // NOLINT
+ 1246, 4, 1248, 4, 1250, 4, 1252, 4, 1254, 4, 1256, 4, 1258, 4, 1260, 4, // NOLINT
+ 1262, 4, 1264, 4, 1266, 4, 1268, 4, 1270, 4, 1272, 4, 1274, 4, 1276, 4, // NOLINT
+ 1278, 4, 1280, 4, 1282, 4, 1284, 4, 1286, 4, 1288, 4, 1290, 4, 1292, 4, // NOLINT
+ 1294, 4, 1296, 4, 1298, 4, 1300, 4, 1302, 4, 1304, 4, 1306, 4, 1308, 4, // NOLINT
+ 1310, 4, 1312, 4, 1314, 4, 1316, 4, 1318, 4, 1073743153, 192, 1366, 192, 1073746080, 29056, // NOLINT
+ 4293, 29056, 4295, 29056, 4301, 29056, 7680, 4, 7682, 4, 7684, 4, 7686, 4, 7688, 4, // NOLINT
+ 7690, 4, 7692, 4, 7694, 4, 7696, 4, 7698, 4, 7700, 4, 7702, 4, 7704, 4, // NOLINT
+ 7706, 4, 7708, 4, 7710, 4, 7712, 4, 7714, 4, 7716, 4, 7718, 4, 7720, 4, // NOLINT
+ 7722, 4, 7724, 4, 7726, 4, 7728, 4, 7730, 4, 7732, 4, 7734, 4, 7736, 4, // NOLINT
+ 7738, 4, 7740, 4, 7742, 4, 7744, 4, 7746, 4, 7748, 4, 7750, 4, 7752, 4, // NOLINT
+ 7754, 4, 7756, 4, 7758, 4, 7760, 4, 7762, 4, 7764, 4, 7766, 4, 7768, 4, // NOLINT
+ 7770, 4, 7772, 4, 7774, 4, 7776, 4, 7778, 4, 7780, 4, 7782, 4, 7784, 4, // NOLINT
+ 7786, 4, 7788, 4, 7790, 4, 7792, 4, 7794, 4, 7796, 4, 7798, 4, 7800, 4, // NOLINT
+ 7802, 4, 7804, 4, 7806, 4, 7808, 4, 7810, 4, 7812, 4, 7814, 4, 7816, 4, // NOLINT
+ 7818, 4, 7820, 4, 7822, 4, 7824, 4, 7826, 4, 7828, 4, 7838, -30460, 7840, 4, // NOLINT
+ 7842, 4, 7844, 4, 7846, 4, 7848, 4, 7850, 4, 7852, 4, 7854, 4, 7856, 4, // NOLINT
+ 7858, 4, 7860, 4, 7862, 4, 7864, 4, 7866, 4, 7868, 4, 7870, 4, 7872, 4, // NOLINT
+ 7874, 4, 7876, 4, 7878, 4, 7880, 4, 7882, 4, 7884, 4, 7886, 4, 7888, 4, // NOLINT
+ 7890, 4, 7892, 4, 7894, 4, 7896, 4, 7898, 4, 7900, 4, 7902, 4, 7904, 4, // NOLINT
+ 7906, 4, 7908, 4, 7910, 4, 7912, 4, 7914, 4, 7916, 4, 7918, 4, 7920, 4, // NOLINT
+ 7922, 4, 7924, 4, 7926, 4, 7928, 4, 7930, 4, 7932, 4, 7934, 4, 1073749768, -32, // NOLINT
+ 7951, -32, 1073749784, -32, 7965, -32, 1073749800, -32, 7983, -32, 1073749816, -32, 7999, -32, 1073749832, -32, // NOLINT
+ 8013, -32, 8025, -32, 8027, -32, 8029, -32, 8031, -32, 1073749864, -32, 8047, -32, 1073749896, -32, // NOLINT
+ 8079, -32, 1073749912, -32, 8095, -32, 1073749928, -32, 8111, -32, 1073749944, -32, 8121, -32, 1073749946, -296, // NOLINT
+ 8123, -296, 8124, -36, 1073749960, -344, 8139, -344, 8140, -36, 1073749976, -32, 8153, -32, 1073749978, -400, // NOLINT
+ 8155, -400, 1073749992, -32, 8169, -32, 1073749994, -448, 8171, -448, 8172, -28, 1073750008, -512, 8185, -512, // NOLINT
+ 1073750010, -504, 8187, -504, 8188, -36 }; // NOLINT
+static const uint16_t kToLowercaseMultiStrings0Size = 2; // NOLINT
+static const MultiCharacterSpecialCase<1> kToLowercaseMultiStrings1[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kToLowercaseTable1Size = 79; // NOLINT
+static const int32_t kToLowercaseTable1[158] = {
+ 294, -30068, 298, -33532, 299, -33048, 306, 112, 1073742176, 64, 367, 64, 387, 4, 1073743030, 104, // NOLINT
+ 1231, 104, 1073744896, 192, 3118, 192, 3168, 4, 3170, -42972, 3171, -15256, 3172, -42908, 3175, 4, // NOLINT
+ 3177, 4, 3179, 4, 3181, -43120, 3182, -42996, 3183, -43132, 3184, -43128, 3186, 4, 3189, 4, // NOLINT
+ 1073745022, -43260, 3199, -43260, 3200, 4, 3202, 4, 3204, 4, 3206, 4, 3208, 4, 3210, 4, // NOLINT
+ 3212, 4, 3214, 4, 3216, 4, 3218, 4, 3220, 4, 3222, 4, 3224, 4, 3226, 4, // NOLINT
+ 3228, 4, 3230, 4, 3232, 4, 3234, 4, 3236, 4, 3238, 4, 3240, 4, 3242, 4, // NOLINT
+ 3244, 4, 3246, 4, 3248, 4, 3250, 4, 3252, 4, 3254, 4, 3256, 4, 3258, 4, // NOLINT
+ 3260, 4, 3262, 4, 3264, 4, 3266, 4, 3268, 4, 3270, 4, 3272, 4, 3274, 4, // NOLINT
+ 3276, 4, 3278, 4, 3280, 4, 3282, 4, 3284, 4, 3286, 4, 3288, 4, 3290, 4, // NOLINT
+ 3292, 4, 3294, 4, 3296, 4, 3298, 4, 3307, 4, 3309, 4, 3314, 4 }; // NOLINT
+static const uint16_t kToLowercaseMultiStrings1Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<1> kToLowercaseMultiStrings5[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kToLowercaseTable5Size = 91; // NOLINT
+static const int32_t kToLowercaseTable5[182] = {
+ 1600, 4, 1602, 4, 1604, 4, 1606, 4, 1608, 4, 1610, 4, 1612, 4, 1614, 4, // NOLINT
+ 1616, 4, 1618, 4, 1620, 4, 1622, 4, 1624, 4, 1626, 4, 1628, 4, 1630, 4, // NOLINT
+ 1632, 4, 1634, 4, 1636, 4, 1638, 4, 1640, 4, 1642, 4, 1644, 4, 1664, 4, // NOLINT
+ 1666, 4, 1668, 4, 1670, 4, 1672, 4, 1674, 4, 1676, 4, 1678, 4, 1680, 4, // NOLINT
+ 1682, 4, 1684, 4, 1686, 4, 1826, 4, 1828, 4, 1830, 4, 1832, 4, 1834, 4, // NOLINT
+ 1836, 4, 1838, 4, 1842, 4, 1844, 4, 1846, 4, 1848, 4, 1850, 4, 1852, 4, // NOLINT
+ 1854, 4, 1856, 4, 1858, 4, 1860, 4, 1862, 4, 1864, 4, 1866, 4, 1868, 4, // NOLINT
+ 1870, 4, 1872, 4, 1874, 4, 1876, 4, 1878, 4, 1880, 4, 1882, 4, 1884, 4, // NOLINT
+ 1886, 4, 1888, 4, 1890, 4, 1892, 4, 1894, 4, 1896, 4, 1898, 4, 1900, 4, // NOLINT
+ 1902, 4, 1913, 4, 1915, 4, 1917, -141328, 1918, 4, 1920, 4, 1922, 4, 1924, 4, // NOLINT
+ 1926, 4, 1931, 4, 1933, -169120, 1936, 4, 1938, 4, 1952, 4, 1954, 4, 1956, 4, // NOLINT
+ 1958, 4, 1960, 4, 1962, -169232 }; // NOLINT
+static const uint16_t kToLowercaseMultiStrings5Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<1> kToLowercaseMultiStrings7[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kToLowercaseTable7Size = 2; // NOLINT
+static const int32_t kToLowercaseTable7[4] = {
+ 1073749793, 128, 7994, 128 }; // NOLINT
+static const uint16_t kToLowercaseMultiStrings7Size = 1; // NOLINT
+int ToLowercase::Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupMapping<true>(kToLowercaseTable0,
+ kToLowercaseTable0Size,
+ kToLowercaseMultiStrings0,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 1: return LookupMapping<true>(kToLowercaseTable1,
+ kToLowercaseTable1Size,
+ kToLowercaseMultiStrings1,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 5: return LookupMapping<true>(kToLowercaseTable5,
+ kToLowercaseTable5Size,
+ kToLowercaseMultiStrings5,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 7: return LookupMapping<true>(kToLowercaseTable7,
+ kToLowercaseTable7Size,
+ kToLowercaseMultiStrings7,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ default: return 0;
+ }
+}
+
+static const MultiCharacterSpecialCase<3> kToUppercaseMultiStrings0[62] = { // NOLINT
+ {{83, 83, kSentinel}}, {{700, 78, kSentinel}}, {{74, 780, kSentinel}}, {{921, 776, 769}}, // NOLINT
+ {{933, 776, 769}}, {{1333, 1362, kSentinel}}, {{72, 817, kSentinel}}, {{84, 776, kSentinel}}, // NOLINT
+ {{87, 778, kSentinel}}, {{89, 778, kSentinel}}, {{65, 702, kSentinel}}, {{933, 787, kSentinel}}, // NOLINT
+ {{933, 787, 768}}, {{933, 787, 769}}, {{933, 787, 834}}, {{7944, 921, kSentinel}}, // NOLINT
+ {{7945, 921, kSentinel}}, {{7946, 921, kSentinel}}, {{7947, 921, kSentinel}}, {{7948, 921, kSentinel}}, // NOLINT
+ {{7949, 921, kSentinel}}, {{7950, 921, kSentinel}}, {{7951, 921, kSentinel}}, {{7976, 921, kSentinel}}, // NOLINT
+ {{7977, 921, kSentinel}}, {{7978, 921, kSentinel}}, {{7979, 921, kSentinel}}, {{7980, 921, kSentinel}}, // NOLINT
+ {{7981, 921, kSentinel}}, {{7982, 921, kSentinel}}, {{7983, 921, kSentinel}}, {{8040, 921, kSentinel}}, // NOLINT
+ {{8041, 921, kSentinel}}, {{8042, 921, kSentinel}}, {{8043, 921, kSentinel}}, {{8044, 921, kSentinel}}, // NOLINT
+ {{8045, 921, kSentinel}}, {{8046, 921, kSentinel}}, {{8047, 921, kSentinel}}, {{8122, 921, kSentinel}}, // NOLINT
+ {{913, 921, kSentinel}}, {{902, 921, kSentinel}}, {{913, 834, kSentinel}}, {{913, 834, 921}}, // NOLINT
+ {{8138, 921, kSentinel}}, {{919, 921, kSentinel}}, {{905, 921, kSentinel}}, {{919, 834, kSentinel}}, // NOLINT
+ {{919, 834, 921}}, {{921, 776, 768}}, {{921, 834, kSentinel}}, {{921, 776, 834}}, // NOLINT
+ {{933, 776, 768}}, {{929, 787, kSentinel}}, {{933, 834, kSentinel}}, {{933, 776, 834}}, // NOLINT
+ {{8186, 921, kSentinel}}, {{937, 921, kSentinel}}, {{911, 921, kSentinel}}, {{937, 834, kSentinel}}, // NOLINT
+ {{937, 834, 921}}, {{kSentinel}} }; // NOLINT
+static const uint16_t kToUppercaseTable0Size = 580; // NOLINT
+static const int32_t kToUppercaseTable0[1160] = {
+ 1073741921, -128, 122, -128, 181, 2972, 223, 1, 1073742048, -128, 246, -128, 1073742072, -128, 254, -128, // NOLINT
+ 255, 484, 257, -4, 259, -4, 261, -4, 263, -4, 265, -4, 267, -4, 269, -4, // NOLINT
+ 271, -4, 273, -4, 275, -4, 277, -4, 279, -4, 281, -4, 283, -4, 285, -4, // NOLINT
+ 287, -4, 289, -4, 291, -4, 293, -4, 295, -4, 297, -4, 299, -4, 301, -4, // NOLINT
+ 303, -4, 305, -928, 307, -4, 309, -4, 311, -4, 314, -4, 316, -4, 318, -4, // NOLINT
+ 320, -4, 322, -4, 324, -4, 326, -4, 328, -4, 329, 5, 331, -4, 333, -4, // NOLINT
+ 335, -4, 337, -4, 339, -4, 341, -4, 343, -4, 345, -4, 347, -4, 349, -4, // NOLINT
+ 351, -4, 353, -4, 355, -4, 357, -4, 359, -4, 361, -4, 363, -4, 365, -4, // NOLINT
+ 367, -4, 369, -4, 371, -4, 373, -4, 375, -4, 378, -4, 380, -4, 382, -4, // NOLINT
+ 383, -1200, 384, 780, 387, -4, 389, -4, 392, -4, 396, -4, 402, -4, 405, 388, // NOLINT
+ 409, -4, 410, 652, 414, 520, 417, -4, 419, -4, 421, -4, 424, -4, 429, -4, // NOLINT
+ 432, -4, 436, -4, 438, -4, 441, -4, 445, -4, 447, 224, 453, -4, 454, -8, // NOLINT
+ 456, -4, 457, -8, 459, -4, 460, -8, 462, -4, 464, -4, 466, -4, 468, -4, // NOLINT
+ 470, -4, 472, -4, 474, -4, 476, -4, 477, -316, 479, -4, 481, -4, 483, -4, // NOLINT
+ 485, -4, 487, -4, 489, -4, 491, -4, 493, -4, 495, -4, 496, 9, 498, -4, // NOLINT
+ 499, -8, 501, -4, 505, -4, 507, -4, 509, -4, 511, -4, 513, -4, 515, -4, // NOLINT
+ 517, -4, 519, -4, 521, -4, 523, -4, 525, -4, 527, -4, 529, -4, 531, -4, // NOLINT
+ 533, -4, 535, -4, 537, -4, 539, -4, 541, -4, 543, -4, 547, -4, 549, -4, // NOLINT
+ 551, -4, 553, -4, 555, -4, 557, -4, 559, -4, 561, -4, 563, -4, 572, -4, // NOLINT
+ 1073742399, 43260, 576, 43260, 578, -4, 583, -4, 585, -4, 587, -4, 589, -4, 591, -4, // NOLINT
+ 592, 43132, 593, 43120, 594, 43128, 595, -840, 596, -824, 1073742422, -820, 599, -820, 601, -808, // NOLINT
+ 603, -812, 608, -820, 611, -828, 613, 169120, 614, 169232, 616, -836, 617, -844, 619, 42972, // NOLINT
+ 623, -844, 625, 42996, 626, -852, 629, -856, 637, 42908, 640, -872, 643, -872, 648, -872, // NOLINT
+ 649, -276, 1073742474, -868, 651, -868, 652, -284, 658, -876, 837, 336, 881, -4, 883, -4, // NOLINT
+ 887, -4, 1073742715, 520, 893, 520, 912, 13, 940, -152, 1073742765, -148, 943, -148, 944, 17, // NOLINT
+ 1073742769, -128, 961, -128, 962, -124, 1073742787, -128, 971, -128, 972, -256, 1073742797, -252, 974, -252, // NOLINT
+ 976, -248, 977, -228, 981, -188, 982, -216, 983, -32, 985, -4, 987, -4, 989, -4, // NOLINT
+ 991, -4, 993, -4, 995, -4, 997, -4, 999, -4, 1001, -4, 1003, -4, 1005, -4, // NOLINT
+ 1007, -4, 1008, -344, 1009, -320, 1010, 28, 1013, -384, 1016, -4, 1019, -4, 1073742896, -128, // NOLINT
+ 1103, -128, 1073742928, -320, 1119, -320, 1121, -4, 1123, -4, 1125, -4, 1127, -4, 1129, -4, // NOLINT
+ 1131, -4, 1133, -4, 1135, -4, 1137, -4, 1139, -4, 1141, -4, 1143, -4, 1145, -4, // NOLINT
+ 1147, -4, 1149, -4, 1151, -4, 1153, -4, 1163, -4, 1165, -4, 1167, -4, 1169, -4, // NOLINT
+ 1171, -4, 1173, -4, 1175, -4, 1177, -4, 1179, -4, 1181, -4, 1183, -4, 1185, -4, // NOLINT
+ 1187, -4, 1189, -4, 1191, -4, 1193, -4, 1195, -4, 1197, -4, 1199, -4, 1201, -4, // NOLINT
+ 1203, -4, 1205, -4, 1207, -4, 1209, -4, 1211, -4, 1213, -4, 1215, -4, 1218, -4, // NOLINT
+ 1220, -4, 1222, -4, 1224, -4, 1226, -4, 1228, -4, 1230, -4, 1231, -60, 1233, -4, // NOLINT
+ 1235, -4, 1237, -4, 1239, -4, 1241, -4, 1243, -4, 1245, -4, 1247, -4, 1249, -4, // NOLINT
+ 1251, -4, 1253, -4, 1255, -4, 1257, -4, 1259, -4, 1261, -4, 1263, -4, 1265, -4, // NOLINT
+ 1267, -4, 1269, -4, 1271, -4, 1273, -4, 1275, -4, 1277, -4, 1279, -4, 1281, -4, // NOLINT
+ 1283, -4, 1285, -4, 1287, -4, 1289, -4, 1291, -4, 1293, -4, 1295, -4, 1297, -4, // NOLINT
+ 1299, -4, 1301, -4, 1303, -4, 1305, -4, 1307, -4, 1309, -4, 1311, -4, 1313, -4, // NOLINT
+ 1315, -4, 1317, -4, 1319, -4, 1073743201, -192, 1414, -192, 1415, 21, 7545, 141328, 7549, 15256, // NOLINT
+ 7681, -4, 7683, -4, 7685, -4, 7687, -4, 7689, -4, 7691, -4, 7693, -4, 7695, -4, // NOLINT
+ 7697, -4, 7699, -4, 7701, -4, 7703, -4, 7705, -4, 7707, -4, 7709, -4, 7711, -4, // NOLINT
+ 7713, -4, 7715, -4, 7717, -4, 7719, -4, 7721, -4, 7723, -4, 7725, -4, 7727, -4, // NOLINT
+ 7729, -4, 7731, -4, 7733, -4, 7735, -4, 7737, -4, 7739, -4, 7741, -4, 7743, -4, // NOLINT
+ 7745, -4, 7747, -4, 7749, -4, 7751, -4, 7753, -4, 7755, -4, 7757, -4, 7759, -4, // NOLINT
+ 7761, -4, 7763, -4, 7765, -4, 7767, -4, 7769, -4, 7771, -4, 7773, -4, 7775, -4, // NOLINT
+ 7777, -4, 7779, -4, 7781, -4, 7783, -4, 7785, -4, 7787, -4, 7789, -4, 7791, -4, // NOLINT
+ 7793, -4, 7795, -4, 7797, -4, 7799, -4, 7801, -4, 7803, -4, 7805, -4, 7807, -4, // NOLINT
+ 7809, -4, 7811, -4, 7813, -4, 7815, -4, 7817, -4, 7819, -4, 7821, -4, 7823, -4, // NOLINT
+ 7825, -4, 7827, -4, 7829, -4, 7830, 25, 7831, 29, 7832, 33, 7833, 37, 7834, 41, // NOLINT
+ 7835, -236, 7841, -4, 7843, -4, 7845, -4, 7847, -4, 7849, -4, 7851, -4, 7853, -4, // NOLINT
+ 7855, -4, 7857, -4, 7859, -4, 7861, -4, 7863, -4, 7865, -4, 7867, -4, 7869, -4, // NOLINT
+ 7871, -4, 7873, -4, 7875, -4, 7877, -4, 7879, -4, 7881, -4, 7883, -4, 7885, -4, // NOLINT
+ 7887, -4, 7889, -4, 7891, -4, 7893, -4, 7895, -4, 7897, -4, 7899, -4, 7901, -4, // NOLINT
+ 7903, -4, 7905, -4, 7907, -4, 7909, -4, 7911, -4, 7913, -4, 7915, -4, 7917, -4, // NOLINT
+ 7919, -4, 7921, -4, 7923, -4, 7925, -4, 7927, -4, 7929, -4, 7931, -4, 7933, -4, // NOLINT
+ 7935, -4, 1073749760, 32, 7943, 32, 1073749776, 32, 7957, 32, 1073749792, 32, 7975, 32, 1073749808, 32, // NOLINT
+ 7991, 32, 1073749824, 32, 8005, 32, 8016, 45, 8017, 32, 8018, 49, 8019, 32, 8020, 53, // NOLINT
+ 8021, 32, 8022, 57, 8023, 32, 1073749856, 32, 8039, 32, 1073749872, 296, 8049, 296, 1073749874, 344, // NOLINT
+ 8053, 344, 1073749878, 400, 8055, 400, 1073749880, 512, 8057, 512, 1073749882, 448, 8059, 448, 1073749884, 504, // NOLINT
+ 8061, 504, 8064, 61, 8065, 65, 8066, 69, 8067, 73, 8068, 77, 8069, 81, 8070, 85, // NOLINT
+ 8071, 89, 8072, 61, 8073, 65, 8074, 69, 8075, 73, 8076, 77, 8077, 81, 8078, 85, // NOLINT
+ 8079, 89, 8080, 93, 8081, 97, 8082, 101, 8083, 105, 8084, 109, 8085, 113, 8086, 117, // NOLINT
+ 8087, 121, 8088, 93, 8089, 97, 8090, 101, 8091, 105, 8092, 109, 8093, 113, 8094, 117, // NOLINT
+ 8095, 121, 8096, 125, 8097, 129, 8098, 133, 8099, 137, 8100, 141, 8101, 145, 8102, 149, // NOLINT
+ 8103, 153, 8104, 125, 8105, 129, 8106, 133, 8107, 137, 8108, 141, 8109, 145, 8110, 149, // NOLINT
+ 8111, 153, 1073749936, 32, 8113, 32, 8114, 157, 8115, 161, 8116, 165, 8118, 169, 8119, 173, // NOLINT
+ 8124, 161, 8126, -28820, 8130, 177, 8131, 181, 8132, 185, 8134, 189, 8135, 193, 8140, 181, // NOLINT
+ 1073749968, 32, 8145, 32, 8146, 197, 8147, 13, 8150, 201, 8151, 205, 1073749984, 32, 8161, 32, // NOLINT
+ 8162, 209, 8163, 17, 8164, 213, 8165, 28, 8166, 217, 8167, 221, 8178, 225, 8179, 229, // NOLINT
+ 8180, 233, 8182, 237, 8183, 241, 8188, 229 }; // NOLINT
+static const uint16_t kToUppercaseMultiStrings0Size = 62; // NOLINT
+static const MultiCharacterSpecialCase<1> kToUppercaseMultiStrings1[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kToUppercaseTable1Size = 73; // NOLINT
+static const int32_t kToUppercaseTable1[146] = {
+ 334, -112, 1073742192, -64, 383, -64, 388, -4, 1073743056, -104, 1257, -104, 1073744944, -192, 3166, -192, // NOLINT
+ 3169, -4, 3173, -43180, 3174, -43168, 3176, -4, 3178, -4, 3180, -4, 3187, -4, 3190, -4, // NOLINT
+ 3201, -4, 3203, -4, 3205, -4, 3207, -4, 3209, -4, 3211, -4, 3213, -4, 3215, -4, // NOLINT
+ 3217, -4, 3219, -4, 3221, -4, 3223, -4, 3225, -4, 3227, -4, 3229, -4, 3231, -4, // NOLINT
+ 3233, -4, 3235, -4, 3237, -4, 3239, -4, 3241, -4, 3243, -4, 3245, -4, 3247, -4, // NOLINT
+ 3249, -4, 3251, -4, 3253, -4, 3255, -4, 3257, -4, 3259, -4, 3261, -4, 3263, -4, // NOLINT
+ 3265, -4, 3267, -4, 3269, -4, 3271, -4, 3273, -4, 3275, -4, 3277, -4, 3279, -4, // NOLINT
+ 3281, -4, 3283, -4, 3285, -4, 3287, -4, 3289, -4, 3291, -4, 3293, -4, 3295, -4, // NOLINT
+ 3297, -4, 3299, -4, 3308, -4, 3310, -4, 3315, -4, 1073745152, -29056, 3365, -29056, 3367, -29056, // NOLINT
+ 3373, -29056 }; // NOLINT
+static const uint16_t kToUppercaseMultiStrings1Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<1> kToUppercaseMultiStrings5[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kToUppercaseTable5Size = 88; // NOLINT
+static const int32_t kToUppercaseTable5[176] = {
+ 1601, -4, 1603, -4, 1605, -4, 1607, -4, 1609, -4, 1611, -4, 1613, -4, 1615, -4, // NOLINT
+ 1617, -4, 1619, -4, 1621, -4, 1623, -4, 1625, -4, 1627, -4, 1629, -4, 1631, -4, // NOLINT
+ 1633, -4, 1635, -4, 1637, -4, 1639, -4, 1641, -4, 1643, -4, 1645, -4, 1665, -4, // NOLINT
+ 1667, -4, 1669, -4, 1671, -4, 1673, -4, 1675, -4, 1677, -4, 1679, -4, 1681, -4, // NOLINT
+ 1683, -4, 1685, -4, 1687, -4, 1827, -4, 1829, -4, 1831, -4, 1833, -4, 1835, -4, // NOLINT
+ 1837, -4, 1839, -4, 1843, -4, 1845, -4, 1847, -4, 1849, -4, 1851, -4, 1853, -4, // NOLINT
+ 1855, -4, 1857, -4, 1859, -4, 1861, -4, 1863, -4, 1865, -4, 1867, -4, 1869, -4, // NOLINT
+ 1871, -4, 1873, -4, 1875, -4, 1877, -4, 1879, -4, 1881, -4, 1883, -4, 1885, -4, // NOLINT
+ 1887, -4, 1889, -4, 1891, -4, 1893, -4, 1895, -4, 1897, -4, 1899, -4, 1901, -4, // NOLINT
+ 1903, -4, 1914, -4, 1916, -4, 1919, -4, 1921, -4, 1923, -4, 1925, -4, 1927, -4, // NOLINT
+ 1932, -4, 1937, -4, 1939, -4, 1953, -4, 1955, -4, 1957, -4, 1959, -4, 1961, -4 }; // NOLINT
+static const uint16_t kToUppercaseMultiStrings5Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<3> kToUppercaseMultiStrings7[12] = { // NOLINT
+ {{70, 70, kSentinel}}, {{70, 73, kSentinel}}, {{70, 76, kSentinel}}, {{70, 70, 73}}, // NOLINT
+ {{70, 70, 76}}, {{83, 84, kSentinel}}, {{1348, 1350, kSentinel}}, {{1348, 1333, kSentinel}}, // NOLINT
+ {{1348, 1339, kSentinel}}, {{1358, 1350, kSentinel}}, {{1348, 1341, kSentinel}}, {{kSentinel}} }; // NOLINT
+static const uint16_t kToUppercaseTable7Size = 14; // NOLINT
+static const int32_t kToUppercaseTable7[28] = {
+ 6912, 1, 6913, 5, 6914, 9, 6915, 13, 6916, 17, 6917, 21, 6918, 21, 6931, 25, // NOLINT
+ 6932, 29, 6933, 33, 6934, 37, 6935, 41, 1073749825, -128, 8026, -128 }; // NOLINT
+static const uint16_t kToUppercaseMultiStrings7Size = 12; // NOLINT
+int ToUppercase::Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupMapping<true>(kToUppercaseTable0,
+ kToUppercaseTable0Size,
+ kToUppercaseMultiStrings0,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 1: return LookupMapping<true>(kToUppercaseTable1,
+ kToUppercaseTable1Size,
+ kToUppercaseMultiStrings1,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 5: return LookupMapping<true>(kToUppercaseTable5,
+ kToUppercaseTable5Size,
+ kToUppercaseMultiStrings5,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 7: return LookupMapping<true>(kToUppercaseTable7,
+ kToUppercaseTable7Size,
+ kToUppercaseMultiStrings7,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ default: return 0;
+ }
+}
+
+static const MultiCharacterSpecialCase<1> kEcma262CanonicalizeMultiStrings0[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kEcma262CanonicalizeTable0Size = 488; // NOLINT
+static const int32_t kEcma262CanonicalizeTable0[976] = {
+ 1073741921, -128, 122, -128, 181, 2972, 1073742048, -128, 246, -128, 1073742072, -128, 254, -128, 255, 484, // NOLINT
+ 257, -4, 259, -4, 261, -4, 263, -4, 265, -4, 267, -4, 269, -4, 271, -4, // NOLINT
+ 273, -4, 275, -4, 277, -4, 279, -4, 281, -4, 283, -4, 285, -4, 287, -4, // NOLINT
+ 289, -4, 291, -4, 293, -4, 295, -4, 297, -4, 299, -4, 301, -4, 303, -4, // NOLINT
+ 307, -4, 309, -4, 311, -4, 314, -4, 316, -4, 318, -4, 320, -4, 322, -4, // NOLINT
+ 324, -4, 326, -4, 328, -4, 331, -4, 333, -4, 335, -4, 337, -4, 339, -4, // NOLINT
+ 341, -4, 343, -4, 345, -4, 347, -4, 349, -4, 351, -4, 353, -4, 355, -4, // NOLINT
+ 357, -4, 359, -4, 361, -4, 363, -4, 365, -4, 367, -4, 369, -4, 371, -4, // NOLINT
+ 373, -4, 375, -4, 378, -4, 380, -4, 382, -4, 384, 780, 387, -4, 389, -4, // NOLINT
+ 392, -4, 396, -4, 402, -4, 405, 388, 409, -4, 410, 652, 414, 520, 417, -4, // NOLINT
+ 419, -4, 421, -4, 424, -4, 429, -4, 432, -4, 436, -4, 438, -4, 441, -4, // NOLINT
+ 445, -4, 447, 224, 453, -4, 454, -8, 456, -4, 457, -8, 459, -4, 460, -8, // NOLINT
+ 462, -4, 464, -4, 466, -4, 468, -4, 470, -4, 472, -4, 474, -4, 476, -4, // NOLINT
+ 477, -316, 479, -4, 481, -4, 483, -4, 485, -4, 487, -4, 489, -4, 491, -4, // NOLINT
+ 493, -4, 495, -4, 498, -4, 499, -8, 501, -4, 505, -4, 507, -4, 509, -4, // NOLINT
+ 511, -4, 513, -4, 515, -4, 517, -4, 519, -4, 521, -4, 523, -4, 525, -4, // NOLINT
+ 527, -4, 529, -4, 531, -4, 533, -4, 535, -4, 537, -4, 539, -4, 541, -4, // NOLINT
+ 543, -4, 547, -4, 549, -4, 551, -4, 553, -4, 555, -4, 557, -4, 559, -4, // NOLINT
+ 561, -4, 563, -4, 572, -4, 1073742399, 43260, 576, 43260, 578, -4, 583, -4, 585, -4, // NOLINT
+ 587, -4, 589, -4, 591, -4, 592, 43132, 593, 43120, 594, 43128, 595, -840, 596, -824, // NOLINT
+ 1073742422, -820, 599, -820, 601, -808, 603, -812, 608, -820, 611, -828, 613, 169120, 614, 169232, // NOLINT
+ 616, -836, 617, -844, 619, 42972, 623, -844, 625, 42996, 626, -852, 629, -856, 637, 42908, // NOLINT
+ 640, -872, 643, -872, 648, -872, 649, -276, 1073742474, -868, 651, -868, 652, -284, 658, -876, // NOLINT
+ 837, 336, 881, -4, 883, -4, 887, -4, 1073742715, 520, 893, 520, 940, -152, 1073742765, -148, // NOLINT
+ 943, -148, 1073742769, -128, 961, -128, 962, -124, 1073742787, -128, 971, -128, 972, -256, 1073742797, -252, // NOLINT
+ 974, -252, 976, -248, 977, -228, 981, -188, 982, -216, 983, -32, 985, -4, 987, -4, // NOLINT
+ 989, -4, 991, -4, 993, -4, 995, -4, 997, -4, 999, -4, 1001, -4, 1003, -4, // NOLINT
+ 1005, -4, 1007, -4, 1008, -344, 1009, -320, 1010, 28, 1013, -384, 1016, -4, 1019, -4, // NOLINT
+ 1073742896, -128, 1103, -128, 1073742928, -320, 1119, -320, 1121, -4, 1123, -4, 1125, -4, 1127, -4, // NOLINT
+ 1129, -4, 1131, -4, 1133, -4, 1135, -4, 1137, -4, 1139, -4, 1141, -4, 1143, -4, // NOLINT
+ 1145, -4, 1147, -4, 1149, -4, 1151, -4, 1153, -4, 1163, -4, 1165, -4, 1167, -4, // NOLINT
+ 1169, -4, 1171, -4, 1173, -4, 1175, -4, 1177, -4, 1179, -4, 1181, -4, 1183, -4, // NOLINT
+ 1185, -4, 1187, -4, 1189, -4, 1191, -4, 1193, -4, 1195, -4, 1197, -4, 1199, -4, // NOLINT
+ 1201, -4, 1203, -4, 1205, -4, 1207, -4, 1209, -4, 1211, -4, 1213, -4, 1215, -4, // NOLINT
+ 1218, -4, 1220, -4, 1222, -4, 1224, -4, 1226, -4, 1228, -4, 1230, -4, 1231, -60, // NOLINT
+ 1233, -4, 1235, -4, 1237, -4, 1239, -4, 1241, -4, 1243, -4, 1245, -4, 1247, -4, // NOLINT
+ 1249, -4, 1251, -4, 1253, -4, 1255, -4, 1257, -4, 1259, -4, 1261, -4, 1263, -4, // NOLINT
+ 1265, -4, 1267, -4, 1269, -4, 1271, -4, 1273, -4, 1275, -4, 1277, -4, 1279, -4, // NOLINT
+ 1281, -4, 1283, -4, 1285, -4, 1287, -4, 1289, -4, 1291, -4, 1293, -4, 1295, -4, // NOLINT
+ 1297, -4, 1299, -4, 1301, -4, 1303, -4, 1305, -4, 1307, -4, 1309, -4, 1311, -4, // NOLINT
+ 1313, -4, 1315, -4, 1317, -4, 1319, -4, 1073743201, -192, 1414, -192, 7545, 141328, 7549, 15256, // NOLINT
+ 7681, -4, 7683, -4, 7685, -4, 7687, -4, 7689, -4, 7691, -4, 7693, -4, 7695, -4, // NOLINT
+ 7697, -4, 7699, -4, 7701, -4, 7703, -4, 7705, -4, 7707, -4, 7709, -4, 7711, -4, // NOLINT
+ 7713, -4, 7715, -4, 7717, -4, 7719, -4, 7721, -4, 7723, -4, 7725, -4, 7727, -4, // NOLINT
+ 7729, -4, 7731, -4, 7733, -4, 7735, -4, 7737, -4, 7739, -4, 7741, -4, 7743, -4, // NOLINT
+ 7745, -4, 7747, -4, 7749, -4, 7751, -4, 7753, -4, 7755, -4, 7757, -4, 7759, -4, // NOLINT
+ 7761, -4, 7763, -4, 7765, -4, 7767, -4, 7769, -4, 7771, -4, 7773, -4, 7775, -4, // NOLINT
+ 7777, -4, 7779, -4, 7781, -4, 7783, -4, 7785, -4, 7787, -4, 7789, -4, 7791, -4, // NOLINT
+ 7793, -4, 7795, -4, 7797, -4, 7799, -4, 7801, -4, 7803, -4, 7805, -4, 7807, -4, // NOLINT
+ 7809, -4, 7811, -4, 7813, -4, 7815, -4, 7817, -4, 7819, -4, 7821, -4, 7823, -4, // NOLINT
+ 7825, -4, 7827, -4, 7829, -4, 7835, -236, 7841, -4, 7843, -4, 7845, -4, 7847, -4, // NOLINT
+ 7849, -4, 7851, -4, 7853, -4, 7855, -4, 7857, -4, 7859, -4, 7861, -4, 7863, -4, // NOLINT
+ 7865, -4, 7867, -4, 7869, -4, 7871, -4, 7873, -4, 7875, -4, 7877, -4, 7879, -4, // NOLINT
+ 7881, -4, 7883, -4, 7885, -4, 7887, -4, 7889, -4, 7891, -4, 7893, -4, 7895, -4, // NOLINT
+ 7897, -4, 7899, -4, 7901, -4, 7903, -4, 7905, -4, 7907, -4, 7909, -4, 7911, -4, // NOLINT
+ 7913, -4, 7915, -4, 7917, -4, 7919, -4, 7921, -4, 7923, -4, 7925, -4, 7927, -4, // NOLINT
+ 7929, -4, 7931, -4, 7933, -4, 7935, -4, 1073749760, 32, 7943, 32, 1073749776, 32, 7957, 32, // NOLINT
+ 1073749792, 32, 7975, 32, 1073749808, 32, 7991, 32, 1073749824, 32, 8005, 32, 8017, 32, 8019, 32, // NOLINT
+ 8021, 32, 8023, 32, 1073749856, 32, 8039, 32, 1073749872, 296, 8049, 296, 1073749874, 344, 8053, 344, // NOLINT
+ 1073749878, 400, 8055, 400, 1073749880, 512, 8057, 512, 1073749882, 448, 8059, 448, 1073749884, 504, 8061, 504, // NOLINT
+ 1073749936, 32, 8113, 32, 8126, -28820, 1073749968, 32, 8145, 32, 1073749984, 32, 8161, 32, 8165, 28 }; // NOLINT
+static const uint16_t kEcma262CanonicalizeMultiStrings0Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<1> kEcma262CanonicalizeMultiStrings1[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kEcma262CanonicalizeTable1Size = 73; // NOLINT
+static const int32_t kEcma262CanonicalizeTable1[146] = {
+ 334, -112, 1073742192, -64, 383, -64, 388, -4, 1073743056, -104, 1257, -104, 1073744944, -192, 3166, -192, // NOLINT
+ 3169, -4, 3173, -43180, 3174, -43168, 3176, -4, 3178, -4, 3180, -4, 3187, -4, 3190, -4, // NOLINT
+ 3201, -4, 3203, -4, 3205, -4, 3207, -4, 3209, -4, 3211, -4, 3213, -4, 3215, -4, // NOLINT
+ 3217, -4, 3219, -4, 3221, -4, 3223, -4, 3225, -4, 3227, -4, 3229, -4, 3231, -4, // NOLINT
+ 3233, -4, 3235, -4, 3237, -4, 3239, -4, 3241, -4, 3243, -4, 3245, -4, 3247, -4, // NOLINT
+ 3249, -4, 3251, -4, 3253, -4, 3255, -4, 3257, -4, 3259, -4, 3261, -4, 3263, -4, // NOLINT
+ 3265, -4, 3267, -4, 3269, -4, 3271, -4, 3273, -4, 3275, -4, 3277, -4, 3279, -4, // NOLINT
+ 3281, -4, 3283, -4, 3285, -4, 3287, -4, 3289, -4, 3291, -4, 3293, -4, 3295, -4, // NOLINT
+ 3297, -4, 3299, -4, 3308, -4, 3310, -4, 3315, -4, 1073745152, -29056, 3365, -29056, 3367, -29056, // NOLINT
+ 3373, -29056 }; // NOLINT
+static const uint16_t kEcma262CanonicalizeMultiStrings1Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<1> kEcma262CanonicalizeMultiStrings5[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kEcma262CanonicalizeTable5Size = 88; // NOLINT
+static const int32_t kEcma262CanonicalizeTable5[176] = {
+ 1601, -4, 1603, -4, 1605, -4, 1607, -4, 1609, -4, 1611, -4, 1613, -4, 1615, -4, // NOLINT
+ 1617, -4, 1619, -4, 1621, -4, 1623, -4, 1625, -4, 1627, -4, 1629, -4, 1631, -4, // NOLINT
+ 1633, -4, 1635, -4, 1637, -4, 1639, -4, 1641, -4, 1643, -4, 1645, -4, 1665, -4, // NOLINT
+ 1667, -4, 1669, -4, 1671, -4, 1673, -4, 1675, -4, 1677, -4, 1679, -4, 1681, -4, // NOLINT
+ 1683, -4, 1685, -4, 1687, -4, 1827, -4, 1829, -4, 1831, -4, 1833, -4, 1835, -4, // NOLINT
+ 1837, -4, 1839, -4, 1843, -4, 1845, -4, 1847, -4, 1849, -4, 1851, -4, 1853, -4, // NOLINT
+ 1855, -4, 1857, -4, 1859, -4, 1861, -4, 1863, -4, 1865, -4, 1867, -4, 1869, -4, // NOLINT
+ 1871, -4, 1873, -4, 1875, -4, 1877, -4, 1879, -4, 1881, -4, 1883, -4, 1885, -4, // NOLINT
+ 1887, -4, 1889, -4, 1891, -4, 1893, -4, 1895, -4, 1897, -4, 1899, -4, 1901, -4, // NOLINT
+ 1903, -4, 1914, -4, 1916, -4, 1919, -4, 1921, -4, 1923, -4, 1925, -4, 1927, -4, // NOLINT
+ 1932, -4, 1937, -4, 1939, -4, 1953, -4, 1955, -4, 1957, -4, 1959, -4, 1961, -4 }; // NOLINT
+static const uint16_t kEcma262CanonicalizeMultiStrings5Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<1> kEcma262CanonicalizeMultiStrings7[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kEcma262CanonicalizeTable7Size = 2; // NOLINT
+static const int32_t kEcma262CanonicalizeTable7[4] = {
+ 1073749825, -128, 8026, -128 }; // NOLINT
+static const uint16_t kEcma262CanonicalizeMultiStrings7Size = 1; // NOLINT
+int Ecma262Canonicalize::Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupMapping<true>(kEcma262CanonicalizeTable0,
+ kEcma262CanonicalizeTable0Size,
+ kEcma262CanonicalizeMultiStrings0,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 1: return LookupMapping<true>(kEcma262CanonicalizeTable1,
+ kEcma262CanonicalizeTable1Size,
+ kEcma262CanonicalizeMultiStrings1,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 5: return LookupMapping<true>(kEcma262CanonicalizeTable5,
+ kEcma262CanonicalizeTable5Size,
+ kEcma262CanonicalizeMultiStrings5,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 7: return LookupMapping<true>(kEcma262CanonicalizeTable7,
+ kEcma262CanonicalizeTable7Size,
+ kEcma262CanonicalizeMultiStrings7,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ default: return 0;
+ }
+}
+
+static const MultiCharacterSpecialCase<4> kEcma262UnCanonicalizeMultiStrings0[497] = { // NOLINT
+ {{65, 97, kSentinel}}, {{90, 122, kSentinel}}, {{181, 924, 956, kSentinel}}, {{192, 224, kSentinel}}, // NOLINT
+ {{214, 246, kSentinel}}, {{216, 248, kSentinel}}, {{222, 254, kSentinel}}, {{255, 376, kSentinel}}, // NOLINT
+ {{256, 257, kSentinel}}, {{258, 259, kSentinel}}, {{260, 261, kSentinel}}, {{262, 263, kSentinel}}, // NOLINT
+ {{264, 265, kSentinel}}, {{266, 267, kSentinel}}, {{268, 269, kSentinel}}, {{270, 271, kSentinel}}, // NOLINT
+ {{272, 273, kSentinel}}, {{274, 275, kSentinel}}, {{276, 277, kSentinel}}, {{278, 279, kSentinel}}, // NOLINT
+ {{280, 281, kSentinel}}, {{282, 283, kSentinel}}, {{284, 285, kSentinel}}, {{286, 287, kSentinel}}, // NOLINT
+ {{288, 289, kSentinel}}, {{290, 291, kSentinel}}, {{292, 293, kSentinel}}, {{294, 295, kSentinel}}, // NOLINT
+ {{296, 297, kSentinel}}, {{298, 299, kSentinel}}, {{300, 301, kSentinel}}, {{302, 303, kSentinel}}, // NOLINT
+ {{306, 307, kSentinel}}, {{308, 309, kSentinel}}, {{310, 311, kSentinel}}, {{313, 314, kSentinel}}, // NOLINT
+ {{315, 316, kSentinel}}, {{317, 318, kSentinel}}, {{319, 320, kSentinel}}, {{321, 322, kSentinel}}, // NOLINT
+ {{323, 324, kSentinel}}, {{325, 326, kSentinel}}, {{327, 328, kSentinel}}, {{330, 331, kSentinel}}, // NOLINT
+ {{332, 333, kSentinel}}, {{334, 335, kSentinel}}, {{336, 337, kSentinel}}, {{338, 339, kSentinel}}, // NOLINT
+ {{340, 341, kSentinel}}, {{342, 343, kSentinel}}, {{344, 345, kSentinel}}, {{346, 347, kSentinel}}, // NOLINT
+ {{348, 349, kSentinel}}, {{350, 351, kSentinel}}, {{352, 353, kSentinel}}, {{354, 355, kSentinel}}, // NOLINT
+ {{356, 357, kSentinel}}, {{358, 359, kSentinel}}, {{360, 361, kSentinel}}, {{362, 363, kSentinel}}, // NOLINT
+ {{364, 365, kSentinel}}, {{366, 367, kSentinel}}, {{368, 369, kSentinel}}, {{370, 371, kSentinel}}, // NOLINT
+ {{372, 373, kSentinel}}, {{374, 375, kSentinel}}, {{377, 378, kSentinel}}, {{379, 380, kSentinel}}, // NOLINT
+ {{381, 382, kSentinel}}, {{384, 579, kSentinel}}, {{385, 595, kSentinel}}, {{386, 387, kSentinel}}, // NOLINT
+ {{388, 389, kSentinel}}, {{390, 596, kSentinel}}, {{391, 392, kSentinel}}, {{393, 598, kSentinel}}, // NOLINT
+ {{394, 599, kSentinel}}, {{395, 396, kSentinel}}, {{398, 477, kSentinel}}, {{399, 601, kSentinel}}, // NOLINT
+ {{400, 603, kSentinel}}, {{401, 402, kSentinel}}, {{403, 608, kSentinel}}, {{404, 611, kSentinel}}, // NOLINT
+ {{405, 502, kSentinel}}, {{406, 617, kSentinel}}, {{407, 616, kSentinel}}, {{408, 409, kSentinel}}, // NOLINT
+ {{410, 573, kSentinel}}, {{412, 623, kSentinel}}, {{413, 626, kSentinel}}, {{414, 544, kSentinel}}, // NOLINT
+ {{415, 629, kSentinel}}, {{416, 417, kSentinel}}, {{418, 419, kSentinel}}, {{420, 421, kSentinel}}, // NOLINT
+ {{422, 640, kSentinel}}, {{423, 424, kSentinel}}, {{425, 643, kSentinel}}, {{428, 429, kSentinel}}, // NOLINT
+ {{430, 648, kSentinel}}, {{431, 432, kSentinel}}, {{433, 650, kSentinel}}, {{434, 651, kSentinel}}, // NOLINT
+ {{435, 436, kSentinel}}, {{437, 438, kSentinel}}, {{439, 658, kSentinel}}, {{440, 441, kSentinel}}, // NOLINT
+ {{444, 445, kSentinel}}, {{447, 503, kSentinel}}, {{452, 453, 454, kSentinel}}, {{455, 456, 457, kSentinel}}, // NOLINT
+ {{458, 459, 460, kSentinel}}, {{461, 462, kSentinel}}, {{463, 464, kSentinel}}, {{465, 466, kSentinel}}, // NOLINT
+ {{467, 468, kSentinel}}, {{469, 470, kSentinel}}, {{471, 472, kSentinel}}, {{473, 474, kSentinel}}, // NOLINT
+ {{475, 476, kSentinel}}, {{478, 479, kSentinel}}, {{480, 481, kSentinel}}, {{482, 483, kSentinel}}, // NOLINT
+ {{484, 485, kSentinel}}, {{486, 487, kSentinel}}, {{488, 489, kSentinel}}, {{490, 491, kSentinel}}, // NOLINT
+ {{492, 493, kSentinel}}, {{494, 495, kSentinel}}, {{497, 498, 499, kSentinel}}, {{500, 501, kSentinel}}, // NOLINT
+ {{504, 505, kSentinel}}, {{506, 507, kSentinel}}, {{508, 509, kSentinel}}, {{510, 511, kSentinel}}, // NOLINT
+ {{512, 513, kSentinel}}, {{514, 515, kSentinel}}, {{516, 517, kSentinel}}, {{518, 519, kSentinel}}, // NOLINT
+ {{520, 521, kSentinel}}, {{522, 523, kSentinel}}, {{524, 525, kSentinel}}, {{526, 527, kSentinel}}, // NOLINT
+ {{528, 529, kSentinel}}, {{530, 531, kSentinel}}, {{532, 533, kSentinel}}, {{534, 535, kSentinel}}, // NOLINT
+ {{536, 537, kSentinel}}, {{538, 539, kSentinel}}, {{540, 541, kSentinel}}, {{542, 543, kSentinel}}, // NOLINT
+ {{546, 547, kSentinel}}, {{548, 549, kSentinel}}, {{550, 551, kSentinel}}, {{552, 553, kSentinel}}, // NOLINT
+ {{554, 555, kSentinel}}, {{556, 557, kSentinel}}, {{558, 559, kSentinel}}, {{560, 561, kSentinel}}, // NOLINT
+ {{562, 563, kSentinel}}, {{570, 11365, kSentinel}}, {{571, 572, kSentinel}}, {{574, 11366, kSentinel}}, // NOLINT
+ {{575, 11390, kSentinel}}, {{576, 11391, kSentinel}}, {{577, 578, kSentinel}}, {{580, 649, kSentinel}}, // NOLINT
+ {{581, 652, kSentinel}}, {{582, 583, kSentinel}}, {{584, 585, kSentinel}}, {{586, 587, kSentinel}}, // NOLINT
+ {{588, 589, kSentinel}}, {{590, 591, kSentinel}}, {{592, 11375, kSentinel}}, {{593, 11373, kSentinel}}, // NOLINT
+ {{594, 11376, kSentinel}}, {{613, 42893, kSentinel}}, {{614, 42922, kSentinel}}, {{619, 11362, kSentinel}}, // NOLINT
+ {{625, 11374, kSentinel}}, {{637, 11364, kSentinel}}, {{837, 921, 953, 8126}}, {{880, 881, kSentinel}}, // NOLINT
+ {{882, 883, kSentinel}}, {{886, 887, kSentinel}}, {{891, 1021, kSentinel}}, {{893, 1023, kSentinel}}, // NOLINT
+ {{902, 940, kSentinel}}, {{904, 941, kSentinel}}, {{906, 943, kSentinel}}, {{908, 972, kSentinel}}, // NOLINT
+ {{910, 973, kSentinel}}, {{911, 974, kSentinel}}, {{913, 945, kSentinel}}, {{914, 946, 976, kSentinel}}, // NOLINT
+ {{915, 947, kSentinel}}, {{916, 948, kSentinel}}, {{917, 949, 1013, kSentinel}}, {{918, 950, kSentinel}}, // NOLINT
+ {{919, 951, kSentinel}}, {{920, 952, 977, kSentinel}}, {{922, 954, 1008, kSentinel}}, {{923, 955, kSentinel}}, // NOLINT
+ {{925, 957, kSentinel}}, {{927, 959, kSentinel}}, {{928, 960, 982, kSentinel}}, {{929, 961, 1009, kSentinel}}, // NOLINT
+ {{931, 962, 963, kSentinel}}, {{932, 964, kSentinel}}, {{933, 965, kSentinel}}, {{934, 966, 981, kSentinel}}, // NOLINT
+ {{935, 967, kSentinel}}, {{939, 971, kSentinel}}, {{975, 983, kSentinel}}, {{984, 985, kSentinel}}, // NOLINT
+ {{986, 987, kSentinel}}, {{988, 989, kSentinel}}, {{990, 991, kSentinel}}, {{992, 993, kSentinel}}, // NOLINT
+ {{994, 995, kSentinel}}, {{996, 997, kSentinel}}, {{998, 999, kSentinel}}, {{1000, 1001, kSentinel}}, // NOLINT
+ {{1002, 1003, kSentinel}}, {{1004, 1005, kSentinel}}, {{1006, 1007, kSentinel}}, {{1010, 1017, kSentinel}}, // NOLINT
+ {{1015, 1016, kSentinel}}, {{1018, 1019, kSentinel}}, {{1024, 1104, kSentinel}}, {{1039, 1119, kSentinel}}, // NOLINT
+ {{1040, 1072, kSentinel}}, {{1071, 1103, kSentinel}}, {{1120, 1121, kSentinel}}, {{1122, 1123, kSentinel}}, // NOLINT
+ {{1124, 1125, kSentinel}}, {{1126, 1127, kSentinel}}, {{1128, 1129, kSentinel}}, {{1130, 1131, kSentinel}}, // NOLINT
+ {{1132, 1133, kSentinel}}, {{1134, 1135, kSentinel}}, {{1136, 1137, kSentinel}}, {{1138, 1139, kSentinel}}, // NOLINT
+ {{1140, 1141, kSentinel}}, {{1142, 1143, kSentinel}}, {{1144, 1145, kSentinel}}, {{1146, 1147, kSentinel}}, // NOLINT
+ {{1148, 1149, kSentinel}}, {{1150, 1151, kSentinel}}, {{1152, 1153, kSentinel}}, {{1162, 1163, kSentinel}}, // NOLINT
+ {{1164, 1165, kSentinel}}, {{1166, 1167, kSentinel}}, {{1168, 1169, kSentinel}}, {{1170, 1171, kSentinel}}, // NOLINT
+ {{1172, 1173, kSentinel}}, {{1174, 1175, kSentinel}}, {{1176, 1177, kSentinel}}, {{1178, 1179, kSentinel}}, // NOLINT
+ {{1180, 1181, kSentinel}}, {{1182, 1183, kSentinel}}, {{1184, 1185, kSentinel}}, {{1186, 1187, kSentinel}}, // NOLINT
+ {{1188, 1189, kSentinel}}, {{1190, 1191, kSentinel}}, {{1192, 1193, kSentinel}}, {{1194, 1195, kSentinel}}, // NOLINT
+ {{1196, 1197, kSentinel}}, {{1198, 1199, kSentinel}}, {{1200, 1201, kSentinel}}, {{1202, 1203, kSentinel}}, // NOLINT
+ {{1204, 1205, kSentinel}}, {{1206, 1207, kSentinel}}, {{1208, 1209, kSentinel}}, {{1210, 1211, kSentinel}}, // NOLINT
+ {{1212, 1213, kSentinel}}, {{1214, 1215, kSentinel}}, {{1216, 1231, kSentinel}}, {{1217, 1218, kSentinel}}, // NOLINT
+ {{1219, 1220, kSentinel}}, {{1221, 1222, kSentinel}}, {{1223, 1224, kSentinel}}, {{1225, 1226, kSentinel}}, // NOLINT
+ {{1227, 1228, kSentinel}}, {{1229, 1230, kSentinel}}, {{1232, 1233, kSentinel}}, {{1234, 1235, kSentinel}}, // NOLINT
+ {{1236, 1237, kSentinel}}, {{1238, 1239, kSentinel}}, {{1240, 1241, kSentinel}}, {{1242, 1243, kSentinel}}, // NOLINT
+ {{1244, 1245, kSentinel}}, {{1246, 1247, kSentinel}}, {{1248, 1249, kSentinel}}, {{1250, 1251, kSentinel}}, // NOLINT
+ {{1252, 1253, kSentinel}}, {{1254, 1255, kSentinel}}, {{1256, 1257, kSentinel}}, {{1258, 1259, kSentinel}}, // NOLINT
+ {{1260, 1261, kSentinel}}, {{1262, 1263, kSentinel}}, {{1264, 1265, kSentinel}}, {{1266, 1267, kSentinel}}, // NOLINT
+ {{1268, 1269, kSentinel}}, {{1270, 1271, kSentinel}}, {{1272, 1273, kSentinel}}, {{1274, 1275, kSentinel}}, // NOLINT
+ {{1276, 1277, kSentinel}}, {{1278, 1279, kSentinel}}, {{1280, 1281, kSentinel}}, {{1282, 1283, kSentinel}}, // NOLINT
+ {{1284, 1285, kSentinel}}, {{1286, 1287, kSentinel}}, {{1288, 1289, kSentinel}}, {{1290, 1291, kSentinel}}, // NOLINT
+ {{1292, 1293, kSentinel}}, {{1294, 1295, kSentinel}}, {{1296, 1297, kSentinel}}, {{1298, 1299, kSentinel}}, // NOLINT
+ {{1300, 1301, kSentinel}}, {{1302, 1303, kSentinel}}, {{1304, 1305, kSentinel}}, {{1306, 1307, kSentinel}}, // NOLINT
+ {{1308, 1309, kSentinel}}, {{1310, 1311, kSentinel}}, {{1312, 1313, kSentinel}}, {{1314, 1315, kSentinel}}, // NOLINT
+ {{1316, 1317, kSentinel}}, {{1318, 1319, kSentinel}}, {{1329, 1377, kSentinel}}, {{1366, 1414, kSentinel}}, // NOLINT
+ {{4256, 11520, kSentinel}}, {{4293, 11557, kSentinel}}, {{4295, 11559, kSentinel}}, {{4301, 11565, kSentinel}}, // NOLINT
+ {{7545, 42877, kSentinel}}, {{7549, 11363, kSentinel}}, {{7680, 7681, kSentinel}}, {{7682, 7683, kSentinel}}, // NOLINT
+ {{7684, 7685, kSentinel}}, {{7686, 7687, kSentinel}}, {{7688, 7689, kSentinel}}, {{7690, 7691, kSentinel}}, // NOLINT
+ {{7692, 7693, kSentinel}}, {{7694, 7695, kSentinel}}, {{7696, 7697, kSentinel}}, {{7698, 7699, kSentinel}}, // NOLINT
+ {{7700, 7701, kSentinel}}, {{7702, 7703, kSentinel}}, {{7704, 7705, kSentinel}}, {{7706, 7707, kSentinel}}, // NOLINT
+ {{7708, 7709, kSentinel}}, {{7710, 7711, kSentinel}}, {{7712, 7713, kSentinel}}, {{7714, 7715, kSentinel}}, // NOLINT
+ {{7716, 7717, kSentinel}}, {{7718, 7719, kSentinel}}, {{7720, 7721, kSentinel}}, {{7722, 7723, kSentinel}}, // NOLINT
+ {{7724, 7725, kSentinel}}, {{7726, 7727, kSentinel}}, {{7728, 7729, kSentinel}}, {{7730, 7731, kSentinel}}, // NOLINT
+ {{7732, 7733, kSentinel}}, {{7734, 7735, kSentinel}}, {{7736, 7737, kSentinel}}, {{7738, 7739, kSentinel}}, // NOLINT
+ {{7740, 7741, kSentinel}}, {{7742, 7743, kSentinel}}, {{7744, 7745, kSentinel}}, {{7746, 7747, kSentinel}}, // NOLINT
+ {{7748, 7749, kSentinel}}, {{7750, 7751, kSentinel}}, {{7752, 7753, kSentinel}}, {{7754, 7755, kSentinel}}, // NOLINT
+ {{7756, 7757, kSentinel}}, {{7758, 7759, kSentinel}}, {{7760, 7761, kSentinel}}, {{7762, 7763, kSentinel}}, // NOLINT
+ {{7764, 7765, kSentinel}}, {{7766, 7767, kSentinel}}, {{7768, 7769, kSentinel}}, {{7770, 7771, kSentinel}}, // NOLINT
+ {{7772, 7773, kSentinel}}, {{7774, 7775, kSentinel}}, {{7776, 7777, 7835, kSentinel}}, {{7778, 7779, kSentinel}}, // NOLINT
+ {{7780, 7781, kSentinel}}, {{7782, 7783, kSentinel}}, {{7784, 7785, kSentinel}}, {{7786, 7787, kSentinel}}, // NOLINT
+ {{7788, 7789, kSentinel}}, {{7790, 7791, kSentinel}}, {{7792, 7793, kSentinel}}, {{7794, 7795, kSentinel}}, // NOLINT
+ {{7796, 7797, kSentinel}}, {{7798, 7799, kSentinel}}, {{7800, 7801, kSentinel}}, {{7802, 7803, kSentinel}}, // NOLINT
+ {{7804, 7805, kSentinel}}, {{7806, 7807, kSentinel}}, {{7808, 7809, kSentinel}}, {{7810, 7811, kSentinel}}, // NOLINT
+ {{7812, 7813, kSentinel}}, {{7814, 7815, kSentinel}}, {{7816, 7817, kSentinel}}, {{7818, 7819, kSentinel}}, // NOLINT
+ {{7820, 7821, kSentinel}}, {{7822, 7823, kSentinel}}, {{7824, 7825, kSentinel}}, {{7826, 7827, kSentinel}}, // NOLINT
+ {{7828, 7829, kSentinel}}, {{7840, 7841, kSentinel}}, {{7842, 7843, kSentinel}}, {{7844, 7845, kSentinel}}, // NOLINT
+ {{7846, 7847, kSentinel}}, {{7848, 7849, kSentinel}}, {{7850, 7851, kSentinel}}, {{7852, 7853, kSentinel}}, // NOLINT
+ {{7854, 7855, kSentinel}}, {{7856, 7857, kSentinel}}, {{7858, 7859, kSentinel}}, {{7860, 7861, kSentinel}}, // NOLINT
+ {{7862, 7863, kSentinel}}, {{7864, 7865, kSentinel}}, {{7866, 7867, kSentinel}}, {{7868, 7869, kSentinel}}, // NOLINT
+ {{7870, 7871, kSentinel}}, {{7872, 7873, kSentinel}}, {{7874, 7875, kSentinel}}, {{7876, 7877, kSentinel}}, // NOLINT
+ {{7878, 7879, kSentinel}}, {{7880, 7881, kSentinel}}, {{7882, 7883, kSentinel}}, {{7884, 7885, kSentinel}}, // NOLINT
+ {{7886, 7887, kSentinel}}, {{7888, 7889, kSentinel}}, {{7890, 7891, kSentinel}}, {{7892, 7893, kSentinel}}, // NOLINT
+ {{7894, 7895, kSentinel}}, {{7896, 7897, kSentinel}}, {{7898, 7899, kSentinel}}, {{7900, 7901, kSentinel}}, // NOLINT
+ {{7902, 7903, kSentinel}}, {{7904, 7905, kSentinel}}, {{7906, 7907, kSentinel}}, {{7908, 7909, kSentinel}}, // NOLINT
+ {{7910, 7911, kSentinel}}, {{7912, 7913, kSentinel}}, {{7914, 7915, kSentinel}}, {{7916, 7917, kSentinel}}, // NOLINT
+ {{7918, 7919, kSentinel}}, {{7920, 7921, kSentinel}}, {{7922, 7923, kSentinel}}, {{7924, 7925, kSentinel}}, // NOLINT
+ {{7926, 7927, kSentinel}}, {{7928, 7929, kSentinel}}, {{7930, 7931, kSentinel}}, {{7932, 7933, kSentinel}}, // NOLINT
+ {{7934, 7935, kSentinel}}, {{7936, 7944, kSentinel}}, {{7943, 7951, kSentinel}}, {{7952, 7960, kSentinel}}, // NOLINT
+ {{7957, 7965, kSentinel}}, {{7968, 7976, kSentinel}}, {{7975, 7983, kSentinel}}, {{7984, 7992, kSentinel}}, // NOLINT
+ {{7991, 7999, kSentinel}}, {{8000, 8008, kSentinel}}, {{8005, 8013, kSentinel}}, {{8017, 8025, kSentinel}}, // NOLINT
+ {{8019, 8027, kSentinel}}, {{8021, 8029, kSentinel}}, {{8023, 8031, kSentinel}}, {{8032, 8040, kSentinel}}, // NOLINT
+ {{8039, 8047, kSentinel}}, {{8048, 8122, kSentinel}}, {{8049, 8123, kSentinel}}, {{8050, 8136, kSentinel}}, // NOLINT
+ {{8053, 8139, kSentinel}}, {{8054, 8154, kSentinel}}, {{8055, 8155, kSentinel}}, {{8056, 8184, kSentinel}}, // NOLINT
+ {{8057, 8185, kSentinel}}, {{8058, 8170, kSentinel}}, {{8059, 8171, kSentinel}}, {{8060, 8186, kSentinel}}, // NOLINT
+ {{8061, 8187, kSentinel}}, {{8112, 8120, kSentinel}}, {{8113, 8121, kSentinel}}, {{8144, 8152, kSentinel}}, // NOLINT
+ {{8145, 8153, kSentinel}}, {{8160, 8168, kSentinel}}, {{8161, 8169, kSentinel}}, {{8165, 8172, kSentinel}}, // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kEcma262UnCanonicalizeTable0Size = 990; // NOLINT
+static const int32_t kEcma262UnCanonicalizeTable0[1980] = {
+ 1073741889, 1, 90, 5, 1073741921, 1, 122, 5, 181, 9, 1073742016, 13, 214, 17, 1073742040, 21, // NOLINT
+ 222, 25, 1073742048, 13, 246, 17, 1073742072, 21, 254, 25, 255, 29, 256, 33, 257, 33, // NOLINT
+ 258, 37, 259, 37, 260, 41, 261, 41, 262, 45, 263, 45, 264, 49, 265, 49, // NOLINT
+ 266, 53, 267, 53, 268, 57, 269, 57, 270, 61, 271, 61, 272, 65, 273, 65, // NOLINT
+ 274, 69, 275, 69, 276, 73, 277, 73, 278, 77, 279, 77, 280, 81, 281, 81, // NOLINT
+ 282, 85, 283, 85, 284, 89, 285, 89, 286, 93, 287, 93, 288, 97, 289, 97, // NOLINT
+ 290, 101, 291, 101, 292, 105, 293, 105, 294, 109, 295, 109, 296, 113, 297, 113, // NOLINT
+ 298, 117, 299, 117, 300, 121, 301, 121, 302, 125, 303, 125, 306, 129, 307, 129, // NOLINT
+ 308, 133, 309, 133, 310, 137, 311, 137, 313, 141, 314, 141, 315, 145, 316, 145, // NOLINT
+ 317, 149, 318, 149, 319, 153, 320, 153, 321, 157, 322, 157, 323, 161, 324, 161, // NOLINT
+ 325, 165, 326, 165, 327, 169, 328, 169, 330, 173, 331, 173, 332, 177, 333, 177, // NOLINT
+ 334, 181, 335, 181, 336, 185, 337, 185, 338, 189, 339, 189, 340, 193, 341, 193, // NOLINT
+ 342, 197, 343, 197, 344, 201, 345, 201, 346, 205, 347, 205, 348, 209, 349, 209, // NOLINT
+ 350, 213, 351, 213, 352, 217, 353, 217, 354, 221, 355, 221, 356, 225, 357, 225, // NOLINT
+ 358, 229, 359, 229, 360, 233, 361, 233, 362, 237, 363, 237, 364, 241, 365, 241, // NOLINT
+ 366, 245, 367, 245, 368, 249, 369, 249, 370, 253, 371, 253, 372, 257, 373, 257, // NOLINT
+ 374, 261, 375, 261, 376, 29, 377, 265, 378, 265, 379, 269, 380, 269, 381, 273, // NOLINT
+ 382, 273, 384, 277, 385, 281, 386, 285, 387, 285, 388, 289, 389, 289, 390, 293, // NOLINT
+ 391, 297, 392, 297, 1073742217, 301, 394, 305, 395, 309, 396, 309, 398, 313, 399, 317, // NOLINT
+ 400, 321, 401, 325, 402, 325, 403, 329, 404, 333, 405, 337, 406, 341, 407, 345, // NOLINT
+ 408, 349, 409, 349, 410, 353, 412, 357, 413, 361, 414, 365, 415, 369, 416, 373, // NOLINT
+ 417, 373, 418, 377, 419, 377, 420, 381, 421, 381, 422, 385, 423, 389, 424, 389, // NOLINT
+ 425, 393, 428, 397, 429, 397, 430, 401, 431, 405, 432, 405, 1073742257, 409, 434, 413, // NOLINT
+ 435, 417, 436, 417, 437, 421, 438, 421, 439, 425, 440, 429, 441, 429, 444, 433, // NOLINT
+ 445, 433, 447, 437, 452, 441, 453, 441, 454, 441, 455, 445, 456, 445, 457, 445, // NOLINT
+ 458, 449, 459, 449, 460, 449, 461, 453, 462, 453, 463, 457, 464, 457, 465, 461, // NOLINT
+ 466, 461, 467, 465, 468, 465, 469, 469, 470, 469, 471, 473, 472, 473, 473, 477, // NOLINT
+ 474, 477, 475, 481, 476, 481, 477, 313, 478, 485, 479, 485, 480, 489, 481, 489, // NOLINT
+ 482, 493, 483, 493, 484, 497, 485, 497, 486, 501, 487, 501, 488, 505, 489, 505, // NOLINT
+ 490, 509, 491, 509, 492, 513, 493, 513, 494, 517, 495, 517, 497, 521, 498, 521, // NOLINT
+ 499, 521, 500, 525, 501, 525, 502, 337, 503, 437, 504, 529, 505, 529, 506, 533, // NOLINT
+ 507, 533, 508, 537, 509, 537, 510, 541, 511, 541, 512, 545, 513, 545, 514, 549, // NOLINT
+ 515, 549, 516, 553, 517, 553, 518, 557, 519, 557, 520, 561, 521, 561, 522, 565, // NOLINT
+ 523, 565, 524, 569, 525, 569, 526, 573, 527, 573, 528, 577, 529, 577, 530, 581, // NOLINT
+ 531, 581, 532, 585, 533, 585, 534, 589, 535, 589, 536, 593, 537, 593, 538, 597, // NOLINT
+ 539, 597, 540, 601, 541, 601, 542, 605, 543, 605, 544, 365, 546, 609, 547, 609, // NOLINT
+ 548, 613, 549, 613, 550, 617, 551, 617, 552, 621, 553, 621, 554, 625, 555, 625, // NOLINT
+ 556, 629, 557, 629, 558, 633, 559, 633, 560, 637, 561, 637, 562, 641, 563, 641, // NOLINT
+ 570, 645, 571, 649, 572, 649, 573, 353, 574, 653, 1073742399, 657, 576, 661, 577, 665, // NOLINT
+ 578, 665, 579, 277, 580, 669, 581, 673, 582, 677, 583, 677, 584, 681, 585, 681, // NOLINT
+ 586, 685, 587, 685, 588, 689, 589, 689, 590, 693, 591, 693, 592, 697, 593, 701, // NOLINT
+ 594, 705, 595, 281, 596, 293, 1073742422, 301, 599, 305, 601, 317, 603, 321, 608, 329, // NOLINT
+ 611, 333, 613, 709, 614, 713, 616, 345, 617, 341, 619, 717, 623, 357, 625, 721, // NOLINT
+ 626, 361, 629, 369, 637, 725, 640, 385, 643, 393, 648, 401, 649, 669, 1073742474, 409, // NOLINT
+ 651, 413, 652, 673, 658, 425, 837, 729, 880, 733, 881, 733, 882, 737, 883, 737, // NOLINT
+ 886, 741, 887, 741, 1073742715, 745, 893, 749, 902, 753, 1073742728, 757, 906, 761, 908, 765, // NOLINT
+ 1073742734, 769, 911, 773, 913, 777, 914, 781, 1073742739, 785, 916, 789, 917, 793, 1073742742, 797, // NOLINT
+ 919, 801, 920, 805, 921, 729, 922, 809, 923, 813, 924, 9, 1073742749, 817, 927, 821, // NOLINT
+ 928, 825, 929, 829, 931, 833, 1073742756, 837, 933, 841, 934, 845, 1073742759, 849, 939, 853, // NOLINT
+ 940, 753, 1073742765, 757, 943, 761, 945, 777, 946, 781, 1073742771, 785, 948, 789, 949, 793, // NOLINT
+ 1073742774, 797, 951, 801, 952, 805, 953, 729, 954, 809, 955, 813, 956, 9, 1073742781, 817, // NOLINT
+ 959, 821, 960, 825, 961, 829, 962, 833, 963, 833, 1073742788, 837, 965, 841, 966, 845, // NOLINT
+ 1073742791, 849, 971, 853, 972, 765, 1073742797, 769, 974, 773, 975, 857, 976, 781, 977, 805, // NOLINT
+ 981, 845, 982, 825, 983, 857, 984, 861, 985, 861, 986, 865, 987, 865, 988, 869, // NOLINT
+ 989, 869, 990, 873, 991, 873, 992, 877, 993, 877, 994, 881, 995, 881, 996, 885, // NOLINT
+ 997, 885, 998, 889, 999, 889, 1000, 893, 1001, 893, 1002, 897, 1003, 897, 1004, 901, // NOLINT
+ 1005, 901, 1006, 905, 1007, 905, 1008, 809, 1009, 829, 1010, 909, 1013, 793, 1015, 913, // NOLINT
+ 1016, 913, 1017, 909, 1018, 917, 1019, 917, 1073742845, 745, 1023, 749, 1073742848, 921, 1039, 925, // NOLINT
+ 1073742864, 929, 1071, 933, 1073742896, 929, 1103, 933, 1073742928, 921, 1119, 925, 1120, 937, 1121, 937, // NOLINT
+ 1122, 941, 1123, 941, 1124, 945, 1125, 945, 1126, 949, 1127, 949, 1128, 953, 1129, 953, // NOLINT
+ 1130, 957, 1131, 957, 1132, 961, 1133, 961, 1134, 965, 1135, 965, 1136, 969, 1137, 969, // NOLINT
+ 1138, 973, 1139, 973, 1140, 977, 1141, 977, 1142, 981, 1143, 981, 1144, 985, 1145, 985, // NOLINT
+ 1146, 989, 1147, 989, 1148, 993, 1149, 993, 1150, 997, 1151, 997, 1152, 1001, 1153, 1001, // NOLINT
+ 1162, 1005, 1163, 1005, 1164, 1009, 1165, 1009, 1166, 1013, 1167, 1013, 1168, 1017, 1169, 1017, // NOLINT
+ 1170, 1021, 1171, 1021, 1172, 1025, 1173, 1025, 1174, 1029, 1175, 1029, 1176, 1033, 1177, 1033, // NOLINT
+ 1178, 1037, 1179, 1037, 1180, 1041, 1181, 1041, 1182, 1045, 1183, 1045, 1184, 1049, 1185, 1049, // NOLINT
+ 1186, 1053, 1187, 1053, 1188, 1057, 1189, 1057, 1190, 1061, 1191, 1061, 1192, 1065, 1193, 1065, // NOLINT
+ 1194, 1069, 1195, 1069, 1196, 1073, 1197, 1073, 1198, 1077, 1199, 1077, 1200, 1081, 1201, 1081, // NOLINT
+ 1202, 1085, 1203, 1085, 1204, 1089, 1205, 1089, 1206, 1093, 1207, 1093, 1208, 1097, 1209, 1097, // NOLINT
+ 1210, 1101, 1211, 1101, 1212, 1105, 1213, 1105, 1214, 1109, 1215, 1109, 1216, 1113, 1217, 1117, // NOLINT
+ 1218, 1117, 1219, 1121, 1220, 1121, 1221, 1125, 1222, 1125, 1223, 1129, 1224, 1129, 1225, 1133, // NOLINT
+ 1226, 1133, 1227, 1137, 1228, 1137, 1229, 1141, 1230, 1141, 1231, 1113, 1232, 1145, 1233, 1145, // NOLINT
+ 1234, 1149, 1235, 1149, 1236, 1153, 1237, 1153, 1238, 1157, 1239, 1157, 1240, 1161, 1241, 1161, // NOLINT
+ 1242, 1165, 1243, 1165, 1244, 1169, 1245, 1169, 1246, 1173, 1247, 1173, 1248, 1177, 1249, 1177, // NOLINT
+ 1250, 1181, 1251, 1181, 1252, 1185, 1253, 1185, 1254, 1189, 1255, 1189, 1256, 1193, 1257, 1193, // NOLINT
+ 1258, 1197, 1259, 1197, 1260, 1201, 1261, 1201, 1262, 1205, 1263, 1205, 1264, 1209, 1265, 1209, // NOLINT
+ 1266, 1213, 1267, 1213, 1268, 1217, 1269, 1217, 1270, 1221, 1271, 1221, 1272, 1225, 1273, 1225, // NOLINT
+ 1274, 1229, 1275, 1229, 1276, 1233, 1277, 1233, 1278, 1237, 1279, 1237, 1280, 1241, 1281, 1241, // NOLINT
+ 1282, 1245, 1283, 1245, 1284, 1249, 1285, 1249, 1286, 1253, 1287, 1253, 1288, 1257, 1289, 1257, // NOLINT
+ 1290, 1261, 1291, 1261, 1292, 1265, 1293, 1265, 1294, 1269, 1295, 1269, 1296, 1273, 1297, 1273, // NOLINT
+ 1298, 1277, 1299, 1277, 1300, 1281, 1301, 1281, 1302, 1285, 1303, 1285, 1304, 1289, 1305, 1289, // NOLINT
+ 1306, 1293, 1307, 1293, 1308, 1297, 1309, 1297, 1310, 1301, 1311, 1301, 1312, 1305, 1313, 1305, // NOLINT
+ 1314, 1309, 1315, 1309, 1316, 1313, 1317, 1313, 1318, 1317, 1319, 1317, 1073743153, 1321, 1366, 1325, // NOLINT
+ 1073743201, 1321, 1414, 1325, 1073746080, 1329, 4293, 1333, 4295, 1337, 4301, 1341, 7545, 1345, 7549, 1349, // NOLINT
+ 7680, 1353, 7681, 1353, 7682, 1357, 7683, 1357, 7684, 1361, 7685, 1361, 7686, 1365, 7687, 1365, // NOLINT
+ 7688, 1369, 7689, 1369, 7690, 1373, 7691, 1373, 7692, 1377, 7693, 1377, 7694, 1381, 7695, 1381, // NOLINT
+ 7696, 1385, 7697, 1385, 7698, 1389, 7699, 1389, 7700, 1393, 7701, 1393, 7702, 1397, 7703, 1397, // NOLINT
+ 7704, 1401, 7705, 1401, 7706, 1405, 7707, 1405, 7708, 1409, 7709, 1409, 7710, 1413, 7711, 1413, // NOLINT
+ 7712, 1417, 7713, 1417, 7714, 1421, 7715, 1421, 7716, 1425, 7717, 1425, 7718, 1429, 7719, 1429, // NOLINT
+ 7720, 1433, 7721, 1433, 7722, 1437, 7723, 1437, 7724, 1441, 7725, 1441, 7726, 1445, 7727, 1445, // NOLINT
+ 7728, 1449, 7729, 1449, 7730, 1453, 7731, 1453, 7732, 1457, 7733, 1457, 7734, 1461, 7735, 1461, // NOLINT
+ 7736, 1465, 7737, 1465, 7738, 1469, 7739, 1469, 7740, 1473, 7741, 1473, 7742, 1477, 7743, 1477, // NOLINT
+ 7744, 1481, 7745, 1481, 7746, 1485, 7747, 1485, 7748, 1489, 7749, 1489, 7750, 1493, 7751, 1493, // NOLINT
+ 7752, 1497, 7753, 1497, 7754, 1501, 7755, 1501, 7756, 1505, 7757, 1505, 7758, 1509, 7759, 1509, // NOLINT
+ 7760, 1513, 7761, 1513, 7762, 1517, 7763, 1517, 7764, 1521, 7765, 1521, 7766, 1525, 7767, 1525, // NOLINT
+ 7768, 1529, 7769, 1529, 7770, 1533, 7771, 1533, 7772, 1537, 7773, 1537, 7774, 1541, 7775, 1541, // NOLINT
+ 7776, 1545, 7777, 1545, 7778, 1549, 7779, 1549, 7780, 1553, 7781, 1553, 7782, 1557, 7783, 1557, // NOLINT
+ 7784, 1561, 7785, 1561, 7786, 1565, 7787, 1565, 7788, 1569, 7789, 1569, 7790, 1573, 7791, 1573, // NOLINT
+ 7792, 1577, 7793, 1577, 7794, 1581, 7795, 1581, 7796, 1585, 7797, 1585, 7798, 1589, 7799, 1589, // NOLINT
+ 7800, 1593, 7801, 1593, 7802, 1597, 7803, 1597, 7804, 1601, 7805, 1601, 7806, 1605, 7807, 1605, // NOLINT
+ 7808, 1609, 7809, 1609, 7810, 1613, 7811, 1613, 7812, 1617, 7813, 1617, 7814, 1621, 7815, 1621, // NOLINT
+ 7816, 1625, 7817, 1625, 7818, 1629, 7819, 1629, 7820, 1633, 7821, 1633, 7822, 1637, 7823, 1637, // NOLINT
+ 7824, 1641, 7825, 1641, 7826, 1645, 7827, 1645, 7828, 1649, 7829, 1649, 7835, 1545, 7840, 1653, // NOLINT
+ 7841, 1653, 7842, 1657, 7843, 1657, 7844, 1661, 7845, 1661, 7846, 1665, 7847, 1665, 7848, 1669, // NOLINT
+ 7849, 1669, 7850, 1673, 7851, 1673, 7852, 1677, 7853, 1677, 7854, 1681, 7855, 1681, 7856, 1685, // NOLINT
+ 7857, 1685, 7858, 1689, 7859, 1689, 7860, 1693, 7861, 1693, 7862, 1697, 7863, 1697, 7864, 1701, // NOLINT
+ 7865, 1701, 7866, 1705, 7867, 1705, 7868, 1709, 7869, 1709, 7870, 1713, 7871, 1713, 7872, 1717, // NOLINT
+ 7873, 1717, 7874, 1721, 7875, 1721, 7876, 1725, 7877, 1725, 7878, 1729, 7879, 1729, 7880, 1733, // NOLINT
+ 7881, 1733, 7882, 1737, 7883, 1737, 7884, 1741, 7885, 1741, 7886, 1745, 7887, 1745, 7888, 1749, // NOLINT
+ 7889, 1749, 7890, 1753, 7891, 1753, 7892, 1757, 7893, 1757, 7894, 1761, 7895, 1761, 7896, 1765, // NOLINT
+ 7897, 1765, 7898, 1769, 7899, 1769, 7900, 1773, 7901, 1773, 7902, 1777, 7903, 1777, 7904, 1781, // NOLINT
+ 7905, 1781, 7906, 1785, 7907, 1785, 7908, 1789, 7909, 1789, 7910, 1793, 7911, 1793, 7912, 1797, // NOLINT
+ 7913, 1797, 7914, 1801, 7915, 1801, 7916, 1805, 7917, 1805, 7918, 1809, 7919, 1809, 7920, 1813, // NOLINT
+ 7921, 1813, 7922, 1817, 7923, 1817, 7924, 1821, 7925, 1821, 7926, 1825, 7927, 1825, 7928, 1829, // NOLINT
+ 7929, 1829, 7930, 1833, 7931, 1833, 7932, 1837, 7933, 1837, 7934, 1841, 7935, 1841, 1073749760, 1845, // NOLINT
+ 7943, 1849, 1073749768, 1845, 7951, 1849, 1073749776, 1853, 7957, 1857, 1073749784, 1853, 7965, 1857, 1073749792, 1861, // NOLINT
+ 7975, 1865, 1073749800, 1861, 7983, 1865, 1073749808, 1869, 7991, 1873, 1073749816, 1869, 7999, 1873, 1073749824, 1877, // NOLINT
+ 8005, 1881, 1073749832, 1877, 8013, 1881, 8017, 1885, 8019, 1889, 8021, 1893, 8023, 1897, 8025, 1885, // NOLINT
+ 8027, 1889, 8029, 1893, 8031, 1897, 1073749856, 1901, 8039, 1905, 1073749864, 1901, 8047, 1905, 1073749872, 1909, // NOLINT
+ 8049, 1913, 1073749874, 1917, 8053, 1921, 1073749878, 1925, 8055, 1929, 1073749880, 1933, 8057, 1937, 1073749882, 1941, // NOLINT
+ 8059, 1945, 1073749884, 1949, 8061, 1953, 1073749936, 1957, 8113, 1961, 1073749944, 1957, 8121, 1961, 1073749946, 1909, // NOLINT
+ 8123, 1913, 8126, 729, 1073749960, 1917, 8139, 1921, 1073749968, 1965, 8145, 1969, 1073749976, 1965, 8153, 1969, // NOLINT
+ 1073749978, 1925, 8155, 1929, 1073749984, 1973, 8161, 1977, 8165, 1981, 1073749992, 1973, 8169, 1977, 1073749994, 1941, // NOLINT
+ 8171, 1945, 8172, 1981, 1073750008, 1933, 8185, 1937, 1073750010, 1949, 8187, 1953 }; // NOLINT
+static const uint16_t kEcma262UnCanonicalizeMultiStrings0Size = 497; // NOLINT
+static const MultiCharacterSpecialCase<2> kEcma262UnCanonicalizeMultiStrings1[83] = { // NOLINT
+ {{8498, 8526}}, {{8544, 8560}}, {{8559, 8575}}, {{8579, 8580}}, // NOLINT
+ {{9398, 9424}}, {{9423, 9449}}, {{11264, 11312}}, {{11310, 11358}}, // NOLINT
+ {{11360, 11361}}, {{619, 11362}}, {{7549, 11363}}, {{637, 11364}}, // NOLINT
+ {{570, 11365}}, {{574, 11366}}, {{11367, 11368}}, {{11369, 11370}}, // NOLINT
+ {{11371, 11372}}, {{593, 11373}}, {{625, 11374}}, {{592, 11375}}, // NOLINT
+ {{594, 11376}}, {{11378, 11379}}, {{11381, 11382}}, {{575, 11390}}, // NOLINT
+ {{576, 11391}}, {{11392, 11393}}, {{11394, 11395}}, {{11396, 11397}}, // NOLINT
+ {{11398, 11399}}, {{11400, 11401}}, {{11402, 11403}}, {{11404, 11405}}, // NOLINT
+ {{11406, 11407}}, {{11408, 11409}}, {{11410, 11411}}, {{11412, 11413}}, // NOLINT
+ {{11414, 11415}}, {{11416, 11417}}, {{11418, 11419}}, {{11420, 11421}}, // NOLINT
+ {{11422, 11423}}, {{11424, 11425}}, {{11426, 11427}}, {{11428, 11429}}, // NOLINT
+ {{11430, 11431}}, {{11432, 11433}}, {{11434, 11435}}, {{11436, 11437}}, // NOLINT
+ {{11438, 11439}}, {{11440, 11441}}, {{11442, 11443}}, {{11444, 11445}}, // NOLINT
+ {{11446, 11447}}, {{11448, 11449}}, {{11450, 11451}}, {{11452, 11453}}, // NOLINT
+ {{11454, 11455}}, {{11456, 11457}}, {{11458, 11459}}, {{11460, 11461}}, // NOLINT
+ {{11462, 11463}}, {{11464, 11465}}, {{11466, 11467}}, {{11468, 11469}}, // NOLINT
+ {{11470, 11471}}, {{11472, 11473}}, {{11474, 11475}}, {{11476, 11477}}, // NOLINT
+ {{11478, 11479}}, {{11480, 11481}}, {{11482, 11483}}, {{11484, 11485}}, // NOLINT
+ {{11486, 11487}}, {{11488, 11489}}, {{11490, 11491}}, {{11499, 11500}}, // NOLINT
+ {{11501, 11502}}, {{11506, 11507}}, {{4256, 11520}}, {{4293, 11557}}, // NOLINT
+ {{4295, 11559}}, {{4301, 11565}}, {{kSentinel}} }; // NOLINT
+static const uint16_t kEcma262UnCanonicalizeTable1Size = 149; // NOLINT
+static const int32_t kEcma262UnCanonicalizeTable1[298] = {
+ 306, 1, 334, 1, 1073742176, 5, 367, 9, 1073742192, 5, 383, 9, 387, 13, 388, 13, // NOLINT
+ 1073743030, 17, 1231, 21, 1073743056, 17, 1257, 21, 1073744896, 25, 3118, 29, 1073744944, 25, 3166, 29, // NOLINT
+ 3168, 33, 3169, 33, 3170, 37, 3171, 41, 3172, 45, 3173, 49, 3174, 53, 3175, 57, // NOLINT
+ 3176, 57, 3177, 61, 3178, 61, 3179, 65, 3180, 65, 3181, 69, 3182, 73, 3183, 77, // NOLINT
+ 3184, 81, 3186, 85, 3187, 85, 3189, 89, 3190, 89, 1073745022, 93, 3199, 97, 3200, 101, // NOLINT
+ 3201, 101, 3202, 105, 3203, 105, 3204, 109, 3205, 109, 3206, 113, 3207, 113, 3208, 117, // NOLINT
+ 3209, 117, 3210, 121, 3211, 121, 3212, 125, 3213, 125, 3214, 129, 3215, 129, 3216, 133, // NOLINT
+ 3217, 133, 3218, 137, 3219, 137, 3220, 141, 3221, 141, 3222, 145, 3223, 145, 3224, 149, // NOLINT
+ 3225, 149, 3226, 153, 3227, 153, 3228, 157, 3229, 157, 3230, 161, 3231, 161, 3232, 165, // NOLINT
+ 3233, 165, 3234, 169, 3235, 169, 3236, 173, 3237, 173, 3238, 177, 3239, 177, 3240, 181, // NOLINT
+ 3241, 181, 3242, 185, 3243, 185, 3244, 189, 3245, 189, 3246, 193, 3247, 193, 3248, 197, // NOLINT
+ 3249, 197, 3250, 201, 3251, 201, 3252, 205, 3253, 205, 3254, 209, 3255, 209, 3256, 213, // NOLINT
+ 3257, 213, 3258, 217, 3259, 217, 3260, 221, 3261, 221, 3262, 225, 3263, 225, 3264, 229, // NOLINT
+ 3265, 229, 3266, 233, 3267, 233, 3268, 237, 3269, 237, 3270, 241, 3271, 241, 3272, 245, // NOLINT
+ 3273, 245, 3274, 249, 3275, 249, 3276, 253, 3277, 253, 3278, 257, 3279, 257, 3280, 261, // NOLINT
+ 3281, 261, 3282, 265, 3283, 265, 3284, 269, 3285, 269, 3286, 273, 3287, 273, 3288, 277, // NOLINT
+ 3289, 277, 3290, 281, 3291, 281, 3292, 285, 3293, 285, 3294, 289, 3295, 289, 3296, 293, // NOLINT
+ 3297, 293, 3298, 297, 3299, 297, 3307, 301, 3308, 301, 3309, 305, 3310, 305, 3314, 309, // NOLINT
+ 3315, 309, 1073745152, 313, 3365, 317, 3367, 321, 3373, 325 }; // NOLINT
+static const uint16_t kEcma262UnCanonicalizeMultiStrings1Size = 83; // NOLINT
+static const MultiCharacterSpecialCase<2> kEcma262UnCanonicalizeMultiStrings5[92] = { // NOLINT
+ {{42560, 42561}}, {{42562, 42563}}, {{42564, 42565}}, {{42566, 42567}}, // NOLINT
+ {{42568, 42569}}, {{42570, 42571}}, {{42572, 42573}}, {{42574, 42575}}, // NOLINT
+ {{42576, 42577}}, {{42578, 42579}}, {{42580, 42581}}, {{42582, 42583}}, // NOLINT
+ {{42584, 42585}}, {{42586, 42587}}, {{42588, 42589}}, {{42590, 42591}}, // NOLINT
+ {{42592, 42593}}, {{42594, 42595}}, {{42596, 42597}}, {{42598, 42599}}, // NOLINT
+ {{42600, 42601}}, {{42602, 42603}}, {{42604, 42605}}, {{42624, 42625}}, // NOLINT
+ {{42626, 42627}}, {{42628, 42629}}, {{42630, 42631}}, {{42632, 42633}}, // NOLINT
+ {{42634, 42635}}, {{42636, 42637}}, {{42638, 42639}}, {{42640, 42641}}, // NOLINT
+ {{42642, 42643}}, {{42644, 42645}}, {{42646, 42647}}, {{42786, 42787}}, // NOLINT
+ {{42788, 42789}}, {{42790, 42791}}, {{42792, 42793}}, {{42794, 42795}}, // NOLINT
+ {{42796, 42797}}, {{42798, 42799}}, {{42802, 42803}}, {{42804, 42805}}, // NOLINT
+ {{42806, 42807}}, {{42808, 42809}}, {{42810, 42811}}, {{42812, 42813}}, // NOLINT
+ {{42814, 42815}}, {{42816, 42817}}, {{42818, 42819}}, {{42820, 42821}}, // NOLINT
+ {{42822, 42823}}, {{42824, 42825}}, {{42826, 42827}}, {{42828, 42829}}, // NOLINT
+ {{42830, 42831}}, {{42832, 42833}}, {{42834, 42835}}, {{42836, 42837}}, // NOLINT
+ {{42838, 42839}}, {{42840, 42841}}, {{42842, 42843}}, {{42844, 42845}}, // NOLINT
+ {{42846, 42847}}, {{42848, 42849}}, {{42850, 42851}}, {{42852, 42853}}, // NOLINT
+ {{42854, 42855}}, {{42856, 42857}}, {{42858, 42859}}, {{42860, 42861}}, // NOLINT
+ {{42862, 42863}}, {{42873, 42874}}, {{42875, 42876}}, {{7545, 42877}}, // NOLINT
+ {{42878, 42879}}, {{42880, 42881}}, {{42882, 42883}}, {{42884, 42885}}, // NOLINT
+ {{42886, 42887}}, {{42891, 42892}}, {{613, 42893}}, {{42896, 42897}}, // NOLINT
+ {{42898, 42899}}, {{42912, 42913}}, {{42914, 42915}}, {{42916, 42917}}, // NOLINT
+ {{42918, 42919}}, {{42920, 42921}}, {{614, 42922}}, {{kSentinel}} }; // NOLINT
+static const uint16_t kEcma262UnCanonicalizeTable5Size = 179; // NOLINT
+static const int32_t kEcma262UnCanonicalizeTable5[358] = {
+ 1600, 1, 1601, 1, 1602, 5, 1603, 5, 1604, 9, 1605, 9, 1606, 13, 1607, 13, // NOLINT
+ 1608, 17, 1609, 17, 1610, 21, 1611, 21, 1612, 25, 1613, 25, 1614, 29, 1615, 29, // NOLINT
+ 1616, 33, 1617, 33, 1618, 37, 1619, 37, 1620, 41, 1621, 41, 1622, 45, 1623, 45, // NOLINT
+ 1624, 49, 1625, 49, 1626, 53, 1627, 53, 1628, 57, 1629, 57, 1630, 61, 1631, 61, // NOLINT
+ 1632, 65, 1633, 65, 1634, 69, 1635, 69, 1636, 73, 1637, 73, 1638, 77, 1639, 77, // NOLINT
+ 1640, 81, 1641, 81, 1642, 85, 1643, 85, 1644, 89, 1645, 89, 1664, 93, 1665, 93, // NOLINT
+ 1666, 97, 1667, 97, 1668, 101, 1669, 101, 1670, 105, 1671, 105, 1672, 109, 1673, 109, // NOLINT
+ 1674, 113, 1675, 113, 1676, 117, 1677, 117, 1678, 121, 1679, 121, 1680, 125, 1681, 125, // NOLINT
+ 1682, 129, 1683, 129, 1684, 133, 1685, 133, 1686, 137, 1687, 137, 1826, 141, 1827, 141, // NOLINT
+ 1828, 145, 1829, 145, 1830, 149, 1831, 149, 1832, 153, 1833, 153, 1834, 157, 1835, 157, // NOLINT
+ 1836, 161, 1837, 161, 1838, 165, 1839, 165, 1842, 169, 1843, 169, 1844, 173, 1845, 173, // NOLINT
+ 1846, 177, 1847, 177, 1848, 181, 1849, 181, 1850, 185, 1851, 185, 1852, 189, 1853, 189, // NOLINT
+ 1854, 193, 1855, 193, 1856, 197, 1857, 197, 1858, 201, 1859, 201, 1860, 205, 1861, 205, // NOLINT
+ 1862, 209, 1863, 209, 1864, 213, 1865, 213, 1866, 217, 1867, 217, 1868, 221, 1869, 221, // NOLINT
+ 1870, 225, 1871, 225, 1872, 229, 1873, 229, 1874, 233, 1875, 233, 1876, 237, 1877, 237, // NOLINT
+ 1878, 241, 1879, 241, 1880, 245, 1881, 245, 1882, 249, 1883, 249, 1884, 253, 1885, 253, // NOLINT
+ 1886, 257, 1887, 257, 1888, 261, 1889, 261, 1890, 265, 1891, 265, 1892, 269, 1893, 269, // NOLINT
+ 1894, 273, 1895, 273, 1896, 277, 1897, 277, 1898, 281, 1899, 281, 1900, 285, 1901, 285, // NOLINT
+ 1902, 289, 1903, 289, 1913, 293, 1914, 293, 1915, 297, 1916, 297, 1917, 301, 1918, 305, // NOLINT
+ 1919, 305, 1920, 309, 1921, 309, 1922, 313, 1923, 313, 1924, 317, 1925, 317, 1926, 321, // NOLINT
+ 1927, 321, 1931, 325, 1932, 325, 1933, 329, 1936, 333, 1937, 333, 1938, 337, 1939, 337, // NOLINT
+ 1952, 341, 1953, 341, 1954, 345, 1955, 345, 1956, 349, 1957, 349, 1958, 353, 1959, 353, // NOLINT
+ 1960, 357, 1961, 357, 1962, 361 }; // NOLINT
+static const uint16_t kEcma262UnCanonicalizeMultiStrings5Size = 92; // NOLINT
+static const MultiCharacterSpecialCase<2> kEcma262UnCanonicalizeMultiStrings7[3] = { // NOLINT
+ {{65313, 65345}}, {{65338, 65370}}, {{kSentinel}} }; // NOLINT
+static const uint16_t kEcma262UnCanonicalizeTable7Size = 4; // NOLINT
+static const int32_t kEcma262UnCanonicalizeTable7[8] = {
+ 1073749793, 1, 7994, 5, 1073749825, 1, 8026, 5 }; // NOLINT
+static const uint16_t kEcma262UnCanonicalizeMultiStrings7Size = 3; // NOLINT
+int Ecma262UnCanonicalize::Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupMapping<true>(kEcma262UnCanonicalizeTable0,
+ kEcma262UnCanonicalizeTable0Size,
+ kEcma262UnCanonicalizeMultiStrings0,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 1: return LookupMapping<true>(kEcma262UnCanonicalizeTable1,
+ kEcma262UnCanonicalizeTable1Size,
+ kEcma262UnCanonicalizeMultiStrings1,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 5: return LookupMapping<true>(kEcma262UnCanonicalizeTable5,
+ kEcma262UnCanonicalizeTable5Size,
+ kEcma262UnCanonicalizeMultiStrings5,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 7: return LookupMapping<true>(kEcma262UnCanonicalizeTable7,
+ kEcma262UnCanonicalizeTable7Size,
+ kEcma262UnCanonicalizeMultiStrings7,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ default: return 0;
+ }
+}
+
+static const MultiCharacterSpecialCase<1> kCanonicalizationRangeMultiStrings0[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kCanonicalizationRangeTable0Size = 70; // NOLINT
+static const int32_t kCanonicalizationRangeTable0[140] = {
+ 1073741889, 100, 90, 0, 1073741921, 100, 122, 0, 1073742016, 88, 214, 0, 1073742040, 24, 222, 0, // NOLINT
+ 1073742048, 88, 246, 0, 1073742072, 24, 254, 0, 1073742715, 8, 893, 0, 1073742728, 8, 906, 0, // NOLINT
+ 1073742749, 8, 927, 0, 1073742759, 16, 939, 0, 1073742765, 8, 943, 0, 1073742781, 8, 959, 0, // NOLINT
+ 1073742791, 16, 971, 0, 1073742845, 8, 1023, 0, 1073742848, 60, 1039, 0, 1073742864, 124, 1071, 0, // NOLINT
+ 1073742896, 124, 1103, 0, 1073742928, 60, 1119, 0, 1073743153, 148, 1366, 0, 1073743201, 148, 1414, 0, // NOLINT
+ 1073746080, 148, 4293, 0, 1073749760, 28, 7943, 0, 1073749768, 28, 7951, 0, 1073749776, 20, 7957, 0, // NOLINT
+ 1073749784, 20, 7965, 0, 1073749792, 28, 7975, 0, 1073749800, 28, 7983, 0, 1073749808, 28, 7991, 0, // NOLINT
+ 1073749816, 28, 7999, 0, 1073749824, 20, 8005, 0, 1073749832, 20, 8013, 0, 1073749856, 28, 8039, 0, // NOLINT
+ 1073749864, 28, 8047, 0, 1073749874, 12, 8053, 0, 1073749960, 12, 8139, 0 }; // NOLINT
+static const uint16_t kCanonicalizationRangeMultiStrings0Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<1> kCanonicalizationRangeMultiStrings1[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kCanonicalizationRangeTable1Size = 14; // NOLINT
+static const int32_t kCanonicalizationRangeTable1[28] = {
+ 1073742176, 60, 367, 0, 1073742192, 60, 383, 0, 1073743030, 100, 1231, 0, 1073743056, 100, 1257, 0, // NOLINT
+ 1073744896, 184, 3118, 0, 1073744944, 184, 3166, 0, 1073745152, 148, 3365, 0 }; // NOLINT
+static const uint16_t kCanonicalizationRangeMultiStrings1Size = 1; // NOLINT
+static const MultiCharacterSpecialCase<1> kCanonicalizationRangeMultiStrings7[1] = { // NOLINT
+ {{kSentinel}} }; // NOLINT
+static const uint16_t kCanonicalizationRangeTable7Size = 4; // NOLINT
+static const int32_t kCanonicalizationRangeTable7[8] = {
+ 1073749793, 100, 7994, 0, 1073749825, 100, 8026, 0 }; // NOLINT
+static const uint16_t kCanonicalizationRangeMultiStrings7Size = 1; // NOLINT
+int CanonicalizationRange::Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr) {
+ int chunk_index = c >> 13;
+ switch (chunk_index) {
+ case 0: return LookupMapping<false>(kCanonicalizationRangeTable0,
+ kCanonicalizationRangeTable0Size,
+ kCanonicalizationRangeMultiStrings0,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 1: return LookupMapping<false>(kCanonicalizationRangeTable1,
+ kCanonicalizationRangeTable1Size,
+ kCanonicalizationRangeMultiStrings1,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ case 7: return LookupMapping<false>(kCanonicalizationRangeTable7,
+ kCanonicalizationRangeTable7Size,
+ kCanonicalizationRangeMultiStrings7,
+ c,
+ n,
+ result,
+ allow_caching_ptr);
+ default: return 0;
+ }
+}
+
+
+const uchar UnicodeData::kMaxCodePoint = 65533;
+
+int UnicodeData::GetByteCount() {
+ return kUppercaseTable0Size * sizeof(int32_t) // NOLINT
+ + kUppercaseTable1Size * sizeof(int32_t) // NOLINT
+ + kUppercaseTable5Size * sizeof(int32_t) // NOLINT
+ + kUppercaseTable7Size * sizeof(int32_t) // NOLINT
+ + kLowercaseTable0Size * sizeof(int32_t) // NOLINT
+ + kLowercaseTable1Size * sizeof(int32_t) // NOLINT
+ + kLowercaseTable5Size * sizeof(int32_t) // NOLINT
+ + kLowercaseTable7Size * sizeof(int32_t) // NOLINT
+ + kLetterTable0Size * sizeof(int32_t) // NOLINT
+ + kLetterTable1Size * sizeof(int32_t) // NOLINT
+ + kLetterTable2Size * sizeof(int32_t) // NOLINT
+ + kLetterTable3Size * sizeof(int32_t) // NOLINT
+ + kLetterTable4Size * sizeof(int32_t) // NOLINT
+ + kLetterTable5Size * sizeof(int32_t) // NOLINT
+ + kLetterTable6Size * sizeof(int32_t) // NOLINT
+ + kLetterTable7Size * sizeof(int32_t) // NOLINT
+ + kSpaceTable0Size * sizeof(int32_t) // NOLINT
+ + kSpaceTable1Size * sizeof(int32_t) // NOLINT
+ + kNumberTable0Size * sizeof(int32_t) // NOLINT
+ + kNumberTable5Size * sizeof(int32_t) // NOLINT
+ + kNumberTable7Size * sizeof(int32_t) // NOLINT
+ + kWhiteSpaceTable0Size * sizeof(int32_t) // NOLINT
+ + kWhiteSpaceTable1Size * sizeof(int32_t) // NOLINT
+ + kLineTerminatorTable0Size * sizeof(int32_t) // NOLINT
+ + kLineTerminatorTable1Size * sizeof(int32_t) // NOLINT
+ + kCombiningMarkTable0Size * sizeof(int32_t) // NOLINT
+ + kCombiningMarkTable1Size * sizeof(int32_t) // NOLINT
+ + kCombiningMarkTable5Size * sizeof(int32_t) // NOLINT
+ + kCombiningMarkTable7Size * sizeof(int32_t) // NOLINT
+ + kConnectorPunctuationTable0Size * sizeof(int32_t) // NOLINT
+ + kConnectorPunctuationTable1Size * sizeof(int32_t) // NOLINT
+ + kConnectorPunctuationTable7Size * sizeof(int32_t) // NOLINT
+ + kToLowercaseMultiStrings0Size * sizeof(MultiCharacterSpecialCase<2>) // NOLINT
+ + kToLowercaseMultiStrings1Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kToLowercaseMultiStrings5Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kToLowercaseMultiStrings7Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kToUppercaseMultiStrings0Size * sizeof(MultiCharacterSpecialCase<3>) // NOLINT
+ + kToUppercaseMultiStrings1Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kToUppercaseMultiStrings5Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kToUppercaseMultiStrings7Size * sizeof(MultiCharacterSpecialCase<3>) // NOLINT
+ + kEcma262CanonicalizeMultiStrings0Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kEcma262CanonicalizeMultiStrings1Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kEcma262CanonicalizeMultiStrings5Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kEcma262CanonicalizeMultiStrings7Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kEcma262UnCanonicalizeMultiStrings0Size * sizeof(MultiCharacterSpecialCase<4>) // NOLINT
+ + kEcma262UnCanonicalizeMultiStrings1Size * sizeof(MultiCharacterSpecialCase<2>) // NOLINT
+ + kEcma262UnCanonicalizeMultiStrings5Size * sizeof(MultiCharacterSpecialCase<2>) // NOLINT
+ + kEcma262UnCanonicalizeMultiStrings7Size * sizeof(MultiCharacterSpecialCase<2>) // NOLINT
+ + kCanonicalizationRangeMultiStrings0Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kCanonicalizationRangeMultiStrings1Size * sizeof(MultiCharacterSpecialCase<1>) // NOLINT
+ + kCanonicalizationRangeMultiStrings7Size * sizeof(MultiCharacterSpecialCase<1>); // NOLINT
+}
+
+} // namespace unicode
diff --git a/chromium/v8/src/unicode.h b/chromium/v8/src/unicode.h
new file mode 100644
index 00000000000..42a81824bac
--- /dev/null
+++ b/chromium/v8/src/unicode.h
@@ -0,0 +1,274 @@
+// 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_UNICODE_H_
+#define V8_UNICODE_H_
+
+#include <sys/types.h>
+#include <globals.h>
+/**
+ * \file
+ * Definitions and convenience functions for working with unicode.
+ */
+
+namespace unibrow {
+
+typedef unsigned int uchar;
+typedef unsigned char byte;
+
+/**
+ * The max length of the result of converting the case of a single
+ * character.
+ */
+const int kMaxMappingSize = 4;
+
+template <class T, int size = 256>
+class Predicate {
+ public:
+ inline Predicate() { }
+ inline bool get(uchar c);
+ private:
+ friend class Test;
+ bool CalculateValue(uchar c);
+ struct CacheEntry {
+ inline CacheEntry() : code_point_(0), value_(0) { }
+ inline CacheEntry(uchar code_point, bool value)
+ : code_point_(code_point),
+ value_(value) { }
+ uchar code_point_ : 21;
+ bool value_ : 1;
+ };
+ static const int kSize = size;
+ static const int kMask = kSize - 1;
+ CacheEntry entries_[kSize];
+};
+
+// A cache used in case conversion. It caches the value for characters
+// that either have no mapping or map to a single character independent
+// of context. Characters that map to more than one character or that
+// map differently depending on context are always looked up.
+template <class T, int size = 256>
+class Mapping {
+ public:
+ inline Mapping() { }
+ inline int get(uchar c, uchar n, uchar* result);
+ private:
+ friend class Test;
+ int CalculateValue(uchar c, uchar n, uchar* result);
+ struct CacheEntry {
+ inline CacheEntry() : code_point_(kNoChar), offset_(0) { }
+ inline CacheEntry(uchar code_point, signed offset)
+ : code_point_(code_point),
+ offset_(offset) { }
+ uchar code_point_;
+ signed offset_;
+ static const int kNoChar = (1 << 21) - 1;
+ };
+ static const int kSize = size;
+ static const int kMask = kSize - 1;
+ CacheEntry entries_[kSize];
+};
+
+class UnicodeData {
+ private:
+ friend class Test;
+ static int GetByteCount();
+ static const uchar kMaxCodePoint;
+};
+
+class Utf16 {
+ public:
+ static inline bool IsLeadSurrogate(int code) {
+ if (code == kNoPreviousCharacter) return false;
+ return (code & 0xfc00) == 0xd800;
+ }
+ static inline bool IsTrailSurrogate(int code) {
+ if (code == kNoPreviousCharacter) return false;
+ return (code & 0xfc00) == 0xdc00;
+ }
+
+ static inline int CombineSurrogatePair(uchar lead, uchar trail) {
+ return 0x10000 + ((lead & 0x3ff) << 10) + (trail & 0x3ff);
+ }
+ static const int kNoPreviousCharacter = -1;
+ static const uchar kMaxNonSurrogateCharCode = 0xffff;
+ // Encoding a single UTF-16 code unit will produce 1, 2 or 3 bytes
+ // of UTF-8 data. The special case where the unit is a surrogate
+ // trail produces 1 byte net, because the encoding of the pair is
+ // 4 bytes and the 3 bytes that were used to encode the lead surrogate
+ // can be reclaimed.
+ static const int kMaxExtraUtf8BytesForOneUtf16CodeUnit = 3;
+ // One UTF-16 surrogate is endoded (illegally) as 3 UTF-8 bytes.
+ // The illegality stems from the surrogate not being part of a pair.
+ static const int kUtf8BytesToCodeASurrogate = 3;
+ static inline uint16_t LeadSurrogate(uint32_t char_code) {
+ return 0xd800 + (((char_code - 0x10000) >> 10) & 0x3ff);
+ }
+ static inline uint16_t TrailSurrogate(uint32_t char_code) {
+ return 0xdc00 + (char_code & 0x3ff);
+ }
+};
+
+class Latin1 {
+ public:
+ static const unsigned kMaxChar = 0xff;
+ // Returns 0 if character does not convert to single latin-1 character
+ // or if the character doesn't not convert back to latin-1 via inverse
+ // operation (upper to lower, etc).
+ static inline uint16_t ConvertNonLatin1ToLatin1(uint16_t);
+};
+
+class Utf8 {
+ public:
+ static inline uchar Length(uchar chr, int previous);
+ static inline unsigned EncodeOneByte(char* out, uint8_t c);
+ static inline unsigned Encode(
+ char* out, uchar c, int previous);
+ static uchar CalculateValue(const byte* str,
+ unsigned length,
+ unsigned* cursor);
+ static const uchar kBadChar = 0xFFFD;
+ static const unsigned kMaxEncodedSize = 4;
+ static const unsigned kMaxOneByteChar = 0x7f;
+ static const unsigned kMaxTwoByteChar = 0x7ff;
+ static const unsigned kMaxThreeByteChar = 0xffff;
+ static const unsigned kMaxFourByteChar = 0x1fffff;
+
+ // A single surrogate is coded as a 3 byte UTF-8 sequence, but two together
+ // that match are coded as a 4 byte UTF-8 sequence.
+ static const unsigned kBytesSavedByCombiningSurrogates = 2;
+ static const unsigned kSizeOfUnmatchedSurrogate = 3;
+ static inline uchar ValueOf(const byte* str,
+ unsigned length,
+ unsigned* cursor);
+};
+
+
+class Utf8DecoderBase {
+ public:
+ // Initialization done in subclass.
+ inline Utf8DecoderBase();
+ inline Utf8DecoderBase(uint16_t* buffer,
+ unsigned buffer_length,
+ const uint8_t* stream,
+ unsigned stream_length);
+ inline unsigned Utf16Length() const { return utf16_length_; }
+ protected:
+ // This reads all characters and sets the utf16_length_.
+ // The first buffer_length utf16 chars are cached in the buffer.
+ void Reset(uint16_t* buffer,
+ unsigned buffer_length,
+ const uint8_t* stream,
+ unsigned stream_length);
+ static void WriteUtf16Slow(const uint8_t* stream,
+ uint16_t* data,
+ unsigned length);
+ const uint8_t* unbuffered_start_;
+ unsigned utf16_length_;
+ bool last_byte_of_buffer_unused_;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Utf8DecoderBase);
+};
+
+template <unsigned kBufferSize>
+class Utf8Decoder : public Utf8DecoderBase {
+ public:
+ inline Utf8Decoder() {}
+ inline Utf8Decoder(const char* stream, unsigned length);
+ inline void Reset(const char* stream, unsigned length);
+ inline unsigned WriteUtf16(uint16_t* data, unsigned length) const;
+ private:
+ uint16_t buffer_[kBufferSize];
+};
+
+
+struct Uppercase {
+ static bool Is(uchar c);
+};
+struct Lowercase {
+ static bool Is(uchar c);
+};
+struct Letter {
+ static bool Is(uchar c);
+};
+struct Space {
+ static bool Is(uchar c);
+};
+struct Number {
+ static bool Is(uchar c);
+};
+struct WhiteSpace {
+ static bool Is(uchar c);
+};
+struct LineTerminator {
+ static bool Is(uchar c);
+};
+struct CombiningMark {
+ static bool Is(uchar c);
+};
+struct ConnectorPunctuation {
+ static bool Is(uchar c);
+};
+struct ToLowercase {
+ static const int kMaxWidth = 3;
+ static int Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr);
+};
+struct ToUppercase {
+ static const int kMaxWidth = 3;
+ static int Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr);
+};
+struct Ecma262Canonicalize {
+ static const int kMaxWidth = 1;
+ static int Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr);
+};
+struct Ecma262UnCanonicalize {
+ static const int kMaxWidth = 4;
+ static int Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr);
+};
+struct CanonicalizationRange {
+ static const int kMaxWidth = 1;
+ static int Convert(uchar c,
+ uchar n,
+ uchar* result,
+ bool* allow_caching_ptr);
+};
+
+} // namespace unibrow
+
+#endif // V8_UNICODE_H_
diff --git a/chromium/v8/src/uri.h b/chromium/v8/src/uri.h
new file mode 100644
index 00000000000..ee1baeb5129
--- /dev/null
+++ b/chromium/v8/src/uri.h
@@ -0,0 +1,309 @@
+// Copyright 2013 the V8 project authors. 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_URI_H_
+#define V8_URI_H_
+
+#include "v8.h"
+
+#include "string-search.h"
+#include "v8utils.h"
+#include "v8conversions.h"
+
+namespace v8 {
+namespace internal {
+
+
+template <typename Char>
+static INLINE(Vector<const Char> GetCharVector(Handle<String> string));
+
+
+template <>
+Vector<const uint8_t> GetCharVector(Handle<String> string) {
+ String::FlatContent flat = string->GetFlatContent();
+ ASSERT(flat.IsAscii());
+ return flat.ToOneByteVector();
+}
+
+
+template <>
+Vector<const uc16> GetCharVector(Handle<String> string) {
+ String::FlatContent flat = string->GetFlatContent();
+ ASSERT(flat.IsTwoByte());
+ return flat.ToUC16Vector();
+}
+
+
+class URIUnescape : public AllStatic {
+ public:
+ template<typename Char>
+ static Handle<String> Unescape(Isolate* isolate, Handle<String> source);
+
+ private:
+ static const signed char kHexValue['g'];
+
+ template<typename Char>
+ static Handle<String> UnescapeSlow(
+ Isolate* isolate, Handle<String> string, int start_index);
+
+ static INLINE(int TwoDigitHex(uint16_t character1, uint16_t character2));
+
+ template <typename Char>
+ static INLINE(int UnescapeChar(Vector<const Char> vector,
+ int i,
+ int length,
+ int* step));
+};
+
+
+const signed char URIUnescape::kHexValue[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15 };
+
+
+template<typename Char>
+Handle<String> URIUnescape::Unescape(Isolate* isolate, Handle<String> source) {
+ int index;
+ { DisallowHeapAllocation no_allocation;
+ StringSearch<uint8_t, Char> search(isolate, STATIC_ASCII_VECTOR("%"));
+ index = search.Search(GetCharVector<Char>(source), 0);
+ if (index < 0) return source;
+ }
+ return UnescapeSlow<Char>(isolate, source, index);
+}
+
+
+template <typename Char>
+Handle<String> URIUnescape::UnescapeSlow(
+ Isolate* isolate, Handle<String> string, int start_index) {
+ bool one_byte = true;
+ int length = string->length();
+
+ int unescaped_length = 0;
+ { DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = start_index; i < length; unescaped_length++) {
+ int step;
+ if (UnescapeChar(vector, i, length, &step) >
+ String::kMaxOneByteCharCode) {
+ one_byte = false;
+ }
+ i += step;
+ }
+ }
+
+ ASSERT(start_index < length);
+ Handle<String> first_part =
+ isolate->factory()->NewProperSubString(string, 0, start_index);
+
+ int dest_position = 0;
+ Handle<String> second_part;
+ if (one_byte) {
+ Handle<SeqOneByteString> dest =
+ isolate->factory()->NewRawOneByteString(unescaped_length);
+ DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = start_index; i < length; dest_position++) {
+ int step;
+ dest->SeqOneByteStringSet(dest_position,
+ UnescapeChar(vector, i, length, &step));
+ i += step;
+ }
+ second_part = dest;
+ } else {
+ Handle<SeqTwoByteString> dest =
+ isolate->factory()->NewRawTwoByteString(unescaped_length);
+ DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = start_index; i < length; dest_position++) {
+ int step;
+ dest->SeqTwoByteStringSet(dest_position,
+ UnescapeChar(vector, i, length, &step));
+ i += step;
+ }
+ second_part = dest;
+ }
+ return isolate->factory()->NewConsString(first_part, second_part);
+}
+
+
+int URIUnescape::TwoDigitHex(uint16_t character1, uint16_t character2) {
+ if (character1 > 'f') return -1;
+ int hi = kHexValue[character1];
+ if (hi == -1) return -1;
+ if (character2 > 'f') return -1;
+ int lo = kHexValue[character2];
+ if (lo == -1) return -1;
+ return (hi << 4) + lo;
+}
+
+
+template <typename Char>
+int URIUnescape::UnescapeChar(Vector<const Char> vector,
+ int i,
+ int length,
+ int* step) {
+ uint16_t character = vector[i];
+ int32_t hi = 0;
+ int32_t lo = 0;
+ if (character == '%' &&
+ i <= length - 6 &&
+ vector[i + 1] == 'u' &&
+ (hi = TwoDigitHex(vector[i + 2],
+ vector[i + 3])) != -1 &&
+ (lo = TwoDigitHex(vector[i + 4],
+ vector[i + 5])) != -1) {
+ *step = 6;
+ return (hi << 8) + lo;
+ } else if (character == '%' &&
+ i <= length - 3 &&
+ (lo = TwoDigitHex(vector[i + 1],
+ vector[i + 2])) != -1) {
+ *step = 3;
+ return lo;
+ } else {
+ *step = 1;
+ return character;
+ }
+}
+
+
+class URIEscape : public AllStatic {
+ public:
+ template<typename Char>
+ static Handle<String> Escape(Isolate* isolate, Handle<String> string);
+
+ private:
+ static const char kHexChars[17];
+ static const char kNotEscaped[256];
+
+ static bool IsNotEscaped(uint16_t c) { return kNotEscaped[c] != 0; }
+};
+
+
+const char URIEscape::kHexChars[] = "0123456789ABCDEF";
+
+
+// kNotEscaped is generated by the following:
+//
+// #!/bin/perl
+// for (my $i = 0; $i < 256; $i++) {
+// print "\n" if $i % 16 == 0;
+// my $c = chr($i);
+// my $escaped = 1;
+// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
+// print $escaped ? "0, " : "1, ";
+// }
+
+const char URIEscape::kNotEscaped[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+
+template<typename Char>
+Handle<String> URIEscape::Escape(Isolate* isolate, Handle<String> string) {
+ ASSERT(string->IsFlat());
+ int escaped_length = 0;
+ int length = string->length();
+
+ { DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = 0; i < length; i++) {
+ uint16_t c = vector[i];
+ if (c >= 256) {
+ escaped_length += 6;
+ } else if (IsNotEscaped(c)) {
+ escaped_length++;
+ } else {
+ escaped_length += 3;
+ }
+
+ // We don't allow strings that are longer than a maximal length.
+ ASSERT(String::kMaxLength < 0x7fffffff - 6); // Cannot overflow.
+ if (escaped_length > String::kMaxLength) {
+ isolate->context()->mark_out_of_memory();
+ return Handle<String>::null();
+ }
+ }
+ }
+
+ // No length change implies no change. Return original string if no change.
+ if (escaped_length == length) return string;
+
+ Handle<SeqOneByteString> dest =
+ isolate->factory()->NewRawOneByteString(escaped_length);
+ int dest_position = 0;
+
+ { DisallowHeapAllocation no_allocation;
+ Vector<const Char> vector = GetCharVector<Char>(string);
+ for (int i = 0; i < length; i++) {
+ uint16_t c = vector[i];
+ if (c >= 256) {
+ dest->SeqOneByteStringSet(dest_position, '%');
+ dest->SeqOneByteStringSet(dest_position+1, 'u');
+ dest->SeqOneByteStringSet(dest_position+2, kHexChars[c >> 12]);
+ dest->SeqOneByteStringSet(dest_position+3, kHexChars[(c >> 8) & 0xf]);
+ dest->SeqOneByteStringSet(dest_position+4, kHexChars[(c >> 4) & 0xf]);
+ dest->SeqOneByteStringSet(dest_position+5, kHexChars[c & 0xf]);
+ dest_position += 6;
+ } else if (IsNotEscaped(c)) {
+ dest->SeqOneByteStringSet(dest_position, c);
+ dest_position++;
+ } else {
+ dest->SeqOneByteStringSet(dest_position, '%');
+ dest->SeqOneByteStringSet(dest_position+1, kHexChars[c >> 4]);
+ dest->SeqOneByteStringSet(dest_position+2, kHexChars[c & 0xf]);
+ dest_position += 3;
+ }
+ }
+ }
+
+ return dest;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_URI_H_
diff --git a/chromium/v8/src/uri.js b/chromium/v8/src/uri.js
new file mode 100644
index 00000000000..4e3f084af27
--- /dev/null
+++ b/chromium/v8/src/uri.js
@@ -0,0 +1,457 @@
+// 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.
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js:
+// var $Array = global.Array;
+
+// -------------------------------------------------------------------
+
+// This file contains support for URI manipulations written in
+// JavaScript.
+
+// Lazily initialized.
+var hexCharArray = 0;
+var hexCharCodeArray = 0;
+
+
+function URIAddEncodedOctetToBuffer(octet, result, index) {
+ result[index++] = 37; // Char code of '%'.
+ result[index++] = hexCharCodeArray[octet >> 4];
+ result[index++] = hexCharCodeArray[octet & 0x0F];
+ return index;
+}
+
+
+function URIEncodeOctets(octets, result, index) {
+ if (hexCharCodeArray === 0) {
+ hexCharCodeArray = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 65, 66, 67, 68, 69, 70];
+ }
+ index = URIAddEncodedOctetToBuffer(octets[0], result, index);
+ if (octets[1]) index = URIAddEncodedOctetToBuffer(octets[1], result, index);
+ if (octets[2]) index = URIAddEncodedOctetToBuffer(octets[2], result, index);
+ if (octets[3]) index = URIAddEncodedOctetToBuffer(octets[3], result, index);
+ return index;
+}
+
+
+function URIEncodeSingle(cc, result, index) {
+ var x = (cc >> 12) & 0xF;
+ var y = (cc >> 6) & 63;
+ var z = cc & 63;
+ var octets = new $Array(3);
+ if (cc <= 0x007F) {
+ octets[0] = cc;
+ } else if (cc <= 0x07FF) {
+ octets[0] = y + 192;
+ octets[1] = z + 128;
+ } else {
+ octets[0] = x + 224;
+ octets[1] = y + 128;
+ octets[2] = z + 128;
+ }
+ return URIEncodeOctets(octets, result, index);
+}
+
+
+function URIEncodePair(cc1 , cc2, result, index) {
+ var u = ((cc1 >> 6) & 0xF) + 1;
+ var w = (cc1 >> 2) & 0xF;
+ var x = cc1 & 3;
+ var y = (cc2 >> 6) & 0xF;
+ var z = cc2 & 63;
+ var octets = new $Array(4);
+ octets[0] = (u >> 2) + 240;
+ octets[1] = (((u & 3) << 4) | w) + 128;
+ octets[2] = ((x << 4) | y) + 128;
+ octets[3] = z + 128;
+ return URIEncodeOctets(octets, result, index);
+}
+
+
+function URIHexCharsToCharCode(highChar, lowChar) {
+ var highCode = HexValueOf(highChar);
+ var lowCode = HexValueOf(lowChar);
+ if (highCode == -1 || lowCode == -1) {
+ throw new $URIError("URI malformed");
+ }
+ return (highCode << 4) | lowCode;
+}
+
+
+function URIDecodeOctets(octets, result, index) {
+ var value;
+ var o0 = octets[0];
+ if (o0 < 0x80) {
+ value = o0;
+ } else if (o0 < 0xc2) {
+ throw new $URIError("URI malformed");
+ } else {
+ var o1 = octets[1];
+ if (o0 < 0xe0) {
+ var a = o0 & 0x1f;
+ if ((o1 < 0x80) || (o1 > 0xbf)) {
+ throw new $URIError("URI malformed");
+ }
+ var b = o1 & 0x3f;
+ value = (a << 6) + b;
+ 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)) {
+ throw new $URIError("URI malformed");
+ }
+ var b = o1 & 0x3f;
+ 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)) {
+ throw new $URIError("URI malformed");
+ }
+ } else {
+ var o3 = octets[3];
+ if (o0 < 0xf8) {
+ var a = (o0 & 0x07);
+ if ((o1 < 0x80) || (o1 > 0xbf)) {
+ throw new $URIError("URI malformed");
+ }
+ var b = (o1 & 0x3f);
+ if ((o2 < 0x80) || (o2 > 0xbf)) {
+ throw new $URIError("URI malformed");
+ }
+ var c = (o2 & 0x3f);
+ 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)) {
+ throw new $URIError("URI malformed");
+ }
+ } else {
+ throw new $URIError("URI malformed");
+ }
+ }
+ }
+ }
+ if (0xD800 <= value && value <= 0xDFFF) {
+ throw new $URIError("URI malformed");
+ }
+ if (value < 0x10000) {
+ %_TwoByteSeqStringSetChar(result, index++, value);
+ return index;
+ } else {
+ %_TwoByteSeqStringSetChar(result, index++, (value >> 10) + 0xd7c0);
+ %_TwoByteSeqStringSetChar(result, index++, (value & 0x3ff) + 0xdc00);
+ return index;
+ }
+}
+
+
+// ECMA-262, section 15.1.3
+function Encode(uri, unescape) {
+ var uriLength = uri.length;
+ var array = new InternalArray(uriLength);
+ var index = 0;
+ for (var k = 0; k < uriLength; k++) {
+ var cc1 = uri.charCodeAt(k);
+ if (unescape(cc1)) {
+ array[index++] = cc1;
+ } else {
+ if (cc1 >= 0xDC00 && cc1 <= 0xDFFF) throw new $URIError("URI malformed");
+ if (cc1 < 0xD800 || cc1 > 0xDBFF) {
+ index = URIEncodeSingle(cc1, array, index);
+ } else {
+ k++;
+ if (k == uriLength) throw new $URIError("URI malformed");
+ var cc2 = uri.charCodeAt(k);
+ if (cc2 < 0xDC00 || cc2 > 0xDFFF) throw new $URIError("URI malformed");
+ index = URIEncodePair(cc1, cc2, array, index);
+ }
+ }
+ }
+
+ var result = %NewString(array.length, NEW_ONE_BYTE_STRING);
+ for (var i = 0; i < array.length; i++) {
+ %_OneByteSeqStringSetChar(result, i, array[i]);
+ }
+ return result;
+}
+
+
+// ECMA-262, section 15.1.3
+function Decode(uri, reserved) {
+ var uriLength = uri.length;
+ var one_byte = %NewString(uriLength, NEW_ONE_BYTE_STRING);
+ var index = 0;
+ var k = 0;
+
+ // Optimistically assume ascii string.
+ for ( ; k < uriLength; k++) {
+ var code = uri.charCodeAt(k);
+ if (code == 37) { // '%'
+ if (k + 2 >= uriLength) throw new $URIError("URI malformed");
+ var cc = URIHexCharsToCharCode(uri.charCodeAt(k+1), uri.charCodeAt(k+2));
+ if (cc >> 7) break; // Assumption wrong, two byte string.
+ if (reserved(cc)) {
+ %_OneByteSeqStringSetChar(one_byte, index++, 37); // '%'.
+ %_OneByteSeqStringSetChar(one_byte, index++, uri.charCodeAt(k+1));
+ %_OneByteSeqStringSetChar(one_byte, index++, uri.charCodeAt(k+2));
+ } else {
+ %_OneByteSeqStringSetChar(one_byte, index++, cc);
+ }
+ k += 2;
+ } else {
+ if (code > 0x7f) break; // Assumption wrong, two byte string.
+ %_OneByteSeqStringSetChar(one_byte, index++, code);
+ }
+ }
+
+ one_byte = %TruncateString(one_byte, index);
+ if (k == uriLength) return one_byte;
+
+ // Write into two byte string.
+ var two_byte = %NewString(uriLength - k, NEW_TWO_BYTE_STRING);
+ index = 0;
+
+ for ( ; k < uriLength; k++) {
+ var code = uri.charCodeAt(k);
+ if (code == 37) { // '%'
+ if (k + 2 >= uriLength) throw new $URIError("URI malformed");
+ var cc = URIHexCharsToCharCode(uri.charCodeAt(++k), uri.charCodeAt(++k));
+ if (cc >> 7) {
+ var n = 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));
+ }
+ index = URIDecodeOctets(octets, two_byte, index);
+ } else if (reserved(cc)) {
+ %_TwoByteSeqStringSetChar(two_byte, index++, 37); // '%'.
+ %_TwoByteSeqStringSetChar(two_byte, index++, uri.charCodeAt(k - 1));
+ %_TwoByteSeqStringSetChar(two_byte, index++, uri.charCodeAt(k));
+ } else {
+ %_TwoByteSeqStringSetChar(two_byte, index++, cc);
+ }
+ } else {
+ %_TwoByteSeqStringSetChar(two_byte, index++, code);
+ }
+ }
+
+ two_byte = %TruncateString(two_byte, index);
+ return one_byte + two_byte;
+}
+
+
+// ECMA-262 - 15.1.3.1.
+function URIDecode(uri) {
+ var reservedPredicate = function(cc) {
+ // #$
+ if (35 <= cc && cc <= 36) return true;
+ // &
+ if (cc == 38) return true;
+ // +,
+ if (43 <= cc && cc <= 44) return true;
+ // /
+ if (cc == 47) return true;
+ // :;
+ if (58 <= cc && cc <= 59) return true;
+ // =
+ if (cc == 61) return true;
+ // ?@
+ if (63 <= cc && cc <= 64) return true;
+
+ return false;
+ };
+ var string = ToString(uri);
+ return Decode(string, reservedPredicate);
+}
+
+
+// ECMA-262 - 15.1.3.2.
+function URIDecodeComponent(component) {
+ var reservedPredicate = function(cc) { return false; };
+ var string = ToString(component);
+ return Decode(string, reservedPredicate);
+}
+
+
+// Does the char code correspond to an alpha-numeric char.
+function isAlphaNumeric(cc) {
+ // a - z
+ if (97 <= cc && cc <= 122) return true;
+ // A - Z
+ if (65 <= cc && cc <= 90) return true;
+ // 0 - 9
+ if (48 <= cc && cc <= 57) return true;
+
+ return false;
+}
+
+
+// ECMA-262 - 15.1.3.3.
+function URIEncode(uri) {
+ var unescapePredicate = function(cc) {
+ if (isAlphaNumeric(cc)) return true;
+ // !
+ if (cc == 33) return true;
+ // #$
+ if (35 <= cc && cc <= 36) return true;
+ // &'()*+,-./
+ if (38 <= cc && cc <= 47) return true;
+ // :;
+ if (58 <= cc && cc <= 59) return true;
+ // =
+ if (cc == 61) return true;
+ // ?@
+ if (63 <= cc && cc <= 64) return true;
+ // _
+ if (cc == 95) return true;
+ // ~
+ if (cc == 126) return true;
+
+ return false;
+ };
+
+ var string = ToString(uri);
+ return Encode(string, unescapePredicate);
+}
+
+
+// ECMA-262 - 15.1.3.4
+function URIEncodeComponent(component) {
+ var unescapePredicate = function(cc) {
+ if (isAlphaNumeric(cc)) return true;
+ // !
+ if (cc == 33) return true;
+ // '()*
+ if (39 <= cc && cc <= 42) return true;
+ // -.
+ if (45 <= cc && cc <= 46) return true;
+ // _
+ if (cc == 95) return true;
+ // ~
+ if (cc == 126) return true;
+
+ return false;
+ };
+
+ var string = ToString(component);
+ return Encode(string, unescapePredicate);
+}
+
+
+function HexValueOf(code) {
+ // 0-9
+ if (code >= 48 && code <= 57) return code - 48;
+ // A-F
+ if (code >= 65 && code <= 70) return code - 55;
+ // a-f
+ if (code >= 97 && code <= 102) return code - 87;
+
+ return -1;
+}
+
+
+// Convert a character code to 4-digit hex string representation
+// 64 -> 0040, 62234 -> F31A.
+function CharCodeToHex4Str(cc) {
+ var r = "";
+ if (hexCharArray === 0) {
+ hexCharArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "A", "B", "C", "D", "E", "F"];
+ }
+ for (var i = 0; i < 4; ++i) {
+ var c = hexCharArray[cc & 0x0F];
+ r = c + r;
+ cc = cc >>> 4;
+ }
+ return r;
+}
+
+
+// Returns true if all digits in string s are valid hex numbers
+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)) {
+ // '0'..'9', 'A'..'F' and 'a' .. 'f'.
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// ECMA-262 - B.2.1.
+function URIEscape(str) {
+ var s = ToString(str);
+ return %URIEscape(s);
+}
+
+
+// ECMA-262 - B.2.2.
+function URIUnescape(str) {
+ var s = ToString(str);
+ return %URIUnescape(s);
+}
+
+
+// -------------------------------------------------------------------
+
+function SetUpUri() {
+ %CheckIsBootstrapping();
+
+ // Set up non-enumerable URI functions on the global object and set
+ // their names.
+ InstallFunctions(global, DONT_ENUM, $Array(
+ "escape", URIEscape,
+ "unescape", URIUnescape,
+ "decodeURI", URIDecode,
+ "decodeURIComponent", URIDecodeComponent,
+ "encodeURI", URIEncode,
+ "encodeURIComponent", URIEncodeComponent
+ ));
+}
+
+SetUpUri();
diff --git a/chromium/v8/src/utils-inl.h b/chromium/v8/src/utils-inl.h
new file mode 100644
index 00000000000..76a3c104ef5
--- /dev/null
+++ b/chromium/v8/src/utils-inl.h
@@ -0,0 +1,48 @@
+// 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_UTILS_INL_H_
+#define V8_UTILS_INL_H_
+
+#include "list-inl.h"
+
+namespace v8 {
+namespace internal {
+
+template<typename T, int growth_factor, int max_growth>
+void Collector<T, growth_factor, max_growth>::Reset() {
+ for (int i = chunks_.length() - 1; i >= 0; i--) {
+ chunks_.at(i).Dispose();
+ }
+ chunks_.Rewind(0);
+ index_ = 0;
+ size_ = 0;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_UTILS_INL_H_
diff --git a/chromium/v8/src/utils.cc b/chromium/v8/src/utils.cc
new file mode 100644
index 00000000000..8462615200a
--- /dev/null
+++ b/chromium/v8/src/utils.cc
@@ -0,0 +1,114 @@
+// 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 <stdarg.h>
+#include "../include/v8stdint.h"
+#include "checks.h"
+#include "platform.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+
+SimpleStringBuilder::SimpleStringBuilder(int size) {
+ buffer_ = Vector<char>::New(size);
+ position_ = 0;
+}
+
+
+void SimpleStringBuilder::AddString(const char* s) {
+ AddSubstring(s, StrLength(s));
+}
+
+
+void SimpleStringBuilder::AddSubstring(const char* s, int n) {
+ ASSERT(!is_finalized() && position_ + n <= buffer_.length());
+ ASSERT(static_cast<size_t>(n) <= strlen(s));
+ OS::MemCopy(&buffer_[position_], s, n * kCharSize);
+ position_ += n;
+}
+
+
+void SimpleStringBuilder::AddPadding(char c, int count) {
+ for (int i = 0; i < count; i++) {
+ AddCharacter(c);
+ }
+}
+
+
+void SimpleStringBuilder::AddDecimalInteger(int32_t value) {
+ uint32_t number = static_cast<uint32_t>(value);
+ if (value < 0) {
+ AddCharacter('-');
+ number = static_cast<uint32_t>(-value);
+ }
+ int digits = 1;
+ for (uint32_t factor = 10; digits < 10; digits++, factor *= 10) {
+ if (factor > number) break;
+ }
+ position_ += digits;
+ for (int i = 1; i <= digits; i++) {
+ buffer_[position_ - i] = '0' + static_cast<char>(number % 10);
+ number /= 10;
+ }
+}
+
+
+char* SimpleStringBuilder::Finalize() {
+ ASSERT(!is_finalized() && position_ <= buffer_.length());
+ // If there is no space for null termination, overwrite last character.
+ if (position_ == buffer_.length()) {
+ position_--;
+ // Print ellipsis.
+ for (int i = 3; i > 0 && position_ > i; --i) buffer_[position_ - i] = '.';
+ }
+ buffer_[position_] = '\0';
+ // Make sure nobody managed to add a 0-character to the
+ // buffer while building the string.
+ ASSERT(strlen(buffer_.start()) == static_cast<size_t>(position_));
+ position_ = -1;
+ ASSERT(is_finalized());
+ return buffer_.start();
+}
+
+
+const DivMagicNumbers DivMagicNumberFor(int32_t divisor) {
+ switch (divisor) {
+ case 3: return DivMagicNumberFor3;
+ case 5: return DivMagicNumberFor5;
+ case 7: return DivMagicNumberFor7;
+ case 9: return DivMagicNumberFor9;
+ case 11: return DivMagicNumberFor11;
+ case 25: return DivMagicNumberFor25;
+ case 125: return DivMagicNumberFor125;
+ case 625: return DivMagicNumberFor625;
+ default: return InvalidDivMagicNumber;
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/utils.h b/chromium/v8/src/utils.h
new file mode 100644
index 00000000000..4a08319044b
--- /dev/null
+++ b/chromium/v8/src/utils.h
@@ -0,0 +1,1144 @@
+// Copyright 2012 the V8 project authors. 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_UTILS_H_
+#define V8_UTILS_H_
+
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <climits>
+
+#include "allocation.h"
+#include "checks.h"
+#include "globals.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// General helper functions
+
+#define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0)
+
+// 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>
+inline bool IsPowerOf2(T x) {
+ return IS_POWER_OF_TWO(x);
+}
+
+
+// X must be a power of 2. Returns the number of trailing zeros.
+inline int WhichPowerOf2(uint32_t x) {
+ ASSERT(IsPowerOf2(x));
+ ASSERT(x != 0);
+ int bits = 0;
+#ifdef DEBUG
+ int original_x = x;
+#endif
+ if (x >= 0x10000) {
+ bits += 16;
+ x >>= 16;
+ }
+ if (x >= 0x100) {
+ bits += 8;
+ x >>= 8;
+ }
+ if (x >= 0x10) {
+ bits += 4;
+ x >>= 4;
+ }
+ switch (x) {
+ default: UNREACHABLE();
+ case 8: bits++; // Fall through.
+ case 4: bits++; // Fall through.
+ case 2: bits++; // Fall through.
+ case 1: break;
+ }
+ ASSERT_EQ(1 << bits, original_x);
+ return bits;
+ return 0;
+}
+
+
+inline int MostSignificantBit(uint32_t x) {
+ static const int msb4[] = {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4};
+ int nibble = 0;
+ if (x & 0xffff0000) {
+ nibble += 16;
+ x >>= 16;
+ }
+ if (x & 0xff00) {
+ nibble += 8;
+ x >>= 8;
+ }
+ if (x & 0xf0) {
+ nibble += 4;
+ x >>= 4;
+ }
+ return nibble + msb4[x];
+}
+
+
+// Magic numbers for integer division.
+// These are kind of 2's complement reciprocal of the divisors.
+// Details and proofs can be found in:
+// - Hacker's Delight, Henry S. Warren, Jr.
+// - The PowerPC Compiler Writer’s Guide
+// and probably many others.
+// See details in the implementation of the algorithm in
+// lithium-codegen-arm.cc : LCodeGen::TryEmitSignedIntegerDivisionByConstant().
+struct DivMagicNumbers {
+ unsigned M;
+ unsigned s;
+};
+
+const DivMagicNumbers InvalidDivMagicNumber= {0, 0};
+const DivMagicNumbers DivMagicNumberFor3 = {0x55555556, 0};
+const DivMagicNumbers DivMagicNumberFor5 = {0x66666667, 1};
+const DivMagicNumbers DivMagicNumberFor7 = {0x92492493, 2};
+const DivMagicNumbers DivMagicNumberFor9 = {0x38e38e39, 1};
+const DivMagicNumbers DivMagicNumberFor11 = {0x2e8ba2e9, 1};
+const DivMagicNumbers DivMagicNumberFor25 = {0x51eb851f, 3};
+const DivMagicNumbers DivMagicNumberFor125 = {0x10624dd3, 3};
+const DivMagicNumbers DivMagicNumberFor625 = {0x68db8bad, 8};
+
+const DivMagicNumbers DivMagicNumberFor(int32_t divisor);
+
+
+// The C++ standard leaves the semantics of '>>' undefined for
+// negative signed operands. Most implementations do the right thing,
+// though.
+inline int ArithmeticShiftRight(int x, int s) {
+ return x >> s;
+}
+
+
+// Compute the 0-relative offset of some absolute value x of type T.
+// This allows conversion of Addresses and integral types into
+// 0-relative int offsets.
+template <typename T>
+inline intptr_t OffsetFrom(T x) {
+ return x - static_cast<T>(0);
+}
+
+
+// Compute the absolute value of type T for some 0-relative offset x.
+// This allows conversion of 0-relative int offsets into Addresses and
+// integral types.
+template <typename T>
+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>
+inline T RoundDown(T x, intptr_t m) {
+ ASSERT(IsPowerOf2(m));
+ return AddressFrom<T>(OffsetFrom(x) & -m);
+}
+
+
+// Return the smallest multiple of m which is >= x.
+template <typename T>
+inline T RoundUp(T x, intptr_t m) {
+ return RoundDown<T>(static_cast<T>(x + m - 1), m);
+}
+
+
+template <typename T>
+int Compare(const T& a, const T& b) {
+ if (a == b)
+ return 0;
+ else if (a < b)
+ return -1;
+ else
+ return 1;
+}
+
+
+template <typename T>
+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.
+inline uint32_t RoundUpToPowerOf2(uint32_t x) {
+ ASSERT(x <= 0x80000000u);
+ x = x - 1;
+ x = x | (x >> 1);
+ x = x | (x >> 2);
+ x = x | (x >> 4);
+ x = x | (x >> 8);
+ x = x | (x >> 16);
+ return x + 1;
+}
+
+
+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, typename U>
+inline bool IsAligned(T value, U alignment) {
+ return (value & (alignment - 1)) == 0;
+}
+
+
+// Returns true if (addr + offset) is aligned.
+inline bool IsAddressAligned(Address addr,
+ intptr_t alignment,
+ int offset = 0) {
+ intptr_t offs = OffsetFrom(addr + offset);
+ return IsAligned(offs, alignment);
+}
+
+
+// Returns the maximum of the two parameters.
+template <typename T>
+T Max(T a, T b) {
+ return a < b ? b : a;
+}
+
+
+// Returns the minimum of the two parameters.
+template <typename T>
+T Min(T a, T b) {
+ return a < b ? a : b;
+}
+
+
+// Returns the absolute value of its argument.
+template <typename T>
+T Abs(T a) {
+ return a < 0 ? -a : a;
+}
+
+
+// Returns the negative absolute value of its argument.
+template <typename T>
+T NegAbs(T a) {
+ return a < 0 ? a : -a;
+}
+
+
+inline int StrLength(const char* string) {
+ size_t length = strlen(string);
+ ASSERT(length == static_cast<size_t>(static_cast<int>(length)));
+ return static_cast<int>(length);
+}
+
+
+// ----------------------------------------------------------------------------
+// BitField is a help template for encoding and decode bitfield with
+// unsigned content.
+
+template<class T, int shift, int size, class U>
+class BitFieldBase {
+ public:
+ // A type U mask of bit field. To use all bits of a type U of x bits
+ // in a bitfield without compiler warnings we have to compute 2^x
+ // without using a shift count of x in the computation.
+ static const U kOne = static_cast<U>(1U);
+ static const U kMask = ((kOne << shift) << size) - (kOne << shift);
+ static const U kShift = shift;
+ static const U kSize = size;
+
+ // Value for the field with all bits set.
+ static const T kMax = static_cast<T>((1U << size) - 1);
+
+ // Tells whether the provided value fits into the bit field.
+ static bool is_valid(T value) {
+ return (static_cast<U>(value) & ~static_cast<U>(kMax)) == 0;
+ }
+
+ // Returns a type U with the bit field value encoded.
+ static U encode(T value) {
+ ASSERT(is_valid(value));
+ return static_cast<U>(value) << shift;
+ }
+
+ // Returns a type U with the bit field value updated.
+ static U update(U previous, T value) {
+ return (previous & ~kMask) | encode(value);
+ }
+
+ // Extracts the bit field from the value.
+ static T decode(U value) {
+ return static_cast<T>((value & kMask) >> shift);
+ }
+};
+
+
+template<class T, int shift, int size>
+class BitField : public BitFieldBase<T, shift, size, uint32_t> { };
+
+
+template<class T, int shift, int size>
+class BitField64 : public BitFieldBase<T, shift, size, uint64_t> { };
+
+
+// ----------------------------------------------------------------------------
+// Hash function.
+
+static const uint32_t kZeroHashSeed = 0;
+
+// Thomas Wang, Integer Hash Functions.
+// http://www.concentric.net/~Ttwang/tech/inthash.htm
+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;
+ hash = hash ^ (hash >> 12);
+ hash = hash + (hash << 2);
+ hash = hash ^ (hash >> 4);
+ hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11);
+ hash = hash ^ (hash >> 16);
+ return hash;
+}
+
+
+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 static_cast<uint32_t>(hash);
+}
+
+
+inline uint32_t ComputePointerHash(void* ptr) {
+ return ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<intptr_t>(ptr)),
+ v8::internal::kZeroHashSeed);
+}
+
+
+// ----------------------------------------------------------------------------
+// Miscellaneous
+
+// A static resource holds a static instance that can be reserved in
+// a local scope using an instance of Access. Attempts to re-reserve
+// the instance will cause an error.
+template <typename T>
+class StaticResource {
+ public:
+ StaticResource() : is_reserved_(false) {}
+
+ private:
+ template <typename S> friend class Access;
+ T instance_;
+ bool is_reserved_;
+};
+
+
+// Locally scoped access to a static resource.
+template <typename T>
+class Access {
+ public:
+ explicit Access(StaticResource<T>* resource)
+ : resource_(resource)
+ , instance_(&resource->instance_) {
+ ASSERT(!resource->is_reserved_);
+ resource->is_reserved_ = true;
+ }
+
+ ~Access() {
+ resource_->is_reserved_ = false;
+ resource_ = NULL;
+ instance_ = NULL;
+ }
+
+ T* value() { return instance_; }
+ T* operator -> () { return instance_; }
+
+ private:
+ StaticResource<T>* resource_;
+ T* instance_;
+};
+
+
+template <typename T>
+class Vector {
+ public:
+ Vector() : start_(NULL), length_(0) {}
+ Vector(T* data, int length) : start_(data), length_(length) {
+ ASSERT(length == 0 || (length > 0 && data != NULL));
+ }
+
+ static Vector<T> New(int length) {
+ return Vector<T>(NewArray<T>(length), length);
+ }
+
+ // Returns a vector using the same backing storage as this one,
+ // spanning from and including 'from', to but not including 'to'.
+ Vector<T> SubVector(int from, int to) {
+ ASSERT(to <= length_);
+ ASSERT(from < to);
+ ASSERT(0 <= from);
+ return Vector<T>(start() + from, to - from);
+ }
+
+ // Returns the length of the vector.
+ int length() const { return length_; }
+
+ // Returns whether or not the vector is empty.
+ bool is_empty() const { return length_ == 0; }
+
+ // Returns the pointer to the start of the data in the vector.
+ T* start() const { return start_; }
+
+ // Access individual vector elements - checks bounds in debug mode.
+ T& operator[](int index) const {
+ ASSERT(0 <= index && index < length_);
+ return start_[index];
+ }
+
+ const T& at(int index) const { return operator[](index); }
+
+ T& first() { return start_[0]; }
+
+ T& last() { return start_[length_ - 1]; }
+
+ // Returns a clone of this vector with a new backing store.
+ Vector<T> Clone() const {
+ T* result = NewArray<T>(length_);
+ for (int i = 0; i < length_; i++) result[i] = start_[i];
+ return Vector<T>(result, length_);
+ }
+
+ void Sort(int (*cmp)(const T*, const T*)) {
+ std::sort(start(), start() + length(), RawComparer(cmp));
+ }
+
+ void Sort() {
+ std::sort(start(), start() + length());
+ }
+
+ void Truncate(int length) {
+ ASSERT(length <= length_);
+ length_ = length;
+ }
+
+ // Releases the array underlying this vector. Once disposed the
+ // vector is empty.
+ void Dispose() {
+ DeleteArray(start_);
+ start_ = NULL;
+ length_ = 0;
+ }
+
+ inline Vector<T> operator+(int offset) {
+ ASSERT(offset < length_);
+ return Vector<T>(start_ + offset, length_ - offset);
+ }
+
+ // Factory method for creating empty vectors.
+ static Vector<T> empty() { return Vector<T>(NULL, 0); }
+
+ template<typename S>
+ static Vector<T> cast(Vector<S> input) {
+ return Vector<T>(reinterpret_cast<T*>(input.start()),
+ input.length() * sizeof(S) / sizeof(T));
+ }
+
+ protected:
+ void set_start(T* start) { start_ = start; }
+
+ private:
+ T* start_;
+ int length_;
+
+ class RawComparer {
+ public:
+ explicit RawComparer(int (*cmp)(const T*, const T*)) : cmp_(cmp) {}
+ bool operator()(const T& a, const T& b) {
+ return cmp_(&a, &b) < 0;
+ }
+
+ private:
+ int (*cmp_)(const T*, const T*);
+ };
+};
+
+
+// A pointer that can only be set once and doesn't allow NULL values.
+template<typename T>
+class SetOncePointer {
+ public:
+ SetOncePointer() : pointer_(NULL) { }
+
+ bool is_set() const { return pointer_ != NULL; }
+
+ T* get() const {
+ ASSERT(pointer_ != NULL);
+ return pointer_;
+ }
+
+ void set(T* value) {
+ ASSERT(pointer_ == NULL && value != NULL);
+ pointer_ = value;
+ }
+
+ private:
+ T* pointer_;
+};
+
+
+template <typename T, int kSize>
+class EmbeddedVector : public Vector<T> {
+ public:
+ EmbeddedVector() : Vector<T>(buffer_, kSize) { }
+
+ explicit EmbeddedVector(T initial_value) : Vector<T>(buffer_, kSize) {
+ for (int i = 0; i < kSize; ++i) {
+ buffer_[i] = initial_value;
+ }
+ }
+
+ // When copying, make underlying Vector to reference our buffer.
+ EmbeddedVector(const EmbeddedVector& rhs)
+ : Vector<T>(rhs) {
+ // TODO(jkummerow): Refactor #includes and use OS::MemCopy() instead.
+ memcpy(buffer_, rhs.buffer_, sizeof(T) * kSize);
+ set_start(buffer_);
+ }
+
+ EmbeddedVector& operator=(const EmbeddedVector& rhs) {
+ if (this == &rhs) return *this;
+ Vector<T>::operator=(rhs);
+ // TODO(jkummerow): Refactor #includes and use OS::MemCopy() instead.
+ memcpy(buffer_, rhs.buffer_, sizeof(T) * kSize);
+ this->set_start(buffer_);
+ return *this;
+ }
+
+ private:
+ T buffer_[kSize];
+};
+
+
+template <typename T>
+class ScopedVector : public Vector<T> {
+ public:
+ explicit ScopedVector(int length) : Vector<T>(NewArray<T>(length), length) { }
+ ~ScopedVector() {
+ DeleteArray(this->start());
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedVector);
+};
+
+#define STATIC_ASCII_VECTOR(x) \
+ v8::internal::Vector<const uint8_t>(reinterpret_cast<const uint8_t*>(x), \
+ ARRAY_SIZE(x)-1)
+
+inline Vector<const char> CStrVector(const char* data) {
+ return Vector<const char>(data, StrLength(data));
+}
+
+inline Vector<const uint8_t> OneByteVector(const char* data, int length) {
+ return Vector<const uint8_t>(reinterpret_cast<const uint8_t*>(data), length);
+}
+
+inline Vector<const uint8_t> OneByteVector(const char* data) {
+ return OneByteVector(data, StrLength(data));
+}
+
+inline Vector<char> MutableCStrVector(char* data) {
+ return Vector<char>(data, StrLength(data));
+}
+
+inline Vector<char> MutableCStrVector(char* data, int max) {
+ int length = StrLength(data);
+ return Vector<char>(data, (length < max) ? length : max);
+}
+
+
+/*
+ * A class that collects values into a backing store.
+ * Specialized versions of the class can allow access to the backing store
+ * in different ways.
+ * There is no guarantee that the backing store is contiguous (and, as a
+ * consequence, no guarantees that consecutively added elements are adjacent
+ * in memory). The collector may move elements unless it has guaranteed not
+ * to.
+ */
+template <typename T, int growth_factor = 2, int max_growth = 1 * MB>
+class Collector {
+ public:
+ explicit Collector(int initial_capacity = kMinCapacity)
+ : index_(0), size_(0) {
+ current_chunk_ = Vector<T>::New(initial_capacity);
+ }
+
+ virtual ~Collector() {
+ // Free backing store (in reverse allocation order).
+ current_chunk_.Dispose();
+ for (int i = chunks_.length() - 1; i >= 0; i--) {
+ chunks_.at(i).Dispose();
+ }
+ }
+
+ // Add a single element.
+ inline void Add(T value) {
+ if (index_ >= current_chunk_.length()) {
+ Grow(1);
+ }
+ current_chunk_[index_] = value;
+ index_++;
+ size_++;
+ }
+
+ // Add a block of contiguous elements and return a Vector backed by the
+ // memory area.
+ // A basic Collector will keep this vector valid as long as the Collector
+ // is alive.
+ inline Vector<T> AddBlock(int size, T initial_value) {
+ ASSERT(size > 0);
+ if (size > current_chunk_.length() - index_) {
+ Grow(size);
+ }
+ T* position = current_chunk_.start() + index_;
+ index_ += size;
+ size_ += size;
+ for (int i = 0; i < size; i++) {
+ position[i] = initial_value;
+ }
+ return Vector<T>(position, size);
+ }
+
+
+ // Add a contiguous block of elements and return a vector backed
+ // by the added block.
+ // A basic Collector will keep this vector valid as long as the Collector
+ // is alive.
+ inline Vector<T> AddBlock(Vector<const T> source) {
+ if (source.length() > current_chunk_.length() - index_) {
+ Grow(source.length());
+ }
+ T* position = current_chunk_.start() + index_;
+ index_ += source.length();
+ size_ += source.length();
+ for (int i = 0; i < source.length(); i++) {
+ position[i] = source[i];
+ }
+ return Vector<T>(position, source.length());
+ }
+
+
+ // Write the contents of the collector into the provided vector.
+ void WriteTo(Vector<T> destination) {
+ ASSERT(size_ <= destination.length());
+ int position = 0;
+ for (int i = 0; i < chunks_.length(); i++) {
+ Vector<T> chunk = chunks_.at(i);
+ for (int j = 0; j < chunk.length(); j++) {
+ destination[position] = chunk[j];
+ position++;
+ }
+ }
+ for (int i = 0; i < index_; i++) {
+ destination[position] = current_chunk_[i];
+ position++;
+ }
+ }
+
+ // Allocate a single contiguous vector, copy all the collected
+ // elements to the vector, and return it.
+ // The caller is responsible for freeing the memory of the returned
+ // vector (e.g., using Vector::Dispose).
+ Vector<T> ToVector() {
+ Vector<T> new_store = Vector<T>::New(size_);
+ WriteTo(new_store);
+ return new_store;
+ }
+
+ // Resets the collector to be empty.
+ virtual void Reset();
+
+ // Total number of elements added to collector so far.
+ inline int size() { return size_; }
+
+ protected:
+ static const int kMinCapacity = 16;
+ List<Vector<T> > chunks_;
+ Vector<T> current_chunk_; // Block of memory currently being written into.
+ int index_; // Current index in current chunk.
+ int size_; // Total number of elements in collector.
+
+ // Creates a new current chunk, and stores the old chunk in the chunks_ list.
+ void Grow(int min_capacity) {
+ ASSERT(growth_factor > 1);
+ int new_capacity;
+ int current_length = current_chunk_.length();
+ if (current_length < kMinCapacity) {
+ // The collector started out as empty.
+ new_capacity = min_capacity * growth_factor;
+ if (new_capacity < kMinCapacity) new_capacity = kMinCapacity;
+ } else {
+ int growth = current_length * (growth_factor - 1);
+ if (growth > max_growth) {
+ growth = max_growth;
+ }
+ new_capacity = current_length + growth;
+ if (new_capacity < min_capacity) {
+ new_capacity = min_capacity + growth;
+ }
+ }
+ NewChunk(new_capacity);
+ ASSERT(index_ + min_capacity <= current_chunk_.length());
+ }
+
+ // Before replacing the current chunk, give a subclass the option to move
+ // some of the current data into the new chunk. The function may update
+ // the current index_ value to represent data no longer in the current chunk.
+ // Returns the initial index of the new chunk (after copied data).
+ virtual void NewChunk(int new_capacity) {
+ Vector<T> new_chunk = Vector<T>::New(new_capacity);
+ if (index_ > 0) {
+ chunks_.Add(current_chunk_.SubVector(0, index_));
+ } else {
+ current_chunk_.Dispose();
+ }
+ current_chunk_ = new_chunk;
+ index_ = 0;
+ }
+};
+
+
+/*
+ * A collector that allows sequences of values to be guaranteed to
+ * stay consecutive.
+ * If the backing store grows while a sequence is active, the current
+ * sequence might be moved, but after the sequence is ended, it will
+ * not move again.
+ * NOTICE: Blocks allocated using Collector::AddBlock(int) can move
+ * as well, if inside an active sequence where another element is added.
+ */
+template <typename T, int growth_factor = 2, int max_growth = 1 * MB>
+class SequenceCollector : public Collector<T, growth_factor, max_growth> {
+ public:
+ explicit SequenceCollector(int initial_capacity)
+ : Collector<T, growth_factor, max_growth>(initial_capacity),
+ sequence_start_(kNoSequence) { }
+
+ virtual ~SequenceCollector() {}
+
+ void StartSequence() {
+ ASSERT(sequence_start_ == kNoSequence);
+ sequence_start_ = this->index_;
+ }
+
+ Vector<T> EndSequence() {
+ ASSERT(sequence_start_ != kNoSequence);
+ int sequence_start = sequence_start_;
+ sequence_start_ = kNoSequence;
+ if (sequence_start == this->index_) return Vector<T>();
+ return this->current_chunk_.SubVector(sequence_start, this->index_);
+ }
+
+ // Drops the currently added sequence, and all collected elements in it.
+ void DropSequence() {
+ ASSERT(sequence_start_ != kNoSequence);
+ int sequence_length = this->index_ - sequence_start_;
+ this->index_ = sequence_start_;
+ this->size_ -= sequence_length;
+ sequence_start_ = kNoSequence;
+ }
+
+ virtual void Reset() {
+ sequence_start_ = kNoSequence;
+ this->Collector<T, growth_factor, max_growth>::Reset();
+ }
+
+ private:
+ static const int kNoSequence = -1;
+ int sequence_start_;
+
+ // Move the currently active sequence to the new chunk.
+ virtual void NewChunk(int new_capacity) {
+ if (sequence_start_ == kNoSequence) {
+ // Fall back on default behavior if no sequence has been started.
+ this->Collector<T, growth_factor, max_growth>::NewChunk(new_capacity);
+ return;
+ }
+ int sequence_length = this->index_ - sequence_start_;
+ Vector<T> new_chunk = Vector<T>::New(sequence_length + new_capacity);
+ ASSERT(sequence_length < new_chunk.length());
+ for (int i = 0; i < sequence_length; i++) {
+ new_chunk[i] = this->current_chunk_[sequence_start_ + i];
+ }
+ if (sequence_start_ > 0) {
+ this->chunks_.Add(this->current_chunk_.SubVector(0, sequence_start_));
+ } else {
+ this->current_chunk_.Dispose();
+ }
+ this->current_chunk_ = new_chunk;
+ this->index_ = sequence_length;
+ sequence_start_ = 0;
+ }
+};
+
+
+// Compare ASCII/16bit chars to ASCII/16bit chars.
+template <typename lchar, typename rchar>
+inline int CompareCharsUnsigned(const lchar* lhs,
+ const rchar* rhs,
+ int chars) {
+ const lchar* limit = lhs + chars;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ if (sizeof(*lhs) == sizeof(*rhs)) {
+ // Number of characters in a uintptr_t.
+ static const int kStepSize = sizeof(uintptr_t) / sizeof(*lhs); // NOLINT
+ while (lhs <= limit - kStepSize) {
+ if (*reinterpret_cast<const uintptr_t*>(lhs) !=
+ *reinterpret_cast<const uintptr_t*>(rhs)) {
+ break;
+ }
+ lhs += kStepSize;
+ rhs += kStepSize;
+ }
+ }
+#endif
+ while (lhs < limit) {
+ int r = static_cast<int>(*lhs) - static_cast<int>(*rhs);
+ if (r != 0) return r;
+ ++lhs;
+ ++rhs;
+ }
+ return 0;
+}
+
+template<typename lchar, typename rchar>
+inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) {
+ ASSERT(sizeof(lchar) <= 2);
+ ASSERT(sizeof(rchar) <= 2);
+ if (sizeof(lchar) == 1) {
+ if (sizeof(rchar) == 1) {
+ return CompareCharsUnsigned(reinterpret_cast<const uint8_t*>(lhs),
+ reinterpret_cast<const uint8_t*>(rhs),
+ chars);
+ } else {
+ return CompareCharsUnsigned(reinterpret_cast<const uint8_t*>(lhs),
+ reinterpret_cast<const uint16_t*>(rhs),
+ chars);
+ }
+ } else {
+ if (sizeof(rchar) == 1) {
+ return CompareCharsUnsigned(reinterpret_cast<const uint16_t*>(lhs),
+ reinterpret_cast<const uint8_t*>(rhs),
+ chars);
+ } else {
+ return CompareCharsUnsigned(reinterpret_cast<const uint16_t*>(lhs),
+ reinterpret_cast<const uint16_t*>(rhs),
+ chars);
+ }
+ }
+}
+
+
+// Calculate 10^exponent.
+inline int TenToThe(int exponent) {
+ ASSERT(exponent <= 9);
+ ASSERT(exponent >= 1);
+ int answer = 10;
+ for (int i = 1; i < exponent; i++) answer *= 10;
+ return answer;
+}
+
+
+// The type-based aliasing rule allows the compiler to assume that pointers of
+// different types (for some definition of different) never alias each other.
+// Thus the following code does not work:
+//
+// float f = foo();
+// int fbits = *(int*)(&f);
+//
+// The compiler 'knows' that the int pointer can't refer to f since the types
+// don't match, so the compiler may cache f in a register, leaving random data
+// in fbits. Using C++ style casts makes no difference, however a pointer to
+// char data is assumed to alias any other pointer. This is the 'memcpy
+// exception'.
+//
+// Bit_cast uses the memcpy exception to move the bits from a variable of one
+// type of a variable of another type. Of course the end result is likely to
+// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005)
+// will completely optimize BitCast away.
+//
+// There is an additional use for BitCast.
+// Recent gccs will warn when they see casts that may result in breakage due to
+// the type-based aliasing rule. If you have checked that there is no breakage
+// you can use BitCast to cast one pointer type to another. This confuses gcc
+// enough that it can no longer see that you have cast one pointer type to
+// another thus avoiding the warning.
+
+// We need different implementations of BitCast for pointer and non-pointer
+// values. We use partial specialization of auxiliary struct to work around
+// issues with template functions overloading.
+template <class Dest, class Source>
+struct BitCastHelper {
+ STATIC_ASSERT(sizeof(Dest) == sizeof(Source));
+
+ INLINE(static Dest cast(const Source& source)) {
+ Dest dest;
+ // TODO(jkummerow): Refactor #includes and use OS::MemCopy() instead.
+ memcpy(&dest, &source, sizeof(dest));
+ return dest;
+ }
+};
+
+template <class Dest, class Source>
+struct BitCastHelper<Dest, Source*> {
+ INLINE(static Dest cast(Source* source)) {
+ return BitCastHelper<Dest, uintptr_t>::
+ cast(reinterpret_cast<uintptr_t>(source));
+ }
+};
+
+template <class Dest, class Source>
+INLINE(Dest BitCast(const Source& source));
+
+template <class Dest, class Source>
+inline Dest BitCast(const Source& source) {
+ return BitCastHelper<Dest, Source>::cast(source);
+}
+
+
+template<typename ElementType, int NumElements>
+class EmbeddedContainer {
+ public:
+ EmbeddedContainer() : elems_() { }
+
+ int length() const { return NumElements; }
+ const ElementType& operator[](int i) const {
+ ASSERT(i < length());
+ return elems_[i];
+ }
+ ElementType& operator[](int i) {
+ ASSERT(i < length());
+ return elems_[i];
+ }
+
+ private:
+ ElementType elems_[NumElements];
+};
+
+
+template<typename ElementType>
+class EmbeddedContainer<ElementType, 0> {
+ public:
+ int length() const { return 0; }
+ const ElementType& operator[](int i) const {
+ UNREACHABLE();
+ static ElementType t = 0;
+ return t;
+ }
+ ElementType& operator[](int i) {
+ UNREACHABLE();
+ static ElementType t = 0;
+ return t;
+ }
+};
+
+
+// Helper class for building result strings in a character buffer. The
+// purpose of the class is to use safe operations that checks the
+// buffer bounds on all operations in debug mode.
+// This simple base class does not allow formatted output.
+class SimpleStringBuilder {
+ public:
+ // Create a string builder with a buffer of the given size. The
+ // buffer is allocated through NewArray<char> and must be
+ // deallocated by the caller of Finalize().
+ explicit SimpleStringBuilder(int size);
+
+ SimpleStringBuilder(char* buffer, int size)
+ : buffer_(buffer, size), position_(0) { }
+
+ ~SimpleStringBuilder() { if (!is_finalized()) Finalize(); }
+
+ int size() const { return buffer_.length(); }
+
+ // Get the current position in the builder.
+ int position() const {
+ ASSERT(!is_finalized());
+ return position_;
+ }
+
+ // Reset the position.
+ void Reset() { position_ = 0; }
+
+ // Add a single character to the builder. It is not allowed to add
+ // 0-characters; use the Finalize() method to terminate the string
+ // instead.
+ void AddCharacter(char c) {
+ ASSERT(c != '\0');
+ ASSERT(!is_finalized() && position_ < buffer_.length());
+ buffer_[position_++] = c;
+ }
+
+ // Add an entire string to the builder. Uses strlen() internally to
+ // compute the length of the input string.
+ void AddString(const char* s);
+
+ // Add the first 'n' characters of the given string 's' to the
+ // builder. The input string must have enough characters.
+ void AddSubstring(const char* s, int n);
+
+ // Add character padding to the builder. If count is non-positive,
+ // nothing is added to the builder.
+ void AddPadding(char c, int count);
+
+ // Add the decimal representation of the value.
+ void AddDecimalInteger(int value);
+
+ // Finalize the string by 0-terminating it and returning the buffer.
+ char* Finalize();
+
+ protected:
+ Vector<char> buffer_;
+ int position_;
+
+ bool is_finalized() const { return position_ < 0; }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SimpleStringBuilder);
+};
+
+
+// A poor man's version of STL's bitset: A bit set of enums E (without explicit
+// values), fitting into an integral type T.
+template <class E, class T = int>
+class EnumSet {
+ public:
+ 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_; }
+ bool operator!=(const EnumSet& set) { return bits_ != set.bits_; }
+ EnumSet<E, T> operator|(const EnumSet& set) const {
+ return EnumSet<E, T>(bits_ | set.bits_);
+ }
+
+ private:
+ T Mask(E element) const {
+ // The strange typing in ASSERT is necessary to avoid stupid warnings, see:
+ // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43680
+ ASSERT(static_cast<int>(element) < static_cast<int>(sizeof(T) * CHAR_BIT));
+ return 1 << element;
+ }
+
+ T bits_;
+};
+
+
+class TypeFeedbackId {
+ public:
+ explicit TypeFeedbackId(int id) : id_(id) { }
+ int ToInt() const { return id_; }
+
+ static TypeFeedbackId None() { return TypeFeedbackId(kNoneId); }
+ bool IsNone() const { return id_ == kNoneId; }
+
+ private:
+ static const int kNoneId = -1;
+
+ int id_;
+};
+
+
+class BailoutId {
+ public:
+ explicit BailoutId(int id) : id_(id) { }
+ int ToInt() const { return id_; }
+
+ static BailoutId None() { return BailoutId(kNoneId); }
+ static BailoutId FunctionEntry() { return BailoutId(kFunctionEntryId); }
+ static BailoutId Declarations() { return BailoutId(kDeclarationsId); }
+ static BailoutId FirstUsable() { return BailoutId(kFirstUsableId); }
+ static BailoutId StubEntry() { return BailoutId(kStubEntryId); }
+
+ bool IsNone() const { return id_ == kNoneId; }
+ bool operator==(const BailoutId& other) const { return id_ == other.id_; }
+
+ private:
+ static const int kNoneId = -1;
+
+ // Using 0 could disguise errors.
+ static const int kFunctionEntryId = 2;
+
+ // This AST id identifies the point after the declarations have been visited.
+ // We need it to capture the environment effects of declarations that emit
+ // code (function declarations).
+ static const int kDeclarationsId = 3;
+
+ // Every FunctionState starts with this id.
+ static const int kFirstUsableId = 4;
+
+ // Every compiled stub starts with this id.
+ static const int kStubEntryId = 5;
+
+ int id_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_UTILS_H_
diff --git a/chromium/v8/src/v8-counters.cc b/chromium/v8/src/v8-counters.cc
new file mode 100644
index 00000000000..905e178fec6
--- /dev/null
+++ b/chromium/v8/src/v8-counters.cc
@@ -0,0 +1,96 @@
+// Copyright 2012 the V8 project authors. 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 "v8-counters.h"
+
+namespace v8 {
+namespace internal {
+
+Counters::Counters(Isolate* isolate) {
+#define HT(name, caption) \
+ name##_ = HistogramTimer(#caption, 0, 10000, 50, isolate);
+ HISTOGRAM_TIMER_LIST(HT)
+#undef HT
+
+#define HP(name, caption) \
+ name##_ = Histogram(#caption, 0, 101, 100, isolate);
+ HISTOGRAM_PERCENTAGE_LIST(HP)
+#undef HP
+
+#define HM(name, caption) \
+ name##_ = Histogram(#caption, 1000, 500000, 50, isolate);
+ HISTOGRAM_MEMORY_LIST(HM)
+#undef HM
+
+#define SC(name, caption) \
+ name##_ = StatsCounter("c:" #caption);
+
+ STATS_COUNTER_LIST_1(SC)
+ STATS_COUNTER_LIST_2(SC)
+#undef SC
+
+#define SC(name) \
+ count_of_##name##_ = StatsCounter("c:" "V8.CountOf_" #name); \
+ size_of_##name##_ = StatsCounter("c:" "V8.SizeOf_" #name);
+ INSTANCE_TYPE_LIST(SC)
+#undef SC
+
+#define SC(name) \
+ count_of_CODE_TYPE_##name##_ = \
+ StatsCounter("c:" "V8.CountOf_CODE_TYPE-" #name); \
+ size_of_CODE_TYPE_##name##_ = \
+ StatsCounter("c:" "V8.SizeOf_CODE_TYPE-" #name);
+ CODE_KIND_LIST(SC)
+#undef SC
+
+#define SC(name) \
+ count_of_FIXED_ARRAY_##name##_ = \
+ StatsCounter("c:" "V8.CountOf_FIXED_ARRAY-" #name); \
+ size_of_FIXED_ARRAY_##name##_ = \
+ StatsCounter("c:" "V8.SizeOf_FIXED_ARRAY-" #name);
+ FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC)
+#undef SC
+}
+
+
+void Counters::ResetHistograms() {
+#define HT(name, caption) name##_.Reset();
+ HISTOGRAM_TIMER_LIST(HT)
+#undef HT
+
+#define HP(name, caption) name##_.Reset();
+ HISTOGRAM_PERCENTAGE_LIST(HP)
+#undef HP
+
+#define HM(name, caption) name##_.Reset();
+ HISTOGRAM_MEMORY_LIST(HM)
+#undef HM
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/v8-counters.h b/chromium/v8/src/v8-counters.h
new file mode 100644
index 00000000000..ff2247cba16
--- /dev/null
+++ b/chromium/v8/src/v8-counters.h
@@ -0,0 +1,402 @@
+// Copyright 2012 the V8 project authors. 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_V8_COUNTERS_H_
+#define V8_V8_COUNTERS_H_
+
+#include "allocation.h"
+#include "counters.h"
+#include "objects.h"
+#include "v8globals.h"
+
+namespace v8 {
+namespace internal {
+
+#define HISTOGRAM_TIMER_LIST(HT) \
+ /* Garbage collection timers. */ \
+ HT(gc_compactor, V8.GCCompactor) \
+ HT(gc_scavenger, V8.GCScavenger) \
+ HT(gc_context, V8.GCContext) /* GC context cleanup time */ \
+ /* Parsing timers. */ \
+ HT(parse, V8.Parse) \
+ HT(parse_lazy, V8.ParseLazy) \
+ HT(pre_parse, V8.PreParse) \
+ /* Total compilation times. */ \
+ HT(compile, V8.Compile) \
+ HT(compile_eval, V8.CompileEval) \
+ HT(compile_lazy, V8.CompileLazy)
+
+#define HISTOGRAM_PERCENTAGE_LIST(HP) \
+ HP(external_fragmentation_total, \
+ V8.MemoryExternalFragmentationTotal) \
+ HP(external_fragmentation_old_pointer_space, \
+ V8.MemoryExternalFragmentationOldPointerSpace) \
+ HP(external_fragmentation_old_data_space, \
+ V8.MemoryExternalFragmentationOldDataSpace) \
+ HP(external_fragmentation_code_space, \
+ V8.MemoryExternalFragmentationCodeSpace) \
+ HP(external_fragmentation_map_space, \
+ V8.MemoryExternalFragmentationMapSpace) \
+ HP(external_fragmentation_cell_space, \
+ V8.MemoryExternalFragmentationCellSpace) \
+ HP(external_fragmentation_property_cell_space, \
+ V8.MemoryExternalFragmentationPropertyCellSpace) \
+ HP(external_fragmentation_lo_space, \
+ V8.MemoryExternalFragmentationLoSpace) \
+ HP(heap_fraction_map_space, \
+ V8.MemoryHeapFractionMapSpace) \
+ HP(heap_fraction_cell_space, \
+ V8.MemoryHeapFractionCellSpace) \
+ HP(heap_fraction_property_cell_space, \
+ V8.MemoryHeapFractionPropertyCellSpace) \
+
+
+#define HISTOGRAM_MEMORY_LIST(HM) \
+ HM(heap_sample_total_committed, V8.MemoryHeapSampleTotalCommitted) \
+ HM(heap_sample_total_used, V8.MemoryHeapSampleTotalUsed) \
+ HM(heap_sample_map_space_committed, \
+ V8.MemoryHeapSampleMapSpaceCommitted) \
+ HM(heap_sample_cell_space_committed, \
+ V8.MemoryHeapSampleCellSpaceCommitted) \
+ HM(heap_sample_property_cell_space_committed, \
+ V8.MemoryHeapSamplePropertyCellSpaceCommitted) \
+
+
+// WARNING: STATS_COUNTER_LIST_* is a very large macro that is causing MSVC
+// Intellisense to crash. It was broken into two macros (each of length 40
+// lines) rather than one macro (of length about 80 lines) to work around
+// this problem. Please avoid using recursive macros of this length when
+// possible.
+#define STATS_COUNTER_LIST_1(SC) \
+ /* Global Handle Count*/ \
+ SC(global_handles, V8.GlobalHandles) \
+ /* OS Memory allocated */ \
+ SC(memory_allocated, V8.OsMemoryAllocated) \
+ SC(normalized_maps, V8.NormalizedMaps) \
+ SC(props_to_dictionary, V8.ObjectPropertiesToDictionary) \
+ SC(elements_to_dictionary, V8.ObjectElementsToDictionary) \
+ SC(alive_after_last_gc, V8.AliveAfterLastGC) \
+ SC(objs_since_last_young, V8.ObjsSinceLastYoung) \
+ SC(objs_since_last_full, V8.ObjsSinceLastFull) \
+ SC(string_table_capacity, V8.StringTableCapacity) \
+ SC(number_of_symbols, V8.NumberOfSymbols) \
+ SC(script_wrappers, V8.ScriptWrappers) \
+ SC(call_initialize_stubs, V8.CallInitializeStubs) \
+ SC(call_premonomorphic_stubs, V8.CallPreMonomorphicStubs) \
+ SC(call_normal_stubs, V8.CallNormalStubs) \
+ SC(call_megamorphic_stubs, V8.CallMegamorphicStubs) \
+ SC(arguments_adaptors, V8.ArgumentsAdaptors) \
+ SC(compilation_cache_hits, V8.CompilationCacheHits) \
+ SC(compilation_cache_misses, V8.CompilationCacheMisses) \
+ SC(string_ctor_calls, V8.StringConstructorCalls) \
+ SC(string_ctor_conversions, V8.StringConstructorConversions) \
+ SC(string_ctor_cached_number, V8.StringConstructorCachedNumber) \
+ SC(string_ctor_string_value, V8.StringConstructorStringValue) \
+ SC(string_ctor_gc_required, V8.StringConstructorGCRequired) \
+ /* Amount of evaled source code. */ \
+ SC(total_eval_size, V8.TotalEvalSize) \
+ /* Amount of loaded source code. */ \
+ SC(total_load_size, V8.TotalLoadSize) \
+ /* Amount of parsed source code. */ \
+ SC(total_parse_size, V8.TotalParseSize) \
+ /* Amount of source code skipped over using preparsing. */ \
+ SC(total_preparse_skipped, V8.TotalPreparseSkipped) \
+ /* Number of symbol lookups skipped using preparsing */ \
+ SC(total_preparse_symbols_skipped, V8.TotalPreparseSymbolSkipped) \
+ /* Amount of compiled source code. */ \
+ SC(total_compile_size, V8.TotalCompileSize) \
+ /* Amount of source code compiled with the full codegen. */ \
+ SC(total_full_codegen_source_size, V8.TotalFullCodegenSourceSize) \
+ /* Number of contexts created from scratch. */ \
+ SC(contexts_created_from_scratch, V8.ContextsCreatedFromScratch) \
+ /* Number of contexts created by partial snapshot. */ \
+ 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) \
+ /* 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) \
+ /* Number of code stubs. */ \
+ SC(code_stubs, V8.CodeStubs) \
+ /* Amount of stub code. */ \
+ SC(total_stubs_code_size, V8.TotalStubsCodeSize) \
+ /* Amount of (JS) compiled code. */ \
+ SC(total_compiled_code_size, V8.TotalCompiledCodeSize) \
+ SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest) \
+ SC(gc_compactor_caused_by_promoted_data, \
+ V8.GCCompactorCausedByPromotedData) \
+ SC(gc_compactor_caused_by_oldspace_exhaustion, \
+ V8.GCCompactorCausedByOldspaceExhaustion) \
+ SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
+ SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
+ /* How is the generic keyed-load stub used? */ \
+ SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
+ SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \
+ SC(keyed_load_generic_lookup_cache, V8.KeyedLoadGenericLookupCache) \
+ SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \
+ SC(keyed_load_polymorphic_stubs, V8.KeyedLoadPolymorphicStubs) \
+ SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \
+ /* How is the generic keyed-call stub used? */ \
+ SC(keyed_call_generic_smi_fast, V8.KeyedCallGenericSmiFast) \
+ SC(keyed_call_generic_smi_dict, V8.KeyedCallGenericSmiDict) \
+ SC(keyed_call_generic_lookup_cache, V8.KeyedCallGenericLookupCache) \
+ SC(keyed_call_generic_lookup_dict, V8.KeyedCallGenericLookupDict) \
+ SC(keyed_call_generic_slow, V8.KeyedCallGenericSlow) \
+ SC(keyed_call_generic_slow_load, V8.KeyedCallGenericSlowLoad) \
+ SC(named_load_global_stub, V8.NamedLoadGlobalStub) \
+ SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
+ SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
+ SC(keyed_store_polymorphic_stubs, V8.KeyedStorePolymorphicStubs) \
+ SC(keyed_store_external_array_slow, V8.KeyedStoreExternalArraySlow) \
+ SC(store_normal_miss, V8.StoreNormalMiss) \
+ SC(store_normal_hit, V8.StoreNormalHit) \
+ SC(cow_arrays_created_stub, V8.COWArraysCreatedStub) \
+ SC(cow_arrays_created_runtime, V8.COWArraysCreatedRuntime) \
+ SC(cow_arrays_converted, V8.COWArraysConverted) \
+ SC(call_miss, V8.CallMiss) \
+ SC(keyed_call_miss, V8.KeyedCallMiss) \
+ SC(load_miss, V8.LoadMiss) \
+ SC(keyed_load_miss, V8.KeyedLoadMiss) \
+ SC(call_const, V8.CallConst) \
+ SC(call_const_fast_api, V8.CallConstFastApi) \
+ SC(call_const_interceptor, V8.CallConstInterceptor) \
+ SC(call_const_interceptor_fast_api, V8.CallConstInterceptorFastApi) \
+ SC(call_global_inline, V8.CallGlobalInline) \
+ SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \
+ SC(constructed_objects, V8.ConstructedObjects) \
+ SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
+ SC(negative_lookups, V8.NegativeLookups) \
+ SC(negative_lookups_miss, V8.NegativeLookupsMiss) \
+ SC(megamorphic_stub_cache_probes, V8.MegamorphicStubCacheProbes) \
+ SC(megamorphic_stub_cache_misses, V8.MegamorphicStubCacheMisses) \
+ SC(megamorphic_stub_cache_updates, V8.MegamorphicStubCacheUpdates) \
+ SC(array_function_runtime, V8.ArrayFunctionRuntime) \
+ SC(array_function_native, V8.ArrayFunctionNative) \
+ SC(for_in, V8.ForIn) \
+ SC(enum_cache_hits, V8.EnumCacheHits) \
+ SC(enum_cache_misses, V8.EnumCacheMisses) \
+ SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
+ SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
+ SC(fast_new_closure_total, V8.FastNewClosureTotal) \
+ SC(fast_new_closure_try_optimized, V8.FastNewClosureTryOptimized) \
+ SC(fast_new_closure_install_optimized, V8.FastNewClosureInstallOptimized) \
+ SC(string_add_runtime, V8.StringAddRuntime) \
+ SC(string_add_native, V8.StringAddNative) \
+ SC(string_add_runtime_ext_to_ascii, V8.StringAddRuntimeExtToAscii) \
+ SC(sub_string_runtime, V8.SubStringRuntime) \
+ SC(sub_string_native, V8.SubStringNative) \
+ SC(string_add_make_two_char, V8.StringAddMakeTwoChar) \
+ SC(string_compare_native, V8.StringCompareNative) \
+ SC(string_compare_runtime, V8.StringCompareRuntime) \
+ SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \
+ SC(regexp_entry_native, V8.RegExpEntryNative) \
+ SC(number_to_string_native, V8.NumberToStringNative) \
+ SC(number_to_string_runtime, V8.NumberToStringRuntime) \
+ SC(math_acos, V8.MathAcos) \
+ SC(math_asin, V8.MathAsin) \
+ SC(math_atan, V8.MathAtan) \
+ SC(math_atan2, V8.MathAtan2) \
+ SC(math_ceil, V8.MathCeil) \
+ SC(math_cos, V8.MathCos) \
+ SC(math_exp, V8.MathExp) \
+ SC(math_floor, V8.MathFloor) \
+ SC(math_log, V8.MathLog) \
+ SC(math_pow, V8.MathPow) \
+ SC(math_round, V8.MathRound) \
+ SC(math_sin, V8.MathSin) \
+ SC(math_sqrt, V8.MathSqrt) \
+ SC(math_tan, V8.MathTan) \
+ SC(transcendental_cache_hit, V8.TranscendentalCacheHit) \
+ SC(transcendental_cache_miss, V8.TranscendentalCacheMiss) \
+ SC(stack_interrupts, V8.StackInterrupts) \
+ SC(runtime_profiler_ticks, V8.RuntimeProfilerTicks) \
+ SC(bounds_checks_eliminated, V8.BoundsChecksEliminated) \
+ SC(bounds_checks_hoisted, V8.BoundsChecksHoisted) \
+ SC(soft_deopts_requested, V8.SoftDeoptsRequested) \
+ SC(soft_deopts_inserted, V8.SoftDeoptsInserted) \
+ SC(soft_deopts_executed, V8.SoftDeoptsExecuted) \
+ SC(new_space_bytes_available, V8.MemoryNewSpaceBytesAvailable) \
+ SC(new_space_bytes_committed, V8.MemoryNewSpaceBytesCommitted) \
+ SC(new_space_bytes_used, V8.MemoryNewSpaceBytesUsed) \
+ SC(old_pointer_space_bytes_available, \
+ V8.MemoryOldPointerSpaceBytesAvailable) \
+ SC(old_pointer_space_bytes_committed, \
+ V8.MemoryOldPointerSpaceBytesCommitted) \
+ SC(old_pointer_space_bytes_used, V8.MemoryOldPointerSpaceBytesUsed) \
+ SC(old_data_space_bytes_available, V8.MemoryOldDataSpaceBytesAvailable) \
+ SC(old_data_space_bytes_committed, V8.MemoryOldDataSpaceBytesCommitted) \
+ SC(old_data_space_bytes_used, V8.MemoryOldDataSpaceBytesUsed) \
+ SC(code_space_bytes_available, V8.MemoryCodeSpaceBytesAvailable) \
+ SC(code_space_bytes_committed, V8.MemoryCodeSpaceBytesCommitted) \
+ SC(code_space_bytes_used, V8.MemoryCodeSpaceBytesUsed) \
+ SC(map_space_bytes_available, V8.MemoryMapSpaceBytesAvailable) \
+ SC(map_space_bytes_committed, V8.MemoryMapSpaceBytesCommitted) \
+ SC(map_space_bytes_used, V8.MemoryMapSpaceBytesUsed) \
+ SC(cell_space_bytes_available, V8.MemoryCellSpaceBytesAvailable) \
+ SC(cell_space_bytes_committed, V8.MemoryCellSpaceBytesCommitted) \
+ SC(cell_space_bytes_used, V8.MemoryCellSpaceBytesUsed) \
+ SC(property_cell_space_bytes_available, \
+ V8.MemoryPropertyCellSpaceBytesAvailable) \
+ SC(property_cell_space_bytes_committed, \
+ V8.MemoryPropertyCellSpaceBytesCommitted) \
+ SC(property_cell_space_bytes_used, \
+ V8.MemoryPropertyCellSpaceBytesUsed) \
+ SC(lo_space_bytes_available, V8.MemoryLoSpaceBytesAvailable) \
+ SC(lo_space_bytes_committed, V8.MemoryLoSpaceBytesCommitted) \
+ SC(lo_space_bytes_used, V8.MemoryLoSpaceBytesUsed)
+
+
+// This file contains all the v8 counters that are in use.
+class Counters {
+ public:
+#define HT(name, caption) \
+ HistogramTimer* name() { return &name##_; }
+ HISTOGRAM_TIMER_LIST(HT)
+#undef HT
+
+#define HP(name, caption) \
+ Histogram* name() { return &name##_; }
+ HISTOGRAM_PERCENTAGE_LIST(HP)
+#undef HP
+
+#define HM(name, caption) \
+ Histogram* name() { return &name##_; }
+ HISTOGRAM_MEMORY_LIST(HM)
+#undef HM
+
+#define SC(name, caption) \
+ StatsCounter* name() { return &name##_; }
+ STATS_COUNTER_LIST_1(SC)
+ STATS_COUNTER_LIST_2(SC)
+#undef SC
+
+#define SC(name) \
+ StatsCounter* count_of_##name() { return &count_of_##name##_; } \
+ StatsCounter* size_of_##name() { return &size_of_##name##_; }
+ INSTANCE_TYPE_LIST(SC)
+#undef SC
+
+#define SC(name) \
+ StatsCounter* count_of_CODE_TYPE_##name() \
+ { return &count_of_CODE_TYPE_##name##_; } \
+ StatsCounter* size_of_CODE_TYPE_##name() \
+ { return &size_of_CODE_TYPE_##name##_; }
+ CODE_KIND_LIST(SC)
+#undef SC
+
+#define SC(name) \
+ StatsCounter* count_of_FIXED_ARRAY_##name() \
+ { return &count_of_FIXED_ARRAY_##name##_; } \
+ StatsCounter* size_of_FIXED_ARRAY_##name() \
+ { return &size_of_FIXED_ARRAY_##name##_; }
+ FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC)
+#undef SC
+
+ enum Id {
+#define RATE_ID(name, caption) k_##name,
+ HISTOGRAM_TIMER_LIST(RATE_ID)
+#undef RATE_ID
+#define PERCENTAGE_ID(name, caption) k_##name,
+ HISTOGRAM_PERCENTAGE_LIST(PERCENTAGE_ID)
+#undef PERCENTAGE_ID
+#define MEMORY_ID(name, caption) k_##name,
+ HISTOGRAM_MEMORY_LIST(MEMORY_ID)
+#undef MEMORY_ID
+#define COUNTER_ID(name, caption) k_##name,
+ STATS_COUNTER_LIST_1(COUNTER_ID)
+ STATS_COUNTER_LIST_2(COUNTER_ID)
+#undef COUNTER_ID
+#define COUNTER_ID(name) kCountOf##name, kSizeOf##name,
+ INSTANCE_TYPE_LIST(COUNTER_ID)
+#undef COUNTER_ID
+#define COUNTER_ID(name) kCountOfCODE_TYPE_##name, \
+ kSizeOfCODE_TYPE_##name,
+ CODE_KIND_LIST(COUNTER_ID)
+#undef COUNTER_ID
+#define COUNTER_ID(name) kCountOfFIXED_ARRAY__##name, \
+ kSizeOfFIXED_ARRAY__##name,
+ FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(COUNTER_ID)
+#undef COUNTER_ID
+ stats_counter_count
+ };
+
+ void ResetHistograms();
+
+ private:
+#define HT(name, caption) \
+ HistogramTimer name##_;
+ HISTOGRAM_TIMER_LIST(HT)
+#undef HT
+
+#define HP(name, caption) \
+ Histogram name##_;
+ HISTOGRAM_PERCENTAGE_LIST(HP)
+#undef HP
+
+#define HM(name, caption) \
+ Histogram name##_;
+ HISTOGRAM_MEMORY_LIST(HM)
+#undef HM
+
+#define SC(name, caption) \
+ StatsCounter name##_;
+ STATS_COUNTER_LIST_1(SC)
+ STATS_COUNTER_LIST_2(SC)
+#undef SC
+
+#define SC(name) \
+ StatsCounter size_of_##name##_; \
+ StatsCounter count_of_##name##_;
+ INSTANCE_TYPE_LIST(SC)
+#undef SC
+
+#define SC(name) \
+ StatsCounter size_of_CODE_TYPE_##name##_; \
+ StatsCounter count_of_CODE_TYPE_##name##_;
+ CODE_KIND_LIST(SC)
+#undef SC
+
+#define SC(name) \
+ StatsCounter size_of_FIXED_ARRAY_##name##_; \
+ StatsCounter count_of_FIXED_ARRAY_##name##_;
+ FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC)
+#undef SC
+
+ friend class Isolate;
+
+ explicit Counters(Isolate* isolate);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Counters);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_V8_COUNTERS_H_
diff --git a/chromium/v8/src/v8.cc b/chromium/v8/src/v8.cc
new file mode 100644
index 00000000000..93f3efb2e3f
--- /dev/null
+++ b/chromium/v8/src/v8.cc
@@ -0,0 +1,337 @@
+// Copyright 2012 the V8 project authors. 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 "assembler.h"
+#include "isolate.h"
+#include "elements.h"
+#include "bootstrapper.h"
+#include "debug.h"
+#include "deoptimizer.h"
+#include "frames.h"
+#include "heap-profiler.h"
+#include "hydrogen.h"
+#include "lithium-allocator.h"
+#include "objects.h"
+#include "once.h"
+#include "platform.h"
+#include "sampler.h"
+#include "runtime-profiler.h"
+#include "serialize.h"
+#include "store-buffer.h"
+
+namespace v8 {
+namespace internal {
+
+V8_DECLARE_ONCE(init_once);
+
+bool V8::is_running_ = 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;
+v8::ArrayBuffer::Allocator* V8::array_buffer_allocator_ = NULL;
+
+static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER;
+
+static EntropySource entropy_source;
+
+
+bool V8::Initialize(Deserializer* des) {
+ InitializeOncePerProcess();
+
+ // The current thread may not yet had entered an isolate to run.
+ // Note the Isolate::Current() may be non-null because for various
+ // initialization purposes an initializing thread may be assigned an isolate
+ // but not actually enter it.
+ if (i::Isolate::CurrentPerIsolateThreadData() == NULL) {
+ i::Isolate::EnterDefaultIsolate();
+ }
+
+ ASSERT(i::Isolate::CurrentPerIsolateThreadData() != NULL);
+ ASSERT(i::Isolate::CurrentPerIsolateThreadData()->thread_id().Equals(
+ i::ThreadId::Current()));
+ ASSERT(i::Isolate::CurrentPerIsolateThreadData()->isolate() ==
+ i::Isolate::Current());
+
+ if (IsDead()) return false;
+
+ Isolate* isolate = Isolate::Current();
+ if (isolate->IsInitialized()) return true;
+
+ is_running_ = true;
+ has_been_set_up_ = true;
+ has_fatal_error_ = false;
+ has_been_disposed_ = false;
+
+ return isolate->Init(des);
+}
+
+
+void V8::SetFatalError() {
+ is_running_ = false;
+ has_fatal_error_ = true;
+}
+
+
+void V8::TearDown() {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(isolate->IsDefaultIsolate());
+
+ if (!has_been_set_up_ || has_been_disposed_) return;
+
+ // The isolate has to be torn down before clearing the LOperand
+ // caches so that the optimizing compiler thread (if running)
+ // doesn't see an inconsistent view of the lithium instructions.
+ isolate->TearDown();
+ delete isolate;
+
+ ElementsAccessor::TearDown();
+ LOperand::TearDownCaches();
+ ExternalReference::TearDownMathExpData();
+ RegisteredExtension::UnregisterAll();
+ Isolate::GlobalTearDown();
+
+ is_running_ = false;
+ has_been_disposed_ = true;
+
+ delete call_completed_callbacks_;
+ call_completed_callbacks_ = NULL;
+
+ Sampler::TearDown();
+ OS::TearDown();
+}
+
+
+static void seed_random(uint32_t* state) {
+ for (int i = 0; i < 2; ++i) {
+ if (FLAG_random_seed != 0) {
+ state[i] = FLAG_random_seed;
+ } else if (entropy_source != NULL) {
+ uint32_t val;
+ ScopedLock lock(entropy_mutex.Pointer());
+ entropy_source(reinterpret_cast<unsigned char*>(&val), sizeof(uint32_t));
+ state[i] = val;
+ } else {
+ state[i] = random();
+ }
+ }
+}
+
+
+// Random number generator using George Marsaglia's MWC algorithm.
+static uint32_t random_base(uint32_t* state) {
+ // Initialize seed using the system random().
+ // No non-zero seed will ever become zero again.
+ if (state[0] == 0) seed_random(state);
+
+ // Mix the bits. Never replaces state[i] with 0 if it is nonzero.
+ state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16);
+ state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16);
+
+ return (state[0] << 14) + (state[1] & 0x3FFFF);
+}
+
+
+void V8::SetEntropySource(EntropySource source) {
+ entropy_source = source;
+}
+
+
+void V8::SetReturnAddressLocationResolver(
+ ReturnAddressLocationResolver resolver) {
+ StackFrame::SetReturnAddressLocationResolver(resolver);
+}
+
+
+// Used by JavaScript APIs
+uint32_t V8::Random(Context* context) {
+ ASSERT(context->IsNativeContext());
+ ByteArray* seed = context->random_seed();
+ return random_base(reinterpret_cast<uint32_t*>(seed->GetDataStartAddress()));
+}
+
+
+// Used internally by the JIT and memory allocator for security
+// purposes. So, we keep a different state to prevent informations
+// leaks that could be used in an exploit.
+uint32_t V8::RandomPrivate(Isolate* isolate) {
+ ASSERT(isolate == Isolate::Current());
+ return random_base(isolate->private_random_seed());
+}
+
+
+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(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) {
+ bool has_call_completed_callbacks = call_completed_callbacks_ != NULL;
+ bool observer_delivery_pending =
+ FLAG_harmony_observation && isolate->observer_delivery_pending();
+ if (!has_call_completed_callbacks && !observer_delivery_pending) 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();
+ if (observer_delivery_pending) {
+ JSObject::DeliverChangeRecords(isolate);
+ }
+ if (has_call_completed_callbacks) {
+ for (int i = 0; i < call_completed_callbacks_->length(); i++) {
+ call_completed_callbacks_->at(i)();
+ }
+ }
+ handle_scope_implementer->DecrementCallDepth();
+}
+
+
+// Use a union type to avoid type-aliasing optimizations in GCC.
+typedef union {
+ double double_value;
+ uint64_t uint64_t_value;
+} double_int_union;
+
+
+Object* V8::FillHeapNumberWithRandom(Object* heap_number,
+ Context* context) {
+ double_int_union r;
+ uint64_t random_bits = Random(context);
+ // Convert 32 random bits to 0.(32 random bits) in a double
+ // by computing:
+ // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
+ static const double binary_million = 1048576.0;
+ r.double_value = binary_million;
+ r.uint64_t_value |= random_bits;
+ r.double_value -= binary_million;
+
+ HeapNumber::cast(heap_number)->set_value(r.double_value);
+ return heap_number;
+}
+
+
+void V8::InitializeOncePerProcessImpl() {
+ FlagList::EnforceFlagImplications();
+ if (FLAG_stress_compaction) {
+ FLAG_force_marking_deque_overflows = true;
+ FLAG_gc_global = true;
+ FLAG_max_new_space_size = (1 << (kPageSizeBits - 10)) * 2;
+ }
+
+ if (FLAG_parallel_recompilation &&
+ (FLAG_trace_hydrogen || FLAG_trace_hydrogen_stubs)) {
+ FLAG_parallel_recompilation = false;
+ PrintF("Parallel recompilation has been disabled for tracing.\n");
+ }
+
+ if (FLAG_sweeper_threads <= 0) {
+ if (FLAG_concurrent_sweeping) {
+ FLAG_sweeper_threads = SystemThreadManager::
+ NumberOfParallelSystemThreads(
+ SystemThreadManager::CONCURRENT_SWEEPING);
+ } else if (FLAG_parallel_sweeping) {
+ FLAG_sweeper_threads = SystemThreadManager::
+ NumberOfParallelSystemThreads(
+ SystemThreadManager::PARALLEL_SWEEPING);
+ }
+ if (FLAG_sweeper_threads == 0) {
+ FLAG_concurrent_sweeping = false;
+ FLAG_parallel_sweeping = false;
+ }
+ } else if (!FLAG_concurrent_sweeping && !FLAG_parallel_sweeping) {
+ FLAG_sweeper_threads = 0;
+ }
+
+ if (FLAG_parallel_marking) {
+ if (FLAG_marking_threads <= 0) {
+ FLAG_marking_threads = SystemThreadManager::
+ NumberOfParallelSystemThreads(
+ SystemThreadManager::PARALLEL_MARKING);
+ }
+ if (FLAG_marking_threads == 0) {
+ FLAG_parallel_marking = false;
+ }
+ } else {
+ FLAG_marking_threads = 0;
+ }
+
+ if (FLAG_parallel_recompilation &&
+ SystemThreadManager::NumberOfParallelSystemThreads(
+ SystemThreadManager::PARALLEL_RECOMPILATION) == 0) {
+ FLAG_parallel_recompilation = false;
+ }
+
+ OS::SetUp();
+ Sampler::SetUp();
+ CPU::SetUp();
+ use_crankshaft_ = FLAG_crankshaft
+ && !Serializer::enabled()
+ && CPU::SupportsCrankshaft();
+ OS::PostSetUp();
+ ElementsAccessor::InitializeOncePerProcess();
+ LOperand::SetUpCaches();
+ SetUpJSCallerSavedCodeData();
+ ExternalReference::SetUp();
+ Bootstrapper::InitializeOncePerProcess();
+}
+
+
+void V8::InitializeOncePerProcess() {
+ CallOnce(&init_once, &InitializeOncePerProcessImpl);
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/v8.h b/chromium/v8/src/v8.h
new file mode 100644
index 00000000000..47893e8215e
--- /dev/null
+++ b/chromium/v8/src/v8.h
@@ -0,0 +1,161 @@
+// 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.
+
+//
+// Top include for all V8 .cc files.
+//
+
+#ifndef V8_V8_H_
+#define V8_V8_H_
+
+#if defined(GOOGLE3)
+// Google3 special flag handling.
+#if defined(DEBUG) && defined(NDEBUG)
+// V8 only uses DEBUG and whenever it is set we are building a debug
+// version of V8. We do not use NDEBUG and simply undef it here for
+// consistency.
+#undef NDEBUG
+#endif
+#endif // defined(GOOGLE3)
+
+// V8 only uses DEBUG, but included external files
+// may use NDEBUG - make sure they are consistent.
+#if defined(DEBUG) && defined(NDEBUG)
+#error both DEBUG and NDEBUG are set
+#endif
+
+// Basic includes
+#include "../include/v8.h"
+#include "v8globals.h"
+#include "v8checks.h"
+#include "allocation.h"
+#include "assert-scope.h"
+#include "v8utils.h"
+#include "flags.h"
+
+// Objects & heap
+#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 "handles-inl.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+class Deserializer;
+
+class V8 : public AllStatic {
+ public:
+ // Global actions.
+
+ // If Initialize is called with des == NULL, the initial state is
+ // created from scratch. If a non-null Deserializer is given, the
+ // initial state is created by reading the deserialized data into an
+ // empty heap.
+ static bool Initialize(Deserializer* des);
+ static void TearDown();
+ static bool IsRunning() { return is_running_; }
+ static bool UseCrankshaft() { return use_crankshaft_; }
+ // To be dead you have to have lived
+ // TODO(isolates): move IsDead to Isolate.
+ static bool IsDead() { return has_fatal_error_ || has_been_disposed_; }
+ static void SetFatalError();
+
+ // Report process out of memory. Implementation found in api.cc.
+ static void FatalProcessOutOfMemory(const char* location,
+ bool take_snapshot = false);
+
+ // Allows an entropy source to be provided for use in random number
+ // generation.
+ static void SetEntropySource(EntropySource source);
+ // Support for return-address rewriting profilers.
+ static void SetReturnAddressLocationResolver(
+ ReturnAddressLocationResolver resolver);
+ // Support for entry hooking JITed code.
+ static void SetFunctionEntryHook(FunctionEntryHook entry_hook);
+ // Random number generation support. Not cryptographically safe.
+ 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,
+ Context* context);
+
+ // Idle notification directly from the API.
+ static bool IdleNotification(int hint);
+
+ static void AddCallCompletedCallback(CallCompletedCallback callback);
+ static void RemoveCallCompletedCallback(CallCompletedCallback callback);
+ static void FireCallCompletedCallback(Isolate* isolate);
+
+ static v8::ArrayBuffer::Allocator* ArrayBufferAllocator() {
+ return array_buffer_allocator_;
+ }
+
+ static void SetArrayBufferAllocator(v8::ArrayBuffer::Allocator *allocator) {
+ CHECK_EQ(NULL, array_buffer_allocator_);
+ array_buffer_allocator_ = allocator;
+ }
+
+ private:
+ static void InitializeOncePerProcessImpl();
+ static void InitializeOncePerProcess();
+
+ // True if engine is currently running
+ static bool is_running_;
+ // True if V8 has ever been run
+ 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_;
+ // True if engine has been shut down
+ // (reset if engine is restarted)
+ 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_;
+ // Allocator for external array buffers.
+ static v8::ArrayBuffer::Allocator* array_buffer_allocator_;
+};
+
+
+// JavaScript defines two kinds of 'nil'.
+enum NilValue { kNullValue, kUndefinedValue };
+
+
+} } // namespace v8::internal
+
+namespace i = v8::internal;
+
+#endif // V8_V8_H_
diff --git a/chromium/v8/src/v8checks.h b/chromium/v8/src/v8checks.h
new file mode 100644
index 00000000000..9857f73d174
--- /dev/null
+++ b/chromium/v8/src/v8checks.h
@@ -0,0 +1,64 @@
+// 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_V8CHECKS_H_
+#define V8_V8CHECKS_H_
+
+#include "checks.h"
+
+void API_Fatal(const char* location, const char* format, ...);
+
+namespace v8 {
+ class Value;
+ template <class T> class Handle;
+
+namespace internal {
+ intptr_t HeapObjectTagMask();
+
+} } // namespace v8::internal
+
+
+void CheckNonEqualsHelper(const char* file,
+ int line,
+ const char* unexpected_source,
+ v8::Handle<v8::Value> unexpected,
+ const char* value_source,
+ v8::Handle<v8::Value> value);
+
+void CheckEqualsHelper(const char* file,
+ int line,
+ const char* expected_source,
+ v8::Handle<v8::Value> expected,
+ const char* value_source,
+ v8::Handle<v8::Value> value);
+
+#define ASSERT_TAG_ALIGNED(address) \
+ ASSERT((reinterpret_cast<intptr_t>(address) & HeapObjectTagMask()) == 0)
+
+#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & HeapObjectTagMask()) == 0)
+
+#endif // V8_V8CHECKS_H_
diff --git a/chromium/v8/src/v8conversions.cc b/chromium/v8/src/v8conversions.cc
new file mode 100644
index 00000000000..900b62d10b3
--- /dev/null
+++ b/chromium/v8/src/v8conversions.cc
@@ -0,0 +1,132 @@
+// 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 <stdarg.h>
+#include <limits.h>
+
+#include "v8.h"
+
+#include "conversions-inl.h"
+#include "v8conversions.h"
+#include "dtoa.h"
+#include "factory.h"
+#include "strtod.h"
+
+namespace v8 {
+namespace internal {
+
+namespace {
+
+// C++-style iterator adaptor for StringCharacterStream
+// (unlike C++ iterators the end-marker has different type).
+class StringCharacterStreamIterator {
+ public:
+ class EndMarker {};
+
+ explicit StringCharacterStreamIterator(StringCharacterStream* stream);
+
+ uint16_t operator*() const;
+ void operator++();
+ bool operator==(EndMarker const&) const { return end_; }
+ bool operator!=(EndMarker const& m) const { return !end_; }
+
+ private:
+ StringCharacterStream* const stream_;
+ uint16_t current_;
+ bool end_;
+};
+
+
+StringCharacterStreamIterator::StringCharacterStreamIterator(
+ StringCharacterStream* stream) : stream_(stream) {
+ ++(*this);
+}
+
+uint16_t StringCharacterStreamIterator::operator*() const {
+ return current_;
+}
+
+
+void StringCharacterStreamIterator::operator++() {
+ end_ = !stream_->HasMore();
+ if (!end_) {
+ current_ = stream_->GetNext();
+ }
+}
+} // End anonymous namespace.
+
+
+double StringToDouble(UnicodeCache* unicode_cache,
+ String* str, int flags, double empty_string_val) {
+ StringShape shape(str);
+ // TODO(dcarney): Use a Visitor here.
+ if (shape.IsSequentialAscii()) {
+ const uint8_t* begin = SeqOneByteString::cast(str)->GetChars();
+ const uint8_t* end = begin + str->length();
+ return InternalStringToDouble(unicode_cache, begin, end, flags,
+ empty_string_val);
+ } else if (shape.IsSequentialTwoByte()) {
+ const uc16* begin = SeqTwoByteString::cast(str)->GetChars();
+ const uc16* end = begin + str->length();
+ return InternalStringToDouble(unicode_cache, begin, end, flags,
+ empty_string_val);
+ } else {
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(str, &op);
+ return InternalStringToDouble(unicode_cache,
+ StringCharacterStreamIterator(&stream),
+ StringCharacterStreamIterator::EndMarker(),
+ flags,
+ empty_string_val);
+ }
+}
+
+
+double StringToInt(UnicodeCache* unicode_cache,
+ String* str,
+ int radix) {
+ StringShape shape(str);
+ // TODO(dcarney): Use a Visitor here.
+ if (shape.IsSequentialAscii()) {
+ const uint8_t* begin = SeqOneByteString::cast(str)->GetChars();
+ const uint8_t* end = begin + str->length();
+ return InternalStringToInt(unicode_cache, begin, end, radix);
+ } else if (shape.IsSequentialTwoByte()) {
+ const uc16* begin = SeqTwoByteString::cast(str)->GetChars();
+ const uc16* end = begin + str->length();
+ return InternalStringToInt(unicode_cache, begin, end, radix);
+ } else {
+ ConsStringIteratorOp op;
+ StringCharacterStream stream(str, &op);
+ return InternalStringToInt(unicode_cache,
+ StringCharacterStreamIterator(&stream),
+ StringCharacterStreamIterator::EndMarker(),
+ radix);
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/v8conversions.h b/chromium/v8/src/v8conversions.h
new file mode 100644
index 00000000000..3a7b5242ab7
--- /dev/null
+++ b/chromium/v8/src/v8conversions.h
@@ -0,0 +1,73 @@
+// 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_V8CONVERSIONS_H_
+#define V8_V8CONVERSIONS_H_
+
+#include "conversions.h"
+
+namespace v8 {
+namespace internal {
+
+// Convert from Number object to C integer.
+inline int32_t NumberToInt32(Object* number) {
+ if (number->IsSmi()) return Smi::cast(number)->value();
+ return DoubleToInt32(number->Number());
+}
+
+
+inline uint32_t NumberToUint32(Object* number) {
+ if (number->IsSmi()) return Smi::cast(number)->value();
+ return DoubleToUint32(number->Number());
+}
+
+
+// Converts a string into a double value according to ECMA-262 9.3.1
+double StringToDouble(UnicodeCache* unicode_cache,
+ String* str,
+ int flags,
+ double empty_string_val = 0);
+
+// Converts a string into an integer.
+double StringToInt(UnicodeCache* unicode_cache, String* str, int radix);
+
+// Converts a number into size_t.
+inline size_t NumberToSize(Isolate* isolate,
+ Object* number) {
+ SealHandleScope shs(isolate);
+ if (number->IsSmi()) {
+ return Smi::cast(number)->value();
+ } else {
+ ASSERT(number->IsHeapNumber());
+ double value = HeapNumber::cast(number)->value();
+ return static_cast<size_t>(value);
+ }
+}
+
+} } // namespace v8::internal
+
+#endif // V8_V8CONVERSIONS_H_
diff --git a/chromium/v8/src/v8dll-main.cc b/chromium/v8/src/v8dll-main.cc
new file mode 100644
index 00000000000..49d868957d9
--- /dev/null
+++ b/chromium/v8/src/v8dll-main.cc
@@ -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.
+
+// The GYP based build ends up defining USING_V8_SHARED when compiling this
+// file.
+#undef USING_V8_SHARED
+#include "../include/v8.h"
+
+#ifdef WIN32
+#include <windows.h> // NOLINT
+
+extern "C" {
+BOOL WINAPI DllMain(HANDLE hinstDLL,
+ DWORD dwReason,
+ LPVOID lpvReserved) {
+ // Do nothing.
+ return TRUE;
+}
+}
+#endif
diff --git a/chromium/v8/src/v8globals.h b/chromium/v8/src/v8globals.h
new file mode 100644
index 00000000000..6ec75478875
--- /dev/null
+++ b/chromium/v8/src/v8globals.h
@@ -0,0 +1,590 @@
+// Copyright 2012 the V8 project authors. 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_V8GLOBALS_H_
+#define V8_V8GLOBALS_H_
+
+#include "globals.h"
+#include "checks.h"
+
+namespace v8 {
+namespace internal {
+
+// This file contains constants and global declarations related to the
+// V8 system.
+
+// Mask for the sign bit in a smi.
+const intptr_t kSmiSignMask = kIntptrSignBit;
+
+const int kObjectAlignmentBits = kPointerSizeLog2;
+const intptr_t kObjectAlignment = 1 << kObjectAlignmentBits;
+const intptr_t kObjectAlignmentMask = kObjectAlignment - 1;
+
+// Desired alignment for pointers.
+const intptr_t kPointerAlignment = (1 << kPointerSizeLog2);
+const intptr_t kPointerAlignmentMask = kPointerAlignment - 1;
+
+// Desired alignment for double values.
+const intptr_t kDoubleAlignment = 8;
+const intptr_t kDoubleAlignmentMask = kDoubleAlignment - 1;
+
+// Desired alignment for generated code is 32 bytes (to improve cache line
+// utilization).
+const int kCodeAlignmentBits = 5;
+const intptr_t kCodeAlignment = 1 << kCodeAlignmentBits;
+const intptr_t kCodeAlignmentMask = kCodeAlignment - 1;
+
+// Tag information for Failure.
+const int kFailureTag = 3;
+const int kFailureTagSize = 2;
+const intptr_t kFailureTagMask = (1 << kFailureTagSize) - 1;
+
+
+// Zap-value: The value used for zapping dead objects.
+// Should be a recognizable hex value tagged as a failure.
+#ifdef V8_HOST_ARCH_64_BIT
+const Address kZapValue =
+ reinterpret_cast<Address>(V8_UINT64_C(0xdeadbeedbeadbeef));
+const Address kHandleZapValue =
+ reinterpret_cast<Address>(V8_UINT64_C(0x1baddead0baddeaf));
+const Address kGlobalHandleZapValue =
+ reinterpret_cast<Address>(V8_UINT64_C(0x1baffed00baffedf));
+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 kGlobalHandleZapValue = reinterpret_cast<Address>(0xbaffedf);
+const Address kFromSpaceZapValue = reinterpret_cast<Address>(0xbeefdaf);
+const uint32_t kSlotsZapValue = 0xbeefdeef;
+const uint32_t kDebugZapValue = 0xbadbaddb;
+const uint32_t kFreeListZapValue = 0xfeed1eaf;
+#endif
+
+const int kCodeZapValue = 0xbadc0de;
+
+// 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
+// used for aligning data, it doesn't hurt to align on a greater value.
+const int kProcessorCacheLineSize = 64;
+
+// Constants relevant to double precision floating point numbers.
+// 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
+
+class AccessorInfo;
+class Allocation;
+class Arguments;
+class Assembler;
+class Code;
+class CodeGenerator;
+class CodeStub;
+class Context;
+class Debug;
+class Debugger;
+class DebugInfo;
+class Descriptor;
+class DescriptorArray;
+class TransitionArray;
+class ExternalReference;
+class FixedArray;
+class FunctionTemplateInfo;
+class MemoryChunk;
+class SeededNumberDictionary;
+class UnseededNumberDictionary;
+class NameDictionary;
+template <typename T> class Handle;
+class Heap;
+class HeapObject;
+class IC;
+class InterceptorInfo;
+class JSReceiver;
+class JSArray;
+class JSFunction;
+class JSObject;
+class LargeObjectSpace;
+class LookupResult;
+class MacroAssembler;
+class Map;
+class MapSpace;
+class MarkCompactCollector;
+class NewSpace;
+class Object;
+class MaybeObject;
+class OldSpace;
+class Foreign;
+class Scope;
+class ScopeInfo;
+class Script;
+class Smi;
+template <typename Config, class Allocator = FreeStoreAllocationPolicy>
+ class SplayTree;
+class String;
+class Name;
+class Struct;
+class Variable;
+class RelocInfo;
+class Deserializer;
+class MessageLocation;
+class VirtualMemory;
+class Mutex;
+
+typedef bool (*WeakSlotCallback)(Object** pointer);
+
+typedef bool (*WeakSlotCallbackWithHeap)(Heap* heap, Object** pointer);
+
+// -----------------------------------------------------------------------------
+// Miscellaneous
+
+// NOTE: SpaceIterator depends on AllocationSpace enumeration values being
+// consecutive.
+enum AllocationSpace {
+ NEW_SPACE, // Semispaces collected with copying collector.
+ OLD_POINTER_SPACE, // May contain pointers to new space.
+ OLD_DATA_SPACE, // Must not have pointers to new space.
+ CODE_SPACE, // No pointers to new space, marked executable.
+ MAP_SPACE, // Only and all map objects.
+ CELL_SPACE, // Only and all cell objects.
+ PROPERTY_CELL_SPACE, // Only and all global property cell objects.
+ LO_SPACE, // Promoted large objects.
+
+ FIRST_SPACE = NEW_SPACE,
+ LAST_SPACE = LO_SPACE,
+ FIRST_PAGED_SPACE = OLD_POINTER_SPACE,
+ LAST_PAGED_SPACE = PROPERTY_CELL_SPACE
+};
+const int kSpaceTagSize = 3;
+const int kSpaceTagMask = (1 << kSpaceTagSize) - 1;
+
+
+// A flag that indicates whether objects should be pretenured when
+// allocated (allocated directly into the old generation) or not
+// (allocated in the young generation if the object size and type
+// allows).
+enum PretenureFlag { NOT_TENURED, TENURED };
+
+enum GarbageCollector { SCAVENGER, MARK_COMPACTOR };
+
+enum Executability { NOT_EXECUTABLE, EXECUTABLE };
+
+enum VisitMode {
+ VISIT_ALL,
+ VISIT_ALL_IN_SCAVENGE,
+ VISIT_ALL_IN_SWEEP_NEWSPACE,
+ VISIT_ONLY_STRONG
+};
+
+// Flag indicating whether code is built into the VM (one of the natives files).
+enum NativesFlag { NOT_NATIVES_CODE, NATIVES_CODE };
+
+
+// A CodeDesc describes a buffer holding instructions and relocation
+// information. The instructions start at the beginning of the buffer
+// and grow forward, the relocation information starts at the end of
+// the buffer and grows backward.
+//
+// |<--------------- buffer_size ---------------->|
+// |<-- instr_size -->| |<-- reloc_size -->|
+// +==================+========+==================+
+// | instructions | free | reloc info |
+// +==================+========+==================+
+// ^
+// |
+// buffer
+
+struct CodeDesc {
+ byte* buffer;
+ int buffer_size;
+ int instr_size;
+ int reloc_size;
+ Assembler* origin;
+};
+
+
+// Callback function used for iterating objects in heap spaces,
+// for example, scanning heap objects.
+typedef int (*HeapObjectCallback)(HeapObject* obj);
+
+
+// Callback function used for checking constraints when copying/relocating
+// objects. Returns true if an object can be copied/relocated from its
+// old_addr to a new_addr.
+typedef bool (*ConstraintCallback)(Address new_addr, Address old_addr);
+
+
+// Callback function on inline caches, used for iterating over inline caches
+// in compiled code.
+typedef void (*InlineCacheCallback)(Code* code, Address ic);
+
+
+// State for inline cache call sites. Aliased as IC::State.
+enum InlineCacheState {
+ // Has never been executed.
+ UNINITIALIZED,
+ // Has been executed but monomorhic state has been delayed.
+ PREMONOMORPHIC,
+ // Has been executed and only one receiver type has been seen.
+ MONOMORPHIC,
+ // Like MONOMORPHIC but check failed due to prototype.
+ MONOMORPHIC_PROTOTYPE_FAILURE,
+ // Multiple receiver types have been seen.
+ POLYMORPHIC,
+ // Many receiver types have been seen.
+ MEGAMORPHIC,
+ // A generic handler is installed and no extra typefeedback is recorded.
+ GENERIC,
+ // Special state for debug break or step in prepare stubs.
+ DEBUG_STUB
+};
+
+
+enum CheckType {
+ RECEIVER_MAP_CHECK,
+ STRING_CHECK,
+ SYMBOL_CHECK,
+ NUMBER_CHECK,
+ BOOLEAN_CHECK
+};
+
+
+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,
+ // The call target is cached in the instruction stream.
+ RECORD_CALL_TARGET = 1 << 1
+};
+
+
+enum InlineCacheHolderFlag {
+ OWN_MAP, // For fast properties objects.
+ PROTOTYPE_MAP // For slow properties objects (except GlobalObjects).
+};
+
+
+// The Store Buffer (GC).
+typedef enum {
+ kStoreBufferFullEvent,
+ kStoreBufferStartScanningPagesEvent,
+ kStoreBufferScanningPageEvent
+} StoreBufferEvent;
+
+
+typedef void (*StoreBufferCallback)(Heap* heap,
+ MemoryChunk* page,
+ StoreBufferEvent event);
+
+
+// Union used for fast testing of specific double values.
+union DoubleRepresentation {
+ double value;
+ int64_t bits;
+ DoubleRepresentation(double x) { value = x; }
+};
+
+
+// Union used for customized checking of the IEEE double types
+// inlined within v8 runtime, rather than going to the underlying
+// platform headers and libraries
+union IeeeDoubleLittleEndianArchType {
+ double d;
+ struct {
+ unsigned int man_low :32;
+ unsigned int man_high :20;
+ unsigned int exp :11;
+ unsigned int sign :1;
+ } bits;
+};
+
+
+union IeeeDoubleBigEndianArchType {
+ double d;
+ struct {
+ unsigned int sign :1;
+ unsigned int exp :11;
+ unsigned int man_high :20;
+ unsigned int man_low :32;
+ } bits;
+};
+
+
+// AccessorCallback
+struct AccessorDescriptor {
+ MaybeObject* (*getter)(Object* object, void* data);
+ MaybeObject* (*setter)(JSObject* object, Object* value, void* data);
+ void* data;
+};
+
+
+// Logging and profiling. A StateTag represents a possible state of
+// the VM. The logger maintains a stack of these. Creating a VMState
+// object enters a state by pushing on the stack, and destroying a
+// VMState object leaves a state by popping the current state from the
+// stack.
+
+enum StateTag {
+ JS,
+ GC,
+ COMPILER,
+ OTHER,
+ EXTERNAL,
+ IDLE
+};
+
+
+// -----------------------------------------------------------------------------
+// Macros
+
+// Testers for test.
+
+#define HAS_SMI_TAG(value) \
+ ((reinterpret_cast<intptr_t>(value) & kSmiTagMask) == kSmiTag)
+
+#define HAS_FAILURE_TAG(value) \
+ ((reinterpret_cast<intptr_t>(value) & kFailureTagMask) == kFailureTag)
+
+// OBJECT_POINTER_ALIGN returns the value aligned as a HeapObject pointer
+#define OBJECT_POINTER_ALIGN(value) \
+ (((value) + kObjectAlignmentMask) & ~kObjectAlignmentMask)
+
+// POINTER_SIZE_ALIGN returns the value aligned as a pointer.
+#define POINTER_SIZE_ALIGN(value) \
+ (((value) + kPointerAlignmentMask) & ~kPointerAlignmentMask)
+
+// CODE_POINTER_ALIGN returns the value aligned as a generated code segment.
+#define CODE_POINTER_ALIGN(value) \
+ (((value) + kCodeAlignmentMask) & ~kCodeAlignmentMask)
+
+// Support for tracking C++ memory allocation. Insert TRACK_MEMORY("Fisk")
+// inside a C++ class and new and delete will be overloaded so logging is
+// performed.
+// This file (globals.h) is included before log.h, so we use direct calls to
+// the Logger rather than the LOG macro.
+#ifdef DEBUG
+#define TRACK_MEMORY(name) \
+ void* operator new(size_t size) { \
+ void* result = ::operator new(size); \
+ Logger::NewEventStatic(name, result, size); \
+ return result; \
+ } \
+ void operator delete(void* object) { \
+ Logger::DeleteEventStatic(name, object); \
+ ::operator delete(object); \
+ }
+#else
+#define TRACK_MEMORY(name)
+#endif
+
+
+enum CpuImplementer {
+ UNKNOWN_IMPLEMENTER,
+ ARM_IMPLEMENTER,
+ QUALCOMM_IMPLEMENTER
+};
+
+
+enum CpuPart {
+ CPU_UNKNOWN,
+ CORTEX_A15,
+ CORTEX_A12,
+ CORTEX_A9,
+ CORTEX_A8,
+ CORTEX_A7,
+ CORTEX_A5
+};
+
+
+// Feature flags bit positions. They are mostly based on the CPUID spec.
+// (We assign CPUID itself to one of the currently reserved bits --
+// feel free to change this if needed.)
+// On X86/X64, values below 32 are bits in EDX, values above 32 are bits in ECX.
+enum CpuFeature { SSE4_1 = 32 + 19, // x86
+ SSE3 = 32 + 0, // x86
+ SSE2 = 26, // x86
+ CMOV = 15, // x86
+ RDTSC = 4, // x86
+ CPUID = 10, // x86
+ VFP3 = 1, // ARM
+ ARMv7 = 2, // ARM
+ SUDIV = 3, // ARM
+ UNALIGNED_ACCESSES = 4, // ARM
+ MOVW_MOVT_IMMEDIATE_LOADS = 5, // ARM
+ VFP32DREGS = 6, // ARM
+ NEON = 7, // ARM
+ SAHF = 0, // x86
+ FPU = 1}; // MIPS
+
+
+// Used to specify if a macro instruction must perform a smi check on tagged
+// values.
+enum SmiCheckType {
+ DONT_DO_SMI_CHECK,
+ DO_SMI_CHECK
+};
+
+
+// Used to specify whether a receiver is implicitly or explicitly
+// provided to a call.
+enum CallKind {
+ CALL_AS_METHOD,
+ CALL_AS_FUNCTION
+};
+
+
+enum ScopeType {
+ EVAL_SCOPE, // The top-level scope for an eval source.
+ FUNCTION_SCOPE, // The top-level scope for a function.
+ MODULE_SCOPE, // The scope introduced by a module literal
+ 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);
+
+
+// The order of this enum has to be kept in sync with the predicates below.
+enum VariableMode {
+ // User declared variables:
+ VAR, // declared via 'var', and 'function' declarations
+
+ CONST, // declared via 'const' declarations
+
+ LET, // declared via 'let' declarations (first lexical)
+
+ CONST_HARMONY, // declared via 'const' declarations in harmony mode
+
+ MODULE, // declared via 'module' declaration (last lexical)
+
+ // Variables introduced by the compiler:
+ INTERNAL, // like VAR, but not user-visible (may or may not
+ // be in a context)
+
+ TEMPORARY, // temporary variables (not user-visible), stack-allocated
+ // unless the scope as a whole has forced context allocation
+
+ 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
+};
+
+
+inline bool IsDynamicVariableMode(VariableMode mode) {
+ return mode >= DYNAMIC && mode <= DYNAMIC_LOCAL;
+}
+
+
+inline bool IsDeclaredVariableMode(VariableMode mode) {
+ return mode >= VAR && mode <= MODULE;
+}
+
+
+inline bool IsLexicalVariableMode(VariableMode mode) {
+ return mode >= LET && mode <= MODULE;
+}
+
+
+inline bool IsImmutableVariableMode(VariableMode mode) {
+ return mode == CONST || (mode >= CONST_HARMONY && mode <= MODULE);
+}
+
+
+// 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/chromium/v8/src/v8memory.h b/chromium/v8/src/v8memory.h
new file mode 100644
index 00000000000..c72ce7ab7b8
--- /dev/null
+++ b/chromium/v8/src/v8memory.h
@@ -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.
+
+#ifndef V8_MEMORY_H_
+#define V8_MEMORY_H_
+
+namespace v8 {
+namespace internal {
+
+// Memory provides an interface to 'raw' memory. It encapsulates the casts
+// that typically are needed when incompatible pointer types are used.
+
+class Memory {
+ public:
+ static uint8_t& uint8_at(Address addr) {
+ return *reinterpret_cast<uint8_t*>(addr);
+ }
+
+ static uint16_t& uint16_at(Address addr) {
+ return *reinterpret_cast<uint16_t*>(addr);
+ }
+
+ static uint32_t& uint32_at(Address addr) {
+ return *reinterpret_cast<uint32_t*>(addr);
+ }
+
+ static int32_t& int32_at(Address addr) {
+ return *reinterpret_cast<int32_t*>(addr);
+ }
+
+ static uint64_t& uint64_at(Address addr) {
+ return *reinterpret_cast<uint64_t*>(addr);
+ }
+
+ static int& int_at(Address addr) {
+ return *reinterpret_cast<int*>(addr);
+ }
+
+ static unsigned& unsigned_at(Address addr) {
+ return *reinterpret_cast<unsigned*>(addr);
+ }
+
+ static intptr_t& intptr_at(Address addr) {
+ return *reinterpret_cast<intptr_t*>(addr);
+ }
+
+ static uintptr_t& uintptr_at(Address addr) {
+ return *reinterpret_cast<uintptr_t*>(addr);
+ }
+
+ static double& double_at(Address addr) {
+ return *reinterpret_cast<double*>(addr);
+ }
+
+ static Address& Address_at(Address addr) {
+ return *reinterpret_cast<Address*>(addr);
+ }
+
+ static Object*& Object_at(Address addr) {
+ return *reinterpret_cast<Object**>(addr);
+ }
+
+ static Handle<Object>& Object_Handle_at(Address addr) {
+ return *reinterpret_cast<Handle<Object>*>(addr);
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_MEMORY_H_
diff --git a/chromium/v8/src/v8natives.js b/chromium/v8/src/v8natives.js
new file mode 100644
index 00000000000..76eeac6a58f
--- /dev/null
+++ b/chromium/v8/src/v8natives.js
@@ -0,0 +1,1838 @@
+// Copyright 2012 the V8 project authors. 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 declarations have been made
+// in runtime.js:
+// var $Object = global.Object;
+// var $Boolean = global.Boolean;
+// var $Number = global.Number;
+// var $Function = global.Function;
+// var $Array = global.Array;
+// var $NaN = 0/0;
+//
+// in math.js:
+// var $floor = MathFloor
+
+var $isNaN = GlobalIsNaN;
+var $isFinite = GlobalIsFinite;
+
+// ----------------------------------------------------------------------------
+
+// Helper function used to install functions on objects.
+function InstallFunctions(object, attributes, functions) {
+ if (functions.length >= 8) {
+ %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
+ }
+ for (var i = 0; i < functions.length; i += 2) {
+ var key = functions[i];
+ var f = functions[i + 1];
+ %FunctionSetName(f, key);
+ %FunctionRemovePrototype(f);
+ %SetProperty(object, key, f, attributes);
+ %SetNativeFlag(f);
+ }
+ %ToFastProperties(object);
+}
+
+
+// Helper function to install a getter-only accessor property.
+function InstallGetter(object, name, getter) {
+ %FunctionSetName(getter, name);
+ %FunctionRemovePrototype(getter);
+ %DefineOrRedefineAccessorProperty(object, name, getter, null, DONT_ENUM);
+ %SetNativeFlag(getter);
+}
+
+
+// Helper function to install a getter/setter accessor property.
+function InstallGetterSetter(object, name, getter, setter) {
+ %FunctionSetName(getter, name);
+ %FunctionSetName(setter, name);
+ %FunctionRemovePrototype(getter);
+ %FunctionRemovePrototype(setter);
+ %DefineOrRedefineAccessorProperty(object, name, getter, setter, DONT_ENUM);
+ %SetNativeFlag(getter);
+ %SetNativeFlag(setter);
+}
+
+
+// Prevents changes to the prototype of a built-in function.
+// The "prototype" property of the function object is made non-configurable,
+// and the prototype object is made non-extensible. The latter prevents
+// changing the __proto__ property.
+function SetUpLockedPrototype(constructor, fields, methods) {
+ %CheckIsBootstrapping();
+ var prototype = constructor.prototype;
+ // Install functions first, because this function is used to initialize
+ // PropertyDescriptor itself.
+ var property_count = (methods.length >> 1) + (fields ? fields.length : 0);
+ if (property_count >= 4) {
+ %OptimizeObjectForAddingMultipleProperties(prototype, property_count);
+ }
+ if (fields) {
+ for (var i = 0; i < fields.length; i++) {
+ %SetProperty(prototype, fields[i], void 0, DONT_ENUM | DONT_DELETE);
+ }
+ }
+ for (var i = 0; i < methods.length; i += 2) {
+ var key = methods[i];
+ var f = methods[i + 1];
+ %SetProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetNativeFlag(f);
+ }
+ %SetPrototype(prototype, null);
+ %ToFastProperties(prototype);
+}
+
+
+// ----------------------------------------------------------------------------
+
+
+// ECMA 262 - 15.1.4
+function GlobalIsNaN(number) {
+ if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
+ return NUMBER_IS_NAN(number);
+}
+
+
+// ECMA 262 - 15.1.5
+function GlobalIsFinite(number) {
+ if (!IS_NUMBER(number)) number = NonNumberToNumber(number);
+ return NUMBER_IS_FINITE(number);
+}
+
+
+// ECMA-262 - 15.1.2.2
+function GlobalParseInt(string, radix) {
+ if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
+ // Some people use parseInt instead of Math.floor. This
+ // optimization makes parseInt on a Smi 12 times faster (60ns
+ // vs 800ns). The following optimization makes parseInt on a
+ // non-Smi number 9 times faster (230ns vs 2070ns). Together
+ // they make parseInt on a string 1.4% slower (274ns vs 270ns).
+ if (%_IsSmi(string)) return string;
+ if (IS_NUMBER(string) &&
+ ((0.01 < string && string < 1e9) ||
+ (-1e9 < string && string < -0.01))) {
+ // Truncate number.
+ return string | 0;
+ }
+ string = TO_STRING_INLINE(string);
+ radix = radix | 0;
+ } else {
+ // 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))) {
+ return $NaN;
+ }
+ }
+
+ if (%_HasCachedArrayIndex(string) &&
+ (radix == 0 || radix == 10)) {
+ return %_GetCachedArrayIndex(string);
+ }
+ return %StringParseInt(string, radix);
+}
+
+
+// ECMA-262 - 15.1.2.3
+function GlobalParseFloat(string) {
+ string = TO_STRING_INLINE(string);
+ if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
+ return %StringParseFloat(string);
+}
+
+
+function GlobalEval(x) {
+ if (!IS_STRING(x)) return x;
+
+ var global_receiver = %GlobalReceiver(global);
+ 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.
+ // 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, false);
+ if (!IS_FUNCTION(f)) return f;
+
+ return %_CallFunction(global_receiver, f);
+}
+
+
+// ----------------------------------------------------------------------------
+
+// Set up global object.
+function SetUpGlobal() {
+ %CheckIsBootstrapping();
+
+ // ECMA 262 - 15.1.1.1.
+ %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 | READ_ONLY);
+
+ // ECMA-262 - 15.1.1.3.
+ %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(
+ "isNaN", GlobalIsNaN,
+ "isFinite", GlobalIsFinite,
+ "parseInt", GlobalParseInt,
+ "parseFloat", GlobalParseFloat,
+ "eval", GlobalEval
+ ));
+}
+
+SetUpGlobal();
+
+
+// ----------------------------------------------------------------------------
+// Object
+
+// ECMA-262 - 15.2.4.2
+function ObjectToString() {
+ if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
+ if (IS_NULL(this)) return "[object Null]";
+ return "[object " + %_ClassOf(ToObject(this)) + "]";
+}
+
+
+// ECMA-262 - 15.2.4.3
+function ObjectToLocaleString() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Object.prototype.toLocaleString"]);
+ }
+ return this.toString();
+}
+
+
+// ECMA-262 - 15.2.4.4
+function ObjectValueOf() {
+ return ToObject(this);
+}
+
+
+// ECMA-262 - 15.2.4.5
+function ObjectHasOwnProperty(V) {
+ if (%IsJSProxy(this)) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(V)) return false;
+
+ var handler = %GetHandler(this);
+ return CallTrap1(handler, "hasOwn", DerivedHasOwnTrap, ToName(V));
+ }
+ return %HasLocalProperty(TO_OBJECT_INLINE(this), ToName(V));
+}
+
+
+// ECMA-262 - 15.2.4.6
+function ObjectIsPrototypeOf(V) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Object.prototype.isPrototypeOf"]);
+ }
+ if (!IS_SPEC_OBJECT(V)) return false;
+ return %IsInPrototypeChain(this, V);
+}
+
+
+// ECMA-262 - 15.2.4.6
+function ObjectPropertyIsEnumerable(V) {
+ var P = ToName(V);
+ if (%IsJSProxy(this)) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(V)) return false;
+
+ var desc = GetOwnProperty(this, P);
+ return IS_UNDEFINED(desc) ? false : desc.isEnumerable();
+ }
+ return %IsPropertyEnumerable(ToObject(this), P);
+}
+
+
+// Extensions for providing property getters and setters.
+function ObjectDefineGetter(name, fun) {
+ var receiver = this;
+ if (receiver == null && !IS_UNDETECTABLE(receiver)) {
+ receiver = %GlobalReceiver(global);
+ }
+ if (!IS_SPEC_FUNCTION(fun)) {
+ throw new $TypeError(
+ 'Object.prototype.__defineGetter__: Expecting function');
+ }
+ var desc = new PropertyDescriptor();
+ desc.setGet(fun);
+ desc.setEnumerable(true);
+ desc.setConfigurable(true);
+ DefineOwnProperty(ToObject(receiver), ToName(name), desc, false);
+}
+
+
+function ObjectLookupGetter(name) {
+ var receiver = this;
+ if (receiver == null && !IS_UNDETECTABLE(receiver)) {
+ receiver = %GlobalReceiver(global);
+ }
+ return %LookupAccessor(ToObject(receiver), ToName(name), GETTER);
+}
+
+
+function ObjectDefineSetter(name, fun) {
+ var receiver = this;
+ if (receiver == null && !IS_UNDETECTABLE(receiver)) {
+ receiver = %GlobalReceiver(global);
+ }
+ if (!IS_SPEC_FUNCTION(fun)) {
+ throw new $TypeError(
+ 'Object.prototype.__defineSetter__: Expecting function');
+ }
+ var desc = new PropertyDescriptor();
+ desc.setSet(fun);
+ desc.setEnumerable(true);
+ desc.setConfigurable(true);
+ DefineOwnProperty(ToObject(receiver), ToName(name), desc, false);
+}
+
+
+function ObjectLookupSetter(name) {
+ var receiver = this;
+ if (receiver == null && !IS_UNDETECTABLE(receiver)) {
+ receiver = %GlobalReceiver(global);
+ }
+ return %LookupAccessor(ToObject(receiver), ToName(name), SETTER);
+}
+
+
+function ObjectKeys(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.keys"]);
+ }
+ if (%IsJSProxy(obj)) {
+ var handler = %GetHandler(obj);
+ var names = CallTrap0(handler, "keys", DerivedKeysTrap);
+ return ToNameArray(names, "keys", false);
+ }
+ return %LocalKeys(obj);
+}
+
+
+// ES5 8.10.1.
+function IsAccessorDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return false;
+ return desc.hasGetter() || desc.hasSetter();
+}
+
+
+// ES5 8.10.2.
+function IsDataDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return false;
+ return desc.hasValue() || desc.hasWritable();
+}
+
+
+// ES5 8.10.3.
+function IsGenericDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return false;
+ return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
+}
+
+
+function IsInconsistentDescriptor(desc) {
+ return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
+}
+
+
+// ES5 8.10.4
+function FromPropertyDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return desc;
+
+ if (IsDataDescriptor(desc)) {
+ return { value: desc.getValue(),
+ writable: desc.isWritable(),
+ enumerable: desc.isEnumerable(),
+ configurable: desc.isConfigurable() };
+ }
+ // Must be an AccessorDescriptor then. We never return a generic descriptor.
+ return { get: desc.getGet(),
+ set: desc.getSet() === ObjectSetProto ? ObjectPoisonProto
+ : desc.getSet(),
+ enumerable: desc.isEnumerable(),
+ configurable: desc.isConfigurable() };
+}
+
+
+// Harmony Proxies
+function FromGenericPropertyDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return desc;
+ var obj = new $Object();
+
+ if (desc.hasValue()) {
+ %IgnoreAttributesAndSetProperty(obj, "value", desc.getValue(), NONE);
+ }
+ if (desc.hasWritable()) {
+ %IgnoreAttributesAndSetProperty(obj, "writable", desc.isWritable(), NONE);
+ }
+ if (desc.hasGetter()) {
+ %IgnoreAttributesAndSetProperty(obj, "get", desc.getGet(), NONE);
+ }
+ if (desc.hasSetter()) {
+ %IgnoreAttributesAndSetProperty(obj, "set", desc.getSet(), NONE);
+ }
+ if (desc.hasEnumerable()) {
+ %IgnoreAttributesAndSetProperty(obj, "enumerable",
+ desc.isEnumerable(), NONE);
+ }
+ if (desc.hasConfigurable()) {
+ %IgnoreAttributesAndSetProperty(obj, "configurable",
+ desc.isConfigurable(), NONE);
+ }
+ return obj;
+}
+
+
+// ES5 8.10.5.
+function ToPropertyDescriptor(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("property_desc_object", [obj]);
+ }
+ var desc = new PropertyDescriptor();
+
+ if ("enumerable" in obj) {
+ desc.setEnumerable(ToBoolean(obj.enumerable));
+ }
+
+ if ("configurable" in obj) {
+ desc.setConfigurable(ToBoolean(obj.configurable));
+ }
+
+ if ("value" in obj) {
+ desc.setValue(obj.value);
+ }
+
+ if ("writable" in obj) {
+ desc.setWritable(ToBoolean(obj.writable));
+ }
+
+ if ("get" in obj) {
+ var get = obj.get;
+ if (!IS_UNDEFINED(get) && !IS_SPEC_FUNCTION(get)) {
+ throw MakeTypeError("getter_must_be_callable", [get]);
+ }
+ desc.setGet(get);
+ }
+
+ if ("set" in obj) {
+ var set = obj.set;
+ if (!IS_UNDEFINED(set) && !IS_SPEC_FUNCTION(set)) {
+ throw MakeTypeError("setter_must_be_callable", [set]);
+ }
+ desc.setSet(set);
+ }
+
+ if (IsInconsistentDescriptor(desc)) {
+ throw MakeTypeError("value_and_accessor", [obj]);
+ }
+ return desc;
+}
+
+
+// For Harmony proxies.
+function ToCompletePropertyDescriptor(obj) {
+ var desc = ToPropertyDescriptor(obj);
+ if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) {
+ if (!desc.hasValue()) desc.setValue(void 0);
+ if (!desc.hasWritable()) desc.setWritable(false);
+ } else {
+ // Is accessor descriptor.
+ if (!desc.hasGetter()) desc.setGet(void 0);
+ if (!desc.hasSetter()) desc.setSet(void 0);
+ }
+ if (!desc.hasEnumerable()) desc.setEnumerable(false);
+ if (!desc.hasConfigurable()) desc.setConfigurable(false);
+ return desc;
+}
+
+
+function PropertyDescriptor() {
+ // Initialize here so they are all in-object and have the same map.
+ // Default values from ES5 8.6.1.
+ this.value_ = void 0;
+ this.hasValue_ = false;
+ this.writable_ = false;
+ this.hasWritable_ = false;
+ this.enumerable_ = false;
+ this.hasEnumerable_ = false;
+ this.configurable_ = false;
+ this.hasConfigurable_ = false;
+ this.get_ = void 0;
+ this.hasGetter_ = false;
+ this.set_ = void 0;
+ this.hasSetter_ = false;
+}
+
+SetUpLockedPrototype(PropertyDescriptor, $Array(
+ "value_",
+ "hasValue_",
+ "writable_",
+ "hasWritable_",
+ "enumerable_",
+ "hasEnumerable_",
+ "configurable_",
+ "hasConfigurable_",
+ "get_",
+ "hasGetter_",
+ "set_",
+ "hasSetter_"
+ ), $Array(
+ "toString", function() {
+ return "[object PropertyDescriptor]";
+ },
+ "setValue", function(value) {
+ this.value_ = value;
+ this.hasValue_ = true;
+ },
+ "getValue", function() {
+ return this.value_;
+ },
+ "hasValue", function() {
+ return this.hasValue_;
+ },
+ "setEnumerable", function(enumerable) {
+ this.enumerable_ = enumerable;
+ this.hasEnumerable_ = true;
+ },
+ "isEnumerable", function () {
+ return this.enumerable_;
+ },
+ "hasEnumerable", function() {
+ return this.hasEnumerable_;
+ },
+ "setWritable", function(writable) {
+ this.writable_ = writable;
+ this.hasWritable_ = true;
+ },
+ "isWritable", function() {
+ return this.writable_;
+ },
+ "hasWritable", function() {
+ return this.hasWritable_;
+ },
+ "setConfigurable", function(configurable) {
+ this.configurable_ = configurable;
+ this.hasConfigurable_ = true;
+ },
+ "hasConfigurable", function() {
+ return this.hasConfigurable_;
+ },
+ "isConfigurable", function() {
+ return this.configurable_;
+ },
+ "setGet", function(get) {
+ this.get_ = get;
+ this.hasGetter_ = true;
+ },
+ "getGet", function() {
+ return this.get_;
+ },
+ "hasGetter", function() {
+ return this.hasGetter_;
+ },
+ "setSet", function(set) {
+ this.set_ = set;
+ this.hasSetter_ = true;
+ },
+ "getSet", function() {
+ return this.set_;
+ },
+ "hasSetter", function() {
+ return this.hasSetter_;
+ }));
+
+
+// Converts an array returned from Runtime_GetOwnProperty to an actual
+// property descriptor. For a description of the array layout please
+// see the runtime.cc file.
+function ConvertDescriptorArrayToDescriptor(desc_array) {
+ if (desc_array === false) {
+ throw 'Internal error: invalid desc_array';
+ }
+
+ if (IS_UNDEFINED(desc_array)) {
+ return void 0;
+ }
+
+ var desc = new PropertyDescriptor();
+ // This is an accessor.
+ if (desc_array[IS_ACCESSOR_INDEX]) {
+ desc.setGet(desc_array[GETTER_INDEX]);
+ desc.setSet(desc_array[SETTER_INDEX]);
+ } else {
+ desc.setValue(desc_array[VALUE_INDEX]);
+ desc.setWritable(desc_array[WRITABLE_INDEX]);
+ }
+ desc.setEnumerable(desc_array[ENUMERABLE_INDEX]);
+ desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]);
+
+ return desc;
+}
+
+
+// For Harmony proxies.
+function GetTrap(handler, name, defaultTrap) {
+ var trap = handler[name];
+ if (IS_UNDEFINED(trap)) {
+ if (IS_UNDEFINED(defaultTrap)) {
+ throw MakeTypeError("handler_trap_missing", [handler, name]);
+ }
+ trap = defaultTrap;
+ } else if (!IS_SPEC_FUNCTION(trap)) {
+ throw MakeTypeError("handler_trap_must_be_callable", [handler, name]);
+ }
+ return trap;
+}
+
+
+function CallTrap0(handler, name, defaultTrap) {
+ return %_CallFunction(handler, GetTrap(handler, name, defaultTrap));
+}
+
+
+function CallTrap1(handler, name, defaultTrap, x) {
+ return %_CallFunction(handler, x, GetTrap(handler, name, defaultTrap));
+}
+
+
+function CallTrap2(handler, name, defaultTrap, x, y) {
+ return %_CallFunction(handler, x, y, GetTrap(handler, name, defaultTrap));
+}
+
+
+// ES5 section 8.12.1.
+function GetOwnProperty(obj, v) {
+ var p = ToName(v);
+ if (%IsJSProxy(obj)) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(v)) return void 0;
+
+ var handler = %GetHandler(obj);
+ var descriptor = CallTrap1(handler, "getOwnPropertyDescriptor", void 0, p);
+ if (IS_UNDEFINED(descriptor)) return descriptor;
+ var desc = ToCompletePropertyDescriptor(descriptor);
+ if (!desc.isConfigurable()) {
+ throw MakeTypeError("proxy_prop_not_configurable",
+ [handler, "getOwnPropertyDescriptor", p, descriptor]);
+ }
+ return desc;
+ }
+
+ // GetOwnProperty returns an array indexed by the constants
+ // defined in macros.py.
+ // If p is not a property on obj undefined is returned.
+ var props = %GetOwnProperty(ToObject(obj), p);
+
+ // A false value here means that access checks failed.
+ if (props === false) return void 0;
+
+ return ConvertDescriptorArrayToDescriptor(props);
+}
+
+
+// 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) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(p)) return false;
+
+ var handler = %GetHandler(obj);
+ var result = CallTrap2(handler, "defineProperty", void 0, p, attributes);
+ if (!ToBoolean(result)) {
+ if (should_throw) {
+ throw MakeTypeError("handler_returned_false",
+ [handler, "defineProperty"]);
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// ES5 8.12.9.
+function DefineObjectProperty(obj, p, desc, should_throw) {
+ var current_or_access = %GetOwnProperty(ToObject(obj), ToName(p));
+ // A false value here means that access checks failed.
+ if (current_or_access === false) return void 0;
+
+ var current = ConvertDescriptorArrayToDescriptor(current_or_access);
+ var extensible = %IsExtensible(ToObject(obj));
+
+ // Error handling according to spec.
+ // Step 3
+ if (IS_UNDEFINED(current) && !extensible) {
+ if (should_throw) {
+ throw MakeTypeError("define_disallowed", [p]);
+ } else {
+ return false;
+ }
+ }
+
+ if (!IS_UNDEFINED(current)) {
+ // Step 5 and 6
+ if ((IsGenericDescriptor(desc) ||
+ IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
+ (!desc.hasEnumerable() ||
+ SameValue(desc.isEnumerable(), current.isEnumerable())) &&
+ (!desc.hasConfigurable() ||
+ SameValue(desc.isConfigurable(), current.isConfigurable())) &&
+ (!desc.hasWritable() ||
+ SameValue(desc.isWritable(), current.isWritable())) &&
+ (!desc.hasValue() ||
+ SameValue(desc.getValue(), current.getValue())) &&
+ (!desc.hasGetter() ||
+ SameValue(desc.getGet(), current.getGet())) &&
+ (!desc.hasSetter() ||
+ SameValue(desc.getSet(), current.getSet()))) {
+ return true;
+ }
+ if (!current.isConfigurable()) {
+ // Step 7
+ if (desc.isConfigurable() ||
+ (desc.hasEnumerable() &&
+ desc.isEnumerable() != current.isEnumerable())) {
+ if (should_throw) {
+ throw MakeTypeError("redefine_disallowed", [p]);
+ } else {
+ return false;
+ }
+ }
+ // Step 8
+ if (!IsGenericDescriptor(desc)) {
+ // Step 9a
+ if (IsDataDescriptor(current) != IsDataDescriptor(desc)) {
+ if (should_throw) {
+ throw MakeTypeError("redefine_disallowed", [p]);
+ } else {
+ return false;
+ }
+ }
+ // Step 10a
+ if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
+ if (!current.isWritable() && desc.isWritable()) {
+ if (should_throw) {
+ throw MakeTypeError("redefine_disallowed", [p]);
+ } else {
+ return false;
+ }
+ }
+ if (!current.isWritable() && desc.hasValue() &&
+ !SameValue(desc.getValue(), current.getValue())) {
+ if (should_throw) {
+ throw MakeTypeError("redefine_disallowed", [p]);
+ } else {
+ return false;
+ }
+ }
+ }
+ // Step 11
+ if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
+ if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) {
+ if (should_throw) {
+ throw MakeTypeError("redefine_disallowed", [p]);
+ } else {
+ return false;
+ }
+ }
+ if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) {
+ if (should_throw) {
+ throw MakeTypeError("redefine_disallowed", [p]);
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Send flags - enumerable and configurable are common - writable is
+ // only send to the data descriptor.
+ // Take special care if enumerable and configurable is not defined on
+ // desc (we need to preserve the existing values from current).
+ var flag = NONE;
+ if (desc.hasEnumerable()) {
+ flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
+ } else if (!IS_UNDEFINED(current)) {
+ flag |= current.isEnumerable() ? 0 : DONT_ENUM;
+ } else {
+ flag |= DONT_ENUM;
+ }
+
+ if (desc.hasConfigurable()) {
+ flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
+ } else if (!IS_UNDEFINED(current)) {
+ flag |= current.isConfigurable() ? 0 : DONT_DELETE;
+ } else
+ flag |= DONT_DELETE;
+
+ if (IsDataDescriptor(desc) ||
+ (IsGenericDescriptor(desc) &&
+ (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
+ // There are 3 cases that lead here:
+ // Step 4a - defining a new data property.
+ // Steps 9b & 12 - replacing an existing accessor property with a data
+ // property.
+ // Step 12 - updating an existing data property with a data or generic
+ // descriptor.
+
+ if (desc.hasWritable()) {
+ flag |= desc.isWritable() ? 0 : READ_ONLY;
+ } else if (!IS_UNDEFINED(current)) {
+ flag |= current.isWritable() ? 0 : READ_ONLY;
+ } else {
+ flag |= READ_ONLY;
+ }
+
+ var value = void 0; // Default value is undefined.
+ if (desc.hasValue()) {
+ value = desc.getValue();
+ } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
+ value = current.getValue();
+ }
+
+ %DefineOrRedefineDataProperty(obj, p, value, flag);
+ } else {
+ // There are 3 cases that lead here:
+ // Step 4b - defining a new accessor property.
+ // Steps 9c & 12 - replacing an existing data property with an accessor
+ // property.
+ // Step 12 - updating an existing accessor property with an accessor
+ // descriptor.
+ var getter = desc.hasGetter() ? desc.getGet() : null;
+ var setter = desc.hasSetter() ? desc.getSet() : null;
+ %DefineOrRedefineAccessorProperty(obj, p, getter, setter, flag);
+ }
+ return true;
+}
+
+
+// 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;
+ var old_length = 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;
+
+ var emit_splice = %IsObserved(obj) && new_length !== old_length;
+ var removed;
+ if (emit_splice) {
+ BeginPerformSplice(obj);
+ removed = [];
+ if (new_length < old_length)
+ removed.length = old_length - new_length;
+ }
+
+ while (new_length < length--) {
+ var index = ToString(length);
+ if (emit_splice) {
+ var deletedDesc = GetOwnProperty(obj, index);
+ if (deletedDesc && deletedDesc.hasValue())
+ removed[length - new_length] = deletedDesc.getValue();
+ }
+ if (!Delete(obj, index, 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.
+ // TODO(mstarzinger): This hack should be removed once we have addressed the
+ // respective TODO in Runtime_DefineOrRedefineDataProperty.
+ // For the time being, we need a hack to prevent Object.observe from
+ // generating two change records.
+ obj.length = new_length;
+ desc.value_ = void 0;
+ desc.hasValue_ = false;
+ threw = !DefineObjectProperty(obj, "length", desc, should_throw) || threw;
+ if (emit_splice) {
+ EndPerformSplice(obj);
+ EnqueueSpliceRecord(obj,
+ new_length < old_length ? new_length : old_length,
+ removed,
+ new_length > old_length ? new_length - old_length : 0);
+ }
+ if (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);
+ var emit_splice = false;
+ if (ToString(index) == p && index != 4294967295) {
+ var length = obj.length;
+ if (index >= length && %IsObserved(obj)) {
+ emit_splice = true;
+ BeginPerformSplice(obj);
+ }
+
+ var length_desc = GetOwnProperty(obj, "length");
+ if ((index >= length && !length_desc.isWritable()) ||
+ !DefineObjectProperty(obj, p, desc, true)) {
+ if (emit_splice)
+ EndPerformSplice(obj);
+ if (should_throw) {
+ throw MakeTypeError("define_disallowed", [p]);
+ } else {
+ return false;
+ }
+ }
+ if (index >= length) {
+ obj.length = index + 1;
+ }
+ if (emit_splice) {
+ EndPerformSplice(obj);
+ EnqueueSpliceRecord(obj, length, [], index + 1 - length);
+ }
+ 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)) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(p)) return false;
+
+ 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)) {
+ throw MakeTypeError("called_on_non_object", ["Object.getPrototypeOf"]);
+ }
+ return %GetPrototype(obj);
+}
+
+
+// ES5 section 15.2.3.3
+function ObjectGetOwnPropertyDescriptor(obj, p) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object",
+ ["Object.getOwnPropertyDescriptor"]);
+ }
+ var desc = GetOwnProperty(obj, p);
+ return FromPropertyDescriptor(desc);
+}
+
+
+// For Harmony proxies
+function ToNameArray(obj, trap, includeSymbols) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("proxy_non_object_prop_names", [obj, trap]);
+ }
+ var n = ToUint32(obj.length);
+ var array = new $Array(n);
+ var realLength = 0;
+ var names = { __proto__: null }; // TODO(rossberg): use sets once ready.
+ for (var index = 0; index < n; index++) {
+ var s = ToName(obj[index]);
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(s) && !includeSymbols) continue;
+ if (%HasLocalProperty(names, s)) {
+ throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s]);
+ }
+ array[index] = s;
+ ++realLength;
+ names[s] = 0;
+ }
+ array.length = realLength;
+ return array;
+}
+
+
+// ES5 section 15.2.3.4.
+function ObjectGetOwnPropertyNames(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.getOwnPropertyNames"]);
+ }
+ // Special handling for proxies.
+ if (%IsJSProxy(obj)) {
+ var handler = %GetHandler(obj);
+ var names = CallTrap0(handler, "getOwnPropertyNames", void 0);
+ return ToNameArray(names, "getOwnPropertyNames", false);
+ }
+
+ var nameArrays = new InternalArray();
+
+ // Find all the indexed properties.
+
+ // Get the local element names.
+ var localElementNames = %GetLocalElementNames(obj);
+ for (var i = 0; i < localElementNames.length; ++i) {
+ localElementNames[i] = %_NumberToString(localElementNames[i]);
+ }
+ nameArrays.push(localElementNames);
+
+ // Get names for indexed interceptor properties.
+ var interceptorInfo = %GetInterceptorInfo(obj);
+ if ((interceptorInfo & 1) != 0) {
+ var indexedInterceptorNames = %GetIndexedInterceptorElementNames(obj);
+ if (!IS_UNDEFINED(indexedInterceptorNames)) {
+ nameArrays.push(indexedInterceptorNames);
+ }
+ }
+
+ // Find all the named properties.
+
+ // Get the local property names.
+ nameArrays.push(%GetLocalPropertyNames(obj, false));
+
+ // Get names for named interceptor properties if any.
+ if ((interceptorInfo & 2) != 0) {
+ var namedInterceptorNames = %GetNamedInterceptorPropertyNames(obj);
+ if (!IS_UNDEFINED(namedInterceptorNames)) {
+ nameArrays.push(namedInterceptorNames);
+ }
+ }
+
+ var propertyNames =
+ %Apply(InternalArray.prototype.concat,
+ nameArrays[0], nameArrays, 1, nameArrays.length - 1);
+
+ // Property names are expected to be unique strings,
+ // but interceptors can interfere with that assumption.
+ if (interceptorInfo != 0) {
+ var propertySet = { __proto__: null };
+ var j = 0;
+ for (var i = 0; i < propertyNames.length; ++i) {
+ if (IS_SYMBOL(propertyNames[i])) continue;
+ var name = ToString(propertyNames[i]);
+ // 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) {
+ continue;
+ }
+ propertySet[name] = true;
+ propertyNames[j++] = name;
+ }
+ propertyNames.length = j;
+ }
+
+ return propertyNames;
+}
+
+
+// ES5 section 15.2.3.5.
+function ObjectCreate(proto, properties) {
+ if (!IS_SPEC_OBJECT(proto) && proto !== null) {
+ throw MakeTypeError("proto_object_or_null", [proto]);
+ }
+ var obj = { __proto__: proto };
+ if (!IS_UNDEFINED(properties)) ObjectDefineProperties(obj, properties);
+ return obj;
+}
+
+
+// ES5 section 15.2.3.6.
+function ObjectDefineProperty(obj, p, attributes) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.defineProperty"]);
+ }
+ var name = ToName(p);
+ if (%IsJSProxy(obj)) {
+ // Clone the attributes object for protection.
+ // TODO(rossberg): not spec'ed yet, so not sure if this should involve
+ // non-own properties as it does (or non-enumerable ones, as it doesn't?).
+ var attributesClone = { __proto__: null };
+ for (var a in attributes) {
+ attributesClone[a] = attributes[a];
+ }
+ DefineProxyProperty(obj, name, attributesClone, true);
+ // The following would implement the spec as in the current proposal,
+ // but after recent comments on es-discuss, is most likely obsolete.
+ /*
+ var defineObj = FromGenericPropertyDescriptor(desc);
+ var names = ObjectGetOwnPropertyNames(attributes);
+ var standardNames =
+ {value: 0, writable: 0, get: 0, set: 0, enumerable: 0, configurable: 0};
+ for (var i = 0; i < names.length; i++) {
+ var N = names[i];
+ if (!(%HasLocalProperty(standardNames, N))) {
+ var attr = GetOwnProperty(attributes, N);
+ DefineOwnProperty(descObj, N, attr, true);
+ }
+ }
+ // This is really confusing the types, but it is what the proxies spec
+ // currently requires:
+ desc = descObj;
+ */
+ } else {
+ var desc = ToPropertyDescriptor(attributes);
+ DefineOwnProperty(obj, name, desc, true);
+ }
+ return obj;
+}
+
+
+function GetOwnEnumerablePropertyNames(properties) {
+ var names = new InternalArray();
+ for (var key in properties) {
+ if (%HasLocalProperty(properties, key)) {
+ names.push(key);
+ }
+ }
+ return names;
+}
+
+
+// ES5 section 15.2.3.7.
+function ObjectDefineProperties(obj, properties) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.defineProperties"]);
+ }
+ var props = ToObject(properties);
+ var names = GetOwnEnumerablePropertyNames(props);
+ var descriptors = new InternalArray();
+ for (var i = 0; i < names.length; i++) {
+ descriptors.push(ToPropertyDescriptor(props[names[i]]));
+ }
+ for (var i = 0; i < names.length; i++) {
+ DefineOwnProperty(obj, names[i], descriptors[i], true);
+ }
+ return obj;
+}
+
+
+// Harmony proxies.
+function ProxyFix(obj) {
+ var handler = %GetHandler(obj);
+ var props = CallTrap0(handler, "fix", void 0);
+ if (IS_UNDEFINED(props)) {
+ throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
+ }
+
+ 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);
+ }
+ ObjectDefineProperties(obj, props);
+}
+
+
+// ES5 section 15.2.3.8.
+function ObjectSeal(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.seal"]);
+ }
+ if (%IsJSProxy(obj)) {
+ ProxyFix(obj);
+ }
+ var names = ObjectGetOwnPropertyNames(obj);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ var desc = GetOwnProperty(obj, name);
+ if (desc.isConfigurable()) {
+ desc.setConfigurable(false);
+ DefineOwnProperty(obj, name, desc, true);
+ }
+ }
+ %PreventExtensions(obj);
+ return obj;
+}
+
+
+// ES5 section 15.2.3.9.
+function ObjectFreeze(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.freeze"]);
+ }
+ var isProxy = %IsJSProxy(obj);
+ if (isProxy || %HasNonStrictArgumentsElements(obj)) {
+ if (isProxy) {
+ ProxyFix(obj);
+ }
+ var names = ObjectGetOwnPropertyNames(obj);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ var desc = GetOwnProperty(obj, name);
+ if (desc.isWritable() || desc.isConfigurable()) {
+ if (IsDataDescriptor(desc)) desc.setWritable(false);
+ desc.setConfigurable(false);
+ DefineOwnProperty(obj, name, desc, true);
+ }
+ }
+ %PreventExtensions(obj);
+ } else {
+ // TODO(adamk): Is it worth going to this fast path if the
+ // object's properties are already in dictionary mode?
+ %ObjectFreeze(obj);
+ }
+ return obj;
+}
+
+
+// ES5 section 15.2.3.10
+function ObjectPreventExtension(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.preventExtension"]);
+ }
+ if (%IsJSProxy(obj)) {
+ ProxyFix(obj);
+ }
+ %PreventExtensions(obj);
+ return obj;
+}
+
+
+// ES5 section 15.2.3.11
+function ObjectIsSealed(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.isSealed"]);
+ }
+ if (%IsJSProxy(obj)) {
+ return false;
+ }
+ if (%IsExtensible(obj)) {
+ return false;
+ }
+ var names = ObjectGetOwnPropertyNames(obj);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ var desc = GetOwnProperty(obj, name);
+ if (desc.isConfigurable()) return false;
+ }
+ return true;
+}
+
+
+// ES5 section 15.2.3.12
+function ObjectIsFrozen(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.isFrozen"]);
+ }
+ if (%IsJSProxy(obj)) {
+ return false;
+ }
+ if (%IsExtensible(obj)) {
+ return false;
+ }
+ var names = ObjectGetOwnPropertyNames(obj);
+ for (var i = 0; i < names.length; i++) {
+ var name = names[i];
+ var desc = GetOwnProperty(obj, name);
+ if (IsDataDescriptor(desc) && desc.isWritable()) return false;
+ if (desc.isConfigurable()) return false;
+ }
+ return true;
+}
+
+
+// ES5 section 15.2.3.13
+function ObjectIsExtensible(obj) {
+ if (!IS_SPEC_OBJECT(obj)) {
+ throw MakeTypeError("called_on_non_object", ["Object.isExtensible"]);
+ }
+ if (%IsJSProxy(obj)) {
+ return true;
+ }
+ return %IsExtensible(obj);
+}
+
+
+// Harmony egal.
+function ObjectIs(obj1, obj2) {
+ if (obj1 === obj2) {
+ return (obj1 !== 0) || (1 / obj1 === 1 / obj2);
+ } else {
+ return (obj1 !== obj1) && (obj2 !== obj2);
+ }
+}
+
+
+// Harmony __proto__ getter.
+function ObjectGetProto() {
+ return %GetPrototype(this);
+}
+
+
+// Harmony __proto__ setter.
+function ObjectSetProto(obj) {
+ return %SetPrototype(this, obj);
+}
+
+
+// Harmony __proto__ poison pill.
+function ObjectPoisonProto(obj) {
+ throw MakeTypeError("proto_poison_pill", []);
+}
+
+
+function ObjectConstructor(x) {
+ if (%_IsConstructCall()) {
+ if (x == null) return this;
+ return ToObject(x);
+ } else {
+ if (x == null) return { };
+ return ToObject(x);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// Object
+
+function SetUpObject() {
+ %CheckIsBootstrapping();
+
+ %SetNativeFlag($Object);
+ %SetCode($Object, ObjectConstructor);
+ %FunctionSetName(ObjectPoisonProto, "__proto__");
+ %FunctionRemovePrototype(ObjectPoisonProto);
+ %SetExpectedNumberOfProperties($Object, 4);
+
+ %SetProperty($Object.prototype, "constructor", $Object, DONT_ENUM);
+
+ // Set up non-enumerable functions on the Object.prototype object.
+ InstallFunctions($Object.prototype, DONT_ENUM, $Array(
+ "toString", ObjectToString,
+ "toLocaleString", ObjectToLocaleString,
+ "valueOf", ObjectValueOf,
+ "hasOwnProperty", ObjectHasOwnProperty,
+ "isPrototypeOf", ObjectIsPrototypeOf,
+ "propertyIsEnumerable", ObjectPropertyIsEnumerable,
+ "__defineGetter__", ObjectDefineGetter,
+ "__lookupGetter__", ObjectLookupGetter,
+ "__defineSetter__", ObjectDefineSetter,
+ "__lookupSetter__", ObjectLookupSetter
+ ));
+ InstallGetterSetter($Object.prototype, "__proto__",
+ ObjectGetProto, ObjectSetProto);
+
+ // Set up non-enumerable functions in the Object object.
+ InstallFunctions($Object, DONT_ENUM, $Array(
+ "keys", ObjectKeys,
+ "create", ObjectCreate,
+ "defineProperty", ObjectDefineProperty,
+ "defineProperties", ObjectDefineProperties,
+ "freeze", ObjectFreeze,
+ "getPrototypeOf", ObjectGetPrototypeOf,
+ "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
+ "getOwnPropertyNames", ObjectGetOwnPropertyNames,
+ "is", ObjectIs,
+ "isExtensible", ObjectIsExtensible,
+ "isFrozen", ObjectIsFrozen,
+ "isSealed", ObjectIsSealed,
+ "preventExtensions", ObjectPreventExtension,
+ "seal", ObjectSeal
+ ));
+}
+
+SetUpObject();
+
+
+// ----------------------------------------------------------------------------
+// Boolean
+
+function BooleanConstructor(x) {
+ if (%_IsConstructCall()) {
+ %_SetValueOf(this, ToBoolean(x));
+ } else {
+ return ToBoolean(x);
+ }
+}
+
+
+function BooleanToString() {
+ // NOTE: Both Boolean objects and values can enter here as
+ // 'this'. This is not as dictated by ECMA-262.
+ var b = this;
+ if (!IS_BOOLEAN(b)) {
+ if (!IS_BOOLEAN_WRAPPER(b)) {
+ throw new $TypeError('Boolean.prototype.toString is not generic');
+ }
+ b = %_ValueOf(b);
+ }
+ return b ? 'true' : 'false';
+}
+
+
+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)) {
+ throw new $TypeError('Boolean.prototype.valueOf is not generic');
+ }
+ return %_ValueOf(this);
+}
+
+
+// ----------------------------------------------------------------------------
+
+function SetUpBoolean () {
+ %CheckIsBootstrapping();
+
+ %SetCode($Boolean, BooleanConstructor);
+ %FunctionSetPrototype($Boolean, new $Boolean(false));
+ %SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM);
+
+ InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
+ "toString", BooleanToString,
+ "valueOf", BooleanValueOf
+ ));
+}
+
+SetUpBoolean();
+
+
+// ----------------------------------------------------------------------------
+// Number
+
+function NumberConstructor(x) {
+ var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
+ if (%_IsConstructCall()) {
+ %_SetValueOf(this, value);
+ } else {
+ return value;
+ }
+}
+
+
+// ECMA-262 section 15.7.4.2.
+function NumberToString(radix) {
+ // NOTE: Both Number objects and values can enter here as
+ // 'this'. This is not as dictated by ECMA-262.
+ var number = this;
+ if (!IS_NUMBER(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);
+ }
+ // Fast case: Convert number in radix 10.
+ if (IS_UNDEFINED(radix) || radix === 10) {
+ return %_NumberToString(number);
+ }
+
+ // Convert the radix to an integer and check the range.
+ radix = TO_INTEGER(radix);
+ if (radix < 2 || radix > 36) {
+ throw new $RangeError('toString() radix argument must be between 2 and 36');
+ }
+ // Convert the number to a string in the given radix.
+ return %NumberToRadixString(number, radix);
+}
+
+
+// ECMA-262 section 15.7.4.3
+function NumberToLocaleString() {
+ return %_CallFunction(this, NumberToString);
+}
+
+
+// ECMA-262 section 15.7.4.4
+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)) {
+ throw new $TypeError('Number.prototype.valueOf is not generic');
+ }
+ return %_ValueOf(this);
+}
+
+
+// ECMA-262 section 15.7.4.5
+function NumberToFixed(fractionDigits) {
+ var x = this;
+ if (!IS_NUMBER(this)) {
+ if (!IS_NUMBER_WRAPPER(this)) {
+ throw MakeTypeError("incompatible_method_receiver",
+ ["Number.prototype.toFixed", this]);
+ }
+ // Get the value of this number in case it's an object.
+ x = %_ValueOf(this);
+ }
+ var f = TO_INTEGER(fractionDigits);
+
+ if (f < 0 || f > 20) {
+ throw new $RangeError("toFixed() digits argument must be between 0 and 20");
+ }
+
+ if (NUMBER_IS_NAN(x)) return "NaN";
+ if (x == 1/0) return "Infinity";
+ if (x == -1/0) return "-Infinity";
+
+ return %NumberToFixed(x, f);
+}
+
+
+// ECMA-262 section 15.7.4.6
+function NumberToExponential(fractionDigits) {
+ var x = this;
+ if (!IS_NUMBER(this)) {
+ if (!IS_NUMBER_WRAPPER(this)) {
+ throw MakeTypeError("incompatible_method_receiver",
+ ["Number.prototype.toExponential", this]);
+ }
+ // Get the value of this number in case it's an object.
+ x = %_ValueOf(this);
+ }
+ var f = IS_UNDEFINED(fractionDigits) ? void 0 : TO_INTEGER(fractionDigits);
+
+ if (NUMBER_IS_NAN(x)) return "NaN";
+ if (x == 1/0) return "Infinity";
+ if (x == -1/0) return "-Infinity";
+
+ if (IS_UNDEFINED(f)) {
+ f = -1; // Signal for runtime function that f is not defined.
+ } else if (f < 0 || f > 20) {
+ throw new $RangeError("toExponential() argument must be between 0 and 20");
+ }
+ return %NumberToExponential(x, f);
+}
+
+
+// ECMA-262 section 15.7.4.7
+function NumberToPrecision(precision) {
+ var x = this;
+ if (!IS_NUMBER(this)) {
+ if (!IS_NUMBER_WRAPPER(this)) {
+ throw MakeTypeError("incompatible_method_receiver",
+ ["Number.prototype.toPrecision", this]);
+ }
+ // Get the value of this number in case it's an object.
+ x = %_ValueOf(this);
+ }
+ if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
+ var p = TO_INTEGER(precision);
+
+ if (NUMBER_IS_NAN(x)) return "NaN";
+ if (x == 1/0) return "Infinity";
+ if (x == -1/0) return "-Infinity";
+
+ if (p < 1 || p > 21) {
+ throw new $RangeError("toPrecision() argument must be between 1 and 21");
+ }
+ return %NumberToPrecision(x, p);
+}
+
+
+// Harmony isFinite.
+function NumberIsFinite(number) {
+ return IS_NUMBER(number) && NUMBER_IS_FINITE(number);
+}
+
+
+// Harmony isNaN.
+function NumberIsNaN(number) {
+ return IS_NUMBER(number) && NUMBER_IS_NAN(number);
+}
+
+
+// ----------------------------------------------------------------------------
+
+function SetUpNumber() {
+ %CheckIsBootstrapping();
+
+ %SetCode($Number, NumberConstructor);
+ %FunctionSetPrototype($Number, new $Number(0));
+
+ %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
+ // Set up the constructor property on the Number prototype object.
+ %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);
+
+ %OptimizeObjectForAddingMultipleProperties($Number, 5);
+ // ECMA-262 section 15.7.3.1.
+ %SetProperty($Number,
+ "MAX_VALUE",
+ 1.7976931348623157e+308,
+ 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);
+
+ // ECMA-262 section 15.7.3.3.
+ %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
+
+ // ECMA-262 section 15.7.3.4.
+ %SetProperty($Number,
+ "NEGATIVE_INFINITY",
+ -1/0,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+
+ // ECMA-262 section 15.7.3.5.
+ %SetProperty($Number,
+ "POSITIVE_INFINITY",
+ 1/0,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %ToFastProperties($Number);
+
+ // Set up non-enumerable functions on the Number prototype object.
+ InstallFunctions($Number.prototype, DONT_ENUM, $Array(
+ "toString", NumberToString,
+ "toLocaleString", NumberToLocaleString,
+ "valueOf", NumberValueOf,
+ "toFixed", NumberToFixed,
+ "toExponential", NumberToExponential,
+ "toPrecision", NumberToPrecision
+ ));
+ InstallFunctions($Number, DONT_ENUM, $Array(
+ "isFinite", NumberIsFinite,
+ "isNaN", NumberIsNaN
+ ));
+}
+
+SetUpNumber();
+
+
+// ----------------------------------------------------------------------------
+// Function
+
+function FunctionSourceString(func) {
+ while (%IsJSFunctionProxy(func)) {
+ func = %GetCallTrap(func);
+ }
+
+ if (!IS_FUNCTION(func)) {
+ throw new $TypeError('Function.prototype.toString is not generic');
+ }
+
+ var source = %FunctionGetSourceCode(func);
+ if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
+ var name = %FunctionGetName(func);
+ if (name) {
+ // Mimic what KJS does.
+ return 'function ' + name + '() { [native code] }';
+ } else {
+ return 'function () { [native code] }';
+ }
+ }
+
+ var name = %FunctionNameShouldPrintAsAnonymous(func)
+ ? 'anonymous'
+ : %FunctionGetName(func);
+ var head = %FunctionIsGenerator(func) ? 'function* ' : 'function ';
+ return head + name + source;
+}
+
+
+function FunctionToString() {
+ return FunctionSourceString(this);
+}
+
+
+// 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');
+ }
+ 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 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();
+ 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
+ // try to redefine these as defined by the spec. The spec says
+ // 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.
+ // TODO(lrn): Do set these to be thrower.
+ return result;
+}
+
+
+function NewFunctionString(arguments, function_token) {
+ var n = arguments.length;
+ var p = '';
+ if (n > 1) {
+ p = ToString(arguments[0]);
+ for (var i = 1; i < n - 1; i++) {
+ p += ',' + ToString(arguments[i]);
+ }
+ // If the formal parameters string include ) - an illegal
+ // character - it may make the combined function expression
+ // compile. We avoid this problem by checking for this early on.
+ if (%_CallFunction(p, ')', StringIndexOf) != -1) {
+ throw MakeSyntaxError('paren_in_arg_string', []);
+ }
+ // If the formal parameters include an unbalanced block comment, the
+ // function must be rejected. Since JavaScript does not allow nested
+ // comments we can include a trailing block comment to catch this.
+ p += '\n/' + '**/';
+ }
+ var body = (n > 0) ? ToString(arguments[n - 1]) : '';
+ return '(' + function_token + '(' + p + ') {\n' + body + '\n})';
+}
+
+
+function FunctionConstructor(arg1) { // length == 1
+ var source = NewFunctionString(arguments, 'function');
+ var global_receiver = %GlobalReceiver(global);
+ // Compile the string in the constructor and not a helper so that errors
+ // appear to come from here.
+ var f = %_CallFunction(global_receiver, %CompileString(source, true));
+ %FunctionMarkNameShouldPrintAsAnonymous(f);
+ return f;
+}
+
+
+// ----------------------------------------------------------------------------
+
+function SetUpFunction() {
+ %CheckIsBootstrapping();
+
+ %SetCode($Function, FunctionConstructor);
+ %SetProperty($Function.prototype, "constructor", $Function, DONT_ENUM);
+
+ InstallFunctions($Function.prototype, DONT_ENUM, $Array(
+ "bind", FunctionBind,
+ "toString", FunctionToString
+ ));
+}
+
+SetUpFunction();
diff --git a/chromium/v8/src/v8preparserdll-main.cc b/chromium/v8/src/v8preparserdll-main.cc
new file mode 100644
index 00000000000..c0344d344ab
--- /dev/null
+++ b/chromium/v8/src/v8preparserdll-main.cc
@@ -0,0 +1,39 @@
+// 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.
+
+#include <windows.h>
+
+#include "../include/v8-preparser.h"
+
+extern "C" {
+BOOL WINAPI DllMain(HANDLE hinstDLL,
+ DWORD dwReason,
+ LPVOID lpvReserved) {
+ // Do nothing.
+ return TRUE;
+}
+}
diff --git a/chromium/v8/src/v8threads.cc b/chromium/v8/src/v8threads.cc
new file mode 100644
index 00000000000..2df187a572c
--- /dev/null
+++ b/chromium/v8/src/v8threads.cc
@@ -0,0 +1,494 @@
+// Copyright 2012 the V8 project authors. 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 "api.h"
+#include "bootstrapper.h"
+#include "debug.h"
+#include "execution.h"
+#include "v8threads.h"
+#include "regexp-stack.h"
+
+namespace v8 {
+
+
+// Track whether this V8 instance has ever called v8::Locker. This allows the
+// API code to verify that the lock is always held when V8 is being entered.
+bool Locker::active_ = false;
+
+
+Locker::Locker() {
+ Initialize(i::Isolate::GetDefaultIsolateForLocking());
+}
+
+
+// Once the Locker is initialized, the current thread will be guaranteed to have
+// the lock for a given isolate.
+void Locker::Initialize(v8::Isolate* isolate) {
+ ASSERT(isolate != NULL);
+ has_lock_= false;
+ top_level_ = true;
+ isolate_ = reinterpret_cast<i::Isolate*>(isolate);
+ // Record that the Locker has been used at least once.
+ active_ = true;
+ // Get the big lock if necessary.
+ if (!isolate_->thread_manager()->IsLockedByCurrentThread()) {
+ isolate_->thread_manager()->Lock();
+ has_lock_ = true;
+
+ // Make sure that V8 is initialized. Archiving of threads interferes
+ // with deserialization by adding additional root pointers, so we must
+ // initialize here, before anyone can call ~Locker() or Unlocker().
+ if (!isolate_->IsInitialized()) {
+ isolate_->Enter();
+ V8::Initialize();
+ isolate_->Exit();
+ }
+
+ // This may be a locker within an unlocker in which case we have to
+ // get the saved state for this thread and restore it.
+ if (isolate_->thread_manager()->RestoreThread()) {
+ top_level_ = false;
+ } else {
+ internal::ExecutionAccess access(isolate_);
+ isolate_->stack_guard()->ClearThread(access);
+ isolate_->stack_guard()->InitThread(access);
+ }
+ if (isolate_->IsDefaultIsolate()) {
+ // This only enters if not yet entered.
+ internal::Isolate::EnterDefaultIsolate();
+ }
+ }
+ ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
+}
+
+
+bool Locker::IsLocked(v8::Isolate* isolate) {
+ ASSERT(isolate != NULL);
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ return internal_isolate->thread_manager()->IsLockedByCurrentThread();
+}
+
+
+bool Locker::IsActive() {
+ return active_;
+}
+
+
+Locker::~Locker() {
+ ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
+ if (has_lock_) {
+ if (isolate_->IsDefaultIsolate()) {
+ isolate_->Exit();
+ }
+ if (top_level_) {
+ isolate_->thread_manager()->FreeThreadResources();
+ } else {
+ isolate_->thread_manager()->ArchiveThread();
+ }
+ isolate_->thread_manager()->Unlock();
+ }
+}
+
+
+Unlocker::Unlocker() {
+ Initialize(i::Isolate::GetDefaultIsolateForLocking());
+}
+
+
+void Unlocker::Initialize(v8::Isolate* isolate) {
+ ASSERT(isolate != NULL);
+ isolate_ = reinterpret_cast<i::Isolate*>(isolate);
+ ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
+ if (isolate_->IsDefaultIsolate()) {
+ isolate_->Exit();
+ }
+ isolate_->thread_manager()->ArchiveThread();
+ isolate_->thread_manager()->Unlock();
+}
+
+
+Unlocker::~Unlocker() {
+ ASSERT(!isolate_->thread_manager()->IsLockedByCurrentThread());
+ isolate_->thread_manager()->Lock();
+ isolate_->thread_manager()->RestoreThread();
+ if (isolate_->IsDefaultIsolate()) {
+ isolate_->Enter();
+ }
+}
+
+
+void Locker::StartPreemption(int every_n_ms) {
+ v8::internal::ContextSwitcher::StartPreemption(every_n_ms);
+}
+
+
+void Locker::StopPreemption() {
+ v8::internal::ContextSwitcher::StopPreemption();
+}
+
+
+namespace internal {
+
+
+bool ThreadManager::RestoreThread() {
+ ASSERT(IsLockedByCurrentThread());
+ // 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())) {
+ lazily_archived_thread_ = ThreadId::Invalid();
+ Isolate::PerIsolateThreadData* per_thread =
+ isolate_->FindPerThreadDataForThisThread();
+ ASSERT(per_thread != NULL);
+ ASSERT(per_thread->thread_state() == lazily_archived_thread_state_);
+ lazily_archived_thread_state_->set_id(ThreadId::Invalid());
+ lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST);
+ lazily_archived_thread_state_ = NULL;
+ per_thread->set_thread_state(NULL);
+ return true;
+ }
+
+ // Make sure that the preemption thread cannot modify the thread state while
+ // it is being archived or restored.
+ ExecutionAccess access(isolate_);
+
+ // If there is another thread that was lazily archived then we have to really
+ // archive it now.
+ if (lazily_archived_thread_.IsValid()) {
+ EagerlyArchiveThread();
+ }
+ Isolate::PerIsolateThreadData* per_thread =
+ isolate_->FindPerThreadDataForThisThread();
+ if (per_thread == NULL || per_thread->thread_state() == NULL) {
+ // This is a new thread.
+ isolate_->stack_guard()->InitThread(access);
+ return false;
+ }
+ ThreadState* state = per_thread->thread_state();
+ char* from = state->data();
+ from = isolate_->handle_scope_implementer()->RestoreThread(from);
+ from = isolate_->RestoreThread(from);
+ from = Relocatable::RestoreState(isolate_, from);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ from = isolate_->debug()->RestoreDebug(from);
+#endif
+ from = isolate_->stack_guard()->RestoreStackGuard(from);
+ from = isolate_->regexp_stack()->RestoreStack(from);
+ from = isolate_->bootstrapper()->RestoreState(from);
+ per_thread->set_thread_state(NULL);
+ if (state->terminate_on_restore()) {
+ isolate_->stack_guard()->TerminateExecution();
+ state->set_terminate_on_restore(false);
+ }
+ state->set_id(ThreadId::Invalid());
+ state->Unlink();
+ state->LinkInto(ThreadState::FREE_LIST);
+ return true;
+}
+
+
+void ThreadManager::Lock() {
+ mutex_->Lock();
+ mutex_owner_ = ThreadId::Current();
+ ASSERT(IsLockedByCurrentThread());
+}
+
+
+void ThreadManager::Unlock() {
+ mutex_owner_ = ThreadId::Invalid();
+ mutex_->Unlock();
+}
+
+
+static int ArchiveSpacePerThread() {
+ return HandleScopeImplementer::ArchiveSpacePerThread() +
+ Isolate::ArchiveSpacePerThread() +
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Debug::ArchiveSpacePerThread() +
+#endif
+ StackGuard::ArchiveSpacePerThread() +
+ RegExpStack::ArchiveSpacePerThread() +
+ Bootstrapper::ArchiveSpacePerThread() +
+ Relocatable::ArchiveSpacePerThread();
+}
+
+
+ThreadState::ThreadState(ThreadManager* thread_manager)
+ : id_(ThreadId::Invalid()),
+ terminate_on_restore_(false),
+ data_(NULL),
+ next_(this),
+ previous_(this),
+ thread_manager_(thread_manager) {
+}
+
+
+ThreadState::~ThreadState() {
+ DeleteArray<char>(data_);
+}
+
+
+void ThreadState::AllocateSpace() {
+ data_ = NewArray<char>(ArchiveSpacePerThread());
+}
+
+
+void ThreadState::Unlink() {
+ next_->previous_ = previous_;
+ previous_->next_ = next_;
+}
+
+
+void ThreadState::LinkInto(List list) {
+ ThreadState* flying_anchor =
+ list == FREE_LIST ? thread_manager_->free_anchor_
+ : thread_manager_->in_use_anchor_;
+ next_ = flying_anchor->next_;
+ previous_ = flying_anchor;
+ flying_anchor->next_ = this;
+ next_->previous_ = this;
+}
+
+
+ThreadState* ThreadManager::GetFreeThreadState() {
+ ThreadState* gotten = free_anchor_->next_;
+ if (gotten == free_anchor_) {
+ ThreadState* new_thread_state = new ThreadState(this);
+ new_thread_state->AllocateSpace();
+ return new_thread_state;
+ }
+ return gotten;
+}
+
+
+// Gets the first in the list of archived threads.
+ThreadState* ThreadManager::FirstThreadStateInUse() {
+ return in_use_anchor_->Next();
+}
+
+
+ThreadState* ThreadState::Next() {
+ if (next_ == thread_manager_->in_use_anchor_) return NULL;
+ return next_;
+}
+
+
+// Thread ids must start with 1, because in TLS having thread id 0 can't
+// be distinguished from not having a thread id at all (since NULL is
+// defined as 0.)
+ThreadManager::ThreadManager()
+ : mutex_(OS::CreateMutex()),
+ mutex_owner_(ThreadId::Invalid()),
+ lazily_archived_thread_(ThreadId::Invalid()),
+ lazily_archived_thread_state_(NULL),
+ free_anchor_(NULL),
+ in_use_anchor_(NULL) {
+ free_anchor_ = new ThreadState(this);
+ in_use_anchor_ = new ThreadState(this);
+}
+
+
+ThreadManager::~ThreadManager() {
+ delete mutex_;
+ DeleteThreadStateList(free_anchor_);
+ DeleteThreadStateList(in_use_anchor_);
+}
+
+
+void ThreadManager::DeleteThreadStateList(ThreadState* anchor) {
+ // The list starts and ends with the anchor.
+ for (ThreadState* current = anchor->next_; current != anchor;) {
+ ThreadState* next = current->next_;
+ delete current;
+ current = next;
+ }
+ delete anchor;
+}
+
+
+void ThreadManager::ArchiveThread() {
+ ASSERT(lazily_archived_thread_.Equals(ThreadId::Invalid()));
+ ASSERT(!IsArchived());
+ ASSERT(IsLockedByCurrentThread());
+ ThreadState* state = GetFreeThreadState();
+ state->Unlink();
+ Isolate::PerIsolateThreadData* per_thread =
+ isolate_->FindOrAllocatePerThreadDataForThisThread();
+ per_thread->set_thread_state(state);
+ lazily_archived_thread_ = ThreadId::Current();
+ lazily_archived_thread_state_ = state;
+ ASSERT(state->id().Equals(ThreadId::Invalid()));
+ state->set_id(CurrentId());
+ ASSERT(!state->id().Equals(ThreadId::Invalid()));
+}
+
+
+void ThreadManager::EagerlyArchiveThread() {
+ ASSERT(IsLockedByCurrentThread());
+ ThreadState* state = lazily_archived_thread_state_;
+ state->LinkInto(ThreadState::IN_USE_LIST);
+ char* to = state->data();
+ // Ensure that data containing GC roots are archived first, and handle them
+ // in ThreadManager::Iterate(ObjectVisitor*).
+ to = isolate_->handle_scope_implementer()->ArchiveThread(to);
+ to = isolate_->ArchiveThread(to);
+ to = Relocatable::ArchiveState(isolate_, to);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ to = isolate_->debug()->ArchiveDebug(to);
+#endif
+ to = isolate_->stack_guard()->ArchiveStackGuard(to);
+ to = isolate_->regexp_stack()->ArchiveStack(to);
+ to = isolate_->bootstrapper()->ArchiveState(to);
+ lazily_archived_thread_ = ThreadId::Invalid();
+ lazily_archived_thread_state_ = NULL;
+}
+
+
+void ThreadManager::FreeThreadResources() {
+ isolate_->handle_scope_implementer()->FreeThreadResources();
+ isolate_->FreeThreadResources();
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ isolate_->debug()->FreeThreadResources();
+#endif
+ isolate_->stack_guard()->FreeThreadResources();
+ isolate_->regexp_stack()->FreeThreadResources();
+ isolate_->bootstrapper()->FreeThreadResources();
+}
+
+
+bool ThreadManager::IsArchived() {
+ Isolate::PerIsolateThreadData* data =
+ isolate_->FindPerThreadDataForThisThread();
+ return data != NULL && data->thread_state() != NULL;
+}
+
+
+void ThreadManager::Iterate(ObjectVisitor* v) {
+ // Expecting no threads during serialization/deserialization
+ for (ThreadState* state = FirstThreadStateInUse();
+ state != NULL;
+ state = state->Next()) {
+ char* data = state->data();
+ data = HandleScopeImplementer::Iterate(v, data);
+ data = isolate_->Iterate(v, data);
+ data = Relocatable::Iterate(v, data);
+ }
+}
+
+
+void ThreadManager::IterateArchivedThreads(ThreadVisitor* v) {
+ for (ThreadState* state = FirstThreadStateInUse();
+ state != NULL;
+ state = state->Next()) {
+ char* data = state->data();
+ data += HandleScopeImplementer::ArchiveSpacePerThread();
+ isolate_->IterateThread(v, data);
+ }
+}
+
+
+ThreadId ThreadManager::CurrentId() {
+ return ThreadId::Current();
+}
+
+
+void ThreadManager::TerminateExecution(ThreadId thread_id) {
+ for (ThreadState* state = FirstThreadStateInUse();
+ state != NULL;
+ state = state->Next()) {
+ if (thread_id.Equals(state->id())) {
+ state->set_terminate_on_restore(true);
+ }
+ }
+}
+
+
+ContextSwitcher::ContextSwitcher(Isolate* isolate, int every_n_ms)
+ : Thread("v8:CtxtSwitcher"),
+ keep_going_(true),
+ sleep_ms_(every_n_ms),
+ isolate_(isolate) {
+}
+
+
+// Set the scheduling interval of V8 threads. This function starts the
+// ContextSwitcher thread if needed.
+void ContextSwitcher::StartPreemption(int every_n_ms) {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(Locker::IsLocked(reinterpret_cast<v8::Isolate*>(isolate)));
+ if (isolate->context_switcher() == NULL) {
+ // If the ContextSwitcher thread is not running at the moment start it now.
+ isolate->set_context_switcher(new ContextSwitcher(isolate, every_n_ms));
+ isolate->context_switcher()->Start();
+ } else {
+ // ContextSwitcher thread is already running, so we just change the
+ // scheduling interval.
+ isolate->context_switcher()->sleep_ms_ = every_n_ms;
+ }
+}
+
+
+// Disable preemption of V8 threads. If multiple threads want to use V8 they
+// must cooperatively schedule amongst them from this point on.
+void ContextSwitcher::StopPreemption() {
+ Isolate* isolate = Isolate::Current();
+ ASSERT(Locker::IsLocked(reinterpret_cast<v8::Isolate*>(isolate)));
+ if (isolate->context_switcher() != NULL) {
+ // The ContextSwitcher thread is running. We need to stop it and release
+ // its resources.
+ isolate->context_switcher()->keep_going_ = false;
+ // Wait for the ContextSwitcher thread to exit.
+ isolate->context_switcher()->Join();
+ // Thread has exited, now we can delete it.
+ delete(isolate->context_switcher());
+ isolate->set_context_switcher(NULL);
+ }
+}
+
+
+// Main loop of the ContextSwitcher thread: Preempt the currently running V8
+// thread at regular intervals.
+void ContextSwitcher::Run() {
+ while (keep_going_) {
+ OS::Sleep(sleep_ms_);
+ isolate()->stack_guard()->Preempt();
+ }
+}
+
+
+// Acknowledge the preemption by the receiving thread.
+void ContextSwitcher::PreemptionReceived() {
+ ASSERT(Locker::IsLocked(i::Isolate::GetDefaultIsolateForLocking()));
+ // There is currently no accounting being done for this. But could be in the
+ // future, which is why we leave this in.
+}
+
+
+} // namespace internal
+} // namespace v8
diff --git a/chromium/v8/src/v8threads.h b/chromium/v8/src/v8threads.h
new file mode 100644
index 00000000000..8dce8602f66
--- /dev/null
+++ b/chromium/v8/src/v8threads.h
@@ -0,0 +1,172 @@
+// Copyright 2012 the V8 project authors. 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_V8THREADS_H_
+#define V8_V8THREADS_H_
+
+namespace v8 {
+namespace internal {
+
+
+class ThreadState {
+ public:
+ // Returns NULL after the last one.
+ ThreadState* Next();
+
+ enum List {FREE_LIST, IN_USE_LIST};
+
+ void LinkInto(List list);
+ void Unlink();
+
+ // Id of thread.
+ void set_id(ThreadId id) { id_ = id; }
+ ThreadId id() { return id_; }
+
+ // Should the thread be terminated when it is restored?
+ bool terminate_on_restore() { return terminate_on_restore_; }
+ void set_terminate_on_restore(bool terminate_on_restore) {
+ terminate_on_restore_ = terminate_on_restore;
+ }
+
+ // Get data area for archiving a thread.
+ char* data() { return data_; }
+
+ private:
+ explicit ThreadState(ThreadManager* thread_manager);
+ ~ThreadState();
+
+ void AllocateSpace();
+
+ ThreadId id_;
+ bool terminate_on_restore_;
+ char* data_;
+ ThreadState* next_;
+ ThreadState* previous_;
+
+ ThreadManager* thread_manager_;
+
+ friend class ThreadManager;
+};
+
+
+// Defined in isolate.h.
+class ThreadLocalTop;
+
+
+class ThreadVisitor {
+ public:
+ // ThreadLocalTop may be only available during this call.
+ virtual void VisitThread(Isolate* isolate, ThreadLocalTop* top) = 0;
+
+ protected:
+ virtual ~ThreadVisitor() {}
+};
+
+
+class ThreadManager {
+ public:
+ void Lock();
+ void Unlock();
+
+ void ArchiveThread();
+ bool RestoreThread();
+ void FreeThreadResources();
+ bool IsArchived();
+
+ void Iterate(ObjectVisitor* v);
+ void IterateArchivedThreads(ThreadVisitor* v);
+ bool IsLockedByCurrentThread() {
+ return mutex_owner_.Equals(ThreadId::Current());
+ }
+
+ ThreadId CurrentId();
+
+ void TerminateExecution(ThreadId thread_id);
+
+ // Iterate over in-use states.
+ ThreadState* FirstThreadStateInUse();
+ ThreadState* GetFreeThreadState();
+
+ private:
+ ThreadManager();
+ ~ThreadManager();
+
+ void DeleteThreadStateList(ThreadState* anchor);
+
+ void EagerlyArchiveThread();
+
+ Mutex* mutex_;
+ ThreadId mutex_owner_;
+ ThreadId lazily_archived_thread_;
+ ThreadState* lazily_archived_thread_state_;
+
+ // In the following two lists there is always at least one object on the list.
+ // The first object is a flying anchor that is only there to simplify linking
+ // and unlinking.
+ // Head of linked list of free states.
+ ThreadState* free_anchor_;
+ // Head of linked list of states in use.
+ ThreadState* in_use_anchor_;
+
+ Isolate* isolate_;
+
+ friend class Isolate;
+ friend class ThreadState;
+};
+
+
+// The ContextSwitcher thread is used to schedule regular preemptions to
+// multiple running V8 threads. Generally it is necessary to call
+// StartPreemption if there is more than one thread running. If not, a single
+// JavaScript can take full control of V8 and not allow other threads to run.
+class ContextSwitcher: public Thread {
+ public:
+ // Set the preemption interval for the ContextSwitcher thread.
+ static void StartPreemption(int every_n_ms);
+
+ // Stop sending preemption requests to threads.
+ static void StopPreemption();
+
+ // Preempted thread needs to call back to the ContextSwitcher to acknowledge
+ // the handling of a preemption request.
+ static void PreemptionReceived();
+
+ private:
+ ContextSwitcher(Isolate* isolate, int every_n_ms);
+
+ Isolate* isolate() const { return isolate_; }
+
+ void Run();
+
+ bool keep_going_;
+ int sleep_ms_;
+ Isolate* isolate_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_V8THREADS_H_
diff --git a/chromium/v8/src/v8utils.cc b/chromium/v8/src/v8utils.cc
new file mode 100644
index 00000000000..7390d854e63
--- /dev/null
+++ b/chromium/v8/src/v8utils.cc
@@ -0,0 +1,276 @@
+// 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 <stdarg.h>
+
+#include "v8.h"
+
+#include "platform.h"
+
+#include "sys/stat.h"
+
+namespace v8 {
+namespace internal {
+
+
+void PrintF(const char* format, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ OS::VPrint(format, arguments);
+ va_end(arguments);
+}
+
+
+void PrintF(FILE* out, const char* format, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ OS::VFPrint(out, format, arguments);
+ va_end(arguments);
+}
+
+
+void PrintPID(const char* format, ...) {
+ OS::Print("[%d] ", OS::GetCurrentProcessId());
+ va_list arguments;
+ va_start(arguments, format);
+ OS::VPrint(format, arguments);
+ va_end(arguments);
+}
+
+
+void Flush(FILE* out) {
+ fflush(out);
+}
+
+
+char* ReadLine(const char* prompt) {
+ char* result = NULL;
+ char line_buf[256];
+ int offset = 0;
+ bool keep_going = true;
+ fprintf(stdout, "%s", prompt);
+ fflush(stdout);
+ while (keep_going) {
+ if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) {
+ // fgets got an error. Just give up.
+ if (result != NULL) {
+ DeleteArray(result);
+ }
+ return NULL;
+ }
+ int len = StrLength(line_buf);
+ if (len > 1 &&
+ line_buf[len - 2] == '\\' &&
+ line_buf[len - 1] == '\n') {
+ // When we read a line that ends with a "\" we remove the escape and
+ // append the remainder.
+ line_buf[len - 2] = '\n';
+ line_buf[len - 1] = 0;
+ len -= 1;
+ } else if ((len > 0) && (line_buf[len - 1] == '\n')) {
+ // Since we read a new line we are done reading the line. This
+ // will exit the loop after copying this buffer into the result.
+ keep_going = false;
+ }
+ if (result == NULL) {
+ // Allocate the initial result and make room for the terminating '\0'
+ result = NewArray<char>(len + 1);
+ } else {
+ // Allocate a new result with enough room for the new addition.
+ int new_len = offset + len + 1;
+ char* new_result = NewArray<char>(new_len);
+ // Copy the existing input into the new array and set the new
+ // array as the result.
+ OS::MemCopy(new_result, result, offset * kCharSize);
+ DeleteArray(result);
+ result = new_result;
+ }
+ // Copy the newly read line into the result.
+ OS::MemCopy(result + offset, line_buf, len * kCharSize);
+ offset += len;
+ }
+ ASSERT(result != NULL);
+ result[offset] = '\0';
+ return result;
+}
+
+
+char* ReadCharsFromFile(FILE* file,
+ int* size,
+ int extra_space,
+ bool verbose,
+ const char* filename) {
+ if (file == NULL || fseek(file, 0, SEEK_END) != 0) {
+ if (verbose) {
+ OS::PrintError("Cannot read from file %s.\n", filename);
+ }
+ return NULL;
+ }
+
+ // Get the size of the file and rewind it.
+ *size = ftell(file);
+ rewind(file);
+
+ char* result = NewArray<char>(*size + extra_space);
+ for (int i = 0; i < *size && feof(file) == 0;) {
+ int read = static_cast<int>(fread(&result[i], 1, *size - i, file));
+ if (read != (*size - i) && ferror(file) != 0) {
+ fclose(file);
+ DeleteArray(result);
+ return NULL;
+ }
+ i += read;
+ }
+ return result;
+}
+
+
+char* ReadCharsFromFile(const char* filename,
+ int* size,
+ int extra_space,
+ bool verbose) {
+ FILE* file = OS::FOpen(filename, "rb");
+ char* result = ReadCharsFromFile(file, size, extra_space, verbose, filename);
+ if (file != NULL) fclose(file);
+ return result;
+}
+
+
+byte* ReadBytes(const char* filename, int* size, bool verbose) {
+ char* chars = ReadCharsFromFile(filename, size, 0, verbose);
+ return reinterpret_cast<byte*>(chars);
+}
+
+
+static Vector<const char> SetVectorContents(char* chars,
+ int size,
+ bool* exists) {
+ if (!chars) {
+ *exists = false;
+ return Vector<const char>::empty();
+ }
+ chars[size] = '\0';
+ *exists = true;
+ return Vector<const char>(chars, size);
+}
+
+
+Vector<const char> ReadFile(const char* filename,
+ bool* exists,
+ bool verbose) {
+ int size;
+ char* result = ReadCharsFromFile(filename, &size, 1, verbose);
+ return SetVectorContents(result, size, exists);
+}
+
+
+Vector<const char> ReadFile(FILE* file,
+ bool* exists,
+ bool verbose) {
+ int size;
+ char* result = ReadCharsFromFile(file, &size, 1, verbose, "");
+ return SetVectorContents(result, size, exists);
+}
+
+
+int WriteCharsToFile(const char* str, int size, FILE* f) {
+ int total = 0;
+ while (total < size) {
+ int write = static_cast<int>(fwrite(str, 1, size - total, f));
+ if (write == 0) {
+ return total;
+ }
+ total += write;
+ str += write;
+ }
+ return total;
+}
+
+
+int AppendChars(const char* filename,
+ const char* str,
+ int size,
+ bool verbose) {
+ FILE* f = OS::FOpen(filename, "ab");
+ if (f == NULL) {
+ if (verbose) {
+ OS::PrintError("Cannot open file %s for writing.\n", filename);
+ }
+ return 0;
+ }
+ int written = WriteCharsToFile(str, size, f);
+ fclose(f);
+ return written;
+}
+
+
+int WriteChars(const char* filename,
+ const char* str,
+ int size,
+ bool verbose) {
+ FILE* f = OS::FOpen(filename, "wb");
+ if (f == NULL) {
+ if (verbose) {
+ OS::PrintError("Cannot open file %s for writing.\n", filename);
+ }
+ return 0;
+ }
+ int written = WriteCharsToFile(str, size, f);
+ fclose(f);
+ return written;
+}
+
+
+int WriteBytes(const char* filename,
+ const byte* bytes,
+ int size,
+ bool verbose) {
+ const char* str = reinterpret_cast<const char*>(bytes);
+ return WriteChars(filename, str, size, verbose);
+}
+
+
+
+void StringBuilder::AddFormatted(const char* format, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ AddFormattedList(format, arguments);
+ va_end(arguments);
+}
+
+
+void StringBuilder::AddFormattedList(const char* format, va_list list) {
+ ASSERT(!is_finalized() && position_ <= buffer_.length());
+ int n = OS::VSNPrintF(buffer_ + position_, format, list);
+ if (n < 0 || n >= (buffer_.length() - position_)) {
+ position_ = buffer_.length();
+ } else {
+ position_ += n;
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/v8utils.h b/chromium/v8/src/v8utils.h
new file mode 100644
index 00000000000..fd3f4a50954
--- /dev/null
+++ b/chromium/v8/src/v8utils.h
@@ -0,0 +1,498 @@
+// Copyright 2012 the V8 project authors. 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_V8UTILS_H_
+#define V8_V8UTILS_H_
+
+#include "utils.h"
+#include "platform.h" // For va_list on Solaris.
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// I/O support.
+
+#if __GNUC__ >= 4
+// On gcc we can ask the compiler to check the types of %d-style format
+// specifiers and their associated arguments. TODO(erikcorry) fix this
+// so it works on MacOSX.
+#if defined(__MACH__) && defined(__APPLE__)
+#define PRINTF_CHECKING
+#define FPRINTF_CHECKING
+#else // MacOsX.
+#define PRINTF_CHECKING __attribute__ ((format (printf, 1, 2)))
+#define FPRINTF_CHECKING __attribute__ ((format (printf, 2, 3)))
+#endif
+#else
+#define PRINTF_CHECKING
+#define FPRINTF_CHECKING
+#endif
+
+// Our version of printf().
+void PRINTF_CHECKING PrintF(const char* format, ...);
+void FPRINTF_CHECKING PrintF(FILE* out, const char* format, ...);
+
+// Prepends the current process ID to the output.
+void PRINTF_CHECKING PrintPID(const char* format, ...);
+
+// Our version of fflush.
+void Flush(FILE* out);
+
+inline void Flush() {
+ Flush(stdout);
+}
+
+
+// Read a line of characters after printing the prompt to stdout. The resulting
+// char* needs to be disposed off with DeleteArray by the caller.
+char* ReadLine(const char* prompt);
+
+
+// Read and return the raw bytes in a file. the size of the buffer is returned
+// in size.
+// The returned buffer must be freed by the caller.
+byte* ReadBytes(const char* filename, int* size, bool verbose = true);
+
+
+// Append size chars from str to the file given by filename.
+// The file is overwritten. Returns the number of chars written.
+int AppendChars(const char* filename,
+ const char* str,
+ int size,
+ bool verbose = true);
+
+
+// Write size chars from str to the file given by filename.
+// The file is overwritten. Returns the number of chars written.
+int WriteChars(const char* filename,
+ const char* str,
+ int size,
+ bool verbose = true);
+
+
+// Write size bytes to the file given by filename.
+// The file is overwritten. Returns the number of bytes written.
+int WriteBytes(const char* filename,
+ const byte* bytes,
+ int size,
+ bool verbose = true);
+
+
+// Write the C code
+// const char* <varname> = "<str>";
+// const int <varname>_len = <len>;
+// to the file given by filename. Only the first len chars are written.
+int WriteAsCFile(const char* filename, const char* varname,
+ const char* str, int size, bool verbose = true);
+
+
+// ----------------------------------------------------------------------------
+// Data structures
+
+template <typename T>
+inline Vector< Handle<Object> > HandleVector(v8::internal::Handle<T>* elms,
+ int length) {
+ return Vector< Handle<Object> >(
+ reinterpret_cast<v8::internal::Handle<Object>*>(elms), length);
+}
+
+
+// ----------------------------------------------------------------------------
+// Memory
+
+// Copies words from |src| to |dst|. The data spans must not overlap.
+template <typename T>
+inline void CopyWords(T* dst, const T* src, size_t num_words) {
+ STATIC_ASSERT(sizeof(T) == kPointerSize);
+ ASSERT(Min(dst, const_cast<T*>(src)) + num_words <=
+ Max(dst, const_cast<T*>(src)));
+ ASSERT(num_words > 0);
+
+ // Use block copying OS::MemCopy if the segment we're copying is
+ // enough to justify the extra call/setup overhead.
+ static const size_t kBlockCopyLimit = 16;
+
+ if (num_words < kBlockCopyLimit) {
+ do {
+ num_words--;
+ *dst++ = *src++;
+ } while (num_words > 0);
+ } else {
+ OS::MemCopy(dst, src, num_words * kPointerSize);
+ }
+}
+
+
+// Copies words from |src| to |dst|. No restrictions.
+template <typename T>
+inline void MoveWords(T* dst, const T* src, size_t num_words) {
+ STATIC_ASSERT(sizeof(T) == kPointerSize);
+ ASSERT(num_words > 0);
+
+ // Use block copying OS::MemCopy if the segment we're copying is
+ // enough to justify the extra call/setup overhead.
+ static const size_t kBlockCopyLimit = 16;
+
+ if (num_words < kBlockCopyLimit &&
+ ((dst < src) || (dst >= (src + num_words * kPointerSize)))) {
+ T* end = dst + num_words;
+ do {
+ num_words--;
+ *dst++ = *src++;
+ } while (num_words > 0);
+ } else {
+ OS::MemMove(dst, src, num_words * kPointerSize);
+ }
+}
+
+
+// Copies data from |src| to |dst|. The data spans must not overlap.
+template <typename T>
+inline void CopyBytes(T* dst, const T* src, size_t num_bytes) {
+ STATIC_ASSERT(sizeof(T) == 1);
+ ASSERT(Min(dst, const_cast<T*>(src)) + num_bytes <=
+ Max(dst, const_cast<T*>(src)));
+ if (num_bytes == 0) return;
+
+ // Use block copying OS::MemCopy if the segment we're copying is
+ // enough to justify the extra call/setup overhead.
+ static const int kBlockCopyLimit = OS::kMinComplexMemCopy;
+
+ if (num_bytes < static_cast<size_t>(kBlockCopyLimit)) {
+ do {
+ num_bytes--;
+ *dst++ = *src++;
+ } while (num_bytes > 0);
+ } else {
+ OS::MemCopy(dst, src, num_bytes);
+ }
+}
+
+
+// Copies data from |src| to |dst|. No restrictions.
+template <typename T>
+inline void MoveBytes(T* dst, const T* src, size_t num_bytes) {
+ STATIC_ASSERT(sizeof(T) == 1);
+ switch (num_bytes) {
+ case 0: return;
+ case 1:
+ *dst = *src;
+ return;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ case 2:
+ *reinterpret_cast<uint16_t*>(dst) = *reinterpret_cast<const uint16_t*>(src);
+ return;
+ case 3: {
+ uint16_t part1 = *reinterpret_cast<const uint16_t*>(src);
+ byte part2 = *(src + 2);
+ *reinterpret_cast<uint16_t*>(dst) = part1;
+ *(dst + 2) = part2;
+ return;
+ }
+ case 4:
+ *reinterpret_cast<uint32_t*>(dst) = *reinterpret_cast<const uint32_t*>(src);
+ return;
+ case 5:
+ case 6:
+ case 7:
+ case 8: {
+ uint32_t part1 = *reinterpret_cast<const uint32_t*>(src);
+ uint32_t part2 = *reinterpret_cast<const uint32_t*>(src + num_bytes - 4);
+ *reinterpret_cast<uint32_t*>(dst) = part1;
+ *reinterpret_cast<uint32_t*>(dst + num_bytes - 4) = part2;
+ return;
+ }
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16: {
+ double part1 = *reinterpret_cast<const double*>(src);
+ double part2 = *reinterpret_cast<const double*>(src + num_bytes - 8);
+ *reinterpret_cast<double*>(dst) = part1;
+ *reinterpret_cast<double*>(dst + num_bytes - 8) = part2;
+ return;
+ }
+#endif
+ default:
+ OS::MemMove(dst, src, num_bytes);
+ return;
+ }
+}
+
+
+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 V8_HOST_ARCH_IA32
+#define STOS "stosl"
+#elif V8_HOST_ARCH_X64
+#define STOS "stosq"
+#endif
+#if defined(__native_client__)
+ // This STOS sequence does not validate for x86_64 Native Client.
+ // Here we #undef STOS to force use of the slower C version.
+ // TODO(bradchen): Profile V8 and implement a faster REP STOS
+ // here if the profile indicates it matters.
+#undef STOS
+#endif
+
+#if defined(__GNUC__) && defined(STOS)
+ asm volatile(
+ "cld;"
+ "rep ; " STOS
+ : "+&c" (counter), "+&D" (dest)
+ : "a" (value)
+ : "memory", "cc");
+#else
+ for (int i = 0; i < counter; i++) {
+ dest[i] = value;
+ }
+#endif
+
+#undef STOS
+}
+
+
+// Simple wrapper that allows an ExternalString to refer to a
+// Vector<const char>. Doesn't assume ownership of the data.
+class AsciiStringAdapter: public v8::String::ExternalAsciiStringResource {
+ public:
+ explicit AsciiStringAdapter(Vector<const char> data) : data_(data) {}
+
+ virtual const char* data() const { return data_.start(); }
+
+ virtual size_t length() const { return data_.length(); }
+
+ private:
+ Vector<const char> data_;
+};
+
+
+// Simple support to read a file into a 0-terminated C-string.
+// The returned buffer must be freed by the caller.
+// On return, *exits tells whether the file existed.
+Vector<const char> ReadFile(const char* filename,
+ bool* exists,
+ bool verbose = true);
+Vector<const char> ReadFile(FILE* file,
+ bool* exists,
+ bool verbose = true);
+
+
+template <typename sourcechar, typename sinkchar>
+INLINE(static void CopyCharsUnsigned(sinkchar* dest,
+ const sourcechar* src,
+ int chars));
+#if defined(V8_HOST_ARCH_ARM)
+INLINE(void CopyCharsUnsigned(uint8_t* dest, const uint8_t* src, int chars));
+INLINE(void CopyCharsUnsigned(uint16_t* dest, const uint8_t* src, int chars));
+INLINE(void CopyCharsUnsigned(uint16_t* dest, const uint16_t* src, int chars));
+#endif
+
+// Copy from ASCII/16bit chars to ASCII/16bit chars.
+template <typename sourcechar, typename sinkchar>
+INLINE(void CopyChars(sinkchar* dest, const sourcechar* src, int chars));
+
+template<typename sourcechar, typename sinkchar>
+void CopyChars(sinkchar* dest, const sourcechar* src, int chars) {
+ ASSERT(sizeof(sourcechar) <= 2);
+ ASSERT(sizeof(sinkchar) <= 2);
+ if (sizeof(sinkchar) == 1) {
+ if (sizeof(sourcechar) == 1) {
+ CopyCharsUnsigned(reinterpret_cast<uint8_t*>(dest),
+ reinterpret_cast<const uint8_t*>(src),
+ chars);
+ } else {
+ CopyCharsUnsigned(reinterpret_cast<uint8_t*>(dest),
+ reinterpret_cast<const uint16_t*>(src),
+ chars);
+ }
+ } else {
+ if (sizeof(sourcechar) == 1) {
+ CopyCharsUnsigned(reinterpret_cast<uint16_t*>(dest),
+ reinterpret_cast<const uint8_t*>(src),
+ chars);
+ } else {
+ CopyCharsUnsigned(reinterpret_cast<uint16_t*>(dest),
+ reinterpret_cast<const uint16_t*>(src),
+ chars);
+ }
+ }
+}
+
+template <typename sourcechar, typename sinkchar>
+void CopyCharsUnsigned(sinkchar* dest, const sourcechar* src, int chars) {
+ sinkchar* limit = dest + chars;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ if (sizeof(*dest) == sizeof(*src)) {
+ if (chars >= static_cast<int>(OS::kMinComplexMemCopy / sizeof(*dest))) {
+ OS::MemCopy(dest, src, chars * sizeof(*dest));
+ return;
+ }
+ // Number of characters in a uintptr_t.
+ static const int kStepSize = sizeof(uintptr_t) / sizeof(*dest); // NOLINT
+ ASSERT(dest + kStepSize > dest); // Check for overflow.
+ while (dest + kStepSize <= limit) {
+ *reinterpret_cast<uintptr_t*>(dest) =
+ *reinterpret_cast<const uintptr_t*>(src);
+ dest += kStepSize;
+ src += kStepSize;
+ }
+ }
+#endif
+ while (dest < limit) {
+ *dest++ = static_cast<sinkchar>(*src++);
+ }
+}
+
+
+#if defined(V8_HOST_ARCH_ARM)
+void CopyCharsUnsigned(uint8_t* dest, const uint8_t* src, int chars) {
+ switch (static_cast<unsigned>(chars)) {
+ case 0:
+ break;
+ case 1:
+ *dest = *src;
+ break;
+ case 2:
+ memcpy(dest, src, 2);
+ break;
+ case 3:
+ memcpy(dest, src, 3);
+ break;
+ case 4:
+ memcpy(dest, src, 4);
+ break;
+ case 5:
+ memcpy(dest, src, 5);
+ break;
+ case 6:
+ memcpy(dest, src, 6);
+ break;
+ case 7:
+ memcpy(dest, src, 7);
+ break;
+ case 8:
+ memcpy(dest, src, 8);
+ break;
+ case 9:
+ memcpy(dest, src, 9);
+ break;
+ case 10:
+ memcpy(dest, src, 10);
+ break;
+ case 11:
+ memcpy(dest, src, 11);
+ break;
+ case 12:
+ memcpy(dest, src, 12);
+ break;
+ case 13:
+ memcpy(dest, src, 13);
+ break;
+ case 14:
+ memcpy(dest, src, 14);
+ break;
+ case 15:
+ memcpy(dest, src, 15);
+ break;
+ default:
+ OS::MemCopy(dest, src, chars);
+ break;
+ }
+}
+
+
+void CopyCharsUnsigned(uint16_t* dest, const uint8_t* src, int chars) {
+ if (chars >= OS::kMinComplexConvertMemCopy) {
+ OS::MemCopyUint16Uint8(dest, src, chars);
+ } else {
+ OS::MemCopyUint16Uint8Wrapper(dest, src, chars);
+ }
+}
+
+
+void CopyCharsUnsigned(uint16_t* dest, const uint16_t* src, int chars) {
+ switch (static_cast<unsigned>(chars)) {
+ case 0:
+ break;
+ case 1:
+ *dest = *src;
+ break;
+ case 2:
+ memcpy(dest, src, 4);
+ break;
+ case 3:
+ memcpy(dest, src, 6);
+ break;
+ case 4:
+ memcpy(dest, src, 8);
+ break;
+ case 5:
+ memcpy(dest, src, 10);
+ break;
+ case 6:
+ memcpy(dest, src, 12);
+ break;
+ case 7:
+ memcpy(dest, src, 14);
+ break;
+ default:
+ OS::MemCopy(dest, src, chars * sizeof(*dest));
+ break;
+ }
+}
+#endif
+
+
+class StringBuilder : public SimpleStringBuilder {
+ public:
+ explicit StringBuilder(int size) : SimpleStringBuilder(size) { }
+ StringBuilder(char* buffer, int size) : SimpleStringBuilder(buffer, size) { }
+
+ // Add formatted contents to the builder just like printf().
+ void AddFormatted(const char* format, ...);
+
+ // Add formatted contents like printf based on a va_list.
+ void AddFormattedList(const char* format, va_list list);
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_V8UTILS_H_
diff --git a/chromium/v8/src/variables.cc b/chromium/v8/src/variables.cc
new file mode 100644
index 00000000000..488da42ce66
--- /dev/null
+++ b/chromium/v8/src/variables.cc
@@ -0,0 +1,101 @@
+// 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 "ast.h"
+#include "scopes.h"
+#include "variables.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// Implementation Variable.
+
+const char* Variable::Mode2String(VariableMode mode) {
+ switch (mode) {
+ case VAR: return "VAR";
+ case CONST: return "CONST";
+ case LET: return "LET";
+ case CONST_HARMONY: return "CONST_HARMONY";
+ case MODULE: return "MODULE";
+ case DYNAMIC: return "DYNAMIC";
+ case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL";
+ case DYNAMIC_LOCAL: return "DYNAMIC_LOCAL";
+ case INTERNAL: return "INTERNAL";
+ case TEMPORARY: return "TEMPORARY";
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+Variable::Variable(Scope* scope,
+ Handle<String> name,
+ VariableMode mode,
+ bool is_valid_LHS,
+ Kind kind,
+ InitializationFlag initialization_flag,
+ Interface* interface)
+ : 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),
+ force_context_allocation_(false),
+ is_used_(false),
+ initialization_flag_(initialization_flag),
+ interface_(interface) {
+ // Names must be canonicalized for fast equality checks.
+ ASSERT(name->IsInternalizedString());
+ // Var declared variables never need initialization.
+ ASSERT(!(mode == VAR && initialization_flag == kNeedsInitialization));
+}
+
+
+bool Variable::IsGlobalObjectProperty() const {
+ // Temporaries are never global, they must always be allocated in the
+ // activation frame.
+ return (IsDynamicVariableMode(mode_) ||
+ (IsDeclaredVariableMode(mode_) && !IsLexicalVariableMode(mode_)))
+ && 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/chromium/v8/src/variables.h b/chromium/v8/src/variables.h
new file mode 100644
index 00000000000..39451d5dfb1
--- /dev/null
+++ b/chromium/v8/src/variables.h
@@ -0,0 +1,190 @@
+// 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_VARIABLES_H_
+#define V8_VARIABLES_H_
+
+#include "zone.h"
+#include "interface.h"
+
+namespace v8 {
+namespace internal {
+
+// The AST refers to variables via VariableProxies - placeholders for the actual
+// variables. Variables themselves are never directly referred to from the AST,
+// they are maintained by scopes, and referred to from VariableProxies and Slots
+// after binding and variable allocation.
+
+class Variable: public ZoneObject {
+ public:
+ enum Kind {
+ NORMAL,
+ THIS,
+ ARGUMENTS
+ };
+
+ enum Location {
+ // Before and during variable allocation, a variable whose location is
+ // not yet determined. After allocation, a variable looked up as a
+ // property on the global object (and possibly absent). name() is the
+ // variable name, index() is invalid.
+ UNALLOCATED,
+
+ // A slot in the parameter section on the stack. index() is the
+ // parameter index, counting left-to-right. The receiver is index -1;
+ // the first parameter is index 0.
+ PARAMETER,
+
+ // A slot in the local section on the stack. index() is the variable
+ // index in the stack frame, starting at 0.
+ LOCAL,
+
+ // An indexed slot in a heap context. index() is the variable index in
+ // the context object on the heap, starting at 0. scope() is the
+ // corresponding scope.
+ CONTEXT,
+
+ // A named slot in a heap context. name() is the variable name in the
+ // context object on the heap, with lookup starting at the current
+ // context. index() is invalid.
+ LOOKUP
+ };
+
+ Variable(Scope* scope,
+ Handle<String> name,
+ VariableMode mode,
+ bool is_valid_lhs,
+ Kind kind,
+ InitializationFlag initialization_flag,
+ Interface* interface = Interface::NewValue());
+
+ // Printing support
+ static const char* Mode2String(VariableMode mode);
+
+ bool IsValidLeftHandSide() { return is_valid_LHS_; }
+
+ // The source code for an eval() call may refer to a variable that is
+ // in an outer scope about which we don't know anything (it may not
+ // be the global scope). scope() is NULL in that case. Currently the
+ // scope is only used to follow the context chain length.
+ Scope* scope() const { return scope_; }
+
+ Handle<String> name() const { return name_; }
+ VariableMode mode() const { return mode_; }
+ bool has_forced_context_allocation() const {
+ return force_context_allocation_;
+ }
+ void ForceContextAllocation() {
+ ASSERT(mode_ != TEMPORARY);
+ 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);
+ }
+
+ bool IsUnallocated() const { return location_ == UNALLOCATED; }
+ bool IsParameter() const { return location_ == PARAMETER; }
+ bool IsStackLocal() const { return location_ == LOCAL; }
+ bool IsStackAllocated() const { return IsParameter() || IsStackLocal(); }
+ bool IsContextSlot() const { return location_ == CONTEXT; }
+ bool IsLookupSlot() const { return location_ == LOOKUP; }
+ bool IsGlobalObjectProperty() const;
+
+ bool is_dynamic() const { return IsDynamicVariableMode(mode_); }
+ bool is_const_mode() const { return IsImmutableVariableMode(mode_); }
+ bool binding_needs_init() const {
+ return initialization_flag_ == kNeedsInitialization;
+ }
+
+ bool is_this() const { return kind_ == THIS; }
+ bool is_arguments() const { return kind_ == ARGUMENTS; }
+
+ // True if the variable is named eval and not known to be shadowed.
+ bool is_possibly_eval(Isolate* isolate) const {
+ return IsVariable(isolate->factory()->eval_string());
+ }
+
+ Variable* local_if_not_shadowed() const {
+ ASSERT(mode_ == DYNAMIC_LOCAL && local_if_not_shadowed_ != NULL);
+ return local_if_not_shadowed_;
+ }
+
+ void set_local_if_not_shadowed(Variable* local) {
+ local_if_not_shadowed_ = local;
+ }
+
+ Location location() const { return location_; }
+ int index() const { return index_; }
+ InitializationFlag initialization_flag() const {
+ return initialization_flag_;
+ }
+ Interface* interface() const { return interface_; }
+
+ 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_;
+ 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 force_context_allocation_; // set by variable resolver
+ bool is_used_;
+ InitializationFlag initialization_flag_;
+
+ // Module type info.
+ Interface* interface_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_VARIABLES_H_
diff --git a/chromium/v8/src/version.cc b/chromium/v8/src/version.cc
new file mode 100644
index 00000000000..5142d389c21
--- /dev/null
+++ b/chromium/v8/src/version.cc
@@ -0,0 +1,116 @@
+// Copyright 2012 the V8 project authors. 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 "version.h"
+
+// These macros define the version number for the current version.
+// NOTE these macros are used by some of the tool scripts and the build
+// system so their names cannot be changed without changing the scripts.
+#define MAJOR_VERSION 3
+#define MINOR_VERSION 20
+#define BUILD_NUMBER 17
+#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
+
+// Define SONAME to have the build system put a specific SONAME into the
+// shared library instead the generic SONAME generated from the V8 version
+// number. This define is mainly used by the build system script.
+#define SONAME ""
+
+#if IS_CANDIDATE_VERSION
+#define CANDIDATE_STRING " (candidate)"
+#else
+#define CANDIDATE_STRING ""
+#endif
+
+#define SX(x) #x
+#define S(x) SX(x)
+
+#if PATCH_LEVEL > 0
+#define VERSION_STRING \
+ S(MAJOR_VERSION) "." S(MINOR_VERSION) "." S(BUILD_NUMBER) "." \
+ S(PATCH_LEVEL) CANDIDATE_STRING
+#else
+#define VERSION_STRING \
+ S(MAJOR_VERSION) "." S(MINOR_VERSION) "." S(BUILD_NUMBER) \
+ CANDIDATE_STRING
+#endif
+
+namespace v8 {
+namespace internal {
+
+int Version::major_ = MAJOR_VERSION;
+int Version::minor_ = MINOR_VERSION;
+int Version::build_ = BUILD_NUMBER;
+int Version::patch_ = PATCH_LEVEL;
+bool Version::candidate_ = (IS_CANDIDATE_VERSION != 0);
+const char* Version::soname_ = SONAME;
+const char* Version::version_string_ = VERSION_STRING;
+
+// Calculate the V8 version string.
+void Version::GetString(Vector<char> str) {
+ const char* candidate = IsCandidate() ? " (candidate)" : "";
+#ifdef USE_SIMULATOR
+ const char* is_simulator = " SIMULATOR";
+#else
+ const char* is_simulator = "";
+#endif // USE_SIMULATOR
+ if (GetPatch() > 0) {
+ OS::SNPrintF(str, "%d.%d.%d.%d%s%s",
+ GetMajor(), GetMinor(), GetBuild(), GetPatch(), candidate,
+ is_simulator);
+ } else {
+ OS::SNPrintF(str, "%d.%d.%d%s%s",
+ GetMajor(), GetMinor(), GetBuild(), candidate,
+ is_simulator);
+ }
+}
+
+
+// Calculate the SONAME for the V8 shared library.
+void Version::GetSONAME(Vector<char> str) {
+ if (soname_ == NULL || *soname_ == '\0') {
+ // Generate generic SONAME if no specific SONAME is defined.
+ const char* candidate = IsCandidate() ? "-candidate" : "";
+ if (GetPatch() > 0) {
+ OS::SNPrintF(str, "libv8-%d.%d.%d.%d%s.so",
+ GetMajor(), GetMinor(), GetBuild(), GetPatch(), candidate);
+ } else {
+ OS::SNPrintF(str, "libv8-%d.%d.%d%s.so",
+ GetMajor(), GetMinor(), GetBuild(), candidate);
+ }
+ } else {
+ // Use specific SONAME.
+ OS::SNPrintF(str, "%s", soname_);
+ }
+}
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/version.h b/chromium/v8/src/version.h
new file mode 100644
index 00000000000..4b3e7e2bde3
--- /dev/null
+++ b/chromium/v8/src/version.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef V8_VERSION_H_
+#define V8_VERSION_H_
+
+namespace v8 {
+namespace internal {
+
+class Version {
+ public:
+ // Return the various version components.
+ static int GetMajor() { return major_; }
+ static int GetMinor() { return minor_; }
+ static int GetBuild() { return build_; }
+ static int GetPatch() { return patch_; }
+ static bool IsCandidate() { return candidate_; }
+
+ // Calculate the V8 version string.
+ static void GetString(Vector<char> str);
+
+ // Calculate the SONAME for the V8 shared library.
+ static void GetSONAME(Vector<char> str);
+
+ static const char* GetVersion() { return version_string_; }
+
+ private:
+ // NOTE: can't make these really const because of test-version.cc.
+ static int major_;
+ static int minor_;
+ static int build_;
+ static int patch_;
+ static bool candidate_;
+ static const char* soname_;
+ static const char* version_string_;
+
+ // In test-version.cc.
+ friend void SetVersion(int major, int minor, int build, int patch,
+ bool candidate, const char* soname);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_VERSION_H_
diff --git a/chromium/v8/src/vm-state-inl.h b/chromium/v8/src/vm-state-inl.h
new file mode 100644
index 00000000000..658773e6d6a
--- /dev/null
+++ b/chromium/v8/src/vm-state-inl.h
@@ -0,0 +1,109 @@
+// 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.
+
+#ifndef V8_VM_STATE_INL_H_
+#define V8_VM_STATE_INL_H_
+
+#include "vm-state.h"
+#include "log.h"
+#include "simulator.h"
+
+namespace v8 {
+namespace internal {
+
+//
+// VMState class implementation. A simple stack of VM states held by the
+// logger and partially threaded through the call stack. States are pushed by
+// VMState construction and popped by destruction.
+//
+inline const char* StateToString(StateTag state) {
+ switch (state) {
+ case JS:
+ return "JS";
+ case GC:
+ return "GC";
+ case COMPILER:
+ return "COMPILER";
+ case OTHER:
+ return "OTHER";
+ case EXTERNAL:
+ return "EXTERNAL";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+template <StateTag Tag>
+VMState<Tag>::VMState(Isolate* isolate)
+ : isolate_(isolate), previous_tag_(isolate->current_vm_state()) {
+ if (FLAG_log_timer_events && previous_tag_ != EXTERNAL && Tag == EXTERNAL) {
+ LOG(isolate_,
+ TimerEvent(Logger::START, Logger::TimerEventScope::v8_external));
+ }
+ isolate_->set_current_vm_state(Tag);
+}
+
+
+template <StateTag Tag>
+VMState<Tag>::~VMState() {
+ if (FLAG_log_timer_events && previous_tag_ != EXTERNAL && Tag == EXTERNAL) {
+ LOG(isolate_,
+ TimerEvent(Logger::END, Logger::TimerEventScope::v8_external));
+ }
+ isolate_->set_current_vm_state(previous_tag_);
+}
+
+
+ExternalCallbackScope::ExternalCallbackScope(Isolate* isolate, Address callback)
+ : isolate_(isolate),
+ callback_(callback),
+ previous_scope_(isolate->external_callback_scope()) {
+#ifdef USE_SIMULATOR
+ int32_t sp = Simulator::current(isolate)->get_register(Simulator::sp);
+ scope_address_ = reinterpret_cast<Address>(static_cast<intptr_t>(sp));
+#endif
+ isolate_->set_external_callback_scope(this);
+}
+
+ExternalCallbackScope::~ExternalCallbackScope() {
+ isolate_->set_external_callback_scope(previous_scope_);
+}
+
+Address ExternalCallbackScope::scope_address() {
+#ifdef USE_SIMULATOR
+ return scope_address_;
+#else
+ return reinterpret_cast<Address>(this);
+#endif
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_VM_STATE_INL_H_
diff --git a/chromium/v8/src/vm-state.h b/chromium/v8/src/vm-state.h
new file mode 100644
index 00000000000..f592bb92ca5
--- /dev/null
+++ b/chromium/v8/src/vm-state.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef V8_VM_STATE_H_
+#define V8_VM_STATE_H_
+
+#include "allocation.h"
+#include "isolate.h"
+
+namespace v8 {
+namespace internal {
+
+template <StateTag Tag>
+class VMState BASE_EMBEDDED {
+ public:
+ explicit inline VMState(Isolate* isolate);
+ inline ~VMState();
+
+ private:
+ Isolate* isolate_;
+ StateTag previous_tag_;
+};
+
+
+class ExternalCallbackScope BASE_EMBEDDED {
+ public:
+ inline ExternalCallbackScope(Isolate* isolate, Address callback);
+ inline ~ExternalCallbackScope();
+ Address callback() { return callback_; }
+ Address* callback_address() { return &callback_; }
+ ExternalCallbackScope* previous() { return previous_scope_; }
+ inline Address scope_address();
+
+ private:
+ Isolate* isolate_;
+ Address callback_;
+ ExternalCallbackScope* previous_scope_;
+#ifdef USE_SIMULATOR
+ Address scope_address_;
+#endif
+};
+
+} } // namespace v8::internal
+
+
+#endif // V8_VM_STATE_H_
diff --git a/chromium/v8/src/win32-headers.h b/chromium/v8/src/win32-headers.h
new file mode 100644
index 00000000000..2b5d7d71f21
--- /dev/null
+++ b/chromium/v8/src/win32-headers.h
@@ -0,0 +1,98 @@
+// Copyright 2012 the V8 project authors. 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 WIN32_LEAN_AND_MEAN
+// WIN32_LEAN_AND_MEAN implies NOCRYPT and NOGDI.
+#define WIN32_LEAN_AND_MEAN
+#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
+// Require Windows XP or higher (this is required for the RtlCaptureContext
+// function to be present).
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x501
+#endif
+
+#include <windows.h>
+
+#ifdef V8_WIN32_HEADERS_FULL
+#include <signal.h> // For raise().
+#include <time.h> // For LocalOffset() implementation.
+#include <mmsystem.h> // For timeGetTime().
+#ifdef __MINGW32__
+// Require Windows XP or higher when compiling with MinGW. This is for MinGW
+// header files to expose getaddrinfo.
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x501
+#endif // __MINGW32__
+#if !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)
+#include <dbghelp.h> // For SymLoadModule64 and al.
+#include <errno.h> // For STRUNCATE
+#endif // !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)
+#include <limits.h> // For INT_MAX and al.
+#include <tlhelp32.h> // For Module32First and al.
+
+// These additional WIN32 includes have to be right here as the #undef's below
+// 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
+
+#undef VOID
+#undef DELETE
+#undef IN
+#undef THIS
+#undef CONST
+#undef NAN
+#undef UNKNOWN
+#undef NONE
+#undef ANY
+#undef IGNORE
+#undef GetObject
+#undef CreateMutex
+#undef CreateSemaphore
diff --git a/chromium/v8/src/win32-math.cc b/chromium/v8/src/win32-math.cc
new file mode 100644
index 00000000000..9ffc4ea73bd
--- /dev/null
+++ b/chromium/v8/src/win32-math.cc
@@ -0,0 +1,107 @@
+// 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.
+
+// Extra POSIX/ANSI routines for Win32 when using Visual Studio C++. Please
+// refer to The Open Group Base Specification for specification of the correct
+// semantics for these functions.
+// (http://www.opengroup.org/onlinepubs/000095399/)
+#ifdef _MSC_VER
+
+#undef V8_WIN32_LEAN_AND_MEAN
+#define V8_WIN32_HEADERS_FULL
+#include "win32-headers.h"
+#include <limits.h> // Required for INT_MAX etc.
+#include <float.h> // Required for DBL_MAX and on Win32 for finite()
+#include <cmath>
+#include "win32-math.h"
+
+#include "checks.h"
+
+
+namespace std {
+
+// Test for a NaN (not a number) value - usually defined in math.h
+int isnan(double x) {
+ return _isnan(x);
+}
+
+
+// Test for infinity - usually defined in math.h
+int isinf(double x) {
+ return (_fpclass(x) & (_FPCLASS_PINF | _FPCLASS_NINF)) != 0;
+}
+
+
+// Test for finite value - usually defined in math.h
+int isfinite(double x) {
+ return _finite(x);
+}
+
+
+// Test if x is less than y and both nominal - usually defined in math.h
+int isless(double x, double y) {
+ return isnan(x) || isnan(y) ? 0 : x < y;
+}
+
+
+// Test if x is greater than y and both nominal - usually defined in math.h
+int isgreater(double x, double y) {
+ return isnan(x) || isnan(y) ? 0 : x > y;
+}
+
+
+// Classify floating point number - usually defined in math.h
+int fpclassify(double x) {
+ // Use the MS-specific _fpclass() for classification.
+ int flags = _fpclass(x);
+
+ // Determine class. We cannot use a switch statement because
+ // the _FPCLASS_ constants are defined as flags.
+ if (flags & (_FPCLASS_PN | _FPCLASS_NN)) return FP_NORMAL;
+ if (flags & (_FPCLASS_PZ | _FPCLASS_NZ)) return FP_ZERO;
+ if (flags & (_FPCLASS_PD | _FPCLASS_ND)) return FP_SUBNORMAL;
+ if (flags & (_FPCLASS_PINF | _FPCLASS_NINF)) return FP_INFINITE;
+
+ // All cases should be covered by the code above.
+ ASSERT(flags & (_FPCLASS_SNAN | _FPCLASS_QNAN));
+ return FP_NAN;
+}
+
+
+// Test sign - usually defined in math.h
+int signbit(double x) {
+ // We need to take care of the special case of both positive
+ // and negative versions of zero.
+ if (x == 0)
+ return _fpclass(x) & _FPCLASS_NZ;
+ else
+ return x < 0;
+}
+
+} // namespace std
+
+#endif // _MSC_VER
diff --git a/chromium/v8/src/win32-math.h b/chromium/v8/src/win32-math.h
new file mode 100644
index 00000000000..0397c7e14ea
--- /dev/null
+++ b/chromium/v8/src/win32-math.h
@@ -0,0 +1,61 @@
+// 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.
+
+// Extra POSIX/ANSI routines for Win32 when using Visual Studio C++. Please
+// refer to The Open Group Base Specification for specification of the correct
+// semantics for these functions.
+// (http://www.opengroup.org/onlinepubs/000095399/)
+
+#ifndef V8_WIN32_MATH_H_
+#define V8_WIN32_MATH_H_
+
+#ifndef _MSC_VER
+#error Wrong environment, expected MSVC.
+#endif // _MSC_VER
+
+enum {
+ FP_NAN,
+ FP_INFINITE,
+ FP_ZERO,
+ FP_SUBNORMAL,
+ FP_NORMAL
+};
+
+
+namespace std {
+
+int isfinite(double x);
+int isinf(double x);
+int isnan(double x);
+int isless(double x, double y);
+int isgreater(double x, double y);
+int fpclassify(double x);
+int signbit(double x);
+
+} // namespace std
+
+#endif // V8_WIN32_MATH_H_
diff --git a/chromium/v8/src/x64/assembler-x64-inl.h b/chromium/v8/src/x64/assembler-x64-inl.h
new file mode 100644
index 00000000000..826c06e5bad
--- /dev/null
+++ b/chromium/v8/src/x64/assembler-x64-inl.h
@@ -0,0 +1,549 @@
+// Copyright 2012 the V8 project authors. 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_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"
+
+namespace v8 {
+namespace internal {
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Assembler
+
+
+static const byte kCallOpcode = 0xE8;
+
+
+void Assembler::emitl(uint32_t x) {
+ Memory::uint32_at(pc_) = x;
+ pc_ += sizeof(uint32_t);
+}
+
+
+void Assembler::emitp(void* x, RelocInfo::Mode rmode) {
+ uintptr_t value = reinterpret_cast<uintptr_t>(x);
+ Memory::uintptr_at(pc_) = value;
+ if (!RelocInfo::IsNone(rmode)) {
+ RecordRelocInfo(rmode, value);
+ }
+ pc_ += sizeof(uintptr_t);
+}
+
+
+void Assembler::emitq(uint64_t x, RelocInfo::Mode rmode) {
+ Memory::uint64_at(pc_) = x;
+ if (!RelocInfo::IsNone(rmode)) {
+ RecordRelocInfo(rmode, x);
+ }
+ pc_ += sizeof(uint64_t);
+}
+
+
+void Assembler::emitw(uint16_t x) {
+ Memory::uint16_at(pc_) = x;
+ pc_ += sizeof(uint16_t);
+}
+
+
+void Assembler::emit_code_target(Handle<Code> target,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id) {
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) {
+ RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, ast_id.ToInt());
+ } else {
+ RecordRelocInfo(rmode);
+ }
+ int current = code_targets_.length();
+ if (current > 0 && code_targets_.last().is_identical_to(target)) {
+ // Optimization if we keep jumping to the same code target.
+ emitl(current - 1);
+ } else {
+ code_targets_.Add(target);
+ emitl(current);
+ }
+}
+
+
+void Assembler::emit_runtime_entry(Address entry, RelocInfo::Mode rmode) {
+ ASSERT(RelocInfo::IsRuntimeEntry(rmode));
+ ASSERT(isolate()->code_range()->exists());
+ RecordRelocInfo(rmode);
+ emitl(static_cast<uint32_t>(entry - isolate()->code_range()->start()));
+}
+
+
+void Assembler::emit_rex_64(Register reg, Register rm_reg) {
+ emit(0x48 | reg.high_bit() << 2 | rm_reg.high_bit());
+}
+
+
+void Assembler::emit_rex_64(XMMRegister reg, Register rm_reg) {
+ emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3);
+}
+
+
+void Assembler::emit_rex_64(Register reg, XMMRegister rm_reg) {
+ emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3);
+}
+
+
+void Assembler::emit_rex_64(Register reg, const Operand& op) {
+ emit(0x48 | reg.high_bit() << 2 | op.rex_);
+}
+
+
+void Assembler::emit_rex_64(XMMRegister reg, const Operand& op) {
+ emit(0x48 | (reg.code() & 0x8) >> 1 | op.rex_);
+}
+
+
+void Assembler::emit_rex_64(Register rm_reg) {
+ ASSERT_EQ(rm_reg.code() & 0xf, rm_reg.code());
+ emit(0x48 | rm_reg.high_bit());
+}
+
+
+void Assembler::emit_rex_64(const Operand& op) {
+ emit(0x48 | op.rex_);
+}
+
+
+void Assembler::emit_rex_32(Register reg, Register rm_reg) {
+ emit(0x40 | reg.high_bit() << 2 | rm_reg.high_bit());
+}
+
+
+void Assembler::emit_rex_32(Register reg, const Operand& op) {
+ emit(0x40 | reg.high_bit() << 2 | op.rex_);
+}
+
+
+void Assembler::emit_rex_32(Register rm_reg) {
+ emit(0x40 | rm_reg.high_bit());
+}
+
+
+void Assembler::emit_rex_32(const Operand& op) {
+ emit(0x40 | op.rex_);
+}
+
+
+void Assembler::emit_optional_rex_32(Register reg, Register rm_reg) {
+ byte rex_bits = reg.high_bit() << 2 | rm_reg.high_bit();
+ if (rex_bits != 0) emit(0x40 | rex_bits);
+}
+
+
+void Assembler::emit_optional_rex_32(Register reg, const Operand& op) {
+ byte rex_bits = reg.high_bit() << 2 | op.rex_;
+ if (rex_bits != 0) emit(0x40 | rex_bits);
+}
+
+
+void Assembler::emit_optional_rex_32(XMMRegister reg, const Operand& op) {
+ byte rex_bits = (reg.code() & 0x8) >> 1 | op.rex_;
+ if (rex_bits != 0) emit(0x40 | rex_bits);
+}
+
+
+void Assembler::emit_optional_rex_32(XMMRegister reg, XMMRegister base) {
+ byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3;
+ if (rex_bits != 0) emit(0x40 | rex_bits);
+}
+
+
+void Assembler::emit_optional_rex_32(XMMRegister reg, Register base) {
+ byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3;
+ if (rex_bits != 0) emit(0x40 | rex_bits);
+}
+
+
+void Assembler::emit_optional_rex_32(Register reg, XMMRegister base) {
+ byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3;
+ if (rex_bits != 0) emit(0x40 | rex_bits);
+}
+
+
+void Assembler::emit_optional_rex_32(Register rm_reg) {
+ if (rm_reg.high_bit()) emit(0x41);
+}
+
+
+void Assembler::emit_optional_rex_32(const Operand& op) {
+ if (op.rex_ != 0) emit(0x40 | op.rex_);
+}
+
+
+Address Assembler::target_address_at(Address pc) {
+ return Memory::int32_at(pc) + pc + 4;
+}
+
+
+void Assembler::set_target_address_at(Address pc, Address target) {
+ Memory::int32_at(pc) = static_cast<int32_t>(target - pc - 4);
+ CPU::FlushICache(pc, sizeof(int32_t));
+}
+
+
+Address Assembler::target_address_from_return_address(Address pc) {
+ return pc - kCallTargetAddressOffset;
+}
+
+
+Handle<Object> Assembler::code_target_object_handle_at(Address pc) {
+ return code_targets_[Memory::int32_at(pc)];
+}
+
+
+Address Assembler::runtime_entry_at(Address pc) {
+ ASSERT(isolate()->code_range()->exists());
+ return Memory::int32_at(pc) + isolate()->code_range()->start();
+}
+
+// -----------------------------------------------------------------------------
+// Implementation of RelocInfo
+
+// The modes possibly affected by apply must be in kApplyMask.
+void RelocInfo::apply(intptr_t delta) {
+ if (IsInternalReference(rmode_)) {
+ // absolute code pointer inside code object moves with the code object.
+ Memory::Address_at(pc_) += static_cast<int32_t>(delta);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) {
+ Memory::int32_at(pc_) -= static_cast<int32_t>(delta);
+ CPU::FlushICache(pc_, sizeof(int32_t));
+ } else if (rmode_ == CODE_AGE_SEQUENCE) {
+ if (*pc_ == kCallOpcode) {
+ int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
+ *p -= static_cast<int32_t>(delta); // Relocate entry.
+ CPU::FlushICache(p, sizeof(uint32_t));
+ }
+ }
+}
+
+
+Address RelocInfo::target_address() {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
+ return Assembler::target_address_at(pc_);
+}
+
+
+Address RelocInfo::target_address_address() {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
+ || rmode_ == EMBEDDED_OBJECT
+ || rmode_ == EXTERNAL_REFERENCE);
+ return reinterpret_cast<Address>(pc_);
+}
+
+
+int RelocInfo::target_address_size() {
+ if (IsCodedSpecially()) {
+ return Assembler::kSpecialTargetSize;
+ } else {
+ return kPointerSize;
+ }
+}
+
+
+void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) {
+ ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
+ 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));
+ }
+}
+
+
+Object* RelocInfo::target_object() {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ return Memory::Object_at(pc_);
+}
+
+
+Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ if (rmode_ == EMBEDDED_OBJECT) {
+ return Memory::Object_Handle_at(pc_);
+ } else {
+ return origin->code_target_object_handle_at(pc_);
+ }
+}
+
+
+Object** RelocInfo::target_object_address() {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ return reinterpret_cast<Object**>(pc_);
+}
+
+
+Address* RelocInfo::target_reference_address() {
+ ASSERT(rmode_ == RelocInfo::EXTERNAL_REFERENCE);
+ return reinterpret_cast<Address*>(pc_);
+}
+
+
+void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
+ ASSERT(!target->IsConsString());
+ 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));
+ }
+}
+
+
+Address RelocInfo::target_runtime_entry(Assembler* origin) {
+ ASSERT(IsRuntimeEntry(rmode_));
+ return origin->runtime_entry_at(pc_);
+}
+
+
+void RelocInfo::set_target_runtime_entry(Address target,
+ WriteBarrierMode mode) {
+ ASSERT(IsRuntimeEntry(rmode_));
+ if (target_address() != target) set_target_address(target, mode);
+}
+
+
+Handle<Cell> RelocInfo::target_cell_handle() {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ Address address = Memory::Address_at(pc_);
+ return Handle<Cell>(reinterpret_cast<Cell**>(address));
+}
+
+
+Cell* RelocInfo::target_cell() {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ return Cell::FromValueAddress(Memory::Address_at(pc_));
+}
+
+
+void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) {
+ ASSERT(rmode_ == RelocInfo::CELL);
+ Address address = cell->address() + Cell::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);
+ }
+}
+
+
+bool RelocInfo::IsPatchedReturnSequence() {
+ // The recognized call sequence is:
+ // movq(kScratchRegister, address); call(kScratchRegister);
+ // It only needs to be distinguished from a return sequence
+ // movq(rsp, rbp); pop(rbp); ret(n); int3 *6
+ // The 11th byte is int3 (0xCC) in the return sequence and
+ // REX.WB (0x48+register bit) for the call sequence.
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ return pc_[Assembler::kMoveAddressIntoScratchRegisterInstructionLength] !=
+ 0xCC;
+#else
+ return false;
+#endif
+}
+
+
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+ return !Assembler::IsNop(pc());
+}
+
+
+Code* RelocInfo::code_age_stub() {
+ ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
+ ASSERT(*pc_ == kCallOpcode);
+ return Code::GetCodeFromTargetAddress(
+ Assembler::target_address_at(pc_ + 1));
+}
+
+
+void RelocInfo::set_code_age_stub(Code* stub) {
+ ASSERT(*pc_ == kCallOpcode);
+ ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
+ Assembler::set_target_address_at(pc_ + 1, stub->instruction_start());
+}
+
+
+Address RelocInfo::call_address() {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ return Memory::Address_at(
+ pc_ + Assembler::kRealPatchReturnSequenceAddressOffset);
+}
+
+
+void RelocInfo::set_call_address(Address target) {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ Memory::Address_at(pc_ + Assembler::kRealPatchReturnSequenceAddressOffset) =
+ 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));
+ }
+}
+
+
+Object* RelocInfo::call_object() {
+ return *call_object_address();
+}
+
+
+void RelocInfo::set_call_object(Object* target) {
+ *call_object_address() = target;
+}
+
+
+Object** RelocInfo::call_object_address() {
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+ return reinterpret_cast<Object**>(
+ pc_ + Assembler::kPatchReturnSequenceAddressOffset);
+}
+
+
+void RelocInfo::Visit(ObjectVisitor* visitor) {
+ RelocInfo::Mode mode = rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT) {
+ visitor->VisitEmbeddedPointer(this);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (RelocInfo::IsCodeTarget(mode)) {
+ visitor->VisitCodeTarget(this);
+ } else if (mode == RelocInfo::CELL) {
+ visitor->VisitCell(this);
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
+ visitor->VisitExternalReference(this);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (RelocInfo::IsCodeAgeSequence(mode)) {
+ visitor->VisitCodeAgeSequence(this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // TODO(isolates): Get a cached isolate below.
+ } else if (((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence())) &&
+ Isolate::Current()->debug()->has_break_points()) {
+ visitor->VisitDebugTarget(this);
+#endif
+ } else if (RelocInfo::IsRuntimeEntry(mode)) {
+ visitor->VisitRuntimeEntry(this);
+ }
+}
+
+
+template<typename StaticVisitor>
+void RelocInfo::Visit(Heap* heap) {
+ RelocInfo::Mode mode = rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT) {
+ StaticVisitor::VisitEmbeddedPointer(heap, this);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (RelocInfo::IsCodeTarget(mode)) {
+ StaticVisitor::VisitCodeTarget(heap, this);
+ } else if (mode == RelocInfo::CELL) {
+ StaticVisitor::VisitCell(heap, this);
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
+ StaticVisitor::VisitExternalReference(this);
+ CPU::FlushICache(pc_, sizeof(Address));
+ } else if (RelocInfo::IsCodeAgeSequence(mode)) {
+ StaticVisitor::VisitCodeAgeSequence(heap, this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ } else if (heap->isolate()->debug()->has_break_points() &&
+ ((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()))) {
+ StaticVisitor::VisitDebugTarget(heap, this);
+#endif
+ } else if (RelocInfo::IsRuntimeEntry(mode)) {
+ StaticVisitor::VisitRuntimeEntry(this);
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Operand
+
+void Operand::set_modrm(int mod, Register rm_reg) {
+ ASSERT(is_uint2(mod));
+ buf_[0] = mod << 6 | rm_reg.low_bits();
+ // Set REX.B to the high bit of rm.code().
+ rex_ |= rm_reg.high_bit();
+}
+
+
+void Operand::set_sib(ScaleFactor scale, Register index, Register base) {
+ ASSERT(len_ == 1);
+ ASSERT(is_uint2(scale));
+ // Use SIB with no index register only for base rsp or r12. Otherwise we
+ // would skip the SIB byte entirely.
+ ASSERT(!index.is(rsp) || base.is(rsp) || base.is(r12));
+ buf_[1] = (scale << 6) | (index.low_bits() << 3) | base.low_bits();
+ rex_ |= index.high_bit() << 1 | base.high_bit();
+ len_ = 2;
+}
+
+void Operand::set_disp8(int disp) {
+ ASSERT(is_int8(disp));
+ ASSERT(len_ == 1 || len_ == 2);
+ int8_t* p = reinterpret_cast<int8_t*>(&buf_[len_]);
+ *p = disp;
+ len_ += sizeof(int8_t);
+}
+
+void Operand::set_disp32(int disp) {
+ ASSERT(len_ == 1 || len_ == 2);
+ int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]);
+ *p = disp;
+ len_ += sizeof(int32_t);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_X64_ASSEMBLER_X64_INL_H_
diff --git a/chromium/v8/src/x64/assembler-x64.cc b/chromium/v8/src/x64/assembler-x64.cc
new file mode 100644
index 00000000000..8969d89a6a7
--- /dev/null
+++ b/chromium/v8/src/x64/assembler-x64.cc
@@ -0,0 +1,3119 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "macro-assembler.h"
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+// -----------------------------------------------------------------------------
+// Implementation of CpuFeatures
+
+
+#ifdef DEBUG
+bool CpuFeatures::initialized_ = false;
+#endif
+uint64_t CpuFeatures::supported_ = CpuFeatures::kDefaultCpuFeatures;
+uint64_t CpuFeatures::found_by_runtime_probing_only_ = 0;
+
+
+ExternalReference ExternalReference::cpu_features() {
+ ASSERT(CpuFeatures::initialized_);
+ return ExternalReference(&CpuFeatures::supported_);
+}
+
+
+void CpuFeatures::Probe() {
+ ASSERT(supported_ == CpuFeatures::kDefaultCpuFeatures);
+#ifdef DEBUG
+ initialized_ = true;
+#endif
+ supported_ = kDefaultCpuFeatures;
+ if (Serializer::enabled()) {
+ supported_ |= OS::CpuFeaturesImpliedByPlatform();
+ return; // No features if we might serialize.
+ }
+
+ const int kBufferSize = 4 * KB;
+ VirtualMemory* memory = new VirtualMemory(kBufferSize);
+ if (!memory->IsReserved()) {
+ delete memory;
+ return;
+ }
+ ASSERT(memory->size() >= static_cast<size_t>(kBufferSize));
+ if (!memory->Commit(memory->address(), kBufferSize, true/*executable*/)) {
+ delete memory;
+ return;
+ }
+
+ Assembler assm(NULL, memory->address(), kBufferSize);
+ Label cpuid, done;
+#define __ assm.
+ // Save old rsp, since we are going to modify the stack.
+ __ push(rbp);
+ __ pushfq();
+ __ push(rdi);
+ __ push(rcx);
+ __ push(rbx);
+ __ movq(rbp, rsp);
+
+ // If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
+ __ pushfq();
+ __ pop(rax);
+ __ movq(rdx, rax);
+ __ xor_(rax, Immediate(0x200000)); // Flip bit 21.
+ __ push(rax);
+ __ popfq();
+ __ pushfq();
+ __ pop(rax);
+ __ xor_(rax, rdx); // Different if CPUID is supported.
+ __ j(not_zero, &cpuid);
+
+ // CPUID not supported. Clear the supported features in rax.
+ __ xor_(rax, rax);
+ __ jmp(&done);
+
+ // Invoke CPUID with 1 in eax to get feature information in
+ // ecx:edx. Temporarily enable CPUID support because we know it's
+ // safe here.
+ __ bind(&cpuid);
+ __ movl(rax, Immediate(1));
+ supported_ = kDefaultCpuFeatures | (1 << CPUID);
+ { CpuFeatureScope fscope(&assm, CPUID);
+ __ cpuid();
+ // Move the result from ecx:edx to rdi.
+ __ movl(rdi, rdx); // Zero-extended to 64 bits.
+ __ shl(rcx, Immediate(32));
+ __ or_(rdi, rcx);
+
+ // Get the sahf supported flag, from CPUID(0x80000001)
+ __ movq(rax, 0x80000001, RelocInfo::NONE64);
+ __ cpuid();
+ }
+ supported_ = kDefaultCpuFeatures;
+
+ // Put the CPU flags in rax.
+ // rax = (rcx & 1) | (rdi & ~1) | (1 << CPUID).
+ __ movl(rax, Immediate(1));
+ __ and_(rcx, rax); // Bit 0 is set if SAHF instruction supported.
+ __ not_(rax);
+ __ and_(rax, rdi);
+ __ or_(rax, rcx);
+ __ or_(rax, Immediate(1 << CPUID));
+
+ // Done.
+ __ bind(&done);
+ __ movq(rsp, rbp);
+ __ pop(rbx);
+ __ pop(rcx);
+ __ pop(rdi);
+ __ popfq();
+ __ pop(rbp);
+ __ ret(0);
+#undef __
+
+ typedef uint64_t (*F0)();
+ F0 probe = FUNCTION_CAST<F0>(reinterpret_cast<Address>(memory->address()));
+
+ uint64_t probed_features = probe();
+ uint64_t platform_features = OS::CpuFeaturesImpliedByPlatform();
+ supported_ = probed_features | platform_features;
+ found_by_runtime_probing_only_
+ = probed_features & ~kDefaultCpuFeatures & ~platform_features;
+
+ // CMOV must be available on an X64 CPU.
+ ASSERT(IsSupported(CPUID));
+ ASSERT(IsSupported(CMOV));
+
+ delete memory;
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of RelocInfo
+
+// Patch the code at the current PC with a call to the target address.
+// Additional guard int3 instructions can be added if required.
+void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
+ int code_size = Assembler::kCallSequenceLength + guard_bytes;
+
+ // Create a code patcher.
+ CodePatcher patcher(pc_, code_size);
+
+ // Add a label for checking the size of the code used for returning.
+#ifdef DEBUG
+ Label check_codesize;
+ patcher.masm()->bind(&check_codesize);
+#endif
+
+ // Patch the code.
+ patcher.masm()->movq(r10, target, RelocInfo::NONE64);
+ patcher.masm()->call(r10);
+
+ // Check that the size of the code generated is as expected.
+ ASSERT_EQ(Assembler::kCallSequenceLength,
+ patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
+
+ // Add the requested number of int3 instructions after the call.
+ for (int i = 0; i < guard_bytes; i++) {
+ patcher.masm()->int3();
+ }
+}
+
+
+void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
+ // Patch the code at the current address with the supplied instructions.
+ for (int i = 0; i < instruction_count; i++) {
+ *(pc_ + i) = *(instructions + i);
+ }
+
+ // Indicate that code has changed.
+ CPU::FlushICache(pc_, instruction_count);
+}
+
+
+// -----------------------------------------------------------------------------
+// Register constants.
+
+const int
+ Register::kRegisterCodeByAllocationIndex[kMaxNumAllocatableRegisters] = {
+ // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r15
+ 0, 3, 2, 1, 7, 8, 9, 11, 14, 15
+};
+
+const int Register::kAllocationIndexByRegisterCode[kNumRegisters] = {
+ 0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, -1, -1, 8, 9
+};
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Operand
+
+Operand::Operand(Register base, int32_t disp) : rex_(0) {
+ len_ = 1;
+ if (base.is(rsp) || base.is(r12)) {
+ // SIB byte is needed to encode (rsp + offset) or (r12 + offset).
+ set_sib(times_1, rsp, base);
+ }
+
+ if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
+ set_modrm(0, base);
+ } else if (is_int8(disp)) {
+ set_modrm(1, base);
+ set_disp8(disp);
+ } else {
+ set_modrm(2, base);
+ set_disp32(disp);
+ }
+}
+
+
+Operand::Operand(Register base,
+ Register index,
+ ScaleFactor scale,
+ int32_t disp) : rex_(0) {
+ ASSERT(!index.is(rsp));
+ len_ = 1;
+ set_sib(scale, index, base);
+ if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
+ // This call to set_modrm doesn't overwrite the REX.B (or REX.X) bits
+ // possibly set by set_sib.
+ set_modrm(0, rsp);
+ } else if (is_int8(disp)) {
+ set_modrm(1, rsp);
+ set_disp8(disp);
+ } else {
+ set_modrm(2, rsp);
+ set_disp32(disp);
+ }
+}
+
+
+Operand::Operand(Register index,
+ ScaleFactor scale,
+ int32_t disp) : rex_(0) {
+ ASSERT(!index.is(rsp));
+ len_ = 1;
+ set_modrm(0, rsp);
+ set_sib(scale, index, rbp);
+ set_disp32(disp);
+}
+
+
+Operand::Operand(const Operand& operand, int32_t offset) {
+ ASSERT(operand.len_ >= 1);
+ // Operand encodes REX ModR/M [SIB] [Disp].
+ byte modrm = operand.buf_[0];
+ ASSERT(modrm < 0xC0); // Disallow mode 3 (register target).
+ bool has_sib = ((modrm & 0x07) == 0x04);
+ byte mode = modrm & 0xC0;
+ int disp_offset = has_sib ? 2 : 1;
+ int base_reg = (has_sib ? operand.buf_[1] : modrm) & 0x07;
+ // Mode 0 with rbp/r13 as ModR/M or SIB base register always has a 32-bit
+ // displacement.
+ bool is_baseless = (mode == 0) && (base_reg == 0x05); // No base or RIP base.
+ int32_t disp_value = 0;
+ if (mode == 0x80 || is_baseless) {
+ // Mode 2 or mode 0 with rbp/r13 as base: Word displacement.
+ disp_value = *BitCast<const int32_t*>(&operand.buf_[disp_offset]);
+ } else if (mode == 0x40) {
+ // Mode 1: Byte displacement.
+ disp_value = static_cast<signed char>(operand.buf_[disp_offset]);
+ }
+
+ // Write new operand with same registers, but with modified displacement.
+ ASSERT(offset >= 0 ? disp_value + offset > disp_value
+ : disp_value + offset < disp_value); // No overflow.
+ disp_value += offset;
+ rex_ = operand.rex_;
+ if (!is_int8(disp_value) || is_baseless) {
+ // Need 32 bits of displacement, mode 2 or mode 1 with register rbp/r13.
+ buf_[0] = (modrm & 0x3f) | (is_baseless ? 0x00 : 0x80);
+ len_ = disp_offset + 4;
+ Memory::int32_at(&buf_[disp_offset]) = disp_value;
+ } else if (disp_value != 0 || (base_reg == 0x05)) {
+ // Need 8 bits of displacement.
+ buf_[0] = (modrm & 0x3f) | 0x40; // Mode 1.
+ len_ = disp_offset + 1;
+ buf_[disp_offset] = static_cast<byte>(disp_value);
+ } else {
+ // Need no displacement.
+ buf_[0] = (modrm & 0x3f); // Mode 0.
+ len_ = disp_offset;
+ }
+ if (has_sib) {
+ buf_[1] = operand.buf_[1];
+ }
+}
+
+
+bool Operand::AddressUsesRegister(Register reg) const {
+ int code = reg.code();
+ ASSERT((buf_[0] & 0xC0) != 0xC0); // Always a memory operand.
+ // Start with only low three bits of base register. Initial decoding doesn't
+ // distinguish on the REX.B bit.
+ int base_code = buf_[0] & 0x07;
+ if (base_code == rsp.code()) {
+ // SIB byte present in buf_[1].
+ // Check the index register from the SIB byte + REX.X prefix.
+ int index_code = ((buf_[1] >> 3) & 0x07) | ((rex_ & 0x02) << 2);
+ // Index code (including REX.X) of 0x04 (rsp) means no index register.
+ if (index_code != rsp.code() && index_code == code) return true;
+ // Add REX.B to get the full base register code.
+ base_code = (buf_[1] & 0x07) | ((rex_ & 0x01) << 3);
+ // A base register of 0x05 (rbp) with mod = 0 means no base register.
+ if (base_code == rbp.code() && ((buf_[0] & 0xC0) == 0)) return false;
+ return code == base_code;
+ } else {
+ // A base register with low bits of 0x05 (rbp or r13) and mod = 0 means
+ // no base register.
+ if (base_code == rbp.code() && ((buf_[0] & 0xC0) == 0)) return false;
+ base_code |= ((rex_ & 0x01) << 3);
+ return code == base_code;
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of Assembler.
+
+#ifdef GENERATED_CODE_COVERAGE
+static void InitCoverageLog();
+#endif
+
+Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size)
+ : AssemblerBase(isolate, buffer, buffer_size),
+ code_targets_(100),
+ positions_recorder_(this) {
+ // Clear the buffer in debug mode unless it was provided by the
+ // caller in which case we can't be sure it's okay to overwrite
+ // existing code in it.
+#ifdef DEBUG
+ if (own_buffer_) {
+ memset(buffer_, 0xCC, buffer_size_); // int3
+ }
+#endif
+
+ reloc_info_writer.Reposition(buffer_ + buffer_size_, pc_);
+
+
+#ifdef GENERATED_CODE_COVERAGE
+ InitCoverageLog();
+#endif
+}
+
+
+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.
+ // Set up code descriptor.
+ desc->buffer = buffer_;
+ desc->buffer_size = buffer_size_;
+ desc->instr_size = pc_offset();
+ ASSERT(desc->instr_size > 0); // Zero-size code objects upset the system.
+ desc->reloc_size =
+ static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer.pos());
+ desc->origin = this;
+}
+
+
+void Assembler::Align(int m) {
+ ASSERT(IsPowerOf2(m));
+ int delta = (m - (pc_offset() & (m - 1))) & (m - 1);
+ Nop(delta);
+}
+
+
+void Assembler::CodeTargetAlign() {
+ Align(16); // Preferred alignment of jump targets on x64.
+}
+
+
+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.
+ if (L->is_linked()) {
+ int current = L->pos();
+ int next = long_at(current);
+ while (next != current) {
+ // Relative address, relative to point after address.
+ int imm32 = pos - (current + sizeof(int32_t));
+ long_at_put(current, imm32);
+ current = next;
+ next = long_at(next);
+ }
+ // Fix up last fixup on linked list.
+ int last_imm32 = pos - (current + sizeof(int32_t));
+ long_at_put(current, last_imm32);
+ }
+ while (L->is_near_linked()) {
+ int fixup_pos = L->near_link_pos();
+ int offset_to_next =
+ static_cast<int>(*reinterpret_cast<int8_t*>(addr_at(fixup_pos)));
+ ASSERT(offset_to_next <= 0);
+ int disp = pos - (fixup_pos + sizeof(int8_t));
+ CHECK(is_int8(disp));
+ set_byte_at(fixup_pos, disp);
+ if (offset_to_next < 0) {
+ L->link_to(fixup_pos + offset_to_next, Label::kNear);
+ } else {
+ L->UnuseNear();
+ }
+ }
+ L->bind_to(pos);
+}
+
+
+void Assembler::bind(Label* L) {
+ bind_to(L, pc_offset());
+}
+
+
+void Assembler::GrowBuffer() {
+ ASSERT(buffer_overflow());
+ if (!own_buffer_) FATAL("external code buffer is too small");
+
+ // Compute new buffer size.
+ CodeDesc desc; // the new buffer
+ if (buffer_size_ < 4*KB) {
+ desc.buffer_size = 4*KB;
+ } else {
+ desc.buffer_size = 2*buffer_size_;
+ }
+ // Some internal data structures overflow for very large buffers,
+ // they must ensure that kMaximalBufferSize is not too large.
+ if ((desc.buffer_size > kMaximalBufferSize) ||
+ (desc.buffer_size > HEAP->MaxOldGenerationSize())) {
+ V8::FatalProcessOutOfMemory("Assembler::GrowBuffer");
+ }
+
+ // Set up new buffer.
+ desc.buffer = NewArray<byte>(desc.buffer_size);
+ desc.instr_size = pc_offset();
+ desc.reloc_size =
+ static_cast<int>((buffer_ + buffer_size_) - (reloc_info_writer.pos()));
+
+ // Clear the buffer in debug mode. Use 'int3' instructions to make
+ // sure to get into problems if we ever run uninitialized code.
+#ifdef DEBUG
+ memset(desc.buffer, 0xCC, desc.buffer_size);
+#endif
+
+ // Copy the data.
+ intptr_t pc_delta = desc.buffer - buffer_;
+ intptr_t rc_delta = (desc.buffer + desc.buffer_size) -
+ (buffer_ + buffer_size_);
+ OS::MemMove(desc.buffer, buffer_, desc.instr_size);
+ OS::MemMove(rc_delta + reloc_info_writer.pos(),
+ reloc_info_writer.pos(), desc.reloc_size);
+
+ // Switch buffers.
+ if (isolate() != NULL &&
+ isolate()->assembler_spare_buffer() == NULL &&
+ buffer_size_ == kMinimalBufferSize) {
+ isolate()->set_assembler_spare_buffer(buffer_);
+ } else {
+ DeleteArray(buffer_);
+ }
+ buffer_ = desc.buffer;
+ buffer_size_ = desc.buffer_size;
+ pc_ += pc_delta;
+ reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
+ reloc_info_writer.last_pc() + pc_delta);
+
+ // Relocate runtime entries.
+ for (RelocIterator it(desc); !it.done(); it.next()) {
+ RelocInfo::Mode rmode = it.rinfo()->rmode();
+ if (rmode == RelocInfo::INTERNAL_REFERENCE) {
+ intptr_t* p = reinterpret_cast<intptr_t*>(it.rinfo()->pc());
+ if (*p != 0) { // 0 means uninitialized.
+ *p += pc_delta;
+ }
+ }
+ }
+
+ ASSERT(!buffer_overflow());
+}
+
+
+void Assembler::emit_operand(int code, const Operand& adr) {
+ ASSERT(is_uint3(code));
+ const unsigned length = adr.len_;
+ ASSERT(length > 0);
+
+ // Emit updated ModR/M byte containing the given register.
+ ASSERT((adr.buf_[0] & 0x38) == 0);
+ pc_[0] = adr.buf_[0] | code << 3;
+
+ // Emit the rest of the encoded operand.
+ for (unsigned i = 1; i < length; i++) pc_[i] = adr.buf_[i];
+ pc_ += length;
+}
+
+
+// Assembler Instruction implementations.
+
+void Assembler::arithmetic_op(byte opcode, Register reg, const Operand& op) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(reg, op);
+ emit(opcode);
+ emit_operand(reg, op);
+}
+
+
+void Assembler::arithmetic_op(byte opcode, Register reg, Register rm_reg) {
+ EnsureSpace ensure_space(this);
+ ASSERT((opcode & 0xC6) == 2);
+ if (rm_reg.low_bits() == 4) { // Forces SIB byte.
+ // Swap reg and rm_reg and change opcode operand order.
+ emit_rex_64(rm_reg, reg);
+ emit(opcode ^ 0x02);
+ emit_modrm(rm_reg, reg);
+ } else {
+ emit_rex_64(reg, rm_reg);
+ emit(opcode);
+ emit_modrm(reg, rm_reg);
+ }
+}
+
+
+void Assembler::arithmetic_op_16(byte opcode, Register reg, Register rm_reg) {
+ EnsureSpace ensure_space(this);
+ ASSERT((opcode & 0xC6) == 2);
+ if (rm_reg.low_bits() == 4) { // Forces SIB byte.
+ // Swap reg and rm_reg and change opcode operand order.
+ emit(0x66);
+ emit_optional_rex_32(rm_reg, reg);
+ emit(opcode ^ 0x02);
+ emit_modrm(rm_reg, reg);
+ } else {
+ emit(0x66);
+ emit_optional_rex_32(reg, rm_reg);
+ emit(opcode);
+ emit_modrm(reg, rm_reg);
+ }
+}
+
+
+void Assembler::arithmetic_op_16(byte opcode,
+ Register reg,
+ const Operand& rm_reg) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(reg, rm_reg);
+ emit(opcode);
+ emit_operand(reg, rm_reg);
+}
+
+
+void Assembler::arithmetic_op_32(byte opcode, Register reg, Register rm_reg) {
+ EnsureSpace ensure_space(this);
+ ASSERT((opcode & 0xC6) == 2);
+ if (rm_reg.low_bits() == 4) { // Forces SIB byte.
+ // Swap reg and rm_reg and change opcode operand order.
+ emit_optional_rex_32(rm_reg, reg);
+ emit(opcode ^ 0x02); // E.g. 0x03 -> 0x01 for ADD.
+ emit_modrm(rm_reg, reg);
+ } else {
+ emit_optional_rex_32(reg, rm_reg);
+ emit(opcode);
+ emit_modrm(reg, rm_reg);
+ }
+}
+
+
+void Assembler::arithmetic_op_32(byte opcode,
+ Register reg,
+ const Operand& rm_reg) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(reg, rm_reg);
+ emit(opcode);
+ emit_operand(reg, rm_reg);
+}
+
+
+void Assembler::immediate_arithmetic_op(byte subcode,
+ Register dst,
+ Immediate src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ if (is_int8(src.value_)) {
+ emit(0x83);
+ emit_modrm(subcode, dst);
+ emit(src.value_);
+ } else if (dst.is(rax)) {
+ emit(0x05 | (subcode << 3));
+ emitl(src.value_);
+ } else {
+ emit(0x81);
+ emit_modrm(subcode, dst);
+ emitl(src.value_);
+ }
+}
+
+void Assembler::immediate_arithmetic_op(byte subcode,
+ const Operand& dst,
+ Immediate src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ if (is_int8(src.value_)) {
+ emit(0x83);
+ emit_operand(subcode, dst);
+ emit(src.value_);
+ } else {
+ emit(0x81);
+ emit_operand(subcode, dst);
+ emitl(src.value_);
+ }
+}
+
+
+void Assembler::immediate_arithmetic_op_16(byte subcode,
+ Register dst,
+ Immediate src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66); // Operand size override prefix.
+ emit_optional_rex_32(dst);
+ if (is_int8(src.value_)) {
+ emit(0x83);
+ emit_modrm(subcode, dst);
+ emit(src.value_);
+ } else if (dst.is(rax)) {
+ emit(0x05 | (subcode << 3));
+ emitw(src.value_);
+ } else {
+ emit(0x81);
+ emit_modrm(subcode, dst);
+ emitw(src.value_);
+ }
+}
+
+
+void Assembler::immediate_arithmetic_op_16(byte subcode,
+ const Operand& dst,
+ Immediate src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66); // Operand size override prefix.
+ emit_optional_rex_32(dst);
+ if (is_int8(src.value_)) {
+ emit(0x83);
+ emit_operand(subcode, dst);
+ emit(src.value_);
+ } else {
+ emit(0x81);
+ emit_operand(subcode, dst);
+ emitw(src.value_);
+ }
+}
+
+
+void Assembler::immediate_arithmetic_op_32(byte subcode,
+ Register dst,
+ Immediate src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ if (is_int8(src.value_)) {
+ emit(0x83);
+ emit_modrm(subcode, dst);
+ emit(src.value_);
+ } else if (dst.is(rax)) {
+ emit(0x05 | (subcode << 3));
+ emitl(src.value_);
+ } else {
+ emit(0x81);
+ emit_modrm(subcode, dst);
+ emitl(src.value_);
+ }
+}
+
+
+void Assembler::immediate_arithmetic_op_32(byte subcode,
+ const Operand& dst,
+ Immediate src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ if (is_int8(src.value_)) {
+ emit(0x83);
+ emit_operand(subcode, dst);
+ emit(src.value_);
+ } else {
+ emit(0x81);
+ emit_operand(subcode, dst);
+ emitl(src.value_);
+ }
+}
+
+
+void Assembler::immediate_arithmetic_op_8(byte subcode,
+ const Operand& dst,
+ Immediate src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ ASSERT(is_int8(src.value_) || is_uint8(src.value_));
+ emit(0x80);
+ emit_operand(subcode, dst);
+ emit(src.value_);
+}
+
+
+void Assembler::immediate_arithmetic_op_8(byte subcode,
+ Register dst,
+ Immediate src) {
+ EnsureSpace ensure_space(this);
+ if (!dst.is_byte_register()) {
+ // Use 64-bit mode byte registers.
+ emit_rex_64(dst);
+ }
+ ASSERT(is_int8(src.value_) || is_uint8(src.value_));
+ emit(0x80);
+ emit_modrm(subcode, dst);
+ emit(src.value_);
+}
+
+
+void Assembler::shift(Register dst, Immediate shift_amount, int subcode) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint6(shift_amount.value_)); // illegal shift count
+ if (shift_amount.value_ == 1) {
+ emit_rex_64(dst);
+ emit(0xD1);
+ emit_modrm(subcode, dst);
+ } else {
+ emit_rex_64(dst);
+ emit(0xC1);
+ emit_modrm(subcode, dst);
+ emit(shift_amount.value_);
+ }
+}
+
+
+void Assembler::shift(Register dst, int subcode) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xD3);
+ emit_modrm(subcode, dst);
+}
+
+
+void Assembler::shift_32(Register dst, int subcode) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xD3);
+ emit_modrm(subcode, dst);
+}
+
+
+void Assembler::shift_32(Register dst, Immediate shift_amount, int subcode) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint5(shift_amount.value_)); // illegal shift count
+ if (shift_amount.value_ == 1) {
+ emit_optional_rex_32(dst);
+ emit(0xD1);
+ emit_modrm(subcode, dst);
+ } else {
+ emit_optional_rex_32(dst);
+ emit(0xC1);
+ emit_modrm(subcode, dst);
+ emit(shift_amount.value_);
+ }
+}
+
+
+void Assembler::bt(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(src, dst);
+ emit(0x0F);
+ emit(0xA3);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::bts(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(src, dst);
+ emit(0x0F);
+ emit(0xAB);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::call(Label* L) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ // 1110 1000 #32-bit disp.
+ emit(0xE8);
+ if (L->is_bound()) {
+ int offset = L->pos() - pc_offset() - sizeof(int32_t);
+ ASSERT(offset <= 0);
+ emitl(offset);
+ } else if (L->is_linked()) {
+ emitl(L->pos());
+ L->link_to(pc_offset() - sizeof(int32_t));
+ } else {
+ ASSERT(L->is_unused());
+ int32_t current = pc_offset();
+ emitl(current);
+ L->link_to(current);
+ }
+}
+
+
+void Assembler::call(Address entry, RelocInfo::Mode rmode) {
+ ASSERT(RelocInfo::IsRuntimeEntry(rmode));
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ // 1110 1000 #32-bit disp.
+ emit(0xE8);
+ emit_runtime_entry(entry, rmode);
+}
+
+
+void Assembler::call(Handle<Code> target,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ // 1110 1000 #32-bit disp.
+ emit(0xE8);
+ emit_code_target(target, rmode, ast_id);
+}
+
+
+void Assembler::call(Register adr) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ // Opcode: FF /2 r64.
+ emit_optional_rex_32(adr);
+ emit(0xFF);
+ emit_modrm(0x2, adr);
+}
+
+
+void Assembler::call(const Operand& op) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ // Opcode: FF /2 m64.
+ emit_optional_rex_32(op);
+ emit(0xFF);
+ emit_operand(0x2, op);
+}
+
+
+// Calls directly to the given address using a relative offset.
+// Should only ever be used in Code objects for calls within the
+// same Code object. Should not be used when generating new code (use labels),
+// but only when patching existing code.
+void Assembler::call(Address target) {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ // 1110 1000 #32-bit disp.
+ emit(0xE8);
+ Address source = pc_ + 4;
+ intptr_t displacement = target - source;
+ ASSERT(is_int32(displacement));
+ emitl(static_cast<int32_t>(displacement));
+}
+
+
+void Assembler::clc() {
+ EnsureSpace ensure_space(this);
+ emit(0xF8);
+}
+
+
+void Assembler::cld() {
+ EnsureSpace ensure_space(this);
+ emit(0xFC);
+}
+
+
+void Assembler::cdq() {
+ EnsureSpace ensure_space(this);
+ emit(0x99);
+}
+
+
+void Assembler::cmovq(Condition cc, Register dst, Register src) {
+ if (cc == always) {
+ movq(dst, src);
+ } else if (cc == never) {
+ return;
+ }
+ // No need to check CpuInfo for CMOV support, it's a required part of the
+ // 64-bit architecture.
+ ASSERT(cc >= 0); // Use mov for unconditional moves.
+ EnsureSpace ensure_space(this);
+ // Opcode: REX.W 0f 40 + cc /r.
+ emit_rex_64(dst, src);
+ emit(0x0f);
+ emit(0x40 + cc);
+ emit_modrm(dst, src);
+}
+
+
+void Assembler::cmovq(Condition cc, Register dst, const Operand& src) {
+ if (cc == always) {
+ movq(dst, src);
+ } else if (cc == never) {
+ return;
+ }
+ ASSERT(cc >= 0);
+ EnsureSpace ensure_space(this);
+ // Opcode: REX.W 0f 40 + cc /r.
+ emit_rex_64(dst, src);
+ emit(0x0f);
+ emit(0x40 + cc);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::cmovl(Condition cc, Register dst, Register src) {
+ if (cc == always) {
+ movl(dst, src);
+ } else if (cc == never) {
+ return;
+ }
+ ASSERT(cc >= 0);
+ EnsureSpace ensure_space(this);
+ // Opcode: 0f 40 + cc /r.
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x40 + cc);
+ emit_modrm(dst, src);
+}
+
+
+void Assembler::cmovl(Condition cc, Register dst, const Operand& src) {
+ if (cc == always) {
+ movl(dst, src);
+ } else if (cc == never) {
+ return;
+ }
+ ASSERT(cc >= 0);
+ EnsureSpace ensure_space(this);
+ // Opcode: 0f 40 + cc /r.
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x40 + cc);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::cmpb_al(Immediate imm8) {
+ ASSERT(is_int8(imm8.value_) || is_uint8(imm8.value_));
+ EnsureSpace ensure_space(this);
+ emit(0x3c);
+ emit(imm8.value_);
+}
+
+
+void Assembler::cpuid() {
+ ASSERT(IsEnabled(CPUID));
+ EnsureSpace ensure_space(this);
+ emit(0x0F);
+ emit(0xA2);
+}
+
+
+void Assembler::cqo() {
+ EnsureSpace ensure_space(this);
+ emit_rex_64();
+ emit(0x99);
+}
+
+
+void Assembler::decq(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xFF);
+ emit_modrm(0x1, dst);
+}
+
+
+void Assembler::decq(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xFF);
+ emit_operand(1, dst);
+}
+
+
+void Assembler::decl(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xFF);
+ emit_modrm(0x1, dst);
+}
+
+
+void Assembler::decl(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xFF);
+ emit_operand(1, dst);
+}
+
+
+void Assembler::decb(Register dst) {
+ EnsureSpace ensure_space(this);
+ if (!dst.is_byte_register()) {
+ // Register is not one of al, bl, cl, dl. Its encoding needs REX.
+ emit_rex_32(dst);
+ }
+ emit(0xFE);
+ emit_modrm(0x1, dst);
+}
+
+
+void Assembler::decb(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xFE);
+ emit_operand(1, dst);
+}
+
+
+void Assembler::enter(Immediate size) {
+ EnsureSpace ensure_space(this);
+ emit(0xC8);
+ emitw(size.value_); // 16 bit operand, always.
+ emit(0);
+}
+
+
+void Assembler::hlt() {
+ EnsureSpace ensure_space(this);
+ emit(0xF4);
+}
+
+
+void Assembler::idivq(Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(src);
+ emit(0xF7);
+ emit_modrm(0x7, src);
+}
+
+
+void Assembler::idivl(Register src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(src);
+ emit(0xF7);
+ emit_modrm(0x7, src);
+}
+
+
+void Assembler::imul(Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(src);
+ emit(0xF7);
+ emit_modrm(0x5, src);
+}
+
+
+void Assembler::imul(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0xAF);
+ emit_modrm(dst, src);
+}
+
+
+void Assembler::imul(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0xAF);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::imul(Register dst, Register src, Immediate imm) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ if (is_int8(imm.value_)) {
+ emit(0x6B);
+ emit_modrm(dst, src);
+ emit(imm.value_);
+ } else {
+ emit(0x69);
+ emit_modrm(dst, src);
+ emitl(imm.value_);
+ }
+}
+
+
+void Assembler::imull(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0xAF);
+ emit_modrm(dst, src);
+}
+
+
+void Assembler::imull(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0xAF);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::imull(Register dst, Register src, Immediate imm) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ if (is_int8(imm.value_)) {
+ emit(0x6B);
+ emit_modrm(dst, src);
+ emit(imm.value_);
+ } else {
+ emit(0x69);
+ emit_modrm(dst, src);
+ emitl(imm.value_);
+ }
+}
+
+
+void Assembler::incq(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xFF);
+ emit_modrm(0x0, dst);
+}
+
+
+void Assembler::incq(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xFF);
+ emit_operand(0, dst);
+}
+
+
+void Assembler::incl(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xFF);
+ emit_operand(0, dst);
+}
+
+
+void Assembler::incl(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xFF);
+ emit_modrm(0, dst);
+}
+
+
+void Assembler::int3() {
+ EnsureSpace ensure_space(this);
+ emit(0xCC);
+}
+
+
+void Assembler::j(Condition cc, Label* L, Label::Distance distance) {
+ if (cc == always) {
+ jmp(L);
+ return;
+ } else if (cc == never) {
+ return;
+ }
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint4(cc));
+ if (L->is_bound()) {
+ const int short_size = 2;
+ const int long_size = 6;
+ int offs = L->pos() - pc_offset();
+ ASSERT(offs <= 0);
+ // Determine whether we can use 1-byte offsets for backwards branches,
+ // which have a max range of 128 bytes.
+
+ // We also need to check predictable_code_size() flag here, because on x64,
+ // when the full code generator recompiles code for debugging, some places
+ // need to be padded out to a certain size. The debugger is keeping track of
+ // how often it did this so that it can adjust return addresses on the
+ // stack, but if the size of jump instructions can also change, that's not
+ // enough and the calculated offsets would be incorrect.
+ if (is_int8(offs - short_size) && !predictable_code_size()) {
+ // 0111 tttn #8-bit disp.
+ emit(0x70 | cc);
+ emit((offs - short_size) & 0xFF);
+ } else {
+ // 0000 1111 1000 tttn #32-bit disp.
+ emit(0x0F);
+ emit(0x80 | cc);
+ emitl(offs - long_size);
+ }
+ } else if (distance == Label::kNear) {
+ // 0111 tttn #8-bit disp
+ emit(0x70 | cc);
+ byte disp = 0x00;
+ if (L->is_near_linked()) {
+ int offset = L->near_link_pos() - pc_offset();
+ ASSERT(is_int8(offset));
+ disp = static_cast<byte>(offset & 0xFF);
+ }
+ L->link_to(pc_offset(), Label::kNear);
+ emit(disp);
+ } else if (L->is_linked()) {
+ // 0000 1111 1000 tttn #32-bit disp.
+ emit(0x0F);
+ emit(0x80 | cc);
+ emitl(L->pos());
+ L->link_to(pc_offset() - sizeof(int32_t));
+ } else {
+ ASSERT(L->is_unused());
+ emit(0x0F);
+ emit(0x80 | cc);
+ int32_t current = pc_offset();
+ emitl(current);
+ L->link_to(current);
+ }
+}
+
+
+void Assembler::j(Condition cc, Address entry, RelocInfo::Mode rmode) {
+ ASSERT(RelocInfo::IsRuntimeEntry(rmode));
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint4(cc));
+ emit(0x0F);
+ emit(0x80 | cc);
+ emit_runtime_entry(entry, rmode);
+}
+
+
+void Assembler::j(Condition cc,
+ Handle<Code> target,
+ RelocInfo::Mode rmode) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint4(cc));
+ // 0000 1111 1000 tttn #32-bit disp.
+ emit(0x0F);
+ emit(0x80 | cc);
+ emit_code_target(target, rmode);
+}
+
+
+void Assembler::jmp(Label* L, Label::Distance distance) {
+ EnsureSpace ensure_space(this);
+ const int short_size = sizeof(int8_t);
+ const int long_size = sizeof(int32_t);
+ if (L->is_bound()) {
+ int offs = L->pos() - pc_offset() - 1;
+ ASSERT(offs <= 0);
+ if (is_int8(offs - short_size) && !predictable_code_size()) {
+ // 1110 1011 #8-bit disp.
+ emit(0xEB);
+ emit((offs - short_size) & 0xFF);
+ } else {
+ // 1110 1001 #32-bit disp.
+ emit(0xE9);
+ emitl(offs - long_size);
+ }
+ } else if (distance == Label::kNear) {
+ emit(0xEB);
+ byte disp = 0x00;
+ if (L->is_near_linked()) {
+ int offset = L->near_link_pos() - pc_offset();
+ ASSERT(is_int8(offset));
+ disp = static_cast<byte>(offset & 0xFF);
+ }
+ L->link_to(pc_offset(), Label::kNear);
+ emit(disp);
+ } else if (L->is_linked()) {
+ // 1110 1001 #32-bit disp.
+ emit(0xE9);
+ emitl(L->pos());
+ L->link_to(pc_offset() - long_size);
+ } else {
+ // 1110 1001 #32-bit disp.
+ ASSERT(L->is_unused());
+ emit(0xE9);
+ int32_t current = pc_offset();
+ emitl(current);
+ L->link_to(current);
+ }
+}
+
+
+void Assembler::jmp(Handle<Code> target, RelocInfo::Mode rmode) {
+ EnsureSpace ensure_space(this);
+ // 1110 1001 #32-bit disp.
+ emit(0xE9);
+ emit_code_target(target, rmode);
+}
+
+
+void Assembler::jmp(Address entry, RelocInfo::Mode rmode) {
+ ASSERT(RelocInfo::IsRuntimeEntry(rmode));
+ EnsureSpace ensure_space(this);
+ ASSERT(RelocInfo::IsRuntimeEntry(rmode));
+ emit(0xE9);
+ emit_runtime_entry(entry, rmode);
+}
+
+
+void Assembler::jmp(Register target) {
+ EnsureSpace ensure_space(this);
+ // Opcode FF/4 r64.
+ emit_optional_rex_32(target);
+ emit(0xFF);
+ emit_modrm(0x4, target);
+}
+
+
+void Assembler::jmp(const Operand& src) {
+ EnsureSpace ensure_space(this);
+ // Opcode FF/4 m64.
+ emit_optional_rex_32(src);
+ emit(0xFF);
+ emit_operand(0x4, src);
+}
+
+
+void Assembler::lea(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ emit(0x8D);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::leal(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x8D);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::load_rax(void* value, RelocInfo::Mode mode) {
+ EnsureSpace ensure_space(this);
+ emit(0x48); // REX.W
+ emit(0xA1);
+ emitp(value, mode);
+}
+
+
+void Assembler::load_rax(ExternalReference ref) {
+ load_rax(ref.address(), RelocInfo::EXTERNAL_REFERENCE);
+}
+
+
+void Assembler::leave() {
+ EnsureSpace ensure_space(this);
+ emit(0xC9);
+}
+
+
+void Assembler::movb(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ if (!dst.is_byte_register()) {
+ // Register is not one of al, bl, cl, dl. Its encoding needs REX.
+ emit_rex_32(dst, src);
+ } else {
+ emit_optional_rex_32(dst, src);
+ }
+ emit(0x8A);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movb(Register dst, Immediate imm) {
+ EnsureSpace ensure_space(this);
+ if (!dst.is_byte_register()) {
+ emit_rex_32(dst);
+ }
+ emit(0xB0 + dst.low_bits());
+ emit(imm.value_);
+}
+
+
+void Assembler::movb(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ if (!src.is_byte_register()) {
+ emit_rex_32(src, dst);
+ } else {
+ emit_optional_rex_32(src, dst);
+ }
+ emit(0x88);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::movw(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(src, dst);
+ emit(0x89);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::movl(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x8B);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movl(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ emit_optional_rex_32(src, dst);
+ emit(0x89);
+ emit_modrm(src, dst);
+ } else {
+ emit_optional_rex_32(dst, src);
+ emit(0x8B);
+ emit_modrm(dst, src);
+ }
+}
+
+
+void Assembler::movl(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(src, dst);
+ emit(0x89);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::movl(const Operand& dst, Immediate value) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xC7);
+ emit_operand(0x0, dst);
+ emit(value);
+}
+
+
+void Assembler::movl(Register dst, Immediate value) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xB8 + dst.low_bits());
+ emit(value);
+}
+
+
+void Assembler::movq(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ emit(0x8B);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movq(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ emit_rex_64(src, dst);
+ emit(0x89);
+ emit_modrm(src, dst);
+ } else {
+ emit_rex_64(dst, src);
+ emit(0x8B);
+ emit_modrm(dst, src);
+ }
+}
+
+
+void Assembler::movq(Register dst, Immediate value) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xC7);
+ emit_modrm(0x0, dst);
+ emit(value); // Only 32-bit immediates are possible, not 8-bit immediates.
+}
+
+
+void Assembler::movq(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(src, dst);
+ emit(0x89);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::movq(Register dst, void* value, RelocInfo::Mode rmode) {
+ // This method must not be used with heap object references. The stored
+ // address is not GC safe. Use the handle version instead.
+ ASSERT(rmode > RelocInfo::LAST_GCED_ENUM);
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xB8 | dst.low_bits());
+ emitp(value, rmode);
+}
+
+
+void Assembler::movq(Register dst, int64_t value, RelocInfo::Mode rmode) {
+ // Non-relocatable values might not need a 64-bit representation.
+ if (RelocInfo::IsNone(rmode)) {
+ if (is_uint32(value)) {
+ movl(dst, Immediate(static_cast<int32_t>(value)));
+ return;
+ } else if (is_int32(value)) {
+ movq(dst, Immediate(static_cast<int32_t>(value)));
+ return;
+ }
+ // Value cannot be represented by 32 bits, so do a full 64 bit immediate
+ // value.
+ }
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xB8 | dst.low_bits());
+ emitq(value, rmode);
+}
+
+
+void Assembler::movq(Register dst, ExternalReference ref) {
+ int64_t value = reinterpret_cast<int64_t>(ref.address());
+ movq(dst, value, RelocInfo::EXTERNAL_REFERENCE);
+}
+
+
+void Assembler::movq(const Operand& dst, Immediate value) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xC7);
+ emit_operand(0, dst);
+ emit(value);
+}
+
+
+// Loads the ip-relative location of the src label into the target location
+// (as a 32-bit offset sign extended to 64-bit).
+void Assembler::movl(const Operand& dst, Label* src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xC7);
+ emit_operand(0, dst);
+ if (src->is_bound()) {
+ int offset = src->pos() - pc_offset() - sizeof(int32_t);
+ ASSERT(offset <= 0);
+ emitl(offset);
+ } else if (src->is_linked()) {
+ emitl(src->pos());
+ src->link_to(pc_offset() - sizeof(int32_t));
+ } else {
+ ASSERT(src->is_unused());
+ int32_t current = pc_offset();
+ emitl(current);
+ src->link_to(current);
+ }
+}
+
+
+void Assembler::movq(Register dst, Handle<Object> value, RelocInfo::Mode mode) {
+ AllowDeferredHandleDereference using_raw_address;
+ // If there is no relocation info, emit the value of the handle efficiently
+ // (possibly using less that 8 bytes for the value).
+ if (RelocInfo::IsNone(mode)) {
+ // There is no possible reason to store a heap pointer without relocation
+ // info, so it must be a smi.
+ ASSERT(value->IsSmi());
+ movq(dst, reinterpret_cast<int64_t>(*value), RelocInfo::NONE64);
+ } else {
+ EnsureSpace ensure_space(this);
+ ASSERT(value->IsHeapObject());
+ ASSERT(!HEAP->InNewSpace(*value));
+ emit_rex_64(dst);
+ emit(0xB8 | dst.low_bits());
+ emitp(value.location(), mode);
+ }
+}
+
+
+void Assembler::movsxbq(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0xBE);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movsxwq(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0xBF);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movsxlq(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ emit(0x63);
+ emit_modrm(dst, src);
+}
+
+
+void Assembler::movsxlq(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst, src);
+ emit(0x63);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movzxbq(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ // 32 bit operations zero the top 32 bits of 64 bit registers. Therefore
+ // there is no need to make this a 64 bit operation.
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0xB6);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movzxbl(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0xB6);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movzxwq(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0xB7);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movzxwl(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0xB7);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::movzxwl(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0xB7);
+ emit_modrm(dst, src);
+}
+
+
+void Assembler::repmovsb() {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit(0xA4);
+}
+
+
+void Assembler::repmovsw() {
+ EnsureSpace ensure_space(this);
+ emit(0x66); // Operand size override.
+ emit(0xF3);
+ emit(0xA4);
+}
+
+
+void Assembler::repmovsl() {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit(0xA5);
+}
+
+
+void Assembler::repmovsq() {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit_rex_64();
+ emit(0xA5);
+}
+
+
+void Assembler::mul(Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(src);
+ emit(0xF7);
+ emit_modrm(0x4, src);
+}
+
+
+void Assembler::neg(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xF7);
+ emit_modrm(0x3, dst);
+}
+
+
+void Assembler::negl(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xF7);
+ emit_modrm(0x3, dst);
+}
+
+
+void Assembler::neg(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xF7);
+ emit_operand(3, dst);
+}
+
+
+void Assembler::nop() {
+ EnsureSpace ensure_space(this);
+ emit(0x90);
+}
+
+
+void Assembler::not_(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xF7);
+ emit_modrm(0x2, dst);
+}
+
+
+void Assembler::not_(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(dst);
+ emit(0xF7);
+ emit_operand(2, dst);
+}
+
+
+void Assembler::notl(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0xF7);
+ emit_modrm(0x2, dst);
+}
+
+
+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.
+ //
+ // Length Assembly Byte Sequence
+ // 2 bytes 66 NOP 66 90H
+ // 3 bytes NOP DWORD ptr [EAX] 0F 1F 00H
+ // 4 bytes NOP DWORD ptr [EAX + 00H] 0F 1F 40 00H
+ // 5 bytes NOP DWORD ptr [EAX + EAX*1 + 00H] 0F 1F 44 00 00H
+ // 6 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00H] 66 0F 1F 44 00 00H
+ // 7 bytes NOP DWORD ptr [EAX + 00000000H] 0F 1F 80 00 00 00 00H
+ // 8 bytes NOP DWORD ptr [EAX + EAX*1 + 00000000H] 0F 1F 84 00 00 00 00 00H
+ // 9 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 66 0F 1F 84 00 00 00 00
+ // 00000000H] 00H
+
+ EnsureSpace ensure_space(this);
+ 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;
+ }
+ }
+}
+
+
+void Assembler::pop(Register dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0x58 | dst.low_bits());
+}
+
+
+void Assembler::pop(const Operand& dst) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst);
+ emit(0x8F);
+ emit_operand(0, dst);
+}
+
+
+void Assembler::popfq() {
+ EnsureSpace ensure_space(this);
+ emit(0x9D);
+}
+
+
+void Assembler::push(Register src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(src);
+ emit(0x50 | src.low_bits());
+}
+
+
+void Assembler::push(const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(src);
+ emit(0xFF);
+ emit_operand(6, src);
+}
+
+
+void Assembler::push(Immediate value) {
+ EnsureSpace ensure_space(this);
+ if (is_int8(value.value_)) {
+ emit(0x6A);
+ emit(value.value_); // Emit low byte of value.
+ } else {
+ emit(0x68);
+ emitl(value.value_);
+ }
+}
+
+
+void Assembler::push_imm32(int32_t imm32) {
+ EnsureSpace ensure_space(this);
+ emit(0x68);
+ emitl(imm32);
+}
+
+
+void Assembler::pushfq() {
+ EnsureSpace ensure_space(this);
+ emit(0x9C);
+}
+
+
+void Assembler::rdtsc() {
+ EnsureSpace ensure_space(this);
+ emit(0x0F);
+ emit(0x31);
+}
+
+
+void Assembler::ret(int imm16) {
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint16(imm16));
+ if (imm16 == 0) {
+ emit(0xC3);
+ } else {
+ emit(0xC2);
+ emit(imm16 & 0xFF);
+ emit((imm16 >> 8) & 0xFF);
+ }
+}
+
+
+void Assembler::setcc(Condition cc, Register reg) {
+ if (cc > last_condition) {
+ movb(reg, Immediate(cc == always ? 1 : 0));
+ return;
+ }
+ EnsureSpace ensure_space(this);
+ ASSERT(is_uint4(cc));
+ if (!reg.is_byte_register()) { // Use x64 byte registers, where different.
+ emit_rex_32(reg);
+ }
+ emit(0x0F);
+ emit(0x90 | cc);
+ emit_modrm(0x0, reg);
+}
+
+
+void Assembler::shld(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(src, dst);
+ emit(0x0F);
+ emit(0xA5);
+ emit_modrm(src, dst);
+}
+
+
+void Assembler::shrd(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(src, dst);
+ emit(0x0F);
+ emit(0xAD);
+ emit_modrm(src, dst);
+}
+
+
+void Assembler::xchg(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ if (src.is(rax) || dst.is(rax)) { // Single-byte encoding
+ Register other = src.is(rax) ? dst : src;
+ emit_rex_64(other);
+ emit(0x90 | other.low_bits());
+ } else if (dst.low_bits() == 4) {
+ emit_rex_64(dst, src);
+ emit(0x87);
+ emit_modrm(dst, src);
+ } else {
+ emit_rex_64(src, dst);
+ emit(0x87);
+ emit_modrm(src, dst);
+ }
+}
+
+
+void Assembler::store_rax(void* dst, RelocInfo::Mode mode) {
+ EnsureSpace ensure_space(this);
+ emit(0x48); // REX.W
+ emit(0xA3);
+ emitp(dst, mode);
+}
+
+
+void Assembler::store_rax(ExternalReference ref) {
+ store_rax(ref.address(), RelocInfo::EXTERNAL_REFERENCE);
+}
+
+
+void Assembler::testb(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ emit_rex_32(src, dst);
+ emit(0x84);
+ emit_modrm(src, dst);
+ } else {
+ if (!dst.is_byte_register() || !src.is_byte_register()) {
+ // Register is not one of al, bl, cl, dl. Its encoding needs REX.
+ emit_rex_32(dst, src);
+ }
+ emit(0x84);
+ emit_modrm(dst, src);
+ }
+}
+
+
+void Assembler::testb(Register reg, Immediate mask) {
+ ASSERT(is_int8(mask.value_) || is_uint8(mask.value_));
+ EnsureSpace ensure_space(this);
+ if (reg.is(rax)) {
+ emit(0xA8);
+ emit(mask.value_); // Low byte emitted.
+ } else {
+ if (!reg.is_byte_register()) {
+ // Register is not one of al, bl, cl, dl. Its encoding needs REX.
+ emit_rex_32(reg);
+ }
+ emit(0xF6);
+ emit_modrm(0x0, reg);
+ emit(mask.value_); // Low byte emitted.
+ }
+}
+
+
+void Assembler::testb(const Operand& op, Immediate mask) {
+ ASSERT(is_int8(mask.value_) || is_uint8(mask.value_));
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(rax, op);
+ emit(0xF6);
+ emit_operand(rax, op); // Operation code 0
+ emit(mask.value_); // Low byte emitted.
+}
+
+
+void Assembler::testb(const Operand& op, Register reg) {
+ EnsureSpace ensure_space(this);
+ if (!reg.is_byte_register()) {
+ // Register is not one of al, bl, cl, dl. Its encoding needs REX.
+ emit_rex_32(reg, op);
+ } else {
+ emit_optional_rex_32(reg, op);
+ }
+ emit(0x84);
+ emit_operand(reg, op);
+}
+
+
+void Assembler::testl(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ emit_optional_rex_32(src, dst);
+ emit(0x85);
+ emit_modrm(src, dst);
+ } else {
+ emit_optional_rex_32(dst, src);
+ emit(0x85);
+ emit_modrm(dst, src);
+ }
+}
+
+
+void Assembler::testl(Register reg, Immediate mask) {
+ // testl with a mask that fits in the low byte is exactly testb.
+ if (is_uint8(mask.value_)) {
+ testb(reg, mask);
+ return;
+ }
+ EnsureSpace ensure_space(this);
+ if (reg.is(rax)) {
+ emit(0xA9);
+ emit(mask);
+ } else {
+ emit_optional_rex_32(rax, reg);
+ emit(0xF7);
+ emit_modrm(0x0, reg);
+ emit(mask);
+ }
+}
+
+
+void Assembler::testl(const Operand& op, Immediate mask) {
+ // testl with a mask that fits in the low byte is exactly testb.
+ if (is_uint8(mask.value_)) {
+ testb(op, mask);
+ return;
+ }
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(rax, op);
+ emit(0xF7);
+ emit_operand(rax, op); // Operation code 0
+ emit(mask);
+}
+
+
+void Assembler::testq(const Operand& op, Register reg) {
+ EnsureSpace ensure_space(this);
+ emit_rex_64(reg, op);
+ emit(0x85);
+ emit_operand(reg, op);
+}
+
+
+void Assembler::testq(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ emit_rex_64(src, dst);
+ emit(0x85);
+ emit_modrm(src, dst);
+ } else {
+ emit_rex_64(dst, src);
+ emit(0x85);
+ emit_modrm(dst, src);
+ }
+}
+
+
+void Assembler::testq(Register dst, Immediate mask) {
+ EnsureSpace ensure_space(this);
+ if (dst.is(rax)) {
+ emit_rex_64();
+ emit(0xA9);
+ emit(mask);
+ } else {
+ emit_rex_64(dst);
+ emit(0xF7);
+ emit_modrm(0, dst);
+ emit(mask);
+ }
+}
+
+
+// FPU instructions.
+
+
+void Assembler::fld(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xD9, 0xC0, i);
+}
+
+
+void Assembler::fld1() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xE8);
+}
+
+
+void Assembler::fldz() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xEE);
+}
+
+
+void Assembler::fldpi() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xEB);
+}
+
+
+void Assembler::fldln2() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xED);
+}
+
+
+void Assembler::fld_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xD9);
+ emit_operand(0, adr);
+}
+
+
+void Assembler::fld_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDD);
+ emit_operand(0, adr);
+}
+
+
+void Assembler::fstp_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xD9);
+ emit_operand(3, adr);
+}
+
+
+void Assembler::fstp_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDD);
+ emit_operand(3, adr);
+}
+
+
+void Assembler::fstp(int index) {
+ ASSERT(is_uint3(index));
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDD, 0xD8, index);
+}
+
+
+void Assembler::fild_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDB);
+ emit_operand(0, adr);
+}
+
+
+void Assembler::fild_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDF);
+ emit_operand(5, adr);
+}
+
+
+void Assembler::fistp_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDB);
+ emit_operand(3, adr);
+}
+
+
+void Assembler::fisttp_s(const Operand& adr) {
+ ASSERT(IsEnabled(SSE3));
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDB);
+ emit_operand(1, adr);
+}
+
+
+void Assembler::fisttp_d(const Operand& adr) {
+ ASSERT(IsEnabled(SSE3));
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDD);
+ emit_operand(1, adr);
+}
+
+
+void Assembler::fist_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDB);
+ emit_operand(2, adr);
+}
+
+
+void Assembler::fistp_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDF);
+ emit_operand(7, adr);
+}
+
+
+void Assembler::fabs() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xE1);
+}
+
+
+void Assembler::fchs() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xE0);
+}
+
+
+void Assembler::fcos() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xFF);
+}
+
+
+void Assembler::fsin() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xFE);
+}
+
+
+void Assembler::fptan() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xF2);
+}
+
+
+void Assembler::fyl2x() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xF1);
+}
+
+
+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);
+}
+
+
+void Assembler::fsub(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDC, 0xE8, i);
+}
+
+
+void Assembler::fisub_s(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(adr);
+ emit(0xDA);
+ emit_operand(4, adr);
+}
+
+
+void Assembler::fmul(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDC, 0xC8, i);
+}
+
+
+void Assembler::fdiv(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDC, 0xF8, i);
+}
+
+
+void Assembler::faddp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xC0, i);
+}
+
+
+void Assembler::fsubp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xE8, i);
+}
+
+
+void Assembler::fsubrp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xE0, i);
+}
+
+
+void Assembler::fmulp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xC8, i);
+}
+
+
+void Assembler::fdivp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDE, 0xF8, i);
+}
+
+
+void Assembler::fprem() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xF8);
+}
+
+
+void Assembler::fprem1() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xF5);
+}
+
+
+void Assembler::fxch(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xD9, 0xC8, i);
+}
+
+
+void Assembler::fincstp() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xF7);
+}
+
+
+void Assembler::ffree(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDD, 0xC0, i);
+}
+
+
+void Assembler::ftst() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xE4);
+}
+
+
+void Assembler::fucomp(int i) {
+ EnsureSpace ensure_space(this);
+ emit_farith(0xDD, 0xE8, i);
+}
+
+
+void Assembler::fucompp() {
+ EnsureSpace ensure_space(this);
+ emit(0xDA);
+ emit(0xE9);
+}
+
+
+void Assembler::fucomi(int i) {
+ EnsureSpace ensure_space(this);
+ emit(0xDB);
+ emit(0xE8 + i);
+}
+
+
+void Assembler::fucomip() {
+ EnsureSpace ensure_space(this);
+ emit(0xDF);
+ emit(0xE9);
+}
+
+
+void Assembler::fcompp() {
+ EnsureSpace ensure_space(this);
+ emit(0xDE);
+ emit(0xD9);
+}
+
+
+void Assembler::fnstsw_ax() {
+ EnsureSpace ensure_space(this);
+ emit(0xDF);
+ emit(0xE0);
+}
+
+
+void Assembler::fwait() {
+ EnsureSpace ensure_space(this);
+ emit(0x9B);
+}
+
+
+void Assembler::frndint() {
+ EnsureSpace ensure_space(this);
+ emit(0xD9);
+ emit(0xFC);
+}
+
+
+void Assembler::fnclex() {
+ EnsureSpace ensure_space(this);
+ emit(0xDB);
+ emit(0xE2);
+}
+
+
+void Assembler::sahf() {
+ // TODO(X64): Test for presence. Not all 64-bit intel CPU's have sahf
+ // in 64-bit mode. Test CpuID.
+ EnsureSpace ensure_space(this);
+ emit(0x9E);
+}
+
+
+void Assembler::emit_farith(int b1, int b2, int i) {
+ ASSERT(is_uint8(b1) && is_uint8(b2)); // wrong opcode
+ ASSERT(is_uint3(i)); // illegal stack offset
+ emit(b1);
+ emit(b2 + i);
+}
+
+
+// SSE 2 operations.
+
+void Assembler::movd(XMMRegister dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x6E);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movd(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0x7E);
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movq(XMMRegister dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0x6E);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movq(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_rex_64(src, dst);
+ emit(0x0F);
+ emit(0x7E);
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movq(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ if (dst.low_bits() == 4) {
+ // Avoid unnecessary SIB byte.
+ emit(0xf3);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x7e);
+ emit_sse_operand(dst, src);
+ } else {
+ emit(0x66);
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0xD6);
+ emit_sse_operand(src, dst);
+ }
+}
+
+
+void Assembler::movdqa(const Operand& dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_rex_64(src, dst);
+ emit(0x0F);
+ emit(0x7F);
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movdqa(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0x6F);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movdqu(const Operand& dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit_rex_64(src, dst);
+ emit(0x0F);
+ emit(0x7F);
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movdqu(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0x6F);
+ emit_sse_operand(dst, 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_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x3A);
+ emit(0x17);
+ emit_sse_operand(dst, src);
+ emit(imm8);
+}
+
+
+void Assembler::movsd(const Operand& dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2); // double
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0x11); // store
+ emit_sse_operand(src, dst);
+}
+
+
+void Assembler::movsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2); // double
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x10); // load
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movsd(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2); // double
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x10); // load
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movaps(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ // Try to avoid an unnecessary SIB byte.
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0x29);
+ emit_sse_operand(src, dst);
+ } else {
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x28);
+ emit_sse_operand(dst, src);
+ }
+}
+
+
+void Assembler::movapd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ // Try to avoid an unnecessary SIB byte.
+ emit(0x66);
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0x29);
+ emit_sse_operand(src, dst);
+ } else {
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x28);
+ emit_sse_operand(dst, src);
+ }
+}
+
+
+void Assembler::movss(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3); // single
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x10); // load
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movss(const Operand& src, XMMRegister dst) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3); // single
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x11); // store
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvttss2si(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x2C);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::cvttss2si(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x2C);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvttsd2si(Register dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x2C);
+ emit_operand(dst, src);
+}
+
+
+void Assembler::cvttsd2si(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x2C);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvttsd2siq(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0x2C);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtlsi2sd(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x2A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtlsi2sd(XMMRegister dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x2A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtlsi2ss(XMMRegister dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x2A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtqsi2sd(XMMRegister dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0x2A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x5A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtss2sd(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF3);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x5A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x5A);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtsd2si(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x2D);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::cvtsd2siq(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_rex_64(dst, src);
+ emit(0x0F);
+ emit(0x2D);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::addsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x58);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::addsd(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x58);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x59);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::mulsd(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x59);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::subsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x5C);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::divsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x5E);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::andpd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x54);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::orpd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x56);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::xorpd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x57);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::xorps(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x57);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0xF2);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x51);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x2e);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::ucomisd(XMMRegister dst, const Operand& src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x2e);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::roundsd(XMMRegister dst, XMMRegister src,
+ Assembler::RoundingMode mode) {
+ ASSERT(IsEnabled(SSE4_1));
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x3a);
+ emit(0x0b);
+ emit_sse_operand(dst, src);
+ // Mask precision exeption.
+ emit(static_cast<byte>(mode) | 0x8);
+}
+
+
+void Assembler::movmskpd(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x50);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movmskps(Register dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x50);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) {
+ Register ireg = { reg.code() };
+ emit_operand(ireg, adr);
+}
+
+
+void Assembler::emit_sse_operand(XMMRegister dst, XMMRegister src) {
+ emit(0xC0 | (dst.low_bits() << 3) | src.low_bits());
+}
+
+
+void Assembler::emit_sse_operand(XMMRegister dst, Register src) {
+ emit(0xC0 | (dst.low_bits() << 3) | src.low_bits());
+}
+
+
+void Assembler::emit_sse_operand(Register dst, XMMRegister src) {
+ emit(0xC0 | (dst.low_bits() << 3) | src.low_bits());
+}
+
+
+void Assembler::db(uint8_t data) {
+ EnsureSpace ensure_space(this);
+ emit(data);
+}
+
+
+void Assembler::dd(uint32_t data) {
+ EnsureSpace ensure_space(this);
+ emitl(data);
+}
+
+
+// Relocation information implementations.
+
+void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
+ ASSERT(!RelocInfo::IsNone(rmode));
+ // Don't record external references unless the heap will be serialized.
+ if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
+#ifdef DEBUG
+ if (!Serializer::enabled()) {
+ Serializer::TooLateToEnableNow();
+ }
+#endif
+ if (!Serializer::enabled() && !emit_debug_code()) {
+ return;
+ }
+ }
+ RelocInfo rinfo(pc_, rmode, data, NULL);
+ reloc_info_writer.Write(&rinfo);
+}
+
+
+void Assembler::RecordJSReturn() {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::JS_RETURN);
+}
+
+
+void Assembler::RecordDebugBreakSlot() {
+ positions_recorder()->WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
+void Assembler::RecordComment(const char* msg, bool force) {
+ if (FLAG_code_comments || force) {
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg));
+ }
+}
+
+
+const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask |
+ 1 << RelocInfo::RUNTIME_ENTRY |
+ 1 << RelocInfo::INTERNAL_REFERENCE |
+ 1 << RelocInfo::CODE_AGE_SEQUENCE;
+
+
+bool RelocInfo::IsCodedSpecially() {
+ // The deserializer needs to know whether a pointer is specially coded. Being
+ // specially coded on x64 means that it is a relative 32 bit address, as used
+ // by branch instructions.
+ return (1 << rmode_) & kApplyMask;
+}
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/assembler-x64.h b/chromium/v8/src/x64/assembler-x64.h
new file mode 100644
index 00000000000..4e36b6e4bc4
--- /dev/null
+++ b/chromium/v8/src/x64/assembler-x64.h
@@ -0,0 +1,1668 @@
+// Copyright (c) 1994-2006 Sun Microsystems 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.
+//
+// - Redistribution 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 Sun Microsystems or the names of 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.
+
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+// A lightweight X64 Assembler.
+
+#ifndef V8_X64_ASSEMBLER_X64_H_
+#define V8_X64_ASSEMBLER_X64_H_
+
+#include "serialize.h"
+
+namespace v8 {
+namespace internal {
+
+// Utility functions
+
+// Test whether a 64-bit value is in a specific range.
+inline bool is_uint32(int64_t x) {
+ static const uint64_t kMaxUInt32 = V8_UINT64_C(0xffffffff);
+ return static_cast<uint64_t>(x) <= kMaxUInt32;
+}
+
+inline bool is_int32(int64_t x) {
+ static const int64_t kMinInt32 = -V8_INT64_C(0x80000000);
+ return is_uint32(x - kMinInt32);
+}
+
+inline bool uint_is_int32(uint64_t x) {
+ static const uint64_t kMaxInt32 = V8_UINT64_C(0x7fffffff);
+ return x <= kMaxInt32;
+}
+
+inline bool is_uint32(uint64_t x) {
+ static const uint64_t kMaxUInt32 = V8_UINT64_C(0xffffffff);
+ return x <= kMaxUInt32;
+}
+
+// CPU Registers.
+//
+// 1) We would prefer to use an enum, but enum values are assignment-
+// compatible with int, which has caused code-generation bugs.
+//
+// 2) We would prefer to use a class instead of a struct but we don't like
+// the register initialization to depend on the particular initialization
+// order (which appears to be different on OS X, Linux, and Windows for the
+// installed versions of C++ we tried). Using a struct permits C-style
+// "initialization". Also, the Register objects cannot be const as this
+// forces initialization stubs in MSVC, making us dependent on initialization
+// order.
+//
+// 3) By not using an enum, we are possibly preventing the compiler from
+// doing certain constant folds, which may significantly reduce the
+// code generated for some assembly instructions (because they boil down
+// to a few constants). If this is a problem, we could change the code
+// such that we use an enum in optimized mode, and the struct in debug
+// mode. This way we get the compile-time error checking in debug mode
+// and best performance in optimized code.
+//
+
+struct Register {
+ // The non-allocatable registers are:
+ // rsp - stack pointer
+ // rbp - frame pointer
+ // rsi - context register
+ // r10 - fixed scratch register
+ // r12 - smi constant register
+ // r13 - root register
+ static const int kMaxNumAllocatableRegisters = 10;
+ static int NumAllocatableRegisters() {
+ return kMaxNumAllocatableRegisters;
+ }
+ static const int kNumRegisters = 16;
+
+ static int ToAllocationIndex(Register reg) {
+ return kAllocationIndexByRegisterCode[reg.code()];
+ }
+
+ static Register FromAllocationIndex(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ Register result = { kRegisterCodeByAllocationIndex[index] };
+ return result;
+ }
+
+ static const char* AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ const char* const names[] = {
+ "rax",
+ "rbx",
+ "rdx",
+ "rcx",
+ "rdi",
+ "r8",
+ "r9",
+ "r11",
+ "r14",
+ "r15"
+ };
+ return names[index];
+ }
+
+ static Register from_code(int code) {
+ Register r = { code };
+ return r;
+ }
+ bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; }
+ bool is(Register reg) const { return code_ == reg.code_; }
+ // rax, rbx, rcx and rdx are byte registers, the rest are not.
+ bool is_byte_register() const { return code_ <= 3; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+ int bit() const {
+ return 1 << code_;
+ }
+
+ // Return the high bit of the register code as a 0 or 1. Used often
+ // when constructing the REX prefix byte.
+ int high_bit() const {
+ return code_ >> 3;
+ }
+ // Return the 3 low bits of the register code. Used when encoding registers
+ // in modR/M, SIB, and opcode bytes.
+ int low_bits() const {
+ return code_ & 0x7;
+ }
+
+ // Unfortunately we can't make this private in a struct when initializing
+ // by assignment.
+ int code_;
+
+ private:
+ static const int kRegisterCodeByAllocationIndex[kMaxNumAllocatableRegisters];
+ static const int kAllocationIndexByRegisterCode[kNumRegisters];
+};
+
+const int kRegister_rax_Code = 0;
+const int kRegister_rcx_Code = 1;
+const int kRegister_rdx_Code = 2;
+const int kRegister_rbx_Code = 3;
+const int kRegister_rsp_Code = 4;
+const int kRegister_rbp_Code = 5;
+const int kRegister_rsi_Code = 6;
+const int kRegister_rdi_Code = 7;
+const int kRegister_r8_Code = 8;
+const int kRegister_r9_Code = 9;
+const int kRegister_r10_Code = 10;
+const int kRegister_r11_Code = 11;
+const int kRegister_r12_Code = 12;
+const int kRegister_r13_Code = 13;
+const int kRegister_r14_Code = 14;
+const int kRegister_r15_Code = 15;
+const int kRegister_no_reg_Code = -1;
+
+const Register rax = { kRegister_rax_Code };
+const Register rcx = { kRegister_rcx_Code };
+const Register rdx = { kRegister_rdx_Code };
+const Register rbx = { kRegister_rbx_Code };
+const Register rsp = { kRegister_rsp_Code };
+const Register rbp = { kRegister_rbp_Code };
+const Register rsi = { kRegister_rsi_Code };
+const Register rdi = { kRegister_rdi_Code };
+const Register r8 = { kRegister_r8_Code };
+const Register r9 = { kRegister_r9_Code };
+const Register r10 = { kRegister_r10_Code };
+const Register r11 = { kRegister_r11_Code };
+const Register r12 = { kRegister_r12_Code };
+const Register r13 = { kRegister_r13_Code };
+const Register r14 = { kRegister_r14_Code };
+const Register r15 = { kRegister_r15_Code };
+const Register no_reg = { kRegister_no_reg_Code };
+
+#ifdef _WIN64
+ // Windows calling convention
+ const Register arg_reg_1 = { kRegister_rcx_Code };
+ const Register arg_reg_2 = { kRegister_rdx_Code };
+ const Register arg_reg_3 = { kRegister_r8_Code };
+ const Register arg_reg_4 = { kRegister_r9_Code };
+#else
+ // AMD64 calling convention
+ const Register arg_reg_1 = { kRegister_rdi_Code };
+ const Register arg_reg_2 = { kRegister_rsi_Code };
+ const Register arg_reg_3 = { kRegister_rdx_Code };
+ const Register arg_reg_4 = { kRegister_rcx_Code };
+#endif // _WIN64
+
+struct XMMRegister {
+ static const int kMaxNumRegisters = 16;
+ static const int kMaxNumAllocatableRegisters = 15;
+ static int NumAllocatableRegisters() {
+ return kMaxNumAllocatableRegisters;
+ }
+
+ static int ToAllocationIndex(XMMRegister reg) {
+ ASSERT(reg.code() != 0);
+ return reg.code() - 1;
+ }
+
+ static XMMRegister FromAllocationIndex(int index) {
+ ASSERT(0 <= index && index < kMaxNumAllocatableRegisters);
+ XMMRegister result = { index + 1 };
+ return result;
+ }
+
+ static const char* AllocationIndexToString(int index) {
+ ASSERT(index >= 0 && index < kMaxNumAllocatableRegisters);
+ const char* const names[] = {
+ "xmm1",
+ "xmm2",
+ "xmm3",
+ "xmm4",
+ "xmm5",
+ "xmm6",
+ "xmm7",
+ "xmm8",
+ "xmm9",
+ "xmm10",
+ "xmm11",
+ "xmm12",
+ "xmm13",
+ "xmm14",
+ "xmm15"
+ };
+ return names[index];
+ }
+
+ static XMMRegister from_code(int code) {
+ ASSERT(code >= 0);
+ ASSERT(code < kMaxNumRegisters);
+ XMMRegister r = { code };
+ return r;
+ }
+ bool is_valid() const { return 0 <= code_ && code_ < kMaxNumRegisters; }
+ bool is(XMMRegister reg) const { return code_ == reg.code_; }
+ int code() const {
+ ASSERT(is_valid());
+ return code_;
+ }
+
+ // Return the high bit of the register code as a 0 or 1. Used often
+ // when constructing the REX prefix byte.
+ int high_bit() const {
+ return code_ >> 3;
+ }
+ // Return the 3 low bits of the register code. Used when encoding registers
+ // in modR/M, SIB, and opcode bytes.
+ int low_bits() const {
+ return code_ & 0x7;
+ }
+
+ int code_;
+};
+
+const XMMRegister xmm0 = { 0 };
+const XMMRegister xmm1 = { 1 };
+const XMMRegister xmm2 = { 2 };
+const XMMRegister xmm3 = { 3 };
+const XMMRegister xmm4 = { 4 };
+const XMMRegister xmm5 = { 5 };
+const XMMRegister xmm6 = { 6 };
+const XMMRegister xmm7 = { 7 };
+const XMMRegister xmm8 = { 8 };
+const XMMRegister xmm9 = { 9 };
+const XMMRegister xmm10 = { 10 };
+const XMMRegister xmm11 = { 11 };
+const XMMRegister xmm12 = { 12 };
+const XMMRegister xmm13 = { 13 };
+const XMMRegister xmm14 = { 14 };
+const XMMRegister xmm15 = { 15 };
+
+
+typedef XMMRegister DoubleRegister;
+
+
+enum Condition {
+ // any value < 0 is considered no_condition
+ no_condition = -1,
+
+ overflow = 0,
+ no_overflow = 1,
+ below = 2,
+ above_equal = 3,
+ equal = 4,
+ not_equal = 5,
+ below_equal = 6,
+ above = 7,
+ negative = 8,
+ positive = 9,
+ parity_even = 10,
+ parity_odd = 11,
+ less = 12,
+ greater_equal = 13,
+ less_equal = 14,
+ greater = 15,
+
+ // Fake conditions that are handled by the
+ // opcodes using them.
+ always = 16,
+ never = 17,
+ // aliases
+ carry = below,
+ not_carry = above_equal,
+ zero = equal,
+ not_zero = not_equal,
+ sign = negative,
+ not_sign = positive,
+ last_condition = greater
+};
+
+
+// Returns the equivalent of !cc.
+// Negation of the default no_condition (-1) results in a non-default
+// no_condition value (-2). As long as tests for no_condition check
+// for condition < 0, this will work as expected.
+inline Condition NegateCondition(Condition cc) {
+ return static_cast<Condition>(cc ^ 1);
+}
+
+
+// Corresponds to transposing the operands of a comparison.
+inline Condition ReverseCondition(Condition cc) {
+ switch (cc) {
+ case below:
+ return above;
+ case above:
+ return below;
+ case above_equal:
+ return below_equal;
+ case below_equal:
+ return above_equal;
+ case less:
+ return greater;
+ case greater:
+ return less;
+ case greater_equal:
+ return less_equal;
+ case less_equal:
+ return greater_equal;
+ default:
+ return cc;
+ };
+}
+
+
+// -----------------------------------------------------------------------------
+// Machine instruction Immediates
+
+class Immediate BASE_EMBEDDED {
+ public:
+ explicit Immediate(int32_t value) : value_(value) {}
+
+ private:
+ int32_t value_;
+
+ friend class Assembler;
+};
+
+
+// -----------------------------------------------------------------------------
+// Machine instruction Operands
+
+enum ScaleFactor {
+ times_1 = 0,
+ times_2 = 1,
+ times_4 = 2,
+ times_8 = 3,
+ times_int_size = times_4,
+ times_pointer_size = times_8
+};
+
+
+class Operand BASE_EMBEDDED {
+ public:
+ // [base + disp/r]
+ Operand(Register base, int32_t disp);
+
+ // [base + index*scale + disp/r]
+ Operand(Register base,
+ Register index,
+ ScaleFactor scale,
+ int32_t disp);
+
+ // [index*scale + disp/r]
+ Operand(Register index,
+ ScaleFactor scale,
+ int32_t disp);
+
+ // Offset from existing memory operand.
+ // Offset is added to existing displacement as 32-bit signed values and
+ // this must not overflow.
+ Operand(const Operand& base, int32_t offset);
+
+ // Checks whether either base or index register is the given register.
+ // Does not check the "reg" part of the Operand.
+ bool AddressUsesRegister(Register reg) const;
+
+ // Queries related to the size of the generated instruction.
+ // Whether the generated instruction will have a REX prefix.
+ bool requires_rex() const { return rex_ != 0; }
+ // Size of the ModR/M, SIB and displacement parts of the generated
+ // instruction.
+ int operand_size() const { return len_; }
+
+ private:
+ byte rex_;
+ byte buf_[6];
+ // The number of bytes of buf_ in use.
+ byte len_;
+
+ // Set the ModR/M byte without an encoded 'reg' register. The
+ // register is encoded later as part of the emit_operand operation.
+ // set_modrm can be called before or after set_sib and set_disp*.
+ inline void set_modrm(int mod, Register rm);
+
+ // Set the SIB byte if one is needed. Sets the length to 2 rather than 1.
+ inline void set_sib(ScaleFactor scale, Register index, Register base);
+
+ // Adds operand displacement fields (offsets added to the memory address).
+ // Needs to be called after set_sib, not before it.
+ inline void set_disp8(int disp);
+ inline void set_disp32(int disp);
+
+ friend class Assembler;
+};
+
+
+// CpuFeatures keeps track of which features are supported by the target CPU.
+// Supported features must be enabled by a CpuFeatureScope before use.
+// Example:
+// if (assembler->IsSupported(SSE3)) {
+// CpuFeatureScope fscope(assembler, SSE3);
+// // Generate SSE3 floating point code.
+// } else {
+// // Generate standard SSE2 floating point code.
+// }
+class CpuFeatures : public AllStatic {
+ public:
+ // Detect features of the target CPU. Set safe defaults if the serializer
+ // is enabled (snapshots must be portable).
+ static void Probe();
+
+ // Check whether a feature is supported by the target CPU.
+ static bool IsSupported(CpuFeature f) {
+ ASSERT(initialized_);
+ if (f == SSE3 && !FLAG_enable_sse3) return false;
+ if (f == SSE4_1 && !FLAG_enable_sse4_1) return false;
+ if (f == CMOV && !FLAG_enable_cmov) return false;
+ if (f == RDTSC && !FLAG_enable_rdtsc) return false;
+ if (f == SAHF && !FLAG_enable_sahf) return false;
+ return (supported_ & (static_cast<uint64_t>(1) << f)) != 0;
+ }
+
+ static bool IsFoundByRuntimeProbingOnly(CpuFeature f) {
+ ASSERT(initialized_);
+ return (found_by_runtime_probing_only_ &
+ (static_cast<uint64_t>(1) << f)) != 0;
+ }
+
+ static bool IsSafeForSnapshot(CpuFeature f) {
+ return (IsSupported(f) &&
+ (!Serializer::enabled() || !IsFoundByRuntimeProbingOnly(f)));
+ }
+
+ private:
+ // Safe defaults include CMOV for X64. It is always available, if
+ // anyone checks, but they shouldn't need to check.
+ // The required user mode extensions in X64 are (from AMD64 ABI Table A.1):
+ // fpu, tsc, cx8, cmov, mmx, sse, sse2, fxsr, syscall
+ static const uint64_t kDefaultCpuFeatures = (1 << CMOV);
+
+#ifdef DEBUG
+ static bool initialized_;
+#endif
+ static uint64_t supported_;
+ static uint64_t found_by_runtime_probing_only_;
+
+ friend class ExternalReference;
+ DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
+};
+
+
+class Assembler : public AssemblerBase {
+ private:
+ // We check before assembling an instruction that there is sufficient
+ // space to write an instruction and its relocation information.
+ // The relocation writer's position must be kGap bytes above the end of
+ // the generated instructions. This leaves enough space for the
+ // longest possible x64 instruction, 15 bytes, and the longest possible
+ // relocation information encoding, RelocInfoWriter::kMaxLength == 16.
+ // (There is a 15 byte limit on x64 instruction length that rules out some
+ // otherwise valid instructions.)
+ // This allows for a single, fast space check per instruction.
+ static const int kGap = 32;
+
+ public:
+ // Create an assembler. Instructions and relocation information are emitted
+ // into a buffer, with the instructions starting from the beginning and the
+ // relocation information starting from the end of the buffer. See CodeDesc
+ // for a detailed comment on the layout (globals.h).
+ //
+ // If the provided buffer is NULL, the assembler allocates and grows its own
+ // buffer, and buffer_size determines the initial buffer size. The buffer is
+ // owned by the assembler and deallocated upon destruction of the assembler.
+ //
+ // If the provided buffer is not NULL, the assembler uses the provided buffer
+ // for code generation and assumes its size to be buffer_size. If the buffer
+ // is too small, a fatal error occurs. No deallocation of the buffer is done
+ // upon destruction of the assembler.
+ Assembler(Isolate* isolate, void* buffer, int buffer_size);
+ virtual ~Assembler() { }
+
+ // GetCode emits any pending (non-emitted) code and fills the descriptor
+ // desc. GetCode() is idempotent; it returns the same result if no other
+ // Assembler functions are invoked in between GetCode() calls.
+ void GetCode(CodeDesc* desc);
+
+ // Read/Modify the code target in the relative branch/call instruction at pc.
+ // On the x64 architecture, we use relative jumps with a 32-bit displacement
+ // to jump to other Code objects in the Code space in the heap.
+ // Jumps to C functions are done indirectly through a 64-bit register holding
+ // the absolute address of the target.
+ // These functions convert between absolute Addresses of Code objects and
+ // the relative displacements stored in the code.
+ static inline Address target_address_at(Address pc);
+ static inline void set_target_address_at(Address pc, Address target);
+
+ // Return the code target address at a call site from the return address
+ // of that call in the instruction stream.
+ static inline Address target_address_from_return_address(Address pc);
+
+ // This sets the branch destination (which is in the instruction on x64).
+ // This is for calls and branches within generated code.
+ inline static void deserialization_set_special_target_at(
+ Address instruction_payload, Address target) {
+ set_target_address_at(instruction_payload, target);
+ }
+
+ // This sets the branch destination (which is a load instruction on x64).
+ // This is for calls and branches to runtime code.
+ inline static void set_external_target_at(Address instruction_payload,
+ Address target) {
+ *reinterpret_cast<Address*>(instruction_payload) = target;
+ }
+
+ inline Handle<Object> code_target_object_handle_at(Address pc);
+ inline Address runtime_entry_at(Address pc);
+ // Number of bytes taken up by the branch target in the code.
+ static const int kSpecialTargetSize = 4; // Use 32-bit displacement.
+ // Distance between the address of the code target in the call instruction
+ // and the return address pushed on the stack.
+ static const int kCallTargetAddressOffset = 4; // Use 32-bit displacement.
+ // The length of call(kScratchRegister).
+ static const int kCallScratchRegisterInstructionLength = 3;
+ // The length of call(Immediate32).
+ static const int kShortCallInstructionLength = 5;
+ // The length of movq(kScratchRegister, address).
+ static const int kMoveAddressIntoScratchRegisterInstructionLength =
+ 2 + kPointerSize;
+ // The length of movq(kScratchRegister, address) and call(kScratchRegister).
+ static const int kCallSequenceLength =
+ kMoveAddressIntoScratchRegisterInstructionLength +
+ kCallScratchRegisterInstructionLength;
+
+ // The js return and debug break slot must be able to contain an indirect
+ // call sequence, some x64 JS code is padded with int3 to make it large
+ // enough to hold an instruction when the debugger patches it.
+ static const int kJSReturnSequenceLength = kCallSequenceLength;
+ static const int kDebugBreakSlotLength = kCallSequenceLength;
+ static const int kPatchDebugBreakSlotReturnOffset = kCallTargetAddressOffset;
+ // Distance between the start of the JS return sequence and where the
+ // 32-bit displacement of a short call would be. The short call is from
+ // SetDebugBreakAtIC from debug-x64.cc.
+ static const int kPatchReturnSequenceAddressOffset =
+ kJSReturnSequenceLength - kPatchDebugBreakSlotReturnOffset;
+ // Distance between the start of the JS return sequence and where the
+ // 32-bit displacement of a short call would be. The short call is from
+ // SetDebugBreakAtIC from debug-x64.cc.
+ static const int kPatchDebugBreakSlotAddressOffset =
+ kDebugBreakSlotLength - kPatchDebugBreakSlotReturnOffset;
+ static const int kRealPatchReturnSequenceAddressOffset =
+ kMoveAddressIntoScratchRegisterInstructionLength - kPointerSize;
+
+ // 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.
+ static const byte kNopByte = 0x90;
+
+ // One byte prefix for a short conditional jump.
+ static const byte kJccShortPrefix = 0x70;
+ static const byte kJncShortOpcode = kJccShortPrefix | not_carry;
+ static const byte kJcShortOpcode = kJccShortPrefix | carry;
+ static const byte kJnzShortOpcode = kJccShortPrefix | not_zero;
+ static const byte kJzShortOpcode = kJccShortPrefix | zero;
+
+
+ // ---------------------------------------------------------------------------
+ // Code generation
+ //
+ // Function names correspond one-to-one to x64 instruction mnemonics.
+ // Unless specified otherwise, instructions operate on 64-bit operands.
+ //
+ // If we need versions of an assembly instruction that operate on different
+ // width arguments, we add a single-letter suffix specifying the width.
+ // This is done for the following instructions: mov, cmp, inc, dec,
+ // add, sub, and test.
+ // There are no versions of these instructions without the suffix.
+ // - Instructions on 8-bit (byte) operands/registers have a trailing 'b'.
+ // - Instructions on 16-bit (word) operands/registers have a trailing 'w'.
+ // - Instructions on 32-bit (doubleword) operands/registers use 'l'.
+ // - Instructions on 64-bit (quadword) operands/registers use 'q'.
+ //
+ // Some mnemonics, such as "and", are the same as C++ keywords.
+ // Naming conflicts with C++ keywords are resolved by adding a trailing '_'.
+
+ // Insert the smallest number of nop instructions
+ // 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();
+
+ // Stack
+ void pushfq();
+ void popfq();
+
+ void push(Immediate value);
+ // Push a 32 bit integer, and guarantee that it is actually pushed as a
+ // 32 bit value, the normal push will optimize the 8 bit case.
+ void push_imm32(int32_t imm32);
+ void push(Register src);
+ void push(const Operand& src);
+
+ void pop(Register dst);
+ void pop(const Operand& dst);
+
+ void enter(Immediate size);
+ void leave();
+
+ // Moves
+ void movb(Register dst, const Operand& src);
+ void movb(Register dst, Immediate imm);
+ void movb(const Operand& dst, Register src);
+
+ // Move the low 16 bits of a 64-bit register value to a 16-bit
+ // memory location.
+ void movw(const Operand& dst, Register src);
+
+ void movl(Register dst, Register src);
+ void movl(Register dst, const Operand& src);
+ void movl(const Operand& dst, Register src);
+ void movl(const Operand& dst, Immediate imm);
+ // Load a 32-bit immediate value, zero-extended to 64 bits.
+ void movl(Register dst, Immediate imm32);
+
+ // Move 64 bit register value to 64-bit memory location.
+ void movq(const Operand& dst, Register src);
+ // Move 64 bit memory location to 64-bit register value.
+ void movq(Register dst, const Operand& src);
+ void movq(Register dst, Register src);
+ // Sign extends immediate 32-bit value to 64 bits.
+ void movq(Register dst, Immediate x);
+ // Move the offset of the label location relative to the current
+ // position (after the move) to the destination.
+ void movl(const Operand& dst, Label* src);
+
+ // Move sign extended immediate to memory location.
+ void movq(const Operand& dst, Immediate value);
+ // Instructions to load a 64-bit immediate into a register.
+ // All 64-bit immediates must have a relocation mode.
+ void movq(Register dst, void* ptr, RelocInfo::Mode rmode);
+ void movq(Register dst, int64_t value, RelocInfo::Mode rmode);
+ void movq(Register dst, const char* s, RelocInfo::Mode rmode);
+ // Moves the address of the external reference into the register.
+ void movq(Register dst, ExternalReference ext);
+ void movq(Register dst, Handle<Object> handle, RelocInfo::Mode rmode);
+
+ void movsxbq(Register dst, const Operand& src);
+ void movsxwq(Register dst, const Operand& src);
+ void movsxlq(Register dst, Register src);
+ void movsxlq(Register dst, const Operand& src);
+ void movzxbq(Register dst, const Operand& src);
+ void movzxbl(Register dst, const Operand& src);
+ void movzxwq(Register dst, const Operand& src);
+ void movzxwl(Register dst, const Operand& src);
+ void movzxwl(Register dst, Register src);
+
+ // Repeated moves.
+
+ void repmovsb();
+ void repmovsw();
+ void repmovsl();
+ void repmovsq();
+
+ // Instruction to load from an immediate 64-bit pointer into RAX.
+ void load_rax(void* ptr, RelocInfo::Mode rmode);
+ void load_rax(ExternalReference ext);
+
+ // Conditional moves.
+ void cmovq(Condition cc, Register dst, Register src);
+ void cmovq(Condition cc, Register dst, const Operand& src);
+ void cmovl(Condition cc, Register dst, Register src);
+ void cmovl(Condition cc, Register dst, const Operand& src);
+
+ // Exchange two registers
+ void xchg(Register dst, Register src);
+
+ // Arithmetics
+ void addl(Register dst, Register src) {
+ arithmetic_op_32(0x03, dst, src);
+ }
+
+ void addl(Register dst, Immediate src) {
+ immediate_arithmetic_op_32(0x0, dst, src);
+ }
+
+ void addl(Register dst, const Operand& src) {
+ arithmetic_op_32(0x03, dst, src);
+ }
+
+ void addl(const Operand& dst, Immediate src) {
+ 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);
+ }
+
+ void addq(Register dst, const Operand& src) {
+ arithmetic_op(0x03, dst, src);
+ }
+
+ void addq(const Operand& dst, Register src) {
+ arithmetic_op(0x01, src, dst);
+ }
+
+ void addq(Register dst, Immediate src) {
+ immediate_arithmetic_op(0x0, dst, src);
+ }
+
+ void addq(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op(0x0, dst, src);
+ }
+
+ void sbbl(Register dst, Register src) {
+ arithmetic_op_32(0x1b, dst, src);
+ }
+
+ void sbbq(Register dst, Register src) {
+ arithmetic_op(0x1b, dst, src);
+ }
+
+ void cmpb(Register dst, Immediate src) {
+ immediate_arithmetic_op_8(0x7, dst, src);
+ }
+
+ void cmpb_al(Immediate src);
+
+ void cmpb(Register dst, Register src) {
+ arithmetic_op(0x3A, dst, src);
+ }
+
+ void cmpb(Register dst, const Operand& src) {
+ arithmetic_op(0x3A, dst, src);
+ }
+
+ void cmpb(const Operand& dst, Register src) {
+ arithmetic_op(0x38, src, dst);
+ }
+
+ void cmpb(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op_8(0x7, dst, src);
+ }
+
+ void cmpw(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op_16(0x7, dst, src);
+ }
+
+ void cmpw(Register dst, Immediate src) {
+ immediate_arithmetic_op_16(0x7, dst, src);
+ }
+
+ void cmpw(Register dst, const Operand& src) {
+ arithmetic_op_16(0x3B, dst, src);
+ }
+
+ void cmpw(Register dst, Register src) {
+ arithmetic_op_16(0x3B, dst, src);
+ }
+
+ void cmpw(const Operand& dst, Register src) {
+ arithmetic_op_16(0x39, src, dst);
+ }
+
+ void cmpl(Register dst, Register src) {
+ arithmetic_op_32(0x3B, dst, src);
+ }
+
+ void cmpl(Register dst, const Operand& src) {
+ arithmetic_op_32(0x3B, dst, src);
+ }
+
+ void cmpl(const Operand& dst, Register src) {
+ arithmetic_op_32(0x39, src, dst);
+ }
+
+ void cmpl(Register dst, Immediate src) {
+ immediate_arithmetic_op_32(0x7, dst, src);
+ }
+
+ void cmpl(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op_32(0x7, dst, src);
+ }
+
+ void cmpq(Register dst, Register src) {
+ arithmetic_op(0x3B, dst, src);
+ }
+
+ void cmpq(Register dst, const Operand& src) {
+ arithmetic_op(0x3B, dst, src);
+ }
+
+ void cmpq(const Operand& dst, Register src) {
+ arithmetic_op(0x39, src, dst);
+ }
+
+ void cmpq(Register dst, Immediate src) {
+ immediate_arithmetic_op(0x7, dst, src);
+ }
+
+ void cmpq(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op(0x7, dst, src);
+ }
+
+ void and_(Register dst, Register src) {
+ arithmetic_op(0x23, dst, src);
+ }
+
+ void and_(Register dst, const Operand& src) {
+ arithmetic_op(0x23, dst, src);
+ }
+
+ void and_(const Operand& dst, Register src) {
+ arithmetic_op(0x21, src, dst);
+ }
+
+ void and_(Register dst, Immediate src) {
+ immediate_arithmetic_op(0x4, dst, src);
+ }
+
+ void and_(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op(0x4, dst, src);
+ }
+
+ void andl(Register dst, Immediate src) {
+ immediate_arithmetic_op_32(0x4, dst, src);
+ }
+
+ void andl(Register dst, Register src) {
+ arithmetic_op_32(0x23, dst, src);
+ }
+
+ void andl(Register dst, const Operand& src) {
+ arithmetic_op_32(0x23, dst, src);
+ }
+
+ void andb(Register dst, Immediate src) {
+ immediate_arithmetic_op_8(0x4, dst, src);
+ }
+
+ void decq(Register dst);
+ void decq(const Operand& dst);
+ void decl(Register dst);
+ void decl(const Operand& dst);
+ void decb(Register dst);
+ void decb(const Operand& dst);
+
+ // Sign-extends rax into rdx:rax.
+ void cqo();
+ // Sign-extends eax into edx:eax.
+ void cdq();
+
+ // Divide rdx:rax by src. Quotient in rax, remainder in rdx.
+ void idivq(Register src);
+ // Divide edx:eax by lower 32 bits of src. Quotient in eax, rem. in edx.
+ void idivl(Register src);
+
+ // Signed multiply instructions.
+ void imul(Register src); // rdx:rax = rax * src.
+ void imul(Register dst, Register src); // dst = dst * src.
+ void imul(Register dst, const Operand& src); // dst = dst * src.
+ void imul(Register dst, Register src, Immediate imm); // dst = src * imm.
+ // Signed 32-bit multiply instructions.
+ void imull(Register dst, Register src); // dst = dst * src.
+ void imull(Register dst, const Operand& src); // dst = dst * src.
+ void imull(Register dst, Register src, Immediate imm); // dst = src * imm.
+
+ void incq(Register dst);
+ void incq(const Operand& dst);
+ void incl(Register dst);
+ void incl(const Operand& dst);
+
+ void lea(Register dst, const Operand& src);
+ void leal(Register dst, const Operand& src);
+
+ // Multiply rax by src, put the result in rdx:rax.
+ void mul(Register src);
+
+ void neg(Register dst);
+ void neg(const Operand& dst);
+ void negl(Register dst);
+
+ void not_(Register dst);
+ void not_(const Operand& dst);
+ void notl(Register dst);
+
+ void or_(Register dst, Register src) {
+ arithmetic_op(0x0B, dst, src);
+ }
+
+ void orl(Register dst, Register src) {
+ arithmetic_op_32(0x0B, dst, src);
+ }
+
+ void or_(Register dst, const Operand& src) {
+ arithmetic_op(0x0B, dst, src);
+ }
+
+ void orl(Register dst, const Operand& src) {
+ arithmetic_op_32(0x0B, dst, src);
+ }
+
+ void or_(const Operand& dst, Register src) {
+ arithmetic_op(0x09, src, dst);
+ }
+
+ void or_(Register dst, Immediate src) {
+ immediate_arithmetic_op(0x1, dst, src);
+ }
+
+ void orl(Register dst, Immediate src) {
+ immediate_arithmetic_op_32(0x1, dst, src);
+ }
+
+ void or_(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op(0x1, dst, src);
+ }
+
+ void orl(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op_32(0x1, dst, src);
+ }
+
+
+ void rcl(Register dst, Immediate imm8) {
+ shift(dst, imm8, 0x2);
+ }
+
+ void rol(Register dst, Immediate imm8) {
+ shift(dst, imm8, 0x0);
+ }
+
+ void rcr(Register dst, Immediate imm8) {
+ shift(dst, imm8, 0x3);
+ }
+
+ void ror(Register dst, Immediate imm8) {
+ shift(dst, imm8, 0x1);
+ }
+
+ void rorl(Register dst, Immediate imm8) {
+ shift_32(dst, imm8, 0x1);
+ }
+
+ void rorl_cl(Register dst) {
+ shift_32(dst, 0x1);
+ }
+
+ // Shifts dst:src left by cl bits, affecting only dst.
+ void shld(Register dst, Register src);
+
+ // Shifts src:dst right by cl bits, affecting only dst.
+ void shrd(Register dst, Register src);
+
+ // Shifts dst right, duplicating sign bit, by shift_amount bits.
+ // Shifting by 1 is handled efficiently.
+ void sar(Register dst, Immediate shift_amount) {
+ shift(dst, shift_amount, 0x7);
+ }
+
+ // Shifts dst right, duplicating sign bit, by shift_amount bits.
+ // Shifting by 1 is handled efficiently.
+ void sarl(Register dst, Immediate shift_amount) {
+ shift_32(dst, shift_amount, 0x7);
+ }
+
+ // Shifts dst right, duplicating sign bit, by cl % 64 bits.
+ void sar_cl(Register dst) {
+ shift(dst, 0x7);
+ }
+
+ // Shifts dst right, duplicating sign bit, by cl % 64 bits.
+ void sarl_cl(Register dst) {
+ shift_32(dst, 0x7);
+ }
+
+ void shl(Register dst, Immediate shift_amount) {
+ shift(dst, shift_amount, 0x4);
+ }
+
+ void shl_cl(Register dst) {
+ shift(dst, 0x4);
+ }
+
+ void shll_cl(Register dst) {
+ shift_32(dst, 0x4);
+ }
+
+ void shll(Register dst, Immediate shift_amount) {
+ shift_32(dst, shift_amount, 0x4);
+ }
+
+ void shr(Register dst, Immediate shift_amount) {
+ shift(dst, shift_amount, 0x5);
+ }
+
+ void shr_cl(Register dst) {
+ shift(dst, 0x5);
+ }
+
+ void shrl_cl(Register dst) {
+ shift_32(dst, 0x5);
+ }
+
+ void shrl(Register dst, Immediate shift_amount) {
+ shift_32(dst, shift_amount, 0x5);
+ }
+
+ void store_rax(void* dst, RelocInfo::Mode mode);
+ void store_rax(ExternalReference ref);
+
+ void subq(Register dst, Register src) {
+ arithmetic_op(0x2B, dst, src);
+ }
+
+ void subq(Register dst, const Operand& src) {
+ arithmetic_op(0x2B, dst, src);
+ }
+
+ void subq(const Operand& dst, Register src) {
+ arithmetic_op(0x29, src, dst);
+ }
+
+ void subq(Register dst, Immediate src) {
+ immediate_arithmetic_op(0x5, dst, src);
+ }
+
+ void subq(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op(0x5, dst, src);
+ }
+
+ void subl(Register dst, Register src) {
+ arithmetic_op_32(0x2B, dst, src);
+ }
+
+ void subl(Register dst, const Operand& src) {
+ arithmetic_op_32(0x2B, dst, src);
+ }
+
+ void subl(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op_32(0x5, dst, src);
+ }
+
+ void subl(Register dst, Immediate src) {
+ immediate_arithmetic_op_32(0x5, dst, src);
+ }
+
+ void subb(Register dst, Immediate src) {
+ immediate_arithmetic_op_8(0x5, dst, src);
+ }
+
+ void testb(Register dst, Register src);
+ void testb(Register reg, Immediate mask);
+ void testb(const Operand& op, Immediate mask);
+ void testb(const Operand& op, Register reg);
+ void testl(Register dst, Register src);
+ void testl(Register reg, Immediate mask);
+ void testl(const Operand& op, Immediate mask);
+ void testq(const Operand& op, Register reg);
+ void testq(Register dst, Register src);
+ void testq(Register dst, Immediate mask);
+
+ void xor_(Register dst, Register src) {
+ if (dst.code() == src.code()) {
+ arithmetic_op_32(0x33, dst, src);
+ } else {
+ arithmetic_op(0x33, dst, src);
+ }
+ }
+
+ void xorl(Register dst, Register src) {
+ arithmetic_op_32(0x33, dst, src);
+ }
+
+ void xorl(Register dst, const Operand& src) {
+ arithmetic_op_32(0x33, dst, src);
+ }
+
+ void xorl(Register dst, Immediate src) {
+ immediate_arithmetic_op_32(0x6, dst, src);
+ }
+
+ void xorl(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op_32(0x6, dst, src);
+ }
+
+ void xor_(Register dst, const Operand& src) {
+ arithmetic_op(0x33, dst, src);
+ }
+
+ void xor_(const Operand& dst, Register src) {
+ arithmetic_op(0x31, src, dst);
+ }
+
+ void xor_(Register dst, Immediate src) {
+ immediate_arithmetic_op(0x6, dst, src);
+ }
+
+ void xor_(const Operand& dst, Immediate src) {
+ immediate_arithmetic_op(0x6, dst, src);
+ }
+
+ // Bit operations.
+ void bt(const Operand& dst, Register src);
+ void bts(const Operand& dst, Register src);
+
+ // Miscellaneous
+ void clc();
+ void cld();
+ void cpuid();
+ void hlt();
+ void int3();
+ void nop();
+ void rdtsc();
+ void ret(int imm16);
+ void setcc(Condition cc, Register reg);
+
+ // Label operations & relative jumps (PPUM Appendix D)
+ //
+ // Takes a branch opcode (cc) and a label (L) and generates
+ // either a backward branch or a forward branch and links it
+ // to the label fixup chain. Usage:
+ //
+ // Label L; // unbound label
+ // j(cc, &L); // forward branch to unbound label
+ // bind(&L); // bind label to the current pc
+ // j(cc, &L); // backward branch to bound label
+ // bind(&L); // illegal: a label may be bound only once
+ //
+ // Note: The same Label can be used for forward and backward branches
+ // but it may be bound only once.
+
+ void bind(Label* L); // binds an unbound label L to the current code position
+
+ // Calls
+ // Call near relative 32-bit displacement, relative to next instruction.
+ void call(Label* L);
+ void call(Address entry, RelocInfo::Mode rmode);
+ void call(Handle<Code> target,
+ RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
+ TypeFeedbackId ast_id = TypeFeedbackId::None());
+
+ // Calls directly to the given address using a relative offset.
+ // Should only ever be used in Code objects for calls within the
+ // same Code object. Should not be used when generating new code (use labels),
+ // but only when patching existing code.
+ void call(Address target);
+
+ // Call near absolute indirect, address in register
+ void call(Register adr);
+
+ // Call near indirect
+ void call(const Operand& operand);
+
+ // Jumps
+ // Jump short or near relative.
+ // Use a 32-bit signed displacement.
+ // Unconditional jump to L
+ void jmp(Label* L, Label::Distance distance = Label::kFar);
+ void jmp(Address entry, RelocInfo::Mode rmode);
+ void jmp(Handle<Code> target, RelocInfo::Mode rmode);
+
+ // Jump near absolute indirect (r64)
+ void jmp(Register adr);
+
+ // Jump near absolute indirect (m64)
+ void jmp(const Operand& src);
+
+ // Conditional jumps
+ void j(Condition cc,
+ Label* L,
+ Label::Distance distance = Label::kFar);
+ void j(Condition cc, Address entry, RelocInfo::Mode rmode);
+ void j(Condition cc, Handle<Code> target, RelocInfo::Mode rmode);
+
+ // Floating-point operations
+ void fld(int i);
+
+ void fld1();
+ void fldz();
+ void fldpi();
+ void fldln2();
+
+ void fld_s(const Operand& adr);
+ void fld_d(const Operand& adr);
+
+ void fstp_s(const Operand& adr);
+ void fstp_d(const Operand& adr);
+ void fstp(int index);
+
+ void fild_s(const Operand& adr);
+ void fild_d(const Operand& adr);
+
+ void fist_s(const Operand& adr);
+
+ void fistp_s(const Operand& adr);
+ void fistp_d(const Operand& adr);
+
+ void fisttp_s(const Operand& adr);
+ void fisttp_d(const Operand& adr);
+
+ void fabs();
+ void fchs();
+
+ void fadd(int i);
+ void fsub(int i);
+ void fmul(int i);
+ void fdiv(int i);
+
+ void fisub_s(const Operand& adr);
+
+ void faddp(int i = 1);
+ void fsubp(int i = 1);
+ void fsubrp(int i = 1);
+ void fmulp(int i = 1);
+ void fdivp(int i = 1);
+ void fprem();
+ void fprem1();
+
+ void fxch(int i = 1);
+ void fincstp();
+ void ffree(int i = 0);
+
+ void ftst();
+ void fucomp(int i);
+ void fucompp();
+ void fucomi(int i);
+ void fucomip();
+
+ void fcompp();
+ void fnstsw_ax();
+ void fwait();
+ void fnclex();
+
+ void fsin();
+ void fcos();
+ void fptan();
+ void fyl2x();
+ void f2xm1();
+ void fscale();
+ void fninit();
+
+ void frndint();
+
+ void sahf();
+
+ // SSE2 instructions
+ void movd(XMMRegister dst, Register src);
+ void movd(Register dst, XMMRegister src);
+ void movq(XMMRegister dst, Register src);
+ void movq(Register dst, XMMRegister src);
+ void movq(XMMRegister dst, XMMRegister src);
+ void extractps(Register dst, XMMRegister src, byte imm8);
+
+ // Don't use this unless it's important to keep the
+ // top half of the destination register unchanged.
+ // Used movaps when moving double values and movq for integer
+ // values in xmm registers.
+ void movsd(XMMRegister dst, XMMRegister src);
+
+ void movsd(const Operand& dst, XMMRegister src);
+ void movsd(XMMRegister dst, const Operand& src);
+
+ void movdqa(const Operand& dst, XMMRegister src);
+ void movdqa(XMMRegister dst, const Operand& src);
+
+ void movdqu(const Operand& dst, XMMRegister src);
+ void movdqu(XMMRegister dst, const Operand& src);
+
+ void movapd(XMMRegister dst, XMMRegister src);
+ void movaps(XMMRegister dst, XMMRegister src);
+
+ void movss(XMMRegister dst, const Operand& src);
+ void movss(const Operand& dst, XMMRegister src);
+
+ void cvttss2si(Register dst, const Operand& src);
+ void cvttss2si(Register dst, XMMRegister src);
+ void cvttsd2si(Register dst, const Operand& src);
+ void cvttsd2si(Register dst, XMMRegister src);
+ void cvttsd2siq(Register dst, XMMRegister src);
+
+ void cvtlsi2sd(XMMRegister dst, const Operand& src);
+ void cvtlsi2sd(XMMRegister dst, Register src);
+ void cvtqsi2sd(XMMRegister dst, const Operand& src);
+ void cvtqsi2sd(XMMRegister dst, Register src);
+
+ void cvtlsi2ss(XMMRegister dst, Register src);
+
+ void cvtss2sd(XMMRegister dst, XMMRegister src);
+ void cvtss2sd(XMMRegister dst, const Operand& src);
+ void cvtsd2ss(XMMRegister dst, XMMRegister src);
+
+ void cvtsd2si(Register dst, XMMRegister src);
+ void cvtsd2siq(Register dst, XMMRegister src);
+
+ void addsd(XMMRegister dst, XMMRegister src);
+ void addsd(XMMRegister dst, const Operand& src);
+ void subsd(XMMRegister dst, XMMRegister src);
+ void mulsd(XMMRegister dst, XMMRegister src);
+ void mulsd(XMMRegister dst, const Operand& src);
+ void divsd(XMMRegister dst, XMMRegister src);
+
+ void andpd(XMMRegister dst, XMMRegister src);
+ void orpd(XMMRegister dst, XMMRegister src);
+ void xorpd(XMMRegister dst, XMMRegister src);
+ void xorps(XMMRegister dst, XMMRegister src);
+ void sqrtsd(XMMRegister dst, XMMRegister src);
+
+ void ucomisd(XMMRegister dst, XMMRegister src);
+ void ucomisd(XMMRegister dst, const Operand& src);
+
+ enum RoundingMode {
+ kRoundToNearest = 0x0,
+ kRoundDown = 0x1,
+ kRoundUp = 0x2,
+ kRoundToZero = 0x3
+ };
+
+ void roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode);
+
+ void movmskpd(Register dst, XMMRegister src);
+ void movmskps(Register dst, XMMRegister src);
+
+ // The first argument is the reg field, the second argument is the r/m field.
+ void emit_sse_operand(XMMRegister dst, XMMRegister src);
+ void emit_sse_operand(XMMRegister reg, const Operand& adr);
+ void emit_sse_operand(XMMRegister dst, Register src);
+ void emit_sse_operand(Register dst, XMMRegister src);
+
+ // Debugging
+ void Print();
+
+ // Check the code size generated from label to here.
+ int SizeOfCodeGeneratedSince(Label* label) {
+ return pc_offset() - label->pos();
+ }
+
+ // Mark address of the ExitJSFrame code.
+ void RecordJSReturn();
+
+ // Mark address of a debug break slot.
+ void RecordDebugBreakSlot();
+
+ // Record a comment relocation entry that can be used by a disassembler.
+ // Use --code-comments to enable.
+ void RecordComment(const char* msg, bool force = false);
+
+ // Writes a single word of data in the code stream.
+ // Used for inline tables, e.g., jump-tables.
+ void db(uint8_t data);
+ void dd(uint32_t data);
+
+ PositionsRecorder* positions_recorder() { return &positions_recorder_; }
+
+ // Check if there is less than kGap bytes available in the buffer.
+ // If this is the case, we need to grow the buffer before emitting
+ // an instruction or relocation information.
+ inline bool buffer_overflow() const {
+ return pc_ >= reloc_info_writer.pos() - kGap;
+ }
+
+ // Get the number of bytes available in the buffer.
+ inline int available_space() const {
+ return static_cast<int>(reloc_info_writer.pos() - pc_);
+ }
+
+ static bool IsNop(Address addr);
+
+ // Avoid overflows for displacements etc.
+ static const int kMaximalBufferSize = 512*MB;
+
+ byte byte_at(int pos) { return buffer_[pos]; }
+ void set_byte_at(int pos, byte value) { buffer_[pos] = value; }
+
+ private:
+ byte* addr_at(int pos) { return buffer_ + pos; }
+ uint32_t long_at(int pos) {
+ return *reinterpret_cast<uint32_t*>(addr_at(pos));
+ }
+ void long_at_put(int pos, uint32_t x) {
+ *reinterpret_cast<uint32_t*>(addr_at(pos)) = x;
+ }
+
+ // code emission
+ void GrowBuffer();
+
+ void emit(byte x) { *pc_++ = x; }
+ inline void emitl(uint32_t x);
+ inline void emitp(void* x, RelocInfo::Mode rmode);
+ inline void emitq(uint64_t x, RelocInfo::Mode rmode);
+ inline void emitw(uint16_t x);
+ inline void emit_code_target(Handle<Code> target,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id = TypeFeedbackId::None());
+ inline void emit_runtime_entry(Address entry, RelocInfo::Mode rmode);
+ void emit(Immediate x) { emitl(x.value_); }
+
+ // Emits a REX prefix that encodes a 64-bit operand size and
+ // the top bit of both register codes.
+ // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B.
+ // REX.W is set.
+ inline void emit_rex_64(XMMRegister reg, Register rm_reg);
+ inline void emit_rex_64(Register reg, XMMRegister rm_reg);
+ inline void emit_rex_64(Register reg, Register rm_reg);
+
+ // Emits a REX prefix that encodes a 64-bit operand size and
+ // the top bit of the destination, index, and base register codes.
+ // The high bit of reg is used for REX.R, the high bit of op's base
+ // register is used for REX.B, and the high bit of op's index register
+ // is used for REX.X. REX.W is set.
+ inline void emit_rex_64(Register reg, const Operand& op);
+ inline void emit_rex_64(XMMRegister reg, const Operand& op);
+
+ // Emits a REX prefix that encodes a 64-bit operand size and
+ // the top bit of the register code.
+ // The high bit of register is used for REX.B.
+ // REX.W is set and REX.R and REX.X are clear.
+ inline void emit_rex_64(Register rm_reg);
+
+ // Emits a REX prefix that encodes a 64-bit operand size and
+ // the top bit of the index and base register codes.
+ // The high bit of op's base register is used for REX.B, and the high
+ // bit of op's index register is used for REX.X.
+ // REX.W is set and REX.R clear.
+ inline void emit_rex_64(const Operand& op);
+
+ // Emit a REX prefix that only sets REX.W to choose a 64-bit operand size.
+ void emit_rex_64() { emit(0x48); }
+
+ // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B.
+ // REX.W is clear.
+ inline void emit_rex_32(Register reg, Register rm_reg);
+
+ // The high bit of reg is used for REX.R, the high bit of op's base
+ // register is used for REX.B, and the high bit of op's index register
+ // is used for REX.X. REX.W is cleared.
+ inline void emit_rex_32(Register reg, const Operand& op);
+
+ // High bit of rm_reg goes to REX.B.
+ // REX.W, REX.R and REX.X are clear.
+ inline void emit_rex_32(Register rm_reg);
+
+ // High bit of base goes to REX.B and high bit of index to REX.X.
+ // REX.W and REX.R are clear.
+ inline void emit_rex_32(const Operand& op);
+
+ // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B.
+ // REX.W is cleared. If no REX bits are set, no byte is emitted.
+ inline void emit_optional_rex_32(Register reg, Register rm_reg);
+
+ // The high bit of reg is used for REX.R, the high bit of op's base
+ // register is used for REX.B, and the high bit of op's index register
+ // is used for REX.X. REX.W is cleared. If no REX bits are set, nothing
+ // is emitted.
+ inline void emit_optional_rex_32(Register reg, const Operand& op);
+
+ // As for emit_optional_rex_32(Register, Register), except that
+ // the registers are XMM registers.
+ inline void emit_optional_rex_32(XMMRegister reg, XMMRegister base);
+
+ // As for emit_optional_rex_32(Register, Register), except that
+ // one of the registers is an XMM registers.
+ inline void emit_optional_rex_32(XMMRegister reg, Register base);
+
+ // As for emit_optional_rex_32(Register, Register), except that
+ // one of the registers is an XMM registers.
+ inline void emit_optional_rex_32(Register reg, XMMRegister base);
+
+ // As for emit_optional_rex_32(Register, const Operand&), except that
+ // the register is an XMM register.
+ inline void emit_optional_rex_32(XMMRegister reg, const Operand& op);
+
+ // Optionally do as emit_rex_32(Register) if the register number has
+ // the high bit set.
+ inline void emit_optional_rex_32(Register rm_reg);
+
+ // Optionally do as emit_rex_32(const Operand&) if the operand register
+ // numbers have a high bit set.
+ inline void emit_optional_rex_32(const Operand& op);
+
+
+ // Emit the ModR/M byte, and optionally the SIB byte and
+ // 1- or 4-byte offset for a memory operand. Also encodes
+ // the second operand of the operation, a register or operation
+ // subcode, into the reg field of the ModR/M byte.
+ void emit_operand(Register reg, const Operand& adr) {
+ emit_operand(reg.low_bits(), adr);
+ }
+
+ // Emit the ModR/M byte, and optionally the SIB byte and
+ // 1- or 4-byte offset for a memory operand. Also used to encode
+ // a three-bit opcode extension into the ModR/M byte.
+ void emit_operand(int rm, const Operand& adr);
+
+ // Emit a ModR/M byte with registers coded in the reg and rm_reg fields.
+ void emit_modrm(Register reg, Register rm_reg) {
+ emit(0xC0 | reg.low_bits() << 3 | rm_reg.low_bits());
+ }
+
+ // Emit a ModR/M byte with an operation subcode in the reg field and
+ // a register in the rm_reg field.
+ void emit_modrm(int code, Register rm_reg) {
+ ASSERT(is_uint3(code));
+ emit(0xC0 | code << 3 | rm_reg.low_bits());
+ }
+
+ // Emit the code-object-relative offset of the label's position
+ inline void emit_code_relative_offset(Label* label);
+
+ // Emit machine code for one of the operations ADD, ADC, SUB, SBC,
+ // AND, OR, XOR, or CMP. The encodings of these operations are all
+ // similar, differing just in the opcode or in the reg field of the
+ // ModR/M byte.
+ void arithmetic_op_16(byte opcode, Register reg, Register rm_reg);
+ void arithmetic_op_16(byte opcode, Register reg, const Operand& rm_reg);
+ void arithmetic_op_32(byte opcode, Register reg, Register rm_reg);
+ void arithmetic_op_32(byte opcode, Register reg, const Operand& rm_reg);
+ void arithmetic_op(byte opcode, Register reg, Register rm_reg);
+ void arithmetic_op(byte opcode, Register reg, const Operand& rm_reg);
+ void immediate_arithmetic_op(byte subcode, Register dst, Immediate src);
+ void immediate_arithmetic_op(byte subcode, const Operand& dst, Immediate src);
+ // Operate on a byte in memory or register.
+ void immediate_arithmetic_op_8(byte subcode,
+ Register dst,
+ Immediate src);
+ void immediate_arithmetic_op_8(byte subcode,
+ const Operand& dst,
+ Immediate src);
+ // Operate on a word in memory or register.
+ void immediate_arithmetic_op_16(byte subcode,
+ Register dst,
+ Immediate src);
+ void immediate_arithmetic_op_16(byte subcode,
+ const Operand& dst,
+ Immediate src);
+ // Operate on a 32-bit word in memory or register.
+ void immediate_arithmetic_op_32(byte subcode,
+ Register dst,
+ Immediate src);
+ void immediate_arithmetic_op_32(byte subcode,
+ const Operand& dst,
+ Immediate src);
+
+ // Emit machine code for a shift operation.
+ void shift(Register dst, Immediate shift_amount, int subcode);
+ void shift_32(Register dst, Immediate shift_amount, int subcode);
+ // Shift dst by cl % 64 bits.
+ void shift(Register dst, int subcode);
+ void shift_32(Register dst, int subcode);
+
+ void emit_farith(int b1, int b2, int i);
+
+ // labels
+ // void print(Label* L);
+ void bind_to(Label* L, int pos);
+
+ // record reloc info for current pc_
+ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
+
+ friend class CodePatcher;
+ friend class EnsureSpace;
+ friend class RegExpMacroAssemblerX64;
+
+ // code generation
+ RelocInfoWriter reloc_info_writer;
+
+ List< Handle<Code> > code_targets_;
+
+ PositionsRecorder positions_recorder_;
+ friend class PositionsRecorder;
+};
+
+
+// Helper class that ensures that there is enough space for generating
+// instructions and relocation information. The constructor makes
+// sure that there is enough space and (in debug mode) the destructor
+// checks that we did not generate too much.
+class EnsureSpace BASE_EMBEDDED {
+ public:
+ explicit EnsureSpace(Assembler* assembler) : assembler_(assembler) {
+ if (assembler_->buffer_overflow()) assembler_->GrowBuffer();
+#ifdef DEBUG
+ space_before_ = assembler_->available_space();
+#endif
+ }
+
+#ifdef DEBUG
+ ~EnsureSpace() {
+ int bytes_generated = space_before_ - assembler_->available_space();
+ ASSERT(bytes_generated < assembler_->kGap);
+ }
+#endif
+
+ private:
+ Assembler* assembler_;
+#ifdef DEBUG
+ int space_before_;
+#endif
+};
+
+} } // namespace v8::internal
+
+#endif // V8_X64_ASSEMBLER_X64_H_
diff --git a/chromium/v8/src/x64/builtins-x64.cc b/chromium/v8/src/x64/builtins-x64.cc
new file mode 100644
index 00000000000..18a6e566c6c
--- /dev/null
+++ b/chromium/v8/src/x64/builtins-x64.cc
@@ -0,0 +1,1443 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "codegen.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+
+namespace v8 {
+namespace internal {
+
+
+#define __ ACCESS_MASM(masm)
+
+
+void Builtins::Generate_Adaptor(MacroAssembler* masm,
+ CFunctionId id,
+ BuiltinExtraArguments extra_args) {
+ // ----------- S t a t e -------------
+ // -- rax : number of arguments excluding receiver
+ // -- rdi : called function (only guaranteed when
+ // extra_args requires it)
+ // -- rsi : context
+ // -- rsp[0] : return address
+ // -- rsp[8] : last argument
+ // -- ...
+ // -- rsp[8 * argc] : first argument (argc == rax)
+ // -- rsp[8 * (argc + 1)] : receiver
+ // -----------------------------------
+
+ // Insert extra arguments.
+ int num_extra_args = 0;
+ if (extra_args == NEEDS_CALLED_FUNCTION) {
+ num_extra_args = 1;
+ __ PopReturnAddressTo(kScratchRegister);
+ __ push(rdi);
+ __ PushReturnAddressFrom(kScratchRegister);
+ } else {
+ ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
+ }
+
+ // JumpToExternalReference expects rax to contain the number of arguments
+ // including the receiver and the extra arguments.
+ __ addq(rax, Immediate(num_extra_args + 1));
+ __ JumpToExternalReference(ExternalReference(id, masm->isolate()), 1);
+}
+
+
+static void GenerateTailCallToSharedCode(MacroAssembler* masm) {
+ __ movq(kScratchRegister,
+ FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movq(kScratchRegister,
+ FieldOperand(kScratchRegister, SharedFunctionInfo::kCodeOffset));
+ __ lea(kScratchRegister, FieldOperand(kScratchRegister, Code::kHeaderSize));
+ __ jmp(kScratchRegister);
+}
+
+
+void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) {
+ GenerateTailCallToSharedCode(masm);
+}
+
+
+void Builtins::Generate_InstallRecompiledCode(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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::kInstallRecompiledCode, 1);
+
+ // Restore call kind information.
+ __ pop(rcx);
+ // Restore function.
+ __ pop(rdi);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ lea(rax, FieldOperand(rax, Code::kHeaderSize));
+ __ jmp(rax);
+}
+
+
+void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) {
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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::kParallelRecompile, 1);
+
+ // Restore call kind information.
+ __ pop(rcx);
+ // Restore receiver.
+ __ pop(rdi);
+
+ // Tear down internal frame.
+ }
+
+ GenerateTailCallToSharedCode(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
+ // -----------------------------------
+
+ // Should never count constructions for api objects.
+ ASSERT(!is_api_function || !count_constructions);
+
+ // Enter a construct frame.
+ {
+ FrameScope scope(masm, StackFrame::CONSTRUCT);
+
+ // Store a smi-tagged arguments count on the stack.
+ __ Integer32ToSmi(rax, rax);
+ __ push(rax);
+
+ // 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;
+
+#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);
+#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
+ 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);
+
+ __ push(rax);
+ __ push(rdi);
+
+ __ push(rdi); // constructor
+ // The call will replace the stub, so the countdown is only done once.
+ __ CallRuntime(Runtime::kFinalizeInstanceSize, 1);
+
+ __ 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
+ __ Allocate(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,
+ kUnexpectedNumberOfPreAllocatedPropertyFields);
+ }
+ __ InitializeFieldsWithFiller(rcx, rsi, rdx);
+ __ LoadRoot(rdx, Heap::kOnePointerFillerMapRootIndex);
+ }
+ __ 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, kPropertyAllocationCountFailed);
+
+ // 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
+ __ Allocate(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);
+
+
+ // 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);
+ }
+
+ // 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);
+
+ // 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);
+
+ // 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);
+
+ // 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);
+ }
+
+ // Store offset of return address for deoptimizer.
+ if (!is_api_function && !count_constructions) {
+ masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // 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 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));
+
+ // Restore the arguments count and leave the construct frame.
+ __ bind(&exit);
+ __ movq(rbx, Operand(rsp, kPointerSize)); // Get arguments count.
+
+ // Leave construct frame.
+ }
+
+ // Remove caller arguments from the stack and return.
+ __ PopReturnAddressTo(rcx);
+ SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
+ __ lea(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize));
+ __ PushReturnAddressFrom(rcx);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->constructed_objects(), 1);
+ __ ret(0);
+}
+
+
+void Builtins::Generate_JSConstructStubCountdown(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, true);
+}
+
+
+void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, false);
+}
+
+
+void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, true, false);
+}
+
+
+static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
+ bool is_construct) {
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Expects five C++ function parameters.
+ // - Address entry (ignored)
+ // - JSFunction* function (
+ // - Object* receiver
+ // - int argc
+ // - Object*** argv
+ // (see Handle::Invoke in execution.cc).
+
+ // 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.
+
+#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 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);
+#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) {
+ // No type feedback cell is available
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->factory()->undefined_value());
+ __ Move(rbx, undefined_sentinel);
+ // 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.
+ }
+
+ // TODO(X64): Is argument correct? Is there a receiver to remove?
+ __ ret(1 * kPointerSize); // Remove receiver.
+}
+
+
+void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
+ Generate_JSEntryTrampolineHelper(masm, false);
+}
+
+
+void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
+ Generate_JSEntryTrampolineHelper(masm, true);
+}
+
+
+void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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);
+
+ // Restore call kind information.
+ __ pop(rcx);
+ // Restore receiver.
+ __ pop(rdi);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ lea(rax, FieldOperand(rax, Code::kHeaderSize));
+ __ jmp(rax);
+}
+
+
+void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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);
+
+ // Restore call kind information.
+ __ pop(rcx);
+ // Restore function.
+ __ pop(rdi);
+
+ // Tear down internal frame.
+ }
+
+ // Do a tail-call of the compiled function.
+ __ lea(rax, FieldOperand(rax, Code::kHeaderSize));
+ __ jmp(rax);
+}
+
+
+static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) {
+ // For now, we are relying on the fact that make_code_young doesn't do any
+ // garbage collection which allows us to save/restore the registers without
+ // worrying about which of them contain pointers. We also don't build an
+ // internal frame to make the code faster, since we shouldn't have to do stack
+ // crawls in MakeCodeYoung. This seems a bit fragile.
+
+ // Re-execute the code that was patched back to the young age when
+ // the stub returns.
+ __ subq(Operand(rsp, 0), Immediate(5));
+ __ Pushad();
+ __ movq(arg_reg_1, Operand(rsp, kNumSafepointRegisters * kPointerSize));
+ { // NOLINT
+ FrameScope scope(masm, StackFrame::MANUAL);
+ __ PrepareCallCFunction(1);
+ __ CallCFunction(
+ ExternalReference::get_make_code_young_function(masm->isolate()), 1);
+ }
+ __ Popad();
+ __ ret(0);
+}
+
+
+#define DEFINE_CODE_AGE_BUILTIN_GENERATOR(C) \
+void Builtins::Generate_Make##C##CodeYoungAgainEvenMarking( \
+ MacroAssembler* masm) { \
+ GenerateMakeCodeYoungAgainCommon(masm); \
+} \
+void Builtins::Generate_Make##C##CodeYoungAgainOddMarking( \
+ MacroAssembler* masm) { \
+ GenerateMakeCodeYoungAgainCommon(masm); \
+}
+CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR)
+#undef DEFINE_CODE_AGE_BUILTIN_GENERATOR
+
+
+void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Preserve registers across notification, this is important for compiled
+ // stubs that tail call the runtime on deopts passing their parameters in
+ // registers.
+ __ Pushad();
+ __ CallRuntime(Runtime::kNotifyStubFailure, 0);
+ __ Popad();
+ // Tear down internal frame.
+ }
+
+ __ pop(MemOperand(rsp, 0)); // Ignore state offset
+ __ ret(0); // Return to IC Miss stub, continuation still on stack.
+}
+
+
+static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
+ Deoptimizer::BailoutType type) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Pass the deoptimization type to the runtime system.
+ __ Push(Smi::FromInt(static_cast<int>(type)));
+
+ __ CallRuntime(Runtime::kNotifyDeoptimized, 1);
+ // Tear down internal frame.
+ }
+
+ // Get the full codegen state from the stack and untag it.
+ __ SmiToInteger32(r10, Operand(rsp, 1 * kPointerSize));
+
+ // Switch on the state.
+ Label not_no_registers, not_tos_rax;
+ __ cmpq(r10, Immediate(FullCodeGenerator::NO_REGISTERS));
+ __ j(not_equal, &not_no_registers, Label::kNear);
+ __ ret(1 * kPointerSize); // Remove state.
+
+ __ bind(&not_no_registers);
+ __ movq(rax, Operand(rsp, 2 * kPointerSize));
+ __ cmpq(r10, Immediate(FullCodeGenerator::TOS_REG));
+ __ j(not_equal, &not_tos_rax, Label::kNear);
+ __ ret(2 * kPointerSize); // Remove state, rax.
+
+ __ bind(&not_tos_rax);
+ __ Abort(kNoCasesLeft);
+}
+
+
+void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
+}
+
+
+void Builtins::Generate_NotifySoftDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::SOFT);
+}
+
+
+void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) {
+ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
+}
+
+
+void Builtins::Generate_NotifyOSR(MacroAssembler* masm) {
+ // 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.
+ __ Pushad();
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ CallRuntime(Runtime::kNotifyOSR, 0);
+ }
+ __ Popad();
+ __ ret(0);
+}
+
+
+void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
+ // Stack Layout:
+ // rsp[0] : Return address
+ // rsp[8] : Argument n
+ // rsp[16] : Argument n-1
+ // ...
+ // rsp[8 * n] : Argument 1
+ // rsp[8 * (n + 1)] : Receiver (function to call)
+ //
+ // rax contains the number of arguments, n, not counting the receiver.
+ //
+ // 1. Make sure we have at least one argument.
+ { Label done;
+ __ testq(rax, rax);
+ __ j(not_zero, &done);
+ __ PopReturnAddressTo(rbx);
+ __ Push(masm->isolate()->factory()->undefined_value());
+ __ PushReturnAddressFrom(rbx);
+ __ incq(rax);
+ __ bind(&done);
+ }
+
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ Label slow, non_function;
+ // The function to call is at position n+1 on the stack.
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
+ __ JumpIfSmi(rdi, &non_function);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &slow);
+
+ // 3a. Patch the first argument if necessary when calling a function.
+ Label shift_arguments;
+ __ Set(rdx, 0); // indicate regular JS_FUNCTION
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+
+ // Do not transform the receiver for strict mode functions.
+ __ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ testb(FieldOperand(rbx, SharedFunctionInfo::kStrictModeByteOffset),
+ Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte));
+ __ j(not_equal, &shift_arguments);
+
+ // Do not transform the receiver for natives.
+ // SharedFunctionInfo is already loaded into rbx.
+ __ testb(FieldOperand(rbx, SharedFunctionInfo::kNativeByteOffset),
+ Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte));
+ __ j(not_zero, &shift_arguments);
+
+ // Compute the receiver in non-strict mode.
+ __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
+ __ JumpIfSmi(rbx, &convert_to_object, Label::kNear);
+
+ __ CompareRoot(rbx, Heap::kNullValueRootIndex);
+ __ j(equal, &use_global_receiver);
+ __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
+ __ j(equal, &use_global_receiver);
+
+ STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
+ __ CmpObjectType(rbx, FIRST_SPEC_OBJECT_TYPE, rcx);
+ __ j(above_equal, &shift_arguments);
+
+ __ bind(&convert_to_object);
+ {
+ // 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
+
+ __ pop(rax);
+ __ SmiToInteger32(rax, rax);
+ }
+
+ // Restore the function to rdi.
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
+ __ jmp(&patch_receiver, Label::kNear);
+
+ // Use the global receiver object from the called function as the
+ // receiver.
+ __ bind(&use_global_receiver);
+ const int kGlobalIndex =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ __ movq(rbx, FieldOperand(rsi, kGlobalIndex));
+ __ movq(rbx, FieldOperand(rbx, GlobalObject::kNativeContextOffset));
+ __ movq(rbx, FieldOperand(rbx, kGlobalIndex));
+ __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+
+ __ bind(&patch_receiver);
+ __ movq(Operand(rsp, rax, times_pointer_size, 0), rbx);
+
+ __ jmp(&shift_arguments);
+ }
+
+ // 3b. Check for function proxy.
+ __ bind(&slow);
+ __ Set(rdx, 1); // indicate function proxy
+ __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
+ __ j(equal, &shift_arguments);
+ __ bind(&non_function);
+ __ Set(rdx, 2); // 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.
+ __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi);
+
+ // 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.
+ __ bind(&shift_arguments);
+ { Label loop;
+ __ movq(rcx, rax);
+ __ bind(&loop);
+ __ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0));
+ __ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx);
+ __ decq(rcx);
+ __ j(not_sign, &loop); // While non-negative (to copy return address).
+ __ pop(rbx); // Discard copy of return address.
+ __ decq(rax); // One fewer argument (first argument is new receiver).
+ }
+
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
+ // or a function proxy via CALL_FUNCTION_PROXY.
+ { Label function, non_proxy;
+ __ testq(rdx, rdx);
+ __ j(zero, &function);
+ __ Set(rbx, 0);
+ __ SetCallKind(rcx, CALL_AS_METHOD);
+ __ cmpq(rdx, Immediate(1));
+ __ j(not_equal, &non_proxy);
+
+ __ PopReturnAddressTo(rdx);
+ __ push(rdi); // re-add proxy object as additional argument
+ __ PushReturnAddressFrom(rdx);
+ __ incq(rax);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
+ __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ __ bind(&non_proxy);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+ __ bind(&function);
+ }
+
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movsxlq(rbx,
+ FieldOperand(rdx,
+ SharedFunctionInfo::kFormalParameterCountOffset));
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
+ __ SetCallKind(rcx, CALL_AS_METHOD);
+ __ cmpq(rax, rbx);
+ __ j(not_equal,
+ masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ ParameterCount expected(0);
+ __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
+}
+
+
+void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
+ // Stack at entry:
+ // rsp : return address
+ // rsp[8] : arguments
+ // rsp[16] : receiver ("this")
+ // rsp[24] : function
+ {
+ FrameScope frame_scope(masm, StackFrame::INTERNAL);
+ // Stack frame:
+ // rbp : Old base pointer
+ // rbp[8] : return address
+ // rbp[16] : function arguments
+ // rbp[24] : receiver
+ // rbp[32] : 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));
+
+ // 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_OBJECT_INDEX * kPointerSize;
+ __ movq(rbx, FieldOperand(rsi, kGlobalOffset));
+ __ movq(rbx, FieldOperand(rbx, GlobalObject::kNativeContextOffset));
+ __ 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);
+
+ // 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);
+
+ // Leave internal frame.
+ }
+ __ ret(3 * kPointerSize); // remove this, receiver, and arguments
+}
+
+
+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, kUnexpectedInitialMapForInternalArrayFunction);
+ __ CmpObjectType(rbx, MAP_TYPE, rcx);
+ __ Check(equal, kUnexpectedInitialMapForInternalArrayFunction);
+ }
+
+ // Run the native code for the InternalArray function called as a normal
+ // function.
+ // tail call a stub
+ InternalArrayConstructorStub stub(masm->isolate());
+ __ TailCallStub(&stub);
+}
+
+
+void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : argc
+ // -- rsp[0] : return address
+ // -- rsp[8] : last argument
+ // -----------------------------------
+ Label generic_array_code;
+
+ // Get the Array function.
+ __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, rdi);
+
+ if (FLAG_debug_code) {
+ // Initial map for the builtin Array 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, kUnexpectedInitialMapForArrayFunction);
+ __ CmpObjectType(rbx, MAP_TYPE, rcx);
+ __ Check(equal, kUnexpectedInitialMapForArrayFunction);
+ }
+
+ // Run the native code for the Array function called as a normal function.
+ // tail call a stub
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(),
+ masm->isolate());
+ __ Move(rbx, undefined_sentinel);
+ ArrayConstructorStub stub(masm->isolate());
+ __ TailCallStub(&stub);
+}
+
+
+void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : number of arguments
+ // -- rdi : constructor function
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- rsp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->string_ctor_calls(), 1);
+
+ if (FLAG_debug_code) {
+ __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, rcx);
+ __ cmpq(rdi, rcx);
+ __ Assert(equal, kUnexpectedStringFunction);
+ }
+
+ // Load the first argument into rax and get rid of the rest
+ // (including the receiver).
+ Label no_arguments;
+ __ testq(rax, rax);
+ __ j(zero, &no_arguments);
+ __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
+ __ PopReturnAddressTo(rcx);
+ __ lea(rsp, Operand(rsp, rax, times_pointer_size, kPointerSize));
+ __ PushReturnAddressFrom(rcx);
+ __ movq(rax, rbx);
+
+ // Lookup the argument in the number to string cache.
+ Label not_cached, argument_is_string;
+ NumberToStringStub::GenerateLookupNumberStringCache(
+ masm,
+ rax, // Input.
+ rbx, // Result.
+ rcx, // Scratch 1.
+ rdx, // Scratch 2.
+ &not_cached);
+ __ IncrementCounter(counters->string_ctor_cached_number(), 1);
+ __ bind(&argument_is_string);
+
+ // ----------- S t a t e -------------
+ // -- rbx : argument converted to string
+ // -- rdi : constructor function
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ // Allocate a JSValue and put the tagged pointer into rax.
+ Label gc_required;
+ __ Allocate(JSValue::kSize,
+ rax, // Result.
+ rcx, // New allocation top (we ignore it).
+ no_reg,
+ &gc_required,
+ TAG_OBJECT);
+
+ // Set the map.
+ __ LoadGlobalFunctionInitialMap(rdi, rcx);
+ if (FLAG_debug_code) {
+ __ cmpb(FieldOperand(rcx, Map::kInstanceSizeOffset),
+ Immediate(JSValue::kSize >> kPointerSizeLog2));
+ __ Assert(equal, kUnexpectedStringWrapperInstanceSize);
+ __ cmpb(FieldOperand(rcx, Map::kUnusedPropertyFieldsOffset), Immediate(0));
+ __ Assert(equal, kUnexpectedUnusedPropertiesOfStringWrapper);
+ }
+ __ movq(FieldOperand(rax, HeapObject::kMapOffset), rcx);
+
+ // Set properties and elements.
+ __ LoadRoot(rcx, Heap::kEmptyFixedArrayRootIndex);
+ __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), rcx);
+ __ movq(FieldOperand(rax, JSObject::kElementsOffset), rcx);
+
+ // Set the value.
+ __ movq(FieldOperand(rax, JSValue::kValueOffset), rbx);
+
+ // Ensure the object is fully initialized.
+ STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
+
+ // We're done. Return.
+ __ ret(0);
+
+ // The argument was not found in the number to string cache. Check
+ // if it's a string already before calling the conversion builtin.
+ Label convert_argument;
+ __ bind(&not_cached);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(rax, &convert_argument);
+ Condition is_string = masm->IsObjectStringType(rax, rbx, rcx);
+ __ j(NegateCondition(is_string), &convert_argument);
+ __ movq(rbx, rax);
+ __ IncrementCounter(counters->string_ctor_string_value(), 1);
+ __ jmp(&argument_is_string);
+
+ // Invoke the conversion builtin and put the result into rbx.
+ __ bind(&convert_argument);
+ __ IncrementCounter(counters->string_ctor_conversions(), 1);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(rdi); // Preserve the function.
+ __ push(rax);
+ __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
+ __ pop(rdi);
+ }
+ __ movq(rbx, rax);
+ __ jmp(&argument_is_string);
+
+ // Load the empty string into rbx, remove the receiver from the
+ // stack, and jump back to the case where the argument is a string.
+ __ bind(&no_arguments);
+ __ LoadRoot(rbx, Heap::kempty_stringRootIndex);
+ __ PopReturnAddressTo(rcx);
+ __ lea(rsp, Operand(rsp, kPointerSize));
+ __ PushReturnAddressFrom(rcx);
+ __ jmp(&argument_is_string);
+
+ // At this point the argument is already a string. Call runtime to
+ // create a string wrapper.
+ __ bind(&gc_required);
+ __ IncrementCounter(counters->string_ctor_gc_required(), 1);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(rbx);
+ __ CallRuntime(Runtime::kNewStringWrapper, 1);
+ }
+ __ ret(0);
+}
+
+
+static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
+ __ push(rbp);
+ __ movq(rbp, rsp);
+
+ // Store the arguments adaptor context sentinel.
+ __ Push(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+
+ // Push the function on the stack.
+ __ push(rdi);
+
+ // Preserve the number of arguments on the stack. Must preserve rax,
+ // rbx and rcx because these registers are used when copying the
+ // arguments and the receiver.
+ __ Integer32ToSmi(r8, rax);
+ __ push(r8);
+}
+
+
+static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
+ // Retrieve the number of arguments from the stack. Number is a Smi.
+ __ movq(rbx, Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ // Leave the frame.
+ __ movq(rsp, rbp);
+ __ pop(rbp);
+
+ // Remove caller arguments from the stack.
+ __ PopReturnAddressTo(rcx);
+ SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
+ __ lea(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize));
+ __ PushReturnAddressFrom(rcx);
+}
+
+
+void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : actual number of arguments
+ // -- rbx : expected number of arguments
+ // -- rcx : call kind information
+ // -- rdx : code entry to call
+ // -----------------------------------
+
+ Label invoke, dont_adapt_arguments;
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->arguments_adaptors(), 1);
+
+ Label enough, too_few;
+ __ cmpq(rax, rbx);
+ __ j(less, &too_few);
+ __ cmpq(rbx, Immediate(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
+ __ j(equal, &dont_adapt_arguments);
+
+ { // Enough parameters: Actual >= expected.
+ __ bind(&enough);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Copy receiver and all expected arguments.
+ const int offset = StandardFrameConstants::kCallerSPOffset;
+ __ lea(rax, Operand(rbp, rax, times_pointer_size, offset));
+ __ Set(r8, -1); // account for receiver
+
+ Label copy;
+ __ bind(&copy);
+ __ incq(r8);
+ __ push(Operand(rax, 0));
+ __ subq(rax, Immediate(kPointerSize));
+ __ cmpq(r8, rbx);
+ __ j(less, &copy);
+ __ jmp(&invoke);
+ }
+
+ { // Too few parameters: Actual < expected.
+ __ bind(&too_few);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Copy receiver and all actual arguments.
+ const int offset = StandardFrameConstants::kCallerSPOffset;
+ __ lea(rdi, Operand(rbp, rax, times_pointer_size, offset));
+ __ Set(r8, -1); // account for receiver
+
+ Label copy;
+ __ bind(&copy);
+ __ incq(r8);
+ __ push(Operand(rdi, 0));
+ __ subq(rdi, Immediate(kPointerSize));
+ __ cmpq(r8, rax);
+ __ j(less, &copy);
+
+ // Fill remaining expected arguments with undefined values.
+ Label fill;
+ __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
+ __ bind(&fill);
+ __ incq(r8);
+ __ push(kScratchRegister);
+ __ cmpq(r8, rbx);
+ __ j(less, &fill);
+
+ // Restore function pointer.
+ __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ }
+
+ // Call the entry point.
+ __ bind(&invoke);
+ __ call(rdx);
+
+ // Store offset of return address for deoptimizer.
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
+
+ // Leave frame and return.
+ LeaveArgumentsAdaptorFrame(masm);
+ __ ret(0);
+
+ // -------------------------------------------
+ // Dont adapt arguments.
+ // -------------------------------------------
+ __ bind(&dont_adapt_arguments);
+ __ jmp(rdx);
+}
+
+
+void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
+ __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+
+ // Pass the function to optimize as the argument to the on-stack
+ // replacement runtime function.
+ {
+ 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.
+ Label skip;
+ __ SmiCompare(rax, Smi::FromInt(-1));
+ __ j(not_equal, &skip, Label::kNear);
+ __ ret(0);
+
+ __ bind(&skip);
+ // Untag the AST id and push it on the stack.
+ __ SmiToInteger32(rax, rax);
+ __ push(rax);
+
+ // Generate the code for doing the frame-to-frame translation using
+ // the deoptimizer infrastructure.
+ Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR);
+ generator.Generate();
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/code-stubs-x64.cc b/chromium/v8/src/x64/code-stubs-x64.cc
new file mode 100644
index 00000000000..ad33a8c6319
--- /dev/null
+++ b/chromium/v8/src/x64/code-stubs-x64.cc
@@ -0,0 +1,6809 @@
+// Copyright 2013 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "bootstrapper.h"
+#include "code-stubs.h"
+#include "regexp-macro-assembler.h"
+#include "stub-cache.h"
+#include "runtime.h"
+
+namespace v8 {
+namespace internal {
+
+
+void ToNumberStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void FastCloneShallowArrayStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax, rbx, rcx };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kCreateArrayLiteralShallow)->entry;
+}
+
+
+void FastCloneShallowObjectStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax, rbx, rcx, rdx };
+ descriptor->register_param_count_ = 4;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kCreateObjectLiteralShallow)->entry;
+}
+
+
+void CreateAllocationSiteStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rbx };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedLoadFastElementStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rdx, rax };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure);
+}
+
+
+void LoadFieldStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedLoadFieldStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rdx };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ = NULL;
+}
+
+
+void KeyedStoreFastElementStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rdx, rcx, rax };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure);
+}
+
+
+void TransitionElementsKindStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax, rbx };
+ descriptor->register_param_count_ = 2;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry;
+}
+
+
+static void InitializeArrayConstructorDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor,
+ int constant_stack_parameter_count) {
+ // register state
+ // rax -- number of arguments
+ // rdi -- function
+ // rbx -- type info cell with elements kind
+ static Register registers[] = { rdi, rbx };
+ descriptor->register_param_count_ = 2;
+ if (constant_stack_parameter_count != 0) {
+ // stack param count needs (constructor pointer, and single argument)
+ descriptor->stack_parameter_count_ = &rax;
+ }
+ descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
+ descriptor->register_params_ = registers;
+ descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kArrayConstructor)->entry;
+}
+
+
+static void InitializeInternalArrayConstructorDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor,
+ int constant_stack_parameter_count) {
+ // register state
+ // rax -- number of arguments
+ // rdi -- constructor function
+ static Register registers[] = { rdi };
+ descriptor->register_param_count_ = 1;
+
+ if (constant_stack_parameter_count != 0) {
+ // stack param count needs (constructor pointer, and single argument)
+ descriptor->stack_parameter_count_ = &rax;
+ }
+ descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count;
+ descriptor->register_params_ = registers;
+ descriptor->function_mode_ = JS_FUNCTION_STUB_MODE;
+ descriptor->deoptimization_handler_ =
+ Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry;
+}
+
+
+void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, 0);
+}
+
+
+void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, 1);
+}
+
+
+void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeArrayConstructorDescriptor(isolate, descriptor, -1);
+}
+
+
+void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0);
+}
+
+
+void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1);
+}
+
+
+void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1);
+}
+
+
+void CompareNilICStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(CompareNilIC_Miss);
+ descriptor->SetMissHandler(
+ ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate));
+}
+
+
+void ToBooleanStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax };
+ descriptor->register_param_count_ = 1;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(ToBooleanIC_Miss);
+ descriptor->SetMissHandler(
+ ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate));
+}
+
+
+void StoreGlobalStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rdx, rcx, rax };
+ descriptor->register_param_count_ = 3;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(StoreIC_MissFromStubFailure);
+}
+
+
+void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
+ Isolate* isolate,
+ CodeStubInterfaceDescriptor* descriptor) {
+ static Register registers[] = { rax, rbx, rcx, rdx };
+ descriptor->register_param_count_ = 4;
+ descriptor->register_params_ = registers;
+ descriptor->deoptimization_handler_ =
+ FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss);
+}
+
+
+#define __ ACCESS_MASM(masm)
+
+
+void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
+ // Update the static counter each time a new code stub is generated.
+ Isolate* isolate = masm->isolate();
+ isolate->counters()->code_stubs()->Increment();
+
+ CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate);
+ int param_count = descriptor->register_param_count_;
+ {
+ // Call the runtime system in a fresh internal frame.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ ASSERT(descriptor->register_param_count_ == 0 ||
+ rax.is(descriptor->register_params_[param_count - 1]));
+ // Push arguments
+ for (int i = 0; i < param_count; ++i) {
+ __ push(descriptor->register_params_[i]);
+ }
+ ExternalReference miss = descriptor->miss_handler();
+ __ CallExternalReference(miss, descriptor->register_param_count_);
+ }
+
+ __ Ret();
+}
+
+
+void FastNewClosureStub::Generate(MacroAssembler* masm) {
+ // Create a new closure from the given function info in new
+ // space. Set the context to the current context in rsi.
+ Counters* counters = masm->isolate()->counters();
+
+ Label gc;
+ __ Allocate(JSFunction::kSize, rax, rbx, rcx, &gc, TAG_OBJECT);
+
+ __ IncrementCounter(counters->fast_new_closure_total(), 1);
+
+ // Get the function info from the stack.
+ __ movq(rdx, Operand(rsp, 1 * kPointerSize));
+
+ int map_index = Context::FunctionMapIndex(language_mode_, is_generator_);
+
+ // Compute the function map in the current native context and set that
+ // as the map of the allocated object.
+ __ movq(rcx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ movq(rcx, FieldOperand(rcx, GlobalObject::kNativeContextOffset));
+ __ movq(rbx, Operand(rcx, Context::SlotOffset(map_index)));
+ __ movq(FieldOperand(rax, JSObject::kMapOffset), rbx);
+
+ // Initialize the rest of the function. We don't have to update the
+ // write barrier because the allocated object is in new space.
+ __ LoadRoot(rbx, Heap::kEmptyFixedArrayRootIndex);
+ __ LoadRoot(r8, Heap::kTheHoleValueRootIndex);
+ __ LoadRoot(rdi, Heap::kUndefinedValueRootIndex);
+ __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), rbx);
+ __ movq(FieldOperand(rax, JSObject::kElementsOffset), rbx);
+ __ movq(FieldOperand(rax, JSFunction::kPrototypeOrInitialMapOffset), r8);
+ __ movq(FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset), rdx);
+ __ movq(FieldOperand(rax, JSFunction::kContextOffset), rsi);
+ __ movq(FieldOperand(rax, JSFunction::kLiteralsOffset), rbx);
+
+ // Initialize the code pointer in the function to be the one
+ // found in the shared function info object.
+ // But first check if there is an optimized version for our context.
+ Label check_optimized;
+ Label install_unoptimized;
+ if (FLAG_cache_optimized_code) {
+ __ movq(rbx,
+ FieldOperand(rdx, SharedFunctionInfo::kOptimizedCodeMapOffset));
+ __ testq(rbx, rbx);
+ __ j(not_zero, &check_optimized, Label::kNear);
+ }
+ __ bind(&install_unoptimized);
+ __ movq(FieldOperand(rax, JSFunction::kNextFunctionLinkOffset),
+ rdi); // Initialize with undefined.
+ __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
+ __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
+ __ movq(FieldOperand(rax, JSFunction::kCodeEntryOffset), rdx);
+
+ // Return and remove the on-stack parameter.
+ __ ret(1 * kPointerSize);
+
+ __ bind(&check_optimized);
+
+ __ IncrementCounter(counters->fast_new_closure_try_optimized(), 1);
+
+ // rcx holds native context, rbx points to fixed array of 3-element entries
+ // (native context, optimized code, literals).
+ // The optimized code map must never be empty, so check the first elements.
+ Label install_optimized;
+ // Speculatively move code object into edx.
+ __ movq(rdx, FieldOperand(rbx, SharedFunctionInfo::kFirstCodeSlot));
+ __ cmpq(rcx, FieldOperand(rbx, SharedFunctionInfo::kFirstContextSlot));
+ __ j(equal, &install_optimized);
+
+ // Iterate through the rest of map backwards. rdx holds an index.
+ Label loop;
+ Label restore;
+ __ movq(rdx, FieldOperand(rbx, FixedArray::kLengthOffset));
+ __ SmiToInteger32(rdx, rdx);
+ __ bind(&loop);
+ // Do not double check first entry.
+ __ cmpq(rdx, Immediate(SharedFunctionInfo::kSecondEntryIndex));
+ __ j(equal, &restore);
+ __ subq(rdx, Immediate(SharedFunctionInfo::kEntryLength));
+ __ cmpq(rcx, FieldOperand(rbx,
+ rdx,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ j(not_equal, &loop, Label::kNear);
+ // Hit: fetch the optimized code.
+ __ movq(rdx, FieldOperand(rbx,
+ rdx,
+ times_pointer_size,
+ FixedArray::kHeaderSize + 1 * kPointerSize));
+
+ __ bind(&install_optimized);
+ __ IncrementCounter(counters->fast_new_closure_install_optimized(), 1);
+
+ // TODO(fschneider): Idea: store proper code pointers in the map and either
+ // unmangle them on marking or do nothing as the whole map is discarded on
+ // major GC anyway.
+ __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
+ __ movq(FieldOperand(rax, JSFunction::kCodeEntryOffset), rdx);
+
+ // Now link a function into a list of optimized functions.
+ __ movq(rdx, ContextOperand(rcx, Context::OPTIMIZED_FUNCTIONS_LIST));
+
+ __ movq(FieldOperand(rax, JSFunction::kNextFunctionLinkOffset), rdx);
+ // No need for write barrier as JSFunction (rax) is in the new space.
+
+ __ movq(ContextOperand(rcx, Context::OPTIMIZED_FUNCTIONS_LIST), rax);
+ // Store JSFunction (rax) into rdx before issuing write barrier as
+ // it clobbers all the registers passed.
+ __ movq(rdx, rax);
+ __ RecordWriteContextSlot(
+ rcx,
+ Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST),
+ rdx,
+ rbx,
+ kDontSaveFPRegs);
+
+ // Return and remove the on-stack parameter.
+ __ ret(1 * kPointerSize);
+
+ __ bind(&restore);
+ __ movq(rdx, Operand(rsp, 1 * kPointerSize));
+ __ jmp(&install_unoptimized);
+
+ // Create a new closure through the slower runtime call.
+ __ bind(&gc);
+ __ PopReturnAddressTo(rcx);
+ __ pop(rdx);
+ __ push(rsi);
+ __ push(rdx);
+ __ PushRoot(Heap::kFalseValueRootIndex);
+ __ PushReturnAddressFrom(rcx);
+ __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
+}
+
+
+void FastNewContextStub::Generate(MacroAssembler* masm) {
+ // Try to allocate the context in new space.
+ Label gc;
+ int length = slots_ + Context::MIN_CONTEXT_SLOTS;
+ __ Allocate((length * kPointerSize) + FixedArray::kHeaderSize,
+ rax, rbx, rcx, &gc, TAG_OBJECT);
+
+ // Get the function from the stack.
+ __ movq(rcx, Operand(rsp, 1 * kPointerSize));
+
+ // Set up the object header.
+ __ LoadRoot(kScratchRegister, Heap::kFunctionContextMapRootIndex);
+ __ movq(FieldOperand(rax, HeapObject::kMapOffset), kScratchRegister);
+ __ Move(FieldOperand(rax, FixedArray::kLengthOffset), Smi::FromInt(length));
+
+ // 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);
+ __ movq(Operand(rax, Context::SlotOffset(Context::EXTENSION_INDEX)), rbx);
+
+ // Copy the global object from the previous context.
+ __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ movq(Operand(rax, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)), rbx);
+
+ // Initialize the rest of the slots to undefined.
+ __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex);
+ for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
+ __ movq(Operand(rax, Context::SlotOffset(i)), rbx);
+ }
+
+ // Return and remove the on-stack parameter.
+ __ movq(rsi, rax);
+ __ ret(1 * kPointerSize);
+
+ // Need to collect. Call into runtime system.
+ __ bind(&gc);
+ __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
+}
+
+
+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;
+ __ Allocate(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 native context we get a smi
+ // sentinel instead of a function. The block context should get the
+ // canonical empty function of the native context as its closure which
+ // we still have to look up.
+ Label after_sentinel;
+ __ JumpIfNotSmi(rcx, &after_sentinel, Label::kNear);
+ if (FLAG_debug_code) {
+ __ cmpq(rcx, Immediate(0));
+ __ Assert(equal, kExpected0AsASmiSentinel);
+ }
+ __ movq(rcx, GlobalObjectOperand());
+ __ movq(rcx, FieldOperand(rcx, GlobalObject::kNativeContextOffset));
+ __ 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_OBJECT_INDEX));
+ __ movq(ContextOperand(rax, Context::GLOBAL_OBJECT_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);
+}
+
+
+void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
+ __ PushCallerSaved(save_doubles_);
+ const int argument_count = 1;
+ __ PrepareCallCFunction(argument_count);
+ __ LoadAddress(arg_reg_1,
+ ExternalReference::isolate_address(masm->isolate()));
+
+ AllowExternalCallThatCantCauseGC scope(masm);
+ __ CallCFunction(
+ ExternalReference::store_buffer_overflow_function(masm->isolate()),
+ argument_count);
+ __ PopCallerSaved(save_doubles_);
+ __ ret(0);
+}
+
+
+class FloatingPointHelper : public AllStatic {
+ public:
+ enum ConvertUndefined {
+ CONVERT_UNDEFINED_TO_ZERO,
+ BAILOUT_ON_UNDEFINED
+ };
+ // Load the operands from rdx and rax into xmm0 and xmm1, as doubles.
+ // If the operands are not both numbers, jump to not_numbers.
+ // Leaves rdx and rax unchanged. SmiOperands assumes both are smis.
+ // NumberOperands assumes both are smis or heap numbers.
+ static void LoadSSE2SmiOperands(MacroAssembler* masm);
+ static void LoadSSE2NumberOperands(MacroAssembler* masm);
+ static void LoadSSE2UnknownOperands(MacroAssembler* masm,
+ Label* not_numbers);
+
+ // Takes the operands in rdx and rax and loads them as integers in rax
+ // and rcx.
+ static void LoadAsIntegers(MacroAssembler* masm,
+ Label* operand_conversion_failure,
+ Register heap_number_map);
+ // As above, but we know the operands to be numbers. In that case,
+ // conversion can't fail.
+ static void LoadNumbersAsIntegers(MacroAssembler* masm);
+
+ // Tries to convert two values to smis losslessly.
+ // This fails if either argument is not a Smi nor a HeapNumber,
+ // or if it's a HeapNumber with a value that can't be converted
+ // losslessly to a Smi. In that case, control transitions to the
+ // on_not_smis label.
+ // On success, either control goes to the on_success label (if one is
+ // provided), or it falls through at the end of the code (if on_success
+ // is NULL).
+ // On success, both first and second holds Smi tagged values.
+ // One of first or second must be non-Smi when entering.
+ static void NumbersToSmis(MacroAssembler* masm,
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* on_success,
+ Label* on_not_smis,
+ ConvertUndefined convert_undefined);
+};
+
+
+void DoubleToIStub::Generate(MacroAssembler* masm) {
+ Register input_reg = this->source();
+ Register final_result_reg = this->destination();
+ ASSERT(is_truncating());
+
+ Label check_negative, process_64_bits, done;
+
+ int double_offset = offset();
+
+ // Account for return address and saved regs if input is rsp.
+ if (input_reg.is(rsp)) double_offset += 3 * kPointerSize;
+
+ MemOperand mantissa_operand(MemOperand(input_reg, double_offset));
+ MemOperand exponent_operand(MemOperand(input_reg,
+ double_offset + kDoubleSize / 2));
+
+ Register scratch1;
+ Register scratch_candidates[3] = { rbx, rdx, rdi };
+ for (int i = 0; i < 3; i++) {
+ scratch1 = scratch_candidates[i];
+ if (!final_result_reg.is(scratch1) && !input_reg.is(scratch1)) break;
+ }
+
+ // Since we must use rcx for shifts below, use some other register (rax)
+ // to calculate the result if ecx is the requested return register.
+ Register result_reg = final_result_reg.is(rcx) ? rax : final_result_reg;
+ // Save ecx if it isn't the return register and therefore volatile, or if it
+ // is the return register, then save the temp register we use in its stead
+ // for the result.
+ Register save_reg = final_result_reg.is(rcx) ? rax : rcx;
+ __ push(scratch1);
+ __ push(save_reg);
+
+ bool stash_exponent_copy = !input_reg.is(rsp);
+ __ movl(scratch1, mantissa_operand);
+ __ movsd(xmm0, mantissa_operand);
+ __ movl(rcx, exponent_operand);
+ if (stash_exponent_copy) __ push(rcx);
+
+ __ andl(rcx, Immediate(HeapNumber::kExponentMask));
+ __ shrl(rcx, Immediate(HeapNumber::kExponentShift));
+ __ leal(result_reg, MemOperand(rcx, -HeapNumber::kExponentBias));
+ __ cmpl(result_reg, Immediate(HeapNumber::kMantissaBits));
+ __ j(below, &process_64_bits);
+
+ // Result is entirely in lower 32-bits of mantissa
+ int delta = HeapNumber::kExponentBias + Double::kPhysicalSignificandSize;
+ __ subl(rcx, Immediate(delta));
+ __ xorl(result_reg, result_reg);
+ __ cmpl(rcx, Immediate(31));
+ __ j(above, &done);
+ __ shll_cl(scratch1);
+ __ jmp(&check_negative);
+
+ __ bind(&process_64_bits);
+ __ cvttsd2siq(result_reg, xmm0);
+ __ jmp(&done, Label::kNear);
+
+ // If the double was negative, negate the integer result.
+ __ bind(&check_negative);
+ __ movl(result_reg, scratch1);
+ __ negl(result_reg);
+ if (stash_exponent_copy) {
+ __ cmpl(MemOperand(rsp, 0), Immediate(0));
+ } else {
+ __ cmpl(exponent_operand, Immediate(0));
+ }
+ __ cmovl(greater, result_reg, scratch1);
+
+ // Restore registers
+ __ bind(&done);
+ if (stash_exponent_copy) {
+ __ addq(rsp, Immediate(kDoubleSize));
+ }
+ if (!final_result_reg.is(result_reg)) {
+ ASSERT(final_result_reg.is(rcx));
+ __ movl(final_result_reg, result_reg);
+ }
+ __ pop(save_reg);
+ __ pop(scratch1);
+ __ ret(0);
+}
+
+
+void BinaryOpStub::Initialize() {}
+
+
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ __ PopReturnAddressTo(rcx);
+ __ push(rdx);
+ __ push(rax);
+ // Left and right arguments are now on top.
+ __ Push(Smi::FromInt(MinorKey()));
+
+ __ PushReturnAddressFrom(rcx);
+
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
+ masm->isolate()),
+ 3,
+ 1);
+}
+
+
+static void BinaryOpStub_GenerateSmiCode(
+ MacroAssembler* masm,
+ Label* slow,
+ BinaryOpStub::SmiCodeGenerateHeapNumberResults allow_heapnumber_results,
+ Token::Value op) {
+
+ // Arguments to BinaryOpStub are in rdx and rax.
+ const Register left = rdx;
+ const Register right = rax;
+
+ // We only generate heapnumber answers for overflowing calculations
+ // for the four basic arithmetic operations and logical right shift by 0.
+ bool generate_inline_heapnumber_results =
+ (allow_heapnumber_results == BinaryOpStub::ALLOW_HEAPNUMBER_RESULTS) &&
+ (op == Token::ADD || op == Token::SUB ||
+ op == Token::MUL || op == Token::DIV || op == Token::SHR);
+
+ // Smi check of both operands. If op is BIT_OR, the check is delayed
+ // until after the OR operation.
+ Label not_smis;
+ Label use_fp_on_smis;
+ Label fail;
+
+ if (op != Token::BIT_OR) {
+ Comment smi_check_comment(masm, "-- Smi check arguments");
+ __ JumpIfNotBothSmi(left, right, &not_smis);
+ }
+
+ Label smi_values;
+ __ bind(&smi_values);
+ // Perform the operation.
+ Comment perform_smi(masm, "-- Perform smi operation");
+ switch (op) {
+ case Token::ADD:
+ ASSERT(right.is(rax));
+ __ SmiAdd(right, right, left, &use_fp_on_smis); // ADD is commutative.
+ break;
+
+ case Token::SUB:
+ __ SmiSub(left, left, right, &use_fp_on_smis);
+ __ movq(rax, left);
+ break;
+
+ case Token::MUL:
+ ASSERT(right.is(rax));
+ __ SmiMul(right, right, left, &use_fp_on_smis); // MUL is commutative.
+ break;
+
+ case Token::DIV:
+ // SmiDiv will not accept left in rdx or right in rax.
+ __ movq(rbx, rax);
+ __ movq(rcx, rdx);
+ __ SmiDiv(rax, rcx, rbx, &use_fp_on_smis);
+ break;
+
+ case Token::MOD:
+ // SmiMod will not accept left in rdx or right in rax.
+ __ movq(rbx, rax);
+ __ movq(rcx, rdx);
+ __ SmiMod(rax, rcx, rbx, &use_fp_on_smis);
+ break;
+
+ case Token::BIT_OR: {
+ ASSERT(right.is(rax));
+ __ SmiOrIfSmis(right, right, left, &not_smis); // BIT_OR is commutative.
+ break;
+ }
+ case Token::BIT_XOR:
+ ASSERT(right.is(rax));
+ __ SmiXor(right, right, left); // BIT_XOR is commutative.
+ break;
+
+ case Token::BIT_AND:
+ ASSERT(right.is(rax));
+ __ SmiAnd(right, right, left); // BIT_AND is commutative.
+ break;
+
+ case Token::SHL:
+ __ SmiShiftLeft(left, left, right);
+ __ movq(rax, left);
+ break;
+
+ case Token::SAR:
+ __ SmiShiftArithmeticRight(left, left, right);
+ __ movq(rax, left);
+ break;
+
+ case Token::SHR:
+ __ SmiShiftLogicalRight(left, left, right, &use_fp_on_smis);
+ __ movq(rax, left);
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ // 5. Emit return of result in rax. Some operations have registers pushed.
+ __ ret(0);
+
+ if (use_fp_on_smis.is_linked()) {
+ // 6. For some operations emit inline code to perform floating point
+ // operations on known smis (e.g., if the result of the operation
+ // overflowed the smi range).
+ __ bind(&use_fp_on_smis);
+ if (op == Token::DIV || op == Token::MOD) {
+ // Restore left and right to rdx and rax.
+ __ movq(rdx, rcx);
+ __ movq(rax, rbx);
+ }
+
+ if (generate_inline_heapnumber_results) {
+ __ AllocateHeapNumber(rcx, rbx, slow);
+ Comment perform_float(masm, "-- Perform float operation on smis");
+ if (op == Token::SHR) {
+ __ SmiToInteger32(left, left);
+ __ cvtqsi2sd(xmm0, left);
+ } else {
+ FloatingPointHelper::LoadSSE2SmiOperands(masm);
+ switch (op) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ }
+ __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
+ __ movq(rax, rcx);
+ __ ret(0);
+ } else {
+ __ jmp(&fail);
+ }
+ }
+
+ // 7. Non-smi operands reach the end of the code generated by
+ // GenerateSmiCode, and fall through to subsequent code,
+ // with the operands in rdx and rax.
+ // But first we check if non-smi values are HeapNumbers holding
+ // values that could be smi.
+ __ bind(&not_smis);
+ Comment done_comment(masm, "-- Enter non-smi code");
+ FloatingPointHelper::ConvertUndefined convert_undefined =
+ FloatingPointHelper::BAILOUT_ON_UNDEFINED;
+ // This list must be in sync with BinaryOpPatch() behavior in ic.cc.
+ if (op == Token::BIT_AND ||
+ op == Token::BIT_OR ||
+ op == Token::BIT_XOR ||
+ op == Token::SAR ||
+ op == Token::SHL ||
+ op == Token::SHR) {
+ convert_undefined = FloatingPointHelper::CONVERT_UNDEFINED_TO_ZERO;
+ }
+ FloatingPointHelper::NumbersToSmis(masm, left, right, rbx, rdi, rcx,
+ &smi_values, &fail, convert_undefined);
+ __ jmp(&smi_values);
+ __ bind(&fail);
+}
+
+
+static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
+ Label* alloc_failure,
+ OverwriteMode mode);
+
+
+static void BinaryOpStub_GenerateFloatingPointCode(MacroAssembler* masm,
+ Label* allocation_failure,
+ Label* non_numeric_failure,
+ Token::Value op,
+ OverwriteMode mode) {
+ switch (op) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV: {
+ FloatingPointHelper::LoadSSE2UnknownOperands(masm, non_numeric_failure);
+
+ switch (op) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ BinaryOpStub_GenerateHeapResultAllocation(
+ masm, allocation_failure, mode);
+ __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0);
+ __ ret(0);
+ break;
+ }
+ case Token::MOD: {
+ // For MOD we jump to the allocation_failure label, to call runtime.
+ __ jmp(allocation_failure);
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR: {
+ Label non_smi_shr_result;
+ Register heap_number_map = r9;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+ FloatingPointHelper::LoadAsIntegers(masm, non_numeric_failure,
+ heap_number_map);
+ switch (op) {
+ case Token::BIT_OR: __ orl(rax, rcx); break;
+ case Token::BIT_AND: __ andl(rax, rcx); break;
+ case Token::BIT_XOR: __ xorl(rax, rcx); break;
+ case Token::SAR: __ sarl_cl(rax); break;
+ case Token::SHL: __ shll_cl(rax); break;
+ case Token::SHR: {
+ __ shrl_cl(rax);
+ // Check if result is negative. This can only happen for a shift
+ // by zero.
+ __ testl(rax, rax);
+ __ j(negative, &non_smi_shr_result);
+ break;
+ }
+ default: UNREACHABLE();
+ }
+ STATIC_ASSERT(kSmiValueSize == 32);
+ // Tag smi result and return.
+ __ Integer32ToSmi(rax, rax);
+ __ Ret();
+
+ // Logical shift right can produce an unsigned int32 that is not
+ // an int32, and so is not in the smi range. Allocate a heap number
+ // in that case.
+ if (op == Token::SHR) {
+ __ bind(&non_smi_shr_result);
+ Label allocation_failed;
+ __ movl(rbx, rax); // rbx holds result value (uint32 value as int64).
+ // Allocate heap number in new space.
+ // Not using AllocateHeapNumber macro in order to reuse
+ // already loaded heap_number_map.
+ __ Allocate(HeapNumber::kSize, rax, rdx, no_reg, &allocation_failed,
+ TAG_OBJECT);
+ // Set the map.
+ __ AssertRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ kHeapNumberMapRegisterClobbered);
+ __ movq(FieldOperand(rax, HeapObject::kMapOffset),
+ heap_number_map);
+ __ cvtqsi2sd(xmm0, rbx);
+ __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm0);
+ __ Ret();
+
+ __ bind(&allocation_failed);
+ // We need tagged values in rdx and rax for the following code,
+ // not int32 in rax and rcx.
+ __ Integer32ToSmi(rax, rcx);
+ __ Integer32ToSmi(rdx, rbx);
+ __ jmp(allocation_failure);
+ }
+ break;
+ }
+ default: UNREACHABLE(); break;
+ }
+ // No fall-through from this generated code.
+ if (FLAG_debug_code) {
+ __ Abort(kUnexpectedFallThroughInBinaryStubGenerateFloatingPointCode);
+ }
+}
+
+
+static void BinaryOpStub_GenerateRegisterArgsPushUnderReturn(
+ MacroAssembler* masm) {
+ // Push arguments, but ensure they are under the return address
+ // for a tail call.
+ __ PopReturnAddressTo(rcx);
+ __ push(rdx);
+ __ push(rax);
+ __ PushReturnAddressFrom(rcx);
+}
+
+
+void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
+ ASSERT(op_ == Token::ADD);
+ Label left_not_string, call_runtime;
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &left_not_string, Label::kNear);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &left_not_string, Label::kNear);
+ StringAddStub string_add_left_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_RIGHT | STRING_ADD_ERECT_FRAME));
+ BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm);
+ __ TailCallStub(&string_add_left_stub);
+
+ // Left operand is not a string, test right.
+ __ bind(&left_not_string);
+ __ JumpIfSmi(right, &call_runtime, Label::kNear);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime, Label::kNear);
+
+ StringAddStub string_add_right_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_LEFT | STRING_ADD_ERECT_FRAME));
+ BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm);
+ __ TailCallStub(&string_add_right_stub);
+
+ // Neither argument is a string.
+ __ bind(&call_runtime);
+}
+
+
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ Label right_arg_changed, call_runtime;
+
+ if (op_ == Token::MOD && encoded_right_arg_.has_value) {
+ // It is guaranteed that the value will fit into a Smi, because if it
+ // didn't, we wouldn't be here, see BinaryOp_Patch.
+ __ Cmp(rax, Smi::FromInt(fixed_right_arg_value()));
+ __ j(not_equal, &right_arg_changed);
+ }
+
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
+ // Only allow smi results.
+ BinaryOpStub_GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS, op_);
+ } else {
+ // Allow heap number result and don't make a transition if a heap number
+ // cannot be allocated.
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
+ }
+
+ // Code falls through if the result is not returned as either a smi or heap
+ // number.
+ __ bind(&right_arg_changed);
+ GenerateTypeTransition(masm);
+
+ if (call_runtime.is_linked()) {
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+ }
+}
+
+
+void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+ // The int32 case is identical to the Smi case. We avoid creating this
+ // ic state on x64.
+ UNREACHABLE();
+}
+
+
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(left_type_ == BinaryOpIC::STRING && right_type_ == BinaryOpIC::STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime);
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime);
+
+ StringAddStub string_add_stub(
+ (StringAddFlags)(STRING_ADD_CHECK_NONE | STRING_ADD_ERECT_FRAME));
+ BinaryOpStub_GenerateRegisterArgsPushUnderReturn(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
+ Label call_runtime;
+
+ if (op_ == Token::ADD) {
+ // Handle string addition here, because it is the only operation
+ // that does not do a ToNumber conversion on the operands.
+ GenerateAddStrings(masm);
+ }
+
+ // Convert oddball arguments to numbers.
+ Label check, done;
+ __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, &check, Label::kNear);
+ if (Token::IsBitOp(op_)) {
+ __ xor_(rdx, rdx);
+ } else {
+ __ LoadRoot(rdx, Heap::kNanValueRootIndex);
+ }
+ __ jmp(&done, Label::kNear);
+ __ bind(&check);
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, &done, Label::kNear);
+ if (Token::IsBitOp(op_)) {
+ __ xor_(rax, rax);
+ } else {
+ __ LoadRoot(rax, Heap::kNanValueRootIndex);
+ }
+ __ bind(&done);
+
+ GenerateNumberStub(masm);
+}
+
+
+static void BinaryOpStub_CheckSmiInput(MacroAssembler* masm,
+ Register input,
+ Label* fail) {
+ Label ok;
+ __ JumpIfSmi(input, &ok, Label::kNear);
+ Register heap_number_map = r8;
+ Register scratch1 = r9;
+ Register scratch2 = r10;
+ // HeapNumbers containing 32bit integer values are also allowed.
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+ __ cmpq(FieldOperand(input, HeapObject::kMapOffset), heap_number_map);
+ __ j(not_equal, fail);
+ __ movsd(xmm0, FieldOperand(input, HeapNumber::kValueOffset));
+ // Convert, convert back, and compare the two doubles' bits.
+ __ cvttsd2siq(scratch2, xmm0);
+ __ cvtlsi2sd(xmm1, scratch2);
+ __ movq(scratch1, xmm0);
+ __ movq(scratch2, xmm1);
+ __ cmpq(scratch1, scratch2);
+ __ j(not_equal, fail);
+ __ bind(&ok);
+}
+
+
+void BinaryOpStub::GenerateNumberStub(MacroAssembler* masm) {
+ Label gc_required, not_number;
+
+ // It could be that only SMIs have been seen at either the left
+ // or the right operand. For precise type feedback, patch the IC
+ // again if this changes.
+ if (left_type_ == BinaryOpIC::SMI) {
+ BinaryOpStub_CheckSmiInput(masm, rdx, &not_number);
+ }
+ if (right_type_ == BinaryOpIC::SMI) {
+ BinaryOpStub_CheckSmiInput(masm, rax, &not_number);
+ }
+
+ BinaryOpStub_GenerateFloatingPointCode(
+ masm, &gc_required, &not_number, op_, mode_);
+
+ __ bind(&not_number);
+ GenerateTypeTransition(masm);
+
+ __ bind(&gc_required);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+ Label call_runtime, call_string_add_or_runtime;
+
+ BinaryOpStub_GenerateSmiCode(
+ masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS, op_);
+
+ BinaryOpStub_GenerateFloatingPointCode(
+ masm, &call_runtime, &call_string_add_or_runtime, op_, mode_);
+
+ __ bind(&call_string_add_or_runtime);
+ if (op_ == Token::ADD) {
+ GenerateAddStrings(masm);
+ }
+
+ __ bind(&call_runtime);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ GenerateCallRuntime(masm);
+ }
+ __ Ret();
+}
+
+
+static void BinaryOpStub_GenerateHeapResultAllocation(MacroAssembler* masm,
+ Label* alloc_failure,
+ OverwriteMode mode) {
+ Label skip_allocation;
+ switch (mode) {
+ case OVERWRITE_LEFT: {
+ // If the argument in rdx is already an object, we skip the
+ // allocation of a heap number.
+ __ JumpIfNotSmi(rdx, &skip_allocation);
+ // Allocate a heap number for the result. Keep rax and rdx intact
+ // for the possible runtime call.
+ __ AllocateHeapNumber(rbx, rcx, alloc_failure);
+ // Now rdx can be overwritten losing one of the arguments as we are
+ // now done and will not need it any more.
+ __ movq(rdx, rbx);
+ __ bind(&skip_allocation);
+ // Use object in rdx as a result holder
+ __ movq(rax, rdx);
+ break;
+ }
+ case OVERWRITE_RIGHT:
+ // If the argument in rax is already an object, we skip the
+ // allocation of a heap number.
+ __ JumpIfNotSmi(rax, &skip_allocation);
+ // Fall through!
+ case NO_OVERWRITE:
+ // Allocate a heap number for the result. Keep rax and rdx intact
+ // for the possible runtime call.
+ __ AllocateHeapNumber(rbx, rcx, alloc_failure);
+ // Now rax can be overwritten losing one of the arguments as we are
+ // now done and will not need it any more.
+ __ movq(rax, rbx);
+ __ bind(&skip_allocation);
+ break;
+ default: UNREACHABLE();
+ }
+}
+
+
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ push(rdx);
+ __ push(rax);
+}
+
+
+void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
+ // TAGGED case:
+ // Input:
+ // rsp[8] : argument (should be number).
+ // rsp[0] : return address.
+ // Output:
+ // rax: tagged double result.
+ // UNTAGGED case:
+ // Input::
+ // rsp[0] : return address.
+ // xmm1 : untagged double input argument
+ // Output:
+ // xmm1 : untagged double result.
+
+ Label runtime_call;
+ Label runtime_call_clear_stack;
+ Label skip_cache;
+ const bool tagged = (argument_type_ == TAGGED);
+ if (tagged) {
+ Label input_not_smi, loaded;
+ // Test that rax is a number.
+ __ movq(rax, Operand(rsp, kPointerSize));
+ __ JumpIfNotSmi(rax, &input_not_smi, Label::kNear);
+ // Input is a smi. Untag and load it onto the FPU stack.
+ // Then load the bits of the double into rbx.
+ __ SmiToInteger32(rax, rax);
+ __ subq(rsp, Immediate(kDoubleSize));
+ __ cvtlsi2sd(xmm1, rax);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ movq(rbx, xmm1);
+ __ movq(rdx, xmm1);
+ __ fld_d(Operand(rsp, 0));
+ __ addq(rsp, Immediate(kDoubleSize));
+ __ jmp(&loaded, Label::kNear);
+
+ __ bind(&input_not_smi);
+ // Check if input is a HeapNumber.
+ __ LoadRoot(rbx, Heap::kHeapNumberMapRootIndex);
+ __ cmpq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+ __ j(not_equal, &runtime_call);
+ // Input is a HeapNumber. Push it on the FPU stack and load its
+ // bits into rbx.
+ __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset));
+ __ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ movq(rdx, rbx);
+
+ __ bind(&loaded);
+ } else { // UNTAGGED.
+ __ movq(rbx, xmm1);
+ __ movq(rdx, xmm1);
+ }
+
+ // ST[0] == double value, if TAGGED.
+ // rbx = bits of double value.
+ // rdx = also bits of double value.
+ // Compute hash (h is 32 bits, bits are 64 and the shifts are arithmetic):
+ // h = h0 = bits ^ (bits >> 32);
+ // h ^= h >> 16;
+ // h ^= h >> 8;
+ // h = h & (cacheSize - 1);
+ // or h = (h0 ^ (h0 >> 8) ^ (h0 >> 16) ^ (h0 >> 24)) & (cacheSize - 1)
+ __ sar(rdx, Immediate(32));
+ __ xorl(rdx, rbx);
+ __ movl(rcx, rdx);
+ __ movl(rax, rdx);
+ __ movl(rdi, rdx);
+ __ sarl(rdx, Immediate(8));
+ __ sarl(rcx, Immediate(16));
+ __ sarl(rax, Immediate(24));
+ __ xorl(rcx, rdx);
+ __ xorl(rax, rdi);
+ __ xorl(rcx, rax);
+ ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
+ __ andl(rcx, Immediate(TranscendentalCache::SubCache::kCacheSize - 1));
+
+ // ST[0] == double value.
+ // rbx = bits of double value.
+ // rcx = TranscendentalCache::hash(double value).
+ ExternalReference cache_array =
+ ExternalReference::transcendental_cache_array_address(masm->isolate());
+ __ movq(rax, cache_array);
+ int cache_array_index =
+ type_ * sizeof(Isolate::Current()->transcendental_cache()->caches_[0]);
+ __ movq(rax, Operand(rax, cache_array_index));
+ // rax points to the cache for the type type_.
+ // If NULL, the cache hasn't been initialized yet, so go through runtime.
+ __ testq(rax, rax);
+ __ j(zero, &runtime_call_clear_stack); // Only clears stack if TAGGED.
+#ifdef DEBUG
+ // Check that the layout of cache elements match expectations.
+ { // NOLINT - doesn't like a single brace on a line.
+ TranscendentalCache::SubCache::Element test_elem[2];
+ char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
+ char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
+ char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
+ char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
+ char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
+ // Two uint_32's and a pointer per element.
+ CHECK_EQ(2 * kIntSize + 1 * kPointerSize,
+ static_cast<int>(elem2_start - elem_start));
+ CHECK_EQ(0, static_cast<int>(elem_in0 - elem_start));
+ CHECK_EQ(kIntSize, static_cast<int>(elem_in1 - elem_start));
+ CHECK_EQ(2 * kIntSize, static_cast<int>(elem_out - elem_start));
+ }
+#endif
+ // Find the address of the rcx'th entry in the cache, i.e., &rax[rcx*16].
+ __ addl(rcx, rcx);
+ __ lea(rcx, Operand(rax, rcx, times_8, 0));
+ // Check if cache matches: Double value is stored in uint32_t[2] array.
+ Label cache_miss;
+ __ 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.
+ __ ret(kPointerSize);
+ } else { // UNTAGGED.
+ __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ Ret();
+ }
+
+ __ bind(&cache_miss);
+ __ IncrementCounter(counters->transcendental_cache_miss(), 1);
+ // Update cache with new value.
+ if (tagged) {
+ __ AllocateHeapNumber(rax, rdi, &runtime_call_clear_stack);
+ } else { // UNTAGGED.
+ __ AllocateHeapNumber(rax, rdi, &skip_cache);
+ __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm1);
+ __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset));
+ }
+ GenerateOperation(masm, type_);
+ __ movq(Operand(rcx, 0), rbx);
+ __ movq(Operand(rcx, 2 * kIntSize), rax);
+ __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset));
+ if (tagged) {
+ __ ret(kPointerSize);
+ } else { // UNTAGGED.
+ __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ Ret();
+
+ // Skip cache and return answer directly, only in untagged case.
+ __ bind(&skip_cache);
+ __ subq(rsp, Immediate(kDoubleSize));
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ fld_d(Operand(rsp, 0));
+ GenerateOperation(masm, type_);
+ __ fstp_d(Operand(rsp, 0));
+ __ movsd(xmm1, Operand(rsp, 0));
+ __ 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.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ // Allocate an unused object bigger than a HeapNumber.
+ __ Push(Smi::FromInt(2 * kDoubleSize));
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
+ }
+ __ Ret();
+ }
+
+ // Call runtime, doing whatever allocation and cleanup is necessary.
+ if (tagged) {
+ __ bind(&runtime_call_clear_stack);
+ __ fstp(0);
+ __ bind(&runtime_call);
+ __ TailCallExternalReference(
+ ExternalReference(RuntimeFunction(), masm->isolate()), 1, 1);
+ } else { // UNTAGGED.
+ __ bind(&runtime_call_clear_stack);
+ __ bind(&runtime_call);
+ __ AllocateHeapNumber(rax, rdi, &skip_cache);
+ __ movsd(FieldOperand(rax, HeapNumber::kValueOffset), xmm1);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ push(rax);
+ __ CallRuntime(RuntimeFunction(), 1);
+ }
+ __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ Ret();
+ }
+}
+
+
+Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
+ switch (type_) {
+ // 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();
+ return Runtime::kAbort;
+ }
+}
+
+
+void TranscendentalCacheStub::GenerateOperation(
+ MacroAssembler* masm, TranscendentalCache::Type type) {
+ // Registers:
+ // rax: Newly allocated HeapNumber, which must be preserved.
+ // rbx: Bits of input double. Must be preserved.
+ // rcx: Pointer to cache entry. Must be preserved.
+ // st(0): Input double
+ Label done;
+ 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.
+ Label in_range;
+ // If argument is outside the range -2^63..2^63, fsin/cos doesn't
+ // work. We must reduce it to the appropriate range.
+ __ movq(rdi, rbx);
+ // Move exponent and sign bits to low bits.
+ __ shr(rdi, Immediate(HeapNumber::kMantissaBits));
+ // Remove sign bit.
+ __ andl(rdi, Immediate((1 << HeapNumber::kExponentBits) - 1));
+ int supported_exponent_limit = (63 + HeapNumber::kExponentBias);
+ __ cmpl(rdi, Immediate(supported_exponent_limit));
+ __ j(below, &in_range);
+ // Check for infinity and NaN. Both return NaN for sin.
+ __ cmpl(rdi, Immediate(0x7ff));
+ Label non_nan_result;
+ __ j(not_equal, &non_nan_result, Label::kNear);
+ // Input is +/-Infinity or NaN. Result is NaN.
+ __ fstp(0);
+ // NaN is represented by 0x7ff8000000000000.
+ __ subq(rsp, Immediate(kPointerSize));
+ __ movl(Operand(rsp, 4), Immediate(0x7ff80000));
+ __ movl(Operand(rsp, 0), Immediate(0x00000000));
+ __ fld_d(Operand(rsp, 0));
+ __ addq(rsp, Immediate(kPointerSize));
+ __ jmp(&done);
+
+ __ bind(&non_nan_result);
+
+ // Use fpmod to restrict argument to the range +/-2*PI.
+ __ movq(rdi, rax); // Save rax before using fnstsw_ax.
+ __ fldpi();
+ __ fadd(0);
+ __ fld(1);
+ // FPU Stack: input, 2*pi, input.
+ {
+ Label no_exceptions;
+ __ fwait();
+ __ fnstsw_ax();
+ // Clear if Illegal Operand or Zero Division exceptions are set.
+ __ testl(rax, Immediate(5)); // #IO and #ZD flags of FPU status word.
+ __ j(zero, &no_exceptions);
+ __ fnclex();
+ __ bind(&no_exceptions);
+ }
+
+ // Compute st(0) % st(1)
+ {
+ Label partial_remainder_loop;
+ __ bind(&partial_remainder_loop);
+ __ fprem1();
+ __ fwait();
+ __ fnstsw_ax();
+ __ testl(rax, Immediate(0x400)); // Check C2 bit of FPU status word.
+ // If C2 is set, computation only has partial result. Loop to
+ // continue computation.
+ __ j(not_zero, &partial_remainder_loop);
+ }
+ // FPU Stack: input, 2*pi, input % 2*pi
+ __ fstp(2);
+ // FPU Stack: input % 2*pi, 2*pi,
+ __ fstp(0);
+ // FPU Stack: input % 2*pi
+ __ movq(rax, rdi); // Restore rax, pointer to the new HeapNumber.
+ __ bind(&in_range);
+ switch (type) {
+ case TranscendentalCache::SIN:
+ __ fsin();
+ break;
+ 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();
+ }
+ __ bind(&done);
+ } else {
+ ASSERT(type == TranscendentalCache::LOG);
+ __ fldln2();
+ __ fxch();
+ __ fyl2x();
+ }
+}
+
+
+// Input: rdx, rax are the left and right objects of a bit op.
+// Output: rax, rcx are left and right integers for a bit op.
+void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm) {
+ // Check float operands.
+ Label done;
+ Label rax_is_smi;
+ Label rax_is_object;
+ Label rdx_is_object;
+
+ __ JumpIfNotSmi(rdx, &rdx_is_object);
+ __ SmiToInteger32(rdx, rdx);
+ __ JumpIfSmi(rax, &rax_is_smi);
+
+ __ bind(&rax_is_object);
+ DoubleToIStub stub1(rax, rcx, HeapNumber::kValueOffset - kHeapObjectTag,
+ true);
+ __ call(stub1.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+
+ __ jmp(&done);
+
+ __ bind(&rdx_is_object);
+ DoubleToIStub stub2(rdx, rdx, HeapNumber::kValueOffset - kHeapObjectTag,
+ true);
+ __ call(stub1.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+ __ JumpIfNotSmi(rax, &rax_is_object);
+
+ __ bind(&rax_is_smi);
+ __ SmiToInteger32(rcx, rax);
+
+ __ bind(&done);
+ __ movl(rax, rdx);
+}
+
+
+// Input: rdx, rax are the left and right objects of a bit op.
+// Output: rax, rcx are left and right integers for a bit op.
+// Jump to conversion_failure: rdx and rax are unchanged.
+void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
+ Label* conversion_failure,
+ Register heap_number_map) {
+ // Check float operands.
+ Label arg1_is_object, check_undefined_arg1;
+ Label arg2_is_object, check_undefined_arg2;
+ Label load_arg2, done;
+
+ __ JumpIfNotSmi(rdx, &arg1_is_object);
+ __ SmiToInteger32(r8, rdx);
+ __ jmp(&load_arg2);
+
+ // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
+ __ bind(&check_undefined_arg1);
+ __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, conversion_failure);
+ __ Set(r8, 0);
+ __ jmp(&load_arg2);
+
+ __ bind(&arg1_is_object);
+ __ cmpq(FieldOperand(rdx, HeapObject::kMapOffset), heap_number_map);
+ __ j(not_equal, &check_undefined_arg1);
+ // Get the untagged integer version of the rdx heap number in rcx.
+ DoubleToIStub stub1(rdx, r8, HeapNumber::kValueOffset - kHeapObjectTag,
+ true);
+ __ call(stub1.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+
+ // Here r8 has the untagged integer, rax has a Smi or a heap number.
+ __ bind(&load_arg2);
+ // Test if arg2 is a Smi.
+ __ JumpIfNotSmi(rax, &arg2_is_object);
+ __ SmiToInteger32(rcx, rax);
+ __ jmp(&done);
+
+ // If the argument is undefined it converts to zero (ECMA-262, section 9.5).
+ __ bind(&check_undefined_arg2);
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, conversion_failure);
+ __ Set(rcx, 0);
+ __ jmp(&done);
+
+ __ bind(&arg2_is_object);
+ __ cmpq(FieldOperand(rax, HeapObject::kMapOffset), heap_number_map);
+ __ j(not_equal, &check_undefined_arg2);
+ // Get the untagged integer version of the rax heap number in rcx.
+ DoubleToIStub stub2(rax, rcx, HeapNumber::kValueOffset - kHeapObjectTag,
+ true);
+ __ call(stub2.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+
+ __ bind(&done);
+ __ movl(rax, r8);
+}
+
+
+void FloatingPointHelper::LoadSSE2SmiOperands(MacroAssembler* masm) {
+ __ SmiToInteger32(kScratchRegister, rdx);
+ __ cvtlsi2sd(xmm0, kScratchRegister);
+ __ SmiToInteger32(kScratchRegister, rax);
+ __ cvtlsi2sd(xmm1, kScratchRegister);
+}
+
+
+void FloatingPointHelper::LoadSSE2NumberOperands(MacroAssembler* masm) {
+ Label load_smi_rdx, load_nonsmi_rax, load_smi_rax, done;
+ // Load operand in rdx into xmm0.
+ __ JumpIfSmi(rdx, &load_smi_rdx);
+ __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
+ // Load operand in rax into xmm1.
+ __ JumpIfSmi(rax, &load_smi_rax);
+ __ bind(&load_nonsmi_rax);
+ __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ jmp(&done);
+
+ __ bind(&load_smi_rdx);
+ __ SmiToInteger32(kScratchRegister, rdx);
+ __ cvtlsi2sd(xmm0, kScratchRegister);
+ __ JumpIfNotSmi(rax, &load_nonsmi_rax);
+
+ __ bind(&load_smi_rax);
+ __ SmiToInteger32(kScratchRegister, rax);
+ __ cvtlsi2sd(xmm1, kScratchRegister);
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadSSE2UnknownOperands(MacroAssembler* masm,
+ Label* not_numbers) {
+ Label load_smi_rdx, load_nonsmi_rax, load_smi_rax, load_float_rax, done;
+ // Load operand in rdx into xmm0, or branch to not_numbers.
+ __ LoadRoot(rcx, Heap::kHeapNumberMapRootIndex);
+ __ JumpIfSmi(rdx, &load_smi_rdx);
+ __ cmpq(FieldOperand(rdx, HeapObject::kMapOffset), rcx);
+ __ j(not_equal, not_numbers); // Argument in rdx is not a number.
+ __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
+ // Load operand in rax into xmm1, or branch to not_numbers.
+ __ JumpIfSmi(rax, &load_smi_rax);
+
+ __ bind(&load_nonsmi_rax);
+ __ cmpq(FieldOperand(rax, HeapObject::kMapOffset), rcx);
+ __ j(not_equal, not_numbers);
+ __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ jmp(&done);
+
+ __ bind(&load_smi_rdx);
+ __ SmiToInteger32(kScratchRegister, rdx);
+ __ cvtlsi2sd(xmm0, kScratchRegister);
+ __ JumpIfNotSmi(rax, &load_nonsmi_rax);
+
+ __ bind(&load_smi_rax);
+ __ SmiToInteger32(kScratchRegister, rax);
+ __ cvtlsi2sd(xmm1, kScratchRegister);
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm,
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* on_success,
+ Label* on_not_smis,
+ ConvertUndefined convert_undefined) {
+ Register heap_number_map = scratch3;
+ Register smi_result = scratch1;
+ Label done, maybe_undefined_first, maybe_undefined_second, first_done;
+
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ Label first_smi;
+ __ JumpIfSmi(first, &first_smi, Label::kNear);
+ __ cmpq(FieldOperand(first, HeapObject::kMapOffset), heap_number_map);
+ __ j(not_equal,
+ (convert_undefined == CONVERT_UNDEFINED_TO_ZERO)
+ ? &maybe_undefined_first
+ : on_not_smis);
+ // Convert HeapNumber to smi if possible.
+ __ movsd(xmm0, FieldOperand(first, HeapNumber::kValueOffset));
+ __ movq(scratch2, xmm0);
+ __ cvttsd2siq(smi_result, xmm0);
+ // Check if conversion was successful by converting back and
+ // comparing to the original double's bits.
+ __ cvtlsi2sd(xmm1, smi_result);
+ __ movq(kScratchRegister, xmm1);
+ __ cmpq(scratch2, kScratchRegister);
+ __ j(not_equal, on_not_smis);
+ __ Integer32ToSmi(first, smi_result);
+
+ __ bind(&first_done);
+ __ JumpIfSmi(second, (on_success != NULL) ? on_success : &done);
+ __ bind(&first_smi);
+ __ AssertNotSmi(second);
+ __ cmpq(FieldOperand(second, HeapObject::kMapOffset), heap_number_map);
+ __ j(not_equal,
+ (convert_undefined == CONVERT_UNDEFINED_TO_ZERO)
+ ? &maybe_undefined_second
+ : on_not_smis);
+ // Convert second to smi, if possible.
+ __ movsd(xmm0, FieldOperand(second, HeapNumber::kValueOffset));
+ __ movq(scratch2, xmm0);
+ __ cvttsd2siq(smi_result, xmm0);
+ __ cvtlsi2sd(xmm1, smi_result);
+ __ movq(kScratchRegister, xmm1);
+ __ cmpq(scratch2, kScratchRegister);
+ __ j(not_equal, on_not_smis);
+ __ Integer32ToSmi(second, smi_result);
+ if (on_success != NULL) {
+ __ jmp(on_success);
+ } else {
+ __ jmp(&done);
+ }
+
+ __ bind(&maybe_undefined_first);
+ __ CompareRoot(first, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, on_not_smis);
+ __ xor_(first, first);
+ __ jmp(&first_done);
+
+ __ bind(&maybe_undefined_second);
+ __ CompareRoot(second, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, on_not_smis);
+ __ xor_(second, second);
+ if (on_success != NULL) {
+ __ jmp(on_success);
+ }
+ // Else: fall through.
+
+ __ bind(&done);
+}
+
+
+void MathPowStub::Generate(MacroAssembler* masm) {
+ const Register exponent = rdx;
+ 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;
+
+ Label call_runtime, done, exponent_not_smi, int_exponent;
+
+ // 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));
+ }
+
+ 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::NONE64);
+ __ 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::NONE64);
+ __ 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::NONE64);
+ __ 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);
+ }
+
+ // 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); // 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);
+
+ __ bind(&fast_power_failed);
+ __ fninit();
+ __ addq(rsp, Immediate(kDoubleSize));
+ __ jmp(&call_runtime);
+ }
+
+ // 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, while_true, while_false;
+ __ testl(scratch, scratch);
+ __ j(positive, &no_neg, Label::kNear);
+ __ negl(scratch);
+ __ bind(&no_neg);
+
+ __ j(zero, &while_false, Label::kNear);
+ __ shrl(scratch, Immediate(1));
+ // Above condition means CF==0 && ZF==0. This means that the
+ // bit that has been shifted out is 0 and the result is not 0.
+ __ j(above, &while_true, Label::kNear);
+ __ movsd(double_result, double_scratch);
+ __ j(zero, &while_false, Label::kNear);
+
+ __ bind(&while_true);
+ __ shrl(scratch, Immediate(1));
+ __ mulsd(double_scratch, double_scratch);
+ __ j(above, &while_true, Label::kNear);
+ __ mulsd(double_result, double_scratch);
+ __ j(not_zero, &while_true);
+
+ __ bind(&while_false);
+ // 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);
+
+ // The stub is called from non-optimized code, which expects the result
+ // as heap number in rax.
+ __ 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(&done);
+ __ IncrementCounter(counters->math_pow(), 1);
+ __ ret(0);
+ }
+}
+
+
+void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
+ Label miss;
+ Register receiver;
+ if (kind() == Code::KEYED_LOAD_IC) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ __ Cmp(rax, masm->isolate()->factory()->prototype_string());
+ __ j(not_equal, &miss);
+ receiver = rdx;
+ } else {
+ ASSERT(kind() == Code::LOAD_IC);
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+ receiver = rax;
+ }
+
+ StubCompiler::GenerateLoadFunctionPrototype(masm, receiver, r8, r9, &miss);
+ __ bind(&miss);
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void StringLengthStub::Generate(MacroAssembler* masm) {
+ Label miss;
+ Register receiver;
+ if (kind() == Code::KEYED_LOAD_IC) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ __ Cmp(rax, masm->isolate()->factory()->length_string());
+ __ j(not_equal, &miss);
+ receiver = rdx;
+ } else {
+ ASSERT(kind() == Code::LOAD_IC);
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+ receiver = rax;
+ }
+
+ StubCompiler::GenerateLoadStringLength(masm, receiver, r8, r9, &miss,
+ support_wrapper_);
+ __ bind(&miss);
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void StoreArrayLengthStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ //
+ // 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;
+
+ Register receiver = rdx;
+ Register value = rax;
+ Register scratch = rbx;
+ if (kind() == Code::KEYED_STORE_IC) {
+ __ Cmp(rcx, masm->isolate()->factory()->length_string());
+ __ j(not_equal, &miss);
+ }
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Check that the object is a JS array.
+ __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
+ __ j(not_equal, &miss);
+
+ // Check that elements are FixedArray.
+ // We rely on StoreIC_ArrayLength below to deal with all types of
+ // fast elements (including COW).
+ __ movq(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
+ __ 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);
+
+ // Prepare tail call to StoreIC_ArrayLength.
+ __ PopReturnAddressTo(scratch);
+ __ push(receiver);
+ __ push(value);
+ __ PushReturnAddressFrom(scratch);
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+
+ __ bind(&miss);
+
+ StubCompiler::TailCallBuiltin(
+ masm, BaseLoadStoreStubCompiler::MissBuiltin(kind()));
+}
+
+
+void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+ // The key is in rdx and the parameter count is in rax.
+
+ // The displacement is used for skipping the frame pointer on the
+ // stack. It is the offset of the last parameter (if any) relative
+ // to the frame pointer.
+ static const int kDisplacement = 1 * kPointerSize;
+
+ // Check that the key is a smi.
+ Label slow;
+ __ JumpIfNotSmi(rdx, &slow);
+
+ // Check if the calling frame is an arguments adaptor frame. We look at the
+ // context offset, and if the frame is not a regular one, then we find a
+ // Smi instead of the context. We can't use SmiCompare here, because that
+ // only works for comparing two smis.
+ Label adaptor;
+ __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ Cmp(Operand(rbx, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(equal, &adaptor);
+
+ // Check index against formal parameters count limit passed in
+ // through register rax. Use unsigned comparison to get negative
+ // check for free.
+ __ cmpq(rdx, rax);
+ __ j(above_equal, &slow);
+
+ // Read the argument from the stack and return it.
+ SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2);
+ __ lea(rbx, Operand(rbp, index.reg, index.scale, 0));
+ index = masm->SmiToNegativeIndex(rdx, rdx, kPointerSizeLog2);
+ __ movq(rax, Operand(rbx, index.reg, index.scale, kDisplacement));
+ __ Ret();
+
+ // Arguments adaptor case: Check index against actual arguments
+ // limit found in the arguments adaptor frame. Use unsigned
+ // comparison to get negative check for free.
+ __ bind(&adaptor);
+ __ movq(rcx, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ cmpq(rdx, rcx);
+ __ j(above_equal, &slow);
+
+ // Read the argument from the stack and return it.
+ index = masm->SmiToIndex(rax, rcx, kPointerSizeLog2);
+ __ lea(rbx, Operand(rbx, index.reg, index.scale, 0));
+ index = masm->SmiToNegativeIndex(rdx, rdx, kPointerSizeLog2);
+ __ movq(rax, Operand(rbx, index.reg, index.scale, kDisplacement));
+ __ Ret();
+
+ // Slow-case: Handle non-smi or out-of-bounds access to arguments
+ // by calling the runtime system.
+ __ bind(&slow);
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx);
+ __ PushReturnAddressFrom(rbx);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
+ // Stack layout:
+ // rsp[0] : return address
+ // rsp[8] : number of parameters (tagged)
+ // rsp[16] : receiver displacement
+ // rsp[24] : function
+ // Registers used over the whole function:
+ // rbx: the mapped parameter count (untagged)
+ // rax: the allocated object (tagged).
+
+ Factory* factory = masm->isolate()->factory();
+
+ __ SmiToInteger64(rbx, Operand(rsp, 1 * kPointerSize));
+ // rbx = parameter count (untagged)
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label runtime;
+ Label adaptor_frame, try_allocate;
+ __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ movq(rcx, Operand(rdx, StandardFrameConstants::kContextOffset));
+ __ Cmp(rcx, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(equal, &adaptor_frame);
+
+ // No adaptor, parameter count = argument count.
+ __ movq(rcx, rbx);
+ __ jmp(&try_allocate, Label::kNear);
+
+ // We have an adaptor frame. Patch the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ SmiToInteger64(rcx,
+ Operand(rdx,
+ ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ lea(rdx, Operand(rdx, rcx, times_pointer_size,
+ StandardFrameConstants::kCallerSPOffset));
+ __ movq(Operand(rsp, 2 * kPointerSize), rdx);
+
+ // rbx = parameter count (untagged)
+ // rcx = argument count (untagged)
+ // Compute the mapped parameter count = min(rbx, rcx) in rbx.
+ __ cmpq(rbx, rcx);
+ __ j(less_equal, &try_allocate, Label::kNear);
+ __ movq(rbx, rcx);
+
+ __ bind(&try_allocate);
+
+ // Compute the sizes of backing store, parameter map, and arguments object.
+ // 1. Parameter map, has 2 extra words containing context and backing store.
+ 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));
+ __ bind(&no_parameter_map);
+
+ // 2. Backing store.
+ __ lea(r8, Operand(r8, rcx, times_pointer_size, FixedArray::kHeaderSize));
+
+ // 3. Arguments object.
+ __ addq(r8, Immediate(Heap::kArgumentsObjectSize));
+
+ // Do the allocation of all three objects in one go.
+ __ Allocate(r8, rax, rdx, rdi, &runtime, TAG_OBJECT);
+
+ // rax = address of new object(s) (tagged)
+ // rcx = argument count (untagged)
+ // Get the arguments boilerplate from the current native context into rdi.
+ Label has_mapped_parameters, copy;
+ __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ movq(rdi, FieldOperand(rdi, GlobalObject::kNativeContextOffset));
+ __ testq(rbx, rbx);
+ __ j(not_zero, &has_mapped_parameters, Label::kNear);
+
+ const int kIndex = Context::ARGUMENTS_BOILERPLATE_INDEX;
+ __ movq(rdi, Operand(rdi, Context::SlotOffset(kIndex)));
+ __ jmp(&copy, Label::kNear);
+
+ const int kAliasedIndex = Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX;
+ __ bind(&has_mapped_parameters);
+ __ movq(rdi, Operand(rdi, Context::SlotOffset(kAliasedIndex)));
+ __ bind(&copy);
+
+ // rax = address of new object (tagged)
+ // rbx = mapped parameter count (untagged)
+ // rcx = argument count (untagged)
+ // rdi = address of boilerplate object (tagged)
+ // Copy the JS object part.
+ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+ __ movq(rdx, FieldOperand(rdi, i));
+ __ movq(FieldOperand(rax, i), rdx);
+ }
+
+ // Set up the callee in-object property.
+ STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
+ __ movq(rdx, Operand(rsp, 3 * kPointerSize));
+ __ movq(FieldOperand(rax, JSObject::kHeaderSize +
+ Heap::kArgumentsCalleeIndex * kPointerSize),
+ rdx);
+
+ // Use the length (smi tagged) and set that as an in-object property too.
+ // Note: rcx is tagged from here on.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ __ Integer32ToSmi(rcx, rcx);
+ __ movq(FieldOperand(rax, JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize),
+ rcx);
+
+ // 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));
+ __ movq(FieldOperand(rax, JSObject::kElementsOffset), rdi);
+
+ // rax = address of new object (tagged)
+ // rbx = mapped parameter count (untagged)
+ // rcx = argument count (tagged)
+ // rdi = address of parameter map or backing store (tagged)
+
+ // Initialize parameter map. If there are no mapped arguments, we're done.
+ Label skip_parameter_map;
+ __ testq(rbx, rbx);
+ __ j(zero, &skip_parameter_map);
+
+ __ LoadRoot(kScratchRegister, Heap::kNonStrictArgumentsElementsMapRootIndex);
+ // rbx contains the untagged argument count. Add 2 and tag to write.
+ __ movq(FieldOperand(rdi, FixedArray::kMapOffset), kScratchRegister);
+ __ Integer64PlusConstantToSmi(r9, rbx, 2);
+ __ movq(FieldOperand(rdi, FixedArray::kLengthOffset), r9);
+ __ movq(FieldOperand(rdi, FixedArray::kHeaderSize + 0 * kPointerSize), rsi);
+ __ lea(r9, Operand(rdi, rbx, times_pointer_size, kParameterMapHeaderSize));
+ __ movq(FieldOperand(rdi, FixedArray::kHeaderSize + 1 * kPointerSize), r9);
+
+ // Copy the parameter slots and the holes in the arguments.
+ // We need to fill in mapped_parameter_count slots. They index the context,
+ // where parameters are stored in reverse order, at
+ // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
+ // The mapped parameter thus need to get indices
+ // MIN_CONTEXT_SLOTS+parameter_count-1 ..
+ // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
+ // We loop from right to left.
+ Label parameters_loop, parameters_test;
+
+ // Load tagged parameter count into r9.
+ __ Integer32ToSmi(r9, rbx);
+ __ Move(r8, Smi::FromInt(Context::MIN_CONTEXT_SLOTS));
+ __ addq(r8, Operand(rsp, 1 * kPointerSize));
+ __ subq(r8, r9);
+ __ Move(r11, factory->the_hole_value());
+ __ movq(rdx, rdi);
+ __ lea(rdi, Operand(rdi, rbx, times_pointer_size, kParameterMapHeaderSize));
+ // r9 = loop variable (tagged)
+ // r8 = mapping index (tagged)
+ // r11 = the hole value
+ // rdx = address of parameter map (tagged)
+ // rdi = address of backing store (tagged)
+ __ jmp(&parameters_test, Label::kNear);
+
+ __ bind(&parameters_loop);
+ __ SmiSubConstant(r9, r9, Smi::FromInt(1));
+ __ SmiToInteger64(kScratchRegister, r9);
+ __ movq(FieldOperand(rdx, kScratchRegister,
+ times_pointer_size,
+ kParameterMapHeaderSize),
+ r8);
+ __ movq(FieldOperand(rdi, kScratchRegister,
+ times_pointer_size,
+ FixedArray::kHeaderSize),
+ r11);
+ __ SmiAddConstant(r8, r8, Smi::FromInt(1));
+ __ bind(&parameters_test);
+ __ SmiTest(r9);
+ __ j(not_zero, &parameters_loop, Label::kNear);
+
+ __ bind(&skip_parameter_map);
+
+ // rcx = argument count (tagged)
+ // rdi = address of backing store (tagged)
+ // Copy arguments header and remaining slots (if there are any).
+ __ Move(FieldOperand(rdi, FixedArray::kMapOffset),
+ factory->fixed_array_map());
+ __ movq(FieldOperand(rdi, FixedArray::kLengthOffset), rcx);
+
+ Label arguments_loop, arguments_test;
+ __ movq(r8, rbx);
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize));
+ // Untag rcx for the loop below.
+ __ SmiToInteger64(rcx, rcx);
+ __ lea(kScratchRegister, Operand(r8, times_pointer_size, 0));
+ __ subq(rdx, kScratchRegister);
+ __ jmp(&arguments_test, Label::kNear);
+
+ __ bind(&arguments_loop);
+ __ subq(rdx, Immediate(kPointerSize));
+ __ movq(r9, Operand(rdx, 0));
+ __ movq(FieldOperand(rdi, r8,
+ times_pointer_size,
+ FixedArray::kHeaderSize),
+ r9);
+ __ addq(r8, Immediate(1));
+
+ __ bind(&arguments_test);
+ __ cmpq(r8, rcx);
+ __ j(less, &arguments_loop, Label::kNear);
+
+ // Return and remove the on-stack parameters.
+ __ ret(3 * kPointerSize);
+
+ // Do the runtime call to allocate the arguments object.
+ // rcx = argument count (untagged)
+ __ bind(&runtime);
+ __ Integer32ToSmi(rcx, rcx);
+ __ movq(Operand(rsp, 1 * kPointerSize), rcx); // Patch argument count.
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
+ // rsp[0] : return address
+ // rsp[8] : number of parameters
+ // rsp[16] : receiver displacement
+ // rsp[24] : function
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label runtime;
+ __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ movq(rcx, Operand(rdx, StandardFrameConstants::kContextOffset));
+ __ Cmp(rcx, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(not_equal, &runtime);
+
+ // Patch the arguments.length and the parameters pointer.
+ __ movq(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ movq(Operand(rsp, 1 * kPointerSize), rcx);
+ __ SmiToInteger64(rcx, rcx);
+ __ lea(rdx, Operand(rdx, rcx, times_pointer_size,
+ StandardFrameConstants::kCallerSPOffset));
+ __ movq(Operand(rsp, 2 * kPointerSize), rdx);
+
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
+}
+
+
+void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
+ // rsp[0] : return address
+ // rsp[8] : number of parameters
+ // rsp[16] : receiver displacement
+ // rsp[24] : function
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor_frame, try_allocate, runtime;
+ __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ movq(rcx, Operand(rdx, StandardFrameConstants::kContextOffset));
+ __ Cmp(rcx, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(equal, &adaptor_frame);
+
+ // Get the length from the frame.
+ __ movq(rcx, Operand(rsp, 1 * kPointerSize));
+ __ SmiToInteger64(rcx, rcx);
+ __ jmp(&try_allocate);
+
+ // Patch the arguments.length and the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ movq(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ movq(Operand(rsp, 1 * kPointerSize), rcx);
+ __ SmiToInteger64(rcx, rcx);
+ __ lea(rdx, Operand(rdx, rcx, times_pointer_size,
+ StandardFrameConstants::kCallerSPOffset));
+ __ movq(Operand(rsp, 2 * kPointerSize), rdx);
+
+ // Try the new space allocation. Start out with computing the size of
+ // the arguments object and the elements array.
+ Label add_arguments_object;
+ __ bind(&try_allocate);
+ __ testq(rcx, rcx);
+ __ j(zero, &add_arguments_object, Label::kNear);
+ __ lea(rcx, Operand(rcx, times_pointer_size, FixedArray::kHeaderSize));
+ __ bind(&add_arguments_object);
+ __ addq(rcx, Immediate(Heap::kArgumentsObjectSizeStrict));
+
+ // Do the allocation of both objects in one go.
+ __ Allocate(rcx, rax, rdx, rbx, &runtime, TAG_OBJECT);
+
+ // Get the arguments boilerplate from the current native context.
+ __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ movq(rdi, FieldOperand(rdi, GlobalObject::kNativeContextOffset));
+ const int offset =
+ Context::SlotOffset(Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX);
+ __ movq(rdi, Operand(rdi, offset));
+
+ // Copy the JS object part.
+ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+ __ movq(rbx, FieldOperand(rdi, i));
+ __ movq(FieldOperand(rax, i), rbx);
+ }
+
+ // Get the length (smi tagged) and set that as an in-object property too.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ __ movq(rcx, Operand(rsp, 1 * kPointerSize));
+ __ movq(FieldOperand(rax, JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize),
+ rcx);
+
+ // If there are no actual arguments, we're done.
+ Label done;
+ __ testq(rcx, rcx);
+ __ j(zero, &done);
+
+ // Get the parameters pointer from the stack.
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize));
+
+ // 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);
+ __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex);
+ __ movq(FieldOperand(rdi, FixedArray::kMapOffset), kScratchRegister);
+
+
+ __ movq(FieldOperand(rdi, FixedArray::kLengthOffset), rcx);
+ // Untag the length for the loop below.
+ __ SmiToInteger64(rcx, rcx);
+
+ // Copy the fixed array slots.
+ Label loop;
+ __ bind(&loop);
+ __ movq(rbx, Operand(rdx, -1 * kPointerSize)); // Skip receiver.
+ __ movq(FieldOperand(rdi, FixedArray::kHeaderSize), rbx);
+ __ addq(rdi, Immediate(kPointerSize));
+ __ subq(rdx, Immediate(kPointerSize));
+ __ decq(rcx);
+ __ j(not_zero, &loop);
+
+ // Return and remove the on-stack parameters.
+ __ bind(&done);
+ __ ret(3 * kPointerSize);
+
+ // Do the runtime call to allocate the arguments object.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
+}
+
+
+void RegExpExecStub::Generate(MacroAssembler* masm) {
+ // Just jump directly to runtime if native RegExp is not selected at compile
+ // time or if regexp entry in generated code is turned off runtime switch or
+ // at compilation.
+#ifdef V8_INTERPRETED_REGEXP
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+#else // V8_INTERPRETED_REGEXP
+
+ // Stack frame on entry.
+ // rsp[0] : return address
+ // rsp[8] : last_match_info (expected JSArray)
+ // rsp[16] : previous index
+ // rsp[24] : subject string
+ // rsp[32] : JSRegExp object
+
+ static const int kLastMatchInfoOffset = 1 * kPointerSize;
+ static const int kPreviousIndexOffset = 2 * kPointerSize;
+ static const int kSubjectOffset = 3 * kPointerSize;
+ static const int kJSRegExpOffset = 4 * kPointerSize;
+
+ Label runtime;
+ // Ensure that a RegExp stack is allocated.
+ Isolate* isolate = masm->isolate();
+ ExternalReference address_of_regexp_stack_memory_address =
+ ExternalReference::address_of_regexp_stack_memory_address(isolate);
+ ExternalReference address_of_regexp_stack_memory_size =
+ ExternalReference::address_of_regexp_stack_memory_size(isolate);
+ __ Load(kScratchRegister, address_of_regexp_stack_memory_size);
+ __ testq(kScratchRegister, kScratchRegister);
+ __ j(zero, &runtime);
+
+ // Check that the first argument is a JSRegExp object.
+ __ movq(rax, Operand(rsp, kJSRegExpOffset));
+ __ JumpIfSmi(rax, &runtime);
+ __ CmpObjectType(rax, JS_REGEXP_TYPE, kScratchRegister);
+ __ j(not_equal, &runtime);
+
+ // Check that the RegExp has been compiled (data contains a fixed array).
+ __ movq(rax, FieldOperand(rax, JSRegExp::kDataOffset));
+ if (FLAG_debug_code) {
+ Condition is_smi = masm->CheckSmi(rax);
+ __ Check(NegateCondition(is_smi),
+ kUnexpectedTypeForRegExpDataFixedArrayExpected);
+ __ CmpObjectType(rax, FIXED_ARRAY_TYPE, kScratchRegister);
+ __ Check(equal, kUnexpectedTypeForRegExpDataFixedArrayExpected);
+ }
+
+ // rax: RegExp data (FixedArray)
+ // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
+ __ SmiToInteger32(rbx, FieldOperand(rax, JSRegExp::kDataTagOffset));
+ __ cmpl(rbx, Immediate(JSRegExp::IRREGEXP));
+ __ j(not_equal, &runtime);
+
+ // rax: RegExp data (FixedArray)
+ // Check that the number of captures fit in the static offsets vector buffer.
+ __ SmiToInteger32(rdx,
+ FieldOperand(rax, JSRegExp::kIrregexpCaptureCountOffset));
+ // Check (number_of_captures + 1) * 2 <= offsets vector size
+ // Or number_of_captures <= offsets vector size / 2 - 1
+ STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
+ __ cmpl(rdx, Immediate(Isolate::kJSRegexpStaticOffsetsVectorSize / 2 - 1));
+ __ j(above, &runtime);
+
+ // Reset offset for possibly sliced string.
+ __ Set(r14, 0);
+ __ movq(rdi, Operand(rsp, kSubjectOffset));
+ __ JumpIfSmi(rdi, &runtime);
+ __ movq(r15, rdi); // Make a copy of the original subject string.
+ __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
+ __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
+ // rax: RegExp data (FixedArray)
+ // rdi: subject string
+ // r15: subject string
+ // Handle subject string according to its encoding and representation:
+ // (1) Sequential two byte? If yes, go to (9).
+ // (2) Sequential one byte? If yes, go to (6).
+ // (3) Anything but sequential or cons? If yes, go to (7).
+ // (4) Cons string. If the string is flat, replace subject with first string.
+ // Otherwise bailout.
+ // (5a) Is subject sequential two byte? If yes, go to (9).
+ // (5b) Is subject external? If yes, go to (8).
+ // (6) One byte sequential. Load regexp code for one byte.
+ // (E) Carry on.
+ /// [...]
+
+ // Deferred code at the end of the stub:
+ // (7) Not a long external string? If yes, go to (10).
+ // (8) External string. Make it, offset-wise, look like a sequential string.
+ // (8a) Is the external string one byte? If yes, go to (6).
+ // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
+ // (10) Short external string or not a string? If yes, bail out to runtime.
+ // (11) Sliced string. Replace subject with parent. Go to (5a).
+
+ Label seq_one_byte_string /* 6 */, seq_two_byte_string /* 9 */,
+ external_string /* 8 */, check_underlying /* 5a */,
+ not_seq_nor_cons /* 7 */, check_code /* E */,
+ not_long_external /* 10 */;
+
+ // (1) Sequential two byte? If yes, go to (9).
+ __ andb(rbx, Immediate(kIsNotStringMask |
+ kStringRepresentationMask |
+ kStringEncodingMask |
+ kShortExternalStringMask));
+ STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
+ __ j(zero, &seq_two_byte_string); // Go to (9).
+
+ // (2) Sequential one byte? If yes, go to (6).
+ // Any other sequential string must be one byte.
+ __ andb(rbx, Immediate(kIsNotStringMask |
+ kStringRepresentationMask |
+ kShortExternalStringMask));
+ __ j(zero, &seq_one_byte_string, Label::kNear); // Go to (6).
+
+ // (3) Anything but sequential or cons? If yes, go to (7).
+ // We check whether the subject string is a cons, since sequential strings
+ // have already been covered.
+ STATIC_ASSERT(kConsStringTag < kExternalStringTag);
+ STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
+ STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
+ STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
+ __ cmpq(rbx, Immediate(kExternalStringTag));
+ __ j(greater_equal, &not_seq_nor_cons); // Go to (7).
+
+ // (4) Cons string. Check that it's flat.
+ // Replace subject with first string and reload instance type.
+ __ CompareRoot(FieldOperand(rdi, ConsString::kSecondOffset),
+ Heap::kempty_stringRootIndex);
+ __ j(not_equal, &runtime);
+ __ movq(rdi, FieldOperand(rdi, ConsString::kFirstOffset));
+ __ bind(&check_underlying);
+ __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
+ __ movq(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
+
+ // (5a) Is subject sequential two byte? If yes, go to (9).
+ __ testb(rbx, Immediate(kStringRepresentationMask | kStringEncodingMask));
+ STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
+ __ j(zero, &seq_two_byte_string); // Go to (9).
+ // (5b) Is subject external? If yes, go to (8).
+ __ testb(rbx, Immediate(kStringRepresentationMask));
+ // The underlying external string is never a short external string.
+ STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength);
+ STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength);
+ __ j(not_zero, &external_string); // Go to (8)
+
+ // (6) One byte sequential. Load regexp code for one byte.
+ __ bind(&seq_one_byte_string);
+ // rax: RegExp data (FixedArray)
+ __ movq(r11, FieldOperand(rax, JSRegExp::kDataAsciiCodeOffset));
+ __ Set(rcx, 1); // Type is one byte.
+
+ // (E) Carry on. String handling is done.
+ __ bind(&check_code);
+ // r11: irregexp code
+ // Check that the irregexp code has been generated for the actual string
+ // encoding. If it has, the field contains a code object otherwise it contains
+ // smi (code flushing support)
+ __ JumpIfSmi(r11, &runtime);
+
+ // rdi: sequential subject string (or look-alike, external string)
+ // r15: original subject string
+ // 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.
+ // We have to use r15 instead of rdi to load the length because rdi might
+ // have been only made to look like a sequential string when it actually
+ // is an external string.
+ __ movq(rbx, Operand(rsp, kPreviousIndexOffset));
+ __ JumpIfNotSmi(rbx, &runtime);
+ __ SmiCompare(rbx, FieldOperand(r15, String::kLengthOffset));
+ __ j(above_equal, &runtime);
+ __ SmiToInteger64(rbx, rbx);
+
+ // rdi: subject string
+ // rbx: previous index
+ // 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();
+ __ IncrementCounter(counters->regexp_entry_native(), 1);
+
+ // Isolates: note we add an additional parameter here (isolate pointer).
+ static const int kRegExpExecuteArguments = 9;
+ int argument_slots_on_stack =
+ masm->ArgumentStackSlotsForCFunctionCall(kRegExpExecuteArguments);
+ __ EnterApiExitFrame(argument_slots_on_stack);
+
+ // Argument 9: Pass current isolate address.
+ __ LoadAddress(kScratchRegister,
+ ExternalReference::isolate_address(masm->isolate()));
+ __ movq(Operand(rsp, (argument_slots_on_stack - 1) * kPointerSize),
+ kScratchRegister);
+
+ // Argument 8: Indicate that this is a direct call from JavaScript.
+ __ movq(Operand(rsp, (argument_slots_on_stack - 2) * kPointerSize),
+ Immediate(1));
+
+ // Argument 7: Start (high end) of backtracking stack memory area.
+ __ movq(kScratchRegister, address_of_regexp_stack_memory_address);
+ __ movq(r9, Operand(kScratchRegister, 0));
+ __ movq(kScratchRegister, address_of_regexp_stack_memory_size);
+ __ addq(r9, Operand(kScratchRegister, 0));
+ __ movq(Operand(rsp, (argument_slots_on_stack - 3) * kPointerSize), r9);
+
+ // Argument 6: Set the number of capture registers to zero to force global
+ // regexps to behave as non-global. This does not affect non-global regexps.
+ // Argument 6 is passed in r9 on Linux and on the stack on Windows.
+#ifdef _WIN64
+ __ movq(Operand(rsp, (argument_slots_on_stack - 4) * kPointerSize),
+ Immediate(0));
+#else
+ __ Set(r9, 0);
+#endif
+
+ // Argument 5: static offsets vector buffer.
+ __ LoadAddress(r8,
+ ExternalReference::address_of_static_offsets_vector(isolate));
+ // Argument 5 passed in r8 on Linux and on the stack on Windows.
+#ifdef _WIN64
+ __ movq(Operand(rsp, (argument_slots_on_stack - 5) * kPointerSize), r8);
+#endif
+
+ // rdi: subject string
+ // rbx: previous index
+ // rcx: encoding of subject string (1 if ASCII 0 if two_byte);
+ // r11: code
+ // r14: slice offset
+ // r15: original subject string
+
+ // Argument 2: Previous index.
+ __ movq(arg_reg_2, rbx);
+
+ // Argument 4: End of string data
+ // Argument 3: Start of string data
+ Label setup_two_byte, setup_rest, got_length, length_not_from_slice;
+ // Prepare start and end index of the input.
+ // Load the length from the original sliced string if that is the case.
+ __ addq(rbx, r14);
+ __ SmiToInteger32(arg_reg_3, FieldOperand(r15, String::kLengthOffset));
+ __ addq(r14, arg_reg_3); // Using arg3 as scratch.
+
+ // rbx: start index of the input
+ // r14: end index of the input
+ // r15: original subject string
+ __ testb(rcx, rcx); // Last use of rcx as encoding of subject string.
+ __ j(zero, &setup_two_byte, Label::kNear);
+ __ lea(arg_reg_4,
+ FieldOperand(rdi, r14, times_1, SeqOneByteString::kHeaderSize));
+ __ lea(arg_reg_3,
+ FieldOperand(rdi, rbx, times_1, SeqOneByteString::kHeaderSize));
+ __ jmp(&setup_rest, Label::kNear);
+ __ bind(&setup_two_byte);
+ __ lea(arg_reg_4,
+ FieldOperand(rdi, r14, times_2, SeqTwoByteString::kHeaderSize));
+ __ lea(arg_reg_3,
+ FieldOperand(rdi, rbx, times_2, SeqTwoByteString::kHeaderSize));
+ __ bind(&setup_rest);
+
+ // Argument 1: Original subject string.
+ // The original subject is in the previous stack frame. Therefore we have to
+ // use rbp, which points exactly to one pointer size below the previous rsp.
+ // (Because creating a new stack frame pushes the previous rbp onto the stack
+ // and thereby moves up rsp by one kPointerSize.)
+ __ movq(arg_reg_1, r15);
+
+ // Locate the code entry and call it.
+ __ addq(r11, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ __ call(r11);
+
+ __ LeaveApiExitFrame();
+
+ // Check the result.
+ Label success;
+ Label exception;
+ __ cmpl(rax, Immediate(1));
+ // We expect exactly one result since we force the called regexp to behave
+ // as non-global.
+ __ j(equal, &success, Label::kNear);
+ __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::EXCEPTION));
+ __ j(equal, &exception);
+ __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::FAILURE));
+ // If none of the above, it can only be retry.
+ // Handle that in the runtime system.
+ __ j(not_equal, &runtime);
+
+ // For failure return null.
+ __ LoadRoot(rax, Heap::kNullValueRootIndex);
+ __ ret(4 * kPointerSize);
+
+ // Load RegExp data.
+ __ bind(&success);
+ __ movq(rax, Operand(rsp, kJSRegExpOffset));
+ __ movq(rcx, FieldOperand(rax, JSRegExp::kDataOffset));
+ __ SmiToInteger32(rax,
+ FieldOperand(rcx, JSRegExp::kIrregexpCaptureCountOffset));
+ // Calculate number of capture registers (number_of_captures + 1) * 2.
+ __ leal(rdx, Operand(rax, rax, times_1, 2));
+
+ // rdx: Number of capture registers
+ // Check that the fourth object is a JSArray object.
+ __ movq(r15, Operand(rsp, kLastMatchInfoOffset));
+ __ JumpIfSmi(r15, &runtime);
+ __ CmpObjectType(r15, JS_ARRAY_TYPE, kScratchRegister);
+ __ j(not_equal, &runtime);
+ // Check that the JSArray is in fast case.
+ __ movq(rbx, FieldOperand(r15, JSArray::kElementsOffset));
+ __ movq(rax, FieldOperand(rbx, HeapObject::kMapOffset));
+ __ CompareRoot(rax, Heap::kFixedArrayMapRootIndex);
+ __ j(not_equal, &runtime);
+ // Check that the last match info has space for the capture registers and the
+ // additional information. Ensure no overflow in add.
+ STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
+ __ SmiToInteger32(rax, FieldOperand(rbx, FixedArray::kLengthOffset));
+ __ subl(rax, Immediate(RegExpImpl::kLastMatchOverhead));
+ __ cmpl(rdx, rax);
+ __ j(greater, &runtime);
+
+ // rbx: last_match_info backing store (FixedArray)
+ // rdx: number of capture registers
+ // Store the capture count.
+ __ Integer32ToSmi(kScratchRegister, rdx);
+ __ movq(FieldOperand(rbx, RegExpImpl::kLastCaptureCountOffset),
+ kScratchRegister);
+ // Store last subject and last input.
+ __ movq(rax, Operand(rsp, kSubjectOffset));
+ __ movq(FieldOperand(rbx, RegExpImpl::kLastSubjectOffset), rax);
+ __ movq(rcx, rax);
+ __ RecordWriteField(rbx,
+ RegExpImpl::kLastSubjectOffset,
+ rax,
+ rdi,
+ kDontSaveFPRegs);
+ __ movq(rax, rcx);
+ __ movq(FieldOperand(rbx, RegExpImpl::kLastInputOffset), rax);
+ __ RecordWriteField(rbx,
+ RegExpImpl::kLastInputOffset,
+ rax,
+ rdi,
+ kDontSaveFPRegs);
+
+ // Get the static offsets vector filled by the native regexp code.
+ __ LoadAddress(rcx,
+ ExternalReference::address_of_static_offsets_vector(isolate));
+
+ // rbx: last_match_info backing store (FixedArray)
+ // rcx: offsets vector
+ // rdx: number of capture registers
+ Label next_capture, done;
+ // Capture register counter starts from number of capture registers and
+ // counts down until wraping after zero.
+ __ bind(&next_capture);
+ __ subq(rdx, Immediate(1));
+ __ j(negative, &done, Label::kNear);
+ // Read the value from the static offsets vector buffer and make it a smi.
+ __ movl(rdi, Operand(rcx, rdx, times_int_size, 0));
+ __ Integer32ToSmi(rdi, rdi);
+ // Store the smi value in the last match info.
+ __ movq(FieldOperand(rbx,
+ rdx,
+ times_pointer_size,
+ RegExpImpl::kFirstCaptureOffset),
+ rdi);
+ __ jmp(&next_capture);
+ __ bind(&done);
+
+ // Return last match info.
+ __ movq(rax, r15);
+ __ ret(4 * kPointerSize);
+
+ __ bind(&exception);
+ // Result must now be exception. If there is no pending exception already a
+ // 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.
+ ExternalReference pending_exception_address(
+ Isolate::kPendingExceptionAddress, isolate);
+ Operand pending_exception_operand =
+ masm->ExternalOperand(pending_exception_address, rbx);
+ __ movq(rax, pending_exception_operand);
+ __ LoadRoot(rdx, Heap::kTheHoleValueRootIndex);
+ __ cmpq(rax, rdx);
+ __ j(equal, &runtime);
+ __ movq(pending_exception_operand, rdx);
+
+ __ CompareRoot(rax, Heap::kTerminationExceptionRootIndex);
+ Label termination_exception;
+ __ j(equal, &termination_exception, Label::kNear);
+ __ Throw(rax);
+
+ __ bind(&termination_exception);
+ __ ThrowUncatchable(rax);
+
+ // Do the runtime call to execute the regexp.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+
+ // Deferred code for string handling.
+ // (7) Not a long external string? If yes, go to (10).
+ __ bind(&not_seq_nor_cons);
+ // Compare flags are still set from (3).
+ __ j(greater, &not_long_external, Label::kNear); // Go to (10).
+
+ // (8) External string. Short external strings have been ruled out.
+ __ 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, kExternalStringExpectedButNotFound);
+ }
+ __ movq(rdi, FieldOperand(rdi, ExternalString::kResourceDataOffset));
+ // Move the pointer so that offset-wise, it looks like a sequential string.
+ STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
+ __ subq(rdi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ // (8a) Is the external string one byte? If yes, go to (6).
+ __ testb(rbx, Immediate(kStringEncodingMask));
+ __ j(not_zero, &seq_one_byte_string); // Goto (6).
+
+ // rdi: subject string (flat two-byte)
+ // rax: RegExp data (FixedArray)
+ // (9) Two byte sequential. Load regexp code for one byte. Go to (E).
+ __ bind(&seq_two_byte_string);
+ __ movq(r11, FieldOperand(rax, JSRegExp::kDataUC16CodeOffset));
+ __ Set(rcx, 0); // Type is two byte.
+ __ jmp(&check_code); // Go to (E).
+
+ // (10) Not a string or a short external string? If yes, bail out to runtime.
+ __ bind(&not_long_external);
+ // Catch non-string subject or short external string.
+ STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
+ __ testb(rbx, Immediate(kIsNotStringMask | kShortExternalStringMask));
+ __ j(not_zero, &runtime);
+
+ // (11) Sliced string. Replace subject with parent. Go to (5a).
+ // Load offset into r14 and replace subject string with parent.
+ __ SmiToInteger32(r14, FieldOperand(rdi, SlicedString::kOffsetOffset));
+ __ movq(rdi, FieldOperand(rdi, SlicedString::kParentOffset));
+ __ jmp(&check_underlying);
+#endif // V8_INTERPRETED_REGEXP
+}
+
+
+void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
+ const int kMaxInlineLength = 100;
+ Label slowcase;
+ Label done;
+ __ movq(r8, Operand(rsp, kPointerSize * 3));
+ __ JumpIfNotSmi(r8, &slowcase);
+ __ SmiToInteger32(rbx, r8);
+ __ cmpl(rbx, Immediate(kMaxInlineLength));
+ __ j(above, &slowcase);
+ // Smi-tagging is equivalent to multiplying by 2.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ // Allocate RegExpResult followed by FixedArray with size in rbx.
+ // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
+ // Elements: [Map][Length][..elements..]
+ __ Allocate(JSRegExpResult::kSize + FixedArray::kHeaderSize,
+ times_pointer_size,
+ rbx, // In: Number of elements.
+ rax, // Out: Start of allocation (tagged).
+ rcx, // Out: End of allocation.
+ rdx, // Scratch register
+ &slowcase,
+ TAG_OBJECT);
+ // rax: Start of allocated area, object-tagged.
+ // rbx: Number of array elements as int32.
+ // r8: Number of array elements as smi.
+
+ // Set JSArray map to global.regexp_result_map().
+ __ movq(rdx, ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX));
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kNativeContextOffset));
+ __ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX));
+ __ movq(FieldOperand(rax, HeapObject::kMapOffset), rdx);
+
+ // Set empty properties FixedArray.
+ __ LoadRoot(kScratchRegister, Heap::kEmptyFixedArrayRootIndex);
+ __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), kScratchRegister);
+
+ // Set elements to point to FixedArray allocated right after the JSArray.
+ __ lea(rcx, Operand(rax, JSRegExpResult::kSize));
+ __ movq(FieldOperand(rax, JSObject::kElementsOffset), rcx);
+
+ // Set input, index and length fields from arguments.
+ __ movq(r8, Operand(rsp, kPointerSize * 1));
+ __ movq(FieldOperand(rax, JSRegExpResult::kInputOffset), r8);
+ __ movq(r8, Operand(rsp, kPointerSize * 2));
+ __ movq(FieldOperand(rax, JSRegExpResult::kIndexOffset), r8);
+ __ movq(r8, Operand(rsp, kPointerSize * 3));
+ __ movq(FieldOperand(rax, JSArray::kLengthOffset), r8);
+
+ // Fill out the elements FixedArray.
+ // rax: JSArray.
+ // rcx: FixedArray.
+ // rbx: Number of elements in array as int32.
+
+ // Set map.
+ __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex);
+ __ movq(FieldOperand(rcx, HeapObject::kMapOffset), kScratchRegister);
+ // Set length.
+ __ Integer32ToSmi(rdx, rbx);
+ __ movq(FieldOperand(rcx, FixedArray::kLengthOffset), rdx);
+ // Fill contents of fixed-array with undefined.
+ __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
+ __ lea(rcx, FieldOperand(rcx, FixedArray::kHeaderSize));
+ // Fill fixed array elements with undefined.
+ // rax: JSArray.
+ // rbx: Number of elements in array that remains to be filled, as int32.
+ // rcx: Start of elements in FixedArray.
+ // rdx: undefined.
+ Label loop;
+ __ testl(rbx, rbx);
+ __ bind(&loop);
+ __ j(less_equal, &done); // Jump if rcx is negative or zero.
+ __ subl(rbx, Immediate(1));
+ __ movq(Operand(rcx, rbx, times_pointer_size, 0), rdx);
+ __ jmp(&loop);
+
+ __ bind(&done);
+ __ ret(3 * kPointerSize);
+
+ __ bind(&slowcase);
+ __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
+}
+
+
+void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* not_found) {
+ // Use of registers. Register result is used as a temporary.
+ Register number_string_cache = result;
+ Register mask = scratch1;
+ Register scratch = scratch2;
+
+ // Load the number string cache.
+ __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
+
+ // Make the hash mask from the length of the number string cache. It
+ // contains two elements (number and string) for each cache entry.
+ __ SmiToInteger32(
+ mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
+ __ shrl(mask, Immediate(1));
+ __ subq(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
+ // doubles is the xor of the upper and lower words. See
+ // Heap::GetNumberStringCache.
+ Label is_smi;
+ Label load_result_from_cache;
+ Factory* factory = masm->isolate()->factory();
+ __ JumpIfSmi(object, &is_smi);
+ __ CheckMap(object,
+ factory->heap_number_map(),
+ not_found,
+ DONT_DO_SMI_CHECK);
+
+ STATIC_ASSERT(8 == kDoubleSize);
+ __ movl(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
+ __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset));
+ GenerateConvertHashCodeToIndex(masm, scratch, mask);
+
+ Register index = scratch;
+ Register probe = mask;
+ __ movq(probe,
+ FieldOperand(number_string_cache,
+ index,
+ times_1,
+ FixedArray::kHeaderSize));
+ __ JumpIfSmi(probe, not_found);
+ __ movsd(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
+ __ movsd(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
+ __ ucomisd(xmm0, xmm1);
+ __ j(parity_even, not_found); // Bail out if NaN is involved.
+ __ j(not_equal, not_found); // The cache did not contain this value.
+ __ jmp(&load_result_from_cache);
+
+ __ bind(&is_smi);
+ __ SmiToInteger32(scratch, object);
+ GenerateConvertHashCodeToIndex(masm, scratch, mask);
+
+ // Check if the entry is the smi we are looking for.
+ __ cmpq(object,
+ FieldOperand(number_string_cache,
+ index,
+ times_1,
+ FixedArray::kHeaderSize));
+ __ j(not_equal, not_found);
+
+ // Get the result from the cache.
+ __ bind(&load_result_from_cache);
+ __ movq(result,
+ FieldOperand(number_string_cache,
+ index,
+ times_1,
+ FixedArray::kHeaderSize + kPointerSize));
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->number_to_string_native(), 1);
+}
+
+
+void NumberToStringStub::GenerateConvertHashCodeToIndex(MacroAssembler* masm,
+ Register hash,
+ Register mask) {
+ __ and_(hash, mask);
+ // Each entry in string cache consists of two pointer sized fields,
+ // but times_twice_pointer_size (multiplication by 16) scale factor
+ // is not supported by addrmode on x64 platform.
+ // So we have to premultiply entry index before lookup.
+ __ shl(hash, Immediate(kPointerSizeLog2 + 1));
+}
+
+
+void NumberToStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ __ movq(rbx, Operand(rsp, kPointerSize));
+
+ // Generate code to lookup number in the number string cache.
+ GenerateLookupNumberStringCache(masm, rbx, rax, r8, r9, &runtime);
+ __ ret(1 * kPointerSize);
+
+ __ bind(&runtime);
+ // Handle number to string in the runtime system if not found in the cache.
+ __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
+}
+
+
+static int NegativeComparisonResult(Condition cc) {
+ ASSERT(cc != equal);
+ ASSERT((cc == less) || (cc == less_equal)
+ || (cc == greater) || (cc == greater_equal));
+ return (cc == greater || cc == greater_equal) ? LESS : GREATER;
+}
+
+
+static void CheckInputType(MacroAssembler* masm,
+ Register input,
+ CompareIC::State expected,
+ Label* fail) {
+ Label ok;
+ if (expected == CompareIC::SMI) {
+ __ JumpIfNotSmi(input, fail);
+ } else if (expected == CompareIC::NUMBER) {
+ __ JumpIfSmi(input, &ok);
+ __ CompareMap(input, masm->isolate()->factory()->heap_number_map(), NULL);
+ __ j(not_equal, fail);
+ }
+ // We could be strict about internalized/non-internalized here, but as long as
+ // hydrogen doesn't care, the stub doesn't have to care either.
+ __ bind(&ok);
+}
+
+
+static void BranchIfNotInternalizedString(MacroAssembler* masm,
+ Label* label,
+ Register object,
+ Register scratch) {
+ __ JumpIfSmi(object, label);
+ __ movq(scratch, FieldOperand(object, HeapObject::kMapOffset));
+ __ movzxbq(scratch,
+ FieldOperand(scratch, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ testb(scratch, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
+ __ j(not_zero, label);
+}
+
+
+void ICCompareStub::GenerateGeneric(MacroAssembler* masm) {
+ Label check_unequal_objects, done;
+ Condition cc = GetCondition();
+ Factory* factory = masm->isolate()->factory();
+
+ Label miss;
+ CheckInputType(masm, rdx, left_, &miss);
+ CheckInputType(masm, rax, right_, &miss);
+
+ // Compare two smis.
+ Label non_smi, smi_done;
+ __ JumpIfNotBothSmi(rax, rdx, &non_smi);
+ __ subq(rdx, rax);
+ __ j(no_overflow, &smi_done);
+ __ not_(rdx); // Correct sign in case of overflow. rdx cannot be 0 here.
+ __ bind(&smi_done);
+ __ movq(rax, rdx);
+ __ ret(0);
+ __ bind(&non_smi);
+
+ // The compare stub returns a positive, negative, or zero 64-bit integer
+ // value in rax, corresponding to result of comparing the two inputs.
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+ // it is certain that at least one operand isn't a smi.
+
+ // Two identical objects are equal unless they are both NaN or undefined.
+ {
+ Label not_identical;
+ __ cmpq(rax, rdx);
+ __ j(not_equal, &not_identical, Label::kNear);
+
+ if (cc != equal) {
+ // Check for undefined. undefined OP undefined is false even though
+ // undefined == undefined.
+ Label check_for_nan;
+ __ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, &check_for_nan, Label::kNear);
+ __ Set(rax, NegativeComparisonResult(cc));
+ __ ret(0);
+ __ bind(&check_for_nan);
+ }
+
+ // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
+ // so we do the second best thing - test it ourselves.
+ Label heap_number;
+ // If it's not a heap number, then return equal for (in)equality operator.
+ __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
+ factory->heap_number_map());
+ __ j(equal, &heap_number, Label::kNear);
+ if (cc != equal) {
+ // Call runtime on identical objects. Otherwise return equal.
+ __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
+ __ j(above_equal, &not_identical, Label::kNear);
+ }
+ __ Set(rax, EQUAL);
+ __ ret(0);
+
+ __ bind(&heap_number);
+ // It is a heap number, so return equal if it's not NaN.
+ // For NaN, return 1 for every condition except greater and
+ // greater-equal. Return -1 for them, so the comparison yields
+ // false for all conditions except not-equal.
+ __ Set(rax, EQUAL);
+ __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
+ __ ucomisd(xmm0, xmm0);
+ __ setcc(parity_even, rax);
+ // rax is 0 for equal non-NaN heapnumbers, 1 for NaNs.
+ if (cc == greater_equal || cc == greater) {
+ __ neg(rax);
+ }
+ __ ret(0);
+
+ __ bind(&not_identical);
+ }
+
+ if (cc == equal) { // Both strict and non-strict.
+ Label slow; // Fallthrough label.
+
+ // If we're doing a strict equality comparison, we don't have to do
+ // type conversion, so we generate code to do fast comparison for objects
+ // and oddballs. Non-smi numbers and strings still go through the usual
+ // slow-case code.
+ if (strict()) {
+ // If either is a Smi (we know that not both are), then they can only
+ // be equal if the other is a HeapNumber. If so, use the slow case.
+ {
+ Label not_smis;
+ __ SelectNonSmi(rbx, rax, rdx, &not_smis);
+
+ // Check if the non-smi operand is a heap number.
+ __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
+ factory->heap_number_map());
+ // If heap number, handle it in the slow case.
+ __ j(equal, &slow);
+ // Return non-equal. ebx (the lower half of rbx) is not zero.
+ __ movq(rax, rbx);
+ __ ret(0);
+
+ __ bind(&not_smis);
+ }
+
+ // If either operand is a JSObject or an oddball value, then they are not
+ // equal since their pointers are different
+ // There is no test for undetectability in strict equality.
+
+ // If the first object is a JS object, we have done pointer comparison.
+ STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
+ Label first_non_object;
+ __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
+ __ j(below, &first_non_object, Label::kNear);
+ // Return non-zero (rax (not rax) is not zero)
+ Label return_not_equal;
+ STATIC_ASSERT(kHeapObjectTag != 0);
+ __ bind(&return_not_equal);
+ __ ret(0);
+
+ __ bind(&first_non_object);
+ // Check for oddballs: true, false, null, undefined.
+ __ CmpInstanceType(rcx, ODDBALL_TYPE);
+ __ j(equal, &return_not_equal);
+
+ __ CmpObjectType(rdx, FIRST_SPEC_OBJECT_TYPE, rcx);
+ __ j(above_equal, &return_not_equal);
+
+ // Check for oddballs: true, false, null, undefined.
+ __ CmpInstanceType(rcx, ODDBALL_TYPE);
+ __ j(equal, &return_not_equal);
+
+ // Fall through to the general case.
+ }
+ __ bind(&slow);
+ }
+
+ // Generate the number comparison code.
+ Label non_number_comparison;
+ Label unordered;
+ FloatingPointHelper::LoadSSE2UnknownOperands(masm, &non_number_comparison);
+ __ xorl(rax, rax);
+ __ xorl(rcx, rcx);
+ __ ucomisd(xmm0, xmm1);
+
+ // Don't base result on EFLAGS when a NaN is involved.
+ __ j(parity_even, &unordered, Label::kNear);
+ // Return a result of -1, 0, or 1, based on EFLAGS.
+ __ setcc(above, rax);
+ __ setcc(below, rcx);
+ __ subq(rax, rcx);
+ __ ret(0);
+
+ // If one of the numbers was NaN, then the result is always false.
+ // The cc is never not-equal.
+ __ bind(&unordered);
+ ASSERT(cc != not_equal);
+ if (cc == less || cc == less_equal) {
+ __ Set(rax, 1);
+ } else {
+ __ Set(rax, -1);
+ }
+ __ ret(0);
+
+ // The number comparison code did not provide a valid result.
+ __ bind(&non_number_comparison);
+
+ // Fast negative check for internalized-to-internalized equality.
+ Label check_for_strings;
+ if (cc == equal) {
+ BranchIfNotInternalizedString(
+ masm, &check_for_strings, rax, kScratchRegister);
+ BranchIfNotInternalizedString(
+ masm, &check_for_strings, rdx, kScratchRegister);
+
+ // We've already checked for object identity, so if both operands are
+ // internalized strings they aren't equal. Register rax (not rax) already
+ // holds a non-zero value, which indicates not equal, so just return.
+ __ ret(0);
+ }
+
+ __ bind(&check_for_strings);
+
+ __ JumpIfNotBothSequentialAsciiStrings(
+ rdx, rax, rcx, rbx, &check_unequal_objects);
+
+ // Inline comparison of ASCII strings.
+ if (cc == equal) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
+ rdx,
+ rax,
+ rcx,
+ rbx);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ rdx,
+ rax,
+ rcx,
+ rbx,
+ rdi,
+ r8);
+ }
+
+#ifdef DEBUG
+ __ Abort(kUnexpectedFallThroughFromStringComparison);
+#endif
+
+ __ bind(&check_unequal_objects);
+ if (cc == equal && !strict()) {
+ // Not strict equality. Objects are unequal if
+ // they are both JSObjects and not undetectable,
+ // and their pointers are different.
+ Label not_both_objects, return_unequal;
+ // At most one is a smi, so we can test for smi by adding the two.
+ // A smi plus a heap object has the low bit set, a heap object plus
+ // a heap object has the low bit clear.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagMask == 1);
+ __ lea(rcx, Operand(rax, rdx, times_1, 0));
+ __ testb(rcx, Immediate(kSmiTagMask));
+ __ j(not_zero, &not_both_objects, Label::kNear);
+ __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rbx);
+ __ j(below, &not_both_objects, Label::kNear);
+ __ CmpObjectType(rdx, FIRST_SPEC_OBJECT_TYPE, rcx);
+ __ j(below, &not_both_objects, Label::kNear);
+ __ testb(FieldOperand(rbx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ __ j(zero, &return_unequal, Label::kNear);
+ __ testb(FieldOperand(rcx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ __ j(zero, &return_unequal, Label::kNear);
+ // The objects are both undetectable, so they both compare as the value
+ // undefined, and are equal.
+ __ Set(rax, EQUAL);
+ __ bind(&return_unequal);
+ // Return non-equal by returning the non-zero object pointer in rax,
+ // or return equal if we fell through to here.
+ __ ret(0);
+ __ bind(&not_both_objects);
+ }
+
+ // Push arguments below the return address to prepare jump to builtin.
+ __ PopReturnAddressTo(rcx);
+ __ push(rdx);
+ __ push(rax);
+
+ // Figure out which native to call and setup the arguments.
+ Builtins::JavaScript builtin;
+ if (cc == equal) {
+ builtin = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
+ } else {
+ builtin = Builtins::COMPARE;
+ __ Push(Smi::FromInt(NegativeComparisonResult(cc)));
+ }
+
+ __ PushReturnAddressFrom(rcx);
+
+ // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ InvokeBuiltin(builtin, JUMP_FUNCTION);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void StackCheckStub::Generate(MacroAssembler* masm) {
+ __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
+}
+
+
+void InterruptStub::Generate(MacroAssembler* masm) {
+ __ TailCallRuntime(Runtime::kInterrupt, 0, 1);
+}
+
+
+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, miss, megamorphic, not_array_function;
+
+ // Load the cache state into rcx.
+ __ movq(rcx, FieldOperand(rbx, Cell::kValueOffset));
+
+ // A monomorphic cache hit or an already megamorphic state: invoke the
+ // function without changing the state.
+ __ cmpq(rcx, rdi);
+ __ j(equal, &done);
+ __ Cmp(rcx, TypeFeedbackCells::MegamorphicSentinel(isolate));
+ __ j(equal, &done);
+
+ // If we came here, we need to see if we are the array function.
+ // If we didn't have a matching function, and we didn't find the megamorph
+ // sentinel, then we have in the cell either some other function or an
+ // AllocationSite. Do a map check on the object in rcx.
+ Handle<Map> allocation_site_map(
+ masm->isolate()->heap()->allocation_site_map(),
+ masm->isolate());
+ __ Cmp(FieldOperand(rcx, 0), allocation_site_map);
+ __ j(not_equal, &miss);
+
+ // Make sure the function is the Array() function
+ __ LoadArrayFunction(rcx);
+ __ cmpq(rdi, rcx);
+ __ j(not_equal, &megamorphic);
+ __ jmp(&done);
+
+ __ bind(&miss);
+
+ // A monomorphic miss (i.e, here the cache is not uninitialized) goes
+ // megamorphic.
+ __ Cmp(rcx, TypeFeedbackCells::UninitializedSentinel(isolate));
+ __ j(equal, &initialize);
+ // MegamorphicSentinel is an immortal immovable object (undefined) so no
+ // write-barrier is needed.
+ __ bind(&megamorphic);
+ __ Move(FieldOperand(rbx, Cell::kValueOffset),
+ TypeFeedbackCells::MegamorphicSentinel(isolate));
+ __ jmp(&done);
+
+ // An uninitialized cache is patched with the function or sentinel to
+ // indicate the ElementsKind if function is the Array constructor.
+ __ bind(&initialize);
+ // Make sure the function is the Array() function
+ __ LoadArrayFunction(rcx);
+ __ cmpq(rdi, rcx);
+ __ j(not_equal, &not_array_function);
+
+ // The target function is the Array constructor,
+ // Create an AllocationSite if we don't already have it, store it in the cell
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ __ push(rax);
+ __ push(rdi);
+ __ push(rbx);
+
+ CreateAllocationSiteStub create_stub;
+ __ CallStub(&create_stub);
+
+ __ pop(rbx);
+ __ pop(rdi);
+ __ pop(rax);
+ }
+ __ jmp(&done);
+
+ __ bind(&not_array_function);
+ __ movq(FieldOperand(rbx, Cell::kValueOffset), rdi);
+ // No need for a write barrier here - cells are rescanned.
+
+ __ bind(&done);
+}
+
+
+void CallFunctionStub::Generate(MacroAssembler* masm) {
+ // rbx : cache cell for call target
+ // rdi : 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;
+ // Get the receiver from the stack.
+ // +1 ~ return address
+ __ movq(rax, Operand(rsp, (argc_ + 1) * kPointerSize));
+ // Call as function is indicated with the hole.
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &call, Label::kNear);
+ // Patch the receiver on the stack with the global receiver object.
+ __ movq(rcx, GlobalObjectOperand());
+ __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rcx);
+ __ bind(&call);
+ }
+
+ // Check that the function really is a JavaScript function.
+ __ JumpIfSmi(rdi, &non_function);
+ // Goto slow case if we do not have a function.
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &slow);
+
+ if (RecordCallTarget()) {
+ GenerateRecordCallTarget(masm);
+ }
+
+ // Fast-case: Just invoke the function.
+ ParameterCount actual(argc_);
+
+ if (ReceiverMightBeImplicit()) {
+ Label call_as_function;
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &call_as_function);
+ __ InvokeFunction(rdi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_METHOD);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(rdi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
+
+ // 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.
+ __ Move(FieldOperand(rbx, Cell::kValueOffset),
+ TypeFeedbackCells::MegamorphicSentinel(isolate));
+ }
+ // Check for function proxy.
+ __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
+ __ j(not_equal, &non_function);
+ __ PopReturnAddressTo(rcx);
+ __ push(rdi); // put proxy as additional argument under return address
+ __ PushReturnAddressFrom(rcx);
+ __ Set(rax, argc_ + 1);
+ __ Set(rbx, 0);
+ __ SetCallKind(rcx, CALL_AS_METHOD);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
+ {
+ Handle<Code> adaptor =
+ masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ __ jmp(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);
+ __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdi);
+ __ Set(rax, argc_);
+ __ Set(rbx, 0);
+ __ SetCallKind(rcx, CALL_AS_METHOD);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
+ Handle<Code> adaptor =
+ Isolate::Current()->builtins()->ArgumentsAdaptorTrampoline();
+ __ Jump(adaptor, RelocInfo::CODE_TARGET);
+}
+
+
+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.
+ Register jmp_reg = rcx;
+ __ movq(jmp_reg, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movq(jmp_reg, FieldOperand(jmp_reg,
+ SharedFunctionInfo::kConstructStubOffset));
+ __ lea(jmp_reg, FieldOperand(jmp_reg, Code::kHeaderSize));
+ __ jmp(jmp_reg);
+
+ // 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(Isolate* isolate) {
+ CEntryStub::GenerateAheadOfTime(isolate);
+ StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ StubFailureTrampolineStub::GenerateAheadOfTime(isolate);
+ // It is important that the store buffer overflow stubs are generated first.
+ RecordWriteStub::GenerateFixedRegStubsAheadOfTime(isolate);
+ ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);
+ CreateAllocationSiteStub::GenerateAheadOfTime(isolate);
+}
+
+
+void CodeStub::GenerateFPStubs(Isolate* isolate) {
+}
+
+
+void CEntryStub::GenerateAheadOfTime(Isolate* isolate) {
+ CEntryStub stub(1, kDontSaveFPRegs);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ CEntryStub save_doubles(1, kSaveFPRegs);
+ save_doubles.GetCode(isolate)->set_is_pregenerated(true);
+}
+
+
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ __ movq(scratch, value);
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ and_(scratch, Immediate(0xf));
+ __ cmpq(scratch, Immediate(0xf));
+ __ j(equal, oom_label);
+}
+
+
+void CEntryStub::GenerateCore(MacroAssembler* masm,
+ Label* throw_normal_exception,
+ Label* throw_termination_exception,
+ Label* throw_out_of_memory_exception,
+ bool do_gc,
+ bool always_allocate_scope) {
+ // rax: result parameter for PerformGC, if any.
+ // rbx: pointer to C function (C callee-saved).
+ // rbp: frame pointer (restored after C call).
+ // rsp: stack pointer (restored after C call).
+ // r14: number of arguments including receiver (C callee-saved).
+ // r15: pointer to the first argument (C callee-saved).
+ // This pointer is reused in LeaveExitFrame(), so it is stored in a
+ // callee-saved register.
+
+ // Simple results returned in rax (both AMD64 and Win64 calling conventions).
+ // Complex results must be written to address passed as first argument.
+ // AMD64 calling convention: a struct of two pointers in rax+rdx
+
+ // Check stack alignment.
+ if (FLAG_debug_code) {
+ __ CheckStackAlignment();
+ }
+
+ if (do_gc) {
+ // Pass failure code returned from last attempt as first argument to
+ // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
+ // stack is known to be aligned. This function takes one argument which is
+ // passed in register.
+ __ movq(arg_reg_1, rax);
+ __ movq(kScratchRegister,
+ ExternalReference::perform_gc_function(masm->isolate()));
+ __ call(kScratchRegister);
+ }
+
+ ExternalReference scope_depth =
+ ExternalReference::heap_always_allocate_scope_depth(masm->isolate());
+ if (always_allocate_scope) {
+ Operand scope_depth_operand = masm->ExternalOperand(scope_depth);
+ __ incl(scope_depth_operand);
+ }
+
+ // Call C function.
+#ifdef _WIN64
+ // Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9.
+ // Pass argv and argc as two parameters. The arguments object will
+ // be created by stubs declared by DECLARE_RUNTIME_FUNCTION().
+ if (result_size_ < 2) {
+ // Pass a pointer to the Arguments object as the first argument.
+ // Return result in single register (rax).
+ __ movq(rcx, r14); // argc.
+ __ movq(rdx, r15); // argv.
+ __ movq(r8, ExternalReference::isolate_address(masm->isolate()));
+ } else {
+ ASSERT_EQ(2, result_size_);
+ // Pass a pointer to the result location as the first argument.
+ __ lea(rcx, StackSpaceOperand(2));
+ // Pass a pointer to the Arguments object as the second argument.
+ __ movq(rdx, r14); // argc.
+ __ movq(r8, r15); // argv.
+ __ movq(r9, ExternalReference::isolate_address(masm->isolate()));
+ }
+
+#else // _WIN64
+ // GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
+ __ movq(rdi, r14); // argc.
+ __ movq(rsi, r15); // argv.
+ __ movq(rdx, ExternalReference::isolate_address(masm->isolate()));
+#endif
+ __ call(rbx);
+ // Result is in rax - do not destroy this register!
+
+ if (always_allocate_scope) {
+ Operand scope_depth_operand = masm->ExternalOperand(scope_depth);
+ __ decl(scope_depth_operand);
+ }
+
+ // Check for failure result.
+ Label failure_returned;
+ STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
+#ifdef _WIN64
+ // If return value is on the stack, pop it to registers.
+ if (result_size_ > 1) {
+ ASSERT_EQ(2, result_size_);
+ // Read result values stored on stack. Result is stored
+ // above the four argument mirror slots and the two
+ // Arguments object slots.
+ __ movq(rax, Operand(rsp, 6 * kPointerSize));
+ __ movq(rdx, Operand(rsp, 7 * kPointerSize));
+ }
+#endif
+ __ lea(rcx, Operand(rax, 1));
+ // Lower 2 bits of rcx are 0 iff rax has failure tag.
+ __ testl(rcx, Immediate(kFailureTagMask));
+ __ j(zero, &failure_returned);
+
+ // Exit the JavaScript to C++ exit frame.
+ __ LeaveExitFrame(save_doubles_);
+ __ ret(0);
+
+ // Handling of failure.
+ __ bind(&failure_returned);
+
+ Label retry;
+ // If the returned exception is RETRY_AFTER_GC continue at retry label
+ STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
+ __ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
+ __ j(zero, &retry, Label::kNear);
+
+ // Special handling of out of memory exceptions.
+ JumpIfOOM(masm, rax, kScratchRegister, throw_out_of_memory_exception);
+
+ // Retrieve the pending exception.
+ ExternalReference pending_exception_address(
+ Isolate::kPendingExceptionAddress, masm->isolate());
+ Operand pending_exception_operand =
+ masm->ExternalOperand(pending_exception_address);
+ __ movq(rax, pending_exception_operand);
+
+ // See if we just retrieved an OOM exception.
+ JumpIfOOM(masm, rax, kScratchRegister, throw_out_of_memory_exception);
+
+ // Clear the pending exception.
+ pending_exception_operand =
+ masm->ExternalOperand(pending_exception_address);
+ __ LoadRoot(rdx, Heap::kTheHoleValueRootIndex);
+ __ movq(pending_exception_operand, rdx);
+
+ // Special handling of termination exceptions which are uncatchable
+ // by javascript code.
+ __ CompareRoot(rax, Heap::kTerminationExceptionRootIndex);
+ __ j(equal, throw_termination_exception);
+
+ // Handle normal exception.
+ __ jmp(throw_normal_exception);
+
+ // Retry.
+ __ bind(&retry);
+}
+
+
+void CEntryStub::Generate(MacroAssembler* masm) {
+ // rax: number of arguments including receiver
+ // rbx: pointer to C function (C callee-saved)
+ // rbp: frame pointer of calling JS frame (restored after C call)
+ // rsp: stack pointer (restored after C call)
+ // rsi: current context (restored)
+
+ // NOTE: Invocations of builtins may return failure objects
+ // instead of a proper result. The builtin entry handles
+ // this by performing a garbage collection and retrying the
+ // builtin once.
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ // Enter the exit frame that transitions from JavaScript to C++.
+#ifdef _WIN64
+ int arg_stack_space = (result_size_ < 2 ? 2 : 4);
+#else
+ int arg_stack_space = 0;
+#endif
+ __ EnterExitFrame(arg_stack_space, save_doubles_);
+
+ // rax: Holds the context at this point, but should not be used.
+ // On entry to code generated by GenerateCore, it must hold
+ // a failure result if the collect_garbage argument to GenerateCore
+ // is true. This failure result can be the result of code
+ // generated by a previous call to GenerateCore. The value
+ // of rax is then passed to Runtime::PerformGC.
+ // rbx: pointer to builtin function (C callee-saved).
+ // rbp: frame pointer of exit frame (restored after C call).
+ // rsp: stack pointer (restored after C call).
+ // r14: number of arguments including receiver (C callee-saved).
+ // r15: argv pointer (C callee-saved).
+
+ Label throw_normal_exception;
+ Label throw_termination_exception;
+ Label throw_out_of_memory_exception;
+
+ // Call into the runtime system.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ false,
+ false);
+
+ // Do space-specific GC and retry runtime call.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ false);
+
+ // Do full GC and retry runtime call one final time.
+ Failure* failure = Failure::InternalError();
+ __ movq(rax, failure, RelocInfo::NONE64);
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ true);
+
+ __ bind(&throw_out_of_memory_exception);
+ // Set external caught exception to false.
+ Isolate* isolate = masm->isolate();
+ ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
+ isolate);
+ __ Set(rax, static_cast<int64_t>(false));
+ __ Store(external_caught, rax);
+
+ // Set pending exception and rax to out of memory exception.
+ ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
+ isolate);
+ Label already_have_failure;
+ JumpIfOOM(masm, rax, kScratchRegister, &already_have_failure);
+ __ movq(rax, Failure::OutOfMemoryException(0x1), RelocInfo::NONE64);
+ __ bind(&already_have_failure);
+ __ Store(pending_exception, rax);
+ // Fall through to the next label.
+
+ __ bind(&throw_termination_exception);
+ __ ThrowUncatchable(rax);
+
+ __ bind(&throw_normal_exception);
+ __ Throw(rax);
+}
+
+
+void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
+ Label invoke, handler_entry, exit;
+ Label not_outermost_js, not_outermost_js_2;
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm);
+
+ { // NOLINT. Scope block confuses linter.
+ MacroAssembler::NoRootArrayScope uninitialized_root_register(masm);
+ // Set up frame.
+ __ push(rbp);
+ __ movq(rbp, rsp);
+
+ // Push the stack frame type marker twice.
+ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
+ // Scratch register is neither callee-save, nor an argument register on any
+ // platform. It's free to use at this point.
+ // Cannot use smi-register for loading yet.
+ __ movq(kScratchRegister,
+ reinterpret_cast<uint64_t>(Smi::FromInt(marker)),
+ RelocInfo::NONE64);
+ __ push(kScratchRegister); // context slot
+ __ push(kScratchRegister); // function slot
+ // Save callee-saved registers (X64/Win64 calling conventions).
+ __ push(r12);
+ __ push(r13);
+ __ push(r14);
+ __ push(r15);
+#ifdef _WIN64
+ __ push(rdi); // Only callee save in Win64 ABI, argument in AMD64 ABI.
+ __ push(rsi); // Only callee save in Win64 ABI, argument in AMD64 ABI.
+#endif
+ __ push(rbx);
+
+#ifdef _WIN64
+ // On Win64 XMM6-XMM15 are callee-save
+ __ subq(rsp, Immediate(EntryFrameConstants::kXMMRegistersBlockSize));
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 0), xmm6);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 1), xmm7);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 2), xmm8);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 3), xmm9);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 4), xmm10);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 5), xmm11);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 6), xmm12);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 7), xmm13);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 8), xmm14);
+ __ movdqu(Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 9), xmm15);
+#endif
+
+ // Set up the roots and smi constant registers.
+ // Needs to be done before any further smi loads.
+ __ InitializeSmiConstantRegister();
+ __ InitializeRootRegister();
+ }
+
+ Isolate* isolate = masm->isolate();
+
+ // Save copies of the top frame descriptor on the stack.
+ ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, isolate);
+ {
+ Operand c_entry_fp_operand = masm->ExternalOperand(c_entry_fp);
+ __ push(c_entry_fp_operand);
+ }
+
+ // If this is the outermost JS call, set js_entry_sp value.
+ ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, isolate);
+ __ Load(rax, js_entry_sp);
+ __ testq(rax, rax);
+ __ j(not_zero, &not_outermost_js);
+ __ Push(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME));
+ __ movq(rax, rbp);
+ __ Store(js_entry_sp, rax);
+ Label cont;
+ __ jmp(&cont);
+ __ bind(&not_outermost_js);
+ __ Push(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME));
+ __ bind(&cont);
+
+ // 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::NONE64);
+ __ jmp(&exit);
+
+ // 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(StackHandler::JS_ENTRY, 0);
+
+ // Clear any pending exceptions.
+ __ LoadRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ Store(pending_exception, rax);
+
+ // 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.
+ if (is_construct) {
+ ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
+ isolate);
+ __ Load(rax, construct_entry);
+ } else {
+ ExternalReference entry(Builtins::kJSEntryTrampoline, isolate);
+ __ Load(rax, entry);
+ }
+ __ lea(kScratchRegister, FieldOperand(rax, Code::kHeaderSize));
+ __ call(kScratchRegister);
+
+ // Unlink this frame from the handler chain.
+ __ PopTryHandler();
+
+ __ bind(&exit);
+ // Check if the current stack frame is marked as the outermost JS frame.
+ __ pop(rbx);
+ __ Cmp(rbx, Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME));
+ __ j(not_equal, &not_outermost_js_2);
+ __ movq(kScratchRegister, js_entry_sp);
+ __ movq(Operand(kScratchRegister, 0), Immediate(0));
+ __ bind(&not_outermost_js_2);
+
+ // Restore the top frame descriptor from the stack.
+ { Operand c_entry_fp_operand = masm->ExternalOperand(c_entry_fp);
+ __ pop(c_entry_fp_operand);
+ }
+
+ // Restore callee-saved registers (X64 conventions).
+#ifdef _WIN64
+ // On Win64 XMM6-XMM15 are callee-save
+ __ movdqu(xmm6, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 0));
+ __ movdqu(xmm7, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 1));
+ __ movdqu(xmm8, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 2));
+ __ movdqu(xmm9, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 3));
+ __ movdqu(xmm10, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 4));
+ __ movdqu(xmm11, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 5));
+ __ movdqu(xmm12, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 6));
+ __ movdqu(xmm13, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 7));
+ __ movdqu(xmm14, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 8));
+ __ movdqu(xmm15, Operand(rsp, EntryFrameConstants::kXMMRegisterSize * 9));
+ __ addq(rsp, Immediate(EntryFrameConstants::kXMMRegistersBlockSize));
+#endif
+
+ __ pop(rbx);
+#ifdef _WIN64
+ // Callee save on in Win64 ABI, arguments/volatile in AMD64 ABI.
+ __ pop(rsi);
+ __ pop(rdi);
+#endif
+ __ pop(r15);
+ __ pop(r14);
+ __ pop(r13);
+ __ pop(r12);
+ __ addq(rsp, Immediate(2 * kPointerSize)); // remove markers
+
+ // Restore frame pointer and return.
+ __ pop(rbp);
+ __ ret(0);
+}
+
+
+void InstanceofStub::Generate(MacroAssembler* masm) {
+ // Implements "value instanceof function" operator.
+ // Expected input state with no inline cache:
+ // rsp[0] : return address
+ // rsp[8] : function pointer
+ // rsp[16] : value
+ // Expected input state with an inline one-element cache:
+ // rsp[0] : return address
+ // rsp[8] : offset from return address to location of inline cache
+ // rsp[16] : function pointer
+ // rsp[24] : value
+ // Returns a bitwise zero to indicate that the value
+ // is and instance of the function and anything else to
+ // indicate that the value is not an instance.
+
+ static const int kOffsetToMapCheckValue = 2;
+ static const int kOffsetToResultValue = 18;
+ // The last 4 bytes of the instruction sequence
+ // movq(rdi, FieldOperand(rax, HeapObject::kMapOffset))
+ // Move(kScratchRegister, Factory::the_hole_value())
+ // in front of the hole value address.
+ static const unsigned int kWordBeforeMapCheckValue = 0xBA49FF78;
+ // The last 4 bytes of the instruction sequence
+ // __ j(not_equal, &cache_miss);
+ // __ LoadRoot(ToRegister(instr->result()), Heap::kTheHoleValueRootIndex);
+ // before the offset of the hole value in the root array.
+ static const unsigned int kWordBeforeResultValue = 0x458B4909;
+ // Only the inline check flag is supported on X64.
+ ASSERT(flags_ == kNoFlags || HasCallSiteInlineCheck());
+ int extra_stack_space = HasCallSiteInlineCheck() ? kPointerSize : 0;
+
+ // Get the object - go slow case if it's a smi.
+ Label slow;
+
+ __ movq(rax, Operand(rsp, 2 * kPointerSize + extra_stack_space));
+ __ JumpIfSmi(rax, &slow);
+
+ // Check that the left hand is a JS object. Leave its map in rax.
+ __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rax);
+ __ j(below, &slow);
+ __ CmpInstanceType(rax, LAST_SPEC_OBJECT_TYPE);
+ __ j(above, &slow);
+
+ // Get the prototype of the function.
+ __ movq(rdx, Operand(rsp, 1 * kPointerSize + extra_stack_space));
+ // rdx is function, rax is map.
+
+ // If there is a call site cache don't look in the global cache, but do the
+ // real lookup and update the call site cache.
+ if (!HasCallSiteInlineCheck()) {
+ // Look up the function and the map in the instanceof cache.
+ Label miss;
+ __ CompareRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
+ __ j(not_equal, &miss, Label::kNear);
+ __ CompareRoot(rax, Heap::kInstanceofCacheMapRootIndex);
+ __ j(not_equal, &miss, Label::kNear);
+ __ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
+ __ ret(2 * kPointerSize);
+ __ bind(&miss);
+ }
+
+ __ TryGetFunctionPrototype(rdx, rbx, &slow, true);
+
+ // Check that the function prototype is a JS object.
+ __ JumpIfSmi(rbx, &slow);
+ __ CmpObjectType(rbx, FIRST_SPEC_OBJECT_TYPE, kScratchRegister);
+ __ j(below, &slow);
+ __ CmpInstanceType(kScratchRegister, LAST_SPEC_OBJECT_TYPE);
+ __ j(above, &slow);
+
+ // Register mapping:
+ // rax is object map.
+ // rdx is function.
+ // rbx is function prototype.
+ if (!HasCallSiteInlineCheck()) {
+ __ 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));
+ if (FLAG_debug_code) {
+ __ movl(rdi, Immediate(kWordBeforeMapCheckValue));
+ __ cmpl(Operand(kScratchRegister, kOffsetToMapCheckValue - 4), rdi);
+ __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheCheck);
+ }
+ __ movq(kScratchRegister,
+ Operand(kScratchRegister, kOffsetToMapCheckValue));
+ __ movq(Operand(kScratchRegister, 0), rax);
+ }
+
+ __ movq(rcx, FieldOperand(rax, Map::kPrototypeOffset));
+
+ // Loop through the prototype chain looking for the function prototype.
+ Label loop, is_instance, is_not_instance;
+ __ LoadRoot(kScratchRegister, Heap::kNullValueRootIndex);
+ __ bind(&loop);
+ __ cmpq(rcx, rbx);
+ __ j(equal, &is_instance, Label::kNear);
+ __ cmpq(rcx, kScratchRegister);
+ // The code at is_not_instance assumes that kScratchRegister contains a
+ // non-zero GCable value (the null object in this case).
+ __ j(equal, &is_not_instance, Label::kNear);
+ __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
+ __ movq(rcx, FieldOperand(rcx, Map::kPrototypeOffset));
+ __ jmp(&loop);
+
+ __ bind(&is_instance);
+ if (!HasCallSiteInlineCheck()) {
+ __ xorl(rax, rax);
+ // Store bitwise zero in the cache. This is a Smi in GC terms.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ StoreRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ // Store offset of true in the root array at the inline check site.
+ 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);
+ if (FLAG_debug_code) {
+ __ movl(rax, Immediate(kWordBeforeResultValue));
+ __ cmpl(Operand(kScratchRegister, kOffsetToResultValue - 4), rax);
+ __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
+ }
+ __ Set(rax, 0);
+ }
+ __ ret(2 * kPointerSize + extra_stack_space);
+
+ __ bind(&is_not_instance);
+ if (!HasCallSiteInlineCheck()) {
+ // We have to store a non-zero value in the cache.
+ __ StoreRoot(kScratchRegister, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ // Store offset of false in the root array at the inline check site.
+ 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);
+ if (FLAG_debug_code) {
+ __ movl(rax, Immediate(kWordBeforeResultValue));
+ __ cmpl(Operand(kScratchRegister, kOffsetToResultValue - 4), rax);
+ __ Assert(equal, kInstanceofStubUnexpectedCallSiteCacheMov);
+ }
+ }
+ __ ret(2 * kPointerSize + extra_stack_space);
+
+ // Slow-case: Go through the JavaScript implementation.
+ __ bind(&slow);
+ if (HasCallSiteInlineCheck()) {
+ // Remove extra value from the stack.
+ __ PopReturnAddressTo(rcx);
+ __ pop(rax);
+ __ PushReturnAddressFrom(rcx);
+ }
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
+}
+
+
+// Passing arguments in registers is not supported.
+Register InstanceofStub::left() { return no_reg; }
+
+
+Register InstanceofStub::right() { return no_reg; }
+
+
+// -------------------------------------------------------------------------
+// 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.
+ __ JumpIfSmi(object_, receiver_not_string_);
+
+ // Fetch the instance type of the receiver into result register.
+ __ movq(result_, FieldOperand(object_, HeapObject::kMapOffset));
+ __ movzxbl(result_, FieldOperand(result_, Map::kInstanceTypeOffset));
+ // If the receiver is not a string trigger the non-string case.
+ __ testb(result_, Immediate(kIsNotStringMask));
+ __ j(not_zero, receiver_not_string_);
+
+ // If the index is non-smi trigger the non-smi case.
+ __ JumpIfNotSmi(index_, &index_not_smi_);
+ __ bind(&got_smi_index_);
+
+ // Check for index out of range.
+ __ SmiCompare(index_, FieldOperand(object_, String::kLengthOffset));
+ __ j(above_equal, index_out_of_range_);
+
+ __ SmiToInteger32(index_, index_);
+
+ StringCharLoadGenerator::Generate(
+ masm, object_, index_, result_, &call_runtime_);
+
+ __ Integer32ToSmi(result_, result_);
+ __ bind(&exit_);
+}
+
+
+void StringCharCodeAtGenerator::GenerateSlow(
+ MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase);
+
+ Factory* factory = masm->isolate()->factory();
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_,
+ factory->heap_number_map(),
+ index_not_number_,
+ DONT_DO_SMI_CHECK);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ push(index_); // Consumed by runtime conversion function.
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+ if (!index_.is(rax)) {
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+ __ movq(index_, rax);
+ }
+ __ 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(index_, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ jmp(&got_smi_index_);
+
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // is too complex (e.g., when the string needs to be flattened).
+ __ bind(&call_runtime_);
+ call_helper.BeforeCall(masm);
+ __ push(object_);
+ __ Integer32ToSmi(index_, index_);
+ __ push(index_);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+ if (!result_.is(rax)) {
+ __ movq(result_, rax);
+ }
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase);
+}
+
+
+// -------------------------------------------------------------------------
+// StringCharFromCodeGenerator
+
+void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+ __ JumpIfNotSmi(code_, &slow_case_);
+ __ SmiCompare(code_, Smi::FromInt(String::kMaxOneByteCharCode));
+ __ j(above, &slow_case_);
+
+ __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
+ SmiIndex index = masm->SmiToIndex(kScratchRegister, code_, kPointerSizeLog2);
+ __ movq(result_, FieldOperand(result_, index.reg, index.scale,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(result_, Heap::kUndefinedValueRootIndex);
+ __ j(equal, &slow_case_);
+ __ bind(&exit_);
+}
+
+
+void StringCharFromCodeGenerator::GenerateSlow(
+ MacroAssembler* masm,
+ const RuntimeCallHelper& call_helper) {
+ __ Abort(kUnexpectedFallthroughToCharFromCodeSlowCase);
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ if (!result_.is(rax)) {
+ __ movq(result_, rax);
+ }
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase);
+}
+
+
+void StringAddStub::Generate(MacroAssembler* masm) {
+ Label call_runtime, call_builtin;
+ Builtins::JavaScript builtin_id = Builtins::ADD;
+
+ // Load the two arguments.
+ __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument (left).
+ __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument (right).
+
+ // Make sure that both arguments are strings if not known in advance.
+ // Otherwise, at least one of the arguments is definitely a string,
+ // and we convert the one that is not known to be a string.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
+ ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT);
+ ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT);
+ __ JumpIfSmi(rax, &call_runtime);
+ __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8);
+ __ j(above_equal, &call_runtime);
+
+ // First argument is a a string, test second.
+ __ JumpIfSmi(rdx, &call_runtime);
+ __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
+ __ j(above_equal, &call_runtime);
+ } else if ((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
+ ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == 0);
+ GenerateConvertArgument(masm, 2 * kPointerSize, rax, rbx, rcx, rdi,
+ &call_builtin);
+ builtin_id = Builtins::STRING_ADD_RIGHT;
+ } else if ((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
+ ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == 0);
+ GenerateConvertArgument(masm, 1 * kPointerSize, rdx, rbx, rcx, rdi,
+ &call_builtin);
+ builtin_id = Builtins::STRING_ADD_LEFT;
+ }
+
+ // Both arguments are strings.
+ // rax: first string
+ // rdx: second string
+ // Check if either of the strings are empty. In that case return the other.
+ Label second_not_zero_length, both_not_zero_length;
+ __ movq(rcx, FieldOperand(rdx, String::kLengthOffset));
+ __ SmiTest(rcx);
+ __ j(not_zero, &second_not_zero_length, Label::kNear);
+ // Second string is empty, result is first string which is already in rax.
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+ __ bind(&second_not_zero_length);
+ __ movq(rbx, FieldOperand(rax, String::kLengthOffset));
+ __ SmiTest(rbx);
+ __ j(not_zero, &both_not_zero_length, Label::kNear);
+ // First string is empty, result is second string which is in rdx.
+ __ movq(rax, rdx);
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+
+ // Both strings are non-empty.
+ // rax: first string
+ // rbx: length of first string
+ // rcx: length of second string
+ // rdx: second string
+ // r8: map of first string (if flags_ == NO_STRING_ADD_FLAGS)
+ // r9: map of second string (if flags_ == NO_STRING_ADD_FLAGS)
+ Label string_add_flat_result, longer_than_two;
+ __ bind(&both_not_zero_length);
+
+ // If arguments where known to be strings, maps are not loaded to r8 and r9
+ // by the code above.
+ if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
+ __ movq(r8, FieldOperand(rax, HeapObject::kMapOffset));
+ __ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset));
+ }
+ // Get the instance types of the two strings as they will be needed soon.
+ __ movzxbl(r8, FieldOperand(r8, Map::kInstanceTypeOffset));
+ __ movzxbl(r9, FieldOperand(r9, Map::kInstanceTypeOffset));
+
+ // Look at the length of the result of adding the two strings.
+ STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue / 2);
+ __ SmiAdd(rbx, rbx, rcx);
+ // Use the string table when adding two one character strings, as it
+ // helps later optimizations to return an internalized string here.
+ __ SmiCompare(rbx, Smi::FromInt(2));
+ __ j(not_equal, &longer_than_two);
+
+ // Check that both strings are non-external ASCII strings.
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx,
+ &call_runtime);
+
+ // Get the two characters forming the sub string.
+ __ movzxbq(rbx, FieldOperand(rax, SeqOneByteString::kHeaderSize));
+ __ movzxbq(rcx, FieldOperand(rdx, SeqOneByteString::kHeaderSize));
+
+ // Try to lookup two character string in string table. If it is not found
+ // just allocate a new one.
+ Label make_two_character_string, make_flat_ascii_string;
+ StringHelper::GenerateTwoCharacterStringTableProbe(
+ masm, rbx, rcx, r14, r11, rdi, r15, &make_two_character_string);
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&make_two_character_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, SeqOneByteString::kHeaderSize));
+ __ shll(rcx, Immediate(kBitsPerByte));
+ __ orl(rbx, rcx);
+ // Write both characters to the new string.
+ __ movw(FieldOperand(rax, SeqOneByteString::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(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, &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.
+ // rax: first string
+ // rbx: length of resulting flat string
+ // rdx: second string
+ // r8: instance type of first string
+ // r9: instance type of second string
+ Label non_ascii, allocated, ascii_data;
+ __ movl(rcx, r8);
+ __ and_(rcx, r9);
+ STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
+ STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
+ __ testl(rcx, Immediate(kStringEncodingMask));
+ __ j(zero, &non_ascii);
+ __ bind(&ascii_data);
+ // 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);
+ __ movq(FieldOperand(rcx, ConsString::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+
+ Label skip_write_barrier, after_writing;
+ ExternalReference high_promotion_mode = ExternalReference::
+ new_space_high_promotion_mode_active_address(masm->isolate());
+ __ Load(rbx, high_promotion_mode);
+ __ testb(rbx, Immediate(1));
+ __ j(zero, &skip_write_barrier);
+
+ __ movq(FieldOperand(rcx, ConsString::kFirstOffset), rax);
+ __ RecordWriteField(rcx,
+ ConsString::kFirstOffset,
+ rax,
+ rbx,
+ kDontSaveFPRegs);
+ __ movq(FieldOperand(rcx, ConsString::kSecondOffset), rdx);
+ __ RecordWriteField(rcx,
+ ConsString::kSecondOffset,
+ rdx,
+ rbx,
+ kDontSaveFPRegs);
+ __ jmp(&after_writing);
+
+ __ bind(&skip_write_barrier);
+ __ movq(FieldOperand(rcx, ConsString::kFirstOffset), rax);
+ __ movq(FieldOperand(rcx, ConsString::kSecondOffset), rdx);
+
+ __ bind(&after_writing);
+
+ __ movq(rax, rcx);
+ __ IncrementCounter(counters->string_add_native(), 1);
+ __ ret(2 * kPointerSize);
+ __ bind(&non_ascii);
+ // At least one of the strings is two-byte. Check whether it happens
+ // to contain only one byte characters.
+ // rcx: first instance type AND second instance type.
+ // r8: first instance type.
+ // r9: second instance type.
+ __ testb(rcx, Immediate(kOneByteDataHintMask));
+ __ j(not_zero, &ascii_data);
+ __ xor_(r8, r9);
+ STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
+ __ andb(r8, Immediate(kOneByteStringTag | kOneByteDataHintTag));
+ __ cmpb(r8, Immediate(kOneByteStringTag | kOneByteDataHintTag));
+ __ j(equal, &ascii_data);
+ // Allocate a two byte cons string.
+ __ AllocateTwoByteConsString(rcx, rdi, no_reg, &call_runtime);
+ __ jmp(&allocated);
+
+ // 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(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(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
+ __ lea(rcx, FieldOperand(rax, SeqOneByteString::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(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
+ __ lea(rdx, FieldOperand(rdx, SeqOneByteString::kHeaderSize));
+ __ bind(&second_prepared);
+
+ Label non_ascii_string_add_flat_result;
+ // 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);
+
+ __ bind(&make_flat_ascii_string);
+ // 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.
+ __ lea(rbx, FieldOperand(rax, SeqOneByteString::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);
+
+ __ bind(&non_ascii_string_add_flat_result);
+ // 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.
+ __ 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(&call_runtime);
+
+ if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
+ GenerateRegisterArgsPop(masm, rcx);
+ // Build a frame
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ __ CallRuntime(Runtime::kStringAdd, 2);
+ }
+ __ Ret();
+ } else {
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
+ }
+
+ if (call_builtin.is_linked()) {
+ __ bind(&call_builtin);
+ if ((flags_ & STRING_ADD_ERECT_FRAME) != 0) {
+ GenerateRegisterArgsPop(masm, rcx);
+ // Build a frame
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ GenerateRegisterArgsPush(masm);
+ __ InvokeBuiltin(builtin_id, CALL_FUNCTION);
+ }
+ __ Ret();
+ } else {
+ __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
+ }
+ }
+}
+
+
+void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ push(rax);
+ __ push(rdx);
+}
+
+
+void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm,
+ Register temp) {
+ __ PopReturnAddressTo(temp);
+ __ pop(rdx);
+ __ pop(rax);
+ __ PushReturnAddressFrom(temp);
+}
+
+
+void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* slow) {
+ // First check if the argument is already a string.
+ Label not_string, done;
+ __ JumpIfSmi(arg, &not_string);
+ __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
+ __ j(below, &done);
+
+ // Check the number to string cache.
+ Label not_cached;
+ __ bind(&not_string);
+ // Puts the cached result into scratch1.
+ NumberToStringStub::GenerateLookupNumberStringCache(masm,
+ arg,
+ scratch1,
+ scratch2,
+ scratch3,
+ &not_cached);
+ __ movq(arg, scratch1);
+ __ movq(Operand(rsp, stack_offset), arg);
+ __ jmp(&done);
+
+ // Check if the argument is a safe string wrapper.
+ __ bind(&not_cached);
+ __ JumpIfSmi(arg, slow);
+ __ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
+ __ j(not_equal, slow);
+ __ testb(FieldOperand(scratch1, Map::kBitField2Offset),
+ Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ j(zero, slow);
+ __ movq(arg, FieldOperand(arg, JSValue::kValueOffset));
+ __ movq(Operand(rsp, stack_offset), arg);
+
+ __ bind(&done);
+}
+
+
+void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ bool ascii) {
+ Label loop;
+ __ bind(&loop);
+ // This loop just copies one character at a time, as it is only used for very
+ // short strings.
+ if (ascii) {
+ __ movb(kScratchRegister, Operand(src, 0));
+ __ movb(Operand(dest, 0), kScratchRegister);
+ __ incq(src);
+ __ incq(dest);
+ } else {
+ __ movzxwl(kScratchRegister, Operand(src, 0));
+ __ movw(Operand(dest, 0), kScratchRegister);
+ __ addq(src, Immediate(2));
+ __ addq(dest, Immediate(2));
+ }
+ __ decl(count);
+ __ j(not_zero, &loop);
+}
+
+
+void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ bool ascii) {
+ // Copy characters using rep movs of doublewords. Align destination on 4 byte
+ // boundary before starting rep movs. Copy remaining characters after running
+ // rep movs.
+ // Count is positive int32, dest and src are character pointers.
+ ASSERT(dest.is(rdi)); // rep movs destination
+ ASSERT(src.is(rsi)); // rep movs source
+ ASSERT(count.is(rcx)); // rep movs count
+
+ // Nothing to do for zero characters.
+ Label done;
+ __ testl(count, count);
+ __ j(zero, &done, Label::kNear);
+
+ // Make count the number of bytes to copy.
+ if (!ascii) {
+ STATIC_ASSERT(2 == sizeof(uc16));
+ __ addl(count, count);
+ }
+
+ // Don't enter the rep movs if there are less than 4 bytes to copy.
+ Label last_bytes;
+ __ testl(count, Immediate(~(kPointerSize - 1)));
+ __ j(zero, &last_bytes, Label::kNear);
+
+ // Copy from edi to esi using rep movs instruction.
+ __ movl(kScratchRegister, count);
+ __ shr(count, Immediate(kPointerSizeLog2)); // Number of doublewords to copy.
+ __ repmovsq();
+
+ // Find number of bytes left.
+ __ movl(count, kScratchRegister);
+ __ and_(count, Immediate(kPointerSize - 1));
+
+ // Check if there are more bytes to copy.
+ __ bind(&last_bytes);
+ __ testl(count, count);
+ __ j(zero, &done, Label::kNear);
+
+ // Copy remaining characters.
+ Label loop;
+ __ bind(&loop);
+ __ movb(kScratchRegister, Operand(src, 0));
+ __ movb(Operand(dest, 0), kScratchRegister);
+ __ incq(src);
+ __ incq(dest);
+ __ decl(count);
+ __ j(not_zero, &loop);
+
+ __ bind(&done);
+}
+
+void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* not_found) {
+ // Register scratch3 is the general scratch register in this function.
+ Register scratch = scratch3;
+
+ // Make sure that both characters are not digits as such strings has a
+ // different hash algorithm. Don't try to look for these in the string table.
+ Label not_array_index;
+ __ leal(scratch, Operand(c1, -'0'));
+ __ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
+ __ j(above, &not_array_index, Label::kNear);
+ __ leal(scratch, Operand(c2, -'0'));
+ __ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
+ __ j(below_equal, not_found);
+
+ __ bind(&not_array_index);
+ // Calculate the two character string hash.
+ Register hash = scratch1;
+ GenerateHashInit(masm, hash, c1, scratch);
+ GenerateHashAddCharacter(masm, hash, c2, scratch);
+ GenerateHashGetHash(masm, hash, scratch);
+
+ // Collect the two characters in a register.
+ Register chars = c1;
+ __ shl(c2, Immediate(kBitsPerByte));
+ __ orl(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 string table.
+ Register string_table = c2;
+ __ LoadRoot(string_table, Heap::kStringTableRootIndex);
+
+ // Calculate capacity mask from the string table capacity.
+ Register mask = scratch2;
+ __ SmiToInteger32(mask,
+ FieldOperand(string_table, StringTable::kCapacityOffset));
+ __ decl(mask);
+
+ Register map = scratch4;
+
+ // Registers
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string (32-bit int)
+ // string_table: string table
+ // mask: capacity mask (32-bit int)
+ // map: -
+ // scratch: -
+
+ // Perform a number of probes in the string table.
+ static const int kProbes = 4;
+ Label found_in_string_table;
+ Label next_probe[kProbes];
+ Register candidate = scratch; // Scratch register contains candidate.
+ for (int i = 0; i < kProbes; i++) {
+ // Calculate entry in string table.
+ __ movl(scratch, hash);
+ if (i > 0) {
+ __ addl(scratch, Immediate(StringTable::GetProbeOffset(i)));
+ }
+ __ andl(scratch, mask);
+
+ // Load the entry from the string table.
+ STATIC_ASSERT(StringTable::kEntrySize == 1);
+ __ movq(candidate,
+ FieldOperand(string_table,
+ scratch,
+ times_pointer_size,
+ StringTable::kElementsStartOffset));
+
+ // If entry is undefined no string with this hash can be found.
+ Label is_string;
+ __ CmpObjectType(candidate, ODDBALL_TYPE, map);
+ __ j(not_equal, &is_string, Label::kNear);
+
+ __ CompareRoot(candidate, Heap::kUndefinedValueRootIndex);
+ __ j(equal, not_found);
+ // Must be the hole (deleted entry).
+ if (FLAG_debug_code) {
+ __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
+ __ cmpq(kScratchRegister, candidate);
+ __ Assert(equal, kOddballInStringTableIsNotUndefinedOrTheHole);
+ }
+ __ jmp(&next_probe[i]);
+
+ __ bind(&is_string);
+
+ // If length is not 2 the string is not a candidate.
+ __ SmiCompare(FieldOperand(candidate, String::kLengthOffset),
+ Smi::FromInt(2));
+ __ j(not_equal, &next_probe[i]);
+
+ // We use kScratchRegister as a temporary register in assumption that
+ // JumpIfInstanceTypeIsNotSequentialAscii does not use it implicitly
+ Register temp = kScratchRegister;
+
+ // Check that the candidate is a non-external ASCII string.
+ __ movzxbl(temp, FieldOperand(map, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(
+ temp, temp, &next_probe[i]);
+
+ // Check if the two characters match.
+ __ movl(temp, FieldOperand(candidate, SeqOneByteString::kHeaderSize));
+ __ andl(temp, Immediate(0x0000ffff));
+ __ cmpl(chars, temp);
+ __ j(equal, &found_in_string_table);
+ __ bind(&next_probe[i]);
+ }
+
+ // No matching 2 character string found by probing.
+ __ jmp(not_found);
+
+ // Scratch register contains result when we fall through to here.
+ Register result = candidate;
+ __ bind(&found_in_string_table);
+ if (!result.is(rax)) {
+ __ movq(rax, result);
+ }
+}
+
+
+void StringHelper::GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
+ // hash = (seed + character) + ((seed + character) << 10);
+ __ LoadRoot(scratch, Heap::kHashSeedRootIndex);
+ __ SmiToInteger32(scratch, scratch);
+ __ addl(scratch, character);
+ __ movl(hash, scratch);
+ __ shll(scratch, Immediate(10));
+ __ addl(hash, scratch);
+ // hash ^= hash >> 6;
+ __ movl(scratch, hash);
+ __ shrl(scratch, Immediate(6));
+ __ xorl(hash, scratch);
+}
+
+
+void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
+ // hash += character;
+ __ addl(hash, character);
+ // hash += hash << 10;
+ __ movl(scratch, hash);
+ __ shll(scratch, Immediate(10));
+ __ addl(hash, scratch);
+ // hash ^= hash >> 6;
+ __ movl(scratch, hash);
+ __ shrl(scratch, Immediate(6));
+ __ xorl(hash, scratch);
+}
+
+
+void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch) {
+ // hash += hash << 3;
+ __ leal(hash, Operand(hash, hash, times_8, 0));
+ // hash ^= hash >> 11;
+ __ movl(scratch, hash);
+ __ shrl(scratch, Immediate(11));
+ __ xorl(hash, scratch);
+ // hash += hash << 15;
+ __ movl(scratch, hash);
+ __ shll(scratch, Immediate(15));
+ __ addl(hash, scratch);
+
+ __ andl(hash, Immediate(String::kHashBitMask));
+
+ // if (hash == 0) hash = 27;
+ Label hash_not_zero;
+ __ j(not_zero, &hash_not_zero);
+ __ Set(hash, StringHasher::kZeroHash);
+ __ bind(&hash_not_zero);
+}
+
+
+void SubStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ // Stack frame on entry.
+ // rsp[0] : return address
+ // rsp[8] : to
+ // rsp[16] : from
+ // rsp[24] : string
+
+ const int kToOffset = 1 * kPointerSize;
+ const int kFromOffset = kToOffset + kPointerSize;
+ const int kStringOffset = kFromOffset + kPointerSize;
+ const int kArgumentsSize = (kStringOffset + kPointerSize) - kToOffset;
+
+ // Make sure first argument is a string.
+ __ movq(rax, Operand(rsp, kStringOffset));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ testl(rax, Immediate(kSmiTagMask));
+ __ j(zero, &runtime);
+ Condition is_string = masm->IsObjectStringType(rax, rbx, rbx);
+ __ j(NegateCondition(is_string), &runtime);
+
+ // rax: string
+ // rbx: instance type
+ // Calculate length of sub string using the smi values.
+ __ movq(rcx, Operand(rsp, kToOffset));
+ __ movq(rdx, Operand(rsp, kFromOffset));
+ __ JumpUnlessBothNonNegativeSmi(rcx, rdx, &runtime);
+
+ __ SmiSub(rcx, rcx, rdx); // Overflow doesn't happen.
+ __ cmpq(rcx, FieldOperand(rax, String::kLengthOffset));
+ Label not_original_string;
+ // Shorter than original string's length: an actual substring.
+ __ j(below, &not_original_string, Label::kNear);
+ // Longer than original string's length or negative: unsafe arguments.
+ __ j(above, &runtime);
+ // Return original string.
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(kArgumentsSize);
+ __ bind(&not_original_string);
+
+ Label single_char;
+ __ SmiCompare(rcx, Smi::FromInt(1));
+ __ j(equal, &single_char);
+
+ __ SmiToInteger32(rcx, rcx);
+
+ // 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::kempty_stringRootIndex);
+ __ 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));
+ __ 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.
+ __ cmpq(rcx, Immediate(SlicedString::kMinLength));
+ // Short slice. Copy instead of slicing.
+ __ j(less, &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 & kOneByteStringTag) != 0);
+ STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
+ __ testb(rbx, Immediate(kStringEncodingMask));
+ __ j(zero, &two_byte_slice, Label::kNear);
+ __ AllocateAsciiSlicedString(rax, rbx, r14, &runtime);
+ __ jmp(&set_slice_header, Label::kNear);
+ __ bind(&two_byte_slice);
+ __ AllocateTwoByteSlicedString(rax, rbx, r14, &runtime);
+ __ bind(&set_slice_header);
+ __ Integer32ToSmi(rcx, rcx);
+ __ movq(FieldOperand(rax, SlicedString::kLengthOffset), rcx);
+ __ movq(FieldOperand(rax, SlicedString::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+ __ movq(FieldOperand(rax, SlicedString::kParentOffset), rdi);
+ __ movq(FieldOperand(rax, SlicedString::kOffsetOffset), rdx);
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(kArgumentsSize);
+
+ __ bind(&copy_routine);
+ }
+
+ // 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 == SeqOneByteString::kHeaderSize);
+ __ subq(rdi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+
+ __ bind(&sequential_string);
+ STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
+ __ testb(rbx, Immediate(kStringEncodingMask));
+ __ j(zero, &two_byte_sequential);
+
+ // Allocate the result.
+ __ AllocateAsciiString(rax, rcx, r11, r14, r15, &runtime);
+
+ // rax: result string
+ // rcx: result string length
+ __ 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,
+ SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ }
+ // Locate first character of result.
+ __ lea(rdi, FieldOperand(rax, SeqOneByteString::kHeaderSize));
+
+ // rax: result string
+ // rcx: result length
+ // 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, r14); // Restore rsi.
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(kArgumentsSize);
+
+ __ bind(&two_byte_sequential);
+ // Allocate the result.
+ __ AllocateTwoByteString(rax, rcx, r11, r14, r15, &runtime);
+
+ // rax: result string
+ // rcx: result string length
+ __ 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,
+ SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ }
+ // Locate first character of result.
+ __ lea(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize));
+
+ // rax: result string
+ // rcx: result length
+ // 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, r14); // Restore esi.
+ __ IncrementCounter(counters->sub_string_native(), 1);
+ __ ret(kArgumentsSize);
+
+ // Just jump to runtime to create the sub string.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kSubString, 3, 1);
+
+ __ bind(&single_char);
+ // rax: string
+ // rbx: instance type
+ // rcx: sub string length (smi)
+ // rdx: from index (smi)
+ StringCharAtGenerator generator(
+ rax, rdx, rcx, rax, &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm);
+ __ ret(kArgumentsSize);
+ generator.SkipSlow(masm, &runtime);
+}
+
+
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label check_zero_length;
+ __ movq(length, FieldOperand(left, String::kLengthOffset));
+ __ SmiCompare(length, FieldOperand(right, String::kLengthOffset));
+ __ j(equal, &check_zero_length, Label::kNear);
+ __ Move(rax, Smi::FromInt(NOT_EQUAL));
+ __ ret(0);
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ SmiTest(length);
+ __ j(not_zero, &compare_chars, Label::kNear);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Compare characters.
+ __ bind(&compare_chars);
+ Label strings_not_equal;
+ GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
+ &strings_not_equal, Label::kNear);
+
+ // Characters are equal.
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Characters are not equal.
+ __ bind(&strings_not_equal);
+ __ Move(rax, Smi::FromInt(NOT_EQUAL));
+ __ ret(0);
+}
+
+
+void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4) {
+ // Ensure that you can always subtract a string length from a non-negative
+ // number (e.g. another length).
+ STATIC_ASSERT(String::kMaxLength < 0x7fffffff);
+
+ // Find minimum length and length difference.
+ __ movq(scratch1, FieldOperand(left, String::kLengthOffset));
+ __ movq(scratch4, scratch1);
+ __ SmiSub(scratch4,
+ scratch4,
+ FieldOperand(right, String::kLengthOffset));
+ // Register scratch4 now holds left.length - right.length.
+ const Register length_difference = scratch4;
+ Label left_shorter;
+ __ j(less, &left_shorter, Label::kNear);
+ // The right string isn't longer that the left one.
+ // Get the right string's length by subtracting the (non-negative) difference
+ // from the left string's length.
+ __ SmiSub(scratch1, scratch1, length_difference);
+ __ bind(&left_shorter);
+ // Register scratch1 now holds Min(left.length, right.length).
+ const Register min_length = scratch1;
+
+ Label compare_lengths;
+ // If min-length is zero, go directly to comparing lengths.
+ __ SmiTest(min_length);
+ __ j(zero, &compare_lengths, Label::kNear);
+
+ // Compare loop.
+ Label result_not_equal;
+ GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
+ &result_not_equal, Label::kNear);
+
+ // Completed loop without finding different characters.
+ // Compare lengths (precomputed).
+ __ bind(&compare_lengths);
+ __ SmiTest(length_difference);
+ Label length_not_equal;
+ __ j(not_zero, &length_not_equal, Label::kNear);
+
+ // Result is EQUAL.
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ Label result_greater;
+ Label result_less;
+ __ bind(&length_not_equal);
+ __ j(greater, &result_greater, Label::kNear);
+ __ jmp(&result_less, Label::kNear);
+ __ bind(&result_not_equal);
+ // Unequal comparison of left to right, either character or length.
+ __ j(above, &result_greater, Label::kNear);
+ __ bind(&result_less);
+
+ // Result is LESS.
+ __ Move(rax, Smi::FromInt(LESS));
+ __ ret(0);
+
+ // Result is GREATER.
+ __ bind(&result_greater);
+ __ Move(rax, Smi::FromInt(GREATER));
+ __ ret(0);
+}
+
+
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance near_jump) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiToInteger32(length, length);
+ __ lea(left,
+ FieldOperand(left, length, times_1, SeqOneByteString::kHeaderSize));
+ __ lea(right,
+ FieldOperand(right, length, times_1, SeqOneByteString::kHeaderSize));
+ __ neg(length);
+ Register index = length; // index = -length;
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ movb(scratch, Operand(left, index, times_1, 0));
+ __ cmpb(scratch, Operand(right, index, times_1, 0));
+ __ j(not_equal, chars_not_equal, near_jump);
+ __ incq(index);
+ __ j(not_zero, &loop);
+}
+
+
+void StringCompareStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ // Stack frame on entry.
+ // rsp[0] : return address
+ // rsp[8] : right string
+ // rsp[16] : left string
+
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize)); // left
+ __ movq(rax, Operand(rsp, 1 * kPointerSize)); // right
+
+ // Check for identity.
+ Label not_same;
+ __ cmpq(rdx, rax);
+ __ j(not_equal, &not_same, Label::kNear);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->string_compare_native(), 1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&not_same);
+
+ // Check that both are sequential ASCII strings.
+ __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &runtime);
+
+ // Inline comparison of ASCII strings.
+ __ IncrementCounter(counters->string_compare_native(), 1);
+ // Drop arguments from the stack
+ __ PopReturnAddressTo(rcx);
+ __ addq(rsp, Immediate(2 * kPointerSize));
+ __ PushReturnAddressFrom(rcx);
+ GenerateCompareFlatAsciiStrings(masm, rdx, rax, rcx, rbx, rdi, r8);
+
+ // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+}
+
+
+void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SMI);
+ Label miss;
+ __ JumpIfNotBothSmi(rdx, rax, &miss, Label::kNear);
+
+ if (GetCondition() == equal) {
+ // For equality we do not care about the sign of the result.
+ __ subq(rax, rdx);
+ } else {
+ Label done;
+ __ subq(rdx, rax);
+ __ j(no_overflow, &done, Label::kNear);
+ // Correct sign of result in case of overflow.
+ __ not_(rdx);
+ __ bind(&done);
+ __ movq(rax, rdx);
+ }
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateNumbers(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::NUMBER);
+
+ Label generic_stub;
+ Label unordered, maybe_undefined1, maybe_undefined2;
+ Label miss;
+
+ if (left_ == CompareIC::SMI) {
+ __ JumpIfNotSmi(rdx, &miss);
+ }
+ if (right_ == CompareIC::SMI) {
+ __ JumpIfNotSmi(rax, &miss);
+ }
+
+ // Load left and right operand.
+ Label done, left, left_smi, right_smi;
+ __ JumpIfSmi(rax, &right_smi, Label::kNear);
+ __ CompareMap(rax, masm->isolate()->factory()->heap_number_map(), NULL);
+ __ j(not_equal, &maybe_undefined1, Label::kNear);
+ __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ jmp(&left, Label::kNear);
+ __ bind(&right_smi);
+ __ SmiToInteger32(rcx, rax); // Can't clobber rax yet.
+ __ cvtlsi2sd(xmm1, rcx);
+
+ __ bind(&left);
+ __ JumpIfSmi(rdx, &left_smi, Label::kNear);
+ __ CompareMap(rdx, masm->isolate()->factory()->heap_number_map(), NULL);
+ __ j(not_equal, &maybe_undefined2, Label::kNear);
+ __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
+ __ jmp(&done);
+ __ bind(&left_smi);
+ __ SmiToInteger32(rcx, rdx); // Can't clobber rdx yet.
+ __ cvtlsi2sd(xmm0, rcx);
+
+ __ bind(&done);
+ // Compare operands
+ __ ucomisd(xmm0, xmm1);
+
+ // Don't base result on EFLAGS when a NaN is involved.
+ __ j(parity_even, &unordered, Label::kNear);
+
+ // Return a result of -1, 0, or 1, based on EFLAGS.
+ // Performing mov, because xor would destroy the flag register.
+ __ movl(rax, Immediate(0));
+ __ movl(rcx, Immediate(0));
+ __ setcc(above, rax); // Add one to zero if carry clear and not equal.
+ __ sbbq(rax, rcx); // Subtract one if below (aka. carry set).
+ __ ret(0);
+
+ __ bind(&unordered);
+ __ bind(&generic_stub);
+ ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC,
+ CompareIC::GENERIC);
+ __ jmp(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+
+ __ bind(&maybe_undefined1);
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ __ Cmp(rax, masm->isolate()->factory()->undefined_value());
+ __ j(not_equal, &miss);
+ __ JumpIfSmi(rdx, &unordered);
+ __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx);
+ __ j(not_equal, &maybe_undefined2, Label::kNear);
+ __ jmp(&unordered);
+ }
+
+ __ bind(&maybe_undefined2);
+ if (Token::IsOrderedRelationalCompareOp(op_)) {
+ __ Cmp(rdx, masm->isolate()->factory()->undefined_value());
+ __ j(equal, &unordered);
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::INTERNALIZED_STRING);
+ ASSERT(GetCondition() == equal);
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+ Register tmp1 = rcx;
+ Register tmp2 = rbx;
+
+ // Check that both operands are heap objects.
+ Label miss;
+ Condition cond = masm->CheckEitherSmi(left, right, tmp1);
+ __ j(cond, &miss, Label::kNear);
+
+ // Check that both operands are internalized strings.
+ __ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ __ or_(tmp1, tmp2);
+ __ testb(tmp1, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
+ __ j(not_zero, &miss, Label::kNear);
+
+ // Internalized strings are compared by identity.
+ Label done;
+ __ cmpq(left, right);
+ // Make sure rax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(rax));
+ __ j(not_equal, &done, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ bind(&done);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::UNIQUE_NAME);
+ ASSERT(GetCondition() == equal);
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+ Register tmp1 = rcx;
+ Register tmp2 = rbx;
+
+ // Check that both operands are heap objects.
+ Label miss;
+ Condition cond = masm->CheckEitherSmi(left, right, tmp1);
+ __ j(cond, &miss, Label::kNear);
+
+ // Check that both operands are unique names. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+
+ __ JumpIfNotUniqueName(tmp1, &miss, Label::kNear);
+ __ JumpIfNotUniqueName(tmp2, &miss, Label::kNear);
+
+ // Unique names are compared by identity.
+ Label done;
+ __ cmpq(left, right);
+ // Make sure rax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(rax));
+ __ j(not_equal, &done, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ bind(&done);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRING);
+ Label miss;
+
+ bool equality = Token::IsEqualityOp(op_);
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+ Register tmp1 = rcx;
+ Register tmp2 = rbx;
+ Register tmp3 = rdi;
+
+ // Check that both operands are heap objects.
+ Condition cond = masm->CheckEitherSmi(left, right, tmp1);
+ __ j(cond, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ __ movq(tmp3, tmp1);
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ or_(tmp3, tmp2);
+ __ testb(tmp3, Immediate(kIsNotStringMask));
+ __ j(not_zero, &miss);
+
+ // Fast check for identical strings.
+ Label not_same;
+ __ cmpq(left, right);
+ __ j(not_equal, &not_same, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Handle not identical strings.
+ __ bind(&not_same);
+
+ // Check that both strings are internalized strings. If they are, we're done
+ // because we already know they are not identical. We also know they are both
+ // strings.
+ if (equality) {
+ Label do_compare;
+ STATIC_ASSERT(kInternalizedTag == 0);
+ __ or_(tmp1, tmp2);
+ __ testb(tmp1, Immediate(kIsNotInternalizedMask));
+ __ j(not_zero, &do_compare, Label::kNear);
+ // Make sure rax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(rax));
+ __ ret(0);
+ __ bind(&do_compare);
+ }
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ if (equality) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(
+ masm, left, right, tmp1, tmp2, tmp3, kScratchRegister);
+ }
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ PopReturnAddressTo(tmp1);
+ __ push(left);
+ __ push(right);
+ __ PushReturnAddressFrom(tmp1);
+ if (equality) {
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+ } else {
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+ }
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::OBJECT);
+ Label miss;
+ Condition either_smi = masm->CheckEitherSmi(rdx, rax);
+ __ j(either_smi, &miss, Label::kNear);
+
+ __ CmpObjectType(rax, JS_OBJECT_TYPE, rcx);
+ __ j(not_equal, &miss, Label::kNear);
+ __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx);
+ __ j(not_equal, &miss, Label::kNear);
+
+ ASSERT(GetCondition() == equal);
+ __ subq(rax, rdx);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) {
+ Label miss;
+ Condition either_smi = masm->CheckEitherSmi(rdx, rax);
+ __ j(either_smi, &miss, Label::kNear);
+
+ __ 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);
+
+ __ subq(rax, rdx);
+ __ ret(0);
+
+ __ 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);
+}
+
+
+void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ Handle<Name> name,
+ Register r0) {
+ ASSERT(name->IsUniqueName());
+ // 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
+ // (their names are the hole value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // r0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = r0;
+ // Capacity is smi 2^n.
+ __ SmiToInteger32(index, FieldOperand(properties, kCapacityOffset));
+ __ decl(index);
+ __ and_(index,
+ Immediate(name->Hash() + NameDictionary::GetProbeOffset(i)));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
+
+ Register entity_name = r0;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ movq(entity_name, Operand(properties,
+ index,
+ times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ Cmp(entity_name, masm->isolate()->factory()->undefined_value());
+ __ j(equal, done);
+
+ // Stop if found the property.
+ __ Cmp(entity_name, Handle<Name>(name));
+ __ j(equal, miss);
+
+ Label good;
+ // Check for the hole and skip.
+ __ CompareRoot(entity_name, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &good, Label::kNear);
+
+ // Check if the entry name is not a unique name.
+ __ movq(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
+ __ JumpIfNotUniqueName(FieldOperand(entity_name, Map::kInstanceTypeOffset),
+ miss);
+ __ bind(&good);
+ }
+
+ NameDictionaryLookupStub stub(properties, r0, r0, NEGATIVE_LOOKUP);
+ __ Push(Handle<Object>(name));
+ __ push(Immediate(name->Hash()));
+ __ CallStub(&stub);
+ __ testq(r0, r0);
+ __ j(not_zero, miss);
+ __ jmp(done);
+}
+
+
+// Probe the name dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found leaving the
+// index into the dictionary in |r1|. Jump to the |miss| label
+// otherwise.
+void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1) {
+ ASSERT(!elements.is(r0));
+ ASSERT(!elements.is(r1));
+ ASSERT(!name.is(r0));
+ ASSERT(!name.is(r1));
+
+ __ AssertName(name);
+
+ __ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset));
+ __ decl(r0);
+
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ movl(r1, FieldOperand(name, Name::kHashFieldOffset));
+ __ shrl(r1, Immediate(Name::kHashShift));
+ if (i > 0) {
+ __ addl(r1, Immediate(NameDictionary::GetProbeOffset(i)));
+ }
+ __ and_(r1, r0);
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3
+
+ // Check if the key is identical to the name.
+ __ cmpq(name, Operand(elements, r1, times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ j(equal, done);
+ }
+
+ NameDictionaryLookupStub stub(elements, r0, r1, POSITIVE_LOOKUP);
+ __ push(name);
+ __ movl(r0, FieldOperand(name, Name::kHashFieldOffset));
+ __ shrl(r0, Immediate(Name::kHashShift));
+ __ push(r0);
+ __ CallStub(&stub);
+
+ __ testq(r0, r0);
+ __ j(zero, miss);
+ __ jmp(done);
+}
+
+
+void NameDictionaryLookupStub::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:
+ // rsp[0 * kPointerSize] : return address.
+ // rsp[1 * kPointerSize] : key's hash.
+ // rsp[2 * kPointerSize] : key.
+ // Registers:
+ // dictionary_: NameDictionary to probe.
+ // result_: used as scratch.
+ // index_: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ Register scratch = result_;
+
+ __ SmiToInteger32(scratch, FieldOperand(dictionary_, kCapacityOffset));
+ __ decl(scratch);
+ __ push(scratch);
+
+ // 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
+ // (their names are the null value).
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ movq(scratch, Operand(rsp, 2 * kPointerSize));
+ if (i > 0) {
+ __ addl(scratch, Immediate(NameDictionary::GetProbeOffset(i)));
+ }
+ __ and_(scratch, Operand(rsp, 0));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NameDictionary::kEntrySize == 3);
+ __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
+
+ // Having undefined at this place means the name is not contained.
+ __ movq(scratch, Operand(dictionary_,
+ index_,
+ times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+
+ __ Cmp(scratch, masm->isolate()->factory()->undefined_value());
+ __ j(equal, &not_in_dictionary);
+
+ // Stop if found the property.
+ __ cmpq(scratch, Operand(rsp, 3 * kPointerSize));
+ __ j(equal, &in_dictionary);
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // If we hit a key that is not a unique name during negative
+ // lookup we have to bailout as this key might be equal to the
+ // key we are looking for.
+
+ // Check if the entry name is not a unique name.
+ __ movq(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
+ __ JumpIfNotUniqueName(FieldOperand(scratch, Map::kInstanceTypeOffset),
+ &maybe_in_dictionary);
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ movq(scratch, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+ }
+
+ __ bind(&in_dictionary);
+ __ movq(scratch, Immediate(1));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&not_in_dictionary);
+ __ movq(scratch, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+}
+
+
+struct AheadOfTimeWriteBarrierStubList {
+ Register object, value, address;
+ RememberedSetAction action;
+};
+
+
+#define REG(Name) { kRegister_ ## Name ## _Code }
+
+struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
+ // Used in RegExpExecStub.
+ { REG(rbx), REG(rax), REG(rdi), EMIT_REMEMBERED_SET },
+ // Used in CompileArrayPushCall.
+ { REG(rbx), REG(rcx), REG(rdx), EMIT_REMEMBERED_SET },
+ // Used in CompileStoreGlobal.
+ { REG(rbx), REG(rcx), REG(rdx), OMIT_REMEMBERED_SET },
+ // Used in StoreStubCompiler::CompileStoreField and
+ // KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
+ { REG(rdx), REG(rcx), REG(rbx), EMIT_REMEMBERED_SET },
+ // GenerateStoreField calls the stub with two different permutations of
+ // registers. This is the second.
+ { REG(rbx), REG(rcx), REG(rdx), EMIT_REMEMBERED_SET },
+ // StoreIC::GenerateNormal via GenerateDictionaryStore.
+ { REG(rbx), REG(r8), REG(r9), EMIT_REMEMBERED_SET },
+ // KeyedStoreIC::GenerateGeneric.
+ { REG(rbx), REG(rdx), REG(rcx), EMIT_REMEMBERED_SET},
+ // KeyedStoreStubCompiler::GenerateStoreFastElement.
+ { REG(rdi), REG(rbx), REG(rcx), EMIT_REMEMBERED_SET},
+ { REG(rdx), REG(rdi), REG(rbx), EMIT_REMEMBERED_SET},
+ // ElementsTransitionGenerator::GenerateMapChangeElementTransition
+ // and ElementsTransitionGenerator::GenerateSmiToDouble
+ // and ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(rdx), REG(rbx), REG(rdi), EMIT_REMEMBERED_SET},
+ { REG(rdx), REG(rbx), REG(rdi), OMIT_REMEMBERED_SET},
+ // ElementsTransitionGenerator::GenerateSmiToDouble
+ // and ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(rdx), REG(r11), REG(r15), EMIT_REMEMBERED_SET},
+ // ElementsTransitionGenerator::GenerateDoubleToObject
+ { REG(r11), REG(rax), REG(r15), EMIT_REMEMBERED_SET},
+ // StoreArrayLiteralElementStub::Generate
+ { REG(rbx), REG(rax), REG(rcx), EMIT_REMEMBERED_SET},
+ // FastNewClosureStub::Generate and
+ // StringAddStub::Generate
+ { REG(rcx), REG(rdx), REG(rbx), EMIT_REMEMBERED_SET},
+ // StringAddStub::Generate
+ { REG(rcx), REG(rax), REG(rbx), EMIT_REMEMBERED_SET},
+ // Null termination.
+ { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
+};
+
+#undef REG
+
+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(
+ Isolate* isolate) {
+ StoreBufferOverflowStub stub1(kDontSaveFPRegs);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ StoreBufferOverflowStub stub2(kSaveFPRegs);
+ stub2.GetCode(isolate)->set_is_pregenerated(true);
+}
+
+
+void RecordWriteStub::GenerateFixedRegStubsAheadOfTime(Isolate* isolate) {
+ for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
+ !entry->object.is(no_reg);
+ entry++) {
+ RecordWriteStub stub(entry->object,
+ entry->value,
+ entry->address,
+ entry->action,
+ kDontSaveFPRegs);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+bool CodeStub::CanUseFPRegisters() {
+ return true; // Always have SSE2 on x64.
+}
+
+
+// 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_);
+ Register address =
+ arg_reg_1.is(regs_.address()) ? kScratchRegister : regs_.address();
+ ASSERT(!address.is(regs_.object()));
+ ASSERT(!address.is(arg_reg_1));
+ __ Move(address, regs_.address());
+ __ Move(arg_reg_1, regs_.object());
+ // TODO(gc) Can we just set address arg2 in the beginning?
+ __ Move(arg_reg_2, address);
+ __ LoadAddress(arg_reg_3,
+ ExternalReference::isolate_address(masm->isolate()));
+ 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;
+
+ __ movq(regs_.scratch0(), Immediate(~Page::kPageAlignmentMask));
+ __ and_(regs_.scratch0(), regs_.object());
+ __ movq(regs_.scratch1(),
+ Operand(regs_.scratch0(),
+ MemoryChunk::kWriteBarrierCounterOffset));
+ __ subq(regs_.scratch1(), Immediate(1));
+ __ movq(Operand(regs_.scratch0(),
+ MemoryChunk::kWriteBarrierCounterOffset),
+ regs_.scratch1());
+ __ j(negative, &need_incremental);
+
+ // 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
+ // -- rcx : element index as smi
+ // -- rsp[0] : return address
+ // -- rsp[8] : array literal index in function
+ // -- rsp[16] : array literal
+ // clobbers rbx, rdx, rdi
+ // -----------------------------------
+
+ Label element_done;
+ Label double_elements;
+ Label smi_element;
+ Label slow_elements;
+ Label fast_elements;
+
+ // Get array literal index, array literal and its map.
+ __ movq(rdx, Operand(rsp, 1 * kPointerSize));
+ __ movq(rbx, Operand(rsp, 2 * kPointerSize));
+ __ movq(rdi, FieldOperand(rbx, JSObject::kMapOffset));
+
+ __ CheckFastElements(rdi, &double_elements);
+
+ // FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS
+ __ JumpIfSmi(rax, &smi_element);
+ __ CheckFastSmiElements(rdi, &fast_elements);
+
+ // Store into the array literal requires a elements transition. Call into
+ // the runtime.
+
+ __ bind(&slow_elements);
+ __ PopReturnAddressTo(rdi);
+ __ push(rbx);
+ __ push(rcx);
+ __ push(rax);
+ __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset));
+ __ push(rdx);
+ __ PushReturnAddressFrom(rdi);
+ __ 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_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);
+}
+
+
+void StubFailureTrampolineStub::Generate(MacroAssembler* masm) {
+ CEntryStub ces(1, fp_registers_ ? kSaveFPRegs : kDontSaveFPRegs);
+ __ Call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET);
+ int parameter_count_offset =
+ StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset;
+ __ movq(rbx, MemOperand(rbp, parameter_count_offset));
+ masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE);
+ __ PopReturnAddressTo(rcx);
+ int additional_offset = function_mode_ == JS_FUNCTION_STUB_MODE
+ ? kPointerSize
+ : 0;
+ __ lea(rsp, MemOperand(rsp, rbx, times_pointer_size, additional_offset));
+ __ jmp(rcx); // Return to IC Miss stub, continuation still on stack.
+}
+
+
+void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
+ if (masm->isolate()->function_entry_hook() != NULL) {
+ // It's always safe to call the entry hook stub, as the hook itself
+ // is not allowed to call back to V8.
+ AllowStubCallsScope allow_stub_calls(masm, true);
+
+ ProfileEntryHookStub stub;
+ masm->CallStub(&stub);
+ }
+}
+
+
+void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
+ // This stub can be called from essentially anywhere, so it needs to save
+ // all volatile and callee-save registers.
+ const size_t kNumSavedRegisters = 2;
+ __ push(arg_reg_1);
+ __ push(arg_reg_2);
+
+ // Calculate the original stack pointer and store it in the second arg.
+ __ lea(arg_reg_2, Operand(rsp, (kNumSavedRegisters + 1) * kPointerSize));
+
+ // Calculate the function address to the first arg.
+ __ movq(arg_reg_1, Operand(rsp, kNumSavedRegisters * kPointerSize));
+ __ subq(arg_reg_1, Immediate(Assembler::kShortCallInstructionLength));
+
+ // Save the remainder of the volatile registers.
+ masm->PushCallerSaved(kSaveFPRegs, arg_reg_1, arg_reg_2);
+
+ // Call the entry hook function.
+ __ movq(rax, FUNCTION_ADDR(masm->isolate()->function_entry_hook()),
+ RelocInfo::NONE64);
+
+ AllowExternalCallThatCantCauseGC scope(masm);
+
+ const int kArgumentCount = 2;
+ __ PrepareCallCFunction(kArgumentCount);
+ __ CallCFunction(rax, kArgumentCount);
+
+ // Restore volatile regs.
+ masm->PopCallerSaved(kSaveFPRegs, arg_reg_1, arg_reg_2);
+ __ pop(arg_reg_2);
+ __ pop(arg_reg_1);
+
+ __ Ret();
+}
+
+
+template<class T>
+static void CreateArrayDispatch(MacroAssembler* masm) {
+ int last_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= last_index; ++i) {
+ Label next;
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ __ cmpl(rdx, Immediate(kind));
+ __ j(not_equal, &next);
+ T stub(kind);
+ __ TailCallStub(&stub);
+ __ bind(&next);
+ }
+
+ // If we reached this point there is a problem.
+ __ Abort(kUnexpectedElementsKindInArrayConstructor);
+}
+
+
+static void CreateArrayDispatchOneArgument(MacroAssembler* masm) {
+ // rbx - type info cell
+ // rdx - kind
+ // rax - number of arguments
+ // rdi - constructor?
+ // rsp[0] - return address
+ // rsp[8] - last argument
+ ASSERT(FAST_SMI_ELEMENTS == 0);
+ ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ ASSERT(FAST_ELEMENTS == 2);
+ ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ ASSERT(FAST_DOUBLE_ELEMENTS == 4);
+ ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5);
+
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(),
+ masm->isolate());
+
+ // is the low bit set? If so, we are holey and that is good.
+ __ testb(rdx, Immediate(1));
+ Label normal_sequence;
+ __ j(not_zero, &normal_sequence);
+
+ // look at the first argument
+ __ movq(rcx, Operand(rsp, kPointerSize));
+ __ testq(rcx, rcx);
+ __ j(zero, &normal_sequence);
+
+ // We are going to create a holey array, but our kind is non-holey.
+ // Fix kind and retry (only if we have an allocation site in the cell).
+ __ incl(rdx);
+ __ Cmp(rbx, undefined_sentinel);
+ __ j(equal, &normal_sequence);
+ __ movq(rcx, FieldOperand(rbx, Cell::kValueOffset));
+ Handle<Map> allocation_site_map(
+ masm->isolate()->heap()->allocation_site_map(),
+ masm->isolate());
+ __ Cmp(FieldOperand(rcx, 0), allocation_site_map);
+ __ j(not_equal, &normal_sequence);
+
+ // Save the resulting elements kind in type info
+ __ Integer32ToSmi(rdx, rdx);
+ __ movq(FieldOperand(rcx, AllocationSite::kTransitionInfoOffset), rdx);
+ __ SmiToInteger32(rdx, rdx);
+
+ __ bind(&normal_sequence);
+ int last_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= last_index; ++i) {
+ Label next;
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ __ cmpl(rdx, Immediate(kind));
+ __ j(not_equal, &next);
+ ArraySingleArgumentConstructorStub stub(kind);
+ __ TailCallStub(&stub);
+ __ bind(&next);
+ }
+
+ // If we reached this point there is a problem.
+ __ Abort(kUnexpectedElementsKindInArrayConstructor);
+}
+
+
+template<class T>
+static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) {
+ int to_index = GetSequenceIndexFromFastElementsKind(
+ TERMINAL_FAST_ELEMENTS_KIND);
+ for (int i = 0; i <= to_index; ++i) {
+ ElementsKind kind = GetFastElementsKindFromSequenceIndex(i);
+ T stub(kind);
+ stub.GetCode(isolate)->set_is_pregenerated(true);
+ if (AllocationSite::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) {
+ T stub1(kind, CONTEXT_CHECK_REQUIRED, DISABLE_ALLOCATION_SITES);
+ stub1.GetCode(isolate)->set_is_pregenerated(true);
+ }
+ }
+}
+
+
+void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) {
+ ArrayConstructorStubAheadOfTimeHelper<ArrayNoArgumentConstructorStub>(
+ isolate);
+ ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
+ isolate);
+ ArrayConstructorStubAheadOfTimeHelper<ArrayNArgumentsConstructorStub>(
+ isolate);
+}
+
+
+void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime(
+ Isolate* isolate) {
+ ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS };
+ for (int i = 0; i < 2; i++) {
+ // For internal arrays we only need a few things
+ InternalArrayNoArgumentConstructorStub stubh1(kinds[i]);
+ stubh1.GetCode(isolate)->set_is_pregenerated(true);
+ InternalArraySingleArgumentConstructorStub stubh2(kinds[i]);
+ stubh2.GetCode(isolate)->set_is_pregenerated(true);
+ InternalArrayNArgumentsConstructorStub stubh3(kinds[i]);
+ stubh3.GetCode(isolate)->set_is_pregenerated(true);
+ }
+}
+
+
+void ArrayConstructorStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : argc
+ // -- rbx : type info cell
+ // -- rdi : constructor
+ // -- rsp[0] : return address
+ // -- rsp[8] : last argument
+ // -----------------------------------
+ Handle<Object> undefined_sentinel(
+ masm->isolate()->heap()->undefined_value(),
+ masm->isolate());
+
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the global and natives
+ // builtin Array functions which always have maps.
+
+ // Initial map for the builtin Array function should be a map.
+ __ movq(rcx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ STATIC_ASSERT(kSmiTag == 0);
+ Condition not_smi = NegateCondition(masm->CheckSmi(rcx));
+ __ Check(not_smi, kUnexpectedInitialMapForArrayFunction);
+ __ CmpObjectType(rcx, MAP_TYPE, rcx);
+ __ Check(equal, kUnexpectedInitialMapForArrayFunction);
+
+ // We should either have undefined in rbx or a valid cell
+ Label okay_here;
+ Handle<Map> cell_map = masm->isolate()->factory()->cell_map();
+ __ Cmp(rbx, undefined_sentinel);
+ __ j(equal, &okay_here);
+ __ Cmp(FieldOperand(rbx, 0), cell_map);
+ __ Assert(equal, kExpectedPropertyCellInRegisterRbx);
+ __ bind(&okay_here);
+ }
+
+ Label no_info, switch_ready;
+ // Get the elements kind and case on that.
+ __ Cmp(rbx, undefined_sentinel);
+ __ j(equal, &no_info);
+ __ movq(rdx, FieldOperand(rbx, Cell::kValueOffset));
+
+ // The type cell may have undefined in its value.
+ __ Cmp(rdx, undefined_sentinel);
+ __ j(equal, &no_info);
+
+ // The type cell has either an AllocationSite or a JSFunction
+ __ Cmp(FieldOperand(rdx, 0),
+ Handle<Map>(masm->isolate()->heap()->allocation_site_map()));
+ __ j(not_equal, &no_info);
+
+ __ movq(rdx, FieldOperand(rdx, AllocationSite::kTransitionInfoOffset));
+ __ SmiToInteger32(rdx, rdx);
+ __ jmp(&switch_ready);
+ __ bind(&no_info);
+ __ movq(rdx, Immediate(GetInitialFastElementsKind()));
+ __ bind(&switch_ready);
+
+ if (argument_count_ == ANY) {
+ Label not_zero_case, not_one_case;
+ __ testq(rax, rax);
+ __ j(not_zero, &not_zero_case);
+ CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
+
+ __ bind(&not_zero_case);
+ __ cmpl(rax, Immediate(1));
+ __ j(greater, &not_one_case);
+ CreateArrayDispatchOneArgument(masm);
+
+ __ bind(&not_one_case);
+ CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
+ } else if (argument_count_ == NONE) {
+ CreateArrayDispatch<ArrayNoArgumentConstructorStub>(masm);
+ } else if (argument_count_ == ONE) {
+ CreateArrayDispatchOneArgument(masm);
+ } else if (argument_count_ == MORE_THAN_ONE) {
+ CreateArrayDispatch<ArrayNArgumentsConstructorStub>(masm);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void InternalArrayConstructorStub::GenerateCase(
+ MacroAssembler* masm, ElementsKind kind) {
+ Label not_zero_case, not_one_case;
+ Label normal_sequence;
+
+ __ testq(rax, rax);
+ __ j(not_zero, &not_zero_case);
+ InternalArrayNoArgumentConstructorStub stub0(kind);
+ __ TailCallStub(&stub0);
+
+ __ bind(&not_zero_case);
+ __ cmpl(rax, Immediate(1));
+ __ j(greater, &not_one_case);
+
+ if (IsFastPackedElementsKind(kind)) {
+ // We might need to create a holey array
+ // look at the first argument
+ __ movq(rcx, Operand(rsp, kPointerSize));
+ __ testq(rcx, rcx);
+ __ j(zero, &normal_sequence);
+
+ InternalArraySingleArgumentConstructorStub
+ stub1_holey(GetHoleyElementsKind(kind));
+ __ TailCallStub(&stub1_holey);
+ }
+
+ __ bind(&normal_sequence);
+ InternalArraySingleArgumentConstructorStub stub1(kind);
+ __ TailCallStub(&stub1);
+
+ __ bind(&not_one_case);
+ InternalArrayNArgumentsConstructorStub stubN(kind);
+ __ TailCallStub(&stubN);
+}
+
+
+void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : argc
+ // -- rbx : type info cell
+ // -- rdi : constructor
+ // -- rsp[0] : return address
+ // -- rsp[8] : last argument
+ // -----------------------------------
+
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the global and natives
+ // builtin Array functions which always have maps.
+
+ // Initial map for the builtin Array function should be a map.
+ __ movq(rcx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
+ // Will both indicate a NULL and a Smi.
+ STATIC_ASSERT(kSmiTag == 0);
+ Condition not_smi = NegateCondition(masm->CheckSmi(rcx));
+ __ Check(not_smi, kUnexpectedInitialMapForArrayFunction);
+ __ CmpObjectType(rcx, MAP_TYPE, rcx);
+ __ Check(equal, kUnexpectedInitialMapForArrayFunction);
+ }
+
+ // Figure out the right elements kind
+ __ movq(rcx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Load the map's "bit field 2" into |result|. We only need the first byte,
+ // but the following masking takes care of that anyway.
+ __ movzxbq(rcx, FieldOperand(rcx, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ and_(rcx, Immediate(Map::kElementsKindMask));
+ __ shr(rcx, Immediate(Map::kElementsKindShift));
+
+ if (FLAG_debug_code) {
+ Label done;
+ __ cmpl(rcx, Immediate(FAST_ELEMENTS));
+ __ j(equal, &done);
+ __ cmpl(rcx, Immediate(FAST_HOLEY_ELEMENTS));
+ __ Assert(equal,
+ kInvalidElementsKindForInternalArrayOrInternalPackedArray);
+ __ bind(&done);
+ }
+
+ Label fast_elements_case;
+ __ cmpl(rcx, Immediate(FAST_ELEMENTS));
+ __ j(equal, &fast_elements_case);
+ GenerateCase(masm, FAST_HOLEY_ELEMENTS);
+
+ __ bind(&fast_elements_case);
+ GenerateCase(masm, FAST_ELEMENTS);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/code-stubs-x64.h b/chromium/v8/src/x64/code-stubs-x64.h
new file mode 100644
index 00000000000..e430bf2c805
--- /dev/null
+++ b/chromium/v8/src/x64/code-stubs-x64.h
@@ -0,0 +1,543 @@
+// 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_X64_CODE_STUBS_X64_H_
+#define V8_X64_CODE_STUBS_X64_H_
+
+#include "ic-inl.h"
+#include "type-info.h"
+
+namespace v8 {
+namespace internal {
+
+
+void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code);
+
+// Compute a transcendental math function natively, or call the
+// TranscendentalCache runtime function.
+class TranscendentalCacheStub: public PlatformCodeStub {
+ public:
+ enum ArgumentType {
+ TAGGED = 0,
+ UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
+ };
+
+ explicit TranscendentalCacheStub(TranscendentalCache::Type type,
+ ArgumentType argument_type)
+ : type_(type), argument_type_(argument_type) {}
+ void Generate(MacroAssembler* masm);
+ static void GenerateOperation(MacroAssembler* masm,
+ TranscendentalCache::Type type);
+ private:
+ TranscendentalCache::Type type_;
+ ArgumentType argument_type_;
+
+ Major MajorKey() { return TranscendentalCache; }
+ int MinorKey() { return type_ | argument_type_; }
+ Runtime::FunctionId RuntimeFunction();
+};
+
+
+class StoreBufferOverflowStub: public PlatformCodeStub {
+ public:
+ explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
+ : save_doubles_(save_fp) { }
+
+ void Generate(MacroAssembler* masm);
+
+ virtual bool IsPregenerated() { return true; }
+ static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate);
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ private:
+ SaveFPRegsMode save_doubles_;
+
+ Major MajorKey() { return StoreBufferOverflow; }
+ int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
+};
+
+
+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 GenerateCopyCharactersREP adds too much
+ // overhead. Copying of overlapping regions is not supported.
+ static void GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ bool ascii);
+
+ // Generate code for copying characters using the rep movs instruction.
+ // Copies rcx characters from rsi to rdi. Copying of overlapping regions is
+ // not supported.
+ static void GenerateCopyCharactersREP(MacroAssembler* masm,
+ Register dest, // Must be rdi.
+ Register src, // Must be rsi.
+ Register count, // Must be rcx.
+ bool ascii);
+
+
+ // Probe the string 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 string table. If the
+ // string is found the code falls through with the string in register rax.
+ static void GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* not_found);
+
+ // Generate string hash.
+ static void GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ static void GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ static void GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
+};
+
+
+class StringAddStub: public PlatformCodeStub {
+ public:
+ explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
+
+ private:
+ Major MajorKey() { return StringAdd; }
+ int MinorKey() { return flags_; }
+
+ void Generate(MacroAssembler* masm);
+
+ void GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* slow);
+
+ void GenerateRegisterArgsPush(MacroAssembler* masm);
+ void GenerateRegisterArgsPop(MacroAssembler* masm, Register temp);
+
+ const StringAddFlags flags_;
+};
+
+
+class SubStringStub: public PlatformCodeStub {
+ public:
+ SubStringStub() {}
+
+ private:
+ Major MajorKey() { return SubString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class StringCompareStub: public PlatformCodeStub {
+ public:
+ StringCompareStub() {}
+
+ // Compares two flat ASCII strings and returns result in rax.
+ static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4);
+
+ // Compares two flat ASCII strings for equality and returns result
+ // in rax.
+ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2);
+
+ private:
+ virtual Major MajorKey() { return StringCompare; }
+ virtual int MinorKey() { return 0; }
+ virtual void Generate(MacroAssembler* masm);
+
+ static void GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance near_jump = Label::kFar);
+};
+
+
+class NumberToStringStub: public PlatformCodeStub {
+ public:
+ NumberToStringStub() { }
+
+ // Generate code to do a lookup in the number string cache. If the number in
+ // the register object is found in the cache the generated code falls through
+ // with the result in the result register. The object and the result register
+ // can be the same. If the number is not found in the cache the code jumps to
+ // the label not_found with only the content of register object unchanged.
+ static void GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* not_found);
+
+ private:
+ static void GenerateConvertHashCodeToIndex(MacroAssembler* masm,
+ Register hash,
+ Register mask);
+
+ Major MajorKey() { return NumberToString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
+class NameDictionaryLookupStub: public PlatformCodeStub {
+ public:
+ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
+
+ NameDictionaryLookupStub(Register dictionary,
+ Register result,
+ Register index,
+ LookupMode mode)
+ : dictionary_(dictionary), result_(result), index_(index), mode_(mode) { }
+
+ void Generate(MacroAssembler* masm);
+
+ static void GenerateNegativeLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ Handle<Name> name,
+ Register r0);
+
+ static void GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1);
+
+ virtual bool SometimesSetsUpAFrame() { return false; }
+
+ private:
+ static const int kInlinedProbes = 4;
+ static const int kTotalProbes = 20;
+
+ static const int kCapacityOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kCapacityIndex * kPointerSize;
+
+ static const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+
+ Major MajorKey() { return NameDictionaryLookup; }
+
+ int MinorKey() {
+ return DictionaryBits::encode(dictionary_.code()) |
+ ResultBits::encode(result_.code()) |
+ IndexBits::encode(index_.code()) |
+ LookupModeBits::encode(mode_);
+ }
+
+ class DictionaryBits: public BitField<int, 0, 4> {};
+ class ResultBits: public BitField<int, 4, 4> {};
+ class IndexBits: public BitField<int, 8, 4> {};
+ class LookupModeBits: public BitField<LookupMode, 12, 1> {};
+
+ Register dictionary_;
+ Register result_;
+ Register index_;
+ LookupMode mode_;
+};
+
+
+class RecordWriteStub: public PlatformCodeStub {
+ 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(Isolate* isolate);
+ 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::NumAllocatableRegisters(); 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/chromium/v8/src/x64/codegen-x64.cc b/chromium/v8/src/x64/codegen-x64.cc
new file mode 100644
index 00000000000..a39f14b0757
--- /dev/null
+++ b/chromium/v8/src/x64/codegen-x64.cc
@@ -0,0 +1,749 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "codegen.h"
+#include "macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+// -------------------------------------------------------------------------
+// Platform-specific RuntimeCallHelper functions.
+
+void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
+ masm->EnterFrame(StackFrame::INTERNAL);
+ ASSERT(!masm->has_frame());
+ masm->set_has_frame(true);
+}
+
+
+void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
+ masm->LeaveFrame(StackFrame::INTERNAL);
+ ASSERT(masm->has_frame());
+ masm->set_has_frame(false);
+}
+
+
+#define __ masm.
+
+
+UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type) {
+ size_t actual_size;
+ // Allocate buffer in executable space.
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB,
+ &actual_size,
+ true));
+ if (buffer == NULL) {
+ // Fallback to library function if function cannot be created.
+ switch (type) {
+ case TranscendentalCache::SIN: return &sin;
+ case TranscendentalCache::COS: return &cos;
+ case TranscendentalCache::TAN: return &tan;
+ case TranscendentalCache::LOG: return &log;
+ default: UNIMPLEMENTED();
+ }
+ }
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+ // xmm0: raw double input.
+ // Move double input into registers.
+ __ push(rbx);
+ __ push(rdi);
+ __ movq(rbx, xmm0);
+ __ push(rbx);
+ __ fld_d(Operand(rsp, 0));
+ TranscendentalCacheStub::GenerateOperation(&masm, type);
+ // The return value is expected to be in xmm0.
+ __ fstp_d(Operand(rsp, 0));
+ __ pop(rbx);
+ __ movq(xmm0, rbx);
+ __ pop(rdi);
+ __ pop(rbx);
+ __ Ret();
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+ return FUNCTION_CAST<UnaryMathFunction>(buffer);
+}
+
+
+UnaryMathFunction CreateExpFunction() {
+ if (!FLAG_fast_math) return &exp;
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true));
+ if (buffer == NULL) return &exp;
+ ExternalReference::InitializeMathExpData();
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+ // xmm0: raw double input.
+ XMMRegister input = xmm0;
+ XMMRegister result = xmm1;
+ __ push(rax);
+ __ push(rbx);
+
+ MathExpGenerator::EmitMathExp(&masm, input, result, xmm2, rax, rbx);
+
+ __ pop(rbx);
+ __ pop(rax);
+ __ movsd(xmm0, result);
+ __ Ret();
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+ return FUNCTION_CAST<UnaryMathFunction>(buffer);
+}
+
+
+UnaryMathFunction CreateSqrtFunction() {
+ size_t actual_size;
+ // Allocate buffer in executable space.
+ byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB,
+ &actual_size,
+ true));
+ if (buffer == NULL) return &sqrt;
+
+ MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
+ // xmm0: raw double input.
+ // Move double input into registers.
+ __ sqrtsd(xmm0, xmm0);
+ __ Ret();
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ ASSERT(!RelocInfo::RequiresRelocation(desc));
+
+ CPU::FlushICache(buffer, actual_size);
+ OS::ProtectCode(buffer, actual_size);
+ return FUNCTION_CAST<UnaryMathFunction>(buffer);
+}
+
+
+#ifdef _WIN64
+typedef double (*ModuloFunction)(double, double);
+// Define custom fmod implementation.
+ModuloFunction CreateModuloFunction() {
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ Assembler masm(NULL, buffer, static_cast<int>(actual_size));
+ // Generated code is put into a fixed, unmovable, buffer, and not into
+ // the V8 heap. We can't, and don't, refer to any relocatable addresses
+ // (e.g. the JavaScript nan-object).
+
+ // Windows 64 ABI passes double arguments in xmm0, xmm1 and
+ // returns result in xmm0.
+ // Argument backing space is allocated on the stack above
+ // the return address.
+
+ // Compute x mod y.
+ // Load y and x (use argument backing store as temporary storage).
+ __ movsd(Operand(rsp, kPointerSize * 2), xmm1);
+ __ movsd(Operand(rsp, kPointerSize), xmm0);
+ __ fld_d(Operand(rsp, kPointerSize * 2));
+ __ fld_d(Operand(rsp, kPointerSize));
+
+ // Clear exception flags before operation.
+ {
+ Label no_exceptions;
+ __ fwait();
+ __ fnstsw_ax();
+ // Clear if Illegal Operand or Zero Division exceptions are set.
+ __ testb(rax, Immediate(5));
+ __ j(zero, &no_exceptions);
+ __ fnclex();
+ __ bind(&no_exceptions);
+ }
+
+ // Compute st(0) % st(1)
+ {
+ Label partial_remainder_loop;
+ __ bind(&partial_remainder_loop);
+ __ fprem();
+ __ fwait();
+ __ fnstsw_ax();
+ __ testl(rax, Immediate(0x400 /* C2 */));
+ // If C2 is set, computation only has partial result. Loop to
+ // continue computation.
+ __ j(not_zero, &partial_remainder_loop);
+ }
+
+ Label valid_result;
+ Label return_result;
+ // If Invalid Operand or Zero Division exceptions are set,
+ // return NaN.
+ __ testb(rax, Immediate(5));
+ __ j(zero, &valid_result);
+ __ fstp(0); // Drop result in st(0).
+ int64_t kNaNValue = V8_INT64_C(0x7ff8000000000000);
+ __ movq(rcx, kNaNValue, RelocInfo::NONE64);
+ __ movq(Operand(rsp, kPointerSize), rcx);
+ __ movsd(xmm0, Operand(rsp, kPointerSize));
+ __ jmp(&return_result);
+
+ // If result is valid, return that.
+ __ bind(&valid_result);
+ __ fstp_d(Operand(rsp, kPointerSize));
+ __ movsd(xmm0, Operand(rsp, kPointerSize));
+
+ // Clean up FPU stack and exceptions and return xmm0
+ __ bind(&return_result);
+ __ fstp(0); // Unload y.
+
+ Label clear_exceptions;
+ __ testb(rax, Immediate(0x3f /* Any Exception*/));
+ __ j(not_zero, &clear_exceptions);
+ __ ret(0);
+ __ bind(&clear_exceptions);
+ __ fnclex();
+ __ ret(0);
+
+ CodeDesc desc;
+ masm.GetCode(&desc);
+ OS::ProtectCode(buffer, actual_size);
+ // Call the function from C++ through this pointer.
+ return FUNCTION_CAST<ModuloFunction>(buffer);
+}
+
+#endif
+
+#undef __
+
+// -------------------------------------------------------------------------
+// Code generators
+
+#define __ ACCESS_MASM(masm)
+
+void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
+ MacroAssembler* masm, AllocationSiteMode mode,
+ Label* allocation_memento_found) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rbx : target map
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ if (mode == TRACK_ALLOCATION_SITE) {
+ ASSERT(allocation_memento_found != NULL);
+ __ TestJSArrayForAllocationMemento(rdx, rdi);
+ __ j(equal, allocation_memento_found);
+ }
+
+ // Set transitioned map.
+ __ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx);
+ __ RecordWriteField(rdx,
+ HeapObject::kMapOffset,
+ rbx,
+ rdi,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+}
+
+
+void ElementsTransitionGenerator::GenerateSmiToDouble(
+ MacroAssembler* masm, AllocationSiteMode mode, 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, new_backing_store, only_change_map, done;
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ __ TestJSArrayForAllocationMemento(rdx, rdi);
+ __ j(equal, fail);
+ }
+
+ // Check for empty arrays, which only require a map transition and no changes
+ // to the backing store.
+ __ movq(r8, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ CompareRoot(r8, Heap::kEmptyFixedArrayRootIndex);
+ __ j(equal, &only_change_map);
+
+ // Check backing store for COW-ness. For COW arrays we have to
+ // allocate a new backing store.
+ __ SmiToInteger32(r9, FieldOperand(r8, FixedDoubleArray::kLengthOffset));
+ __ CompareRoot(FieldOperand(r8, HeapObject::kMapOffset),
+ Heap::kFixedCOWArrayMapRootIndex);
+ __ j(equal, &new_backing_store);
+ // Check if the backing store is in new-space. If not, we need to allocate
+ // a new one since the old one is in pointer-space.
+ // If in new space, we can reuse the old backing store because it is
+ // the same size.
+ __ JumpIfNotInNewSpace(r8, rdi, &new_backing_store);
+
+ __ movq(r14, r8); // Destination array equals source array.
+
+ // 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);
+
+ __ bind(&allocated);
+ // 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::NONE64);
+ // r15: the-hole NaN
+ __ jmp(&entry);
+
+ // Allocate new backing store.
+ __ bind(&new_backing_store);
+ __ lea(rdi, Operand(r9, times_8, FixedArray::kHeaderSize));
+ __ Allocate(rdi, r14, r11, r15, fail, TAG_OBJECT);
+ // Set backing store's map
+ __ LoadRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex);
+ __ movq(FieldOperand(r14, HeapObject::kMapOffset), rdi);
+ // 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);
+
+ __ bind(&only_change_map);
+ // Set transitioned map.
+ __ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx);
+ __ RecordWriteField(rdx,
+ HeapObject::kMapOffset,
+ rbx,
+ rdi,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ __ jmp(&done);
+
+ // Conversion loop.
+ __ bind(&loop);
+ __ movq(rbx,
+ FieldOperand(r8, r9, times_pointer_size, 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, kObjectFoundInSmiOnlyArray);
+ }
+
+ __ movq(FieldOperand(r14, r9, times_8, FixedDoubleArray::kHeaderSize), r15);
+ __ bind(&entry);
+ __ decq(r9);
+ __ j(not_sign, &loop);
+
+ __ bind(&done);
+}
+
+
+void ElementsTransitionGenerator::GenerateDoubleToObject(
+ MacroAssembler* masm, AllocationSiteMode mode, 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, only_change_map;
+
+ if (mode == TRACK_ALLOCATION_SITE) {
+ __ TestJSArrayForAllocationMemento(rdx, rdi);
+ __ j(equal, fail);
+ }
+
+ // Check for empty arrays, which only require a map transition and no changes
+ // to the backing store.
+ __ movq(r8, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ CompareRoot(r8, Heap::kEmptyFixedArrayRootIndex);
+ __ j(equal, &only_change_map);
+
+ __ 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));
+ __ Allocate(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::NONE64);
+ __ 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_8,
+ 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);
+
+ // 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));
+
+ __ bind(&only_change_map);
+ // Set transitioned map.
+ __ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx);
+ __ RecordWriteField(rdx,
+ HeapObject::kMapOffset,
+ rbx,
+ rdi,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+}
+
+
+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::kempty_stringRootIndex);
+ __ 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, kExternalStringExpectedButNotFound);
+ }
+ // 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 & kOneByteStringTag) != 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,
+ SeqOneByteString::kHeaderSize));
+ __ bind(&done);
+}
+
+
+void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
+ XMMRegister input,
+ XMMRegister result,
+ XMMRegister double_scratch,
+ Register temp1,
+ Register temp2) {
+ ASSERT(!input.is(result));
+ ASSERT(!input.is(double_scratch));
+ ASSERT(!result.is(double_scratch));
+ ASSERT(!temp1.is(temp2));
+ ASSERT(ExternalReference::math_exp_constants(0).address() != NULL);
+
+ Label done;
+
+ __ movq(kScratchRegister, ExternalReference::math_exp_constants(0));
+ __ movsd(double_scratch, Operand(kScratchRegister, 0 * kDoubleSize));
+ __ xorpd(result, result);
+ __ ucomisd(double_scratch, input);
+ __ j(above_equal, &done);
+ __ ucomisd(input, Operand(kScratchRegister, 1 * kDoubleSize));
+ __ movsd(result, Operand(kScratchRegister, 2 * kDoubleSize));
+ __ j(above_equal, &done);
+ __ movsd(double_scratch, Operand(kScratchRegister, 3 * kDoubleSize));
+ __ movsd(result, Operand(kScratchRegister, 4 * kDoubleSize));
+ __ mulsd(double_scratch, input);
+ __ addsd(double_scratch, result);
+ __ movq(temp2, double_scratch);
+ __ subsd(double_scratch, result);
+ __ movsd(result, Operand(kScratchRegister, 6 * kDoubleSize));
+ __ lea(temp1, Operand(temp2, 0x1ff800));
+ __ and_(temp2, Immediate(0x7ff));
+ __ shr(temp1, Immediate(11));
+ __ mulsd(double_scratch, Operand(kScratchRegister, 5 * kDoubleSize));
+ __ movq(kScratchRegister, ExternalReference::math_exp_log_table());
+ __ shl(temp1, Immediate(52));
+ __ or_(temp1, Operand(kScratchRegister, temp2, times_8, 0));
+ __ movq(kScratchRegister, ExternalReference::math_exp_constants(0));
+ __ subsd(double_scratch, input);
+ __ movsd(input, double_scratch);
+ __ subsd(result, double_scratch);
+ __ mulsd(input, double_scratch);
+ __ mulsd(result, input);
+ __ movq(input, temp1);
+ __ mulsd(result, Operand(kScratchRegister, 7 * kDoubleSize));
+ __ subsd(result, double_scratch);
+ __ addsd(result, Operand(kScratchRegister, 8 * kDoubleSize));
+ __ mulsd(result, input);
+
+ __ bind(&done);
+}
+
+#undef __
+
+
+static const int kNoCodeAgeSequenceLength = 6;
+
+static byte* GetNoCodeAgeSequence(uint32_t* length) {
+ static bool initialized = false;
+ static byte sequence[kNoCodeAgeSequenceLength];
+ *length = kNoCodeAgeSequenceLength;
+ if (!initialized) {
+ // The sequence of instructions that is patched out for aging code is the
+ // following boilerplate stack-building prologue that is found both in
+ // FUNCTION and OPTIMIZED_FUNCTION code:
+ CodePatcher patcher(sequence, kNoCodeAgeSequenceLength);
+ patcher.masm()->push(rbp);
+ patcher.masm()->movq(rbp, rsp);
+ patcher.masm()->push(rsi);
+ patcher.masm()->push(rdi);
+ initialized = true;
+ }
+ return sequence;
+}
+
+
+bool Code::IsYoungSequence(byte* sequence) {
+ uint32_t young_length;
+ byte* young_sequence = GetNoCodeAgeSequence(&young_length);
+ bool result = (!memcmp(sequence, young_sequence, young_length));
+ ASSERT(result || *sequence == kCallOpcode);
+ return result;
+}
+
+
+void Code::GetCodeAgeAndParity(byte* sequence, Age* age,
+ MarkingParity* parity) {
+ if (IsYoungSequence(sequence)) {
+ *age = kNoAge;
+ *parity = NO_MARKING_PARITY;
+ } else {
+ sequence++; // Skip the kCallOpcode byte
+ Address target_address = sequence + *reinterpret_cast<int*>(sequence) +
+ Assembler::kCallTargetAddressOffset;
+ Code* stub = GetCodeFromTargetAddress(target_address);
+ GetCodeAgeAndParity(stub, age, parity);
+ }
+}
+
+
+void Code::PatchPlatformCodeAge(byte* sequence,
+ Code::Age age,
+ MarkingParity parity) {
+ uint32_t young_length;
+ byte* young_sequence = GetNoCodeAgeSequence(&young_length);
+ if (age == kNoAge) {
+ CopyBytes(sequence, young_sequence, young_length);
+ CPU::FlushICache(sequence, young_length);
+ } else {
+ Code* stub = GetCodeAgeStub(age, parity);
+ CodePatcher patcher(sequence, young_length);
+ patcher.masm()->call(stub->instruction_start());
+ for (int i = 0;
+ i < kNoCodeAgeSequenceLength - Assembler::kShortCallInstructionLength;
+ i++) {
+ patcher.masm()->nop();
+ }
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/codegen-x64.h b/chromium/v8/src/x64/codegen-x64.h
new file mode 100644
index 00000000000..5747e0bc6f0
--- /dev/null
+++ b/chromium/v8/src/x64/codegen-x64.h
@@ -0,0 +1,108 @@
+// 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_X64_CODEGEN_X64_H_
+#define V8_X64_CODEGEN_X64_H_
+
+#include "ast.h"
+#include "ic-inl.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations
+class CompilationInfo;
+
+enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
+
+// -------------------------------------------------------------------------
+// CodeGenerator
+
+class CodeGenerator: public AstVisitor {
+ public:
+ CodeGenerator() {
+ InitializeAstVisitor();
+ }
+
+ static bool MakeCode(CompilationInfo* info);
+
+ // Printing of AST, etc. as requested by flags.
+ static void MakeCodePrologue(CompilationInfo* info, const char* kind);
+
+ // Allocate and install the code.
+ static Handle<Code> MakeCodeEpilogue(MacroAssembler* masm,
+ Code::Flags flags,
+ CompilationInfo* info);
+
+ // Print the code after compiling it.
+ static void PrintCode(Handle<Code> code, CompilationInfo* info);
+
+ static bool ShouldGenerateLog(Expression* type);
+
+ static bool RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here = false);
+
+ DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
+
+ 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);
+};
+
+
+class MathExpGenerator : public AllStatic {
+ public:
+ static void EmitMathExp(MacroAssembler* masm,
+ XMMRegister input,
+ XMMRegister result,
+ XMMRegister double_scratch,
+ Register temp1,
+ Register temp2);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MathExpGenerator);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_X64_CODEGEN_X64_H_
diff --git a/chromium/v8/src/x64/cpu-x64.cc b/chromium/v8/src/x64/cpu-x64.cc
new file mode 100644
index 00000000000..96c53308326
--- /dev/null
+++ b/chromium/v8/src/x64/cpu-x64.cc
@@ -0,0 +1,89 @@
+// Copyright 2012 the V8 project authors. 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.
+
+// CPU specific code for x64 independent of OS goes here.
+
+#if defined(__GNUC__) && !defined(__MINGW64__)
+#include "third_party/valgrind/valgrind.h"
+#endif
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_X64
+
+#include "cpu.h"
+#include "macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+
+void CPU::SetUp() {
+ CpuFeatures::Probe();
+}
+
+
+bool CPU::SupportsCrankshaft() {
+ return true; // Yay!
+}
+
+
+void CPU::FlushICache(void* start, size_t size) {
+ // No need to flush the instruction cache on Intel. On Intel instruction
+ // cache flushing is only necessary when multiple cores running the same
+ // code simultaneously. V8 (and JavaScript) is single threaded and when code
+ // is patched on an intel CPU the core performing the patching will have its
+ // own instruction cache updated automatically.
+
+ // If flushing of the instruction cache becomes necessary Windows has the
+ // API function FlushInstructionCache.
+
+ // By default, valgrind only checks the stack for writes that might need to
+ // invalidate already cached translated code. This leads to random
+ // instability when code patches or moves are sometimes unnoticed. One
+ // solution is to run valgrind with --smc-check=all, but this comes at a big
+ // performance cost. We can notify valgrind to invalidate its cache.
+#ifdef VALGRIND_DISCARD_TRANSLATIONS
+ unsigned res = VALGRIND_DISCARD_TRANSLATIONS(start, size);
+ USE(res);
+#endif
+}
+
+
+void CPU::DebugBreak() {
+#ifdef _MSC_VER
+ // To avoid Visual Studio runtime support the following code can be used
+ // instead
+ // __asm { int 3 }
+ __debugbreak();
+#else
+ asm("int $3");
+#endif
+}
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/debug-x64.cc b/chromium/v8/src/x64/debug-x64.cc
new file mode 100644
index 00000000000..e6bc92950a9
--- /dev/null
+++ b/chromium/v8/src/x64/debug-x64.cc
@@ -0,0 +1,362 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "assembler.h"
+#include "codegen.h"
+#include "debug.h"
+
+
+namespace v8 {
+namespace internal {
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+bool BreakLocationIterator::IsDebugBreakAtReturn() {
+ return Debug::IsDebugBreakAtReturn(rinfo());
+}
+
+
+// Patch the JS frame exit code with a debug break call. See
+// CodeGenerator::VisitReturnStatement and VirtualFrame::Exit in codegen-x64.cc
+// for the precise return instructions sequence.
+void BreakLocationIterator::SetDebugBreakAtReturn() {
+ ASSERT(Assembler::kJSReturnSequenceLength >= Assembler::kCallSequenceLength);
+ rinfo()->PatchCodeWithCall(
+ Isolate::Current()->debug()->debug_break_return()->entry(),
+ Assembler::kJSReturnSequenceLength - Assembler::kCallSequenceLength);
+}
+
+
+// Restore the JS frame exit code.
+void BreakLocationIterator::ClearDebugBreakAtReturn() {
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kJSReturnSequenceLength);
+}
+
+
+// A debug break in the frame exit code is identified by the JS frame exit code
+// having been patched with a call instruction.
+bool Debug::IsDebugBreakAtReturn(v8::internal::RelocInfo* rinfo) {
+ ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
+ return rinfo->IsPatchedReturnSequence();
+}
+
+
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return !Assembler::IsNop(rinfo()->pc());
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCodeWithCall(
+ Isolate::Current()->debug()->debug_break_slot()->entry(),
+ Assembler::kDebugBreakSlotLength - Assembler::kCallSequenceLength);
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
+}
+
+const bool Debug::FramePaddingLayout::kIsSupported = true;
+
+
+#define __ ACCESS_MASM(masm)
+
+
+static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
+ RegList object_regs,
+ RegList non_object_regs,
+ bool convert_call_to_jmp) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Load padding words on stack.
+ for (int i = 0; i < Debug::FramePaddingLayout::kInitialSize; i++) {
+ __ Push(Smi::FromInt(Debug::FramePaddingLayout::kPaddingValue));
+ }
+ __ Push(Smi::FromInt(Debug::FramePaddingLayout::kInitialSize));
+
+ // 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);
+ }
+ }
+
+#ifdef DEBUG
+ __ 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);
+ }
+ // 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);
+ }
+ }
+
+ // Read current padding counter and skip corresponding number of words.
+ __ pop(kScratchRegister);
+ __ SmiToInteger32(kScratchRegister, kScratchRegister);
+ __ lea(rsp, Operand(rsp, kScratchRegister, times_pointer_size, 0));
+
+ // 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) {
+ __ addq(rsp, Immediate(kPointerSize));
+ }
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ ExternalReference after_break_target =
+ ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate());
+ __ movq(kScratchRegister, after_break_target);
+ __ jmp(Operand(kScratchRegister, 0));
+}
+
+
+void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
+ // Register state for IC load call (from ic-x64.cc).
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit(), 0, false);
+}
+
+
+void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
+ // Register state for IC store call (from ic-x64.cc).
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(
+ masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
+}
+
+
+void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
+ // Register state for keyed IC load call (from ic-x64.cc).
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, rax.bit() | rdx.bit(), 0, false);
+}
+
+
+void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
+ // Register state for keyed IC load call (from ic-x64.cc).
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(
+ masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
+}
+
+
+void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) {
+ // Register state for CompareNil IC
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, rax.bit(), 0, false);
+}
+
+
+void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
+ // Register state for IC call call (from ic-x64.cc)
+ // ----------- S t a t e -------------
+ // -- rcx: function name
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, rcx.bit(), 0, false);
+}
+
+
+void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
+ // Register state just before return from JS function (from codegen-x64.cc).
+ // ----------- S t a t e -------------
+ // -- rax: return value
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, rax.bit(), 0, true);
+}
+
+
+void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
+ // Register state for CallFunctionStub (from code-stubs-x64.cc).
+ // ----------- S t a t e -------------
+ // -- rdi : function
+ // -----------------------------------
+ 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::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 -------------
+ // -- rax: number of arguments
+ // -----------------------------------
+ // 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);
+}
+
+
+void Debug::GenerateSlot(MacroAssembler* masm) {
+ // Generate enough nop's to make space for a call instruction.
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ RecordDebugBreakSlot();
+ __ Nop(Assembler::kDebugBreakSlotLength);
+ ASSERT_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
+ // In the places where a debug break slot is inserted no registers can contain
+ // object pointers.
+ Generate_DebugBreakCallHelper(masm, 0, 0, true);
+}
+
+
+void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ masm->ret(0);
+}
+
+
+void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ ExternalReference restarter_frame_function_slot =
+ ExternalReference(Debug_Address::RestarterFrameFunctionPointer(),
+ masm->isolate());
+ __ movq(rax, restarter_frame_function_slot);
+ __ movq(Operand(rax, 0), Immediate(0));
+
+ // We do not know our frame height, but set rsp based on rbp.
+ __ lea(rsp, Operand(rbp, -1 * kPointerSize));
+
+ __ pop(rdi); // Function.
+ __ pop(rbp);
+
+ // Load context from the function.
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+
+ // Get function code.
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
+ __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
+
+ // Re-run JSFunction, rdi is function, rsi is context.
+ __ jmp(rdx);
+}
+
+const bool Debug::kFrameDropperSupported = true;
+
+#undef __
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/deoptimizer-x64.cc b/chromium/v8/src/x64/deoptimizer-x64.cc
new file mode 100644
index 00000000000..e9cf567f7e4
--- /dev/null
+++ b/chromium/v8/src/x64/deoptimizer-x64.cc
@@ -0,0 +1,596 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "codegen.h"
+#include "deoptimizer.h"
+#include "full-codegen.h"
+#include "safepoint-table.h"
+
+namespace v8 {
+namespace internal {
+
+
+const int Deoptimizer::table_entry_size_ = 10;
+
+
+int Deoptimizer::patch_size() {
+ return Assembler::kCallSequenceLength;
+}
+
+
+void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
+ // 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 absolute call to the
+ // corresponding deoptimization entry, or a short call to an absolute
+ // jump if space is short. The absolute jumps are put in a table just
+ // before the safepoint table (space was allocated there when the Code
+ // object was created, if necessary).
+
+ Address instruction_start = code->instruction_start();
+#ifdef DEBUG
+ Address prev_call_address = NULL;
+#endif
+ DeoptimizationInputData* deopt_data =
+ DeoptimizationInputData::cast(code->deoptimization_data());
+ for (int i = 0; i < deopt_data->DeoptCount(); i++) {
+ if (deopt_data->Pc(i)->value() == -1) continue;
+ // Position where Call will be patched in.
+ Address call_address = instruction_start + deopt_data->Pc(i)->value();
+ // There is room enough to write a long call instruction because we pad
+ // LLazyBailout instructions with nops if necessary.
+ CodePatcher patcher(call_address, Assembler::kCallSequenceLength);
+ patcher.masm()->Call(GetDeoptimizationEntry(isolate, i, LAZY),
+ RelocInfo::NONE64);
+ 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
+ }
+}
+
+
+static const byte kJnsInstruction = 0x79;
+static const byte kJnsOffset = 0x1d;
+static const byte kCallInstruction = 0xe8;
+static const byte kNopByteOne = 0x66;
+static const byte kNopByteTwo = 0x90;
+
+// The back edge bookkeeping code matches the pattern:
+//
+// add <profiling_counter>, <-delta>
+// jns ok
+// call <stack guard>
+// ok:
+//
+// We will patch away the branch so the code is:
+//
+// add <profiling_counter>, <-delta> ;; Not changed
+// nop
+// nop
+// call <on-stack replacment>
+// ok:
+
+void Deoptimizer::PatchInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ ASSERT(!InterruptCodeIsPatched(unoptimized_code,
+ pc_after,
+ interrupt_code,
+ replacement_code));
+ // Turn the jump into nops.
+ Address call_target_address = pc_after - kIntSize;
+ *(call_target_address - 3) = kNopByteOne;
+ *(call_target_address - 2) = kNopByteTwo;
+ // Replace the call address.
+ 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::RevertInterruptCodeAt(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ ASSERT(InterruptCodeIsPatched(unoptimized_code,
+ pc_after,
+ interrupt_code,
+ replacement_code));
+ // Restore the original jump.
+ Address call_target_address = pc_after - kIntSize;
+ *(call_target_address - 3) = kJnsInstruction;
+ *(call_target_address - 2) = kJnsOffset;
+ // Restore the original call address.
+ Assembler::set_target_address_at(call_target_address,
+ interrupt_code->entry());
+
+ interrupt_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
+ unoptimized_code, call_target_address, interrupt_code);
+}
+
+
+#ifdef DEBUG
+bool Deoptimizer::InterruptCodeIsPatched(Code* unoptimized_code,
+ Address pc_after,
+ Code* interrupt_code,
+ Code* replacement_code) {
+ Address call_target_address = pc_after - kIntSize;
+ ASSERT_EQ(kCallInstruction, *(call_target_address - 1));
+ if (*(call_target_address - 3) == kNopByteOne) {
+ ASSERT(replacement_code->entry() ==
+ Assembler::target_address_at(call_target_address));
+ ASSERT_EQ(kNopByteTwo, *(call_target_address - 2));
+ return true;
+ } else {
+ ASSERT_EQ(interrupt_code->entry(),
+ Assembler::target_address_at(call_target_address));
+ ASSERT_EQ(kJnsInstruction, *(call_target_address - 3));
+ ASSERT_EQ(kJnsOffset, *(call_target_address - 2));
+ return false;
+ }
+}
+#endif // DEBUG
+
+
+static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) {
+ ByteArray* translations = data->TranslationByteArray();
+ int length = data->DeoptCount();
+ for (int i = 0; i < length; i++) {
+ if (data->AstId(i) == 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() {
+ DeoptimizationInputData* data = DeoptimizationInputData::cast(
+ compiled_code_->deoptimization_data());
+ unsigned ast_id = data->OsrAstId()->value();
+ // TODO(kasperl): This should not be the bailout_id_. It should be
+ // the ast id. Confusing.
+ ASSERT(bailout_id_ == ast_id);
+
+ int bailout_id = LookupBailoutId(data, BailoutId(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);
+ int closure_id = iterator.Next();
+ USE(closure_id);
+ ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
+ 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 = compiled_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_));
+ PrintFunctionName();
+ 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) {
+ intptr_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(" [rsp + %d] <- 0x%08" V8PRIxPTR " ; [rsp + %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<intptr_t>(from_));
+ } else {
+ // 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()));
+
+ unsigned pc_offset = data->OsrPcOffset()->value();
+ intptr_t pc = reinterpret_cast<intptr_t>(
+ compiled_code_->entry() + pc_offset);
+ output_[0]->SetPc(pc);
+ }
+ Code* continuation =
+ function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR);
+ output_[0]->SetContinuation(
+ reinterpret_cast<intptr_t>(continuation->entry()));
+
+ if (FLAG_trace_osr) {
+ PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
+ ok ? "finished" : "aborted",
+ reinterpret_cast<intptr_t>(function_));
+ PrintFunctionName();
+ PrintF(" => pc=0x%0" V8PRIxPTR "]\n", output_[0]->GetPc());
+ }
+}
+
+
+void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
+ // 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 rbp and rsp are set to the correct values though.
+ for (int i = 0; i < Register::kNumRegisters; i++) {
+ input_->SetRegister(i, i * 4);
+ }
+ input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp()));
+ input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp()));
+ for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); 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::uint64_at(tos + i));
+ }
+}
+
+
+void Deoptimizer::SetPlatformCompiledStubRegisters(
+ FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
+ intptr_t handler =
+ reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
+ int params = descriptor->register_param_count_;
+ if (descriptor->stack_parameter_count_ != NULL) {
+ params++;
+ }
+ output_frame->SetRegister(rax.code(), params);
+ output_frame->SetRegister(rbx.code(), handler);
+}
+
+
+void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
+ for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
+ double double_value = input_->GetDoubleRegister(i);
+ output_frame->SetDoubleRegister(i, double_value);
+ }
+}
+
+
+bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
+ // There is no dynamic alignment padding on x64 in the input frame.
+ return false;
+}
+
+
+#define __ masm()->
+
+void Deoptimizer::EntryGenerator::Generate() {
+ GeneratePrologue();
+
+ // Save all general purpose registers before messing with them.
+ const int kNumberOfRegisters = Register::kNumRegisters;
+
+ const int kDoubleRegsSize = kDoubleSize *
+ XMMRegister::NumAllocatableRegisters();
+ __ subq(rsp, Immediate(kDoubleRegsSize));
+
+ for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
+ XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
+ int offset = i * kDoubleSize;
+ __ movsd(Operand(rsp, offset), xmm_reg);
+ }
+
+ // We push all registers onto the stack, even though we do not need
+ // to restore all later.
+ for (int i = 0; i < kNumberOfRegisters; i++) {
+ Register r = Register::from_code(i);
+ __ push(r);
+ }
+
+ const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
+ kDoubleRegsSize;
+
+ // We use this to keep the value of the fifth argument temporarily.
+ // Unfortunately we can't store it directly in r8 (used for passing
+ // this on linux), since it is another parameter passing register on windows.
+ Register arg5 = r11;
+
+ // Get the bailout id from the stack.
+ __ movq(arg_reg_3, Operand(rsp, kSavedRegistersAreaSize));
+
+ // Get the address of the location in the code object
+ // and compute the fp-to-sp delta in register arg5.
+ __ movq(arg_reg_4,
+ Operand(rsp, kSavedRegistersAreaSize + 1 * kPointerSize));
+ __ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 2 * kPointerSize));
+
+ __ subq(arg5, rbp);
+ __ neg(arg5);
+
+ // Allocate a new deoptimizer object.
+ __ PrepareCallCFunction(6);
+ __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ movq(arg_reg_1, rax);
+ __ Set(arg_reg_2, type());
+ // Args 3 and 4 are already in the right registers.
+
+ // On windows put the arguments on the stack (PrepareCallCFunction
+ // has created space for this). On linux pass the arguments in r8 and r9.
+#ifdef _WIN64
+ __ movq(Operand(rsp, 4 * kPointerSize), arg5);
+ __ LoadAddress(arg5, ExternalReference::isolate_address(isolate()));
+ __ movq(Operand(rsp, 5 * kPointerSize), arg5);
+#else
+ __ movq(r8, arg5);
+ __ LoadAddress(r9, ExternalReference::isolate_address(isolate()));
+#endif
+
+ { 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()));
+
+ // Fill in the input registers.
+ for (int i = kNumberOfRegisters -1; i >= 0; i--) {
+ int offset = (i * kPointerSize) + FrameDescription::registers_offset();
+ __ pop(Operand(rbx, offset));
+ }
+
+ // Fill in the double input registers.
+ int double_regs_offset = FrameDescription::double_registers_offset();
+ for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) {
+ int dst_offset = i * kDoubleSize + double_regs_offset;
+ __ pop(Operand(rbx, dst_offset));
+ }
+
+ // Remove the bailout id and return address from the stack.
+ __ addq(rsp, Immediate(2 * kPointerSize));
+
+ // Compute a pointer to the unwinding limit in register rcx; that is
+ // the first stack slot not part of the input frame.
+ __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
+ __ addq(rcx, rsp);
+
+ // Unwind the stack down to - but not including - the unwinding
+ // limit and copy the contents of the activation frame to the input
+ // frame description.
+ __ lea(rdx, Operand(rbx, FrameDescription::frame_content_offset()));
+ Label pop_loop_header;
+ __ jmp(&pop_loop_header);
+ Label pop_loop;
+ __ bind(&pop_loop);
+ __ pop(Operand(rdx, 0));
+ __ addq(rdx, Immediate(sizeof(intptr_t)));
+ __ bind(&pop_loop_header);
+ __ cmpq(rcx, rsp);
+ __ j(not_equal, &pop_loop);
+
+ // Compute the output frame in the deoptimizer.
+ __ push(rax);
+ __ PrepareCallCFunction(2);
+ __ movq(arg_reg_1, rax);
+ __ LoadAddress(arg_reg_2, ExternalReference::isolate_address(isolate()));
+ {
+ AllowExternalCallThatCantCauseGC scope(masm());
+ __ CallCFunction(
+ ExternalReference::compute_output_frames_function(isolate()), 2);
+ }
+ __ pop(rax);
+
+ // Replace the current frame with the output frames.
+ Label outer_push_loop, inner_push_loop,
+ outer_loop_header, inner_loop_header;
+ // Outer loop state: rax = current FrameDescription**, rdx = one past the
+ // last FrameDescription**.
+ __ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
+ __ movq(rax, Operand(rax, Deoptimizer::output_offset()));
+ __ lea(rdx, Operand(rax, rdx, times_pointer_size, 0));
+ __ jmp(&outer_loop_header);
+ __ bind(&outer_push_loop);
+ // Inner loop state: rbx = current FrameDescription*, rcx = loop index.
+ __ movq(rbx, Operand(rax, 0));
+ __ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
+ __ jmp(&inner_loop_header);
+ __ bind(&inner_push_loop);
+ __ subq(rcx, Immediate(sizeof(intptr_t)));
+ __ push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset()));
+ __ bind(&inner_loop_header);
+ __ testq(rcx, rcx);
+ __ j(not_zero, &inner_push_loop);
+ __ addq(rax, Immediate(kPointerSize));
+ __ bind(&outer_loop_header);
+ __ cmpq(rax, rdx);
+ __ j(below, &outer_push_loop);
+
+ for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); ++i) {
+ XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
+ int src_offset = i * kDoubleSize + double_regs_offset;
+ __ movsd(xmm_reg, Operand(rbx, src_offset));
+ }
+
+ // Push state, pc, and continuation from the last output frame.
+ if (type() != OSR) {
+ __ push(Operand(rbx, FrameDescription::state_offset()));
+ }
+ __ push(Operand(rbx, FrameDescription::pc_offset()));
+ __ push(Operand(rbx, FrameDescription::continuation_offset()));
+
+ // Push the registers from the last output frame.
+ for (int i = 0; i < kNumberOfRegisters; i++) {
+ int offset = (i * kPointerSize) + FrameDescription::registers_offset();
+ __ push(Operand(rbx, offset));
+ }
+
+ // Restore the registers from the stack.
+ for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
+ Register r = Register::from_code(i);
+ // Do not restore rsp, simply pop the value into the next register
+ // and overwrite this afterwards.
+ if (r.is(rsp)) {
+ ASSERT(i > 0);
+ r = Register::from_code(i - 1);
+ }
+ __ pop(r);
+ }
+
+ // Set up the roots register.
+ __ InitializeRootRegister();
+ __ InitializeSmiConstantRegister();
+
+ // Return to the continuation point.
+ __ ret(0);
+}
+
+
+void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
+ // Create a sequence of deoptimization entries.
+ Label done;
+ for (int i = 0; i < count(); i++) {
+ int start = masm()->pc_offset();
+ USE(start);
+ __ push_imm32(i);
+ __ jmp(&done);
+ ASSERT(masm()->pc_offset() - start == table_entry_size_);
+ }
+ __ bind(&done);
+}
+
+
+void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
+ SetFrameSlot(offset, value);
+}
+
+
+void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
+ SetFrameSlot(offset, value);
+}
+
+
+#undef __
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/disasm-x64.cc b/chromium/v8/src/x64/disasm-x64.cc
new file mode 100644
index 00000000000..eefa70372ec
--- /dev/null
+++ b/chromium/v8/src/x64/disasm-x64.cc
@@ -0,0 +1,1874 @@
+// 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 <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_X64
+
+#include "disasm.h"
+#include "lazy-instance.h"
+
+namespace disasm {
+
+enum OperandType {
+ UNSET_OP_ORDER = 0,
+ // Operand size decides between 16, 32 and 64 bit operands.
+ REG_OPER_OP_ORDER = 1, // Register destination, operand source.
+ OPER_REG_OP_ORDER = 2, // Operand destination, register source.
+ // Fixed 8-bit operands.
+ BYTE_SIZE_OPERAND_FLAG = 4,
+ BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG,
+ BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG
+};
+
+
+//------------------------------------------------------------------
+// Tables
+//------------------------------------------------------------------
+struct ByteMnemonic {
+ int b; // -1 terminates, otherwise must be in range (0..255)
+ OperandType op_order_;
+ const char* mnem;
+};
+
+
+static const ByteMnemonic two_operands_instr[] = {
+ { 0x00, BYTE_OPER_REG_OP_ORDER, "add" },
+ { 0x01, OPER_REG_OP_ORDER, "add" },
+ { 0x02, BYTE_REG_OPER_OP_ORDER, "add" },
+ { 0x03, REG_OPER_OP_ORDER, "add" },
+ { 0x08, BYTE_OPER_REG_OP_ORDER, "or" },
+ { 0x09, OPER_REG_OP_ORDER, "or" },
+ { 0x0A, BYTE_REG_OPER_OP_ORDER, "or" },
+ { 0x0B, REG_OPER_OP_ORDER, "or" },
+ { 0x10, BYTE_OPER_REG_OP_ORDER, "adc" },
+ { 0x11, OPER_REG_OP_ORDER, "adc" },
+ { 0x12, BYTE_REG_OPER_OP_ORDER, "adc" },
+ { 0x13, REG_OPER_OP_ORDER, "adc" },
+ { 0x18, BYTE_OPER_REG_OP_ORDER, "sbb" },
+ { 0x19, OPER_REG_OP_ORDER, "sbb" },
+ { 0x1A, BYTE_REG_OPER_OP_ORDER, "sbb" },
+ { 0x1B, REG_OPER_OP_ORDER, "sbb" },
+ { 0x20, BYTE_OPER_REG_OP_ORDER, "and" },
+ { 0x21, OPER_REG_OP_ORDER, "and" },
+ { 0x22, BYTE_REG_OPER_OP_ORDER, "and" },
+ { 0x23, REG_OPER_OP_ORDER, "and" },
+ { 0x28, BYTE_OPER_REG_OP_ORDER, "sub" },
+ { 0x29, OPER_REG_OP_ORDER, "sub" },
+ { 0x2A, BYTE_REG_OPER_OP_ORDER, "sub" },
+ { 0x2B, REG_OPER_OP_ORDER, "sub" },
+ { 0x30, BYTE_OPER_REG_OP_ORDER, "xor" },
+ { 0x31, OPER_REG_OP_ORDER, "xor" },
+ { 0x32, BYTE_REG_OPER_OP_ORDER, "xor" },
+ { 0x33, REG_OPER_OP_ORDER, "xor" },
+ { 0x38, BYTE_OPER_REG_OP_ORDER, "cmp" },
+ { 0x39, OPER_REG_OP_ORDER, "cmp" },
+ { 0x3A, BYTE_REG_OPER_OP_ORDER, "cmp" },
+ { 0x3B, REG_OPER_OP_ORDER, "cmp" },
+ { 0x63, REG_OPER_OP_ORDER, "movsxlq" },
+ { 0x84, BYTE_REG_OPER_OP_ORDER, "test" },
+ { 0x85, REG_OPER_OP_ORDER, "test" },
+ { 0x86, BYTE_REG_OPER_OP_ORDER, "xchg" },
+ { 0x87, REG_OPER_OP_ORDER, "xchg" },
+ { 0x88, BYTE_OPER_REG_OP_ORDER, "mov" },
+ { 0x89, OPER_REG_OP_ORDER, "mov" },
+ { 0x8A, BYTE_REG_OPER_OP_ORDER, "mov" },
+ { 0x8B, REG_OPER_OP_ORDER, "mov" },
+ { 0x8D, REG_OPER_OP_ORDER, "lea" },
+ { -1, UNSET_OP_ORDER, "" }
+};
+
+
+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" },
+ { 0x9C, UNSET_OP_ORDER, "pushfd" },
+ { 0x9D, UNSET_OP_ORDER, "popfd" },
+ { 0x9E, UNSET_OP_ORDER, "sahf" },
+ { 0x99, UNSET_OP_ORDER, "cdq" },
+ { 0x9B, UNSET_OP_ORDER, "fwait" },
+ { 0xA4, UNSET_OP_ORDER, "movs" },
+ { 0xA5, UNSET_OP_ORDER, "movs" },
+ { 0xA6, UNSET_OP_ORDER, "cmps" },
+ { 0xA7, UNSET_OP_ORDER, "cmps" },
+ { -1, UNSET_OP_ORDER, "" }
+};
+
+
+static const ByteMnemonic call_jump_instr[] = {
+ { 0xE8, UNSET_OP_ORDER, "call" },
+ { 0xE9, UNSET_OP_ORDER, "jmp" },
+ { -1, UNSET_OP_ORDER, "" }
+};
+
+
+static const ByteMnemonic short_immediate_instr[] = {
+ { 0x05, UNSET_OP_ORDER, "add" },
+ { 0x0D, UNSET_OP_ORDER, "or" },
+ { 0x15, UNSET_OP_ORDER, "adc" },
+ { 0x1D, UNSET_OP_ORDER, "sbb" },
+ { 0x25, UNSET_OP_ORDER, "and" },
+ { 0x2D, UNSET_OP_ORDER, "sub" },
+ { 0x35, UNSET_OP_ORDER, "xor" },
+ { 0x3D, UNSET_OP_ORDER, "cmp" },
+ { -1, UNSET_OP_ORDER, "" }
+};
+
+
+static const char* const conditional_code_suffix[] = {
+ "o", "no", "c", "nc", "z", "nz", "na", "a",
+ "s", "ns", "pe", "po", "l", "ge", "le", "g"
+};
+
+
+enum InstructionType {
+ NO_INSTR,
+ ZERO_OPERANDS_INSTR,
+ TWO_OPERANDS_INSTR,
+ JUMP_CONDITIONAL_SHORT_INSTR,
+ REGISTER_INSTR,
+ PUSHPOP_INSTR, // Has implicit 64-bit operand size.
+ MOVE_REG_INSTR,
+ CALL_JUMP_INSTR,
+ SHORT_IMMEDIATE_INSTR
+};
+
+
+enum Prefixes {
+ ESCAPE_PREFIX = 0x0F,
+ OPERAND_SIZE_OVERRIDE_PREFIX = 0x66,
+ ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67,
+ REPNE_PREFIX = 0xF2,
+ REP_PREFIX = 0xF3,
+ REPEQ_PREFIX = REP_PREFIX
+};
+
+
+struct InstructionDesc {
+ const char* mnem;
+ InstructionType type;
+ OperandType op_order_;
+ bool byte_size_operation; // Fixed 8-bit operation.
+};
+
+
+class InstructionTable {
+ public:
+ InstructionTable();
+ const InstructionDesc& Get(byte x) const {
+ return instructions_[x];
+ }
+
+ private:
+ InstructionDesc instructions_[256];
+ void Clear();
+ void Init();
+ void CopyTable(const ByteMnemonic bm[], InstructionType type);
+ void SetTableRange(InstructionType type, byte start, byte end, bool byte_size,
+ const char* mnem);
+ void AddJumpConditionalShort();
+};
+
+
+InstructionTable::InstructionTable() {
+ Clear();
+ Init();
+}
+
+
+void InstructionTable::Clear() {
+ for (int i = 0; i < 256; i++) {
+ instructions_[i].mnem = "(bad)";
+ instructions_[i].type = NO_INSTR;
+ instructions_[i].op_order_ = UNSET_OP_ORDER;
+ instructions_[i].byte_size_operation = false;
+ }
+}
+
+
+void InstructionTable::Init() {
+ CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
+ CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
+ CopyTable(call_jump_instr, CALL_JUMP_INSTR);
+ CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
+ AddJumpConditionalShort();
+ SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, false, "push");
+ SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop");
+ SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov");
+}
+
+
+void InstructionTable::CopyTable(const ByteMnemonic bm[],
+ InstructionType type) {
+ for (int i = 0; bm[i].b >= 0; i++) {
+ InstructionDesc* id = &instructions_[bm[i].b];
+ id->mnem = bm[i].mnem;
+ OperandType op_order = bm[i].op_order_;
+ id->op_order_ =
+ static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG);
+ ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
+ id->type = type;
+ id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0);
+ }
+}
+
+
+void InstructionTable::SetTableRange(InstructionType type,
+ byte start,
+ byte end,
+ bool byte_size,
+ const char* mnem) {
+ for (byte b = start; b <= end; b++) {
+ InstructionDesc* id = &instructions_[b];
+ ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
+ id->mnem = mnem;
+ id->type = type;
+ id->byte_size_operation = byte_size;
+ }
+}
+
+
+void InstructionTable::AddJumpConditionalShort() {
+ for (byte b = 0x70; b <= 0x7F; b++) {
+ InstructionDesc* id = &instructions_[b];
+ ASSERT_EQ(NO_INSTR, id->type); // Information not already entered
+ id->mnem = NULL; // Computed depending on condition code.
+ id->type = JUMP_CONDITIONAL_SHORT_INSTR;
+ }
+}
+
+
+static v8::internal::LazyInstance<InstructionTable>::type instruction_table =
+ LAZY_INSTANCE_INITIALIZER;
+
+
+static InstructionDesc cmov_instructions[16] = {
+ {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovno", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovnc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovnz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovna", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmova", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovs", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovns", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovpe", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovpo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovl", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovge", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovle", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
+ {"cmovg", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}
+};
+
+
+//------------------------------------------------------------------------------
+// DisassemblerX64 implementation.
+
+enum UnimplementedOpcodeAction {
+ CONTINUE_ON_UNIMPLEMENTED_OPCODE,
+ ABORT_ON_UNIMPLEMENTED_OPCODE
+};
+
+
+// A new DisassemblerX64 object is created to disassemble each instruction.
+// The object can only disassemble a single instruction.
+class DisassemblerX64 {
+ public:
+ DisassemblerX64(const NameConverter& converter,
+ UnimplementedOpcodeAction unimplemented_action =
+ ABORT_ON_UNIMPLEMENTED_OPCODE)
+ : converter_(converter),
+ tmp_buffer_pos_(0),
+ abort_on_unimplemented_(
+ unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE),
+ rex_(0),
+ operand_size_(0),
+ group_1_prefix_(0),
+ byte_size_operand_(false),
+ instruction_table_(instruction_table.Pointer()) {
+ tmp_buffer_[0] = '\0';
+ }
+
+ virtual ~DisassemblerX64() {
+ }
+
+ // Writes one disassembled instruction into 'buffer' (0-terminated).
+ // Returns the length of the disassembled machine instruction in bytes.
+ int InstructionDecode(v8::internal::Vector<char> buffer, byte* instruction);
+
+ private:
+ enum OperandSize {
+ BYTE_SIZE = 0,
+ WORD_SIZE = 1,
+ DOUBLEWORD_SIZE = 2,
+ QUADWORD_SIZE = 3
+ };
+
+ const NameConverter& converter_;
+ v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
+ unsigned int tmp_buffer_pos_;
+ bool abort_on_unimplemented_;
+ // Prefixes parsed
+ byte rex_;
+ byte operand_size_; // 0x66 or (if no group 3 prefix is present) 0x0.
+ byte group_1_prefix_; // 0xF2, 0xF3, or (if no group 1 prefix is present) 0.
+ // Byte size operand override.
+ bool byte_size_operand_;
+ const InstructionTable* const instruction_table_;
+
+ void setRex(byte rex) {
+ ASSERT_EQ(0x40, rex & 0xF0);
+ rex_ = rex;
+ }
+
+ bool rex() { return rex_ != 0; }
+
+ bool rex_b() { return (rex_ & 0x01) != 0; }
+
+ // Actual number of base register given the low bits and the rex.b state.
+ int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); }
+
+ bool rex_x() { return (rex_ & 0x02) != 0; }
+
+ bool rex_r() { return (rex_ & 0x04) != 0; }
+
+ bool rex_w() { return (rex_ & 0x08) != 0; }
+
+ OperandSize operand_size() {
+ if (byte_size_operand_) return BYTE_SIZE;
+ if (rex_w()) return QUADWORD_SIZE;
+ if (operand_size_ != 0) return WORD_SIZE;
+ return DOUBLEWORD_SIZE;
+ }
+
+ char operand_size_code() {
+ return "bwlq"[operand_size()];
+ }
+
+ const char* NameOfCPURegister(int reg) const {
+ return converter_.NameOfCPURegister(reg);
+ }
+
+ const char* NameOfByteCPURegister(int reg) const {
+ return converter_.NameOfByteCPURegister(reg);
+ }
+
+ const char* NameOfXMMRegister(int reg) const {
+ return converter_.NameOfXMMRegister(reg);
+ }
+
+ const char* NameOfAddress(byte* addr) const {
+ return converter_.NameOfAddress(addr);
+ }
+
+ // Disassembler helper functions.
+ void get_modrm(byte data,
+ int* mod,
+ int* regop,
+ int* rm) {
+ *mod = (data >> 6) & 3;
+ *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0);
+ *rm = (data & 7) | (rex_b() ? 8 : 0);
+ }
+
+ void get_sib(byte data,
+ int* scale,
+ int* index,
+ int* base) {
+ *scale = (data >> 6) & 3;
+ *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0);
+ *base = (data & 7) | (rex_b() ? 8 : 0);
+ }
+
+ typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const;
+
+ int PrintRightOperandHelper(byte* modrmp,
+ RegisterNameMapping register_name);
+ int PrintRightOperand(byte* modrmp);
+ int PrintRightByteOperand(byte* modrmp);
+ int PrintRightXMMOperand(byte* modrmp);
+ int PrintOperands(const char* mnem,
+ OperandType op_order,
+ byte* data);
+ int PrintImmediate(byte* data, OperandSize size);
+ int PrintImmediateOp(byte* data);
+ const char* TwoByteMnemonic(byte opcode);
+ int TwoByteOpcodeInstruction(byte* data);
+ int F6F7Instruction(byte* data);
+ int ShiftInstruction(byte* data);
+ int JumpShort(byte* data);
+ int JumpConditional(byte* data);
+ int JumpConditionalShort(byte* data);
+ int SetCC(byte* data);
+ int FPUInstruction(byte* data);
+ int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
+ int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
+ void AppendToBuffer(const char* format, ...);
+
+ void UnimplementedInstruction() {
+ if (abort_on_unimplemented_) {
+ CHECK(false);
+ } else {
+ AppendToBuffer("'Unimplemented Instruction'");
+ }
+ }
+};
+
+
+void DisassemblerX64::AppendToBuffer(const char* format, ...) {
+ v8::internal::Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
+ va_list args;
+ va_start(args, format);
+ int result = v8::internal::OS::VSNPrintF(buf, format, args);
+ va_end(args);
+ tmp_buffer_pos_ += result;
+}
+
+
+int DisassemblerX64::PrintRightOperandHelper(
+ byte* modrmp,
+ RegisterNameMapping direct_register_name) {
+ int mod, regop, rm;
+ get_modrm(*modrmp, &mod, &regop, &rm);
+ RegisterNameMapping register_name = (mod == 3) ? direct_register_name :
+ &DisassemblerX64::NameOfCPURegister;
+ switch (mod) {
+ case 0:
+ if ((rm & 7) == 5) {
+ int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1);
+ AppendToBuffer("[0x%x]", disp);
+ return 5;
+ } else if ((rm & 7) == 4) {
+ // Codes for SIB byte.
+ byte sib = *(modrmp + 1);
+ int scale, index, base;
+ get_sib(sib, &scale, &index, &base);
+ if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
+ // index == rsp means no index. Only use sib byte with no index for
+ // rsp and r12 base.
+ AppendToBuffer("[%s]", NameOfCPURegister(base));
+ return 2;
+ } else if (base == 5) {
+ // base == rbp means no base register (when mod == 0).
+ int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
+ AppendToBuffer("[%s*%d+0x%x]",
+ NameOfCPURegister(index),
+ 1 << scale, disp);
+ return 6;
+ } else if (index != 4 && base != 5) {
+ // [base+index*scale]
+ AppendToBuffer("[%s+%s*%d]",
+ NameOfCPURegister(base),
+ NameOfCPURegister(index),
+ 1 << scale);
+ return 2;
+ } else {
+ UnimplementedInstruction();
+ return 1;
+ }
+ } else {
+ AppendToBuffer("[%s]", NameOfCPURegister(rm));
+ return 1;
+ }
+ break;
+ case 1: // fall through
+ case 2:
+ if ((rm & 7) == 4) {
+ byte sib = *(modrmp + 1);
+ int scale, index, base;
+ get_sib(sib, &scale, &index, &base);
+ int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2)
+ : *reinterpret_cast<char*>(modrmp + 2);
+ if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
+ if (-disp > 0) {
+ AppendToBuffer("[%s-0x%x]", NameOfCPURegister(base), -disp);
+ } else {
+ AppendToBuffer("[%s+0x%x]", NameOfCPURegister(base), disp);
+ }
+ } else {
+ if (-disp > 0) {
+ AppendToBuffer("[%s+%s*%d-0x%x]",
+ NameOfCPURegister(base),
+ NameOfCPURegister(index),
+ 1 << scale,
+ -disp);
+ } else {
+ AppendToBuffer("[%s+%s*%d+0x%x]",
+ NameOfCPURegister(base),
+ NameOfCPURegister(index),
+ 1 << scale,
+ disp);
+ }
+ }
+ return mod == 2 ? 6 : 3;
+ } else {
+ // No sib.
+ int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1)
+ : *reinterpret_cast<char*>(modrmp + 1);
+ if (-disp > 0) {
+ AppendToBuffer("[%s-0x%x]", NameOfCPURegister(rm), -disp);
+ } else {
+ AppendToBuffer("[%s+0x%x]", NameOfCPURegister(rm), disp);
+ }
+ return (mod == 2) ? 5 : 2;
+ }
+ break;
+ case 3:
+ AppendToBuffer("%s", (this->*register_name)(rm));
+ return 1;
+ default:
+ UnimplementedInstruction();
+ return 1;
+ }
+ UNREACHABLE();
+}
+
+
+int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) {
+ int64_t value;
+ int count;
+ switch (size) {
+ case BYTE_SIZE:
+ value = *data;
+ count = 1;
+ break;
+ case WORD_SIZE:
+ value = *reinterpret_cast<int16_t*>(data);
+ count = 2;
+ break;
+ case DOUBLEWORD_SIZE:
+ value = *reinterpret_cast<uint32_t*>(data);
+ count = 4;
+ break;
+ case QUADWORD_SIZE:
+ value = *reinterpret_cast<int32_t*>(data);
+ count = 4;
+ break;
+ default:
+ UNREACHABLE();
+ value = 0; // Initialize variables on all paths to satisfy the compiler.
+ count = 0;
+ }
+ AppendToBuffer("%" V8_PTR_PREFIX "x", value);
+ return count;
+}
+
+
+int DisassemblerX64::PrintRightOperand(byte* modrmp) {
+ return PrintRightOperandHelper(modrmp,
+ &DisassemblerX64::NameOfCPURegister);
+}
+
+
+int DisassemblerX64::PrintRightByteOperand(byte* modrmp) {
+ return PrintRightOperandHelper(modrmp,
+ &DisassemblerX64::NameOfByteCPURegister);
+}
+
+
+int DisassemblerX64::PrintRightXMMOperand(byte* modrmp) {
+ return PrintRightOperandHelper(modrmp,
+ &DisassemblerX64::NameOfXMMRegister);
+}
+
+
+// Returns number of bytes used including the current *data.
+// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
+int DisassemblerX64::PrintOperands(const char* mnem,
+ OperandType op_order,
+ byte* data) {
+ byte modrm = *data;
+ int mod, regop, rm;
+ get_modrm(modrm, &mod, &regop, &rm);
+ int advance = 0;
+ const char* register_name =
+ byte_size_operand_ ? NameOfByteCPURegister(regop)
+ : NameOfCPURegister(regop);
+ switch (op_order) {
+ case REG_OPER_OP_ORDER: {
+ AppendToBuffer("%s%c %s,",
+ mnem,
+ operand_size_code(),
+ register_name);
+ advance = byte_size_operand_ ? PrintRightByteOperand(data)
+ : PrintRightOperand(data);
+ break;
+ }
+ case OPER_REG_OP_ORDER: {
+ AppendToBuffer("%s%c ", mnem, operand_size_code());
+ advance = byte_size_operand_ ? PrintRightByteOperand(data)
+ : PrintRightOperand(data);
+ AppendToBuffer(",%s", register_name);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ return advance;
+}
+
+
+// Returns number of bytes used by machine instruction, including *data byte.
+// Writes immediate instructions to 'tmp_buffer_'.
+int DisassemblerX64::PrintImmediateOp(byte* data) {
+ bool byte_size_immediate = (*data & 0x02) != 0;
+ byte modrm = *(data + 1);
+ int mod, regop, rm;
+ get_modrm(modrm, &mod, &regop, &rm);
+ const char* mnem = "Imm???";
+ switch (regop) {
+ case 0:
+ mnem = "add";
+ break;
+ case 1:
+ mnem = "or";
+ break;
+ case 2:
+ mnem = "adc";
+ break;
+ case 3:
+ mnem = "sbb";
+ break;
+ case 4:
+ mnem = "and";
+ break;
+ case 5:
+ mnem = "sub";
+ break;
+ case 6:
+ mnem = "xor";
+ break;
+ case 7:
+ mnem = "cmp";
+ break;
+ default:
+ UnimplementedInstruction();
+ }
+ AppendToBuffer("%s%c ", mnem, operand_size_code());
+ int count = PrintRightOperand(data + 1);
+ AppendToBuffer(",0x");
+ OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size();
+ count += PrintImmediate(data + 1 + count, immediate_size);
+ return 1 + count;
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerX64::F6F7Instruction(byte* data) {
+ ASSERT(*data == 0xF7 || *data == 0xF6);
+ byte modrm = *(data + 1);
+ int mod, regop, rm;
+ get_modrm(modrm, &mod, &regop, &rm);
+ if (mod == 3 && regop != 0) {
+ const char* mnem = NULL;
+ switch (regop) {
+ case 2:
+ mnem = "not";
+ break;
+ case 3:
+ mnem = "neg";
+ break;
+ case 4:
+ mnem = "mul";
+ break;
+ case 5:
+ mnem = "imul";
+ break;
+ case 7:
+ mnem = "idiv";
+ break;
+ default:
+ UnimplementedInstruction();
+ }
+ AppendToBuffer("%s%c %s",
+ mnem,
+ operand_size_code(),
+ NameOfCPURegister(rm));
+ return 2;
+ } else if (regop == 0) {
+ AppendToBuffer("test%c ", operand_size_code());
+ int count = PrintRightOperand(data + 1); // Use name of 64-bit register.
+ AppendToBuffer(",0x");
+ count += PrintImmediate(data + 1 + count, operand_size());
+ return 1 + count;
+ } else {
+ UnimplementedInstruction();
+ return 2;
+ }
+}
+
+
+int DisassemblerX64::ShiftInstruction(byte* data) {
+ byte op = *data & (~1);
+ if (op != 0xD0 && op != 0xD2 && op != 0xC0) {
+ UnimplementedInstruction();
+ return 1;
+ }
+ byte modrm = *(data + 1);
+ int mod, regop, rm;
+ get_modrm(modrm, &mod, &regop, &rm);
+ regop &= 0x7; // The REX.R bit does not affect the operation.
+ int imm8 = -1;
+ int num_bytes = 2;
+ if (mod != 3) {
+ UnimplementedInstruction();
+ return num_bytes;
+ }
+ const char* mnem = NULL;
+ switch (regop) {
+ case 0:
+ mnem = "rol";
+ break;
+ case 1:
+ mnem = "ror";
+ break;
+ case 2:
+ mnem = "rcl";
+ break;
+ case 3:
+ mnem = "rcr";
+ break;
+ case 4:
+ mnem = "shl";
+ break;
+ case 5:
+ mnem = "shr";
+ break;
+ case 7:
+ mnem = "sar";
+ break;
+ default:
+ UnimplementedInstruction();
+ return num_bytes;
+ }
+ ASSERT_NE(NULL, mnem);
+ if (op == 0xD0) {
+ imm8 = 1;
+ } else if (op == 0xC0) {
+ imm8 = *(data + 2);
+ num_bytes = 3;
+ }
+ AppendToBuffer("%s%c %s,",
+ mnem,
+ operand_size_code(),
+ byte_size_operand_ ? NameOfByteCPURegister(rm)
+ : NameOfCPURegister(rm));
+ if (op == 0xD2) {
+ AppendToBuffer("cl");
+ } else {
+ AppendToBuffer("%d", imm8);
+ }
+ return num_bytes;
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerX64::JumpShort(byte* data) {
+ ASSERT_EQ(0xEB, *data);
+ byte b = *(data + 1);
+ byte* dest = data + static_cast<int8_t>(b) + 2;
+ AppendToBuffer("jmp %s", NameOfAddress(dest));
+ return 2;
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerX64::JumpConditional(byte* data) {
+ ASSERT_EQ(0x0F, *data);
+ byte cond = *(data + 1) & 0x0F;
+ byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
+ const char* mnem = conditional_code_suffix[cond];
+ AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
+ return 6; // includes 0x0F
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerX64::JumpConditionalShort(byte* data) {
+ byte cond = *data & 0x0F;
+ byte b = *(data + 1);
+ byte* dest = data + static_cast<int8_t>(b) + 2;
+ const char* mnem = conditional_code_suffix[cond];
+ AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
+ return 2;
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerX64::SetCC(byte* data) {
+ ASSERT_EQ(0x0F, *data);
+ byte cond = *(data + 1) & 0x0F;
+ const char* mnem = conditional_code_suffix[cond];
+ AppendToBuffer("set%s%c ", mnem, operand_size_code());
+ PrintRightByteOperand(data + 2);
+ return 3; // includes 0x0F
+}
+
+
+// Returns number of bytes used, including *data.
+int DisassemblerX64::FPUInstruction(byte* data) {
+ byte escape_opcode = *data;
+ ASSERT_EQ(0xD8, escape_opcode & 0xF8);
+ byte modrm_byte = *(data+1);
+
+ if (modrm_byte >= 0xC0) {
+ return RegisterFPUInstruction(escape_opcode, modrm_byte);
+ } else {
+ return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1);
+ }
+}
+
+int DisassemblerX64::MemoryFPUInstruction(int escape_opcode,
+ int modrm_byte,
+ byte* modrm_start) {
+ const char* mnem = "?";
+ int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte.
+ switch (escape_opcode) {
+ case 0xD9: switch (regop) {
+ case 0: mnem = "fld_s"; break;
+ case 3: mnem = "fstp_s"; break;
+ case 7: mnem = "fstcw"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDB: switch (regop) {
+ case 0: mnem = "fild_s"; break;
+ case 1: mnem = "fisttp_s"; break;
+ case 2: mnem = "fist_s"; break;
+ case 3: mnem = "fistp_s"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDD: switch (regop) {
+ case 0: mnem = "fld_d"; break;
+ case 3: mnem = "fstp_d"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDF: switch (regop) {
+ case 5: mnem = "fild_d"; break;
+ case 7: mnem = "fistp_d"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ default: UnimplementedInstruction();
+ }
+ AppendToBuffer("%s ", mnem);
+ int count = PrintRightOperand(modrm_start);
+ return count + 1;
+}
+
+int DisassemblerX64::RegisterFPUInstruction(int escape_opcode,
+ byte modrm_byte) {
+ bool has_register = false; // Is the FPU register encoded in modrm_byte?
+ const char* mnem = "?";
+
+ switch (escape_opcode) {
+ case 0xD8:
+ UnimplementedInstruction();
+ break;
+
+ case 0xD9:
+ switch (modrm_byte & 0xF8) {
+ case 0xC0:
+ mnem = "fld";
+ has_register = true;
+ break;
+ case 0xC8:
+ mnem = "fxch";
+ has_register = true;
+ break;
+ default:
+ 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();
+ }
+ }
+ break;
+
+ case 0xDA:
+ if (modrm_byte == 0xE9) {
+ mnem = "fucompp";
+ } else {
+ UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDB:
+ if ((modrm_byte & 0xF8) == 0xE8) {
+ mnem = "fucomi";
+ has_register = true;
+ } else if (modrm_byte == 0xE2) {
+ mnem = "fclex";
+ } else {
+ UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDC:
+ has_register = true;
+ switch (modrm_byte & 0xF8) {
+ case 0xC0: mnem = "fadd"; break;
+ case 0xE8: mnem = "fsub"; break;
+ case 0xC8: mnem = "fmul"; break;
+ case 0xF8: mnem = "fdiv"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDD:
+ has_register = true;
+ switch (modrm_byte & 0xF8) {
+ case 0xC0: mnem = "ffree"; break;
+ case 0xD8: mnem = "fstp"; break;
+ default: UnimplementedInstruction();
+ }
+ break;
+
+ case 0xDE:
+ if (modrm_byte == 0xD9) {
+ mnem = "fcompp";
+ } else {
+ has_register = true;
+ switch (modrm_byte & 0xF8) {
+ case 0xC0: mnem = "faddp"; break;
+ case 0xE8: mnem = "fsubp"; break;
+ case 0xC8: mnem = "fmulp"; break;
+ case 0xF8: mnem = "fdivp"; break;
+ default: UnimplementedInstruction();
+ }
+ }
+ break;
+
+ case 0xDF:
+ if (modrm_byte == 0xE0) {
+ mnem = "fnstsw_ax";
+ } else if ((modrm_byte & 0xF8) == 0xE8) {
+ mnem = "fucomip";
+ has_register = true;
+ }
+ break;
+
+ default: UnimplementedInstruction();
+ }
+
+ if (has_register) {
+ AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7);
+ } else {
+ AppendToBuffer("%s", mnem);
+ }
+ return 2;
+}
+
+
+
+// Handle all two-byte opcodes, which start with 0x0F.
+// These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix.
+// We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A.
+int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
+ byte opcode = *(data + 1);
+ byte* current = data + 2;
+ // At return, "current" points to the start of the next instruction.
+ const char* mnemonic = TwoByteMnemonic(opcode);
+ if (operand_size_ == 0x66) {
+ // 0x66 0x0F prefix.
+ int mod, regop, rm;
+ if (opcode == 0x3A) {
+ byte third_byte = *current;
+ current = data + 3;
+ if (third_byte == 0x17) {
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("extractps "); // reg/m32, xmm, imm8
+ current += PrintRightOperand(current);
+ AppendToBuffer(", %s, %d", NameOfCPURegister(regop), (*current) & 3);
+ current += 1;
+ } else if (third_byte == 0x0b) {
+ get_modrm(*current, &mod, &regop, &rm);
+ // roundsd xmm, xmm/m64, imm8
+ AppendToBuffer("roundsd %s, ", NameOfCPURegister(regop));
+ current += PrintRightOperand(current);
+ AppendToBuffer(", %d", (*current) & 3);
+ current += 1;
+ } else {
+ UnimplementedInstruction();
+ }
+ } else {
+ get_modrm(*current, &mod, &regop, &rm);
+ 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) {
+ AppendToBuffer("movapd ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+ } else if (opcode == 0x6E) {
+ AppendToBuffer("mov%c %s,",
+ rex_w() ? 'q' : 'd',
+ NameOfXMMRegister(regop));
+ current += PrintRightOperand(current);
+ } else if (opcode == 0x6F) {
+ AppendToBuffer("movdqa %s,",
+ NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x7E) {
+ AppendToBuffer("mov%c ",
+ rex_w() ? 'q' : 'd');
+ current += PrintRightOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+ } else if (opcode == 0x7F) {
+ AppendToBuffer("movdqa ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+ } else if (opcode == 0xD6) {
+ AppendToBuffer("movq ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+ } else if (opcode == 0x50) {
+ AppendToBuffer("movmskpd %s,", NameOfCPURegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else {
+ const char* mnemonic = "?";
+ if (opcode == 0x54) {
+ mnemonic = "andpd";
+ } else if (opcode == 0x56) {
+ mnemonic = "orpd";
+ } else if (opcode == 0x57) {
+ mnemonic = "xorpd";
+ } else if (opcode == 0x2E) {
+ mnemonic = "ucomisd";
+ } else if (opcode == 0x2F) {
+ mnemonic = "comisd";
+ } else {
+ UnimplementedInstruction();
+ }
+ AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ }
+ }
+ } else if (group_1_prefix_ == 0xF2) {
+ // Beginning of instructions with prefix 0xF2.
+
+ if (opcode == 0x11 || opcode == 0x10) {
+ // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
+ AppendToBuffer("movsd ");
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ if (opcode == 0x11) {
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else {
+ AppendToBuffer("%s,", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ }
+ } else if (opcode == 0x2A) {
+ // CVTSI2SD: integer to XMM double conversion.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("%sd %s,", mnemonic, NameOfXMMRegister(regop));
+ current += PrintRightOperand(current);
+ } else if (opcode == 0x2C) {
+ // CVTTSD2SI:
+ // Convert with truncation scalar double-precision FP to integer.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("cvttsd2si%c %s,",
+ operand_size_code(), NameOfCPURegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x2D) {
+ // CVTSD2SI: Convert scalar double-precision FP to integer.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("cvtsd2si%c %s,",
+ operand_size_code(), NameOfCPURegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if ((opcode & 0xF8) == 0x58 || opcode == 0x51) {
+ // XMM arithmetic. Mnemonic was retrieved at the start of this function.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (group_1_prefix_ == 0xF3) {
+ // Instructions with prefix 0xF3.
+ if (opcode == 0x11 || opcode == 0x10) {
+ // MOVSS: Move scalar double-precision fp to/from/between XMM registers.
+ AppendToBuffer("movss ");
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ if (opcode == 0x11) {
+ current += PrintRightOperand(current);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else {
+ AppendToBuffer("%s,", NameOfXMMRegister(regop));
+ current += PrintRightOperand(current);
+ }
+ } else if (opcode == 0x2A) {
+ // CVTSI2SS: integer to XMM single conversion.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("%ss %s,", mnemonic, NameOfXMMRegister(regop));
+ current += PrintRightOperand(current);
+ } else if (opcode == 0x2C) {
+ // CVTTSS2SI:
+ // Convert with truncation scalar single-precision FP to dword integer.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("cvttss2si%c %s,",
+ operand_size_code(), NameOfCPURegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x5A) {
+ // CVTSS2SD:
+ // Convert scalar single-precision FP to scalar double-precision FP.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x7E) {
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("movq %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (opcode == 0x1F) {
+ // NOP
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ 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) {
+ // movaps xmm, xmm/m128
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("movaps %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+
+ } else if (opcode == 0x29) {
+ // movaps xmm/m128, xmm
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("movaps ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+
+ } else if (opcode == 0xA2 || opcode == 0x31) {
+ // RDTSC or CPUID
+ AppendToBuffer("%s", mnemonic);
+
+ } else if ((opcode & 0xF0) == 0x40) {
+ // CMOVcc: conditional move.
+ int condition = opcode & 0x0F;
+ const InstructionDesc& idesc = cmov_instructions[condition];
+ byte_size_operand_ = idesc.byte_size_operation;
+ current += PrintOperands(idesc.mnem, idesc.op_order_, current);
+
+ } else if (opcode == 0x57) {
+ // xorps xmm, xmm/m128
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("xorps %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+
+ } else if (opcode == 0x50) {
+ // movmskps reg, xmm
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("movmskps %s, ", NameOfCPURegister(regop));
+ current += PrintRightXMMOperand(current);
+
+ } else if ((opcode & 0xF0) == 0x80) {
+ // Jcc: Conditional jump (branch).
+ current = data + JumpConditional(data);
+
+ } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 ||
+ opcode == 0xB7 || opcode == 0xAF) {
+ // Size-extending moves, IMUL.
+ current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current);
+
+ } else if ((opcode & 0xF0) == 0x90) {
+ // SETcc: Set byte on condition. Needs pointer to beginning of instruction.
+ current = data + SetCC(data);
+
+ } else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) {
+ // SHLD, SHRD (double-precision shift), BTS (bit set).
+ AppendToBuffer("%s ", mnemonic);
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ current += PrintRightOperand(current);
+ if (opcode == 0xAB) {
+ AppendToBuffer(",%s", NameOfCPURegister(regop));
+ } else {
+ AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
+ }
+ } else {
+ UnimplementedInstruction();
+ }
+ return static_cast<int>(current - data);
+}
+
+
+// Mnemonics for two-byte opcode instructions starting with 0x0F.
+// The argument is the second byte of the two-byte opcode.
+// Returns NULL if the instruction is not handled here.
+const char* DisassemblerX64::TwoByteMnemonic(byte opcode) {
+ switch (opcode) {
+ case 0x1F:
+ return "nop";
+ case 0x2A: // F2/F3 prefix.
+ return "cvtsi2s";
+ case 0x31:
+ return "rdtsc";
+ case 0x51: // F2 prefix.
+ return "sqrtsd";
+ case 0x58: // F2 prefix.
+ return "addsd";
+ case 0x59: // F2 prefix.
+ return "mulsd";
+ case 0x5C: // F2 prefix.
+ return "subsd";
+ case 0x5E: // F2 prefix.
+ return "divsd";
+ case 0xA2:
+ return "cpuid";
+ case 0xA5:
+ return "shld";
+ case 0xAB:
+ return "bts";
+ case 0xAD:
+ return "shrd";
+ case 0xAF:
+ return "imul";
+ case 0xB6:
+ return "movzxb";
+ case 0xB7:
+ return "movzxw";
+ case 0xBE:
+ return "movsxb";
+ case 0xBF:
+ return "movsxw";
+ default:
+ return NULL;
+ }
+}
+
+
+// Disassembles the instruction at instr, and writes it into out_buffer.
+int DisassemblerX64::InstructionDecode(v8::internal::Vector<char> out_buffer,
+ byte* instr) {
+ tmp_buffer_pos_ = 0; // starting to write as position 0
+ byte* data = instr;
+ bool processed = true; // Will be set to false if the current instruction
+ // is not in 'instructions' table.
+ byte current;
+
+ // Scan for prefixes.
+ while (true) {
+ current = *data;
+ if (current == OPERAND_SIZE_OVERRIDE_PREFIX) { // Group 3 prefix.
+ operand_size_ = current;
+ } else if ((current & 0xF0) == 0x40) { // REX prefix.
+ setRex(current);
+ if (rex_w()) AppendToBuffer("REX.W ");
+ } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3).
+ group_1_prefix_ = current;
+ } else { // Not a prefix - an opcode.
+ break;
+ }
+ data++;
+ }
+
+ const InstructionDesc& idesc = instruction_table_->Get(current);
+ byte_size_operand_ = idesc.byte_size_operation;
+ switch (idesc.type) {
+ case ZERO_OPERANDS_INSTR:
+ if (current >= 0xA4 && current <= 0xA7) {
+ // String move or compare operations.
+ if (group_1_prefix_ == REP_PREFIX) {
+ // REP.
+ AppendToBuffer("rep ");
+ }
+ if (rex_w()) AppendToBuffer("REX.W ");
+ AppendToBuffer("%s%c", idesc.mnem, operand_size_code());
+ } else {
+ AppendToBuffer("%s", idesc.mnem, operand_size_code());
+ }
+ data++;
+ break;
+
+ case TWO_OPERANDS_INSTR:
+ data++;
+ data += PrintOperands(idesc.mnem, idesc.op_order_, data);
+ break;
+
+ case JUMP_CONDITIONAL_SHORT_INSTR:
+ data += JumpConditionalShort(data);
+ break;
+
+ case REGISTER_INSTR:
+ AppendToBuffer("%s%c %s",
+ idesc.mnem,
+ operand_size_code(),
+ NameOfCPURegister(base_reg(current & 0x07)));
+ data++;
+ break;
+ case PUSHPOP_INSTR:
+ AppendToBuffer("%s %s",
+ idesc.mnem,
+ NameOfCPURegister(base_reg(current & 0x07)));
+ data++;
+ break;
+ case MOVE_REG_INSTR: {
+ byte* addr = NULL;
+ switch (operand_size()) {
+ case WORD_SIZE:
+ addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1));
+ data += 3;
+ break;
+ case DOUBLEWORD_SIZE:
+ addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
+ data += 5;
+ break;
+ case QUADWORD_SIZE:
+ addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1));
+ data += 9;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ AppendToBuffer("mov%c %s,%s",
+ operand_size_code(),
+ NameOfCPURegister(base_reg(current & 0x07)),
+ NameOfAddress(addr));
+ break;
+ }
+
+ case CALL_JUMP_INSTR: {
+ byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5;
+ AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
+ data += 5;
+ break;
+ }
+
+ case SHORT_IMMEDIATE_INSTR: {
+ byte* addr =
+ reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
+ AppendToBuffer("%s rax, %s", idesc.mnem, NameOfAddress(addr));
+ data += 5;
+ break;
+ }
+
+ case NO_INSTR:
+ processed = false;
+ break;
+
+ default:
+ UNIMPLEMENTED(); // This type is not implemented.
+ }
+
+ // The first byte didn't match any of the simple opcodes, so we
+ // need to do special processing on it.
+ if (!processed) {
+ switch (*data) {
+ case 0xC2:
+ AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data + 1));
+ data += 3;
+ break;
+
+ case 0x69: // fall through
+ case 0x6B: {
+ int mod, regop, rm;
+ get_modrm(*(data + 1), &mod, &regop, &rm);
+ int32_t imm = *data == 0x6B ? *(data + 2)
+ : *reinterpret_cast<int32_t*>(data + 2);
+ AppendToBuffer("imul%c %s,%s,0x%x",
+ operand_size_code(),
+ NameOfCPURegister(regop),
+ NameOfCPURegister(rm), imm);
+ data += 2 + (*data == 0x6B ? 1 : 4);
+ break;
+ }
+
+ case 0x81: // fall through
+ case 0x83: // 0x81 with sign extension bit set
+ data += PrintImmediateOp(data);
+ break;
+
+ case 0x0F:
+ data += TwoByteOpcodeInstruction(data);
+ break;
+
+ case 0x8F: {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (regop == 0) {
+ AppendToBuffer("pop ");
+ data += PrintRightOperand(data);
+ }
+ }
+ break;
+
+ case 0xFF: {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ const char* mnem = NULL;
+ switch (regop) {
+ case 0:
+ mnem = "inc";
+ break;
+ case 1:
+ mnem = "dec";
+ break;
+ case 2:
+ mnem = "call";
+ break;
+ case 4:
+ mnem = "jmp";
+ break;
+ case 6:
+ mnem = "push";
+ break;
+ default:
+ mnem = "???";
+ }
+ AppendToBuffer(((regop <= 1) ? "%s%c " : "%s "),
+ mnem,
+ operand_size_code());
+ data += PrintRightOperand(data);
+ }
+ break;
+
+ case 0xC7: // imm32, fall through
+ case 0xC6: // imm8
+ {
+ bool is_byte = *data == 0xC6;
+ data++;
+ if (is_byte) {
+ AppendToBuffer("movb ");
+ data += PrintRightByteOperand(data);
+ int32_t imm = *data;
+ AppendToBuffer(",0x%x", imm);
+ data++;
+ } else {
+ AppendToBuffer("mov%c ", operand_size_code());
+ data += PrintRightOperand(data);
+ int32_t imm = *reinterpret_cast<int32_t*>(data);
+ AppendToBuffer(",0x%x", imm);
+ data += 4;
+ }
+ }
+ break;
+
+ case 0x80: {
+ data++;
+ AppendToBuffer("cmpb ");
+ data += PrintRightByteOperand(data);
+ int32_t imm = *data;
+ AppendToBuffer(",0x%x", imm);
+ data++;
+ }
+ break;
+
+ case 0x88: // 8bit, fall through
+ case 0x89: // 32bit
+ {
+ bool is_byte = *data == 0x88;
+ int mod, regop, rm;
+ data++;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (is_byte) {
+ AppendToBuffer("movb ");
+ data += PrintRightByteOperand(data);
+ AppendToBuffer(",%s", NameOfByteCPURegister(regop));
+ } else {
+ AppendToBuffer("mov%c ", operand_size_code());
+ data += PrintRightOperand(data);
+ AppendToBuffer(",%s", NameOfCPURegister(regop));
+ }
+ }
+ break;
+
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97: {
+ int reg = (*data & 0x7) | (rex_b() ? 8 : 0);
+ if (reg == 0) {
+ AppendToBuffer("nop"); // Common name for xchg rax,rax.
+ } else {
+ AppendToBuffer("xchg%c rax, %s",
+ operand_size_code(),
+ NameOfCPURegister(reg));
+ }
+ data++;
+ }
+ break;
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF: {
+ // mov reg8,imm8 or mov reg32,imm32
+ byte opcode = *data;
+ data++;
+ bool is_32bit = (opcode >= 0xB8);
+ int reg = (opcode & 0x7) | (rex_b() ? 8 : 0);
+ if (is_32bit) {
+ AppendToBuffer("mov%c %s, ",
+ operand_size_code(),
+ NameOfCPURegister(reg));
+ data += PrintImmediate(data, DOUBLEWORD_SIZE);
+ } else {
+ AppendToBuffer("movb %s, ",
+ NameOfByteCPURegister(reg));
+ data += PrintImmediate(data, BYTE_SIZE);
+ }
+ break;
+ }
+ case 0xFE: {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ if (regop == 1) {
+ AppendToBuffer("decb ");
+ data += PrintRightByteOperand(data);
+ } else {
+ UnimplementedInstruction();
+ }
+ break;
+ }
+ case 0x68:
+ AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1));
+ data += 5;
+ break;
+
+ case 0x6A:
+ AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
+ data += 2;
+ break;
+
+ case 0xA1: // Fall through.
+ case 0xA3:
+ switch (operand_size()) {
+ case DOUBLEWORD_SIZE: {
+ const char* memory_location = NameOfAddress(
+ reinterpret_cast<byte*>(
+ *reinterpret_cast<int32_t*>(data + 1)));
+ if (*data == 0xA1) { // Opcode 0xA1
+ AppendToBuffer("movzxlq rax,(%s)", memory_location);
+ } else { // Opcode 0xA3
+ AppendToBuffer("movzxlq (%s),rax", memory_location);
+ }
+ data += 5;
+ break;
+ }
+ case QUADWORD_SIZE: {
+ // New x64 instruction mov rax,(imm_64).
+ const char* memory_location = NameOfAddress(
+ *reinterpret_cast<byte**>(data + 1));
+ if (*data == 0xA1) { // Opcode 0xA1
+ AppendToBuffer("movq rax,(%s)", memory_location);
+ } else { // Opcode 0xA3
+ AppendToBuffer("movq (%s),rax", memory_location);
+ }
+ data += 9;
+ break;
+ }
+ default:
+ UnimplementedInstruction();
+ data += 2;
+ }
+ break;
+
+ case 0xA8:
+ AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data + 1));
+ data += 2;
+ break;
+
+ case 0xA9: {
+ int64_t value = 0;
+ switch (operand_size()) {
+ case WORD_SIZE:
+ value = *reinterpret_cast<uint16_t*>(data + 1);
+ data += 3;
+ break;
+ case DOUBLEWORD_SIZE:
+ value = *reinterpret_cast<uint32_t*>(data + 1);
+ data += 5;
+ break;
+ case QUADWORD_SIZE:
+ value = *reinterpret_cast<int32_t*>(data + 1);
+ data += 5;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ AppendToBuffer("test%c rax,0x%" V8_PTR_PREFIX "x",
+ operand_size_code(),
+ value);
+ break;
+ }
+ case 0xD1: // fall through
+ case 0xD3: // fall through
+ case 0xC1:
+ data += ShiftInstruction(data);
+ break;
+ case 0xD0: // fall through
+ case 0xD2: // fall through
+ case 0xC0:
+ byte_size_operand_ = true;
+ data += ShiftInstruction(data);
+ break;
+
+ case 0xD9: // fall through
+ case 0xDA: // fall through
+ case 0xDB: // fall through
+ case 0xDC: // fall through
+ case 0xDD: // fall through
+ case 0xDE: // fall through
+ case 0xDF:
+ data += FPUInstruction(data);
+ break;
+
+ case 0xEB:
+ data += JumpShort(data);
+ break;
+
+ case 0xF6:
+ byte_size_operand_ = true; // fall through
+ case 0xF7:
+ data += F6F7Instruction(data);
+ break;
+
+ case 0x3C:
+ AppendToBuffer("cmp al, 0x%x", *reinterpret_cast<int8_t*>(data + 1));
+ data +=2;
+ break;
+
+ default:
+ UnimplementedInstruction();
+ data += 1;
+ }
+ } // !processed
+
+ if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
+ tmp_buffer_[tmp_buffer_pos_] = '\0';
+ }
+
+ int instr_len = static_cast<int>(data - instr);
+ ASSERT(instr_len > 0); // Ensure progress.
+
+ int outp = 0;
+ // Instruction bytes.
+ for (byte* bp = instr; bp < data; bp++) {
+ outp += v8::internal::OS::SNPrintF(out_buffer + outp, "%02x", *bp);
+ }
+ for (int i = 6 - instr_len; i >= 0; i--) {
+ outp += v8::internal::OS::SNPrintF(out_buffer + outp, " ");
+ }
+
+ outp += v8::internal::OS::SNPrintF(out_buffer + outp, " %s",
+ tmp_buffer_.start());
+ return instr_len;
+}
+
+
+//------------------------------------------------------------------------------
+
+
+static const char* cpu_regs[16] = {
+ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+};
+
+
+static const char* byte_cpu_regs[16] = {
+ "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
+ "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
+};
+
+
+static const char* xmm_regs[16] = {
+ "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
+ "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
+};
+
+
+const char* NameConverter::NameOfAddress(byte* addr) const {
+ v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
+ return tmp_buffer_.start();
+}
+
+
+const char* NameConverter::NameOfConstant(byte* addr) const {
+ return NameOfAddress(addr);
+}
+
+
+const char* NameConverter::NameOfCPURegister(int reg) const {
+ if (0 <= reg && reg < 16)
+ return cpu_regs[reg];
+ return "noreg";
+}
+
+
+const char* NameConverter::NameOfByteCPURegister(int reg) const {
+ if (0 <= reg && reg < 16)
+ return byte_cpu_regs[reg];
+ return "noreg";
+}
+
+
+const char* NameConverter::NameOfXMMRegister(int reg) const {
+ if (0 <= reg && reg < 16)
+ return xmm_regs[reg];
+ return "noxmmreg";
+}
+
+
+const char* NameConverter::NameInCode(byte* addr) const {
+ // X64 does not embed debug strings at the moment.
+ UNREACHABLE();
+ return "";
+}
+
+
+//------------------------------------------------------------------------------
+
+Disassembler::Disassembler(const NameConverter& converter)
+ : converter_(converter) { }
+
+Disassembler::~Disassembler() { }
+
+
+int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
+ byte* instruction) {
+ DisassemblerX64 d(converter_, CONTINUE_ON_UNIMPLEMENTED_OPCODE);
+ return d.InstructionDecode(buffer, instruction);
+}
+
+
+// The X64 assembler does not use constant pools.
+int Disassembler::ConstantPoolSizeAt(byte* instruction) {
+ return -1;
+}
+
+
+void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
+ NameConverter converter;
+ Disassembler d(converter);
+ for (byte* pc = begin; pc < end;) {
+ v8::internal::EmbeddedVector<char, 128> buffer;
+ buffer[0] = '\0';
+ byte* prev_pc = pc;
+ pc += d.InstructionDecode(buffer, pc);
+ fprintf(f, "%p", prev_pc);
+ fprintf(f, " ");
+
+ for (byte* bp = prev_pc; bp < pc; bp++) {
+ fprintf(f, "%02x", *bp);
+ }
+ for (int i = 6 - static_cast<int>(pc - prev_pc); i >= 0; i--) {
+ fprintf(f, " ");
+ }
+ fprintf(f, " %s\n", buffer.start());
+ }
+}
+
+} // namespace disasm
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/frames-x64.cc b/chromium/v8/src/x64/frames-x64.cc
new file mode 100644
index 00000000000..5cc27a6e12b
--- /dev/null
+++ b/chromium/v8/src/x64/frames-x64.cc
@@ -0,0 +1,51 @@
+// 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.
+
+#include "v8.h"
+
+#if V8_TARGET_ARCH_X64
+
+#include "assembler.h"
+#include "assembler-x64.h"
+#include "assembler-x64-inl.h"
+#include "frames.h"
+
+namespace v8 {
+namespace internal {
+
+
+Register JavaScriptFrame::fp_register() { return rbp; }
+Register JavaScriptFrame::context_register() { return rsi; }
+
+
+Register StubFailureTrampolineFrame::fp_register() { return rbp; }
+Register StubFailureTrampolineFrame::context_register() { return rsi; }
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/frames-x64.h b/chromium/v8/src/x64/frames-x64.h
new file mode 100644
index 00000000000..2af5a81bb5f
--- /dev/null
+++ b/chromium/v8/src/x64/frames-x64.h
@@ -0,0 +1,137 @@
+// Copyright 2012 the V8 project authors. 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_X64_FRAMES_X64_H_
+#define V8_X64_FRAMES_X64_H_
+
+namespace v8 {
+namespace internal {
+
+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
+
+const int kNumJSCallerSaved = 5;
+
+typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
+
+// Number of registers for which space is reserved in safepoints.
+const int kNumSafepointRegisters = 16;
+
+// ----------------------------------------------------
+
+class EntryFrameConstants : public AllStatic {
+ public:
+#ifdef _WIN64
+ static const int kCalleeSaveXMMRegisters = 10;
+ static const int kXMMRegisterSize = 16;
+ static const int kXMMRegistersBlockSize =
+ kXMMRegisterSize * kCalleeSaveXMMRegisters;
+ static const int kCallerFPOffset =
+ -10 * kPointerSize - kXMMRegistersBlockSize;
+#else
+ static const int kCallerFPOffset = -8 * kPointerSize;
+#endif
+ static const int kArgvOffset = 6 * kPointerSize;
+};
+
+
+class ExitFrameConstants : public AllStatic {
+ public:
+ static const int kCodeOffset = -2 * kPointerSize;
+ static const int kSPOffset = -1 * kPointerSize;
+
+ static const int kCallerFPOffset = +0 * kPointerSize;
+ static const int kCallerPCOffset = +1 * kPointerSize;
+
+ // FP-relative displacement of the caller's SP. It points just
+ // below the saved PC.
+ static const int kCallerSPDisplacement = +2 * kPointerSize;
+};
+
+
+class JavaScriptFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
+ static const int kLastParameterOffset = +2 * kPointerSize;
+ static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
+
+ // Caller SP-relative.
+ static const int kParam0Offset = -2 * kPointerSize;
+ static const int kReceiverOffset = -1 * kPointerSize;
+};
+
+
+class ArgumentsAdaptorFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
+};
+
+
+class ConstructFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kImplicitReceiverOffset = -5 * kPointerSize;
+ static const int kConstructorOffset = kMinInt;
+ static const int kLengthOffset = -4 * kPointerSize;
+ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
+
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + 3 * kPointerSize;
+};
+
+
+class InternalFrameConstants : public AllStatic {
+ public:
+ // FP-relative.
+ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
+};
+
+
+inline Object* JavaScriptFrame::function_slot_object() const {
+ const int offset = JavaScriptFrameConstants::kFunctionOffset;
+ return Memory::Object_at(fp() + offset);
+}
+
+
+inline void StackHandler::SetFp(Address slot, Address fp) {
+ Memory::Address_at(slot) = fp;
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_X64_FRAMES_X64_H_
diff --git a/chromium/v8/src/x64/full-codegen-x64.cc b/chromium/v8/src/x64/full-codegen-x64.cc
new file mode 100644
index 00000000000..6333e87bea1
--- /dev/null
+++ b/chromium/v8/src/x64/full-codegen-x64.cc
@@ -0,0 +1,4891 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "code-stubs.h"
+#include "codegen.h"
+#include "compiler.h"
+#include "debug.h"
+#include "full-codegen.h"
+#include "isolate-inl.h"
+#include "parser.h"
+#include "scopes.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm_)
+
+
+class JumpPatchSite BASE_EMBEDDED {
+ public:
+ explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) {
+#ifdef DEBUG
+ info_emitted_ = false;
+#endif
+ }
+
+ ~JumpPatchSite() {
+ ASSERT(patch_site_.is_bound() == info_emitted_);
+ }
+
+ void EmitJumpIfNotSmi(Register reg,
+ Label* target,
+ Label::Distance near_jump = Label::kFar) {
+ __ testb(reg, Immediate(kSmiTagMask));
+ EmitJump(not_carry, target, near_jump); // Always taken before patched.
+ }
+
+ void EmitJumpIfSmi(Register reg,
+ Label* target,
+ Label::Distance near_jump = Label::kFar) {
+ __ testb(reg, Immediate(kSmiTagMask));
+ EmitJump(carry, target, near_jump); // Never taken before patched.
+ }
+
+ void EmitPatchInfo() {
+ if (patch_site_.is_bound()) {
+ int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site_);
+ ASSERT(is_int8(delta_to_patch_site));
+ __ testl(rax, Immediate(delta_to_patch_site));
+#ifdef DEBUG
+ info_emitted_ = true;
+#endif
+ } else {
+ __ nop(); // Signals no inlined code.
+ }
+ }
+
+ private:
+ // jc will be patched with jz, jnc will become jnz.
+ void EmitJump(Condition cc, Label* target, Label::Distance near_jump) {
+ ASSERT(!patch_site_.is_bound() && !info_emitted_);
+ ASSERT(cc == carry || cc == not_carry);
+ __ bind(&patch_site_);
+ __ j(cc, target, near_jump);
+ }
+
+ MacroAssembler* masm_;
+ Label patch_site_;
+#ifdef DEBUG
+ bool info_emitted_;
+#endif
+};
+
+
+// Generate code for a JS function. On entry to the function the receiver
+// and arguments have been pushed on the stack left to right, with the
+// return address on top of them. The actual argument count matches the
+// formal parameter count expected by the function.
+//
+// The live registers are:
+// 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)
+//
+// The function builds a JS frame. Please see JavaScriptFrameConstants in
+// frames-x64.h for its layout.
+void FullCodeGenerator::Generate() {
+ CompilationInfo* info = info_;
+ handler_table_ =
+ isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
+ profiling_counter_ = isolate()->factory()->NewCell(
+ Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget), isolate()));
+ SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
+
+ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info->function()->name()->IsUtf8EqualTo(CStrVector(FLAG_stop_at))) {
+ __ int3();
+ }
+#endif
+
+ // 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_classic_mode() || info->is_native()) {
+ Label ok;
+ __ testq(rcx, rcx);
+ __ j(zero, &ok, Label::kNear);
+ // +1 for return address.
+ int receiver_offset = (info->scope()->num_parameters() + 1) * kPointerSize;
+ __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
+ __ movq(Operand(rsp, receiver_offset), kScratchRegister);
+ __ 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);
+
+ info->set_prologue_offset(masm_->pc_offset());
+ __ push(rbp); // Caller's frame pointer.
+ __ movq(rbp, rsp);
+ __ push(rsi); // Callee's context.
+ __ push(rdi); // Callee's JS Function.
+ info->AddNoFrameRange(0, masm_->pc_offset());
+
+ { Comment cmnt(masm_, "[ Allocate locals");
+ int locals_count = info->scope()->num_stack_slots();
+ // Generators allocate locals, if any, in context slots.
+ ASSERT(!info->function()->is_generator() || locals_count == 0);
+ if (locals_count == 1) {
+ __ PushRoot(Heap::kUndefinedValueRootIndex);
+ } else if (locals_count > 1) {
+ __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
+ for (int i = 0; i < locals_count; i++) {
+ __ push(rdx);
+ }
+ }
+ }
+
+ bool function_in_register = true;
+
+ // Possibly allocate a local context.
+ int heap_slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ if (heap_slots > 0) {
+ Comment cmnt(masm_, "[ Allocate context");
+ // Argument to NewContext is the function, which is still in rdi.
+ __ push(rdi);
+ if (FLAG_harmony_scoping && info->scope()->is_global_scope()) {
+ __ Push(info->scope()->GetScopeInfo());
+ __ CallRuntime(Runtime::kNewGlobalContext, 2);
+ } else if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kNewFunctionContext, 1);
+ }
+ function_in_register = false;
+ // Context is returned in both rax and rsi. It replaces the context
+ // passed to us. It's saved in the stack and kept live in rsi.
+ __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi);
+
+ // Copy any necessary parameters into the context.
+ int num_parameters = info->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.
+ __ movq(rax, Operand(rbp, parameter_offset));
+ // Store it in the context.
+ int context_offset = Context::SlotOffset(var->index());
+ __ movq(Operand(rsi, context_offset), rax);
+ // Update the write barrier. This clobbers rax and rbx.
+ __ RecordWriteContextSlot(
+ rsi, context_offset, rax, rbx, kDontSaveFPRegs);
+ }
+ }
+ }
+
+ // Possibly allocate an arguments object.
+ Variable* arguments = scope()->arguments();
+ if (arguments != NULL) {
+ // Arguments object must be allocated after the context object, in
+ // case the "arguments" or ".arguments" variables are in the context.
+ Comment cmnt(masm_, "[ Allocate arguments object");
+ if (function_in_register) {
+ __ push(rdi);
+ } else {
+ __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ }
+ // The receiver is just before the parameters on the caller's stack.
+ int num_parameters = info->scope()->num_parameters();
+ int offset = num_parameters * kPointerSize;
+ __ lea(rdx,
+ Operand(rbp, StandardFrameConstants::kCallerSPOffset + offset));
+ __ push(rdx);
+ __ Push(Smi::FromInt(num_parameters));
+ // 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_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);
+ }
+
+ if (FLAG_trace) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+
+ // Visit the declarations and body unless there is an illegal
+ // redeclaration.
+ if (scope()->HasIllegalRedeclaration()) {
+ Comment cmnt(masm_, "[ Declarations");
+ scope()->VisitIllegalRedeclaration(this);
+
+ } else {
+ PrepareForBailoutForId(BailoutId::FunctionEntry(), NO_REGISTERS);
+ { Comment cmnt(masm_, "[ Declarations");
+ // For named function expressions, declare the function name as a
+ // constant.
+ if (scope()->is_function_scope() && scope()->function() != NULL) {
+ VariableDeclaration* function = scope()->function();
+ ASSERT(function->proxy()->var()->mode() == CONST ||
+ function->proxy()->var()->mode() == CONST_HARMONY);
+ ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED);
+ VisitVariableDeclaration(function);
+ }
+ VisitDeclarations(scope()->declarations());
+ }
+
+ { Comment cmnt(masm_, "[ Stack check");
+ PrepareForBailoutForId(BailoutId::Declarations(), NO_REGISTERS);
+ Label ok;
+ __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
+ __ j(above_equal, &ok, Label::kNear);
+ StackCheckStub stub;
+ __ CallStub(&stub);
+ __ bind(&ok);
+ }
+
+ { Comment cmnt(masm_, "[ Body");
+ ASSERT(loop_depth() == 0);
+ VisitStatements(function()->body());
+ ASSERT(loop_depth() == 0);
+ }
+ }
+
+ // Always emit a 'return undefined' in case control fell off the end of
+ // the body.
+ { Comment cmnt(masm_, "[ return <undefined>;");
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ EmitReturnSequence();
+ }
+}
+
+
+void FullCodeGenerator::ClearAccumulator() {
+ __ Set(rax, 0);
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) {
+ __ movq(rbx, profiling_counter_, RelocInfo::EMBEDDED_OBJECT);
+ __ SmiAddConstant(FieldOperand(rbx, Cell::kValueOffset),
+ Smi::FromInt(-delta));
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterReset() {
+ int reset_value = FLAG_interrupt_budget;
+ if (info_->ShouldSelfOptimize() && !FLAG_retry_self_opt) {
+ // Self-optimization is a one-off thing; if it fails, don't try again.
+ reset_value = Smi::kMaxValue;
+ }
+ __ movq(rbx, profiling_counter_, RelocInfo::EMBEDDED_OBJECT);
+ __ movq(kScratchRegister,
+ reinterpret_cast<uint64_t>(Smi::FromInt(reset_value)),
+ RelocInfo::NONE64);
+ __ movq(FieldOperand(rbx, Cell::kValueOffset), kScratchRegister);
+}
+
+
+void FullCodeGenerator::EmitBackEdgeBookkeeping(IterationStatement* stmt,
+ Label* back_edge_target) {
+ Comment cmnt(masm_, "[ Back edge bookkeeping");
+ Label ok;
+
+ int weight = 1;
+ if (FLAG_weighted_back_edges) {
+ ASSERT(back_edge_target->is_bound());
+ int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target);
+ weight = Min(kMaxBackEdgeWeight,
+ Max(1, distance / kCodeSizeMultiplier));
+ }
+ EmitProfilingCounterDecrement(weight);
+ __ j(positive, &ok, Label::kNear);
+ InterruptStub 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.
+ RecordBackEdge(stmt->OsrEntryId());
+
+ EmitProfilingCounterReset();
+
+ __ bind(&ok);
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ // Record a mapping of the OSR id to this PC. This is used if the OSR
+ // entry becomes the target of a bailout. We don't expect it to be, but
+ // we want it to work if it is.
+ PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::EmitReturnSequence() {
+ Comment cmnt(masm_, "[ Return sequence");
+ if (return_label_.is_bound()) {
+ __ jmp(&return_label_);
+ } else {
+ __ bind(&return_label_);
+ if (FLAG_trace) {
+ __ push(rax);
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+ if (FLAG_interrupt_at_exit || FLAG_self_optimization) {
+ // Pretend that the exit is a backwards jump to the entry.
+ int weight = 1;
+ if (info_->ShouldSelfOptimize()) {
+ weight = FLAG_interrupt_budget / FLAG_self_opt_count;
+ } else if (FLAG_weighted_back_edges) {
+ int distance = masm_->pc_offset();
+ weight = Min(kMaxBackEdgeWeight,
+ Max(1, distance / kCodeSizeMultiplier));
+ }
+ EmitProfilingCounterDecrement(weight);
+ Label ok;
+ __ j(positive, &ok, Label::kNear);
+ __ push(rax);
+ if (info_->ShouldSelfOptimize() && FLAG_direct_self_opt) {
+ __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ CallRuntime(Runtime::kOptimizeFunctionOnNextCall, 1);
+ } else {
+ InterruptStub stub;
+ __ CallStub(&stub);
+ }
+ __ pop(rax);
+ EmitProfilingCounterReset();
+ __ bind(&ok);
+ }
+#ifdef DEBUG
+ // Add a label for checking the size of the code used for returning.
+ Label check_exit_codesize;
+ masm_->bind(&check_exit_codesize);
+#endif
+ CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
+ __ RecordJSReturn();
+ // Do not use the leave instruction here because it is too short to
+ // patch with the code required by the debugger.
+ __ movq(rsp, rbp);
+ __ pop(rbp);
+ int no_frame_start = masm_->pc_offset();
+
+ int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize;
+ __ Ret(arguments_bytes, rcx);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Add padding that will be overwritten by a debugger breakpoint. We
+ // have just generated at least 7 bytes: "movq rsp, rbp; pop rbp; ret k"
+ // (3 + 1 + 3).
+ const int kPadding = Assembler::kJSReturnSequenceLength - 7;
+ for (int i = 0; i < kPadding; ++i) {
+ masm_->int3();
+ }
+ // Check that the size of the code used for returning is large enough
+ // for the debugger's requirements.
+ ASSERT(Assembler::kJSReturnSequenceLength <=
+ masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
+#endif
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ codegen()->GetVar(result_register(), var);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Variable* var) const {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ MemOperand operand = codegen()->VarOperand(var, result_register());
+ __ push(operand);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Variable* var) const {
+ codegen()->GetVar(result_register(), var);
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Heap::RootListIndex index) const {
+ __ LoadRoot(result_register(), index);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(
+ Heap::RootListIndex index) const {
+ __ PushRoot(index);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ if (index == Heap::kUndefinedValueRootIndex ||
+ index == Heap::kNullValueRootIndex ||
+ index == Heap::kFalseValueRootIndex) {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ } else if (index == Heap::kTrueValueRootIndex) {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ } else {
+ __ LoadRoot(result_register(), index);
+ codegen()->DoTest(this);
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Handle<Object> lit) const {
+ if (lit->IsSmi()) {
+ __ SafeMove(result_register(), Smi::cast(*lit));
+ } else {
+ __ Move(result_register(), lit);
+ }
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const {
+ if (lit->IsSmi()) {
+ __ SafePush(Smi::cast(*lit));
+ } else {
+ __ Push(lit);
+ }
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ ASSERT(!lit->IsUndetectableObject()); // There are no undetectable literals.
+ if (lit->IsUndefined() || lit->IsNull() || lit->IsFalse()) {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ } else if (lit->IsTrue() || lit->IsJSObject()) {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ } else if (lit->IsString()) {
+ if (String::cast(*lit)->length() == 0) {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ }
+ } else if (lit->IsSmi()) {
+ if (Smi::cast(*lit)->value() == 0) {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ }
+ } else {
+ // For simplicity we always test the accumulator register.
+ __ Move(result_register(), lit);
+ codegen()->DoTest(this);
+ }
+}
+
+
+void FullCodeGenerator::EffectContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ __ Drop(count);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::DropAndPlug(
+ int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ __ Drop(count);
+ __ Move(result_register(), reg);
+}
+
+
+void FullCodeGenerator::StackValueContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ if (count > 1) __ Drop(count - 1);
+ __ movq(Operand(rsp, 0), reg);
+}
+
+
+void FullCodeGenerator::TestContext::DropAndPlug(int count,
+ Register reg) const {
+ ASSERT(count > 0);
+ // For simplicity we always test the accumulator register.
+ __ Drop(count);
+ __ Move(result_register(), reg);
+ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
+ codegen()->DoTest(this);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(Label* materialize_true,
+ Label* materialize_false) const {
+ ASSERT(materialize_true == materialize_false);
+ __ bind(materialize_true);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(
+ Label* materialize_true,
+ Label* materialize_false) const {
+ Label done;
+ __ bind(materialize_true);
+ __ Move(result_register(), isolate()->factory()->true_value());
+ __ jmp(&done, Label::kNear);
+ __ bind(materialize_false);
+ __ Move(result_register(), isolate()->factory()->false_value());
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(
+ Label* materialize_true,
+ Label* materialize_false) const {
+ Label done;
+ __ bind(materialize_true);
+ __ Push(isolate()->factory()->true_value());
+ __ jmp(&done, Label::kNear);
+ __ bind(materialize_false);
+ __ Push(isolate()->factory()->false_value());
+ __ bind(&done);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(Label* materialize_true,
+ Label* materialize_false) const {
+ ASSERT(materialize_true == true_label_);
+ ASSERT(materialize_false == false_label_);
+}
+
+
+void FullCodeGenerator::EffectContext::Plug(bool flag) const {
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const {
+ Heap::RootListIndex value_root_index =
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
+ __ LoadRoot(result_register(), value_root_index);
+}
+
+
+void FullCodeGenerator::StackValueContext::Plug(bool flag) const {
+ Heap::RootListIndex value_root_index =
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
+ __ PushRoot(value_root_index);
+}
+
+
+void FullCodeGenerator::TestContext::Plug(bool flag) const {
+ codegen()->PrepareForBailoutBeforeSplit(condition(),
+ true,
+ true_label_,
+ false_label_);
+ if (flag) {
+ if (true_label_ != fall_through_) __ jmp(true_label_);
+ } else {
+ if (false_label_ != fall_through_) __ jmp(false_label_);
+ }
+}
+
+
+void FullCodeGenerator::DoTest(Expression* condition,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ Handle<Code> ic = ToBooleanStub::GetUninitialized(isolate());
+ CallIC(ic, RelocInfo::CODE_TARGET, condition->test_id());
+ __ testq(result_register(), result_register());
+ // The stub returns nonzero for true.
+ Split(not_zero, if_true, if_false, fall_through);
+}
+
+
+void FullCodeGenerator::Split(Condition cc,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through) {
+ if (if_false == fall_through) {
+ __ j(cc, if_true);
+ } else if (if_true == fall_through) {
+ __ j(NegateCondition(cc), if_false);
+ } else {
+ __ j(cc, if_true);
+ __ jmp(if_false);
+ }
+}
+
+
+MemOperand FullCodeGenerator::StackOperand(Variable* var) {
+ ASSERT(var->IsStackAllocated());
+ // Offset is negative because higher indexes are at lower addresses.
+ int offset = -var->index() * kPointerSize;
+ // Adjust by a (parameter or local) base offset.
+ if (var->IsParameter()) {
+ offset += (info_->scope()->num_parameters() + 1) * kPointerSize;
+ } else {
+ offset += JavaScriptFrameConstants::kLocal0Offset;
+ }
+ return Operand(rbp, offset);
+}
+
+
+MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ if (var->IsContextSlot()) {
+ int context_chain_length = scope()->ContextChainLength(var->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return ContextOperand(scratch, var->index());
+ } else {
+ return StackOperand(var);
+ }
+}
+
+
+void FullCodeGenerator::GetVar(Register dest, Variable* var) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ MemOperand location = VarOperand(var, dest);
+ __ movq(dest, location);
+}
+
+
+void FullCodeGenerator::SetVar(Variable* var,
+ Register src,
+ Register scratch0,
+ Register scratch1) {
+ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
+ ASSERT(!scratch0.is(src));
+ ASSERT(!scratch0.is(scratch1));
+ 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());
+ __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs);
+ }
+}
+
+
+void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
+ bool should_normalize,
+ Label* if_true,
+ Label* if_false) {
+ // Only prepare for bailouts before splits if we're in a test
+ // context. Otherwise, we let the Visit function deal with the
+ // preparation to avoid preparing with the same AST id twice.
+ if (!context()->IsTest() || !info_->IsOptimizable()) return;
+
+ Label skip;
+ if (should_normalize) __ jmp(&skip, Label::kNear);
+ PrepareForBailout(expr, TOS_REG);
+ if (should_normalize) {
+ __ CompareRoot(rax, Heap::kTrueValueRootIndex);
+ Split(equal, if_true, if_false, NULL);
+ __ bind(&skip);
+ }
+}
+
+
+void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
+ // The variable in the declaration always resides in the current context.
+ ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
+ if (generate_debug_code_) {
+ // Check that we're not inside a with or catch context.
+ __ movq(rbx, FieldOperand(rsi, HeapObject::kMapOffset));
+ __ CompareRoot(rbx, Heap::kWithContextMapRootIndex);
+ __ Check(not_equal, kDeclarationInWithContext);
+ __ CompareRoot(rbx, Heap::kCatchContextMapRootIndex);
+ __ Check(not_equal, kDeclarationInCatchContext);
+ }
+}
+
+
+void FullCodeGenerator::VisitVariableDeclaration(
+ VariableDeclaration* declaration) {
+ // 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.
+ VariableProxy* proxy = declaration->proxy();
+ VariableMode mode = declaration->mode();
+ Variable* variable = proxy->var();
+ bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ globals_->Add(variable->name(), zone());
+ globals_->Add(variable->binding_needs_init()
+ ? isolate()->factory()->the_hole_value()
+ : isolate()->factory()->undefined_value(),
+ zone());
+ break;
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ if (hole_init) {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
+ __ movq(StackOperand(variable), kScratchRegister);
+ }
+ break;
+
+ case Variable::CONTEXT:
+ if (hole_init) {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
+ __ movq(ContextOperand(rsi, variable->index()), kScratchRegister);
+ // No write barrier since the hole value is in old space.
+ PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
+ }
+ break;
+
+ case Variable::LOOKUP: {
+ Comment cmnt(masm_, "[ VariableDeclaration");
+ __ push(rsi);
+ __ Push(variable->name());
+ // Declaration nodes are always introduced in one of four modes.
+ ASSERT(IsDeclaredVariableMode(mode));
+ PropertyAttributes attr =
+ IsImmutableVariableMode(mode) ? READ_ONLY : NONE;
+ __ Push(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.
+ if (hole_init) {
+ __ PushRoot(Heap::kTheHoleValueRootIndex);
+ } else {
+ __ Push(Smi::FromInt(0)); // Indicates no initial value.
+ }
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitFunctionDeclaration(
+ FunctionDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED: {
+ globals_->Add(variable->name(), zone());
+ Handle<SharedFunctionInfo> function =
+ Compiler::BuildFunctionInfo(declaration->fun(), script());
+ // Check for stack-overflow exception.
+ if (function.is_null()) return SetStackOverflow();
+ globals_->Add(function, zone());
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ VisitForAccumulatorValue(declaration->fun());
+ __ movq(StackOperand(variable), result_register());
+ break;
+ }
+
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ VisitForAccumulatorValue(declaration->fun());
+ __ movq(ContextOperand(rsi, variable->index()), result_register());
+ int offset = Context::SlotOffset(variable->index());
+ // 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);
+ break;
+ }
+
+ case Variable::LOOKUP: {
+ Comment cmnt(masm_, "[ FunctionDeclaration");
+ __ push(rsi);
+ __ Push(variable->name());
+ __ Push(Smi::FromInt(NONE));
+ VisitForStackValue(declaration->fun());
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
+ Variable* variable = declaration->proxy()->var();
+ ASSERT(variable->location() == Variable::CONTEXT);
+ ASSERT(variable->interface()->IsFrozen());
+
+ Comment cmnt(masm_, "[ ModuleDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+
+ // Load instance object.
+ __ LoadContext(rax, scope_->ContextChainLength(scope_->GlobalScope()));
+ __ movq(rax, ContextOperand(rax, variable->interface()->Index()));
+ __ movq(rax, ContextOperand(rax, Context::EXTENSION_INDEX));
+
+ // Assign it.
+ __ movq(ContextOperand(rsi, variable->index()), rax);
+ // We know that we have written a module, which is not a smi.
+ __ RecordWriteContextSlot(rsi,
+ Context::SlotOffset(variable->index()),
+ rax,
+ rcx,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS);
+
+ // Traverse into body.
+ Visit(declaration->module());
+}
+
+
+void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) {
+ VariableProxy* proxy = declaration->proxy();
+ Variable* variable = proxy->var();
+ switch (variable->location()) {
+ case Variable::UNALLOCATED:
+ // TODO(rossberg)
+ break;
+
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, "[ ImportDeclaration");
+ EmitDebugCheckDeclarationContext(variable);
+ // TODO(rossberg)
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ case Variable::LOOKUP:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
+ // TODO(rossberg)
+}
+
+
+void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
+ // Call the runtime to declare the globals.
+ __ push(rsi); // The context is the first argument.
+ __ Push(pairs);
+ __ Push(Smi::FromInt(DeclareGlobalsFlags()));
+ __ CallRuntime(Runtime::kDeclareGlobals, 3);
+ // Return value is ignored.
+}
+
+
+void FullCodeGenerator::DeclareModules(Handle<FixedArray> descriptions) {
+ // Call the runtime to declare the modules.
+ __ Push(descriptions);
+ __ CallRuntime(Runtime::kDeclareModules, 1);
+ // Return value is ignored.
+}
+
+
+void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
+ Comment cmnt(masm_, "[ SwitchStatement");
+ Breakable nested_statement(this, stmt);
+ SetStatementPosition(stmt);
+
+ // Keep the switch value on the stack until a case matches.
+ VisitForStackValue(stmt->tag());
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+
+ ZoneList<CaseClause*>* clauses = stmt->cases();
+ CaseClause* default_clause = NULL; // Can occur anywhere in the list.
+
+ Label next_test; // Recycled for each test.
+ // Compile all the tests with branches to their bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ CaseClause* clause = clauses->at(i);
+ clause->body_target()->Unuse();
+
+ // The default is not a test, but remember it as final fall through.
+ if (clause->is_default()) {
+ default_clause = clause;
+ continue;
+ }
+
+ Comment cmnt(masm_, "[ Case comparison");
+ __ bind(&next_test);
+ next_test.Unuse();
+
+ // Compile the label expression.
+ VisitForAccumulatorValue(clause->label());
+
+ // Perform the comparison as if via '==='.
+ __ movq(rdx, Operand(rsp, 0)); // Switch value.
+ bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ movq(rcx, rdx);
+ __ or_(rcx, rax);
+ patch_site.EmitJumpIfNotSmi(rcx, &slow_case, Label::kNear);
+
+ __ cmpq(rdx, rax);
+ __ j(not_equal, &next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ __ jmp(clause->body_target());
+ __ bind(&slow_case);
+ }
+
+ // Record position before stub call for type feedback.
+ SetSourcePosition(clause->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), Token::EQ_STRICT);
+ CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
+ patch_site.EmitPatchInfo();
+
+ __ testq(rax, rax);
+ __ j(not_equal, &next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ __ jmp(clause->body_target());
+ }
+
+ // Discard the test value and jump to the default if present, otherwise to
+ // the end of the statement.
+ __ bind(&next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ if (default_clause == NULL) {
+ __ jmp(nested_statement.break_label());
+ } else {
+ __ jmp(default_clause->body_target());
+ }
+
+ // Compile all the case bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ Comment cmnt(masm_, "[ Case body");
+ CaseClause* clause = clauses->at(i);
+ __ bind(clause->body_target());
+ PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
+ VisitStatements(clause->statements());
+ }
+
+ __ bind(nested_statement.break_label());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
+ Comment cmnt(masm_, "[ ForInStatement");
+ SetStatementPosition(stmt);
+
+ Label loop, exit;
+ ForIn loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // Get the object to enumerate over. If the object is null or undefined, skip
+ // over the loop. See ECMA-262 version 5, section 12.6.4.
+ VisitForAccumulatorValue(stmt->enumerable());
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ j(equal, &exit);
+ Register null_value = rdi;
+ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
+ __ cmpq(rax, null_value);
+ __ j(equal, &exit);
+
+ PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
+
+ // Convert the object to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(rax, &convert);
+ __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
+ __ j(above_equal, &done_convert);
+ __ bind(&convert);
+ __ push(rax);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ 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.
+ __ CheckEnumCache(null_value, &call_runtime);
+
+ // The enum cache is valid. Load the map of the object being
+ // iterated over and use the cache for the iteration.
+ Label use_cache;
+ __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset));
+ __ jmp(&use_cache, Label::kNear);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(rax); // Duplicate the enumerable object on the stack.
+ __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
+
+ // If we got a map from the runtime call, we can do a fast
+ // modification check. Otherwise, we got a fixed array, and we have
+ // to do a slow check.
+ Label fixed_array;
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kMetaMapRootIndex);
+ __ j(not_equal, &fixed_array);
+
+ // We got a map in register rax. Get the enumeration cache from it.
+ __ bind(&use_cache);
+
+ Label no_descriptors;
+
+ __ EnumLength(rdx, rax);
+ __ Cmp(rdx, Smi::FromInt(0));
+ __ j(equal, &no_descriptors);
+
+ __ LoadInstanceDescriptors(rax, rcx);
+ __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumCacheOffset));
+ __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset));
+
+ // Set up the four remaining stack slots.
+ __ push(rax); // Map.
+ __ push(rcx); // Enumeration cache.
+ __ push(rdx); // Number of valid entries for the map in the enum cache.
+ __ Push(Smi::FromInt(0)); // Initial index.
+ __ jmp(&loop);
+
+ __ bind(&no_descriptors);
+ __ addq(rsp, Immediate(kPointerSize));
+ __ jmp(&exit);
+
+ // We got a fixed array in register rax. Iterate through that.
+ Label non_proxy;
+ __ bind(&fixed_array);
+
+ Handle<Cell> cell = isolate()->factory()->NewCell(
+ Handle<Object>(Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker),
+ isolate()));
+ RecordTypeFeedbackCell(stmt->ForInFeedbackId(), cell);
+ __ LoadHeapObject(rbx, cell);
+ __ Move(FieldOperand(rbx, Cell::kValueOffset),
+ Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker));
+
+ __ 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.
+
+ // Generate code for doing the condition check.
+ PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
+ __ bind(&loop);
+ __ movq(rax, Operand(rsp, 0 * kPointerSize)); // Get the current index.
+ __ cmpq(rax, Operand(rsp, 1 * kPointerSize)); // Compare to the array length.
+ __ j(above_equal, loop_statement.break_label());
+
+ // Get the current entry of the array into register rbx.
+ __ movq(rbx, Operand(rsp, 2 * kPointerSize));
+ SmiIndex index = masm()->SmiToIndex(rax, rax, kPointerSizeLog2);
+ __ movq(rbx, FieldOperand(rbx,
+ index.reg,
+ index.scale,
+ FixedArray::kHeaderSize));
+
+ // 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 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.
+ __ push(rcx); // Enumerable.
+ __ push(rbx); // Current entry.
+ __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
+ __ Cmp(rax, Smi::FromInt(0));
+ __ j(equal, loop_statement.continue_label());
+ __ movq(rbx, rax);
+
+ // Update the 'each' property or variable from the possibly filtered
+ // entry in register rbx.
+ __ bind(&update_each);
+ __ movq(result_register(), rbx);
+ // Perform the assignment as if via '='.
+ { EffectContext context(this);
+ EmitAssignment(stmt->each());
+ }
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Generate code for going to the next element by incrementing the
+ // index (smi) stored on top of the stack.
+ __ bind(loop_statement.continue_label());
+ __ SmiAddConstant(Operand(rsp, 0 * kPointerSize), Smi::FromInt(1));
+
+ EmitBackEdgeBookkeeping(stmt, &loop);
+ __ jmp(&loop);
+
+ // Remove the pointers stored on the stack.
+ __ bind(loop_statement.break_label());
+ __ addq(rsp, Immediate(5 * kPointerSize));
+
+ // Exit and decrement the loop depth.
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(&exit);
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
+ Comment cmnt(masm_, "[ ForOfStatement");
+ SetStatementPosition(stmt);
+
+ Iteration loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // var iterator = iterable[@@iterator]()
+ VisitForAccumulatorValue(stmt->assign_iterator());
+
+ // As with for-in, skip the loop if the iterator is null or undefined.
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ j(equal, loop_statement.break_label());
+ __ CompareRoot(rax, Heap::kNullValueRootIndex);
+ __ j(equal, loop_statement.break_label());
+
+ // Convert the iterator to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(rax, &convert);
+ __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
+ __ j(above_equal, &done_convert);
+ __ bind(&convert);
+ __ push(rax);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ bind(&done_convert);
+
+ // Loop entry.
+ __ bind(loop_statement.continue_label());
+
+ // result = iterator.next()
+ VisitForEffect(stmt->next_result());
+
+ // if (result.done) break;
+ Label result_not_done;
+ VisitForControl(stmt->result_done(),
+ loop_statement.break_label(),
+ &result_not_done,
+ &result_not_done);
+ __ bind(&result_not_done);
+
+ // each = result.value
+ VisitForEffect(stmt->assign_each());
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Check stack before looping.
+ PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS);
+ EmitBackEdgeBookkeeping(stmt, loop_statement.continue_label());
+ __ jmp(loop_statement.continue_label());
+
+ // Exit and decrement the loop depth.
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
+ __ bind(loop_statement.break_label());
+ decrement_loop_depth();
+}
+
+
+void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
+ bool pretenure) {
+ // Use the fast case closure allocation code that allocates in new
+ // space for nested functions that don't need literals cloning. If
+ // we're running with the --always-opt or the --prepare-always-opt
+ // flag, we need to use the runtime function so that the new function
+ // we are creating here gets a chance to have its code optimized and
+ // doesn't just get a copy of the existing unoptimized code.
+ if (!FLAG_always_opt &&
+ !FLAG_prepare_always_opt &&
+ !pretenure &&
+ scope()->is_function_scope() &&
+ info->num_literals() == 0) {
+ FastNewClosureStub stub(info->language_mode(), info->is_generator());
+ __ Push(info);
+ __ CallStub(&stub);
+ } else {
+ __ push(rsi);
+ __ Push(info);
+ __ Push(pretenure
+ ? isolate()->factory()->true_value()
+ : isolate()->factory()->false_value());
+ __ CallRuntime(Runtime::kNewClosure, 3);
+ }
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
+ Comment cmnt(masm_, "[ VariableProxy");
+ EmitVariableLoad(expr);
+}
+
+
+void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
+ TypeofState typeof_state,
+ Label* slow) {
+ Register context = rsi;
+ Register temp = rdx;
+
+ Scope* s = scope();
+ while (s != NULL) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_non_strict_eval()) {
+ // Check that extension is NULL.
+ __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
+ Immediate(0));
+ __ j(not_equal, slow);
+ }
+ // Load next context in chain.
+ __ movq(temp, ContextOperand(context, Context::PREVIOUS_INDEX));
+ // Walk the rest of the chain without clobbering rsi.
+ context = temp;
+ }
+ // 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_non_strict_eval() || s->is_eval_scope()) break;
+ s = s->outer_scope();
+ }
+
+ if (s != NULL && s->is_eval_scope()) {
+ // Loop up the context chain. There is no frame effect so it is
+ // safe to use raw labels here.
+ Label next, fast;
+ if (!context.is(temp)) {
+ __ movq(temp, context);
+ }
+ // Load map for comparison into register, outside loop.
+ __ LoadRoot(kScratchRegister, Heap::kNativeContextMapRootIndex);
+ __ bind(&next);
+ // Terminate at native context.
+ __ cmpq(kScratchRegister, FieldOperand(temp, HeapObject::kMapOffset));
+ __ j(equal, &fast, Label::kNear);
+ // Check that extension is NULL.
+ __ cmpq(ContextOperand(temp, Context::EXTENSION_INDEX), Immediate(0));
+ __ j(not_equal, slow);
+ // Load next context in chain.
+ __ movq(temp, ContextOperand(temp, Context::PREVIOUS_INDEX));
+ __ jmp(&next);
+ __ bind(&fast);
+ }
+
+ // All extension objects were empty and it is safe to use a global
+ // load IC call.
+ __ movq(rax, GlobalObjectOperand());
+ __ Move(rcx, var->name());
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+ ? RelocInfo::CODE_TARGET
+ : RelocInfo::CODE_TARGET_CONTEXT;
+ CallIC(ic, mode);
+}
+
+
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var,
+ Label* slow) {
+ ASSERT(var->IsContextSlot());
+ Register context = rsi;
+ Register temp = rbx;
+
+ for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_non_strict_eval()) {
+ // Check that extension is NULL.
+ __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
+ Immediate(0));
+ __ j(not_equal, slow);
+ }
+ __ movq(temp, ContextOperand(context, Context::PREVIOUS_INDEX));
+ // Walk the rest of the chain without clobbering rsi.
+ context = temp;
+ }
+ }
+ // Check that last extension is NULL.
+ __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
+ __ j(not_equal, slow);
+
+ // This function is used only for loads, not stores, so it's safe to
+ // return an rsi-based operand (the write barrier cannot be allowed to
+ // destroy the rsi register).
+ return ContextOperand(context, var->index());
+}
+
+
+void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var,
+ TypeofState typeof_state,
+ Label* slow,
+ Label* done) {
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // 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() == DYNAMIC_GLOBAL) {
+ EmitLoadGlobalCheckExtensions(var, typeof_state, slow);
+ __ jmp(done);
+ } else if (var->mode() == DYNAMIC_LOCAL) {
+ Variable* local = var->local_if_not_shadowed();
+ __ movq(rax, ContextSlotOperandCheckExtensions(local, slow));
+ if (local->mode() == LET ||
+ local->mode() == CONST ||
+ local->mode() == CONST_HARMONY) {
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, done);
+ if (local->mode() == CONST) {
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ } else { // LET || CONST_HARMONY
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ }
+ }
+ __ jmp(done);
+ }
+}
+
+
+void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
+ // Record position before possible IC call.
+ SetSourcePosition(proxy->position());
+ Variable* var = proxy->var();
+
+ // Three cases: global variables, lookup variables, and all other types of
+ // variables.
+ switch (var->location()) {
+ case Variable::UNALLOCATED: {
+ Comment cmnt(masm_, "Global variable");
+ // Use inline caching. Variable name is passed in rcx and the global
+ // object on the stack.
+ __ Move(rcx, var->name());
+ __ movq(rax, GlobalObjectOperand());
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ context()->Plug(rax);
+ break;
+ }
+
+ case Variable::PARAMETER:
+ case Variable::LOCAL:
+ case Variable::CONTEXT: {
+ Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack slot");
+ 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;
+ }
+ }
+ context()->Plug(var);
+ break;
+ }
+
+ case Variable::LOOKUP: {
+ Label done, slow;
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done);
+ __ bind(&slow);
+ Comment cmnt(masm_, "Lookup slot");
+ __ push(rsi); // Context.
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ bind(&done);
+ context()->Plug(rax);
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
+ Comment cmnt(masm_, "[ RegExpLiteral");
+ Label materialized;
+ // Registers will be used as follows:
+ // rdi = JS function.
+ // rcx = literals array.
+ // rbx = regexp literal.
+ // rax = regexp literal clone.
+ __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ movq(rcx, FieldOperand(rdi, JSFunction::kLiteralsOffset));
+ int literal_offset =
+ FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
+ __ movq(rbx, FieldOperand(rcx, literal_offset));
+ __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, &materialized, Label::kNear);
+
+ // Create regexp literal using runtime function
+ // Result will be in rax.
+ __ push(rcx);
+ __ Push(Smi::FromInt(expr->literal_index()));
+ __ Push(expr->pattern());
+ __ Push(expr->flags());
+ __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
+ __ movq(rbx, rax);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+ __ Allocate(size, rax, rcx, rdx, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ push(rbx);
+ __ Push(Smi::FromInt(size));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ pop(rbx);
+
+ __ 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) {
+ __ movq(rdx, FieldOperand(rbx, i));
+ __ movq(rcx, FieldOperand(rbx, i + kPointerSize));
+ __ movq(FieldOperand(rax, i), rdx);
+ __ movq(FieldOperand(rax, i + kPointerSize), rcx);
+ }
+ if ((size % (2 * kPointerSize)) != 0) {
+ __ movq(rdx, FieldOperand(rbx, size - kPointerSize));
+ __ movq(FieldOperand(rax, size - kPointerSize), rdx);
+ }
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitAccessor(Expression* expression) {
+ if (expression == NULL) {
+ __ PushRoot(Heap::kNullValueRootIndex);
+ } else {
+ VisitForStackValue(expression);
+ }
+}
+
+
+void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
+ Comment cmnt(masm_, "[ ObjectLiteral");
+ Handle<FixedArray> constant_properties = expr->constant_properties();
+ int flags = expr->fast_elements()
+ ? ObjectLiteral::kFastElements
+ : ObjectLiteral::kNoFlags;
+ flags |= expr->has_function()
+ ? ObjectLiteral::kHasFunction
+ : ObjectLiteral::kNoFlags;
+ int properties_count = constant_properties->length() / 2;
+ if ((FLAG_track_double_fields && expr->may_store_doubles()) ||
+ expr->depth() > 1) {
+ __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(rdi, JSFunction::kLiteralsOffset));
+ __ Push(Smi::FromInt(expr->literal_index()));
+ __ Push(constant_properties);
+ __ Push(Smi::FromInt(flags));
+ __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
+ } else if (Serializer::enabled() || flags != ObjectLiteral::kFastElements ||
+ properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) {
+ __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(rdi, JSFunction::kLiteralsOffset));
+ __ Push(Smi::FromInt(expr->literal_index()));
+ __ Push(constant_properties);
+ __ Push(Smi::FromInt(flags));
+ __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
+ } else {
+ __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ movq(rax, FieldOperand(rdi, JSFunction::kLiteralsOffset));
+ __ Move(rbx, Smi::FromInt(expr->literal_index()));
+ __ Move(rcx, constant_properties);
+ __ Move(rdx, Smi::FromInt(flags));
+ FastCloneShallowObjectStub stub(properties_count);
+ __ CallStub(&stub);
+ }
+
+ // If result_saved is true the result is on top of the stack. If
+ // result_saved is false the result is in rax.
+ bool result_saved = false;
+
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ expr->CalculateEmitStore(zone());
+
+ AccessorTable accessor_table(zone());
+ for (int i = 0; i < expr->properties()->length(); i++) {
+ ObjectLiteral::Property* property = expr->properties()->at(i);
+ if (property->IsCompileTimeValue()) continue;
+
+ Literal* key = property->key();
+ Expression* value = property->value();
+ if (!result_saved) {
+ __ push(rax); // Save result on the stack
+ result_saved = true;
+ }
+ switch (property->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ UNREACHABLE();
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+ ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
+ // Fall through.
+ case ObjectLiteral::Property::COMPUTED:
+ if (key->value()->IsInternalizedString()) {
+ if (property->emit_store()) {
+ VisitForAccumulatorValue(value);
+ __ Move(rcx, key->value());
+ __ movq(rdx, Operand(rsp, 0));
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, key->LiteralFeedbackId());
+ PrepareForBailoutForId(key->id(), NO_REGISTERS);
+ } else {
+ VisitForEffect(value);
+ }
+ break;
+ }
+ __ push(Operand(rsp, 0)); // Duplicate receiver.
+ VisitForStackValue(key);
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ Push(Smi::FromInt(NONE)); // PropertyAttributes
+ __ CallRuntime(Runtime::kSetProperty, 4);
+ } else {
+ __ Drop(3);
+ }
+ break;
+ case ObjectLiteral::Property::PROTOTYPE:
+ __ push(Operand(rsp, 0)); // Duplicate receiver.
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ CallRuntime(Runtime::kSetPrototype, 2);
+ } else {
+ __ Drop(2);
+ }
+ break;
+ case ObjectLiteral::Property::GETTER:
+ accessor_table.lookup(key)->second->getter = value;
+ break;
+ case ObjectLiteral::Property::SETTER:
+ accessor_table.lookup(key)->second->setter = value;
+ break;
+ }
+ }
+
+ // Emit code to define accessors, using only a single call to the runtime for
+ // each pair of corresponding getters and setters.
+ for (AccessorTable::Iterator it = accessor_table.begin();
+ it != accessor_table.end();
+ ++it) {
+ __ push(Operand(rsp, 0)); // Duplicate receiver.
+ VisitForStackValue(it->first);
+ EmitAccessor(it->second->getter);
+ EmitAccessor(it->second->setter);
+ __ Push(Smi::FromInt(NONE));
+ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
+ }
+
+ if (expr->has_function()) {
+ ASSERT(result_saved);
+ __ push(Operand(rsp, 0));
+ __ CallRuntime(Runtime::kToFastProperties, 1);
+ }
+
+ if (result_saved) {
+ context()->PlugTOS();
+ } else {
+ context()->Plug(rax);
+ }
+}
+
+
+void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
+ Comment cmnt(masm_, "[ ArrayLiteral");
+
+ 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 =
+ IsFastObjectElementsKind(constant_elements_kind);
+ Handle<FixedArrayBase> constant_elements_values(
+ FixedArrayBase::cast(constant_elements->get(1)));
+
+ 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);
+ __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ movq(rax, FieldOperand(rbx, JSFunction::kLiteralsOffset));
+ __ Move(rbx, Smi::FromInt(expr->literal_index()));
+ __ Move(rcx, constant_elements);
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS,
+ DONT_TRACK_ALLOCATION_SITE,
+ length);
+ __ CallStub(&stub);
+ } else if (expr->depth() > 1) {
+ __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset));
+ __ Push(Smi::FromInt(expr->literal_index()));
+ __ Push(constant_elements);
+ __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
+ } else if (Serializer::enabled() ||
+ length > FastCloneShallowArrayStub::kMaximumClonedLength) {
+ __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset));
+ __ Push(Smi::FromInt(expr->literal_index()));
+ __ Push(constant_elements);
+ __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
+ } else {
+ ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) ||
+ FLAG_smi_only_arrays);
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
+ AllocationSiteMode allocation_site_mode = FLAG_track_allocation_sites
+ ? TRACK_ALLOCATION_SITE : DONT_TRACK_ALLOCATION_SITE;
+
+ // If the elements are already FAST_*_ELEMENTS, the boilerplate cannot
+ // change, so it's possible to specialize the stub in advance.
+ if (has_constant_fast_elements) {
+ mode = FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
+ }
+
+ __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ movq(rax, FieldOperand(rbx, JSFunction::kLiteralsOffset));
+ __ Move(rbx, Smi::FromInt(expr->literal_index()));
+ __ Move(rcx, constant_elements);
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
+ __ CallStub(&stub);
+ }
+
+ bool result_saved = false; // Is the result saved to the stack?
+
+ // Emit code to evaluate all the non-constant subexpressions and to store
+ // them into the newly cloned array.
+ for (int i = 0; i < length; i++) {
+ Expression* subexpr = subexprs->at(i);
+ // If the subexpression is a literal or a simple materialized literal it
+ // is already set in the cloned array.
+ if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
+
+ if (!result_saved) {
+ __ push(rax); // array literal
+ __ Push(Smi::FromInt(expr->literal_index()));
+ result_saved = true;
+ }
+ VisitForAccumulatorValue(subexpr);
+
+ if (IsFastObjectElementsKind(constant_elements_kind)) {
+ // 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, kPointerSize)); // 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.
+ __ Move(rcx, Smi::FromInt(i));
+ StoreArrayLiteralElementStub stub;
+ __ CallStub(&stub);
+ }
+
+ PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
+ }
+
+ if (result_saved) {
+ __ addq(rsp, Immediate(kPointerSize)); // literal index
+ context()->PlugTOS();
+ } else {
+ context()->Plug(rax);
+ }
+}
+
+
+void FullCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // on the left-hand side.
+ if (!expr->target()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->target());
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* property = expr->target()->AsProperty();
+ if (property != NULL) {
+ assign_type = (property->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ // Evaluate LHS expression.
+ switch (assign_type) {
+ case VARIABLE:
+ // Nothing to do here.
+ break;
+ case NAMED_PROPERTY:
+ if (expr->is_compound()) {
+ // We need the receiver both on the stack and in the accumulator.
+ VisitForAccumulatorValue(property->obj());
+ __ push(result_register());
+ } else {
+ VisitForStackValue(property->obj());
+ }
+ break;
+ case KEYED_PROPERTY: {
+ if (expr->is_compound()) {
+ VisitForStackValue(property->obj());
+ VisitForAccumulatorValue(property->key());
+ __ movq(rdx, Operand(rsp, 0));
+ __ push(rax);
+ } else {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ }
+ break;
+ }
+ }
+
+ // For compound assignments we need another deoptimization point after the
+ // variable/property load.
+ if (expr->is_compound()) {
+ { AccumulatorValueContext context(this);
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableLoad(expr->target()->AsVariableProxy());
+ PrepareForBailout(expr->target(), TOS_REG);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyLoad(property);
+ PrepareForBailoutForId(property->LoadId(), TOS_REG);
+ break;
+ }
+ }
+
+ Token::Value op = expr->binary_op();
+ __ push(rax); // Left operand goes on the stack.
+ VisitForAccumulatorValue(expr->value());
+
+ OverwriteMode mode = expr->value()->ResultOverwriteAllowed()
+ ? OVERWRITE_RIGHT
+ : NO_OVERWRITE;
+ SetSourcePosition(expr->position() + 1);
+ AccumulatorValueContext context(this);
+ if (ShouldInlineSmiCase(op)) {
+ EmitInlineSmiBinaryOp(expr->binary_operation(),
+ op,
+ mode,
+ expr->target(),
+ expr->value());
+ } else {
+ EmitBinaryOp(expr->binary_operation(), op, mode);
+ }
+ // Deoptimization point in case the binary operation may have side effects.
+ PrepareForBailout(expr->binary_operation(), TOS_REG);
+ } else {
+ VisitForAccumulatorValue(expr->value());
+ }
+
+ // Record source position before possible IC call.
+ SetSourcePosition(expr->position());
+
+ // Store the value.
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ expr->op());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(rax);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyAssignment(expr);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyAssignment(expr);
+ break;
+ }
+}
+
+
+void FullCodeGenerator::VisitYield(Yield* expr) {
+ Comment cmnt(masm_, "[ Yield");
+ // Evaluate yielded value first; the initial iterator definition depends on
+ // this. It stays on the stack while we update the iterator.
+ VisitForStackValue(expr->expression());
+
+ switch (expr->yield_kind()) {
+ case Yield::SUSPEND:
+ // Pop value from top-of-stack slot; box result into result register.
+ EmitCreateIteratorResult(false);
+ __ push(result_register());
+ // Fall through.
+ case Yield::INITIAL: {
+ Label suspend, continuation, post_runtime, resume;
+
+ __ jmp(&suspend);
+
+ __ bind(&continuation);
+ __ jmp(&resume);
+
+ __ bind(&suspend);
+ VisitForAccumulatorValue(expr->generator_object());
+ ASSERT(continuation.pos() > 0 && Smi::IsValid(continuation.pos()));
+ __ Move(FieldOperand(rax, JSGeneratorObject::kContinuationOffset),
+ Smi::FromInt(continuation.pos()));
+ __ movq(FieldOperand(rax, JSGeneratorObject::kContextOffset), rsi);
+ __ movq(rcx, rsi);
+ __ RecordWriteField(rax, JSGeneratorObject::kContextOffset, rcx, rdx,
+ kDontSaveFPRegs);
+ __ lea(rbx, Operand(rbp, StandardFrameConstants::kExpressionsOffset));
+ __ cmpq(rsp, rbx);
+ __ j(equal, &post_runtime);
+ __ push(rax); // generator object
+ __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+ __ movq(context_register(),
+ Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ bind(&post_runtime);
+
+ __ pop(result_register());
+ EmitReturnSequence();
+
+ __ bind(&resume);
+ context()->Plug(result_register());
+ break;
+ }
+
+ case Yield::FINAL: {
+ VisitForAccumulatorValue(expr->generator_object());
+ __ Move(FieldOperand(result_register(),
+ JSGeneratorObject::kContinuationOffset),
+ Smi::FromInt(JSGeneratorObject::kGeneratorClosed));
+ // Pop value from top-of-stack slot, box result into result register.
+ EmitCreateIteratorResult(true);
+ EmitUnwindBeforeReturn();
+ EmitReturnSequence();
+ break;
+ }
+
+ case Yield::DELEGATING: {
+ VisitForStackValue(expr->generator_object());
+
+ // Initial stack layout is as follows:
+ // [sp + 1 * kPointerSize] iter
+ // [sp + 0 * kPointerSize] g
+
+ Label l_catch, l_try, l_suspend, l_continuation, l_resume;
+ Label l_next, l_call, l_loop;
+ // Initial send value is undefined.
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ jmp(&l_next);
+
+ // catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; }
+ __ bind(&l_catch);
+ handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos()));
+ __ LoadRoot(rcx, Heap::kthrow_stringRootIndex); // "throw"
+ __ push(rcx);
+ __ push(Operand(rsp, 2 * kPointerSize)); // iter
+ __ push(rax); // exception
+ __ jmp(&l_call);
+
+ // try { received = %yield result }
+ // Shuffle the received result above a try handler and yield it without
+ // re-boxing.
+ __ bind(&l_try);
+ __ pop(rax); // result
+ __ PushTryHandler(StackHandler::CATCH, expr->index());
+ const int handler_size = StackHandlerConstants::kSize;
+ __ push(rax); // result
+ __ jmp(&l_suspend);
+ __ bind(&l_continuation);
+ __ jmp(&l_resume);
+ __ bind(&l_suspend);
+ const int generator_object_depth = kPointerSize + handler_size;
+ __ movq(rax, Operand(rsp, generator_object_depth));
+ __ push(rax); // g
+ ASSERT(l_continuation.pos() > 0 && Smi::IsValid(l_continuation.pos()));
+ __ Move(FieldOperand(rax, JSGeneratorObject::kContinuationOffset),
+ Smi::FromInt(l_continuation.pos()));
+ __ movq(FieldOperand(rax, JSGeneratorObject::kContextOffset), rsi);
+ __ movq(rcx, rsi);
+ __ RecordWriteField(rax, JSGeneratorObject::kContextOffset, rcx, rdx,
+ kDontSaveFPRegs);
+ __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1);
+ __ movq(context_register(),
+ Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ pop(rax); // result
+ EmitReturnSequence();
+ __ bind(&l_resume); // received in rax
+ __ PopTryHandler();
+
+ // receiver = iter; f = 'next'; arg = received;
+ __ bind(&l_next);
+ __ LoadRoot(rcx, Heap::knext_stringRootIndex); // "next"
+ __ push(rcx);
+ __ push(Operand(rsp, 2 * kPointerSize)); // iter
+ __ push(rax); // received
+
+ // result = receiver[f](arg);
+ __ bind(&l_call);
+ Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(1);
+ CallIC(ic);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ Drop(1); // The key is still on the stack; drop it.
+
+ // if (!result.done) goto l_try;
+ __ bind(&l_loop);
+ __ push(rax); // save result
+ __ LoadRoot(rcx, Heap::kdone_stringRootIndex); // "done"
+ Handle<Code> done_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(done_ic); // result.done in rax
+ Handle<Code> bool_ic = ToBooleanStub::GetUninitialized(isolate());
+ CallIC(bool_ic);
+ __ testq(result_register(), result_register());
+ __ j(zero, &l_try);
+
+ // result.value
+ __ pop(rax); // result
+ __ LoadRoot(rcx, Heap::kvalue_stringRootIndex); // "value"
+ Handle<Code> value_ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(value_ic); // result.value in rax
+ context()->DropAndPlug(2, rax); // drop iter and g
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode) {
+ // The value stays in rax, and is ultimately read by the resumed generator, as
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. rbx
+ // will hold the generator object until the activation has been resumed.
+ VisitForStackValue(generator);
+ VisitForAccumulatorValue(value);
+ __ pop(rbx);
+
+ // Check generator state.
+ Label wrong_state, done;
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ __ SmiCompare(FieldOperand(rbx, JSGeneratorObject::kContinuationOffset),
+ Smi::FromInt(0));
+ __ j(less_equal, &wrong_state);
+
+ // Load suspended function and context.
+ __ movq(rsi, FieldOperand(rbx, JSGeneratorObject::kContextOffset));
+ __ movq(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset));
+
+ // Push receiver.
+ __ push(FieldOperand(rbx, JSGeneratorObject::kReceiverOffset));
+
+ // Push holes for arguments to generator function.
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movsxlq(rdx,
+ FieldOperand(rdx,
+ SharedFunctionInfo::kFormalParameterCountOffset));
+ __ LoadRoot(rcx, Heap::kTheHoleValueRootIndex);
+ Label push_argument_holes, push_frame;
+ __ bind(&push_argument_holes);
+ __ subq(rdx, Immediate(1));
+ __ j(carry, &push_frame);
+ __ push(rcx);
+ __ jmp(&push_argument_holes);
+
+ // Enter a new JavaScript frame, and initialize its slots as they were when
+ // the generator was suspended.
+ Label resume_frame;
+ __ bind(&push_frame);
+ __ call(&resume_frame);
+ __ jmp(&done);
+ __ bind(&resume_frame);
+ __ push(rbp); // Caller's frame pointer.
+ __ movq(rbp, rsp);
+ __ push(rsi); // Callee's context.
+ __ push(rdi); // Callee's JS Function.
+
+ // Load the operand stack size.
+ __ movq(rdx, FieldOperand(rbx, JSGeneratorObject::kOperandStackOffset));
+ __ movq(rdx, FieldOperand(rdx, FixedArray::kLengthOffset));
+ __ SmiToInteger32(rdx, rdx);
+
+ // If we are sending a value and there is no operand stack, we can jump back
+ // in directly.
+ if (resume_mode == JSGeneratorObject::NEXT) {
+ Label slow_resume;
+ __ cmpq(rdx, Immediate(0));
+ __ j(not_zero, &slow_resume);
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
+ __ SmiToInteger64(rcx,
+ FieldOperand(rbx, JSGeneratorObject::kContinuationOffset));
+ __ addq(rdx, rcx);
+ __ Move(FieldOperand(rbx, JSGeneratorObject::kContinuationOffset),
+ Smi::FromInt(JSGeneratorObject::kGeneratorExecuting));
+ __ jmp(rdx);
+ __ bind(&slow_resume);
+ }
+
+ // Otherwise, we push holes for the operand stack and call the runtime to fix
+ // up the stack and the handlers.
+ Label push_operand_holes, call_resume;
+ __ bind(&push_operand_holes);
+ __ subq(rdx, Immediate(1));
+ __ j(carry, &call_resume);
+ __ push(rcx);
+ __ jmp(&push_operand_holes);
+ __ bind(&call_resume);
+ __ push(rbx);
+ __ push(result_register());
+ __ Push(Smi::FromInt(resume_mode));
+ __ CallRuntime(Runtime::kResumeJSGeneratorObject, 3);
+ // Not reached: the runtime call returns elsewhere.
+ __ Abort(kGeneratorFailedToResume);
+
+ // Throw error if we attempt to operate on a running generator.
+ __ bind(&wrong_state);
+ __ push(rbx);
+ __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
+
+ __ bind(&done);
+ context()->Plug(result_register());
+}
+
+
+void FullCodeGenerator::EmitCreateIteratorResult(bool done) {
+ Label gc_required;
+ Label allocated;
+
+ Handle<Map> map(isolate()->native_context()->generator_result_map());
+
+ __ Allocate(map->instance_size(), rax, rcx, rdx, &gc_required, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&gc_required);
+ __ Push(Smi::FromInt(map->instance_size()));
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ movq(context_register(),
+ Operand(rbp, StandardFrameConstants::kContextOffset));
+
+ __ bind(&allocated);
+ __ Move(rbx, map);
+ __ pop(rcx);
+ __ Move(rdx, isolate()->factory()->ToBoolean(done));
+ ASSERT_EQ(map->instance_size(), 5 * kPointerSize);
+ __ movq(FieldOperand(rax, HeapObject::kMapOffset), rbx);
+ __ Move(FieldOperand(rax, JSObject::kPropertiesOffset),
+ isolate()->factory()->empty_fixed_array());
+ __ Move(FieldOperand(rax, JSObject::kElementsOffset),
+ isolate()->factory()->empty_fixed_array());
+ __ movq(FieldOperand(rax, JSGeneratorObject::kResultValuePropertyOffset),
+ rcx);
+ __ movq(FieldOperand(rax, JSGeneratorObject::kResultDonePropertyOffset),
+ rdx);
+
+ // Only the value field needs a write barrier, as the other values are in the
+ // root set.
+ __ RecordWriteField(rax, JSGeneratorObject::kResultValuePropertyOffset,
+ rcx, rdx, kDontSaveFPRegs);
+}
+
+
+void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ Literal* key = prop->key()->AsLiteral();
+ __ Move(rcx, key->value());
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
+}
+
+
+void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
+ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
+}
+
+
+void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode,
+ Expression* left,
+ Expression* right) {
+ // Do combined smi check of the operands. Left operand is on the
+ // stack (popped into rdx). Right operand is in rax but moved into
+ // rcx to make the shifts easier.
+ Label done, stub_call, smi_case;
+ __ pop(rdx);
+ __ movq(rcx, rax);
+ __ or_(rax, rdx);
+ JumpPatchSite patch_site(masm_);
+ patch_site.EmitJumpIfSmi(rax, &smi_case, Label::kNear);
+
+ __ bind(&stub_call);
+ __ movq(rax, rcx);
+ BinaryOpStub stub(op, mode);
+ CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET,
+ expr->BinaryOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&smi_case);
+ switch (op) {
+ case Token::SAR:
+ __ SmiShiftArithmeticRight(rax, rdx, rcx);
+ break;
+ case Token::SHL:
+ __ SmiShiftLeft(rax, rdx, rcx);
+ break;
+ case Token::SHR:
+ __ SmiShiftLogicalRight(rax, rdx, rcx, &stub_call);
+ break;
+ case Token::ADD:
+ __ SmiAdd(rax, rdx, rcx, &stub_call);
+ break;
+ case Token::SUB:
+ __ SmiSub(rax, rdx, rcx, &stub_call);
+ break;
+ case Token::MUL:
+ __ SmiMul(rax, rdx, rcx, &stub_call);
+ break;
+ case Token::BIT_OR:
+ __ SmiOr(rax, rdx, rcx);
+ break;
+ case Token::BIT_AND:
+ __ SmiAnd(rax, rdx, rcx);
+ break;
+ case Token::BIT_XOR:
+ __ SmiXor(rax, rdx, rcx);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ __ bind(&done);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
+ OverwriteMode mode) {
+ __ pop(rdx);
+ BinaryOpStub stub(op, mode);
+ JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code.
+ CallIC(stub.GetCode(isolate()), RelocInfo::CODE_TARGET,
+ expr->BinaryOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitAssignment(Expression* expr) {
+ // Invalid left-hand sides are rewritten by the parser to have a 'throw
+ // ReferenceError' on the left-hand side.
+ if (!expr->IsValidLeftHandSide()) {
+ VisitForEffect(expr);
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->AsProperty();
+ if (prop != NULL) {
+ assign_type = (prop->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ switch (assign_type) {
+ case VARIABLE: {
+ Variable* var = expr->AsVariableProxy()->var();
+ EffectContext context(this);
+ EmitVariableAssignment(var, Token::ASSIGN);
+ break;
+ }
+ case NAMED_PROPERTY: {
+ __ push(rax); // Preserve value.
+ VisitForAccumulatorValue(prop->obj());
+ __ movq(rdx, rax);
+ __ pop(rax); // Restore value.
+ __ Move(rcx, prop->key()->AsLiteral()->value());
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic);
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ push(rax); // Preserve value.
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ __ movq(rcx, rax);
+ __ pop(rdx);
+ __ pop(rax); // Restore value.
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic);
+ break;
+ }
+ }
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitVariableAssignment(Variable* var,
+ Token::Value op) {
+ if (var->IsUnallocated()) {
+ // Global var, const, or let.
+ __ Move(rcx, var->name());
+ __ movq(rdx, GlobalObjectOperand());
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ } else if (op == Token::INIT_CONST) {
+ // Const initializers need a write barrier.
+ ASSERT(!var->IsParameter()); // No const parameters.
+ if (var->IsStackLocal()) {
+ Label skip;
+ __ movq(rdx, StackOperand(var));
+ __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &skip);
+ __ movq(StackOperand(var), rax);
+ __ bind(&skip);
+ } else {
+ ASSERT(var->IsContextSlot() || var->IsLookupSlot());
+ // Like var declarations, const declarations are hoisted to function
+ // scope. However, unlike var initializers, const initializers are
+ // able to drill a hole to that function context, even from inside a
+ // 'with' context. We thus bypass the normal static scope lookup for
+ // var->IsContextSlot().
+ __ push(rax);
+ __ push(rsi);
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
+ }
+
+ } 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(language_mode()));
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ } else {
+ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
+ Label assign;
+ MemOperand location = VarOperand(var, rcx);
+ __ movq(rdx, location);
+ __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &assign, Label::kNear);
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kThrowReferenceError, 1);
+ __ bind(&assign);
+ __ movq(location, rax);
+ if (var->IsContextSlot()) {
+ __ movq(rdx, rax);
+ __ RecordWriteContextSlot(
+ rcx, Context::SlotOffset(var->index()), rdx, rbx, kDontSaveFPRegs);
+ }
+ }
+
+ } 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 (generate_debug_code_ && op == Token::INIT_LET) {
+ // Check for an uninitialized let binding.
+ __ movq(rdx, location);
+ __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex);
+ __ Check(equal, kLetBindingReInitialization);
+ }
+ // Perform the assignment.
+ __ movq(location, rax);
+ if (var->IsContextSlot()) {
+ __ movq(rdx, rax);
+ __ 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(language_mode()));
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ }
+ }
+ // Non-initializing assignments to consts are ignored.
+}
+
+
+void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
+ // Assignment to a property, using a named store IC.
+ Property* prop = expr->target()->AsProperty();
+ ASSERT(prop != NULL);
+ ASSERT(prop->key()->AsLiteral() != NULL);
+
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ __ Move(rcx, prop->key()->AsLiteral()->value());
+ __ pop(rdx);
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
+
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
+ // Assignment to a property, using a keyed store IC.
+
+ __ pop(rcx);
+ __ pop(rdx);
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
+
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::VisitProperty(Property* expr) {
+ Comment cmnt(masm_, "[ Property");
+ Expression* key = expr->key();
+
+ if (key->IsPropertyName()) {
+ VisitForAccumulatorValue(expr->obj());
+ EmitNamedPropertyLoad(expr);
+ PrepareForBailoutForId(expr->LoadId(), TOS_REG);
+ context()->Plug(rax);
+ } else {
+ VisitForStackValue(expr->obj());
+ VisitForAccumulatorValue(expr->key());
+ __ pop(rdx);
+ EmitKeyedPropertyLoad(expr);
+ context()->Plug(rax);
+ }
+}
+
+
+void FullCodeGenerator::CallIC(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id) {
+ ic_total_count_++;
+ __ call(code, rmode, ast_id);
+}
+
+
+void FullCodeGenerator::EmitCallWithIC(Call* expr,
+ Handle<Object> name,
+ RelocInfo::Mode mode) {
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ __ Move(rcx, name);
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ CallIC(ic, mode, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
+ Expression* key) {
+ // Load the key.
+ VisitForAccumulatorValue(key);
+
+ // Swap the name of the function and the receiver on the stack to follow
+ // the calling convention for call ICs.
+ __ pop(rcx);
+ __ push(rax);
+ __ push(rcx);
+
+ // Load the arguments.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
+ __ movq(rcx, Operand(rsp, (arg_count + 1) * kPointerSize)); // Key.
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, rax); // Drop the key still on the stack.
+}
+
+
+void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
+ // Code common for calls using the call stub.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+
+ // Record call targets in unoptimized code.
+ flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<Cell> cell = isolate()->factory()->NewCell(uninitialized);
+ RecordTypeFeedbackCell(expr->CallFeedbackId(), cell);
+ __ Move(rbx, cell);
+
+ CallFunctionStub stub(arg_count, flags);
+ __ movq(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
+ __ CallStub(&stub, expr->CallFeedbackId());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ // Discard the function left on TOS.
+ context()->DropAndPlug(1, rax);
+}
+
+
+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));
+ } else {
+ __ PushRoot(Heap::kUndefinedValueRootIndex);
+ }
+
+ // Push the receiver of the enclosing function and do runtime call.
+ __ push(Operand(rbp, (2 + info_->scope()->num_parameters()) * kPointerSize));
+
+ // Push the language mode.
+ __ Push(Smi::FromInt(language_mode()));
+
+ // 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);
+}
+
+
+void FullCodeGenerator::VisitCall(Call* expr) {
+#ifdef DEBUG
+ // We want to verify that RecordJSReturnSite gets called on all paths
+ // through this function. Avoid early returns.
+ expr->return_is_recorded_ = false;
+#endif
+
+ Comment cmnt(masm_, "[ Call");
+ Expression* callee = expr->expression();
+ VariableProxy* proxy = callee->AsVariableProxy();
+ Property* property = callee->AsProperty();
+
+ if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
+ // In a call to eval, we first call %ResolvePossiblyDirectEval to
+ // resolve the function we need to call and the receiver of the call.
+ // Then we call the resolved function using the given arguments.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope pos_scope(masm()->positions_recorder());
+ VisitForStackValue(callee);
+ __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot.
+
+ // Push the arguments.
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Push a copy of the function (found below the arguments) and resolve
+ // eval.
+ __ push(Operand(rsp, (arg_count + 1) * kPointerSize));
+ 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.
+ __ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx);
+ __ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
+ }
+ // 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.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, rax);
+ } else if (proxy != NULL && proxy->var()->IsUnallocated()) {
+ // Call to a global variable. Push global object as receiver for the
+ // call IC lookup.
+ __ push(GlobalObjectOperand());
+ EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
+ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
+ // Call to a lookup slot (dynamically introduced variable).
+ Label slow, done;
+
+ { PreservePositionScope scope(masm()->positions_recorder());
+ // Generate code for loading from variables potentially shadowed by
+ // eval-introduced variables.
+ EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done);
+ }
+ __ bind(&slow);
+ // Call the runtime to find the function to call (returned in rax) and
+ // the object holding it (returned in rdx).
+ __ push(context_register());
+ __ Push(proxy->name());
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ push(rax); // Function.
+ __ push(rdx); // Receiver.
+
+ // If fast case code has been generated, emit code to push the function
+ // and receiver and have the slow path jump around this code.
+ if (done.is_linked()) {
+ Label call;
+ __ jmp(&call, Label::kNear);
+ __ bind(&done);
+ // Push function.
+ __ push(rax);
+ // The receiver is implicitly the global receiver. Indicate this by
+ // passing the hole to the call function stub.
+ __ PushRoot(Heap::kTheHoleValueRootIndex);
+ __ bind(&call);
+ }
+
+ // The receiver is either the global receiver or an object found by
+ // LoadContextSlot. That object could be the hole if the receiver is
+ // implicitly the global object.
+ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
+ } else if (property != NULL) {
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(property->obj());
+ }
+ if (property->key()->IsPropertyName()) {
+ EmitCallWithIC(expr,
+ property->key()->AsLiteral()->value(),
+ RelocInfo::CODE_TARGET);
+ } else {
+ EmitKeyedCallWithIC(expr, property->key());
+ }
+ } else {
+ // Call to an arbitrary expression not handled specially above.
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(callee);
+ }
+ // Load global receiver object.
+ __ movq(rbx, GlobalObjectOperand());
+ __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+ // Emit function call.
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ }
+
+#ifdef DEBUG
+ // RecordJSReturnSite should have been called.
+ ASSERT(expr->return_is_recorded_);
+#endif
+}
+
+
+void FullCodeGenerator::VisitCallNew(CallNew* expr) {
+ Comment cmnt(masm_, "[ CallNew");
+ // According to ECMA-262, section 11.2.2, page 44, the function
+ // expression in new calls must be evaluated before the
+ // arguments.
+
+ // Push constructor on the stack. If it's not a function it's used as
+ // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
+ // ignored.
+ VisitForStackValue(expr->expression());
+
+ // Push the arguments ("left-to-right") on the stack.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Call the construct call builtin that handles allocation and
+ // constructor invocation.
+ SetSourcePosition(expr->position());
+
+ // Load function and argument count into rdi and rax.
+ __ Set(rax, arg_count);
+ __ movq(rdi, Operand(rsp, arg_count * kPointerSize));
+
+ // Record call targets in unoptimized code, but not in the snapshot.
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<Cell> cell = isolate()->factory()->NewCell(uninitialized);
+ RecordTypeFeedbackCell(expr->CallNewFeedbackId(), cell);
+ __ Move(rbx, cell);
+
+ CallConstructStub stub(RECORD_CALL_TARGET);
+ __ Call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL);
+ PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ JumpIfSmi(rax, if_true);
+ __ jmp(if_false);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Condition non_negative_smi = masm()->CheckNonNegativeSmi(rax);
+ Split(non_negative_smi, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsObject(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(rax, if_false);
+ __ CompareRoot(rax, Heap::kNullValueRootIndex);
+ __ j(equal, if_true);
+ __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+ // Undetectable objects behave like undefined when tested with typeof.
+ __ testb(FieldOperand(rbx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ __ j(not_zero, if_false);
+ __ movzxbq(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
+ __ cmpq(rbx, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ __ j(below, if_false);
+ __ cmpq(rbx, Immediate(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ 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(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(rax, if_false);
+ __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rbx);
+ 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(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(rax, if_false);
+ __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+ __ testb(FieldOperand(rbx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(not_zero, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
+ CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ AssertNotSmi(rax);
+
+ // Check whether this map has already been checked to be safe for default
+ // valueOf.
+ __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+ __ testb(FieldOperand(rbx, Map::kBitField2Offset),
+ Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ j(not_zero, if_true);
+
+ // Check for fast case object. Generate false result for slow case object.
+ __ movq(rcx, FieldOperand(rax, JSObject::kPropertiesOffset));
+ __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
+ __ CompareRoot(rcx, Heap::kHashTableMapRootIndex);
+ __ j(equal, if_false);
+
+ // Look for valueOf string in the descriptor array, and indicate false if
+ // found. Since we omit an enumeration index check, if it is added via a
+ // transition that shares its descriptor array, this is a false positive.
+ Label entry, loop, done;
+
+ // Skip loop if no descriptors are valid.
+ __ NumberOfOwnDescriptors(rcx, rbx);
+ __ cmpq(rcx, Immediate(0));
+ __ j(equal, &done);
+
+ __ LoadInstanceDescriptors(rbx, rbx);
+ // rbx: descriptor array.
+ // rcx: valid entries in the descriptor array.
+ // Calculate the end of the descriptor array.
+ __ imul(rcx, rcx, Immediate(DescriptorArray::kDescriptorSize));
+ SmiIndex index = masm_->SmiToIndex(rdx, rcx, kPointerSizeLog2);
+ __ lea(rcx,
+ Operand(
+ rbx, index.reg, index.scale, DescriptorArray::kFirstOffset));
+ // Calculate location of the first key name.
+ __ addq(rbx, Immediate(DescriptorArray::kFirstOffset));
+ // Loop through all the keys in the descriptor array. If one of these is the
+ // internalized string "valueOf" the result is false.
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ movq(rdx, FieldOperand(rbx, 0));
+ __ Cmp(rdx, isolate()->factory()->value_of_string());
+ __ j(equal, if_false);
+ __ addq(rbx, Immediate(DescriptorArray::kDescriptorSize * kPointerSize));
+ __ bind(&entry);
+ __ cmpq(rbx, rcx);
+ __ j(not_equal, &loop);
+
+ __ bind(&done);
+ // Reload map as register rbx was used as temporary above.
+ __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+
+ // If a valueOf property is not found on the object check that its
+ // prototype is the un-modified String prototype. If not result is false.
+ __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
+ __ testq(rcx, Immediate(kSmiTagMask));
+ __ j(zero, if_false);
+ __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
+ __ movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kNativeContextOffset));
+ __ cmpq(rcx,
+ ContextOperand(rdx, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
+ __ j(not_equal, if_false);
+ // Set the bit in the map to indicate that it has been checked safe for
+ // default valueOf and set true result.
+ __ or_(FieldOperand(rbx, Map::kBitField2Offset),
+ Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ jmp(if_true);
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(rax, if_false);
+ __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(rax, if_false);
+ __ CmpObjectType(rax, JS_ARRAY_TYPE, rbx);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ JumpIfSmi(rax, if_false);
+ __ CmpObjectType(rax, JS_REGEXP_TYPE, rbx);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+
+void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+
+ 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);
+
+ // Get the frame pointer for the calling frame.
+ __ movq(rax, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+
+ // Skip the arguments adaptor frame if it exists.
+ Label check_frame_marker;
+ __ Cmp(Operand(rax, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(not_equal, &check_frame_marker);
+ __ movq(rax, Operand(rax, StandardFrameConstants::kCallerFPOffset));
+
+ // Check the marker in the calling frame.
+ __ bind(&check_frame_marker);
+ __ Cmp(Operand(rax, StandardFrameConstants::kMarkerOffset),
+ Smi::FromInt(StackFrame::CONSTRUCT));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ // Load the two objects into registers and perform the comparison.
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ 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);
+
+ __ pop(rbx);
+ __ cmpq(rax, rbx);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ Split(equal, if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitArguments(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ // ArgumentsAccessStub expects the key in rdx and the formal
+ // parameter count in rax.
+ VisitForAccumulatorValue(args->at(0));
+ __ movq(rdx, rax);
+ __ Move(rax, Smi::FromInt(info_->scope()->num_parameters()));
+ ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
+ __ CallStub(&stub);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+
+ Label exit;
+ // Get the number of formal parameters.
+ __ Move(rax, Smi::FromInt(info_->scope()->num_parameters()));
+
+ // Check if the calling frame is an arguments adaptor frame.
+ __ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ Cmp(Operand(rbx, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(not_equal, &exit, Label::kNear);
+
+ // Arguments adaptor case: Read the arguments length from the
+ // adaptor frame.
+ __ movq(rax, Operand(rbx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ __ bind(&exit);
+ __ AssertSmi(rax);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitClassOf(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ Label done, null, function, non_function_constructor;
+
+ VisitForAccumulatorValue(args->at(0));
+
+ // If the object is a smi, we return null.
+ __ JumpIfSmi(rax, &null);
+
+ // 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);
+ 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);
+
+ // rax now contains the constructor function. Grab the
+ // instance class name from there.
+ __ movq(rax, FieldOperand(rax, JSFunction::kSharedFunctionInfoOffset));
+ __ movq(rax, FieldOperand(rax, SharedFunctionInfo::kInstanceClassNameOffset));
+ __ jmp(&done);
+
+ // Functions have class 'Function'.
+ __ bind(&function);
+ __ Move(rax, isolate()->factory()->function_class_string());
+ __ jmp(&done);
+
+ // Objects with a non-function constructor have class 'Object'.
+ __ bind(&non_function_constructor);
+ __ Move(rax, isolate()->factory()->Object_string());
+ __ jmp(&done);
+
+ // Non-JS objects have class null.
+ __ bind(&null);
+ __ LoadRoot(rax, Heap::kNullValueRootIndex);
+
+ // All done.
+ __ bind(&done);
+
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitLog(CallRuntime* expr) {
+ // Conditionally generate a log call.
+ // Args:
+ // 0 (literal string): The type of logging (corresponds to the flags).
+ // This is used to determine whether or not to generate the log call.
+ // 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);
+ }
+ // Finally, we're expected to leave a value on the top of the stack.
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) {
+ ASSERT(expr->arguments()->length() == 0);
+
+ Label slow_allocate_heapnumber;
+ Label heapnumber_allocated;
+
+ __ AllocateHeapNumber(rbx, rcx, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ // Allocate a heap number.
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ movq(rbx, rax);
+
+ __ bind(&heapnumber_allocated);
+
+ // Return a random uint32 number in rax.
+ // The fresh HeapNumber is in rbx, which is callee-save on both x64 ABIs.
+ __ PrepareCallCFunction(1);
+ __ movq(arg_reg_1,
+ ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX));
+ __ movq(arg_reg_1,
+ FieldOperand(arg_reg_1, GlobalObject::kNativeContextOffset));
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
+
+ // 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(xmm1, rcx);
+ __ movd(xmm0, rax);
+ __ cvtss2sd(xmm1, xmm1);
+ __ xorps(xmm0, xmm1);
+ __ subsd(xmm0, xmm1);
+ __ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0);
+
+ __ movq(rax, rbx);
+ context()->Plug(rax);
+}
+
+
+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);
+ context()->Plug(rax);
+}
+
+
+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);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitValueOf(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label done;
+ // If the object is a smi return the object.
+ __ JumpIfSmi(rax, &done);
+ // If the object is not a value type, return the object.
+ __ CmpObjectType(rax, JS_VALUE_TYPE, rbx);
+ __ j(not_equal, &done);
+ __ movq(rax, FieldOperand(rax, JSValue::kValueOffset));
+
+ __ bind(&done);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ ASSERT_NE(NULL, args->at(1)->AsLiteral());
+ Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->value()));
+
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label runtime, done, not_date_object;
+ Register object = rax;
+ Register result = rax;
+ Register scratch = rcx;
+
+ __ JumpIfSmi(object, &not_date_object);
+ __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+ __ j(not_equal, &not_date_object);
+
+ if (index->value() == 0) {
+ __ movq(result, FieldOperand(object, JSDate::kValueOffset));
+ __ jmp(&done);
+ } else {
+ if (index->value() < JSDate::kFirstUncachedField) {
+ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+ Operand stamp_operand = __ ExternalOperand(stamp);
+ __ movq(scratch, stamp_operand);
+ __ cmpq(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
+ __ j(not_equal, &runtime, Label::kNear);
+ __ movq(result, FieldOperand(object, JSDate::kValueOffset +
+ kPointerSize * index->value()));
+ __ jmp(&done);
+ }
+ __ bind(&runtime);
+ __ PrepareCallCFunction(2);
+ __ movq(arg_reg_1, object);
+ __ movq(arg_reg_2, index, RelocInfo::NONE64);
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ jmp(&done);
+ }
+
+ __ bind(&not_date_object);
+ __ CallRuntime(Runtime::kThrowNotDateError, 0);
+ __ bind(&done);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitSeqStringSetCharCheck(Register string,
+ Register index,
+ Register value,
+ uint32_t encoding_mask) {
+ __ Check(masm()->CheckSmi(index), kNonSmiIndex);
+ __ Check(masm()->CheckSmi(value), kNonSmiValue);
+
+ __ SmiCompare(index, FieldOperand(string, String::kLengthOffset));
+ __ Check(less, kIndexIsTooLarge);
+
+ __ SmiCompare(index, Smi::FromInt(0));
+ __ Check(greater_equal, kIndexIsNegative);
+
+ __ push(value);
+ __ movq(value, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzxbq(value, FieldOperand(value, Map::kInstanceTypeOffset));
+
+ __ andb(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
+ __ cmpq(value, Immediate(encoding_mask));
+ __ Check(equal, kUnexpectedStringType);
+ __ pop(value);
+}
+
+
+void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(3, args->length());
+
+ Register string = rax;
+ Register index = rbx;
+ Register value = rcx;
+
+ VisitForStackValue(args->at(1)); // index
+ VisitForStackValue(args->at(2)); // value
+ __ pop(value);
+ __ pop(index);
+ VisitForAccumulatorValue(args->at(0)); // string
+
+ if (FLAG_debug_code) {
+ static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
+ EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
+ }
+
+ __ SmiToInteger32(value, value);
+ __ SmiToInteger32(index, index);
+ __ movb(FieldOperand(string, index, times_1, SeqOneByteString::kHeaderSize),
+ value);
+ context()->Plug(string);
+}
+
+
+void FullCodeGenerator::EmitTwoByteSeqStringSetChar(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(3, args->length());
+
+ Register string = rax;
+ Register index = rbx;
+ Register value = rcx;
+
+ VisitForStackValue(args->at(1)); // index
+ VisitForStackValue(args->at(2)); // value
+ __ pop(value);
+ __ pop(index);
+ VisitForAccumulatorValue(args->at(0)); // string
+
+ if (FLAG_debug_code) {
+ static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
+ EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
+ }
+
+ __ SmiToInteger32(value, value);
+ __ SmiToInteger32(index, index);
+ __ movw(FieldOperand(string, index, times_2, SeqTwoByteString::kHeaderSize),
+ value);
+ context()->Plug(rax);
+}
+
+
+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::ON_STACK);
+ __ CallStub(&stub);
+ context()->Plug(rax);
+}
+
+
+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(rbx); // rax = value. rbx = object.
+
+ Label done;
+ // If the object is a smi, return the value.
+ __ JumpIfSmi(rbx, &done);
+
+ // If the object is not a value type, return the value.
+ __ CmpObjectType(rbx, JS_VALUE_TYPE, rcx);
+ __ j(not_equal, &done);
+
+ // Store the value.
+ __ movq(FieldOperand(rbx, JSValue::kValueOffset), rax);
+ // Update the write barrier. Save the value as it will be
+ // overwritten by the write barrier code and is needed afterward.
+ __ movq(rdx, rax);
+ __ RecordWriteField(rbx, JSValue::kValueOffset, rdx, rcx, kDontSaveFPRegs);
+
+ __ bind(&done);
+ context()->Plug(rax);
+}
+
+
+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));
+
+ NumberToStringStub stub;
+ __ CallStub(&stub);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label done;
+ StringCharFromCodeGenerator generator(rax, rbx);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(rbx);
+}
+
+
+void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ Register object = rbx;
+ Register index = rax;
+ Register result = rdx;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharCodeAtGenerator generator(object,
+ index,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ LoadRoot(result, Heap::kNanValueRootIndex);
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Move the undefined value into the result register, which will
+ // trigger conversion.
+ __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ Register object = rbx;
+ Register index = rax;
+ Register scratch = rdx;
+ Register result = rax;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharAtGenerator generator(object,
+ index,
+ scratch,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // the empty string.
+ __ LoadRoot(result, Heap::kempty_stringRootIndex);
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ Move(result, Smi::FromInt(0));
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
+}
+
+
+void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(STRING_ADD_CHECK_BOTH);
+ __ CallStub(&stub);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringCompareStub stub;
+ __ CallStub(&stub);
+ context()->Plug(rax);
+}
+
+
+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);
+ context()->Plug(rax);
+}
+
+
+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);
+ context()->Plug(rax);
+}
+
+
+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);
+ context()->Plug(rax);
+}
+
+
+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);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() >= 2);
+
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ VisitForAccumulatorValue(args->last()); // Function.
+
+ Label runtime, done;
+ // Check for non-function argument (including proxy).
+ __ JumpIfSmi(rax, &runtime);
+ __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx);
+ __ j(not_equal, &runtime);
+
+ // 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(&runtime);
+ __ push(rax);
+ __ CallRuntime(Runtime::kCall, args->length());
+ __ bind(&done);
+
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) {
+ 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);
+ context()->Plug(rax);
+}
+
+
+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()->value()))->value();
+
+ Handle<FixedArray> jsfunction_result_caches(
+ isolate()->native_context()->jsfunction_result_caches());
+ if (jsfunction_result_caches->length() <= cache_id) {
+ __ Abort(kAttemptToUseUndefinedCache);
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ context()->Plug(rax);
+ return;
+ }
+
+ VisitForAccumulatorValue(args->at(1));
+
+ Register key = rax;
+ Register cache = rbx;
+ Register tmp = rcx;
+ __ movq(cache, ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX));
+ __ movq(cache,
+ FieldOperand(cache, GlobalObject::kNativeContextOffset));
+ __ movq(cache,
+ ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+ __ movq(cache,
+ FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
+
+ Label done, not_found;
+ // tmp now holds finger offset as a smi.
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ movq(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
+ SmiIndex index =
+ __ SmiToIndex(kScratchRegister, tmp, kPointerSizeLog2);
+ __ cmpq(key, FieldOperand(cache,
+ index.reg,
+ index.scale,
+ FixedArray::kHeaderSize));
+ __ j(not_equal, &not_found, Label::kNear);
+ __ movq(rax, FieldOperand(cache,
+ index.reg,
+ index.scale,
+ FixedArray::kHeaderSize + kPointerSize));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&not_found);
+ // Call runtime to perform the lookup.
+ __ push(cache);
+ __ push(key);
+ __ CallRuntime(Runtime::kGetFromCache, 2);
+
+ __ bind(&done);
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT_EQ(2, args->length());
+
+ Register right = rax;
+ Register left = rbx;
+ Register tmp = rcx;
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+ __ pop(left);
+
+ Label done, fail, ok;
+ __ cmpq(left, right);
+ __ j(equal, &ok, Label::kNear);
+ // Fail if either is a non-HeapObject.
+ Condition either_smi = masm()->CheckEitherSmi(left, right, tmp);
+ __ j(either_smi, &fail, Label::kNear);
+ __ j(zero, &fail, Label::kNear);
+ __ movq(tmp, FieldOperand(left, HeapObject::kMapOffset));
+ __ cmpb(FieldOperand(tmp, Map::kInstanceTypeOffset),
+ Immediate(JS_REGEXP_TYPE));
+ __ j(not_equal, &fail, Label::kNear);
+ __ cmpq(tmp, FieldOperand(right, HeapObject::kMapOffset));
+ __ j(not_equal, &fail, Label::kNear);
+ __ movq(tmp, FieldOperand(left, JSRegExp::kDataOffset));
+ __ cmpq(tmp, FieldOperand(right, JSRegExp::kDataOffset));
+ __ j(equal, &ok, Label::kNear);
+ __ bind(&fail);
+ __ Move(rax, isolate()->factory()->false_value());
+ __ jmp(&done, Label::kNear);
+ __ bind(&ok);
+ __ Move(rax, isolate()->factory()->true_value());
+ __ bind(&done);
+
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ 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);
+
+ __ testl(FieldOperand(rax, String::kHashFieldOffset),
+ Immediate(String::kContainsCachedArrayIndexMask));
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ j(zero, if_true);
+ __ jmp(if_false);
+
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 1);
+ VisitForAccumulatorValue(args->at(0));
+
+ __ AssertString(rax);
+
+ __ movl(rax, FieldOperand(rax, String::kHashFieldOffset));
+ ASSERT(String::kHashShift >= kSmiTagSize);
+ __ IndexFromHash(rax, rax);
+
+ context()->Plug(rax);
+}
+
+
+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));
+ // Load this to rax (= array)
+ VisitForAccumulatorValue(args->at(0));
+ // All aliases of the same register have disjoint lifetimes.
+ Register array = rax;
+ Register elements = no_reg; // Will be rax.
+
+ Register index = rdx;
+
+ Register string_length = rcx;
+
+ Register string = rsi;
+
+ Register scratch = rbx;
+
+ Register array_length = rdi;
+ Register result_pos = no_reg; // Will be rdi.
+
+ Operand separator_operand = Operand(rsp, 2 * kPointerSize);
+ Operand result_operand = Operand(rsp, 1 * kPointerSize);
+ Operand array_length_operand = Operand(rsp, 0 * kPointerSize);
+ // Separator operand is already pushed. Make room for the two
+ // other stack fields, and clear the direction flag in anticipation
+ // of calling CopyBytes.
+ __ subq(rsp, Immediate(2 * kPointerSize));
+ __ cld();
+ // Check that the array is a JSArray
+ __ JumpIfSmi(array, &bailout);
+ __ CmpObjectType(array, JS_ARRAY_TYPE, scratch);
+ __ j(not_equal, &bailout);
+
+ // Check that the array has fast elements.
+ __ CheckFastElements(scratch, &bailout);
+
+ // Array has fast elements, so its length must be a smi.
+ // If the array has length zero, return the empty string.
+ __ movq(array_length, FieldOperand(array, JSArray::kLengthOffset));
+ __ SmiCompare(array_length, Smi::FromInt(0));
+ __ j(not_zero, &non_trivial_array);
+ __ LoadRoot(rax, Heap::kempty_stringRootIndex);
+ __ jmp(&return_result);
+
+ // Save the array length on the stack.
+ __ bind(&non_trivial_array);
+ __ SmiToInteger32(array_length, array_length);
+ __ movl(array_length_operand, array_length);
+
+ // Save the FixedArray containing array's elements.
+ // End of array's live range.
+ elements = array;
+ __ movq(elements, FieldOperand(array, JSArray::kElementsOffset));
+ array = no_reg;
+
+
+ // Check that all array elements are sequential ASCII strings, and
+ // accumulate the sum of their lengths, as a smi-encoded value.
+ __ Set(index, 0);
+ __ Set(string_length, 0);
+ // Loop condition: while (index < array_length).
+ // Live loop registers: index(int32), array_length(int32), string(String*),
+ // scratch, string_length(int32), elements(FixedArray*).
+ if (generate_debug_code_) {
+ __ cmpq(index, array_length);
+ __ Assert(below, kNoEmptyArraysHereInEmitFastAsciiArrayJoin);
+ }
+ __ bind(&loop);
+ __ movq(string, FieldOperand(elements,
+ index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ JumpIfSmi(string, &bailout);
+ __ movq(scratch, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzxbl(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ __ andb(scratch, Immediate(
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
+ __ cmpb(scratch, Immediate(kStringTag | kOneByteStringTag | kSeqStringTag));
+ __ j(not_equal, &bailout);
+ __ AddSmiField(string_length,
+ FieldOperand(string, SeqOneByteString::kLengthOffset));
+ __ j(overflow, &bailout);
+ __ incl(index);
+ __ cmpl(index, array_length);
+ __ j(less, &loop);
+
+ // Live registers:
+ // string_length: Sum of string lengths.
+ // elements: FixedArray of strings.
+ // index: Array length.
+ // array_length: Array length.
+
+ // If array_length is 1, return elements[0], a string.
+ __ cmpl(array_length, Immediate(1));
+ __ j(not_equal, &not_size_one_array);
+ __ movq(rax, FieldOperand(elements, FixedArray::kHeaderSize));
+ __ jmp(&return_result);
+
+ __ bind(&not_size_one_array);
+
+ // End of array_length live range.
+ result_pos = array_length;
+ array_length = no_reg;
+
+ // Live registers:
+ // string_length: Sum of string lengths.
+ // elements: FixedArray of strings.
+ // index: Array length.
+
+ // Check that the separator is a sequential ASCII string.
+ __ movq(string, separator_operand);
+ __ JumpIfSmi(string, &bailout);
+ __ movq(scratch, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzxbl(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ __ andb(scratch, Immediate(
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
+ __ cmpb(scratch, Immediate(kStringTag | kOneByteStringTag | kSeqStringTag));
+ __ j(not_equal, &bailout);
+
+ // Live registers:
+ // string_length: Sum of string lengths.
+ // elements: FixedArray of strings.
+ // index: Array length.
+ // string: Separator string.
+
+ // Add (separator length times (array_length - 1)) to string_length.
+ __ SmiToInteger32(scratch,
+ FieldOperand(string, SeqOneByteString::kLengthOffset));
+ __ decl(index);
+ __ imull(scratch, index);
+ __ j(overflow, &bailout);
+ __ addl(string_length, scratch);
+ __ j(overflow, &bailout);
+
+ // Live registers and stack values:
+ // string_length: Total length of result string.
+ // elements: FixedArray of strings.
+ __ AllocateAsciiString(result_pos, string_length, scratch,
+ index, string, &bailout);
+ __ movq(result_operand, result_pos);
+ __ lea(result_pos, FieldOperand(result_pos, SeqOneByteString::kHeaderSize));
+
+ __ movq(string, separator_operand);
+ __ SmiCompare(FieldOperand(string, SeqOneByteString::kLengthOffset),
+ Smi::FromInt(1));
+ __ j(equal, &one_char_separator);
+ __ j(greater, &long_separator);
+
+
+ // Empty separator case:
+ __ Set(index, 0);
+ __ movl(scratch, array_length_operand);
+ __ jmp(&loop_1_condition);
+ // Loop condition: while (index < array_length).
+ __ bind(&loop_1);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+ // elements: the FixedArray of strings we are joining.
+ // scratch: array length.
+
+ // Get string = array[index].
+ __ movq(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ SmiToInteger32(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ lea(string,
+ FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ CopyBytes(result_pos, string, string_length);
+ __ incl(index);
+ __ bind(&loop_1_condition);
+ __ cmpl(index, scratch);
+ __ j(less, &loop_1); // Loop while (index < array_length).
+ __ jmp(&done);
+
+ // Generic bailout code used from several places.
+ __ bind(&bailout);
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ jmp(&return_result);
+
+
+ // One-character separator case
+ __ bind(&one_char_separator);
+ // Get the separator ASCII character value.
+ // Register "string" holds the separator.
+ __ movzxbl(scratch, FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ Set(index, 0);
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&loop_2_entry);
+ // Loop condition: while (index < length).
+ __ bind(&loop_2);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // elements: The FixedArray of strings we are joining.
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+ // scratch: Separator character.
+
+ // Copy the separator character to the result.
+ __ movb(Operand(result_pos, 0), scratch);
+ __ incq(result_pos);
+
+ __ bind(&loop_2_entry);
+ // Get string = array[index].
+ __ movq(string, FieldOperand(elements, index,
+ times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ SmiToInteger32(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ lea(string,
+ FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ CopyBytes(result_pos, string, string_length);
+ __ incl(index);
+ __ cmpl(index, array_length_operand);
+ __ j(less, &loop_2); // End while (index < length).
+ __ jmp(&done);
+
+
+ // Long separator case (separator is more than one character).
+ __ bind(&long_separator);
+
+ // Make elements point to end of elements array, and index
+ // count from -array_length to zero, so we don't need to maintain
+ // a loop limit.
+ __ movl(index, array_length_operand);
+ __ lea(elements, FieldOperand(elements, index, times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ neg(index);
+
+ // Replace separator string with pointer to its first character, and
+ // make scratch be its length.
+ __ movq(string, separator_operand);
+ __ SmiToInteger32(scratch,
+ FieldOperand(string, String::kLengthOffset));
+ __ lea(string,
+ FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ movq(separator_operand, string);
+
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator
+ __ jmp(&loop_3_entry);
+ // Loop condition: while (index < length).
+ __ bind(&loop_3);
+ // Each iteration of the loop concatenates one string to the result.
+ // Live values in registers:
+ // index: which element of the elements array we are adding to the result.
+ // result_pos: the position to which we are currently copying characters.
+ // scratch: Separator length.
+ // separator_operand (rsp[0x10]): Address of first char of separator.
+
+ // Copy the separator to the result.
+ __ movq(string, separator_operand);
+ __ movl(string_length, scratch);
+ __ CopyBytes(result_pos, string, string_length, 2);
+
+ __ bind(&loop_3_entry);
+ // Get string = array[index].
+ __ movq(string, Operand(elements, index, times_pointer_size, 0));
+ __ SmiToInteger32(string_length,
+ FieldOperand(string, String::kLengthOffset));
+ __ lea(string,
+ FieldOperand(string, SeqOneByteString::kHeaderSize));
+ __ CopyBytes(result_pos, string, string_length);
+ __ incq(index);
+ __ j(not_equal, &loop_3); // Loop while (index < 0).
+
+ __ bind(&done);
+ __ movq(rax, result_operand);
+
+ __ bind(&return_result);
+ // Drop temp values from the stack, and restore context register.
+ __ addq(rsp, Immediate(3 * kPointerSize));
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
+ Handle<String> name = expr->name();
+ if (name->length() > 0 && name->Get(0) == '_') {
+ Comment cmnt(masm_, "[ InlineRuntimeCall");
+ EmitInlineRuntimeCall(expr);
+ return;
+ }
+
+ Comment cmnt(masm_, "[ CallRuntime");
+ ZoneList<Expression*>* args = expr->arguments();
+
+ if (expr->is_jsruntime()) {
+ // Prepare for calling JS runtime function.
+ __ movq(rax, GlobalObjectOperand());
+ __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset));
+ }
+
+ // Push the arguments ("left-to-right").
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ if (expr->is_jsruntime()) {
+ // Call the JS runtime function using a call IC.
+ __ Move(rcx, expr->name());
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
+ CallIC(ic, mode, expr->CallRuntimeFeedbackId());
+ // Restore context register.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ } else {
+ __ CallRuntime(expr->function(), arg_count);
+ }
+ context()->Plug(rax);
+}
+
+
+void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
+ switch (expr->op()) {
+ case Token::DELETE: {
+ Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
+ Property* property = expr->expression()->AsProperty();
+ VariableProxy* proxy = expr->expression()->AsVariableProxy();
+
+ if (property != NULL) {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ 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(language_mode() == CLASSIC_MODE || var->is_this());
+ if (var->IsUnallocated()) {
+ __ push(GlobalObjectOperand());
+ __ Push(var->name());
+ __ Push(Smi::FromInt(kNonStrictMode));
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
+ context()->Plug(rax);
+ } else if (var->IsStackAllocated() || var->IsContextSlot()) {
+ // Result of deleting non-global variables is false. 'this' is
+ // not really a variable, though we implement it as one. The
+ // subexpression does not have side effects.
+ context()->Plug(var->is_this());
+ } else {
+ // Non-global variable. Call the runtime to try to delete from the
+ // context where the variable was introduced.
+ __ push(context_register());
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kDeleteContextSlot, 2);
+ context()->Plug(rax);
+ }
+ } else {
+ // Result of deleting non-property, non-variable reference is true.
+ // The subexpression may have side effects.
+ VisitForEffect(expr->expression());
+ context()->Plug(true);
+ }
+ break;
+ }
+
+ case Token::VOID: {
+ Comment cmnt(masm_, "[ UnaryOperation (VOID)");
+ VisitForEffect(expr->expression());
+ context()->Plug(Heap::kUndefinedValueRootIndex);
+ break;
+ }
+
+ case Token::NOT: {
+ Comment cmnt(masm_, "[ UnaryOperation (NOT)");
+ if (context()->IsEffect()) {
+ // 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 {
+ // 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;
+ }
+
+ case Token::TYPEOF: {
+ Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
+ { StackValueContext context(this);
+ VisitForTypeofValue(expr->expression());
+ }
+ __ CallRuntime(Runtime::kTypeof, 1);
+ context()->Plug(rax);
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
+ Comment cmnt(masm_, "[ CountOperation");
+ SetSourcePosition(expr->position());
+
+ // Invalid left-hand-sides are rewritten to have a 'throw
+ // ReferenceError' as the left-hand side.
+ if (!expr->expression()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->expression());
+ return;
+ }
+
+ // Expression can only be a property, a global or a (parameter or local)
+ // slot.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->expression()->AsProperty();
+ // In case of a property we use the uninitialized expression context
+ // of the key to detect a named property.
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate expression and get value.
+ if (assign_type == VARIABLE) {
+ ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
+ AccumulatorValueContext context(this);
+ EmitVariableLoad(expr->expression()->AsVariableProxy());
+ } else {
+ // Reserve space for result of postfix operation.
+ if (expr->is_postfix() && !context()->IsEffect()) {
+ __ Push(Smi::FromInt(0));
+ }
+ if (assign_type == NAMED_PROPERTY) {
+ VisitForAccumulatorValue(prop->obj());
+ __ push(rax); // Copy of receiver, needed for later store.
+ EmitNamedPropertyLoad(prop);
+ } else {
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ __ movq(rdx, Operand(rsp, 0)); // Leave receiver on stack
+ __ push(rax); // Copy of key, needed for later store.
+ EmitKeyedPropertyLoad(prop);
+ }
+ }
+
+ // We need a second deoptimization point after loading the value
+ // in case evaluating the property load my have a side effect.
+ if (assign_type == VARIABLE) {
+ PrepareForBailout(expr->expression(), TOS_REG);
+ } else {
+ PrepareForBailoutForId(prop->LoadId(), TOS_REG);
+ }
+
+ // Call ToNumber only if operand is not a smi.
+ Label no_conversion;
+ if (ShouldInlineSmiCase(expr->op())) {
+ __ JumpIfSmi(rax, &no_conversion, Label::kNear);
+ }
+ ToNumberStub convert_stub;
+ __ CallStub(&convert_stub);
+ __ bind(&no_conversion);
+
+ // Save result for postfix expressions.
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ // Save the result on the stack. If we have a named or keyed property
+ // we store the result under the receiver that is currently on top
+ // of the stack.
+ switch (assign_type) {
+ case VARIABLE:
+ __ push(rax);
+ break;
+ case NAMED_PROPERTY:
+ __ movq(Operand(rsp, kPointerSize), rax);
+ break;
+ case KEYED_PROPERTY:
+ __ movq(Operand(rsp, 2 * kPointerSize), rax);
+ break;
+ }
+ }
+ }
+
+ // Inline smi case if we are in a loop.
+ Label done, stub_call;
+ JumpPatchSite patch_site(masm_);
+
+ if (ShouldInlineSmiCase(expr->op())) {
+ if (expr->op() == Token::INC) {
+ __ SmiAddConstant(rax, rax, Smi::FromInt(1));
+ } else {
+ __ SmiSubConstant(rax, rax, Smi::FromInt(1));
+ }
+ __ j(overflow, &stub_call, Label::kNear);
+ // We could eliminate this smi check if we split the code at
+ // the first smi check before calling ToNumber.
+ patch_site.EmitJumpIfSmi(rax, &done, Label::kNear);
+
+ __ bind(&stub_call);
+ // Call stub. Undo operation first.
+ if (expr->op() == Token::INC) {
+ __ SmiSubConstant(rax, rax, Smi::FromInt(1));
+ } else {
+ __ SmiAddConstant(rax, rax, Smi::FromInt(1));
+ }
+ }
+
+ // Record position before stub call.
+ SetSourcePosition(expr->position());
+
+ // Call stub for +1/-1.
+ __ movq(rdx, rax);
+ __ Move(rax, Smi::FromInt(1));
+ BinaryOpStub stub(expr->binary_op(), NO_OVERWRITE);
+ CallIC(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ expr->CountBinOpFeedbackId());
+ patch_site.EmitPatchInfo();
+ __ bind(&done);
+
+ // Store the value returned in rax.
+ switch (assign_type) {
+ case VARIABLE:
+ if (expr->is_postfix()) {
+ // Perform the assignment as if via '='.
+ { EffectContext context(this);
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context.Plug(rax);
+ }
+ // For all contexts except kEffect: We have the result on
+ // top of the stack.
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ // Perform the assignment as if via '='.
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(rax);
+ }
+ break;
+ case NAMED_PROPERTY: {
+ __ Move(rcx, prop->key()->AsLiteral()->value());
+ __ pop(rdx);
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->StoreIC_Initialize()
+ : isolate()->builtins()->StoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(rax);
+ }
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ pop(rcx);
+ __ pop(rdx);
+ Handle<Code> ic = is_classic_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize()
+ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(rax);
+ }
+ break;
+ }
+ }
+}
+
+
+void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
+ VariableProxy* proxy = expr->AsVariableProxy();
+ ASSERT(!context()->IsEffect());
+ ASSERT(!context()->IsTest());
+
+ if (proxy != NULL && proxy->var()->IsUnallocated()) {
+ Comment cmnt(masm_, "Global variable");
+ __ Move(rcx, proxy->name());
+ __ movq(rax, GlobalObjectOperand());
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ // Use a regular load, not a contextual load, to avoid a reference
+ // error.
+ CallIC(ic);
+ PrepareForBailout(expr, TOS_REG);
+ context()->Plug(rax);
+ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
+ Label done, slow;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done);
+
+ __ bind(&slow);
+ __ push(rsi);
+ __ Push(proxy->name());
+ __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ PrepareForBailout(expr, TOS_REG);
+ __ bind(&done);
+
+ context()->Plug(rax);
+ } else {
+ // This expression cannot throw a reference error at the top level.
+ VisitInDuplicateContext(expr);
+ }
+}
+
+
+void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
+ 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(sub_expr);
+ }
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+
+ if (check->Equals(isolate()->heap()->number_string())) {
+ __ JumpIfSmi(rax, if_true);
+ __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset));
+ __ CompareRoot(rax, Heap::kHeapNumberMapRootIndex);
+ Split(equal, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->string_string())) {
+ __ JumpIfSmi(rax, if_false);
+ // Check for undetectable objects => false.
+ __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rdx);
+ __ j(above_equal, if_false);
+ __ testb(FieldOperand(rdx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ Split(zero, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->symbol_string())) {
+ __ JumpIfSmi(rax, if_false);
+ __ CmpObjectType(rax, SYMBOL_TYPE, rdx);
+ Split(equal, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->boolean_string())) {
+ __ CompareRoot(rax, Heap::kTrueValueRootIndex);
+ __ j(equal, if_true);
+ __ CompareRoot(rax, Heap::kFalseValueRootIndex);
+ Split(equal, if_true, if_false, fall_through);
+ } else if (FLAG_harmony_typeof &&
+ check->Equals(isolate()->heap()->null_string())) {
+ __ CompareRoot(rax, Heap::kNullValueRootIndex);
+ Split(equal, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->undefined_string())) {
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ j(equal, if_true);
+ __ JumpIfSmi(rax, if_false);
+ // Check for undetectable objects => true.
+ __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
+ __ testb(FieldOperand(rdx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ Split(not_zero, if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->function_string())) {
+ __ JumpIfSmi(rax, if_false);
+ 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_string())) {
+ __ JumpIfSmi(rax, if_false);
+ if (!FLAG_harmony_typeof) {
+ __ CompareRoot(rax, Heap::kNullValueRootIndex);
+ __ j(equal, if_true);
+ }
+ __ CmpObjectType(rax, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, rdx);
+ __ j(below, if_false);
+ __ CmpInstanceType(rdx, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ j(above, if_false);
+ // Check for undetectable objects => false.
+ __ testb(FieldOperand(rdx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ Split(zero, if_true, if_false, fall_through);
+ } else {
+ if (if_false != fall_through) __ jmp(if_false);
+ }
+ context()->Plug(if_true, if_false);
+}
+
+
+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;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ Token::Value op = expr->op();
+ VisitForStackValue(expr->left());
+ switch (op) {
+ case Token::IN:
+ VisitForStackValue(expr->right());
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
+ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
+ __ CompareRoot(rax, Heap::kTrueValueRootIndex);
+ Split(equal, if_true, if_false, fall_through);
+ break;
+
+ case Token::INSTANCEOF: {
+ VisitForStackValue(expr->right());
+ InstanceofStub stub(InstanceofStub::kNoFlags);
+ __ CallStub(&stub);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ testq(rax, rax);
+ // The stub returns 0 for true.
+ Split(zero, if_true, if_false, fall_through);
+ break;
+ }
+
+ default: {
+ VisitForAccumulatorValue(expr->right());
+ Condition cc = CompareIC::ComputeCondition(op);
+ __ pop(rdx);
+
+ bool inline_smi_code = ShouldInlineSmiCase(op);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ movq(rcx, rdx);
+ __ or_(rcx, rax);
+ patch_site.EmitJumpIfNotSmi(rcx, &slow_case, Label::kNear);
+ __ cmpq(rdx, rax);
+ Split(cc, if_true, if_false, NULL);
+ __ bind(&slow_case);
+ }
+
+ // Record position and call the compare IC.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ patch_site.EmitPatchInfo();
+
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ __ testq(rax, rax);
+ Split(cc, if_true, if_false, fall_through);
+ }
+ }
+
+ // Convert the result of the comparison into one expected for this
+ // expression's context.
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr,
+ Expression* sub_expr,
+ NilValue nil) {
+ 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);
+
+ VisitForAccumulatorValue(sub_expr);
+ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
+ if (expr->op() == Token::EQ_STRICT) {
+ Heap::RootListIndex nil_value = nil == kNullValue ?
+ Heap::kNullValueRootIndex :
+ Heap::kUndefinedValueRootIndex;
+ __ CompareRoot(rax, nil_value);
+ Split(equal, if_true, if_false, fall_through);
+ } else {
+ Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(), nil);
+ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+ __ testq(rax, rax);
+ Split(not_zero, if_true, if_false, fall_through);
+ }
+ context()->Plug(if_true, if_false);
+}
+
+
+void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
+ __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ context()->Plug(rax);
+}
+
+
+Register FullCodeGenerator::result_register() {
+ return rax;
+}
+
+
+Register FullCodeGenerator::context_register() {
+ return rsi;
+}
+
+
+void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
+ ASSERT(IsAligned(frame_offset, kPointerSize));
+ __ movq(Operand(rbp, frame_offset), value);
+}
+
+
+void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
+ __ movq(dst, ContextOperand(rsi, context_index));
+}
+
+
+void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
+ Scope* declaration_scope = scope()->DeclarationScope();
+ if (declaration_scope->is_global_scope() ||
+ declaration_scope->is_module_scope()) {
+ // Contexts nested in the native context have a canonical empty function
+ // as their closure, not the anonymous closure containing the global
+ // code. Pass a smi sentinel and let the runtime look up the empty
+ // function.
+ __ Push(Smi::FromInt(0));
+ } else if (declaration_scope->is_eval_scope()) {
+ // Contexts created by a call to eval have the same closure as the
+ // context calling eval, not the anonymous closure containing the eval
+ // code. Fetch it from the context.
+ __ push(ContextOperand(rsi, Context::CLOSURE_INDEX));
+ } else {
+ ASSERT(declaration_scope->is_function_scope());
+ __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// Non-local control flow support.
+
+
+void FullCodeGenerator::EnterFinallyBlock() {
+ ASSERT(!result_register().is(rdx));
+ ASSERT(!result_register().is(rcx));
+ // Cook return address on top of stack (smi encoded Code* delta)
+ __ PopReturnAddressTo(rdx);
+ __ Move(rcx, masm_->CodeObject());
+ __ subq(rdx, rcx);
+ __ Integer32ToSmi(rdx, rdx);
+ __ push(rdx);
+
+ // Store result register while executing finally block.
+ __ push(result_register());
+
+ // Store pending message while executing finally block.
+ ExternalReference pending_message_obj =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ __ Load(rdx, pending_message_obj);
+ __ push(rdx);
+
+ ExternalReference has_pending_message =
+ ExternalReference::address_of_has_pending_message(isolate());
+ __ Load(rdx, has_pending_message);
+ __ Integer32ToSmi(rdx, rdx);
+ __ push(rdx);
+
+ ExternalReference pending_message_script =
+ ExternalReference::address_of_pending_message_script(isolate());
+ __ Load(rdx, pending_message_script);
+ __ push(rdx);
+}
+
+
+void FullCodeGenerator::ExitFinallyBlock() {
+ ASSERT(!result_register().is(rdx));
+ ASSERT(!result_register().is(rcx));
+ // Restore pending message from stack.
+ __ pop(rdx);
+ ExternalReference pending_message_script =
+ ExternalReference::address_of_pending_message_script(isolate());
+ __ Store(pending_message_script, rdx);
+
+ __ pop(rdx);
+ __ SmiToInteger32(rdx, rdx);
+ ExternalReference has_pending_message =
+ ExternalReference::address_of_has_pending_message(isolate());
+ __ Store(has_pending_message, rdx);
+
+ __ pop(rdx);
+ ExternalReference pending_message_obj =
+ ExternalReference::address_of_pending_message_obj(isolate());
+ __ Store(pending_message_obj, rdx);
+
+ // Restore result register from stack.
+ __ pop(result_register());
+
+ // Uncook return address.
+ __ pop(rdx);
+ __ SmiToInteger32(rdx, rdx);
+ __ Move(rcx, masm_->CodeObject());
+ __ addq(rdx, rcx);
+ __ jmp(rdx);
+}
+
+
+#undef __
+
+#define __ ACCESS_MASM(masm())
+
+FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
+ int* stack_depth,
+ int* context_length) {
+ // The macros used here must preserve the result register.
+
+ // Because the handler block contains the context of the finally
+ // code, we can restore it directly from there for the finally code
+ // rather than iteratively unwinding contexts via their previous
+ // links.
+ __ Drop(*stack_depth); // Down to the handler block.
+ if (*context_length > 0) {
+ // Restore the context to its dedicated register and the stack.
+ __ movq(rsi, Operand(rsp, StackHandlerConstants::kContextOffset));
+ __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi);
+ }
+ __ PopTryHandler();
+ __ call(finally_entry_);
+
+ *stack_depth = 0;
+ *context_length = 0;
+ return previous_;
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/ic-x64.cc b/chromium/v8/src/x64/ic-x64.cc
new file mode 100644
index 00000000000..4837b9aa9a8
--- /dev/null
+++ b/chromium/v8/src/x64/ic-x64.cc
@@ -0,0 +1,1686 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "codegen.h"
+#include "ic-inl.h"
+#include "runtime.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// Static IC stub generators.
+//
+
+#define __ ACCESS_MASM(masm)
+
+
+static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
+ Register type,
+ Label* global_object) {
+ // Register usage:
+ // type: holds the receiver instance type on entry.
+ __ cmpb(type, Immediate(JS_GLOBAL_OBJECT_TYPE));
+ __ j(equal, global_object);
+ __ cmpb(type, Immediate(JS_BUILTINS_OBJECT_TYPE));
+ __ j(equal, global_object);
+ __ cmpb(type, Immediate(JS_GLOBAL_PROXY_TYPE));
+ __ j(equal, global_object);
+}
+
+
+// Generated code falls through if the receiver is a regular non-global
+// JS object with slow properties and no interceptors.
+static void GenerateNameDictionaryReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register r0,
+ Register r1,
+ Label* miss) {
+ // Register usage:
+ // receiver: holds the receiver on entry and is unchanged.
+ // r0: used to hold receiver instance type.
+ // Holds the property dictionary on fall through.
+ // r1: used to hold receivers map.
+
+ __ JumpIfSmi(receiver, miss);
+
+ // Check that the receiver is a valid JS object.
+ __ movq(r1, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ movb(r0, FieldOperand(r1, Map::kInstanceTypeOffset));
+ __ cmpb(r0, Immediate(FIRST_SPEC_OBJECT_TYPE));
+ __ j(below, miss);
+
+ // If this assert fails, we have to check upper bound too.
+ STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
+
+ GenerateGlobalInstanceTypeCheck(masm, r0, miss);
+
+ // Check for non-global object that requires access check.
+ __ testb(FieldOperand(r1, Map::kBitFieldOffset),
+ Immediate((1 << Map::kIsAccessCheckNeeded) |
+ (1 << Map::kHasNamedInterceptor)));
+ __ j(not_zero, miss);
+
+ __ movq(r0, FieldOperand(receiver, JSObject::kPropertiesOffset));
+ __ CompareRoot(FieldOperand(r0, HeapObject::kMapOffset),
+ Heap::kHashTableMapRootIndex);
+ __ j(not_equal, miss);
+}
+
+
+
+// Helper function used to load a property from a dictionary backing storage.
+// This function may return false negatives, so miss_label
+// must always call a backup property load that is complete.
+// This function is safe to call if name is not an internalized string,
+// and will jump to the miss_label in that case.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss_label,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1,
+ Register result) {
+ // Register use:
+ //
+ // elements - holds the property dictionary on entry and is unchanged.
+ //
+ // name - holds the name of the property on entry and is unchanged.
+ //
+ // r0 - used to hold the capacity of the property dictionary.
+ //
+ // r1 - used to hold the index into the property dictionary.
+ //
+ // result - holds the result on exit if the load succeeded.
+
+ Label done;
+
+ // Probe the dictionary.
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
+
+ // If probing finds an entry in the dictionary, r1 contains the
+ // index into the dictionary. Check that the value is a normal
+ // property.
+ __ bind(&done);
+ const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ __ Test(Operand(elements, r1, times_pointer_size,
+ kDetailsOffset - kHeapObjectTag),
+ Smi::FromInt(PropertyDetails::TypeField::kMask));
+ __ j(not_zero, miss_label);
+
+ // Get the value at the masked, scaled index.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ movq(result,
+ Operand(elements, r1, times_pointer_size,
+ kValueOffset - kHeapObjectTag));
+}
+
+
+// Helper function used to store a property to a dictionary backing
+// storage. This function may fail to store a property even though it
+// is in the dictionary, so code at miss_label must always call a
+// backup property store that is complete. This function is safe to
+// call if name is not an internalized string, and will jump to the miss_label
+// in that case. The generated code assumes that the receiver has slow
+// properties, is not a global object and does not have interceptors.
+static void GenerateDictionaryStore(MacroAssembler* masm,
+ Label* miss_label,
+ Register elements,
+ Register name,
+ Register value,
+ Register scratch0,
+ Register scratch1) {
+ // Register use:
+ //
+ // elements - holds the property dictionary on entry and is clobbered.
+ //
+ // name - holds the name of the property on entry and is unchanged.
+ //
+ // value - holds the value to store and is unchanged.
+ //
+ // scratch0 - used during the positive dictionary lookup and is clobbered.
+ //
+ // scratch1 - used for index into the property dictionary and is clobbered.
+ Label done;
+
+ // Probe the dictionary.
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ scratch0,
+ scratch1);
+
+ // If probing finds an entry in the dictionary, scratch0 contains the
+ // index into the dictionary. Check that the value is a normal
+ // property that is not read only.
+ __ bind(&done);
+ const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ const int kTypeAndReadOnlyMask =
+ (PropertyDetails::TypeField::kMask |
+ PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
+ __ Test(Operand(elements,
+ scratch1,
+ times_pointer_size,
+ kDetailsOffset - kHeapObjectTag),
+ Smi::FromInt(kTypeAndReadOnlyMask));
+ __ j(not_zero, miss_label);
+
+ // Store the value at the masked, scaled index.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ lea(scratch1, Operand(elements,
+ scratch1,
+ times_pointer_size,
+ kValueOffset - kHeapObjectTag));
+ __ movq(Operand(scratch1, 0), value);
+
+ // Update write barrier. Make sure not to clobber the value.
+ __ movq(scratch0, value);
+ __ RecordWrite(elements, scratch1, scratch0, kDontSaveFPRegs);
+}
+
+
+// Checks the receiver for special cases (value type, slow case bits).
+// Falls through for regular JS object.
+static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register map,
+ int interceptor_bit,
+ Label* slow) {
+ // Register use:
+ // receiver - holds the receiver and is unchanged.
+ // Scratch registers:
+ // map - used to hold the map of the receiver.
+
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, slow);
+
+ // Check that the object is some kind of JS object EXCEPT JS Value type.
+ // In the case that the object is a value-wrapper object,
+ // we enter the runtime system to make sure that indexing
+ // into string objects work as intended.
+ ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
+ __ CmpObjectType(receiver, JS_OBJECT_TYPE, map);
+ __ j(below, slow);
+
+ // Check bit field.
+ __ testb(FieldOperand(map, Map::kBitFieldOffset),
+ Immediate((1 << Map::kIsAccessCheckNeeded) |
+ (1 << interceptor_bit)));
+ __ j(not_zero, slow);
+}
+
+
+// Loads an indexed element from a fast case array.
+// If not_fast_array is NULL, doesn't perform the elements map check.
+static void GenerateFastArrayLoad(MacroAssembler* masm,
+ Register receiver,
+ Register key,
+ Register elements,
+ Register scratch,
+ Register result,
+ Label* not_fast_array,
+ Label* out_of_range) {
+ // Register use:
+ //
+ // receiver - holds the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // elements - holds the elements of the receiver on exit.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the the same as 'receiver' or 'key'.
+ // Unchanged on bailout so 'receiver' and 'key' can be safely
+ // used by further computation.
+ //
+ // Scratch registers:
+ //
+ // scratch - used to hold elements of the receiver and the loaded value.
+
+ __ movq(elements, FieldOperand(receiver, JSObject::kElementsOffset));
+ if (not_fast_array != NULL) {
+ // Check that the object is in fast mode and writable.
+ __ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
+ __ j(not_equal, not_fast_array);
+ } else {
+ __ AssertFastElements(elements);
+ }
+ // Check that the key (index) is within bounds.
+ __ SmiCompare(key, FieldOperand(elements, FixedArray::kLengthOffset));
+ // Unsigned comparison rejects negative indices.
+ __ j(above_equal, out_of_range);
+ // Fast case: Do the load.
+ SmiIndex index = masm->SmiToIndex(scratch, key, kPointerSizeLog2);
+ __ movq(scratch, FieldOperand(elements,
+ index.reg,
+ index.scale,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(scratch, Heap::kTheHoleValueRootIndex);
+ // 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);
+ if (!result.is(scratch)) {
+ __ movq(result, scratch);
+ }
+}
+
+
+// Checks whether a key is an array index string or a unique name.
+// Falls through if the key is a unique name.
+static void GenerateKeyNameCheck(MacroAssembler* masm,
+ Register key,
+ Register map,
+ Register hash,
+ Label* index_string,
+ Label* not_unique) {
+ // Register use:
+ // key - holds the key and is unchanged. Assumed to be non-smi.
+ // Scratch registers:
+ // map - used to hold the map of the key.
+ // hash - used to hold the hash of the key.
+ Label unique;
+ __ CmpObjectType(key, LAST_UNIQUE_NAME_TYPE, map);
+ __ j(above, not_unique);
+ STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
+ __ j(equal, &unique);
+
+ // Is the string an array index, with cached numeric value?
+ __ movl(hash, FieldOperand(key, Name::kHashFieldOffset));
+ __ testl(hash, Immediate(Name::kContainsCachedArrayIndexMask));
+ __ j(zero, index_string); // The value in hash is used at jump target.
+
+ // Is the string internalized? We already know it's a string so a single
+ // bit test is enough.
+ STATIC_ASSERT(kNotInternalizedTag != 0);
+ __ testb(FieldOperand(map, Map::kInstanceTypeOffset),
+ Immediate(kIsNotInternalizedMask));
+ __ j(not_zero, not_unique);
+
+ __ bind(&unique);
+}
+
+
+
+void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow, check_name, index_smi, index_name, property_array_property;
+ Label probe_dictionary, check_number_dictionary;
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rax, &check_name);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, rdx, rcx, Map::kHasIndexedInterceptor, &slow);
+
+ // Check the receiver's map to see if it has fast elements.
+ __ CheckFastElements(rcx, &check_number_dictionary);
+
+ GenerateFastArrayLoad(masm,
+ rdx,
+ rax,
+ rcx,
+ rbx,
+ rax,
+ NULL,
+ &slow);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->keyed_load_generic_smi(), 1);
+ __ ret(0);
+
+ __ bind(&check_number_dictionary);
+ __ SmiToInteger32(rbx, rax);
+ __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
+
+ // Check whether the elements is a number dictionary.
+ // rdx: receiver
+ // rax: key
+ // rbx: key as untagged int32
+ // rcx: elements
+ __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset),
+ Heap::kHashTableMapRootIndex);
+ __ j(not_equal, &slow);
+ __ LoadFromNumberDictionary(&slow, rcx, rax, rbx, r9, rdi, rax);
+ __ ret(0);
+
+ __ bind(&slow);
+ // Slow case: Jump to runtime.
+ // rdx: receiver
+ // rax: key
+ __ IncrementCounter(counters->keyed_load_generic_slow(), 1);
+ GenerateRuntimeGetProperty(masm);
+
+ __ bind(&check_name);
+ GenerateKeyNameCheck(masm, rax, rcx, rbx, &index_name, &slow);
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, rdx, rcx, Map::kHasNamedInterceptor, &slow);
+
+ // If the receiver is a fast-case object, check the keyed lookup
+ // cache. Otherwise probe the dictionary leaving result in rcx.
+ __ movq(rbx, FieldOperand(rdx, JSObject::kPropertiesOffset));
+ __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
+ Heap::kHashTableMapRootIndex);
+ __ j(equal, &probe_dictionary);
+
+ // Load the map of the receiver, compute the keyed lookup cache hash
+ // based on 32 bits of the map pointer and the string hash.
+ __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset));
+ __ movl(rcx, rbx);
+ __ shr(rcx, Immediate(KeyedLookupCache::kMapHashShift));
+ __ movl(rdi, FieldOperand(rax, String::kHashFieldOffset));
+ __ shr(rdi, Immediate(String::kHashShift));
+ __ xor_(rcx, rdi);
+ int mask = (KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask);
+ __ and_(rcx, Immediate(mask));
+
+ // Load the key (consisting of map and internalized string) 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());
+
+ 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, 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());
+
+ // 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));
+ __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1);
+ __ ret(0);
+
+ // Load property array property.
+ __ bind(&property_array_property);
+ __ movq(rax, FieldOperand(rdx, JSObject::kPropertiesOffset));
+ __ movq(rax, FieldOperand(rax, rdi, times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1);
+ __ ret(0);
+
+ // Do a quick inline probe of the receiver's dictionary, if it
+ // exists.
+ __ bind(&probe_dictionary);
+ // rdx: receiver
+ // rax: key
+ // rbx: elements
+
+ __ movq(rcx, FieldOperand(rdx, JSObject::kMapOffset));
+ __ movb(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset));
+ GenerateGlobalInstanceTypeCheck(masm, rcx, &slow);
+
+ GenerateDictionaryLoad(masm, &slow, rbx, rax, rcx, rdi, rax);
+ __ IncrementCounter(counters->keyed_load_generic_symbol(), 1);
+ __ ret(0);
+
+ __ bind(&index_name);
+ __ IndexFromHash(rbx, rax);
+ __ jmp(&index_smi);
+}
+
+
+void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ Register receiver = rdx;
+ Register index = rax;
+ Register scratch = rcx;
+ Register result = rax;
+
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &miss, // When index out of range.
+ STRING_INDEX_IS_ARRAY_INDEX);
+ char_at_generator.GenerateFast(masm);
+ __ ret(0);
+
+ StubRuntimeCallHelper call_helper;
+ char_at_generator.GenerateSlow(masm, call_helper);
+
+ __ bind(&miss);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &slow);
+
+ // Check that the key is an array index, that is Uint32.
+ STATIC_ASSERT(kSmiValueSize <= 32);
+ __ JumpUnlessNonNegativeSmi(rax, &slow);
+
+ // Get the map of the receiver.
+ __ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset));
+
+ // Check that it has indexed interceptor and access checks
+ // are not enabled for this object.
+ __ movb(rcx, FieldOperand(rcx, Map::kBitFieldOffset));
+ __ andb(rcx, Immediate(kSlowCaseBitFieldMask));
+ __ cmpb(rcx, Immediate(1 << Map::kHasIndexedInterceptor));
+ __ j(not_zero, &slow);
+
+ // Everything is fine, call runtime.
+ __ PopReturnAddressTo(rcx);
+ __ push(rdx); // receiver
+ __ push(rax); // key
+ __ PushReturnAddressFrom(rcx);
+
+ // Perform tail call to the entry.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(kKeyedLoadPropertyWithInterceptor),
+ masm->isolate()),
+ 2,
+ 1);
+
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+static void KeyedStoreGenerateGenericHelper(
+ MacroAssembler* masm,
+ Label* fast_object,
+ Label* fast_double,
+ Label* slow,
+ KeyedStoreCheckMap check_map,
+ KeyedStoreIncrementLength increment_length) {
+ Label transition_smi_elements;
+ Label finish_object_store, non_double_value, transition_double_elements;
+ Label fast_double_without_map_check;
+ // Fast case: Do the store, could be either Object or double.
+ __ bind(fast_object);
+ // rax: value
+ // rbx: receiver's elements array (a FixedArray)
+ // rcx: index
+ // rdx: receiver (a JSArray)
+ // r9: map of receiver
+ if (check_map == kCheckMap) {
+ __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset));
+ __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex);
+ __ j(not_equal, fast_double);
+ }
+ // Smi stores don't require further checks.
+ Label non_smi_value;
+ __ JumpIfNotSmi(rax, &non_smi_value);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ leal(rdi, Operand(rcx, 1));
+ __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi);
+ }
+ // It's irrelevant whether array is smi-only or not when writing a smi.
+ __ movq(FieldOperand(rbx, rcx, times_pointer_size, FixedArray::kHeaderSize),
+ rax);
+ __ ret(0);
+
+ __ bind(&non_smi_value);
+ // Writing a non-smi, check whether array allows non-smi elements.
+ // r9: receiver's map
+ __ CheckFastObjectElements(r9, &transition_smi_elements);
+
+ __ bind(&finish_object_store);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ leal(rdi, Operand(rcx, 1));
+ __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi);
+ }
+ __ 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);
+ if (check_map == kCheckMap) {
+ // 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);
+ __ StoreNumberToDoubleElements(rax, rbx, rcx, xmm0,
+ &transition_double_elements);
+ if (increment_length == kIncrementLength) {
+ // Add 1 to receiver->length.
+ __ leal(rdi, Operand(rcx, 1));
+ __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rdi);
+ }
+ __ 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_ELEMENTS ->
+ // FAST_DOUBLE_ELEMENTS and complete the store.
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS,
+ rbx,
+ rdi,
+ slow);
+ AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, slow);
+ __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ jmp(&fast_double_without_map_check);
+
+ __ bind(&non_double_value);
+ // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_ELEMENTS,
+ rbx,
+ rdi,
+ slow);
+ mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm, mode,
+ slow);
+ __ 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);
+ mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, slow);
+ __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ jmp(&finish_object_store);
+}
+
+
+void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow, slow_with_tagged_index, fast_object, fast_object_grow;
+ Label fast_double, fast_double_grow;
+ Label array, extra, check_if_double_array;
+
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(rdx, &slow_with_tagged_index);
+ // Get the map from the receiver.
+ __ 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(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(r9, JS_ARRAY_TYPE);
+ __ j(equal, &array);
+ // Check that the object is some kind of JSObject.
+ __ CmpInstanceType(r9, FIRST_JS_OBJECT_TYPE);
+ __ j(below, &slow);
+
+ // Object case: Check key against length in the elements array.
+ // rax: value
+ // rdx: JSObject
+ // rcx: index
+ __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
+ // Check array bounds.
+ __ SmiCompareInteger32(FieldOperand(rbx, FixedArray::kLengthOffset), rcx);
+ // rax: value
+ // rbx: FixedArray
+ // rcx: index
+ __ j(above, &fast_object);
+
+ // Slow case: call runtime.
+ __ bind(&slow);
+ __ Integer32ToSmi(rcx, rcx);
+ __ bind(&slow_with_tagged_index);
+ GenerateRuntimeSetProperty(masm, strict_mode);
+ // Never returns to here.
+
+ // 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);
+ // rax: value
+ // rdx: receiver (a JSArray)
+ // rbx: receiver's elements array (a FixedArray)
+ // rcx: index
+ // flags: smicompare (rdx.length(), rbx)
+ __ j(not_equal, &slow); // do not leave holes in the array
+ __ 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_if_double_array);
+ __ jmp(&fast_object_grow);
+
+ __ bind(&check_if_double_array);
+ // rdi: elements array's map
+ __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex);
+ __ j(not_equal, &slow);
+ __ jmp(&fast_double_grow);
+
+ // 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);
+ // rax: value
+ // rdx: receiver (a JSArray)
+ // rcx: index
+ __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
+
+ // Check the key against the length in the array, compute the
+ // address to store into and fall through to fast case.
+ __ SmiCompareInteger32(FieldOperand(rdx, JSArray::kLengthOffset), rcx);
+ __ j(below_equal, &extra);
+
+ KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double,
+ &slow, kCheckMap, kDontIncrementLength);
+ KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow,
+ &slow, kDontCheckMap, kIncrementLength);
+}
+
+
+// The generated code does not accept smi keys.
+// The generated code falls through if both probes miss.
+void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm,
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_state) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rdx : receiver
+ // -----------------------------------
+ Label number, non_number, non_string, boolean, probe, miss;
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(kind,
+ MONOMORPHIC,
+ extra_state,
+ Code::NORMAL,
+ argc);
+ Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, rdx, rcx, rbx,
+ rax);
+
+ // If the stub cache probing failed, the receiver might be a value.
+ // For value objects, we use the map of the prototype objects for
+ // the corresponding JSValue for the cache and that is what we need
+ // to probe.
+ //
+ // Check for number.
+ __ JumpIfSmi(rdx, &number);
+ __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rbx);
+ __ j(not_equal, &non_number);
+ __ bind(&number);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::NUMBER_FUNCTION_INDEX, rdx);
+ __ jmp(&probe);
+
+ // Check for string.
+ __ bind(&non_number);
+ __ CmpInstanceType(rbx, FIRST_NONSTRING_TYPE);
+ __ j(above_equal, &non_string);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::STRING_FUNCTION_INDEX, rdx);
+ __ jmp(&probe);
+
+ // Check for boolean.
+ __ bind(&non_string);
+ __ CompareRoot(rdx, Heap::kTrueValueRootIndex);
+ __ j(equal, &boolean);
+ __ CompareRoot(rdx, Heap::kFalseValueRootIndex);
+ __ j(not_equal, &miss);
+ __ bind(&boolean);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::BOOLEAN_FUNCTION_INDEX, rdx);
+
+ // Probe the stub cache for the value object.
+ __ bind(&probe);
+ Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, rdx, rcx, rbx,
+ no_reg);
+
+ __ bind(&miss);
+}
+
+
+static void GenerateFunctionTailCall(MacroAssembler* masm,
+ int argc,
+ Label* miss) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rdi : function
+ // 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
+ // -----------------------------------
+ __ JumpIfSmi(rdi, miss);
+ // Check that the value is a JavaScript function.
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rdx);
+ __ j(not_equal, miss);
+
+ // Invoke the function.
+ ParameterCount actual(argc);
+ __ InvokeFunction(rdi, actual, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
+}
+
+
+// The generated code falls through if the call should be handled by runtime.
+void CallICBase::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
+ // -----------------------------------
+ Label miss;
+
+ // Get the receiver of the function from the stack.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ GenerateNameDictionaryReceiverCheck(masm, rdx, rax, rbx, &miss);
+
+ // rax: elements
+ // Search the dictionary placing the result in rdi.
+ GenerateDictionaryLoad(masm, &miss, rax, rcx, rbx, rdi, rdi);
+
+ GenerateFunctionTailCall(masm, argc, &miss);
+
+ __ bind(&miss);
+}
+
+
+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
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ Counters* counters = masm->isolate()->counters();
+ if (id == IC::kCallIC_Miss) {
+ __ IncrementCounter(counters->call_miss(), 1);
+ } else {
+ __ IncrementCounter(counters->keyed_call_miss(), 1);
+ }
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // 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);
+
+ // 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.
+ if (id == IC::kCallIC_Miss) {
+ Label invoke, global;
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver
+ __ JumpIfSmi(rdx, &invoke);
+ __ CmpObjectType(rdx, JS_GLOBAL_OBJECT_TYPE, rcx);
+ __ j(equal, &global);
+ __ CmpInstanceType(rcx, JS_BUILTINS_OBJECT_TYPE);
+ __ j(not_equal, &invoke);
+
+ // Patch the receiver on the stack.
+ __ bind(&global);
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ __ bind(&invoke);
+ }
+
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount actual(argc);
+ __ InvokeFunction(rdi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ call_kind);
+}
+
+
+void CallIC::GenerateMegamorphic(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
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state);
+ GenerateMiss(masm, argc, extra_ic_state);
+}
+
+
+void KeyedCallIC::GenerateMegamorphic(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
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ Label do_call, slow_call, slow_load;
+ Label check_number_dictionary, check_name, lookup_monomorphic_cache;
+ Label index_smi, index_name;
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rcx, &check_name);
+
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, rdx, rax, Map::kHasIndexedInterceptor, &slow_call);
+
+ GenerateFastArrayLoad(
+ masm, rdx, rcx, rax, rbx, rdi, &check_number_dictionary, &slow_load);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->keyed_call_generic_smi_fast(), 1);
+
+ __ bind(&do_call);
+ // receiver in rdx is not used after this point.
+ // rcx: key
+ // rdi: function
+ GenerateFunctionTailCall(masm, argc, &slow_call);
+
+ __ bind(&check_number_dictionary);
+ // rax: elements
+ // rcx: smi key
+ // Check whether the elements is a number dictionary.
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kHashTableMapRootIndex);
+ __ j(not_equal, &slow_load);
+ __ SmiToInteger32(rbx, rcx);
+ // ebx: untagged index
+ __ LoadFromNumberDictionary(&slow_load, rax, rcx, rbx, r9, rdi, rdi);
+ __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1);
+ __ jmp(&do_call);
+
+ __ bind(&slow_load);
+ // This branch is taken when calling KeyedCallIC_Miss is neither required
+ // nor beneficial.
+ __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1);
+ {
+ 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);
+
+ __ bind(&check_name);
+ GenerateKeyNameCheck(masm, rcx, rax, rbx, &index_name, &slow_call);
+
+ // The key is known to be a unique name.
+ // If the receiver is a regular JS object with slow properties then do
+ // a quick inline probe of the receiver's dictionary.
+ // Otherwise do the monomorphic cache probe.
+ GenerateKeyedLoadReceiverCheck(
+ masm, rdx, rax, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
+
+ __ movq(rbx, FieldOperand(rdx, JSObject::kPropertiesOffset));
+ __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
+ Heap::kHashTableMapRootIndex);
+ __ j(not_equal, &lookup_monomorphic_cache);
+
+ GenerateDictionaryLoad(masm, &slow_load, rbx, rcx, rax, rdi, rdi);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1);
+ __ jmp(&do_call);
+
+ __ bind(&lookup_monomorphic_cache);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1);
+ GenerateMonomorphicCacheProbe(masm,
+ argc,
+ Code::KEYED_CALL_IC,
+ Code::kNoExtraICState);
+ // Fall through on miss.
+
+ __ bind(&slow_call);
+ // This branch is taken if:
+ // - the receiver requires boxing or access check,
+ // - the key is neither smi nor a unique name,
+ // - the value loaded is not a function,
+ // - there is hope that the runtime will create a monomorphic call stub
+ // that will get fetched next time.
+ __ IncrementCounter(counters->keyed_call_generic_slow(), 1);
+ GenerateMiss(masm, argc);
+
+ __ bind(&index_name);
+ __ IndexFromHash(rbx, rcx);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
+}
+
+
+void KeyedCallIC::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
+ // -----------------------------------
+
+ // Check if the name is really a name.
+ Label miss;
+ __ JumpIfSmi(rcx, &miss);
+ Condition cond = masm->IsObjectNameType(rcx, rax, rax);
+ __ j(NegateCondition(cond), &miss);
+ CallICBase::GenerateNormal(masm, argc);
+ __ bind(&miss);
+ GenerateMiss(masm, argc);
+}
+
+
+static Operand GenerateMappedArgumentsLookup(MacroAssembler* masm,
+ Register object,
+ Register key,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* unmapped_case,
+ Label* slow_case) {
+ Heap* heap = masm->isolate()->heap();
+
+ // Check that the receiver is a JSObject. Because of the elements
+ // map check later, we do not need to check for interceptors or
+ // whether it requires access checks.
+ __ JumpIfSmi(object, slow_case);
+ // Check that the object is some kind of JSObject.
+ __ CmpObjectType(object, FIRST_JS_RECEIVER_TYPE, scratch1);
+ __ j(below, slow_case);
+
+ // Check that the key is a positive smi.
+ Condition check = masm->CheckNonNegativeSmi(key);
+ __ j(NegateCondition(check), slow_case);
+
+ // Load the elements into scratch1 and check its map. If not, jump
+ // to the unmapped lookup with the parameter map in scratch1.
+ Handle<Map> arguments_map(heap->non_strict_arguments_elements_map());
+ __ movq(scratch1, FieldOperand(object, JSObject::kElementsOffset));
+ __ CheckMap(scratch1, arguments_map, slow_case, DONT_DO_SMI_CHECK);
+
+ // Check if element is in the range of mapped arguments.
+ __ movq(scratch2, FieldOperand(scratch1, FixedArray::kLengthOffset));
+ __ SmiSubConstant(scratch2, scratch2, Smi::FromInt(2));
+ __ cmpq(key, scratch2);
+ __ j(greater_equal, unmapped_case);
+
+ // Load element index and check whether it is the hole.
+ const int kHeaderSize = FixedArray::kHeaderSize + 2 * kPointerSize;
+ __ SmiToInteger64(scratch3, key);
+ __ movq(scratch2, FieldOperand(scratch1,
+ scratch3,
+ times_pointer_size,
+ kHeaderSize));
+ __ CompareRoot(scratch2, Heap::kTheHoleValueRootIndex);
+ __ j(equal, unmapped_case);
+
+ // Load value from context and return it. We can reuse scratch1 because
+ // we do not jump to the unmapped lookup (which requires the parameter
+ // map in scratch1).
+ __ movq(scratch1, FieldOperand(scratch1, FixedArray::kHeaderSize));
+ __ SmiToInteger64(scratch3, scratch2);
+ return FieldOperand(scratch1,
+ scratch3,
+ times_pointer_size,
+ Context::kHeaderSize);
+}
+
+
+static Operand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
+ Register key,
+ Register parameter_map,
+ Register scratch,
+ Label* slow_case) {
+ // Element is in arguments backing store, which is referenced by the
+ // second element of the parameter_map. The parameter_map register
+ // must be loaded with the parameter map of the arguments object and is
+ // overwritten.
+ const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize;
+ Register backing_store = parameter_map;
+ __ movq(backing_store, FieldOperand(parameter_map, kBackingStoreOffset));
+ Handle<Map> fixed_array_map(masm->isolate()->heap()->fixed_array_map());
+ __ CheckMap(backing_store, fixed_array_map, slow_case, DONT_DO_SMI_CHECK);
+ __ movq(scratch, FieldOperand(backing_store, FixedArray::kLengthOffset));
+ __ cmpq(key, scratch);
+ __ j(greater_equal, slow_case);
+ __ SmiToInteger64(scratch, key);
+ return FieldOperand(backing_store,
+ scratch,
+ times_pointer_size,
+ FixedArray::kHeaderSize);
+}
+
+
+void KeyedLoadIC::GenerateNonStrictArguments(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow, notin;
+ Operand mapped_location =
+ GenerateMappedArgumentsLookup(
+ masm, rdx, rax, rbx, rcx, rdi, &notin, &slow);
+ __ movq(rax, mapped_location);
+ __ Ret();
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in rbx.
+ Operand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, rax, rbx, rcx, &slow);
+ __ CompareRoot(unmapped_location, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &slow);
+ __ movq(rax, unmapped_location);
+ __ Ret();
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow, notin;
+ Operand mapped_location = GenerateMappedArgumentsLookup(
+ masm, rdx, rcx, rbx, rdi, r8, &notin, &slow);
+ __ movq(mapped_location, rax);
+ __ lea(r9, mapped_location);
+ __ movq(r8, rax);
+ __ 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.
+ Operand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, rcx, rbx, rdi, &slow);
+ __ movq(unmapped_location, rax);
+ __ lea(r9, unmapped_location);
+ __ movq(r8, rax);
+ __ RecordWrite(rbx,
+ r9,
+ r8,
+ kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ INLINE_SMI_CHECK);
+ __ Ret();
+ __ bind(&slow);
+ GenerateMiss(masm, MISS);
+}
+
+
+void KeyedCallIC::GenerateNonStrictArguments(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
+ // -----------------------------------
+ Label slow, notin;
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+ Operand mapped_location = GenerateMappedArgumentsLookup(
+ masm, rdx, rcx, rbx, rax, r8, &notin, &slow);
+ __ movq(rdi, mapped_location);
+ GenerateFunctionTailCall(masm, argc, &slow);
+ __ bind(&notin);
+ // The unmapped lookup expects that the parameter map is in rbx.
+ Operand unmapped_location =
+ GenerateUnmappedArgumentsLookup(masm, rcx, rbx, rax, &slow);
+ __ CompareRoot(unmapped_location, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &slow);
+ __ movq(rdi, unmapped_location);
+ GenerateFunctionTailCall(masm, argc, &slow);
+ __ bind(&slow);
+ GenerateMiss(masm, argc);
+}
+
+
+void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, Code::kNoExtraICState,
+ Code::NORMAL, Code::LOAD_IC);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, rax, rcx, rbx, rdx);
+
+ GenerateMiss(masm);
+}
+
+
+void LoadIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameDictionaryReceiverCheck(masm, rax, rdx, rbx, &miss);
+
+ // rdx: elements
+ // Search the dictionary placing the result in rax.
+ GenerateDictionaryLoad(masm, &miss, rdx, rcx, rbx, rdi, rax);
+ __ ret(0);
+
+ // Cache miss: Jump to runtime.
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void LoadIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->load_miss(), 1);
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rax); // receiver
+ __ push(rcx); // name
+ __ PushReturnAddressFrom(rbx);
+
+ // Perform tail call to the entry.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kLoadIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rax); // receiver
+ __ push(rcx); // name
+ __ PushReturnAddressFrom(rbx);
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->keyed_load_miss(), 1);
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx); // receiver
+ __ push(rax); // name
+ __ PushReturnAddressFrom(rbx);
+
+ // Perform tail call to the entry.
+ ExternalReference ref = miss_mode == MISS_FORCE_GENERIC
+ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx); // receiver
+ __ push(rax); // name
+ __ PushReturnAddressFrom(rbx);
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
+}
+
+
+void StoreIC::GenerateMegamorphic(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ // Get the receiver from the stack and probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(
+ Code::STUB, MONOMORPHIC, strict_mode,
+ Code::NORMAL, Code::STORE_IC);
+ Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, rdx, rcx, rbx,
+ no_reg);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
+}
+
+
+void StoreIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // name
+ __ push(rax); // value
+ __ PushReturnAddressFrom(rbx);
+
+ // Perform tail call to the entry.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ Label miss;
+
+ GenerateNameDictionaryReceiverCheck(masm, rdx, rbx, rdi, &miss);
+
+ GenerateDictionaryStore(masm, &miss, rbx, rcx, rax, r8, r9);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->store_normal_hit(), 1);
+ __ ret(0);
+
+ __ bind(&miss);
+ __ IncrementCounter(counters->store_normal_miss(), 1);
+ GenerateMiss(masm);
+}
+
+
+void StoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx);
+ __ push(rcx);
+ __ push(rax);
+ __ Push(Smi::FromInt(NONE)); // PropertyAttributes
+ __ Push(Smi::FromInt(strict_mode));
+ __ PushReturnAddressFrom(rbx);
+
+ // Do tail-call to runtime routine.
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+}
+
+
+void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
+ StrictModeFlag strict_mode) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // key
+ __ push(rax); // value
+ __ Push(Smi::FromInt(NONE)); // PropertyAttributes
+ __ Push(Smi::FromInt(strict_mode)); // Strict mode.
+ __ PushReturnAddressFrom(rbx);
+
+ // Do tail-call to runtime routine.
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+}
+
+
+void StoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // key
+ __ push(rax); // value
+ __ PushReturnAddressFrom(rbx);
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref(IC_Utility(kStoreIC_Slow), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // key
+ __ push(rax); // value
+ __ PushReturnAddressFrom(rbx);
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, ICMissMode miss_mode) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ PopReturnAddressTo(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // key
+ __ push(rax); // value
+ __ PushReturnAddressFrom(rbx);
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref = miss_mode == MISS_FORCE_GENERIC
+ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+#undef __
+
+
+Condition CompareIC::ComputeCondition(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;
+ }
+}
+
+
+bool CompareIC::HasInlinedSmiCode(Address address) {
+ // The address of the instruction following the call.
+ Address test_instruction_address =
+ address + Assembler::kCallTargetAddressOffset;
+
+ // If the instruction following the call is not a test al, nothing
+ // was inlined.
+ return *test_instruction_address == Assembler::kTestAlByte;
+}
+
+
+void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
+ // The address of the instruction following the call.
+ Address test_instruction_address =
+ address + Assembler::kCallTargetAddressOffset;
+
+ // If the instruction following the call is not a test al, nothing
+ // was inlined.
+ if (*test_instruction_address != Assembler::kTestAlByte) {
+ ASSERT(*test_instruction_address == Assembler::kNopByte);
+ return;
+ }
+
+ Address delta_address = test_instruction_address + 1;
+ // The delta to the start of the map check instruction and the
+ // condition code uses at the patched jump.
+ int8_t delta = *reinterpret_cast<int8_t*>(delta_address);
+ if (FLAG_trace_ic) {
+ PrintF("[ patching ic at %p, test=%p, delta=%d\n",
+ address, test_instruction_address, delta);
+ }
+
+ // Patch with a short conditional jump. Enabling means switching from a short
+ // jump-if-carry/not-carry to jump-if-zero/not-zero, whereas disabling is the
+ // reverse operation of that.
+ Address jmp_address = test_instruction_address - delta;
+ ASSERT((check == ENABLE_INLINED_SMI_CHECK)
+ ? (*jmp_address == Assembler::kJncShortOpcode ||
+ *jmp_address == Assembler::kJcShortOpcode)
+ : (*jmp_address == Assembler::kJnzShortOpcode ||
+ *jmp_address == Assembler::kJzShortOpcode));
+ Condition cc = (check == ENABLE_INLINED_SMI_CHECK)
+ ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero)
+ : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry);
+ *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/lithium-codegen-x64.cc b/chromium/v8/src/x64/lithium-codegen-x64.cc
new file mode 100644
index 00000000000..abb8c77b5b7
--- /dev/null
+++ b/chromium/v8/src/x64/lithium-codegen-x64.cc
@@ -0,0 +1,5519 @@
+// Copyright 2013 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "x64/lithium-codegen-x64.h"
+#include "code-stubs.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+
+// When invoking builtins, we need to record the safepoint in the middle of
+// the invoke instruction sequence generated by the macro assembler.
+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 {
+ codegen_->EnsureSpaceForLazyDeopt(Deoptimizer::patch_size() - call_size);
+ }
+
+ virtual void AfterCall() const {
+ codegen_->RecordSafepoint(pointers_, deopt_mode_);
+ }
+
+ private:
+ LCodeGen* codegen_;
+ LPointerMap* pointers_;
+ Safepoint::DeoptMode deopt_mode_;
+};
+
+
+#define __ masm()->
+
+bool LCodeGen::GenerateCode() {
+ LPhase phase("Z_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() &&
+ GenerateJumpTable() &&
+ GenerateSafepointTable();
+}
+
+
+void LCodeGen::FinishCode(Handle<Code> code) {
+ ASSERT(is_done());
+ code->set_stack_slots(GetStackSlotCount());
+ code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
+ if (FLAG_weak_embedded_maps_in_optimized_code) {
+ RegisterDependentCodeForEmbeddedMaps(code);
+ }
+ PopulateDeoptimizationData(code);
+ info()->CommitDependencies(code);
+}
+
+
+void LChunkBuilder::Abort(BailoutReason reason) {
+ info()->set_bailout_reason(reason);
+ 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.
+ int length = builder.position();
+ Vector<char> copy = Vector<char>::New(length + 1);
+ OS::MemCopy(copy.start(), builder.Finalize(), copy.length());
+ masm()->RecordComment(copy.start());
+}
+
+
+#ifdef _MSC_VER
+void LCodeGen::MakeSureStackPagesMapped(int offset) {
+ const int kPageSize = 4 * KB;
+ for (offset -= kPageSize; offset > 0; offset -= kPageSize) {
+ __ movq(Operand(rsp, offset), rax);
+ }
+}
+#endif
+
+
+bool LCodeGen::GeneratePrologue() {
+ ASSERT(is_generating());
+
+ if (info()->IsOptimizing()) {
+ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info_->function()->name()->IsUtf8EqualTo(CStrVector(FLAG_stop_at))) {
+ __ int3();
+ }
+#endif
+
+ // Strict mode functions 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_classic_mode() || info_->is_native()) {
+ Label ok;
+ __ testq(rcx, rcx);
+ __ j(zero, &ok, Label::kNear);
+ // +1 for return address.
+ int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
+ __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
+ __ movq(Operand(rsp, receiver_offset), kScratchRegister);
+ __ bind(&ok);
+ }
+ }
+
+ info()->set_prologue_offset(masm_->pc_offset());
+ if (NeedsEagerFrame()) {
+ ASSERT(!frame_is_built_);
+ frame_is_built_ = true;
+ __ push(rbp); // Caller's frame pointer.
+ __ movq(rbp, rsp);
+ __ push(rsi); // Callee's context.
+ if (info()->IsStub()) {
+ __ Push(Smi::FromInt(StackFrame::STUB));
+ } else {
+ __ push(rdi); // Callee's JS function.
+ }
+ info()->AddNoFrameRange(0, masm_->pc_offset());
+ }
+
+ // Reserve space for the stack slots needed by the code.
+ int slots = GetStackSlotCount();
+ if (slots > 0) {
+ if (FLAG_debug_code) {
+ __ subq(rsp, Immediate(slots * kPointerSize));
+#ifdef _MSC_VER
+ MakeSureStackPagesMapped(slots * kPointerSize);
+#endif
+ __ push(rax);
+ __ Set(rax, slots);
+ __ movq(kScratchRegister, kSlotsZapValue, RelocInfo::NONE64);
+ Label loop;
+ __ bind(&loop);
+ __ movq(MemOperand(rsp, rax, times_pointer_size, 0),
+ kScratchRegister);
+ __ decl(rax);
+ __ j(not_zero, &loop);
+ __ pop(rax);
+ } else {
+ __ subq(rsp, Immediate(slots * kPointerSize));
+#ifdef _MSC_VER
+ MakeSureStackPagesMapped(slots * kPointerSize);
+#endif
+ }
+
+ if (info()->saves_caller_doubles()) {
+ Comment(";;; Save clobbered callee double registers");
+ int count = 0;
+ BitVector* doubles = chunk()->allocated_double_registers();
+ BitVector::Iterator save_iterator(doubles);
+ while (!save_iterator.Done()) {
+ __ movsd(MemOperand(rsp, count * kDoubleSize),
+ XMMRegister::FromAllocationIndex(save_iterator.Current()));
+ save_iterator.Advance();
+ count++;
+ }
+ }
+ }
+
+ // Possibly allocate a local context.
+ int heap_slots = info_->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ if (heap_slots > 0) {
+ Comment(";;; Allocate local context");
+ // Argument to NewContext is the function, which is still in rdi.
+ __ push(rdi);
+ if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kNewFunctionContext, 1);
+ }
+ RecordSafepoint(Safepoint::kNoLazyDeopt);
+ // Context is returned in both rax and rsi. It replaces the context
+ // passed to us. It's saved in the stack and kept live in rsi.
+ __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi);
+
+ // 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.
+ __ movq(rax, Operand(rbp, parameter_offset));
+ // Store it in the context.
+ int context_offset = Context::SlotOffset(var->index());
+ __ movq(Operand(rsi, context_offset), rax);
+ // Update the write barrier. This clobbers rax and rbx.
+ __ RecordWriteContextSlot(rsi, context_offset, rax, rbx, kSaveFPRegs);
+ }
+ }
+ Comment(";;; End allocate local context");
+ }
+
+ // Trace the call.
+ if (FLAG_trace && info()->IsOptimizing()) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+ 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_);
+
+ // Don't emit code for basic blocks with a replacement.
+ if (instr->IsLabel()) {
+ emit_instructions = !LLabel::cast(instr)->HasReplacement();
+ }
+ if (!emit_instructions) continue;
+
+ if (FLAG_code_comments && instr->HasInterestingComment(this)) {
+ Comment(";;; <@%d,#%d> %s",
+ current_instruction_,
+ instr->hydrogen_value()->id(),
+ instr->Mnemonic());
+ }
+
+ RecordAndUpdatePosition(instr->position());
+
+ instr->CompileToNative(this);
+ }
+ EnsureSpaceForLazyDeopt(Deoptimizer::patch_size());
+ return !is_aborted();
+}
+
+
+bool LCodeGen::GenerateJumpTable() {
+ Label needs_frame;
+ if (jump_table_.length() > 0) {
+ Comment(";;; -------------------- Jump table --------------------");
+ }
+ for (int i = 0; i < jump_table_.length(); i++) {
+ __ bind(&jump_table_[i].label);
+ Address entry = jump_table_[i].address;
+ Deoptimizer::BailoutType type = jump_table_[i].bailout_type;
+ int id = Deoptimizer::GetDeoptimizationId(isolate(), entry, type);
+ if (id == Deoptimizer::kNotDeoptimizationEntry) {
+ Comment(";;; jump table entry %d.", i);
+ } else {
+ Comment(";;; jump table entry %d: deoptimization bailout %d.", i, id);
+ }
+ if (jump_table_[i].needs_frame) {
+ __ movq(kScratchRegister, ExternalReference::ForDeoptEntry(entry));
+ if (needs_frame.is_bound()) {
+ __ jmp(&needs_frame);
+ } else {
+ __ bind(&needs_frame);
+ __ push(rbp);
+ __ movq(rbp, rsp);
+ __ push(rsi);
+ // This variant of deopt can only be used with stubs. Since we don't
+ // have a function pointer to install in the stack frame that we're
+ // building, install a special marker there instead.
+ ASSERT(info()->IsStub());
+ __ Move(rsi, Smi::FromInt(StackFrame::STUB));
+ __ push(rsi);
+ __ movq(rsi, MemOperand(rsp, kPointerSize));
+ __ call(kScratchRegister);
+ }
+ } else {
+ __ call(entry, RelocInfo::RUNTIME_ENTRY);
+ }
+ }
+ 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];
+
+ int pos = instructions_->at(code->instruction_index())->position();
+ RecordAndUpdatePosition(pos);
+
+ Comment(";;; <@%d,#%d> "
+ "-------------------- Deferred %s --------------------",
+ code->instruction_index(),
+ code->instr()->hydrogen_value()->id(),
+ code->instr()->Mnemonic());
+ __ bind(code->entry());
+ if (NeedsDeferredFrame()) {
+ Comment(";;; Build frame");
+ ASSERT(!frame_is_built_);
+ ASSERT(info()->IsStub());
+ frame_is_built_ = true;
+ // Build the frame in such a way that esi isn't trashed.
+ __ push(rbp); // Caller's frame pointer.
+ __ push(Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ Push(Smi::FromInt(StackFrame::STUB));
+ __ lea(rbp, Operand(rsp, 2 * kPointerSize));
+ Comment(";;; Deferred code");
+ }
+ code->Generate();
+ if (NeedsDeferredFrame()) {
+ Comment(";;; Destroy frame");
+ ASSERT(frame_is_built_);
+ frame_is_built_ = false;
+ __ movq(rsp, rbp);
+ __ pop(rbp);
+ }
+ __ 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::GenerateSafepointTable() {
+ ASSERT(is_done());
+ safepoints_.Emit(masm(), GetStackSlotCount());
+ return !is_aborted();
+}
+
+
+Register LCodeGen::ToRegister(int index) const {
+ return Register::FromAllocationIndex(index);
+}
+
+
+XMMRegister LCodeGen::ToDoubleRegister(int index) const {
+ return XMMRegister::FromAllocationIndex(index);
+}
+
+
+Register LCodeGen::ToRegister(LOperand* op) const {
+ ASSERT(op->IsRegister());
+ return ToRegister(op->index());
+}
+
+
+XMMRegister LCodeGen::ToDoubleRegister(LOperand* op) const {
+ ASSERT(op->IsDoubleRegister());
+ return ToDoubleRegister(op->index());
+}
+
+
+bool LCodeGen::IsInteger32Constant(LConstantOperand* op) const {
+ return op->IsConstantOperand() &&
+ chunk_->LookupLiteralRepresentation(op).IsSmiOrInteger32();
+}
+
+
+bool LCodeGen::IsSmiConstant(LConstantOperand* op) const {
+ return op->IsConstantOperand() &&
+ chunk_->LookupLiteralRepresentation(op).IsSmi();
+}
+
+
+bool LCodeGen::IsTaggedConstant(LConstantOperand* op) const {
+ return op->IsConstantOperand() &&
+ chunk_->LookupLiteralRepresentation(op).IsTagged();
+}
+
+
+int32_t LCodeGen::ToInteger32(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ return constant->Integer32Value();
+}
+
+
+Smi* LCodeGen::ToSmi(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ return Smi::FromInt(constant->Integer32Value());
+}
+
+
+double LCodeGen::ToDouble(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(constant->HasDoubleValue());
+ return constant->DoubleValue();
+}
+
+
+ExternalReference LCodeGen::ToExternalReference(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(constant->HasExternalReferenceValue());
+ return constant->ExternalReferenceValue();
+}
+
+
+Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const {
+ HConstant* constant = chunk_->LookupConstant(op);
+ ASSERT(chunk_->LookupLiteralRepresentation(op).IsSmiOrTagged());
+ return constant->handle();
+}
+
+
+Operand LCodeGen::ToOperand(LOperand* op) const {
+ // Does not handle registers. In X64 assembler, plain registers are not
+ // representable as an Operand.
+ ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot());
+ return Operand(rbp, StackSlotOffset(op->index()));
+}
+
+
+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->translation_size();
+ // The output frame height does not include the parameters.
+ int height = translation_size - environment->parameter_count();
+
+ WriteTranslation(environment->outer(), translation);
+ bool has_closure_id = !info()->closure().is_null() &&
+ !info()->closure().is_identical_to(environment->closure());
+ int closure_id = has_closure_id
+ ? DefineDeoptimizationLiteral(environment->closure())
+ : Translation::kSelfLiteralId;
+
+ switch (environment->frame_type()) {
+ case JS_FUNCTION:
+ translation->BeginJSFrame(environment->ast_id(), closure_id, height);
+ break;
+ case JS_CONSTRUCT:
+ translation->BeginConstructStubFrame(closure_id, translation_size);
+ break;
+ case JS_GETTER:
+ ASSERT(translation_size == 1);
+ ASSERT(height == 0);
+ translation->BeginGetterStubFrame(closure_id);
+ break;
+ case JS_SETTER:
+ ASSERT(translation_size == 2);
+ ASSERT(height == 0);
+ translation->BeginSetterStubFrame(closure_id);
+ break;
+ case ARGUMENTS_ADAPTOR:
+ translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
+ break;
+ case STUB:
+ translation->BeginCompiledStubFrame();
+ break;
+ }
+
+ int object_index = 0;
+ int dematerialized_index = 0;
+ for (int i = 0; i < translation_size; ++i) {
+ LOperand* value = environment->values()->at(i);
+ AddToTranslation(environment,
+ translation,
+ value,
+ environment->HasTaggedValueAt(i),
+ environment->HasUint32ValueAt(i),
+ &object_index,
+ &dematerialized_index);
+ }
+}
+
+
+void LCodeGen::AddToTranslation(LEnvironment* environment,
+ Translation* translation,
+ LOperand* op,
+ bool is_tagged,
+ bool is_uint32,
+ int* object_index_pointer,
+ int* dematerialized_index_pointer) {
+ if (op == LEnvironment::materialization_marker()) {
+ int object_index = (*object_index_pointer)++;
+ if (environment->ObjectIsDuplicateAt(object_index)) {
+ int dupe_of = environment->ObjectDuplicateOfAt(object_index);
+ translation->DuplicateObject(dupe_of);
+ return;
+ }
+ int object_length = environment->ObjectLengthAt(object_index);
+ if (environment->ObjectIsArgumentsAt(object_index)) {
+ translation->BeginArgumentsObject(object_length);
+ } else {
+ translation->BeginCapturedObject(object_length);
+ }
+ int dematerialized_index = *dematerialized_index_pointer;
+ int env_offset = environment->translation_size() + dematerialized_index;
+ *dematerialized_index_pointer += object_length;
+ for (int i = 0; i < object_length; ++i) {
+ LOperand* value = environment->values()->at(env_offset + i);
+ AddToTranslation(environment,
+ translation,
+ value,
+ environment->HasTaggedValueAt(env_offset + i),
+ environment->HasUint32ValueAt(env_offset + i),
+ object_index_pointer,
+ dematerialized_index_pointer);
+ }
+ return;
+ }
+
+ if (op->IsStackSlot()) {
+ if (is_tagged) {
+ translation->StoreStackSlot(op->index());
+ } else if (is_uint32) {
+ translation->StoreUint32StackSlot(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 if (is_uint32) {
+ translation->StoreUint32Register(reg);
+ } else {
+ translation->StoreInt32Register(reg);
+ }
+ } else if (op->IsDoubleRegister()) {
+ XMMRegister reg = ToDoubleRegister(op);
+ translation->StoreDoubleRegister(reg);
+ } else if (op->IsConstantOperand()) {
+ HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op));
+ int src_index = DefineDeoptimizationLiteral(constant->handle());
+ translation->StoreLiteral(src_index);
+ } else {
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ SafepointMode safepoint_mode,
+ int argc) {
+ EnsureSpaceForLazyDeopt(Deoptimizer::patch_size() - masm()->CallSize(code));
+ ASSERT(instr != NULL);
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+ __ call(code, mode);
+ RecordSafepointWithLazyDeopt(instr, safepoint_mode, argc);
+
+ // Signal that we don't inline smi code before these stubs in the
+ // optimizing code generator.
+ if (code->kind() == Code::BINARY_OP_IC ||
+ code->kind() == Code::COMPARE_IC) {
+ __ nop();
+ }
+}
+
+
+void LCodeGen::CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr) {
+ CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT, 0);
+}
+
+
+void LCodeGen::CallRuntime(const Runtime::Function* function,
+ int num_arguments,
+ LInstruction* instr) {
+ ASSERT(instr != NULL);
+ ASSERT(instr->HasPointerMap());
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+
+ __ CallRuntime(function, num_arguments);
+ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0);
+}
+
+
+void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id,
+ int argc,
+ LInstruction* instr) {
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ 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->frame_type() == JS_FUNCTION) {
+ ++jsframe_count;
+ }
+ }
+ Translation translation(&translations_, frame_count, jsframe_count, zone());
+ 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, environment->zone());
+ }
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition cc,
+ LEnvironment* environment,
+ Deoptimizer::BailoutType bailout_type) {
+ RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt);
+ ASSERT(environment->HasBeenRegistered());
+ int id = environment->deoptimization_index();
+ ASSERT(info()->IsOptimizing() || info()->IsStub());
+ Address entry =
+ Deoptimizer::GetDeoptimizationEntry(isolate(), id, bailout_type);
+ if (entry == NULL) {
+ Abort(kBailoutWasNotPrepared);
+ return;
+ }
+
+ ASSERT(FLAG_deopt_every_n_times == 0); // Not yet implemented on x64.
+
+ if (info()->ShouldTrapOnDeopt()) {
+ Label done;
+ if (cc != no_condition) {
+ __ j(NegateCondition(cc), &done, Label::kNear);
+ }
+ __ int3();
+ __ bind(&done);
+ }
+
+ ASSERT(info()->IsStub() || frame_is_built_);
+ if (cc == no_condition && frame_is_built_) {
+ __ call(entry, RelocInfo::RUNTIME_ENTRY);
+ } else {
+ // We often have several deopts to the same entry, reuse the last
+ // jump entry if this is the case.
+ if (jump_table_.is_empty() ||
+ jump_table_.last().address != entry ||
+ jump_table_.last().needs_frame != !frame_is_built_ ||
+ jump_table_.last().bailout_type != bailout_type) {
+ Deoptimizer::JumpTableEntry table_entry(entry,
+ bailout_type,
+ !frame_is_built_);
+ jump_table_.Add(table_entry, zone());
+ }
+ if (cc == no_condition) {
+ __ jmp(&jump_table_.last().label);
+ } else {
+ __ j(cc, &jump_table_.last().label);
+ }
+ }
+}
+
+
+void LCodeGen::DeoptimizeIf(Condition cc,
+ LEnvironment* environment) {
+ Deoptimizer::BailoutType bailout_type = info()->IsStub()
+ ? Deoptimizer::LAZY
+ : Deoptimizer::EAGER;
+ DeoptimizeIf(cc, environment, bailout_type);
+}
+
+
+void LCodeGen::RegisterDependentCodeForEmbeddedMaps(Handle<Code> code) {
+ ZoneList<Handle<Map> > maps(1, zone());
+ int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) {
+ RelocInfo::Mode mode = it.rinfo()->rmode();
+ if (mode == RelocInfo::EMBEDDED_OBJECT &&
+ it.rinfo()->target_object()->IsMap()) {
+ Handle<Map> map(Map::cast(it.rinfo()->target_object()));
+ if (map->CanTransition()) {
+ maps.Add(map, zone());
+ }
+ }
+ }
+#ifdef VERIFY_HEAP
+ // This disables verification of weak embedded maps after full GC.
+ // AddDependentCode can cause a GC, which would observe the state where
+ // this code is not yet in the depended code lists of the embedded maps.
+ NoWeakEmbeddedMapsVerificationScope disable_verification_of_embedded_maps;
+#endif
+ for (int i = 0; i < maps.length(); i++) {
+ maps.at(i)->AddDependentCode(DependentCode::kWeaklyEmbeddedGroup, code);
+ }
+}
+
+
+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(isolate()->factory());
+ data->SetTranslationByteArray(*translations);
+ data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_));
+
+ Handle<FixedArray> literals =
+ factory()->NewFixedArray(deoptimization_literals_.length(), TENURED);
+ { AllowDeferredHandleDereference copy_handles;
+ 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().ToInt()));
+ 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, 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, zone());
+ 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, int argc) {
+ if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) {
+ RecordSafepoint(instr->pointer_map(), Safepoint::kLazyDeopt);
+ } else {
+ ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), argc, Safepoint::kLazyDeopt);
+ }
+}
+
+
+void LCodeGen::RecordSafepoint(
+ LPointerMap* pointers,
+ Safepoint::Kind kind,
+ int arguments,
+ Safepoint::DeoptMode deopt_mode) {
+ ASSERT(kind == expected_safepoint_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(), zone());
+ } else if (pointer->IsRegister() && (kind & Safepoint::kWithRegisters)) {
+ safepoint.DefinePointerRegister(ToRegister(pointer), zone());
+ }
+ }
+ if (kind & Safepoint::kWithRegisters) {
+ // Register rsi always contains a pointer to the context.
+ safepoint.DefinePointerRegister(rsi, zone());
+ }
+}
+
+
+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, zone());
+ 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::RecordPosition(int position) {
+ if (position == RelocInfo::kNoPosition) return;
+ masm()->positions_recorder()->RecordPosition(position);
+}
+
+
+void LCodeGen::RecordAndUpdatePosition(int position) {
+ if (position >= 0 && position != old_position_) {
+ masm()->positions_recorder()->RecordPosition(position);
+ old_position_ = position;
+ }
+}
+
+
+static const char* LabelType(LLabel* label) {
+ if (label->is_loop_header()) return " (loop header)";
+ if (label->is_osr_entry()) return " (OSR entry)";
+ return "";
+}
+
+
+void LCodeGen::DoLabel(LLabel* label) {
+ Comment(";;; <@%d,#%d> -------------------- B%d%s --------------------",
+ current_instruction_,
+ label->hydrogen_value()->id(),
+ label->block_id(),
+ LabelType(label));
+ __ 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(rax));
+ switch (instr->hydrogen()->major_key()) {
+ case CodeStub::RegExpConstructResult: {
+ RegExpConstructResultStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::RegExpExec: {
+ RegExpExecStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::SubString: {
+ SubStringStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::NumberToString: {
+ NumberToStringStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::StringCompare: {
+ StringCompareStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ case CodeStub::TranscendentalCache: {
+ TranscendentalCacheStub stub(instr->transcendental_type(),
+ TranscendentalCacheStub::TAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) {
+ // Record the address of the first unknown OSR value as the place to enter.
+ if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset();
+}
+
+
+void LCodeGen::DoModI(LModI* instr) {
+ HMod* hmod = instr->hydrogen();
+ HValue* left = hmod->left();
+ HValue* right = hmod->right();
+ if (hmod->HasPowerOf2Divisor()) {
+ // TODO(svenpanne) We should really do the strength reduction on the
+ // Hydrogen level.
+ Register left_reg = ToRegister(instr->left());
+ ASSERT(left_reg.is(ToRegister(instr->result())));
+
+ // Note: The code below even works when right contains kMinInt.
+ int32_t divisor = Abs(right->GetInteger32Constant());
+
+ Label left_is_not_negative, done;
+ if (left->CanBeNegative()) {
+ __ testl(left_reg, left_reg);
+ __ j(not_sign, &left_is_not_negative, Label::kNear);
+ __ negl(left_reg);
+ __ andl(left_reg, Immediate(divisor - 1));
+ __ negl(left_reg);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(zero, instr->environment());
+ }
+ __ jmp(&done, Label::kNear);
+ }
+
+ __ bind(&left_is_not_negative);
+ __ andl(left_reg, Immediate(divisor - 1));
+ __ bind(&done);
+
+ } else if (hmod->fixed_right_arg().has_value) {
+ Register left_reg = ToRegister(instr->left());
+ ASSERT(left_reg.is(ToRegister(instr->result())));
+ Register right_reg = ToRegister(instr->right());
+
+ int32_t divisor = hmod->fixed_right_arg().value;
+ ASSERT(IsPowerOf2(divisor));
+
+ // Check if our assumption of a fixed right operand still holds.
+ __ cmpl(right_reg, Immediate(divisor));
+ DeoptimizeIf(not_equal, instr->environment());
+
+ Label left_is_not_negative, done;
+ if (left->CanBeNegative()) {
+ __ testl(left_reg, left_reg);
+ __ j(not_sign, &left_is_not_negative, Label::kNear);
+ __ negl(left_reg);
+ __ andl(left_reg, Immediate(divisor - 1));
+ __ negl(left_reg);
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(zero, instr->environment());
+ }
+ __ jmp(&done, Label::kNear);
+ }
+
+ __ bind(&left_is_not_negative);
+ __ andl(left_reg, Immediate(divisor - 1));
+ __ bind(&done);
+
+ } else {
+ Register left_reg = ToRegister(instr->left());
+ ASSERT(left_reg.is(rax));
+ Register right_reg = ToRegister(instr->right());
+ ASSERT(!right_reg.is(rax));
+ ASSERT(!right_reg.is(rdx));
+ Register result_reg = ToRegister(instr->result());
+ ASSERT(result_reg.is(rdx));
+
+ Label done;
+ // Check for x % 0, idiv would signal a divide error. We have to
+ // deopt in this case because we can't return a NaN.
+ if (right->CanBeZero()) {
+ __ testl(right_reg, right_reg);
+ DeoptimizeIf(zero, instr->environment());
+ }
+
+ // Check for kMinInt % -1, idiv would signal a divide error. We
+ // have to deopt if we care about -0, because we can't return that.
+ if (left->RangeCanInclude(kMinInt) && right->RangeCanInclude(-1)) {
+ Label no_overflow_possible;
+ __ cmpl(left_reg, Immediate(kMinInt));
+ __ j(not_zero, &no_overflow_possible, Label::kNear);
+ __ cmpl(right_reg, Immediate(-1));
+ if (hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(equal, instr->environment());
+ } else {
+ __ j(not_equal, &no_overflow_possible, Label::kNear);
+ __ Set(result_reg, 0);
+ __ jmp(&done, Label::kNear);
+ }
+ __ bind(&no_overflow_possible);
+ }
+
+ // Sign extend dividend in eax into edx:eax, since we are using only the low
+ // 32 bits of the values.
+ __ cdq();
+
+ // If we care about -0, test if the dividend is <0 and the result is 0.
+ if (left->CanBeNegative() &&
+ hmod->CanBeZero() &&
+ hmod->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label positive_left;
+ __ testl(left_reg, left_reg);
+ __ j(not_sign, &positive_left, Label::kNear);
+ __ idivl(right_reg);
+ __ testl(result_reg, result_reg);
+ DeoptimizeIf(zero, instr->environment());
+ __ jmp(&done, Label::kNear);
+ __ bind(&positive_left);
+ }
+ __ idivl(right_reg);
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
+ ASSERT(instr->right()->IsConstantOperand());
+
+ const Register dividend = ToRegister(instr->left());
+ int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right()));
+ const Register result = ToRegister(instr->result());
+
+ switch (divisor) {
+ case 0:
+ DeoptimizeIf(no_condition, instr->environment());
+ return;
+
+ case 1:
+ if (!result.is(dividend)) {
+ __ movl(result, dividend);
+ }
+ return;
+
+ case -1:
+ if (!result.is(dividend)) {
+ __ movl(result, dividend);
+ }
+ __ negl(result);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(zero, instr->environment());
+ }
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+ return;
+ }
+
+ uint32_t divisor_abs = abs(divisor);
+ if (IsPowerOf2(divisor_abs)) {
+ int32_t power = WhichPowerOf2(divisor_abs);
+ if (divisor < 0) {
+ __ movsxlq(result, dividend);
+ __ neg(result);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(zero, instr->environment());
+ }
+ __ sar(result, Immediate(power));
+ } else {
+ if (!result.is(dividend)) {
+ __ movl(result, dividend);
+ }
+ __ sarl(result, Immediate(power));
+ }
+ } else {
+ Register reg1 = ToRegister(instr->temp());
+ Register reg2 = ToRegister(instr->result());
+
+ // Find b which: 2^b < divisor_abs < 2^(b+1).
+ unsigned b = 31 - CompilerIntrinsics::CountLeadingZeros(divisor_abs);
+ unsigned shift = 32 + b; // Precision +1bit (effectively).
+ double multiplier_f =
+ static_cast<double>(static_cast<uint64_t>(1) << shift) / divisor_abs;
+ int64_t multiplier;
+ if (multiplier_f - floor(multiplier_f) < 0.5) {
+ multiplier = static_cast<int64_t>(floor(multiplier_f));
+ } else {
+ multiplier = static_cast<int64_t>(floor(multiplier_f)) + 1;
+ }
+ // The multiplier is a uint32.
+ ASSERT(multiplier > 0 &&
+ multiplier < (static_cast<int64_t>(1) << 32));
+ // The multiply is int64, so sign-extend to r64.
+ __ movsxlq(reg1, dividend);
+ if (divisor < 0 &&
+ instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ neg(reg1);
+ DeoptimizeIf(zero, instr->environment());
+ }
+ __ movq(reg2, multiplier, RelocInfo::NONE64);
+ // Result just fit in r64, because it's int32 * uint32.
+ __ imul(reg2, reg1);
+
+ __ addq(reg2, Immediate(1 << 30));
+ __ sar(reg2, Immediate(shift));
+ }
+}
+
+
+void LCodeGen::DoDivI(LDivI* instr) {
+ if (!instr->is_flooring() && instr->hydrogen()->HasPowerOf2Divisor()) {
+ Register dividend = ToRegister(instr->left());
+ int32_t divisor =
+ HConstant::cast(instr->hydrogen()->right())->Integer32Value();
+ int32_t test_value = 0;
+ int32_t power = 0;
+
+ if (divisor > 0) {
+ test_value = divisor - 1;
+ power = WhichPowerOf2(divisor);
+ } else {
+ // Check for (0 / -x) that will produce negative zero.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ testl(dividend, dividend);
+ DeoptimizeIf(zero, instr->environment());
+ }
+ // Check for (kMinInt / -1).
+ if (divisor == -1 && instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ __ cmpl(dividend, Immediate(kMinInt));
+ DeoptimizeIf(zero, instr->environment());
+ }
+ test_value = - divisor - 1;
+ power = WhichPowerOf2(-divisor);
+ }
+
+ if (test_value != 0) {
+ if (instr->hydrogen()->CheckFlag(
+ HInstruction::kAllUsesTruncatingToInt32)) {
+ Label done, negative;
+ __ cmpl(dividend, Immediate(0));
+ __ j(less, &negative, Label::kNear);
+ __ sarl(dividend, Immediate(power));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&negative);
+ __ negl(dividend);
+ __ sarl(dividend, Immediate(power));
+ if (divisor > 0) __ negl(dividend);
+ __ bind(&done);
+ return; // Don't fall through to "__ neg" below.
+ } else {
+ // Deoptimize if remainder is not 0.
+ __ testl(dividend, Immediate(test_value));
+ DeoptimizeIf(not_zero, instr->environment());
+ __ sarl(dividend, Immediate(power));
+ }
+ }
+
+ if (divisor < 0) __ negl(dividend);
+
+ return;
+ }
+
+ LOperand* right = instr->right();
+ ASSERT(ToRegister(instr->result()).is(rax));
+ ASSERT(ToRegister(instr->left()).is(rax));
+ ASSERT(!ToRegister(instr->right()).is(rax));
+ ASSERT(!ToRegister(instr->right()).is(rdx));
+
+ Register left_reg = rax;
+
+ // Check for x / 0.
+ Register right_reg = ToRegister(right);
+ if (instr->hydrogen_value()->CheckFlag(HValue::kCanBeDivByZero)) {
+ __ testl(right_reg, right_reg);
+ DeoptimizeIf(zero, instr->environment());
+ }
+
+ // Check for (0 / -x) that will produce negative zero.
+ if (instr->hydrogen_value()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label left_not_zero;
+ __ testl(left_reg, left_reg);
+ __ j(not_zero, &left_not_zero, Label::kNear);
+ __ testl(right_reg, right_reg);
+ DeoptimizeIf(sign, instr->environment());
+ __ bind(&left_not_zero);
+ }
+
+ // Check for (kMinInt / -1).
+ if (instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow)) {
+ Label left_not_min_int;
+ __ cmpl(left_reg, Immediate(kMinInt));
+ __ j(not_zero, &left_not_min_int, Label::kNear);
+ __ cmpl(right_reg, Immediate(-1));
+ DeoptimizeIf(zero, instr->environment());
+ __ bind(&left_not_min_int);
+ }
+
+ // Sign extend to rdx.
+ __ cdq();
+ __ idivl(right_reg);
+
+ if (instr->is_flooring()) {
+ Label done;
+ __ testl(rdx, rdx);
+ __ j(zero, &done, Label::kNear);
+ __ xorl(rdx, right_reg);
+ __ sarl(rdx, Immediate(31));
+ __ addl(rax, rdx);
+ __ bind(&done);
+ } else if (!instr->hydrogen()->CheckFlag(
+ HInstruction::kAllUsesTruncatingToInt32)) {
+ // Deoptimize if remainder is not 0.
+ __ testl(rdx, rdx);
+ DeoptimizeIf(not_zero, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoMulI(LMulI* instr) {
+ Register left = ToRegister(instr->left());
+ LOperand* right = instr->right();
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ movl(kScratchRegister, left);
+ }
+
+ bool can_overflow =
+ instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
+ if (right->IsConstantOperand()) {
+ int32_t right_value = ToInteger32(LConstantOperand::cast(right));
+ if (right_value == -1) {
+ __ negl(left);
+ } else if (right_value == 0) {
+ __ xorl(left, left);
+ } else if (right_value == 2) {
+ __ addl(left, left);
+ } else if (!can_overflow) {
+ // If the multiplication is known to not overflow, we
+ // can use operations that don't set the overflow flag
+ // correctly.
+ switch (right_value) {
+ case 1:
+ // Do nothing.
+ break;
+ case 3:
+ __ leal(left, Operand(left, left, times_2, 0));
+ break;
+ case 4:
+ __ shll(left, Immediate(2));
+ break;
+ case 5:
+ __ leal(left, Operand(left, left, times_4, 0));
+ break;
+ case 8:
+ __ shll(left, Immediate(3));
+ break;
+ case 9:
+ __ leal(left, Operand(left, left, times_8, 0));
+ break;
+ case 16:
+ __ shll(left, Immediate(4));
+ break;
+ default:
+ __ imull(left, left, Immediate(right_value));
+ break;
+ }
+ } else {
+ __ imull(left, left, Immediate(right_value));
+ }
+ } else if (right->IsStackSlot()) {
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ SmiToInteger32(left, left);
+ __ imul(left, ToOperand(right));
+ } else {
+ __ imull(left, ToOperand(right));
+ }
+ } else {
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ SmiToInteger32(left, left);
+ __ imul(left, ToRegister(right));
+ } else {
+ __ imull(left, ToRegister(right));
+ }
+ }
+
+ if (can_overflow) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Bail out if the result is supposed to be negative zero.
+ Label done;
+ __ testl(left, left);
+ __ j(not_zero, &done, Label::kNear);
+ if (right->IsConstantOperand()) {
+ if (ToInteger32(LConstantOperand::cast(right)) < 0) {
+ DeoptimizeIf(no_condition, instr->environment());
+ } else if (ToInteger32(LConstantOperand::cast(right)) == 0) {
+ __ cmpl(kScratchRegister, Immediate(0));
+ DeoptimizeIf(less, instr->environment());
+ }
+ } else if (right->IsStackSlot()) {
+ __ orl(kScratchRegister, ToOperand(right));
+ DeoptimizeIf(sign, instr->environment());
+ } else {
+ // Test the non-zero operand for negative sign.
+ __ orl(kScratchRegister, ToRegister(right));
+ DeoptimizeIf(sign, instr->environment());
+ }
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoBitI(LBitI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ ASSERT(left->Equals(instr->result()));
+ ASSERT(left->IsRegister());
+
+ if (right->IsConstantOperand()) {
+ int32_t right_operand = ToInteger32(LConstantOperand::cast(right));
+ switch (instr->op()) {
+ case Token::BIT_AND:
+ __ andl(ToRegister(left), Immediate(right_operand));
+ break;
+ case Token::BIT_OR:
+ __ orl(ToRegister(left), Immediate(right_operand));
+ break;
+ case Token::BIT_XOR:
+ if (right_operand == int32_t(~0)) {
+ __ not_(ToRegister(left));
+ } else {
+ __ xorl(ToRegister(left), Immediate(right_operand));
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } else if (right->IsStackSlot()) {
+ switch (instr->op()) {
+ case Token::BIT_AND:
+ __ and_(ToRegister(left), ToOperand(right));
+ break;
+ case Token::BIT_OR:
+ __ or_(ToRegister(left), ToOperand(right));
+ break;
+ case Token::BIT_XOR:
+ __ xor_(ToRegister(left), ToOperand(right));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } else {
+ ASSERT(right->IsRegister());
+ switch (instr->op()) {
+ case Token::BIT_AND:
+ __ and_(ToRegister(left), ToRegister(right));
+ break;
+ case Token::BIT_OR:
+ __ or_(ToRegister(left), ToRegister(right));
+ break;
+ case Token::BIT_XOR:
+ __ xor_(ToRegister(left), ToRegister(right));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoShiftI(LShiftI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ ASSERT(left->Equals(instr->result()));
+ ASSERT(left->IsRegister());
+ if (right->IsRegister()) {
+ ASSERT(ToRegister(right).is(rcx));
+
+ switch (instr->op()) {
+ case Token::ROR:
+ __ rorl_cl(ToRegister(left));
+ break;
+ case Token::SAR:
+ __ sarl_cl(ToRegister(left));
+ break;
+ case Token::SHR:
+ __ shrl_cl(ToRegister(left));
+ if (instr->can_deopt()) {
+ __ testl(ToRegister(left), ToRegister(left));
+ DeoptimizeIf(negative, instr->environment());
+ }
+ break;
+ case Token::SHL:
+ __ shll_cl(ToRegister(left));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } else {
+ int32_t value = ToInteger32(LConstantOperand::cast(right));
+ uint8_t shift_count = static_cast<uint8_t>(value & 0x1F);
+ switch (instr->op()) {
+ case Token::ROR:
+ if (shift_count != 0) {
+ __ rorl(ToRegister(left), Immediate(shift_count));
+ }
+ break;
+ case Token::SAR:
+ if (shift_count != 0) {
+ __ sarl(ToRegister(left), Immediate(shift_count));
+ }
+ break;
+ case Token::SHR:
+ if (shift_count == 0 && instr->can_deopt()) {
+ __ testl(ToRegister(left), ToRegister(left));
+ DeoptimizeIf(negative, instr->environment());
+ } else {
+ __ shrl(ToRegister(left), Immediate(shift_count));
+ }
+ break;
+ case Token::SHL:
+ if (shift_count != 0) {
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ shl(ToRegister(left), Immediate(shift_count));
+ } else {
+ __ shll(ToRegister(left), Immediate(shift_count));
+ }
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoSubI(LSubI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ ASSERT(left->Equals(instr->result()));
+
+ if (right->IsConstantOperand()) {
+ __ subl(ToRegister(left),
+ Immediate(ToInteger32(LConstantOperand::cast(right))));
+ } else if (right->IsRegister()) {
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ subq(ToRegister(left), ToRegister(right));
+ } else {
+ __ subl(ToRegister(left), ToRegister(right));
+ }
+ } else {
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ subq(ToRegister(left), ToOperand(right));
+ } else {
+ __ subl(ToRegister(left), ToOperand(right));
+ }
+ }
+
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoConstantI(LConstantI* instr) {
+ __ Set(ToRegister(instr->result()), instr->value());
+}
+
+
+void LCodeGen::DoConstantS(LConstantS* instr) {
+ __ Move(ToRegister(instr->result()), instr->value());
+}
+
+
+void LCodeGen::DoConstantD(LConstantD* instr) {
+ ASSERT(instr->result()->IsDoubleRegister());
+ XMMRegister res = ToDoubleRegister(instr->result());
+ double v = instr->value();
+ uint64_t int_val = BitCast<uint64_t, double>(v);
+ // Use xor to produce +0.0 in a fast and compact way, but avoid to
+ // do so if the constant is -0.0.
+ if (int_val == 0) {
+ __ xorps(res, res);
+ } else {
+ Register tmp = ToRegister(instr->temp());
+ __ Set(tmp, int_val);
+ __ movq(res, tmp);
+ }
+}
+
+
+void LCodeGen::DoConstantE(LConstantE* instr) {
+ __ LoadAddress(ToRegister(instr->result()), instr->value());
+}
+
+
+void LCodeGen::DoConstantT(LConstantT* instr) {
+ Handle<Object> value = instr->value();
+ AllowDeferredHandleDereference smi_check;
+ __ LoadObject(ToRegister(instr->result()), value);
+}
+
+
+void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
+ Register result = ToRegister(instr->result());
+ Register map = ToRegister(instr->value());
+ __ EnumLength(result, map);
+}
+
+
+void LCodeGen::DoElementsKind(LElementsKind* instr) {
+ Register result = ToRegister(instr->result());
+ Register input = ToRegister(instr->value());
+
+ // Load map into |result|.
+ __ movq(result, FieldOperand(input, HeapObject::kMapOffset));
+ // Load the map's "bit field 2" into |result|. We only need the first byte.
+ __ movzxbq(result, FieldOperand(result, Map::kBitField2Offset));
+ // Retrieve elements_kind from bit field 2.
+ __ and_(result, Immediate(Map::kElementsKindMask));
+ __ shr(result, Immediate(Map::kElementsKindShift));
+}
+
+
+void LCodeGen::DoValueOf(LValueOf* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+ ASSERT(input.is(result));
+ Label done;
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ // If the object is a smi return the object.
+ __ JumpIfSmi(input, &done, Label::kNear);
+ }
+
+ // If the object is not a value type, return the object.
+ __ CmpObjectType(input, JS_VALUE_TYPE, kScratchRegister);
+ __ j(not_equal, &done, Label::kNear);
+ __ movq(result, FieldOperand(input, JSValue::kValueOffset));
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDateField(LDateField* instr) {
+ Register object = ToRegister(instr->date());
+ Register result = ToRegister(instr->result());
+ Smi* index = instr->index();
+ Label runtime, done, not_date_object;
+ ASSERT(object.is(result));
+ ASSERT(object.is(rax));
+
+ Condition cc = masm()->CheckSmi(object);
+ DeoptimizeIf(cc, instr->environment());
+ __ CmpObjectType(object, JS_DATE_TYPE, kScratchRegister);
+ DeoptimizeIf(not_equal, instr->environment());
+
+ if (index->value() == 0) {
+ __ movq(result, FieldOperand(object, JSDate::kValueOffset));
+ } else {
+ if (index->value() < JSDate::kFirstUncachedField) {
+ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+ Operand stamp_operand = __ ExternalOperand(stamp);
+ __ movq(kScratchRegister, stamp_operand);
+ __ cmpq(kScratchRegister, FieldOperand(object,
+ JSDate::kCacheStampOffset));
+ __ j(not_equal, &runtime, Label::kNear);
+ __ movq(result, FieldOperand(object, JSDate::kValueOffset +
+ kPointerSize * index->value()));
+ __ jmp(&done);
+ }
+ __ bind(&runtime);
+ __ PrepareCallCFunction(2);
+ __ movq(arg_reg_1, object);
+ __ movq(arg_reg_2, index, RelocInfo::NONE64);
+ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) {
+ Register string = ToRegister(instr->string());
+ Register index = ToRegister(instr->index());
+ Register value = ToRegister(instr->value());
+ String::Encoding encoding = instr->encoding();
+
+ if (FLAG_debug_code) {
+ __ push(value);
+ __ movq(value, FieldOperand(string, HeapObject::kMapOffset));
+ __ movzxbq(value, FieldOperand(value, Map::kInstanceTypeOffset));
+
+ __ andb(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
+ static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
+ static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
+ __ cmpq(value, Immediate(encoding == String::ONE_BYTE_ENCODING
+ ? one_byte_seq_type : two_byte_seq_type));
+ __ Check(equal, kUnexpectedStringType);
+ __ pop(value);
+ }
+
+ if (encoding == String::ONE_BYTE_ENCODING) {
+ __ movb(FieldOperand(string, index, times_1, SeqString::kHeaderSize),
+ value);
+ } else {
+ __ movw(FieldOperand(string, index, times_2, SeqString::kHeaderSize),
+ value);
+ }
+}
+
+
+void LCodeGen::DoThrow(LThrow* instr) {
+ __ push(ToRegister(instr->value()));
+ CallRuntime(Runtime::kThrow, 1, instr);
+
+ if (FLAG_debug_code) {
+ Comment("Unreachable code.");
+ __ int3();
+ }
+}
+
+
+void LCodeGen::DoAddI(LAddI* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+
+ if (LAddI::UseLea(instr->hydrogen()) && !left->Equals(instr->result())) {
+ if (right->IsConstantOperand()) {
+ int32_t offset = ToInteger32(LConstantOperand::cast(right));
+ __ leal(ToRegister(instr->result()),
+ MemOperand(ToRegister(left), offset));
+ } else {
+ Operand address(ToRegister(left), ToRegister(right), times_1, 0);
+ if (instr->hydrogen()->representation().IsSmi()) {
+ __ lea(ToRegister(instr->result()), address);
+ } else {
+ __ leal(ToRegister(instr->result()), address);
+ }
+ }
+ } else {
+ if (right->IsConstantOperand()) {
+ __ addl(ToRegister(left),
+ Immediate(ToInteger32(LConstantOperand::cast(right))));
+ } else if (right->IsRegister()) {
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ addq(ToRegister(left), ToRegister(right));
+ } else {
+ __ addl(ToRegister(left), ToRegister(right));
+ }
+ } else {
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ addq(ToRegister(left), ToOperand(right));
+ } else {
+ __ addl(ToRegister(left), ToOperand(right));
+ }
+ }
+ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoMathMinMax(LMathMinMax* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ ASSERT(left->Equals(instr->result()));
+ HMathMinMax::Operation operation = instr->hydrogen()->operation();
+ if (instr->hydrogen()->representation().IsSmiOrInteger32()) {
+ Label return_left;
+ Condition condition = (operation == HMathMinMax::kMathMin)
+ ? less_equal
+ : greater_equal;
+ Register left_reg = ToRegister(left);
+ if (right->IsConstantOperand()) {
+ Immediate right_imm =
+ Immediate(ToInteger32(LConstantOperand::cast(right)));
+ ASSERT(!instr->hydrogen_value()->representation().IsSmi());
+ __ cmpl(left_reg, right_imm);
+ __ j(condition, &return_left, Label::kNear);
+ __ movq(left_reg, right_imm);
+ } else if (right->IsRegister()) {
+ Register right_reg = ToRegister(right);
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ cmpq(left_reg, right_reg);
+ } else {
+ __ cmpl(left_reg, right_reg);
+ }
+ __ j(condition, &return_left, Label::kNear);
+ __ movq(left_reg, right_reg);
+ } else {
+ Operand right_op = ToOperand(right);
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ cmpq(left_reg, right_op);
+ } else {
+ __ cmpl(left_reg, right_op);
+ }
+ __ j(condition, &return_left, Label::kNear);
+ __ movq(left_reg, right_op);
+ }
+ __ bind(&return_left);
+ } else {
+ ASSERT(instr->hydrogen()->representation().IsDouble());
+ Label check_nan_left, check_zero, return_left, return_right;
+ Condition condition = (operation == HMathMinMax::kMathMin) ? below : above;
+ XMMRegister left_reg = ToDoubleRegister(left);
+ XMMRegister right_reg = ToDoubleRegister(right);
+ __ ucomisd(left_reg, right_reg);
+ __ j(parity_even, &check_nan_left, Label::kNear); // At least one NaN.
+ __ j(equal, &check_zero, Label::kNear); // left == right.
+ __ j(condition, &return_left, Label::kNear);
+ __ jmp(&return_right, Label::kNear);
+
+ __ bind(&check_zero);
+ XMMRegister xmm_scratch = xmm0;
+ __ xorps(xmm_scratch, xmm_scratch);
+ __ ucomisd(left_reg, xmm_scratch);
+ __ j(not_equal, &return_left, Label::kNear); // left == right != 0.
+ // At this point, both left and right are either 0 or -0.
+ if (operation == HMathMinMax::kMathMin) {
+ __ orpd(left_reg, right_reg);
+ } else {
+ // Since we operate on +0 and/or -0, addsd and andsd have the same effect.
+ __ addsd(left_reg, right_reg);
+ }
+ __ jmp(&return_left, Label::kNear);
+
+ __ bind(&check_nan_left);
+ __ ucomisd(left_reg, left_reg); // NaN check.
+ __ j(parity_even, &return_left, Label::kNear);
+ __ bind(&return_right);
+ __ movsd(left_reg, right_reg);
+
+ __ bind(&return_left);
+ }
+}
+
+
+void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
+ XMMRegister left = ToDoubleRegister(instr->left());
+ XMMRegister right = ToDoubleRegister(instr->right());
+ XMMRegister result = ToDoubleRegister(instr->result());
+ // All operations except MOD are computed in-place.
+ ASSERT(instr->op() == Token::MOD || left.is(result));
+ switch (instr->op()) {
+ case Token::ADD:
+ __ addsd(left, right);
+ break;
+ case Token::SUB:
+ __ subsd(left, right);
+ break;
+ case Token::MUL:
+ __ mulsd(left, right);
+ break;
+ case Token::DIV:
+ __ divsd(left, right);
+ // Don't delete this mov. It may improve performance on some CPUs,
+ // when there is a mulsd depending on the result
+ __ movaps(left, left);
+ break;
+ case Token::MOD:
+ __ PrepareCallCFunction(2);
+ __ movaps(xmm0, left);
+ ASSERT(right.is(xmm1));
+ __ CallCFunction(
+ ExternalReference::double_fp_operation(Token::MOD, isolate()), 2);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ movaps(result, xmm0);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
+ ASSERT(ToRegister(instr->left()).is(rdx));
+ ASSERT(ToRegister(instr->right()).is(rax));
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ BinaryOpStub stub(instr->op(), NO_OVERWRITE);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ __ nop(); // Signals no inlined code.
+}
+
+
+int LCodeGen::GetNextEmittedBlock() const {
+ for (int i = current_block_ + 1; i < graph()->blocks()->length(); ++i) {
+ if (!chunk_->GetLabel(i)->HasReplacement()) return i;
+ }
+ return -1;
+}
+
+
+template<class InstrType>
+void LCodeGen::EmitBranch(InstrType instr, Condition cc) {
+ int left_block = instr->TrueDestination(chunk_);
+ int right_block = instr->FalseDestination(chunk_);
+
+ int next_block = GetNextEmittedBlock();
+
+ if (right_block == left_block || cc == no_condition) {
+ EmitGoto(left_block);
+ } else if (left_block == next_block) {
+ __ j(NegateCondition(cc), chunk_->GetAssemblyLabel(right_block));
+ } else if (right_block == next_block) {
+ __ j(cc, chunk_->GetAssemblyLabel(left_block));
+ } else {
+ __ j(cc, chunk_->GetAssemblyLabel(left_block));
+ if (cc != always) {
+ __ jmp(chunk_->GetAssemblyLabel(right_block));
+ }
+ }
+}
+
+
+template<class InstrType>
+void LCodeGen::EmitFalseBranch(InstrType instr, Condition cc) {
+ int false_block = instr->FalseDestination(chunk_);
+ __ j(cc, chunk_->GetAssemblyLabel(false_block));
+}
+
+
+void LCodeGen::DoDebugBreak(LDebugBreak* instr) {
+ __ int3();
+}
+
+
+void LCodeGen::DoIsNumberAndBranch(LIsNumberAndBranch* instr) {
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsSmiOrInteger32() || r.IsDouble()) {
+ EmitBranch(instr, no_condition);
+ } else {
+ ASSERT(r.IsTagged());
+ Register reg = ToRegister(instr->value());
+ HType type = instr->hydrogen()->value()->type();
+ if (type.IsTaggedNumber()) {
+ EmitBranch(instr, no_condition);
+ }
+ __ JumpIfSmi(reg, instr->TrueLabel(chunk_));
+ __ CompareRoot(FieldOperand(reg, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+ EmitBranch(instr, equal);
+ }
+}
+
+
+void LCodeGen::DoBranch(LBranch* instr) {
+ Representation r = instr->hydrogen()->value()->representation();
+ if (r.IsInteger32()) {
+ ASSERT(!info()->IsStub());
+ Register reg = ToRegister(instr->value());
+ __ testl(reg, reg);
+ EmitBranch(instr, not_zero);
+ } else if (r.IsSmi()) {
+ ASSERT(!info()->IsStub());
+ Register reg = ToRegister(instr->value());
+ __ testq(reg, reg);
+ EmitBranch(instr, not_zero);
+ } else if (r.IsDouble()) {
+ ASSERT(!info()->IsStub());
+ XMMRegister reg = ToDoubleRegister(instr->value());
+ __ xorps(xmm0, xmm0);
+ __ ucomisd(reg, xmm0);
+ EmitBranch(instr, not_equal);
+ } else {
+ ASSERT(r.IsTagged());
+ Register reg = ToRegister(instr->value());
+ HType type = instr->hydrogen()->value()->type();
+ if (type.IsBoolean()) {
+ ASSERT(!info()->IsStub());
+ __ CompareRoot(reg, Heap::kTrueValueRootIndex);
+ EmitBranch(instr, equal);
+ } else if (type.IsSmi()) {
+ ASSERT(!info()->IsStub());
+ __ SmiCompare(reg, Smi::FromInt(0));
+ EmitBranch(instr, not_equal);
+ } else if (type.IsJSArray()) {
+ ASSERT(!info()->IsStub());
+ EmitBranch(instr, no_condition);
+ } else if (type.IsHeapNumber()) {
+ ASSERT(!info()->IsStub());
+ __ xorps(xmm0, xmm0);
+ __ ucomisd(xmm0, FieldOperand(reg, HeapNumber::kValueOffset));
+ EmitBranch(instr, not_equal);
+ } else if (type.IsString()) {
+ ASSERT(!info()->IsStub());
+ __ cmpq(FieldOperand(reg, String::kLengthOffset), Immediate(0));
+ EmitBranch(instr, not_equal);
+ } else {
+ 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::Types::Generic();
+
+ if (expected.Contains(ToBooleanStub::UNDEFINED)) {
+ // undefined -> false.
+ __ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
+ __ j(equal, instr->FalseLabel(chunk_));
+ }
+ if (expected.Contains(ToBooleanStub::BOOLEAN)) {
+ // true -> true.
+ __ CompareRoot(reg, Heap::kTrueValueRootIndex);
+ __ j(equal, instr->TrueLabel(chunk_));
+ // false -> false.
+ __ CompareRoot(reg, Heap::kFalseValueRootIndex);
+ __ j(equal, instr->FalseLabel(chunk_));
+ }
+ if (expected.Contains(ToBooleanStub::NULL_TYPE)) {
+ // 'null' -> false.
+ __ CompareRoot(reg, Heap::kNullValueRootIndex);
+ __ j(equal, instr->FalseLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::SMI)) {
+ // Smis: 0 -> false, all other -> true.
+ __ Cmp(reg, Smi::FromInt(0));
+ __ j(equal, instr->FalseLabel(chunk_));
+ __ JumpIfSmi(reg, instr->TrueLabel(chunk_));
+ } else if (expected.NeedsMap()) {
+ // If we need a map later and have a Smi -> deopt.
+ __ testb(reg, Immediate(kSmiTagMask));
+ DeoptimizeIf(zero, instr->environment());
+ }
+
+ const Register map = kScratchRegister;
+ if (expected.NeedsMap()) {
+ __ movq(map, FieldOperand(reg, HeapObject::kMapOffset));
+
+ if (expected.CanBeUndetectable()) {
+ // Undetectable -> false.
+ __ testb(FieldOperand(map, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ __ j(not_zero, instr->FalseLabel(chunk_));
+ }
+ }
+
+ if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) {
+ // spec object -> true.
+ __ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
+ __ j(above_equal, instr->TrueLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::STRING)) {
+ // String value -> false iff empty.
+ Label not_string;
+ __ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
+ __ j(above_equal, &not_string, Label::kNear);
+ __ cmpq(FieldOperand(reg, String::kLengthOffset), Immediate(0));
+ __ j(not_zero, instr->TrueLabel(chunk_));
+ __ jmp(instr->FalseLabel(chunk_));
+ __ bind(&not_string);
+ }
+
+ if (expected.Contains(ToBooleanStub::SYMBOL)) {
+ // Symbol value -> true.
+ __ CmpInstanceType(map, SYMBOL_TYPE);
+ __ j(equal, instr->TrueLabel(chunk_));
+ }
+
+ if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
+ // heap number -> false iff +0, -0, or NaN.
+ Label not_heap_number;
+ __ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
+ __ j(not_equal, &not_heap_number, Label::kNear);
+ __ xorps(xmm0, xmm0);
+ __ ucomisd(xmm0, FieldOperand(reg, HeapNumber::kValueOffset));
+ __ j(zero, instr->FalseLabel(chunk_));
+ __ jmp(instr->TrueLabel(chunk_));
+ __ bind(&not_heap_number);
+ }
+
+ if (!expected.IsGeneric()) {
+ // We've seen something for the first time -> deopt.
+ // This can only happen if we are not generic already.
+ DeoptimizeIf(no_condition, instr->environment());
+ }
+ }
+ }
+}
+
+
+void LCodeGen::EmitGoto(int block) {
+ if (!IsNextEmittedBlock(block)) {
+ __ jmp(chunk_->GetAssemblyLabel(chunk_->LookupDestination(block)));
+ }
+}
+
+
+void LCodeGen::DoGoto(LGoto* instr) {
+ EmitGoto(instr->block_id());
+}
+
+
+inline Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) {
+ Condition cond = no_condition;
+ switch (op) {
+ case Token::EQ:
+ case Token::EQ_STRICT:
+ cond = equal;
+ break;
+ case Token::LT:
+ cond = is_unsigned ? below : less;
+ break;
+ case Token::GT:
+ cond = is_unsigned ? above : greater;
+ break;
+ case Token::LTE:
+ cond = is_unsigned ? below_equal : less_equal;
+ break;
+ case Token::GTE:
+ cond = is_unsigned ? above_equal : greater_equal;
+ break;
+ case Token::IN:
+ case Token::INSTANCEOF:
+ default:
+ UNREACHABLE();
+ }
+ return cond;
+}
+
+
+void LCodeGen::DoCompareNumericAndBranch(LCompareNumericAndBranch* instr) {
+ LOperand* left = instr->left();
+ LOperand* right = instr->right();
+ Condition cc = TokenToCondition(instr->op(), instr->is_double());
+
+ 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) ?
+ instr->TrueDestination(chunk_) : instr->FalseDestination(chunk_);
+ EmitGoto(next_block);
+ } else {
+ 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, instr->FalseLabel(chunk_));
+ } else {
+ int32_t value;
+ if (right->IsConstantOperand()) {
+ value = ToInteger32(LConstantOperand::cast(right));
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ __ Cmp(ToRegister(left), Smi::FromInt(value));
+ } else {
+ __ cmpl(ToRegister(left), Immediate(value));
+ }
+ } else if (left->IsConstantOperand()) {
+ value = ToInteger32(LConstantOperand::cast(left));
+ if (instr->hydrogen_value()->representation().IsSmi()) {
+ if (right->IsRegister()) {
+ __ Cmp(ToRegister(right), Smi::FromInt(value));
+ } else {
+ __ Cmp(ToOperand(right), Smi::FromInt(value));
+ }
+ } else 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 (instr->hydrogen_value()->representation().IsSmi()) {
+ if (right->IsRegister()) {
+ __ cmpq(ToRegister(left), ToRegister(right));
+ } else {
+ __ cmpq(ToRegister(left), ToOperand(right));
+ }
+ } else {
+ if (right->IsRegister()) {
+ __ cmpl(ToRegister(left), ToRegister(right));
+ } else {
+ __ cmpl(ToRegister(left), ToOperand(right));
+ }
+ }
+ }
+ EmitBranch(instr, cc);
+ }
+}
+
+
+void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) {
+ Register left = ToRegister(instr->left());
+
+ if (instr->right()->IsConstantOperand()) {
+ Handle<Object> right = ToHandle(LConstantOperand::cast(instr->right()));
+ __ CmpObject(left, right);
+ } else {
+ Register right = ToRegister(instr->right());
+ __ cmpq(left, right);
+ }
+ EmitBranch(instr, equal);
+}
+
+
+void LCodeGen::DoCmpHoleAndBranch(LCmpHoleAndBranch* instr) {
+ if (instr->hydrogen()->representation().IsTagged()) {
+ Register input_reg = ToRegister(instr->object());
+ __ Cmp(input_reg, factory()->the_hole_value());
+ EmitBranch(instr, equal);
+ return;
+ }
+
+ XMMRegister input_reg = ToDoubleRegister(instr->object());
+ __ ucomisd(input_reg, input_reg);
+ EmitFalseBranch(instr, parity_odd);
+
+ __ subq(rsp, Immediate(kDoubleSize));
+ __ movsd(MemOperand(rsp, 0), input_reg);
+ __ addq(rsp, Immediate(kDoubleSize));
+
+ int offset = sizeof(kHoleNanUpper32);
+ __ cmpl(MemOperand(rsp, -offset), Immediate(kHoleNanUpper32));
+ EmitBranch(instr, equal);
+}
+
+
+Condition LCodeGen::EmitIsObject(Register input,
+ Label* is_not_object,
+ Label* is_object) {
+ ASSERT(!input.is(kScratchRegister));
+
+ __ JumpIfSmi(input, is_not_object);
+
+ __ CompareRoot(input, Heap::kNullValueRootIndex);
+ __ j(equal, is_object);
+
+ __ movq(kScratchRegister, FieldOperand(input, HeapObject::kMapOffset));
+ // Undetectable objects behave like undefined.
+ __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ __ j(not_zero, is_not_object);
+
+ __ movzxbl(kScratchRegister,
+ FieldOperand(kScratchRegister, Map::kInstanceTypeOffset));
+ __ cmpb(kScratchRegister, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ __ j(below, is_not_object);
+ __ cmpb(kScratchRegister, Immediate(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ return below_equal;
+}
+
+
+void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+
+ Condition true_cond = EmitIsObject(
+ reg, instr->FalseLabel(chunk_), instr->TrueLabel(chunk_));
+
+ EmitBranch(instr, true_cond);
+}
+
+
+Condition LCodeGen::EmitIsString(Register input,
+ Register temp1,
+ Label* is_not_string,
+ SmiCheck check_needed = INLINE_SMI_CHECK) {
+ if (check_needed == INLINE_SMI_CHECK) {
+ __ JumpIfSmi(input, is_not_string);
+ }
+
+ Condition cond = masm_->IsObjectStringType(input, temp1, temp1);
+
+ return cond;
+}
+
+
+void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+
+ Condition true_cond = EmitIsString(
+ reg, temp, instr->FalseLabel(chunk_), check_needed);
+
+ EmitBranch(instr, true_cond);
+}
+
+
+void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
+ Condition is_smi;
+ if (instr->value()->IsRegister()) {
+ Register input = ToRegister(instr->value());
+ is_smi = masm()->CheckSmi(input);
+ } else {
+ Operand input = ToOperand(instr->value());
+ is_smi = masm()->CheckSmi(input);
+ }
+ EmitBranch(instr, is_smi);
+}
+
+
+void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ __ JumpIfSmi(input, instr->FalseLabel(chunk_));
+ }
+ __ movq(temp, FieldOperand(input, HeapObject::kMapOffset));
+ __ testb(FieldOperand(temp, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ EmitBranch(instr, not_zero);
+}
+
+
+void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
+ Token::Value op = instr->op();
+
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+
+ Condition condition = TokenToCondition(op, false);
+ __ testq(rax, rax);
+
+ EmitBranch(instr, condition);
+}
+
+
+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 equal;
+ if (to == LAST_TYPE) return above_equal;
+ if (from == FIRST_TYPE) return below_equal;
+ UNREACHABLE();
+ return equal;
+}
+
+
+void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ __ JumpIfSmi(input, instr->FalseLabel(chunk_));
+ }
+
+ __ CmpObjectType(input, TestType(instr->hydrogen()), kScratchRegister);
+ EmitBranch(instr, BranchCondition(instr->hydrogen()));
+}
+
+
+void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) {
+ Register input = ToRegister(instr->value());
+ Register result = ToRegister(instr->result());
+
+ __ AssertString(input);
+
+ __ movl(result, FieldOperand(input, String::kHashFieldOffset));
+ ASSERT(String::kHashShift >= kSmiTagSize);
+ __ IndexFromHash(result, result);
+}
+
+
+void LCodeGen::DoHasCachedArrayIndexAndBranch(
+ LHasCachedArrayIndexAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+
+ __ testl(FieldOperand(input, String::kHashFieldOffset),
+ Immediate(String::kContainsCachedArrayIndexMask));
+ EmitBranch(instr, equal);
+}
+
+
+// Branches to a label or falls through with the answer in the z flag.
+// Trashes the temp register.
+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->IsOneByteEqualTo(STATIC_ASCII_VECTOR("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);
+ __ 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 {
+ // 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));
+ __ movzxbl(temp2, FieldOperand(temp, Map::kInstanceTypeOffset));
+ __ subq(temp2, Immediate(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
+ __ cmpq(temp2, Immediate(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));
+
+ // Objects with a non-function constructor have class 'Object'.
+ __ CmpObjectType(temp, JS_FUNCTION_TYPE, kScratchRegister);
+ if (class_name->IsOneByteEqualTo(STATIC_ASCII_VECTOR("Object"))) {
+ __ j(not_equal, is_true);
+ } else {
+ __ j(not_equal, is_false);
+ }
+
+ // temp now contains the constructor function. Grab the
+ // instance class name from there.
+ __ movq(temp, FieldOperand(temp, JSFunction::kSharedFunctionInfoOffset));
+ __ movq(temp, FieldOperand(temp,
+ SharedFunctionInfo::kInstanceClassNameOffset));
+ // The class name we are testing against is internalized since it's a literal.
+ // The name in the constructor is internalized 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 internalized it is sufficient to use an
+ // identity comparison.
+ ASSERT(class_name->IsInternalizedString());
+ __ Cmp(temp, class_name);
+ // End with the answer in the z flag.
+}
+
+
+void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+ Register temp = ToRegister(instr->temp());
+ Register temp2 = ToRegister(instr->temp2());
+ Handle<String> class_name = instr->hydrogen()->class_name();
+
+ EmitClassOfTest(instr->TrueLabel(chunk_), instr->FalseLabel(chunk_),
+ class_name, input, temp, temp2);
+
+ EmitBranch(instr, equal);
+}
+
+
+void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
+ Register reg = ToRegister(instr->value());
+
+ __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), instr->map());
+ EmitBranch(instr, equal);
+}
+
+
+void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
+ InstanceofStub stub(InstanceofStub::kNoFlags);
+ __ push(ToRegister(instr->left()));
+ __ push(ToRegister(instr->right()));
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ Label true_value, done;
+ __ testq(rax, rax);
+ __ j(zero, &true_value, Label::kNear);
+ __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
+ __ jmp(&done, Label::kNear);
+ __ bind(&true_value);
+ __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
+ __ 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(zone()) DeferredInstanceOfKnownGlobal(this, instr);
+
+ Label done, false_result;
+ Register object = ToRegister(instr->value());
+
+ // A Smi is not an 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;
+ // Use a temp register to avoid memory operands with variable lengths.
+ Register map = ToRegister(instr->temp());
+ __ movq(map, FieldOperand(object, HeapObject::kMapOffset));
+ __ bind(deferred->map_check()); // Label for calculating code patching.
+ Handle<Cell> cache_cell = factory()->NewCell(factory()->the_hole_value());
+ __ movq(kScratchRegister, cache_cell, RelocInfo::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);
+#ifdef DEBUG
+ // Check that the code size between patch label and patch sites is invariant.
+ Label end_of_patched_code;
+ __ bind(&end_of_patched_code);
+ ASSERT(true);
+#endif
+ __ jmp(&done);
+
+ // The inlined call site cache did not match. Check for null and string
+ // before calling the deferred code.
+ __ bind(&cache_miss); // Null is not an instance of anything.
+ __ CompareRoot(object, Heap::kNullValueRootIndex);
+ __ j(equal, &false_result, Label::kNear);
+
+ // String values are not instances of anything.
+ __ JumpIfNotString(object, kScratchRegister, deferred->entry());
+
+ __ bind(&false_result);
+ __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
+
+ __ bind(deferred->exit());
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check) {
+ {
+ PushSafepointRegistersScope scope(this);
+ InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
+ InstanceofStub::kNoFlags | InstanceofStub::kCallSiteInlineCheck);
+ InstanceofStub stub(flags);
+
+ __ push(ToRegister(instr->value()));
+ __ PushHeapObject(instr->function());
+
+ static const int kAdditionalDelta = 10;
+ int delta =
+ masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
+ ASSERT(delta >= 0);
+ __ push_imm32(delta);
+
+ // We are pushing three values on the stack but recording a
+ // safepoint with two arguments because stub is going to
+ // remove the third argument from the stack before jumping
+ // to instanceof builtin on the slow path.
+ CallCodeGeneric(stub.GetCode(isolate()),
+ RelocInfo::CODE_TARGET,
+ instr,
+ RECORD_SAFEPOINT_WITH_REGISTERS,
+ 2);
+ ASSERT(delta == masm_->SizeOfCodeGeneratedSince(map_check));
+ LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
+ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
+ // Move result to a register that survives the end of the
+ // PushSafepointRegisterScope.
+ __ movq(kScratchRegister, rax);
+ }
+ __ testq(kScratchRegister, kScratchRegister);
+ Label load_false;
+ Label done;
+ __ j(not_zero, &load_false);
+ __ LoadRoot(rax, Heap::kTrueValueRootIndex);
+ __ jmp(&done);
+ __ bind(&load_false);
+ __ LoadRoot(rax, Heap::kFalseValueRootIndex);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoInstanceSize(LInstanceSize* instr) {
+ Register object = ToRegister(instr->object());
+ Register result = ToRegister(instr->result());
+ __ movq(result, FieldOperand(object, HeapObject::kMapOffset));
+ __ movzxbq(result, FieldOperand(result, Map::kInstanceSizeOffset));
+}
+
+
+void LCodeGen::DoCmpT(LCmpT* instr) {
+ Token::Value op = instr->op();
+
+ Handle<Code> ic = CompareIC::GetUninitialized(isolate(), op);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+
+ Condition condition = TokenToCondition(op, false);
+ Label true_value, done;
+ __ testq(rax, rax);
+ __ j(condition, &true_value, Label::kNear);
+ __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
+ __ jmp(&done, Label::kNear);
+ __ bind(&true_value);
+ __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoReturn(LReturn* instr) {
+ if (FLAG_trace && info()->IsOptimizing()) {
+ // Preserve the return value on the stack and rely on the runtime
+ // call to return the value in the same register.
+ __ push(rax);
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+ if (info()->saves_caller_doubles()) {
+ ASSERT(NeedsEagerFrame());
+ BitVector* doubles = chunk()->allocated_double_registers();
+ BitVector::Iterator save_iterator(doubles);
+ int count = 0;
+ while (!save_iterator.Done()) {
+ __ movsd(XMMRegister::FromAllocationIndex(save_iterator.Current()),
+ MemOperand(rsp, count * kDoubleSize));
+ save_iterator.Advance();
+ count++;
+ }
+ }
+ int no_frame_start = -1;
+ if (NeedsEagerFrame()) {
+ __ movq(rsp, rbp);
+ __ pop(rbp);
+ no_frame_start = masm_->pc_offset();
+ }
+ if (instr->has_constant_parameter_count()) {
+ __ Ret((ToInteger32(instr->constant_parameter_count()) + 1) * kPointerSize,
+ rcx);
+ } else {
+ Register reg = ToRegister(instr->parameter_count());
+ // The argument count parameter is a smi
+ __ SmiToInteger32(reg, reg);
+ Register return_addr_reg = reg.is(rcx) ? rbx : rcx;
+ __ PopReturnAddressTo(return_addr_reg);
+ __ shl(reg, Immediate(kPointerSizeLog2));
+ __ addq(rsp, reg);
+ __ jmp(return_addr_reg);
+ }
+ if (no_frame_start != -1) {
+ info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
+ }
+}
+
+
+void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
+ Register result = ToRegister(instr->result());
+ __ LoadGlobalCell(result, instr->hydrogen()->cell());
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(equal, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->global_object()).is(rax));
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ __ Move(rcx, 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());
+ Handle<Cell> 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()->RequiresHoleCheck()) {
+ // We have a temp because CompareRoot might clobber kScratchRegister.
+ Register cell = ToRegister(instr->temp());
+ ASSERT(!value.is(cell));
+ __ movq(cell, cell_handle, RelocInfo::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::CELL);
+ __ movq(Operand(kScratchRegister, 0), value);
+ }
+ // Cells are always rescanned, so no write barrier here.
+}
+
+
+void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
+ ASSERT(ToRegister(instr->global_object()).is(rdx));
+ ASSERT(ToRegister(instr->value()).is(rax));
+
+ __ Move(rcx, 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());
+ __ 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());
+
+ 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()) {
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ int offset = Context::SlotOffset(instr->slot_index());
+ Register scratch = ToRegister(instr->temp());
+ __ RecordWriteContextSlot(context,
+ offset,
+ value,
+ scratch,
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+
+ __ bind(&skip_assignment);
+}
+
+
+void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
+ HObjectAccess access = instr->hydrogen()->access();
+ int offset = access.offset();
+
+ if (access.IsExternalMemory()) {
+ Register result = ToRegister(instr->result());
+ if (instr->object()->IsConstantOperand()) {
+ ASSERT(result.is(rax));
+ __ load_rax(ToExternalReference(LConstantOperand::cast(instr->object())));
+ } else {
+ Register object = ToRegister(instr->object());
+ __ movq(result, MemOperand(object, offset));
+ }
+ return;
+ }
+
+ Register object = ToRegister(instr->object());
+ if (FLAG_track_double_fields &&
+ instr->hydrogen()->representation().IsDouble()) {
+ XMMRegister result = ToDoubleRegister(instr->result());
+ __ movsd(result, FieldOperand(object, offset));
+ return;
+ }
+
+ Register result = ToRegister(instr->result());
+ if (access.IsInobject()) {
+ __ movq(result, FieldOperand(object, offset));
+ } else {
+ __ movq(result, FieldOperand(object, JSObject::kPropertiesOffset));
+ __ movq(result, FieldOperand(result, offset));
+ }
+}
+
+
+void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
+ ASSERT(ToRegister(instr->object()).is(rax));
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ __ Move(rcx, instr->name());
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
+ Register function = ToRegister(instr->function());
+ Register result = ToRegister(instr->result());
+
+ // Check that the function really is a function.
+ __ CmpObjectType(function, JS_FUNCTION_TYPE, result);
+ DeoptimizeIf(not_equal, instr->environment());
+
+ // Check whether the function has an instance prototype.
+ Label non_instance;
+ __ testb(FieldOperand(result, Map::kBitFieldOffset),
+ Immediate(1 << Map::kHasNonInstancePrototype));
+ __ j(not_zero, &non_instance, Label::kNear);
+
+ // Get the prototype or initial map from the function.
+ __ movq(result,
+ FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Check that the function has a prototype or an initial map.
+ __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(equal, instr->environment());
+
+ // If the function does not have an initial map, we're done.
+ Label done;
+ __ CmpObjectType(result, MAP_TYPE, kScratchRegister);
+ __ j(not_equal, &done, Label::kNear);
+
+ // Get the prototype from the initial map.
+ __ movq(result, FieldOperand(result, Map::kPrototypeOffset));
+ __ jmp(&done, Label::kNear);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in the function's map.
+ __ bind(&non_instance);
+ __ movq(result, FieldOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoLoadExternalArrayPointer(
+ LLoadExternalArrayPointer* instr) {
+ Register result = ToRegister(instr->result());
+ Register input = ToRegister(instr->object());
+ __ movq(result, FieldOperand(input,
+ ExternalPixelArray::kExternalPointerOffset));
+}
+
+
+void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
+ Register arguments = ToRegister(instr->arguments());
+ Register result = ToRegister(instr->result());
+
+ if (instr->length()->IsConstantOperand() &&
+ instr->index()->IsConstantOperand()) {
+ int32_t const_index = ToInteger32(LConstantOperand::cast(instr->index()));
+ int32_t const_length = ToInteger32(LConstantOperand::cast(instr->length()));
+ int index = (const_length - const_index) + 1;
+ __ movq(result, Operand(arguments, index * kPointerSize));
+ } else {
+ Register length = ToRegister(instr->length());
+ // There are two words between the frame pointer and the last argument.
+ // Subtracting from length accounts for one of them add one more.
+ if (instr->index()->IsRegister()) {
+ __ subl(length, ToRegister(instr->index()));
+ } else {
+ __ subl(length, ToOperand(instr->index()));
+ }
+ __ movq(result,
+ Operand(arguments, length, times_pointer_size, kPointerSize));
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) {
+ ElementsKind elements_kind = instr->elements_kind();
+ LOperand* key = instr->key();
+ if (!key->IsConstantOperand()) {
+ Register key_reg = ToRegister(key);
+ // Even though the HLoad/StoreKeyed (in this case) instructions force
+ // the input representation for the key to be an integer, the input
+ // gets replaced during bound check elimination with the index argument
+ // to the bounds check, which can be tagged, so that case must be
+ // handled here, too.
+ if (instr->hydrogen()->IsDehoisted()) {
+ // Sign extend key because it could be a 32 bit negative value
+ // and the dehoisted address computation happens in 64 bits
+ __ movsxlq(key_reg, key_reg);
+ }
+ }
+ Operand operand(BuildFastArrayOperand(
+ instr->elements(),
+ key,
+ elements_kind,
+ 0,
+ instr->additional_index()));
+
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ XMMRegister result(ToDoubleRegister(instr->result()));
+ __ movss(result, operand);
+ __ cvtss2sd(result, result);
+ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ __ movsd(ToDoubleRegister(instr->result()), operand);
+ } else {
+ Register result(ToRegister(instr->result()));
+ switch (elements_kind) {
+ case EXTERNAL_BYTE_ELEMENTS:
+ __ movsxbq(result, operand);
+ break;
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ case EXTERNAL_PIXEL_ELEMENTS:
+ __ movzxbq(result, operand);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ __ movsxwq(result, operand);
+ break;
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ movzxwq(result, operand);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ __ movsxlq(result, operand);
+ break;
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ movl(result, operand);
+ if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) {
+ __ testl(result, result);
+ DeoptimizeIf(negative, instr->environment());
+ }
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr) {
+ XMMRegister result(ToDoubleRegister(instr->result()));
+ LOperand* key = instr->key();
+ if (!key->IsConstantOperand()) {
+ Register key_reg = ToRegister(key);
+ // Even though the HLoad/StoreKeyed instructions force the input
+ // representation for the key to be an integer, the input gets replaced
+ // during bound check elimination with the index argument to the bounds
+ // check, which can be tagged, so that case must be handled here, too.
+ if (instr->hydrogen()->IsDehoisted()) {
+ // Sign extend key because it could be a 32 bit negative value
+ // and the dehoisted address computation happens in 64 bits
+ __ movsxlq(key_reg, key_reg);
+ }
+ }
+
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag +
+ sizeof(kHoleNanLower32);
+ Operand hole_check_operand = BuildFastArrayOperand(
+ instr->elements(),
+ key,
+ FAST_DOUBLE_ELEMENTS,
+ offset,
+ instr->additional_index());
+ __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32));
+ DeoptimizeIf(equal, instr->environment());
+ }
+
+ Operand double_load_operand = BuildFastArrayOperand(
+ instr->elements(),
+ key,
+ FAST_DOUBLE_ELEMENTS,
+ FixedDoubleArray::kHeaderSize - kHeapObjectTag,
+ instr->additional_index());
+ __ movsd(result, double_load_operand);
+}
+
+
+void LCodeGen::DoLoadKeyedFixedArray(LLoadKeyed* instr) {
+ Register result = ToRegister(instr->result());
+ LOperand* key = instr->key();
+ if (!key->IsConstantOperand()) {
+ Register key_reg = ToRegister(key);
+ // Even though the HLoad/StoreKeyedFastElement instructions force
+ // the input representation for the key to be an integer, the input
+ // gets replaced during bound check elimination with the index
+ // argument to the bounds check, which can be tagged, so that
+ // case must be handled here, too.
+ if (instr->hydrogen()->IsDehoisted()) {
+ // Sign extend key because it could be a 32 bit negative value
+ // and the dehoisted address computation happens in 64 bits
+ __ movsxlq(key_reg, key_reg);
+ }
+ }
+
+ // Load the result.
+ __ movq(result,
+ BuildFastArrayOperand(instr->elements(),
+ key,
+ FAST_ELEMENTS,
+ FixedArray::kHeaderSize - kHeapObjectTag,
+ instr->additional_index()));
+
+ // Check for the hole value.
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ if (IsFastSmiElementsKind(instr->hydrogen()->elements_kind())) {
+ Condition smi = __ CheckSmi(result);
+ DeoptimizeIf(NegateCondition(smi), instr->environment());
+ } else {
+ __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(equal, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoLoadKeyed(LLoadKeyed* instr) {
+ if (instr->is_external()) {
+ DoLoadKeyedExternalArray(instr);
+ } else if (instr->hydrogen()->representation().IsDouble()) {
+ DoLoadKeyedFixedDoubleArray(instr);
+ } else {
+ DoLoadKeyedFixedArray(instr);
+ }
+}
+
+
+Operand LCodeGen::BuildFastArrayOperand(
+ LOperand* elements_pointer,
+ LOperand* key,
+ ElementsKind elements_kind,
+ uint32_t offset,
+ uint32_t additional_index) {
+ Register elements_pointer_reg = ToRegister(elements_pointer);
+ int shift_size = ElementsKindToShiftSize(elements_kind);
+ if (key->IsConstantOperand()) {
+ int32_t constant_value = ToInteger32(LConstantOperand::cast(key));
+ if (constant_value & 0xF0000000) {
+ Abort(kArrayIndexConstantValueTooBig);
+ }
+ return Operand(elements_pointer_reg,
+ ((constant_value + additional_index) << shift_size)
+ + offset);
+ } else {
+ ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size);
+ return Operand(elements_pointer_reg,
+ ToRegister(key),
+ scale_factor,
+ offset + (additional_index << shift_size));
+ }
+}
+
+
+void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) {
+ ASSERT(ToRegister(instr->object()).is(rdx));
+ ASSERT(ToRegister(instr->key()).is(rax));
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
+ Register result = ToRegister(instr->result());
+
+ if (instr->hydrogen()->from_inlined()) {
+ __ lea(result, Operand(rsp, -2 * kPointerSize));
+ } else {
+ // Check for arguments adapter frame.
+ Label done, adapted;
+ __ movq(result, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ Cmp(Operand(result, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(equal, &adapted, Label::kNear);
+
+ // No arguments adaptor frame.
+ __ movq(result, rbp);
+ __ jmp(&done, Label::kNear);
+
+ // Arguments adaptor frame present.
+ __ bind(&adapted);
+ __ movq(result, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+
+ // Result is the frame pointer for the frame if not adapted and for the real
+ // frame below the adaptor frame if adapted.
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
+ Register result = ToRegister(instr->result());
+
+ Label done;
+
+ // If no arguments adaptor frame the number of arguments is fixed.
+ if (instr->elements()->IsRegister()) {
+ __ cmpq(rbp, ToRegister(instr->elements()));
+ } else {
+ __ cmpq(rbp, ToOperand(instr->elements()));
+ }
+ __ movl(result, Immediate(scope()->num_parameters()));
+ __ j(equal, &done, Label::kNear);
+
+ // Arguments adaptor frame present. Get argument length from there.
+ __ movq(result, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ SmiToInteger32(result,
+ Operand(result,
+ ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ // Argument length is in result register.
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) {
+ Register receiver = ToRegister(instr->receiver());
+ Register function = ToRegister(instr->function());
+
+ // 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.
+ __ movq(kScratchRegister,
+ FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
+ __ testb(FieldOperand(kScratchRegister,
+ SharedFunctionInfo::kStrictModeByteOffset),
+ Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte));
+ __ j(not_equal, &receiver_ok, Label::kNear);
+
+ // Do not transform the receiver to object for builtins.
+ __ testb(FieldOperand(kScratchRegister,
+ SharedFunctionInfo::kNativeByteOffset),
+ Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte));
+ __ j(not_equal, &receiver_ok, Label::kNear);
+
+ // Normal function. Replace undefined or null with global receiver.
+ __ CompareRoot(receiver, Heap::kNullValueRootIndex);
+ __ j(equal, &global_object, Label::kNear);
+ __ CompareRoot(receiver, Heap::kUndefinedValueRootIndex);
+ __ j(equal, &global_object, Label::kNear);
+
+ // The receiver should be a JS object.
+ Condition is_smi = __ CheckSmi(receiver);
+ DeoptimizeIf(is_smi, instr->environment());
+ __ CmpObjectType(receiver, FIRST_SPEC_OBJECT_TYPE, kScratchRegister);
+ DeoptimizeIf(below, instr->environment());
+ __ jmp(&receiver_ok, Label::kNear);
+
+ __ bind(&global_object);
+ // TODO(kmillikin): We have a hydrogen value for the global object. See
+ // if it's better to use it than to explicitly fetch it from the context
+ // here.
+ __ movq(receiver, ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX));
+ __ movq(receiver,
+ FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
+ __ bind(&receiver_ok);
+}
+
+
+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());
+ ASSERT(receiver.is(rax)); // Used for parameter count.
+ ASSERT(function.is(rdi)); // Required by InvokeFunction.
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ // Copy the arguments to this function possibly from the
+ // adaptor frame below it.
+ const uint32_t kArgumentsLimit = 1 * KB;
+ __ cmpq(length, Immediate(kArgumentsLimit));
+ DeoptimizeIf(above, instr->environment());
+
+ __ push(receiver);
+ __ movq(receiver, length);
+
+ // 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.
+ __ testl(length, length);
+ __ j(zero, &invoke, Label::kNear);
+ __ bind(&loop);
+ __ push(Operand(elements, length, times_pointer_size, 1 * kPointerSize));
+ __ decl(length);
+ __ j(not_zero, &loop);
+
+ // Invoke the function.
+ __ bind(&invoke);
+ ASSERT(instr->HasPointerMap());
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+ SafepointGenerator safepoint_generator(
+ this, pointers, Safepoint::kLazyDeopt);
+ ParameterCount actual(rax);
+ __ InvokeFunction(function, actual, CALL_FUNCTION,
+ safepoint_generator, CALL_AS_METHOD);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoPushArgument(LPushArgument* instr) {
+ LOperand* argument = instr->value();
+ EmitPushTaggedOperand(argument);
+}
+
+
+void LCodeGen::DoDrop(LDrop* instr) {
+ __ Drop(instr->count());
+}
+
+
+void LCodeGen::DoThisFunction(LThisFunction* instr) {
+ Register result = ToRegister(instr->result());
+ __ movq(result, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+}
+
+
+void LCodeGen::DoContext(LContext* instr) {
+ Register result = ToRegister(instr->result());
+ __ movq(result, rsi);
+}
+
+
+void LCodeGen::DoOuterContext(LOuterContext* instr) {
+ Register context = ToRegister(instr->context());
+ Register result = ToRegister(instr->result());
+ __ movq(result,
+ Operand(context, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+}
+
+
+void LCodeGen::DoDeclareGlobals(LDeclareGlobals* instr) {
+ __ push(rsi); // The context is the first argument.
+ __ PushHeapObject(instr->hydrogen()->pairs());
+ __ Push(Smi::FromInt(instr->hydrogen()->flags()));
+ CallRuntime(Runtime::kDeclareGlobals, 3, instr);
+}
+
+
+void LCodeGen::DoGlobalObject(LGlobalObject* instr) {
+ Register result = ToRegister(instr->result());
+ __ movq(result, GlobalObjectOperand());
+}
+
+
+void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
+ Register global = ToRegister(instr->global());
+ Register result = ToRegister(instr->result());
+ __ movq(result, FieldOperand(global, GlobalObject::kGlobalReceiverOffset));
+}
+
+
+void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
+ int formal_parameter_count,
+ int arity,
+ LInstruction* instr,
+ CallKind call_kind,
+ RDIState rdi_state) {
+ bool dont_adapt_arguments =
+ formal_parameter_count == SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+ bool can_invoke_directly =
+ dont_adapt_arguments || formal_parameter_count == arity;
+
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+
+ if (can_invoke_directly) {
+ if (rdi_state == RDI_UNINITIALIZED) {
+ __ LoadHeapObject(rdi, function);
+ }
+
+ // 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 (dont_adapt_arguments) {
+ __ Set(rax, arity);
+ }
+
+ // Invoke function.
+ __ SetCallKind(rcx, call_kind);
+ if (function.is_identical_to(info()->closure())) {
+ __ CallSelf();
+ } else {
+ __ call(FieldOperand(rdi, JSFunction::kCodeEntryOffset));
+ }
+
+ // Set up deoptimization.
+ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0);
+ } else {
+ // We need to adapt arguments.
+ SafepointGenerator generator(
+ this, pointers, Safepoint::kLazyDeopt);
+ ParameterCount count(arity);
+ ParameterCount expected(formal_parameter_count);
+ __ InvokeFunction(
+ function, expected, count, CALL_FUNCTION, generator, call_kind);
+ }
+
+ // Restore context.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
+ ASSERT(ToRegister(instr->result()).is(rax));
+ CallKnownFunction(instr->hydrogen()->function(),
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD,
+ RDI_UNINITIALIZED);
+}
+
+
+void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr) {
+ Register input_reg = ToRegister(instr->value());
+ __ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+ DeoptimizeIf(not_equal, instr->environment());
+
+ Label slow, allocated, done;
+ Register tmp = input_reg.is(rax) ? rcx : rax;
+ Register tmp2 = tmp.is(rcx) ? rdx : input_reg.is(rcx) ? rdx : rcx;
+
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this);
+
+ __ movl(tmp, FieldOperand(input_reg, HeapNumber::kExponentOffset));
+ // Check the sign of the argument. If the argument is positive, just
+ // return it. We do not need to patch the stack since |input| and
+ // |result| are the same register and |input| will be restored
+ // unchanged by popping safepoint registers.
+ __ testl(tmp, Immediate(HeapNumber::kSignMask));
+ __ j(zero, &done);
+
+ __ AllocateHeapNumber(tmp, tmp2, &slow);
+ __ jmp(&allocated, Label::kNear);
+
+ // 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 (!tmp.is(rax)) __ movq(tmp, rax);
+ // Restore input_reg after call to runtime.
+ __ LoadFromSafepointRegisterSlot(input_reg, input_reg);
+
+ __ bind(&allocated);
+ __ movq(tmp2, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ shl(tmp2, Immediate(1));
+ __ shr(tmp2, Immediate(1));
+ __ movq(FieldOperand(tmp, HeapNumber::kValueOffset), tmp2);
+ __ StoreToSafepointRegisterSlot(input_reg, tmp);
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::EmitIntegerMathAbs(LMathAbs* instr) {
+ Register input_reg = ToRegister(instr->value());
+ __ testl(input_reg, input_reg);
+ Label is_positive;
+ __ j(not_sign, &is_positive, Label::kNear);
+ __ negl(input_reg); // Sets flags.
+ DeoptimizeIf(negative, instr->environment());
+ __ bind(&is_positive);
+}
+
+
+void LCodeGen::EmitSmiMathAbs(LMathAbs* instr) {
+ Register input_reg = ToRegister(instr->value());
+ __ testq(input_reg, input_reg);
+ Label is_positive;
+ __ j(not_sign, &is_positive, Label::kNear);
+ __ neg(input_reg); // Sets flags.
+ DeoptimizeIf(negative, instr->environment());
+ __ bind(&is_positive);
+}
+
+
+void LCodeGen::DoMathAbs(LMathAbs* instr) {
+ // Class for deferred case.
+ class DeferredMathAbsTaggedHeapNumber: public LDeferredCode {
+ public:
+ DeferredMathAbsTaggedHeapNumber(LCodeGen* codegen, LMathAbs* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LMathAbs* instr_;
+ };
+
+ ASSERT(instr->value()->Equals(instr->result()));
+ Representation r = instr->hydrogen()->value()->representation();
+
+ if (r.IsDouble()) {
+ XMMRegister scratch = xmm0;
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ __ xorps(scratch, scratch);
+ __ subsd(scratch, input_reg);
+ __ andpd(input_reg, scratch);
+ } else if (r.IsInteger32()) {
+ EmitIntegerMathAbs(instr);
+ } else if (r.IsSmi()) {
+ EmitSmiMathAbs(instr);
+ } else { // Tagged case.
+ DeferredMathAbsTaggedHeapNumber* deferred =
+ new(zone()) DeferredMathAbsTaggedHeapNumber(this, instr);
+ Register input_reg = ToRegister(instr->value());
+ // Smi check.
+ __ JumpIfNotSmi(input_reg, deferred->entry());
+ EmitSmiMathAbs(instr);
+ __ bind(deferred->exit());
+ }
+}
+
+
+void LCodeGen::DoMathFloor(LMathFloor* instr) {
+ XMMRegister xmm_scratch = xmm0;
+ Register output_reg = ToRegister(instr->result());
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+
+ if (CpuFeatures::IsSupported(SSE4_1)) {
+ CpuFeatureScope scope(masm(), SSE4_1);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Deoptimize if minus zero.
+ __ movq(output_reg, input_reg);
+ __ subq(output_reg, Immediate(1));
+ DeoptimizeIf(overflow, instr->environment());
+ }
+ __ roundsd(xmm_scratch, input_reg, Assembler::kRoundDown);
+ __ cvttsd2si(output_reg, xmm_scratch);
+ __ cmpl(output_reg, Immediate(0x80000000));
+ DeoptimizeIf(equal, instr->environment());
+ } else {
+ Label negative_sign, done;
+ // Deoptimize on unordered.
+ __ xorps(xmm_scratch, xmm_scratch); // Zero the register.
+ __ ucomisd(input_reg, xmm_scratch);
+ DeoptimizeIf(parity_even, instr->environment());
+ __ j(below, &negative_sign, Label::kNear);
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Check for negative zero.
+ Label positive_sign;
+ __ j(above, &positive_sign, Label::kNear);
+ __ movmskpd(output_reg, input_reg);
+ __ testq(output_reg, Immediate(1));
+ DeoptimizeIf(not_zero, instr->environment());
+ __ Set(output_reg, 0);
+ __ jmp(&done);
+ __ bind(&positive_sign);
+ }
+
+ // Use truncating instruction (OK because input is positive).
+ __ cvttsd2si(output_reg, input_reg);
+ // Overflow is signalled with minint.
+ __ cmpl(output_reg, Immediate(0x80000000));
+ DeoptimizeIf(equal, instr->environment());
+ __ jmp(&done, Label::kNear);
+
+ // Non-zero negative reaches here.
+ __ bind(&negative_sign);
+ // Truncate, then compare and compensate.
+ __ cvttsd2si(output_reg, input_reg);
+ __ cvtlsi2sd(xmm_scratch, output_reg);
+ __ ucomisd(input_reg, xmm_scratch);
+ __ j(equal, &done, Label::kNear);
+ __ subl(output_reg, Immediate(1));
+ DeoptimizeIf(overflow, instr->environment());
+
+ __ bind(&done);
+ }
+}
+
+
+void LCodeGen::DoMathRound(LMathRound* instr) {
+ const XMMRegister xmm_scratch = xmm0;
+ Register output_reg = ToRegister(instr->result());
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ static int64_t one_half = V8_INT64_C(0x3FE0000000000000); // 0.5
+ static int64_t minus_one_half = V8_INT64_C(0xBFE0000000000000); // -0.5
+
+ Label done, round_to_zero, below_one_half, do_not_compensate, restore;
+ __ movq(kScratchRegister, one_half, RelocInfo::NONE64);
+ __ movq(xmm_scratch, kScratchRegister);
+ __ ucomisd(xmm_scratch, input_reg);
+ __ j(above, &below_one_half);
+
+ // CVTTSD2SI rounds towards zero, since 0.5 <= x, we use floor(0.5 + x).
+ __ addsd(xmm_scratch, input_reg);
+ __ cvttsd2si(output_reg, xmm_scratch);
+ // Overflow is signalled with minint.
+ __ cmpl(output_reg, Immediate(0x80000000));
+ __ RecordComment("D2I conversion overflow");
+ DeoptimizeIf(equal, instr->environment());
+ __ jmp(&done);
+
+ __ bind(&below_one_half);
+ __ movq(kScratchRegister, minus_one_half, RelocInfo::NONE64);
+ __ movq(xmm_scratch, kScratchRegister);
+ __ ucomisd(xmm_scratch, input_reg);
+ __ j(below_equal, &round_to_zero);
+
+ // CVTTSD2SI rounds towards zero, we use ceil(x - (-0.5)) and then
+ // compare and compensate.
+ __ movq(kScratchRegister, input_reg); // Back up input_reg.
+ __ subsd(input_reg, xmm_scratch);
+ __ cvttsd2si(output_reg, input_reg);
+ // Catch minint due to overflow, and to prevent overflow when compensating.
+ __ cmpl(output_reg, Immediate(0x80000000));
+ __ RecordComment("D2I conversion overflow");
+ DeoptimizeIf(equal, instr->environment());
+
+ __ cvtlsi2sd(xmm_scratch, output_reg);
+ __ ucomisd(input_reg, xmm_scratch);
+ __ j(equal, &restore, Label::kNear);
+ __ subl(output_reg, Immediate(1));
+ // No overflow because we already ruled out minint.
+ __ bind(&restore);
+ __ movq(input_reg, kScratchRegister); // Restore input_reg.
+ __ jmp(&done);
+
+ __ bind(&round_to_zero);
+ // We return 0 for the input range [+0, 0.5[, or [-0.5, 0.5[ if
+ // we can ignore the difference between a result of -0 and +0.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ movq(output_reg, input_reg);
+ __ testq(output_reg, output_reg);
+ __ RecordComment("Minus zero");
+ DeoptimizeIf(negative, instr->environment());
+ }
+ __ Set(output_reg, 0);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoMathSqrt(LMathSqrt* instr) {
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ ASSERT(ToDoubleRegister(instr->result()).is(input_reg));
+ __ sqrtsd(input_reg, input_reg);
+}
+
+
+void LCodeGen::DoMathPowHalf(LMathPowHalf* instr) {
+ XMMRegister xmm_scratch = xmm0;
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ 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::NONE64);
+ __ 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) {
+ 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.
+
+ Register exponent = rdx;
+ ASSERT(!instr->right()->IsRegister() ||
+ ToRegister(instr->right()).is(exponent));
+ ASSERT(!instr->right()->IsDoubleRegister() ||
+ ToDoubleRegister(instr->right()).is(xmm1));
+ ASSERT(ToDoubleRegister(instr->left()).is(xmm2));
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm3));
+
+ if (exponent_type.IsSmi()) {
+ MathPowStub stub(MathPowStub::TAGGED);
+ __ CallStub(&stub);
+ } else 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.IsDouble());
+ MathPowStub stub(MathPowStub::DOUBLE);
+ __ CallStub(&stub);
+ }
+}
+
+
+void LCodeGen::DoRandom(LRandom* instr) {
+ class DeferredDoRandom: public LDeferredCode {
+ public:
+ DeferredDoRandom(LCodeGen* codegen, LRandom* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredRandom(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LRandom* instr_;
+ };
+
+ DeferredDoRandom* deferred = new(zone()) DeferredDoRandom(this, 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->global_object()).is(rcx));
+ Register global_object = rcx;
+#else
+ ASSERT(ToRegister(instr->global_object()).is(rdi));
+ Register global_object = rdi;
+#endif
+
+ static const int kSeedSize = sizeof(uint32_t);
+ STATIC_ASSERT(kPointerSize == 2 * kSeedSize);
+
+ __ movq(global_object,
+ FieldOperand(global_object, GlobalObject::kNativeContextOffset));
+ static const int kRandomSeedOffset =
+ FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
+ __ movq(rbx, FieldOperand(global_object, kRandomSeedOffset));
+ // rbx: FixedArray of the native context's random seeds
+
+ // Load state[0].
+ __ movl(rax, FieldOperand(rbx, ByteArray::kHeaderSize));
+ // If state[0] == 0, call runtime to initialize seeds.
+ __ testl(rax, rax);
+ __ j(zero, deferred->entry());
+ // Load state[1].
+ __ movl(rcx, FieldOperand(rbx, ByteArray::kHeaderSize + kSeedSize));
+
+ // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
+ // Only operate on the lower 32 bit of rax.
+ __ movzxwl(rdx, rax);
+ __ imull(rdx, rdx, Immediate(18273));
+ __ shrl(rax, Immediate(16));
+ __ addl(rax, rdx);
+ // Save state[0].
+ __ movl(FieldOperand(rbx, ByteArray::kHeaderSize), rax);
+
+ // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
+ __ movzxwl(rdx, rcx);
+ __ imull(rdx, rdx, Immediate(36969));
+ __ shrl(rcx, Immediate(16));
+ __ addl(rcx, rdx);
+ // Save state[1].
+ __ movl(FieldOperand(rbx, ByteArray::kHeaderSize + kSeedSize), rcx);
+
+ // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF)
+ __ shll(rax, Immediate(14));
+ __ andl(rcx, Immediate(0x3FFFF));
+ __ addl(rax, rcx);
+
+ __ bind(deferred->exit());
+ // 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)).
+ __ movq(rcx, V8_INT64_C(0x4130000000000000),
+ RelocInfo::NONE64); // 1.0 x 2^20 as double
+ __ movq(xmm2, rcx);
+ __ movd(xmm1, rax);
+ __ xorps(xmm1, xmm2);
+ __ subsd(xmm1, xmm2);
+}
+
+
+void LCodeGen::DoDeferredRandom(LRandom* instr) {
+ __ PrepareCallCFunction(1);
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ // Return value is in rax.
+}
+
+
+void LCodeGen::DoMathExp(LMathExp* instr) {
+ XMMRegister input = ToDoubleRegister(instr->value());
+ XMMRegister result = ToDoubleRegister(instr->result());
+ Register temp1 = ToRegister(instr->temp1());
+ Register temp2 = ToRegister(instr->temp2());
+
+ MathExpGenerator::EmitMathExp(masm(), input, result, xmm0, temp1, temp2);
+}
+
+
+void LCodeGen::DoMathLog(LMathLog* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
+ TranscendentalCacheStub stub(TranscendentalCache::LOG,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathTan(LMathTan* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
+ TranscendentalCacheStub stub(TranscendentalCache::TAN,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathCos(LMathCos* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
+ TranscendentalCacheStub stub(TranscendentalCache::COS,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoMathSin(LMathSin* instr) {
+ ASSERT(ToDoubleRegister(instr->result()).is(xmm1));
+ TranscendentalCacheStub stub(TranscendentalCache::SIN,
+ TranscendentalCacheStub::UNTAGGED);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+}
+
+
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(rdi));
+ ASSERT(instr->HasPointerMap());
+
+ Handle<JSFunction> known_function = instr->hydrogen()->known_function();
+ if (known_function.is_null()) {
+ LPointerMap* pointers = instr->pointer_map();
+ RecordPosition(pointers->position());
+ SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt);
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(rdi, count, CALL_FUNCTION, generator, CALL_AS_METHOD);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ } else {
+ CallKnownFunction(known_function,
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD,
+ RDI_CONTAINS_TARGET);
+ }
+}
+
+
+void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
+ ASSERT(ToRegister(instr->key()).is(rcx));
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ int arity = instr->arity();
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arity);
+ CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallNamed(LCallNamed* instr) {
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ int arity = instr->arity();
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
+ __ Move(rcx, instr->name());
+ CallCode(ic, mode, instr);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallFunction(LCallFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(rdi));
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ int arity = instr->arity();
+ CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
+ ASSERT(ToRegister(instr->result()).is(rax));
+ int arity = instr->arity();
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
+ __ Move(rcx, instr->name());
+ CallCode(ic, mode, instr);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+}
+
+
+void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) {
+ ASSERT(ToRegister(instr->result()).is(rax));
+ CallKnownFunction(instr->hydrogen()->target(),
+ instr->hydrogen()->formal_parameter_count(),
+ instr->arity(),
+ instr,
+ CALL_AS_FUNCTION,
+ RDI_UNINITIALIZED);
+}
+
+
+void LCodeGen::DoCallNew(LCallNew* instr) {
+ ASSERT(ToRegister(instr->constructor()).is(rdi));
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ __ Set(rax, instr->arity());
+ // No cell in ebx for construct type feedback in optimized code
+ Handle<Object> undefined_value(isolate()->factory()->undefined_value());
+ __ Move(rbx, undefined_value);
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+}
+
+
+void LCodeGen::DoCallNewArray(LCallNewArray* instr) {
+ ASSERT(ToRegister(instr->constructor()).is(rdi));
+ ASSERT(ToRegister(instr->result()).is(rax));
+
+ __ Set(rax, instr->arity());
+ __ Move(rbx, instr->hydrogen()->property_cell());
+ ElementsKind kind = instr->hydrogen()->elements_kind();
+ AllocationSiteOverrideMode override_mode =
+ (AllocationSite::GetMode(kind) == TRACK_ALLOCATION_SITE)
+ ? DISABLE_ALLOCATION_SITES
+ : DONT_OVERRIDE;
+ ContextCheckMode context_mode = CONTEXT_CHECK_NOT_REQUIRED;
+
+ if (instr->arity() == 0) {
+ ArrayNoArgumentConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ } else if (instr->arity() == 1) {
+ Label done;
+ if (IsFastPackedElementsKind(kind)) {
+ Label packed_case;
+ // We might need a change here
+ // look at the first argument
+ __ movq(rcx, Operand(rsp, 0));
+ __ testq(rcx, rcx);
+ __ j(zero, &packed_case);
+
+ ElementsKind holey_kind = GetHoleyElementsKind(kind);
+ ArraySingleArgumentConstructorStub stub(holey_kind, context_mode,
+ override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ __ jmp(&done);
+ __ bind(&packed_case);
+ }
+
+ ArraySingleArgumentConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ __ bind(&done);
+ } else {
+ ArrayNArgumentsConstructorStub stub(kind, context_mode, override_mode);
+ CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr);
+ }
+}
+
+
+void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
+ CallRuntime(instr->function(), instr->arity(), instr);
+}
+
+
+void LCodeGen::DoInnerAllocatedObject(LInnerAllocatedObject* instr) {
+ Register result = ToRegister(instr->result());
+ Register base = ToRegister(instr->base_object());
+ __ lea(result, Operand(base, instr->offset()));
+}
+
+
+void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
+ Representation representation = instr->representation();
+
+ HObjectAccess access = instr->hydrogen()->access();
+ int offset = access.offset();
+
+ if (access.IsExternalMemory()) {
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ Register value = ToRegister(instr->value());
+ if (instr->object()->IsConstantOperand()) {
+ ASSERT(value.is(rax));
+ LConstantOperand* object = LConstantOperand::cast(instr->object());
+ __ store_rax(ToExternalReference(object));
+ } else {
+ Register object = ToRegister(instr->object());
+ __ movq(MemOperand(object, offset), value);
+ }
+ return;
+ }
+
+ Register object = ToRegister(instr->object());
+ Handle<Map> transition = instr->transition();
+
+ if (FLAG_track_fields && representation.IsSmi()) {
+ if (instr->value()->IsConstantOperand()) {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ if (!IsSmiConstant(operand_value)) {
+ DeoptimizeIf(no_condition, instr->environment());
+ }
+ }
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ if (instr->value()->IsConstantOperand()) {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ if (IsInteger32Constant(operand_value)) {
+ DeoptimizeIf(no_condition, instr->environment());
+ }
+ } else {
+ if (!instr->hydrogen()->value()->type().IsHeapObject()) {
+ Register value = ToRegister(instr->value());
+ Condition cc = masm()->CheckSmi(value);
+ DeoptimizeIf(cc, instr->environment());
+ }
+ }
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ ASSERT(transition.is_null());
+ ASSERT(access.IsInobject());
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ XMMRegister value = ToDoubleRegister(instr->value());
+ __ movsd(FieldOperand(object, offset), value);
+ return;
+ }
+
+ if (!transition.is_null()) {
+ if (!instr->hydrogen()->NeedsWriteBarrierForMap()) {
+ __ Move(FieldOperand(object, HeapObject::kMapOffset), transition);
+ } else {
+ Register temp = ToRegister(instr->temp());
+ __ Move(kScratchRegister, transition);
+ __ movq(FieldOperand(object, HeapObject::kMapOffset), kScratchRegister);
+ // Update the write barrier for the map field.
+ __ RecordWriteField(object,
+ HeapObject::kMapOffset,
+ kScratchRegister,
+ temp,
+ kSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+ }
+ }
+
+ // Do the store.
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+
+ Register write_register = object;
+ if (!access.IsInobject()) {
+ write_register = ToRegister(instr->temp());
+ __ movq(write_register, FieldOperand(object, JSObject::kPropertiesOffset));
+ }
+
+ if (instr->value()->IsConstantOperand()) {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ if (operand_value->IsRegister()) {
+ __ movq(FieldOperand(write_register, offset),
+ ToRegister(operand_value));
+ } else {
+ Handle<Object> handle_value = ToHandle(operand_value);
+ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
+ __ Move(FieldOperand(write_register, offset), handle_value);
+ }
+ } else {
+ __ movq(FieldOperand(write_register, offset), ToRegister(instr->value()));
+ }
+
+ if (instr->hydrogen()->NeedsWriteBarrier()) {
+ Register value = ToRegister(instr->value());
+ Register temp = access.IsInobject() ? ToRegister(instr->temp()) : object;
+ // Update the write barrier for the object for in-object properties.
+ __ RecordWriteField(write_register,
+ offset,
+ value,
+ temp,
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+}
+
+
+void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
+ ASSERT(ToRegister(instr->object()).is(rdx));
+ ASSERT(ToRegister(instr->value()).is(rax));
+
+ __ Move(rcx, instr->hydrogen()->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::ApplyCheckIf(Condition cc, LBoundsCheck* check) {
+ if (FLAG_debug_code && check->hydrogen()->skip_check()) {
+ Label done;
+ __ j(NegateCondition(cc), &done, Label::kNear);
+ __ int3();
+ __ bind(&done);
+ } else {
+ DeoptimizeIf(cc, check->environment());
+ }
+}
+
+
+void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
+ if (instr->hydrogen()->skip_check()) return;
+
+ if (instr->length()->IsRegister()) {
+ Register reg = ToRegister(instr->length());
+ if (!instr->hydrogen()->length()->representation().IsSmi()) {
+ __ AssertZeroExtended(reg);
+ }
+ if (instr->index()->IsConstantOperand()) {
+ int32_t constant_index =
+ ToInteger32(LConstantOperand::cast(instr->index()));
+ if (instr->hydrogen()->length()->representation().IsSmi()) {
+ __ Cmp(reg, Smi::FromInt(constant_index));
+ } else {
+ __ cmpq(reg, Immediate(constant_index));
+ }
+ } else {
+ Register reg2 = ToRegister(instr->index());
+ if (!instr->hydrogen()->index()->representation().IsSmi()) {
+ __ AssertZeroExtended(reg2);
+ }
+ __ cmpq(reg, reg2);
+ }
+ } else {
+ Operand length = ToOperand(instr->length());
+ if (instr->index()->IsConstantOperand()) {
+ int32_t constant_index =
+ ToInteger32(LConstantOperand::cast(instr->index()));
+ if (instr->hydrogen()->length()->representation().IsSmi()) {
+ __ Cmp(length, Smi::FromInt(constant_index));
+ } else {
+ __ cmpq(length, Immediate(constant_index));
+ }
+ } else {
+ __ cmpq(length, ToRegister(instr->index()));
+ }
+ }
+ Condition condition =
+ instr->hydrogen()->allow_equality() ? below : below_equal;
+ ApplyCheckIf(condition, instr);
+}
+
+
+void LCodeGen::DoStoreKeyedExternalArray(LStoreKeyed* instr) {
+ ElementsKind elements_kind = instr->elements_kind();
+ LOperand* key = instr->key();
+ if (!key->IsConstantOperand()) {
+ Register key_reg = ToRegister(key);
+ // Even though the HLoad/StoreKeyedFastElement instructions force
+ // the input representation for the key to be an integer, the input
+ // gets replaced during bound check elimination with the index
+ // argument to the bounds check, which can be tagged, so that case
+ // must be handled here, too.
+ if (instr->hydrogen()->IsDehoisted()) {
+ // Sign extend key because it could be a 32 bit negative value
+ // and the dehoisted address computation happens in 64 bits
+ __ movsxlq(key_reg, key_reg);
+ }
+ }
+ Operand operand(BuildFastArrayOperand(
+ instr->elements(),
+ key,
+ elements_kind,
+ 0,
+ instr->additional_index()));
+
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ XMMRegister value(ToDoubleRegister(instr->value()));
+ __ cvtsd2ss(value, value);
+ __ movss(operand, value);
+ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ __ movsd(operand, ToDoubleRegister(instr->value()));
+ } else {
+ Register value(ToRegister(instr->value()));
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ movb(operand, value);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ movw(operand, value);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ movl(operand, value);
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr) {
+ XMMRegister value = ToDoubleRegister(instr->value());
+ LOperand* key = instr->key();
+ if (!key->IsConstantOperand()) {
+ Register key_reg = ToRegister(key);
+ // Even though the HLoad/StoreKeyedFastElement instructions force
+ // the input representation for the key to be an integer, the
+ // input gets replaced during bound check elimination with the index
+ // argument to the bounds check, which can be tagged, so that case
+ // must be handled here, too.
+ if (instr->hydrogen()->IsDehoisted()) {
+ // Sign extend key because it could be a 32 bit negative value
+ // and the dehoisted address computation happens in 64 bits
+ __ movsxlq(key_reg, key_reg);
+ }
+ }
+
+ if (instr->NeedsCanonicalization()) {
+ Label have_value;
+
+ __ ucomisd(value, value);
+ __ j(parity_odd, &have_value); // NaN.
+
+ __ Set(kScratchRegister, BitCast<uint64_t>(
+ FixedDoubleArray::canonical_not_the_hole_nan_as_double()));
+ __ movq(value, kScratchRegister);
+
+ __ bind(&have_value);
+ }
+
+ Operand double_store_operand = BuildFastArrayOperand(
+ instr->elements(),
+ key,
+ FAST_DOUBLE_ELEMENTS,
+ FixedDoubleArray::kHeaderSize - kHeapObjectTag,
+ instr->additional_index());
+
+ __ movsd(double_store_operand, value);
+}
+
+
+void LCodeGen::DoStoreKeyedFixedArray(LStoreKeyed* instr) {
+ Register elements = ToRegister(instr->elements());
+ LOperand* key = instr->key();
+ if (!key->IsConstantOperand()) {
+ Register key_reg = ToRegister(key);
+ // Even though the HLoad/StoreKeyedFastElement instructions force
+ // the input representation for the key to be an integer, the
+ // input gets replaced during bound check elimination with the index
+ // argument to the bounds check, which can be tagged, so that case
+ // must be handled here, too.
+ if (instr->hydrogen()->IsDehoisted()) {
+ // Sign extend key because it could be a 32 bit negative value
+ // and the dehoisted address computation happens in 64 bits
+ __ movsxlq(key_reg, key_reg);
+ }
+ }
+
+ Operand operand =
+ BuildFastArrayOperand(instr->elements(),
+ key,
+ FAST_ELEMENTS,
+ FixedArray::kHeaderSize - kHeapObjectTag,
+ instr->additional_index());
+ if (instr->value()->IsRegister()) {
+ __ movq(operand, ToRegister(instr->value()));
+ } else {
+ LConstantOperand* operand_value = LConstantOperand::cast(instr->value());
+ if (IsInteger32Constant(operand_value)) {
+ Smi* smi_value = Smi::FromInt(ToInteger32(operand_value));
+ __ Move(operand, smi_value);
+ } else {
+ Handle<Object> handle_value = ToHandle(operand_value);
+ __ Move(operand, handle_value);
+ }
+ }
+
+ if (instr->hydrogen()->NeedsWriteBarrier()) {
+ ASSERT(instr->value()->IsRegister());
+ Register value = ToRegister(instr->value());
+ ASSERT(!instr->key()->IsConstantOperand());
+ SmiCheck check_needed =
+ instr->hydrogen()->value()->IsHeapObject()
+ ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
+ // Compute address of modified element and store it into key register.
+ Register key_reg(ToRegister(key));
+ __ lea(key_reg, operand);
+ __ RecordWrite(elements,
+ key_reg,
+ value,
+ kSaveFPRegs,
+ EMIT_REMEMBERED_SET,
+ check_needed);
+ }
+}
+
+
+void LCodeGen::DoStoreKeyed(LStoreKeyed* instr) {
+ if (instr->is_external()) {
+ DoStoreKeyedExternalArray(instr);
+ } else if (instr->hydrogen()->value()->representation().IsDouble()) {
+ DoStoreKeyedFixedDoubleArray(instr);
+ } else {
+ DoStoreKeyedFixedArray(instr);
+ }
+}
+
+
+void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
+ ASSERT(ToRegister(instr->object()).is(rdx));
+ ASSERT(ToRegister(instr->key()).is(rcx));
+ ASSERT(ToRegister(instr->value()).is(rax));
+
+ 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());
+
+ Handle<Map> from_map = instr->original_map();
+ Handle<Map> to_map = instr->transitioned_map();
+ ElementsKind from_kind = instr->from_kind();
+ ElementsKind to_kind = instr->to_kind();
+
+ Label not_applicable;
+ __ Cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map);
+ __ j(not_equal, &not_applicable);
+ if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
+ Register new_map_reg = ToRegister(instr->new_map_temp());
+ __ movq(new_map_reg, to_map, RelocInfo::EMBEDDED_OBJECT);
+ __ movq(FieldOperand(object_reg, HeapObject::kMapOffset), new_map_reg);
+ // Write barrier.
+ ASSERT_NE(instr->temp(), NULL);
+ __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
+ ToRegister(instr->temp()), kDontSaveFPRegs);
+ } else {
+ PushSafepointRegistersScope scope(this);
+ if (!object_reg.is(rax)) {
+ __ movq(rax, object_reg);
+ }
+ __ Move(rbx, to_map);
+ TransitionElementsKindStub stub(from_kind, to_kind);
+ __ CallStub(&stub);
+ RecordSafepointWithRegisters(
+ instr->pointer_map(), 0, Safepoint::kNoLazyDeopt);
+ }
+ __ bind(&not_applicable);
+}
+
+
+void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
+ Register object = ToRegister(instr->object());
+ Register temp = ToRegister(instr->temp());
+ __ TestJSArrayForAllocationMemento(object, temp);
+ DeoptimizeIf(equal, instr->environment());
+}
+
+
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ EmitPushTaggedOperand(instr->left());
+ EmitPushTaggedOperand(instr->right());
+ StringAddStub stub(instr->hydrogen()->flags());
+ CallCode(stub.GetCode(isolate()), 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(zone()) 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());
+
+ // 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.
+ __ Set(result, 0);
+
+ PushSafepointRegistersScope scope(this);
+ __ push(string);
+ // Push the index as a smi. This is safe because of the checks in
+ // DoStringCharCodeAt above.
+ STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
+ if (instr->index()->IsConstantOperand()) {
+ int32_t const_index = ToInteger32(LConstantOperand::cast(instr->index()));
+ __ Push(Smi::FromInt(const_index));
+ } else {
+ Register index = ToRegister(instr->index());
+ __ Integer32ToSmi(index, index);
+ __ push(index);
+ }
+ CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr);
+ __ AssertSmi(rax);
+ __ SmiToInteger32(rax, rax);
+ __ StoreToSafepointRegisterSlot(result, rax);
+}
+
+
+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(zone()) DeferredStringCharFromCode(this, instr);
+
+ ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+ Register char_code = ToRegister(instr->char_code());
+ Register result = ToRegister(instr->result());
+ ASSERT(!char_code.is(result));
+
+ __ cmpl(char_code, Immediate(String::kMaxOneByteCharCode));
+ __ j(above, deferred->entry());
+ __ movsxlq(char_code, char_code);
+ __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
+ __ movq(result, FieldOperand(result,
+ char_code, times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(result, Heap::kUndefinedValueRootIndex);
+ __ j(equal, deferred->entry());
+ __ 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.
+ __ Set(result, 0);
+
+ PushSafepointRegistersScope scope(this);
+ __ Integer32ToSmi(char_code, char_code);
+ __ push(char_code);
+ CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr);
+ __ StoreToSafepointRegisterSlot(result, rax);
+}
+
+
+void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() || input->IsStackSlot());
+ LOperand* output = instr->result();
+ ASSERT(output->IsDoubleRegister());
+ if (input->IsRegister()) {
+ __ cvtlsi2sd(ToDoubleRegister(output), ToRegister(input));
+ } else {
+ __ cvtlsi2sd(ToDoubleRegister(output), ToOperand(input));
+ }
+}
+
+
+void LCodeGen::DoInteger32ToSmi(LInteger32ToSmi* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ LOperand* output = instr->result();
+ __ Integer32ToSmi(ToRegister(output), ToRegister(input));
+ if (!instr->hydrogen()->value()->HasRange() ||
+ !instr->hydrogen()->value()->range()->IsInSmiRange()) {
+ DeoptimizeIf(overflow, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) {
+ LOperand* input = instr->value();
+ LOperand* output = instr->result();
+ LOperand* temp = instr->temp();
+
+ __ LoadUint32(ToDoubleRegister(output),
+ ToRegister(input),
+ ToDoubleRegister(temp));
+}
+
+
+void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ Register reg = ToRegister(input);
+
+ __ Integer32ToSmi(reg, reg);
+}
+
+
+void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
+ class DeferredNumberTagU: public LDeferredCode {
+ public:
+ DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() {
+ codegen()->DoDeferredNumberTagU(instr_);
+ }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LNumberTagU* instr_;
+ };
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister() && input->Equals(instr->result()));
+ Register reg = ToRegister(input);
+
+ DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr);
+ __ cmpl(reg, Immediate(Smi::kMaxValue));
+ __ j(above, deferred->entry());
+ __ Integer32ToSmi(reg, reg);
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredNumberTagU(LNumberTagU* instr) {
+ Label slow;
+ Register reg = ToRegister(instr->value());
+ Register tmp = reg.is(rax) ? rcx : rax;
+
+ // Preserve the value of all registers.
+ PushSafepointRegistersScope scope(this);
+
+ Label done;
+ // Load value into xmm1 which will be preserved across potential call to
+ // runtime (MacroAssembler::EnterExitFrameEpilogue preserves only allocatable
+ // XMM registers on x64).
+ __ LoadUint32(xmm1, reg, xmm0);
+
+ if (FLAG_inline_new) {
+ __ AllocateHeapNumber(reg, tmp, &slow);
+ __ jmp(&done, Label::kNear);
+ }
+
+ // Slow case: Call the runtime system to do the number allocation.
+ __ bind(&slow);
+
+ // 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(reg, Immediate(0));
+
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
+ if (!reg.is(rax)) __ movq(reg, rax);
+
+ // Done. Put the value in xmm1 into the value of the allocated heap
+ // number.
+ __ bind(&done);
+ __ movsd(FieldOperand(reg, HeapNumber::kValueOffset), xmm1);
+ __ StoreToSafepointRegisterSlot(reg, reg);
+}
+
+
+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_;
+ };
+
+ XMMRegister input_reg = ToDoubleRegister(instr->value());
+ Register reg = ToRegister(instr->result());
+ Register tmp = ToRegister(instr->temp());
+
+ DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr);
+ if (FLAG_inline_new) {
+ __ AllocateHeapNumber(reg, tmp, deferred->entry());
+ } else {
+ __ jmp(deferred->entry());
+ }
+ __ bind(deferred->exit());
+ __ movsd(FieldOperand(reg, HeapNumber::kValueOffset), input_reg);
+}
+
+
+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());
+ __ Move(reg, Smi::FromInt(0));
+
+ {
+ PushSafepointRegistersScope scope(this);
+ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
+ // Ensure that value in rax survives popping registers.
+ __ movq(kScratchRegister, rax);
+ }
+ __ movq(reg, kScratchRegister);
+}
+
+
+void LCodeGen::DoSmiTag(LSmiTag* instr) {
+ ASSERT(instr->value()->Equals(instr->result()));
+ Register input = ToRegister(instr->value());
+ ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow));
+ __ Integer32ToSmi(input, input);
+}
+
+
+void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
+ ASSERT(instr->value()->Equals(instr->result()));
+ Register input = ToRegister(instr->value());
+ if (instr->needs_check()) {
+ Condition is_smi = __ CheckSmi(input);
+ DeoptimizeIf(NegateCondition(is_smi), instr->environment());
+ } else {
+ __ AssertSmi(input);
+ }
+ __ SmiToInteger32(input, input);
+}
+
+
+void LCodeGen::EmitNumberUntagD(Register input_reg,
+ XMMRegister result_reg,
+ bool can_convert_undefined_to_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode) {
+ Label load_smi, done;
+
+ if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) {
+ // Smi check.
+ __ JumpIfSmi(input_reg, &load_smi, Label::kNear);
+
+ // Heap number map check.
+ __ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+ if (!can_convert_undefined_to_nan) {
+ DeoptimizeIf(not_equal, env);
+ } else {
+ Label heap_number, convert;
+ __ j(equal, &heap_number, Label::kNear);
+
+ // Convert undefined (and hole) to NaN. Compute NaN as 0/0.
+ __ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
+ DeoptimizeIf(not_equal, env);
+
+ __ bind(&convert);
+ __ xorps(result_reg, result_reg);
+ __ divsd(result_reg, result_reg);
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&heap_number);
+ }
+ // 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);
+ } else {
+ ASSERT(mode == NUMBER_CANDIDATE_IS_SMI);
+ }
+
+ // Smi to XMM conversion
+ __ bind(&load_smi);
+ __ SmiToInteger32(kScratchRegister, input_reg);
+ __ cvtlsi2sd(result_reg, kScratchRegister);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
+ Label done, heap_number;
+ Register input_reg = ToRegister(instr->value());
+
+ // Heap number map check.
+ __ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+
+ if (instr->truncating()) {
+ __ j(equal, &heap_number, Label::kNear);
+ // Check for undefined. Undefined is converted to zero for truncating
+ // conversions.
+ __ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
+ DeoptimizeIf(not_equal, instr->environment());
+ __ Set(input_reg, 0);
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&heap_number);
+
+ __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ cvttsd2siq(input_reg, xmm0);
+ __ Set(kScratchRegister, V8_UINT64_C(0x8000000000000000));
+ __ cmpq(input_reg, kScratchRegister);
+ DeoptimizeIf(equal, instr->environment());
+ } else {
+ // Deoptimize if we don't have a heap number.
+ DeoptimizeIf(not_equal, instr->environment());
+
+ XMMRegister xmm_temp = ToDoubleRegister(instr->temp());
+ __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ cvttsd2si(input_reg, xmm0);
+ __ cvtlsi2sd(xmm_temp, input_reg);
+ __ ucomisd(xmm0, xmm_temp);
+ DeoptimizeIf(not_equal, instr->environment());
+ DeoptimizeIf(parity_even, instr->environment()); // NaN.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ testl(input_reg, input_reg);
+ __ j(not_zero, &done);
+ __ movmskpd(input_reg, xmm0);
+ __ andl(input_reg, Immediate(1));
+ DeoptimizeIf(not_zero, instr->environment());
+ }
+ }
+ __ 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->value();
+ ASSERT(input->IsRegister());
+ ASSERT(input->Equals(instr->result()));
+
+ Register input_reg = ToRegister(input);
+ DeferredTaggedToI* deferred = new(zone()) DeferredTaggedToI(this, instr);
+ __ JumpIfNotSmi(input_reg, deferred->entry());
+ __ SmiToInteger32(input_reg, input_reg);
+ __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ LOperand* result = instr->result();
+ ASSERT(result->IsDoubleRegister());
+
+ Register input_reg = ToRegister(input);
+ XMMRegister result_reg = ToDoubleRegister(result);
+
+ HValue* value = instr->hydrogen()->value();
+ NumberUntagDMode mode = value->representation().IsSmi()
+ ? NUMBER_CANDIDATE_IS_SMI : NUMBER_CANDIDATE_IS_ANY_TAGGED;
+
+ EmitNumberUntagD(input_reg, result_reg,
+ instr->hydrogen()->can_convert_undefined_to_nan(),
+ instr->hydrogen()->deoptimize_on_minus_zero(),
+ instr->environment(),
+ mode);
+}
+
+
+void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsDoubleRegister());
+ LOperand* result = instr->result();
+ ASSERT(result->IsRegister());
+
+ XMMRegister input_reg = ToDoubleRegister(input);
+ Register result_reg = ToRegister(result);
+
+ if (instr->truncating()) {
+ // Performs a truncating conversion of a floating point number as used by
+ // the JS bitwise operations.
+ __ cvttsd2siq(result_reg, input_reg);
+ __ movq(kScratchRegister,
+ V8_INT64_C(0x8000000000000000),
+ RelocInfo::NONE64);
+ __ cmpq(result_reg, kScratchRegister);
+ DeoptimizeIf(equal, instr->environment());
+ } else {
+ __ cvttsd2si(result_reg, input_reg);
+ __ cvtlsi2sd(xmm0, result_reg);
+ __ ucomisd(xmm0, input_reg);
+ DeoptimizeIf(not_equal, instr->environment());
+ DeoptimizeIf(parity_even, instr->environment()); // NaN.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ Label done;
+ // The integer converted back is equal to the original. We
+ // only have to test if we got -0 as an input.
+ __ testl(result_reg, result_reg);
+ __ j(not_zero, &done, Label::kNear);
+ __ movmskpd(result_reg, input_reg);
+ // Bit 0 contains the sign of the double in input_reg.
+ // If input was positive, we are ok and return 0, otherwise
+ // deoptimize.
+ __ andl(result_reg, Immediate(1));
+ DeoptimizeIf(not_zero, instr->environment());
+ __ bind(&done);
+ }
+ }
+}
+
+
+void LCodeGen::DoDoubleToSmi(LDoubleToSmi* instr) {
+ LOperand* input = instr->value();
+ ASSERT(input->IsDoubleRegister());
+ LOperand* result = instr->result();
+ ASSERT(result->IsRegister());
+ CpuFeatureScope scope(masm(), SSE2);
+
+ XMMRegister input_reg = ToDoubleRegister(input);
+ Register result_reg = ToRegister(result);
+
+ Label done;
+ __ cvttsd2si(result_reg, input_reg);
+ __ cvtlsi2sd(xmm0, result_reg);
+ __ ucomisd(xmm0, input_reg);
+ DeoptimizeIf(not_equal, instr->environment());
+ DeoptimizeIf(parity_even, instr->environment()); // NaN.
+
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // The integer converted back is equal to the original. We
+ // only have to test if we got -0 as an input.
+ __ testl(result_reg, result_reg);
+ __ j(not_zero, &done, Label::kNear);
+ __ movmskpd(result_reg, input_reg);
+ // Bit 0 contains the sign of the double in input_reg.
+ // If input was positive, we are ok and return 0, otherwise
+ // deoptimize.
+ __ andl(result_reg, Immediate(1));
+ DeoptimizeIf(not_zero, instr->environment());
+ __ bind(&done);
+ }
+ __ Integer32ToSmi(result_reg, result_reg);
+ DeoptimizeIf(overflow, instr->environment());
+}
+
+
+void LCodeGen::DoCheckSmi(LCheckSmi* instr) {
+ LOperand* input = instr->value();
+ Condition cc = masm()->CheckSmi(ToRegister(input));
+ DeoptimizeIf(NegateCondition(cc), instr->environment());
+}
+
+
+void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
+ if (!instr->hydrogen()->value()->IsHeapObject()) {
+ LOperand* input = instr->value();
+ Condition cc = masm()->CheckSmi(ToRegister(input));
+ DeoptimizeIf(cc, instr->environment());
+ }
+}
+
+
+void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
+ Register input = ToRegister(instr->value());
+
+ __ movq(kScratchRegister, FieldOperand(input, HeapObject::kMapOffset));
+
+ if (instr->hydrogen()->is_interval_check()) {
+ InstanceType first;
+ InstanceType last;
+ instr->hydrogen()->GetCheckInterval(&first, &last);
+
+ __ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
+ Immediate(static_cast<int8_t>(first)));
+
+ // If there is only one type in the interval check for equality.
+ if (first == last) {
+ DeoptimizeIf(not_equal, instr->environment());
+ } else {
+ DeoptimizeIf(below, instr->environment());
+ // Omit check for the last type.
+ if (last != LAST_TYPE) {
+ __ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
+ Immediate(static_cast<int8_t>(last)));
+ DeoptimizeIf(above, instr->environment());
+ }
+ }
+ } else {
+ uint8_t mask;
+ uint8_t tag;
+ instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
+
+ if (IsPowerOf2(mask)) {
+ ASSERT(tag == 0 || IsPowerOf2(tag));
+ __ testb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
+ Immediate(mask));
+ DeoptimizeIf(tag == 0 ? not_zero : zero, instr->environment());
+ } else {
+ __ movzxbl(kScratchRegister,
+ FieldOperand(kScratchRegister, Map::kInstanceTypeOffset));
+ __ andb(kScratchRegister, Immediate(mask));
+ __ cmpb(kScratchRegister, Immediate(tag));
+ DeoptimizeIf(not_equal, instr->environment());
+ }
+ }
+}
+
+
+void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
+ Register reg = ToRegister(instr->value());
+ Handle<JSFunction> target = instr->hydrogen()->target();
+ __ CmpHeapObject(reg, target);
+ DeoptimizeIf(not_equal, instr->environment());
+}
+
+
+void LCodeGen::DoDeferredInstanceMigration(LCheckMaps* instr, Register object) {
+ {
+ PushSafepointRegistersScope scope(this);
+ __ push(object);
+ CallRuntimeFromDeferred(Runtime::kMigrateInstance, 1, instr);
+ __ testq(rax, Immediate(kSmiTagMask));
+ }
+ DeoptimizeIf(zero, instr->environment());
+}
+
+
+void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
+ class DeferredCheckMaps: public LDeferredCode {
+ public:
+ DeferredCheckMaps(LCodeGen* codegen, LCheckMaps* instr, Register object)
+ : LDeferredCode(codegen), instr_(instr), object_(object) {
+ SetExit(check_maps());
+ }
+ virtual void Generate() {
+ codegen()->DoDeferredInstanceMigration(instr_, object_);
+ }
+ Label* check_maps() { return &check_maps_; }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LCheckMaps* instr_;
+ Label check_maps_;
+ Register object_;
+ };
+
+ if (instr->hydrogen()->CanOmitMapChecks()) return;
+
+ LOperand* input = instr->value();
+ ASSERT(input->IsRegister());
+ Register reg = ToRegister(input);
+
+ SmallMapList* map_set = instr->hydrogen()->map_set();
+
+ DeferredCheckMaps* deferred = NULL;
+ if (instr->hydrogen()->has_migration_target()) {
+ deferred = new(zone()) DeferredCheckMaps(this, instr, reg);
+ __ bind(deferred->check_maps());
+ }
+
+ Label success;
+ for (int i = 0; i < map_set->length() - 1; i++) {
+ Handle<Map> map = map_set->at(i);
+ __ CompareMap(reg, map, &success);
+ __ j(equal, &success);
+ }
+
+ Handle<Map> map = map_set->last();
+ __ CompareMap(reg, map, &success);
+ if (instr->hydrogen()->has_migration_target()) {
+ __ j(not_equal, deferred->entry());
+ } else {
+ DeoptimizeIf(not_equal, instr->environment());
+ }
+
+ __ bind(&success);
+}
+
+
+void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
+ XMMRegister value_reg = ToDoubleRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ __ ClampDoubleToUint8(value_reg, xmm0, result_reg);
+}
+
+
+void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
+ ASSERT(instr->unclamped()->Equals(instr->result()));
+ Register value_reg = ToRegister(instr->result());
+ __ ClampUint8(value_reg);
+}
+
+
+void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) {
+ ASSERT(instr->unclamped()->Equals(instr->result()));
+ Register input_reg = ToRegister(instr->unclamped());
+ XMMRegister temp_xmm_reg = ToDoubleRegister(instr->temp_xmm());
+ Label is_smi, done, heap_number;
+
+ __ JumpIfSmi(input_reg, &is_smi);
+
+ // Check for heap number
+ __ Cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ __ j(equal, &heap_number, Label::kNear);
+
+ // Check for undefined. Undefined is converted to zero for clamping
+ // conversions.
+ __ Cmp(input_reg, factory()->undefined_value());
+ DeoptimizeIf(not_equal, instr->environment());
+ __ movq(input_reg, Immediate(0));
+ __ jmp(&done, Label::kNear);
+
+ // Heap number
+ __ bind(&heap_number);
+ __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ ClampDoubleToUint8(xmm0, temp_xmm_reg, input_reg);
+ __ jmp(&done, Label::kNear);
+
+ // smi
+ __ bind(&is_smi);
+ __ SmiToInteger32(input_reg, input_reg);
+ __ ClampUint8(input_reg);
+
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoAllocate(LAllocate* instr) {
+ class DeferredAllocate: public LDeferredCode {
+ public:
+ DeferredAllocate(LCodeGen* codegen, LAllocate* instr)
+ : LDeferredCode(codegen), instr_(instr) { }
+ virtual void Generate() { codegen()->DoDeferredAllocate(instr_); }
+ virtual LInstruction* instr() { return instr_; }
+ private:
+ LAllocate* instr_;
+ };
+
+ DeferredAllocate* deferred =
+ new(zone()) DeferredAllocate(this, instr);
+
+ Register result = ToRegister(instr->result());
+ Register temp = ToRegister(instr->temp());
+
+ // Allocate memory for the object.
+ AllocationFlags flags = TAG_OBJECT;
+ if (instr->hydrogen()->MustAllocateDoubleAligned()) {
+ flags = static_cast<AllocationFlags>(flags | DOUBLE_ALIGNMENT);
+ }
+ if (instr->hydrogen()->IsOldPointerSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation());
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ flags = static_cast<AllocationFlags>(flags | PRETENURE_OLD_POINTER_SPACE);
+ } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ flags = static_cast<AllocationFlags>(flags | PRETENURE_OLD_DATA_SPACE);
+ }
+
+ if (instr->size()->IsConstantOperand()) {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ Allocate(size, result, temp, no_reg, deferred->entry(), flags);
+ } else {
+ Register size = ToRegister(instr->size());
+ __ Allocate(size, result, temp, no_reg, deferred->entry(), flags);
+ }
+
+ __ bind(deferred->exit());
+
+ if (instr->hydrogen()->MustPrefillWithFiller()) {
+ if (instr->size()->IsConstantOperand()) {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ movl(temp, Immediate((size / kPointerSize) - 1));
+ } else {
+ temp = ToRegister(instr->size());
+ __ sar(temp, Immediate(kPointerSizeLog2));
+ __ decl(temp);
+ }
+ Label loop;
+ __ bind(&loop);
+ __ Move(FieldOperand(result, temp, times_pointer_size, 0),
+ isolate()->factory()->one_pointer_filler_map());
+ __ decl(temp);
+ __ j(not_zero, &loop);
+ }
+}
+
+
+void LCodeGen::DoDeferredAllocate(LAllocate* instr) {
+ 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.
+ __ Move(result, Smi::FromInt(0));
+
+ PushSafepointRegistersScope scope(this);
+ if (instr->size()->IsRegister()) {
+ Register size = ToRegister(instr->size());
+ ASSERT(!size.is(result));
+ __ Integer32ToSmi(size, size);
+ __ push(size);
+ } else {
+ int32_t size = ToInteger32(LConstantOperand::cast(instr->size()));
+ __ Push(Smi::FromInt(size));
+ }
+
+ if (instr->hydrogen()->IsOldPointerSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation());
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ CallRuntimeFromDeferred(Runtime::kAllocateInOldPointerSpace, 1, instr);
+ } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) {
+ ASSERT(!instr->hydrogen()->IsNewSpaceAllocation());
+ CallRuntimeFromDeferred(Runtime::kAllocateInOldDataSpace, 1, instr);
+ } else {
+ CallRuntimeFromDeferred(Runtime::kAllocateInNewSpace, 1, instr);
+ }
+ __ StoreToSafepointRegisterSlot(result, rax);
+}
+
+
+void LCodeGen::DoToFastProperties(LToFastProperties* instr) {
+ ASSERT(ToRegister(instr->value()).is(rax));
+ __ push(rax);
+ CallRuntime(Runtime::kToFastProperties, 1, instr);
+}
+
+
+void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
+ Label materialized;
+ // Registers will be used as follows:
+ // rcx = literals array.
+ // rbx = regexp literal.
+ // rax = regexp literal clone.
+ int literal_offset =
+ FixedArray::OffsetOfElementAt(instr->hydrogen()->literal_index());
+ __ LoadHeapObject(rcx, instr->hydrogen()->literals());
+ __ movq(rbx, FieldOperand(rcx, literal_offset));
+ __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
+ __ j(not_equal, &materialized, Label::kNear);
+
+ // Create regexp literal using runtime function
+ // Result will be in rax.
+ __ push(rcx);
+ __ Push(Smi::FromInt(instr->hydrogen()->literal_index()));
+ __ Push(instr->hydrogen()->pattern());
+ __ Push(instr->hydrogen()->flags());
+ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr);
+ __ movq(rbx, rax);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+ __ Allocate(size, rax, rcx, rdx, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ push(rbx);
+ __ Push(Smi::FromInt(size));
+ CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
+ __ pop(rbx);
+
+ __ 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) {
+ __ movq(rdx, FieldOperand(rbx, i));
+ __ movq(rcx, FieldOperand(rbx, i + kPointerSize));
+ __ movq(FieldOperand(rax, i), rdx);
+ __ movq(FieldOperand(rax, i + kPointerSize), rcx);
+ }
+ if ((size % (2 * kPointerSize)) != 0) {
+ __ movq(rdx, FieldOperand(rbx, size - kPointerSize));
+ __ movq(FieldOperand(rax, size - kPointerSize), rdx);
+ }
+}
+
+
+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.
+ bool pretenure = instr->hydrogen()->pretenure();
+ if (!pretenure && instr->hydrogen()->has_no_literals()) {
+ FastNewClosureStub stub(instr->hydrogen()->language_mode(),
+ instr->hydrogen()->is_generator());
+ __ Push(instr->hydrogen()->shared_info());
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ } else {
+ __ push(rsi);
+ __ Push(instr->hydrogen()->shared_info());
+ __ PushRoot(pretenure ? Heap::kTrueValueRootIndex :
+ Heap::kFalseValueRootIndex);
+ CallRuntime(Runtime::kNewClosure, 3, instr);
+ }
+}
+
+
+void LCodeGen::DoTypeof(LTypeof* instr) {
+ LOperand* input = instr->value();
+ EmitPushTaggedOperand(input);
+ CallRuntime(Runtime::kTypeof, 1, instr);
+}
+
+
+void LCodeGen::EmitPushTaggedOperand(LOperand* operand) {
+ ASSERT(!operand->IsDoubleRegister());
+ if (operand->IsConstantOperand()) {
+ Handle<Object> object = ToHandle(LConstantOperand::cast(operand));
+ AllowDeferredHandleDereference smi_check;
+ 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));
+ }
+}
+
+
+void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) {
+ Register input = ToRegister(instr->value());
+
+ Condition final_branch_condition =
+ EmitTypeofIs(instr->TrueLabel(chunk_),
+ instr->FalseLabel(chunk_), input, instr->type_literal());
+ if (final_branch_condition != no_condition) {
+ EmitBranch(instr, final_branch_condition);
+ }
+}
+
+
+Condition LCodeGen::EmitTypeofIs(Label* true_label,
+ Label* false_label,
+ Register input,
+ Handle<String> type_name) {
+ Condition final_branch_condition = no_condition;
+ if (type_name->Equals(heap()->number_string())) {
+ __ JumpIfSmi(input, true_label);
+ __ CompareRoot(FieldOperand(input, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+
+ final_branch_condition = equal;
+
+ } else if (type_name->Equals(heap()->string_string())) {
+ __ JumpIfSmi(input, false_label);
+ __ CmpObjectType(input, FIRST_NONSTRING_TYPE, input);
+ __ j(above_equal, false_label);
+ __ testb(FieldOperand(input, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ final_branch_condition = zero;
+
+ } else if (type_name->Equals(heap()->symbol_string())) {
+ __ JumpIfSmi(input, false_label);
+ __ CmpObjectType(input, SYMBOL_TYPE, input);
+ final_branch_condition = equal;
+
+ } else if (type_name->Equals(heap()->boolean_string())) {
+ __ CompareRoot(input, Heap::kTrueValueRootIndex);
+ __ j(equal, true_label);
+ __ CompareRoot(input, Heap::kFalseValueRootIndex);
+ final_branch_condition = equal;
+
+ } else if (FLAG_harmony_typeof && type_name->Equals(heap()->null_string())) {
+ __ CompareRoot(input, Heap::kNullValueRootIndex);
+ final_branch_condition = equal;
+
+ } else if (type_name->Equals(heap()->undefined_string())) {
+ __ CompareRoot(input, Heap::kUndefinedValueRootIndex);
+ __ j(equal, true_label);
+ __ JumpIfSmi(input, false_label);
+ // Check for undetectable objects => true.
+ __ movq(input, FieldOperand(input, HeapObject::kMapOffset));
+ __ testb(FieldOperand(input, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ final_branch_condition = not_zero;
+
+ } else if (type_name->Equals(heap()->function_string())) {
+ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
+ __ JumpIfSmi(input, false_label);
+ __ 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_string())) {
+ __ JumpIfSmi(input, false_label);
+ if (!FLAG_harmony_typeof) {
+ __ CompareRoot(input, Heap::kNullValueRootIndex);
+ __ j(equal, true_label);
+ }
+ __ CmpObjectType(input, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, input);
+ __ j(below, false_label);
+ __ CmpInstanceType(input, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
+ __ j(above, false_label);
+ // Check for undetectable objects => false.
+ __ testb(FieldOperand(input, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ final_branch_condition = zero;
+
+ } else {
+ __ jmp(false_label);
+ }
+
+ return final_branch_condition;
+}
+
+
+void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) {
+ Register temp = ToRegister(instr->temp());
+
+ EmitIsConstructCall(temp);
+ EmitBranch(instr, equal);
+}
+
+
+void LCodeGen::EmitIsConstructCall(Register temp) {
+ // Get the frame pointer for the calling frame.
+ __ movq(temp, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+
+ // Skip the arguments adaptor frame if it exists.
+ Label check_frame_marker;
+ __ Cmp(Operand(temp, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(not_equal, &check_frame_marker, Label::kNear);
+ __ movq(temp, Operand(rax, StandardFrameConstants::kCallerFPOffset));
+
+ // Check the marker in the calling frame.
+ __ bind(&check_frame_marker);
+ __ Cmp(Operand(temp, StandardFrameConstants::kMarkerOffset),
+ Smi::FromInt(StackFrame::CONSTRUCT));
+}
+
+
+void LCodeGen::EnsureSpaceForLazyDeopt(int space_needed) {
+ if (info()->IsStub()) return;
+ // Ensure that we have enough space after the previous lazy-bailout
+ // instruction for patching the code here.
+ 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;
+ __ Nop(padding_size);
+ }
+}
+
+
+void LCodeGen::DoLazyBailout(LLazyBailout* instr) {
+ EnsureSpaceForLazyDeopt(Deoptimizer::patch_size());
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ ASSERT(instr->HasEnvironment());
+ LEnvironment* env = instr->environment();
+ RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt);
+ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
+}
+
+
+void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
+ Deoptimizer::BailoutType type = instr->hydrogen()->type();
+ // TODO(danno): Stubs expect all deopts to be lazy for historical reasons (the
+ // needed return address), even though the implementation of LAZY and EAGER is
+ // now identical. When LAZY is eventually completely folded into EAGER, remove
+ // the special case below.
+ if (info()->IsStub() && type == Deoptimizer::EAGER) {
+ type = Deoptimizer::LAZY;
+ }
+
+ Comment(";;; deoptimize: %s", instr->hydrogen()->reason());
+ DeoptimizeIf(no_condition, instr->environment(), type);
+}
+
+
+void LCodeGen::DoDummyUse(LDummyUse* instr) {
+ // Nothing to see here, move on!
+}
+
+
+void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) {
+ PushSafepointRegistersScope scope(this);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
+ RecordSafepointWithLazyDeopt(instr, RECORD_SAFEPOINT_WITH_REGISTERS, 0);
+ 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;
+ __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
+ __ j(above_equal, &done, Label::kNear);
+ StackCheckStub stub;
+ CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
+ EnsureSpaceForLazyDeopt(Deoptimizer::patch_size());
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ __ 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(zone()) DeferredStackCheck(this, instr);
+ __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
+ __ j(below, deferred_stack_check->entry());
+ EnsureSpaceForLazyDeopt(Deoptimizer::patch_size());
+ last_lazy_deopt_pc_ = masm()->pc_offset();
+ __ 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();
+
+ // 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);
+
+ // Normally we record the first unknown OSR value as the entrypoint to the OSR
+ // code, but if there were none, record the entrypoint here.
+ if (osr_pc_offset_ == -1) osr_pc_offset_ = masm()->pc_offset();
+}
+
+
+void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+ DeoptimizeIf(equal, instr->environment());
+
+ Register null_value = rdi;
+ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
+ __ cmpq(rax, null_value);
+ DeoptimizeIf(equal, instr->environment());
+
+ Condition cc = masm()->CheckSmi(rax);
+ DeoptimizeIf(cc, instr->environment());
+
+ STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
+ __ CmpObjectType(rax, LAST_JS_PROXY_TYPE, rcx);
+ DeoptimizeIf(below_equal, instr->environment());
+
+ Label use_cache, call_runtime;
+ __ CheckEnumCache(null_value, &call_runtime);
+
+ __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset));
+ __ jmp(&use_cache, Label::kNear);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(rax);
+ CallRuntime(Runtime::kGetPropertyNamesFast, 1, instr);
+
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kMetaMapRootIndex);
+ DeoptimizeIf(not_equal, instr->environment());
+ __ bind(&use_cache);
+}
+
+
+void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
+ Register map = ToRegister(instr->map());
+ Register result = ToRegister(instr->result());
+ Label load_cache, done;
+ __ EnumLength(result, map);
+ __ Cmp(result, Smi::FromInt(0));
+ __ j(not_equal, &load_cache);
+ __ LoadRoot(result, Heap::kEmptyFixedArrayRootIndex);
+ __ jmp(&done);
+ __ bind(&load_cache);
+ __ LoadInstanceDescriptors(map, result);
+ __ movq(result,
+ FieldOperand(result, DescriptorArray::kEnumCacheOffset));
+ __ movq(result,
+ FieldOperand(result, FixedArray::SizeFor(instr->idx())));
+ __ bind(&done);
+ Condition cc = masm()->CheckSmi(result);
+ DeoptimizeIf(cc, instr->environment());
+}
+
+
+void LCodeGen::DoCheckMapValue(LCheckMapValue* instr) {
+ Register object = ToRegister(instr->value());
+ __ cmpq(ToRegister(instr->map()),
+ FieldOperand(object, HeapObject::kMapOffset));
+ DeoptimizeIf(not_equal, instr->environment());
+}
+
+
+void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
+ Register object = ToRegister(instr->object());
+ Register index = ToRegister(instr->index());
+
+ Label out_of_object, done;
+ __ SmiToInteger32(index, index);
+ __ cmpl(index, Immediate(0));
+ __ j(less, &out_of_object);
+ __ movq(object, FieldOperand(object,
+ index,
+ times_pointer_size,
+ JSObject::kHeaderSize));
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&out_of_object);
+ __ movq(object, FieldOperand(object, JSObject::kPropertiesOffset));
+ __ negl(index);
+ // Index is now equal to out of object property index plus 1.
+ __ movq(object, FieldOperand(object,
+ index,
+ times_pointer_size,
+ FixedArray::kHeaderSize - kPointerSize));
+ __ bind(&done);
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/lithium-codegen-x64.h b/chromium/v8/src/x64/lithium-codegen-x64.h
new file mode 100644
index 00000000000..a74ec7982c4
--- /dev/null
+++ b/chromium/v8/src/x64/lithium-codegen-x64.h
@@ -0,0 +1,446 @@
+// Copyright 2012 the V8 project authors. 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_X64_LITHIUM_CODEGEN_X64_H_
+#define V8_X64_LITHIUM_CODEGEN_X64_H_
+
+#include "x64/lithium-x64.h"
+
+#include "checks.h"
+#include "deoptimizer.h"
+#include "safepoint-table.h"
+#include "scopes.h"
+#include "v8utils.h"
+#include "x64/lithium-gap-resolver-x64.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class LDeferredCode;
+class SafepointGenerator;
+
+class LCodeGen BASE_EMBEDDED {
+ public:
+ LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info)
+ : zone_(info->zone()),
+ chunk_(static_cast<LPlatformChunk*>(chunk)),
+ masm_(assembler),
+ info_(info),
+ current_block_(-1),
+ current_instruction_(-1),
+ instructions_(chunk->instructions()),
+ deoptimizations_(4, info->zone()),
+ jump_table_(4, info->zone()),
+ deoptimization_literals_(8, info->zone()),
+ inlined_function_count_(0),
+ scope_(info->scope()),
+ status_(UNUSED),
+ translations_(info->zone()),
+ deferred_(8, info->zone()),
+ osr_pc_offset_(-1),
+ last_lazy_deopt_pc_(0),
+ frame_is_built_(false),
+ safepoints_(info->zone()),
+ resolver_(this),
+ expected_safepoint_kind_(Safepoint::kSimple),
+ old_position_(RelocInfo::kNoPosition) {
+ 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(); }
+ Zone* zone() const { return zone_; }
+
+ int LookupDestination(int block_id) const {
+ return chunk()->LookupDestination(block_id);
+ }
+
+ bool IsNextEmittedBlock(int block_id) const {
+ return LookupDestination(block_id) == GetNextEmittedBlock();
+ }
+
+ bool NeedsEagerFrame() const {
+ return GetStackSlotCount() > 0 ||
+ info()->is_non_deferred_calling() ||
+ !info()->IsStub() ||
+ info()->requires_frame();
+ }
+ bool NeedsDeferredFrame() const {
+ return !NeedsEagerFrame() && info()->is_deferred_calling();
+ }
+
+ // Support for converting LOperands to assembler types.
+ Register ToRegister(LOperand* op) const;
+ XMMRegister ToDoubleRegister(LOperand* op) const;
+ bool IsInteger32Constant(LConstantOperand* op) const;
+ bool IsSmiConstant(LConstantOperand* op) const;
+ int32_t ToInteger32(LConstantOperand* op) const;
+ Smi* ToSmi(LConstantOperand* op) const;
+ double ToDouble(LConstantOperand* op) const;
+ ExternalReference ToExternalReference(LConstantOperand* op) const;
+ bool IsTaggedConstant(LConstantOperand* op) const;
+ Handle<Object> ToHandle(LConstantOperand* op) const;
+ Operand ToOperand(LOperand* 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();
+
+ // Finish the code by setting stack height, safepoint, and bailout
+ // information on it.
+ void FinishCode(Handle<Code> code);
+
+ // Deferred code support.
+ void DoDeferredNumberTagD(LNumberTagD* instr);
+ void DoDeferredNumberTagU(LNumberTagU* instr);
+ void DoDeferredTaggedToI(LTaggedToI* instr);
+ void DoDeferredMathAbsTaggedHeapNumber(LMathAbs* instr);
+ void DoDeferredStackCheck(LStackCheck* instr);
+ void DoDeferredRandom(LRandom* instr);
+ void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+ void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
+ void DoDeferredAllocate(LAllocate* instr);
+ void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
+ Label* map_check);
+ void DoDeferredInstanceMigration(LCheckMaps* instr, Register object);
+
+// 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;
+ }
+
+ LPlatformChunk* chunk() const { return chunk_; }
+ Scope* scope() const { return scope_; }
+ HGraph* graph() const { return chunk()->graph(); }
+
+ int GetNextEmittedBlock() const;
+
+ void EmitClassOfTest(Label* if_true,
+ Label* if_false,
+ Handle<String> class_name,
+ Register input,
+ Register temporary,
+ Register scratch);
+
+ int GetStackSlotCount() const { return chunk()->spill_slot_count(); }
+
+ void Abort(BailoutReason reason);
+ void FPRINTF_CHECKING Comment(const char* format, ...);
+
+ void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code, zone()); }
+
+ // Code generation passes. Returns true if code generation should
+ // continue.
+ bool GeneratePrologue();
+ bool GenerateBody();
+ bool GenerateDeferredCode();
+ bool GenerateJumpTable();
+ bool GenerateSafepointTable();
+
+ enum SafepointMode {
+ RECORD_SIMPLE_SAFEPOINT,
+ RECORD_SAFEPOINT_WITH_REGISTERS
+ };
+
+ void CallCodeGeneric(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr,
+ SafepointMode safepoint_mode,
+ int argc);
+
+
+ void CallCode(Handle<Code> code,
+ RelocInfo::Mode mode,
+ LInstruction* instr);
+
+ 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);
+
+ enum RDIState {
+ RDI_UNINITIALIZED,
+ RDI_CONTAINS_TARGET
+ };
+
+ // Generate a direct call to a known function. Expects the function
+ // to be in rdi.
+ void CallKnownFunction(Handle<JSFunction> function,
+ int formal_parameter_count,
+ int arity,
+ LInstruction* instr,
+ CallKind call_kind,
+ RDIState rdi_state);
+
+ void RecordSafepointWithLazyDeopt(LInstruction* instr,
+ SafepointMode safepoint_mode,
+ int argc);
+ void RegisterEnvironmentForDeoptimization(LEnvironment* environment,
+ Safepoint::DeoptMode mode);
+ void DeoptimizeIf(Condition cc,
+ LEnvironment* environment,
+ Deoptimizer::BailoutType bailout_type);
+ void DeoptimizeIf(Condition cc, LEnvironment* environment);
+ void ApplyCheckIf(Condition cc, LBoundsCheck* check);
+
+ void AddToTranslation(LEnvironment* environment,
+ Translation* translation,
+ LOperand* op,
+ bool is_tagged,
+ bool is_uint32,
+ int* object_index_pointer,
+ int* dematerialized_index_pointer);
+ void RegisterDependentCodeForEmbeddedMaps(Handle<Code> code);
+ void PopulateDeoptimizationData(Handle<Code> code);
+ int DefineDeoptimizationLiteral(Handle<Object> literal);
+
+ void PopulateDeoptimizationLiteralsWithInlinedFunctions();
+
+ Register ToRegister(int index) const;
+ XMMRegister ToDoubleRegister(int index) const;
+ Operand BuildFastArrayOperand(
+ LOperand* elements_pointer,
+ LOperand* key,
+ ElementsKind elements_kind,
+ uint32_t offset,
+ uint32_t additional_index = 0);
+
+ void EmitIntegerMathAbs(LMathAbs* instr);
+ void EmitSmiMathAbs(LMathAbs* 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 RecordPosition(int position);
+ void RecordAndUpdatePosition(int position);
+
+ static Condition TokenToCondition(Token::Value op, bool is_unsigned);
+ void EmitGoto(int block);
+ template<class InstrType>
+ void EmitBranch(InstrType instr, Condition cc);
+ template<class InstrType>
+ void EmitFalseBranch(InstrType instr, Condition cc);
+ void EmitNumberUntagD(
+ Register input,
+ XMMRegister result,
+ bool allow_undefined_as_nan,
+ bool deoptimize_on_minus_zero,
+ LEnvironment* env,
+ NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED);
+
+ // 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);
+
+ // 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,
+ 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,
+ SmiCheck check_needed);
+
+ // Emits optimized code for %_IsConstructCall().
+ // Caller should branch on equal condition.
+ void EmitIsConstructCall(Register temp);
+
+ // Emits code for pushing either a tagged constant, a (non-double)
+ // 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,
+ AllocationSiteMode mode);
+
+ void EnsureSpaceForLazyDeopt(int space_needed);
+ void DoLoadKeyedExternalArray(LLoadKeyed* instr);
+ void DoLoadKeyedFixedDoubleArray(LLoadKeyed* instr);
+ void DoLoadKeyedFixedArray(LLoadKeyed* instr);
+ void DoStoreKeyedExternalArray(LStoreKeyed* instr);
+ void DoStoreKeyedFixedDoubleArray(LStoreKeyed* instr);
+ void DoStoreKeyedFixedArray(LStoreKeyed* instr);
+#ifdef _MSC_VER
+ // On windows, you may not access the stack more than one page below
+ // the most recently mapped page. To make the allocated area randomly
+ // accessible, we write an arbitrary value to each page in range
+ // rsp + offset - page_size .. rsp in turn.
+ void MakeSureStackPagesMapped(int offset);
+#endif
+
+ Zone* zone_;
+ LPlatformChunk* const chunk_;
+ MacroAssembler* const masm_;
+ CompilationInfo* const info_;
+
+ int current_block_;
+ int current_instruction_;
+ const ZoneList<LInstruction*>* instructions_;
+ ZoneList<LEnvironment*> deoptimizations_;
+ ZoneList<Deoptimizer::JumpTableEntry> 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_;
+ bool frame_is_built_;
+
+ // 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_;
+
+ int old_position_;
+
+ class PushSafepointRegistersScope BASE_EMBEDDED {
+ public:
+ explicit PushSafepointRegistersScope(LCodeGen* codegen)
+ : codegen_(codegen) {
+ ASSERT(codegen_->info()->is_calling());
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kSimple);
+ codegen_->masm_->PushSafepointRegisters();
+ codegen_->expected_safepoint_kind_ = Safepoint::kWithRegisters;
+ }
+
+ ~PushSafepointRegistersScope() {
+ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kWithRegisters);
+ codegen_->masm_->PopSafepointRegisters();
+ 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
+
+#endif // V8_X64_LITHIUM_CODEGEN_X64_H_
diff --git a/chromium/v8/src/x64/lithium-gap-resolver-x64.cc b/chromium/v8/src/x64/lithium-gap-resolver-x64.cc
new file mode 100644
index 00000000000..71db17c9315
--- /dev/null
+++ b/chromium/v8/src/x64/lithium-gap-resolver-x64.cc
@@ -0,0 +1,336 @@
+// 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "x64/lithium-gap-resolver-x64.h"
+#include "x64/lithium-codegen-x64.h"
+
+namespace v8 {
+namespace internal {
+
+LGapResolver::LGapResolver(LCodeGen* owner)
+ : cgen_(owner), moves_(32, owner->zone()) {}
+
+
+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()) {
+ PerformMove(i);
+ }
+ }
+
+ // 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, cgen_->zone());
+ }
+ 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 use operand swaps to resolve cycles,
+ // which means that a call to PerformMove could change any source operand
+ // in the move graph.
+
+ 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. Recursion may allow
+ // multiple moves to be pending.
+ 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()) {
+ // Though PerformMove can change any source operand in the move graph,
+ // this call cannot create a blocking move via a swap (this loop does
+ // not miss any). Assume there is a non-blocking move with source A
+ // and this move is blocked on source B and there is a swap of A and
+ // B. Then A and B must be involved in the same cycle (or they would
+ // not be swapped). Since this move's destination is B and there is
+ // only a single incoming edge to an operand, this move must also be
+ // involved in the same cycle. In that case, the blocking move will
+ // be created but will be "pending" when we return from PerformMove.
+ PerformMove(i);
+ }
+ }
+
+ // We are about to resolve this move and don't need it marked as
+ // pending, so restore its destination.
+ moves_[index].set_destination(destination);
+
+ // This move's source may have changed due to swaps to resolve cycles and
+ // so it may now be the last move in the cycle. If so remove it.
+ if (moves_[index].source()->Equals(destination)) {
+ moves_[index].Eliminate();
+ return;
+ }
+
+ // The move may be blocked on a (at most one) pending move, in which case
+ // we have a cycle. Search for such a blocking move and perform a swap to
+ // resolve it.
+ for (int i = 0; i < moves_.length(); ++i) {
+ LMoveOperands other_move = moves_[i];
+ if (other_move.Blocks(destination)) {
+ ASSERT(other_move.IsPending());
+ EmitSwap(index);
+ return;
+ }
+ }
+
+ // This move is not 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::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 src = cgen_->ToRegister(source);
+ if (destination->IsRegister()) {
+ Register dst = cgen_->ToRegister(destination);
+ __ movq(dst, src);
+ } else {
+ ASSERT(destination->IsStackSlot());
+ Operand dst = cgen_->ToOperand(destination);
+ __ movq(dst, src);
+ }
+
+ } else if (source->IsStackSlot()) {
+ Operand src = cgen_->ToOperand(source);
+ if (destination->IsRegister()) {
+ Register dst = cgen_->ToRegister(destination);
+ __ movq(dst, src);
+ } else {
+ ASSERT(destination->IsStackSlot());
+ Operand dst = cgen_->ToOperand(destination);
+ __ movq(kScratchRegister, src);
+ __ movq(dst, kScratchRegister);
+ }
+
+ } else if (source->IsConstantOperand()) {
+ LConstantOperand* constant_source = LConstantOperand::cast(source);
+ if (destination->IsRegister()) {
+ Register dst = cgen_->ToRegister(destination);
+ if (cgen_->IsSmiConstant(constant_source)) {
+ __ Move(dst, cgen_->ToSmi(constant_source));
+ } else if (cgen_->IsInteger32Constant(constant_source)) {
+ __ movl(dst, Immediate(cgen_->ToInteger32(constant_source)));
+ } else {
+ __ LoadObject(dst, cgen_->ToHandle(constant_source));
+ }
+ } else if (destination->IsDoubleRegister()) {
+ double v = cgen_->ToDouble(constant_source);
+ uint64_t int_val = BitCast<uint64_t, double>(v);
+ XMMRegister dst = cgen_->ToDoubleRegister(destination);
+ if (int_val == 0) {
+ __ xorps(dst, dst);
+ } else {
+ __ movq(kScratchRegister, int_val, RelocInfo::NONE64);
+ __ movq(dst, kScratchRegister);
+ }
+ } else {
+ ASSERT(destination->IsStackSlot());
+ Operand dst = cgen_->ToOperand(destination);
+ if (cgen_->IsSmiConstant(constant_source)) {
+ __ Move(dst, cgen_->ToSmi(constant_source));
+ } else if (cgen_->IsInteger32Constant(constant_source)) {
+ // Zero top 32 bits of a 64 bit spill slot that holds a 32 bit untagged
+ // value.
+ __ movq(dst, Immediate(cgen_->ToInteger32(constant_source)));
+ } else {
+ __ LoadObject(kScratchRegister, cgen_->ToHandle(constant_source));
+ __ movq(dst, kScratchRegister);
+ }
+ }
+
+ } else if (source->IsDoubleRegister()) {
+ XMMRegister src = cgen_->ToDoubleRegister(source);
+ if (destination->IsDoubleRegister()) {
+ __ movaps(cgen_->ToDoubleRegister(destination), src);
+ } else {
+ ASSERT(destination->IsDoubleStackSlot());
+ __ movsd(cgen_->ToOperand(destination), src);
+ }
+ } else if (source->IsDoubleStackSlot()) {
+ Operand src = cgen_->ToOperand(source);
+ if (destination->IsDoubleRegister()) {
+ __ movsd(cgen_->ToDoubleRegister(destination), src);
+ } else {
+ ASSERT(destination->IsDoubleStackSlot());
+ __ movsd(xmm0, src);
+ __ movsd(cgen_->ToOperand(destination), xmm0);
+ }
+ } else {
+ UNREACHABLE();
+ }
+
+ moves_[index].Eliminate();
+}
+
+
+void LGapResolver::EmitSwap(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() && destination->IsRegister()) {
+ // Swap two general-purpose registers.
+ Register src = cgen_->ToRegister(source);
+ Register dst = cgen_->ToRegister(destination);
+ __ xchg(dst, src);
+
+ } else if ((source->IsRegister() && destination->IsStackSlot()) ||
+ (source->IsStackSlot() && destination->IsRegister())) {
+ // Swap a general-purpose register and a stack slot.
+ Register reg =
+ cgen_->ToRegister(source->IsRegister() ? source : destination);
+ Operand mem =
+ cgen_->ToOperand(source->IsRegister() ? destination : source);
+ __ movq(kScratchRegister, mem);
+ __ movq(mem, reg);
+ __ movq(reg, kScratchRegister);
+
+ } else if ((source->IsStackSlot() && destination->IsStackSlot()) ||
+ (source->IsDoubleStackSlot() && destination->IsDoubleStackSlot())) {
+ // Swap two stack slots or two double stack slots.
+ Operand src = cgen_->ToOperand(source);
+ Operand dst = cgen_->ToOperand(destination);
+ __ movsd(xmm0, src);
+ __ movq(kScratchRegister, dst);
+ __ movsd(dst, xmm0);
+ __ movq(src, kScratchRegister);
+
+ } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
+ // Swap two double registers.
+ XMMRegister source_reg = cgen_->ToDoubleRegister(source);
+ XMMRegister destination_reg = cgen_->ToDoubleRegister(destination);
+ __ movaps(xmm0, source_reg);
+ __ movaps(source_reg, destination_reg);
+ __ movaps(destination_reg, xmm0);
+
+ } else if (source->IsDoubleRegister() || destination->IsDoubleRegister()) {
+ // Swap a double register and a double stack slot.
+ ASSERT((source->IsDoubleRegister() && destination->IsDoubleStackSlot()) ||
+ (source->IsDoubleStackSlot() && destination->IsDoubleRegister()));
+ XMMRegister reg = cgen_->ToDoubleRegister(source->IsDoubleRegister()
+ ? source
+ : destination);
+ LOperand* other = source->IsDoubleRegister() ? destination : source;
+ ASSERT(other->IsDoubleStackSlot());
+ Operand other_operand = cgen_->ToOperand(other);
+ __ movsd(xmm0, other_operand);
+ __ movsd(other_operand, reg);
+ __ movsd(reg, xmm0);
+
+ } else {
+ // No other combinations are possible.
+ UNREACHABLE();
+ }
+
+ // The swap of source and destination has executed a move from source to
+ // destination.
+ moves_[index].Eliminate();
+
+ // Any unperformed (including pending) move with a source of either
+ // this move's source or destination needs to have their source
+ // changed to reflect the state of affairs after the swap.
+ for (int i = 0; i < moves_.length(); ++i) {
+ LMoveOperands other_move = moves_[i];
+ if (other_move.Blocks(source)) {
+ moves_[i].set_source(destination);
+ } else if (other_move.Blocks(destination)) {
+ moves_[i].set_source(source);
+ }
+ }
+}
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/lithium-gap-resolver-x64.h b/chromium/v8/src/x64/lithium-gap-resolver-x64.h
new file mode 100644
index 00000000000..d828455921a
--- /dev/null
+++ b/chromium/v8/src/x64/lithium-gap-resolver-x64.h
@@ -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.
+
+#ifndef V8_X64_LITHIUM_GAP_RESOLVER_X64_H_
+#define V8_X64_LITHIUM_GAP_RESOLVER_X64_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);
+
+ // Emit a move and remove it from the move graph.
+ void EmitMove(int index);
+
+ // Execute a move by emitting a swap of two operands. The move from
+ // source to destination is removed from the move graph.
+ void EmitSwap(int index);
+
+ // Verify the move list before performing moves.
+ void Verify();
+
+ LCodeGen* cgen_;
+
+ // List of moves not yet resolved.
+ ZoneList<LMoveOperands> moves_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_X64_LITHIUM_GAP_RESOLVER_X64_H_
diff --git a/chromium/v8/src/x64/lithium-x64.cc b/chromium/v8/src/x64/lithium-x64.cc
new file mode 100644
index 00000000000..c058b0df44a
--- /dev/null
+++ b/chromium/v8/src/x64/lithium-x64.cc
@@ -0,0 +1,2541 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "lithium-allocator-inl.h"
+#include "x64/lithium-x64.h"
+#include "x64/lithium-codegen-x64.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
+
+
+#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 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);
+ }
+}
+
+
+void LInstruction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ for (int i = 0; i < InputCount(); i++) {
+ if (i > 0) stream->Add(" ");
+ if (InputAt(i) == NULL) {
+ stream->Add("NULL");
+ } else {
+ InputAt(i)->PrintTo(stream);
+ }
+ }
+}
+
+
+void LInstruction::PrintOutputOperandTo(StringStream* stream) {
+ if (HasResult()) result()->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::ROR: return "ror-t";
+ case Token::SHL: return "sal-t";
+ case Token::SAR: return "sar-t";
+ case Token::SHR: return "shr-t";
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+bool LGoto::HasInterestingComment(LCodeGen* gen) const {
+ return !gen->IsNextEmittedBlock(block_id());
+}
+
+
+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());
+ value()->PrintTo(stream);
+}
+
+
+void LCompareNumericAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if ");
+ left()->PrintTo(stream);
+ stream->Add(" %s ", Token::String(op()));
+ right()->PrintTo(stream);
+ stream->Add(" then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
+void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_object(");
+ value()->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(");
+ value()->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(");
+ value()->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(");
+ value()->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(");
+ left()->PrintTo(stream);
+ right()->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(");
+ value()->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(");
+ value()->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(");
+ value()->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 ");
+ value()->PrintTo(stream);
+ stream->Add(" == \"%s\" then B%d else B%d",
+ *hydrogen()->type_literal()->ToCString(),
+ true_block_id(), false_block_id());
+}
+
+
+void LInnerAllocatedObject::PrintDataTo(StringStream* stream) {
+ stream->Add(" = ");
+ base_object()->PrintTo(stream);
+ stream->Add(" + %d", offset());
+}
+
+
+void LCallConstantFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("#%d / ", arity());
+}
+
+
+void LLoadContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintTo(stream);
+ stream->Add("[%d]", slot_index());
+}
+
+
+void LStoreContextSlot::PrintDataTo(StringStream* stream) {
+ context()->PrintTo(stream);
+ stream->Add("[%d] <- ", slot_index());
+ value()->PrintTo(stream);
+}
+
+
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ function()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
+void LCallKeyed::PrintDataTo(StringStream* stream) {
+ stream->Add("[rcx] #%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("= ");
+ constructor()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
+void LCallNewArray::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ constructor()->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+ ElementsKind kind = hydrogen()->elements_kind();
+ stream->Add(" (%s) ", ElementsKindToString(kind));
+}
+
+
+void LAccessArgumentsAt::PrintDataTo(StringStream* stream) {
+ arguments()->PrintTo(stream);
+
+ stream->Add(" length ");
+ length()->PrintTo(stream);
+
+ stream->Add(" index ");
+ index()->PrintTo(stream);
+}
+
+
+int LPlatformChunk::GetNextSpillIndex(bool is_double) {
+ return spill_slot_count_++;
+}
+
+
+LOperand* LPlatformChunk::GetNextSpillSlot(bool is_double) {
+ // All stack slots are Double stack slots on x64.
+ // Alternatively, at some point, start using half-size
+ // stack slots for int32 values.
+ int index = GetNextSpillIndex(is_double);
+ if (is_double) {
+ return LDoubleStackSlot::Create(index, zone());
+ } else {
+ return LStackSlot::Create(index, zone());
+ }
+}
+
+
+void LStoreNamedField::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ hydrogen()->access().PrintTo(stream);
+ 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 LLoadKeyed::PrintDataTo(StringStream* stream) {
+ elements()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ if (hydrogen()->IsDehoisted()) {
+ stream->Add(" + %d]", additional_index());
+ } else {
+ stream->Add("]");
+ }
+}
+
+
+void LStoreKeyed::PrintDataTo(StringStream* stream) {
+ elements()->PrintTo(stream);
+ stream->Add("[");
+ key()->PrintTo(stream);
+ if (hydrogen()->IsDehoisted()) {
+ stream->Add(" + %d] <-", additional_index());
+ } else {
+ stream->Add("] <- ");
+ }
+
+ if (value() == NULL) {
+ ASSERT(hydrogen()->IsConstantHoleStore() &&
+ hydrogen()->value()->representation().IsDouble());
+ stream->Add("<the hole(nan)>");
+ } else {
+ 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());
+}
+
+
+LPlatformChunk* LChunkBuilder::Build() {
+ ASSERT(is_unused());
+ chunk_ = new(zone()) LPlatformChunk(info(), graph());
+ LPhase phase("L_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 LCodeGen::Abort(BailoutReason reason) {
+ info()->set_bailout_reason(reason);
+ status_ = ABORTED;
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(Register reg) {
+ return new(zone()) LUnallocated(LUnallocated::FIXED_REGISTER,
+ Register::ToAllocationIndex(reg));
+}
+
+
+LUnallocated* LChunkBuilder::ToUnallocated(XMMRegister reg) {
+ return new(zone()) LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER,
+ XMMRegister::ToAllocationIndex(reg));
+}
+
+
+LOperand* LChunkBuilder::UseFixed(HValue* value, Register fixed_register) {
+ return Use(value, ToUnallocated(fixed_register));
+}
+
+
+LOperand* LChunkBuilder::UseFixedDouble(HValue* value, XMMRegister reg) {
+ return Use(value, ToUnallocated(reg));
+}
+
+
+LOperand* LChunkBuilder::UseRegister(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) {
+ return Use(value,
+ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER,
+ LUnallocated::USED_AT_START));
+}
+
+
+LOperand* LChunkBuilder::UseTempRegister(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::WRITABLE_REGISTER));
+}
+
+
+LOperand* LChunkBuilder::Use(HValue* value) {
+ return Use(value, new(zone()) LUnallocated(LUnallocated::NONE));
+}
+
+
+LOperand* LChunkBuilder::UseAtStart(HValue* value) {
+ return Use(value, new(zone()) 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::UseConstant(HValue* value) {
+ return chunk_->DefineConstantOperand(HConstant::cast(value));
+}
+
+
+LOperand* LChunkBuilder::UseAny(HValue* value) {
+ return value->IsConstant()
+ ? chunk_->DefineConstantOperand(HConstant::cast(value))
+ : Use(value, new(zone()) 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(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
+}
+
+
+template<int I, int T>
+LInstruction* LChunkBuilder::DefineAsSpilled(
+ LTemplateInstruction<1, I, T>* instr,
+ int 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(zone()) 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,
+ XMMRegister reg) {
+ return Define(instr, ToUnallocated(reg));
+}
+
+
+LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
+ HEnvironment* hydrogen_env = current_block_->last_environment();
+ int argument_index_accumulator = 0;
+ ZoneList<HValue*> objects_to_materialize(0, zone());
+ instr->set_environment(CreateEnvironment(hydrogen_env,
+ &argument_index_accumulator,
+ &objects_to_materialize));
+ return instr;
+}
+
+
+LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
+ HInstruction* hinstr,
+ CanDeoptimize can_deoptimize) {
+ info()->MarkAsNonDeferredCalling();
+
+#ifdef DEBUG
+ instr->VerifyCall();
+#endif
+ instr->MarkAsCall();
+ instr = AssignPointerMap(instr);
+
+ if (hinstr->HasObservableSideEffects()) {
+ ASSERT(hinstr->next()->IsSimulate());
+ HSimulate* sim = HSimulate::cast(hinstr->next());
+ ASSERT(instruction_pending_deoptimization_environment_ == NULL);
+ ASSERT(pending_deoptimization_ast_id_.IsNone());
+ instruction_pending_deoptimization_environment_ = instr;
+ pending_deoptimization_ast_id_ = 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::AssignPointerMap(LInstruction* instr) {
+ ASSERT(!instr->HasPointerMap());
+ instr->set_pointer_map(new(zone()) LPointerMap(position_, zone()));
+ return instr;
+}
+
+
+LUnallocated* LChunkBuilder::TempRegister() {
+ LUnallocated* operand =
+ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER);
+ int vreg = allocator_->GetVirtualRegister();
+ if (!allocator_->AllocationOk()) {
+ Abort(kOutOfVirtualRegistersWhileTryingToAllocateTempRegister);
+ vreg = 0;
+ }
+ operand->set_virtual_register(vreg);
+ return operand;
+}
+
+
+LOperand* LChunkBuilder::FixedTemp(Register reg) {
+ LUnallocated* operand = ToUnallocated(reg);
+ ASSERT(operand->HasFixedPolicy());
+ return operand;
+}
+
+
+LOperand* LChunkBuilder::FixedTemp(XMMRegister reg) {
+ LUnallocated* operand = ToUnallocated(reg);
+ ASSERT(operand->HasFixedPolicy());
+ return operand;
+}
+
+
+LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) {
+ return new(zone()) LLabel(instr->block());
+}
+
+
+LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
+ return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value())));
+}
+
+
+LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
+ return AssignEnvironment(new(zone()) 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(), rdx);
+ LOperand* right = UseFixed(instr->right(), rax);
+ LArithmeticT* result = new(zone()) LArithmeticT(op, left, right);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+ }
+
+ ASSERT(instr->representation().IsSmiOrInteger32());
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ 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 = UseFixed(right_value, rcx);
+ }
+
+ // Shift operations can only deoptimize if we do a logical shift by 0 and
+ // the result cannot be truncated to int32.
+ bool does_deopt = false;
+ if (op == Token::SHR && constant_value == 0) {
+ if (FLAG_opt_safe_uint32_operations) {
+ does_deopt = !instr->CheckFlag(HInstruction::kUint32);
+ } else {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
+ does_deopt = true;
+ break;
+ }
+ }
+ }
+ }
+
+ LInstruction* result =
+ DefineSameAsFirst(new(zone()) 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->BetterLeftOperand());
+ LOperand* right = UseRegisterAtStart(instr->BetterRightOperand());
+ LArithmeticD* result = new(zone()) LArithmeticD(op, left, right);
+ return DefineSameAsFirst(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, rdx);
+ LOperand* right_operand = UseFixed(right, rax);
+ LArithmeticT* result =
+ new(zone()) LArithmeticT(op, left_operand, right_operand);
+ return MarkAsCall(DefineFixed(result, rax), 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);
+ if (phi->HasMergedIndex()) {
+ last_environment->SetValueAt(phi->merged_index(), phi);
+ }
+ }
+ for (int i = 0; i < block->deleted_phis()->length(); ++i) {
+ if (block->deleted_phis()->at(i) < last_environment->length()) {
+ 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 DEBUG
+ // Make sure that the lithium instruction has either no fixed register
+ // constraints in temps or the result OR no uses that are only used at
+ // start. If this invariant doesn't hold, the register allocator can decide
+ // to insert a split of a range immediately before the instruction due to an
+ // already allocated register needing to be used for the instruction's fixed
+ // register constraint. In this case, The register allocator won't see an
+ // interference between the split child and the use-at-start (it would if
+ // the it was just a plain use), so it is free to move the split child into
+ // the same register that is used for the use-at-start.
+ // See https://code.google.com/p/chromium/issues/detail?id=201590
+ if (!(instr->ClobbersRegisters() && instr->ClobbersDoubleRegisters())) {
+ int fixed = 0;
+ int used_at_start = 0;
+ for (UseIterator it(instr); !it.Done(); it.Advance()) {
+ LUnallocated* operand = LUnallocated::cast(it.Current());
+ if (operand->IsUsedAtStart()) ++used_at_start;
+ }
+ if (instr->Output() != NULL) {
+ if (LUnallocated::cast(instr->Output())->HasFixedPolicy()) ++fixed;
+ }
+ for (TempIterator it(instr); !it.Done(); it.Advance()) {
+ LUnallocated* operand = LUnallocated::cast(it.Current());
+ if (operand->HasFixedPolicy()) ++fixed;
+ }
+ ASSERT(fixed == 0 || used_at_start == 0);
+ }
+#endif
+
+ instr->set_position(position_);
+ 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,
+ ZoneList<HValue*>* objects_to_materialize) {
+ if (hydrogen_env == NULL) return NULL;
+
+ LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(),
+ argument_index_accumulator,
+ objects_to_materialize);
+ BailoutId ast_id = hydrogen_env->ast_id();
+ ASSERT(!ast_id.IsNone() ||
+ hydrogen_env->frame_type() != JS_FUNCTION);
+ int value_count = hydrogen_env->length() - hydrogen_env->specials_count();
+ LEnvironment* result = new(zone()) LEnvironment(
+ hydrogen_env->closure(),
+ hydrogen_env->frame_type(),
+ ast_id,
+ hydrogen_env->parameter_count(),
+ argument_count_,
+ value_count,
+ outer,
+ hydrogen_env->entry(),
+ zone());
+ int argument_index = *argument_index_accumulator;
+ int object_index = objects_to_materialize->length();
+ for (int i = 0; i < hydrogen_env->length(); ++i) {
+ if (hydrogen_env->is_special_index(i)) continue;
+
+ LOperand* op;
+ HValue* value = hydrogen_env->values()->at(i);
+ if (value->IsArgumentsObject() || value->IsCapturedObject()) {
+ objects_to_materialize->Add(value, zone());
+ op = LEnvironment::materialization_marker();
+ } else if (value->IsPushArgument()) {
+ op = new(zone()) LArgument(argument_index++);
+ } else {
+ op = UseAny(value);
+ }
+ result->AddValue(op,
+ value->representation(),
+ value->CheckFlag(HInstruction::kUint32));
+ }
+
+ for (int i = object_index; i < objects_to_materialize->length(); ++i) {
+ HValue* object_to_materialize = objects_to_materialize->at(i);
+ int previously_materialized_object = -1;
+ for (int prev = 0; prev < i; ++prev) {
+ if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) {
+ previously_materialized_object = prev;
+ break;
+ }
+ }
+ int length = object_to_materialize->OperandCount();
+ bool is_arguments = object_to_materialize->IsArgumentsObject();
+ if (previously_materialized_object >= 0) {
+ result->AddDuplicateObject(previously_materialized_object);
+ continue;
+ } else {
+ result->AddNewObject(is_arguments ? length - 1 : length, is_arguments);
+ }
+ for (int i = is_arguments ? 1 : 0; i < length; ++i) {
+ LOperand* op;
+ HValue* value = object_to_materialize->OperandAt(i);
+ if (value->IsArgumentsObject() || value->IsCapturedObject()) {
+ objects_to_materialize->Add(value, zone());
+ op = LEnvironment::materialization_marker();
+ } else {
+ ASSERT(!value->IsPushArgument());
+ op = UseAny(value);
+ }
+ result->AddValue(op,
+ value->representation(),
+ value->CheckFlag(HInstruction::kUint32));
+ }
+ }
+
+ if (hydrogen_env->frame_type() == JS_FUNCTION) {
+ *argument_index_accumulator = argument_index;
+ }
+
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
+ return new(zone()) LGoto(instr->FirstSuccessor()->block_id());
+}
+
+
+LInstruction* LChunkBuilder::DoDebugBreak(HDebugBreak* instr) {
+ return new(zone()) LDebugBreak();
+}
+
+
+LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
+ HValue* value = instr->value();
+ if (value->EmitAtUses()) {
+ ASSERT(value->IsConstant());
+ ASSERT(!value->representation().IsDouble());
+ HBasicBlock* successor = HConstant::cast(value)->BooleanValue()
+ ? instr->FirstSuccessor()
+ : instr->SecondSuccessor();
+ return new(zone()) LGoto(successor->block_id());
+ }
+
+ LBranch* result = new(zone()) LBranch(UseRegister(value));
+ // Tagged values that are not known smis or booleans require a
+ // deoptimization environment. If the instruction is generic no
+ // environment is needed since all cases are handled.
+ ToBooleanStub::Types expected = instr->expected_input_types();
+ Representation rep = value->representation();
+ HType type = value->type();
+ if (rep.IsTagged() && !type.IsSmi() && !type.IsBoolean() &&
+ !expected.IsGeneric()) {
+ return AssignEnvironment(result);
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoCompareMap(HCompareMap* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return new(zone()) LCmpMapAndBranch(value);
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* length) {
+ info()->MarkAsRequiresFrame();
+ return DefineAsRegister(new(zone()) LArgumentsLength(Use(length->value())));
+}
+
+
+LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
+ info()->MarkAsRequiresFrame();
+ return DefineAsRegister(new(zone()) LArgumentsElements);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
+ LOperand* left = UseFixed(instr->left(), rax);
+ LOperand* right = UseFixed(instr->right(), rdx);
+ LInstanceOf* result = new(zone()) LInstanceOf(left, right);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
+ HInstanceOfKnownGlobal* instr) {
+ LInstanceOfKnownGlobal* result =
+ new(zone()) LInstanceOfKnownGlobal(UseFixed(instr->left(), rax),
+ FixedTemp(rdi));
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInstanceSize(HInstanceSize* instr) {
+ LOperand* object = UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new(zone()) LInstanceSize(object));
+}
+
+
+LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) {
+ LOperand* receiver = UseRegister(instr->receiver());
+ LOperand* function = UseRegisterAtStart(instr->function());
+ LWrapReceiver* result = new(zone()) LWrapReceiver(receiver, function);
+ return AssignEnvironment(DefineSameAsFirst(result));
+}
+
+
+LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) {
+ LOperand* function = UseFixed(instr->function(), rdi);
+ LOperand* receiver = UseFixed(instr->receiver(), rax);
+ LOperand* length = UseFixed(instr->length(), rbx);
+ LOperand* elements = UseFixed(instr->elements(), rcx);
+ LApplyArguments* result = new(zone()) LApplyArguments(function,
+ receiver,
+ length,
+ elements);
+ return MarkAsCall(DefineFixed(result, rax), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
+ ++argument_count_;
+ LOperand* argument = UseOrConstant(instr->argument());
+ return new(zone()) LPushArgument(argument);
+}
+
+
+LInstruction* LChunkBuilder::DoInnerAllocatedObject(
+ HInnerAllocatedObject* inner_object) {
+ LOperand* base_object = UseRegisterAtStart(inner_object->base_object());
+ LInnerAllocatedObject* result =
+ new(zone()) LInnerAllocatedObject(base_object);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoThisFunction(HThisFunction* instr) {
+ return instr->HasNoUses()
+ ? NULL
+ : DefineAsRegister(new(zone()) LThisFunction);
+}
+
+
+LInstruction* LChunkBuilder::DoContext(HContext* instr) {
+ // If there is a non-return use, the context must be allocated in a register.
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->IsReturn()) {
+ return DefineAsRegister(new(zone()) LContext);
+ }
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LOuterContext(context));
+}
+
+
+LInstruction* LChunkBuilder::DoDeclareGlobals(HDeclareGlobals* instr) {
+ return MarkAsCall(new(zone()) LDeclareGlobals, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) {
+ return DefineAsRegister(new(zone()) LGlobalObject);
+}
+
+
+LInstruction* LChunkBuilder::DoGlobalReceiver(HGlobalReceiver* instr) {
+ LOperand* global_object = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LGlobalReceiver(global_object));
+}
+
+
+LInstruction* LChunkBuilder::DoCallConstantFunction(
+ HCallConstantFunction* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallConstantFunction, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), rdi);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new(zone()) LInvokeFunction(function);
+ return MarkAsCall(DefineFixed(result, rax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
+ switch (instr->op()) {
+ case kMathFloor: return DoMathFloor(instr);
+ case kMathRound: return DoMathRound(instr);
+ case kMathAbs: return DoMathAbs(instr);
+ case kMathLog: return DoMathLog(instr);
+ case kMathSin: return DoMathSin(instr);
+ case kMathCos: return DoMathCos(instr);
+ case kMathTan: return DoMathTan(instr);
+ case kMathExp: return DoMathExp(instr);
+ case kMathSqrt: return DoMathSqrt(instr);
+ case kMathPowHalf: return DoMathPowHalf(instr);
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMathFloor(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathFloor* result = new(zone()) LMathFloor(input);
+ return AssignEnvironment(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoMathRound(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathRound* result = new(zone()) LMathRound(input);
+ return AssignEnvironment(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoMathAbs(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathAbs* result = new(zone()) LMathAbs(input);
+ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoMathLog(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), xmm1);
+ LMathLog* result = new(zone()) LMathLog(input);
+ return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathSin(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), xmm1);
+ LMathSin* result = new(zone()) LMathSin(input);
+ return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathCos(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), xmm1);
+ LMathCos* result = new(zone()) LMathCos(input);
+ return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathTan(HUnaryMathOperation* instr) {
+ LOperand* input = UseFixedDouble(instr->value(), xmm1);
+ LMathTan* result = new(zone()) LMathTan(input);
+ return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoMathExp(HUnaryMathOperation* instr) {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->value()->representation().IsDouble());
+ LOperand* value = UseTempRegister(instr->value());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ LMathExp* result = new(zone()) LMathExp(value, temp1, temp2);
+ return DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathSqrt(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathSqrt* result = new(zone()) LMathSqrt(input);
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoMathPowHalf(HUnaryMathOperation* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ LMathPowHalf* result = new(zone()) LMathPowHalf(input);
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) {
+ ASSERT(instr->key()->representation().IsTagged());
+ LOperand* key = UseFixed(instr->key(), rcx);
+ argument_count_ -= instr->argument_count();
+ LCallKeyed* result = new(zone()) LCallKeyed(key);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallNamed, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallGlobal(HCallGlobal* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallGlobal, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallKnownGlobal(HCallKnownGlobal* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallKnownGlobal, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
+ LOperand* constructor = UseFixed(instr->constructor(), rdi);
+ argument_count_ -= instr->argument_count();
+ LCallNew* result = new(zone()) LCallNew(constructor);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) {
+ LOperand* constructor = UseFixed(instr->constructor(), rdi);
+ argument_count_ -= instr->argument_count();
+ LCallNewArray* result = new(zone()) LCallNewArray(constructor);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), rdi);
+ argument_count_ -= instr->argument_count();
+ LCallFunction* result = new(zone()) LCallFunction(function);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallRuntime, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoRor(HRor* instr) {
+ return DoShift(Token::ROR, 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().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ LOperand* right = UseOrConstantAtStart(instr->BetterRightOperand());
+ return DefineSameAsFirst(new(zone()) 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(zone()) LArithmeticT(instr->op(), left, right);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
+ if (instr->representation().IsDouble()) {
+ return DoArithmeticD(Token::DIV, instr);
+ } else if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+ LOperand* value = UseRegisterAtStart(instr->left());
+ LDivI* div =
+ new(zone()) LDivI(value, UseOrConstant(instr->right()), NULL);
+ return AssignEnvironment(DefineSameAsFirst(div));
+ }
+ // The temporary operand is necessary to ensure that right is not allocated
+ // into rdx.
+ LOperand* temp = FixedTemp(rdx);
+ LOperand* dividend = UseFixed(instr->left(), rax);
+ LOperand* divisor = UseRegister(instr->right());
+ LDivI* result = new(zone()) LDivI(dividend, divisor, temp);
+ return AssignEnvironment(DefineFixed(result, rax));
+ } else {
+ ASSERT(instr->representation().IsTagged());
+ return DoArithmeticT(Token::DIV, instr);
+ }
+}
+
+
+HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
+ if (divisor->IsConstant() &&
+ HConstant::cast(divisor)->HasInteger32Value()) {
+ HConstant* constant_val = HConstant::cast(divisor);
+ return constant_val->CopyToRepresentation(Representation::Integer32(),
+ divisor->block()->zone());
+ }
+ // A value with an integer representation does not need to be transformed.
+ if (divisor->representation().IsInteger32()) {
+ return divisor;
+ // A change from an integer32 can be replaced by the integer32 value.
+ } else if (divisor->IsChange() &&
+ HChange::cast(divisor)->from().IsInteger32()) {
+ return HChange::cast(divisor)->value();
+ }
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
+ HValue* right = instr->right();
+ if (!right->IsConstant()) {
+ ASSERT(right->representation().IsInteger32());
+ // The temporary operand is necessary to ensure that right is not allocated
+ // into rdx.
+ LOperand* temp = FixedTemp(rdx);
+ LOperand* dividend = UseFixed(instr->left(), rax);
+ LOperand* divisor = UseRegister(instr->right());
+ LDivI* flooring_div = new(zone()) LDivI(dividend, divisor, temp);
+ return AssignEnvironment(DefineFixed(flooring_div, rax));
+ }
+
+ ASSERT(right->IsConstant() && HConstant::cast(right)->HasInteger32Value());
+ LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right));
+ int32_t divisor_si = HConstant::cast(right)->Integer32Value();
+ if (divisor_si == 0) {
+ LOperand* dividend = UseRegister(instr->left());
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LMathFloorOfDiv(dividend, divisor, NULL)));
+ } else if (IsPowerOf2(abs(divisor_si))) {
+ LOperand* dividend = UseRegisterAtStart(instr->left());
+ LInstruction* result = DefineAsRegister(
+ new(zone()) LMathFloorOfDiv(dividend, divisor, NULL));
+ return divisor_si < 0 ? AssignEnvironment(result) : result;
+ } else {
+ // use two r64
+ LOperand* dividend = UseRegisterAtStart(instr->left());
+ LOperand* temp = TempRegister();
+ LInstruction* result = DefineAsRegister(
+ new(zone()) LMathFloorOfDiv(dividend, divisor, temp));
+ return divisor_si < 0 ? AssignEnvironment(result) : result;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMod(HMod* instr) {
+ HValue* left = instr->left();
+ HValue* right = instr->right();
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(left->representation().Equals(instr->representation()));
+ ASSERT(right->representation().Equals(instr->representation()));
+ if (instr->HasPowerOf2Divisor()) {
+ ASSERT(!right->CanBeZero());
+ LModI* mod = new(zone()) LModI(UseRegisterAtStart(left),
+ UseOrConstant(right),
+ NULL);
+ LInstruction* result = DefineSameAsFirst(mod);
+ return (left->CanBeNegative() &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero))
+ ? AssignEnvironment(result)
+ : result;
+ } else if (instr->fixed_right_arg().has_value) {
+ LModI* mod = new(zone()) LModI(UseRegister(left),
+ UseRegisterAtStart(right),
+ NULL);
+ return AssignEnvironment(DefineSameAsFirst(mod));
+ } else {
+ // The temporary operand is necessary to ensure that right is not
+ // allocated into edx.
+ LModI* mod = new(zone()) LModI(UseFixed(left, rax),
+ UseRegister(right),
+ FixedTemp(rdx));
+ LInstruction* result = DefineFixed(mod, rdx);
+ return (right->CanBeZero() ||
+ (left->RangeCanInclude(kMinInt) &&
+ right->RangeCanInclude(-1) &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)) ||
+ (left->CanBeNegative() &&
+ instr->CanBeZero() &&
+ instr->CheckFlag(HValue::kBailoutOnMinusZero)))
+ ? AssignEnvironment(result)
+ : result;
+ }
+ } 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.
+ LArithmeticD* mod = new(zone()) LArithmeticD(Token::MOD,
+ UseFixedDouble(left, xmm2),
+ UseFixedDouble(right, xmm1));
+ return MarkAsCall(DefineFixedDouble(mod, xmm1), instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoMul(HMul* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ LOperand* right = UseOrConstant(instr->BetterRightOperand());
+ LMulI* mul = new(zone()) LMulI(left, right);
+ 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 {
+ ASSERT(instr->representation().IsTagged());
+ return DoArithmeticT(Token::MUL, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoSub(HSub* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ LSubI* sub = new(zone()) LSubI(left, right);
+ LInstruction* result = DefineSameAsFirst(sub);
+ if (instr->CheckFlag(HValue::kCanOverflow)) {
+ result = AssignEnvironment(result);
+ }
+ return result;
+ } else if (instr->representation().IsDouble()) {
+ return DoArithmeticD(Token::SUB, instr);
+ } else {
+ ASSERT(instr->representation().IsTagged());
+ return DoArithmeticT(Token::SUB, instr);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
+ if (instr->representation().IsSmiOrInteger32()) {
+ // Check to see if it would be advantageous to use an lea instruction rather
+ // than an add. This is the case when no overflow check is needed and there
+ // are multiple uses of the add's inputs, so using a 3-register add will
+ // preserve all input values for later uses.
+ bool use_lea = LAddI::UseLea(instr);
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand());
+ HValue* right_candidate = instr->BetterRightOperand();
+ LOperand* right = use_lea
+ ? UseRegisterOrConstantAtStart(right_candidate)
+ : UseOrConstantAtStart(right_candidate);
+ LAddI* add = new(zone()) LAddI(left, right);
+ bool can_overflow = instr->CheckFlag(HValue::kCanOverflow);
+ LInstruction* result = use_lea
+ ? DefineAsRegister(add)
+ : DefineSameAsFirst(add);
+ if (can_overflow) {
+ 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);
+ }
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) {
+ LOperand* left = NULL;
+ LOperand* right = NULL;
+ if (instr->representation().IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(instr->representation()));
+ ASSERT(instr->right()->representation().Equals(instr->representation()));
+ left = UseRegisterAtStart(instr->BetterLeftOperand());
+ right = UseOrConstantAtStart(instr->BetterRightOperand());
+ } else {
+ ASSERT(instr->representation().IsDouble());
+ ASSERT(instr->left()->representation().IsDouble());
+ ASSERT(instr->right()->representation().IsDouble());
+ left = UseRegisterAtStart(instr->left());
+ right = UseRegisterAtStart(instr->right());
+ }
+ LMathMinMax* minmax = new(zone()) LMathMinMax(left, right);
+ return DefineSameAsFirst(minmax);
+}
+
+
+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(), xmm2);
+ LOperand* right = exponent_type.IsDouble() ?
+ UseFixedDouble(instr->right(), xmm1) : UseFixed(instr->right(), rdx);
+ 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(), arg_reg_1);
+ LRandom* result = new(zone()) LRandom(global_object);
+ return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
+ ASSERT(instr->left()->representation().IsTagged());
+ ASSERT(instr->right()->representation().IsTagged());
+ LOperand* left = UseFixed(instr->left(), rdx);
+ LOperand* right = UseFixed(instr->right(), rax);
+ LCmpT* result = new(zone()) LCmpT(left, right);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareNumericAndBranch(
+ HCompareNumericAndBranch* instr) {
+ Representation r = instr->representation();
+ if (r.IsSmiOrInteger32()) {
+ ASSERT(instr->left()->representation().Equals(r));
+ ASSERT(instr->right()->representation().Equals(r));
+ LOperand* left = UseRegisterOrConstantAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ return new(zone()) LCompareNumericAndBranch(left, right);
+ } else {
+ ASSERT(r.IsDouble());
+ ASSERT(instr->left()->representation().IsDouble());
+ ASSERT(instr->right()->representation().IsDouble());
+ 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()) LCompareNumericAndBranch(left, right);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch(
+ HCompareObjectEqAndBranch* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterOrConstantAtStart(instr->right());
+ return new(zone()) LCmpObjectEqAndBranch(left, right);
+}
+
+
+LInstruction* LChunkBuilder::DoCompareHoleAndBranch(
+ HCompareHoleAndBranch* instr) {
+ LOperand* object = UseRegisterAtStart(instr->object());
+ return new(zone()) LCmpHoleAndBranch(object);
+}
+
+
+LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LIsObjectAndBranch(UseRegisterAtStart(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* temp = TempRegister();
+ return new(zone()) LIsStringAndBranch(value, temp);
+}
+
+
+LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ return new(zone()) LIsSmiAndBranch(Use(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
+ HIsUndetectableAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* temp = TempRegister();
+ return new(zone()) LIsUndetectableAndBranch(value, temp);
+}
+
+
+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(zone()) LStringCompareAndBranch(left, right);
+
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
+ HHasInstanceTypeAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return new(zone()) LHasInstanceTypeAndBranch(value);
+}
+
+
+LInstruction* LChunkBuilder::DoGetCachedArrayIndex(
+ HGetCachedArrayIndex* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+
+ return DefineAsRegister(new(zone()) LGetCachedArrayIndex(value));
+}
+
+
+LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch(
+ HHasCachedArrayIndexAndBranch* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return new(zone()) LHasCachedArrayIndexAndBranch(value);
+}
+
+
+LInstruction* LChunkBuilder::DoClassOfTestAndBranch(
+ HClassOfTestAndBranch* instr) {
+ LOperand* value = UseRegister(instr->value());
+ return new(zone()) LClassOfTestAndBranch(value,
+ TempRegister(),
+ TempRegister());
+}
+
+
+LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
+ LOperand* map = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LMapEnumLength(map));
+}
+
+
+LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
+ LOperand* object = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LElementsKind(object));
+}
+
+
+LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
+ LOperand* object = UseRegister(instr->value());
+ LValueOf* result = new(zone()) LValueOf(object);
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+ LOperand* object = UseFixed(instr->value(), rax);
+ LDateField* result = new(zone()) LDateField(object, instr->index());
+ return MarkAsCall(DefineFixed(result, rax), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) {
+ LOperand* string = UseRegister(instr->string());
+ LOperand* index = UseRegister(instr->index());
+ ASSERT(rcx.is_byte_register());
+ LOperand* value = UseFixed(instr->value(), rcx);
+ LSeqStringSetChar* result =
+ new(zone()) LSeqStringSetChar(instr->encoding(), string, index, value);
+ return DefineSameAsFirst(result);
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
+ LOperand* value = UseRegisterOrConstantAtStart(instr->index());
+ LOperand* length = Use(instr->length());
+ return AssignEnvironment(new(zone()) LBoundsCheck(value, length));
+}
+
+
+LInstruction* LChunkBuilder::DoBoundsCheckBaseIndexInformation(
+ HBoundsCheckBaseIndexInformation* instr) {
+ UNREACHABLE();
+ return NULL;
+}
+
+
+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(), rax);
+ return MarkAsCall(new(zone()) 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.IsSmi()) {
+ if (to.IsTagged()) {
+ LOperand* value = UseRegister(instr->value());
+ return DefineSameAsFirst(new(zone()) LDummyUse(value));
+ }
+ from = Representation::Tagged();
+ }
+ // Only mark conversions that might need to allocate as calling rather than
+ // all changes. This makes simple, non-allocating conversion not have to force
+ // building a stack frame.
+ if (from.IsTagged()) {
+ if (to.IsDouble()) {
+ info()->MarkAsDeferredCalling();
+ LOperand* value = UseRegister(instr->value());
+ LNumberUntagD* res = new(zone()) LNumberUntagD(value);
+ return AssignEnvironment(DefineAsRegister(res));
+ } else if (to.IsSmi()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ if (val->type().IsSmi()) {
+ return DefineSameAsFirst(new(zone()) LDummyUse(value));
+ }
+ return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value)));
+ } else {
+ ASSERT(to.IsInteger32());
+ LOperand* value = UseRegister(instr->value());
+ if (instr->value()->type().IsSmi()) {
+ return DefineSameAsFirst(new(zone()) LSmiUntag(value, false));
+ } else {
+ bool truncating = instr->CanTruncateToInt32();
+ LOperand* xmm_temp = truncating ? NULL : FixedTemp(xmm1);
+ LTaggedToI* res = new(zone()) LTaggedToI(value, xmm_temp);
+ return AssignEnvironment(DefineSameAsFirst(res));
+ }
+ }
+ } else if (from.IsDouble()) {
+ if (to.IsTagged()) {
+ info()->MarkAsDeferredCalling();
+ LOperand* value = UseRegister(instr->value());
+ LOperand* temp = TempRegister();
+
+ // Make sure that temp and result_temp are different registers.
+ LUnallocated* result_temp = TempRegister();
+ LNumberTagD* result = new(zone()) LNumberTagD(value, temp);
+ return AssignPointerMap(Define(result, result_temp));
+ } else if (to.IsSmi()) {
+ LOperand* value = UseRegister(instr->value());
+ return AssignEnvironment(
+ DefineAsRegister(new(zone()) LDoubleToSmi(value)));
+ } else {
+ ASSERT(to.IsInteger32());
+ LOperand* value = UseRegister(instr->value());
+ return AssignEnvironment(
+ DefineAsRegister(new(zone()) LDoubleToI(value)));
+ }
+ } else if (from.IsInteger32()) {
+ info()->MarkAsDeferredCalling();
+ if (to.IsTagged()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ if (val->CheckFlag(HInstruction::kUint32)) {
+ LOperand* temp = FixedTemp(xmm1);
+ LNumberTagU* result = new(zone()) LNumberTagU(value, temp);
+ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
+ } else if (val->HasRange() && val->range()->IsInSmiRange()) {
+ return DefineSameAsFirst(new(zone()) LSmiTag(value));
+ } else {
+ LNumberTagI* result = new(zone()) LNumberTagI(value);
+ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
+ }
+ } else if (to.IsSmi()) {
+ HValue* val = instr->value();
+ LOperand* value = UseRegister(val);
+ LInstruction* result =
+ DefineAsRegister(new(zone()) LInteger32ToSmi(value));
+ if (val->HasRange() && val->range()->IsInSmiRange()) {
+ return result;
+ }
+ return AssignEnvironment(result);
+ } else {
+ if (instr->value()->CheckFlag(HInstruction::kUint32)) {
+ LOperand* temp = FixedTemp(xmm1);
+ return DefineAsRegister(
+ new(zone()) LUint32ToDouble(UseRegister(instr->value()), temp));
+ } else {
+ ASSERT(to.IsDouble());
+ LOperand* value = Use(instr->value());
+ return DefineAsRegister(new(zone()) LInteger32ToDouble(value));
+ }
+ }
+ }
+ UNREACHABLE();
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoCheckHeapObject(HCheckHeapObject* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckNonSmi(value));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckSmi(value));
+}
+
+
+LInstruction* LChunkBuilder::DoIsNumberAndBranch(HIsNumberAndBranch* instr) {
+ return new(zone()) LIsNumberAndBranch(
+ UseRegisterOrConstantAtStart(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LCheckInstanceType* result = new(zone()) LCheckInstanceType(value);
+ return AssignEnvironment(result);
+}
+
+
+LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ return AssignEnvironment(new(zone()) LCheckFunction(value));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMaps(HCheckMaps* instr) {
+ LOperand* value = NULL;
+ if (!instr->CanOmitMapChecks()) {
+ value = UseRegisterAtStart(instr->value());
+ if (instr->has_migration_target()) info()->MarkAsDeferredCalling();
+ }
+ LCheckMaps* result = new(zone()) LCheckMaps(value);
+ if (!instr->CanOmitMapChecks()) {
+ AssignEnvironment(result);
+ if (instr->has_migration_target()) return AssignPointerMap(result);
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ LOperand* reg = UseRegister(value);
+ if (input_rep.IsDouble()) {
+ return DefineAsRegister(new(zone()) LClampDToUint8(reg));
+ } else if (input_rep.IsInteger32()) {
+ return DefineSameAsFirst(new(zone()) LClampIToUint8(reg));
+ } else {
+ ASSERT(input_rep.IsSmiOrTagged());
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve xmm1 explicitly.
+ LClampTToUint8* result = new(zone()) LClampTToUint8(reg,
+ FixedTemp(xmm1));
+ return AssignEnvironment(DefineSameAsFirst(result));
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
+ LOperand* parameter_count = UseRegisterOrConstant(instr->parameter_count());
+ return new(zone()) LReturn(UseFixed(instr->value(), rax),
+ parameter_count);
+}
+
+
+LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
+ Representation r = instr->representation();
+ if (r.IsSmi()) {
+ return DefineAsRegister(new(zone()) LConstantS);
+ } else if (r.IsInteger32()) {
+ return DefineAsRegister(new(zone()) LConstantI);
+ } else if (r.IsDouble()) {
+ LOperand* temp = TempRegister();
+ return DefineAsRegister(new(zone()) LConstantD(temp));
+ } else if (r.IsExternal()) {
+ return DefineAsRegister(new(zone()) LConstantE);
+ } else if (r.IsTagged()) {
+ return DefineAsRegister(new(zone()) LConstantT);
+ } else {
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
+ LLoadGlobalCell* result = new(zone()) LLoadGlobalCell;
+ return instr->RequiresHoleCheck()
+ ? AssignEnvironment(DefineAsRegister(result))
+ : DefineAsRegister(result);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
+ LOperand* global_object = UseFixed(instr->global_object(), rax);
+ LLoadGlobalGeneric* result = new(zone()) LLoadGlobalGeneric(global_object);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) {
+ 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(zone()) LStoreGlobalCell(value, TempRegister()))
+ : new(zone()) LStoreGlobalCell(value, NULL);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
+ LOperand* global_object = UseFixed(instr->global_object(), rdx);
+ LOperand* value = UseFixed(instr->value(), rax);
+ LStoreGlobalGeneric* result = new(zone()) LStoreGlobalGeneric(global_object,
+ value);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
+ LOperand* context = UseRegisterAtStart(instr->value());
+ LInstruction* result =
+ DefineAsRegister(new(zone()) LLoadContextSlot(context));
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) {
+ LOperand* context;
+ LOperand* value;
+ LOperand* temp;
+ if (instr->NeedsWriteBarrier()) {
+ context = UseTempRegister(instr->context());
+ value = UseTempRegister(instr->value());
+ temp = TempRegister();
+ } else {
+ context = UseRegister(instr->context());
+ value = UseRegister(instr->value());
+ temp = NULL;
+ }
+ LInstruction* result = new(zone()) LStoreContextSlot(context, value, temp);
+ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
+ if (instr->access().IsExternalMemory() && instr->access().offset() == 0) {
+ LOperand* obj = UseRegisterOrConstantAtStart(instr->object());
+ return DefineFixed(new(zone()) LLoadNamedField(obj), rax);
+ }
+ LOperand* obj = UseRegisterAtStart(instr->object());
+ return DefineAsRegister(new(zone()) LLoadNamedField(obj));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
+ LOperand* object = UseFixed(instr->object(), rax);
+ LLoadNamedGeneric* result = new(zone()) LLoadNamedGeneric(object);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFunctionPrototype(
+ HLoadFunctionPrototype* instr) {
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LLoadFunctionPrototype(UseRegister(instr->function()))));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadExternalArrayPointer(
+ HLoadExternalArrayPointer* instr) {
+ LOperand* input = UseRegisterAtStart(instr->value());
+ return DefineAsRegister(new(zone()) LLoadExternalArrayPointer(input));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyed(HLoadKeyed* instr) {
+ ASSERT(instr->key()->representation().IsInteger32());
+ ElementsKind elements_kind = instr->elements_kind();
+ LOperand* key = UseRegisterOrConstantAtStart(instr->key());
+ LLoadKeyed* result = NULL;
+
+ if (!instr->is_external()) {
+ LOperand* obj = UseRegisterAtStart(instr->elements());
+ result = new(zone()) LLoadKeyed(obj, key);
+ } else {
+ ASSERT(
+ (instr->representation().IsInteger32() &&
+ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
+ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
+ (instr->representation().IsDouble() &&
+ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
+ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
+ LOperand* external_pointer = UseRegister(instr->elements());
+ result = new(zone()) LLoadKeyed(external_pointer, key);
+ }
+
+ DefineAsRegister(result);
+ bool can_deoptimize = instr->RequiresHoleCheck() ||
+ (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS);
+ // An unsigned int array load might overflow and cause a deopt, make sure it
+ // has an environment.
+ return can_deoptimize ? AssignEnvironment(result) : result;
+}
+
+
+LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
+ LOperand* object = UseFixed(instr->object(), rdx);
+ LOperand* key = UseFixed(instr->key(), rax);
+
+ LLoadKeyedGeneric* result = new(zone()) LLoadKeyedGeneric(object, key);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) {
+ ElementsKind elements_kind = instr->elements_kind();
+
+ if (!instr->is_external()) {
+ ASSERT(instr->elements()->representation().IsTagged());
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
+ LOperand* object = NULL;
+ LOperand* key = NULL;
+ LOperand* val = NULL;
+
+ if (instr->value()->representation().IsDouble()) {
+ object = UseRegisterAtStart(instr->elements());
+ val = UseTempRegister(instr->value());
+ key = UseRegisterOrConstantAtStart(instr->key());
+ } else {
+ ASSERT(instr->value()->representation().IsSmiOrTagged());
+ object = UseTempRegister(instr->elements());
+ if (needs_write_barrier) {
+ val = UseTempRegister(instr->value());
+ key = UseTempRegister(instr->key());
+ } else {
+ val = UseRegisterOrConstantAtStart(instr->value());
+ key = UseRegisterOrConstantAtStart(instr->key());
+ }
+ }
+
+ return new(zone()) LStoreKeyed(object, key, val);
+ }
+
+ ASSERT(
+ (instr->value()->representation().IsInteger32() &&
+ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
+ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
+ (instr->value()->representation().IsDouble() &&
+ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
+ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
+ ASSERT(instr->elements()->representation().IsExternal());
+ 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 = UseRegisterOrConstantAtStart(instr->key());
+ LOperand* external_pointer = UseRegister(instr->elements());
+ return new(zone()) LStoreKeyed(external_pointer, key, val);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
+ LOperand* object = UseFixed(instr->object(), rdx);
+ LOperand* key = UseFixed(instr->key(), rcx);
+ LOperand* value = UseFixed(instr->value(), rax);
+
+ ASSERT(instr->object()->representation().IsTagged());
+ ASSERT(instr->key()->representation().IsTagged());
+ ASSERT(instr->value()->representation().IsTagged());
+
+ LStoreKeyedGeneric* result =
+ new(zone()) LStoreKeyedGeneric(object, key, value);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTransitionElementsKind(
+ HTransitionElementsKind* instr) {
+ LOperand* object = UseRegister(instr->object());
+ if (IsSimpleMapChangeTransition(instr->from_kind(), instr->to_kind())) {
+ 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 result;
+ } else {
+ LTransitionElementsKind* result =
+ new(zone()) LTransitionElementsKind(object, NULL, NULL);
+ return AssignPointerMap(result);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoTrapAllocationMemento(
+ HTrapAllocationMemento* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* temp = TempRegister();
+ LTrapAllocationMemento* result =
+ new(zone()) LTrapAllocationMemento(object, temp);
+ return AssignEnvironment(result);
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
+ bool is_in_object = instr->access().IsInobject();
+ bool is_external_location = instr->access().IsExternalMemory() &&
+ instr->access().offset() == 0;
+ bool needs_write_barrier = instr->NeedsWriteBarrier();
+ bool needs_write_barrier_for_map = instr->has_transition() &&
+ instr->NeedsWriteBarrierForMap();
+
+ LOperand* obj;
+ if (needs_write_barrier) {
+ obj = is_in_object
+ ? UseRegister(instr->object())
+ : UseTempRegister(instr->object());
+ } else if (is_external_location) {
+ ASSERT(!is_in_object);
+ ASSERT(!needs_write_barrier);
+ ASSERT(!needs_write_barrier_for_map);
+ obj = UseRegisterOrConstant(instr->object());
+ } else {
+ obj = needs_write_barrier_for_map
+ ? UseRegister(instr->object())
+ : UseRegisterAtStart(instr->object());
+ }
+
+ bool can_be_constant = instr->value()->IsConstant() &&
+ HConstant::cast(instr->value())->NotInNewSpace() &&
+ !(FLAG_track_double_fields && instr->field_representation().IsDouble());
+
+ LOperand* val;
+ if (needs_write_barrier) {
+ val = UseTempRegister(instr->value());
+ } else if (is_external_location) {
+ val = UseFixed(instr->value(), rax);
+ } else if (can_be_constant) {
+ val = UseRegisterOrConstant(instr->value());
+ } else if (FLAG_track_fields && instr->field_representation().IsSmi()) {
+ val = UseTempRegister(instr->value());
+ } else if (FLAG_track_double_fields &&
+ instr->field_representation().IsDouble()) {
+ val = UseRegisterAtStart(instr->value());
+ } else {
+ val = UseRegister(instr->value());
+ }
+
+ // We only need a scratch register if we have a write barrier or we
+ // have a store into the properties array (not in-object-property).
+ LOperand* temp = (!is_in_object || needs_write_barrier ||
+ needs_write_barrier_for_map) ? TempRegister() : NULL;
+
+ LStoreNamedField* result = new(zone()) LStoreNamedField(obj, val, temp);
+ if (FLAG_track_heap_object_fields &&
+ instr->field_representation().IsHeapObject()) {
+ if (!instr->value()->type().IsHeapObject()) {
+ return AssignEnvironment(result);
+ }
+ }
+ return result;
+}
+
+
+LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
+ LOperand* object = UseFixed(instr->object(), rdx);
+ LOperand* value = UseFixed(instr->value(), rax);
+
+ LStoreNamedGeneric* result = new(zone()) LStoreNamedGeneric(object, value);
+ return MarkAsCall(result, instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
+ LOperand* left = UseOrConstantAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ return MarkAsCall(DefineFixed(new(zone()) LStringAdd(left, right), rax),
+ instr);
+}
+
+
+LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
+ LOperand* string = UseTempRegister(instr->string());
+ LOperand* index = UseTempRegister(instr->index());
+ LStringCharCodeAt* result = new(zone()) LStringCharCodeAt(string, index);
+ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
+}
+
+
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+ LOperand* char_code = UseRegister(instr->value());
+ LStringCharFromCode* result = new(zone()) LStringCharFromCode(char_code);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) {
+ info()->MarkAsDeferredCalling();
+ LOperand* size = instr->size()->IsConstant()
+ ? UseConstant(instr->size())
+ : UseTempRegister(instr->size());
+ LOperand* temp = TempRegister();
+ LAllocate* result = new(zone()) LAllocate(size, temp);
+ return AssignPointerMap(DefineAsRegister(result));
+}
+
+
+LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) {
+ return MarkAsCall(DefineFixed(new(zone()) LRegExpLiteral, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
+ return MarkAsCall(DefineFixed(new(zone()) LFunctionLiteral, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) {
+ ASSERT(argument_count_ == 0);
+ allocator_->MarkAsOsrEntry();
+ current_block_->last_environment()->set_ast_id(instr->ast_id());
+ return AssignEnvironment(new(zone()) LOsrEntry);
+}
+
+
+LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
+ LParameter* result = new(zone()) LParameter;
+ if (instr->kind() == HParameter::STACK_PARAMETER) {
+ int spill_index = chunk()->GetParameterStackSlot(instr->index());
+ return DefineAsSpilled(result, spill_index);
+ } else {
+ ASSERT(info()->IsStub());
+ CodeStubInterfaceDescriptor* descriptor =
+ info()->code_stub()->GetInterfaceDescriptor(info()->isolate());
+ int index = static_cast<int>(instr->index());
+ Register reg = DESCRIPTOR_GET_PARAMETER_REGISTER(descriptor, index);
+ return DefineFixed(result, reg);
+ }
+}
+
+
+LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
+ int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
+ if (spill_index > LUnallocated::kMaxFixedSlotIndex) {
+ Abort(kTooManySpillSlotsNeededForOSR);
+ spill_index = 0;
+ }
+ return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index);
+}
+
+
+LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) {
+ argument_count_ -= instr->argument_count();
+ return MarkAsCall(DefineFixed(new(zone()) LCallStub, rax), 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::DoCapturedObject(HCapturedObject* instr) {
+ // There are no real uses of a captured object.
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
+ info()->MarkAsRequiresFrame();
+ LOperand* args = UseRegister(instr->arguments());
+ LOperand* length;
+ LOperand* index;
+ if (instr->length()->IsConstant() && instr->index()->IsConstant()) {
+ length = UseRegisterOrConstant(instr->length());
+ index = UseOrConstant(instr->index());
+ } else {
+ length = UseTempRegister(instr->length());
+ index = Use(instr->index());
+ }
+ return DefineAsRegister(new(zone()) LAccessArgumentsAt(args, length, index));
+}
+
+
+LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) {
+ LOperand* object = UseFixed(instr->value(), rax);
+ LToFastProperties* result = new(zone()) LToFastProperties(object);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
+ LTypeof* result = new(zone()) LTypeof(UseAtStart(instr->value()));
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
+LInstruction* LChunkBuilder::DoTypeofIsAndBranch(HTypeofIsAndBranch* instr) {
+ return new(zone()) LTypeofIsAndBranch(UseTempRegister(instr->value()));
+}
+
+
+LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
+ HIsConstructCallAndBranch* instr) {
+ return new(zone()) 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 = instr->values()->length() - 1; i >= 0; --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()) {
+ LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
+ LInstruction* result = AssignEnvironment(lazy_bailout);
+ // Store the lazy deopt environment with the instruction if needed. Right
+ // now it is only used for LInstanceOfKnownGlobal.
+ instruction_pending_deoptimization_environment_->
+ SetDeferredLazyDeoptimizationEnvironment(result->environment());
+ instruction_pending_deoptimization_environment_ = NULL;
+ pending_deoptimization_ast_id_ = BailoutId::None();
+ return result;
+ }
+
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) {
+ info()->MarkAsDeferredCalling();
+ if (instr->is_function_entry()) {
+ return MarkAsCall(new(zone()) LStackCheck, instr);
+ } else {
+ ASSERT(instr->is_backwards_branch());
+ return AssignEnvironment(AssignPointerMap(new(zone()) 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->inlining_kind(),
+ instr->undefined_receiver());
+ // Only replay binding of arguments object if it wasn't removed from graph.
+ if (instr->arguments_var() != NULL && instr->arguments_object()->IsLinked()) {
+ inner->Bind(instr->arguments_var(), instr->arguments_object());
+ }
+ inner->set_entry(instr);
+ current_block_->UpdateEnvironment(inner);
+ chunk_->AddInlinedClosure(instr->closure());
+ return NULL;
+}
+
+
+LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
+ LInstruction* pop = NULL;
+
+ HEnvironment* env = current_block_->last_environment();
+
+ if (env->entry()->arguments_pushed()) {
+ int argument_count = env->arguments_environment()->parameter_count();
+ pop = new(zone()) LDrop(argument_count);
+ argument_count_ -= argument_count;
+ }
+
+ HEnvironment* outer = current_block_->last_environment()->
+ DiscardInlined(false);
+ current_block_->UpdateEnvironment(outer);
+
+ return pop;
+}
+
+
+LInstruction* LChunkBuilder::DoForInPrepareMap(HForInPrepareMap* instr) {
+ LOperand* object = UseFixed(instr->enumerable(), rax);
+ LForInPrepareMap* result = new(zone()) LForInPrepareMap(object);
+ return MarkAsCall(DefineFixed(result, rax), instr, CAN_DEOPTIMIZE_EAGERLY);
+}
+
+
+LInstruction* LChunkBuilder::DoForInCacheArray(HForInCacheArray* instr) {
+ LOperand* map = UseRegister(instr->map());
+ return AssignEnvironment(DefineAsRegister(
+ new(zone()) LForInCacheArray(map)));
+}
+
+
+LInstruction* LChunkBuilder::DoCheckMapValue(HCheckMapValue* instr) {
+ LOperand* value = UseRegisterAtStart(instr->value());
+ LOperand* map = UseRegisterAtStart(instr->map());
+ return AssignEnvironment(new(zone()) LCheckMapValue(value, map));
+}
+
+
+LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* index = UseTempRegister(instr->index());
+ return DefineSameAsFirst(new(zone()) LLoadFieldByIndex(object, index));
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/lithium-x64.h b/chromium/v8/src/x64/lithium-x64.h
new file mode 100644
index 00000000000..77bebe64bd7
--- /dev/null
+++ b/chromium/v8/src/x64/lithium-x64.h
@@ -0,0 +1,2678 @@
+// Copyright 2012 the V8 project authors. 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_X64_LITHIUM_X64_H_
+#define V8_X64_LITHIUM_X64_H_
+
+#include "hydrogen.h"
+#include "lithium-allocator.h"
+#include "lithium.h"
+#include "safepoint-table.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+// Forward declarations.
+class LCodeGen;
+
+#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
+ V(AccessArgumentsAt) \
+ V(AddI) \
+ V(Allocate) \
+ V(ApplyArguments) \
+ V(ArgumentsElements) \
+ V(ArgumentsLength) \
+ V(ArithmeticD) \
+ V(ArithmeticT) \
+ V(BitI) \
+ V(BoundsCheck) \
+ V(Branch) \
+ V(CallConstantFunction) \
+ V(CallFunction) \
+ V(CallGlobal) \
+ V(CallKeyed) \
+ V(CallKnownGlobal) \
+ V(CallNamed) \
+ V(CallNew) \
+ V(CallNewArray) \
+ V(CallRuntime) \
+ V(CallStub) \
+ V(CheckFunction) \
+ V(CheckInstanceType) \
+ V(CheckMaps) \
+ V(CheckMapValue) \
+ V(CheckNonSmi) \
+ V(CheckSmi) \
+ V(ClampDToUint8) \
+ V(ClampIToUint8) \
+ V(ClampTToUint8) \
+ V(ClassOfTestAndBranch) \
+ V(CompareNumericAndBranch) \
+ V(CmpObjectEqAndBranch) \
+ V(CmpHoleAndBranch) \
+ V(CmpMapAndBranch) \
+ V(CmpT) \
+ V(ConstantD) \
+ V(ConstantE) \
+ V(ConstantI) \
+ V(ConstantS) \
+ V(ConstantT) \
+ V(Context) \
+ V(DateField) \
+ V(DebugBreak) \
+ V(DeclareGlobals) \
+ V(Deoptimize) \
+ V(DivI) \
+ V(DoubleToI) \
+ V(DoubleToSmi) \
+ V(Drop) \
+ V(DummyUse) \
+ V(ElementsKind) \
+ V(ForInCacheArray) \
+ V(ForInPrepareMap) \
+ V(FunctionLiteral) \
+ V(GetCachedArrayIndex) \
+ V(GlobalObject) \
+ V(GlobalReceiver) \
+ V(Goto) \
+ V(HasCachedArrayIndexAndBranch) \
+ V(HasInstanceTypeAndBranch) \
+ V(InnerAllocatedObject) \
+ V(InstanceOf) \
+ V(InstanceOfKnownGlobal) \
+ V(InstanceSize) \
+ V(InstructionGap) \
+ V(Integer32ToDouble) \
+ V(Integer32ToSmi) \
+ V(InvokeFunction) \
+ V(IsConstructCallAndBranch) \
+ V(IsObjectAndBranch) \
+ V(IsStringAndBranch) \
+ V(IsSmiAndBranch) \
+ V(IsNumberAndBranch) \
+ V(IsUndetectableAndBranch) \
+ V(Label) \
+ V(LazyBailout) \
+ V(LoadContextSlot) \
+ V(LoadExternalArrayPointer) \
+ V(LoadFieldByIndex) \
+ V(LoadFunctionPrototype) \
+ V(LoadGlobalCell) \
+ V(LoadGlobalGeneric) \
+ V(LoadKeyed) \
+ V(LoadKeyedGeneric) \
+ V(LoadNamedField) \
+ V(LoadNamedGeneric) \
+ V(MapEnumLength) \
+ V(MathAbs) \
+ V(MathCos) \
+ V(MathExp) \
+ V(MathFloor) \
+ V(MathFloorOfDiv) \
+ V(MathLog) \
+ V(MathMinMax) \
+ V(MathPowHalf) \
+ V(MathRound) \
+ V(MathSin) \
+ V(MathSqrt) \
+ V(MathTan) \
+ V(ModI) \
+ V(MulI) \
+ V(NumberTagD) \
+ V(NumberTagI) \
+ V(NumberTagU) \
+ V(NumberUntagD) \
+ V(OsrEntry) \
+ V(OuterContext) \
+ V(Parameter) \
+ V(Power) \
+ V(PushArgument) \
+ V(Random) \
+ V(RegExpLiteral) \
+ V(Return) \
+ V(SeqStringSetChar) \
+ V(ShiftI) \
+ V(SmiTag) \
+ V(SmiUntag) \
+ V(StackCheck) \
+ V(StoreContextSlot) \
+ V(StoreGlobalCell) \
+ V(StoreGlobalGeneric) \
+ V(StoreKeyed) \
+ V(StoreKeyedGeneric) \
+ V(StoreNamedField) \
+ V(StoreNamedGeneric) \
+ V(StringAdd) \
+ V(StringCharCodeAt) \
+ V(StringCharFromCode) \
+ V(StringCompareAndBranch) \
+ V(SubI) \
+ V(TaggedToI) \
+ V(ThisFunction) \
+ V(Throw) \
+ V(ToFastProperties) \
+ V(TransitionElementsKind) \
+ V(TrapAllocationMemento) \
+ V(Typeof) \
+ V(TypeofIsAndBranch) \
+ V(Uint32ToDouble) \
+ V(UnknownOSRValue) \
+ V(ValueOf) \
+ V(WrapReceiver)
+
+
+#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()
+ : environment_(NULL),
+ hydrogen_value_(NULL),
+ bit_field_(IsCallBits::encode(false)) {
+ set_position(RelocInfo::kNoPosition);
+ }
+
+ virtual ~LInstruction() { }
+
+ virtual void CompileToNative(LCodeGen* generator) = 0;
+ virtual const char* Mnemonic() const = 0;
+ virtual void PrintTo(StringStream* stream);
+ virtual void PrintDataTo(StringStream* stream);
+ virtual void PrintOutputOperandTo(StringStream* stream);
+
+ 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(); }
+
+ // The 31 bits PositionBits is used to store the int position value. And the
+ // position value may be RelocInfo::kNoPosition (-1). The accessor always
+ // +1/-1 so that the encoded value of position in bit_field_ is always >= 0
+ // and can fit into the 31 bits PositionBits.
+ void set_position(int pos) {
+ bit_field_ = PositionBits::update(bit_field_, pos + 1);
+ }
+ int position() { return PositionBits::decode(bit_field_) - 1; }
+
+ void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
+ HValue* hydrogen_value() const { return hydrogen_value_; }
+
+ void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
+ bool IsCall() const { return IsCallBits::decode(bit_field_); }
+
+ // Interface to the register allocator and iterators.
+ bool ClobbersTemps() const { return IsCall(); }
+ bool ClobbersRegisters() const { return IsCall(); }
+ bool ClobbersDoubleRegisters() const { return IsCall(); }
+
+ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
+
+ // Interface to the register allocator and iterators.
+ bool IsMarkedAsCall() const { return IsCall(); }
+
+ virtual bool HasResult() const = 0;
+ virtual LOperand* result() const = 0;
+
+ LOperand* FirstInput() { return InputAt(0); }
+ LOperand* Output() { return HasResult() ? result() : NULL; }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return true; }
+
+#ifdef DEBUG
+ void VerifyCall();
+#endif
+
+ private:
+ // Iterator support.
+ friend class InputIterator;
+ virtual int InputCount() = 0;
+ virtual LOperand* InputAt(int i) = 0;
+
+ friend class TempIterator;
+ virtual int TempCount() = 0;
+ virtual LOperand* TempAt(int i) = 0;
+
+ class IsCallBits: public BitField<bool, 0, 1> {};
+ class PositionBits: public BitField<int, 1, 31> {};
+
+ LEnvironment* environment_;
+ SetOncePointer<LPointerMap> pointer_map_;
+ HValue* hydrogen_value_;
+ int bit_field_;
+};
+
+
+// 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 && result() != NULL; }
+ void set_result(LOperand* operand) { results_[0] = operand; }
+ LOperand* result() const { return results_[0]; }
+
+ protected:
+ EmbeddedContainer<LOperand*, R> results_;
+ EmbeddedContainer<LOperand*, I> inputs_;
+ EmbeddedContainer<LOperand*, T> temps_;
+
+ private:
+ // Iterator support.
+ virtual int InputCount() { return I; }
+ virtual LOperand* InputAt(int i) { return inputs_[i]; }
+
+ virtual int TempCount() { return T; }
+ virtual LOperand* TempAt(int i) { return temps_[i]; }
+};
+
+
+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;
+ }
+
+ // 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);
+ }
+
+ bool IsRedundant() const;
+
+ HBasicBlock* block() const { return block_; }
+
+ enum InnerPosition {
+ BEFORE,
+ START,
+ END,
+ AFTER,
+ FIRST_INNER_POSITION = BEFORE,
+ LAST_INNER_POSITION = AFTER
+ };
+
+ LParallelMove* GetOrCreateParallelMove(InnerPosition pos,
+ Zone* zone) {
+ if (parallel_moves_[pos] == NULL) {
+ parallel_moves_[pos] = new(zone) LParallelMove(zone);
+ }
+ return parallel_moves_[pos];
+ }
+
+ LParallelMove* GetParallelMove(InnerPosition pos) {
+ return parallel_moves_[pos];
+ }
+
+ private:
+ LParallelMove* parallel_moves_[LAST_INNER_POSITION + 1];
+ HBasicBlock* block_;
+};
+
+
+class LInstructionGap: public LGap {
+ public:
+ explicit LInstructionGap(HBasicBlock* block) : LGap(block) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const {
+ return !IsRedundant();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap")
+};
+
+
+class LGoto: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LGoto(int block_id) : block_id_(block_id) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const;
+ 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_; }
+
+ private:
+ int gap_instructions_size_;
+};
+
+
+class LDummyUse: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDummyUse(LOperand* value) {
+ inputs_[0] = value;
+ }
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use")
+};
+
+
+class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
+ DECLARE_HYDROGEN_ACCESSOR(Deoptimize)
+};
+
+
+class LLabel: public LGap {
+ public:
+ explicit LLabel(HBasicBlock* block)
+ : LGap(block), replacement_(NULL) { }
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ 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(); }
+ bool is_osr_entry() const { return block()->is_osr_entry(); }
+ 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:
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ 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();
+ }
+};
+
+
+class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
+ public:
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value")
+};
+
+
+template<int I, int T>
+class LControlInstruction: public LTemplateInstruction<0, I, T> {
+ public:
+ LControlInstruction() : false_label_(NULL), true_label_(NULL) { }
+
+ virtual bool IsControl() const { return true; }
+
+ int SuccessorCount() { return hydrogen()->SuccessorCount(); }
+ HBasicBlock* SuccessorAt(int i) { return hydrogen()->SuccessorAt(i); }
+
+ int TrueDestination(LChunk* chunk) {
+ return chunk->LookupDestination(true_block_id());
+ }
+ int FalseDestination(LChunk* chunk) {
+ return chunk->LookupDestination(false_block_id());
+ }
+
+ Label* TrueLabel(LChunk* chunk) {
+ if (true_label_ == NULL) {
+ true_label_ = chunk->GetAssemblyLabel(TrueDestination(chunk));
+ }
+ return true_label_;
+ }
+ Label* FalseLabel(LChunk* chunk) {
+ if (false_label_ == NULL) {
+ false_label_ = chunk->GetAssemblyLabel(FalseDestination(chunk));
+ }
+ return false_label_;
+ }
+
+ protected:
+ int true_block_id() { return SuccessorAt(0)->block_id(); }
+ int false_block_id() { return SuccessorAt(1)->block_id(); }
+
+ private:
+ HControlInstruction* hydrogen() {
+ return HControlInstruction::cast(this->hydrogen_value());
+ }
+
+ Label* false_label_;
+ Label* true_label_;
+};
+
+
+class LWrapReceiver: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LWrapReceiver(LOperand* receiver, LOperand* function) {
+ inputs_[0] = receiver;
+ inputs_[1] = function;
+ }
+
+ LOperand* receiver() { return inputs_[0]; }
+ LOperand* function() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver")
+};
+
+
+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;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+ LOperand* receiver() { return inputs_[1]; }
+ LOperand* length() { return inputs_[2]; }
+ LOperand* elements() { return inputs_[3]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments")
+};
+
+
+class LAccessArgumentsAt: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LAccessArgumentsAt(LOperand* arguments, LOperand* length, LOperand* index) {
+ inputs_[0] = arguments;
+ inputs_[1] = length;
+ inputs_[2] = index;
+ }
+
+ LOperand* arguments() { return inputs_[0]; }
+ LOperand* length() { return inputs_[1]; }
+ LOperand* index() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at")
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LArgumentsLength: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LArgumentsLength(LOperand* elements) {
+ inputs_[0] = elements;
+ }
+
+ LOperand* elements() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length")
+};
+
+
+class LArgumentsElements: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments-elements")
+ DECLARE_HYDROGEN_ACCESSOR(ArgumentsElements)
+};
+
+
+class LModI: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LModI(LOperand* left, LOperand* right, LOperand* temp) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mod)
+};
+
+
+class LDivI: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LDivI(LOperand* left, LOperand* right, LOperand* temp) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
+ DECLARE_HYDROGEN_ACCESSOR(Div)
+};
+
+
+class LMathFloorOfDiv: public LTemplateInstruction<1, 2, 1> {
+ public:
+ LMathFloorOfDiv(LOperand* left,
+ LOperand* right,
+ LOperand* temp = NULL) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ temps_[0] = temp;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
+ DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
+};
+
+
+class LMulI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LMulI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i")
+ DECLARE_HYDROGEN_ACCESSOR(Mul)
+};
+
+
+class LCompareNumericAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCompareNumericAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareNumericAndBranch,
+ "compare-numeric-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareNumericAndBranch)
+
+ Token::Value op() const { return hydrogen()->token(); }
+ bool is_double() const {
+ return hydrogen()->representation().IsDouble();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LMathFloor: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathFloor(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathFloor, "math-floor")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathRound: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathRound(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathRound, "math-round")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathAbs: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathAbs(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathAbs, "math-abs")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
+};
+
+
+class LMathLog: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathLog(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathLog, "math-log")
+};
+
+
+class LMathSin: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathSin(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathSin, "math-sin")
+};
+
+
+class LMathCos: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathCos(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathCos, "math-cos")
+};
+
+
+class LMathTan: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathTan(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathTan, "math-tan")
+};
+
+
+class LMathExp: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LMathExp(LOperand* value, LOperand* temp1, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp1;
+ temps_[1] = temp2;
+ ExternalReference::InitializeMathExpData();
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp1() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathExp, "math-exp")
+};
+
+
+class LMathSqrt: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathSqrt(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathSqrt, "math-sqrt")
+};
+
+
+class LMathPowHalf: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMathPowHalf(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathPowHalf, "math-pow-half")
+};
+
+
+class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCmpObjectEqAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, "cmp-object-eq-and-branch")
+};
+
+
+class LCmpHoleAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LCmpHoleAndBranch(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpHoleAndBranch, "cmp-hole-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareHoleAndBranch)
+};
+
+
+class LIsObjectAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LIsObjectAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsObjectAndBranch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LIsNumberAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LIsNumberAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsNumberAndBranch, "is-number-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsNumberAndBranch)
+};
+
+
+class LIsStringAndBranch: public LControlInstruction<1, 1> {
+ public:
+ explicit LIsStringAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch,
+ "is-undetectable-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsUndetectableAndBranch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LStringCompareAndBranch: public LControlInstruction<2, 0> {
+ public:
+ explicit LStringCompareAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get-cached-array-index")
+ DECLARE_HYDROGEN_ACCESSOR(GetCachedArrayIndex)
+};
+
+
+class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LHasCachedArrayIndexAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch,
+ "has-cached-array-index-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(HasCachedArrayIndexAndBranch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LClassOfTestAndBranch: public LControlInstruction<1, 2> {
+ public:
+ LClassOfTestAndBranch(LOperand* value, LOperand* temp, LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+ LOperand* temp2() { return temps_[1]; }
+
+ 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:
+ LCmpT(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
+ "instance-of-known-global")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
+
+ Handle<JSFunction> function() const { return hydrogen()->function(); }
+ LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
+ return lazy_deopt_env_;
+ }
+ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) {
+ lazy_deopt_env_ = env;
+ }
+
+ private:
+ LEnvironment* lazy_deopt_env_;
+};
+
+
+class LInstanceSize: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInstanceSize(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstanceSize, "instance-size")
+ DECLARE_HYDROGEN_ACCESSOR(InstanceSize)
+};
+
+
+class LBoundsCheck: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LBoundsCheck(LOperand* index, LOperand* length) {
+ inputs_[0] = index;
+ inputs_[1] = length;
+ }
+
+ LOperand* index() { return inputs_[0]; }
+ LOperand* length() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check")
+ DECLARE_HYDROGEN_ACCESSOR(BoundsCheck)
+};
+
+
+class LBitI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LBitI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ Token::Value op() const { return op_; }
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+ 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;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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 LConstantS: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ConstantS, "constant-s")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ Smi* value() const { return Smi::FromInt(hydrogen()->Integer32Value()); }
+};
+
+
+class LConstantD: public LTemplateInstruction<1, 0, 1> {
+ public:
+ explicit LConstantD(LOperand* temp) {
+ temps_[0] = temp;
+ }
+
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ConstantD, "constant-d")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ double value() const { return hydrogen()->DoubleValue(); }
+};
+
+
+class LConstantE: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(ConstantE, "constant-e")
+ DECLARE_HYDROGEN_ACCESSOR(Constant)
+
+ ExternalReference value() const {
+ return hydrogen()->ExternalReferenceValue();
+ }
+};
+
+
+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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Branch, "branch")
+ DECLARE_HYDROGEN_ACCESSOR(Branch)
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LDebugBreak: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(DebugBreak, "break")
+};
+
+
+class LCmpMapAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LCmpMapAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(CompareMap)
+
+ Handle<Map> map() const { return hydrogen()->map(); }
+};
+
+
+class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LMapEnumLength(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length")
+};
+
+
+class LElementsKind: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LElementsKind(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ElementsKind, "elements-kind")
+ DECLARE_HYDROGEN_ACCESSOR(ElementsKind)
+};
+
+
+class LValueOf: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LValueOf(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+};
+
+
+class LDateField: public LTemplateInstruction<1, 1, 0> {
+ public:
+ LDateField(LOperand* date, Smi* index) : index_(index) {
+ inputs_[0] = date;
+ }
+
+ LOperand* date() { return inputs_[0]; }
+ Smi* index() const { return index_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
+ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+
+ private:
+ Smi* index_;
+};
+
+
+class LSeqStringSetChar: public LTemplateInstruction<1, 3, 0> {
+ public:
+ LSeqStringSetChar(String::Encoding encoding,
+ LOperand* string,
+ LOperand* index,
+ LOperand* value) : encoding_(encoding) {
+ inputs_[0] = string;
+ inputs_[1] = index;
+ inputs_[2] = value;
+ }
+
+ String::Encoding encoding() { return encoding_; }
+ LOperand* string() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar, "seq-string-set-char")
+ DECLARE_HYDROGEN_ACCESSOR(SeqStringSetChar)
+
+ private:
+ String::Encoding encoding_;
+};
+
+
+class LThrow: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LThrow(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
+};
+
+
+class LAddI: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LAddI(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ static bool UseLea(HAdd* add) {
+ return !add->CheckFlag(HValue::kCanOverflow) &&
+ add->BetterLeftOperand()->UseCount() > 1;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i")
+ DECLARE_HYDROGEN_ACCESSOR(Add)
+};
+
+
+class LMathMinMax: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LMathMinMax(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(MathMinMax, "math-min-max")
+ DECLARE_HYDROGEN_ACCESSOR(MathMinMax)
+};
+
+
+class LPower: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LPower(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+
+ 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_; }
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ 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;
+ }
+
+ Token::Value op() const { return op_; }
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ virtual Opcode opcode() const { return LInstruction::kArithmeticT; }
+ virtual void CompileToNative(LCodeGen* generator);
+ virtual const char* Mnemonic() const;
+
+ private:
+ Token::Value op_;
+};
+
+
+class LReturn: public LTemplateInstruction<0, 2, 0> {
+ public:
+ explicit LReturn(LOperand* value, LOperand* parameter_count) {
+ inputs_[0] = value;
+ inputs_[1] = parameter_count;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ bool has_constant_parameter_count() {
+ return parameter_count()->IsConstantOperand();
+ }
+ LConstantOperand* constant_parameter_count() {
+ ASSERT(has_constant_parameter_count());
+ return LConstantOperand::cast(parameter_count());
+ }
+ LOperand* parameter_count() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Return, "return")
+ DECLARE_HYDROGEN_ACCESSOR(Return)
+};
+
+
+class LLoadNamedField: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadNamedField(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(LoadNamedField)
+};
+
+
+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 LLoadExternalArrayPointer: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadExternalArrayPointer(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer,
+ "load-external-array-pointer")
+};
+
+
+class LLoadKeyed: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadKeyed(LOperand* elements, LOperand* key) {
+ inputs_[0] = elements;
+ inputs_[1] = key;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyed, "load-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(LoadKeyed)
+
+ bool is_external() const {
+ return hydrogen()->is_external();
+ }
+ LOperand* elements() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ virtual void PrintDataTo(StringStream* stream);
+ uint32_t additional_index() const { return hydrogen()->index_offset(); }
+ 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:
+ explicit LStoreGlobalCell(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell)
+};
+
+
+class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> {
+ public:
+ explicit LStoreGlobalGeneric(LOperand* global_object,
+ LOperand* value) {
+ inputs_[0] = global_object;
+ inputs_[1] = value;
+ }
+
+ LOperand* global_object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric)
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LLoadContextSlot: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LLoadContextSlot(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot)
+
+ int slot_index() { return hydrogen()->slot_index(); }
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
+class LStoreContextSlot: public LTemplateInstruction<0, 2, 1> {
+ public:
+ LStoreContextSlot(LOperand* context, LOperand* value, LOperand* temp) {
+ inputs_[0] = context;
+ inputs_[1] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot")
+ DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument")
+};
+
+
+class LDrop: public LTemplateInstruction<0, 0, 0> {
+ public:
+ explicit LDrop(int count) : count_(count) { }
+
+ int count() const { return count_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Drop, "drop")
+
+ private:
+ int count_;
+};
+
+
+class LInnerAllocatedObject: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInnerAllocatedObject(LOperand* base_object) {
+ inputs_[0] = base_object;
+ }
+
+ LOperand* base_object() { return inputs_[0]; }
+ int offset() { return hydrogen()->offset(); }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(InnerAllocatedObject, "sub-allocated-object")
+ DECLARE_HYDROGEN_ACCESSOR(InnerAllocatedObject)
+};
+
+
+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")
+ DECLARE_HYDROGEN_ACCESSOR(Context)
+};
+
+
+class LOuterContext: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LOuterContext(LOperand* context) {
+ inputs_[0] = context;
+ }
+
+ LOperand* context() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context")
+};
+
+
+class LDeclareGlobals: public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals, "declare-globals")
+ DECLARE_HYDROGEN_ACCESSOR(DeclareGlobals)
+};
+
+
+class LGlobalObject: public LTemplateInstruction<1, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object")
+};
+
+
+class LGlobalReceiver: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LGlobalReceiver(LOperand* global_object) {
+ inputs_[0] = global_object;
+ }
+
+ LOperand* global() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver")
+};
+
+
+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;
+ }
+
+ LOperand* function() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ 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)
+
+ LOperand* key() { return inputs_[0]; }
+
+ 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);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNew: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallNew(LOperand* constructor) {
+ inputs_[0] = constructor;
+ }
+
+ LOperand* constructor() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new")
+ DECLARE_HYDROGEN_ACCESSOR(CallNew)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
+class LCallNewArray: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCallNewArray(LOperand* constructor) {
+ inputs_[0] = constructor;
+ }
+
+ LOperand* constructor() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CallNewArray, "call-new-array")
+ DECLARE_HYDROGEN_ACCESSOR(CallNewArray)
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double")
+};
+
+
+class LInteger32ToSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInteger32ToSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Integer32ToSmi, "int32-to-smi")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+};
+
+
+class LUint32ToDouble: public LTemplateInstruction<1, 1, 1> {
+ public:
+ explicit LUint32ToDouble(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double")
+};
+
+
+class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberTagI(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i")
+};
+
+
+class LNumberTagU: public LTemplateInstruction<1, 1, 1> {
+ public:
+ explicit LNumberTagU(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u")
+};
+
+
+class LNumberTagD: public LTemplateInstruction<1, 1, 1> {
+ public:
+ explicit LNumberTagD(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d")
+ DECLARE_HYDROGEN_ACCESSOR(Change)
+};
+
+
+// Sometimes truncating conversion from a tagged value to an int32.
+class LDoubleToI: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDoubleToI(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+
+ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
+};
+
+
+class LDoubleToSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDoubleToSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(DoubleToSmi, "double-to-smi")
+ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
+};
+
+
+// Truncating conversion from a tagged value to an int32.
+class LTaggedToI: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LTaggedToI(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag")
+};
+
+
+class LNumberUntagD: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LNumberUntagD(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ bool needs_check() const { return needs_check_; }
+
+ DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag")
+
+ private:
+ bool needs_check_;
+};
+
+
+class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
+ public:
+ LStoreNamedField(LOperand* object, LOperand* value, LOperand* temp) {
+ inputs_[0] = object;
+ inputs_[1] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> transition() const { return hydrogen()->transition_map(); }
+ Representation representation() const {
+ return hydrogen()->field_representation();
+ }
+};
+
+
+class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LStoreNamedGeneric(LOperand* object, LOperand* value) {
+ inputs_[0] = object;
+ inputs_[1] = value;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* value() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Object> name() const { return hydrogen()->name(); }
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreKeyed(LOperand* object, LOperand* key, LOperand* value) {
+ inputs_[0] = object;
+ inputs_[1] = key;
+ inputs_[2] = value;
+ }
+
+ bool is_external() const { return hydrogen()->is_external(); }
+ LOperand* elements() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+ ElementsKind elements_kind() const { return hydrogen()->elements_kind(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyed, "store-keyed")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyed)
+
+ virtual void PrintDataTo(StringStream* stream);
+ bool NeedsCanonicalization() { return hydrogen()->NeedsCanonicalization(); }
+ uint32_t additional_index() const { return hydrogen()->index_offset(); }
+};
+
+
+class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
+ public:
+ LStoreKeyedGeneric(LOperand* object, LOperand* key, LOperand* value) {
+ inputs_[0] = object;
+ inputs_[1] = key;
+ inputs_[2] = value;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* key() { return inputs_[1]; }
+ LOperand* value() { return inputs_[2]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
+ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
+};
+
+
+class LTransitionElementsKind: public LTemplateInstruction<0, 1, 2> {
+ public:
+ LTransitionElementsKind(LOperand* object,
+ LOperand* new_map_temp,
+ LOperand* temp) {
+ inputs_[0] = object;
+ temps_[0] = new_map_temp;
+ temps_[1] = temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* new_map_temp() { return temps_[0]; }
+ LOperand* temp() { return temps_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind,
+ "transition-elements-kind")
+ DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ Handle<Map> original_map() { return hydrogen()->original_map(); }
+ Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+ ElementsKind from_kind() { return hydrogen()->from_kind(); }
+ ElementsKind to_kind() { return hydrogen()->to_kind(); }
+};
+
+
+class LTrapAllocationMemento : public LTemplateInstruction<0, 1, 1> {
+ public:
+ LTrapAllocationMemento(LOperand* object,
+ LOperand* temp) {
+ inputs_[0] = object;
+ temps_[0] = temp;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(TrapAllocationMemento,
+ "trap-allocation-memento")
+};
+
+
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+};
+
+
+class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringCharCodeAt(LOperand* string, LOperand* index) {
+ inputs_[0] = string;
+ inputs_[1] = index;
+ }
+
+ LOperand* string() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt)
+};
+
+
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LStringCharFromCode(LOperand* char_code) {
+ inputs_[0] = char_code;
+ }
+
+ LOperand* char_code() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+ DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+};
+
+
+class LCheckFunction: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckFunction(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type")
+ DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType)
+};
+
+
+class LCheckMaps: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckMaps(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMaps, "check-maps")
+ DECLARE_HYDROGEN_ACCESSOR(CheckMaps)
+};
+
+
+class LCheckSmi: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LCheckSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check-smi")
+};
+
+
+class LClampDToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampDToUint8(LOperand* unclamped) {
+ inputs_[0] = unclamped;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampIToUint8(LOperand* unclamped) {
+ inputs_[0] = unclamped;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LClampTToUint8(LOperand* unclamped,
+ LOperand* temp_xmm) {
+ inputs_[0] = unclamped;
+ temps_[0] = temp_xmm;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+ LOperand* temp_xmm() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8")
+};
+
+
+class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
+ public:
+ explicit LCheckNonSmi(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check-non-smi")
+ DECLARE_HYDROGEN_ACCESSOR(CheckHeapObject)
+};
+
+
+class LAllocate: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LAllocate(LOperand* size, LOperand* temp) {
+ inputs_[0] = size;
+ temps_[0] = temp;
+ }
+
+ LOperand* size() { return inputs_[0]; }
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Allocate, "allocate")
+ DECLARE_HYDROGEN_ACCESSOR(Allocate)
+};
+
+
+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)
+};
+
+
+class LToFastProperties: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LToFastProperties(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
+};
+
+
+class LTypeofIsAndBranch: public LControlInstruction<1, 0> {
+ public:
+ explicit LTypeofIsAndBranch(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+
+ 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;
+ }
+
+ LOperand* temp() { return temps_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch,
+ "is-construct-call-and-branch")
+ DECLARE_HYDROGEN_ACCESSOR(IsConstructCallAndBranch)
+};
+
+
+class LOsrEntry: public LTemplateInstruction<0, 0, 0> {
+ public:
+ LOsrEntry() {}
+
+ virtual bool HasInterestingComment(LCodeGen* gen) const { return false; }
+ DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr-entry")
+};
+
+
+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 LForInPrepareMap: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LForInPrepareMap(LOperand* object) {
+ inputs_[0] = object;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap, "for-in-prepare-map")
+};
+
+
+class LForInCacheArray: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LForInCacheArray(LOperand* map) {
+ inputs_[0] = map;
+ }
+
+ LOperand* map() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray, "for-in-cache-array")
+
+ int idx() {
+ return HForInCacheArray::cast(this->hydrogen_value())->idx();
+ }
+};
+
+
+class LCheckMapValue: public LTemplateInstruction<0, 2, 0> {
+ public:
+ LCheckMapValue(LOperand* value, LOperand* map) {
+ inputs_[0] = value;
+ inputs_[1] = map;
+ }
+
+ LOperand* value() { return inputs_[0]; }
+ LOperand* map() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckMapValue, "check-map-value")
+};
+
+
+class LLoadFieldByIndex: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LLoadFieldByIndex(LOperand* object, LOperand* index) {
+ inputs_[0] = object;
+ inputs_[1] = index;
+ }
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* index() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex, "load-field-by-index")
+};
+
+
+class LChunkBuilder;
+class LPlatformChunk: public LChunk {
+ public:
+ LPlatformChunk(CompilationInfo* info, HGraph* graph)
+ : LChunk(info, graph) { }
+
+ int GetNextSpillIndex(bool is_double);
+ LOperand* GetNextSpillSlot(bool is_double);
+};
+
+
+class LChunkBuilder BASE_EMBEDDED {
+ public:
+ LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
+ : chunk_(NULL),
+ info_(info),
+ graph_(graph),
+ zone_(graph->zone()),
+ 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_(BailoutId::None()) { }
+
+ // Build the sequence for the graph.
+ LPlatformChunk* Build();
+
+ // Declare methods that deal with the individual node types.
+#define DECLARE_DO(type) LInstruction* Do##type(H##type* node);
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
+#undef DECLARE_DO
+
+ static HValue* SimplifiedDivisorForMathFloorOfDiv(HValue* val);
+
+ LInstruction* DoMathFloor(HUnaryMathOperation* instr);
+ LInstruction* DoMathRound(HUnaryMathOperation* instr);
+ LInstruction* DoMathAbs(HUnaryMathOperation* instr);
+ LInstruction* DoMathLog(HUnaryMathOperation* instr);
+ LInstruction* DoMathSin(HUnaryMathOperation* instr);
+ LInstruction* DoMathCos(HUnaryMathOperation* instr);
+ LInstruction* DoMathTan(HUnaryMathOperation* instr);
+ LInstruction* DoMathExp(HUnaryMathOperation* instr);
+ LInstruction* DoMathSqrt(HUnaryMathOperation* instr);
+ LInstruction* DoMathPowHalf(HUnaryMathOperation* instr);
+
+ private:
+ enum Status {
+ UNUSED,
+ BUILDING,
+ DONE,
+ ABORTED
+ };
+
+ LPlatformChunk* chunk() const { return chunk_; }
+ CompilationInfo* info() const { return info_; }
+ HGraph* graph() const { return graph_; }
+ Zone* zone() const { return zone_; }
+
+ 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(BailoutReason reason);
+
+ // Methods for getting operands for Use / Define / Temp.
+ LUnallocated* ToUnallocated(Register reg);
+ LUnallocated* ToUnallocated(XMMRegister 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,
+ XMMRegister 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 a constant operand.
+ MUST_USE_RESULT LOperand* UseConstant(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(XMMRegister 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,
+ XMMRegister reg);
+ // Assigns an environment to an instruction. An instruction which can
+ // deoptimize must have an environment.
+ LInstruction* AssignEnvironment(LInstruction* instr);
+ // Assigns a pointer map to an instruction. An instruction which can
+ // trigger a GC or a lazy deoptimization must have a pointer map.
+ LInstruction* AssignPointerMap(LInstruction* instr);
+
+ enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY };
+
+ // Marks a call for the register allocator. Assigns a pointer map to
+ // support GC and lazy deoptimization. Assigns an environment to support
+ // eager deoptimization if CAN_DEOPTIMIZE_EAGERLY.
+ LInstruction* MarkAsCall(
+ LInstruction* instr,
+ HInstruction* hinstr,
+ CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
+
+ LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
+ int* argument_index_accumulator,
+ ZoneList<HValue*>* objects_to_materialize);
+
+ void VisitInstruction(HInstruction* current);
+
+ void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
+ LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr);
+ LInstruction* DoArithmeticD(Token::Value op,
+ HArithmeticBinaryOperation* instr);
+ LInstruction* DoArithmeticT(Token::Value op,
+ HArithmeticBinaryOperation* instr);
+
+ LPlatformChunk* chunk_;
+ CompilationInfo* info_;
+ HGraph* const graph_;
+ Zone* zone_;
+ Status status_;
+ HInstruction* current_instruction_;
+ HBasicBlock* current_block_;
+ HBasicBlock* next_block_;
+ int argument_count_;
+ LAllocator* allocator_;
+ int position_;
+ LInstruction* instruction_pending_deoptimization_environment_;
+ BailoutId pending_deoptimization_ast_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
+};
+
+#undef DECLARE_HYDROGEN_ACCESSOR
+#undef DECLARE_CONCRETE_INSTRUCTION
+
+} } // namespace v8::int
+
+#endif // V8_X64_LITHIUM_X64_H_
diff --git a/chromium/v8/src/x64/macro-assembler-x64.cc b/chromium/v8/src/x64/macro-assembler-x64.cc
new file mode 100644
index 00000000000..9c9b1620e58
--- /dev/null
+++ b/chromium/v8/src/x64/macro-assembler-x64.cc
@@ -0,0 +1,4703 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "bootstrapper.h"
+#include "codegen.h"
+#include "cpu-profiler.h"
+#include "assembler-x64.h"
+#include "macro-assembler-x64.h"
+#include "serialize.h"
+#include "debug.h"
+#include "heap.h"
+
+namespace v8 {
+namespace internal {
+
+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(),
+ isolate());
+ }
+}
+
+
+static const int kInvalidRootRegisterDelta = -1;
+
+
+intptr_t MacroAssembler::RootRegisterDelta(ExternalReference other) {
+ if (predictable_code_size() &&
+ (other.address() < reinterpret_cast<Address>(isolate()) ||
+ other.address() >= reinterpret_cast<Address>(isolate() + 1))) {
+ return kInvalidRootRegisterDelta;
+ }
+ Address roots_register_value = kRootRegisterBias +
+ reinterpret_cast<Address>(isolate()->heap()->roots_array_start());
+ intptr_t delta = other.address() - roots_register_value;
+ return delta;
+}
+
+
+Operand MacroAssembler::ExternalOperand(ExternalReference target,
+ Register scratch) {
+ if (root_array_available_ && !Serializer::enabled()) {
+ intptr_t delta = RootRegisterDelta(target);
+ if (delta != kInvalidRootRegisterDelta && is_int32(delta)) {
+ Serializer::TooLateToEnableNow();
+ return Operand(kRootRegister, static_cast<int32_t>(delta));
+ }
+ }
+ movq(scratch, target);
+ return Operand(scratch, 0);
+}
+
+
+void MacroAssembler::Load(Register destination, ExternalReference source) {
+ if (root_array_available_ && !Serializer::enabled()) {
+ intptr_t delta = RootRegisterDelta(source);
+ if (delta != kInvalidRootRegisterDelta && is_int32(delta)) {
+ Serializer::TooLateToEnableNow();
+ movq(destination, Operand(kRootRegister, static_cast<int32_t>(delta)));
+ return;
+ }
+ }
+ // Safe code.
+ if (destination.is(rax)) {
+ load_rax(source);
+ } else {
+ movq(kScratchRegister, source);
+ movq(destination, Operand(kScratchRegister, 0));
+ }
+}
+
+
+void MacroAssembler::Store(ExternalReference destination, Register source) {
+ if (root_array_available_ && !Serializer::enabled()) {
+ intptr_t delta = RootRegisterDelta(destination);
+ if (delta != kInvalidRootRegisterDelta && is_int32(delta)) {
+ Serializer::TooLateToEnableNow();
+ movq(Operand(kRootRegister, static_cast<int32_t>(delta)), source);
+ return;
+ }
+ }
+ // Safe code.
+ if (source.is(rax)) {
+ store_rax(destination);
+ } else {
+ movq(kScratchRegister, destination);
+ movq(Operand(kScratchRegister, 0), source);
+ }
+}
+
+
+void MacroAssembler::LoadAddress(Register destination,
+ ExternalReference source) {
+ if (root_array_available_ && !Serializer::enabled()) {
+ intptr_t delta = RootRegisterDelta(source);
+ if (delta != kInvalidRootRegisterDelta && is_int32(delta)) {
+ Serializer::TooLateToEnableNow();
+ lea(destination, Operand(kRootRegister, static_cast<int32_t>(delta)));
+ return;
+ }
+ }
+ // Safe code.
+ movq(destination, source);
+}
+
+
+int MacroAssembler::LoadAddressSize(ExternalReference source) {
+ if (root_array_available_ && !Serializer::enabled()) {
+ // This calculation depends on the internals of LoadAddress.
+ // It's correctness is ensured by the asserts in the Call
+ // instruction below.
+ intptr_t delta = RootRegisterDelta(source);
+ if (delta != kInvalidRootRegisterDelta && is_int32(delta)) {
+ Serializer::TooLateToEnableNow();
+ // Operand is lea(scratch, Operand(kRootRegister, delta));
+ // Opcodes : REX.W 8D ModRM Disp8/Disp32 - 4 or 7.
+ int size = 4;
+ if (!is_int8(static_cast<int32_t>(delta))) {
+ size += 3; // Need full four-byte displacement in lea.
+ }
+ return size;
+ }
+ }
+ // Size of movq(destination, src);
+ return Assembler::kMoveAddressIntoScratchRegisterInstructionLength;
+}
+
+
+void MacroAssembler::PushAddress(ExternalReference source) {
+ int64_t address = reinterpret_cast<int64_t>(source.address());
+ if (is_int32(address) && !Serializer::enabled()) {
+ if (emit_debug_code()) {
+ movq(kScratchRegister, BitCast<int64_t>(kZapValue), RelocInfo::NONE64);
+ }
+ push(Immediate(static_cast<int32_t>(address)));
+ return;
+ }
+ LoadAddress(kScratchRegister, source);
+ push(kScratchRegister);
+}
+
+
+void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) {
+ ASSERT(root_array_available_);
+ movq(destination, Operand(kRootRegister,
+ (index << kPointerSizeLog2) - kRootRegisterBias));
+}
+
+
+void MacroAssembler::LoadRootIndexed(Register destination,
+ Register variable_offset,
+ int fixed_offset) {
+ ASSERT(root_array_available_);
+ movq(destination,
+ Operand(kRootRegister,
+ variable_offset, times_pointer_size,
+ (fixed_offset << kPointerSizeLog2) - kRootRegisterBias));
+}
+
+
+void MacroAssembler::StoreRoot(Register source, Heap::RootListIndex index) {
+ ASSERT(root_array_available_);
+ movq(Operand(kRootRegister, (index << kPointerSizeLog2) - kRootRegisterBias),
+ source);
+}
+
+
+void MacroAssembler::PushRoot(Heap::RootListIndex index) {
+ ASSERT(root_array_available_);
+ push(Operand(kRootRegister, (index << kPointerSizeLog2) - kRootRegisterBias));
+}
+
+
+void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) {
+ ASSERT(root_array_available_);
+ cmpq(with, Operand(kRootRegister,
+ (index << kPointerSizeLog2) - kRootRegisterBias));
+}
+
+
+void MacroAssembler::CompareRoot(const Operand& with,
+ Heap::RootListIndex index) {
+ ASSERT(root_array_available_);
+ ASSERT(!with.AddressUsesRegister(kScratchRegister));
+ LoadRoot(kScratchRegister, index);
+ cmpq(with, kScratchRegister);
+}
+
+
+void MacroAssembler::RememberedSetHelper(Register object, // For debug tests.
+ Register addr,
+ Register scratch,
+ SaveFPRegsMode save_fp,
+ RememberedSetFinalAction and_then) {
+ if (emit_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);
+ }
+}
+
+
+void MacroAssembler::InNewSpace(Register object,
+ Register scratch,
+ Condition cc,
+ Label* branch,
+ 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
+ // case the size of the new space is different between the snapshot maker
+ // and the running system.
+ if (scratch.is(object)) {
+ movq(kScratchRegister, ExternalReference::new_space_mask(isolate()));
+ and_(scratch, kScratchRegister);
+ } else {
+ movq(scratch, ExternalReference::new_space_mask(isolate()));
+ and_(scratch, object);
+ }
+ movq(kScratchRegister, ExternalReference::new_space_start(isolate()));
+ cmpq(scratch, kScratchRegister);
+ j(cc, branch, distance);
+ } else {
+ ASSERT(is_int32(static_cast<int64_t>(HEAP->NewSpaceMask())));
+ intptr_t new_space_start =
+ reinterpret_cast<intptr_t>(HEAP->NewSpaceStart());
+ movq(kScratchRegister, -new_space_start, RelocInfo::NONE64);
+ if (scratch.is(object)) {
+ addq(scratch, kScratchRegister);
+ } else {
+ lea(scratch, Operand(object, kScratchRegister, times_1, 0));
+ }
+ and_(scratch, Immediate(static_cast<int32_t>(HEAP->NewSpaceMask())));
+ j(cc, branch, distance);
+ }
+}
+
+
+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(!value.is(rsi) && !dst.is(rsi));
+
+ // 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) {
+ 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 clobbered input registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (emit_debug_code()) {
+ movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE64);
+ movq(dst, BitCast<int64_t>(kZapValue), RelocInfo::NONE64);
+ }
+}
+
+
+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) {
+ JumpIfSmi(value, &done);
+ }
+
+ // 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 clobbered input registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (emit_debug_code()) {
+ movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE64);
+ movq(index, BitCast<int64_t>(kZapValue), RelocInfo::NONE64);
+ }
+}
+
+
+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));
+ AssertNotSmi(object);
+
+ if (remembered_set_action == OMIT_REMEMBERED_SET &&
+ !FLAG_incremental_marking) {
+ return;
+ }
+
+ if (emit_debug_code()) {
+ Label ok;
+ cmpq(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 the young generation.
+ Label done;
+
+ if (smi_check == INLINE_SMI_CHECK) {
+ // Skip barrier if writing a smi.
+ JumpIfSmi(value, &done);
+ }
+
+ 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 clobbered registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (emit_debug_code()) {
+ movq(address, BitCast<int64_t>(kZapValue), RelocInfo::NONE64);
+ movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE64);
+ }
+}
+
+
+void MacroAssembler::Assert(Condition cc, BailoutReason reason) {
+ if (emit_debug_code()) Check(cc, reason);
+}
+
+
+void MacroAssembler::AssertFastElements(Register elements) {
+ if (emit_debug_code()) {
+ Label ok;
+ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
+ j(equal, &ok, Label::kNear);
+ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
+ Heap::kFixedDoubleArrayMapRootIndex);
+ j(equal, &ok, Label::kNear);
+ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
+ Heap::kFixedCOWArrayMapRootIndex);
+ j(equal, &ok, Label::kNear);
+ Abort(kJSObjectWithFastElementsMapHasSlowElements);
+ bind(&ok);
+ }
+}
+
+
+void MacroAssembler::Check(Condition cc, BailoutReason reason) {
+ Label L;
+ j(cc, &L, Label::kNear);
+ Abort(reason);
+ // Control will not return here.
+ bind(&L);
+}
+
+
+void MacroAssembler::CheckStackAlignment() {
+ int frame_alignment = OS::ActivationFrameAlignment();
+ int frame_alignment_mask = frame_alignment - 1;
+ if (frame_alignment > kPointerSize) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ Label alignment_as_expected;
+ testq(rsp, Immediate(frame_alignment_mask));
+ j(zero, &alignment_as_expected, Label::kNear);
+ // Abort if stack is not aligned.
+ int3();
+ bind(&alignment_as_expected);
+ }
+}
+
+
+void MacroAssembler::NegativeZeroTest(Register result,
+ Register op,
+ Label* then_label) {
+ Label ok;
+ testl(result, result);
+ j(not_zero, &ok, Label::kNear);
+ testl(op, op);
+ j(sign, then_label);
+ bind(&ok);
+}
+
+
+void MacroAssembler::Abort(BailoutReason reason) {
+ // We want to pass the msg string like a smi to avoid GC
+ // problems, however msg is not guaranteed to be aligned
+ // properly. Instead, we pass an aligned pointer that is
+ // a proper v8 smi, but also pass the alignment difference
+ // from the real pointer as a smi.
+ const char* msg = GetBailoutReason(reason);
+ 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.
+ ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
+#ifdef DEBUG
+ if (msg != NULL) {
+ RecordComment("Abort message: ");
+ RecordComment(msg);
+ }
+#endif
+ push(rax);
+ movq(kScratchRegister, p0, RelocInfo::NONE64);
+ push(kScratchRegister);
+ movq(kScratchRegister,
+ reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(p1 - p0))),
+ RelocInfo::NONE64);
+ push(kScratchRegister);
+
+ 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, TypeFeedbackId ast_id) {
+ ASSERT(AllowThisStubCall(stub)); // Calls are not allowed in some stubs
+ Call(stub->GetCode(isolate()), RelocInfo::CODE_TARGET, ast_id);
+}
+
+
+void MacroAssembler::TailCallStub(CodeStub* stub) {
+ ASSERT(allow_stub_calls_ ||
+ stub->CompilingCallsToThisStubIsGCSafe(isolate()));
+ Jump(stub->GetCode(isolate()), RelocInfo::CODE_TARGET);
+}
+
+
+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(isolate());
+}
+
+
+void MacroAssembler::IllegalOperation(int num_arguments) {
+ if (num_arguments > 0) {
+ addq(rsp, Immediate(num_arguments * kPointerSize));
+ }
+ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+}
+
+
+void MacroAssembler::IndexFromHash(Register hash, Register index) {
+ // The assert checks that the constants for the maximum number of digits
+ // for an array index cached in the hash field and the number of bits
+ // reserved for it does not conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. Even if we subsequently go to
+ // the slow case, converting the key to a smi is always valid.
+ // key: string key
+ // hash: key's hash field, including its array index value.
+ and_(hash, Immediate(String::kArrayIndexValueMask));
+ shr(hash, Immediate(String::kHashShift));
+ // Here we actually clobber the key which will be used if calling into
+ // runtime later. However as the new key is the numeric value of a string key
+ // there is no difference in using either key.
+ Integer32ToSmi(index, hash);
+}
+
+
+void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
+ CallRuntime(Runtime::FunctionForId(id), num_arguments);
+}
+
+
+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, kSaveFPRegs);
+ CallStub(&ces);
+}
+
+
+void MacroAssembler::CallRuntime(const Runtime::Function* f,
+ int num_arguments) {
+ // If the expected number of arguments of the runtime function is
+ // constant, we check that the actual number of arguments match the
+ // expectation.
+ if (f->nargs >= 0 && f->nargs != num_arguments) {
+ IllegalOperation(num_arguments);
+ return;
+ }
+
+ // 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);
+ CallStub(&ces);
+}
+
+
+void MacroAssembler::CallExternalReference(const ExternalReference& ext,
+ int num_arguments) {
+ Set(rax, num_arguments);
+ LoadAddress(rbx, ext);
+
+ CEntryStub stub(1);
+ CallStub(&stub);
+}
+
+
+void MacroAssembler::TailCallExternalReference(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);
+ JumpToExternalReference(ext, result_size);
+}
+
+
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(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.
+ ASSERT(static_cast<int>(offset) == offset);
+ return static_cast<int>(offset);
+}
+
+
+void MacroAssembler::PrepareCallApiFunction(int arg_stack_space,
+ bool returns_handle) {
+#if defined(_WIN64) && !defined(__MINGW64__)
+ if (!returns_handle) {
+ EnterApiExitFrame(arg_stack_space);
+ return;
+ }
+ // We need to prepare a slot for result handle on stack and put
+ // a pointer to it into 1st arg register.
+ EnterApiExitFrame(arg_stack_space + 1);
+
+ // rcx must be used to pass the pointer to the return value slot.
+ lea(rcx, StackSpaceOperand(arg_stack_space));
+#else
+ EnterApiExitFrame(arg_stack_space);
+#endif
+}
+
+
+void MacroAssembler::CallApiFunctionAndReturn(Address function_address,
+ Address thunk_address,
+ Register thunk_last_arg,
+ int stack_space,
+ bool returns_handle,
+ int return_value_offset) {
+ Label prologue;
+ Label promote_scheduled_exception;
+ Label delete_allocated_handles;
+ Label leave_exit_frame;
+ Label write_back;
+
+ Factory* factory = isolate()->factory();
+ ExternalReference next_address =
+ ExternalReference::handle_scope_next_address(isolate());
+ const int kNextOffset = 0;
+ const int kLimitOffset = Offset(
+ ExternalReference::handle_scope_limit_address(isolate()),
+ next_address);
+ const int kLevelOffset = Offset(
+ ExternalReference::handle_scope_level_address(isolate()),
+ next_address);
+ ExternalReference scheduled_exception_address =
+ ExternalReference::scheduled_exception_address(isolate());
+
+ // Allocate HandleScope in callee-save registers.
+ Register prev_next_address_reg = r14;
+ Register prev_limit_reg = rbx;
+ Register base_reg = r15;
+ movq(base_reg, next_address);
+ movq(prev_next_address_reg, Operand(base_reg, kNextOffset));
+ movq(prev_limit_reg, Operand(base_reg, kLimitOffset));
+ addl(Operand(base_reg, kLevelOffset), Immediate(1));
+
+ if (FLAG_log_timer_events) {
+ FrameScope frame(this, StackFrame::MANUAL);
+ PushSafepointRegisters();
+ PrepareCallCFunction(1);
+ LoadAddress(arg_reg_1, ExternalReference::isolate_address(isolate()));
+ CallCFunction(ExternalReference::log_enter_external_function(isolate()), 1);
+ PopSafepointRegisters();
+ }
+
+
+ Label profiler_disabled;
+ Label end_profiler_check;
+ bool* is_profiling_flag =
+ isolate()->cpu_profiler()->is_profiling_address();
+ STATIC_ASSERT(sizeof(*is_profiling_flag) == 1);
+ movq(rax, is_profiling_flag, RelocInfo::EXTERNAL_REFERENCE);
+ cmpb(Operand(rax, 0), Immediate(0));
+ j(zero, &profiler_disabled);
+
+ // Third parameter is the address of the actual getter function.
+ movq(thunk_last_arg, function_address, RelocInfo::EXTERNAL_REFERENCE);
+ movq(rax, thunk_address, RelocInfo::EXTERNAL_REFERENCE);
+ jmp(&end_profiler_check);
+
+ bind(&profiler_disabled);
+ // Call the api function!
+ movq(rax, reinterpret_cast<int64_t>(function_address),
+ RelocInfo::EXTERNAL_REFERENCE);
+
+ bind(&end_profiler_check);
+
+ // Call the api function!
+ call(rax);
+
+ if (FLAG_log_timer_events) {
+ FrameScope frame(this, StackFrame::MANUAL);
+ PushSafepointRegisters();
+ PrepareCallCFunction(1);
+ LoadAddress(arg_reg_1, ExternalReference::isolate_address(isolate()));
+ CallCFunction(ExternalReference::log_leave_external_function(isolate()), 1);
+ PopSafepointRegisters();
+ }
+
+ // Can skip the result check for new-style callbacks
+ // TODO(dcarney): may need to pass this information down
+ // as some function_addresses might not have been registered
+ if (returns_handle) {
+ Label empty_result;
+#if defined(_WIN64) && !defined(__MINGW64__)
+ // rax keeps a pointer to v8::Handle, unpack it.
+ movq(rax, Operand(rax, 0));
+#endif
+ // Check if the result handle holds 0.
+ testq(rax, rax);
+ j(zero, &empty_result);
+ // It was non-zero. Dereference to get the result value.
+ movq(rax, Operand(rax, 0));
+ jmp(&prologue);
+ bind(&empty_result);
+ }
+ // Load the value from ReturnValue
+ movq(rax, Operand(rbp, return_value_offset * kPointerSize));
+ bind(&prologue);
+
+ // No more valid handles (the result handle was the last one). Restore
+ // previous handle scope.
+ subl(Operand(base_reg, kLevelOffset), Immediate(1));
+ movq(Operand(base_reg, kNextOffset), prev_next_address_reg);
+ cmpq(prev_limit_reg, Operand(base_reg, kLimitOffset));
+ j(not_equal, &delete_allocated_handles);
+ bind(&leave_exit_frame);
+
+ // Check if the function scheduled an exception.
+ movq(rsi, scheduled_exception_address);
+ Cmp(Operand(rsi, 0), factory->the_hole_value());
+ j(not_equal, &promote_scheduled_exception);
+
+#if ENABLE_EXTRA_CHECKS
+ // Check if the function returned a valid JavaScript value.
+ Label ok;
+ Register return_value = rax;
+ Register map = rcx;
+
+ JumpIfSmi(return_value, &ok, Label::kNear);
+ movq(map, FieldOperand(return_value, HeapObject::kMapOffset));
+
+ CmpInstanceType(map, FIRST_NONSTRING_TYPE);
+ j(below, &ok, Label::kNear);
+
+ CmpInstanceType(map, FIRST_SPEC_OBJECT_TYPE);
+ j(above_equal, &ok, Label::kNear);
+
+ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
+ j(equal, &ok, Label::kNear);
+
+ CompareRoot(return_value, Heap::kUndefinedValueRootIndex);
+ j(equal, &ok, Label::kNear);
+
+ CompareRoot(return_value, Heap::kTrueValueRootIndex);
+ j(equal, &ok, Label::kNear);
+
+ CompareRoot(return_value, Heap::kFalseValueRootIndex);
+ j(equal, &ok, Label::kNear);
+
+ CompareRoot(return_value, Heap::kNullValueRootIndex);
+ j(equal, &ok, Label::kNear);
+
+ Abort(kAPICallReturnedInvalidObject);
+
+ bind(&ok);
+#endif
+
+ LeaveApiExitFrame();
+ ret(stack_space * kPointerSize);
+
+ bind(&promote_scheduled_exception);
+ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
+
+ // HandleScope limit has changed. Delete allocated extensions.
+ bind(&delete_allocated_handles);
+ movq(Operand(base_reg, kLimitOffset), prev_limit_reg);
+ movq(prev_limit_reg, rax);
+ LoadAddress(arg_reg_1, ExternalReference::isolate_address(isolate()));
+ LoadAddress(rax,
+ ExternalReference::delete_handle_scope_extensions(isolate()));
+ call(rax);
+ movq(rax, prev_limit_reg);
+ jmp(&leave_exit_frame);
+}
+
+
+void MacroAssembler::JumpToExternalReference(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);
+ jmp(ces.GetCode(isolate()), RelocInfo::CODE_TARGET);
+}
+
+
+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());
+
+ // Rely on the assertion to check that the number of provided
+ // arguments match the expected number of arguments. Fake a
+ // parameter count to avoid emitting code to do the check.
+ ParameterCount expected(0);
+ GetBuiltinEntry(rdx, id);
+ InvokeCode(rdx, expected, expected, flag, call_wrapper, CALL_AS_METHOD);
+}
+
+
+void MacroAssembler::GetBuiltinFunction(Register target,
+ Builtins::JavaScript id) {
+ // Load the builtins object into target register.
+ movq(target, Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ movq(target, FieldOperand(target, GlobalObject::kBuiltinsOffset));
+ movq(target, FieldOperand(target,
+ JSBuiltinsObject::OffsetOfFunctionWithId(id)));
+}
+
+
+void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
+ ASSERT(!target.is(rdi));
+ // Load the JavaScript builtin function from the builtins object.
+ GetBuiltinFunction(rdi, id);
+ movq(target, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
+}
+
+
+#define REG(Name) { kRegister_ ## Name ## _Code }
+
+static const Register saved_regs[] = {
+ REG(rax), REG(rcx), REG(rdx), REG(rbx), REG(rbp), REG(rsi), REG(rdi), REG(r8),
+ REG(r9), REG(r10), REG(r11)
+};
+
+#undef REG
+
+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) {
+ subq(rsp, Immediate(kDoubleSize * XMMRegister::kMaxNumRegisters));
+ for (int i = 0; i < XMMRegister::kMaxNumRegisters; 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) {
+ for (int i = 0; i < XMMRegister::kMaxNumRegisters; i++) {
+ XMMRegister reg = XMMRegister::from_code(i);
+ movsd(reg, Operand(rsp, i * kDoubleSize));
+ }
+ addq(rsp, Immediate(kDoubleSize * XMMRegister::kMaxNumRegisters));
+ }
+ 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);
+ } else if (is_uint32(x)) {
+ movl(dst, Immediate(static_cast<uint32_t>(x)));
+ } else if (is_int32(x)) {
+ movq(dst, Immediate(static_cast<int32_t>(x)));
+ } else {
+ movq(dst, x, RelocInfo::NONE64);
+ }
+}
+
+
+void MacroAssembler::Set(const Operand& dst, int64_t x) {
+ if (is_int32(x)) {
+ movq(dst, Immediate(static_cast<int32_t>(x)));
+ } else {
+ Set(kScratchRegister, x);
+ movq(dst, kScratchRegister);
+ }
+}
+
+
+bool MacroAssembler::IsUnsafeInt(const int x) {
+ static const int kMaxBits = 17;
+ return !is_intn(x, kMaxBits);
+}
+
+
+void MacroAssembler::SafeMove(Register dst, Smi* src) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(kSmiValueSize == 32); // JIT cookie can be converted to Smi.
+ if (IsUnsafeInt(src->value()) && jit_cookie() != 0) {
+ Move(dst, Smi::FromInt(src->value() ^ jit_cookie()));
+ Move(kScratchRegister, Smi::FromInt(jit_cookie()));
+ xor_(dst, kScratchRegister);
+ } else {
+ Move(dst, src);
+ }
+}
+
+
+void MacroAssembler::SafePush(Smi* src) {
+ ASSERT(kSmiValueSize == 32); // JIT cookie can be converted to Smi.
+ if (IsUnsafeInt(src->value()) && jit_cookie() != 0) {
+ Push(Smi::FromInt(src->value() ^ jit_cookie()));
+ Move(kScratchRegister, Smi::FromInt(jit_cookie()));
+ xor_(Operand(rsp, 0), kScratchRegister);
+ } else {
+ Push(src);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// Smi tagging, untagging and tag detection.
+
+Register MacroAssembler::GetSmiConstant(Smi* source) {
+ int value = source->value();
+ if (value == 0) {
+ xorl(kScratchRegister, kScratchRegister);
+ return kScratchRegister;
+ }
+ if (value == 1) {
+ return kSmiConstantRegister;
+ }
+ LoadSmiConstant(kScratchRegister, source);
+ return kScratchRegister;
+}
+
+
+void MacroAssembler::LoadSmiConstant(Register dst, Smi* source) {
+ if (emit_debug_code()) {
+ movq(dst,
+ reinterpret_cast<uint64_t>(Smi::FromInt(kSmiConstantRegisterValue)),
+ RelocInfo::NONE64);
+ cmpq(dst, kSmiConstantRegister);
+ if (allow_stub_calls()) {
+ Assert(equal, kUninitializedKSmiConstantRegister);
+ } else {
+ Label ok;
+ j(equal, &ok, Label::kNear);
+ int3();
+ bind(&ok);
+ }
+ }
+ int value = source->value();
+ if (value == 0) {
+ xorl(dst, dst);
+ return;
+ }
+ bool negative = value < 0;
+ unsigned int uvalue = negative ? -value : value;
+
+ switch (uvalue) {
+ case 9:
+ lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_8, 0));
+ break;
+ case 8:
+ xorl(dst, dst);
+ lea(dst, Operand(dst, kSmiConstantRegister, times_8, 0));
+ break;
+ case 4:
+ xorl(dst, dst);
+ lea(dst, Operand(dst, kSmiConstantRegister, times_4, 0));
+ break;
+ case 5:
+ lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_4, 0));
+ break;
+ case 3:
+ lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_2, 0));
+ break;
+ case 2:
+ lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_1, 0));
+ break;
+ case 1:
+ movq(dst, kSmiConstantRegister);
+ break;
+ case 0:
+ UNREACHABLE();
+ return;
+ default:
+ movq(dst, reinterpret_cast<uint64_t>(source), RelocInfo::NONE64);
+ return;
+ }
+ if (negative) {
+ neg(dst);
+ }
+}
+
+
+void MacroAssembler::Integer32ToSmi(Register dst, Register src) {
+ STATIC_ASSERT(kSmiTag == 0);
+ if (!dst.is(src)) {
+ movl(dst, src);
+ }
+ shl(dst, Immediate(kSmiShift));
+}
+
+
+void MacroAssembler::Integer32ToSmiField(const Operand& dst, Register src) {
+ if (emit_debug_code()) {
+ testb(dst, Immediate(0x01));
+ Label ok;
+ j(zero, &ok, Label::kNear);
+ if (allow_stub_calls()) {
+ Abort(kInteger32ToSmiFieldWritingToNonSmiLocation);
+ } else {
+ int3();
+ }
+ bind(&ok);
+ }
+ ASSERT(kSmiShift % kBitsPerByte == 0);
+ movl(Operand(dst, kSmiShift / kBitsPerByte), src);
+}
+
+
+void MacroAssembler::Integer64PlusConstantToSmi(Register dst,
+ Register src,
+ int constant) {
+ if (dst.is(src)) {
+ addl(dst, Immediate(constant));
+ } else {
+ leal(dst, Operand(src, constant));
+ }
+ shl(dst, Immediate(kSmiShift));
+}
+
+
+void MacroAssembler::SmiToInteger32(Register dst, Register src) {
+ STATIC_ASSERT(kSmiTag == 0);
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ shr(dst, Immediate(kSmiShift));
+}
+
+
+void MacroAssembler::SmiToInteger32(Register dst, const Operand& src) {
+ movl(dst, Operand(src, kSmiShift / kBitsPerByte));
+}
+
+
+void MacroAssembler::SmiToInteger64(Register dst, Register src) {
+ STATIC_ASSERT(kSmiTag == 0);
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ sar(dst, Immediate(kSmiShift));
+}
+
+
+void MacroAssembler::SmiToInteger64(Register dst, const Operand& src) {
+ movsxlq(dst, Operand(src, kSmiShift / kBitsPerByte));
+}
+
+
+void MacroAssembler::SmiTest(Register src) {
+ AssertSmi(src);
+ testq(src, src);
+}
+
+
+void MacroAssembler::SmiCompare(Register smi1, Register smi2) {
+ AssertSmi(smi1);
+ AssertSmi(smi2);
+ cmpq(smi1, smi2);
+}
+
+
+void MacroAssembler::SmiCompare(Register dst, Smi* src) {
+ AssertSmi(dst);
+ Cmp(dst, src);
+}
+
+
+void MacroAssembler::Cmp(Register dst, Smi* src) {
+ ASSERT(!dst.is(kScratchRegister));
+ if (src->value() == 0) {
+ testq(dst, dst);
+ } else {
+ Register constant_reg = GetSmiConstant(src);
+ cmpq(dst, constant_reg);
+ }
+}
+
+
+void MacroAssembler::SmiCompare(Register dst, const Operand& src) {
+ AssertSmi(dst);
+ AssertSmi(src);
+ cmpq(dst, src);
+}
+
+
+void MacroAssembler::SmiCompare(const Operand& dst, Register src) {
+ AssertSmi(dst);
+ AssertSmi(src);
+ cmpq(dst, src);
+}
+
+
+void MacroAssembler::SmiCompare(const Operand& dst, Smi* src) {
+ AssertSmi(dst);
+ cmpl(Operand(dst, kSmiShift / kBitsPerByte), Immediate(src->value()));
+}
+
+
+void MacroAssembler::Cmp(const Operand& dst, Smi* src) {
+ // The Operand cannot use the smi register.
+ Register smi_reg = GetSmiConstant(src);
+ ASSERT(!dst.AddressUsesRegister(smi_reg));
+ cmpq(dst, smi_reg);
+}
+
+
+void MacroAssembler::SmiCompareInteger32(const Operand& dst, Register src) {
+ cmpl(Operand(dst, kSmiShift / kBitsPerByte), src);
+}
+
+
+void MacroAssembler::PositiveSmiTimesPowerOfTwoToInteger64(Register dst,
+ Register src,
+ int power) {
+ ASSERT(power >= 0);
+ ASSERT(power < 64);
+ if (power == 0) {
+ SmiToInteger64(dst, src);
+ return;
+ }
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ if (power < kSmiShift) {
+ sar(dst, Immediate(kSmiShift - power));
+ } else if (power > kSmiShift) {
+ shl(dst, Immediate(power - kSmiShift));
+ }
+}
+
+
+void MacroAssembler::PositiveSmiDivPowerOfTwoToInteger32(Register dst,
+ Register src,
+ int power) {
+ ASSERT((0 <= power) && (power < 32));
+ if (dst.is(src)) {
+ shr(dst, Immediate(power + kSmiShift));
+ } else {
+ UNIMPLEMENTED(); // Not used.
+ }
+}
+
+
+void MacroAssembler::SmiOrIfSmis(Register dst, Register src1, Register src2,
+ Label* on_not_smis,
+ Label::Distance near_jump) {
+ if (dst.is(src1) || dst.is(src2)) {
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ movq(kScratchRegister, src1);
+ or_(kScratchRegister, src2);
+ JumpIfNotSmi(kScratchRegister, on_not_smis, near_jump);
+ movq(dst, kScratchRegister);
+ } else {
+ movq(dst, src1);
+ or_(dst, src2);
+ JumpIfNotSmi(dst, on_not_smis, near_jump);
+ }
+}
+
+
+Condition MacroAssembler::CheckSmi(Register src) {
+ STATIC_ASSERT(kSmiTag == 0);
+ testb(src, Immediate(kSmiTagMask));
+ return zero;
+}
+
+
+Condition MacroAssembler::CheckSmi(const Operand& src) {
+ STATIC_ASSERT(kSmiTag == 0);
+ testb(src, Immediate(kSmiTagMask));
+ return zero;
+}
+
+
+Condition MacroAssembler::CheckNonNegativeSmi(Register src) {
+ STATIC_ASSERT(kSmiTag == 0);
+ // Test that both bits of the mask 0x8000000000000001 are zero.
+ movq(kScratchRegister, src);
+ rol(kScratchRegister, Immediate(1));
+ testb(kScratchRegister, Immediate(3));
+ return zero;
+}
+
+
+Condition MacroAssembler::CheckBothSmi(Register first, Register second) {
+ if (first.is(second)) {
+ return CheckSmi(first);
+ }
+ STATIC_ASSERT(kSmiTag == 0 && kHeapObjectTag == 1 && kHeapObjectTagMask == 3);
+ leal(kScratchRegister, Operand(first, second, times_1, 0));
+ testb(kScratchRegister, Immediate(0x03));
+ return zero;
+}
+
+
+Condition MacroAssembler::CheckBothNonNegativeSmi(Register first,
+ Register second) {
+ if (first.is(second)) {
+ return CheckNonNegativeSmi(first);
+ }
+ movq(kScratchRegister, first);
+ or_(kScratchRegister, second);
+ rol(kScratchRegister, Immediate(1));
+ testl(kScratchRegister, Immediate(3));
+ return zero;
+}
+
+
+Condition MacroAssembler::CheckEitherSmi(Register first,
+ Register second,
+ Register scratch) {
+ if (first.is(second)) {
+ return CheckSmi(first);
+ }
+ if (scratch.is(second)) {
+ andl(scratch, first);
+ } else {
+ if (!scratch.is(first)) {
+ movl(scratch, first);
+ }
+ andl(scratch, second);
+ }
+ testb(scratch, Immediate(kSmiTagMask));
+ return zero;
+}
+
+
+Condition MacroAssembler::CheckIsMinSmi(Register src) {
+ ASSERT(!src.is(kScratchRegister));
+ // If we overflow by subtracting one, it's the minimal smi value.
+ cmpq(src, kSmiConstantRegister);
+ return overflow;
+}
+
+
+Condition MacroAssembler::CheckInteger32ValidSmiValue(Register src) {
+ // A 32-bit integer value can always be converted to a smi.
+ return always;
+}
+
+
+Condition MacroAssembler::CheckUInteger32ValidSmiValue(Register src) {
+ // An unsigned 32-bit integer value is valid as long as the high bit
+ // is not set.
+ testl(src, src);
+ return positive;
+}
+
+
+void MacroAssembler::CheckSmiToIndicator(Register dst, Register src) {
+ if (dst.is(src)) {
+ andl(dst, Immediate(kSmiTagMask));
+ } else {
+ movl(dst, Immediate(kSmiTagMask));
+ andl(dst, src);
+ }
+}
+
+
+void MacroAssembler::CheckSmiToIndicator(Register dst, const Operand& src) {
+ if (!(src.AddressUsesRegister(dst))) {
+ movl(dst, Immediate(kSmiTagMask));
+ andl(dst, src);
+ } else {
+ movl(dst, src);
+ andl(dst, Immediate(kSmiTagMask));
+ }
+}
+
+
+void MacroAssembler::JumpIfNotValidSmiValue(Register src,
+ Label* on_invalid,
+ Label::Distance near_jump) {
+ Condition is_valid = CheckInteger32ValidSmiValue(src);
+ j(NegateCondition(is_valid), on_invalid, near_jump);
+}
+
+
+void MacroAssembler::JumpIfUIntNotValidSmiValue(Register src,
+ Label* on_invalid,
+ Label::Distance near_jump) {
+ Condition is_valid = CheckUInteger32ValidSmiValue(src);
+ j(NegateCondition(is_valid), on_invalid, near_jump);
+}
+
+
+void MacroAssembler::JumpIfSmi(Register src,
+ Label* on_smi,
+ Label::Distance near_jump) {
+ Condition smi = CheckSmi(src);
+ j(smi, on_smi, near_jump);
+}
+
+
+void MacroAssembler::JumpIfNotSmi(Register src,
+ Label* on_not_smi,
+ Label::Distance near_jump) {
+ Condition smi = CheckSmi(src);
+ j(NegateCondition(smi), on_not_smi, near_jump);
+}
+
+
+void MacroAssembler::JumpUnlessNonNegativeSmi(
+ Register src, Label* on_not_smi_or_negative,
+ Label::Distance near_jump) {
+ Condition non_negative_smi = CheckNonNegativeSmi(src);
+ j(NegateCondition(non_negative_smi), on_not_smi_or_negative, near_jump);
+}
+
+
+void MacroAssembler::JumpIfSmiEqualsConstant(Register src,
+ Smi* constant,
+ Label* on_equals,
+ Label::Distance near_jump) {
+ SmiCompare(src, constant);
+ j(equal, on_equals, near_jump);
+}
+
+
+void MacroAssembler::JumpIfNotBothSmi(Register src1,
+ Register src2,
+ Label* on_not_both_smi,
+ Label::Distance near_jump) {
+ Condition both_smi = CheckBothSmi(src1, src2);
+ j(NegateCondition(both_smi), on_not_both_smi, near_jump);
+}
+
+
+void MacroAssembler::JumpUnlessBothNonNegativeSmi(Register src1,
+ Register src2,
+ Label* on_not_both_smi,
+ Label::Distance near_jump) {
+ Condition both_smi = CheckBothNonNegativeSmi(src1, src2);
+ j(NegateCondition(both_smi), on_not_both_smi, near_jump);
+}
+
+
+void MacroAssembler::SmiTryAddConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ // Does not assume that src is a smi.
+ ASSERT_EQ(static_cast<int>(1), static_cast<int>(kSmiTagMask));
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src.is(kScratchRegister));
+
+ JumpIfNotSmi(src, on_not_smi_result, near_jump);
+ Register tmp = (dst.is(src) ? kScratchRegister : dst);
+ LoadSmiConstant(tmp, constant);
+ addq(tmp, src);
+ j(overflow, on_not_smi_result, near_jump);
+ if (dst.is(src)) {
+ movq(dst, tmp);
+ }
+}
+
+
+void MacroAssembler::SmiAddConstant(Register dst, Register src, Smi* constant) {
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ return;
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ switch (constant->value()) {
+ case 1:
+ addq(dst, kSmiConstantRegister);
+ return;
+ case 2:
+ lea(dst, Operand(src, kSmiConstantRegister, times_2, 0));
+ return;
+ case 4:
+ lea(dst, Operand(src, kSmiConstantRegister, times_4, 0));
+ return;
+ case 8:
+ lea(dst, Operand(src, kSmiConstantRegister, times_8, 0));
+ return;
+ default:
+ Register constant_reg = GetSmiConstant(constant);
+ addq(dst, constant_reg);
+ return;
+ }
+ } else {
+ switch (constant->value()) {
+ case 1:
+ lea(dst, Operand(src, kSmiConstantRegister, times_1, 0));
+ return;
+ case 2:
+ lea(dst, Operand(src, kSmiConstantRegister, times_2, 0));
+ return;
+ case 4:
+ lea(dst, Operand(src, kSmiConstantRegister, times_4, 0));
+ return;
+ case 8:
+ lea(dst, Operand(src, kSmiConstantRegister, times_8, 0));
+ return;
+ default:
+ LoadSmiConstant(dst, constant);
+ addq(dst, src);
+ return;
+ }
+ }
+}
+
+
+void MacroAssembler::SmiAddConstant(const Operand& dst, Smi* constant) {
+ if (constant->value() != 0) {
+ addl(Operand(dst, kSmiShift / kBitsPerByte), Immediate(constant->value()));
+ }
+}
+
+
+void MacroAssembler::SmiAddConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+
+ LoadSmiConstant(kScratchRegister, constant);
+ addq(kScratchRegister, src);
+ j(overflow, on_not_smi_result, near_jump);
+ movq(dst, kScratchRegister);
+ } else {
+ LoadSmiConstant(dst, constant);
+ addq(dst, src);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiSubConstant(Register dst, Register src, Smi* constant) {
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ Register constant_reg = GetSmiConstant(constant);
+ subq(dst, constant_reg);
+ } else {
+ if (constant->value() == Smi::kMinValue) {
+ LoadSmiConstant(dst, constant);
+ // Adding and subtracting the min-value gives the same result, it only
+ // differs on the overflow bit, which we don't check here.
+ addq(dst, src);
+ } else {
+ // Subtract by adding the negation.
+ LoadSmiConstant(dst, Smi::FromInt(-constant->value()));
+ addq(dst, src);
+ }
+ }
+}
+
+
+void MacroAssembler::SmiSubConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ if (constant->value() == Smi::kMinValue) {
+ // Subtracting min-value from any non-negative value will overflow.
+ // We test the non-negativeness before doing the subtraction.
+ testq(src, src);
+ j(not_sign, on_not_smi_result, near_jump);
+ LoadSmiConstant(kScratchRegister, constant);
+ subq(dst, kScratchRegister);
+ } else {
+ // Subtract by adding the negation.
+ LoadSmiConstant(kScratchRegister, Smi::FromInt(-constant->value()));
+ addq(kScratchRegister, dst);
+ j(overflow, on_not_smi_result, near_jump);
+ movq(dst, kScratchRegister);
+ }
+ } else {
+ if (constant->value() == Smi::kMinValue) {
+ // Subtracting min-value from any non-negative value will overflow.
+ // We test the non-negativeness before doing the subtraction.
+ testq(src, src);
+ j(not_sign, on_not_smi_result, near_jump);
+ LoadSmiConstant(dst, constant);
+ // Adding and subtracting the min-value gives the same result, it only
+ // differs on the overflow bit, which we don't check here.
+ addq(dst, src);
+ } else {
+ // Subtract by adding the negation.
+ LoadSmiConstant(dst, Smi::FromInt(-(constant->value())));
+ addq(dst, src);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+ }
+}
+
+
+void MacroAssembler::SmiNeg(Register dst,
+ Register src,
+ Label* on_smi_result,
+ Label::Distance near_jump) {
+ if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ movq(kScratchRegister, src);
+ neg(dst); // Low 32 bits are retained as zero by negation.
+ // Test if result is zero or Smi::kMinValue.
+ cmpq(dst, kScratchRegister);
+ j(not_equal, on_smi_result, near_jump);
+ movq(src, kScratchRegister);
+ } else {
+ movq(dst, src);
+ neg(dst);
+ cmpq(dst, src);
+ // If the result is zero or Smi::kMinValue, negation failed to create a smi.
+ j(not_equal, on_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiAdd(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT_NOT_NULL(on_not_smi_result);
+ ASSERT(!dst.is(src2));
+ if (dst.is(src1)) {
+ movq(kScratchRegister, src1);
+ addq(kScratchRegister, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ movq(dst, kScratchRegister);
+ } else {
+ movq(dst, src1);
+ addq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiAdd(Register dst,
+ Register src1,
+ const Operand& src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT_NOT_NULL(on_not_smi_result);
+ if (dst.is(src1)) {
+ movq(kScratchRegister, src1);
+ addq(kScratchRegister, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ movq(dst, kScratchRegister);
+ } else {
+ ASSERT(!src2.AddressUsesRegister(dst));
+ movq(dst, src1);
+ addq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiAdd(Register dst,
+ Register src1,
+ Register src2) {
+ // No overflow checking. Use only when it's known that
+ // overflowing is impossible.
+ if (!dst.is(src1)) {
+ if (emit_debug_code()) {
+ movq(kScratchRegister, src1);
+ addq(kScratchRegister, src2);
+ Check(no_overflow, kSmiAdditionOverflow);
+ }
+ lea(dst, Operand(src1, src2, times_1, 0));
+ } else {
+ addq(dst, src2);
+ Assert(no_overflow, kSmiAdditionOverflow);
+ }
+}
+
+
+void MacroAssembler::SmiSub(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT_NOT_NULL(on_not_smi_result);
+ ASSERT(!dst.is(src2));
+ if (dst.is(src1)) {
+ cmpq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ subq(dst, src2);
+ } else {
+ movq(dst, src1);
+ subq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiSub(Register dst, Register src1, Register src2) {
+ // No overflow checking. Use only when it's known that
+ // overflowing is impossible (e.g., subtracting two positive smis).
+ ASSERT(!dst.is(src2));
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
+ subq(dst, src2);
+ Assert(no_overflow, kSmiSubtractionOverflow);
+}
+
+
+void MacroAssembler::SmiSub(Register dst,
+ Register src1,
+ const Operand& src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT_NOT_NULL(on_not_smi_result);
+ if (dst.is(src1)) {
+ movq(kScratchRegister, src2);
+ cmpq(src1, kScratchRegister);
+ j(overflow, on_not_smi_result, near_jump);
+ subq(src1, kScratchRegister);
+ } else {
+ movq(dst, src1);
+ subq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiSub(Register dst,
+ Register src1,
+ const Operand& src2) {
+ // No overflow checking. Use only when it's known that
+ // overflowing is impossible (e.g., subtracting two positive smis).
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
+ subq(dst, src2);
+ Assert(no_overflow, kSmiSubtractionOverflow);
+}
+
+
+void MacroAssembler::SmiMul(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT(!dst.is(src2));
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+
+ if (dst.is(src1)) {
+ Label failure, zero_correct_result;
+ movq(kScratchRegister, src1); // Create backup for later testing.
+ SmiToInteger64(dst, src1);
+ imul(dst, src2);
+ j(overflow, &failure, Label::kNear);
+
+ // Check for negative zero result. If product is zero, and one
+ // argument is negative, go to slow case.
+ Label correct_result;
+ testq(dst, dst);
+ j(not_zero, &correct_result, Label::kNear);
+
+ movq(dst, kScratchRegister);
+ xor_(dst, src2);
+ // Result was positive zero.
+ j(positive, &zero_correct_result, Label::kNear);
+
+ bind(&failure); // Reused failure exit, restores src1.
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result, near_jump);
+
+ bind(&zero_correct_result);
+ Set(dst, 0);
+
+ bind(&correct_result);
+ } else {
+ SmiToInteger64(dst, src1);
+ imul(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ // Check for negative zero result. If product is zero, and one
+ // argument is negative, go to slow case.
+ Label correct_result;
+ testq(dst, dst);
+ j(not_zero, &correct_result, Label::kNear);
+ // One of src1 and src2 is zero, the check whether the other is
+ // negative.
+ movq(kScratchRegister, src1);
+ xor_(kScratchRegister, src2);
+ j(negative, on_not_smi_result, near_jump);
+ bind(&correct_result);
+ }
+}
+
+
+void MacroAssembler::SmiDiv(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src2.is(rax));
+ ASSERT(!src2.is(rdx));
+ ASSERT(!src1.is(rdx));
+
+ // Check for 0 divisor (result is +/-Infinity).
+ testq(src2, src2);
+ j(zero, on_not_smi_result, near_jump);
+
+ if (src1.is(rax)) {
+ movq(kScratchRegister, src1);
+ }
+ SmiToInteger32(rax, src1);
+ // We need to rule out dividing Smi::kMinValue by -1, since that would
+ // overflow in idiv and raise an exception.
+ // We combine this with negative zero test (negative zero only happens
+ // when dividing zero by a negative number).
+
+ // We overshoot a little and go to slow case if we divide min-value
+ // by any negative value, not just -1.
+ Label safe_div;
+ testl(rax, Immediate(0x7fffffff));
+ j(not_zero, &safe_div, Label::kNear);
+ testq(src2, src2);
+ if (src1.is(rax)) {
+ j(positive, &safe_div, Label::kNear);
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result, near_jump);
+ } else {
+ j(negative, on_not_smi_result, near_jump);
+ }
+ bind(&safe_div);
+
+ SmiToInteger32(src2, src2);
+ // Sign extend src1 into edx:eax.
+ cdq();
+ idivl(src2);
+ Integer32ToSmi(src2, src2);
+ // Check that the remainder is zero.
+ testl(rdx, rdx);
+ if (src1.is(rax)) {
+ Label smi_result;
+ j(zero, &smi_result, Label::kNear);
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result, near_jump);
+ bind(&smi_result);
+ } else {
+ j(not_zero, on_not_smi_result, near_jump);
+ }
+ if (!dst.is(src1) && src1.is(rax)) {
+ movq(src1, kScratchRegister);
+ }
+ Integer32ToSmi(dst, rax);
+}
+
+
+void MacroAssembler::SmiMod(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!src2.is(rax));
+ ASSERT(!src2.is(rdx));
+ ASSERT(!src1.is(rdx));
+ ASSERT(!src1.is(src2));
+
+ testq(src2, src2);
+ j(zero, on_not_smi_result, near_jump);
+
+ if (src1.is(rax)) {
+ movq(kScratchRegister, src1);
+ }
+ SmiToInteger32(rax, src1);
+ SmiToInteger32(src2, src2);
+
+ // Test for the edge case of dividing Smi::kMinValue by -1 (will overflow).
+ Label safe_div;
+ cmpl(rax, Immediate(Smi::kMinValue));
+ j(not_equal, &safe_div, Label::kNear);
+ cmpl(src2, Immediate(-1));
+ j(not_equal, &safe_div, Label::kNear);
+ // Retag inputs and go slow case.
+ Integer32ToSmi(src2, src2);
+ if (src1.is(rax)) {
+ movq(src1, kScratchRegister);
+ }
+ jmp(on_not_smi_result, near_jump);
+ bind(&safe_div);
+
+ // Sign extend eax into edx:eax.
+ cdq();
+ idivl(src2);
+ // Restore smi tags on inputs.
+ Integer32ToSmi(src2, src2);
+ if (src1.is(rax)) {
+ movq(src1, kScratchRegister);
+ }
+ // Check for a negative zero result. If the result is zero, and the
+ // dividend is negative, go slow to return a floating point negative zero.
+ Label smi_result;
+ testl(rdx, rdx);
+ j(not_zero, &smi_result, Label::kNear);
+ testq(src1, src1);
+ j(negative, on_not_smi_result, near_jump);
+ bind(&smi_result);
+ Integer32ToSmi(dst, rdx);
+}
+
+
+void MacroAssembler::SmiNot(Register dst, Register src) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src.is(kScratchRegister));
+ // Set tag and padding bits before negating, so that they are zero afterwards.
+ movl(kScratchRegister, Immediate(~0));
+ if (dst.is(src)) {
+ xor_(dst, kScratchRegister);
+ } else {
+ lea(dst, Operand(src, kScratchRegister, times_1, 0));
+ }
+ not_(dst);
+}
+
+
+void MacroAssembler::SmiAnd(Register dst, Register src1, Register src2) {
+ ASSERT(!dst.is(src2));
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
+ and_(dst, src2);
+}
+
+
+void MacroAssembler::SmiAndConstant(Register dst, Register src, Smi* constant) {
+ if (constant->value() == 0) {
+ Set(dst, 0);
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ Register constant_reg = GetSmiConstant(constant);
+ and_(dst, constant_reg);
+ } else {
+ LoadSmiConstant(dst, constant);
+ and_(dst, src);
+ }
+}
+
+
+void MacroAssembler::SmiOr(Register dst, Register src1, Register src2) {
+ if (!dst.is(src1)) {
+ ASSERT(!src1.is(src2));
+ movq(dst, src1);
+ }
+ or_(dst, src2);
+}
+
+
+void MacroAssembler::SmiOrConstant(Register dst, Register src, Smi* constant) {
+ if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ Register constant_reg = GetSmiConstant(constant);
+ or_(dst, constant_reg);
+ } else {
+ LoadSmiConstant(dst, constant);
+ or_(dst, src);
+ }
+}
+
+
+void MacroAssembler::SmiXor(Register dst, Register src1, Register src2) {
+ if (!dst.is(src1)) {
+ ASSERT(!src1.is(src2));
+ movq(dst, src1);
+ }
+ xor_(dst, src2);
+}
+
+
+void MacroAssembler::SmiXorConstant(Register dst, Register src, Smi* constant) {
+ if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ Register constant_reg = GetSmiConstant(constant);
+ xor_(dst, constant_reg);
+ } else {
+ LoadSmiConstant(dst, constant);
+ xor_(dst, src);
+ }
+}
+
+
+void MacroAssembler::SmiShiftArithmeticRightConstant(Register dst,
+ Register src,
+ int shift_value) {
+ ASSERT(is_uint5(shift_value));
+ if (shift_value > 0) {
+ if (dst.is(src)) {
+ sar(dst, Immediate(shift_value + kSmiShift));
+ shl(dst, Immediate(kSmiShift));
+ } else {
+ UNIMPLEMENTED(); // Not used.
+ }
+ }
+}
+
+
+void MacroAssembler::SmiShiftLeftConstant(Register dst,
+ Register src,
+ int shift_value) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ if (shift_value > 0) {
+ shl(dst, Immediate(shift_value));
+ }
+}
+
+
+void MacroAssembler::SmiShiftLogicalRightConstant(
+ Register dst, Register src, int shift_value,
+ Label* on_not_smi_result, Label::Distance near_jump) {
+ // Logic right shift interprets its result as an *unsigned* number.
+ if (dst.is(src)) {
+ UNIMPLEMENTED(); // Not used.
+ } else {
+ movq(dst, src);
+ if (shift_value == 0) {
+ testq(dst, dst);
+ j(negative, on_not_smi_result, near_jump);
+ }
+ shr(dst, Immediate(shift_value + kSmiShift));
+ shl(dst, Immediate(kSmiShift));
+ }
+}
+
+
+void MacroAssembler::SmiShiftLeft(Register dst,
+ Register src1,
+ Register src2) {
+ ASSERT(!dst.is(rcx));
+ // Untag shift amount.
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
+ SmiToInteger32(rcx, src2);
+ // Shift amount specified by lower 5 bits, not six as the shl opcode.
+ and_(rcx, Immediate(0x1f));
+ shl_cl(dst);
+}
+
+
+void MacroAssembler::SmiShiftLogicalRight(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!dst.is(rcx));
+ // dst and src1 can be the same, because the one case that bails out
+ // is a shift by 0, which leaves dst, and therefore src1, unchanged.
+ if (src1.is(rcx) || src2.is(rcx)) {
+ movq(kScratchRegister, rcx);
+ }
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
+ SmiToInteger32(rcx, src2);
+ orl(rcx, Immediate(kSmiShift));
+ shr_cl(dst); // Shift is rcx modulo 0x1f + 32.
+ shl(dst, Immediate(kSmiShift));
+ testq(dst, dst);
+ if (src1.is(rcx) || src2.is(rcx)) {
+ Label positive_result;
+ j(positive, &positive_result, Label::kNear);
+ if (src1.is(rcx)) {
+ movq(src1, kScratchRegister);
+ } else {
+ movq(src2, kScratchRegister);
+ }
+ jmp(on_not_smi_result, near_jump);
+ bind(&positive_result);
+ } else {
+ // src2 was zero and src1 negative.
+ j(negative, on_not_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiShiftArithmeticRight(Register dst,
+ Register src1,
+ Register src2) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!dst.is(rcx));
+ if (src1.is(rcx)) {
+ movq(kScratchRegister, src1);
+ } else if (src2.is(rcx)) {
+ movq(kScratchRegister, src2);
+ }
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
+ SmiToInteger32(rcx, src2);
+ orl(rcx, Immediate(kSmiShift));
+ sar_cl(dst); // Shift 32 + original rcx & 0x1f.
+ shl(dst, Immediate(kSmiShift));
+ if (src1.is(rcx)) {
+ movq(src1, kScratchRegister);
+ } else if (src2.is(rcx)) {
+ movq(src2, kScratchRegister);
+ }
+}
+
+
+void MacroAssembler::SelectNonSmi(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smis,
+ Label::Distance near_jump) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!dst.is(src1));
+ ASSERT(!dst.is(src2));
+ // Both operands must not be smis.
+#ifdef DEBUG
+ if (allow_stub_calls()) { // Check contains a stub call.
+ Condition not_both_smis = NegateCondition(CheckBothSmi(src1, src2));
+ Check(not_both_smis, kBothRegistersWereSmisInSelectNonSmi);
+ }
+#endif
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT_EQ(0, Smi::FromInt(0));
+ movl(kScratchRegister, Immediate(kSmiTagMask));
+ and_(kScratchRegister, src1);
+ testl(kScratchRegister, src2);
+ // If non-zero then both are smis.
+ j(not_zero, on_not_smis, near_jump);
+
+ // Exactly one operand is a smi.
+ ASSERT_EQ(1, static_cast<int>(kSmiTagMask));
+ // kScratchRegister still holds src1 & kSmiTag, which is either zero or one.
+ subq(kScratchRegister, Immediate(1));
+ // If src1 is a smi, then scratch register all 1s, else it is all 0s.
+ movq(dst, src1);
+ xor_(dst, src2);
+ and_(dst, kScratchRegister);
+ // If src1 is a smi, dst holds src1 ^ src2, else it is zero.
+ xor_(dst, src1);
+ // If src1 is a smi, dst is src2, else it is src1, i.e., the non-smi.
+}
+
+
+SmiIndex MacroAssembler::SmiToIndex(Register dst,
+ Register src,
+ int shift) {
+ ASSERT(is_uint6(shift));
+ // There is a possible optimization if shift is in the range 60-63, but that
+ // will (and must) never happen.
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ if (shift < kSmiShift) {
+ sar(dst, Immediate(kSmiShift - shift));
+ } else {
+ shl(dst, Immediate(shift - kSmiShift));
+ }
+ return SmiIndex(dst, times_1);
+}
+
+SmiIndex MacroAssembler::SmiToNegativeIndex(Register dst,
+ Register src,
+ int shift) {
+ // Register src holds a positive smi.
+ ASSERT(is_uint6(shift));
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ neg(dst);
+ if (shift < kSmiShift) {
+ sar(dst, Immediate(kSmiShift - shift));
+ } else {
+ shl(dst, Immediate(shift - kSmiShift));
+ }
+ return SmiIndex(dst, times_1);
+}
+
+
+void MacroAssembler::AddSmiField(Register dst, const Operand& src) {
+ ASSERT_EQ(0, kSmiShift % kBitsPerByte);
+ addl(dst, Operand(src, kSmiShift / kBitsPerByte));
+}
+
+
+void MacroAssembler::JumpIfNotString(Register object,
+ Register object_map,
+ Label* not_string,
+ Label::Distance near_jump) {
+ Condition is_smi = CheckSmi(object);
+ j(is_smi, not_string, near_jump);
+ CmpObjectType(object, FIRST_NONSTRING_TYPE, object_map);
+ j(above_equal, not_string, near_jump);
+}
+
+
+void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(
+ Register first_object,
+ Register second_object,
+ Register scratch1,
+ Register scratch2,
+ Label* on_fail,
+ Label::Distance near_jump) {
+ // Check that both objects are not smis.
+ Condition either_smi = CheckEitherSmi(first_object, second_object);
+ j(either_smi, on_fail, near_jump);
+
+ // Load instance type for both strings.
+ movq(scratch1, FieldOperand(first_object, HeapObject::kMapOffset));
+ movq(scratch2, FieldOperand(second_object, HeapObject::kMapOffset));
+ movzxbl(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset));
+ movzxbl(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset));
+
+ // Check that both are flat ASCII strings.
+ ASSERT(kNotStringTag != 0);
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+ const int kFlatAsciiStringTag =
+ kStringTag | kOneByteStringTag | kSeqStringTag;
+
+ andl(scratch1, Immediate(kFlatAsciiStringMask));
+ andl(scratch2, Immediate(kFlatAsciiStringMask));
+ // Interleave the bits to check both scratch1 and scratch2 in one test.
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
+ cmpl(scratch1,
+ Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 3)));
+ j(not_equal, on_fail, near_jump);
+}
+
+
+void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(
+ Register instance_type,
+ Register scratch,
+ Label* failure,
+ Label::Distance near_jump) {
+ if (!scratch.is(instance_type)) {
+ movl(scratch, instance_type);
+ }
+
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+
+ andl(scratch, Immediate(kFlatAsciiStringMask));
+ cmpl(scratch, Immediate(kStringTag | kSeqStringTag | kOneByteStringTag));
+ j(not_equal, failure, near_jump);
+}
+
+
+void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first_object_instance_type,
+ Register second_object_instance_type,
+ Register scratch1,
+ Register scratch2,
+ Label* on_fail,
+ Label::Distance near_jump) {
+ // Load instance type for both strings.
+ movq(scratch1, first_object_instance_type);
+ movq(scratch2, second_object_instance_type);
+
+ // Check that both are flat ASCII strings.
+ ASSERT(kNotStringTag != 0);
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+ const int kFlatAsciiStringTag =
+ kStringTag | kOneByteStringTag | kSeqStringTag;
+
+ andl(scratch1, Immediate(kFlatAsciiStringMask));
+ andl(scratch2, Immediate(kFlatAsciiStringMask));
+ // Interleave the bits to check both scratch1 and scratch2 in one test.
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
+ cmpl(scratch1,
+ Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 3)));
+ j(not_equal, on_fail, near_jump);
+}
+
+
+template<class T>
+static void JumpIfNotUniqueNameHelper(MacroAssembler* masm,
+ T operand_or_register,
+ Label* not_unique_name,
+ Label::Distance distance) {
+ STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
+ Label succeed;
+ masm->testb(operand_or_register,
+ Immediate(kIsNotStringMask | kIsNotInternalizedMask));
+ masm->j(zero, &succeed, Label::kNear);
+ masm->cmpb(operand_or_register, Immediate(static_cast<uint8_t>(SYMBOL_TYPE)));
+ masm->j(not_equal, not_unique_name, distance);
+
+ masm->bind(&succeed);
+}
+
+
+void MacroAssembler::JumpIfNotUniqueName(Operand operand,
+ Label* not_unique_name,
+ Label::Distance distance) {
+ JumpIfNotUniqueNameHelper<Operand>(this, operand, not_unique_name, distance);
+}
+
+
+void MacroAssembler::JumpIfNotUniqueName(Register reg,
+ Label* not_unique_name,
+ Label::Distance distance) {
+ JumpIfNotUniqueNameHelper<Register>(this, reg, not_unique_name, distance);
+}
+
+
+void MacroAssembler::Move(Register dst, Register src) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+}
+
+
+void MacroAssembler::Move(Register dst, Handle<Object> source) {
+ AllowDeferredHandleDereference smi_check;
+ if (source->IsSmi()) {
+ Move(dst, Smi::cast(*source));
+ } else {
+ ASSERT(source->IsHeapObject());
+ movq(dst, source, RelocInfo::EMBEDDED_OBJECT);
+ }
+}
+
+
+void MacroAssembler::Move(const Operand& dst, Handle<Object> source) {
+ AllowDeferredHandleDereference smi_check;
+ if (source->IsSmi()) {
+ Move(dst, Smi::cast(*source));
+ } else {
+ ASSERT(source->IsHeapObject());
+ movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
+ movq(dst, kScratchRegister);
+ }
+}
+
+
+void MacroAssembler::Cmp(Register dst, Handle<Object> source) {
+ AllowDeferredHandleDereference smi_check;
+ if (source->IsSmi()) {
+ Cmp(dst, Smi::cast(*source));
+ } else {
+ ASSERT(source->IsHeapObject());
+ movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
+ cmpq(dst, kScratchRegister);
+ }
+}
+
+
+void MacroAssembler::Cmp(const Operand& dst, Handle<Object> source) {
+ AllowDeferredHandleDereference smi_check;
+ if (source->IsSmi()) {
+ Cmp(dst, Smi::cast(*source));
+ } else {
+ ASSERT(source->IsHeapObject());
+ movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
+ cmpq(dst, kScratchRegister);
+ }
+}
+
+
+void MacroAssembler::Push(Handle<Object> source) {
+ AllowDeferredHandleDereference smi_check;
+ if (source->IsSmi()) {
+ Push(Smi::cast(*source));
+ } else {
+ ASSERT(source->IsHeapObject());
+ movq(kScratchRegister, source, RelocInfo::EMBEDDED_OBJECT);
+ push(kScratchRegister);
+ }
+}
+
+
+void MacroAssembler::LoadHeapObject(Register result,
+ Handle<HeapObject> object) {
+ AllowDeferredHandleDereference using_raw_address;
+ if (isolate()->heap()->InNewSpace(*object)) {
+ Handle<Cell> cell = isolate()->factory()->NewCell(object);
+ movq(result, cell, RelocInfo::CELL);
+ movq(result, Operand(result, 0));
+ } else {
+ Move(result, object);
+ }
+}
+
+
+void MacroAssembler::CmpHeapObject(Register reg, Handle<HeapObject> object) {
+ AllowDeferredHandleDereference using_raw_address;
+ if (isolate()->heap()->InNewSpace(*object)) {
+ Handle<Cell> cell = isolate()->factory()->NewCell(object);
+ movq(kScratchRegister, cell, RelocInfo::CELL);
+ cmpq(reg, Operand(kScratchRegister, 0));
+ } else {
+ Cmp(reg, object);
+ }
+}
+
+
+void MacroAssembler::PushHeapObject(Handle<HeapObject> object) {
+ AllowDeferredHandleDereference using_raw_address;
+ if (isolate()->heap()->InNewSpace(*object)) {
+ Handle<Cell> cell = isolate()->factory()->NewCell(object);
+ movq(kScratchRegister, cell, RelocInfo::CELL);
+ movq(kScratchRegister, Operand(kScratchRegister, 0));
+ push(kScratchRegister);
+ } else {
+ Push(object);
+ }
+}
+
+
+void MacroAssembler::LoadGlobalCell(Register dst, Handle<Cell> cell) {
+ if (dst.is(rax)) {
+ AllowDeferredHandleDereference embedding_raw_address;
+ load_rax(cell.location(), RelocInfo::CELL);
+ } else {
+ movq(dst, cell, RelocInfo::CELL);
+ movq(dst, Operand(dst, 0));
+ }
+}
+
+
+void MacroAssembler::Push(Smi* source) {
+ intptr_t smi = reinterpret_cast<intptr_t>(source);
+ if (is_int32(smi)) {
+ push(Immediate(static_cast<int32_t>(smi)));
+ } else {
+ Register constant = GetSmiConstant(source);
+ push(constant);
+ }
+}
+
+
+void MacroAssembler::Drop(int stack_elements) {
+ if (stack_elements > 0) {
+ addq(rsp, Immediate(stack_elements * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::Test(const Operand& src, Smi* source) {
+ testl(Operand(src, kIntSize), Immediate(source->value()));
+}
+
+
+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);
+}
+
+
+void MacroAssembler::Jump(Address destination, RelocInfo::Mode rmode) {
+ movq(kScratchRegister, destination, rmode);
+ jmp(kScratchRegister);
+}
+
+
+void MacroAssembler::Jump(Handle<Code> code_object, RelocInfo::Mode rmode) {
+ // TODO(X64): Inline this
+ jmp(code_object, rmode);
+}
+
+
+int MacroAssembler::CallSize(ExternalReference ext) {
+ // Opcode for call kScratchRegister is: Rex.B FF D4 (three bytes).
+ return LoadAddressSize(ext) +
+ Assembler::kCallScratchRegisterInstructionLength;
+}
+
+
+void MacroAssembler::Call(ExternalReference ext) {
+#ifdef DEBUG
+ int end_position = pc_offset() + CallSize(ext);
+#endif
+ LoadAddress(kScratchRegister, ext);
+ call(kScratchRegister);
+#ifdef DEBUG
+ CHECK_EQ(end_position, pc_offset());
+#endif
+}
+
+
+void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) {
+#ifdef DEBUG
+ int end_position = pc_offset() + CallSize(destination, rmode);
+#endif
+ movq(kScratchRegister, destination, rmode);
+ call(kScratchRegister);
+#ifdef DEBUG
+ CHECK_EQ(pc_offset(), end_position);
+#endif
+}
+
+
+void MacroAssembler::Call(Handle<Code> code_object,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id) {
+#ifdef DEBUG
+ int end_position = pc_offset() + CallSize(code_object);
+#endif
+ ASSERT(RelocInfo::IsCodeTarget(rmode));
+ call(code_object, rmode, ast_id);
+#ifdef DEBUG
+ CHECK_EQ(end_position, pc_offset());
+#endif
+}
+
+
+void MacroAssembler::Pushad() {
+ push(rax);
+ push(rcx);
+ push(rdx);
+ push(rbx);
+ // Not pushing rsp or rbp.
+ push(rsi);
+ push(rdi);
+ push(r8);
+ push(r9);
+ // r10 is kScratchRegister.
+ push(r11);
+ // r12 is kSmiConstantRegister.
+ // r13 is kRootRegister.
+ push(r14);
+ push(r15);
+ STATIC_ASSERT(11 == kNumSafepointSavedRegisters);
+ // Use lea for symmetry with Popad.
+ int sp_delta =
+ (kNumSafepointRegisters - kNumSafepointSavedRegisters) * kPointerSize;
+ lea(rsp, Operand(rsp, -sp_delta));
+}
+
+
+void MacroAssembler::Popad() {
+ // Popad must not change the flags, so use lea instead of addq.
+ int sp_delta =
+ (kNumSafepointRegisters - kNumSafepointSavedRegisters) * kPointerSize;
+ lea(rsp, Operand(rsp, sp_delta));
+ pop(r15);
+ pop(r14);
+ pop(r11);
+ pop(r9);
+ pop(r8);
+ pop(rdi);
+ pop(rsi);
+ pop(rbx);
+ pop(rdx);
+ pop(rcx);
+ pop(rax);
+}
+
+
+void MacroAssembler::Dropad() {
+ addq(rsp, Immediate(kNumSafepointRegisters * kPointerSize));
+}
+
+
+// Order general registers are pushed by Pushad:
+// rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
+const int
+MacroAssembler::kSafepointPushRegisterIndices[Register::kNumRegisters] = {
+ 0,
+ 1,
+ 2,
+ 3,
+ -1,
+ -1,
+ 4,
+ 5,
+ 6,
+ 7,
+ -1,
+ 8,
+ -1,
+ -1,
+ 9,
+ 10
+};
+
+
+void MacroAssembler::StoreToSafepointRegisterSlot(Register dst,
+ const Immediate& imm) {
+ movq(SafepointRegisterSlot(dst), imm);
+}
+
+
+void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) {
+ movq(SafepointRegisterSlot(dst), src);
+}
+
+
+void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
+ movq(dst, SafepointRegisterSlot(src));
+}
+
+
+Operand MacroAssembler::SafepointRegisterSlot(Register reg) {
+ return Operand(rsp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
+}
+
+
+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::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);
+ }
+
+ // 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() {
+ 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_pointer_size, 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);
+ 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());
+ 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.
+
+ // If the handler is a JS frame, restore the context to the frame.
+ // (kind == ENTRY) == (rbp == 0) == (rsi == 0), so we could test either
+ // rbp or rsi.
+ Label skip;
+ testq(rsi, rsi);
+ j(zero, &skip, Label::kNear);
+ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi);
+ bind(&skip);
+
+ JumpToHandlerEntry();
+}
+
+
+void MacroAssembler::ThrowUncatchable(Register value) {
+ // Adjust this code if not the case.
+ STATIC_ASSERT(StackHandlerConstants::kSize == 5 * 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 rax.
+ if (!value.is(rax)) {
+ movq(rax, value);
+ }
+ // Drop the stack pointer to the top of the top stack handler.
+ ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
+ Load(rsp, 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);
+ movq(rsp, Operand(rsp, StackHandlerConstants::kNextOffset));
+
+ 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();
+}
+
+
+void MacroAssembler::Ret() {
+ ret(0);
+}
+
+
+void MacroAssembler::Ret(int bytes_dropped, Register scratch) {
+ if (is_uint16(bytes_dropped)) {
+ ret(bytes_dropped);
+ } else {
+ PopReturnAddressTo(scratch);
+ addq(rsp, Immediate(bytes_dropped));
+ PushReturnAddressFrom(scratch);
+ ret(0);
+ }
+}
+
+
+void MacroAssembler::FCmp() {
+ fucomip();
+ fstp(0);
+}
+
+
+void MacroAssembler::CmpObjectType(Register heap_object,
+ InstanceType type,
+ Register map) {
+ movq(map, FieldOperand(heap_object, HeapObject::kMapOffset));
+ CmpInstanceType(map, type);
+}
+
+
+void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
+ cmpb(FieldOperand(map, Map::kInstanceTypeOffset),
+ Immediate(static_cast<int8_t>(type)));
+}
+
+
+void MacroAssembler::CheckFastElements(Register map,
+ Label* fail,
+ Label::Distance distance) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ STATIC_ASSERT(FAST_ELEMENTS == 2);
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ cmpb(FieldOperand(map, Map::kBitField2Offset),
+ Immediate(Map::kMaximumBitField2FastHoleyElementValue));
+ j(above, fail, distance);
+}
+
+
+void MacroAssembler::CheckFastObjectElements(Register map,
+ Label* fail,
+ Label::Distance distance) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ STATIC_ASSERT(FAST_ELEMENTS == 2);
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
+ cmpb(FieldOperand(map, Map::kBitField2Offset),
+ Immediate(Map::kMaximumBitField2FastHoleySmiElementValue));
+ j(below_equal, fail, distance);
+ cmpb(FieldOperand(map, Map::kBitField2Offset),
+ Immediate(Map::kMaximumBitField2FastHoleyElementValue));
+ j(above, fail, distance);
+}
+
+
+void MacroAssembler::CheckFastSmiElements(Register map,
+ Label* fail,
+ Label::Distance distance) {
+ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
+ cmpb(FieldOperand(map, Map::kBitField2Offset),
+ Immediate(Map::kMaximumBitField2FastHoleySmiElementValue));
+ j(above, fail, distance);
+}
+
+
+void MacroAssembler::StoreNumberToDoubleElements(
+ Register maybe_number,
+ Register elements,
+ Register index,
+ XMMRegister xmm_scratch,
+ Label* fail,
+ int elements_offset) {
+ 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 - elements_offset),
+ 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 - elements_offset),
+ xmm_scratch);
+ bind(&done);
+}
+
+
+void MacroAssembler::CompareMap(Register obj,
+ Handle<Map> map,
+ Label* early_success) {
+ Cmp(FieldOperand(obj, HeapObject::kMapOffset), map);
+}
+
+
+void MacroAssembler::CheckMap(Register obj,
+ Handle<Map> map,
+ Label* fail,
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, fail);
+ }
+
+ Label success;
+ CompareMap(obj, map, &success);
+ j(not_equal, fail);
+ bind(&success);
+}
+
+
+void MacroAssembler::ClampUint8(Register reg) {
+ Label done;
+ testl(reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ setcc(negative, reg); // 1 if negative, 0 if positive.
+ decb(reg); // 0 if negative, 255 if positive.
+ bind(&done);
+}
+
+
+void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg,
+ XMMRegister temp_xmm_reg,
+ Register result_reg) {
+ Label done;
+ Label conv_failure;
+ xorps(temp_xmm_reg, temp_xmm_reg);
+ cvtsd2si(result_reg, input_reg);
+ testl(result_reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ cmpl(result_reg, Immediate(0x80000000));
+ j(equal, &conv_failure, Label::kNear);
+ movl(result_reg, Immediate(0));
+ setcc(above, result_reg);
+ subl(result_reg, Immediate(1));
+ andl(result_reg, Immediate(255));
+ jmp(&done, Label::kNear);
+ bind(&conv_failure);
+ Set(result_reg, 0);
+ ucomisd(input_reg, temp_xmm_reg);
+ j(below, &done, Label::kNear);
+ Set(result_reg, 255);
+ bind(&done);
+}
+
+
+void MacroAssembler::LoadUint32(XMMRegister dst,
+ Register src,
+ XMMRegister scratch) {
+ if (FLAG_debug_code) {
+ cmpq(src, Immediate(0xffffffff));
+ Assert(below_equal, kInputGPRIsExpectedToHaveUpper32Cleared);
+ }
+ cvtqsi2sd(dst, src);
+}
+
+
+void MacroAssembler::LoadInstanceDescriptors(Register map,
+ Register descriptors) {
+ movq(descriptors, FieldOperand(map, Map::kDescriptorsOffset));
+}
+
+
+void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
+ movq(dst, FieldOperand(map, Map::kBitField3Offset));
+ DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
+}
+
+
+void MacroAssembler::EnumLength(Register dst, Register map) {
+ STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
+ movq(dst, FieldOperand(map, Map::kBitField3Offset));
+ Move(kScratchRegister, Smi::FromInt(Map::EnumLengthBits::kMask));
+ and_(dst, kScratchRegister);
+}
+
+
+void MacroAssembler::DispatchMap(Register obj,
+ Register unused,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type) {
+ Label fail;
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, &fail);
+ }
+ Cmp(FieldOperand(obj, HeapObject::kMapOffset), map);
+ j(equal, success, RelocInfo::CODE_TARGET);
+
+ bind(&fail);
+}
+
+
+void MacroAssembler::AssertNumber(Register object) {
+ if (emit_debug_code()) {
+ Label ok;
+ Condition is_smi = CheckSmi(object);
+ j(is_smi, &ok, Label::kNear);
+ Cmp(FieldOperand(object, HeapObject::kMapOffset),
+ isolate()->factory()->heap_number_map());
+ Check(equal, kOperandIsNotANumber);
+ bind(&ok);
+ }
+}
+
+
+void MacroAssembler::AssertNotSmi(Register object) {
+ if (emit_debug_code()) {
+ Condition is_smi = CheckSmi(object);
+ Check(NegateCondition(is_smi), kOperandIsASmi);
+ }
+}
+
+
+void MacroAssembler::AssertSmi(Register object) {
+ if (emit_debug_code()) {
+ Condition is_smi = CheckSmi(object);
+ Check(is_smi, kOperandIsNotASmi);
+ }
+}
+
+
+void MacroAssembler::AssertSmi(const Operand& object) {
+ if (emit_debug_code()) {
+ Condition is_smi = CheckSmi(object);
+ Check(is_smi, kOperandIsNotASmi);
+ }
+}
+
+
+void MacroAssembler::AssertZeroExtended(Register int32_register) {
+ if (emit_debug_code()) {
+ ASSERT(!int32_register.is(kScratchRegister));
+ movq(kScratchRegister, 0x100000000l, RelocInfo::NONE64);
+ cmpq(kScratchRegister, int32_register);
+ Check(above_equal, k32BitValueInRegisterIsNotZeroExtended);
+ }
+}
+
+
+void MacroAssembler::AssertString(Register object) {
+ if (emit_debug_code()) {
+ testb(object, Immediate(kSmiTagMask));
+ Check(not_equal, kOperandIsASmiAndNotAString);
+ push(object);
+ movq(object, FieldOperand(object, HeapObject::kMapOffset));
+ CmpInstanceType(object, FIRST_NONSTRING_TYPE);
+ pop(object);
+ Check(below, kOperandIsNotAString);
+ }
+}
+
+
+void MacroAssembler::AssertName(Register object) {
+ if (emit_debug_code()) {
+ testb(object, Immediate(kSmiTagMask));
+ Check(not_equal, kOperandIsASmiAndNotAName);
+ push(object);
+ movq(object, FieldOperand(object, HeapObject::kMapOffset));
+ CmpInstanceType(object, LAST_NAME_TYPE);
+ pop(object);
+ Check(below_equal, kOperandIsNotAName);
+ }
+}
+
+
+void MacroAssembler::AssertRootValue(Register src,
+ Heap::RootListIndex root_value_index,
+ BailoutReason reason) {
+ if (emit_debug_code()) {
+ ASSERT(!src.is(kScratchRegister));
+ LoadRoot(kScratchRegister, root_value_index);
+ cmpq(src, kScratchRegister);
+ Check(equal, reason);
+ }
+}
+
+
+
+Condition MacroAssembler::IsObjectStringType(Register heap_object,
+ Register map,
+ Register instance_type) {
+ movq(map, FieldOperand(heap_object, HeapObject::kMapOffset));
+ movzxbl(instance_type, FieldOperand(map, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ testb(instance_type, Immediate(kIsNotStringMask));
+ return zero;
+}
+
+
+Condition MacroAssembler::IsObjectNameType(Register heap_object,
+ Register map,
+ Register instance_type) {
+ movq(map, FieldOperand(heap_object, HeapObject::kMapOffset));
+ movzxbl(instance_type, FieldOperand(map, Map::kInstanceTypeOffset));
+ cmpb(instance_type, Immediate(static_cast<uint8_t>(LAST_NAME_TYPE)));
+ return below_equal;
+}
+
+
+void MacroAssembler::TryGetFunctionPrototype(Register function,
+ Register result,
+ Label* miss,
+ bool miss_on_bound_function) {
+ // Check that the receiver isn't a smi.
+ testl(function, Immediate(kSmiTagMask));
+ j(zero, miss);
+
+ // Check that the function really is a 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),
+ Immediate(1 << Map::kHasNonInstancePrototype));
+ j(not_zero, &non_instance, Label::kNear);
+
+ // Get the prototype or initial map from the function.
+ movq(result,
+ FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+
+ // 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.
+ CompareRoot(result, Heap::kTheHoleValueRootIndex);
+ j(equal, miss);
+
+ // If the function does not have an initial map, we're done.
+ Label done;
+ CmpObjectType(result, MAP_TYPE, kScratchRegister);
+ j(not_equal, &done, Label::kNear);
+
+ // Get the prototype from the initial map.
+ movq(result, FieldOperand(result, Map::kPrototypeOffset));
+ jmp(&done, Label::kNear);
+
+ // Non-instance prototype: Fetch prototype from constructor field
+ // in initial map.
+ bind(&non_instance);
+ movq(result, FieldOperand(result, Map::kConstructorOffset));
+
+ // All done.
+ bind(&done);
+}
+
+
+void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ Operand counter_operand = ExternalOperand(ExternalReference(counter));
+ movl(counter_operand, Immediate(value));
+ }
+}
+
+
+void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ Operand counter_operand = ExternalOperand(ExternalReference(counter));
+ if (value == 1) {
+ incl(counter_operand);
+ } else {
+ addl(counter_operand, Immediate(value));
+ }
+ }
+}
+
+
+void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
+ ASSERT(value > 0);
+ if (FLAG_native_code_counters && counter->Enabled()) {
+ Operand counter_operand = ExternalOperand(ExternalReference(counter));
+ if (value == 1) {
+ decl(counter_operand);
+ } else {
+ subl(counter_operand, Immediate(value));
+ }
+ }
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void MacroAssembler::DebugBreak() {
+ Set(rax, 0); // No arguments.
+ LoadAddress(rbx, ExternalReference(Runtime::kDebugBreak, isolate()));
+ CEntryStub ces(1);
+ ASSERT(AllowThisStubCall(&ces));
+ Call(ces.GetCode(isolate()), RelocInfo::DEBUG_BREAK);
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+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 rcx to
+ // follow the calling convention which requires the call type to be
+ // in rcx.
+ ASSERT(dst.is(rcx));
+ if (call_kind == CALL_AS_FUNCTION) {
+ LoadSmiConstant(dst, Smi::FromInt(1));
+ } else {
+ LoadSmiConstant(dst, Smi::FromInt(0));
+ }
+}
+
+
+void MacroAssembler::InvokeCode(Register code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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 (!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);
+ }
+}
+
+
+void MacroAssembler::InvokeCode(Handle<Code> code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ RelocInfo::Mode rmode,
+ 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 (!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);
+ }
+}
+
+
+void MacroAssembler::InvokeFunction(Register function,
+ const ParameterCount& actual,
+ 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));
+ movsxlq(rbx,
+ FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
+ // Advances rdx to the end of the Code object header, to the start of
+ // the executable code.
+ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
+
+ ParameterCount expected(rbx);
+ InvokeCode(rdx, expected, actual, flag, call_wrapper, call_kind);
+}
+
+
+void MacroAssembler::InvokeFunction(Handle<JSFunction> function,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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());
+
+ // Get the function and setup the context.
+ LoadHeapObject(rdi, function);
+ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+
+ // 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, actual, flag, call_wrapper, call_kind);
+}
+
+
+void MacroAssembler::InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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());
+ if (expected.immediate() == actual.immediate()) {
+ definitely_matches = true;
+ } else {
+ Set(rax, actual.immediate());
+ if (expected.immediate() ==
+ SharedFunctionInfo::kDontAdaptArgumentsSentinel) {
+ // Don't worry about adapting arguments for built-ins that
+ // don't want that done. Skip adaption code by making it look
+ // like we have a match between expected and actual number of
+ // arguments.
+ definitely_matches = true;
+ } else {
+ *definitely_mismatches = true;
+ Set(rbx, expected.immediate());
+ }
+ }
+ } else {
+ if (actual.is_immediate()) {
+ // Expected is in register, actual is immediate. This is the
+ // case when we invoke function values without going through the
+ // IC mechanism.
+ cmpq(expected.reg(), Immediate(actual.immediate()));
+ j(equal, &invoke, Label::kNear);
+ ASSERT(expected.reg().is(rbx));
+ Set(rax, actual.immediate());
+ } 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.
+ cmpq(expected.reg(), actual.reg());
+ j(equal, &invoke, Label::kNear);
+ ASSERT(actual.reg().is(rax));
+ ASSERT(expected.reg().is(rbx));
+ }
+ }
+
+ if (!definitely_matches) {
+ Handle<Code> adaptor = isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ if (!code_constant.is_null()) {
+ movq(rdx, code_constant, RelocInfo::EMBEDDED_OBJECT);
+ addq(rdx, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ } else if (!code_register.is(rdx)) {
+ movq(rdx, code_register);
+ }
+
+ if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(adaptor));
+ SetCallKind(rcx, call_kind);
+ Call(adaptor, RelocInfo::CODE_TARGET);
+ call_wrapper.AfterCall();
+ if (!*definitely_mismatches) {
+ jmp(done, near_jump);
+ }
+ } else {
+ SetCallKind(rcx, call_kind);
+ Jump(adaptor, RelocInfo::CODE_TARGET);
+ }
+ bind(&invoke);
+ }
+}
+
+
+void MacroAssembler::EnterFrame(StackFrame::Type type) {
+ push(rbp);
+ movq(rbp, rsp);
+ push(rsi); // Context.
+ Push(Smi::FromInt(type));
+ movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
+ push(kScratchRegister);
+ if (emit_debug_code()) {
+ movq(kScratchRegister,
+ isolate()->factory()->undefined_value(),
+ RelocInfo::EMBEDDED_OBJECT);
+ cmpq(Operand(rsp, 0), kScratchRegister);
+ Check(not_equal, kCodeObjectNotProperlyPatched);
+ }
+}
+
+
+void MacroAssembler::LeaveFrame(StackFrame::Type type) {
+ if (emit_debug_code()) {
+ Move(kScratchRegister, Smi::FromInt(type));
+ cmpq(Operand(rbp, StandardFrameConstants::kMarkerOffset), kScratchRegister);
+ Check(equal, kStackFrameTypesMustMatch);
+ }
+ movq(rsp, rbp);
+ pop(rbp);
+}
+
+
+void MacroAssembler::EnterExitFramePrologue(bool save_rax) {
+ // 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);
+ ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize);
+ push(rbp);
+ movq(rbp, rsp);
+
+ // Reserve room for entry stack pointer and push the code object.
+ ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
+ push(Immediate(0)); // Saved entry sp, patched before call.
+ movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
+ push(kScratchRegister); // Accessed from EditFrame::code_slot.
+
+ // Save the frame pointer and the context in top.
+ if (save_rax) {
+ movq(r14, rax); // Backup rax in callee-save register.
+ }
+
+ Store(ExternalReference(Isolate::kCEntryFPAddress, isolate()), rbp);
+ Store(ExternalReference(Isolate::kContextAddress, isolate()), rsi);
+}
+
+
+void MacroAssembler::EnterExitFrameEpilogue(int arg_stack_space,
+ bool save_doubles) {
+#ifdef _WIN64
+ const int kShadowSpace = 4;
+ arg_stack_space += kShadowSpace;
+#endif
+ // Optionally save all XMM registers.
+ if (save_doubles) {
+ int space = XMMRegister::kMaxNumRegisters * kDoubleSize +
+ arg_stack_space * kPointerSize;
+ subq(rsp, Immediate(space));
+ int offset = -2 * kPointerSize;
+ for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) {
+ XMMRegister reg = XMMRegister::FromAllocationIndex(i);
+ movsd(Operand(rbp, offset - ((i + 1) * kDoubleSize)), reg);
+ }
+ } else if (arg_stack_space > 0) {
+ subq(rsp, Immediate(arg_stack_space * kPointerSize));
+ }
+
+ // Get the required frame alignment for the OS.
+ const int kFrameAlignment = OS::ActivationFrameAlignment();
+ if (kFrameAlignment > 0) {
+ ASSERT(IsPowerOf2(kFrameAlignment));
+ ASSERT(is_int8(kFrameAlignment));
+ and_(rsp, Immediate(-kFrameAlignment));
+ }
+
+ // Patch the saved entry sp.
+ movq(Operand(rbp, ExitFrameConstants::kSPOffset), rsp);
+}
+
+
+void MacroAssembler::EnterExitFrame(int arg_stack_space, bool save_doubles) {
+ EnterExitFramePrologue(true);
+
+ // 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));
+
+ EnterExitFrameEpilogue(arg_stack_space, save_doubles);
+}
+
+
+void MacroAssembler::EnterApiExitFrame(int arg_stack_space) {
+ EnterExitFramePrologue(false);
+ EnterExitFrameEpilogue(arg_stack_space, false);
+}
+
+
+void MacroAssembler::LeaveExitFrame(bool save_doubles) {
+ // Registers:
+ // r15 : argv
+ if (save_doubles) {
+ int offset = -2 * kPointerSize;
+ for (int i = 0; i < XMMRegister::NumAllocatableRegisters(); i++) {
+ XMMRegister reg = XMMRegister::FromAllocationIndex(i);
+ movsd(reg, Operand(rbp, offset - ((i + 1) * kDoubleSize)));
+ }
+ }
+ // Get the return address from the stack and restore the frame pointer.
+ movq(rcx, Operand(rbp, 1 * kPointerSize));
+ movq(rbp, Operand(rbp, 0 * kPointerSize));
+
+ // Drop everything up to and including the arguments and the receiver
+ // from the caller stack.
+ lea(rsp, Operand(r15, 1 * kPointerSize));
+
+ PushReturnAddressFrom(rcx);
+
+ LeaveExitFrameEpilogue();
+}
+
+
+void MacroAssembler::LeaveApiExitFrame() {
+ movq(rsp, rbp);
+ pop(rbp);
+
+ LeaveExitFrameEpilogue();
+}
+
+
+void MacroAssembler::LeaveExitFrameEpilogue() {
+ // Restore current context from top and clear it in debug mode.
+ ExternalReference context_address(Isolate::kContextAddress, isolate());
+ Operand context_operand = ExternalOperand(context_address);
+ movq(rsi, context_operand);
+#ifdef DEBUG
+ movq(context_operand, Immediate(0));
+#endif
+
+ // Clear the top frame.
+ ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress,
+ isolate());
+ Operand c_entry_fp_operand = ExternalOperand(c_entry_fp_address);
+ movq(c_entry_fp_operand, Immediate(0));
+}
+
+
+void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss) {
+ Label same_contexts;
+
+ ASSERT(!holder_reg.is(scratch));
+ ASSERT(!scratch.is(kScratchRegister));
+ // Load current lexical context from the stack frame.
+ movq(scratch, Operand(rbp, StandardFrameConstants::kContextOffset));
+
+ // When generating debug code, make sure the lexical context is set.
+ if (emit_debug_code()) {
+ cmpq(scratch, Immediate(0));
+ Check(not_equal, kWeShouldNotHaveAnEmptyLexicalContext);
+ }
+ // Load the native context of the current context.
+ int offset =
+ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
+ movq(scratch, FieldOperand(scratch, offset));
+ movq(scratch, FieldOperand(scratch, GlobalObject::kNativeContextOffset));
+
+ // Check the context is a native context.
+ if (emit_debug_code()) {
+ Cmp(FieldOperand(scratch, HeapObject::kMapOffset),
+ isolate()->factory()->native_context_map());
+ Check(equal, kJSGlobalObjectNativeContextShouldBeANativeContext);
+ }
+
+ // Check if both contexts are the same.
+ cmpq(scratch, FieldOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+ j(equal, &same_contexts);
+
+ // Compare security tokens.
+ // Check that the security token in the calling global object is
+ // compatible with the security token in the receiving global
+ // object.
+
+ // Check the context is a native context.
+ if (emit_debug_code()) {
+ // Preserve original value of holder_reg.
+ push(holder_reg);
+ movq(holder_reg,
+ FieldOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+ CompareRoot(holder_reg, Heap::kNullValueRootIndex);
+ Check(not_equal, kJSGlobalProxyContextShouldNotBeNull);
+
+ // Read the first word and compare to native_context_map(),
+ movq(holder_reg, FieldOperand(holder_reg, HeapObject::kMapOffset));
+ CompareRoot(holder_reg, Heap::kNativeContextMapRootIndex);
+ Check(equal, kJSGlobalObjectNativeContextShouldBeANativeContext);
+ pop(holder_reg);
+ }
+
+ movq(kScratchRegister,
+ FieldOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
+ int token_offset =
+ Context::kHeaderSize + Context::SECURITY_TOKEN_INDEX * kPointerSize;
+ movq(scratch, FieldOperand(scratch, token_offset));
+ cmpq(scratch, FieldOperand(kScratchRegister, token_offset));
+ j(not_equal, miss);
+
+ bind(&same_contexts);
+}
+
+
+void MacroAssembler::GetNumberHash(Register r0, Register scratch) {
+ // First of all we assign the hash seed to scratch.
+ LoadRoot(scratch, Heap::kHashSeedRootIndex);
+ SmiToInteger32(scratch, scratch);
+
+ // Xor original key with a seed.
+ xorl(r0, scratch);
+
+ // Compute the hash code from the untagged key. This must be kept in sync
+ // with ComputeIntegerHash in utils.h.
+ //
+ // hash = ~hash + (hash << 15);
+ movl(scratch, r0);
+ notl(r0);
+ shll(scratch, Immediate(15));
+ addl(r0, scratch);
+ // hash = hash ^ (hash >> 12);
+ movl(scratch, r0);
+ shrl(scratch, Immediate(12));
+ xorl(r0, scratch);
+ // hash = hash + (hash << 2);
+ leal(r0, Operand(r0, r0, times_4, 0));
+ // hash = hash ^ (hash >> 4);
+ movl(scratch, r0);
+ shrl(scratch, Immediate(4));
+ xorl(r0, scratch);
+ // hash = hash * 2057;
+ imull(r0, r0, Immediate(2057));
+ // hash = hash ^ (hash >> 16);
+ movl(scratch, r0);
+ shrl(scratch, Immediate(16));
+ xorl(r0, scratch);
+}
+
+
+
+void MacroAssembler::LoadFromNumberDictionary(Label* miss,
+ Register elements,
+ Register key,
+ Register r0,
+ Register r1,
+ Register r2,
+ Register result) {
+ // Register use:
+ //
+ // elements - holds the slow-case elements of the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // Scratch registers:
+ //
+ // r0 - holds the untagged key on entry and holds the hash once computed.
+ //
+ // r1 - used to hold the capacity mask of the dictionary
+ //
+ // r2 - used for the index into the dictionary.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the same as 'key' or 'result'.
+ // Unchanged on bailout so 'key' or 'result' can be used
+ // in further computation.
+
+ Label done;
+
+ GetNumberHash(r0, r1);
+
+ // Compute capacity mask.
+ SmiToInteger32(r1, FieldOperand(elements,
+ SeededNumberDictionary::kCapacityOffset));
+ decl(r1);
+
+ // Generate an unrolled loop that performs a few probes before giving up.
+ const int kProbes = 4;
+ for (int i = 0; i < kProbes; i++) {
+ // Use r2 for index calculations and keep the hash intact in r0.
+ movq(r2, r0);
+ // Compute the masked index: (hash + i + i * i) & mask.
+ if (i > 0) {
+ addl(r2, Immediate(SeededNumberDictionary::GetProbeOffset(i)));
+ }
+ and_(r2, r1);
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(SeededNumberDictionary::kEntrySize == 3);
+ lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3
+
+ // Check if the key matches.
+ cmpq(key, FieldOperand(elements,
+ r2,
+ times_pointer_size,
+ SeededNumberDictionary::kElementsStartOffset));
+ if (i != (kProbes - 1)) {
+ j(equal, &done);
+ } else {
+ j(not_equal, miss);
+ }
+ }
+
+ bind(&done);
+ // Check that the value is a normal propety.
+ const int kDetailsOffset =
+ SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize;
+ ASSERT_EQ(NORMAL, 0);
+ Test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset),
+ Smi::FromInt(PropertyDetails::TypeField::kMask));
+ j(not_zero, miss);
+
+ // Get the value at the masked, scaled index.
+ const int kValueOffset =
+ SeededNumberDictionary::kElementsStartOffset + kPointerSize;
+ movq(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset));
+}
+
+
+void MacroAssembler::LoadAllocationTopHelper(Register result,
+ Register scratch,
+ AllocationFlags flags) {
+ ExternalReference allocation_top =
+ AllocationUtils::GetAllocationTopReference(isolate(), flags);
+
+ // Just return if allocation top is already known.
+ if ((flags & RESULT_CONTAINS_TOP) != 0) {
+ // No use of scratch if allocation top is provided.
+ ASSERT(!scratch.is_valid());
+#ifdef DEBUG
+ // Assert that result actually contains top on entry.
+ Operand top_operand = ExternalOperand(allocation_top);
+ cmpq(result, top_operand);
+ Check(equal, kUnexpectedAllocationTop);
+#endif
+ return;
+ }
+
+ // Move address of new object to result. Use scratch register if available,
+ // and keep address in scratch until call to UpdateAllocationTopHelper.
+ if (scratch.is_valid()) {
+ LoadAddress(scratch, allocation_top);
+ movq(result, Operand(scratch, 0));
+ } else {
+ Load(result, allocation_top);
+ }
+}
+
+
+void MacroAssembler::UpdateAllocationTopHelper(Register result_end,
+ Register scratch,
+ AllocationFlags flags) {
+ if (emit_debug_code()) {
+ testq(result_end, Immediate(kObjectAlignmentMask));
+ Check(zero, kUnalignedAllocationInNewSpace);
+ }
+
+ ExternalReference allocation_top =
+ AllocationUtils::GetAllocationTopReference(isolate(), flags);
+
+ // Update new top.
+ if (scratch.is_valid()) {
+ // Scratch already contains address of allocation top.
+ movq(Operand(scratch, 0), result_end);
+ } else {
+ Store(allocation_top, result_end);
+ }
+}
+
+
+void MacroAssembler::Allocate(int object_size,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0);
+ ASSERT(object_size <= Page::kMaxNonCodeHeapObjectSize);
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ movl(result, Immediate(0x7091));
+ if (result_end.is_valid()) {
+ movl(result_end, Immediate(0x7191));
+ }
+ if (scratch.is_valid()) {
+ movl(scratch, Immediate(0x7291));
+ }
+ }
+ jmp(gc_required);
+ return;
+ }
+ ASSERT(!result.is(result_end));
+
+ // Load address of new object into result.
+ LoadAllocationTopHelper(result, scratch, flags);
+
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ if (((flags & DOUBLE_ALIGNMENT) != 0) && FLAG_debug_code) {
+ testq(result, Immediate(kDoubleAlignmentMask));
+ Check(zero, kAllocationIsNotDoubleAligned);
+ }
+
+ // Calculate new top and bail out if new space is exhausted.
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+
+ Register top_reg = result_end.is_valid() ? result_end : result;
+
+ if (!top_reg.is(result)) {
+ movq(top_reg, result);
+ }
+ addq(top_reg, Immediate(object_size));
+ j(carry, gc_required);
+ Operand limit_operand = ExternalOperand(allocation_limit);
+ cmpq(top_reg, limit_operand);
+ j(above, gc_required);
+
+ // Update allocation top.
+ UpdateAllocationTopHelper(top_reg, scratch, flags);
+
+ bool tag_result = (flags & TAG_OBJECT) != 0;
+ if (top_reg.is(result)) {
+ if (tag_result) {
+ subq(result, Immediate(object_size - kHeapObjectTag));
+ } else {
+ subq(result, Immediate(object_size));
+ }
+ } else if (tag_result) {
+ // Tag the result if requested.
+ ASSERT(kHeapObjectTag == 1);
+ incq(result);
+ }
+}
+
+
+void MacroAssembler::Allocate(int header_size,
+ ScaleFactor element_size,
+ Register element_count,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT((flags & SIZE_IN_WORDS) == 0);
+ lea(result_end, Operand(element_count, element_size, header_size));
+ Allocate(result_end, result, result_end, scratch, gc_required, flags);
+}
+
+
+void MacroAssembler::Allocate(Register object_size,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags) {
+ ASSERT((flags & SIZE_IN_WORDS) == 0);
+ if (!FLAG_inline_new) {
+ if (emit_debug_code()) {
+ // Trash the registers to simulate an allocation failure.
+ movl(result, Immediate(0x7091));
+ movl(result_end, Immediate(0x7191));
+ if (scratch.is_valid()) {
+ movl(scratch, Immediate(0x7291));
+ }
+ // object_size is left unchanged by this function.
+ }
+ jmp(gc_required);
+ return;
+ }
+ ASSERT(!result.is(result_end));
+
+ // Load address of new object into result.
+ LoadAllocationTopHelper(result, scratch, flags);
+
+ // Align the next allocation. Storing the filler map without checking top is
+ // always safe because the limit of the heap is always aligned.
+ if (((flags & DOUBLE_ALIGNMENT) != 0) && FLAG_debug_code) {
+ testq(result, Immediate(kDoubleAlignmentMask));
+ Check(zero, kAllocationIsNotDoubleAligned);
+ }
+
+ // Calculate new top and bail out if new space is exhausted.
+ ExternalReference allocation_limit =
+ AllocationUtils::GetAllocationLimitReference(isolate(), flags);
+ if (!object_size.is(result_end)) {
+ movq(result_end, object_size);
+ }
+ addq(result_end, result);
+ j(carry, gc_required);
+ Operand limit_operand = ExternalOperand(allocation_limit);
+ cmpq(result_end, limit_operand);
+ j(above, gc_required);
+
+ // Update allocation top.
+ UpdateAllocationTopHelper(result_end, scratch, flags);
+
+ // Tag the result if requested.
+ if ((flags & TAG_OBJECT) != 0) {
+ addq(result, Immediate(kHeapObjectTag));
+ }
+}
+
+
+void MacroAssembler::UndoAllocationInNewSpace(Register object) {
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+
+ // Make sure the object has no tag before resetting top.
+ and_(object, Immediate(~kHeapObjectTagMask));
+ Operand top_operand = ExternalOperand(new_space_allocation_top);
+#ifdef DEBUG
+ cmpq(object, top_operand);
+ Check(below, kUndoAllocationOfNonAllocatedMemory);
+#endif
+ movq(top_operand, object);
+}
+
+
+void MacroAssembler::AllocateHeapNumber(Register result,
+ Register scratch,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ Allocate(HeapNumber::kSize, result, scratch, no_reg, gc_required, TAG_OBJECT);
+
+ // Set the map.
+ LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+}
+
+
+void MacroAssembler::AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ const int kHeaderAlignment = SeqTwoByteString::kHeaderSize &
+ kObjectAlignmentMask;
+ ASSERT(kShortSize == 2);
+ // scratch1 = length * 2 + kObjectAlignmentMask.
+ lea(scratch1, Operand(length, length, times_1, kObjectAlignmentMask +
+ kHeaderAlignment));
+ and_(scratch1, Immediate(~kObjectAlignmentMask));
+ if (kHeaderAlignment > 0) {
+ subq(scratch1, Immediate(kHeaderAlignment));
+ }
+
+ // Allocate two byte string in new space.
+ Allocate(SeqTwoByteString::kHeaderSize,
+ times_1,
+ scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ LoadRoot(kScratchRegister, Heap::kStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+ Integer32ToSmi(scratch1, length);
+ movq(FieldOperand(result, String::kLengthOffset), scratch1);
+ movq(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ const int kHeaderAlignment = SeqOneByteString::kHeaderSize &
+ kObjectAlignmentMask;
+ movl(scratch1, length);
+ ASSERT(kCharSize == 1);
+ addq(scratch1, Immediate(kObjectAlignmentMask + kHeaderAlignment));
+ and_(scratch1, Immediate(~kObjectAlignmentMask));
+ if (kHeaderAlignment > 0) {
+ subq(scratch1, Immediate(kHeaderAlignment));
+ }
+
+ // Allocate ASCII string in new space.
+ Allocate(SeqOneByteString::kHeaderSize,
+ times_1,
+ scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ LoadRoot(kScratchRegister, Heap::kAsciiStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+ Integer32ToSmi(scratch1, length);
+ movq(FieldOperand(result, String::kLengthOffset), scratch1);
+ movq(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateTwoByteConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ Allocate(ConsString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ LoadRoot(kScratchRegister, Heap::kConsStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+}
+
+
+void MacroAssembler::AllocateAsciiConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ Label allocate_new_space, install_map;
+ AllocationFlags flags = TAG_OBJECT;
+
+ ExternalReference high_promotion_mode = ExternalReference::
+ new_space_high_promotion_mode_active_address(isolate());
+
+ Load(scratch1, high_promotion_mode);
+ testb(scratch1, Immediate(1));
+ j(zero, &allocate_new_space);
+ Allocate(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ static_cast<AllocationFlags>(flags | PRETENURE_OLD_POINTER_SPACE));
+
+ jmp(&install_map);
+
+ bind(&allocate_new_space);
+ Allocate(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ flags);
+
+ bind(&install_map);
+
+ // Set the map. The other fields are left uninitialized.
+ LoadRoot(kScratchRegister, Heap::kConsAsciiStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+}
+
+
+void MacroAssembler::AllocateTwoByteSlicedString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ LoadRoot(kScratchRegister, Heap::kSlicedStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+}
+
+
+void MacroAssembler::AllocateAsciiSlicedString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ LoadRoot(kScratchRegister, Heap::kSlicedAsciiStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+}
+
+
+// Copy memory, byte-by-byte, from source to destination. Not optimized for
+// long or aligned copies. The contents of scratch and length are destroyed.
+// Destination is incremented by length, source, length and scratch are
+// clobbered.
+// A simpler loop is faster on small copies, but slower on large ones.
+// The cld() instruction must have been emitted, to set the direction flag(),
+// before calling this function.
+void MacroAssembler::CopyBytes(Register destination,
+ Register source,
+ Register length,
+ int min_length,
+ Register scratch) {
+ ASSERT(min_length >= 0);
+ if (emit_debug_code()) {
+ cmpl(length, Immediate(min_length));
+ Assert(greater_equal, kInvalidMinLength);
+ }
+ Label loop, done, short_string, short_loop;
+
+ const int kLongStringLimit = 20;
+ if (min_length <= kLongStringLimit) {
+ cmpl(length, Immediate(kLongStringLimit));
+ j(less_equal, &short_string);
+ }
+
+ ASSERT(source.is(rsi));
+ ASSERT(destination.is(rdi));
+ ASSERT(length.is(rcx));
+
+ // Because source is 8-byte aligned in our uses of this function,
+ // we keep source aligned for the rep movs operation by copying the odd bytes
+ // at the end of the ranges.
+ movq(scratch, length);
+ shrl(length, Immediate(kPointerSizeLog2));
+ repmovsq();
+ // Move remaining bytes of length.
+ andl(scratch, Immediate(kPointerSize - 1));
+ movq(length, Operand(source, scratch, times_1, -kPointerSize));
+ movq(Operand(destination, scratch, times_1, -kPointerSize), length);
+ addq(destination, scratch);
+
+ if (min_length <= kLongStringLimit) {
+ jmp(&done);
+
+ bind(&short_string);
+ if (min_length == 0) {
+ testl(length, length);
+ j(zero, &done);
+ }
+ lea(scratch, Operand(destination, length, times_1, 0));
+
+ bind(&short_loop);
+ movb(length, Operand(source, 0));
+ movb(Operand(destination, 0), length);
+ incq(source);
+ incq(destination);
+ cmpq(destination, scratch);
+ j(not_equal, &short_loop);
+
+ bind(&done);
+ }
+}
+
+
+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.
+ movq(dst, Operand(rsi, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ for (int i = 1; i < context_chain_length; i++) {
+ movq(dst, Operand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ }
+ } else {
+ // Slot is in the current function context. Move it into the
+ // destination register in case we store into it (the write barrier
+ // cannot be allowed to destroy the context in rsi).
+ movq(dst, rsi);
+ }
+
+ // We should not have found a with context by walking the context
+ // chain (i.e., the static scope chain and runtime context chain do
+ // not agree). A variable occurring in such a scope should have
+ // slot type LOOKUP and not CONTEXT.
+ if (emit_debug_code()) {
+ CompareRoot(FieldOperand(dst, HeapObject::kMapOffset),
+ Heap::kWithContextMapRootIndex);
+ Check(not_equal, kVariableResolvedToWithContext);
+ }
+}
+
+
+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_OBJECT_INDEX)));
+ movq(scratch, FieldOperand(scratch, GlobalObject::kNativeContextOffset));
+
+ // Check that the function's map is the same as the expected cached map.
+ movq(scratch, Operand(scratch,
+ Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX)));
+
+ int offset = expected_kind * kPointerSize +
+ FixedArrayBase::kHeaderSize;
+ cmpq(map_in_out, FieldOperand(scratch, offset));
+ j(not_equal, no_map_match);
+
+ // Use the transitioned cached map.
+ offset = transitioned_kind * kPointerSize +
+ FixedArrayBase::kHeaderSize;
+ movq(map_in_out, FieldOperand(scratch, offset));
+}
+
+
+void MacroAssembler::LoadInitialArrayMap(
+ Register function_in, Register scratch,
+ Register map_out, bool can_have_holes) {
+ ASSERT(!function_in.is(map_out));
+ Label done;
+ movq(map_out, FieldOperand(function_in,
+ JSFunction::kPrototypeOrInitialMapOffset));
+ if (!FLAG_smi_only_arrays) {
+ ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
+ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ kind,
+ map_out,
+ scratch,
+ &done);
+ } else if (can_have_holes) {
+ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_HOLEY_SMI_ELEMENTS,
+ map_out,
+ scratch,
+ &done);
+ }
+ bind(&done);
+}
+
+#ifdef _WIN64
+static const int kRegisterPassedArguments = 4;
+#else
+static const int kRegisterPassedArguments = 6;
+#endif
+
+void MacroAssembler::LoadGlobalFunction(int index, Register function) {
+ // Load the global or builtins object from the current context.
+ movq(function,
+ Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ // Load the native context from the global or builtins object.
+ movq(function, FieldOperand(function, GlobalObject::kNativeContextOffset));
+ // Load the function from the native context.
+ movq(function, Operand(function, Context::SlotOffset(index)));
+}
+
+
+void MacroAssembler::LoadArrayFunction(Register function) {
+ movq(function,
+ Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ movq(function, FieldOperand(function, GlobalObject::kGlobalContextOffset));
+ movq(function,
+ Operand(function, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
+}
+
+
+void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
+ Register map) {
+ // Load the initial map. The global functions all have initial maps.
+ movq(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
+ if (emit_debug_code()) {
+ Label ok, fail;
+ CheckMap(map, isolate()->factory()->meta_map(), &fail, DO_SMI_CHECK);
+ jmp(&ok);
+ bind(&fail);
+ Abort(kGlobalFunctionsMustHaveInitialMap);
+ bind(&ok);
+ }
+}
+
+
+int MacroAssembler::ArgumentStackSlotsForCFunctionCall(int num_arguments) {
+ // On Windows 64 stack slots are reserved by the caller for all arguments
+ // including the ones passed in registers, and space is always allocated for
+ // the four register arguments even if the function takes fewer than four
+ // arguments.
+ // On AMD64 ABI (Linux/Mac) the first six arguments are passed in registers
+ // and the caller does not reserve stack slots for them.
+ ASSERT(num_arguments >= 0);
+#ifdef _WIN64
+ const int kMinimumStackSlots = kRegisterPassedArguments;
+ if (num_arguments < kMinimumStackSlots) return kMinimumStackSlots;
+ return num_arguments;
+#else
+ if (num_arguments < kRegisterPassedArguments) return 0;
+ return num_arguments - kRegisterPassedArguments;
+#endif
+}
+
+
+void MacroAssembler::PrepareCallCFunction(int num_arguments) {
+ int frame_alignment = OS::ActivationFrameAlignment();
+ ASSERT(frame_alignment != 0);
+ ASSERT(num_arguments >= 0);
+
+ // Make stack end at alignment and allocate space for arguments and old rsp.
+ movq(kScratchRegister, rsp);
+ ASSERT(IsPowerOf2(frame_alignment));
+ int argument_slots_on_stack =
+ ArgumentStackSlotsForCFunctionCall(num_arguments);
+ subq(rsp, Immediate((argument_slots_on_stack + 1) * kPointerSize));
+ and_(rsp, Immediate(-frame_alignment));
+ movq(Operand(rsp, argument_slots_on_stack * kPointerSize), kScratchRegister);
+}
+
+
+void MacroAssembler::CallCFunction(ExternalReference function,
+ int num_arguments) {
+ LoadAddress(rax, function);
+ CallCFunction(rax, num_arguments);
+}
+
+
+void MacroAssembler::CallCFunction(Register function, int num_arguments) {
+ ASSERT(has_frame());
+ // Check stack alignment.
+ if (emit_debug_code()) {
+ CheckStackAlignment();
+ }
+
+ call(function);
+ ASSERT(OS::ActivationFrameAlignment() != 0);
+ ASSERT(num_arguments >= 0);
+ int argument_slots_on_stack =
+ ArgumentStackSlotsForCFunctionCall(num_arguments);
+ movq(rsp, Operand(rsp, argument_slots_on_stack * 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),
+ masm_(NULL, address, size + Assembler::kGap) {
+ // Create a new macro assembler pointing to the address of the code to patch.
+ // The size is adjusted with kGap on order for the assembler to generate size
+ // bytes of instructions without failing with buffer size constraints.
+ ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
+}
+
+
+CodePatcher::~CodePatcher() {
+ // Indicate that code has changed.
+ CPU::FlushICache(address_, size_);
+
+ // Check that the code was patched as expected.
+ ASSERT(masm_.pc_ == address_ + size_);
+ 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::CheckMapDeprecated(Handle<Map> map,
+ Register scratch,
+ Label* if_deprecated) {
+ if (map->CanBeDeprecated()) {
+ Move(scratch, map);
+ movq(scratch, FieldOperand(scratch, Map::kBitField3Offset));
+ SmiToInteger32(scratch, scratch);
+ and_(scratch, Immediate(Map::Deprecated::kMask));
+ j(not_zero, if_deprecated);
+ }
+}
+
+
+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 (emit_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(kOneByteStringTag == 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);
+}
+
+
+void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
+ Label next, start;
+ Register empty_fixed_array_value = r8;
+ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
+ movq(rcx, rax);
+
+ // Check if the enum length field is properly initialized, indicating that
+ // there is an enum cache.
+ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
+
+ EnumLength(rdx, rbx);
+ Cmp(rdx, Smi::FromInt(Map::kInvalidEnumCache));
+ j(equal, call_runtime);
+
+ jmp(&start);
+
+ bind(&next);
+
+ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
+
+ // For all objects but the receiver, check that the cache is empty.
+ EnumLength(rdx, rbx);
+ Cmp(rdx, Smi::FromInt(0));
+ j(not_equal, call_runtime);
+
+ bind(&start);
+
+ // Check that there are no elements. Register rcx contains the current JS
+ // object we've reached through the prototype chain.
+ cmpq(empty_fixed_array_value,
+ FieldOperand(rcx, JSObject::kElementsOffset));
+ j(not_equal, call_runtime);
+
+ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
+ cmpq(rcx, null_value);
+ j(not_equal, &next);
+}
+
+void MacroAssembler::TestJSArrayForAllocationMemento(
+ Register receiver_reg,
+ Register scratch_reg) {
+ Label no_memento_available;
+ ExternalReference new_space_start =
+ ExternalReference::new_space_start(isolate());
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(isolate());
+
+ lea(scratch_reg, Operand(receiver_reg,
+ JSArray::kSize + AllocationMemento::kSize - kHeapObjectTag));
+ movq(kScratchRegister, new_space_start);
+ cmpq(scratch_reg, kScratchRegister);
+ j(less, &no_memento_available);
+ cmpq(scratch_reg, ExternalOperand(new_space_allocation_top));
+ j(greater, &no_memento_available);
+ CompareRoot(MemOperand(scratch_reg, -AllocationMemento::kSize),
+ Heap::kAllocationMementoMapRootIndex);
+ bind(&no_memento_available);
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/macro-assembler-x64.h b/chromium/v8/src/x64/macro-assembler-x64.h
new file mode 100644
index 00000000000..61abc206e10
--- /dev/null
+++ b/chromium/v8/src/x64/macro-assembler-x64.h
@@ -0,0 +1,1552 @@
+// Copyright 2012 the V8 project authors. 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_X64_MACRO_ASSEMBLER_X64_H_
+#define V8_X64_MACRO_ASSEMBLER_X64_H_
+
+#include "assembler.h"
+#include "frames.h"
+#include "v8globals.h"
+
+namespace v8 {
+namespace internal {
+
+// 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.
+const Register kScratchRegister = { 10 }; // r10.
+const Register kSmiConstantRegister = { 12 }; // r12 (callee save).
+const Register kRootRegister = { 13 }; // r13 (callee save).
+// Value of smi in kSmiConstantRegister.
+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.
+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;
+
+struct SmiIndex {
+ SmiIndex(Register index_register, ScaleFactor scale)
+ : reg(index_register),
+ scale(scale) {}
+ Register reg;
+ ScaleFactor scale;
+};
+
+
+// MacroAssembler implements a collection of frequently used macros.
+class MacroAssembler: public Assembler {
+ public:
+ // The isolate parameter can be NULL if the macro assembler should
+ // not use isolate-dependent functionality. In this case, it's the
+ // responsibility of the caller to never invoke such function on the
+ // macro assembler.
+ MacroAssembler(Isolate* isolate, void* buffer, int size);
+
+ // Prevent the use of the RootArray during the lifetime of this
+ // scope object.
+ class NoRootArrayScope BASE_EMBEDDED {
+ public:
+ explicit NoRootArrayScope(MacroAssembler* assembler)
+ : variable_(&assembler->root_array_available_),
+ old_value_(assembler->root_array_available_) {
+ assembler->root_array_available_ = false;
+ }
+ ~NoRootArrayScope() {
+ *variable_ = old_value_;
+ }
+ private:
+ bool* variable_;
+ bool old_value_;
+ };
+
+ // Operand pointing to an external reference.
+ // May emit code to set up the scratch register. The operand is
+ // only guaranteed to be correct as long as the scratch register
+ // isn't changed.
+ // If the operand is used more than once, use a scratch register
+ // that is guaranteed not to be clobbered.
+ Operand ExternalOperand(ExternalReference reference,
+ Register scratch = kScratchRegister);
+ // Loads and stores the value of an external reference.
+ // Special case code for load and store to take advantage of
+ // load_rax/store_rax if possible/necessary.
+ // For other operations, just use:
+ // Operand operand = ExternalOperand(extref);
+ // operation(operand, ..);
+ void Load(Register destination, ExternalReference source);
+ void Store(ExternalReference destination, Register source);
+ // Loads the address of the external reference into the destination
+ // register.
+ void LoadAddress(Register destination, ExternalReference source);
+ // Returns the size of the code generated by LoadAddress.
+ // Used by CallSize(ExternalReference) to find the size of a call.
+ int LoadAddressSize(ExternalReference source);
+ // Pushes the address of the external reference onto the stack.
+ void PushAddress(ExternalReference source);
+
+ // Operations on roots in the root-array.
+ void LoadRoot(Register destination, Heap::RootListIndex index);
+ void StoreRoot(Register source, Heap::RootListIndex index);
+ // Load a root value where the index (or part of it) is variable.
+ // The variable_offset register is added to the fixed_offset value
+ // to get the index into the root-array.
+ void LoadRootIndexed(Register destination,
+ Register variable_offset,
+ int fixed_offset);
+ void CompareRoot(Register with, Heap::RootListIndex index);
+ void CompareRoot(const Operand& with, Heap::RootListIndex index);
+ void PushRoot(Heap::RootListIndex index);
+
+ // 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
+ };
+
+ // 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);
+
+ void CheckMapDeprecated(Handle<Map> map,
+ Register scratch,
+ Label* if_deprecated);
+
+ // 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. 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,
+ SaveFPRegsMode save_fp,
+ RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
+ SmiCheck smi_check = INLINE_SMI_CHECK);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // ---------------------------------------------------------------------------
+ // Debugger Support
+
+ void DebugBreak();
+#endif
+
+ // 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
+ // to the first argument in register rsi.
+ //
+ // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack
+ // accessible via StackSpaceOperand.
+ void EnterExitFrame(int arg_stack_space = 0, bool save_doubles = false);
+
+ // Enter specific kind of exit frame. Allocates arg_stack_space * kPointerSize
+ // memory (not GCed) on the stack accessible via StackSpaceOperand.
+ void EnterApiExitFrame(int arg_stack_space);
+
+ // Leave the current exit frame. Expects/provides the return value in
+ // register rax:rdx (untouched) and the pointer to the first
+ // argument in register rsi.
+ void LeaveExitFrame(bool save_doubles = false);
+
+ // Leave the current exit frame. Expects/provides the return value in
+ // register rax (untouched).
+ void LeaveApiExitFrame();
+
+ // Push and pop the registers that can hold pointers.
+ void PushSafepointRegisters() { Pushad(); }
+ void PopSafepointRegisters() { Popad(); }
+ // Store the value in register src in the safepoint register stack
+ // slot for register dst.
+ void StoreToSafepointRegisterSlot(Register dst, const Immediate& imm);
+ void StoreToSafepointRegisterSlot(Register dst, Register src);
+ void LoadFromSafepointRegisterSlot(Register dst, Register src);
+
+ void InitializeRootRegister() {
+ ExternalReference roots_array_start =
+ ExternalReference::roots_array_start(isolate());
+ movq(kRootRegister, roots_array_start);
+ addq(kRootRegister, Immediate(kRootRegisterBias));
+ }
+
+ // ---------------------------------------------------------------------------
+ // JavaScript invokes
+
+ // 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);
+
+ // 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);
+
+ void InvokeCode(Handle<Code> code,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ RelocInfo::Mode rmode,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ // Invoke the JavaScript function in the given register. Changes the
+ // current context to the context in the function before invoking.
+ void InvokeFunction(Register function,
+ const ParameterCount& actual,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ void InvokeFunction(Handle<JSFunction> function,
+ const ParameterCount& expected,
+ const ParameterCount& actual,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
+
+ // Invoke specified builtin JavaScript function. Adds an entry to
+ // the unresolved list if the name does not resolve.
+ void InvokeBuiltin(Builtins::JavaScript id,
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper = NullCallWrapper());
+
+ // Store the function for the given builtin in the target register.
+ void GetBuiltinFunction(Register target, Builtins::JavaScript id);
+
+ // Store the code object for the given builtin in the target register.
+ void GetBuiltinEntry(Register target, Builtins::JavaScript id);
+
+
+ // ---------------------------------------------------------------------------
+ // Smi tagging, untagging and operations on tagged smis.
+
+ void InitializeSmiConstantRegister() {
+ movq(kSmiConstantRegister,
+ reinterpret_cast<uint64_t>(Smi::FromInt(kSmiConstantRegisterValue)),
+ RelocInfo::NONE64);
+ }
+
+ // Conversions between tagged smi values and non-tagged integer values.
+
+ // Tag an integer value. The result must be known to be a valid smi value.
+ // Only uses the low 32 bits of the src register. Sets the N and Z flags
+ // based on the value of the resulting smi.
+ void Integer32ToSmi(Register dst, Register src);
+
+ // Stores an integer32 value into a memory field that already holds a smi.
+ void Integer32ToSmiField(const Operand& dst, Register src);
+
+ // Adds constant to src and tags the result as a smi.
+ // Result must be a valid smi.
+ void Integer64PlusConstantToSmi(Register dst, Register src, int constant);
+
+ // Convert smi to 32-bit integer. I.e., not sign extended into
+ // high 32 bits of destination.
+ void SmiToInteger32(Register dst, Register src);
+ void SmiToInteger32(Register dst, const Operand& src);
+
+ // Convert smi to 64-bit integer (sign extended if necessary).
+ void SmiToInteger64(Register dst, Register src);
+ void SmiToInteger64(Register dst, const Operand& src);
+
+ // Multiply a positive smi's integer value by a power of two.
+ // Provides result as 64-bit integer value.
+ void PositiveSmiTimesPowerOfTwoToInteger64(Register dst,
+ Register src,
+ int power);
+
+ // Divide a positive smi's integer value by a power of two.
+ // Provides result as 32-bit integer value.
+ void PositiveSmiDivPowerOfTwoToInteger32(Register dst,
+ Register src,
+ int power);
+
+ // Perform the logical or of two smi values and return a smi value.
+ // If either argument is not a smi, jump to on_not_smis and retain
+ // the original values of source registers. The destination register
+ // may be changed if it's not one of the source registers.
+ void SmiOrIfSmis(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smis,
+ Label::Distance near_jump = Label::kFar);
+
+
+ // Simple comparison of smis. Both sides must be known smis to use these,
+ // otherwise use Cmp.
+ void SmiCompare(Register smi1, Register smi2);
+ void SmiCompare(Register dst, Smi* src);
+ void SmiCompare(Register dst, const Operand& src);
+ void SmiCompare(const Operand& dst, Register src);
+ void SmiCompare(const Operand& dst, Smi* src);
+ // Compare the int32 in src register to the value of the smi stored at dst.
+ void SmiCompareInteger32(const Operand& dst, Register src);
+ // Sets sign and zero flags depending on value of smi in register.
+ void SmiTest(Register src);
+
+ // Functions performing a check on a known or potential smi. Returns
+ // a condition that is satisfied if the check is successful.
+
+ // Is the value a tagged smi.
+ Condition CheckSmi(Register src);
+ Condition CheckSmi(const Operand& src);
+
+ // Is the value a non-negative tagged smi.
+ Condition CheckNonNegativeSmi(Register src);
+
+ // Are both values tagged smis.
+ Condition CheckBothSmi(Register first, Register second);
+
+ // Are both values non-negative tagged smis.
+ Condition CheckBothNonNegativeSmi(Register first, Register second);
+
+ // Are either value a tagged smi.
+ Condition CheckEitherSmi(Register first,
+ Register second,
+ Register scratch = kScratchRegister);
+
+ // Is the value the minimum smi value (since we are using
+ // two's complement numbers, negating the value is known to yield
+ // a non-smi value).
+ Condition CheckIsMinSmi(Register src);
+
+ // Checks whether an 32-bit integer value is a valid for conversion
+ // to a smi.
+ Condition CheckInteger32ValidSmiValue(Register src);
+
+ // Checks whether an 32-bit unsigned integer value is a valid for
+ // conversion to a smi.
+ Condition CheckUInteger32ValidSmiValue(Register src);
+
+ // Check whether src is a Smi, and set dst to zero if it is a smi,
+ // and to one if it isn't.
+ void CheckSmiToIndicator(Register dst, Register src);
+ void CheckSmiToIndicator(Register dst, const Operand& src);
+
+ // Test-and-jump functions. Typically combines a check function
+ // above with a conditional jump.
+
+ // Jump if the value cannot be represented by a smi.
+ void JumpIfNotValidSmiValue(Register src, Label* on_invalid,
+ Label::Distance near_jump = Label::kFar);
+
+ // Jump if the unsigned integer value cannot be represented by a smi.
+ void JumpIfUIntNotValidSmiValue(Register src, Label* on_invalid,
+ Label::Distance near_jump = Label::kFar);
+
+ // Jump to label if the value is a tagged smi.
+ void JumpIfSmi(Register src,
+ Label* on_smi,
+ Label::Distance near_jump = Label::kFar);
+
+ // Jump to label if the value is not a tagged smi.
+ void JumpIfNotSmi(Register src,
+ Label* on_not_smi,
+ Label::Distance near_jump = Label::kFar);
+
+ // Jump to label if the value is not a non-negative tagged smi.
+ void JumpUnlessNonNegativeSmi(Register src,
+ Label* on_not_smi,
+ Label::Distance near_jump = Label::kFar);
+
+ // Jump to label if the value, which must be a tagged smi, has value equal
+ // to the constant.
+ void JumpIfSmiEqualsConstant(Register src,
+ Smi* constant,
+ Label* on_equals,
+ Label::Distance near_jump = Label::kFar);
+
+ // Jump if either or both register are not smi values.
+ void JumpIfNotBothSmi(Register src1,
+ Register src2,
+ Label* on_not_both_smi,
+ Label::Distance near_jump = Label::kFar);
+
+ // Jump if either or both register are not non-negative smi values.
+ void JumpUnlessBothNonNegativeSmi(Register src1, Register src2,
+ Label* on_not_both_smi,
+ Label::Distance near_jump = Label::kFar);
+
+ // Operations on tagged smi values.
+
+ // Smis represent a subset of integers. The subset is always equivalent to
+ // a two's complement interpretation of a fixed number of bits.
+
+ // Optimistically adds an integer constant to a supposed smi.
+ // If the src is not a smi, or the result is not a smi, jump to
+ // the label.
+ void SmiTryAddConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ // Add an integer constant to a tagged smi, giving a tagged smi as result.
+ // No overflow testing on the result is done.
+ void SmiAddConstant(Register dst, Register src, Smi* constant);
+
+ // Add an integer constant to a tagged smi, giving a tagged smi as result.
+ // No overflow testing on the result is done.
+ void SmiAddConstant(const Operand& dst, Smi* constant);
+
+ // Add an integer constant to a tagged smi, giving a tagged smi as result,
+ // or jumping to a label if the result cannot be represented by a smi.
+ void SmiAddConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ // Subtract an integer constant from a tagged smi, giving a tagged smi as
+ // result. No testing on the result is done. Sets the N and Z flags
+ // based on the value of the resulting integer.
+ void SmiSubConstant(Register dst, Register src, Smi* constant);
+
+ // Subtract an integer constant from a tagged smi, giving a tagged smi as
+ // result, or jumping to a label if the result cannot be represented by a smi.
+ void SmiSubConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ // Negating a smi can give a negative zero or too large positive value.
+ // NOTICE: This operation jumps on success, not failure!
+ void SmiNeg(Register dst,
+ Register src,
+ Label* on_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ // Adds smi values and return the result as a smi.
+ // If dst is src1, then src1 will be destroyed, even if
+ // the operation is unsuccessful.
+ void SmiAdd(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+ void SmiAdd(Register dst,
+ Register src1,
+ const Operand& src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ void SmiAdd(Register dst,
+ Register src1,
+ Register src2);
+
+ // Subtracts smi values and return the result as a smi.
+ // If dst is src1, then src1 will be destroyed, even if
+ // the operation is unsuccessful.
+ void SmiSub(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ void SmiSub(Register dst,
+ Register src1,
+ Register src2);
+
+ void SmiSub(Register dst,
+ Register src1,
+ const Operand& src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ void SmiSub(Register dst,
+ Register src1,
+ const Operand& src2);
+
+ // Multiplies smi values and return the result as a smi,
+ // if possible.
+ // If dst is src1, then src1 will be destroyed, even if
+ // the operation is unsuccessful.
+ void SmiMul(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ // Divides one smi by another and returns the quotient.
+ // Clobbers rax and rdx registers.
+ void SmiDiv(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ // Divides one smi by another and returns the remainder.
+ // Clobbers rax and rdx registers.
+ void SmiMod(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+
+ // Bitwise operations.
+ void SmiNot(Register dst, Register src);
+ void SmiAnd(Register dst, Register src1, Register src2);
+ void SmiOr(Register dst, Register src1, Register src2);
+ void SmiXor(Register dst, Register src1, Register src2);
+ void SmiAndConstant(Register dst, Register src1, Smi* constant);
+ void SmiOrConstant(Register dst, Register src1, Smi* constant);
+ void SmiXorConstant(Register dst, Register src1, Smi* constant);
+
+ void SmiShiftLeftConstant(Register dst,
+ Register src,
+ int shift_value);
+ void SmiShiftLogicalRightConstant(Register dst,
+ Register src,
+ int shift_value,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+ void SmiShiftArithmeticRightConstant(Register dst,
+ Register src,
+ int shift_value);
+
+ // Shifts a smi value to the left, and returns the result if that is a smi.
+ // Uses and clobbers rcx, so dst may not be rcx.
+ void SmiShiftLeft(Register dst,
+ Register src1,
+ Register src2);
+ // Shifts a smi value to the right, shifting in zero bits at the top, and
+ // returns the unsigned intepretation of the result if that is a smi.
+ // Uses and clobbers rcx, so dst may not be rcx.
+ void SmiShiftLogicalRight(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
+ // Shifts a smi value to the right, sign extending the top, and
+ // returns the signed intepretation of the result. That will always
+ // be a valid smi value, since it's numerically smaller than the
+ // original.
+ // Uses and clobbers rcx, so dst may not be rcx.
+ void SmiShiftArithmeticRight(Register dst,
+ Register src1,
+ Register src2);
+
+ // Specialized operations
+
+ // Select the non-smi register of two registers where exactly one is a
+ // smi. If neither are smis, jump to the failure label.
+ void SelectNonSmi(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smis,
+ Label::Distance near_jump = Label::kFar);
+
+ // Converts, if necessary, a smi to a combination of number and
+ // multiplier to be used as a scaled index.
+ // The src register contains a *positive* smi value. The shift is the
+ // power of two to multiply the index value by (e.g.
+ // to index by smi-value * kPointerSize, pass the smi and kPointerSizeLog2).
+ // The returned index register may be either src or dst, depending
+ // on what is most efficient. If src and dst are different registers,
+ // src is always unchanged.
+ SmiIndex SmiToIndex(Register dst, Register src, int shift);
+
+ // Converts a positive smi to a negative index.
+ SmiIndex SmiToNegativeIndex(Register dst, Register src, int shift);
+
+ // Add the value of a smi in memory to an int32 register.
+ // Sets flags as a normal add.
+ void AddSmiField(Register dst, const Operand& src);
+
+ // Basic Smi operations.
+ void Move(Register dst, Smi* source) {
+ LoadSmiConstant(dst, source);
+ }
+
+ void Move(const Operand& dst, Smi* source) {
+ Register constant = GetSmiConstant(source);
+ movq(dst, constant);
+ }
+
+ void Push(Smi* smi);
+ void Test(const Operand& dst, Smi* source);
+
+
+ // ---------------------------------------------------------------------------
+ // String macros.
+
+ // If object is a string, its map is loaded into object_map.
+ void JumpIfNotString(Register object,
+ Register object_map,
+ Label* not_string,
+ Label::Distance near_jump = Label::kFar);
+
+
+ void JumpIfNotBothSequentialAsciiStrings(
+ Register first_object,
+ Register second_object,
+ Register scratch1,
+ Register scratch2,
+ 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
+ // label if not. If the instance type can be scratched specify same register
+ // for both instance type and scratch.
+ void JumpIfInstanceTypeIsNotSequentialAscii(
+ Register instance_type,
+ Register scratch,
+ Label*on_not_flat_ascii_string,
+ Label::Distance near_jump = Label::kFar);
+
+ void JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first_object_instance_type,
+ Register second_object_instance_type,
+ Register scratch1,
+ Register scratch2,
+ Label* on_fail,
+ Label::Distance near_jump = Label::kFar);
+
+ // Checks if the given register or operand is a unique name
+ void JumpIfNotUniqueName(Register reg, Label* not_unique_name,
+ Label::Distance distance = Label::kFar);
+ void JumpIfNotUniqueName(Operand operand, Label* not_unique_name,
+ Label::Distance distance = Label::kFar);
+
+ // ---------------------------------------------------------------------------
+ // Macro instructions.
+
+ // Load a register with a long value as efficiently as possible.
+ void Set(Register dst, int64_t x);
+ void Set(const Operand& dst, int64_t x);
+
+ // Move if the registers are not identical.
+ void Move(Register target, Register source);
+
+ // Support for constant splitting.
+ bool IsUnsafeInt(const int x);
+ void SafeMove(Register dst, Smi* src);
+ void SafePush(Smi* src);
+
+ // 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);
+ void Cmp(Register dst, Handle<Object> source);
+ void Cmp(const Operand& dst, Handle<Object> source);
+ void Cmp(Register dst, Smi* src);
+ 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 CmpHeapObject(Register reg, Handle<HeapObject> object);
+ void PushHeapObject(Handle<HeapObject> object);
+
+ void LoadObject(Register result, Handle<Object> object) {
+ AllowDeferredHandleDereference heap_object_check;
+ if (object->IsHeapObject()) {
+ LoadHeapObject(result, Handle<HeapObject>::cast(object));
+ } else {
+ Move(result, object);
+ }
+ }
+
+ void CmpObject(Register reg, Handle<Object> object) {
+ AllowDeferredHandleDereference heap_object_check;
+ if (object->IsHeapObject()) {
+ CmpHeapObject(reg, Handle<HeapObject>::cast(object));
+ } else {
+ Cmp(reg, object);
+ }
+ }
+
+ // Load a global cell into a register.
+ void LoadGlobalCell(Register dst, Handle<Cell> 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);
+
+ void Call(Label* target) { call(target); }
+ void Push(Register src) { push(src); }
+ void Pop(Register dst) { pop(dst); }
+ void PushReturnAddressFrom(Register src) { push(src); }
+ void PopReturnAddressTo(Register dst) { pop(dst); }
+
+ // Control Flow
+ void Jump(Address destination, RelocInfo::Mode rmode);
+ void Jump(ExternalReference ext);
+ void Jump(Handle<Code> code_object, RelocInfo::Mode rmode);
+
+ void Call(Address destination, RelocInfo::Mode rmode);
+ void Call(ExternalReference ext);
+ void Call(Handle<Code> code_object,
+ RelocInfo::Mode rmode,
+ TypeFeedbackId ast_id = TypeFeedbackId::None());
+
+ // The size of the code generated for different call instructions.
+ int CallSize(Address destination, RelocInfo::Mode rmode) {
+ return kCallSequenceLength;
+ }
+ int CallSize(ExternalReference ext);
+ int CallSize(Handle<Code> code_object) {
+ // Code calls use 32-bit relative addressing.
+ return kShortCallInstructionLength;
+ }
+ int CallSize(Register target) {
+ // Opcode: REX_opt FF /2 m64
+ return (target.high_bit() != 0) ? 3 : 2;
+ }
+ int CallSize(const Operand& target) {
+ // Opcode: REX_opt FF /2 m64
+ return (target.requires_rex() ? 2 : 1) + target.operand_size();
+ }
+
+ // Emit call to the code we are currently generating.
+ void CallSelf() {
+ Handle<Code> self(reinterpret_cast<Code**>(CodeObject().location()));
+ Call(self, RelocInfo::CODE_TARGET);
+ }
+
+ // Non-x64 instructions.
+ // Push/pop all general purpose registers.
+ // Does not push rsp/rbp nor any of the assembler's special purpose registers
+ // (kScratchRegister, kSmiConstantRegister, kRootRegister).
+ void Pushad();
+ void Popad();
+ // Sets the stack as after performing Popad, without actually loading the
+ // registers.
+ void Dropad();
+
+ // Compare object type for heap object.
+ // Always use unsigned comparisons: above and below, not less and greater.
+ // Incoming register is heap_object and outgoing register is map.
+ // They may be the same register, and may be kScratchRegister.
+ void CmpObjectType(Register heap_object, InstanceType type, Register map);
+
+ // Compare instance type for map.
+ // Always use unsigned comparisons: above and below, not less and greater.
+ void CmpInstanceType(Register map, InstanceType type);
+
+ // Check if a map for a JSObject indicates that the object has fast elements.
+ // Jump to the specified label if it does not.
+ void CheckFastElements(Register map,
+ 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 CheckFastSmiElements(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,
+ int elements_offset = 0);
+
+ // 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);
+
+ // 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);
+
+ // 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
+ // known to be a heap object)
+ void DispatchMap(Register obj,
+ Register unused,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type);
+
+ // Check if the object in register heap_object is a string. Afterwards the
+ // register map contains the object map and the register instance_type
+ // contains the instance_type. The registers map and instance_type can be the
+ // same in which case it contains the instance type afterwards. Either of the
+ // registers map and instance_type can be the same as heap_object.
+ Condition IsObjectStringType(Register heap_object,
+ Register map,
+ Register instance_type);
+
+ // Check if the object in register heap_object is a name. Afterwards the
+ // register map contains the object map and the register instance_type
+ // contains the instance_type. The registers map and instance_type can be the
+ // same in which case it contains the instance type afterwards. Either of the
+ // registers map and instance_type can be the same as heap_object.
+ Condition IsObjectNameType(Register heap_object,
+ Register map,
+ Register instance_type);
+
+ // FCmp compares and pops the two values on top of the FPU stack.
+ // The flag results are similar to integer cmp, but requires unsigned
+ // jcc instructions (je, ja, jae, jb, jbe, je, and jz).
+ void FCmp();
+
+ void ClampUint8(Register reg);
+
+ void ClampDoubleToUint8(XMMRegister input_reg,
+ XMMRegister temp_xmm_reg,
+ Register result_reg);
+
+ void LoadUint32(XMMRegister dst, Register src, XMMRegister scratch);
+
+ void LoadInstanceDescriptors(Register map, Register descriptors);
+ void EnumLength(Register dst, Register map);
+ void NumberOfOwnDescriptors(Register dst, Register map);
+
+ template<typename Field>
+ void DecodeField(Register reg) {
+ static const int shift = Field::kShift + kSmiShift;
+ static const int mask = Field::kMask >> Field::kShift;
+ shr(reg, Immediate(shift));
+ and_(reg, Immediate(mask));
+ shl(reg, Immediate(kSmiShift));
+ }
+
+ // Abort execution if argument is not a number, enabled via --debug-code.
+ void AssertNumber(Register object);
+
+ // Abort execution if argument is a smi, enabled via --debug-code.
+ void AssertNotSmi(Register object);
+
+ // Abort execution if argument is not a smi, enabled via --debug-code.
+ void AssertSmi(Register object);
+ void AssertSmi(const Operand& object);
+
+ // Abort execution if a 64 bit register containing a 32 bit payload does not
+ // have zeros in the top 32 bits, enabled via --debug-code.
+ void AssertZeroExtended(Register reg);
+
+ // Abort execution if argument is not a string, enabled via --debug-code.
+ void AssertString(Register object);
+
+ // Abort execution if argument is not a name, enabled via --debug-code.
+ void AssertName(Register object);
+
+ // Abort execution if argument is not the root value with the given index,
+ // enabled via --debug-code.
+ void AssertRootValue(Register src,
+ Heap::RootListIndex root_value_index,
+ BailoutReason reason);
+
+ // ---------------------------------------------------------------------------
+ // Exception handling
+
+ // 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();
+
+ // Activate the top handler in the try hander chain and pass the
+ // thrown value.
+ void Throw(Register value);
+
+ // Propagate an uncatchable exception out of the current JS stack.
+ void ThrowUncatchable(Register value);
+
+ // ---------------------------------------------------------------------------
+ // Inline caching support
+
+ // Generate code for checking access rights - used for security checks
+ // on access to global objects across environments. The holder register
+ // is left untouched, but the scratch register and kScratchRegister,
+ // which must be different, are clobbered.
+ void CheckAccessGlobalProxy(Register holder_reg,
+ Register scratch,
+ Label* miss);
+
+ void GetNumberHash(Register r0, Register scratch);
+
+ void LoadFromNumberDictionary(Label* miss,
+ Register elements,
+ Register key,
+ Register r0,
+ Register r1,
+ Register r2,
+ Register result);
+
+
+ // ---------------------------------------------------------------------------
+ // Allocation support
+
+ // Allocate an object in new space or old pointer space. If the given space
+ // is exhausted control continues at the gc_required label. The allocated
+ // object is returned in result and end of the new object is returned in
+ // result_end. The register scratch can be passed as no_reg in which case
+ // an additional object reference will be added to the reloc info. The
+ // returned pointers in result and result_end have not yet been tagged as
+ // heap objects. If result_contains_top_on_entry is true the content of
+ // result is known to be the allocation top on entry (could be result_end
+ // from a previous call). If result_contains_top_on_entry is true scratch
+ // should be no_reg as it is never used.
+ void Allocate(int object_size,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ void Allocate(int header_size,
+ ScaleFactor element_size,
+ Register element_count,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ void Allocate(Register object_size,
+ Register result,
+ Register result_end,
+ Register scratch,
+ Label* gc_required,
+ AllocationFlags flags);
+
+ // Undo allocation in new space. The object passed and objects allocated after
+ // it will no longer be allocated. Make sure that no pointers are left to the
+ // object(s) no longer allocated as they would be invalid when allocation is
+ // un-done.
+ void UndoAllocationInNewSpace(Register object);
+
+ // Allocate a heap number in new space with undefined value. Returns
+ // tagged pointer in result register, or jumps to gc_required if new
+ // space is full.
+ void AllocateHeapNumber(Register result,
+ Register scratch,
+ Label* gc_required);
+
+ // Allocate a sequential string. All the header fields of the string object
+ // are initialized.
+ void AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+
+ // Allocate a raw cons string object. Only the map field of the result is
+ // initialized.
+ void AllocateTwoByteConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // Allocate a raw sliced string object. Only the map field of the result is
+ // initialized.
+ void AllocateTwoByteSlicedString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiSlicedString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
+ // ---------------------------------------------------------------------------
+ // Support functions.
+
+ // Check if result is zero and op is negative.
+ void NegativeZeroTest(Register result, Register op, Label* then_label);
+
+ // Check if result is zero and op is negative in code using jump targets.
+ void NegativeZeroTest(CodeGenerator* cgen,
+ Register result,
+ Register op,
+ JumpTarget* then_target);
+
+ // Check if result is zero and any of op1 and op2 are negative.
+ // Register scratch is destroyed, and it must be different from op2.
+ void NegativeZeroTest(Register result, Register op1, Register op2,
+ Register scratch, Label* then_label);
+
+ // Try to get function prototype of a function and puts the value in
+ // the result register. Checks that the function really is a
+ // function and jumps to the miss label if the fast checks fail. The
+ // function register will be untouched; the other register may be
+ // clobbered.
+ void TryGetFunctionPrototype(Register function,
+ Register result,
+ Label* miss,
+ bool miss_on_bound_function = false);
+
+ // Generates code for reporting that an illegal operation has
+ // occurred.
+ void IllegalOperation(int num_arguments);
+
+ // Picks out an array index from the hash field.
+ // Register use:
+ // hash - holds the index's hash. Clobbered.
+ // index - holds the overwritten index on exit.
+ void IndexFromHash(Register hash, Register index);
+
+ // 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 native context if the map in register
+ // map_in_out is the cached Array map in the native 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,
+ bool can_have_holes);
+
+ // Load the global function with the given index.
+ void LoadGlobalFunction(int index, Register function);
+ void LoadArrayFunction(Register function);
+
+ // Load the initial map from the global function. The registers
+ // function and map can be the same.
+ void LoadGlobalFunctionInitialMap(Register function, Register map);
+
+ // ---------------------------------------------------------------------------
+ // Runtime calls
+
+ // Call a code stub.
+ void CallStub(CodeStub* stub, TypeFeedbackId ast_id = TypeFeedbackId::None());
+
+ // Tail call a code stub (jump).
+ void TailCallStub(CodeStub* stub);
+
+ // Return from a code stub after popping its arguments.
+ void StubReturn(int argc);
+
+ // Call a runtime routine.
+ void CallRuntime(const Runtime::Function* f, int num_arguments);
+
+ // Call a runtime function and save the value of XMM registers.
+ void CallRuntimeSaveDoubles(Runtime::FunctionId id);
+
+ // Convenience function: Same as above, but takes the fid instead.
+ void CallRuntime(Runtime::FunctionId id, int num_arguments);
+
+ // Convenience function: call an external reference.
+ void CallExternalReference(const ExternalReference& ext,
+ int num_arguments);
+
+ // Tail call of a runtime routine (jump).
+ // Like JumpToExternalReference, but also takes care of passing the number
+ // of parameters.
+ void TailCallExternalReference(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);
+
+ // Jump to a runtime routine.
+ void JumpToExternalReference(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 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, bool returns_handle);
+
+ // 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,
+ Address thunk_address,
+ Register thunk_last_arg,
+ int stack_space,
+ bool returns_handle,
+ int return_value_offset_from_rbp);
+
+ // Before calling a C-function from generated code, align arguments on stack.
+ // After aligning the frame, arguments must be stored in rsp[0], rsp[8],
+ // etc., not pushed. The argument count assumes all arguments are word sized.
+ // The number of slots reserved for arguments depends on platform. On Windows
+ // stack slots are reserved for the arguments passed in registers. On other
+ // platforms stack slots are only reserved for the arguments actually passed
+ // on the stack.
+ void PrepareCallCFunction(int num_arguments);
+
+ // Calls a C function and cleans up the space for arguments allocated
+ // by PrepareCallCFunction. The called function is not allowed to trigger a
+ // garbage collection, since that might move the code and invalidate the
+ // return address (unless this is somehow accounted for by the called
+ // function).
+ void CallCFunction(ExternalReference function, int num_arguments);
+ void CallCFunction(Register function, int num_arguments);
+
+ // Calculate the number of stack slots to reserve for arguments when calling a
+ // C function.
+ int ArgumentStackSlotsForCFunctionCall(int num_arguments);
+
+ // ---------------------------------------------------------------------------
+ // Utilities
+
+ void Ret();
+
+ // Return and drop arguments from stack, where the number of arguments
+ // may be bigger than 2^16 - 1. Requires a scratch register.
+ void Ret(int bytes_dropped, Register scratch);
+
+ Handle<Object> CodeObject() {
+ ASSERT(!code_object_.is_null());
+ return code_object_;
+ }
+
+ // Copy length bytes from source to destination.
+ // Uses scratch register internally (if you have a low-eight register
+ // free, do use it, otherwise kScratchRegister will be used).
+ // The min_length is a minimum limit on the value that length will have.
+ // The algorithm has some special cases that might be omitted if the string
+ // is known to always be long.
+ void CopyBytes(Register destination,
+ Register source,
+ Register length,
+ 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
+
+ void SetCounter(StatsCounter* counter, int value);
+ void IncrementCounter(StatsCounter* counter, int value);
+ void DecrementCounter(StatsCounter* counter, int value);
+
+
+ // ---------------------------------------------------------------------------
+ // Debugging
+
+ // Calls Abort(msg) if the condition cc is not satisfied.
+ // Use --debug_code to enable.
+ void Assert(Condition cc, BailoutReason reason);
+
+ void AssertFastElements(Register elements);
+
+ // Like Assert(), but always enabled.
+ void Check(Condition cc, BailoutReason reason);
+
+ // Print a message to stdout and abort execution.
+ void Abort(BailoutReason msg);
+
+ // Check that the stack is aligned.
+ void CheckStackAlignment();
+
+ // Verify restrictions about code generated in stubs.
+ void set_generating_stub(bool value) { generating_stub_ = value; }
+ 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);
+
+ // Expects object in rax and returns map with validated enum cache
+ // in rax. Assumes that any other register can be used as a scratch.
+ void CheckEnumCache(Register null_value,
+ Label* call_runtime);
+
+ // AllocationMemento support. Arrays may have an associated
+ // AllocationMemento object that can be checked for in order to pretransition
+ // to another type.
+ // On entry, receiver_reg should point to the array object.
+ // scratch_reg gets clobbered.
+ // If allocation info is present, condition flags are set to equal
+ void TestJSArrayForAllocationMemento(Register receiver_reg,
+ Register scratch_reg);
+
+ private:
+ // Order general registers are pushed by Pushad.
+ // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
+ static const int kSafepointPushRegisterIndices[Register::kNumRegisters];
+ static const int kNumSafepointSavedRegisters = 11;
+ static const int kSmiShift = kSmiTagSize + kSmiShiftSize;
+
+ 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
+ // modified. It may be the "smi 1 constant" register.
+ Register GetSmiConstant(Smi* value);
+
+ intptr_t RootRegisterDelta(ExternalReference other);
+
+ // Moves the smi value to the destination register.
+ void LoadSmiConstant(Register dst, Smi* value);
+
+ // This handle will be patched with the code object on installation.
+ Handle<Object> code_object_;
+
+ // Helper functions for generating invokes.
+ void InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ 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);
+
+ void EnterExitFramePrologue(bool save_rax);
+
+ // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack
+ // accessible via StackSpaceOperand.
+ void EnterExitFrameEpilogue(int arg_stack_space, bool save_doubles);
+
+ void LeaveExitFrameEpilogue();
+
+ // Allocation support helpers.
+ // Loads the top of new-space into the result register.
+ // Otherwise the address of the new-space top is loaded into scratch (if
+ // scratch is valid), and the new-space top is loaded into result.
+ void LoadAllocationTopHelper(Register result,
+ Register scratch,
+ AllocationFlags flags);
+
+ // Update allocation top with value in result_end register.
+ // If scratch is valid, it contains the address of the allocation top.
+ void UpdateAllocationTopHelper(Register result_end,
+ Register scratch,
+ AllocationFlags flags);
+
+ // Helper for PopHandleScope. Allowed to perform a GC and returns
+ // NULL if gc_allowed. Does not perform a GC if !gc_allowed, and
+ // possibly returns a failure object indicating an allocation failure.
+ Object* PopHandleScopeHelper(Register saved,
+ 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);
+ static int SafepointRegisterStackIndex(int reg_code) {
+ return kNumSafepointRegisters - kSafepointPushRegisterIndices[reg_code] - 1;
+ }
+
+ // Needs access to SafepointRegisterStackIndex for compiled frame
+ // traversal.
+ friend class StandardFrame;
+};
+
+
+// The code patcher is used to patch (typically) small parts of code e.g. for
+// debugging and other types of instrumentation. When using the code patcher
+// the exact number of bytes specified must be emitted. Is not legal to emit
+// relocation information. If any of these constraints are violated it causes
+// an assertion.
+class CodePatcher {
+ public:
+ CodePatcher(byte* address, int size);
+ virtual ~CodePatcher();
+
+ // Macro assembler to emit code.
+ MacroAssembler* masm() { return &masm_; }
+
+ private:
+ byte* address_; // The address of the code being patched.
+ int size_; // Number of bytes of the expected patch size.
+ MacroAssembler masm_; // Macro assembler used to generate the code.
+};
+
+
+// -----------------------------------------------------------------------------
+// Static helper functions.
+
+// Generate an Operand for loading a field from an object.
+inline Operand FieldOperand(Register object, int offset) {
+ return Operand(object, offset - kHeapObjectTag);
+}
+
+
+// Generate an Operand for loading an indexed field from an object.
+inline Operand FieldOperand(Register object,
+ Register index,
+ ScaleFactor scale,
+ int offset) {
+ return Operand(object, index, scale, offset - kHeapObjectTag);
+}
+
+
+inline Operand ContextOperand(Register context, int index) {
+ return Operand(context, Context::SlotOffset(index));
+}
+
+
+inline Operand GlobalObjectOperand() {
+ return ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX);
+}
+
+
+// Provides access to exit frame stack space (not GCed).
+inline Operand StackSpaceOperand(int index) {
+#ifdef _WIN64
+ const int kShaddowSpace = 4;
+ return Operand(rsp, (index + kShaddowSpace) * kPointerSize);
+#else
+ return Operand(rsp, index * kPointerSize);
+#endif
+}
+
+
+inline Operand StackOperandForReturnAddress(int32_t disp) {
+ return Operand(rsp, disp);
+}
+
+
+#ifdef GENERATED_CODE_COVERAGE
+extern void LogGeneratedCodeCoverage(const char* file_line);
+#define CODE_COVERAGE_STRINGIFY(x) #x
+#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
+#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
+#define ACCESS_MASM(masm) { \
+ Address x64_coverage_function = FUNCTION_ADDR(LogGeneratedCodeCoverage); \
+ masm->pushfq(); \
+ masm->Pushad(); \
+ masm->push(Immediate(reinterpret_cast<int>(&__FILE_LINE__))); \
+ masm->Call(x64_coverage_function, RelocInfo::EXTERNAL_REFERENCE); \
+ masm->pop(rax); \
+ masm->Popad(); \
+ masm->popfq(); \
+ } \
+ masm->
+#else
+#define ACCESS_MASM(masm) masm->
+#endif
+
+} } // namespace v8::internal
+
+#endif // V8_X64_MACRO_ASSEMBLER_X64_H_
diff --git a/chromium/v8/src/x64/regexp-macro-assembler-x64.cc b/chromium/v8/src/x64/regexp-macro-assembler-x64.cc
new file mode 100644
index 00000000000..dcd317c666e
--- /dev/null
+++ b/chromium/v8/src/x64/regexp-macro-assembler-x64.cc
@@ -0,0 +1,1449 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "cpu-profiler.h"
+#include "serialize.h"
+#include "unicode.h"
+#include "log.h"
+#include "regexp-stack.h"
+#include "macro-assembler.h"
+#include "regexp-macro-assembler.h"
+#include "x64/regexp-macro-assembler-x64.h"
+
+namespace v8 {
+namespace internal {
+
+#ifndef V8_INTERPRETED_REGEXP
+
+/*
+ * This assembler uses the following register assignment convention
+ * - rdx : Currently loaded character(s) as ASCII or UC16. Must be loaded
+ * using LoadCurrentCharacter before using any of the dispatch methods.
+ * Temporarily stores the index of capture start after a matching pass
+ * for a global regexp.
+ * - rdi : Current position in input, as negative offset from end of string.
+ * Please notice that this is the byte offset, not the character
+ * offset! Is always a 32-bit signed (negative) offset, but must be
+ * maintained sign-extended to 64 bits, since it is used as index.
+ * - rsi : End of input (points to byte after last character in input),
+ * so that rsi+rdi points to the current character.
+ * - rbp : Frame pointer. Used to access arguments, local variables and
+ * RegExp registers.
+ * - rsp : Points to tip of C stack.
+ * - rcx : Points to tip of backtrack stack. The backtrack stack contains
+ * only 32-bit values. Most are offsets from some base (e.g., character
+ * positions from end of string or code location from Code* pointer).
+ * - r8 : Code object pointer. Used to convert between absolute and
+ * code-object-relative addresses.
+ *
+ * The registers rax, rbx, r9 and r11 are free to use for computations.
+ * If changed to use r12+, they should be saved as callee-save registers.
+ * The macro assembler special registers r12 and r13 (kSmiConstantRegister,
+ * kRootRegister) aren't special during execution of RegExp code (they don't
+ * hold the values assumed when creating JS code), so no Smi or Root related
+ * macro operations can be used.
+ *
+ * Each call to a C++ method should retain these registers.
+ *
+ * The stack will have the following content, in some order, indexable from the
+ * frame pointer (see, e.g., kStackHighEnd):
+ * - Isolate* isolate (address of the current isolate)
+ * - direct_call (if 1, direct call from JavaScript code, if 0 call
+ * through the runtime system)
+ * - stack_area_base (high end of the memory area to use as
+ * backtracking stack)
+ * - capture array size (may fit multiple sets of matches)
+ * - int* capture_array (int[num_saved_registers_], for output).
+ * - end of input (address of end of string)
+ * - start of input (address of first character in string)
+ * - start index (character index of start)
+ * - String* input_string (input string)
+ * - return address
+ * - backup of callee save registers (rbx, possibly rsi and rdi).
+ * - success counter (only useful for global regexp to count matches)
+ * - Offset of location before start of input (effectively character
+ * position -1). Used to initialize capture registers to a non-position.
+ * - At start of string (if 1, we are starting at the start of the
+ * string, otherwise 0)
+ * - register 0 rbp[-n] (Only positions must be stored in the first
+ * - register 1 rbp[-n-8] num_saved_registers_ registers)
+ * - ...
+ *
+ * The first num_saved_registers_ registers are initialized to point to
+ * "character -1" in the string (i.e., char_size() bytes before the first
+ * character of the string). The remaining registers starts out uninitialized.
+ *
+ * The first seven values must be provided by the calling code by
+ * calling the code's entry address cast to a function pointer with the
+ * following signature:
+ * int (*match)(String* input_string,
+ * int start_index,
+ * Address start,
+ * Address end,
+ * int* capture_output_array,
+ * bool at_start,
+ * byte* stack_area_base,
+ * bool direct_call)
+ */
+
+#define __ ACCESS_MASM((&masm_))
+
+RegExpMacroAssemblerX64::RegExpMacroAssemblerX64(
+ Mode mode,
+ int registers_to_save,
+ Zone* zone)
+ : NativeRegExpMacroAssembler(zone),
+ masm_(zone->isolate(), NULL, kRegExpCodeSize),
+ no_root_array_scope_(&masm_),
+ code_relative_fixup_positions_(4, zone),
+ mode_(mode),
+ num_registers_(registers_to_save),
+ num_saved_registers_(registers_to_save),
+ entry_label_(),
+ start_label_(),
+ success_label_(),
+ backtrack_label_(),
+ exit_label_() {
+ ASSERT_EQ(0, registers_to_save % 2);
+ __ jmp(&entry_label_); // We'll write the entry code when we know more.
+ __ bind(&start_label_); // And then continue from here.
+}
+
+
+RegExpMacroAssemblerX64::~RegExpMacroAssemblerX64() {
+ // Unuse labels in case we throw away the assembler without calling GetCode.
+ entry_label_.Unuse();
+ start_label_.Unuse();
+ success_label_.Unuse();
+ backtrack_label_.Unuse();
+ exit_label_.Unuse();
+ check_preempt_label_.Unuse();
+ stack_overflow_label_.Unuse();
+}
+
+
+int RegExpMacroAssemblerX64::stack_limit_slack() {
+ return RegExpStack::kStackLimitSlack;
+}
+
+
+void RegExpMacroAssemblerX64::AdvanceCurrentPosition(int by) {
+ if (by != 0) {
+ __ addq(rdi, Immediate(by * char_size()));
+ }
+}
+
+
+void RegExpMacroAssemblerX64::AdvanceRegister(int reg, int by) {
+ ASSERT(reg >= 0);
+ ASSERT(reg < num_registers_);
+ if (by != 0) {
+ __ addq(register_location(reg), Immediate(by));
+ }
+}
+
+
+void RegExpMacroAssemblerX64::Backtrack() {
+ CheckPreemption();
+ // Pop Code* offset from backtrack stack, add Code* and jump to location.
+ Pop(rbx);
+ __ addq(rbx, code_object_pointer());
+ __ jmp(rbx);
+}
+
+
+void RegExpMacroAssemblerX64::Bind(Label* label) {
+ __ bind(label);
+}
+
+
+void RegExpMacroAssemblerX64::CheckCharacter(uint32_t c, Label* on_equal) {
+ __ cmpl(current_character(), Immediate(c));
+ BranchOrBacktrack(equal, on_equal);
+}
+
+
+void RegExpMacroAssemblerX64::CheckCharacterGT(uc16 limit, Label* on_greater) {
+ __ cmpl(current_character(), Immediate(limit));
+ BranchOrBacktrack(greater, 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?
+ __ 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));
+ __ cmpq(rax, Operand(rbp, kInputStart));
+ BranchOrBacktrack(equal, on_at_start);
+ __ bind(&not_at_start);
+}
+
+
+void RegExpMacroAssemblerX64::CheckNotAtStart(Label* on_not_at_start) {
+ // Did we start the match at the start of the string at all?
+ __ 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));
+ __ cmpq(rax, Operand(rbp, kInputStart));
+ BranchOrBacktrack(not_equal, on_not_at_start);
+}
+
+
+void RegExpMacroAssemblerX64::CheckCharacterLT(uc16 limit, Label* on_less) {
+ __ cmpl(current_character(), Immediate(limit));
+ BranchOrBacktrack(less, on_less);
+}
+
+
+void RegExpMacroAssemblerX64::CheckGreedyLoop(Label* on_equal) {
+ Label fallthrough;
+ __ cmpl(rdi, Operand(backtrack_stackpointer(), 0));
+ __ j(not_equal, &fallthrough);
+ Drop();
+ BranchOrBacktrack(no_condition, on_equal);
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerX64::CheckNotBackReferenceIgnoreCase(
+ int start_reg,
+ Label* on_no_match) {
+ Label fallthrough;
+ __ movq(rdx, register_location(start_reg)); // Offset of start of capture
+ __ movq(rbx, register_location(start_reg + 1)); // Offset of end of capture
+ __ subq(rbx, rdx); // Length of capture.
+
+ // -----------------------
+ // rdx = Start offset of capture.
+ // rbx = Length of capture
+
+ // If length is negative, this code will fail (it's a symptom of a partial or
+ // illegal capture where start of capture after end of capture).
+ // This must not happen (no back-reference can reference a capture that wasn't
+ // closed before in the reg-exp, and we must not generate code that can cause
+ // this condition).
+
+ // If length is zero, either the capture is empty or it is nonparticipating.
+ // In either case succeed immediately.
+ __ j(equal, &fallthrough);
+
+ // -----------------------
+ // rdx - Start of capture
+ // rbx - length of capture
+ // Check that there are sufficient characters left in the input.
+ __ movl(rax, rdi);
+ __ addl(rax, rbx);
+ BranchOrBacktrack(greater, on_no_match);
+
+ if (mode_ == ASCII) {
+ Label loop_increment;
+ if (on_no_match == NULL) {
+ on_no_match = &backtrack_label_;
+ }
+
+ __ lea(r9, Operand(rsi, rdx, times_1, 0));
+ __ lea(r11, Operand(rsi, rdi, times_1, 0));
+ __ addq(rbx, r9); // End of capture
+ // ---------------------
+ // r11 - current input character address
+ // r9 - current capture character address
+ // rbx - end of capture
+
+ Label loop;
+ __ bind(&loop);
+ __ movzxbl(rdx, Operand(r9, 0));
+ __ movzxbl(rax, Operand(r11, 0));
+ // al - input character
+ // dl - capture character
+ __ cmpb(rax, rdx);
+ __ j(equal, &loop_increment);
+
+ // Mismatch, try case-insensitive match (converting letters to lower-case).
+ // I.e., if or-ing with 0x20 makes values equal and in range 'a'-'z', it's
+ // a match.
+ __ or_(rax, Immediate(0x20)); // Convert match character to lower-case.
+ __ or_(rdx, Immediate(0x20)); // Convert capture character to lower-case.
+ __ cmpb(rax, rdx);
+ __ j(not_equal, on_no_match); // Definitely not equal.
+ __ subb(rax, Immediate('a'));
+ __ cmpb(rax, Immediate('z' - 'a'));
+ __ j(below_equal, &loop_increment); // In range 'a'-'z'.
+ // Latin-1: Check for values in range [224,254] but not 247.
+ __ subb(rax, Immediate(224 - 'a'));
+ __ cmpb(rax, Immediate(254 - 224));
+ __ j(above, on_no_match); // Weren't Latin-1 letters.
+ __ cmpb(rax, Immediate(247 - 224)); // Check for 247.
+ __ j(equal, on_no_match);
+ __ bind(&loop_increment);
+ // Increment pointers into match and capture strings.
+ __ addq(r11, Immediate(1));
+ __ addq(r9, Immediate(1));
+ // Compare to end of capture, and loop if not done.
+ __ cmpq(r9, rbx);
+ __ j(below, &loop);
+
+ // Compute new value of character position after the matched part.
+ __ movq(rdi, r11);
+ __ subq(rdi, rsi);
+ } else {
+ ASSERT(mode_ == UC16);
+ // Save important/volatile registers before calling C function.
+#ifndef _WIN64
+ // Caller save on Linux and callee save in Windows.
+ __ push(rsi);
+ __ push(rdi);
+#endif
+ __ push(backtrack_stackpointer());
+
+ static const int num_arguments = 4;
+ __ PrepareCallCFunction(num_arguments);
+
+ // Put arguments into parameter registers. Parameters are
+ // Address byte_offset1 - Address captured substring's start.
+ // Address byte_offset2 - Address of current character position.
+ // size_t byte_length - length of capture in bytes(!)
+ // Isolate* isolate
+#ifdef _WIN64
+ // Compute and set byte_offset1 (start of capture).
+ __ lea(rcx, Operand(rsi, rdx, times_1, 0));
+ // Set byte_offset2.
+ __ lea(rdx, Operand(rsi, rdi, times_1, 0));
+ // Set byte_length.
+ __ movq(r8, rbx);
+ // Isolate.
+ __ LoadAddress(r9, ExternalReference::isolate_address(isolate()));
+#else // AMD64 calling convention
+ // Compute byte_offset2 (current position = rsi+rdi).
+ __ lea(rax, Operand(rsi, rdi, times_1, 0));
+ // Compute and set byte_offset1 (start of capture).
+ __ lea(rdi, Operand(rsi, rdx, times_1, 0));
+ // Set byte_offset2.
+ __ movq(rsi, rax);
+ // Set byte_length.
+ __ movq(rdx, rbx);
+ // Isolate.
+ __ LoadAddress(rcx, ExternalReference::isolate_address(isolate()));
+#endif
+
+ { // 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(isolate());
+ __ CallCFunction(compare, num_arguments);
+ }
+
+ // Restore original values before reacting on result value.
+ __ Move(code_object_pointer(), masm_.CodeObject());
+ __ pop(backtrack_stackpointer());
+#ifndef _WIN64
+ __ pop(rdi);
+ __ pop(rsi);
+#endif
+
+ // Check if function returned non-zero for success or zero for failure.
+ __ testq(rax, rax);
+ BranchOrBacktrack(zero, on_no_match);
+ // On success, increment position by length of capture.
+ // Requires that rbx is callee save (true for both Win64 and AMD64 ABIs).
+ __ addq(rdi, rbx);
+ }
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerX64::CheckNotBackReference(
+ int start_reg,
+ Label* on_no_match) {
+ Label fallthrough;
+
+ // Find length of back-referenced capture.
+ __ movq(rdx, register_location(start_reg));
+ __ movq(rax, register_location(start_reg + 1));
+ __ subq(rax, rdx); // Length to check.
+
+ // Fail on partial or illegal capture (start of capture after end of capture).
+ // This must not happen (no back-reference can reference a capture that wasn't
+ // closed before in the reg-exp).
+ __ Check(greater_equal, kInvalidCaptureReferenced);
+
+ // Succeed on empty capture (including non-participating capture)
+ __ j(equal, &fallthrough);
+
+ // -----------------------
+ // rdx - Start of capture
+ // rax - length of capture
+
+ // Check that there are sufficient characters left in the input.
+ __ movl(rbx, rdi);
+ __ addl(rbx, rax);
+ BranchOrBacktrack(greater, on_no_match);
+
+ // Compute pointers to match string and capture string
+ __ lea(rbx, Operand(rsi, rdi, times_1, 0)); // Start of match.
+ __ addq(rdx, rsi); // Start of capture.
+ __ lea(r9, Operand(rdx, rax, times_1, 0)); // End of capture
+
+ // -----------------------
+ // rbx - current capture character address.
+ // rbx - current input character address .
+ // r9 - end of input to match (capture length after rbx).
+
+ Label loop;
+ __ bind(&loop);
+ if (mode_ == ASCII) {
+ __ movzxbl(rax, Operand(rdx, 0));
+ __ cmpb(rax, Operand(rbx, 0));
+ } else {
+ ASSERT(mode_ == UC16);
+ __ movzxwl(rax, Operand(rdx, 0));
+ __ cmpw(rax, Operand(rbx, 0));
+ }
+ BranchOrBacktrack(not_equal, on_no_match);
+ // Increment pointers into capture and match string.
+ __ addq(rbx, Immediate(char_size()));
+ __ addq(rdx, Immediate(char_size()));
+ // Check if we have reached end of match area.
+ __ cmpq(rdx, r9);
+ __ j(below, &loop);
+
+ // Success.
+ // Set current character position to position after match.
+ __ movq(rdi, rbx);
+ __ subq(rdi, rsi);
+
+ __ bind(&fallthrough);
+}
+
+
+void RegExpMacroAssemblerX64::CheckNotCharacter(uint32_t c,
+ Label* on_not_equal) {
+ __ cmpl(current_character(), Immediate(c));
+ BranchOrBacktrack(not_equal, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerX64::CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_equal) {
+ if (c == 0) {
+ __ testl(current_character(), Immediate(mask));
+ } else {
+ __ movl(rax, Immediate(mask));
+ __ and_(rax, current_character());
+ __ cmpl(rax, Immediate(c));
+ }
+ BranchOrBacktrack(equal, on_equal);
+}
+
+
+void RegExpMacroAssemblerX64::CheckNotCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_not_equal) {
+ if (c == 0) {
+ __ testl(current_character(), Immediate(mask));
+ } else {
+ __ movl(rax, Immediate(mask));
+ __ and_(rax, current_character());
+ __ cmpl(rax, Immediate(c));
+ }
+ BranchOrBacktrack(not_equal, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerX64::CheckNotCharacterAfterMinusAnd(
+ uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal) {
+ ASSERT(minus < String::kMaxUtf16CodeUnit);
+ __ lea(rax, Operand(current_character(), -minus));
+ __ and_(rax, Immediate(mask));
+ __ cmpl(rax, Immediate(c));
+ BranchOrBacktrack(not_equal, on_not_equal);
+}
+
+
+void RegExpMacroAssemblerX64::CheckCharacterInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_in_range) {
+ __ leal(rax, Operand(current_character(), -from));
+ __ cmpl(rax, Immediate(to - from));
+ BranchOrBacktrack(below_equal, on_in_range);
+}
+
+
+void RegExpMacroAssemblerX64::CheckCharacterNotInRange(
+ uc16 from,
+ uc16 to,
+ Label* on_not_in_range) {
+ __ leal(rax, Operand(current_character(), -from));
+ __ cmpl(rax, Immediate(to - from));
+ BranchOrBacktrack(above, on_not_in_range);
+}
+
+
+void RegExpMacroAssemblerX64::CheckBitInTable(
+ Handle<ByteArray> table,
+ Label* on_bit_set) {
+ __ Move(rax, table);
+ Register index = current_character();
+ if (mode_ != ASCII || kTableMask != String::kMaxOneByteCharCode) {
+ __ movq(rbx, current_character());
+ __ and_(rbx, Immediate(kTableMask));
+ index = rbx;
+ }
+ __ cmpb(FieldOperand(rax, index, times_1, ByteArray::kHeaderSize),
+ Immediate(0));
+ BranchOrBacktrack(not_equal, on_bit_set);
+}
+
+
+bool RegExpMacroAssemblerX64::CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match) {
+ // Range checks (c in min..max) are generally implemented by an unsigned
+ // (c - min) <= (max - min) check, using the sequence:
+ // lea(rax, Operand(current_character(), -min)) or sub(rax, Immediate(min))
+ // cmp(rax, Immediate(max - min))
+ switch (type) {
+ case 's':
+ // Match space-characters
+ if (mode_ == ASCII) {
+ // One byte space characters are '\t'..'\r', ' ' and \u00a0.
+ Label success;
+ __ cmpl(current_character(), Immediate(' '));
+ __ j(equal, &success, Label::kNear);
+ // Check range 0x09..0x0d
+ __ lea(rax, Operand(current_character(), -'\t'));
+ __ cmpl(rax, Immediate('\r' - '\t'));
+ __ j(below_equal, &success, Label::kNear);
+ // \u00a0 (NBSP).
+ __ cmpl(rax, Immediate(0x00a0 - '\t'));
+ BranchOrBacktrack(not_equal, on_no_match);
+ __ bind(&success);
+ return true;
+ }
+ return false;
+ case 'S':
+ // The emitted code for generic character classes is good enough.
+ return false;
+ case 'd':
+ // Match ASCII digits ('0'..'9')
+ __ lea(rax, Operand(current_character(), -'0'));
+ __ cmpl(rax, Immediate('9' - '0'));
+ BranchOrBacktrack(above, on_no_match);
+ return true;
+ case 'D':
+ // Match non ASCII-digits
+ __ lea(rax, Operand(current_character(), -'0'));
+ __ cmpl(rax, Immediate('9' - '0'));
+ BranchOrBacktrack(below_equal, on_no_match);
+ return true;
+ case '.': {
+ // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
+ __ movl(rax, current_character());
+ __ xor_(rax, Immediate(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
+ __ subl(rax, Immediate(0x0b));
+ __ cmpl(rax, Immediate(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.
+ __ subl(rax, Immediate(0x2028 - 0x0b));
+ __ cmpl(rax, Immediate(0x2029 - 0x2028));
+ BranchOrBacktrack(below_equal, on_no_match);
+ }
+ return true;
+ }
+ case 'n': {
+ // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
+ __ movl(rax, current_character());
+ __ xor_(rax, Immediate(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
+ __ subl(rax, Immediate(0x0b));
+ __ cmpl(rax, Immediate(0x0c - 0x0b));
+ if (mode_ == ASCII) {
+ BranchOrBacktrack(above, on_no_match);
+ } else {
+ Label done;
+ BranchOrBacktrack(below_equal, &done);
+ // Compare original value to 0x2028 and 0x2029, using the already
+ // computed (current_char ^ 0x01 - 0x0b). I.e., check for
+ // 0x201d (0x2028 - 0x0b) or 0x201e.
+ __ subl(rax, Immediate(0x2028 - 0x0b));
+ __ cmpl(rax, Immediate(0x2029 - 0x2028));
+ BranchOrBacktrack(above, on_no_match);
+ __ bind(&done);
+ }
+ return true;
+ }
+ case 'w': {
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ cmpl(current_character(), Immediate('z'));
+ BranchOrBacktrack(above, on_no_match);
+ }
+ __ movq(rbx, ExternalReference::re_word_character_map());
+ ASSERT_EQ(0, word_character_map[0]); // Character '\0' is not a word char.
+ __ testb(Operand(rbx, current_character(), times_1, 0),
+ current_character());
+ BranchOrBacktrack(zero, on_no_match);
+ return true;
+ }
+ case 'W': {
+ Label done;
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ cmpl(current_character(), Immediate('z'));
+ __ j(above, &done);
+ }
+ __ movq(rbx, ExternalReference::re_word_character_map());
+ ASSERT_EQ(0, word_character_map[0]); // Character '\0' is not a word char.
+ __ testb(Operand(rbx, current_character(), times_1, 0),
+ current_character());
+ BranchOrBacktrack(not_zero, on_no_match);
+ if (mode_ != ASCII) {
+ __ bind(&done);
+ }
+ return true;
+ }
+
+ case '*':
+ // Match any character.
+ return true;
+ // No custom implementation (yet): s(UC16), S(UC16).
+ default:
+ return false;
+ }
+}
+
+
+void RegExpMacroAssemblerX64::Fail() {
+ STATIC_ASSERT(FAILURE == 0); // Return value for failure is zero.
+ if (!global()) {
+ __ Set(rax, FAILURE);
+ }
+ __ jmp(&exit_label_);
+}
+
+
+Handle<HeapObject> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
+ Label return_rax;
+ // Finalize code - write the entry point code now we know how many
+ // registers we need.
+ // 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(rbp);
+ __ movq(rbp, rsp);
+ // Save parameters and callee-save registers. Order here should correspond
+ // to order of kBackup_ebx etc.
+#ifdef _WIN64
+ // MSVC passes arguments in rcx, rdx, r8, r9, with backing stack slots.
+ // Store register parameters in pre-allocated stack slots,
+ __ movq(Operand(rbp, kInputString), rcx);
+ __ movq(Operand(rbp, kStartIndex), rdx); // Passed as int32 in edx.
+ __ movq(Operand(rbp, kInputStart), r8);
+ __ movq(Operand(rbp, kInputEnd), r9);
+ // Callee-save on Win64.
+ __ push(rsi);
+ __ push(rdi);
+ __ push(rbx);
+#else
+ // GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9 (and then on stack).
+ // Push register parameters on stack for reference.
+ ASSERT_EQ(kInputString, -1 * kPointerSize);
+ ASSERT_EQ(kStartIndex, -2 * kPointerSize);
+ ASSERT_EQ(kInputStart, -3 * kPointerSize);
+ ASSERT_EQ(kInputEnd, -4 * kPointerSize);
+ ASSERT_EQ(kRegisterOutput, -5 * kPointerSize);
+ ASSERT_EQ(kNumOutputRegisters, -6 * kPointerSize);
+ __ push(rdi);
+ __ push(rsi);
+ __ push(rdx);
+ __ push(rcx);
+ __ push(r8);
+ __ push(r9);
+
+ __ push(rbx); // Callee-save
+#endif
+
+ __ push(Immediate(0)); // Number of successful matches in a global regexp.
+ __ push(Immediate(0)); // Make room for "input start - 1" constant.
+
+ // Check if we have space on the stack for registers.
+ Label stack_limit_hit;
+ Label stack_ok;
+
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ movq(rcx, rsp);
+ __ movq(kScratchRegister, stack_limit);
+ __ subq(rcx, Operand(kScratchRegister, 0));
+ // Handle it if the stack pointer is already below the stack limit.
+ __ j(below_equal, &stack_limit_hit);
+ // Check if there is room for the variable number of registers above
+ // the stack limit.
+ __ cmpq(rcx, Immediate(num_registers_ * kPointerSize));
+ __ j(above_equal, &stack_ok);
+ // Exit with OutOfMemory exception. There is not enough space on the stack
+ // for our working registers.
+ __ Set(rax, EXCEPTION);
+ __ jmp(&return_rax);
+
+ __ bind(&stack_limit_hit);
+ __ Move(code_object_pointer(), masm_.CodeObject());
+ CallCheckStackGuardState(); // Preserves no registers beside rbp and rsp.
+ __ testq(rax, rax);
+ // If returned value is non-zero, we exit with the returned value as result.
+ __ j(not_zero, &return_rax);
+
+ __ bind(&stack_ok);
+
+ // Allocate space on stack for registers.
+ __ subq(rsp, Immediate(num_registers_ * kPointerSize));
+ // Load string length.
+ __ movq(rsi, Operand(rbp, kInputEnd));
+ // Load input position.
+ __ movq(rdi, Operand(rbp, kInputStart));
+ // Set up rdi to be negative offset from string end.
+ __ subq(rdi, rsi);
+ // Set rax to address of char before start of the string
+ // (effectively string position -1).
+ __ movq(rbx, Operand(rbp, kStartIndex));
+ __ neg(rbx);
+ if (mode_ == UC16) {
+ __ lea(rax, Operand(rdi, rbx, times_2, -char_size()));
+ } else {
+ __ lea(rax, Operand(rdi, rbx, times_1, -char_size()));
+ }
+ // Store this value in a local variable, for use when clearing
+ // position registers.
+ __ movq(Operand(rbp, kInputStartMinusOne), rax);
+
+#ifdef WIN32
+ // Ensure that we have written to each stack page, in order. Skipping a page
+ // on Windows can cause segmentation faults. Assuming page size is 4k.
+ const int kPageSize = 4096;
+ const int kRegistersPerPage = kPageSize / kPointerSize;
+ for (int i = num_saved_registers_ + kRegistersPerPage - 1;
+ i < num_registers_;
+ i += kRegistersPerPage) {
+ __ movq(register_location(i), rax); // One write every page.
+ }
+#endif // WIN32
+
+ // Initialize code object pointer.
+ __ Move(code_object_pointer(), masm_.CodeObject());
+
+ Label load_char_start_regexp, start_regexp;
+ // Load newline if index is at start, previous character otherwise.
+ __ cmpl(Operand(rbp, kStartIndex), Immediate(0));
+ __ j(not_equal, &load_char_start_regexp, Label::kNear);
+ __ Set(current_character(), '\n');
+ __ jmp(&start_regexp, Label::kNear);
+
+ // Global regexp restarts matching here.
+ __ bind(&load_char_start_regexp);
+ // Load previous char as initial value of current character register.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&start_regexp);
+
+ // Initialize on-stack registers.
+ if (num_saved_registers_ > 0) {
+ // Fill saved registers with initial value = start offset - 1
+ // Fill in stack push order, to avoid accessing across an unwritten
+ // page (a problem on Windows).
+ if (num_saved_registers_ > 8) {
+ __ Set(rcx, kRegisterZero);
+ Label init_loop;
+ __ bind(&init_loop);
+ __ movq(Operand(rbp, rcx, times_1, 0), rax);
+ __ subq(rcx, Immediate(kPointerSize));
+ __ cmpq(rcx,
+ Immediate(kRegisterZero - num_saved_registers_ * kPointerSize));
+ __ j(greater, &init_loop);
+ } else { // Unroll the loop.
+ for (int i = 0; i < num_saved_registers_; i++) {
+ __ movq(register_location(i), rax);
+ }
+ }
+ }
+
+ // Initialize backtrack stack pointer.
+ __ movq(backtrack_stackpointer(), Operand(rbp, kStackHighEnd));
+
+ __ jmp(&start_label_);
+
+ // Exit code:
+ if (success_label_.is_linked()) {
+ // Save captures when successful.
+ __ bind(&success_label_);
+ if (num_saved_registers_ > 0) {
+ // copy captures to output
+ __ movq(rdx, Operand(rbp, kStartIndex));
+ __ movq(rbx, Operand(rbp, kRegisterOutput));
+ __ movq(rcx, Operand(rbp, kInputEnd));
+ __ subq(rcx, Operand(rbp, kInputStart));
+ if (mode_ == UC16) {
+ __ lea(rcx, Operand(rcx, rdx, times_2, 0));
+ } else {
+ __ addq(rcx, rdx);
+ }
+ for (int i = 0; i < num_saved_registers_; i++) {
+ __ movq(rax, register_location(i));
+ if (i == 0 && global_with_zero_length_check()) {
+ // Keep capture start in rdx for the zero-length check later.
+ __ movq(rdx, rax);
+ }
+ __ addq(rax, rcx); // Convert to index from start, not end.
+ if (mode_ == UC16) {
+ __ sar(rax, Immediate(1)); // Convert byte index to character index.
+ }
+ __ movl(Operand(rbx, i * kIntSize), rax);
+ }
+ }
+
+ if (global()) {
+ // Restart matching if the regular expression is flagged as global.
+ // Increment success counter.
+ __ incq(Operand(rbp, kSuccessfulCaptures));
+ // Capture results have been stored, so the number of remaining global
+ // output registers is reduced by the number of stored captures.
+ __ movsxlq(rcx, Operand(rbp, kNumOutputRegisters));
+ __ subq(rcx, Immediate(num_saved_registers_));
+ // Check whether we have enough room for another set of capture results.
+ __ cmpq(rcx, Immediate(num_saved_registers_));
+ __ j(less, &exit_label_);
+
+ __ movq(Operand(rbp, kNumOutputRegisters), rcx);
+ // Advance the location for output.
+ __ addq(Operand(rbp, kRegisterOutput),
+ Immediate(num_saved_registers_ * kIntSize));
+
+ // Prepare rax to initialize registers with its value in the next run.
+ __ movq(rax, Operand(rbp, kInputStartMinusOne));
+
+ if (global_with_zero_length_check()) {
+ // Special case for zero-length matches.
+ // rdx: capture start index
+ __ cmpq(rdi, rdx);
+ // Not a zero-length match, restart.
+ __ j(not_equal, &load_char_start_regexp);
+ // rdi (offset from the end) is zero if we already reached the end.
+ __ testq(rdi, rdi);
+ __ j(zero, &exit_label_, Label::kNear);
+ // Advance current position after a zero-length match.
+ if (mode_ == UC16) {
+ __ addq(rdi, Immediate(2));
+ } else {
+ __ incq(rdi);
+ }
+ }
+
+ __ jmp(&load_char_start_regexp);
+ } else {
+ __ movq(rax, Immediate(SUCCESS));
+ }
+ }
+
+ __ bind(&exit_label_);
+ if (global()) {
+ // Return the number of successful captures.
+ __ movq(rax, Operand(rbp, kSuccessfulCaptures));
+ }
+
+ __ bind(&return_rax);
+#ifdef _WIN64
+ // Restore callee save registers.
+ __ lea(rsp, Operand(rbp, kLastCalleeSaveRegister));
+ __ pop(rbx);
+ __ pop(rdi);
+ __ pop(rsi);
+ // Stack now at rbp.
+#else
+ // Restore callee save register.
+ __ movq(rbx, Operand(rbp, kBackup_rbx));
+ // Skip rsp to rbp.
+ __ movq(rsp, rbp);
+#endif
+ // Exit function frame, restore previous one.
+ __ pop(rbp);
+ __ ret(0);
+
+ // Backtrack code (branch target for conditional backtracks).
+ if (backtrack_label_.is_linked()) {
+ __ bind(&backtrack_label_);
+ Backtrack();
+ }
+
+ Label exit_with_exception;
+
+ // Preempt-code
+ if (check_preempt_label_.is_linked()) {
+ SafeCallTarget(&check_preempt_label_);
+
+ __ push(backtrack_stackpointer());
+ __ push(rdi);
+
+ CallCheckStackGuardState();
+ __ testq(rax, rax);
+ // If returning non-zero, we should end execution with the given
+ // result as return value.
+ __ j(not_zero, &return_rax);
+
+ // Restore registers.
+ __ Move(code_object_pointer(), masm_.CodeObject());
+ __ pop(rdi);
+ __ pop(backtrack_stackpointer());
+ // String might have moved: Reload esi from frame.
+ __ movq(rsi, Operand(rbp, kInputEnd));
+ SafeReturn();
+ }
+
+ // Backtrack stack overflow code.
+ if (stack_overflow_label_.is_linked()) {
+ SafeCallTarget(&stack_overflow_label_);
+ // Reached if the backtrack-stack limit has been hit.
+
+ Label grow_failed;
+ // Save registers before calling C function
+#ifndef _WIN64
+ // Callee-save in Microsoft 64-bit ABI, but not in AMD64 ABI.
+ __ push(rsi);
+ __ push(rdi);
+#endif
+
+ // Call GrowStack(backtrack_stackpointer())
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments);
+#ifdef _WIN64
+ // Microsoft passes parameters in rcx, rdx, r8.
+ // First argument, backtrack stackpointer, is already in rcx.
+ __ lea(rdx, Operand(rbp, kStackHighEnd)); // Second argument
+ __ LoadAddress(r8, ExternalReference::isolate_address(isolate()));
+#else
+ // AMD64 ABI passes parameters in rdi, rsi, rdx.
+ __ movq(rdi, backtrack_stackpointer()); // First argument.
+ __ lea(rsi, Operand(rbp, kStackHighEnd)); // Second argument.
+ __ LoadAddress(rdx, ExternalReference::isolate_address(isolate()));
+#endif
+ ExternalReference grow_stack =
+ ExternalReference::re_grow_stack(isolate());
+ __ CallCFunction(grow_stack, num_arguments);
+ // If return NULL, we have failed to grow the stack, and
+ // must exit with a stack-overflow exception.
+ __ testq(rax, rax);
+ __ j(equal, &exit_with_exception);
+ // Otherwise use return value as new stack pointer.
+ __ movq(backtrack_stackpointer(), rax);
+ // Restore saved registers and continue.
+ __ Move(code_object_pointer(), masm_.CodeObject());
+#ifndef _WIN64
+ __ pop(rdi);
+ __ pop(rsi);
+#endif
+ SafeReturn();
+ }
+
+ if (exit_with_exception.is_linked()) {
+ // If any of the code above needed to exit with an exception.
+ __ bind(&exit_with_exception);
+ // Exit with Result EXCEPTION(-1) to signal thrown exception.
+ __ Set(rax, EXCEPTION);
+ __ jmp(&return_rax);
+ }
+
+ FixupCodeRelativePositions();
+
+ CodeDesc code_desc;
+ masm_.GetCode(&code_desc);
+ Isolate* isolate = ISOLATE;
+ Handle<Code> code = isolate->factory()->NewCode(
+ code_desc, Code::ComputeFlags(Code::REGEXP),
+ masm_.CodeObject());
+ PROFILE(isolate, RegExpCodeCreateEvent(*code, *source));
+ return Handle<HeapObject>::cast(code);
+}
+
+
+void RegExpMacroAssemblerX64::GoTo(Label* to) {
+ BranchOrBacktrack(no_condition, to);
+}
+
+
+void RegExpMacroAssemblerX64::IfRegisterGE(int reg,
+ int comparand,
+ Label* if_ge) {
+ __ cmpq(register_location(reg), Immediate(comparand));
+ BranchOrBacktrack(greater_equal, if_ge);
+}
+
+
+void RegExpMacroAssemblerX64::IfRegisterLT(int reg,
+ int comparand,
+ Label* if_lt) {
+ __ cmpq(register_location(reg), Immediate(comparand));
+ BranchOrBacktrack(less, if_lt);
+}
+
+
+void RegExpMacroAssemblerX64::IfRegisterEqPos(int reg,
+ Label* if_eq) {
+ __ cmpq(rdi, register_location(reg));
+ BranchOrBacktrack(equal, if_eq);
+}
+
+
+RegExpMacroAssembler::IrregexpImplementation
+ RegExpMacroAssemblerX64::Implementation() {
+ return kX64Implementation;
+}
+
+
+void RegExpMacroAssemblerX64::LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds,
+ int characters) {
+ ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
+ ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
+ if (check_bounds) {
+ CheckPosition(cp_offset + characters - 1, on_end_of_input);
+ }
+ LoadCurrentCharacterUnchecked(cp_offset, characters);
+}
+
+
+void RegExpMacroAssemblerX64::PopCurrentPosition() {
+ Pop(rdi);
+}
+
+
+void RegExpMacroAssemblerX64::PopRegister(int register_index) {
+ Pop(rax);
+ __ movq(register_location(register_index), rax);
+}
+
+
+void RegExpMacroAssemblerX64::PushBacktrack(Label* label) {
+ Push(label);
+ CheckStackLimit();
+}
+
+
+void RegExpMacroAssemblerX64::PushCurrentPosition() {
+ Push(rdi);
+}
+
+
+void RegExpMacroAssemblerX64::PushRegister(int register_index,
+ StackCheckFlag check_stack_limit) {
+ __ movq(rax, register_location(register_index));
+ Push(rax);
+ if (check_stack_limit) CheckStackLimit();
+}
+
+
+void RegExpMacroAssemblerX64::ReadCurrentPositionFromRegister(int reg) {
+ __ movq(rdi, register_location(reg));
+}
+
+
+void RegExpMacroAssemblerX64::ReadStackPointerFromRegister(int reg) {
+ __ movq(backtrack_stackpointer(), register_location(reg));
+ __ addq(backtrack_stackpointer(), Operand(rbp, kStackHighEnd));
+}
+
+
+void RegExpMacroAssemblerX64::SetCurrentPositionFromEnd(int by) {
+ Label after_position;
+ __ cmpq(rdi, Immediate(-by * char_size()));
+ __ j(greater_equal, &after_position, Label::kNear);
+ __ movq(rdi, Immediate(-by * char_size()));
+ // On RegExp code entry (where this operation is used), the character before
+ // the current position is expected to be already loaded.
+ // We have advanced the position, so it's safe to read backwards.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&after_position);
+}
+
+
+void RegExpMacroAssemblerX64::SetRegister(int register_index, int to) {
+ ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
+ __ movq(register_location(register_index), Immediate(to));
+}
+
+
+bool RegExpMacroAssemblerX64::Succeed() {
+ __ jmp(&success_label_);
+ return global();
+}
+
+
+void RegExpMacroAssemblerX64::WriteCurrentPositionToRegister(int reg,
+ int cp_offset) {
+ if (cp_offset == 0) {
+ __ movq(register_location(reg), rdi);
+ } else {
+ __ lea(rax, Operand(rdi, cp_offset * char_size()));
+ __ movq(register_location(reg), rax);
+ }
+}
+
+
+void RegExpMacroAssemblerX64::ClearRegisters(int reg_from, int reg_to) {
+ ASSERT(reg_from <= reg_to);
+ __ movq(rax, Operand(rbp, kInputStartMinusOne));
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ __ movq(register_location(reg), rax);
+ }
+}
+
+
+void RegExpMacroAssemblerX64::WriteStackPointerToRegister(int reg) {
+ __ movq(rax, backtrack_stackpointer());
+ __ subq(rax, Operand(rbp, kStackHighEnd));
+ __ movq(register_location(reg), rax);
+}
+
+
+// Private methods:
+
+void RegExpMacroAssemblerX64::CallCheckStackGuardState() {
+ // This function call preserves no register values. Caller should
+ // store anything volatile in a C call or overwritten by this function.
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments);
+#ifdef _WIN64
+ // Second argument: Code* of self. (Do this before overwriting r8).
+ __ movq(rdx, code_object_pointer());
+ // Third argument: RegExp code frame pointer.
+ __ movq(r8, rbp);
+ // First argument: Next address on the stack (will be address of
+ // return address).
+ __ lea(rcx, Operand(rsp, -kPointerSize));
+#else
+ // Third argument: RegExp code frame pointer.
+ __ movq(rdx, rbp);
+ // Second argument: Code* of self.
+ __ movq(rsi, code_object_pointer());
+ // First argument: Next address on the stack (will be address of
+ // return address).
+ __ lea(rdi, Operand(rsp, -kPointerSize));
+#endif
+ ExternalReference stack_check =
+ ExternalReference::re_check_stack_guard_state(isolate());
+ __ CallCFunction(stack_check, num_arguments);
+}
+
+
+// Helper function for reading a value out of a stack frame.
+template <typename T>
+static T& frame_entry(Address re_frame, int frame_offset) {
+ return reinterpret_cast<T&>(Memory::int32_at(re_frame + frame_offset));
+}
+
+
+int RegExpMacroAssemblerX64::CheckStackGuardState(Address* return_address,
+ Code* re_code,
+ Address re_frame) {
+ Isolate* isolate = frame_entry<Isolate*>(re_frame, kIsolate);
+ ASSERT(isolate == Isolate::Current());
+ if (isolate->stack_guard()->IsStackOverflow()) {
+ isolate->StackOverflow();
+ return EXCEPTION;
+ }
+
+ // If not real stack overflow the stack guard was used to interrupt
+ // execution for another purpose.
+
+ // If this is a direct call from JavaScript retry the RegExp forcing the call
+ // through the runtime system. Currently the direct call cannot handle a GC.
+ if (frame_entry<int>(re_frame, kDirectCall) == 1) {
+ return RETRY;
+ }
+
+ // Prepare for possible GC.
+ HandleScope handles(isolate);
+ Handle<Code> code_handle(re_code);
+
+ Handle<String> subject(frame_entry<String*>(re_frame, kInputString));
+
+ // Current string.
+ bool is_ascii = subject->IsOneByteRepresentationUnderneath();
+
+ ASSERT(re_code->instruction_start() <= *return_address);
+ ASSERT(*return_address <=
+ re_code->instruction_start() + re_code->instruction_size());
+
+ MaybeObject* result = Execution::HandleStackGuardInterrupt(isolate);
+
+ if (*code_handle != re_code) { // Return address no longer valid
+ intptr_t delta = code_handle->address() - re_code->address();
+ // Overwrite the return address on the stack.
+ *return_address += delta;
+ }
+
+ if (result->IsException()) {
+ return EXCEPTION;
+ }
+
+ Handle<String> subject_tmp = subject;
+ int slice_offset = 0;
+
+ // Extract the underlying string and the slice offset.
+ if (StringShape(*subject_tmp).IsCons()) {
+ subject_tmp = Handle<String>(ConsString::cast(*subject_tmp)->first());
+ } else if (StringShape(*subject_tmp).IsSliced()) {
+ SlicedString* slice = SlicedString::cast(*subject_tmp);
+ subject_tmp = Handle<String>(slice->parent());
+ slice_offset = slice->offset();
+ }
+
+ // String might have changed.
+ if (subject_tmp->IsOneByteRepresentation() != is_ascii) {
+ // If we changed between an ASCII and an UC16 string, the specialized
+ // code cannot be used, and we need to restart regexp matching from
+ // scratch (including, potentially, compiling a new version of the code).
+ return RETRY;
+ }
+
+ // Otherwise, the content of the string might have moved. It must still
+ // be a sequential or external string with the same content.
+ // Update the start and end pointers in the stack frame to the current
+ // location (whether it has actually moved or not).
+ ASSERT(StringShape(*subject_tmp).IsSequential() ||
+ StringShape(*subject_tmp).IsExternal());
+
+ // The original start address of the characters to match.
+ const byte* start_address = frame_entry<const byte*>(re_frame, kInputStart);
+
+ // Find the current start address of the same character at the current string
+ // position.
+ int start_index = frame_entry<int>(re_frame, kStartIndex);
+ const byte* new_address = StringCharacterPosition(*subject_tmp,
+ start_index + slice_offset);
+
+ if (start_address != new_address) {
+ // If there is a difference, update the object pointer and start and end
+ // addresses in the RegExp stack frame to match the new value.
+ const byte* end_address = frame_entry<const byte* >(re_frame, kInputEnd);
+ int byte_length = static_cast<int>(end_address - start_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;
+}
+
+
+Operand RegExpMacroAssemblerX64::register_location(int register_index) {
+ ASSERT(register_index < (1<<30));
+ if (num_registers_ <= register_index) {
+ num_registers_ = register_index + 1;
+ }
+ return Operand(rbp, kRegisterZero - register_index * kPointerSize);
+}
+
+
+void RegExpMacroAssemblerX64::CheckPosition(int cp_offset,
+ Label* on_outside_input) {
+ __ cmpl(rdi, Immediate(-cp_offset * char_size()));
+ BranchOrBacktrack(greater_equal, on_outside_input);
+}
+
+
+void RegExpMacroAssemblerX64::BranchOrBacktrack(Condition condition,
+ Label* to) {
+ if (condition < 0) { // No condition
+ if (to == NULL) {
+ Backtrack();
+ return;
+ }
+ __ jmp(to);
+ return;
+ }
+ if (to == NULL) {
+ __ j(condition, &backtrack_label_);
+ return;
+ }
+ __ j(condition, to);
+}
+
+
+void RegExpMacroAssemblerX64::SafeCall(Label* to) {
+ __ call(to);
+}
+
+
+void RegExpMacroAssemblerX64::SafeCallTarget(Label* label) {
+ __ bind(label);
+ __ subq(Operand(rsp, 0), code_object_pointer());
+}
+
+
+void RegExpMacroAssemblerX64::SafeReturn() {
+ __ addq(Operand(rsp, 0), code_object_pointer());
+ __ ret(0);
+}
+
+
+void RegExpMacroAssemblerX64::Push(Register source) {
+ ASSERT(!source.is(backtrack_stackpointer()));
+ // Notice: This updates flags, unlike normal Push.
+ __ subq(backtrack_stackpointer(), Immediate(kIntSize));
+ __ movl(Operand(backtrack_stackpointer(), 0), source);
+}
+
+
+void RegExpMacroAssemblerX64::Push(Immediate value) {
+ // Notice: This updates flags, unlike normal Push.
+ __ subq(backtrack_stackpointer(), Immediate(kIntSize));
+ __ movl(Operand(backtrack_stackpointer(), 0), value);
+}
+
+
+void RegExpMacroAssemblerX64::FixupCodeRelativePositions() {
+ for (int i = 0, n = code_relative_fixup_positions_.length(); i < n; i++) {
+ int position = code_relative_fixup_positions_[i];
+ // The position succeeds a relative label offset from position.
+ // Patch the relative offset to be relative to the Code object pointer
+ // instead.
+ int patch_position = position - kIntSize;
+ int offset = masm_.long_at(patch_position);
+ masm_.long_at_put(patch_position,
+ offset
+ + position
+ + Code::kHeaderSize
+ - kHeapObjectTag);
+ }
+ code_relative_fixup_positions_.Clear();
+}
+
+
+void RegExpMacroAssemblerX64::Push(Label* backtrack_target) {
+ __ subq(backtrack_stackpointer(), Immediate(kIntSize));
+ __ movl(Operand(backtrack_stackpointer(), 0), backtrack_target);
+ MarkPositionForCodeRelativeFixup();
+}
+
+
+void RegExpMacroAssemblerX64::Pop(Register target) {
+ ASSERT(!target.is(backtrack_stackpointer()));
+ __ movsxlq(target, Operand(backtrack_stackpointer(), 0));
+ // Notice: This updates flags, unlike normal Pop.
+ __ addq(backtrack_stackpointer(), Immediate(kIntSize));
+}
+
+
+void RegExpMacroAssemblerX64::Drop() {
+ __ addq(backtrack_stackpointer(), Immediate(kIntSize));
+}
+
+
+void RegExpMacroAssemblerX64::CheckPreemption() {
+ // Check for preemption.
+ Label no_preempt;
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(isolate());
+ __ load_rax(stack_limit);
+ __ cmpq(rsp, rax);
+ __ j(above, &no_preempt);
+
+ SafeCall(&check_preempt_label_);
+
+ __ bind(&no_preempt);
+}
+
+
+void RegExpMacroAssemblerX64::CheckStackLimit() {
+ Label no_stack_overflow;
+ ExternalReference stack_limit =
+ ExternalReference::address_of_regexp_stack_limit(isolate());
+ __ load_rax(stack_limit);
+ __ cmpq(backtrack_stackpointer(), rax);
+ __ j(above, &no_stack_overflow);
+
+ SafeCall(&stack_overflow_label_);
+
+ __ bind(&no_stack_overflow);
+}
+
+
+void RegExpMacroAssemblerX64::LoadCurrentCharacterUnchecked(int cp_offset,
+ int characters) {
+ if (mode_ == ASCII) {
+ if (characters == 4) {
+ __ movl(current_character(), Operand(rsi, rdi, times_1, cp_offset));
+ } else if (characters == 2) {
+ __ movzxwl(current_character(), Operand(rsi, rdi, times_1, cp_offset));
+ } else {
+ ASSERT(characters == 1);
+ __ movzxbl(current_character(), Operand(rsi, rdi, times_1, cp_offset));
+ }
+ } else {
+ ASSERT(mode_ == UC16);
+ if (characters == 2) {
+ __ movl(current_character(),
+ Operand(rsi, rdi, times_1, cp_offset * sizeof(uc16)));
+ } else {
+ ASSERT(characters == 1);
+ __ movzxwl(current_character(),
+ Operand(rsi, rdi, times_1, cp_offset * sizeof(uc16)));
+ }
+ }
+}
+
+#undef __
+
+#endif // V8_INTERPRETED_REGEXP
+
+}} // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/x64/regexp-macro-assembler-x64.h b/chromium/v8/src/x64/regexp-macro-assembler-x64.h
new file mode 100644
index 00000000000..b230ea47fc6
--- /dev/null
+++ b/chromium/v8/src/x64/regexp-macro-assembler-x64.h
@@ -0,0 +1,302 @@
+// Copyright 2012 the V8 project authors. 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_X64_REGEXP_MACRO_ASSEMBLER_X64_H_
+#define V8_X64_REGEXP_MACRO_ASSEMBLER_X64_H_
+
+#include "x64/assembler-x64.h"
+#include "x64/assembler-x64-inl.h"
+#include "macro-assembler.h"
+#include "code.h"
+#include "x64/macro-assembler-x64.h"
+
+namespace v8 {
+namespace internal {
+
+#ifndef V8_INTERPRETED_REGEXP
+
+class RegExpMacroAssemblerX64: public NativeRegExpMacroAssembler {
+ public:
+ RegExpMacroAssemblerX64(Mode mode, int registers_to_save, Zone* zone);
+ virtual ~RegExpMacroAssemblerX64();
+ virtual int stack_limit_slack();
+ virtual void AdvanceCurrentPosition(int by);
+ virtual void AdvanceRegister(int reg, int by);
+ virtual void Backtrack();
+ virtual void Bind(Label* label);
+ virtual void CheckAtStart(Label* on_at_start);
+ virtual void CheckCharacter(uint32_t c, Label* on_equal);
+ virtual void CheckCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_equal);
+ virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
+ virtual void CheckCharacterLT(uc16 limit, Label* on_less);
+ // A "greedy loop" is a loop that is both greedy and with a simple
+ // body. It has a particularly simple implementation.
+ virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
+ virtual void CheckNotAtStart(Label* on_not_at_start);
+ virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
+ virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
+ Label* on_no_match);
+ virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
+ virtual void CheckNotCharacterAfterAnd(uint32_t c,
+ uint32_t mask,
+ Label* on_not_equal);
+ virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
+ uc16 minus,
+ uc16 mask,
+ Label* on_not_equal);
+ virtual void CheckCharacterInRange(uc16 from,
+ uc16 to,
+ Label* on_in_range);
+ virtual void CheckCharacterNotInRange(uc16 from,
+ uc16 to,
+ Label* on_not_in_range);
+ virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
+
+ // Checks whether the given offset from the current position is before
+ // the end of the string.
+ virtual void CheckPosition(int cp_offset, Label* on_outside_input);
+ virtual bool CheckSpecialCharacterClass(uc16 type,
+ Label* on_no_match);
+ virtual void Fail();
+ virtual Handle<HeapObject> GetCode(Handle<String> source);
+ virtual void GoTo(Label* label);
+ virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
+ virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
+ virtual void IfRegisterEqPos(int reg, Label* if_eq);
+ virtual IrregexpImplementation Implementation();
+ virtual void LoadCurrentCharacter(int cp_offset,
+ Label* on_end_of_input,
+ bool check_bounds = true,
+ int characters = 1);
+ virtual void PopCurrentPosition();
+ virtual void PopRegister(int register_index);
+ virtual void PushBacktrack(Label* label);
+ virtual void PushCurrentPosition();
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
+ virtual void ReadCurrentPositionFromRegister(int reg);
+ virtual void ReadStackPointerFromRegister(int reg);
+ virtual void SetCurrentPositionFromEnd(int by);
+ virtual void SetRegister(int register_index, int to);
+ virtual bool Succeed();
+ virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
+ virtual void ClearRegisters(int reg_from, int reg_to);
+ virtual void WriteStackPointerToRegister(int reg);
+
+ static Result Match(Handle<Code> regexp,
+ Handle<String> subject,
+ int* offsets_vector,
+ int offsets_vector_length,
+ int previous_index,
+ Isolate* isolate);
+
+ static Result Execute(Code* code,
+ String* input,
+ int start_offset,
+ const byte* input_start,
+ const byte* input_end,
+ int* output,
+ bool at_start);
+
+ // Called from RegExp if the stack-guard is triggered.
+ // If the code object is relocated, the return address is fixed before
+ // returning.
+ static int CheckStackGuardState(Address* return_address,
+ Code* re_code,
+ Address re_frame);
+
+ private:
+ // Offsets from rbp of function parameters and stored registers.
+ static const int kFramePointer = 0;
+ // Above the frame pointer - function parameters and return address.
+ static const int kReturn_eip = kFramePointer + kPointerSize;
+ static const int kFrameAlign = kReturn_eip + kPointerSize;
+
+#ifdef _WIN64
+ // Parameters (first four passed as registers, but with room on stack).
+ // In Microsoft 64-bit Calling Convention, there is room on the callers
+ // stack (before the return address) to spill parameter registers. We
+ // use this space to store the register passed parameters.
+ static const int kInputString = kFrameAlign;
+ // StartIndex is passed as 32 bit int.
+ static const int kStartIndex = kInputString + kPointerSize;
+ static const int kInputStart = kStartIndex + kPointerSize;
+ static const int kInputEnd = kInputStart + kPointerSize;
+ static const int kRegisterOutput = kInputEnd + kPointerSize;
+ // For the case of global regular expression, we have room to store at least
+ // one set of capture results. For the case of non-global regexp, we ignore
+ // this value. NumOutputRegisters is passed as 32-bit value. The upper
+ // 32 bit of this 64-bit stack slot may contain garbage.
+ static const int kNumOutputRegisters = kRegisterOutput + kPointerSize;
+ static const int kStackHighEnd = kNumOutputRegisters + kPointerSize;
+ // DirectCall is passed as 32 bit int (values 0 or 1).
+ static const int kDirectCall = kStackHighEnd + kPointerSize;
+ static const int kIsolate = kDirectCall + kPointerSize;
+#else
+ // In AMD64 ABI Calling Convention, the first six integer parameters
+ // are passed as registers, and caller must allocate space on the stack
+ // if it wants them stored. We push the parameters after the frame pointer.
+ static const int kInputString = kFramePointer - kPointerSize;
+ static const int kStartIndex = kInputString - kPointerSize;
+ static const int kInputStart = kStartIndex - kPointerSize;
+ static const int kInputEnd = kInputStart - kPointerSize;
+ static const int kRegisterOutput = kInputEnd - kPointerSize;
+ // For the case of global regular expression, we have room to store at least
+ // one set of capture results. For the case of non-global regexp, we ignore
+ // this value.
+ static const int kNumOutputRegisters = kRegisterOutput - kPointerSize;
+ static const int kStackHighEnd = kFrameAlign;
+ static const int kDirectCall = kStackHighEnd + kPointerSize;
+ static const int kIsolate = kDirectCall + kPointerSize;
+#endif
+
+#ifdef _WIN64
+ // Microsoft calling convention has three callee-saved registers
+ // (that we are using). We push these after the frame pointer.
+ static const int kBackup_rsi = kFramePointer - kPointerSize;
+ static const int kBackup_rdi = kBackup_rsi - kPointerSize;
+ static const int kBackup_rbx = kBackup_rdi - kPointerSize;
+ static const int kLastCalleeSaveRegister = kBackup_rbx;
+#else
+ // AMD64 Calling Convention has only one callee-save register that
+ // we use. We push this after the frame pointer (and after the
+ // parameters).
+ static const int kBackup_rbx = kNumOutputRegisters - kPointerSize;
+ static const int kLastCalleeSaveRegister = kBackup_rbx;
+#endif
+
+ static const int kSuccessfulCaptures = kLastCalleeSaveRegister - kPointerSize;
+ // When adding local variables remember to push space for them in
+ // the frame in GetCode.
+ static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
+
+ // First register address. Following registers are below it on the stack.
+ static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
+
+ // Initial size of code buffer.
+ static const size_t kRegExpCodeSize = 1024;
+
+ // Load a number of characters at the given offset from the
+ // current position, into the current-character register.
+ void LoadCurrentCharacterUnchecked(int cp_offset, int character_count);
+
+ // Check whether preemption has been requested.
+ void CheckPreemption();
+
+ // Check whether we are exceeding the stack limit on the backtrack stack.
+ void CheckStackLimit();
+
+ // Generate a call to CheckStackGuardState.
+ void CallCheckStackGuardState();
+
+ // The rbp-relative location of a regexp register.
+ Operand register_location(int register_index);
+
+ // The register containing the current character after LoadCurrentCharacter.
+ inline Register current_character() { return rdx; }
+
+ // The register containing the backtrack stack top. Provides a meaningful
+ // name to the register.
+ inline Register backtrack_stackpointer() { return rcx; }
+
+ // The registers containing a self pointer to this code's Code object.
+ inline Register code_object_pointer() { return r8; }
+
+ // Byte size of chars in the string to match (decided by the Mode argument)
+ inline int char_size() { return static_cast<int>(mode_); }
+
+ // Equivalent to a conditional branch to the label, unless the label
+ // is NULL, in which case it is a conditional Backtrack.
+ void BranchOrBacktrack(Condition condition, Label* to);
+
+ void MarkPositionForCodeRelativeFixup() {
+ code_relative_fixup_positions_.Add(masm_.pc_offset(), zone());
+ }
+
+ void FixupCodeRelativePositions();
+
+ // Call and return internally in the generated code in a way that
+ // is GC-safe (i.e., doesn't leave absolute code addresses on the stack)
+ inline void SafeCall(Label* to);
+ inline void SafeCallTarget(Label* label);
+ inline void SafeReturn();
+
+ // Pushes the value of a register on the backtrack stack. Decrements the
+ // stack pointer (rcx) by a word size and stores the register's value there.
+ inline void Push(Register source);
+
+ // Pushes a value on the backtrack stack. Decrements the stack pointer (rcx)
+ // by a word size and stores the value there.
+ inline void Push(Immediate value);
+
+ // Pushes the Code object relative offset of a label on the backtrack stack
+ // (i.e., a backtrack target). Decrements the stack pointer (rcx)
+ // by a word size and stores the value there.
+ inline void Push(Label* label);
+
+ // Pops a value from the backtrack stack. Reads the word at the stack pointer
+ // (rcx) and increments it by a word size.
+ inline void Pop(Register target);
+
+ // Drops the top value from the backtrack stack without reading it.
+ // Increments the stack pointer (rcx) by a word size.
+ inline void Drop();
+
+ Isolate* isolate() const { return masm_.isolate(); }
+
+ MacroAssembler masm_;
+ MacroAssembler::NoRootArrayScope no_root_array_scope_;
+
+ ZoneList<int> code_relative_fixup_positions_;
+
+ // Which mode to generate code for (ASCII or UC16).
+ Mode mode_;
+
+ // One greater than maximal register index actually used.
+ int num_registers_;
+
+ // Number of registers to output at the end (the saved registers
+ // are always 0..num_saved_registers_-1)
+ int num_saved_registers_;
+
+ // Labels used internally.
+ Label entry_label_;
+ Label start_label_;
+ Label success_label_;
+ Label backtrack_label_;
+ Label exit_label_;
+ Label check_preempt_label_;
+ Label stack_overflow_label_;
+};
+
+#endif // V8_INTERPRETED_REGEXP
+
+}} // namespace v8::internal
+
+#endif // V8_X64_REGEXP_MACRO_ASSEMBLER_X64_H_
diff --git a/chromium/v8/src/x64/simulator-x64.cc b/chromium/v8/src/x64/simulator-x64.cc
new file mode 100644
index 00000000000..209aa2d3070
--- /dev/null
+++ b/chromium/v8/src/x64/simulator-x64.cc
@@ -0,0 +1,27 @@
+// 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.
+
diff --git a/chromium/v8/src/x64/simulator-x64.h b/chromium/v8/src/x64/simulator-x64.h
new file mode 100644
index 00000000000..8aba70181f4
--- /dev/null
+++ b/chromium/v8/src/x64/simulator-x64.h
@@ -0,0 +1,72 @@
+// Copyright 2012 the V8 project authors. 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_X64_SIMULATOR_X64_H_
+#define V8_X64_SIMULATOR_X64_H_
+
+#include "allocation.h"
+
+namespace v8 {
+namespace internal {
+
+// Since there is no simulator for the x64 architecture the only thing we can
+// do is to call the entry directly.
+// TODO(X64): Don't pass p0, since it isn't used?
+#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
+ (entry(p0, p1, p2, p3, p4))
+
+typedef int (*regexp_matcher)(String*, int, const byte*,
+ const byte*, int*, int, Address, int, Isolate*);
+
+// Call the generated regexp code directly. The code at the entry address should
+// expect eight int/pointer sized arguments and return an int.
+#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \
+ (FUNCTION_CAST<regexp_matcher>(entry)(p0, p1, p2, p3, p4, p5, p6, p7, p8))
+
+#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
+ (reinterpret_cast<TryCatch*>(try_catch_address))
+
+// The stack limit beyond which we will throw stack overflow errors in
+// generated code. Because generated code on x64 uses the C stack, we
+// just use the C stack limit.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+ static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+ uintptr_t c_limit) {
+ return c_limit;
+ }
+
+ static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
+ return try_catch_address;
+ }
+
+ static inline void UnregisterCTryCatch() { }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_X64_SIMULATOR_X64_H_
diff --git a/chromium/v8/src/x64/stub-cache-x64.cc b/chromium/v8/src/x64/stub-cache-x64.cc
new file mode 100644
index 00000000000..34a557bd1af
--- /dev/null
+++ b/chromium/v8/src/x64/stub-cache-x64.cc
@@ -0,0 +1,3522 @@
+// Copyright 2012 the V8 project authors. 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"
+
+#if V8_TARGET_ARCH_X64
+
+#include "ic-inl.h"
+#include "codegen.h"
+#include "stub-cache.h"
+
+namespace v8 {
+namespace internal {
+
+#define __ ACCESS_MASM(masm)
+
+
+static void ProbeTable(Isolate* isolate,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ StubCache::Table table,
+ Register receiver,
+ Register name,
+ // The offset is scaled by 4, based on
+ // kHeapObjectTagSize, which is two bits
+ Register offset) {
+ // We need to scale up the pointer by 2 because the offset is scaled by less
+ // than the pointer size.
+ ASSERT(kPointerSizeLog2 == kHeapObjectTagSize + 1);
+ ScaleFactor scale_factor = times_2;
+
+ ASSERT_EQ(3 * kPointerSize, sizeof(StubCache::Entry));
+ // The offset register holds the entry offset times four (due to masking
+ // and shifting optimizations).
+ ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
+ ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
+ Label miss;
+
+ // Multiply by 3 because there are 3 fields per entry (name, code, map).
+ __ lea(offset, Operand(offset, offset, times_2, 0));
+
+ __ LoadAddress(kScratchRegister, key_offset);
+
+ // Check that the key in the entry matches the name.
+ // Multiply entry offset by 16 to get the entry address. Since the
+ // offset register already holds the entry offset times four, multiply
+ // by a further four.
+ __ cmpl(name, Operand(kScratchRegister, offset, scale_factor, 0));
+ __ j(not_equal, &miss);
+
+ // Get the map entry from the cache.
+ // Use key_offset + kPointerSize * 2, rather than loading map_offset.
+ __ movq(kScratchRegister,
+ Operand(kScratchRegister, offset, scale_factor, kPointerSize * 2));
+ __ cmpq(kScratchRegister, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ j(not_equal, &miss);
+
+ // Get the code entry from the cache.
+ __ LoadAddress(kScratchRegister, value_offset);
+ __ movq(kScratchRegister,
+ Operand(kScratchRegister, offset, scale_factor, 0));
+
+ // Check that the flags match what we're looking for.
+ __ movl(offset, FieldOperand(kScratchRegister, Code::kFlagsOffset));
+ __ and_(offset, Immediate(~Code::kFlagsNotUsedInLookup));
+ __ cmpl(offset, Immediate(flags));
+ __ j(not_equal, &miss);
+
+#ifdef DEBUG
+ if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
+ __ jmp(&miss);
+ } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
+ __ jmp(&miss);
+ }
+#endif
+
+ // Jump to the first instruction in the code stub.
+ __ addq(kScratchRegister, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ __ jmp(kScratchRegister);
+
+ __ bind(&miss);
+}
+
+
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// 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 unique and receiver must be a heap object.
+static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ Handle<Name> name,
+ Register r0,
+ Register r1) {
+ ASSERT(name->IsUniqueName());
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->negative_lookups(), 1);
+ __ IncrementCounter(counters->negative_lookups_miss(), 1);
+
+ __ movq(r0, FieldOperand(receiver, HeapObject::kMapOffset));
+
+ const int kInterceptorOrAccessCheckNeededMask =
+ (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
+
+ // Bail out if the receiver has a named interceptor or requires access checks.
+ __ testb(FieldOperand(r0, Map::kBitFieldOffset),
+ Immediate(kInterceptorOrAccessCheckNeededMask));
+ __ j(not_zero, miss_label);
+
+ // Check that receiver is a JSObject.
+ __ CmpInstanceType(r0, FIRST_SPEC_OBJECT_TYPE);
+ __ j(below, miss_label);
+
+ // Load properties array.
+ Register properties = r0;
+ __ movq(properties, FieldOperand(receiver, JSObject::kPropertiesOffset));
+
+ // Check that the properties array is a dictionary.
+ __ CompareRoot(FieldOperand(properties, HeapObject::kMapOffset),
+ Heap::kHashTableMapRootIndex);
+ __ j(not_equal, miss_label);
+
+ Label done;
+ NameDictionaryLookupStub::GenerateNegativeLookup(masm,
+ miss_label,
+ &done,
+ properties,
+ name,
+ r1);
+ __ bind(&done);
+ __ DecrementCounter(counters->negative_lookups_miss(), 1);
+}
+
+
+void StubCache::GenerateProbe(MacroAssembler* masm,
+ Code::Flags flags,
+ Register receiver,
+ Register name,
+ Register scratch,
+ Register extra,
+ Register extra2,
+ Register extra3) {
+ Isolate* isolate = masm->isolate();
+ Label miss;
+ USE(extra); // The register extra is not used on the X64 platform.
+ USE(extra2); // The register extra2 is not used on the X64 platform.
+ USE(extra3); // The register extra2 is not used on the X64 platform.
+ // Make sure that code is valid. The multiplying code relies on the
+ // entry size being 3 * kPointerSize.
+ ASSERT(sizeof(Entry) == 3 * kPointerSize);
+
+ // Make sure the flags do not name a specific type.
+ ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
+
+ // Make sure that there are no register conflicts.
+ ASSERT(!scratch.is(receiver));
+ ASSERT(!scratch.is(name));
+
+ // Check scratch register is valid, extra and extra2 are unused.
+ ASSERT(!scratch.is(no_reg));
+ ASSERT(extra2.is(no_reg));
+ ASSERT(extra3.is(no_reg));
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Get the map of the receiver and compute the hash.
+ __ movl(scratch, FieldOperand(name, Name::kHashFieldOffset));
+ // Use only the low 32 bits of the map pointer.
+ __ addl(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ xor_(scratch, Immediate(flags));
+ // We mask out the last two bits because they are not part of the hash and
+ // they are always 01 for maps. Also in the two 'and' instructions below.
+ __ and_(scratch, Immediate((kPrimaryTableSize - 1) << kHeapObjectTagSize));
+
+ // Probe the primary table.
+ ProbeTable(isolate, masm, flags, kPrimary, receiver, name, scratch);
+
+ // Primary miss: Compute hash for secondary probe.
+ __ movl(scratch, FieldOperand(name, Name::kHashFieldOffset));
+ __ addl(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ xor_(scratch, Immediate(flags));
+ __ and_(scratch, Immediate((kPrimaryTableSize - 1) << kHeapObjectTagSize));
+ __ subl(scratch, name);
+ __ addl(scratch, Immediate(flags));
+ __ and_(scratch, Immediate((kSecondaryTableSize - 1) << kHeapObjectTagSize));
+
+ // Probe the secondary table.
+ ProbeTable(isolate, masm, flags, kSecondary, receiver, name, scratch);
+
+ // Cache miss: Fall-through and let caller handle the miss by
+ // entering the runtime system.
+ __ bind(&miss);
+ __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
+}
+
+
+void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
+ int index,
+ Register prototype) {
+ // Load the global or builtins object from the current context.
+ __ movq(prototype,
+ Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
+ // Load the native context from the global or builtins object.
+ __ movq(prototype,
+ FieldOperand(prototype, GlobalObject::kNativeContextOffset));
+ // Load the function from the native context.
+ __ movq(prototype, Operand(prototype, Context::SlotOffset(index)));
+ // Load the initial map. The global functions all have initial maps.
+ __ movq(prototype,
+ FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset));
+ // Load the prototype from the initial map.
+ __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
+}
+
+
+void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
+ MacroAssembler* masm,
+ int index,
+ Register prototype,
+ Label* miss) {
+ Isolate* isolate = masm->isolate();
+ // Check we're still in the same context.
+ __ Move(prototype, isolate->global_object());
+ __ cmpq(Operand(rsi, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)),
+ prototype);
+ __ j(not_equal, miss);
+ // Get the global function with the given index.
+ Handle<JSFunction> function(
+ JSFunction::cast(isolate->native_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.
+ __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset));
+}
+
+
+void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch,
+ Label* miss_label) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss_label);
+
+ // Check that the object is a JS array.
+ __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
+ __ j(not_equal, miss_label);
+
+ // Load length directly from the JS array.
+ __ movq(rax, FieldOperand(receiver, JSArray::kLengthOffset));
+ __ ret(0);
+}
+
+
+// Generate code to check if an object is a string. If the object is
+// a string, the map's instance type is left in the scratch register.
+static void GenerateStringCheck(MacroAssembler* masm,
+ Register receiver,
+ Register scratch,
+ Label* smi,
+ Label* non_string_object) {
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, smi);
+
+ // Check that the object is a string.
+ __ movq(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ movzxbq(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ testl(scratch, Immediate(kNotStringTag));
+ __ j(not_zero, non_string_object);
+}
+
+
+void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss,
+ bool support_wrappers) {
+ Label check_wrapper;
+
+ // Check if the object is a string leaving the instance type in the
+ // scratch register.
+ GenerateStringCheck(masm, receiver, scratch1, miss,
+ support_wrappers ? &check_wrapper : miss);
+
+ // Load length directly from the string.
+ __ movq(rax, FieldOperand(receiver, String::kLengthOffset));
+ __ ret(0);
+
+ if (support_wrappers) {
+ // Check if the object is a JSValue wrapper.
+ __ bind(&check_wrapper);
+ __ cmpl(scratch1, Immediate(JS_VALUE_TYPE));
+ __ j(not_equal, miss);
+
+ // Check if the wrapped value is a string and load the length
+ // directly if it is.
+ __ movq(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
+ GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
+ __ movq(rax, FieldOperand(scratch2, String::kLengthOffset));
+ __ ret(0);
+ }
+}
+
+
+void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
+ Register receiver,
+ Register result,
+ Register scratch,
+ Label* miss_label) {
+ __ TryGetFunctionPrototype(receiver, result, miss_label);
+ if (!result.is(rax)) __ movq(rax, result);
+ __ ret(0);
+}
+
+
+void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
+ Register dst,
+ Register src,
+ bool inobject,
+ int index,
+ Representation representation) {
+ ASSERT(!FLAG_track_double_fields || !representation.IsDouble());
+ int offset = index * kPointerSize;
+ if (!inobject) {
+ // Calculate the offset into the properties array.
+ offset = offset + FixedArray::kHeaderSize;
+ __ movq(dst, FieldOperand(src, JSObject::kPropertiesOffset));
+ src = dst;
+ }
+ __ movq(dst, FieldOperand(src, offset));
+}
+
+
+static void PushInterceptorArguments(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ Handle<JSObject> holder_obj) {
+ __ push(name);
+ Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor());
+ ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor));
+ __ Move(kScratchRegister, interceptor);
+ __ push(kScratchRegister);
+ __ push(receiver);
+ __ push(holder);
+ __ push(FieldOperand(kScratchRegister, InterceptorInfo::kDataOffset));
+ __ PushAddress(ExternalReference::isolate_address(masm->isolate()));
+}
+
+
+static void CompileCallLoadPropertyWithInterceptor(
+ MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ Handle<JSObject> holder_obj) {
+ PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly),
+ masm->isolate());
+ __ Set(rax, 6);
+ __ LoadAddress(rbx, ref);
+
+ CEntryStub stub(1);
+ __ CallStub(&stub);
+}
+
+
+// Number of pointers to be reserved on stack for fast API call.
+static const int kFastApiCallArguments = FunctionCallbackArguments::kArgsLength;
+
+
+// Reserves space for the extra arguments to API function in the
+// caller's frame.
+//
+// These arguments are set by CheckPrototypes and GenerateFastApiCall.
+static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
+ // ----------- S t a t e -------------
+ // -- rsp[0] : return address
+ // -- rsp[8] : last argument in the internal frame of the caller
+ // -----------------------------------
+ __ movq(scratch, StackOperandForReturnAddress(0));
+ __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize));
+ __ movq(StackOperandForReturnAddress(0), scratch);
+ __ Move(scratch, Smi::FromInt(0));
+ for (int i = 1; i <= kFastApiCallArguments; i++) {
+ __ movq(Operand(rsp, i * kPointerSize), scratch);
+ }
+}
+
+
+// Undoes the effects of ReserveSpaceForFastApiCall.
+static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
+ // ----------- S t a t e -------------
+ // -- rsp[0] : return address.
+ // -- rsp[8] : last fast api call extra argument.
+ // -- ...
+ // -- rsp[kFastApiCallArguments * 8] : first fast api call extra
+ // argument.
+ // -- rsp[kFastApiCallArguments * 8 + 8] : last argument in the internal
+ // frame.
+ // -----------------------------------
+ __ movq(scratch, StackOperandForReturnAddress(0));
+ __ movq(StackOperandForReturnAddress(kFastApiCallArguments * kPointerSize),
+ scratch);
+ __ addq(rsp, Immediate(kPointerSize * kFastApiCallArguments));
+}
+
+
+// Generates call to API function.
+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
+ // (last fast api call extra argument,
+ // set by CheckPrototypes)
+ // -- rsp[16] : api function
+ // (first fast api call extra argument)
+ // -- rsp[24] : api call data
+ // -- rsp[32] : isolate
+ // -- rsp[40] : ReturnValue default value
+ // -- rsp[48] : ReturnValue
+ //
+ // -- rsp[56] : last argument
+ // -- ...
+ // -- rsp[(argc + 6) * 8] : first argument
+ // -- rsp[(argc + 7) * 8] : receiver
+ // -----------------------------------
+ // Get the function and setup the context.
+ 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);
+ Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
+ Handle<Object> call_data(api_call_info->data(), masm->isolate());
+ 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), call_data);
+ }
+ __ movq(kScratchRegister,
+ ExternalReference::isolate_address(masm->isolate()));
+ __ movq(Operand(rsp, 4 * kPointerSize), kScratchRegister);
+ __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
+ __ movq(Operand(rsp, 5 * kPointerSize), kScratchRegister);
+ __ movq(Operand(rsp, 6 * kPointerSize), kScratchRegister);
+
+ // Prepare arguments.
+ STATIC_ASSERT(kFastApiCallArguments == 6);
+ __ lea(rbx, Operand(rsp, kFastApiCallArguments * kPointerSize));
+
+ // Function address is a foreign pointer outside V8's heap.
+ Address function_address = v8::ToCData<Address>(api_call_info->callback());
+ bool returns_handle =
+ !CallbackTable::ReturnsVoid(masm->isolate(), function_address);
+
+#if defined(__MINGW64__)
+ Register arguments_arg = rcx;
+ Register callback_arg = rdx;
+#elif defined(_WIN64)
+ // Win64 uses first register--rcx--for returned value.
+ Register arguments_arg = returns_handle ? rdx : rcx;
+ Register callback_arg = returns_handle ? r8 : rdx;
+#else
+ Register arguments_arg = rdi;
+ Register callback_arg = rsi;
+#endif
+
+ // Allocate the v8::Arguments structure in the arguments' space since
+ // it's not controlled by GC.
+ const int kApiStackSpace = 4;
+
+ __ PrepareCallApiFunction(kApiStackSpace, returns_handle);
+
+ __ movq(StackSpaceOperand(0), rbx); // v8::Arguments::implicit_args_.
+ __ addq(rbx, Immediate(argc * kPointerSize));
+ __ movq(StackSpaceOperand(1), rbx); // v8::Arguments::values_.
+ __ Set(StackSpaceOperand(2), argc); // v8::Arguments::length_.
+ // v8::Arguments::is_construct_call_.
+ __ Set(StackSpaceOperand(3), 0);
+
+ // v8::InvocationCallback's argument.
+ __ lea(arguments_arg, StackSpaceOperand(0));
+
+ Address thunk_address = returns_handle
+ ? FUNCTION_ADDR(&InvokeInvocationCallback)
+ : FUNCTION_ADDR(&InvokeFunctionCallback);
+
+ __ CallApiFunctionAndReturn(function_address,
+ thunk_address,
+ callback_arg,
+ argc + kFastApiCallArguments + 1,
+ returns_handle,
+ kFastApiCallArguments + 1);
+}
+
+
+class CallInterceptorCompiler BASE_EMBEDDED {
+ public:
+ CallInterceptorCompiler(StubCompiler* stub_compiler,
+ const ParameterCount& arguments,
+ Register name,
+ Code::ExtraICState extra_ic_state)
+ : stub_compiler_(stub_compiler),
+ arguments_(arguments),
+ name_(name),
+ extra_ic_state_(extra_ic_state) {}
+
+ void Compile(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> 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()) {
+ CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3,
+ holder, lookup, name, optimization, miss);
+ } else {
+ CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3,
+ name, holder, miss);
+ }
+ }
+
+ private:
+ void CompileCacheable(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<JSObject> interceptor_holder,
+ LookupResult* lookup,
+ Handle<Name> name,
+ const CallOptimization& optimization,
+ Label* miss_label) {
+ ASSERT(optimization.is_constant_call());
+ ASSERT(!lookup->holder()->IsGlobalObject());
+
+ 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, Handle<JSObject>(lookup->holder()));
+ }
+ can_do_fast_api_call =
+ depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth;
+ }
+
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->call_const_interceptor(), 1);
+
+ if (can_do_fast_api_call) {
+ __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1);
+ ReserveSpaceForFastApiCall(masm, scratch1);
+ }
+
+ // Check that the maps from receiver to interceptor's holder
+ // haven't changed and thus we can invoke interceptor.
+ 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);
+
+ // Invoke an interceptor and if it provides a value,
+ // branch to |regular_invoke|.
+ Label regular_invoke;
+ LoadWithInterceptor(masm, receiver, holder, interceptor_holder,
+ &regular_invoke);
+
+ // Interceptor returned nothing for this property. Try to use cached
+ // constant function.
+
+ // 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()) {
+ stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
+ 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
+ // safe to omit it here, as if present, it should be fetched
+ // by the previous CheckPrototypes.
+ ASSERT(depth2 == kInvalidProtoDepth);
+ }
+
+ // Invoke function.
+ if (can_do_fast_api_call) {
+ GenerateFastApiCall(masm, optimization, arguments_.immediate());
+ } else {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ Handle<JSFunction> fun = optimization.constant_function();
+ ParameterCount expected(fun);
+ __ InvokeFunction(fun, expected, arguments_,
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+ }
+
+ // Deferred code for fast API call case---clean preallocated space.
+ if (can_do_fast_api_call) {
+ __ bind(&miss_cleanup);
+ FreeSpaceForFastApiCall(masm, scratch1);
+ __ jmp(miss_label);
+ }
+
+ // Invoke a regular function.
+ __ bind(&regular_invoke);
+ if (can_do_fast_api_call) {
+ FreeSpaceForFastApiCall(masm, scratch1);
+ }
+ }
+
+ void CompileRegular(MacroAssembler* masm,
+ Handle<JSObject> object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Handle<Name> name,
+ Handle<JSObject> interceptor_holder,
+ Label* miss_label) {
+ Register holder =
+ stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3,
+ name, miss_label);
+
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ // Save the name_ register across the call.
+ __ push(name_);
+
+ PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder);
+
+ __ CallExternalReference(
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
+ masm->isolate()),
+ 6);
+
+ // Restore the name_ register.
+ __ pop(name_);
+
+ // Leave the internal frame.
+ }
+
+ void LoadWithInterceptor(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Handle<JSObject> holder_obj,
+ Label* interceptor_succeeded) {
+ {
+ 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);
+ }
+
+ StubCompiler* stub_compiler_;
+ const ParameterCount& arguments_;
+ Register name_;
+ Code::ExtraICState extra_ic_state_;
+};
+
+
+void BaseStoreStubCompiler::GenerateRestoreName(MacroAssembler* masm,
+ Label* label,
+ Handle<Name> name) {
+ if (!label->is_unused()) {
+ __ bind(label);
+ __ Move(this->name(), name);
+ }
+}
+
+
+// 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.
+static void GenerateCheckPropertyCell(MacroAssembler* masm,
+ Handle<GlobalObject> global,
+ Handle<Name> name,
+ Register scratch,
+ Label* miss) {
+ Handle<PropertyCell> cell =
+ GlobalObject::EnsurePropertyCell(global, name);
+ ASSERT(cell->value()->IsTheHole());
+ __ Move(scratch, cell);
+ __ Cmp(FieldOperand(scratch, Cell::kValueOffset),
+ masm->isolate()->factory()->the_hole_value());
+ __ j(not_equal, miss);
+}
+
+
+void BaseStoreStubCompiler::GenerateNegativeHolderLookup(
+ MacroAssembler* masm,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Handle<Name> name,
+ Label* miss) {
+ if (holder->IsJSGlobalObject()) {
+ GenerateCheckPropertyCell(
+ masm, Handle<GlobalObject>::cast(holder), name, scratch1(), miss);
+ } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) {
+ GenerateDictionaryNegativeLookup(
+ masm, miss, holder_reg, name, scratch1(), scratch2());
+ }
+}
+
+
+// Receiver_reg is preserved on jumps to miss_label, but may be destroyed if
+// store is successful.
+void BaseStoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Handle<Map> transition,
+ Handle<Name> name,
+ Register receiver_reg,
+ Register storage_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Register unused,
+ Label* miss_label,
+ Label* slow) {
+ int descriptor = transition->LastAdded();
+ DescriptorArray* descriptors = transition->instance_descriptors();
+ PropertyDetails details = descriptors->GetDetails(descriptor);
+ Representation representation = details.representation();
+ ASSERT(!representation.IsNone());
+
+ if (details.type() == CONSTANT) {
+ Handle<Object> constant(descriptors->GetValue(descriptor), masm->isolate());
+ __ CmpObject(value_reg, constant);
+ __ j(not_equal, miss_label);
+ } else if (FLAG_track_fields && representation.IsSmi()) {
+ __ JumpIfNotSmi(value_reg, miss_label);
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ __ JumpIfSmi(value_reg, miss_label);
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ Label do_store, heap_number;
+ __ AllocateHeapNumber(storage_reg, scratch1, slow);
+
+ __ JumpIfNotSmi(value_reg, &heap_number);
+ __ SmiToInteger32(scratch1, value_reg);
+ __ cvtlsi2sd(xmm0, scratch1);
+ __ jmp(&do_store);
+
+ __ bind(&heap_number);
+ __ CheckMap(value_reg, masm->isolate()->factory()->heap_number_map(),
+ miss_label, DONT_DO_SMI_CHECK);
+ __ movsd(xmm0, FieldOperand(value_reg, HeapNumber::kValueOffset));
+
+ __ bind(&do_store);
+ __ movsd(FieldOperand(storage_reg, HeapNumber::kValueOffset), xmm0);
+ }
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ // Perform map transition for the receiver if necessary.
+ if (details.type() == FIELD &&
+ 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.
+ __ PopReturnAddressTo(scratch1);
+ __ push(receiver_reg);
+ __ Push(transition);
+ __ push(value_reg);
+ __ PushReturnAddressFrom(scratch1);
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
+ masm->isolate()),
+ 3,
+ 1);
+ return;
+ }
+
+ // Update the map of the object.
+ __ Move(scratch1, transition);
+ __ movq(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch1);
+
+ // Update the write barrier for the map field.
+ __ RecordWriteField(receiver_reg,
+ HeapObject::kMapOffset,
+ scratch1,
+ scratch2,
+ kDontSaveFPRegs,
+ OMIT_REMEMBERED_SET,
+ OMIT_SMI_CHECK);
+
+ if (details.type() == CONSTANT) {
+ ASSERT(value_reg.is(rax));
+ __ ret(0);
+ return;
+ }
+
+ int index = transition->instance_descriptors()->GetFieldIndex(
+ transition->LastAdded());
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ // TODO(verwaest): Share this code as a code stub.
+ SmiCheck smi_check = representation.IsTagged()
+ ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ __ movq(FieldOperand(receiver_reg, offset), storage_reg);
+ } else {
+ __ movq(FieldOperand(receiver_reg, offset), value_reg);
+ }
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ if (!FLAG_track_double_fields || !representation.IsDouble()) {
+ __ movq(storage_reg, value_reg);
+ }
+ __ RecordWriteField(
+ receiver_reg, offset, storage_reg, scratch1, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, smi_check);
+ }
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array (optimistically).
+ __ movq(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ if (FLAG_track_double_fields && representation.IsDouble()) {
+ __ movq(FieldOperand(scratch1, offset), storage_reg);
+ } else {
+ __ movq(FieldOperand(scratch1, offset), value_reg);
+ }
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ if (!FLAG_track_double_fields || !representation.IsDouble()) {
+ __ movq(storage_reg, value_reg);
+ }
+ __ RecordWriteField(
+ scratch1, offset, storage_reg, receiver_reg, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, smi_check);
+ }
+ }
+
+ // Return the value (register rax).
+ ASSERT(value_reg.is(rax));
+ __ ret(0);
+}
+
+
+// Both name_reg and receiver_reg are preserved on jumps to miss_label,
+// but may be destroyed if store is successful.
+void BaseStoreStubCompiler::GenerateStoreField(MacroAssembler* masm,
+ Handle<JSObject> object,
+ LookupResult* lookup,
+ Register receiver_reg,
+ Register name_reg,
+ Register value_reg,
+ Register scratch1,
+ Register scratch2,
+ Label* miss_label) {
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ int index = lookup->GetFieldIndex().field_index();
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ Representation representation = lookup->representation();
+ ASSERT(!representation.IsNone());
+ if (FLAG_track_fields && representation.IsSmi()) {
+ __ JumpIfNotSmi(value_reg, miss_label);
+ } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
+ __ JumpIfSmi(value_reg, miss_label);
+ } else if (FLAG_track_double_fields && representation.IsDouble()) {
+ // Load the double storage.
+ if (index < 0) {
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ movq(scratch1, FieldOperand(receiver_reg, offset));
+ } else {
+ __ movq(scratch1,
+ FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ __ movq(scratch1, FieldOperand(scratch1, offset));
+ }
+
+ // Store the value into the storage.
+ Label do_store, heap_number;
+ __ JumpIfNotSmi(value_reg, &heap_number);
+ __ SmiToInteger32(scratch2, value_reg);
+ __ cvtlsi2sd(xmm0, scratch2);
+ __ jmp(&do_store);
+
+ __ bind(&heap_number);
+ __ CheckMap(value_reg, masm->isolate()->factory()->heap_number_map(),
+ miss_label, DONT_DO_SMI_CHECK);
+ __ movsd(xmm0, FieldOperand(value_reg, HeapNumber::kValueOffset));
+ __ bind(&do_store);
+ __ movsd(FieldOperand(scratch1, HeapNumber::kValueOffset), xmm0);
+ // Return the value (register rax).
+ ASSERT(value_reg.is(rax));
+ __ ret(0);
+ return;
+ }
+
+ // TODO(verwaest): Share this code as a code stub.
+ SmiCheck smi_check = representation.IsTagged()
+ ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ movq(FieldOperand(receiver_reg, offset), value_reg);
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ // Pass the value being stored in the now unused name_reg.
+ __ movq(name_reg, value_reg);
+ __ RecordWriteField(
+ receiver_reg, offset, name_reg, scratch1, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, smi_check);
+ }
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array (optimistically).
+ __ movq(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset));
+ __ movq(FieldOperand(scratch1, offset), value_reg);
+
+ if (!FLAG_track_fields || !representation.IsSmi()) {
+ // Update the write barrier for the array address.
+ // Pass the value being stored in the now unused name_reg.
+ __ movq(name_reg, value_reg);
+ __ RecordWriteField(
+ scratch1, offset, name_reg, receiver_reg, kDontSaveFPRegs,
+ EMIT_REMEMBERED_SET, smi_check);
+ }
+ }
+
+ // Return the value (register rax).
+ ASSERT(value_reg.is(rax));
+ __ ret(0);
+}
+
+
+// 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<Name> 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()));
+ }
+}
+
+
+void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) {
+ __ jmp(code, RelocInfo::CODE_TARGET);
+}
+
+
+#undef __
+#define __ ACCESS_MASM((masm()))
+
+
+Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Register holder_reg,
+ Register scratch1,
+ Register scratch2,
+ Handle<Name> name,
+ int save_at_depth,
+ Label* miss,
+ PrototypeCheckType check) {
+ // Make sure that the type feedback oracle harvests the receiver map.
+ // TODO(svenpanne) Remove this hack when all ICs are reworked.
+ __ Move(scratch1, Handle<Map>(object->map()));
+
+ Handle<JSObject> first = object;
+ // Make sure there's no overlap between holder and object registers.
+ ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
+ ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
+ && !scratch2.is(scratch1));
+
+ // Keep track of the current object in register reg. On the first
+ // iteration, reg is an alias for object_reg, on later iterations,
+ // it is an alias for holder_reg.
+ Register reg = object_reg;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ __ movq(Operand(rsp, kPointerSize), object_reg);
+ }
+
+ // Check the maps in the prototype chain.
+ // Traverse the prototype chain from the object and do map checks.
+ 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());
+
+ Handle<JSObject> prototype(JSObject::cast(current->GetPrototype()));
+ if (!current->HasFastProperties() &&
+ !current->IsJSGlobalObject() &&
+ !current->IsJSGlobalProxy()) {
+ if (!name->IsUniqueName()) {
+ ASSERT(name->IsString());
+ name = factory()->InternalizeString(Handle<String>::cast(name));
+ }
+ ASSERT(current->property_dictionary()->FindEntry(*name) ==
+ NameDictionary::kNotFound);
+
+ GenerateDictionaryNegativeLookup(masm(), miss, reg, name,
+ scratch1, scratch2);
+
+ __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
+ reg = holder_reg; // From now on the object will be in holder_reg.
+ __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
+ } 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));
+ }
+ if (!current.is_identical_to(first) || check == CHECK_ALL_MAPS) {
+ __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK);
+ }
+
+ // 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, 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);
+ }
+ }
+
+ if (save_at_depth == depth) {
+ __ movq(Operand(rsp, kPointerSize), reg);
+ }
+
+ // Go to the next object in the prototype chain.
+ current = prototype;
+ }
+ ASSERT(current.is_identical_to(holder));
+
+ // Log the check depth.
+ LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
+
+ if (!holder.is_identical_to(first) || check == CHECK_ALL_MAPS) {
+ // Check the holder map.
+ __ CheckMap(reg, Handle<Map>(holder->map()), miss, DONT_DO_SMI_CHECK);
+ }
+
+ // 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.
+ GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss);
+
+ // Return the register containing the holder.
+ return reg;
+}
+
+
+void BaseLoadStubCompiler::HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) {
+ if (!miss->is_unused()) {
+ __ jmp(success);
+ __ bind(miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+ }
+}
+
+
+void BaseStoreStubCompiler::HandlerFrontendFooter(Handle<Name> name,
+ Label* success,
+ Label* miss) {
+ if (!miss->is_unused()) {
+ __ jmp(success);
+ GenerateRestoreName(masm(), miss, name);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+ }
+}
+
+
+Register BaseLoadStubCompiler::CallbackHandlerFrontend(
+ Handle<JSObject> object,
+ Register object_reg,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* success,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label miss;
+
+ Register reg = HandlerFrontendHeader(object, object_reg, holder, name, &miss);
+
+ if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
+ ASSERT(!reg.is(scratch2()));
+ ASSERT(!reg.is(scratch3()));
+ ASSERT(!reg.is(scratch4()));
+
+ // Load the properties dictionary.
+ Register dictionary = scratch4();
+ __ movq(dictionary, FieldOperand(reg, JSObject::kPropertiesOffset));
+
+ // Probe the dictionary.
+ Label probe_done;
+ NameDictionaryLookupStub::GeneratePositiveLookup(masm(),
+ &miss,
+ &probe_done,
+ dictionary,
+ this->name(),
+ scratch2(),
+ scratch3());
+ __ bind(&probe_done);
+
+ // If probing finds an entry in the dictionary, scratch3 contains the
+ // index into the dictionary. Check that the value is the callback.
+ Register index = scratch3();
+ const int kElementsStartOffset =
+ NameDictionary::kHeaderSize +
+ NameDictionary::kElementsStartIndex * kPointerSize;
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ movq(scratch2(),
+ Operand(dictionary, index, times_pointer_size,
+ kValueOffset - kHeapObjectTag));
+ __ movq(scratch3(), callback, RelocInfo::EMBEDDED_OBJECT);
+ __ cmpq(scratch2(), scratch3());
+ __ j(not_equal, &miss);
+ }
+
+ HandlerFrontendFooter(name, success, &miss);
+ return reg;
+}
+
+
+void BaseLoadStubCompiler::NonexistentHandlerFrontend(
+ Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Label* success,
+ Handle<GlobalObject> global) {
+ Label miss;
+
+ HandlerFrontendHeader(object, receiver(), last, name, &miss);
+
+ // If the last object in the prototype chain is a global object,
+ // check that the global property cell is empty.
+ if (!global.is_null()) {
+ GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss);
+ }
+
+ HandlerFrontendFooter(name, success, &miss);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadField(Register reg,
+ Handle<JSObject> holder,
+ PropertyIndex field,
+ Representation representation) {
+ if (!reg.is(receiver())) __ movq(receiver(), reg);
+ if (kind() == Code::LOAD_IC) {
+ LoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode(isolate()));
+ } else {
+ KeyedLoadFieldStub stub(field.is_inobject(holder),
+ field.translate(holder),
+ representation);
+ GenerateTailCall(masm(), stub.GetCode(isolate()));
+ }
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadCallback(
+ Register reg,
+ Handle<ExecutableAccessorInfo> callback) {
+ // Insert additional parameters into the stack frame above return address.
+ ASSERT(!scratch4().is(reg));
+ __ PopReturnAddressTo(scratch4());
+
+ __ push(receiver()); // receiver
+ __ push(reg); // holder
+ if (heap()->InNewSpace(callback->data())) {
+ __ Move(scratch1(), callback);
+ __ push(FieldOperand(scratch1(),
+ ExecutableAccessorInfo::kDataOffset)); // data
+ } else {
+ __ Push(Handle<Object>(callback->data(), isolate()));
+ }
+ __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
+ __ push(kScratchRegister); // return value
+ __ push(kScratchRegister); // return value default
+ __ PushAddress(ExternalReference::isolate_address(isolate()));
+ __ push(name()); // name
+ // Save a pointer to where we pushed the arguments pointer. This will be
+ // passed as the const ExecutableAccessorInfo& to the C++ callback.
+
+ Address getter_address = v8::ToCData<Address>(callback->getter());
+ bool returns_handle =
+ !CallbackTable::ReturnsVoid(isolate(), getter_address);
+
+#if defined(__MINGW64__)
+ Register getter_arg = r8;
+ Register accessor_info_arg = rdx;
+ Register name_arg = rcx;
+#elif defined(_WIN64)
+ // Win64 uses first register--rcx--for returned value.
+ Register getter_arg = returns_handle ? r9 : r8;
+ Register accessor_info_arg = returns_handle ? r8 : rdx;
+ Register name_arg = returns_handle ? rdx : rcx;
+#else
+ Register getter_arg = rdx;
+ Register accessor_info_arg = rsi;
+ Register name_arg = rdi;
+#endif
+
+ ASSERT(!name_arg.is(scratch4()));
+ __ movq(name_arg, rsp);
+ __ PushReturnAddressFrom(scratch4());
+
+ // v8::Arguments::values_ and handler for name.
+ const int kStackSpace = PropertyCallbackArguments::kArgsLength + 1;
+
+ // Allocate v8::AccessorInfo in non-GCed stack space.
+ const int kArgStackSpace = 1;
+
+ __ PrepareCallApiFunction(kArgStackSpace, returns_handle);
+ STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 6);
+ __ lea(rax, Operand(name_arg, 6 * kPointerSize));
+
+ // v8::AccessorInfo::args_.
+ __ movq(StackSpaceOperand(0), rax);
+
+ // The context register (rsi) has been saved in PrepareCallApiFunction and
+ // could be used to pass arguments.
+ __ lea(accessor_info_arg, StackSpaceOperand(0));
+
+ Address thunk_address = returns_handle
+ ? FUNCTION_ADDR(&InvokeAccessorGetter)
+ : FUNCTION_ADDR(&InvokeAccessorGetterCallback);
+
+ __ CallApiFunctionAndReturn(getter_address,
+ thunk_address,
+ getter_arg,
+ kStackSpace,
+ returns_handle,
+ 5);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadConstant(Handle<Object> value) {
+ // Return the constant value.
+ __ LoadObject(rax, value);
+ __ ret(0);
+}
+
+
+void BaseLoadStubCompiler::GenerateLoadInterceptor(
+ Register holder_reg,
+ Handle<JSObject> object,
+ Handle<JSObject> interceptor_holder,
+ LookupResult* lookup,
+ Handle<Name> name) {
+ ASSERT(interceptor_holder->HasNamedInterceptor());
+ ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // So far the most popular follow ups for interceptor loads are FIELD
+ // and CALLBACKS, so inline only them, other cases may be added
+ // later.
+ bool compile_followup_inline = false;
+ if (lookup->IsFound() && lookup->IsCacheable()) {
+ if (lookup->IsField()) {
+ compile_followup_inline = true;
+ } else if (lookup->type() == CALLBACKS &&
+ lookup->GetCallbackObject()->IsExecutableAccessorInfo()) {
+ ExecutableAccessorInfo* callback =
+ ExecutableAccessorInfo::cast(lookup->GetCallbackObject());
+ compile_followup_inline = callback->getter() != NULL &&
+ callback->IsCompatibleReceiver(*object);
+ }
+ }
+
+ if (compile_followup_inline) {
+ // Compile the interceptor call, followed by inline code to load the
+ // property from further up the prototype chain if the call fails.
+ // Check that the maps haven't changed.
+ ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
+
+ // Preserve the receiver register explicitly whenever it is different from
+ // the holder and it is needed should the interceptor return without any
+ // result. The CALLBACKS case needs the receiver to be passed into C++ code,
+ // the FIELD case might cause a miss during the prototype check.
+ bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
+ bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
+ (lookup->type() == CALLBACKS || must_perfrom_prototype_check);
+
+ // Save necessary data before invoking an interceptor.
+ // Requires a frame to make GC aware of pushed pointers.
+ {
+ FrameScope frame_scope(masm(), StackFrame::INTERNAL);
+
+ if (must_preserve_receiver_reg) {
+ __ push(receiver());
+ }
+ __ push(holder_reg);
+ __ push(this->name());
+
+ // 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,
+ this->name(),
+ 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);
+
+ __ bind(&interceptor_failed);
+ __ pop(this->name());
+ __ pop(holder_reg);
+ if (must_preserve_receiver_reg) {
+ __ pop(receiver());
+ }
+
+ // Leave the internal frame.
+ }
+
+ GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup);
+ } else { // !compile_followup_inline
+ // Call the runtime system to load the interceptor.
+ // Check that the maps haven't changed.
+ __ PopReturnAddressTo(scratch2());
+ PushInterceptorArguments(masm(), receiver(), holder_reg,
+ this->name(), interceptor_holder);
+ __ PushReturnAddressFrom(scratch2());
+
+ ExternalReference ref = ExternalReference(
+ IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), isolate());
+ __ TailCallExternalReference(ref, 6, 1);
+ }
+}
+
+
+void CallStubCompiler::GenerateNameCheck(Handle<Name> name, Label* miss) {
+ if (kind_ == Code::KEYED_CALL_IC) {
+ __ Cmp(rcx, name);
+ __ j(not_equal, miss);
+ }
+}
+
+
+void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Label* miss) {
+ ASSERT(holder->IsGlobalObject());
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ // Get the receiver from the stack.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+
+ // Check that the maps haven't changed.
+ __ JumpIfSmi(rdx, miss);
+ CheckPrototypes(object, rdx, holder, rbx, rax, rdi, name, miss);
+}
+
+
+void CallStubCompiler::GenerateLoadFunctionFromCell(
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Label* miss) {
+ // Get the value from the cell.
+ __ Move(rdi, cell);
+ __ movq(rdi, FieldOperand(rdi, Cell::kValueOffset));
+
+ // Check that the cell contains the same 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
+ // function can all use this call IC. Before we load through the
+ // function, we have to verify that it still is a function.
+ __ JumpIfSmi(rdi, miss);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
+ __ j(not_equal, miss);
+
+ // Check the shared function info. Make sure it hasn't changed.
+ __ Move(rax, Handle<SharedFunctionInfo>(function->shared()));
+ __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax);
+ } else {
+ __ Cmp(rdi, function);
+ }
+ __ j(not_equal, miss);
+}
+
+
+void CallStubCompiler::GenerateMissBranch() {
+ Handle<Code> code =
+ isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
+ kind_,
+ extra_state_);
+ __ Jump(code, RelocInfo::CODE_TARGET);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ PropertyIndex index,
+ Handle<Name> name) {
+ // ----------- 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
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &miss);
+
+ // Do the right check and compute the holder register.
+ Register reg = CheckPrototypes(object, rdx, holder, rbx, rax, rdi,
+ name, &miss);
+
+ GenerateFastPropertyLoad(masm(), rdi, reg, index.is_inobject(holder),
+ index.translate(holder), Representation::Tagged());
+
+ // Check that the function really is a function.
+ __ JumpIfSmi(rdi, &miss);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rbx);
+ __ j(not_equal, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ }
+
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::FIELD, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayCodeCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ Label miss;
+
+ // Check that function is still array
+ const int argc = arguments().immediate();
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ // Get the receiver from the stack.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ Handle<AllocationSite> site = isolate()->factory()->NewAllocationSite();
+ site->set_transition_info(Smi::FromInt(GetInitialFastElementsKind()));
+ Handle<Cell> site_feedback_cell = isolate()->factory()->NewCell(site);
+ __ movq(rax, Immediate(argc));
+ __ Move(rbx, site_feedback_cell);
+ __ Move(rdi, function);
+
+ ArrayConstructorStub stub(isolate());
+ __ TailCallStub(&stub);
+
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayPushCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- ...
+ // -- rsp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null();
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &miss);
+
+ CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi,
+ name, &miss);
+
+ if (argc == 0) {
+ // Noop, return the length.
+ __ movq(rax, FieldOperand(rdx, JSArray::kLengthOffset));
+ __ ret((argc + 1) * kPointerSize);
+ } else {
+ Label call_builtin;
+
+ if (argc == 1) { // Otherwise fall through to call builtin.
+ Label attempt_to_grow_elements, with_write_barrier, check_double;
+
+ // Get the elements array of the object.
+ __ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset));
+
+ // Check that the elements are in fast mode and writable.
+ __ Cmp(FieldOperand(rdi, HeapObject::kMapOffset),
+ factory()->fixed_array_map());
+ __ j(not_equal, &check_double);
+
+ // 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 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);
+
+ // Store the value.
+ __ movq(FieldOperand(rdi,
+ rax,
+ times_pointer_size,
+ FixedArray::kHeaderSize - argc * kPointerSize),
+ rcx);
+
+ __ Integer32ToSmi(rax, rax); // Return new length as smi.
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&check_double);
+
+ // Check that the elements are in double mode.
+ __ Cmp(FieldOperand(rdi, HeapObject::kMapOffset),
+ factory()->fixed_double_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 elements' length into rcx.
+ __ SmiToInteger32(rcx, FieldOperand(rdi, FixedArray::kLengthOffset));
+
+ // Check if we could survive without allocation.
+ __ cmpl(rax, rcx);
+ __ j(greater, &call_builtin);
+
+ __ movq(rcx, Operand(rsp, argc * kPointerSize));
+ __ StoreNumberToDoubleElements(
+ rcx, rdi, rax, xmm0, &call_builtin, argc * kDoubleSize);
+
+ // Save new length.
+ __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rax);
+ __ Integer32ToSmi(rax, rax); // Return new length as smi.
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&with_write_barrier);
+
+ __ 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);
+ __ CheckFastSmiElements(rbx, &call_builtin);
+ __ Cmp(FieldOperand(rcx, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ __ j(equal, &call_builtin);
+ // rdx: receiver
+ // rbx: map
+
+ Label try_holey_map;
+ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
+ FAST_ELEMENTS,
+ rbx,
+ rdi,
+ &try_holey_map);
+
+ ElementsTransitionGenerator::
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
+ // Restore edi.
+ __ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset));
+ __ jmp(&fast_object);
+
+ __ bind(&try_holey_map);
+ __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS,
+ FAST_HOLEY_ELEMENTS,
+ rbx,
+ rdi,
+ &call_builtin);
+ ElementsTransitionGenerator::
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
+ __ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset));
+ __ bind(&fast_object);
+ } else {
+ __ 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);
+
+ __ 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);
+ if (!FLAG_inline_new) {
+ __ 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 =
+ ExternalReference::new_space_allocation_limit_address(isolate());
+
+ const int kAllocationDelta = 4;
+ // Load top.
+ __ Load(rcx, new_space_allocation_top);
+
+ // Check if it's the end of elements.
+ __ lea(rdx, FieldOperand(rdi,
+ rax, times_pointer_size,
+ FixedArray::kHeaderSize - argc * kPointerSize));
+ __ cmpq(rdx, rcx);
+ __ j(not_equal, &call_builtin);
+ __ addq(rcx, Immediate(kAllocationDelta * kPointerSize));
+ Operand limit_operand =
+ masm()->ExternalOperand(new_space_allocation_limit);
+ __ cmpq(rcx, limit_operand);
+ __ j(above, &call_builtin);
+
+ // We fit and could grow elements.
+ __ Store(new_space_allocation_top, rcx);
+
+ // Push the argument...
+ __ 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(rdi, FixedArray::kLengthOffset),
+ Smi::FromInt(kAllocationDelta));
+
+ // Make new length a smi before returning it.
+ __ Integer32ToSmi(rax, rax);
+ __ movq(FieldOperand(rdx, JSArray::kLengthOffset), rax);
+
+ __ ret((argc + 1) * kPointerSize);
+ }
+
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush,
+ isolate()),
+ argc + 1,
+ 1);
+ }
+
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileArrayPopCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- ...
+ // -- rsp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ 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.
+ const int argc = arguments().immediate();
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &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));
+
+ // Check that the elements are in fast mode and writable.
+ __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
+ __ j(not_equal, &call_builtin);
+
+ // Get the array's length into rcx and calculate new length.
+ __ SmiToInteger32(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
+ __ subl(rcx, Immediate(1));
+ __ j(negative, &return_undefined);
+
+ // Get the last element.
+ __ LoadRoot(r9, Heap::kTheHoleValueRootIndex);
+ __ movq(rax, FieldOperand(rbx,
+ rcx, times_pointer_size,
+ FixedArray::kHeaderSize));
+ // Check if element is already the hole.
+ __ cmpq(rax, r9);
+ // If so, call slow-case to also check prototypes for value.
+ __ j(equal, &call_builtin);
+
+ // Set the array's length.
+ __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rcx);
+
+ // Fill with the hole and return original value.
+ __ movq(FieldOperand(rbx,
+ rcx, times_pointer_size,
+ FixedArray::kHeaderSize),
+ r9);
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&return_undefined);
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(
+ ExternalReference(Builtins::c_ArrayPop, isolate()),
+ argc + 1,
+ 1);
+
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- rcx : function name
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- ...
+ // -- rsp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ 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_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.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ rax,
+ &miss);
+ ASSERT(!object.is_identical_to(holder));
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ rax, holder, rbx, rdx, rdi, name, &miss);
+
+ Register receiver = rbx;
+ Register index = rdi;
+ Register result = rax;
+ __ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize));
+ if (argc > 0) {
+ __ movq(index, Operand(rsp, (argc - 0) * kPointerSize));
+ } else {
+ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
+ }
+
+ 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;
+ generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ LoadRoot(rax, Heap::kNanValueRootIndex);
+ __ ret((argc + 1) * kPointerSize);
+ }
+
+ __ bind(&miss);
+ // Restore function name in rcx.
+ __ Move(rcx, name);
+ __ bind(&name_miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringCharAtCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- rcx : function name
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- ...
+ // -- rsp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ 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_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.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ rax,
+ &miss);
+ ASSERT(!object.is_identical_to(holder));
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ rax, holder, rbx, rdx, rdi, name, &miss);
+
+ Register receiver = rax;
+ Register index = rdi;
+ Register scratch = rdx;
+ Register result = rax;
+ __ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize));
+ if (argc > 0) {
+ __ movq(index, Operand(rsp, (argc - 0) * kPointerSize));
+ } else {
+ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
+ }
+
+ 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;
+ generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ LoadRoot(rax, Heap::kempty_stringRootIndex);
+ __ ret((argc + 1) * kPointerSize);
+ }
+ __ bind(&miss);
+ // Restore function name in rcx.
+ __ Move(rcx, name);
+ __ bind(&name_miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- rcx : function name
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- ...
+ // -- rsp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+
+ // If the object is not a JSObject or we got an unexpected number of
+ // arguments, bail out to the regular call.
+ const int argc = arguments().immediate();
+ if (!object->IsJSObject() || argc != 1) return Handle<Code>::null();
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize));
+ __ JumpIfSmi(rdx, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi,
+ name, &miss);
+ } else {
+ ASSERT(cell->value() == *function);
+ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
+ &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the char code argument.
+ Register code = rbx;
+ __ movq(code, Operand(rsp, 1 * kPointerSize));
+
+ // Check the code is a smi.
+ Label slow;
+ __ JumpIfNotSmi(code, &slow);
+
+ // Convert the smi code to uint16.
+ __ SmiAndConstant(code, code, Smi::FromInt(0xffff));
+
+ StringCharFromCodeGenerator generator(code, rax);
+ generator.GenerateFast(masm());
+ __ ret(2 * kPointerSize);
+
+ StubRuntimeCallHelper 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_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+
+ __ bind(&miss);
+ // rcx: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileMathFloorCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // TODO(872): implement this.
+ return Handle<Code>::null();
+}
+
+
+Handle<Code> CallStubCompiler::CompileMathAbsCall(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> cell,
+ Handle<JSFunction> function,
+ Handle<String> name,
+ Code::StubType type) {
+ // ----------- S t a t e -------------
+ // -- rcx : function name
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- ...
+ // -- rsp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+
+ // If the object is not a JSObject or we got an unexpected number of
+ // arguments, bail out to the regular call.
+ const int argc = arguments().immediate();
+ if (!object->IsJSObject() || argc != 1) return Handle<Code>::null();
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell.is_null()) {
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize));
+ __ JumpIfSmi(rdx, &miss);
+ CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi,
+ name, &miss);
+ } else {
+ 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));
+
+ // Check if the argument is a smi.
+ Label not_smi;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(rax, &not_smi);
+
+ // Branchless abs implementation, refer to below:
+ // http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs
+ // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0
+ // otherwise.
+ __ movq(rbx, rax);
+ __ sar(rbx, Immediate(kBitsPerPointer - 1));
+
+ // Do bitwise not or do nothing depending on ebx.
+ __ xor_(rax, rbx);
+
+ // Add 1 or do nothing depending on ebx.
+ __ subq(rax, rbx);
+
+ // If the result is still negative, go to the slow case.
+ // This only happens for the most negative smi.
+ Label slow;
+ __ j(negative, &slow);
+
+ __ ret(2 * kPointerSize);
+
+ // Check if the argument is a heap number and load its value.
+ __ bind(&not_smi);
+ __ CheckMap(rax, factory()->heap_number_map(), &slow, DONT_DO_SMI_CHECK);
+ __ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset));
+
+ // Check the sign of the argument. If the argument is positive,
+ // just return it.
+ Label negative_sign;
+ const int sign_mask_shift =
+ (HeapNumber::kExponentOffset - HeapNumber::kValueOffset) * kBitsPerByte;
+ __ movq(rdi, static_cast<int64_t>(HeapNumber::kSignMask) << sign_mask_shift,
+ RelocInfo::NONE64);
+ __ testq(rbx, rdi);
+ __ j(not_zero, &negative_sign);
+ __ ret(2 * kPointerSize);
+
+ // If the argument is negative, clear the sign, and return a new
+ // number. We still have the sign mask in rdi.
+ __ bind(&negative_sign);
+ __ xor_(rbx, rdi);
+ __ AllocateHeapNumber(rax, rdx, &slow);
+ __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rbx);
+ __ ret(2 * kPointerSize);
+
+ // 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_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+
+ __ bind(&miss);
+ // rcx: function name.
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(type, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileFastApiCall(
+ const CallOptimization& optimization,
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Cell> 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 Handle<Code>::null();
+ if (!cell.is_null()) return Handle<Code>::null();
+ if (!object->IsJSObject()) return Handle<Code>::null();
+ int depth = optimization.GetPrototypeDepthOfExpectedType(
+ 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.
+ const int argc = arguments().immediate();
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rdx, &miss_before_stack_reserved);
+
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->call_const(), 1);
+ __ IncrementCounter(counters->call_const_fast_api(), 1);
+
+ // Allocate space for v8::Arguments implicit values. Must be initialized
+ // before calling any runtime function.
+ __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize));
+
+ // Check that the maps haven't changed and find a Holder as a side effect.
+ CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax, rdi,
+ name, depth, &miss);
+
+ // Move the return address on top of the stack.
+ __ movq(rax,
+ StackOperandForReturnAddress(kFastApiCallArguments * kPointerSize));
+ __ movq(StackOperandForReturnAddress(0), rax);
+
+ GenerateFastApiCall(masm(), optimization, argc);
+
+ __ bind(&miss);
+ __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize));
+
+ __ bind(&miss_before_stack_reserved);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+void CallStubCompiler::CompileHandlerFrontend(Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Label* success) {
+ // ----------- 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
+ // -----------------------------------
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ if (check != NUMBER_CHECK) {
+ __ JumpIfSmi(rdx, &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);
+
+ Counters* counters = isolate()->counters();
+ switch (check) {
+ case RECEIVER_MAP_CHECK:
+ __ IncrementCounter(counters->call_const(), 1);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(Handle<JSObject>::cast(object), rdx, holder, rbx, rax,
+ rdi, name, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ }
+ break;
+
+ case STRING_CHECK:
+ // Check that the object is a string.
+ __ 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(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ rax, holder, rbx, rdx, rdi, name, &miss);
+ break;
+
+ case SYMBOL_CHECK:
+ // Check that the object is a symbol.
+ __ CmpObjectType(rdx, SYMBOL_TYPE, rax);
+ __ j(not_equal, &miss);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::SYMBOL_FUNCTION_INDEX, rax, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ rax, holder, rbx, rdx, rdi, name, &miss);
+ break;
+
+ case NUMBER_CHECK: {
+ Label fast;
+ // Check that the object is a smi or a heap number.
+ __ JumpIfSmi(rdx, &fast);
+ __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax);
+ __ j(not_equal, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::NUMBER_FUNCTION_INDEX, rax, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ rax, holder, rbx, rdx, rdi, name, &miss);
+ break;
+ }
+ case BOOLEAN_CHECK: {
+ Label fast;
+ // Check that the object is a boolean.
+ __ CompareRoot(rdx, Heap::kTrueValueRootIndex);
+ __ j(equal, &fast);
+ __ CompareRoot(rdx, Heap::kFalseValueRootIndex);
+ __ j(not_equal, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::BOOLEAN_FUNCTION_INDEX, rax, &miss);
+ CheckPrototypes(
+ Handle<JSObject>(JSObject::cast(object->GetPrototype(isolate()))),
+ rax, holder, rbx, rdx, rdi, name, &miss);
+ break;
+ }
+ }
+
+ __ jmp(success);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+}
+
+
+void CallStubCompiler::CompileHandlerBackend(Handle<JSFunction> function) {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount expected(function);
+ __ InvokeFunction(function, expected, arguments(),
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallConstant(
+ Handle<Object> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ CheckType check,
+ Handle<JSFunction> function) {
+ if (HasCustomCallGenerator(function)) {
+ Handle<Code> code = CompileCustomCall(object, holder,
+ Handle<PropertyCell>::null(),
+ function, Handle<String>::cast(name),
+ Code::CONSTANT);
+ // A null handle means bail out to the regular compiler code below.
+ if (!code.is_null()) return code;
+ }
+
+ Label success;
+
+ CompileHandlerFrontend(object, holder, name, check, &success);
+ __ bind(&success);
+ CompileHandlerBackend(function);
+
+ // Return the generated code.
+ return GetCode(function);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name) {
+ // ----------- 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
+ // -----------------------------------
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ 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_state_);
+ compiler.Compile(masm(), object, holder, name, &lookup, rdx, rbx, rdi, rax,
+ &miss);
+
+ // Restore receiver.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ // Check that the function really is a function.
+ __ JumpIfSmi(rax, &miss);
+ __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx);
+ __ j(not_equal, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ }
+
+ // Invoke the function.
+ __ movq(rdi, rax);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
+
+ // Handle load cache miss.
+ __ bind(&miss);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::INTERCEPTOR, name);
+}
+
+
+Handle<Code> CallStubCompiler::CompileCallGlobal(
+ Handle<JSObject> object,
+ Handle<GlobalObject> holder,
+ Handle<PropertyCell> cell,
+ Handle<JSFunction> function,
+ Handle<Name> name) {
+ // ----------- 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
+ // -----------------------------------
+
+ if (HasCustomCallGenerator(function)) {
+ Handle<Code> code = CompileCustomCall(
+ object, holder, cell, function, Handle<String>::cast(name),
+ Code::NORMAL);
+ // 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 (object->IsGlobalObject()) {
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
+ }
+
+ // 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);
+ ParameterCount expected(function->shared()->formal_parameter_count());
+ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ // 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);
+ GenerateMissBranch();
+
+ // Return the generated code.
+ return GetCode(Code::NORMAL, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreCallback(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ Handle<ExecutableAccessorInfo> callback) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+
+ __ PopReturnAddressTo(scratch1());
+ __ push(receiver());
+ __ Push(callback); // callback info
+ __ Push(name);
+ __ push(value());
+ __ PushReturnAddressFrom(scratch1());
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_callback_property =
+ ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate());
+ __ TailCallExternalReference(store_callback_property, 4, 1);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void StoreStubCompiler::GenerateStoreViaSetter(
+ MacroAssembler* masm,
+ Handle<JSFunction> setter) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Save value register, so we can restore it later.
+ __ push(rax);
+
+ if (!setter.is_null()) {
+ // Call the JavaScript setter with receiver and value on the stack.
+ __ push(rdx);
+ __ push(rax);
+ ParameterCount actual(1);
+ ParameterCount expected(setter);
+ __ InvokeFunction(setter, expected, actual,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+ } else {
+ // If we generate a global code snippet for deoptimization only, remember
+ // the place to continue after deoptimization.
+ masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // We have to return the passed value, not the return value of the setter.
+ __ pop(rax);
+
+ // Restore context register.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ }
+ __ ret(0);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> StoreStubCompiler::CompileStoreInterceptor(
+ Handle<JSObject> object,
+ Handle<Name> name) {
+ __ PopReturnAddressTo(scratch1());
+ __ push(receiver());
+ __ push(this->name());
+ __ push(value());
+ __ Push(Smi::FromInt(strict_mode()));
+ __ PushReturnAddressFrom(scratch1());
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_ic_property =
+ ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate());
+ __ TailCallExternalReference(store_ic_property, 4, 1);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::INTERCEPTOR, name);
+}
+
+
+Handle<Code> StoreStubCompiler::CompileStoreGlobal(
+ Handle<GlobalObject> object,
+ Handle<PropertyCell> cell,
+ Handle<Name> name) {
+ Label miss;
+
+ // Check that the map of the global has not changed.
+ __ Cmp(FieldOperand(receiver(), HeapObject::kMapOffset),
+ Handle<Map>(object->map()));
+ __ j(not_equal, &miss);
+
+ // Compute the cell operand to use.
+ __ Move(scratch1(), cell);
+ Operand cell_operand =
+ FieldOperand(scratch1(), PropertyCell::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.
+ __ CompareRoot(cell_operand, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &miss);
+
+ // Store the value in the cell.
+ __ movq(cell_operand, value());
+ // Cells are always rescanned, so no write barrier here.
+
+ // Return the value (register rax).
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->named_store_global_inline(), 1);
+ __ ret(0);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ __ IncrementCounter(counters->named_store_global_inline_miss(), 1);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, name);
+}
+
+
+Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic(
+ MapHandleList* receiver_maps,
+ CodeHandleList* handler_stubs,
+ MapHandleList* transitioned_maps) {
+ Label miss;
+ __ JumpIfSmi(receiver(), &miss, Label::kNear);
+
+ __ movq(scratch1(), FieldOperand(receiver(), HeapObject::kMapOffset));
+ int receiver_count = receiver_maps->length();
+ for (int i = 0; i < receiver_count; ++i) {
+ // Check map and tail call if there's a match
+ __ Cmp(scratch1(), 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(transition_map(),
+ transitioned_maps->at(i),
+ RelocInfo::EMBEDDED_OBJECT);
+ __ jmp(handler_stubs->at(i), RelocInfo::CODE_TARGET);
+ __ bind(&next_map);
+ }
+ }
+
+ __ bind(&miss);
+
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ return GetICCode(
+ kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC);
+}
+
+
+Handle<Code> LoadStubCompiler::CompileLoadNonexistent(
+ Handle<JSObject> object,
+ Handle<JSObject> last,
+ Handle<Name> name,
+ Handle<GlobalObject> global) {
+ Label success;
+
+ NonexistentHandlerFrontend(object, last, name, &success, global);
+
+ __ bind(&success);
+ // Return undefined if maps of the full prototype chain are still the
+ // same and no global property with this name contains a value.
+ __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ ret(0);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::NONEXISTENT, name);
+}
+
+
+Register* LoadStubCompiler::registers() {
+ // receiver, name, scratch1, scratch2, scratch3, scratch4.
+ static Register registers[] = { rax, rcx, rdx, rbx, rdi, r8 };
+ return registers;
+}
+
+
+Register* KeyedLoadStubCompiler::registers() {
+ // receiver, name, scratch1, scratch2, scratch3, scratch4.
+ static Register registers[] = { rdx, rax, rbx, rcx, rdi, r8 };
+ return registers;
+}
+
+
+Register* StoreStubCompiler::registers() {
+ // receiver, name, value, scratch1, scratch2, scratch3.
+ static Register registers[] = { rdx, rcx, rax, rbx, rdi, r8 };
+ return registers;
+}
+
+
+Register* KeyedStoreStubCompiler::registers() {
+ // receiver, name, value, scratch1, scratch2, scratch3.
+ static Register registers[] = { rdx, rcx, rax, rbx, rdi, r8 };
+ return registers;
+}
+
+
+void KeyedLoadStubCompiler::GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) {
+ __ Cmp(name_reg, name);
+ __ j(not_equal, miss);
+}
+
+
+void KeyedStoreStubCompiler::GenerateNameCheck(Handle<Name> name,
+ Register name_reg,
+ Label* miss) {
+ __ Cmp(name_reg, name);
+ __ j(not_equal, miss);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm,
+ Handle<JSFunction> getter) {
+ // ----------- S t a t e -------------
+ // -- rax : receiver
+ // -- rcx : name
+ // -- rsp[0] : return address
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ if (!getter.is_null()) {
+ // Call the JavaScript getter with the receiver on the stack.
+ __ push(rax);
+ ParameterCount actual(0);
+ ParameterCount expected(getter);
+ __ InvokeFunction(getter, expected, actual,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
+ } else {
+ // If we generate a global code snippet for deoptimization only, remember
+ // the place to continue after deoptimization.
+ masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
+ }
+
+ // Restore context register.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ }
+ __ ret(0);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm())
+
+
+Handle<Code> LoadStubCompiler::CompileLoadGlobal(
+ Handle<JSObject> object,
+ Handle<GlobalObject> global,
+ Handle<PropertyCell> cell,
+ Handle<Name> name,
+ bool is_dont_delete) {
+ Label success, miss;
+ // TODO(verwaest): Directly store to rax. Currently we cannot do this, since
+ // rax is used as receiver(), which we would otherwise clobber before a
+ // potential miss.
+
+ __ CheckMap(receiver(), Handle<Map>(object->map()), &miss, DO_SMI_CHECK);
+ HandlerFrontendHeader(
+ object, receiver(), Handle<JSObject>::cast(global), name, &miss);
+
+ // Get the value from the cell.
+ __ Move(rbx, cell);
+ __ movq(rbx, FieldOperand(rbx, PropertyCell::kValueOffset));
+
+ // Check for deleted property if property can actually be deleted.
+ if (!is_dont_delete) {
+ __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &miss);
+ } else if (FLAG_debug_code) {
+ __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex);
+ __ Check(not_equal, kDontDeleteCellsCannotContainTheHole);
+ }
+
+ HandlerFrontendFooter(name, &success, &miss);
+ __ bind(&success);
+
+ Counters* counters = isolate()->counters();
+ __ IncrementCounter(counters->named_load_global_stub(), 1);
+ __ movq(rax, rbx);
+ __ ret(0);
+
+ // Return the generated code.
+ return GetICCode(kind(), Code::NORMAL, name);
+}
+
+
+Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC(
+ MapHandleList* receiver_maps,
+ CodeHandleList* handlers,
+ Handle<Name> name,
+ Code::StubType type,
+ IcCheckType check) {
+ Label miss;
+
+ if (check == PROPERTY) {
+ GenerateNameCheck(name, this->name(), &miss);
+ }
+
+ __ JumpIfSmi(receiver(), &miss);
+ Register map_reg = scratch1();
+ __ movq(map_reg, FieldOperand(receiver(), HeapObject::kMapOffset));
+ int receiver_count = receiver_maps->length();
+ int number_of_handled_maps = 0;
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map = receiver_maps->at(current);
+ if (!map->is_deprecated()) {
+ number_of_handled_maps++;
+ // Check map and tail call if there's a match
+ __ Cmp(map_reg, receiver_maps->at(current));
+ __ j(equal, handlers->at(current), RelocInfo::CODE_TARGET);
+ }
+ }
+ ASSERT(number_of_handled_maps > 0);
+
+ __ bind(&miss);
+ TailCallBuiltin(masm(), MissBuiltin(kind()));
+
+ // Return the generated code.
+ InlineCacheState state =
+ number_of_handled_maps > 1 ? POLYMORPHIC : MONOMORPHIC;
+ return GetICCode(kind(), type, name, state);
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void KeyedLoadStubCompiler::GenerateLoadDictionaryElement(
+ MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow, miss_force_generic;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ __ JumpIfNotSmi(rax, &miss_force_generic);
+ __ SmiToInteger32(rbx, rax);
+ __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
+
+ // Check whether the elements is a number dictionary.
+ // rdx: receiver
+ // rax: key
+ // rbx: key as untagged int32
+ // rcx: elements
+ __ LoadFromNumberDictionary(&slow, rcx, rax, rbx, r9, rdi, rax);
+ __ ret(0);
+
+ __ bind(&slow);
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow);
+
+ __ bind(&miss_force_generic);
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedLoadIC_MissForceGeneric);
+}
+
+
+static void GenerateSmiKeyCheck(MacroAssembler* masm,
+ Register key,
+ Register scratch,
+ XMMRegister xmm_scratch0,
+ XMMRegister xmm_scratch1,
+ Label* fail) {
+ // Check that key is a smi or a heap number containing a smi and branch
+ // if the check fails.
+ Label key_ok;
+ __ JumpIfSmi(key, &key_ok);
+ __ CheckMap(key,
+ masm->isolate()->factory()->heap_number_map(),
+ fail,
+ DONT_DO_SMI_CHECK);
+ __ movsd(xmm_scratch0, FieldOperand(key, HeapNumber::kValueOffset));
+ __ cvttsd2si(scratch, xmm_scratch0);
+ __ cvtlsi2sd(xmm_scratch1, scratch);
+ __ ucomisd(xmm_scratch1, xmm_scratch0);
+ __ j(not_equal, fail);
+ __ j(parity_even, fail); // NaN.
+ __ Integer32ToSmi(key, scratch);
+ __ bind(&key_ok);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreExternalArray(
+ MacroAssembler* masm,
+ ElementsKind elements_kind) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow, miss_force_generic;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, rcx, rbx, xmm0, xmm1, &miss_force_generic);
+
+ // Check that the index is in range.
+ __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ SmiToInteger32(rdi, rcx); // Untag the index.
+ __ cmpq(rcx, FieldOperand(rbx, ExternalArray::kLengthOffset));
+ // Unsigned comparison catches both negative and too-large values.
+ __ j(above_equal, &miss_force_generic);
+
+ // Handle both smis and HeapNumbers in the fast path. Go to the
+ // runtime for all other kinds of values.
+ // rax: value
+ // rcx: key (a smi)
+ // rdx: receiver (a JSObject)
+ // rbx: elements array
+ // rdi: untagged key
+ Label check_heap_number;
+ if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) {
+ // Float to pixel conversion is only implemented in the runtime for now.
+ __ JumpIfNotSmi(rax, &slow);
+ } else {
+ __ JumpIfNotSmi(rax, &check_heap_number, Label::kNear);
+ }
+ // No more branches to slow case on this path. Key and receiver not needed.
+ __ SmiToInteger32(rdx, rax);
+ __ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset));
+ // rbx: base pointer of external storage
+ switch (elements_kind) {
+ case EXTERNAL_PIXEL_ELEMENTS:
+ { // Clamp the value to [0..255].
+ Label done;
+ __ testl(rdx, Immediate(0xFFFFFF00));
+ __ j(zero, &done, Label::kNear);
+ __ setcc(negative, rdx); // 1 if negative, 0 if positive.
+ __ decb(rdx); // 0 if negative, 255 if positive.
+ __ bind(&done);
+ }
+ __ movb(Operand(rbx, rdi, times_1, 0), rdx);
+ break;
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ movb(Operand(rbx, rdi, times_1, 0), rdx);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ movw(Operand(rbx, rdi, times_2, 0), rdx);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ movl(Operand(rbx, rdi, times_4, 0), rdx);
+ break;
+ case EXTERNAL_FLOAT_ELEMENTS:
+ // Need to perform int-to-float conversion.
+ __ cvtlsi2ss(xmm0, rdx);
+ __ movss(Operand(rbx, rdi, times_4, 0), xmm0);
+ break;
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ // Need to perform int-to-float conversion.
+ __ cvtlsi2sd(xmm0, rdx);
+ __ movsd(Operand(rbx, rdi, times_8, 0), xmm0);
+ break;
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ __ ret(0);
+
+ // TODO(danno): handle heap number -> pixel array conversion
+ if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) {
+ __ bind(&check_heap_number);
+ // rax: value
+ // rcx: key (a smi)
+ // rdx: receiver (a JSObject)
+ // rbx: elements array
+ // rdi: untagged key
+ __ CmpObjectType(rax, HEAP_NUMBER_TYPE, kScratchRegister);
+ __ j(not_equal, &slow);
+ // No more branches to slow case on this path.
+
+ // The WebGL specification leaves the behavior of storing NaN and
+ // +/-Infinity into integer arrays basically undefined. For more
+ // reproducible behavior, convert these to zero.
+ __ movsd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset));
+ // rdi: untagged index
+ // rbx: base pointer of external storage
+ // top of FPU stack: value
+ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
+ __ cvtsd2ss(xmm0, xmm0);
+ __ movss(Operand(rbx, rdi, times_4, 0), xmm0);
+ __ ret(0);
+ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
+ __ movsd(Operand(rbx, rdi, times_8, 0), xmm0);
+ __ ret(0);
+ } else {
+ // Perform float-to-int conversion with truncation (round-to-zero)
+ // behavior.
+ // Fast path: use machine instruction to convert to int64. If that
+ // fails (out-of-range), go into the runtime.
+ __ cvttsd2siq(r8, xmm0);
+ __ Set(kScratchRegister, V8_UINT64_C(0x8000000000000000));
+ __ cmpq(r8, kScratchRegister);
+ __ j(equal, &slow);
+
+ // rdx: value (converted to an untagged integer)
+ // rdi: untagged index
+ // rbx: base pointer of external storage
+ switch (elements_kind) {
+ case EXTERNAL_BYTE_ELEMENTS:
+ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+ __ movb(Operand(rbx, rdi, times_1, 0), r8);
+ break;
+ case EXTERNAL_SHORT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+ __ movw(Operand(rbx, rdi, times_2, 0), r8);
+ break;
+ case EXTERNAL_INT_ELEMENTS:
+ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+ __ movl(Operand(rbx, rdi, times_4, 0), r8);
+ break;
+ case EXTERNAL_PIXEL_ELEMENTS:
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
+ case FAST_ELEMENTS:
+ case FAST_SMI_ELEMENTS:
+ case FAST_DOUBLE_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_DOUBLE_ELEMENTS:
+ case DICTIONARY_ELEMENTS:
+ case NON_STRICT_ARGUMENTS_ELEMENTS:
+ UNREACHABLE();
+ break;
+ }
+ __ ret(0);
+ }
+ }
+
+ // Slow case: call runtime.
+ __ bind(&slow);
+
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+
+ // Miss case: call runtime.
+ __ bind(&miss_force_generic);
+
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastElement(
+ MacroAssembler* masm,
+ bool is_js_array,
+ ElementsKind elements_kind,
+ KeyedAccessStoreMode store_mode) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic, transition_elements_kind, finish_store, grow;
+ Label check_capacity, slow;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, rcx, rbx, xmm0, xmm1, &miss_force_generic);
+
+ if (IsFastSmiElementsKind(elements_kind)) {
+ __ JumpIfNotSmi(rax, &transition_elements_kind);
+ }
+
+ // Get the elements array and make sure it is a fast element array, not 'cow'.
+ __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
+ // Check that the key is within bounds.
+ if (is_js_array) {
+ __ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
+ if (IsGrowStoreMode(store_mode)) {
+ __ j(above_equal, &grow);
+ } else {
+ __ j(above_equal, &miss_force_generic);
+ }
+ } else {
+ __ SmiCompare(rcx, FieldOperand(rdi, FixedArray::kLengthOffset));
+ __ j(above_equal, &miss_force_generic);
+ }
+
+ __ CompareRoot(FieldOperand(rdi, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
+ __ j(not_equal, &miss_force_generic);
+
+ __ bind(&finish_store);
+ if (IsFastSmiElementsKind(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(IsFastObjectElementsKind(elements_kind));
+ __ 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(rbx, rax);
+ __ RecordWrite(rdi, rcx, rbx, kDontSaveFPRegs);
+ }
+
+ // Done.
+ __ ret(0);
+
+ // Handle store cache miss.
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+
+ __ bind(&transition_elements_kind);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Miss);
+
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ // Grow the array by a single element if possible.
+ __ bind(&grow);
+
+ // Make sure the array is only growing by a single element, anything else
+ // must be handled by the runtime. Flags are already set by previous
+ // compare.
+ __ j(not_equal, &miss_force_generic);
+
+ // Check for the empty array, and preallocate a small backing store if
+ // possible.
+ __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ CompareRoot(rdi, Heap::kEmptyFixedArrayRootIndex);
+ __ j(not_equal, &check_capacity);
+
+ int size = FixedArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ Allocate(size, rdi, rbx, r8, &slow, TAG_OBJECT);
+
+ // rax: value
+ // rcx: key
+ // rdx: receiver
+ // rdi: elements
+ // Make sure that the backing store can hold additional elements.
+ __ Move(FieldOperand(rdi, JSObject::kMapOffset),
+ masm->isolate()->factory()->fixed_array_map());
+ __ Move(FieldOperand(rdi, FixedArray::kLengthOffset),
+ Smi::FromInt(JSArray::kPreallocatedArrayElements));
+ __ LoadRoot(rbx, Heap::kTheHoleValueRootIndex);
+ for (int i = 1; i < JSArray::kPreallocatedArrayElements; ++i) {
+ __ movq(FieldOperand(rdi, FixedArray::SizeFor(i)), rbx);
+ }
+
+ // Store the element at index zero.
+ __ movq(FieldOperand(rdi, FixedArray::SizeFor(0)), rax);
+
+ // Install the new backing store in the JSArray.
+ __ movq(FieldOperand(rdx, JSObject::kElementsOffset), rdi);
+ __ RecordWriteField(rdx, JSObject::kElementsOffset, rdi, rbx,
+ kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+
+ // Increment the length of the array.
+ __ Move(FieldOperand(rdx, JSArray::kLengthOffset), Smi::FromInt(1));
+ __ ret(0);
+
+ __ bind(&check_capacity);
+ // Check for cow elements, in general they are not handled by this stub.
+ __ CompareRoot(FieldOperand(rdi, HeapObject::kMapOffset),
+ Heap::kFixedCOWArrayMapRootIndex);
+ __ j(equal, &miss_force_generic);
+
+ // rax: value
+ // rcx: key
+ // rdx: receiver
+ // rdi: elements
+ // Make sure that the backing store can hold additional elements.
+ __ cmpq(rcx, FieldOperand(rdi, FixedArray::kLengthOffset));
+ __ j(above_equal, &slow);
+
+ // Grow the array and finish the store.
+ __ SmiAddConstant(FieldOperand(rdx, JSArray::kLengthOffset),
+ Smi::FromInt(1));
+ __ jmp(&finish_store);
+
+ __ bind(&slow);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+ }
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
+ MacroAssembler* masm,
+ bool is_js_array,
+ KeyedAccessStoreMode store_mode) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic, transition_elements_kind, finish_store;
+ Label grow, slow, check_capacity, restore_key_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.
+
+ // Check that the key is a smi or a heap number convertible to a smi.
+ GenerateSmiKeyCheck(masm, rcx, rbx, xmm0, xmm1, &miss_force_generic);
+
+ // Get the elements array.
+ __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ AssertFastElements(rdi);
+
+ // Check that the key is within bounds.
+ if (is_js_array) {
+ __ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
+ if (IsGrowStoreMode(store_mode)) {
+ __ j(above_equal, &grow);
+ } else {
+ __ j(above_equal, &miss_force_generic);
+ }
+ } else {
+ __ SmiCompare(rcx, FieldOperand(rdi, FixedDoubleArray::kLengthOffset));
+ __ j(above_equal, &miss_force_generic);
+ }
+
+ // Handle smi values specially
+ __ bind(&finish_store);
+ __ SmiToInteger32(rcx, rcx);
+ __ StoreNumberToDoubleElements(rax, rdi, rcx, xmm0,
+ &restore_key_transition_elements_kind);
+ __ ret(0);
+
+ // Handle store cache miss, replacing the ic with the generic stub.
+ __ bind(&miss_force_generic);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_MissForceGeneric);
+
+ __ bind(&restore_key_transition_elements_kind);
+ // Restore smi-tagging of rcx.
+ __ Integer32ToSmi(rcx, rcx);
+ __ bind(&transition_elements_kind);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Miss);
+
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
+ // Grow the array by a single element if possible.
+ __ bind(&grow);
+
+ // Make sure the array is only growing by a single element, anything else
+ // must be handled by the runtime. Flags are already set by previous
+ // compare.
+ __ j(not_equal, &miss_force_generic);
+
+ // Transition on values that can't be stored in a FixedDoubleArray.
+ Label value_is_smi;
+ __ JumpIfSmi(rax, &value_is_smi);
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+ __ j(not_equal, &transition_elements_kind);
+ __ bind(&value_is_smi);
+
+ // Check for the empty array, and preallocate a small backing store if
+ // possible.
+ __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ CompareRoot(rdi, Heap::kEmptyFixedArrayRootIndex);
+ __ j(not_equal, &check_capacity);
+
+ int size = FixedDoubleArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ Allocate(size, rdi, rbx, r8, &slow, TAG_OBJECT);
+
+ // rax: value
+ // rcx: key
+ // rdx: receiver
+ // rdi: elements
+ // Initialize the new FixedDoubleArray. Leave elements unitialized for
+ // efficiency, they are guaranteed to be initialized before use.
+ __ Move(FieldOperand(rdi, JSObject::kMapOffset),
+ masm->isolate()->factory()->fixed_double_array_map());
+ __ Move(FieldOperand(rdi, FixedDoubleArray::kLengthOffset),
+ Smi::FromInt(JSArray::kPreallocatedArrayElements));
+
+ // Increment the length of the array.
+ __ SmiToInteger32(rcx, rcx);
+ __ StoreNumberToDoubleElements(rax, rdi, rcx, xmm0,
+ &restore_key_transition_elements_kind);
+
+ __ movq(r8, BitCast<int64_t, uint64_t>(kHoleNanInt64), RelocInfo::NONE64);
+ for (int i = 1; i < JSArray::kPreallocatedArrayElements; i++) {
+ __ movq(FieldOperand(rdi, FixedDoubleArray::OffsetOfElementAt(i)), r8);
+ }
+
+ // Install the new backing store in the JSArray.
+ __ movq(FieldOperand(rdx, JSObject::kElementsOffset), rdi);
+ __ RecordWriteField(rdx, JSObject::kElementsOffset, rdi, rbx,
+ kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
+
+ // Increment the length of the array.
+ __ Move(FieldOperand(rdx, JSArray::kLengthOffset), Smi::FromInt(1));
+ __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ ret(0);
+
+ __ bind(&check_capacity);
+ // rax: value
+ // rcx: key
+ // rdx: receiver
+ // rdi: elements
+ // Make sure that the backing store can hold additional elements.
+ __ cmpq(rcx, FieldOperand(rdi, FixedDoubleArray::kLengthOffset));
+ __ j(above_equal, &slow);
+
+ // Grow the array and finish the store.
+ __ SmiAddConstant(FieldOperand(rdx, JSArray::kLengthOffset),
+ Smi::FromInt(1));
+ __ jmp(&finish_store);
+
+ __ bind(&slow);
+ TailCallBuiltin(masm, Builtins::kKeyedStoreIC_Slow);
+ }
+}
+
+
+#undef __
+
+} } // namespace v8::internal
+
+#endif // V8_TARGET_ARCH_X64
diff --git a/chromium/v8/src/zone-inl.h b/chromium/v8/src/zone-inl.h
new file mode 100644
index 00000000000..f257382a2db
--- /dev/null
+++ b/chromium/v8/src/zone-inl.h
@@ -0,0 +1,120 @@
+// Copyright 2012 the V8 project authors. 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_ZONE_INL_H_
+#define V8_ZONE_INL_H_
+
+#include "zone.h"
+
+#include "counters.h"
+#include "isolate.h"
+#include "utils.h"
+#include "v8-counters.h"
+
+namespace v8 {
+namespace internal {
+
+
+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_;
+
+ if (size > limit_ - position_) {
+ result = NewExpand(size);
+ } else {
+ position_ += size;
+ }
+
+ // Check that the result has the proper alignment and return it.
+ ASSERT(IsAddressAligned(result, kAlignment, 0));
+ allocation_size_ += size;
+ return reinterpret_cast<void*>(result);
+}
+
+
+template <typename T>
+T* Zone::NewArray(int length) {
+ return static_cast<T*>(New(length * sizeof(T)));
+}
+
+
+bool Zone::excess_allocation() {
+ return segment_bytes_allocated_ > kExcessLimit;
+}
+
+
+void Zone::adjust_segment_bytes_allocated(int delta) {
+ segment_bytes_allocated_ += delta;
+ isolate_->counters()->zone_segment_bytes()->Set(segment_bytes_allocated_);
+}
+
+
+template <typename Config>
+ZoneSplayTree<Config>::~ZoneSplayTree() {
+ // Reset the root to avoid unneeded iteration over all tree nodes
+ // in the destructor. For a zone-allocated tree, nodes will be
+ // freed by the Zone.
+ SplayTree<Config, ZoneAllocationPolicy>::ResetRoot();
+}
+
+
+void* ZoneObject::operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+}
+
+inline void* ZoneAllocationPolicy::New(size_t size) {
+ ASSERT(zone_);
+ return zone_->New(static_cast<int>(size));
+}
+
+
+template <typename T>
+void* ZoneList<T>::operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+}
+
+
+template <typename T>
+void* ZoneSplayTree<T>::operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_ZONE_INL_H_
diff --git a/chromium/v8/src/zone.cc b/chromium/v8/src/zone.cc
new file mode 100644
index 00000000000..9ee00edcba1
--- /dev/null
+++ b/chromium/v8/src/zone.cc
@@ -0,0 +1,226 @@
+// Copyright 2012 the V8 project authors. 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 <string.h>
+
+#include "v8.h"
+#include "zone-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+// Segments represent chunks of memory: They have starting address
+// (encoded in the this pointer) and a size in bytes. Segments are
+// chained together forming a LIFO structure with the newest segment
+// available as segment_head_. Segments are allocated using malloc()
+// and de-allocated using free().
+
+class Segment {
+ public:
+ void Initialize(Segment* next, int size) {
+ next_ = next;
+ size_ = size;
+ }
+
+ Segment* next() const { return next_; }
+ void clear_next() { next_ = NULL; }
+
+ int size() const { return size_; }
+ int capacity() const { return size_ - sizeof(Segment); }
+
+ Address start() const { return address(sizeof(Segment)); }
+ Address end() const { return address(size_); }
+
+ private:
+ // Computes the address of the nth byte in this segment.
+ Address address(int n) const {
+ return Address(this) + n;
+ }
+
+ Segment* next_;
+ int size_;
+};
+
+
+Zone::Zone(Isolate* isolate)
+ : allocation_size_(0),
+ segment_bytes_allocated_(0),
+ position_(0),
+ limit_(0),
+ segment_head_(NULL),
+ isolate_(isolate) {
+}
+
+
+Zone::~Zone() {
+ DeleteAll();
+ DeleteKeptSegment();
+
+ ASSERT(segment_bytes_allocated_ == 0);
+}
+
+
+void Zone::DeleteAll() {
+#ifdef DEBUG
+ // Constant byte value used for zapping dead memory in debug mode.
+ static const unsigned char kZapDeadByte = 0xcd;
+#endif
+
+ // Find a segment with a suitable size to keep around.
+ Segment* keep = NULL;
+ // Traverse the chained list of segments, zapping (in debug mode)
+ // and freeing every segment except the one we wish to keep.
+ for (Segment* current = segment_head_; current != NULL; ) {
+ Segment* next = current->next();
+ if (keep == NULL && current->size() <= kMaximumKeptSegmentSize) {
+ // Unlink the segment we wish to keep from the list.
+ keep = current;
+ keep->clear_next();
+ } else {
+ int size = current->size();
+#ifdef DEBUG
+ // Zap the entire current segment (including the header).
+ memset(current, kZapDeadByte, size);
+#endif
+ DeleteSegment(current, size);
+ }
+ current = next;
+ }
+
+ // If we have found a segment we want to keep, we must recompute the
+ // variables 'position' and 'limit' to prepare for future allocate
+ // attempts. Otherwise, we must clear the position and limit to
+ // force a new segment to be allocated on demand.
+ if (keep != NULL) {
+ Address start = keep->start();
+ position_ = RoundUp(start, kAlignment);
+ limit_ = keep->end();
+#ifdef DEBUG
+ // Zap the contents of the kept segment (but not the header).
+ memset(start, kZapDeadByte, keep->capacity());
+#endif
+ } else {
+ position_ = limit_ = 0;
+ }
+
+ // Update the head segment to be the kept segment (if any).
+ segment_head_ = keep;
+}
+
+
+void Zone::DeleteKeptSegment() {
+#ifdef DEBUG
+ // Constant byte value used for zapping dead memory in debug mode.
+ static const unsigned char kZapDeadByte = 0xcd;
+#endif
+
+ ASSERT(segment_head_ == NULL || segment_head_->next() == NULL);
+ if (segment_head_ != NULL) {
+ int size = segment_head_->size();
+#ifdef DEBUG
+ // Zap the entire kept segment (including the header).
+ memset(segment_head_, kZapDeadByte, size);
+#endif
+ DeleteSegment(segment_head_, size);
+ segment_head_ = NULL;
+ }
+
+ ASSERT(segment_bytes_allocated_ == 0);
+}
+
+
+// Creates a new segment, sets it size, and pushes it to the front
+// of the segment chain. Returns the new segment.
+Segment* Zone::NewSegment(int size) {
+ Segment* result = reinterpret_cast<Segment*>(Malloced::New(size));
+ adjust_segment_bytes_allocated(size);
+ if (result != NULL) {
+ result->Initialize(segment_head_, size);
+ segment_head_ = result;
+ }
+ return result;
+}
+
+
+// Deletes the given segment. Does not touch the segment chain.
+void Zone::DeleteSegment(Segment* segment, int size) {
+ adjust_segment_bytes_allocated(-size);
+ Malloced::Delete(segment);
+}
+
+
+Address Zone::NewExpand(int size) {
+ // Make sure the requested size is already properly aligned and that
+ // there isn't enough room in the Zone to satisfy the request.
+ ASSERT(size == RoundDown(size, kAlignment));
+ ASSERT(size > limit_ - position_);
+
+ // Compute the new segment size. We use a 'high water mark'
+ // strategy, where we increase the segment size every time we expand
+ // except that we employ a maximum segment size when we delete. This
+ // is to avoid excessive malloc() and free() overhead.
+ Segment* head = segment_head_;
+ int old_size = (head == NULL) ? 0 : head->size();
+ static const int kSegmentOverhead = sizeof(Segment) + kAlignment;
+ int new_size_no_overhead = size + (old_size << 1);
+ int new_size = kSegmentOverhead + new_size_no_overhead;
+ // Guard against integer overflow.
+ if (new_size_no_overhead < size || new_size < kSegmentOverhead) {
+ V8::FatalProcessOutOfMemory("Zone");
+ return NULL;
+ }
+ if (new_size < kMinimumSegmentSize) {
+ new_size = kMinimumSegmentSize;
+ } else if (new_size > kMaximumSegmentSize) {
+ // Limit the size of new segments to avoid growing the segment size
+ // exponentially, thus putting pressure on contiguous virtual address space.
+ // All the while making sure to allocate a segment large enough to hold the
+ // requested size.
+ new_size = Max(kSegmentOverhead + size, kMaximumSegmentSize);
+ }
+ Segment* segment = NewSegment(new_size);
+ if (segment == NULL) {
+ V8::FatalProcessOutOfMemory("Zone");
+ return NULL;
+ }
+
+ // Recompute 'top' and 'limit' based on the new segment.
+ Address result = RoundUp(segment->start(), kAlignment);
+ position_ = result + size;
+ // Check for address overflow.
+ if (position_ < result) {
+ V8::FatalProcessOutOfMemory("Zone");
+ return NULL;
+ }
+ limit_ = segment->end();
+ ASSERT(position_ <= limit_);
+ return result;
+}
+
+
+} } // namespace v8::internal
diff --git a/chromium/v8/src/zone.h b/chromium/v8/src/zone.h
new file mode 100644
index 00000000000..bd7cc39b0c4
--- /dev/null
+++ b/chromium/v8/src/zone.h
@@ -0,0 +1,261 @@
+// Copyright 2012 the V8 project authors. 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_ZONE_H_
+#define V8_ZONE_H_
+
+#include "allocation.h"
+#include "checks.h"
+#include "hashmap.h"
+#include "globals.h"
+#include "list.h"
+#include "splay-tree.h"
+
+namespace v8 {
+namespace internal {
+
+
+class Segment;
+class Isolate;
+
+// The Zone supports very fast allocation of small chunks of
+// memory. The chunks cannot be deallocated individually, but instead
+// the Zone supports deallocating all chunks in one fast
+// operation. The Zone is used to hold temporary data structures like
+// the abstract syntax tree, which is deallocated after compilation.
+
+// Note: There is no need to initialize the Zone; the first time an
+// allocation is attempted, a segment of memory will be requested
+// through a call to malloc().
+
+// Note: The implementation is inherently not thread safe. Do not use
+// from multi-threaded code.
+
+class Zone {
+ public:
+ explicit Zone(Isolate* isolate);
+ ~Zone();
+ // Allocate 'size' bytes of memory in the Zone; expands the Zone by
+ // allocating new segments of memory on demand using malloc().
+ inline void* New(int size);
+
+ template <typename T>
+ inline T* NewArray(int length);
+
+ // Deletes all objects and free all memory allocated in the Zone. Keeps one
+ // small (size <= kMaximumKeptSegmentSize) segment around if it finds one.
+ void DeleteAll();
+
+ // Deletes the last small segment kept around by DeleteAll(). You
+ // may no longer allocate in the Zone after a call to this method.
+ void DeleteKeptSegment();
+
+ // Returns true if more memory has been allocated in zones than
+ // the limit allows.
+ inline bool excess_allocation();
+
+ inline void adjust_segment_bytes_allocated(int delta);
+
+ inline unsigned allocation_size() { return allocation_size_; }
+
+ inline Isolate* isolate() { return isolate_; }
+
+ private:
+ friend class Isolate;
+
+ // 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.
+ static const int kMinimumSegmentSize = 8 * KB;
+
+ // Never allocate segments larger than this size in bytes.
+ static const int kMaximumSegmentSize = 1 * MB;
+
+ // Never keep segments larger than this size in bytes around.
+ static const int kMaximumKeptSegmentSize = 64 * KB;
+
+ // Report zone excess when allocation exceeds this limit.
+ static const int kExcessLimit = 256 * MB;
+
+ // The number of bytes allocated in this zone so far.
+ unsigned allocation_size_;
+
+ // The number of bytes allocated in segments. Note that this number
+ // includes memory allocated from the OS but not yet allocated from
+ // the zone.
+ int segment_bytes_allocated_;
+
+ // Expand the Zone to hold at least 'size' more bytes and allocate
+ // the bytes. Returns the address of the newly allocated chunk of
+ // memory in the Zone. Should only be called if there isn't enough
+ // room in the Zone already.
+ Address NewExpand(int size);
+
+ // Creates a new segment, sets it size, and pushes it to the front
+ // of the segment chain. Returns the new segment.
+ INLINE(Segment* NewSegment(int size));
+
+ // Deletes the given segment. Does not touch the segment chain.
+ INLINE(void DeleteSegment(Segment* segment, int size));
+
+ // The free region in the current (front) segment is represented as
+ // the half-open interval [position, limit). The 'position' variable
+ // is guaranteed to be aligned as dictated by kAlignment.
+ Address position_;
+ Address limit_;
+
+ Segment* segment_head_;
+ Isolate* isolate_;
+};
+
+
+// ZoneObject is an abstraction that helps define classes of objects
+// allocated in the Zone. Use it as a base class; see ast.h.
+class ZoneObject {
+ public:
+ // Allocate a new ZoneObject of 'size' bytes in the Zone.
+ INLINE(void* operator new(size_t size, Zone* zone));
+
+ // Ideally, the delete operator should be private instead of
+ // public, but unfortunately the compiler sometimes synthesizes
+ // (unused) destructors for classes derived from ZoneObject, which
+ // require the operator to be visible. MSVC requires the delete
+ // operator to be public.
+
+ // ZoneObjects should never be deleted individually; use
+ // Zone::DeleteAll() to delete all zone objects in one go.
+ void operator delete(void*, size_t) { UNREACHABLE(); }
+ void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
+};
+
+
+// The ZoneScope is used to automatically call DeleteAll() on a
+// Zone when the ZoneScope is destroyed (i.e. goes out of scope)
+struct ZoneScope {
+ public:
+ explicit ZoneScope(Zone* zone) : zone_(zone) { }
+ ~ZoneScope() { zone_->DeleteAll(); }
+
+ Zone* zone() { return zone_; }
+
+ private:
+ Zone* zone_;
+};
+
+
+// The ZoneAllocationPolicy is used to specialize generic data
+// structures to allocate themselves and their elements in the Zone.
+struct ZoneAllocationPolicy {
+ public:
+ explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) { }
+ INLINE(void* New(size_t size));
+ INLINE(static void Delete(void *pointer)) { }
+ Zone* zone() { return zone_; }
+
+ private:
+ Zone* zone_;
+};
+
+
+// ZoneLists are growable lists with constant-time access to the
+// elements. The list itself and all its elements are allocated in the
+// Zone. ZoneLists cannot be deleted individually; you can delete all
+// objects in the Zone by calling Zone::DeleteAll().
+template<typename T>
+class ZoneList: public List<T, ZoneAllocationPolicy> {
+ public:
+ // Construct a new ZoneList with the given capacity; the length is
+ // always zero. The capacity must be non-negative.
+ ZoneList(int capacity, Zone* zone)
+ : List<T, ZoneAllocationPolicy>(capacity, ZoneAllocationPolicy(zone)) { }
+
+ INLINE(void* operator new(size_t size, Zone* zone));
+
+ // Construct a new ZoneList by copying the elements of the given ZoneList.
+ ZoneList(const ZoneList<T>& other, Zone* zone)
+ : List<T, ZoneAllocationPolicy>(other.length(),
+ ZoneAllocationPolicy(zone)) {
+ AddAll(other, zone);
+ }
+
+ // We add some convenience wrappers so that we can pass in a Zone
+ // instead of a (less convenient) ZoneAllocationPolicy.
+ INLINE(void Add(const T& element, Zone* zone)) {
+ List<T, ZoneAllocationPolicy>::Add(element, ZoneAllocationPolicy(zone));
+ }
+ INLINE(void AddAll(const List<T, ZoneAllocationPolicy>& other, Zone* zone)) {
+ List<T, ZoneAllocationPolicy>::AddAll(other, ZoneAllocationPolicy(zone));
+ }
+ INLINE(void AddAll(const Vector<T>& other, Zone* zone)) {
+ List<T, ZoneAllocationPolicy>::AddAll(other, ZoneAllocationPolicy(zone));
+ }
+ INLINE(void InsertAt(int index, const T& element, Zone* zone)) {
+ List<T, ZoneAllocationPolicy>::InsertAt(index, element,
+ ZoneAllocationPolicy(zone));
+ }
+ INLINE(Vector<T> AddBlock(T value, int count, Zone* zone)) {
+ return List<T, ZoneAllocationPolicy>::AddBlock(value, count,
+ ZoneAllocationPolicy(zone));
+ }
+ INLINE(void Allocate(int length, Zone* zone)) {
+ List<T, ZoneAllocationPolicy>::Allocate(length, ZoneAllocationPolicy(zone));
+ }
+ INLINE(void Initialize(int capacity, Zone* zone)) {
+ List<T, ZoneAllocationPolicy>::Initialize(capacity,
+ ZoneAllocationPolicy(zone));
+ }
+
+ void operator delete(void* pointer) { UNREACHABLE(); }
+ void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
+};
+
+
+// A zone splay tree. The config type parameter encapsulates the
+// different configurations of a concrete splay tree (see splay-tree.h).
+// The tree itself and all its elements are allocated in the Zone.
+template <typename Config>
+class ZoneSplayTree: public SplayTree<Config, ZoneAllocationPolicy> {
+ public:
+ explicit ZoneSplayTree(Zone* zone)
+ : SplayTree<Config, ZoneAllocationPolicy>(ZoneAllocationPolicy(zone)) {}
+ ~ZoneSplayTree();
+
+ INLINE(void* operator new(size_t size, Zone* zone));
+
+ void operator delete(void* pointer) { UNREACHABLE(); }
+ void operator delete(void* pointer, Zone* zone) { UNREACHABLE(); }
+};
+
+
+typedef TemplateHashMapImpl<ZoneAllocationPolicy> ZoneHashMap;
+
+} } // namespace v8::internal
+
+#endif // V8_ZONE_H_
diff --git a/chromium/v8/test/cctest/cctest.gyp b/chromium/v8/test/cctest/cctest.gyp
new file mode 100644
index 00000000000..9df5c7bccc3
--- /dev/null
+++ b/chromium/v8/test/cctest/cctest.gyp
@@ -0,0 +1,217 @@
+# Copyright 2012 the V8 project authors. 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': {
+ 'v8_code': 1,
+ 'generated_file': '<(SHARED_INTERMEDIATE_DIR)/resources.cc',
+ },
+ 'includes': ['../../build/toolchain.gypi', '../../build/features.gypi'],
+ 'targets': [
+ {
+ 'target_name': 'cctest',
+ 'type': 'executable',
+ 'dependencies': [
+ 'resources',
+ ],
+ 'include_dirs': [
+ '../../src',
+ ],
+ 'sources': [
+ '<(generated_file)',
+ 'cctest.cc',
+ 'gay-fixed.cc',
+ 'gay-precision.cc',
+ 'gay-shortest.cc',
+ 'test-accessors.cc',
+ 'test-alloc.cc',
+ 'test-api.cc',
+ 'test-ast.cc',
+ 'test-bignum.cc',
+ 'test-bignum-dtoa.cc',
+ 'test-circular-queue.cc',
+ 'test-compiler.cc',
+ 'test-conversions.cc',
+ 'test-cpu-profiler.cc',
+ 'test-dataflow.cc',
+ 'test-date.cc',
+ 'test-debug.cc',
+ 'test-declarative-accessors.cc',
+ 'test-decls.cc',
+ 'test-deoptimization.cc',
+ 'test-dictionary.cc',
+ 'test-diy-fp.cc',
+ 'test-double.cc',
+ 'test-dtoa.cc',
+ 'test-fast-dtoa.cc',
+ 'test-fixed-dtoa.cc',
+ 'test-flags.cc',
+ 'test-func-name-inference.cc',
+ 'test-global-handles.cc',
+ 'test-global-object.cc',
+ 'test-hashing.cc',
+ 'test-hashmap.cc',
+ 'test-heap.cc',
+ 'test-heap-profiler.cc',
+ 'test-list.cc',
+ 'test-liveedit.cc',
+ 'test-lock.cc',
+ 'test-lockers.cc',
+ 'test-log.cc',
+ 'test-mark-compact.cc',
+ 'test-object-observe.cc',
+ 'test-parsing.cc',
+ 'test-platform.cc',
+ 'test-platform-tls.cc',
+ 'test-profile-generator.cc',
+ 'test-random.cc',
+ 'test-regexp.cc',
+ 'test-reloc-info.cc',
+ 'test-serialize.cc',
+ 'test-sockets.cc',
+ 'test-spaces.cc',
+ 'test-strings.cc',
+ 'test-symbols.cc',
+ 'test-strtod.cc',
+ 'test-thread-termination.cc',
+ 'test-threads.cc',
+ 'test-types.cc',
+ 'test-unbound-queue.cc',
+ 'test-utils.cc',
+ 'test-version.cc',
+ 'test-weakmaps.cc',
+ 'test-weaksets.cc',
+ 'test-weaktypedarrays.cc'
+ ],
+ 'conditions': [
+ ['v8_target_arch=="ia32"', {
+ 'sources': [
+ 'test-assembler-ia32.cc',
+ 'test-code-stubs.cc',
+ 'test-code-stubs-ia32.cc',
+ 'test-disasm-ia32.cc',
+ 'test-log-stack-tracer.cc'
+ ],
+ }],
+ ['v8_target_arch=="x64"', {
+ 'sources': [
+ 'test-assembler-x64.cc',
+ 'test-code-stubs.cc',
+ 'test-code-stubs-x64.cc',
+ 'test-macro-assembler-x64.cc',
+ 'test-log-stack-tracer.cc'
+ ],
+ }],
+ ['v8_target_arch=="arm"', {
+ 'sources': [
+ 'test-assembler-arm.cc',
+ 'test-disasm-arm.cc'
+ ],
+ }],
+ ['v8_target_arch=="mipsel"', {
+ 'sources': [
+ 'test-assembler-mips.cc',
+ 'test-disasm-mips.cc',
+ ],
+ }],
+ [ 'OS=="linux"', {
+ 'sources': [
+ 'test-platform-linux.cc',
+ ],
+ }],
+ [ 'OS=="mac"', {
+ 'sources': [
+ 'test-platform-macos.cc',
+ ],
+ }],
+ [ 'OS=="win"', {
+ '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
+ # depend on the underlying static target in that case.
+ 'conditions': [
+ ['v8_use_snapshot=="true"', {
+ 'dependencies': ['../../tools/gyp/v8.gyp:v8_snapshot'],
+ },
+ {
+ 'dependencies': [
+ '../../tools/gyp/v8.gyp:v8_nosnapshot.<(v8_target_arch)',
+ ],
+ }],
+ ],
+ }, {
+ 'dependencies': ['../../tools/gyp/v8.gyp:v8'],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'resources',
+ 'type': 'none',
+ 'variables': {
+ 'file_list': [
+ '../../tools/splaytree.js',
+ '../../tools/codemap.js',
+ '../../tools/csvparser.js',
+ '../../tools/consarray.js',
+ '../../tools/profile.js',
+ '../../tools/profile_view.js',
+ '../../tools/logreader.js',
+ 'log-eq-of-logging-and-traversal.js',
+ ],
+ },
+ 'actions': [
+ {
+ 'action_name': 'js2c',
+ 'inputs': [
+ '../../tools/js2c.py',
+ '<@(file_list)',
+ ],
+ 'outputs': [
+ '<(generated_file)',
+ ],
+ 'action': [
+ 'python',
+ '../../tools/js2c.py',
+ '<@(_outputs)',
+ 'TEST', # type
+ 'off', # compression
+ '<@(file_list)',
+ ],
+ }
+ ],
+ },
+ ],
+}
diff --git a/chromium/v8/tools/android-build.sh b/chromium/v8/tools/android-build.sh
new file mode 100755
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/v8/tools/android-build.sh
diff --git a/chromium/v8/tools/android-ll-prof.sh b/chromium/v8/tools/android-ll-prof.sh
new file mode 100755
index 00000000000..436f262bb36
--- /dev/null
+++ b/chromium/v8/tools/android-ll-prof.sh
@@ -0,0 +1,69 @@
+#!/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.
+
+# Runs d8 with the given arguments on the device under 'perf' and
+# processes the profiler trace and v8 logs using ll_prof.py.
+#
+# Usage:
+# > ./tools/android-ll-prof.sh (debug|release) "args to d8" "args to ll_prof.py"
+#
+# The script creates deploy directory deploy/data/local/tmp/v8, copies there
+# the d8 binary either from out/android_arm.release or out/android_arm.debug,
+# and then sync the deploy directory with /data/local/tmp/v8 on the device.
+# You can put JS files in the deploy directory before running the script.
+# Note: $ANDROID_NDK_ROOT must be set.
+
+MODE=$1
+RUN_ARGS=$2
+LL_PROF_ARGS=$3
+
+BASE=`cd $(dirname "$0")/..; pwd`
+DEPLOY="$BASE/deploy"
+
+set +e
+mkdir -p "$DEPLOY/data/local/tmp/v8"
+
+cp "$BASE/out/android_arm.$MODE/d8" "$DEPLOY/data/local/tmp/v8/d8"
+
+adb -p "$DEPLOY" sync data
+
+adb shell "cd /data/local/tmp/v8;\
+ perf record -R -e cycles -c 10000 -f -i \
+ ./d8 --ll_prof --gc-fake-mmap=/data/local/tmp/__v8_gc__ $RUN_ARGS"
+
+adb pull /data/local/tmp/v8/v8.log .
+adb pull /data/local/tmp/v8/v8.log.ll .
+adb pull /data/perf.data .
+
+ARCH=arm-linux-androideabi-4.6
+TOOLCHAIN="${ANDROID_NDK_ROOT}/toolchains/$ARCH/prebuilt/linux-x86/bin"
+
+$BASE/tools/ll_prof.py --host-root="$BASE/deploy" \
+ --gc-fake-mmap=/data/local/tmp/__v8_gc__ \
+ --objdump="$TOOLCHAIN/arm-linux-androideabi-objdump" \
+ $LL_PROF_ARGS
diff --git a/chromium/v8/tools/android-run.py b/chromium/v8/tools/android-run.py
new file mode 100755
index 00000000000..dc1359883a1
--- /dev/null
+++ b/chromium/v8/tools/android-run.py
@@ -0,0 +1,109 @@
+#!/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.
+
+# This script executes the passed command line on Android device
+# using 'adb shell' command. Unfortunately, 'adb shell' always
+# returns exit code 0, ignoring the exit code of executed command.
+# Since we need to return non-zero exit code if the command failed,
+# we augment the passed command line with exit code checking statement
+# and output special error string in case of non-zero exit code.
+# Then we parse the output of 'adb shell' and look for that error string.
+
+import os
+from os.path import join, dirname, abspath
+import subprocess
+import sys
+import tempfile
+
+def Check(output, errors):
+ failed = any([s.startswith('/system/bin/sh:') or s.startswith('ANDROID')
+ for s in output.split('\n')])
+ return 1 if failed else 0
+
+def Execute(cmdline):
+ (fd_out, outname) = tempfile.mkstemp()
+ (fd_err, errname) = tempfile.mkstemp()
+ process = subprocess.Popen(
+ args=cmdline,
+ shell=True,
+ stdout=fd_out,
+ stderr=fd_err,
+ )
+ exit_code = process.wait()
+ os.close(fd_out)
+ os.close(fd_err)
+ output = file(outname).read()
+ errors = file(errname).read()
+ os.unlink(outname)
+ os.unlink(errname)
+ sys.stdout.write(output)
+ sys.stderr.write(errors)
+ return exit_code or Check(output, errors)
+
+def Escape(arg):
+ def ShouldEscape():
+ for x in arg:
+ if not x.isalnum() and x != '-' and x != '_':
+ return True
+ return False
+
+ return arg if not ShouldEscape() else '"%s"' % (arg.replace('"', '\\"'))
+
+def WriteToTemporaryFile(data):
+ (fd, fname) = tempfile.mkstemp()
+ os.close(fd)
+ tmp_file = open(fname, "w")
+ tmp_file.write(data)
+ tmp_file.close()
+ return fname
+
+def Main():
+ if (len(sys.argv) == 1):
+ print("Usage: %s <command-to-run-on-device>" % sys.argv[0])
+ return 1
+ workspace = abspath(join(dirname(sys.argv[0]), '..'))
+ android_workspace = os.getenv("ANDROID_V8", "/data/local/tmp/v8")
+ args = [Escape(arg) for arg in sys.argv[1:]]
+ script = (" ".join(args) + "\n"
+ "case $? in\n"
+ " 0) ;;\n"
+ " *) echo \"ANDROID: Error returned by test\";;\n"
+ "esac\n")
+ script = script.replace(workspace, android_workspace)
+ script_file = WriteToTemporaryFile(script)
+ android_script_file = android_workspace + "/" + script_file
+ command = ("adb push '%s' %s;" % (script_file, android_script_file) +
+ "adb shell 'sh %s';" % android_script_file +
+ "adb shell 'rm %s'" % android_script_file)
+ error_code = Execute(command)
+ os.unlink(script_file)
+ return error_code
+
+if __name__ == '__main__':
+ sys.exit(Main())
diff --git a/chromium/v8/tools/android-sync.sh b/chromium/v8/tools/android-sync.sh
new file mode 100755
index 00000000000..5d4ef2effdc
--- /dev/null
+++ b/chromium/v8/tools/android-sync.sh
@@ -0,0 +1,105 @@
+#!/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.
+
+# This script pushes android binaries and test data to the device.
+# The first argument can be either "android.release" or "android.debug".
+# The second argument is a relative path to the output directory with binaries.
+# The third argument is the absolute path to the V8 directory on the host.
+# The fourth argument is the absolute path to the V8 directory on the device.
+
+if [ ${#@} -lt 4 ] ; then
+ echo "$0: Error: need 4 arguments"
+ exit 1
+fi
+
+ARCH_MODE=$1
+OUTDIR=$2
+HOST_V8=$3
+ANDROID_V8=$4
+
+function LINUX_MD5 {
+ local HASH=$(md5sum $1)
+ echo ${HASH%% *}
+}
+
+function DARWIN_MD5 {
+ local HASH=$(md5 $1)
+ echo ${HASH} | cut -f2 -d "=" | cut -f2 -d " "
+}
+
+host_os=$(uname -s)
+case "${host_os}" in
+ "Linux")
+ MD5=LINUX_MD5
+ ;;
+ "Darwin")
+ MD5=DARWIN_MD5
+ ;;
+ *)
+ echo "$0: Host platform ${host_os} is not supported" >& 2
+ exit 1
+esac
+
+function sync_file {
+ local FILE=$1
+ local ANDROID_HASH=$(adb shell "md5 \"$ANDROID_V8/$FILE\"")
+ local HOST_HASH=$($MD5 "$HOST_V8/$FILE")
+ if [ "${ANDROID_HASH%% *}" != "${HOST_HASH}" ]; then
+ adb push "$HOST_V8/$FILE" "$ANDROID_V8/$FILE" &> /dev/null
+ fi
+ echo -n "."
+}
+
+function sync_dir {
+ local DIR=$1
+ echo -n "sync to $ANDROID_V8/$DIR"
+ for FILE in $(find "$HOST_V8/$DIR" -not -path "*.svn*" -type f); do
+ local RELATIVE_FILE=${FILE:${#HOST_V8}}
+ sync_file "$RELATIVE_FILE"
+ done
+ echo ""
+}
+
+echo -n "sync to $ANDROID_V8/$OUTDIR/$ARCH_MODE"
+sync_file "$OUTDIR/$ARCH_MODE/cctest"
+sync_file "$OUTDIR/$ARCH_MODE/d8"
+sync_file "$OUTDIR/$ARCH_MODE/preparser"
+echo ""
+echo -n "sync to $ANDROID_V8/tools"
+sync_file tools/consarray.js
+sync_file tools/codemap.js
+sync_file tools/csvparser.js
+sync_file tools/profile.js
+sync_file tools/splaytree.js
+sync_file tools/profile_view.js
+sync_file tools/logreader.js
+sync_file tools/tickprocessor.js
+echo ""
+sync_dir test/message
+sync_dir test/mjsunit
+sync_dir test/preparser
diff --git a/chromium/v8/tools/bash-completion.sh b/chromium/v8/tools/bash-completion.sh
new file mode 100755
index 00000000000..9f65c677317
--- /dev/null
+++ b/chromium/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/chromium/v8/tools/blink_tests/TestExpectations b/chromium/v8/tools/blink_tests/TestExpectations
new file mode 100644
index 00000000000..eec1d5a2165
--- /dev/null
+++ b/chromium/v8/tools/blink_tests/TestExpectations
@@ -0,0 +1,27 @@
+# Tests that sometimes fail only on the V8 waterfall:
+[ Linux Release x86 ] fast/js/JSON-stringify.html [ Pass Failure Slow ]
+[ Linux Release x86 ] fast/text/atsui-multiple-renderers.html [ Pass Failure Slow ]
+[ Linux Release x86 ] fast/text/international/complex-joining-using-gpos.html [ Pass Failure Slow ]
+[ Linux Release x86 ] fast/text/international/danda-space.html [ Pass Failure Slow ]
+[ Linux Release x86 ] fast/text/international/thai-baht-space.html [ Pass Failure Slow ]
+[ Linux Release x86 ] fast/text/international/thai-line-breaks.html [ Pass Failure Slow ]
+[ Linux Release x86 ] inspector/profiler/memory-instrumentation-external-array.html [ Pass Failure Slow ]
+[ Linux Release x86_64 ] fast/text/atsui-multiple-renderers.html [ Pass Failure Slow ]
+[ Linux Release x86_64 ] fast/text/international/complex-joining-using-gpos.html [ Pass Failure Slow ]
+[ Linux Release x86_64 ] fast/text/international/danda-space.html [ Pass Failure Slow ]
+[ Linux Release x86_64 ] fast/text/international/thai-baht-space.html [ Pass Failure Slow ]
+[ Linux Release x86_64 ] fast/text/international/thai-line-breaks.html [ Pass Failure Slow ]
+[ Linux Release x86_64 ] inspector/profiler/memory-instrumentation-external-array.html [ Pass Failure Slow ]
+[ Linux Debug ] fast/text/atsui-multiple-renderers.html [ Pass Failure Slow ]
+[ Linux Debug ] fast/text/international/complex-joining-using-gpos.html [ Pass Failure Slow ]
+[ Linux Debug ] fast/text/international/danda-space.html [ Pass Failure Slow ]
+[ Linux Debug ] fast/text/international/thai-baht-space.html [ Pass Failure Slow ]
+[ Linux Debug ] fast/text/international/thai-line-breaks.html [ Pass Failure Slow ]
+crbug.com/108833 [ Win Debug ] plugins/geturlnotify-during-document-teardown.html [ Crash Failure Timeout ]
+webkit.org/b/48655 [ Win ] plugins/js-from-destroy.html [ Crash Timeout ]
+crbug.com/178745 [ Win Debug ] plugins/open-and-close-window-with-plugin.html [ Crash Failure Timeout ]
+
+# Slow on the trunk builder:
+[ Linux Debug ] fast/js/regress/function-dot-apply.html [ Slow ]
+crbug.com/249894 [ Linux Debug ] fast/js/regress/inline-arguments-access.html [ Pass Failure Crash Slow ]
+[ Linux Debug ] fast/js/regress/inline-arguments-local-escape.html [ Slow ]
diff --git a/chromium/v8/tools/check-static-initializers.sh b/chromium/v8/tools/check-static-initializers.sh
new file mode 100755
index 00000000000..11ba080d5aa
--- /dev/null
+++ b/chromium/v8/tools/check-static-initializers.sh
@@ -0,0 +1,62 @@
+#!/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.
+
+# Checks that the number of compilation units having at least one static
+# initializer in d8 matches the one defined below.
+
+# Allow:
+# - _GLOBAL__I__ZN2v810LineEditor6first_E
+# - _GLOBAL__I__ZN2v88internal32AtomicOps_Internalx86CPUFeaturesE
+# - _GLOBAL__I__ZN2v88internal8ThreadId18highest_thread_id_E
+expected_static_init_count=3
+
+v8_root=$(readlink -f $(dirname $BASH_SOURCE)/../)
+
+if [ -n "$1" ] ; then
+ d8="${v8_root}/$1"
+else
+ d8="${v8_root}/d8"
+fi
+
+if [ ! -f "$d8" ]; then
+ echo "d8 binary not found: $d8"
+ exit 1
+fi
+
+static_inits=$(nm "$d8" | grep _GLOBAL_ | grep _I_ | awk '{ print $NF; }')
+
+static_init_count=$(echo "$static_inits" | wc -l)
+
+if [ $static_init_count -gt $expected_static_init_count ]; then
+ echo "Too many static initializers."
+ echo "$static_inits"
+ exit 1
+else
+ echo "Static initializer check passed ($static_init_count initializers)."
+ exit 0
+fi
diff --git a/chromium/v8/tools/codemap.js b/chromium/v8/tools/codemap.js
new file mode 100644
index 00000000000..129179e8456
--- /dev/null
+++ b/chromium/v8/tools/codemap.js
@@ -0,0 +1,292 @@
+// 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.
+
+
+/**
+ * Constructs a mapper that maps addresses into code entries.
+ *
+ * @constructor
+ */
+function CodeMap() {
+ /**
+ * Dynamic code entries. Used for JIT compiled code.
+ */
+ this.dynamics_ = new SplayTree();
+
+ /**
+ * Name generator for entries having duplicate names.
+ */
+ this.dynamicsNameGen_ = new CodeMap.NameGenerator();
+
+ /**
+ * Static code entries. Used for statically compiled code.
+ */
+ this.statics_ = new SplayTree();
+
+ /**
+ * Libraries entries. Used for the whole static code libraries.
+ */
+ this.libraries_ = new SplayTree();
+
+ /**
+ * Map of memory pages occupied with static code.
+ */
+ this.pages_ = [];
+};
+
+
+/**
+ * The number of alignment bits in a page address.
+ */
+CodeMap.PAGE_ALIGNMENT = 12;
+
+
+/**
+ * Page size in bytes.
+ */
+CodeMap.PAGE_SIZE =
+ 1 << CodeMap.PAGE_ALIGNMENT;
+
+
+/**
+ * Adds a dynamic (i.e. moveable and discardable) code entry.
+ *
+ * @param {number} start The starting address.
+ * @param {CodeMap.CodeEntry} codeEntry Code entry object.
+ */
+CodeMap.prototype.addCode = function(start, codeEntry) {
+ this.deleteAllCoveredNodes_(this.dynamics_, start, start + codeEntry.size);
+ this.dynamics_.insert(start, codeEntry);
+};
+
+
+/**
+ * Moves a dynamic code entry. Throws an exception if there is no dynamic
+ * code entry with the specified starting address.
+ *
+ * @param {number} from The starting address of the entry being moved.
+ * @param {number} to The destination address.
+ */
+CodeMap.prototype.moveCode = function(from, to) {
+ var removedNode = this.dynamics_.remove(from);
+ this.deleteAllCoveredNodes_(this.dynamics_, to, to + removedNode.value.size);
+ this.dynamics_.insert(to, removedNode.value);
+};
+
+
+/**
+ * Discards a dynamic code entry. Throws an exception if there is no dynamic
+ * code entry with the specified starting address.
+ *
+ * @param {number} start The starting address of the entry being deleted.
+ */
+CodeMap.prototype.deleteCode = function(start) {
+ var removedNode = this.dynamics_.remove(start);
+};
+
+
+/**
+ * Adds a library entry.
+ *
+ * @param {number} start The starting address.
+ * @param {CodeMap.CodeEntry} codeEntry Code entry object.
+ */
+CodeMap.prototype.addLibrary = function(
+ start, codeEntry) {
+ this.markPages_(start, start + codeEntry.size);
+ this.libraries_.insert(start, codeEntry);
+};
+
+
+/**
+ * Adds a static code entry.
+ *
+ * @param {number} start The starting address.
+ * @param {CodeMap.CodeEntry} codeEntry Code entry object.
+ */
+CodeMap.prototype.addStaticCode = function(
+ start, codeEntry) {
+ this.statics_.insert(start, codeEntry);
+};
+
+
+/**
+ * @private
+ */
+CodeMap.prototype.markPages_ = function(start, end) {
+ for (var addr = start; addr <= end;
+ addr += CodeMap.PAGE_SIZE) {
+ this.pages_[addr >>> CodeMap.PAGE_ALIGNMENT] = 1;
+ }
+};
+
+
+/**
+ * @private
+ */
+CodeMap.prototype.deleteAllCoveredNodes_ = function(tree, start, end) {
+ var to_delete = [];
+ var addr = end - 1;
+ while (addr >= start) {
+ var node = tree.findGreatestLessThan(addr);
+ if (!node) break;
+ var start2 = node.key, end2 = start2 + node.value.size;
+ if (start2 < end && start < end2) to_delete.push(start2);
+ addr = start2 - 1;
+ }
+ for (var i = 0, l = to_delete.length; i < l; ++i) tree.remove(to_delete[i]);
+};
+
+
+/**
+ * @private
+ */
+CodeMap.prototype.isAddressBelongsTo_ = function(addr, node) {
+ return addr >= node.key && addr < (node.key + node.value.size);
+};
+
+
+/**
+ * @private
+ */
+CodeMap.prototype.findInTree_ = function(tree, addr) {
+ var node = tree.findGreatestLessThan(addr);
+ return node && this.isAddressBelongsTo_(addr, node) ? node.value : null;
+};
+
+
+/**
+ * Finds a code entry that contains the specified address. Both static and
+ * dynamic code entries are considered.
+ *
+ * @param {number} addr Address.
+ */
+CodeMap.prototype.findEntry = function(addr) {
+ var pageAddr = addr >>> CodeMap.PAGE_ALIGNMENT;
+ if (pageAddr in this.pages_) {
+ // Static code entries can contain "holes" of unnamed code.
+ // In this case, the whole library is assigned to this address.
+ return this.findInTree_(this.statics_, addr) ||
+ this.findInTree_(this.libraries_, addr);
+ }
+ var min = this.dynamics_.findMin();
+ var max = this.dynamics_.findMax();
+ if (max != null && addr < (max.key + max.value.size) && addr >= min.key) {
+ var dynaEntry = this.findInTree_(this.dynamics_, addr);
+ if (dynaEntry == null) return null;
+ // Dedupe entry name.
+ if (!dynaEntry.nameUpdated_) {
+ dynaEntry.name = this.dynamicsNameGen_.getName(dynaEntry.name);
+ dynaEntry.nameUpdated_ = true;
+ }
+ return dynaEntry;
+ }
+ return null;
+};
+
+
+/**
+ * Returns a dynamic code entry using its starting address.
+ *
+ * @param {number} addr Address.
+ */
+CodeMap.prototype.findDynamicEntryByStartAddress =
+ function(addr) {
+ var node = this.dynamics_.find(addr);
+ return node ? node.value : null;
+};
+
+
+/**
+ * Returns an array of all dynamic code entries.
+ */
+CodeMap.prototype.getAllDynamicEntries = function() {
+ return this.dynamics_.exportValues();
+};
+
+
+/**
+ * Returns an array of pairs of all dynamic code entries and their addresses.
+ */
+CodeMap.prototype.getAllDynamicEntriesWithAddresses = function() {
+ return this.dynamics_.exportKeysAndValues();
+};
+
+
+/**
+ * Returns an array of all static code entries.
+ */
+CodeMap.prototype.getAllStaticEntries = function() {
+ return this.statics_.exportValues();
+};
+
+
+/**
+ * Returns an array of all libraries entries.
+ */
+CodeMap.prototype.getAllLibrariesEntries = function() {
+ return this.libraries_.exportValues();
+};
+
+
+/**
+ * Creates a code entry object.
+ *
+ * @param {number} size Code entry size in bytes.
+ * @param {string} opt_name Code entry name.
+ * @constructor
+ */
+CodeMap.CodeEntry = function(size, opt_name) {
+ this.size = size;
+ this.name = opt_name || '';
+ this.nameUpdated_ = false;
+};
+
+
+CodeMap.CodeEntry.prototype.getName = function() {
+ return this.name;
+};
+
+
+CodeMap.CodeEntry.prototype.toString = function() {
+ return this.name + ': ' + this.size.toString(16);
+};
+
+
+CodeMap.NameGenerator = function() {
+ this.knownNames_ = {};
+};
+
+
+CodeMap.NameGenerator.prototype.getName = function(name) {
+ if (!(name in this.knownNames_)) {
+ this.knownNames_[name] = 0;
+ return name;
+ }
+ var count = ++this.knownNames_[name];
+ return name + ' {' + count + '}';
+};
diff --git a/chromium/v8/tools/common-includes.sh b/chromium/v8/tools/common-includes.sh
new file mode 100644
index 00000000000..7785e9fc306
--- /dev/null
+++ b/chromium/v8/tools/common-includes.sh
@@ -0,0 +1,198 @@
+# Copyright 2012 the V8 project authors. 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 contains common function definitions for various other shell
+# scripts in this directory. It is not meant to be executed by itself.
+
+# Important: before including this file, the following variables must be set:
+# - BRANCHNAME
+# - PERSISTFILE_BASENAME
+
+TEMP_BRANCH=$BRANCHNAME-temporary-branch-created-by-script
+VERSION_FILE="src/version.cc"
+CHANGELOG_ENTRY_FILE="$PERSISTFILE_BASENAME-changelog-entry"
+PATCH_FILE="$PERSISTFILE_BASENAME-patch"
+COMMITMSG_FILE="$PERSISTFILE_BASENAME-commitmsg"
+TRUNK_REVISION_FILE="$PERSISTFILE_BASENAME-trunkrevision"
+START_STEP=0
+CURRENT_STEP=0
+
+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}' | grep -x $1)
+ 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"
+ local VALUE="${!VARNAME}"
+ if [ -z "$VALUE" ] ; then
+ VALUE="__EMPTY__"
+ fi
+ echo "$VALUE" > $FILE
+}
+
+restore() {
+ local VARNAME=$1
+ local FILE="$PERSISTFILE_BASENAME-$VARNAME"
+ local VALUE="$(cat $FILE)"
+ [[ -z "$VALUE" ]] && die "Variable '$VARNAME' could not be restored."
+ if [ "$VALUE" == "__EMPTY__" ] ; then
+ VALUE=""
+ fi
+ eval "$VARNAME=\"$VALUE\""
+}
+
+restore_if_unset() {
+ local VARNAME=$1
+ [[ -z "${!VARNAME}" ]] && restore "$VARNAME"
+}
+
+initial_environment_checks() {
+ # 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."
+}
+
+common_prepare() {
+ # 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"
+
+ # Fetch unfetched revisions.
+ git svn fetch || die "'git svn fetch' failed."
+
+ # Get ahold of a safe temporary branch and check it out.
+ if [ "$CURRENT_BRANCH" != "$TEMP_BRANCH" ] ; then
+ delete_branch $TEMP_BRANCH
+ git checkout -b $TEMP_BRANCH
+ fi
+
+ # Delete the branch that will be created later if it exists already.
+ delete_branch $BRANCHNAME
+}
+
+common_cleanup() {
+ restore_if_unset "CURRENT_BRANCH"
+ git checkout -f $CURRENT_BRANCH
+ [[ "$TEMP_BRANCH" != "$CURRENT_BRANCH" ]] && git branch -D $TEMP_BRANCH
+ [[ "$BRANCHNAME" != "$CURRENT_BRANCH" ]] && git branch -D $BRANCHNAME
+ # Clean up all temporary files.
+ rm -f "$PERSISTFILE_BASENAME"*
+}
+
+# These two functions take a prefix for the variable names as first argument.
+read_and_persist_version() {
+ for v in MAJOR_VERSION MINOR_VERSION BUILD_NUMBER PATCH_LEVEL; do
+ VARNAME="$1${v%%_*}"
+ VALUE=$(grep "#define $v" "$VERSION_FILE" | awk '{print $NF}')
+ eval "$VARNAME=\"$VALUE\""
+ persist "$VARNAME"
+ done
+}
+restore_version_if_unset() {
+ for v in MAJOR MINOR BUILD PATCH; do
+ restore_if_unset "$1$v"
+ done
+}
+
+upload_step() {
+ 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
+}
+
+wait_for_lgtm() {
+ 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
+}
+
+wait_for_resolving_conflicts() {
+ echo "Applying the patch \"$1\" failed. Either type \"ABORT<Return>\", or \
+resolve the conflicts, stage *all* touched files with 'git add', and \
+type \"RESOLVED<Return>\""
+ unset ANSWER
+ while [ "$ANSWER" != "RESOLVED" ] ; do
+ [[ "$ANSWER" == "ABORT" ]] && die "Applying the patch failed."
+ [[ -n "$ANSWER" ]] && echo "That was not 'RESOLVED' or 'ABORT'."
+ echo -n "> "
+ read ANSWER
+ done
+}
+
+# Takes a file containing the patch to apply as first argument.
+apply_patch() {
+ git apply --index --reject $REVERSE_PATCH "$1" || \
+ wait_for_resolving_conflicts "$1";
+}
diff --git a/chromium/v8/tools/consarray.js b/chromium/v8/tools/consarray.js
new file mode 100644
index 00000000000..c67abb79711
--- /dev/null
+++ b/chromium/v8/tools/consarray.js
@@ -0,0 +1,93 @@
+// 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.
+
+
+/**
+ * Constructs a ConsArray object. It is used mainly for tree traversal.
+ * In this use case we have lots of arrays that we need to iterate
+ * sequentally. The internal Array implementation is horribly slow
+ * when concatenating on large (10K items) arrays due to memory copying.
+ * That's why we avoid copying memory and insead build a linked list
+ * of arrays to iterate through.
+ *
+ * @constructor
+ */
+function ConsArray() {
+ this.tail_ = new ConsArray.Cell(null, null);
+ this.currCell_ = this.tail_;
+ this.currCellPos_ = 0;
+};
+
+
+/**
+ * Concatenates another array for iterating. Empty arrays are ignored.
+ * This operation can be safely performed during ongoing ConsArray
+ * iteration.
+ *
+ * @param {Array} arr Array to concatenate.
+ */
+ConsArray.prototype.concat = function(arr) {
+ if (arr.length > 0) {
+ this.tail_.data = arr;
+ this.tail_ = this.tail_.next = new ConsArray.Cell(null, null);
+ }
+};
+
+
+/**
+ * Whether the end of iteration is reached.
+ */
+ConsArray.prototype.atEnd = function() {
+ return this.currCell_ === null ||
+ this.currCell_.data === null ||
+ this.currCellPos_ >= this.currCell_.data.length;
+};
+
+
+/**
+ * Returns the current item, moves to the next one.
+ */
+ConsArray.prototype.next = function() {
+ var result = this.currCell_.data[this.currCellPos_++];
+ if (this.currCellPos_ >= this.currCell_.data.length) {
+ this.currCell_ = this.currCell_.next;
+ this.currCellPos_ = 0;
+ }
+ return result;
+};
+
+
+/**
+ * A cell object used for constructing a list in ConsArray.
+ *
+ * @constructor
+ */
+ConsArray.Cell = function(data, next) {
+ this.data = data;
+ this.next = next;
+};
+
diff --git a/chromium/v8/tools/csvparser.js b/chromium/v8/tools/csvparser.js
new file mode 100644
index 00000000000..c7d46b535c7
--- /dev/null
+++ b/chromium/v8/tools/csvparser.js
@@ -0,0 +1,78 @@
+// 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.
+
+
+/**
+ * Creates a CSV lines parser.
+ */
+function CsvParser() {
+};
+
+
+/**
+ * A regex for matching a CSV field.
+ * @private
+ */
+CsvParser.CSV_FIELD_RE_ = /^"((?:[^"]|"")*)"|([^,]*)/;
+
+
+/**
+ * A regex for matching a double quote.
+ * @private
+ */
+CsvParser.DOUBLE_QUOTE_RE_ = /""/g;
+
+
+/**
+ * Parses a line of CSV-encoded values. Returns an array of fields.
+ *
+ * @param {string} line Input line.
+ */
+CsvParser.prototype.parseLine = function(line) {
+ var fieldRe = CsvParser.CSV_FIELD_RE_;
+ var doubleQuoteRe = CsvParser.DOUBLE_QUOTE_RE_;
+ var pos = 0;
+ var endPos = line.length;
+ var fields = [];
+ if (endPos > 0) {
+ do {
+ var fieldMatch = fieldRe.exec(line.substr(pos));
+ if (typeof fieldMatch[1] === "string") {
+ var field = fieldMatch[1];
+ pos += field.length + 3; // Skip comma and quotes.
+ fields.push(field.replace(doubleQuoteRe, '"'));
+ } else {
+ // The second field pattern will match anything, thus
+ // in the worst case the match will be an empty string.
+ var field = fieldMatch[2];
+ pos += field.length + 1; // Skip comma.
+ fields.push(field);
+ }
+ } while (pos <= endPos);
+ }
+ return fields;
+};
diff --git a/chromium/v8/tools/disasm.py b/chromium/v8/tools/disasm.py
new file mode 100644
index 00000000000..6fa81cab938
--- /dev/null
+++ b/chromium/v8/tools/disasm.py
@@ -0,0 +1,93 @@
+#!/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.
+
+import os
+import re
+import subprocess
+import tempfile
+
+
+# Avoid using the slow (google-specific) wrapper around objdump.
+OBJDUMP_BIN = "/usr/bin/objdump"
+if not os.path.exists(OBJDUMP_BIN):
+ OBJDUMP_BIN = "objdump"
+
+
+_COMMON_DISASM_OPTIONS = ["-M", "intel-mnemonic", "-C"]
+
+_DISASM_HEADER_RE = re.compile(r"[a-f0-9]+\s+<.*:$")
+_DISASM_LINE_RE = re.compile(r"\s*([a-f0-9]+):\s*(\S.*)")
+
+# Keys must match constants in Logger::LogCodeInfo.
+_ARCH_MAP = {
+ "ia32": "-m i386",
+ "x64": "-m i386 -M x86-64",
+ "arm": "-m arm", # Not supported by our objdump build.
+ "mips": "-m mips" # Not supported by our objdump build.
+}
+
+
+def GetDisasmLines(filename, offset, size, arch, inplace, arch_flags=""):
+ tmp_name = None
+ if not inplace:
+ # Create a temporary file containing a copy of the code.
+ assert arch in _ARCH_MAP, "Unsupported architecture '%s'" % arch
+ arch_flags = arch_flags + " " + _ARCH_MAP[arch]
+ tmp_name = tempfile.mktemp(".v8code")
+ command = "dd if=%s of=%s bs=1 count=%d skip=%d && " \
+ "%s %s -D -b binary %s %s" % (
+ filename, tmp_name, size, offset,
+ OBJDUMP_BIN, ' '.join(_COMMON_DISASM_OPTIONS), arch_flags,
+ tmp_name)
+ else:
+ command = "%s %s %s --start-address=%d --stop-address=%d -d %s " % (
+ OBJDUMP_BIN, ' '.join(_COMMON_DISASM_OPTIONS), arch_flags,
+ offset,
+ offset + size,
+ filename)
+ process = subprocess.Popen(command,
+ shell=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, err = process.communicate()
+ lines = out.split("\n")
+ header_line = 0
+ for i, line in enumerate(lines):
+ if _DISASM_HEADER_RE.match(line):
+ header_line = i
+ break
+ if tmp_name:
+ os.unlink(tmp_name)
+ split_lines = []
+ for line in lines[header_line + 1:]:
+ match = _DISASM_LINE_RE.match(line)
+ if match:
+ line_address = int(match.group(1), 16)
+ split_lines.append((line_address, match.group(2)))
+ return split_lines
diff --git a/chromium/v8/tools/freebsd-tick-processor b/chromium/v8/tools/freebsd-tick-processor
new file mode 100755
index 00000000000..2bb2618bfa0
--- /dev/null
+++ b/chromium/v8/tools/freebsd-tick-processor
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# A wrapper script to call 'linux-tick-processor'.
+
+# Known issues on FreeBSD:
+# No ticks from C++ code.
+# You must have d8 built and in your path before calling this.
+
+tools_path=`cd $(dirname "$0");pwd`
+$tools_path/linux-tick-processor "$@"
diff --git a/chromium/v8/tools/fuzz-harness.sh b/chromium/v8/tools/fuzz-harness.sh
new file mode 100755
index 00000000000..efbf8646cee
--- /dev/null
+++ b/chromium/v8/tools/fuzz-harness.sh
@@ -0,0 +1,92 @@
+#!/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.
+
+# A simple harness that downloads and runs 'jsfunfuzz' against d8. This
+# takes a long time because it runs many iterations and is intended for
+# automated usage. The package containing 'jsfunfuzz' can be found as an
+# attachment to this bug:
+# https://bugzilla.mozilla.org/show_bug.cgi?id=jsfunfuzz
+
+JSFUNFUZZ_URL="https://bugzilla.mozilla.org/attachment.cgi?id=310631"
+JSFUNFUZZ_MD5="d0e497201c5cd7bffbb1cdc1574f4e32"
+
+v8_root=$(readlink -f $(dirname $BASH_SOURCE)/../)
+
+if [ -n "$1" ]; then
+ d8="${v8_root}/$1"
+else
+ d8="${v8_root}/d8"
+fi
+
+if [ ! -f "$d8" ]; then
+ echo "Failed to find d8 binary: $d8"
+ exit 1
+fi
+
+jsfunfuzz_file="$v8_root/tools/jsfunfuzz.zip"
+if [ ! -f "$jsfunfuzz_file" ]; then
+ echo "Downloading $jsfunfuzz_file ..."
+ wget -q -O "$jsfunfuzz_file" $JSFUNFUZZ_URL || exit 1
+fi
+
+jsfunfuzz_sum=$(md5sum "$jsfunfuzz_file" | awk '{ print $1 }')
+if [ $jsfunfuzz_sum != $JSFUNFUZZ_MD5 ]; then
+ echo "Failed to verify checksum!"
+ exit 1
+fi
+
+jsfunfuzz_dir="$v8_root/tools/jsfunfuzz"
+if [ ! -d "$jsfunfuzz_dir" ]; then
+ echo "Unpacking into $jsfunfuzz_dir ..."
+ unzip "$jsfunfuzz_file" -d "$jsfunfuzz_dir" || exit 1
+ echo "Patching runner ..."
+ cat << EOF | patch -s -p0 -d "$v8_root"
+--- tools/jsfunfuzz/jsfunfuzz/multi_timed_run.py~
++++ tools/jsfunfuzz/jsfunfuzz/multi_timed_run.py
+@@ -125,7 +125,7 @@
+
+ def many_timed_runs():
+ iteration = 0
+- while True:
++ while iteration < 100:
+ iteration += 1
+ logfilename = "w%d" % iteration
+ one_timed_run(logfilename)
+EOF
+fi
+
+flags='--debug-code --expose-gc --verify-gc'
+python -u "$jsfunfuzz_dir/jsfunfuzz/multi_timed_run.py" 300 \
+ "$d8" $flags "$jsfunfuzz_dir/jsfunfuzz/jsfunfuzz.js"
+exit_code=$(cat w* | grep " looking good" -c)
+exit_code=$((100-exit_code))
+tar -cjf fuzz-results-$(date +%y%m%d).tar.bz2 err-* w*
+rm -f err-* w*
+
+echo "Total failures: $exit_code"
+exit $exit_code
diff --git a/chromium/v8/tools/gc-nvp-trace-processor.py b/chromium/v8/tools/gc-nvp-trace-processor.py
new file mode 100755
index 00000000000..fe5a7f361e9
--- /dev/null
+++ b/chromium/v8/tools/gc-nvp-trace-processor.py
@@ -0,0 +1,389 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+#
+# This is an utility for plotting charts based on GC traces produced by V8 when
+# run with flags --trace-gc --trace-gc-nvp. Relies on gnuplot for actual
+# plotting.
+#
+# Usage: gc-nvp-trace-processor.py <GC-trace-filename>
+#
+
+
+from __future__ import with_statement
+import sys, types, re, subprocess, math
+
+def flatten(l):
+ flat = []
+ for i in l: flat.extend(i)
+ return flat
+
+def split_nvp(s):
+ t = {}
+ for (name, value) in re.findall(r"(\w+)=([-\w]+)", s):
+ try:
+ t[name] = int(value)
+ except ValueError:
+ t[name] = value
+
+ return t
+
+def parse_gc_trace(input):
+ trace = []
+ with open(input) as f:
+ for line in f:
+ info = split_nvp(line)
+ if info and 'pause' in info and info['pause'] > 0:
+ info['i'] = len(trace)
+ trace.append(info)
+ return trace
+
+def extract_field_names(script):
+ fields = { 'data': true, 'in': true }
+
+ for m in re.finditer(r"$(\w+)", script):
+ field_name = m.group(1)
+ if field_name not in fields:
+ fields[field] = field_count
+ field_count = field_count + 1
+
+ return fields
+
+def gnuplot(script):
+ gnuplot = subprocess.Popen(["gnuplot"], stdin=subprocess.PIPE)
+ gnuplot.stdin.write(script)
+ gnuplot.stdin.close()
+ gnuplot.wait()
+
+x1y1 = 'x1y1'
+x1y2 = 'x1y2'
+x2y1 = 'x2y1'
+x2y2 = 'x2y2'
+
+class Item(object):
+ def __init__(self, title, field, axis = x1y1, **keywords):
+ self.title = title
+ self.axis = axis
+ self.props = keywords
+ if type(field) is types.ListType:
+ self.field = field
+ else:
+ self.field = [field]
+
+ def fieldrefs(self):
+ return self.field
+
+ def to_gnuplot(self, context):
+ args = ['"%s"' % context.datafile,
+ 'using %s' % context.format_fieldref(self.field),
+ 'title "%s"' % self.title,
+ 'axis %s' % self.axis]
+ if 'style' in self.props:
+ args.append('with %s' % self.props['style'])
+ if 'lc' in self.props:
+ args.append('lc rgb "%s"' % self.props['lc'])
+ if 'fs' in self.props:
+ args.append('fs %s' % self.props['fs'])
+ return ' '.join(args)
+
+class Plot(object):
+ def __init__(self, *items):
+ self.items = items
+
+ def fieldrefs(self):
+ return flatten([item.fieldrefs() for item in self.items])
+
+ def to_gnuplot(self, ctx):
+ return 'plot ' + ', '.join([item.to_gnuplot(ctx) for item in self.items])
+
+class Set(object):
+ def __init__(self, value):
+ self.value = value
+
+ def to_gnuplot(self, ctx):
+ return 'set ' + self.value
+
+ def fieldrefs(self):
+ return []
+
+class Context(object):
+ def __init__(self, datafile, field_to_index):
+ self.datafile = datafile
+ self.field_to_index = field_to_index
+
+ def format_fieldref(self, fieldref):
+ return ':'.join([str(self.field_to_index[field]) for field in fieldref])
+
+def collect_fields(plot):
+ field_to_index = {}
+ fields = []
+
+ def add_field(field):
+ if field not in field_to_index:
+ fields.append(field)
+ field_to_index[field] = len(fields)
+
+ for field in flatten([item.fieldrefs() for item in plot]):
+ add_field(field)
+
+ return (fields, field_to_index)
+
+def is_y2_used(plot):
+ for subplot in plot:
+ if isinstance(subplot, Plot):
+ for item in subplot.items:
+ if item.axis == x1y2 or item.axis == x2y2:
+ return True
+ return False
+
+def get_field(trace_line, field):
+ t = type(field)
+ if t is types.StringType:
+ return trace_line[field]
+ elif t is types.FunctionType:
+ return field(trace_line)
+
+def generate_datafile(datafile_name, trace, fields):
+ with open(datafile_name, 'w') as datafile:
+ for line in trace:
+ data_line = [str(get_field(line, field)) for field in fields]
+ datafile.write('\t'.join(data_line))
+ datafile.write('\n')
+
+def generate_script_and_datafile(plot, trace, datafile, output):
+ (fields, field_to_index) = collect_fields(plot)
+ generate_datafile(datafile, trace, fields)
+ script = [
+ 'set terminal png',
+ 'set output "%s"' % output,
+ 'set autoscale',
+ 'set ytics nomirror',
+ 'set xtics nomirror',
+ 'set key below'
+ ]
+
+ if is_y2_used(plot):
+ script.append('set autoscale y2')
+ script.append('set y2tics')
+
+ context = Context(datafile, field_to_index)
+
+ for item in plot:
+ script.append(item.to_gnuplot(context))
+
+ return '\n'.join(script)
+
+def plot_all(plots, trace, prefix):
+ charts = []
+
+ for plot in plots:
+ outfilename = "%s_%d.png" % (prefix, len(charts))
+ charts.append(outfilename)
+ script = generate_script_and_datafile(plot, trace, '~datafile', outfilename)
+ print 'Plotting %s...' % outfilename
+ gnuplot(script)
+
+ return charts
+
+def reclaimed_bytes(row):
+ return row['total_size_before'] - row['total_size_after']
+
+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['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'),
+ 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 = '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'),
+ Set('style data histograms'),
+ Plot(Item('Heap Size (before GC)', 'total_size_before', x1y2,
+ fs = 'solid 0.4 noborder',
+ lc = 'green'),
+ Item('Total holes (after GC)', 'holes_size_before', x1y2,
+ fs = 'solid 0.4 noborder',
+ lc = 'red'),
+ Item('GC Time', ['i', 'pause'], style = 'lines', lc = 'red'))
+ ],
+ [
+ Set('style histogram rowstacked'),
+ Set('style data histograms'),
+ Plot(Item('Heap Size (after GC)', 'total_size_after', x1y2,
+ fs = 'solid 0.4 noborder',
+ lc = 'green'),
+ Item('Total holes (after GC)', 'holes_size_after', x1y2,
+ fs = 'solid 0.4 noborder',
+ lc = 'red'),
+ Item('GC Time', ['i', 'pause'],
+ style = 'lines',
+ lc = 'red'))
+ ],
+ [
+ Set('style fill solid 0.5 noborder'),
+ Set('style data histograms'),
+ Plot(Item('Allocated', 'allocated'),
+ Item('Reclaimed', reclaimed_bytes),
+ Item('Promoted', 'promoted', style = 'lines', lc = 'black'))
+ ],
+]
+
+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 + long(v), field, trace, long(0))
+
+def calc_max(trace, field):
+ return freduce(lambda t,r: max(t, r), field, trace, 0)
+
+def count_nonzero(trace, field):
+ return freduce(lambda t,r: t if r == 0 else t + 1, field, trace, 0)
+
+
+def process_trace(filename):
+ trace = parse_gc_trace(filename)
+
+ marksweeps = filter(lambda r: r['gc'] == 'ms', trace)
+ scavenges = filter(lambda r: r['gc'] == 's', trace)
+ globalgcs = filter(lambda r: r['gc'] != 's', trace)
+
+
+ charts = plot_all(plots, trace, filename)
+
+ def stats(out, prefix, trace, field):
+ n = len(trace)
+ total = calc_total(trace, field)
+ max = calc_max(trace, field)
+ if n > 0:
+ avg = total / n
+ else:
+ avg = 0
+ if n > 1:
+ dev = math.sqrt(freduce(lambda t,r: t + (r - avg) ** 2, field, trace, 0) /
+ (n - 1))
+ else:
+ dev = 0
+
+ out.write('<tr><td>%s</td><td>%d</td><td>%d</td>'
+ '<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>')
+ out.write('<table>')
+ out.write('<tr><td>Phase</td><td>Count</td><td>Time (ms)</td>')
+ out.write('<td>Max</td><td>Avg</td></tr>')
+ stats(out, 'Total in GC', trace, 'pause')
+ stats(out, 'Scavenge', scavenges, 'pause')
+ stats(out, 'MarkSweep', marksweeps, '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,
+ '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>')
+
+ print "%s generated." % (filename + '.html')
+
+if len(sys.argv) != 2:
+ print "Usage: %s <GC-trace-filename>" % sys.argv[0]
+ sys.exit(1)
+
+process_trace(sys.argv[1])
diff --git a/chromium/v8/tools/gcmole/Makefile b/chromium/v8/tools/gcmole/Makefile
new file mode 100644
index 00000000000..764245caf61
--- /dev/null
+++ b/chromium/v8/tools/gcmole/Makefile
@@ -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.
+
+# This is Makefile for clang plugin part of gcmole tool. See README.
+
+LLVM_INCLUDE:=$(LLVM_SRC_ROOT)/include
+CLANG_INCLUDE:=$(LLVM_SRC_ROOT)/tools/clang/include
+
+libgcmole.so: gcmole.cc
+ g++ -I$(LLVM_INCLUDE) -I$(CLANG_INCLUDE) -I. -D_DEBUG -D_GNU_SOURCE \
+ -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -O3 \
+ -fomit-frame-pointer -fno-exceptions -fno-rtti -fPIC \
+ -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing \
+ -pedantic -Wno-long-long -Wall \
+ -W -Wno-unused-parameter -Wwrite-strings \
+ -shared -o libgcmole.so gcmole.cc
+
+clean:
+ rm -f libgcmole.so
diff --git a/chromium/v8/tools/gcmole/README b/chromium/v8/tools/gcmole/README
new file mode 100644
index 00000000000..37f8afbd6f7
--- /dev/null
+++ b/chromium/v8/tools/gcmole/README
@@ -0,0 +1,62 @@
+DESCRIPTION -------------------------------------------------------------------
+
+gcmole is a simple static analysis tool used to find possible evaluation order
+dependent GC-unsafe places in the V8 codebase.
+
+For example the following code is GC-unsafe:
+
+Handle<Object> Foo(); // Assume Foo can trigger a GC.
+void Bar(Object*, Object*);
+
+Handle<Object> baz;
+baz->Qux(*Foo()); // (a)
+Bar(*Foo(), *baz); // (b)
+
+Both in cases (a) and (b) compiler is free to evaluate call arguments (that
+includes receiver) in any order. That means it can dereference baz before
+calling to Foo and save a raw pointer to a heap object in the register or
+on the stack.
+
+PREREQUISITES -----------------------------------------------------------------
+
+1) Install Lua 5.1
+
+2) Get LLVM 2.9 and Clang 2.9 sources and build them.
+
+Follow the instructions on http://clang.llvm.org/get_started.html.
+
+Make sure to pass --enable-optimized to configure to get Release build
+instead of a Debug one.
+
+3) Build gcmole Clang plugin (libgcmole.so)
+
+In the tools/gcmole execute the following command:
+
+LLVM_SRC_ROOT=<path-to-llvm-source-root> make
+
+USING GCMOLE ------------------------------------------------------------------
+
+gcmole consists of driver script written in Lua and Clang plugin that does
+C++ AST processing. Plugin (libgcmole.so) is expected to be in the same
+folder as driver (gcmole.lua).
+
+To start analysis cd into the root of v8 checkout and execute the following
+command:
+
+CLANG_BIN=<path-to-clang-bin-folder> lua tools/gcmole/gcmole.lua [<arch>]
+
+where arch should be one of architectures supported by V8 (arm, ia32, x64).
+
+Analysis will be performed in 2 stages:
+
+- on the first stage driver will parse all files and build a global callgraph
+approximation to find all functions that might potentially cause GC, list
+of this functions will be written into gcsuspects file.
+
+- on the second stage driver will parse all files again and will locate all
+callsites that might be GC-unsafe based on the list of functions causing GC.
+Such places are marked with a "Possible problem with evaluation order."
+warning. Messages "Failed to resolve v8::internal::Object" are benign and
+can be ignored.
+
+If any errors were found driver exits with non-zero status.
diff --git a/chromium/v8/tools/gcmole/bootstrap.sh b/chromium/v8/tools/gcmole/bootstrap.sh
new file mode 100755
index 00000000000..baa0b1f5f54
--- /dev/null
+++ b/chromium/v8/tools/gcmole/bootstrap.sh
@@ -0,0 +1,126 @@
+#!/usr/bin/env bash
+
+# Copyright 2013 the V8 project authors. 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 script will build libgcmole.so.
+
+CLANG_RELEASE=2.9
+
+THIS_DIR="$(dirname "${0}")"
+LLVM_DIR="${THIS_DIR}/../../third_party/llvm"
+CLANG_DIR="${LLVM_DIR}/tools/clang"
+
+LLVM_REPO_URL=${LLVM_URL:-https://llvm.org/svn/llvm-project}
+
+# Die if any command dies.
+set -e
+
+OS="$(uname -s)"
+
+# Xcode and clang don't get along when predictive compilation is enabled.
+# http://crbug.com/96315
+if [[ "${OS}" = "Darwin" ]] && xcodebuild -version | grep -q 'Xcode 3.2' ; then
+ XCONF=com.apple.Xcode
+ if [[ "${GYP_GENERATORS}" != "make" ]] && \
+ [ "$(defaults read "${XCONF}" EnablePredictiveCompilation)" != "0" ]; then
+ echo
+ echo " HEARKEN!"
+ echo "You're using Xcode3 and you have 'Predictive Compilation' enabled."
+ echo "This does not work well with clang (http://crbug.com/96315)."
+ echo "Disable it in Preferences->Building (lower right), or run"
+ echo " defaults write ${XCONF} EnablePredictiveCompilation -boolean NO"
+ echo "while Xcode is not running."
+ echo
+ fi
+
+ SUB_VERSION=$(xcodebuild -version | sed -Ene 's/Xcode 3\.2\.([0-9]+)/\1/p')
+ if [[ "${SUB_VERSION}" < 6 ]]; then
+ echo
+ echo " YOUR LD IS BUGGY!"
+ echo "Please upgrade Xcode to at least 3.2.6."
+ echo
+ fi
+fi
+
+echo Getting LLVM r"${CLANG_RELEASE}" in "${LLVM_DIR}"
+if ! svn co --force \
+ "${LLVM_REPO_URL}/llvm/branches/release_${CLANG_RELEASE/./}" \
+ "${LLVM_DIR}"; then
+ echo Checkout failed, retrying
+ rm -rf "${LLVM_DIR}"
+ svn co --force \
+ "${LLVM_REPO_URL}/llvm/branches/release_${CLANG_RELEASE/./}" \
+ "${LLVM_DIR}"
+fi
+
+echo Getting clang r"${CLANG_RELEASE}" in "${CLANG_DIR}"
+svn co --force \
+ "${LLVM_REPO_URL}/cfe/branches/release_${CLANG_RELEASE/./}" \
+ "${CLANG_DIR}"
+
+# Echo all commands
+set -x
+
+NUM_JOBS=3
+if [[ "${OS}" = "Linux" ]]; then
+ NUM_JOBS="$(grep -c "^processor" /proc/cpuinfo)"
+elif [ "${OS}" = "Darwin" ]; then
+ NUM_JOBS="$(sysctl -n hw.ncpu)"
+fi
+
+# Build clang.
+cd "${LLVM_DIR}"
+if [[ ! -f ./config.status ]]; then
+ ../llvm/configure \
+ --enable-optimized \
+ --disable-threads \
+ --disable-pthreads \
+ --without-llvmgcc \
+ --without-llvmgxx
+fi
+
+MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}"
+STRIP_FLAGS=
+if [ "${OS}" = "Darwin" ]; then
+ # See http://crbug.com/256342
+ STRIP_FLAGS=-x
+fi
+strip ${STRIP_FLAGS} Release/bin/clang
+cd -
+
+# Build libgcmole.so
+make -C "${THIS_DIR}" clean
+make -C "${THIS_DIR}" LLVM_SRC_ROOT="${LLVM_DIR}" libgcmole.so
+
+set +x
+
+echo
+echo You can now run gcmole using this command:
+echo
+echo CLANG_BIN=\"third_party/llvm/Release/bin\" lua tools/gcmole/gcmole.lua
+echo
diff --git a/chromium/v8/tools/gcmole/gccause.lua b/chromium/v8/tools/gcmole/gccause.lua
new file mode 100644
index 00000000000..b9891767de3
--- /dev/null
+++ b/chromium/v8/tools/gcmole/gccause.lua
@@ -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.
+
+-- This is an auxiliary tool that reads gccauses file generated by
+-- gcmole.lua and prints tree of the calls that can potentially cause a GC
+-- inside a given function.
+--
+-- Usage: lua tools/gcmole/gccause.lua <function-name-pattern>
+--
+
+assert(loadfile "gccauses")()
+
+local P = ...
+
+local T = {}
+
+local function TrackCause(name, lvl)
+ io.write((" "):rep(lvl or 0), name, "\n")
+ if GC[name] then
+ local causes = GC[name]
+ for i = 1, #causes do
+ local f = causes[i]
+ if not T[f] then
+ T[f] = true
+ TrackCause(f, (lvl or 0) + 1)
+ end
+
+ if f == '<GC>' then break end
+ end
+ end
+end
+
+for name, _ in pairs(GC) do
+ if name:match(P) then
+ T = {}
+ TrackCause(name)
+ end
+end
diff --git a/chromium/v8/tools/gcmole/gcmole.cc b/chromium/v8/tools/gcmole/gcmole.cc
new file mode 100644
index 00000000000..bdff18952b5
--- /dev/null
+++ b/chromium/v8/tools/gcmole/gcmole.cc
@@ -0,0 +1,1288 @@
+// 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 clang plugin used by gcmole tool. See README for more details.
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/Mangle.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <bitset>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <set>
+#include <stack>
+
+namespace {
+
+typedef std::string MangledName;
+typedef std::set<MangledName> CalleesSet;
+
+static bool GetMangledName(clang::MangleContext* ctx,
+ const clang::NamedDecl* decl,
+ MangledName* result) {
+ if (!isa<clang::CXXConstructorDecl>(decl) &&
+ !isa<clang::CXXDestructorDecl>(decl)) {
+ llvm::SmallVector<char, 512> output;
+ llvm::raw_svector_ostream out(output);
+ ctx->mangleName(decl, out);
+ *result = out.str().str();
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool InV8Namespace(const clang::NamedDecl* decl) {
+ return decl->getQualifiedNameAsString().compare(0, 4, "v8::") == 0;
+}
+
+
+static std::string EXTERNAL("EXTERNAL");
+static std::string STATE_TAG("enum v8::internal::StateTag");
+
+static bool IsExternalVMState(const clang::ValueDecl* var) {
+ const clang::EnumConstantDecl* enum_constant =
+ dyn_cast<clang::EnumConstantDecl>(var);
+ if (enum_constant != NULL && enum_constant->getNameAsString() == EXTERNAL) {
+ clang::QualType type = enum_constant->getType();
+ return (type.getAsString() == STATE_TAG);
+ }
+
+ return false;
+}
+
+
+struct Resolver {
+ explicit Resolver(clang::ASTContext& ctx)
+ : ctx_(ctx), decl_ctx_(ctx.getTranslationUnitDecl()) {
+ }
+
+ Resolver(clang::ASTContext& ctx, clang::DeclContext* decl_ctx)
+ : ctx_(ctx), decl_ctx_(decl_ctx) {
+ }
+
+ clang::DeclarationName ResolveName(const char* n) {
+ clang::IdentifierInfo* ident = &ctx_.Idents.get(n);
+ return ctx_.DeclarationNames.getIdentifier(ident);
+ }
+
+ Resolver ResolveNamespace(const char* n) {
+ return Resolver(ctx_, Resolve<clang::NamespaceDecl>(n));
+ }
+
+ template<typename T>
+ T* Resolve(const char* n) {
+ if (decl_ctx_ == NULL) return NULL;
+
+ clang::DeclContext::lookup_result result =
+ decl_ctx_->lookup(ResolveName(n));
+
+ clang::DeclContext::lookup_iterator end = result.second;
+ for (clang::DeclContext::lookup_iterator i = result.first;
+ i != end;
+ i++) {
+ if (isa<T>(*i)) return cast<T>(*i);
+ }
+
+ return NULL;
+ }
+
+ private:
+ clang::ASTContext& ctx_;
+ clang::DeclContext* decl_ctx_;
+};
+
+
+class CalleesPrinter : public clang::RecursiveASTVisitor<CalleesPrinter> {
+ public:
+ explicit CalleesPrinter(clang::MangleContext* ctx) : ctx_(ctx) {
+ }
+
+ virtual bool VisitCallExpr(clang::CallExpr* expr) {
+ const clang::FunctionDecl* callee = expr->getDirectCallee();
+ if (callee != NULL) AnalyzeFunction(callee);
+ return true;
+ }
+
+ virtual bool VisitDeclRefExpr(clang::DeclRefExpr* expr) {
+ // If function mentions EXTERNAL VMState add artificial garbage collection
+ // mark.
+ if (IsExternalVMState(expr->getDecl())) AddCallee("CollectGarbage");
+ return true;
+ }
+
+ void AnalyzeFunction(const clang::FunctionDecl* f) {
+ MangledName name;
+ if (InV8Namespace(f) && GetMangledName(ctx_, f, &name)) {
+ AddCallee(name);
+
+ const clang::FunctionDecl* body = NULL;
+ if (f->hasBody(body) && !Analyzed(name)) {
+ EnterScope(name);
+ TraverseStmt(body->getBody());
+ LeaveScope();
+ }
+ }
+ }
+
+ typedef std::map<MangledName, CalleesSet* > Callgraph;
+
+ bool Analyzed(const MangledName& name) {
+ return callgraph_[name] != NULL;
+ }
+
+ void EnterScope(const MangledName& name) {
+ CalleesSet* callees = callgraph_[name];
+
+ if (callees == NULL) {
+ callgraph_[name] = callees = new CalleesSet();
+ }
+
+ scopes_.push(callees);
+ }
+
+ void LeaveScope() {
+ scopes_.pop();
+ }
+
+ void AddCallee(const MangledName& name) {
+ if (!scopes_.empty()) scopes_.top()->insert(name);
+ }
+
+ void PrintCallGraph() {
+ for (Callgraph::const_iterator i = callgraph_.begin(), e = callgraph_.end();
+ i != e;
+ ++i) {
+ std::cout << i->first << "\n";
+
+ CalleesSet* callees = i->second;
+ for (CalleesSet::const_iterator j = callees->begin(), e = callees->end();
+ j != e;
+ ++j) {
+ std::cout << "\t" << *j << "\n";
+ }
+ }
+ }
+
+ private:
+ clang::MangleContext* ctx_;
+
+ std::stack<CalleesSet* > scopes_;
+ Callgraph callgraph_;
+};
+
+
+class FunctionDeclarationFinder
+ : public clang::ASTConsumer,
+ public clang::RecursiveASTVisitor<FunctionDeclarationFinder> {
+ public:
+ explicit FunctionDeclarationFinder(clang::Diagnostic& d,
+ clang::SourceManager& sm,
+ const std::vector<std::string>& args)
+ : d_(d), sm_(sm) { }
+
+ virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
+ mangle_context_ = clang::createItaniumMangleContext(ctx, d_);
+ callees_printer_ = new CalleesPrinter(mangle_context_);
+
+ TraverseDecl(ctx.getTranslationUnitDecl());
+
+ callees_printer_->PrintCallGraph();
+ }
+
+ virtual bool VisitFunctionDecl(clang::FunctionDecl* decl) {
+ callees_printer_->AnalyzeFunction(decl);
+ return true;
+ }
+
+ private:
+ clang::Diagnostic& d_;
+ clang::SourceManager& sm_;
+ clang::MangleContext* mangle_context_;
+
+ CalleesPrinter* callees_printer_;
+};
+
+
+static bool loaded = false;
+static CalleesSet gc_suspects;
+
+
+static void LoadGCSuspects() {
+ if (loaded) return;
+
+ std::ifstream fin("gcsuspects");
+ std::string s;
+
+ while (fin >> s) gc_suspects.insert(s);
+
+ loaded = true;
+}
+
+
+static bool KnownToCauseGC(clang::MangleContext* ctx,
+ const clang::FunctionDecl* decl) {
+ LoadGCSuspects();
+
+ if (!InV8Namespace(decl)) return false;
+
+ MangledName name;
+ if (GetMangledName(ctx, decl, &name)) {
+ return gc_suspects.find(name) != gc_suspects.end();
+ }
+
+ return false;
+}
+
+
+static const int kNoEffect = 0;
+static const int kCausesGC = 1;
+static const int kRawDef = 2;
+static const int kRawUse = 4;
+static const int kAllEffects = kCausesGC | kRawDef | kRawUse;
+
+class Environment;
+
+class ExprEffect {
+ public:
+ bool hasGC() { return (effect_ & kCausesGC) != 0; }
+ void setGC() { effect_ |= kCausesGC; }
+
+ bool hasRawDef() { return (effect_ & kRawDef) != 0; }
+ void setRawDef() { effect_ |= kRawDef; }
+
+ bool hasRawUse() { return (effect_ & kRawUse) != 0; }
+ void setRawUse() { effect_ |= kRawUse; }
+
+ static ExprEffect None() { return ExprEffect(kNoEffect, NULL); }
+ static ExprEffect NoneWithEnv(Environment* env) {
+ return ExprEffect(kNoEffect, env);
+ }
+ static ExprEffect RawUse() { return ExprEffect(kRawUse, NULL); }
+
+ static ExprEffect Merge(ExprEffect a, ExprEffect b);
+ static ExprEffect MergeSeq(ExprEffect a, ExprEffect b);
+ ExprEffect Define(const std::string& name);
+
+ Environment* env() {
+ return reinterpret_cast<Environment*>(effect_ & ~kAllEffects);
+ }
+
+ static ExprEffect GC() {
+ return ExprEffect(kCausesGC, NULL);
+ }
+
+ private:
+ ExprEffect(int effect, Environment* env)
+ : effect_((effect & kAllEffects) |
+ reinterpret_cast<intptr_t>(env)) { }
+
+ intptr_t effect_;
+};
+
+
+const std::string BAD_EXPR_MSG("Possible problem with evaluation order.");
+const std::string DEAD_VAR_MSG("Possibly dead variable.");
+
+
+class Environment {
+ public:
+ Environment() { }
+
+ static Environment Unreachable() {
+ Environment env;
+ env.live_.set();
+ return env;
+ }
+
+ static Environment Merge(const Environment& l,
+ const Environment& r) {
+ return Environment(l, r);
+ }
+
+ Environment ApplyEffect(ExprEffect effect) const {
+ Environment out = effect.hasGC() ? Environment() : Environment(*this);
+ if (effect.env() != NULL) out.live_ |= effect.env()->live_;
+ return out;
+ }
+
+ typedef std::map<std::string, int> SymbolTable;
+
+ bool IsAlive(const std::string& name) const {
+ SymbolTable::iterator code = symbol_table_.find(name);
+ if (code == symbol_table_.end()) return false;
+ return live_[code->second];
+ }
+
+ bool Equal(const Environment& env) {
+ return live_ == env.live_;
+ }
+
+ Environment Define(const std::string& name) const {
+ return Environment(*this, SymbolToCode(name));
+ }
+
+ void MDefine(const std::string& name) {
+ live_.set(SymbolToCode(name));
+ }
+
+ static int SymbolToCode(const std::string& name) {
+ SymbolTable::iterator code = symbol_table_.find(name);
+
+ if (code == symbol_table_.end()) {
+ int new_code = symbol_table_.size();
+ symbol_table_.insert(std::make_pair(name, new_code));
+ return new_code;
+ }
+
+ return code->second;
+ }
+
+ static void ClearSymbolTable() {
+ std::vector<Environment*>::iterator end = envs_.end();
+ for (std::vector<Environment*>::iterator i = envs_.begin();
+ i != end;
+ ++i) {
+ delete *i;
+ }
+ envs_.clear();
+ symbol_table_.clear();
+ }
+
+ void Print() const {
+ bool comma = false;
+ std::cout << "{";
+ SymbolTable::iterator end = symbol_table_.end();
+ for (SymbolTable::iterator i = symbol_table_.begin();
+ i != end;
+ ++i) {
+ if (live_[i->second]) {
+ if (comma) std::cout << ", ";
+ std::cout << i->first;
+ comma = true;
+ }
+ }
+ std::cout << "}";
+ }
+
+ static Environment* Allocate(const Environment& env) {
+ Environment* allocated_env = new Environment(env);
+ envs_.push_back(allocated_env);
+ return allocated_env;
+ }
+
+ private:
+ Environment(const Environment& l, const Environment& r)
+ : live_(l.live_ & r.live_) {
+ }
+
+ Environment(const Environment& l, int code)
+ : live_(l.live_) {
+ live_.set(code);
+ }
+
+ static SymbolTable symbol_table_;
+ static std::vector<Environment* > envs_;
+
+ static const int kMaxNumberOfLocals = 256;
+ std::bitset<kMaxNumberOfLocals> live_;
+
+ friend class ExprEffect;
+ friend class CallProps;
+};
+
+
+class CallProps {
+ public:
+ CallProps() : env_(NULL) { }
+
+ void SetEffect(int arg, ExprEffect in) {
+ if (in.hasGC()) gc_.set(arg);
+ if (in.hasRawDef()) raw_def_.set(arg);
+ if (in.hasRawUse()) raw_use_.set(arg);
+ if (in.env() != NULL) {
+ if (env_ == NULL) env_ = in.env();
+ env_->live_ |= in.env()->live_;
+ }
+ }
+
+ ExprEffect ComputeCumulativeEffect(bool result_is_raw) {
+ ExprEffect out = ExprEffect::NoneWithEnv(env_);
+ if (gc_.any()) out.setGC();
+ if (raw_use_.any()) out.setRawUse();
+ if (result_is_raw) out.setRawDef();
+ return out;
+ }
+
+ bool IsSafe() {
+ if (!gc_.any()) return true;
+ std::bitset<kMaxNumberOfArguments> raw = (raw_def_ | raw_use_);
+ if (!raw.any()) return true;
+ return gc_.count() == 1 && !((raw ^ gc_).any());
+ }
+
+ private:
+ static const int kMaxNumberOfArguments = 64;
+ std::bitset<kMaxNumberOfArguments> raw_def_;
+ std::bitset<kMaxNumberOfArguments> raw_use_;
+ std::bitset<kMaxNumberOfArguments> gc_;
+ Environment* env_;
+};
+
+
+Environment::SymbolTable Environment::symbol_table_;
+std::vector<Environment* > Environment::envs_;
+
+
+ExprEffect ExprEffect::Merge(ExprEffect a, ExprEffect b) {
+ Environment* a_env = a.env();
+ Environment* b_env = b.env();
+ Environment* out = NULL;
+ if (a_env != NULL && b_env != NULL) {
+ out = Environment::Allocate(*a_env);
+ out->live_ &= b_env->live_;
+ }
+ return ExprEffect(a.effect_ | b.effect_, out);
+}
+
+
+ExprEffect ExprEffect::MergeSeq(ExprEffect a, ExprEffect b) {
+ Environment* a_env = b.hasGC() ? NULL : a.env();
+ Environment* b_env = b.env();
+ Environment* out = (b_env == NULL) ? a_env : b_env;
+ if (a_env != NULL && b_env != NULL) {
+ out = Environment::Allocate(*b_env);
+ out->live_ |= a_env->live_;
+ }
+ return ExprEffect(a.effect_ | b.effect_, out);
+}
+
+
+ExprEffect ExprEffect::Define(const std::string& name) {
+ Environment* e = env();
+ if (e == NULL) {
+ e = Environment::Allocate(Environment());
+ }
+ e->MDefine(name);
+ return ExprEffect(effect_, e);
+}
+
+
+static std::string THIS ("this");
+
+
+class FunctionAnalyzer {
+ public:
+ FunctionAnalyzer(clang::MangleContext* ctx,
+ clang::DeclarationName handle_decl_name,
+ clang::CXXRecordDecl* object_decl,
+ clang::CXXRecordDecl* smi_decl,
+ clang::Diagnostic& d,
+ clang::SourceManager& sm,
+ bool dead_vars_analysis)
+ : ctx_(ctx),
+ handle_decl_name_(handle_decl_name),
+ object_decl_(object_decl),
+ smi_decl_(smi_decl),
+ d_(d),
+ sm_(sm),
+ block_(NULL),
+ dead_vars_analysis_(dead_vars_analysis) {
+ }
+
+
+ // --------------------------------------------------------------------------
+ // Expressions
+ // --------------------------------------------------------------------------
+
+ ExprEffect VisitExpr(clang::Expr* expr, const Environment& env) {
+#define VISIT(type) do { \
+ clang::type* concrete_expr = dyn_cast_or_null<clang::type>(expr); \
+ if (concrete_expr != NULL) { \
+ return Visit##type (concrete_expr, env); \
+ } \
+ } while(0);
+
+ VISIT(AbstractConditionalOperator);
+ VISIT(AddrLabelExpr);
+ VISIT(ArraySubscriptExpr);
+ VISIT(BinaryOperator);
+ VISIT(BinaryTypeTraitExpr);
+ VISIT(BlockDeclRefExpr);
+ VISIT(BlockExpr);
+ VISIT(CallExpr);
+ VISIT(CastExpr);
+ VISIT(CharacterLiteral);
+ VISIT(ChooseExpr);
+ VISIT(CompoundLiteralExpr);
+ VISIT(CXXBindTemporaryExpr);
+ VISIT(CXXBoolLiteralExpr);
+ VISIT(CXXConstructExpr);
+ VISIT(CXXDefaultArgExpr);
+ VISIT(CXXDeleteExpr);
+ VISIT(CXXDependentScopeMemberExpr);
+ VISIT(CXXNewExpr);
+ VISIT(CXXNoexceptExpr);
+ VISIT(CXXNullPtrLiteralExpr);
+ VISIT(CXXPseudoDestructorExpr);
+ VISIT(CXXScalarValueInitExpr);
+ VISIT(CXXThisExpr);
+ VISIT(CXXThrowExpr);
+ VISIT(CXXTypeidExpr);
+ VISIT(CXXUnresolvedConstructExpr);
+ VISIT(CXXUuidofExpr);
+ VISIT(DeclRefExpr);
+ VISIT(DependentScopeDeclRefExpr);
+ VISIT(DesignatedInitExpr);
+ VISIT(ExprWithCleanups);
+ VISIT(ExtVectorElementExpr);
+ VISIT(FloatingLiteral);
+ VISIT(GNUNullExpr);
+ VISIT(ImaginaryLiteral);
+ VISIT(ImplicitValueInitExpr);
+ VISIT(InitListExpr);
+ VISIT(IntegerLiteral);
+ VISIT(MemberExpr);
+ VISIT(OffsetOfExpr);
+ VISIT(OpaqueValueExpr);
+ VISIT(OverloadExpr);
+ VISIT(PackExpansionExpr);
+ VISIT(ParenExpr);
+ VISIT(ParenListExpr);
+ VISIT(PredefinedExpr);
+ VISIT(ShuffleVectorExpr);
+ VISIT(SizeOfPackExpr);
+ VISIT(StmtExpr);
+ VISIT(StringLiteral);
+ VISIT(SubstNonTypeTemplateParmPackExpr);
+ VISIT(UnaryOperator);
+ VISIT(UnaryTypeTraitExpr);
+ VISIT(VAArgExpr);
+#undef VISIT
+
+ return ExprEffect::None();
+ }
+
+#define DECL_VISIT_EXPR(type) \
+ ExprEffect Visit##type (clang::type* expr, const Environment& env)
+
+#define IGNORE_EXPR(type) \
+ ExprEffect Visit##type (clang::type* expr, const Environment& env) { \
+ return ExprEffect::None(); \
+ }
+
+ IGNORE_EXPR(AddrLabelExpr);
+ IGNORE_EXPR(BinaryTypeTraitExpr);
+ IGNORE_EXPR(BlockExpr);
+ IGNORE_EXPR(CharacterLiteral);
+ IGNORE_EXPR(ChooseExpr);
+ IGNORE_EXPR(CompoundLiteralExpr);
+ IGNORE_EXPR(CXXBoolLiteralExpr);
+ IGNORE_EXPR(CXXDependentScopeMemberExpr);
+ IGNORE_EXPR(CXXNullPtrLiteralExpr);
+ IGNORE_EXPR(CXXPseudoDestructorExpr);
+ IGNORE_EXPR(CXXScalarValueInitExpr);
+ IGNORE_EXPR(CXXNoexceptExpr);
+ IGNORE_EXPR(CXXTypeidExpr);
+ IGNORE_EXPR(CXXUnresolvedConstructExpr);
+ IGNORE_EXPR(CXXUuidofExpr);
+ IGNORE_EXPR(DependentScopeDeclRefExpr);
+ IGNORE_EXPR(DesignatedInitExpr);
+ IGNORE_EXPR(ExtVectorElementExpr);
+ IGNORE_EXPR(FloatingLiteral);
+ IGNORE_EXPR(ImaginaryLiteral);
+ IGNORE_EXPR(IntegerLiteral);
+ IGNORE_EXPR(OffsetOfExpr);
+ IGNORE_EXPR(ImplicitValueInitExpr);
+ IGNORE_EXPR(PackExpansionExpr);
+ IGNORE_EXPR(PredefinedExpr);
+ IGNORE_EXPR(ShuffleVectorExpr);
+ IGNORE_EXPR(SizeOfPackExpr);
+ IGNORE_EXPR(StmtExpr);
+ IGNORE_EXPR(StringLiteral);
+ IGNORE_EXPR(SubstNonTypeTemplateParmPackExpr);
+ IGNORE_EXPR(UnaryTypeTraitExpr);
+ IGNORE_EXPR(VAArgExpr);
+ IGNORE_EXPR(GNUNullExpr);
+ IGNORE_EXPR(OverloadExpr);
+
+ DECL_VISIT_EXPR(CXXThisExpr) {
+ return Use(expr, expr->getType(), THIS, env);
+ }
+
+ DECL_VISIT_EXPR(AbstractConditionalOperator) {
+ Environment after_cond = env.ApplyEffect(VisitExpr(expr->getCond(), env));
+ return ExprEffect::Merge(VisitExpr(expr->getTrueExpr(), after_cond),
+ VisitExpr(expr->getFalseExpr(), after_cond));
+ }
+
+ DECL_VISIT_EXPR(ArraySubscriptExpr) {
+ clang::Expr* exprs[2] = {expr->getBase(), expr->getIdx()};
+ return Par(expr, 2, exprs, env);
+ }
+
+ bool IsRawPointerVar(clang::Expr* expr, std::string* var_name) {
+ if (isa<clang::BlockDeclRefExpr>(expr)) {
+ *var_name = cast<clang::BlockDeclRefExpr>(expr)->getDecl()->
+ getNameAsString();
+ return true;
+ } else if (isa<clang::DeclRefExpr>(expr)) {
+ *var_name = cast<clang::DeclRefExpr>(expr)->getDecl()->getNameAsString();
+ return true;
+ }
+ return false;
+ }
+
+ DECL_VISIT_EXPR(BinaryOperator) {
+ clang::Expr* lhs = expr->getLHS();
+ clang::Expr* rhs = expr->getRHS();
+ clang::Expr* exprs[2] = {lhs, rhs};
+
+ switch (expr->getOpcode()) {
+ case clang::BO_Comma:
+ return Seq(expr, 2, exprs, env);
+
+ case clang::BO_LAnd:
+ case clang::BO_LOr:
+ return ExprEffect::Merge(VisitExpr(lhs, env), VisitExpr(rhs, env));
+
+ case clang::BO_Assign: {
+ std::string var_name;
+ if (IsRawPointerVar(lhs, &var_name)) {
+ return VisitExpr(rhs, env).Define(var_name);
+ }
+ return Par(expr, 2, exprs, env);
+ }
+
+ default:
+ return Par(expr, 2, exprs, env);
+ }
+ }
+
+ DECL_VISIT_EXPR(CXXBindTemporaryExpr) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(CXXConstructExpr) {
+ return VisitArguments<>(expr, env);
+ }
+
+ DECL_VISIT_EXPR(CXXDefaultArgExpr) {
+ return VisitExpr(expr->getExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(CXXDeleteExpr) {
+ return VisitExpr(expr->getArgument(), env);
+ }
+
+ DECL_VISIT_EXPR(CXXNewExpr) {
+ return Par(expr,
+ expr->getNumConstructorArgs(),
+ expr->getConstructorArgs(),
+ env);
+ }
+
+ DECL_VISIT_EXPR(ExprWithCleanups) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(CXXThrowExpr) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(InitListExpr) {
+ return Seq(expr, expr->getNumInits(), expr->getInits(), env);
+ }
+
+ DECL_VISIT_EXPR(MemberExpr) {
+ return VisitExpr(expr->getBase(), env);
+ }
+
+ DECL_VISIT_EXPR(OpaqueValueExpr) {
+ return VisitExpr(expr->getSourceExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(ParenExpr) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(ParenListExpr) {
+ return Par(expr, expr->getNumExprs(), expr->getExprs(), env);
+ }
+
+ DECL_VISIT_EXPR(UnaryOperator) {
+ // TODO We are treating all expressions that look like &raw_pointer_var
+ // as definitions of raw_pointer_var. This should be changed to
+ // recognize less generic pattern:
+ //
+ // if (maybe_object->ToObject(&obj)) return maybe_object;
+ //
+ if (expr->getOpcode() == clang::UO_AddrOf) {
+ std::string var_name;
+ if (IsRawPointerVar(expr->getSubExpr(), &var_name)) {
+ return ExprEffect::None().Define(var_name);
+ }
+ }
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(CastExpr) {
+ return VisitExpr(expr->getSubExpr(), env);
+ }
+
+ DECL_VISIT_EXPR(DeclRefExpr) {
+ return Use(expr, expr->getDecl(), env);
+ }
+
+ DECL_VISIT_EXPR(BlockDeclRefExpr) {
+ return Use(expr, expr->getDecl(), env);
+ }
+
+ ExprEffect Par(clang::Expr* parent,
+ int n,
+ clang::Expr** exprs,
+ const Environment& env) {
+ CallProps props;
+
+ for (int i = 0; i < n; ++i) {
+ props.SetEffect(i, VisitExpr(exprs[i], env));
+ }
+
+ if (!props.IsSafe()) ReportUnsafe(parent, BAD_EXPR_MSG);
+
+ return props.ComputeCumulativeEffect(IsRawPointerType(parent->getType()));
+ }
+
+ ExprEffect Seq(clang::Stmt* parent,
+ int n,
+ clang::Expr** exprs,
+ const Environment& env) {
+ ExprEffect out = ExprEffect::None();
+ Environment out_env = env;
+ for (int i = 0; i < n; ++i) {
+ out = ExprEffect::MergeSeq(out, VisitExpr(exprs[i], out_env));
+ out_env = out_env.ApplyEffect(out);
+ }
+ return out;
+ }
+
+ ExprEffect Use(const clang::Expr* parent,
+ const clang::QualType& var_type,
+ const std::string& var_name,
+ const Environment& env) {
+ if (IsRawPointerType(var_type)) {
+ if (!env.IsAlive(var_name) && dead_vars_analysis_) {
+ ReportUnsafe(parent, DEAD_VAR_MSG);
+ }
+ return ExprEffect::RawUse();
+ }
+ return ExprEffect::None();
+ }
+
+ ExprEffect Use(const clang::Expr* parent,
+ const clang::ValueDecl* var,
+ const Environment& env) {
+ if (IsExternalVMState(var)) {
+ return ExprEffect::GC();
+ }
+ return Use(parent, var->getType(), var->getNameAsString(), env);
+ }
+
+
+ template<typename ExprType>
+ ExprEffect VisitArguments(ExprType* call, const Environment& env) {
+ CallProps props;
+ VisitArguments<>(call, &props, env);
+ if (!props.IsSafe()) ReportUnsafe(call, BAD_EXPR_MSG);
+ return props.ComputeCumulativeEffect(IsRawPointerType(call->getType()));
+ }
+
+ template<typename ExprType>
+ void VisitArguments(ExprType* call,
+ CallProps* props,
+ const Environment& env) {
+ for (unsigned arg = 0; arg < call->getNumArgs(); arg++) {
+ props->SetEffect(arg + 1, VisitExpr(call->getArg(arg), env));
+ }
+ }
+
+
+ ExprEffect VisitCallExpr(clang::CallExpr* call,
+ const Environment& env) {
+ CallProps props;
+
+ clang::CXXMemberCallExpr* memcall =
+ dyn_cast_or_null<clang::CXXMemberCallExpr>(call);
+ if (memcall != NULL) {
+ clang::Expr* receiver = memcall->getImplicitObjectArgument();
+ props.SetEffect(0, VisitExpr(receiver, env));
+ }
+
+ VisitArguments<>(call, &props, env);
+
+ if (!props.IsSafe()) ReportUnsafe(call, BAD_EXPR_MSG);
+
+ ExprEffect out =
+ props.ComputeCumulativeEffect(IsRawPointerType(call->getType()));
+
+ clang::FunctionDecl* callee = call->getDirectCallee();
+ if ((callee != NULL) && KnownToCauseGC(ctx_, callee)) {
+ out.setGC();
+ }
+
+ return out;
+ }
+
+ // --------------------------------------------------------------------------
+ // Statements
+ // --------------------------------------------------------------------------
+
+ Environment VisitStmt(clang::Stmt* stmt, const Environment& env) {
+#define VISIT(type) do { \
+ clang::type* concrete_stmt = dyn_cast_or_null<clang::type>(stmt); \
+ if (concrete_stmt != NULL) { \
+ return Visit##type (concrete_stmt, env); \
+ } \
+ } while(0);
+
+ if (clang::Expr* expr = dyn_cast_or_null<clang::Expr>(stmt)) {
+ return env.ApplyEffect(VisitExpr(expr, env));
+ }
+
+ VISIT(AsmStmt);
+ VISIT(BreakStmt);
+ VISIT(CompoundStmt);
+ VISIT(ContinueStmt);
+ VISIT(CXXCatchStmt);
+ VISIT(CXXTryStmt);
+ VISIT(DeclStmt);
+ VISIT(DoStmt);
+ VISIT(ForStmt);
+ VISIT(GotoStmt);
+ VISIT(IfStmt);
+ VISIT(IndirectGotoStmt);
+ VISIT(LabelStmt);
+ VISIT(NullStmt);
+ VISIT(ReturnStmt);
+ VISIT(CaseStmt);
+ VISIT(DefaultStmt);
+ VISIT(SwitchStmt);
+ VISIT(WhileStmt);
+#undef VISIT
+
+ return env;
+ }
+
+#define DECL_VISIT_STMT(type) \
+ Environment Visit##type (clang::type* stmt, const Environment& env)
+
+#define IGNORE_STMT(type) \
+ Environment Visit##type (clang::type* stmt, const Environment& env) { \
+ return env; \
+ }
+
+ IGNORE_STMT(IndirectGotoStmt);
+ IGNORE_STMT(NullStmt);
+ IGNORE_STMT(AsmStmt);
+
+ // We are ignoring control flow for simplicity.
+ IGNORE_STMT(GotoStmt);
+ IGNORE_STMT(LabelStmt);
+
+ // We are ignoring try/catch because V8 does not use them.
+ IGNORE_STMT(CXXCatchStmt);
+ IGNORE_STMT(CXXTryStmt);
+
+ class Block {
+ public:
+ Block(const Environment& in,
+ FunctionAnalyzer* owner)
+ : in_(in),
+ out_(Environment::Unreachable()),
+ changed_(false),
+ owner_(owner) {
+ parent_ = owner_->EnterBlock(this);
+ }
+
+ ~Block() {
+ owner_->LeaveBlock(parent_);
+ }
+
+ void MergeIn(const Environment& env) {
+ Environment old_in = in_;
+ in_ = Environment::Merge(in_, env);
+ changed_ = !old_in.Equal(in_);
+ }
+
+ bool changed() {
+ if (changed_) {
+ changed_ = false;
+ return true;
+ }
+ return false;
+ }
+
+ const Environment& in() {
+ return in_;
+ }
+
+ const Environment& out() {
+ return out_;
+ }
+
+ void MergeOut(const Environment& env) {
+ out_ = Environment::Merge(out_, env);
+ }
+
+ void Seq(clang::Stmt* a, clang::Stmt* b, clang::Stmt* c) {
+ Environment a_out = owner_->VisitStmt(a, in());
+ Environment b_out = owner_->VisitStmt(b, a_out);
+ Environment c_out = owner_->VisitStmt(c, b_out);
+ MergeOut(c_out);
+ }
+
+ void Seq(clang::Stmt* a, clang::Stmt* b) {
+ Environment a_out = owner_->VisitStmt(a, in());
+ Environment b_out = owner_->VisitStmt(b, a_out);
+ MergeOut(b_out);
+ }
+
+ void Loop(clang::Stmt* a, clang::Stmt* b, clang::Stmt* c) {
+ Seq(a, b, c);
+ MergeIn(out());
+ }
+
+ void Loop(clang::Stmt* a, clang::Stmt* b) {
+ Seq(a, b);
+ MergeIn(out());
+ }
+
+
+ private:
+ Environment in_;
+ Environment out_;
+ bool changed_;
+ FunctionAnalyzer* owner_;
+ Block* parent_;
+ };
+
+
+ DECL_VISIT_STMT(BreakStmt) {
+ block_->MergeOut(env);
+ return Environment::Unreachable();
+ }
+
+ DECL_VISIT_STMT(ContinueStmt) {
+ block_->MergeIn(env);
+ return Environment::Unreachable();
+ }
+
+ DECL_VISIT_STMT(CompoundStmt) {
+ Environment out = env;
+ clang::CompoundStmt::body_iterator end = stmt->body_end();
+ for (clang::CompoundStmt::body_iterator s = stmt->body_begin();
+ s != end;
+ ++s) {
+ out = VisitStmt(*s, out);
+ }
+ return out;
+ }
+
+ DECL_VISIT_STMT(WhileStmt) {
+ Block block (env, this);
+ do {
+ block.Loop(stmt->getCond(), stmt->getBody());
+ } while (block.changed());
+ return block.out();
+ }
+
+ DECL_VISIT_STMT(DoStmt) {
+ Block block (env, this);
+ do {
+ block.Loop(stmt->getBody(), stmt->getCond());
+ } while (block.changed());
+ return block.out();
+ }
+
+ DECL_VISIT_STMT(ForStmt) {
+ Block block (VisitStmt(stmt->getInit(), env), this);
+ do {
+ block.Loop(stmt->getCond(),
+ stmt->getBody(),
+ stmt->getInc());
+ } while (block.changed());
+ return block.out();
+ }
+
+ DECL_VISIT_STMT(IfStmt) {
+ Environment cond_out = VisitStmt(stmt->getCond(), env);
+ Environment then_out = VisitStmt(stmt->getThen(), cond_out);
+ Environment else_out = VisitStmt(stmt->getElse(), cond_out);
+ return Environment::Merge(then_out, else_out);
+ }
+
+ DECL_VISIT_STMT(SwitchStmt) {
+ Block block (env, this);
+ block.Seq(stmt->getCond(), stmt->getBody());
+ return block.out();
+ }
+
+ DECL_VISIT_STMT(CaseStmt) {
+ Environment in = Environment::Merge(env, block_->in());
+ Environment after_lhs = VisitStmt(stmt->getLHS(), in);
+ return VisitStmt(stmt->getSubStmt(), after_lhs);
+ }
+
+ DECL_VISIT_STMT(DefaultStmt) {
+ Environment in = Environment::Merge(env, block_->in());
+ return VisitStmt(stmt->getSubStmt(), in);
+ }
+
+ DECL_VISIT_STMT(ReturnStmt) {
+ VisitExpr(stmt->getRetValue(), env);
+ return Environment::Unreachable();
+ }
+
+ const clang::TagType* ToTagType(const clang::Type* t) {
+ if (t == NULL) {
+ return NULL;
+ } else if (isa<clang::TagType>(t)) {
+ return cast<clang::TagType>(t);
+ } else if (isa<clang::SubstTemplateTypeParmType>(t)) {
+ return ToTagType(cast<clang::SubstTemplateTypeParmType>(t)->
+ getReplacementType().getTypePtr());
+ } else {
+ return NULL;
+ }
+ }
+
+ bool IsDerivedFrom(clang::CXXRecordDecl* record,
+ clang::CXXRecordDecl* base) {
+ return (record == base) || record->isDerivedFrom(base);
+ }
+
+ bool IsRawPointerType(clang::QualType qtype) {
+ const clang::PointerType* type =
+ dyn_cast_or_null<clang::PointerType>(qtype.getTypePtrOrNull());
+ if (type == NULL) return false;
+
+ const clang::TagType* pointee =
+ ToTagType(type->getPointeeType().getTypePtr());
+ if (pointee == NULL) return false;
+
+ clang::CXXRecordDecl* record =
+ dyn_cast_or_null<clang::CXXRecordDecl>(pointee->getDecl());
+ if (record == NULL) return false;
+
+ if (!InV8Namespace(record)) return false;
+
+ if (!record->hasDefinition()) return false;
+
+ record = record->getDefinition();
+
+ return IsDerivedFrom(record, object_decl_) &&
+ !IsDerivedFrom(record, smi_decl_);
+ }
+
+ Environment VisitDecl(clang::Decl* decl, const Environment& env) {
+ if (clang::VarDecl* var = dyn_cast<clang::VarDecl>(decl)) {
+ Environment out = var->hasInit() ? VisitStmt(var->getInit(), env) : env;
+
+ if (IsRawPointerType(var->getType())) {
+ out = out.Define(var->getNameAsString());
+ }
+
+ return out;
+ }
+ // TODO: handle other declarations?
+ return env;
+ }
+
+ DECL_VISIT_STMT(DeclStmt) {
+ Environment out = env;
+ clang::DeclStmt::decl_iterator end = stmt->decl_end();
+ for (clang::DeclStmt::decl_iterator decl = stmt->decl_begin();
+ decl != end;
+ ++decl) {
+ out = VisitDecl(*decl, out);
+ }
+ return out;
+ }
+
+
+ void DefineParameters(const clang::FunctionDecl* f,
+ Environment* env) {
+ env->MDefine(THIS);
+ clang::FunctionDecl::param_const_iterator end = f->param_end();
+ for (clang::FunctionDecl::param_const_iterator p = f->param_begin();
+ p != end;
+ ++p) {
+ env->MDefine((*p)->getNameAsString());
+ }
+ }
+
+
+ void AnalyzeFunction(const clang::FunctionDecl* f) {
+ const clang::FunctionDecl* body = NULL;
+ if (f->hasBody(body)) {
+ Environment env;
+ DefineParameters(body, &env);
+ VisitStmt(body->getBody(), env);
+ Environment::ClearSymbolTable();
+ }
+ }
+
+ Block* EnterBlock(Block* block) {
+ Block* parent = block_;
+ block_ = block;
+ return parent;
+ }
+
+ void LeaveBlock(Block* block) {
+ block_ = block;
+ }
+
+ private:
+ void ReportUnsafe(const clang::Expr* expr, const std::string& msg) {
+ d_.Report(clang::FullSourceLoc(expr->getExprLoc(), sm_),
+ d_.getCustomDiagID(clang::Diagnostic::Warning, msg));
+ }
+
+
+ clang::MangleContext* ctx_;
+ clang::DeclarationName handle_decl_name_;
+ clang::CXXRecordDecl* object_decl_;
+ clang::CXXRecordDecl* smi_decl_;
+
+ clang::Diagnostic& d_;
+ clang::SourceManager& sm_;
+
+ Block* block_;
+ bool dead_vars_analysis_;
+};
+
+
+class ProblemsFinder : public clang::ASTConsumer,
+ public clang::RecursiveASTVisitor<ProblemsFinder> {
+ public:
+ ProblemsFinder(clang::Diagnostic& d,
+ clang::SourceManager& sm,
+ const std::vector<std::string>& args)
+ : d_(d), sm_(sm), dead_vars_analysis_(false) {
+ for (unsigned i = 0; i < args.size(); ++i) {
+ if (args[i] == "--dead-vars") {
+ dead_vars_analysis_ = true;
+ }
+ }
+ }
+
+ virtual void HandleTranslationUnit(clang::ASTContext &ctx) {
+ Resolver r(ctx);
+
+ clang::CXXRecordDecl* object_decl =
+ r.ResolveNamespace("v8").ResolveNamespace("internal").
+ Resolve<clang::CXXRecordDecl>("Object");
+
+ clang::CXXRecordDecl* smi_decl =
+ r.ResolveNamespace("v8").ResolveNamespace("internal").
+ Resolve<clang::CXXRecordDecl>("Smi");
+
+ if (object_decl != NULL) object_decl = object_decl->getDefinition();
+
+ if (smi_decl != NULL) smi_decl = smi_decl->getDefinition();
+
+ if (object_decl != NULL && smi_decl != NULL) {
+ function_analyzer_ =
+ new FunctionAnalyzer(clang::createItaniumMangleContext(ctx, d_),
+ r.ResolveName("Handle"),
+ object_decl,
+ smi_decl,
+ d_,
+ sm_,
+ dead_vars_analysis_);
+ TraverseDecl(ctx.getTranslationUnitDecl());
+ } else {
+ if (object_decl == NULL) {
+ llvm::errs() << "Failed to resolve v8::internal::Object\n";
+ }
+ if (smi_decl == NULL) {
+ llvm::errs() << "Failed to resolve v8::internal::Smi\n";
+ }
+ }
+ }
+
+ virtual bool VisitFunctionDecl(clang::FunctionDecl* decl) {
+ function_analyzer_->AnalyzeFunction(decl);
+ return true;
+ }
+
+ private:
+ clang::Diagnostic& d_;
+ clang::SourceManager& sm_;
+ bool dead_vars_analysis_;
+
+ FunctionAnalyzer* function_analyzer_;
+};
+
+
+template<typename ConsumerType>
+class Action : public clang::PluginASTAction {
+ protected:
+ clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &CI,
+ llvm::StringRef InFile) {
+ return new ConsumerType(CI.getDiagnostics(), CI.getSourceManager(), args_);
+ }
+
+ bool ParseArgs(const clang::CompilerInstance &CI,
+ const std::vector<std::string>& args) {
+ args_ = args;
+ return true;
+ }
+
+ void PrintHelp(llvm::raw_ostream& ros) {
+ }
+ private:
+ std::vector<std::string> args_;
+};
+
+
+}
+
+static clang::FrontendPluginRegistry::Add<Action<ProblemsFinder> >
+FindProblems("find-problems", "Find GC-unsafe places.");
+
+static clang::FrontendPluginRegistry::Add<
+ Action<FunctionDeclarationFinder> >
+DumpCallees("dump-callees", "Dump callees for each function.");
diff --git a/chromium/v8/tools/gcmole/gcmole.lua b/chromium/v8/tools/gcmole/gcmole.lua
new file mode 100644
index 00000000000..aa9324756ac
--- /dev/null
+++ b/chromium/v8/tools/gcmole/gcmole.lua
@@ -0,0 +1,384 @@
+-- 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 main driver for gcmole tool. See README for more details.
+-- Usage: CLANG_BIN=clang-bin-dir lua tools/gcmole/gcmole.lua [arm|ia32|x64]
+
+local DIR = arg[0]:match("^(.+)/[^/]+$")
+
+local FLAGS = {
+ -- Do not build gcsuspects file and reuse previously generated one.
+ reuse_gcsuspects = false;
+
+ -- Print commands to console before executing them.
+ verbose = false;
+
+ -- Perform dead variable analysis (generates many false positives).
+ -- TODO add some sort of whiteliste to filter out false positives.
+ dead_vars = false;
+
+ -- When building gcsuspects whitelist certain functions as if they
+ -- can be causing GC. Currently used to reduce number of false
+ -- positives in dead variables analysis. See TODO for WHITELIST
+ -- below.
+ whitelist = true;
+}
+local ARGS = {}
+
+for i = 1, #arg do
+ local flag = arg[i]:match "^%-%-([%w_-]+)$"
+ if flag then
+ local no, real_flag = flag:match "^(no)([%w_-]+)$"
+ if real_flag then flag = real_flag end
+
+ flag = flag:gsub("%-", "_")
+ if FLAGS[flag] ~= nil then
+ FLAGS[flag] = (no ~= "no")
+ else
+ error("Unknown flag: " .. flag)
+ end
+ else
+ table.insert(ARGS, arg[i])
+ end
+end
+
+local ARCHS = ARGS[1] and { ARGS[1] } or { 'ia32', 'arm', 'x64' }
+
+local io = require "io"
+local os = require "os"
+
+function log(...)
+ io.stderr:write(string.format(...))
+ io.stderr:write "\n"
+end
+
+-------------------------------------------------------------------------------
+-- Clang invocation
+
+local CLANG_BIN = os.getenv "CLANG_BIN"
+local CLANG_PLUGINS = os.getenv "CLANG_PLUGINS"
+
+if not CLANG_BIN or CLANG_BIN == "" then
+ error "CLANG_BIN not set"
+end
+
+if not CLANG_PLUGINS or CLANG_PLUGINS == "" then
+ CLANG_PLUGINS = DIR
+end
+
+local function MakeClangCommandLine(plugin, plugin_args, triple, arch_define)
+ if plugin_args then
+ for i = 1, #plugin_args do
+ plugin_args[i] = "-plugin-arg-" .. plugin .. " " .. plugin_args[i]
+ end
+ plugin_args = " " .. table.concat(plugin_args, " ")
+ end
+ return CLANG_BIN .. "/clang -cc1 -load " .. CLANG_PLUGINS .. "/libgcmole.so"
+ .. " -plugin " .. plugin
+ .. (plugin_args or "")
+ .. " -triple " .. triple
+ .. " -D" .. arch_define
+ .. " -DENABLE_DEBUGGER_SUPPORT"
+ .. " -DV8_I18N_SUPPORT"
+ .. " -Isrc"
+ .. " -Ithird_party/icu/source/common"
+ .. " -Ithird_party/icu/source/i18n"
+end
+
+function InvokeClangPluginForEachFile(filenames, cfg, func)
+ local cmd_line = MakeClangCommandLine(cfg.plugin,
+ cfg.plugin_args,
+ cfg.triple,
+ cfg.arch_define)
+ for _, filename in ipairs(filenames) do
+ log("-- %s", filename)
+ local action = cmd_line .. " src/" .. filename .. " 2>&1"
+ if FLAGS.verbose then print('popen ', action) end
+ local pipe = io.popen(action)
+ func(filename, pipe:lines())
+ local success = pipe:close()
+ if not success then error("Failed to run: " .. action) end
+ end
+end
+
+-------------------------------------------------------------------------------
+-- GYP file parsing
+
+local function ParseGYPFile()
+ local f = assert(io.open("tools/gyp/v8.gyp"), "failed to open GYP file")
+ local gyp = f:read('*a')
+ f:close()
+
+ local result = {}
+
+ for condition, sources in
+ gyp:gmatch "'sources': %[.-### gcmole%((.-)%) ###(.-)%]" do
+ local files = {}
+ for file in sources:gmatch "'%.%./%.%./src/([^']-%.cc)'" do
+ table.insert(files, file)
+ end
+ result[condition] = files
+ end
+
+ return result
+end
+
+local function EvaluateCondition(cond, props)
+ if cond == 'all' then return true end
+
+ local p, v = cond:match "(%w+):(%w+)"
+
+ assert(p and v, "failed to parse condition: " .. cond)
+ assert(props[p] ~= nil, "undefined configuration property: " .. p)
+
+ return props[p] == v
+end
+
+local function BuildFileList(sources, props)
+ local list = {}
+ for condition, files in pairs(sources) do
+ if EvaluateCondition(condition, props) then
+ for i = 1, #files do table.insert(list, files[i]) end
+ end
+ end
+ return list
+end
+
+local sources = ParseGYPFile()
+
+local function FilesForArch(arch)
+ return BuildFileList(sources, { os = 'linux',
+ arch = arch,
+ mode = 'debug',
+ simulator = ''})
+end
+
+local mtConfig = {}
+
+mtConfig.__index = mtConfig
+
+local function config (t) return setmetatable(t, mtConfig) end
+
+function mtConfig:extend(t)
+ local e = {}
+ for k, v in pairs(self) do e[k] = v end
+ for k, v in pairs(t) do e[k] = v end
+ return config(e)
+end
+
+local ARCHITECTURES = {
+ ia32 = config { triple = "i586-unknown-linux",
+ arch_define = "V8_TARGET_ARCH_IA32" },
+ arm = config { triple = "i586-unknown-linux",
+ arch_define = "V8_TARGET_ARCH_ARM" },
+ x64 = config { triple = "x86_64-unknown-linux",
+ arch_define = "V8_TARGET_ARCH_X64" }
+}
+
+-------------------------------------------------------------------------------
+-- GCSuspects Generation
+
+local gc, gc_caused, funcs
+
+local WHITELIST = {
+ -- The following functions call CEntryStub which is always present.
+ "MacroAssembler.*CallExternalReference",
+ "MacroAssembler.*CallRuntime",
+ "CompileCallLoadPropertyWithInterceptor",
+ "CallIC.*GenerateMiss",
+
+ -- DirectCEntryStub is a special stub used on ARM.
+ -- It is pinned and always present.
+ "DirectCEntryStub.*GenerateCall",
+
+ -- TODO GCMole currently is sensitive enough to understand that certain
+ -- functions only cause GC and return Failure simulataneously.
+ -- Callsites of such functions are safe as long as they are properly
+ -- check return value and propagate the Failure to the caller.
+ -- It should be possible to extend GCMole to understand this.
+ "Heap.*AllocateFunctionPrototype",
+
+ -- Ignore all StateTag methods.
+ "StateTag",
+
+ -- Ignore printing of elements transition.
+ "PrintElementsTransition"
+};
+
+local function AddCause(name, cause)
+ local t = gc_caused[name]
+ if not t then
+ t = {}
+ gc_caused[name] = t
+ end
+ table.insert(t, cause)
+end
+
+local function resolve(name)
+ local f = funcs[name]
+
+ if not f then
+ f = {}
+ funcs[name] = f
+
+ if name:match "Collect.*Garbage" then
+ gc[name] = true
+ AddCause(name, "<GC>")
+ end
+
+ if FLAGS.whitelist then
+ for i = 1, #WHITELIST do
+ if name:match(WHITELIST[i]) then
+ gc[name] = false
+ end
+ end
+ end
+ end
+
+ return f
+end
+
+local function parse (filename, lines)
+ local scope
+
+ for funcname in lines do
+ if funcname:sub(1, 1) ~= '\t' then
+ resolve(funcname)
+ scope = funcname
+ else
+ local name = funcname:sub(2)
+ resolve(name)[scope] = true
+ end
+ end
+end
+
+local function propagate ()
+ log "** Propagating GC information"
+
+ local function mark(from, callers)
+ for caller, _ in pairs(callers) do
+ if gc[caller] == nil then
+ gc[caller] = true
+ mark(caller, funcs[caller])
+ end
+ AddCause(caller, from)
+ end
+ end
+
+ for funcname, callers in pairs(funcs) do
+ if gc[funcname] then mark(funcname, callers) end
+ end
+end
+
+local function GenerateGCSuspects(arch, files, cfg)
+ -- Reset the global state.
+ gc, gc_caused, funcs = {}, {}, {}
+
+ log ("** Building GC Suspects for %s", arch)
+ InvokeClangPluginForEachFile (files,
+ cfg:extend { plugin = "dump-callees" },
+ parse)
+
+ propagate()
+
+ local out = assert(io.open("gcsuspects", "w"))
+ for name, value in pairs(gc) do if value then out:write (name, '\n') end end
+ out:close()
+
+ local out = assert(io.open("gccauses", "w"))
+ out:write "GC = {"
+ for name, causes in pairs(gc_caused) do
+ out:write("['", name, "'] = {")
+ for i = 1, #causes do out:write ("'", causes[i], "';") end
+ out:write("};\n")
+ end
+ out:write "}"
+ out:close()
+
+ log ("** GCSuspects generated for %s", arch)
+end
+
+--------------------------------------------------------------------------------
+-- Analysis
+
+local function CheckCorrectnessForArch(arch)
+ local files = FilesForArch(arch)
+ local cfg = ARCHITECTURES[arch]
+
+ if not FLAGS.reuse_gcsuspects then
+ GenerateGCSuspects(arch, files, cfg)
+ end
+
+ local processed_files = 0
+ local errors_found = false
+ local function SearchForErrors(filename, lines)
+ processed_files = processed_files + 1
+ for l in lines do
+ errors_found = errors_found or
+ l:match "^[^:]+:%d+:%d+:" or
+ l:match "error" or
+ l:match "warning"
+ print(l)
+ end
+ end
+
+ log("** Searching for evaluation order problems%s for %s",
+ FLAGS.dead_vars and " and dead variables" or "",
+ arch)
+ local plugin_args
+ if FLAGS.dead_vars then plugin_args = { "--dead-vars" } end
+ InvokeClangPluginForEachFile(files,
+ cfg:extend { plugin = "find-problems",
+ plugin_args = plugin_args },
+ SearchForErrors)
+ log("** Done processing %d files. %s",
+ processed_files,
+ errors_found and "Errors found" or "No errors found")
+
+ return errors_found
+end
+
+local function SafeCheckCorrectnessForArch(arch)
+ local status, errors = pcall(CheckCorrectnessForArch, arch)
+ if not status then
+ print(string.format("There was an error: %s", errors))
+ errors = true
+ end
+ return errors
+end
+
+local errors = false
+
+for _, arch in ipairs(ARCHS) do
+ if not ARCHITECTURES[arch] then
+ error ("Unknown arch: " .. arch)
+ end
+
+ errors = SafeCheckCorrectnessForArch(arch, report) or errors
+end
+
+os.exit(errors and 1 or 0)
diff --git a/chromium/v8/tools/gdb-v8-support.py b/chromium/v8/tools/gdb-v8-support.py
new file mode 100644
index 00000000000..9cc046c7a50
--- /dev/null
+++ b/chromium/v8/tools/gdb-v8-support.py
@@ -0,0 +1,154 @@
+# 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.
+
+
+kSmiTag = 0
+kSmiTagSize = 1
+kSmiTagMask = (1 << kSmiTagSize) - 1
+
+
+kHeapObjectTag = 1
+kHeapObjectTagSize = 2
+kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1
+
+
+kFailureTag = 3
+kFailureTagSize = 2
+kFailureTagMask = (1 << kFailureTagSize) - 1
+
+
+kSmiShiftSize32 = 0
+kSmiValueSize32 = 31
+kSmiShiftBits32 = kSmiTagSize + kSmiShiftSize32
+
+
+kSmiShiftSize64 = 31
+kSmiValueSize64 = 32
+kSmiShiftBits64 = kSmiTagSize + kSmiShiftSize64
+
+
+kAllBits = 0xFFFFFFFF
+kTopBit32 = 0x80000000
+kTopBit64 = 0x8000000000000000
+
+
+t_u32 = gdb.lookup_type('unsigned int')
+t_u64 = gdb.lookup_type('unsigned long long')
+
+
+def has_smi_tag(v):
+ return v & kSmiTagMask == kSmiTag
+
+
+def has_failure_tag(v):
+ return v & kFailureTagMask == kFailureTag
+
+
+def has_heap_object_tag(v):
+ return v & kHeapObjectTagMask == kHeapObjectTag
+
+
+def raw_heap_object(v):
+ return v - kHeapObjectTag
+
+
+def smi_to_int_32(v):
+ v = v & kAllBits
+ if (v & kTopBit32) == kTopBit32:
+ return ((v & kAllBits) >> kSmiShiftBits32) - 2147483648
+ else:
+ return (v & kAllBits) >> kSmiShiftBits32
+
+
+def smi_to_int_64(v):
+ return (v >> kSmiShiftBits64)
+
+
+def decode_v8_value(v, bitness):
+ base_str = 'v8[%x]' % v
+ if has_smi_tag(v):
+ if bitness == 32:
+ return base_str + (" SMI(%d)" % smi_to_int_32(v))
+ else:
+ return base_str + (" SMI(%d)" % smi_to_int_64(v))
+ elif has_failure_tag(v):
+ return base_str + " (failure)"
+ elif has_heap_object_tag(v):
+ return base_str + (" H(0x%x)" % raw_heap_object(v))
+ else:
+ return base_str
+
+
+class V8ValuePrinter(object):
+ "Print a v8value."
+ def __init__(self, val):
+ self.val = val
+ def to_string(self):
+ if self.val.type.sizeof == 4:
+ v_u32 = self.val.cast(t_u32)
+ return decode_v8_value(int(v_u32), 32)
+ elif self.val.type.sizeof == 8:
+ v_u64 = self.val.cast(t_u64)
+ return decode_v8_value(int(v_u64), 64)
+ else:
+ return 'v8value?'
+ def display_hint(self):
+ return 'v8value'
+
+
+def v8_pretty_printers(val):
+ lookup_tag = val.type.tag
+ if lookup_tag == None:
+ return None
+ elif lookup_tag == 'v8value':
+ return V8ValuePrinter(val)
+ return None
+gdb.pretty_printers.append(v8_pretty_printers)
+
+
+def v8_to_int(v):
+ if v.type.sizeof == 4:
+ return int(v.cast(t_u32))
+ elif v.type.sizeof == 8:
+ return int(v.cast(t_u64))
+ else:
+ return '?'
+
+
+def v8_get_value(vstring):
+ v = gdb.parse_and_eval(vstring)
+ return v8_to_int(v)
+
+
+class V8PrintObject (gdb.Command):
+ """Prints a v8 object."""
+ def __init__ (self):
+ super (V8PrintObject, self).__init__ ("v8print", gdb.COMMAND_DATA)
+ def invoke (self, arg, from_tty):
+ v = v8_get_value(arg)
+ gdb.execute('call __gdb_print_v8_object(%d)' % v)
+V8PrintObject()
diff --git a/chromium/v8/tools/gen-postmortem-metadata.py b/chromium/v8/tools/gen-postmortem-metadata.py
new file mode 100644
index 00000000000..0acb658c53b
--- /dev/null
+++ b/chromium/v8/tools/gen-postmortem-metadata.py
@@ -0,0 +1,479 @@
+#!/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': 'kOneByteStringTag' },
+
+ { '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': 'SmiShiftSize', 'value': 'kSmiShiftSize' },
+ { 'name': 'PointerSizeLog2', 'value': 'kPointerSizeLog2' },
+
+ { 'name': 'prop_idx_first',
+ 'value': 'DescriptorArray::kFirstIndex' },
+ { 'name': 'prop_type_field',
+ 'value': 'FIELD' },
+ { 'name': 'prop_type_first_phantom',
+ 'value': '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, 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',
+ 'SeqOneByteString, chars, char, kHeaderSize',
+ 'SharedFunctionInfo, code, Code, kCodeOffset',
+ 'Code, instruction_start, uintptr_t, kHeaderSize',
+ 'Code, instruction_size, int, kInstructionSizeOffset',
+];
+
+#
+# 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',
+ 'SeqOneByteString', '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$',
+ 'SeqOneByteString', 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/chromium/v8/tools/generate-ten-powers.scm b/chromium/v8/tools/generate-ten-powers.scm
new file mode 100644
index 00000000000..eaeb7f4bfdc
--- /dev/null
+++ b/chromium/v8/tools/generate-ten-powers.scm
@@ -0,0 +1,286 @@
+;; 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.
+
+;; This is a Scheme script for the Bigloo compiler. Bigloo must be compiled with
+;; support for bignums. The compilation of the script can be done as follows:
+;; bigloo -static-bigloo -o generate-ten-powers generate-ten-powers.scm
+;;
+;; Generate approximations of 10^k.
+
+(module gen-ten-powers
+ (static (class Cached-Fast
+ v::bignum
+ e::bint
+ exact?::bool))
+ (main my-main))
+
+
+;;----------------bignum shifts -----------------------------------------------
+(define (bit-lshbx::bignum x::bignum by::bint)
+ (if (<fx by 0)
+ #z0
+ (*bx x (exptbx #z2 (fixnum->bignum by)))))
+
+(define (bit-rshbx::bignum x::bignum by::bint)
+ (if (<fx by 0)
+ #z0
+ (/bx x (exptbx #z2 (fixnum->bignum by)))))
+
+;;----------------the actual power generation -------------------------------
+
+;; e should be an indication. it might be too small.
+(define (round-n-cut n e nb-bits)
+ (define max-container (- (bit-lshbx #z1 nb-bits) 1))
+ (define (round n)
+ (case *round*
+ ((down) n)
+ ((up)
+ (+bx n
+ ;; with the -1 it will only round up if the cut off part is
+ ;; non-zero
+ (-bx (bit-lshbx #z1
+ (-fx (+fx e nb-bits) 1))
+ #z1)))
+ ((round)
+ (+bx n
+ (bit-lshbx #z1
+ (-fx (+fx e nb-bits) 2))))))
+ (let* ((shift (-fx (+fx e nb-bits) 1))
+ (cut (bit-rshbx (round n) shift))
+ (exact? (=bx n (bit-lshbx cut shift))))
+ (if (<=bx cut max-container)
+ (values cut e exact?)
+ (round-n-cut n (+fx e 1) nb-bits))))
+
+(define (rounded-/bx x y)
+ (case *round*
+ ((down) (/bx x y))
+ ((up) (+bx (/bx x y) #z1))
+ ((round) (let ((tmp (/bx (*bx #z2 x) y)))
+ (if (zerobx? (remainderbx tmp #z2))
+ (/bx tmp #z2)
+ (+bx (/bx tmp #z2) #z1))))))
+
+(define (generate-powers from to mantissa-size)
+ (let* ((nb-bits mantissa-size)
+ (offset (- from))
+ (nb-elements (+ (- from) to 1))
+ (vec (make-vector nb-elements))
+ (max-container (- (bit-lshbx #z1 nb-bits) 1)))
+ ;; the negative ones. 10^-1, 10^-2, etc.
+ ;; We already know, that we can't be exact, so exact? will always be #f.
+ ;; Basically we will have a ten^i that we will *10 at each iteration. We
+ ;; want to create the matissa of 1/ten^i. However the mantissa must be
+ ;; normalized (start with a 1). -> we have to shift the number.
+ ;; We shift by multiplying with two^e. -> We encode two^e*(1/ten^i) ==
+ ;; two^e/ten^i.
+ (let loop ((i 1)
+ (ten^i #z10)
+ (two^e #z1)
+ (e 0))
+ (unless (< (- i) from)
+ (if (>bx (/bx (*bx #z2 two^e) ten^i) max-container)
+ ;; another shift would make the number too big. We are
+ ;; hence normalized now.
+ (begin
+ (vector-set! vec (-fx offset i)
+ (instantiate::Cached-Fast
+ (v (rounded-/bx two^e ten^i))
+ (e (negfx e))
+ (exact? #f)))
+ (loop (+fx i 1) (*bx ten^i #z10) two^e e))
+ (loop i ten^i (bit-lshbx two^e 1) (+fx e 1)))))
+ ;; the positive ones 10^0, 10^1, etc.
+ ;; start with 1.0. mantissa: 10...0 (1 followed by nb-bits-1 bits)
+ ;; -> e = -(nb-bits-1)
+ ;; exact? is true when the container can still hold the complete 10^i
+ (let loop ((i 0)
+ (n (bit-lshbx #z1 (-fx nb-bits 1)))
+ (e (-fx 1 nb-bits)))
+ (when (<= i to)
+ (receive (cut e exact?)
+ (round-n-cut n e nb-bits)
+ (vector-set! vec (+fx i offset)
+ (instantiate::Cached-Fast
+ (v cut)
+ (e e)
+ (exact? exact?)))
+ (loop (+fx i 1) (*bx n #z10) e))))
+ vec))
+
+(define (print-c powers from to struct-type
+ cache-name max-distance-name offset-name macro64)
+ (define (display-power power k)
+ (with-access::Cached-Fast power (v e exact?)
+ (let ((tmp-p (open-output-string)))
+ ;; really hackish way of getting the digits
+ (display (format "~x" v) tmp-p)
+ (let ((str (close-output-port tmp-p)))
+ (printf " {~a(0x~a, ~a), ~a, ~a},\n"
+ macro64
+ (substring str 0 8)
+ (substring str 8 16)
+ e
+ k)))))
+ (define (print-powers-reduced n)
+ (print "static const " struct-type " " cache-name
+ "(" n ")"
+ "[] = {")
+ (let loop ((i 0)
+ (nb-elements 0)
+ (last-e 0)
+ (max-distance 0))
+ (cond
+ ((>= i (vector-length powers))
+ (print " };")
+ (print "static const int " max-distance-name "(" n ") = "
+ max-distance ";")
+ (print "// nb elements (" n "): " nb-elements))
+ (else
+ (let* ((power (vector-ref powers i))
+ (e (Cached-Fast-e power)))
+ (display-power power (+ i from))
+ (loop (+ i n)
+ (+ nb-elements 1)
+ e
+ (cond
+ ((=fx i 0) max-distance)
+ ((> (- e last-e) max-distance) (- e last-e))
+ (else max-distance))))))))
+ (print "// Copyright 2010 the V8 project authors. All rights reserved.")
+ (print "// ------------ GENERATED FILE ----------------")
+ (print "// command used:")
+ (print "// "
+ (apply string-append (map (lambda (str)
+ (string-append " " str))
+ *main-args*))
+ " // NOLINT")
+ (print)
+ (print
+ "// This file is intended to be included inside another .h or .cc files\n"
+ "// with the following defines set:\n"
+ "// GRISU_CACHE_STRUCT: should expand to the name of a struct that will\n"
+ "// hold the cached powers of ten. Each entry will hold a 64-bit\n"
+ "// significand, a 16-bit signed binary exponent, and a 16-bit\n"
+ "// signed decimal exponent. Each entry will be constructed as follows:\n"
+ "// { significand, binary_exponent, decimal_exponent }.\n"
+ "// GRISU_CACHE_NAME(i): generates the name for the different caches.\n"
+ "// The parameter i will be a number in the range 1-20. A cache will\n"
+ "// hold every i'th element of a full cache. GRISU_CACHE_NAME(1) will\n"
+ "// thus hold all elements. The higher i the fewer elements it has.\n"
+ "// Ideally the user should only reference one cache and let the\n"
+ "// compiler remove the unused ones.\n"
+ "// GRISU_CACHE_MAX_DISTANCE(i): generates the name for the maximum\n"
+ "// binary exponent distance between all elements of a given cache.\n"
+ "// GRISU_CACHE_OFFSET: is used as variable name for the decimal\n"
+ "// exponent offset. It is equal to -cache[0].decimal_exponent.\n"
+ "// GRISU_UINT64_C: used to construct 64-bit values in a platform\n"
+ "// independent way. In order to encode 0x123456789ABCDEF0 the macro\n"
+ "// will be invoked as follows: GRISU_UINT64_C(0x12345678,9ABCDEF0).\n")
+ (print)
+ (print-powers-reduced 1)
+ (print-powers-reduced 2)
+ (print-powers-reduced 3)
+ (print-powers-reduced 4)
+ (print-powers-reduced 5)
+ (print-powers-reduced 6)
+ (print-powers-reduced 7)
+ (print-powers-reduced 8)
+ (print-powers-reduced 9)
+ (print-powers-reduced 10)
+ (print-powers-reduced 11)
+ (print-powers-reduced 12)
+ (print-powers-reduced 13)
+ (print-powers-reduced 14)
+ (print-powers-reduced 15)
+ (print-powers-reduced 16)
+ (print-powers-reduced 17)
+ (print-powers-reduced 18)
+ (print-powers-reduced 19)
+ (print-powers-reduced 20)
+ (print "static const int GRISU_CACHE_OFFSET = " (- from) ";"))
+
+;;----------------main --------------------------------------------------------
+(define *main-args* #f)
+(define *mantissa-size* #f)
+(define *dest* #f)
+(define *round* #f)
+(define *from* #f)
+(define *to* #f)
+
+(define (my-main args)
+ (set! *main-args* args)
+ (args-parse (cdr args)
+ (section "Help")
+ (("?") (args-parse-usage #f))
+ ((("-h" "--help") (help "?, -h, --help" "This help message"))
+ (args-parse-usage #f))
+ (section "Misc")
+ (("-o" ?file (help "The output file"))
+ (set! *dest* file))
+ (("--mantissa-size" ?size (help "Container-size in bits"))
+ (set! *mantissa-size* (string->number size)))
+ (("--round" ?direction (help "Round bignums (down, round or up)"))
+ (set! *round* (string->symbol direction)))
+ (("--from" ?from (help "start at 10^from"))
+ (set! *from* (string->number from)))
+ (("--to" ?to (help "go up to 10^to"))
+ (set! *to* (string->number to)))
+ (else
+ (print "Illegal argument `" else "'. Usage:")
+ (args-parse-usage #f)))
+ (when (not *from*)
+ (error "generate-ten-powers"
+ "Missing from"
+ #f))
+ (when (not *to*)
+ (error "generate-ten-powers"
+ "Missing to"
+ #f))
+ (when (not *mantissa-size*)
+ (error "generate-ten-powers"
+ "Missing mantissa size"
+ #f))
+ (when (not (memv *round* '(up down round)))
+ (error "generate-ten-powers"
+ "Missing round-method"
+ *round*))
+
+ (let ((dividers (generate-powers *from* *to* *mantissa-size*))
+ (p (if (not *dest*)
+ (current-output-port)
+ (open-output-file *dest*))))
+ (unwind-protect
+ (with-output-to-port p
+ (lambda ()
+ (print-c dividers *from* *to*
+ "GRISU_CACHE_STRUCT" "GRISU_CACHE_NAME"
+ "GRISU_CACHE_MAX_DISTANCE" "GRISU_CACHE_OFFSET"
+ "GRISU_UINT64_C"
+ )))
+ (if *dest*
+ (close-output-port p)))))
diff --git a/chromium/v8/tools/generate_shim_headers/generate_shim_headers.py b/chromium/v8/tools/generate_shim_headers/generate_shim_headers.py
new file mode 100755
index 00000000000..d0e6d069def
--- /dev/null
+++ b/chromium/v8/tools/generate_shim_headers/generate_shim_headers.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 the V8 project authors. 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.
+
+"""
+Generates shim headers that mirror the directory structure of bundled headers,
+but just forward to the system ones.
+
+This allows seamless compilation against system headers with no changes
+to our source code.
+"""
+
+
+import optparse
+import os.path
+import sys
+
+
+def GeneratorMain(argv):
+ parser = optparse.OptionParser()
+ parser.add_option('--headers-root', action='append')
+ parser.add_option('--define', action='append')
+ parser.add_option('--output-directory')
+ parser.add_option('--prefix', default='')
+ parser.add_option('--use-include-next', action='store_true')
+ parser.add_option('--outputs', action='store_true')
+ parser.add_option('--generate', action='store_true')
+
+ options, args = parser.parse_args(argv)
+
+ if not options.headers_root:
+ parser.error('Missing --headers-root parameter.')
+ if not options.output_directory:
+ parser.error('Missing --output-directory parameter.')
+ if not args:
+ parser.error('Missing arguments - header file names.')
+
+ source_tree_root = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
+
+ for root in options.headers_root:
+ target_directory = os.path.join(
+ options.output_directory,
+ os.path.relpath(root, source_tree_root))
+ if options.generate and not os.path.exists(target_directory):
+ os.makedirs(target_directory)
+
+ for header_spec in args:
+ if ';' in header_spec:
+ (header_filename,
+ include_before,
+ include_after) = header_spec.split(';', 2)
+ else:
+ header_filename = header_spec
+ include_before = ''
+ include_after = ''
+ if options.outputs:
+ yield os.path.join(target_directory, header_filename)
+ if options.generate:
+ with open(os.path.join(target_directory, header_filename), 'w') as f:
+ if options.define:
+ for define in options.define:
+ key, value = define.split('=', 1)
+ # This non-standard push_macro extension is supported
+ # by compilers we support (GCC, clang).
+ f.write('#pragma push_macro("%s")\n' % key)
+ f.write('#undef %s\n' % key)
+ f.write('#define %s %s\n' % (key, value))
+
+ if include_before:
+ for header in include_before.split(':'):
+ f.write('#include %s\n' % header)
+
+ include_target = options.prefix + header_filename
+ if options.use_include_next:
+ f.write('#include_next <%s>\n' % include_target)
+ else:
+ f.write('#include <%s>\n' % include_target)
+
+ if include_after:
+ for header in include_after.split(':'):
+ f.write('#include %s\n' % header)
+
+ if options.define:
+ for define in options.define:
+ key, value = define.split('=', 1)
+ # This non-standard pop_macro extension is supported
+ # by compilers we support (GCC, clang).
+ f.write('#pragma pop_macro("%s")\n' % key)
+
+
+def DoMain(argv):
+ return '\n'.join(GeneratorMain(argv))
+
+
+if __name__ == '__main__':
+ DoMain(sys.argv[1:])
diff --git a/chromium/v8/tools/grokdump.py b/chromium/v8/tools/grokdump.py
new file mode 100755
index 00000000000..12ccefdef74
--- /dev/null
+++ b/chromium/v8/tools/grokdump.py
@@ -0,0 +1,2003 @@
+#!/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.
+
+import bisect
+import cmd
+import codecs
+import ctypes
+import datetime
+import disasm
+import mmap
+import optparse
+import os
+import re
+import struct
+import sys
+import types
+import v8heapconst
+
+USAGE="""usage: %prog [OPTIONS] [DUMP-FILE]
+
+Minidump analyzer.
+
+Shows the processor state at the point of exception including the
+stack of the active thread and the referenced objects in the V8
+heap. Code objects are disassembled and the addresses linked from the
+stack (e.g. pushed return addresses) are marked with "=>".
+
+Examples:
+ $ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp"""
+
+
+DEBUG=False
+
+
+def DebugPrint(s):
+ if not DEBUG: return
+ print s
+
+
+class Descriptor(object):
+ """Descriptor of a structure in a memory."""
+
+ def __init__(self, fields):
+ self.fields = fields
+ self.is_flexible = False
+ for _, type_or_func in fields:
+ if isinstance(type_or_func, types.FunctionType):
+ self.is_flexible = True
+ break
+ if not self.is_flexible:
+ self.ctype = Descriptor._GetCtype(fields)
+ self.size = ctypes.sizeof(self.ctype)
+
+ def Read(self, memory, offset):
+ if self.is_flexible:
+ fields_copy = self.fields[:]
+ last = 0
+ for name, type_or_func in fields_copy:
+ if isinstance(type_or_func, types.FunctionType):
+ partial_ctype = Descriptor._GetCtype(fields_copy[:last])
+ partial_object = partial_ctype.from_buffer(memory, offset)
+ type = type_or_func(partial_object)
+ if type is not None:
+ fields_copy[last] = (name, type)
+ last += 1
+ else:
+ last += 1
+ complete_ctype = Descriptor._GetCtype(fields_copy[:last])
+ else:
+ complete_ctype = self.ctype
+ return complete_ctype.from_buffer(memory, offset)
+
+ @staticmethod
+ def _GetCtype(fields):
+ class Raw(ctypes.Structure):
+ _fields_ = fields
+ _pack_ = 1
+
+ def __str__(self):
+ return "{" + ", ".join("%s: %s" % (field, self.__getattribute__(field))
+ for field, _ in Raw._fields_) + "}"
+ return Raw
+
+
+def FullDump(reader, heap):
+ """Dump all available memory regions."""
+ def dump_region(reader, start, size, location):
+ print
+ while start & 3 != 0:
+ start += 1
+ size -= 1
+ location += 1
+ is_executable = reader.IsProbableExecutableRegion(location, size)
+ is_ascii = reader.IsProbableASCIIRegion(location, size)
+
+ if is_executable is not False:
+ lines = reader.GetDisasmLines(start, size)
+ for line in lines:
+ print FormatDisasmLine(start, heap, line)
+ print
+
+ if is_ascii is not False:
+ # Output in the same format as the Unix hd command
+ addr = start
+ for slot in xrange(location, location + size, 16):
+ hex_line = ""
+ asc_line = ""
+ for i in xrange(0, 16):
+ if slot + i < location + size:
+ byte = ctypes.c_uint8.from_buffer(reader.minidump, slot + i).value
+ if byte >= 0x20 and byte < 0x7f:
+ asc_line += chr(byte)
+ else:
+ asc_line += "."
+ hex_line += " %02x" % (byte)
+ else:
+ hex_line += " "
+ if i == 7:
+ hex_line += " "
+ print "%s %s |%s|" % (reader.FormatIntPtr(addr),
+ hex_line,
+ asc_line)
+ addr += 16
+
+ if is_executable is not True and is_ascii is not True:
+ print "%s - %s" % (reader.FormatIntPtr(start),
+ reader.FormatIntPtr(start + size))
+ for slot in xrange(start,
+ start + size,
+ reader.PointerSize()):
+ maybe_address = reader.ReadUIntPtr(slot)
+ heap_object = heap.FindObject(maybe_address)
+ print "%s: %s" % (reader.FormatIntPtr(slot),
+ reader.FormatIntPtr(maybe_address))
+ if heap_object:
+ heap_object.Print(Printer())
+ print
+
+ reader.ForEachMemoryRegion(dump_region)
+
+# Heap constants generated by 'make grokdump' in v8heapconst module.
+INSTANCE_TYPES = v8heapconst.INSTANCE_TYPES
+KNOWN_MAPS = v8heapconst.KNOWN_MAPS
+KNOWN_OBJECTS = v8heapconst.KNOWN_OBJECTS
+
+# Set of structures and constants that describe the layout of minidump
+# files. Based on MSDN and Google Breakpad.
+
+MINIDUMP_HEADER = Descriptor([
+ ("signature", ctypes.c_uint32),
+ ("version", ctypes.c_uint32),
+ ("stream_count", ctypes.c_uint32),
+ ("stream_directories_rva", ctypes.c_uint32),
+ ("checksum", ctypes.c_uint32),
+ ("time_date_stampt", ctypes.c_uint32),
+ ("flags", ctypes.c_uint64)
+])
+
+MINIDUMP_LOCATION_DESCRIPTOR = Descriptor([
+ ("data_size", ctypes.c_uint32),
+ ("rva", ctypes.c_uint32)
+])
+
+MINIDUMP_STRING = Descriptor([
+ ("length", ctypes.c_uint32),
+ ("buffer", lambda t: ctypes.c_uint8 * (t.length + 2))
+])
+
+MINIDUMP_DIRECTORY = Descriptor([
+ ("stream_type", ctypes.c_uint32),
+ ("location", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
+])
+
+MD_EXCEPTION_MAXIMUM_PARAMETERS = 15
+
+MINIDUMP_EXCEPTION = Descriptor([
+ ("code", ctypes.c_uint32),
+ ("flags", ctypes.c_uint32),
+ ("record", ctypes.c_uint64),
+ ("address", ctypes.c_uint64),
+ ("parameter_count", ctypes.c_uint32),
+ ("unused_alignment", ctypes.c_uint32),
+ ("information", ctypes.c_uint64 * MD_EXCEPTION_MAXIMUM_PARAMETERS)
+])
+
+MINIDUMP_EXCEPTION_STREAM = Descriptor([
+ ("thread_id", ctypes.c_uint32),
+ ("unused_alignment", ctypes.c_uint32),
+ ("exception", MINIDUMP_EXCEPTION.ctype),
+ ("thread_context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
+])
+
+# Stream types.
+MD_UNUSED_STREAM = 0
+MD_RESERVED_STREAM_0 = 1
+MD_RESERVED_STREAM_1 = 2
+MD_THREAD_LIST_STREAM = 3
+MD_MODULE_LIST_STREAM = 4
+MD_MEMORY_LIST_STREAM = 5
+MD_EXCEPTION_STREAM = 6
+MD_SYSTEM_INFO_STREAM = 7
+MD_THREAD_EX_LIST_STREAM = 8
+MD_MEMORY_64_LIST_STREAM = 9
+MD_COMMENT_STREAM_A = 10
+MD_COMMENT_STREAM_W = 11
+MD_HANDLE_DATA_STREAM = 12
+MD_FUNCTION_TABLE_STREAM = 13
+MD_UNLOADED_MODULE_LIST_STREAM = 14
+MD_MISC_INFO_STREAM = 15
+MD_MEMORY_INFO_LIST_STREAM = 16
+MD_THREAD_INFO_LIST_STREAM = 17
+MD_HANDLE_OPERATION_LIST_STREAM = 18
+
+MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE = 80
+
+MINIDUMP_FLOATING_SAVE_AREA_X86 = Descriptor([
+ ("control_word", ctypes.c_uint32),
+ ("status_word", ctypes.c_uint32),
+ ("tag_word", ctypes.c_uint32),
+ ("error_offset", ctypes.c_uint32),
+ ("error_selector", ctypes.c_uint32),
+ ("data_offset", ctypes.c_uint32),
+ ("data_selector", ctypes.c_uint32),
+ ("register_area", ctypes.c_uint8 * MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE),
+ ("cr0_npx_state", ctypes.c_uint32)
+])
+
+MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE = 512
+
+# Context flags.
+MD_CONTEXT_X86 = 0x00010000
+MD_CONTEXT_X86_CONTROL = (MD_CONTEXT_X86 | 0x00000001)
+MD_CONTEXT_X86_INTEGER = (MD_CONTEXT_X86 | 0x00000002)
+MD_CONTEXT_X86_SEGMENTS = (MD_CONTEXT_X86 | 0x00000004)
+MD_CONTEXT_X86_FLOATING_POINT = (MD_CONTEXT_X86 | 0x00000008)
+MD_CONTEXT_X86_DEBUG_REGISTERS = (MD_CONTEXT_X86 | 0x00000010)
+MD_CONTEXT_X86_EXTENDED_REGISTERS = (MD_CONTEXT_X86 | 0x00000020)
+
+def EnableOnFlag(type, flag):
+ return lambda o: [None, type][int((o.context_flags & flag) != 0)]
+
+MINIDUMP_CONTEXT_X86 = Descriptor([
+ ("context_flags", ctypes.c_uint32),
+ # MD_CONTEXT_X86_DEBUG_REGISTERS.
+ ("dr0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
+ ("dr1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
+ ("dr2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
+ ("dr3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
+ ("dr6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
+ ("dr7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_DEBUG_REGISTERS)),
+ # MD_CONTEXT_X86_FLOATING_POINT.
+ ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_X86.ctype,
+ MD_CONTEXT_X86_FLOATING_POINT)),
+ # MD_CONTEXT_X86_SEGMENTS.
+ ("gs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
+ ("fs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
+ ("es", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
+ ("ds", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_SEGMENTS)),
+ # MD_CONTEXT_X86_INTEGER.
+ ("edi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
+ ("esi", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
+ ("ebx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
+ ("edx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
+ ("ecx", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
+ ("eax", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_INTEGER)),
+ # MD_CONTEXT_X86_CONTROL.
+ ("ebp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
+ ("eip", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
+ ("cs", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
+ ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
+ ("esp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
+ ("ss", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_X86_CONTROL)),
+ # MD_CONTEXT_X86_EXTENDED_REGISTERS.
+ ("extended_registers",
+ EnableOnFlag(ctypes.c_uint8 * MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE,
+ MD_CONTEXT_X86_EXTENDED_REGISTERS))
+])
+
+MD_CONTEXT_ARM = 0x40000000
+MD_CONTEXT_ARM_INTEGER = (MD_CONTEXT_ARM | 0x00000002)
+MD_CONTEXT_ARM_FLOATING_POINT = (MD_CONTEXT_ARM | 0x00000004)
+MD_FLOATINGSAVEAREA_ARM_FPR_COUNT = 32
+MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT = 8
+
+MINIDUMP_FLOATING_SAVE_AREA_ARM = Descriptor([
+ ("fpscr", ctypes.c_uint64),
+ ("regs", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPR_COUNT),
+ ("extra", ctypes.c_uint64 * MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT)
+])
+
+MINIDUMP_CONTEXT_ARM = Descriptor([
+ ("context_flags", ctypes.c_uint32),
+ # MD_CONTEXT_ARM_INTEGER.
+ ("r0", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r1", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r2", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r3", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r4", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r5", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r6", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r7", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r8", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r9", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r10", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r11", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("r12", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("sp", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("lr", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("pc", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_ARM_INTEGER)),
+ ("cpsr", ctypes.c_uint32),
+ ("float_save", EnableOnFlag(MINIDUMP_FLOATING_SAVE_AREA_ARM.ctype,
+ MD_CONTEXT_ARM_FLOATING_POINT))
+])
+
+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)
+])
+
+MINIDUMP_MEMORY_DESCRIPTOR64 = Descriptor([
+ ("start", ctypes.c_uint64),
+ ("size", ctypes.c_uint64)
+])
+
+MINIDUMP_MEMORY_LIST = Descriptor([
+ ("range_count", ctypes.c_uint32),
+ ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR.ctype * m.range_count)
+])
+
+MINIDUMP_MEMORY_LIST64 = Descriptor([
+ ("range_count", ctypes.c_uint64),
+ ("base_rva", ctypes.c_uint64),
+ ("ranges", lambda m: MINIDUMP_MEMORY_DESCRIPTOR64.ctype * m.range_count)
+])
+
+MINIDUMP_THREAD = Descriptor([
+ ("id", ctypes.c_uint32),
+ ("suspend_count", ctypes.c_uint32),
+ ("priority_class", ctypes.c_uint32),
+ ("priority", ctypes.c_uint32),
+ ("ted", ctypes.c_uint64),
+ ("stack", MINIDUMP_MEMORY_DESCRIPTOR.ctype),
+ ("context", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
+])
+
+MINIDUMP_THREAD_LIST = Descriptor([
+ ("thread_count", ctypes.c_uint32),
+ ("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
+])
+
+MINIDUMP_VS_FIXEDFILEINFO = Descriptor([
+ ("dwSignature", ctypes.c_uint32),
+ ("dwStrucVersion", ctypes.c_uint32),
+ ("dwFileVersionMS", ctypes.c_uint32),
+ ("dwFileVersionLS", ctypes.c_uint32),
+ ("dwProductVersionMS", ctypes.c_uint32),
+ ("dwProductVersionLS", ctypes.c_uint32),
+ ("dwFileFlagsMask", ctypes.c_uint32),
+ ("dwFileFlags", ctypes.c_uint32),
+ ("dwFileOS", ctypes.c_uint32),
+ ("dwFileType", ctypes.c_uint32),
+ ("dwFileSubtype", ctypes.c_uint32),
+ ("dwFileDateMS", ctypes.c_uint32),
+ ("dwFileDateLS", ctypes.c_uint32)
+])
+
+MINIDUMP_RAW_MODULE = Descriptor([
+ ("base_of_image", ctypes.c_uint64),
+ ("size_of_image", ctypes.c_uint32),
+ ("checksum", ctypes.c_uint32),
+ ("time_date_stamp", ctypes.c_uint32),
+ ("module_name_rva", ctypes.c_uint32),
+ ("version_info", MINIDUMP_VS_FIXEDFILEINFO.ctype),
+ ("cv_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
+ ("misc_record", MINIDUMP_LOCATION_DESCRIPTOR.ctype),
+ ("reserved0", ctypes.c_uint32 * 2),
+ ("reserved1", ctypes.c_uint32 * 2)
+])
+
+MINIDUMP_MODULE_LIST = Descriptor([
+ ("number_of_modules", ctypes.c_uint32),
+ ("modules", lambda t: MINIDUMP_RAW_MODULE.ctype * t.number_of_modules)
+])
+
+MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
+ ("processor_architecture", ctypes.c_uint16)
+])
+
+MD_CPU_ARCHITECTURE_X86 = 0
+MD_CPU_ARCHITECTURE_ARM = 5
+MD_CPU_ARCHITECTURE_AMD64 = 9
+
+class FuncSymbol:
+ def __init__(self, start, size, name):
+ self.start = start
+ self.end = self.start + size
+ self.name = name
+
+ def __cmp__(self, other):
+ if isinstance(other, FuncSymbol):
+ return self.start - other.start
+ return self.start - other
+
+ def Covers(self, addr):
+ return (self.start <= addr) and (addr < self.end)
+
+class MinidumpReader(object):
+ """Minidump (.dmp) reader."""
+
+ _HEADER_MAGIC = 0x504d444d
+
+ def __init__(self, options, minidump_name):
+ self.minidump_name = minidump_name
+ self.minidump_file = open(minidump_name, "r")
+ self.minidump = mmap.mmap(self.minidump_file.fileno(), 0, mmap.MAP_PRIVATE)
+ self.header = MINIDUMP_HEADER.Read(self.minidump, 0)
+ if self.header.signature != MinidumpReader._HEADER_MAGIC:
+ print >>sys.stderr, "Warning: Unsupported minidump header magic!"
+ DebugPrint(self.header)
+ directories = []
+ offset = self.header.stream_directories_rva
+ 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.module_list = None
+ self.thread_map = {}
+
+ self.symdir = options.symdir
+ self.modules_with_symbols = []
+ self.symbols = []
+
+ # 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_ARM,
+ MD_CPU_ARCHITECTURE_X86]
+ assert not self.arch is None
+
+ for d in directories:
+ DebugPrint(d)
+ if d.stream_type == MD_EXCEPTION_STREAM:
+ self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
+ self.minidump, d.location.rva)
+ DebugPrint(self.exception)
+ 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)
+ elif self.arch == MD_CPU_ARCHITECTURE_ARM:
+ self.exception_context = MINIDUMP_CONTEXT_ARM.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)
+ assert ctypes.sizeof(thread_list) == d.location.data_size
+ DebugPrint(thread_list)
+ for thread in thread_list.threads:
+ DebugPrint(thread)
+ self.thread_map[thread.id] = thread
+ elif d.stream_type == MD_MODULE_LIST_STREAM:
+ assert self.module_list is None
+ self.module_list = MINIDUMP_MODULE_LIST.Read(
+ self.minidump, d.location.rva)
+ assert ctypes.sizeof(self.module_list) == d.location.data_size
+ elif d.stream_type == MD_MEMORY_LIST_STREAM:
+ print >>sys.stderr, "Warning: This is not a full minidump!"
+ assert self.memory_list is None
+ self.memory_list = MINIDUMP_MEMORY_LIST.Read(
+ self.minidump, d.location.rva)
+ assert ctypes.sizeof(self.memory_list) == d.location.data_size
+ DebugPrint(self.memory_list)
+ elif d.stream_type == MD_MEMORY_64_LIST_STREAM:
+ assert self.memory_list64 is None
+ self.memory_list64 = MINIDUMP_MEMORY_LIST64.Read(
+ self.minidump, d.location.rva)
+ assert ctypes.sizeof(self.memory_list64) == d.location.data_size
+ DebugPrint(self.memory_list64)
+
+ def IsValidAddress(self, address):
+ return self.FindLocation(address) is not None
+
+ def ReadU8(self, address):
+ location = self.FindLocation(address)
+ return ctypes.c_uint8.from_buffer(self.minidump, location).value
+
+ def ReadU32(self, address):
+ 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_ARM:
+ return self.ReadU32(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]
+
+ def _ReadWord(self, location):
+ if self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return ctypes.c_uint64.from_buffer(self.minidump, location).value
+ elif self.arch == MD_CPU_ARCHITECTURE_ARM:
+ return ctypes.c_uint32.from_buffer(self.minidump, location).value
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return ctypes.c_uint32.from_buffer(self.minidump, location).value
+
+ def IsProbableASCIIRegion(self, location, length):
+ ascii_bytes = 0
+ non_ascii_bytes = 0
+ for loc in xrange(location, location + length):
+ byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
+ if byte >= 0x7f:
+ non_ascii_bytes += 1
+ if byte < 0x20 and byte != 0:
+ non_ascii_bytes += 1
+ if byte < 0x7f and byte >= 0x20:
+ ascii_bytes += 1
+ if byte == 0xa: # newline
+ ascii_bytes += 1
+ if ascii_bytes * 10 <= length:
+ return False
+ if length > 0 and ascii_bytes > non_ascii_bytes * 7:
+ return True
+ if ascii_bytes > non_ascii_bytes * 3:
+ return None # Maybe
+ return False
+
+ def IsProbableExecutableRegion(self, location, length):
+ opcode_bytes = 0
+ sixty_four = self.arch == MD_CPU_ARCHITECTURE_AMD64
+ for loc in xrange(location, location + length):
+ byte = ctypes.c_uint8.from_buffer(self.minidump, loc).value
+ if (byte == 0x8b or # mov
+ byte == 0x89 or # mov reg-reg
+ (byte & 0xf0) == 0x50 or # push/pop
+ (sixty_four and (byte & 0xf0) == 0x40) or # rex prefix
+ byte == 0xc3 or # return
+ byte == 0x74 or # jeq
+ byte == 0x84 or # jeq far
+ byte == 0x75 or # jne
+ byte == 0x85 or # jne far
+ byte == 0xe8 or # call
+ byte == 0xe9 or # jmp far
+ byte == 0xeb): # jmp near
+ opcode_bytes += 1
+ opcode_percent = (opcode_bytes * 100) / length
+ threshold = 20
+ if opcode_percent > threshold + 2:
+ return True
+ if opcode_percent > threshold - 2:
+ return None # Maybe
+ return False
+
+ def FindRegion(self, addr):
+ answer = [-1, -1]
+ def is_in(reader, start, size, location):
+ if addr >= start and addr < start + size:
+ answer[0] = start
+ answer[1] = size
+ self.ForEachMemoryRegion(is_in)
+ if answer[0] == -1:
+ return None
+ return answer
+
+ def ForEachMemoryRegion(self, cb):
+ if self.memory_list64 is not None:
+ for r in self.memory_list64.ranges:
+ location = self.memory_list64.base_rva + offset
+ cb(self, r.start, r.size, location)
+ offset += r.size
+
+ if self.memory_list is not None:
+ for r in self.memory_list.ranges:
+ cb(self, r.start, r.memory.data_size, r.memory.rva)
+
+ def FindWord(self, word, alignment=0):
+ def search_inside_region(reader, start, size, location):
+ location = (location + alignment) & ~alignment
+ for loc in xrange(location, location + size - self.PointerSize()):
+ if reader._ReadWord(loc) == word:
+ slot = start + (loc - location)
+ print "%s: %s" % (reader.FormatIntPtr(slot),
+ reader.FormatIntPtr(word))
+ self.ForEachMemoryRegion(search_inside_region)
+
+ def FindLocation(self, address):
+ offset = 0
+ if self.memory_list64 is not None:
+ for r in self.memory_list64.ranges:
+ if r.start <= address < r.start + r.size:
+ return self.memory_list64.base_rva + offset + address - r.start
+ offset += r.size
+ if self.memory_list is not None:
+ for r in self.memory_list.ranges:
+ if r.start <= address < r.start + r.memory.data_size:
+ return r.memory.rva + address - r.start
+ return None
+
+ def GetDisasmLines(self, address, size):
+ def CountUndefinedInstructions(lines):
+ pattern = "<UNDEFINED>"
+ return sum([line.count(pattern) for (ignore, line) in lines])
+
+ location = self.FindLocation(address)
+ if location is None: return []
+ arch = None
+ possible_objdump_flags = [""]
+ if self.arch == MD_CPU_ARCHITECTURE_X86:
+ arch = "ia32"
+ elif self.arch == MD_CPU_ARCHITECTURE_ARM:
+ arch = "arm"
+ possible_objdump_flags = ["", "--disassembler-options=force-thumb"]
+ elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ arch = "x64"
+ results = [ disasm.GetDisasmLines(self.minidump_name,
+ location,
+ size,
+ arch,
+ False,
+ objdump_flags)
+ for objdump_flags in possible_objdump_flags ]
+ return min(results, key=CountUndefinedInstructions)
+
+
+ def Dispose(self):
+ 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_ARM:
+ return self.exception_context.pc
+ 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_ARM:
+ return self.exception_context.sp
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return self.exception_context.esp
+
+ def ExceptionFP(self):
+ if self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return self.exception_context.rbp
+ elif self.arch == MD_CPU_ARCHITECTURE_ARM:
+ return None
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return self.exception_context.ebp
+
+ def FormatIntPtr(self, value):
+ if self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return "%016x" % value
+ elif self.arch == MD_CPU_ARCHITECTURE_ARM:
+ return "%08x" % 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_ARM:
+ return 4
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return 4
+
+ def Register(self, name):
+ return self.exception_context.__getattribute__(name)
+
+ def ReadMinidumpString(self, rva):
+ string = bytearray(MINIDUMP_STRING.Read(self.minidump, rva).buffer)
+ string = string.decode("utf16")
+ return string[0:len(string) - 1]
+
+ # Load FUNC records from a BreakPad symbol file
+ #
+ # http://code.google.com/p/google-breakpad/wiki/SymbolFiles
+ #
+ def _LoadSymbolsFrom(self, symfile, baseaddr):
+ print "Loading symbols from %s" % (symfile)
+ funcs = []
+ with open(symfile) as f:
+ for line in f:
+ result = re.match(
+ r"^FUNC ([a-f0-9]+) ([a-f0-9]+) ([a-f0-9]+) (.*)$", line)
+ if result is not None:
+ start = int(result.group(1), 16)
+ size = int(result.group(2), 16)
+ name = result.group(4).rstrip()
+ bisect.insort_left(self.symbols,
+ FuncSymbol(baseaddr + start, size, name))
+ print " ... done"
+
+ def TryLoadSymbolsFor(self, modulename, module):
+ try:
+ symfile = os.path.join(self.symdir,
+ modulename.replace('.', '_') + ".pdb.sym")
+ if os.path.isfile(symfile):
+ self._LoadSymbolsFrom(symfile, module.base_of_image)
+ self.modules_with_symbols.append(module)
+ except Exception as e:
+ print " ... failure (%s)" % (e)
+
+ # Returns true if address is covered by some module that has loaded symbols.
+ def _IsInModuleWithSymbols(self, addr):
+ for module in self.modules_with_symbols:
+ start = module.base_of_image
+ end = start + module.size_of_image
+ if (start <= addr) and (addr < end):
+ return True
+ return False
+
+ # Find symbol covering the given address and return its name in format
+ # <symbol name>+<offset from the start>
+ def FindSymbol(self, addr):
+ if not self._IsInModuleWithSymbols(addr):
+ return None
+
+ i = bisect.bisect_left(self.symbols, addr)
+ symbol = None
+ if (0 < i) and self.symbols[i - 1].Covers(addr):
+ symbol = self.symbols[i - 1]
+ elif (i < len(self.symbols)) and self.symbols[i].Covers(addr):
+ symbol = self.symbols[i]
+ else:
+ return None
+ diff = addr - symbol.start
+ return "%s+0x%x" % (symbol.name, diff)
+
+
+class Printer(object):
+ """Printer with indentation support."""
+
+ def __init__(self):
+ self.indent = 0
+
+ def Indent(self):
+ self.indent += 2
+
+ def Dedent(self):
+ self.indent -= 2
+
+ def Print(self, string):
+ print "%s%s" % (self._IndentString(), string)
+
+ def PrintLines(self, lines):
+ indent = self._IndentString()
+ print "\n".join("%s%s" % (indent, line) for line in lines)
+
+ def _IndentString(self):
+ return self.indent * " "
+
+
+ADDRESS_RE = re.compile(r"0x[0-9a-fA-F]+")
+
+
+def FormatDisasmLine(start, heap, line):
+ line_address = start + line[0]
+ stack_slot = heap.stack_map.get(line_address)
+ marker = " "
+ if stack_slot:
+ marker = "=>"
+ code = AnnotateAddresses(heap, line[1])
+ return "%s%08x %08x: %s" % (marker, line_address, line[0], code)
+
+
+def AnnotateAddresses(heap, line):
+ extra = []
+ for m in ADDRESS_RE.finditer(line):
+ maybe_address = int(m.group(0), 16)
+ object = heap.FindObject(maybe_address)
+ if not object: continue
+ extra.append(str(object))
+ if len(extra) == 0: return line
+ return "%s ;; %s" % (line, ", ".join(extra))
+
+
+class HeapObject(object):
+ def __init__(self, heap, map, address):
+ self.heap = heap
+ self.map = map
+ self.address = address
+
+ def Is(self, cls):
+ return isinstance(self, cls)
+
+ def Print(self, p):
+ p.Print(str(self))
+
+ def __str__(self):
+ 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.ReadUIntPtr(self.address + offset)
+ return self.heap.FindObjectOrSmi(field_value)
+
+ def SmiField(self, offset):
+ field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
+ assert (field_value & 1) == 0
+ return field_value / 2
+
+
+class Map(HeapObject):
+ def Decode(self, offset, size, value):
+ return (value >> offset) & ((1 << size) - 1)
+
+ # Instance Sizes
+ def InstanceSizesOffset(self):
+ return self.heap.PointerSize()
+
+ def InstanceSizeOffset(self):
+ return self.InstanceSizesOffset()
+
+ def InObjectProperties(self):
+ return self.InstanceSizeOffset() + 1
+
+ def PreAllocatedPropertyFields(self):
+ return self.InObjectProperties() + 1
+
+ def VisitorId(self):
+ return self.PreAllocatedPropertyFields() + 1
+
+ # Instance Attributes
+ def InstanceAttributesOffset(self):
+ return self.InstanceSizesOffset() + self.heap.IntSize()
+
+ def InstanceTypeOffset(self):
+ return self.InstanceAttributesOffset()
+
+ def UnusedPropertyFieldsOffset(self):
+ return self.InstanceTypeOffset() + 1
+
+ def BitFieldOffset(self):
+ return self.UnusedPropertyFieldsOffset() + 1
+
+ def BitField2Offset(self):
+ return self.BitFieldOffset() + 1
+
+ # Other fields
+ def PrototypeOffset(self):
+ return self.InstanceAttributesOffset() + self.heap.IntSize()
+
+ def ConstructorOffset(self):
+ return self.PrototypeOffset() + self.heap.PointerSize()
+
+ def TransitionsOrBackPointerOffset(self):
+ return self.ConstructorOffset() + self.heap.PointerSize()
+
+ def DescriptorsOffset(self):
+ return self.TransitionsOrBackPointerOffset() + self.heap.PointerSize()
+
+ def CodeCacheOffset(self):
+ return self.DescriptorsOffset() + self.heap.PointerSize()
+
+ def DependentCodeOffset(self):
+ return self.CodeCacheOffset() + self.heap.PointerSize()
+
+ def BitField3Offset(self):
+ return self.DependentCodeOffset() + self.heap.PointerSize()
+
+ def ReadByte(self, offset):
+ return self.heap.reader.ReadU8(self.address + offset)
+
+ def Print(self, p):
+ p.Print("Map(%08x)" % (self.address))
+ p.Print("- size: %d, inobject: %d, preallocated: %d, visitor: %d" % (
+ self.ReadByte(self.InstanceSizeOffset()),
+ self.ReadByte(self.InObjectProperties()),
+ self.ReadByte(self.PreAllocatedPropertyFields()),
+ self.VisitorId()))
+
+ bitfield = self.ReadByte(self.BitFieldOffset())
+ bitfield2 = self.ReadByte(self.BitField2Offset())
+ p.Print("- %s, unused: %d, bf: %d, bf2: %d" % (
+ INSTANCE_TYPES[self.ReadByte(self.InstanceTypeOffset())],
+ self.ReadByte(self.UnusedPropertyFieldsOffset()),
+ bitfield, bitfield2))
+
+ p.Print("- kind: %s" % (self.Decode(3, 5, bitfield2)))
+
+ bitfield3 = self.ObjectField(self.BitField3Offset())
+ p.Print(
+ "- EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
+ self.Decode(0, 11, bitfield3),
+ self.Decode(11, 11, bitfield3),
+ self.Decode(25, 1, bitfield3)))
+ p.Print("- IsShared: %s" % (self.Decode(22, 1, bitfield3)))
+ p.Print("- FunctionWithPrototype: %s" % (self.Decode(23, 1, bitfield3)))
+ p.Print("- DictionaryMap: %s" % (self.Decode(24, 1, bitfield3)))
+
+ descriptors = self.ObjectField(self.DescriptorsOffset())
+ if descriptors.__class__ == FixedArray:
+ DescriptorArray(descriptors).Print(p)
+ else:
+ p.Print("Descriptors: %s" % (descriptors))
+
+ transitions = self.ObjectField(self.TransitionsOrBackPointerOffset())
+ if transitions.__class__ == FixedArray:
+ TransitionArray(transitions).Print(p)
+ else:
+ p.Print("TransitionsOrBackPointer: %s" % (transitions))
+
+ def __init__(self, heap, map, address):
+ HeapObject.__init__(self, heap, map, address)
+ self.instance_type = \
+ heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
+
+
+class String(HeapObject):
+ def LengthOffset(self):
+ return self.heap.PointerSize()
+
+ def __init__(self, heap, map, address):
+ HeapObject.__init__(self, heap, map, address)
+ self.length = self.SmiField(self.LengthOffset())
+
+ def GetChars(self):
+ return "?string?"
+
+ def Print(self, p):
+ p.Print(str(self))
+
+ def __str__(self):
+ return "\"%s\"" % self.GetChars()
+
+
+class SeqString(String):
+ 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 + self.CharsOffset(),
+ self.length)
+
+ def GetChars(self):
+ return self.chars
+
+
+class ExternalString(String):
+ # TODO(vegorov) fix ExternalString for X64 architecture
+ RESOURCE_OFFSET = 12
+
+ WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
+ WEBKIT_STRING_IMPL_CHARS_OFFSET = 8
+
+ def __init__(self, heap, map, address):
+ String.__init__(self, heap, map, address)
+ reader = heap.reader
+ self.resource = \
+ reader.ReadU32(self.address + ExternalString.RESOURCE_OFFSET)
+ self.chars = "?external string?"
+ if not reader.IsValidAddress(self.resource): return
+ string_impl_address = self.resource + \
+ ExternalString.WEBKIT_RESOUCE_STRING_IMPL_OFFSET
+ if not reader.IsValidAddress(string_impl_address): return
+ string_impl = reader.ReadU32(string_impl_address)
+ chars_ptr_address = string_impl + \
+ ExternalString.WEBKIT_STRING_IMPL_CHARS_OFFSET
+ if not reader.IsValidAddress(chars_ptr_address): return
+ chars_ptr = reader.ReadU32(chars_ptr_address)
+ if not reader.IsValidAddress(chars_ptr): return
+ raw_chars = reader.ReadBytes(chars_ptr, 2 * self.length)
+ self.chars = codecs.getdecoder("utf16")(raw_chars)[0]
+
+ def GetChars(self):
+ return self.chars
+
+
+class ConsString(String):
+ 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(self.LeftOffset())
+ self.right = self.ObjectField(self.RightOffset())
+
+ def GetChars(self):
+ try:
+ return self.left.GetChars() + self.right.GetChars()
+ except:
+ return "***CAUGHT EXCEPTION IN GROKDUMP***"
+
+
+class Oddball(HeapObject):
+ # Should match declarations in objects.h
+ KINDS = [
+ "False",
+ "True",
+ "TheHole",
+ "Null",
+ "ArgumentMarker",
+ "Undefined",
+ "Other"
+ ]
+
+ def ToStringOffset(self):
+ return self.heap.PointerSize()
+
+ def ToNumberOffset(self):
+ return self.ToStringOffset() + self.heap.PointerSize()
+
+ def KindOffset(self):
+ return self.ToNumberOffset() + self.heap.PointerSize()
+
+ def __init__(self, heap, map, address):
+ HeapObject.__init__(self, heap, map, address)
+ self.to_string = self.ObjectField(self.ToStringOffset())
+ self.kind = self.SmiField(self.KindOffset())
+
+ def Print(self, p):
+ p.Print(str(self))
+
+ def __str__(self):
+ if self.to_string:
+ return "Oddball(%08x, <%s>)" % (self.address, self.to_string.GetChars())
+ else:
+ kind = "???"
+ if 0 <= self.kind < len(Oddball.KINDS):
+ kind = Oddball.KINDS[self.kind]
+ return "Oddball(%08x, kind=%s)" % (self.address, kind)
+
+
+class FixedArray(HeapObject):
+ def LengthOffset(self):
+ return self.heap.PointerSize()
+
+ def ElementsOffset(self):
+ return self.heap.PointerSize() * 2
+
+ def MemberOffset(self, i):
+ return self.ElementsOffset() + self.heap.PointerSize() * i
+
+ def Get(self, i):
+ return self.ObjectField(self.MemberOffset(i))
+
+ def __init__(self, heap, map, address):
+ HeapObject.__init__(self, heap, map, address)
+ self.length = self.SmiField(self.LengthOffset())
+
+ def Print(self, p):
+ 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 = base_offset + 4 * i
+ try:
+ p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
+ except TypeError:
+ p.Dedent()
+ p.Print("...")
+ p.Print("}")
+ return
+ p.Dedent()
+ p.Print("}")
+
+ def __str__(self):
+ return "FixedArray(%08x, length=%d)" % (self.address, self.length)
+
+
+class DescriptorArray(object):
+ def __init__(self, array):
+ self.array = array
+
+ def Length(self):
+ return self.array.Get(0)
+
+ def Decode(self, offset, size, value):
+ return (value >> offset) & ((1 << size) - 1)
+
+ TYPES = [
+ "normal",
+ "field",
+ "function",
+ "callbacks"
+ ]
+
+ def Type(self, value):
+ return DescriptorArray.TYPES[self.Decode(0, 3, value)]
+
+ def Attributes(self, value):
+ attributes = self.Decode(3, 3, value)
+ result = []
+ if (attributes & 0): result += ["ReadOnly"]
+ if (attributes & 1): result += ["DontEnum"]
+ if (attributes & 2): result += ["DontDelete"]
+ return "[" + (",".join(result)) + "]"
+
+ def Deleted(self, value):
+ return self.Decode(6, 1, value) == 1
+
+ def Storage(self, value):
+ return self.Decode(7, 11, value)
+
+ def Pointer(self, value):
+ return self.Decode(18, 11, value)
+
+ def Details(self, di, value):
+ return (
+ di,
+ self.Type(value),
+ self.Attributes(value),
+ self.Storage(value),
+ self.Pointer(value)
+ )
+
+
+ def Print(self, p):
+ length = self.Length()
+ array = self.array
+
+ p.Print("Descriptors(%08x, length=%d)" % (array.address, length))
+ p.Print("[et] %s" % (array.Get(1)))
+
+ for di in xrange(length):
+ i = 2 + di * 3
+ p.Print("0x%x" % (array.address + array.MemberOffset(i)))
+ p.Print("[%i] name: %s" % (di, array.Get(i + 0)))
+ p.Print("[%i] details: %s %s enum %i pointer %i" % \
+ self.Details(di, array.Get(i + 1)))
+ p.Print("[%i] value: %s" % (di, array.Get(i + 2)))
+
+ end = self.array.length // 3
+ if length != end:
+ p.Print("[%i-%i] slack descriptors" % (length, end))
+
+
+class TransitionArray(object):
+ def __init__(self, array):
+ self.array = array
+
+ def IsSimpleTransition(self):
+ return self.array.length <= 2
+
+ def Length(self):
+ # SimpleTransition cases
+ if self.IsSimpleTransition():
+ return self.array.length - 1
+ return (self.array.length - 3) // 2
+
+ def Print(self, p):
+ length = self.Length()
+ array = self.array
+
+ p.Print("Transitions(%08x, length=%d)" % (array.address, length))
+ p.Print("[backpointer] %s" % (array.Get(0)))
+ if self.IsSimpleTransition():
+ if length == 1:
+ p.Print("[simple target] %s" % (array.Get(1)))
+ return
+
+ elements = array.Get(1)
+ if elements is not None:
+ p.Print("[elements ] %s" % (elements))
+
+ prototype = array.Get(2)
+ if prototype is not None:
+ p.Print("[prototype ] %s" % (prototype))
+
+ for di in xrange(length):
+ i = 3 + di * 2
+ p.Print("[%i] symbol: %s" % (di, array.Get(i + 0)))
+ p.Print("[%i] target: %s" % (di, array.Get(i + 1)))
+
+
+class JSFunction(HeapObject):
+ 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 + 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(%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):
+ p.Print("script name: %s" % self.shared.script.name)
+ p.Print("source:")
+ p.PrintLines(self._GetSource().split("\n"))
+ p.Print("code:")
+ self.code.Print(p)
+ if self.code != self.shared.code:
+ p.Print("unoptimized code:")
+ self.shared.code.Print(p)
+ p.Dedent()
+ p.Print("}")
+
+ def __str__(self):
+ inferred_name = ""
+ if self.shared.Is(SharedFunctionInfo):
+ inferred_name = self.shared.inferred_name
+ return "JSFunction(%s, %s)" % \
+ (self.heap.reader.FormatIntPtr(self.address), inferred_name)
+
+ def _GetSource(self):
+ source = "?source?"
+ start = self.shared.start_position
+ end = self.shared.end_position
+ if not self.shared.script.Is(Script): return source
+ script_source = self.shared.script.source
+ if not script_source.Is(String): return source
+ return script_source.GetChars()[start:end]
+
+
+class SharedFunctionInfo(HeapObject):
+ 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(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):
+ 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(self.SourceOffset())
+ self.name = self.ObjectField(self.NameOffset())
+
+
+class CodeCache(HeapObject):
+ def DefaultCacheOffset(self):
+ return self.heap.PointerSize()
+
+ def NormalTypeCacheOffset(self):
+ return self.DefaultCacheOffset() + self.heap.PointerSize()
+
+ def __init__(self, heap, map, address):
+ HeapObject.__init__(self, heap, map, address)
+ self.default_cache = self.ObjectField(self.DefaultCacheOffset())
+ self.normal_type_cache = self.ObjectField(self.NormalTypeCacheOffset())
+
+ def Print(self, p):
+ p.Print("CodeCache(%s) {" % self.heap.reader.FormatIntPtr(self.address))
+ p.Indent()
+ p.Print("default cache: %s" % self.default_cache)
+ p.Print("normal type cache: %s" % self.normal_type_cache)
+ p.Dedent()
+ p.Print("}")
+
+
+class Code(HeapObject):
+ 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.HeaderSize(heap)
+ self.instruction_size = \
+ heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
+
+ def Print(self, p):
+ lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
+ 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)
+ p.Dedent()
+ p.Print("}")
+
+ def _FormatLine(self, line):
+ return FormatDisasmLine(self.entry, self.heap, line)
+
+
+class V8Heap(object):
+ CLASS_MAP = {
+ "SYMBOL_TYPE": SeqString,
+ "ASCII_SYMBOL_TYPE": SeqString,
+ "CONS_SYMBOL_TYPE": ConsString,
+ "CONS_ASCII_SYMBOL_TYPE": ConsString,
+ "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,
+ "CONS_ASCII_STRING_TYPE": ConsString,
+ "EXTERNAL_STRING_TYPE": ExternalString,
+ "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE": ExternalString,
+ "EXTERNAL_ASCII_STRING_TYPE": ExternalString,
+ "MAP_TYPE": Map,
+ "ODDBALL_TYPE": Oddball,
+ "FIXED_ARRAY_TYPE": FixedArray,
+ "JS_FUNCTION_TYPE": JSFunction,
+ "SHARED_FUNCTION_INFO_TYPE": SharedFunctionInfo,
+ "SCRIPT_TYPE": Script,
+ "CODE_CACHE_TYPE": CodeCache,
+ "CODE_TYPE": Code,
+ }
+
+ def __init__(self, reader, stack_map):
+ self.reader = reader
+ self.stack_map = stack_map
+ self.objects = {}
+
+ def FindObjectOrSmi(self, tagged_address):
+ if (tagged_address & 1) == 0: return tagged_address / 2
+ return self.FindObject(tagged_address)
+
+ def FindObject(self, tagged_address):
+ if tagged_address in self.objects:
+ return self.objects[tagged_address]
+ 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.ReadUIntPtr(address)
+ if tagged_address == map_tagged_address:
+ # Meta map?
+ meta_map = Map(self, None, address)
+ instance_type_name = INSTANCE_TYPES.get(meta_map.instance_type)
+ if instance_type_name != "MAP_TYPE": return None
+ meta_map.map = meta_map
+ object = meta_map
+ else:
+ 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
+ cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
+ object = cls(self, map, address)
+ 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_ARM:
+ return (1 << 4) - 1
+ elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
+ return (1 << 5) - 1
+
+ def PageAlignmentMask(self):
+ return (1 << 20) - 1
+
+
+class KnownObject(HeapObject):
+ def __init__(self, heap, known_name):
+ HeapObject.__init__(self, heap, None, None)
+ self.known_name = known_name
+
+ def __str__(self):
+ return "<%s>" % self.known_name
+
+
+class KnownMap(HeapObject):
+ def __init__(self, heap, known_name, instance_type):
+ HeapObject.__init__(self, heap, None, None)
+ self.instance_type = instance_type
+ self.known_name = known_name
+
+ def __str__(self):
+ return "<%s>" % self.known_name
+
+
+class InspectionPadawan(object):
+ """The padawan can improve annotations by sensing well-known objects."""
+ def __init__(self, reader, heap):
+ self.reader = reader
+ self.heap = heap
+ self.known_first_map_page = 0
+ self.known_first_data_page = 0
+ self.known_first_pointer_page = 0
+
+ def __getattr__(self, name):
+ """An InspectionPadawan can be used instead of V8Heap, even though
+ it does not inherit from V8Heap (aka. mixin)."""
+ return getattr(self.heap, name)
+
+ def GetPageOffset(self, tagged_address):
+ return tagged_address & self.heap.PageAlignmentMask()
+
+ def IsInKnownMapSpace(self, tagged_address):
+ page_address = tagged_address & ~self.heap.PageAlignmentMask()
+ return page_address == self.known_first_map_page
+
+ def IsInKnownOldSpace(self, tagged_address):
+ page_address = tagged_address & ~self.heap.PageAlignmentMask()
+ return page_address in [self.known_first_data_page,
+ self.known_first_pointer_page]
+
+ def ContainingKnownOldSpaceName(self, tagged_address):
+ page_address = tagged_address & ~self.heap.PageAlignmentMask()
+ if page_address == self.known_first_data_page: return "OLD_DATA_SPACE"
+ if page_address == self.known_first_pointer_page: return "OLD_POINTER_SPACE"
+ return None
+
+ def SenseObject(self, tagged_address):
+ if self.IsInKnownOldSpace(tagged_address):
+ offset = self.GetPageOffset(tagged_address)
+ lookup_key = (self.ContainingKnownOldSpaceName(tagged_address), offset)
+ known_obj_name = KNOWN_OBJECTS.get(lookup_key)
+ if known_obj_name:
+ return KnownObject(self, known_obj_name)
+ if self.IsInKnownMapSpace(tagged_address):
+ known_map = self.SenseMap(tagged_address)
+ if known_map:
+ return known_map
+ found_obj = self.heap.FindObject(tagged_address)
+ if found_obj: return found_obj
+ address = tagged_address - 1
+ if self.reader.IsValidAddress(address):
+ map_tagged_address = self.reader.ReadUIntPtr(address)
+ map = self.SenseMap(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
+ cls = V8Heap.CLASS_MAP.get(instance_type_name, HeapObject)
+ return cls(self, map, address)
+ return None
+
+ def SenseMap(self, tagged_address):
+ if self.IsInKnownMapSpace(tagged_address):
+ offset = self.GetPageOffset(tagged_address)
+ known_map_info = KNOWN_MAPS.get(offset)
+ if known_map_info:
+ known_map_type, known_map_name = known_map_info
+ return KnownMap(self, known_map_name, known_map_type)
+ found_map = self.heap.FindMap(tagged_address)
+ if found_map: return found_map
+ return None
+
+ def FindObjectOrSmi(self, tagged_address):
+ """When used as a mixin in place of V8Heap."""
+ found_obj = self.SenseObject(tagged_address)
+ if found_obj: return found_obj
+ if (tagged_address & 1) == 0:
+ return "Smi(%d)" % (tagged_address / 2)
+ else:
+ return "Unknown(%s)" % self.reader.FormatIntPtr(tagged_address)
+
+ def FindObject(self, tagged_address):
+ """When used as a mixin in place of V8Heap."""
+ raise NotImplementedError
+
+ def FindMap(self, tagged_address):
+ """When used as a mixin in place of V8Heap."""
+ raise NotImplementedError
+
+ def PrintKnowledge(self):
+ print " known_first_map_page = %s\n"\
+ " known_first_data_page = %s\n"\
+ " known_first_pointer_page = %s" % (
+ self.reader.FormatIntPtr(self.known_first_map_page),
+ self.reader.FormatIntPtr(self.known_first_data_page),
+ self.reader.FormatIntPtr(self.known_first_pointer_page))
+
+
+class InspectionShell(cmd.Cmd):
+ def __init__(self, reader, heap):
+ cmd.Cmd.__init__(self)
+ self.reader = reader
+ self.heap = heap
+ self.padawan = InspectionPadawan(reader, heap)
+ self.prompt = "(grok) "
+
+ def do_da(self, address):
+ """
+ Print ASCII string starting at specified address.
+ """
+ address = int(address, 16)
+ string = ""
+ while self.reader.IsValidAddress(address):
+ code = self.reader.ReadU8(address)
+ if code < 128:
+ string += chr(code)
+ else:
+ break
+ address += 1
+ if string == "":
+ print "Not an ASCII string at %s" % self.reader.FormatIntPtr(address)
+ else:
+ print "%s\n" % string
+
+ def do_dd(self, address):
+ """
+ Interpret memory at the given address (if available) as a sequence
+ of words. Automatic alignment is not performed.
+ """
+ start = int(address, 16)
+ if (start & self.heap.ObjectAlignmentMask()) != 0:
+ print "Warning: Dumping un-aligned memory, is this what you had in mind?"
+ for slot in xrange(start,
+ start + self.reader.PointerSize() * 10,
+ self.reader.PointerSize()):
+ if not self.reader.IsValidAddress(slot):
+ print "Address is not contained within the minidump!"
+ return
+ maybe_address = self.reader.ReadUIntPtr(slot)
+ heap_object = self.padawan.SenseObject(maybe_address)
+ print "%s: %s %s" % (self.reader.FormatIntPtr(slot),
+ self.reader.FormatIntPtr(maybe_address),
+ heap_object or '')
+
+ def do_do(self, address):
+ """
+ Interpret memory at the given address as a V8 object. Automatic
+ alignment makes sure that you can pass tagged as well as un-tagged
+ addresses.
+ """
+ address = int(address, 16)
+ if (address & self.heap.ObjectAlignmentMask()) == 0:
+ address = address + 1
+ elif (address & self.heap.ObjectAlignmentMask()) != 1:
+ print "Address doesn't look like a valid pointer!"
+ return
+ heap_object = self.padawan.SenseObject(address)
+ if heap_object:
+ heap_object.Print(Printer())
+ else:
+ print "Address cannot be interpreted as object!"
+
+ def do_do_desc(self, address):
+ """
+ Print a descriptor array in a readable format.
+ """
+ start = int(address, 16)
+ if ((start & 1) == 1): start = start - 1
+ DescriptorArray(FixedArray(self.heap, None, start)).Print(Printer())
+
+ def do_do_map(self, address):
+ """
+ Print a descriptor array in a readable format.
+ """
+ start = int(address, 16)
+ if ((start & 1) == 1): start = start - 1
+ Map(self.heap, None, start).Print(Printer())
+
+ def do_do_trans(self, address):
+ """
+ Print a transition array in a readable format.
+ """
+ start = int(address, 16)
+ if ((start & 1) == 1): start = start - 1
+ TransitionArray(FixedArray(self.heap, None, start)).Print(Printer())
+
+ def do_dp(self, address):
+ """
+ Interpret memory at the given address as being on a V8 heap page
+ and print information about the page header (if available).
+ """
+ address = int(address, 16)
+ page_address = address & ~self.heap.PageAlignmentMask()
+ if self.reader.IsValidAddress(page_address):
+ raise NotImplementedError
+ else:
+ print "Page header is not available!"
+
+ def do_k(self, arguments):
+ """
+ Teach V8 heap layout information to the inspector. This increases
+ the amount of annotations the inspector can produce while dumping
+ data. The first page of each heap space is of particular interest
+ because it contains known objects that do not move.
+ """
+ self.padawan.PrintKnowledge()
+
+ def do_kd(self, address):
+ """
+ Teach V8 heap layout information to the inspector. Set the first
+ data-space page by passing any pointer into that page.
+ """
+ address = int(address, 16)
+ page_address = address & ~self.heap.PageAlignmentMask()
+ self.padawan.known_first_data_page = page_address
+
+ def do_km(self, address):
+ """
+ Teach V8 heap layout information to the inspector. Set the first
+ map-space page by passing any pointer into that page.
+ """
+ address = int(address, 16)
+ page_address = address & ~self.heap.PageAlignmentMask()
+ self.padawan.known_first_map_page = page_address
+
+ def do_kp(self, address):
+ """
+ Teach V8 heap layout information to the inspector. Set the first
+ pointer-space page by passing any pointer into that page.
+ """
+ address = int(address, 16)
+ page_address = address & ~self.heap.PageAlignmentMask()
+ self.padawan.known_first_pointer_page = page_address
+
+ def do_list(self, smth):
+ """
+ List all available memory regions.
+ """
+ def print_region(reader, start, size, location):
+ print " %s - %s (%d bytes)" % (reader.FormatIntPtr(start),
+ reader.FormatIntPtr(start + size),
+ size)
+ print "Available memory regions:"
+ self.reader.ForEachMemoryRegion(print_region)
+
+ def do_lm(self, arg):
+ """
+ List details for all loaded modules in the minidump. An argument can
+ be passed to limit the output to only those modules that contain the
+ argument as a substring (case insensitive match).
+ """
+ for module in self.reader.module_list.modules:
+ if arg:
+ name = GetModuleName(self.reader, module).lower()
+ if name.find(arg.lower()) >= 0:
+ PrintModuleDetails(self.reader, module)
+ else:
+ PrintModuleDetails(self.reader, module)
+ print
+
+ def do_s(self, word):
+ """
+ Search for a given word in available memory regions. The given word
+ is expanded to full pointer size and searched at aligned as well as
+ un-aligned memory locations. Use 'sa' to search aligned locations
+ only.
+ """
+ try:
+ word = int(word, 0)
+ except ValueError:
+ print "Malformed word, prefix with '0x' to use hexadecimal format."
+ return
+ print "Searching for word %d/0x%s:" % (word, self.reader.FormatIntPtr(word))
+ self.reader.FindWord(word)
+
+ def do_sh(self, none):
+ """
+ Search for the V8 Heap object in all available memory regions. You
+ might get lucky and find this rare treasure full of invaluable
+ information.
+ """
+ raise NotImplementedError
+
+ def do_u(self, args):
+ """
+ Unassemble memory in the region [address, address + size). If the
+ size is not specified, a default value of 32 bytes is used.
+ Synopsis: u 0x<address> 0x<size>
+ """
+ args = args.split(' ')
+ start = int(args[0], 16)
+ size = int(args[1], 16) if len(args) > 1 else 0x20
+ if not self.reader.IsValidAddress(start):
+ print "Address is not contained within the minidump!"
+ return
+ lines = self.reader.GetDisasmLines(start, size)
+ for line in lines:
+ print FormatDisasmLine(start, self.heap, line)
+ print
+
+ def do_EOF(self, none):
+ raise KeyboardInterrupt
+
+EIP_PROXIMITY = 64
+
+CONTEXT_FOR_ARCH = {
+ MD_CPU_ARCHITECTURE_AMD64:
+ ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip',
+ 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15'],
+ MD_CPU_ARCHITECTURE_ARM:
+ ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
+ 'r10', 'r11', 'r12', 'sp', 'lr', 'pc'],
+ MD_CPU_ARCHITECTURE_X86:
+ ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
+}
+
+KNOWN_MODULES = {'chrome.exe', 'chrome.dll'}
+
+def GetVersionString(ms, ls):
+ return "%d.%d.%d.%d" % (ms >> 16, ms & 0xffff, ls >> 16, ls & 0xffff)
+
+
+def GetModuleName(reader, module):
+ name = reader.ReadMinidumpString(module.module_name_rva)
+ # simplify for path manipulation
+ name = name.encode('utf-8')
+ return str(os.path.basename(str(name).replace("\\", "/")))
+
+
+def PrintModuleDetails(reader, module):
+ print "%s" % GetModuleName(reader, module)
+ file_version = GetVersionString(module.version_info.dwFileVersionMS,
+ module.version_info.dwFileVersionLS)
+ product_version = GetVersionString(module.version_info.dwProductVersionMS,
+ module.version_info.dwProductVersionLS)
+ print " base: %s" % reader.FormatIntPtr(module.base_of_image)
+ print " end: %s" % reader.FormatIntPtr(module.base_of_image +
+ module.size_of_image)
+ print " file version: %s" % file_version
+ print " product version: %s" % product_version
+ time_date_stamp = datetime.datetime.fromtimestamp(module.time_date_stamp)
+ print " timestamp: %s" % time_date_stamp
+
+
+def AnalyzeMinidump(options, minidump_name):
+ reader = MinidumpReader(options, minidump_name)
+ heap = None
+ DebugPrint("========================================")
+ if reader.exception is None:
+ print "Minidump has no exception info"
+ else:
+ print "Exception info:"
+ exception_thread = reader.thread_map[reader.exception.thread_id]
+ print " thread id: %d" % exception_thread.id
+ print " code: %08X" % reader.exception.exception.code
+ print " context:"
+ for r in CONTEXT_FOR_ARCH[reader.arch]:
+ print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
+ # TODO(vitalyr): decode eflags.
+ if reader.arch == MD_CPU_ARCHITECTURE_ARM:
+ print " cpsr: %s" % bin(reader.exception_context.cpsr)[2:]
+ else:
+ print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
+
+ print
+ print " modules:"
+ for module in reader.module_list.modules:
+ name = GetModuleName(reader, module)
+ if name in KNOWN_MODULES:
+ print " %s at %08X" % (name, module.base_of_image)
+ reader.TryLoadSymbolsFor(name, module)
+ print
+
+ stack_top = reader.ExceptionSP()
+ stack_bottom = exception_thread.stack.start + \
+ exception_thread.stack.memory.data_size
+ 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:"
+ eip_symbol = reader.FindSymbol(reader.ExceptionIP())
+ if eip_symbol is not None:
+ print eip_symbol
+ disasm_start = reader.ExceptionIP() - EIP_PROXIMITY
+ disasm_bytes = 2 * EIP_PROXIMITY
+ if (options.full):
+ full_range = reader.FindRegion(reader.ExceptionIP())
+ if full_range is not None:
+ disasm_start = full_range[0]
+ disasm_bytes = full_range[1]
+
+ lines = reader.GetDisasmLines(disasm_start, disasm_bytes)
+
+ for line in lines:
+ print FormatDisasmLine(disasm_start, heap, line)
+ print
+
+ if heap is None:
+ heap = V8Heap(reader, None)
+
+ if options.full:
+ FullDump(reader, heap)
+
+ if options.command:
+ InspectionShell(reader, heap).onecmd(options.command)
+
+ if options.shell:
+ try:
+ InspectionShell(reader, heap).cmdloop("type help to get help")
+ except KeyboardInterrupt:
+ print "Kthxbye."
+ elif not options.command:
+ if reader.exception is not None:
+ frame_pointer = reader.ExceptionFP()
+ print "Annotated stack (from exception.esp to bottom):"
+ for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
+ maybe_address = reader.ReadUIntPtr(slot)
+ heap_object = heap.FindObject(maybe_address)
+ maybe_symbol = reader.FindSymbol(maybe_address)
+ if slot == frame_pointer:
+ maybe_symbol = "<---- frame pointer"
+ frame_pointer = maybe_address
+ print "%s: %s %s" % (reader.FormatIntPtr(slot),
+ reader.FormatIntPtr(maybe_address),
+ maybe_symbol or "")
+ if heap_object:
+ heap_object.Print(Printer())
+ print
+
+ reader.Dispose()
+
+
+if __name__ == "__main__":
+ parser = optparse.OptionParser(USAGE)
+ parser.add_option("-s", "--shell", dest="shell", action="store_true",
+ help="start an interactive inspector shell")
+ parser.add_option("-c", "--command", dest="command", default="",
+ help="run an interactive inspector shell command and exit")
+ parser.add_option("-f", "--full", dest="full", action="store_true",
+ help="dump all information contained in the minidump")
+ parser.add_option("--symdir", dest="symdir", default=".",
+ help="directory containing *.pdb.sym file with symbols")
+ parser.add_option("--objdump",
+ default="/usr/bin/objdump",
+ help="objdump tool to use [default: %default]")
+ options, args = parser.parse_args()
+ if os.path.exists(options.objdump):
+ disasm.OBJDUMP_BIN = options.objdump
+ OBJDUMP_BIN = options.objdump
+ else:
+ print "Cannot find %s, falling back to default objdump" % options.objdump
+ if len(args) != 1:
+ parser.print_help()
+ sys.exit(1)
+ AnalyzeMinidump(options, args[0])
diff --git a/chromium/v8/tools/gyp/v8.gyp b/chromium/v8/tools/gyp/v8.gyp
new file mode 100644
index 00000000000..66376c17df5
--- /dev/null
+++ b/chromium/v8/tools/gyp/v8.gyp
@@ -0,0 +1,1041 @@
+# Copyright 2012 the V8 project authors. 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': {
+ 'v8_code': 1,
+ },
+ 'includes': ['../../build/toolchain.gypi', '../../build/features.gypi'],
+ 'targets': [
+ {
+ 'target_name': 'v8',
+ 'dependencies_traverse': 1,
+ 'conditions': [
+ ['want_separate_host_toolset==1', {
+ 'toolsets': ['host', 'target'],
+ }, {
+ 'toolsets': ['target'],
+ }],
+ ['v8_use_snapshot=="true"', {
+ # The dependency on v8_base should come from a transitive
+ # dependency however the Android toolchain requires libv8_base.a
+ # to appear before libv8_snapshot.a so it's listed explicitly.
+ 'dependencies': ['v8_base.<(v8_target_arch)', 'v8_snapshot'],
+ },
+ {
+ # The dependency on v8_base should come from a transitive
+ # dependency however the Android toolchain requires libv8_base.a
+ # to appear before libv8_snapshot.a so it's listed explicitly.
+ 'dependencies': [
+ 'v8_base.<(v8_target_arch)',
+ 'v8_nosnapshot.<(v8_target_arch)',
+ ],
+ }],
+ ['component=="shared_library"', {
+ 'type': '<(component)',
+ 'sources': [
+ # Note: on non-Windows we still build this file so that gyp
+ # has some sources to link into the component.
+ '../../src/v8dll-main.cc',
+ ],
+ 'defines': [
+ 'V8_SHARED',
+ 'BUILDING_V8_SHARED',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'V8_SHARED',
+ 'USING_V8_SHARED',
+ ],
+ },
+ 'target_conditions': [
+ ['OS=="android" and _toolset=="target"', {
+ 'libraries': [
+ '-llog',
+ ],
+ 'include_dirs': [
+ 'src/common/android/include',
+ ],
+ }],
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': ['-dynamiclib', '-all_load']
+ },
+ }],
+ ['soname_version!=""', {
+ 'product_extension': 'so.<(soname_version)',
+ }],
+ ],
+ },
+ {
+ 'type': 'none',
+ }],
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../../include',
+ ],
+ },
+ },
+ {
+ 'target_name': 'v8_snapshot',
+ 'type': 'static_library',
+ 'conditions': [
+ ['want_separate_host_toolset==1', {
+ 'toolsets': ['host', 'target'],
+ 'dependencies': [
+ 'mksnapshot.<(v8_target_arch)#host',
+ 'js2c#host',
+ ],
+ }, {
+ 'toolsets': ['target'],
+ 'dependencies': ['mksnapshot.<(v8_target_arch)', 'js2c'],
+ }],
+ ['component=="shared_library"', {
+ 'defines': [
+ 'V8_SHARED',
+ 'BUILDING_V8_SHARED',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'V8_SHARED',
+ 'USING_V8_SHARED',
+ ],
+ },
+ }],
+ ['v8_enable_i18n_support==1', {
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-libraries.cc',
+ ],
+ }],
+ ],
+ 'dependencies': [
+ 'v8_base.<(v8_target_arch)',
+ ],
+ 'include_dirs+': [
+ '../../src',
+ ],
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/libraries.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc',
+ '<(INTERMEDIATE_DIR)/snapshot.cc',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'run_mksnapshot',
+ 'inputs': [
+ '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mksnapshot.<(v8_target_arch)<(EXECUTABLE_SUFFIX)',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/snapshot.cc',
+ ],
+ 'variables': {
+ 'mksnapshot_flags': [
+ '--log-snapshot-positions',
+ '--logfile', '<(INTERMEDIATE_DIR)/snapshot.log',
+ ],
+ },
+ 'action': [
+ '<@(_inputs)',
+ '<@(mksnapshot_flags)',
+ '<@(_outputs)'
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'v8_nosnapshot.<(v8_target_arch)',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'v8_base.<(v8_target_arch)',
+ ],
+ 'include_dirs+': [
+ '../../src',
+ ],
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/libraries.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc',
+ '../../src/snapshot-empty.cc',
+ ],
+ 'conditions': [
+ ['want_separate_host_toolset==1', {
+ 'toolsets': ['host', 'target'],
+ 'dependencies': ['js2c#host'],
+ }, {
+ 'toolsets': ['target'],
+ 'dependencies': ['js2c'],
+ }],
+ ['component=="shared_library"', {
+ 'defines': [
+ 'BUILDING_V8_SHARED',
+ 'V8_SHARED',
+ ],
+ }],
+ ['v8_enable_i18n_support==1', {
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-libraries.cc',
+ ],
+ }],
+ ]
+ },
+ {
+ 'target_name': 'v8_base.<(v8_target_arch)',
+ 'type': 'static_library',
+ 'variables': {
+ 'optimize': 'max',
+ },
+ 'include_dirs+': [
+ '../../src',
+ ],
+ 'sources': [ ### gcmole(all) ###
+ '../../src/accessors.cc',
+ '../../src/accessors.h',
+ '../../src/allocation.cc',
+ '../../src/allocation.h',
+ '../../src/api.cc',
+ '../../src/api.h',
+ '../../src/apiutils.h',
+ '../../src/arguments.cc',
+ '../../src/arguments.h',
+ '../../src/assembler.cc',
+ '../../src/assembler.h',
+ '../../src/assert-scope.h',
+ '../../src/ast.cc',
+ '../../src/ast.h',
+ '../../src/atomicops.h',
+ '../../src/atomicops_internals_x86_gcc.cc',
+ '../../src/bignum-dtoa.cc',
+ '../../src/bignum-dtoa.h',
+ '../../src/bignum.cc',
+ '../../src/bignum.h',
+ '../../src/bootstrapper.cc',
+ '../../src/bootstrapper.h',
+ '../../src/builtins.cc',
+ '../../src/builtins.h',
+ '../../src/bytecodes-irregexp.h',
+ '../../src/cached-powers.cc',
+ '../../src/cached-powers.h',
+ '../../src/char-predicates-inl.h',
+ '../../src/char-predicates.h',
+ '../../src/checks.cc',
+ '../../src/checks.h',
+ '../../src/circular-queue-inl.h',
+ '../../src/circular-queue.cc',
+ '../../src/circular-queue.h',
+ '../../src/code-stubs.cc',
+ '../../src/code-stubs.h',
+ '../../src/code-stubs-hydrogen.cc',
+ '../../src/code.h',
+ '../../src/codegen.cc',
+ '../../src/codegen.h',
+ '../../src/compilation-cache.cc',
+ '../../src/compilation-cache.h',
+ '../../src/compiler.cc',
+ '../../src/compiler.h',
+ '../../src/contexts.cc',
+ '../../src/contexts.h',
+ '../../src/conversions-inl.h',
+ '../../src/conversions.cc',
+ '../../src/conversions.h',
+ '../../src/counters.cc',
+ '../../src/counters.h',
+ '../../src/cpu-profiler-inl.h',
+ '../../src/cpu-profiler.cc',
+ '../../src/cpu-profiler.h',
+ '../../src/cpu.h',
+ '../../src/data-flow.cc',
+ '../../src/data-flow.h',
+ '../../src/date.cc',
+ '../../src/date.h',
+ '../../src/dateparser-inl.h',
+ '../../src/dateparser.cc',
+ '../../src/dateparser.h',
+ '../../src/debug-agent.cc',
+ '../../src/debug-agent.h',
+ '../../src/debug.cc',
+ '../../src/debug.h',
+ '../../src/deoptimizer.cc',
+ '../../src/deoptimizer.h',
+ '../../src/disasm.h',
+ '../../src/disassembler.cc',
+ '../../src/disassembler.h',
+ '../../src/diy-fp.cc',
+ '../../src/diy-fp.h',
+ '../../src/double.h',
+ '../../src/dtoa.cc',
+ '../../src/dtoa.h',
+ '../../src/effects.h',
+ '../../src/elements-kind.cc',
+ '../../src/elements-kind.h',
+ '../../src/elements.cc',
+ '../../src/elements.h',
+ '../../src/execution.cc',
+ '../../src/execution.h',
+ '../../src/extensions/externalize-string-extension.cc',
+ '../../src/extensions/externalize-string-extension.h',
+ '../../src/extensions/gc-extension.cc',
+ '../../src/extensions/gc-extension.h',
+ '../../src/extensions/statistics-extension.cc',
+ '../../src/extensions/statistics-extension.h',
+ '../../src/factory.cc',
+ '../../src/factory.h',
+ '../../src/fast-dtoa.cc',
+ '../../src/fast-dtoa.h',
+ '../../src/fixed-dtoa.cc',
+ '../../src/fixed-dtoa.h',
+ '../../src/flag-definitions.h',
+ '../../src/flags.cc',
+ '../../src/flags.h',
+ '../../src/frames-inl.h',
+ '../../src/frames.cc',
+ '../../src/frames.h',
+ '../../src/full-codegen.cc',
+ '../../src/full-codegen.h',
+ '../../src/func-name-inferrer.cc',
+ '../../src/func-name-inferrer.h',
+ '../../src/gdb-jit.cc',
+ '../../src/gdb-jit.h',
+ '../../src/global-handles.cc',
+ '../../src/global-handles.h',
+ '../../src/globals.h',
+ '../../src/handles-inl.h',
+ '../../src/handles.cc',
+ '../../src/handles.h',
+ '../../src/hashmap.h',
+ '../../src/heap-inl.h',
+ '../../src/heap-profiler.cc',
+ '../../src/heap-profiler.h',
+ '../../src/heap-snapshot-generator-inl.h',
+ '../../src/heap-snapshot-generator.cc',
+ '../../src/heap-snapshot-generator.h',
+ '../../src/heap.cc',
+ '../../src/heap.h',
+ '../../src/hydrogen-bce.cc',
+ '../../src/hydrogen-bce.h',
+ '../../src/hydrogen-bch.cc',
+ '../../src/hydrogen-bch.h',
+ '../../src/hydrogen-canonicalize.cc',
+ '../../src/hydrogen-canonicalize.h',
+ '../../src/hydrogen-dce.cc',
+ '../../src/hydrogen-dce.h',
+ '../../src/hydrogen-dehoist.cc',
+ '../../src/hydrogen-dehoist.h',
+ '../../src/hydrogen-deoptimizing-mark.cc',
+ '../../src/hydrogen-deoptimizing-mark.h',
+ '../../src/hydrogen-environment-liveness.cc',
+ '../../src/hydrogen-environment-liveness.h',
+ '../../src/hydrogen-escape-analysis.cc',
+ '../../src/hydrogen-escape-analysis.h',
+ '../../src/hydrogen-instructions.cc',
+ '../../src/hydrogen-instructions.h',
+ '../../src/hydrogen.cc',
+ '../../src/hydrogen.h',
+ '../../src/hydrogen-gvn.cc',
+ '../../src/hydrogen-gvn.h',
+ '../../src/hydrogen-infer-representation.cc',
+ '../../src/hydrogen-infer-representation.h',
+ '../../src/hydrogen-infer-types.cc',
+ '../../src/hydrogen-infer-types.h',
+ '../../src/hydrogen-mark-deoptimize.cc',
+ '../../src/hydrogen-mark-deoptimize.h',
+ '../../src/hydrogen-minus-zero.cc',
+ '../../src/hydrogen-minus-zero.h',
+ '../../src/hydrogen-range-analysis.cc',
+ '../../src/hydrogen-range-analysis.h',
+ '../../src/hydrogen-redundant-phi.cc',
+ '../../src/hydrogen-redundant-phi.h',
+ '../../src/hydrogen-removable-simulates.cc',
+ '../../src/hydrogen-removable-simulates.h',
+ '../../src/hydrogen-representation-changes.cc',
+ '../../src/hydrogen-representation-changes.h',
+ '../../src/hydrogen-sce.cc',
+ '../../src/hydrogen-sce.h',
+ '../../src/hydrogen-uint32-analysis.cc',
+ '../../src/hydrogen-uint32-analysis.h',
+ '../../src/hydrogen-osr.cc',
+ '../../src/hydrogen-osr.h',
+ '../../src/icu_util.cc',
+ '../../src/icu_util.h',
+ '../../src/ic-inl.h',
+ '../../src/ic.cc',
+ '../../src/ic.h',
+ '../../src/incremental-marking.cc',
+ '../../src/incremental-marking.h',
+ '../../src/interface.cc',
+ '../../src/interface.h',
+ '../../src/interpreter-irregexp.cc',
+ '../../src/interpreter-irregexp.h',
+ '../../src/isolate.cc',
+ '../../src/isolate.h',
+ '../../src/json-parser.h',
+ '../../src/json-stringifier.h',
+ '../../src/jsregexp-inl.h',
+ '../../src/jsregexp.cc',
+ '../../src/jsregexp.h',
+ '../../src/lazy-instance.h',
+ '../../src/list-inl.h',
+ '../../src/list.h',
+ '../../src/lithium-allocator-inl.h',
+ '../../src/lithium-allocator.cc',
+ '../../src/lithium-allocator.h',
+ '../../src/lithium.cc',
+ '../../src/lithium.h',
+ '../../src/liveedit.cc',
+ '../../src/liveedit.h',
+ '../../src/log-inl.h',
+ '../../src/log-utils.cc',
+ '../../src/log-utils.h',
+ '../../src/log.cc',
+ '../../src/log.h',
+ '../../src/macro-assembler.h',
+ '../../src/mark-compact.cc',
+ '../../src/mark-compact.h',
+ '../../src/marking-thread.h',
+ '../../src/marking-thread.cc',
+ '../../src/messages.cc',
+ '../../src/messages.h',
+ '../../src/natives.h',
+ '../../src/objects-debug.cc',
+ '../../src/objects-inl.h',
+ '../../src/objects-printer.cc',
+ '../../src/objects-visiting.cc',
+ '../../src/objects-visiting.h',
+ '../../src/objects.cc',
+ '../../src/objects.h',
+ '../../src/once.cc',
+ '../../src/once.h',
+ '../../src/optimizing-compiler-thread.h',
+ '../../src/optimizing-compiler-thread.cc',
+ '../../src/parser.cc',
+ '../../src/parser.h',
+ '../../src/platform-posix.h',
+ '../../src/platform.h',
+ '../../src/preparse-data-format.h',
+ '../../src/preparse-data.cc',
+ '../../src/preparse-data.h',
+ '../../src/preparser.cc',
+ '../../src/preparser.h',
+ '../../src/prettyprinter.cc',
+ '../../src/prettyprinter.h',
+ '../../src/profile-generator-inl.h',
+ '../../src/profile-generator.cc',
+ '../../src/profile-generator.h',
+ '../../src/property-details.h',
+ '../../src/property.cc',
+ '../../src/property.h',
+ '../../src/regexp-macro-assembler-irregexp-inl.h',
+ '../../src/regexp-macro-assembler-irregexp.cc',
+ '../../src/regexp-macro-assembler-irregexp.h',
+ '../../src/regexp-macro-assembler-tracer.cc',
+ '../../src/regexp-macro-assembler-tracer.h',
+ '../../src/regexp-macro-assembler.cc',
+ '../../src/regexp-macro-assembler.h',
+ '../../src/regexp-stack.cc',
+ '../../src/regexp-stack.h',
+ '../../src/rewriter.cc',
+ '../../src/rewriter.h',
+ '../../src/runtime-profiler.cc',
+ '../../src/runtime-profiler.h',
+ '../../src/runtime.cc',
+ '../../src/runtime.h',
+ '../../src/safepoint-table.cc',
+ '../../src/safepoint-table.h',
+ '../../src/sampler.cc',
+ '../../src/sampler.h',
+ '../../src/scanner-character-streams.cc',
+ '../../src/scanner-character-streams.h',
+ '../../src/scanner.cc',
+ '../../src/scanner.h',
+ '../../src/scopeinfo.cc',
+ '../../src/scopeinfo.h',
+ '../../src/scopes.cc',
+ '../../src/scopes.h',
+ '../../src/serialize.cc',
+ '../../src/serialize.h',
+ '../../src/small-pointer-list.h',
+ '../../src/smart-pointers.h',
+ '../../src/snapshot-common.cc',
+ '../../src/snapshot.h',
+ '../../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',
+ '../../src/string-stream.h',
+ '../../src/strtod.cc',
+ '../../src/strtod.h',
+ '../../src/stub-cache.cc',
+ '../../src/stub-cache.h',
+ '../../src/sweeper-thread.h',
+ '../../src/sweeper-thread.cc',
+ '../../src/token.cc',
+ '../../src/token.h',
+ '../../src/transitions-inl.h',
+ '../../src/transitions.cc',
+ '../../src/transitions.h',
+ '../../src/type-info.cc',
+ '../../src/type-info.h',
+ '../../src/types.cc',
+ '../../src/types.h',
+ '../../src/typing.cc',
+ '../../src/typing.h',
+ '../../src/unbound-queue-inl.h',
+ '../../src/unbound-queue.h',
+ '../../src/unicode-inl.h',
+ '../../src/unicode.cc',
+ '../../src/unicode.h',
+ '../../src/uri.h',
+ '../../src/utils-inl.h',
+ '../../src/utils.cc',
+ '../../src/utils.h',
+ '../../src/v8-counters.cc',
+ '../../src/v8-counters.h',
+ '../../src/v8.cc',
+ '../../src/v8.h',
+ '../../src/v8checks.h',
+ '../../src/v8conversions.cc',
+ '../../src/v8conversions.h',
+ '../../src/v8globals.h',
+ '../../src/v8memory.h',
+ '../../src/v8threads.cc',
+ '../../src/v8threads.h',
+ '../../src/v8utils.cc',
+ '../../src/v8utils.h',
+ '../../src/variables.cc',
+ '../../src/variables.h',
+ '../../src/version.cc',
+ '../../src/version.h',
+ '../../src/vm-state-inl.h',
+ '../../src/vm-state.h',
+ '../../src/zone-inl.h',
+ '../../src/zone.cc',
+ '../../src/zone.h',
+ ],
+ 'conditions': [
+ ['want_separate_host_toolset==1', {
+ 'toolsets': ['host', 'target'],
+ }, {
+ 'toolsets': ['target'],
+ }],
+ ['v8_target_arch=="arm"', {
+ 'sources': [ ### gcmole(arch:arm) ###
+ '../../src/arm/assembler-arm-inl.h',
+ '../../src/arm/assembler-arm.cc',
+ '../../src/arm/assembler-arm.h',
+ '../../src/arm/builtins-arm.cc',
+ '../../src/arm/code-stubs-arm.cc',
+ '../../src/arm/code-stubs-arm.h',
+ '../../src/arm/codegen-arm.cc',
+ '../../src/arm/codegen-arm.h',
+ '../../src/arm/constants-arm.h',
+ '../../src/arm/constants-arm.cc',
+ '../../src/arm/cpu-arm.cc',
+ '../../src/arm/debug-arm.cc',
+ '../../src/arm/deoptimizer-arm.cc',
+ '../../src/arm/disasm-arm.cc',
+ '../../src/arm/frames-arm.cc',
+ '../../src/arm/frames-arm.h',
+ '../../src/arm/full-codegen-arm.cc',
+ '../../src/arm/ic-arm.cc',
+ '../../src/arm/lithium-arm.cc',
+ '../../src/arm/lithium-arm.h',
+ '../../src/arm/lithium-codegen-arm.cc',
+ '../../src/arm/lithium-codegen-arm.h',
+ '../../src/arm/lithium-gap-resolver-arm.cc',
+ '../../src/arm/lithium-gap-resolver-arm.h',
+ '../../src/arm/macro-assembler-arm.cc',
+ '../../src/arm/macro-assembler-arm.h',
+ '../../src/arm/regexp-macro-assembler-arm.cc',
+ '../../src/arm/regexp-macro-assembler-arm.h',
+ '../../src/arm/simulator-arm.cc',
+ '../../src/arm/stub-cache-arm.cc',
+ ],
+ }],
+ ['v8_target_arch=="ia32" or v8_target_arch=="mac" or OS=="mac"', {
+ 'sources': [ ### gcmole(arch:ia32) ###
+ '../../src/ia32/assembler-ia32-inl.h',
+ '../../src/ia32/assembler-ia32.cc',
+ '../../src/ia32/assembler-ia32.h',
+ '../../src/ia32/builtins-ia32.cc',
+ '../../src/ia32/code-stubs-ia32.cc',
+ '../../src/ia32/code-stubs-ia32.h',
+ '../../src/ia32/codegen-ia32.cc',
+ '../../src/ia32/codegen-ia32.h',
+ '../../src/ia32/cpu-ia32.cc',
+ '../../src/ia32/debug-ia32.cc',
+ '../../src/ia32/deoptimizer-ia32.cc',
+ '../../src/ia32/disasm-ia32.cc',
+ '../../src/ia32/frames-ia32.cc',
+ '../../src/ia32/frames-ia32.h',
+ '../../src/ia32/full-codegen-ia32.cc',
+ '../../src/ia32/ic-ia32.cc',
+ '../../src/ia32/lithium-codegen-ia32.cc',
+ '../../src/ia32/lithium-codegen-ia32.h',
+ '../../src/ia32/lithium-gap-resolver-ia32.cc',
+ '../../src/ia32/lithium-gap-resolver-ia32.h',
+ '../../src/ia32/lithium-ia32.cc',
+ '../../src/ia32/lithium-ia32.h',
+ '../../src/ia32/macro-assembler-ia32.cc',
+ '../../src/ia32/macro-assembler-ia32.h',
+ '../../src/ia32/regexp-macro-assembler-ia32.cc',
+ '../../src/ia32/regexp-macro-assembler-ia32.h',
+ '../../src/ia32/stub-cache-ia32.cc',
+ ],
+ }],
+ ['v8_target_arch=="mipsel"', {
+ 'sources': [ ### gcmole(arch:mipsel) ###
+ '../../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': [ ### gcmole(arch:x64) ###
+ '../../src/x64/assembler-x64-inl.h',
+ '../../src/x64/assembler-x64.cc',
+ '../../src/x64/assembler-x64.h',
+ '../../src/x64/builtins-x64.cc',
+ '../../src/x64/code-stubs-x64.cc',
+ '../../src/x64/code-stubs-x64.h',
+ '../../src/x64/codegen-x64.cc',
+ '../../src/x64/codegen-x64.h',
+ '../../src/x64/cpu-x64.cc',
+ '../../src/x64/debug-x64.cc',
+ '../../src/x64/deoptimizer-x64.cc',
+ '../../src/x64/disasm-x64.cc',
+ '../../src/x64/frames-x64.cc',
+ '../../src/x64/frames-x64.h',
+ '../../src/x64/full-codegen-x64.cc',
+ '../../src/x64/ic-x64.cc',
+ '../../src/x64/lithium-codegen-x64.cc',
+ '../../src/x64/lithium-codegen-x64.h',
+ '../../src/x64/lithium-gap-resolver-x64.cc',
+ '../../src/x64/lithium-gap-resolver-x64.h',
+ '../../src/x64/lithium-x64.cc',
+ '../../src/x64/lithium-x64.h',
+ '../../src/x64/macro-assembler-x64.cc',
+ '../../src/x64/macro-assembler-x64.h',
+ '../../src/x64/regexp-macro-assembler-x64.cc',
+ '../../src/x64/regexp-macro-assembler-x64.h',
+ '../../src/x64/stub-cache-x64.cc',
+ ],
+ }],
+ ['OS=="linux"', {
+ 'link_settings': {
+ 'conditions': [
+ ['v8_compress_startup_data=="bz2"', {
+ 'libraries': [
+ '-lbz2',
+ ]
+ }],
+ ],
+ },
+ 'sources': [ ### gcmole(os:linux) ###
+ '../../src/platform-linux.cc',
+ '../../src/platform-posix.cc'
+ ],
+ }
+ ],
+ ['OS=="android"', {
+ 'defines': [
+ 'CAN_USE_VFP_INSTRUCTIONS',
+ ],
+ 'sources': [
+ '../../src/platform-posix.cc',
+ ],
+ 'conditions': [
+ ['host_os=="mac"', {
+ 'target_conditions': [
+ ['_toolset=="host"', {
+ 'sources': [
+ '../../src/platform-macos.cc'
+ ]
+ }, {
+ 'sources': [
+ '../../src/platform-linux.cc'
+ ]
+ }],
+ ],
+ }, {
+ 'sources': [
+ '../../src/platform-linux.cc'
+ ]
+ }],
+ ],
+ },
+ ],
+ ['OS=="freebsd"', {
+ 'link_settings': {
+ 'libraries': [
+ '-L/usr/local/lib -lexecinfo',
+ ]},
+ 'sources': [
+ '../../src/platform-freebsd.cc',
+ '../../src/platform-posix.cc'
+ ],
+ }
+ ],
+ ['OS=="openbsd"', {
+ 'link_settings': {
+ 'libraries': [
+ '-L/usr/local/lib -lexecinfo',
+ ]},
+ 'sources': [
+ '../../src/platform-openbsd.cc',
+ '../../src/platform-posix.cc'
+ ],
+ }
+ ],
+ ['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',
+ ],
+ }
+ ],
+ ['OS=="mac"', {
+ 'sources': [
+ '../../src/platform-macos.cc',
+ '../../src/platform-posix.cc'
+ ]},
+ ],
+ ['OS=="win"', {
+ 'variables': {
+ 'gyp_generators': '<!(echo $GYP_GENERATORS)',
+ },
+ 'conditions': [
+ ['gyp_generators=="make"', {
+ 'variables': {
+ 'build_env': '<!(uname -o)',
+ },
+ 'conditions': [
+ ['build_env=="Cygwin"', {
+ 'sources': [
+ '../../src/platform-cygwin.cc',
+ '../../src/platform-posix.cc',
+ ],
+ }, {
+ 'sources': [
+ '../../src/platform-win32.cc',
+ '../../src/win32-math.h',
+ '../../src/win32-math.cc',
+ ],
+ }],
+ ],
+ 'link_settings': {
+ 'libraries': [ '-lwinmm', '-lws2_32' ],
+ },
+ }, {
+ 'sources': [
+ '../../src/platform-win32.cc',
+ '../../src/win32-math.h',
+ '../../src/win32-math.cc',
+ ],
+ 'msvs_disabled_warnings': [4351, 4355, 4800],
+ 'link_settings': {
+ 'libraries': [ '-lwinmm.lib', '-lws2_32.lib' ],
+ },
+ }],
+ ],
+ }],
+ ['component=="shared_library"', {
+ 'defines': [
+ 'BUILDING_V8_SHARED',
+ 'V8_SHARED',
+ ],
+ }],
+ ['v8_postmortem_support=="true"', {
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/debug-support.cc',
+ ]
+ }],
+ ['v8_enable_i18n_support==1', {
+ 'sources': [
+ '../../src/i18n.cc',
+ '../../src/i18n.h',
+ '../../src/extensions/i18n/break-iterator.cc',
+ '../../src/extensions/i18n/break-iterator.h',
+ '../../src/extensions/i18n/i18n-extension.cc',
+ '../../src/extensions/i18n/i18n-extension.h',
+ '../../src/extensions/i18n/i18n-utils.cc',
+ '../../src/extensions/i18n/i18n-utils.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu.gyp:icui18n',
+ '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
+ ]
+ }],
+ ['OS=="win" and v8_enable_i18n_support==1', {
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu.gyp:icudata',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'js2c',
+ 'type': 'none',
+ 'conditions': [
+ ['want_separate_host_toolset==1', {
+ 'toolsets': ['host'],
+ }, {
+ 'toolsets': ['target'],
+ }],
+ ['v8_enable_i18n_support==1', {
+ 'actions': [{
+ 'action_name': 'js2c_i18n',
+ 'inputs': [
+ '../../tools/js2c.py',
+ '<@(i18n_library_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-libraries.cc',
+ ],
+ 'action': [
+ 'python',
+ '../../tools/js2c.py',
+ '<@(_outputs)',
+ 'I18N',
+ '<(v8_compress_startup_data)',
+ '<@(i18n_library_files)'
+ ],
+ }],
+ }],
+ ],
+ 'variables': {
+ 'library_files': [
+ '../../src/runtime.js',
+ '../../src/v8natives.js',
+ '../../src/array.js',
+ '../../src/string.js',
+ '../../src/uri.js',
+ '../../src/math.js',
+ '../../src/messages.js',
+ '../../src/apinatives.js',
+ '../../src/debug-debugger.js',
+ '../../src/mirror-debugger.js',
+ '../../src/liveedit-debugger.js',
+ '../../src/date.js',
+ '../../src/json.js',
+ '../../src/regexp.js',
+ '../../src/macros.py',
+ ],
+ 'experimental_library_files': [
+ '../../src/macros.py',
+ '../../src/symbol.js',
+ '../../src/proxy.js',
+ '../../src/collection.js',
+ '../../src/object-observe.js',
+ '../../src/arraybuffer.js',
+ '../../src/typedarray.js',
+ '../../src/generator.js',
+ '../../src/array-iterator.js',
+ '../../src/harmony-string.js',
+ '../../src/harmony-array.js',
+ ],
+ 'i18n_library_files': [
+ '../../src/extensions/i18n/header.js',
+ '../../src/extensions/i18n/globals.js',
+ '../../src/extensions/i18n/locale.js',
+ '../../src/extensions/i18n/collator.js',
+ '../../src/extensions/i18n/number-format.js',
+ '../../src/extensions/i18n/date-format.js',
+ '../../src/extensions/i18n/break-iterator.js',
+ '../../src/extensions/i18n/i18n-utils.js',
+ '../../src/extensions/i18n/overrides.js',
+ '../../src/extensions/i18n/footer.js',
+ ],
+ },
+ 'actions': [
+ {
+ 'action_name': 'js2c',
+ 'inputs': [
+ '../../tools/js2c.py',
+ '<@(library_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/libraries.cc',
+ ],
+ 'action': [
+ 'python',
+ '../../tools/js2c.py',
+ '<@(_outputs)',
+ 'CORE',
+ '<(v8_compress_startup_data)',
+ '<@(library_files)'
+ ],
+ },
+ {
+ 'action_name': 'js2c_experimental',
+ 'inputs': [
+ '../../tools/js2c.py',
+ '<@(experimental_library_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/experimental-libraries.cc',
+ ],
+ 'action': [
+ 'python',
+ '../../tools/js2c.py',
+ '<@(_outputs)',
+ 'EXPERIMENTAL',
+ '<(v8_compress_startup_data)',
+ '<@(experimental_library_files)'
+ ],
+ },
+ ],
+ },
+ {
+ '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.<(v8_target_arch)',
+ 'type': 'executable',
+ 'dependencies': [
+ 'v8_base.<(v8_target_arch)',
+ 'v8_nosnapshot.<(v8_target_arch)',
+ ],
+ 'include_dirs+': [
+ '../../src',
+ ],
+ 'sources': [
+ '../../src/mksnapshot.cc',
+ ],
+ 'conditions': [
+ ['want_separate_host_toolset==1', {
+ 'toolsets': ['host'],
+ }, {
+ 'toolsets': ['target'],
+ }],
+ ['v8_compress_startup_data=="bz2"', {
+ 'libraries': [
+ '-lbz2',
+ ]
+ }],
+ ],
+ },
+ {
+ 'target_name': 'v8_shell',
+ 'type': 'executable',
+ 'dependencies': [
+ 'v8'
+ ],
+ 'sources': [
+ '../../samples/shell.cc',
+ ],
+ 'conditions': [
+ ['want_separate_host_toolset==1', {
+ 'toolsets': ['host'],
+ }, {
+ 'toolsets': ['target'],
+ }],
+ ['OS=="win"', {
+ # This could be gotten by not setting chromium_code, if that's OK.
+ 'defines': ['_CRT_SECURE_NO_WARNINGS'],
+ }],
+ ['v8_compress_startup_data=="bz2"', {
+ 'libraries': [
+ '-lbz2',
+ ]
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/v8/tools/js2c.py b/chromium/v8/tools/js2c.py
new file mode 100644
index 00000000000..9492b0030c0
--- /dev/null
+++ b/chromium/v8/tools/js2c.py
@@ -0,0 +1,396 @@
+#!/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.
+
+# This is a utility for converting JavaScript source code into C-style
+# char arrays. It is used for embedded JavaScript code in the V8
+# library.
+
+import os, re, sys, string
+import jsmin
+import bz2
+
+
+def ToCAsciiArray(lines):
+ result = []
+ for chr in lines:
+ value = ord(chr)
+ assert value < 128
+ result.append(str(value))
+ return ", ".join(result)
+
+
+def ToCArray(lines):
+ result = []
+ for chr in lines:
+ result.append(str(ord(chr)))
+ 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
+
+
+def ReadLines(filename):
+ result = []
+ for line in open(filename, "rt"):
+ if '#' in line:
+ line = line[:line.index('#')]
+ line = line.strip()
+ if len(line) > 0:
+ result.append(line)
+ return result
+
+
+def LoadConfigFrom(name):
+ import ConfigParser
+ config = ConfigParser.ConfigParser()
+ config.read(name)
+ return config
+
+
+def ParseValue(string):
+ string = string.strip()
+ if string.startswith('[') and string.endswith(']'):
+ return string.lstrip('[').rstrip(']').split()
+ else:
+ return string
+
+
+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)
+
+
+def ExpandConstants(lines, constants):
+ for key, value in constants:
+ lines = key.sub(str(value), lines)
+ return lines
+
+
+def ExpandMacros(lines, macros):
+ # We allow macros to depend on the previously declared macros, but
+ # we don't allow self-dependecies or recursion.
+ for name_pattern, macro in reversed(macros):
+ pattern_match = name_pattern.search(lines, 0)
+ while pattern_match is not None:
+ # Scan over the arguments
+ height = 1
+ start = pattern_match.start()
+ end = pattern_match.end()
+ assert lines[end - 1] == '('
+ last_match = end
+ 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[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:
+ add_arg(lines[last_match:end])
+ last_match = end + 1
+ elif lines[end] in ['(', '{', '[']:
+ height = height + 1
+ elif lines[end] in [')', '}', ']']:
+ height = height - 1
+ end = end + 1
+ # Remember to add the last match.
+ add_arg(lines[last_match:end-1])
+ result = macro.expand(mapping)
+ # Replace the occurrence of the macro with the expansion
+ lines = lines[:start] + result + lines[end:]
+ pattern_match = name_pattern.search(lines, start + len(result))
+ return lines
+
+class TextMacro:
+ def __init__(self, args, body):
+ self.args = args
+ self.body = body
+ def expand(self, mapping):
+ result = self.body
+ for key, value in mapping.items():
+ result = result.replace(key, value)
+ return result
+
+class PythonMacro:
+ def __init__(self, args, fun):
+ self.args = args
+ self.fun = fun
+ def expand(self, mapping):
+ args = []
+ for arg in self.args:
+ args.append(mapping[arg])
+ return str(self.fun(*args))
+
+CONST_PATTERN = re.compile(r'^const\s+([a-zA-Z0-9_]+)\s*=\s*([^;]*);$')
+MACRO_PATTERN = re.compile(r'^macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$')
+PYTHON_MACRO_PATTERN = re.compile(r'^python\s+macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$')
+
+
+def ReadMacros(lines):
+ constants = []
+ macros = []
+ for line in lines:
+ hash = line.find('#')
+ if hash != -1: line = line[:hash]
+ line = line.strip()
+ if len(line) is 0: continue
+ const_match = CONST_PATTERN.match(line)
+ if const_match:
+ name = const_match.group(1)
+ value = const_match.group(2).strip()
+ constants.append((re.compile("\\b%s\\b" % name), value))
+ else:
+ macro_match = MACRO_PATTERN.match(line)
+ if macro_match:
+ name = macro_match.group(1)
+ args = [match.strip() for match in macro_match.group(2).split(',')]
+ body = macro_match.group(3).strip()
+ macros.append((re.compile("\\b%s\\(" % name), TextMacro(args, body)))
+ else:
+ python_match = PYTHON_MACRO_PATTERN.match(line)
+ if python_match:
+ name = python_match.group(1)
+ args = [match.strip() for match in python_match.group(2).split(',')]
+ body = python_match.group(3).strip()
+ fun = eval("lambda " + ",".join(args) + ': ' + body)
+ macros.append((re.compile("\\b%s\\(" % name), PythonMacro(args, fun)))
+ else:
+ raise ("Illegal line: " + line)
+ return (constants, macros)
+
+
+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 GYP script.
+
+#include "v8.h"
+#include "natives.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+ static const byte sources[] = { %(sources_data)s };
+
+%(raw_sources_declaration)s\
+
+ template <>
+ int NativesCollection<%(type)s>::GetBuiltinsCount() {
+ return %(builtin_count)i;
+ }
+
+ template <>
+ int NativesCollection<%(type)s>::GetDebuggerCount() {
+ return %(debugger_count)i;
+ }
+
+ template <>
+ int NativesCollection<%(type)s>::GetIndex(const char* name) {
+%(get_index_cases)s\
+ return -1;
+ }
+
+ template <>
+ int NativesCollection<%(type)s>::GetRawScriptsSize() {
+ return %(raw_total_length)i;
+ }
+
+ template <>
+ Vector<const char> NativesCollection<%(type)s>::GetRawScriptSource(int index) {
+%(get_raw_script_source_cases)s\
+ return Vector<const char>("", 0);
+ }
+
+ template <>
+ Vector<const char> NativesCollection<%(type)s>::GetScriptName(int index) {
+%(get_script_name_cases)s\
+ return Vector<const char>("", 0);
+ }
+
+ template <>
+ Vector<const byte> NativesCollection<%(type)s>::GetScriptsSource() {
+ return Vector<const byte>(sources, %(total_length)i);
+ }
+
+ template <>
+ void NativesCollection<%(type)s>::SetRawScriptsSource(Vector<const char> raw_source) {
+ ASSERT(%(raw_total_length)i == raw_source.length());
+ raw_sources = raw_source.start();
+ }
+
+} // internal
+} // v8
+"""
+
+
+RAW_SOURCES_COMPRESSION_DECLARATION = """\
+ static const char* raw_sources = NULL;
+"""
+
+
+RAW_SOURCES_DECLARATION = """\
+ static const char* raw_sources = reinterpret_cast<const char*>(sources);
+"""
+
+
+GET_INDEX_CASE = """\
+ if (strcmp(name, "%(id)s") == 0) return %(i)i;
+"""
+
+
+GET_RAW_SCRIPT_SOURCE_CASE = """\
+ if (index == %(i)i) return Vector<const char>(raw_sources + %(offset)i, %(raw_length)i);
+"""
+
+
+GET_SCRIPT_NAME_CASE = """\
+ if (index == %(i)i) return Vector<const char>("%(name)s", %(length)i);
+"""
+
+def JS2C(source, target, env):
+ ids = []
+ debugger_ids = []
+ modules = []
+ # Locate the macros file name.
+ consts = []
+ macros = []
+ for s in source:
+ if 'macros.py' == (os.path.split(str(s))[1]):
+ (consts, macros) = ReadMacros(ReadLines(str(s)))
+ else:
+ modules.append(s)
+
+ minifier = jsmin.JavaScriptMinifier()
+
+ module_offset = 0
+ all_sources = []
+ for module in modules:
+ filename = str(module)
+ debugger = filename.endswith('-debugger.js')
+ lines = ReadFile(filename)
+ lines = ExpandConstants(lines, consts)
+ lines = ExpandMacros(lines, macros)
+ Validate(lines, filename)
+ lines = minifier.JSMinify(lines)
+ id = (os.path.split(filename)[1])[:-3]
+ if debugger: id = id[:-9]
+ raw_length = len(lines)
+ if debugger:
+ debugger_ids.append((id, raw_length, module_offset))
+ else:
+ ids.append((id, raw_length, module_offset))
+ all_sources.append(lines)
+ module_offset += raw_length
+ total_length = raw_total_length = module_offset
+
+ if env['COMPRESSION'] == 'off':
+ raw_sources_declaration = RAW_SOURCES_DECLARATION
+ sources_data = ToCAsciiArray("".join(all_sources))
+ else:
+ raw_sources_declaration = RAW_SOURCES_COMPRESSION_DECLARATION
+ if env['COMPRESSION'] == 'bz2':
+ all_sources = bz2.compress("".join(all_sources))
+ total_length = len(all_sources)
+ sources_data = ToCArray(all_sources)
+
+ # Build debugger support functions
+ get_index_cases = [ ]
+ get_raw_script_source_cases = [ ]
+ get_script_name_cases = [ ]
+
+ i = 0
+ for (id, raw_length, module_offset) in debugger_ids + ids:
+ native_name = "native %s.js" % id
+ get_index_cases.append(GET_INDEX_CASE % { 'id': id, 'i': i })
+ get_raw_script_source_cases.append(GET_RAW_SCRIPT_SOURCE_CASE % {
+ 'offset': module_offset,
+ 'raw_length': raw_length,
+ 'i': i
+ })
+ get_script_name_cases.append(GET_SCRIPT_NAME_CASE % {
+ 'name': native_name,
+ 'length': len(native_name),
+ 'i': i
+ })
+ i = i + 1
+
+ # Emit result
+ output = open(str(target[0]), "w")
+ output.write(HEADER_TEMPLATE % {
+ 'builtin_count': len(ids) + len(debugger_ids),
+ 'debugger_count': len(debugger_ids),
+ 'sources_data': sources_data,
+ 'raw_sources_declaration': raw_sources_declaration,
+ 'raw_total_length': raw_total_length,
+ 'total_length': total_length,
+ 'get_index_cases': "".join(get_index_cases),
+ 'get_raw_script_source_cases': "".join(get_raw_script_source_cases),
+ 'get_script_name_cases': "".join(get_script_name_cases),
+ 'type': env['TYPE']
+ })
+ output.close()
+
+def main():
+ natives = sys.argv[1]
+ type = sys.argv[2]
+ compression = sys.argv[3]
+ source_files = sys.argv[4:]
+ JS2C(source_files, [natives], { 'TYPE': type, 'COMPRESSION': compression })
+
+if __name__ == "__main__":
+ main()
diff --git a/chromium/v8/tools/jsmin.py b/chromium/v8/tools/jsmin.py
new file mode 100644
index 00000000000..250dea9d72b
--- /dev/null
+++ b/chromium/v8/tools/jsmin.py
@@ -0,0 +1,282 @@
+#!/usr/bin/python2.4
+
+# Copyright 2012 the V8 project authors. 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.
+
+"""A JavaScript minifier.
+
+It is far from being a complete JS parser, so there are many valid
+JavaScript programs that will be ruined by it. Another strangeness is that
+it accepts $ and % as parts of identifiers. It doesn't merge lines or strip
+out blank lines in order to ease debugging. Variables at the top scope are
+properties of the global object so we can't rename them. It is assumed that
+you introduce variables with var as if JavaScript followed C++ scope rules
+around curly braces, so the declaration must be above the first use.
+
+Use as:
+import jsmin
+minifier = JavaScriptMinifier()
+program1 = minifier.JSMinify(program1)
+program2 = minifier.JSMinify(program2)
+"""
+
+import re
+
+
+class JavaScriptMinifier(object):
+ """An object that you can feed code snippets to to get them minified."""
+
+ def __init__(self):
+ # We prepopulate the list of identifiers that shouldn't be used. These
+ # short language keywords could otherwise be used by the script as variable
+ # names.
+ self.seen_identifiers = {"do": True, "in": True}
+ self.identifier_counter = 0
+ self.in_comment = False
+ self.map = {}
+ self.nesting = 0
+
+ def LookAtIdentifier(self, m):
+ """Records identifiers or keywords that we see in use.
+
+ (So we can avoid renaming variables to these strings.)
+ Args:
+ m: The match object returned by re.search.
+
+ Returns:
+ Nothing.
+ """
+ identifier = m.group(1)
+ self.seen_identifiers[identifier] = True
+
+ def Push(self):
+ """Called when we encounter a '{'."""
+ self.nesting += 1
+
+ def Pop(self):
+ """Called when we encounter a '}'."""
+ self.nesting -= 1
+ # We treat each top-level opening brace as a single scope that can span
+ # several sets of nested braces.
+ if self.nesting == 0:
+ self.map = {}
+ self.identifier_counter = 0
+
+ def Declaration(self, m):
+ """Rewrites bits of the program selected by a regexp.
+
+ These can be curly braces, literal strings, function declarations and var
+ declarations. (These last two must be on one line including the opening
+ curly brace of the function for their variables to be renamed).
+
+ Args:
+ m: The match object returned by re.search.
+
+ Returns:
+ The string that should replace the match in the rewritten program.
+ """
+ matched_text = m.group(0)
+ if matched_text == "{":
+ self.Push()
+ return matched_text
+ if matched_text == "}":
+ self.Pop()
+ return matched_text
+ if re.match("[\"'/]", matched_text):
+ return matched_text
+ m = re.match(r"var ", matched_text)
+ if m:
+ var_names = matched_text[m.end():]
+ var_names = re.split(r",", var_names)
+ return "var " + ",".join(map(self.FindNewName, var_names))
+ m = re.match(r"(function\b[^(]*)\((.*)\)\{$", matched_text)
+ if m:
+ up_to_args = m.group(1)
+ args = m.group(2)
+ args = re.split(r",", args)
+ self.Push()
+ return up_to_args + "(" + ",".join(map(self.FindNewName, args)) + "){"
+
+ if matched_text in self.map:
+ return self.map[matched_text]
+
+ return matched_text
+
+ def CharFromNumber(self, number):
+ """A single-digit base-52 encoding using a-zA-Z."""
+ if number < 26:
+ return chr(number + 97)
+ number -= 26
+ return chr(number + 65)
+
+ def FindNewName(self, var_name):
+ """Finds a new 1-character or 2-character name for a variable.
+
+ Enters it into the mapping table for this scope.
+
+ Args:
+ var_name: The name of the variable before renaming.
+
+ Returns:
+ The new name of the variable.
+ """
+ new_identifier = ""
+ # Variable names that end in _ are member variables of the global object,
+ # so they can be visible from code in a different scope. We leave them
+ # alone.
+ if var_name in self.map:
+ return self.map[var_name]
+ if self.nesting == 0:
+ return var_name
+ while True:
+ identifier_first_char = self.identifier_counter % 52
+ identifier_second_char = self.identifier_counter // 52
+ new_identifier = self.CharFromNumber(identifier_first_char)
+ if identifier_second_char != 0:
+ new_identifier = (
+ self.CharFromNumber(identifier_second_char - 1) + new_identifier)
+ self.identifier_counter += 1
+ if not new_identifier in self.seen_identifiers:
+ break
+
+ self.map[var_name] = new_identifier
+ return new_identifier
+
+ def RemoveSpaces(self, m):
+ """Returns literal strings unchanged, replaces other inputs with group 2.
+
+ Other inputs are replaced with the contents of capture 1. This is either
+ a single space or an empty string.
+
+ Args:
+ m: The match object returned by re.search.
+
+ Returns:
+ The string that should be inserted instead of the matched text.
+ """
+ entire_match = m.group(0)
+ replacement = m.group(1)
+ if re.match(r"'.*'$", entire_match):
+ return entire_match
+ if re.match(r'".*"$', entire_match):
+ return entire_match
+ if re.match(r"/.+/$", entire_match):
+ return entire_match
+ return replacement
+
+ def JSMinify(self, text):
+ """The main entry point. Takes a text and returns a compressed version.
+
+ The compressed version hopefully does the same thing. Line breaks are
+ preserved.
+
+ Args:
+ text: The text of the code snippet as a multiline string.
+
+ Returns:
+ The compressed text of the code snippet as a multiline string.
+ """
+ new_lines = []
+ for line in re.split(r"\n", text):
+ line = line.replace("\t", " ")
+ if self.in_comment:
+ m = re.search(r"\*/", line)
+ if m:
+ line = line[m.end():]
+ self.in_comment = False
+ else:
+ new_lines.append("")
+ continue
+
+ if not self.in_comment:
+ line = re.sub(r"/\*.*?\*/", " ", line)
+ line = re.sub(r"//.*", "", line)
+ m = re.search(r"/\*", line)
+ if m:
+ line = line[:m.start()]
+ self.in_comment = True
+
+ # Strip leading and trailing spaces.
+ line = re.sub(r"^ +", "", line)
+ line = re.sub(r" +$", "", line)
+ # A regexp that matches a literal string surrounded by "double quotes".
+ # This regexp can handle embedded backslash-escaped characters including
+ # embedded backslash-escaped double quotes.
+ double_quoted_string = r'"(?:[^"\\]|\\.)*"'
+ # A regexp that matches a literal string surrounded by 'double quotes'.
+ single_quoted_string = r"'(?:[^'\\]|\\.)*'"
+ # A regexp that matches a regexp literal surrounded by /slashes/.
+ # Don't allow a regexp to have a ) before the first ( since that's a
+ # syntax error and it's probably just two unrelated slashes.
+ # Also don't allow it to come after anything that can only be the
+ # end of a primary expression.
+ slash_quoted_regexp = r"(?<![\w$'\")\]])/(?:(?=\()|(?:[^()/\\]|\\.)+)(?:\([^/\\]|\\.)*/"
+ # Replace multiple spaces with a single space.
+ line = re.sub("|".join([double_quoted_string,
+ single_quoted_string,
+ slash_quoted_regexp,
+ "( )+"]),
+ self.RemoveSpaces,
+ line)
+ # Strip single spaces unless they have an identifier character both before
+ # and after the space. % and $ are counted as identifier characters.
+ line = re.sub("|".join([double_quoted_string,
+ single_quoted_string,
+ slash_quoted_regexp,
+ r"(?<![a-zA-Z_0-9$%]) | (?![a-zA-Z_0-9$%])()"]),
+ self.RemoveSpaces,
+ line)
+ # Collect keywords and identifiers that are already in use.
+ if self.nesting == 0:
+ re.sub(r"([a-zA-Z0-9_$%]+)", self.LookAtIdentifier, line)
+ function_declaration_regexp = (
+ r"\bfunction" # Function definition keyword...
+ r"( [\w$%]+)?" # ...optional function name...
+ r"\([\w$%,]+\)\{") # ...argument declarations.
+ # Unfortunately the keyword-value syntax { key:value } makes the key look
+ # like a variable where in fact it is a literal string. We use the
+ # presence or absence of a question mark to try to distinguish between
+ # this case and the ternary operator: "condition ? iftrue : iffalse".
+ if re.search(r"\?", line):
+ block_trailing_colon = r""
+ else:
+ block_trailing_colon = r"(?![:\w$%])"
+ # Variable use. Cannot follow a period precede a colon.
+ variable_use_regexp = r"(?<![.\w$%])[\w$%]+" + block_trailing_colon
+ line = re.sub("|".join([double_quoted_string,
+ single_quoted_string,
+ slash_quoted_regexp,
+ r"\{", # Curly braces.
+ r"\}",
+ r"\bvar [\w$%,]+", # var declarations.
+ function_declaration_regexp,
+ variable_use_regexp]),
+ self.Declaration,
+ line)
+ new_lines.append(line)
+
+ return "\n".join(new_lines) + "\n"
diff --git a/chromium/v8/tools/linux-tick-processor b/chromium/v8/tools/linux-tick-processor
new file mode 100755
index 00000000000..93f143f9a95
--- /dev/null
+++ b/chromium/v8/tools/linux-tick-processor
@@ -0,0 +1,40 @@
+#!/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`
+ if [ -x "$d8_public" ]; then D8_PATH=$(dirname "$d8_public"); fi
+fi
+[ -n "$D8_PATH" ] || D8_PATH=$tools_path/..
+d8_exec=$D8_PATH/d8
+
+if [ ! -x "$d8_exec" ]; then
+ 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
+
+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 \
+ $tools_path/csvparser.js $tools_path/consarray.js \
+ $tools_path/profile.js $tools_path/profile_view.js \
+ $tools_path/logreader.js $tools_path/tickprocessor.js \
+ $tools_path/tickprocessor-driver.js -- $@ 2>/dev/null
diff --git a/chromium/v8/tools/ll_prof.py b/chromium/v8/tools/ll_prof.py
new file mode 100755
index 00000000000..216929d1e20
--- /dev/null
+++ b/chromium/v8/tools/ll_prof.py
@@ -0,0 +1,1011 @@
+#!/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.
+
+import bisect
+import collections
+import ctypes
+import disasm
+import mmap
+import optparse
+import os
+import re
+import subprocess
+import sys
+import time
+
+
+USAGE="""usage: %prog [OPTION]...
+
+Analyses V8 and perf logs to produce profiles.
+
+Perf logs can be collected using a command like:
+ $ perf record -R -e cycles -c 10000 -f -i ./d8 bench.js --ll-prof
+ # -R: collect all data
+ # -e cycles: use cpu-cycles event (run "perf list" for details)
+ # -c 10000: write a sample after each 10000 events
+ # -f: force output file overwrite
+ # -i: limit profiling to our process and the kernel
+ # --ll-prof shell flag enables the right V8 logs
+This will produce a binary trace file (perf.data) that %prog can analyse.
+
+IMPORTANT:
+ The kernel has an internal maximum for events per second, it is 100K by
+ default. That's not enough for "-c 10000". Set it to some higher value:
+ $ echo 10000000 | sudo tee /proc/sys/kernel/perf_event_max_sample_rate
+ You can also make the warning about kernel address maps go away:
+ $ echo 0 | sudo tee /proc/sys/kernel/kptr_restrict
+
+We have a convenience script that handles all of the above for you:
+ $ tools/run-llprof.sh ./d8 bench.js
+
+Examples:
+ # Print flat profile with annotated disassembly for the 10 top
+ # symbols. Use default log names and include the snapshot log.
+ $ %prog --snapshot --disasm-top=10
+
+ # Print flat profile with annotated disassembly for all used symbols.
+ # Use default log names and include kernel symbols into analysis.
+ $ %prog --disasm-all --kernel
+
+ # Print flat profile. Use custom log names.
+ $ %prog --log=foo.log --snapshot-log=snap-foo.log --trace=foo.data --snapshot
+"""
+
+
+JS_ORIGIN = "js"
+JS_SNAPSHOT_ORIGIN = "js-snapshot"
+
+class Code(object):
+ """Code object."""
+
+ _id = 0
+ UNKNOWN = 0
+ V8INTERNAL = 1
+ FULL_CODEGEN = 2
+ OPTIMIZED = 3
+
+ def __init__(self, name, start_address, end_address, origin, origin_offset):
+ self.id = Code._id
+ Code._id += 1
+ self.name = name
+ self.other_names = None
+ self.start_address = start_address
+ self.end_address = end_address
+ self.origin = origin
+ self.origin_offset = origin_offset
+ self.self_ticks = 0
+ self.self_ticks_map = None
+ self.callee_ticks = None
+ if name.startswith("LazyCompile:*"):
+ self.codetype = Code.OPTIMIZED
+ elif name.startswith("LazyCompile:"):
+ self.codetype = Code.FULL_CODEGEN
+ elif name.startswith("v8::internal::"):
+ self.codetype = Code.V8INTERNAL
+ else:
+ self.codetype = Code.UNKNOWN
+
+ def AddName(self, name):
+ assert self.name != name
+ if self.other_names is None:
+ self.other_names = [name]
+ return
+ if not name in self.other_names:
+ self.other_names.append(name)
+
+ def FullName(self):
+ if self.other_names is None:
+ return self.name
+ self.other_names.sort()
+ return "%s (aka %s)" % (self.name, ", ".join(self.other_names))
+
+ def IsUsed(self):
+ return self.self_ticks > 0 or self.callee_ticks is not None
+
+ def Tick(self, pc):
+ self.self_ticks += 1
+ if self.self_ticks_map is None:
+ self.self_ticks_map = collections.defaultdict(lambda: 0)
+ offset = pc - self.start_address
+ self.self_ticks_map[offset] += 1
+
+ def CalleeTick(self, callee):
+ if self.callee_ticks is None:
+ self.callee_ticks = collections.defaultdict(lambda: 0)
+ self.callee_ticks[callee] += 1
+
+ def PrintAnnotated(self, arch, options):
+ if self.self_ticks_map is None:
+ ticks_map = []
+ else:
+ ticks_map = self.self_ticks_map.items()
+ # Convert the ticks map to offsets and counts arrays so that later
+ # we can do binary search in the offsets array.
+ ticks_map.sort(key=lambda t: t[0])
+ ticks_offsets = [t[0] for t in ticks_map]
+ ticks_counts = [t[1] for t in ticks_map]
+ # Get a list of disassembled lines and their addresses.
+ lines = self._GetDisasmLines(arch, options)
+ if len(lines) == 0:
+ return
+ # Print annotated lines.
+ address = lines[0][0]
+ total_count = 0
+ for i in xrange(len(lines)):
+ start_offset = lines[i][0] - address
+ if i == len(lines) - 1:
+ end_offset = self.end_address - self.start_address
+ else:
+ end_offset = lines[i + 1][0] - address
+ # Ticks (reported pc values) are not always precise, i.e. not
+ # necessarily point at instruction starts. So we have to search
+ # for ticks that touch the current instruction line.
+ j = bisect.bisect_left(ticks_offsets, end_offset)
+ count = 0
+ for offset, cnt in reversed(zip(ticks_offsets[:j], ticks_counts[:j])):
+ if offset < start_offset:
+ break
+ count += cnt
+ total_count += count
+ count = 100.0 * count / self.self_ticks
+ if count >= 0.01:
+ print "%15.2f %x: %s" % (count, lines[i][0], lines[i][1])
+ else:
+ print "%s %x: %s" % (" " * 15, lines[i][0], lines[i][1])
+ print
+ assert total_count == self.self_ticks, \
+ "Lost ticks (%d != %d) in %s" % (total_count, self.self_ticks, self)
+
+ def __str__(self):
+ return "%s [0x%x, 0x%x) size: %d origin: %s" % (
+ self.name,
+ self.start_address,
+ self.end_address,
+ self.end_address - self.start_address,
+ self.origin)
+
+ def _GetDisasmLines(self, arch, options):
+ if self.origin == JS_ORIGIN or self.origin == JS_SNAPSHOT_ORIGIN:
+ inplace = False
+ filename = options.log + ".ll"
+ else:
+ inplace = True
+ filename = self.origin
+ return disasm.GetDisasmLines(filename,
+ self.origin_offset,
+ self.end_address - self.start_address,
+ arch,
+ inplace)
+
+
+class CodePage(object):
+ """Group of adjacent code objects."""
+
+ SHIFT = 20 # 1M pages
+ SIZE = (1 << SHIFT)
+ MASK = ~(SIZE - 1)
+
+ @staticmethod
+ def PageAddress(address):
+ return address & CodePage.MASK
+
+ @staticmethod
+ def PageId(address):
+ return address >> CodePage.SHIFT
+
+ @staticmethod
+ def PageAddressFromId(id):
+ return id << CodePage.SHIFT
+
+ def __init__(self, address):
+ self.address = address
+ self.code_objects = []
+
+ def Add(self, code):
+ self.code_objects.append(code)
+
+ def Remove(self, code):
+ self.code_objects.remove(code)
+
+ def Find(self, pc):
+ code_objects = self.code_objects
+ for i, code in enumerate(code_objects):
+ if code.start_address <= pc < code.end_address:
+ code_objects[0], code_objects[i] = code, code_objects[0]
+ return code
+ return None
+
+ def __iter__(self):
+ return self.code_objects.__iter__()
+
+
+class CodeMap(object):
+ """Code object map."""
+
+ def __init__(self):
+ self.pages = {}
+ self.min_address = 1 << 64
+ self.max_address = -1
+
+ def Add(self, code, max_pages=-1):
+ page_id = CodePage.PageId(code.start_address)
+ limit_id = CodePage.PageId(code.end_address + CodePage.SIZE - 1)
+ pages = 0
+ while page_id < limit_id:
+ if max_pages >= 0 and pages > max_pages:
+ print >>sys.stderr, \
+ "Warning: page limit (%d) reached for %s [%s]" % (
+ max_pages, code.name, code.origin)
+ break
+ if page_id in self.pages:
+ page = self.pages[page_id]
+ else:
+ page = CodePage(CodePage.PageAddressFromId(page_id))
+ self.pages[page_id] = page
+ page.Add(code)
+ page_id += 1
+ pages += 1
+ self.min_address = min(self.min_address, code.start_address)
+ self.max_address = max(self.max_address, code.end_address)
+
+ def Remove(self, code):
+ page_id = CodePage.PageId(code.start_address)
+ limit_id = CodePage.PageId(code.end_address + CodePage.SIZE - 1)
+ removed = False
+ while page_id < limit_id:
+ if page_id not in self.pages:
+ page_id += 1
+ continue
+ page = self.pages[page_id]
+ page.Remove(code)
+ removed = True
+ page_id += 1
+ return removed
+
+ def AllCode(self):
+ for page in self.pages.itervalues():
+ for code in page:
+ if CodePage.PageAddress(code.start_address) == page.address:
+ yield code
+
+ def UsedCode(self):
+ for code in self.AllCode():
+ if code.IsUsed():
+ yield code
+
+ def Print(self):
+ for code in self.AllCode():
+ print code
+
+ def Find(self, pc):
+ if pc < self.min_address or pc >= self.max_address:
+ return None
+ page_id = CodePage.PageId(pc)
+ if page_id not in self.pages:
+ return None
+ return self.pages[page_id].Find(pc)
+
+
+class CodeInfo(object):
+ """Generic info about generated code objects."""
+
+ def __init__(self, arch, header_size):
+ self.arch = arch
+ self.header_size = header_size
+
+
+class SnapshotLogReader(object):
+ """V8 snapshot log reader."""
+
+ _SNAPSHOT_CODE_NAME_RE = re.compile(
+ r"snapshot-code-name,(\d+),\"(.*)\"")
+
+ def __init__(self, log_name):
+ self.log_name = log_name
+
+ def ReadNameMap(self):
+ log = open(self.log_name, "r")
+ try:
+ snapshot_pos_to_name = {}
+ for line in log:
+ match = SnapshotLogReader._SNAPSHOT_CODE_NAME_RE.match(line)
+ if match:
+ pos = int(match.group(1))
+ name = match.group(2)
+ snapshot_pos_to_name[pos] = name
+ finally:
+ log.close()
+ return snapshot_pos_to_name
+
+
+class LogReader(object):
+ """V8 low-level (binary) log reader."""
+
+ _ARCH_TO_POINTER_TYPE_MAP = {
+ "ia32": ctypes.c_uint32,
+ "arm": ctypes.c_uint32,
+ "mips": ctypes.c_uint32,
+ "x64": ctypes.c_uint64
+ }
+
+ _CODE_CREATE_TAG = "C"
+ _CODE_MOVE_TAG = "M"
+ _CODE_DELETE_TAG = "D"
+ _SNAPSHOT_POSITION_TAG = "P"
+ _CODE_MOVING_GC_TAG = "G"
+
+ def __init__(self, log_name, code_map, snapshot_pos_to_name):
+ self.log_file = open(log_name, "r")
+ self.log = mmap.mmap(self.log_file.fileno(), 0, mmap.MAP_PRIVATE)
+ self.log_pos = 0
+ self.code_map = code_map
+ self.snapshot_pos_to_name = snapshot_pos_to_name
+ self.address_to_snapshot_name = {}
+
+ self.arch = self.log[:self.log.find("\0")]
+ self.log_pos += len(self.arch) + 1
+ assert self.arch in LogReader._ARCH_TO_POINTER_TYPE_MAP, \
+ "Unsupported architecture %s" % self.arch
+ pointer_type = LogReader._ARCH_TO_POINTER_TYPE_MAP[self.arch]
+
+ self.code_create_struct = LogReader._DefineStruct([
+ ("name_size", ctypes.c_int32),
+ ("code_address", pointer_type),
+ ("code_size", ctypes.c_int32)])
+
+ self.code_move_struct = LogReader._DefineStruct([
+ ("from_address", pointer_type),
+ ("to_address", pointer_type)])
+
+ self.code_delete_struct = LogReader._DefineStruct([
+ ("address", pointer_type)])
+
+ self.snapshot_position_struct = LogReader._DefineStruct([
+ ("address", pointer_type),
+ ("position", ctypes.c_int32)])
+
+ def ReadUpToGC(self):
+ while self.log_pos < self.log.size():
+ tag = self.log[self.log_pos]
+ self.log_pos += 1
+
+ if tag == LogReader._CODE_MOVING_GC_TAG:
+ self.address_to_snapshot_name.clear()
+ return
+
+ if tag == LogReader._CODE_CREATE_TAG:
+ event = self.code_create_struct.from_buffer(self.log, self.log_pos)
+ self.log_pos += ctypes.sizeof(event)
+ start_address = event.code_address
+ end_address = start_address + event.code_size
+ if start_address in self.address_to_snapshot_name:
+ name = self.address_to_snapshot_name[start_address]
+ origin = JS_SNAPSHOT_ORIGIN
+ else:
+ name = self.log[self.log_pos:self.log_pos + event.name_size]
+ origin = JS_ORIGIN
+ self.log_pos += event.name_size
+ origin_offset = self.log_pos
+ self.log_pos += event.code_size
+ code = Code(name, start_address, end_address, origin, origin_offset)
+ conficting_code = self.code_map.Find(start_address)
+ if conficting_code:
+ 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
+
+ if tag == LogReader._CODE_MOVE_TAG:
+ event = self.code_move_struct.from_buffer(self.log, self.log_pos)
+ self.log_pos += ctypes.sizeof(event)
+ old_start_address = event.from_address
+ new_start_address = event.to_address
+ if old_start_address == new_start_address:
+ # Skip useless code move entries.
+ continue
+ code = self.code_map.Find(old_start_address)
+ if not code:
+ print >>sys.stderr, "Warning: Not found %x" % old_start_address
+ continue
+ assert code.start_address == old_start_address, \
+ "Inexact move address %x for %s" % (old_start_address, code)
+ self.code_map.Remove(code)
+ size = code.end_address - code.start_address
+ code.start_address = new_start_address
+ code.end_address = new_start_address + size
+ self.code_map.Add(code)
+ continue
+
+ if tag == LogReader._CODE_DELETE_TAG:
+ event = self.code_delete_struct.from_buffer(self.log, self.log_pos)
+ self.log_pos += ctypes.sizeof(event)
+ old_start_address = event.address
+ code = self.code_map.Find(old_start_address)
+ if not code:
+ print >>sys.stderr, "Warning: Not found %x" % old_start_address
+ continue
+ assert code.start_address == old_start_address, \
+ "Inexact delete address %x for %s" % (old_start_address, code)
+ self.code_map.Remove(code)
+ continue
+
+ if tag == LogReader._SNAPSHOT_POSITION_TAG:
+ event = self.snapshot_position_struct.from_buffer(self.log,
+ self.log_pos)
+ self.log_pos += ctypes.sizeof(event)
+ start_address = event.address
+ snapshot_pos = event.position
+ if snapshot_pos in self.snapshot_pos_to_name:
+ self.address_to_snapshot_name[start_address] = \
+ self.snapshot_pos_to_name[snapshot_pos]
+ continue
+
+ assert False, "Unknown tag %s" % tag
+
+ def Dispose(self):
+ self.log.close()
+ self.log_file.close()
+
+ @staticmethod
+ def _DefineStruct(fields):
+ class Struct(ctypes.Structure):
+ _fields_ = fields
+ return Struct
+
+ @staticmethod
+ def _HandleCodeConflict(old_code, new_code):
+ assert (old_code.start_address == new_code.start_address and
+ old_code.end_address == new_code.end_address), \
+ "Conficting code log entries %s and %s" % (old_code, new_code)
+ if old_code.name == new_code.name:
+ return
+ # Code object may be shared by a few functions. Collect the full
+ # set of names.
+ old_code.AddName(new_code.name)
+
+
+class Descriptor(object):
+ """Descriptor of a structure in the binary trace log."""
+
+ CTYPE_MAP = {
+ "u16": ctypes.c_uint16,
+ "u32": ctypes.c_uint32,
+ "u64": ctypes.c_uint64
+ }
+
+ def __init__(self, fields):
+ class TraceItem(ctypes.Structure):
+ _fields_ = Descriptor.CtypesFields(fields)
+
+ def __str__(self):
+ return ", ".join("%s: %s" % (field, self.__getattribute__(field))
+ for field, _ in TraceItem._fields_)
+
+ self.ctype = TraceItem
+
+ def Read(self, trace, offset):
+ return self.ctype.from_buffer(trace, offset)
+
+ @staticmethod
+ def CtypesFields(fields):
+ return [(field, Descriptor.CTYPE_MAP[format]) for (field, format) in fields]
+
+
+# Please see http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=tree;f=tools/perf
+# for the gory details.
+
+
+# Reference: struct perf_file_header in kernel/tools/perf/util/header.h
+TRACE_HEADER_DESC = Descriptor([
+ ("magic", "u64"),
+ ("size", "u64"),
+ ("attr_size", "u64"),
+ ("attrs_offset", "u64"),
+ ("attrs_size", "u64"),
+ ("data_offset", "u64"),
+ ("data_size", "u64"),
+ ("event_types_offset", "u64"),
+ ("event_types_size", "u64")
+])
+
+
+# Reference: /usr/include/linux/perf_event.h
+PERF_EVENT_ATTR_DESC = Descriptor([
+ ("type", "u32"),
+ ("size", "u32"),
+ ("config", "u64"),
+ ("sample_period_or_freq", "u64"),
+ ("sample_type", "u64"),
+ ("read_format", "u64"),
+ ("flags", "u64"),
+ ("wakeup_events_or_watermark", "u32"),
+ ("bp_type", "u32"),
+ ("bp_addr", "u64"),
+ ("bp_len", "u64")
+])
+
+
+# Reference: /usr/include/linux/perf_event.h
+PERF_EVENT_HEADER_DESC = Descriptor([
+ ("type", "u32"),
+ ("misc", "u16"),
+ ("size", "u16")
+])
+
+
+# Reference: kernel/events/core.c
+PERF_MMAP_EVENT_BODY_DESC = Descriptor([
+ ("pid", "u32"),
+ ("tid", "u32"),
+ ("addr", "u64"),
+ ("len", "u64"),
+ ("pgoff", "u64")
+])
+
+
+# perf_event_attr.sample_type bits control the set of
+# perf_sample_event fields.
+PERF_SAMPLE_IP = 1 << 0
+PERF_SAMPLE_TID = 1 << 1
+PERF_SAMPLE_TIME = 1 << 2
+PERF_SAMPLE_ADDR = 1 << 3
+PERF_SAMPLE_READ = 1 << 4
+PERF_SAMPLE_CALLCHAIN = 1 << 5
+PERF_SAMPLE_ID = 1 << 6
+PERF_SAMPLE_CPU = 1 << 7
+PERF_SAMPLE_PERIOD = 1 << 8
+PERF_SAMPLE_STREAM_ID = 1 << 9
+PERF_SAMPLE_RAW = 1 << 10
+
+
+# Reference: /usr/include/perf_event.h, the comment for PERF_RECORD_SAMPLE.
+PERF_SAMPLE_EVENT_BODY_FIELDS = [
+ ("ip", "u64", PERF_SAMPLE_IP),
+ ("pid", "u32", PERF_SAMPLE_TID),
+ ("tid", "u32", PERF_SAMPLE_TID),
+ ("time", "u64", PERF_SAMPLE_TIME),
+ ("addr", "u64", PERF_SAMPLE_ADDR),
+ ("id", "u64", PERF_SAMPLE_ID),
+ ("stream_id", "u64", PERF_SAMPLE_STREAM_ID),
+ ("cpu", "u32", PERF_SAMPLE_CPU),
+ ("res", "u32", PERF_SAMPLE_CPU),
+ ("period", "u64", PERF_SAMPLE_PERIOD),
+ # Don't want to handle read format that comes after the period and
+ # before the callchain and has variable size.
+ ("nr", "u64", PERF_SAMPLE_CALLCHAIN)
+ # Raw data follows the callchain and is ignored.
+]
+
+
+PERF_SAMPLE_EVENT_IP_FORMAT = "u64"
+
+
+PERF_RECORD_MMAP = 1
+PERF_RECORD_SAMPLE = 9
+
+
+class TraceReader(object):
+ """Perf (linux-2.6/tools/perf) trace file reader."""
+
+ _TRACE_HEADER_MAGIC = 4993446653023372624
+
+ def __init__(self, trace_name):
+ self.trace_file = open(trace_name, "r")
+ self.trace = mmap.mmap(self.trace_file.fileno(), 0, mmap.MAP_PRIVATE)
+ self.trace_header = TRACE_HEADER_DESC.Read(self.trace, 0)
+ if self.trace_header.magic != TraceReader._TRACE_HEADER_MAGIC:
+ print >>sys.stderr, "Warning: unsupported trace header magic"
+ self.offset = self.trace_header.data_offset
+ self.limit = self.trace_header.data_offset + self.trace_header.data_size
+ assert self.limit <= self.trace.size(), \
+ "Trace data limit exceeds trace file size"
+ self.header_size = ctypes.sizeof(PERF_EVENT_HEADER_DESC.ctype)
+ assert self.trace_header.attrs_size != 0, \
+ "No perf event attributes found in the trace"
+ perf_event_attr = PERF_EVENT_ATTR_DESC.Read(self.trace,
+ self.trace_header.attrs_offset)
+ self.sample_event_body_desc = self._SampleEventBodyDesc(
+ perf_event_attr.sample_type)
+ self.callchain_supported = \
+ (perf_event_attr.sample_type & PERF_SAMPLE_CALLCHAIN) != 0
+ if self.callchain_supported:
+ self.ip_struct = Descriptor.CTYPE_MAP[PERF_SAMPLE_EVENT_IP_FORMAT]
+ self.ip_size = ctypes.sizeof(self.ip_struct)
+
+ def ReadEventHeader(self):
+ if self.offset >= self.limit:
+ return None, 0
+ offset = self.offset
+ header = PERF_EVENT_HEADER_DESC.Read(self.trace, self.offset)
+ self.offset += header.size
+ return header, offset
+
+ def ReadMmap(self, header, offset):
+ mmap_info = PERF_MMAP_EVENT_BODY_DESC.Read(self.trace,
+ offset + self.header_size)
+ # Read null-terminated filename.
+ filename = self.trace[offset + self.header_size + ctypes.sizeof(mmap_info):
+ offset + header.size]
+ mmap_info.filename = HOST_ROOT + filename[:filename.find(chr(0))]
+ return mmap_info
+
+ def ReadSample(self, header, offset):
+ sample = self.sample_event_body_desc.Read(self.trace,
+ offset + self.header_size)
+ if not self.callchain_supported:
+ return sample
+ sample.ips = []
+ offset += self.header_size + ctypes.sizeof(sample)
+ for _ in xrange(sample.nr):
+ sample.ips.append(
+ self.ip_struct.from_buffer(self.trace, offset).value)
+ offset += self.ip_size
+ return sample
+
+ def Dispose(self):
+ self.trace.close()
+ self.trace_file.close()
+
+ def _SampleEventBodyDesc(self, sample_type):
+ assert (sample_type & PERF_SAMPLE_READ) == 0, \
+ "Can't hande read format in samples"
+ fields = [(field, format)
+ for (field, format, bit) in PERF_SAMPLE_EVENT_BODY_FIELDS
+ if (bit & sample_type) != 0]
+ return Descriptor(fields)
+
+
+OBJDUMP_SECTION_HEADER_RE = re.compile(
+ r"^\s*\d+\s(\.\S+)\s+[a-f0-9]")
+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")
+OBJDUMP_SKIP_RE = re.compile(
+ r"^.*ld\.so\.cache$")
+KERNEL_ALLSYMS_FILE = "/proc/kallsyms"
+PERF_KERNEL_ALLSYMS_RE = re.compile(
+ r".*kallsyms.*")
+KERNEL_ALLSYMS_LINE_RE = re.compile(
+ r"^([a-f0-9]+)\s(?:t|T)\s(\S+)$")
+
+
+class LibraryRepo(object):
+ def __init__(self):
+ self.infos = []
+ self.names = set()
+ self.ticks = {}
+
+ def Load(self, mmap_info, code_map, options):
+ # Skip kernel mmaps when requested using the fact that their tid
+ # 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)
+ mmap_info.ticks = 0
+ mmap_info.unique_name = self._UniqueMmapName(mmap_info)
+ if not os.path.exists(mmap_info.filename):
+ return True
+ # Request section headers (-h), symbols (-t), and dynamic symbols
+ # (-T) from objdump.
+ # Unfortunately, section headers span two lines, so we have to
+ # keep the just seen section name (from the first line in each
+ # section header) in the after_section variable.
+ if mmap_info.filename.endswith(".ko"):
+ dynamic_symbols = ""
+ else:
+ dynamic_symbols = "-T"
+ process = subprocess.Popen(
+ "%s -h -t %s -C %s" % (OBJDUMP_BIN, dynamic_symbols, mmap_info.filename),
+ shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ pipe = process.stdout
+ after_section = None
+ code_sections = set()
+ reloc_sections = set()
+ dynamic = False
+ try:
+ for line in pipe:
+ if after_section:
+ if line.find("CODE") != -1:
+ code_sections.add(after_section)
+ if line.find("RELOC") != -1:
+ reloc_sections.add(after_section)
+ after_section = None
+ continue
+
+ match = OBJDUMP_SECTION_HEADER_RE.match(line)
+ if match:
+ after_section = match.group(1)
+ continue
+
+ if OBJDUMP_DYNAMIC_SYMBOLS_START_RE.match(line):
+ dynamic = True
+ continue
+
+ match = OBJDUMP_SYMBOL_LINE_RE.match(line)
+ if match:
+ start_address = int(match.group(1), 16)
+ origin_offset = start_address
+ flags = match.group(2)
+ section = match.group(3)
+ if section in code_sections:
+ if dynamic or section in reloc_sections:
+ start_address += mmap_info.addr
+ size = int(match.group(4), 16)
+ name = match.group(5)
+ origin = mmap_info.filename
+ code_map.Add(Code(name, start_address, start_address + size,
+ origin, origin_offset))
+ finally:
+ pipe.close()
+ assert process.wait() == 0, "Failed to objdump %s" % mmap_info.filename
+
+ def Tick(self, pc):
+ for i, mmap_info in enumerate(self.infos):
+ if mmap_info.addr <= pc < (mmap_info.addr + mmap_info.len):
+ mmap_info.ticks += 1
+ self.infos[0], self.infos[i] = mmap_info, self.infos[0]
+ return True
+ return False
+
+ def _UniqueMmapName(self, mmap_info):
+ name = mmap_info.filename
+ index = 1
+ while name in self.names:
+ name = "%s-%d" % (mmap_info.filename, index)
+ index += 1
+ self.names.add(name)
+ return name
+
+ def _LoadKernelSymbols(self, code_map):
+ if not os.path.exists(KERNEL_ALLSYMS_FILE):
+ print >>sys.stderr, "Warning: %s not found" % KERNEL_ALLSYMS_FILE
+ return False
+ kallsyms = open(KERNEL_ALLSYMS_FILE, "r")
+ code = None
+ for line in kallsyms:
+ match = KERNEL_ALLSYMS_LINE_RE.match(line)
+ if match:
+ start_address = int(match.group(1), 16)
+ end_address = start_address
+ name = match.group(2)
+ if code:
+ code.end_address = start_address
+ code_map.Add(code, 16)
+ code = Code(name, start_address, end_address, "kernel", 0)
+ return True
+
+
+def PrintReport(code_map, library_repo, arch, ticks, options):
+ print "Ticks per symbol:"
+ used_code = [code for code in code_map.UsedCode()]
+ used_code.sort(key=lambda x: x.self_ticks, reverse=True)
+ for i, code in enumerate(used_code):
+ code_ticks = code.self_ticks
+ print "%10d %5.1f%% %s [%s]" % (code_ticks, 100. * code_ticks / ticks,
+ code.FullName(), code.origin)
+ if options.disasm_all or i < options.disasm_top:
+ code.PrintAnnotated(arch, options)
+ print
+ print "Ticks per library:"
+ mmap_infos = [m for m in library_repo.infos if m.ticks > 0]
+ mmap_infos.sort(key=lambda m: m.ticks, reverse=True)
+ for mmap_info in mmap_infos:
+ mmap_ticks = mmap_info.ticks
+ print "%10d %5.1f%% %s" % (mmap_ticks, 100. * mmap_ticks / ticks,
+ mmap_info.unique_name)
+
+
+def PrintDot(code_map, options):
+ print "digraph G {"
+ for code in code_map.UsedCode():
+ if code.self_ticks < 10:
+ continue
+ print "n%d [shape=box,label=\"%s\"];" % (code.id, code.name)
+ if code.callee_ticks:
+ for callee, ticks in code.callee_ticks.iteritems():
+ print "n%d -> n%d [label=\"%d\"];" % (code.id, callee.id, ticks)
+ print "}"
+
+
+if __name__ == "__main__":
+ parser = optparse.OptionParser(USAGE)
+ parser.add_option("--snapshot-log",
+ default="obj/release/snapshot.log",
+ help="V8 snapshot log file name [default: %default]")
+ parser.add_option("--log",
+ default="v8.log",
+ help="V8 log file name [default: %default]")
+ parser.add_option("--snapshot",
+ default=False,
+ action="store_true",
+ help="process V8 snapshot log [default: %default]")
+ parser.add_option("--trace",
+ default="perf.data",
+ help="perf trace file name [default: %default]")
+ parser.add_option("--kernel",
+ default=False,
+ action="store_true",
+ help="process kernel entries [default: %default]")
+ parser.add_option("--disasm-top",
+ default=0,
+ type="int",
+ help=("number of top symbols to disassemble and annotate "
+ "[default: %default]"))
+ parser.add_option("--disasm-all",
+ default=False,
+ action="store_true",
+ help=("disassemble and annotate all used symbols "
+ "[default: %default]"))
+ parser.add_option("--dot",
+ default=False,
+ action="store_true",
+ help="produce dot output (WIP) [default: %default]")
+ parser.add_option("--quiet", "-q",
+ default=False,
+ action="store_true",
+ help="no auxiliary messages [default: %default]")
+ parser.add_option("--gc-fake-mmap",
+ default="/tmp/__v8_gc__",
+ help="gc fake mmap file [default: %default]")
+ parser.add_option("--objdump",
+ default="/usr/bin/objdump",
+ help="objdump tool to use [default: %default]")
+ parser.add_option("--host-root",
+ default="",
+ help="Path to the host root [default: %default]")
+ options, args = parser.parse_args()
+
+ if not options.quiet:
+ if options.snapshot:
+ print "V8 logs: %s, %s, %s.ll" % (options.snapshot_log,
+ options.log,
+ options.log)
+ else:
+ print "V8 log: %s, %s.ll (no snapshot)" % (options.log, options.log)
+ print "Perf trace file: %s" % options.trace
+
+ V8_GC_FAKE_MMAP = options.gc_fake_mmap
+ HOST_ROOT = options.host_root
+ if os.path.exists(options.objdump):
+ disasm.OBJDUMP_BIN = options.objdump
+ OBJDUMP_BIN = options.objdump
+ else:
+ print "Cannot find %s, falling back to default objdump" % options.objdump
+
+ # Stats.
+ events = 0
+ ticks = 0
+ missed_ticks = 0
+ really_missed_ticks = 0
+ optimized_ticks = 0
+ generated_ticks = 0
+ v8_internal_ticks = 0
+ mmap_time = 0
+ sample_time = 0
+
+ # Process the snapshot log to fill the snapshot name map.
+ snapshot_name_map = {}
+ if options.snapshot:
+ snapshot_log_reader = SnapshotLogReader(log_name=options.snapshot_log)
+ snapshot_name_map = snapshot_log_reader.ReadNameMap()
+
+ # Initialize the log reader.
+ code_map = CodeMap()
+ log_reader = LogReader(log_name=options.log + ".ll",
+ code_map=code_map,
+ snapshot_pos_to_name=snapshot_name_map)
+ if not options.quiet:
+ print "Generated code architecture: %s" % log_reader.arch
+ print
+ sys.stdout.flush()
+
+ # Process the code and trace logs.
+ library_repo = LibraryRepo()
+ log_reader.ReadUpToGC()
+ trace_reader = TraceReader(options.trace)
+ while True:
+ header, offset = trace_reader.ReadEventHeader()
+ if not header:
+ break
+ events += 1
+ if header.type == PERF_RECORD_MMAP:
+ start = time.time()
+ mmap_info = trace_reader.ReadMmap(header, offset)
+ if mmap_info.filename == HOST_ROOT + V8_GC_FAKE_MMAP:
+ log_reader.ReadUpToGC()
+ else:
+ library_repo.Load(mmap_info, code_map, options)
+ mmap_time += time.time() - start
+ elif header.type == PERF_RECORD_SAMPLE:
+ ticks += 1
+ start = time.time()
+ sample = trace_reader.ReadSample(header, offset)
+ code = code_map.Find(sample.ip)
+ if code:
+ code.Tick(sample.ip)
+ if code.codetype == Code.OPTIMIZED:
+ optimized_ticks += 1
+ elif code.codetype == Code.FULL_CODEGEN:
+ generated_ticks += 1
+ elif code.codetype == Code.V8INTERNAL:
+ v8_internal_ticks += 1
+ else:
+ missed_ticks += 1
+ if not library_repo.Tick(sample.ip) and not code:
+ really_missed_ticks += 1
+ if trace_reader.callchain_supported:
+ for ip in sample.ips:
+ caller_code = code_map.Find(ip)
+ if caller_code:
+ if code:
+ caller_code.CalleeTick(code)
+ code = caller_code
+ sample_time += time.time() - start
+
+ if options.dot:
+ PrintDot(code_map, options)
+ else:
+ PrintReport(code_map, library_repo, log_reader.arch, ticks, options)
+
+ if not options.quiet:
+ def PrintTicks(number, total, description):
+ print("%10d %5.1f%% ticks in %s" %
+ (number, 100.0*number/total, description))
+ print
+ print "Stats:"
+ print "%10d total trace events" % events
+ print "%10d total ticks" % ticks
+ print "%10d ticks not in symbols" % missed_ticks
+ unaccounted = "unaccounted ticks"
+ if really_missed_ticks > 0:
+ unaccounted += " (probably in the kernel, try --kernel)"
+ PrintTicks(really_missed_ticks, ticks, unaccounted)
+ PrintTicks(optimized_ticks, ticks, "ticks in optimized code")
+ PrintTicks(generated_ticks, ticks, "ticks in other lazily compiled code")
+ PrintTicks(v8_internal_ticks, ticks, "ticks in v8::internal::*")
+ print "%10d total symbols" % len([c for c in code_map.AllCode()])
+ print "%10d used symbols" % len([c for c in code_map.UsedCode()])
+ print "%9.2fs library processing time" % mmap_time
+ print "%9.2fs tick processing time" % sample_time
+
+ log_reader.Dispose()
+ trace_reader.Dispose()
diff --git a/chromium/v8/tools/logreader.js b/chromium/v8/tools/logreader.js
new file mode 100644
index 00000000000..a8141da21bf
--- /dev/null
+++ b/chromium/v8/tools/logreader.js
@@ -0,0 +1,184 @@
+// 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.
+
+/**
+ * @fileoverview Log Reader is used to process log file produced by V8.
+ */
+
+
+/**
+ * Base class for processing log files.
+ *
+ * @param {Array.<Object>} dispatchTable A table used for parsing and processing
+ * log records.
+ * @constructor
+ */
+function LogReader(dispatchTable) {
+ /**
+ * @type {Array.<Object>}
+ */
+ this.dispatchTable_ = dispatchTable;
+
+ /**
+ * Current line.
+ * @type {number}
+ */
+ this.lineNum_ = 0;
+
+ /**
+ * CSV lines parser.
+ * @type {CsvParser}
+ */
+ this.csvParser_ = new CsvParser();
+};
+
+
+/**
+ * Used for printing error messages.
+ *
+ * @param {string} str Error message.
+ */
+LogReader.prototype.printError = function(str) {
+ // Do nothing.
+};
+
+
+/**
+ * Processes a portion of V8 profiler event log.
+ *
+ * @param {string} chunk A portion of log.
+ */
+LogReader.prototype.processLogChunk = function(chunk) {
+ this.processLog_(chunk.split('\n'));
+};
+
+
+/**
+ * Processes a line of V8 profiler event log.
+ *
+ * @param {string} line A line of log.
+ */
+LogReader.prototype.processLogLine = function(line) {
+ this.processLog_([line]);
+};
+
+
+/**
+ * Processes stack record.
+ *
+ * @param {number} pc Program counter.
+ * @param {number} func JS Function.
+ * @param {Array.<string>} stack String representation of a stack.
+ * @return {Array.<number>} Processed stack.
+ */
+LogReader.prototype.processStack = function(pc, func, stack) {
+ var fullStack = func ? [pc, func] : [pc];
+ var prevFrame = pc;
+ for (var i = 0, n = stack.length; i < n; ++i) {
+ var frame = stack[i];
+ var firstChar = frame.charAt(0);
+ if (firstChar == '+' || firstChar == '-') {
+ // An offset from the previous frame.
+ prevFrame += parseInt(frame, 16);
+ fullStack.push(prevFrame);
+ // Filter out possible 'overflow' string.
+ } else if (firstChar != 'o') {
+ fullStack.push(parseInt(frame, 16));
+ }
+ }
+ return fullStack;
+};
+
+
+/**
+ * Returns whether a particular dispatch must be skipped.
+ *
+ * @param {!Object} dispatch Dispatch record.
+ * @return {boolean} True if dispatch must be skipped.
+ */
+LogReader.prototype.skipDispatch = function(dispatch) {
+ return false;
+};
+
+
+/**
+ * Does a dispatch of a log record.
+ *
+ * @param {Array.<string>} fields Log record.
+ * @private
+ */
+LogReader.prototype.dispatchLogRow_ = function(fields) {
+ // Obtain the dispatch.
+ var command = fields[0];
+ if (!(command in this.dispatchTable_)) return;
+
+ var dispatch = this.dispatchTable_[command];
+
+ if (dispatch === null || this.skipDispatch(dispatch)) {
+ return;
+ }
+
+ // Parse fields.
+ var parsedFields = [];
+ for (var i = 0; i < dispatch.parsers.length; ++i) {
+ var parser = dispatch.parsers[i];
+ if (parser === null) {
+ parsedFields.push(fields[1 + i]);
+ } else if (typeof parser == 'function') {
+ parsedFields.push(parser(fields[1 + i]));
+ } else {
+ // var-args
+ parsedFields.push(fields.slice(1 + i));
+ break;
+ }
+ }
+
+ // Run the processor.
+ dispatch.processor.apply(this, parsedFields);
+};
+
+
+/**
+ * Processes log lines.
+ *
+ * @param {Array.<string>} lines Log lines.
+ * @private
+ */
+LogReader.prototype.processLog_ = function(lines) {
+ for (var i = 0, n = lines.length; i < n; ++i, ++this.lineNum_) {
+ var line = lines[i];
+ if (!line) {
+ continue;
+ }
+ try {
+ var fields = this.csvParser_.parseLine(line);
+ this.dispatchLogRow_(fields);
+ } catch (e) {
+ this.printError('line ' + (this.lineNum_ + 1) + ': ' + (e.message || e));
+ }
+ }
+};
diff --git a/chromium/v8/tools/mac-nm b/chromium/v8/tools/mac-nm
new file mode 100755
index 00000000000..07efb07bae8
--- /dev/null
+++ b/chromium/v8/tools/mac-nm
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# This script is a wrapper for OS X nm(1) tool. nm(1) perform C++ function
+# names demangling, so we're piping its output to c++filt(1) tool which does it.
+# But c++filt(1) comes with XCode (as a part of GNU binutils), so it doesn't
+# guaranteed to exist on a system.
+#
+# An alternative approach is to perform demangling in tick processor, but
+# for GNU C++ ABI this is a complex process (see cp-demangle.c sources), and
+# can't be done partially, because term boundaries are plain text symbols, such
+# as 'N', 'E', so one can't just do a search through a function name, it really
+# needs to be parsed, which requires a lot of knowledge to be coded in.
+
+if [ "`which c++filt`" == "" ]; then
+ nm "$@"
+else
+ nm "$@" | c++filt -p -i
+fi
diff --git a/chromium/v8/tools/mac-tick-processor b/chromium/v8/tools/mac-tick-processor
new file mode 100755
index 00000000000..5fba622c9a3
--- /dev/null
+++ b/chromium/v8/tools/mac-tick-processor
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# A wrapper script to call 'linux-tick-processor' with Mac-specific settings.
+
+tools_path=`cd $(dirname "$0");pwd`
+$tools_path/linux-tick-processor --mac --nm=$tools_path/mac-nm $@
diff --git a/chromium/v8/tools/merge-to-branch.sh b/chromium/v8/tools/merge-to-branch.sh
new file mode 100755
index 00000000000..e0011edff02
--- /dev/null
+++ b/chromium/v8/tools/merge-to-branch.sh
@@ -0,0 +1,280 @@
+#!/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
+PERSISTFILE_BASENAME=/tmp/v8-merge-to-branch-tempfile
+ALREADY_MERGING_SENTINEL_FILE="$PERSISTFILE_BASENAME-already-merging"
+COMMIT_HASHES_FILE="$PERSISTFILE_BASENAME-PATCH_COMMIT_HASHES"
+TEMPORARY_PATCH_FILE="$PERSISTFILE_BASENAME-temporary-patch"
+
+########## Function definitions
+
+source $(dirname $BASH_SOURCE)/common-includes.sh
+
+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.
+ -p Specify a patch file to apply as part of the merge
+ -m Specify a commit message for the patch
+ -r Reverse specified patches
+EOF
+}
+
+persist_patch_commit_hashes() {
+ echo "PATCH_COMMIT_HASHES=( ${PATCH_COMMIT_HASHES[@]} )" > $COMMIT_HASHES_FILE
+}
+
+restore_patch_commit_hashes() {
+ source $COMMIT_HASHES_FILE
+}
+
+restore_patch_commit_hashes_if_unset() {
+ [[ "${#PATCH_COMMIT_HASHES[@]}" == 0 ]] && restore_patch_commit_hashes
+ [[ "${#PATCH_COMMIT_HASHES[@]}" == 0 ]] && [[ -z "$EXTRA_PATCH" ]] && \
+ die "Variable PATCH_COMMIT_HASHES could not be restored."
+}
+
+########## Option parsing
+
+while getopts ":hs:fp:rm:" OPTION ; do
+ case $OPTION in
+ h) usage
+ exit 0
+ ;;
+ p) EXTRA_PATCH=$OPTARG
+ ;;
+ f) rm -f "$ALREADY_MERGING_SENTINEL_FILE"
+ ;;
+ r) REVERSE_PATCH="--reverse"
+ ;;
+ m) NEW_COMMIT_MSG=$OPTARG
+ ;;
+ 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" ]] && [[ $START_STEP -eq 0 ]] \
+ && die "A merge is already in progress"
+touch "$ALREADY_MERGING_SENTINEL_FILE"
+
+initial_environment_checks
+
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ if [ ${#@} -lt 2 ] ; then
+ if [ -z "$EXTRA_PATCH" ] ; then
+ die "Either a patch file or revision numbers must be specified"
+ fi
+ if [ -z "$NEW_COMMIT_MSG" ] ; then
+ die "You must specify a merge comment if no patches are specified"
+ fi
+ fi
+ echo ">>> Step $CURRENT_STEP: Preparation"
+ MERGE_TO_BRANCH=$1
+ [[ -n "$MERGE_TO_BRANCH" ]] || die "Please specify a branch to merge to"
+ shift
+ persist "MERGE_TO_BRANCH"
+ common_prepare
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create a fresh branch for the patch."
+ restore_if_unset "MERGE_TO_BRANCH"
+ 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 "$REVISION_LIST" ]] && REVISION_LIST="$REVISION_LIST,"
+ REVISION_LIST="$REVISION_LIST r$REVISION"
+ let current+=1
+ done
+ if [ -n "$REVISION_LIST" ] ; then
+ if [ -n "$REVERSE_PATCH" ] ; then
+ NEW_COMMIT_MSG="Rollback of$REVISION_LIST in $MERGE_TO_BRANCH branch."
+ else
+ NEW_COMMIT_MSG="Merged$REVISION_LIST into $MERGE_TO_BRANCH branch."
+ fi;
+ fi;
+
+ 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 [ -n "$BUG" ] ; then
+ [[ -n "$BUG_AGGREGATE" ]] && BUG_AGGREGATE="$BUG_AGGREGATE,"
+ BUG_AGGREGATE="$BUG_AGGREGATE$BUG"
+ fi
+ done
+ if [ -n "$BUG_AGGREGATE" ] ; then
+ echo "BUG=$BUG_AGGREGATE" >> $COMMITMSG_FILE
+ fi
+ persist "NEW_COMMIT_MSG"
+ persist "REVISION_LIST"
+ persist_patch_commit_hashes
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Apply patches for selected revisions."
+ restore_if_unset "MERGE_TO_BRANCH"
+ restore_patch_commit_hashes_if_unset "PATCH_COMMIT_HASHES"
+ rm -f "$TOUCHED_FILES_FILE"
+ for HASH in ${PATCH_COMMIT_HASHES[@]} ; do
+ echo "Applying patch for $HASH to $MERGE_TO_BRANCH..."
+ git log -1 -p $HASH > "$TEMPORARY_PATCH_FILE"
+ apply_patch "$TEMPORARY_PATCH_FILE"
+ done
+ if [ -n "$EXTRA_PATCH" ] ; then
+ apply_patch "$EXTRA_PATCH"
+ fi
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Prepare $VERSION_FILE."
+ # These version numbers are used again for creating the tag
+ read_and_persist_version
+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
+ echo $NEWPATCH $VERSION_FILE
+ sed -e "/#define PATCH_LEVEL/s/[0-9]*$/$NEWPATCH/" \
+ -i.bak "$VERSION_FILE" || die "Could not increment patch level"
+ else
+ $EDITOR "$VERSION_FILE"
+ fi
+ read_and_persist_version "NEW"
+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
+
+upload_step
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to the repository."
+ restore_if_unset "MERGE_TO_BRANCH"
+ git checkout $BRANCHNAME \
+ || die "cannot ensure that the current branch is $BRANCHNAME"
+ wait_for_lgtm
+ git cl dcommit || die "failed to commit to $MERGE_TO_BRANCH"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Determine svn commit revision"
+ restore_if_unset "NEW_COMMIT_MSG"
+ restore_if_unset "MERGE_TO_BRANCH"
+ 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
+ echo ">>> Step $CURRENT_STEP: Create the tag."
+ restore_if_unset "SVN_REVISION"
+ restore_version_if_unset "NEW"
+ echo "Creating tag svn/tags/$NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH"
+ if [ "$MERGE_TO_BRANCH" == "trunk" ] ; then
+ TO_URL="$MERGE_TO_BRANCH"
+ else
+ TO_URL="branches/$MERGE_TO_BRANCH"
+ fi
+ svn copy -r $SVN_REVISION \
+ https://v8.googlecode.com/svn/$TO_URL \
+ https://v8.googlecode.com/svn/tags/$NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH \
+ -m "Tagging version $NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH"
+ persist "TO_URL"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Cleanup."
+ restore_if_unset "SVN_REVISION"
+ restore_if_unset "TO_URL"
+ restore_if_unset "REVISION_LIST"
+ restore_version_if_unset "NEW"
+ common_cleanup
+ echo "*** SUMMARY ***"
+ echo "version: $NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH"
+ echo "branch: $TO_URL"
+ echo "svn revision: $SVN_REVISION"
+ [[ -n "$REVISION_LIST" ]] && echo "patches:$REVISION_LIST"
+fi
diff --git a/chromium/v8/tools/mingw-generate-makefiles.sh b/chromium/v8/tools/mingw-generate-makefiles.sh
new file mode 100755
index 00000000000..32af52d39e0
--- /dev/null
+++ b/chromium/v8/tools/mingw-generate-makefiles.sh
@@ -0,0 +1,97 @@
+#!/bin/sh
+# Copyright 2013 the V8 project authors. 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.
+
+# Monkey-patch GYP.
+cat > build/gyp/gyp.mingw << EOF
+#!/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
+
+# TODO(mark): sys.path manipulation is some temporary testing stuff.
+try:
+ import gyp
+except ImportError, e:
+ import os.path
+ sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), 'pylib'))
+ import gyp
+
+def MonkeyBuildFileTargets(target_list, build_file):
+ """From a target_list, returns the subset from the specified build_file.
+ """
+ build_file = build_file.replace('/', '\\\\')
+ return [p for p in target_list if gyp.common.BuildFile(p) == build_file]
+gyp.common.BuildFileTargets = MonkeyBuildFileTargets
+
+import gyp.generator.make
+import os
+def Monkey_ITIP(self):
+ """Returns the location of the final output for an installable target."""
+ sep = os.path.sep
+ # Xcode puts shared_library results into PRODUCT_DIR, and some gyp files
+ # rely on this. Emulate this behavior for mac.
+ if (self.type == 'shared_library' and
+ (self.flavor != 'mac' or self.toolset != 'target')):
+ # Install all shared libs into a common directory (per toolset) for
+ # convenient access with LD_LIBRARY_PATH.
+ return '\$(builddir)%slib.%s%s%s' % (sep, self.toolset, sep, self.alias)
+ return '\$(builddir)' + sep + self.alias
+gyp.generator.make.MakefileWriter._InstallableTargetInstallPath = Monkey_ITIP
+
+if __name__ == '__main__':
+ sys.exit(gyp.main(sys.argv[1:]))
+EOF
+
+# Delete old generated Makefiles.
+find out -name '*.mk' -or -name 'Makefile*' -exec rm {} \;
+
+# Generate fresh Makefiles.
+mv build/gyp/gyp build/gyp/gyp.original
+mv build/gyp/gyp.mingw build/gyp/gyp
+make out/Makefile.ia32
+mv build/gyp/gyp build/gyp/gyp.mingw
+mv build/gyp/gyp.original build/gyp/gyp
+
+# Patch generated Makefiles: replace most backslashes with forward slashes,
+# fix library names in linker flags.
+FILES=$(find out -name '*.mk' -or -name 'Makefile*')
+for F in $FILES ; do
+ echo "Patching $F..."
+ cp $F $F.orig
+ cat $F.orig \
+ | sed -e 's|\([)a-zA-Z0-9]\)\\\([a-zA-Z]\)|\1/\2|g' \
+ -e 's|\([)a-zA-Z0-9]\)\\\\\([a-zA-Z]\)|\1/\2|g' \
+ -e 's|'%s/n'|'%s\\\\n'|g' \
+ -e 's|-lwinmm\.lib|-lwinmm|g' \
+ -e 's|-lws2_32\.lib|-lws2_32|g' \
+ > $F
+ rm $F.orig
+done
diff --git a/chromium/v8/tools/nacl-run.py b/chromium/v8/tools/nacl-run.py
new file mode 100755
index 00000000000..135172caf90
--- /dev/null
+++ b/chromium/v8/tools/nacl-run.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 the V8 project authors. 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 script executes the passed command line using the Native Client
+# 'sel_ldr' container. It is derived from android-run.py.
+
+import os
+from os.path import join, dirname, abspath
+import subprocess
+import sys
+import tempfile
+
+def Check(output, errors):
+ failed = any([s.startswith('/system/bin/sh:') or s.startswith('ANDROID')
+ for s in output.split('\n')])
+ return 1 if failed else 0
+
+def Execute(cmdline):
+ (fd_out, outname) = tempfile.mkstemp()
+ (fd_err, errname) = tempfile.mkstemp()
+ process = subprocess.Popen(
+ args=cmdline,
+ shell=True,
+ stdout=fd_out,
+ stderr=fd_err,
+ )
+ exit_code = process.wait()
+ os.close(fd_out)
+ os.close(fd_err)
+ output = file(outname).read()
+ errors = file(errname).read()
+ os.unlink(outname)
+ os.unlink(errname)
+ sys.stdout.write(output)
+ sys.stderr.write(errors)
+ return exit_code or Check(output, errors)
+
+def Escape(arg):
+ def ShouldEscape():
+ for x in arg:
+ if not x.isalnum() and x != '-' and x != '_':
+ return True
+ return False
+
+ return arg if not ShouldEscape() else '"%s"' % (arg.replace('"', '\\"'))
+
+def WriteToTemporaryFile(data):
+ (fd, fname) = tempfile.mkstemp()
+ os.close(fd)
+ tmp_file = open(fname, "w")
+ tmp_file.write(data)
+ tmp_file.close()
+ return fname
+
+def GetNaClArchFromNexe(nexe):
+ try:
+ p = subprocess.Popen(['file', nexe], stdout=subprocess.PIPE)
+ out, err = p.communicate()
+ lines = out.split('\n')
+ if lines[0].find(": ELF 32-bit LSB executable, Intel 80386") > 0:
+ return "x86_32"
+ if lines[0].find(": ELF 64-bit LSB executable, x86-64") > 0:
+ return "x86_64"
+ except:
+ print 'file ' + sys.argv[1] + ' failed'
+ return None
+
+def GetNaClResources(nexe):
+ nacl_sdk_dir = os.environ["NACL_SDK_ROOT"]
+ nacl_arch = GetNaClArchFromNexe(nexe)
+ if sys.platform.startswith("linux"):
+ platform = "linux"
+ elif sys.platform == "darwin":
+ platform = "mac"
+ else:
+ print("NaCl V8 testing is supported on Linux and MacOS only.")
+ sys.exit(1)
+
+ if nacl_arch is "x86_64":
+ toolchain = platform + "_x86_glibc"
+ sel_ldr = "sel_ldr_x86_64"
+ irt = "irt_core_x86_64.nexe"
+ libdir = "lib64"
+ elif nacl_arch is "x86_32":
+ toolchain = platform + "_x86_glibc"
+ sel_ldr = "sel_ldr_x86_32"
+ irt = "irt_core_x86_32.nexe"
+ libdir = "lib32"
+ elif nacl_arch is "arm":
+ print("NaCl V8 ARM support is not ready yet.")
+ sys.exit(1)
+ else:
+ print("Invalid nexe %s" % nexe)
+ sys.exit(1)
+
+ nacl_sel_ldr = os.path.join(nacl_sdk_dir, "tools", sel_ldr)
+ nacl_irt = os.path.join(nacl_sdk_dir, "tools", irt)
+ nacl_ld_so = os.path.join(nacl_sdk_dir, "toolchain", toolchain,
+ "x86_64-nacl", libdir, "runnable-ld.so")
+ nacl_lib_path = os.path.join(nacl_sdk_dir, "toolchain", toolchain,
+ "x86_64-nacl", libdir)
+
+ return (nacl_sdk_dir, nacl_sel_ldr, nacl_irt, nacl_ld_so, nacl_lib_path)
+
+def Main():
+ if (len(sys.argv) == 1):
+ print("Usage: %s <command-to-run-on-device>" % sys.argv[0])
+ return 1
+
+ args = [Escape(arg) for arg in sys.argv[1:]]
+
+ (nacl_sdk_dir, nacl_sel_ldr, nacl_irt, nacl_ld_so,
+ nacl_lib_path) = GetNaClResources(sys.argv[1])
+
+ # sel_ldr Options:
+ # -c -c: disable validation (for performance)
+ # -a: allow file access
+ # -B <irt>: load the IRT
+ command = ' '.join([nacl_sel_ldr, '-c', '-c', '-a', '-B', nacl_irt, '--',
+ nacl_ld_so, '--library-path', nacl_lib_path] + args)
+ error_code = Execute(command)
+ return error_code
+
+if __name__ == '__main__':
+ sys.exit(Main())
diff --git a/chromium/v8/tools/oom_dump/README b/chromium/v8/tools/oom_dump/README
new file mode 100644
index 00000000000..1d840b9a9ce
--- /dev/null
+++ b/chromium/v8/tools/oom_dump/README
@@ -0,0 +1,33 @@
+oom_dump extracts useful information from Google Chrome OOM minidumps.
+
+To build one needs a google-breakpad checkout
+(http://code.google.com/p/google-breakpad/).
+
+First, one needs to build and install breakpad itself. For instructions
+check google-breakpad, but currently it's as easy as:
+
+ ./configure
+ make
+ sudo make install
+
+(the catch: breakpad installs .so into /usr/local/lib, so you might
+need some additional tweaking to make it discoverable, for example,
+put a soft link into /usr/lib directory).
+
+Next step is to build v8. Note: you should build x64 version of v8,
+if you're on 64-bit platform, otherwise you would get a link error when
+building oom_dump. Also, if you are testing against an older version of chrome
+you should build the corresponding version of V8 to make sure that the type-id
+enum have the correct values.
+
+The last step is to build oom_dump itself. The following command should work:
+
+ cd <v8 working copy>/tools/oom_dump
+ scons BREAKPAD_DIR=<path to google-breakpad working copy>
+
+(Additionally you can control v8 working copy dir, but the default should work.)
+
+If everything goes fine, oom_dump <path to minidump> should print
+some useful information about the OOM crash.
+
+Note: currently only 32-bit Windows minidumps are supported.
diff --git a/chromium/v8/tools/oom_dump/SConstruct b/chromium/v8/tools/oom_dump/SConstruct
new file mode 100644
index 00000000000..f228c890760
--- /dev/null
+++ b/chromium/v8/tools/oom_dump/SConstruct
@@ -0,0 +1,42 @@
+# 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.
+
+vars = Variables('custom.py')
+vars.Add(PathVariable('BREAKPAD_DIR',
+ 'Path to checkout of google-breakpad project',
+ '~/google-breakpad',
+ PathVariable.PathIsDir))
+vars.Add(PathVariable('V8_DIR',
+ 'Path to checkout of v8 project',
+ '../..',
+ PathVariable.PathIsDir))
+
+env = Environment(variables = vars,
+ CPPPATH = ['${BREAKPAD_DIR}/src', '${V8_DIR}/src'],
+ LIBPATH = ['/usr/local/lib', '${V8_DIR}'])
+
+env.Program('oom_dump.cc', LIBS = ['breakpad', 'v8', 'pthread'])
diff --git a/chromium/v8/tools/oom_dump/oom_dump.cc b/chromium/v8/tools/oom_dump/oom_dump.cc
new file mode 100644
index 00000000000..5dfb5dff356
--- /dev/null
+++ b/chromium/v8/tools/oom_dump/oom_dump.cc
@@ -0,0 +1,289 @@
+// 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.
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include <google_breakpad/processor/minidump.h>
+
+#define ENABLE_DEBUGGER_SUPPORT
+
+#include <v8.h>
+
+namespace {
+
+using google_breakpad::Minidump;
+using google_breakpad::MinidumpContext;
+using google_breakpad::MinidumpThread;
+using google_breakpad::MinidumpThreadList;
+using google_breakpad::MinidumpException;
+using google_breakpad::MinidumpMemoryRegion;
+
+const char* InstanceTypeToString(int type) {
+ static char const* names[v8::internal::LAST_TYPE] = {0};
+ if (names[v8::internal::STRING_TYPE] == NULL) {
+ using namespace v8::internal;
+#define SET(type) names[type] = #type;
+ INSTANCE_TYPE_LIST(SET)
+#undef SET
+ }
+ return names[type];
+}
+
+
+u_int32_t ReadPointedValue(MinidumpMemoryRegion* region,
+ u_int64_t base,
+ int offset) {
+ u_int32_t ptr = 0;
+ CHECK(region->GetMemoryAtAddress(base + 4 * offset, &ptr));
+ u_int32_t value = 0;
+ CHECK(region->GetMemoryAtAddress(ptr, &value));
+ return value;
+}
+
+
+void ReadArray(MinidumpMemoryRegion* region,
+ u_int64_t array_ptr,
+ int size,
+ int* output) {
+ for (int i = 0; i < size; i++) {
+ u_int32_t value;
+ CHECK(region->GetMemoryAtAddress(array_ptr + 4 * i, &value));
+ output[i] = value;
+ }
+}
+
+
+u_int32_t ReadArrayFrom(MinidumpMemoryRegion* region,
+ u_int64_t base,
+ int offset,
+ int size,
+ int* output) {
+ u_int32_t ptr = 0;
+ CHECK(region->GetMemoryAtAddress(base + 4 * offset, &ptr));
+ ReadArray(region, ptr, size, output);
+}
+
+
+double toM(int size) {
+ return size / (1024. * 1024.);
+}
+
+
+class IndirectSorter {
+ public:
+ explicit IndirectSorter(int* a) : a_(a) { }
+
+ bool operator() (int i0, int i1) {
+ return a_[i0] > a_[i1];
+ }
+
+ private:
+ int* a_;
+};
+
+
+void DumpHeapStats(const char *minidump_file) {
+ Minidump minidump(minidump_file);
+ CHECK(minidump.Read());
+
+ MinidumpException *exception = minidump.GetException();
+ CHECK(exception);
+
+ MinidumpContext* crash_context = exception->GetContext();
+ CHECK(crash_context);
+
+ u_int32_t exception_thread_id = 0;
+ CHECK(exception->GetThreadID(&exception_thread_id));
+
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ CHECK(thread_list);
+
+ MinidumpThread* exception_thread =
+ thread_list->GetThreadByID(exception_thread_id);
+ CHECK(exception_thread);
+
+ // Currently only 32-bit Windows minidumps are supported.
+ CHECK_EQ(MD_CONTEXT_X86, crash_context->GetContextCPU());
+
+ const MDRawContextX86* contextX86 = crash_context->GetContextX86();
+ CHECK(contextX86);
+
+ const u_int32_t esp = contextX86->esp;
+
+ MinidumpMemoryRegion* memory_region = exception_thread->GetMemory();
+ CHECK(memory_region);
+
+ const u_int64_t last = memory_region->GetBase() + memory_region->GetSize();
+
+ u_int64_t heap_stats_addr = 0;
+ for (u_int64_t addr = esp; addr < last; addr += 4) {
+ u_int32_t value = 0;
+ CHECK(memory_region->GetMemoryAtAddress(addr, &value));
+ if (value >= esp && value < last) {
+ u_int32_t value2 = 0;
+ CHECK(memory_region->GetMemoryAtAddress(value, &value2));
+ if (value2 == v8::internal::HeapStats::kStartMarker) {
+ heap_stats_addr = addr;
+ break;
+ }
+ }
+ }
+ CHECK(heap_stats_addr);
+
+ // Read heap stats.
+
+#define READ_FIELD(offset) \
+ ReadPointedValue(memory_region, heap_stats_addr, offset)
+
+ CHECK(READ_FIELD(0) == v8::internal::HeapStats::kStartMarker);
+ CHECK(READ_FIELD(24) == v8::internal::HeapStats::kEndMarker);
+
+ const int new_space_size = READ_FIELD(1);
+ const int new_space_capacity = READ_FIELD(2);
+ const int old_pointer_space_size = READ_FIELD(3);
+ const int old_pointer_space_capacity = READ_FIELD(4);
+ const int old_data_space_size = READ_FIELD(5);
+ const int old_data_space_capacity = READ_FIELD(6);
+ const int code_space_size = READ_FIELD(7);
+ const int code_space_capacity = READ_FIELD(8);
+ const int map_space_size = READ_FIELD(9);
+ const int map_space_capacity = READ_FIELD(10);
+ const int cell_space_size = READ_FIELD(11);
+ const int cell_space_capacity = READ_FIELD(12);
+ const int lo_space_size = READ_FIELD(13);
+ const int global_handle_count = READ_FIELD(14);
+ const int weak_global_handle_count = READ_FIELD(15);
+ const int pending_global_handle_count = READ_FIELD(16);
+ const int near_death_global_handle_count = READ_FIELD(17);
+ const int destroyed_global_handle_count = READ_FIELD(18);
+ const int memory_allocator_size = READ_FIELD(19);
+ const int memory_allocator_capacity = READ_FIELD(20);
+ const int os_error = READ_FIELD(23);
+#undef READ_FIELD
+
+ int objects_per_type[v8::internal::LAST_TYPE + 1] = {0};
+ ReadArrayFrom(memory_region, heap_stats_addr, 21,
+ v8::internal::LAST_TYPE + 1, objects_per_type);
+
+ int size_per_type[v8::internal::LAST_TYPE + 1] = {0};
+ ReadArrayFrom(memory_region, heap_stats_addr, 22, v8::internal::LAST_TYPE + 1,
+ size_per_type);
+
+ int js_global_objects =
+ objects_per_type[v8::internal::JS_GLOBAL_OBJECT_TYPE];
+ int js_builtins_objects =
+ objects_per_type[v8::internal::JS_BUILTINS_OBJECT_TYPE];
+ int js_global_proxies =
+ objects_per_type[v8::internal::JS_GLOBAL_PROXY_TYPE];
+
+ int indices[v8::internal::LAST_TYPE + 1];
+ for (int i = 0; i <= v8::internal::LAST_TYPE; i++) {
+ indices[i] = i;
+ }
+
+ std::stable_sort(indices, indices + sizeof(indices)/sizeof(indices[0]),
+ IndirectSorter(size_per_type));
+
+ int total_size = 0;
+ for (int i = 0; i <= v8::internal::LAST_TYPE; i++) {
+ total_size += size_per_type[i];
+ }
+
+ // Print heap stats.
+
+ printf("exception thread ID: %" PRIu32 " (%#" PRIx32 ")\n",
+ exception_thread_id, exception_thread_id);
+ printf("heap stats address: %#" PRIx64 "\n", heap_stats_addr);
+#define PRINT_INT_STAT(stat) \
+ printf("\t%-25s\t% 10d\n", #stat ":", stat);
+#define PRINT_MB_STAT(stat) \
+ printf("\t%-25s\t% 10.3f MB\n", #stat ":", toM(stat));
+ PRINT_MB_STAT(new_space_size);
+ PRINT_MB_STAT(new_space_capacity);
+ PRINT_MB_STAT(old_pointer_space_size);
+ PRINT_MB_STAT(old_pointer_space_capacity);
+ PRINT_MB_STAT(old_data_space_size);
+ PRINT_MB_STAT(old_data_space_capacity);
+ PRINT_MB_STAT(code_space_size);
+ PRINT_MB_STAT(code_space_capacity);
+ PRINT_MB_STAT(map_space_size);
+ PRINT_MB_STAT(map_space_capacity);
+ PRINT_MB_STAT(cell_space_size);
+ PRINT_MB_STAT(cell_space_capacity);
+ PRINT_MB_STAT(lo_space_size);
+ PRINT_INT_STAT(global_handle_count);
+ PRINT_INT_STAT(weak_global_handle_count);
+ PRINT_INT_STAT(pending_global_handle_count);
+ PRINT_INT_STAT(near_death_global_handle_count);
+ PRINT_INT_STAT(destroyed_global_handle_count);
+ PRINT_MB_STAT(memory_allocator_size);
+ PRINT_MB_STAT(memory_allocator_capacity);
+ PRINT_INT_STAT(os_error);
+#undef PRINT_STAT
+
+ printf("\n");
+
+ printf(
+ "\tJS_GLOBAL_OBJECT_TYPE/JS_BUILTINS_OBJECT_TYPE/JS_GLOBAL_PROXY_TYPE: "
+ "%d/%d/%d\n\n",
+ js_global_objects, js_builtins_objects, js_global_proxies);
+
+ int running_size = 0;
+ for (int i = 0; i <= v8::internal::LAST_TYPE; i++) {
+ int type = indices[i];
+ const char* name = InstanceTypeToString(type);
+ if (name == NULL) {
+ // Unknown instance type. Check that there is no objects of that type.
+ CHECK_EQ(0, objects_per_type[type]);
+ CHECK_EQ(0, size_per_type[type]);
+ continue;
+ }
+ int size = size_per_type[type];
+ running_size += size;
+ printf("\t%-37s% 9d% 11.3f MB% 10.3f%%% 10.3f%%\n",
+ name, objects_per_type[type], toM(size),
+ 100. * size / total_size, 100. * running_size / total_size);
+ }
+ printf("\t%-37s% 9d% 11.3f MB% 10.3f%%% 10.3f%%\n",
+ "total", 0, toM(total_size), 100., 100.);
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <minidump>\n", argv[0]);
+ return 1;
+ }
+
+ DumpHeapStats(argv[1]);
+
+ return 0;
+}
diff --git a/chromium/v8/tools/perf_tests/chromium_revision b/chromium/v8/tools/perf_tests/chromium_revision
new file mode 100644
index 00000000000..0cdcc110f84
--- /dev/null
+++ b/chromium/v8/tools/perf_tests/chromium_revision
@@ -0,0 +1 @@
+210122
diff --git a/chromium/v8/tools/plot-timer-events b/chromium/v8/tools/plot-timer-events
new file mode 100755
index 00000000000..0723150be57
--- /dev/null
+++ b/chromium/v8/tools/plot-timer-events
@@ -0,0 +1,70 @@
+#!/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`
+ if [ -x "$d8_public" ]; then D8_PATH=$(dirname "$d8_public"); fi
+fi
+[ -n "$D8_PATH" ] || D8_PATH=$tools_path/..
+d8_exec=$D8_PATH/d8
+
+if [ ! -x "$d8_exec" ]; then
+ 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
+
+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
+
+if [[ "$@" != *--distortion* ]]; then
+ # Try to find out how much the instrumentation overhead is.
+ calibration_log=calibration.log
+ calibration_script="for (var i = 0; i < 1000000; i++) print();"
+
+ $d8_exec --nocrankshaft --prof --logfile $calibration_log \
+ --log-timer-events -e "$calibration_script" > /dev/null
+ t_1_start=`grep "timer-event-start,\"V8.Execute\"" $calibration_log \
+ | tail -n1 | awk -F, '{print $3}'`
+ t_1_end=`grep "timer-event-end,\"V8.Execute\"" $calibration_log \
+ | tail -n1 | awk -F, '{print $3}'`
+ n_1=`grep "timer-event\|tick" $calibration_log | wc -l`
+
+ $d8_exec --nocrankshaft --prof --logfile $calibration_log \
+ --log-internal-timer-events -e "$calibration_script" > /dev/null
+ t_2_start=`grep "timer-event-start,\"V8.Execute\"" $calibration_log \
+ | tail -n1 | awk -F, '{print $3}'`
+ t_2_end=`grep "timer-event-end,\"V8.Execute\"" $calibration_log \
+ | tail -n1 | awk -F, '{print $3}'`
+ n_2=`grep "timer-event\|tick" $calibration_log | wc -l`
+
+ rm $calibration_log
+
+ # Overhead in picoseconds.
+ options=--distortion=
+ options+=`echo "1000*(($t_1_end - $t_1_start) - ($t_2_end - $t_2_start)) \
+ / ($n_1 - $n_2)" | bc`
+ echo $options
+fi
+
+cat $log_file |
+ $d8_exec $tools_path/csvparser.js $tools_path/splaytree.js \
+ $tools_path/codemap.js $tools_path/profile.js $tools_path/profile_view.js \
+ $tools_path/logreader.js $tools_path/tickprocessor.js \
+ $tools_path/profviz/composer.js $tools_path/profviz/stdio.js \
+ -- $@ $options 2>/dev/null | gnuplot > timer-events.png
diff --git a/chromium/v8/tools/presubmit.py b/chromium/v8/tools/presubmit.py
new file mode 100755
index 00000000000..12475b33c48
--- /dev/null
+++ b/chromium/v8/tools/presubmit.py
@@ -0,0 +1,435 @@
+#!/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.
+
+try:
+ import hashlib
+ md5er = hashlib.md5
+except ImportError, e:
+ import md5
+ md5er = md5.new
+
+
+import optparse
+import os
+from os.path import abspath, join, dirname, basename, exists
+import pickle
+import re
+import sys
+import subprocess
+import multiprocessing
+from subprocess import PIPE
+
+# Disabled LINT rules and reason.
+# build/include_what_you_use: Started giving false positives for variables
+# named "string" and "map" assuming that you needed to include STL headers.
+
+ENABLED_LINT_RULES = """
+build/class
+build/deprecated
+build/endif_comment
+build/forward_decl
+build/include_order
+build/printf_format
+build/storage_class
+legal/copyright
+readability/boost
+readability/braces
+readability/casting
+readability/check
+readability/constructors
+readability/fn_size
+readability/function
+readability/multiline_comment
+readability/multiline_string
+readability/streams
+readability/todo
+readability/utf8
+runtime/arrays
+runtime/casting
+runtime/deprecated_fn
+runtime/explicit
+runtime/int
+runtime/memset
+runtime/mutex
+runtime/nonconf
+runtime/printf
+runtime/printf_format
+runtime/references
+runtime/rtti
+runtime/sizeof
+runtime/string
+runtime/virtual
+runtime/vlog
+whitespace/blank_line
+whitespace/braces
+whitespace/comma
+whitespace/comments
+whitespace/ending_newline
+whitespace/indent
+whitespace/labels
+whitespace/line_length
+whitespace/newline
+whitespace/operators
+whitespace/parens
+whitespace/tab
+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:
+ if error_count == -1:
+ print "Failed to process %s" % command.pop()
+ return 1
+ break
+ m = LINT_OUTPUT_PATTERN.match(out_line)
+ if m:
+ out_lines += out_line
+ error_count += 1
+ sys.stdout.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):
+ self.sums = {}
+ self.sums_file_name = sums_file_name
+
+ def Load(self):
+ try:
+ sums_file = None
+ try:
+ sums_file = open(self.sums_file_name, 'r')
+ self.sums = pickle.load(sums_file)
+ except IOError:
+ # File might not exist, this is OK.
+ pass
+ finally:
+ if sums_file:
+ sums_file.close()
+
+ def Save(self):
+ try:
+ sums_file = open(self.sums_file_name, 'w')
+ pickle.dump(self.sums, sums_file)
+ finally:
+ sums_file.close()
+
+ def FilterUnchangedFiles(self, files):
+ changed_or_new = []
+ for file in files:
+ try:
+ handle = open(file, "r")
+ file_sum = md5er(handle.read()).digest()
+ if not file in self.sums or self.sums[file] != file_sum:
+ changed_or_new.append(file)
+ self.sums[file] = file_sum
+ finally:
+ handle.close()
+ return changed_or_new
+
+ def RemoveFile(self, file):
+ if file in self.sums:
+ self.sums.pop(file)
+
+
+class SourceFileProcessor(object):
+ """
+ Utility class that can run through a directory structure, find all relevant
+ files and invoke a custom check on the files.
+ """
+
+ def Run(self, path):
+ all_files = []
+ for file in self.GetPathsToSearch():
+ all_files += self.FindFilesIn(join(path, file))
+ if not self.ProcessFiles(all_files, path):
+ return False
+ return True
+
+ def IgnoreDir(self, name):
+ return name.startswith('.') or name == 'data' or name == 'sputniktests'
+
+ def IgnoreFile(self, name):
+ return name.startswith('.')
+
+ def FindFilesIn(self, path):
+ result = []
+ for (root, dirs, files) in os.walk(path):
+ for ignored in [x for x in dirs if self.IgnoreDir(x)]:
+ dirs.remove(ignored)
+ for file in files:
+ if not self.IgnoreFile(file) and self.IsRelevant(file):
+ result.append(join(root, file))
+ return result
+
+
+class CppLintProcessor(SourceFileProcessor):
+ """
+ Lint files to check that they follow the google code style.
+ """
+
+ def IsRelevant(self, name):
+ return name.endswith('.cc') or name.endswith('.h')
+
+ def IgnoreDir(self, name):
+ return (super(CppLintProcessor, self).IgnoreDir(name)
+ or (name == 'third_party'))
+
+ IGNORE_LINT = ['flag-definitions.h']
+
+ def IgnoreFile(self, name):
+ return (super(CppLintProcessor, self).IgnoreFile(name)
+ or (name in CppLintProcessor.IGNORE_LINT))
+
+ def GetPathsToSearch(self):
+ return ['src', 'preparser', 'include', 'samples', join('test', 'cctest')]
+
+ def GetCpplintScript(self, prio_path):
+ for path in [prio_path] + os.environ["PATH"].split(os.pathsep):
+ path = path.strip('"')
+ cpplint = os.path.join(path, "cpplint.py")
+ if os.path.isfile(cpplint):
+ return cpplint
+
+ return None
+
+ def ProcessFiles(self, files, path):
+ good_files_cache = FileContentsCache('.cpplint-cache')
+ good_files_cache.Load()
+ files = good_files_cache.FilterUnchangedFiles(files)
+ if len(files) == 0:
+ print 'No changes in files detected. Skipping cpplint check.'
+ return True
+
+ filt = '-,' + ",".join(['+' + n for n in ENABLED_LINT_RULES])
+ command = [sys.executable, 'cpplint.py', '--filter', filt]
+ cpplint = self.GetCpplintScript(join(path, "tools"))
+ if cpplint is None:
+ print('Could not find cpplint.py. Make sure '
+ 'depot_tools is installed and in the path.')
+ sys.exit(1)
+
+ command = [sys.executable, cpplint, '--filter', filt]
+
+ 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 total_errors == 0
+
+
+COPYRIGHT_HEADER_PATTERN = re.compile(
+ r'Copyright [\d-]*20[0-1][0-9] the V8 project authors. All rights reserved.')
+
+class SourceProcessor(SourceFileProcessor):
+ """
+ Check that all files include a copyright notice and no trailing whitespaces.
+ """
+
+ RELEVANT_EXTENSIONS = ['.js', '.cc', '.h', '.py', '.c', 'SConscript',
+ 'SConstruct', '.status', '.gyp', '.gypi']
+
+ # Overwriting the one in the parent class.
+ def FindFilesIn(self, path):
+ if os.path.exists(path+'/.git'):
+ output = subprocess.Popen('git ls-files --full-name',
+ stdout=PIPE, cwd=path, shell=True)
+ result = []
+ for file in output.stdout.read().split():
+ for dir_part in os.path.dirname(file).split(os.sep):
+ if self.IgnoreDir(dir_part):
+ break
+ else:
+ if self.IsRelevant(file) and not self.IgnoreFile(file):
+ result.append(join(path, file))
+ if output.wait() == 0:
+ return result
+ return super(SourceProcessor, self).FindFilesIn(path)
+
+ def IsRelevant(self, name):
+ for ext in SourceProcessor.RELEVANT_EXTENSIONS:
+ if name.endswith(ext):
+ return True
+ return False
+
+ def GetPathsToSearch(self):
+ return ['.']
+
+ def IgnoreDir(self, name):
+ return (super(SourceProcessor, self).IgnoreDir(name)
+ or (name == 'third_party')
+ or (name == 'gyp')
+ or (name == 'out')
+ or (name == 'obj')
+ or (name == 'DerivedSources'))
+
+ IGNORE_COPYRIGHTS = ['cpplint.py',
+ 'daemon.py',
+ 'earley-boyer.js',
+ 'raytrace.js',
+ 'crypto.js',
+ 'libraries.cc',
+ 'libraries-empty.cc',
+ 'jsmin.py',
+ 'regexp-pcre.js',
+ 'gnuplot-4.6.3-emscripten.js']
+ IGNORE_TABS = IGNORE_COPYRIGHTS + ['unicode-test.js', 'html-comments.js']
+
+ def EndOfDeclaration(self, line):
+ return line == "}" or line == "};"
+
+ def StartOfDeclaration(self, line):
+ return line.find("//") == 0 or \
+ line.find("/*") == 0 or \
+ line.find(") {") != -1
+
+ def ProcessContents(self, name, contents):
+ result = True
+ base = basename(name)
+ if not base in SourceProcessor.IGNORE_TABS:
+ if '\t' in contents:
+ print "%s contains tabs" % name
+ result = False
+ if not base in SourceProcessor.IGNORE_COPYRIGHTS:
+ if not COPYRIGHT_HEADER_PATTERN.search(contents):
+ print "%s is missing a correct copyright header." % name
+ result = False
+ if ' \n' in contents or contents.endswith(' '):
+ line = 0
+ lines = []
+ parts = contents.split(' \n')
+ if not contents.endswith(' '):
+ parts.pop()
+ for part in parts:
+ line += part.count('\n') + 1
+ lines.append(str(line))
+ linenumbers = ', '.join(lines)
+ if len(lines) > 1:
+ print "%s has trailing whitespaces in lines %s." % (name, linenumbers)
+ else:
+ print "%s has trailing whitespaces in line %s." % (name, linenumbers)
+ result = False
+ # Check two empty lines between declarations.
+ if name.endswith(".cc"):
+ line = 0
+ lines = []
+ parts = contents.split('\n')
+ while line < len(parts) - 2:
+ if self.EndOfDeclaration(parts[line]):
+ if self.StartOfDeclaration(parts[line + 1]):
+ lines.append(str(line + 1))
+ line += 1
+ elif parts[line + 1] == "" and \
+ self.StartOfDeclaration(parts[line + 2]):
+ lines.append(str(line + 1))
+ line += 2
+ line += 1
+ if len(lines) >= 1:
+ linenumbers = ', '.join(lines)
+ if len(lines) > 1:
+ print "%s does not have two empty lines between declarations " \
+ "in lines %s." % (name, linenumbers)
+ else:
+ print "%s does not have two empty lines between declarations " \
+ "in line %s." % (name, linenumbers)
+ result = False
+ return result
+
+ def ProcessFiles(self, files, path):
+ success = True
+ violations = 0
+ for file in files:
+ try:
+ handle = open(file)
+ contents = handle.read()
+ if not self.ProcessContents(file, contents):
+ success = False
+ violations += 1
+ finally:
+ handle.close()
+ print "Total violating files: %s" % violations
+ return success
+
+
+def GetOptions():
+ result = optparse.OptionParser()
+ result.add_option('--no-lint', help="Do not run cpplint", default=False,
+ action="store_true")
+ return result
+
+
+def Main():
+ workspace = abspath(join(dirname(sys.argv[0]), '..'))
+ parser = GetOptions()
+ (options, args) = parser.parse_args()
+ success = True
+ print "Running C++ lint check..."
+ if not options.no_lint:
+ success = CppLintProcessor().Run(workspace) and success
+ print "Running copyright header, trailing whitespaces and " \
+ "two empty lines between declarations check..."
+ success = SourceProcessor().Run(workspace) and success
+ if success:
+ return 0
+ else:
+ return 1
+
+
+if __name__ == '__main__':
+ sys.exit(Main())
diff --git a/chromium/v8/tools/process-heap-prof.py b/chromium/v8/tools/process-heap-prof.py
new file mode 100755
index 00000000000..a26cbf1589b
--- /dev/null
+++ b/chromium/v8/tools/process-heap-prof.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+#
+# 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.
+
+# This is an utility for converting V8 heap logs into .hp files that can
+# be further processed using 'hp2ps' tool (bundled with GHC and Valgrind)
+# to produce heap usage histograms.
+
+# Sample usage:
+# $ ./shell --log-gc script.js
+# $ tools/process-heap-prof.py v8.log | hp2ps -c > script-heap-graph.ps
+# ('-c' enables color, see hp2ps manual page for more options)
+# or
+# $ tools/process-heap-prof.py --js-cons-profile v8.log | hp2ps -c > script-heap-graph.ps
+# to get JS constructor profile
+
+
+import csv, sys, time, optparse
+
+def ProcessLogFile(filename, options):
+ if options.js_cons_profile:
+ itemname = 'heap-js-cons-item'
+ else:
+ itemname = 'heap-sample-item'
+
+ first_call_time = None
+ sample_time = 0.0
+ sampling = False
+ try:
+ logfile = open(filename, 'rb')
+ try:
+ logreader = csv.reader(logfile)
+
+ print('JOB "v8"')
+ print('DATE "%s"' % time.asctime(time.localtime()))
+ print('SAMPLE_UNIT "seconds"')
+ print('VALUE_UNIT "bytes"')
+
+ for row in logreader:
+ if row[0] == 'heap-sample-begin' and row[1] == 'Heap':
+ sample_time = float(row[3])/1000.0
+ if first_call_time == None:
+ first_call_time = sample_time
+ sample_time -= first_call_time
+ print('BEGIN_SAMPLE %.2f' % sample_time)
+ sampling = True
+ elif row[0] == 'heap-sample-end' and row[1] == 'Heap':
+ print('END_SAMPLE %.2f' % sample_time)
+ sampling = False
+ elif row[0] == itemname and sampling:
+ print(row[1]),
+ if options.count:
+ print('%d' % (int(row[2]))),
+ if options.size:
+ print('%d' % (int(row[3]))),
+ print
+ finally:
+ logfile.close()
+ except:
+ sys.exit('can\'t open %s' % filename)
+
+
+def BuildOptions():
+ result = optparse.OptionParser()
+ result.add_option("--js_cons_profile", help="Constructor profile",
+ default=False, action="store_true")
+ result.add_option("--size", help="Report object size",
+ default=False, action="store_true")
+ result.add_option("--count", help="Report object count",
+ default=False, action="store_true")
+ return result
+
+
+def ProcessOptions(options):
+ if not options.size and not options.count:
+ options.size = True
+ return True
+
+
+def Main():
+ parser = BuildOptions()
+ (options, args) = parser.parse_args()
+ if not ProcessOptions(options):
+ parser.print_help()
+ sys.exit();
+
+ if not args:
+ print "Missing logfile"
+ sys.exit();
+
+ ProcessLogFile(args[0], options)
+
+
+if __name__ == '__main__':
+ sys.exit(Main())
diff --git a/chromium/v8/tools/profile.js b/chromium/v8/tools/profile.js
new file mode 100644
index 00000000000..10a07f8246a
--- /dev/null
+++ b/chromium/v8/tools/profile.js
@@ -0,0 +1,795 @@
+// 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.
+
+
+/**
+ * Creates a profile object for processing profiling-related events
+ * and calculating function execution times.
+ *
+ * @constructor
+ */
+function Profile() {
+ this.codeMap_ = new CodeMap();
+ this.topDownTree_ = new CallTree();
+ this.bottomUpTree_ = new CallTree();
+};
+
+
+/**
+ * Returns whether a function with the specified name must be skipped.
+ * Should be overriden by subclasses.
+ *
+ * @param {string} name Function name.
+ */
+Profile.prototype.skipThisFunction = function(name) {
+ return false;
+};
+
+
+/**
+ * Enum for profiler operations that involve looking up existing
+ * code entries.
+ *
+ * @enum {number}
+ */
+Profile.Operation = {
+ MOVE: 0,
+ DELETE: 1,
+ TICK: 2
+};
+
+
+/**
+ * Enum for code state regarding its dynamic optimization.
+ *
+ * @enum {number}
+ */
+Profile.CodeState = {
+ COMPILED: 0,
+ OPTIMIZABLE: 1,
+ OPTIMIZED: 2
+};
+
+
+/**
+ * Called whenever the specified operation has failed finding a function
+ * containing the specified address. Should be overriden by subclasses.
+ * See the Profile.Operation enum for the list of
+ * possible operations.
+ *
+ * @param {number} operation Operation.
+ * @param {number} addr Address of the unknown code.
+ * @param {number} opt_stackPos If an unknown address is encountered
+ * during stack strace processing, specifies a position of the frame
+ * containing the address.
+ */
+Profile.prototype.handleUnknownCode = function(
+ operation, addr, opt_stackPos) {
+};
+
+
+/**
+ * Registers a library.
+ *
+ * @param {string} name Code entry name.
+ * @param {number} startAddr Starting address.
+ * @param {number} endAddr Ending address.
+ */
+Profile.prototype.addLibrary = function(
+ name, startAddr, endAddr) {
+ var entry = new CodeMap.CodeEntry(
+ endAddr - startAddr, name);
+ this.codeMap_.addLibrary(startAddr, entry);
+ return entry;
+};
+
+
+/**
+ * Registers statically compiled code entry.
+ *
+ * @param {string} name Code entry name.
+ * @param {number} startAddr Starting address.
+ * @param {number} endAddr Ending address.
+ */
+Profile.prototype.addStaticCode = function(
+ name, startAddr, endAddr) {
+ var entry = new CodeMap.CodeEntry(
+ endAddr - startAddr, name);
+ this.codeMap_.addStaticCode(startAddr, entry);
+ return entry;
+};
+
+
+/**
+ * Registers dynamic (JIT-compiled) code entry.
+ *
+ * @param {string} type Code entry type.
+ * @param {string} name Code entry name.
+ * @param {number} start Starting address.
+ * @param {number} size Code entry size.
+ */
+Profile.prototype.addCode = function(
+ type, name, start, size) {
+ var entry = new Profile.DynamicCodeEntry(size, type, name);
+ this.codeMap_.addCode(start, entry);
+ return entry;
+};
+
+
+/**
+ * Registers dynamic (JIT-compiled) code entry.
+ *
+ * @param {string} type Code entry type.
+ * @param {string} name Code entry name.
+ * @param {number} start Starting address.
+ * @param {number} size Code entry size.
+ * @param {number} funcAddr Shared function object address.
+ * @param {Profile.CodeState} state Optimization state.
+ */
+Profile.prototype.addFuncCode = function(
+ type, name, start, size, funcAddr, state) {
+ // As code and functions are in the same address space,
+ // it is safe to put them in a single code map.
+ var func = this.codeMap_.findDynamicEntryByStartAddress(funcAddr);
+ if (!func) {
+ func = new Profile.FunctionEntry(name);
+ this.codeMap_.addCode(funcAddr, func);
+ } else if (func.name !== name) {
+ // Function object has been overwritten with a new one.
+ func.name = name;
+ }
+ var entry = this.codeMap_.findDynamicEntryByStartAddress(start);
+ if (entry) {
+ if (entry.size === size && entry.func === func) {
+ // Entry state has changed.
+ entry.state = state;
+ }
+ } else {
+ entry = new Profile.DynamicFuncCodeEntry(size, type, func, state);
+ this.codeMap_.addCode(start, entry);
+ }
+ return entry;
+};
+
+
+/**
+ * Reports about moving of a dynamic code entry.
+ *
+ * @param {number} from Current code entry address.
+ * @param {number} to New code entry address.
+ */
+Profile.prototype.moveCode = function(from, to) {
+ try {
+ this.codeMap_.moveCode(from, to);
+ } catch (e) {
+ this.handleUnknownCode(Profile.Operation.MOVE, from);
+ }
+};
+
+
+/**
+ * Reports about deletion of a dynamic code entry.
+ *
+ * @param {number} start Starting address.
+ */
+Profile.prototype.deleteCode = function(start) {
+ try {
+ this.codeMap_.deleteCode(start);
+ } catch (e) {
+ this.handleUnknownCode(Profile.Operation.DELETE, start);
+ }
+};
+
+
+/**
+ * Reports about moving of a dynamic code entry.
+ *
+ * @param {number} from Current code entry address.
+ * @param {number} to New code entry address.
+ */
+Profile.prototype.moveFunc = function(from, to) {
+ if (this.codeMap_.findDynamicEntryByStartAddress(from)) {
+ this.codeMap_.moveCode(from, to);
+ }
+};
+
+
+/**
+ * Retrieves a code entry by an address.
+ *
+ * @param {number} addr Entry address.
+ */
+Profile.prototype.findEntry = function(addr) {
+ return this.codeMap_.findEntry(addr);
+};
+
+
+/**
+ * Records a tick event. Stack must contain a sequence of
+ * addresses starting with the program counter value.
+ *
+ * @param {Array<number>} stack Stack sample.
+ */
+Profile.prototype.recordTick = function(stack) {
+ var processedStack = this.resolveAndFilterFuncs_(stack);
+ this.bottomUpTree_.addPath(processedStack);
+ processedStack.reverse();
+ this.topDownTree_.addPath(processedStack);
+};
+
+
+/**
+ * Translates addresses into function names and filters unneeded
+ * functions.
+ *
+ * @param {Array<number>} stack Stack sample.
+ */
+Profile.prototype.resolveAndFilterFuncs_ = function(stack) {
+ var result = [];
+ for (var i = 0; i < stack.length; ++i) {
+ var entry = this.codeMap_.findEntry(stack[i]);
+ if (entry) {
+ var name = entry.getName();
+ if (!this.skipThisFunction(name)) {
+ result.push(name);
+ }
+ } else {
+ this.handleUnknownCode(
+ Profile.Operation.TICK, stack[i], i);
+ }
+ }
+ return result;
+};
+
+
+/**
+ * Performs a BF traversal of the top down call graph.
+ *
+ * @param {function(CallTree.Node)} f Visitor function.
+ */
+Profile.prototype.traverseTopDownTree = function(f) {
+ this.topDownTree_.traverse(f);
+};
+
+
+/**
+ * Performs a BF traversal of the bottom up call graph.
+ *
+ * @param {function(CallTree.Node)} f Visitor function.
+ */
+Profile.prototype.traverseBottomUpTree = function(f) {
+ this.bottomUpTree_.traverse(f);
+};
+
+
+/**
+ * Calculates a top down profile for a node with the specified label.
+ * If no name specified, returns the whole top down calls tree.
+ *
+ * @param {string} opt_label Node label.
+ */
+Profile.prototype.getTopDownProfile = function(opt_label) {
+ return this.getTreeProfile_(this.topDownTree_, opt_label);
+};
+
+
+/**
+ * Calculates a bottom up profile for a node with the specified label.
+ * If no name specified, returns the whole bottom up calls tree.
+ *
+ * @param {string} opt_label Node label.
+ */
+Profile.prototype.getBottomUpProfile = function(opt_label) {
+ return this.getTreeProfile_(this.bottomUpTree_, opt_label);
+};
+
+
+/**
+ * Helper function for calculating a tree profile.
+ *
+ * @param {Profile.CallTree} tree Call tree.
+ * @param {string} opt_label Node label.
+ */
+Profile.prototype.getTreeProfile_ = function(tree, opt_label) {
+ if (!opt_label) {
+ tree.computeTotalWeights();
+ return tree;
+ } else {
+ var subTree = tree.cloneSubtree(opt_label);
+ subTree.computeTotalWeights();
+ return subTree;
+ }
+};
+
+
+/**
+ * Calculates a flat profile of callees starting from a node with
+ * the specified label. If no name specified, starts from the root.
+ *
+ * @param {string} opt_label Starting node label.
+ */
+Profile.prototype.getFlatProfile = function(opt_label) {
+ var counters = new CallTree();
+ var rootLabel = opt_label || CallTree.ROOT_NODE_LABEL;
+ var precs = {};
+ precs[rootLabel] = 0;
+ var root = counters.findOrAddChild(rootLabel);
+
+ this.topDownTree_.computeTotalWeights();
+ this.topDownTree_.traverseInDepth(
+ function onEnter(node) {
+ if (!(node.label in precs)) {
+ precs[node.label] = 0;
+ }
+ var nodeLabelIsRootLabel = node.label == rootLabel;
+ if (nodeLabelIsRootLabel || precs[rootLabel] > 0) {
+ if (precs[rootLabel] == 0) {
+ root.selfWeight += node.selfWeight;
+ root.totalWeight += node.totalWeight;
+ } else {
+ var rec = root.findOrAddChild(node.label);
+ rec.selfWeight += node.selfWeight;
+ if (nodeLabelIsRootLabel || precs[node.label] == 0) {
+ rec.totalWeight += node.totalWeight;
+ }
+ }
+ precs[node.label]++;
+ }
+ },
+ function onExit(node) {
+ if (node.label == rootLabel || precs[rootLabel] > 0) {
+ precs[node.label]--;
+ }
+ },
+ null);
+
+ if (!opt_label) {
+ // If we have created a flat profile for the whole program, we don't
+ // need an explicit root in it. Thus, replace the counters tree
+ // root with the node corresponding to the whole program.
+ counters.root_ = root;
+ } else {
+ // Propagate weights so percents can be calculated correctly.
+ counters.getRoot().selfWeight = root.selfWeight;
+ counters.getRoot().totalWeight = root.totalWeight;
+ }
+ return counters;
+};
+
+
+/**
+ * Cleans up function entries that are not referenced by code entries.
+ */
+Profile.prototype.cleanUpFuncEntries = function() {
+ var referencedFuncEntries = [];
+ var entries = this.codeMap_.getAllDynamicEntriesWithAddresses();
+ for (var i = 0, l = entries.length; i < l; ++i) {
+ if (entries[i][1].constructor === Profile.FunctionEntry) {
+ entries[i][1].used = false;
+ }
+ }
+ for (var i = 0, l = entries.length; i < l; ++i) {
+ if ("func" in entries[i][1]) {
+ entries[i][1].func.used = true;
+ }
+ }
+ for (var i = 0, l = entries.length; i < l; ++i) {
+ if (entries[i][1].constructor === Profile.FunctionEntry &&
+ !entries[i][1].used) {
+ this.codeMap_.deleteCode(entries[i][0]);
+ }
+ }
+};
+
+
+/**
+ * Creates a dynamic code entry.
+ *
+ * @param {number} size Code size.
+ * @param {string} type Code type.
+ * @param {string} name Function name.
+ * @constructor
+ */
+Profile.DynamicCodeEntry = function(size, type, name) {
+ CodeMap.CodeEntry.call(this, size, name);
+ this.type = type;
+};
+
+
+/**
+ * Returns node name.
+ */
+Profile.DynamicCodeEntry.prototype.getName = function() {
+ return this.type + ': ' + this.name;
+};
+
+
+/**
+ * Returns raw node name (without type decoration).
+ */
+Profile.DynamicCodeEntry.prototype.getRawName = function() {
+ return this.name;
+};
+
+
+Profile.DynamicCodeEntry.prototype.isJSFunction = function() {
+ return false;
+};
+
+
+Profile.DynamicCodeEntry.prototype.toString = function() {
+ return this.getName() + ': ' + this.size.toString(16);
+};
+
+
+/**
+ * Creates a dynamic code entry.
+ *
+ * @param {number} size Code size.
+ * @param {string} type Code type.
+ * @param {Profile.FunctionEntry} func Shared function entry.
+ * @param {Profile.CodeState} state Code optimization state.
+ * @constructor
+ */
+Profile.DynamicFuncCodeEntry = function(size, type, func, state) {
+ CodeMap.CodeEntry.call(this, size);
+ this.type = type;
+ this.func = func;
+ this.state = state;
+};
+
+Profile.DynamicFuncCodeEntry.STATE_PREFIX = ["", "~", "*"];
+
+/**
+ * Returns node name.
+ */
+Profile.DynamicFuncCodeEntry.prototype.getName = function() {
+ var name = this.func.getName();
+ return this.type + ': ' + Profile.DynamicFuncCodeEntry.STATE_PREFIX[this.state] + name;
+};
+
+
+/**
+ * Returns raw node name (without type decoration).
+ */
+Profile.DynamicFuncCodeEntry.prototype.getRawName = function() {
+ return this.func.getName();
+};
+
+
+Profile.DynamicFuncCodeEntry.prototype.isJSFunction = function() {
+ return true;
+};
+
+
+Profile.DynamicFuncCodeEntry.prototype.toString = function() {
+ return this.getName() + ': ' + this.size.toString(16);
+};
+
+
+/**
+ * Creates a shared function object entry.
+ *
+ * @param {string} name Function name.
+ * @constructor
+ */
+Profile.FunctionEntry = function(name) {
+ CodeMap.CodeEntry.call(this, 0, name);
+};
+
+
+/**
+ * Returns node name.
+ */
+Profile.FunctionEntry.prototype.getName = function() {
+ var name = this.name;
+ if (name.length == 0) {
+ name = '<anonymous>';
+ } else if (name.charAt(0) == ' ') {
+ // An anonymous function with location: " aaa.js:10".
+ name = '<anonymous>' + name;
+ }
+ return name;
+};
+
+Profile.FunctionEntry.prototype.toString = CodeMap.CodeEntry.prototype.toString;
+
+/**
+ * Constructs a call graph.
+ *
+ * @constructor
+ */
+function CallTree() {
+ this.root_ = new CallTree.Node(
+ CallTree.ROOT_NODE_LABEL);
+};
+
+
+/**
+ * The label of the root node.
+ */
+CallTree.ROOT_NODE_LABEL = '';
+
+
+/**
+ * @private
+ */
+CallTree.prototype.totalsComputed_ = false;
+
+
+/**
+ * Returns the tree root.
+ */
+CallTree.prototype.getRoot = function() {
+ return this.root_;
+};
+
+
+/**
+ * Adds the specified call path, constructing nodes as necessary.
+ *
+ * @param {Array<string>} path Call path.
+ */
+CallTree.prototype.addPath = function(path) {
+ if (path.length == 0) {
+ return;
+ }
+ var curr = this.root_;
+ for (var i = 0; i < path.length; ++i) {
+ curr = curr.findOrAddChild(path[i]);
+ }
+ curr.selfWeight++;
+ this.totalsComputed_ = false;
+};
+
+
+/**
+ * Finds an immediate child of the specified parent with the specified
+ * label, creates a child node if necessary. If a parent node isn't
+ * specified, uses tree root.
+ *
+ * @param {string} label Child node label.
+ */
+CallTree.prototype.findOrAddChild = function(label) {
+ return this.root_.findOrAddChild(label);
+};
+
+
+/**
+ * Creates a subtree by cloning and merging all subtrees rooted at nodes
+ * with a given label. E.g. cloning the following call tree on label 'A'
+ * will give the following result:
+ *
+ * <A>--<B> <B>
+ * / /
+ * <root> == clone on 'A' ==> <root>--<A>
+ * \ \
+ * <C>--<A>--<D> <D>
+ *
+ * And <A>'s selfWeight will be the sum of selfWeights of <A>'s from the
+ * source call tree.
+ *
+ * @param {string} label The label of the new root node.
+ */
+CallTree.prototype.cloneSubtree = function(label) {
+ var subTree = new CallTree();
+ this.traverse(function(node, parent) {
+ if (!parent && node.label != label) {
+ return null;
+ }
+ var child = (parent ? parent : subTree).findOrAddChild(node.label);
+ child.selfWeight += node.selfWeight;
+ return child;
+ });
+ return subTree;
+};
+
+
+/**
+ * Computes total weights in the call graph.
+ */
+CallTree.prototype.computeTotalWeights = function() {
+ if (this.totalsComputed_) {
+ return;
+ }
+ this.root_.computeTotalWeight();
+ this.totalsComputed_ = true;
+};
+
+
+/**
+ * Traverses the call graph in preorder. This function can be used for
+ * building optionally modified tree clones. This is the boilerplate code
+ * for this scenario:
+ *
+ * callTree.traverse(function(node, parentClone) {
+ * var nodeClone = cloneNode(node);
+ * if (parentClone)
+ * parentClone.addChild(nodeClone);
+ * return nodeClone;
+ * });
+ *
+ * @param {function(CallTree.Node, *)} f Visitor function.
+ * The second parameter is the result of calling 'f' on the parent node.
+ */
+CallTree.prototype.traverse = function(f) {
+ var pairsToProcess = new ConsArray();
+ pairsToProcess.concat([{node: this.root_, param: null}]);
+ while (!pairsToProcess.atEnd()) {
+ var pair = pairsToProcess.next();
+ var node = pair.node;
+ var newParam = f(node, pair.param);
+ var morePairsToProcess = [];
+ node.forEachChild(function (child) {
+ morePairsToProcess.push({node: child, param: newParam}); });
+ pairsToProcess.concat(morePairsToProcess);
+ }
+};
+
+
+/**
+ * Performs an indepth call graph traversal.
+ *
+ * @param {function(CallTree.Node)} enter A function called
+ * prior to visiting node's children.
+ * @param {function(CallTree.Node)} exit A function called
+ * after visiting node's children.
+ */
+CallTree.prototype.traverseInDepth = function(enter, exit) {
+ function traverse(node) {
+ enter(node);
+ node.forEachChild(traverse);
+ exit(node);
+ }
+ traverse(this.root_);
+};
+
+
+/**
+ * Constructs a call graph node.
+ *
+ * @param {string} label Node label.
+ * @param {CallTree.Node} opt_parent Node parent.
+ */
+CallTree.Node = function(label, opt_parent) {
+ this.label = label;
+ this.parent = opt_parent;
+ this.children = {};
+};
+
+
+/**
+ * Node self weight (how many times this node was the last node in
+ * a call path).
+ * @type {number}
+ */
+CallTree.Node.prototype.selfWeight = 0;
+
+
+/**
+ * Node total weight (includes weights of all children).
+ * @type {number}
+ */
+CallTree.Node.prototype.totalWeight = 0;
+
+
+/**
+ * Adds a child node.
+ *
+ * @param {string} label Child node label.
+ */
+CallTree.Node.prototype.addChild = function(label) {
+ var child = new CallTree.Node(label, this);
+ this.children[label] = child;
+ return child;
+};
+
+
+/**
+ * Computes node's total weight.
+ */
+CallTree.Node.prototype.computeTotalWeight =
+ function() {
+ var totalWeight = this.selfWeight;
+ this.forEachChild(function(child) {
+ totalWeight += child.computeTotalWeight(); });
+ return this.totalWeight = totalWeight;
+};
+
+
+/**
+ * Returns all node's children as an array.
+ */
+CallTree.Node.prototype.exportChildren = function() {
+ var result = [];
+ this.forEachChild(function (node) { result.push(node); });
+ return result;
+};
+
+
+/**
+ * Finds an immediate child with the specified label.
+ *
+ * @param {string} label Child node label.
+ */
+CallTree.Node.prototype.findChild = function(label) {
+ return this.children[label] || null;
+};
+
+
+/**
+ * Finds an immediate child with the specified label, creates a child
+ * node if necessary.
+ *
+ * @param {string} label Child node label.
+ */
+CallTree.Node.prototype.findOrAddChild = function(label) {
+ return this.findChild(label) || this.addChild(label);
+};
+
+
+/**
+ * Calls the specified function for every child.
+ *
+ * @param {function(CallTree.Node)} f Visitor function.
+ */
+CallTree.Node.prototype.forEachChild = function(f) {
+ for (var c in this.children) {
+ f(this.children[c]);
+ }
+};
+
+
+/**
+ * Walks up from the current node up to the call tree root.
+ *
+ * @param {function(CallTree.Node)} f Visitor function.
+ */
+CallTree.Node.prototype.walkUpToRoot = function(f) {
+ for (var curr = this; curr != null; curr = curr.parent) {
+ f(curr);
+ }
+};
+
+
+/**
+ * Tries to find a node with the specified path.
+ *
+ * @param {Array<string>} labels The path.
+ * @param {function(CallTree.Node)} opt_f Visitor function.
+ */
+CallTree.Node.prototype.descendToChild = function(
+ labels, opt_f) {
+ for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) {
+ var child = curr.findChild(labels[pos]);
+ if (opt_f) {
+ opt_f(child, pos);
+ }
+ curr = child;
+ }
+ return curr;
+};
diff --git a/chromium/v8/tools/profile_view.js b/chromium/v8/tools/profile_view.js
new file mode 100644
index 00000000000..e041909b01a
--- /dev/null
+++ b/chromium/v8/tools/profile_view.js
@@ -0,0 +1,219 @@
+// 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.
+
+
+/**
+ * Creates a Profile View builder object.
+ *
+ * @param {number} samplingRate Number of ms between profiler ticks.
+ * @constructor
+ */
+function ViewBuilder(samplingRate) {
+ this.samplingRate = samplingRate;
+};
+
+
+/**
+ * Builds a profile view for the specified call tree.
+ *
+ * @param {CallTree} callTree A call tree.
+ * @param {boolean} opt_bottomUpViewWeights Whether remapping
+ * of self weights for a bottom up view is needed.
+ */
+ViewBuilder.prototype.buildView = function(
+ callTree, opt_bottomUpViewWeights) {
+ var head;
+ var samplingRate = this.samplingRate;
+ var createViewNode = this.createViewNode;
+ callTree.traverse(function(node, viewParent) {
+ var totalWeight = node.totalWeight * samplingRate;
+ var selfWeight = node.selfWeight * samplingRate;
+ if (opt_bottomUpViewWeights === true) {
+ if (viewParent === head) {
+ selfWeight = totalWeight;
+ } else {
+ selfWeight = 0;
+ }
+ }
+ var viewNode = createViewNode(node.label, totalWeight, selfWeight, head);
+ if (viewParent) {
+ viewParent.addChild(viewNode);
+ } else {
+ head = viewNode;
+ }
+ return viewNode;
+ });
+ var view = this.createView(head);
+ return view;
+};
+
+
+/**
+ * Factory method for a profile view.
+ *
+ * @param {ProfileView.Node} head View head node.
+ * @return {ProfileView} Profile view.
+ */
+ViewBuilder.prototype.createView = function(head) {
+ return new ProfileView(head);
+};
+
+
+/**
+ * Factory method for a profile view node.
+ *
+ * @param {string} internalFuncName A fully qualified function name.
+ * @param {number} totalTime Amount of time that application spent in the
+ * corresponding function and its descendants (not that depending on
+ * profile they can be either callees or callers.)
+ * @param {number} selfTime Amount of time that application spent in the
+ * corresponding function only.
+ * @param {ProfileView.Node} head Profile view head.
+ * @return {ProfileView.Node} Profile view node.
+ */
+ViewBuilder.prototype.createViewNode = function(
+ funcName, totalTime, selfTime, head) {
+ return new ProfileView.Node(
+ funcName, totalTime, selfTime, head);
+};
+
+
+/**
+ * Creates a Profile View object. It allows to perform sorting
+ * and filtering actions on the profile.
+ *
+ * @param {ProfileView.Node} head Head (root) node.
+ * @constructor
+ */
+function ProfileView(head) {
+ this.head = head;
+};
+
+
+/**
+ * Sorts the profile view using the specified sort function.
+ *
+ * @param {function(ProfileView.Node,
+ * ProfileView.Node):number} sortFunc A sorting
+ * functions. Must comply with Array.sort sorting function requirements.
+ */
+ProfileView.prototype.sort = function(sortFunc) {
+ this.traverse(function (node) {
+ node.sortChildren(sortFunc);
+ });
+};
+
+
+/**
+ * Traverses profile view nodes in preorder.
+ *
+ * @param {function(ProfileView.Node)} f Visitor function.
+ */
+ProfileView.prototype.traverse = function(f) {
+ var nodesToTraverse = new ConsArray();
+ nodesToTraverse.concat([this.head]);
+ while (!nodesToTraverse.atEnd()) {
+ var node = nodesToTraverse.next();
+ f(node);
+ nodesToTraverse.concat(node.children);
+ }
+};
+
+
+/**
+ * Constructs a Profile View node object. Each node object corresponds to
+ * a function call.
+ *
+ * @param {string} internalFuncName A fully qualified function name.
+ * @param {number} totalTime Amount of time that application spent in the
+ * corresponding function and its descendants (not that depending on
+ * profile they can be either callees or callers.)
+ * @param {number} selfTime Amount of time that application spent in the
+ * corresponding function only.
+ * @param {ProfileView.Node} head Profile view head.
+ * @constructor
+ */
+ProfileView.Node = function(
+ internalFuncName, totalTime, selfTime, head) {
+ this.internalFuncName = internalFuncName;
+ this.totalTime = totalTime;
+ this.selfTime = selfTime;
+ this.head = head;
+ this.parent = null;
+ this.children = [];
+};
+
+
+/**
+ * Returns a share of the function's total time in application's total time.
+ */
+ProfileView.Node.prototype.__defineGetter__(
+ 'totalPercent',
+ function() { return this.totalTime /
+ (this.head ? this.head.totalTime : this.totalTime) * 100.0; });
+
+
+/**
+ * Returns a share of the function's self time in application's total time.
+ */
+ProfileView.Node.prototype.__defineGetter__(
+ 'selfPercent',
+ function() { return this.selfTime /
+ (this.head ? this.head.totalTime : this.totalTime) * 100.0; });
+
+
+/**
+ * Returns a share of the function's total time in its parent's total time.
+ */
+ProfileView.Node.prototype.__defineGetter__(
+ 'parentTotalPercent',
+ function() { return this.totalTime /
+ (this.parent ? this.parent.totalTime : this.totalTime) * 100.0; });
+
+
+/**
+ * Adds a child to the node.
+ *
+ * @param {ProfileView.Node} node Child node.
+ */
+ProfileView.Node.prototype.addChild = function(node) {
+ node.parent = this;
+ this.children.push(node);
+};
+
+
+/**
+ * Sorts all the node's children recursively.
+ *
+ * @param {function(ProfileView.Node,
+ * ProfileView.Node):number} sortFunc A sorting
+ * functions. Must comply with Array.sort sorting function requirements.
+ */
+ProfileView.Node.prototype.sortChildren = function(
+ sortFunc) {
+ this.children.sort(sortFunc);
+};
diff --git a/chromium/v8/tools/profviz/composer.js b/chromium/v8/tools/profviz/composer.js
new file mode 100644
index 00000000000..cdfc0b7b39a
--- /dev/null
+++ b/chromium/v8/tools/profviz/composer.js
@@ -0,0 +1,580 @@
+// Copyright 2013 the V8 project authors. 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.prototype.top = function() {
+ if (this.length == 0) return undefined;
+ return this[this.length - 1];
+}
+
+
+function PlotScriptComposer(kResX, kResY) {
+ // Constants.
+ var kV8BinarySuffixes = ["/d8", "/libv8.so"];
+ var kStackFrames = 8; // Stack frames to display in the plot.
+
+ var kTimerEventWidth = 0.33; // Width of each timeline.
+ var kExecutionFrameWidth = 0.2; // Width of the top stack frame line.
+ var kStackFrameWidth = 0.1; // Width of the lower stack frame lines.
+ var kGapWidth = 0.05; // Gap between stack frame lines.
+
+ var kY1Offset = 11; // Offset for stack frame vs. event lines.
+ var kDeoptRow = 7; // Row displaying deopts.
+ var kMaxDeoptLength = 4; // Draw size of the largest deopt.
+ var kPauseLabelPadding = 5; // Padding for pause time labels.
+ var kNumPauseLabels = 7; // Number of biggest pauses to label.
+ var kCodeKindLabelPadding = 100; // Padding for code kind labels.
+
+ var kTickHalfDuration = 0.5; // Duration of half a tick in ms.
+ var kMinRangeLength = 0.0005; // Minimum length for an event in ms.
+
+ var kNumThreads = 2; // Number of threads.
+ var kExecutionThreadId = 0; // ID of main thread.
+
+ // Init values.
+ var num_timer_event = kY1Offset + 0.5;
+
+ // Data structures.
+ function TimerEvent(label, color, pause, thread_id) {
+ assert(thread_id >= 0 && thread_id < kNumThreads, "invalid thread id");
+ this.label = label;
+ this.color = color;
+ this.pause = pause;
+ this.ranges = [];
+ this.thread_id = thread_id;
+ this.index = ++num_timer_event;
+ }
+
+ function CodeKind(color, kinds) {
+ this.color = color;
+ this.in_execution = [];
+ this.stack_frames = [];
+ for (var i = 0; i < kStackFrames; i++) this.stack_frames.push([]);
+ this.kinds = kinds;
+ }
+
+ function Range(start, end) {
+ this.start = start; // In milliseconds.
+ this.end = end; // In milliseconds.
+ }
+
+ function Deopt(time, size) {
+ this.time = time; // In milliseconds.
+ this.size = size; // In bytes.
+ }
+
+ Range.prototype.duration = function() { return this.end - this.start; }
+
+ function Tick(tick) {
+ this.tick = tick;
+ }
+
+ var TimerEvents = {
+ 'V8.Execute':
+ new TimerEvent("execution", "#000000", false, 0),
+ 'V8.External':
+ new TimerEvent("external", "#3399FF", false, 0),
+ 'V8.CompileFullCode':
+ new TimerEvent("compile unopt", "#CC0000", true, 0),
+ 'V8.RecompileSynchronous':
+ new TimerEvent("recompile sync", "#CC0044", true, 0),
+ 'V8.RecompileParallel':
+ new TimerEvent("recompile async", "#CC4499", false, 1),
+ 'V8.CompileEval':
+ new TimerEvent("compile eval", "#CC4400", true, 0),
+ 'V8.Parse':
+ new TimerEvent("parse", "#00CC00", true, 0),
+ 'V8.PreParse':
+ new TimerEvent("preparse", "#44CC00", true, 0),
+ 'V8.ParseLazy':
+ new TimerEvent("lazy parse", "#00CC44", true, 0),
+ 'V8.GCScavenger':
+ new TimerEvent("gc scavenge", "#0044CC", true, 0),
+ 'V8.GCCompactor':
+ new TimerEvent("gc compaction", "#4444CC", true, 0),
+ 'V8.GCContext':
+ new TimerEvent("gc context", "#4400CC", true, 0),
+ };
+
+ var CodeKinds = {
+ 'external ': new CodeKind("#3399FF", [-2]),
+ 'runtime ': new CodeKind("#000000", [-1]),
+ 'full code': new CodeKind("#DD0000", [0]),
+ 'opt code ': new CodeKind("#00EE00", [1]),
+ 'code stub': new CodeKind("#FF00FF", [2]),
+ 'built-in ': new CodeKind("#AA00AA", [3]),
+ 'inl.cache': new CodeKind("#4444AA",
+ [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]),
+ 'reg.exp. ': new CodeKind("#0000FF", [15]),
+ };
+
+ var code_map = new CodeMap();
+ var execution_pauses = [];
+ var deopts = [];
+ var event_stack = [];
+ var last_time_stamp = [];
+ for (var i = 0; i < kNumThreads; i++) {
+ event_stack[i] = [];
+ last_time_stamp[i] = -1;
+ }
+
+ var range_start = undefined;
+ var range_end = undefined;
+ var obj_index = 0;
+ var pause_tolerance = 0.005; // Milliseconds.
+ var distortion = 0;
+
+ // Utility functions.
+ function assert(something, message) {
+ if (!something) print(new Error(message).stack);
+ }
+
+ function FindCodeKind(kind) {
+ for (name in CodeKinds) {
+ if (CodeKinds[name].kinds.indexOf(kind) >= 0) {
+ return CodeKinds[name];
+ }
+ }
+ }
+
+ function TicksToRanges(ticks) {
+ var ranges = [];
+ for (var i = 0; i < ticks.length; i++) {
+ var tick = ticks[i].tick;
+ ranges.push(
+ new Range(tick - kTickHalfDuration, tick + kTickHalfDuration));
+ }
+ return ranges;
+ }
+
+ function MergeRanges(ranges) {
+ ranges.sort(function(a, b) { return a.start - b.start; });
+ var result = [];
+ var j = 0;
+ for (var i = 0; i < ranges.length; i = j) {
+ var merge_start = ranges[i].start;
+ if (merge_start > range_end) break; // Out of plot range.
+ var merge_end = ranges[i].end;
+ for (j = i + 1; j < ranges.length; j++) {
+ var next_range = ranges[j];
+ // Don't merge ranges if there is no overlap (incl. merge tolerance).
+ if (next_range.start > merge_end + pause_tolerance) break;
+ // Merge ranges.
+ if (next_range.end > merge_end) { // Extend range end.
+ merge_end = next_range.end;
+ }
+ }
+ if (merge_end < range_start) continue; // Out of plot range.
+ if (merge_end < merge_start) continue; // Not an actual range.
+ result.push(new Range(merge_start, merge_end));
+ }
+ return result;
+ }
+
+ function RestrictRangesTo(ranges, start, end) {
+ var result = [];
+ for (var i = 0; i < ranges.length; i++) {
+ if (ranges[i].start <= end && ranges[i].end >= start) {
+ result.push(new Range(Math.max(ranges[i].start, start),
+ Math.min(ranges[i].end, end)));
+ }
+ }
+ return result;
+ }
+
+ // Public methods.
+ this.collectData = function(input, distortion_per_entry) {
+
+ // Parse functions.
+ var parseTimeStamp = function(timestamp) {
+ distortion += distortion_per_entry;
+ return parseInt(timestamp) / 1000 - distortion;
+ }
+
+ var processTimerEventStart = function(name, start) {
+ // Find out the thread id.
+ var new_event = TimerEvents[name];
+ if (new_event === undefined) return;
+ var thread_id = new_event.thread_id;
+
+ start = Math.max(last_time_stamp[thread_id] + kMinRangeLength, start);
+
+ // Last event on this thread is done with the start of this event.
+ var last_event = event_stack[thread_id].top();
+ if (last_event !== undefined) {
+ var new_range = new Range(last_time_stamp[thread_id], start);
+ last_event.ranges.push(new_range);
+ }
+ event_stack[thread_id].push(new_event);
+ last_time_stamp[thread_id] = start;
+ };
+
+ var processTimerEventEnd = function(name, end) {
+ // Find out about the thread_id.
+ var finished_event = TimerEvents[name];
+ var thread_id = finished_event.thread_id;
+ assert(finished_event === event_stack[thread_id].pop(),
+ "inconsistent event stack");
+
+ end = Math.max(last_time_stamp[thread_id] + kMinRangeLength, end);
+
+ var new_range = new Range(last_time_stamp[thread_id], end);
+ finished_event.ranges.push(new_range);
+ last_time_stamp[thread_id] = end;
+ };
+
+ var processCodeCreateEvent = function(type, kind, address, size, name) {
+ var code_entry = new CodeMap.CodeEntry(size, name);
+ code_entry.kind = kind;
+ code_map.addCode(address, code_entry);
+ };
+
+ var processCodeMoveEvent = function(from, to) {
+ code_map.moveCode(from, to);
+ };
+
+ var processCodeDeleteEvent = function(address) {
+ code_map.deleteCode(address);
+ };
+
+ var processSharedLibrary = function(name, start, end) {
+ var code_entry = new CodeMap.CodeEntry(end - start, name);
+ code_entry.kind = -2; // External code kind.
+ for (var i = 0; i < kV8BinarySuffixes.length; i++) {
+ var suffix = kV8BinarySuffixes[i];
+ if (name.indexOf(suffix, name.length - suffix.length) >= 0) {
+ code_entry.kind = -1; // V8 runtime code kind.
+ break;
+ }
+ }
+ code_map.addLibrary(start, code_entry);
+ };
+
+ var processTimerEventStart = function(name, start) {
+ // Find out the thread id.
+ var new_event = TimerEvents[name];
+ if (new_event === undefined) return;
+ var thread_id = new_event.thread_id;
+
+ start = Math.max(last_time_stamp[thread_id] + kMinRangeLength, start);
+
+ // Last event on this thread is done with the start of this event.
+ var last_event = event_stack[thread_id].top();
+ if (last_event !== undefined) {
+ var new_range = new Range(last_time_stamp[thread_id], start);
+ last_event.ranges.push(new_range);
+ }
+ event_stack[thread_id].push(new_event);
+ last_time_stamp[thread_id] = start;
+ };
+
+ var processTimerEventEnd = function(name, end) {
+ // Find out about the thread_id.
+ var finished_event = TimerEvents[name];
+ var thread_id = finished_event.thread_id;
+ assert(finished_event === event_stack[thread_id].pop(),
+ "inconsistent event stack");
+
+ end = Math.max(last_time_stamp[thread_id] + kMinRangeLength, end);
+
+ var new_range = new Range(last_time_stamp[thread_id], end);
+ finished_event.ranges.push(new_range);
+ last_time_stamp[thread_id] = end;
+ };
+
+ var processCodeCreateEvent = function(type, kind, address, size, name) {
+ var code_entry = new CodeMap.CodeEntry(size, name);
+ code_entry.kind = kind;
+ code_map.addCode(address, code_entry);
+ };
+
+ var processCodeMoveEvent = function(from, to) {
+ code_map.moveCode(from, to);
+ };
+
+ var processCodeDeleteEvent = function(address) {
+ code_map.deleteCode(address);
+ };
+
+ var processCodeDeoptEvent = function(time, size) {
+ deopts.push(new Deopt(time, size));
+ }
+
+ var processSharedLibrary = function(name, start, end) {
+ var code_entry = new CodeMap.CodeEntry(end - start, name);
+ code_entry.kind = -3; // External code kind.
+ for (var i = 0; i < kV8BinarySuffixes.length; i++) {
+ var suffix = kV8BinarySuffixes[i];
+ if (name.indexOf(suffix, name.length - suffix.length) >= 0) {
+ code_entry.kind = -1; // V8 runtime code kind.
+ break;
+ }
+ }
+ code_map.addLibrary(start, code_entry);
+ };
+
+ var processTickEvent = function(
+ pc, timer, unused_x, unused_y, vmstate, stack) {
+ var tick = new Tick(timer);
+
+ var entry = code_map.findEntry(pc);
+ if (entry) FindCodeKind(entry.kind).in_execution.push(tick);
+
+ for (var i = 0; i < kStackFrames; i++) {
+ if (!stack[i]) break;
+ var entry = code_map.findEntry(stack[i]);
+ if (entry) FindCodeKind(entry.kind).stack_frames[i].push(tick);
+ }
+ };
+ // Collect data from log.
+ var logreader = new LogReader(
+ { 'timer-event-start': { parsers: [null, parseTimeStamp],
+ processor: processTimerEventStart },
+ 'timer-event-end': { parsers: [null, parseTimeStamp],
+ processor: processTimerEventEnd },
+ 'shared-library': { parsers: [null, parseInt, parseInt],
+ processor: processSharedLibrary },
+ 'code-creation': { parsers: [null, parseInt, parseInt, parseInt, null],
+ processor: processCodeCreateEvent },
+ 'code-move': { parsers: [parseInt, parseInt],
+ processor: processCodeMoveEvent },
+ 'code-delete': { parsers: [parseInt],
+ processor: processCodeDeleteEvent },
+ 'code-deopt': { parsers: [parseTimeStamp, parseInt],
+ processor: processCodeDeoptEvent },
+ 'tick': { parsers: [parseInt, parseTimeStamp,
+ null, null, parseInt, 'var-args'],
+ processor: processTickEvent }
+ });
+
+ var line;
+ while (line = input()) {
+ logreader.processLogLine(line);
+ }
+
+ // Collect execution pauses.
+ for (name in TimerEvents) {
+ var event = TimerEvents[name];
+ if (!event.pause) continue;
+ var ranges = event.ranges;
+ for (var j = 0; j < ranges.length; j++) execution_pauses.push(ranges[j]);
+ }
+ execution_pauses = MergeRanges(execution_pauses);
+ };
+
+
+ this.findPlotRange = function(
+ range_start_override, range_end_override, result_callback) {
+ var start_found = (range_start_override || range_start_override == 0);
+ var end_found = (range_end_override || range_end_override == 0);
+ range_start = start_found ? range_start_override : Infinity;
+ range_end = end_found ? range_end_override : -Infinity;
+
+ if (!start_found || !end_found) {
+ for (name in TimerEvents) {
+ var ranges = TimerEvents[name].ranges;
+ for (var i = 0; i < ranges.length; i++) {
+ if (ranges[i].start < range_start && !start_found) {
+ range_start = ranges[i].start;
+ }
+ if (ranges[i].end > range_end && !end_found) {
+ range_end = ranges[i].end;
+ }
+ }
+ }
+
+ for (codekind in CodeKinds) {
+ var ticks = CodeKinds[codekind].in_execution;
+ for (var i = 0; i < ticks.length; i++) {
+ if (ticks[i].tick < range_start && !start_found) {
+ range_start = ticks[i].tick;
+ }
+ if (ticks[i].tick > range_end && !end_found) {
+ range_end = ticks[i].tick;
+ }
+ }
+ }
+ }
+ // Set pause tolerance to something appropriate for the plot resolution
+ // to make it easier for gnuplot.
+ pause_tolerance = (range_end - range_start) / kResX / 10;
+
+ if (typeof result_callback === 'function') {
+ result_callback(range_start, range_end);
+ }
+ };
+
+
+ this.assembleOutput = function(output) {
+ output("set yrange [0:" + (num_timer_event + 1) + "]");
+ output("set xlabel \"execution time in ms\"");
+ output("set xrange [" + range_start + ":" + range_end + "]");
+ output("set style fill pattern 2 bo 1");
+ output("set style rect fs solid 1 noborder");
+ output("set style line 1 lt 1 lw 1 lc rgb \"#000000\"");
+ output("set border 15 lw 0.2"); // Draw thin border box.
+ output("set style line 2 lt 1 lw 1 lc rgb \"#9944CC\"");
+ output("set xtics out nomirror");
+ output("unset key");
+
+ function DrawBarBase(color, start, end, top, bottom) {
+ obj_index++;
+ command = "set object " + obj_index + " rect";
+ command += " from " + start + ", " + top;
+ command += " to " + end + ", " + bottom;
+ command += " fc rgb \"" + color + "\"";
+ output(command);
+ }
+
+ function DrawBar(row, color, start, end, width) {
+ DrawBarBase(color, start, end, row + width, row - width);
+ }
+
+ function DrawHalfBar(row, color, start, end, width) {
+ DrawBarBase(color, start, end, row, row - width);
+ }
+
+ var percentages = {};
+ var total = 0;
+ for (var name in TimerEvents) {
+ var event = TimerEvents[name];
+ var ranges = RestrictRangesTo(event.ranges, range_start, range_end);
+ ranges = MergeRanges(ranges);
+ var sum =
+ ranges.map(function(range) { return range.duration(); })
+ .reduce(function(a, b) { return a + b; }, 0);
+ percentages[name] = (sum / (range_end - range_start) * 100).toFixed(1);
+ }
+
+ // Plot deopts.
+ deopts.sort(function(a, b) { return b.size - a.size; });
+ var max_deopt_size = deopts.length > 0 ? deopts[0].size : Infinity;
+
+ for (var i = 0; i < deopts.length; i++) {
+ var deopt = deopts[i];
+ DrawHalfBar(kDeoptRow, "#9944CC", deopt.time,
+ deopt.time + 10 * pause_tolerance,
+ deopt.size / max_deopt_size * kMaxDeoptLength);
+ }
+
+ // Name Y-axis.
+ var ytics = [];
+ for (name in TimerEvents) {
+ var index = TimerEvents[name].index;
+ var label = TimerEvents[name].label;
+ ytics.push('"' + label + ' (' + percentages[name] + '%%)" ' + index);
+ }
+ ytics.push('"code kind color coding" ' + kY1Offset);
+ ytics.push('"code kind in execution" ' + (kY1Offset - 1));
+ ytics.push('"top ' + kStackFrames + ' js stack frames"' + ' ' +
+ (kY1Offset - 2));
+ ytics.push('"pause times" 0');
+ ytics.push('"max deopt size: ' + (max_deopt_size / 1024).toFixed(1) +
+ ' kB" ' + kDeoptRow);
+ output("set ytics out nomirror (" + ytics.join(', ') + ")");
+
+ // Plot timeline.
+ for (var name in TimerEvents) {
+ var event = TimerEvents[name];
+ var ranges = MergeRanges(event.ranges);
+ for (var i = 0; i < ranges.length; i++) {
+ DrawBar(event.index, event.color,
+ ranges[i].start, ranges[i].end,
+ kTimerEventWidth);
+ }
+ }
+
+ // Plot code kind gathered from ticks.
+ for (var name in CodeKinds) {
+ var code_kind = CodeKinds[name];
+ var offset = kY1Offset - 1;
+ // Top most frame.
+ var row = MergeRanges(TicksToRanges(code_kind.in_execution));
+ for (var j = 0; j < row.length; j++) {
+ DrawBar(offset, code_kind.color,
+ row[j].start, row[j].end, kExecutionFrameWidth);
+ }
+ offset = offset - 2 * kExecutionFrameWidth - kGapWidth;
+ // Javascript frames.
+ for (var i = 0; i < kStackFrames; i++) {
+ offset = offset - 2 * kStackFrameWidth - kGapWidth;
+ row = MergeRanges(TicksToRanges(code_kind.stack_frames[i]));
+ for (var j = 0; j < row.length; j++) {
+ DrawBar(offset, code_kind.color,
+ row[j].start, row[j].end, kStackFrameWidth);
+ }
+ }
+ }
+
+ // Add labels as legend for code kind colors.
+ var padding = kCodeKindLabelPadding * (range_end - range_start) / kResX;
+ var label_x = range_start;
+ var label_y = kY1Offset;
+ for (var name in CodeKinds) {
+ label_x += padding;
+ output("set label \"" + name + "\" at " + label_x + "," + label_y +
+ " textcolor rgb \"" + CodeKinds[name].color + "\"" +
+ " font \"Helvetica,9'\"");
+ obj_index++;
+ }
+
+ if (execution_pauses.length == 0) {
+ // Force plot and return without plotting execution pause impulses.
+ output("plot 1/0");
+ return;
+ }
+
+ // Label the longest pauses.
+ execution_pauses.sort(
+ function(a, b) { return b.duration() - a.duration(); });
+
+ var max_pause_time = execution_pauses[0].duration();
+ padding = kPauseLabelPadding * (range_end - range_start) / kResX;
+ var y_scale = kY1Offset / max_pause_time / 2;
+ for (var i = 0; i < execution_pauses.length && i < kNumPauseLabels; i++) {
+ var pause = execution_pauses[i];
+ var label_content = (pause.duration() | 0) + " ms";
+ var label_x = pause.end + padding;
+ var label_y = Math.max(1, (pause.duration() * y_scale));
+ output("set label \"" + label_content + "\" at " +
+ label_x + "," + label_y + " font \"Helvetica,7'\"");
+ obj_index++;
+ }
+
+ // Scale second Y-axis appropriately.
+ var y2range = max_pause_time * num_timer_event / kY1Offset * 2;
+ output("set y2range [0:" + y2range + "]");
+ // Plot graph with impulses as data set.
+ output("plot '-' using 1:2 axes x1y2 with impulses ls 1");
+ for (var i = 0; i < execution_pauses.length; i++) {
+ var pause = execution_pauses[i];
+ output(pause.end + " " + pause.duration());
+ obj_index++;
+ }
+ output("e");
+ return obj_index;
+ };
+}
diff --git a/chromium/v8/tools/profviz/gnuplot-4.6.3-emscripten.js b/chromium/v8/tools/profviz/gnuplot-4.6.3-emscripten.js
new file mode 100644
index 00000000000..42a21c8919c
--- /dev/null
+++ b/chromium/v8/tools/profviz/gnuplot-4.6.3-emscripten.js
@@ -0,0 +1,4658 @@
+// Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
+//
+// Permission to use, copy, and distribute this software and its
+// documentation for any purpose with or without fee is hereby granted,
+// provided that the above copyright notice appear in all copies and
+// that both that copyright notice and this permission notice appear
+// in supporting documentation.
+//
+// Permission to modify the software is granted, but not the right to
+// distribute the complete modified source code. Modifications are to
+// be distributed as patches to the released version. Permission to
+// distribute binaries produced by compiling modified sources is granted,
+// provided you
+// 1. distribute the corresponding source modifications from the
+// released version in the form of a patch file along with the binaries,
+// 2. add special version identification to distinguish your version
+// in addition to the base release version number,
+// 3. provide your name and address as the primary contact for the
+// support of your modified version, and
+// 4. retain our contact information in regard to use of the base
+// software.
+// Permission to distribute the released version of the source code along
+// with corresponding source modifications in the form of a patch file is
+// granted with same provisions 2 through 4 for binary distributions.
+//
+// This software is provided "as is" without express or implied warranty
+// to the extent permitted by applicable law.
+
+// This Javascript port of gnuplot 4.6.3 [1] has been generated using
+// Emscripten [2]. This port has been made possible by Christian Huettig [3],
+// whose changes to gnuplot's source files can be found below [4] in
+// accordance to gnuplot's copyright.
+//
+// [1] http://www.gnuplot.info/
+// [2] https://github.com/kripken/emscripten/
+// [3] https://github.com/chhu/gnuplot-JS
+// [4] Patch to gnuplot 4.6.3
+// --- gnuplot-4.6.3/src/axis.c 2012-11-08 18:13:08.000000000 +0100
+// +++ gnuplot-4.6.3_mod/src/axis.c 2013-05-11 11:08:08.791587160 +0200
+// @@ -467,8 +467,9 @@ copy_or_invent_formatstring(AXIS_INDEX a
+// int precision = (ceil(-log10(fabs(axmax-axmin))));
+// if ((axmin*axmax > 0) && precision > 4)
+// sprintf(ticfmt[axis],"%%.%df", (precision>14) ? 14 : precision);
+// + else
+// + strcpy(ticfmt[axis], "%g");
+// }
+// -
+// return ticfmt[axis];
+// }
+
+var Module = {
+ 'noInitialRun': true,
+ print: function(text) {
+ self.postMessage({'transaction': -1, 'content': text});
+ },
+ printErr: function(text) {
+ self.postMessage({'transaction': -2, 'content': text});
+ },
+};
+function gnuplot_create() {
+// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code
+// Note: Some Emscripten settings may limit the speed of the generated code.
+try {
+ this['Module'] = Module;
+ Module.test;
+} catch(e) {
+ this['Module'] = Module = {};
+}
+// The environment setup code below is customized to use Module.
+// *** Environment setup code ***
+var ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof require === 'function';
+var ENVIRONMENT_IS_WEB = typeof window === 'object';
+var ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
+var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
+if (typeof module === "object") {
+ module.exports = Module;
+}
+if (ENVIRONMENT_IS_NODE) {
+ // Expose functionality in the same simple way that the shells work
+ // Note that we pollute the global namespace here, otherwise we break in node
+ Module['print'] = function(x) {
+ process['stdout'].write(x + '\n');
+ };
+ Module['printErr'] = function(x) {
+ process['stderr'].write(x + '\n');
+ };
+ var nodeFS = require('fs');
+ var nodePath = require('path');
+ Module['read'] = function(filename, binary) {
+ filename = nodePath['normalize'](filename);
+ var ret = nodeFS['readFileSync'](filename);
+ // The path is absolute if the normalized version is the same as the resolved.
+ if (!ret && filename != nodePath['resolve'](filename)) {
+ filename = path.join(__dirname, '..', 'src', filename);
+ ret = nodeFS['readFileSync'](filename);
+ }
+ if (ret && !binary) ret = ret.toString();
+ return ret;
+ };
+ Module['readBinary'] = function(filename) { return Module['read'](filename, true) };
+ Module['load'] = function(f) {
+ globalEval(read(f));
+ };
+ if (!Module['arguments']) {
+ Module['arguments'] = process['argv'].slice(2);
+ }
+}
+if (ENVIRONMENT_IS_SHELL) {
+ Module['print'] = print;
+ if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm
+ Module['read'] = read;
+ Module['readBinary'] = function(f) {
+ return read(f, 'binary');
+ };
+ if (!Module['arguments']) {
+ if (typeof scriptArgs != 'undefined') {
+ Module['arguments'] = scriptArgs;
+ } else if (typeof arguments != 'undefined') {
+ Module['arguments'] = arguments;
+ }
+ }
+}
+if (ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER) {
+ if (!Module['print']) {
+ Module['print'] = function(x) {
+ console.log(x);
+ };
+ }
+ if (!Module['printErr']) {
+ Module['printErr'] = function(x) {
+ console.log(x);
+ };
+ }
+}
+if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
+ Module['read'] = function(url) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ xhr.send(null);
+ return xhr.responseText;
+ };
+ if (!Module['arguments']) {
+ if (typeof arguments != 'undefined') {
+ Module['arguments'] = arguments;
+ }
+ }
+}
+if (ENVIRONMENT_IS_WORKER) {
+ // We can do very little here...
+ var TRY_USE_DUMP = false;
+ if (!Module['print']) {
+ Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) {
+ dump(x);
+ }) : (function(x) {
+ // self.postMessage(x); // enable this if you want stdout to be sent as messages
+ }));
+ }
+ Module['load'] = importScripts;
+}
+if (!ENVIRONMENT_IS_WORKER && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_SHELL) {
+ // Unreachable because SHELL is dependant on the others
+ throw 'Unknown runtime environment. Where are we?';
+}
+function globalEval(x) {
+ eval.call(null, x);
+}
+if (!Module['load'] == 'undefined' && Module['read']) {
+ Module['load'] = function(f) {
+ globalEval(Module['read'](f));
+ };
+}
+if (!Module['print']) {
+ Module['print'] = function(){};
+}
+if (!Module['printErr']) {
+ Module['printErr'] = Module['print'];
+}
+if (!Module['arguments']) {
+ Module['arguments'] = [];
+}
+// *** Environment setup code ***
+// Closure helpers
+Module.print = Module['print'];
+Module.printErr = Module['printErr'];
+// Callbacks
+if (!Module['preRun']) Module['preRun'] = [];
+if (!Module['postRun']) Module['postRun'] = [];
+// === Auto-generated preamble library stuff ===
+//========================================
+// Runtime code shared with compiler
+//========================================
+var Runtime = {
+ stackSave: function () {
+ return STACKTOP;
+ },
+ stackRestore: function (stackTop) {
+ STACKTOP = stackTop;
+ },
+ forceAlign: function (target, quantum) {
+ quantum = quantum || 4;
+ if (quantum == 1) return target;
+ if (isNumber(target) && isNumber(quantum)) {
+ return Math.ceil(target/quantum)*quantum;
+ } else if (isNumber(quantum) && isPowerOfTwo(quantum)) {
+ var logg = log2(quantum);
+ return '((((' +target + ')+' + (quantum-1) + ')>>' + logg + ')<<' + logg + ')';
+ }
+ return 'Math.ceil((' + target + ')/' + quantum + ')*' + quantum;
+ },
+ isNumberType: function (type) {
+ return type in Runtime.INT_TYPES || type in Runtime.FLOAT_TYPES;
+ },
+ isPointerType: function isPointerType(type) {
+ return type[type.length-1] == '*';
+},
+ isStructType: function isStructType(type) {
+ if (isPointerType(type)) return false;
+ if (isArrayType(type)) return true;
+ if (/<?{ ?[^}]* ?}>?/.test(type)) return true; // { i32, i8 } etc. - anonymous struct types
+ // See comment in isStructPointerType()
+ return type[0] == '%';
+},
+ INT_TYPES: {"i1":0,"i8":0,"i16":0,"i32":0,"i64":0},
+ FLOAT_TYPES: {"float":0,"double":0},
+ or64: function (x, y) {
+ var l = (x | 0) | (y | 0);
+ var h = (Math.round(x / 4294967296) | Math.round(y / 4294967296)) * 4294967296;
+ return l + h;
+ },
+ and64: function (x, y) {
+ var l = (x | 0) & (y | 0);
+ var h = (Math.round(x / 4294967296) & Math.round(y / 4294967296)) * 4294967296;
+ return l + h;
+ },
+ xor64: function (x, y) {
+ var l = (x | 0) ^ (y | 0);
+ var h = (Math.round(x / 4294967296) ^ Math.round(y / 4294967296)) * 4294967296;
+ return l + h;
+ },
+ getNativeTypeSize: function (type, quantumSize) {
+ if (Runtime.QUANTUM_SIZE == 1) return 1;
+ var size = {
+ '%i1': 1,
+ '%i8': 1,
+ '%i16': 2,
+ '%i32': 4,
+ '%i64': 8,
+ "%float": 4,
+ "%double": 8
+ }['%'+type]; // add '%' since float and double confuse Closure compiler as keys, and also spidermonkey as a compiler will remove 's from '_i8' etc
+ if (!size) {
+ if (type.charAt(type.length-1) == '*') {
+ size = Runtime.QUANTUM_SIZE; // A pointer
+ } else if (type[0] == 'i') {
+ var bits = parseInt(type.substr(1));
+ assert(bits % 8 == 0);
+ size = bits/8;
+ }
+ }
+ return size;
+ },
+ getNativeFieldSize: function (type) {
+ return Math.max(Runtime.getNativeTypeSize(type), Runtime.QUANTUM_SIZE);
+ },
+ dedup: function dedup(items, ident) {
+ var seen = {};
+ if (ident) {
+ return items.filter(function(item) {
+ if (seen[item[ident]]) return false;
+ seen[item[ident]] = true;
+ return true;
+ });
+ } else {
+ return items.filter(function(item) {
+ if (seen[item]) return false;
+ seen[item] = true;
+ return true;
+ });
+ }
+},
+ set: function set() {
+ var args = typeof arguments[0] === 'object' ? arguments[0] : arguments;
+ var ret = {};
+ for (var i = 0; i < args.length; i++) {
+ ret[args[i]] = 0;
+ }
+ return ret;
+},
+ STACK_ALIGN: 8,
+ getAlignSize: function (type, size, vararg) {
+ // we align i64s and doubles on 64-bit boundaries, unlike x86
+ if (type == 'i64' || type == 'double' || vararg) return 8;
+ if (!type) return Math.min(size, 8); // align structures internally to 64 bits
+ return Math.min(size || (type ? Runtime.getNativeFieldSize(type) : 0), Runtime.QUANTUM_SIZE);
+ },
+ calculateStructAlignment: function calculateStructAlignment(type) {
+ type.flatSize = 0;
+ type.alignSize = 0;
+ var diffs = [];
+ var prev = -1;
+ type.flatIndexes = type.fields.map(function(field) {
+ var size, alignSize;
+ if (Runtime.isNumberType(field) || Runtime.isPointerType(field)) {
+ size = Runtime.getNativeTypeSize(field); // pack char; char; in structs, also char[X]s.
+ alignSize = Runtime.getAlignSize(field, size);
+ } else if (Runtime.isStructType(field)) {
+ size = Types.types[field].flatSize;
+ alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize);
+ } else if (field[0] == 'b') {
+ // bN, large number field, like a [N x i8]
+ size = field.substr(1)|0;
+ alignSize = 1;
+ } else {
+ throw 'Unclear type in struct: ' + field + ', in ' + type.name_ + ' :: ' + dump(Types.types[type.name_]);
+ }
+ if (type.packed) alignSize = 1;
+ type.alignSize = Math.max(type.alignSize, alignSize);
+ var curr = Runtime.alignMemory(type.flatSize, alignSize); // if necessary, place this on aligned memory
+ type.flatSize = curr + size;
+ if (prev >= 0) {
+ diffs.push(curr-prev);
+ }
+ prev = curr;
+ return curr;
+ });
+ type.flatSize = Runtime.alignMemory(type.flatSize, type.alignSize);
+ if (diffs.length == 0) {
+ type.flatFactor = type.flatSize;
+ } else if (Runtime.dedup(diffs).length == 1) {
+ type.flatFactor = diffs[0];
+ }
+ type.needsFlattening = (type.flatFactor != 1);
+ return type.flatIndexes;
+ },
+ generateStructInfo: function (struct, typeName, offset) {
+ var type, alignment;
+ if (typeName) {
+ offset = offset || 0;
+ type = (typeof Types === 'undefined' ? Runtime.typeInfo : Types.types)[typeName];
+ if (!type) return null;
+ if (type.fields.length != struct.length) {
+ printErr('Number of named fields must match the type for ' + typeName + ': possibly duplicate struct names. Cannot return structInfo');
+ return null;
+ }
+ alignment = type.flatIndexes;
+ } else {
+ var type = { fields: struct.map(function(item) { return item[0] }) };
+ alignment = Runtime.calculateStructAlignment(type);
+ }
+ var ret = {
+ __size__: type.flatSize
+ };
+ if (typeName) {
+ struct.forEach(function(item, i) {
+ if (typeof item === 'string') {
+ ret[item] = alignment[i] + offset;
+ } else {
+ // embedded struct
+ var key;
+ for (var k in item) key = k;
+ ret[key] = Runtime.generateStructInfo(item[key], type.fields[i], alignment[i]);
+ }
+ });
+ } else {
+ struct.forEach(function(item, i) {
+ ret[item[1]] = alignment[i];
+ });
+ }
+ return ret;
+ },
+ dynCall: function (sig, ptr, args) {
+ if (args && args.length) {
+ if (!args.splice) args = Array.prototype.slice.call(args);
+ args.splice(0, 0, ptr);
+ return Module['dynCall_' + sig].apply(null, args);
+ } else {
+ return Module['dynCall_' + sig].call(null, ptr);
+ }
+ },
+ functionPointers: [],
+ addFunction: function (func) {
+ for (var i = 0; i < Runtime.functionPointers.length; i++) {
+ if (!Runtime.functionPointers[i]) {
+ Runtime.functionPointers[i] = func;
+ return 2 + 2*i;
+ }
+ }
+ throw 'Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.';
+ },
+ removeFunction: function (index) {
+ Runtime.functionPointers[(index-2)/2] = null;
+ },
+ warnOnce: function (text) {
+ if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {};
+ if (!Runtime.warnOnce.shown[text]) {
+ Runtime.warnOnce.shown[text] = 1;
+ Module.printErr(text);
+ }
+ },
+ funcWrappers: {},
+ getFuncWrapper: function (func, sig) {
+ assert(sig);
+ if (!Runtime.funcWrappers[func]) {
+ Runtime.funcWrappers[func] = function() {
+ return Runtime.dynCall(sig, func, arguments);
+ };
+ }
+ return Runtime.funcWrappers[func];
+ },
+ UTF8Processor: function () {
+ var buffer = [];
+ var needed = 0;
+ this.processCChar = function (code) {
+ code = code & 0xff;
+ if (needed) {
+ buffer.push(code);
+ needed--;
+ }
+ if (buffer.length == 0) {
+ if (code < 128) return String.fromCharCode(code);
+ buffer.push(code);
+ if (code > 191 && code < 224) {
+ needed = 1;
+ } else {
+ needed = 2;
+ }
+ return '';
+ }
+ if (needed > 0) return '';
+ var c1 = buffer[0];
+ var c2 = buffer[1];
+ var c3 = buffer[2];
+ var ret;
+ if (c1 > 191 && c1 < 224) {
+ ret = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
+ } else {
+ ret = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ }
+ buffer.length = 0;
+ return ret;
+ }
+ this.processJSString = function(string) {
+ string = unescape(encodeURIComponent(string));
+ var ret = [];
+ for (var i = 0; i < string.length; i++) {
+ ret.push(string.charCodeAt(i));
+ }
+ return ret;
+ }
+ },
+ stackAlloc: function (size) { var ret = STACKTOP;STACKTOP = (STACKTOP + size)|0;STACKTOP = ((((STACKTOP)+7)>>3)<<3); return ret; },
+ staticAlloc: function (size) { var ret = STATICTOP;STATICTOP = (STATICTOP + size)|0;STATICTOP = ((((STATICTOP)+7)>>3)<<3); return ret; },
+ dynamicAlloc: function (size) { var ret = DYNAMICTOP;DYNAMICTOP = (DYNAMICTOP + size)|0;DYNAMICTOP = ((((DYNAMICTOP)+7)>>3)<<3); if (DYNAMICTOP >= TOTAL_MEMORY) enlargeMemory();; return ret; },
+ alignMemory: function (size,quantum) { var ret = size = Math.ceil((size)/(quantum ? quantum : 8))*(quantum ? quantum : 8); return ret; },
+ makeBigInt: function (low,high,unsigned) { var ret = (unsigned ? ((+(((low)>>>(0))))+((+(((high)>>>(0))))*(+(4294967296)))) : ((+(((low)>>>(0))))+((+(((high)|(0))))*(+(4294967296))))); return ret; },
+ GLOBAL_BASE: 8,
+ QUANTUM_SIZE: 4,
+ __dummy__: 0
+}
+//========================================
+// Runtime essentials
+//========================================
+var __THREW__ = 0; // Used in checking for thrown exceptions.
+var ABORT = false; // whether we are quitting the application. no code should run after this. set in exit() and abort()
+var undef = 0;
+// tempInt is used for 32-bit signed values or smaller. tempBigInt is used
+// for 32-bit unsigned values or more than 32 bits. TODO: audit all uses of tempInt
+var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD;
+var tempI64, tempI64b;
+var tempRet0, tempRet1, tempRet2, tempRet3, tempRet4, tempRet5, tempRet6, tempRet7, tempRet8, tempRet9;
+function abort(text) {
+ Module.print(text + ':\n' + (new Error).stack);
+ ABORT = true;
+ throw "Assertion: " + text;
+}
+function assert(condition, text) {
+ if (!condition) {
+ abort('Assertion failed: ' + text);
+ }
+}
+var globalScope = this;
+// C calling interface. A convenient way to call C functions (in C files, or
+// defined with extern "C").
+//
+// Note: LLVM optimizations can inline and remove functions, after which you will not be
+// able to call them. Closure can also do so. To avoid that, add your function to
+// the exports using something like
+//
+// -s EXPORTED_FUNCTIONS='["_main", "_myfunc"]'
+//
+// @param ident The name of the C function (note that C++ functions will be name-mangled - use extern "C")
+// @param returnType The return type of the function, one of the JS types 'number', 'string' or 'array' (use 'number' for any C pointer, and
+// 'array' for JavaScript arrays and typed arrays).
+// @param argTypes An array of the types of arguments for the function (if there are no arguments, this can be ommitted). Types are as in returnType,
+// except that 'array' is not possible (there is no way for us to know the length of the array)
+// @param args An array of the arguments to the function, as native JS values (as in returnType)
+// Note that string arguments will be stored on the stack (the JS string will become a C string on the stack).
+// @return The return value, as a native JS value (as in returnType)
+function ccall(ident, returnType, argTypes, args) {
+ return ccallFunc(getCFunc(ident), returnType, argTypes, args);
+}
+Module["ccall"] = ccall;
+// Returns the C function with a specified identifier (for C++, you need to do manual name mangling)
+function getCFunc(ident) {
+ try {
+ var func = globalScope['Module']['_' + ident]; // closure exported function
+ if (!func) func = eval('_' + ident); // explicit lookup
+ } catch(e) {
+ }
+ assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)');
+ return func;
+}
+// Internal function that does a C call using a function, not an identifier
+function ccallFunc(func, returnType, argTypes, args) {
+ var stack = 0;
+ function toC(value, type) {
+ if (type == 'string') {
+ if (value === null || value === undefined || value === 0) return 0; // null string
+ if (!stack) stack = Runtime.stackSave();
+ var ret = Runtime.stackAlloc(value.length+1);
+ writeStringToMemory(value, ret);
+ return ret;
+ } else if (type == 'array') {
+ if (!stack) stack = Runtime.stackSave();
+ var ret = Runtime.stackAlloc(value.length);
+ writeArrayToMemory(value, ret);
+ return ret;
+ }
+ return value;
+ }
+ function fromC(value, type) {
+ if (type == 'string') {
+ return Pointer_stringify(value);
+ }
+ assert(type != 'array');
+ return value;
+ }
+ var i = 0;
+ var cArgs = args ? args.map(function(arg) {
+ return toC(arg, argTypes[i++]);
+ }) : [];
+ var ret = fromC(func.apply(null, cArgs), returnType);
+ if (stack) Runtime.stackRestore(stack);
+ return ret;
+}
+// Returns a native JS wrapper for a C function. This is similar to ccall, but
+// returns a function you can call repeatedly in a normal way. For example:
+//
+// var my_function = cwrap('my_c_function', 'number', ['number', 'number']);
+// alert(my_function(5, 22));
+// alert(my_function(99, 12));
+//
+function cwrap(ident, returnType, argTypes) {
+ var func = getCFunc(ident);
+ return function() {
+ return ccallFunc(func, returnType, argTypes, Array.prototype.slice.call(arguments));
+ }
+}
+Module["cwrap"] = cwrap;
+// Sets a value in memory in a dynamic way at run-time. Uses the
+// type data. This is the same as makeSetValue, except that
+// makeSetValue is done at compile-time and generates the needed
+// code then, whereas this function picks the right code at
+// run-time.
+// Note that setValue and getValue only do *aligned* writes and reads!
+// Note that ccall uses JS types as for defining types, while setValue and
+// getValue need LLVM types ('i8', 'i32') - this is a lower-level operation
+function setValue(ptr, value, type, noSafe) {
+ type = type || 'i8';
+ if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit
+ switch(type) {
+ case 'i1': HEAP8[(ptr)]=value; break;
+ case 'i8': HEAP8[(ptr)]=value; break;
+ case 'i16': HEAP16[((ptr)>>1)]=value; break;
+ case 'i32': HEAP32[((ptr)>>2)]=value; break;
+ case 'i64': (tempI64 = [value>>>0,Math.min(Math.floor((value)/(+(4294967296))), (+(4294967295)))>>>0],HEAP32[((ptr)>>2)]=tempI64[0],HEAP32[(((ptr)+(4))>>2)]=tempI64[1]); break;
+ case 'float': HEAPF32[((ptr)>>2)]=value; break;
+ case 'double': HEAPF64[((ptr)>>3)]=value; break;
+ default: abort('invalid type for setValue: ' + type);
+ }
+}
+Module['setValue'] = setValue;
+// Parallel to setValue.
+function getValue(ptr, type, noSafe) {
+ type = type || 'i8';
+ if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit
+ switch(type) {
+ case 'i1': return HEAP8[(ptr)];
+ case 'i8': return HEAP8[(ptr)];
+ case 'i16': return HEAP16[((ptr)>>1)];
+ case 'i32': return HEAP32[((ptr)>>2)];
+ case 'i64': return HEAP32[((ptr)>>2)];
+ case 'float': return HEAPF32[((ptr)>>2)];
+ case 'double': return HEAPF64[((ptr)>>3)];
+ default: abort('invalid type for setValue: ' + type);
+ }
+ return null;
+}
+Module['getValue'] = getValue;
+var ALLOC_NORMAL = 0; // Tries to use _malloc()
+var ALLOC_STACK = 1; // Lives for the duration of the current function call
+var ALLOC_STATIC = 2; // Cannot be freed
+var ALLOC_DYNAMIC = 3; // Cannot be freed except through sbrk
+var ALLOC_NONE = 4; // Do not allocate
+Module['ALLOC_NORMAL'] = ALLOC_NORMAL;
+Module['ALLOC_STACK'] = ALLOC_STACK;
+Module['ALLOC_STATIC'] = ALLOC_STATIC;
+Module['ALLOC_DYNAMIC'] = ALLOC_DYNAMIC;
+Module['ALLOC_NONE'] = ALLOC_NONE;
+// allocate(): This is for internal use. You can use it yourself as well, but the interface
+// is a little tricky (see docs right below). The reason is that it is optimized
+// for multiple syntaxes to save space in generated code. So you should
+// normally not use allocate(), and instead allocate memory using _malloc(),
+// initialize it with setValue(), and so forth.
+// @slab: An array of data, or a number. If a number, then the size of the block to allocate,
+// in *bytes* (note that this is sometimes confusing: the next parameter does not
+// affect this!)
+// @types: Either an array of types, one for each byte (or 0 if no type at that position),
+// or a single type which is used for the entire block. This only matters if there
+// is initial data - if @slab is a number, then this does not matter at all and is
+// ignored.
+// @allocator: How to allocate memory, see ALLOC_*
+function allocate(slab, types, allocator, ptr) {
+ var zeroinit, size;
+ if (typeof slab === 'number') {
+ zeroinit = true;
+ size = slab;
+ } else {
+ zeroinit = false;
+ size = slab.length;
+ }
+ var singleType = typeof types === 'string' ? types : null;
+ var ret;
+ if (allocator == ALLOC_NONE) {
+ ret = ptr;
+ } else {
+ ret = [_malloc, Runtime.stackAlloc, Runtime.staticAlloc, Runtime.dynamicAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length));
+ }
+ if (zeroinit) {
+ var ptr = ret, stop;
+ assert((ret & 3) == 0);
+ stop = ret + (size & ~3);
+ for (; ptr < stop; ptr += 4) {
+ HEAP32[((ptr)>>2)]=0;
+ }
+ stop = ret + size;
+ while (ptr < stop) {
+ HEAP8[((ptr++)|0)]=0;
+ }
+ return ret;
+ }
+ if (singleType === 'i8') {
+ if (slab.subarray || slab.slice) {
+ HEAPU8.set(slab, ret);
+ } else {
+ HEAPU8.set(new Uint8Array(slab), ret);
+ }
+ return ret;
+ }
+ var i = 0, type, typeSize, previousType;
+ while (i < size) {
+ var curr = slab[i];
+ if (typeof curr === 'function') {
+ curr = Runtime.getFunctionIndex(curr);
+ }
+ type = singleType || types[i];
+ if (type === 0) {
+ i++;
+ continue;
+ }
+ if (type == 'i64') type = 'i32'; // special case: we have one i32 here, and one i32 later
+ setValue(ret+i, curr, type);
+ // no need to look up size unless type changes, so cache it
+ if (previousType !== type) {
+ typeSize = Runtime.getNativeTypeSize(type);
+ previousType = type;
+ }
+ i += typeSize;
+ }
+ return ret;
+}
+Module['allocate'] = allocate;
+function Pointer_stringify(ptr, /* optional */ length) {
+ // Find the length, and check for UTF while doing so
+ var hasUtf = false;
+ var t;
+ var i = 0;
+ while (1) {
+ t = HEAPU8[(((ptr)+(i))|0)];
+ if (t >= 128) hasUtf = true;
+ else if (t == 0 && !length) break;
+ i++;
+ if (length && i == length) break;
+ }
+ if (!length) length = i;
+ var ret = '';
+ if (!hasUtf) {
+ var MAX_CHUNK = 1024; // split up into chunks, because .apply on a huge string can overflow the stack
+ var curr;
+ while (length > 0) {
+ curr = String.fromCharCode.apply(String, HEAPU8.subarray(ptr, ptr + Math.min(length, MAX_CHUNK)));
+ ret = ret ? ret + curr : curr;
+ ptr += MAX_CHUNK;
+ length -= MAX_CHUNK;
+ }
+ return ret;
+ }
+ var utf8 = new Runtime.UTF8Processor();
+ for (i = 0; i < length; i++) {
+ t = HEAPU8[(((ptr)+(i))|0)];
+ ret += utf8.processCChar(t);
+ }
+ return ret;
+}
+Module['Pointer_stringify'] = Pointer_stringify;
+// Memory management
+var PAGE_SIZE = 4096;
+function alignMemoryPage(x) {
+ return ((x+4095)>>12)<<12;
+}
+var HEAP;
+var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;
+var STATIC_BASE = 0, STATICTOP = 0, staticSealed = false; // static area
+var STACK_BASE = 0, STACKTOP = 0, STACK_MAX = 0; // stack area
+var DYNAMIC_BASE = 0, DYNAMICTOP = 0; // dynamic area handled by sbrk
+function enlargeMemory() {
+ abort('Cannot enlarge memory arrays in asm.js. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, or (2) set Module.TOTAL_MEMORY before the program runs.');
+}
+var TOTAL_STACK = Module['TOTAL_STACK'] || 5242880;
+var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || 16777216;
+var FAST_MEMORY = Module['FAST_MEMORY'] || 2097152;
+// Initialize the runtime's memory
+// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
+assert(!!Int32Array && !!Float64Array && !!(new Int32Array(1)['subarray']) && !!(new Int32Array(1)['set']),
+ 'Cannot fallback to non-typed array case: Code is too specialized');
+var buffer = new ArrayBuffer(TOTAL_MEMORY);
+HEAP8 = new Int8Array(buffer);
+HEAP16 = new Int16Array(buffer);
+HEAP32 = new Int32Array(buffer);
+HEAPU8 = new Uint8Array(buffer);
+HEAPU16 = new Uint16Array(buffer);
+HEAPU32 = new Uint32Array(buffer);
+HEAPF32 = new Float32Array(buffer);
+HEAPF64 = new Float64Array(buffer);
+// Endianness check (note: assumes compiler arch was little-endian)
+HEAP32[0] = 255;
+assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system');
+Module['HEAP'] = HEAP;
+Module['HEAP8'] = HEAP8;
+Module['HEAP16'] = HEAP16;
+Module['HEAP32'] = HEAP32;
+Module['HEAPU8'] = HEAPU8;
+Module['HEAPU16'] = HEAPU16;
+Module['HEAPU32'] = HEAPU32;
+Module['HEAPF32'] = HEAPF32;
+Module['HEAPF64'] = HEAPF64;
+function callRuntimeCallbacks(callbacks) {
+ while(callbacks.length > 0) {
+ var callback = callbacks.shift();
+ if (typeof callback == 'function') {
+ callback();
+ continue;
+ }
+ var func = callback.func;
+ if (typeof func === 'number') {
+ if (callback.arg === undefined) {
+ Runtime.dynCall('v', func);
+ } else {
+ Runtime.dynCall('vi', func, [callback.arg]);
+ }
+ } else {
+ func(callback.arg === undefined ? null : callback.arg);
+ }
+ }
+}
+var __ATINIT__ = []; // functions called during startup
+var __ATMAIN__ = []; // functions called when main() is to be run
+var __ATEXIT__ = []; // functions called during shutdown
+var runtimeInitialized = false;
+function ensureInitRuntime() {
+ if (runtimeInitialized) return;
+ runtimeInitialized = true;
+ callRuntimeCallbacks(__ATINIT__);
+}
+function preMain() {
+ callRuntimeCallbacks(__ATMAIN__);
+}
+function exitRuntime() {
+ callRuntimeCallbacks(__ATEXIT__);
+}
+// Tools
+// This processes a JS string into a C-line array of numbers, 0-terminated.
+// For LLVM-originating strings, see parser.js:parseLLVMString function
+function intArrayFromString(stringy, dontAddNull, length /* optional */) {
+ var ret = (new Runtime.UTF8Processor()).processJSString(stringy);
+ if (length) {
+ ret.length = length;
+ }
+ if (!dontAddNull) {
+ ret.push(0);
+ }
+ return ret;
+}
+Module['intArrayFromString'] = intArrayFromString;
+function intArrayToString(array) {
+ var ret = [];
+ for (var i = 0; i < array.length; i++) {
+ var chr = array[i];
+ if (chr > 0xFF) {
+ chr &= 0xFF;
+ }
+ ret.push(String.fromCharCode(chr));
+ }
+ return ret.join('');
+}
+Module['intArrayToString'] = intArrayToString;
+// Write a Javascript array to somewhere in the heap
+function writeStringToMemory(string, buffer, dontAddNull) {
+ var array = intArrayFromString(string, dontAddNull);
+ var i = 0;
+ while (i < array.length) {
+ var chr = array[i];
+ HEAP8[(((buffer)+(i))|0)]=chr
+ i = i + 1;
+ }
+}
+Module['writeStringToMemory'] = writeStringToMemory;
+function writeArrayToMemory(array, buffer) {
+ for (var i = 0; i < array.length; i++) {
+ HEAP8[(((buffer)+(i))|0)]=array[i];
+ }
+}
+Module['writeArrayToMemory'] = writeArrayToMemory;
+function unSign(value, bits, ignore, sig) {
+ if (value >= 0) {
+ return value;
+ }
+ return bits <= 32 ? 2*Math.abs(1 << (bits-1)) + value // Need some trickery, since if bits == 32, we are right at the limit of the bits JS uses in bitshifts
+ : Math.pow(2, bits) + value;
+}
+function reSign(value, bits, ignore, sig) {
+ if (value <= 0) {
+ return value;
+ }
+ var half = bits <= 32 ? Math.abs(1 << (bits-1)) // abs is needed if bits == 32
+ : Math.pow(2, bits-1);
+ if (value >= half && (bits <= 32 || value > half)) { // for huge values, we can hit the precision limit and always get true here. so don't do that
+ // but, in general there is no perfect solution here. With 64-bit ints, we get rounding and errors
+ // TODO: In i64 mode 1, resign the two parts separately and safely
+ value = -2*half + value; // Cannot bitshift half, as it may be at the limit of the bits JS uses in bitshifts
+ }
+ return value;
+}
+if (!Math['imul']) Math['imul'] = function(a, b) {
+ var ah = a >>> 16;
+ var al = a & 0xffff;
+ var bh = b >>> 16;
+ var bl = b & 0xffff;
+ return (al*bl + ((ah*bl + al*bh) << 16))|0;
+};
+// A counter of dependencies for calling run(). If we need to
+// do asynchronous work before running, increment this and
+// decrement it. Incrementing must happen in a place like
+// PRE_RUN_ADDITIONS (used by emcc to add file preloading).
+// Note that you can add dependencies in preRun, even though
+// it happens right before run - run will be postponed until
+// the dependencies are met.
+var runDependencies = 0;
+var runDependencyTracking = {};
+var calledInit = false, calledRun = false;
+var runDependencyWatcher = null;
+function addRunDependency(id) {
+ runDependencies++;
+ if (Module['monitorRunDependencies']) {
+ Module['monitorRunDependencies'](runDependencies);
+ }
+ if (id) {
+ assert(!runDependencyTracking[id]);
+ runDependencyTracking[id] = 1;
+ } else {
+ Module.printErr('warning: run dependency added without ID');
+ }
+}
+Module['addRunDependency'] = addRunDependency;
+function removeRunDependency(id) {
+ runDependencies--;
+ if (Module['monitorRunDependencies']) {
+ Module['monitorRunDependencies'](runDependencies);
+ }
+ if (id) {
+ assert(runDependencyTracking[id]);
+ delete runDependencyTracking[id];
+ } else {
+ Module.printErr('warning: run dependency removed without ID');
+ }
+ if (runDependencies == 0) {
+ if (runDependencyWatcher !== null) {
+ clearInterval(runDependencyWatcher);
+ runDependencyWatcher = null;
+ }
+ // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)
+ if (!calledRun && shouldRunNow) run();
+ }
+}
+Module['removeRunDependency'] = removeRunDependency;
+Module["preloadedImages"] = {}; // maps url to image data
+Module["preloadedAudios"] = {}; // maps url to audio data
+function addPreRun(func) {
+ if (!Module['preRun']) Module['preRun'] = [];
+ else if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
+ Module['preRun'].push(func);
+}
+var awaitingMemoryInitializer = false;
+function loadMemoryInitializer(filename) {
+ function applyData(data) {
+ HEAPU8.set(data, STATIC_BASE);
+ runPostSets();
+ }
+ // always do this asynchronously, to keep shell and web as similar as possible
+ addPreRun(function() {
+ if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {
+ applyData(Module['readBinary'](filename));
+ } else {
+ Browser.asyncLoad(filename, function(data) {
+ applyData(data);
+ }, function(data) {
+ throw 'could not load memory initializer ' + filename;
+ });
+ }
+ });
+ awaitingMemoryInitializer = false;
+}
+// === Body ===
+STATIC_BASE = 8;
+STATICTOP = STATIC_BASE + 243440;
+var _stdout;
+var _stdin;
+var _stderr;
+var _stdout = _stdout=allocate([0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC);
+var _stdin = _stdin=allocate([0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC);
+var _stderr = _stderr=allocate([0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC);
+/* memory initializer */ allocate([0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,71,110,117,112,108,111,116,80,0,0,0,0,0,0,0,90,71,110,117,112,108,111,116,68,0,0,0,0,0,0,0,90,71,110,117,112,108,111,116,0,0,0,0,0,0,0,0,58,140,48,226,142,121,69,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,139,1,0,240,84,3,0,80,126,1,0,0,0,0,0,80,131,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,134,1,0,248,132,1,0,232,123,1,0,0,0,0,0,224,128,1,0,0,0,0,0,0,85,1,0,8,0,0,0,208,80,1,0,9,0,0,0,192,76,1,0,10,0,0,0,64,73,1,0,13,0,0,0,240,70,1,0,27,0,0,0,40,67,1,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,88,244,1,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,1,0,0,0,1,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,3,0,0,0,1,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,154,153,153,153,153,153,169,63,184,30,133,235,81,184,158,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,2,0,0,0,1,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,123,20,174,71,225,122,148,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,118,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,193,1,0,16,189,1,0,16,220,1,0,192,222,1,0,0,0,0,0,96,133,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,184,253,1,0,0,0,0,0,160,149,1,0,32,148,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,7,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,5,0,0,0,1,0,0,0,20,0,0,0,26,0,0,0,21,0,0,0,27,0,0,0,14,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,26,1,0,0,0,0,0,0,2,0,0,0,94,1,0,0,1,0,0,0,2,0,0,0,88,0,0,0,0,0,0,0,2,0,0,0,26,0,0,0,1,0,0,0,2,0,0,0,58,0,0,0,0,0,0,0,2,0,0,0,48,1,0,0,1,0,0,0,1,0,0,0,96,0,0,0,1,0,0,0,2,0,0,0,96,0,0,0,1,0,0,0,3,0,0,0,96,0,0,0,3,0,0,0,1,0,0,0,96,0,0,0,3,0,0,0,2,0,0,0,96,0,0,0,3,0,0,0,3,0,0,0,96,0,0,0,3,0,0,0,2,0,0,0,26,1,0,0,3,0,0,0,2,0,0,0,88,0,0,0,3,0,0,0,2,0,0,0,58,0,0,0,1,0,0,0,2,0,0,0,134,1,0,0,3,0,0,0,2,0,0,0,134,1,0,0,1,0,0,0,2,0,0,0,140,1,0,0,1,0,0,0,2,0,0,0,8,0,0,0,1,0,0,0,2,0,0,0,38,1,0,0,1,0,0,0,2,0,0,0,54,1,0,0,1,0,0,0,2,0,0,0,94,0,0,0,1,0,0,0,2,0,0,0,182,0,0,0,3,0,0,0,2,0,0,0,140,1,0,0,1,0,0,0,2,0,0,0,14,0,0,0,1,0,0,0,2,0,0,0,16,0,0,0,1,0,0,0,2,0,0,0,24,1,0,0,1,0,0,0,2,0,0,0,132,0,0,0,1,0,0,0,2,0,0,0,118,1,0,0,1,0,0,0,2,0,0,0,108,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,3,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,2,0,0,0,1,0,0,0,4,0,0,0,3,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,4,0,0,0,2,0,0,0,2,0,0,0,4,0,0,0,3,0,0,0,2,0,0,0,4,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,3,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,3,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,3,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,1,0,0,0,3,0,0,0,4,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,3,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,153,153,153,153,153,185,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,93,1,0,8,93,1,0,248,92,1,0,240,92,1,0,208,92,1,0,200,92,1,0,184,92,1,0,160,92,1,0,0,0,0,0,0,0,0,0,119,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,42,2,0,1,0,0,0,176,34,3,0,2,0,0,0,32,42,2,0,3,0,0,0,0,0,0,0,0,0,0,0,117,110,115,32,112,109,51,100,59,115,101,32,108,109,97,114,103,32,115,99,114,101,32,48,46,48,53,59,115,101,32,114,109,97,114,103,32,115,99,114,101,32,48,46,57,55,53,59,32,115,101,32,98,109,97,114,103,32,115,99,114,101,32,48,46,50,50,59,32,115,101,32,116,109,97,114,103,32,115,99,114,101,32,48,46,56,54,59,115,101,32,103,114,105,100,59,115,101,32,116,105,99,115,32,115,99,97,108,101,32,48,59,32,115,101,32,120,116,105,99,115,32,48,44,48,46,49,59,115,101,32,121,116,105,99,115,32,48,44,48,46,49,59,115,101,32,107,101,121,32,116,111,112,32,114,105,103,104,116,32,97,116,32,115,99,114,101,32,48,46,57,55,53,44,48,46,57,55,53,32,104,111,114,105,122,111,110,116,97,108,32,116,105,116,108,101,32,39,82,44,71,44,66,32,112,114,111,102,105,108,101,115,32,111,102,32,116,104,101,32,99,117,114,114,101,110,116,32,99,111,108,111,114,32,112,97,108,101,116,116,101,39,59,0,0,0,0,0,115,112,108,111,116,32,49,47,48,59,10,10,10,0,0,0,114,101,115,101,116,59,115,101,116,32,109,117,108,116,105,59,117,110,115,32,98,111,114,100,101,114,59,117,110,115,32,107,101,121,59,115,101,116,32,116,105,99,32,105,110,59,117,110,115,32,120,116,105,99,115,59,117,110,115,32,121,116,105,99,115,59,115,101,32,99,98,116,105,99,32,48,44,48,46,49,44,49,32,109,105,114,114,32,102,111,114,109,97,116,32,39,39,59,115,101,32,120,114,91,48,58,49,93,59,115,101,32,121,114,91,48,58,49,93,59,115,101,32,122,114,91,48,58,49,93,59,115,101,32,99,98,114,91,48,58,49,93,59,115,101,32,112,109,51,100,32,109,97,112,59,115,101,116,32,99,111,108,111,114,98,111,120,32,104,111,114,32,117,115,101,114,32,111,114,105,103,32,48,46,48,53,44,48,46,48,50,32,115,105,122,101,32,48,46,57,50,53,44,48,46,49,50,59,0,0,0,0,0,0,0,0,10,10,10,117,110,115,32,109,117,108,116,105,59,10,0,0,248,110,3,0,56,16,3,0,100,0,0,0,100,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,48,1,0,0,190,0,0,0,190,0,0,0,190,0,0,0,2,0,0,0,190,0,0,0,126,0,0,0,126,0,0,0,90,0,0,0,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,13,3,0,128,10,3,0,220,5,0,0,132,3,0,0,45,0,0,0,22,0,0,0,20,0,0,0,20,0,0,0,20,0,0,0,142,1,0,0,158,1,0,0,18,1,0,0,2,0,0,0,120,0,0,0,82,0,0,0,34,0,0,0,174,0,0,0,40,0,0,0,62,0,0,0,158,0,0,0,54,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,9,3,0,176,7,3,0,32,3,0,0,184,1,0,0,20,0,0,0,9,0,0,0,8,0,0,0,6,0,0,0,102,0,0,0,48,0,0,0,218,0,0,0,98,0,0,0,2,0,0,0,12,1,0,0,50,0,0,0,96,0,0,0,158,0,0,0,42,0,0,0,164,0,0,0,124,0,0,0,54,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,6,3,0,176,4,3,0,255,15,0,0,59,12,0,0,71,0,0,0,51,0,0,0,36,0,0,0,36,0,0,0,48,1,0,0,8,0,0,0,104,1,0,0,100,0,0,0,2,0,0,0,52,0,0,0,102,0,0,0,86,0,0,0,38,0,0,0,80,0,0,0,160,0,0,0,124,0,0,0,72,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,2,3,0,96,0,3,0,0,4,0,0,12,3,0,0,25,0,0,0,14,0,0,0,11,0,0,0,11,0,0,0,48,1,0,0,20,1,0,0,40,1,0,0,170,0,0,0,2,0,0,0,172,0,0,0,128,0,0,0,60,0,0,0,106,1,0,0,78,0,0,0,12,0,0,0,124,0,0,0,54,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,232,253,2,0,160,251,2,0,0,4,0,0,12,3,0,0,25,0,0,0,14,0,0,0,11,0,0,0,11,0,0,0,48,1,0,0,234,0,0,0,50,0,0,0,170,0,0,0,2,0,0,0,172,0,0,0,136,0,0,0,42,0,0,0,40,0,0,0,62,0,0,0,12,0,0,0,124,0,0,0,54,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,245,2,0,64,243,2,0,0,4,0,0,12,3,0,0,25,0,0,0,14,0,0,0,11,0,0,0,11,0,0,0,48,1,0,0,20,1,0,0,40,1,0,0,66,0,0,0,2,0,0,0,6,0,0,0,136,0,0,0,42,0,0,0,40,0,0,0,62,0,0,0,12,0,0,0,124,0,0,0,54,0,0,0,48,0,0,0,152,0,0,0,0,0,0,0,65,0,0,0,66,0,0,0,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,234,2,0,192,220,2,0,112,23,0,0,160,15,0,0,100,0,0,0,80,0,0,0,100,0,0,0,100,0,0,0,206,0,0,0,148,0,0,0,222,0,0,0,116,1,0,0,2,0,0,0,180,1,0,0,100,0,0,0,130,0,0,0,140,0,0,0,118,0,0,0,34,0,0,0,20,0,0,0,70,0,0,0,48,0,0,0,60,0,0,0,30,0,0,0,1,11,0,0,0,0,0,0,0,0,0,0,52,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,0,0,0,0,0,0,0,56,0,0,0,98,0,0,0,0,0,0,0,4,0,0,0,120,1,0,0,102,0,0,0,24,0,0,0,130,0,0,0,0,0,0,0,0,0,0,0,128,218,2,0,160,216,2,0,73,127,0,0,182,91,0,0,12,3,0,0,177,1,0,0,153,1,0,0,153,1,0,0,106,0,0,0,150,0,0,0,168,0,0,0,2,0,0,0,2,0,0,0,176,1,0,0,40,0,0,0,106,0,0,0,230,0,0,0,28,0,0,0,96,0,0,0,50,0,0,0,58,0,0,0,48,0,0,0,106,0,0,0,40,0,0,0,4,9,0,0,0,0,0,0,0,0,0,0,50,0,0,0,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,42,1,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,214,2,0,120,212,2,0,72,23,0,0,32,28,0,0,220,0,0,0,132,0,0,0,90,0,0,0,90,0,0,0,24,0,0,0,28,1,0,0,62,0,0,0,56,1,0,0,2,0,0,0,46,1,0,0,144,0,0,0,54,0,0,0,28,0,0,0,38,0,0,0,138,0,0,0,146,0,0,0,60,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,210,2,0,248,208,2,0,79,0,0,0,24,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,196,0,0,0,40,0,0,0,242,0,0,0,34,1,0,0,2,0,0,0,86,0,0,0,108,0,0,0,18,0,0,0,146,0,0,0,104,0,0,0,12,0,0,0,124,0,0,0,110,0,0,0,42,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,28,0,0,0,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216,207,2,0,0,206,2,0,32,28,0,0,192,18,0,0,124,0,0,0,100,0,0,0,120,0,0,0,120,0,0,0,48,1,0,0,126,1,0,0,254,0,0,0,182,0,0,0,2,0,0,0,90,0,0,0,104,0,0,0,124,0,0,0,70,1,0,0,64,0,0,0,8,0,0,0,194,0,0,0,60,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,204,2,0,144,202,2,0,122,105,0,0,28,79,0,0,38,2,0,0,254,0,0,0,168,0,0,0,168,0,0,0,50,1,0,0,246,0,0,0,148,1,0,0,32,0,0,0,2,0,0,0,74,1,0,0,28,0,0,0,48,0,0,0,220,0,0,0,102,0,0,0,36,0,0,0,184,0,0,0,50,0,0,0,48,0,0,0,180,0,0,0,20,0,0,0,4,25,0,0,0,0,0,0,0,0,0,0,32,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,0,0,0,136,0,0,0,54,0,0,0,64,0,0,0,0,0,0,0,8,0,0,0,238,0,0,0,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,199,2,0,232,194,2,0,112,23,0,0,16,14,0,0,125,0,0,0,75,0,0,0,75,0,0,0,75,0,0,0,78,0,0,0,6,1,0,0,178,1,0,0,66,1,0,0,2,0,0,0,56,0,0,0,122,0,0,0,10,0,0,0,128,1,0,0,94,0,0,0,82,0,0,0,94,0,0,0,112,0,0,0,46,0,0,0,0,0,0,0,24,0,0,0,4,1,0,0,0,0,0,0,0,0,0,0,38,0,0,0,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,0,0,0,0,0,0,0,108,1,0,0,148,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,184,193,2,0,232,191,2,0,0,2,0,0,134,1,0,0,10,0,0,0,7,0,0,0,5,0,0,0,5,0,0,0,48,1,0,0,192,0,0,0,72,0,0,0,86,1,0,0,2,0,0,0,220,0,0,0,22,0,0,0,16,0,0,0,132,1,0,0,98,0,0,0,118,0,0,0,124,0,0,0,44,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,190,2,0,224,188,2,0,208,2,0,0,104,1,0,0,12,0,0,0,7,0,0,0,8,0,0,0,8,0,0,0,48,1,0,0,250,0,0,0,216,0,0,0,88,1,0,0,2,0,0,0,134,0,0,0,38,0,0,0,46,0,0,0,156,0,0,0,52,0,0,0,28,0,0,0,124,0,0,0,54,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,187,2,0,16,185,2,0,16,39,0,0,76,29,0,0,60,0,0,0,30,0,0,0,107,0,0,0,107,0,0,0,104,0,0,0,8,1,0,0,4,1,0,0,16,1,0,0,2,0,0,0,178,0,0,0,26,0,0,0,4,0,0,0,84,1,0,0,108,0,0,0,22,0,0,0,124,0,0,0,60,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,183,2,0,120,182,2,0,21,177,0,0,148,132,0,0,169,0,0,0,112,0,0,0,106,0,0,0,106,0,0,0,204,0,0,0,110,1,0,0,58,0,0,0,30,1,0,0,2,0,0,0,154,1,0,0,152,0,0,0,92,0,0,0,120,0,0,0,18,0,0,0,2,0,0,0,84,0,0,0,32,0,0,0,48,0,0,0,142,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,181,2,0,88,179,2,0,228,12,0,0,36,9,0,0,60,0,0,0,30,0,0,0,20,0,0,0,20,0,0,0,68,0,0,0,80,1,0,0,88,0,0,0,12,0,0,0,2,0,0,0,70,0,0,0,52,0,0,0,68,0,0,0,168,0,0,0,22,0,0,0,76,0,0,0,168,0,0,0,54,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,178,2,0,168,175,2,0,152,58,0,0,16,39,0,0,66,1,0,0,157,0,0,0,66,0,0,0,66,0,0,0,24,1,0,0,210,0,0,0,64,1,0,0,0,1,0,0,2,0,0,0,198,0,0,0,12,0,0,0,84,0,0,0,18,1,0,0,4,0,0,0,16,0,0,0,80,0,0,0,120,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,174,2,0,152,172,2,0,32,28,0,0,176,19,0,0,140,0,0,0,84,0,0,0,63,0,0,0,63,0,0,0,122,1,0,0,42,0,0,0,42,1,0,0,146,0,0,0,2,0,0,0,162,0,0,0,20,0,0,0,118,0,0,0,242,0,0,0,116,0,0,0,26,0,0,0,154,0,0,0,114,0,0,0,54,0,0,0,90,0,0,0,6,0,0,0,148,29,0,0,0,0,0,0,0,0,0,0,6,0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,16,0,0,0,30,1,0,0,8,0,0,0,34,0,0,0,10,0,0,0,132,0,0,0,136,1,0,0,0,1,0,0,86,0,0,0,0,0,0,0,0,0,36,64,248,170,2,0,192,169,2,0,40,35,0,0,112,23,0,0,120,0,0,0,70,0,0,0,70,0,0,0,70,0,0,0,48,1,0,0,228,0,0,0,118,1,0,0,70,1,0,0,2,0,0,0,62,1,0,0,30,0,0,0,56,0,0,0,18,0,0,0,24,0,0,0,12,0,0,0,124,0,0,0,54,0,0,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].concat([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,3,0,64,168,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,172,1,0,0,34,0,0,0,4,0,0,0,156,0,0,0,2,0,0,0,212,0,0,0,74,0,0,0,62,0,0,0,20,0,0,0,68,0,0,0,132,0,0,0,110,0,0,0,122,0,0,0,48,0,0,0,144,0,0,0,10,0,0,0,5,11,0,0,0,0,0,0,0,0,0,0,28,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,0,0,0,180,0,0,0,4,1,0,0,14,0,0,0,0,0,0,0,6,0,0,0,82,0,0,0,194,0,0,0,228,0,0,0,102,1,0,0,0,0,0,0,0,0,36,64,208,165,2,0,48,164,2,0,182,3,0,0,122,2,0,0,18,0,0,0,10,0,0,0,7,0,0,0,7,0,0,0,200,0,0,0,248,0,0,0,18,0,0,0,140,1,0,0,2,0,0,0,152,0,0,0,94,0,0,0,142,0,0,0,180,0,0,0,30,0,0,0,176,0,0,0,148,0,0,0,26,0,0,0,10,0,0,0,204,0,0,0,2,0,0,0,1,9,0,0,0,0,0,0,0,0,0,0,24,0,0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,166,0,0,0,82,1,0,0,138,0,0,0,76,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,162,2,0,72,161,2,0,232,3,0,0,232,3,0,0,25,0,0,0,16,0,0,0,18,0,0,0,18,0,0,0,230,0,0,0,100,1,0,0,244,0,0,0,76,0,0,0,2,0,0,0,130,0,0,0,32,0,0,0,154,0,0,0,236,0,0,0,96,0,0,0,12,0,0,0,6,0,0,0,60,0,0,0,48,0,0,0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,160,2,0,208,158,2,0,220,5,0,0,132,3,0,0,45,0,0,0,22,0,0,0,20,0,0,0,20,0,0,0,188,0,0,0,106,1,0,0,182,1,0,0,174,1,0,0,2,0,0,0,26,1,0,0,134,0,0,0,132,0,0,0,160,0,0,0,90,0,0,0,114,0,0,0,30,0,0,0,56,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,176,157,2,0,72,156,2,0,220,5,0,0,132,3,0,0,45,0,0,0,22,0,0,0,20,0,0,0,20,0,0,0,188,0,0,0,14,1,0,0,158,0,0,0,146,1,0,0,2,0,0,0,26,1,0,0,134,0,0,0,132,0,0,0,160,0,0,0,90,0,0,0,114,0,0,0,30,0,0,0,56,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,154,2,0,248,152,2,0,32,28,0,0,176,19,0,0,110,0,0,0,66,0,0,0,63,0,0,0,63,0,0,0,122,1,0,0,42,0,0,0,186,0,0,0,146,0,0,0,2,0,0,0,162,0,0,0,20,0,0,0,118,0,0,0,34,1,0,0,16,0,0,0,26,0,0,0,154,0,0,0,114,0,0,0,48,0,0,0,90,0,0,0,6,0,0,0,148,32,0,0,0,0,0,0,0,0,0,0,6,0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,16,0,0,0,32,0,0,0,8,0,0,0,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,86,0,0,0,0,0,0,0,0,0,0,0,0,152,2,0,216,149,2,0,32,28,0,0,176,19,0,0,100,0,0,0,60,0,0,0,63,0,0,0,63,0,0,0,122,1,0,0,42,0,0,0,186,0,0,0,14,0,0,0,2,0,0,0,162,0,0,0,20,0,0,0,118,0,0,0,242,0,0,0,12,0,0,0,26,0,0,0,154,0,0,0,114,0,0,0,54,0,0,0,0,0,0,0,6,0,0,0,128,32,0,0,0,0,0,0,0,0,0,0,6,0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,16,0,0,0,30,1,0,0,8,0,0,0,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,0,0,0,0,0,0,0,0,0,0,0,232,147,2,0,88,146,2,0,32,28,0,0,176,19,0,0,100,0,0,0,60,0,0,0,63,0,0,0,63,0,0,0,122,1,0,0,42,0,0,0,186,0,0,0,14,0,0,0,2,0,0,0,162,0,0,0,20,0,0,0,118,0,0,0,242,0,0,0,12,0,0,0,26,0,0,0,154,0,0,0,114,0,0,0,54,0,0,0,0,0,0,0,6,0,0,0,128,32,0,0,0,0,0,0,0,0,0,0,6,0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,16,0,0,0,30,1,0,0,8,0,0,0,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,86,0,0,0,0,0,0,0,0,0,0,0,240,144,2,0,176,142,2,0,184,11,0,0,8,7,0,0,83,0,0,0,41,0,0,0,41,0,0,0,41,0,0,0,46,0,0,0,84,0,0,0,164,0,0,0,52,1,0,0,2,0,0,0,124,1,0,0,116,0,0,0,44,0,0,0,62,0,0,0,20,0,0,0,64,0,0,0,128,0,0,0,36,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,141,2,0,152,139,2,0,136,19,0,0,184,11,0,0,152,0,0,0,73,0,0,0,69,0,0,0,69,0,0,0,10,1,0,0,152,1,0,0,160,0,0,0,224,0,0,0,2,0,0,0,154,0,0,0,6,0,0,0,72,0,0,0,48,0,0,0,100,0,0,0,88,0,0,0,190,0,0,0,82,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,138,2,0,168,136,2,0,16,39,0,0,16,39,0,0,164,1,0,0,160,0,0,0,200,0,0,0,150,0,0,0,138,1,0,0,92,1,0,0,68,1,0,0,102,1,0,0,2,0,0,0,44,1,0,0,90,0,0,0,66,0,0,0,34,0,0,0,66,0,0,0,116,0,0,0,186,0,0,0,14,0,0,0,40,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,0,0,0,0,148,0,0,0,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,134,2,0,8,133,2,0,220,5,0,0,132,3,0,0,45,0,0,0,22,0,0,0,20,0,0,0,20,0,0,0,48,1,0,0,226,0,0,0,54,1,0,0,122,0,0,0,2,0,0,0,176,0,0,0,146,0,0,0,110,0,0,0,142,0,0,0,126,0,0,0,130,0,0,0,170,0,0,0,84,0,0,0,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,131,2,0,0,130,2,0,220,5,0,0,132,3,0,0,45,0,0,0,22,0,0,0,20,0,0,0,20,0,0,0,48,1,0,0,114,0,0,0,130,1,0,0,90,1,0,0,2,0,0,0,94,0,0,0,138,0,0,0,140,0,0,0,120,1,0,0,106,0,0,0,70,0,0,0,198,0,0,0,54,0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,128,2,0,136,125,2,0,224,46,0,0,32,28,0,0,110,1,0,0,176,0,0,0,166,0,0,0,166,0,0,0,136,1,0,0,60,1,0,0,126,0,0,0,134,1,0,0,2,0,0,0,252,0,0,0,112,0,0,0,58,0,0,0,204,0,0,0,10,0,0,0,72,0,0,0,200,0,0,0,34,0,0,0,22,0,0,0,68,0,0,0,12,0,0,0,132,1,0,0,0,0,0,0,0,0,0,0,26,0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,178,0,0,0,156,1,0,0,212,0,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,123,2,0,208,120,2,0,156,49,0,0,196,29,0,0,165,1,0,0,206,0,0,0,123,0,0,0,123,0,0,0,116,0,0,0,74,0,0,0,58,1,0,0,110,0,0,0,2,0,0,0,54,0,0,0,70,0,0,0,2,0,0,0,46,0,0,0,48,0,0,0,188,0,0,0,112,0,0,0,92,0,0,0,30,0,0,0,38,0,0,0,28,0,0,0,0,59,0,0,0,0,0,0,0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,140,0,0,0,0,0,0,0,178,0,0,0,114,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,0,0,0,0,0,128,63,0,0,0,0,0,0,240,65,0,0,0,0,0,0,112,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,93,0,0,0,0,0,0,9,46,46,46,105,116,32,105,115,78,101,119,76,101,118,101,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,0,0,0,0,0,0,0,97,65,101,69,102,70,103,71,0,0,0,0,0,0,0,0,99,100,105,111,117,120,88,0,104,108,76,113,106,122,90,116,67,83,112,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,21,3,0,160,0,3,0,176,212,2,0,32,192,2,0,224,175,2,0,120,160,2,0,32,145,2,0,136,128,2,0,8,184,2,0,64,79,2,0,120,129,2,0,96,64,2,0,40,56,2,0,168,48,2,0,208,42,2,0,48,36,2,0,152,28,2,0,80,22,2,0,184,14,2,0,232,8,2,0,120,3,2,0,80,254,1,0,56,250,1,0,64,245,1,0,8,240,1,0,200,235,1,0])
+.concat([240,230,1,0,40,226,1,0,224,220,1,0,48,214,1,0,40,209,1,0,160,197,1,0,56,194,1,0,128,190,1,0,192,177,1,0,216,165,1,0,16,162,1,0,184,158,1,0,48,154,1,0,224,151,1,0,240,149,1,0,128,148,1,0,224,139,1,0,240,137,1,0,8,135,1,0,88,133,1,0,144,131,1,0,152,129,1,0,8,127,1,0,96,124,1,0,32,122,1,0,72,120,1,0,200,118,1,0,192,116,1,0,8,115,1,0,56,112,1,0,216,109,1,0,160,107,1,0,184,103,1,0,8,101,1,0,136,98,1,0,232,96,1,0,88,94,1,0,104,92,1,0,80,89,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,199,1,0,1,0,0,0,88,199,1,0,2,0,0,0,224,198,1,0,3,0,0,0,40,43,1,0,4,0,0,0,136,198,1,0,4,0,0,0,176,111,1,0,5,0,0,0,88,198,1,0,9,0,0,0,232,197,1,0,10,0,0,0,152,197,1,0,6,0,0,0,8,197,1,0,7,0,0,0,112,196,1,0,8,0,0,0,248,195,1,0,11,0,0,0,0,0,0,0,0,0,0,0,248,60,1,0,56,55,1,0,88,52,1,0,48,50,1,0,200,48,1,0,0,0,0,0,16,241,1,0,152,240,1,0,88,52,1,0,48,50,1,0,200,48,1,0,0,0,0,0,16,241,1,0,152,240,1,0,88,52,1,0,48,50,1,0,104,249,1,0,0,0,0,0,0,0,0,0,0,0,0,0,37,115,115,99,97,108,101,32,109,117,115,116,32,98,101,32,62,32,48,59,32,118,105,101,119,32,117,110,99,104,97,110,103,101,100,0,0,0,0,0,114,111,116,95,37,99,32,109,117,115,116,32,98,101,32,105,110,32,91,48,58,37,100,93,32,100,101,103,114,101,101,115,32,114,97,110,103,101,59,32,118,105,101,119,32,117,110,99,104,97,110,103,101,100,0,0,232,119,1,0,2,0,0,0,16,118,1,0,1,0,0,0,48,116,1,0,1,0,0,0,96,114,1,0,3,0,0,0,176,111,1,0,4,0,0,0,56,109,1,0,5,0,0,0,248,105,1,0,6,0,0,0,8,199,1,0,7,0,0,0,40,100,1,0,8,0,0,0,232,97,1,0,9,0,0,0,248,95,1,0,10,0,0,0,32,93,1,0,11,0,0,0,56,91,1,0,12,0,0,0,0,89,1,0,13,0,0,0,0,84,1,0,14,0,0,0,224,79,1,0,15,0,0,0,16,75,1,0,17,0,0,0,232,72,1,0,18,0,0,0,160,69,1,0,19,0,0,0,16,66,1,0,20,0,0,0,80,60,1,0,21,0,0,0,248,54,1,0,22,0,0,0,24,52,1,0,23,0,0,0,248,49,1,0,16,0,0,0,96,48,1,0,16,0,0,0,0,46,1,0,24,0,0,0,184,43,1,0,25,0,0,0,96,41,1,0,26,0,0,0,24,39,1,0,27,0,0,0,104,37,1,0,28,0,0,0,104,35,1,0,29,0,0,0,200,33,1,0,30,0,0,0,96,32,1,0,31,0,0,0,208,30,1,0,32,0,0,0,112,28,1,0,31,0,0,0,32,111,3,0,32,0,0,0,80,109,3,0,33,0,0,0,80,105,3,0,34,0,0,0,240,102,3,0,35,0,0,0,64,101,3,0,36,0,0,0,32,98,3,0,37,0,0,0,56,96,3,0,37,0,0,0,136,93,3,0,38,0,0,0,0,87,3,0,39,0,0,0,48,85,3,0,40,0,0,0,192,82,3,0,41,0,0,0,128,80,3,0,42,0,0,0,240,77,3,0,44,0,0,0,32,76,3,0,45,0,0,0,56,74,3,0,48,0,0,0,56,72,3,0,49,0,0,0,32,70,3,0,46,0,0,0,128,67,3,0,47,0,0,0,120,65,3,0,52,0,0,0,80,63,3,0,53,0,0,0,208,60,3,0,50,0,0,0,168,58,3,0,51,0,0,0,48,57,3,0,54,0,0,0,56,55,3,0,55,0,0,0,104,52,3,0,68,0,0,0,176,49,3,0,69,0,0,0,88,48,3,0,56,0,0,0,16,46,3,0,57,0,0,0,200,43,3,0,58,0,0,0,136,40,3,0,59,0,0,0,0,38,3,0,61,0,0,0,176,34,3,0,60,0,0,0,64,32,3,0,62,0,0,0,32,31,3,0,63,0,0,0,56,30,3,0,63,0,0,0,16,189,2,0,76,0,0,0,16,29,3,0,77,0,0,0,24,28,3,0,78,0,0,0,56,27,3,0,79,0,0,0,248,172,2,0,80,0,0,0,72,26,3,0,81,0,0,0,24,25,3,0,75,0,0,0,0,24,3,0,82,0,0,0,120,97,1,0,83,0,0,0,152,19,3,0,85,0,0,0,184,16,3,0,84,0,0,0,128,13,3,0,86,0,0,0,176,123,1,0,87,0,0,0,200,10,3,0,88,0,0,0,112,9,3,0,89,0,0,0,216,7,3,0,90,0,0,0,168,6,3,0,91,0,0,0,248,4,3,0,92,0,0,0,224,2,3,0,93,0,0,0,152,0,3,0,94,0,0,0,128,121,1,0,97,0,0,0,24,254,2,0,98,0,0,0,0,252,2,0,99,0,0,0,88,245,2,0,119,0,0,0,120,243,2,0,110,0,0,0,144,235,2,0,101,0,0,0,32,221,2,0,129,0,0,0,216,218,2,0,120,0,0,0,224,216,2,0,138,0,0,0,112,214,2,0,70,0,0,0,168,212,2,0,113,0,0,0,184,210,2,0,104,0,0,0,48,209,2,0,132,0,0,0,232,207,2,0,123,0,0,0,64,206,2,0,141,0,0,0,208,204,2,0,64,0,0,0,232,202,2,0,117,0,0,0,96,200,2,0,118,0,0,0,40,195,2,0,108,0,0,0,200,193,2,0,109,0,0,0,24,192,2,0,136,0,0,0,120,190,2,0,137,0,0,0,8,189,2,0,127,0,0,0,192,187,2,0,128,0,0,0,96,185,2,0,145,0,0,0,160,183,2,0,146,0,0,0,200,182,2,0,147,0,0,0,96,181,2,0,148,0,0,0,120,179,2,0,66,0,0,0,48,178,2,0,67,0,0,0,216,175,2,0,111,0,0,0,80,174,2,0,112,0,0,0,232,172,2,0,102,0,0,0,16,171,2,0,103,0,0,0,0,170,2,0,130,0,0,0,160,168,2,0,131,0,0,0,24,166,2,0,121,0,0,0,112,164,2,0,122,0,0,0,8,163,2,0,139,0,0,0,136,161,2,0,140,0,0,0,104,160,2,0,71,0,0,0,0,159,2,0,72,0,0,0,200,157,2,0,114,0,0,0,120,156,2,0,115,0,0,0,200,154,2,0,105,0,0,0,56,153,2,0,106,0,0,0,16,152,2,0,133,0,0,0,40,150,2,0,134,0,0,0,248,147,2,0,124,0,0,0,152,146,2,0,125,0,0,0,24,145,2,0,142,0,0,0,232,142,2,0,143,0,0,0,64,141,2,0,73,0,0,0,232,139,2,0,74,0,0,0,120,138,2,0,116,0,0,0,232,136,2,0,107,0,0,0,16,135,2,0,135,0,0,0,88,133,2,0,126,0,0,0,224,131,2,0,144,0,0,0,56,130,2,0,65,0,0,0,80,128,2,0,149,0,0,0,192,125,2,0,95,0,0,0,224,123,2,0,96,0,0,0,88,121,2,0,100,0,0,0,192,111,2,0,153,0,0,0,24,109,2,0,154,0,0,0,80,107,2,0,155,0,0,0,240,104,2,0,156,0,0,0,168,96,2,0,157,0,0,0,80,93,2,0,152,0,0,0,248,91,2,0,150,0,0,0,216,90,2,0,151,0,0,0,0,0,0,0,0,0,0,0,48,116,1,0,1,0,0,0,24,20,2,0,2,0,0,0,128,19,2,0,3,0,0,0,136,18,2,0,4,0,0,0,240,17,2,0,5,0,0,0,120,17,2,0,6,0,0,0,160,16,2,0,7,0,0,0,32,16,2,0,8,0,0,0,144,15,2,0,9,0,0,0,168,14,2,0,10,0,0,0,24,14,2,0,11,0,0,0,152,13,2,0,12,0,0,0,40,13,2,0,13,0,0,0,136,12,2,0,14,0,0,0,232,247,1,0,15,0,0,0,48,11,2,0,16,0,0,0,168,10,2,0,17,0,0,0,16,10,2,0,18,0,0,0,144,9,2,0,19,0,0,0,216,8,2,0,20,0,0,0,168,7,2,0,21,0,0,0,240,6,2,0,22,0,0,0,96,6,2,0,23,0,0,0,0,0,0,0,0,0,0,0,240,34,2,0,1,0,0,0,40,34,2,0,2,0,0,0,8,93,1,0,3,0,0,0,128,32,2,0,3,0,0,0,208,250,1,0,4,0,0,0,248,30,2,0,5,0,0,0,88,30,2,0,9,0,0,0,104,29,2,0,10,0,0,0,136,28,2,0,11,0,0,0,112,27,2,0,12,0,0,0,192,26,2,0,6,0,0,0,56,26,2,0,7,0,0,0,168,25,2,0,8,0,0,0,64,25,2,0,13,0,0,0,128,24,2,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,154,153,153,153,153,153,169,63,154,153,153,153,153,153,169,63,154,153,153,153,153,153,201,63,154,153,153,153,153,153,185,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,208,63,102,102,102,102,102,102,230,63,51,51,51,51,51,51,235,63,205,204,204,204,204,204,236,63,154,153,153,153,153,153,217,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,232,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,224,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,102,102,102,102,102,102,230,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,205,204,204,204,204,204,236,63,51,51,51,51,51,51,227,63,51,51,51,51,51,51,227,63,51,51,51,51,51,51,227,63,0,0,0,0,0,0,240,63,102,102,102,102,102,102,238,63,102,102,102,102,102,102,238,63,102,102,102,102,102,102,238,63,232,211,1,0,0,0,0,0,128,82,2,0,1,0,0,0,112,82,2,0,2,0,0,0,96,82,2,0,4,0,0,0,80,82,2,0,5,0,0,0,8,82,2,0,3,0,0,0,0,82,2,0,6,0,0,0,128,234,1,0,7,0,0,0,0,0,0,0,8,0,0,0,136,208,2,0,39,0,0,0,104,75,2,0,40,0,0,0,192,74,2,0,41,0,0,0,32,74,2,0,1,0,0,0,88,73,2,0,2,0,0,0,192,72,2,0,3,0,0,0,64,72,2,0,4,0,0,0,56,71,2,0,5,0,0,0,8,70,2,0,6,0,0,0,104,69,2,0,7,0,0,0,216,68,2,0,8,0,0,0,232,67,2,0,13,0,0,0,168,67,2,0,9,0,0,0,192,66,2,0,14,0,0,0,48,116,1,0,10,0,0,0,32,66,2,0,11,0,0,0,88,65,2,0,12,0,0,0,80,64,2,0,15,0,0,0,72,63,2,0,16,0,0,0,64,62,2,0,17,0,0,0,48,61,2,0,18,0,0,0,136,60,2,0,19,0,0,0,200,59,2,0,20,0,0,0,32,59,2,0,21,0,0,0,120,58,2,0,22,0,0,0,232,57,2,0,23,0,0,0,200,56,2,0,24,0,0,0,0,224,1,0,25,0,0,0,192,218,1,0,26,0,0,0,240,53,2,0,27,0,0,0,16,53,2,0,28,0,0,0,40,52,2,0,29,0,0,0,72,51,2,0,30,0,0,0,184,50,2,0,31,0,0,0,64,50,2,0,32,0,0,0,248,49,2,0,37,0,0,0,96,49,2,0,38,0,0,0,128,234,1,0,33,0,0,0,0,48,2,0,34,0,0,0,192,229,1,0,35,0,0,0,192,46,2,0,36,0,0,0,8,46,2,0,36,0,0,0,72,45,2,0,42,0,0,0,200,44,2,0,42,0,0,0,40,44,2,0,43,0,0,0,176,43,2,0,44,0,0,0,88,43,2,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,89,2,0,1,0,0,0,0,82,2,0,2,0,0,0,40,86,2,0,3,0,0,0,112,84,2,0,4,0,0,0,0,83,2,0,5,0,0,0,192,81,2,0,6,0,0,0,200,80,2,0,7,0,0,0,32,80,2,0,8,0,0,0,48,79,2,0,9,0,0,0,8,78,2,0,10,0,0,0,72,77,2,0,11,0,0,0,176,76,2,0,12,0,0,0,0,0,0,0,0,0,0,0,136,208,2,0,0,0,0,0,88,188,2,0,15,0,0,0,24,172,2,0,1,0,0,0,176,73,2,0,2,0,0,0,152,4,2,0,3,0,0,0,160,209,1,0,4,0,0,0,88,157,2,0,5,0,0,0,144,122,1,0,6,0,0,0,184,98,1,0,7,0,0,0,184,67,1,0,8,0,0,0,24,38,1,0,9,0,0,0,40,102,3,0,10,0,0,0,56,75,3,0,11,0,0,0,152,140,2,0,12,0,0,0,112,122,2,0,13,0,0,0,152,88,2,0,14,0,0,0,0,0,0,0,16,0,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,194,176,0,0,0,0,0,0,160,41,2,0,1,0,0,0,216,40,2,0,2,0,0,0,136,208,2,0,3,0,0,0,32,40,2,0,4,0,0,0,48,116,1,0,4,0,0,0,80,39,2,0,5,0,0,0,184,38,2,0,6,0,0,0,56,38,2,0,7,0,0,0,184,37,2,0,8,0,0,0,56,86,3,0,9,0,0,0,40,36,2,0,10,0,0,0,120,35,2,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,128,1,0,1,0,0,0,16,126,1,0,3,0,0,0,176,123,1,0,2,0,0,0,128,121,1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,141,2,0,40,140,2,0,160,138,2,0,56,137,2,0,96,135,2,0,0,0,0,0,100,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,72,101,108,118,101,116,105,99,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,205,204,204,204,204,204,236,63,205,204,204,204,204,204,236,63,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,16,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,37,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,1,0,0,208,7,0,0,250,126,106,188,116,147,104,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,1,0,0,208,7,0,0,250,126,106,188,116,147,104,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,1,0,0,208,7,0,0,250,126,106,188,116,147,104,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,0,1,0,0,208,7,0,0,250,126,106,188,116,147,104,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,118,0,0,0,0,0,0,0,0,0,0,0,0,0,0,208,243,1,0,24,150,1,0,104,136,1,0,8,116,1,0,128,90,1,0,48,153,2,0,80,32,1,0,112,22,3,0,64,67,3,0,192,45,3,0,248,26,3,0,168,7,3,0,120,220,2,0,112,202,2,0,96,182,2,0,152,165,2,0,232,151,2,0,216,134,2,0,0,107,2,0,144,82,2,0,144,74,2,0,120,67,2,0,224,58,2,0,128,50,2,0,152,44,2,0,152,38,2,0,112,31,2,0,96,24,2,0,16,17,2,0,8,11,2,0,16,5,2,0,72,0,2,0,240,251,1,0,136,246,1,0,248,241,1,0,8,237,1,0,24,232,1,0,248,227,1,0,136,222,1,0,144,216,1,0,16,210,1,0,152,198,1,0,32,195,1,0,200,191,1,0,184,186,1,0,72,167,1,0,104,163,1,0,0,160,1,0,40,155,1,0,104,152,1,0,80,150,1,0,208,148,1,0,184,140,1,0,152,138,1,0,176,135,1,0,184,133,1,0,56,132,1,0,168,130,1,0,176,127,1,0,32,125,1,0,232,122,1,0,168,120,1,0,48,119,1,0,40,117,1,0,80,115,1,0,152,112,1,0,64,110,1,0,72,108,1,0,168,104,1,0,144,101,1,0,208,98,1,0,72,97,1,0,48,95,1,0,192,92,1,0,152,190,2,0,152,190,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,93,1,0,72,93,1,0,248,92,1,0,208,92,1,0,240,92,1,0,184,92,1,0,160,92,1,0,48,55,1,0,232,54,1,0,200,54,1,0,72,93,1,0,192,54,1,0,0,0,0,0,0,0,255,0,0,0,255,0,0,0,255,255,0,255,0,255,255,255,255,0,143,188,143,255,105,180,0,0,0,255,127,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,89,64,0,0,0,0,0,136,195,64,0,0,0,0,132,215,151,65,0,128,224,55,121,195,65,67,23,110,5,181,181,184,147,70,245,249,63,233,3,79,56,77,50,29,48,249,72,119,130,90,60,191,115,127,221,79,21,117,3,0,0,0,50,0,0,0,50,0,0,0,2,0,0,0,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,65,0,0,128,63,0,0,0,0,208,7,0,0,250,126,106,188,116,147,104,63,0,1,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,3,0,0,0,50,0,0,0,50,0,0,0,2,0,0,0,0,0,0,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,65,0,0,128,63,0,0,0,0,208,7,0,0,250,126,106,188,116,147,104,63,0,1,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,255,255,255,255,0,0,0,0,48,0,0,0,0,0,0,0,128,194,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,0,0,0,0,0,0,98,0,97,52,0,0,0,0,0,0,0,0,0,4,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,224,198,1,0,1,0,0,0,184,195,1,0,33,0,0,0,232,52,2,0,18,0,0,0,48,195,1,0,51,0,0,0,240,194,1,0,51,0,0,0,192,194,1,0,64,0,0,0,104,194,1,0,39,1,0,0])
+.concat([40,194,1,0,39,1,0,0,88,193,1,0,23,1,0,0,240,192,1,0,55,1,0,0,168,192,1,0,102,0,0,0,104,192,1,0,102,0,0,0,16,192,1,0,86,0,0,0,224,191,1,0,118,0,0,0,168,191,1,0,153,0,0,0,112,191,1,0,136,1,0,0,32,191,1,0,89,1,0,0,112,190,1,0,169,0,0,0,88,189,1,0,137,0,0,0,192,188,1,0,177,0,0,0,96,188,1,0,184,0,0,0,40,188,1,0,193,0,0,0,120,187,1,0,209,0,0,0,216,186,1,0,225,0,0,0,104,186,1,0,1,1,0,0,88,185,1,0,252,0,0,0,248,195,1,0,218,1,0,0,0,38,3,0,96,1,0,0,80,184,1,0,112,1,0,0,184,177,1,0,144,1,0,0,248,175,1,0,160,1,0,0,128,172,1,0,176,1,0,0,216,170,1,0,201,1,0,0,176,168,1,0,233,1,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,128,196,1,0,1,0,0,0,0,193,1,0,2,0,0,0,200,188,1,0,3,0,0,0,144,172,1,0,4,0,0,0,216,164,1,0,5,0,0,0,8,161,1,0,6,0,0,0,48,157,1,0,7,0,0,0,16,153,1,0,8,0,0,0,200,150,1,0,9,0,0,0,0,0,0,0,0,0,0,0,67,97,110,32,115,112,101,99,105,102,121,32,96,111,114,105,103,105,110,96,32,111,114,32,96,99,101,110,116,101,114,96,44,32,98,117,116,32,110,111,116,32,98,111,116,104,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,224,1,0,0,0,0,0,216,219,1,0,1,0,0,0,64,212,1,0,2,0,0,0,232,199,1,0,3,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,154,153,153,153,153,153,201,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,123,20,174,71,225,122,148,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,118,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,4,0,0,0,1,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0,0,0,0,0,10,215,163,59,56,0,0,0,71,0,0,0,111,18,3,59,49,8,172,61,40,0,0,0,59,0,0,0,111,18,3,59,49,8,172,61,40,0,0,0,110,0,0,0,111,18,3,59,49,8,172,61,40,0,0,0,53,0,0,0,111,18,3,59,49,8,172,61,40,0,0,0,115,0,0,0,111,18,3,59,182,243,125,61,40,0,0,0,75,0,0,0,10,215,163,59,154,153,153,61,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,1,0,0,0,44,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,32,35,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,28,0,0,0,31,0,0,0,30,0,0,0,31,0,0,0,30,0,0,0,31,0,0,0,31,0,0,0,30,0,0,0,31,0,0,0,30,0,0,0,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,8,0,0,0,12,0,0,0,13,0,0,0,5,0,0,0,9,0,0,0,10,0,0,0,11,0,0,0,12,0,0,0,13,0,0,0,14,0,0,0,15,0,0,0,0,0,0,0,231,251,31,65,160,134,0,0,160,134,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,12,0,0,0,3,0,0,0,0,0,0,0,9,0,0,0,8,0,0,0,14,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,208,43,3,0,136,43,3,0,0,43,3,0,168,42,3,0,120,42,3,0,64,42,3,0,216,41,3,0,128,41,3,0,64,41,3,0,240,40,3,0,152,40,3,0,120,40,3,0,32,40,3,0,0,40,3,0,192,39,3,0,128,39,3,0,64,39,3,0,0,39,3,0,176,38,3,0,160,38,3,0,8,38,3,0,184,37,3,0,48,37,3,0,248,36,3,0,176,36,3,0,80,36,3,0,40,36,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,200,1,0,240,196,1,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,150,1,0,48,153,2,0,240,152,2,0,224,156,0,0,232,152,2,0,224,152,2,0,112,169,2,0,0,0,0,0,128,152,2,0,120,152,2,0,96,152,2,0,24,152,2,0,8,152,2,0,248,151,2,0,224,151,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,127,0,0,0,0,255,255,255,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,64,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,205,204,204,204,204,204,236,63,205,204,204,204,204,204,236,63,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,16,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216,117,1,0,32,235,1,0,80,230,1,0,192,224,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,236,175,203,174,131,28,212,63,5,131,82,81,72,84,238,63,69,54,70,161,27,144,249,63,152,255,235,55,110,60,2,64,78,179,64,187,67,42,8,64,182,126,159,22,40,30,15,64,4,60,105,225,178,74,246,63,1,185,171,244,102,150,205,63,22,140,132,149,142,226,195,191,74,181,106,247,109,120,121,63,254,41,194,141,220,23,24,63,203,225,243,45,104,14,165,190,132,27,223,205,9,48,240,63,248,153,229,120,38,16,232,191,53,126,55,150,221,183,137,63,17,80,205,36,107,134,132,63,156,200,73,125,117,186,47,191,218,83,12,136,64,206,160,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,15,0,0,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,56,15,0,14,53,15,0,14,59,15,0,14,58,15,0,14,60,15,0,15,124,15,0,14,61,15,0,14,43,15,0,0,0,0,0,14,121,15,0,14,123,15,0,0,0,0,0,0,0,0,0,0,0,0,0,14,48,15,0,14,122,15,0,14,126,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,122,15,0,14,125,15,0,14,119,15,0,14,120,15,0,0,0,0,0,14,57,15,0,14,33,15,0,14,96,15,0,14,34,15,0,14,97,15,0,14,88,15,0,14,80,15,0,14,83,15,0,14,52,15,0,14,35,15,0,14,92,15,0,14,36,15,0,14,37,15,0,14,102,15,0,14,101,15,0,14,38,15,0,14,39,15,0,14,99,15,0,14,54,15,0,14,104,15,0,14,103,15,0,14,95,15,0,14,105,15,0,14,90,15,0,0,0,0,0,14,82,15,0,14,45,15,0,14,109,15,0,14,46,15,0,14,91,15,0,0,0,0,0,14,112,15,0,14,94,15,0,14,72,15,0,14,68,15,0,14,64,15,0,14,98,15,0,14,76,15,0,14,84,15,0,14,87,15,0,14,53,15,0,14,73,15,0,14,69,15,0,14,65,15,0,14,77,15,0,14,89,15,0,14,85,15,0,14,81,15,0,14,93,15,0,14,100,15,0,14,55,15,0,14,74,15,0,14,70,15,0,14,66,15,0,14,106,15,0,14,78,15,0,0,0,0,0,14,86,15,0,14,75,15,0,14,71,15,0,14,67,15,0,14,79,15,0,0,0,0,0,14,113,15,0,14,111,15,0,14,52,15,0,14,79,15,0,14,69,15,0,14,64,15,0,14,76,15,0,14,72,15,0,14,84,15,0,14,53,15,0,14,65,15,0,14,77,15,0,14,73,15,0,14,93,15,0,14,81,15,0,14,89,15,0,14,88,15,0,14,80,15,0,14,92,15,0,14,87,15,0,14,83,15,0,14,66,15,0,14,78,15,0,14,74,15,0,14,67,15,0,14,75,15,0,14,111,15,0,14,90,15,0,14,91,15,0,14,86,15,0,14,59,15,0,14,82,15,0,0,0,0,0,14,62,15,0,14,68,15,0,14,85,15,0,14,70,15,0,14,71,15,0,14,55,15,0,14,54,15,0,14,121,15,0,14,122,15,0,14,57,15,0,0,0,0,0,0,0,0,0,14,120,15,0,14,119,15,0,14,56,15,0,14,123,15,0,14,125,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,96,15,0,14,34,15,0,14,33,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,63,15,0,14,60,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,98,15,0,14,97,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,58,15,0,14,100,15,0,14,99,15,0,14,36,15,0,14,37,15,0,14,35,15,0,0,0,0,0,14,101,15,0,14,38,15,0,14,39,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,102,15,0,0,0,0,0,14,103,15,0,14,94,15,0,14,95,15,0,14,104,15,0,14,106,15,0,14,105,15,0,0,0,0,0,14,113,15,0,14,112,15,0,14,109,15,0,14,46,15,0,14,45,15,0,0,0,0,0,0,0,0,0,14,48,15,0,14,40,15,0,14,118,15,0,14,126,15,0,0,0,0,0,0,0,0,0,0,0,0,0,14,61,15,0,0,0,0,0,0,0,0,0,14,122,15,0,14,43,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,251,255,255,255,251,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,46,54,0,0,0,0,0,51,0,0,0,0,0,0,0,50,48,49,51,45,48,52,45,49,50,32,0,0,0,0,0,67,111,112,121,114,105,103,104,116,32,40,67,41,32,49,57,56,54,45,49,57,57,51,44,32,49,57,57,56,44,32,50,48,48,52,44,32,50,48,48,55,45,50,48,49,51,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,74,97,110,117,97,114,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,101,98,114,117,97,114,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,77,97,114,99,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,112,114,105,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,77,97,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,74,117,110,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,74,117,108,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,117,103,117,115,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,101,112,116,101,109,98,101,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,99,116,111,98,101,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,111,118,101,109,98,101,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,101,99,101,109,98,101,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,117,110,100,97,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
+.concat([77,111,110,100,97,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,117,101,115,100,97,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,101,100,110,101,115,100,97,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,104,117,114,115,100,97,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,114,105,100,97,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,97,116,117,114,100,97,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,88,78,3,0,116,1,0,0,72,75,2,0,32,1,0,0,112,5,2,0,64,1,0,0,64,210,1,0,62,1,0,0,96,150,1,0,36,1,0,0,152,76,3,0,10,1,0,0,248,109,3,0,188,0,0,0,72,68,1,0,128,0,0,0,216,150,1,0,100,0,0,0,104,102,3,0,170,0,0,0,136,75,3,0,210,0,0,0,88,54,3,0,80,1,0,0,224,30,3,0,72,1,0,0,120,15,3,0,222,0,0,0,136,250,2,0,78,1,0,0,176,208,2,0,28,1,0,0,120,188,2,0,232,0,0,0,168,42,2,0,56,1,0,0,24,36,2,0,252,0,0,0,184,140,2,0,172,0,0,0,32,111,3,0,68,0,0,0,168,88,2,0,74,0,0,0,40,77,2,0,124,0,0,0,184,69,2,0,206,0,0,0,0,62,2,0,192,0,0,0,136,53,2,0,166,0,0,0,224,46,2,0,72,0,0,0,80,41,2,0,246,0,0,0,88,34,2,0,96,1,0,0,152,26,2,0,216,0,0,0,192,19,2,0,214,0,0,0,112,13,2,0,74,1,0,0,160,6,2,0,2,0,0,0,208,1,2,0,84,0,0,0,64,253,1,0,208,0,0,0,16,248,1,0,114,1,0,0,184,243,1,0,82,1,0,0,184,238,1,0,104,0,0,0,112,234,1,0,234,0,0,0,176,229,1,0,6,1,0,0,208,223,1,0,30,0,0,0,152,190,2,0,0,0,0,0,72,158,1,0,240,0,0,0,144,199,1,0,104,1,0,0,0,196,1,0,104,1,0,0,144,42,2,0,116,0,0,0,112,188,1,0,176,0,0,0,240,170,1,0,2,1,0,0,120,164,1,0,98,1,0,0,184,160,1,0,12,0,0,0,248,156,1,0,152,0,0,0,216,152,1,0,112,1,0,0,168,150,1,0,110,0,0,0,72,149,1,0,250,0,0,0,120,145,1,0,184,0,0,0,0,139,1,0,130,1,0,0,96,136,1,0,126,0,0,0,80,134,1,0,134,0,0,0,144,132,1,0,20,1,0,0,240,130,1,0,60,0,0,0,64,128,1,0,66,0,0,0,208,125,1,0,70,0,0,0,112,123,1,0,90,1,0,0,88,121,1,0,76,1,0,0,184,119,1,0,76,0,0,0,216,117,1,0,110,1,0,0,0,116,1,0,68,1,0,0,224,113,1,0,36,0,0,0,96,182,2,0,196,0,0,0,0,137,1,0,122,0,0,0,208,105,1,0,106,0,0,0,136,102,1,0,6,0,0,0,224,99,1,0,224,0,0,0,192,97,1,0,226,0,0,0,208,95,1,0,46,1,0,0,0,93,1,0,44,1,0,0,120,90,1,0,82,0,0,0,160,87,1,0,202,0,0,0,168,83,1,0,154,0,0,0,168,79,1,0,164,0,0,0,144,74,1,0,186,0,0,0,184,72,1,0,218,0,0,0,104,69,1,0,44,0,0,0,136,65,1,0,12,1,0,0,184,59,1,0,112,0,0,0,208,54,1,0,8,1,0,0,248,51,1,0,58,1,0,0,208,49,1,0,122,1,0,0,0,48,1,0,78,0,0,0,216,45,1,0,66,1,0,0,120,43,1,0,118,0,0,0,48,41,1,0,52,1,0,0,208,38,1,0,126,1,0,0,48,37,1,0,244,0,0,0,72,35,1,0,64,0,0,0,152,33,1,0,14,1,0,0,72,32,1,0,88,1,0,0,144,30,1,0,254,0,0,0,72,28,1,0,124,1,0,0,240,110,3,0,190,0,0,0,192,108,3,0,50,1,0,0,56,105,3,0,50,0,0,0,208,102,3,0,86,1,0,0,192,165,1,0,60,1,0,0,200,97,3,0,42,0,0,0,32,96,3,0,40,1,0,0,48,93,3,0,198,0,0,0,144,86,3,0,114,1,0,0,240,84,3,0,238,0,0,0,240,161,1,0,238,0,0,0,24,80,3,0,248,0,0,0,192,77,3,0,22,1,0,0,32,42,2,0,10,0,0,0,8,74,3,0,92,1,0,0,0,72,3,0,162,0,0,0,208,69,3,0,162,0,0,0,56,67,3,0,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,25,2,0,232,18,2,0,200,12,2,0,48,6,2,0,128,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,121,1,0,0,0,0,0,8,120,1,0,0,0,0,0,192,172,1,0,240,164,1,0,102,105,116,46,108,111,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,1,0,0,0,8,0,0,0,9,0,0,0,10,0,0,0,0,0,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,118,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,167,1,0,0,0,0,0,96,167,1,0,1,0,0,0,8,167,1,0,2,0,0,0,176,166,1,0,3,0,0,0,88,166,1,0,4,0,0,0,160,76,2,0,9,0,0,0,96,244,2,0,10,0,0,0,208,164,1,0,11,0,0,0,112,164,1,0,12,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,80,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,50,0,0,0,50,0,0,0,0,0,0,0,0,0,1,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,65,0,0,128,63,1,0,0,0,208,7,0,0,250,126,106,188,116,147,104,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,2,0,0,0,50,0,0,0,50,0,0,0,0,0,0,0,0,0,1,0,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,65,0,0,128,63,1,0,0,0,208,7,0,0,250,126,106,188,116,147,104,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,241,104,227,136,181,248,228,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,236,1,0,96,25,3,0,176,73,2,0,152,4,2,0,160,209,1,0,32,150,1,0,144,122,1,0,184,98,1,0,184,67,1,0,24,38,1,0,40,102,3,0,56,75,3,0,0,54,3,0,176,30,3,0,240,14,3,0,48,246,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,251,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,65,0,0,0,0,0,0,0,0,0,0,240,63,65,114,105,97,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,65,0,0,0,0,65,114,105,97,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,0,0,0,0,0,0,160,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,7,0,0,0,8,0,0,0,0,0,0,0,12,0,0,0,10,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,121,1,0,24,121,1,0,216,120,1,0,152,120,1,0,128,120,1,0,104,120,1,0,88,120,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,57,2,0,1,0,0,0,0,0,1,0,184,49,2,0,0,0,0,0,1,0,1,0,56,49,2,0,3,0,0,0,0,0,2,0,48,43,2,0,2,0,0,0,1,0,2,0,168,36,2,0,5,0,0,0,0,0,4,0,232,28,2,0,4,0,0,0,1,0,4,0,192,22,2,0,7,0,0,0,0,0,8,0,248,14,2,0,6,0,0,0,1,0,8,0,16,9,2,0,8,0,0,0,2,0,4,0,152,3,2,0,9,0,0,0,2,0,8,0,160,254,1,0,8,0,0,0,2,0,4,0,160,250,1,0,9,0,0,0,2,0,8,0,0,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,32,73,2,0,0,0,0,0,0,66,2,0,3,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,79,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,101,120,101,99,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,10,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,104,149,1,0,1,0,0,0,72,147,1,0,2,0,0,0,88,139,1,0,3,0,0,0,0,137,1,0,4,0,0,0,120,134,1,0,5,0,0,0,192,132,1,0,6,0,0,0,24,131,1,0,7,0,0,0,0,0,0,0,8,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,129,2,0,88,59,3,0,16,92,2,0,8,161,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,1,0,1,2,2,1,0,1,3,2,1,0,152,187,0,0,10,0,0,0,248,186,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,152,219,0,0,2,0,0,0,0,0,0,0,1,0,0,0,248,7,0,0,2,0,0,0,1,0,0,0,1,0,0,0,72,3,0,0,2,0,0,0,2,0,0,0,2,0,0,0,168,3,0,0,2,0,0,0,3,0,0,0,2,0,0,0,88,3,0,0,1,0,0,0,6,0,0,0,4,0,0,0,184,3,0,0,1,0,0,0,7,0,0,0,4,0,0,0,80,3,0,0,1,0,0,0,10,0,0,0,8,0,0,0,176,3,0,0,1,0,0,0,11,0,0,0,8,0,0,0,0,165,0,0,1,0,0,0,8,0,0,0,4,0,0,0,8,165,0,0,1,0,0,0,9,0,0,0,8,0,0,0,168,211,0,0,3,0,0,0,0,0,0,0,1,0,0,0,240,7,0,0,1,0,0,0,1,0,0,0,1,0,0,0,88,85,0,0,1,0,0,0,2,0,0,0,2,0,0,0,112,4,0,0,1,0,0,0,3,0,0,0,2,0,0,0,64,146,0,0,4,0,0,0,4,0,0,0,4,0,0,0,184,7,0,0,2,0,0,0,5,0,0,0,4,0,0,0,64,137,0,0,2,0,0,0,6,0,0,0,4,0,0,0,176,7,0,0,2,0,0,0,7,0,0,0,4,0,0,0,16,165,0,0,2,0,0,0,8,0,0,0,4,0,0,0,96,201,0,0,2,0,0,0,9,0,0,0,8,0,0,0,72,55,1,0,253,255,255,255,252,255,255,255,251,255,255,255,200,125,2,0,252,255,255,255,251,255,255,255,253,255,255,255,224,90,2,0,251,255,255,255,253,255,255,255,252,255,255,255,0,78,2,0,252,255,255,255,253,255,255,255,251,255,255,255,48,71,2,0,253,255,255,255,251,255,255,255,252,255,255,255,64,63,2,0,251,255,255,255,252,255,255,255,253,255,255,255,32,55,2,0,253,255,255,255,252,255,255,255,251,255,255,255,248,47,2,0,252,255,255,255,251,255,255,255,253,255,255,255,24,42,2,0,251,255,255,255,253,255,255,255,252,255,255,255,112,35,2,0,252,255,255,255,253,255,255,255,251,255,255,255,88,27,2,0,253,255,255,255,251,255,255,255,252,255,255,255,224,20,2,0,251,255,255,255,252,255,255,255,253,255,255,255,160,76,2,0,253,255,255,255,252,255,255,255,251,255,255,255,64,69,2,0,252,255,255,255,253,255,255,255,251,255,255,255,96,174,2,0,253,255,255,255,252,255,255,255,251,255,255,255,16,159,2,0,252,255,255,255,253,255,255,255,251,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,253,255,255,255,252,255,255,255,251,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,168,220,1,0,202,0,0,0,240,153,1,0,174,0,0,0,224,126,1,0,164,1,0,0,80,103,1,0,164,1,0,0,80,87,3,0,142,0,0,0,24,42,1,0,124,0,0,0,232,105,3,0,2,1,0,0,80,78,3,0,2,1,0,0,128,57,3,0,144,0,0,0,160,32,3,0,174,0,0,0,224,109,1,0,174,0,0,0,40,17,3,0,64,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,194,176,0,0,0,0,0,0,1,0,0,0,1,0,1,0,18,0,0,0,1,0,1,0,33,0,0,0,1,0,1,0,51,0,0,0,1,0,1,0,64,0,0,0,1,0,1,0,86,0,0,0,2,0,1,0,102,0,0,0,2,0,1,0,118,0,0,0,3,0,1,0,137,0,0,0,3,0,1,0,153,0,0,0,1,0,1,0,169,0,0,0,3,0,1,0,177,0,0,0,1,0,1,0,193,0,0,0,1,0,1,0,184,0,0,0,1,0,1,0,209,0,0,0,1,0,1,0,225,0,0,0,2,0,2,0,252,0,0,0,4,0,1,0,1,1,0,0,4,0,1,0,218,1,0,0,2,0,1,0,23,1,0,0,2,0,1,0,39,1,0,0,2,0,1,0,55,1,0,0,3,0,1,0,89,1,0,0,1,0,1,0,96,1,0,0,1,0,2,0,112,1,0,0,2,0,1,0,136,1,0,0,1,0,0,0,144,1,0,0,1,0,2,0,160,1,0,0,3,0,2,0,176,1,0,0,4,0,2,0,201,1,0,0,2,0,1,0,233,1,0,0,2,0,3,0,0,0,0,0,255,255,255,255,0,0,0,0,1,0,0,0,1,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,3,0,0,0,1,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,154,153,153,153,153,153,169,63,184,30,133,235,81,184,158,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,152,73,1,0,255,255,255,0,72,93,1,0,0,0,0,0,48,5,2,0,160,160,160,0,248,92,1,0,0,0,255,0,32,4,2,0,0,192,0,0,208,3,2,0,255,128,0,0,104,3,2,0,255,0,192,0,136,2,2,0,238,238,0,0,8,2,2,0,0,64,192,0,184,1,2,0,0,200,200,0,96,1,2,0,225,105,65,0,184,0,2,0,32,192,255,0,120,0,2,0,64,128,0,0,0,0,2,0,255,128,192,0,104,255,1,0,128,96,48,0,208,254,1,0,0,0,139,0,64,254,1,0,0,128,64,0,200,253,1,0,255,128,255,0,104,253,1,0,212,255,127,0,200,92,1,0,42,42,165,0,48,55,1,0,0,255,255,0,160,252,1,0,208,224,64,0,16,252,1,0,0,0,0,0,208,251,1,0,26,26,26,0,88,251,1,0,51,51,51,0,216,250,1,0,77,77,77,0,48,250,1,0,102,102,102,0,128,249,1,0,127,127,127,0,88,248,1,0,153,153,153,0,240,247,1,0,179,179,179,0,128,32,2,0,192,192,192,0,144,247,1,0,204,204,204,0,24,247,1,0,229,229,229,0,168,246,1,0,255,255,255,0,64,246,1,0,50,50,240,0,232,245,1,0,144,238,144,0,160,245,1,0,230,216,173,0,48,245,1,0,240,85,240,0,112,244,1,0,255,255,224,0,232,243,1,0,130,221,238,0,144,243,1,0,193,182,255,0,8,243,1,0,238,238,175,0,120,242,1,0,0,215,255,0,208,92,1,0,0,255,0,0,200,241,1,0,0,100,0,0,0,241,1,0,127,255,0,0,136,240,1,0,34,139,34,0,248,239,1,0,87,139,46,0,240,92,1,0,255,0,0,0,248,238,1,0,139,0,0,0,104,238,1,0,112,25,25,0,232,237,1,0,128,0,0,0,104,237,1,0,205,0,0,0,48,237,1,0,235,206,135,0,160,92,1,0,255,255,0,0,184,92,1,0,255,0,255,0,32,236,1,0,209,206,0,0,184,235,1,0,147,20,255,0,192,54,1,0,80,127,255,0,160,234,1,0,128,128,240,0,72,234,1,0,0,69,255,0,216,233,1,0,114,128,250,0,128,233,1,0,122,150,233,0,168,232,1,0,140,230,240,0,240,231,1,0,107,183,189,0,144,231,1,0,11,134,184,0,64,231,1,0,220,245,245,0,232,230,1,0,32,128,160,0,128,230,1,0,0,165,255,0,232,229,1,0,238,130,238,0,136,229,1,0,211,0,148,0,32,229,1,0,221,160,221,0,160,228,1,0,64,80,144,0,56,228,1,0,47,107,85,0,176,227,1,0,0,20,128,0,104,227,1,0,20,20,128,0,176,226,1,0,20,64,128,0,32,226,1,0,128,64,128,0,224,224,1,0,192,96,128,0,56,224,1,0,255,96,128,0,184,223,1,0,0,128,128,0,120,223,1,0,64,128,255,0,40,223,1,0,64,160,255,0,176,222,1,0,96,160,255,0,64,222,1,0,112,160,255,0,216,221,1,0,192,192,255,0,72,221,1,0,128,255,255,0,208,220,1,0,192,255,255,0,56,220,1,0,158,183,205,0,200,219,1,0,240,255,240,0,168,218,1,0,205,182,160,0,96,218,1,0,193,255,193,0,24,218,1,0,176,192,205,0,80,217,1,0,64,255,124,0,112,216,1,0,32,255,160,0,8,93,1,0,190,190,190,0,24,216,1,0,211,211,211,0,120,215,1,0,211,211,211,0,32,214,1,0,160,160,160,0,152,213,1,0,205,182,160,0,56,212,1,0,0,0,0,0,192,211,1,0,26,26,26,0,56,211,1,0,51,51,51,0,248,210,1,0,77,77,77,0,56,210,1,0,102,102,102,0,232,209,1,0,127,127,127,0,152,209,1,0,153,153,153,0,112,209,1,0,179,179,179,0,32,209,1,0,204,204,204,0,152,200,1,0,229,229,229,0,224,199,1,0,255,255,255,0,0,0,0,0,255,255,255,255,100,118,1,0,254,255,255,255,1,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,205,204,204,204,204,204,236,63,154,153,153,153,153,153,201,63,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,154,153,153,153,153,153,169,63,51,51,51,51,51,51,227,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,2,0,0,0,1,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,123,20,174,71,225,122,148,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,118,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
+.concat([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,88,161,1,0,120,157,1,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,28,0,0,0,0,0,0,72,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,65,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0,0,0,83,119,105,116,122,101,114,108,97,110,100,76,105,103,104,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,85,3,0,88,83,3,0,72,89,1,0,40,81,3,0,96,78,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,56,46,51,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,9,2,0,10,0,0,0,152,31,3,0,160,1,0,0,168,77,2,0,96,0,0,0,80,7,2,0,72,1,0,0,96,212,1,0,30,0,0,0,224,150,1,0,76,1,0,0,208,123,1,0,60,0,0,0,64,100,1,0,214,0,0,0,184,69,1,0,166,1,0,0,64,39,1,0,140,0,0,0,224,194,2,0,98,1,0,0,104,76,3,0,98,1,0,0,96,55,3,0,236,0,0,0,48,31,3,0,166,0,0,0,200,16,3,0,132,1,0,0,8,252,2,0,94,1,0,0,80,209,2,0,36,0,0,0,16,189,2,0,240,0,0,0,248,172,2,0,150,1,0,0,208,157,2,0,112,0,0,0,80,141,2,0,166,1,0,0,232,123,2,0,170,1,0,0,88,89,2,0,208,0,0,0,80,77,2,0,22,1,0,0,24,70,2,0,138,0,0,0,80,62,2,0,128,0,0,0,248,53,2,0,114,1,0,0,144,47,2,0,38,1,0,0,176,41,2,0,184,0,0,0,0,35,2,0,38,0,0,0,208,26,2,0,22,0,0,0,40,20,2,0,108,1,0,0,160,13,2,0,26,0,0,0,0,7,2,0,32,1,0,0,24,2,2,0,232,0,0,0,120,253,1,0,194,0,0,0,96,248,1,0,84,1,0,0,248,243,1,0,144,1,0,0,8,239,1,0,96,1,0,0,176,234,1,0,36,1,0,0,240,148,1,0,118,0,0,0,0,0,0,0,112,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,157,255,255,255,0,0,0,0,24,24,2,0,114,0,0,0,216,23,2,0,104,0,0,0,88,23,2,0,99,0,0,0,72,22,2,0,121,0,0,0,240,20,2,0,120,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,8,124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,232,13,2,0,144,7,2,0,72,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,176,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,254,255,255,255,0,0,0,0,254,255,255,255,254,255,255,255,254,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,112,213,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,32,66,111,108,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216,97,1,0,0,0,0,0,57,180,200,118,190,159,240,63,224,95,1,0,0,0,0,0,47,221,36,6,129,149,241,63,16,93,1,0,0,0,0,0,135,22,217,206,247,83,241,63,32,91,1,0,0,0,0,0,147,24,4,86,14,45,240,63,104,134,1,0,0,0,0,0,49,8,172,28,90,100,239,63,8,131,1,0,0,0,0,0,133,235,81,184,30,133,239,63,176,132,1,0,0,0,0,0,227,165,155,196,32,176,238,63,88,128,1,0,0,0,0,0,0,0,0,0,0,0,240,63,48,109,1,0,0,0,0,0,8,172,28,90,100,59,245,63,176,102,1,0,0,0,0,0,8,172,28,90,100,59,245,63,232,105,1,0,0,0,0,0,176,114,104,145,237,124,243,63,16,100,1,0,0,0,0,0,219,249,126,106,188,116,245,63,224,45,1,0,0,0,0,0,78,98,16,88,57,180,236,63,168,141,2,0,0,0,0,0,155,85,159,171,173,216,243,63,88,141,2,0,0,0,0,0,210,111,95,7,206,25,237,63,40,141,2,0,0,0,0,0,39,194,134,167,87,202,243,63,8,141,2,0,0,0,0,0,188,150,144,15,122,54,237,63,240,140,2,0,0,0,0,0,84,82,39,160,137,176,246,63,216,140,2,0,0,0,0,0,215,163,112,61,10,215,241,63,192,140,2,0,0,0,0,0,233,72,46,255,33,253,236,63,160,140,2,0,0,0,0,0,28,124,97,50,85,48,246,63,120,140,2,0,0,0,0,0,224,190,14,156,51,162,246,63,88,140,2,0,0,0,0,0,238,235,192,57,35,74,243,63,48,140,2,0,0,0,0,0,208,213,86,236,47,187,242,63,248,139,2,0,0,0,0,0,201,118,190,159,26,47,245,63,208,139,2,0,0,0,0,0,178,157,239,167,198,75,245,63,128,139,2,0,0,0,0,0,238,235,192,57,35,74,243,63,104,139,2,0,0,0,0,0,121,88,168,53,205,59,243,63,64,139,2,0,0,0,0,0,121,88,168,53,205,59,243,63,16,139,2,0,0,0,0,0,233,72,46,255,33,253,236,63,248,138,2,0,0,0,0,0,233,72,46,255,33,253,236,63,216,138,2,0,0,0,0,0,233,72,46,255,33,253,236,63,200,138,2,0,0,0,0,0,135,22,217,206,247,83,249,63,168,138,2,0,0,0,0,0,211,77,98,16,88,57,242,63,128,138,2,0,0,0,0,0,211,77,98,16,88,57,242,63,96,138,2,0,0,0,0,0,47,221,36,6,129,149,241,63,56,138,2,0,0,0,0,0,147,24,4,86,14,45,240,63,40,138,2,0,0,0,0,0,176,114,104,145,237,124,243,63,16,138,2,0,0,0,0,0,219,249,126,106,188,116,245,63,0,138,2,0,0,0,0,0,227,165,155,196,32,176,238,63,232,137,2,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,160,15,0,0,0,0,0,0,255,255,255,255,0,0,0,0,112,23,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,251,255,255,255,0,0,0,0,152,190,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,153,1,0,40,151,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,63,1,0,0,0,6,0,0,0,252,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,0,0,0,0,0,0,240,191,2,0,0,0,0,0,0,0,0,0,0,0,0,56,143,192,0,0,0,0,0,56,143,192,0,0,0,0,0,56,143,192,0,0,0,0,0,56,143,192,0,0,0,0,0,56,143,192,0,0,0,0,0,56,143,192,0,0,0,0,0,56,143,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,37,45,49,55,115,32,32,37,115,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,1,0,0,0,0,0,0,0,0,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,1,0,0,0,252,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,46,1,0,0,0,0,0,224,61,3,0,1,0,0,0,112,22,3,0,2,0,0,0,96,89,1,0,3,0,0,0,96,231,1,0,4,0,0,0,88,166,1,0,5,0,0,0,176,166,1,0,6,0,0,0,96,244,2,0,7,0,0,0,208,174,2,0,8,0,0,0,192,222,1,0,9,0,0,0,8,112,3,0,10,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,122,0,0,0,1,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,121,0,0,0,5,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,120,0,0,0,5,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,99,98,0,0,5,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,122,50,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,121,50,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,120,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,114,0,0,0,2,0,0,0,0,0,0,0,0,0,20,192,0,0,0,0,0,0,20,64,116,0,0,0,0,0,0,0,0,0,0,0,0,0,20,192,0,0,0,0,0,0,20,64,117,0,0,0,0,0,0,0,0,0,0,0,0,0,20,192,0,0,0,0,0,0,20,64,118,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
+.concat([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,36,192,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
+.concat([0,0,0,0,1,37,32,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,240,63,0,0,0,0,0,0,224,63,1,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,192,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,77,2,0,176,69,2,0,240,61,2,0,128,53,2,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,74,97,110,0,0,0,0,0,70,101,98,0,0,0,0,0,77,97,114,0,0,0,0,0,65,112,114,0,0,0,0,0,77,97,121,0,0,0,0,0,74,117,110,0,0,0,0,0,74,117,108,0,0,0,0,0,65,117,103,0,0,0,0,0,83,101,112,0,0,0,0,0,79,99,116,0,0,0,0,0,78,111,118,0,0,0,0,0,68,101,99,0,0,0,0,0,83,117,110,0,0,0,0,0,77,111,110,0,0,0,0,0,84,117,101,0,0,0,0,0,87,101,100,0,0,0,0,0,84,104,117,0,0,0,0,0,70,114,105,0,0,0,0,0,83,97,116,0,0,0,0,0,0,0,0,0,0,0,0,0,112,108,111,116,95,97,120,105,115,95,120,109,97,120,0,0,71,80,86,65,76,95,88,95,77,65,88,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,37,115,32,61,32,37,103,59,10,0,0,0,87,105,108,108,32,110,111,116,32,99,104,101,99,107,32,102,111,114,32,117,110,100,101,102,105,110,101,100,32,100,97,116,97,112,111,105,110,116,115,32,40,109,97,121,32,99,97,117,115,101,32,99,114,97,115,104,101,115,41,46,10,0,0,0,117,110,100,101,102,105,110,101,100,32,118,97,114,105,97,98,108,101,58,32,37,115,0,0,101,120,101,99,117,116,105,110,103,58,32,37,115,0,0,0,112,108,111,116,95,97,120,105,115,95,120,109,105,110,0,0,65,109,98,105,103,117,111,117,115,32,114,101,113,117,101,115,116,32,39,37,46,42,115,39,59,32,112,111,115,115,105,98,108,101,32,109,97,116,99,104,101,115,58,10,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,37,115,32,61,32,37,100,59,10,0,0,0,116,109,95,109,100,97,121,0,97,108,108,95,116,101,114,109,95,110,97,109,101,115,50,0,71,80,86,65,76,95,88,95,77,73,78,0,0,0,0,0,108,115,0,0,0,0,0,0,105,110,100,101,120,95,109,105,110,0,0,0,0,0,0,0,110,111,32,99,111,108,117,109,110,32,119,105,116,104,32,104,101,97,100,101,114,32,34,37,115,34,0,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,104,101,105,103,104,116,32,61,32,37,46,49,102,59,10,0,0,0,0,0,0,0,0,10,9,85,115,101,114,32,97,110,100,32,100,101,102,97,117,108,116,32,118,97,114,105,97,98,108,101,115,58,10,0,0,114,101,108,36,97,116,105,118,101,0,0,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,119,105,100,116,104,32,61,32,37,46,49,102,59,10,0,99,104,97,110,103,101,32,118,105,101,119,32,40,115,99,97,108,105,110,103,41,46,32,85,115,101,32,60,99,116,114,108,62,32,116,111,32,115,99,97,108,101,32,116,104,101,32,97,120,101,115,32,111,110,108,121,46,0,0,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,121,116,111,112,32,61,32,37,46,49,102,59,10,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,121,98,111,116,32,61,32,37,46,49,102,59,10,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,120,109,97,120,32,61,32,37,46,49,102,59,10,0,0,101,108,108,105,112,115,101,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,120,109,105,110,32,61,32,37,46,49,102,59,10,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,116,101,114,109,95,121,109,97,120,32,61,32,37,100,59,10,0,0,0,0,0,0,0,67,111,110,116,105,110,117,101,46,0,0,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,116,101,114,109,95,120,109,97,120,32,61,32,37,100,59,10,0,0,0,0,0,0,0,122,116,105,99,115,0,0,0,47,47,32,112,108,111,116,32,98,111,117,110,100,97,114,105,101,115,32,97,110,100,32,97,120,105,115,32,115,99,97,108,105,110,103,32,105,110,102,111,114,109,97,116,105,111,110,32,102,111,114,32,109,111,117,115,105,110,103,32,10,0,0,0,116,109,95,104,111,117,114,0,37,115,32,0,0,0,0,0,10,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,62,60,33,91,67,68,65,84,65,91,10,0,0,0,0,0,0,108,105,110,101,116,121,112,101,36,115,0,0,0,0,0,0,115,117,109,115,113,0,0,0,115,116,114,105,110,103,99,111,108,117,109,110,40,41,32,99,97,108,108,101,100,32,102,114,111,109,32,105,110,118,97,108,105,100,32,99,111,110,116,101,120,116,0,0,0,0,0,0,32,57,44,52,44,49,44,52,44,49,44,52,0,0,0,0,10,9,86,97,114,105,97,98,108,101,115,32,98,101,103,105,110,110,105,110,103,32,119,105,116,104,32,37,115,58,10,0,32,56,44,52,44,50,44,52,0,0,0,0,0,0,0,0,116,105,109,101,32,102,111,114,109,97,116,32,115,116,114,105,110,103,32,101,120,112,101,99,116,101,100,0,0,0,0,0,10,37,115,111,98,106,101,99,116,32,37,50,100,32,0,0,68,105,109,95,49,0,0,0,60,66,50,45,77,111,116,105,111,110,62,0,0,0,0,0,104,101,97,100,32,115,105,122,101,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,32,50,44,52,0,0,0,0,32,53,44,56,0,0,0,0,83,117,98,116,111,112,105,99,32,111,102,32,0,0,0,0,32,100,61,39,0,0,0,0,99,108,97,115,115,61,34,103,114,105,100,108,105,110,101,34,32,0,0,0,0,0,0,0,115,116,114,111,107,101,45,100,97,115,104,97,114,114,97,121,61,39,37,115,39,32,0,0,83,116,111,112,46,0,0,0,115,116,114,111,107,101,61,39,37,115,39,32,0,0,0,0,115,116,114,111,107,101,61,39,114,103,98,40,37,51,100,44,32,37,51,100,44,32,37,51,100,41,39,32,0,0,0,0,116,109,95,109,105,110,0,0,32,0,0,0,0,0,0,0,9,60,112,97,116,104,32,0,108,105,110,101,115,36,116,121,108,101,0,0,0,0,0,0,77,37,46,49,102,44,37,46,49,102,32,0,0,0,0,0,10,9,65,108,108,32,97,118,97,105,108,97,98,108,101,32,118,97,114,105,97,98,108,101,115,58,10,0,0,0,0,0,10,9,9,9,32,32,32,32,116,111,32,0,0,0,0,0,10,9,9,0,0,0,0,0,76,37,46,49,102,44,37,46,49,102,32,0,0,0,0,0,99,104,97,110,103,101,32,118,105,101,119,32,40,114,111,116,97,116,105,111,110,41,46,32,85,115,101,32,60,99,116,114,108,62,32,116,111,32,114,111,116,97,116,101,32,116,104,101,32,97,120,101,115,32,111,110,108,121,46,0,0,0,0,0,60,47,116,101,120,116,62,10,9,60,47,103,62,10,0,0,38,97,109,112,59,0,0,0,38,108,116,59,0,0,0,0,9,9,60,116,101,120,116,62,0,0,0,0,0,0,0,0,10,10,40,83,41,116,111,112,32,102,105,116,44,32,40,67,41,111,110,116,105,110,117,101,44,32,40,69,41,120,101,99,117,116,101,32,70,73,84,95,83,67,82,73,80,84,58,32,32,0,0,0,0,0,0,0,9,9,60,116,101,120,116,32,120,109,108,58,115,112,97,99,101,61,34,112,114,101,115,101,114,118,101,34,62,0,0,0,32,32,0,0,0,0,0,0,116,109,95,115,101,99,0,0,97,108,108,95,116,101,114,109,95,110,97,109,101,115,0,0,32,116,101,120,116,45,97,110,99,104,111,114,58,37,115,34,62,10,0,0,0,0,0,0,108,97,36,98,101,108,0,0,99,114,101,97,116,101,95,97,110,100,95,115,101,116,95,118,97,114,0,0,0,0,0,0,99,111,108,117,109,110,40,41,32,99,97,108,108,101,100,32,102,114,111,109,32,105,110,118,97,108,105,100,32,99,111,110,116,101,120,116,0,0,0,0,32,102,111,110,116,45,115,116,121,108,101,58,37,115,59,0,97,108,108,0,0,0,0,0,32,102,111,110,116,45,119,101,105,103,104,116,58,37,115,59,0,0,0,0,0,0,0,0,37,97,32,37,98,32,37,100,32,37,72,58,37,77,58,37,83,32,37,89,0,0,0,0,37,115,111,98,106,101,99,116,32,37,50,100,32,112,111,108,121,103,111,110,32,0,0,0,60,66,49,45,77,111,116,105,111,110,62,0,0,0,0,0,110,111,102,105,108,108,36,101,100,0,0,0,0,0,0,0,59,32,102,111,110,116,45,102,97,109,105,108,121,58,37,115,59,32,102,111,110,116,45,115,105,122,101,58,37,46,50,102,112,116,59,0,0,0,0,0,114,103,98,40,37,100,44,37,100,44,37,100,41,0,0,0,104,101,108,112,32,112,114,111,109,112,116,0,0,0,0,0,34,32,115,116,121,108,101,61,34,115,116,114,111,107,101,58,110,111,110,101,59,32,102,105,108,108,58,0,0,0,0,0,32,114,111,116,97,116,101,40,37,105,41,0,0,0,0,0,9,60,103,32,116,114,97,110,115,102,111,114,109,61,34,116,114,97,110,115,108,97,116,101,40,37,46,49,102,44,37,46,49,102,41,0,0,0,0,0,37,115,95,101,114,114,0,0,101,110,100,0,0,0,0,0,109,105,100,100,108,101,0,0,101,120,112,105,110,116,0,0,32,32,37,49,53,115,32,32,37,115,10,0,0,0,0,0,115,116,97,114,116,0,0,0,107,101,121,116,36,105,116,108,101,0,0,0,0,0,0,0,112,111,115,95,109,97,120,95,121,0,0,0,0,0,0,0,68,97,116,97,32,102,105,108,101,32,105,115,32,101,109,112,116,121,0,0,0,0,0,0,9,60,117,115,101,32,120,108,105,110,107,58,104,114,101,102,61,39,35,103,112,80,116,37,117,39,32,116,114,97,110,115,102,111,114,109,61,39,116,114,97,110,115,108,97,116,101,40,37,46,49,102,44,37,46,49,102,41,32,115,99,97,108,101,40,37,46,50,102,41,39,37,115,47,62,10,0,0,0,0,108,105,110,101,115,116,121,108,101,32,110,111,116,32,102,111,117,110,100,0,0,0,0,0,9,60,117,115,101,32,120,108,105,110,107,58,104,114,101,102,61,39,35,103,112,68,111,116,39,32,120,61,39,37,46,49,102,39,32,121,61,39,37,46,49,102,39,37,115,47,62,10,0,0,0,0,0,0,0,0,109,97,114,107,32,122,111,111,109,32,114,101,103,105,111,110,32,40,111,110,108,121,32,102,111,114,32,50,100,45,112,108,111,116,115,32,97,110,100,32,109,97,112,115,41,46,0,0,32,99,111,108,111,114,61,39,37,115,39,0,0,0,0,0,32,99,111,108,111,114,61,39,114,103,98,40,37,51,100,44,32,37,51,100,44,32,37,51,100,41,39,0,0,0,0,0,104,101,108,112,32,98,117,102,102,101,114,0,0,0,0,0,98,111,108,100,0,0,0,0,105,116,97,108,105,99,0,0,47,117,115,114,47,108,111,99,97,108,47,115,104,97,114,101,47,103,110,117,112,108,111,116,47,52,46,54,0,0,0,0,32,73,116,97,108,105,99,0,37,46,48,102,0,0,0,0,32,105,116,97,108,105,99,0,101,120,112,101,99,116,101,100,32,111,112,116,105,111,110,97,108,32,97,120,105,115,32,110,97,109,101,0,0,0,0,0,37,109,47,37,100,47,37,121,0,0,0,0,0,0,0,0,32,66,111,108,100,0,0,0,97,105,114,121,0,0,0,0,10,65,118,97,105,108,97,98,108,101,32,116,101,114,109,105,110,97,108,32,116,121,112,101,115,58,10,0,0,0,0,0,32,98,111,108,100,0,0,0,34,62,10,0,0,0,0,0,107,36,101,121,0,0,0,0,112,111,115,95,109,105,110,95,121,0,0,0,0,0,0,0,68,97,116,97,32,102,105,108,101,32,114,101,97,100,32,101,114,114,111,114,0,0,0,0,80,65,71,69,82,0,0,0,9,108,105,110,101,115,116,121,108,101,32,37,100,44,32,0,109,105,116,101,114,0,0,0,60,66,51,62,0,0,0,0,102,105,108,108,36,101,100,0,59,32,115,116,114,111,107,101,45,119,105,100,116,104,58,37,46,50,102,59,32,115,116,114,111,107,101,45,108,105,110,101,99,97,112,58,37,115,59,32,115,116,114,111,107,101,45,108,105,110,101,106,111,105,110,58,37,115,0,0,0,0,0,0,99,117,114,114,101,110,116,67,111,108,111,114,0,0,0,0,99,112,49,50,53,48,0,0,32,111,112,101,110,32,121,108,111,119,32,121,104,105,103,104,32,121,99,108,111,115,101,32,119,105,100,116,104,0,0,0,114,103,98,40,37,51,100,44,32,37,51,100,44,32,37,51,100,41,0,0,0,0,0,0,60,103,32,115,116,121,108,101,61,34,102,105,108,108,58,110,111,110,101,59,32,99,111,108,111,114,58,37,115,59,32,115,116,114,111,107,101,58,0,0,37,54,46,51,102,32,0,0,9,9,60,47,112,97,116,116,101,114,110,62,10,9,60,47,100,101,102,115,62,10,0,0,9,9,9,60,112,97,116,104,32,115,116,121,108,101,32,61,32,39,37,115,32,37,115,58,99,117,114,114,101,110,116,67,111,108,111,114,39,32,100,61,39,37,115,39,47,62,10,0,108,97,109,98,101,114,116,119,0,0,0,0,0,0,0,0,108,105,115,116,95,116,101,114,109,115,0,0,0,0,0,0,9,9,9,60,112,97,116,104,32,115,116,121,108,101,32,61,32,39,37,115,32,37,115,58,37,115,39,32,100,61,32,39,37,115,39,47,62,10,0,0,105,115,36,111,115,97,109,112,108,101,115,0,0,0,0,0,32,32,68,97,116,97,32,66,108,111,99,107,115,58,32,32,37,42,108,100,10,0,0,0,102,36,105,116,0,0,0,0,9,9,9,60,112,97,116,104,32,115,116,121,108,101,61,39,37,115,32,37,115,58,114,103,98,40,37,100,44,37,100,44,37,100,41,39,32,100,61,39,37,115,39,47,62,10,0,0,108,105,110,101,116,121,112,101,32,110,111,116,32,102,111,117,110,100,0,0,0,0,0,0,83,116,97,116,115,32,99,111,109,109,97,110,100,32,110,111,116,32,97,118,97,105,108,97,98,108,101,32,105,110,32,112,111,108,97,114,32,109,111,100,101,0,0,0,0,0,0,0,115,116,114,111,107,101,58,110,111,110,101,59,0,0,0,0,114,101,109,111,118,101,32,108,97,98,101,108,32,99,108,111,115,101,32,116,111,32,112,111,105,110,116,101,114,32,105,102,32,96,115,101,116,32,109,111,117,115,101,32,108,97,98,101,108,115,96,32,105,115,32,111,110,0,0,0,0,0,0,0,102,105,108,108,58,110,111,110,101,59,0,0,0,0,0,0,10,37,115,32,0,0,0,0,77,45,50,44,48,32,76,52,44,49,50,32,77,48,44,45,52,32,76,56,44,49,50,32,77,52,44,45,52,32,76,49,48,44,56,0,0,0,0,0,77,45,50,44,56,32,76,52,44,45,52,32,77,48,44,49,50,32,76,56,44,45,52,32,77,52,44,49,50,32,76,49,48,44,48,0,0,0,0,0,77,45,52,44,56,32,76,56,44,45,52,32,77,48,44,49,50,32,76,49,50,44,48,0,77,45,52,44,48,32,76,56,44,49,50,32,77,48,44,45,52,32,76,49,50,44,56,0,37,45,49,53,46,49,53,115,0,0,0,0,0,0,0,0,85,110,114,101,99,111,103,110,105,122,101,100,32,111,112,116,105,111,110,46,32,83,101,101,32,39,104,101,108,112,32,115,104,111,119,39,46,0,0,0,77,48,44,48,32,76,48,44,56,32,76,56,44,56,32,76,56,44,48,32,76,48,44,48,0,0,0,0,0,0,0,0,77,48,44,48,32,76,56,44,56,32,77,48,44,56,32,76,56,44,48,32,77,48,44,52,32,76,52,44,56,32,76,56,44,52,32,76,52,44,48,32,76,48,44,52,0,0,0,0,97,116,97,110,104,0,0,0,90,97,112,102,32,68,105,110,103,98,97,116,115,0,0,0,77,48,44,48,32,76,56,44,56,32,77,48,44,56,32,76,56,44,48,0,0,0,0,0,104,105,115,36,116,111,114,121,115,105,122,101,0,0,0,0,32,32,66,108,97,110,107,58,32,32,32,32,32,32,32,32,37,42,108,100,10,0,0,0,46,46,46,0,0,0,0,0,9,60,100,101,102,115,62,10,9,9,60,112,97,116,116,101,114,110,32,105,100,61,39,103,112,80,97,116,37,100,39,32,112,97,116,116,101,114,110,85,110,105,116,115,61,39,117,115,101,114,83,112,97,99,101,79,110,85,115,101,39,32,120,61,39,48,39,32,121,61,39,48,39,32,119,105,100,116,104,61,39,56,39,32,104,101,105,103,104,116,61,39,56,39,62,10,0,0,0,0,0,0,0,0,9,108,105,110,101,116,121,112,101,32,37,100,44,32,0,0,110,111,102,111,114,116,36,114,97,110,0,0,0,0,0,0,103,112,98,105,110,0,0,0,32,117,110,105,116,115,32,0,115,116,114,111,107,101,0,0,60,67,116,114,108,45,66,50,62,0,0,0,0,0,0,0,9,60,103,32,115,116,121,108,101,32,61,32,39,115,116,114,111,107,101,58,110,111,110,101,59,32,115,104,97,112,101,45,114,101,110,100,101,114,105,110,103,58,99,114,105,115,112,69,100,103,101,115,39,62,10,0,103,101,110,95,111,110,101,95,99,111,110,116,111,117,114,58,32,110,111,32,99,111,110,116,111,117,114,32,102,111,117,110,100,10,0,0,0,0,0,0,37,115,32,61,32,39,99,117,114,114,101,110,116,67,111,108,111,114,39,0,0,0,0,0,32,60,32,0,0,0,0,0,37,115,32,61,32,39,37,115,39,0,0,0,0,0,0,0,99,97,110,110,111,116,32,98,101,32,104,101,114,101,0,0,78,101,101,100,32,102,117,108,108,32,117,115,105,110,103,32,115,112,101,99,32,102,111,114,32,121,32,116,105,109,101,32,100,97,116,97,0,0,0,0,37,115,32,61,32,39,114,103,98,40,37,51,100,44,32,37,51,100,44,32,37,51,100,41,39,0,0,0,0,0,0,0,102,105,108,108,0,0,0,0,45,45,100,101,102,97,117,108,116,45,115,101,116,116,105,110,103,115,0,0,0,0,0,0,37,45,54,46,54,115,32,0,39,47,62,10,0,0,0,0,117,115,101,114,32,102,117,110,99,0,0,0,0,0,0,0,37,46,49,102,44,37,46,49,102,37,115,0,0,0,0,0,97,99,111,115,104,0,0,0,90,97,112,102,32,67,104,97,110,99,101,114,121,32,77,101,100,105,117,109,32,73,116,97,108,105,99,0,0,0,0,0,32,112,111,105,110,116,115,32,61,32,39,0,0,0,0,0,109,111,114,101,62,32,0,0,104,105,100,36,100,101,110,51,100,0,0,0,0,0,0,0,32,32,73,110,118,97,108,105,100,58,32,32,32,32,32,32,37,42,108,100,10,0,0,0,96,98,117,105,108,116,105,110,45,116,111,103,103,108,101,45,114,117,108,101,114,96,0,0,37,46,55,55,115,37,115,10,37,115,58,37,100,58,0,0,32,102,105,108,108,32,61,32,39,117,114,108,40,35,103,112,80,97,116,37,100,41,39,0,9,76,105,110,101,116,121,112,101,115,32,114,101,112,101,97,116,32,101,118,101,114,121,32,37,100,32,117,110,108,101,115,115,32,101,120,112,108,105,99,105,116,108,121,32,100,101,102,105,110,101,100,10,0,0,0,32,32,97,110,103,108,101,32,37,103,0,0,0,0,0,0,32,102,105,108,108,45,111,112,97,99,105,116,121,32,61,32,39,37,102,39,0,0,0,0,47,121,115,116,101,112,32,49,32,105,109,97,120,32,100,105,118,32,100,101,102,32,47,121,48,32,48,32,100,101,102,32,47,105,105,32,48,32,100,101,102,10,0,0,0,0,0,0,67,97,110,110,111,116,32,111,112,101,110,32,37,115,32,102,105,108,101,32,39,37,115,39,0,0,0,0,0,0,0,0,99,117,114,118,101,0,0,0,120,32,112,111,115,0,0,0,111,114,32,100,114,97,119,32,108,97,98,101,108,115,32,105,102,32,96,115,101,116,32,109,111,117,115,101,32,108,97,98,101,108,115,32,105,115,32,111,110,96,0,0,0,0,0,0,32,102,105,108,108,32,61,32,39,37,115,39,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,116,121,112,101,32,110,101,105,116,104,101,114,32,73,78,84,32,111,114,32,67,77,80,76,88,0,0,0,0,0,0,9,9,60,112,111,108,121,103,111,110,32,0,0,0,0,0,99,97,110,110,111,116,32,115,101,116,32,114,97,110,103,101,32,119,105,116,104,32,114,101,112,108,111,116,0,0,0,0,32,120,109,108,58,115,112,97,99,101,61,34,112,114,101,115,101,114,118,101,34,0,0,0,32,102,105,108,108,61,34,110,111,110,101,34,0,0,0,0,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0,32,100,121,61,34,37,46,50,102,112,116,34,0,0,0,0,32,100,120,61,34,37,46,50,102,101,109,34,0,0,0,0,97,115,105,110,104,0,0,0,83,121,109,98,111,108,0,0,32,102,111,110,116,45,115,116,121,108,101,61,34,37,115,34,32,0,0,0,0,0,0,0,103,36,114,105,100,0,0,0,32,32,79,117,116,32,111,102,32,114,97,110,103,101,58,32,37,42,108,100,10,0,0,0,82,101,119,105,110,100,105,110,103,32,102,100,32,37,100,10,0,0,0,0,0,0,0,0,32,102,111,110,116,45,119,101,105,103,104,116,61,34,37,115,34,32,0,0,0,0,0,0,40,37,115,37,103,44,32,37,115,37,103,44,32,37,115,37,103,41,0,0,0,0,0,0,122,0,0,0,0,0,0,0,44,32,37,115,37,103,0,0,110,111,114,109,97,108,0,0,73,110,32,108,111,103,32,109,111,100,101,32,114,114,97,110,103,101,32,109,117,115,116,32,110,111,116,32,105,110,99,108,117,100,101,32,48,0,0,0,97,110,110,111,116,97,116,101,32,116,104,101,32,103,114,97,112,104,32,117,115,105,110,103,32,96,109,111,117,115,101,102,111,114,109,97,116,96,32,40,115,101,101,32,107,101,121,115,32,39,49,39,44,32,39,50,39,41,0,0,0,0,0,0,32,102,111,110,116,45,115,105,122,101,61,34,37,46,49,102,112,116,34,0,0,0,0,0,32,115,116,121,108,101,61,34,102,111,110,116,45,102,97,109,105,108,121,58,37,115,34,32,0,0,0,0,0,0,0,0,60,116,115,112,97,110,0,0,60,116,115,112,97,110,32,100,120,61,34,45,37,46,49,102,101,109,34,32,100,121,61,34,37,46,49,102,112,116,34,62,0,0,0,0,0,0,0,0,79,110,108,121,32,117,110,100,101,102,105,110,101,100,32,100,97,116,97,112,111,105,110,116,115,32,97,114,101,32,111,109,105,116,116,101,100,32,102,114,111,109,32,116,104,101,32,115,117,114,102,97,99,101,46,10,0,0,0,0,0,0,0,0,37,115,60,47,116,115,112,97,110,62,0,0,0,0,0,0,10,10,99,111,114,114,101,108,97,116,105,111,110,32,109,97,116,114,105,120,32,111,102,32,116,104,101,32,102,105,116,32,112,97,114,97,109,101,116,101,114,115,58,10,10,0,0,0,60,47,103,62,10,0,0,0,10,72,101,108,112,32,116,111,112,105,99,115,32,97,118,97,105,108,97,98,108,101,58,10,0,0,0,0,0,0,0,0,62,10,0,0,0,0,0,0,105,110,118,110,111,114,109,0,80,97,108,97,116,105,110,111,32,66,111,108,100,32,73,116,97,108,105,99,0,0,0,0,111,110,99,108,105,99,107,61,34,103,110,117,112,108,111,116,95,115,118,103,46,116,111,103,103,108,101,86,105,115,105,98,105,108,105,116,121,40,101,118,116,44,39,37,115,95,112,108,111,116,95,37,100,37,115,39,41,34,0,0,0,0,0,0,102,117,36,110,99,116,105,111,110,115,0,0,0,0,0,0,32,32,82,101,99,111,114,100,115,58,32,32,32,32,32,32,37,42,108,100,10,0,0,0,83,107,105,112,112,105,110,103,32,117,110,114,101,97,100,97,98,108,101,32,102,105,108,101,32,34,37,115,34,0,0,0,9,60,103,32,118,105,115,105,98,105,108,105,116,121,61,34,118,105,115,105,98,108,101,34,32,0,0,0,0,0,0,0,40,99,104,97,114,97,99,116,101,114,32,117,110,105,116,115,41,32,0,0,0,0,0,0,37,115,111,98,106,101,99,116,32,37,50,100,32,101,108,108,105,112,115,101,32,0,0,0,62,60,116,105,116,108,101,62,37,115,95,112,108,111,116,95,37,100,37,115,60,47,116,105,116,108,101,62,10,0,0,0,84,104,105,115,32,112,108,111,116,32,115,116,121,108,101,32,100,111,101,115,32,110,111,116,32,119,111,114,107,32,119,105,116,104,32,54,32,99,111,108,115,46,32,83,101,116,116,105,110,103,32,116,111,32,120,121,101,114,114,111,114,98,97,114,115,0,0,0,0,0,0,0,60,66,50,62,0,0,0,0,9,60,103,32,105,100,61,34,37,115,95,112,108,111,116,95,37,100,37,115,34,32,0,0,103,110,117,112,108,111,116,0,105,110,118,97,108,105,100,32,99,111,109,109,97,110,100,0,9,60,47,103,62,10,0,0,32,104,48,46,48,49,39,47,62,0,0,0,0,0,0,0,90,32,0,0,0,0,0,0,43,47,45,0,0,0,0,0,100,36,97,115,104,101,100,0,91,0,0,0,0,0,0,0,105,110,118,101,114,102,0,0,80,97,108,97,116,105,110,111,32,66,111,108,100,0,0,0,108,36,97,110,100,115,99,97,112,101,0,0,0,0,0,0,102,117,36,110,99,116,105,111,110,0,0,0,0,0,0,0,42,32,70,73,76,69,58,32,10,0,0,0,0,0,0,0,114,98,0,0,0,0,0,0,112,36,111,114,116,114,97,105,116,0,0,0,0,0,0,0,40,115,99,114,101,101,110,32,117,110,105,116,115,41,32,0,32,97,114,99,32,91,37,103,58,37,103,93,32,0,0,0,32,37,115,32,102,111,110,116,32,34,37,115,44,37,117,34,0,0,0,0,0,0,0,0,85,110,114,101,99,111,103,110,105,122,101,100,32,53,32,99,111,108,117,109,110,32,112,108,111,116,32,115,116,121,108,101,59,32,114,101,115,101,116,116,105,110,103,32,116,111,32,98,111,120,101,114,114,111,114,98,97,114,115,0,0,0,0,0,32,108,105,110,101,119,105,100,116,104,32,37,102,0,0,0,112,114,105,110,116,32,99,111,111,114,100,105,110,97,116,101,115,32,116,111,32,99,108,105,112,98,111,97,114,100,32,117,115,105,110,103,32,96,99,108,105,112,98,111,97,114,100,102,111,114,109,97,116,96,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,40,115,101,101,32,107,101,121,115,32,39,51,39,44,32,39,52,39,41,0,0,97,114,114,111,119,115,36,116,121,108,101,0,0,0,0,0,69,68,70,95,66,105,110,97,114,121,70,105,108,101,80,111,115,105,116,105,111,110,0,0,32,109,111,110,111,99,104,114,111,109,101,0,0,0,0,0,78,101,119,32,112,97,114,97,109,101,116,101,114,32,102,105,108,101,110,97,109,101,32,101,120,112,101,99,116,101,100,0,108,97,110,100,115,99,97,112,101,0,0,0,0,0,0,0,112,111,114,116,114,97,105,116,0,0,0,0,0,0,0,0,37,115,32,91,37,117,44,37,117,93,0,0,0,0,0,0,37,45,49,53,46,49,53,115,32,61,32,37,45,49,53,103,32,32,37,45,51,46,51,115,32,37,45,49,50,46,52,103,32,40,37,46,52,103,37,37,41,10,0,0,0,0,0,0,69,120,112,101,99,116,105,110,103,32,102,111,110,116,32,110,97,109,101,32,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,93,39,0,0,0,110,111,114,109,0,0,0,0,80,97,108,97,116,105,110,111,32,73,116,97,108,105,99,0,93,0,0,0,0,0,0,0,102,111,36,114,109,97,116,0,111,117,116,111,102,114,97,110,103,101,0,0,0,0,0,0,34,37,115,34,32,105,115,32,97,32,100,105,114,101,99,116,111,114,121,0,0,0,0,0,39,44,39,32,101,120,112,101,99,116,101,100,0,0,0,0,40,103,114,97,112,104,32,117,110,105,116,115,41,32,0,0,110,111,101,113,117,97,108,36,95,97,120,101,115,0,0,0,37,115,37,103,0,0,0,0,110,111,46,32,119,105,110,100,111,119,115,58,32,91,104,111,114,105,122,111,110,116,97,108,44,118,101,114,116,105,99,97,108,93,32,101,120,112,101,99,116,101,100,0,0,0,0,0,84,104,105,115,32,112,108,111,116,32,115,116,121,108,101,32,100,111,101,115,32,110,111,116,32,119,111,114,107,32,119,105,116,104,32,52,32,99,111,108,115,46,32,83,101,116,116,105,110,103,32,116,111,32,121,101,114,114,111,114,98,97,114,115,0,0,0,0,0,0,0,0,108,105,110,101,119,105,100,116,104,58,32,111,117,116,32,111,102,32,114,97,110,103,101,0,50,120,60,66,49,62,0,0,97,114,114,111,119,115,116,121,108,101,32,37,100,32,110,111,116,32,102,111,117,110,100,0,108,105,110,101,119,105,100,116,104,58,32,119,105,100,116,104,32,105,115,32,110,111,116,32,115,112,101,99,105,102,105,101,100,46,0,0,0,0,0,0,80,97,114,97,109,101,116,101,114,32,102,105,108,101,110,97,109,101,32,101,120,112,101,99,116,101,100,0,0,0,0,0,37,37,84,71,73,70,32,50,46,49,53,45,112,55,10,115,116,97,116,101,40,37,100,44,51,48,44,37,117,44,48,44,48,44,37,117,44,49,54,44,49,44,57,44,49,44,49,44,48,44,48,44,48,44,48,44,49,44,48,44,39,37,115,39,44,48,44,37,117,44,48,44,48,44,49,44,49,48,44,48,44,48,44,49,44,49,44,48,44,49,54,44,48,44,48,44,49,44,49,44,49,41,46,10,37,37,10,37,37,32,64,40,35,41,67,114,101,97,116,111,114,58,32,103,110,117,112,108,111,116,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,10,37,37,32,37,37,87,37,37,10,37,37,10,112,97,103,101,40,49,44,34,34,41,46,10,0,0,0,0,119,97,114,110,105,110,103,58,32,117,115,105,110,103,32,115,116,97,110,100,97,114,100,32,109,117,108,116,105,112,108,111,116,10,0,0,0,0,0,0,101,114,114,111,114,58,32,110,117,109,98,101,114,32,111,102,32,112,108,111,116,115,32,62,32,112,108,111,116,115,32,112,101,114,32,112,97,103,101,10,0,0,0,0,0,0,0,0,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,32,32,32,32,32,32,32,32,32,32,32,32,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,10,10,0,99,111,114,97,108,0,0,0,72,111,116,80,105,110,107,0,99,101,105,108,0,0,0,0,80,97,108,97,116,105,110,111,32,82,111,109,97,110,0,0,68,97,114,107,83,101,97,71,114,101,101,110,0,0,0,0,102,111,110,116,36,112,97,116,104,0,0,0,0,0,0,0,98,108,111,99,107,115,0,0,99,97,110,110,111,116,32,99,114,101,97,116,101,32,112,105,112,101,32,102,111,114,32,100,97,116,97,0,0,0,0,0,121,101,108,108,111,119,0,0,40,115,101,99,111,110,100,32,97,120,101,115,41,32,0,0,120,121,122,0,0,0,0,0,37,115,111,98,106,101,99,116,32,37,50,100,32,99,105,114,99,108,101,32,0,0,0,0,116,101,120,116,40,39,37,115,39,44,37,117,44,37,117,44,39,37,115,39,44,48,44,37,117,44,49,44,37,117,44,37,117,44,49,44,53,53,44,49,49,57,44,37,117,44,48,44,49,53,44,52,44,48,44,48,44,48,44,48,44,91,10,9,34,37,115,34,93,41,46,10,0,0,0,0,0,0,0,0,84,104,105,115,32,112,108,111,116,32,115,116,121,108,101,32,100,111,101,115,32,110,111,116,32,119,111,114,107,32,119,105,116,104,32,51,32,99,111,108,115,46,32,83,101,116,116,105,110,103,32,116,111,32,121,101,114,114,111,114,98,97,114,115,0,0,0,0,0,0,0,0,117,110,114,101,99,111,103,110,105,122,101,100,32,99,111,108,111,114,32,110,97,109,101,32,97,110,100,32,110,111,116,32,97,32,115,116,114,105,110,103,32,34,35,65,65,82,82,71,71,66,66,34,0,0,0,0,112,111,108,121,103,111,110,40,39,37,115,39,44,55,44,91,10,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,49,44,49,44,49,44,48,44,37,117,44,48,44,48,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,53,55,54,48,44,49,55,50,56,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,111,110,108,121,32,107,101,121,119,111,114,100,115,32,97,114,101,32,39,116,101,114,109,105,110,97,108,39,32,97,110,100,32,39,112,97,108,101,116,116,101,39,0,0,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,49,49,53,50,48,44,49,55,50,56,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,49,49,53,50,48,44,49,49,53,50,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,115,121,110,116,97,120,32,101,114,114,111,114,32,105,110,32,112,97,114,97,109,101,116,101,114,32,102,105,108,101,32,37,115,0,0,0,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,49,55,50,56,48,44,49,55,50,56,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,70,105,110,97,108,32,115,101,116,32,111,102,32,112,97,114,97,109,101,116,101,114,115,32,32,32,32,32,32,32,32,32,32,32,32,65,115,121,109,112,116,111,116,105,99,32,83,116,97,110,100,97,114,100,32,69,114,114,111,114,10,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,49,55,50,56,48,44,49,49,53,50,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,49,55,50,56,48,44,53,55,54,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,102,108,111,111,114,0,0,0,78,101,119,32,67,101,110,116,117,114,121,32,83,99,104,111,111,108,98,111,111,107,32,66,111,108,100,32,73,116,97,108,105,99,0,0,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,48,44,49,55,50,56,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,0,0,102,105,116,0,0,0,0,0,98,108,97,110,107,115,0,0,99,97,110,110,111,116,32,111,112,101,110,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,32,102,111,114,32,114,101,97,100,105,110,103,32,100,97,116,97,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,53,55,54,48,44,49,49,53,50,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,40,102,105,114,115,116,32,97,120,101,115,41,32,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,49,49,53,50,48,44,53,55,54,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,84,104,105,115,32,112,108,111,116,32,115,116,121,108,101,32,100,111,101,115,32,110,111,116,32,119,111,114,107,32,119,105,116,104,32,49,32,111,114,32,50,32,99,111,108,115,46,32,83,101,116,116,105,110,103,32,116,111,32,112,111,105,110,116,115,0,0,0,0,0,0,0,98,105,110,100,95,97,112,112,101,110,100,45,62,110,101,119,0,0,0,0,0,0,0,0,35,37,108,120,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,48,44,49,49,53,50,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,53,55,54,48,44,53,55,54,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,115,99,114,101,101,110,100,117,109,112,32,110,111,116,32,105,109,112,108,101,109,101,110,116,101,100,10,0,0,0,0,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,48,44,53,55,54,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,97,114,99,40,39,37,115,39,44,49,44,49,44,49,44,48,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,48,44,37,46,49,102,44,37,46,49,102,44,52,52,56,48,44,50,53,54,48,44,37,117,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,9,103,110,117,112,108,111,116,114,99,32,105,115,32,114,101,97,100,32,102,114,111,109,32,37,115,10,0,0,0,0,0,112,111,108,121,103,111,110,40,39,37,115,39,44,54,44,91,10,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,49,44,49,44,49,44,48,44,37,117])
+.concat([44,48,44,48,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,0,67,97,108,99,117,108,97,116,105,111,110,32,101,114,114,111,114,58,32,110,111,110,45,112,111,115,105,116,105,118,101,32,100,105,97,103,111,110,97,108,32,101,108,101,109,101,110,116,32,105,110,32,99,111,118,97,114,46,32,109,97,116,114,105,120,0,0,0,0,0,0,0,70,111,114,109,97,116,32,99,104,97,114,97,99,116,101,114,32,109,105,115,109,97,116,99,104,58,32,37,37,76,32,105,115,32,111,110,108,121,32,118,97,108,105,100,32,119,105,116,104,32,37,37,108,0,0,0,112,111,108,121,103,111,110,40,39,37,115,39,44,54,44,91,10,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,49,44,49,44,48,44,37,117,44,48,44,48,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,0,37,100,47,37,109,47,37,121,44,37,72,58,37,77,0,0,37,42,100,0,0,0,0,0,112,111,108,121,103,111,110,40,39,37,115,39,44,53,44,91,10,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,49,44,49,44,49,44,48,44,37,117,44,48,44,48,44,48,44,48,44,48,44,91,10,93,41,46,10,0,114,97,110,100,0,0,0,0,112,111,108,121,103,111,110,40,39,37,115,39,44,53,44,91,10,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,49,44,49,44,48,44,37,117,44,48,44,48,44,48,44,48,44,48,44,91,10,93,41,46,10,0,78,101,119,32,67,101,110,116,117,114,121,32,83,99,104,111,111,108,98,111,111,107,32,66,111,108,100,0,0,0,0,0,100,101,99,36,105,109,97,108,115,105,103,110,0,0,0,0,105,110,118,97,108,105,100,0,112,111,108,121,103,111,110,40,39,37,115,39,44,52,44,91,10,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,49,44,49,44,49,44,48,44,37,117,44,48,44,48,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,9,32,32,102,111,110,116,32,34,37,115,34,10,0,0,0,101,113,117,97,108,36,95,97,120,101,115,0,0,0,0,0,102,114,111,109,32,0,0,0,112,111,108,121,103,111,110,40,39,37,115,39,44,52,44,91,10,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,49,44,49,44,48,44,37,117,44,48,44,48,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,84,111,111,32,109,97,110,121,32,99,111,108,117,109,110,115,32,105,110,32,117,115,105,110,103,32,115,112,101,99,105,102,105,99,97,116,105,111,110,0,68,101,108,101,116,101,0,0,111,118,97,108,40,39,37,115,39,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,49,44,49,44,49,44,37,117,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,0,0,111,118,97,108,40,39,37,115,39,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,50,44,49,44,49,44,37,117,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,0,0,67,97,110,110,111,116,32,111,112,101,110,32,115,97,118,101,32,102,105,108,101,0,0,0,99,112,57,53,48,0,0,0,98,111,120,40,39,37,115,39,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,49,44,49,44,49,44,37,117,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,32,111,112,101,110,32,121,108,111,119,32,121,104,105,103,104,32,121,99,108,111,115,101,0,98,111,120,40,39,37,115,39,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,50,44,49,44,49,44,37,117,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,99,97,108,108,110,0,0,0,112,111,108,121,40,39,37,115,39,44,50,44,91,10,9,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,50,44,49,44,37,117,44,48,44,48,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,0,70,73,84,95,87,83,83,82,0,0,0,0,0,0,0,0,112,111,108,121,40,39,37,115,39,44,50,44,91,10,9,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,50,44,49,44,37,117,44,48,44,48,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,10,0,0,0,0,0,0,0,0,112,111,108,121,40,39,37,115,39,44,50,44,91,10,9,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,50,44,49,44,37,117,44,48,44,48,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,44,10,0,0,0,0,0,0,0,67,97,110,110,111,116,32,115,112,108,111,116,32,105,110,32,112,111,108,97,114,32,99,111,111,114,100,105,110,97,116,101,32,115,121,115,116,101,109,46,0,0,0,0,0,0,0,0,105,103,97,109,109,97,0,0,103,114,111,117,112,40,91,10,0,0,0,0,0,0,0,0,78,101,119,32,67,101,110,116,117,114,121,32,83,99,104,111,111,108,98,111,111,107,32,73,116,97,108,105,99,0,0,0,101,110,99,36,111,100,105,110,103,0,0,0,0,0,0,0,114,101,99,111,114,100,115,0,101,120,36,105,116,0,0,0,99,97,110,110,111,116,32,112,108,111,116,32,102,114,111,109,32,115,116,100,105,110,47,115,116,100,111,117,116,47,115,116,100,101,114,114,0,0,0,0,103,114,111,117,112,40,91,10,112,111,108,121,40,39,37,115,39,44,50,44,91,10,9,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,50,44,49,44,37,117,44,48,44,48,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,112,111,108,121,40,39,37,115,39,44,50,44,91,10,9,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,50,44,49,44,37,117,44,48,44,48,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,83,116,97,116,115,32,99,111,109,109,97,110,100,32,110,111,116,32,97,118,97,105,108,97,98,108,101,32,105,110,32,116,105,109,101,100,97,116,97,32,109,111,100,101,0,0,0,0,32,115,105,122,101,32,0,0,93,44,10,37,117,44,48,44,91,10,93,41,46,10,0,0,78,111,116,32,101,110,111,117,103,104,32,99,111,108,117,109,110,115,32,105,110,32,117,115,105,110,103,32,115,112,101,99,105,102,105,99,97,116,105,111,110,0,0,0,0,0,0,0,69,115,99,97,112,101,0,0,99,111,108,111,114,115,112,101,99,32,111,112,116,105,111,110,32,110,111,116,32,114,101,99,111,103,110,105,122,101,100,0,103,114,111,117,112,40,91,10,112,111,108,121,40,39,37,115,39,44,50,44,91,10,9,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,50,44,49,44,37,117,44,48,44,48,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,44,10,112,111,108,121,40,39,37,115,39,44,50,44,91,10,9,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,50,44,49,44,37,117,44,48,44,48,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,10,0,0,0,0,0,0,0,112,111,108,121,40,39,37,115,39,44,50,44,91,10,9,37,46,49,102,44,37,46,49,102,44,37,46,49,102,44,37,46,49,102,93,44,48,44,49,44,49,44,37,117,44,48,44,48,44,48,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,0,0,0,112,111,108,121,40,39,37,115,39,44,37,100,44,91,10,9,37,117,44,37,117,44,37,117,44,37,117,93,44,49,44,37,117,44,49,44,37,117,44,48,44,48,44,37,117,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,0,0,37,117,44,37,117,93,44,48,44,37,117,44,49,44,37,117,44,48,44,48,44,37,117,44,48,44,56,44,51,44,48,44,91,10,93,41,46,10,0,0,112,111,108,121,40,39,37,115,39,44,37,100,44,91,10,9,0,0,0,0,0,0,0,0,91,110,111,110,101,93,0,0,70,73,84,95,83,84,68,70,73,84,0,0,0,0,0,0,35,37,46,50,120,37,46,50,120,37,46,50,120,0,0,0,93,44,37,100,44,49,44,48,44,48,44,37,117,44,48,44,48,44,48,44,48,44,91,10,93,41,46,10,0,0,0,0,118,111,105,103,116,0,0,0,37,117,44,37,117,0,0,0,78,101,119,32,67,101,110,116,117,114,121,32,83,99,104,111,111,108,98,111,111,107,32,82,111,109,97,110,0,0,0,0,100,117,36,109,109,121,0,0,37,115,9,37,108,100,10,0,105,110,118,97,108,105,100,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,32,105,110,116,101,103,101,114,0,10,9,0,0,0,0,0,0,9,32,0,0,0,0,0,0,116,36,105,109,101,0,0,0,99,101,110,116,101,114,32,0,37,117,44,37,117,44,0,0,82,101,116,117,114,110,0,0,112,97,108,101,116,116,101,32,102,114,97,99,116,105,111,110,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,0,112,111,108,121,103,111,110,40,39,37,115,39,44,37,100,44,91,10,9,0,0,0,0,0,99,111,110,116,111,117,114,32,116,114,105,100,105,97,103,32,109,0,0,0,0,0,0,0,119,104,105,116,101,0,0,0,110,111,32,112,114,101,118,105,111,117,115,32,112,108,111,116,0,0,0,0,0,0,0,0,105,36,110,116,101,114,97,99,116,105,118,101,0,0,0,0,98,108,45,105,110,116,101,114,112,32,98,101,116,119,101,101,110,32,115,99,97,110,0,0,78,101,101,100,32,102,117,108,108,32,117,115,105,110,103,32,115,112,101,99,32,102,111,114,32,120,32,116,105,109,101,32,100,97,116,97,0,0,0,0,85,110,100,101,102,105,110,101,100,32,118,97,108,117,101,32,116,104,105,114,100,32,99,111,108,111,114,32,100,117,114,105,110,103,32,102,117,110,99,116,105,111,110,32,101,118,97,108,117,97,116,105,111,110,0,0,112,36,101,114,108,116,107,0,108,97,98,101,108,112,111,105,110,116,32,108,97,98,101,108,0,0,0,0,0,0,0,0,105,110,116,101,114,97,99,116,105,118,101,0,0,0,0,0,45,100,0,0,0,0,0,0,70,73,84,95,78,68,70,0,112,101,114,108,116,107,0,0,37,115,32,37,115,0,0,0,105,98,101,116,97,0,0,0,105,102,32,123,91,115,116,114,105,110,103,32,108,101,110,103,116,104,32,36,121,50,109,93,62,48,125,32,123,112,117,116,115,32,34,32,36,121,50,109,34,125,32,101,108,115,101,32,123,112,117,116,115,32,34,32,91,101,120,112,114,32,48,46,53,42,40,36,121,50,115,43,36,121,50,101,41,93,34,125,10,0,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,32,78,97,114,114,111,119,32,66,111,108,100,32,79,98,108,105,113,117,101,0,0,0,100,103,36,114,105,100,51,100,0,0,0,0,0,0,0,0,32,32,77,101,100,105,97,110,58,32,32,32,37,115,32,10,0,0,0,0,0,0,0,0,116,117,114,110,105,110,103,32,114,117,108,101,114,32,111,102,102,46,10,0,0,0,0,0,100,97,116,97,102,105,108,101,32,108,105,110,101,32,98,117,102,102,101,114,0,0,0,0,105,102,32,123,91,115,116,114,105,110,103,32,108,101,110,103,116,104,32,36,120,50,109,93,62,48,125,32,123,112,117,116,115,32,45,110,111,110,101,119,108,105,110,101,32,34,32,36,120,50,109,34,125,32,101,108,115,101,32,123,112,117,116,115,32,45,110,111,110,101,119,108,105,110,101,32,34,32,91,101,120,112,114,32,48,46,53,42,40,36,120,50,115,43,36,120,50,101,41,93,34,125,10,0,41,10,0,0,0,0,0,0,110,111,119,114,105,36,116,101,98,97,99,107,0,0,0,0,105,102,32,123,91,115,116,114,105,110,103,32,108,101,110,103,116,104,32,36,121,49,109,93,62,48,125,32,123,112,117,116,115,32,45,110,111,110,101,119,108,105,110,101,32,34,32,36,121,49,109,34,125,32,101,108,115,101,32,123,112,117,116,115,32,45,110,111,110,101,119,108,105,110,101,32,34,32,91,101,120,112,114,32,48,46,53,42,40,36,121,49,115,43,36,121,49,101,41,93,34,125,10,0,115,101,116,32,0,0,0,0,73,110,118,97,108,105,100,32,115,117,98,115,116,105,116,117,116,105,111,110,32,36,37,99,0,0,0,0,0,0,0,0,37,105,32,37,105,32,116,114,97,110,115,108,97,116,101,32,37,105,32,37,105,32,115,99,97,108,101,32,48,32,115,101,116,108,105,110,101,119,105,100,116,104,10,0,0,0,0,0,115,112,108,105,110,101,32,104,101,108,112,32,118,101,99,116,111,114,0,0,0,0,0,0,75,80,95,69,110,116,101,114,0,0,0,0,0,0,0,0,101,120,112,101,99,116,101,100,32,112,97,108,101,116,116,101,32,102,114,97,99,116,105,111,110,0,0,0,0,0,0,0,105,102,32,123,91,115,116,114,105,110,103,32,108,101,110,103,116,104,32,36,120,49,109,93,62,48,125,32,123,112,117,116,115,32,45,110,111,110,101,119,108,105,110,101,32,34,32,36,120,49,109,34,125,32,101,108,115,101,32,123,112,117,116,115,32,45,110,111,110,101,119,108,105,110,101,32,34,32,91,101,120,112,114,32,48,46,53,42,40,36,120,49,115,43,36,120,49,101,41,93,34,125,10,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,102,95,115,117,109,32,99,111,117,108,100,32,110,111,116,32,97,99,99,101,115,115,32,115,117,109,109,97,116,105,111,110,32,99,111,101,102,102,105,99,105,101,110,116,32,102,117,110,99,116,105,111,110,0,0,0,117,115,101,114,95,103,110,117,112,108,111,116,95,99,111,111,114,100,105,110,97,116,101,115,32,36,119,105,110,32,36,105,100,32,36,120,49,115,32,36,121,49,115,32,36,120,50,115,32,36,121,50,115,32,36,120,49,101,32,36,121,49,101,32,36,120,50,101,32,36,121,50,101,32,36,120,49,109,32,36,121,49,109,32,36,120,50,109,32,36,121,50,109,10,0,0,73,110,116,101,114,110,97,108,32,101,114,114,111,114,32,45,32,114,101,102,114,101,115,104,32,111,102,32,117,110,107,110,111,119,110,32,112,108,111,116,32,116,121,112,101,0,0,0,115,101,116,32,105,100,32,91,36,119,105,110,32,102,105,110,100,32,119,105,116,104,116,97,103,32,99,117,114,114,101,110,116,93,10,0,0,0,0,0,85,110,100,101,102,105,110,101,100,32,118,97,108,117,101,32,115,101,99,111,110,100,32,99,111,108,111,114,32,100,117,114,105,110,103,32,102,117,110,99,116,105,111,110,32,101,118,97,108,117,97,116,105,111,110,0,105,102,32,123,40,91,108,108,101,110,103,116,104,32,91,105,110,102,111,32,99,111,109,109,97,110,100,115,32,117,115,101,114,95,103,110,117,112,108,111,116,95,99,111,111,114,100,105,110,97,116,101,115,93,93,41,125,32,123,10,0,0,0,0,112,114,111,99,32,103,110,117,112,108,111,116,95,120,121,32,123,119,105,110,32,120,49,115,32,121,49,115,32,120,50,115,32,121,50,115,32,120,49,101,32,121,49,101,32,120,50,101,32,121,50,101,32,120,49,109,32,121,49,109,32,120,50,109,32,121,50,109,125,32,123,10,0,0,0,0,0,0,0,0,118,97,114,105,97,110,99,101,32,111,102,32,114,101,115,105,100,117,97,108,115,32,40,114,101,100,117,99,101,100,32,99,104,105,115,113,117,97,114,101,41,32,61,32,87,83,83,82,47,110,100,102,32,32,32,58,32,37,103,10,10,0,0,0,114,101,116,117,114,110,32,123,37,102,32,37,102,32,37,102,32,37,102,32,37,102,32,37,102,32,37,102,32,37,102,125,10,0,0,0,0,0,0,0,112,114,111,99,32,103,110,117,112,108,111,116,95,97,120,105,115,114,97,110,103,101,115,32,123,125,32,123,10,0,0,0,108,103,97,109,109,97,0,0,114,101,116,117,114,110,32,123,37,100,32,37,100,32,37,100,32,37,100,125,10,0,0,0,72,101,108,118,101,116,105,99,97,32,78,97,114,114,111,119,32,66,111,108,100,0,0,0,100,97,116,97,36,102,105,108,101,0,0,0,0,0,0,0,32,32,81,117,97,114,116,105,108,101,58,32,37,115,32,10,0,0,0,0,0,0,0,0,112,114,111,99,32,103,110,117,112,108,111,116,95,112,108,111,116,97,114,101,97,32,123,125,32,123,10,0,0,0,0,0,100,117,112,108,105,99,97,116,101,100,32,111,114,32,99,111,110,116,114,97,100,105,99,116,105,110,103,32,97,114,103,117,109,101,110,116,115,32,105,110,32,100,97,116,97,102,105,108,101,32,111,112,116,105,111,110,115,0,0,0,0,0,0,0,44,32,0,0,0,0,0,0,119,114,36,105,116,101,98,97,99,107,0,0,0,0,0,0,125,10,0,0,0,0,0,0,9,0,0,0,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,100,102,95,114,101,97,100,108,105,110,101,32,114,101,116,117,114,110,101,100,32,37,100,32,58,32,100,97,116,97,102,105,108,101,32,108,105,110,101,32,37,100,0,0,0,0,0,84,97,98,0,0,0,0,0,112,114,105,110,116,32,34,92,110,34,10,0,0,0,0,0,105,102,32,40,108,101,110,103,116,104,40,36,121,50,109,41,62,48,41,32,123,112,114,105,110,116,32,34,32,36,121,50,109,34,59,125,32,101,108,115,101,32,123,112,114,105,110,116,32,34,32,34,44,32,48,46,53,42,40,36,121,50,115,43,36,121,50,101,41,59,125,10,0,0,0,0,0,0,0,0,99,97,110,110,111,116,32,114,101,102,114,101,115,104,32,102,114,111,109,32,116,104,105,115,32,115,116,97,116,101,46,32,116,114,121,105,110,103,32,102,117,108,108,32,114,101,112,108,111,116,0,0,0,0,0,0,105,102,32,40,108,101,110,103,116,104,40,36,120,50,109,41,62,48,41,32,123,112,114,105,110,116,32,34,32,36,120,50,109,34,59,125,32,101,108,115,101,32,123,112,114,105,110,116,32,34,32,34,44,32,48,46,53,42,40,36,120,50,115,43,36,120,50,101,41,59,125,10,0,0,0,0,0,0,0,0,85,110,100,101,102,105,110,101,100,32,118,97,108,117,101,32,102,105,114,115,116,32,99,111,108,111,114,32,100,117,114,105,110,103,32,102,117,110,99,116,105,111,110,32,101,118,97,108,117,97,116,105,111,110,0,0,105,102,32,40,108,101,110,103,116,104,40,36,121,49,109,41,62,48,41,32,123,112,114,105,110,116,32,34,32,36,121,49,109,34,59,125,32,101,108,115,101,32,123,112,114,105,110,116,32,34,32,34,44,32,48,46,53,42,40,36,121,49,115,43,36,121,49,101,41,59,125,10,0,0,0,0,0,0,0,0,105,102,32,40,108,101,110,103,116,104,40,36,120,49,109,41,62,48,41,32,123,112,114,105,110,116,32,34,32,36,120,49,109,34,59,125,32,101,108,115,101,32,123,112,114,105,110,116,32,34,32,34,44,32,48,46,53,42,40,36,120,49,115,43,36,120,49,101,41,59,125,10,0,0,0,0,0,0,0,0,79,117,116,114,97,110,103,101,100,32,97,110,100,32,117,110,100,101,102,105,110,101,100,32,100,97,116,97,112,111,105,110,116,115,32,97,114,101,32,111,109,105,116,116,101,100,32,102,114,111,109,32,116,104,101,32,115,117,114,102,97,99,101,46,10,0,0,0,0,0,0,0,114,109,115,32,111,102,32,114,101,115,105,100,117,97,108,115,32,32,32,32,32,32,40,70,73,84,95,83,84,68,70,73,84,41,32,61,32,115,113,114,116,40,87,83,83,82,47,110,100,102,41,32,32,32,32,58,32,37,103,10,0,0,0,0,125,32,101,108,115,101,32,123,10,0,0,0,0,0,0,0,58,10,0,0,0,0,0,0,117,115,101,114,95,103,110,117,112,108,111,116,95,99,111,111,114,100,105,110,97,116,101,115,32,36,119,105,110,44,32,36,105,100,44,32,36,120,49,115,44,32,36,121,49,115,44,32,36,120,50,115,44,32,36,121,50,115,44,32,36,120,49,101,44,32,36,121,49,101,44,32,36,120,50,101,44,32,36,121,50,101,44,32,36,120,49,109,44,32,36,121,49,109,44,32,36,120,50,109,44,32,36,121,50,109,10,0,0,0,0,0,103,97,109,109,97,0,0,0,109,121,32,36,105,100,32,61,32,36,119,105,110,45,62,102,105,110,100,40,39,119,105,116,104,116,97,103,39,44,32,39,99,117,114,114,101,110,116,39,41,59,10,0,0,0,0,0,72,101,108,118,101,116,105,99,97,32,78,97,114,114,111,119,32,79,98,108,105,113,117,101,0,0,0,0,0,0,0,0,100,97,36,116,97,0,0,0,32,32,77,97,120,105,109,117,109,58,32,32,37,115,32,91,37,42,108,100,93,10,0,0,105,102,32,40,100,101,102,105,110,101,100,32,38,117,115,101,114,95,103,110,117,112,108,111,116,95,99,111,111,114,100,105,110,97,116,101,115,41,32,123,10,0,0,0,0,0,0,0,110,111,97,117,116,111,36,115,99,97,108,101,0,0,0,0,109,121,32,40,36,119,105,110,44,32,36,120,49,115,44,32,36,121,49,115,44,32,36,120,50,115,44,32,36,121,50,115,44,32,36,120,49,101,44,32,36,121,49,101,44,32,36,120,50,101,44,32,36,121,50,101,44,32,36,120,49,109,44,32,36,121,49,109,44,32,36,120,50,109,44,32,36,121,50,109,41,32,61,32,64,95,59,10,0,0,0,0,0,0,0,0,37,115,111,98,106,101,99,116,32,37,50,100,32,114,101,99,116,32,0,0,0,0,0,0,78,111,116,32,101,110,111,117,103,104,32,99,111,108,117,109,110,115,32,102,111,114,32,118,97,114,105,97,98,108,101,32,99,111,108,111,114,0,0,0,66,97,99,107,83,112,97,99,101,0,0,0,0,0,0,0,101,120,112,101,99,116,101,100,32,99,98,32,118,97,108,117,101,0,0,0,0,0,0,0,115,117,98,32,103,110,117,112,108,111,116,95,120,121,32,123,10,0,0,0,0,0,0,0,114,101,116,117,114,110,32,40,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,41,59,10,0,0,0,0,0,0,0,110,111,32,97,99,116,105,118,101,32,112,108,111,116,59,32,99,97,110,110,111,116,32,114,101,102,114,101,115,104,0,0,115,117,98,32,103,110,117,112,108,111,116,95,97,120,105,115,114,97,110,103,101,115,32,123,10,0,0,0,0,0,0,0,105,110,32,108,97,98,101,108,95,119,105,100,116,104,0,0,37,115,58,37,100,32,111,111,111,112,115,58,32,85,110,107,110,111,119,110,32,99,111,108,111,114,77,111,100,101,32,39,37,99,39,46,10,0,0,0,114,101,116,117,114,110,32,40,37,100,44,32,37,100,44,32,37,100,44,32,37,100,41,59,10,0,0,0,0,0,0,0,115,117,98,32,103,110,117,112,108,111,116,95,112,108,111,116,97,114,101,97,32,123,10,0,100,101,103,114,101,101,115,32,111,102,32,102,114,101,101,100,111,109,32,32,32,32,40,70,73,84,95,78,68,70,41,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,58,32,37,100,10,0,0,0,0,125,59,10,0,0,0,0,0,112,114,111,99,32,103,110,117,112,108,111,116,32,99,97,110,32,123,10,36,99,97,110,32,100,101,108,101,116,101,32,97,108,108,10,115,101,116,32,99,109,120,32,91,101,120,112,114,32,91,119,105,110,102,111,32,119,105,100,116,104,32,36,99,97,110,93,45,50,42,91,36,99,97,110,32,99,103,101,116,32,45,98,111,114,100,101,114,93,45,50,42,91,36,99,97,110,32,99,103,101,116,32,45,104,105,103,104,108,105,103,104,116,116,104,105,99,107,110,101,115,115,93,93,10,105,102,32,123,36,99,109,120,32,60,61,32,49,125,32,123,115,101,116,32,99,109,120,32,91,36,99,97,110,32,99,103,101,116,32,45,119,105,100,116,104,93,125,10,115,101,116,32,99,109,121,32,91,101,120,112,114,32,91,119,105,110,102,111,32,104,101,105,103,104,116,32,36,99,97,110,93,45,50,42,91,36,99,97,110,32,99,103,101,116,32,45,98,111,114,100,101,114,93,45,50,42,91,36,99,97,110,32,99,103,101,116,32,45,104,105,103,104,108,105,103,104,116,116,104,105,99,107,110,101,115,115,93,93,10,105,102,32,123,36,99,109,121,32,60,61,32,49,125,32,123,115,101,116,32,99,109,121,32,91,36,99,97,110,32,99,103,101,116,32,45,104,101,105,103,104,116,93,125,10,0,0,0,0,0,0,0,101,114,102,99,0,0,0,0,115,117,98,32,123,10,109,121,40,36,99,97,110,41,32,61,32,64,95,59,10,36,99,97,110,45,62,100,101,108,101,116,101,40,39,97,108,108,39,41,59,10,109,121,32,36,99,109,120,32,61,32,36,99,97,110,45,62,119,105,100,116,104,32,45,32,50,32,42,32,36,99,97,110,45,62,99,103,101,116,40,45,98,111,114,100,101,114,41,32,45,32,50,32,42,32,36,99,97,110,45,62,99,103,101,116,40,45,104,105,103,104,108,105,103,104,116,116,104,105,99,107,110,101,115,115,41,59,10,105,102,32,40,36,99,109,120,32,60,61,32,49,41,32,123,10,36,99,109,120,32,61,32,40,36,99,97,110,45,62,99,103,101,116,40,45,119,105,100,116,104,41,41,59,10,125,10,109,121,32,36,99,109,121,32,61,32,36,99,97,110,45,62,104,101,105,103,104,116,32,45,32,50,32,42,32,36,99,97,110,45,62,99,103,101,116,40,45,98,111,114,100,101,114,41,32,45,32,50,32,42,32,36,99,97,110,45,62,99,103,101,116,40,45,104,105,103,104,108,105,103,104,116,116,104,105,99,107,110,101,115,115,41,59,10,105,102,32,40,36,99,109,121,32,60,61,32,49,41,32,123,10,36,99,109,121,32,61,32,40,36,99,97,110,45,62,99,103,101,116,40,45,104,101,105,103,104,116,41,41,59,10,125,10,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,32,78,97,114,114,111,119,0,0,0,0,0,0,0,0,99,111,36,110,116,111,117,114,115,0,0,0,0,0,0,0,32,32,77,105,110,105,109,117,109,58,32,32,37,115,32,91,37,42,108,100,93,10,0,0,34,10,0,0,0,0,0,0,118,111,108,97,116,105,108,101,0,0,0,0,0,0,0,0,32,123,125,0,0,0,0,0,103,114,97,112,104,32,0,0,71,80,95,76,65,83,84,95,75,69,89,0,0,0,0,0,99,98,0,0,0,0,0,0,93,32,60,66,117,116,116,111,110,62,32,34,103,110,117,112,108,111,116,95,120,121,32,37,37,87,32,37,102,32,37,102,32,37,102,32,37,102,32,37,102,32,37,102,32,37,102,32,37,102,0,0,0,0,0,0,93,41,59,10,0,0,0,0,100,97,116,97,102,105,108,101,32,110,97,109,101,0,0,0,112,114,105,110,116,32,99,117,114,114,101,110,116,32,100,105,114,0,0,0,0,0,0,0,32,34,34,0,0,0,0,0,32,37,102,0,0,0,0,0,32,34,34,44,0,0,0,0,10,72,109,109,109,109,46,46,46,46,32,83,117,109,32,111,102,32,115,113,117,97,114,101,100,32,114,101,115,105,100,117,97,108,115,32,105,115,32,122,101,114,111,46,32,67,97,110,39,116,32,99,111,109,112,117,116,101,32,101,114,114,111,114,115,46,10,10,0,0,0,0,32,37,102,44,0,0,0,0,44,32,39,60,66,117,116,116,111,110,62,39,32,61,62,32,91,92,38,103,110,117,112,108,111,116,95,120,121,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,32,37,102,44,0,0,101,114,102,0,0,0,0,0,112,111,112,32,49,0,0,0,36,99,97,110,32,99,114,101,97,116,101,32,108,105,110,101,32,91,101,120,112,114,32,36,99,109,120,32,42,32,37,100,32,47,49,48,48,48,93,32,91,101,120,112,114,32,36,99,109,121,32,42,32,37,100,32,47,49,48,48,48,93,32,91,101,120,112,114,32,36,99,109,120,32,42,32,37,100,32,47,49,48,48,48,93,32,91,101,120,112,114,32,36,99,109,121,32,42,32,37,100,32,47,49,48,48,48,93,32,45,102,105,108,108,32,37,115,32,45,119,105,100,116,104,32,37,102,32,45,99,97,112,115,116,121,108,101,32,114,111,117,110,100,10,0,0,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,32,66,111,108,100,32,79,98,108,105,113,117,101,0,0,99,110,36,116,114,112,97,114,97,109,0,0,0,0,0,0,32,32,67,79,71,58,32,32,32,32,32,32,37,115,32,37,115,10,0,0,0,0,0,0,36,99,97,110,45,62,99,114,101,97,116,101,76,105,110,101,40,36,99,109,120,32,42,32,37,100,32,47,32,49,48,48,48,44,32,36,99,109,121,32,42,32,37,100,32,47,32,49,48,48,48,44,32,36,99,109,120,32,42,32,37,100,32,47,32,49,48,48,48,44,32,36,99,109,121,32,42,32,37,100,32,47,32,49,48,48,48,44,32,45,102,105,108,108,32,61,62,32,113,123,37,115,125,44,32,45,119,105,100,116,104,32,61,62,32,37,102,44,32,45,99,97,112,115,116,121,108,101,32,61,62,32,113,123,114,111,117,110,100,125,41,0,0,0,117,36,115,105,110,103,0,0,9,32,32,101,120,112,108,105,99,105,116,32,108,105,115,116,32,40,0,0,0,0,0,0,36,99,97,110,32,98,105,110,100,32,91,10,0,0,0,0,37,115,32,37,115,37,103,44,32,37,115,37,103,44,32,37,115,37,103,44,32,37,115,37,103,10,0,0,0,0,0,0,78,111,116,32,101,110,111,117,103,104,32,99,111,108,117,109,110,115,32,102,111,114,32,116,104,105,115,32,115,116,121,108,101,0,0,0,0,0,0,0,66,117,116,116,111,110,49,0,112,97,108,101,116,116,101,32,122,32,110,111,116,32,112,111,115,115,105,98,108,101,32,104,101,114,101,0,0,0,0,0,36,99,97,110,45,62,98,105,110,100,40,0,0,0,0,0,99,121,97,110,0,0,0,0,60,115,116,100,101,114,114,62,0,0,0,0,0,0,0,0,109,97,103,101,110,116,97,0,50,42,120,32,45,32,49,0,98,114,111,119,110,0,0,0,103,114,101,101,110,0,0,0,37,45,49,53,46,49,53,115,32,61,32,37,45,49,53,103,10,0,0,0,0,0,0,0,98,108,117,101,0,0,0,0,114,101,100,0,0,0,0,0,98,101,115,121,49,0,0,0,103,114,97,121,0,0,0,0,72,101,108,118,101,116,105,99,97,32,66,111,108,100,0,0,99,36,108,105,112,0,0,0,32,32,77,97,120,105,109,117,109,58,32,32,37,115,32,91,37,42,108,100,32,37,108,100,32,93,10,0,0,0,0,0,98,108,97,99,107,0,0,0,116,104,114,117,36,0,0,0,117,110,107,110,111,119,110,32,116,105,99,100,101,102,32,116,121,112,101,32,105,110,32,115,104,111,119,95,116,105,99,100,101,102,40,41,0,0,0,0,101,118,97,108,32,36,99,97,110,32,99,114,101,97,116,101,32,116,101,120,116,32,91,101,120,112,114,32,36,99,109,120,32,42,32,37,100,32,47,49,48,48,48,93,32,91,101,120,112,114,32,36,99,109,121,32,42,32,37,100,32,47,49,48,48,48,93,32,45,116,101,120,116,32,92,123,37,115,92,125,32,45,102,105,108,108,32,37,115,32,45,97,110,99,104,111,114,32,37,115,32,91,101,120,112,114,32,91,105,110,102,111,32,101,120,105,115,116,115,32,102,111,110,116,93,63,34,45,102,111,110,116,32,92,36,102,111,110,116,34,58,123,125,93,10,0,0,0,0,0,0,0,32,112,111,105,110,116,105,110,116,101,114,118,97,108,32,37,100,0,0,0,0,0,0,0,84,111,111,32,109,97,110,121,32,117,115,105,110,103,32,115,112,101,99,115,32,102,111,114,32,116,104,105,115,32,115,116,121,108,101,0,0,0,0,0,67,108,111,115,101,0,0,0,84,75,95,112,117,116,95,116,101,120,116,58,32,113,117,111,116,101,100,32,115,116,114,105,110,103,0,0,0,0,0,0,36,99,97,110,45,62,99,114,101,97,116,101,84,101,120,116,40,36,99,109,120,32,42,32,37,100,32,47,32,49,48,48,48,44,32,36,99,109,121,32,42,32,37,100,32,47,32,49,48,48,48,44,32,45,116,101,120,116,32,61,62,32,113,123,37,115,125,44,32,45,102,105,108,108,32,61,62,32,113,123,37,115,125,44,32,45,97,110,99,104,111,114,32,61,62,32,39,37,115,39,44,32,40,100,101,102,105,110,101,100,32,36,102,111,110,116,32,63,32,40,45,102,111,110,116,32,61,62,32,36,102,111,110,116,41,32,58,32,40,41,41,41,59,10,0,0,0,0,0,0,0,0,60,115,116,100,111,117,116,62,0,0,0,0,0,0,0,0,99,101,110,116,101,114,0,0,50,32,109,117,108,32,49,32,115,117,98,0,0,0,0,0,110,101,119,32,112,97,114,97,109,101,116,101,114,32,102,105,108,101,32,37,115,32,99,111,117,108,100,32,110,111,116,32,98,101,32,99,114,101,97,116,101,100,0,0,0,0,0,0,93,10,0,0,0,0,0,0,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,32,10,10,0,0,0,0,0,0,32,45,115,105,122,101,32,37,100,0,0,0,0,0,0,0,115,101,116,32,102,111,110,116,32,91,102,111,110,116,32,99,114,101,97,116,101,32,45,102,97,109,105,108,121,32,37,115,0,0,0,0,0,0,0,0,98,101,115,121,48,0,0,0,41,59,10,125,10,0,0,0,72,101,108,118,101,116,105,99,97,32,79,98,108,105,113,117,101,0,0,0,0,0,0,0,99,108,36,97,98,101,108,0,32,32,77,105,110,105,109,117,109,58,32,32,37,115,32,91,37,42,108,100,32,37,108,100,32,93,10,0,0,0,0,0,44,32,45,115,105,122,101,32,61,62,32,37,100,0,0,0,101,118,36,101,114,121,0,0,32,32,110,111,32,97,117,116,111,45,103,101,110,101,114,97,116,101,100,32,116,105,99,115,10,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,91,39,32,111,114,32,39,114,101,115,116,111,114,101,39,0,0,0,0,0,0,105,102,32,40,36,99,97,110,45,62,99,97,110,40,39,102,111,110,116,67,114,101,97,116,101,39,41,41,32,123,10,36,102,111,110,116,32,61,32,36,99,97,110,45,62,102,111,110,116,67,114,101,97,116,101,40,45,102,97,109,105,108,121,32,61,62,32,113,123,37,115,125,0,0,0,0,0,0,0,0,32,112,111,105,110,116,115,105,122,101,32,37,46,51,102,0,118,97,114,99,111,108,111,114,32,97,114,114,97,121,0,0,70,49,50,0,0,0,0,0,111,110,108,121,32,116,99,32,108,116,32,60,110,62,32,112,111,115,115,105,98,108,101,32,104,101,114,101,0,0,0,0,99,97,116,99,104,32,123,117,110,115,101,116,32,36,102,111,110,116,125,10,0,0,0,0,117,110,100,101,102,32,36,102,111,110,116,59,10,0,0,0,110,111,36,114,111,116,97,116,101,0,0,0,0,0,0,0,50,42,120,32,45,32,48,46,53,0,0,0,0,0,0,0,114,111,116,36,97,116,101,0,9,108,111,97,100,112,97,116,104,32,105,115,32,101,109,112,116,121,10,0,0,0,0,0,115,105,36,122,101,0,0,0,70,105,110,97,108,32,115,101,116,32,111,102,32,112,97,114,97,109,101,116,101,114,115,32,10,0,0,0,0,0,0,0,114,36,111,109,97,110,0,0,37,48,42,100,0,0,0,0,99,36,111,117,114,105,101,114,0,0,0,0,0,0,0,0,98,101,115,106,49,0,0,0,32,110,111,114,111,116,97,116,101,0,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,0,0,0,0,0,0,0,98,111,120,36,119,105,100,116,104,0,0,0,0,0,0,0,32,32,83,117,109,32,83,113,46,58,32,32,37,115,10,0,115,105,122,101,32,37,46,50,102,105,110,44,32,37,46,50,102,105,110,32,0,0,0,0,105,36,110,100,101,120,0,0,32,117,110,116,105,108,32,0,115,105,122,101,32,37,46,50,102,99,109,44,32,37,46,50,102,99,109,32,0,0,0,0,32,112,111,105,110,116,115,105,122,101,32,100,101,102,97,117,108,116,0,0,0,0,0,0,65,108,108,32,112,111,105,110,116,115,32,105,110,32,104,105,115,116,111,103,114,97,109,32,85,78,68,69,70,73,78,69,68,0,0,0,0,0,0,0,70,49,49,0,0,0,0,0,105,108,108,101,103,97,108,32,108,105,110,101,116,121,112,101,0,0,0,0,0,0,0,0,114,111,109,97,110,0,0,0,99,111,117,114,105,101,114,0,99,112,56,53,50,0,0,0,37,115,32,37,100,0,0,0,49,32,121,50,0,0,0,0,50,32,109,117,108,32,48,46,53,32,115,117,98,0,0,0,40,100,111,99,117,109,101,110,116,32,115,112,101,99,105,102,105,99,32,102,111,110,116,41,0,0,0,0,0,0,0,0,73,110,32,116,104,105,115,32,100,101,103,101,110,101,114,97,116,101,32,99,97,115,101,44,32,97,108,108,32,101,114,114,111,114,115,32,97,114,101,32,122,101,114,111,32,98,121,32,100,101,102,105,110,105,116,105,111,110,46,10,10,0,0,0,99,109,116,116,0,0,0,0,37,37,32,71,78,85,80,76,79,84,58,32,76,97,84,101,88,32,112,105,99,116,117,114,101,10,92,115,101,116,108,101,110,103,116,104,123,92,117,110,105,116,108,101,110,103,116,104,125,123,37,102,112,116,125,10,92,105,102,120,92,112,108,111,116,112,111,105,110,116,92,117,110,100,101,102,105,110,101,100,92,110,101,119,115,97,118,101,98,111,120,123,92,112,108,111,116,112,111,105,110,116,125,92,102,105,10,0,0,0,0,0,37,115,58,37,100,32,111,111,111,112,115,58,32,85,110,107,110,111,119,110,32,99,111,108,111,114,32,109,111,100,101,108,32,39,37,99,39,10,0,0,98,101,115,106,48,0,0,0,92,102,111,110,116,92,103,110,117,112,108,111,116,61,37,115,49,48,32,97,116,32,37,100,112,116,10,92,103,110,117,112,108,111,116,10,0,0,0,0,67,111,117,114,105,101,114,32,66,111,108,100,32,79,98,108,105,113,117,101,0,0,0,0,98,111,114,36,100,101,114,0,32,32,83,117,109,58,32,32,32,32,32,32,37,115,10,0,101,118,97,108,36,117,97,116,101,0,0,0,0,0,0,0,100,111,99,0,0,0,0,0,110,111,110,117,110,105,36,102,111,114,109,0,0,0,0,0,32,115,101,99,115,0,0,0,114,101,36,115,116,111,114,101,0,0,0,0,0,0,0,0,83,116,97,116,115,32,99,111,109,109,97,110,100,32,110,111,116,32,97,118,97,105,108,97,98,108,101,32,119,105,116,104,32,108,111,103,115,99,97,108,101,32,97,99,116,105,118,101,0,0,0,0,0,0,0,0,92,98,101,103,105,110,123,112,105,99,116,117,114,101,125,40,37,100,44,37,100,41,40,48,44,48,41,10,0,0,0,0,32,112,111,105,110,116,115,105,122,101,32,118,97,114,105,97,98,108,101,0,0,0,0,0,115,116,97,99,107,104,101,105,103,104,116,32,97,114,114,97,121,0,0,0,0,0,0,0,70,49,48,0,0,0,0,0,101,120,112,101,99,116,101,100,32,108,105,110,101,116,121,112,101,0,0,0,0,0,0,0,92,109,117,108,116,105,112,117,116,40,37,117,44,37,117,41,40,37,46,51,102,44,37,46,51,102,41,123,37,117,125,123,37,115,125,10,0,0,0,0,92,112,117,116,40,37,46,50,102,44,37,46,50,102,41,123,37,115,125,10,0,0,0,0,92,115,112,101,99,105,97,108,123,101,109,58,108,105,110,101,119,105,100,116,104,32,37,46,49,102,112,116,125,37,37,10,0,0,0,0,0,0,0,0,50,42,120,0,0,0,0,0,92,115,98,111,120,123,92,112,108,111,116,112,111,105,110,116,125,123,92,114,117,108,101,91,37,46,51,102,112,116,93,123,37,46,51,102,112,116,125,123,37,46,51,102,112,116,125,125,37,37,10,0,0,0,0,0,123,92,114,111,116,97,116,101,98,111,120,123,37,100,125,0,84,101,114,109,105,110,97,108,32,111,112,116,105,111,110,115,32,97,114,101,32,39,37,115,39,10,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,112,97,100,101,115,117,105,116,36,125,0,0,0,0,0,10,69,120,97,99,116,108,121,32,97,115,32,109,97,110,121,32,100,97,116,97,32,112,111,105,110,116,115,32,97,115,32,116,104,101,114,101,32,97,114,101,32,112,97,114,97,109,101,116,101,114,115,46,10,0,0,103,101,116,95,111,102,102,115,101,116,115,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,104,101,97,114,116,115,117,105,116,36,125,0,0,0,0,0,108,111,103,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,98,108,97,99,107,108,111,122,101,110,103,101,36,125,0,0,67,111,117,114,105,101,114,32,66,111,108,100,0,0,0,0,32,32,83,116,100,32,68,101,118,58,32,32,37,115,10,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,108,111,122,101,110,103,101,36,125,0,0,0,0,0,0,0,32,98,121,32,37,103,37,115,0,0,0,0,0,0,0,0,105,110,116,101,103,101,114,32,111,118,101,114,102,108,111,119,59,32,99,104,97,110,103,105,110,103,32,116,111,32,102,108,111,97,116,105,110,103,32,112,111,105,110,116,0,0,0,0,101,120,112,101,99,116,105,110,103,32,114,105,103,104,116,32,112,97,114,101,110,116,104,101,115,105,115,32,41,0,0,0,101,104,102,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,98,108,97,99,107,116,114,105,97,110,103,108,101,100,111,119,110,36,125,0,0,0,0,0,32,112,111,105,110,116,116,121,112,101,32,37,100,0,0,0,98,111,120,112,108,111,116,32,104,97,115,32,117,110,100,101,102,105,110,101,100,32,120,32,99,111,111,114,100,105,110,97,116,101,0,0,0,0,0,0,70,57,0,0,0,0,0,0,101,120,112,101,99,116,101,100,32,99,111,108,111,114,115,112,101,99,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,116,114,105,97,110,103,108,101,100,111,119,110,36,125,0,0,99,111,110,116,111,117,114,32,100,50,121,0,0,0,0,0])
+.concat([92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,98,108,97,99,107,116,114,105,97,110,103,108,101,36,125,0,115,101,116,32,37,115,114,97,110,103,101,32,91,32,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,98,117,108,108,101,116,36,125,0,0,0,0,0,0,0,0,98,108,45,105,110,116,101,114,112,32,97,108,111,110,103,32,115,99,97,110,0,0,0,0,112,114,101,118,105,111,117,115,32,112,97,114,97,109,101,116,114,105,99,32,102,117,110,99,116,105,111,110,32,110,111,116,32,102,117,108,108,121,32,115,112,101,99,105,102,105,101,100,0,0,0,0,0,0,0,0,50,32,109,117,108,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,99,105,114,99,36,125,0,0,116,101,120,116,95,108,97,98,101,108,32,108,105,115,116,32,119,97,115,32,110,111,116,32,105,110,105,116,105,97,108,105,122,101,100,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,98,108,97,99,107,115,113,117,97,114,101,36,125,0,0,0,45,45,112,101,114,115,105,115,116,0,0,0,0,0,0,0,97,98,115,46,32,99,104,97,110,103,101,32,100,117,114,105,110,103,32,108,97,115,116,32,105,116,101,114,97,116,105,111,110,32,58,32,37,103,10,10,0,0,0,0,0,0,0,0,92,114,97,105,115,101,98,111,120,123,45,46,56,112,116,125,123,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,66,111,120,36,125,125,0,87,97,114,110,105,110,103,32,58,32,117,100,102,32,115,104,97,100,111,119,101,100,32,98,121,32,98,117,105,108,116,45,105,110,32,102,117,110,99,116,105,111,110,32,111,102,32,116,104,101,32,115,97,109,101,32,110,97,109,101,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,97,115,116,36,125,0,0,0,108,111,103,49,48,0,0,0,92,114,117,108,101,123,49,112,116,125,123,49,112,116,125,0,67,111,117,114,105,101,114,32,79,98,108,105,113,117,101,0,98,36,97,114,115,0,0,0,32,32,77,101,97,110,58,32,32,32,32,32,37,115,10,0,77,79,85,83,69,95,82,85,76,69,82,95,89,0,0,0,92,112,117,116,40,37,100,44,37,100,41,123,92,114,117,108,101,123,37,103,112,116,125,123,37,103,112,116,125,125,10,0,37,35,103,0,0,0,0,0,105,110,99,114,101,109,101,110,116,32,109,117,115,116,32,98,101,32,110,101,103,97,116,105,118,101,0,0,0,0,0,0,37,37,32,71,78,85,80,76,79,84,58,32,76,97,84,101,88,32,112,105,99,116,117,114,101,32,119,105,116,104,32,101,109,116,101,120,32,115,112,101,99,105,97,108,115,10,92,115,101,116,108,101,110,103,116,104,123,92,117,110,105,116,108,101,110,103,116,104,125,123,37,102,112,116,125,10,92,105,102,120,92,112,108,111,116,112,111,105,110,116,92,117,110,100,101,102,105,110,101,100,92,110,101,119,115,97,118,101,98,111,120,123,92,112,108,111,116,112,111,105,110,116,125,92,102,105,10,0,32,108,105,110,101,119,105,100,116,104,32,37,46,51,102,0,98,111,120,112,108,111,116,32,112,101,114,109,117,116,97,116,105,111,110,115,32,97,114,114,97,121,0,0,0,0,0,0,115,116,114,111,107,101,32,103,115,97,118,101,9,37,37,32,100,114,97,119,32,103,114,97,121,32,115,99,97,108,101,32,115,109,111,111,116,104,32,98,111,120,10,109,97,120,99,111,108,111,114,115,32,48,32,103,116,32,123,47,105,109,97,120,32,109,97,120,99,111,108,111,114,115,32,100,101,102,125,32,123,47,105,109,97,120,32,49,48,50,52,32,100,101,102,125,32,105,102,101,108,115,101,10,0,0,0,0,0,0,0,0,115,112,108,105,110,101,32,115,111,108,117,116,105,111,110,32,118,101,99,116,111,114,0,0,70,56,0,0,0,0,0,0,92,101,110,100,71,78,85,80,76,79,84,112,105,99,116,117,114,101,10,92,101,110,100,103,114,111,117,112,10,92,101,110,100,105,110,112,117,116,10,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,102,95,115,117,109,32,99,111,117,108,100,32,110,111,116,32,97,99,99,101,115,115,32,105,116,101,114,97,116,105,111,110,32,118,97,114,105,97,98,108,101,46,0,0,0,0,0,0,92,101,110,100,123,112,105,99,116,117,114,101,125,37,10,92,101,110,100,103,114,111,117,112,10,92,101,110,100,105,110,112,117,116,10,0,0,0,0,0,92,101,110,100,123,100,111,99,117,109,101,110,116,125,10,0,124,50,42,120,32,45,32,48,46,53,124,0,0,0,0,0,32,32,32,32,92,103,112,108,98,97,99,107,116,101,120,116,10,32,32,32,32,92,112,117,116,40,48,44,48,41,123,92,105,110,99,108,117,100,101,103,114,97,112,104,105,99,115,123,37,115,125,125,37,37,10,32,32,32,32,92,103,112,108,102,114,111,110,116,116,101,120,116,10,32,32,92,101,110,100,123,112,105,99,116,117,114,101,125,37,37,10,92,101,110,100,103,114,111,117,112,10,0,0,0,114,101,108,46,32,99,104,97,110,103,101,32,100,117,114,105,110,103,32,108,97,115,116,32,105,116,101,114,97,116,105,111,110,32,58,32,37,103,10,10,0,0,0,0,0,0,0,0,92,114,111,116,97,116,101,98,111,120,123,37,100,125,123,0,32,32,32,32,32,32,92,112,117,116,40,37,100,44,37,100,41,123,0,0,0,0,0,0,32,32,32,32,32,32,92,99,111,108,111,114,103,114,97,121,123,37,115,125,37,37,10,0,67,111,117,114,105,101,114,0,97,117,36,116,111,115,99,97,108,101,0,0,0,0,0,0,42,32,67,79,76,85,77,78,58,32,10,0,0,0,0,0,32,32,32,32,32,32,92,99,111,108,111,114,123,119,104,105,116,101,125,37,37,10,0,0,32,102,114,111,109,32,0,0,105,110,99,114,101,109,101,110,116,32,109,117,115,116,32,98,101,32,112,111,115,105,116,105,118,101,0,0,0,0,0,0,32,32,32,32,32,32,92,99,111,108,111,114,123,98,108,97,99,107,125,37,37,10,0,0,32,118,97,114,105,97,98,108,101,0,0,0,0,0,0,0,98,111,120,112,108,111,116,32,108,97,98,101,108,115,32,97,114,114,97,121,0,0,0,0,70,55,0,0,0,0,0,0,114,103,98,0,0,0,0,0,119,98,97,48,49,50,51,52,53,54,55,56,0,0,0,0,32,32,32,32,32,32,92,99,115,110,97,109,101,32,76,84,37,99,92,101,110,100,99,115,110,97,109,101,37,37,10,0,32,32,32,32,32,32,92,99,111,108,111,114,114,103,98,123,37,51,46,50,102,44,37,51,46,50,102,44,37,51,46,50,102,125,37,37,10,0,0,0,50,32,109,117,108,32,48,46,53,32,115,117,98,32,97,98,115,0,0,0,0,0,0,0,37,37,112,109,51,100,95,109,97,112,95,101,110,100,10,0,37,37,112,109,51,100,95,109,97,112,95,98,101,103,105,110,10,0,0,0,0,0,0,0,9,32,32,66,97,99,107,32,115,105,100,101,32,111,102,32,115,117,114,102,97,99,101,115,32,104,97,115,32,108,105,110,101,115,116,121,108,101,32,111,102,102,115,101,116,32,111,102,32,37,100,10,9,32,32,66,105,116,45,77,97,115,107,32,111,102,32,76,105,110,101,115,32,116,111,32,100,114,97,119,32,105,110,32,101,97,99,104,32,116,114,105,97,110,103,108,101,32,105,115,32,37,108,100,10,9,32,32,37,100,58,32,0,0,0,0,0,0,0,0,102,105,110,97,108,32,115,117,109,32,111,102,32,115,113,117,97,114,101,115,32,111,102,32,114,101,115,105,100,117,97,108,115,32,58,32,37,103,10,0,32,32,32,32,92,103,112,108,103,97,100,100,116,111,109,97,99,114,111,92,103,112,108,102,114,111,110,116,116,101,120,116,123,37,10,0,0,0,0,0,32,32,32,32,92,103,112,108,103,97,100,100,116,111,109,97,99,114,111,92,103,112,108,98,97,99,107,116,101,120,116,123,37,10,0,0,0,0,0,0,10,83,117,98,116,111,112,105,99,115,32,97,118,97,105,108,97,98,108,101,32,102,111,114,32,0,0,0,0,0,0,0,32,32,32,32,125,37,10,0,66,111,111,107,109,97,110,32,68,101,109,105,32,73,116,97,108,105,99,0,0,0,0,0,97,114,36,114,111,119,0,0,42,32,77,65,84,82,73,88,58,32,91,37,100,32,88,32,37,100,93,32,10,0,0,0,125,37,10,0,0,0,0,0,78,111,32,112,114,101,118,105,111,117,115,32,102,105,108,101,110,97,109,101,0,0,0,0,32,32,115,101,114,105,101,115,0,0,0,0,0,0,0,0,37,10,32,32,92,115,112,101,99,105,97,108,123,112,115,58,32,99,117,114,114,101,110,116,112,111,105,110,116,32,103,114,101,115,116,111,114,101,32,109,111,118,101,116,111,125,37,10,32,32,0,0,0,0,0,0,70,54,0,0,0,0,0,0,92,114,106,117,115,116,123,92,115,116,114,117,116,123,125,37,115,125,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,91,114,93,123,92,115,116,114,117,116,123,125,37,115,125,0,0,0,0,92,99,106,117,115,116,123,92,115,116,114,117,116,123,125,37,115,125,0,0,0,0,0,0,121,116,105,99,115,0,0,0,52,120,59,49,59,45,50,120,43,49,46,56,52,59,120,47,48,46,48,56,45,49,49,46,53,0,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,92,115,116,114,117,116,123,125,37,115,125,0,0,0,0,0,0,0,92,108,106,117,115,116,123,92,115,116,114,117,116,123,125,37,115,125,0,0,0,0,0,0,10,65,102,116,101,114,32,37,100,32,105,116,101,114,97,116,105,111,110,115,32,116,104,101,32,102,105,116,32,99,111,110,118,101,114,103,101,100,46,10,0,0,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,91,108,93,123,92,115,116,114,117,116,123,125,37,115,125,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,37,115,0,32,32,87,97,114,110,105,110,103,58,32,83,105,110,103,108,101,32,105,115,111,108,105,110,101,32,40,115,99,97,110,41,32,105,115,32,110,111,116,32,101,110,111,117,103,104,32,102,111,114,32,97,32,112,109,51,100,32,112,108,111,116,46,10,9,32,32,32,72,105,110,116,58,32,77,105,115,115,105,110,103,32,98,108,97,110,107,32,108,105,110,101,115,32,105,110,32,116,104,101,32,100,97,116,97,32,102,105,108,101,63,32,83,101,101,32,39,104,101,108,112,32,112,109,51,100,39,32,97,110,100,32,70,65,81,46,10,0,0,0,0,0,0,0,115,103,110,0,0,0,0,0,37,37,10,32,32,92,115,112,101,99,105,97,108,123,112,115,58,32,103,115,97,118,101,32,99,117,114,114,101,110,116,112,111,105,110,116,32,99,117,114,114,101,110,116,112,111,105,110,116,32,116,114,97,110,115,108,97,116,101,10,37,100,32,114,111,116,97,116,101,32,110,101,103,32,101,120,99,104,32,110,101,103,32,101,120,99,104,32,116,114,97,110,115,108,97,116,101,125,37,37,10,32,32,0,66,111,111,107,109,97,110,32,68,101,109,105,0,0,0,0,97,110,36,103,108,101,115,0,109,97,120,95,105,110,100,101,120,0,0,0,0,0,0,0,32,32,92,112,117,116,40,37,100,44,37,100,41,123,0,0,32,32,68,97,121,115,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,92,102,111,110,116,115,105,122,101,123,37,103,125,123,92,98,97,115,101,108,105,110,101,115,107,105,112,125,92,115,101,108,101,99,116,102,111,110,116,10,0,0,0,0,0,0,0,0,32,108,105,110,101,99,111,108,111,114,0,0,0,0,0,0,120,112,45,62,112,95,99,111,117,110,116,32,61,61,32,121,112,45,62,112,95,99,111,117,110,116,0,0,0,0,0,0,70,53,0,0,0,0,0,0,32,32,125,125,37,10,0,0,32,115,105,122,101,32,37,46,50,102,105,110,44,32,37,46,50,102,105,110,32,0,0,0,32,115,105,122,101,32,37,46,50,102,99,109,44,32,37,46,50,102,99,109,32,0,0,0,32,59,10,0,0,0,0,0,100,117,112,32,48,46,52,50,32,108,101,32,123,52,32,109,117,108,125,32,123,100,117,112,32,48,46,57,50,32,108,101,32,123,45,50,32,109,117,108,32,49,46,56,52,32,97,100,100,125,32,123,48,46,48,56,32,100,105,118,32,49,49,46,53,32,115,117,98,125,32,105,102,101,108,115,101,125,32,105,102,101,108,115,101,0,0,0,32,37,100,0,0,0,0,0,32,116,105,110,121,0,0,0,10,84,104,101,32,102,105,116,32,119,97,115,32,115,116,111,112,112,101,100,32,98,121,32,116,104,101,32,117,115,101,114,32,97,102,116,101,114,32,37,100,32,105,116,101,114,97,116,105,111,110,115,46,10,0,0,32,115,109,97,108,108,0,0,32,114,111,116,97,116,101,0,97,98,115,0,0,0,0,0,48,46,53,0,0,0,0,0,32,100,97,115,104,101,100,0,66,111,111,107,109,97,110,32,76,105,103,104,116,32,73,116,97,108,105,99,0,0,0,0,97,116,0,0,0,0,0,0,109,105,110,95,105,110,100,101,120,0,0,0,0,0,0,0,32,99,111,108,111,114,0,0,109,97,120,95,117,115,105,110,103,32,60,61,32,77,65,88,68,65,84,65,67,79,76,83,0,0,0,0,0,0,0,0,32,32,77,111,110,116,104,115,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,0,0,0,0,0,0,100,36,116,105,99,115,0,0,37,115,37,115,37,115,37,115,0,0,0,0,0,0,0,0,32,108,105,110,101,116,121,112,101,32,37,100,0,0,0,0,70,52,0,0,0,0,0,0,112,36,97,116,116,101,114,110,0,0,0,0,0,0,0,0,117,110,114,101,99,111,103,110,105,122,101,100,32,111,112,116,105,111,110,0,0,0,0,0,102,111,110,116,32,115,105,122,101,32,111,117,116,32,111,102,32,98,111,117,110,100,115,32,91,49,46,46,49,48,48,93,0,0,0,0,0,0,0,0,116,36,105,110,121,0,0,0,115,36,109,97,108,108,0,0,50,42,120,45,48,46,56,52,0,0,0,0,0,0,0,0,114,36,111,116,97,116,101,0,10,77,97,120,105,109,117,109,32,105,116,101,114,97,116,105,111,110,32,99,111,117,110,116,32,40,37,100,41,32,114,101,97,99,104,101,100,46,32,70,105,116,32,115,116,111,112,112,101,100,46,10,0,0,0,0,37,37,32,71,78,85,80,76,79,84,58,32,76,97,84,101,88,32,112,105,99,116,117,114,101,32,117,115,105,110,103,32,69,69,80,73,67,32,109,97,99,114,111,115,10,92,115,101,116,108,101,110,103,116,104,123,92,117,110,105,116,108,101,110,103,116,104,125,123,37,102,112,116,125,10,0,0,0,0,0,92,102,111,111,116,110,111,116,101,115,105,122,101,10,0,0,105,110,116,0,0,0,0,0,92,102,111,110,116,115,105,122,101,123,37,100,125,123,37,103,125,92,115,101,108,101,99,116,102,111,110,116,10,0,0,0,66,111,111,107,109,97,110,32,76,105,103,104,116,0,0,0,97,99,36,116,105,111,110,95,116,97,98,108,101,0,0,0,99,111,103,95,121,0,0,0,92,98,101,103,105,110,123,112,105,99,116,117,114,101,125,40,37,100,44,37,100,41,40,37,100,44,37,100,41,10,0,0,32,32,105,110,116,101,114,118,97,108,115,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,0,0,0,109,36,116,105,99,115,0,0,92,116,104,105,110,108,105,110,101,115,32,92,100,111,116,116,101,100,108,105,110,101,123,50,48,125,0,0,0,0,0,0,45,45,45,101,114,114,111,114,33,45,45,45,10,0,0,0,117,115,101,115,95,97,120,105,115,91,83,69,67,79,78,68,95,89,95,65,88,73,83,93,0,0,0,0,0,0,0,0,70,51,0,0,0,0,0,0,92,116,104,105,110,108,105,110,101,115,32,92,100,97,115,104,108,105,110,101,91,54,48,93,123,50,48,125,0,0,0,0,92,116,104,105,110,108,105,110,101,115,32,92,100,111,116,116,101,100,108,105,110,101,123,49,48,125,0,0,0,0,0,0,92,116,104,105,110,108,105,110,101,115,32,92,100,97,115,104,108,105,110,101,91,57,48,93,123,49,48,125,0,0,0,0,50,32,109,117,108,32,48,46,56,52,32,115,117,98,0,0,92,84,104,105,99,107,108,105,110,101,115,32,92,112,97,116,104,0,0,0,0,0,0,0,92,116,104,105,110,108,105,110,101,115,32,92,112,97,116,104,0,0,0,0,0,0,0,0,70,73,84,95,67,79,78,86,69,82,71,69,68,0,0,0,92,116,104,105,110,108,105,110,101,115,32,92,100,114,97,119,108,105,110,101,91,45,53,48,93,0,0,0,0,0,0,0,92,116,104,105,99,107,108,105,110,101,115,32,92,112,97,116,104,0,0,0,0,0,0,0,69,108,108,105,112,116,105,99,80,105,0,0,0,0,0,0,40,37,117,44,37,117,41,0,65,118,97,110,116,71,97,114,100,101,32,68,101,109,105,32,79,98,108,105,113,117,101,0,97,36,108,108,0,0,0,0,99,111,103,95,120,0,0,0,10,37,115,40,37,117,44,37,117,41,0,0,0,0,0,0,102,108,111,97,116,54,52,0,37,115,40,37,117,44,37,117,41,0,0,0,0,0,0,0,101,108,108,105,112,115,101,115,10,0,0,0,0,0,0,0,117,115,101,115,95,97,120,105,115,91,70,73,82,83,84,95,89,95,65,88,73,83,93,0,70,50,0,0,0,0,0,0,101,36,109,112,116,121,0,0,92,99,111,108,111,114,123,121,101,108,108,111,119,125,10,0,92,99,111,108,111,114,123,99,121,97,110,125,10,0,0,0,112,97,117,115,101,100,0,0,92,99,111,108,111,114,123,109,97,103,101,110,116,97,125,10,0,0,0,0,0,0,0,0,92,99,111,108,111,114,123,103,114,101,101,110,125,10,0,0,120,47,48,46,51,50,45,48,46,55,56,49,50,53,0,0,9,108,111,97,100,112,97,116,104,32,102,114,111,109,32,71,78,85,80,76,79,84,95,76,73,66,32,105,115,32,0,0,92,99,111,108,111,114,123,98,108,117,101,125,10,0,0,0,103,112,95,115,116,114,97,100,100,0,0,0,0,0,0,0,70,73,84,58,32,101,114,114,111,114,32,111,99,99,117,114,114,101,100,32,100,117,114,105,110,103,32,102,105,116,0,0,92,99,111,108,111,114,123,114,101,100,125,10,0,0,0,0,37,56,46,51,103,0,0,0,116,105,109,101,32,118,97,108,117,101,32,111,117,116,32,111,102,32,114,97,110,103,101,0,92,99,111,108,111,114,123,98,108,97,99,107,125,10,0,0,69,108,108,105,112,116,105,99,69,0,0,0,0,0,0,0,125,125,125,10,0,0,0,0,65,118,97,110,116,71,97,114,100,101,32,68,101,109,105,0,118,36,97,114,105,97,98,108,101,115,0,0,0,0,0,0,105,110,100,101,120,95,109,97,120,95,121,0,0,0,0,0,37,99,92,92,0,0,0,0,102,108,111,97,116,51,50,0,32,32,32,32,111,102,102,115,101,116,32,0,0,0,0,0,97,117,36,116,111,102,114,101,113,0,0,0,0,0,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,91,108,116,93,123,92,115,104,111,114,116,115,116,97,99,107,123,0,99,105,114,99,108,101,115,10,0,0,0,0,0,0,0,0,97,108,108,32,112,111,105,110,116,115,32,121,50,32,118,97,108,117,101,32,117,110,100,101,102,105,110,101,100,33,0,0,70,49,0,0,0,0,0,0,116,114,97,110,115,36,112,97,114,101,110,116,0,0,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,91,108,93,123,92,115,104,111,114,116,115,116,97,99,107,123,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,91,108,98,93,123,92,115,104,111,114,116,115,116,97,99,107,123,0,77,111,117,115,105,110,103,32,110,111,116,32,97,99,116,105,118,101,0,0,0,0,0,0,99,112,56,53,48,0,0,0,37,115,125,125,125,10,0,0,32,120,108,111,119,32,120,104,105,103,104,32,121,108,111,119,32,121,104,105,103,104,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,91,108,116,93,123,92,114,111,116,97,116,101,98,111,120,91,111,114,105,103,105,110,61,99,93,123,57,48,125,123,0,0,0,0,48,46,51,50,32,100,105,118,32,48,46,55,56,49,50,53,32,115,117,98,0,0,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,91,108,93,123,92,114,111,116,97,116,101,98,111,120,91,111,114,105,103,105,110,61,99,93,123,57,48,125,123,0,0,0,0,0,108,111,103,102,105,108,101,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,91,108,98,93,123,92,114,111,116,97,116,101,98,111,120,91,111,114,105,103,105,110,61,99,93,123,57,48,125,123,0,0,0,0,37,115,125,125,10,0,0,0,69,108,108,105,112,116,105,99,75,0,0,0,0,0,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,91,114,93,123,0,0,0,0,0,0,65,118,97,110,116,71,97,114,100,101,32,66,111,111,107,32,79,98,108,105,113,117,101,0,116,36,101,114,109,105,110,97,108,0,0,0,0,0,0,0,105,110,100,101,120,95,109,97,120,95,120,0,0,0,0,0,100,111,0,0,0,0,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,123,0,117,105,110,116,54,52,0,0,32,97,110,100,32,97,114,101,32,110,111,116,32,114,111,116,97,116,101,100,44,10,9,0,101,120,112,101,99,116,101,100,32,102,111,114,109,97,116,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,91,108,93,123,0,0,0,0,0,0,114,103,98,105,109,97,103,101,10,0,0,0,0,0,0,0,97,108,108,32,112,111,105,110,116,115,32,121,32,118,97,108,117,101,32,117,110,100,101,102,105,110,101,100,33,0,0,0,75,80,95,57,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,99,114,105,112,116,115,116,121,108,101,92,115,116,97,114,36,125,0,0,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,99,114,105,112,116,115,116,121,108,101,92,116,114,105,97,110,103,108,101,36,125,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,99,114,105,112,116,115,116,121,108,101,92,116,105,109,101,115,36,125,0,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,99,114,105,112,116,115,116,121,108,101,92,66,111,120,36,125,0,124,40,51,120,45,50,41,47,50,124,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,99,114,105,112,116,115,116,121,108,101,32,43,36,125,0,0,0,78,111,32,102,105,116,116,97,98,108,101,32,112,97,114,97,109,101,116,101,114,115,33,10,0,0,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,99,114,105,112,116,115,116,121,108,101,92,68,105,97,109,111,110,100,36,125,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,116,121,108,101,92,115,116,97,114,36,125,0,0,0,0,0,0,116,97,110,104,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,116,121,108,101,92,116,114,105,97,110,103,108,101,36,125,0,0,65,118,97,110,116,71,97,114,100,101,32,66,111,111,107,0,115,36,101,116,0,0,0,0,105,110,100,101,120,95,109,105,110,95,121,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,116,121,108,101,92,116,105,109,101,115,36,125,0,0,0,0,0,105,110,116,54,52,0,0,0,32,105,110,32,50,68,32,109,111,100,101,44,32,116,101,114,109,105,110,97,108,32,112,101,114,109,105,116,116,105,110,103,44,10,9,0,0,0,0,0,99,111,109,36,109,101,110,116,115,99,104,97,114,115,0,0,101,120,112,101,99,116,105,110,103,32,101,120,112,111,110,101,110,116,0,0,0,0,0,0,102,111,114,109,97,116,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,116,121,108,101,92,66,111,120,36,125,0,0,0,0,0,0,0,105,109,97,103,101,10,0,0,101,100,102,0,0,0,0,0,117,115,101,115,95,97,120,105,115,91,83,69,67,79,78,68,95,88,95,65,88,73,83,93,0,0,0,0,0,0,0,0,75,80,95,56,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,116,121,108,101,32,43,36,125,0,99,111,110,116,111,117,114,32,100,50,120,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,99,114,105,112,116,115,116,121,108,101,92,68,105,97,109,111,110,100,36,125,0,0,0,35,32,115,101,116,32,111,117,116,112,117,116,10,0,0,0,92,99,105,114,99,108,101,42,123,50,52,125,0,0,0,0,112,109,51,100,95,112,108,111,116,45,62,113,117,97,100,114,97,110,103,108,101,115,0,0,92,99,105,114,99,108,101,42,123,49,56,125,0,0,0,0,49,46,53,32,109,117,108,32,49,32,115,117,98,32,97,98,115,0,0,0,0,0,0,0,92,99,105,114,99,108,101,42,123,49,50,125,0,0,0,0,45,112,101,114,115,105,115,116,0,0,0,0,0,0,0,0,78,117,109,98,101,114,32,111,102,32,100,97,116,97,32,112,111,105,110,116,115,32,115,109,97,108,108,101,114,32,116,104,97,110,32,110,117,109,98,101,114,32,111,102,32,112,97,114,97,109,101,116,101,114,115,0,92,99,105,114,99,108,101,123,50,52,125,0,0,0,0,0,92,99,105,114,99,108,101,123,49,56,125,0,0,0,0,0,99,111,115,104,0,0,0,0,92,99,105,114,99,108,101,123,49,50,125,0,0,0,0,0,84,105,109,101,115,32,66,111,108,100,32,73,116,97,108,105,99,0,0,0,0,0,0,0,102,36,117,110,99,116,105,111,110,115,0,0,0,0,0,0,105,110,100,101,120,95,109,105,110,95,120,0,0,0,0,0,84,111,111,32,109,97,110,121,32,108,101,118,101,108,115,32,111,102,32,110,101,115,116,101,100,32,109,97,99,114,111,115,0,0,0,0,0,0,0,0,77,79,85,83,69,95,82,85,76,69,82,95,88,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,115,116,97,114,36,125,0,0,117,105,110,116,51,50,0,0,32,98,121,32,37,100,0,0,101,120,112,101,99,116,101,100,32,102,111,110,116,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,116,114,105,97,110,103,108,101,36,125,0,0,0,0,0,0,108,97,98,101,108,115,10,0,99,108,111,115,101,0,0,0,37,105,0,0,0,0,0,0,117,115,101,115,95,97,120,105,115,91,70,73,82,83,84,95,88,95,65,88,73,83,93,0,83,105,110,103,117,108,97,114,32,109,97,116,114,105,120,32,105,110,32,76,85,45,68,69,67,79,77,80,0,0,0,0,115,109,111,111,116,104,95,98,111,120,0,0,0,0,0,0,115,112,108,105,110,101,32,114,105,103,104,116,32,115,105,100,101,0,0,0,0,0,0,0,75,80,95,55,0,0,0,0,100,117,112,108,105,99,97,116,101,100,32,97,114,103,117,109,101,110,116,115,32,105,110,32,115,116,121,108,101,32,115,112,101,99,105,102,105,99,97,116,105,111,110,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,116,105,109,101,115,36,125,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,102,95,115,117,109,32,101,120,112,101,99,116,115,32,97,114,103,117,109,101,110,116,32,40,118,97,114,110,97,109,101,41,32,111,102,32,116,121,112,101,32,115,116,114,105,110,103,46,0,0,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,66,111,120,36,125,0,0,0,37,115,32,97,120,105,115,32,114,97,110,103,101,32,117,110,100,101,102,105,110,101,100,32,111,114,32,111,118,101,114,102,108,111,119,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,43,36,125,0,0,0,0,0,0,92,109,97,107,101,98,111,120,40,48,44,48,41,123,36,92,68,105,97,109,111,110,100,36,125,0,0,0,0,0,0,0,124,40,51,120,45,49,41,47,50,124,0,0,0,0,0,0,92,112,117,116,40,37,100,44,37,100,41,123,37,115,125,10,0,0,0,0,0,0,0,0,37,100,32,37,100,32,37,102,0,0,0,0,0,0,0,0,92,101,110,100,123,112,105,99,116,117,114,101,125,10,0,0,115,105,110,104,0,0,0,0,92,102,111,111,116,110,111,116,101,115,105,122,101,37,10,0,84,105,109,101,115,32,66,111,108,100,0,0,0,0,0,0,104,97,110,110,0,0,0,0,37,115,37,115,9,37,108,100,10,0,0,0,0,0,0,0,116,114,105,109,32,116,111,112,32,38,32,98,111,116,116,111,109,32,109,97,114,103,105,110,115,0,0,0,0,0,0,0,105,110,116,51,50,0,0,0,32,114,111,116,97,116,101,100,0,0,0,0,0,0,0,0,116,114,105,109,32,98,111,116,116,111,109,32,109,97,114,103,105,110,0,0,0,0,0,0,112,109,51,100,10,0,0,0,98,117,116,116,111,110,51,0,75,80,95,54,0,0,0,0,78,111,32,112,111,105,110,116,105,110,116,101,114,118,97,108,32,115,112,101,99,105,102,105,101,114,32,97,108,108,111,119,101,100,44,32,104,101,114,101,0,0,0,0,0,0,0,0,116,114,105,109,32,116,111,112,32,109,97,114,103,105,110,0,116,114,105,109,32,108,101,102,116,32,38,32,114,105,103,104,116,32,109,97,114,103,105,110,115,0,0,0,0,0,0,0,99,104,97,114,36,97,99,116,101,114,0,0,0,0,0,0,116,114,105,109,32,114,105,103,104,116,32,109,97,114,103,105,110,0,0,0,0,0,0,0,116,114,105,109,32,108,101,102,116,32,109,97,114,103,105,110,0,0,0,0,0,0,0,0,49,46,53,32,109,117,108,32,46,53,32,115,117,98,32,97,98,115,0,0,0,0,0,0,37,37,32,0,0,0,0,0,110,111,32,112,97,114,97,109,101,116,101,114,32,115,112,101,99,105,102,105,101,100,0,0,37,10,0,0,0,0,0,0,92,115,112,101,99,105,97,108,123,100,116,32,37,102,125,37,37,10,0,0,0,0,0,0,97,116,97,110,50,0,0,0,92,115,112,101,99,105,97,108,123,100,97,32,37,102,125,37,37,10,0,0,0,0,0,0,84,105,109,101,115,32,73,116,97,108,105,99,0,0,0,0,98,111,120,0,0,0,0,0,109,97,120,0,0,0,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,37,115,123,92,115,104,111,114,116,115,116,97,99,107,123,37,115,125,125,125,10,0,0,0,0,0,117,119,111,114,100,0,0,0,102,111,114,109,97,116,32,34,37,115,34,0,0,0,0,0,110,111,114,97,110,103,101,36,108,105,109,105,116,101,100,0,91,108,116,93,0,0,0,0,98,111,120,112,108,111,116,10,0,0,0,0,0,0,0,0,98,117,116,116,111,110,50,0,97,108,108,32,112,111,105,110,116,115,32,117,110,100,101,102,105,110,101,100,33,0,0,0,75,80,95,53,0,0,0,0,112,105,0,0,0,0,0,0,91,108,98,93,0,0,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,37,115,123,37,115,125,125,10,0,0,123,92,109,97,107,101,98,111,120,40,48,44,48,41,37,115,125,10,0,0,0,0,0,0,120,116,105,99,115,0,0,0,92,112,117,116,40,37,100,44,37,100,41,0,0,0,0,0,40,51,120,45,50,41,47,50,0,0,0,0,0,0,0,0,92,115,112,101,99,105,97,108,123,112,97,32,37,100,32,37,100,125,0,0,0,0,0,0,102,105,116,116,101,100,32,112,97,114,97,109,101,116,101,114,115,32,105,110,105,116,105,97,108,105,122,101,100,32,119,105,116,104,32,99,117,114,114,101,110,116,32,118,97,114,105,97,98,108,101,32,118,97,108,117,101,115,10,10,0,0,0,0,92,115,112,101,99,105,97,108,123,102,112,125,37,10,0,0,92,115,112,101,99,105,97,108,123,97,114,32,48,32,48,32,37,100,32,37,100,32,48,32,55,125,0,0,0,0,0,0,97,116,97,110,0,0,0,0,92,115,112,101,99,105,97,108,123,112,110,32,37,100,125,0,84,105,109,101,115,32,82,111,109,97,110,0,0,0,0,0,99,97,117,99,104,121,0,0,117,112,95,113,117,97,114,116,105,108,101,0,0,0,0,0,92,115,112,101,99,105,97,108,123,115,104,32,37,102,125,0,117,105,110,116,49,54,0,0,106,117,115,116,105,102,105,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,44,32,0,0,0,0,0,0,0,114,97,110,103,101,36,108,105,109,105,116,101,100,0,0,0,125,37,37,10,0,0,0,0,99,97,110,100,108,101,115,116,105,99,107,115,10,0,0,0,98,117,116,116,111,110,49,0,78,111,32,100,97,116,97,32,105,110,32,112,108,111,116,0,75,80,95,52,0,0,0,0,112,111,105,110,116,105,36,110,116,101,114,118,97,108,0,0,92,114,117,108,101,123,46,49,112,116,125,123,46,49,112,116,125,0,0,0,0,0,0,0,92,112,117,116,40,37,100,44,37,100,41,123,0,0,0,0,92,112,117,116,40,37,100,44,37,100,41,123,92,115,112,101,99,105,97,108,123,101,109,58,108,105,110,101,116,111,125,125,10,0,0,0,0,0,0,0,92,112,117,116,40,37,100,44,37,100,41,123,92,115,112,101,99,105,97,108,123,101,109,58,109,111,118,101,116,111,125,125,10,0,0,0,0,0,0,0,69,68,70,95,66,105,110,97,114,121,70,105,108,101,78,97,109,101,0,0,0,0,0,0,49,46,53,32,109,117,108,32,49,32,115,117,98,0,0,0,92,112,117,116,40,37,46,49,102,44,37,46,49,102,41,123,92,114,117,108,101,91,37,46,51,102,112,116,93,123,37,46,51,102,112,116,125,123,37,46,51,102,112,116,125,125,10,0,79,117,116,32,111,102,32,109,101,109,111,114,121,32,105,110,32,102,105,116,58,32,116,111,111,32,109,97,110,121,32,112,97,114,97,109,101,116,101,114,115,63,0,0,0,0,0,0,92,112,117,116,40,37,46,49,102,44,37,46,49,102,41,123,37,115,125,10,0,0,0,0,92,112,117,116,40,37,46,50,102,44,37,117,41,123,92,114,117,108,101,123,37,46,51,102,112,116,125,123,37,46,51,102,112,116,125,125,10,0,0,0,97,99,111,115,0,0,0,0,112,111,112,32,48,46,53,0,92,109,117,108,116,105,112,117,116,40,37,46,50,102,44,37,46,50,102,41,40,37,46,51,102,44,37,46,51,102,41,123,50,125,123,92,114,117,108,101,123,37,46,51,102,112,116,125,123,37,46,51,102,112,116,125,125,10,0,0,0,0,0,0,77,117,115,116,32,115,101,116,32,111,117,116,112,117,116,32,116,111,32,97,32,102,105,108,101,32,111,114,32,112,117,116,32,97,108,108,32,109,117,108,116,105,112,108,111,116,32,99,111,109,109,97,110,100,115,32,111,110,32,111,110,101,32,105,110,112,117,116,32,108,105,110,101,0,0,0,0,0,0,0,101,120,112,0,0,0,0,0,109,101,100,105,97,110,0,0,92,109,117,108,116,105,112,117,116,40,37,46,50,102,44,37,46,50,102,41,40,37,46,51,102,44,37,46,51,102,41,123,37,117,125,123,92,114,117,108,101,123,37,46,51,102,112,116,125,123,37,46,51,102,112,116,125,125,10,0,0,0,0,0,99,101,110,116,101,114,32,106,117,115,116,105,102,105,101,100,44,32,0,0,0,0,0,0,97,117,116,111,106,36,117,115,116,105,102,121,0,0,0,0,92,112,117,116,40,37,117,44,37,46,50,102,41,123,92,114,117,108,101,123,37,46,51,102,112,116,125,123,37,46,51,102,112,116,125,125,10,0,0,0,102,105,110,97,110,99,101,98,97,114,115,10,0,0,0,0,97,110,121,0,0,0,0,0,69,120,112,101,99,116,105,110,103,32,39,115,117,109,32,91,60,118,97,114,62,32,61,32,60,115,116,97,114,116,62,58,60,101,110,100,62,93,32,60,101,120,112,114,101,115,115,105,111,110,62,39,10,0,0,0,75,80,95,51,0,0,0,0,78,111,32,112,111,105,110,116,115,105,122,101,32,115,112,101,99,105,102,105,101,114,32,97,108,108,111,119,101,100,44,32,104,101,114,101,0,0,0,0,92,117,115,101,98,111,120,123,92,112,108,111,116,112,111,105,110,116,125,0,0,0,0,0,92,112,117,116,40,37,117,44,37,117,41,123,37,115,125,10,0,0,0,0,0,0,0,0,115,101,99,36,111,110,100,0,92,112,117,116,40,37,100,44,37,100,41,123,92,118,101,99,116,111,114,40,37,100,44,37,100,41,123,48,125,125,10,0,92,112,117,116,40,37,100,44,37,100,41,123,92,37,115,40,37,100,44,37,100,41,123,37,100,125,125,10,0,0,0,0,40,51,120,45,49,41,47,50,0,0,0,0,0,0,0,0,92,112,117,116,40,37,100,44,37,100,41,123,92,37,115,40,37,100,44,48,41,123,37,100,125,125,10,0,0,0,0,0,102,105,116,32,112,97,114,97,109,32,114,101,115,105,122,101,0,0,0,0,0,0,0,0,108,105,110,101,0,0,0,0,118,101,99,116,111,114,0,0,32,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,97,115,105,110,0,0,0,0,92,112,117,116,40,37,100,44,37,100,41,123,92,37,115,40,48,44,37,100,41,123,37,100,125,125,10,0,0,0,0,0,84,104,105,115,32,116,101,114,109,105,110,97,108,32,100,111,101,115,32,110,111,116,32,115,117,112,112,111,114,116,32,109,117,108,116,105,112,108,111,116,0,0,0,0,0,0,0,0,103,97,117,115,115,0,0,0,108,111,95,113,117,97,114,116,105,108,101,0,0,0,0,0,117,36,110,105,116,0,0,0,114,105,103,104,116,32,106,117,115,116,105,102,105,101,100,44,32,0,0,0,0,0,0,0,105,110,116,49,54,0,0,0,114,105,36,103,104,116,0,0,110,111,36,104,97,99,107,116,101,120,116,0,0,0,0,0,118,101,99,116,111,114,10,0,115,117,114,102,97,99,101,0,107,101,121,36,112,114,101,115,115,0,0,0,0,0,0,0,39,93,39,32,101,120,112,101,99,116,101,100,0,0,0,0,75,80,95,50,0,0,0,0,37,32,71,78,85,80,76,79,84,58,32,76,97,84,101,88,32,112,105,99,116,117,114,101,32,117,115,105,110,103,32,80,83,84,82,73,67,75,83,32,109,97,99,114,111,115,10,0,92,99,97,116,99,111,100,101,96,64,61,49,50,10,92,102,105,10,92,101,110,100,112,115,112,105,99,116,117,114,101,10,0,0,0,0,0,0,0,0,102,105,114,36,115,116,0,0,92,112,115,112,105,99,116,117,114,101,40,37,102,44,37,102,41,40,37,102,44,37,102,41,10,92,105,102,120,92,110,111,102,105,103,115,92,117,110,100,101,102,105,110,101,100,10,92,99,97,116,99,111,100,101,96,64,61,49,49,10,10,0,0,92,112,115,115,101,116,123,117,110,105,116,61,53,46,48,105,110,44,120,117,110,105,116,61,53,46,48,105,110,44,121,117,110,105,116,61,51,46,48,105,110,125,10,0,0,0,0,0,49,46,53,32,109,117,108,32,46,53,32,115,117,98,0,0,112,97,114,97,109,101,116,101,114,32,102,105,108,101,32,37,115,32,99,111,117,108,100,32,110,111,116,32,98,101,32,114,101,97,100,0,0,0,0,0,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,65,114,114,111,119,125,123,112,115,108,105,110,101,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,125,10,92,99,97,116,99,111,100,101,96,64,61,49,50,10,10,92,102,105,10,0,0,0,0,0,115,121,110,116,97,120,32,101,114,114,111,114,32,105,110,32,112,97,114,97,109,101,116,101,114,32,102,105,108,101,0,0,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,80,108,117,115,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,43,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,83,113,117,97,114,101,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,115,113,117,97,114,101,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,67,105,114,99,108,101,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,111,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,84,114,105,97,110,103,108,101,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,116,114,105,97,110,103,108,101,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,80,101,110,116,97,103,111,110,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,112,101,110,116,97,103,111,110,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,70,105,108,108,115,113,117,97,114,101,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,115,113,117,97,114,101,42,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,70,105,108,108,99,105,114,99,108,101,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,42,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,70,105,108,108,116,114,105,97,110,103,108,101,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,116,114,105,97,110,103,108,101,42,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83])
+.concat([84,64,70,105,108,108,112,101,110,116,97,103,111,110,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,112,101,110,116,97,103,111,110,42,125,10,0,0,0,0,0,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,68,105,97,109,111,110,100,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,115,113,117,97,114,101,44,100,111,116,97,110,103,108,101,61,52,53,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,70,105,108,108,100,105,97,109,111,110,100,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,115,113,117,97,114,101,42,44,100,111,116,97,110,103,108,101,61,52,53,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,67,114,111,115,115,125,123,112,115,100,111,116,115,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,44,100,111,116,115,116,121,108,101,61,43,44,100,111,116,97,110,103,108,101,61,52,53,125,10,0,0,0,0,0,0,0,0,116,97,110,0,0,0,0,0,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,66,111,114,100,101,114,125,123,112,115,108,105,110,101,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,53,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,65,120,101,115,125,123,112,115,108,105,110,101,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,53,44,108,105,110,101,115,116,121,108,101,61,100,111,116,116,101,100,44,100,111,116,115,101,112,61,46,48,48,52,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,83,111,108,105,100,125,123,112,115,108,105,110,101,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,53,44,108,105,110,101,115,116,121,108,101,61,115,111,108,105,100,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,68,97,115,104,101,100,125,123,112,115,108,105,110,101,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,53,44,108,105,110,101,115,116,121,108,101,61,100,97,115,104,101,100,44,100,97,115,104,61,46,48,49,32,46,48,49,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,68,111,116,116,101,100,125,123,112,115,108,105,110,101,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,50,53,44,108,105,110,101,115,116,121,108,101,61,100,111,116,116,101,100,44,100,111,116,115,101,112,61,46,48,48,56,125,10,92,110,101,119,112,115,111,98,106,101,99,116,123,80,83,84,64,76,111,110,103,68,97,115,104,125,123,112,115,108,105,110,101,125,123,108,105,110,101,119,105,100,116,104,61,46,48,48,49,53,44,108,105,110,101,115,116,121,108,101,61,100,97,115,104,101,100,44,100,97,115,104,61,46,48,50,32,46,48,49,125,10,0,0,0,0,0,0,0,115,112,108,105,110,101,36,115,0,0,0,0,0,0,0,0,109,105,110,0,0,0,0,0,37,32,68,101,102,105,110,101,32,110,101,119,32,80,83,84,32,111,98,106,101,99,116,115,44,32,105,102,32,110,111,116,32,97,108,114,101,97,100,121,32,100,101,102,105,110,101,100,10,92,105,102,120,92,80,83,84,108,111,97,100,101,100,92,117,110,100,101,102,105,110,101,100,10,92,100,101,102,92,80,83,84,108,111,97,100,101,100,123,116,125,10,92,112,115,115,101,116,123,97,114,114,111,119,115,105,122,101,61,46,48,49,32,51,46,50,32,49,46,52,32,46,51,125,10,92,112,115,115,101,116,123,100,111,116,115,105,122,101,61,46,48,49,125,10,92,99,97,116,99,111,100,101,96,64,61,49,49,10,10,0,0,0,0,0,0,0,0,108,101,102,116,32,106,117,115,116,105,102,105,101,100,44,32,0,0,0,0,0,0,0,0,117,98,121,116,101,0,0,0,92,80,83,84,64,76,111,110,103,68,97,115,104,0,0,0,104,105,115,116,101,112,115,10,0,0,0,0,0,0,0,0,105,110,100,101,120,101,115,32,108,117,0,0,0,0,0,0,120,50,32,114,97,110,103,101,32,105,115,32,105,110,118,97,108,105,100,0,0,0,0,0,39,58,39,32,101,120,112,101,99,116,101,100,0,0,0,0,75,80,95,49,0,0,0,0,112,115,0,0,0,0,0,0,92,80,83,84,64,68,111,116,116,101,100,0,0,0,0,0,92,80,83,84,64,68,97,115,104,101,100,0,0,0,0,0,92,80,83,84,64,83,111,108,105,100,0,0,0,0,0,0,92,80,83,84,64,65,120,101,115,0,0,0,0,0,0,0,124,51,120,45,50,124,0,0,34,37,115,34,32,0,0,0,92,80,83,84,64,66,111,114,100,101,114,0,0,0,0,0,59,0,0,0,0,0,0,0,70,73,88,69,68,58,32,32,37,115,10,0,0,0,0,0,40,37,46,52,102,44,37,46,52,102,41,10,0,0,0,0,105,108,108,101,103,97,108,32,100,97,121,32,111,102,32,109,111,110,116,104,0,0,0,0,36,37,102,32,92,116,105,109,101,115,32,49,48,94,123,37,100,125,36,0,0,0,0,0,99,111,115,0,0,0,0,0,36,37,102,36,0,0,0,0,109,112,32,116,105,116,108,101,0,0,0,0,0,0,0,0,113,110,111,114,109,0,0,0,115,117,109,95,115,113,0,0,36,37,100,32,92,116,105,109,101,115,32,49,48,94,123,37,100,125,36,0,0,0,0,0,10,9,32,32,108,97,98,101,108,115,32,97,114,101,32,0,117,105,110,116,56,0,0,0,36,37,100,36,0,0,0,0,102,115,116,101,112,115,10,0,109,97,116,114,105,120,32,58,32,116,104,105,110,32,112,108,97,116,101,32,115,112,108,105,110,101,115,32,50,100,0,0,117,110,101,120,112,101,99,116,101,100,32,125,0,0,0,0,42,42,0,0,0,0,0,0,75,80,95,48,0,0,0,0,112,111,105,110,116,115,36,105,122,101,0,0,0,0,0,0,36,49,48,94,123,37,100,125,36,0,0,0,0,0,0,0,48,0,0,0,0,0,0,0,99,112,52,51,55,0,0,0,123,37,115,125,10,0,0,0,32,121,108,111,119,32,121,104,105,103,104,0,0,0,0,0,40,37,46,52,102,44,37,46,52,102,41,0,0,0,0,0,51,32,109,117,108,32,50,32,115,117,98,32,97,98,115,0,112,117,115,104,100,0,0,0,123,76,125,0,0,0,0,0,99,111,117,108,100,32,110,111,116,32,114,101,97,100,32,112,97,114,97,109,101,116,101,114,45,102,105,108,101,32,34,37,115,34,0,0,0,0,0,0,91,114,93,0,0,0,0,0,91,108,93,0,0,0,0,0,115,105,110,0,0,0,0,0,92,114,112,117,116,0,0,0,71,80,86,65,76,95,77,85,76,84,73,80,76,79,84,0,99,110,36,111,114,109,97,108,0,0,0,0,0,0,0,0,115,117,109,0,0,0,0,0,99,108,36,101,97,114,0,0,92,80,83,84,64,70,105,108,108,112,101,110,116,97,103,111,110,0,0,0,0,0,0,0,10,9,32,32,116,105,99,115,32,97,114,101,32,108,105,109,105,116,101,100,32,116,111,32,100,97,116,97,32,114,97,110,103,101,0,0,0,0,0,0,98,121,116,101,0,0,0,0,110,111,114,111,36,116,97,116,101,0,0,0,0,0,0,0,78,101,101,100,32,48,32,116,111,32,50,32,117,115,105,110,103,32,115,112,101,99,115,32,102,111,114,32,115,116,97,116,115,32,99,111,109,109,97,110,100,0,0,0,0,0,0,0,92,80,83,84,64,70,105,108,108,99,105,114,99,108,101,0,115,116,101,112,115,10,0,0,116,104,105,110,32,112,108,97,116,101,32,115,112,108,105,110,101,115,32,105,110,32,100,103,114,105,100,51,100,0,0,0,101,120,112,101,99,116,105,110,103,32,123,119,104,105,108,101,45,99,108,97,117,115,101,125,0,0,0,0,0,0,0,0,105,110,118,97,108,105,100,32,101,120,112,114,101,115,115,105,111,110,32,0,0,0,0,0,75,80,95,68,105,118,105,100,101,0,0,0,0,0,0,0,78,111,32,112,111,105,110,116,116,121,112,101,32,115,112,101,99,105,102,105,101,114,32,97,108,108,111,119,101,100,44,32,104,101,114,101,0,0,0,0,92,80,83,84,64,70,105,108,108,116,114,105,97,110,103,108,101,0,0,0,0,0,0,0,92,80,83,84,64,70,105,108,108,115,113,117,97,114,101,0,92,80,83,84,64,70,105,108,108,100,105,97,109,111,110,100,0,0,0,0,0,0,0,0,92,80,83,84,64,80,101,110,116,97,103,111,110,0,0,0,124,51,120,45,49,124,0,0,92,80,83,84,64,84,114,105,97,110,103,108,101,0,0,0,102,105,116,116,101,100,32,112,97,114,97,109,101,116,101,114,115,32,97,110,100,32,105,110,105,116,105,97,108,32,118,97,108,117,101,115,32,102,114,111,109,32,102,105,108,101,58,32,37,115,10,10,0,0,0,0,92,80,83,84,64,67,105,114,99,108,101,0,0,0,0,0,92,80,83,84,64,67,114,111,115,115,0,0,0,0,0,0,99,111,110,106,103,0,0,0,92,80,83,84,64,83,113,117,97,114,101,0,0,0,0,0,105,110,118,97,108,105,100,32,111,114,32,100,117,112,108,105,99,97,116,101,32,111,112,116,105,111,110,0,0,0,0,0,107,36,100,101,110,115,105,116,121,0,0,0,0,0,0,0,115,116,100,100,101,118,0,0,92,80,83,84,64,80,108,117,115,0,0,0,0,0,0,0,32,97,110,100,32,109,105,114,114,111,114,101,100,32,111,110,32,111,112,112,111,115,105,116,101,32,98,111,114,100,101,114,0,0,0,0,0,0,0,0,105,110,116,56,0,0,0,0,92,80,83,84,64,68,105,97,109,111,110,100,0,0,0,0,105,110,112,117,116,32,108,105,110,101,32,99,111,112,121,0,114,111,36,116,97,116,101,0,32,37,100,10,0,0,0,0,98,111,120,120,121,101,114,114,111,114,98,97,114,115,10,0,71,114,105,100,100,105,110,103,32,111,102,32,116,104,101,32,99,111,108,111,114,32,99,111,108,117,109,110,32,105,115,32,110,111,116,32,105,109,112,108,101,109,101,110,116,101,100,0,101,120,112,101,99,116,105,110,103,32,123,100,111,45,99,108,97,117,115,101,125,0,0,0,98,105,110,0,0,0,0,0,83,107,105,112,112,105,110,103,32,100,97,116,97,32,102,105,108,101,32,119,105,116,104,32,110,111,32,118,97,108,105,100,32,112,111,105,110,116,115,0,37,115,40,37,46,52,102,44,37,46,52,102,41,10,0,0,75,80,95,68,101,99,105,109,97,108,0,0,0,0,0,0,32,43,37,100,10,0,0,0,92,113,100,105,115,107,40,37,46,52,102,44,37,46,52,102,41,123,37,46,52,102,125,10,0,0,0,0,0,0,0,0,99,111,110,116,111,117,114,32,100,101,108,116,97,95,116,0,32,40,117,110,100,101,102,105,110,101,100,41,10,0,0,0,97,100,100,95,116,105,99,95,117,115,101,114,58,32,108,105,115,116,32,115,111,114,116,32,101,114,114,111,114,0,0,0,123,45,62,125,0,0,0,0,35,32,115,101,116,32,111,117,116,112,117,116,32,39,37,115,39,10,0,0,0,0,0,0,32,37,99,32,100,117,109,109,121,10,0,0,0,0,0,0,92,80,83,84,64,65,114,114,111,119,37,115,40,37,46,52,102,44,37,46,52,102,41,40,37,46,52,102,44,37,46,52,102,41,10,0,0,0,0,0,102,117,110,99,116,105,111,110,32,116,111,32,112,108,111,116,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,0,32,37,115,10,0,0,0,0,51,32,109,117,108,32,49,32,115,117,98,32,97,98,115,0,92,100,101,102,92,112,111,108,121,112,109,73,73,73,100,35,49,123,92,112,115,112,111,108,121,103,111,110,91,108,105,110,101,115,116,121,108,101,61,110,111,110,101,44,102,105,108,108,115,116,121,108,101,61,115,111,108,105,100,44,102,105,108,108,99,111,108,111,114,61,80,83,84,64,67,79,76,79,82,35,49,93,125,10,10,0,0,0,10,9,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,85,115,97,103,101,58,32,103,110,117,112,108,111,116,32,91,79,80,84,73,79,78,93,46,46,46,32,91,70,73,76,69,93,10,32,32,45,86,44,32,45,45,118,101,114,115,105,111,110,10,32,32,45,104,44,32,45,45,104,101,108,112,10,32,32,45,112,32,32,45,45,112,101,114,115,105,115,116,10,32,32,45,100,32,32,45,45,100,101,102,97,117,108,116,45,115,101,116,116,105,110,103,115,10,32,32,45,101,32,32,34,99,111,109,109,97,110,100,49,59,32,99,111,109,109,97,110,100,50,59,32,46,46,46,34,10,103,110,117,112,108,111,116,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,10,0,0,0,0,0,0,0,92,110,101,119,114,103,98,99,111,108,111,114,123,80,83,84,64,67,79,76,79,82,37,100,125,123,37,103,32,37,103,32,37,103,125,10,0,0,0,0,102,105,116,32,112,97,114,97,109,0,0,0,0,0,0,0,32,40,102,105,120,109,97,120,41,0,0,0,0,0,0,0,112,101,114,109,95,97,116,0,92,110,101,119,103,114,97,121,123,80,83,84,64,67,79,76,79,82,37,100,125,123,37,103,125,10,0,0,0,0,0,0,32,40,102,105,120,109,105,110,41,0,0,0,0,0,0,0,112,115,116,114,105,99,107,115,58,32,80,97,108,101,116,116,101,32,117,115,101,100,32,98,101,102,111,114,101,32,115,101,116,33,10,0,0,0,0,0,97,114,103,0,0,0,0,0,32,40,109,97,120,41,0,0,101,120,112,101,99,116,105,110,103,32,60,121,111,102,102,115,101,116,62,0,0,0,0,0,40,37,46,52,103,44,37,46,52,103,41,0,0,0,0,0,99,117,109,36,117,108,97,116,105,118,101,0,0,0,0,0,109,101,97,110,0,0,0,0,109,111,117,115,101,46,99,0,103,110,117,112,108,111,116,62,32,0,0,0,0,0,0,0,32,40,109,105,110,41,0,0,111,110,32,98,111,114,100,101,114,0,0,0,0,0,0,0,108,102,0,0,0,0,0,0,92,112,111,108,121,112,109,73,73,73,100,123,37,100,125,0,9,37,115,58,32,37,115,37,115,37,115,37,115,37,115,44,32,0,0,0,0,0,0,0,98,111,120,101,114,114,111,114,98,97,114,115,10,0,0,0,78,111,32,117,115,97,98,108,101,32,100,97,116,97,32,105,110,32,116,104,105,115,32,112,108,111,116,32,116,111,32,97,117,116,111,45,115,99,97,108,101,32,97,120,105,115,32,114,97,110,103,101,0,0,0,0,99,97,108,108,95,97,114,103,99,32,62,61,32,48,32,38,38,32,99,97,108,108,95,97,114,103,99,32,60,61,32,57,0,0,0,0,0,0,0,0,101,108,115,101,32,119,105,116,104,111,117,116,32,105,102,0,78,101,101,100,32,117,115,105,110,103,32,115,112,101,99,32,102,111,114,32,121,32,116,105,109,101,32,100,97,116,97,0,99,111,108,117,109,110,0,0,83,105,110,103,117,108,97,114,32,109,97,116,114,105,120,32,105,110,32,73,110,118,101,114,116,95,82,116,82,0,0,0,99,98,111,120,0,0,0,0,115,112,108,105,110,101,32,104,101,108,112,32,109,97,116,114,105,120,0,0,0,0,0,0,37,37,32,71,78,85,80,76,79,84,58,32,76,97,84,101,88,32,117,115,105,110,103,32,84,69,88,68,82,65,87,32,109,97,99,114,111,115,10,0,75,80,95,83,117,98,116,114,97,99,116,0,0,0,0,0,115,112,114,105,110,116,102,95,115,112,101,99,105,102,105,101,114,58,32,110,111,32,102,111,114,109,97,116,32,115,112,101,99,105,102,105,101,114,10,0,9,97,117,116,111,115,99,97,108,105,110,103,32,105,115,32,0,0,0,0,0,0,0,0,92,101,110,100,123,116,101,120,100,114,97,119,125,10,0,0,114,97,110,103,101,32,115,112,101,99,105,102,105,101,114,115,32,111,102,32,115,117,109,32,109,117,115,116,32,104,97,118,101,32,105,110,116,101,103,101,114,32,118,97,108,117,101,115,0,0,0,0,0,0,0,0,9,101,114,114,111,114,115,32,97,114,101,32,112,108,111,116,116,101,100,32,119,105,116,104,111,117,116,32,98,97,114,115,10,0,0,0,0,0,0,0,40,37,100,32,37,100,41,0,9,101,114,114,111,114,98,97,114,115,32,97,114,101,32,112,108,111,116,116,101,100,32,105,110,32,37,115,32,119,105,116,104,32,98,97,114,115,32,111,102,32,115,105,122,101,32,37,102,10,0,0,0,0,0,0,10,92,99,112,97,116,104,32,0,0,0,0,0,0,0,0,9,98,111,114,100,101,114,32,37,100,32,105,115,32,100,114,97,119,110,32,105,110,32,37,115,32,111,102,32,116,104,101,32,112,108,111,116,32,101,108,101,109,101,110,116,115,32,119,105,116,104,10,9,32,0,0,51,120,45,50,0,0,0,0,92,112,97,116,104,32,40,37,100,32,37,100,41,0,0,0,9,98,111,114,100,101,114,32,105,115,32,110,111,116,32,100,114,97,119,110,10,0,0,0,92,108,105,110,101,119,100,32,37,100,10,0,0,0,0,0,78,101,101,100,32,118,105,97,32,97,110,100,32,101,105,116,104,101,114,32,112,97,114,97,109,101,116,101,114,32,108,105,115,116,32,111,114,32,102,105,108,101,0,0,0,0,0,0,114,101,108,97,116,105,118,101,0,0,0,0,0,0,0,0,92,37,99,116,101,120,116,123,37,115,125,10,0,0,0,0,97,98,115,111,108,117,116,101,0,0,0,0,0,0,0,0,92,116,101,120,116,114,101,102,32,104,58,82,32,118,58,67,32,0,0,0,0,0,0,0,105,109,97,103,0,0,0,0,9,98,111,120,119,105,100,116,104,32,105,115,32,37,103,32,37,115,10,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,60,121,115,99,97,108,101,62,0,0,0,0,0,0,92,116,101,120,116,114,101,102,32,104,58,76,32,118,58,67,32,0,0,0,0,0,0,0,102,36,114,101,113,117,101,110,99,121,0,0,0,0,0,0,37,115,37,115,9,37,102,10,0,0,0,0,0,0,0,0,9,98,111,120,119,105,100,116,104,32,105,115,32,97,117,116,111,10,0,0,0,0,0,0,32,97,110,100,32,109,105,114,114,111,114,101,100,32,37,115,0,0,0,0,0,0,0,0,100,111,117,98,108,101,0,0,92,109,111,118,101,32,40,37,100,32,37,100,41,0,0,0,115,99,36,97,108,101,0,0,9,110,111,116,32,100,114,97,119,105,110,103,32,108,105,110,101,115,32,98,101,116,119,101,101,110,32,116,119,111,32,111,117,116,114,97,110,103,101,32,112,111,105,110,116,115,10,0,87,114,111,110,103,32,110,117,109,98,101,114,32,111,102,32,99,111,108,117,109,110,115,32,105,110,32,105,110,112,117,116,32,100,97,116,97,32,45,32,108,105,110,101,32,37,100,0,73,110,118,97,108,105,100,32,123,101,108,115,101,45,99,108,97,117,115,101,125,0,0,0,119,111,114,100,115,0,0,0,92,102,99,105,114,32,102,58,48,46,57,32,114,58,49,54,0,0,0,0,0,0,0,0,75,80,95,83,101,112,97,114,97,116,111,114,0,0,0,0,115,112,114,105,110,116,102,95,115,112,101,99,105,102,105,101,114,58,32,117,115,101,100,32,119,105,116,104,32,105,110,118,97,108,105,100,32,102,111,114,109,97,116,32,115,112,101,99,105,102,105,101,114,10,0,0,9,100,114,97,119,105,110,103,32,97,110,100,32,99,108,105,112,112,105,110,103,32,108,105,110,101,115,32,98,101,116,119,101,101,110,32,116,119,111,32,111,117,116,114,97,110,103,101,32,112,111,105,110,116,115,10,0,0,0,0,0,0,0,0,92,102,99,105,114,32,102,58,48,46,57,32,114,58,49,50,0,0,0,0,0,0,0,0,9,110,111,116,32,100,114,97,119,105,110,103,32,108,105,110,101,115,32,98,101,116,119,101,101,110,32,105,110,114,97,110,103,101,32,97,110,100,32,111,117,116,114,97,110,103,101,32,112,111,105,110,116,115,10,0,92,102,99,105,114,32,102,58,48,46,57,32,114,58,57,0,9,100,114,97,119,105,110,103,32,97,110,100,32,99,108,105,112,112,105,110,103,32,108,105,110,101,115,32,98,101,116,119,101,101,110,32,105,110,114,97,110,103,101,32,97,110,100,32,111,117,116,114,97,110,103,101,32,112,111,105,110,116,115,10,0,0,0,0,0,0,0,0,92,108,99,105,114,32,114,58,49,54,0,0,0,0,0,0,9,112,111,105,110,116,32,99,108,105,112,32,105,115,32,37,115,10,0,0,0,0,0,0,51,32,109,117,108,32,50,32,115,117,98,0,0,0,0,0,92,108,99,105,114,32,114,58,49,50,0,0,0,0,0,0,9,99,111,110,116,111,117,114,32,108,105,110,101,32,116,121,112,101,115,32,97,114,101,32,97,108,108,32,116,104,101,32,115,97,109,101,10,0,0,0,92,108,99,105,114,32,114,58,57,0,0,0,0,0,0,0,118,105,97,0,0,0,0,0,9,99,111,110,116,111,117,114,32,108,105,110,101,32,116,121,112,101,115,32,97,114,101,32,118,97,114,105,101,100,32,38,32,108,97,98,101,108,101,100,32,119,105,116,104,32,102,111,114,109,97,116,32,39,37,115,39,10,0,0,0,0,0,0,92,104,116,101,120,116,123,36,92,115,116,97,114,36,125,0,9,9,37,100,32,105,110,99,114,101,109,101,110,116,97,108,32,108,101,118,101,108,115,32,115,116,97,114,116,105,110,103,32,97,116,32,37,103,44,32,115,116,101,112,32,37,103,44,32,101,110,100,32,37,103,10,0,0,0,0,0,0,0,0,92,104,116,101,120,116,123,36,92,116,114,105,97,110,103,108,101,36,125,0,0,0,0,0,98,101,108,111,119,0,0,0,114,101,97,108,0,0,0,0,44,37,103,32,0,0,0,0,111,110,108,121,32,118,97,108,105,100,32,97,115,32,112,97,114,116,32,111,102,32,97,110,32,97,117,116,111,45,108,97,121,111,117,116,32,99,111,109,109,97,110,100,0,0,0,0,92,104,116,101,120,116,123,36,92,116,105,109,101,115,36,125,0,0,0,0,0,0,0,0,97,98,111,118,101,0,0,0,117,36,110,105,113,117,101,0,37,49,49,46,53,101,0,0,111,110,32,97,120,105,115,0,102,0,0,0,0,0,0,0,92,114,109,111,118,101,40,48,32,52,41,92,104,116,101,120,116,123,36,92,66,111,120,36,125,0,0,0,0,0,0,0,111,117,116,36,119,97,114,100,115,0,0,0,0,0,0,0,9,9,37,100,32,100,105,115,99,114,101,116,101,32,108,101,118,101,108,115,32,97,116,32,0,0,0,0,0,0,0,0,100,97,116,97,0,0,0,0,73,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,85,110,107,110,111,119,110,32,109,97,112,112,105,110,103,32,116,121,112,101,0,0,0,0,79,108,100,45,115,116,121,108,101,32,105,102,47,101,108,115,101,32,115,116,97,116,101,109,101,110,116,32,101,110,99,111,117,110,116,101,114,101,100,32,105,110,115,105,100,101,32,98,114,97,99,107,101,116,115,0,78,101,119,32,104,105,115,116,111,103,114,97,109,0,0,0,115,112,114,105,110,116,102,0,92,104,116,101,120,116,123,36,43,36,125,0,0,0,0,0,75,80,95,65,100,100,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,115,112,114,105,110,116,102,95,115,112,101,99,105,102,105,101,114,32,99,97,108,108,101,100,32,119,105,116,104,111,117,116,32,39,37,39,10,0,0,0,9,9,97,112,112,114,111,120,46,32,37,100,32,97,117,116,111,109,97,116,105,99,32,108,101,118,101,108,115,10,0,0,92,114,109,111,118,101,40,48,32,52,41,92,104,116,101,120,116,123,36,92,68,105,97,109,111,110,100,36,125,0,0,0,121,50,0,0,0,0,0,0,9,9,97,115,32,98,115,112,108,105,110,101,32,97,112,112,114,111,120,105,109,97,116,105,111,110,32,115,101,103,109,101,110,116,115,32,111,102,32,111,114,100,101,114,32,37,100,32,119,105,116,104,32,37,100,32,112,116,115,10,0,0,0,0,92,104,116,101,120,116,123,36,92,99,100,111,116,36,125,0,120,50,0,0,0,0,0,0,9,9,97,115,32,99,117,98,105,99,32,115,112,108,105,110,101,32,105,110,116,101,114,112,111,108,97,116,105,111,110,32,115,101,103,109,101,110,116,115,32,119,105,116,104,32,37,100,32,112,116,115,10,0,0,0,92,116,101,120,116,114,101,102,32,104,58,67,32,118,58,67,32,0,0,0,0,0,0,0,121,49,0,0,0,0,0,0,9,9,97,115,32,108,105,110,101,97,114,32,115,101,103,109,101,110,116,115,10,0,0,0,67,97,110,39,116,32,117,115,101,32,112,109,51,100,32,102,111,114,32,50,100,32,112,108,111,116,115,0,0,0,0,0,51,120,45,49,0,0,0,0,92,109,111,118,101,32,40,37,100,32,37,100,41,10,0,0,120,49,0,0,0,0,0,0,103,114,105,100,32,98,97,115,101,32,97,110,100,32,115,117,114,102,97,99,101,10,0,0,92,109,111,118,101,32,40,37,100,32,37,100,41,92,37,99,118,101,99,32,40,37,100,32,37,100,41,0,0,0,0,0,99,36,108,111,115,101,100,0,102,117,110,99,116,105,111,110,32,117,115,101,100,32,102,111,114,32,102,105,116,116,105,110,103,58,32,37,115,10,0,0,115,117,114,102,97,99,101,10,0,0,0,0,0,0,0,0,10,10,102,111,110,116,95,105,100,101,110,116,105,102,105,101,114,58,61,34,71,78,85,80,76,79,84,34,59,10,102,111,110,116,95,115,105,122,101,32,55,50,112,116,35,59,10,116,104,35,61,48,46,52,112,116,35,59,32,100,101,102,105,110,101,95,119,104,111,108,101,95,112,105,120,101,108,115,40,116,104,41,59,10,10,112,97,116,104,32,97,114,114,111,119,104,101,97,100,59,10,97,114,114,111,119,104,101,97,100,32,61,32,40,45,55,112,116,44,45,50,112,116,41,123,100,105,114,51,48,125,46,46,40,45,54,112,116,44,48,112,116,41,46,46,123,100,105,114,49,53,48,125,40,45,55,112,116,44,50,112,116,41,32,38,10,32,32,40,45,55,112,116,44,50,112,116,41,45,45,40,48,112,116,44,48,112,116,41,45,45,40,45,55,112,116,44,45,50,112,116,41,32,38,32,99,121,99,108,101,59,10,0,0,0,0,101,108,108,36,105,112,115,101,115,0,0,0,0,0,0,0,103,114,105,100,32,98,97,115,101,10,0,0,0,0,0,0,10,100,101,102,32,101,110,100,99,104,97,114,32,61,10,32,32,37,32,78,101,120,116,32,108,105,110,101,32,115,104,111,117,108,100,32,112,114,111,98,97,98,108,121,32,98,101,32,114,101,109,111,118,101,100,32,105,102,32,67,77,32,98,97,115,101,32,105,115,32,117,115,101,100,10,32,32,108,58,61,48,59,32,114,58,61,119,59,10,32,32,37,73,110,99,108,117,100,101,32,116,104,101,32,110,101,120,116,32,116,119,111,32,108,105,110,101,115,32,105,102,32,121,111,117,32,119,97,110,116,32,116,111,10,32,32,37,114,111,116,97,116,101,32,116,104,101,32,112,105,99,116,117,114,101,32,57,48,32,100,101,103,46,40,80,111,114,116,114,97,105,116,32,116,111,32,76,97,110,100,115,99,97,112,101,41,10,32,32,37,99,117,114,114,101,110,116,112,105,99,116,117,114,101,58,61,99,117,114,114,101,110,116,112,105,99,116,117,114,101,32,114,111,116,97,116,101,100,32,57,48,32,115,104,105,102,116,101,100,32,40,104,44,48,41,59,10,32,32,37,116,109,112,58,61,99,104,97,114,104,116,59,32,99,104,97,114,104,116,58,61,99,104,97,114,119,100,59,32,99,104,97,114,119,100,58,61,116,109,112,59,10,32,32,115,99,97,110,116,111,107,101,110,115,32,101,120,116,114,97,95,101,110,100,99,104,97,114,59,10,32,32,105,102,32,112,114,111,111,102,105,110,103,62,48,58,32,109,97,107,101,98,111,120,40,112,114,111,111,102,114,117,108,101,41,59,32,102,105,10,32,32,99,104,97,114,100,120,58,61,119,59,10,32,32,115,104,105,112,105,116,59,10,32,32,105,102,32,100,105,115,112,108,97,121,105,110,103,62,48,58,32,109,97,107,101,98,111,120,40,115,99,114,101,101,110,114,117,108,101,41,59,32,115,104,111,119,105,116,59,32,102,105,10,32,32,101,110,100,103,114,111,117,112,32,10,101,110,100,100,101,102,59,10,108,101,116,32,101,110,100,99,104,97,114,95,32,61,32,101,110,100,99,104,97,114,59,10,108,101,116,32,103,101,110,101,114,97,116,101,32,61,32,105,110,112,117,116,59,10,108,101,116,32,114,111,109,97,110,32,61,32,114,111,109,97,110,59,10,0,99,105,114,36,99,108,101,115,0,0,0,0,0,0,0,0,51,100,112,108,111,116,0,0,116,105,109,101,99,111,108,117,109,110,0,0,0,0,0,0,32,105,110,32,37,100,32,108,101,118,101,108,115,32,111,110,32,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,60,110,117,109,95,99,111,108,115,62,0,0,0,0,10,99,109,99,104,97,114,32,34,80,101,114,105,111,100,34,59,10,32,32,110,117,109,101,114,105,99,32,100,111,116,95,100,105,97,109,35,59,32,100,111,116,95,100,105,97,109,35,58,61,105,102,32,109,111,110,111,115,112,97,99,101,58,32,53,47,52,32,102,105,92,32,100,111,116,95,115,105,122,101,35,59,10,32,32,100,101,102,105,110,101,95,119,104,111,108,101,95,98,108,97,99,107,101,114,95,112,105,120,101,108,115,40,100,111,116,95,100,105,97,109,41,59,10,32,32,98,101,103,105,110,99,104,97,114,40,34,46,34,44,53,117,35,44,100,111,116,95,100,105,97,109,35,44,48,41,59,10,32,32,97,100,106,117,115,116,95,102,105,116,40,48,44,48,41,59,32,112,105,99,107,117,112,32,102,105,110,101,46,110,105,98,59,10,32,32,112,111,115,49,40,100,111,116,95,100,105,97,109,44,48,41,59,32,112,111,115,50,40,100,111,116,95,100,105,97,109,44,57,48,41,59,10,32,32,108,102,116,32,120,49,108,61,104,114,111,117,110,100,40,46,53,119,45,46,53,100,111,116,95,100,105,97,109,41,59,32,98,111,116,32,121,50,108,61,48,59,32,122,49,61,122,50,59,32,100,111,116,40,49,44,50,41,59,9,37,32,100,111,116,10,32,32,112,101,110,108,97,98,101,108,115,40,49,44,50,41,59,10,101,110,100,99,104,97,114,59,10,0,0,0,0,0,0,0,0,114,103,98,97,36,108,112,104,97,0,0,0,0,0,0,0,115,36,98,101,122,105,101,114,0,0,0,0,0,0,0,0,37,49,49,46,52,102,0,0,110,111,116,32,100,114,97,119,110,10,0,0,0,0,0,0,79,70,70,10,0,0,0,0,102,108,111,97,116,0,0,0,105,110,112,117,116,32,99,109,114,49,48,46,109,102,10,105,102,32,108,105,103,115,62,49,58,32,102,111,110,116,95,99,111,100,105,110,103,95,115,99,104,101,109,101,58,61,34,84,101,88,32,116,101,120,116,34,59,10,32,32,115,112,97,110,105,115,104,95,115,104,114,105,101,107,61,111,99,116,34,48,55,52,34,59,32,115,112,97,110,105,115,104,95,113,117,101,114,121,61,111,99,116,34,48,55,54,34,59,10,101,108,115,101,58,32,102,111,110,116,95,99,111,100,105,110,103,95,115,99,104,101,109,101,58,61,10,32,32,105,102,32,108,105,103,115,61,48,58,32,34,84,101,88,32,116,121,112,101,119,114,105,116,101,114,32,116,101,120,116,34,10,32,32,101,108,115,101,58,32,34,84,101,88,32,116,101,120,116,32,119,105,116,104,111,117,116,32,102,45,108,105,103,97,116,117,114,101,115,34,32,102,105,59,10,32,32,115,112,97,110,105,115,104,95,115,104,114,105,101,107,61,111,99,116,34,48,49,54,34,59,32,115,112,97,110,105,115,104,95,113,117,101,114,121,61,111,99,116,34,48,49,55,34,59,32,102,105,10,102,111,110,116,95,115,101,116,117,112,59,10,105,110,112,117,116,32,114,111,109,97,110,117,46,109,102,32,37,82,111,109,97,110,32,117,112,112,101,114,99,97,115,101,46,10,105,110,112,117,116,32,114,111,109,97,110,108,46,109,102,32,37,82,111,109,97,110,32,108,111,119,101,114,99,97,115,101,46,10,105,110,112,117,116,32,103,114,101,101,107,117,46,109,102,32,37,71,114,101,101,107,32,117,112,112,101,114,99,97,115,101,46,10,105,110,112,117,116,32,114,111,109,97,110,100,46,109,102,32,37,78,117,109,101,114,97,108,115,46,10,105,110,112,117,116,32,114,111,109,97,110,112,46,109,102,32,37,65,109,112,101,114,115,97,110,100,44,32,113,117,101,115,116,105,111,110,32,109,97,114,107,115,44,32,99,117,114,114,101,110,99,121,32,115,105,103,110,46,10,105,110,112,117,116,32,114,111,109,115,112,108,46,109,102,32,37,76,111,119,101,114,99,97,115,101,32,115,112,101,99,105,97,108,115,32,40,100,111,116,108,101,115,115,32,92,105,44,32,108,105,103,97,116,117,114,101,32,92,97,101,44,32,101,116,99,46,41,10,105,110,112,117,116,32,114,111,109,115,112,117,46,109,102,32,37,85,112,112,101,114,99,97,115,101,32,115,112,101,99,105,97,108,115,32,40,92,65,69,44,32,92,79,69,44,32,92,79,41,10,105,110,112,117,116,32,112,117,110,99,116,46,109,102,32,37,80,117,110,99,116,117,97,116,105,111,110,32,115,121,109,98,111,108,115,46,10,10,109,105,110,117,115,61,65,83,67,73,73,34,45,34,59,32,99,109,99,104,97,114,32,34,77,105,110,117,115,32,115,105,103,110,34,59,10,32,98,101,103,105,110,97,114,105,116,104,99,104,97,114,40,109,105,110,117,115,41,59,32,10,32,32,112,105,99,107,117,112,32,114,117,108,101,46,110,105,98,59,10,32,32,108,102,116,32,120,49,61,104,114,111,117,110,100,32,49,46,53,117,45,101,112,115,59,10,32,32,120,50,61,119,45,120,49,59,32,121,49,61,121,50,61,109,97,116,104,95,97,120,105,115,59,10,32,32,100,114,97,119,32,122,49,45,45,122,50,59,9,32,37,32,98,97,114,10,32,32,108,97,98,101,108,115,40,49,44,50,41,59,32,10,101,110,100,99,104,97,114,59,10,0,0,0,0,0,0,0,0,114,103,98,105,109,97,36,103,101,0,0,0,0,0,0,0,105,110,36,119,97,114,100,115,0,0,0,0,0,0,0,0,9,99,111,110,116,111,117,114,32,102,111,114,32,115,117,114,102,97,99,101,115,32,97,114,101,32,37,115,0,0,0,0,102,105,108,108,101,100,99,117,114,118,101,115,32,0,0,0,78,101,101,100,32,50,32,111,114,32,51,32,99,111,108,117,109,110,115,0,0,0,0,0,99,108,97,117,115,101,0,0,76,111,103,32,115,99,97,108,101,32,111,110,32,89,32,105,115,32,105,110,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,115,116,97,99,107,101,100,32,104,105,115,116,111,103,114,97,109,32,112,108,111,116,10,0,0,0,0,0,80,111,115,105,116,105,118,101,32,105,110,116,101,103,101,114,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,0,10,100,101,102,32,101,110,100,99,104,97,114,32,61,10,32,32,114,91,99,104,97,114,99,111,100,101,93,58,61,99,117,114,114,101,110,116,112,105,99,116,117,114,101,59,10,32,32,119,100,91,99,104,97,114,99,111,100,101,93,58,61,119,59,104,116,91,99,104,97,114,99,111,100,101,93,58,61,104,59,100,112,91,99,104,97,114,99,111,100,101,93,58,61,100,59,10,32,32,109,101,115,115,97,103,101,32,34,80,105,99,116,117,114,101,32,111,102,32,99,104,97,114,99,111,100,101,32,110,111,46,34,32,38,32,100,101,99,105,109,97,108,32,99,104,97,114,99,111,100,101,59,10,32,32,101,110,100,103,114,111,117,112,59,10,101,110,100,100,101,102,59,10,108,101,116,32,101,110,100,99,104,97,114,95,32,61,32,101,110,100,99,104,97,114,59,10,108,101,116,32,103,101,110,101,114,97,116,101,32,61,32,114,101,108,97,120,59,10,108,101,116,32,114,111,109,97,110,32,61,32,114,101,108,97,120,59,10,0,0,105,109,97,36,103,101,0,0,75,80,95,77,117,108,116,105,112,108,121,0,0,0,0,0,118,97,114,36,105,97,98,108,101,0,0,0,0,0,0,0,97,116,116,101,109,112,116,32,116,111,32,97,115,115,105,103,110,32,116,111,32,115,111,109,101,116,104,105,110,103,32,111,116,104,101,114,32,116,104,97,110,32,97,32,110,97,109,101,100,32,118,97,114,105,97,98,108,101,0,0,0,0,0,0,9,100,97,116,97,32,103,114,105,100,51,100,32,105,115,32,100,105,115,97,98,108,101,100,10,0,0,0,0,0,0,0,10,100,101,102,32,112,117,116,95,116,101,120,116,40,101,120,112,114,32,116,115,44,120,115,116,97,114,116,44,121,115,116,97,114,116,44,114,111,116,44,106,117,115,116,105,102,105,99,97,116,105,111,110,41,32,61,10,32,32,98,101,103,105,110,103,114,111,117,112,10,32,32,32,32,116,101,120,116,95,119,105,100,116,104,58,61,48,59,116,101,120,116,95,104,101,105,103,104,116,58,61,48,59,116,101,120,116,95,100,101,112,116,104,58,61,48,59,10,32,32,32,32,102,111,114,32,105,110,100,58,61,48,32,115,116,101,112,32,49,32,117,110,116,105,108,32,108,101,110,103,116,104,40,116,115,41,45,49,58,10,32,32,32,32,32,32,100,101,99,95,110,117,109,58,61,65,83,67,73,73,32,115,117,98,115,116,114,105,110,103,32,40,105,110,100,44,105,110,100,43,49,41,32,111,102,32,116,115,59,10,32,32,32,32,32,32,105,102,32,117,110,107,110,111,119,110,32,114,91,100,101,99,95,110,117,109,93,58,32,100,101,99,95,110,117,109,58,61,51,50,59,32,102,105,10,32,32,32,32,32,32,105,102,32,100,101,99,95,110,117,109,61,51,50,58,32,10,32,32,32,32,32,32,32,32,116,101,120,116,95,119,105,100,116,104,58,61,116,101,120,116,95,119,105,100,116,104,43,119,100,91,54,53,93,59,10,32,32,32,32,32,32,32,32,116,101,120,116,95,104,101,105,103,104,116,58,61,109,97,120,40,116,101,120,116,95,104,101,105,103,104,116,44,104,116,91,54,53,93,41,59,10,32,32,32,32,32,32,32,32,116,101,120,116,95,100,101,112,116,104,58,61,109,97,120,40,116,101,120,116,95,100,101,112,116,104,44,100,112,91,54,53,93,41,59,10,32,32,32,32,32,32,101,108,115,101,105,102,32,100,101,99,95,110,117,109,62,61,48,58,32,10,32,32,32,32,32,32,32,32,116,101,120,116,95,119,105,100,116,104,58,61,116,101,120,116,95,119,105,100,116,104,43,119,100,91,100,101,99,95,110,117,109,93,59,10,32,32,32,32,32,32,32,32,116,101,120,116,95,104,101,105,103,104,116,58,61,109,97,120,40,116,101,120,116,95,104,101,105,103,104,116,44,104,116,91,100,101,99,95,110,117,109,93,41,59,10,32,32,32,32,32,32,32,32,116,101,120,116,95,100,101,112,116,104,58,61,109,97,120,40,116,101,120,116,95,100,101,112,116,104,44,100,112,91,100,101,99,95,110,117,109,93,41,59,10,32,32,32,32,32,32,102,105,10,32,32,32,32,101,110,100,102,111,114,10,32,32,32,32,105,102,32,114,111,116,61,57,48,58,10,32,32,32,32,32,32,105,102,32,106,117,115,116,105,102,105,99,97,116,105,111,110,61,49,58,32,121,110,101,120,116,58,61,121,115,116,97,114,116,59,10,32,32,32,32,32,32,101,108,115,101,105,102,32,106,117,115,116,105,102,105,99,97,116,105,111,110,61,50,58,32,121,110,101,120,116,58,61,114,111,117,110,100,40,121,115,116,97,114,116,45,116,101,120,116,95,119,105,100,116,104,47,50,41,59,10,32,32,32,32,32,32,101,108,115,101,58,32,121,110,101,120,116,58,61,114,111,117,110,100,40,121,115,116,97,114,116,45,116,101,120,116,95,119,105,100,116,104,41,59,10,32,32,32,32,32,32,102,105,10,32,32,32,32,32,32,120,110,101,120,116,58,61,120,115,116,97,114,116,43,40,116,101,120,116,95,104,101,105,103,104,116,45,116,101,120,116,95,100,101,112,116,104,41,47,50,59,10,32,32,32,32,101,108,115,101,58,10,32,32,32,32,32,32,105,102,32,106,117,115,116,105,102,105,99,97,116,105,111,110,61,49,58,32,120,110,101,120,116,58,61,120,115,116,97,114,116,59,10,32,32,32,32,32,32,101,108,115,101,105,102,32,106,117,115,116,105,102,105,99,97,116,105,111,110,61,50,58,32,120,110,101,120,116,58,61,114,111,117,110,100,40,120,115,116,97,114,116,45,116,101,120,116,95,119,105,100,116,104,47,50,41,59,10,32,32,32,32,32,32,101,108,115,101,58,32,120,110,101,120,116,58,61,114,111,117,110,100,40,120,115,116,97,114,116,45,116,101,120,116,95,119,105,100,116,104,41,59,10,32,32,32,32,32,32,102,105,10,32,32,32,32,32,32,121,110,101,120,116,58,61,121,115,116,97,114,116,45,40,116,101,120,116,95,104,101,105,103,104,116,45,116,101,120,116,95,100,101,112,116,104,41,47,50,59,10,32,32,32,32,102,105,10,32,32,32,32,102,111,114,32,105,110,100,58,61,48,32,115,116,101,112,32,49,32,117,110,116,105,108,32,108,101,110,103,116,104,40,116,115,41,45,49,58,10,32,32,32,32,32,32,100,101,99,95,110,117,109,58,61,65,83,67,73,73,32,115,117,98,115,116,114,105,110,103,32,40,105,110,100,44,105,110,100,43,49,41,32,111,102,32,116,115,59,10,32,32,32,32,32,32,105,102,32,117,110,107,110,111,119,110,32,114,91,100,101,99,95,110,117,109,93,58,32,100,101,99,95,110,117,109,58,61,51,50,59,32,102,105,10,32,32,32,32,32,32,105,102,32,100,101,99,95,110,117,109,61,51,50,58,32,10,32,32,32,32,32,32,32,32,120,110,101,120,116,58,61,120,110,101,120,116,43,119,100,91,54,53,93,42,99,111,115,100,32,114,111,116,59,10,32,32,32,32,32,32,32,32,121,110,101,120,116,58,61,121,110,101,120,116,43,119,100,91,54,53,93,42,115,105,110,100,32,114,111,116,59,10,32,32,32,32,32,32,101,108,115,101,105,102,32,100,101,99,95,110,117,109,62,61,48,58,32,10,32,32,32,32,32,32,32,32,99,117,114,114,101,110,116,112,105,99,116,117,114,101,58,61,99,117,114,114,101,110,116,112,105,99,116,117,114,101,43,114,91,100,101,99,95,110,117,109,93,32,115,104,105,102,116,101,100,40,120,110,101,120,116,44,121,110,101,120,116,41,10,32,32,32,32,32,32,32,32,32,32,114,111,116,97,116,101,100,97,114,111,117,110,100,32,40,40,120,110,101,120,116,44,121,110,101,120,116,41,44,114,111,116,41,59,32,10,32,32,32,32,32,32,32,32,120,110,101,120,116,58,61,120,110,101,120,116,43,119,100,91,100,101,99,95,110,117,109,93,42,99,111,115,100,32,114,111,116,59,10,32,32,32,32,32,32,32,32,121,110,101,120,116,58,61])
+.concat([121,110,101,120,116,43,119,100,91,100,101,99,95,110,117,109,93,42,115,105,110,100,32,114,111,116,59,10,32,32,32,32,32,32,102,105,10,32,32,32,32,101,110,100,102,111,114,10,32,32,101,110,100,103,114,111,117,112,32,10,101,110,100,100,101,102,59,10,0,0,0,0,108,97,98,101,108,115,0,0,44,32,107,100,101,110,115,105,116,121,50,100,32,109,111,100,101,0,0,0,0,0,0,0,99,111,108,111,114,32,97,120,105,115,0,0,0,0,0,0,10,37,73,110,99,108,117,100,101,32,110,101,120,116,32,101,105,103,104,116,32,108,105,110,101,115,32,105,102,32,121,111,117,32,104,97,118,101,32,112,114,111,98,108,101,109,115,32,119,105,116,104,32,116,104,101,32,109,111,100,101,32,111,110,32,121,111,117,114,32,115,121,115,116,101,109,46,46,10,37,112,114,111,111,102,105,110,103,58,61,48,59,10,37,102,111,110,116,109,97,107,105,110,103,58,61,49,59,10,37,116,114,97,99,105,110,103,116,105,116,108,101,115,58,61,48,59,10,37,112,105,120,101,108,115,95,112,101,114,95,105,110,99,104,58,61,51,48,48,59,10,37,98,108,97,99,107,101,114,58,61,48,59,10,37,102,105,108,108,105,110,58,61,46,50,59,10,37,111,95,99,111,114,114,101,99,116,105,111,110,58,61,46,54,59,10,37,102,105,120,95,117,110,105,116,115,59,10,0,0,0,0,0,0,0,0,99,97,110,36,100,108,101,115,116,105,99,107,115,0,0,0,9,100,97,116,97,32,103,114,105,100,51,100,32,105,115,32,101,110,97,98,108,101,100,32,102,111,114,32,109,101,115,104,32,111,102,32,115,105,122,101,32,37,100,120,37,100,44,32,107,101,114,110,101,108,61,37,115,44,10,9,115,99,97,108,101,32,102,97,99,116,111,114,115,32,120,61,37,102,44,32,121,61,37,102,37,115,10,0,105,102,32,117,110,107,110,111,119,110,32,99,109,98,97,115,101,58,32,105,110,112,117,116,32,99,109,98,97,115,101,32,102,105,10,10,116,114,97,99,105,110,103,115,116,97,116,115,58,61,49,59,10,112,105,99,116,117,114,101,32,114,91,93,59,10,10,100,101,102,32,111,112,101,110,105,116,32,61,32,111,112,101,110,119,105,110,100,111,119,32,99,117,114,114,101,110,116,119,105,110,100,111,119,10,32,32,102,114,111,109,32,40,48,44,48,41,32,116,111,32,40,52,48,48,44,56,48,48,41,32,97,116,32,40,45,53,48,44,53,48,48,41,32,101,110,100,100,101,102,59,10,10,109,111,100,101,95,115,101,116,117,112,59,10,0,0,0,102,105,110,36,97,110,99,101,98,97,114,115,0,0,0,0,9,100,97,116,97,32,103,114,105,100,51,100,32,105,115,32,101,110,97,98,108,101,100,32,102,111,114,32,109,101,115,104,32,111,102,32,115,105,122,101,32,37,100,120,37,100,44,32,115,112,108,105,110,101,115,10,0,0,0,0,0,0,0,0,51,32,109,117,108,32,49,32,115,117,98,0,0,0,0,0,101,110,100,99,104,97,114,59,10,0,0,0,0,0,0,0,118,101,99,36,116,111,114,115,0,0,0,0,0,0,0,0,9,100,97,116,97,32,103,114,105,100,51,100,32,105,115,32,101,110,97,98,108,101,100,32,102,111,114,32,109,101,115,104,32,111,102,32,115,105,122,101,32,37,100,120,37,100,44,32,110,111,114,109,61,37,100,10,0,0,0,0,0,0,0,0,68,97,109,97,103,101,100,32,69,68,70,32,104,101,97,100,101,114,32,111,102,32,37,115,58,32,110,111,116,32,109,117,108,116,105,112,108,101,32,111,102,32,53,49,50,32,66,46,10,0,0,0,0,0,0,0,97,58,61,119,47,37,100,59,98,58,61,104,47,37,100,59,10,0,0,0,0,0,0,0,104,105,115,36,116,101,112,115,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,114,101,115,105,100,117,97,108,115,32,97,114,101,32,119,101,105,103,104,116,101,100,32,101,113,117,97,108,108,121,32,40,117,110,105,116,32,119,101,105,103,104,116,41,10,10,0,0,9,99,111,109,109,97,110,100,32,108,105,110,101,32,109,97,99,114,111,115,32,119,105,108,108,32,37,115,98,101,32,101,120,112,97,110,100,101,100,10,0,0,0,0,0,0,0,0,65,115,115,101,114,116,105,111,110,32,102,97,105,108,101,100,58,32,37,115,0,0,0,0,10,10,98,101,103,105,110,99,104,97,114,40,37,100,44,37,103,105,110,35,44,37,103,105,110,35,44,48,41,59,10,0,102,115,36,116,101,112,115,0,99,121,108,105,110,100,114,105,99,97,108,10,0,0,0,0,100,114,97,119,32,40,37,100,97,44,37,100,98,41,45,45,40,37,100,97,44,37,100,98,41,59,10,0,0,0,0,0,102,105,108,108,115,116,36,101,112,115,0,0,0,0,0,0,118,97,108,105,100,0,0,0,115,112,104,101,114,105,99,97,108,10,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,44,32,60,110,117,109,95,99,111,108,115,62,39,0,0,0,0,0,0,0,0,100,114,97,119,100,111,116,32,40,37,100,97,44,37,100,98,41,59,10,0,0,0,0,0,115,116,36,101,112,115,0,0,99,36,115,112,108,105,110,101,115,0,0,0,0,0,0,0,32,32,83,117,109,32,120,121,58,32,32,32,32,32,32,32,37,46,52,103,10,0,0,0,99,97,114,116,101,115,105,97,110,10,0,0,0,0,0,0,9,37,115,45,97,120,105,115,32,116,105,99,115,58,9,0,108,117,0,0,0,0,0,0,101,114,114,111,114,32,105,110,32,101,100,103,101,51,100,95,105,110,116,101,114,115,101,99,116,0,0,0,0,0,0,0,112,105,99,107,117,112,32,112,101,110,99,105,114,99,108,101,32,115,99,97,108,101,100,32,37,103,116,104,59,10,0,0,98,111,120,120,36,121,101,114,114,111,114,98,97,114,115,0,110,111,109,105,36,114,114,111,114,0,0,0,0,0,0,0,9,109,97,112,112,105,110,103,32,102,111,114,32,51,45,100,32,100,97,116,97,32,105,115,32,0,0,0,0,0,0,0,104,105,115,116,111,103,114,97,109,115,10,0,0,0,0,0,50,32,99,111,108,117,109,110,115,32,111,110,108,121,32,112,111,115,115,105,98,108,101,32,119,105,116,104,32,101,120,112,108,105,99,105,116,32,112,109,51,100,32,115,116,121,108,101,32,40,108,105,110,101,32,37,100,41,0,0,0,0,0,0,76,111,103,32,115,99,97,108,101,32,111,110,32,88,32,105,115,32,105,110,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,104,105,115,116,111,103,114,97,109,32,112,108,111,116,115,10,0,0,0,0,101,120,112,101,99,116,101,100,32,123,101,108,115,101,45,99,108,97,117,115,101,125,0,0,67,111,108,117,109,110,32,110,117,109,98,101,114,32,101,120,112,101,99,116,101,100,0,0,112,117,116,95,116,101,120,116,40,34,37,115,34,44,37,100,97,44,37,100,98,44,37,100,44,37,100,41,59,10,0,0,98,111,120,101,114,36,114,111,114,98,97,114,115,0,0,0,75,80,95,69,113,117,97,108,0,0,0,0,0,0,0,0,65,116,116,101,109,112,116,32,116,111,32,97,115,115,105,103,110,32,116,111,32,97,32,114,101,97,100,45,111,110,108,121,32,118,97,114,105,97,98,108,101,0,0,0,0,0,0,0,9,100,117,109,109,121,32,118,97,114,105,97,98,108,101,115,32,97,114,101,32,34,37,115,34,32,97,110,100,32,34,37,115,34,10,0,0,0,0,0,102,105,108,108,32,97,114,114,111,119,104,101,97,100,32,114,111,116,97,116,101,100,32,97,110,103,108,101,40,37,100,44,37,100,41,32,115,104,105,102,116,101,100,32,40,37,100,97,44,37,100,98,41,59,10,0,102,105,108,108,101,100,99,36,117,114,118,101,115,0,0,0,9,32,32,37,115,45,97,120,105,115,58,32,34,37,115,34,10,0,0,0,0,0,0,0,39,58,39,32,111,114,32,107,101,121,119,111,114,100,32,39,116,111,39,32,101,120,112,101,99,116,101,100,0,0,0,0,102,111,36,110,116,0,0,0,104,105,115,116,36,111,103,114,97,109,115,0,0,0,0,0,9,116,105,99,32,102,111,114,109,97,116,32,105,115,58,10,0,0,0,0,0,0,0,0,109,97,36,103,110,105,102,105,99,97,116,105,111,110,0,0,98,111,120,101,115,0,0,0,9,37,115,32,97,114,101,32,112,108,111,116,116,101,100,32,119,105,116,104,32,0,0,0,51,120,0,0,0,0,0,0,110,111,112,114,111,36,108,111,103,117,101,115,0,0,0,0,120,121,101,36,114,114,111,114,98,97,114,115,0,0,0,0,32,119,105,116,104,32,98,111,114,100,101,114,32,0,0,0,112,114,111,36,108,111,103,117,101,115,0,0,0,0,0,0,120,101,36,114,114,111,114,98,97,114,115,0,0,0,0,0,32,119,105,116,104,32,110,111,32,98,111,114,100,101,114,10,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,35,100,97,116,97,112,111,105,110,116,115,32,61,32,37,100,10,0,0,0,0,0,0,0,110,111,112,115,36,110,102,115,115,0,0,0,0,0,0,0,101,36,114,114,111,114,98,97,114,115,0,0,0,0,0,0,9,70,105,108,108,32,115,116,121,108,101,32,105,115,32,101,109,112,116,121,0,0,0,0,112,115,110,102,115,115,45,118,36,101,114,115,105,111,110,55,0,0,0,0,0,0,0,0,121,101,36,114,114,111,114,98,97,114,115,0,0,0,0,0,9,70,105,108,108,32,115,116,121,108,101,32,117,115,101,115,32,37,115,32,112,97,116,116,101,114,110,115,32,115,116,97,114,116,105,110,103,32,97,116,32,37,100,0,0,0,0,0,112,115,36,110,102,115,115,0,120,121,101,114,114,111,114,108,36,105,110,101,115,0,0,0,98,36,101,122,105,101,114,0,32,32,67,111,114,114,101,108,97,116,105,111,110,58,32,32,114,32,61,32,37,46,52,103,10,0,0,0,0,0,0,0,116,114,97,110,115,112,97,114,101,110,116,0,0,0,0,0,61,0,0,0,0,0,0,0,79,85,84,0,0,0,0,0,117,108,111,110,103,0,0,0,97,109,36,115,116,101,120,0,120,101,114,114,111,114,108,36,105,110,101,115,0,0,0,0,109,105,36,114,114,111,114,0,9,70,105,108,108,32,115,116,121,108,101,32,117,115,101,115,32,37,115,32,115,111,108,105,100,32,99,111,108,111,117,114,32,119,105,116,104,32,100,101,110,115,105,116,121,32,37,46,51,102,0,0,0,0,0,0,98,111,120,101,115,10,0,0,103,101,116,95,100,97,116,97,58,32,107,101,121,32,116,105,116,108,101,32,110,111,116,32,102,111,117,110,100,32,105,110,32,114,101,113,117,101,115,116,101,100,32,99,111,108,117,109,110,10,0,0,0,0,0,0,84,104,105,115,32,112,108,111,116,32,115,116,121,108,101,32,105,115,32,110,111,116,32,97,118,97,105,108,97,98,108,101,32,105,110,32,112,111,108,97,114,32,109,111,100,101,0,0,97,52,36,112,97,112,101,114,0,0,0,0,0,0,0,0,101,114,114,111,114,108,36,105,110,101,115,0,0,0,0,0,75,80,95,68,101,108,101,116,101,0,0,0,0,0,0,0,100,101,102,97,117,108,116,32,108,105,110,101,116,121,112,101,115,10,0,0,0,0,0,0,108,97,36,116,101,120,0,0,121,101,114,114,111,114,108,36,105,110,101,115,0,0,0,0,117,115,101,114,45,100,101,102,105,110,101,100,32,108,105,110,101,32,115,116,121,108,101,115,32,114,97,116,104,101,114,32,116,104,97,110,32,100,101,102,97,117,108,116,32,108,105,110,101,32,116,121,112,101,115,10,0,0,0,0,0,0,0,0,116,36,101,120,0,0,0,0,100,36,111,116,115,0,0,0,9,80,108,111,116,32,108,105,110,101,115,32,105,110,99,114,101,109,101,110,116,32,111,118,101,114,32,0,0,0,0,0,110,36,111,116,101,120,0,0,108,112,0,0,0,0,0,0,102,111,114,116,36,114,97,110,0,0,0,0,0,0,0,0,32,116,101,120,116,99,111,108,111,114,32,108,116,32,37,100,0,0,0,0,0,0,0,0,51,32,109,117,108,0,0,0,115,36,111,108,105,100,0,0,108,105,110,101,115,112,36,111,105,110,116,115,0,0,0,0,32,116,105,116,108,101,32,111,102,102,115,101,116,32,0,0,99,36,111,108,111,117,114,0,78,111,32,100,97,116,97,32,116,111,32,102,105,116,32,0,9,72,105,115,116,111,103,114,97,109,32,115,116,121,108,101,32,105,115,32,99,111,108,117,109,110,115,116,97,99,107,101,100,32,0,0,0,0,0,0,99,36,111,108,111,114,0,0,66,97,100,32,97,98,98,114,101,118,105,97,116,101,100,32,109,111,110,116,104,32,110,97,109,101,0,0,0,0,0,0,105,36,109,112,117,108,115,101,115,0,0,0,0,0,0,0,9,72,105,115,116,111,103,114,97,109,32,115,116,121,108,101,32,105,115,32,114,111,119,115,116,97,99,107,101,100,32,0,109,111,36,110,111,99,104,114,111,109,101,0,0,0,0,0,98,111,120,112,108,111,116,0,115,116,114,99,111,108,0,0,9,72,105,115,116,111,103,114,97,109,32,115,116,121,108,101,32,105,115,32,101,114,114,111,114,98,97,114,115,32,119,105,116,104,32,103,97,112,32,37,100,32,108,119,32,37,103,32,0,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,60,110,117,109,95,99,111,108,115,62,44,60,110,117,109,95,114,111,119,115,62,39,0,0,0,0,0,0,0,112,115,110,115,102,115,115,0,114,101,99,116,36,97,110,103,108,101,0,0,0,0,0,0,97,36,99,115,112,108,105,110,101,115,0,0,0,0,0,0,32,32,76,105,110,101,97,114,32,77,111,100,101,108,58,32,121,32,61,32,37,46,52,103,32,120,32,43,32,37,46,52,103,10,0,0,0,0,0,0,9,72,105,115,116,111,103,114,97,109,32,115,116,121,108,101,32,105,115,32,99,108,117,115,116,101,114,101,100,32,119,105,116,104,32,103,97,112,32,37,100,32,0,0,0,0,0,0,73,78,0,0,0,0,0,0,108,100,0,0,0,0,0,0,112,115,110,115,102,115,115,40,118,55,41,0,0,0,0,0,101,108,108,36,105,112,115,101,0,0,0,0,0,0,0,0,97,114,114,111,119,115,116,121,108,101,32,110,111,116,32,102,111,117,110,100,0,0,0,0,120,121,101,114,114,111,114,98,97,114,115,10,0,0,0,0,101,120,112,101,99,116,105,110,103,32,40,101,120,112,114,101,115,115,105,111,110,41,0,0,39,41,39,32,101,120,112,101,99,116,101,100,0,0,0,0,37,115,32,37,115,32,37,115,116,101,120,37,115,37,115,32,109,97,103,32,37,46,51,102,32,37,115,32,37,115,112,114,111,108,111,103,117,101,115,40,37,100,41,0,0,0,0,0,99,105,114,99,36,108,101,0,75,80,95,80,97,103,101,85,112,0,0,0,0,0,0,0,108,105,110,101,99,36,111,108,111,114,0,0,0,0,0,0,32,40,100,101,102,97,117,108,116,32,108,101,110,103,116,104,32,97,110,100,32,97,110,103,108,101,115,41,10,0,0,0,32,97,109,115,116,101,120,0,104,105,115,116,36,111,103,114,97,109,0,0,0,0,0,0,32,108,101,110,103,116,104,32,37,115,37,103,44,32,97,110,103,108,101,32,37,103,32,100,101,103,0,0,0,0,0,0,115,116,97,114,116,105,110,103,32,114,97,110,103,101,32,118,97,108,117,101,32,111,114,32,39,58,39,32,111,114,32,39,116,111,39,32,101,120,112,101,99,116,101,100,0,0,0,0,32,97,52,112,97,112,101,114,0,0,0,0,0,0,0,0,105,110,99,114,36,101,109,101,110,116,0,0,0,0,0,0,9,32,32,97,114,114,111,119,32,104,101,97,100,115,58,32,37,115,44,32,0,0,0,0,108,97,0,0,0,0,0,0,102,115,0,0,0,0,0,0,32,110,111,104,101,97,100,0,124,99,111,115,40,55,50,48,120,41,124,0,0,0,0,0,9,108,111,97,100,112,97,116,104,32,105,115,32,0,0,0,109,111,110,111,99,104,114,111,109,101,0,0,0,0,0,0,32,111,110,101,32,104,101,97,100,32,0,0,0,0,0,0,99,111,108,111,114,0,0,0,108,36,105,110,101,115,0,0,115,116,114,105,110,103,0,0,37,103,93,10,0,0,0,0,32,98,111,116,104,32,104,101,97,100,115,32,0,0,0,0,98,105,110,100,0,0,0,0,105,108,108,101,103,97,108,32,109,111,110,116,104,0,0,0,37,115,32,37,115,32,37,115,116,101,120,37,115,37,115,32,109,97,103,32,37,46,51,102,32,102,111,110,116,32,34,37,115,44,37,46,50,102,34,32,37,115,112,114,111,108,111,103,117,101,115,40,37,100,41,0,102,36,117,110,99,116,105,111,110,0,0,0,0,0,0,0,9,32,37,115,32,37,115,0,101,120,112,101,99,116,105,110,103,32,102,111,110,116,32,110,97,109,101,0,0,0,0,0,100,36,97,116,97,0,0,0,115,116,114,105,110,103,99,111,108,117,109,110,0,0,0,0,9,97,114,114,111,119,115,116,121,108,101,32,37,100,44,32,0,0,0,0,0,0,0,0,116,111,111,32,109,97,110,121,32,108,97,121,111,117,116,32,99,111,109,109,97,110,100,115,0,0,0,0,0,0,0,0,112,99,114,114,56,114,0,0,103,114,97,121,49,48,48,0,120,50,121,49,0,0,0,0,32,32,76,105,110,101,97,114,32,77,111,100,101,108,58,32,121,32,61,32,37,46,52,103,32,120,32,45,32,37,46,52,103,10,0,0,0,0,0,0,97,112,112,101,97,114,32,105,110,32,116,104,101,32,111,114,100,101,114,32,116,104,101,121,32,119,101,114,101,32,102,111,117,110,100,0,0,0,0,0,9,37,115,45,97,120,105,115,32,116,105,99,115,32,97,114,101,32,37,115,44,32,9,109,97,106,111,114,32,116,105,99,115,99,97,108,101,32,105,115,32,37,103,32,97,110,100,32,109,105,110,111,114,32,116,105,99,115,99,97,108,101,32,105,115,32,37,103,10,0,0,0,108,111,110,103,0,0,0,0,99,109,114,49,48,0,0,0,103,114,97,121,57,48,0,0,97,120,36,105,115,0,0,0,98,101,32,115,111,114,116,101,100,32,97,108,112,104,97,98,101,116,105,99,97,108,108,121,0,0,0,0,0,0,0,0,120,101,114,114,111,114,98,97,114,115,10,0,0,0,0,0,66,97,100,32,100,97,116,97,32,111,110,32,108,105,110,101,32,37,100,32,111,102,32,102,105,108,101,32,37,115,0,0,89,111,117,32,104,97,118,101,32,116,111,32,99,111,109,112,105,108,101,32,103,110,117,112,108,111,116,32,119,105,116,104,32,98,117,105,108,116,105,110,32,114,101,97,100,108,105,110,101,32,111,114,32,71,78,85,32,114,101,97,100,108,105,110,101,32,111,114,32,66,83,68,32,101,100,105,116,108,105,110,101,32,116,111,32,101,110,97,98,108,101,32,104,105,115,116,111,114,121,32,115,117,112,112,111,114,116,46,0,0,0,0,10,100,101,102,32,95,119,99,32,61,32,119,105,116,104,112,101,110,32,99,117,114,114,101,110,116,112,101,110,32,119,105,116,104,99,111,108,111,114,32,99,117,114,114,101,110,116,99,111,108,111,114,32,101,110,100,100,101,102,59,10,100,101,102,32,95,97,99,32,61,32,97,100,100,116,111,32,99,117,114,114,101,110,116,112,105,99,116,117,114,101,32,101,110,100,100,101,102,59,10,100,101,102,32,95,115,109,115,32,61,32,115,99,97,108,101,100,32,109,112,116,32,115,104,105,102,116,101,100,32,101,110,100,100,101,102,59,10,37,32,100,114,97,119,105,110,103,32,112,111,105,110,116,45,116,121,112,101,115,10,100,101,102,32,103,112,100,114,97,119,32,40,101,120,112,114,32,110,44,32,120,44,32,121,41,32,61,10,32,32,105,102,32,110,60,48,58,32,95,97,99,32,99,111,110,116,111,117,114,32,102,117,108,108,99,105,114,99,108,101,32,95,115,109,115,32,40,120,44,121,41,10,32,32,101,108,115,101,105,102,32,40,110,61,49,41,32,111,114,32,40,110,61,51,41,58,10,32,32,32,32,95,97,99,32,100,111,117,98,108,101,112,97,116,104,32,112,116,112,97,116,104,91,110,93,32,95,115,109,115,32,40,120,44,121,41,32,95,119,99,59,10,32,32,32,32,95,97,99,32,100,111,117,98,108,101,112,97,116,104,32,112,116,112,97,116,104,91,110,93,32,114,111,116,97,116,101,100,32,57,48,32,95,115,109,115,32,40,120,44,121,41,32,95,119,99,10,32,32,101,108,115,101,105,102,32,110,60,54,58,32,95,97,99,32,100,111,117,98,108,101,112,97,116,104,32,112,116,112,97,116,104,91,110,93,32,95,115,109,115,32,40,120,44,121,41,32,95,119,99,10,32,32,101,108,115,101,58,32,95,97,99,32,99,111,110,116,111,117,114,32,112,116,112,97,116,104,91,110,93,32,95,115,109,115,32,40,120,44,121,41,32,95,119,99,10,32,32,102,105,10,101,110,100,100,101,102,59,10,10,37,32,116,104,101,32,112,111,105,110,116,32,115,104,97,112,101,115,10,112,97,116,104,32,112,116,112,97,116,104,91,93,59,10,37,100,105,97,109,111,110,100,10,112,116,112,97,116,104,48,32,61,32,112,116,112,97,116,104,54,32,61,32,40,45,49,47,50,44,48,41,45,45,40,48,44,45,49,47,50,41,45,45,40,49,47,50,44,48,41,45,45,40,48,44,49,47,50,41,45,45,99,121,99,108,101,59,10,37,32,112,108,117,115,32,115,105,103,110,10,112,116,112,97,116,104,49,32,61,32,40,45,49,47,50,44,48,41,45,45,40,49,47,50,44,48,41,59,10,37,32,115,113,117,97,114,101,10,112,116,112,97,116,104,50,32,61,32,112,116,112,97,116,104,55,32,61,32,40,45,49,47,50,44,45,49,47,50,41,45,45,40,49,47,50,44,45,49,47,50,41,45,45,40,49,47,50,44,49,47,50,41,45,45,40,45,49,47,50,44,49,47,50,41,45,45,99,121,99,108,101,59,10,37,32,99,114,111,115,115,10,112,116,112,97,116,104,51,32,58,61,32,40,45,49,47,50,44,45,49,47,50,41,45,45,40,49,47,50,44,49,47,50,41,59,10,37,32,99,105,114,99,108,101,58,10,112,116,112,97,116,104,52,32,61,32,112,116,112,97,116,104,56,58,61,32,102,117,108,108,99,105,114,99,108,101,59,10,37,32,116,114,105,97,110,103,108,101,10,112,116,112,97,116,104,53,32,61,32,112,116,112,97,116,104,57,32,58,61,32,40,48,44,49,47,50,41,45,45,40,45,49,47,50,44,45,49,47,50,41,45,45,40,49,47,50,44,45,49,47,50,41,45,45,99,121,99,108,101,59,10,10,100,101,102,32,108,105,110,101,116,121,112,101,32,101,120,112,114,32,110,32,61,10,32,32,99,117,114,114,101,110,116,99,111,108,111,114,58,61,32,105,102,32,99,111,108,111,114,108,105,110,101,115,32,58,32,99,111,108,91,110,93,32,101,108,115,101,58,32,98,108,97,99,107,32,102,105,59,10,32,32,105,102,32,110,32,61,32,45,49,32,58,10,32,32,32,32,32,32,100,114,97,119,111,112,116,105,111,110,115,40,119,105,116,104,99,111,108,111,114,32,99,117,114,114,101,110,116,99,111,108,111,114,32,119,105,116,104,112,101,110,32,40,99,117,114,114,101,110,116,112,101,110,32,115,99,97,108,101,100,32,46,53,41,41,59,10,32,32,101,108,115,101,105,102,32,110,32,60,32,49,32,58,10,32,32,32,32,100,114,97,119,111,112,116,105,111,110,115,40,95,119,99,41,59,10,32,32,101,108,115,101,32,58,10,32,32,32,32,100,114,97,119,111,112,116,105,111,110,115,40,32,105,102,32,100,97,115,104,101,100,108,105,110,101,115,58,32,100,97,115,104,101,100,32,108,116,91,110,93,32,102,105,32,95,119,99,41,59,10,32,32,102,105,10,101,110,100,100,101,102,59,10,10,37,32,100,97,115,104,32,112,97,116,116,101,114,110,115,10,112,105,99,116,117,114,101,32,108,116,91,93,59,10,108,116,49,61,100,97,115,104,112,97,116,116,101,114,110,40,111,110,32,50,32,111,102,102,32,50,41,59,32,37,32,100,97,115,104,101,115,10,108,116,50,61,100,97,115,104,112,97,116,116,101,114,110,40,111,110,32,50,32,111,102,102,32,50,32,111,110,32,48,46,50,32,111,102,102,32,50,41,59,32,37,100,97,115,104,45,100,111,116,10,108,116,51,61,108,116,49,32,115,99,97,108,101,100,32,49,46,52,49,52,59,10,108,116,52,61,108,116,50,32,115,99,97,108,101,100,32,49,46,52,49,52,59,10,108,116,53,61,108,116,49,32,115,99,97,108,101,100,32,50,59,10,108,116,54,58,61,108,116,50,32,115,99,97,108,101,100,32,50,59,10,108,116,55,61,100,97,115,104,112,97,116,116,101,114,110,40,111,110,32,48,46,50,32,111,102,102,32,50,41,59,32,37,100,111,116,115,10,10,99,111,108,111,114,32,99,111,108,91,93,44,99,121,97,110,44,32,109,97,103,101,110,116,97,44,32,121,101,108,108,111,119,59,10,99,121,97,110,61,98,108,117,101,43,103,114,101,101,110,59,32,109,97,103,101,110,116,97,61,114,101,100,43,98,108,117,101,59,121,101,108,108,111,119,61,103,114,101,101,110,43,114,101,100,59,10,99,111,108,91,45,50,93,58,61,99,111,108,91,45,49,93,58,61,99,111,108,48,58,61,98,108,97,99,107,59,10,99,111,108,49,58,61,114,101,100,59,10,99,111,108,50,58,61,40,46,50,44,46,50,44,49,41,59,32,37,98,108,117,101,10,99,111,108,51,58,61,40,49,44,46,54,54,44,48,41,59,32,37,111,114,97,110,103,101,10,99,111,108,52,58,61,46,56,53,42,103,114,101,101,110,59,10,99,111,108,53,58,61,46,57,42,109,97,103,101,110,116,97,59,10,99,111,108,54,58,61,48,46,56,53,42,99,121,97,110,59,10,99,111,108,55,58,61,46,56,53,42,121,101,108,108,111,119,59,10,10,37,112,108,97,99,105,110,103,32,116,101,120,116,10,112,105,99,116,117,114,101,32,71,80,116,101,120,116,59,10,100,101,102,32,112,117,116,95,116,101,120,116,40,101,120,112,114,32,112,105,99,44,32,120,44,32,121,44,32,114,44,32,106,41,32,61,10,32,32,71,80,116,101,120,116,58,61,109,97,107,101,112,105,99,40,112,105,99,41,59,10,32,32,71,80,116,101,120,116,58,61,71,80,116,101,120,116,32,115,104,105,102,116,101,100,10,32,32,32,32,105,102,32,106,32,61,32,49,58,32,40,45,40,117,108,99,111,114,110,101,114,32,71,80,116,101,120,116,32,43,32,108,108,99,111,114,110,101,114,32,71,80,116,101,120,116,41,47,50,41,10,32,32,32,32,101,108,115,101,105,102,32,106,32,61,32,50,58,32,40,45,99,101,110,116,101,114,32,71,80,116,101,120,116,41,10,32,32,32,32,101,108,115,101,58,32,40,45,40,117,114,99,111,114,110,101,114,32,71,80,116,101,120,116,32,43,32,108,114,99,111,114,110,101,114,32,71,80,116,101,120,116,41,47,50,41,10,32,32,32,32,102,105,10,32,32,32,32,114,111,116,97,116,101,100,32,114,59,10,32,32,97,100,100,116,111,32,99,117,114,114,101,110,116,112,105,99,116,117,114,101,32,97,108,115,111,32,71,80,116,101,120,116,32,115,104,105,102,116,101,100,32,40,120,44,121,41,10,101,110,100,100,101,102,59,10,0,0,0,0,0,0,0,0,103,114,97,121,56,48,0,0,75,80,95,85,112,0,0,0,108,99,0,0,0,0,0,0,69,82,82,78,79,0,0,0,9,102,97,99,116,111,114,32,108,97,98,101,108,115,32,119,105,108,108,32,37,115,10,0,100,97,115,104,101,100,108,105,110,101,115,58,61,116,114,117,101,59,10,0,0,0,0,0,103,114,97,121,55,48,0,0,97,114,101,32,111,102,102,0,100,97,115,104,101,100,108,105,110,101,115,58,61,102,97,108,115,101,59,10,0,0,0,0,103,114,97,121,54,48,0,0,105,115,111,95,56,56,53,57,95,49,53,0,0,0,0,0,97,114,101,32,97,117,116,111,109,97,116,105,99,0,0,0,32,120,108,111,119,32,120,104,105,103,104,0,0,0,0,0,99,111,108,111,114,108,105,110,101,115,58,61,102,97,108,115,101,59,10,0,0,0,0,0,103,114,97,121,53,48,0,0,119,105,108,108,32,98,101,32,112,117,116,32,111,110,32,116,104,101,32,120,50,32,97,120,105,115,0,0,0,0,0,0,55,50,48,32,109,117,108,32,99,111,115,32,97,98,115,0,99,111,108,111,114,108,105,110,101,115,58,61,116,114,117,101,59,10,0,0,0,0,0,0,103,114,97,121,52,48,0,0,112,117,115,104,100,50,0,0,119,105,108,108,32,98,101,32,112,117,116,32,111,110,32,116,104,101,32,120,32,97,120,105,115,0,0,0,0,0,0,0,100,114,111,112,108,97,115,116,95,100,121,110,97,114,114,97,121,58,32,100,121,110,97,114,114,97,121,32,119,97,115,110,39,116,32,105,110,105,116,105,97,108,105,122,101,100,33,0,10,99,111,108,111,114,32,99,117,114,114,101,110,116,99,111,108,111,114,59,32,99,117,114,114,101,110,116,99,111,108,111,114,58,61,98,108,97,99,107,59,10,99,111,108,111,114,32,102,105,108,108,99,111,108,111,114,59,10,98,111,111,108,101,97,110,32,99,111,108,111,114,108,105,110,101,115,44,100,97,115,104,101,100,108,105,110,101,115,59,10,0,0,0,0,0,103,114,97,121,51,48,0,0,9,102,97,99,116,111,114,32,108,97,98,101,108,115,32,37,115,10,0,0,0,0,0,0,92,115,101,116,102,111,110,116,123,37,115,125,123,37,53,46,50,102,125,10,101,116,101,120,10,0,0,0,0,0,0,0,103,114,97,121,50,48,0,0,9,115,101,112,97,114,97,116,105,111,110,32,98,101,116,119,101,101,110,32,98,111,120,112,108,111,116,115,32,105,115,32,37,103,10,0,0,0,0,0,10,37,102,111,110,116,32,99,104,97,110,103,101,115,10,118,101,114,98,97,116,105,109,116,101,120,10,92,100,101,102,92,115,101,116,102,111,110,116,35,49,35,50,123,37,46,10,32,32,92,102,111,110,116,92,103,112,102,111,110,116,61,35,49,32,97,116,32,35,50,112,116,10,92,103,112,102,111,110,116,125,10,0,0,0,0,0,0,103,114,97,121,49,48,0,0,9,111,117,116,108,105,101,114,115,32,119,105,108,108,32,110,111,116,32,98,101,32,100,114,97,119,110,10,0,0,0,0,108,97,121,36,111,117,116,0,10,100,101,102,97,117,108,116,102,111,110,116,58,61,32,34,37,115,34,59,10,100,101,102,97,117,108,116,115,99,97,108,101,32,58,61,32,37,54,46,51,102,47,102,111,110,116,115,105,122,101,32,100,101,102,97,117,108,116,102,111,110,116,59,10,0,0,0,0,0,0,0,103,114,97,121,48,0,0,0,120,49,121,50,0,0,0,0,32,32,77,101,100,105,97,110,58,32,32,32,37,115,32,37,115,32,37,115,10,0,0,0,99,100,0,0,0,0,0,0,9,111,117,116,108,105,101,114,115,32,119,105,108,108,32,98,101,32,100,114,97,119,110,32,117,115,105,110,103,32,112,111,105,110,116,32,116,121,112,101,32,37,100,10,0,0,0,0,46,48,0,0,0,0,0,0,118,97,114,100,101,102,32,109,97,107,101,112,105,99,40,101,120,112,114,32,115,116,114,41,32,61,10,32,32,105,102,32,112,105,99,116,117,114,101,32,115,116,114,32,58,32,115,116,114,32,115,99,97,108,101,100,32,116,101,120,116,109,97,103,10,32,32,37,32,111,116,104,101,114,119,105,115,101,32,97,32,115,116,114,105,110,103,10,32,32,101,108,115,101,58,32,115,116,114,32,105,110,102,111,110,116,32,100,101,102,97,117,108,116,102,111,110,116,32,115,99,97,108,101,100,32,40,100,101,102,97,117,108,116,115,99,97,108,101,42,116,101,120,116,109,97,103,41,10,32,32,102,105,10,101,110,100,100,101,102,59,10,10,100,101,102,32,105,110,102,111,110,116,115,105,122,101,40,101,120,112,114,32,115,116,114,44,32,115,105,122,101,41,32,61,10,32,32,105,110,102,111,110,116,32,115,116,114,32,115,99,97,108,101,100,32,40,115,105,122,101,32,47,32,102,111,110,116,115,105,122,101,32,115,116,114,41,10,101,110,100,100,101,102,59,10,0,0,115,108,97,116,101,103,114,97,121,0,0,0,0,0,0,0,116,36,105,99,115,0,0,0,67,97,110,39,116,32,114,101,97,100,32,100,97,116,97,32,102,105,108,101,0,0,0,0,32,32,98,111,120,32,98,121,32,37,53,46,50,102,32,111,102,32,116,104,101,32,105,110,116,101,114,113,117,97,114,116,105,108,101,32,100,105,115,116,97,110,99,101,10,0,0,0,121,101,114,114,111,114,98,97,114,115,10,0,0,0,0,0,10,116,101,120,116,109,97,103,58,61,37,54,46,51,102,59,10,0,0,0,0,0,0,0,100,97,114,107,45,103,114,97,121,0,0,0,0,0,0,0,75,80,95,72,111,109,101,0,98,103,110,100,0,0,0,0,110,111,110,45,115,116,114,105,110,103,32,97,114,103,117,109,101,110,116,32,116,111,32,115,121,115,116,101,109,40,41,0,32,32,109,101,100,105,97,110,32,116,111,32,105,110,99,108,117,100,101,32,37,53,46,50,102,32,111,102,32,116,104,101,32,112,111,105,110,116,115,10,0,0,0,0,0,0,0,0,10,119,97,114,110,105,110,103,99,104,101,99,107,58,61,48,59,10,100,101,102,97,117,108,116,109,112,116,58,61,109,112,116,58,61,52,59,10,116,104,58,61,46,54,59,10,37,37,32,72,97,118,101,32,110,105,99,101,32,115,104,97,114,112,32,106,111,105,110,115,32,111,110,32,111,117,114,32,108,105,110,101,115,10,108,105,110,101,99,97,112,58,61,98,117,116,116,59,10,108,105,110,101,106,111,105,110,58,61,109,105,116,101,114,101,100,59,10,10,100,101,102,32,115,99,97,108,101,112,101,110,32,101,120,112,114,32,110,32,61,32,112,105,99,107,117,112,32,112,101,110,99,105,114,99,108,101,32,115,99,97,108,101,100,32,40,110,42,116,104,41,32,101,110,100,100,101,102,59,10,100,101,102,32,112,116,115,105,122,101,32,101,120,112,114,32,110,32,61,32,109,112,116,58,61,110,42,100,101,102,97,117,108,116,109,112,116,32,101,110,100,100,101,102,59,10,10,0,0,0,0,0,108,105,103,104,116,45,103,114,101,121,0,0,0,0,0,0,9,98,111,120,112,108,111,116,32,114,97,110,103,101,32,101,120,116,101,110,100,115,32,102,114,111,109,32,116,104,101,32,0,0,0,0,0,0,0,0,85,112,112,101,114,32,98,111,117,110,100,32,111,102,32,99,111,110,115,116,114,97,105,110,116,32,60,32,108,111,119,101,114,32,98,111,117,110,100,58,32,32,84,117,114,110,105,110,103,32,111,102,32,99,111,110,115,116,114,97,105,110,116,115,46,0,0,0,0,0,0,0,92,98,101,103,105,110,123,100,111,99,117,109,101,110,116,125,10,101,116,101,120,10,37,32,69,78,68,80,82,69,10,0,108,105,103,104,116,45,103,114,97,121,0,0,0,0,0,0,98,111,120,32,97,110,100,32,119,104,105,115,107,101,114,0,92,117,115,101,112,97,99,107,97,103,101,91,105,110,116,108,105,109,105,116,115,93,123,97,109,115,109,97,116,104,125,10,92,117,115,101,112,97,99,107,97,103,101,123,97,109,115,102,111,110,116,115,125,10,0,0,103,114,101,101,110,121,101,108,108,111,119,0,0,0,0,0,102,105,110,97,110,99,101,32,98,97,114,0,0,0,0,0,124,115,105,110,40,55,50,48,120,41,124,0,0,0,0,0,92,117,115,101,112,97,99,107,97,103,101,91,108,97,116,105,110,49,93,123,105,110,112,117,116,101,110,99,125,10,92,117,115,101,112,97,99,107,97,103,101,91,84,49,93,123,102,111,110,116,101,110,99,125,10,92,117,115,101,112,97,99,107,97,103,101,123,116,101,120,116,99,111,109,112,125,10,92,117,115,101,112,97,99,107,97,103,101,123,109,97,116,104,112,116,109,120,125,10,92,117,115,101,112,97,99,107,97,103,101,91,115,99,97,108,101,100,61,46,57,50,93,123,104,101,108,118,101,116,125,10,92,117,115,101,112,97,99,107,97,103,101,123,99,111,117,114,105,101,114,125,10,92,117,115,101,112,97,99,107,97,103,101,123,108,97,116,101,120,115,121,109,125,10,0,0,99,104,97,114,116,114,101,117,115,101,0,0,0,0,0,0,9,98,111,120,112,108,111,116,32,114,101,112,114,101,115,101,110,116,97,116,105,111,110,32,105,115,32,37,115,10,0,0,109,117,108,116,105,112,108,111,116,32,109,111,100,101,32,105,115,32,37,115,10,0,0,0,92,117,115,101,112,97,99,107,97,103,101,91,108,97,116,105,110,49,93,123,105,110,112,117,116,101,110,99,125,10,92,117,115,101,112,97,99,107,97,103,101,91,84,49,93,123,102,111,110,116,101,110,99,125,10,92,117,115,101,112,97,99,107,97,103,101,123,116,105,109,101,115,44,109,97,116,104,112,116,109,120,125,10,92,117,115,101,112,97,99,107,97,103,101,123,104,101,108,118,101,116,125,10,92,117,115,101,112,97,99,107,97,103,101,123,99,111,117,114,105,101,114,125,10,0,0,0,0,97,110,116,105,113,117,101,119,104,105,116,101,0,0,0,0,37,103,58,0,0,0,0,0,44,32,102,105,108,108,115,116,121,108,101,0,0,0,0,0,92,100,111,99,117,109,101,110,116,99,108,97,115,115,123,97,114,116,105,99,108,101,125,10,0,0,0,0,0,0,0,0,115,101,97,103,114,101,101,110,0,0,0,0,0,0,0,0,44,32,108,119,32,37,46,49,102,32,0,0,0,0,0,0,92,100,111,99,117,109,101,110,116,99,108,97,115,115,91,97,52,112,97,112,101,114,93,123,97,114,116,105,99,108,101,125,10,0,0,0,0,0,0,0,115,108,97,116,101,103,114,101,121,0,0,0,0,0,0,0,108,116,32,37,100,0,0,0,110,111,101,110,104,36,97,110,99,101,100,0,0,0,0,0,10,37,37,32,65,100,100,32,92,100,111,99,117,109,101,110,116,99,108,97,115,115,32,97,110,100,32,92,98,101,103,105,110,123,100,99,111,117,109,101,110,116,125,32,102,111,114,32,108,97,116,101,120,10,37,37,32,78,66,32,121,111,117,32,115,104,111,117,108,100,32,115,101,116,32,116,104,101,32,101,110,118,105,114,111,110,109,101,110,116,32,118,97,114,105,97,98,108,101,32,84,69,88,32,116,111,32,116,104,101,32,110,97,109,101,32,111,102,32,121,111,117,114,10,37,37,32,108,97,116,101,120,32,101,120,101,99,117,116,97,98,108,101,32,40,110,111,114,109,97,108,108,121,32,108,97,116,101,120,41,32,105,110,111,114,100,101,114,32,102,111,114,32,109,101,116,97,112,111,115,116,32,116,111,32,119,111,114,107,10,37,37,32,111,114,32,114,117,110,10,37,37,32,109,112,111,115,116,32,45,45,116,101,120,61,108,97,116,101,120,32,46,46,46,10,10,37,32,66,69,71,80,82,69,10,118,101,114,98,97,116,105,109,116,101,120,10,0,104,111,110,101,121,100,101,119,0,0,0,0,0,0,0,0,120,50,121,50,0,0,0,0,32,32,81,117,97,114,116,105,108,101,58,32,37,115,32,37,115,32,37,115,10,0,0,0,98,97,99,107,103,114,111,117,110,100,0,0,0,0,0,0,37,46,49,53,103,0,0,0,117,105,110,116,0,0,0,0,112,114,111,108,111,103,117,101,115,58,61,37,100,59,10,0,99,111,109,109,97,110,100,32,115,116,114,105,110,103,0,0,98,105,115,113,117,101,0,0,98,101,104,105,110,100,0,0,120,121,101,114,114,111,114,108,105,110,101,115,10,0,0,0,78,101,101,100,32,50,32,111,114,32,51,32,99,111,108,117,109,110,115,32,102,111,114,32,112,111,108,97,114,32,100,97,116,97,0,0,0,0,0,0,102,105,108,108,36,115,116,121,108,101,0,0,0,0,0,0,69,120,112,101,99,116,101,100,32,99,111,109,109,97,110,100,32,115,116,114,105,110,103,0,97,118,115,0,0,0,0,0,37,37,71,78,85,80,76,79,84,32,77,101,116,97,112,111,115,116,32,111,117,116,112,117,116,58,32,37,115,10,0,0,108,101,109,111,110,99,104,105,102,102,111,110,0,0,0,0,75,80,95,82,105,103,104,116,0,0,0,0,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,105,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,32,116,121,112,101,0,0,0,9,82,101,99,116,97,110,103,108,101,32,115,116,121,108,101,32,105,115,32,37,115,44,32,102,105,108,108,32,99,111,108,111,114,32,0,0,0,0,0,101,110,100,46,10,0,0,0,107,104,97,107,105,49,0,0,9,67,105,114,99,108,101,32,115,116,121,108,101,32,104,97,115,32,100,101,102,97,117,108,116,32,114,97,100,105,117,115,32,0,0,0,0,0,0,0,110,111,32,117,112,112,101,114,32,98,111,117,110,100,32,99,111,110,115,116,114,97,105,110,116,32,97,108,108,111,119,101,100,32,105,102,32,110,111,116,32,97,117,116,111,115,99,97,108,105,110,103,0,0,0,0,99,111,110,116,111,117,114,32,98,95,115,112,108,105,110,101,0,0,0,0,0,0,0,0,37,32,69,78,68,80,79,83,84,10,0,0,0,0,0,0,112,105,110,107,0,0,0,0,115,101,116,32,116,101,114,109,105,110,97,108,32,117,110,107,110,111,119,110,10,0,0,0,44,32,98,111,116,104,32,100,105,97,109,101,116,101,114,115,32,97,114,101,32,105,110,32,116,104,101,32,115,97,109,101,32,117,110,105,116,115,32,97,115,32,116,104,101,32,121,32,97,120,105,115,10,0,0,0,60,110,111,62,0,0,0,0,101,116,101,120,10,0,0,0,108,105,103,104,116,45,115,97,108,109,111,110,0,0,0,0,44,32,98,111,116,104,32,100,105,97,109,101,116,101,114,115,32,97,114,101,32,105,110,32,116,104,101,32,115,97,109,101,32,117,110,105,116,115,32,97,115,32,116,104,101,32,120,32,97,120,105,115,10,0,0,0,55,50,48,32,109,117,108,32,115,105,110,32,97,98,115,0,32,92,101,110,100,123,100,111,99,117,109,101,110,116,125,10,0,0,0,0,0,0,0,0,115,97,110,100,121,98,114,111,119,110,0,0,0,0,0,0,117,0,0,0,0,0,0,0,44,32,100,105,97,109,101,116,101,114,115,32,97,114,101,32,105,110,32,100,105,102,102,101,114,101,110,116,32,117,110,105,116,115,32,40,109,97,106,111,114,58,32,120,32,97,120,105,115,44,32,109,105,110,111,114,58,32,121,32,97,120,105,115,41,10,0,0,0,0,0,0,45,45,104,101,108,112,0,0,118,101,114,98,97,116,105,109,116,101,120,10,0,0,0,0,116,97,110,49,0,0,0,0,42,58,0,0,0,0,0,0,44,32,100,101,102,97,117,108,116,32,97,110,103,108,101,32,105,115,32,37,46,49,102,32,100,101,103,114,101,101,115,0,97,99,116,105,111,110,32,116,97,98,108,101,0,0,0,0,37,32,66,69,71,80,79,83,84,10,0,0,0,0,0,0,115,105,101,110,110,97,49,0,9,69,108,108,105,112,115,101,32,115,116,121,108,101,32,104,97,115,32,100,101,102,97,117,108,116,32,115,105,122,101,32,0,0,0,0,0,0,0,0,101,110,100,102,105,103,59,10,0,0,0,0,0,0,0,0,121,101,108,108,111,119,52,0,70,117,110,99,116,105,111,110,115,0,0,0,0,0,0,0,106,116,101,114,110,0,0,0,101,120,112,101,99,116,105,110,103,32,39,114,97,100,105,97,110,115,39,32,111,114,32,39,100,101,103,114,101,101,115,39,0,0,0,0,0,0,0,0,101,110,104,36,97,110,99,101])
+.concat([100,0,0,0,0,0,0,0,115,99,97,108,101,112,101,110,32,49,59,32,112,116,115,105,122,101,32,37,46,51,102,59,108,105,110,101,116,121,112,101,32,45,50,59,10,0,0,0,115,108,97,116,101,98,108,117,101,49,0,0,0,0,0,0,120,49,121,49,0,0,0,0,68,97,116,97,0,0,0,0,32,32,77,97,120,105,109,117,109,58,32,32,37,115,32,91,37,42,108,100,93,32,32,32,37,115,32,91,37,42,108,100,93,10,0,0,0,0,0,0,37,115,58,37,100,32,112,114,111,116,111,99,111,108,32,101,114,114,111,114,10,0,0,0,100,36,101,103,114,101,101,115,0,0,0,0,0,0,0,0,109,117,108,116,105,112,108,111,116,62,32,0,0,0,0,0,37,48,51,111,0,0,0,0,100,0,0,0,0,0,0,0,97,58,61,119,47,37,46,49,102,59,98,58,61,104,47,37,46,49,102,59,10,0,0,0,109,101,100,105,117,109,112,117,114,112,108,101,51,0,0,0,9,37,115,32,105,115,32,117,110,100,101,102,105,110,101,100,10,0,0,0,0,0,0,0,115,116,121,108,101,32,108,105,110,101,0,0,0,0,0,0,114,36,97,100,105,97,110,115,0,0,0,0,0,0,0,0,120,101,114,114,111,114,108,105,110,101,115,10,0,0,0,0,78,101,101,100,32,49,32,111,114,32,51,32,99,111,108,117,109,110,115,32,102,111,114,32,99,97,114,116,101,115,105,97,110,32,100,97,116,97,0,0,67,97,110,39,116,32,99,104,97,110,103,101,32,116,111,32,116,104,105,115,32,100,105,114,101,99,116,111,114,121,0,0,126,0,0,0,0,0,0,0,83,105,110,103,117,108,97,114,32,109,97,116,114,105,120,32,105,110,32,71,105,118,101,110,115,40,41,0,0,0,0,0,67,97,110,39,116,32,99,97,108,99,117,108,97,116,101,32,97,112,112,114,111,120,105,109,97,116,105,111,110,32,115,112,108,105,110,101,115,44,32,97,108,108,32,119,101,105,103,104,116,115,32,104,97,118,101,32,116,111,32,98,101,32,62,32,48,0,0,0,0,0,0,0,10,98,101,103,105,110,102,105,103,40,37,100,41,59,10,119,58,61,37,46,51,102,105,110,59,104,58,61,37,46,51,102,105,110,59,10,0,0,0,0,99,98,116,105,99,115,0,0,111,114,99,104,105,100,52,0,75,80,95,66,101,103,105,110,0,0,0,0,0,0,0,0,9,37,115,10,0,0,0,0,114,103,98,36,99,111,108,111,114,0,0,0,0,0,0,0,73,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,115,116,114,105,110,103,32,110,111,116,32,97,108,108,111,99,97,116,101,100,0,0,0,0,100,117,112,108,105,99,97,116,101,32,111,114,32,99,111,110,116,114,97,100,105,99,116,111,114,121,32,97,114,103,117,109,101,110,116,115,0,0,0,0,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,0,115,105,101,110,110,97,52,0,10,9,85,115,101,114,45,68,101,102,105,110,101,100,32,70,117,110,99,116,105,111,110,115,58,10,0,0,0,0,0,0,102,117,110,99,116,105,111,110,32,37,115,32,114,101,113,117,105,114,101,115,32,37,100,32,118,97,114,105,97,98,108,101,37,99,0,0,0,0,0,0,119,114,111,110,103,32,97,114,103,117,109,101,110,116,32,105,110,32,115,101,116,32,97,114,114,111,119,0,0,0,0,0,109,97,108,102,111,114,109,101,100,32,114,97,110,103,101,32,119,105,116,104,32,99,111,110,115,116,114,97,105,110,116,32,40,117,115,101,32,39,60,39,32,111,110,108,121,41,0,0,100,114,97,119,32,40,37,46,49,102,97,44,37,46,49,102,98,41,0,0,0,0,0,0,98,114,111,119,110,52,0,0,100,101,102,97,117,108,116,32,108,97,121,101,114,0,0,0,101,110,100,32,99,111,111,114,100,105,110,97,116,101,115,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,0,0,108,105,110,101,116,121,112,101,32,37,100,59,10,0,0,0,111,114,97,110,103,101,114,101,100,52,0,0,0,0,0,0,9,71,114,105,100,32,100,114,97,119,110,32,97,116,32,37,115,10,0,0,0,0,0,0,115,116,97,114,116,32,99,111,111,114,100,105,110,97,116,101,115,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,124,99,111,115,40,51,54,48,120,41,124,0,0,0,0,0,112,117,116,95,116,101,120,116,40,32,98,116,101,120,32,37,115,32,101,116,101,120,44,32,37,46,49,102,97,44,32,37,46,49,102,98,44,32,37,100,44,32,37,100,41,59,10,0,100,97,114,107,45,111,108,105,118,101,103,114,101,101,110,0,100,101,103,114,101,101,115,0,97,114,114,111,119,0,0,0,112,117,116,95,116,101,120,116,40,32,98,116,101,120,32,92,115,101,116,102,111,110,116,123,37,115,125,123,37,53,46,50,102,125,32,37,115,32,101,116,101,120,44,32,37,46,49,102,97,44,32,37,46,49,102,98,44,32,37,100,44,32,37,100,41,59,10,0,0,0,0,0,100,97,114,107,45,112,108,117,109,0,0,0,0,0,0,0,114,97,100,105,97,110,115,0,32,32,32,32,32,32,32,32,32,83,107,105,112,112,101,100,32,37,100,32,112,111,105,110,116,115,32,111,117,116,115,105,100,101,32,114,97,110,103,101,32,91,37,115,61,0,0,0,116,97,103,32,109,117,115,116,32,98,101,32,62,32,48,0,112,117,116,95,116,101,120,116,40,34,37,115,34,44,32,37,46,49,102,97,44,32,37,46,49,102,98,44,32,37,100,44,32,37,100,41,59,10,0,0,112,108,117,109,0,0,0,0,9,71,114,105,100,32,114,97,100,105,105,32,100,114,97,119,110,32,101,118,101,114,121,32,37,102,32,37,115,10,0,0,112,117,116,95,116,101,120,116,40,34,37,115,34,32,105,110,102,111,110,116,115,105,122,101,40,34,37,115,34,44,37,53,46,50,102,41,44,32,37,46,49,102,97,44,32,37,46,49,102,98,44,32,37,100,44,32,37,100,41,59,10,0,0,0,100,97,114,107,45,118,105,111,108,101,116,0,0,0,0,0,10,9,77,105,110,111,114,32,103,114,105,100,32,100,114,97,119,110,32,119,105,116,104,0,106,117,109,112,110,122,0,0,104,101,97,100,36,115,0,0,102,111,110,116,0,0,0,0,103,112,100,114,97,119,40,37,100,44,37,46,49,102,97,44,37,46,49,102,98,41,59,10,0,0,0,0,0,0,0,0,118,105,111,108,101,116,0,0,9,77,97,106,111,114,32,103,114,105,100,32,100,114,97,119,110,32,119,105,116,104,0,0,32,32,77,105,110,105,109,117,109,58,32,32,37,115,32,91,37,42,108,100,93,32,32,32,37,115,32,91,37,42,108,100,93,10,0,0,0,0,0,0,97,115,0,0,0,0,0,0,99,111,110,118,95,116,101,120,116,32,98,117,102,102,101,114,0,0,0,0,0,0,0,0,105,0,0,0,0,0,0,0,100,114,97,119,32,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,59,10,0,0,0,0,0,111,114,97,110,103,101,0,0,32,116,105,99,115,10,0,0,108,105,110,101,116,121,112,101,0,0,0,0,0,0,0,0,121,101,114,114,111,114,108,105,110,101,115,10,0,0,0,0,101,120,112,101,99,116,105,110,103,32,100,105,114,101,99,116,111,114,121,32,110,97,109,101,0,0,0,0,0,0,0,0,33,0,0,0,0,0,0,0,100,114,97,119,100,98,108,97,114,114,111,119,0,0,0,0,111,108,105,118,101,0,0,0,75,80,95,76,101,102,116,0,32,109,37,115,0,0,0,0,66,111,116,104,32,112,97,114,97,109,101,116,101,114,115,32,116,111,32,115,116,114,112,116,105,109,101,32,109,117,115,116,32,98,101,32,115,116,114,105,110,103,115,0,0,0,0,0,100,114,97,119,97,114,114,111,119,0,0,0,0,0,0,0,98,101,105,103,101,0,0,0,32,37,115,0,0,0,0,0,98,97,99,107,36,104,101,97,100,0,0,0,0,0,0,0,122,50,0,0,0,0,0,0,37,115,32,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,59,10,0,0,0,0,0,0,0,100,97,114,107,45,103,111,108,100,101,110,114,111,100,0,0,82,101,99,116,97,110,103,117,108,97,114,0,0,0,0,0,121,95,109,105,110,32,115,104,111,117,108,100,32,110,111,116,32,101,113,117,97,108,32,121,95,109,97,120,33,0,0,0,73,110,118,97,108,105,100,32,114,97,110,103,101,0,0,0,112,116,115,105,122,101,32,37,46,51,102,59,10,0,0,0,100,97,114,107,45,107,104,97,107,105,0,0,0,0,0,0,80,111,108,97,114,0,0,0,37,115,102,105,120,109,97,36,120,0,0,0,0,0,0,0,51,54,48,32,109,117,108,32,99,111,115,32,97,98,115,0,102,105,108,108,32,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,99,121,99,108,101,32,119,105,116,104,112,101,110,32,40,112,101,110,99,105,114,99,108,101,32,115,99,97,108,101,100,32,48,112,116,41,32,119,105,116,104,99,111,108,111,114,32,102,105,108,108,99,111,108,111,114,59,10,0,0,0,0,0,107,104,97,107,105,0,0,0,9,37,115,32,103,114,105,100,32,100,114,97,119,110,32,97,116,0,0,0,0,0,0,0,37,115,102,105,120,109,105,36,110,0,0,0,0,0,0,0,9,32,32,72,105,100,100,101,110,51,100,32,101,108,101,109,101,110,116,115,32,119,105,108,108,32,98,101,32,100,114,97,119,110,32,105,110,32,37,115,32,111,102,32,110,111,110,45,104,105,100,100,101,110,51,100,32,101,108,101,109,101,110,116,115,10,0,0,0,0,0,0,102,105,108,108,32,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,40,37,46,49,102,97,44,37,46,49,102,98,41,45,45,99,121,99,108,101,32,119,105,116,104,99,111,108,111,114,32,98,97,99,107,103,114,111,117,110,100,59,10,0,0,0,100,97,114,107,45,115,97,108,109,111,110,0,0,0,0,0,9,103,114,105,100,32,105,115,32,79,70,70,10,0,0,0,32,32,32,32,32,32,32,32,32,82,101,97,100,32,37,100,32,112,111,105,110,116,115,10,0,0,0,0,0,0,0,0,37,115,102,105,120,0,0,0,115,99,97,108,101,112,101,110,32,37,46,51,102,59,10,0,115,97,108,109,111,110,0,0,114,97,120,105,115,32,105,115,32,37,115,100,114,97,119,110,10,0,0,0,0,0,0,0,37,115,109,97,36,120,0,0,80,114,101,115,115,32,114,101,116,117,114,110,32,102,111,114,32,109,111,114,101,58,32,0,99,117,114,114,101,110,116,99,111,108,111,114,58,61,37,46,52,103,42,114,101,100,43,37,46,52,103,42,103,114,101,101,110,43,37,46,52,103,42,98,108,117,101,59,10,0,0,0,111,114,97,110,103,101,45,114,101,100,0,0,0,0,0,0,9,37,115,122,101,114,111,97,120,105,115,32,105,115,32,79,70,70,10,0,0,0,0,0,106,117,109,112,122,0,0,0,37,115,109,105,36,110,0,0,116,105,36,116,108,101,0,0,99,117,114,114,101,110,116,99,111,108,111,114,58,61,99,111,108,37,100,59,10,0,0,0,108,105,103,104,116,45,99,111,114,97,108,0,0,0,0,0,125,0,0,0,0,0,0,0,9,37,115,122,101,114,111,97,120,105,115,32,105,115,32,100,114,97,119,110,32,119,105,116,104,0,0,0,0,0,0,0,32,32,83,117,109,32,83,113,46,58,32,32,37,115,32,37,115,32,37,115,10,0,0,0,107,101,36,101,112,102,105,120,0,0,0,0,0,0,0,0,117,110,107,110,111,119,110,32,116,121,112,101,32,105,110,32,100,105,115,112,95,118,97,108,117,101,40,41,0,0,0,0,115,105,110,116,0,0,0,0,99,117,114,114,101,110,116,99,111,108,111,114,58,61,98,108,97,99,107,59,10,0,0,0,108,97,98,101,108,32,110,111,116,32,102,111,117,110,100,0,69,120,116,114,97,110,101,111,117,115,32,97,114,103,117,109,101,110,116,115,32,116,111,32,115,101,116,32,37,115,0,0,102,105,120,0,0,0,0,0,100,111,116,115,10,0,0,0,120,112,45,62,110,101,120,116,95,115,112,32,61,61,32,121,112,0,0,0,0,0,0,0,99,117,114,114,101,110,116,99,111,108,111,114,58,61,37,46,51,103,119,104,105,116,101,59,10,0,0,0,0,0,0,0,100,97,114,107,45,112,105,110,107,0,0,0,0,0,0,0,75,80,95,80,97,103,101,68,111,119,110,0,0,0,0,0,32,112,111,105,110,116,32,119,105,116,104,32,99,111,108,111,114,32,111,102,0,0,0,0,98,117,102,102,101,114,91,108,101,110,103,116,104,45,49,93,32,61,61,32,39,32,39,0,102,117,108,108,36,119,105,100,116,104,0,0,0,0,0,0,59,10,0,0,0,0,0,0,100,97,114,107,45,116,117,114,113,117,111,105,115,101,0,0,32,110,111,112,111,105,110,116,0,0,0,0,0,0,0,0,108,36,97,114,103,101,0,0,100,101,102,97,117,108,116,0,37,115,32,61,32,0,0,0,109,97,108,102,111,114,109,101,100,32,114,97,110,103,101,32,119,105,116,104,32,99,111,110,115,116,97,114,105,110,116,0,99,121,99,108,101,32,119,105,116,104,99,111,108,111,114,32,102,105,108,108,99,111,108,111,114,59,10,0,0,0,0,0,32,102,111,110,116,32,34,37,115,34,0,0,0,0,0,0,120,95,109,105,110,32,115,104,111,117,108,100,32,110,111,116,32,101,113,117,97,108,32,120,95,109,97,120,33,0,0,0,45,45,0,0,0,0,0,0,32,37,115,32,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,97,98,115,111,108,117,116,101,39,32,111,114,32,39,114,101,108,97,116,105,118,101,39,32,0,0,0,0,0,77,79,85,83,69,95,0,0,124,115,105,110,40,51,54,48,120,41,124,0,0,0,0,0,10,45,45,0,0,0,0,0,116,105,109,101,108,97,98,101,108,46,116,101,120,116,0,0,115,107,121,98,108,117,101,0,32,110,111,116,32,114,111,116,97,116,101,100,0,0,0,0,114,36,101,108,97,116,105,118,101,0,0,0,0,0,0,0,40,37,46,49,102,97,44,37,46,49,102,98,41,37,115,0,109,101,100,105,117,109,45,98,108,117,101,0,0,0,0,0,32,114,111,116,97,116,101,100,32,98,121,32,37,100,32,100,101,103,114,101,101,115,32,40,105,102,32,112,111,115,115,105,98,108,101,41,0,0,0,0,97,36,98,115,111,108,117,116,101,0,0,0,0,0,0,0,98,97,100,32,100,97,116,97,32,111,110,32,108,105,110,101,32,37,100,32,111,102,32,100,97,116,97,102,105,108,101,0,115,101,116,32,37,115,122,101,114,111,97,120,105,115,0,0,102,105,108,108,32,0,0,0,110,97,118,121,0,0,0,0,32,99,101,110,116,114,101,0,112,114,105,110,116,95,116,97,98,108,101,58,32,111,117,116,112,117,116,32,98,117,102,102,101,114,0,0,0,0,0,0,73,110,116,101,114,110,97,108,32,101,114,114,111,114,32,105,110,32,115,99,105,101,110,116,105,102,105,99,32,110,117,109,98,101,114,32,102,111,114,109,97,116,116,105,110,103,0,0,102,105,108,108,99,111,108,111,114,58,61,99,117,114,114,101,110,116,99,111,108,111,114,59,10,0,0,0,0,0,0,0,109,105,100,110,105,103,104,116,45,98,108,117,101,0,0,0,9,108,97,98,101,108,32,37,100,32,34,37,115,34,32,97,116,32,0,0,0,0,0,0,122,95,109,105,110,51,100,32,115,104,111,117,108,100,32,110,111,116,32,101,113,117,97,108,32,122,95,109,97,120,51,100,33,0,0,0,0,0,0,0,106,117,109,112,0,0,0,0,32,40,0,0,0,0,0,0,102,105,108,108,99,111,108,111,114,58,61,99,117,114,114,101,110,116,99,111,108,111,114,42,37,46,50,102,43,98,97,99,107,103,114,111,117,110,100,42,37,46,50,102,59,10,0,0,100,97,114,107,45,98,108,117,101,0,0,0,0,0,0,0,123,0,0,0,0,0,0,0,32,32,83,117,109,58,32,32,32,32,32,32,37,115,32,37,115,32,37,115,10,0,0,0,34,37,115,34,0,0,0,0,102,105,108,108,99,111,108,111,114,58,61,98,97,99,107,103,114,111,117,110,100,59,10,0,44,32,98,97,99,107,97,110,103,108,101,32,37,103,32,100,101,103,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,108,105,110,101,97,114,39,44,32,39,99,117,98,105,99,115,112,108,105,110,101,39,44,32,39,98,115,112,108,105,110,101,39,44,32,39,112,111,105,110,116,115,39,44,32,39,108,101,118,101,108,115,39,32,111,114,32,39,111,114,100,101,114,39,0,0,0,0,0,115,101,116,32,37,115,116,105,99,115,32,37,115,32,0,0,108,105,110,101,115,112,111,105,110,116,115,10,0,0,0,0,71,80,86,65,76,95,76,65,83,84,95,80,76,79,84,0,47,0,0,0,0,0,0,0,100,101,102,97,117,108,116,102,111,110,116,0,0,0,0,0,115,101,97,45,103,114,101,101,110,0,0,0,0,0,0,0,75,80,95,68,111,119,110,0,10,9,32,32,97,114,114,111,119,32,104,101,97,100,58,32,108,101,110,103,116,104,32,37,115,37,103,44,32,97,110,103,108,101,32,37,103,32,100,101,103,0,0,0,0,0,0,0,98,115,112,108,105,110,101,32,111,114,100,101,114,32,109,117,115,116,32,98,101,32,105,110,32,91,50,46,46,49,48,93,32,114,97,110,103,101,46,0,32,110,111,114,97,110,103,101,108,105,109,105,116,0,0,0,101,120,116,101,114,110,97,108,36,105,109,97,103,101,115,0,102,111,114,101,115,116,45,103,114,101,101,110,0,0,0,0,40,115,101,99,111,110,100,32,120,32,97,120,105,115,41,32,0,0,0,0,0,0,0,0,111,36,114,100,101,114,0,0,32,114,97,110,103,101,108,105,109,105,116,0,0,0,0,0,117,110,102,105,110,105,115,104,101,100,32,114,97,110,103,101,32,119,105,116,104,32,99,111,110,115,116,114,97,105,110,116,0,0,0,0,0,0,0,0,105,110,108,105,110,101,36,105,109,97,103,101,115,0,0,0,115,112,114,105,110,103,45,103,114,101,101,110,0,0,0,0,40,102,105,114,115,116,32,120,32,97,120,105,115,41,32,0,76,101,118,101,108,115,32,116,121,112,101,32,105,115,32,100,105,115,99,114,101,116,101,44,32,105,103,110,111,114,105,110,103,32,110,101,119,32,110,117,109,98,101,114,32,111,102,32,99,111,110,116,111,117,114,32,108,101,118,101,108,115,0,0,84,101,114,109,105,110,97,108,32,99,97,110,118,97,115,32,97,114,101,97,32,116,111,111,32,115,109,97,108,108,32,116,111,32,104,111,108,100,32,112,108,111,116,46,10,9,32,32,32,32,67,104,101,99,107,32,112,108,111,116,32,98,111,117,110,100,97,114,121,32,97,110,100,32,102,111,110,116,32,115,105,122,101,115,46,0,0,0,112,111,105,110,116,115,119,105,116,104,116,101,120,0,0,0,100,97,114,107,45,103,114,101,101,110,0,0,0,0,0,0,32,116,111,32,0,0,0,0,97,117,36,116,111,0,0,0,10,115,101,116,32,37,115,100,116,105,99,115,0,0,0,0,51,54,48,32,109,117,108,32,115,105,110,32,97,98,115,0,112,111,105,110,116,115,119,105,116,104,109,101,116,97,112,111,115,116,0,0,0,0,0,0,32,114,116,111,32,0,0,0,101,120,112,101,99,116,105,110,103,32,99,111,109,109,97,32,116,111,32,115,101,112,97,114,97,116,101,32,105,110,99,114,44,115,116,111,112,32,108,101,118,101,108,115,0,0,0,0,10,115,101,116,32,37,115,109,116,105,99,115,0,0,0,0,112,111,105,110,116,115,119,105,116,104,109,112,0,0,0,0,103,111,108,100,0,0,0,0,10,9,32,32,102,114,111,109,32,0,0,0,0,0,0,0,105,110,99,114,101,109,101,110,116,32,99,97,110,110,111,116,32,98,101,32,48,0,0,0,97,117,116,111,102,114,101,113,32,0,0,0,0,0,0,0,77,97,120,46,32,110,117,109,98,101,114,32,111,102,32,100,97,116,97,32,112,111,105,110,116,115,32,115,99,97,108,101,100,32,117,112,32,116,111,58,32,37,100,10,0,0,0,0,69,68,70,32,104,101,97,100,101,114,0,0,0,0,0,0,116,101,120,36,112,111,105,110,116,115,0,0,0,0,0,0,108,105,103,104,116,45,116,117,114,113,117,111,105,115,101,0,110,111,102,105,108,108,101,100,0,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,99,111,109,109,97,32,116,111,32,115,101,112,97,114,97,116,101,32,115,116,97,114,116,44,105,110,99,114,32,108,101,118,101,108,115,0,0,0,10,115,101,116,32,37,115,116,105,99,115,32,0,0,0,0,108,111,103,49,48,95,98,97,115,101,32,61,61,32,49,46,48,0,0,0,0,0,0,0,109,112,36,112,111,105,110,116,115,0,0,0,0,0,0,0,108,105,103,104,116,45,112,105,110,107,0,0,0,0,0,0,101,109,112,116,121,0,0,0,105,110,36,99,114,101,109,101,110,116,97,108,0,0,0,0,97,115,115,105,103,110,0,0,32,97,117,116,111,106,117,115,116,105,102,121,0,0,0,0,112,111,112,32,48,0,0,0,116,101,120,116,115,99,97,108,101,0,0,0,0,0,0,0,108,105,103,104,116,45,103,111,108,100,101,110,114,111,100,0,119,104,105,108,101,0,0,0,102,105,108,108,101,100,0,0,101,120,112,101,99,116,105,110,103,32,99,111,109,109,97,32,116,111,32,115,101,112,97,114,97,116,101,32,100,105,115,99,114,101,116,101,32,108,101,118,101,108,115,0,0,0,0,0,32,32,83,116,100,32,68,101,118,58,32,32,37,115,32,37,115,32,37,115,10,0,0,0,98,121,32,37,100,32,0,0,117,115,104,111,114,116,0,0,102,111,110,116,115,99,97,108,101,0,0,0,0,0,0,0,108,105,103,104,116,45,99,121,97,110,0,0,0,0,0,0,9,97,114,114,111,119,32,37,100,44,32,37,115,32,37,115,32,37,115,0,0,0,0,0,108,105,110,101,115,116,121,108,101,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,100,105,115,99,114,101,116,101,32,108,101,118,101,108,0,0,0,0,0,0,0,0,105,109,112,117,108,115,101,115,10,0,0,0,0,0,0,0,67,97,110,110,111,116,32,99,111,110,116,111,117,114,32,110,111,110,32,103,114,105,100,32,100,97,116,97,46,32,80,108,101,97,115,101,32,117,115,101,32,34,115,101,116,32,100,103,114,105,100,51,100,34,46,0,98,105,110,100,95,99,111,109,109,97,110,100,45,62,114,104,115,0,0,0,0,0,0,0,108,119,0,0,0,0,0,0,108,105,103,104,116,45,109,97,103,101,110,116,97,0,0,0,75,80,95,69,110,100,0,0,9,107,101,121,32,116,105,116,108,101,32,105,115,32,34,37,115,34,10,0,0,0,0,0,100,105,36,115,99,114,101,116,101,0,0,0,0,0,0,0,82,101,115,117,108,116,105,110,103,32,115,116,114,105,110,103,32,105,115,32,116,111,111,32,108,111,110,103,0,0,0,0,108,105,110,101,119,36,105,100,116,104,0,0,0,0,0,0,108,105,103,104,116,45,98,108,117,101,0,0,0,0,0,0,37,100,32,102,111,114,32,118,101,114,116,105,99,97,108,32,97,108,105,103,110,109,101,110,116,10,0,0,0,0,0,0,108,101,36,118,101,108,115,0,111,117,116,0,0,0,0,0,100,108,0,0,0,0,0,0,108,105,103,104,116,45,103,114,101,101,110,0,0,0,0,0,9,109,97,120,105,109,117,109,32,110,117,109,98,101,114,32,111,102,32,114,111,119,115,32,105,115,32,0,0,0,0,0,98,36,115,112,108,105,110,101,0,0,0,0,0,0,0,0,107,101,121,0,0,0,0,0,100,97,115,104,108,36,101,110,103,116,104,0,0,0,0,0,108,105,103,104,116,45,114,101,100,0,0,0,0,0,0,0,99,97,108,99,117,108,97,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,0,0,0,0,0,99,36,117,98,105,99,115,112,108,105,110,101,0,0,0,0,97,120,105,115,0,0,0,0,99,111,115,40,51,54,48,120,41,0,0,0,0,0,0,0,115,113,117,97,114,101,36,100,0,0,0,0,0,0,0,0,103,114,101,121,49,48,48,0,37,100,32,102,111,114,32,104,111,114,105,122,111,110,116,97,108,32,97,108,105,103,110,109,101,110,116,10,0,0,0,0,108,105,36,110,101,97,114,0,115,101,116,32,37,115,116,105,99,115,32,37,115,32,37,115,32,115,99,97,108,101,32,37,103,44,37,103,32,37,115,109,105,114,114,111,114,32,37,115,32,0,0,0,0,0,0,0,98,101,118,101,108,36,101,100,0,0,0,0,0,0,0,0,103,114,101,121,57,48,0,0,9,109,97,120,105,109,117,109,32,110,117,109,98,101,114,32,111,102,32,99,111,108,117,109,110,115,32,105,115,32,0,0,115,101,116,32,110,111,37,115,116,105,99,115,10,0,0,0,79,117,116,32,111,102,32,109,101,109,111,114,121,32,105,110,32,102,105,116,58,32,116,111,111,32,109,97,110,121,32,100,97,116,97,112,111,105,110,116,115,32,40,37,100,41,63,0,109,105,116,101,114,36,101,100,0,0,0,0,0,0,0,0,103,114,101,121,56,48,0,0,119,105,116,104,32,99,111,108,117,109,110,32,104,101,97,100,101,114,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,98,97,115,101,39,44,32,39,115,117,114,102,97,99,101,39,44,32,111,114,32,39,98,111,116,104,39,0,0,32,108,111,103,102,105,108,101,32,39,37,115,39,0,0,0,115,111,36,108,105,100,0,0,103,114,101,121,55,48,0,0,119,105,116,104,32,102,105,108,101,110,97,109,101,0,0,0,98,111,36,116,104,0,0,0,91,93,0,0,0,0,0,0,115,101,116,32,102,105,116,32,37,115,101,114,114,111,114,118,97,114,105,97,98,108,101,115,0,0,0,0,0,0,0,0,116,101,109,112,32,102,105,108,101,32,115,116,114,105,110,103,0,0,0,0,0,0,0,0,100,97,36,115,104,101,100,0,103,114,101,121,54,48,0,0,117,112,36,100,97,116,101,0,9,115,97,109,112,108,101,32,108,101,110,103,116,104,32,105,115,32,37,103,32,99,104,97,114,97,99,116,101,114,115,10,9,118,101,114,116,105,99,97,108,32,115,112,97,99,105,110,103,32,105,115,32,37,103,32,99,104,97,114,97,99,116,101,114,115,10,9,119,105,100,116,104,32,97,100,106,117,115,116,109,101,110,116,32,105,115,32,37,103,32,99,104,97,114,97,99,116,101,114,115,10,9,104,101,105,103,104,116,32,97,100,106,117,115,116,109,101,110,116,32,105,115,32,37,103,32,99,104,97,114,97,99,116,101,114,115,10,9,99,117,114,118,101,115,32,97,114,101,37,115,32,97,117,116,111,109,97,116,105,99,97,108,108,121,32,116,105,116,108,101,100,32,37,115,10,0,0,0,0,0,0,0,0,115,36,117,114,102,97,99,101,0,0,0,0,0,0,0,0,32,32,77,101,97,110,58,32,32,32,32,32,37,115,32,37,115,32,37,115,10,0,0,0,115,101,116,32,112,115,100,105,114,10,0,0,0,0,0,0,123,37,115,44,32,37,115,125,0,0,0,0,0,0,0,0,115,104,111,114,116,0,0,0,109,111,110,111,36,99,104,114,111,109,101,0,0,0,0,0,103,114,101,121,53,48,0,0,9,107,101,121,32,98,111,120,32,105,115,32,111,112,97,113,117,101,32,97,110,100,32,100,114,97,119,110,32,105,110,32,102,114,111,110,116,32,111,102,32,116,104,101,32,103,114,97,112,104,10,0,0,0,0,0,98,97,36,115,101,0,0,0,115,101,116,32,112,115,100,105,114,32,34,37,115,34,10,0,112,111,105,110,116,115,10,0,65,108,108,32,112,111,105,110,116,115,32,111,102,32,99,111,108,111,114,98,111,120,32,118,97,108,117,101,32,117,110,100,101,102,105,110,101,100,0,0,98,105,110,100,95,99,111,109,109,97,110,100,45,62,108,104,115,0,0,0,0,0,0,0,43,0,0,0,0,0,0,0,99,111,108,36,111,117,114,0,103,114,101,121,52,48,0,0,75,80,95,73,110,115,101,114,116,0,0,0,0,0,0,0,110,111,116,32,98,111,120,101,100,10,0,0,0,0,0,0,83,99,97,108,101,32,102,97,99,116,111,114,115,32,109,117,115,116,32,98,101,32,103,114,101,97,116,101,114,32,116,104,97,110,32,122,101,114,111,32,45,32,110,111,116,32,99,104,97,110,103,101,100,33,0,0,115,101,116,32,102,111,110,116,112,97,116,104,32,0,0,0,68,111,117,98,108,101,0,0,102,95,115,116,114,102,116,105,109,101,58,32,98,117,102,102,101,114,0,0,0,0,0,0,105,110,105,116,32,100,121,110,97,114,114,97,121,0,0,0,99,111,108,36,111,114,0,0,103,114,101,121,51,48,0,0,98,111,120,101,100,10,9,119,105,116,104,32,0,0,0,0,78,117,109,98,101,114,32,111,102,32,103,114,105,100,32,112,111,105,110,116,115,32,109,117,115,116,32,98,101,32,105,110,32,91,50,58,49,48,48,48,93,32,45,32,110,111,116,32,99,104,97,110,103,101,100,33,0,0,0,0,0,0,0,0,117,110,102,105,110,105,115,104,101,100,32,114,97,110,103,101,0,0,0,0,0,0,0,0,110,111,104,101,97,100,101,114,0,0,0,0,0,0,0,0,103,114,101,121,50,48,0,0,114,105,103,104,116,0,0,0,85,110,114,101,99,111,103,110,105,122,101,32,107,101,121,119,111,114,100,32,111,114,32,117,110,101,120,112,101,99,116,101,100,32,118,97,108,117,101,0,100,105,102,102,105,99,117,108,116,121,32,109,97,107,105,110,103,32,114,111,111,109,32,102,111,114,32,120,116,105,99,32,108,97,98,101,108,115,0,0,115,101,116,32,108,111,97,100,112,97,116,104,32,0,0,0,104,101,97,100,101,114,0,0,103,114,101,121,49,48,0,0,108,101,102,116,0,0,0,0,107,100,101,110,115,36,105,116,121,50,100,0,0,0,0,0,51,54,48,32,109,117,108,32,99,111,115,0,0,0,0,0,110,111,116,105,109,101,36,115,116,97,109,112,0,0,0,0,103,114,101,121,48,0,0,0,9,107,101,121,32,105,115,32,37,115,32,106,117,115,116,105,102,105,101,100,44,32,37,115,114,101,118,101,114,115,101,100,44,32,37,115,105,110,118,101,114,116,101,100,44,32,37,115,101,110,104,97,110,99,101,100,32,97,110,100,32,0,0,0,100,101,99,105,109,97,108,95,115,105,103,110,32,105,110,32,108,111,99,97,108,101,32,105,115,32,37,115,10,0,0,0,101,114,114,111,114,32,100,117,114,105,110,103,32,102,105,116,0,0,0,0,0,0,0,0,116,105,109,101,36,115,116,97,109,112,0,0,0,0,0,0,116,117,114,113,117,111,105,115,101,0,0,0,0,0,0,0,9,107,101,121,32,105,115,32,97,116,32,0,0,0,0,0,67,111,117,108,100,32,110,111,116,32,102,105,110,100,32,114,101,113,117,101,115,116,101,100,32,108,111,99,97,108,101,0,115,116,97,110,100,36,97,108,111,110,101,0,0,0,0,0,32,114,109,97,114,103,105,110,0,0,0,0,0,0,0,0,76,65,78,71,0,0,0,0,85,83,69,82,78,65,77,69,0,0,0,0,0,0,0,0,105,110,112,36,117,116,0,0,32,108,109,97,114,103,105,110,0,0,0,0,0,0,0,0,76,67,95,78,85,77,69,82,73,67,0,0,0,0,0,0,110,101,115,0,0,0,0,0,67,108,111,115,105,110,103,32,37,115,10,0,0,0,0,0,100,101,102,97,117,108,116,115,105,122,101,0,0,0,0,0,97,113,117,97,109,97,114,105,110,101,0,0,0,0,0,0,117,110,115,36,101,116,0,0,32,98,109,97,114,103,105,110,0,0,0,0,0,0,0,0,76,67,95,65,76,76,0,0,42,32,67,79,76,85,77,78,83,58,10,0,0,0,0,0,102,114,97,99,116,105,111,110,0,0,0,0,0,0,0,0,117,99,104,97,114,0,0,0,115,105,122,101,0,0,0,0,111,114,99,104,105,100,0,0,32,116,109,97,114,103,105,110,0,0,0,0,0,0,0,0,99,97,110,100,108,101,115,0,108,105,110,101,115,10,0,0,65,108,108,32,112,111,105,110,116,115,32,122,32,118,97,108,117,101,32,117,110,100,101,102,105,110,101,100,0,0,0,0,119,104,105,115,107,101,114,36,98,97,114,115,0,0,0,0,99,111,109,109,97,110,100,46,99,0,0,0,0,0,0,0,100,36,101,102,97,117,108,116,0,0,0,0,0,0,0,0,100,97,114,107,45,99,104,97,114,116,114,101,117,115,101,0,75,80,95,70,52,0,0,0,32,111,117,116,115,105,100,101,0,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,115,101,99,111,110,100,32,100,117,109,109,121,32,118,97,114,105,97,98,108,101,32,110,97,109,101,0,0,0,0,120,121,61,37,103,44,37,103,0,0,0,0,0,0,0,0,70,108,111,97,116,0,0,0,102,105,110,97,110,99,101,98,97,114,115,0,0,0,0,0,115,105,122,101,58,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,0,0,0,0,100,97,114,107,45,114,101,100,0,0,0,0,0,0,0,0,32,105,110,115,105,100,101,0,101,120,112,101,99,116,105,110,103,32,100,117,109,109,121,32,118,97,114,105,97,98,108,101,32,110,97,109,101,0,0,0,115,101,116,32,115,116,121,108,101,32,98,111,120,112,108,111,116,32,37,115,32,37,115,32,37,53,46,50,102,32,37,115,111,117,116,108,105,101,114,115,32,112,116,32,37,100,32,115,101,112,97,114,97,116,105,111,110,32,37,103,32,108,97,98,101,108,115,32,37,115,32,37,115,115,111,114,116,101,100,10,0,0,0,0,0,0,0,0,105,110,36,99,104,101,115,0,115,116,101,101,108,98,108,117,101,0,0,0,0,0,0,0,32,104,111,114,105,122,111,110,116,97,108,0,0,0,0,0,105,99,111,110,118,32,102,97,105,108,101,100,32,116,111,32,99,111,110,118,101,114,116,32,100,101,103,114,101,101,32,115,105,103,110,0,0,0,0,0,120,116,105,99,0,0,0,0,10,117,110,115,101,116,32,99,111,108,111,114,98,111,120,10,0,0,0,0,0,0,0,0,115,105,122,101,32,114,101,113,117,105,114,101,115,32,116,119,111,32,110,117,109,98,101,114,115,58,32,32,120,115,105,122,101,44,32,121,115,105,122,101,0,0,0,0,0,0,0,0,112,117,114,112,108,101,0,0,32,118,101,114,116,105,99,97,108,0,0,0,0,0,0,0,105,99,111,110,118,95,111,112,101,110,32,102,97,105,108,101,100,32,102,111,114,32,37,115,0,0,0,0,0,0,0,0,98,111,114,100,101,114,32,37,100,0,0,0,0,0,0,0,115,105,110,40,51,54,48,120,41,0,0,0,0,0,0,0,101,120,112,97,110,100,32,108,111,97,100,112,97,116,104,0,102,111,110,116,32,34,37,115,44,37,103,34,0,0,0,0,100,97,114,107,45,115,112,114,105,110,103,45,103,114,101,101,110,0,0,0,0,0,0,0,32,99,101,110,116,101,114,0,98,100,101,102,97,117,108,116,0,0,0,0,0,0,0,0,109,112,112,111,105,110,116,115,32,0,0,0,0,0,0,0,103,111,108,100,101,110,114,111,100,0,0,0,0,0,0,0,41,0,0,0,0,0,0,0,32,114,105,103,104,116,0,0,117,110,114,101,99,111,103,110,105,122,101,100,32,101,110,99,111,100,105,110,103,32,115,112,101,99,105,102,105,99,97,116,105,111,110,59,32,115,101,101,32,39,104,101,108,112,32,101,110,99,111,100,105,110,103,39,46,0,0,0,0,0,0,0,110,111,98,111,114,100,101,114,0,0,0,0,0,0,0,0,101,114,114,36,111,114,115,116,97,116,101,0,0,0,0,0,73,108,108,101,103,97,108,32,100,97,121,32,111,102,32,121,101,97,114,0,0,0,0,0,116,101,120,112,111,105,110,116,115,32,0,0,0,0,0,0,114,111,121,97,108,98,108,117,101,0,0,0,0,0,0,0,32,108,101,102,116,0,0,0,57,51,50,0,0,0,0,0,47,117,115,114,47,108,111,99,97,108,47,115,104,97,114,101,47,103,104,111,115,116,115,99,114,105,112,116,47,102,111,110,116,115,0,0,0,0,0,0,85,83,69,82,0,0,0,0,115,111,108,105,100,0,0,0,100,97,114,107,45,121,101,108,108,111,119,0,0,0,0,0,83,74,73,83,0,0,0,0,101,113,115,0,0,0,0,0,104,111,114,105,122,111,110,116,0,0,0,0,0,0,0,0,78,111,32,116,101,114,109,105,110,97,108,32,100,101,102,105,110,101,100,0,0,0,0,0,100,97,115,104,101,100,0,0,100,97,114,107,45,111,114,97,110,103,101,0,0,0,0,0,117,110,100,36,101,102,105,110,101,0,0,0,0,0,0,0,9,107,101,121,32,105,115,32,79,78,44,32,112,111,115,105,116,105,111,110,58,32,0,0,118,101,114,116,105,99,0,0,99,0,0,0,0,0,0,0,32,37,115,32,100,97,115,104,108,101,110,103,116,104,32,37,103,32,108,105,110,101,119,105,100,116,104,32,37,103,32,102,111,110,116,115,99,97,108,101,32,37,103,32,92,10,32,32,32,0,0,0,0,0,0,0,100,97,114,107,45,99,121,97,110,0,0,0,0,0,0,0,9,107,101,121,32,105,115,32,79,70,70,10,0,0,0,0,85,84,70,0,0,0,0,0,115,101,116,32,99,111,108,111,114,98,111,120,32,37,115,97,108,32,111,114,105,103,105,110,32,0,0,0,0,0,0,0,32,114,103,98,32,34,35,37,54,46,54,120,34,32,0,0,65,108,108,32,112,111,105,110,116,115,32,121,32,118,97,108,117,101,32,117,110,100,101,102,105,110,101,100,0,0,0,0,84,104,105,115,32,112,108,111,116,32,115,116,121,108,101,32,105,115,32,111,110,108,121,32,102,111,114,32,100,97,116,97,102,105,108,101,115,44,32,114,101,118,101,114,116,105,110,103,32,116,111,32,34,112,111,105,110,116,115,34,0,0,0,0,40,98,105,110,100,95,99,111,109,109,97,110,100,41,32,37,115,58,37,100,10,0,0,0,60,61,0,0,0,0,0,0,114,111,117,110,100,0,0,0,100,97,114,107,45,109,97,103,101,110,116,97,0,0,0,0,75,80,95,70,51,0,0,0,32,111,110,108,121,10,0,0,117,116,102,0,0,0,0,0,37,115,61,37,103,0,0,0,68,111,117,98,108,101,86,97,108,117,101,0,0,0,0,0,102,95,115,116,114,102,116,105,109,101,58,32,102,109,116,0,117,115,101,114,0,0,0,0,98,101,118,101,108,101,100,32,0,0,0,0,0,0,0,0,119,101,98,45,98,108,117,101,0,0,0,0,0,0,0,0,9,110,111,32,108,111,103,115,99,97,108,105,110,103,10,0,108,111,99,97,108,101,0,0,115,101,116,32,99,111,108,111,114,98,111,120,32,37,115,10,0,0,0,0,0,0,0,0,114,111,117,110,100,101,100,32,0,0,0,0,0,0,0,0,119,101,98,45,103,114,101,101,110,0,0,0,0,0,0,0,32,97,110,100,0,0,0,0,117,110,107,110,111,119,110,32,45,45,45,32,101,120,112,101,99,116,101,100,32,39,108,111,103,102,105,108,101,39,32,111,114,32,91,110,111,93,101,114,114,111,114,118,97,114,105,97,98,108,101,115,0,0,0,0,65,108,108,32,112,111,105,110,116,115,32,111,102,32,99,111,108,111,114,32,97,120,105,115,32,117,110,100,101,102,105,110,101,100,46,0,0,0,0,0,105,115,111,95,56,56,53,57,95,57,0,0,0,0,0,0,10,35,32,120,32,121,0,0,109,105,116,101,114,101,100,32,0,0,0,0,0,0,0,0,9,108,111,103,115,99,97,108,105,110,103,0,0,0,0,0,110,111,113,117,105,101,116,0,99,117,98,101,104,101,108,105,120,32,115,116,97,114,116,32,37,46,50,103,32,99,121,99,108,101,115,32,37,46,50,103,32,115,97,116,117,114,97,116,105,111,110,32,37,46,50,103,10,0,0,0,0,0,0,0,51,54,48,32,109,117,108,32,115,105,110,0,0,0,0,0,109,111,110,111,99,104,114,111,109,101,32,0,0,0,0,0,100,97,114,107,45,103,114,101,121,0,0,0,0,0,0,0,37,115,32,37,115,32,40,98,97,115,101,32,37,103,41,0,113,117,105,101,116,0,0,0,102,117,110,99,116,105,111,110,115,32,37,115,44,32,37,115,44,32,37,115,10,0,0,0,112,117,115,104,100,49,0,0,99,111,108,111,114,32,0,0,110,101,120,116,102,114,111,109,95,100,121,110,97,114,114,97,121,58,32,100,121,110,97,114,114,97,121,32,119,97,110,39,116,32,105,110,105,116,105,97,108,105,122,101,100,33,0,0,9,111,102,102,115,101,116,115,32,97,114,101,0,0,0,0,110,111,101,114,114,36,111,114,118,97,114,105,97,98,108,101,115,0,0,0,0,0,0,0,32,41,10,0,0,0,0,0,122,58,115,10,0,0,0,0,34,32,92,10,32,32,32,0,9,116,109,97,114,103,105,110,32,105,115,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,0,0,0,101,114,114,36,111,114,118,97,114,105,97,98,108,101,115,0,92,10,32,32,32,32,0,0,47,117,115,114,47,115,104,97,114,101,47,103,104,111,115,116,115,99,114,105,112,116,47,102,111,110,116,115,0,0,0,0,37,51,111,37,110,0,0,0,34,0,0,0,0,0,0,0,99,111,114,110,101,114,115,50,99,36,111,108,111,114,0,0,9,116,109,97,114,103,105,110,32,105,115,32,115,101,116,32,116,111,32,37,103,10,0,0,101,120,112,101,99,116,105,110,103,32,115,116,114,105,110,103,0,0,0,0,0,0,0,0,99,111,110,99,97,116,101,110,97,116,101,0,0,0,0,0,99,97,110,110,111,116,32,111,112,101,110,32,102,105,108,101,59,32,111,117,116,112,117,116,32,110,111,116,32,99,104,97,110,103,101,100,0,0,0,0,37,115,0,0,0,0,0,0,92,10,32,32,32,104,101,97,100,101,114,32,0,0,0,0,101,36,120,112,108,105,99,105,116,0,0,0,0,0,0,0,116,101,115,116,0,0,0,0,9,116,109,97,114,103,105,110,32,105,115,32,115,101,116,32,116,111,32,115,99,114,101,101,110,32,37,103,10,0,0,0,108,111,103,36,102,105,108,101,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0,0,0,0,0,0,0,99,97,36,108,108,0,0,0,32,37,46,52,103,32,37,46,52,103,32,37,46,52,103,32,37,46,52,103,0,0,0,0,72,69,76,80,70,73,76,69,32,32,32,32,32,32,32,32,32,32,32,61,32,34,37,115,34,10,0,0,0,0,0,0,115,99,104,97,114,0,0,0,110,111,104,101,97,100,101,114,32,92,10,32,32,32,0,0,110,111,105,36,109,112,108,105,99,105,116,0,0,0,0,0,9,114,109,97,114,103,105,110,32,105,115,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,102,111,114,109,97,116,32,115,116,114,105,110,103,0,100,101,102,105,110,101,100,32,40,0,0,0,0,0,0,0])
+.concat([79,117,116,32,111,102,32,109,101,109,111,114,121,32,105,110,32,115,116,97,116,115,58,32,116,111,111,32,109,97,110,121,32,100,97,116,97,112,111,105,110,116,115,32,40,37,100,41,63,0,0,0,0,0,0,0,32,114,103,98,32,34,37,115,34,32,0,0,0,0,0,0,65,108,108,32,112,111,105,110,116,115,32,120,32,118,97,108,117,101,32,117,110,100,101,102,105,110,101,100,0,0,0,0,34,119,105,116,104,34,32,97,108,108,111,119,101,100,32,111,110,108,121,32,97,102,116,101,114,32,112,97,114,97,109,101,116,114,105,99,32,102,117,110,99,116,105,111,110,32,102,117,108,108,121,32,115,112,101,99,105,102,105,101,100,0,0,0,97,108,108,36,119,105,110,100,111,119,115,0,0,0,0,0,62,61,0,0,0,0,0,0,110,111,116,105,109,101,115,116,97,109,112,0,0,0,0,0,110,111,101,36,120,112,108,105,99,105,116,0,0,0,0,0,75,80,95,70,50,0,0,0,9,114,109,97,114,103,105,110,32,105,115,32,115,101,116,32,116,111,32,37,103,10,0,0,99,108,111,115,101,100,0,0,70,108,111,97,116,86,97,108,117,101,0,0,0,0,0,0,70,105,114,115,116,32,112,97,114,97,109,101,116,101,114,32,116,111,32,115,116,114,102,116,105,109,101,32,109,117,115,116,32,98,101,32,97,32,102,111,114,109,97,116,32,115,116,114,105,110,103,0,0,0,0,0,114,103,98,102,111,114,109,117,108,97,101,32,37,100,44,32,37,100,44,32,37,100,10,0,114,97,36,105,115,101,0,0,100,111,109,97,105,110,0,0,116,105,109,101,115,116,97,109,112,0,0,0,0,0,0,0,105,36,109,112,108,105,99,105,116,0,0,0,0,0,0,0,9,114,109,97,114,103,105,110,32,105,115,32,115,101,116,32,116,111,32,115,99,114,101,101,110,32,37,103,10,0,0,0,78,97,78,0,0,0,0,0,10,115,101,116,32,112,97,108,101,116,116,101,32,0,0,0,119,104,105,99,104,61,61,65,85,84,79,83,67,65,76,69,95,77,73,78,32,124,124,32,119,104,105,99,104,61,61,65,85,84,79,83,67,65,76,69,95,77,65,88,0,0,0,0,105,110,112,117,116,0,0,0,116,114,36,97,110,115,112,97,114,101,110,116,0,0,0,0,9,98,109,97,114,103,105,110,32,105,115,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,0,0,0,108,97,121,101,114,100,36,101,102,97,117,108,116,0,0,0,87,97,114,110,105,110,103,32,45,32,100,105,102,102,105,99,117,108,116,121,32,102,105,116,116,105,110,103,32,112,108,111,116,32,116,105,116,108,101,115,32,105,110,116,111,32,107,101,121,0,0,0,0,0,0,0,115,97,118,101,46,99,0,0,115,116,97,110,100,97,108,111,110,101,0,0,0,0,0,0,110,111,115,111,36,108,105,100,0,0,0,0,0,0,0,0,9,98,109,97,114,103,105,110,32,105,115,32,115,101,116,32,116,111,32,37,103,10,0,0,110,111,112,111,36,108,97,114,0,0,0,0,0,0,0,0,37,115,58,37,100,32,111,111,111,112,115,58,32,85,110,107,110,111,119,110,32,99,111,108,111,114,32,109,111,100,101,108,32,39,37,99,39,46,10,0,124,99,111,115,40,49,56,48,120,41,124,0,0,0,0,0,115,105,122,101,32,37,103,37,115,44,37,103,37,115,32,37,115,32,37,115,32,37,115,0,110,111,116,114,36,97,110,115,112,97,114,101,110,116,0,0,9,98,109,97,114,103,105,110,32,105,115,32,115,101,116,32,116,111,32,115,99,114,101,101,110,32,37,103,10,0,0,0,112,111,36,108,97,114,0,0,88,89,90,32,0,0,0,0,107,101,121,119,111,114,100,32,39,100,97,116,97,39,32,100,101,112,114,101,99,97,116,101,100,44,32,117,115,101,32,39,115,104,111,119,32,115,116,121,108,101,32,100,97,116,97,39,0,0,0,0,0,0,0,0,101,120,116,114,97,110,101,111,117,115,32,97,114,103,117,109,101,110,116,32,105,110,32,115,101,116,32,116,101,114,109,105,110,97,108,32,37,115,0,0,9,108,109,97,114,103,105,110,32,105,115,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,0,0,0,110,111,114,36,116,105,99,115,0,0,0,0,0,0,0,0,89,73,81,32,0,0,0,0,122,10,0,0,0,0,0,0,71,110,117,112,108,111,116,32,119,97,115,32,98,117,105,108,116,32,119,105,116,104,111,117,116,32,115,117,112,112,111,114,116,32,102,111,114,32,80,78,71,32,105,109,97,103,101,115,46,32,89,111,117,32,99,97,110,110,111,116,32,117,115,101,32,116,104,105,115,32,111,112,116,105,111,110,32,117,110,108,101,115,115,32,121,111,117,32,114,101,98,117,105,108,100,32,103,110,117,112,108,111,116,46,0,0,0,0,0,0,0,0,110,111,104,105,36,100,100,101,110,51,100,0,0,0,0,0,9,108,109,97,114,103,105,110,32,105,115,32,115,101,116,32,116,111,32,37,103,10,0,0,110,111,109,99,98,36,116,105,99,115,0,0,0,0,0,0,67,77,89,32,0,0,0,0,47,117,115,114,47,108,105,98,47,88,49,49,47,102,111,110,116,115,33,0,0,0,0,0,37,52,111,37,110,0,0,0,97,114,114,111,119,32,110,111,116,32,102,111,117,110,100,0,83,116,114,105,110,103,32,99,111,110,116,97,105,110,105,110,103,32,104,101,97,100,101,114,32,105,110,102,111,114,109,97,116,105,111,110,32,101,120,112,101,99,116,101,100,0,0,0,104,105,36,100,100,101,110,51,100,0,0,0,0,0,0,0,9,108,109,97,114,103,105,110,32,105,115,32,115,101,116,32,116,111,32,115,99,114,101,101,110,32,37,103,10,0,0,0,110,111,99,98,36,116,105,99,115,0,0,0,0,0,0,0,72,83,86,32,0,0,0,0,100,111,108,108,97,114,115,0,119,98,0,0,0,0,0,0,101,110,108,0,0,0,0,0,10,92,115,116,97,114,116,116,101,120,116,10,10,0,0,0,109,97,112,0,0,0,0,0,115,121,36,115,116,101,109,0,9,111,117,116,112,117,116,32,105,115,32,115,101,110,116,32,116,111,32,83,84,68,79,85,84,10,0,0,0,0,0,0,110,111,109,121,50,36,116,105,99,115,0,0,0,0,0,0,115,117,109,120,121,0,0,0,82,71,66,32,0,0,0,0,99,104,97,114,0,0,0,0,101,120,116,101,114,110,97,108,0,0,0,0,0,0,0,0,105,110,118,97,108,105,100,32,99,104,97,114,97,99,116,101,114,32,37,99,0,0,0,0,99,108,105,112,52,36,105,110,0,0,0,0,0,0,0,0,9,111,117,116,112,117,116,32,105,115,32,115,101,110,116,32,116,111,32,39,37,115,39,10,0,0,0,0,0,0,0,0,110,111,109,120,50,36,116,105,99,115,0,0,0,0,0,0,99,111,108,111,114,32,109,111,100,101,108,32,0,0,0,0,32,114,103,98,32,118,97,114,105,97,98,108,101,32,0,0,110,111,32,102,117,110,99,116,105,111,110,115,32,111,114,32,100,97,116,97,32,116,111,32,112,108,111,116,0,0,0,0,42,0,0,0,0,0,0,0,105,110,108,105,110,101,0,0,99,108,105,112,49,36,105,110,0,0,0,0,0,0,0,0,75,80,95,70,49,0,0,0,9,112,114,105,110,116,32,111,117,116,112,117,116,32,105,115,32,115,101,110,116,32,116,111,32,39,37,115,39,10,0,0,110,111,109,122,36,116,105,99,115,0,0,0,0,0,0,0,98,101,108,111,119,32,0,0,83,105,103,110,101,100,76,111,110,103,0,0,0,0,0,0,102,95,103,112,114,105,110,116,102,0,0,0,0,0,0,0,103,114,97,121,10,0,0,0,32,32,32,32,105,109,97,103,101,115,61,37,115,93,32,37,37,32,42,105,110,108,105,110,101,42,32,124,32,101,120,116,101,114,110,97,108,32,40,105,110,108,105,110,101,32,111,110,108,121,32,119,111,114,107,115,32,105,110,32,77,75,73,86,44,32,101,120,116,101,114,110,97,108,32,114,101,113,117,105,114,101,115,32,112,110,103,32,115,117,112,112,111,114,116,32,105,110,32,103,110,117,112,108,111,116,41,10,0,0,0,0,110,111,102,116,114,36,105,97,110,103,108,101,115,0,0,0,9,100,101,102,97,117,108,116,32,115,121,115,116,101,109,32,100,105,114,101,99,116,111,114,121,32,34,37,115,34,10,0,110,111,109,121,36,116,105,99,115,0,0,0,0,0,0,0,103,97,109,109,97,32,37,103,32,0,0,0,0,0,0,0,99,111,110,116,111,117,114,32,99,111,111,114,100,115,0,0,73,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,111,117,116,32,111,102,32,109,101,109,111,114,121,32,105,110,32,115,116,97,116,115,0,0,116,101,120,0,0,0,0,0,102,116,114,36,105,97,110,103,108,101,115,0,0,0,0,0,110,111,109,120,36,116,105,99,115,0,0,0,0,0,0,0,115,101,116,32,116,101,114,109,105,110,97,108,32,37,115,32,37,115,10,0,0,0,0,0,98,111,117,110,100,97,114,121,0,0,0,0,0,0,0,0,35,32,67,111,110,116,111,117,114,58,32,112,111,105,110,116,115,32,37,105,44,32,122,32,37,103,44,32,108,97,98,101,108,58,32,37,115,10,0,0,109,101,116,97,112,111,115,116,0,0,0,0,0,0,0,0,102,108,36,117,115,104,0,0,9,101,110,118,105,114,111,110,109,101,110,116,32,118,97,114,105,97,98,108,101,32,71,78,85,80,76,79,84,95,80,83,95,68,73,82,58,32,0,0,110,111,121,50,36,116,105,99,115,0,0,0,0,0,0,0,117,115,101,32,39,115,101,116,32,116,101,114,109,39,32,116,111,32,115,101,116,32,116,101,114,109,105,110,97,108,32,116,121,112,101,32,102,105,114,115,116,0,0,0,0,0,0,0,49,56,48,32,109,117,108,32,99,111,115,32,97,98,115,0,32,32,32,32,112,111,105,110,116,115,61,37,115,44,32,37,37,32,42,109,101,116,97,112,111,115,116,42,32,124,32,116,101,120,32,40,83,104,111,117,108,100,32,112,111,105,110,116,115,32,98,101,32,100,114,97,119,110,32,119,105,116,104,32,77,101,116,97,80,111,115,116,32,111,114,32,84,101,88,63,41,10,0,0,0,0,0,0,100,101,112,36,116,104,111,114,100,101,114,0,0,0,0,0,110,111,110,101,0,0,0,0,110,111,120,50,36,116,105,99,115,0,0,0,0,0,0,0,110,101,103,97,116,105,118,101,0,0,0,0,0,0,0,0,45,104,0,0,0,0,0,0,32,32,32,32,37,37,102,111,110,116,115,99,97,108,101,61,37,103,44,32,37,37,32,115,99,97,108,105,110,103,32,102,97,99,116,111,114,32,102,111,114,32,116,101,120,116,32,108,97,98,101,108,115,10,0,0,115,99,97,110,115,97,117,116,111,36,109,97,116,105,99,0,110,111,122,36,116,105,99,115,0,0,0,0,0,0,0,0,112,111,115,105,116,105,118,101,0,0,0,0,0,0,0,0,37,115,58,0,0,0,0,0,101,120,112,114,101,115,115,105,111,110,32,101,120,112,101,99,116,101,100,0,0,0,0,0,32,32,32,32,108,105,110,101,119,105,100,116,104,61,37,103,44,32,37,37,32,115,99,97,108,105,110,103,32,102,97,99,116,111,114,32,102,111,114,32,108,105,110,101,32,119,105,100,116,104,115,32,40,49,46,48,32,109,101,97,110,115,32,48,46,53,98,112,41,10,0,0,115,99,97,110,115,98,97,99,107,36,119,97,114,100,0,0,9,100,105,114,101,99,116,111,114,121,32,102,114,111,109,32,39,115,101,116,32,112,115,100,105,114,39,58,32,0,0,0,110,111,121,36,116,105,99,115,0,0,0,0,0,0,0,0,115,101,116,32,112,97,108,101,116,116,101,32,37,115,32,37,115,32,109,97,120,99,111,108,111,114,115,32,37,100,32,0,47,117,115,114,47,88,49,49,82,54,47,108,105,98,47,88,49,49,47,102,111,110,116,115,47,116,114,117,101,116,121,112,101,0,0,0,0,0,0,0,119,97,114,110,105,110,103,58,32,0,0,0,0,0,0,0,101,120,116,114,97,110,101,111,117,115,32,97,114,103,117,109,101,110,116,115,32,116,111,32,117,110,115,101,116,32,97,114,114,111,119,0,0,0,0,0,32,32,32,32,100,97,115,104,108,101,110,103,116,104,61,37,103,44,32,37,37,32,115,99,97,108,105,110,103,32,102,97,99,116,111,114,32,102,111,114,32,100,97,115,104,32,108,101,110,103,116,104,115,10,0,0,115,99,97,110,115,102,111,114,36,119,97,114,100,0,0,0,9,112,97,114,97,109,101,116,114,105,99,32,105,115,32,37,115,10,0,0,0,0,0,0,110,111,120,36,116,105,99,115,0,0,0,0,0,0,0,0,99,37,105,0,0,0,0,0,98,111,111,108,0,0,0,0,99,97,110,110,111,116,32,99,114,101,97,116,101,32,112,105,112,101,59,32,111,117,116,112,117,116,32,110,111,116,32,99,104,97,110,103,101,100,0,0,10,35,32,67,111,110,116,111,117,114,32,37,100,44,32,108,97,98,101,108,58,32,37,115,10,0,0,0,0,0,0,0,110,111,0,0,0,0,0,0,105,110,116,101,114,112,36,111,108,97,116,101,0,0,0,0,115,116,36,97,116,115,0,0,35,37,48,50,120,37,48,50,120,37,48,50,120,32,61,32,37,51,105,32,37,51,105,32,37,51,105,0,0,0,0,0,105,103,110,111,114,105,110,103,32,108,101,102,116,47,99,101,110,116,101,114,47,114,105,103,104,116,59,32,105,110,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,108,109,97,114,103,105,110,47,116,109,97,114,103,105,110,46,0,0,99,111,114,114,101,108,97,116,105,111,110,0,0,0,0,0,114,101,112,108,111,116,0,0,101,120,116,101,110,100,32,116,111,107,101,110,32,116,97,98,108,101,0,0,0,0,0,0,71,78,85,80,76,79,84,95,80,83,95,68,73,82,32,32,32,32,32,61,32,34,37,115,34,10,0,0,0,0,0,0,122,114,116,0,0,0,0,0,121,101,115,0,0,0,0,0,88,89,90,0,0,0,0,0,10,32,32,37,45,49,56,115,32,0,0,0,0,0,0,0,111,102,36,102,115,101,116,0,105,103,110,111,114,105,110,103,32,116,111,112,47,99,101,110,116,101,114,47,98,111,116,116,111,109,59,32,105,110,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,116,109,97,114,103,105,110,47,98,109,97,114,103,105,110,46,0,0,32,112,97,108,101,116,116,101,32,102,114,97,99,116,105,111,110,32,37,52,46,50,102,0,105,103,110,111,114,105,110,103,32,116,114,97,105,108,105,110,103,32,99,111,109,109,97,32,105,110,32,112,108,111,116,32,99,111,109,109,97,110,100,0,46,103,110,117,112,108,111,116,0,0,0,0,0,0,0,0,60,0,0,0,0,0,0,0,117,115,97,103,101,58,32,114,97,105,115,101,32,123,112,108,111,116,95,105,100,125,0,0,119,32,61,32,48,32,105,110,32,71,105,118,101,110,115,40,41,59,32,32,67,106,106,32,61,32,37,103,44,32,32,67,105,106,32,61,32,37,103,0,67,97,110,39,116,32,99,97,108,99,117,108,97,116,101,32,97,112,112,114,111,120,105,109,97,116,105,111,110,32,115,112,108,105,110,101,115,44,32,110,101,101,100,32,97,116,32,108,101,97,115,116,32,52,32,112,111,105,110,116,115,0,0,0,32,32,32,32,100,97,115,104,101,100,61,37,115,44,32,37,37,32,42,121,101,115,42,32,124,32,110,111,10,0,0,0,89,73,81,0,0,0,0,0,75,80,95,84,97,98,0,0,102,105,108,108,101,100,95,112,111,108,121,103,111,110,95,122,102,105,120,32,99,111,114,110,101,114,115,0,0,0,0,0,9,84,104,101,114,101,32,97,114,101,32,37,100,32,112,114,101,100,101,102,105,110,101,100,32,99,111,108,111,114,32,110,97,109,101,115,58,0,0,0,117,110,107,110,111,119,110,32,107,101,121,32,111,112,116,105,111,110,0,0,0,0,0,0,97,98,111,118,101,32,0,0,85,110,115,105,103,110,101,100,76,111,110,103,0,0,0,0,70,105,114,115,116,32,112,97,114,97,109,101,116,101,114,32,116,111,32,103,112,114,105,110,116,102,32,109,117,115,116,32,98,101,32,97,32,102,111,114,109,97,116,32,115,116,114,105,110,103,0,0,0,0,0,0,99,97,110,32,111,110,108,121,32,100,111,32,101,108,108,105,112,116,105,99,32,105,110,116,101,103,114,97,108,115,32,111,102,32,114,101,97,108,115,0,44,32,37,37,32,42,98,117,116,116,42,32,124,32,114,111,117,110,100,101,100,32,124,32,115,113,117,97,114,101,100,10,0,0,0,0,0,0,0,0,67,77,89,0,0,0,0,0,37,105,9,37,105,9,37,105,10,0,0,0,0,0,0,0,97,36,117,116,111,109,97,116,105,99,0,0,0,0,0,0,114,101,99,117,114,115,105,111,110,32,100,101,112,116,104,32,108,105,109,105,116,32,101,120,99,101,101,100,101,100,0,0,116,105,99,107,32,105,110,116,101,114,118,97,108,32,116,111,111,32,115,109,97,108,108,32,102,111,114,32,109,97,99,104,105,110,101,32,112,114,101,99,105,115,105,111,110,0,0,0,115,113,117,97,114,101,100,0,72,83,86,0,0,0,0,0,37,48,46,52,102,9,37,48,46,52,102,9,37,48,46,52,102,10,0,0,0,0,0,0,99,111,108,36,117,109,110,104,101,97,100,101,114,0,0,0,95,94,0,0,0,0,0,0,98,117,116,116,0,0,0,0,82,71,66,0,0,0,0,0,46,10,0,0,0,0,0,0,77,117,108,116,105,112,108,101,32,115,116,97,99,107,32,100,105,114,101,99,116,105,111,110,32,115,101,116,116,105,110,103,115,0,0,0,0,0,0,0,32,99,111,114,110,101,114,115,50,99,111,108,111,114,32,0,115,105,110,40,49,56,48,120,41,0,0,0,0,0,0,0,32,32,32,32,108,105,110,101,99,97,112,61,0,0,0,0,99,117,98,101,104,101,108,105,120,0,0,0,0,0,0,0,32,115,97,118,101,100,32,116,111,32,34,37,115,34,46,0,77,117,108,116,105,112,108,101,32,108,111,99,97,116,105,111,110,32,114,101,103,105,111,110,32,115,101,116,116,105,110,103,115,0,0,0,0,0,0,0,32,110,111,104,105,100,100,101,110,51,100,0,0,0,0,0,105,110,118,101,114,115,101,95,101,114,114,111,114,95,102,117,110,99,58,32,84,104,101,32,118,97,108,117,101,32,111,117,116,32,111,102,32,116,104,101,32,114,97,110,103,101,32,111,102,32,116,104,101,32,102,117,110,99,116,105,111,110,0,0,44,32,37,37,32,42,109,105,116,101,114,101,100,42,32,124,32,114,111,117,110,100,101,100,32,124,32,98,101,118,101,108,101,100,10,0,0,0,0,0,103,97,109,36,109,97,0,0,67,111,108,111,114,0,0,0,77,117,108,116,105,112,108,101,32,104,111,114,105,122,111,110,116,97,108,32,112,111,115,105,116,105,111,110,32,115,101,116,116,105,110,103,115,0,0,0,32,104,105,100,100,101,110,51,100,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,102,111,114,109,97,116,32,61,32,0,0,0,0,0,0,0,98,101,118,101,108,101,100,0,109,97,120,99,36,111,108,111,114,115,0,0,0,0,0,0,71,114,97,121,0,0,0,0,77,117,108,116,105,112,108,101,32,118,101,114,116,105,99,97,108,32,112,111,115,105,116,105,111,110,32,115,101,116,116,105,110,103,115,0,0,0,0,0,32,104,105,100,100,101,110,51,100,32,37,100,0,0,0,0,47,117,115,114,47,88,49,49,82,54,47,108,105,98,47,88,49,49,47,102,111,110,116,115,47,84,121,112,101,49,0,0,71,80,86,65,76,95,69,82,82,77,83,71,0,0,0,0,116,121,120,0,0,0,0,0,114,111,117,110,100,101,100,0,112,115,95,97,108,108,99,70,0,0,0,0,0,0,0,0,37,115,32,112,97,108,101,116,116,101,32,119,105,116,104,32,37,105,32,100,105,115,99,114,101,116,101,32,99,111,108,111,114,115,0,0,0,0,0,0,102,105,108,108,95,110,117,109,98,101,114,115,32,99,108,111,115,105,110,103,0,0,0,0,102,116,114,105,97,110,103,108,101,115,0,0,0,0,0,0,102,97,99,116,111,114,105,97,108,0,0,0,0,0,0,0,119,0,0,0,0,0,0,0,37,99,10,0,0,0,0,0,109,105,116,101,114,101,100,0,110,111,112,115,95,97,108,108,99,70,0,0,0,0,0,0,115,112,36,108,111,116,0,0,101,120,112,101,99,116,105,110,103,32,110,111,32,111,112,116,105,111,110,32,111,114,32,105,110,116,32,111,114,32,102,108,111,97,116,0,0,0,0,0,115,116,114,105,110,103,32,101,120,112,101,99,116,101,100,0,105,110,116,101,114,99,101,112,116,0,0,0,0,0,0,0,32,110,111,0,0,0,0,0,47,117,115,114,47,108,111,99,97,108,47,115,104,97,114,101,47,103,110,117,112,108,111,116,47,52,46,54,47,103,110,117,112,108,111,116,46,103,105,104,0,0,0,0,0,0,0,0,116,122,114,0,0,0,0,0,108,105,110,101,106,111,105,110,61,0,0,0,0,0,0,0,109,111,36,100,101,108,0,0,105,36,110,116,0,0,0,0,110,111,112,111,36,105,110,116,0,0,0,0,0,0,0,0,102,105,108,108,95,110,117,109,98,101,114,115,32,114,101,115,105,122,101,32,112,97,116,116,101,114,110,0,0,0,0,0,67,111,110,102,108,105,99,116,32,98,101,116,119,101,101,110,32,115,111,109,101,32,109,97,116,114,105,120,32,98,105,110,97,114,121,32,97,110,100,32,103,101,110,101,114,97,108,32,98,105,110,97,114,121,32,107,101,121,119,111,114,100,115,0,32,112,97,108,101,116,116,101,32,99,98,32,37,103,0,0,115,97,109,112,108,101,115,32,111,114,32,105,115,111,95,115,97,109,112,108,101,115,32,60,32,50,46,32,77,117,115,116,32,98,101,32,97,116,32,108,101,97,115,116,32,50,46,0,103,110,117,112,108,111,116,114,99,0,0,0,0,0,0,0,62,0,0,0,0,0,0,0,117,115,97,103,101,58,32,108,111,119,101,114,32,123,112,108,111,116,95,105,100,125,0,0,92,115,101,116,117,112,71,78,85,80,76,79,84,116,101,114,109,105,110,97,108,10,32,32,32,91,99,111,110,116,101,120,116,93,10,32,32,32,91,0,102,117,110,99,36,116,105,111,110,115,0,0,0,0,0,0,75,80,95,83,112,97,99,101,0,0,0,0,0,0,0,0,102,36,108,111,97,116,0,0,102,105,108,108,95,110,117,109,98,101,114,115,32,110,101,120,116,32,110,117,109,98,101,114,0,0,0,0,0,0,0,0,115,121,110,116,97,120,32,105,115,32,120,121,61,60,120,62,44,60,121,62,0,0,0,0,83,105,103,110,101,100,73,110,116,101,103,101,114,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,105,110,118,97,108,105,100,32,115,112,101,99,95,116,121,112,101,0,0,0,0,0,0,0,76,97,115,116,32,108,105,110,101,32,109,117,115,116,32,110,111,116,32,98,101,32,98,101,102,111,114,101,32,102,105,114,115,116,32,108,105,110,101,0,92,115,101,116,117,112,98,111,100,121,102,111,110,116,10,32,32,32,91,37,115,37,115,37,103,112,116,93,10,0,0,0,102,105,108,101,0,0,0,0,112,97,108,101,116,116,101,32,115,105,122,101,32,114,101,113,117,105,114,101,100,0,0,0,99,111,110,115,116,97,110,116,32,101,120,112,114,101,115,115,105,111,110,32,101,120,112,101,99,116,101,100,0,0,0,0,98,101,103,105,110,0,0,0,76,97,115,116,32,112,111,105,110,116,32,109,117,115,116,32,110,111,116,32,98,101,32,98,101,102,111,114,101,32,102,105,114,115,116,32,112,111,105,110,116,0,0,0,0,0,0,0,84,111,111,32,109,97,110,121,32,97,120,105,115,32,116,105,99,107,115,32,114,101,113,117,101,115,116,101,100,32,40,62,37,46,48,103,41,0,0,0,102,117,110,99,116,105,111,110,32,37,115,32,114,101,113,117,105,114,101,115,32,37,100,32,118,97,114,105,97,98,108,101,115,0,0,0,0,0,0,0,92,100,101,102,105,110,101,98,111,100,121,102,111,110,116,101,110,118,105,114,111,110,109,101,110,116,10,32,32,32,91,37,103,112,116,93,10,0,0,0,100,101,102,36,105,110,101,100,0,0,0,0,0,0,0,0,37,51,105,46,32,103,114,97,121,61,37,48,46,52,102,44,32,40,114,44,103,44,98,41,61,40,37,48,46,52,102,44,37,48,46,52,102,44,37,48,46,52,102,41,44,32,35,37,48,50,120,37,48,50,120,37,48,50,120,32,61,32,37,51,105,32,37,51,105,32,37,51,105,10,0,0,0,0,0,0,102,105,108,108,95,110,117,109,98,101,114,115,32,111,117,116,112,117,116,32,98,117,102,102,101,114,0,0,0,0,0,0,69,120,112,101,99,116,101,100,32,110,111,110,45,110,101,103,97,116,105,118,101,32,105,110,116,101,103,101,114,0,0,0,114,103,98,36,102,111,114,109,117,108,97,101,0,0,0,0,9,99,111,108,111,114,32,109,97,112,112,105,110,103,32,42,110,111,116,42,32,100,111,110,101,32,98,121,32,100,101,102,105,110,101,100,32,103,114,97,100,105,101,110,116,46,10,0,112,111,105,110,116,116,36,121,112,101,0,0,0,0,0,0,32,102,108,117,115,104,32,0,69,120,112,101,99,116,101,100,32,112,111,115,105,116,105,118,101,32,105,110,116,101,103,101,114,0,0,0,0,0,0,0,49,56,48,32,109,117,108,32,115,105,110,0,0,0,0,0,92,115,101,116,117,112,99,111,108,111,114,115,10,32,32,32,91,115,116,97,116,101,61,115,116,97,114,116,93,10,0,0,9,32,32,42,32,116,104,117,115,32,116,104,101,32,114,97,110,103,101,115,32,105,110,32,96,115,101,116,32,112,109,51,100,32,114,103,98,102,111,114,109,117,108,97,101,39,32,97,114,101,32,45,37,105,46,46,37,105,10,0,0,0,0,0,108,105,110,101,116,36,121,112,101,0,0,0,0,0,0,0,115,101,116,32,112,109,51,100,32,105,110,116,101,114,112,111,108,97,116,101,32,37,100,44,37,100,0,0,0,0,0,0,73,110,100,101,120,32,115,116,101,112,32,109,117,115,116,32,98,101,32,112,111,115,105,116,105,118,101,0,0,0,0,0,92,117,115,101,109,111,100,117,108,101,10,32,32,32,91,103,110,117,112,108,111,116,93,10,0,0,0,0,0,0,0,0,78,111,32,115,117,99,104,32,111,112,116,105,111,110,32,116,111,32,104,105,100,100,101,110,51,100,32,40,111,114,32,119,114,111,110,103,32,111,114,100,101,114,41,0,0,0,0,0,103,114,101,121,0,0,0,0,9,32,32,42,32,110,101,103,97,116,105,118,101,32,110,117,109,98,101,114,115,32,109,101,97,110,32,105,110,118,101,114,116,101,100,61,110,101,103,97,116,105,118,101,32,99,111,108,111,117,114,32,99,111,109,112,111,110,101,110,116,10,0,0,100,101,112,116,104,111,114,100,101,114,10,0,0,0,0,0,85,112,112,101,114,32,105,110,100,101,120,32,115,104,111,117,108,100,32,98,101,32,98,105,103,103,101,114,32,116,104,97,110,32,108,111,119,101,114,32,105,110,100,101,120,0,0,0,70,73,84,58,32,32,32,32,100,97,116,97,32,114,101,97,100,32,102,114,111,109,32,37,115,10,0,0,0,0,0,0,92,101,110,97,98,108,101,114,101,103,105,109,101,10,32,32,32,91,117,116,102,45,56,93,10,0,0,0,0,0,0,0,37,50,105,58,32,37,45,49,53,115,0,0,0,0,0,0,115,99,97,110,115,98,97,99,107,119,97,114,100,10,0,0,66,105,110,97,114,121,32,109,97,116,114,105,120,32,102,105,108,101,32,102,111,114,109,97,116,32,100,111,101,115,32,110,111,116,32,97,108,108,111,119,32,109,111,114,101,32,116,104,97,110,32,111,110,101,32,115,117,114,102,97,99,101,32,112,101,114,32,102,105,108,101,0,67,0,0,0,0,0,0,0,10,10,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,112,111,105,110,116,115,39,44,32,39,111,110,101,39,44,32,111,114,32,39,116,119,111,39,0,0,0,0,0,76,73,78,69,83,0,0,0,37,37,32,83,101,101,32,97,108,115,111,32,104,116,116,112,58,47,47,119,105,107,105,46,99,111,110,116,101,120,116,103,97,114,100,101,110,46,110,101,116,47,71,110,117,112,108,111,116,10,37,37,10,0,0,0,110,101,103,36,97,116,105,118,101,0,0,0,0,0,0,0,10,9,32,32,32,32,0,0,99,101,110,116,114,101,0,0,115,99,97,110,115,102,111,114,119,97,114,100,10,0,0,0,112,111,119,101,114,0,0,0,73,110,32,109,117,108,116,105,112,108,111,116,32,109,111,100,101,32,121,111,117,32,99,97,110,39,116,32,99,104,97,110,103,101,32,116,104,101,32,111,117,116,112,117,116,10,0,0,32,100,101,108,116,97,95,120,32,100,101,108,116,97,95,121,32,100,101,108,116,97,95,122,0,0,0,0,0,0,0,0,37,37,32,71,78,85,80,76,79,84,32,118,101,114,115,105,111,110,58,32,37,115,46,37,115,44,32,116,101,114,109,105,110,97,108,32,118,101,114,115,105,111,110,58,32,37,115,46,37,115,32,40,37,115,41,10,0,0,0,0,0,0,0,0,112,111,115,36,105,116,105,118,101,0,0,0,0,0,0,0,115,104,36,111,119,0,0,0,9,32,32,42,32,116,104,101,114,101,32,97,114,101,32,37,105,32,97,118,97,105,108,97,98,108,101,32,114,103,98,32,99,111,108,111,114,32,109,97,112,112,105,110,103,32,102,111,114,109,117,108,97,101,58,0,115,108,111,112,101,0,0,0,115,99,97,110,115,97,117,116,111,109,97,116,105,99,10,0,102,70,101,69,103,71,0,0,71,78,85,72,69,76,80,0,10,0,0,0,0,0,0,0,114,116,122,0,0,0,0,0,98,97,36,99,107,0,0,0,9,84,104,101,32,98,101,115,116,32,109,97,116,99,104,32,111,102,32,116,104,101,32,99,117,114,114,101,110,116,32,112,97,108,101,116,116,101,32,99,111,114,114,101,115,112,111,110,100,115,32,116,111,10,9,32,32,32,32,115,101,116,32,112,97,108,101,116,116,101,32,114,103,98,102,111,114,109,117,108,97,101,32,37,105,44,37,105,44,37,105,10,0,0,0,0,112,111,36,105,110,116,0,0,32,97,116,32,37,115,10,0,43,45,35,48,49,50,51,52,53,54,55,56,57,46,0,0,32,112,97,108,101,116,116,101,32,122,0,0,0,0,0,0,121,32,114,97,110,103,101,0,110,101,0,0,0,0,0,0,32,111,110,58,32,37,115,0,102,114,36,111,110,116,0,0,66,101,103,105,110,0,0,0,102,111,114,109,117,108,97,101,32,112,116,115,0,0,0,0,116,109,112,32,108,111,97,100,112,97,116,104,0,0,0,0,101,120,112,108,105,99,105,116,0,0,0,0,0,0,0,0,102,95,115,112,114,105,110,116,102,58,32,97,116,116,101,109,112,116,32,116,111,32,112,114,105,110,116,32,115,116,114,105,110,103,32,118,97,108,117,101,32,119,105,116,104,32,110,117,109,101,114,105,99,32,102,111,114,109,97,116,0,0,0,0,85,110,115,105,103,110,101,100,73,110,116,101,103,101,114,0,116,105,99,108,97,98,101,108,115,32,109,117,115,116,32,99,111,109,101,32,102,114,111,109,32,97,32,114,101,97,108,32,99,111,108,117,109,110,0,0,37,89,45,37,109,45,37,100,32,37,72,58,37,77,32,37,90,0,0,0,0,0,0,0,102,111,114,109,117,108,97,101,0,0,0,0,0,0,0,0,116,109,112,32,102,111,110,116,112,97,116,104,0,0,0,0,105,109,112,108,105,99,105,116,0,0,0,0,0,0,0,0,102,111,114,109,97,116,32,109,117,115,116,32,104,97,118,101,32,49,45,55,32,99,111,110,118,101,114,115,105,111,110,115,32,111,102,32,116,121,112,101,32,100,111,117,98,108,101,32,40,37,37,108,102,41,0,0,73,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,117,110,107,110,111,119,110,32,116,105,99,32,116,121,112,101,0,0,0,0,0,0,0,37,37,32,87,114,105,116,116,101,110,32,98,121,32,67,111,110,84,101,88,116,32,116,101,114,109,105,110,97,108,32,102,111,114,32,71,78,85,80,76,79,84,0,0,0,0,0,0,111,36,114,105,103,105,110,0,102,111,114,109,117,108,97,101,83,101,113,0,0,0,0,0,101,120,112,101,99,116,101,100,32,115,116,114,105,110,103,0,115,101,116,32,112,109,51,100,32,0,0,0,0,0,0,0,99,111,114,110,101,114,115,32,102,111,114,32,102,105,108,108,101,100,99,117,114,118,101,115,0,0,0,0,0,0,0,0,67,111,108,117,109,110,32,109,117,115,116,32,98,101,32,62,61,32,45,50,0,0,0,0,92,101,110,100,105,110,112,117,116,10,0,0,0,0,0,0,110,111,98,111,36,114,100,101,114,0,0,0,0,0,0,0,82,71,66,32,112,116,115,0,108,111,103,32,98,97,115,101,32,109,117,115,116,32,98,101,32,62,32,49,46,48,59,32,108,111,103,115,99,97,108,101,32,117,110,99,104,97,110,103,101,100,0,0,0,0,0,0,115,101,116,32,108,111,99,97,108,101,32,34,37,115,34,10,0,0,0,0,0,0,0,0,40,50,120,45,49,41,94,50,0,0,0,0,0,0,0,0,92,115,116,111,112,116,101,120,116,10,0,0,0,0,0,0,98,100,36,101,102,97,117,108,116,0,0,0,0,0,0,0,100,111,112,108,111,116,0,0,9,67,117,114,114,101,110,116,32,112,97,108,101,116,116,101,32,105,115,10,9,32,32,32,32,115,101,116,32,112,97,108,101,116,116,101,32,114,103,98,102,111,114,109,117,108,97,101,32,37,105,44,37,105,44,37,105,10,0,0,0,0,0,0,115,101,116,32,116,109,97,114,103,105,110,32,37,115,32,37,103,10,0,0,0,0,0,0,99,98,116,105,99,36,108,97,98,101,108,115,0,0,0,0,92,115,116,111,112,71,78,85,80,76,79,84,103,114,97,112,104,105,99,10,0,0,0,0,98,111,36,114,100,101,114,0,69,120,112,101,99,116,105,110,103,32,39,103,114,97,100,105,101,110,116,39,32,111,114,32,39,112,97,108,101,116,116,101,32,60,110,62,39,32,111,114,32,39,114,103,98,102,111,114,109,117,108,97,101,39,32,111,114,32,39,99,111,108,111,114,110,97,109,101,115,39,0,0,101,120,112,101,99,116,105,110,103,32,39,99,97,114,116,101,115,105,97,110,39,44,32,39,115,112,104,101,114,105,99,97,108,39,44,32,111,114,32,39,99,121,108,105,110,100,114,105,99,97,108,39,0,0,0,0,115,101,116,32,114,109,97,114,103,105,110,32,37,115,32,37,103,10,0,0,0,0,0,0,122,116,105,99,36,108,97,98,101,108,115,0,0,0,0,0,37,115,10,10,0,0,0,0,92,115,116,111,112,71,78,85,80,76,79,84,112,97,103,101,10,0,0,0,0,0,0,0,117,36,115,101,114,0,0,0,102,105,116,50,114,103,98,36,102,111,114,109,117,108,97,101,0,0,0,0,0,0,0,0,99,121,36,108,105,110,100,114,105,99,97,108,0,0,0,0,115,101,116,32,98,109,97,114,103,105,110,32,37,115,32,37,103,10,0,0,0,0,0,0,121,50,116,105,99,36,108,97,98,101,108,115,0,0,0,0,9,103,110,117,112,108,111,116,32,76,67,95,78,85,77,69,82,73,67,32,37,115,10,0,37,46,49,50,48,115,0,0,116,36,119,111,0,0,0,0,115,101,116,98,111,117,110,100,115,32,99,117,114,114,101,110,116,112,105,99,116,117,114,101,32,116,111,32,117,110,105,116,115,113,117,97,114,101,32,120,121,115,99,97,108,101,100,32,40,119,44,104,41,59,10,0,104,36,111,114,105,122,111,110,116,97,108,0,0,0,0,0,99,111,108,111,114,36,110,97,109,101,115,0,0,0,0,0,115,36,112,104,101,114,105,99,97,108,0,0,0,0,0,0,97,116,32,115,99,114,101,101,110,0,0,0,0,0,0,0,121,95,109,105,110,51,100,32,115,104,111,117,108,100,32,110,111,116,32,101,113,117,97,108,32,121,95,109,97,120,51,100,33,0,0,0,0,0,0,0,121,116,105,99,36,108,97,98,101,108,115,0,0,0,0,0,109,111,100,0,0,0,0,0,100,101,115,116,32,61,61,32,78,85,76,76,32,124,124,32,100,101,115,116,32,33,61,32,111,117,116,115,116,114,0,0,10,35,32,73,115,111,67,117,114,118,101,32,37,100,44,32,37,100,32,112,111,105,110,116,115,10,35,32,120,32,121,32,122,0,0,0,0,0,0,0,118,36,101,114,116,105,99,97,108,0,0,0,0,0,0,0,115,104,101,36,108,108,0,0,99,97,36,114,116,101,115,105,97,110,0,0,0,0,0,0,37,115,9,37,102,10,0,0,115,101,116,32,108,109,97,114,103,105,110,32,37,115,32,37,103,10,0,0,0,0,0,0,120,50,116,105,99,36,108,97,98,101,108,115,0,0,0,0,67,111,109,112,105,108,101,32,111,112,116,105,111,110,115,58,10,37,115,10,0,0,0,0,37,46,52,103,0,0,0,0,114,122,116,0,0,0,0,0,116,105,109,101,0,0,0,0,114,103,98,102,111,114,36,109,117,108,97,101,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,115,99,114,101,101,110,32,60,102,114,97,99,116,105,111,110,62,39,0,0,0,115,101,116,32,122,101,114,111,32,37,103,10,0,0,0,0,120,116,105,99,36,108,97,98,101,108,115,0,0,0,0,0,32,108,105,110,101,115,116,121,108,101,32,37,100,0,0,0,120,32,114,97,110,103,101,0,99,111,108,117,109,110,104,101,97,100,0,0,0,0,0,0,114,99,102,105,108,101,0,0,101,113,0,0,0,0,0,0,41,59,112,111,115,105,116,105,111,110,115,40,0,0,0,0,116,101,114,109,36,105,110,97,108,0,0,0,0,0,0,0,69,110,100,0,0,0,0,0,103,114,97,36,100,105,101,110,116,0,0,0,0,0,0,0,115,99,36,114,101,101,110,0,102,95,115,112,114,105,110,116,102,58,32,97,116,116,101,109,112,116,32,116,111,32,112,114,105,110,116,32,110,117,109,101,114,105,99,32,118,97,108,117,101,32,119,105,116,104,32,115,116,114,105,110,103,32,102,111,114,109,97,116,0,0,0,0,83,105,103,110,101,100,83,104,111,114,116,0,0,0,0,0,40,37,46,51,103,44,37,46,51,103,44,37,46,51,103,41,0,0,0,0,0,0,0,0,110,111,111,112,97,113,117,101,0,0,0,0,0,0,0,0,101,120,116,114,97,32,99,104,97,114,115,32,97,102,116,101,114,32,60,115,101,112,97,114,97,116,105,111,110,95,99,104,97,114,62,0,0,0,0,0,107,101,121,32,101,110,116,114,121,0,0,0,0,0,0,0,10,37,72,58,37,77,0,0,99,111,108,111,114,115,40,0,111,112,97,113,117,101,0,0,9,103,97,109,109,97,32,105,115,32,37,46,52,103,10,0,39,92,116,39,0,0,0,0,104,105,115,116,101,112,115,32,118,97,108,105,100,32,112,111,105,110,116,32,109,97,112,112,105,110,103,0,0,0,0,0,116,101,109,112,32,115,116,114,105,110,103,32,102,111,114,32,108,97,98,101,108,32,104,97,99,107,0,0,0,0,0,0,99,111,108,111,114,95,109,111,100,101,40,103,114,97,100,105,101,110,116,41,59,0,0,0,109,97,120,114,111,119,36,115,0,0,0,0,0,0,0,0,37,115,58,37,100,32,111,111,111,112,115,58,32,85,110,107,110,111,119,110,32,99,111,108,111,114,32,109,111,100,101,32,39,37,99,39,46,10,0,0,34,92,116,34,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,99,111,108,117,109,110,32,60,61,32,48,32,105,110,32,100,97,116,97,102,105,108,101,46,99,0,0,0,0,0,0,0,50,32,109,117,108,32,49,32,115,117,98,32,100,117,112,32,109,117,108,0,0,0,0,0,99,111,108,111,114,95,109,111,100,101,40,102,117,110,99,116,105,111,110,115,41,0,0,0,109,97,120,99,111,108,117,36,109,110,115,0,0,0,0,0,88,89,90,10,0,0,0,0,101,120,112,101,99,116,101,100,32,34,60,115,101,112,97,114,97,116,111,114,95,99,104,97,114,62,34,0,0,0,0,0,115,101,116,32,116,105,109,101,115,116,97,109,112,32,37,115,32,10,0,0,0,0,0,0,37,49,54,46,51,102,0,0,99,111,108,111,114,95,109,111,100,101,40,114,103,98,41,59,102,111,114,109,117,108,97,101,40,37,100,44,37,100,44,37,100,41,0,0,0,0,0,0,109,97,120,99,111,108,36,115,0,0,0,0,0,0,0,0,89,73,81,10,0,0,0,0,119,104,105,116,101,36,115,112,97,99,101,0,0,0,0,0,32,114,111,116,97,116,101,32,112,97,114,97,108,108,101,108,0,0,0,0,0,0,0,0,116,105,109,101,115,116,114,105,110,103,0,0,0,0,0,0,10,10,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,10,0,0,0,0,0,0,99,111,108,111,114,95,109,111,100,101,40,103,114,97,121,41,0,0,0,0,0,0,0,0,116,101,120,116,36,99,111,108,111,114,0,0,0,0,0,0,84,105,99,32,108,97,98,101,108,32,100,111,101,115,32,110,111,116,32,101,118,97,108,117,97,116,101,32,97,115,32,115,116,114,105,110,103,33,10,0,67,77,89,10,0,0,0,0,101,120,112,101,99,116,101,100,32,115,116,114,105,110,103,32,119,105,116,104,32,99,111,109,109,101,110,116,115,32,99,104,97,114,115,0,0,0,0,0,10,115,101,116,32,37,115,37,115,32,0,0,0,0,0,0,9,103,110,117,112,108,111,116,32,76,67,95,84,73,77,69,32,32,32,32,37,115,10,0,117,116,105,108,46,99,0,0,111,36,110,101,0,0,0,0,103,112,95,109,97,107,101,95,112,97,108,101,116,116,101,40,0,0,0,0,0,0,0,0,116,99,0,0,0,0,0,0,72,83,86,10,0,0,0,0,109,97,120,95,108,105,110,101,95,108,101,110,0,0,0,0,100,105,118,0,0,0,0,0,84,97,98,117,108,97,114,32,111,117,116,112,117,116,32,111,102,32,116,104,105,115,32,51,68,32,112,108,111,116,32,115,116,121,108,101,32,110,111,116,32,105,109,112,108,101,109,101,110,116,101,100,10,0,0,0,70,97,116,97,108,58,32,117,110,100,101,102,105,110,101,100,32,99,111,108,111,114,32,102,111,114,109,117,108,97,32,40,99,97,110,32,98,101,32,48,45,45,37,105,41,10,0,0,37,37,32,102,111,114,32,97,100,100,105,116,105,111,110,97,108,32,117,115,101,114,45,100,101,102,105,110,101,100,32,115,101,116,116,105,110,103,115,10,103,112,95,115,101,116,117,112,95,97,102,116,101,114,59,10,0,0,0,0,0,0,0,0,115,101,36,116,0,0,0,0,82,71,66,10,0,0,0,0,101,120,112,101,99,116,101,100,32,109,105,115,115,105,110,103,45,118,97,108,117,101,32,115,116,114,105,110,103,0,0,0,95,121,0,0,0,0,0,0,115,101,116,32,37,115,37,115,32,34,37,115,34,32,0,0,108,36,111,110,103,0,0,0,103,112,95,115,101,116,95,108,105,110,101,119,105,100,116,104,40,37,103,41,59,10,0,0,122,116,114,0,0,0,0,0,110,111,116,105,36,116,108,101])
+.concat([0,0,0,0,0,0,0,0,9,67,111,108,111,114,45,77,111,100,101,108,58,32,0,0,119,114,111,110,103,32,111,112,116,105,111,110,0,0,0,0,115,101,116,32,109,37,115,116,105,99,115,32,37,102,10,0,37,103,32,37,103,0,0,0,32,108,116,32,37,100,0,0,121,32,114,97,110,103,101,32,105,115,32,105,110,118,97,108,105,100,0,0,0,0,0,0,33,61,0,0,0,0,0,0,37,37,32,108,105,110,101,119,105,100,116,104,32,115,99,97,108,105,110,103,32,102,97,99,116,111,114,32,102,111,114,32,105,110,100,105,118,105,100,117,97,108,32,108,105,110,101,115,10,0,0,0,0,0,0,0,80,97,103,101,68,111,119,110,0,0,0,0,0,0,0,0,32,99,111,108,111,114,32,112,111,115,105,116,105,111,110,115,32,102,111,114,32,100,105,115,99,114,101,116,101,32,112,97,108,101,116,116,101,32,116,101,114,109,105,110,97,108,115,10,0,0,0,0,0,0,0,0,114,117,108,101,114,32,97,116,0,0,0,0,0,0,0,0,115,101,116,32,109,37,115,116,105,99,115,32,100,101,102,97,117,108,116,10,0,0,0,0,117,110,114,101,99,111,103,110,105,122,101,100,32,112,108,111,116,32,116,121,112,101,0,0,37,37,0,0,0,0,0,0,85,110,115,105,103,110,101,100,83,104,111,114,116,0,0,0,103,112,95,115,101,116,95,112,111,105,110,116,115,105,122,101,40,37,103,41,59,10,0,0,110,111,97,36,117,116,111,116,105,116,108,101,115,0,0,0,65,76,76,32,114,101,109,97,105,110,105,110,103,0,0,0,101,120,112,101,99,116,105,110,103,32,114,117,108,101,114,32,99,111,111,114,100,105,110,97,116,101,115,0,0,0,0,0,115,101,116,32,109,37,115,116,105,99,115,10,0,0,0,0,37,100,47,37,109,0,0,0,83,105,103,110,101,100,66,121,116,101,0,0,0,0,0,0,112,118,101,114,116,32,62,61,32,48,0,0,0,0,0,0,37,37,32,112,111,105,110,116,115,105,122,101,32,115,99,97,108,105,110,103,32,102,97,99,116,111,114,10,0,0,0,0,97,36,117,116,111,116,105,116,108,101,115,0,0,0,0,0,77,65,88,32,37,105,0,0,114,117,36,108,101,114,0,0,115,101,116,32,110,111,109,37,115,116,105,99,115,10,0,0,103,112,95,115,99,97,108,101,95,116,101,120,116,32,58,61,32,37,103,59,10,0,0,0,104,36,101,105,103,104,116,0,9,97,108,108,111,99,97,116,105,110,103,32,0,0,0,0,110,111,114,117,36,108,101,114,0,0,0,0,0,0,0,0,115,101,116,32,116,105,99,115,108,101,118,101,108,32,37,103,10,0,0,0,0,0,0,0,124,120,45,48,46,53,124,0,37,37,32,116,101,120,116,32,115,99,97,108,105,110,103,32,102,97,99,116,111,114,32,102,111,114,32,116,104,101,32,119,104,111,108,101,32,102,105,103,117,114,101,10,0,0,0,0,119,36,105,100,116,104,0,0,32,78,79,84,0,0,0,0,115,104,111,117,108,100,32,98,101,58,32,37,100,32,60,61,32,109,111,117,115,101,102,111,114,109,97,116,32,60,61,32,37,100,10,0,0,0,0,0,115,101,116,32,120,121,112,108,97,110,101,32,97,116,32,37,103,10,0,0,0,0,0,0,37,37,32,102,111,114,32,97,100,100,105,116,105,111,110,97,108,32,117,115,101,114,45,100,101,102,105,110,101,100,32,115,101,116,116,105,110,103,115,10,103,112,95,115,101,116,117,112,95,98,101,102,111,114,101,59,10,0,0,0,0,0,0,0,115,112,36,97,99,105,110,103,0,0,0,0,0,0,0,0,9,97,108,108,32,99,111,108,111,114,32,102,111,114,109,117,108,97,101,32,65,82,69,37,115,32,119,114,105,116,116,101,110,32,105,110,116,111,32,111,117,116,112,117,116,32,112,111,115,116,115,99,114,105,112,116,32,102,105,108,101,10,0,0,112,108,101,97,115,101,32,39,115,101,116,32,109,111,117,115,101,32,109,111,117,115,101,102,111,114,109,97,116,32,60,102,109,116,62,39,32,102,105,114,115,116,46,10,0,0,0,0,102,117,110,99,116,105,111,110,0,0,0,0,0,0,0,0,44,32,116,97,110,103,101,110,116,61,37,115,41,0,0,0,99,111,117,108,100,32,110,111,116,32,111,112,101,110,32,108,111,103,45,102,105,108,101,32,37,115,0,0,0,0,0,0,37,37,32,68,105,102,102,101,114,101,110,116,32,105,110,105,116,105,97,108,105,115,97,116,105,111,110,115,10,0,0,0,115,97,36,109,112,108,101,110,0,0,0,0,0,0,0,0,78,69,71,65,84,73,86,69,0,0,0,0,0,0,0,0,109,111,36,117,115,101,102,111,114,109,97,116,0,0,0,0,115,101,116,32,115,116,121,108,101,32,102,117,110,99,116,105,111,110,32,0,0,0,0,0,44,32,37,32,35,46,52,103,100,101,103,41,0,0,0,0,80,115,101,117,100,111,100,97,116,97,32,110,111,116,32,121,101,116,32,105,109,112,108,101,109,101,110,116,101,100,32,102,111,114,32,112,111,108,97,114,32,111,114,32,112,97,114,97,109,101,116,114,105,99,32,103,114,97,112,104,115,0,0,0,9,103,110,117,112,108,111,116,32,101,110,99,111,100,105,110,103,32,32,32,37,115,10,0,108,105,110,101,32,37,100,58,32,0,0,0,0,0,0,0,112,36,111,105,110,116,115,0,37,37,32,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,10,0,0,0,110,111,98,36,111,120,0,0,105,110,112,117,116,32,100,97,116,97,32,40,39,101,39,32,101,110,100,115,41,32,62,32,0,0,0,0,0,0,0,0,80,79,83,73,84,73,86,69,0,0,0,0,0,0,0,0,115,104,111,117,108,100,32,98,101,58,32,37,100,32,60,61,32,99,108,105,112,98,111,97,114,100,102,111,114,109,97,116,32,60,61,32,37,100,10,0,115,101,116,32,115,116,121,108,101,32,100,97,116,97,32,0,104,101,97,100,115,0,0,0,109,117,108,116,0,0,0,0,10,35,32,83,117,114,102,97,99,101,32,37,100,32,111,102,32,37,100,32,115,117,114,102,97,99,101,115,10,0,0,0,37,37,32,116,101,109,112,111,114,97,114,121,32,118,97,114,105,97,98,108,101,32,102,111,114,32,115,116,111,114,105,110,103,32,116,104,101,32,112,97,116,104,10,115,97,118,101,32,112,59,32,112,97,116,104,32,112,59,10,0,0,0,0,0,98,36,111,120,0,0,0,0,115,99,114,36,101,101,110,100,117,109,112,0,0,0,0,0,9,102,105,103,117,114,101,32,105,115,32,37,115,10,0,0,95,120,0,0,0,0,0,0,115,101,116,32,99,110,116,114,112,97,114,97,109,32,112,111,105,110,116,115,32,37,100,10,115,101,116,32,115,105,122,101,32,114,97,116,105,111,32,37,103,32,37,103,44,37,103,10,115,101,116,32,111,114,105,103,105,110,32,37,103,44,37,103,10,0,0,0,0,0,0,0,93,32,32,100,105,115,116,97,110,99,101,58,32,0,0,0,109,97,116,114,105,120,32,99,111,110,116,97,105,110,115,32,109,105,115,115,105,110,103,32,111,114,32,117,110,100,101,102,105,110,101,100,32,118,97,108,117,101,115,0,0,0,0,0,71,32,78,32,85,32,80,32,76,32,79,32,84,0,0,0,37,37,32,116,101,109,112,111,114,97,114,121,32,118,97,114,105,97,98,108,101,32,102,111,114,32,115,116,111,114,105,110,103,32,116,104,101,32,112,97,116,104,32,97,110,100,32,105,109,97,103,101,115,10,115,97,118,101,32,112,44,32,105,109,103,44,32,105,109,97,59,32,112,97,116,104,32,112,59,32,115,116,114,105,110,103,32,105,109,103,44,32,105,109,97,59,10,0,0,0,0,0,0,0,116,114,122,0,0,0,0,0,115,104,111,119,46,99,0,0,39,102,111,110,116,110,97,109,101,44,102,111,110,116,115,105,122,101,39,32,101,120,112,101,99,116,101,100,0,0,0,0,99,108,36,105,112,98,111,97,114,100,102,111,114,109,97,116,0,0,0,0,0,0,0,0,100,121,50,61,0,0,0,0,100,102,95,109,97,116,114,105,120,0,0,0,0,0,0,0,32,116,101,120,116,99,111,108,111,114,0,0,0,0,0,0,120,32,114,97,110,103,101,32,105,115,32,105,110,118,97,108,105,100,0,0,0,0,0,0,80,105,112,101,115,32,97,110,100,32,115,104,101,108,108,32,99,111,109,109,97,110,100,115,32,110,111,116,32,112,101,114,109,105,116,116,101,100,32,100,117,114,105,110,103,32,105,110,105,116,105,97,108,105,122,97,116,105,111,110,0,0,0,0,61,61,0,0,0,0,0,0,67,97,110,110,111,116,32,115,101,116,32,105,110,116,101,114,110,97,108,32,118,97,114,105,97,98,108,101,115,32,71,80,86,65,76,95,32,97,110,100,32,77,79,85,83,69,95,0,99,109,0,0,0,0,0,0,80,97,103,101,85,112,0,0,37,115,58,37,100,32,111,111,112,115,58,32,85,110,107,110,111,119,110,32,99,111,108,111,114,32,109,111,100,101,32,39,37,99,39,46,10,0,0,0,100,105,115,99,114,101,116,101,32,37,103,0,0,0,0,0,78,111,32,116,101,114,109,105,110,97,108,32,104,97,115,32,98,101,101,110,32,112,117,115,104,101,100,32,121,101,116,10,0,0,0,0,0,0,0,0,100,120,50,61,0,0,0,0,77,97,116,114,105,120,32,100,111,101,115,32,110,111,116,32,114,101,112,114,101,115,101,110,116,32,97,32,103,114,105,100,0,0,0,0,0,0,0,0,105,110,0,0,0,0,0,0,110,111,105,110,118,36,101,114,116,0,0,0,0,0,0,0,9,67,117,98,101,104,101,108,105,120,32,99,111,108,111,114,32,112,97,108,101,116,116,101,58,32,115,116,97,114,116,32,37,103,32,99,121,99,108,101,115,32,37,103,32,115,97,116,117,114,97,116,105,111,110,32,37,103,10,0,0,0,0,0,110,111,122,111,111,109,106,117,36,109,112,0,0,0,0,0,105,110,99,114,101,109,101,110,116,97,108,32,37,103,44,37,103,44,37,103,10,0,0,0,70,105,108,101,32,100,111,101,115,110,39,116,32,102,97,99,116,111,114,105,122,101,32,105,110,116,111,32,102,117,108,108,32,109,97,116,114,105,120,0,37,109,47,37,100,0,0,0,85,110,115,105,103,110,101,100,66,121,116,101,0,0,0,0,37,37,32,115,99,97,108,105,110,103,32,102,97,99,116,111,114,44,32,119,105,100,116,104,32,97,110,100,32,104,101,105,103,104,116,32,111,102,32,116,104,101,32,102,105,103,117,114,101,10,97,32,58,61,32,49,99,109,59,32,119,32,58,61,32,37,46,51,102,97,59,32,104,32,58,61,32,37,46,51,102,97,59,32,37,37,32,40,37,103,37,115,44,32,37,103,37,115,41,10,0,0,0,0,105,110,118,36,101,114,116,0,9,32,32,67,45,102,111,114,109,117,108,97,58,32,37,115,10,0,0,0,0,0,0,0,122,111,111,109,106,117,36,109,112,0,0,0,0,0,0,0,97,117,116,111,32,37,100,10,0,0,0,0,0,0,0,0,82,101,97,100,32,103,114,105,100,32,119,105,100,116,104,32,116,111,111,32,108,97,114,103,101,0,0,0,0,0,0,0,115,116,114,105,110,103,32,116,101,114,109,118,101,114,115,105,111,110,59,32,32,32,32,116,101,114,109,118,101,114,115,105,111,110,32,32,32,32,58,61,32,34,37,115,34,59,10,0,110,111,114,101,118,36,101,114,115,101,0,0,0,0,0,0,9,32,32,66,45,102,111,114,109,117,108,97,58,32,37,115,10,0,0,0,0,0,0,0,110,111,118,101,36,114,98,111,115,101,0,0,0,0,0,0,115,101,116,32,99,110,116,114,112,97,114,97,109,32,108,101,118,101,108,115,32,0,0,0,82,101,97,100,32,103,114,105,100,32,111,102,32,122,101,114,111,32,119,105,100,116,104,0,48,46,53,32,115,117,98,32,97,98,115,0,0,0,0,0,115,116,114,105,110,103,32,103,110,117,112,108,111,116,118,101,114,115,105,111,110,59,32,103,110,117,112,108,111,116,118,101,114,115,105,111,110,32,58,61,32,34,37,115,34,59,10,0,114,101,118,36,101,114,115,101,0,0,0,0,0,0,0,0,9,32,32,65,45,102,111,114,109,117,108,97,58,32,37,115,10,0,0,0,0,0,0,0,118,101,36,114,98,111,115,101,0,0,0,0,0,0,0,0,98,115,112,108,105,110,101,10,0,0,0,0,0,0,0,0,67,97,110,39,116,32,114,101,97,100,32,115,101,99,111,110,100,32,100,105,109,101,110,115,105,111,110,32,105,110,32,100,97,116,97,32,102,105,108,101,32,34,37,115,34,0,0,0,66,82,69,65,75,58,32,37,115,0,0,0,0,0,0,0,92,115,116,97,114,116,71,78,85,80,76,79,84,103,114,97,112,104,105,99,91,37,100,93,10,0,0,0,0,0,0,0,82,36,105,103,104,116,0,0,9,99,111,108,111,114,32,109,97,112,112,105,110,103,32,105,115,32,100,111,110,101,32,98,121,32,117,115,101,114,32,100,101,102,105,110,101,100,32,102,117,110,99,116,105,111,110,115,10,0,0,0,0,0,0,0,110,111,108,97,36,98,101,108,115,0,0,0,0,0,0,0,99,117,98,105,99,115,112,108,105,110,101,10,0,0,0,0,121,61,0,0,0,0,0,0,67,97,110,39,116,32,114,101,97,100,32,102,105,114,115,116,32,100,105,109,101,110,115,105,111,110,32,105,110,32,100,97,116,97,32,102,105,108,101,32,34,37,115,34,0,0,0,0,92,115,116,97,114,116,71,78,85,80,76,79,84,112,97,103,101,32,37,37,32,71,114,97,112,104,105,99,32,78,114,46,32,37,100,10,0,0,0,0,76,36,101,102,116,0,0,0,9,99,111,108,111,114,32,109,97,112,112,105,110,103,32,98,121,32,100,101,102,105,110,101,100,32,103,114,97,100,105,101,110,116,10,0,0,0,0,0,108,97,98,101,108,36,115,0,108,105,110,101,97,114,10,0,120,61,0,0,0,0,0,0,67,97,110,39,116,32,111,112,101,110,32,100,97,116,97,32,102,105,108,101,32,34,37,115,34,0,0,0,0,0,0,0,9,103,110,117,112,108,111,116,32,76,67,95,67,84,89,80,69,32,32,32,37,115,10,0,34,37,115,34,44,32,108,105,110,101,32,37,100,58,32,0,112,32,58,61,32,40,37,46,51,102,97,44,37,46,51,102,97,41,0,0,0,0,0,0,114,109,36,97,114,103,105,110,0,0,0,0,0,0,0,0,9,114,103,98,32,99,111,108,111,114,32,109,97,112,112,105,110,103,32,98,121,32,114,103,98,102,111,114,109,117,108,97,101,32,97,114,101,32,37,105,44,37,105,44,37,105,10,0,110,111,112,111,36,108,97,114,100,105,115,116,97,110,99,101,0,0,0,0,0,0,0,0,115,101,116,32,99,110,116,114,112,97,114,97,109,32,0,0,32,32,32,115,99,97,108,101,58,32,0,0,0,0,0,0,84,111,111,32,109,97,110,121,32,99,111,108,117,109,110,115,32,105,110,32,117,115,105,110,103,32,115,112,101,99,105,102,105,99,97,116,105,111,110,32,97,110,100,32,105,109,112,108,105,101,100,32,115,97,109,112,108,105,110,103,32,97,114,114,97,121,0,0,0,0,0,0,98,97,99,107,104,101,97,100,0,0,0,0,0,0,0,0,109,105,110,117,115,0,0,0,112,114,105,110,116,95,51,100,116,97,98,108,101,32,111,117,116,112,117,116,32,98,117,102,102,101,114,0,0,0,0,0,103,112,95,115,101,116,95,108,105,110,101,116,121,112,101,40,37,100,41,59,10,0,0,0,108,109,36,97,114,103,105,110,0,0,0,0,0,0,0,0,115,97,36,118,101,0,0,0,67,79,76,79,82,0,0,0,112,111,108,97,114,100,105,115,116,97,110,99,101,116,36,97,110,0,0,0,0,0,0,0,115,101,116,32,99,110,116,114,112,97,114,97,109,32,111,114,100,101,114,32,37,100,10,0,118,105,101,119,58,32,0,0,80,108,111,116,32,115,116,121,108,101,32,114,101,113,117,105,114,101,115,32,104,105,103,104,101,114,32,116,104,97,110,32,111,110,101,45,100,105,109,101,110,115,105,111,110,97,108,32,115,97,109,112,108,105,110,103,32,97,114,114,97,121,0,0,37,115,9,102,97,113,44,32,98,117,103,115,44,32,101,116,99,58,32,32,32,116,121,112,101,32,34,104,101,108,112,32,70,65,81,34,10,37,115,9,105,109,109,101,100,105,97,116,101,32,104,101,108,112,58,32,32,32,116,121,112,101,32,34,104,101,108,112,34,32,32,40,112,108,111,116,32,119,105,110,100,111,119,58,32,104,105,116,32,39,104,39,41,10,0,0,123,37,115,125,41,59,10,0,122,121,120,0,0,0,0,0,98,109,36,97,114,103,105,110,0,0,0,0,0,0,0,0,71,82,65,89,0,0,0,0,112,111,36,108,97,114,100,105,115,116,97,110,99,101,100,101,103,0,0,0,0,0,0,0,115,101,116,32,100,97,116,97,102,105,108,101,32,110,111,102,112,101,95,116,114,97,112,10,0,0,0,0,0,0,0,0,122,111,111,109,105,110,103,32,99,97,110,99,101,108,108,101,100,46,10,0,0,0,0,0,80,108,111,116,32,115,116,121,108,101,32,114,101,113,117,105,114,101,115,32,104,105,103,104,101,114,32,116,104,97,110,32,116,119,111,45,100,105,109,101,110,115,105,111,110,97,108,32,115,97,109,112,108,105,110,103,32,97,114,114,97,121,0,0,112,97,114,97,109,101,116,114,105,99,32,102,117,110,99,116,105,111,110,32,110,111,116,32,102,117,108,108,121,32,115,112,101,99,105,102,105,101,100,0,72,79,77,69,32,110,111,116,32,115,101,116,32,45,32,99,97,110,110,111,116,32,101,120,112,97,110,100,32,116,105,108,100,101,0,0,0,0,0,0,38,0,0,0,0,0,0,0,91,37,115,93,0,0,0,0,116,109,36,97,114,103,105,110,0,0,0,0,0,0,0,0,68,111,119,110,0,0,0,0,9,112,97,108,101,116,116,101,32,105,115,32,37,115,10,0,110,111,122,111,111,109,99,111,36,111,114,100,105,110,97,116,101,115,0,0,0,0,0,0,115,101,116,32,100,97,116,97,102,105,108,101,32,102,111,114,116,114,97,110,10,0,0,0,32,32,32,114,101,115,116,111,114,101,100,32,116,101,114,109,105,110,97,108,32,105,115,32,37,115,32,37,115,10,0,0,96,98,117,105,108,116,105,110,45,99,97,110,99,101,108,45,122,111,111,109,96,32,99,97,110,99,101,108,32,122,111,111,109,32,114,101,103,105,111,110,0,0,0,0,0,0,0,0,80,108,111,116,32,115,116,121,108,101,32,100,111,101,115,32,110,111,116,32,99,111,110,102,111,114,109,32,116,111,32,116,104,114,101,101,32,99,111,108,117,109,110,32,100,97,116,97,32,105,110,32,116,104,105,115,32,103,114,97,112,104,32,109,111,100,101,0,0,0,0,0,97,108,105,103,110,40,37,115,41,44,32,92,115,111,109,101,116,120,116,91,103,112,93,0,111,36,117,116,115,105,100,101,0,0,0,0,0,0,0,0,72,79,82,73,90,79,78,84,65,76,0,0,0,0,0,0,122,111,111,109,99,111,36,111,114,100,105,110,97,116,101,115,0,0,0,0,0,0,0,0,115,101,116,32,100,97,116,97,102,105,108,101,32,99,111,109,109,101,110,116,115,99,104,97,114,115,32,39,37,115,39,10,0,0,0,0,0,0,0,0,77,79,85,83,69,95,75,69,89,95,87,73,78,68,79,87,0,0,0,0,0,0,0,0,110,111,95,99,111,108,115,32,60,61,32,77,65,88,68,65,84,65,67,79,76,83,0,0,104,105,100,100,101,110,32,116,104,101,115,101,95,101,100,103,101,115,0,0,0,0,0,0,72,105,103,104,66,121,116,101,70,105,114,115,116,0,0,0,97,110,103,108,101,40,37,100,41,44,32,0,0,0,0,0,105,110,115,36,105,100,101,0,86,69,82,84,73,67,65,76,0,0,0,0,0,0,0,0,110,111,100,111,36,117,98,108,101,99,108,105,99,107,0,0,71,80,86,65,76,95,86,73,69,87,95,90,83,67,65,76,69,0,0,0,0,0,0,0,101,108,108,105,112,115,101,32,112,108,111,116,0,0,0,0,67,97,110,110,111,116,32,103,101,110,101,114,97,116,101,32,99,111,111,114,100,115,32,102,111,114,32,116,104,97,116,32,112,108,111,116,32,115,116,121,108,101,0,0,0,0,0,0,103,112,95,112,117,116,95,116,101,120,116,40,40,37,46,51,102,97,44,32,37,46,51,102,97,41,44,32,0,0,0,0,98,101,36,108,111,119,0,0,9,99,111,108,111,114,32,103,114,97,100,105,101,110,116,32,105,115,32,37,115,32,105,110,32,116,104,101,32,99,111,108,111,114,32,98,111,120,10,0,100,111,36,117,98,108,101,99,108,105,99,107,0,0,0,0,115,101,116,32,100,97,116,97,102,105,108,101,32,115,101,112,97,114,97,116,111,114,32,119,104,105,116,101,115,112,97,99,101,10,0,0,0,0,0,0,71,80,86,65,76,95,86,73,69,87,95,83,67,65,76,69,0,0,0,0,0,0,0,0,77,97,116,114,105,120,32,100,97,116,97,32,99,111,110,116,97,105,110,115,32,111,110,108,121,32,116,104,114,101,101,32,99,111,108,117,109,110,115,0,71,78,85,80,76,79,84,95,76,73,66,0,0,0,0,0,99,111,115,40,57,48,120,41,0,0,0,0,0,0,0,0,103,112,95,112,111,105,110,116,40,37,46,51,102,97,44,37,46,51,102,97,44,37,100,41,59,10,0,0,0,0,0,0,117,36,110,100,101,114,0,0,65,114,103,104,33,0,0,0,103,114,36,97,112,104,0,0,115,101,116,32,100,97,116,97,102,105,108,101,32,115,101,112,97,114,97,116,111,114,32,34,37,99,34,10,0,0,0,0,37,108,102,0,0,0,0,0,97,98,36,111,118,101,0,0,44,0,0,0,0,0,0,0,10,9,32,32,32,32,32,32,32,32,32,32,115,105,122,101,58,32,0,0,0,0,0,0,115,101,116,32,100,97,116,97,102,105,108,101,32,109,105,115,115,105,110,103,32,39,37,115,39,10,0,0,0,0,0,0,78,111,32,100,101,102,97,117,108,116,32,99,111,108,117,109,110,115,32,107,110,111,119,110,32,102,111,114,32,116,104,97,116,32,112,108,111,116,32,115,116,121,108,101,0,0,0,0,76,97,109,98,100,97,32,115,99,97,108,105,110,103,32,102,97,99,116,111,114,115,32,114,101,115,101,116,58,32,32,37,103,10,0,0,0,0,0,0,85,110,114,101,99,111,103,110,105,122,101,100,32,111,112,116,105,111,110,46,32,32,83,101,101,32,39,104,101,108,112,32,117,110,115,101,116,39,46,0,66,97,100,32,116,105,109,101,32,102,111,114,109,97,116,32,105,110,32,115,116,114,105,110,103,0,0,0,0,0,0,0,44,37,103,112,116,0,0,0,111,118,36,101,114,0,0,0,97,116,32,85,83,69,82,32,111,114,105,103,105,110,58,32,0,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,107,101,121,119,111,114,100,32,39,97,112,112,101,110,100,39,0,0,0,0,0,0,116,117,112,108,101,32,115,121,110,116,97,120,32,101,114,114,111,114,0,0,0,0,0,0,37,98,0,0,0,0,0,0,94,10,0,0,0,0,0,0,121,120,0,0,0,0,0,0,103,112,95,115,101,116,95,112,111,105,110,116,115,105,122,101,40,37,46,51,102,41,59,10,0,0,0,0,0,0,0,0,104,111,114,36,105,122,111,110,116,97,108,0,0,0,0,0,97,116,32,68,69,70,65,85,76,84,32,112,111,115,105,116,105,111,110,10,0,0,0,0,97,112,112,101,110,100,0,0,69,120,112,101,99,116,105,110,103,32,39,44,39,32,111,114,32,39,41,39,0,0,0,0,104,101,97,100,0,0,0,0,112,108,117,115,0,0,0,0,32,37,99,10,0,0,0,0,112,32,58,61,32,117,110,105,116,115,113,117,97,114,101,32,120,121,115,99,97,108,101,100,32,40,37,46,51,102,97,44,37,46,51,102,97,41,32,115,104,105,102,116,101,100,32,40,37,46,51,102,97,44,37,46,51,102,97,41,59,10,0,0,118,101,114,36,116,105,99,97,108,0,0,0,0,0,0,0,114,101,115,36,101,116,0,0,78,79,84,32,100,114,97,119,110,10,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,102,105,108,101,110,97,109,101,0,0,0,0,0,0,112,114,101,102,105,120,0,0,37,115,10,37,115,9,37,115,10,37,115,9,86,101,114,115,105,111,110,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,32,32,32,32,108,97,115,116,32,109,111,100,105,102,105,101,100,32,37,115,10,37,115,9,66,117,105,108,100,32,83,121,115,116,101,109,58,32,37,115,32,37,115,10,37,115,10,37,115,9,37,115,10,37,115,9,84,104,111,109,97,115,32,87,105,108,108,105,97,109,115,44,32,67,111,108,105,110,32,75,101,108,108,101,121,32,97,110,100,32,109,97,110,121,32,111,116,104,101,114,115,10,37,115,10,37,115,9,103,110,117,112,108,111,116,32,104,111,109,101,58,32,32,32,32,32,104,116,116,112,58,47,47,119,119,119,46,103,110,117,112,108,111,116,46,105,110,102,111,10,0,0,0,0,0,0,103,112,95,115,101,116,95,108,105,110,101,119,105,100,116,104,40,37,46,51,102,41,59,10,0,0,0,0,0,0,0,0,120,122,121,0,0,0,0,0,99,36,101,110,116,101,114,0,100,114,97,119,110,32,98,97,99,107,10,9,0,0,0,0,110,111,114,111,116,36,97,116,101,0,0,0,0,0,0,0,10,9,100,117,109,109,121,32,118,97,114,105,97,98,108,101,32,105,115,32,116,32,102,111,114,32,99,117,114,118,101,115,44,32,117,47,118,32,102,111,114,32,115,117,114,102,97,99,101,115,10,0,0,0,0,0,115,101,116,32,109,97,112,112,105,110,103,32,0,0,0,0,98,111,114,100,101,114,0,0,116,104,105,115,95,112,108,111,116,32,33,61,32,78,85,76,76,0,0,0,0,0,0,0,97,120,101,115,32,109,117,115,116,32,98,101,32,120,49,121,49,44,32,120,49,121,50,44,32,120,50,121,49,32,111,114,32,120,50,121,50,0,0,0,116,105,108,100,101,32,101,120,112,97,110,115,105,111,110,0,94,0,0,0,0,0,0,0,99,111,110,116,101,120,116,46,116,114,109,32,115,101,116,95,99,111,108,111,114,32,117,110,107,110,111,119,110,32,99,111,108,111,114,115,112,101,99,45,62,116,121,112,101,32,37,105,0,0,0,0,0,0,0,0,114,36,105,103,104,116,0,0,100,114,97,119,110,32,102,114,111,110,116,10,9,0,0,0,115,101,116,32,109,97,99,114,111,115,10,0,0,0,0,0,115,101,116,32,116,101,114,109,32,37,115,32,37,115,0,0,102,95,115,112,114,105,110,116,102,0,0,0,0,0,0,0,77,111,114,101,32,116,104,97,110,32,37,100,32,101,108,101,109,101,110,116,115,0,0,0,37,37,103,112,95,115,101,116,95,99,111,108,111,114,40,102,114,97,99,40,37,46,52,102,41,41,59,10,0,0,0,0,108,36,101,102,116,0,0,0,9,99,111,108,111,114,32,98,111,120,32,119,105,116,104,111,117,116,32,98,111,114,100,101,114,32,105,115,32,0,0,0,69,120,112,101,99,116,101,100,32,99,111,109,109,97,46,0,117,110,115,101,116,32,99,108,97,98,101,108,10,0,0,0,104,105,100,100,101,110,32,110,111,114,116,104,95,101,100,103,101,115,0,0,0,0,0,0,76,111,119,66,121,116,101,70,105,114,115,116,0,0,0,0,103,112,95,115,101,116,95,99,111,108,111,114,40,114,103,98,40,37,51,46,50,102,44,37,51,46,50,102,44,37,51,46,50,102,41,41,59,10,0,0,98,36,111,116,116,111,109,0,68,69,70,65,85,76,84,32,108,105,110,101,32,116,121,112,101,32,105,115,32,0,0,0,86,97,108,117,101,32,111,117,116,32,111,102,32,114,97,110,103,101,32,91,48,44,49,93,46,0,0,0,0,0,0,0,115,101,116,32,99,108,97,98,101,108,32,39,37,115,39,10,0,0,0,0,0,0,0,0,105,115,111,95,56,56,53,57,95,50,0,0,0,0,0,0,104,105,115,116,111,103,114,97,109,0,0,0,0,0,0,0,73,110,118,97,108,105,100,32,110,117,109,101,114,105,99,32,111,114,32,116,117,112,108,101,32,102,111,114,109,0,0,0,10,35,32,67,117,114,118,101,32,116,105,116,108,101,58,32,34,37,115,34,0,0,0,0,103,112,95,115,101,116,95,99,111,108,111,114,40,108,116,40,37,100,41,41,59,10,0,0,116,36,111,112,0,0,0,0,108,105,110,101,32,116,121,112,101,32,37,100,32,105,115,32,0,0,0,0,0,0,0,0,85,110,107,110,111,119,110,32,99,111,108,111,114,32,110,97,109,101,46,0,0,0,0,0,32,98,111,116,104,10,0,0,73,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,73,110,118,97,108,105,100,32,99,111,109,109,97,32,115,101,112,97,114,97,116,101,100,32,116,121,112,101,0,0,0,0,57,48,32,109,117,108,32,99,111,115,0,0,0,0,0,0,45,45,40,37,46,51,102,97,44,37,46,51,102,97,41,59,10,103,112,95,100,114,97,119,40,112,41,59,10,0,0,0,111,102,102,0,0,0,0,0,9,99,111,108,111,114,32,98,111,120,32,119,105,116,104,32,98,111,114,100,101,114,44,32,0,0,0,0,0,0,0,0,85,110,107,110,111,119,110,32,99,111,108,111,114,32,115,112,101,99,105,102,105,101,114,46,32,85,115,101,32,39,35,114,114,103,103,98,98,39,46,0,32,115,117,114,102,97,99,101,10,0,0,0,0,0,0,0,80,101,114,112,101,110,100,105,99,117,108,97,114,32,118,101,99,116,111,114,32,99,97,110,110,111,116,32,98,101,32,122,101,114,111,0,0,0,0,0,112,117,115,104,99,0,0,0,45,45,99,121,99,108,101,59,10,103,112,95,100,114,97,119,40,112,41,59,10,0,0,0,111,110,0,0,0,0,0,0,101,120,116,101,110,100,32,100,121,110,97,114,114,97,121,0,99,111,114,110,101,114,32,37,105,10,0,0,0,0,0,0,35,37,50,120,37,50,120,37,50,120,0,0,0,0,0,0,32,98,97,115,101,10,0,0,84,104,114,101,101,45,100,105,109,101,110,115,105,111,110,97,108,32,116,117,112,108,101,32,114,101,113,117,105,114,101,100,0,0,0,0,0,0,0,0,76,97,109,98,100,97,32,83,116,97,114,116,32,118,97,108,117,101,32,115,101,116,58,32,37,103,10,0,0,0,0,0,103,112,95,100,111,116,40,37,46,51,102,97,44,37,46,51,102,97,41,59,10,0,0,0,109,97,120,105,109,117,109,32,111,102,32,52,32,99,111,114,110,101,114,115,10,0,0,0,69,120,112,101,99,116,101,100,32,40,32,116,111,32,115,116,97,114,116,32,103,114,97,100,105,101,110,116,32,100,101,102,105,110,105,116,105,111,110,46,0,0,0,0,0,0,0,0,10,115,101,116,32,115,97,109,112,108,101,115,32,37,100,44,32,37,100,10,115,101,116,32,105,115,111,115,97,109,112,108,101,115,32,37,100,44,32,37,100,10,37,115,115,101,116,32,115,117,114,102,97,99,101,10,37,115,115,101,116,32,99,111,110,116,111,117,114,0,0,0,37,66,0,0,0,0,0,0,120,121,0,0,0,0,0,0,41,59,10,0,0,0,0,0,98,97,99,107,0,0,0,0,109,105,110,105,109,117,109,32,111,102,32,52,32,99,111,114,110,101,114,115,10,0,0,0,71,114,97,121,32,115,99,97,108,101,32,110,111,116,32,115,111,114,116,101,100,32,105,110,32,103,114,97,100,105,101,110,116,46,0,0,0,0,0,0,101,113,117,97,108,32,120,121,122,0,0,0,0,0,0,0,37,100,46,32,37,100,46,32,37,48,52,100,32,37,100,58,37,48,50,100,0,0,0,0,110,111,104,101,97,100,0,0,108,101,0,0,0,0,0,0,48,120,37,48,54,120,0,0,44,100,101,110,115,105,116,121,40,48,41,0,0,0,0,0,102,114,111,110,116,0,0,0,114,101,36,114,101,97,100,0,109,101,100,105,97,110,32,111,102,32,52,32,99,111,114,110,101,114,115,10,0,0,0,0,78,111,32,118,97,108,105,100,32,112,97,108,101,116,116,101,32,102,111,117,110,100,0,0,83,84,65,84,83,95,0,0,101,113,117,97,108,32,120,121,0,0,0,0,0,0,0,0,37,100,58,37,48,50,100,0,98,105,36,110,100,0,0,0,73,110,116,101,114,110,97,108,32,101,114,114,111,114,32,40,100,97,116,97,102,105,108,101,46,99,41,58,32,85,110,107,110,111,119,110,32,112,108,111,116,32,109,111,100,101,0,0,47,117,115,114,47,108,111,99,97,108,47,98,105,110,0,0,44,112,97,116,116,101,114,110,40,37,100,41,0,0,0,0,121,120,122,0,0,0,0,0,110,111,98,101,110,116,36,111,118,101,114,0,0,0,0,0,103,101,111,109,101,116,114,105,99,97,108,32,109,101,97,110,32,111,102,32,52,32,99,111,114,110,101,114,115,10,0,0,105,110,118,97,108,105,100,32,111,112,116,105,111,110,0,0,66,97,100,32,100,97,116,97,32,111,110,32,108,105,110,101,32,37,100,0,0,0,0,0,10,115,101,116,32,118,105,101,119,32,32,37,115,0,0,0,37,100,46,32,37,100,46,32,37,48,52,100,0,0,0,0,84,104,114,101,101,45,100,105,109,101,110,115,105,111,110,97,108,32,116,117,112,108,101,32,114,101,113,117,105,114,101,100,32,102,111,114,32,115,101,116,116,105,110,103,32,98,105,110,97,114,121,32,112,97,114,97,109,101,116,101,114,115,0,0,109,97,116,36,114,105,120,0,110,111,98,111,114,100,101,114,10,0,0,0,0,0,0,0,116,104,105,115,95,112,108,111,116,32,61,61,32,42,116,112,95,51,100,95,112,116,114,0,67,97,110,110,111,116,32,101,120,112,97,110,100,32,101,109,112,116,121,32,112,97,116,104,0,0,0,0,0,0,0,0,124,0,0,0,0,0,0,0,71,80,70,85,78,95,0,0,44,100,101,110,115,105,116,121,40,37,46,50,102,41,0,0,98,101,110,116,36,111,118,101,114,0,0,0,0,0,0,0,85,112,0,0,0,0,0,0,97,118,101,114,97,103,101,100,32,52,32,99,111,114,110,101,114,115,10,0,0,0,0,0,112,109,51,100,32,103,114,97,100,105,101,110,116,0,0,0,37,103,44,32,37,103,44,32,37,103,44,32,37,103,0,0,99,97,110,32,111,110,108,121,32,100,111,32,98,101,115,115,101,108,32,102,117,110,99,116,105,111,110,115,32,111,102,32,114,101,97,108,115,0,0,0,70,105,114,115,116,32,112,97,114,97,109,101,116,101,114,32,116,111,32,115,112,114,105,110,116,102,32,109,117,115,116,32,98,101,32,97,32,102,111,114,109,97,116,32,115,116,114,105,110,103,0,0,0,0,0,0,84,104,114,101,101,45,100,105,109,101,110,115,105,111,110,97,108,32,116,117,112,108,101,32,114,101,113,117,105,114,101,100,32,102,111,114,32,51,68,32,112,108,111,116,0,0,0,0,44,116,114,97,110,115,112,97,114,101,110,116,0,0,0,0,110,111,97,108,116,36,100,105,97,103,111,110,97,108,0,0,9,113,117,97,100,114,97,110,103,108,101,32,99,111,108,111,114,32,97,99,99,111,114,100,105,110,103,32,116,111,32,0,103,114,97,100,105,101,110,116,0,0,0,0,0,0,0,0,84,119,111,45,100,105,109,101,110,115,105,111,110,97,108,32,116,117,112,108,101,32,114,101,113,117,105,114,101,100,32,102,111,114,32,50,68,32,112,108,111,116,0,0,0,0,0,0,104,105,100,100,101,110,32,116,104,101,115,101,95,112,111,108,121,115,0,0,0,0,0,0,88,114,105,103,104,116,89,117,112,0,0,0,0,0,0,0,103,112,95,102,105,108,108,40,112,0,0,0,0,0,0,0,97,108,116,36,100,105,97,103,111,110,97,108,0,0,0,0,9,115,116,101,112,115,32,102,111,114,32,98,105,108,105,110,101,97,114,32,105,110,116,101,114,112,111,108,97,116,105,111,110,58,32,37,100,44,37,100,10,0,0,0,0,0,0,0,76,101,115,115,32,116,104,97,110,32,51,32,117,115,105,110,103,32,115,112,101,99,115,32,102,111,114,32,112,97,108,101,116,116,101,0,0,0,0,0,115,101,116,32,118,105,101,119,32,0,0,0,0,0,0,0,44,32,40,117,110,100,101,102,105,110,101,100,41,47,0,0,99,117,116,101,32,108,105,116,116,108,101,32,101,108,108,105,112,115,101,32,102,111,114,32,116,104,101,32,107,101,121,32,115,97,109,112,108,101,0,0,78,117,109,98,101,114,32,111,102,32,98,121,116,101,115,32,116,111,32,115,107,105,112,32,109,117,115,116,32,98,101,32,112,111,115,105,116,105,118,101,32,105,110,116,101,103,101,114,0,0,0,0,0,0,0,0,45,45,99,121,99,108,101,59,10,0,0,0,0,0,0,0,110,111,117,110,100,36,101,102,105,110,101,100,0,0,0,0,9,112,109,51,100,45,104,105,100,100,101,110,51,100,32,105,115,32,37,115,10,0,0,0,117,110,115,101,116,32,100,101,99,105,109,97,108,115,105,103,110,10,0,0,0,0,0,0,111,102,102,36,115,101,116,0,115,99,97,36,108,101,0,0,73,109,112,114,111,112,101,114,32,115,99,97,110,110,105,110,103,32,115,116,114,105,110,103,46,32,84,114,121,32,50,32,99,104,97,114,97,99,116,101,114,32,115,116,114,105,110,103,32,102,111,114,32,50,68,32,100,97,116,97,0,0,0,0,117,112,36,119,97,114,100,115,0,0,0,0,0,0,0,0,100,111,119,110,36,119,97,114,100,115,0,0,0,0,0,0,114,111,119,36,115,102,105,114,115,116,0,0,0,0,0,0,99,111,108,36,117,109,110,115,102,105,114,115,116,0,0,0,115,105,110,40,57,48,120,41,0,0,0,0,0,0,0,0,79,114,105,103,105,110,32,105,115,32,97,116,32,40,37,102,44,37,102,41,0,0,0,0,45,45,40,37,46,51,102,97,44,37,46,51,102,97,41,0,46,92,34,71,78,85,80,76,79,84,58,32,71,82,79,70,70,32,112,105,99,116,117,114,101,32,117,115,105,110,103,32,116,104,101,32,103,112,105,99,32,112,114,101,112,114,111,99,101,115,115,111,114,10,0,0,117,110,100,101,102,36,105,110,101,100,0,0,0,0,0,0,46,80,69,10,0,0,0,0,9,112,109,51,100,45,104,105,100,100,101,110,51,100,32,105,115,32,111,110,32,97,110,100,32,119,105,108,108,32,117,115,101,32,108,105,110,101,115,116,121,108,101,32,37,100,10,0,69,120,112,101,99,116,101,100,32,99,111,109,109,97,0,0,115,101,116,32,100,101,99,105,109,97,108,115,105,103,110,32,39,37,115,39,10,0,0,0,120,61,37,102,59,32,121,61,37,102,10,0,0,0,0,0,47,40,117,110,100,101,102,105,110,101,100,41,0,0,0,0,46,80,83,32,37,102,32,37,102,10,0,0,0,0,0,0,109,111,118,101,32,116,111,32,40,120,43,37,102,44,121,43,37,102,41,10,0,0,0,0,73,109,112,114,111,112,101,114,32,115,99,97,110,110,105,110,103,32,115,116,114,105,110,103,46,32,84,114,121,32,51,32,99,104,97,114,97,99,116,101,114,32,115,116,114,105,110,103,32,102,111,114,32,51,68,32,100,97,116,97,0,0,0,0,100,97,115,104,101,100,32,48,46,48,55,53,0,0,0,0,100,97,115,104,101,100,32,48,46,48,53,0,0,0,0,0,100,111,116,116,101,100,0,0,116,104,105,99,107,110,101,115,115,32,49,46,48,0,0,0,32,92,10,32,32,32,116,104,101,110,32,116,111,32,40,120,43,37,102,44,121,43,37,102,41,0,0,0,0,0,0,0,10,32,32,0,0,0,0,0,108,105,110,101,32,37,115,32,116,111,32,40,120,43,37,102,44,121,43,37,102,41,0,0,116,114,105,36,97,110,103,108,101,112,97,116,116,101,114,110,0,0,0,0,0,0,0,0,97,116,32,120,43,37,102,44,121,43,37,102,10,0,0,0,97,108,108,32,52,32,112,111,105,110,116,115,32,111,102,32,116,104,101,32,113,117,97,100,114,97,110,103,108,101,32,105,110,32,120,44,121,32,114,97,110,103,101,115,10,0,0,0,110,111,116,32,101,110,111,117,103,104,32,109,101,109,111,114,121,32,102,111,114,32,102,117,110,99,116,105,111,110,0,0,115,101,116,32,100,101,99,105,109,97,108,115,105,103,110,32,108,111,99,97,108,101,32,34,37,115,34,10,0,0,0,0,114,106,117,115,116,32,0,0,108,106,117,115,116,32,0,0,67,97,110,110,111,116,32,97,108,116,101,114,32,115,99,97,110,110,105,110,103,32,109,101,116,104,111,100,32,102,111,114,32,111,110,101,45,100,105,109,101,110,115,105,111,110,97,108,32,100,97,116,97,0,0,0,59,32,114,101,115,101,116,32,108,105,110,101,119,105,100,10,0,0,0,0,0,0,0,0,84,111,111,32,109,97,110,121,32,114,97,110,103,101,45,115,112,101,99,115,32,102,111,114,32,97,32,37,100,45,118,97,114,105,97,98,108,101,32,102,105,116,0,0,0,0,0,0,108,105,110,101,32,102,114,111,109,32,120,43,37,102,44,121,43,37,102,32,116,111,32,120,43,37,102,44,121,43,37,102,10,0,0,0,0,0,0,0,97,114,114,111,119,104,101,97,100,61,55,59,32,97,114,114,111,119,32,102,114,111,109,32,120,43,37,102,44,121,43,37,102,32,116,111,32,120,43,37,102,44,121,43,37,102,10,0,49,54,0,0,0,0,0,0,27,91,114,27,91,50,52,59,49,72,0,0,0,0,0,0,40,37,46,51,102,97,44,37,46,51,102,97,41,0,0,0,27,91,50,74,27,91,50,52,59,49,72,0,0,0,0,0,110,111,111,102,102,36,115,101,116,0,0,0,0,0,0,0,27,92,27,91,50,52,59,49,72,0,0,0,0,0,0,0,97,116,32,108,101,97,115,116,32,49,32,112,111,105,110,116,32,111,102,32,116,104,101,32,113,117,97,100,114,97,110,103,108,101,32,105,110,32,120,44,121,32,114,97,110,103,101,115,10,0,0,0,0,0,0,0,115,101,116,32,112,111,105,110,116,115,105,122,101,32,37,103,10,115,101,116,32,112,111,105,110,116,105,110,116,101,114,118,97,108,98,111,120,32,37,103,10,115,101,116,32,101,110,99,111,100,105,110,103,32,37,115,10,37,115,115,101,116,32,112,111,108,97,114,10,37,115,115,101,116,32,112,97,114,97,109,101,116,114,105,99,10,0,0,27,91,50,74,27,80,49,112,83,40,67,48,41,83,40,69,41,84,40,65,48,41,10,0,80,91,37,100,44,37,100,93,10,0,0,0,0,0,0,0,118,91,93,118,91,37,100,44,37,100,93,10,0,0,0,0,122,90,0,0,0,0,0,0,87,40,80,37,100,41,10,0,87,40,73,37,100,41,10,0,37,97,0,0,0,0,0,0,84,40,68,48,44,83,49,41,10,0,0,0,0,0,0,0,10,37,115,37,115,10,0,0,84,40,68,57,48,44,83,49,41,10,0,0,0,0,0,0,67,111,109,109,97,110,100,32,39,117,110,115,101,116,32,104,105,115,116,111,114,121,115,105,122,101,39,32,114,101,113,117,105,114,101,115,32,104,105,115,116,111,114,121,32,115,117,112,112,111,114,116,46,0,0,0,100,101,112,114,101,99,97,116,101,100,32,115,121,110,116,97,120,44,32,117,115,101,32,34,117,110,115,101,116,34,0,0,27,77,71,49,27,82,75,33,27,83,75,33,27,76,90,27,37,37,33,49,0,0,0,0,112,32,58,61,32,0,0,0,27,37,37,33,48,27,77,78,48,27,77,67,66,55,67,59,27,77,81,49,27,77,84,49,0,0,0,0,0,0,0,0,27,37,37,33,48,27,76,90,27,37,37,33,49,0,0,0,9,99,108,105,112,112,105,110])
+.concat([103,58,32,0,0,0,0,0,78,97,109,101,100,32,99,111,108,111,114,115,32,119,105,108,108,32,112,114,111,100,117,99,101,32,115,116,114,97,110,103,101,32,114,101,115,117,108,116,115,32,105,102,32,110,111,116,32,105,110,32,99,111,108,111,114,32,109,111,100,101,32,82,71,66,46,0,0,0,0,0,115,101,116,32,111,102,102,115,101,116,115,0,0,0,0,0,27,76,86,49,27,37,37,33,49,0,0,0,0,0,0,0,27,37,37,33,48,27,12,27,76,86,48,0,0,0,0,0,27,76,70,0,0,0,0,0,121,89,0,0,0,0,0,0,115,106,36,105,115,0,0,0,27,76,71,0,0,0,0,0,103,101,0,0,0,0,0,0,37,52,100,32,0,0,0,0,27,77,86,0,0,0,0,0,27,77,76,0,0,0,0,0,27,77,76,63,0,0,0,0,37,108,102,32,44,32,37,108,102,32,37,99,0,0,0,0,27,77,76,53,0,0,0,0,32,32,99,108,105,112,32,99,117,114,114,101,110,116,112,105,99,116,117,114,101,32,116,111,32,117,110,105,116,115,113,117,97,114,101,32,120,121,115,99,97,108,101,100,32,40,37,46,51,102,97,44,37,46,51,102,97,41,32,115,104,105,102,116,101,100,32,40,37,46,51,102,97,44,37,46,51,102,97,41,59,41,59,10,0,0,0,0,27,76,84,0,0,0,0,0,100,101,102,36,97,117,108,116,115,0,0,0,0,0,0,0,114,101,112,36,108,111,116,0,27,77,82,69,58,48,0,0,9,102,108,117,115,104,105,110,103,32,116,114,105,97,110,103,108,101,115,32,97,114,101,32,37,115,100,114,97,119,110,10,0,0,0,0,0,0,0,0,105,110,118,97,108,105,100,32,112,97,108,101,116,116,101,32,111,112,116,105,111,110,0,0,69,120,112,101,99,116,105,110,103,32,91,110,111,93,111,117,116,112,117,116,32,111,114,32,112,114,101,102,105,120,0,0,115,101,116,32,108,111,103,115,99,97,108,101,32,37,115,32,37,103,10,0,0,0,0,0,27,77,82,48,48,0,0,0,27,76,72,0,0,0,0,0,27,77,77,0,0,0,0,0,120,88,0,0,0,0,0,0,31,37,115,10,0,0,0,0,27,91,63,51,56,108,0,0,35,33,37,115,47,103,110,117,112,108,111,116,10,35,10,0,96,97,96,97,98,99,100,104,105,106,107,108,0,0,0,0,31,27,3,0,0,0,0,0,67,111,109,109,97,110,100,32,39,115,101,116,32,104,105,115,116,111,114,121,115,105,122,101,39,32,114,101,113,117,105,114,101,115,32,104,105,115,116,111,114,121,32,115,117,112,112,111,114,116,46,0,0,0,0,0,100,114,97,119,32,98,105,116,109,97,112,105,109,97,103,101,32,40,37,117,44,37,117,44,105,109,103,41,32,120,121,115,99,97,108,101,100,32,40,37,46,51,102,97,44,37,46,51,102,97,41,32,115,104,105,102,116,101,100,32,40,37,46,51,102,97,44,37,46,51,102,97,41,59,10,0,0,0,0,0,27,12,0,0,0,0,0,0,110,111,32,109,97,116,99,104,105,110,103,32,39,125,39,0,122,36,101,114,111,0,0,0,121,122,120,0,0,0,0,0,56,57,58,59,0,0,0,0,69,78,68,0,0,0,0,0,112,97,114,97,36,108,108,101,108,0,0,0,0,0,0,0,110,111,110,45,110,101,103,97,116,105,118,101,32,110,117,109,98,101,114,32,114,101,113,117,105,114,101,100,0,0,0,0,117,110,115,101,116,32,108,111,103,115,99,97,108,101,10,0,27,37,99,0,0,0,0,0,27,91,63,51,56,104,0,0,116,105,116,108,101,0,0,0,67,97,110,32,111,110,108,121,32,102,108,105,112,32,120,44,32,121,44,32,97,110,100,47,111,114,32,122,0,0,0,0,32,101,109,112,116,121,32,0,106,115,36,100,105,114,0,0,102,115,105,122,101,0,0,0,97,120,36,101,115,0,0,0,32,106,115,100,105,114,32,34,37,115,34,0,0,0,0,0,47,98,105,110,47,115,104,0,38,38,0,0,0,0,0,0,32,116,105,116,108,101,32,34,37,115,34,0,0,0,0,0,118,97,114,110,97,109,101,0,32,109,111,117,115,105,110,103,0,0,0,0,0,0,0,0,100,114,97,119,32,105,109,97,103,101,40,10,32,32,0,0,32,115,116,97,110,100,97,108,111,110,101,0,0,0,0,0,114,97,120,36,105,115,0,0,32,110,97,109,101,32,34,37,115,34,0,0,0,0,0,0,115,119,97,112,112,101,100,32,112,100,112,32,40,100,105,109,109,108,101,41,0,0,0,0,66,69,71,73,78,0,0,0,85,110,107,110,111,119,110,32,99,111,108,111,114,32,109,111,100,101,108,46,0,0,0,0,37,115,10,0,0,0,0,0,116,105,116,108,101,32,0,0,32,102,111,110,116,115,99,97,108,101,32,37,103,0,0,0,9,99,117,114,114,101,110,116,32,116,101,114,109,105,110,97,108,32,116,121,112,101,32,105,115,32,117,110,107,110,111,119,110,10,0,0,0,0,0,0,77,79,85,83,69,95,67,84,82,76,0,0,0,0,0,0,32,101,110,104,97,110,99,101,100,0,0,0,0,0,0,0,115,112,114,105,110,116,102,32,97,114,103,115,0,0,0,0,37,115,32,102,115,105,122,101,32,37,103,32,108,119,32,37,103,0,0,0,0,0,0,0,120,88,121,89,122,90,0,0,32,115,105,122,101,32,37,100,44,37,100,0,0,0,0,0,32,98,117,116,116,0,0,0,32,114,111,117,110,100,101,100,0,0,0,0,0,0,0,0,32,100,97,115,104,108,101,110,103,116,104,32,37,51,46,49,102,0,0,0,0,0,0,0,32,115,111,108,105,100,0,0,105,109,97,32,58,61,32,34,37,37,10,0,0,0,0,0,102,111,110,116,58,32,101,120,112,101,99,116,105,110,103,32,115,116,114,105,110,103,0,0,122,101,114,111,97,36,120,105,115,0,0,0,0,0,0,0,105,108,108,101,103,97,108,32,106,97,118,97,115,99,114,105,112,116,32,102,117,110,99,116,105,111,110,32,110,97,109,101,0,0,0,0,0,0,0,0,102,108,117,115,104,101,100,32,102,114,111,109,32,37,115,10,0,0,0,0,0,0,0,0,69,120,112,101,99,116,101,100,32,99,111,108,111,114,32,109,111,100,101,108,46,0,0,0,99,111,108,117,109,110,115,116,97,99,107,101,100,32,0,0,101,120,112,101,99,116,105,110,103,32,97,32,106,97,118,97,115,99,114,105,112,116,32,102,117,110,99,116,105,111,110,32,110,97,109,101,0,0,0,0,77,79,85,83,69,95,65,76,84,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,97,110,32,72,84,77,76,32,116,105,116,108,101,32,115,116,114,105,110,103,0,0,60,47,100,105,118,62,10,10,60,47,98,111,100,121,62,10,60,47,104,116,109,108,62,10,0,0,0,0,0,0,0,0,70,108,105,112,112,105,110,103,32,100,105,109,101,110,115,105,111,110,32,100,105,114,101,99,116,105,111,110,32,109,117,115,116,32,98,101,32,49,32,111,114,32,48,0,0,0,0,0,60,47,116,100,62,60,47,116,114,62,60,47,116,97,98,108,101,62,10,0,0,0,0,0,104,105,100,100,101,110,32,110,111,114,116,104,95,112,111,108,121,115,0,0,0,0,0,0,60,116,97,98,108,101,32,99,108,97,115,115,61,34,112,108,111,116,34,62,10,60,116,114,62,60,116,100,62,10,32,32,32,32,60,99,97,110,118,97,115,32,105,100,61,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,34,32,119,105,100,116,104,61,34,37,100,34,32,104,101,105,103,104,116,61,34,37,100,34,32,116,97,98,105,110,100,101,120,61,34,48,34,62,10,9,83,111,114,114,121,44,32,121,111,117,114,32,98,114,111,119,115,101,114,32,115,101,101,109,115,32,110,111,116,32,116,111,32,115,117,112,112,111,114,116,32,116,104,101,32,72,84,77,76,32,53,32,99,97,110,118,97,115,32,101,108,101,109,101,110,116,10,32,32,32,32,60,47,99,97,110,118,97,115,62,10,60,47,116,100,62,60,47,116,114,62,10,60,47,116,97,98,108,101,62,10,0,0,0,0,0,0,0,60,47,116,100,62,60,116,100,62,10,0,0,0,0,0,0,103,110,117,112,108,111,116,95,99,111,110,116,111,117,114,0,60,47,116,97,98,108,101,62,60,47,116,100,62,60,47,116,114,62,10,60,47,116,97,98,108,101,62,10,0,0,0,0,60,116,114,62,32,60,116,100,32,99,108,97,115,115,61,34,109,98,48,34,62,121,50,38,110,98,115,112,59,60,47,116,100,62,32,60,116,100,32,99,108,97,115,115,61,34,109,98,49,34,62,60,115,112,97,110,32,105,100,61,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,95,121,50,34,62,38,110,98,115,112,59,60,47,115,112,97,110,62,60,47,116,100,62,32,60,47,116,114,62,10,0,0,0,0,0,0,0,60,116,114,62,32,60,116,100,32,99,108,97,115,115,61,34,109,98,48,34,62,120,50,38,110,98,115,112,59,60,47,116,100,62,32,60,116,100,32,99,108,97,115,115,61,34,109,98,49,34,62,60,115,112,97,110,32,105,100,61,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,95,120,50,34,62,38,110,98,115,112,59,60,47,115,112,97,110,62,60,47,116,100,62,32,60,47,116,114,62,10,0,0,0,0,0,0,0,37,48,50,120,0,0,0,0,88,114,105,103,104,116,89,100,111,119,110,0,0,0,0,0,122,122,101,114,111,97,36,120,105,115,0,0,0,0,0,0,60,116,97,98,108,101,32,99,108,97,115,115,61,34,109,111,117,115,101,98,111,120,34,32,105,100,61,34,103,110,117,112,108,111,116,95,109,111,117,115,101,98,111,120,34,32,98,111,114,100,101,114,61,49,62,10,60,116,114,62,32,60,116,100,32,99,108,97,115,115,61,34,109,98,48,34,62,120,38,110,98,115,112,59,60,47,116,100,62,32,60,116,100,32,99,108,97,115,115,61,34,109,98,49,34,62,60,115,112,97,110,32,105,100,61,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,95,120,34,62,38,110,98,115,112,59,60,47,115,112,97,110,62,60,47,116,100,62,32,60,47,116,114,62,10,60,116,114,62,32,60,116,100,32,99,108,97,115,115,61,34,109,98,48,34,62,121,38,110,98,115,112,59,60,47,116,100,62,32,60,116,100,32,99,108,97,115,115,61,34,109,98,49,34,62,60,115,112,97,110,32,105,100,61,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,95,121,34,62,38,110,98,115,112,59,60,47,115,112,97,110,62,60,47,116,100,62,32,60,47,116,114,62,10,0,0,67,69,78,84,69,82,69,68,10,0,0,0,0,0,0,0,115,97,116,36,117,114,97,116,105,111,110,0,0,0,0,0,114,111,119,115,116,97,99,107,101,100,32,0,0,0,0,0,35,35,32,0,0,0,0,0,32,32,32,32,32,32,60,47,116,97,98,108,101,62,10,32,32,60,47,116,100,62,60,47,116,114,62,10,60,47,116,97,98,108,101,62,60,47,116,100,62,60,47,116,114,62,60,116,114,62,60,116,100,32,99,108,97,115,115,61,34,109,111,117,115,101,98,111,120,34,62,10,0,0,0,0,0,0,0,0,77,79,85,83,69,95,83,72,73,70,84,0,0,0,0,0,9,60,47,116,114,62,10,0,9,32,32,60,116,100,32,99,108,97,115,115,61,34,105,99,111,110,34,32,62,32,60,47,116,100,62,10,0,0,0,0,83,97,109,112,108,101,32,112,101,114,105,111,100,32,109,117,115,116,32,98,101,32,112,111,115,105,116,105,118,101,46,32,84,114,121,32,96,102,108,105,112,96,32,102,111,114,32,99,104,97,110,103,105,110,103,32,100,105,114,101,99,116,105,111,110,0,0,0,0,0,0,0,84,104,105,115,32,116,101,114,109,105,110,97,108,32,100,111,101,115,32,110,111,116,32,115,117,112,112,111,114,116,32,102,105,108,108,101,100,32,112,111,108,121,103,111,110,115,0,0,9,32,32,60,116,100,32,99,108,97,115,115,61,34,105,99,111,110,34,32,111,110,99,108,105,99,107,61,103,110,117,112,108,111,116,46,116,111,103,103,108,101,95,112,108,111,116,40,34,103,112,95,112,108,111,116,95,37,100,34,41,62,37,100,60,47,116,100,62,10,0,0,112,97,114,97,109,101,116,101,114,32,116,111,32,96,112,109,51,100,32,97,116,96,32,114,101,113,117,105,114,101,115,32,99,111,109,98,105,110,97,116,105,111,110,32,111,102,32,117,112,32,116,111,32,54,32,99,104,97,114,97,99,116,101,114,115,32,98,44,115,44,116,10,9,40,100,114,97,119,105,110,103,32,97,116,32,98,111,116,116,111,109,44,32,115,117,114,102,97,99,101,44,32,116,111,112,41,0,0,0,0,0,0,9,60,116,114,62,10,0,0,60,116,97,98,108,101,32,99,108,97,115,115,61,34,109,98,108,101,102,116,34,62,60,116,114,62,60,116,100,32,99,108,97,115,115,61,34,109,111,117,115,101,98,111,120,34,62,10,60,116,97,98,108,101,32,99,108,97,115,115,61,34,109,111,117,115,101,98,111,120,34,32,98,111,114,100,101,114,61,48,62,10,32,32,60,116,114,62,60,116,100,32,99,108,97,115,115,61,34,109,111,117,115,101,98,111,120,34,62,10,32,32,32,32,60,116,97,98,108,101,32,99,108,97,115,115,61,34,109,111,117,115,101,98,111,120,34,32,105,100,61,34,103,110,117,112,108,111,116,95,109,111,117,115,101,98,111,120,34,32,98,111,114,100,101,114,61,48,62,10,32,32,32,32,60,116,114,62,60,116,100,32,99,108,97,115,115,61,34,109,98,104,34,62,60,47,116,100,62,60,47,116,114,62,10,32,32,32,32,60,116,114,62,60,116,100,32,99,108,97,115,115,61,34,109,98,104,34,62,10,32,32,32,32,32,32,60,116,97,98,108,101,32,99,108,97,115,115,61,34,109,111,117,115,101,98,111,120,34,62,10,9,60,116,114,62,10,9,32,32,60,116,100,32,99,108,97,115,115,61,34,105,99,111,110,34,62,60,47,116,100,62,10,9,32,32,60,116,100,32,99,108,97,115,115,61,34,105,99,111,110,34,32,111,110,99,108,105,99,107,61,103,110,117,112,108,111,116,46,116,111,103,103,108,101,95,103,114,105,100,62,60,105,109,103,32,115,114,99,61,34,37,115,103,114,105,100,46,112,110,103,34,32,105,100,61,34,103,110,117,112,108,111,116,95,103,114,105,100,95,105,99,111,110,34,32,99,108,97,115,115,61,34,105,99,111,110,45,105,109,97,103,101,34,32,97,108,116,61,34,35,34,32,116,105,116,108,101,61,34,116,111,103,103,108,101,32,103,114,105,100,34,62,60,47,116,100,62,10,9,32,32,60,116,100,32,99,108,97,115,115,61,34,105,99,111,110,34,32,111,110,99,108,105,99,107,61,103,110,117,112,108,111,116,46,117,110,122,111,111,109,62,60,105,109,103,32,115,114,99,61,34,37,115,112,114,101,118,105,111,117,115,122,111,111,109,46,112,110,103,34,32,105,100,61,34,103,110,117,112,108,111,116,95,117,110,122,111,111,109,95,105,99,111,110,34,32,99,108,97,115,115,61,34,105,99,111,110,45,105,109,97,103,101,34,32,97,108,116,61,34,117,110,122,111,111,109,34,32,116,105,116,108,101,61,34,117,110,122,111,111,109,34,62,60,47,116,100,62,10,9,32,32,60,116,100,32,99,108,97,115,115,61,34,105,99,111,110,34,32,111,110,99,108,105,99,107,61,103,110,117,112,108,111,116,46,114,101,122,111,111,109,62,60,105,109,103,32,115,114,99,61,34,37,115,110,101,120,116,122,111,111,109,46,112,110,103,34,32,105,100,61,34,103,110,117,112,108,111,116,95,114,101,122,111,111,109,95,105,99,111,110,34,32,99,108,97,115,115,61,34,105,99,111,110,45,105,109,97,103,101,34,32,97,108,116,61,34,114,101,122,111,111,109,34,32,116,105,116,108,101,61,34,114,101,122,111,111,109,34,62,60,47,116,100,62,10,9,32,32,60,116,100,32,99,108,97,115,115,61,34,105,99,111,110,34,32,111,110,99,108,105,99,107,61,103,110,117,112,108,111,116,46,116,111,103,103,108,101,95,122,111,111,109,95,116,101,120,116,62,60,105,109,103,32,115,114,99,61,34,37,115,116,101,120,116,122,111,111,109,46,112,110,103,34,32,105,100,61,34,103,110,117,112,108,111,116,95,116,101,120,116,122,111,111,109,95,105,99,111,110,34,32,99,108,97,115,115,61,34,105,99,111,110,45,105,109,97,103,101,34,32,97,108,116,61,34,122,111,111,109,32,116,101,120,116,34,32,116,105,116,108,101,61,34,122,111,111,109,32,116,101,120,116,32,119,105,116,104,32,112,108,111,116,34,62,60,47,116,100,62,10,9,32,32,60,116,100,32,99,108,97,115,115,61,34,105,99,111,110,34,32,111,110,99,108,105,99,107,61,103,110,117,112,108,111,116,46,112,111,112,117,112,95,104,101,108,112,40,41,62,60,105,109,103,32,115,114,99,61,34,37,115,104,101,108,112,46,112,110,103,34,32,105,100,61,34,103,110,117,112,108,111,116,95,104,101,108,112,95,105,99,111,110,34,32,99,108,97,115,115,61,34,105,99,111,110,45,105,109,97,103,101,34,32,97,108,116,61,34,63,34,32,116,105,116,108,101,61,34,104,101,108,112,34,62,60,47,116,100,62,10,9,60,47,116,114,62,10,0,0,0,0,0,60,47,115,99,114,105,112,116,62,10,60,108,105,110,107,32,116,121,112,101,61,34,116,101,120,116,47,99,115,115,34,32,104,114,101,102,61,34,37,115,103,110,117,112,108,111,116,95,109,111,117,115,101,46,99,115,115,34,32,114,101,108,61,34,115,116,121,108,101,115,104,101,101,116,34,62,10,60,47,104,101,97,100,62,10,60,98,111,100,121,32,111,110,108,111,97,100,61,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,40,41,59,32,103,110,117,112,108,111,116,46,105,110,105,116,40,41,59,34,32,111,110,99,111,110,116,101,120,116,109,101,110,117,61,34,114,101,116,117,114,110,32,102,97,108,115,101,59,34,62,10,10,60,100,105,118,32,99,108,97,115,115,61,34,103,110,117,112,108,111,116,34,62,10,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,116,105,109,101,97,120,105,115,95,120,32,61,32,34,34,59,10,0,0,34,59,10,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,116,105,109,101,97,120,105,115,95,120,32,61,32,34,37,115,34,59,10,0,0,0,0,0,0,0,0,121,50,122,101,114,111,97,36,120,105,115,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,109,97,120,32,61,32,37,46,51,102,59,10,0,9,115,117,98,115,101,113,117,101,110,116,32,115,99,97,110,115,32,119,105,116,104,32,100,105,102,102,101,114,101,110,116,32,110,98,32,111,102,32,112,116,115,32,97,114,101,32,0,99,121,99,36,108,101,115,0,101,114,114,111,114,98,97,114,115,32,103,97,112,32,37,100,32,108,119,32,37,103,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,109,105,110,32,61,32,37,46,51,102,59,10,0,77,79,85,83,69,95,89,50,0,0,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,104,101,105,103,104,116,32,61,32,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,121,109,97,120,32,45,32,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,121,109,105,110,59,10,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,119,105,100,116,104,32,61,32,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,109,97,120,32,45,32,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,109,105,110,59,10,0,0,0,0,0,77,111,114,101,32,112,97,114,97,109,101,116,101,114,115,32,115,112,101,99,105,102,105,101,100,32,116,104,97,110,32,100,97,116,97,32,114,101,99,111,114,100,115,32,115,112,101,99,105,102,105,101,100,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,108,111,103,97,120,105,115,95,114,32,61,32,37,100,59,10,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,108,111,103,97,120,105,115,95,121,32,61,32,37,100,59,10,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,108,111,103,97,120,105,115,95,120,32,61,32,37,100,59,10,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,121,50,109,105,110,32,61,32,34,110,111,110,101,34,10,0,0,0,0,0,0,0,57,48,32,109,117,108,32,115,105,110,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,121,50,109,97,120,0,37,48,50,120,37,48,50,120,37,48,50,120,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,121,50,109,105,110,0,121,122,101,114,111,97,36,120,105,115,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,50,109,105,110,32,61,32,34,110,111,110,101,34,10,0,0,0,0,0,0,0,9,116,97,107,105,110,103,32,115,99,97,110,115,32,100,105,114,101,99,116,105,111,110,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,99,108,117,115,116,101,114,101,100,32,103,97,112,32,37,100,32,0,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,50,109,97,120,0,101,120,112,97,110,100,105,110,103,32,99,117,114,118,101,32,118,97,114,105,97,98,108,101,32,99,111,108,111,114,115,0,77,79,85,83,69,95,88,50,0,0,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,50,109,105,110,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,114,109,105,110,32,61,32,37,103,59,10,0,0,0,67,97,110,110,111,116,32,102,108,105,112,32,97,32,110,111,110,45,101,120,105,115,116,101,110,116,32,100,105,109,101,110,115,105,111,110,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,121,109,97,120,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,121,109,105,110,0,0,103,110,117,112,108,111,116,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,10,0,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,109,97,120,0,0,37,115,32,61,32,37,103,59,10,0,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,97,120,105,115,95,120,109,105,110,0,0,37,37,10,0,0,0,0,0,37,115,32,61,32,37,100,59,10,0,0,0,0,0,0,0,120,50,122,101,114,111,97,36,120,105,115,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,104,101,105,103,104,116,32,61,32,37,46,49,102,59,10,0,0,0,0,66,65,67,75,87,65,82,68,0,0,0,0,0,0,0,0,99,111,108,111,114,32,102,111,114,109,117,108,97,32,111,117,116,32,111,102,32,114,97,110,103,101,32,40,117,115,101,32,96,115,104,111,119,32,112,97,108,101,116,116,101,32,114,103,98,102,111,114,109,117,108,97,101,39,32,116,111,32,100,105,115,112,108,97,121,32,116,104,101,32,114,97,110,103,101,41,0,0,0,0,0,0,0,0,115,101,116,32,115,116,121,108,101,32,104,105,115,116,111,103,114,97,109,32,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,119,105,100,116,104,32,61,32,37,46,49,102,59,10,0,0,0,0,0,77,79,85,83,69,95,89,0,103,110,117,112,108,111,116,46,112,108,111,116,95,121,116,111,112,32,61,32,37,46,49,102,59,10,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,121,98,111,116,32,61,32,37,46,49,102,59,10,0,0,0,0,0,0,70,111,114,109,97,116,32,115,112,101,99,105,102,105,101,114,32,109,117,115,116,32,98,101,103,105,110,32,119,105,116,104,32,39,37,39,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,120,109,97,120,32,61,32,37,46,49,102,59,10,0,0,0,0,0,0,67,97,110,39,116,32,114,101,45,110,97,109,101,32,39,121,39,32,105,110,32,97,32,111,110,101,45,118,97,114,105,97,98,108,101,32,102,105,116,0,117,110,100,101,102,105,110,101,100,32,118,97,108,117,101,0,103,110,117,112,108,111,116,46,112,108,111,116,95,120,109,105,110,32,61,32,37,46,49,102,59,10,0,0,0,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,116,101,114,109,95,121,109,97,120,32,61,32,37,100,59,10,0,0,0,103,110,117,112,108,111,116,46,112,108,111,116,95,116,101,114,109,95,120,109,97,120,32,61,32,37,100,59,10,0,0,0,10,47,47,32,112,108,111,116,32,98,111,117,110,100,97,114,105,101,115,32,97,110,100,32,97,120,105,115,32,115,99,97,108,105,110,103,32,105,110,102,111,114,109,97,116,105,111,110,32,102,111,114,32,109,111,117,115,105,110,103,32,10,0,0,105,109,103,32,58,61,32,34,37,37,10,0,0,0,0,0,67,97,110,118,97,115,84,101,120,116,70,117,110,99,116,105,111,110,115,46,101,110,97,98,108,101,40,99,116,120,41,59,10,99,116,120,46,115,116,114,111,107,101,83,116,121,108,101,32,61,32,34,114,103,98,40,50,49,53,44,50,49,53,44,50,49,53,41,34,59,10,99,116,120,46,108,105,110,101,87,105,100,116,104,32,61,32,37,46,49,103,59,10,10,0,0,120,122,101,114,111,97,36,120,105,115,0,0,0,0,0,0,99,116,120,46,102,105,108,108,83,116,121,108,101,32,61,32,34,37,115,34,59,10,99,116,120,46,102,105,108,108,82,101,99,116,40,48,44,48,44,37,100,44,37,100,41,59,10,0,70,79,82,87,65,82,68,0,99,111,110,102,108,105,99,116,105,110,103,32,111,112,116,105,111,110,115,0,0,0,0,0,99,116,120,46,108,105,110,101,67,97,112,32,61,32,34,37,115,34,59,32,99,116,120,46,108,105,110,101,74,111,105,110,32,61,32,34,37,115,34,59,10,0,0,0,0,0,0,0,77,79,85,83,69,95,88,0,103,110,117,112,108,111,116,46,100,97,115,104,108,101,110,103,116,104,32,61,32,37,100,59,10,0,0,0,0,0,0,0,47,47,32,115,104,111,114,116,32,102,111,114,109,115,32,111,102,32,99,111,109,109,97,110,100,115,32,112,114,111,118,105,100,101,100,32,98,121,32,103,110,117,112,108,111,116,95,99,111,109,109,111,110,46,106,115,10,102,117,110,99,116,105,111,110,32,68,84,32,32,40,100,116,41,32,32,123,103,110,117,112,108,111,116,46,100,97,115,104,116,121,112,101,40,100,116,41,59,125,59,10,102,117,110,99,116,105,111,110,32,68,83,32,32,40,120,44,121,41,32,123,103,110,117,112,108,111,116,46,100,97,115,104,115,116,97,114,116,40,120,44,121,41,59,125,59,10,102,117,110,99,116,105,111,110,32,68,76,32,32,40,120,44,121,41,32,123,103,110,117,112,108,111,116,46,100,97,115,104,115,116,101,112,40,120,44,121,41,59,125,59,10,102,117,110,99,116,105,111,110,32,77,32,32,32,40,120,44,121,41,32,123,105,102,32,40,103,110,117,112,108,111,116,46,112,97,116,116,101,114,110,46,108,101,110,103,116,104,32,62,32,48,41,32,68,83,40,120,44,121,41,59,32,101,108,115,101,32,103,110,117,112,108,111,116,46,77,40,120,44,121,41,59,125,59,10,102,117,110,99,116,105,111,110,32,76,32,32,32,40,120,44,121,41,32,123,105,102,32,40,103,110,117,112,108,111,116,46,112,97,116,116,101,114,110,46,108,101,110,103,116,104,32,62,32,48,41,32,68,76,40,120,44,121,41,59,32,101,108,115,101,32,103,110,117,112,108,111,116,46,76,40,120,44,121,41,59,125,59,10,102,117,110,99,116,105,111,110,32,68,111,116,32,40,120,44,121,41,32,123,103,110,117,112,108,111,116,46,68,111,116,40,120,47,49,48,46,44,121,47,49,48,46,41,59,125,59,10,102,117,110,99,116,105,111,110,32,80,116,32,32,40,78,44,120,44,121,44,119,41,32,123,103,110,117,112,108,111,116,46,80,116,40,78,44,120,47,49,48,46,44,121,47,49,48,46,44,119,47,49,48,46,41,59,125,59,10,102,117,110,99,116,105,111,110,32,82,32,32,32,40,120,44,121,44,119,44,104,41,32,123,103,110,117,112,108,111,116,46,82,40,120,44,121,44,119,44,104,41,59,125,59,10,102,117,110,99,116,105,111,110,32,84,32,32,32,40,120,44,121,44,102,111,110,116,115,105,122,101,44,106,117,115,116,105,102,121,44,115,116,114,105,110,103,41,32,123,103,110,117,112,108,111,116,46,84,40,120,44,121,44,102,111,110,116,115,105,122,101,44,106,117,115,116,105,102,121,44,115,116,114,105,110,103,41,59,125,59,10,102,117,110,99,116,105,111,110,32,84,82,32,32,40,120,44,121,44,97,110,103,108,101,44,102,111,110,116,115,105,122,101,44,106,117,115,116,105,102,121,44,115,116,114,105,110,103,41,32,123,103,110,117,112,108,111,116,46,84,82,40,120,44,121,44,97,110,103,108,101,44,102,111,110,116,115,105,122,101,44,106,117,115,116,105,102,121,44,115,116,114,105,110,103,41,59,125,59,10,102,117,110,99,116,105,111,110,32,98,112,32,32,40,120,44,121,41,32,123,103,110,117,112,108,111,116,46,98,112,40,120,44,121,41,59,125,59,10,102,117,110,99,116,105,111,110,32,99,102,112,32,40,41,32,123,103,110,117,112,108,111,116,46,99,102,112,40,41,59,125,59,10,102,117,110,99,116,105,111,110,32,99,102,115,112,40,41,32,123,103,110,117,112,108,111,116,46,99,102,115,112,40,41,59,125,59,10,10,0,85,110,114,101,99,111,103,110,105,122,101,100,32,98,105,110,97,114,121,32,102,111,114,109,97,116,32,115,112,101,99,105,102,105,99,97,116,105,111,110,0,0,0,0,0,0,0,0,47,47,32,71,110,117,112,108,111,116,32,118,101,114,115,105,111,110,32,37,115,46,37,115,10,0,0,0,0,0,0,0,47,47,32,82,101,105,110,105,116,105,97,108,105,122,101,32,109,111,117,115,101,32,116,114,97,99,107,105,110,103,32,97,110,100,32,122,111,111,109,32,102,111,114,32,116,104,105,115,32,112,97,114,116,105,99,117,108,97,114,32,112,108,111,116,10,105,102,32,40,40,116,121,112,101,111,102,40,103,110,117,112,108,111,116,46,97,99,116,105,118,101,95,112,108,111,116,41,32,61,61,32,34,117,110,100,101,102,105,110,101,100,34,32,124,124,32,103,110,117,112,108,111,116,46,97,99,116,105,118,101,95,112,108,111,116,32,33,61,32,37,115,41,32,32,38,38,32,32,116,121,112,101,111,102,40,103,110,117,112,108,111,116,46,109,111,117,115,101,95,117,112,100,97,116,101,41,32,33,61,32,34,117,110,100,101,102,105,110,101,100,34,41,32,123,10,32,32,103,110,117,112,108,111,116,46,97,99,116,105,118,101,95,112,108,111,116,95,110,97,109,101,32,61,32,34,37,115,34,59,10,32,32,103,110,117,112,108,111,116,46,97,99,116,105,118,101,95,112,108,111,116,32,61,32,37,115,59,10,32,32,99,97,110,118,97,115,46,111,110,109,111,117,115,101,109,111,118,101,32,61,32,103,110,117,112,108,111,116,46,109,111,117,115,101,95,117,112,100,97,116,101,59,10,32,32,99,97,110,118,97,115,46,111,110,109,111,117,115,101,117,112,32,61,32,103,110,117,112,108,111,116,46,122,111,111,109,95,105,110,59,10,32,32,99,97,110,118,97,115,46,111,110,109,111,117,115,101,100,111,119,110,32,61,32,103,110,117,112,108,111,116,46,115,97,118,101,99,108,105,99,107,59,10,32,32,99,97,110,118,97,115,46,111,110,107,101,121,112,114,101,115,115,32,61,32,103,110,117,112,108,111,116,46,100,111,95,104,111,116,107,101,121,59,10,32,32,105,102,32,40,99,97,110,118,97,115,46,97,116,116,97,99,104,69,118,101,110,116,41,32,123,99,97,110,118,97,115,46,97,116,116,97,99,104,69,118,101,110,116,40,39,109,111,117,115,101,111,118,101,114,39,44,32,37,115,41,59,125,10,32,32,101,108,115,101,32,105,102,32,40,99,97,110,118,97,115,46,97,100,100,69,118,101,110,116,76,105,115,116,101,110,101,114,41,32,123,99,97,110,118,97,115,46,97,100,100,69,118,101,110,116,76,105,115,116,101,110,101,114,40,39,109,111,117,115,101,111,118,101,114,39,44,32,37,115,44,32,102,97,108,115,101,41,59,125,32,10,32,32,103,110,117,112,108,111,116,46,122,111,111,109,101,100,32,61,32,102,97,108,115,101,59,10,32,32,103,110,117,112,108,111,116,46,122,111,111,109,95,97,120,105,115,95,119,105,100,116,104,32,61,32,48,59,10,32,32,103,110,117,112,108,111,116,46,122,111,111,109,95,105,110,95,112,114,111,103,114,101,115,115,32,61,32,102,97,108,115,101,59,10,32,32,103,110,117,112,108,111,116,46,112,111,108,97,114,95,109,111,100,101,32,61,32,37,115,59,10,32,32,99,116,120,46,99,108,101,97,114,82,101,99,116,40,48,44,48,44,37,100,44,37,100,41,59,10,125,10,0,37,65,0,0,0,0,0,0,102,117,110,99,116,105,111,110,32,37,115,40,41,32,123,10,99,97,110,118,97,115,32,61,32,100,111,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,66,121,73,100,40,34,37,115,34,41,59,10,99,116,120,32,61,32,99,97,110,118,97,115,46,103,101,116,67,111,110,116,101,120,116,40,34,50,100,34,41,59,10,0,102,111,114,109,97,116,32,116,111,111,32,108,111,110,103,32,100,117,101,32,116,111,32,108,111,110,103,32,100,101,99,105,109,97,108,115,105,103,110,32,115,116,114,105,110,103,0,0,101,120,116,114,97,110,101,111,117,115,32,97,114,103,117,109,101,110,116,115,32,116,111,32,117,110,115,101,116,32,108,97,98,101,108,0,0,0,0,0,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,62,10,118,97,114,32,99,97,110,118,97,115,44,32,99,116,120,59,10,103,110,117,112,108,111,116,46,103,114,105,100,95,108,105,110,101,115,32,61,32,116,114,117,101,59,10,103,110,117,112,108,111,116,46,122,111,111,109,101,100,32,61,32,102,97,108,115,101,59,10,103,110,117,112,108,111,116,46,97,99,116,105,118,101,95,112,108,111,116,95,110,97,109,101,32,61,32,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,34,59,10,10,102,117,110,99,116,105,111,110,32,103,110,117,112,108,111,116,95,99,97,110,118,97,115,40,41,32,123,10,99,97,110,118,97,115,32,61,32,100,111,99,117,109,101,110,116,46,103,101,116,69,108,101,109,101,110,116,66,121,73,100,40,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,34,41,59,10,99,116,120,32,61,32,99,97,110,118,97,115,46,103,101,116,67,111,110,116,101,120,116,40,34,50,100,34,41,59,10,0,0,0,0,0,0,0,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,62,103,110,117,112,108,111,116,46,105,110,105,116,32,61,32,102,117,110,99,116,105,111,110,40,41,32,123,125,59,60,47,115,99,114,105,112,116,62,10,0,0,67,111,110,84,101,88,116,32,119,105,116,104,32,77,101,116,97,70,117,110,32,40,102,111,114,32,80,68,70,32,100,111,99,117,109,101,110,116,115,41,0,0,0,0,0,0,0,0,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,62,32,103,110,117,112,108,111,116,46,104,101,108,112,95,85,82,76,32,61,32,34,37,115,47,99,97,110,118,97,115,95,104,101,108,112,46,104,116,109,108,34,59,32,60,47,115,99,114,105,112,116,62,10,0,0,0,0,118,114,36,97,110,103,101,0,60,115,99,114,105,112,116,32,115,114,99,61,34,37,115,103,110,117,112,108,111,116,95,109,111,117,115,101,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10,0,0,0,0,0,9,116,97,107,105,110,103,32,115,99,97,110,115,32,105,110,32,37,115,32,100,105,114,101,99,116,105,111,110,10,0,0,105,110,118,97,108,105,100,32,99,111,108,111,114,98,111,120,32,111,112,116,105,111,110,0,115,101,116,32,115,116,121,108,101,32,97,114,114,111,119,32,37,100,0,0,0,0,0,0,60,115,99,114,105,112,116,32,115,114,99,61,34,37,115,103,110,117,112,108,111,116,95,100,97,115,104,101,100,108,105,110,101,115,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10,0,0,0,0,0,0,0,107,101,121,95,99,104,97,114,0,0,0,0,0,0,0,0,99,97,110,118,97,115,116,101,120,116,0,0,0,0,0,0,99,97,110,118,97,115,109,97,116,104,0,0,0,0,0,0,70,97,105,108,117,114,101,32,105,110,32,98,105,110,97,114,121,32,116,97,98,108,101,32,105,110,105,116,105,97,108,105,122,97,116,105,111,110,0,0,107,111,105,56,36,117,0,0,60,33,45,45,91,105,102,32,73,69,93,62,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,32,115,114,99,61,34,101,120,99,97,110,118,97,115,46,106,115,34,62,60,47,115,99,114,105,112,116,62,60,33,91,101,110,100,105,102,93,45,45,62,10,60,115,99,114,105,112,116,32,115,114,99,61,34,37,115,37,115,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10,60,115,99,114,105,112,116,32,115,114,99,61,34,37,115,103,110,117,112,108,111,116,95,99,111,109,109,111,110,46,106,115,34,62,60,47,115,99,114,105,112,116,62,10,0,60,109,101,116,97,32,104,116,116,112,45,101,113,117,105,118,61,34,99,111,110,116,101,110,116,45,116,121,112,101,34,32,99,111,110,116,101,110,116,61,34,116,101,120,116,47,104,116,109,108,59,32,99,104,97,114,115,101,116,61,85,84,70,45,56,34,62,10,0,0,0,0,71,110,117,112,108,111,116,32,67,97,110,118,97,115,32,71,114,97,112,104,0,0,0,0,60,33,68,79,67,84,89,80,69,32,72,84,77,76,62,10,60,104,116,109,108,62,10,60,104,101,97,100,62,10,60,116,105,116,108,101,62,37,115,60,47,116,105,116,108,101,62,10,0,0,0,0,0,0,0,0,99,116,120,46,98,101,103,105,110,80,97,116,104,40,41,59,10,0,0,0,0,0,0,0,99,111,110,116,101,120,116,0,77,40,37,117,44,37,117,41,59,10,0,0,0,0,0,0,117,114,36,97,110,103,101,0,114,101,102,36,114,101,115,104,0,0,0,0,0,0,0,0,76,40,37,117,44,37,117,41,59,10,0,0,0,0,0,0,9,116,114,117,101,32,100,101,112,116,104,32,111,114,100,101,114,105,110,103,10,0,0,0,101,120,112,101,99,116,105,110,103,32,115,99,114,101,101,110,32,118,97,108,117,101,32,91,48,32,45,32,49,93,0,0,105,108,108,101,103,97,108,32,112,114,101,102,105,120,0,0,117,110,115,101,116,32,115,116,121,108,101,32,97,114,114,111,119,10,0,0,0,0,0,0,68,84,40,103,110,117,112,108,111,116,46,100,97,115,104,112,97,116,116,101,114,110,51,41,59,10,0,0,0,0,0,0,77,79,85,83,69,95,67,72,65,82,0,0,0,0,0,0,68,84,40,103,110,117,112,108,111,116,46,100,97,115,104,112,97,116,116,101,114,110,37,49,100,41,59,10,0,0,0,0,119,120,116,0,0,0,0,0,114,103,98,40,49,55,49,44,50,49,52,44,48,48,48,41,0,0,0,0,0,0,0,0,37,39,34,32,0,0,0,0,114,103,98,40,50,49,52,44,48,48,48,44,49,50,48,41,0,0,0,0,0,0,0,0,114,103,98,40,50,53,53,44,50,48,52,44,48,48,48,41,0,0,0,0,0,0,0,0,114,103,98,40,49,54,51,44,49,52,53,44,50,53,53,41,0,0,0,0,0,0,0,0,116,111,107,101,110,32,116,97,98,108,101,0,0,0,0,0,37,115,37,115,10,37,115,37,115,10,37,115,37,115,10,37,115,37,115,37,115,37,115,10,37,115,10,0,0,0,0,0,114,103,98,40,50,49,52,44,50,49,52,44,48,54,57,41,0,0,0,0,0,0,0,0,114,103,98,40,48,48,48,44,49,53,51,44,49,54,49,41,0,0,0,0,0,0,0,0,77,101,116,97,80,111,115,116,32,112,108,111,116,116,105,110,103,32,115,116,97,110,100,97,114,100,0,0,0,0,0,0,114,103,98,40,50,53,53,44,49,53,51,44,48,48,48,41,0,0,0,0,0,0,0,0,116,114,36,97,110,103,101,0,122,120,121,0,0,0,0,0,114,103,98,40,48,48,48,44,48,48,48,44,49,52,56,41,0,0,0,0,0,0,0,0,84,79,80,0,0,0,0,0,98,121,0,0,0,0,0,0,116,97,103,32,109,117,115,116,32,98,101,32,115,116,114,105,99,116,108,121,32,112,111,115,105,116,105,118,101,32,40,115,101,101,32,96,104,101,108,112,32,115,101,116,32,115,116,121,108,101,32,108,105,110,101,39,41,0,0,0,0,0,0,0,115,101,116,32,115,116,121,108,101,32,108,105,110,101,32,37,100,32,0,0,0,0,0,0,114,103,98,40,48,50,49,44,49,49,55,44,48,54,57,41,0,0,0,0,0,0,0,0,77,79,85,83,69,95,75,69,89,0,0,0,0,0,0,0,114,103,98,40,48,48,48,44,50,53,53,44,50,53,53,41,0,0,0,0,0,0,0,0,114,103,98,40,49,57,48,44,48,48,48,44,49,57,48,41,0,0,0,0,0,0,0,0,117,115,105,110,103,32,100,101,102,97,117,108,116,32,98,105,110,97,114,121,32,102,111,114,109,97,116,0,0,0,0,0,32,100,101,102,97,117,108,116,10,0,0,0,0,0,0,0,114,103,98,40,48,48,48,44,48,48,48,44,50,50,53,41,0,0,0,0,0,0,0,0,100,117,112,108,105,99,97,116,101,100,32,111,114,32,99,111,110,116,114,97,100,105,99,116,105,110,103,32,97,114,103,117,109,101,110,116,115,32,105,110,32,112,108,111,116,32,111,112,116,105,111,110,115,0,0,0,114,103,98,40,48,48,48,44,49,55,49,44,48,48,48,41,0,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,117,110,105,113,117,101,39,44,32,39,102,114,101,113,117,101,110,99,121,39,44,32,39,99,117,109,117,108,97,116,105,118,101,39,44,32,39,99,110,111,114,109,97,108,39,44,32,39,107,100,101,110,115,105,116,121,39,44,32,39,97,99,115,112,108,105,110,101,115,39,44,32,39,99,115,112,108,105,110,101,115,39,44,32,39,98,101,122,105,101,114,39,32,111,114,32,39,115,98,101,122,105,101,114,39,0,0,0,0,114,103,98,40,50,53,53,44,48,48,48,44,48,48,48,41,0,0,0,0,0,0,0,0,83,72,69,76,76,0,0,0,124,124,0,0,0,0,0,0,114,103,98,40,49,54,48,44,49,54,48,44,49,54,48,41,0,0,0,0,0,0,0,0,109,97,116,114,105,120,32,101,108,101,109,101,110,116,115,0])
+.concat([114,103,98,40,48,48,48,44,48,48,48,44,48,48,48,41,0,0,0,0,0,0,0,0,115,112,108,105,110,101,32,109,97,116,114,105,120,0,0,0,109,112,0,0,0,0,0,0,114,103,98,40,50,53,53,44,50,53,53,44,50,53,53,41,0,0,0,0,0,0,0,0,114,114,36,97,110,103,101,0,117,115,105,110,103,32,100,101,102,97,117,108,116,32,98,105,110,97,114,121,32,114,101,99,111,114,100,47,97,114,114,97,121,32,115,116,114,117,99,116,117,114,101,0,0,0,0,0,72,111,109,101,0,0,0,0,34,41,59,10,0,0,0,0,83,85,82,70,65,67,69,0,105,110,118,97,108,105,100,32,112,109,51,100,32,111,112,116,105,111,110,0,0,0,0,0,84,40,37,100,44,37,100,44,37,46,49,102,44,34,37,115,34,44,34,0,0,0,0,0,117,110,115,101,116,32,115,116,121,108,101,32,108,105,110,101,10,0,0,0,0,0,0,0,102,105,108,108,101,100,95,112,111,108,121,103,111,110,51,100,32,99,111,114,110,101,114,115,0,0,0,0,0,0,0,0,32,32,32,112,117,115,104,101,100,32,116,101,114,109,105,110,97,108,32,37,115,32,37,115,10,0,0,0,0,0,0,0,77,79,85,83,69,95,66,85,84,84,79,78,0,0,0,0,84,82,40,37,100,44,37,100,44,37,100,44,37,46,49,102,44,34,37,115,34,44,34,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,110,111,110,45,83,84,82,73,78,71,32,97,114,103,117,109,101,110,116,0,0,0,0,82,105,103,104,116,0,0,0,108,105,116,116,108,101,0,0,80,116,40,37,100,44,37,100,44,37,100,44,37,46,49,102,41,59,10,0,0,0,0,0,68,111,116,40,37,100,44,37,100,41,59,10,0,0,0,0,82,40,37,100,44,37,100,44,37,100,44,37,100,41,59,10,0,0,0,0,0,0,0,0,99,116,120,46,108,105,110,101,87,105,100,116,104,32,61,32,37,103,59,10,0,0,0,0,99,116,120,46,115,116,114,111,107,101,83,116,121,108,101,32,61,32,34,37,115,34,59,10,0,0,0,0,0,0,0,0,77,101,116,97,102,111,110,116,32,112,108,111,116,116,105,110,103,32,115,116,97,110,100,97,114,100,0,0,0,0,0,0,114,103,98,40,37,48,51,100,44,37,48,51,100,44,37,48,51,100,41,0,0,0,0,0,99,98,114,36,97,110,103,101,0,0,0,0,0,0,0,0,37,115,37,99,0,0,0,0,66,79,84,84,79,77,0,0,101,120,112,101,99,116,105,110,103,32,39,109,101,97,110,39,44,32,39,103,101,111,109,101,97,110,39,44,32,39,109,101,100,105,97,110,39,44,32,39,109,105,110,39,44,32,39,109,97,120,39,44,32,39,99,49,39,44,32,39,99,50,39,44,32,39,99,51,39,32,111,114,32,39,99,52,39,0,0,0,117,115,101,114,115,116,121,108,101,115,0,0,0,0,0,0,114,103,98,40,37,51,100,44,37,51,100,44,37,51,100,41,37,99,0,0,0,0,0,0,114,103,98,97,40,50,53,53,44,50,53,53,44,50,53,53,44,48,46,48,48,41,0,0,114,103,98,97,40,37,49,49,46,49,49,115,44,37,52,46,50,102,41,37,99,0,0,0,68,117,112,108,105,99,97,116,101,100,32,111,114,32,99,111,110,116,114,97,100,105,99,116,105,110,103,32,97,114,103,117,109,101,110,116,115,32,105,110,32,100,97,116,97,102,105,108,101,32,111,112,116,105,111,110,115,0,0,0,0,0,0,0,37,77,0,0,0,0,0,0,99,102,115,112,40,41,59,10,0,0,0,0,0,0,0,0,80,108,111,116,32,116,121,112,101,32,105,115,32,110,101,105,116,104,101,114,32,102,117,110,99,116,105,111,110,32,110,111,114,32,100,97,116,97,0,0,99,102,112,40,41,59,10,0,76,40,37,100,44,32,37,100,41,59,10,0,0,0,0,0,98,112,40,37,100,44,32,37,100,41,59,10,0,0,0,0,99,116,120,46,102,105,108,108,83,116,121,108,101,32,61,32,34,37,115,34,59,10,0,0,109,102,0,0,0,0,0,0,68,84,40,103,110,117,112,108,111,116,46,115,111,108,105,100,41,59,10,0,0,0,0,0,122,114,36,97,110,103,101,0,109,0,0,0,0,0,0,0,84,104,105,115,32,99,111,112,121,32,111,102,32,103,110,117,112,108,111,116,32,99,97,110,110,111,116,32,114,101,97,100,32,112,110,103,47,103,105,102,47,106,112,101,103,32,105,109,97,103,101,115,0,0,0,0,44,32,116,104,101,110,32,0,99,52,0,0,0,0,0,0,115,101,116,32,115,116,121,108,101,32,105,110,99,114,101,109,101,110,116,32,37,115,10,0,77,126,60,62,37,87,61,38,64,0,0,0,0,0,0,0,97,120,105,115,32,114,97,110,103,101,115,32,109,117,115,116,32,98,101,32,97,98,111,118,101,32,48,32,102,111,114,32,108,111,103,32,115,99,97,108,101,33,0,0,0,0,0,0,65,99,101,70,86,63,97,98,100,69,103,104,110,111,112,113,117,0,0,0,0,0,0,0,32,74,84,118,94,95,34,42,121,107,76,115,120,122,0,0,109,105,115,115,105,110,103,32,102,111,114,109,97,116,32,115,116,114,105,110,103,0,0,0,40,41,91,93,123,125,92,0,102,116,114,0,0,0,0,0,106,96,39,44,59,58,33,46,0,0,0,0,0,0,0,0,105,73,108,124,0,0,0,0,99,116,120,46,115,116,114,111,107,101,40,41,59,10,0,0,76,97,84,101,88,32,116,101,120,100,114,97,119,32,101,110,118,105,114,111,110,109,101,110,116,0,0,0,0,0,0,0,99,116,120,46,108,105,110,101,87,105,100,116,104,32,61,32,115,97,118,101,87,105,100,116,104,59,10,125,32,47,47,32,103,114,105,100,95,108,105,110,101,115,10,0,0,0,0,0,121,50,114,36,97,110,103,101,0,0,0,0,0,0,0,0,105,102,32,40,103,110,117,112,108,111,116,46,103,114,105,100,95,108,105,110,101,115,41,32,123,10,118,97,114,32,115,97,118,101,87,105,100,116,104,32,61,32,99,116,120,46,108,105,110,101,87,105,100,116,104,59,10,99,116,120,46,108,105,110,101,87,105,100,116,104,32,61,32,99,116,120,46,108,105,110,101,87,105,100,116,104,32,42,32,48,46,53,59,10,0,0,9,112,109,51,100,32,112,108,111,116,116,101,100,32,97,116,32,0,0,0,0,0,0,0,99,51,0,0,0,0,0,0,32,115,105,122,101,32,37,115,32,37,46,51,102,44,37,46,51,102,44,37,46,51,102,0,125,32,47,47,32,69,110,100,32,37,115,95,112,108,111,116,95,37,100,32,10,0,0,0,115,101,116,32,97,117,116,111,115,99,97,108,101,32,107,101,101,112,102,105,120,0,0,0,105,102,32,40,116,121,112,101,111,102,40,103,110,117,112,108,111,116,46,104,105,100,101,95,37,115,95,112,108,111,116,95,37,100,41,32,61,61,32,34,117,110,100,101,102,105,110,101,100,34,124,124,32,33,103,110,117,112,108,111,116,46,104,105,100,101,95,37,115,95,112,108,111,116,95,37,100,41,32,123,10,0,0,0,0,0,0,0,103,112,0,0,0,0,0,0,102,111,114,109,36,97,116,0,99,116,120,46,99,108,111,115,101,80,97,116,104,40,41,59,10,0,0,0,0,0,0,0,119,105,100,36,116,104,0,0,108,105,36,110,101,119,105,100,116,104,0,0,0,0,0,0,110,111,114,36,111,116,97,116,101,0,0,0,0,0,0,0,115,113,114,116,40,115,113,114,116,40,120,41,41,0,0,0,119,105,110,36,119,111,114,100,54,0,0,0,0,0,0,0,116,101,120,100,114,97,119,0,110,111,102,36,111,110,116,108,105,115,116,0,0,0,0,0,121,114,36,97,110,103,101,0,108,97,36,110,100,115,99,97,112,101,0,0,0,0,0,0,101,120,112,108,105,99,105,116,32,40,100,114,97,119,32,112,109,51,100,32,115,117,114,102,97,99,101,32,97,99,99,111,114,100,105,110,103,32,116,111,32,115,116,121,108,101,41,0,99,50,0,0,0,0,0,0,99,104,97,114,97,99,116,101,114,0,0,0,0,0,0,0,32,120,37,48,50,120,37,48,50,120,37,48,50,120,0,0,96,98,117,105,108,116,105,110,45,97,117,116,111,115,99,97,108,101,96,32,40,115,101,116,32,97,117,116,111,115,99,97,108,101,32,107,101,101,112,102,105,120,59,32,114,101,112,108,111,116,41,0,0,0,0,0,110,111,102,111,110,116,108,105,115,116,0,0,0,0,0,0,37,115,32,37,115,32,37,115,32,37,115,32,37,115,32,119,105,100,116,104,32,37,100,32,108,105,110,101,119,105,100,116,104,32,37,100,32,34,37,115,34,32,37,100,0,0,0,0,79,112,116,105,111,110,115,32,97,114,101,32,100,101,102,97,117,108,116,44,32,115,119,97,112,32,40,115,119,97,98,41,44,32,108,105,116,116,108,101,44,32,98,105,103,44,32,109,105,100,100,108,101,32,40,112,100,112,41,0,0,0,0,0,37,115,44,37,100,0,0,0,105,110,118,97,108,105,100,32,99,111,108,111,114,32,115,112,101,99,44,32,109,117,115,116,32,98,101,32,120,82,82,71,71,66,66,0,0,0,0,0,120,37,50,104,120,37,50,104,120,37,50,104,120,0,0,0,119,105,100,116,104,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,0,0,0,0,108,105,110,101,119,105,100,116,104,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,76,97,84,101,88,32,112,105,99,116,117,114,101,32,101,110,118,105,114,111,110,109,101,110,116,32,119,105,116,104,32,80,83,84,114,105,99,107,115,32,109,97,99,114,111,115,0,0,99,103,109,32,112,111,108,121,108,105,110,101,115,0,0,0,120,50,114,36,97,110,103,101,0,0,0,0,0,0,0,0,37,46,51,49,115,44,37,100,0,0,0,0,0,0,0,0,105,109,112,108,105,99,105,116,32,40,112,109,51,100,32,100,114,97,119,32,102,111,114,32,97,108,108,32,115,117,114,102,97,99,101,115,41,0,0,0,99,49,0,0,0,0,0,0,115,99,114,101,101,110,0,0,80,73,67,84,85,82,69,49,0,0,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,116,111,103,103,108,101,45,98,111,114,100,101,114,96,0,67,71,77,32,102,111,110,116,32,108,105,115,116,0,0,0,71,110,117,112,108,111,116,32,118,101,114,115,105,111,110,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,44,32,67,111,109,112,117,116,101,114,32,71,114,97,112,104,105,99,115,32,77,101,116,97,102,105,108,101,32,118,101,114,115,105,111,110,32,49,32,112,101,114,32,77,73,76,45,68,45,50,56,48,48,51,65,47,66,65,83,73,67,45,49,46,37,100,0,0,0,0,0,0,112,100,112,0,0,0,0,0,84,105,109,101,115,32,66,111,108,100,32,79,98,108,105,113,117,101,0,0,0,0,0,0,84,105,109,101,115,32,79,98,108,105,113,117,101,0,0,0,67,111,117,114,105,101,114,32,66,111,108,100,32,73,116,97,108,105,99,0,0,0,0,0,67,111,117,114,105,101,114,32,73,116,97,108,105,99,0,0,72,101,108,118,101,116,105,99,97,32,66,111,108,100,32,73,116,97,108,105,99,0,0,0,112,115,116,114,105,99,107,115,0,0,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,32,73,116,97,108,105,99,0,0,0,0,0,0,0,0,120,114,36,97,110,103,101,0,49,53,0,0,0,0,0,0,9,112,109,51,100,32,115,116,121,108,101,32,105,115,32,37,115,10,0,0,0,0,0,0,103,114,97,112,104,0,0,0,83,99,114,105,112,116,0,0,96,98,117,105,108,116,105,110,45,114,101,112,108,111,116,96,0,0,0,0,0,0,0,0,90,97,112,102,68,105,110,103,98,97,116,115,0,0,0,0,72,101,114,115,104,101,121,47,83,121,109,98,111,108,95,77,97,116,104,0,0,0,0,0,109,105,100,36,100,108,101,0,72,101,114,115,104,101,121,47,83,121,109,98,111,108,95,83,101,116,95,50,0,0,0,0,72,101,114,115,104,101,121,47,83,121,109,98,111,108,95,83,101,116,95,49,0,0,0,0,76,111,99,97,108,101,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,0,0,0,72,101,114,115,104,101,121,47,71,111,116,104,105,99,95,73,116,97,108,105,97,110,0,0,111,101,69,102,70,103,71,0,99,121,99,108,101,0,0,0,72,101,114,115,104,101,121,47,71,111,116,104,105,99,95,69,110,103,108,105,115,104,0,0,72,101,114,115,104,101,121,47,71,111,116,104,105,99,95,71,101,114,109,97,110,0,0,0,84,80,73,67,32,45,45,32,76,97,84,101,88,32,112,105,99,116,117,114,101,32,101,110,118,105,114,111,110,109,101,110,116,32,119,105,116,104,32,116,112,105,99,32,92,115,112,101,99,105,97,108,115,0,0,0,72,101,114,115,104,101,121,47,84,114,105,112,108,101,120,95,73,116,97,108,105,99,0,0,110,111,99,98,109,116,105,36,99,115,0,0,0,0,0,0,72,101,114,115,104,101,121,47,84,114,105,112,108,101,120,95,82,111,109,97,110,0,0,0,9,112,111,105,110,116,115,105,122,101,32,105,115,32,37,103,10,0,0,0,0,0,0,0,115,101,99,111,110,100,0,0,72,101,114,115,104,101,121,47,68,117,112,108,101,120,95,82,111,109,97,110,0,0,0,0,117,110,115,101,116,32,103,114,105,100,0,0,0,0,0,0,72,101,114,115,104,101,121,47,67,111,109,112,108,101,120,95,67,121,114,105,108,108,105,99,0,0,0,0,0,0,0,0,72,101,114,115,104,101,121,47,67,111,109,112,108,101,120,95,73,116,97,108,105,99,0,0,108,105,116,36,116,108,101,0,107,111,105,56,36,114,0,0,72,101,114,115,104,101,121,47,67,111,109,112,108,101,120,95,83,99,114,105,112,116,0,0,103,116,0,0,0,0,0,0,72,101,114,115,104,101,121,47,67,111,109,112,108,101,120,95,71,114,101,101,107,0,0,0,72,101,114,115,104,101,121,47,67,111,109,112,108,101,120,95,82,111,109,97,110,0,0,0,72,101,114,115,104,101,121,47,83,105,109,112,108,101,120,95,83,99,114,105,112,116,0,0,72,101,114,115,104,101,121,47,83,105,109,112,108,101,120,95,71,114,101,101,107,0,0,0,116,112,105,99,0,0,0,0,72,101,114,115,104,101,121,47,83,105,109,112,108,101,120,95,82,111,109,97,110,0,0,0,99,98,109,116,105,36,99,115,0,0,0,0,0,0,0,0,113,36,117,105,116,0,0,0,72,101,114,115,104,101,121,47,67,97,114,116,111,103,114,97,112,104,105,99,95,71,114,101,101,107,0,0,0,0,0,0,9,112,111,105,110,116,105,110,116,101,114,118,97,108,98,111,120,32,105,115,32,37,103,10,0,0,0,0,0,0,0,0,71,80,86,65,76,95,0,0,102,105,114,115,116,0,0,0,72,101,114,115,104,101,121,47,67,97,114,116,111,103,114,97,112,104,105,99,95,82,111,109,97,110,0,0,0,0,0,0,67,71,77,32,99,111,108,111,114,32,116,97,98,108,101,0,48,32,60,61,32,108,101,110,103,116,104,0,0,0,0,0,115,119,97,98,0,0,0,0,40,48,32,60,61,32,99,103,109,95,105,100,41,32,38,38,32,40,99,103,109,95,105,100,32,60,32,49,50,56,41,0,40,48,32,60,61,32,99,108,97,115,115,41,32,38,38,40,99,108,97,115,115,32,60,49,54,41,0,0,0,0,0,0,118,97,108,117,101,32,60,61,32,51,50,55,54,55,0,0,43,68,65,84,65,83,84,82,73,78,71,83,32,32,43,72,73,83,84,79,71,82,65,77,83,32,32,43,79,66,74,69,67,84,83,32,32,43,83,84,82,73,78,71,86,65,82,83,32,32,43,77,65,67,82,79,83,32,32,43,73,77,65,71,69,32,32,43,85,83,69,82,95,76,73,78,69,84,89,80,69,83,32,43,83,84,65,84,83,32,0,0,0,0,0,0,45,51,50,55,54,56,32,60,61,32,118,97,108,117,101,0,69,69,80,73,67,32,45,45,32,101,120,116,101,110,100,101,100,32,76,97,84,101,88,32,112,105,99,116,117,114,101,32,101,110,118,105,114,111,110,109,101,110,116,0,0,0,0,0,99,111,108,111,117,114,36,0,110,111,122,109,116,105,36,99,115,0,0,0,0,0,0,0,99,111,108,111,114,36,0,0,9,104,111,119,101,118,101,114,32,76,67,95,67,84,89,80,69,32,105,110,32,99,117,114,114,101,110,116,32,108,111,99,97,108,101,32,105,115,32,37,115,10,0,0,0,0,0,0,103,101,111,109,101,97,110,0,37,115,32,34,37,115,34,32,37,100,44,37,48,46,49,102,44,37,48,46,49,102,44,37,48,46,49,102,0,0,0,0,96,98,117,105,108,116,105,110,45,116,111,103,103,108,101,45,103,114,105,100,96,0,0,0,83,119,105,116,122,101,114,108,97,110,100,76,105,103,104,116,0,0,0,0,0,0,0,0,37,37,33,80,83,45,65,100,111,98,101,45,50,46,48,32,69,80,83,70,45,49,46,50,10,37,37,37,37,66,111,117,110,100,105,110,103,66,111,120,58,32,37,100,32,37,100,32,37,100,32,37,100,10,37,37,37,37,84,101,109,112,108,97,116,101,66,111,120,58,32,37,100,32,37,100,32,37,100,32,37,100,10,37,37,37,37,69,110,100,67,111,109,109,101,110,116,115,10,37,37,37,37,69,110,100,80,114,111,108,111,103,10,37,37,37,37,66,101,103,105,110,83,101,116,117,112,10,37,37,37,37,69,110,100,83,101,116,117,112,10,0,0,0,115,119,97,112,0,0,0,0,32,37,115,32,112,97,116,116,101,114,110,32,37,100,32,0,37,48,46,50,102,32,37,48,46,50,102,32,109,10,0,0,110,111,115,117,114,36,102,97,99,101,0,0,0,0,0,0,83,10,37,46,50,102,32,37,46,50,102,32,109,10,0,0,115,36,109,111,111,116,104,0,37,46,50,102,32,37,46,50,102,32,108,10,0,0,0,0,110,111,32,72,79,77,69,32,102,111,117,110,100,0,0,0,101,120,116,101,110,100,95,97,116,0,0,0,0,0,0,0,91,50,32,50,32,50,32,50,32,50,32,50,32,50,32,52,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,0,48,46,53,32,48,46,53,32,48,46,53,32,48,32,75,10,0,0,0,0,0,0,0,0,102,117,110,99,116,105,111,110,32,100,101,102,105,110,105,116,105,111,110,32,101,120,112,101,99,116,101,100,0,0,0,0,101,101,112,105,99,0,0,0,91,50,32,50,32,50,32,50,32,50,32,52,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,0,0,0,0,0,122,109,116,105,36,99,115,0,73,110,115,101,114,116,0,0,48,32,48,46,55,32,49,32,48,32,75,10,0,0,0,0,9,110,111,109,105,110,97,108,32,99,104,97,114,97,99,116,101,114,32,101,110,99,111,100,105,110,103,32,105,115,32,37,115,10,0,0,0,0,0,0,91,50,32,50,32,50,32,52,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,0,102,111,110,116,112,97,116,104,95,102,117,108,108,110,97,109,101,58,32,78,111,32,80,105,112,101,32,97,108,108,111,119,101,100,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,104,101,108,112,96,0,0,91,52,32,51,32,49,32,51,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,110,111,110,45,73,78,84,71,82,32,97,114,103,117,109,101,110,116,0,0,0,0,0,48,32,48,32,49,32,48,32,75,10,0,0,0,0,0,0,91,53,32,50,32,49,32,50,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,0,49,32,48,32,48,32,48,32,75,10,0,0,0,0,0,0,37,102,32,119,10,0,0,0,91,49,32,49,46,53,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,0,0,0,48,32,49,32,48,32,48,32,75,10,0,0,0,0,0,0,112,108,97,105,110,32,84,101,88,32,119,105,116,104,32,80,111,115,116,83,99,114,105,112,116,32,92,115,112,101,99,105,97,108,115,0,0,0,0,0,91,50,32,51,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,0,0,0,0,0,110,111,121,50,109,116,105,36,99,115,0,0,0,0,0,0,48,32,49,32,49,32,48,32,75,10,0,0,0,0,0,0,9,100,101,103,114,101,101,32,115,105,103,110,32,102,111,114,32,111,117,116,112,117,116,32,105,115,32,37,115,32,10,0,68,101,112,114,101,99,97,116,101,100,32,115,121,110,116,97,120,32,45,45,45,32,105,103,110,111,114,101,100,0,0,0,91,52,32,50,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,0,0,0,0,0,67,97,110,110,111,116,32,116,111,103,103,108,101,32,108,111,103,32,115,99,97,108,101,32,102,111,114,32,118,111,108,97,116,105,108,101,32,100,97,116,97,0,0,0,0,0,0,0,49,32,49,32,48,32,48,32,75,10,0,0,0,0,0,0,91,93,32,48,32,100,10,50,32,106,10,48,32,71,10,0,101,110,100,36,105,97,110,0,49,32,48,32,49,32,48,32,75,10,0,0,0,0,0,0,91,49,32,50,93,32,48,32,100,10,48,32,106,10,48,32,71,10,0,0,0,0,0,0,104,105,100,100,101,110,32,115,111,114,116,32,101,100,103,101,115,0,0,0,0,0,0,0,117,110,100,101,102,105,110,101,100,32,102,117,110,99,116,105,111,110,58,32,37,115,0,0,91,93,32,48,32,100,10,48,32,106,10,48,32,71,10,0,48,32,48,32,48,32,49,32,75,10,0,0,0,0,0,0,37,46,50,102,32,119,10,0,112,115,116,101,120,0,0,0,41,116,10,84,10,0,0,0,121,50,109,116,105,36,99,115,0,0,0,0,0,0,0,0,91,48,32,49,32,45,49,32,48,32,37,46,50,102,32,37,46,50,102,93,101,10,48,32,103,10,0,0,0,0,0,0,9,100,101,99,105,109,97,108,115,105,103,110,32,102,111,114,32,111,117,116,112,117,116,32,104,97,115,32,100,101,102,97,117,108,116,32,118,97,108,117,101,32,40,110,111,114,109,97,108,108,121,32,39,46,39,41,10,0,0,0,0,0,0,0,91,49,32,48,32,48,32,49,32,37,46,50,102,32,37,46,50,102,93,101,10,48,32,103,10,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,102,108,117,115,104,32,39,98,101,103,105,110,39,44,32,39,99,101,110,116,101,114,39,32,111,114,32,39,101,110,100,39,0,0,0,0,0,0,32,37,115,32,37,115,32,37,115,0,0,0,0,0,0,0,82,97,115,116,101,114,65,120,101,115,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,116,111,103,103,108,101,45,108,111,103,96,32,121,32,108,111,103,115,99,97,108,101,32,102,111,114,32,112,108,111,116,115,44,32,122,32,97,110,100,32,99,98,32,102,111,114,32,115,112,108,111,116,115,0,0,47,95,37,115,32,37,100,32,37,100,32,48,32,50,32,122,10,0,0,0,0,0,0,0,47,95,37,115,32,37,100,32,37,100,32,48,32,49,32,122,10,0,0,0,0,0,0,0,115,107,105,112,0,0,0,0,86,105,115,105,98,108,101,32,112,105,120,101,108,32,103,114,105,100,32,104,97,115,32,97,32,115,99,97,110,32,108,105,110,101,32,115,104,111,114,116,101,114,32,116,104,97,110,32,112,114,101,118,105,111,117,115,32,115,99,97,110,32,108,105,110,101,115,46,0,0,0,0,47,95,37,115,32,37,100,32,37,100,32,48,32,48,32,122,10,0,0,0,0,0,0,0,83,10,0,0,0,0,0,0,110,111,101,36,110,104,97,110,99,101,100,0,0,0,0,0,110,111,102,36,101,101,100,0,102,36,101,101,100,0,0,0,76,97,84,101,88,32,112,105,99,116,117,114,101,32,101,110,118,105,114,111,110,109,101,110,116,32,119,105,116,104,32,80,111,115,116,83,99,114,105,112,116,32,92,115,112,101,99,105,97,108,115,0,0,0,0,0,37,115,102,101,101,100,32,37,115,32,115,105,122,101,32,37,100,44,32,37,100,0,0,0,110,111,121,109,116,105,36,99,115,0,0,0,0,0,0,0,100,117,109,98,32,116,101,114,109,105,110,97,108,0,0,0,9,100,101,99,105,109,97,108,115,105,103,110,32,102,111,114,32,111,117,116,112,117,116,32,105,115,32,37,115,32,10,0,101,36,110,100,0,0,0,0,32,32,48,10,69,78,68,83,69,67,10,32,32,48,10,69,79,70,10,0,0,0,0,0,115,101,116,32,108,111,103,32,121,50,0,0,0,0,0,0,55,0,0,0,0,0,0,0,32,32,48,10,69,78,68,84,65,66,10,48,10,69,78,68,83,69,67,10,32,32,48,10,83,69,67,84,73,79,78,10,32,32,50,10,66,76,79,67,75,83,10,32,32,48,10,69,78,68,83,69,67,10,32,32,48,10,83,69,67,84,73,79,78,10,32,32,50,10,69,78,84,73,84,73,69,83,10,0,75,101,121,32,119,111,114,100,32,96,112,101,114,112,101,110,100,105,99,117,108,97,114,96,32,105,115,32,110,111,116,32,97,108,108,111,119,101,100,32,119,105,116,104,32,96,112,108,111,116,96,32,99,111,109,109,97,110,100,0,0,0,0,0,32,32,48,10,76,65,89,69,82,10,32,32,50,10,37,115,10,32,55,48,10,32,32,32,54,52,10,54,50,10,32,32,32,37,115,10,32,32,54,10,37,115,10,0,0,0,0,0,32,32,48,10,84,65,66,76,69,10,32,32,50,10,76,65,89,69,82,10,32,55,48,10,32,32,32,37,45,100,10,0,32,32,48,10,80,79,76,89,76,73,78,69,10,32,32,56,10,37,115,10,32,54,54,10,32,32,32,49,10,32,32,54,10,37,115,10,32,32,48,10,86,69,82,84,69,88,10,32,32,56,10,37,115,10,32,32,54,10,37,115,10,32,49,48,10,37,45,54,46,51,102,10,32,50,48,10,37,45,54,46,51,102,10,32,51,48,10,48,46,48,48,48,10,0,0,0,68,65,83,72,68,79,84,0,115,113,114,116,32,115,113,114,116,0,0,0,0,0,0,0,68,79,84,0,0,0,0,0,112,115,108,97,116,101,120,0,80,72,65,78,84,79,77,0,121,109,116,105,36,99,115,0,67,69,78,84,69,82,0,0,9,100,101,99,105,109,97,108,115,105,103,110,32,102,111,114,32,105,110,112,117,116,32,105,115,32,32,37,115,32,10,0,98,36,101,103,105,110,0,0,115,101,116,32,97,114,114,111,119,32,37,100,32,102,114,111,109,32,0,0,0,0,0,0,72,73,68,68,69,78,0,0,117,110,115,101,116,32,108,111,103,32,121,50,0,0,0,0,68,65,83,72,69,68,0,0,67,79,78,84,73,78,85,79,85,83,0,0,0,0,0,0,112,101,114,112,36,101,110,100,105,99,117,108,97,114,0,0,32,32,48,10,86,69,82,84,69,88,10,32,32,56,10,37,115,10,32,32,54,10,37,115,10,32,32,49,48,10,37,45,54,46,51,102,10,32,32,50,48,10,37,45,54,46,51,102,10,32,32,51,48,10,48,46,48,48,48,10,0,0,0,0,53,0,0,0,0,0,0,0,52,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,76,97,84,101,88,32,112,105,99,116,117,114,101,32,101,110,118,105,114,111,110,109,101,110,116,32,117,115,105,110,103,32,103,114,97,112,104,105,99,120,32,112,97,99,107,97,103,101,0,0,0,0,0,0,0,0,49,0,0,0,0,0,0,0,110,111,120,50,109,116,105,36,99,115,0,0,0,0,0,0,83,84,65,78,68,65,82,68,0,0,0,0,0,0,0,0,78,111,32,102,117,114,116,104,101,114,32,111,112,116,105,111,110,115,32,97,108,108,111,119,101,100,32,97,102,116,101,114,32,39,100,101,102,97,117,108,116,115,39,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,115,116,101,112,32,118,97,108,117,101,115,32,105,44,106,0,0,0,0,0,0,0,117,110,115,101,116,32,97,114,114,111,119,10,0,0,0,0,32,55,50,10,37,100,10,32,49,49,10,37,45,54,46,51,102,10,32,50,49,10,37,45,54,46,51,102,10,32,51,49,10,48,46,48,48,48,10,0,115,101,116,32,108,111,103,32,120,50,0,0,0,0,0,0,32,49,48,10,37,45,54,46,51,102,10,32,50,48,10,37,45,54,46,51,102,10,32,51,48,10,48,46,48,48,48,10,32,52,48,10,37,45,54,46,51,102,10,32,32,49,10,37,115,10,32,53,48,10,37,45,54,46,51,102,10,32,32,55,10,37,115,10,0,0,0,0,32,32,48,10,84,69,88,84,10,32,32,56,10,37,115,10,0,0,0,0,0,0,0,0,32,32,48,10,83,69,81,69,78,68,10,0,0,0,0,0,38,35,120,37,50,46,50,120,59,0,0,0,0,0,0,0,123,125,94,95,64,38,126,0,32,98,97,99,107,103,114,111,117,110,100,32,34,35,37,48,54,120,34,0,0,0,0,0,32,100,97,115,104,108,101,110,103,116,104,32,37,46,49,102,0,0,0,0,0,0,0,0,101,112,115,108,97,116,101,120,0,0,0,0,0,0,0,0,32,108,119,32,37,46,49,102,0,0,0,0,0,0,0,0,120,50,109,116,105,36,99,115,0,0,0,0,0,0,0,0,114,111,116,36,97,116,105,111,110,0,0,0,0,0,0,0,32,115,105,122,101,32,37,100,44,37,100,32,0,0,0,0,9,108,111,103,45,102,105,108,101,32,102,111,114,32,102,105,116,115,32,105,115,32,117,110,99,104,97,110,103,101,100,32,102,114,111,109,32,116,104,101,32,101,110,118,105,114,111,110,109,101,110,116,32,100,101,102,97,117,108,116,32,111,102,10,9,39,37,115,39,10,0,0,32,112,111,105,110,116,0,0,32,102,111,110,116,115,99,97,108,101,32,37,46,49,102,0,117,110,115,101,116,32,108,111,103,32,120,50,0,0,0,0,32,101,110,104,97,110,99,101,100,32,0,0,0,0,0,0,37,115,32,37,115,32,37,115,32,102,111,110,116,32,34,37,115,44,37,103,34,0,0,0,99,111,110,116,111,117,114,32,112,111,108,121,103,111,110,0,37,103,0,0,0,0,0,0,110,111,112,114,111,36,112,111,114,116,105,111,110,97,108,0,98,97,99,107,36,103,114,111,117,110,100,0,0,0,0,0,66,97,100,32,102,111,114,109,97,116,32,99,104,97,114,97,99,116,101,114,0,0,0,0,100,101,36,102,97,117,108,116,0,0,0,0,0,0,0,0,101,120,116,114,97,110,101,111,117,115,32,97,114,103,117,109,101,110,116,115,32,116,111,32,117,110,115,101,116,32,114,101,99,116,97,110,103,108,101,0,116,101,114,109,111,112,116,36,105,111,110,0,0,0,0,0,101,109,102,95,109,111,118,101,58,32,40,37,100,44,37,100,41,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,76,97,84,101,88,32,112,105,99,116,117,114,101,32,101,110,118,105,114,111,110,109,101,110,116,32,119,105,116,104,32,101,109,84,101,88,32,115,112,101,99,105,97,108,115,0,0,0,110,111,120,109,116,105,36,99,115,0,0,0,0,0,0,0,101,109,102,95,115,111,108,105,100,95,118,101,99,116,111,114,58,32,40,37,100,44,37,100,41,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,9,108,111,103,45,102,105,108,101,32,102,111,114,32,102,105,116,115,32,105,115,32,119,97,115,32,115,101,116,32,98,121,32,116,104,101,32,117,115,101,114,32,116,111,32,98,101,32,10,9,39,37,115,39,10,0,10,9,100,117,109,109,121,32,118,97,114,105,97,98,108,101,32,105,115,32,116,32,102,111,114,32,99,117,114,118,101,115,10,0,0,0,0,0,0,0,105,99,111,110,118,32,102,97,105,108,101,100,0,0,0,0,115,101,116,32,108,111,103,32,121,0,0,0,0,0,0,0,105,99,111,110,118,95,111,112,101,110,32,102,97,105,108,101,100,0,0,0,0,0,0,0,85,84,70,45,49,54,76,69,0,0,0,0,0,0,0,0,99,112,52,36,51,55,0,0,83,104,105,102,116,95,74,73,83,0,0,0,0,0,0,0,32,34,37,115,34,10,0,0,85,84,70,45,56,0,0,0,105,99,111,110,118,32,115,116,114,105,110,103,0,0,0,0,32,83,116,114,105,107,101,79,117,116,0,0,0,0,0,0,32,83,116,114,105,107,101,111,117,116,0,0,0,0,0,0,101,109,116,101,120,0,0,0,32,115,116,114,105,107,101,111,117,116,0,0,0,0,0,0,120,109,116,105,36,99,115,0,112,119,100,0,0,0,0,0,32,85,110,100,101,114,108,105,110,101,0,0,0,0,0,0,32,110,111,116,0,0,0,0,117,110,114,101,99,111,103,110,105,122,101,100,32,111,98,106,101,99,116,32,116,121,112,101,0,0,0,0,0,0,0,0,32,117,110,100,101,114,108,105,110,101,0,0,0,0,0,0,117,110,115,101,116,32,108,111,103,32,121,0,0,0,0,0,101,109,102,95,100,97,115,104,101,100,95,118,101,99,116,111,114,58,32,40,37,100,44,37,100,41,32,111,117,116,32,111,102,32,114,97,110,103,101,0,32,105,46,44,59,58,124,33,39,0,0,0,0,0,0,0,111,114,105,103,36,105,110,0,109,119,60,62,0,0,0,0,32,105,106,108,46,44,59,58,124,33,40,41,91,93,73,45,39,0,0,0,0,0,0,0,118,36,101,114,115,105,111,110,0,0,0,0,0,0,0,0,43,85,83,69,95,77,79,85,83,69,32,32,0,0,0,0,116,101,120,116,115,36,112,101,99,105,97,108,0,0,0,0,116,101,120,116,114,36,105,103,105,100,0,0,0,0,0,0,76,97,84,101,88,32,112,105,99,116,117,114,101,32,101,110,118,105,114,111,110,109,101,110,116,0,0,0,0,0,0,0,116,101,120,116,110,36,111,114,109,97,108,0,0,0,0,0,110,111,99,98,100,116,105,36,99,115,0,0,0,0,0,0,114,116,0,0,0,0,0,0,116,101,120,116,104,36,105,100,100,101,110,0,0,0,0,0,9,102,105,116,32,119,105,108,108,37,115,32,112,108,97,99,101,32,112,97,114,97,109,101,116,101,114,32,101,114,114,111,114,115,32,105,110,32,118,97,114,105,97,98,108,101,115,10,0,0,0,0,0,0,0,0,117,110,107,110,111,119,110,32,111,98,106,101,99,116,0,0,116,36,104,105,99,107,110,101,115,115,0,0,0,0,0,0,115,101,116,32,108,111,103,32,120,0,0,0,0,0,0,0,115,109,36,97,108,108,0,0,112,111,114,36,116,114,97,105,116,0,0,0,0,0,0,0,116,114,97,110,115,36,112,111,115,101,0,0,0,0,0,0,112,111,105,36,110,116,115,109,97,120,0,0,0,0,0,0,109,101,36,116,114,105,99,0,110,111,99,111,110,36,116,111,117,114,115,0,0,0,0,0,35,32,32,32,32,69,79,70,10,0,0,0,0,0,0,0,102,36,111,110,116,115,105,122,101,0,0,0,0,0,0,0,72,79,77,69,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,58,39,0,0,0,100,101,36,112,116,104,0,0,98,36,105,103,0,0,0,0,102,117,110,99,116,105,111,110,32,99,111,110,116,97,105,110,115,32,116,111,111,32,109,97,110,121,32,112,97,114,97,109,101,116,101,114,115,0,0,0,108,97,116,101,120,0,0,0,32,115,105,122,101,32,37,102,32,37,102,0,0,0,0,0,99,98,100,116,105,36,99,115,0,0,0,0,0,0,0,0,83,121,115,95,82,101,113,0,32,115,105,122,101,32,37,100,32,37,100,0,0,0,0,0,79,70,70,0,0,0,0,0,112,111,108,121,36,103,111,110,0,0,0,0,0,0,0,0,118,101,114,115,105,111,110,0,108,111,97,100,112,97,116,104,95,102,111,112,101,110,0,0,117,110,115,101,116,32,108,111,103,32,120,0,0,0,0,0,100,101,112,116,104,0,0,0,109,101,116,114,105,99,0,0,115,99,97,110,0,0,0,0,105,110,99,104,101,115,0,0,112,111,105,110,116,115,109,97,120,0,0,0,0,0,0,0,115,109,97,108,108,0,0,0,98,105,103,0,0,0,0,0,37,115,32,37,115,32,37,115,32,37,100,32,37,115,32,37,115,32,37,115,37,115,32,37,115,32,34,37,115,44,37,100,34,32,37,115,32,37,100,32,37,115,32,37,100,32,37,115,32,37,115,0,0,0,0,0,84,107,47,84,99,108,32,99,97,110,118,97,115,32,119,105,100,103,101,116,32,91,112,101,114,108,116,107,93,32,91,105,110,116,101,114,97,99,116,105,118,101,93,0,0,0,0,0,32,116,101,120,116,114,105,103,105,100,0,0,0,0,0,0,110,111,122,100,116,105,36,99,115,0,0,0,0,0,0,0,32,116,101,120,116,104,105,100,100,101,110,0,0,0,0,0,79,78,0,0,0,0,0,0,32,114,111,116,97,116,101,32,98,121,32,37,100,0,0,0,32,116,101,120,116,115,112,101,99,105,97,108,0,0,0,0,115,101,116,32,108,111,103,32,122,0,0,0,0,0,0,0,37,115,37,115,37,115,0,0,32,116,101,120,116,110,111,114,109,97,108,0,0,0,0,0,110,111,102,108,105,112,0,0,37,72,0,0,0,0,0,0,119,114,111,110,103,32,118,101,114,115,105,111,110,32,110,117,109,98,101,114,44,32,109,117,115,116,32,98,101,32,51,46,49,32,111,114,32,51,46,50,0,0,0,0,0,0,0,0,104,105,100,100,101,110,32,115,111,114,116,97,114,114,97,121,0,0,0,0,0,0,0,0,118,101,114,115,105,111,110,58,32,51,46,49,32,111,114,32,51,46,50,32,101,120,112,101,99,116,101,100,0,0,0,0,112,111,105,110,116,115,109,97,120,58,32,110,117,109,98,101,114,32,111,117,116,32,111,102,32,114,97,110,103,101,32,40,50,44,37,108,100,41,0,0,109,97,120,46,32,112,111,105,110,116,115,32,112,101,114,32,112,111,108,121,108,105,110,101,58,32,110,117,109,98,101,114,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,0,100,101,112,116,104,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,0,0,0,0,116,107,99,97,110,118,97,115,0,0,0,0,0,0,0,0,100,101,112,116,104,58,32,110,117,109,98,101,114,32,101,120,112,101,99,116,101,100,0,0,122,100,116,105,36,99,115,0,116,104,105,99,107,110,101,115,115,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,9,112,111,108,97,114,32,105,115,32,37,115,10,0,0,0,116,104,105,99,107,110,101,115,115,58,32,110,117,109,98,101,114,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,117,110,115,101,116,32,108,111,103,32,122,0,0,0,0,0,115,105,122,101,58,32,50,32,110,117,109,98,101,114,115,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,0,0,80,83,105,122,101,95,50,0,83,105,110,103,108,101,0,0,102,108,105,112,0,0,0,0,86,105,115,105,98,108,101,32,112,105,120,101,108,32,103,114,105,100,32,104,97,115,32,97,32,115,99,97,110,32,108,105,110,101,32,108,111,110,103,101,114,32,116,104,97,110,32,112,114,101,118,105,111,117,115,32,115,99,97,110,32,108,105,110,101,115,46,0,0,0,0,0,65,52,0,0,0,0,0,0,76,101,116,116,101,114,0,0,35,70,73,71,32,51,46,50,10,37,115,10,37,115,10,37,115,10,37,115,10,37,54,46,50,102,10,37,115,10,37,100,10,37,100,32,37,100,10,0,51,46,50,0,0,0,0,0,77,101,116,114,105,99,0,0,84,71,73,70,32,88,49,49,32,91,109,111,100,101,93,32,91,120,44,121,93,32,91,100,97,115,104,101,100,93,32,91,34,102,111,110,116,34,32,91,102,111,110,116,115,105,122,101,93,93,0,0,0,0,0,0,73,110,99,104,101,115,0,0,110,111,121,50,100,116,105,36,99,115,0,0,0,0,0,0,67,101,110,116,101,114,0,0,100,101,103,114,101,101,115,10,0,0,0,0,0,0,0,0,115,101,116,32,108,97,98,101,108,32,37,100,32,34,37,115,34,32,97,116,32,0,0,0,35,70,73,71,32,51,46,49,10,37,115,10,37,115,10,37,115,10,37,100,32,37,100,10,0,0,0,0,0,0,0,0,115,101,116,32,108,111,103,32,99,98,0,0,0,0,0,0,51,46,49,0,0,0,0,0,70,73,71,95,112,111,105,110,116,115,0,0,0,0,0,0,102,108,105,112,122,0,0,0,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,54,46,51,102,32,37,54,46,51,102,32,37,100,32,37,54,46,51,102,32,37,54,46,51,102,32,37,100,32,37,100,32,37,115,92,48,48,49,10,0,0,0,0,0,0,70,73,71,32,116,101,120,116,0,0,0,0,0,0,0,0,52,10,9,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,10,0,0,0,0,0,53,10,9,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,10,0,0,0,0,0,0,0,115,113,114,116,40,120,41,0,50,32,51,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,54,46,51,102,32,48,32,48,32,48,32,48,32,48,32,0,0,0,0,0,0,0,116,103,105,102,0,0,0,0,49,32,51,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,54,46,51,102,32,49,32,48,46,48,48,48,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,10,0,121,50,100,116,105,36,99,115,0,0,0,0,0,0,0,0,37,100,32,37,100,32,37,100,32,37,100,10,0,0,0,0,114,97,100,105,97,110,115,10,0,0,0,0,0,0,0,0,37,100,32,37,100,32,37,46,51,102,32,37,46,51,102,32,37,46,51,102,10,0,0,0,115,97,109,112,108,105,110,103,32,114,97,116,101,32,109,117,115,116,32,98,101,32,62,32,49,59,32,115,97,109,112,108,105,110,103,32,117,110,99,104,97,110,103,101,100,0,0,0,117,110,115,101,116,32,108,97,98,101,108,10,0,0,0,0,114,101,99,116,0,0,0,0,117,110,115,101,116,32,108,111,103,32,99,98,0,0,0,0,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,57,46,51,102,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,10,0,0,0,0,0,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,54,46,51,102,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,10,32,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,10,0,0,0,0,0,77,117,115,116,32,115,112,101,99,105,102,121,32,97,32,115,97,109,112,108,105,110,103,32,97,114,114,97,121,32,115,105,122,101,32,98,101,102,111,114,101,32,105,110,100,105,99,97,116,105,110,103,32,102,108,105,112,32,105,110,32,115,101,99,111,110,100,32,100,105,109,101,110,115,105,111,110,0,0,0,102,105,103,58,32,65,116,116,101,109,112,116,32,116,111,32,115,101,116,32,112,97,108,101,116,116,101,32,116,119,105,99,101,10,0,0,0,0,0,0,37,100,32,37,100,32,35,37,50,46,50,120,37,50,46,50,120,37,50,46,50,120,10,0,77,111,110,111,99,104,114,111,109,101,32,102,105,103,32,102,105,108,101,58,32,117,115,105,110,103,32,103,114,97,121,32,112,97,108,101,116,116,101,32,105,110,115,116,101,97,100,32])
+.concat([111,102,32,99,111,108,111,114,10,0,0,0,0,0,0,0,102,105,103,58,32,80,97,108,101,116,116,101,32,117,115,101,100,32,98,101,102,111,114,101,32,115,101,116,10,0,0,0,32,37,100,32,37,100,0,0,87,51,67,32,83,99,97,108,97,98,108,101,32,86,101,99,116,111,114,32,71,114,97,112,104,105,99,115,32,100,114,105,118,101,114,0,0,0,0,0,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,57,46,51,102,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,108,100,10,9,0,0,0,110,111,121,100,116,105,36,99,115,0,0,0,0,0,0,0,45,54,10,0,0,0,0,0,9,65,110,103,108,101,115,32,97,114,101,32,105,110,32,0,73,108,108,101,103,97,108,32,118,97,108,117,101,32,102,111,114,32,115,105,122,101,0,0,117,110,115,101,116,32,107,101,121,10,0,0,0,0,0,0,35,32,69,110,100,32,112,108,111,116,32,35,37,100,10,0,96,98,117,105,108,116,105,110,45,110,101,97,114,101,115,116,45,108,111,103,96,32,116,111,103,103,108,101,32,108,111,103,115,99,97,108,101,32,111,102,32,97,120,105,115,32,110,101,97,114,101,115,116,32,99,117,114,115,111,114,0,0,0,0,35,32,66,101,103,105,110,32,112,108,111,116,32,35,37,100,10,0,0,0,0,0,0,0,32,37,100,32,37,100,32,37,100,32,37,100,10,0,0,0,102,108,105,112,121,0,0,0,54,0,0,0,0,0,0,0,78,101,101,100,32,50,32,116,111,32,55,32,117,115,105,110,103,32,115,112,101,99,115,0,27,42,109,112,49,109,50,97,50,81,0,0,0,0,0,0,27,42,100,101,84,0,0,0,27,42,100,97,102,108,115,67,0,0,0,0,0,0,0,0,27,42,112,0,0,0,0,0,81,77,83,47,81,85,73,67,32,76,97,115,101,114,32,112,114,105,110,116,101,114,32,40,97,108,115,111,32,84,97,108,97,114,105,115,32,49,50,48,48,32,97,110,100,32,111,116,104,101,114,115,41,0,0,0,27,42,109,37,100,66,0,0,121,100,116,105,36,99,115,0,27,42,100,84,0,0,0,0,9,115,97,109,112,108,105,110,103,32,114,97,116,101,32,105,115,32,37,100,44,32,37,100,10,0,0,0,0,0,0,0,110,111,115,113,36,117,97,114,101,0,0,0,0,0,0,0,115,101,116,32,107,101,121,32,37,115,111,112,97,113,117,101,10,0,0,0,0,0,0,0,72,80,50,54,0,0,0,0,116,117,114,110,105,110,103,32,109,111,117,115,101,32,111,102,102,46,10,0,0,0,0,0,37,100,44,37,100,79,0,0,37,100,44,37,100,80,0,0,102,108,105,112,120,0,0,0,27,42,100,0,0,0,0,0,27,42,109,37,100,78,0,0,9,102,111,110,116,112,97,116,104,32,105,115,32,101,109,112,116,121,10,0,0,0,0,0,27,42,109,49,109,49,110,49,51,54,44,49,99,90,0,0,120,50,94,123,37,100,125,0,27,42,100,97,99,90,0,0,105,110,118,97,108,105,100,32,97,120,105,115,0,0,0,0,27,42,112,97,102,37,100,44,37,100,90,0,0,0,0,0,113,109,115,0,0,0,0,0,27,42,112,98,102,37,100,44,37,100,90,0,0,0,0,0,110,111,120,50,100,116,105,36,99,115,0,0,0,0,0,0,27,42,109,37,100,98,90,0,9,105,115,111,32,115,97,109,112,108,105,110,103,32,114,97,116,101,32,105,115,32,37,100,44,32,37,100,10,0,0,0,110,111,114,97,36,116,105,111,0,0,0,0,0,0,0,0,10,115,101,116,32,107,101,121,32,109,97,120,99,111,108,117,109,110,115,32,37,100,32,109,97,120,114,111,119,115,32,37,100,0,0,0,0,0,0,0,27,42,108,37,115,10,0,0,116,117,114,110,105,110,103,32,109,111,117,115,101,32,111,110,46,10,0,0,0,0,0,0,27,42,109,37,100,110,90,10,0,0,0,0,0,0,0,0,110,111,101,106,101,99,116,0,120,95,109,105,110,51,100,32,115,104,111,117,108,100,32,110,111,116,32,101,113,117,97,108,32,120,95,109,97,120,51,100,33,0,0,0,0,0,0,0,67,117,114,114,101,110,116,108,121,32,110,111,116,32,115,117,112,112,111,114,116,105,110,103,32,116,104,114,101,101,45,100,105,109,101,110,115,105,111,110,97,108,32,115,97,109,112,108,105,110,103,0,0,0,0,0,105,115,111,36,95,56,56,53,57,95,49,0,0,0,0,0,101,106,101,99,116,0,0,0,32,116,121,112,101,10,0,0,37,100,32,112,101,110,115,32,37,115,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,34,101,106,101,99,116,34,32,111,114,32,110,117,109,98,101,114,32,111,102,32,112,101,110,115,0,0,0,0,0,78,117,109,98,101,114,32,111,102,32,112,101,110,115,32,109,117,115,116,32,98,101,32,112,111,115,105,116,105,118,101,0,101,106,101,36,99,116,0,0,80,111,115,116,83,99,114,105,112,116,32,103,114,97,112,104,105,99,115,44,32,105,110,99,108,117,100,105,110,103,32,69,80,83,70,32,101,109,98,101,100,100,101,100,32,102,105,108,101,115,32,40,42,46,101,112,115,41,0,0,0,0,0,0,80,85,83,80,48,59,80,71,59,27,46,90,10,0,0,0,120,50,100,116,105,36,99,115,0,0,0,0,0,0,0,0,112,114,36,105,110,116,0,0,80,85,83,80,48,59,27,46,90,10,0,0,0,0,0,0,105,110,100,101,112,101,110,100,101,110,116,108,121,32,115,99,97,108,101,100,0,0,0,0,114,97,36,116,105,111,0,0,112,114,101,36,102,105,120,0,10,115,101,116,32,107,101,121,32,37,115,105,110,118,101,114,116,32,115,97,109,112,108,101,110,32,37,103,32,115,112,97,99,105,110,103,32,37,103,32,119,105,100,116,104,32,37,103,32,104,101,105,103,104,116,32,37,103,32,0,0,0,0,0,67,65,55,59,0,0,0,0,96,98,117,105,108,116,105,110,45,116,111,103,103,108,101,45,109,111,117,115,101,96,0,0,73,78,59,37,115,10,83,67,48,44,37,100,44,48,44,37,100,59,10,83,82,37,102,44,37,102,59,10,0,0,0,0,27,46,89,10,27,46,73,56,49,59,59,49,55,58,27,46,78,59,49,57,58,27,46,77,53,48,48,58,10,0,0,0,100,122,0,0,0,0,0,0,80,85,59,80,65,37,100,44,37,100,59,10,0,0,0,0,80,65,37,100,44,37,100,59,10,0,0,0,0,0,0,0,80,68,59,80,65,37,100,44,37,100,59,10,0,0,0,0,45,85,83,69,95,67,87,68,82,67,32,32,0,0,0,0,80,85,59,10,83,80,37,100,59,10,0,0,0,0,0,0,3,10,0,0,0,0,0,0,112,111,115,116,115,99,114,105,112,116,0,0,0,0,0,0,76,66,0,0,0,0,0,0,110,111,120,100,116,105,36,99,115,0,0,0,0,0,0,0,116,114,0,0,0,0,0,0,68,73,48,44,49,59,10,0,111,110,32,116,104,101,32,115,97,109,101,32,115,99,97,108,101,0,0,0,0,0,0,0,115,113,36,117,97,114,101,0,110,111,98,111,120,0,0,0,68,73,48,44,45,49,59,10,0,0,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,112,114,101,118,105,111,117,115,45,109,111,117,115,101,45,102,111,114,109,97,116,96,0,68,73,49,44,48,59,10,0,116,0,0,0,0,0,0,0,112,36,115,112,111,105,110,116,115,0,0,0,0,0,0,0,77,117,115,116,32,115,112,101,99,105,102,121,32,97,32,115,97,109,112,108,105,110,103,32,97,114,114,97,121,32,115,105,122,101,32,98,101,102,111,114,101,32,105,110,100,105,99,97,116,105,110,103,32,115,112,97,99,105,110,103,32,105,110,32,115,101,99,111,110,100,32,100,105,109,101,110,115,105,111,110,0,0,0,0,0,0,0,0,32,37,115,32,115,111,108,105,100,32,37,46,50,102,32,0,110,36,111,112,115,112,111,105,110,116,115,0,0,0,0,0,110,111,104,105,100,100,101,110,36,51,100,0,0,0,0,0,102,36,111,110,116,0,0,0,115,121,110,116,97,120,32,101,114,114,111,114,0,0,0,0,100,97,115,36,104,101,100,0,115,111,108,36,105,100,0,0,110,111,112,115,112,111,105,110,116,115,0,0,0,0,0,0,70,114,97,109,101,32,109,97,107,101,114,32,77,73,70,32,51,46,48,48,32,102,111,114,109,97,116,0,0,0,0,0,112,115,112,111,105,110,116,115,0,0,0,0,0,0,0,0,120,100,116,105,36,99,115,0,83,99,114,111,108,108,95,76,111,99,107,0,0,0,0,0,112,105,116,99,104,0,0,0,120,47,121,47,122,0,0,0,101,120,116,114,97,110,101,111,117,115,32,111,114,32,111,117,116,45,111,102,45,111,114,100,101,114,32,97,114,103,117,109,101,110,116,115,32,105,110,32,115,101,116,32,97,114,114,111,119,115,116,121,108,101,0,0,104,101,105,103,104,116,0,0,115,119,105,116,99,104,101,100,32,109,111,117,115,101,32,102,111,114,109,97,116,32,102,114,111,109,32,37,108,100,32,116,111,32,37,108,100,10,0,0,32,37,115,32,37,100,32,37,115,32,92,10,32,32,32,37,115,32,37,115,32,37,102,32,37,115,0,0,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,115,117,98,115,116,114,105,110,103,32,114,97,110,103,101,32,111,112,101,114,97,116,111,114,32,97,112,112,108,105,101,100,32,116,111,32,110,111,110,45,83,84,82,73,78,71,32,116,121,112,101,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,112,111,105,110,116,32,115,105,122,101,58,32,114,101,97,108,32,110,117,109,98,101,114,0,0,0,0,0,0,0,100,114,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,102,111,110,116,58,32,115,116,105,99,107,44,32,99,103,95,116,105,109,101,115,44,32,117,110,105,118,101,114,115,44,32,122,97,112,102,95,100,105,110,103,98,97,116,115,44,32,97,110,116,105,113,117,101,95,111,108,105,118,101,44,10,97,114,105,97,108,44,32,99,111,117,114,105,101,114,44,32,103,97,114,97,109,111,110,100,95,97,110,116,105,103,117,97,44,32,108,101,116,116,101,114,95,103,111,116,104,105,99,44,32,99,103,95,111,109,101,103,97,44,32,97,108,98,101,114,116,117,115,44,10,116,105,109,101,115,95,110,101,119,95,114,111,109,97,110,44,32,99,108,97,114,101,110,100,111,110,44,32,99,111,114,111,110,101,116,44,32,109,97,114,105,103,111,108,100,44,32,116,114,117,101,116,121,112,101,95,115,121,109,98,111,108,115,44,32,111,114,32,119,105,110,103,100,105,110,103,115,0,0,0,0,0,0,78,117,109,98,101,114,32,111,102,32,112,101,110,115,32,109,117,115,116,32,98,101,32,97,32,112,111,115,105,116,105,118,101,0,0,0,0,0,0,0,108,101,116,116,101,114,0,0,108,101,116,36,116,101,114,0,108,101,103,97,108,0,0,0,109,105,102,0,0,0,0,0,108,101,103,36,97,108,0,0,110,111,99,98,116,105,36,99,115,0,0,0,0,0,0,0,110,111,101,120,116,101,110,100,101,100,0,0,0,0,0,0,120,47,121,0,0,0,0,0,97,114,114,111,119,115,116,121,108,101,0,0,0,0,0,0,110,111,97,117,116,111,116,105,116,108,101,115,0,0,0,0,110,111,101,120,116,36,101,110,100,101,100,0,0,0,0,0,96,98,117,105,108,116,105,110,45,110,101,120,116,45,109,111,117,115,101,45,102,111,114,109,97,116,96,0,0,0,0,0,101,120,116,101,110,100,101,100,0,0,0,0,0,0,0,0,101,120,116,36,101,110,100,101,100,0,0,0,0,0,0,0,100,121,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,109,111,100,101,58,32,112,111,114,116,114,97,105,116,32,111,114,32,108,97,110,100,115,99,97,112,101,0,0,0,109,36,111,100,101,0,0,0,42,65,108,108,42,32,101,100,103,101,115,32,117,110,100,101,102,105,110,101,100,32,111,114,32,111,117,116,32,111,102,32,114,97,110,103,101,44,32,116,104,117,115,32,110,111,32,112,108,111,116,46,0,0,0,0,27,38,108,48,79,0,0,0,27,38,108,49,79,0,0,0,27,69,27,38,108,49,88,37,115,10,0,0,0,0,0,0,73,109,97,103,101,110,32,108,97,115,101,114,32,112,114,105,110,116,101,114,0,0,0,0,27,37,48,65,27,69,10,0,99,98,116,105,36,99,115,0,27,37,49,65,27,38,108,48,72,10,0,0,0,0,0,0,9,9,37,115,32,97,120,101,115,32,97,114,101,32,37,115,10,0,0,0,0,0,0,0,111,98,106,101,99,116,32,105,110,105,116,105,97,108,105,122,97,116,105,111,110,32,102,97,105,108,117,114,101,0,0,0,97,117,116,111,116,105,116,108,101,115,0,0,0,0,0,0,85,76,49,44,49,48,48,59,10,85,76,50,44,56,44,56,44,57,44,56,44,56,44,57,44,56,44,56,44,57,44,56,44,56,44,57,59,10,85,76,51,44,54,44,54,44,54,44,55,44,54,44,54,44,54,44,55,44,54,44,54,44,54,44,55,44,54,44,54,44,54,44,55,59,10,85,76,52,44,53,44,53,44,53,44,49,48,44,53,44,53,44,53,44,49,48,44,53,44,53,44,53,44,49,48,59,10,85,76,53,44,53,44,53,44,53,44,53,44,53,44,56,44,53,44,53,44,53,44,53,44,53,44,56,44,53,44,53,44,53,44,53,44,53,44,57,59,10,85,76,54,44,56,44,56,44,48,44,57,44,56,44,56,44,48,44,57,44,56,44,56,44,48,44,57,59,10,85,76,55,44,52,44,52,44,52,44,52,44,48,44,52,44,52,44,52,44,52,44,52,44,48,44,52,44,52,44,52,44,52,44,52,44,48,44,52,59,10,85,76,56,44,54,44,54,44,54,44,54,44,54,44,54,44,48,44,54,44,54,44,54,44,54,44,54,44,54,44,54,44,48,44,54,59,10,0,96,98,117,105,108,116,105,110,45,100,101,99,114,101,109,101,110,116,45,99,108,105,112,98,111,97,114,100,109,111,100,101,96,0,0,0,0,0,0,0,73,78,83,80,49,83,68,49,44,37,100,44,50,44,37,100,44,0,0,0,0,0,0,0,27,37,48,66,0,0,0,0,80,83,105,122,101,95,49,0,100,116,0,0,0,0,0,0,105,109,97,103,101,0,0,0,80,69,60,0,0,0,0,0,80,69,0,0,0,0,0,0,46,48,56,0,0,0,0,0,105,109,97,103,101,110,0,0,46,49,54,0,0,0,0,0,110,111,114,116,105,36,99,115,0,0,0,0,0,0,0,0,46,50,52,0,0,0,0,0,37,103,32,114,111,116,95,120,44,32,37,103,32,114,111,116,95,122,44,32,37,103,32,115,99,97,108,101,44,32,37,103,32,115,99,97,108,101,95,122,10,0,0,0,0,0,0,0,111,98,106,101,99,116,0,0,97,117,116,111,116,105,116,108,101,115,32,99,111,108,117,109,110,104,101,97,100,0,0,0,44,50,0,0,0,0,0,0,115,119,105,116,99,104,101,100,32,99,108,105,112,98,111,97,114,100,32,102,111,114,109,97,116,32,102,114,111,109,32,37,108,100,32,116,111,32,37,108,100,10,0,0,0,0,0,0,80,87,46,49,76,84,49,44,46,50,53,0,0,0,0,0,80,87,46,50,76,84,0,0,100,120,0,0,0,0,0,0,80,87,37,115,76,84,0,0,80,87,37,115,76,84,37,100,37,115,0,0,0,0,0,0,80,85,59,10,83,80,49,59,10,80,87,46,50,76,84,49,44,46,50,53,0,0,0,0,80,85,59,10,83,80,49,59,10,80,87,46,50,76,84,0,115,113,114,116,0,0,0,0,80,85,59,10,83,80,37,100,59,10,76,84,59,0,0,0,72,80,32,68,101,115,105,103,110,106,101,116,32,55,53,48,67,44,32,72,80,32,76,97,115,101,114,106,101,116,32,73,73,73,47,73,86,44,32,101,116,99,46,32,40,109,97,110,121,32,111,112,116,105,111,110,115,41,0,0,0,0,0,0,80,85,59,10,83,80,37,100,59,10,76,84,37,100,59,0,114,116,105,36,99,115,0,0,76,66,37,115,3,10,0,0,109,97,112,10,0,0,0,0,73,110,99,111,110,115,105,115,116,101,110,116,32,111,112,116,105,111,110,115,0,0,0,0,68,73,49,44,48,0,0,0,96,98,117,105,108,116,105,110,45,105,110,99,114,101,109,101,110,116,45,99,108,105,112,98,111,97,114,100,109,111,100,101,96,0,0,0,0,0,0,0,68,73,48,44,45,49,0,0,68,73,48,44,49,0,0,0,97,114,114,36,97,121,0,0,76,79,55,0,0,0,0,0,76,79,52,0,0,0,0,0,76,79,49,0,0,0,0,0,87,71,37,46,50,102,44,49,56,48,44,49,56,48,59,69,80,59,10,0,0,0,0,0,69,87,37,46,50,102,44,48,44,49,56,48,59,10,0,0,112,99,108,53,0,0,0,0,80,77,50,59,70,84,49,48,44,51,48,59,70,80,59,69,80,59,70,84,59,10,0,0,110,111,122,116,105,36,99,115,0,0,0,0,0,0,0,0,70,84,49,48,44,51,48,59,87,71,37,46,50,102,44,48,44,51,54,48,59,69,80,59,70,84,59,10,0,0,0,0,9,118,105,101,119,32,105,115,32,0,0,0,0,0,0,0,85,110,114,101,99,111,103,110,105,122,101,100,32,111,114,32,100,117,112,108,105,99,97,116,101,32,111,112,116,105,111,110,0,0,0,0,0,0,0,0,76,101,102,116,0,0,0,0,70,84,49,48,44,51,48,59,82,65,37,100,44,37,100,59,69,80,59,70,84,59,10,0,110,111,116,0,0,0,0,0,82,65,37,100,44,37,100,59,69,80,59,10,0,0,0,0,87,71,37,100,44,48,44,51,54,48,59,69,80,59,10,0,114,101,99,36,111,114,100,0,87,71,37,100,44,57,48,44,50,55,48,59,69,80,59,69,87,37,100,44,48,44,57,48,59,10,0,0,0,0,0,0,87,71,37,100,44,49,56,48,44,50,55,48,59,69,80,59,69,87,37,100,44,57,48,44,57,48,59,10,0,0,0,0,87,71,37,100,44,49,56,48,44,49,56,48,59,69,80,59,69,87,37,100,44,48,44,49,56,48,59,10,0,0,0,0,87,71,37,100,44,50,55,48,44,50,55,48,59,69,80,59,69,87,37,100,44,49,56,48,44,57,48,59,10,0,0,0,87,71,37,100,44,57,48,44,57,48,59,69,80,59,69,87,37,100,44,49,56,48,44,57,48,59,87,71,37,100,44,50,55,48,44,57,48,59,69,80,59,69,87,37,100,44,48,44,57,48,59,10,0,0,0,0,72,80,55,52,55,53,32,97,110,100,32,114,101,108,97,116,105,118,101,115,32,91,110,117,109,98,101,114,32,111,102,32,112,101,110,115,93,32,91,101,106,101,99,116,93,0,0,0,87,71,37,100,44,50,55,48,44,49,56,48,59,69,80,59,69,87,37,100,44,57,48,44,49,56,48,59,10,0,0,0,122,116,105,36,99,115,0,0,87,71,37,100,44,50,55,48,44,57,48,59,69,80,59,69,87,37,100,44,48,44,50,55,48,59,10,0,0,0,0,0,9,115,117,114,102,97,99,101,32,105,115,32,37,115,100,114,97,119,110,10,0,0,0,0,102,105,108,108,99,36,111,108,111,114,0,0,0,0,0,0,104,111,114,105,122,111,110,116,97,108,0,0,0,0,0,0,87,71,37,100,44,48,44,50,55,48,59,69,80,59,69,87,37,100,44,50,55,48,44,57,48,59,10,0,0,0,0,0,100,105,115,116,97,110,99,101,32,116,111,32,114,117,108,101,114,32,119,105,108,108,32,37,115,32,98,101,32,115,104,111,119,110,32,105,110,32,112,111,108,97,114,32,99,111,111,114,100,105,110,97,116,101,115,46,10,0,0,0,0,0,0,0,87,71,37,100,44,57,48,44,49,56,48,59,69,80,59,69,87,37,100,44,50,55,48,44,49,56,48,59,10,0,0,0,87,71,37,100,44,48,44,57,48,59,69,80,59,69,87,37,100,44,57,48,44,57,48,59,87,71,37,100,44,49,56,48,44,57,48,59,69,80,59,69,87,37,100,44,50,55,48,44,57,48,59,10,0,0,0,0,85,110,114,101,99,111,103,110,105,122,101,100,32,102,105,108,101,116,121,112,101,59,32,116,114,121,32,34,115,104,111,119,32,100,97,116,97,102,105,108,101,32,98,105,110,97,114,121,32,102,105,108,101,116,121,112,101,115,34,0,0,0,0,0,87,71,37,100,44,49,56,48,44,57,48,59,69,80,59,69,87,37,100,44,50,55,48,44,50,55,48,59,10,0,0,0,87,71,37,100,44,48,44,49,56,48,59,69,80,59,69,87,37,100,44,49,56,48,44,49,56,48,59,10,0,0,0,0,9,115,121,115,116,101,109,32,102,111,110,116,112,97,116,104,32,105,115,32,0,0,0,0,87,71,37,100,44,57,48,44,57,48,59,69,80,59,69,87,37,100,44,49,56,48,44,50,55,48,59,10,0,0,0,0,120,50,94,123,37,100,125,89,105,0,0,0,0,0,0,0,87,71,37,100,44,48,44,57,48,59,69,80,59,69,87,37,100,44,57,48,44,50,55,48,59,10,0,0,0,0,0,0,121,111,117,32,99,97,110,39,116,32,99,104,97,110,103,101,32,116,104,101,32,111,117,116,112,117,116,32,105,110,32,109,117,108,116,105,112,108,111,116,32,109,111,100,101,0,0,0,37,72,58,37,77,58,37,83,0,0,0,0,0,0,0,0,67,73,37,100,59,10,0,0,104,112,103,108,0,0,0,0,80,77,50,59,70,80,59,69,80,59,10,0,0,0,0,0,110,111,121,50,116,105,36,99,115,0,0,0,0,0,0,0,80,77,48,59,10,0,0,0,100,114,97,119,110,0,0,0,102,99,0,0,0,0,0,0,118,101,114,116,105,99,97,108,0,0,0,0,0,0,0,0,87,71,37,46,50,102,44,48,44,51,54,48,59,69,80,59,10,0,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,116,111,103,103,108,101,45,112,111,108,97,114,100,105,115,116,97,110,99,101,96,0,0,67,73,37,46,50,102,59,10,0,0,0,0,0,0,0,0,82,65,37,46,50,102,44,37,46,50,102,59,69,80,59,10,0,0,0,0,0,0,0,0,117,116,102,36,56,0,0,0,119,105,110,103,100,105,110,103,115,0,0,0,0,0,0,0,32,32,99,111,108,111,114,0,98,97,110,100,0,0,0,0,119,36,105,110,103,100,105,110,103,115,0,0,0,0,0,0,116,114,117,101,116,121,112,101,95,115,121,109,98,111,108,115,0,0,0,0,0,0,0,0,116,114,36,117,101,116,121,112,101,95,115,121,109,98,111,108,115,0,0,0,0,0,0,0,103,101,116,99,111,108,111,114,46,99,0,0,0,0,0,0,109,97,114,105,103,111,108,100,0,0,0,0,0,0,0,0,72,80,50,54,52,56,32,97,110,100,32,72,80,50,54,52,55,0,0,0,0,0,0,0,109,36,97,114,105,103,111,108,100,0,0,0,0,0,0,0,121,50,116,105,36,99,115,0,112,36,108,111,116,0,0,0,99,111,114,111,110,101,116,0,114,101,109,111,118,101,100,0,73,110,118,97,108,105,100,32,99,111,109,109,97,110,100,32,45,32,100,105,100,32,121,111,117,32,109,101,97,110,32,39,117,110,115,101,116,32,115,116,121,108,101,32,114,101,99,116,97,110,103,108,101,39,63,0,110,111,111,117,116,36,112,117,116,0,0,0,0,0,0,0,32,37,115,32,37,115,32,37,115,114,101,118,101,114,115,101,32,37,115,101,110,104,97,110,99,101,100,32,37,115,32,0,99,111,114,36,111,110,101,116,0,0,0,0,0,0,0,0,99,111,109,109,117,110,105,99,97,116,105,111,110,32,99,111,109,109,97,110,100,115,32,119,105,108,108,32,98,101,32,101,99,104,111,101,100,46,10,0,99,108,97,114,101,110,100,111,110,0,0,0,0,0,0,0,99,108,36,97,114,101,110,100,111,110,0,0,0,0,0,0,102,105,108,101,36,116,121,112,101,0,0,0,0,0,0,0,116,105,109,101,115,95,110,101,119,95,114,111,109,97,110,0,116,105,36,109,101,115,95,110,101,119,95,114,111,109,97,110,0,0,0,0,0,0,0,0,97,108,98,101,114,116,117,115,0,0,0,0,0,0,0,0,43,66,73,78,65,82,89,95,68,65,84,65,32,32,0,0,97,108,36,98,101,114,116,117,115,0,0,0,0,0,0,0,99,103,95,111,109,101,103,97,0,0,0,0,0,0,0,0,104,112,50,54,52,56,0,0,99,103,95,111,36,109,101,103,97,0,0,0,0,0,0,0,110,111,121,116,105,36,99,115,0,0,0,0,0,0,0,0,108,101,116,116,101,114,95,103,111,116,104,105,99,0,0,0,0,0,0,0,0,0,0,0,9,104,105,100,100,101,110,32,115,117,114,102,97,99,101,32,105,115,32,37,115,10,0,0,99,36,101,110,116,114,101,0,32,98,111,116,116,111,109,0,108,36,101,116,116,101,114,95,103,111,116,104,105,99,0,0,101,99,104,111,105,110,103,32,111,102,32,99,111,109,109,117,110,105,99,97,116,105,111,110,32,99,111,109,109,97,110,100,115,32,105,115,32,116,117,114,110,101,100,32,111,102,102,46,10,0,0,0,0,0,0,0,103,97,114,97,109,111,110,100,95,97,110,116,105,103,117,97,0,0,0,0,0,0,0,0,103,36,97,114,97,109,111,110,100,95,97,110,116,105,103,117,97,0,0,0,0,0,0,0,100,97,116,97,102,105,108,101,32,99,111,108,117,109,110,0,115,101,116,32,97,117,116,111,115,99,97,108,101,32,37,115,102,105,120,109,97,120,10,0,99,111,117,36,114,105,101,114,0,0,0,0,0,0,0,0,97,114,105,97,108,0,0,0,97,114,36,105,97,108,0,0,99,117,114,118,101,32,112,111,105,110,116,115,0,0,0,0,117,110,114,101,99,111,103,110,105,122,101,100,32,111,112,116,105,111,110,32,37,115,10,0,105,116,101,114,97,116,105,111,110,32,108,105,110,107,101,100,32,108,105,115,116,0,0,0,97,110,116,105,113,117,101,95,111,108,105,118,101,0,0,0,97,110,36,116,105,113,117,101,95,111,108,105,118,101,0,0,72,80,50,54,50,51,65,32,97,110,100,32,109,97,121,98,101,32,111,116,104,101,114,115,0,0,0,0,0,0,0,0,122,97,112,102,95,100,105,110,103,98,97,116,115,0,0,0,121,116,105,36,99,115,0,0,80,97,117,115,101,0,0,0,122,36,97,112,102,95,100,105,110,103,98,97,116,115,0,0,9,84,114,121,32,116,111,32,115,101,116,32,76,79,67,75,69,68,32,97,115,112,101,99,116,32,114,97,116,105,111,32,116,111,32,37,103,58,49,46,48,10,0,0,0,0,0,0,85,110,114,101,99,111,103,105,110,105,122,101,100,32,111,98,106,101,99,116,32,116,121,112,101,0,0,0,0,0,0,0,32,116,111,112,0,0,0,0,99,103,95,116,105,109,101,115,0,0,0,0,0,0,0,0,108,102,32,116,111,107,101,110,115,0,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,116,111,103,103,108,101,45,118,101,114,98,111,115,101,96,0,0,0,0,0,0,0,0,99,103,95,116,36,105,109,101,115,0,0,0,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,115,117,98,115,116,114,105,110,103,32,114,97,110,103,101,32,115,112,101,99,105,102,105,101,114,115,32,109,117,115,116,32,104,97,118,101,32,105,110,116,101,103,101,114,32,118,97,108,117,101,115,0,0,0,0,0,115,116,105,99,107,0,0,0,100,102,95,110,111,95,117,115,101,95,115,112,101,99,115,32,61,61,32,48,32,124,124,32,111,117,116,112,117,116,32,61,61,32,100,102,95,110,111,95,117,115,101,95,115,112,101,99,115,32,124,124,32,111,117,116,112,117,116,32,61,61,32,109,97,120,0,0,0,0,0,0,115,36,116,105,99,107,0,0,117,110,105,118,101,114,115,0,117,36,110,105,118,101,114,115,0,0,0,0,0,0,0,0,53,44,37,100,44,54,44,37,100,44,55,44,37,100,59,83,83,59,10,0,0,0,0,0,51,44,37,102,44,0,0,0,104,112,50,54,50,51,65,0,52,44,37,102,44,0,0,0,110,111,120,50,116,105,36,99,115,0,0,0,0,0,0,0,83,68,49,44,37,100,44,50,44,37,100,44,0,0,0,0,9,78,111,32,97,116,116,101,109,112,116,32,116,111,32,99,111,110,116,114,111,108,32,97,115,112,101,99,116,32,114,97,116,105,111,10,0,0,0,0,85,110,114,101,99,111,103,110,105,122,101,100,32,112,111,108,121,103,111,110,32,115,121,110,116,97,120,0,0,0,0,0,36,0,0,0,0,0,0,0,115,101,116,32,115,105,122,101,32,115,113,117,97,114,101,0,37,100,32,37,115,32,91,37,49,100,44,37,49,100,93,0,99,111,117,114,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,58,32,117,110,107,110,111,119,110,32,99,111,108,117,109,110,32,116,121,112,101,0,0,0,0,0,37,37,46,37,100,102,0,0,37,115,37,48,50,100,0,0,64,100,111,99,117,109,101,110,116,40,108,97,110,103,117,97,103,101,32,105,109,112,114,101,115,115,44,32,112,97,112,101,114,32,97,52,41,0,0,0,99,111,110,118,101,114,116,101,100,32,108,97,98,101,108,32,115,116,114,105,110,103,0,0,63,36,0,0,0,0,0,0,104,36,101,108,112,0,0,0,70,73,71,32,103,114,97,112,104,105,99,115,32,108,97,110,103,117,97,103,101,32,102,111,114,32,88,70,73,71,32,103,114,97,112,104,105,99,115,32,101,100,105,116,111,114,0,0,112,36,111,108,121,108,105,110,101,0,0,0,0,0,0,0,120,50,116,105,36,99,115,0,118,36,101,99,116,111,114,115,0,0,0,0,0,0,0,0,9,84,114,121,32,116,111,32,115,101,116,32,97,115,112,101,99,116,32,114,97,116,105,111,32,116,111,32,37,103,58,49,46,48,10,0,0,0,0,0,80,111,108,121,103,111,110,32,105,115,32,110,111,116,32,99,108,111,115,101,100,32,45,32,97,100,100,105,110,103,32,101,120,116,114,97,32,118,101,114,116,101,120,10,0,0,0,0,118,101,99,116,111,114,115,0,115,101,116,32,115,105,122,101,32,110,111,115,113,117,97,114,101,0,0,0,0,0,0,0,112,111,108,121,108,105,110,101,0,0,0,0,0,0,0,0,99,111,108,111,117,114,0,0,113,117,111,116,101,0,0,0,85,115,97,103,101,58,32,115,101,116,32,116,101,114,109,105,110,97,108,32,109,105,102,32,91,111,112,116,105,111,110,115,93,10,9,111,112,116,105,111,110,115,58,10,9,9,99,111,108,111,117,114,32,47,32,32,32,32,32,32,32,32,68,114,97,119,32,112,114,105,109,105,116,105,118,101,115,32,119,105,116,104,32,108,105,110,101,32,116,121,112,101,115,32,62,61,32,48,32,105,110,32,99,111,108,111,117,114,32,40,115,101,112,46,32,50,45,55,41,10,9,9,109,111,110,111,99,104,114,111,109,101,32,32,32,32,32,32,68,114,97,119,32,112,114,105,109,105,116,105,118,101,115,32,105,110,32,98,108,97,99,107,32,40,115,101,112,46,32,48,41,10,10,9,9,112,111,108,121,108,105,110,101,32,47,32,32,32,32,32,32,68,114,97,119,32,108,105,110,101,115,32,97,115,32,99,111,110,116,105,110,117,111,117,115,32,99,117,114,118,101,115,10,9,9,118,101,99,116,111,114,115,32,32,32,32,32,32,32,32,32,68,114,97,119,32,108,105,110,101,115,32,97,115,32,99,111,108,108,101,99,116,105,111,110,115,32,111,102,32,118,101,99,116,111,114,115,10,10,9,9,104,101,108,112,32,47,32,63,32,32,32,32,32,32,32,32,80,114,105,110,116,32,115,104,111,114,116,32,117,115,97,103,101,32,100,101,115,99,114,105,112,116,105,111,110,32,111,110,32,115,116,100,101,114,114,10,0,0,0,0,0,0,0,78,117,109,98,101,114,32,111,102,32,112,105,120,101,108,115,32,99,97,110,110,111,116,32,98,101,32,102,97,99,116,111,114,101,100,32,105,110,116,111,32,105,110,116,101,103,101,114,115,32,109,97,116,99,104,105,110,103,32,103,114,105,100,46,32,78,32,61,32,37,100,32,32,75,32,61,32,37,100,0,67,101,110,116,101,114,95,50,0,0,0,0,0,0,0,0,60,77,73,70,70,105,108,101,32,51,46,48,48,62,32,35,32,71,101,110,101,114,97,116,101,100,32,98,121,32,103,110,117,112,108,111,116,32,118,101,114,115,105,111,110,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,59,32,105,100,101,110,116,105,102,105,101,115,32,116,104,105,115,32,97,115,32,97,32,77,73,70,32,102,105,108,101,10,35,10,35,32,115,104,111,119,32,98,111,114,100,101,114,115,10,60,68,111,99,117,109,101,110,116,10,60,68,66,111,114,100,101,114,115,79,110,32,89,101,115,62,10,62,10,35,32,83,101,116,32,97,32,100,101,102,97,117,108,116,32,112,101,110,32,112,97,116,116,101,114,110,44,32,112,101,110,32,119,105,100,116,104,44,32,117,110,105,116,32,97,110,100,32,102,111,110,116,32,102,111,114,32,115,117,98,115,101,113,117,101,110,116,32,111,98,106,101,99,116,115,10,60,80,101,110,32,48,62,10,60,70,105,108,108,32,49,53,62,10,60,80,101,110,87,105,100,116,104,32,48,46,53,32,112,116,62,10,60,83,101,112,97,114,97,116,105,111,110,32,48,62,10,60,85,110,105,116,115,32,85,99,109,62,10,60,70,111,110,116,67,97,116,97,108,111,103,10,9,60,70,111,110,116,32,60,70,84,97,103,32,96,37,115,39,62,60,70,70,97,109,105,108,121,32,96,84,105,109,101,115,39,62,60,70,83,105,122,101,32,37,100,62,60,70,80,108,97,105,110,32,89,101,115,62,62,10,9,60,70,111,110,116,32,60,70,84,97,103,32,96,37,115,39,62,60,70,70,97,109,105,108,121,32,96,90,97,112,102,68,105,110,103,98,97,116,115,39,62,60,70,83,105,122,101,32,55,46,48,32,112,116,62,60,70,80,108,97,105,110,32,89,101,115,62,62,10,9,60,70,111,110,116,32,60,70,84,97,103,32,96,37,115,39,62,60,70,70,97,109,105,108,121,32,96,83,121,109,98,111,108,39,62,60,70,83,105,122,101,32,53,46,48,32,112,116,62,60,70,80,108,97,105,110,32,89,101,115,62,62,10,62,10,35,10,0,0,0,0,0,0,35,10,35,32,69,110,100,32,111,102,32,77,73,70,70,105,108,101,10,0,0,0,0,0,62,10,35,32,69,110,100,32,111,102,32,70,114,97,109,101,32,110,117,109,98,101,114,32,37,100,10,35,10,0,0,0,37,115,95,37,115,95,37,115,0,0,0,0,0,0,0,0,9,60,71,114,111,117,112,10,9,9,60,73,68,32,37,100,62,10,9,62,10,0,0,0,102,105,103,0,0,0,0,0,9,35,10,9,35,32,71,114,111,117,112,32,116,104,101,32,116,104,101,32,111,98,106,101,99,116,115,32,105,110,32,103,114,111,117,112,115,32,116,111,32,109,97,107,101,32,116,104,101,32,99,104,97,114,116,32,101,97,115,105,101,114,32,116,111,32,109,97,110,105,112,117,108,97,116,101,10,9,35,32,97,102,116,101,114,32,105,116,39,115,32,105,109,112,111,114,116,101,100,32,105,110,116,111,32,70,114,97,109,101,77,97,107,101,114,46,10,0,0,0,110,111,120,116,105,36,99,115,0,0,0,0,0,0,0,0,35,10,35,32,70,114,97,109,101,32,110,117,109,98,101,114,32,37,100,32,119,105,116,104,32,112,108,111,116,32,111,102,32,103,114,97,112,104,105,99,115,10,60,70,114,97,109,101,10,9,60,80,101,110,32,49,53,62,10,9,60,70,105,108,108,32,49,53,62,10,9,60,80,101,110,87,105,100,116,104,32,32,48,46,53,32,112,116,62,10,9,60,83,101,112,97,114,97,116,105,111,110,32,48,62,10,9,60,66,82,101,99,116,32,50,46,48,48,48,32,37,46,51,102,32,37,46,51,102,32,37,46,51,102,62,10,9,60,78,83,79,102,102,115,101,116,32,32,48,46,48,48,48,62,10,9,60,66,76,79,102,102,115,101,116,32,32,48,46,48,48,48,62,10,0,0,9,115,105,122,101,32,105,115,32,115,99,97,108,101,100,32,98,121,32,37,103,44,37,103,10,0,0,0,0,0,0,0,112,111,108,121,103,111,110,32,118,101,114,116,101,120,0,0,77,73,70,32,100,114,105,118,101,114,0,0,0,0,0,0,115,101,116,32,115,105,122,101,32,114,97,116,105,111,32,45,49,0,0,0,0,0,0,0,32,60,80,101,110,32,37,100,62,32,0,0,0,0,0,0,32,60,70,111,110,116,32,60,70,83,101,112,97,114,97,116,105,111,110,32,37,100,62,62,32,0,0,0,0,0,0,0,76,97,115,116,32,112,111,105,110,116,32,105,110,32,116,104,101,32,98,105,110,97,114,121,32,102,105,108,101,32,100,105,100,32,110,111,116,32,109,97,116,99,104,32,116,104,101,32,115,112,101,99,105,102,105,101,100,32,96,117,115,105,110,103,96,32,99,111,108,117,109,110,115,0,0,0,0,0,0,0,32,60,83,101,112,97,114,97,116,105,111,110,32,37,100,62,32,0,0,0,0,0,0,0,32,60,80,101,110,87,105,100,116,104,32,48,46,49,32,112,116,62,32,0,0,0,0,0,32,60,70,111,110,116,32,60,70,83,101,112,97,114,97,116,105,111,110,32,48,62,62,32,0,0,0,0,0,0,0,0,32,60,80,101,110,87,105,100,116,104,32,49,46,48,32,112,116,62,32,0,0,0,0,0,71,80,86,65,76,95,68,65,84,65,0,0,0,0,0,0,120,94,52,0,0,0,0,0,32,60,83,101,112,97,114,97,116,105,111,110,32,48,62,32,0,0,0,0,0,0,0,0,69,110,104,97,110,99,101,100,32,77,101,116,97,102,105,108,101,32,102,111,114,109,97,116,0,0,0,0,0,0,0,0,9,9,60,84,76,79,114,105,103,105,110,32,32,37,46,51,102,32,37,46,51,102,62,32,37,115,32,60,65,110,103,108,101,32,37,100,62,32,60,83,116,114,105,110,103,32,96,37,115,39,62,10,9,62,10,0,120,116,105,36,99,115,0,0,9,60,84,101,120,116,76,105,110,101,32,60,71,114,111,117,112,73,68,32,37,100,62,32,37,115,32,37,115,32,37,115,32,37,115,10,0,0,0,0,9,111,114,105,103,105,110,32,105,115,32,115,101,116,32,116,111,32,37,103,44,37,103,10,0,0,0,0,0,0,0,0,69,120,112,101,99,116,105,110,103,32,97,114,99,32,91,60,98,101,103,105,110,62,58,60,101,110,100,62,93,0,0,0,97,116,32,0,0,0,0,0,32,60,84,76,65,108,105,103,110,109,101,110,116,32,82,105,103,104,116,62,32,0,0,0,96,98,117,105,108,116,105,110,45,116,111,103,103,108,101,45,114,97,116,105,111,96,0,0,32,60,84,76,65,108,105,103,110,109,101,110,116,32,67,101,110,116,101,114,62,32,0,0,45,86,0,0,0,0,0,0,32,60,84,76,65,108,105,103,110,109,101,110,116,32,76,101,102,116,62,32,0,0,0,0,110,111,116,32,101,110,111,117,103,104,32,109,101,109,111,114,121,32,116,111,32,99,114,101,97,116,101,32,118,101,99,116,111,114,0,0,0,0,0,0,9,60,80,111,108,121,76,105,110,101,32,60,71,114,111,117,112,73,68,32,37,100,62,32,37,115,32,37,115,32,37,115,32,60,70,105,108,108,32,49,53,62,10,0,0,0,0,0,9,9,60,70,111,110,116,10,9,9,9,60,70,84,97,103,32,96,37,115,39,62,10,9,9,62,10,0,0,0,0,0,9,62,10,0,0,0,0,0,9,9,60,84,76,79,114,105,103,105,110,32,32,37,46,51,102,32,37,46,51,102,62,32,37,115,32,60,83,116,114,105,110,103,32,96,37,99,39,62,10,0,0,0,0,0,0,0,76,79,71,0,0,0,0,0,9,60,84,101,120,116,76,105,110,101,32,60,71,114,111,117,112,73,68,32,37,100,62,32,37,115,10,0,0,0,0,0,101,109,102,0,0,0,0,0,9,9,60,83,104,97,112,101,82,101,99,116,32,37,46,51,102,32,37,46,51,102,32,37,46,51,102,32,37,46,51,102,62,10,0,0,0,0,0,0,99,98,108,36,97,98,101,108,0,0,0,0,0,0,0,0,9,60,82,101,99,116,97,110,103,108,101,32,60,71,114,111,117,112,73,68,32,37,100,62,32,37,115,10,0,0,0,0,9,116,101,114,109,105,110,97,108,32,116,121,112,101,32,105,115,32,117,110,107,110,111,119,110,10,0,0,0,0,0,0,58,0,0,0,0,0,0,0,114,109,97,114,103,105,110,0,10,9,62,10,0,0,0,0,110,101,120,116,32,122,111,111,109,46,10,0,0,0,0,0,60,80,111,105,110,116,32,32,37,46,51,102,32,37,46,51,102,62,32,0,0,0,0,0,9,9,60,78,117,109,80,111,105,110,116,115,32,37,100,62,32,0,0,0,0,0,0,0,103,112,98,105,110,97,114,121,32,109,97,116,114,105,120,32,114,111,119,0,0,0,0,0,9,9,60,70,105,108,108,32,37,100,62,10,0,0,0,0,9,60,80,111,108,121,103,111,110,32,60,71,114,111,117,112,73,68,32,37,100,62,10,0,110,111,98,97,99,107,103,36,114,111,117,110,100,0,0,0,110,111,97,100,111,98,101,36,103,108,121,112,104,110,97,109,101,115,0,0,0,0,0,0,77,65,88,0,0,0,0,0,97,100,111,98,101,36,103,108,121,112,104,110,97,109,101,115,0,0,0,0,0,0,0,0,100,120,102,45,102,105,108,101,32,102,111,114,32,65,117,116,111,67,97,100,32,40,100,101,102,97,117,108,116,32,115,105,122,101,32,49,50,48,120,56,48,41,0,0,0,0,0,0,110,101,119,36,115,116,121,108,101,0,0,0,0,0,0,0,122,108,36,97,98,101,108,0,111,108,100,36,115,116,121,108,101,0,0,0,0,0,0,0,32,32,32,116,101,114,109,105,110,97,108,32,116,121,112,101,32,105,115,32,37,115,32,37,115,10,0,0,0,0,0,0,65,110,103,108,101,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,0,0,0,0,108,109,97,114,103,105,110,0,110,111,97,36,117,120,102,105,108,101,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,122,111,111,109,45,110,101,120,116,96,32,103,111,32,116,111,32,110,101,120,116,32,122,111,111,109,32,105,110,32,116,104,101,32,122,111,111,109,32,115,116,97,99,107,0,0,0,97,36,117,120,102,105,108,101,0,0,0,0,0,0,0,0,110,36,111,114,111,116,97,116,101,0,0,0,0,0,0,0,66,105,110,97,114,121,32,100,97,116,97,32,116,121,112,101,32,117,110,107,110,111,119,110,0,0,0,0,0,0,0,0,112,97,108,102,36,117,110,99,112,97,114,97,109,0,0,0,110,111,102,111,110,116,102,36,105,108,101,115,0,0,0,0,9,102,111,110,116,112,97,116,104,32,105,115,32,0,0,0,102,111,110,116,102,36,105,108,101,0,0,0,0,0,0,0,32,107,77,71,84,80,69,90,89,0,0,0,0,0,0,0,114,111,117,36,110,100,101,100,0,0,0,0,0,0,0,0,10,9,100,117,109,109,121,32,118,97,114,105,97,98,108,101,32,105,115,32,120,32,102,111,114,32,99,117,114,118,101,115,44,32,120,47,121,32,102,111,114,32,115,117,114,102,97,99,101,115,10,0,0,0,0,0,46,37,48,42,100,0,0,0,77,73,78,0,0,0,0,0,100,101,102,97,117,108,116,112,36,108,101,120,0,0,0,0,100,120,102,0,0,0,0,0,100,117,36,112,108,101,120,0,121,50,108,36,97,98,101,108,0,0,0,0,0,0,0,0,115,105,36,109,112,108,101,120,0,0,0,0,0,0,0,0])
+.concat([97,114,99,0,0,0,0,0,98,109,97,114,103,105,110,0,99,111,108,111,117,114,116,36,101,120,116,0,0,0,0,0,112,114,101,118,105,111,117,115,32,122,111,111,109,46,10,0,99,111,108,111,114,116,36,101,120,116,0,0,0,0,0,0,98,36,108,97,99,107,116,101,120,116,0,0,0,0,0,0,67,111,117,108,100,110,39,116,32,115,108,117,114,112,32,37,108,100,32,98,121,116,101,115,32,40,114,101,116,117,114,110,32,119,97,115,32,37,122,100,41,10,0,0,0,0,0,0,100,101,102,36,97,117,108,116,0,0,0,0,0,0,0,0,109,36,111,110,111,99,104,114,111,109,101,0,0,0,0,0,116,104,105,115,0,0,0,0,120,111,114,0,0,0,0,0,101,112,36,115,102,0,0,0,32,102,111,110,116,115,99,97,108,101,32,37,51,46,49,102,32,0,0,0,0,0,0,0,37,103,32,0,0,0,0,0,71,80,86,65,76,0,0,0,34,37,115,34,32,37,103,32,0,0,0,0,0,0,0,0,97,115,99,105,105,32,97,114,116,32,102,111,114,32,97,110,121,116,104,105,110,103,32,116,104,97,116,32,112,114,105,110,116,115,32,116,101,120,116,0,34,37,115,34,32,37,103,37,115,32,0,0,0,0,0,0,121,108,36,97,98,101,108,0,100,102,95,114,101,97,100,98,105,110,97,114,121,32,115,108,117,114,112,101,114,0,0,0,112,97,36,117,115,101,0,0,32,37,115,97,100,111,98,101,103,108,121,112,104,110,97,109,101,115,32,92,10,32,32,32,0,0,0,0,0,0,0,0,114,97,100,105,117,115,0,0,111,117,116,36,112,117,116,0,116,109,97,114,103,105,110,0,110,111,104,101,97,100,101,114,32,0,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,122,111,111,109,45,112,114,101,118,105,111,117,115,96,32,103,111,32,116,111,32,112,114,101,118,105,111,117,115,32,122,111,111,109,32,105,110,32,116,104,101,32,122,111,111,109,32,115,116,97,99,107,0,0,0,104,101,97,100,101,114,32,34,37,115,34,32,0,0,0,0,110,111,97,117,120,102,105,108,101,0,0,0,0,0,0,0,97,117,120,102,105,108,101,0,37,115,32,37,115,32,0,0,32,32,32,112,97,108,102,117,110,99,112,97,114,97,109,32,37,100,44,37,103,32,92,10,32,32,32,0,0,0,0,0,45,66,65,67,75,87,65,82,68,83,95,67,79,77,80,65,84,73,66,73,76,73,84,89,32,32,0,0,0,0,0,0,32,32,32,110,111,98,97,99,107,103,114,111,117,110,100,32,92,10,0,0,0,0,0,0,71,80,86,65,76,95,84,69,82,77,95,89,83,73,90,69,0,0,0,0,0,0,0,0,32,32,32,98,97,99,107,103,114,111,117,110,100,32,34,35,37,48,50,120,37,48,50,120,37,48,50,120,34,32,92,10,0,0,0,0,0,0,0,0,100,117,109,98,0,0,0,0,110,111,99,108,105,112,0,0,120,50,108,36,97,98,101,108,0,0,0,0,0,0,0,0,99,108,105,112,0,0,0,0,116,105,99,115,32,97,114,101,32,105,110,32,37,115,32,111,102,32,112,108,111,116,10,0,99,101,110,36,116,101,114,0,111,117,116,115,105,100,101,0,99,111,108,111,114,116,101,120,116,0,0,0,0,0,0,0,98,108,97,99,107,116,101,120,116,0,0,0,0,0,0,0,108,101,118,101,108,100,101,102,97,117,108,116,0,0,0,0,83,99,97,110,32,115,105,122,101,32,111,102,32,109,97,116,114,105,120,32,105,115,32,122,101,114,111,0,0,0,0,0,115,101,116,32,97,117,116,111,115,99,97,108,101,32,37,115,102,105,120,109,105,110,10,0,108,101,118,101,108,49,0,0,84,104,105,115,32,112,108,111,116,32,115,116,121,108,101,32,105,115,32,111,110,108,121,32,102,111,114,32,100,97,116,97,102,105,108,101,115,32,44,32,114,101,118,101,114,116,105,110,103,32,116,111,32,34,112,111,105,110,116,115,34,0,0,0,32,32,32,37,115,32,37,115,32,37,115,32,92,10,32,32,32,37,115,32,100,97,115,104,108,101,110,103,116,104,32,37,46,49,102,32,108,105,110,101,119,105,100,116,104,32,37,46,49,102,32,37,115,32,37,115,32,92,10,0,0,0,0,0,32,97,117,120,102,105,108,101,0,0,0,0,0,0,0,0,115,121,110,116,97,120,58,32,32,103,110,117,112,108,111,116,32,45,101,32,34,99,111,109,109,97,110,100,115,34,10,0,110,111,114,111,116,97,116,101,0,0,0,0,0,0,0,0,71,80,86,65,76,95,84,69,82,77,95,88,83,73,90,69,0,0,0,0,0,0,0,0,114,111,116,97,116,101,0,0,114,101,102,114,101,115,104,32,110,111,116,32,112,111,115,115,105,98,108,101,32,97,110,100,32,114,101,112,108,111,116,32,105,115,32,100,105,115,97,98,108,101,100,0,0,0,0,0,69,80,83,32,102,111,114,109,97,116,32,102,111,114,32,67,111,114,101,108,68,82,65,87,0,0,0,0,0,0,0,0,100,101,102,97,117,108,116,112,108,101,120,0,0,0,0,0,120,108,36,97,98,101,108,0,67,108,101,97,114,0,0,0,115,105,109,112,108,101,120,0,9,120,121,112,108,97,110,101,32,116,105,99,115,108,101,118,101,108,32,105,115,32,37,103,10,0,0,0,0,0,0,0,69,120,112,101,99,116,105,110,103,32,116,111,32,111,114,32,114,116,111,0,0,0,0,0,105,110,115,105,100,101,0,0,100,117,112,108,101,120,0,0,108,111,97,100,47,101,118,97,108,32,110,101,115,116,101,100,32,116,111,111,32,100,101,101,112,108,121,0,0,0,0,0,59,32,115,101,116,32,120,50,114,91,37,32,35,103,58,37,32,35,103,93,59,32,115,101,116,32,121,50,114,91,37,32,35,103,58,37,32,35,103,93,0,0,0,0,0,0,0,0,110,111,101,110,104,97,110,99,101,100,0,0,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,110,111,110,45,83,84,82,73,78,71,32,97,114,103,117,109,101,110,116,32,116,111,32,115,116,114,115,116,114,116,0,37,115,32,37,115,32,37,115,32,92,10,0,0,0,0,0,100,102,95,110,111,95,98,105,110,95,99,111,108,115,0,0,32,102,111,110,116,102,105,108,101,32,34,37,115,34,0,0,112,115,95,102,111,110,116,102,105,108,101,95,99,104,97,114,0,0,0,0,0,0,0,0,37,115,44,37,103,0,0,0,111,102,32,102,111,110,116,32,110,97,109,101,0,0,0,0,71,80,86,65,76,95,84,69,82,77,95,89,77,65,88,0,116,101,114,109,105,110,97,108,32,37,115,32,100,111,101,115,32,110,111,116,32,97,108,108,111,119,32,115,112,101,99,105,102,105,99,97,116,105,111,110,32,37,115,0,0,0,0,0,99,111,114,101,108,0,0,0,73,32,119,105,108,108,32,116,114,121,32,116,111,32,102,105,120,32,105,116,32,98,117,116,32,116,104,105,115,32,109,97,121,32,110,111,116,32,119,111,114,107,46,0,0,0,0,0,99,98,100,97,36,116,97,0,73,108,108,101,103,97,108,32,99,104,97,114,97,99,116,101,114,115,32,105,110,32,80,111,115,116,83,99,114,105,112,116,32,102,111,110,116,32,110,97,109,101,46,0,0,0,0,0,9,120,121,112,108,97,110,101,32,105,110,116,101,114,99,101,112,116,115,32,122,32,97,120,105,115,32,97,116,32,37,103,10,0,0,0,0,0,0,0,114,101,108,97,116,105,118,101,32,99,111,111,114,100,105,110,97,116,101,115,32,109,117,115,116,32,109,97,116,99,104,32,105,110,32,116,121,112,101,0,115,101,116,32,107,101,121,32,0,0,0,0,0,0,0,0,40,41,91,93,123,125,124,32,0,0,0,0,0,0,0,0,115,101,116,32,120,114,91,37,46,49,50,103,58,37,46,49,50,103,93,59,32,115,101,116,32,121,114,91,37,46,49,50,103,58,37,46,49,50,103,93,0,0,0,0,0,0,0,0,97,108,108,111,119,101,100,32,100,101,118,105,97,116,105,111,110,32,109,117,115,116,32,98,101,32,60,32,49,0,0,0,67,97,110,39,116,32,100,101,108,101,116,101,32,70,111,110,116,32,102,105,108,101,110,97,109,101,32,39,37,115,39,0,100,102,95,109,97,120,95,98,105,110,105,110,102,111,95,99,111,108,115,32,62,32,100,102,95,110,111,95,98,105,110,95,99,111,108,115,0,0,0,0,37,32,103,0,0,0,0,0,110,101,119,95,112,115,95,102,111,110,116,102,105,108,101,45,62,102,111,110,116,102,105,108,101,95,110,97,109,101,0,0,110,101,119,95,112,115,95,102,111,110,116,102,105,108,101,0,115,101,116,32,104,105,100,100,101,110,51,100,32,37,115,32,111,102,102,115,101,116,32,37,100,32,116,114,105,97,110,103,108,101,112,97,116,116,101,114,110,32,37,108,100,32,117,110,100,101,102,105,110,101,100,32,37,100,32,37,115,97,108,116,100,105,97,103,111,110,97,108,32,37,115,98,101,110,116,111,118,101,114,10,0,0,0,0,100,101,108,36,101,116,101,0,97,100,100,0,0,0,0,0,71,80,86,65,76,95,84,69,82,77,95,89,77,73,78,0,67,97,110,110,111,116,32,117,115,101,32,97,117,120,32,102,105,108,101,32,111,110,32,115,116,100,111,117,116,46,32,83,119,105,116,99,104,105,110,103,32,111,102,102,32,97,117,120,102,105,108,101,32,111,112,116,105,111,110,46,10,0,0,0,67,111,109,112,117,116,101,114,32,71,114,97,112,104,105,99,115,32,77,101,116,97,102,105,108,101,0,0,0,0,0,0,84,117,114,110,105,110,103,32,111,102,102,32,97,117,120,102,105,108,101,32,111,112,116,105,111,110,10,0,0,0,0,0,122,100,97,36,116,97,0,0,67,97,110,110,111,116,32,109,97,107,101,32,80,111,115,116,83,99,114,105,112,116,32,102,105,108,101,32,110,97,109,101,32,102,114,111,109,32,37,115,10,0,0,0,0,0,0,0,85,110,107,110,111,119,110,32,109,105,110,105,116,105,99,32,116,121,112,101,32,105,110,32,115,104,111,119,95,109,116,105,99,115,40,41,0,0,0,0,114,116,111,0,0,0,0,0,67,97,110,110,111,116,32,111,112,101,110,32,97,117,120,32,102,105,108,101,32,37,115,32,102,111,114,32,111,117,116,112,117,116,46,32,83,119,105,116,99,104,105,110,103,32,111,102,102,32,97,117,120,102,105,108,101,32,111,112,116,105,111,110,46,10,0,0,0,0,0,0,117,110,122,111,111,109,46,10,0,0,0,0,0,0,0,0,112,115,108,97,116,101,120,32,97,117,120,32,102,105,108,101,110,97,109,101,0,0,0,0,109,97,120,32,60,61,32,77,65,88,68,65,84,65,67,79,76,83,0,0,0,0,0,0,73,109,97,103,101,32,103,114,105,100,32,109,117,115,116,32,98,101,32,97,116,32,108,101,97,115,116,32,50,32,120,32,50,46,10,10,0,0,0,0,101,112,115,108,97,116,101,120,32,84,101,88,32,102,105,108,101,110,97,109,101,0,0,0,111,112,101,110,32,111,102,32,112,111,115,116,115,99,105,112,116,32,111,117,116,112,117,116,32,102,105,108,101,32,37,115,32,102,97,105,108,101,100,0,67,101,110,116,101,114,95,49,0,0,0,0,0,0,0,0,46,37,115,0,0,0,0,0,45,105,110,99,46,37,115,0,71,80,86,65,76,95,84,69,82,77,95,88,77,65,88,0,45,45,45,32,114,101,111,112,101,110,32,102,97,105,108,101,100,0,0,0,0,0,0,0,99,103,109,0,0,0,0,0,82,101,115,101,116,116,105,110,103,32,112,114,105,109,97,114,121,32,111,117,116,112,117,116,32,102,105,108,101,32,116,111,32,37,115,44,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,80,111,115,116,83,99,114,105,112,116,32,111,117,116,112,117,116,32,116,111,32,37,115,0,0,121,50,100,97,36,116,97,0,9,109,105,110,111,114,32,37,115,116,105,99,115,32,97,114,101,32,100,114,97,119,110,32,119,105,116,104,32,37,100,32,115,117,98,105,110,116,101,114,118,97,108,115,32,98,101,116,119,101,101,110,32,109,97,106,111,114,32,120,116,105,99,32,109,97,114,107,115,10,0,0,116,111,0,0,0,0,0,0,115,101,116,32,107,101,121,32,116,105,116,108,101,32,34,37,115,34,0,0,0,0,0,0,103,105,118,101,32,116,104,101,32,116,101,120,32,102,105,108,101,110,97,109,101,32,97,115,32,111,117,116,112,117,116,0,96,98,117,105,108,116,105,110,45,117,110,122,111,111,109,96,0,0,0,0,0,0,0,0,70,111,114,32,101,112,115,108,97,116,101,120,32,115,116,97,110,100,97,108,111,110,101,32,109,111,100,101,44,32,121,111,117,32,104,97,118,101,32,116,111,32,37,115,0,0,0,0,46,69,80,83,0,0,0,0,100,97,116,97,95,102,112,32,33,61,32,78,85,76,76,0,46,101,112,115,0,0,0,0,101,112,115,108,97,116,101,120,32,101,112,115,32,102,105,108,101,110,97,109,101,0,0,0,101,112,115,108,97,116,101,120,32,111,117,116,112,117,116,32,102,105,108,101,32,110,97,109,101,32,109,117,115,116,32,98,101,32,111,102,32,116,104,101,32,102,111,114,109,32,102,105,108,101,110,97,109,101,46,120,120,120,0,0,0,0,0,0,92,112,117,116,40,48,44,48,41,123,92,99,111,108,111,114,98,111,120,123,103,112,66,97,99,107,103,114,111,117,110,100,125,123,92,109,97,107,101,98,111,120,40,37,46,50,102,44,37,46,50,102,41,91,93,123,125,125,125,37,37,10,0,0,71,80,86,65,76,95,84,69,82,77,95,88,77,73,78,0,100,117,112,32,109,117,108,32,100,117,112,32,109,117,108,0,92,100,101,102,105,110,101,99,111,108,111,114,123,103,112,66,97,99,107,103,114,111,117,110,100,125,123,114,103,98,125,123,37,46,51,102,44,32,37,46,51,102,44,32,37,46,51,102,125,37,37,10,0,0,0,0,72,84,77,76,32,67,97,110,118,97,115,32,111,98,106,101,99,116,0,0,0,0,0,0,32,32,92,115,101,116,108,101,110,103,116,104,123,92,117,110,105,116,108,101,110,103,116,104,125,123,37,46,52,102,98,112,125,37,37,10,32,32,92,98,101,103,105,110,123,112,105,99,116,117,114,101,125,40,37,46,50,102,44,37,46,50,102,41,37,37,10,0,0,0,0,0,121,100,97,36,116,97,0,0,32,32,32,32,92,101,108,115,101,10,32,32,32,32,32,32,37,32,103,114,97,121,10,32,32,32,32,32,32,92,100,101,102,92,99,111,108,111,114,114,103,98,35,49,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,100,101,102,92,99,111,108,111,114,103,114,97,121,35,49,123,92,99,111,108,111,114,91,103,114,97,121,93,123,35,49,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,119,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,119,104,105,116,101,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,98,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,97,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,48,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,49,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,50,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,51,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,52,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,53,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,54,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,55,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,56,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,92,102,105,10,32,32,92,102,105,10,0,0,0,9,109,105,110,111,114,32,37,115,116,105,99,115,32,97,114,101,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,10,0,0,0,0,0,0,0,102,114,111,109,0,0,0,0,37,115,115,101,116,32,114,97,120,105,115,10,0,0,0,0,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,48,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,49,44,48,44,48,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,49,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,48,44,49,44,48,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,50,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,48,44,48,44,49,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,51,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,49,44,48,44,49,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,52,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,48,44,49,44,49,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,53,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,49,44,49,44,48,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,54,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,48,44,48,44,48,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,55,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,49,44,48,46,51,44,48,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,56,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,48,46,53,44,48,46,53,44,48,46,53,125,125,37,10,0,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,114,111,116,97,116,101,45,114,105,103,104,116,96,32,111,110,108,121,32,102,111,114,32,115,112,108,111,116,115,59,32,60,115,104,105,102,116,62,32,105,110,99,114,101,97,115,101,115,32,97,109,111,117,110,116,0,0,0,0,0,0,0,0,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,48,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,49,44,48,44,48,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,49,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,48,44,48,44,49,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,50,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,48,44,49,44,49,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,51,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,91,114,103,98,93,123,49,44,48,44,49,125,125,37,10,0,0,0,0,32,32,92,105,102,71,80,98,108,97,99,107,116,101,120,116,10,32,32,32,32,37,32,110,111,32,116,101,120,116,99,111,108,111,114,32,97,116,32,97,108,108,10,32,32,32,32,92,100,101,102,92,99,111,108,111,114,114,103,98,35,49,123,125,37,10,32,32,32,32,92,100,101,102,92,99,111,108,111,114,103,114,97,121,35,49,123,125,37,10,32,32,92,101,108,115,101,10,32,32,32,32,37,32,103,114,97,121,32,111,114,32,99,111,108,111,114,63,10,32,32,32,32,92,105,102,71,80,99,111,108,111,114,10,32,32,32,32,32,32,92,100,101,102,92,99,111,108,111,114,114,103,98,35,49,123,92,99,111,108,111,114,91,114,103,98,93,123,35,49,125,125,37,10,32,32,32,32,32,32,92,100,101,102,92,99,111,108,111,114,103,114,97,121,35,49,123,92,99,111,108,111,114,91,103,114,97,121,93,123,35,49,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,119,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,119,104,105,116,101,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,98,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,32,32,32,32,32,32,92,101,120,112,97,110,100,97,102,116,101,114,92,100,101,102,92,99,115,110,97,109,101,32,76,84,97,92,101,110,100,99,115,110,97,109,101,123,92,99,111,108,111,114,123,98,108,97,99,107,125,125,37,10,0,0,0,0,9,32,32,37,115,0,0,0,32,32,92,109,97,107,101,97,116,108,101,116,116,101,114,10,32,32,92,112,114,111,118,105,100,101,99,111,109,109,97,110,100,92,99,111,108,111,114,91,50,93,91,93,123,37,37,10,32,32,32,32,92,71,101,110,101,114,105,99,69,114,114,111,114,123,40,103,110,117,112,108,111,116,41,32,92,115,112,97,99,101,92,115,112,97,99,101,92,115,112,97,99,101,92,64,115,112,97,99,101,115,125,123,37,37,10,32,32,32,32,32,32,80,97,99,107,97,103,101,32,99,111,108,111,114,32,110,111,116,32,108,111,97,100,101,100,32,105,110,32,99,111,110,106,117,110,99,116,105,111,110,32,119,105,116,104,10,32,32,32,32,32,32,116,101,114,109,105,110,97,108,32,111,112,116,105,111,110,32,96,99,111,108,111,117,114,116,101,120,116,39,37,37,10,32,32,32,32,125,123,83,101,101,32,116,104,101,32,103,110,117,112,108,111,116,32,100,111,99,117,109,101,110,116,97,116,105,111,110,32,102,111,114,32,101,120,112,108,97,110,97,116,105,111,110,46,37,37,10,32,32,32,32,125,123,69,105,116,104,101,114,32,117,115,101,32,39,98,108,97,99,107,116,101,120,116,39,32,105,110,32,103,110,117,112,108,111,116,32,111,114,32,108,111,97,100,32,116,104,101,32,112,97,99,107,97,103,101,10,32,32,32,32,32,32,99,111,108,111,114,46,115,116,121,32,105,110,32,76,97,84,101,88,46,125,37,37,10,32,32,32,32,92,114,101,110,101,119,99,111,109,109,97,110,100,92,99,111,108,111,114,91,50,93,91,93,123,125,37,37,10,32,32,125,37,37,10,32,32,92,112,114,111,118,105,100,101,99,111,109,109,97,110,100,92,105,110,99,108,117,100,101,103,114,97,112,104,105,99,115,91,50,93,91,93,123,37,37,10,32,32,32,32,92,71,101,110,101,114,105,99,69,114,114,111,114,123,40,103,110,117,112,108,111,116,41,32,92,115,112,97,99,101,92,115,112,97,99,101,92,115,112,97,99,101,92,64,115,112,97,99,101,115,125,123,37,37,10,32,32,32,32,32,32,80,97,99,107,97,103,101,32,103,114,97,112,104,105,99,120,32,111,114,32,103,114,97,112,104,105,99,115,32,110,111,116,32,108,111,97,100,101,100,37,37,10,32,32,32,32,125,123,83,101,101,32,116,104,101,32,103,110,117,112,108,111,116,32,100,111,99,117,109,101,110,116,97,116,105,111,110,32,102,111,114,32,101,120,112,108,97,110,97,116,105,111,110,46,37,37,10,32,32,32,32,125,123,84,104,101,32,103,110,117,112,108,111,116,32,101,112,115,108,97,116,101,120,32,116,101,114,109,105,110,97,108,32,110,101,101,100,115,32,103,114,97,112,104,105,99,120,46,115,116,121,32,111,114,32,103,114,97,112,104,105,99,115,46,115,116,121,46,125,37,37,10,32,32,32,32,92,114,101,110,101,119,99,111,109,109,97,110,100,92,105,110,99,108,117,100,101,103,114,97,112,104,105,99,115,91,50,93,91,93,123,125,37,37,10,32,32,125,37,37,10,32,32,92,112,114,111,118,105,100,101,99,111,109,109,97,110,100,92,114,111,116,97,116,101,98,111,120,91,50,93,123,35,50,125,37,37,10,32,32,92,64,105,102,117,110,100,101,102,105,110,101,100,123,105,102,71,80,99,111,108,111,114,125,123,37,37,10,32,32,32,32,92,110,101,119,105,102,92,105,102,71,80,99,111,108,111,114,10,32,32,32,32,92,71,80,99,111,108,111,114,37,115,10,32,32,125,123,125,37,37,10,32,32,92,64,105,102,117,110,100,101,102,105,110,101,100,123,105,102,71,80,98,108,97,99,107,116,101,120,116,125,123,37,37,10,32,32,32,32,92,110,101,119,105,102,92,105,102,71,80,98,108,97,99,107,116,101,120,116,10,32,32,32,32,92,71,80,98,108,97,99,107,116,101,120,116,37,115,10,32,32,125,123,125,37,37,10,32,32,37,37,32,100,101,102,105,110,101,32,97,32,92,103,64,97,100,100,116,111,64,109,97,99,114,111,32,119,105,116,104,111,117,116,32,64,32,105,110,32,116,104,101,32,110,97,109,101,58,10,32,32,92,108,101,116,92,103,112,108,103,97,100,100,116,111,109,97,99,114,111,92,103,64,97,100,100,116,111,64,109,97,99,114,111,10,32,32,37,37,32,100,101,102,105,110,101,32,101,109,112,116,121,32,116,101,109,112,108,97,116,101,115,32,102,111,114,32,97,108,108,32,99,111,109,109,97,110,100,115,32,116,97,107,105,110,103,32,116,101,120,116,58,10,32,32,92,103,100,101,102,92,103,112,108,98,97,99,107,116,101,120,116,123,125,37,37,10,32,32,92,103,100,101,102,92,103,112,108,102,114,111,110,116,116,101,120,116,123,125,37,37,10,32,32,92,109,97,107,101,97,116,111,116,104,101,114,10,0,0,0,0,0,0,0,32,32,92,115,101,108,101,99,116,102,111,110,116,10,0,0,32,32,92,102,111,110,116,115,104,97,112,101,123,37,115,125,37,37,10,0,0,0,0,0,32,32,92,102,111,110,116,115,101,114,105,101,115,123,37,115,125,37,37,10,0,0,0,0,71,80,86,65,76,95,84,69,82,77,95,87,73,78,68,79,87,73,68,0,0,0,0,0,32,32,92,102,111,110,116,102,97,109,105,108,121,123,37,115,125,37,37,10,0,0,0,0,99,97,110,118,97,115,0,0,32,32,37,37,32,69,110,99,111,100,105,110,103,32,105,110,115,105,100,101,32,116,104,101,32,112,108,111,116,46,32,32,73,110,32,116,104,101,32,104,101,97,100,101,114,32,111,102,32,121,111,117,114,32,100,111,99,117,109,101,110,116,44,32,116,104,105,115,32,101,110,99,111,100,105,110,103,10,32,32,37,37,32,115,104,111,117,108,100,32,116,111,32,100,101,102,105,110,101,100,44,32,101,46,103,46,44,32,98,121,32,117,115,105,110,103,10,32,32,37,37,32,92,117,115,101,112,97,99,107,97,103,101,91,37,115,44,60,111,116,104,101,114,32,101,110,99,111,100,105,110,103,115,62,93,123,105,110,112,117,116,101,110,99,125,10,32,32,92,105,110,112,117,116,101,110,99,111,100,105,110,103,123,37,115,125,37,37,10,0,0,0,120,50,100,97,36,116,97,0,92,98,101,103,105,110,103,114,111,117,112,10,0,0,0,0,9,109,105,110,111,114,32,37,115,116,105,99,115,32,97,114,101,32,111,102,102,32,102,111,114,32,108,105,110,101,97,114,32,115,99,97,108,101,115,10,9,109,105,110,111,114,32,37,115,116,105,99,115,32,97,114,101,32,99,111,109,112,117,116,101,100,32,97,117,116,111,109,97,116,105,99,97,108,108,121,32,102,111,114,32,108,111,103,32,115,99,97,108,101,115,10,0,0,0,0,0,0,0,0,85,110,107,110,111,119,110,32,111,98,106,101,99,116,32,116,121,112,101,0,0,0,0,0,37,0,0,0,0,0,0,0,96,98,117,105,108,116,105,110,45,114,111,116,97,116,101,45,117,112,96,32,111,110,108,121,32,102,111,114,32,115,112,108,111,116,115,59,32,60,115,104,105,102,116,62,32,105,110,99,114,101,97,115,101,115,32,97,109,111,117,110,116,0,0,0,92,109,97,107,101,97,116,108,101,116,116,101,114,10,37,37,32,83,101,108,101,99,116,32,97,110,32,97,112,112,114,111,112,114,105,97,116,101,32,100,101,102,97,117,108,116,32,100,114,105,118,101,114,32,40,102,114,111,109,32,84,101,88,76,105,118,101,32,103,114,97,112,104,105,99,115,46,99,102,103,41,10,92,98,101,103,105,110,103,114,111,117,112,10,32,32,92,99,104,97,114,100,101,102,92,120,61,48,32,37,37,10,32,32,37,37,32,99,104,101,99,107,32,112,100,102,84,101,88,10,32,32,92,64,105,102,117,110,100,101,102,105,110,101,100,123,112,100,102,111,117,116,112,117,116,125,123,125,123,37,37,10,32,32,32,32,92,105,102,99,97,115,101,92,112,100,102,111,117,116,112,117,116,10,32,32,32,32,92,101,108,115,101,10,32,32,32,32,32,32,92,99,104,97,114,100,101,102,92,120,61,49,32,37,37,10,32,32,32,32,92,102,105,10,32,32,125,37,37,10,32,32,37,37,32,99,104,101,99,107,32,86,84,101,88,10,32,32,92,64,105,102,117,110,100,101,102,105,110,101,100,123,79,112,77,111,100,101,125,123,125,123,37,37,10,32,32,32,32,92,99,104,97,114,100,101,102,92,120,61,50,32,37,37,10,32,32,125,37,37,10,92,101,120,112,97,110,100,97,102,116,101,114,92,101,110,100,103,114,111,117,112,10,92,105,102,99,97,115,101,92,120,10,32,32,37,37,32,100,101,102,97,117,108,116,32,99,97,115,101,10,32,32,92,80,97,115,115,79,112,116,105,111,110,115,84,111,80,97,99,107,97,103,101,123,100,118,105,112,115,125,123,103,101,111,109,101,116,114,121,125,10,92,111,114,10,32,32,37,37,32,112,100,102,84,101,88,32,105,115,32,114,117,110,110,105,110,103,32,105,110,32,112,100,102,32,109,111,100,101,10,32,32,92,80,97,115,115,79,112,116,105,111,110,115,84,111,80,97,99,107,97,103,101,123,112,100,102,116,101,120,125,123,103,101,111,109,101,116,114,121,125,10,92,101,108,115,101,10,32,32,37,37,32,86,84,101,88,32,105,115,32,114,117,110,110,105,110,103,10,32,32,92,80,97,115,115,79,112,116,105,111,110,115,84,111,80,97,99,107,97,103,101,123,118,116,101,120,125,123,103,101,111,109,101,116,114,121,125,10,92,102,105,10,92,109,97,107,101,97,116,111,116,104,101,114,10,37,37,32,83,101,116,32,112,97,112,101,114,115,105,122,101,10,92,117,115,101,112,97,99,107,97,103,101,91,112,97,112,101,114,115,105,122,101,61,123,37,46,50,102,98,112,44,37,46,50,102,98,112,125,44,116,101,120,116,61,123,37,46,50,102,98,112,44,37,46,50,102,98,112,125,93,123,103,101,111,109,101,116,114,121,125,10,37,37,32,78,111,32,112,97,103,101,32,110,117,109,98,101,114,115,32,97,110,100,32,110,111,32,112,97,114,97,103,114,97,112,104,32,105,110,100,101,110,116,97,116,105,111,110,10,92,112,97,103,101,115,116,121,108,101,123,101,109,112,116,121,125,10,92,115,101,116,108,101,110,103,116,104,123,92,112,97,114,105,110,100,101,110,116,125,123,48,98,112,125,37,37,10,37,37,32,76,111,97,100,32,99,111,110,102,105,103,117,114,97,116,105,111,110,32,102,105,108,101,10,92,73,110,112,117,116,73,102,70,105,108,101,69,120,105,115,116,115,123,103,110,117,112,108,111,116,46,99,102,103,125,123,37,37,10,32,32,92,116,121,112,101,111,117,116,123,85,115,105,110,103,32,99,111,110,102,105,103,117,114,97,116,105,111,110,32,102,105,108,101,32,103,110,117,112,108,111,116,46,99,102,103,125,37,37,10,125,123,37,37,10,32,92,116,121,112,101,111,117,116,123,78,111,32,99,111,110,102,105,103,117,114,97,116,105,111,110,32,102,105,108,101,32,103,110,117,112,108,111,116,46,99,102,103,32,102,111,117,110,100,46,125,37,37,10,125,37,37,10,37,115,10,92,98,101,103,105,110,123,100,111,99,117,109,101,110,116,125,10,0,0,0,0,0,0,0,0,92,117,115,101,112,97,99,107,97,103,101,91,37,115,93,123,105,110,112,117,116,101,110,99,125,10,0,0,0,0,0,0,9,84,104,105,115,32,118,101,114,115,105,111,110,32,111,102,32,103,110,117,112,108,111,116,32,117,110,100,101,114,115,116,97,110,100,115,32,116,104,101,32,102,111,108,108,111,119,105,110,103,32,98,105,110,97,114,121,32,102,105,108,101,32,116,121,112,101,115,58,10,0,0,37,32,76,111,97,100,32,112,97,99,107,97,103,101,115,10,92,117,115,101,112,97,99,107,97,103,101,123,103,114,97,112,104,105,99,120,125,10,92,117,115,101,112,97,99,107,97,103,101,123,99,111,108,111,114,125,10,0,0,0,0,0,0,0,111,110,108,121,32,54,32,114,97,110,103,101,32,115,112,101,99,115,32,97,114,101,32,112,101,114,109,105,116,116,101,100,0,0,0,0,0,0,0,0,92,114,101,110,101,119,99,111,109,109,97,110,100,42,92,117,112,100,101,102,97,117,108,116,123,37,115,125,37,37,10,0,92,114,101,110,101,119,99,111,109,109,97,110,100,42,92,109,100,100,101,102,97,117,108,116,123,37,115,125,37,37,10,0,92,114,101,110,101,119,99,111,109,109,97,110,100,42,92,114,109,100,101,102,97,117,108,116,123,37,115,125,37,37,10,0,71,80,86,65,76,95,80,87,68,0,0,0,0,0,0,0,92,100,111,99,117,109,101,110,116,99,108,97,115,115,123,109,105,110,105,109,97,108,125,10,37,37,32,83,101,116,32,102,111,110,116,32,115,105,122,101,10,92,109,97,107,101,97,116,108,101,116,116,101,114,10,92,100,101,102,92,64,112,116,115,105,122,101,123,37,100,125,10,92,73,110,112,117,116,73,102,70,105,108,101,69,120,105,115,116,115,123,115,105,122,101,37,100,46,99,108,111,125,123,125,123,37,37,10,32,32,32,92,71,101,110,101,114,105,99,69,114,114,111,114,123,40,103,110,117,112,108,111,116,41,32,92,115,112,97,99,101,92,115,112,97,99,101,92,115,112,97,99,101,92,64,115,112,97,99,101,115,125,123,37,37,10,32,32,32,32,32,32,71,110,117,112,108,111,116,32,69,114,114,111,114,58,32,70,105,108,101,32,96,115,105,122,101,37,100,46,99,108,111,39,32,110,111,116,32,102,111,117,110,100,33,32,67,111,117,108,100,32,110,111,116,32,115,101,116,32,102,111,110,116,32,115,105,122,101,37,37,10,32,32,32,125,123,83,101,101,32,116,104,101,32,103,110,117,112,108,111,116,32,100,111,99,117,109,101,110,116,97,116,105,111,110,32,102,111,114,32,101,120,112,108,97,110,97,116,105,111,110,46,37,37,10,32,32,32,125,123,70,111,114,32,117,115,105,110,103,32,97,32,102,111,110,116,32,115,105,122,101,32,97,32,102,105,108,101,32,96,115,105,122,101,60,102,111,110,116,115,105,122,101,62,46,99,108,111,39,32,104,97,115,32,116,111,32,101,120,105,115,116,46,10,32,32,32,32,32,32,32,32,70,97,108,108,105,110,103,32,98,97,99,107,32,94,94,74,116,111,32,100,101,102,97,117,108,116,32,102,111,110,116,115,105,122,101,32,49,48,112,116,46,125,37,37,10,32,32,92,100,101,102,92,64,112,116,115,105,122,101,123,48,125,10,32,32,92,105,110,112,117,116,123,115,105,122,101,49,48,46,99,108,111,125,37,37,10,125,37,37,10,92,109,97,107,101,97,116,111,116,104,101,114,10,0,0,0,0,88,116,101,114,109,32,84,101,107,116,114,111,110,105,120,32,52,48,49,52,32,77,111,100,101,0,0,0,0,0,0,0,69,80,83,76,65,84,69,88,95,99,111,109,109,111,110,95,105,110,105,116,0,0,0,0,120,100,97,36,116,97,0,0,105,110,118,97,108,105,100,32,105,110,112,117,116,32,101,110,99,111,100,105,110,103,32,117,115,101,100,0,0,0,0,0,9,109,105,110,111,114,32,37,115,116,105,99,115,32,97,114,101,32,111,102,102,10,0,0,108,97,121,101,114,100,101,102,97,117,108,116,0,0,0,0,107,111,105,56,45,117,0,0,96,98,117,105,108,116,105,110,45,114,111,116,97,116,101,45,108,101,102,116,96,32,111,110,108,121,32,102,111,114,32,115,112,108,111,116,115,59,32,60,115,104,105,102,116,62,32,105,110,99,114,101,97,115,101,115,32,97,109,111,117,110,116,0,107,111,105,56,45,114,0,0,99,112,52,51,55,100,101,0,32,45,45,32,112,114,111,99,101,115,115,111,114,32,100,111,101,115,32,110,111,116,32,115,117,112,112,111,114,116,32,116,104,105,115,32,115,105,122,101,0,0,0,0,0,0,0,0,108,97,116,105,110,57,0,0,108,97,116,105,110,53,0,0,114,0,0,0,0,0,0,0,108,97,116,105,110,50,0,0,70,111,114,109,97,116,32,99,104,97,114,97,99,116,101,114,32,109,105,115,109,97,116,99,104,58,32,37,37,66,32,105,115,32,111,110,108,121,32,118,97,108,105,100,32,119,105,116,104,32,37,37,98,0,0,0,108,97,116,105,110,49,0,0,121,111,117,32,99,97,110,39,116,32,117,110,115,101,116,32,116,104,101,32,112,97,108,101,116,116,101,46,10,0,0,0,102,105,108,108,105,110,103,32,71,80,86,65,76,95,80,87,68,0,0,0,0,0,0,0,37,37,32,71,78,85,80,76,79,84,58,32,76,97,84,101,88,32,112,105,99,116,117,114,101,32,119,105,116,104,32,80,111,115,116,115,99,114,105,112,116,10,0,0,0,0,0,0,120,116,101,114,109,0,0,0,101,112,115,108,97,116,101,120,32,116,101,114,109,105,110,97,108,32,99,97,110,110,111,116,32,119,114,105,116,101,32,116,111,32,115,116,97,110,100,97,114,100,32,111,117,116,112,117,116,0,0,0,0,0,0,0,120,121,112,36,108,97,110,101,0,0,0,0,0,0,0,0,9,110,111,116,32,114,111,116,97,116,101,100,10,9,0,0,101,120,112,101,99,116,101,100,32,103,97,112,32,118,97,108,117,101,0,0,0,0,0,0,115,101,116,32,103,114,105,100,32,37,115,32,32,0,0,0,32,32,123,92,71,78,85,80,76,79,84,115,112,101,99,105,97,108,123,34,10,0,0,0,99,104,97,110,103,105,110,103,32,118,105,101,119,32,116,111,32,37,102,44,32,37,102,46,10,0,0,0,0,0,0,0,32,32,92,115,112,101,99,105,97,108,123,112,115,102,105,108,101,61,37,115,32,108,108,120,61,48,32,108,108,121,61,48,32,117,114,120,61,37,100,32,117,114,121,61,37,100,32,114,119,105,61,37,100,125,10,0,92,71,78,85,80,76,79,84,112,105,99,116,117,114,101,40,37,100,44,37,100,41,10,0,40,37,100,41,0,0,0,0,117,116,102,56,0,0,0,0,37,37,32,71,78,85,80,76,79,84,58,32,112,108,97,105,110,32,84,101,88,32,119,105,116,104,32,80,111,115,116,115,99,114,105,112,116,10,92,98,101,103,105,110,103,114,111,117,112,10,92,99,97,116,99,111,100,101,96,92,64,61,49,49,92,114,101,108,97,120,10,92,100,101,102,92,71,78,85,80,76,79,84,115,112,101,99,105,97,108,123,37,37,10,32,32,92,100,101,102,92,100,111,35,35,49,123,92,99,97,116,99,111,100,101,96,35,35,49,61,49,50,92,114,101,108,97,120,125,92,100,111,115,112,101,99,105,97,108,115,10,32,32,92,99,97,116,99,111,100,101,96,92,123,61,49,92,99,97,116,99,111,100,101,96,92,125,61,50,92,99,97,116,99,111,100,101,92,37,37,61,49,52,92,114,101,108,97,120,92,115,112,101,99,105,97,108,125,37,37,10,37,37,10,92,101,120,112,97,110,100,97,102,116,101,114,92,105,102,120,92,99,115,110,97,109,101,32,71,78,85,80,76,79,84,112,105,99,116,117,114,101,92,101,110,100,99,115,110,97,109,101,92,114,101,108,97,120,10,32,32,92,99,115,110,97,109,101,32,110,101,119,100,105,109,101,110,92,101,110,100,99,115,110,97,109,101,92,71,78,85,80,76,79,84,117,110,105,116,10,32,32,92,103,100,101,102,92,71,78,85,80,76,79,84,112,105,99,116,117,114,101,40,35,49,44,35,50,41,123,92,118,98,111,120,32,116,111,35,50,92,71,78,85,80,76,79,84,117,110,105,116,92,98,103,114,111,117,112,10,32,32,32,32,92,100,101,102,92,112,117,116,40,35,35,49,44,35,35,50,41,35,35,51,123,92,117,110,115,107,105,112,92,114,97,105,115,101,35,35,50,92,71,78,85,80,76,79,84,117,110,105,116,10,32,32,32,32,32,32,92,104,98,111,120,32,116,111,48,112,116,123,92,107,101,114,110,35,35,49,92,71,78,85,80,76,79,84,117,110,105,116,32,35,35,51,92,104,115,115,125,92,105,103])
+.concat([110,111,114,101,115,112,97,99,101,115,125,37,37,10,32,32,32,32,92,100,101,102,92,108,106,117,115,116,35,35,49,123,92,118,98,111,120,32,116,111,48,112,116,123,92,118,115,115,92,104,98,111,120,32,116,111,48,112,116,123,35,35,49,92,104,115,115,125,92,118,115,115,125,125,37,37,10,32,32,32,32,92,100,101,102,92,99,106,117,115,116,35,35,49,123,92,118,98,111,120,32,116,111,48,112,116,123,92,118,115,115,92,104,98,111,120,32,116,111,48,112,116,123,92,104,115,115,32,35,35,49,92,104,115,115,125,92,118,115,115,125,125,37,37,10,32,32,32,32,92,100,101,102,92,114,106,117,115,116,35,35,49,123,92,118,98,111,120,32,116,111,48,112,116,123,92,118,115,115,92,104,98,111,120,32,116,111,48,112,116,123,92,104,115,115,32,35,35,49,125,92,118,115,115,125,125,37,37,10,32,32,32,32,92,100,101,102,92,115,116,97,99,107,35,35,49,123,92,108,101,116,92,92,61,92,99,114,92,116,97,98,115,107,105,112,61,48,112,116,92,104,97,108,105,103,110,123,92,104,102,105,108,32,35,35,35,35,92,104,102,105,108,92,99,114,32,35,35,49,92,99,114,99,114,125,125,37,37,10,32,32,32,32,92,100,101,102,92,108,115,116,97,99,107,35,35,49,123,92,104,98,111,120,32,116,111,48,112,116,123,92,118,98,111,120,32,116,111,48,112,116,123,92,118,115,115,92,115,116,97,99,107,123,35,35,49,125,125,92,104,115,115,125,125,37,37,10,32,32,32,32,92,100,101,102,92,99,115,116,97,99,107,35,35,49,123,92,104,98,111,120,32,116,111,48,112,116,123,92,104,115,115,92,118,98,111,120,32,116,111,48,112,116,123,92,118,115,115,92,115,116,97,99,107,123,35,35,49,125,125,92,104,115,115,125,125,37,37,10,32,32,32,32,92,100,101,102,92,114,115,116,97,99,107,35,35,49,123,92,104,98,111,120,32,116,111,48,112,116,123,92,118,98,111,120,32,116,111,48,112,116,123,92,115,116,97,99,107,123,35,35,49,125,92,118,115,115,125,92,104,115,115,125,125,37,37,10,32,32,32,32,92,118,115,115,92,104,98,111,120,32,116,111,35,49,92,71,78,85,80,76,79,84,117,110,105,116,92,98,103,114,111,117,112,92,105,103,110,111,114,101,115,112,97,99,101,115,125,37,37,10,32,32,92,103,100,101,102,92,101,110,100,71,78,85,80,76,79,84,112,105,99,116,117,114,101,123,92,104,115,115,92,101,103,114,111,117,112,92,101,103,114,111,117,112,125,37,37,10,92,102,105,10,92,71,78,85,80,76,79,84,117,110,105,116,61,37,46,52,102,98,112,10,0,104,105,115,116,111,103,114,97,109,115,0,0,0,0,0,0,98,111,114,0,0,0,0,0,92,98,101,103,105,110,123,112,105,99,116,117,114,101,125,40,37,100,44,37,100,41,40,48,44,48,41,37,37,10,0,0,37,37,32,71,78,85,80,76,79,84,58,32,76,97,84,101,88,32,112,105,99,116,117,114,101,32,119,105,116,104,32,80,111,115,116,115,99,114,105,112,116,10,92,98,101,103,105,110,103,114,111,117,112,37,37,10,92,109,97,107,101,97,116,108,101,116,116,101,114,37,37,10,92,110,101,119,99,111,109,109,97,110,100,123,92,71,78,85,80,76,79,84,115,112,101,99,105,97,108,125,123,37,37,10,32,32,92,64,115,97,110,105,116,105,122,101,92,99,97,116,99,111,100,101,96,92,37,37,61,49,52,92,114,101,108,97,120,92,115,112,101,99,105,97,108,125,37,37,10,92,115,101,116,108,101,110,103,116,104,123,92,117,110,105,116,108,101,110,103,116,104,125,123,37,46,52,102,98,112,125,37,37,10,0,80,108,111,116,32,102,97,105,108,101,100,33,0,0,0,0,111,114,32,115,101,116,32,116,104,101,32,108,111,97,100,112,97,116,104,32,97,112,112,114,111,112,114,105,97,116,101,108,121,10,0,0,0,0,0,0,86,84,45,108,105,107,101,32,116,101,107,52,48,120,120,32,116,101,114,109,105,110,97,108,32,101,109,117,108,97,116,111,114,0,0,0,0,0,0,0,111,114,32,115,101,116,32,116,104,101,32,101,110,118,105,114,111,110,109,101,110,116,97,108,32,118,97,114,105,97,98,108,101,32,71,78,85,80,76,79,84,95,80,83,95,68,73,82,10,0,0,0,0,0,0,0,118,105,36,101,119,0,0,0,108,36,111,97,100,0,0,0,80,108,101,97,115,101,32,99,111,112,121,32,37,115,32,116,111,32,111,110,101,32,111,102,32,116,104,101,32,97,98,111,118,101,32,100,105,114,101,99,116,111,114,105,101,115,10,0,9,114,111,116,97,116,101,100,32,105,102,32,116,104,101,32,116,101,114,109,105,110,97,108,32,97,108,108,111,119,115,32,105,116,10,9,0,0,0,0,103,97,112,0,0,0,0,0,32,92,10,0,0,0,0,0,67,97,110,39,116,32,102,105,110,100,32,80,111,115,116,83,99,114,105,112,116,32,112,114,111,108,111,103,117,101,32,102,105,108,101,32,37,115,10,0,71,80,86,65,76,95,86,73,69,87,95,82,79,84,95,90,0,0,0,0,0,0,0,0,71,78,85,80,76,79,84,95,80,83,95,68,73,82,0,0,80,114,111,108,111,103,32,110,97,109,101,0,0,0,0,0,10,9,84,104,101,32,102,111,108,108,111,119,105,110,103,32,98,105,110,97,114,121,32,100,97,116,97,32,115,105,122,101,115,32,97,116,116,101,109,112,116,32,116,111,32,98,101,32,109,97,99,104,105,110,101,32,105,110,100,101,112,101,110,100,101,110,116,58,10,10,9,32,32,110,97,109,101,32,40,115,105,122,101,32,105,110,32,98,121,116,101,115,41,10,10,0,47,117,115,114,47,108,111,99,97,108,47,115,104,97,114,101,47,103,110,117,112,108,111,116,47,52,46,54,47,80,111,115,116,83,99,114,105,112,116,0,97,103,108,105,115,116,0,0,97,103,108,102,110,46,116,120,116,0,0,0,0,0,0,0,47,76,84,51,32,123,32,80,76,32,91,56,32,100,108,49,32,53,32,100,108,49,32,48,46,53,32,100,108,49,32,53,32,100,108,49,93,32,49,32,48,32,49,32,68,76,32,125,32,100,101,102,10,0,0,0,71,80,86,65,76,95,78,97,78,0,0,0,0,0,0,0,47,76,84,50,32,123,32,80,76,32,91,52,32,100,108,49,32,52,32,100,108,49,93,32,48,32,49,32,49,32,68,76,32,125,32,100,101,102,10,0,118,116,116,101,107,0,0,0,47,76,84,49,32,123,32,80,76,32,91,56,32,100,108,49,32,53,32,100,108,49,93,32,48,32,48,32,49,32,68,76,32,125,32,100,101,102,10,0,118,101,36,114,115,105,111,110,0,0,0,0,0,0,0,0,47,76,84,48,32,123,32,80,76,32,91,93,32,49,32,48,32,48,32,68,76,32,125,32,100,101,102,10,0,0,0,0,116,111,112,0,0,0,0,0,99,111,108,117,109,110,115,36,116,97,99,107,101,100,0,0,32,37,115,37,115,116,105,99,115,32,37,115,109,37,115,116,105,99,115,0,0,0,0,0,47,76,84,97,32,123,32,65,76,32,91,49,32,117,100,108,32,109,117,108,32,50,32,117,100,108,32,109,117,108,93,32,48,32,115,101,116,100,97,115,104,32,48,32,48,32,48,32,115,101,116,114,103,98,99,111,108,111,114,32,125,32,100,101,102,10,0,0,0,0,0,0,71,80,86,65,76,95,86,73,69,87,95,82,79,84,95,88,0,0,0,0,0,0,0,0,47,76,84,98,32,123,32,66,76,32,91,93,32,48,32,48,32,48,32,68,76,32,125,32,100,101,102,10,0,0,0,0,47,76,84,119,32,123,32,80,76,32,91,93,32,49,32,115,101,116,103,114,97,121,32,125,32,100,101,102,10,0,0,0,40,37,100,41,10,0,0,0,93,32,41,10,0,0,0,0,37,32,82,101,100,101,102,105,110,101,32,108,105,110,101,32,116,121,112,101,115,32,116,111,32,109,97,116,99,104,32,111,108,100,32,101,112,115,108,97,116,101,120,32,100,114,105,118,101,114,10,0,0,0,0,0,119,36,105,116,104,0,0,0,99,117,114,114,101,110,116,100,105,99,116,32,101,110,100,32,100,101,102,105,110,101,102,111,110,116,32,112,111,112,10,0,100,117,112,32,108,101,110,103,116,104,32,100,105,99,116,32,98,101,103,105,110,32,123,49,32,105,110,100,101,120,32,47,70,73,68,32,101,113,32,123,112,111,112,32,112,111,112,125,32,123,100,101,102,125,32,105,102,101,108,115,101,125,32,102,111,114,97,108,108,10,0,0,45,101,0,0,0,0,0,0,47,67,77,69,88,49,48,45,66,97,115,101,108,105,110,101,32,47,67,77,69,88,49,48,32,102,105,110,100,102,111,110,116,32,91,49,32,48,32,48,32,49,32,48,32,49,93,32,109,97,107,101,102,111,110,116,10,0,0,0,0,0,0,0,71,80,86,65,76,95,112,105,0,0,0,0,0,0,0,0,37,37,66,101,103,105,110,80,114,111,99,83,101,116,58,32,67,77,69,88,49,48,45,66,97,115,101,108,105,110,101,10,0,0,0,0,0,0,0,0,114,101,102,114,101,115,104,10,0,0,0,0,0,0,0,0,84,101,107,116,114,111,110,105,120,32,52,48,49,48,32,97,110,100,32,111,116,104,101,114,115,59,32,109,111,115,116,32,84,69,75,32,101,109,117,108,97,116,111,114,115,0,0,0,67,77,69,88,49,48,0,0,116,105,116,36,108,101,0,0,76,105,110,101,102,101,101,100,0,0,0,0,0,0,0,0,37,37,69,110,100,80,114,111,99,83,101,116,10,0,0,0,98,111,116,116,111,109,0,0,114,111,119,115,36,116,97,99,107,101,100,0,0,0,0,0,115,101,116,32,103,114,105,100,0,0,0,0,0,0,0,0,67,111,109,109,97,110,100,32,39,37,115,39,32,103,101,110,101,114,97,116,101,100,32,101,114,114,111,114,44,32,101,120,105,116,99,111,100,101,32,105,115,32,37,100,0,0,0,0,110,111,116,32,101,110,111,117,103,104,32,109,101,109,111,114,121,32,116,111,32,108,111,97,100,32,102,105,108,101,0,0,67,97,110,39,116,32,99,97,108,99,117,108,97,116,101,32,99,117,98,105,99,32,115,112,108,105,110,101,115,0,0,0,96,98,117,105,108,116,105,110,45,114,111,116,97,116,101,45,100,111,119,110,96,32,111,110,108,121,32,102,111,114,32,115,112,108,111,116,115,59,32,60,115,104,105,102,116,62,32,105,110,99,114,101,97,115,101,115,32,97,109,111,117,110,116,0,80,105,112,101,32,39,37,115,39,32,99,111,110,116,97,105,110,115,32,116,104,101,32,102,111,110,116,32,39,37,115,39,46,10,0,0,0,0,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,115,116,114,108,101,110,32,111,102,32,110,111,110,45,83,84,82,73,78,71,32,97,114,103,117,109,101,110,116,0,0,70,111,110,116,32,102,105,108,101,32,39,37,115,39,32,99,111,110,116,97,105,110,115,32,116,104,101,32,102,111,110,116,32,39,37,115,39,46,32,76,111,99,97,116,105,111,110,58,10,32,32,32,37,115,10,0,108,111,97,100,95,102,111,110,116,102,105,108,101,115,0,0,47,70,111,110,116,78,97,109,101,0,0,0,0,0,0,0,70,111,110,116,32,102,105,108,101,32,39,37,115,39,32,115,101,101,109,115,32,110,111,116,32,116,111,32,98,101,32,97,32,80,70,65,32,102,105,108,101,0,0,0,0,0,0,0,67,111,109,109,97,110,100,32,39,37,115,39,32,115,101,101,109,115,32,110,111,116,32,116,111,32,103,101,110,101,114,97,116,101,32,80,70,65,32,100,97,116,97,0,0,0,0,0,71,80,86,65,76,95,84,69,82,77,73,78,65,76,83,0,37,33,70,111,110,116,84,121,112,101,49,0,0,0,0,0,116,101,107,52,48,120,120,0,37,33,80,83,45,65,100,111,98,101,70,111,110,116,0,0,116,105,109,36,101,115,116,97,109,112,0,0,0,0,0,0,70,111,110,116,32,102,105,108,101,32,39,37,115,39,32,104,97,115,32,117,110,107,110,111,119,110,32,101,120,116,101,110,115,105,111,110,46,32,65,115,115,117,109,101,32,105,116,32,105,115,32,97,32,112,102,97,32,102,105,108,101,0,0,0,9,119,114,105,116,116,101,110,32,105,110,32,37,115,32,99,111,114,110,101,114,10,0,0,101,114,114,111,114,36,98,97,114,115,0,0,0,0,0,0,115,101,116,32,103,114,105,100,32,110,111,112,111,108,97,114,10,0,0,0,0,0,0,0,112,102,97,0,0,0,0,0,78,111,32,99,111,109,109,97,110,100,32,102,111,114,32,97,117,116,111,109,97,116,105,99,32,102,111,110,116,32,99,111,110,118,101,114,115,105,111,110,32,112,102,98,45,62,112,102,97,32,100,101,102,105,110,101,100,0,0,0,0,0,0,0,112,102,98,116,111,112,115,32,37,115,0,0,0,0,0,0,9,32,32,0,0,0,0,0,67,97,110,39,116,32,112,108,111,116,32,119,105,116,104,32,97,110,32,101,109,112,116,121,32,37,115,32,114,97,110,103,101,33,0,0,0,0,0,0,71,78,85,80,76,79,84,95,80,70,66,84,79,80,70,65,0,0,0,0,0,0,0,0,112,102,98,0,0,0,0,0,117,110,115,101,116,32,104,105,100,100,101,110,51,100,10,0,67,111,117,108,100,32,110,111,116,32,101,120,101,99,117,116,101,32,99,111,109,109,97,110,100,32,39,37,115,39,0,0,78,111,32,99,111,109,109,97,110,100,32,102,111,114,32,97,117,116,111,109,97,116,105,99,32,102,111,110,116,32,99,111,110,118,101,114,115,105,111,110,32,116,116,102,45,62,112,102,97,32,100,101,102,105,110,101,100,0,0,0,0,0,0,0,110,101,119,32,107,101,121,32,108,105,115,116,0,0,0,0,116,116,102,50,112,116,49,32,45,97,32,45,101,32,45,87,32,48,32,37,115,32,45,0,84,101,107,116,114,111,110,105,120,32,52,49,48,54,44,32,52,49,48,55,44,32,52,49,48,57,32,97,110,100,32,52,50,48,88,32,116,101,114,109,105,110,97,108,115,0,0,0,71,78,85,80,76,79,84,95,84,84,70,84,79,80,70,65,0,0,0,0,0,0,0,0,116,105,109,101,102,36,109,116,0,0,0,0,0,0,0,0,111,116,102,0,0,0,0,0,99,108,117,115,116,36,101,114,101,100,0,0,0,0,0,0,115,101,116,32,103,114,105,100,32,112,111,108,97,114,32,37,102,10,0,0,0,0,0,0,116,116,102,0,0,0,0,0,112,0,0,0,0,0,0,0,37,37,37,37,66,101,103,105,110,80,114,111,99,83,101,116,58,32,37,115,10,0,0,0,80,83,95,101,115,99,97,112,101,95,115,116,114,105,110,103,0,0,0,0,0,0,0,0,9,84,104,101,32,102,111,108,108,111,119,105,110,103,32,98,105,110,97,114,121,32,100,97,116,97,32,115,105,122,101,115,32,97,114,101,32,109,97,99,104,105,110,101,32,100,101,112,101,110,100,101,110,116,58,10,10,9,32,32,110,97,109,101,32,40,115,105,122,101,32,105,110,32,98,121,116,101,115,41,10,10,0,0,0,0,0,0,76,111,103,32,115,99,97,108,105,110,103,32,111,102,32,51,68,32,105,109,97,103,101,32,112,108,111,116,115,32,105,115,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,0,101,110,100,10,37,37,69,110,100,80,114,111,108,111,103,10,0,0,0,0,0,0,0,0,40,41,92,0,0,0,0,0,115,116,97,116,117,115,100,105,99,116,32,98,101,103,105,110,32,37,115,32,115,101,116,100,117,112,108,101,120,109,111,100,101,32,101,110,100,10,0,0,79,114,105,103,105,110,95,50,0,0,0,0,0,0,0,0,117,116,102,45,56,46,112,115,0,0,0,0,0,0,0,0,71,80,86,65,76,95,67,79,77,80,73,76,69,95,79,80,84,73,79,78,83,0,0,0,107,111,105,56,117,46,112,115,0,0,0,0,0,0,0,0,116,101,107,52,49,48,120,0,107,111,105,56,114,46,112,115,0,0,0,0,0,0,0,0,116,105,99,115,108,36,101,118,101,108,0,0,0,0,0,0,99,112,49,50,53,49,46,112,115,0,0,0,0,0,0,0,9,115,101,116,32,37,115,100,97,116,97,32,116,105,109,101,10,0,0,0,0,0,0,0,117,110,115,101,116,32,103,114,105,100,10,0,0,0,0,0,99,112,49,50,53,48,46,112,115,0,0,0,0,0,0,0,110,0,0,0,0,0,0,0,99,112,56,53,50,46,112,115,0,0,0,0,0,0,0,0,99,112,56,53,48,46,112,115,0,0,0,0,0,0,0,0,99,112,52,51,55,46,112,115,0,0,0,0,0,0,0,0,56,56,53,57,45,49,53,46,112,115,0,0,0,0,0,0,56,56,53,57,45,57,46,112,115,0,0,0,0,0,0,0,56,56,53,57,45,50,46,112,115,0,0,0,0,0,0,0,108,111,97,100,112,97,116,104,32,61,61,32,78,85,76,76,0,0,0,0,0,0,0,0,71,80,86,65,76,95,80,65,84,67,72,76,69,86,69,76,0,0,0,0,0,0,0,0,56,56,53,57,45,49,46,112,115,0,0,0,0,0,0,0,120,94,51,0,0,0,0,0,82,69,71,73,83,32,103,114,97,112,104,105,99,115,32,108,97,110,103,117,97,103,101,0,112,114,111,108,111,103,117,101,46,112,115,0,0,0,0,0,116,105,99,115,99,36,97,108,101,0,0,0,0,0,0,0,47,100,111,99,108,105,112,32,123,10,32,32,67,108,105,112,84,111,66,111,117,110,100,105,110,103,66,111,120,32,123,10,32,32,32,32,110,101,119,112,97,116,104,32,37,100,32,37,100,32,109,111,118,101,116,111,32,37,100,32,37,100,32,108,105,110,101,116,111,32,37,100,32,37,100,32,108,105,110,101,116,111,32,37,100,32,37,100,32,108,105,110,101,116,111,32,99,108,111,115,101,112,97,116,104,10,32,32,32,32,99,108,105,112,10,32,32,125,32,105,102,10,125,32,100,101,102,10,0,0,0,0,0,0,0,0,104,115,0,0,0,0,0,0,115,101,116,32,116,105,99,115,32,37,115,10,0,0,0,0,37,37,80,97,103,101,115,58,32,40,97,116,101,110,100,41,10,0,0,0,0,0,0,0,80,111,114,116,114,97,105,116,0,0,0,0,0,0,0,0,76,97,110,100,115,99,97,112,101,0,0,0,0,0,0,0,44,32,37,100,32,98,101,102,111,114,101,32,112,108,97,110,101,0,0,0,0,0,0,0,37,37,37,37,79,114,105,101,110,116,97,116,105,111,110,58,32,37,115,10,0,0,0,0,37,37,37,37,66,111,117,110,100,105,110,103,66,111,120,58,32,37,100,32,37,100,32,37,100,32,37,100,10,0,0,0,40,97,116,101,110,100,41,0,37,37,37,37,84,105,116,108,101,58,32,37,115,10,0,0,71,80,86,65,76,95,86,69,82,83,73,79,78,0,0,0,37,33,80,83,45,65,100,111,98,101,45,50,46,48,10,0,114,101,103,105,115,0,0,0,37,33,80,83,45,65,100,111,98,101,45,50,46,48,32,69,80,83,70,45,50,46,48,10,0,0,0,0,0,0,0,0,116,105,36,99,115,0,0,0,101,112,115,0,0,0,0,0,40,0,0,0,0,0,0,0,32,110,111,101,110,104,97,110,99,101,100,0,0,0,0,0,117,110,36,115,111,114,116,101,100,0,0,0,0,0,0,0,105,110,118,97,108,105,100,32,112,111,115,116,115,99,114,105,112,116,32,102,111,114,109,97,116,32,117,115,101,100,0,0,37,37,37,37,80,97,103,101,115,58,32,37,100,10,0,0,37,115,37,115,0,0,0,0,44,32,37,100,32,98,101,102,111,114,101,32,108,105,110,101,0,0,0,0,0,0,0,0,37,37,68,111,99,117,109,101,110,116,70,111,110,116,115,58,32,0,0,0,0,0,0,0,35,0,0,0,0,0,0,0,37,37,84,114,97,105,108,101,114,10,0,0,0,0,0,0,115,116,114,111,107,101,10,103,114,101,115,116,111,114,101,10,101,110,100,10,115,104,111,119,112,97,103,101,10,0,0,0,77,82,115,104,111,119,10,0,66,97,100,32,102,117,108,108,32,109,111,110,116,104,32,110,97,109,101,0,0,0,0,0,71,80,86,65,76,95,69,82,82,78,79,0,0,0,0,0,77,67,115,104,111,119,10,0,71,80,73,67,32,45,45,32,80,114,111,100,117,99,101,32,103,114,97,112,104,115,32,105,110,32,103,114,111,102,102,32,117,115,105,110,103,32,116,104,101,32,103,112,105,99,32,112,114,101,112,114,111,99,101,115,115,111,114,0,0,0,0,0,77,76,115,104,111,119,10,0,116,101,114,109,111,36,112,116,105,111,110,115,0,0,0,0,93,32,37,46,49,102,32,0,44,32,114,111,116,97,116,101,100,32,98,121,32,37,100,32,100,101,103,114,101,101,115,32,105,110,32,50,68,32,112,108,111,116,115,0,0,0,0,0,115,111,36,114,116,101,100,0,92,37,111,0,0,0,0,0,91,32,0,0,0,0,0,0,99,117,114,114,101,110,116,112,111,105,110,116,32,103,115,97,118,101,32,116,114,97,110,115,108,97,116,101,32,37,100,32,114,111,116,97,116,101,32,48,32,48,32,109,111,118,101,116,111,10,0,0,0,0,0,0,10,9,32,32,32,32,83,107,105,112,32,98,121,116,101,115,58,32,37,100,32,98,101,102,111,114,101,32,114,101,99,111,114,100,0,0,0,0,0,0,47,88,89,114,101,115,116,111,114,101,32,123,32,91,40,32,41,32,49,32,50,32,116,114,117,101,32,102,97,108,115,101,32,52,32,40,41,93,32,125,32,98,105,110,100,32,100,101,102,10,0,0,0,0,0,0,47,88,89,115,97,118,101,32,32,32,32,123,32,91,40,32,41,32,49,32,50,32,116,114,117,101,32,102,97,108,115,101,32,51,32,40,41,93,32,125,32,98,105,110,100,32,100,101,102,10,0,0,0,0,0,0,101,120,112,97,110,100,32,102,111,110,116,112,97,116,104,0,32,32,101,120,99,104,32,100,117,112,32,77,70,119,105,100,116,104,32,45,50,32,100,105,118,32,51,32,45,49,32,114,111,108,108,32,82,10,32,32,66,108,97,99,107,116,101,120,116,32,123,103,115,97,118,101,32,48,32,115,101,116,103,114,97,121,32,77,70,115,104,111,119,32,103,114,101,115,116,111,114,101,125,32,123,77,70,115,104,111,119,125,32,105,102,101,108,115,101,32,125,32,98,105,110,100,32,100,101,102,10,0,101,37,43,48,50,100,0,0,47,77,67,115,104,111,119,32,123,32,99,117,114,114,101,110,116,112,111,105,110,116,32,115,116,114,111,107,101,32,77,10,0,0,0,0,0,0,0,0,10,9,100,117,109,109,121,32,118,97,114,105,97,98,108,101,32,105,115,32,120,32,102,111,114,32,99,117,114,118,101,115,10,0,0,0,0,0,0,0,37,72,58,37,77,0,0,0,71,80,86,65,76,95,69,78,67,79,68,73,78,71,0,0,32,32,101,120,99,104,32,100,117,112,32,77,70,119,105,100,116,104,32,110,101,103,32,51,32,45,49,32,114,111,108,108,32,82,10,32,32,66,108,97,99,107,116,101,120,116,32,123,103,115,97,118,101,32,48,32,115,101,116,103,114,97,121,32,77,70,115,104,111,119,32,103,114,101,115,116,111,114,101,125,32,123,77,70,115,104,111,119,125,32,105,102,101,108,115,101,32,125,32,98,105,110,100,32,100,101,102,10,0,0,0,0,103,112,105,99,0,0,0,0,47,77,82,115,104,111,119,32,123,32,99,117,114,114,101,110,116,112,111,105,110,116,32,115,116,114,111,107,101,32,77,10,0,0,0,0,0,0,0,0,116,97,98,108,101,0,0,0,32,32,48,32,101,120,99,104,32,82,10,32,32,66,108,97,99,107,116,101,120,116,32,123,103,115,97,118,101,32,48,32,115,101,116,103,114,97,121,32,77,70,115,104,111,119,32,103,114,101,115,116,111,114,101,125,32,123,77,70,115,104,111,119,125,32,105,102,101,108,115,101,32,125,32,98,105,110,100,32,100,101,102,10,0,0,0,0,44,32,112,97,114,97,108,108,101,108,32,116,111,32,97,120,105,115,32,105,110,32,51,68,32,112,108,111,116,115,0,0,101,120,112,101,99,116,105,110,103,32,39,120,39,44,32,39,120,50,39,44,32,39,97,117,116,111,39,32,111,114,32,39,111,102,102,39,0,0,0,0,115,101,116,32,97,110,103,108,101,115,32,37,115,10,0,0,47,77,76,115,104,111,119,32,123,32,99,117,114,114,101,110,116,112,111,105,110,116,32,115,116,114,111,107,101,32,77,10,0,0,0,0,0,0,0,0,32,32,32,32,32,54,32,103,101,116,32,71,115,119,105,100,116,104,32,112,111,112,32,97,100,100,125,32,123,112,111,112,125,32,105,102,101,108,115,101,125,32,105,102,101,108,115,101,125,32,102,111,114,97,108,108,125,32,100,101,102,10,0,0,32,123,100,117,112,32,51,32,103,101,116,123,100,117,112,32,100,117,112,32,48,32,103,101,116,32,102,105,110,100,102,111,110,116,32,101,120,99,104,32,49,32,103,101,116,32,115,99,97,108,101,102,111,110,116,32,115,101,116,102,111,110,116,10,0,0,0,0,0,0,0,0,37,50,46,50,115,0,0,0,115,106,105,115,0,0,0,0,47,77,70,119,105,100,116,104,32,123,48,32,101,120,99,104,32,123,32,100,117,112,32,53,32,103,101,116,32,51,32,103,101,32,123,32,53,32,103,101,116,32,51,32,101,113,32,123,32,48,32,125,32,123,32,112,111,112,32,125,32,105,102,101,108,115,101,32,125,10,0,0,84,97,98,117,108,97,114,32,111,117,116,112,117,116,32,111,102,32,37,115,32,112,108,111,116,32,115,116,121,108,101,32,110,111,116,32,102,117,108,108,121,32,105,109,112,108,101,109,101,110,116,101,100,10,0,0,108,97,110,100,0,0,0,0,47,71,115,119,105,100,116,104,32,123,100,117,112,32,116,121,112,101,32,47,115,116,114,105,110,103,116,121,112,101,32,101,113,32,123,115,116,114,105,110,103,119,105,100,116,104,125,32,123,112,111,112,32,40,110,41,32,115,116,114,105,110,103,119,105,100,116,104,125,32,105,102,101,108,115,101,125,32,100,101,102,10,0,0,0,0,0,0,32,32,32,102,111,114,97,108,108,125,32,100,101,102,10,0,32,32,32,32,32,105,102,101,108,115,101,32,125,10,0,0,71,80,86,65,76,95,79,85,84,80,85,84,0,0,0,0,32,32,32,32,32,112,111,112,32,97,108,111,97,100,32,112,111,112,32,77,125,32,105,102,101,108,115,101,32,125,105,102,101,108,115,101,32,125,105,102,101,108,115,101,32,125,10,0,85,110,107,110,111,119,110,32,116,101,114,109,105,110,97,108,32,116,121,112,101,32,45,32,110,111,116,32,97,32,112,108,111,116,116,105,110,103,32,100,101,118,105,99,101,0,0,0,32,32,32,32,32,115,104,111,119,32,50,32,105,110,100,101,120,32,123,97,108,111,97,100,32,112,111,112,32,77,32,110,101,103,32,51,32,45,49,32,114,111,108,108,32,110,101,103,32,82,32,112,111,112,32,112,111,112,125,32,123,112,111,112,32,112,111,112,32,112,111,112,10,0,0,0,0,0,0,0,115,117,36,114,102,97,99,101,0,0,0,0,0,0,0,0,101,108,115,101,0,0,0,0,32,32,32,32,32,100,117,112,32,48,32,82,125,32,123,100,117,112,32,54,32,103,101,116,32,115,116,114,105,110,103,119,105,100,116,104,32,112,111,112,32,45,50,32,100,105,118,32,48,32,82,32,54,32,103,101,116,10,0,0,0,0,0,0,44,32,117,115,105,110,103,32,102,111,110,116,32,34,37,115,34,0,0,0,0,0,0,0,97,117,116,111,0,0,0,0,78,111,32,118,97,108,105,100,32,100,97,116,97,32,112,111,105,110,116,115,32,102,111,117,110,100,32,105,110,32,102,105,108,101,0,0,0,0,0,0,115,101,116,32,102,111,114,109,97,116,32,37,115,32,34,37,115,34,10,0,0,0,0,0,32,32,32,32,32,103,101,116,32,49,32,101,113,32,123,100,117,112,32,50,32,103,101,116,32,101,120,99,104,32,100,117,112,32,51,32,103,101,116,32,101,120,99,104,32,54,32,103,101,116,32,115,116,114,105,110,103,119,105,100,116,104,32,112,111,112,32,45,50,32,100,105,118,10,0,0,0,0,0,0,32,32,32,32,32,123,100,117,112,32,51,32,103,101,116,32,123,50,32,103,101,116,32,110,101,103,32,48,32,101,120,99,104,32,82,32,112,111,112,125,32,123,112,111,112,32,97,108,111,97,100,32,112,111,112,32,77,125,32,105,102,101,108,115,101,125,32,123,100,117,112,32,53,10,0,0,0,0,0,0,32,32,32,32,32,103,101,116,32,101,120,99,104,32,52,32,103,101,116,32,123,71,115,104,111,119,125,32,123,115,116,114,105,110,103,119,105,100,116,104,32,112,111,112,32,48,32,82,125,32,105,102,101,108,115,101,32,125,105,102,32,100,117,112,32,53,32,103,101,116,32,48,32,101,113,10,0,0,0,0,10,9,32,32,32,32,83,99,97,110,58,32,0,0,0,0,32,32,32,32,32,91,32,99,117,114,114,101,110,116,112,111,105,110,116,32,93,32,101,120,99,104,32,100,117,112,32,50,32,103,101,116,32,48,32,101,120,99,104,32,82,32,100,117,112,32,53,32,103,101,116,32,50,32,110,101,32,123,100,117,112,32,100,117,112,32,54,10,0,0,0,0,0,0,0,0,32,32,32,32,32,123,100,117,112,32,100,117,112,32,48,32,103,101,116,32,102,105,110,100,102,111,110,116,32,101,120,99,104,32,49,32,103,101,116,32,115,99,97,108,101,102,111,110,116,32,115,101,116,102,111,110,116,10,0,0,0,0,0,0,32,32,32,32,32,123,32,53,32,103,101,116,32,51,32,101,113,32,123,103,115,97,118,101,125,32,123,103,114,101,115,116,111,114,101,125,32,105,102,101,108,115,101,32,125,10,0,0,45,76,73,66,71,68,32,32,0,0,0,0,0,0,0,0,32,32,32,123,32,100,117,112,32,53,32,103,101,116,32,51,32,103,101,10,0,0,0,0,71,80,86,65,76,95,84,69,82,77,79,80,84,73,79,78,83,0,0,0,0,0,0,0,47,77,70,115,104,111,119,32,123,10,0,0,0,0,0,0,123,125,94,95,64,38,126,10,0,0,0,0,0,0,0,0,125,32,105,102,10,0,0,0,115,116,36,121,108,101,0,0,103,115,97,118,101,32,66,97,99,107,103,114,111,117,110,100,67,111,108,111,114,32,67,32,99,108,105,112,112,97,116,104,32,102,105,108,108,32,103,114,101,115,116,111,114,101,0,0,9,37,115,37,115,32,105,115,32,34,37,115,34,44,32,111,102,102,115,101,116,32,97,116,32,0,0,0,0,0,0,0,116,101,120,116,95,108,97,98,101,108,0,0,0,0,0,0,115,101,116,32,100,117,109,109,121,32,37,115,44,37,115,10,0,0,0,0,0,0,0,0,66,97,99,107,103,114,111,117,110,100,67,111,108,111,114,32,67,32,49,46,48,48,48,32,48,32,48,32,37,46,50,102,32,37,46,50,102,32,66,111,120,67,111,108,70,105,108,108,0,0,0,0,0,0,0,0,66,97,99,107,103,114,111,117,110,100,67,111,108,111,114,32,48,32,108,116,32,51,32,49,32,114,111,108,108,32,48,32,108,116,32,101,120,99,104,32,48,32,108,116,32,111,114,32,111,114,32,110,111,116,32,123,0,0,0,0,0,0,0,0,40,37,115,41,32,102,105,110,100,102,111,110,116,32,37,100,32,115,99,97,108,101,102,111,110,116,32,115,101,116,102,111,110,116,10,0,0,0,0,0,10,9,32,32,32,32,51,68,32,110,111,114,109,97,108,32,118,101,99,116,111,114,58,32,40,37,102,44,32,37,102,44,32,37,102,41,0,0,0,0,32,32,35,32,40,99,117,114,114,101,110,116,108,121,32,91,0,0,0,0,0,0,0,0,48,32,115,101,116,103,114,97,121,10,110,101,119,112,97,116,104,10,0,0,0,0,0,0,110,111,116,36,105,116,108,101,0,0,0,0,0,0,0,0,57,48,32,114,111,116,97,116,101,10,48,32,37,100,32,116,114,97,110,115,108,97,116,101,10,0,0,0,0,0,0,0,103,110,117,100,105,99,116,32,98,101,103,105,110,10,103,115,97,118,101,10,100,111,99,108,105,112,10,37,100,32,37,100,32,116,114,97,110,115,108,97,116,101,10,37,46,51,102,32,37,46,51,102,32,115,99,97,108,101,10,0,0,0,0,0,45,0,0,0,0,0,0,0,87,114,105,116,105,110,103,32,111,117,116,32,80,111,115,116,83,99,114,105,112,116,32,109,97,99,114,111,115,32,102,111,114,32,101,110,104,97,110,99,101,100,32,116,101,120,116,32,109,111,100,101,10,0,0,0,37,37,37,37,80,97,103,101,58,32,37,100,32,37,100,10,0,0,0,0,0,0,0,0,46,0,0,0,0,0,0,0,37,100,32,37,100,32,78,10,0,0,0,0,0,0,0,0,71,80,95,70,73,82,83,84,95,75,69,89,0,0,0,0,37,100,32,37,100,32,82,10,0,0,0,0,0,0,0,0,108,97,98,101,108,0,0,0,47,121,48,32,121,48,32,121,115,116,101,112,32,97,100,100,32,100,101,102,32,47,105,105,32,105,105,32,49,32,97,100,100,32,100,101,102,10,105,105,32,105,109,97,120,32,103,101,32,123,101,120,105,116,125,32,105,102,32,125,32,108,111,111,112,10,103,114,101,115,116,111,114,101,32,48,32,115,101,116,103,114,97,121,10,0,0,0,120,0,0,0,0,0,0,0,32,107,100,101,110,115,105,116,121,50,100,0,0,0,0,0,37,100,32,37,100,32,77,10,0,0,0,0,0,0,0,0,116,111,107,101,110,95,116,97,98,108,101,95,115,105,122,101,32,62,61,32,108,102,45,62,110,117,109,95,116,111,107,101,110,115,43,49,0,0,0,0,67,97,110,39,116,32,99,97,108,99,117,108,97,116,101,32,115,112,108,105,110,101,115,44,32,110,101,101,100,32,97,116,32,108,101,97,115,116,32,51,32,112,111,105,110,116,115,0,115,116,114,111,107,101,32,37,100,32,37,100,32,77,10,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,83,84,82,73,78,71,32,111,112,101,114,97,116,111,114,32,97,112,112,108,105,101,100,32,116,111,32,110,111,110,45,83,84,82,73,78,71,32,116,121,112,101,0,0,0,0,0,37,100,32,37,100,32,86,10,0,0,0,0,0,0,0,0,10,9,32,32,32,32,50,68,32,114,111,116,97,116,105,111,110,32,97,110,103,108,101,58,32,37,102,0,0,0,0,0,37,100,32,37,100,32,76,10,0,0,0,0,0,0,0,0,76,84,37,99,10,0,0,0,41,32,82,115,104,111,119,10,0,0,0,0,0,0,0,0,41,32,67,115,104,111,119,10,0,0,0,0,0,0,0,0,71,80,86,65,76,95,84,69,82,77,0,0,0,0,0,0,41,32,76,115,104,111,119,10,0,0,0,0,0,0,0,0,101,115,116,105,109,97,116,101,32,119,105,100,116,104,32,111,102,32,101,110,104,97,110,99,101,100,32,116,101,120,116,32,115,116,114,105,110,103,0,0,93,32,71,82,115,104,111,119,10,0,0,0,0,0,0,0,115,97,36,109,112,108,101,115,0,0,0,0,0,0,0,0,93,32,71,67,115,104,111,119,10,0,0,0,0,0,0,0,110,117,109,101,114,105,99,97,108,0,0,0,0,0,0,0,115,101,116,32,100,103,114,105,100,51,100,32,37,100,44,37,100,32,37,115,37,115,32,37,102,44,37,102,10,0,0,0,93,32,71,76,115,104,111,119,10,0,0,0,0,0,0,0,99,117,114,114,101,110,116,112,111,105,110,116,32,103,115,97,118,101,32,116,114,97,110,115,108,97,116,101,32,37,100,32,114,111,116,97,116,101,32,48,32,48,32,77,10,0,0,0,37,100,32,37,100,32,37,115,10,0,0,0,0,0,0,0,32,40,37,102,44,32,37,102,44,32,37,102,41,0,0,0,97,100,106,117,115,116,105,110,103,32,116,111,32,91,37,103,58,37,103,93,10,0,0,0,80,101,110,116,87,0,0,0,68,105,97,87,0,0,0,0,84,114,105,68,87,0,0,0,84,114,105,85,87,0,0,0,110,101,119,32,108,105,110,101,32,98,117,102,102,101,114,0,67,105,114,99,87,0,0,0,101,115,116,105,109,97,116,101,0,0,0,0,0,0,0,0,66,111,120,87,0,0,0,0,111,98,106,36,101,99,116,0,80,101,110,116,69,0,0,0,108,97,98,36,101,108,115,0,115,101,116,32,100,103,114,105,100,51,100,32,37,100,44,37,100,32,115,112,108,105,110,101,115,10,0,0,0,0,0,0,68,105,97,69,0,0,0,0,84,114,105,68,69,0,0,0,105,115,111,95,56,56,53,57,95,49,0,0,0,0,0,0,84,114,105,85,69,0,0,0,10,9,32,32,32,32,67,101,110,116,101,114,58,0,0,0,67,105,114,99,69,0,0,0,73,109,97,103,101,32,103,114,105,100,32,109,117,115,116,32,98,101,32,97,116,32,108,101,97,115,116,32,52,32,112,111,105,110,116,115,32,40,50,32,120,32,50,41,46,10,10,0,66,111,120,69,0,0,0,0,68,49,53,0,0,0,0,0,68,49,52,0,0,0,0,0,85,110,107,110,111,119,110,32,112,111,105,110,116,32,116,121,112,101,32,105,110,32,112,108,111,116,51,100,95,108,105,110,101,115,0,0,0,0,0,0,79,114,105,103,105,110,95,49,0,0,0,0,0,0,0,0,10,35,32,67,117,114,118,101,32,37,100,32,111,102,32,37,100,44,32,37,100,32,112,111,105,110,116,115,0,0,0,0,68,49,51,0,0,0,0,0,123,125,36,91,93,92,0,0,68,49,50,0,0,0,0,0,112,115,100,105,114,0,0,0,68,49,49,0,0,0,0,0,9,37,115,32,105,115,32,115,101,116,32,116,111,32,37,115,10,0,0,0,0,0,0,0,115,101,112,97,114,97,116,105,111,110,32,109,117,115,116,32,98,101,32,62,32,48,0,0,115,101,116,32,100,103,114,105,100,51,100,32,37,100,44,37,100,44,32,37,100,10,0,0,68,49,48,0,0,0,0,0,76,0,0,0,0,0,0,0,68,57,0,0,0,0,0,0,68,56,0,0,0,0,0,0,10,9,32,32,32,32,79,114,105,103,105,110,58,0,0,0,68,55,0,0,0,0,0,0,68,54,0,0,0,0,0,0,68,53,0,0,0,0,0,0,68,52,0,0,0,0,0,0,68,51,0,0,0,0,0,0,100,117,112,32,100,117,112,32,109,117,108,32,109,117,108,0,101,110,104,97,110,99,101,100,32,116,101,120,116,32,109,111,100,101,32,112,97,114,115,105,110,103,32,101,114,114,111,114,0,0,0,0,0,0,0,0,68,50,0,0,0,0,0,0,112,111,108,36,97,114,0,0,68,49,0,0,0,0,0,0,9,114,101,97,100,32,102,111,114,109,97,116,32,102,111,114,32,116,105,109,101,32,111,110,32,37,115,32,97,120,105,115,32,105,115,32,34,37,115,34,10,0,0,0,0,0,0,0,115,101,112,36,97,114,97,116,105,111,110,0,0,0,0,0,121,121,10,0,0,0,0,0,68,48,0,0,0,0,0,0,108,0,0,0,0,0,0,0,83,49,53,0,0,0,0,0,83,49,52,0,0,0,0,0,44,32,100,122,61,37,102,0,83,49,51,0,0,0,0,0,83,49,50,0,0,0,0,0,83,49,49,0,0,0,0,0,83,49,48,0,0,0,0,0,83,57,0,0,0,0,0,0,101,110,104,97,110,99,101,100,32,116,101,120,116,32,109,111,100,101,32,112,97,114,115,101,114,32,45,32,105,103,110,111,114,105,110,103,32,115,112,117,114,105,111,117,115,32,125,0,83,56,0,0,0,0,0,0,112,111,105,36,110,116,115,105,122,101,0,0,0,0,0,0,83,55,0,0,0,0,0,0,9,122,101,114,111,32,105,115,32,37,103,10,0,0,0,0,83,54,0,0,0,0,0,0,102,105,110,97,110,99,101,36,98,97,114,115,0,0,0,0,120,120,10,0,0,0,0,0,114,101,115,105,122,101,95,100,121,110,97,114,114,97,121,58,32,100,121,110,97,114,114,97,121,32,119,97,115,110,39,116,32,105,110,105,116,105,97,108,105,122,101,100,33,0,0,0,104,0,0,0,0,0,0,0,83,53,0,0,0,0,0,0,83,52,0,0,0,0,0,0,44,32,100,121,61,37,102,0,83,51,0,0,0,0,0,0,83,50,0,0,0,0,0,0,73,47,79,32,101,114,114,111,114,32,100,117,114,105,110,103,32,117,112,100,97,116,101,0,83,49,0,0,0,0,0,0,83,48,0,0,0,0,0,0,71,80,86,65,76,95,86,73,69,87,95,77,65,80,0,0,67,49,53,0,0,0,0,0,115,118,103,0,0,0,0,0,67,49,52,0,0,0,0,0,112,111,105,110,116,105,110,116,36,101,114,118,97,108,98,111,120,0,0,0,0,0,0,0,67,49,51,0,0,0,0,0,102,105,108,101,116,36,121,112,101,115,0,0,0,0,0,0,99,97,110,100,108,101,36,115,116,105,99,107,115,0,0,0,120,121,10,0,0,0,0,0,67,49,50,0,0,0,0,0,103,0,0,0,0,0,0,0,67,49,49,0,0,0,0,0,67,49,48,0,0,0,0,0,10,9,32,32,32,32,83,97,109,112,108,101,32,112,101,114,105,111,100,115,58,32,100,120,61,37,102,0,0,0,0,0,67,57,0,0,0,0,0,0,67,56,0,0,0,0,0,0,36,96,0,0,0,0,0,0,67,55,0,0,0,0,0,0,121,122,97,102,112,110,117,109,32,107,77,71,84,80,69,90,89,0,0,0,0,0,0,0,67,54,0,0,0,0,0,0,37,73,58,37,77,58,37,83,32,37,112,0,0,0,0,0,71,80,86,65,76,95,83,80,76,79,84,0,0,0,0,0,67,53,0,0,0,0,0,0,101,110,104,97,110,99,101,100,32,116,101,120,116,32,112,97,114,115,101,114,32,45,45,32,115,112,117,114,105,111,117,115,32,98,97,99,107,115,108,97,115,104,0,0,0,0,0,0,67,52,0,0,0,0,0,0,99,111,108,111,114,115,0,0,67,51,0,0,0,0,0,0,100,97,116,97,115,36,105,122,101,115,0,0,0,0,0,0,102,114,97,99,116,105,111,110,32,109,117,115,116,32,98,101,32,108,101,115,115,32,116,104,97,110,32,49,0,0,0,0,117,110,105,116,115,32,0,0,67,50,0,0,0,0,0,0,101,0,0,0,0,0,0,0,67,49,0,0,0,0,0,0,67,48,0,0,0,0,0,0,97,108,108,32,102,111,114,119,97,114,100,0,0,0,0,0,107,111,105,56,117,0,0,0,80,101,110,116,70,0,0,0,32,32,114,101,100,32,103,114,101,101,110,32,98,108,117,101,32,97,108,112,104,97,0,0,80,101,110,116,0,0,0,0,108,111,114,0,0,0,0,0,68,105,97,70,0,0,0,0,68,105,97,0,0,0,0,0,71,80,86,65,76,95,80,76,79,84,0,0,0,0,0,0,84,114,105,68,70,0,0,0,94,95,64,38,126,123,125,0,84,114,105,68,0,0,0,0,99,111,108,111,114,110,36,97,109,101,115,0,0,0,0,0,105,102,0,0,0,0,0,0,84,114,105,85,70,0,0,0,98,105,110,36,97,114,121,0,102,114,97,99,36,116,105,111,110,0,0,0,0,0,0,0,65,108,108,32,112,111,105,110,116,115,32,111,117,116,32,111,102,32,114,97,110,103,101,0,32,97,110,103,108,101,32,37,103,32,0,0,0,0,0,0,84,114,105,85,0,0,0,0,98,0,0,0,0,0,0,0,67,105,114,99,108,101,70,0,108,111,119,36,101,114,0,0,67,105,114,99,108,101,0,0,37,115,102,108,105,112,32,122,0,0,0,0,0,0,0,0,66,111,120,70,0,0,0,0,66,111,120,0,0,0,0,0,83,116,97,114,0,0,0,0,45,76,73,66,82,69,65,68,76,73,78,69,32,32,45,72,73,83,84,79,82,89,32,32,0,0,0,0,0,0,0,0,67,114,115,0,0,0,0,0,71,80,86,65,76,95,82,95,76,79,71,0,0,0,0,0])
+.concat([80,108,115,0,0,0,0,0,98,97,100,32,115,121,110,116,97,120,32,105,110,32,101,110,104,97,110,99,101,100,32,116,101,120,116,32,115,116,114,105,110,103,0,0,0,0,0,0,80,110,116,0,0,0,0,0,99,111,108,111,114,98,36,111,120,0,0,0,0,0,0,0,103,115,97,118,101,32,91,93,32,48,32,115,101,116,100,97,115,104,10,0,0,0,0,0,9,78,111,32,102,108,111,97,116,105,110,103,32,112,111,105,110,116,32,101,120,99,101,112,116,105,111,110,32,104,97,110,100,108,101,114,32,100,117,114,105,110,103,32,100,97,116,97,32,105,110,112,117,116,10,0,114,97,119,0,0,0,0,0,85,110,114,101,99,111,103,110,105,122,101,100,32,111,112,116,105,111,110,46,32,32,83,101,101,32,39,104,101,108,112,32,115,101,116,39,46,0,0,0,114,97,110,103,101,0,0,0,115,101,116,32,115,116,121,108,101,32,101,108,108,105,112,115,101,32,115,105,122,101,32,0,47,118,115,104,105,102,116,32,37,100,32,100,101,102,10,0,97,0,0,0,0,0,0,0,47,37,115,32,102,105,110,100,102,111,110,116,32,37,103,32,115,99,97,108,101,102,111,110,116,32,115,101,116,102,111,110,116,10,0,0,0,0,0,0,37,102,0,0,0,0,0,0,109,105,115,115,105,110,103,32,102,105,108,101,110,97,109,101,0,0,0,0,0,0,0,0,37,46,51,102,32,85,80,10,0,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,34,116,105,116,108,101,34,32,102,111,114,32,112,108,111,116,0,0,0,0,0,0,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,48,32,80,97,116,116,101,114,110,70,105,108,108,10,0,0,0,0,0,0,0,0,110,101,119,104,105,115,116,36,111,103,114,97,109,0,0,0,49,32,37,100,32,37,100,32,37,100,32,37,100,32,66,111,120,67,111,108,70,105,108,108,10,0,0,0,0,0,0,0,87,65,82,78,73,78,71,58,32,69,114,114,111,114,32,100,117,114,105,110,103,32,105,110,105,116,105,97,108,105,122,97,116,105,111,110,10,10,0,0,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,50,32,80,97,116,116,101,114,110,70,105,108,108,10,0,0,0,0,0,0,0,0,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,37,100,32,49,32,80,97,116,116,101,114,110,70,105,108,108,10,0,0,0,0,0,0,0,0,71,80,86,65,76,95,82,95,77,73,78,0,0,0,0,0,39,59,39,32,101,120,112,101,99,116,101,100,0,0,0,0,101,110,104,97,110,99,101,100,32,116,101,120,116,32,112,97,114,115,101,114,32,45,32,115,112,117,114,105,111,117,115,32,125,0,0,0,0,0,0,0,37,100,32,37,100,32,37,100,32,37,100,32,66,111,120,70,105,108,108,10,0,0,0,0,112,97,108,36,101,116,116,101,0,0,0,0,0,0,0,0,98,105,110,100,58,32,99,97,110,110,111,116,32,112,97,114,115,101,32,37,115,10,0,0,10,32,47,84,114,97,110,115,112,97,114,101,110,116,80,97,116,116,101,114,110,115,32,116,114,117,101,32,100,101,102,10,0,0,0,0,0,0,0,0,9,68,97,116,97,102,105,108,101,32,112,97,114,115,105,110,103,32,119,105,108,108,32,97,99,99,101,112,116,32,70,111,114,116,114,97,110,32,68,32,111,114,32,81,32,99,111,110,115,116,97,110,116,115,10,0,112,116,0,0,0,0,0,0,121,48,32,48,32,78,32,48,32,49,32,86,32,121,115,116,101,112,32,48,32,86,32,48,32,45,49,32,102,10,0,0,32,10,0,0,0,0,0,0,37,46,51,102,32,37,100,32,37,100,32,37,100,32,37,100,32,66,111,120,67,111,108,70,105,108,108,10,0,0,0,0,67,97,110,110,111,116,32,115,109,111,111,116,104,58,32,110,111,32,100,97,116,97,32,119,105,116,104,105,110,32,102,105,120,101,100,32,120,114,97,110,103,101,33,0,0,0,0,0,37,115,65,108,116,45,0,0,105,110,116,101,114,110,97,108,32,101,114,114,111,114,32,58,32,97,114,103,117,109,101,110,116,32,110,101,105,116,104,101,114,32,73,78,84,32,111,114,32,67,77,80,76,88,0,0,37,100,32,37,100,32,37,100,32,37,100,32,82,101,99,32,102,105,108,108,10,0,0,0,37,46,51,102,32,85,76,10,0,0,0,0,0,0,0,0,37,115,102,108,105,112,32,121,0,0,0,0,0,0,0,0,32,32,105,102,125,32,105,102,101,108,115,101,125,32,105,102,101,108,115,101,125,32,105,102,101,108,115,101,125,32,100,101,102,10,0,0,0,0,0,0,32,32,88,89,90,50,82,71,66,125,123,67,111,108,111,114,83,112,97,99,101,32,40,67,77,89,41,32,101,113,32,123,67,77,89,50,82,71,66,125,123,67,111,108,111,114,83,112,97,99,101,32,40,89,73,81,41,32,101,113,32,123,89,73,81,50,82,71,66,125,10,0,105,110,118,101,114,115,101,95,110,111,114,109,97,108,95,102,117,110,99,0,0,0,0,0,47,83,101,108,101,99,116,83,112,97,99,101,32,123,67,111,108,111,114,83,112,97,99,101,32,40,72,83,86,41,32,101,113,32,123,72,83,86,50,82,71,66,125,123,67,111,108,111,114,83,112,97,99,101,32,40,88,89,90,41,32,101,113,32,123,10,0,0,0,0,0,0,32,32,101,120,99,104,32,49,46,57,49,32,109,117,108,32,101,120,99,104,32,97,100,100,32,67,111,110,115,116,114,97,105,110,32,51,32,49,32,114,111,108,108,125,32,100,101,102,10,0,0,0,0,0,0,0,32,32,45,48,46,57,56,52,52,32,109,117,108,32,97,100,100,32,67,111,110,115,116,114,97,105,110,32,53,32,49,32,114,111,108,108,32,45,48,46,50,56,57,49,32,109,117,108,32,101,120,99,104,32,45,48,46,53,51,51,56,32,109,117,108,32,97,100,100,10,0,0,105,115,95,106,117,109,112,40,111,112,101,114,97,116,111,114,41,32,124,124,32,40,106,117,109,112,95,111,102,102,115,101,116,32,61,61,32,49,41,0,78,111,32,102,105,108,108,101,100,32,112,111,108,121,103,111,110,115,0,0,0,0,0,0,32,32,67,111,110,115,116,114,97,105,110,32,52,32,49,32,114,111,108,108,32,51,32,99,111,112,121,32,45,48,46,48,50,55,57,32,109,117,108,32,101,120,99,104,32,49,46,57,57,57,32,109,117,108,32,97,100,100,32,101,120,99,104,10,0,0,0,0,0,0,0,0,112,109,36,51,100,0,0,0,32,32,51,32,99,111,112,121,32,45,48,46,57,48,49,55,32,109,117,108,32,101,120,99,104,32,45,48,46,49,49,56,55,32,109,117,108,32,97,100,100,32,101,120,99,104,32,48,46,48,53,56,53,32,109,117,108,32,101,120,99,104,32,97,100,100,10,0,0,0,0,0,9,67,111,109,109,101,110,116,115,32,99,104,97,114,115,32,97,114,101,32,34,37,115,34,10,0,0,0,0,0,0,0,112,111,105,110,116,36,116,121,112,101,0,0,0,0,0,0,115,101,116,32,115,116,121,108,101,32,99,105,114,99,108,101,32,114,97,100,105,117,115,32,0,0,0,0,0,0,0,0,47,88,89,90,50,82,71,66,32,123,0,0,0,0,0,0,32,32,49,32,101,120,99,104,32,115,117,98,32,101,120,99,104,32,49,32,101,120,99,104,32,115,117,98,32,51,32,50,32,114,111,108,108,32,49,32,101,120,99,104,32,115,117,98,32,51,32,49,32,114,111,108,108,32,101,120,99,104,32,125,32,100,101,102,10,0,0,0,67,116,114,108,45,0,0,0,47,67,77,89,50,82,71,66,32,123,0,0,0,0,0,0,102,108,105,112,32,120,0,0,87,97,114,110,105,110,103,58,32,101,109,112,116,121,32,37,115,32,114,97,110,103,101,32,91,37,103,58,37,103,93,44,32,0,0,0,0,0,0,0,32,32,48,46,54,50,49,32,109,117,108,32,101,120,99,104,32,45,48,46,57,53,54,32,109,117,108,32,97,100,100,32,97,100,100,32,67,111,110,115,116,114,97,105,110,32,51,32,49,32,114,111,108,108,32,125,32,100,101,102,10,0,0,0,32,32,51,32,99,111,112,121,32,45,48,46,54,52,55,32,109,117,108,32,101,120,99,104,32,45,48,46,50,55,50,32,109,117,108,32,97,100,100,32,97,100,100,32,67,111,110,115,116,114,97,105,110,32,53,32,49,32,114,111,108,108,10,0,32,32,51,32,99,111,112,121,32,45,49,46,55,48,50,32,109,117,108,32,101,120,99,104,32,45,49,46,49,48,53,32,109,117,108,32,97,100,100,32,97,100,100,32,67,111,110,115,116,114,97,105,110,32,52,32,49,32,114,111,108,108,10,0,47,89,73,81,50,82,71,66,32,123,10,0,0,0,0,0,107,101,121,32,97,114,114,97,121,0,0,0,0,0,0,0,32,32,100,117,112,32,48,32,108,116,32,123,48,32,101,120,99,104,32,112,111,112,125,123,100,117,112,32,49,32,103,116,32,123,49,32,101,120,99,104,32,112,111,112,125,32,105,102,125,32,105,102,101,108,115,101,125,32,100,101,102,10,0,0,102,105,108,108,101,100,32,112,111,108,121,103,111,110,115,58,0,0,0,0,0,0,0,0,47,67,111,110,115,116,114,97,105,110,32,123,10,0,0,0,112,97,36,114,97,109,101,116,114,105,99,0,0,0,0,0,32,32,125,32,105,102,101,108,115,101,125,32,100,101,102,10,0,0,0,0,0,0,0,0,99,111,109,36,109,101,110,116,115,0,0,0,0,0,0,0,111,117,116,36,108,105,101,114,115,0,0,0,0,0,0,0,115,101,116,32,115,116,121,108,101,32,114,101,99,116,97,110,103,108,101,32,37,115,32,102,99,32,0,0,0,0,0,0,9,32,123,72,83,86,118,32,72,83,86,112,32,72,83,86,113,125,32,105,102,101,108,115,101,125,32,105,102,101,108,115,101,125,32,105,102,101,108,115,101,125,32,105,102,101,108,115,101,125,32,105,102,101,108,115,101,10,0,0,0,0,0,0,96,37,115,58,37,100,32,111,111,112,115,46,39,10,0,0,9,32,123,51,32,72,83,86,105,32,101,113,32,123,72,83,86,112,32,72,83,86,113,32,72,83,86,118,125,123,52,32,72,83,86,105,32,101,113,32,123,72,83,86,116,32,72,83,86,112,32,72,83,86,118,125,10,0,0,0,0,0,0,0,9,32,123,49,32,72,83,86,105,32,101,113,32,123,72,83,86,113,32,72,83,86,118,32,72,83,86,112,125,123,50,32,72,83,86,105,32,101,113,32,123,72,83,86,112,32,72,83,86,118,32,72,83,86,116,125,10,0,0,0,0,0,0,0,10,9,32,32,32,32,68,105,114,101,99,116,105,111,110,58,32,0,0,0,0,0,0,0,9,32,47,72,83,86,105,32,72,83,86,105,32,54,32,109,111,100,32,100,101,102,32,48,32,72,83,86,105,32,101,113,32,123,72,83,86,118,32,72,83,86,116,32,72,83,86,112,125,10,0,0,0,0,0,0,78,111,32,112,111,105,110,116,115,32,40,118,105,115,105,98,108,101,32,111,114,32,105,110,118,105,115,105,98,108,101,41,32,116,111,32,112,108,111,116,46,10,10,0,0,0,0,0,9,32,47,72,83,86,116,32,72,83,86,118,32,49,46,48,32,72,83,86,115,32,49,46,48,32,72,83,86,102,32,115,117,98,32,109,117,108,32,115,117,98,32,109,117,108,32,100,101,102,10,0,0,0,0,0,9,32,47,72,83,86,113,32,72,83,86,118,32,49,46,48,32,72,83,86,115,32,72,83,86,102,32,109,117,108,32,115,117,98,32,109,117,108,32,100,101,102,32,10,0,0,0,0,32,32,32,32,47,72,83,86,102,32,101,120,99,104,32,100,101,102,32,47,72,83,86,105,32,101,120,99,104,32,99,118,105,32,100,101,102,32,47,72,83,86,112,32,72,83,86,118,32,49,46,48,32,72,83,86,115,32,115,117,98,32,109,117,108,32,100,101,102,10,0,0,103,114,97,112,104,98,111,120,0,0,0,0,0,0,0,0,32,32,123,32,47,72,83,86,115,32,101,120,99,104,32,100,101,102,32,47,72,83,86,118,32,101,120,99,104,32,100,101,102,32,54,46,48,32,109,117,108,32,100,117,112,32,102,108,111,111,114,32,100,117,112,32,51,32,49,32,114,111,108,108,32,115,117,98,10,32,0,0,66,121,116,101,79,114,100,101,114,0,0,0,0,0,0,0,110,111,110,45,105,110,116,101,103,101,114,32,112,97,115,115,101,100,32,116,111,32,98,111,111,108,101,97,110,32,111,112,101,114,97,116,111,114,0,0,37,50,100,0,0,0,0,0,32,32,101,120,99,104,32,100,117,112,32,48,46,48,32,101,113,32,123,112,111,112,32,101,120,99,104,32,112,111,112,32,100,117,112,32,100,117,112,125,32,37,32,97,99,104,114,111,109,97,116,105,99,32,103,114,97,121,10,0,0,0,0,0,111,36,117,116,112,117,116,0,47,72,83,86,50,82,71,66,32,123,0,0,0,0,0,0,9,100,97,116,97,102,105,108,101,32,102,105,101,108,100,115,32,115,101,112,97,114,97,116,101,100,32,98,121,32,119,104,105,116,101,115,112,97,99,101,10,0,0,0,0,0,0,0,110,111,111,117,116,36,108,105,101,114,115,0,0,0,0,0,115,101,116,32,115,116,121,108,101,32,102,105,108,108,32,0,47,99,70,37,105,32,123,37,115,125,32,98,105,110,100,32,100,101,102,9,37,37,32,37,115,10,0,0,0,0,0,0,32,32,32,32,123,47,100,103,100,120,118,97,108,32,100,103,100,120,32,100,101,102,32,114,101,100,118,97,108,117,101,32,103,114,101,101,110,118,97,108,117,101,32,98,108,117,101,118,97,108,117,101,125,32,105,102,101,108,115,101,125,32,100,101,102,10,0,0,0,0,0,0,32,32,32,32,123,82,101,100,65,32,103,105,100,120,32,103,101,116,32,71,114,101,101,110,65,32,103,105,100,120,32,103,101,116,32,66,108,117,101,65,32,103,105,100,120,32,103,101,116,125,10,0,0,0,0,0,32,32,103,114,97,121,105,110,100,101,120,32,103,114,97,121,118,32,71,114,97,121,65,32,103,105,100,120,32,103,101,116,32,115,117,98,32,97,98,115,32,49,101,45,53,32,108,101,10,0,0,0,0,0,0,0,47,105,110,116,101,114,112,111,108,97,116,101,32,123,10,0,32,32,66,108,117,101,65,32,103,105,100,120,32,103,101,116,32,115,117,98,32,100,103,100,120,118,97,108,32,109,117,108,32,97,100,100,125,32,100,101,102,10,0,0,0,0,0,0,47,98,108,117,101,118,97,108,117,101,32,123,66,108,117,101,65,32,103,105,100,120,32,103,101,116,32,66,108,117,101,65,32,103,105,100,120,32,49,32,115,117,98,32,103,101,116,10,0,0,0,0,0,0,0,0,32,32,71,114,101,101,110,65,32,103,105,100,120,32,103,101,116,32,115,117,98,32,100,103,100,120,118,97,108,32,109,117,108,32,97,100,100,125,32,100,101,102,10,0,0,0,0,0,115,116,97,99,107,32,111,118,101,114,102,108,111,119,0,0,120,94,50,0,0,0,0,0,112,97,116,116,101,114,110,32,102,105,108,108,0,0,0,0,47,103,114,101,101,110,118,97,108,117,101,32,123,71,114,101,101,110,65,32,103,105,100,120,32,103,101,116,32,71,114,101,101,110,65,32,103,105,100,120,32,49,32,115,117,98,32,103,101,116,10,0,0,0,0,0,111,114,36,105,103,105,110,0,32,32,82,101,100,65,32,103,105,100,120,32,103,101,116,32,115,117,98,32,100,103,100,120,118,97,108,32,109,117,108,32,97,100,100,125,32,100,101,102,10,0,0,0,0,0,0,0,9,100,97,116,97,102,105,108,101,32,102,105,101,108,100,115,32,115,101,112,97,114,97,116,101,100,32,98,121,32,34,37,99,34,10,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,100,97,116,97,39,44,32,39,102,117,110,99,116,105,111,110,39,44,32,39,108,105,110,101,39,44,32,39,102,105,108,108,39,44,32,39,114,101,99,116,97,110,103,108,101,39,44,32,39,99,105,114,99,108,101,39,44,32,39,101,108,108,105,112,115,101,39,32,111,114,32,39,97,114,114,111,119,39,0,0,0,0,0,0,0,47,114,101,100,118,97,108,117,101,32,123,82,101,100,65,32,103,105,100,120,32,103,101,116,32,82,101,100,65,32,103,105,100,120,32,49,32,115,117,98,32,103,101,116,10,0,0,0,96,37,115,39,10,0,0,0,32,32,71,114,97,121,65,32,103,105,100,120,32,103,101,116,32,115,117,98,32,100,105,118,125,32,100,101,102,32,10,0,47,100,103,100,120,32,123,103,114,97,121,118,32,71,114,97,121,65,32,103,105,100,120,32,103,101,116,32,115,117,98,32,71,114,97,121,65,32,103,105,100,120,32,49,32,115,117,98,32,103,101,116,10,0,0,0,32,32,123,71,114,97,121,65,32,103,105,100,120,32,103,101,116,32,103,114,97,121,118,32,103,101,32,123,101,120,105,116,125,32,105,102,32,47,103,105,100,120,32,103,105,100,120,32,49,32,97,100,100,32,100,101,102,125,32,108,111,111,112,125,32,100,101,102,10,0,0,0,47,103,114,97,121,105,110,100,101,120,32,123,47,103,105,100,120,32,48,32,100,101,102,10,0,0,0,0,0,0,0,0,93,32,100,101,102,10,0,0,47,37,115,32,91,0,0,0,66,108,117,101,65,0,0,0,78,111,110,45,110,117,109,101,114,105,99,32,115,116,114,105,110,103,32,102,111,117,110,100,32,119,104,101,114,101,32,97,32,110,117,109,101,114,105,99,32,101,120,112,114,101,115,115,105,111,110,32,119,97,115,32,101,120,112,101,99,116,101,100,0,0,0,0,0,0,0,0,116,97,103,32,109,117,115,116,32,98,101,32,62,32,122,101,114,111,0,0,0,0,0,0,108,105,110,101,119,105,100,116,104,0,0,0,0,0,0,0,71,114,101,101,110,65,0,0,111,102,36,102,115,101,116,115,0,0,0,0,0,0,0,0,82,101,100,65,0,0,0,0,115,101,112,36,97,114,97,116,111,114,0,0,0,0,0,0,117,36,115,101,114,115,116,121,108,101,115,0,0,0,0,0,71,114,97,121,65,0,0,0,37,99,32,0,0,0,0,0,32,100,101,102,10,0,0,0,37,115,58,37,100,32,111,111,111,112,115,58,32,85,110,107,110,111,119,110,32,99,111,108,111,114,32,109,111,100,101,108,32,39,37,99,39,46,32,87,105,108,108,32,98,101,32,82,71,66,10,0,0,0,0,0,10,9,32,32,32,32,71,101,110,101,114,97,116,101,32,99,111,111,114,100,105,110,97,116,101,115,58,32,37,115,0,0,40,88,89,90,41,0,0,0,40,89,73,81,41,0,0,0,37,45,49,53,46,49,53,115,32,61,32,37,45,49,53,46,49,53,115,32,32,32,37,115,0,0,0,0,0,0,0,0,40,67,77,89,41,0,0,0,40,72,83,86,41,0,0,0,40,82,71,66,41,0,0,0,115,116,97,99,107,32,117,110,100,101,114,102,108,111,119,32,40,102,117,110,99,116,105,111,110,32,99,97,108,108,32,119,105,116,104,32,109,105,115,115,105,110,103,32,112,97,114,97,109,101,116,101,114,115,63,41,0,0,0,0,0,0,0,0,32,32,108,119,32,37,49,100,37,99,0,0,0,0,0,0,47,67,111,108,111,114,83,112,97,99,101,32,0,0,0,0,120,37,100,0,0,0,0,0,110,111,109,99,98,116,36,105,99,115,0,0,0,0,0,0,32,32,47,103,32,123,115,116,114,111,107,101,32,112,109,51,100,114,111,117,110,100,32,112,109,51,100,71,97,109,109,97,32,101,120,112,32,115,101,116,103,114,97,121,125,32,98,105,110,100,32,100,101,102,10,0,9,34,37,115,34,32,105,110,32,100,97,116,97,102,105,108,101,32,105,115,32,105,110,116,101,114,112,114,101,116,101,100,32,97,115,32,109,105,115,115,105,110,103,32,118,97,108,117,101,10,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,117,110,105,116,115,32,123,120,121,124,120,120,124,121,121,125,39,44,32,39,97,110,103,108,101,32,60,110,117,109,98,101,114,62,39,32,111,114,32,39,115,105,122,101,32,60,112,111,115,105,116,105,111,110,62,39,0,0,0,0,0,115,101,116,32,98,111,120,119,105,100,116,104,32,37,103,32,37,115,10,0,0,0,0,0,125,123,10,0,0,0,0,0,32,37,45,49,50,115,32,0,32,32,125,32,105,102,101,108,115,101,10,0,0,0,0,0,10,32,32,32,32,32,32,32,83,101,108,101,99,116,83,112,97,99,101,32,115,101,116,114,103,98,99,111,108,111,114,125,32,98,105,110,100,32,100,101,102,10,0,0,0,0,0,0,103,112,95,105,110,112,117,116,95,108,105,110,101,0,0,0,99,70,37,105,32,67,111,110,115,116,114,97,105,110,32,0,99,70,37,105,32,67,111,110,115,116,114,97,105,110,32,101,120,99,104,32,0,0,0,0,36,40,0,0,0,0,0,0,99,70,37,105,32,67,111,110,115,116,114,97,105,110,32,101,120,99,104,32,100,117,112,32,0,0,0,0,0,0,0,0,70,111,114,109,97,116,32,99,104,97,114,97,99,116,101,114,32,109,105,115,109,97,116,99,104,58,32,37,37,99,32,105,115,32,111,110,108,121,32,118,97,108,105,100,32,119,105,116,104,32,37,37,115,0,0,0,49,32,101,120,99,104,32,115,117,98,32,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,100,97,116,97,39,44,32,39,102,117,110,99,116,105,111,110,39,44,32,39,108,105,110,101,39,44,32,39,102,105,108,108,39,32,111,114,32,39,97,114,114,111,119,39,0,112,109,0,0,0,0,0,0,32,32,47,103,32,123,115,116,114,111,107,101,32,112,109,51,100,114,111,117,110,100,32,100,117,112,32,0,0,0,0,0,10,119,97,114,110,105,110,103,58,32,32,105,110,116,101,114,110,97,108,32,101,114,114,111,114,45,45,115,116,97,99,107,32,110,111,116,32,101,109,112,116,121,33,10,32,32,32,32,32,32,32,32,32,32,40,102,117,110,99,116,105,111,110,32,99,97,108,108,101,100,32,119,105,116,104,32,116,111,111,32,109,97,110,121,32,112,97,114,97,109,101,116,101,114,115,63,41,10,0,0,0,0,0,0,37,100,0,0,0,0,0,0,32,32,125,123,10,0,0,0,73,110,102,0,0,0,0,0,109,99,98,116,36,105,99,115,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,32,83,101,108,101,99,116,83,112,97,99,101,32,115,101,116,114,103,98,99,111,108,111,114,125,32,98,105,110,100,32,100,101,102,10,0,0,0,0,0,0,9,78,111,32,109,105,115,115,105,110,103,32,100,97,116,97,32,115,116,114,105,110,103,32,115,101,116,32,102,111,114,32,100,97,116,97,102,105,108,101,10,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,39,120,121,39,44,32,39,120,120,39,32,111,114,32,39,121,121,39,0,0,0,0,115,101,116,32,98,111,120,119,105,100,116,104,10,0,0,0,32,32,32,32,47,103,32,123,115,116,114,111,107,101,32,112,109,51,100,114,111,117,110,100,32,47,103,114,97,121,118,32,101,120,99,104,32,100,101,102,32,105,110,116,101,114,112,111,108,97,116,101,10,0,0,0,32,32,32,32,32,32,32,32,32,32,32,32,32,32,42,32,105,110,100,105,99,97,116,101,115,32,116,104,105,115,32,107,101,121,32,105,115,32,97,99,116,105,118,101,32,102,114,111,109,32,97,108,108,32,112,108,111,116,32,119,105,110,100,111,119,115,10,0,0,0,0,0,32,32,73,110,116,101,114,112,111,108,97,116,101,100,67,111,108,111,114,32,123,32,37,37,32,73,110,116,101,114,112,111,108,97,116,105,111,110,32,118,115,46,32,82,71,66,45,70,111,114,109,117,108,97,10,0,67,111,108,111,114,32,73,110,116,101,114,112,111,108,97,116,101,100,67,111,108,111,114,32,111,114,32,123,32,37,32,67,79,76,79,85,82,32,118,115,46,32,71,82,65,89,32,109,97,112,10,0,0,0,0,0,101,10,0,0,0,0,0,0,107,111,105,56,114,0,0,0,102,97,108,115,101,32,123,32,37,32,67,79,76,79,85,82,32,118,115,46,32,71,82,65,89,32,109,97,112,10,0,0,32,32,112,105,120,101,108,0,47,112,109,51,100,71,97,109,109,97,32,49,46,48,32,37,103,32,71,97,109,109,97,32,109,117,108,32,100,105,118,32,100,101,102,10,0,0,0,0,117,109,105,110,117,115,0,0,9,123,112,111,112,32,49,125,32,123,109,97,120,99,111,108,111,114,115,32,109,117,108,32,102,108,111,111,114,32,109,97,120,99,111,108,111,114,115,32,49,32,115,117,98,32,100,105,118,125,32,105,102,101,108,115,101,125,32,105,102,125,32,100,101,102,10,0,0,0,0,0,47,112,109,51,100,114,111,117,110,100,32,123,109,97,120,99,111,108,111,114,115,32,48,32,103,116,32,123,100,117,112,32,49,32,103,101,10,0,0,0,46,46,47,116,101,114,109,47,112,111,115,116,46,116,114,109,0,0,0,0,0,0,0,0,117,110,107,110,111,119,110,32,116,121,112,101,32,105,110,32,97,110,103,108,101,40,41,0,115,104,111,119,32,116,105,99,115,99,97,108,101,0,0,0,37,115,58,37,100,32,111,111,111,112,115,58,32,85,110,107,110,111,119,110,32,99,111,108,111,114,32,109,111,100,101,32,39,37,99,39,10,0,0,0,110,111,109,122,116,36,105,99,115,0,0,0,0,0,0,0,9,32,32,32,32,68,105,109,101,110,115,105,111,110,58,32,0,0,0,0,0,0,0,0,104,105,36,115,116,111,114,121,0,0,0,0,0,0,0,0,47,73,110,116,101,114,112,111,108,97,116,101,100,67,111,108,111,114,32,116,114,117,101,32,100,101,102,10,0,0,0,0,109,105,115,115,36,105,110,103,0,0,0,0,0,0,0,0,121,121,0,0,0,0,0,0,47,73,110,116,101,114,112,111,108,97,116,101,100,67,111,108,111,114,32,102,97,108,115,101,32,100,101,102,10,0,0,0,37,48,46,52,102,9,37,48,46,52,102,10,0,0,0,0,99,108,111,115,101,32,116,104,105,115,32,112,108,111,116,32,119,105,110,100,111,119,0,0,47,109,97,120,99,111,108,111,114,115,32,37,105,32,100,101,102,10,0,0,0,0,0,0,103,115,97,118,101,32,37,32,99,111,108,111,117,114,32,112,97,108,101,116,116,101,32,98,101,103,105,110,10,0,0,0,37,115,32,104,97,115,32,37,115,32,99,111,111,114,100,32,111,102,32,37,103,59,32,109,117,115,116,32,98,101,32,97,98,111,118,101,32,48,32,102,111,114,32,108,111,103,32,115,99,97,108,101,33,0,0,0,103,114,101,115,116,111,114,101,32,37,32,99,111,108,111,117,114,32,112,97,108,101,116,116,101,32,101,110,100,10,0,0,37,115,32,103,32,0,0,0,49,32,103,32,0,0,0,0,45,82,69,65,68,76,73,78,69,32,32,0,0,0,0,0,48,32,103,32,0,0,0,0,37,51,46,50,102,32,37,51,46,50,102,32,37,51,46,50,102,32,67,32,0,0,0,0,117,110,107,110,111,119,110,32,116,121,112,101,32,105,110,32,109,97,103,110,105,116,117,100,101,40,41,0,0,0,0,0,99,97,110,39,116,32,114,111,116,97,116,101,32,116,101,120,116,0,0,0,0,0,0,0,76,67,37,49,99,32,115,101,116,114,103,98,99,111,108,111,114,10,0,0,0,0,0,0,105,110,118,97,108,105,100,32,99,111,109,112,108,101,120,32,99,111,110,115,116,97,110,116,0,0,0,0,0,0,0,0,109,122,116,36,105,99,115,0,10,9,32,32,82,101,99,111,114,100,32,37,100,58,10,0,80,76,32,0,0,0,0,0,9,109,111,117,115,101,32,105,115,32,111,102,102,10,0,0,101,120,112,101,99,116,105,110,103,32,100,97,116,97,102,105,108,101,32,109,111,100,105,102,105,101,114,0,0,0,0,0,112,110,103,0,0,0,0,0,120,120,0,0,0,0,0,0,115,101,116,32,37,115,100,97,116,97,32,37,115,10,0,0,32,80,97,116,116,101,114,110,37,100,32,102,105,108,108,32,103,114,101,115,116,111,114,101,10,0,0,0,0,0,0,0,113,0,0,0,0,0,0,0,32,37,46,49,102,32,80,111,108,121,70,105,108,108,10,0,32,47,84,114,97,110,115,112,97,114,101,110,116,80,97,116,116,101,114,110,115,32,116,114,117,101,32,100,101,102,10,0,32,37,46,50,102,32,80,111,108,121,70,105,108,108,10,0,32,49,32,80,111,108,121,70,105,108,108,10,0,0,0,0,32,37,105,32,37,105,32,76,0,0,0,0,0,0,0,0,10,84,101,114,109,105,110,97,108,32,116,121,112,101,32,115,101,116,32,116,111,32,39,37,115,39,10,0,0,0,0,0,32,37,105,32,37,105,32,86,0,0,0,0,0,0,0,0,103,115,97,118,101,32,0,0,117,110,107,110,111,119,110,32,116,121,112,101,32,105,110,32,105,109,97,103,40,41,0,0,32,114,111,116,97,116,101,100,32,98,121,32,45,52,53,32,100,101,103,0,0,0,0,0,32,37,105,32,37,105,32,37,105,32,37,105,32,37,105,32,37,105,32,104,10,0,0,0,110,111,109,121,50,116,36,105,99,115,0,0,0,0,0,0,10,9,32,32,68,101,102,97,117,108,116,32,98,105,110,97,114,121,32,102,111,114,109,97,116,58,32,37,115,0,0,0,99,116,114,108,45,0,0,0,37,105,32,37,105,32,78,0,110,111,116,32,0,0,0,0,42,114,101,116,117,114,110,95,110,117,109,95,98,121,116,101,115,32,60,61,32,109,97,120,95,101,110,99,111,100,101,100,95,98,121,116,101,115,0,0,117,110,105,116,36,115,0,0,48,32,121,48,32,78,32,49,32,48,32,86,32,48,32,121,115,116,101,112,32,86,32,45,49,32,48,32,102,10,0,0,115,101,116,32,116,105,109,101,102,109,116,32,37,115,32,34,37,115,34,10,0,0,0,0,112,100,112,32,40,109,105,100,100,108,101,41,0,0,0,0,85,110,101,120,112,101,99,116,101,100,32,125,0,0,0,0,102,105,118,101,95,100,105,97,103,32,104,101,108,112,32,118,97,114,115,0,0,0,0,0,32,37,45,49,50,115,32,42,32,37,115,10,0,0,0,0,126,62,0,0,0,0,0,0,115,116,114,95,99,111,110,115,116,0,0,0,0,0,0,0,37,49,120,0,0,0,0,0,44,32,39,45,39,32,119,32,108,32,116,105,116,108,101,32,39,78,84,83,67,39,32,108,116,32,45,49,0,0,0,0,101,110,99,111,100,101,100,32,105,109,97,103,101,0,0,0,37,115,105,109,97,103,101,10,0,0,0,0,0,0,0,0,37,115,102,97,108,115,101,32,51,10,37,115,99,111,108,111,114,105,109,97,103,101,10,0,32,32,99,117,114,114,101,110,116,102,105,108,101,32,47,65,83,67,73,73,56,53,68,101,99,111,100,101,32,102,105,108,116,101,114,10,0,0,0,0,68,105,100,32,121,111,117,32,116,114,121,32,116,111,32,103,101,110,101,114,97,116,101,32,97,32,102,105,108,101,32,110,97,109,101,32,117,115,105,110,103,32,100,117,109,109,121,32,118,97,114,105,97,98,108,101,32,120,32,111,114,32,121,63,0,0,0,0,0,0,0,0,32,114,111,116,97,116,101,100,32,98,121,32,43,52,53,32,100,101,103,0,0,0,0,0,123,99,117,114,114,101,110,116,102,105,108,101,32,105,109,97,103,101,98,117,102,32,114,101,97,100,104,101,120,115,116,114,105,110,103,32,112,111,112,125,10,0,0,0,0,0,0,0,109,121,50,116,36,105,99,115,0,0,0,0,0,0,0,0,10,9,32,32,70,105,108,101,32,69,110,100,105,97,110,110,101,115,115,58,32,37,115,0,47,105,109,97,103,101,98,117,102,32,37,100,32,115,116,114,105,110,103,32,100,101,102,10,0,0,0,0,0,0,0,0,9,99,111,109,109,117,110,105,99,97,116,105,111,110,32,99,111,109,109,97,110,100,115,32,119,105,108,108,32,37,115,98,101,32,115,104,111,119,110,10,0,0,0,0,0,0,0,0,97,110,103,36,108,101,0,0,58,32,0,0,0,0,0,0,117,110,115,101,116,32,98,111,114,100,101,114,10,0,0,0,37,115,91,32,37,100,32,48,32,48,32,37,100,32,48,32,48,32,93,10,0,0,0,0,114,97,105,115,101,32,103,110,117,112,108,111,116,32,99,111,110,115,111,108,101,32,119,105,110,100,111,119,0,0,0,0,37,115,37,100,32,37,100,32,37,100,10,0,0,0,0,0,37,115,37,100,32,37,100,32,115,99,97,108,101,10,0,0,98,108,117,101,39,119,32,108,32,108,116,32,51,32,108,99,32,114,103,98,32,39,98,108,117,101,39,0,0,0,0,0,121,0,0,0,0,0,0,0,37,115,37,100,32,37,100,32,116,114,97,110,115,108,97,116,101,10,0,0,0,0,0,0,37,115,123,112,109,51,100,71,97,109,109,97,32,101,120,112,125,32,115,101,116,116,114,97,110,115,102,101,114,10,0,0,9,32,32,87,105,108,108,32,37,115,117,115,101,32,111,116,104,101,114,32,100,105,97,103,111,110,97,108,32,105,102,32,105,116,32,103,105,118,101,115,32,97,32,108,101,115,115,32,106,97,103,103,121,32,111,117,116,108,105,110,101,10,9,32,32,87,105,108,108,32,37,115,100,114,97,119,32,100,105,97,103,111,110,97,108,32,118,105,115,105,98,108,121,32,105,102,32,113,117,97,100,114,97,110,103,108,101,32,105,115,32,39,98,101,110,116,32,111,118,101,114,39,10,0,0,0,0,0,37,115,103,115,97,118,101,10,0,0,0,0,0,0,0,0,37,37,37,37,69,110,100,73,109,97,103,101,10,0,0,0,99,111,110,116,111,117,114,32,101,100,103,101,0,0,0,0,103,114,101,115,116,111,114,101,10,0,0,0,0,0,0,0,101,110,99,111,117,110,116,101,114,101,100,32,97,32,115,116,114,105,110,103,32,119,104,101,110,32,101,120,112,101,99,116,105,110,103,32,97,32,110,117,109,98,101,114,0,0,0,0,114,111,116,97,116,101,100,32,99,101,43,110,116,114,101,100,32,116,101,120,116,0,0,0,10,73,110,116,101,114,112,114,101,116,76,101,118,101,108,49,32,110,111,116,32,123,10,32,32,103,114,101,115,116,111,114,101,10,125,32,105,102,10,0,110,111,109,121,116,36,105,99,115,0,0,0,0,0,0,0,10,103,114,101,115,116,111,114,101,10,0,0,0,0,0,0,9,122,111,111,109,106,117,109,112,32,105,115,32,37,115,10,0,0,0,0,0,0,0,0,115,101,116,32,98,111,114,100,101,114,32,37,100,32,37,115,0,0,0,0,0,0,0,0,32,32,47,73,110,116,101,114,112,111,108,97,116,101,32,102,97,108,115,101,10,62,62,10,105,109,97,103,101,10,0,0,83,112,97,99,101,0,0,0,32,32,47,77,117,108,116,105,112,108,101,68,97,116,97,83,111,117,114,99,101,115,32,102,97,108,115,101,10,0,0,0,32,32,47,68,97,116,97,83,111,117,114,99,101,32,99,117,114,114,101,110,116,102,105,108,101,32,47,65,83,67,73,73,56,53,68,101,99,111,100,101,32,102,105,108,116,101,114,10,0,0,0,0,0,0,0,0,103,114,101,101,110,39,119,32,108,32,108,116,32,50,32,108,99,32,114,103,98,32,39,103,114,101,101,110,39,0,0,0,32,32,47,68,97,116,97,83,111,117,114,99,101,32,123,99,117,114,114,101,110,116,102,105,108,101,32,105,109,97,103,101,98,117,102,32,114,101,97,100,104,101,120,115,116,114,105,110,103,32,112,111,112,125,10,0,112,118,101,114,116,0,0,0,32,32,47,68,101,99,111,100,101,32,91,32,48,32,37,100,32,93,10,0,0,0,0,0,32,32,47,66,105,116,115,80,101,114,67,111,109,112,111,110,101,110,116,32,37,100,10,32,32,47,73,109,97,103,101,77,97,116,114,105,120,32,91,32,37,100,32,48,32,48,32,37,100,32,48,32,48,32,93,10,0,0,0,0,0,0,0,0,32,61,9,10,0,0,0,0,60,60,10,32,32,47,73,109,97,103,101,84,121,112,101,32,49,10,32,32,47,87,105,100,116,104,32,37,100,10,32,32,47,72,101,105,103,104,116,32,37,100,10,0,0,0,0,0,115,116,0,0,0,0,0,0,37,37,37,37,69,110,100,80,97,108,101,116,116,101,10,0,117,110,107,110,111,119,110,32,116,121,112,101,32,105,110,32,114,101,97,108,40,41,0,0,10,32,32,62,10,93,32,115,101,116,99,111,108,111,114,115,112,97,99,101,10,0,0,0,114,105,103,104,116,32,106,117,115,116,105,102,105,101,100,0,68,97,116,97,84,121,112,101,0,0,0,0,0,0,0,0,109,121,116,36,105,99,115,0,32,37,50,46,50,120,37,50,46,50,120,37,50,46,50,120,0,0,0,0,0,0,0,0,9,66,117,116,116,111,110,32,50,32,100,114,97,119,115,32,116,101,109,112,111,114,97,114,121,32,108,97,98,101,108,115,10,0,0,0,0,0,0,0,114,36,97,100,105,117,115,0,117,110,0,0,0,0,0,0,91,32,47,73,110,100,101,120,101,100,10,32,32,47,68,101,118,105,99,101,82,71,66,32,37,100,10,32,32,60,0,0,32,37,45,49,50,115,32,32,32,37,115,10,0,0,0,0,37,37,37,37,66,101,103,105,110,80,97,108,101,116,116,101,10,0,0,0,0,0,0,0,37,100,32,37,100,32,115,99,97,108,101,10,0,0,0,0,114,101,100,39,119,32,108,32,108,116,32,49,32,108,99,32,114,103,98,32,39,114,101,100,39,0,0,0,0,0,0,0,37,100,32,37,100,32,116,114,97,110,115,108,97,116,101,10,0,0,0,0,0,0,0,0,103,115,97,118,101,10,0,0,125,32,123,10,0,0,0,0,67,111,117,108,100,32,110,111,116,32,114,101,110,97,109,101,32,102,105,108,101,32,37,115,32,116,111,32,37,115,0,0,32,32,52,48,32,45,49,49,48,32,82,10,32,32,40,80,83,32,108,101,118,101,108,32,50,32,105,109,97,103,101,41,32,76,115,104,111,119,10,32,32,37,37,32,82,101,97,100,32,100,97,116,97,32,98,117,116,32,105,103,110,111,114,101,32,105,116,10,32,32,47,105,109,97,103,101,98,117,102,32,37,100,32,115,116,114,105,110,103,32,100,101,102,10,32,32,99,117,114,114,101,110,116,102,105,108,101,32,105,109,97,103,101,98,117,102,32,114,101,97,100,115,116,114,105,110,103,10,0,0,0,0,0,0,0,0,125,32,105,102,101,108,115,101,10,0,0,0,0,0,0,0,105,115,111,32,99,117,114,118,101,0,0,0,0,0,0,0,118,97,108,117,101,0,0,0,100,117,112,32,109,117,108,0,99,101,110,116,114,101,43,100,32,116,101,120,116,0,0,0,32,32,99,117,114,114,101,110,116,102,105,108,101,32,105,109,97,103,101,98,117,102,32,114,101,97,100,115,116,114,105,110,103,10,125,32,123,10,0,0,110,111,109,120,50,116,36,105,99,115,0,0,0,0,0,0,10,9,32,32,70,105,108,101,32,84,121,112,101,58,32,0,32,32,47,105,109,97,103,101,98,117,102,32,37,100,32,115,116,114,105,110,103,32,100,101,102,10,0,0,0,0,0,0,9,66,117,116,116,111,110,32,50,32,100,114,97,119,115,32,112,101,114,115,105,115,116,101,110,116,32,108,97,98,101,108,115,32,119,105,116,104,32,111,112,116,105,111,110,115,32,34,37,115,34,10,0,0,0,0,115,116,121,108,101,32,110,111,116,32,117,115,97,98,108,101,32,102,111,114,32,102,117,110,99,116,105,111,110,32,112,108,111,116,115,44,32,108,101,102,116,32,117,110,99,104,97,110,103,101,100,0,0,0,0,0,37,115,115,101,116,32,99,108,105,112,32,112,111,105,110,116,115,10,37,115,115,101,116,32,99,108,105,112,32,111,110,101,10,37,115,115,101,116,32,99,108,105,112,32,116,119,111,10,115,101,116,32,98,97,114,32,37,102,32,37,115,10,0,0,32,32,52,48,32,45,49,49,48,32,82,10,32,32,40,80,83,32,108,101,118,101,108,32,50,32,105,109,97,103,101,41,32,76,115,104,111,119,10,32,32,37,32,82,101,97,100,32,100,97,116,97,32,98,117,116,32,105,103,110,111,114,101,32,105,116,10,0,0,0,0,0,101,120,112,97,110,100,105,110,103,32,99,117,114,118,101,32,112,111,105,110,116,115,0,0,122,111,111,109,32,111,117,116,32,111,110,108,121,32,116,104,101,32,88,32,97,120,105,115,46,0,0,0,0,0,0,0,32,32,37,100,32,37,100,32,76,10,0,0,0,0,0,0,32,32,48,32,37,100,32,86,10,0,0,0,0,0,0,0,39,45,39,116,105,116,39,0,32,32,37,100,32,48,32,86,10,0,0,0,0,0,0,0,32,32,37,100,32,37,100,32,77,10,0,0,0,0,0,0,45,45,118,101,114,115,105,111,110,0,0,0,0,0,0,0,73,110,116,101,114,112,114,101,116,76,101,118,101,108,49,32,123,10,32,32,37,37,32,67,111,110,115,116,114,117,99,116,32,97,32,98,111,120,32,105,110,115,116,101,97,100,32,111,102,32,105,109,97,103,101,10,32,32,76,84,98,10,0,0,46,111,108,100,0,0,0,0,103,115,97,118,101,32,37,100,32,37,100,32,78,32,37,100,32,37,100,32,76,32,37,100,32,37,100,32,76,32,37,100,32,37,100,32,76,32,90,32,99,108,105,112,10,0,0,0,37,37,37,37,66,101,103,105,110,73,109,97,103,101,10,0,101,120,105,115,116,115,0,0,108,101,102,116,32,106,117,115,116,105,102,105,101,100,0,0,71,78,85,80,76,79,84,32,40,112,111,115,116,46,116,114,109,41,58,32,32,67,111,109,112,111,110,101,110,116,32,98,105,116,115,32,40,37,100,41,32,111,117,116,32,111,102,32,114,97,110,103,101,46,10,0,109,120,50,116,36,105,99,115,0,0,0,0,0,0,0,0,9,68,101,102,97,117,108,116,32,98,105,110,97,114,121,32,100,97,116,97,32,102,105,108,101,32,115,101,116,116,105,110,103,115,32,40,105,110,45,102,105,108,101,32,115,101,116,116,105,110,103,115,32,109,97,121,32,111,118,101,114,114,105,100,101,41,58,10,0,0,0,0,47,37,115,32,37,115,0,0,9,97,108,116,101,114,110,97,116,105,118,101,32,102,111,114,109,97,116,32,102,111,114,32,66,117,116,116,111,110,32,50,32,105,115,32,39,37,115,39,10,0,0,0,0,0,0,0,99,97,110,110,111,116,32,111,112,101,110,32,116,97,98,108,101,32,111,117,116,112,117,116,32,102,105,108,101,0,0,0,35,32,115,101,116,32,116,101,114,109,105,110,97,108,32,117,110,107,110,111,119,110,10,0,114,101,101,110,99,111,100,101,75,79,73,56,85,32,100,101,102,10,0,0,0,0,0,0,60,115,104,105,102,116,45,99,111,110,116,114,111,108,45,119,104,101,101,108,45,100,111,119,110,62,0,0,0,0,0,0,114,101,101,110,99,111,100,101,67,80,49,50,53,49,32,100,101,102,10,0,0,0,0,0,114,101,101,110,99,111,100,101,67,80,49,50,53,48,32,100,101,102,10,0,0,0,0,0,114,101,101,110,99,111,100,101,75,79,73,56,82,32,100,101,102,10,0,0,0,0,0,0,99,111,110,115,116,97,110,116,32,101,120,112,114,101,115,115,105,111,110,32,114,101,113,117,105,114,101,100,0,0,0,0,114,101,101,110,99,111,100,101,67,80,56,53,50,32,100,101,102,10,0,0,0,0,0,0,114,101,101,110,99,111,100,101,67,80,56,53,48,32,100,101,102,10,0,0,0,0,0,0,70,73,84,95,76,73,77,73,84,0,0,0,0,0,0,0,114,101,101,110,99,111,100,101,67,80,52,51,55,32,100,101,102,10,0,0,0,0,0,0,118,101,99,0,0,0,0,0,114,101,101,110,99,111,100,101,73,83,79,49,53,32,100,101,102,10,0,0,0,0,0,0,101,120,105,115,116,0,0,0])
+.concat([115,101,116,32,116,101,114,109,111,112,116,32,110,111,101,110,104,0,0,0,0,0,0,0,114,101,101,110,99,111,100,101,73,83,79,57,32,100,101,102,10,0,0,0,0,0,0,0,110,111,109,120,116,36,105,99,115,0,0,0,0,0,0,0,100,97,116,97,102,105,108,101,32,99,111,108,117,109,110,115,32,98,105,110,97,114,121,32,105,110,102,111,114,109,97,116,105,111,110,0,0,0,0,0,114,101,101,110,99,111,100,101,73,83,79,50,32,100,101,102,10,0,0,0,0,0,0,0,9,102,111,114,109,97,116,32,102,111,114,32,66,117,116,116,111,110,32,50,32,105,115,32,37,100,10,0,0,0,0,0,35,32,115,101,116,32,116,101,114,109,105,110,97,108,32,37,115,32,37,115,10,0,0,0,114,101,101,110,99,111,100,101,73,83,79,32,100,101,102,10,0,0,0,0,0,0,0,0,122,111,111,109,32,105,110,32,111,110,108,121,32,116,104,101,32,88,32,97,120,105,115,46,0,0,0,0,0,0,0,0,80,111,115,116,83,99,114,105,112,116,32,70,111,110,116,32,114,101,99,111,114,100,0,0,123,125,91,93,40,41,32,0,99,97,110,110,111,116,32,119,114,105,116,101,32,116,101,109,112,111,114,97,114,121,32,102,105,108,101,0,0,0,0,0,83,121,109,98,111,108,45,79,98,108,105,113,117,101,0,0,37,115,40,0,0,0,0,0,71,78,85,80,76,79,84,95,70,79,78,84,80,65,84,72,0,0,0,0,0,0,0,0,91,40,37,115,41,32,37,46,49,102,32,37,46,49,102,32,37,115,32,37,115,32,37,100,32,0,0,0,0,0,0,0,70,73,84,95,77,65,88,73,84,69,82,0,0,0,0,0,70,111,114,109,97,116,32,99,104,97,114,97,99,116,101,114,32,109,105,115,109,97,116,99,104,58,32,37,37,83,32,105,115,32,111,110,108,121,32,118,97,108,105,100,32,119,105,116,104,32,37,37,115,0,0,0,69,78,72,80,83,95,111,112,101,110,115,101,113,117,101,110,99,101,0,0,0,0,0,0,71,78,85,84,69,82,77,0,97,109,0,0,0,0,0,0,88,89,114,101,115,116,111,114,101,10,0,0,0,0,0,0,115,121,115,116,101,109,0,0,69,110,104,97,110,99,101,100,32,116,101,120,116,58,32,32,32,123,120,64,95,123,48,125,94,123,110,43,49,125,125,0,88,89,115,97,118,101,10,0,109,120,116,36,105,99,115,0,99,111,108,32,62,32,48,0,41,93,10,0,0,0,0,0,9,97,108,116,101,114,110,97,116,105,118,101,32,102,111,114,109,97,116,32,102,111,114,32,66,117,116,116,111,110,32,49,32,105,115,32,39,37,115,39,10,0,0,0,0,0,0,0,79,112,116,105,111,110,115,32,97,114,101,32,39,37,115,39,10,0,0,0,0,0,0,0,37,115,37,103,44,32,37,115,37,103,44,32,37,115,37,103,0,0,0,0,0,0,0,0,117,110,105,37,48,52,108,88,0,0,0,0,0,0,0,0,60,115,104,105,102,116,45,99,111,110,116,114,111,108,45,119,104,101,101,108,45,117,112,62,0,0,0,0,0,0,0,0,117,37,108,88,0,0,0,0,37,115,47,0,0,0,0,0,99,111,109,98,105,110,97,116,105,111,110,32,114,103,98,32,111,114,32,103,98,114,32,111,114,32,98,114,103,32,101,116,99,46,32,101,120,112,101,99,116,101,100,0,0,0,0,0,37,37,32,69,110,100,32,112,108,111,116,32,35,37,100,10,0,0,0,0,0,0,0,0,99,112,49,50,53,52,0,0,105,110,116,101,114,112,111,108,97,116,105,111,110,32,116,97,98,108,101,0,0,0,0,0,32,100,101,108,116,97,95,120,32,100,101,108,116,97,95,121,0,0,0,0,0,0,0,0,37,37,32,66,101,103,105,110,32,112,108,111,116,32,35,37,100,10,0,0,0,0,0,0,98,110,111,116,0,0,0,0,115,116,114,111,107,101,10,0,70,73,84,95,83,84,65,82,84,95,76,65,77,66,68,65,0,0,0,0,0,0,0,0,94,80,89,94,45,10,94,73,79,76,10,94,73,83,89,78,84,65,88,48,48,48,48,48,94,70,94,73,66,49,49,48,48,48,94,73,74,48,48,48,48,48,94,73,84,48,48,48,48,48,10,0,0,0,0,0,94,80,78,94,45,10,0,0,116,101,115,116,32,111,102,32,99,104,97,114,97,99,116,101,114,32,119,105,100,116,104,58,0,0,0,0,0,0,0,0,94,73,71,69,10,94,45,94,44,0,0,0,0,0,0,0,109,117,108,116,105,36,112,108,111,116,0,0,0,0,0,0,69,114,114,111,114,32,97,115,115,105,103,110,105,110,103,32,109,101,109,111,114,121,32,102,111,114,32,98,105,110,97,114,121,32,102,105,108,101,32,100,97,116,97,32,114,101,99,111,114,100,115,0,0,0,0,0,63,0,0,0,0,0,0,0,94,73,71,86,10,0,0,0,9,102,111,114,109,97,116,32,102,111,114,32,66,117,116,116,111,110,32,49,32,105,115,32,37,100,10,0,0,0,0,0,112,111,112,0,0,0,0,0,98,97,100,32,100,97,116,97,32,111,110,32,108,105,110,101,32,37,100,32,111,102,32,102,105,108,101,32,37,115,0,0,32,111,102,102,115,101,116,32,0,0,0,0,0,0,0,0,94,85,37,48,53,100,58,37,48,53,100,10,0,0,0,0,32,122,111,111,109,32,111,117,116,46,0,0,0,0,0,0,94,68,37,48,53,100,58,37,48,53,100,10,0,0,0,0,73,108,108,101,103,97,108,32,115,101,101,100,32,118,97,108,117,101,0,0,0,0,0,0,94,86,48,10,0,0,0,0,94,80,86,51,48,50,49,48,48,94,71,10,94,86,51,10,0,0,0,0,0,0,0,0,94,80,86,50,48,50,48,53,48,94,71,10,94,86,50,10,0,0,0,0,0,0,0,0,94,80,86,49,48,50,48,50,53,94,71,10,94,86,49,10,0,0,0,0,0,0,0,0,70,73,84,95,76,65,77,66,68,65,95,70,65,67,84,79,82,0,0,0,0,0,0,0,99,111,109,112,105,108,101,95,111,112,116,105,111,110,115,0,94,80,87,37,48,50,100,10,0,0,0,0,0,0,0,0,101,120,116,101,110,100,32,105,110,112,117,116,32,108,105,110,101,0,0,0,0,0,0,0,10,94,73,71,86,10,0,0,115,116,114,112,116,105,109,101,0,0,0,0,0,0,0,0,49,50,51,52,53,54,55,56,57,48,49,50,51,52,53,54,55,56,57,48,0,0,0,0,94,73,71,69,10,0,0,0,109,111,36,117,115,101,0,0,98,105,110,97,114,121,32,102,105,108,101,32,100,97,116,97,32,114,101,99,111,114,100,115,0,0,0,0,0,0,0,0,65,114,105,97,108,0,0,0,9,102,111,114,109,97,116,116,105,110,103,32,110,117,109,98,101,114,115,32,119,105,116,104,32,34,37,115,34,10,0,0,110,111,102,112,101,95,116,114,97,112,0,0,0,0,0,0,106,112,103,0,0,0,0,0,112,117,115,104,0,0,0,0,99,104,97,114,97,99,116,101,114,32,0,0,0,0,0,0,60,116,115,112,97,110,32,102,111,110,116,45,115,105,122,101,61,34,37,46,49,102,112,116,34,32,100,121,61,34,37,46,50,102,112,116,34,62,60,47,116,115,112,97,110,62,0,0,60,99,111,110,116,114,111,108,45,119,104,101,101,108,45,100,111,119,110,62,0,0,0,0,37,99,0,0,0,0,0,0,99,111,110,116,111,117,114,32,99,110,116,114,95,115,116,114,117,99,116,0,0,0,0,0,98,97,99,107,103,114,111,117,110,100,32,34,35,37,48,54,120,34,32,0,0,0,0,0,99,111,110,118,101,114,116,32,98,97,99,107,32,34,37,115,34,32,45,32,37,100,47,37,100,47,37,100,58,58,37,100,58,37,100,58,37,100,32,44,32,119,100,97,121,61,37,100,44,32,121,100,97,121,61,37,100,10,0,0,0,0,0,0,32,93,32,37,115,114,101,118,101,114,115,101,32,37,115,119,114,105,116,101,98,97,99,107,0,0,0,0,0,0,0,0,108,105,110,101,119,105,100,116,104,32,37,51,46,49,102,32,0,0,0,0,0,0,0,0,34,116,105,116,108,101,34,32,97,108,108,111,119,101,100,32,111,110,108,121,32,97,102,116,101,114,32,112,97,114,97,109,101,116,114,105,99,32,102,117,110,99,116,105,111,110,32,102,117,108,108,121,32,115,112,101,99,105,102,105,101,100,0,0,115,111,108,105,100,32,0,0,105,115,111,32,99,117,114,118,101,32,112,111,105,110,116,115,0,0,0,0,0,0,0,0,100,97,115,104,101,100,32,0,70,73,84,95,83,67,82,73,80,84,0,0,0,0,0,0,98,117,116,116,32,0,0,0,102,111,114,0,0,0,0,0,109,97,116,114,105,120,32,114,111,119,32,112,111,105,110,116,101,114,115,0,0,0,0,0,102,111,110,116,102,105,108,101,32,34,37,115,34,32,0,0,115,116,114,102,116,105,109,101,0,0,0,0,0,0,0,0,98,101,122,105,101,114,32,99,111,101,102,102,105,99,105,101,110,116,115,0,0,0,0,0,77,111,117,115,101,32,97,110,100,32,104,111,116,107,101,121,115,32,97,114,101,32,115,117,112,112,111,114,116,101,100,44,32,104,105,116,58,32,104,32,114,32,109,32,54,0,0,0,110,97,109,101,32,34,37,115,34,32,0,0,0,0,0,0,98,109,97,114,36,103,105,110,0,0,0,0,0,0,0,0,69,113,117,97,108,32,40,39,61,39,41,32,115,121,109,98,111,108,32,114,101,113,117,105,114,101,100,0,0,0,0,0,97,108,116,45,0,0,0,0,115,116,97,110,100,97,108,111,110,101,32,0,0,0,0,0,9,100,111,117,98,108,101,32,99,108,105,99,107,32,114,101,115,111,108,117,116,105,111,110,32,105,115,32,111,102,102,10,0,0,0,0,0,0,0,0,89,111,117,32,99,97,110,39,116,32,99,104,97,110,103,101,32,116,104,101,32,116,101,114,109,105,110,97,108,32,105,110,32,109,117,108,116,105,112,108,111,116,32,109,111,100,101,0,123,32,121,48,32,103,32,0,115,99,114,101,101,110,32,0,109,111,117,115,105,110,103,32,0,0,0,0,0,0,0,0,108,111,97,100,0,0,0,0,67,97,110,39,116,32,99,97,108,99,117,108,97,116,101,32,97,112,112,114,111,120,105,109,97,116,105,111,110,32,115,112,108,105,110,101,115,0,0,0,101,110,104,97,110,99,101,100,0,0,0,0,0,0,0,0,122,111,111,109,32,105,110,32,116,111,119,97,114,100,32,116,104,101,32,99,101,110,116,101,114,32,111,102,32,116,104,101,32,112,108,111,116,46,0,0,112,109,51,100,32,112,97,108,101,116,116,101,32,99,111,108,111,114,0,0,0,0,0,0,102,97,99,116,111,114,105,97,108,32,40,33,41,32,97,114,103,117,109,101,110,116,32,109,117,115,116,32,98,101,32,97,110,32,105,110,116,101,103,101,114,0,0,0,0,0,0,0,32,100,121,110,97,109,105,99,0,0,0,0,0,0,0,0,105,110,116,101,114,110,97,108,32,61,32,37,102,32,45,32,37,100,47,37,100,47,37,100,58,58,37,100,58,37,100,58,37,100,32,44,32,119,100,97,121,61,37,100,44,32,121,100,97,121,61,37,100,10,0,0,32,102,105,120,101,100,0,0,115,105,122,101,32,37,100,44,37,100,37,115,32,37,115,32,102,110,97,109,101,32,39,37,115,39,32,32,102,115,105,122,101,32,37,103,32,0,0,0,117,110,114,101,99,111,103,110,105,122,101,100,32,116,101,114,109,105,110,97,108,32,111,112,116,105,111,110,0,0,0,0,98,97,99,107,103,36,114,111,117,110,100,0,0,0,0,0,100,97,115,104,36,101,100,0,32,32,116,101,114,109,105,110,97,108,32,116,101,115,116,0,114,111,117,110,100,36,101,100,0,0,0,0,0,0,0,0,116,109,97,114,36,103,105,110,0,0,0,0,0,0,0,0,111,112,116,105,111,110,32,101,120,112,101,99,116,101,100,0,70,111,110,116,32,102,105,108,101,110,97,109,101,32,101,120,112,101,99,116,101,100,0,0,9,100,111,117,98,108,101,32,99,108,105,99,107,32,114,101,115,111,108,117,116,105,111,110,32,105,115,32,37,100,32,109,115,10,0,0,0,0,0,0,84,104,105,115,32,111,112,116,105,111,110,32,99,97,110,110,111,116,32,98,101,32,99,104,97,110,103,101,100,32,117,115,105,110,103,32,39,115,101,116,32,116,101,114,109,111,112,116,105,111,110,39,0,0,0,0,115,101,99,111,110,100,32,0,102,111,110,116,102,105,108,101,0,0,0,0,0,0,0,0,60,99,111,110,116,114,111,108,45,119,104,101,101,108,45,117,112,62,0,0,0,0,0,0,102,115,105,122,101,58,32,101,120,112,101,99,116,105,110,103,32,102,111,110,116,32,115,105,122,101,0,0,0,0,0,0,102,115,36,105,122,101,0,0,115,109,111,111,116,104,32,112,97,108,101,116,116,101,32,105,110,32,37,115,58,32,117,115,105,110,103,32,37,105,32,111,102,32,37,105,32,97,118,97,105,108,97,98,108,101,32,99,111,108,111,114,32,112,111,115,105,116,105,111,110,115,10,0,37,115,32,114,97,110,103,101,32,109,117,115,116,32,98,101,32,103,114,101,97,116,101,114,32,116,104,97,110,32,48,32,102,111,114,32,108,111,103,32,115,99,97,108,101,0,0,0,102,110,36,97,109,101,0,0,102,105,36,120,101,100,0,0,86,97,108,117,101,32,115,116,111,114,101,100,32,102,111,114,32,117,110,100,101,102,105,110,101,100,32,100,97,116,97,112,111,105,110,116,32,104,97,110,100,108,105,110,103,32,105,115,32,105,108,108,101,103,97,108,33,33,33,10,0,0,0,0,100,36,121,110,97,109,105,99,0,0,0,0,0,0,0,0,110,97,109,101,32,109,117,115,116,32,99,111,110,116,97,105,110,32,111,110,108,121,32,97,108,112,104,97,110,117,109,101,114,105,99,115,32,111,114,32,95,0,0,0,0,0,0,0,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,95,49,50,51,52,53,54,55,56,57,48,0,119,111,114,100,0,0,0,0,116,101,114,109,105,110,97,108,32,116,121,112,101,32,105,115,32,117,110,107,110,111,119,110,0,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,97,32,112,108,111,116,32,110,97,109,101,0,0,0,114,109,97,114,36,103,105,110,0,0,0,0,0,0,0,0,110,97,109,101,0,0,0,0,9,110,111,32,112,111,108,97,114,32,100,105,115,116,97,110,99,101,32,116,111,32,114,117,108,101,114,32,119,105,108,108,32,98,101,32,115,104,111,119,110,10,0,0,0,0,0,0,100,101,108,97,121,0,0,0,102,105,114,115,116,32,0,0,109,111,117,115,36,105,110,103,0,0,0,0,0,0,0,0,115,99,114,111,108,108,32,114,105,103,104,116,46,0,0,0,109,111,117,115,101,0,0,0,121,32,115,105,122,101,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,0,0,0,101,120,116,101,110,100,32,105,110,32,100,111,95,115,121,115,116,101,109,95,102,117,110,99,0,0,0,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,121,32,115,105,122,101,0,0,0,0,0,0,0,0,112,111,108,121,103,111,110,0,120,32,115,105,122,101,32,111,117,116,32,111,102,32,114,97,110,103,101,0,0,0,0,0,101,120,112,101,99,116,105,110,103,32,120,32,115,105,122,101,0,0,0,0,0,0,0,0,115,36,105,122,101,0,0,0,67,97,110,110,111,116,32,109,105,120,32,115,99,114,101,101,110,32,111,114,32,99,104,97,114,97,99,116,101,114,32,99,111,111,114,100,115,32,119,105,116,104,32,112,108,111,116,32,99,111,111,114,100,115,0,0,70,111,110,116,32,102,105,108,101,32,39,37,115,39,32,105,115,32,101,109,112,116,121,0,115,117,98,115,116,114,0,0,115,101,116,32,116,101,114,109,111,112,116,32,101,110,104,0,67,111,109,109,97,110,100,32,39,37,115,39,32,103,101,110,101,114,97,116,101,115,32,101,109,112,116,121,32,111,117,116,112,117,116,0,0,0,0,0,67,111,109,109,97,110,100,32,39,37,115,39,32,103,101,110,101,114,97,116,101,100,32,101,114,114,111,114,32,101,120,105,116,99,111,100,101,32,37,100,0,0,0,0,0,0,0,0,108,109,97,114,36,103,105,110,0,0,0,0,0,0,0,0,68,105,109,95,50,0,0,0,9,100,105,115,116,97,110,99,101,32,116,111,32,114,117,108,101,114,32,119,105,108,108,32,98,101,32,115,104,111,119,32,105,110,32,112,111,108,97,114,32,99,111,111,114,100,105,110,97,116,101,115,10,0,0,0,103,105,102,0,0,0,0,0,111,98,106,101,99,116,32,110,111,116,32,102,111,117,110,100,0,0,0,0,0,0,0,0,70,111,110,116,32,102,105,108,101,32,39,37,115,39,32,99,111,110,116,97,105,110,115,32,116,104,101,32,102,111,110,116,32,39,37,115,39,10,0,0,60,115,104,105,102,116,45,119,104,101,101,108,45,100,111,119,110,62,0,0,0,0,0,0,102,111,110,116,45,102,97,109,105,108,121,0,0,0,0,0,70,111,110,116,32,102,105,108,101,32,39,37,115,39,32,110,111,116,32,102,111,117,110,100,0,0,0,0,0,0,0,0,42,118,101,114,121,42,32,108,111,110,103,32,115,121,115,116,101,109,32,99,97,108,108,32,111,117,116,112,117,116,32,104,97,115,32,98,101,101,110,32,116,114,117,110,99,97,116,101,100,0,0,0,0,0,0,0,67,111,117,108,100,32,110,111,116,32,101,120,101,99,117,116,101,32,112,105,112,101,32,39,37,115,39,0,0,0,0,0,10,9,60,99,105,114,99,108,101,32,105,100,61,39,103,112,68,111,116,39,32,114,61,39,48,46,53,39,32,115,116,114,111,107,101,45,119,105,100,116,104,61,39,48,46,53,39,47,62,10,9,60,112,97,116,104,32,105,100,61,39,103,112,80,116,48,39,32,115,116,114,111,107,101,45,119,105,100,116,104,61,39,37,46,51,102,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,100,61,39,77,45,49,44,48,32,104,50,32,77,48,44,45,49,32,118,50,39,47,62,10,9,60,112,97,116,104,32,105,100,61,39,103,112,80,116,49,39,32,115,116,114,111,107,101,45,119,105,100,116,104,61,39,37,46,51,102,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,100,61,39,77,45,49,44,45,49,32,76,49,44,49,32,77,49,44,45,49,32,76,45,49,44,49,39,47,62,10,9,60,112,97,116,104,32,105,100,61,39,103,112,80,116,50,39,32,115,116,114,111,107,101,45,119,105,100,116,104,61,39,37,46,51,102,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,100,61,39,77,45,49,44,48,32,76,49,44,48,32,77,48,44,45,49,32,76,48,44,49,32,77,45,49,44,45,49,32,76,49,44,49,32,77,45,49,44,49,32,76,49,44,45,49,39,47,62,10,9,60,114,101,99,116,32,105,100,61,39,103,112,80,116,51,39,32,115,116,114,111,107,101,45,119,105,100,116,104,61,39,37,46,51,102,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,120,61,39,45,49,39,32,121,61,39,45,49,39,32,119,105,100,116,104,61,39,50,39,32,104,101,105,103,104,116,61,39,50,39,47,62,10,9,60,114,101,99,116,32,105,100,61,39,103,112,80,116,52,39,32,115,116,114,111,107,101,45,119,105,100,116,104,61,39,37,46,51,102,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,102,105,108,108,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,120,61,39,45,49,39,32,121,61,39,45,49,39,32,119,105,100,116,104,61,39,50,39,32,104,101,105,103,104,116,61,39,50,39,47,62,10,9,60,99,105,114,99,108,101,32,105,100,61,39,103,112,80,116,53,39,32,115,116,114,111,107,101,45,119,105,100,116,104,61,39,37,46,51,102,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,99,120,61,39,48,39,32,99,121,61,39,48,39,32,114,61,39,49,39,47,62,10,9,60,117,115,101,32,120,108,105,110,107,58,104,114,101,102,61,39,35,103,112,80,116,53,39,32,105,100,61,39,103,112,80,116,54,39,32,102,105,108,108,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,115,116,114,111,107,101,61,39,110,111,110,101,39,47,62,10,9,60,112,97,116,104,32,105,100,61,39,103,112,80,116,55,39,32,115,116,114,111,107,101,45,119,105,100,116,104,61,39,37,46,51,102,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,100,61,39,77,48,44,45,49,46,51,51,32,76,45,49,46,51,51,44,48,46,54,55,32,76,49,46,51,51,44,48,46,54,55,32,122,39,47,62,10,9,60,117,115,101,32,120,108,105,110,107,58,104,114,101,102,61,39,35,103,112,80,116,55,39,32,105,100,61,39,103,112,80,116,56,39,32,102,105,108,108,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,115,116,114,111,107,101,61,39,110,111,110,101,39,47,62,10,9,60,117,115,101,32,120,108,105,110,107,58,104,114,101,102,61,39,35,103,112,80,116,55,39,32,105,100,61,39,103,112,80,116,57,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,116,114,97,110,115,102,111,114,109,61,39,114,111,116,97,116,101,40,49,56,48,41,39,47,62,10,9,60,117,115,101,32,120,108,105,110,107,58,104,114,101,102,61,39,35,103,112,80,116,57,39,32,105,100,61,39,103,112,80,116,49,48,39,32,102,105,108,108,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,115,116,114,111,107,101,61,39,110,111,110,101,39,47,62,10,9,60,117,115,101,32,120,108,105,110,107,58,104,114,101,102,61,39,35,103,112,80,116,51,39,32,105,100,61,39,103,112,80,116,49,49,39,32,115,116,114,111,107,101,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,116,114,97,110,115,102,111,114,109,61,39,114,111,116,97,116,101,40,52,53,41,39,47,62,10,9,60,117,115,101,32,120,108,105,110,107,58,104,114,101,102,61,39,35,103,112,80,116,49,49,39,32,105,100,61,39,103,112,80,116,49,50,39,32,102,105,108,108,61,39,99,117,114,114,101,110,116,67,111,108,111,114,39,32,115,116,114,111,107,101,61,39,110,111,110,101,39,47,62,10,60,47,100,101,102,115,62,10,0,60,100,101,102,115,62,10,0,9,37,115,32,114,97,110,103,101,32,114,101,115,116,114,105,99,116,101,100,32,116,111,32,91,0,0,0,0,0,0,0,47,62,10,0,0,0,0,0,32,102,105,108,108,61,34,35,37,48,54,120,34,0,0,0,115,116,114,115,116,114,116,0,85,110,107,110,111,119,110,32,111,114,32,97,109,98,105,103,117,111,117,115,32,116,101,114,109,105,110,97,108,32,110,97,109,101,32,39,37,115,39,10,0,0,0,0,0,0,0,0,60,103,32,105,100,61,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,34,62,10,10,0,0,0,0,0,0,0,109,97,114,36,103,105,110,0,60,103,32,105,100,61,34,103,110,117,112,108,111,116,95,99,97,110,118,97,115,34,32,111,110,99,108,105,99,107,61,34,103,110,117,112,108,111,116,95,115,118,103,46,116,111,103,103,108,101,67,111,111,114,100,66,111,120,40,101,118,116,41,34,32,111,110,109,111,117,115,101,109,111,118,101,61,34,103,110,117,112,108,111,116,95,115,118,103,46,109,111,118,101,67,111,111,114,100,66,111,120,40,101,118,116,41,34,62,10,10,0,9,110,111,32,122,111,111,109,32,99,111,111,114,100,105,110,97,116,101,115,32,119,105,108,108,32,98,101,32,100,114,97,119,110,10,0,0,0,0,0,10,60,33,45,45,32,65,108,115,111,32,116,114,97,99,107,32,109,111,117,115,101,32,119,104,101,110,32,105,116,32,105,115,32,111,110,32,97,32,112,108,111,116,32,101,108,101,109,101,110,116,32,45,45,62,10,0,0,0,0,0,0,0,0,32,102,105,108,108,115,116,121,108,101,32,0,0,0,0,0,115,99,114,111,108,108,32,108,101,102,116,32,40,105,110,32,45,88,32,100,105,114,101,99,116,105,111,110,41,46,0,0,111,110,99,108,105,99,107,61,34,103,110,117,112,108,111,116,95,115,118,103,46,116,111,103,103,108,101,67,111,111,114,100,66,111,120,40,101,118,116,41,34,32,32,111,110,109,111,117,115,101,109,111,118,101,61,34,103,110,117,112,108,111,116,95,115,118,103,46,109,111,118,101,67,111,111,114,100,66,111,120,40,101,118,116,41,34,47,62,10,0,0,0,0,0,0,0,32,102,105,108,108,61,34,35,37,48,54,120,34,32,115,116,114,111,107,101,61,34,98,108,97,99,107,34,32,115,116,114,111,107,101,45,119,105,100,116,104,61,34,49,34,10,0,0,100,111,95,115,121,115,116,101,109,95,102,117,110,99,0,0,60,114,101,99,116,32,120,61,34,37,100,34,32,121,61,34,37,100,34,32,119,105,100,116,104,61,34,37,100,34,32,104,101,105,103,104,116,61,34,37,100,34,0,0,0,0,0,0,10,60,33,45,45,32,84,105,101,32,109,111,117,115,105,110,103,32,116,111,32,101,110,116,105,114,101,32,98,111,117,110,100,105,110,103,32,98,111,120,32,111,102,32,116,104,101,32,112,108,111,116,32,45,45,62,10,0,0,0,0,0,0,0,60,115,99,114,105,112,116,32,108,97,110,103,117,97,103,101,61,34,106,97,118,97,83,99,114,105,112,116,34,32,84,89,80,69,61,34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,32,62,32,60,33,91,67,68,65,84,65,91,10,0,0,0,0,0,0,0,35,32,70,73,88,69,68,0,70,97,105,108,101,100,32,116,111,32,105,110,115,101,114,116,32,106,97,118,97,115,99,114,105,112,116,32,102,105,108,101,32,37,115,10,0,0,0,0,115,116,114,108,101,110,0,0,106,97,118,97,115,99,114,105,112,116,32,110,97,109,101,0,109,97,112,36,112,105,110,103,51,100,0,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,106,115,0,0,9,122,111,111,109,32,99,111,111,114,100,105,110,97,116,101,115,32,119,105,108,108,32,98,101,32,100,114,97,119,110,10,0,0,0,0,0,0,0,0,60,115,99,114,105,112,116,32,116,121,112,101,61,34,116,101,120,116,47,106,97,118,97,115,99,114,105,112,116,34,32,120,108,105,110,107,58,104,114,101,102,61,34,37,115,103,110,117,112,108,111,116,95,115,118,103,46,106,115,34,47,62,10,0,60,115,104,105,102,116,45,119,104,101,101,108,45,117,112,62,0,0,0,0,0,0,0,0,116,111,111,32,109,97,110,121,32,97,114,103,117,109,101,110,116,115,32,102,111,114,32,39,99,97,108,108,32,60,102,105,108,101,62,39,0,0,0,0,106,115,100,105,114,0,0,0,112,111,112,101,110,32,102,97,105,108,101,100,0,0,0,0,47,117,115,114,47,108,111,99,97,108,47,115,104,97,114,101,47,103,110,117,112,108,111,116,47,52,46,54,47,106,115,0,60,100,101,115,99,62,80,114,111,100,117,99,101,100,32,98,121,32,71,78,85,80,76,79,84,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,32,60,47,100,101,115,99,62,10,10,0,0,0,0,71,110,117,112,108,111,116,0,85,110,100,101,102,105,110,101,100,32,118,97,108,117,101,32,100,117,114,105,110,103,32,102,117,110,99,116,105,111,110,32,101,118,97,108,117,97,116,105,111,110,0,0,0,0,0,0,60,116,105,116,108,101,62,37,115,60,47,116,105,116,108,101,62,10,0,0,0,0,0,0,62,10,10,0,0,0,0,0,103,112,114,105,110,116,102,0,84,101,114,109,105,110,97,108,32,116,121,112,101,32,115,101,116,32,116,111,32,39,37,115,39,10,0,0,0,0,0,0,32,120,109,108,110,115,58,120,108,105,110,107,61,34,104,116,116,112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,49,57,57,57,47,120,108,105,110,107,34,10,0,0,0,0,109,97,112,36,112,105,110,103,0,0,0,0,0,0,0,0,112,108,111,116,32,116,105,116,108,101,0,0,0,0,0,0,32,120,109,108,110,115,61,34,104,116,116,112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,50,48,48,48,47,115,118,103,34,10,0,0,0,0,9,109,111,117,115,101,32,105,115,32,111,110,10,0,0,0,10,32,118,105,101,119,66,111,120,61,34,48,32,48,32,37,117,32,37,117,34,10,0,0,115,99,114,111,108,108,32,100,111,119,110,46,0,0,0,0,46,46,0,0,0,0,0,0,10,32,119,105,100,116,104,61,34,37,117,34,32,104,101,105,103,104,116,61,34,37,117,34,32,0,0,0,0,0,0,0,111,110,108,111,97,100,61,34,105,102,32,40,116,121,112,101,111,102,40,103,110,117,112,108,111,116,95,115,118,103,41,33,61,39,117,110,100,101,102,105,110,101,100,39,41,32,103,110,117,112,108,111,116,95,115,118,103,46,73,110,105,116,40,101,118,116,41,34,32,0,0,0,37,115,32,105,115,32,110,111,116,32,97,32,115,116,114,105,110,103,32,118,97,114,105,97,98,108,101,0,0,0,0,0,60,63,120,109,108,32,118,101,114,115,105,111,110,61,34,49,46,48,34,32,37,115,32,115,116,97,110,100,97,108,111,110,101,61,34,110,111,34,63,62,10,60,33,68,79,67,84,89,80,69,32,115,118,103,32,80,85,66,76,73,67,32,34,45,47,47,87,51,67,47,47,68,84,68,32,83,86,71,32,49,46,49,47,47,69,78,34,32,10,32,34,104,116,116,112,58,47,47,119,119,119,46,119,51,46,111,114,103,47,71,114,97,112,104,105,99,115,47,83,86,71,47,49,46,49,47,68,84,68,47,115,118,103,49,49,46,100,116,100,34,62,10,60,115,118,103,32,0,0,0,0,0,101,110,99,111,100,105,110,103,61,34,117,116,102,45,56,34,32,0,0,0,0,0,0,0,102,111,110,116,112,97,116,104,32,61,61,32,78,85,76,76,0,0,0,0,0,0,0,0,101,110,99,111,100,105,110,103,61,34,83,104,105,102,116,95,74,73,83,34,32,0,0,0,37,45,49,53,46,49,53,115,32,61,32,37,103,10,0,0,70,111,114,109,97,116,32,99,104,97,114,97,99,116,101,114,32,109,105,115,109,97,116,99,104,58,32,37,37,84,32,105,115,32,111,110,108,121,32,118,97,108,105,100,32,119,105,116,104,32,37,37,116,0,0,0,101,110,99,111,100,105,110,103,61,34,107,111,105,56,45,117,34,32,0,0,0,0,0,0,68,101,112,114,101,99,97,116,101,100,32,115,121,110,116,97,120,32,45,32,112,108,101,97,115,101,32,117,115,101,32,39,115,101,116,32,116,105,99,115,32,115,99,97,108,101,32,100,101,102,97,117,108,116,39,0,37,89,45,37,109,45,37,100,0,0,0,0,0,0,0,0,101,110,99,111,100,105,110,103,61,34,107,111,105,56,45,114,34,32,0,0,0,0,0,0,87,97,114,110,105,110,103,58,32,115,99,97,108,101,32,105,110,116,101,114,102,97,99,101,32,105,115,32,110,111,116,32,110,117,108,108,95,115,99,97,108,101,32,45,32,109,97,121,32,110,111,116,32,119,111,114,107,32,119,105,116,104,32,109,117,108,116,105,112,108,111,116,10,0,0,0,0,0,0,0,101,110,99,111,100,105,110,103,61,34,119,105,110,100,111,119,115,45,49,50,53,49,34,32,0,0,0,0,0,0,0,0,109,97,99,36,114,111,115,0,116,105,109,101,99,111,108,117,109,110,40,41,32,99,97,108,108,101,100,32,102,114,111,109,32,105,110,118,97,108,105,100,32,99,111,110,116,101,120,116,0,0,0,0,0,0,0,0,101,110,99,111,100,105,110,103,61,34,119,105,110,100,111,119,115,45,49,50,53,48,34,32,0,0,0,0,0,0,0,0,9,108,97,115,116,32,112,108,111,116,32,99,111,109,109,97,110,100,32,119,97,115,58,32,37,115,10,0,0,0,0,0,102,99,32,0,0,0,0,0,101,110,99,111,100,105,110,103,61,34,99,112,57,53,48,34,32,0,0,0,0,0,0,0,60,119,104,101,101,108,45,100,111,119,110,62,0,0,0,0,101,110,99,111,100,105,110,103,61,34,105,98,109,45,56,53,50,34,32,0,0,0,0,0,101,110,99,111,100,105,110,103,61,34,105,98,109,45,56,53,48,34,32,0,0,0,0,0,115,116,114,105,110,103,32,118,97,114,105,97,98,108,101,0,99,112,49,50,53,49,0,0,101,110,99,111,100,105,110,103,61,34,105,115,111,45,56,56,53,57,45,49,53,34,32,0,32,108,97,98,101,108,0,0,101,110,99,111,100,105,110,103,61,34,105,115,111,45,56,56,53,57,45,57,34,32,0,0,108,110,111,116,0,0,0,0,101,110,99,111,100,105,110,103,61,34,105,115,111,45,56,56,53,57,45,50,34,32,0,0,105,110,105,116,105,97,108,32,115,101,116,32,111,102,32,102,114,101,101,0,0,0,0,0,101,110,99,111,100,105,110,103,61,34,105,115,111,45,56,56,53,57,45,49,34,32,0,0,35,37,50,46,50,88,37,50,46,50,88,37,50,46,50,88,0,0,0,0,0,0,0,0,116,109,95,121,100,97,121,0,120,49,49,0,0,0,0,0,60,47,115,118,103,62,10,10,0,0,0,0,0,0,0,0,108,111,103,36,115,99,97,108,101,0,0,0,0,0,0,0,64,67,79,76,85,77,78,72,69,65,68,64,0,0,0,0,32,32,32,32,111,110,99,108,105,99,107,61,39,103,110,117,112,108,111,116,95,115,118,103,46,116,111,103,103,108,101,71,114,105,100,40,41,59,39,47,62,10,0,0,0,0,0,0,61,32,0,0,0,0,0,0,83,116,97,116,115,32,99,111,109,109,97,110,100,32,110,111,116,32,97,118,97,105,108,97,98,108,101,32,105,110,32,112,97,114,97,109,101,116,114,105,99,32,109,111,100,101,0,0,108,119,32,37,46,49,102,32,0,0,0,0,0,0,0,0,10,32,32,60,105,109,97,103,101,32,120,61,39,49,48,39,32,121,61,39,37,100,39,32,119,105,100,116,104,61,39,49,54,39,32,104,101,105,103,104,116,61,39,49,54,39,32,120,108,105,110,107,58,104,114,101,102,61,39,103,114,105,100,46,112,110,103,39,10,0,0,0,115,99,114,111,108,108,32,117,112,32,40,105,110,32,43,89,32,100,105,114,101,99,116,105,111,110,41,46,0,0,0,0,102,111,110,116,112,97,116,104,95,102,117,108,108,110,97,109,101,0,0,0,0,0,0,0,32,32,118,105,115,105,98,105,108,105,116,121,61,34,104,105,100,100,101,110,34,62,32,60,47,116,101,120,116,62,10,0,37,115,32,101,114,114,111,114,10,0,0,0,0,0,0,0,32,32,102,111,110,116,45,115,105,122,101,61,34,49,50,34,32,102,111,110,116,45,102,97,109,105,108,121,61,34,65,114,105,97,108,34,10,0,0,0,115,121,115,116,101,109,40,41,32,102,97,105,108,101,100,0,10,32,32,60,116,101,120,116,32,105,100,61,34,99,111,111,114,100,95,116,101,120,116,34,32,116,101,120,116,45,97,110,99,104,111,114,61,34,115,116,97,114,116,34,32,112,111,105,110,116,101,114,45,101,118,101,110,116,115,61,34,110,111,110,101,34,10,0,0,0,0,0,93,93,62,10,60,47,115,99,114,105,112,116,62,10,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,116,105,109,101,97,120,105,115,95,120,32,61,32,34,34,59,10,0,0,0,0,0,0,114,101,115,117,108,116,97,110,116,0,0,0,0,0,0,0,105,110,118,97,108,105,100,32,111,114,32,100,101,112,114,101,99,97,116,101,100,32,115,121,110,116,97,120,0,0,0,0,68,97,116,101,84,105,109,101,0,0,0,0,0,0,0,0,84,105,109,101,0,0,0,0,116,109,95,119,100,97,121,0,88,49,49,0,0,0,0,0,68,97,116,101,0,0,0,0,108,111,99,36,97,108,101,0,97,110,97,108,121,122,101,95,115,103,108,95,99,111,108,117,109,110,0,0,0,0,0,0,99,111,108,117,109,110,104,101,97,100,40,41,32,99,97,108,108,101,100,32,102,114,111,109,32,105,110,118,97,108,105,100,32,99,111,110,116,101,120,116,0,0,0,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,116,105,109,101,97,120,105,115,95,120,32,61,32,34,37,115,34,59,10,0,0,0,0,9,37,45,42,115,32,0,0,102,112,101,95,116,114,97,112,0,0,0,0,0,0,0,0,102,110,97,109,101,0,0,0,106,112,101,103,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,97,120,105,115,95,120,109,97,120,32,61,32,37,46,51,102,59,10,0,0,0,0,0,60,119,104,101,101,108,45,117,112,62,0,0,0,0,0,0,114,101,99,117,114,115,105,118,101,102,117,108,108,110,97,109,101,0,0,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,97,120,105,115,95,120,109,105,110,32,61,32,37,46,51,102,59,10,0,0,0,0,0,116,114,97,99,101,95,99,111,110,116,111,117,114,58,32,117,110,101,120,112,101,99,116,101,100,32,101,110,100,32,111,102,32,99,111,110,116,111,117,114,10,0,0,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,108,111,103,97,120,105,115,95,114,32,61,32,37,100,59,10,0,0,0,0,0,0,0,73,109,112,111,115,115,105,98,108,101,32,99,97,115,101,32,105,110,32,115,119,105,116,99,104,0,0,0,0,0,0,0,32,58,32,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,108,111,103,97,120,105,115,95,121,32,61,32,37,100,59,10,0,0,0,0,0,0,0,112,109,51,100,32,115,99,97,110,32,97,114,114,97,121,0,116,36,105,116,108,101,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,108,111,103,97,120,105,115,95,120,32,61,32,37,100,59,10,0,0,0,0,0,0,0,108,97,98,101,108,112,111,105,110,116,32,116,101,120,116,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,97,120,105,115,95,121,50,109,105,110,32,61,32,34,110,111,110,101,34,10,0,0,0,67,111,117,108,100,32,110,111,116,32,108,105,110,101,98,117,102,102,101,114,32,115,116,100,111,117,116,10,0,0,0,0,10,10,32,73,116,101,114,97,116,105,111,110,32,37,100,10,32,87,83,83,82,32,32,32,32,32,32,32,32,58,32,37,45,49,53,103,32,32,32,100,101,108,116,97,40,87,83,83,82,41,47,87,83,83,82,32,32,32,58,32,37,103,10,32,100,101,108,116,97,40,87,83,83,82,41,32,58,32,37,45,49,53,103,32,32,32,108,105,109,105,116,32,102,111,114,32,115,116,111,112,112,105,110,103,32,58,32,37,103,10,32,108,97,109,98,100,97,9,32,32,58,32,37,103,10,10,37,115,32,112,97,114,97,109,101,116,101,114,32,118,97,108,117,101,115,10,10,0,0,0,0,0,112,108,111,116,95,97,120,105,115,95,121,50,109,97,120,0,69,120,112,101,99,116,105,110,103,32,105,116,101,114,97,116,111,114,32,9,102,111,114,32,91,60,118,97,114,62,32,61,32,60,115,116,97,114,116,62,32,58,32,60,101,110,100,62,93,10,9,9,9,111,114,9,102,111,114,32,91,60,118,97,114,62,32,105,110,32,34,115,116,114,105,110,103,32,111,102,32,119,111,114,100,115,34,93,0,0,0,0,0,0,0,0,71,80,86,65,76,95,89,50,95,77,65,88,0,0,0,0,116,109,95,121,101,97,114,0,117,110,107,110,111,119,110,32,111,114,32,97,109,98,105,103,117,111,117,115,32,116,101,114,109,105,110,97,108,32,116,121,112,101,59,32,116,121,112,101,32,106,117,115,116,32,39,115,101,116,32,116,101,114,109,105,110,97,108,39,32,102,111,114,32,97,32,108,105,115,116,0,112,108,111,116,95,97,120,105,115,95,121,50,109,105,110,0,83,121,110,116,97,120,32,101,114,114,111,114,58,32,109,105,115,115,105,110,103,32,98,108,111,99,107,32,116,101,114,109,105,110,97,116,111,114,32,125,0,0,0,0,0,0,0,0,108,111,97,36,100,112,97,116,104,0,0,0,0,0,0,0,98,108,97,110,107,0,0,0,116,117,114,110,105,110,103,32,114,117,108,101,114,32,111,110,46,10,0,0,0,0,0,0,71,80,86,65,76,95,89,50,95,77,73,78,0,0,0,0,101,120,116,114,97,110,101,111,117,115,32,97,114,103,117,109,101,110,116,115,32,105,110,32,115,101,116,32,116,105,99,115,0,0,0,0,0,0,0,0,123,32,48,46,57,57,57,57,57,32,121,48,32,115,117,98,32,103,32,0,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,97,120,105,115,95,120,50,109,105,110,32,61,32,34,110,111,110,101,34,10,0,0,0,99,97,108,108,0,0,0,0,121,32,112,111,115,0,0,0,118,101,114,116,105,99,97,108,32,109,111,116,105,111,110,32,45,45,32,99,104,97,110,103,101,32,120,121,112,108,97,110,101,0,0,0,0,0,0,0,102,97,105,108,115,97,102,101,0,0,0,0,0,0,0,0,112,108,111,116,95,97,120,105,115,95,120,50,109,97,120,0,99,97,110,32,111,110,108,121,32,109,111,100,32,105,110,116,115,0,0,0,0,0,0,0,71,80,86,65,76,95,88,50,95,77,65,88,0,0,0,0,83,111,114,114,121,44,32,110,111,32,104,101,108,112,32,102,111,114,32,39,37,115,39,10,0,0,0,0,0,0,0,0,112,108,111,116,95,97,120,105,115,95,120,50,109,105,110,0,71,80,86,65,76,95,88,50,95,77,73,78,0,0,0,0,103,110,117,112,108,111,116,95,115,118,103,46,112,108,111,116,95,97,120,105,115,95,114,109,105,110,32,61,32,37,103,59,10,0,0,0,0,0,0,0,102,97,108,115,101,0,0,0,116,114,117,101,0,0,0,0,116,109,95,109,111,110,0,0,117,110,107,110,111,119,110,0,103,110,117,112,108,111,116,95,115,118,103,46,112,111,108,97,114,95,109,111,100,101,32,61,32,37,115,59,10,0,0,0,108,116,0,0,0,0,0,0,105,110,100,101,120,95,109,97,120,0,0,0,0,0,0,0,112,97,114,116,105,97,108,32,109,97,116,99,104,32,97,103,97,105,110,115,116,32,99,111,108,117,109,110,32,37,100,32,104,101,97,100,101,114,32,37,115,0,0,0,0,0,0,0,112,108,111,116,95,97,120,105,115,95,121,109,97,120,0,0,68,101,112,114,101,99,97,116,101,100,32,115,121,110,116,97,120,32,45,32,112,108,101,97,115,101,32,117,115,101,32,39,115,101,116,32,116,105,99,115,32,115,99,97,108,101,39,32,107,101,121,119,111,114,100,0,71,80,86,65,76,95,89,95,77,65,88,0,0,0,0,0,60,83,104,105,102,116,45,66,50,45,77,111,116,105,111,110,62,0,0,0,0,0,0,0,112,108,111,116,95,97,120,105,115,95,121,109,105,110,0,0,71,80,86,65,76,95,89,95,77,73,78,0,0,0,0,0,72,101,108,112,32,116,111,112,105,99,58,32,0,0,0,0])
+.concat([118,0,0,0,0,0,0,0,111,117,116,32,111,102,32,109,101,109,111,114,121,32,102,111,114,32,37,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,37,32,71,78,85,80,76,79,84,58,32,76,97,84,101,88,32,112,105,99,116,117,114,101,32,117,115,105,110,103,32,116,112,105,99,32,92,115,112,101,99,105,97,108,115,10,37,37,32,32,32,32,32,32,32,32,32,32,119,105,116,104,32,37,100,32,112,111,105,110,116,32,116,121,112,101,115,32,97,110,100,32,37,100,32,108,105,110,101,32,116,121,112,101,115,10,37,37,32,79,112,116,105,111,110,115,58,32,112,111,105,110,116,115,105,122,101,32,61,32,37,100,44,32,108,105,110,101,119,105,100,116,104,32,61,32,37,100,44,32,105,110,116,101,114,118,97,108,32,61,32,37,102,10,37,37,32,84,111,32,99,104,97,110,103,101,32,97,98,111,118,101,32,111,112,116,105,111,110,115,44,32,115,97,121,58,10,37,37,32,115,101,116,32,116,101,114,109,105,110,97,108,32,116,112,105,99,32,112,111,105,110,116,115,105,122,101,95,118,97,108,117,101,32,108,105,110,101,119,105,100,116,104,95,118,97,108,117,101,32,105,110,116,101,114,118,97,108,95,118,97,108,117,101,10,37,37,32,40,112,111,105,110,116,115,105,122,101,32,97,110,100,32,108,105,110,101,119,105,100,116,104,32,45,32,105,110,116,101,103,101,114,115,32,105,110,32,109,105,108,108,105,45,105,110,99,104,101,115,46,10,37,37,32,32,105,110,116,101,114,118,97,108,32,45,32,97,32,102,108,111,97,116,32,105,110,32,105,110,99,104,101,115,46,32,32,73,102,32,122,101,114,111,32,105,115,32,115,112,101,99,105,102,105,101,100,44,32,10,37,37,32,32,116,104,101,32,100,101,102,97,117,108,116,32,118,97,108,117,101,32,105,115,32,99,104,111,115,101,110,46,41,10,92,115,101,116,108,101,110,103,116,104,123,92,117,110,105,116,108,101,110,103,116,104,125,123,37,102,105,110,125,37,37,10,0,0,0,0,37,115,92,98,101,103,105,110,123,112,105,99,116,117,114,101,125,40,37,100,44,37,100,41,40,37,100,44,37,100,41,37,37,32,37,115,10,0,0,0,64,74,1,0,0,0,0,0,184,73,1,0,1,0,0,0,0,0,0,0,2,0,0,0,232,195,1,0,0,0,0,0,144,195,1,0,1,0,0,0,80,195,1,0,1,0,0,0,144,245,1,0,2,0,0,0,40,245,1,0,2,0,0,0,32,50,1,0,3,0,0,0,232,49,1,0,4,0,0,0,200,49,1,0,5,0,0,0,40,195,1,0,6,0,0,0,192,49,1,0,7,0,0,0,192,229,1,0,8,0,0,0,72,236,1,0,10,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,166,1,0,200,165,1,0,248,164,1,0,184,164,1,0,88,164,1,0,8,164,1,0,176,163,1,0,120,163,1,0,64,163,1,0,232,162,1,0,152,162,1,0,248,161,1,0,4,0,0,0,3,0,0,0,3,0,0,0,4,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,98,101,103,105,110,123,116,101,120,100,114,97,119,125,10,92,110,111,114,109,97,108,115,105,122,101,10,92,105,102,120,92,112,97,116,104,68,69,70,73,78,69,68,92,114,101,108,97,120,92,101,108,115,101,92,108,101,116,92,112,97,116,104,68,69,70,73,78,69,68,92,114,101,108,97,120,10,32,92,100,101,102,92,81,116,71,102,114,123,92,105,102,120,32,40,92,84,71,114,101,32,92,108,101,116,92,89,104,101,116,84,92,99,112,97,116,104,92,101,108,115,101,92,108,101,116,92,89,104,101,116,84,92,114,101,108,97,120,92,102,105,92,89,104,101,116,84,125,10,32,92,100,101,102,92,112,97,116,104,32,40,35,49,32,35,50,41,123,92,109,111,118,101,32,40,35,49,32,35,50,41,92,102,117,116,117,114,101,108,101,116,92,84,71,114,101,92,81,116,71,102,114,125,10,32,92,100,101,102,92,99,112,97,116,104,32,40,35,49,32,35,50,41,123,92,108,118,101,99,32,40,35,49,32,35,50,41,92,102,117,116,117,114,101,108,101,116,92,84,71,114,101,92,81,116,71,102,114,125,10,92,102,105,10,92,100,114,97,119,100,105,109,32,112,116,10,92,115,101,116,117,110,105,116,115,99,97,108,101,32,37,50,46,50,102,10,92,108,105,110,101,119,100,32,37,100,10,92,116,101,120,116,114,101,102,32,104,58,76,32,118,58,67,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,18,0,0,0,0,0,0,255,255,255,255,0,0,0,0,112,23,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,152,190,2,0,192,31,1,0,184,31,1,0,72,31,1,0,24,31,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,6,0,0,0,6,0,0,0,6,0,0,0,0,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,7,0,0,0,7,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,7,0,0,0,8,0,0,0,9,0,0,0,10,0,0,0,11,0,0,0,12,0,0,0,13,0,0,0,14,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,3,0,0,0,5,0,0,0,5,0,0,0,5,0,0,0,7,0,0,0,7,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,32,3,0,240,30,3,0,160,31,3,0,8,32,3,0,240,31,3,0,192,31,3,0,232,30,3,0,144,31,3,0,184,31,3,0,0,0,0,0,56,32,3,0,8,32,3,0,240,31,3,0,200,31,3,0,192,31,3,0,184,31,3,0,160,31,3,0,144,31,3,0,128,31,3,0,56,31,3,0,24,31,3,0,8,31,3,0,240,30,3,0,232,30,3,0,216,30,3,0,184,30,3,0,152,30,3,0,144,30,3,0,128,30,3,0,64,30,3,0,48,30,3,0,248,29,3,0,208,29,3,0,176,29,3,0,160,29,3,0,152,29,3,0,112,29,3,0,104,29,3,0,88,29,3,0,40,29,3,0,8,29,3,0,248,28,3,0,224,28,3,0,216,28,3,0,184,28,3,0,176,28,3,0,160,28,3,0,152,28,3,0,64,28,3,0,40,28,3,0,16,28,3,0,216,27,3,0,208,27,3,0,200,27,3,0,192,27,3,0,184,27,3,0,168,27,3,0,160,27,3,0,144,27,3,0,64,27,3,0,48,27,3,0,240,26,3,0,232,26,3,0,224,26,3,0,216,26,3,0,208,26,3,0,184,26,3,0,176,26,3,0,160,26,3,0,80,26,3,0,64,26,3,0,48,26,3,0,208,25,3,0,200,25,3,0,192,25,3,0,136,25,3,0,112,25,3,0,88,25,3,0,80,25,3,0,32,25,3,0,16,25,3,0,248,24,3,0,224,24,3,0,216,24,3,0,208,24,3,0,200,24,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,254,1,0,3,0,0,0,32,50,1,0,0,0,0,0,232,49,1,0,1,0,0,0,184,208,2,0,2,0,0,0,0,224,1,0,4,0,0,0,192,218,1,0,5,0,0,0,152,208,2,0,9,0,0,0,144,195,1,0,10,0,0,0,80,195,1,0,10,0,0,0,72,208,2,0,11,0,0,0,56,208,2,0,12,0,0,0,24,208,2,0,12,0,0,0,232,247,1,0,13,0,0,0,80,248,1,0,14,0,0,0,48,246,1,0,15,0,0,0,224,245,1,0,15,0,0,0,144,245,1,0,16,0,0,0,40,245,1,0,16,0,0,0,192,253,1,0,39,0,0,0,248,207,2,0,17,0,0,0,224,207,2,0,18,0,0,0,200,207,2,0,19,0,0,0,16,24,2,0,21,0,0,0,112,207,2,0,20,0,0,0,200,210,2,0,22,0,0,0,176,210,2,0,23,0,0,0,80,207,2,0,24,0,0,0,96,244,1,0,30,0,0,0,48,207,2,0,25,0,0,0,32,207,2,0,26,0,0,0,96,211,2,0,27,0,0,0,24,211,2,0,28,0,0,0,192,229,1,0,29,0,0,0,224,252,1,0,7,0,0,0,24,253,1,0,8,0,0,0,200,251,1,0,37,0,0,0,72,251,1,0,38,0,0,0,56,117,1,0,31,0,0,0,240,206,2,0,32,0,0,0,224,206,2,0,33,0,0,0,152,206,2,0,34,0,0,0,72,206,2,0,35,0,0,0,48,206,2,0,36,0,0,0,232,205,2,0,40,0,0,0,200,205,2,0,41,0,0,0,136,82,3,0,42,0,0,0,184,205,2,0,43,0,0,0,0,0,0,0,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,101,108,118,101,116,105,99,97,44,49,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,76,101,118,101,108,49,32,83,117,112,112,114,101,115,115,80,68,70,77,97,114,107,32,111,114,32,10,123,125,32,123,10,47,83,68,105,99,116,32,49,48,32,100,105,99,116,32,100,101,102,10,115,121,115,116,101,109,100,105,99,116,32,47,112,100,102,109,97,114,107,32,107,110,111,119,110,32,110,111,116,32,123,10,32,32,117,115,101,114,100,105,99,116,32,47,112,100,102,109,97,114,107,32,115,121,115,116,101,109,100,105,99,116,32,47,99,108,101,97,114,116,111,109,97,114,107,32,103,101,116,32,112,117,116,10,125,32,105,102,10,83,68,105,99,116,32,98,101,103,105,110,32,91,10,32,32,47,84,105,116,108,101,32,40,37,115,41,10,32,32,47,83,117,98,106,101,99,116,32,40,103,110,117,112,108,111,116,32,112,108,111,116,41,10,32,32,47,67,114,101,97,116,111,114,32,40,103,110,117,112,108,111,116,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,41,10,32,32,47,65,117,116,104,111,114,32,40,37,115,41,10,37,37,32,32,47,80,114,111,100,117,99,101,114,32,40,103,110,117,112,108,111,116,41,10,37,37,32,32,47,75,101,121,119,111,114,100,115,32,40,41,10,32,32,47,67,114,101,97,116,105,111,110,68,97,116,101,32,40,37,115,41,10,32,32,47,68,79,67,73,78,70,79,32,112,100,102,109,97,114,107,10,101,110,100,10,125,32,105,102,101,108,115,101,10,0,0,0,37,37,37,37,69,110,100,67,111,109,109,101,110,116,115,10,37,37,37,37,66,101,103,105,110,80,114,111,108,111,103,10,47,103,110,117,100,105,99,116,32,50,53,54,32,100,105,99,116,32,100,101,102,10,103,110,117,100,105,99,116,32,98,101,103,105,110,10,37,37,10,37,37,32,84,104,101,32,102,111,108,108,111,119,105,110,103,32,116,114,117,101,47,102,97,108,115,101,32,102,108,97,103,115,32,109,97,121,32,98,101,32,101,100,105,116,101,100,32,98,121,32,104,97,110,100,32,105,102,32,100,101,115,105,114,101,100,46,10,37,37,32,84,104,101,32,117,110,105,116,32,108,105,110,101,32,119,105,100,116,104,32,97,110,100,32,103,114,97,121,115,99,97,108,101,32,105,109,97,103,101,32,103,97,109,109,97,32,99,111,114,114,101,99,116,105,111,110,32,109,97,121,32,97,108,115,111,32,98,101,32,99,104,97,110,103,101,100,46,10,37,37,10,47,67,111,108,111,114,32,37,115,32,100,101,102,10,47,66,108,97,99,107,116,101,120,116,32,37,115,32,100,101,102,10,47,83,111,108,105,100,32,37,115,32,100,101,102,10,47,68,97,115,104,108,101,110,103,116,104,32,37,103,32,100,101,102,10,47,76,97,110,100,115,99,97,112,101,32,37,115,32,100,101,102,10,47,76,101,118,101,108,49,32,37,115,32,100,101,102,10,47,82,111,117,110,100,101,100,32,37,115,32,100,101,102,10,47,67,108,105,112,84,111,66,111,117,110,100,105,110,103,66,111,120,32,37,115,32,100,101,102,10,47,83,117,112,112,114,101,115,115,80,68,70,77,97,114,107,32,102,97,108,115,101,32,100,101,102,10,47,84,114,97,110,115,112,97,114,101,110,116,80,97,116,116,101,114,110,115,32,102,97,108,115,101,32,100,101,102,10,47,103,110,117,108,105,110,101,119,105,100,116,104,32,37,46,51,102,32,100,101,102,10,47,117,115,101,114,108,105,110,101,119,105,100,116,104,32,103,110,117,108,105,110,101,119,105,100,116,104,32,100,101,102,10,47,71,97,109,109,97,32,49,46,48,32,100,101,102,10,47,66,97,99,107,103,114,111,117,110,100,67,111,108,111,114,32,123,37,46,51,102,32,37,46,51,102,32,37,46,51,102,125,32,100,101,102,10,37,37,10,47,118,115,104,105,102,116,32,37,100,32,100,101,102,10,47,100,108,49,32,123,10,32,32,37,46,49,102,32,68,97,115,104,108,101,110,103,116,104,32,109,117,108,32,109,117,108,10,32,32,82,111,117,110,100,101,100,32,123,32,99,117,114,114,101,110,116,108,105,110,101,119,105,100,116,104,32,48,46,55,53,32,109,117,108,32,115,117,98,32,100,117,112,32,48,32,108,101,32,123,32,112,111,112,32,48,46,48,49,32,125,32,105,102,32,125,32,105,102,10,125,32,100,101,102,10,47,100,108,50,32,123,10,32,32,37,46,49,102,32,68,97,115,104,108,101,110,103,116,104,32,109,117,108,32,109,117,108,10,32,32,82,111,117,110,100,101,100,32,123,32,99,117,114,114,101,110,116,108,105,110,101,119,105,100,116,104,32,48,46,55,53,32,109,117,108,32,97,100,100,32,125,32,105,102,10,125,32,100,101,102,10,47,104,112,116,95,32,37,46,49,102,32,100,101,102,10,47,118,112,116,95,32,37,46,49,102,32,100,101,102,10,47,104,112,116,32,104,112,116,95,32,100,101,102,10,47,118,112,116,32,118,112,116,95,32,100,101,102,10,0,0,0,0,0,0,37,37,37,37,67,114,101,97,116,111,114,58,32,103,110,117,112,108,111,116,32,37,115,32,112,97,116,99,104,108,101,118,101,108,32,37,115,10,37,37,37,37,67,114,101,97,116,105,111,110,68,97,116,101,58,32,37,115,10,37,37,37,37,68,111,99,117,109,101,110,116,70,111,110,116,115,58,32,37,115,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,153,1,0,40,153,1,0,224,152,1,0,200,152,1,0,184,152,1,0,112,152,1,0,88,152,1,0,64,152,1,0,48,152,1,0,24,152,1,0,112,151,1,0,232,150,1,0,0,0,0,0,0,0,0,0,224,148,1,0,192,148,1,0,176,148,1,0,160,148,1,0,144,148,1,0,40,148,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,232,49,1,0,112,51,1,0,64,179,2,0,21,177,0,0,148,132,0,0,32,50,1,0,128,51,1,0,56,179,2,0,148,132,0,0,21,177,0,0,40,139,3,0,0,0,0,0,64,178,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,232,195,1,0,0,0,0,0,144,195,1,0,1,0,0,0,80,195,1,0,1,0,0,0,40,195,1,0,2,0,0,0,80,248,1,0,3,0,0,0,232,194,1,0,4,0,0,0,184,194,1,0,5,0,0,0,96,194,1,0,6,0,0,0,24,194,1,0,7,0,0,0,80,193,1,0,11,0,0,0,232,192,1,0,8,0,0,0,144,192,1,0,9,0,0,0,88,192,1,0,10,0,0,0,0,192,1,0,14,0,0,0,208,191,1,0,15,0,0,0,152,191,1,0,16,0,0,0,104,191,1,0,12,0,0,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,254,255,255,255,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,152,208,2,0,0,0,0,0,144,195,1,0,1,0,0,0,80,195,1,0,1,0,0,0,48,195,2,0,2,0,0,0,24,195,2,0,3,0,0,0,224,194,2,0,4,0,0,0,216,194,2,0,4,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,192,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,63,5,0,0,0,6,0,0,0,5,0,0,0,6,0,0,0,1,0,0,0,0,0,192,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,63,15,0,0,0,10,0,0,0,15,0,0,0,10,0,0,0,0,0,0,0,0,0,192,63,10,0,0,0,15,0,0,0,10,0,0,0,15,0,0,0,0,0,0,0,0,0,192,63,20,0,0,0,10,0,0,0,5,0,0,0,10,0,0,0,0,0,0,0,0,0,192,63,10,0,0,0,6,0,0,0,10,0,0,0,6,0,0,0,0,0,0,0,0,0,192,63,15,0,0,0,6,0,0,0,5,0,0,0,10,0,0,0,0,0,0,0,0,0,192,63,10,0,0,0,6,0,0,0,10,0,0,0,10,0,0,0,0,0,0,0,0,0,192,63,5,0,0,0,6,0,0,0,5,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,4,0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,3,0,0,0,1,0,0,0,3,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,150,1,0,152,190,2,0,152,150,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,130,1,0,208,129,1,0,184,105,1,0,80,105,1,0,240,104,1,0,176,104,1,0,56,104,1,0,0,129,1,0,8,104,1,0,216,103,1,0,88,103,1,0,208,102,1,0,144,102,1,0,104,102,1,0,0,102,1,0,0,0,0,0,176,97,1,0,0,0,0,0,160,97,1,0,1,0,0,0,48,254,1,0,2,0,0,0,120,97,1,0,3,0,0,0,88,97,1,0,4,0,0,0,56,97,1,0,5,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,205,204,204,62,0,0,0,0,205,204,204,62,0,0,160,64,205,204,76,63,0,0,0,0,0,0,128,63,0,0,160,64,154,153,153,63,0,0,0,0,0,0,128,63,0,0,32,65,10,0,0,0,0,0,0,0,100,111,99,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,4,0,0,0,0,0,0,0,0,3,8,0,0,0,0,0,0,0,0,8,8,0,0,0,0,0,0,0,0,16,6,3,6,0,0,0,0,0,0,16,6,8,6,0,0,0,0,0,0,16,4,1,4,8,4,1,4,0,0,16,4,1,8,1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,9,0,0,0,0,0,0,228,12,0,0,0,0,0,0,36,9,0,0,0,0,0,0,228,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,50,1,0,0,0,0,0,232,49,1,0,1,0,0,0,0,0,0,0,2,0,0,0,246,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,246,255,255,255,0,0,0,0,246,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,246,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,181,2,0,88,181,2,0,72,181,2,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,246,255,255,255,0,0,0,0,112,249,1,0,0,0,0,0,208,250,1,0,1,0,0,0,40,250,1,0,1,0,0,0,144,175,2,0,2,0,0,0,136,175,2,0,3,0,0,0,112,175,2,0,4,0,0,0,56,86,3,0,5,0,0,0,80,175,2,0,6,0,0,0,216,174,2,0,7,0,0,0,0,0,0,0,8,0,0,0,6,0,0,0,0,0,0,0,152,190,2,0,200,181,2,0,200,181,2,0,200,181,2,0,200,181,2,0,200,181,2,0,200,181,2,0,200,181,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
+.concat([136,193,2,0,128,193,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,52,16,0,0,0,0,0,0,120,193,2,0,40,193,2,0,21,1,0,0,0,0,0,0,0,0,0,0,0,0,34,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,0,0,0,0,208,192,2,0,144,192,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,5,16,0,0,0,0,0,0,40,192,2,0,8,192,2,0,108,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,45,16,0,0,0,0,0,0,216,191,2,0,200,191,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,72,16,0,0,0,0,0,0,128,191,2,0,120,191,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,218,64,0,0,0,0,0,0,104,191,2,0,176,98,1,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,3,16,0,0,0,0,0,0,40,191,2,0,16,191,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,101,16,0,0,0,0,0,0,200,190,2,0,136,190,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,6,16,0,0,0,0,0,0,104,190,2,0,80,190,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,17,16,0,0,0,0,0,0,64,190,2,0,32,190,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,1,0,0,0,10,17,0,0,0,0,0,0,8,190,2,0,248,189,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,5,66,0,0,0,0,0,0,216,189,2,0,200,189,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,4,0,0,0,3,0,0,0,44,16,0,0,0,0,0,0,144,189,2,0,24,189,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,1,0,0,0,0,0,0,0,20,16,0,0,0,0,0,0,248,188,2,0,208,188,2,0,21,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,201,16,0,0,0,0,0,0,168,188,2,0,144,188,2,0,109,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,46,65,0,0,0,0,0,0,128,188,2,0,96,188,2,0,108,72,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,1,0,0,0,0,0,0,0,170,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,152,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,1,0,0,0,7,0,0,0,1,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,8,0,0,0,9,0,0,0,10,0,0,0,7,0,0,0,2,0,0,0,0,0,0,0,32,32,114,117,108,101,114,58,32,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,84,2,0,152,190,2,0,152,190,2,0,24,84,2,0,8,84,2,0,248,83,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,73,84,95,76,79,71,0,176,4,0,0,0,0,0,0,176,4,0,0,0,0,0,0,51,46,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,231,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,160,2,0,3,0,0,0,144,195,1,0,1,0,0,0,80,195,1,0,1,0,0,0,80,248,1,0,15,0,0,0,24,160,2,0,12,0,0,0,192,229,1,0,9,0,0,0,240,159,2,0,10,0,0,0,96,255,1,0,4,0,0,0,232,49,1,0,7,0,0,0,200,159,2,0,5,0,0,0,232,195,1,0,0,0,0,0,184,159,2,0,13,0,0,0,152,159,2,0,6,0,0,0,120,97,1,0,8,0,0,0,144,159,2,0,2,0,0,0,232,247,1,0,14,0,0,0,144,245,1,0,11,0,0,0,112,159,2,0,11,0,0,0,24,159,2,0,18,0,0,0,240,158,2,0,16,0,0,0,192,158,2,0,19,0,0,0,176,158,2,0,17,0,0,0,144,158,2,0,20,0,0,0,0,0,0,0,21,0,0,0,4,0,0,0,2,0,0,0,1,0,0,0,5,0,0,0,3,0,0,0,6,0,0,0,0,0,0,0,26,0,0,0,11,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,134,1,0,0,0,0,0,176,132,1,0,1,0,0,0,8,131,1,0,2,0,0,0,88,128,1,0,3,0,0,0,0,126,1,0,4,0,0,0,152,123,1,0,5,0,0,0,112,121,1,0,6,0,0,0,208,119,1,0,7,0,0,0,0,118,1,0,8,0,0,0,24,116,1,0,9,0,0,0,80,114,1,0,10,0,0,0,152,111,1,0,11,0,0,0,48,109,1,0,12,0,0,0,232,105,1,0,13,0,0,0,176,102,1,0,14,0,0,0,16,100,1,0,15,0,0,0,216,97,1,0,16,0,0,0,224,95,1,0,17,0,0,0,16,93,1,0,18,0,0,0,32,91,1,0,19,0,0,0,232,88,1,0,20,0,0,0,224,83,1,0,21,0,0,0,200,79,1,0,22,0,0,0,240,74,1,0,23,0,0,0,200,72,1,0,24,0,0,0,128,69,1,0,25,0,0,0,240,65,1,0,26,0,0,0,192,59,1,0,27,0,0,0,216,54,1,0,28,0,0,0,0,52,1,0,29,0,0,0,216,49,1,0,30,0,0,0,8,48,1,0,31,0,0,0,224,45,1,0,32,0,0,0,128,43,1,0,33,0,0,0,56,41,1,0,34,0,0,0,0,0,0,0,255,255,255,255,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,3,0,200,23,3,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,78,1,0,0,138,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,19,3,0,64,19,3,0,0,19,3,0,192,18,3,0,112,18,3,0,16,18,3,0,192,17,3,0,112,17,3,0,208,16,3,0,104,16,3,0,8,16,3,0,232,15,3,0,216,15,3,0,128,15,3,0,248,14,3,0,160,14,3,0,96,14,3,0,56,14,3,0,136,13,3,0,88,13,3,0,224,12,3,0,120,12,3,0,0,12,3,0,184,11,3,0,128,11,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0,0,255,0,0,0,0,255,0,255,0,255,0,0,0,128,0,128,0,0,0,0,128,128,0,0,0,0,0,128,128,128,0,0,128,64,0,128,128,0,0,128,0,128,0,192,192,192,0,0,255,255,0,255,255,0,0,0,0,0,0,5,0,0,0,8,0,0,0,5,0,0,0,8,0,0,0,5,0,0,0,8,0,0,0,5,0,0,0,8,0,0,0,4,0,0,0,2,0,0,0,4,0,0,0,2,0,0,0,4,0,0,0,2,0,0,0,4,0,0,0,2,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,2,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,2,0,0,0,4,0,0,0,9,0,0,0,4,0,0,0,2,0,0,0,4,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,130,1,0,112,130,1,0,48,130,1,0,208,129,1,0,0,129,1,0,200,128,1,0,72,128,1,0,48,128,1,0,32,128,1,0,200,127,1,0,160,127,1,0,120,127,1,0,64,127,1,0,16,127,1,0,176,126,1,0,40,126,1,0,216,125,1,0,168,125,1,0,72,128,1,0,48,128,1,0,200,127,1,0,160,127,1,0,0,0,0,0,0,0,0,0,120,125,1,0,48,125,1,0,248,124,1,0,200,124,1,0,152,124,1,0,104,124,1,0,72,128,1,0,200,127,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,119,1,0,128,119,1,0,88,119,1,0,160,119,1,0,64,119,1,0,0,0,0,0,0,0,0,0,0,0,0,0,160,119,1,0,128,119,1,0,88,119,1,0,16,119,1,0,240,118,1,0,208,118,1,0,120,118,1,0,0,0,0,0,160,119,1,0,128,119,1,0,88,119,1,0,88,119,1,0,88,119,1,0,88,119,1,0,88,119,1,0,88,119,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,10,84,65,66,76,69,10,32,32,50,10,76,84,89,80,69,10,32,55,48,10,32,32,32,32,37,100,10,48,10,76,84,89,80,69,10,32,32,50,10,67,79,78,84,73,78,85,79,85,83,10,32,55,48,10,32,32,32,32,54,52,10,32,32,51,10,83,111,108,105,100,32,108,105,110,101,10,32,55,50,10,32,32,32,32,54,53,10,32,55,51,10,32,32,32,32,32,32,48,10,32,52,48,10,48,46,48,10,32,32,48,10,76,84,89,80,69,10,32,32,50,10,68,65,83,72,69,68,10,32,55,48,10,32,32,32,32,54,52,10,32,32,51,10,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,32,95,95,10,32,55,50,10,32,32,32,32,54,53,10,32,55,51,10,32,32,32,32,32,50,10,32,52,48,10,48,46,55,53,10,32,52,57,10,48,46,53,10,32,52,57,10,45,48,46,50,53,10,32,32,48,10,76,84,89,80,69,10,32,32,50,10,72,73,68,68,69,78,10,32,55,48,10,32,32,32,32,54,52,10,32,32,51,10,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,32,95,10,32,55,50,10,32,32,32,32,54,53,10,32,55,51,10,32,32,32,32,32,50,10,32,52,48,10,48,46,51,55,53,10,32,52,57,10,48,46,50,53,10,32,52,57,10,45,48,46,49,50,53,10,32,32,48,10,76,84,89,80,69,10,32,32,50,10,67,69,78,84,69,82,10,32,55,48,10,32,32,32,32,54,52,10,32,32,51,10,95,95,95,95,32,95,32,95,95,95,95,32,95,32,95,95,95,95,32,95,32,95,95,95,95,32,95,32,95,95,95,95,32,95,32,95,95,95,95,32,95,32,95,95,95,95,10,32,55,50,10,32,32,32,32,54,53,10,32,55,51,10,32,32,32,32,32,52,10,32,52,48,10,50,46,48,10,32,52,57,10,49,46,50,53,10,32,52,57,10,45,48,46,50,53,10,32,52,57,10,48,46,50,53,10,32,52,57,10,45,48,46,50,53,10,32,32,48,10,76,84,89,80,69,10,32,32,50,10,80,72,65,78,84,79,77,10,32,55,48,10,32,32,32,32,54,52,10,32,32,51,10,95,95,95,95,95,32,95,32,95,32,95,95,95,95,95,32,95,32,95,32,95,95,95,95,95,32,95,32,95,32,95,95,95,95,95,32,95,32,95,32,95,95,95,95,10,32,55,50,10,32,32,32,32,54,53,10,32,55,51,10,32,32,32,32,32,54,10,32,52,48,10,50,46,53,10,32,52,57,10,49,46,50,53,10,32,52,57,10,45,48,46,50,53,10,32,52,57,10,48,46,50,53,10,32,52,57,10,45,48,46,50,53,10,32,52,57,10,48,46,50,53,10,32,52,57,10,45,48,46,50,53,10,32,32,48,10,76,84,89,80,69,10,32,32,50,10,68,79,84,10,32,55,48,10,32,32,32,32,54,52,10,32,32,51,10,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,10,32,55,50,10,32,32,32,32,54,53,10,32,55,51,10,32,32,32,32,32,50,10,32,52,48,10,48,46,50,53,10,32,52,57,10,48,46,48,10,32,52,57,10,45,48,46,50,53,10,32,32,48,10,76,84,89,80,69,10,32,32,50,10,68,65,83,72,68,79,84,10,32,55,48,10,32,32,32,32,54,52,10,32,32,51,10,95,95,32,46,32,95,95,32,46,32,95,95,32,46,32,95,95,32,46,32,95,95,32,46,32,95,95,32,46,32,95,95,32,46,32,95,95,32,46,32,95,95,32,46,32,95,95,10,32,55,50,10,32,32,32,32,54,53,10,32,55,51,10,32,32,32,32,32,52,10,32,52,48,10,49,46,48,10,32,52,57,10,48,46,53,10,32,52,57,10,45,48,46,50,53,10,32,52,57,10,48,46,48,10,32,52,57,10,45,48,46,50,53,10,32,32,48,10,69,78,68,84,65,66,10,0,0,0,0,0,0,0,57,57,57,10,37,37,32,71,78,85,80,76,79,84,58,32,100,120,102,32,102,105,108,101,32,102,111,114,32,65,117,116,111,67,97,100,10,32,32,48,10,83,69,67,84,73,79,78,10,32,32,50,10,72,69,65,68,69,82,10,32,32,57,10,36,69,88,84,77,73,78,10,32,49,48,10,48,46,48,48,48,10,32,50,48,10,48,46,48,48,48,10,32,32,57,10,36,69,88,84,77,65,88,10,32,49,48,10,37,45,54,46,51,102,10,32,50,48,10,37,45,54,46,51,102,10,32,32,57,10,36,76,73,77,77,73,78,10,32,49,48,10,48,46,48,48,48,10,32,50,48,10,48,46,48,48,48,10,32,32,57,10,36,76,73,77,77,65,88,10,32,49,48,10,37,45,54,46,51,102,10,32,50,48,10,37,45,54,46,51,102,10,32,32,57,10,36,84,69,88,84,83,84,89,76,69,10,32,32,55,10,37,115,10,32,32,57,10,36,84,69,88,84,83,73,90,69,10,32,52,48,10,37,45,54,46,51,102,10,32,32,57,10,36,80,76,73,78,69,87,73,68,10,32,52,48,10,37,45,54,46,52,102,10,32,32,57,10,36,76,84,83,67,65,76,69,10,32,32,52,48,10,37,45,54,46,51,102,10,32,32,57,10,36,67,79,79,82,68,83,10,32,55,48,10,32,32,49,10,32,32,57,10,36,67,69,76,84,89,80,69,10,32,54,10,66,89,76,65,89,69,82,10,32,32,57,10,36,67,76,65,89,69,82,10,32,32,56,10,48,10,32,32,57,10,36,67,69,67,79,76,79,82,10,32,54,50,10,32,32,32,37,115,10,32,32,57,10,36,77,69,78,85,10,32,32,49,10,97,99,97,100,10,32,32,48,10,69,78,68,83,69,67,10,32,32,48,10,83,69,67,84,73,79,78,10,32,32,50,10,84,65,66,76,69,83,10,0,0,0,0,0,208,149,2,0,0,0,0,0,200,149,2,0,1,0,0,0,0,224,1,0,2,0,0,0,184,149,2,0,3,0,0,0,192,253,1,0,4,0,0,0,0,0,0,0,5,0,0,0,42,35,36,37,64,38,61,0,0,0,0,0,0,0,0,0,200,172,3,0,0,0,0,0,255,255,255,255,255,255,15,0,255,0,255,0,255,15,240,0,255,127,240,0,7,7,7,7,255,7,255,7,255,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,208,2,0,0,0,0,0,112,249,1,0,1,0,0,0,248,142,2,0,2,0,0,0,224,142,2,0,2,0,0,0,0,0,0,0,3,0,0,0,49,46,48,0,0,0,0,0,50,48,49,49,45,49,49,45,48,53,0,0,0,0,0,0,99,101,110,116,101,114,0,0,0,0,108,101,102,116,0,0,0,0,0,0,114,105,103,104,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,64,0,0,0,0,0,0,8,64,1,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,20,64,0,0,0,0,0,0,8,64,1,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,48,254,1,0,0,0,0,0,192,253,1,0,1,0,0,0,88,253,1,0,2,0,0,0,24,253,1,0,3,0,0,0,224,252,1,0,4,0,0,0,144,252,1,0,5,0,0,0,0,252,1,0,6,0,0,0,200,251,1,0,7,0,0,0,72,251,1,0,8,0,0,0,208,250,1,0,9,0,0,0,40,250,1,0,9,0,0,0,112,249,1,0,10,0,0,0,80,248,1,0,11,0,0,0,232,247,1,0,12,0,0,0,128,247,1,0,13,0,0,0,48,26,2,0,14,0,0,0,8,247,1,0,15,0,0,0,16,24,2,0,16,0,0,0,96,3,2,0,17,0,0,0,152,246,1,0,18,0,0,0,48,246,1,0,19,0,0,0,224,245,1,0,19,0,0,0,144,245,1,0,20,0,0,0,40,245,1,0,20,0,0,0,96,244,1,0,21,0,0,0,216,243,1,0,21,0,0,0,128,243,1,0,22,0,0,0,248,242,1,0,23,0,0,0,104,242,1,0,22,0,0,0,8,242,1,0,22,0,0,0,184,241,1,0,23,0,0,0,240,240,1,0,24,0,0,0,120,240,1,0,25,0,0,0,192,229,1,0,27,0,0,0,232,239,1,0,26,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,253,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,50,1,0,0,0,0,0,24,135,2,0,1,0,0,0,224,155,2,0,2,0,0,0,0,135,2,0,11,0,0,0,232,134,2,0,11,0,0,0,152,208,2,0,3,0,0,0,144,195,1,0,4,0,0,0,80,195,1,0,4,0,0,0,56,117,1,0,5,0,0,0,200,134,2,0,6,0,0,0,80,248,1,0,7,0,0,0,40,195,1,0,8,0,0,0,184,134,2,0,9,0,0,0,40,245,1,0,9,0,0,0,176,134,2,0,10,0,0,0,136,82,3,0,12,0,0,0,0,0,0,0,13,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,16,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,5,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,5,0,0,0,1,0,0,0,6,0,0,0,1,0,0,0,7,0,0,0,1,0,0,0,8,0,0,0,1,0,0,0,9,0,0,0,1,0,0,0,10,0,0,0,1,0,0,0,13,0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,3,0,0,0,2,0,0,0,4,0,0,0,2,0,0,0,5,0,0,0,2,0,0,0,6,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,3,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,7,0,0,0,4,0,0,0,11,0,0,0,4,0,0,0,12,0,0,0,4,0,0,0,15,0,0,0,4,0,0,0,16,0,0,0,4,0,0,0,17,0,0,0,4,0,0,0,18,0,0,0,4,0,0,0,19,0,0,0,5,0,0,0,2,0,0,0,5,0,0,0,3,0,0,0,5,0,0,0,4,0,0,0,5,0,0,0,6,0,0,0,5,0,0,0,7,0,0,0,5,0,0,0,8,0,0,0,5,0,0,0,10,0,0,0,5,0,0,0,14,0,0,0,5,0,0,0,15,0,0,0,5,0,0,0,16,0,0,0,5,0,0,0,18,0,0,0,5,0,0,0,22,0,0,0,5,0,0,0,23,0,0,0,5,0,0,0,24,0,0,0,5,0,0,0,27,0,0,0,5,0,0,0,28,0,0,0,5,0,0,0,29,0,0,0,5,0,0,0,30,0,0,0,5,0,0,0,34,0,0,0,6,0,0,0,1,0,0,0,7,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,5,0,0,0,0,0,0,0,4,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,8,0,0,0,5,0,0,0,8,0,0,0,5,0,0,0,8,0,0,0,5,0,0,0,8,0,0,0,5,0,0,0,3,0,0,0,5,0,0,0,3,0,0,0,5,0,0,0,3,0,0,0,5,0,0,0,3,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,9,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,10,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,10,0,0,0,4,0,0,0,10,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,10,0,0,0,4,0,0,0,10,0,0,0,4,0,0,0,1,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,192,229,1,0,1,0,0,0,128,91,2,0,2,0,0,0,64,85,3,0,3,0,0,0,192,253,1,0,0,0,0,0,152,10,2,0,4,0,0,0,136,85,3,0,7,0,0,0,168,85,3,0,7,0,0,0,120,91,2,0,8,0,0,0,0,224,1,0,9,0,0,0,192,218,1,0,10,0,0,0,40,245,1,0,6,0,0,0,144,245,1,0,6,0,0,0,72,91,2,0,5,0,0,0,96,244,1,0,11,0,0,0,176,1,2,0,12,0,0,0,152,82,3,0,13,0,0,0,48,246,1,0,14,0,0,0,224,245,1,0,14,0,0,0,176,82,3,0,15,0,0,0,16,24,2,0,16,0,0,0,136,82,3,0,17,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,128,2,0,8,128,2,0,224,127,2,0,184,127,2,0,40,127,2,0,216,126,2,0,144,126,2,0,120,126,2,0,80,126,2,0,208,125,2,0,168,125,2,0,112,125,2,0,88,125,2,0,16,125,2,0,248,124,2,0,224,124,2,0,192,124,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
+, "i8", ALLOC_NONE, Runtime.GLOBAL_BASE)
+function runPostSets() {
+}
+if (!awaitingMemoryInitializer) runPostSets();
+var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);
+assert(tempDoublePtr % 8 == 0);
+function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much
+ HEAP8[tempDoublePtr] = HEAP8[ptr];
+ HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];
+ HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];
+ HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];
+}
+function copyTempDouble(ptr) {
+ HEAP8[tempDoublePtr] = HEAP8[ptr];
+ HEAP8[tempDoublePtr+1] = HEAP8[ptr+1];
+ HEAP8[tempDoublePtr+2] = HEAP8[ptr+2];
+ HEAP8[tempDoublePtr+3] = HEAP8[ptr+3];
+ HEAP8[tempDoublePtr+4] = HEAP8[ptr+4];
+ HEAP8[tempDoublePtr+5] = HEAP8[ptr+5];
+ HEAP8[tempDoublePtr+6] = HEAP8[ptr+6];
+ HEAP8[tempDoublePtr+7] = HEAP8[ptr+7];
+}
+ var _log=Math.log;
+ var _fabs=Math.abs;
+ var ERRNO_CODES={EPERM:1,ENOENT:2,ESRCH:3,EINTR:4,EIO:5,ENXIO:6,E2BIG:7,ENOEXEC:8,EBADF:9,ECHILD:10,EAGAIN:11,EWOULDBLOCK:11,ENOMEM:12,EACCES:13,EFAULT:14,ENOTBLK:15,EBUSY:16,EEXIST:17,EXDEV:18,ENODEV:19,ENOTDIR:20,EISDIR:21,EINVAL:22,ENFILE:23,EMFILE:24,ENOTTY:25,ETXTBSY:26,EFBIG:27,ENOSPC:28,ESPIPE:29,EROFS:30,EMLINK:31,EPIPE:32,EDOM:33,ERANGE:34,ENOMSG:35,EIDRM:36,ECHRNG:37,EL2NSYNC:38,EL3HLT:39,EL3RST:40,ELNRNG:41,EUNATCH:42,ENOCSI:43,EL2HLT:44,EDEADLK:45,ENOLCK:46,EBADE:50,EBADR:51,EXFULL:52,ENOANO:53,EBADRQC:54,EBADSLT:55,EDEADLOCK:56,EBFONT:57,ENOSTR:60,ENODATA:61,ETIME:62,ENOSR:63,ENONET:64,ENOPKG:65,EREMOTE:66,ENOLINK:67,EADV:68,ESRMNT:69,ECOMM:70,EPROTO:71,EMULTIHOP:74,ELBIN:75,EDOTDOT:76,EBADMSG:77,EFTYPE:79,ENOTUNIQ:80,EBADFD:81,EREMCHG:82,ELIBACC:83,ELIBBAD:84,ELIBSCN:85,ELIBMAX:86,ELIBEXEC:87,ENOSYS:88,ENMFILE:89,ENOTEMPTY:90,ENAMETOOLONG:91,ELOOP:92,EOPNOTSUPP:95,EPFNOSUPPORT:96,ECONNRESET:104,ENOBUFS:105,EAFNOSUPPORT:106,EPROTOTYPE:107,ENOTSOCK:108,ENOPROTOOPT:109,ESHUTDOWN:110,ECONNREFUSED:111,EADDRINUSE:112,ECONNABORTED:113,ENETUNREACH:114,ENETDOWN:115,ETIMEDOUT:116,EHOSTDOWN:117,EHOSTUNREACH:118,EINPROGRESS:119,EALREADY:120,EDESTADDRREQ:121,EMSGSIZE:122,EPROTONOSUPPORT:123,ESOCKTNOSUPPORT:124,EADDRNOTAVAIL:125,ENETRESET:126,EISCONN:127,ENOTCONN:128,ETOOMANYREFS:129,EPROCLIM:130,EUSERS:131,EDQUOT:132,ESTALE:133,ENOTSUP:134,ENOMEDIUM:135,ENOSHARE:136,ECASECLASH:137,EILSEQ:138,EOVERFLOW:139,ECANCELED:140,ENOTRECOVERABLE:141,EOWNERDEAD:142,ESTRPIPE:143};
+ var ___errno_state=0;function ___setErrNo(value) {
+ // For convenient setting and returning of errno.
+ HEAP32[((___errno_state)>>2)]=value
+ return value;
+ }
+ var _stdin=allocate(1, "i32*", ALLOC_STATIC);
+ var _stdout=allocate(1, "i32*", ALLOC_STATIC);
+ var _stderr=allocate(1, "i32*", ALLOC_STATIC);
+ var __impure_ptr=allocate(1, "i32*", ALLOC_STATIC);var FS={currentPath:"/",nextInode:2,streams:[null],ignorePermissions:true,createFileHandle:function (stream, fd) {
+ if (typeof stream === 'undefined') {
+ stream = null;
+ }
+ if (!fd) {
+ if (stream && stream.socket) {
+ for (var i = 1; i < 64; i++) {
+ if (!FS.streams[i]) {
+ fd = i;
+ break;
+ }
+ }
+ assert(fd, 'ran out of low fds for sockets');
+ } else {
+ fd = Math.max(FS.streams.length, 64);
+ for (var i = FS.streams.length; i < fd; i++) {
+ FS.streams[i] = null; // Keep dense
+ }
+ }
+ }
+ // Close WebSocket first if we are about to replace the fd (i.e. dup2)
+ if (FS.streams[fd] && FS.streams[fd].socket && FS.streams[fd].socket.close) {
+ FS.streams[fd].socket.close();
+ }
+ FS.streams[fd] = stream;
+ return fd;
+ },removeFileHandle:function (fd) {
+ FS.streams[fd] = null;
+ },joinPath:function (parts, forceRelative) {
+ var ret = parts[0];
+ for (var i = 1; i < parts.length; i++) {
+ if (ret[ret.length-1] != '/') ret += '/';
+ ret += parts[i];
+ }
+ if (forceRelative && ret[0] == '/') ret = ret.substr(1);
+ return ret;
+ },absolutePath:function (relative, base) {
+ if (typeof relative !== 'string') return null;
+ if (base === undefined) base = FS.currentPath;
+ if (relative && relative[0] == '/') base = '';
+ var full = base + '/' + relative;
+ var parts = full.split('/').reverse();
+ var absolute = [''];
+ while (parts.length) {
+ var part = parts.pop();
+ if (part == '' || part == '.') {
+ // Nothing.
+ } else if (part == '..') {
+ if (absolute.length > 1) absolute.pop();
+ } else {
+ absolute.push(part);
+ }
+ }
+ return absolute.length == 1 ? '/' : absolute.join('/');
+ },analyzePath:function (path, dontResolveLastLink, linksVisited) {
+ var ret = {
+ isRoot: false,
+ exists: false,
+ error: 0,
+ name: null,
+ path: null,
+ object: null,
+ parentExists: false,
+ parentPath: null,
+ parentObject: null
+ };
+ path = FS.absolutePath(path);
+ if (path == '/') {
+ ret.isRoot = true;
+ ret.exists = ret.parentExists = true;
+ ret.name = '/';
+ ret.path = ret.parentPath = '/';
+ ret.object = ret.parentObject = FS.root;
+ } else if (path !== null) {
+ linksVisited = linksVisited || 0;
+ path = path.slice(1).split('/');
+ var current = FS.root;
+ var traversed = [''];
+ while (path.length) {
+ if (path.length == 1 && current.isFolder) {
+ ret.parentExists = true;
+ ret.parentPath = traversed.length == 1 ? '/' : traversed.join('/');
+ ret.parentObject = current;
+ ret.name = path[0];
+ }
+ var target = path.shift();
+ if (!current.isFolder) {
+ ret.error = ERRNO_CODES.ENOTDIR;
+ break;
+ } else if (!current.read) {
+ ret.error = ERRNO_CODES.EACCES;
+ break;
+ } else if (!current.contents.hasOwnProperty(target)) {
+ ret.error = ERRNO_CODES.ENOENT;
+ break;
+ }
+ current = current.contents[target];
+ if (current.link && !(dontResolveLastLink && path.length == 0)) {
+ if (linksVisited > 40) { // Usual Linux SYMLOOP_MAX.
+ ret.error = ERRNO_CODES.ELOOP;
+ break;
+ }
+ var link = FS.absolutePath(current.link, traversed.join('/'));
+ ret = FS.analyzePath([link].concat(path).join('/'),
+ dontResolveLastLink, linksVisited + 1);
+ return ret;
+ }
+ traversed.push(target);
+ if (path.length == 0) {
+ ret.exists = true;
+ ret.path = traversed.join('/');
+ ret.object = current;
+ }
+ }
+ }
+ return ret;
+ },findObject:function (path, dontResolveLastLink) {
+ FS.ensureRoot();
+ var ret = FS.analyzePath(path, dontResolveLastLink);
+ if (ret.exists) {
+ return ret.object;
+ } else {
+ ___setErrNo(ret.error);
+ return null;
+ }
+ },createObject:function (parent, name, properties, canRead, canWrite) {
+ if (!parent) parent = '/';
+ if (typeof parent === 'string') parent = FS.findObject(parent);
+ if (!parent) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ throw new Error('Parent path must exist.');
+ }
+ if (!parent.isFolder) {
+ ___setErrNo(ERRNO_CODES.ENOTDIR);
+ throw new Error('Parent must be a folder.');
+ }
+ if (!parent.write && !FS.ignorePermissions) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ throw new Error('Parent folder must be writeable.');
+ }
+ if (!name || name == '.' || name == '..') {
+ ___setErrNo(ERRNO_CODES.ENOENT);
+ throw new Error('Name must not be empty.');
+ }
+ if (parent.contents.hasOwnProperty(name)) {
+ ___setErrNo(ERRNO_CODES.EEXIST);
+ throw new Error("Can't overwrite object.");
+ }
+ parent.contents[name] = {
+ read: canRead === undefined ? true : canRead,
+ write: canWrite === undefined ? false : canWrite,
+ timestamp: Date.now(),
+ inodeNumber: FS.nextInode++
+ };
+ for (var key in properties) {
+ if (properties.hasOwnProperty(key)) {
+ parent.contents[name][key] = properties[key];
+ }
+ }
+ return parent.contents[name];
+ },createFolder:function (parent, name, canRead, canWrite) {
+ var properties = {isFolder: true, isDevice: false, contents: {}};
+ return FS.createObject(parent, name, properties, canRead, canWrite);
+ },createPath:function (parent, path, canRead, canWrite) {
+ var current = FS.findObject(parent);
+ if (current === null) throw new Error('Invalid parent.');
+ path = path.split('/').reverse();
+ while (path.length) {
+ var part = path.pop();
+ if (!part) continue;
+ if (!current.contents.hasOwnProperty(part)) {
+ FS.createFolder(current, part, canRead, canWrite);
+ }
+ current = current.contents[part];
+ }
+ return current;
+ },createFile:function (parent, name, properties, canRead, canWrite) {
+ properties.isFolder = false;
+ return FS.createObject(parent, name, properties, canRead, canWrite);
+ },createDataFile:function (parent, name, data, canRead, canWrite) {
+ if (typeof data === 'string') {
+ var dataArray = new Array(data.length);
+ for (var i = 0, len = data.length; i < len; ++i) dataArray[i] = data.charCodeAt(i);
+ data = dataArray;
+ }
+ var properties = {
+ isDevice: false,
+ contents: data.subarray ? data.subarray(0) : data // as an optimization, create a new array wrapper (not buffer) here, to help JS engines understand this object
+ };
+ return FS.createFile(parent, name, properties, canRead, canWrite);
+ },createLazyFile:function (parent, name, url, canRead, canWrite) {
+ if (typeof XMLHttpRequest !== 'undefined') {
+ if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
+ // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
+ var LazyUint8Array = function() {
+ this.lengthKnown = false;
+ this.chunks = []; // Loaded chunks. Index is the chunk number
+ }
+ LazyUint8Array.prototype.get = function(idx) {
+ if (idx > this.length-1 || idx < 0) {
+ return undefined;
+ }
+ var chunkOffset = idx % this.chunkSize;
+ var chunkNum = Math.floor(idx / this.chunkSize);
+ return this.getter(chunkNum)[chunkOffset];
+ }
+ LazyUint8Array.prototype.setDataGetter = function(getter) {
+ this.getter = getter;
+ }
+ LazyUint8Array.prototype.cacheLength = function() {
+ // Find length
+ var xhr = new XMLHttpRequest();
+ xhr.open('HEAD', url, false);
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ var datalength = Number(xhr.getResponseHeader("Content-length"));
+ var header;
+ var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
+ var chunkSize = 1024*1024; // Chunk size in bytes
+ if (!hasByteServing) chunkSize = datalength;
+ // Function to get a range from the remote URL.
+ var doXHR = (function(from, to) {
+ if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
+ if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
+ // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
+ // Some hints to the browser that we want binary data.
+ if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ if (xhr.response !== undefined) {
+ return new Uint8Array(xhr.response || []);
+ } else {
+ return intArrayFromString(xhr.responseText || '', true);
+ }
+ });
+ var lazyArray = this;
+ lazyArray.setDataGetter(function(chunkNum) {
+ var start = chunkNum * chunkSize;
+ var end = (chunkNum+1) * chunkSize - 1; // including this byte
+ end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
+ lazyArray.chunks[chunkNum] = doXHR(start, end);
+ }
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
+ return lazyArray.chunks[chunkNum];
+ });
+ this._length = datalength;
+ this._chunkSize = chunkSize;
+ this.lengthKnown = true;
+ }
+ var lazyArray = new LazyUint8Array();
+ Object.defineProperty(lazyArray, "length", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._length;
+ }
+ });
+ Object.defineProperty(lazyArray, "chunkSize", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._chunkSize;
+ }
+ });
+ var properties = { isDevice: false, contents: lazyArray };
+ } else {
+ var properties = { isDevice: false, url: url };
+ }
+ return FS.createFile(parent, name, properties, canRead, canWrite);
+ },createPreloadedFile:function (parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile) {
+ Browser.init();
+ var fullname = FS.joinPath([parent, name], true);
+ function processData(byteArray) {
+ function finish(byteArray) {
+ if (!dontCreateFile) {
+ FS.createDataFile(parent, name, byteArray, canRead, canWrite);
+ }
+ if (onload) onload();
+ removeRunDependency('cp ' + fullname);
+ }
+ var handled = false;
+ Module['preloadPlugins'].forEach(function(plugin) {
+ if (handled) return;
+ if (plugin['canHandle'](fullname)) {
+ plugin['handle'](byteArray, fullname, finish, function() {
+ if (onerror) onerror();
+ removeRunDependency('cp ' + fullname);
+ });
+ handled = true;
+ }
+ });
+ if (!handled) finish(byteArray);
+ }
+ addRunDependency('cp ' + fullname);
+ if (typeof url == 'string') {
+ Browser.asyncLoad(url, function(byteArray) {
+ processData(byteArray);
+ }, onerror);
+ } else {
+ processData(url);
+ }
+ },createLink:function (parent, name, target, canRead, canWrite) {
+ var properties = {isDevice: false, link: target};
+ return FS.createFile(parent, name, properties, canRead, canWrite);
+ },createDevice:function (parent, name, input, output) {
+ if (!(input || output)) {
+ throw new Error('A device must have at least one callback defined.');
+ }
+ var ops = {isDevice: true, input: input, output: output};
+ return FS.createFile(parent, name, ops, Boolean(input), Boolean(output));
+ },forceLoadFile:function (obj) {
+ if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
+ var success = true;
+ if (typeof XMLHttpRequest !== 'undefined') {
+ throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
+ } else if (Module['read']) {
+ // Command-line.
+ try {
+ // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
+ // read() will try to parse UTF8.
+ obj.contents = intArrayFromString(Module['read'](obj.url), true);
+ } catch (e) {
+ success = false;
+ }
+ } else {
+ throw new Error('Cannot load without read() or XMLHttpRequest.');
+ }
+ if (!success) ___setErrNo(ERRNO_CODES.EIO);
+ return success;
+ },ensureRoot:function () {
+ if (FS.root) return;
+ // The main file system tree. All the contents are inside this.
+ FS.root = {
+ read: true,
+ write: true,
+ isFolder: true,
+ isDevice: false,
+ timestamp: Date.now(),
+ inodeNumber: 1,
+ contents: {}
+ };
+ },init:function (input, output, error) {
+ // Make sure we initialize only once.
+ assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');
+ FS.init.initialized = true;
+ FS.ensureRoot();
+ // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here
+ input = input || Module['stdin'];
+ output = output || Module['stdout'];
+ error = error || Module['stderr'];
+ // Default handlers.
+ var stdinOverridden = true, stdoutOverridden = true, stderrOverridden = true;
+ if (!input) {
+ stdinOverridden = false;
+ input = function() {
+ if (!input.cache || !input.cache.length) {
+ var result;
+ if (typeof window != 'undefined' &&
+ typeof window.prompt == 'function') {
+ // Browser.
+ result = window.prompt('Input: ');
+ if (result === null) result = String.fromCharCode(0); // cancel ==> EOF
+ } else if (typeof readline == 'function') {
+ // Command line.
+ result = readline();
+ }
+ if (!result) result = '';
+ input.cache = intArrayFromString(result + '\n', true);
+ }
+ return input.cache.shift();
+ };
+ }
+ var utf8 = new Runtime.UTF8Processor();
+ function simpleOutput(val) {
+ if (val === null || val === 10) {
+ output.printer(output.buffer.join(''));
+ output.buffer = [];
+ } else {
+ output.buffer.push(utf8.processCChar(val));
+ }
+ }
+ if (!output) {
+ stdoutOverridden = false;
+ output = simpleOutput;
+ }
+ if (!output.printer) output.printer = Module['print'];
+ if (!output.buffer) output.buffer = [];
+ if (!error) {
+ stderrOverridden = false;
+ error = simpleOutput;
+ }
+ if (!error.printer) error.printer = Module['print'];
+ if (!error.buffer) error.buffer = [];
+ // Create the temporary folder, if not already created
+ try {
+ FS.createFolder('/', 'tmp', true, true);
+ } catch(e) {}
+ // Create the I/O devices.
+ var devFolder = FS.createFolder('/', 'dev', true, true);
+ var stdin = FS.createDevice(devFolder, 'stdin', input);
+ var stdout = FS.createDevice(devFolder, 'stdout', null, output);
+ var stderr = FS.createDevice(devFolder, 'stderr', null, error);
+ FS.createDevice(devFolder, 'tty', input, output);
+ FS.createDevice(devFolder, 'null', function(){}, function(){});
+ // Create default streams.
+ FS.streams[1] = {
+ path: '/dev/stdin',
+ object: stdin,
+ position: 0,
+ isRead: true,
+ isWrite: false,
+ isAppend: false,
+ isTerminal: !stdinOverridden,
+ error: false,
+ eof: false,
+ ungotten: []
+ };
+ FS.streams[2] = {
+ path: '/dev/stdout',
+ object: stdout,
+ position: 0,
+ isRead: false,
+ isWrite: true,
+ isAppend: false,
+ isTerminal: !stdoutOverridden,
+ error: false,
+ eof: false,
+ ungotten: []
+ };
+ FS.streams[3] = {
+ path: '/dev/stderr',
+ object: stderr,
+ position: 0,
+ isRead: false,
+ isWrite: true,
+ isAppend: false,
+ isTerminal: !stderrOverridden,
+ error: false,
+ eof: false,
+ ungotten: []
+ };
+ // TODO: put these low in memory like we used to assert on: assert(Math.max(_stdin, _stdout, _stderr) < 15000); // make sure these are low, we flatten arrays with these
+ HEAP32[((_stdin)>>2)]=1;
+ HEAP32[((_stdout)>>2)]=2;
+ HEAP32[((_stderr)>>2)]=3;
+ // Other system paths
+ FS.createPath('/', 'dev/shm/tmp', true, true); // temp files
+ // Newlib initialization
+ for (var i = FS.streams.length; i < Math.max(_stdin, _stdout, _stderr) + 4; i++) {
+ FS.streams[i] = null; // Make sure to keep FS.streams dense
+ }
+ FS.streams[_stdin] = FS.streams[1];
+ FS.streams[_stdout] = FS.streams[2];
+ FS.streams[_stderr] = FS.streams[3];
+ allocate([ allocate(
+ [0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0],
+ 'void*', ALLOC_NORMAL) ], 'void*', ALLOC_NONE, __impure_ptr);
+ },quit:function () {
+ if (!FS.init.initialized) return;
+ // Flush any partially-printed lines in stdout and stderr. Careful, they may have been closed
+ if (FS.streams[2] && FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output(10);
+ if (FS.streams[3] && FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output(10);
+ },standardizePath:function (path) {
+ if (path.substr(0, 2) == './') path = path.substr(2);
+ return path;
+ },deleteFile:function (path) {
+ path = FS.analyzePath(path);
+ if (!path.parentExists || !path.exists) {
+ throw 'Invalid path ' + path;
+ }
+ delete path.parentObject.contents[path.name];
+ }};
+ function _send(fd, buf, len, flags) {
+ var info = FS.streams[fd];
+ if (!info) return -1;
+ info.sender(HEAPU8.subarray(buf, buf+len));
+ return len;
+ }
+ function _pwrite(fildes, buf, nbyte, offset) {
+ // ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html
+ var stream = FS.streams[fildes];
+ if (!stream || stream.object.isDevice) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ } else if (!stream.isWrite) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ return -1;
+ } else if (stream.object.isFolder) {
+ ___setErrNo(ERRNO_CODES.EISDIR);
+ return -1;
+ } else if (nbyte < 0 || offset < 0) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
+ } else {
+ var contents = stream.object.contents;
+ while (contents.length < offset) contents.push(0);
+ for (var i = 0; i < nbyte; i++) {
+ contents[offset + i] = HEAPU8[(((buf)+(i))|0)];
+ }
+ stream.object.timestamp = Date.now();
+ return i;
+ }
+ }function _write(fildes, buf, nbyte) {
+ // ssize_t write(int fildes, const void *buf, size_t nbyte);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html
+ var stream = FS.streams[fildes];
+ if (stream && ('socket' in stream)) {
+ return _send(fildes, buf, nbyte, 0);
+ } else if (!stream) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ } else if (!stream.isWrite) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ return -1;
+ } else if (nbyte < 0) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
+ } else {
+ if (stream.object.isDevice) {
+ if (stream.object.output) {
+ for (var i = 0; i < nbyte; i++) {
+ try {
+ stream.object.output(HEAP8[(((buf)+(i))|0)]);
+ } catch (e) {
+ ___setErrNo(ERRNO_CODES.EIO);
+ return -1;
+ }
+ }
+ stream.object.timestamp = Date.now();
+ return i;
+ } else {
+ ___setErrNo(ERRNO_CODES.ENXIO);
+ return -1;
+ }
+ } else {
+ var bytesWritten = _pwrite(fildes, buf, nbyte, stream.position);
+ if (bytesWritten != -1) stream.position += bytesWritten;
+ return bytesWritten;
+ }
+ }
+ }function _fwrite(ptr, size, nitems, stream) {
+ // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fwrite.html
+ var bytesToWrite = nitems * size;
+ if (bytesToWrite == 0) return 0;
+ var bytesWritten = _write(stream, ptr, bytesToWrite);
+ if (bytesWritten == -1) {
+ if (FS.streams[stream]) FS.streams[stream].error = true;
+ return 0;
+ } else {
+ return Math.floor(bytesWritten / size);
+ }
+ }
+ Module["_strlen"] = _strlen;
+ function __reallyNegative(x) {
+ return x < 0 || (x === 0 && (1/x) === -Infinity);
+ }function __formatString(format, varargs) {
+ var textIndex = format;
+ var argIndex = 0;
+ function getNextArg(type) {
+ // NOTE: Explicitly ignoring type safety. Otherwise this fails:
+ // int x = 4; printf("%c\n", (char)x);
+ var ret;
+ if (type === 'double') {
+ ret = HEAPF64[(((varargs)+(argIndex))>>3)];
+ } else if (type == 'i64') {
+ ret = [HEAP32[(((varargs)+(argIndex))>>2)],
+ HEAP32[(((varargs)+(argIndex+8))>>2)]];
+ argIndex += 8; // each 32-bit chunk is in a 64-bit block
+ } else {
+ type = 'i32'; // varargs are always i32, i64, or double
+ ret = HEAP32[(((varargs)+(argIndex))>>2)];
+ }
+ argIndex += Math.max(Runtime.getNativeFieldSize(type), Runtime.getAlignSize(type, null, true));
+ return ret;
+ }
+ var ret = [];
+ var curr, next, currArg;
+ while(1) {
+ var startTextIndex = textIndex;
+ curr = HEAP8[(textIndex)];
+ if (curr === 0) break;
+ next = HEAP8[((textIndex+1)|0)];
+ if (curr == 37) {
+ // Handle flags.
+ var flagAlwaysSigned = false;
+ var flagLeftAlign = false;
+ var flagAlternative = false;
+ var flagZeroPad = false;
+ flagsLoop: while (1) {
+ switch (next) {
+ case 43:
+ flagAlwaysSigned = true;
+ break;
+ case 45:
+ flagLeftAlign = true;
+ break;
+ case 35:
+ flagAlternative = true;
+ break;
+ case 48:
+ if (flagZeroPad) {
+ break flagsLoop;
+ } else {
+ flagZeroPad = true;
+ break;
+ }
+ default:
+ break flagsLoop;
+ }
+ textIndex++;
+ next = HEAP8[((textIndex+1)|0)];
+ }
+ // Handle width.
+ var width = 0;
+ if (next == 42) {
+ width = getNextArg('i32');
+ textIndex++;
+ next = HEAP8[((textIndex+1)|0)];
+ } else {
+ while (next >= 48 && next <= 57) {
+ width = width * 10 + (next - 48);
+ textIndex++;
+ next = HEAP8[((textIndex+1)|0)];
+ }
+ }
+ // Handle precision.
+ var precisionSet = false;
+ if (next == 46) {
+ var precision = 0;
+ precisionSet = true;
+ textIndex++;
+ next = HEAP8[((textIndex+1)|0)];
+ if (next == 42) {
+ precision = getNextArg('i32');
+ textIndex++;
+ } else {
+ while(1) {
+ var precisionChr = HEAP8[((textIndex+1)|0)];
+ if (precisionChr < 48 ||
+ precisionChr > 57) break;
+ precision = precision * 10 + (precisionChr - 48);
+ textIndex++;
+ }
+ }
+ next = HEAP8[((textIndex+1)|0)];
+ } else {
+ var precision = 6; // Standard default.
+ }
+ // Handle integer sizes. WARNING: These assume a 32-bit architecture!
+ var argSize;
+ switch (String.fromCharCode(next)) {
+ case 'h':
+ var nextNext = HEAP8[((textIndex+2)|0)];
+ if (nextNext == 104) {
+ textIndex++;
+ argSize = 1; // char (actually i32 in varargs)
+ } else {
+ argSize = 2; // short (actually i32 in varargs)
+ }
+ break;
+ case 'l':
+ var nextNext = HEAP8[((textIndex+2)|0)];
+ if (nextNext == 108) {
+ textIndex++;
+ argSize = 8; // long long
+ } else {
+ argSize = 4; // long
+ }
+ break;
+ case 'L': // long long
+ case 'q': // int64_t
+ case 'j': // intmax_t
+ argSize = 8;
+ break;
+ case 'z': // size_t
+ case 't': // ptrdiff_t
+ case 'I': // signed ptrdiff_t or unsigned size_t
+ argSize = 4;
+ break;
+ default:
+ argSize = null;
+ }
+ if (argSize) textIndex++;
+ next = HEAP8[((textIndex+1)|0)];
+ // Handle type specifier.
+ switch (String.fromCharCode(next)) {
+ case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': case 'p': {
+ // Integer.
+ var signed = next == 100 || next == 105;
+ argSize = argSize || 4;
+ var currArg = getNextArg('i' + (argSize * 8));
+ var origArg = currArg;
+ var argText;
+ // Flatten i64-1 [low, high] into a (slightly rounded) double
+ if (argSize == 8) {
+ currArg = Runtime.makeBigInt(currArg[0], currArg[1], next == 117);
+ }
+ // Truncate to requested size.
+ if (argSize <= 4) {
+ var limit = Math.pow(256, argSize) - 1;
+ currArg = (signed ? reSign : unSign)(currArg & limit, argSize * 8);
+ }
+ // Format the number.
+ var currAbsArg = Math.abs(currArg);
+ var prefix = '';
+ if (next == 100 || next == 105) {
+ if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1], null); else
+ argText = reSign(currArg, 8 * argSize, 1).toString(10);
+ } else if (next == 117) {
+ if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1], true); else
+ argText = unSign(currArg, 8 * argSize, 1).toString(10);
+ currArg = Math.abs(currArg);
+ } else if (next == 111) {
+ argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8);
+ } else if (next == 120 || next == 88) {
+ prefix = (flagAlternative && currArg != 0) ? '0x' : '';
+ if (argSize == 8 && i64Math) {
+ if (origArg[1]) {
+ argText = (origArg[1]>>>0).toString(16);
+ var lower = (origArg[0]>>>0).toString(16);
+ while (lower.length < 8) lower = '0' + lower;
+ argText += lower;
+ } else {
+ argText = (origArg[0]>>>0).toString(16);
+ }
+ } else
+ if (currArg < 0) {
+ // Represent negative numbers in hex as 2's complement.
+ currArg = -currArg;
+ argText = (currAbsArg - 1).toString(16);
+ var buffer = [];
+ for (var i = 0; i < argText.length; i++) {
+ buffer.push((0xF - parseInt(argText[i], 16)).toString(16));
+ }
+ argText = buffer.join('');
+ while (argText.length < argSize * 2) argText = 'f' + argText;
+ } else {
+ argText = currAbsArg.toString(16);
+ }
+ if (next == 88) {
+ prefix = prefix.toUpperCase();
+ argText = argText.toUpperCase();
+ }
+ } else if (next == 112) {
+ if (currAbsArg === 0) {
+ argText = '(nil)';
+ } else {
+ prefix = '0x';
+ argText = currAbsArg.toString(16);
+ }
+ }
+ if (precisionSet) {
+ while (argText.length < precision) {
+ argText = '0' + argText;
+ }
+ }
+ // Add sign if needed
+ if (flagAlwaysSigned) {
+ if (currArg < 0) {
+ prefix = '-' + prefix;
+ } else {
+ prefix = '+' + prefix;
+ }
+ }
+ // Add padding.
+ while (prefix.length + argText.length < width) {
+ if (flagLeftAlign) {
+ argText += ' ';
+ } else {
+ if (flagZeroPad) {
+ argText = '0' + argText;
+ } else {
+ prefix = ' ' + prefix;
+ }
+ }
+ }
+ // Insert the result into the buffer.
+ argText = prefix + argText;
+ argText.split('').forEach(function(chr) {
+ ret.push(chr.charCodeAt(0));
+ });
+ break;
+ }
+ case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': {
+ // Float.
+ var currArg = getNextArg('double');
+ var argText;
+ if (isNaN(currArg)) {
+ argText = 'nan';
+ flagZeroPad = false;
+ } else if (!isFinite(currArg)) {
+ argText = (currArg < 0 ? '-' : '') + 'inf';
+ flagZeroPad = false;
+ } else {
+ var isGeneral = false;
+ var effectivePrecision = Math.min(precision, 20);
+ // Convert g/G to f/F or e/E, as per:
+ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html
+ if (next == 103 || next == 71) {
+ isGeneral = true;
+ precision = precision || 1;
+ var exponent = parseInt(currArg.toExponential(effectivePrecision).split('e')[1], 10);
+ if (precision > exponent && exponent >= -4) {
+ next = ((next == 103) ? 'f' : 'F').charCodeAt(0);
+ precision -= exponent + 1;
+ } else {
+ next = ((next == 103) ? 'e' : 'E').charCodeAt(0);
+ precision--;
+ }
+ effectivePrecision = Math.min(precision, 20);
+ }
+ if (next == 101 || next == 69) {
+ argText = currArg.toExponential(effectivePrecision);
+ // Make sure the exponent has at least 2 digits.
+ if (/[eE][-+]\d$/.test(argText)) {
+ argText = argText.slice(0, -1) + '0' + argText.slice(-1);
+ }
+ } else if (next == 102 || next == 70) {
+ argText = currArg.toFixed(effectivePrecision);
+ if (currArg === 0 && __reallyNegative(currArg)) {
+ argText = '-' + argText;
+ }
+ }
+ var parts = argText.split('e');
+ if (isGeneral && !flagAlternative) {
+ // Discard trailing zeros and periods.
+ while (parts[0].length > 1 && parts[0].indexOf('.') != -1 &&
+ (parts[0].slice(-1) == '0' || parts[0].slice(-1) == '.')) {
+ parts[0] = parts[0].slice(0, -1);
+ }
+ } else {
+ // Make sure we have a period in alternative mode.
+ if (flagAlternative && argText.indexOf('.') == -1) parts[0] += '.';
+ // Zero pad until required precision.
+ while (precision > effectivePrecision++) parts[0] += '0';
+ }
+ argText = parts[0] + (parts.length > 1 ? 'e' + parts[1] : '');
+ // Capitalize 'E' if needed.
+ if (next == 69) argText = argText.toUpperCase();
+ // Add sign.
+ if (flagAlwaysSigned && currArg >= 0) {
+ argText = '+' + argText;
+ }
+ }
+ // Add padding.
+ while (argText.length < width) {
+ if (flagLeftAlign) {
+ argText += ' ';
+ } else {
+ if (flagZeroPad && (argText[0] == '-' || argText[0] == '+')) {
+ argText = argText[0] + '0' + argText.slice(1);
+ } else {
+ argText = (flagZeroPad ? '0' : ' ') + argText;
+ }
+ }
+ }
+ // Adjust case.
+ if (next < 97) argText = argText.toUpperCase();
+ // Insert the result into the buffer.
+ argText.split('').forEach(function(chr) {
+ ret.push(chr.charCodeAt(0));
+ });
+ break;
+ }
+ case 's': {
+ // String.
+ var arg = getNextArg('i8*');
+ var argLength = arg ? _strlen(arg) : '(null)'.length;
+ if (precisionSet) argLength = Math.min(argLength, precision);
+ if (!flagLeftAlign) {
+ while (argLength < width--) {
+ ret.push(32);
+ }
+ }
+ if (arg) {
+ for (var i = 0; i < argLength; i++) {
+ ret.push(HEAPU8[((arg++)|0)]);
+ }
+ } else {
+ ret = ret.concat(intArrayFromString('(null)'.substr(0, argLength), true));
+ }
+ if (flagLeftAlign) {
+ while (argLength < width--) {
+ ret.push(32);
+ }
+ }
+ break;
+ }
+ case 'c': {
+ // Character.
+ if (flagLeftAlign) ret.push(getNextArg('i8'));
+ while (--width > 0) {
+ ret.push(32);
+ }
+ if (!flagLeftAlign) ret.push(getNextArg('i8'));
+ break;
+ }
+ case 'n': {
+ // Write the length written so far to the next parameter.
+ var ptr = getNextArg('i32*');
+ HEAP32[((ptr)>>2)]=ret.length
+ break;
+ }
+ case '%': {
+ // Literal percent sign.
+ ret.push(curr);
+ break;
+ }
+ default: {
+ // Unknown specifiers remain untouched.
+ for (var i = startTextIndex; i < textIndex + 2; i++) {
+ ret.push(HEAP8[(i)]);
+ }
+ }
+ }
+ textIndex += 2;
+ // TODO: Support a/A (hex float) and m (last error) specifiers.
+ // TODO: Support %1${specifier} for arg selection.
+ } else {
+ ret.push(curr);
+ textIndex += 1;
+ }
+ }
+ return ret;
+ }function _fprintf(stream, format, varargs) {
+ // int fprintf(FILE *restrict stream, const char *restrict format, ...);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html
+ var result = __formatString(format, varargs);
+ var stack = Runtime.stackSave();
+ var ret = _fwrite(allocate(result, 'i8', ALLOC_STACK), 1, result.length, stream);
+ Runtime.stackRestore(stack);
+ return ret;
+ }
+ Module["_strcpy"] = _strcpy;
+ function _strncmp(px, py, n) {
+ var i = 0;
+ while (i < n) {
+ var x = HEAPU8[(((px)+(i))|0)];
+ var y = HEAPU8[(((py)+(i))|0)];
+ if (x == y && x == 0) return 0;
+ if (x == 0) return -1;
+ if (y == 0) return 1;
+ if (x == y) {
+ i ++;
+ continue;
+ } else {
+ return x > y ? 1 : -1;
+ }
+ }
+ return 0;
+ }function _strcmp(px, py) {
+ return _strncmp(px, py, TOTAL_MEMORY);
+ }
+ var _exp=Math.exp;
+ var _ceil=Math.ceil;
+ function _log10(x) {
+ return Math.log(x) / Math.LN10;
+ }
+ function _snprintf(s, n, format, varargs) {
+ // int snprintf(char *restrict s, size_t n, const char *restrict format, ...);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html
+ var result = __formatString(format, varargs);
+ var limit = (n === undefined) ? result.length
+ : Math.min(result.length, Math.max(n - 1, 0));
+ if (s < 0) {
+ s = -s;
+ var buf = _malloc(limit+1);
+ HEAP32[((s)>>2)]=buf;
+ s = buf;
+ }
+ for (var i = 0; i < limit; i++) {
+ HEAP8[(((s)+(i))|0)]=result[i];
+ }
+ if (limit < n || (n === undefined)) HEAP8[(((s)+(i))|0)]=0;
+ return result.length;
+ }function _sprintf(s, format, varargs) {
+ // int sprintf(char *restrict s, const char *restrict format, ...);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html
+ return _snprintf(s, undefined, format, varargs);
+ }
+ Module["_strcat"] = _strcat;
+ function _strchr(ptr, chr) {
+ ptr--;
+ do {
+ ptr++;
+ var val = HEAP8[(ptr)];
+ if (val == chr) return ptr;
+ } while (val);
+ return 0;
+ }
+ var _floor=Math.floor;
+ var _llvm_pow_f64=Math.pow;
+ Module["_memcpy"] = _memcpy;var _llvm_memcpy_p0i8_p0i8_i32=_memcpy;
+ Module["_memset"] = _memset;var _llvm_memset_p0i8_i32=_memset;
+ function _ftell(stream) {
+ // long ftell(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftell.html
+ if (FS.streams[stream]) {
+ stream = FS.streams[stream];
+ if (stream.object.isDevice) {
+ ___setErrNo(ERRNO_CODES.ESPIPE);
+ return -1;
+ } else {
+ return stream.position;
+ }
+ } else {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ }
+ function _lseek(fildes, offset, whence) {
+ // off_t lseek(int fildes, off_t offset, int whence);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/lseek.html
+ if (FS.streams[fildes] && !FS.streams[fildes].object.isDevice) {
+ var stream = FS.streams[fildes];
+ var position = offset;
+ if (whence === 1) { // SEEK_CUR.
+ position += stream.position;
+ } else if (whence === 2) { // SEEK_END.
+ position += stream.object.contents.length;
+ }
+ if (position < 0) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
+ } else {
+ stream.ungotten = [];
+ stream.position = position;
+ return position;
+ }
+ } else {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ }function _fseek(stream, offset, whence) {
+ // int fseek(FILE *stream, long offset, int whence);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fseek.html
+ var ret = _lseek(stream, offset, whence);
+ if (ret == -1) {
+ return -1;
+ } else {
+ FS.streams[stream].eof = false;
+ return 0;
+ }
+ }function _rewind(stream) {
+ // void rewind(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/rewind.html
+ _fseek(stream, 0, 0); // SEEK_SET.
+ if (FS.streams[stream]) FS.streams[stream].error = false;
+ }
+ function _recv(fd, buf, len, flags) {
+ var info = FS.streams[fd];
+ if (!info) return -1;
+ if (!info.hasData()) {
+ ___setErrNo(ERRNO_CODES.EAGAIN); // no data, and all sockets are nonblocking, so this is the right behavior
+ return -1;
+ }
+ var buffer = info.inQueue.shift();
+ if (len < buffer.length) {
+ if (info.stream) {
+ // This is tcp (reliable), so if not all was read, keep it
+ info.inQueue.unshift(buffer.subarray(len));
+ }
+ buffer = buffer.subarray(0, len);
+ }
+ HEAPU8.set(buffer, buf);
+ return buffer.length;
+ }
+ function _pread(fildes, buf, nbyte, offset) {
+ // ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html
+ var stream = FS.streams[fildes];
+ if (!stream || stream.object.isDevice) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ } else if (!stream.isRead) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ return -1;
+ } else if (stream.object.isFolder) {
+ ___setErrNo(ERRNO_CODES.EISDIR);
+ return -1;
+ } else if (nbyte < 0 || offset < 0) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
+ } else {
+ var bytesRead = 0;
+ while (stream.ungotten.length && nbyte > 0) {
+ HEAP8[((buf++)|0)]=stream.ungotten.pop()
+ nbyte--;
+ bytesRead++;
+ }
+ var contents = stream.object.contents;
+ var size = Math.min(contents.length - offset, nbyte);
+ if (contents.subarray) { // typed array
+ HEAPU8.set(contents.subarray(offset, offset+size), buf);
+ } else
+ if (contents.slice) { // normal array
+ for (var i = 0; i < size; i++) {
+ HEAP8[(((buf)+(i))|0)]=contents[offset + i]
+ }
+ } else {
+ for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR
+ HEAP8[(((buf)+(i))|0)]=contents.get(offset + i)
+ }
+ }
+ bytesRead += size;
+ return bytesRead;
+ }
+ }function _read(fildes, buf, nbyte) {
+ // ssize_t read(int fildes, void *buf, size_t nbyte);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html
+ var stream = FS.streams[fildes];
+ if (stream && ('socket' in stream)) {
+ return _recv(fildes, buf, nbyte, 0);
+ } else if (!stream) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ } else if (!stream.isRead) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ return -1;
+ } else if (nbyte < 0) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
+ } else {
+ var bytesRead;
+ if (stream.object.isDevice) {
+ if (stream.object.input) {
+ bytesRead = 0;
+ while (stream.ungotten.length && nbyte > 0) {
+ HEAP8[((buf++)|0)]=stream.ungotten.pop()
+ nbyte--;
+ bytesRead++;
+ }
+ for (var i = 0; i < nbyte; i++) {
+ try {
+ var result = stream.object.input();
+ } catch (e) {
+ ___setErrNo(ERRNO_CODES.EIO);
+ return -1;
+ }
+ if (result === undefined && bytesRead === 0) {
+ ___setErrNo(ERRNO_CODES.EAGAIN);
+ return -1;
+ }
+ if (result === null || result === undefined) break;
+ bytesRead++;
+ HEAP8[(((buf)+(i))|0)]=result
+ }
+ return bytesRead;
+ } else {
+ ___setErrNo(ERRNO_CODES.ENXIO);
+ return -1;
+ }
+ } else {
+ var ungotSize = stream.ungotten.length;
+ bytesRead = _pread(fildes, buf, nbyte, stream.position);
+ if (bytesRead != -1) {
+ stream.position += (stream.ungotten.length - ungotSize) + bytesRead;
+ }
+ return bytesRead;
+ }
+ }
+ }function _fread(ptr, size, nitems, stream) {
+ // size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html
+ var bytesToRead = nitems * size;
+ if (bytesToRead == 0) return 0;
+ var bytesRead = _read(stream, ptr, bytesToRead);
+ var streamObj = FS.streams[stream];
+ if (bytesRead == -1) {
+ if (streamObj) streamObj.error = true;
+ return 0;
+ } else {
+ if (bytesRead < bytesToRead) streamObj.eof = true;
+ return Math.floor(bytesRead / size);
+ }
+ }
+ function _close(fildes) {
+ // int close(int fildes);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/close.html
+ if (FS.streams[fildes]) {
+ if (FS.streams[fildes].currentEntry) {
+ _free(FS.streams[fildes].currentEntry);
+ }
+ FS.streams[fildes] = null;
+ return 0;
+ } else {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ }
+ function _fsync(fildes) {
+ // int fsync(int fildes);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsync.html
+ if (FS.streams[fildes]) {
+ // We write directly to the file system, so there's nothing to do here.
+ return 0;
+ } else {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ }function _fclose(stream) {
+ // int fclose(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fclose.html
+ _fsync(stream);
+ return _close(stream);
+ }
+ function _strcspn(pstr, pset) {
+ var str = pstr, set, strcurr, setcurr;
+ while (1) {
+ strcurr = HEAP8[(str)];
+ if (!strcurr) return str - pstr;
+ set = pset;
+ while (1) {
+ setcurr = HEAP8[(set)];
+ if (!setcurr || setcurr == strcurr) break;
+ set++;
+ }
+ if (setcurr) return str - pstr;
+ str++;
+ }
+ }
+ Module["_strncpy"] = _strncpy;
+ function _isspace(chr) {
+ return chr in { 32: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0 };
+ }function __parseInt(str, endptr, base, min, max, bits, unsign) {
+ // Skip space.
+ while (_isspace(HEAP8[(str)])) str++;
+ // Check for a plus/minus sign.
+ var multiplier = 1;
+ if (HEAP8[(str)] == 45) {
+ multiplier = -1;
+ str++;
+ } else if (HEAP8[(str)] == 43) {
+ str++;
+ }
+ // Find base.
+ var finalBase = base;
+ if (!finalBase) {
+ if (HEAP8[(str)] == 48) {
+ if (HEAP8[((str+1)|0)] == 120 ||
+ HEAP8[((str+1)|0)] == 88) {
+ finalBase = 16;
+ str += 2;
+ } else {
+ finalBase = 8;
+ str++;
+ }
+ }
+ } else if (finalBase==16) {
+ if (HEAP8[(str)] == 48) {
+ if (HEAP8[((str+1)|0)] == 120 ||
+ HEAP8[((str+1)|0)] == 88) {
+ str += 2;
+ }
+ }
+ }
+ if (!finalBase) finalBase = 10;
+ // Get digits.
+ var chr;
+ var ret = 0;
+ while ((chr = HEAP8[(str)]) != 0) {
+ var digit = parseInt(String.fromCharCode(chr), finalBase);
+ if (isNaN(digit)) {
+ break;
+ } else {
+ ret = ret * finalBase + digit;
+ str++;
+ }
+ }
+ // Apply sign.
+ ret *= multiplier;
+ // Set end pointer.
+ if (endptr) {
+ HEAP32[((endptr)>>2)]=str
+ }
+ // Unsign if needed.
+ if (unsign) {
+ if (Math.abs(ret) > max) {
+ ret = max;
+ ___setErrNo(ERRNO_CODES.ERANGE);
+ } else {
+ ret = unSign(ret, bits);
+ }
+ }
+ // Validate range.
+ if (ret > max || ret < min) {
+ ret = ret > max ? max : min;
+ ___setErrNo(ERRNO_CODES.ERANGE);
+ }
+ if (bits == 64) {
+ return ((asm["setTempRet0"](Math.min(Math.floor((ret)/(+(4294967296))), (+(4294967295)))>>>0),ret>>>0)|0);
+ }
+ return ret;
+ }function _strtol(str, endptr, base) {
+ return __parseInt(str, endptr, base, -2147483648, 2147483647, 32); // LONG_MIN, LONG_MAX.
+ }function _atoi(ptr) {
+ return _strtol(ptr, null, 10);
+ }
+ function _strstr(ptr1, ptr2) {
+ var check = 0, start;
+ do {
+ if (!check) {
+ start = ptr1;
+ check = ptr2;
+ }
+ var curr1 = HEAP8[((ptr1++)|0)];
+ var curr2 = HEAP8[((check++)|0)];
+ if (curr2 == 0) return start;
+ if (curr2 != curr1) {
+ // rewind to one character after start, to find ez in eeez
+ ptr1 = start + 1;
+ check = 0;
+ }
+ } while (curr1);
+ return 0;
+ }
+ Module["_memcmp"] = _memcmp;
+ function _fputs(s, stream) {
+ // int fputs(const char *restrict s, FILE *restrict stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputs.html
+ return _write(stream, s, _strlen(s));
+ }
+ Module["_memmove"] = _memmove;var _llvm_memmove_p0i8_p0i8_i32=_memmove;
+ function _fflush(stream) {
+ // int fflush(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fflush.html
+ var flush = function(filedes) {
+ // Right now we write all data directly, except for output devices.
+ if (FS.streams[filedes] && FS.streams[filedes].object.output) {
+ if (!FS.streams[filedes].isTerminal) { // don't flush terminals, it would cause a \n to also appear
+ FS.streams[filedes].object.output(null);
+ }
+ }
+ };
+ try {
+ if (stream === 0) {
+ for (var i = 0; i < FS.streams.length; i++) if (FS.streams[i]) flush(i);
+ } else {
+ flush(stream);
+ }
+ return 0;
+ } catch (e) {
+ ___setErrNo(ERRNO_CODES.EIO);
+ return -1;
+ }
+ }
+ function __exit(status) {
+ // void _exit(int status);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html
+ function ExitStatus() {
+ this.name = "ExitStatus";
+ this.message = "Program terminated with exit(" + status + ")";
+ this.status = status;
+ Module.print('Exit Status: ' + status);
+ };
+ ExitStatus.prototype = new Error();
+ ExitStatus.prototype.constructor = ExitStatus;
+ exitRuntime();
+ ABORT = true;
+ throw new ExitStatus();
+ }function _exit(status) {
+ __exit(status);
+ }
+ function _fgetc(stream) {
+ // int fgetc(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetc.html
+ if (!FS.streams[stream]) return -1;
+ var streamObj = FS.streams[stream];
+ if (streamObj.eof || streamObj.error) return -1;
+ var ret = _read(stream, _fgetc.ret, 1);
+ if (ret == 0) {
+ streamObj.eof = true;
+ return -1;
+ } else if (ret == -1) {
+ streamObj.error = true;
+ return -1;
+ } else {
+ return HEAPU8[((_fgetc.ret)|0)];
+ }
+ }function _fgets(s, n, stream) {
+ // char *fgets(char *restrict s, int n, FILE *restrict stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgets.html
+ if (!FS.streams[stream]) return 0;
+ var streamObj = FS.streams[stream];
+ if (streamObj.error || streamObj.eof) return 0;
+ var byte_;
+ for (var i = 0; i < n - 1 && byte_ != 10; i++) {
+ byte_ = _fgetc(stream);
+ if (byte_ == -1) {
+ if (streamObj.error || (streamObj.eof && i == 0)) return 0;
+ else if (streamObj.eof) break;
+ }
+ HEAP8[(((s)+(i))|0)]=byte_
+ }
+ HEAP8[(((s)+(i))|0)]=0
+ return s;
+ }
+ function _usleep(useconds) {
+ // int usleep(useconds_t useconds);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/usleep.html
+ // We're single-threaded, so use a busy loop. Super-ugly.
+ var msec = useconds / 1000;
+ var start = Date.now();
+ while (Date.now() - start < msec) {
+ // Do nothing.
+ }
+ return 0;
+ }
+ function _fputc(c, stream) {
+ // int fputc(int c, FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputc.html
+ var chr = unSign(c & 0xFF);
+ HEAP8[((_fputc.ret)|0)]=chr
+ var ret = _write(stream, _fputc.ret, 1);
+ if (ret == -1) {
+ if (FS.streams[stream]) FS.streams[stream].error = true;
+ return -1;
+ } else {
+ return chr;
+ }
+ }
+ function _pclose(stream) {
+ // int pclose(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/pclose.html
+ // We allow only one process, so no pipes.
+ ___setErrNo(ERRNO_CODES.ECHILD);
+ return -1;
+ }
+ function _puts(s) {
+ // int puts(const char *s);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/puts.html
+ // NOTE: puts() always writes an extra newline.
+ var stdout = HEAP32[((_stdout)>>2)];
+ var ret = _fputs(s, stdout);
+ if (ret < 0) {
+ return ret;
+ } else {
+ var newlineRet = _fputc(10, stdout);
+ return (newlineRet < 0) ? -1 : ret + 1;
+ }
+ }
+ var ERRNO_MESSAGES={0:"Success",1:"Not super-user",2:"No such file or directory",3:"No such process",4:"Interrupted system call",5:"I/O error",6:"No such device or address",7:"Arg list too long",8:"Exec format error",9:"Bad file number",10:"No children",11:"No more processes",12:"Not enough core",13:"Permission denied",14:"Bad address",15:"Block device required",16:"Mount device busy",17:"File exists",18:"Cross-device link",19:"No such device",20:"Not a directory",21:"Is a directory",22:"Invalid argument",23:"Too many open files in system",24:"Too many open files",25:"Not a typewriter",26:"Text file busy",27:"File too large",28:"No space left on device",29:"Illegal seek",30:"Read only file system",31:"Too many links",32:"Broken pipe",33:"Math arg out of domain of func",34:"Math result not representable",35:"No message of desired type",36:"Identifier removed",37:"Channel number out of range",38:"Level 2 not synchronized",39:"Level 3 halted",40:"Level 3 reset",41:"Link number out of range",42:"Protocol driver not attached",43:"No CSI structure available",44:"Level 2 halted",45:"Deadlock condition",46:"No record locks available",50:"Invalid exchange",51:"Invalid request descriptor",52:"Exchange full",53:"No anode",54:"Invalid request code",55:"Invalid slot",56:"File locking deadlock error",57:"Bad font file fmt",60:"Device not a stream",61:"No data (for no delay io)",62:"Timer expired",63:"Out of streams resources",64:"Machine is not on the network",65:"Package not installed",66:"The object is remote",67:"The link has been severed",68:"Advertise error",69:"Srmount error",70:"Communication error on send",71:"Protocol error",74:"Multihop attempted",75:"Inode is remote (not really error)",76:"Cross mount point (not really error)",77:"Trying to read unreadable message",79:"Inappropriate file type or format",80:"Given log. name not unique",81:"f.d. invalid for this operation",82:"Remote address changed",83:"Can\t access a needed shared lib",84:"Accessing a corrupted shared lib",85:".lib section in a.out corrupted",86:"Attempting to link in too many libs",87:"Attempting to exec a shared library",88:"Function not implemented",89:"No more files",90:"Directory not empty",91:"File or path name too long",92:"Too many symbolic links",95:"Operation not supported on transport endpoint",96:"Protocol family not supported",104:"Connection reset by peer",105:"No buffer space available",106:"Address family not supported by protocol family",107:"Protocol wrong type for socket",108:"Socket operation on non-socket",109:"Protocol not available",110:"Can't send after socket shutdown",111:"Connection refused",112:"Address already in use",113:"Connection aborted",114:"Network is unreachable",115:"Network interface is not configured",116:"Connection timed out",117:"Host is down",118:"Host is unreachable",119:"Connection already in progress",120:"Socket already connected",121:"Destination address required",122:"Message too long",123:"Unknown protocol",124:"Socket type not supported",125:"Address not available",126:"ENETRESET",127:"Socket is already connected",128:"Socket is not connected",129:"TOOMANYREFS",130:"EPROCLIM",131:"EUSERS",132:"EDQUOT",133:"ESTALE",134:"Not supported",135:"No medium (in tape drive)",136:"No such host or network path",137:"Filename exists with different case",138:"EILSEQ",139:"Value too large for defined data type",140:"Operation canceled",141:"State not recoverable",142:"Previous owner died",143:"Streams pipe error"};function _strerror_r(errnum, strerrbuf, buflen) {
+ if (errnum in ERRNO_MESSAGES) {
+ if (ERRNO_MESSAGES[errnum].length > buflen - 1) {
+ return ___setErrNo(ERRNO_CODES.ERANGE);
+ } else {
+ var msg = ERRNO_MESSAGES[errnum];
+ for (var i = 0; i < msg.length; i++) {
+ HEAP8[(((strerrbuf)+(i))|0)]=msg.charCodeAt(i)
+ }
+ HEAP8[(((strerrbuf)+(i))|0)]=0
+ return 0;
+ }
+ } else {
+ return ___setErrNo(ERRNO_CODES.EINVAL);
+ }
+ }function _strerror(errnum) {
+ if (!_strerror.buffer) _strerror.buffer = _malloc(256);
+ _strerror_r(errnum, _strerror.buffer, 256);
+ return _strerror.buffer;
+ }
+ function ___errno_location() {
+ return ___errno_state;
+ }function _perror(s) {
+ // void perror(const char *s);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/perror.html
+ var stdout = HEAP32[((_stdout)>>2)];
+ if (s) {
+ _fputs(s, stdout);
+ _fputc(58, stdout);
+ _fputc(32, stdout);
+ }
+ var errnum = HEAP32[((___errno_location())>>2)];
+ _puts(_strerror(errnum));
+ }
+ function _popen(command, mode) {
+ // FILE *popen(const char *command, const char *mode);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/popen.html
+ // We allow only one process, so no pipes.
+ ___setErrNo(ERRNO_CODES.EMFILE);
+ return 0;
+ }
+ var ___dirent_struct_layout={__size__:1040,d_ino:0,d_name:4,d_off:1028,d_reclen:1032,d_type:1036};function _open(path, oflag, varargs) {
+ // int open(const char *path, int oflag, ...);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html
+ // NOTE: This implementation tries to mimic glibc rather than strictly
+ // following the POSIX standard.
+ var mode = HEAP32[((varargs)>>2)];
+ // Simplify flags.
+ var accessMode = oflag & 3;
+ var isWrite = accessMode != 0;
+ var isRead = accessMode != 1;
+ var isCreate = Boolean(oflag & 512);
+ var isExistCheck = Boolean(oflag & 2048);
+ var isTruncate = Boolean(oflag & 1024);
+ var isAppend = Boolean(oflag & 8);
+ // Verify path.
+ var origPath = path;
+ path = FS.analyzePath(Pointer_stringify(path));
+ if (!path.parentExists) {
+ ___setErrNo(path.error);
+ return -1;
+ }
+ var target = path.object || null;
+ var finalPath;
+ // Verify the file exists, create if needed and allowed.
+ if (target) {
+ if (isCreate && isExistCheck) {
+ ___setErrNo(ERRNO_CODES.EEXIST);
+ return -1;
+ }
+ if ((isWrite || isCreate || isTruncate) && target.isFolder) {
+ ___setErrNo(ERRNO_CODES.EISDIR);
+ return -1;
+ }
+ if (isRead && !target.read || isWrite && !target.write) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ return -1;
+ }
+ if (isTruncate && !target.isDevice) {
+ target.contents = [];
+ } else {
+ if (!FS.forceLoadFile(target)) {
+ ___setErrNo(ERRNO_CODES.EIO);
+ return -1;
+ }
+ }
+ finalPath = path.path;
+ } else {
+ if (!isCreate) {
+ ___setErrNo(ERRNO_CODES.ENOENT);
+ return -1;
+ }
+ if (!path.parentObject.write) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ return -1;
+ }
+ target = FS.createDataFile(path.parentObject, path.name, [],
+ mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR.
+ finalPath = path.parentPath + '/' + path.name;
+ }
+ // Actually create an open stream.
+ var id;
+ if (target.isFolder) {
+ var entryBuffer = 0;
+ if (___dirent_struct_layout) {
+ entryBuffer = _malloc(___dirent_struct_layout.__size__);
+ }
+ var contents = [];
+ for (var key in target.contents) contents.push(key);
+ id = FS.createFileHandle({
+ path: finalPath,
+ object: target,
+ // An index into contents. Special values: -2 is ".", -1 is "..".
+ position: -2,
+ isRead: true,
+ isWrite: false,
+ isAppend: false,
+ error: false,
+ eof: false,
+ ungotten: [],
+ // Folder-specific properties:
+ // Remember the contents at the time of opening in an array, so we can
+ // seek between them relying on a single order.
+ contents: contents,
+ // Each stream has its own area for readdir() returns.
+ currentEntry: entryBuffer
+ });
+ } else {
+ id = FS.createFileHandle({
+ path: finalPath,
+ object: target,
+ position: 0,
+ isRead: isRead,
+ isWrite: isWrite,
+ isAppend: isAppend,
+ error: false,
+ eof: false,
+ ungotten: []
+ });
+ }
+ return id;
+ }function _fopen(filename, mode) {
+ // FILE *fopen(const char *restrict filename, const char *restrict mode);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fopen.html
+ var flags;
+ mode = Pointer_stringify(mode);
+ if (mode[0] == 'r') {
+ if (mode.indexOf('+') != -1) {
+ flags = 2;
+ } else {
+ flags = 0;
+ }
+ } else if (mode[0] == 'w') {
+ if (mode.indexOf('+') != -1) {
+ flags = 2;
+ } else {
+ flags = 1;
+ }
+ flags |= 512;
+ flags |= 1024;
+ } else if (mode[0] == 'a') {
+ if (mode.indexOf('+') != -1) {
+ flags = 2;
+ } else {
+ flags = 1;
+ }
+ flags |= 512;
+ flags |= 8;
+ } else {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return 0;
+ }
+ var ret = _open(filename, flags, allocate([0x1FF, 0, 0, 0], 'i32', ALLOC_STACK)); // All creation permissions.
+ return (ret == -1) ? 0 : ret;
+ }
+ var _putc=_fputc;
+ function _getcwd(buf, size) {
+ // char *getcwd(char *buf, size_t size);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/getcwd.html
+ if (size == 0) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return 0;
+ } else if (size < FS.currentPath.length + 1) {
+ ___setErrNo(ERRNO_CODES.ERANGE);
+ return 0;
+ } else {
+ for (var i = 0; i < FS.currentPath.length; i++) {
+ HEAP8[(((buf)+(i))|0)]=FS.currentPath.charCodeAt(i)
+ }
+ HEAP8[(((buf)+(i))|0)]=0
+ return buf;
+ }
+ }
+ var _environ=allocate(1, "i32*", ALLOC_STATIC);var ___environ=_environ;function ___buildEnvironment(env) {
+ // WARNING: Arbitrary limit!
+ var MAX_ENV_VALUES = 64;
+ var TOTAL_ENV_SIZE = 1024;
+ // Statically allocate memory for the environment.
+ var poolPtr;
+ var envPtr;
+ if (!___buildEnvironment.called) {
+ ___buildEnvironment.called = true;
+ // Set default values. Use string keys for Closure Compiler compatibility.
+ ENV['USER'] = 'root';
+ ENV['PATH'] = '/';
+ ENV['PWD'] = '/';
+ ENV['HOME'] = '/home/emscripten';
+ ENV['LANG'] = 'en_US.UTF-8';
+ ENV['_'] = './this.program';
+ // Allocate memory.
+ poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC);
+ envPtr = allocate(MAX_ENV_VALUES * 4,
+ 'i8*', ALLOC_STATIC);
+ HEAP32[((envPtr)>>2)]=poolPtr
+ HEAP32[((_environ)>>2)]=envPtr;
+ } else {
+ envPtr = HEAP32[((_environ)>>2)];
+ poolPtr = HEAP32[((envPtr)>>2)];
+ }
+ // Collect key=value lines.
+ var strings = [];
+ var totalSize = 0;
+ for (var key in env) {
+ if (typeof env[key] === 'string') {
+ var line = key + '=' + env[key];
+ strings.push(line);
+ totalSize += line.length;
+ }
+ }
+ if (totalSize > TOTAL_ENV_SIZE) {
+ throw new Error('Environment size exceeded TOTAL_ENV_SIZE!');
+ }
+ // Make new.
+ var ptrSize = 4;
+ for (var i = 0; i < strings.length; i++) {
+ var line = strings[i];
+ for (var j = 0; j < line.length; j++) {
+ HEAP8[(((poolPtr)+(j))|0)]=line.charCodeAt(j);
+ }
+ HEAP8[(((poolPtr)+(j))|0)]=0;
+ HEAP32[(((envPtr)+(i * ptrSize))>>2)]=poolPtr;
+ poolPtr += line.length + 1;
+ }
+ HEAP32[(((envPtr)+(strings.length * ptrSize))>>2)]=0;
+ }var ENV={};function _getenv(name) {
+ // char *getenv(const char *name);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/getenv.html
+ if (name === 0) return 0;
+ name = Pointer_stringify(name);
+ if (!ENV.hasOwnProperty(name)) return 0;
+ if (_getenv.ret) _free(_getenv.ret);
+ _getenv.ret = allocate(intArrayFromString(ENV[name]), 'i8', ALLOC_NORMAL);
+ return _getenv.ret;
+ }
+ function _strncat(pdest, psrc, num) {
+ var len = _strlen(pdest);
+ var i = 0;
+ while(1) {
+ HEAP8[((pdest+len+i)|0)]=HEAP8[((psrc+i)|0)];
+ if (HEAP8[(((pdest)+(len+i))|0)] == 0) break;
+ i ++;
+ if (i == num) {
+ HEAP8[(((pdest)+(len+i))|0)]=0
+ break;
+ }
+ }
+ return pdest;
+ }
+ function _printf(format, varargs) {
+ // int printf(const char *restrict format, ...);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html
+ var stdout = HEAP32[((_stdout)>>2)];
+ return _fprintf(stdout, format, varargs);
+ }
+ function _system(command) {
+ // int system(const char *command);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html
+ // Can't call external programs.
+ ___setErrNo(ERRNO_CODES.EAGAIN);
+ return -1;
+ }
+ function _isalpha(chr) {
+ return (chr >= 97 && chr <= 122) ||
+ (chr >= 65 && chr <= 90);
+ }
+ function _isalnum(chr) {
+ return (chr >= 48 && chr <= 57) ||
+ (chr >= 97 && chr <= 122) ||
+ (chr >= 65 && chr <= 90);
+ }
+ var _getc=_fgetc;
+ function _chdir(path) {
+ // int chdir(const char *path);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/chdir.html
+ // NOTE: The path argument may be a string, to simplify fchdir().
+ if (typeof path !== 'string') path = Pointer_stringify(path);
+ path = FS.analyzePath(path);
+ if (!path.exists) {
+ ___setErrNo(path.error);
+ return -1;
+ } else if (!path.object.isFolder) {
+ ___setErrNo(ERRNO_CODES.ENOTDIR);
+ return -1;
+ } else {
+ FS.currentPath = path.path;
+ return 0;
+ }
+ }
+ function _tmpnam(s, dir, prefix) {
+ // char *tmpnam(char *s);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpnam.html
+ // NOTE: The dir and prefix arguments are for internal use only.
+ var folder = FS.findObject(dir || '/tmp');
+ if (!folder || !folder.isFolder) {
+ dir = '/tmp';
+ folder = FS.findObject(dir);
+ if (!folder || !folder.isFolder) return 0;
+ }
+ var name = prefix || 'file';
+ do {
+ name += String.fromCharCode(65 + Math.floor(Math.random() * 25));
+ } while (name in folder.contents);
+ var result = dir + '/' + name;
+ if (!_tmpnam.buffer) _tmpnam.buffer = _malloc(256);
+ if (!s) s = _tmpnam.buffer;
+ for (var i = 0; i < result.length; i++) {
+ HEAP8[(((s)+(i))|0)]=result.charCodeAt(i);
+ }
+ HEAP8[(((s)+(i))|0)]=0;
+ return s;
+ }function _tmpfile() {
+ // FILE *tmpfile(void);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpfile.html
+ // TODO: Delete the created file on closing.
+ if (_tmpfile.mode) {
+ _tmpfile.mode = allocate(intArrayFromString('w+'), 'i8', ALLOC_NORMAL);
+ }
+ return _fopen(_tmpnam(0), _tmpfile.mode);
+ }
+ function _memchr(ptr, chr, num) {
+ chr = unSign(chr);
+ for (var i = 0; i < num; i++) {
+ if (HEAP8[(ptr)] == chr) return ptr;
+ ptr++;
+ }
+ return 0;
+ }
+ var _sqrt=Math.sqrt;
+ function _fileno(stream) {
+ // int fileno(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fileno.html
+ // We use file descriptor numbers and FILE* streams interchangeably.
+ return stream;
+ }
+ function _fdopen(fildes, mode) {
+ // FILE *fdopen(int fildes, const char *mode);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fdopen.html
+ if (FS.streams[fildes]) {
+ var stream = FS.streams[fildes];
+ mode = Pointer_stringify(mode);
+ if ((mode.indexOf('w') != -1 && !stream.isWrite) ||
+ (mode.indexOf('r') != -1 && !stream.isRead) ||
+ (mode.indexOf('a') != -1 && !stream.isAppend) ||
+ (mode.indexOf('+') != -1 && (!stream.isRead || !stream.isWrite))) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return 0;
+ } else {
+ stream.error = false;
+ stream.eof = false;
+ return fildes;
+ }
+ } else {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return 0;
+ }
+ }
+ var ___stat_struct_layout={__size__:68,st_dev:0,st_ino:4,st_mode:8,st_nlink:12,st_uid:16,st_gid:20,st_rdev:24,st_size:28,st_atime:32,st_spare1:36,st_mtime:40,st_spare2:44,st_ctime:48,st_spare3:52,st_blksize:56,st_blocks:60,st_spare4:64};function _stat(path, buf, dontResolveLastLink) {
+ // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html
+ // int stat(const char *path, struct stat *buf);
+ // NOTE: dontResolveLastLink is a shortcut for lstat(). It should never be
+ // used in client code.
+ var obj = FS.findObject(Pointer_stringify(path), dontResolveLastLink);
+ if (obj === null || !FS.forceLoadFile(obj)) return -1;
+ var offsets = ___stat_struct_layout;
+ // Constants.
+ HEAP32[(((buf)+(offsets.st_nlink))>>2)]=1
+ HEAP32[(((buf)+(offsets.st_uid))>>2)]=0
+ HEAP32[(((buf)+(offsets.st_gid))>>2)]=0
+ HEAP32[(((buf)+(offsets.st_blksize))>>2)]=4096
+ // Variables.
+ HEAP32[(((buf)+(offsets.st_ino))>>2)]=obj.inodeNumber
+ var time = Math.floor(obj.timestamp / 1000);
+ if (offsets.st_atime === undefined) {
+ offsets.st_atime = offsets.st_atim.tv_sec;
+ offsets.st_mtime = offsets.st_mtim.tv_sec;
+ offsets.st_ctime = offsets.st_ctim.tv_sec;
+ var nanosec = (obj.timestamp % 1000) * 1000;
+ HEAP32[(((buf)+(offsets.st_atim.tv_nsec))>>2)]=nanosec
+ HEAP32[(((buf)+(offsets.st_mtim.tv_nsec))>>2)]=nanosec
+ HEAP32[(((buf)+(offsets.st_ctim.tv_nsec))>>2)]=nanosec
+ }
+ HEAP32[(((buf)+(offsets.st_atime))>>2)]=time
+ HEAP32[(((buf)+(offsets.st_mtime))>>2)]=time
+ HEAP32[(((buf)+(offsets.st_ctime))>>2)]=time
+ var mode = 0;
+ var size = 0;
+ var blocks = 0;
+ var dev = 0;
+ var rdev = 0;
+ if (obj.isDevice) {
+ // Device numbers reuse inode numbers.
+ dev = rdev = obj.inodeNumber;
+ size = blocks = 0;
+ mode = 0x2000; // S_IFCHR.
+ } else {
+ dev = 1;
+ rdev = 0;
+ // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize),
+ // but this is not required by the standard.
+ if (obj.isFolder) {
+ size = 4096;
+ blocks = 1;
+ mode = 0x4000; // S_IFDIR.
+ } else {
+ var data = obj.contents || obj.link;
+ size = data.length;
+ blocks = Math.ceil(data.length / 4096);
+ mode = obj.link === undefined ? 0x8000 : 0xA000; // S_IFREG, S_IFLNK.
+ }
+ }
+ HEAP32[(((buf)+(offsets.st_dev))>>2)]=dev;
+ HEAP32[(((buf)+(offsets.st_rdev))>>2)]=rdev;
+ HEAP32[(((buf)+(offsets.st_size))>>2)]=size
+ HEAP32[(((buf)+(offsets.st_blocks))>>2)]=blocks
+ if (obj.read) mode |= 0x16D; // S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH.
+ if (obj.write) mode |= 0x92; // S_IWUSR | S_IWGRP | S_IWOTH.
+ HEAP32[(((buf)+(offsets.st_mode))>>2)]=mode
+ return 0;
+ }
+ function _feof(stream) {
+ // int feof(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/feof.html
+ return Number(FS.streams[stream] && FS.streams[stream].eof);
+ }
+ function _setlocale(category, locale) {
+ if (!_setlocale.ret) _setlocale.ret = allocate([0], 'i8', ALLOC_NORMAL);
+ return _setlocale.ret;
+ }
+ function ___fpclassifyf(x) {
+ if (isNaN(x)) return 0;
+ if (!isFinite(x)) return 1;
+ if (x == 0) return 2;
+ // FP_SUBNORMAL..?
+ return 4;
+ }var ___fpclassifyd=___fpclassifyf;
+ var _cos=Math.cos;
+ var _sin=Math.sin;
+ Module["_tolower"] = _tolower;
+ Module["_strncasecmp"] = _strncasecmp;
+ Module["_strcasecmp"] = _strcasecmp;
+ function _strrchr(ptr, chr) {
+ var ptr2 = ptr + _strlen(ptr);
+ do {
+ if (HEAP8[(ptr2)] == chr) return ptr2;
+ ptr2--;
+ } while (ptr2 >= ptr);
+ return 0;
+ }
+ function _strspn(pstr, pset) {
+ var str = pstr, set, strcurr, setcurr;
+ while (1) {
+ strcurr = HEAP8[(str)];
+ if (!strcurr) return str - pstr;
+ set = pset;
+ while (1) {
+ setcurr = HEAP8[(set)];
+ if (!setcurr || setcurr == strcurr) break;
+ set++;
+ }
+ if (!setcurr) return str - pstr;
+ str++;
+ }
+ }
+ function _strpbrk(ptr1, ptr2) {
+ var curr;
+ var searchSet = {};
+ while (1) {
+ var curr = HEAP8[((ptr2++)|0)];
+ if (!curr) break;
+ searchSet[curr] = 1;
+ }
+ while (1) {
+ curr = HEAP8[(ptr1)];
+ if (!curr) break;
+ if (curr in searchSet) return ptr1;
+ ptr1++;
+ }
+ return 0;
+ }
+ function __isFloat(text) {
+ return !!(/^[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?$/.exec(text));
+ }function __scanString(format, get, unget, varargs) {
+ if (!__scanString.whiteSpace) {
+ __scanString.whiteSpace = {};
+ __scanString.whiteSpace[32] = 1;
+ __scanString.whiteSpace[9] = 1;
+ __scanString.whiteSpace[10] = 1;
+ __scanString.whiteSpace[11] = 1;
+ __scanString.whiteSpace[12] = 1;
+ __scanString.whiteSpace[13] = 1;
+ __scanString.whiteSpace[' '] = 1;
+ __scanString.whiteSpace['\t'] = 1;
+ __scanString.whiteSpace['\n'] = 1;
+ __scanString.whiteSpace['\v'] = 1;
+ __scanString.whiteSpace['\f'] = 1;
+ __scanString.whiteSpace['\r'] = 1;
+ }
+ // Supports %x, %4x, %d.%d, %lld, %s, %f, %lf.
+ // TODO: Support all format specifiers.
+ format = Pointer_stringify(format);
+ var soFar = 0;
+ if (format.indexOf('%n') >= 0) {
+ // need to track soFar
+ var _get = get;
+ get = function() {
+ soFar++;
+ return _get();
+ }
+ var _unget = unget;
+ unget = function() {
+ soFar--;
+ return _unget();
+ }
+ }
+ var formatIndex = 0;
+ var argsi = 0;
+ var fields = 0;
+ var argIndex = 0;
+ var next;
+ mainLoop:
+ for (var formatIndex = 0; formatIndex < format.length;) {
+ if (format[formatIndex] === '%' && format[formatIndex+1] == 'n') {
+ var argPtr = HEAP32[(((varargs)+(argIndex))>>2)];
+ argIndex += Runtime.getAlignSize('void*', null, true);
+ HEAP32[((argPtr)>>2)]=soFar;
+ formatIndex += 2;
+ continue;
+ }
+ // TODO: Support strings like "%5c" etc.
+ if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') {
+ var argPtr = HEAP32[(((varargs)+(argIndex))>>2)];
+ argIndex += Runtime.getAlignSize('void*', null, true);
+ fields++;
+ next = get();
+ HEAP8[(argPtr)]=next
+ formatIndex += 2;
+ continue;
+ }
+ // remove whitespace
+ while (1) {
+ next = get();
+ if (next == 0) return fields;
+ if (!(next in __scanString.whiteSpace)) break;
+ }
+ unget();
+ if (format[formatIndex] === '%') {
+ formatIndex++;
+ var suppressAssignment = false;
+ if (format[formatIndex] == '*') {
+ suppressAssignment = true;
+ formatIndex++;
+ }
+ var maxSpecifierStart = formatIndex;
+ while (format[formatIndex].charCodeAt(0) >= 48 &&
+ format[formatIndex].charCodeAt(0) <= 57) {
+ formatIndex++;
+ }
+ var max_;
+ if (formatIndex != maxSpecifierStart) {
+ max_ = parseInt(format.slice(maxSpecifierStart, formatIndex), 10);
+ }
+ var long_ = false;
+ var half = false;
+ var longLong = false;
+ if (format[formatIndex] == 'l') {
+ long_ = true;
+ formatIndex++;
+ if (format[formatIndex] == 'l') {
+ longLong = true;
+ formatIndex++;
+ }
+ } else if (format[formatIndex] == 'h') {
+ half = true;
+ formatIndex++;
+ }
+ var type = format[formatIndex];
+ formatIndex++;
+ var curr = 0;
+ var buffer = [];
+ // Read characters according to the format. floats are trickier, they may be in an unfloat state in the middle, then be a valid float later
+ if (type == 'f' || type == 'e' || type == 'g' ||
+ type == 'F' || type == 'E' || type == 'G') {
+ var last = 0;
+ next = get();
+ while (next > 0) {
+ buffer.push(String.fromCharCode(next));
+ if (__isFloat(buffer.join(''))) {
+ last = buffer.length;
+ }
+ next = get();
+ }
+ for (var i = 0; i < buffer.length - last + 1; i++) {
+ unget();
+ }
+ buffer.length = last;
+ } else {
+ next = get();
+ var first = true;
+ while ((curr < max_ || isNaN(max_)) && next > 0) {
+ if (!(next in __scanString.whiteSpace) && // stop on whitespace
+ (type == 's' ||
+ ((type === 'd' || type == 'u' || type == 'i') && ((next >= 48 && next <= 57) ||
+ (first && next == 45))) ||
+ ((type === 'x' || type === 'X') && (next >= 48 && next <= 57 ||
+ next >= 97 && next <= 102 ||
+ next >= 65 && next <= 70))) &&
+ (formatIndex >= format.length || next !== format[formatIndex].charCodeAt(0))) { // Stop when we read something that is coming up
+ buffer.push(String.fromCharCode(next));
+ next = get();
+ curr++;
+ first = false;
+ } else {
+ break;
+ }
+ }
+ unget();
+ }
+ if (buffer.length === 0) return 0; // Failure.
+ if (suppressAssignment) continue;
+ var text = buffer.join('');
+ var argPtr = HEAP32[(((varargs)+(argIndex))>>2)];
+ argIndex += Runtime.getAlignSize('void*', null, true);
+ switch (type) {
+ case 'd': case 'u': case 'i':
+ if (half) {
+ HEAP16[((argPtr)>>1)]=parseInt(text, 10);
+ } else if (longLong) {
+ (tempI64 = [parseInt(text, 10)>>>0,Math.min(Math.floor((parseInt(text, 10))/(+(4294967296))), (+(4294967295)))>>>0],HEAP32[((argPtr)>>2)]=tempI64[0],HEAP32[(((argPtr)+(4))>>2)]=tempI64[1]);
+ } else {
+ HEAP32[((argPtr)>>2)]=parseInt(text, 10);
+ }
+ break;
+ case 'X':
+ case 'x':
+ HEAP32[((argPtr)>>2)]=parseInt(text, 16)
+ break;
+ case 'F':
+ case 'f':
+ case 'E':
+ case 'e':
+ case 'G':
+ case 'g':
+ case 'E':
+ // fallthrough intended
+ if (long_) {
+ HEAPF64[((argPtr)>>3)]=parseFloat(text)
+ } else {
+ HEAPF32[((argPtr)>>2)]=parseFloat(text)
+ }
+ break;
+ case 's':
+ var array = intArrayFromString(text);
+ for (var j = 0; j < array.length; j++) {
+ HEAP8[(((argPtr)+(j))|0)]=array[j]
+ }
+ break;
+ }
+ fields++;
+ } else if (format[formatIndex] in __scanString.whiteSpace) {
+ next = get();
+ while (next in __scanString.whiteSpace) {
+ if (next <= 0) break mainLoop; // End of input.
+ next = get();
+ }
+ unget(next);
+ formatIndex++;
+ } else {
+ // Not a specifier.
+ next = get();
+ if (format[formatIndex].charCodeAt(0) !== next) {
+ unget(next);
+ break mainLoop;
+ }
+ formatIndex++;
+ }
+ }
+ return fields;
+ }function _sscanf(s, format, varargs) {
+ // int sscanf(const char *restrict s, const char *restrict format, ... );
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html
+ var index = 0;
+ var get = function() { return HEAP8[(((s)+(index++))|0)]; };
+ var unget = function() { index--; };
+ return __scanString(format, get, unget, varargs);
+ }
+ var _atan2=Math.atan2;
+ var ___errno=___errno_location;
+ function _putchar(c) {
+ // int putchar(int c);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/putchar.html
+ return _fputc(c, HEAP32[((_stdout)>>2)]);
+ }
+ Module["_saveSetjmp"] = _saveSetjmp;
+ Module["_testSetjmp"] = _testSetjmp;var _setjmp=undefined;
+ function _signal(sig, func) {
+ // TODO
+ return 0;
+ }
+ function _toupper(chr) {
+ if (chr >= 97 && chr <= 122) {
+ return chr - 97 + 65;
+ } else {
+ return chr;
+ }
+ }
+ function _longjmp(env, value) {
+ asm['setThrew'](env, value || 1);
+ throw 'longjmp';
+ }
+ function _time(ptr) {
+ var ret = Math.floor(Date.now()/1000);
+ if (ptr) {
+ HEAP32[((ptr)>>2)]=ret
+ }
+ return ret;
+ }
+ var ___tm_struct_layout={__size__:44,tm_sec:0,tm_min:4,tm_hour:8,tm_mday:12,tm_mon:16,tm_year:20,tm_wday:24,tm_yday:28,tm_isdst:32,tm_gmtoff:36,tm_zone:40};
+ var ___tm_current=allocate(4*26, "i8", ALLOC_STATIC);
+ var ___tm_timezones={};
+ var __tzname=allocate(8, "i32*", ALLOC_STATIC);
+ var __daylight=allocate(1, "i32*", ALLOC_STATIC);
+ var __timezone=allocate(1, "i32*", ALLOC_STATIC);function _tzset() {
+ // TODO: Use (malleable) environment variables instead of system settings.
+ if (_tzset.called) return;
+ _tzset.called = true;
+ HEAP32[((__timezone)>>2)]=-(new Date()).getTimezoneOffset() * 60
+ var winter = new Date(2000, 0, 1);
+ var summer = new Date(2000, 6, 1);
+ HEAP32[((__daylight)>>2)]=Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())
+ var winterName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | winter.toString().match(/\(([A-Z]+)\)/)[1];
+ var summerName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | summer.toString().match(/\(([A-Z]+)\)/)[1];
+ var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL);
+ var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL);
+ HEAP32[((__tzname)>>2)]=winterNamePtr
+ HEAP32[(((__tzname)+(4))>>2)]=summerNamePtr
+ }function _localtime_r(time, tmPtr) {
+ _tzset();
+ var offsets = ___tm_struct_layout;
+ var date = new Date(HEAP32[((time)>>2)]*1000);
+ HEAP32[(((tmPtr)+(offsets.tm_sec))>>2)]=date.getSeconds()
+ HEAP32[(((tmPtr)+(offsets.tm_min))>>2)]=date.getMinutes()
+ HEAP32[(((tmPtr)+(offsets.tm_hour))>>2)]=date.getHours()
+ HEAP32[(((tmPtr)+(offsets.tm_mday))>>2)]=date.getDate()
+ HEAP32[(((tmPtr)+(offsets.tm_mon))>>2)]=date.getMonth()
+ HEAP32[(((tmPtr)+(offsets.tm_year))>>2)]=date.getFullYear()-1900
+ HEAP32[(((tmPtr)+(offsets.tm_wday))>>2)]=date.getDay()
+ var start = new Date(date.getFullYear(), 0, 1);
+ var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
+ HEAP32[(((tmPtr)+(offsets.tm_yday))>>2)]=yday
+ HEAP32[(((tmPtr)+(offsets.tm_gmtoff))>>2)]=start.getTimezoneOffset() * 60
+ var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset());
+ HEAP32[(((tmPtr)+(offsets.tm_isdst))>>2)]=dst
+ var timezone = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | date.toString().match(/\(([A-Z]+)\)/)[1];
+ if (!(timezone in ___tm_timezones)) {
+ ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL);
+ }
+ HEAP32[(((tmPtr)+(offsets.tm_zone))>>2)]=___tm_timezones[timezone]
+ return tmPtr;
+ }function _localtime(time) {
+ return _localtime_r(time, ___tm_current);
+ }
+ var ___tm_formatted=allocate(4*26, "i8", ALLOC_STATIC);
+ function _mktime(tmPtr) {
+ _tzset();
+ var offsets = ___tm_struct_layout;
+ var year = HEAP32[(((tmPtr)+(offsets.tm_year))>>2)];
+ var timestamp = new Date(year >= 1900 ? year : year + 1900,
+ HEAP32[(((tmPtr)+(offsets.tm_mon))>>2)],
+ HEAP32[(((tmPtr)+(offsets.tm_mday))>>2)],
+ HEAP32[(((tmPtr)+(offsets.tm_hour))>>2)],
+ HEAP32[(((tmPtr)+(offsets.tm_min))>>2)],
+ HEAP32[(((tmPtr)+(offsets.tm_sec))>>2)],
+ 0).getTime() / 1000;
+ HEAP32[(((tmPtr)+(offsets.tm_wday))>>2)]=new Date(timestamp).getDay()
+ var yday = Math.round((timestamp - (new Date(year, 0, 1)).getTime()) / (1000 * 60 * 60 * 24));
+ HEAP32[(((tmPtr)+(offsets.tm_yday))>>2)]=yday
+ return timestamp;
+ }function _asctime_r(tmPtr, buf) {
+ var date = new Date(_mktime(tmPtr)*1000);
+ var formatted = date.toString();
+ var datePart = formatted.replace(/\d{4}.*/, '').replace(/ 0/, ' ');
+ var timePart = formatted.match(/\d{2}:\d{2}:\d{2}/)[0];
+ formatted = datePart + timePart + ' ' + date.getFullYear() + '\n';
+ formatted.split('').forEach(function(chr, index) {
+ HEAP8[(((buf)+(index))|0)]=chr.charCodeAt(0)
+ });
+ HEAP8[(((buf)+(25))|0)]=0
+ return buf;
+ }function _asctime(tmPtr) {
+ return _asctime_r(tmPtr, ___tm_formatted);
+ }function _ctime(timer) {
+ return _asctime(_localtime(timer));
+ }
+ var _llvm_va_start=undefined;
+ function _vfprintf(s, f, va_arg) {
+ return _fprintf(s, f, HEAP32[((va_arg)>>2)]);
+ }
+ function _llvm_va_end() {}
+ function _rename(old, new_) {
+ // int rename(const char *old, const char *new);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/rename.html
+ var oldObj = FS.analyzePath(Pointer_stringify(old));
+ var newObj = FS.analyzePath(Pointer_stringify(new_));
+ if (newObj.path == oldObj.path) {
+ return 0;
+ } else if (!oldObj.exists) {
+ ___setErrNo(oldObj.error);
+ return -1;
+ } else if (oldObj.isRoot || oldObj.path == FS.currentPath) {
+ ___setErrNo(ERRNO_CODES.EBUSY);
+ return -1;
+ } else if (newObj.parentPath &&
+ newObj.parentPath.indexOf(oldObj.path) == 0) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
+ } else if (newObj.exists && newObj.object.isFolder) {
+ ___setErrNo(ERRNO_CODES.EISDIR);
+ return -1;
+ } else {
+ delete oldObj.parentObject.contents[oldObj.name];
+ newObj.parentObject.contents[newObj.name] = oldObj.object;
+ return 0;
+ }
+ }
+ function _strftime(s, maxsize, format, timeptr) {
+ // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html
+ // TODO: Implement.
+ return 0;
+ }
+ function _qsort(base, num, size, cmp) {
+ if (num == 0 || size == 0) return;
+ // forward calls to the JavaScript sort method
+ // first, sort the items logically
+ var comparator = function(x, y) {
+ return Runtime.dynCall('iii', cmp, [x, y]);
+ }
+ var keys = [];
+ for (var i = 0; i < num; i++) keys.push(i);
+ keys.sort(function(a, b) {
+ return comparator(base+a*size, base+b*size);
+ });
+ // apply the sort
+ var temp = _malloc(num*size);
+ _memcpy(temp, base, num*size);
+ for (var i = 0; i < num; i++) {
+ if (keys[i] == i) continue; // already in place
+ _memcpy(base+i*size, temp+keys[i]*size, size);
+ }
+ _free(temp);
+ }
+ function _gettimeofday(ptr) {
+ // %struct.timeval = type { i32, i32 }
+ var now = Date.now();
+ HEAP32[((ptr)>>2)]=Math.floor(now/1000); // seconds
+ HEAP32[(((ptr)+(4))>>2)]=Math.floor((now-1000*Math.floor(now/1000))*1000); // microseconds
+ return 0;
+ }
+ function _opendir(dirname) {
+ // DIR *opendir(const char *dirname);
+ // http://pubs.opengroup.org/onlinepubs/007908799/xsh/opendir.html
+ // NOTE: Calculating absolute path redundantly since we need to associate it
+ // with the opened stream.
+ var path = FS.absolutePath(Pointer_stringify(dirname));
+ if (path === null) {
+ ___setErrNo(ERRNO_CODES.ENOENT);
+ return 0;
+ }
+ var target = FS.findObject(path);
+ if (target === null) return 0;
+ if (!target.isFolder) {
+ ___setErrNo(ERRNO_CODES.ENOTDIR);
+ return 0;
+ } else if (!target.read) {
+ ___setErrNo(ERRNO_CODES.EACCES);
+ return 0;
+ }
+ var contents = [];
+ for (var key in target.contents) contents.push(key);
+ var id = FS.createFileHandle({
+ path: path,
+ object: target,
+ // An index into contents. Special values: -2 is ".", -1 is "..".
+ position: -2,
+ isRead: true,
+ isWrite: false,
+ isAppend: false,
+ error: false,
+ eof: false,
+ ungotten: [],
+ // Folder-specific properties:
+ // Remember the contents at the time of opening in an array, so we can
+ // seek between them relying on a single order.
+ contents: contents,
+ // Each stream has its own area for readdir() returns.
+ currentEntry: _malloc(___dirent_struct_layout.__size__)
+ });
+ return id;
+ }
+ function _readdir_r(dirp, entry, result) {
+ // int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
+ // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html
+ if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
+ return ___setErrNo(ERRNO_CODES.EBADF);
+ }
+ var stream = FS.streams[dirp];
+ var loc = stream.position;
+ var entries = 0;
+ for (var key in stream.contents) entries++;
+ if (loc < -2 || loc >= entries) {
+ HEAP32[((result)>>2)]=0
+ } else {
+ var name, inode, type;
+ if (loc === -2) {
+ name = '.';
+ inode = 1; // Really undefined.
+ type = 4; //DT_DIR
+ } else if (loc === -1) {
+ name = '..';
+ inode = 1; // Really undefined.
+ type = 4; //DT_DIR
+ } else {
+ var object;
+ name = stream.contents[loc];
+ object = stream.object.contents[name];
+ inode = object.inodeNumber;
+ type = object.isDevice ? 2 // DT_CHR, character device.
+ : object.isFolder ? 4 // DT_DIR, directory.
+ : object.link !== undefined ? 10 // DT_LNK, symbolic link.
+ : 8; // DT_REG, regular file.
+ }
+ stream.position++;
+ var offsets = ___dirent_struct_layout;
+ HEAP32[(((entry)+(offsets.d_ino))>>2)]=inode
+ HEAP32[(((entry)+(offsets.d_off))>>2)]=stream.position
+ HEAP32[(((entry)+(offsets.d_reclen))>>2)]=name.length + 1
+ for (var i = 0; i < name.length; i++) {
+ HEAP8[(((entry + offsets.d_name)+(i))|0)]=name.charCodeAt(i)
+ }
+ HEAP8[(((entry + offsets.d_name)+(i))|0)]=0
+ HEAP8[(((entry)+(offsets.d_type))|0)]=type
+ HEAP32[((result)>>2)]=entry
+ }
+ return 0;
+ }function _readdir(dirp) {
+ // struct dirent *readdir(DIR *dirp);
+ // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html
+ if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return 0;
+ } else {
+ if (!_readdir.result) _readdir.result = _malloc(4);
+ _readdir_r(dirp, FS.streams[dirp].currentEntry, _readdir.result);
+ if (HEAP32[((_readdir.result)>>2)] === 0) {
+ return 0;
+ } else {
+ return FS.streams[dirp].currentEntry;
+ }
+ }
+ }
+ function _closedir(dirp) {
+ // int closedir(DIR *dirp);
+ // http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html
+ if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
+ return ___setErrNo(ERRNO_CODES.EBADF);
+ } else {
+ _free(FS.streams[dirp].currentEntry);
+ FS.streams[dirp] = null;
+ return 0;
+ }
+ }
+ function _ungetc(c, stream) {
+ // int ungetc(int c, FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/ungetc.html
+ if (FS.streams[stream]) {
+ c = unSign(c & 0xFF);
+ FS.streams[stream].ungotten.push(c);
+ return c;
+ } else {
+ return -1;
+ }
+ }
+ function _fmod(x, y) {
+ return x % y;
+ }
+ function _gmtime_r(time, tmPtr) {
+ var date = new Date(HEAP32[((time)>>2)]*1000);
+ var offsets = ___tm_struct_layout;
+ HEAP32[(((tmPtr)+(offsets.tm_sec))>>2)]=date.getUTCSeconds()
+ HEAP32[(((tmPtr)+(offsets.tm_min))>>2)]=date.getUTCMinutes()
+ HEAP32[(((tmPtr)+(offsets.tm_hour))>>2)]=date.getUTCHours()
+ HEAP32[(((tmPtr)+(offsets.tm_mday))>>2)]=date.getUTCDate()
+ HEAP32[(((tmPtr)+(offsets.tm_mon))>>2)]=date.getUTCMonth()
+ HEAP32[(((tmPtr)+(offsets.tm_year))>>2)]=date.getUTCFullYear()-1900
+ HEAP32[(((tmPtr)+(offsets.tm_wday))>>2)]=date.getUTCDay()
+ HEAP32[(((tmPtr)+(offsets.tm_gmtoff))>>2)]=0
+ HEAP32[(((tmPtr)+(offsets.tm_isdst))>>2)]=0
+ var start = new Date(date.getFullYear(), 0, 1);
+ var yday = Math.round((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
+ HEAP32[(((tmPtr)+(offsets.tm_yday))>>2)]=yday
+ var timezone = "GMT";
+ if (!(timezone in ___tm_timezones)) {
+ ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL);
+ }
+ HEAP32[(((tmPtr)+(offsets.tm_zone))>>2)]=___tm_timezones[timezone]
+ return tmPtr;
+ }function _gmtime(time) {
+ return _gmtime_r(time, ___tm_current);
+ }
+ function _setvbuf(stream, buf, type, size) {
+ // int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/setvbuf.html
+ // TODO: Implement custom buffering.
+ return 0;
+ }function _setbuf(stream, buf) {
+ // void setbuf(FILE *restrict stream, char *restrict buf);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/setbuf.html
+ if (buf) _setvbuf(stream, buf, 0, 8192); // _IOFBF, BUFSIZ.
+ else _setvbuf(stream, buf, 2, 8192); // _IONBF, BUFSIZ.
+ }
+ function _atexit(func, arg) {
+ __ATEXIT__.unshift({ func: func, arg: arg });
+ }
+ function _isatty(fildes) {
+ // int isatty(int fildes);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/isatty.html
+ if (!FS.streams[fildes]) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return 0;
+ }
+ if (FS.streams[fildes].isTerminal) return 1;
+ ___setErrNo(ERRNO_CODES.ENOTTY);
+ return 0;
+ }
+ var ___utsname_struct_layout={__size__:160,sysname:0,nodename:32,release:64,version:96,machine:128};function _uname(name) {
+ // int uname(struct utsname *name);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/uname.html
+ function copyString(element, value) {
+ var offset = ___utsname_struct_layout[element];
+ for (var i = 0; i < value.length; i++) {
+ HEAP8[(((name)+(offset + i))|0)]=value.charCodeAt(i)
+ }
+ HEAP8[(((name)+(offset + i))|0)]=0
+ }
+ if (name === 0) {
+ return -1;
+ } else {
+ copyString('sysname', 'Emscripten');
+ copyString('nodename', 'emscripten');
+ copyString('release', '1.0');
+ copyString('version', '#1');
+ copyString('machine', 'x86-JS');
+ return 0;
+ }
+ }
+ function _nl_langinfo(item) {
+ // char *nl_langinfo(nl_item item);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/nl_langinfo.html
+ var result;
+ switch (item) {
+ case 0:
+ result = 'ANSI_X3.4-1968';
+ break;
+ case 1:
+ result = '%a %b %e %H:%M:%S %Y';
+ break;
+ case 2:
+ result = '%m/%d/%y';
+ break;
+ case 3:
+ result = '%H:%M:%S';
+ break;
+ case 4:
+ result = '%I:%M:%S %p';
+ break;
+ case 5:
+ result = 'AM';
+ break;
+ case 6:
+ result = 'PM';
+ break;
+ case 7:
+ result = 'Sunday';
+ break;
+ case 8:
+ result = 'Monday';
+ break;
+ case 9:
+ result = 'Tuesday';
+ break;
+ case 10:
+ result = 'Wednesday';
+ break;
+ case 11:
+ result = 'Thursday';
+ break;
+ case 12:
+ result = 'Friday';
+ break;
+ case 13:
+ result = 'Saturday';
+ break;
+ case 14:
+ result = 'Sun';
+ break;
+ case 15:
+ result = 'Mon';
+ break;
+ case 16:
+ result = 'Tue';
+ break;
+ case 17:
+ result = 'Wed';
+ break;
+ case 18:
+ result = 'Thu';
+ break;
+ case 19:
+ result = 'Fri';
+ break;
+ case 20:
+ result = 'Sat';
+ break;
+ case 21:
+ result = 'January';
+ break;
+ case 22:
+ result = 'February';
+ break;
+ case 23:
+ result = 'March';
+ break;
+ case 24:
+ result = 'April';
+ break;
+ case 25:
+ result = 'May';
+ break;
+ case 26:
+ result = 'June';
+ break;
+ case 27:
+ result = 'July';
+ break;
+ case 28:
+ result = 'August';
+ break;
+ case 29:
+ result = 'September';
+ break;
+ case 30:
+ result = 'October';
+ break;
+ case 31:
+ result = 'November';
+ break;
+ case 32:
+ result = 'December';
+ break;
+ case 33:
+ result = 'Jan';
+ break;
+ case 34:
+ result = 'Feb';
+ break;
+ case 35:
+ result = 'Mar';
+ break;
+ case 36:
+ result = 'Apr';
+ break;
+ case 37:
+ result = 'May';
+ break;
+ case 38:
+ result = 'Jun';
+ break;
+ case 39:
+ result = 'Jul';
+ break;
+ case 40:
+ result = 'Aug';
+ break;
+ case 41:
+ result = 'Sep';
+ break;
+ case 42:
+ result = 'Oct';
+ break;
+ case 43:
+ result = 'Nov';
+ break;
+ case 44:
+ result = 'Dec';
+ break;
+ case 49:
+ result = '';
+ break;
+ case 50:
+ result = '.';
+ break;
+ case 51:
+ result = '';
+ break;
+ case 52:
+ result = '^[yY]';
+ break;
+ case 53:
+ result = '^[nN]';
+ break;
+ case 56:
+ result = '-';
+ break;
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ default:
+ result = '';
+ break;
+ }
+ var me = _nl_langinfo;
+ if (!me.ret) me.ret = _malloc(32);
+ for (var i = 0; i < result.length; i++) {
+ HEAP8[(((me.ret)+(i))|0)]=result.charCodeAt(i)
+ }
+ HEAP8[(((me.ret)+(i))|0)]=0
+ return me.ret;
+ }
+ function _iconv_open() {
+ Module['printErr']('missing function: iconv_open'); abort(-1);
+ }
+ function _iconv() {
+ Module['printErr']('missing function: iconv'); abort(-1);
+ }
+ function _iconv_close() {
+ Module['printErr']('missing function: iconv_close'); abort(-1);
+ }
+ function _localeconv() {
+ // %struct.timeval = type { char* decimal point, other stuff... }
+ // var indexes = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] });
+ var me = _localeconv;
+ if (!me.ret) {
+ me.ret = allocate([allocate(intArrayFromString('.'), 'i8', ALLOC_NORMAL)], 'i8*', ALLOC_NORMAL); // just decimal point, for now
+ }
+ return me.ret;
+ }
+ function _isprint(chr) {
+ return 0x1F < chr && chr < 0x7F;
+ }
+ function _erfc(x) {
+ var MATH_TOLERANCE = 1E-12;
+ var ONE_SQRTPI = 0.564189583547756287;
+ var a = 1;
+ var b = x;
+ var c = x;
+ var d = x * x + 0.5;
+ var n = 1.0;
+ var q2 = b / d;
+ var q1, t;
+ if (Math.abs(x) < 2.2) {
+ return 1.0 - _erf(x);
+ }
+ if (x < 0) {
+ return 2.0 - _erfc(-x);
+ }
+ do {
+ t = a * n + b * x;
+ a = b;
+ b = t;
+ t = c * n + d * x;
+ c = d;
+ d = t;
+ n += 0.5;
+ q1 = q2;
+ q2 = b / d;
+ } while (Math.abs(q1 - q2) / q2 > MATH_TOLERANCE);
+ return (ONE_SQRTPI * Math.exp(- x * x) * q2);
+ }function _erf(x) {
+ var MATH_TOLERANCE = 1E-12;
+ var TWO_SQRTPI = 1.128379167095512574;
+ var sum = x;
+ var term = x;
+ var xsqr = x*x;
+ var j = 1;
+ if (Math.abs(x) > 2.2) {
+ return 1.0 - _erfc(x);
+ }
+ do {
+ term *= xsqr / j;
+ sum -= term / (2 * j + 1);
+ ++j;
+ term *= xsqr / j;
+ sum += term / (2 * j + 1);
+ ++j;
+ } while (Math.abs(term / sum) > MATH_TOLERANCE);
+ return (TWO_SQRTPI * sum);
+ }
+ function _lgamma() {
+ Module['printErr']('missing function: lgamma'); abort(-1);
+ }
+ function ___signgam() {
+ Module['printErr']('missing function: __signgam'); abort(-1);
+ }
+ function _modf(x, intpart) {
+ HEAPF64[((intpart)>>3)]=Math.floor(x)
+ return x - HEAPF64[((intpart)>>3)];
+ }
+ function _cosh(x) {
+ var p = Math.pow(Math.E, x);
+ return (p + (1 / p)) / 2;
+ }
+ function _sinh(x) {
+ var p = Math.pow(Math.E, x);
+ return (p - (1 / p)) / 2;
+ }
+ var _tan=Math.tan;
+ var _asin=Math.asin;
+ var _acos=Math.acos;
+ var _atan=Math.atan;
+ function _sleep(seconds) {
+ // unsigned sleep(unsigned seconds);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/sleep.html
+ return _usleep(seconds * 1e6);
+ }
+ function _strdup(ptr) {
+ var len = _strlen(ptr);
+ var newStr = _malloc(len + 1);
+ _memcpy(newStr, ptr, len);
+ HEAP8[(((newStr)+(len))|0)]=0;
+ return newStr;
+ }
+ function _vsnprintf(s, n, format, va_arg) {
+ return _snprintf(s, n, format, HEAP32[((va_arg)>>2)]);
+ }
+ function _abort() {
+ ABORT = true;
+ throw 'abort() at ' + (new Error().stack);
+ }
+ function _sysconf(name) {
+ // long sysconf(int name);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/sysconf.html
+ switch(name) {
+ case 8: return PAGE_SIZE;
+ case 54:
+ case 56:
+ case 21:
+ case 61:
+ case 63:
+ case 22:
+ case 67:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 69:
+ case 28:
+ case 101:
+ case 70:
+ case 71:
+ case 29:
+ case 30:
+ case 199:
+ case 75:
+ case 76:
+ case 32:
+ case 43:
+ case 44:
+ case 80:
+ case 46:
+ case 47:
+ case 45:
+ case 48:
+ case 49:
+ case 42:
+ case 82:
+ case 33:
+ case 7:
+ case 108:
+ case 109:
+ case 107:
+ case 112:
+ case 119:
+ case 121:
+ return 200809;
+ case 13:
+ case 104:
+ case 94:
+ case 95:
+ case 34:
+ case 35:
+ case 77:
+ case 81:
+ case 83:
+ case 84:
+ case 85:
+ case 86:
+ case 87:
+ case 88:
+ case 89:
+ case 90:
+ case 91:
+ case 94:
+ case 95:
+ case 110:
+ case 111:
+ case 113:
+ case 114:
+ case 115:
+ case 116:
+ case 117:
+ case 118:
+ case 120:
+ case 40:
+ case 16:
+ case 79:
+ case 19:
+ return -1;
+ case 92:
+ case 93:
+ case 5:
+ case 72:
+ case 6:
+ case 74:
+ case 92:
+ case 93:
+ case 96:
+ case 97:
+ case 98:
+ case 99:
+ case 102:
+ case 103:
+ case 105:
+ return 1;
+ case 38:
+ case 66:
+ case 50:
+ case 51:
+ case 4:
+ return 1024;
+ case 15:
+ case 64:
+ case 41:
+ return 32;
+ case 55:
+ case 37:
+ case 17:
+ return 2147483647;
+ case 18:
+ case 1:
+ return 47839;
+ case 59:
+ case 57:
+ return 99;
+ case 68:
+ case 58:
+ return 2048;
+ case 0: return 2097152;
+ case 3: return 65536;
+ case 14: return 32768;
+ case 73: return 32767;
+ case 39: return 16384;
+ case 60: return 1000;
+ case 106: return 700;
+ case 52: return 256;
+ case 62: return 255;
+ case 2: return 100;
+ case 65: return 64;
+ case 36: return 20;
+ case 100: return 16;
+ case 20: return 6;
+ case 53: return 4;
+ case 10: return 1;
+ }
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
+ }
+ function _sbrk(bytes) {
+ // Implement a Linux-like 'memory area' for our 'process'.
+ // Changes the size of the memory area by |bytes|; returns the
+ // address of the previous top ('break') of the memory area
+ // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP
+ var self = _sbrk;
+ if (!self.called) {
+ DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned
+ self.called = true;
+ assert(Runtime.dynamicAlloc);
+ self.alloc = Runtime.dynamicAlloc;
+ Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') };
+ }
+ var ret = DYNAMICTOP;
+ if (bytes != 0) self.alloc(bytes);
+ return ret; // Previous break location.
+ }
+ function _llvm_lifetime_start() {}
+ function _llvm_lifetime_end() {}
+ var _llvm_memset_p0i8_i64=_memset;
+ var _llvm_memcpy_p0i8_p0i8_i64=_memcpy;
+ function _llvm_trap() {
+ throw 'trap! ' + new Error().stack;
+ }
+ var Browser={mainLoop:{scheduler:null,shouldPause:false,paused:false,queue:[],pause:function () {
+ Browser.mainLoop.shouldPause = true;
+ },resume:function () {
+ if (Browser.mainLoop.paused) {
+ Browser.mainLoop.paused = false;
+ Browser.mainLoop.scheduler();
+ }
+ Browser.mainLoop.shouldPause = false;
+ },updateStatus:function () {
+ if (Module['setStatus']) {
+ var message = Module['statusMessage'] || 'Please wait...';
+ var remaining = Browser.mainLoop.remainingBlockers;
+ var expected = Browser.mainLoop.expectedBlockers;
+ if (remaining) {
+ if (remaining < expected) {
+ Module['setStatus'](message + ' (' + (expected - remaining) + '/' + expected + ')');
+ } else {
+ Module['setStatus'](message);
+ }
+ } else {
+ Module['setStatus']('');
+ }
+ }
+ }},isFullScreen:false,pointerLock:false,moduleContextCreatedCallbacks:[],workers:[],init:function () {
+ if (Browser.initted) return;
+ Browser.initted = true;
+ try {
+ new Blob();
+ Browser.hasBlobConstructor = true;
+ } catch(e) {
+ Browser.hasBlobConstructor = false;
+ console.log("warning: no blob constructor, cannot create blobs with mimetypes");
+ }
+ Browser.BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : (!Browser.hasBlobConstructor ? console.log("warning: no BlobBuilder") : null));
+ Browser.URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");
+ // Support for plugins that can process preloaded files. You can add more of these to
+ // your app by creating and appending to Module.preloadPlugins.
+ //
+ // Each plugin is asked if it can handle a file based on the file's name. If it can,
+ // it is given the file's raw data. When it is done, it calls a callback with the file's
+ // (possibly modified) data. For example, a plugin might decompress a file, or it
+ // might create some side data structure for use later (like an Image element, etc.).
+ function getMimetype(name) {
+ return {
+ 'jpg': 'image/jpeg',
+ 'jpeg': 'image/jpeg',
+ 'png': 'image/png',
+ 'bmp': 'image/bmp',
+ 'ogg': 'audio/ogg',
+ 'wav': 'audio/wav',
+ 'mp3': 'audio/mpeg'
+ }[name.substr(name.lastIndexOf('.')+1)];
+ }
+ if (!Module["preloadPlugins"]) Module["preloadPlugins"] = [];
+ var imagePlugin = {};
+ imagePlugin['canHandle'] = function(name) {
+ return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/.exec(name);
+ };
+ imagePlugin['handle'] = function(byteArray, name, onload, onerror) {
+ var b = null;
+ if (Browser.hasBlobConstructor) {
+ try {
+ b = new Blob([byteArray], { type: getMimetype(name) });
+ } catch(e) {
+ Runtime.warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder');
+ }
+ }
+ if (!b) {
+ var bb = new Browser.BlobBuilder();
+ bb.append((new Uint8Array(byteArray)).buffer); // we need to pass a buffer, and must copy the array to get the right data range
+ b = bb.getBlob();
+ }
+ var url = Browser.URLObject.createObjectURL(b);
+ var img = new Image();
+ img.onload = function() {
+ assert(img.complete, 'Image ' + name + ' could not be decoded');
+ var canvas = document.createElement('canvas');
+ canvas.width = img.width;
+ canvas.height = img.height;
+ var ctx = canvas.getContext('2d');
+ ctx.drawImage(img, 0, 0);
+ Module["preloadedImages"][name] = canvas;
+ Browser.URLObject.revokeObjectURL(url);
+ if (onload) onload(byteArray);
+ };
+ img.onerror = function(event) {
+ console.log('Image ' + url + ' could not be decoded');
+ if (onerror) onerror();
+ };
+ img.src = url;
+ };
+ Module['preloadPlugins'].push(imagePlugin);
+ var audioPlugin = {};
+ audioPlugin['canHandle'] = function(name) {
+ return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
+ };
+ audioPlugin['handle'] = function(byteArray, name, onload, onerror) {
+ var done = false;
+ function finish(audio) {
+ if (done) return;
+ done = true;
+ Module["preloadedAudios"][name] = audio;
+ if (onload) onload(byteArray);
+ }
+ function fail() {
+ if (done) return;
+ done = true;
+ Module["preloadedAudios"][name] = new Audio(); // empty shim
+ if (onerror) onerror();
+ }
+ if (Browser.hasBlobConstructor) {
+ try {
+ var b = new Blob([byteArray], { type: getMimetype(name) });
+ } catch(e) {
+ return fail();
+ }
+ var url = Browser.URLObject.createObjectURL(b); // XXX we never revoke this!
+ var audio = new Audio();
+ audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926
+ audio.onerror = function(event) {
+ if (done) return;
+ console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach');
+ function encode64(data) {
+ var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ var PAD = '=';
+ var ret = '';
+ var leftchar = 0;
+ var leftbits = 0;
+ for (var i = 0; i < data.length; i++) {
+ leftchar = (leftchar << 8) | data[i];
+ leftbits += 8;
+ while (leftbits >= 6) {
+ var curr = (leftchar >> (leftbits-6)) & 0x3f;
+ leftbits -= 6;
+ ret += BASE[curr];
+ }
+ }
+ if (leftbits == 2) {
+ ret += BASE[(leftchar&3) << 4];
+ ret += PAD + PAD;
+ } else if (leftbits == 4) {
+ ret += BASE[(leftchar&0xf) << 2];
+ ret += PAD;
+ }
+ return ret;
+ }
+ audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray);
+ finish(audio); // we don't wait for confirmation this worked - but it's worth trying
+ };
+ audio.src = url;
+ // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror
+ Browser.safeSetTimeout(function() {
+ finish(audio); // try to use it even though it is not necessarily ready to play
+ }, 10000);
+ } else {
+ return fail();
+ }
+ };
+ Module['preloadPlugins'].push(audioPlugin);
+ // Canvas event setup
+ var canvas = Module['canvas'];
+ canvas.requestPointerLock = canvas['requestPointerLock'] ||
+ canvas['mozRequestPointerLock'] ||
+ canvas['webkitRequestPointerLock'];
+ canvas.exitPointerLock = document['exitPointerLock'] ||
+ document['mozExitPointerLock'] ||
+ document['webkitExitPointerLock'] ||
+ function(){}; // no-op if function does not exist
+ canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
+ function pointerLockChange() {
+ Browser.pointerLock = document['pointerLockElement'] === canvas ||
+ document['mozPointerLockElement'] === canvas ||
+ document['webkitPointerLockElement'] === canvas;
+ }
+ document.addEventListener('pointerlockchange', pointerLockChange, false);
+ document.addEventListener('mozpointerlockchange', pointerLockChange, false);
+ document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
+ if (Module['elementPointerLock']) {
+ canvas.addEventListener("click", function(ev) {
+ if (!Browser.pointerLock && canvas.requestPointerLock) {
+ canvas.requestPointerLock();
+ ev.preventDefault();
+ }
+ }, false);
+ }
+ },createContext:function (canvas, useWebGL, setInModule) {
+ var ctx;
+ try {
+ if (useWebGL) {
+ ctx = canvas.getContext('experimental-webgl', {
+ alpha: false
+ });
+ } else {
+ ctx = canvas.getContext('2d');
+ }
+ if (!ctx) throw ':(';
+ } catch (e) {
+ Module.print('Could not create canvas - ' + e);
+ return null;
+ }
+ if (useWebGL) {
+ // Set the background of the WebGL canvas to black
+ canvas.style.backgroundColor = "black";
+ // Warn on context loss
+ canvas.addEventListener('webglcontextlost', function(event) {
+ alert('WebGL context lost. You will need to reload the page.');
+ }, false);
+ }
+ if (setInModule) {
+ Module.ctx = ctx;
+ Module.useWebGL = useWebGL;
+ Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() });
+ Browser.init();
+ }
+ return ctx;
+ },destroyContext:function (canvas, useWebGL, setInModule) {},fullScreenHandlersInstalled:false,lockPointer:undefined,resizeCanvas:undefined,requestFullScreen:function (lockPointer, resizeCanvas) {
+ Browser.lockPointer = lockPointer;
+ Browser.resizeCanvas = resizeCanvas;
+ if (typeof Browser.lockPointer === 'undefined') Browser.lockPointer = true;
+ if (typeof Browser.resizeCanvas === 'undefined') Browser.resizeCanvas = false;
+ var canvas = Module['canvas'];
+ function fullScreenChange() {
+ Browser.isFullScreen = false;
+ if ((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] ||
+ document['mozFullScreenElement'] || document['mozFullscreenElement'] ||
+ document['fullScreenElement'] || document['fullscreenElement']) === canvas) {
+ canvas.cancelFullScreen = document['cancelFullScreen'] ||
+ document['mozCancelFullScreen'] ||
+ document['webkitCancelFullScreen'];
+ canvas.cancelFullScreen = canvas.cancelFullScreen.bind(document);
+ if (Browser.lockPointer) canvas.requestPointerLock();
+ Browser.isFullScreen = true;
+ if (Browser.resizeCanvas) Browser.setFullScreenCanvasSize();
+ } else if (Browser.resizeCanvas){
+ Browser.setWindowedCanvasSize();
+ }
+ if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullScreen);
+ }
+ if (!Browser.fullScreenHandlersInstalled) {
+ Browser.fullScreenHandlersInstalled = true;
+ document.addEventListener('fullscreenchange', fullScreenChange, false);
+ document.addEventListener('mozfullscreenchange', fullScreenChange, false);
+ document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
+ }
+ canvas.requestFullScreen = canvas['requestFullScreen'] ||
+ canvas['mozRequestFullScreen'] ||
+ (canvas['webkitRequestFullScreen'] ? function() { canvas['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null);
+ canvas.requestFullScreen();
+ },requestAnimationFrame:function (func) {
+ if (!window.requestAnimationFrame) {
+ window.requestAnimationFrame = window['requestAnimationFrame'] ||
+ window['mozRequestAnimationFrame'] ||
+ window['webkitRequestAnimationFrame'] ||
+ window['msRequestAnimationFrame'] ||
+ window['oRequestAnimationFrame'] ||
+ window['setTimeout'];
+ }
+ window.requestAnimationFrame(func);
+ },safeCallback:function (func) {
+ return function() {
+ if (!ABORT) return func.apply(null, arguments);
+ };
+ },safeRequestAnimationFrame:function (func) {
+ return Browser.requestAnimationFrame(function() {
+ if (!ABORT) func();
+ });
+ },safeSetTimeout:function (func, timeout) {
+ return setTimeout(function() {
+ if (!ABORT) func();
+ }, timeout);
+ },safeSetInterval:function (func, timeout) {
+ return setInterval(function() {
+ if (!ABORT) func();
+ }, timeout);
+ },getUserMedia:function (func) {
+ if(!window.getUserMedia) {
+ window.getUserMedia = navigator['getUserMedia'] ||
+ navigator['mozGetUserMedia'];
+ }
+ window.getUserMedia(func);
+ },getMovementX:function (event) {
+ return event['movementX'] ||
+ event['mozMovementX'] ||
+ event['webkitMovementX'] ||
+ 0;
+ },getMovementY:function (event) {
+ return event['movementY'] ||
+ event['mozMovementY'] ||
+ event['webkitMovementY'] ||
+ 0;
+ },mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,calculateMouseEvent:function (event) { // event should be mousemove, mousedown or mouseup
+ if (Browser.pointerLock) {
+ // When the pointer is locked, calculate the coordinates
+ // based on the movement of the mouse.
+ // Workaround for Firefox bug 764498
+ if (event.type != 'mousemove' &&
+ ('mozMovementX' in event)) {
+ Browser.mouseMovementX = Browser.mouseMovementY = 0;
+ } else {
+ Browser.mouseMovementX = Browser.getMovementX(event);
+ Browser.mouseMovementY = Browser.getMovementY(event);
+ }
+ Browser.mouseX = SDL.mouseX + Browser.mouseMovementX;
+ Browser.mouseY = SDL.mouseY + Browser.mouseMovementY;
+ } else {
+ // Otherwise, calculate the movement based on the changes
+ // in the coordinates.
+ var rect = Module["canvas"].getBoundingClientRect();
+ var x = event.pageX - (window.scrollX + rect.left);
+ var y = event.pageY - (window.scrollY + rect.top);
+ // the canvas might be CSS-scaled compared to its backbuffer;
+ // SDL-using content will want mouse coordinates in terms
+ // of backbuffer units.
+ var cw = Module["canvas"].width;
+ var ch = Module["canvas"].height;
+ x = x * (cw / rect.width);
+ y = y * (ch / rect.height);
+ Browser.mouseMovementX = x - Browser.mouseX;
+ Browser.mouseMovementY = y - Browser.mouseY;
+ Browser.mouseX = x;
+ Browser.mouseY = y;
+ }
+ },xhrLoad:function (url, onload, onerror) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = function() {
+ if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
+ onload(xhr.response);
+ } else {
+ onerror();
+ }
+ };
+ xhr.onerror = onerror;
+ xhr.send(null);
+ },asyncLoad:function (url, onload, onerror, noRunDep) {
+ Browser.xhrLoad(url, function(arrayBuffer) {
+ assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).');
+ onload(new Uint8Array(arrayBuffer));
+ if (!noRunDep) removeRunDependency('al ' + url);
+ }, function(event) {
+ if (onerror) {
+ onerror();
+ } else {
+ throw 'Loading data file "' + url + '" failed.';
+ }
+ });
+ if (!noRunDep) addRunDependency('al ' + url);
+ },resizeListeners:[],updateResizeListeners:function () {
+ var canvas = Module['canvas'];
+ Browser.resizeListeners.forEach(function(listener) {
+ listener(canvas.width, canvas.height);
+ });
+ },setCanvasSize:function (width, height, noUpdates) {
+ var canvas = Module['canvas'];
+ canvas.width = width;
+ canvas.height = height;
+ if (!noUpdates) Browser.updateResizeListeners();
+ },windowedWidth:0,windowedHeight:0,setFullScreenCanvasSize:function () {
+ var canvas = Module['canvas'];
+ this.windowedWidth = canvas.width;
+ this.windowedHeight = canvas.height;
+ canvas.width = screen.width;
+ canvas.height = screen.height;
+ var flags = HEAPU32[((SDL.screen+Runtime.QUANTUM_SIZE*0)>>2)];
+ flags = flags | 0x00800000; // set SDL_FULLSCREEN flag
+ HEAP32[((SDL.screen+Runtime.QUANTUM_SIZE*0)>>2)]=flags
+ Browser.updateResizeListeners();
+ },setWindowedCanvasSize:function () {
+ var canvas = Module['canvas'];
+ canvas.width = this.windowedWidth;
+ canvas.height = this.windowedHeight;
+ var flags = HEAPU32[((SDL.screen+Runtime.QUANTUM_SIZE*0)>>2)];
+ flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag
+ HEAP32[((SDL.screen+Runtime.QUANTUM_SIZE*0)>>2)]=flags
+ Browser.updateResizeListeners();
+ }};
+__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });__ATEXIT__.push({ func: function() { FS.quit() } });Module["FS_createFolder"] = FS.createFolder;Module["FS_createPath"] = FS.createPath;Module["FS_createDataFile"] = FS.createDataFile;Module["FS_createPreloadedFile"] = FS.createPreloadedFile;Module["FS_createLazyFile"] = FS.createLazyFile;Module["FS_createLink"] = FS.createLink;Module["FS_createDevice"] = FS.createDevice;
+___errno_state = Runtime.staticAlloc(4); HEAP32[((___errno_state)>>2)]=0;
+_fgetc.ret = allocate([0], "i8", ALLOC_STATIC);
+_fputc.ret = allocate([0], "i8", ALLOC_STATIC);
+___buildEnvironment(ENV);
+Module["requestFullScreen"] = function(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };
+ Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };
+ Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };
+ Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };
+ Module["getUserMedia"] = function() { Browser.getUserMedia() }
+STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);
+staticSealed = true; // seal the static portion of memory
+STACK_MAX = STACK_BASE + 5242880;
+DYNAMIC_BASE = DYNAMICTOP = Runtime.alignMemory(STACK_MAX);
+assert(DYNAMIC_BASE < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY
+var Math_min = Math.min;
+function invoke_viiiii(index,a1,a2,a3,a4,a5) {
+ try {
+ Module["dynCall_viiiii"](index,a1,a2,a3,a4,a5);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_viffiii(index,a1,a2,a3,a4,a5,a6) {
+ try {
+ Module["dynCall_viffiii"](index,a1,a2,a3,a4,a5,a6);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_vf(index,a1) {
+ try {
+ Module["dynCall_vf"](index,a1);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_i(index) {
+ try {
+ return Module["dynCall_i"](index);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_vi(index,a1) {
+ try {
+ Module["dynCall_vi"](index,a1);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_vii(index,a1,a2) {
+ try {
+ Module["dynCall_vii"](index,a1,a2);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_ii(index,a1) {
+ try {
+ return Module["dynCall_ii"](index,a1);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_iff(index,a1,a2) {
+ try {
+ return Module["dynCall_iff"](index,a1,a2);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_iiii(index,a1,a2,a3) {
+ try {
+ return Module["dynCall_iiii"](index,a1,a2,a3);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_viii(index,a1,a2,a3) {
+ try {
+ Module["dynCall_viii"](index,a1,a2,a3);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_v(index) {
+ try {
+ Module["dynCall_v"](index);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_vifiii(index,a1,a2,a3,a4,a5) {
+ try {
+ Module["dynCall_vifiii"](index,a1,a2,a3,a4,a5);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_iiiii(index,a1,a2,a3,a4) {
+ try {
+ return Module["dynCall_iiiii"](index,a1,a2,a3,a4);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_fii(index,a1,a2) {
+ try {
+ return Module["dynCall_fii"](index,a1,a2);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function invoke_iii(index,a1,a2) {
+ try {
+ return Module["dynCall_iii"](index,a1,a2);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}
+function asmPrintInt(x, y) {
+ Module.print('int ' + x + ',' + y);// + ' ' + new Error().stack);
+}
+function asmPrintFloat(x, y) {
+ Module.print('float ' + x + ',' + y);// + ' ' + new Error().stack);
+}
+// EMSCRIPTEN_START_ASM
+var asm=(function(global,env,buffer){"use asm";var a=new global.Int8Array(buffer);var b=new global.Int16Array(buffer);var c=new global.Int32Array(buffer);var d=new global.Uint8Array(buffer);var e=new global.Uint16Array(buffer);var f=new global.Uint32Array(buffer);var g=new global.Float32Array(buffer);var h=new global.Float64Array(buffer);var i=env.STACKTOP|0;var j=env.STACK_MAX|0;var k=env.tempDoublePtr|0;var l=env.ABORT|0;var m=env._stderr|0;var n=env._stdout|0;var o=env._stdin|0;var p=+env.NaN;var q=+env.Infinity;var r=0;var s=0;var t=0;var u=0;var v=0,w=0,x=0,y=0,z=0.0,A=0,B=0,C=0,D=0.0;var E=0;var F=0;var G=0;var H=0;var I=0;var J=0;var K=0;var L=0;var M=0;var N=0;var O=global.Math.floor;var P=global.Math.abs;var Q=global.Math.sqrt;var R=global.Math.pow;var S=global.Math.cos;var T=global.Math.sin;var U=global.Math.tan;var V=global.Math.acos;var W=global.Math.asin;var X=global.Math.atan;var Y=global.Math.atan2;var Z=global.Math.exp;var _=global.Math.log;var $=global.Math.ceil;var aa=global.Math.imul;var ab=env.abort;var ac=env.assert;var ad=env.asmPrintInt;var ae=env.asmPrintFloat;var af=env.copyTempDouble;var ag=env.copyTempFloat;var ah=env.min;var ai=env.invoke_viiiii;var aj=env.invoke_viffiii;var ak=env.invoke_vf;var al=env.invoke_i;var am=env.invoke_vi;var an=env.invoke_vii;var ao=env.invoke_ii;var ap=env.invoke_iff;var aq=env.invoke_iiii;var ar=env.invoke_viii;var as=env.invoke_v;var at=env.invoke_vifiii;var au=env.invoke_iiiii;var av=env.invoke_fii;var aw=env.invoke_iii;var ax=env._lseek;var ay=env.__scanString;var az=env._fclose;var aA=env._uname;var aB=env._sleep;var aC=env.__isFloat;var aD=env._fflush;var aE=env._strtol;var aF=env._fputc;var aG=env._iconv;var aH=env.___signgam;var aI=env._fwrite;var aJ=env._send;var aK=env._fputs;var aL=env._tmpnam;var aM=env._isspace;var aN=env._localtime;var aO=env._read;var aP=env._ceil;var aQ=env._strstr;var aR=env._fileno;var aS=env._perror;var aT=env._ctime;var aU=env._fsync;var aV=env._signal;var aW=env._opendir;var aX=env._fmod;var aY=env._strcmp;var aZ=env._memchr;var a_=env._strncmp;var a$=env._tmpfile;var a0=env._snprintf;var a1=env._fgetc;var a2=env._pclose;var a3=env._readdir;var a4=env._cosh;var a5=env._atexit;var a6=env._fgets;var a7=env._close;var a8=env._strchr;var a9=env._asin;var ba=env._llvm_lifetime_start;var bb=env.___setErrNo;var bc=env._ftell;var bd=env._exit;var be=env._sprintf;var bf=env._llvm_lifetime_end;var bg=env._asctime;var bh=env._strrchr;var bi=env._iconv_open;var bj=env._modf;var bk=env._strcspn;var bl=env._getcwd;var bm=env._gmtime;var bn=env._localtime_r;var bo=env._asctime_r;var bp=env._recv;var bq=env._cos;var br=env._putchar;var bs=env._isalnum;var bt=env._popen;var bu=env._erfc;var bv=env.__exit;var bw=env._strftime;var bx=env._llvm_va_end;var by=env._tzset;var bz=env._sinh;var bA=env._setlocale;var bB=env._isprint;var bC=env._toupper;var bD=env._printf;var bE=env._pread;var bF=env._fopen;var bG=env._open;var bH=env._usleep;var bI=env._log;var bJ=env._puts;var bK=env._mktime;var bL=env._fdopen;var bM=env._qsort;var bN=env._system;var bO=env._isalpha;var bP=env._strdup;var bQ=env._log10;var bR=env._closedir;var bS=env._isatty;var bT=env.__formatString;var bU=env._getenv;var bV=env._gettimeofday;var bW=env._atoi;var bX=env._vfprintf;var bY=env._chdir;var bZ=env._llvm_pow_f64;var b_=env._sbrk;var b$=env._localeconv;var b0=env.___errno_location;var b1=env._strerror;var b2=env._lgamma;var b3=env._erf;var b4=env._strspn;var b5=env.__parseInt;var b6=env._ungetc;var b7=env._llvm_trap;var b8=env._rename;var b9=env._vsnprintf;var ca=env._sscanf;var cb=env._sysconf;var cc=env._acos;var cd=env._fread;var ce=env._abort;var cf=env._fprintf;var cg=env.___fpclassifyf;var ch=env._tan;var ci=env.___buildEnvironment;var cj=env._feof;var ck=env._strncat;var cl=env._gmtime_r;var cm=env._fabs;var cn=env._floor;var co=env.__reallyNegative;var cp=env._fseek;var cq=env._sqrt;var cr=env._write;var cs=env._rewind;var ct=env._sin;var cu=env._stat;var cv=env._longjmp;var cw=env._atan;var cx=env._readdir_r;var cy=env._strpbrk;var cz=env._iconv_close;var cA=env._setbuf;var cB=env._nl_langinfo;var cC=env._pwrite;var cD=env._strerror_r;var cE=env._atan2;var cF=env._exp;var cG=env._time;var cH=env._setvbuf;
+// EMSCRIPTEN_START_FUNCS
+function cX(a){a=a|0;var b=0;b=i;i=i+a|0;i=i+7>>3<<3;return b|0}function cY(){return i|0}function cZ(a){a=a|0;i=a}function c_(a,b){a=a|0;b=b|0;if((r|0)==0){r=a;s=b}}function c$(a){a=a|0;E=a}function c0(a){a=a|0;F=a}function c1(a){a=a|0;G=a}function c2(a){a=a|0;H=a}function c3(a){a=a|0;I=a}function c4(a){a=a|0;J=a}function c5(a){a=a|0;K=a}function c6(a){a=a|0;L=a}function c7(a){a=a|0;M=a}function c8(a){a=a|0;N=a}function c9(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0.0,u=0.0,x=0.0;d=i;i=i+96|0;e=d|0;f=d+48|0;do{if((c[64808+(b*688&-1)>>2]|0)==1){if((a[64812+(b*688&-1)|0]&1)==0){break}g=4968+(b*51&-1)|0;a[g]=0;j=4608+(b<<2)|0;k=+da(c[j>>2]|0,+h[64664+(b*688&-1)>>3]);t3(e,k);k=+da(c[j>>2]|0,+h[64672+(b*688&-1)>>3]);t3(f,k);l=c[f+20>>2]|0;m=c[e+20>>2]|0;n=(l|0)==(m|0);do{if(n){if((c[f+28>>2]|0)!=(c[e+28>>2]|0)){break}if((c[f+8>>2]|0)!=(c[e+8>>2]|0)){a[g]=a[172544]|0;a[g+1|0]=a[172545|0]|0;a[g+2|0]=a[172546|0]|0}o=c[j>>2]|0;if(o>>>0>=4){p=g;i=d;return p|0}if((a[g]|0)!=0){q=(uA(g|0)|0)+(4968+(b*51&-1))|0;w=58;a[q]=w&255;w=w>>8;a[q+1|0]=w&255}q=(uA(g|0)|0)+(4968+(b*51&-1))|0;a[q]=a[164672]|0;a[q+1|0]=a[164673|0]|0;a[q+2|0]=a[164674|0]|0;if(o>>>0>=3){p=g;i=d;return p|0}o=(uA(g|0)|0)+(4968+(b*51&-1))|0;w=5449018;a[o]=w&255;w=w>>8;a[o+1|0]=w&255;w=w>>8;a[o+2|0]=w&255;w=w>>8;a[o+3|0]=w&255;p=g;i=d;return p|0}}while(0);o=64813+(b*688&-1)|0;q=a8(o|0,109)|0;r=q>>>0<(a8(o|0,100)|0)>>>0;do{if(n){if(r){a[g]=a[145768]|0;a[g+1|0]=a[145769|0]|0;a[g+2|0]=a[145770|0]|0;a[g+3|0]=a[145771|0]|0;a[g+4|0]=a[145772|0]|0;a[g+5|0]=a[145773|0]|0;break}else{a[g]=a[143792]|0;a[g+1|0]=a[143793|0]|0;a[g+2|0]=a[143794|0]|0;a[g+3|0]=a[143795|0]|0;a[g+4|0]=a[143796|0]|0;a[g+5|0]=a[143797|0]|0;break}}else{o=g;q=o|0;w=r?623865125:623862821;a[q]=w&255;w=w>>8;a[q+1|0]=w&255;w=w>>8;a[q+2|0]=w&255;w=w>>8;a[q+3|0]=w&255;q=o+4|0;w=r?2436964:2436973;a[q]=w&255;w=w>>8;a[q+1|0]=w&255;w=w>>8;a[q+2|0]=w&255;w=w>>8;a[q+3|0]=w&255;q=(uA(g|0)|0)+(4968+(b*51&-1))|0;if(((l|0)/100&-1|0)==((m|0)/100&-1|0)){w=121;a[q]=w&255;w=w>>8;a[q+1|0]=w&255;break}else{w=89;a[q]=w&255;w=w>>8;a[q+1|0]=w&255;break}}}while(0);if((c[j>>2]|0)>>>0>=5){p=g;i=d;return p|0}m=(uA(g|0)|0)+(4968+(b*51&-1))|0;a[m]=a[142240]|0;a[m+1|0]=a[142241|0]|0;a[m+2|0]=a[142242|0]|0;a[m+3|0]=a[142243|0]|0;a[m+4|0]=a[142244|0]|0;a[m+5|0]=a[142245|0]|0;a[m+6|0]=a[142246|0]|0;p=g;i=d;return p|0}}while(0);e=4968+(b*51&-1)|0;uB(e|0,64864+(b*688&-1)|0);if((aY(e|0,186288)|0)!=0){p=e;i=d;return p|0}k=+h[64664+(b*688&-1)>>3];if((a[64788+(b*688&-1)|0]&1)==0){s=+h[64672+(b*688&-1)>>3];t=k}else{u=+h[64800+(b*688&-1)>>3];x=+Z(+(k*u));s=+Z(+(+h[64672+(b*688&-1)>>3]*u));t=x}b=~~+$(+(-0.0- +bQ(+(+P(+(s-t))))));if(t*s>0.0&(b|0)>4){f=(b|0)>14?14:b;be(e|0,180872,(v=i,i=i+8|0,c[v>>2]=f,v)|0);p=e;i=d;return p|0}else{a[e]=a[170912]|0;a[e+1|0]=a[170913|0]|0;a[e+2|0]=a[170914|0]|0;p=e;i=d;return p|0}return 0}function da(a,b){a=a|0;b=+b;var d=0,e=0,f=0.0,g=0,h=0,j=0;d=i;i=i+48|0;e=d|0;if(a>>>0<2){f=b;i=d;return+f}t3(e,b);g=e|0;if((c[g>>2]|0)>55){h=e+4|0;c[h>>2]=(c[h>>2]|0)+1}c[g>>2]=0;do{if(a>>>0>2){g=e+4|0;if((c[g>>2]|0)>55){h=e+8|0;c[h>>2]=(c[h>>2]|0)+1}c[g>>2]=0;if(a>>>0<=3){break}g=e+8|0;if((c[g>>2]|0)>22){c[g>>2]=0;c[e+12>>2]=0;g=e+28|0;c[g>>2]=(c[g>>2]|0)+1;b=+t2(e);t3(e,b)}if(a>>>0<=5){break}g=e+12|0;do{if((c[g>>2]|0)>25){h=e+16|0;j=(c[h>>2]|0)+1|0;c[h>>2]=j;if((j|0)<=11){break}j=e+20|0;c[j>>2]=(c[j>>2]|0)+1;c[h>>2]=0}}while(0);c[g>>2]=1}}while(0);f=+t2(e);i=d;return+f}function db(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0;e=i;do{if((a|0)==0){f=ut(b)|0;if((f|0)!=0){g=f;break}gk();f=ut(b)|0;if((f|0)!=0|(d|0)==0){g=f;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=d,v)|0);return 0}else{f=uv(a,b)|0;if((f|0)!=0){g=f;break}gk();f=uv(a,b)|0;if((f|0)!=0|(d|0)==0){g=f;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=d,v)|0);return 0}}while(0);i=e;return g|0}function dc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0.0,g=0,j=0.0,k=0,l=0,n=0.0,o=0,p=0,q=0;d=i;e=64664+(a*688&-1)|0;f=+h[e>>3];g=64672+(a*688&-1)|0;j=+h[g>>3];k=(b|0)!=0;do{if(k){if(!(f==8.988465674311579e+307|j==-8.988465674311579e+307)){break}uf(c[13898]|0,b,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if(j-f!=0.0){i=d;return}b=64648+(a*688&-1)|0;l=c[b>>2]|0;if((l|0)==0){uf(-1,197584,(v=i,i=i+8|0,c[v>>2]=56832+(a*24&-1),v)|0)}if(j==0.0){n=1.0}else{n=+P(+j)*.01}o=(a|0)!=0|k;if(o){k=c[m>>2]|0;p=56832+(a*24&-1)|0;cf(k|0,206616,(v=i,i=i+24|0,c[v>>2]=p,h[v+8>>3]=f,h[v+16>>3]=j,v)|0);q=c[b>>2]|0}else{q=l}if((q&1|0)!=0){h[e>>3]=+h[e>>3]-n}if((q&2|0)!=0){h[g>>3]=n+ +h[g>>3]}if(!o){i=d;return}n=+h[g>>3];cf(c[m>>2]|0,202928,(v=i,i=i+16|0,h[v>>3]=+h[e>>3],h[v+8>>3]=n,v)|0);i=d;return}function dd(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0,p=0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,Q=0.0,S=0,T=0.0,U=0.0;e=i;f=c[64648+(b*688&-1)>>2]|0;g=(f&1|0)==0;if(g){j=0}else{j=(f&4|0)==0}k=(f&2|0)==0;if(k){l=0}else{l=(f&8|0)==0}do{if(!g){if((c[64728+(b*688&-1)>>2]&2|0)==0){break}f=64664+(b*688&-1)|0;m=+h[64744+(b*688&-1)>>3];if(+h[f>>3]<=m){break}h[f>>3]=m}}while(0);do{if(!k){if((c[64732+(b*688&-1)>>2]&1|0)==0){break}g=64672+(b*688&-1)|0;m=+h[64752+(b*688&-1)>>3];if(+h[g>>3]>=m){break}h[g>>3]=m}}while(0);if((c[64916+(b*688&-1)>>2]|0)==0){i=e;return}k=64920+(b*688&-1)|0;g=c[k>>2]|0;do{if((g|0)==2){m=+h[64960+(b*688&-1)>>3];h[4880+(b<<3)>>3]=m;if(j){n=+h[64952+(b*688&-1)>>3]==-8.988465674311579e+307}else{n=0}if(!l){o=0;p=n;q=m;break}o=+h[64968+(b*688&-1)>>3]==8.988465674311579e+307;p=n;q=m}else if((g|0)==1){m=+h[64664+(b*688&-1)>>3]- +h[64672+(b*688&-1)>>3];r=+P(+m);do{if(m==0.0){s=1.0}else{if(r>=8.988465674311579e+307){uf(-1,98888,(v=i,i=i+8|0,c[v>>2]=56832+(b*24&-1),v)|0)}t=+R(10.0,+(+O(+(+bQ(+r)))));u=r/t;w=+(d|0);x=w/u;do{if(x>40.0){y=.05}else{if(x>20.0){y=.1;break}if(x>10.0){y=.2;break}if(x>4.0){y=.5;break}if(x>2.0){y=1.0;break}if(x>.5){y=2.0;break}y=+$(+u)}}while(0);u=t*y;x=(a[64788+(b*688&-1)|0]&1)!=0&u<1.0?1.0:u;if((c[64808+(b*688&-1)>>2]|0)!=1){s=x;break}f=(d*3&-1|0)/5&-1;z=4608+(b<<2)|0;c[z>>2]=1;do{if(x>5.0){u=r/60.0;A=+R(12.0,+(+O(+(+_(+u)/2.4849066497880004))));B=u/A;u=+(f|0)/B;do{if(u>24.0){C=A/24.0}else{if(u>12.0){C=A/12.0;break}if(u>6.0){C=A/6.0;break}if(u>4.0){C=A*.25;break}if(u>2.0){C=A*.5;break}if(u>1.0){C=A;break}if(u>.5){C=A*2.0;break}if(u>.3333333333333333){C=A*3.0;break}else{C=A*+$(+B);break}}}while(0);B=C*60.0;if(B<60.0){D=B;break}c[z>>2]=2;D=B}else{D=x}}while(0);do{if(D>300.0){x=r/3600.0;t=+R(12.0,+(+O(+(+_(+x)/2.4849066497880004))));B=x/t;x=+(f|0)/B;do{if(x>24.0){E=t/24.0}else{if(x>12.0){E=t/12.0;break}if(x>6.0){E=t/6.0;break}if(x>4.0){E=t*.25;break}if(x>2.0){E=t*.5;break}if(x>1.0){E=t;break}if(x>.5){E=t*2.0;break}if(x>.3333333333333333){E=t*3.0;break}else{E=t*+$(+B);break}}}while(0);B=E*3600.0;if(B<3600.0){F=B;break}c[z>>2]=3;F=B}else{F=D}}while(0);do{if(F>3600.0){B=r/86400.0;t=+R(12.0,+(+O(+(+_(+B)/2.4849066497880004))));x=B/t;B=+(f|0)/x;do{if(B>24.0){G=t/24.0}else{if(B>12.0){G=t/12.0;break}if(B>6.0){G=t/6.0;break}if(B>4.0){G=t*.25;break}if(B>2.0){G=t*.5;break}if(B>1.0){G=t;break}if(B>.5){G=t*2.0;break}if(B>.3333333333333333){G=t*3.0;break}else{G=t*+$(+x);break}}}while(0);x=G*86400.0;if(x<86400.0){H=x;break}c[z>>2]=4;H=x}else{H=F}}while(0);do{if(H>172800.0){x=r/604800.0;t=+R(10.0,+(+O(+(+bQ(+x)))));B=x/t;x=w/B;do{if(x>40.0){I=.05}else{if(x>20.0){I=.1;break}if(x>10.0){I=.2;break}if(x>4.0){I=.5;break}if(x>2.0){I=1.0;break}if(x>.5){I=2.0;break}I=+$(+B)}}while(0);B=t*I*604800.0;x=B<604800.0?604800.0:B;if(x<604800.0){J=x;break}c[z>>2]=5;J=x}else{J=H}}while(0);do{if(J>1814400.0){x=r/2629800.0;B=+R(10.0,+(+O(+(+bQ(+x)))));A=x/B;x=w/A;do{if(x>40.0){K=.05}else{if(x>20.0){K=.1;break}if(x>10.0){K=.2;break}if(x>4.0){K=.5;break}if(x>2.0){K=1.0;break}if(x>.5){K=2.0;break}K=+$(+A)}}while(0);A=B*K*2629800.0;x=A<2629800.0?2629800.0:A;if(x<2629800.0){L=x;break}c[z>>2]=6;L=x}else{L=J}}while(0);if(L<=2629800.0){s=L;break}w=r/31557600.0;x=+R(12.0,+(+O(+(+_(+w)/2.4849066497880004))));A=w/x;w=+(f|0)/A;do{if(w>24.0){M=x/24.0}else{if(w>12.0){M=x/12.0;break}if(w>6.0){M=x/6.0;break}if(w>4.0){M=x*.25;break}if(w>2.0){M=x*.5;break}if(w>1.0){M=x;break}if(w>.5){M=x*2.0;break}if(w>.3333333333333333){M=x*3.0;break}else{M=x*+$(+A);break}}}while(0);A=M*31557600.0;if(A<31557600.0){s=A;break}c[z>>2]=7;s=A}}while(0);h[4880+(b<<3)>>3]=s;o=l;p=j;q=s}else{o=0;p=0;q=0.0}}while(0);j=(c[64808+(b*688&-1)>>2]|0)==1;do{if(j){if((c[k>>2]|0)!=2){break}if(q>=31536.0e3){c[4608+(b<<2)>>2]=7;break}if(q>=2419200.0){c[4608+(b<<2)>>2]=6;break}if(q>=604800.0){c[4608+(b<<2)>>2]=5;break}if(q>=86400.0){c[4608+(b<<2)>>2]=4;break}if(q>=3600.0){c[4608+(b<<2)>>2]=3;break}l=4608+(b<<2)|0;if(q<60.0){c[l>>2]=1;break}else{c[l>>2]=2;break}}}while(0);do{if(p){k=64664+(b*688&-1)|0;q=+h[k>>3];l=q>=+h[64672+(b*688&-1)>>3];s=+h[4880+(b<<3)>>3];M=q/s;if(l){N=+$(+M)}else{N=+O(+M)}M=s*N;do{if(j){s=+da(c[4608+(b<<2)>>2]|0,M);if(s>M&l){Q=s;break}if(s<M&(l^1)){Q=s}else{S=231}}else{S=231}}while(0);if((S|0)==231){Q=M}h[k>>3]=Q;if((c[64728+(b*688&-1)>>2]&1|0)==0){break}s=+h[64736+(b*688&-1)>>3];if(Q>=s){break}h[k>>3]=s}}while(0);do{if(o){j=64672+(b*688&-1)|0;Q=+h[j>>3];p=+h[64664+(b*688&-1)>>3]<Q;N=+h[4880+(b<<3)>>3];s=Q/N;if(p){T=+$(+s)}else{T=+O(+s)}s=N*T;do{if((c[64808+(b*688&-1)>>2]|0)==1){N=+da(c[4608+(b<<2)>>2]|0,s);if(N>s&p){U=N;break}if(N<s&(p^1)){U=N}else{S=242}}else{S=242}}while(0);if((S|0)==242){U=s}h[j>>3]=U;if((c[64732+(b*688&-1)>>2]&2|0)==0){break}M=+h[64760+(b*688&-1)>>3];if(U<=M){break}h[j>>3]=M}}while(0);c9(b);i=e;return}function de(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0.0,B=0,C=0.0,D=0.0,E=0,F=0.0,G=0.0,H=0.0,I=0.0,J=0,K=0,L=0,M=0,N=0,Q=0,S=0,T=0,U=0,V=0,W=0,X=0.0,Y=0.0,aa=0.0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0.0,ap=0.0,aq=0.0,ar=0.0,as=0.0,at=0.0,au=0,av=0.0,aw=0.0,ax=0.0,ay=0,az=0.0,aA=0.0,aB=0.0,aC=0.0,aD=0.0,aE=0.0,aF=0.0,aG=0.0,aH=0.0,aI=0.0,aJ=0.0,aK=0.0,aL=0.0,aM=0.0,aN=0.0,aO=0.0,aP=0.0,aQ=0.0,aR=0.0,aS=0.0,aT=0.0;e=i;i=i+432|0;f=e|0;g=e+48|0;j=e+96|0;k=e+152|0;l=e+208|0;m=e+264|0;n=e+320|0;o=e+376|0;p=64920+(b*688&-1)|0;q=c[65040+(b*688&-1)>>2]|0;r=+h[65048+(b*688&-1)>>3];s=j;c[s>>2]=c[10012];c[s+4>>2]=c[10013];c[s+8>>2]=c[10014];c[s+12>>2]=c[10015];c[s+16>>2]=c[10016];c[s+20>>2]=c[10017];c[s+24>>2]=c[10018];c[s+28>>2]=c[10019];c[s+32>>2]=c[10020];c[s+36>>2]=c[10021];c[s+40>>2]=c[10022];c[s+44>>2]=c[10023];c[s+48>>2]=c[10024];c[s+52>>2]=c[10025];t=k;c[t>>2]=c[8694];c[t+4>>2]=c[8695];c[t+8>>2]=c[8696];c[t+12>>2]=c[8697];c[t+16>>2]=c[8698];c[t+20>>2]=c[8699];c[t+24>>2]=c[8700];c[t+28>>2]=c[8701];c[t+32>>2]=c[8702];c[t+36>>2]=c[8703];c[t+40>>2]=c[8704];c[t+44>>2]=c[8705];c[t+48>>2]=c[8706];c[t+52>>2]=c[8707];if((a[65036+(b*688&-1)|0]&1)==0){c[j+4>>2]=-3}if((a[65037+(b*688&-1)|0]&1)==0){c[k+4>>2]=-3}u=64944+(b*688&-1)|0;do{if((c[u>>2]|0)==0){w=64664+(b*688&-1)|0;x=64672+(b*688&-1)|0;y=64788+(b*688&-1)|0}else{z=64672+(b*688&-1)|0;A=+h[z>>3];B=64664+(b*688&-1)|0;C=+h[B>>3];D=(A-C)/10.0*.01;E=64788+(b*688&-1)|0;if((a[E]&1)==0){F=1.0}else{F=+bQ(+(+h[64792+(b*688&-1)>>3]))}if((a[30528]&1)==0){G=C;H=A;I=0.0}else{J=c[200]|0;G=+h[64664+(J*688&-1)>>3];H=+h[64672+(J*688&-1)>>3];I=(c[17366]&1|0)==0?+h[8685]:0.0}A=G-D;C=D+H;J=c[u>>2]|0;if((J|0)!=0){K=(b|0)==7;L=A<C;M=m;N=j+4|0;Q=n;S=64808+(b*688&-1)|0;T=l|0;U=4968+(b*51&-1)|0;V=64800+(b*688&-1)|0;W=J;do{if(K){D=+h[W>>3];if((a[69604]&1)==0){X=I;Y=D}else{aa=+_(+D);ab=+h[8702];X=+_(+I)/ab;Y=aa/ab}ac=Y-X;ad=D}else{D=+h[W>>3];if((a[E]&1)==0){ae=D}else{ab=+_(+D);ae=ab/+h[V>>3]}ac=ae-I;ad=D}if(L){if(!(ac<A|ac>C)){af=272}}else{if(!(ac<C|ac>A)){af=272}}do{if((af|0)==272){af=0;J=W+12|0;ag=c[W+8>>2]|0;do{if((c[J>>2]|0)<0){ah=ag;ai=ag;af=280}else{if((ag|0)==0){af=275}else{if((a8(ag|0,37)|0)==0){aj=ag}else{af=275}}do{if((af|0)==275){af=0;ak=(ag|0)!=0?ag:U;if((c[S>>2]|0)==1){t3(g,ad);D=ad- +O(+ad);t4(T,49,ak,g,D);aj=T;break}else{ud(T,50,ak,F,ad);aj=T;break}}}while(0);ak=(c[J>>2]|0)>0;al=ak?0:aj;if(!ak){ah=aj;ai=al;af=280;break}c[M>>2]=c[t>>2];c[M+4>>2]=c[t+4>>2];c[M+8>>2]=c[t+8>>2];c[M+12>>2]=c[t+12>>2];c[M+16>>2]=c[t+16>>2];c[M+20>>2]=c[t+20>>2];c[M+24>>2]=c[t+24>>2];c[M+28>>2]=c[t+28>>2];c[M+32>>2]=c[t+32>>2];c[M+36>>2]=c[t+36>>2];c[M+40>>2]=c[t+40>>2];c[M+44>>2]=c[t+44>>2];c[M+48>>2]=c[t+48>>2];c[M+52>>2]=c[t+52>>2];am=aj;an=al}}while(0);if((af|0)==280){af=0;c[M>>2]=c[s>>2];c[M+4>>2]=c[s+4>>2];c[M+8>>2]=c[s+8>>2];c[M+12>>2]=c[s+12>>2];c[M+16>>2]=c[s+16>>2];c[M+20>>2]=c[s+20>>2];c[M+24>>2]=c[s+24>>2];c[M+28>>2]=c[s+28>>2];c[M+32>>2]=c[s+32>>2];c[M+36>>2]=c[s+36>>2];c[M+40>>2]=c[s+40>>2];c[M+44>>2]=c[s+44>>2];c[M+48>>2]=c[s+48>>2];c[M+52>>2]=c[s+52>>2];am=ah;an=ai}cT[d&15](b,ac,an,m,0);if(!K){break}if((c[17433]&4|0)==0){break}ag=c[N>>2]|0;c[N>>2]=-3;al=(c[J>>2]|0)>0;if(al){c[Q>>2]=c[t>>2];c[Q+4>>2]=c[t+4>>2];c[Q+8>>2]=c[t+8>>2];c[Q+12>>2]=c[t+12>>2];c[Q+16>>2]=c[t+16>>2];c[Q+20>>2]=c[t+20>>2];c[Q+24>>2]=c[t+24>>2];c[Q+28>>2]=c[t+28>>2];c[Q+32>>2]=c[t+32>>2];c[Q+36>>2]=c[t+36>>2];c[Q+40>>2]=c[t+40>>2];c[Q+44>>2]=c[t+44>>2];c[Q+48>>2]=c[t+48>>2];c[Q+52>>2]=c[t+52>>2]}else{c[Q>>2]=c[s>>2];c[Q+4>>2]=c[s+4>>2];c[Q+8>>2]=c[s+8>>2];c[Q+12>>2]=c[s+12>>2];c[Q+16>>2]=c[s+16>>2];c[Q+20>>2]=c[s+20>>2];c[Q+24>>2]=c[s+24>>2];c[Q+28>>2]=c[s+28>>2];c[Q+32>>2]=c[s+32>>2];c[Q+36>>2]=c[s+36>>2];c[Q+40>>2]=c[s+40>>2];c[Q+44>>2]=c[s+44>>2];c[Q+48>>2]=c[s+48>>2];c[Q+52>>2]=c[s+52>>2]}cT[d&15](7,-0.0-ac,al?0:am,n,0);c[N>>2]=ag}}while(0);W=c[W+16>>2]|0;}while((W|0)!=0)}if((c[p>>2]|0)!=3){w=B;x=z;y=E;break}i=e;return}}while(0);ac=+h[w>>3];ad=+h[x>>3];if((a[y]&1)==0){ao=1.0}else{ao=+bQ(+(+h[64792+(b*688&-1)>>3]))}x=ad<ac;F=x?ad:ac;I=x?ac:ad;x=p|0;p=c[x>>2]|0;do{if((p|0)==2){if((a[y]&1)==0|(b|0)==7){ad=+h[64952+(b*688&-1)>>3];ac=+h[64960+(b*688&-1)>>3];ae=+h[64968+(b*688&-1)>>3];if(ad==-8.988465674311579e+307){ap=ac*+O(+(F/ac))}else{ap=ad}if(ae!=8.988465674311579e+307){aq=ae;ar=ac;as=ap;break}aq=ac*+$(+(I/ac));ar=ac;as=ap;break}ac=+h[64968+(b*688&-1)>>3];if(ac<=0.0){i=e;return}ae=+h[64960+(b*688&-1)>>3];if(ae<=0.0){i=e;return}ad=+_(+ae);ae=+h[64800+(b*688&-1)>>3];X=ad/ae;ad=+h[64952+(b*688&-1)>>3];if(ad>0.0){at=+_(+ad)/ae}else{at=X*+O(+(F/X))}if(ac==8.988465674311579e+307){aq=X*+$(+(I/X));ar=X;as=at;break}else{aq=+_(+ac)/ae;ar=X;as=at;break}}else if((p|0)==1){X=+h[4880+(b<<3)>>3];ae=X*+O(+(F/X));aq=X*+$(+(I/X));ar=X;as=ae}else if((p|0)==4){ae=+O(+F);X=+$(+I);ac=+O(+((X-ae)/12.0));if(ac>=1.0){aq=X;ar=ac;as=ae;break}aq=X;ar=1.0;as=ae}else if((p|0)==5){ae=+O(+F);X=+$(+I);ac=+O(+((X-ae)/14.0));if(ac>=1.0){aq=X;ar=ac;as=ae;break}aq=X;ar=1.0;as=ae}else{uk(140640,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);p=aq<as;at=p?aq:as;ap=p?as:aq;aq=+P(+ar);L424:do{if((q|0)==0){au=0;av=1.0;aw=1.0;ax=0.0}else{if(+h[65064+(b*688&-1)>>3]==0.0){au=q;av=1.0;aw=1.0;ax=0.0;break}do{if((q|0)==2){if(r<=0.0){au=0;av=1.0;aw=1.0;ax=0.0;break L424}as=aq/r;if((a[y]&1)==0){ay=2;az=aq;aA=as;break}ae=+h[64792+(b*688&-1)>>3];ay=2;az=aq*ae;aA=as*ae}else{if((a[y]&1)!=0){if(aq<=1.5){ae=+h[64792+(b*688&-1)>>3];as=ap-at;if(as>=10.0){au=0;av=ae;aw=1.0;ax=0.0;break L424}au=q;av=ae;aw=as<5.0?1.0:3.0;ax=2.0;break L424}as=aq*.2;if(aq<65535.0){aB=+(~~as|0)}else{aB=as}ay=q;az=aq;aA=aB<1.0?1.0:aB;break}if((c[64808+(b*688&-1)>>2]|0)!=1){if((q|0)!=3){au=0;av=1.0;aw=1.0;ax=0.0;break L424}as=+P(+aq);ay=3;az=aq;aA=aq*((~~(as/+R(10.0,+(+O(+(+bQ(+as))))))|0)==2?.5:.2);break}p=c[4608+(b<<2)>>2]|0;w=(p|0)<1?1:p;do{if((w|0)==1|(w|0)==2){if(aq<1440.0){aC=aq<720.0?aq<360.0?aq<120.0?aq<60.0?aq<20.0?aq<10.0?aq<5.0?0.0:1.0:5.0:10.0:20.0:60.0:120.0:180.0;break}aC=360.0}else if((w|0)==3){if(aq<86400.0){aC=aq<43200.0?aq<21600.0?aq<7200.0?aq<3600.0?aq<1200.0?0.0:600.0:1800.0:3600.0:7200.0:10800.0;break}aC=21600.0}else if((w|0)==4){if(aq<=172800.0){aC=aq>86400.0?43200.0:aq>46800.0?21600.0:aq>25200.0?10800.0:aq>14400.0?7200.0:aq>7200.0?3600.0:0.0;break}aC=86400.0}else if((w|0)==5){if(aq<=604800.0){aC=aq>172800.0?86400.0:0.0;break}aC=604800.0}else if((w|0)==6){if(aq<=63115200.0){aC=aq>15778800.0?7889400.0:aq>5259600.0?2629800.0:aq>1296.0e3?864.0e3:aq>172800.0?86400.0:0.0;break}aC=31557600.0}else if((w|0)==7){if(aq<=946728.0e4){aC=aq>631152.0e4?157788.0e4:aq>315576.0e4?631152.0e3:aq>157788.0e4?315576.0e3:aq>315576.0e3?157788.0e3:aq>63115200.0?31557600.0:aq>15778800.0?7889400.0:aq>5259600.0?2629800.0:0.0;break}aC=315576.0e4}else{aC=0.0}}while(0);ay=q;az=aq*.9;aA=aC}}while(0);if(aA>0.0){au=ay;av=az;aw=aA;ax=aA;break}au=0;av=az;aw=aA;ax=aA}}while(0);aA=aq*.01;az=ap+aA;ap=+P(+I);if(aq<ap+ +P(+F)){aD=I+aA;aE=F-aA}else{aD=I;aE=F}if(ar==0.0){i=e;return}ar=(aD-aE)/aq;if(ar>+((c[(c[3524]|0)+8>>2]|0)>>>0>>>0)){uh(-1,138720,(v=i,i=i+8|0,h[v>>3]=ar,v)|0);i=e;return}h[111]=at-aq;h[110]=at;L473:do{if(+h[110]>az){aF=aq}else{ar=aq*.25;while(1){if(+P(+(+h[110]- +h[111]))<ar){break}h[111]=+h[110];h[110]=aq+ +h[110];if(+h[110]>az){aF=aq;break L473}}uh(-1,137120,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);aF=az-at}}while(0);if(at>az){i=e;return}ay=(b|0)==7;q=(au|0)==0;au=65064+(b*688&-1)|0;E=64808+(b*688&-1)|0;z=4608+(b<<2)|0;B=aE<aD;aq=aF*.01;ar=at-aq;F=az+aq;w=ar<F;p=aF>1.5;n=64800+(b*688&-1)|0;am=o|0;o=4968+(b*51&-1)|0;s=65024+(b*688&-1)|0;t=j+4|0;m=64712+(b*688&-1)|0;an=64720+(b*688&-1)|0;I=at;at=0.0;while(1){do{if(ay){aG=at;aH=I}else{if((a[y]&1)!=0){aG=+Z(+(I*+h[n>>3]));aH=I;break}if((c[E>>2]|0)==1){aI=+da(c[z>>2]|0,I)}else{aI=I}aG=+P(+aI)<aq?0.0:aI;aH=aI}}while(0);if(aH>aD){af=427;break}L495:do{if(aH<aE){aJ=aH;af=382}else{ai=c[x>>2]|0;if((ai|0)==5){ah=(~~+O(+(aG+.5))|0)%7&-1;cT[d&15](b,aH,72464+(((ah|0)<0?ah+7|0:ah)<<3)|0,j,c[u>>2]|0);aJ=aH;af=382;break}else if((ai|0)==4){ai=(~~+O(+(aG+-1.0))|0)%12&-1;cT[d&15](b,aH,72368+(((ai|0)<0?ai+12|0:ai)<<3)|0,j,c[u>>2]|0);aJ=aH;af=382;break}else{do{if((c[E>>2]|0)==1){t3(f,aG);aA=aG- +O(+aG);t4(am,49,o,f,aA);aK=aH}else{if((a[30528]&1)==0){ud(am,50,o,ao,aG);aK=aH;break}aA=+h[8685];ap=(c[17366]&1|0)==0?aA:0.0;aC=+P(+aG)+ap;if(ay){if((a[y]&1)==0){aL=aA;aM=I}else{ap=+_(+I);aB=+h[n>>3];aL=+_(+aA)/aB;aM=ap/aB}aN=I;aO=aM-aL}else{aN=aC;aO=aH}ud(am,50,o,ao,aN);aK=aO}}while(0);do{if((a[s]&1)!=0){aC=+h[m>>3];aB=+h[an>>3];if(aC<aB){if(aK<aC|aK>aB){break L495}else{break}}else{if(aK<aB|aK>aC){break L495}else{break}}}}while(0);cT[d&15](b,aK,am,j,c[u>>2]|0);if(!ay){aJ=aK;af=382;break}if((c[17433]&4|0)==0){aJ=aK;af=382;break}ai=c[t>>2]|0;c[t>>2]=-3;cT[d&15](7,-0.0-aK,am,j,c[u>>2]|0);c[t>>2]=ai;aJ=aK;af=382;break}}}while(0);L522:do{if((af|0)==382){af=0;if(q){break}if(+h[au>>3]==0.0|ax>=av){break}if(p){aC=ax;while(1){if((c[E>>2]|0)==1){aP=+da((c[z>>2]|0)-1|0,aJ+aC)}else{aP=aJ+aC}if(B){if(!(aP<aE|aP>aD)){af=392}}else{if(!(aP<aD|aP>aE)){af=392}}do{if((af|0)==392){af=0;if(w){if(aP<ar|aP>F){break}}else{if(aP<F|aP>ar){break}}cT[d&15](b,aP,0,k,0)}}while(0);aC=aw+aC;if(aC>=av){break L522}}}if(B){aC=ax;do{if((c[E>>2]|0)==1){aQ=+da((c[z>>2]|0)-1|0,aJ+aC)}else{if((a[y]&1)==0){aR=aC}else{aB=+_(+aC);aR=aB/+h[n>>3]}aQ=aJ+aR}do{if(!(aQ<aE|aQ>aD)){if(w){if(aQ<ar|aQ>F){break}}else{if(aQ<F|aQ>ar){break}}cT[d&15](b,aQ,0,k,0)}}while(0);aC=aw+aC;}while(aC<av)}else{aC=ax;do{if((c[E>>2]|0)==1){aS=+da((c[z>>2]|0)-1|0,aJ+aC)}else{if((a[y]&1)==0){aT=aC}else{aB=+_(+aC);aT=aB/+h[n>>3]}aS=aJ+aT}do{if(!(aS<aD|aS>aE)){if(w){if(aS<ar|aS>F){break}}else{if(aS<F|aS>ar){break}}cT[d&15](b,aS,0,k,0)}}while(0);aC=aw+aC;}while(aC<av)}}}while(0);aC=aF+I;if(aC>az){af=428;break}else{I=aC;at=aG}}if((af|0)==427){i=e;return}else if((af|0)==428){i=e;return}}function df(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0,u=0.0,v=0,w=0.0,x=0.0,y=0.0,z=0,A=0;g=c[3524]|0;i=(b&3|0)==1;j=(b&-4|0)==4;if(e>>>0>3){k=64768+(e*688&-1)|0;l=64772+(e*688&-1)|0}else{k=64772+(e*688&-1)|0;l=64768+(e*688&-1)|0}m=c[k>>2]|0;k=c[l>>2]|0;l=64916+(b*688&-1)|0;if((c[l>>2]|0)==0){return}n=65032+(b*688&-1)|0;o=c[n>>2]|0;do{if((o|0)==-270){if((cO[c[g+72>>2]&255](-270)|0)==0){p=c[n>>2]|0;q=441;break}if(i){c[1398]=1;r=j?0:2}else{c[1398]=j?0:2;r=1}c[1384]=r;c[6586]=-270;if((b|0)!=1){break}c[d>>2]=(c[d>>2]|0)+((c[g+16>>2]|0)>>>1)}else{p=o;q=441}}while(0);L599:do{if((q|0)==441){do{if((p|0)!=0){if((cO[c[g+72>>2]&255](p)|0)==0){break}if((b|0)==5){c[1398]=0}else if((b|0)==2){c[1398]=0}else if((b|0)==6){c[1398]=0}else if((b|0)==1){c[d>>2]=~~(+(c[d>>2]|0)+ +((c[g+20>>2]|0)>>>0>>>0)*2.5);c[1398]=2}else{c[1398]=0}c[1384]=1;c[6586]=c[n>>2];break L599}}while(0);if(i){c[1398]=j?0:2;s=1}else{c[1398]=1;s=j?2:0}c[1384]=s;c[6586]=0}}while(0);if((a[65272+(b*688&-1)|0]&1)==0){c[65128+(b*688&-1)>>2]=c[1398]}else{c[1398]=c[65128+(b*688&-1)>>2]}s=c[l>>2]|0;l=(s&4|0)==0;c[1396]=l?-1:m;do{if((s&2|0)==0){q=481}else{if((a[64788+(e*688&-1)|0]&1)!=0){q=481;break}t=+h[64664+(e*688&-1)>>3];u=+h[64672+(e*688&-1)>>3];if(t<u){if(t>0.0|u<0.0){q=481;break}}else{if(u>0.0|t<0.0){q=481;break}}m=~~(+(c[64768+(e*688&-1)>>2]|0)+(0.0-t)*+h[64776+(e*688&-1)>>3]+.5);c[1394]=m;n=j?1:-1;c[1400]=n;if(!l){c[1396]=m}p=aa(m-k|0,j?-1:1);if(i){o=g+20|0;do{if(p>>>0>((c[o>>2]|0)*3&-1)>>>0){q=469}else{r=c[11692]|0;if(j){if((r&8|0)==0){q=469;break}else{v=k;break}}else{if((r&2|0)==0){q=469;break}else{v=k;break}}}}while(0);if((q|0)==469){v=m}c[1392]=v;c[1392]=aa(c[o>>2]|0,n)+v;break}r=g+16|0;do{if(p>>>0>c[r>>2]<<1>>>0){t=+(m|0);if(j){w=0.0;x=t;q=479}else{y=t;q=478}}else{z=c[11692]|0;if(j){if((z&4|0)!=0){A=k;break}w=0.0;x=+(m|0);q=479;break}else{if((z&1|0)!=0){A=k;break}y=+(m|0);q=478;break}}}while(0);if((q|0)==478){w=+((c[g+24>>2]|0)>>>0>>>0)*(-0.0- +h[65056+(b*688&-1)>>3]);x=y;q=479}if((q|0)==479){A=~~(x+w)}c[1392]=A;c[1392]=A-(c[r>>2]|0)}}while(0);if((q|0)==481){c[1394]=k;c[1400]=aa((a[65072+(b*688&-1)|0]&1)!=0?1:-1,j?-1:1);c[1392]=c[d>>2]}de(b,f);cO[c[g+72>>2]&255](0);return}function dg(b,d){b=b|0;d=d|0;var e=0,f=0,g=0.0,j=0.0,k=0.0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;e=i;i=i+16|0;f=e|0;g=+h[64664+(d*688&-1)>>3];if(g>0.0){j=+h[64672+(d*688&-1)>>3];if(j>0.0){k=j}else{l=488}}else{l=488}do{if((l|0)==488){if((a[64788+(d*688&-1)|0]&1)!=0){k=+h[64672+(d*688&-1)>>3];break}do{if(g<0.0){j=+h[64672+(d*688&-1)>>3];if(j>=0.0){break}if(j<g){m=64768+(d*688&-1)|0}else{m=64772+(d*688&-1)|0}c[64784+(d*688&-1)>>2]=c[m>>2];i=e;return}}while(0);c[64784+(d*688&-1)>>2]=~~(+(c[64768+(d*688&-1)>>2]|0)+(0.0-g)*+h[64776+(d*688&-1)>>3]+.5);n=c[65284+(b*688&-1)>>2]|0;if((n|0)<=-3){i=e;return}o=f;p=65320+(b*688&-1)|0;c[o>>2]=c[p>>2];c[o+4>>2]=c[p+4>>2];c[o+8>>2]=c[p+8>>2];c[o+12>>2]=c[p+12>>2];do{if((c[65280+(b*688&-1)>>2]|0)!=0){j=+h[65304+(b*688&-1)>>3];p=c[(c[3524]|0)+92>>2]|0;if(j<0.0){cK[p&63](+h[3817]);break}else{cK[p&63](j);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[65296+(b*688&-1)>>3]);p=c[(c[3524]|0)+64>>2]|0;if((n|0)<-5){cM[p&511](-2)}else{cM[p&511](n)}p=c[3524]|0;do{if((a[65312+(b*688&-1)|0]&1)==0){if((c[p+96>>2]&1024|0)!=0){q=p;break}c[f>>2]=1;c[f+4>>2]=n;l=511}else{l=511}}while(0);if((l|0)==511){fn(f,p);q=c[3524]|0}n=c[q+56>>2]|0;if((b&3|0)==2){o=64784+(d*688&-1)|0;cN[n&255](c[64768+(b*688&-1)>>2]|0,c[o>>2]|0);cN[c[(c[3524]|0)+60>>2]&255](c[64772+(b*688&-1)>>2]|0,c[o>>2]|0);i=e;return}else{o=64784+(d*688&-1)|0;cN[n&255](c[o>>2]|0,c[64768+(b*688&-1)>>2]|0);cN[c[(c[3524]|0)+60>>2]&255](c[o>>2]|0,c[64772+(b*688&-1)>>2]|0);i=e;return}}}while(0);if(k<g){r=64772+(d*688&-1)|0}else{r=64768+(d*688&-1)|0}c[64784+(d*688&-1)>>2]=c[r>>2];i=e;return}function dh(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0.0,L=0.0,M=0.0,N=0.0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0.0,V=0.0,W=0.0;g=i;i=i+208|0;j=g|0;k=g+24|0;l=g+48|0;m=g+72|0;n=g+96|0;o=g+144|0;p=g+152|0;q=g+200|0;r=(f|0)==1;s=(f|0)==2;if((f-1|0)>>>0>=2){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=133592,v)|0)}t=c[13898]|0;u=(c[8272]|0)>(t|0);L710:do{if(u){w=c[1054]|0;if((a[w+(t*40&-1)|0]&1)==0){x=533;break}y=c[w+(t*40&-1)+36>>2]|0;z=w+(t*40&-1)+32|0;w=c[10036]|0;A=0;while(1){if((A|0)>=(y|0)){break}if((a[w+((c[z>>2]|0)+A|0)|0]|0)==(a[A+134808|0]|0)){A=A+1|0}else{x=533;break L710}}if((A|0)!=1){x=533;break}c[e>>2]=c[e>>2]|f;if(r){z=64728+(b*688&-1)|0;c[z>>2]=c[z>>2]&-2;h[64736+(b*688&-1)>>3]=0.0}else{z=64732+(b*688&-1)|0;c[z>>2]=c[z>>2]&-2;h[64752+(b*688&-1)>>3]=0.0}z=(c[13898]|0)+1|0;c[13898]=z;B=z}else{x=533}}while(0);L722:do{if((x|0)==533){a[25288]=1;L724:do{if((c[64808+(b*688&-1)>>2]|0)==1){z=c[1054]|0;w=(a[z+(t*40&-1)|0]&1)==0;if(w){x=536}else{y=c[10036]|0;C=a[y+(c[z+(t*40&-1)+32>>2]|0)|0]|0;if((C<<24>>24|0)==39|(C<<24>>24|0)==34){D=y}else{x=536}}if((x|0)==536){y=c[10810]|0;if((y|0)==0){x=558;break}C=z+(t*40&-1)+36|0;E=z+(t*40&-1)+32|0;F=c[10036]|0;G=y;L731:while(1){y=c[G+4>>2]|0;L733:do{if(!(w|u^1)){H=c[C>>2]|0;I=0;while(1){if((I|0)>=(H|0)){break}if((a[F+((c[E>>2]|0)+I|0)|0]|0)==(a[y+I|0]|0)){I=I+1|0}else{break L733}}if((a[y+I|0]|0)==0){break L731}}}while(0);y=c[G>>2]|0;if((y|0)==0){x=558;break L724}else{G=y}}if((a[G+8|0]&1)!=0){x=558;break}if((c[G+16>>2]|0)==3){D=F}else{x=558;break}}L743:do{if(u){L745:do{if(!w){E=c[z+(t*40&-1)+36>>2]|0;C=z+(t*40&-1)+32|0;y=0;while(1){if((y|0)>=(E|0)){break}if((a[D+((c[C>>2]|0)+y|0)|0]|0)==(a[y+103664|0]|0)){y=y+1|0}else{break L745}}if((y|0)==1){J=0;break L743}}}while(0);a[14176]=1;is(m);a[14176]=0;if((c[m>>2]|0)==3){J=c[m+8>>2]|0;break}else{c[13898]=t;J=0;break}}else{J=0}}while(0);if((t1(J,64813+(b*688&-1)|0,n,o)|0)==0){K=0.0}else{L=+t2(n);K=L+ +h[o>>3]}uu(J);M=K}else{x=558}}while(0);do{if((x|0)==558){A=is(l)|0;z=c[A>>2]|0;if((z|0)==1){N=+(c[A+8>>2]|0)}else if((z|0)==2){N=+h[A+8>>3]}else if((z|0)==3){N=+uz(c[A+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}A=l|0;if((c[A>>2]|0)!=3){M=N;break}uu(c[l+8>>2]|0);c[A>>2]=1;M=N}}while(0);a[25288]=0;A=c[13898]|0;z=c[8272]|0;if((A|0)>=(z|0)){uf(A,129840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}w=c[1054]|0;F=(a[w+(A*40&-1)|0]&1)==0;L772:do{if(!F){G=c[w+(A*40&-1)+36>>2]|0;C=w+(A*40&-1)+32|0;E=c[10036]|0;I=0;while(1){if((I|0)>=(G|0)){x=570;break}if((a[E+((c[C>>2]|0)+I|0)|0]|0)==(a[I+103664|0]|0)){I=I+1|0}else{break}}do{if((x|0)==570){if((I|0)!=1){break}uf(A,129840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if(F){break}I=c[w+(A*40&-1)+36>>2]|0;C=w+(A*40&-1)+32|0;E=c[10036]|0;G=0;while(1){if((G|0)>=(I|0)){x=576;break}if((a[E+((c[C>>2]|0)+G|0)|0]|0)==(a[G+136608|0]|0)){G=G+1|0}else{break}}do{if((x|0)==576){if((G|0)!=1){break}C=A+1|0;c[13898]=C;if((C|0)>=(z|0)){uf(C,127176,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}I=(a[w+(C*40&-1)|0]&1)==0;if(I){uf(C,126040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}H=c[w+(C*40&-1)+36>>2]|0;O=w+(C*40&-1)+32|0;P=0;while(1){if((P|0)>=(H|0)){x=582;break}if((a[E+((c[O>>2]|0)+P|0)|0]|0)==(a[P+103664|0]|0)){P=P+1|0}else{break}}do{if((x|0)==582){if((P|0)!=1){break}uf(C,127176,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if(I){uf(C,126040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}P=c[w+(C*40&-1)+36>>2]|0;O=w+(C*40&-1)+32|0;H=0;while(1){if((H|0)>=(P|0)){break}if((a[E+((c[O>>2]|0)+H|0)|0]|0)==(a[H+134808|0]|0)){H=H+1|0}else{x=695;break}}if((x|0)==695){uf(C,126040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((H|0)!=1){uf(C,126040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[e>>2]=c[e>>2]|f;if(r){O=64728+(b*688&-1)|0;c[O>>2]=c[O>>2]|1;h[64736+(b*688&-1)>>3]=M}else{O=64732+(b*688&-1)|0;c[O>>2]=c[O>>2]|1;h[64752+(b*688&-1)>>3]=M}O=(c[13898]|0)+1|0;c[13898]=O;B=O;break L722}}while(0);if(F){break}E=c[w+(A*40&-1)+36>>2]|0;G=w+(A*40&-1)+32|0;O=c[10036]|0;P=0;while(1){if((P|0)>=(E|0)){break}if((a[O+((c[G>>2]|0)+P|0)|0]|0)==(a[P+138304|0]|0)){P=P+1|0}else{break L772}}if((P|0)!=1){break}uf(A,123680,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[e>>2]=c[e>>2]&(f^-1);if(r){c[64728+(b*688&-1)>>2]=0;h[64744+(b*688&-1)>>3]=0.0}else{c[64732+(b*688&-1)>>2]=0;h[64760+(b*688&-1)>>3]=0.0}h[d>>3]=M;B=c[13898]|0}}while(0);d=c[8272]|0;l=(B|0)>=(d|0);L830:do{if((c[e>>2]&f|0)==0){if(l){break}J=c[1054]|0;L923:do{if((a[J+(B*40&-1)|0]&1)!=0){o=c[J+(B*40&-1)+36>>2]|0;n=J+(B*40&-1)+32|0;t=c[10036]|0;m=0;while(1){if((m|0)>=(o|0)){break}if((a[t+((c[n>>2]|0)+m|0)|0]|0)==(a[m+103664|0]|0)){m=m+1|0}else{break L923}}if((m|0)==1){break L830}}}while(0);J=c[1054]|0;L930:do{if((a[J+(B*40&-1)|0]&1)!=0){n=c[J+(B*40&-1)+36>>2]|0;t=J+(B*40&-1)+32|0;o=c[10036]|0;P=0;while(1){if((P|0)>=(n|0)){break}if((a[o+((c[t>>2]|0)+P|0)|0]|0)==(a[P+136608|0]|0)){P=P+1|0}else{break L930}}if((P|0)!=1){break}uf(B,122232,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);J=c[1054]|0;if((a[J+(B*40&-1)|0]&1)==0){break}t=c[J+(B*40&-1)+36>>2]|0;o=J+(B*40&-1)+32|0;J=c[10036]|0;n=0;while(1){if((n|0)>=(t|0)){break}if((a[J+((c[o>>2]|0)+n|0)|0]|0)==(a[n+138304|0]|0)){n=n+1|0}else{break L830}}if((n|0)!=1){break}uf(B,122232,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if(l){uf(B,129840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=c[1054]|0;L835:do{if((a[o+(B*40&-1)|0]&1)!=0){J=c[o+(B*40&-1)+36>>2]|0;t=o+(B*40&-1)+32|0;m=c[10036]|0;D=0;while(1){if((D|0)>=(J|0)){break}if((a[m+((c[t>>2]|0)+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{break L835}}if((D|0)!=1){break}uf(B,129840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);o=c[1054]|0;L843:do{if((a[o+(B*40&-1)|0]&1)!=0){n=c[o+(B*40&-1)+36>>2]|0;t=o+(B*40&-1)+32|0;m=c[10036]|0;J=0;while(1){if((J|0)>=(n|0)){break}if((a[m+((c[t>>2]|0)+J|0)|0]|0)==(a[J+136608|0]|0)){J=J+1|0}else{break L843}}if((J|0)!=1){break}t=B+1|0;c[13898]=t;if((t|0)>=(d|0)){uf(t,127176,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=(a[o+(t*40&-1)|0]&1)==0;L853:do{if(!n){D=c[o+(t*40&-1)+36>>2]|0;P=o+(t*40&-1)+32|0;u=0;while(1){if((u|0)>=(D|0)){break}if((a[m+((c[P>>2]|0)+u|0)|0]|0)==(a[u+103664|0]|0)){u=u+1|0}else{break L853}}if((u|0)!=1){break}uf(t,127176,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);L861:do{if((c[64808+(b*688&-1)>>2]|0)==1){do{if(n){x=627}else{J=a[m+(c[o+(t*40&-1)+32>>2]|0)|0]|0;if(!((J<<24>>24|0)==39|(J<<24>>24|0)==34)){x=627;break}Q=j;x=639}}while(0);if((x|0)==627){u=c[10810]|0;if((u|0)==0){x=649;break}J=o+(t*40&-1)+36|0;P=o+(t*40&-1)+32|0;D=u;L869:while(1){u=c[D+4>>2]|0;L871:do{if(!n){C=c[J>>2]|0;H=0;while(1){if((H|0)>=(C|0)){break}if((a[m+((c[P>>2]|0)+H|0)|0]|0)==(a[u+H|0]|0)){H=H+1|0}else{break L871}}if((a[u+H|0]|0)==0){break L869}}}while(0);u=c[D>>2]|0;if((u|0)==0){x=649;break L861}else{D=u}}if((a[D+8|0]&1)!=0){x=649;break}if((c[D+16>>2]|0)!=3){x=649;break}P=j;if(n){R=P;x=643}else{Q=P;x=639}}L881:do{if((x|0)==639){P=c[o+(t*40&-1)+36>>2]|0;J=o+(t*40&-1)+32|0;u=0;while(1){if((u|0)>=(P|0)){break}if((a[m+((c[J>>2]|0)+u|0)|0]|0)==(a[u+103664|0]|0)){u=u+1|0}else{R=Q;x=643;break L881}}if((u|0)==1){S=0;T=Q}else{R=Q;x=643}}}while(0);do{if((x|0)==643){a[14176]=1;is(j);a[14176]=0;if((c[j>>2]|0)==3){S=c[j+8>>2]|0;T=R;break}else{c[13898]=t;S=0;T=R;break}}}while(0);if((t1(S,64813+(b*688&-1)|0,p,q)|0)==0){U=0.0}else{M=+t2(p);U=M+ +h[q>>3]}uu(S);V=U}else{x=649}}while(0);do{if((x|0)==649){t=is(k)|0;m=c[t>>2]|0;if((m|0)==1){W=+(c[t+8>>2]|0)}else if((m|0)==2){W=+h[t+8>>3]}else if((m|0)==3){W=+uz(c[t+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t=k|0;if((c[t>>2]|0)!=3){V=W;break}uu(c[k+8>>2]|0);c[t>>2]=1;V=W}}while(0);if(r){t=64728+(b*688&-1)|0;c[t>>2]=c[t>>2]|2;h[64744+(b*688&-1)>>3]=V;break L830}else{t=64732+(b*688&-1)|0;c[t>>2]=c[t>>2]|2;h[64760+(b*688&-1)>>3]=V;break L830}}}while(0);o=c[1054]|0;L910:do{if((a[o+(B*40&-1)|0]&1)!=0){t=c[o+(B*40&-1)+36>>2]|0;m=o+(B*40&-1)+32|0;n=c[10036]|0;D=0;while(1){if((D|0)>=(t|0)){break}if((a[n+((c[m>>2]|0)+D|0)|0]|0)==(a[D+138304|0]|0)){D=D+1|0}else{break L910}}if((D|0)!=1){break}uf(B,123680,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if(r){o=64728+(b*688&-1)|0;c[o>>2]=c[o>>2]&-3;h[64744+(b*688&-1)>>3]=0.0;break}else{o=64732+(b*688&-1)|0;c[o>>2]=c[o>>2]&-3;h[64760+(b*688&-1)>>3]=0.0;break}}}while(0);if((c[e>>2]&f|0)==0){i=g;return}do{if(r){f=64728+(b*688&-1)|0;if((c[f>>2]|0)!=3){break}if(+h[64744+(b*688&-1)>>3]>=+h[64736+(b*688&-1)>>3]){break}uh(c[13898]|0,120752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[f>>2]=0}}while(0);if(!s){i=g;return}s=64732+(b*688&-1)|0;if((c[s>>2]|0)!=3){i=g;return}if(+h[64760+(b*688&-1)>>3]>=+h[64752+(b*688&-1)>>3]){i=g;return}uh(c[13898]|0,120752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[s>>2]=0;i=g;return}function di(a,b,d,e,f){a=a|0;b=+b;d=d|0;e=e|0;f=f|0;f=i;a=e;e=i;i=i+56|0;uD(e,a,56);do{if((d|0)!=0){a=ga(d,0)|0;if((a|0)<=(c[216]|0)){break}c[216]=a}}while(0);i=f;return}function dj(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0.0,A=0.0;g=i;i=i+8|0;j=g|0;c[j>>2]=f;k=c[13898]|0;l=c[8272]|0;m=(l|0)>(k|0);if(!m){uf(k,116248,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}n=c[1054]|0;do{if((a[n+(k*40&-1)|0]&1)!=0){o=c[n+(k*40&-1)+36>>2]|0;p=n+(k*40&-1)+32|0;q=c[10036]|0;r=0;while(1){if((r|0)>=(o|0)){s=723;break}if((a[q+((c[p>>2]|0)+r|0)|0]|0)==(a[r+78864|0]|0)){r=r+1|0}else{break}}do{if((s|0)==723){if((r|0)!=1){break}c[64728+(b*688&-1)>>2]=0;c[64732+(b*688&-1)>>2]=0;t=f;i=g;return t|0}}while(0);if(m){break}uf(k,116248,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);m=c[1054]|0;f=(a[m+(k*40&-1)|0]&1)==0;L984:do{if(f){s=742}else{n=c[m+(k*40&-1)+36>>2]|0;r=m+(k*40&-1)+32|0;p=c[10036]|0;q=0;while(1){if((q|0)>=(n|0)){s=730;break}if((a[p+((c[r>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break}}do{if((s|0)==730){if((q|0)!=1){break}uf(k,116248,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);if(f){s=742;break}q=c[m+(k*40&-1)+36>>2]|0;r=m+(k*40&-1)+32|0;p=c[10036]|0;n=0;while(1){if((n|0)>=(q|0)){s=736;break}if((a[p+((c[r>>2]|0)+n|0)|0]|0)==(a[n+187176|0]|0)){n=n+1|0}else{break}}if((s|0)==736){if((n|0)==2){u=k;w=l;break}}if(f){s=742;break}r=c[m+(k*40&-1)+36>>2]|0;p=m+(k*40&-1)+32|0;q=c[10036]|0;o=0;while(1){if((o|0)>=(r|0)){break}if((a[q+((c[p>>2]|0)+o|0)|0]|0)==(a[o+183584|0]|0)){o=o+1|0}else{s=742;break L984}}if((o|0)==1){u=k;w=l}else{s=742}}}while(0);if((s|0)==742){dh(b,d,j,1);u=c[13898]|0;w=c[8272]|0}l=(w|0)>(u|0);if(!l){uf(u,114504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}k=c[1054]|0;do{if((a[k+(u*40&-1)|0]&1)==0){s=750}else{m=c[k+(u*40&-1)+36>>2]|0;f=k+(u*40&-1)+32|0;p=c[10036]|0;q=0;while(1){if((q|0)>=(m|0)){s=748;break}if((a[p+((c[f>>2]|0)+q|0)|0]|0)==(a[q+187176|0]|0)){q=q+1|0}else{break}}if((s|0)==748){if((q|0)==2){x=k;y=p;break}}if(l){s=750;break}uf(u,114504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);do{if((s|0)==750){l=c[1054]|0;if((a[l+(u*40&-1)|0]&1)==0){uf(u,114504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}k=c[l+(u*40&-1)+36>>2]|0;f=l+(u*40&-1)+32|0;m=c[10036]|0;o=0;while(1){if((o|0)>=(k|0)){break}if((a[m+((c[f>>2]|0)+o|0)|0]|0)==(a[o+183584|0]|0)){o=o+1|0}else{s=774;break}}if((s|0)==774){uf(u,114504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((o|0)==1){x=l;y=m;break}uf(u,114504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);f=u+1|0;c[13898]=f;L1033:do{if((w|0)>(f|0)){if((a[x+(f*40&-1)|0]&1)==0){s=762;break}u=c[x+(f*40&-1)+36>>2]|0;k=x+(f*40&-1)+32|0;p=0;while(1){if((p|0)>=(u|0)){break}if((a[y+((c[k>>2]|0)+p|0)|0]|0)==(a[p+78864|0]|0)){p=p+1|0}else{s=762;break L1033}}if((p|0)!=1){s=762}}else{s=762}}while(0);if((s|0)==762){dh(b,e,j,2)}s=c[j>>2]|0;if((s&3|0)!=0){t=s;i=g;return t|0}z=+h[e>>3];A=+h[d>>3];if(z>=A){t=s;i=g;return t|0}h[d>>3]=z;h[e>>3]=A;e=64656+(b*688&-1)|0;c[e>>2]=c[e>>2]|2;t=s;i=g;return t|0}function dk(){var b=0,d=0,e=0.0,f=0.0,g=0.0,j=0,k=0.0,l=0.0,m=0.0,n=0.0;b=i;d=c[16679]|0;e=+h[8341];if((d&1|0)==0|e<8.988465674311579e+307){f=e}else{e=+h[64664+((c[34]|0)*688&-1)>>3];if((a[64788]&1)==0){g=e}else{g=+Z(+(e*+h[8100]))}h[8341]=g;f=g}j=(a[66852]&1)==0;do{if(j){k=f}else{if(f>0.0){g=+_(+f);k=g/+h[8358];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=112752,c[v+8>>2]=56904,h[v+16>>3]=f,v)|0);return 0}}}while(0);h[8341]=k;f=+h[8342];if((d&2|0)==0|f>-8.988465674311579e+307){l=f}else{f=+h[64672+((c[34]|0)*688&-1)>>3];if((a[64788]&1)==0){m=f}else{m=+Z(+(f*+h[8100]))}h[8342]=m;l=m}do{if(j){n=l}else{if(l>0.0){m=+_(+l);n=m/+h[8358];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=112752,c[v+8>>2]=56904,h[v+16>>3]=l,v)|0);return 0}}}while(0);h[8342]=n;if(k<=n){i=b;return 1}h[8342]=k;h[8341]=n;i=b;return 1}function dl(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0.0,M=0.0,N=0,O=0,P=0,Q=0.0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0.0;e=i;i=i+328|0;f=e|0;g=e+24|0;j=e+48|0;k=e+72|0;l=e+96|0;m=e+120|0;n=e+144|0;o=e+152|0;p=e+160|0;q=e+208|0;r=e+216|0;s=e+264|0;t=e+272|0;u=e+320|0;c[o>>2]=d;uE(b|0,0,40);dm(o,n);d=c[o>>2]|0;c[b>>2]=d;w=c[n>>2]|0;L1080:do{if((w|0)>-1){if((c[64808+((w+2|0)*688&-1)>>2]|0)!=1){x=826;break}y=c[13898]|0;z=c[1054]|0;A=(a[z+(y*40&-1)|0]&1)==0;if(A){x=804}else{B=c[10036]|0;C=a[B+(c[z+(y*40&-1)+32>>2]|0)|0]|0;if((C<<24>>24|0)==39|(C<<24>>24|0)==34){D=B}else{x=804}}if((x|0)==804){B=c[10810]|0;if((B|0)==0){x=826;break}C=c[8272]|0;E=z+(y*40&-1)+36|0;F=z+(y*40&-1)+32|0;G=c[10036]|0;H=B;L1088:while(1){B=c[H+4>>2]|0;L1090:do{if(!((C|0)<=(y|0)|A)){I=c[E>>2]|0;J=0;while(1){if((J|0)>=(I|0)){break}if((a[G+((c[F>>2]|0)+J|0)|0]|0)==(a[B+J|0]|0)){J=J+1|0}else{break L1090}}if((a[B+J|0]|0)==0){break L1088}}}while(0);B=c[H>>2]|0;if((B|0)==0){x=826;break L1080}else{H=B}}if((a[H+8|0]&1)!=0){x=826;break}if((c[H+16>>2]|0)==3){D=G}else{x=826;break}}L1100:do{if((y|0)<(c[8272]|0)){L1102:do{if(!A){F=c[z+(y*40&-1)+36>>2]|0;E=z+(y*40&-1)+32|0;C=0;while(1){if((C|0)>=(F|0)){break}if((a[D+((c[E>>2]|0)+C|0)|0]|0)==(a[C+103664|0]|0)){C=C+1|0}else{break L1102}}if((C|0)==1){K=0;break L1100}}}while(0);a[14176]=1;is(l);a[14176]=0;if((c[l>>2]|0)==3){K=c[l+8>>2]|0;break}else{c[13898]=y;K=0;break}}else{K=0}}while(0);if((t1(K,66189,p,q)|0)!=0){L=+t2(p);h[b+16>>3]=L+ +h[q>>3]}uu(K)}else{x=826}}while(0);if((x|0)==826){K=is(k)|0;q=c[K>>2]|0;if((q|0)==1){M=+(c[K+8>>2]|0)}else if((q|0)==2){M=+h[K+8>>3]}else if((q|0)==3){M=+uz(c[K+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}K=k|0;if((c[K>>2]|0)==3){uu(c[k+8>>2]|0);c[K>>2]=1}h[b+16>>3]=M}K=c[13898]|0;L1128:do{if((c[8272]|0)>(K|0)){k=c[1054]|0;if((a[k+(K*40&-1)|0]&1)==0){x=874;break}q=c[k+(K*40&-1)+36>>2]|0;p=k+(K*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(q|0)){break}if((a[k+((c[p>>2]|0)+l|0)|0]|0)==(a[l+148464|0]|0)){l=l+1|0}else{x=874;break L1128}}if((l|0)!=1){x=874;break}c[13898]=K+1;dm(o,n);p=c[o>>2]|0;c[b+4>>2]=p;k=c[n>>2]|0;L1136:do{if((k|0)>-1){if((c[64808+((k+1|0)*688&-1)>>2]|0)!=1){break}q=c[13898]|0;D=c[1054]|0;w=(a[D+(q*40&-1)|0]&1)==0;if(w){x=844}else{y=c[10036]|0;z=a[y+(c[D+(q*40&-1)+32>>2]|0)|0]|0;if((z<<24>>24|0)==39|(z<<24>>24|0)==34){N=y}else{x=844}}if((x|0)==844){y=c[10810]|0;if((y|0)==0){break}z=c[8272]|0;A=D+(q*40&-1)+36|0;G=D+(q*40&-1)+32|0;H=c[10036]|0;E=y;L1144:while(1){y=c[E+4>>2]|0;L1146:do{if(!((z|0)<=(q|0)|w)){F=c[A>>2]|0;J=0;while(1){if((J|0)>=(F|0)){break}if((a[H+((c[G>>2]|0)+J|0)|0]|0)==(a[y+J|0]|0)){J=J+1|0}else{break L1146}}if((a[y+J|0]|0)==0){break L1144}}}while(0);y=c[E>>2]|0;if((y|0)==0){break L1136}else{E=y}}if((a[E+8|0]&1)!=0){break}if((c[E+16>>2]|0)==3){N=H}else{break}}L1156:do{if((q|0)<(c[8272]|0)){L1158:do{if(!w){G=c[D+(q*40&-1)+36>>2]|0;A=D+(q*40&-1)+32|0;z=0;while(1){if((z|0)>=(G|0)){break}if((a[N+((c[A>>2]|0)+z|0)|0]|0)==(a[z+103664|0]|0)){z=z+1|0}else{break L1158}}if((z|0)==1){O=0;break L1156}}}while(0);a[14176]=1;is(j);a[14176]=0;if((c[j>>2]|0)==3){O=c[j+8>>2]|0;break}else{c[13898]=q;O=0;break}}else{O=0}}while(0);if((t1(O,65501,r,s)|0)!=0){M=+t2(r);h[b+24>>3]=M+ +h[s>>3]}uu(O);P=p;break L1128}}while(0);k=is(g)|0;l=c[k>>2]|0;if((l|0)==1){Q=+(c[k+8>>2]|0)}else if((l|0)==2){Q=+h[k+8>>3]}else if((l|0)==3){Q=+uz(c[k+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=g|0;if((c[k>>2]|0)==3){uu(c[g+8>>2]|0);c[k>>2]=1}h[b+24>>3]=Q;P=p}else{x=874}}while(0);if((x|0)==874){h[b+24>>3]=0.0;c[b+4>>2]=d;P=d}d=c[13898]|0;g=c[8272]|0;L1185:do{if((g|0)>(d|0)){O=c[1054]|0;if((a[O+(d*40&-1)|0]&1)==0){break}s=c[O+(d*40&-1)+36>>2]|0;r=O+(d*40&-1)+32|0;j=c[10036]|0;N=0;while(1){if((N|0)>=(s|0)){break}if((a[j+((c[r>>2]|0)+N|0)|0]|0)==(a[N+148464|0]|0)){N=N+1|0}else{break L1185}}if((N|0)!=1){break}r=d+1|0;s=(a[O+(r*40&-1)|0]&1)==0;if(!s){p=a[j+(c[O+(r*40&-1)+32>>2]|0)|0]|0;if((p<<24>>24|0)==39|(p<<24>>24|0)==34){break}}p=c[10810]|0;L1196:do{if((p|0)==0){R=O+(r*40&-1)+36|0;S=O+(r*40&-1)+32|0}else{K=O+(r*40&-1)+36|0;k=O+(r*40&-1)+32|0;l=p;L1199:while(1){q=c[l+4>>2]|0;L1201:do{if(!((g|0)<=(r|0)|s)){D=c[K>>2]|0;w=0;while(1){if((w|0)>=(D|0)){break}if((a[j+((c[k>>2]|0)+w|0)|0]|0)==(a[q+w|0]|0)){w=w+1|0}else{break L1201}}if((a[q+w|0]|0)==0){break L1199}}}while(0);q=c[l>>2]|0;if((q|0)==0){R=K;S=k;break L1196}else{l=q}}if((a[l+8|0]&1)!=0){R=K;S=k;break}if((c[l+16>>2]|0)==3){break L1185}else{R=K;S=k}}}while(0);p=c[R>>2]|0;O=c[S>>2]|0;L1211:do{if((p|0)>0&(s^1)){N=0;q=0;D=O;while(1){if((a[N+205224|0]|0)==(a[j+(N+D|0)|0]|0)){T=D;U=q}else{if((N|0)!=7){break}T=D-1|0;U=1}z=N+1|0;if((z|0)<(U+p|0)){N=z;q=U;D=T}else{x=899;break}}if((x|0)==899){if((U|0)!=0){break L1185}if((N|0)==6|(N|0)==12){break L1185}}if((p|0)>0&(s^1)){V=0}else{break}do{if((a[V+217064|0]|0)!=(a[j+(V+O|0)|0]|0)){break L1211}V=V+1|0;}while((V|0)<(p|0));if((V|0)==3){break L1185}}}while(0);c[13898]=r;dm(o,n);c[b+8>>2]=c[o>>2];p=c[n>>2]|0;L1227:do{if((p|0)>-1){if((c[64808+(p*688&-1)>>2]|0)!=1){break}O=c[13898]|0;j=c[1054]|0;s=(a[j+(O*40&-1)|0]&1)==0;if(s){x=909}else{N=c[10036]|0;D=a[N+(c[j+(O*40&-1)+32>>2]|0)|0]|0;if((D<<24>>24|0)==39|(D<<24>>24|0)==34){W=N}else{x=909}}if((x|0)==909){N=c[10810]|0;if((N|0)==0){break}D=c[8272]|0;q=j+(O*40&-1)+36|0;k=j+(O*40&-1)+32|0;K=c[10036]|0;l=N;L1235:while(1){N=c[l+4>>2]|0;L1237:do{if(!((D|0)<=(O|0)|s)){z=c[q>>2]|0;H=0;while(1){if((H|0)>=(z|0)){break}if((a[K+((c[k>>2]|0)+H|0)|0]|0)==(a[N+H|0]|0)){H=H+1|0}else{break L1237}}if((a[N+H|0]|0)==0){break L1235}}}while(0);N=c[l>>2]|0;if((N|0)==0){break L1227}else{l=N}}if((a[l+8|0]&1)!=0){break}if((c[l+16>>2]|0)==3){W=K}else{break}}L1247:do{if((O|0)<(c[8272]|0)){L1249:do{if(!s){k=c[j+(O*40&-1)+36>>2]|0;q=j+(O*40&-1)+32|0;D=0;while(1){if((D|0)>=(k|0)){break}if((a[W+((c[q>>2]|0)+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{break L1249}}if((D|0)==1){X=0;break L1247}}}while(0);a[14176]=1;is(f);a[14176]=0;if((c[f>>2]|0)==3){X=c[f+8>>2]|0;break}else{c[13898]=O;X=0;break}}else{X=0}}while(0);if((t1(X,64813,t,u)|0)!=0){Q=+t2(t);h[b+32>>3]=Q+ +h[u>>3]}uu(X);i=e;return}}while(0);p=is(m)|0;r=c[p>>2]|0;if((r|0)==1){Y=+(c[p+8>>2]|0)}else if((r|0)==2){Y=+h[p+8>>3]}else if((r|0)==3){Y=+uz(c[p+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=m|0;if((c[p>>2]|0)==3){uu(c[m+8>>2]|0);c[p>>2]=1}h[b+32>>3]=Y;i=e;return}}while(0);h[b+32>>3]=0.0;c[b+8>>2]=P;i=e;return}function dm(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;e=c[13898]|0;f=c[1054]|0;g=c[f+(e*40&-1)+36>>2]|0;h=c[f+(e*40&-1)+32>>2]|0;i=(a[f+(e*40&-1)|0]&1)==0;L1278:do{if((g|0)>0&(i^1)){f=c[10036]|0;j=0;k=0;l=h;while(1){if((a[j+101440|0]|0)==(a[f+(j+l|0)|0]|0)){m=l;n=k}else{if((j|0)!=3){break}m=l-1|0;n=1}o=j+1|0;if((o|0)<(n+g|0)){j=o;k=n;l=m}else{p=950;break}}do{if((p|0)==950){if((n|0)==0){if(!((j|0)==5|(j|0)==2)){break}}c[13898]=e+1;c[b>>2]=0;p=990;break L1278}}while(0);if(!((g|0)>0&(i^1))){p=989;break}j=c[10036]|0;l=0;k=0;f=h;while(1){if((a[l+100944|0]|0)==(a[j+(l+f|0)|0]|0)){q=f;r=k}else{if((l|0)!=3){break}q=f-1|0;r=1}o=l+1|0;if((o|0)<(r+g|0)){l=o;k=r;f=q}else{p=959;break}}do{if((p|0)==959){if((r|0)==0){if(!((l|0)==6|(l|0)==2)){break}}c[13898]=e+1;c[b>>2]=1;p=991;break L1278}}while(0);if(!((g|0)>0&(i^1))){p=989;break}l=c[10036]|0;f=0;k=0;j=h;while(1){if((a[f+148408|0]|0)==(a[l+(f+j|0)|0]|0)){s=j;t=k}else{if((f|0)!=2){break}s=j-1|0;t=1}o=f+1|0;if((o|0)<(t+g|0)){f=o;k=t;j=s}else{p=968;break}}do{if((p|0)==968){if((t|0)==0){if(!((f|0)==5|(f|0)==1)){break}}c[13898]=e+1;c[b>>2]=2;p=992;break L1278}}while(0);if(!((g|0)>0&(i^1))){p=989;break}f=c[10036]|0;j=0;k=0;l=h;while(1){if((a[j+142056|0]|0)==(a[f+(j+l|0)|0]|0)){u=l;v=k}else{if((j|0)!=2){break}u=l-1|0;v=1}o=j+1|0;if((o|0)<(v+g|0)){j=o;k=v;l=u}else{p=977;break}}do{if((p|0)==977){if((v|0)==0){if(!((j|0)==6|(j|0)==1)){break}}c[13898]=e+1;c[b>>2]=3;p=992;break L1278}}while(0);if(!((g|0)>0&(i^1))){p=989;break}j=c[10036]|0;l=0;k=0;f=h;while(1){if((a[l+99320|0]|0)==(a[j+(l+f|0)|0]|0)){w=f;x=k}else{if((l|0)!=4){p=989;break L1278}w=f-1|0;x=1}o=l+1|0;if((o|0)<(x+g|0)){l=o;k=x;f=w}else{break}}if((x|0)==0){if(!((l|0)==3|(l|0)==9)){p=989;break}}c[13898]=e+1;c[b>>2]=4;p=992}else{p=989}}while(0);if((p|0)==989){e=c[b>>2]|0;if((e|0)==0){p=990}else if((e|0)==1){p=991}else{p=992}}if((p|0)==992){c[d>>2]=-1;return}else if((p|0)==990){c[d>>2]=0;return}else if((p|0)==991){c[d>>2]=4;return}}function dn(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0;b=c[8272]|0;d=c[13898]|0;L1352:while(1){e=d+1|0;c[13898]=e;L1354:do{if((b|0)>(d|0)){f=c[1054]|0;if((a[f+(d*40&-1)|0]&1)==0){g=1004;break}h=c[f+(d*40&-1)+36>>2]|0;i=f+(d*40&-1)+32|0;f=c[10036]|0;j=0;while(1){if((j|0)>=(h|0)){break}if((a[f+((c[i>>2]|0)+j|0)|0]|0)==(a[j+78864|0]|0)){j=j+1|0}else{g=1004;break L1354}}if(!((j|0)!=1&(e|0)<(b|0))){g=1011;break L1352}}else{g=1004}}while(0);if((g|0)==1004){g=0;if((e|0)>=(b|0)){g=1012;break}}i=c[1054]|0;if((a[i+(e*40&-1)|0]&1)==0){d=e;continue}f=c[i+(e*40&-1)+36>>2]|0;h=i+(e*40&-1)+32|0;i=c[10036]|0;k=0;while(1){if((k|0)>=(f|0)){break}if((a[i+((c[h>>2]|0)+k|0)|0]|0)==(a[k+103664|0]|0)){k=k+1|0}else{d=e;continue L1352}}if((k|0)==1){g=1013;break}else{d=e}}if((g|0)==1013){return}else if((g|0)==1011){return}else if((g|0)==1012){return}}function dp(b,d,e,f){b=b|0;d=d|0;e=+e;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0,s=0,t=0;g=i;i=i+24|0;j=g|0;k=(d|0)!=0;if((f|0)<0&(k^1)){i=g;return}if((a[64976+(b*688&-1)|0]&1)==0){c[64920+(b*688&-1)>>2]=3}l=64944+(b*688&-1)|0;b=c[l>>2]|0;m=j+16|0;c[m>>2]=b;h[j>>3]=-1.7976931348623157e+308;n=j;j=b;while(1){o=n+16|0;if((j|0)==0){p=1022;break}q=+h[j>>3];if(q>=e){p=1021;break}n=j;j=c[j+16>>2]|0}do{if((p|0)==1021){if(q>e){p=1022;break}if(q!=e){uh(-1,105096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);r=c[o>>2]|0}else{r=j}if((c[r+12>>2]|0)<(f|0)){i=g;return}n=r+8|0;b=c[n>>2]|0;if((b|0)==0){s=r;break}uu(b);c[n>>2]=0;s=r}}while(0);if((p|0)==1022){p=ut(24)|0;if((p|0)==0){gk();t=ut(24)|0}else{t=p}p=t;h[t>>3]=e;c[t+12>>2]=f;c[t+16>>2]=c[o>>2];c[o>>2]=p;s=p}if(k){c[s+8>>2]=bP(d|0)|0}else{c[s+8>>2]=0}c[l>>2]=c[m>>2];i=g;return}function dq(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;e=c[13898]|0;if((c[8272]|0)<=(e|0)){i=d;return}f=c[1054]|0;if((a[f+(e*40&-1)|0]&1)==0){i=d;return}g=c[f+(e*40&-1)+36>>2]|0;h=f+(e*40&-1)+32|0;f=c[10036]|0;j=0;while(1){if((j|0)>=(g|0)){break}if((a[f+((c[h>>2]|0)+j|0)|0]|0)==(a[j+78280|0]|0)){j=j+1|0}else{k=1056;break}}if((k|0)==1056){i=d;return}if((j|0)!=1){i=d;return}c[13898]=e+1;e=64648+(b*688&-1)|0;c[e>>2]=dj(b,64664+(b*688&-1)|0,64672+(b*688&-1)|0,c[e>>2]|0)|0;e=c[13898]|0;L1421:do{if((c[8272]|0)>(e|0)){b=c[1054]|0;if((a[b+(e*40&-1)|0]&1)==0){break}j=c[b+(e*40&-1)+36>>2]|0;k=b+(e*40&-1)+32|0;b=c[10036]|0;h=0;while(1){if((h|0)>=(j|0)){break}if((a[b+((c[k>>2]|0)+h|0)|0]|0)==(a[h+78864|0]|0)){h=h+1|0}else{break L1421}}if((h|0)!=1){break}c[13898]=e+1;i=d;return}}while(0);uf(e,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function dr(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;f=i;g=c[13898]|0;if((c[8272]|0)<=(g|0)){h=e;i=f;return h|0}j=c[1054]|0;if((a[j+(g*40&-1)|0]&1)==0){h=e;i=f;return h|0}k=c[j+(g*40&-1)+36>>2]|0;l=j+(g*40&-1)+32|0;m=c[10036]|0;n=0;while(1){if((n|0)>=(k|0)){break}if((a[m+((c[l>>2]|0)+n|0)|0]|0)==(a[n+78280|0]|0)){n=n+1|0}else{h=e;o=1085;break}}if((o|0)==1085){i=f;return h|0}if((n|0)!=1){h=e;i=f;return h|0}n=g+1|0;c[13898]=n;L1447:do{if((a[j+(n*40&-1)|0]&1)==0){p=e}else{if((bO(d[m+(c[j+(n*40&-1)+32>>2]|0)|0]|0|0)|0)==0){if((a[(c[10036]|0)+(c[(c[1054]|0)+(n*40&-1)+32>>2]|0)|0]|0)!=95){p=e;break}}g=c[13898]|0;l=g+1|0;if((c[8272]|0)<=(l|0)){p=e;break}k=c[1054]|0;if((a[k+(l*40&-1)|0]&1)==0){p=e;break}q=c[k+(l*40&-1)+36>>2]|0;r=k+(l*40&-1)+32|0;l=c[10036]|0;k=0;while(1){if((k|0)>=(q|0)){break}if((a[l+((c[r>>2]|0)+k|0)|0]|0)==(a[k+115e3|0]|0)){k=k+1|0}else{p=e;break L1447}}if((k|0)!=1){p=e;break}c[13898]=g+2;p=g}}while(0);e=64648+(b*688&-1)|0;c[e>>2]=dj(b,64664+(b*688&-1)|0,64672+(b*688&-1)|0,c[e>>2]|0)|0;e=c[13898]|0;if((c[8272]|0)<=(e|0)){uf(e,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}b=c[1054]|0;if((a[b+(e*40&-1)|0]&1)==0){uf(e,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}n=c[b+(e*40&-1)+36>>2]|0;j=b+(e*40&-1)+32|0;b=c[10036]|0;m=0;while(1){if((m|0)>=(n|0)){break}if((a[b+((c[j>>2]|0)+m|0)|0]|0)==(a[m+78864|0]|0)){m=m+1|0}else{o=1089;break}}if((o|0)==1089){uf(e,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((m|0)!=1){uf(e,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}c[13898]=e+1;h=p;i=f;return h|0}function ds(){var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0.0,B=0,C=0;d=i;e=hA(c[11932]|0,78360)|0;if((e|0)==0){uj(-1,146640,(v=i,i=i+8|0,c[v>>2]=c[11932],v)|0)}else{f=0;g=0}L1479:while(1){if((g|0)!=0){if((a_(f+(g-2|0)|0,86144,2)|0)==0){j=1104;break}}k=g+512|0;l=g+513|0;do{if((f|0)==0){m=ut(l)|0;if((m|0)!=0){n=m;break}gk();m=ut(l)|0;if((m|0)==0){j=1099;break L1479}else{n=m}}else{n=db(f,l,127720)|0}}while(0);l=n+g|0;a[l]=0;if((cd(l|0,512,1,e|0)|0)==0){j=1102;break}a[n+k|0]=0;f=n;g=k}if((j|0)==1099){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=127720,v)|0)}else if((j|0)==1102){uu(n);uj(-1,113448,(v=i,i=i+8|0,c[v>>2]=c[11932],v)|0)}else if((j|0)==1104){az(e|0);e=c[11864]|0;do{if((e|0)<1){n=1-e|0;do{if((c[11880]|0)<1){l=db(c[12172]|0,232,216568)|0;c[12172]=l;if((l|0)==0){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[11880]=1;break}}}while(0);if((n|0)<=0){break}k=c[12172]|0;l=0;m=c[11864]|0;do{uD(k+(m*232&-1)|0,48440,232);m=(c[11864]|0)+1|0;c[11864]=m;l=l+1|0;}while((l|0)<(n|0))}}while(0);e=aQ(f|0,100248)|0;L1506:do{if((e|0)==0){j=1122}else{n=a8(e+18|0,61)|0;do{n=n+1|0;}while((aM(a[n]|0|0)|0)!=0);if((n|0)==0){j=1122;break}l=bk(n|0,95048)|0;m=db(c[11932]|0,l+1|0,88488)|0;c[11932]=m;uF(m|0,n|0,l|0);a[(c[11932]|0)+l|0]=0;l=aQ(f|0,78632)|0;do{if((l|0)!=0){m=a8(l+22|0,61)|0;do{m=m+1|0;}while((aM(a[m]|0|0)|0)!=0);if((m|0)==0){break}k=bW(m|0)|0;c[(c[12172]|0)+128>>2]=k;break L1506}}while(0);c[(c[12172]|0)+128>>2]=0}}while(0);if((j|0)==1122){c[(c[12172]|0)+128>>2]=g}c[(c[12172]|0)+152>>2]=1;c[(c[12172]|0)+156>>2]=-1;a[(c[12172]|0)+124|0]=1;c[(c[12172]|0)+112>>2]=-3;c[(c[12172]|0)+116>>2]=-4;eQ(1);g=c[11950]|0;if((c[11884]|0)<1){j=db(g,12,215112)|0;c[11950]=j;c[11884]=1;o=j}else{o=g}c[o>>2]=0;o=c[11950]|0;if((c[11884]|0)<2){g=db(o,24,215112)|0;c[11950]=g;c[11884]=2;p=g}else{p=o}c[p+12>>2]=0;c[11870]=1;c[262]=1;p=aQ(f|0,73608)|0;do{if((p|0)!=0){o=a8(p+5|0,61)|0;do{o=o+1|0;}while((aM(a[o]|0|0)|0)!=0);if((o|0)==0){break}g=bW(o|0)|0;c[(c[12172]|0)+140>>2]=g}}while(0);p=aQ(f|0,218896)|0;do{if((p|0)!=0){g=a8(p+5|0,61)|0;do{g=g+1|0;}while((aM(a[g]|0|0)|0)!=0);if((g|0)==0){break}o=bW(g|0)|0;c[(c[12172]|0)+144>>2]=o}}while(0);p=aQ(f|0,213352)|0;L1542:do{if((p|0)!=0){o=a8(p+8|0,61)|0;do{o=o+1|0;}while((aM(a[o]|0|0)|0)!=0);if((o|0)==0){break}else{q=-1}while(1){r=q+1|0;g=c[46440+(r*12&-1)>>2]|0;if((r|0)==12){break L1542}if((a_(o|0,g|0,uA(g|0)|0)|0)==0){break}else{q=r}}if((r|0)<=-1){break}o=b[46448+(r*12&-1)>>1]|0;if((o|0)==1){g=1328>>>(r>>>0);do{if((g&1|0)==0){j=2752>>>(r>>>0);if(((j|g)&1|0)!=0){s=(j&1|0)!=0?11:5;break}if((q-1|0)>>>0<2){s=3;break}s=r>>>0<2?1:12}else{s=7}}while(0);g=c[11950]|0;if((c[11884]|0)<1){j=db(g,12,215112)|0;c[11950]=j;c[11884]=1;t=j}else{t=g}c[t+4>>2]=s;b[(c[11950]|0)+8>>1]=b[48036+(s<<4)>>1]|0;break}else if((o|0)==2){if((1328>>>(r>>>0)&1|0)==0){u=(2752>>>(r>>>0)&1|0)!=0?9:12}else{u=8}g=c[11950]|0;if((c[11884]|0)<1){j=db(g,12,215112)|0;c[11950]=j;c[11884]=1;w=j}else{w=g}c[w+4>>2]=u;b[(c[11950]|0)+8>>1]=b[48036+(u<<4)>>1]|0;break}else if((o|0)==0){g=1328>>>(r>>>0);do{if((g&1|0)==0){j=2752>>>(r>>>0);if(((j|g)&1|0)!=0){x=(j&1|0)!=0?10:4;break}if((q-1|0)>>>0<2){x=2;break}x=r>>>0<2?0:12}else{x=6}}while(0);g=c[11950]|0;if((c[11884]|0)<1){o=db(g,12,215112)|0;c[11950]=o;c[11884]=1;y=o}else{y=g}c[y+4>>2]=x;b[(c[11950]|0)+8>>1]=b[48036+(x<<4)>>1]|0;break}else{break}}}while(0);x=aQ(f|0,207688)|0;L1579:do{if((x|0)!=0){y=a8(x+9|0,61)|0;do{y=y+1|0;}while((aM(a[y]|0|0)|0)!=0);if((y|0)==0){break}do{if((aY(y|0,149792)|0)==0){z=0}else{r=(a_(y|0,149792,12)|0)!=0;if((aY(y|0,147968)|0)==0){z=1;break}q=(a_(y|0,147968,13)|0)==0&r?1:r<<31>>31;if((q|0)>-1){z=q}else{break L1579}}}while(0);c[12208]=c[46604+(z<<3)>>2]}}while(0);z=aQ(f|0,203264)|0;do{if((z|0)!=0){x=a8(z+8|0,61)|0;do{x=x+1|0;}while((aM(a[x]|0|0)|0)!=0);if((x|0)==0){break}A=+uz(x,0);y=c[12172]|0;h[y+200>>3]=A;c[y+192>>2]=1}}while(0);z=aQ(f|0,198216)|0;do{if((z|0)!=0){y=a8(z+8|0,61)|0;do{y=y+1|0;}while((aM(a[y]|0|0)|0)!=0);if((y|0)==0){break}A=+uz(y,0);x=c[12172]|0;h[x+208>>3]=A;c[x+192>>2]=1}}while(0);z=aQ(f|0,186936)|0;do{if((z|0)!=0){x=a8(z+8|0,61)|0;do{x=x+1|0;}while((aM(a[x]|0|0)|0)!=0);if((x|0)==0){break}A=+uz(x,0);y=c[12172]|0;h[y+200>>3]=A;c[y+192>>2]=2}}while(0);z=aQ(f|0,181632)|0;do{if((z|0)!=0){y=a8(z+8|0,61)|0;do{y=y+1|0;}while((aM(a[y]|0|0)|0)!=0);if((y|0)==0){break}A=+uz(y,0);x=c[12172]|0;h[x+208>>3]=A;c[x+192>>2]=2}}while(0);z=aQ(f|0,177440)|0;do{if((z|0)!=0){x=a8(z+7|0,61)|0;do{x=x+1|0;}while((aM(a[x]|0|0)|0)!=0);if((x|0)==0){break}A=+uz(x,0);h[(c[12172]|0)+168>>3]=A}}while(0);z=aQ(f|0,172936)|0;do{if((z|0)!=0){y=a8(z+7|0,61)|0;do{y=y+1|0;}while((aM(a[y]|0|0)|0)!=0);if((y|0)==0){break}A=+uz(y,0);h[(c[12172]|0)+176>>3]=A}}while(0);z=aQ(f|0,169160)|0;if((z|0)==0){uu(f);i=d;return}x=a8(z+10|0,61)|0;do{x=x+1|0;}while((aM(a[x]|0|0)|0)!=0);if((x|0)==0){uu(f);i=d;return}do{if((aY(x|0,155800)|0)==0){c[(c[12172]|0)+152>>2]=1;B=(c[12172]|0)+156|0}else{if((aY(x|0,151720)|0)==0){c[(c[12172]|0)+152>>2]=1;C=(c[12172]|0)+156|0}else{z=(a_(x|0,155800,11)|0)!=0;q=(a_(x|0,151720,9)|0)==0&z;c[(c[12172]|0)+152>>2]=1;z=(c[12172]|0)+156|0;if(q){C=z}else{B=z;break}}c[C>>2]=1;c[(c[12172]|0)+112>>2]=-3;c[(c[12172]|0)+116>>2]=-4;uu(f);i=d;return}}while(0);c[B>>2]=-1;c[(c[12172]|0)+112>>2]=-3;c[(c[12172]|0)+116>>2]=-4;uu(f);i=d;return}}function dt(){du(0)}function du(a){a=a|0;uf(-1,164848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function dv(){du(0)}function dw(){du(0)}function dx(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0;d=i;i=i+16|0;e=d|0;f=c[12372]|0;if((f|0)==2|(f|0)==5){g=(c[12374]<<4&65520)+f|0;j=1226}else if((f|0)==1|(f|0)==4){g=(c[12373]<<4&65520)+f|0;j=1226}else if((f|0)==0){c[b+8>>2]=1601}else{g=0;j=1226}if((j|0)==1226){c[b+8>>2]=g}cN[c[(c[3524]|0)+148>>2]&255](4,b);if((c[7661]|0)==0){i=d;return}g=e;c[g>>2]=c[7654];c[g+4>>2]=c[30620>>2];c[g+8>>2]=c[30624>>2];c[g+12>>2]=c[30628>>2];g=c[7645]|0;do{if((c[7644]|0)!=0){k=+h[3825];f=c[(c[3524]|0)+92>>2]|0;if(k<0.0){cK[f&63](+h[3817]);break}else{cK[f&63](k);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[3824]);f=c[(c[3524]|0)+64>>2]|0;if((g|0)<-5){cM[f&511](-2)}else{cM[f&511](g)}f=c[3524]|0;do{if((a[30608]&1)==0){if((c[f+96>>2]&1024|0)!=0){l=f;break}c[e>>2]=1;c[e+4>>2]=g;j=1238}else{j=1238}}while(0);if((j|0)==1238){fn(e,f);l=c[3524]|0}f=b|0;e=b+4|0;cN[c[l+56>>2]&255](c[f>>2]|0,c[e>>2]|0);cN[c[(c[3524]|0)+60>>2]&255](c[b+36>>2]|0,c[b+40>>2]|0);cN[c[(c[3524]|0)+60>>2]&255](c[b+24>>2]|0,c[b+28>>2]|0);cN[c[(c[3524]|0)+60>>2]&255](c[b+12>>2]|0,c[b+16>>2]|0);cN[c[(c[3524]|0)+60>>2]&255](c[f>>2]|0,c[e>>2]|0);i=d;return}function dy(a){a=a|0;var b=0,d=0,e=0.0,f=0.0,g=0.0,j=0.0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,v=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0,D=0.0,E=0,F=0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0;b=i;i=i+48|0;d=b|0;e=+h[64664+((c[200]|0)*688&-1)>>3];f=+h[91];g=+h[96];j=+h[64664+((c[144]|0)*688&-1)>>3];k=+h[21];l=+h[68];m=+h[5279];n=+h[2];o=+h[12];p=+h[403];q=+h[391];r=+h[395];s=+h[399];t=+h[404];u=+h[392];v=+h[396];w=+h[400];x=+h[406];y=+h[394];z=+h[398];A=+h[402];B=+(c[180]|0);C=c[186]|0;D=+(c[40]|0);E=c[46]|0;F=0;do{G=(+h[a+(F<<5)>>3]-e)*f+g+-1.0;H=(+h[a+(F<<5)+8>>3]-j)*k+l+-1.0;I=(+h[a+(F<<5)+16>>3]-m)*n+o+-1.0;J=x+G*y+H*z+I*A;K=J==0.0?1.0e-5:J;c[d+(F*12&-1)>>2]=~~((p+G*q+H*r+I*s)/K*B)+C;c[d+(F*12&-1)+4>>2]=~~((t+G*u+H*v+I*w)/K*D)+E;F=F+1|0;}while((F|0)<4);dx(d|0);i=b;return}function dz(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;b=i;d=c[(c[3524]|0)+136>>2]|0;if((d|0)==0){e=1;i=b;return e|0}f=cO[d&255](0)|0;c[5169]=f;if((f|0)==0){if((uG(29056,20648,1024)|0)==0){e=0;i=b;return e|0}d=c[(c[3524]|0)+136>>2]|0;cO[d&255](20648);uD(29056,20648,1024);e=0;i=b;return e|0}d=c[5168]|0;do{if((d|0)>0){if((c[5163]|0)==100){c[5169]=f;g=f;break}if((f|0)<=(d|0)){g=f;break}c[5169]=d;g=d}else{g=f}}while(0);d=c[7264]|0;do{if((d|0)>-1&(c[5162]|0)==(d|0)){if((c[5163]|0)!=(c[7265]|0)){h=1261;break}if((c[5164]|0)!=(c[7266]|0)){h=1261;break}if((c[5165]|0)!=(c[7267]|0)){h=1261;break}if((c[5166]|0)!=(c[7268]|0)){h=1261;break}if(!((a[20668]|0)==(a[29076]|0)&(g|0)==(c[7271]|0))){h=1261}}else{h=1261}}while(0);do{if((h|0)==1261){if((a[37400]&1)==0){break}d=c[m>>2]|0;j=c[c[3524]>>2]|0;cf(d|0,218032,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=g,c[v+16>>2]=f,v)|0)}}while(0);uD(29056,20648,1024);f=c[5170]|0;if((f|0)!=0){uu(f);c[5170]=0}f=(c[5169]|0)*24&-1;g=ut(f)|0;do{if((g|0)==0){gk();h=ut(f)|0;if((h|0)!=0){k=h;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=217512,v)|0);return 0}else{k=g}}while(0);g=k;c[5170]=g;k=c[5169]|0;L1722:do{if((k|0)>0){f=0;h=k;j=g;while(1){fq(+(f|0)/+(h-1|0),j+(f*24&-1)|0);d=f+1|0;l=c[5169]|0;if((d|0)>=(l|0)){break L1722}f=d;h=l;j=c[5170]|0}}}while(0);cO[c[(c[3524]|0)+136>>2]&255](20648);e=0;i=b;return e|0}function dA(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0,q=0,r=0,s=0;d=i;e=a*12&-1;f=ut(e)|0;do{if((f|0)==0){gk();g=ut(e)|0;if((g|0)!=0){j=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=164072,v)|0)}else{j=f}}while(0);f=j;if((a|0)>0){e=0;do{k=(+h[b+(e<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;l=(+h[b+(e<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;m=(+h[b+(e<<6)+24>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;n=+h[406]+k*+h[394]+l*+h[398]+m*+h[402];o=n==0.0?1.0e-5:n;g=~~((+h[404]+k*+h[392]+l*+h[396]+m*+h[400])/o*+(c[40]|0))+(c[46]|0)|0;c[f+(e*12&-1)>>2]=~~((+h[403]+k*+h[391]+l*+h[395]+m*+h[399])/o*+(c[180]|0))+(c[186]|0);c[f+(e*12&-1)+4>>2]=g;e=e+1|0;}while((e|0)<(a|0))}e=c[12372]|0;if((e|0)==1|(e|0)==4){p=(c[12373]<<4&65520)+e|0}else if((e|0)==2|(e|0)==5){p=(c[12374]<<4&65520)+e|0}else if((e|0)==0){c[j+8>>2]=1601;q=c[3524]|0;r=q+148|0;s=c[r>>2]|0;cN[s&255](a,f);uu(j);i=d;return}else{p=0}c[j+8>>2]=p;q=c[3524]|0;r=q+148|0;s=c[r>>2]|0;cN[s&255](a,f);uu(j);i=d;return}function dB(a,b,d){a=a|0;b=b|0;d=+d;var e=0,f=0,g=0,j=0,k=0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0,r=0,s=0,t=0;e=i;f=a*12&-1;g=ut(f)|0;do{if((g|0)==0){gk();j=ut(f)|0;if((j|0)!=0){k=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=136792,v)|0)}else{k=g}}while(0);g=k;if((a|0)>0){f=0;do{l=(+h[b+(f<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;m=(+h[b+(f<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;n=(d- +h[5279])*+h[2]+ +h[12]+-1.0;o=+h[406]+l*+h[394]+m*+h[398]+n*+h[402];p=o==0.0?1.0e-5:o;j=~~((+h[404]+l*+h[392]+m*+h[396]+n*+h[400])/p*+(c[40]|0))+(c[46]|0)|0;c[g+(f*12&-1)>>2]=~~((+h[403]+l*+h[391]+m*+h[395]+n*+h[399])/p*+(c[180]|0))+(c[186]|0);c[g+(f*12&-1)+4>>2]=j;f=f+1|0;}while((f|0)<(a|0))}f=c[12372]|0;if((f|0)==0){c[k+8>>2]=1601;q=c[3524]|0;r=q+148|0;s=c[r>>2]|0;cN[s&255](a,g);uu(k);i=e;return}else if((f|0)==1|(f|0)==4){t=(c[12373]<<4&65520)+f|0}else if((f|0)==2|(f|0)==5){t=(c[12374]<<4&65520)+f|0}else{t=0}c[k+8>>2]=t;q=c[3524]|0;r=q+148|0;s=c[r>>2]|0;cN[s&255](a,g);uu(k);i=e;return}function dC(b,d,e,f,g){b=b|0;d=+d;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0.0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0;j=i;i=i+64|0;k=f;f=i;i=i+56|0;uD(f,k,56);k=j|0;l=j+16|0;m=j+32|0;n=j+48|0;o=j+56|0;p=(e|0)!=0;q=c[3524]|0;r=~~(+((c[q+28>>2]|0)>>>0>>>0)*(p?+h[8390]:+h[8391])*((a[67136]&1)!=0?-1.0:1.0));s=+h[8341];t=(d-s)/(+h[8342]-s);if((a[54017]|0)==104){u=c[13528]|0;v=~~(+(u|0)+t*+((c[13529]|0)-u|0));u=c[13530]|0;w=u-r|0;x=v;y=u;z=v}else{v=c[13529]|0;u=c[13530]|0;A=~~(+(u|0)+t*+((c[13531]|0)-u|0));w=A;x=v+r|0;y=A;z=v}v=c[f+4>>2]|0;do{if((v|0)>-3){A=m;u=f+40|0;c[A>>2]=c[u>>2];c[A+4>>2]=c[u+4>>2];c[A+8>>2]=c[u+8>>2];c[A+12>>2]=c[u+12>>2];do{if((c[f>>2]|0)!=0){t=+h[f+24>>3];u=c[q+92>>2]|0;if(t<0.0){cK[u&63](+h[3817]);break}else{cK[u&63](t);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[f+16>>3]);u=c[(c[3524]|0)+64>>2]|0;if((v|0)<-5){cM[u&511](-2)}else{cM[u&511](v)}u=c[3524]|0;do{if((a[f+32|0]&1)==0){if((c[u+96>>2]&1024|0)!=0){B=u;break}c[m>>2]=1;c[m+4>>2]=v;C=1317}else{C=1317}}while(0);if((C|0)==1317){fn(m,u);B=c[3524]|0}A=c[B+56>>2]|0;if((a[54017]|0)==104){cN[A&255](z,c[13530]|0);cN[c[(c[3524]|0)+60>>2]&255](z,c[13531]|0)}else{cN[A&255](c[13528]|0,y);cN[c[(c[3524]|0)+60>>2]&255](c[13529]|0,y)}A=l;c[A>>2]=c[14084];c[A+4>>2]=c[56340>>2];c[A+8>>2]=c[56344>>2];c[A+12>>2]=c[56348>>2];A=c[14075]|0;do{if((c[14074]|0)!=0){t=+h[7040];D=c[(c[3524]|0)+92>>2]|0;if(t<0.0){cK[D&63](+h[3817]);break}else{cK[D&63](t);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);u=c[(c[3524]|0)+64>>2]|0;if((A|0)<-5){cM[u&511](-2)}else{cM[u&511](A)}u=c[3524]|0;if((a[56328]&1)==0){if((c[u+96>>2]&1024|0)!=0){E=u;break}c[l>>2]=1;c[l+4>>2]=A}fn(l,u);E=c[3524]|0}else{E=q}}while(0);cN[c[E+56>>2]&255](z,y);cN[c[(c[3524]|0)+60>>2]&255](x,w);do{if(p){L1804:do{if((g|0)==0){F=e}else{t=+h[8342]- +h[8341];E=g;while(1){if(+P(+((d- +h[E>>3])/t))<=.001){F=0;break L1804}q=c[E+16>>2]|0;if((q|0)==0){F=e;break}else{E=q}}}}while(0);fx(64984+(b*688&-1)|0,n,o,123416);A=64928+(b*688&-1)|0;if((c[A>>2]|0)!=0){fn(A,c[3524]|0)}do{if((a[54017]|0)==104){A=c[3524]|0;E=(c[13530]|0)-(c[A+16>>2]|0)|0;q=65032+(b*688&-1)|0;l=c[q>>2]|0;do{if((l|0)==0){G=0}else{if((cO[c[A+72>>2]&255](l)|0)==0){G=0;break}G=c[q>>2]|0}}while(0);q=E+((r|0)>0?-r|0:0)|0;l=(G|0)!=0;if((a[65272+(b*688&-1)|0]&1)==0){H=l&1^1}else{H=c[65128+(b*688&-1)>>2]|0}ln((c[n>>2]|0)+x|0,(c[o>>2]|0)+((q|0)<0?0:q)|0,F,H,1,G,c[64924+(b*688&-1)>>2]|0);if(!l){break}l=c[(c[3524]|0)+72>>2]|0;cO[l&255](0)}else{if((a[65272+(b*688&-1)|0]&1)==0){I=0}else{I=c[65128+(b*688&-1)>>2]|0}ln((((c[13529]|0)+((r|0)>0?r:0)|0)+(c[(c[3524]|0)+20>>2]|0)|0)+(c[n>>2]|0)|0,(c[o>>2]|0)+w|0,F,I,1,0,c[64924+(b*688&-1)>>2]|0)}}while(0);l=k;c[l>>2]=c[14084];c[l+4>>2]=c[56340>>2];c[l+8>>2]=c[56344>>2];c[l+12>>2]=c[56348>>2];l=c[14075]|0;do{if((c[14074]|0)!=0){t=+h[7040];q=c[(c[3524]|0)+92>>2]|0;if(t<0.0){cK[q&63](+h[3817]);break}else{cK[q&63](t);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);q=c[(c[3524]|0)+64>>2]|0;if((l|0)<-5){cM[q&511](-2)}else{cM[q&511](l)}q=c[3524]|0;if((a[56328]&1)==0){if((c[q+96>>2]&1024|0)!=0){break}c[k>>2]=1;c[k+4>>2]=l}fn(k,q)}}while(0);if((c[16745]&4|0)==0){i=j;return}if((a[54017]|0)==104){k=c[13531]|0;J=k+r|0;K=x;L=k;M=z}else{z=c[13528]|0;J=w;K=z-r|0;L=y;M=z}cN[c[(c[3524]|0)+56>>2]&255](M,L);cN[c[(c[3524]|0)+60>>2]&255](K,J);i=j;return}function dD(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0.0,T=0.0,U=0.0,V=0.0,W=0.0,X=0.0,Y=0.0,Z=0.0,_=0.0,$=0.0,aa=0.0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0.0,ai=0.0,aj=0.0,ak=0.0,al=0.0,am=0.0,an=0.0,ao=0.0,ap=0.0,aq=0.0,ar=0.0,as=0.0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aJ=0,aK=0.0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0.0,aW=0.0,aX=0,aY=0,aZ=0,a_=0.0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0;d=i;i=i+304|0;e=d|0;f=d+16|0;g=d+32|0;j=d+48|0;k=d+64|0;l=d+80|0;m=d+128|0;n=d+136|0;o=d+144|0;p=d+152|0;q=d+160|0;r=d+168|0;s=d+176|0;t=d+184|0;u=d+192|0;w=d+232|0;x=d+272|0;y=d+280|0;z=d+288|0;A=d+296|0;B=c[10028]|0;C=a[54016]|0;if(C<<24>>24==110){i=d;return}D=c[3524]|0;if((c[D+148>>2]|0)==0){i=d;return}do{if(C<<24>>24==117){do{if((a[37384]&1)==0){fR(54032,o,p,106096);c[13528]=~~+h[o>>3];c[13530]=~~+h[p>>3];fN(54072,q,r,106096);E=~~+h[q>>3];c[13529]=E;F=~~+h[r>>3];c[13531]=F;G=E;H=F}else{if((c[5094]|0)==0){F=D+8|0;c[13528]=~~(+h[6756]*+((c[F>>2]|0)>>>0>>>0)+.5);E=D+12|0;c[13530]=~~(+h[6757]*+((c[E>>2]|0)>>>0>>>0)+.5);I=~~(+h[6761]*+(((c[F>>2]|0)-1|0)>>>0>>>0)+.5);c[13529]=I;F=~~(+h[6762]*+(((c[E>>2]|0)-1|0)>>>0>>>0)+.5);c[13531]=F;G=I;H=F;break}else{fK(54032,s,t,106096);c[13528]=~~+h[s>>3];c[13530]=~~+h[t>>3];fx(54072,54116,54124,106096);G=c[13529]|0;H=c[13531]|0;break}}}while(0);F=c[13528]|0;I=G+F|0;c[13529]=I;E=c[13530]|0;J=H+E|0;c[13531]=J;K=E;L=J;M=F;N=I}else{do{if((b|0)!=2|(c[5094]|0)!=0){if((a[37384]&1)==0){uE(u|0,0,40);c[u>>2]=2;c[u+4>>2]=2;c[u+8>>2]=2;h[u+16>>3]=1.025;uE(w|0,0,40);c[w>>2]=2;c[w+4>>2]=2;c[w+8>>2]=2;h[w+16>>3]=.05;h[w+24>>3]=1.0;fR(u,m,n,106096);I=~~+h[m>>3];c[13530]=~~+h[n>>3];c[13528]=I+(c[13507]|0);fN(w,x,y,106096);I=c[13528]|0;F=~~(+h[x>>3]+ +(I|0));c[13529]=F;J=c[13530]|0;E=~~(+h[y>>3]+ +(J|0));c[13531]=E;O=J;P=E;Q=I;R=F;break}else{F=c[200]|0;S=+h[64672+(F*688&-1)>>3];T=+h[64664+(F*688&-1)>>3];U=S-T;F=c[144]|0;V=+h[64664+(F*688&-1)>>3];W=+h[91];X=+h[96];Y=(S+U*.025-T)*W+X+-1.0;Z=+h[21];_=+h[68];$=(V-V)*Z+_+-1.0;aa=+h[5279];ab=+h[2];ac=+h[12];ad=(+h[7076]-aa)*ab+ac+-1.0;ae=+h[403];af=+h[391];ag=+h[395];ah=+h[399];ai=+h[404];aj=+h[392];ak=+h[396];al=+h[400];am=+h[406];an=+h[394];ao=+h[398];ap=+h[402];aq=am+Y*an+$*ao+ad*ap;ar=aq==0.0?1.0e-5:aq;aq=+(c[180]|0);I=c[186]|0;E=~~((ae+Y*af+$*ag+ad*ah)/ar*aq)+I|0;as=+(c[40]|0);J=c[46]|0;at=~~((ai+Y*aj+$*ak+ad*al)/ar*as)+J|0;c[13528]=E;c[13530]=at;ar=(U*.075+S-T)*W+X+-1.0;X=(+h[64672+(F*688&-1)>>3]-V)*Z+_+-1.0;_=(+h[6915]-aa)*ab+ac+-1.0;ac=am+ar*an+X*ao+_*ap;ap=ac==0.0?1.0e-5:ac;F=~~((ae+ar*af+X*ag+_*ah)/ap*aq)+I|0;I=~~((ai+ar*aj+X*ak+_*al)/ap*as)+J|0;c[13529]=F;c[13531]=I;O=at;P=I;Q=E;R=F;break}}else{as=+(c[186]|0);ap=+(c[180]|0);F=~~(as+ap*.709);c[13528]=F;E=~~(as+ap*.778);c[13529]=E;ap=+(c[46]|0);as=+(c[40]|0);I=~~(ap-as*.147);c[13530]=I;at=~~(ap+as*.497);c[13531]=at;O=I;P=at;Q=F;R=E}}while(0);if((a[54017]|0)!=118){K=O;L=P;M=Q;N=R;break}as=+((c[(c[3524]|0)+28>>2]|0)>>>0>>>0);E=~~(+h[8390]*((a[67136]&1)!=0?-1.0:1.0)*as);F=c[144]|0;at=~~(as*+h[65056+(F*688&-1)>>3]*((a[65072+(F*688&-1)|0]&1)!=0?-1.0:1.0));do{if((E|0)>0){if((c[16745]&4|0)==0){au=Q;av=R;break}F=Q+E|0;c[13528]=F;I=R+E|0;c[13529]=I;au=F;av=I}else{au=Q;av=R}}while(0);if((at|0)<=0){K=O;L=P;M=au;N=av;break}if((c[16401]&4|0)==0){K=O;L=P;M=au;N=av;break}E=au+at|0;c[13528]=E;I=av+at|0;c[13529]=I;K=O;L=P;M=E;N=I}}while(0);if((K|0)>(L|0)){c[13531]=K;c[13530]=L;aw=L;ax=K}else{aw=K;ax=L}if((c[(c[3524]|0)+96>>2]&16|0)==0){if((a[54017]|0)==118){L=l+36|0;c[L>>2]=M;K=l|0;c[K>>2]=M;P=l+24|0;c[P>>2]=N;O=l+12|0;c[O>>2]=N;ay=ax-aw|0;az=ax;aA=aw;aB=l+16|0;aC=l+4|0;aD=l+40|0;aE=l+28|0;aF=L;aG=K;aH=P;aJ=O}else{O=l+16|0;c[O>>2]=aw;P=l+4|0;c[P>>2]=aw;K=l+40|0;c[K>>2]=ax;L=l+28|0;c[L>>2]=ax;ay=N-M|0;az=N;aA=M;aB=O;aC=P;aD=K;aE=L;aF=l+36|0;aG=l|0;aH=l+24|0;aJ=l+12|0}as=+(ay|0)*.0078125;ap=+(az-aA|0);ay=1-aA|0;L=l+8|0;K=l|0;l=k|0;P=k+8|0;O=k+4|0;av=aA;au=0;R=0;while(1){Q=~~(as*+(R+1|0))+aA|0;al=+(ay+av|0)/ap;if((a[20668]|0)==110){aK=1.0-al}else{aK=al}y=(c[3524]|0)+144|0;if((c[y>>2]|0)!=0){c[l>>2]=5;h[P>>3]=aK;c[O>>2]=0;cM[c[y>>2]&511](k)}L1909:do{if((c[5163]|0)==100){y=c[5172]|0;x=c[5173]|0;w=au;n=au;while(1){if((w|0)>=(y|0)){aL=R;aM=n;aN=Q;break L1909}aO=~~(ap*+h[x+(w<<5)>>3])+aA|0;if((av|0)<(aO|0)){if((Q|0)>(aO|0)){break}else{aP=n}}else{aP=w}if((Q|0)<(aO|0)){aL=R;aM=aP;aN=Q;break L1909}else{w=w+1|0;n=aP}}aL=R-1|0;aM=n;aN=aO}else{aL=R;aM=au;aN=Q}}while(0);if((a[54017]|0)==118){c[aB>>2]=av;c[aC>>2]=av;Q=aN+1|0;at=(az|0)<(Q|0)?az:Q;c[aD>>2]=at;c[aE>>2]=at}else{c[aF>>2]=av;c[aG>>2]=av;at=aN+1|0;Q=(az|0)<(at|0)?az:at;c[aH>>2]=Q;c[aJ>>2]=Q}Q=c[12372]|0;if((Q|0)==0){aQ=1601}else if((Q|0)==1|(Q|0)==4){aR=(c[12373]<<4&65520)+Q|0;aS=1421}else if((Q|0)==2|(Q|0)==5){aR=(c[12374]<<4&65520)+Q|0;aS=1421}else{aR=0;aS=1421}if((aS|0)==1421){aS=0;aQ=aR}c[L>>2]=aQ;cN[c[(c[3524]|0)+148>>2]&255](4,K);Q=aL+1|0;if((Q|0)<128){av=aN;au=aM;R=Q}else{break}}}else{aI(92952,104,1,B|0);R=c[13530]|0;cf(B|0,85112,(v=i,i=i+32|0,c[v>>2]=c[13528],c[v+8>>2]=R,c[v+16>>2]=N-M,c[v+24>>2]=ax-aw,v)|0);aI(76928,42,1,B|0);if((a[20668]|0)==110){aI(224696,19,1,B|0)}else{aI(217376,7,1,B|0)}if((a[54017]|0)==118){aI(211744,30,1,B|0)}else{aI(205632,30,1,B|0)}aI(202264,85,1,B|0)}do{if((a[54018]|0)!=0){B=c[13505]|0;do{if((B|0)>-1){aw=c[14074]|0;ap=+h[7039];aK=+h[7040];ax=a[56328]|0;as=+h[7043];M=43280;while(1){aT=c[M>>2]|0;if((aT|0)==0){aS=1429;break}if((c[aT+4>>2]|0)==(B|0)){aS=1428;break}else{M=aT|0}}L1952:do{if((aS|0)==1428){M=c[aT+12>>2]|0;n=a[aT+40|0]|0;N=(n&1)==0;aU=M;aV=+h[aT+24>>3];aW=+h[aT+32>>3];aX=n;aY=N?1:c[aT+48>>2]|0;aZ=N?M:c[aT+52>>2]|0;a_=+h[aT+56>>3]}else if((aS|0)==1429){M=c[8798]|0;N=(M|0)>0;n=B;L1955:while(1){R=43264;while(1){a$=c[R>>2]|0;if((a$|0)==0){break}if((c[a$+4>>2]|0)==(n|0)){break L1955}else{R=a$|0}}R=n-1|0;if(!((n|0)>(M|0)&N)){aU=R;aV=ap;aW=aK;aX=ax;aY=1;aZ=R;a_=as;break L1952}n=((R|0)%(M|0)&-1)+1|0}M=c[a$+12>>2]|0;al=+h[a$+24>>3];_=+h[a$+32>>3];N=a[a$+40|0]|0;R=c[a$+48>>2]|0;aM=c[a$+52>>2]|0;ak=+h[a$+56>>3];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){aU=n;aV=al;aW=_;aX=0;aY=R;aZ=aM;a_=ak;break}au=(N&1)==0;aU=M;aV=al;aW=_;aX=N;aY=au?1:R;aZ=au?M:aM;a_=ak}}while(0);ax=j|0;c[ax>>2]=aY;aM=j+4|0;c[aM>>2]=aZ;h[j+8>>3]=a_;do{if((aw|0)!=0){M=c[(c[3524]|0)+92>>2]|0;if(aW<0.0){cK[M&63](+h[3817]);break}else{cK[M&63](aW);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](aV);aw=c[(c[3524]|0)+64>>2]|0;if((aU|0)<-5){cM[aw&511](-2)}else{cM[aw&511](aU)}aw=c[3524]|0;if((aX&1)==0){if((c[aw+96>>2]&1024|0)!=0){break}c[ax>>2]=1;c[aM>>2]=aU}fn(j,aw)}else{aw=g;c[aw>>2]=c[14084];c[aw+4>>2]=c[56340>>2];c[aw+8>>2]=c[56344>>2];c[aw+12>>2]=c[56348>>2];aw=c[14075]|0;do{if((c[14074]|0)!=0){as=+h[7040];M=c[(c[3524]|0)+92>>2]|0;if(as<0.0){cK[M&63](+h[3817]);break}else{cK[M&63](as);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);aM=c[(c[3524]|0)+64>>2]|0;if((aw|0)<-5){cM[aM&511](-2)}else{cM[aM&511](aw)}aM=c[3524]|0;if((a[56328]&1)==0){if((c[aM+96>>2]&1024|0)!=0){break}c[g>>2]=1;c[g+4>>2]=aw}fn(g,aM)}}while(0);B=c[3524]|0;aM=c[B+172>>2]|0;if((aM|0)==0){a0=B}else{cM[aM&511](0);a0=c[3524]|0}cN[c[a0+56>>2]&255](c[13528]|0,c[13530]|0);cN[c[(c[3524]|0)+60>>2]&255](c[13529]|0,c[13530]|0);cN[c[(c[3524]|0)+60>>2]&255](c[13529]|0,c[13531]|0);cN[c[(c[3524]|0)+60>>2]&255](c[13528]|0,c[13531]|0);cN[c[(c[3524]|0)+60>>2]&255](c[13528]|0,c[13530]|0);aM=c[(c[3524]|0)+172>>2]|0;if((aM|0)!=0){cM[aM&511](1)}aM=f;c[aM>>2]=c[14084];c[aM+4>>2]=c[56340>>2];c[aM+8>>2]=c[56344>>2];c[aM+12>>2]=c[56348>>2];aM=c[14075]|0;do{if((c[14074]|0)!=0){as=+h[7040];B=c[(c[3524]|0)+92>>2]|0;if(as<0.0){cK[B&63](+h[3817]);break}else{cK[B&63](as);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);B=c[(c[3524]|0)+64>>2]|0;if((aM|0)<-5){cM[B&511](-2)}else{cM[B&511](aM)}B=c[3524]|0;if((a[56328]&1)==0){if((c[B+96>>2]&1024|0)!=0){break}c[f>>2]=1;c[f+4>>2]=aM}fn(f,B)}}while(0);if((c[16745]|0)!=0){f=e;c[f>>2]=c[14084];c[f+4>>2]=c[56340>>2];c[f+8>>2]=c[56344>>2];c[f+12>>2]=c[56348>>2];f=c[14075]|0;do{if((c[14074]|0)!=0){aV=+h[7040];a0=c[(c[3524]|0)+92>>2]|0;if(aV<0.0){cK[a0&63](+h[3817]);break}else{cK[a0&63](aV);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);a0=c[(c[3524]|0)+64>>2]|0;if((f|0)<-5){cM[a0&511](-2)}else{cM[a0&511](f)}a0=c[3524]|0;do{if((a[56328]&1)==0){if((c[a0+96>>2]&1024|0)!=0){break}c[e>>2]=1;c[e+4>>2]=f;aS=1485}else{aS=1485}}while(0);if((aS|0)==1485){fn(e,a0)}de(3,8)}if((c[16801]|0)==0){i=d;return}fn(67216,c[3524]|0);aV=+h[8390];a0=(a[67136]&1)!=0;do{if((a[54017]|0)==104){e=~~(aV*(a0?1.0:-1.0)*+((c[(c[3524]|0)+24>>2]|0)>>>0>>>0));fx(67288,z,A,98672);aS=(c[z>>2]|0)+(((c[13529]|0)+(c[13528]|0)|0)/2&-1)|0;c[z>>2]=aS;f=~~(+(c[A>>2]|0)+(+(c[13530]|0)+ +((c[(c[3524]|0)+16>>2]|0)>>>0>>>0)*-2.7))+((e|0)<0?e:0)|0;c[A>>2]=f;if((aS|0)<0){c[z>>2]=0;a1=0}else{a1=aS}if((f|0)<0){c[A>>2]=0;a2=0}else{a2=f}ln(a1,a2,c[16801]|0,1,1,0,c[16802]|0)}else{f=~~(aV*(a0?-1.0:1.0)*+((c[(c[3524]|0)+28>>2]|0)>>>0>>>0));c[216]=0;if((c[16745]&1|0)!=0){c[216]=0;de(3,14)}fx(67288,z,A,98672);aS=c[3524]|0;e=~~(+(c[z>>2]|0)+(+(c[13529]|0)+(+(c[216]|0)+1.5)*+((c[aS+20>>2]|0)>>>0>>>0)))+((f|0)>0?f:0)|0;c[z>>2]=e;f=(c[A>>2]|0)+(((c[13531]|0)+(c[13530]|0)|0)/2&-1)|0;c[A>>2]=f;if((e|0)<0){c[z>>2]=0;a3=0}else{a3=e}if((f|0)<0){c[A>>2]=0;a4=0}else{a4=f}f=(cO[c[aS+72>>2]&255](c[16799]|0)|0)==0;aS=c[16801]|0;if(f){ln(a3,a4,aS,0,0,0,c[16802]|0);break}else{ln(a3,a4,aS,1,0,c[16799]|0,c[16802]|0);aS=c[(c[3524]|0)+72>>2]|0;cO[aS&255](0);break}}}while(0);if((c[16804]|0)==0){i=d;return}cM[c[(c[3524]|0)+64>>2]&511](-2);i=d;return}function dE(){c[13544]=(c[13544]|0)+1;c[13898]=(c[13898]|0)+1;return}function dF(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,n=0,p=0,q=0;c[12906]=b;do{if((d|0)==0){if((a[37400]&1)!=0){e=b;f=0;break}c[8272]=0;c[13898]=0;a[c[10036]|0]=0;e=b;f=0}else{e=b;f=d}}while(0);L2054:while(1){d=(c[10036]|0)+f|0;b=(c[10034]|0)-f|0;if((a[37400]&1)!=0){g=c[m>>2]|0;aK(e|0,g|0)}if((a6(d|0,b|0,c[o>>2]|0)|0)==0){h=1518;break}b=c[10036]|0;d=uA(b|0)|0;g=d-1|0;if((g|0)<=-1){break}i=b+g|0;b=a[i]|0;do{if(b<<24>>24==10){a[i]=0;do{if((g|0)>0){j=d-2|0;k=(c[10036]|0)+j|0;if((a[k]|0)!=13){l=g;break}a[k]=0;l=j}else{l=g}}while(0);j=(((l|0)>0)<<31>>31)+l|0;n=j;p=a[(c[10036]|0)+j|0]|0}else{if((d+1|0)>>>0<(c[10034]|0)>>>0){n=g;p=b;break}dG();e=138304;f=d;continue L2054}}while(0);d=p<<24>>24==92;if(d){e=138304;f=d?n:f}else{q=0;h=1533;break}}if((h|0)==1533){return q|0}do{if((h|0)==1518){if((a[37400]&1)!=0){n=c[m>>2]|0;aF(10,n|0)}a[(c[10036]|0)+f|0]=0;c[9354]=(c[9354]|0)+1;if((f|0)>0&(c[12918]|0)==0){break}else{q=1}return q|0}}while(0);q=0;return q|0}function dG(){var b=0,d=0,e=0,f=0;b=i;d=c[10034]|0;if((d|0)!=0){c[10036]=db(c[10036]|0,d+1024|0,216480)|0;c[10034]=(c[10034]|0)+1024;i=b;return}d=ut(1024)|0;do{if((d|0)==0){gk();e=ut(1024)|0;if((e|0)!=0){f=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=209632,v)|0)}else{f=d}}while(0);c[10036]=f;c[10034]=1024;a[f]=0;i=b;return}function dH(){var a=0,b=0,d=0,e=0;a=i;b=c[1052]|0;if((b|0)!=0){d=db(c[1054]|0,(b*40&-1)+16e3|0,136360)|0;c[1054]=d;uE(d+((c[1052]|0)*40&-1)|0,0,16e3);c[1052]=(c[1052]|0)+400;i=a;return}d=ut(16e3)|0;do{if((d|0)==0){gk();b=ut(16e3)|0;if((b|0)!=0){e=b;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=163112,v)|0)}else{e=d}}while(0);c[1054]=e;c[1052]=400;uE(e|0,0,16e3);i=a;return}function dI(){var b=0,d=0,e=0;b=i;do{if((a[33512]&1)==0){if((dF(105808,0)|0)==0){break}else{d=1}i=b;return d|0}else{do{if((a[14080]&1)!=0){do{if((a[37400]&1)!=0){e=c[(c[3524]|0)+96>>2]|0;if((e&1|0)!=0){break}if((c[10030]|0)!=(c[n>>2]|0)){if((e&2|0)==0){break}}lm();if((c[(c[3524]|0)+96>>2]&2|0)==0){uf(-1,100528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{uf(-1,101160,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}}while(0);if(a[13032]|0){break}e=c[(c[3524]|0)+100>>2]|0;if((e|0)==0){break}cS[e&511]();a[13032]=1}}while(0);if((dF(123048,0)|0)==0){break}else{d=1}i=b;return d|0}}while(0);a[25280]=a[37400]&1;d=(dJ()|0)!=0&1;i=b;return d|0}function dJ(){var b=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0;b=i;do{if((a[43472]&1)!=0){if((dK()|0)==0){break}if((dK()|0)==0){break}if((dK()|0)==0){break}if((dK()|0)==0){break}uf(-1,98448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);e=c[10036]|0;while(1){if((aM(d[e]|0|0)|0)==0){break}else{e=e+1|0}}do{if((a8(e|0,35)|0)!=0){f=jl(40144,40136)|0;c[8272]=f;g=(c[10036]|0)+(c[(c[1054]|0)+(f*40&-1)+32>>2]|0)|0;if((a[g]|0)!=35){break}a[g]=0}}while(0);g=c[10036]|0;if((e|0)==(g|0)){h=e}else{uH(g|0,e|0,uA(e|0)|0);g=uA(e|0)|0;a[(c[10036]|0)+g|0]=0;h=c[10036]|0}if((a[h]|0)==33){g=h+1|0;if((g|0)==0){j=0;i=b;return j|0}if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}bN(g|0);j=0;i=b;return j|0}c[9368]=0;g=jl(40144,40136)|0;c[8272]=g;h=c[12918]|0;if((h|0)<0){uf(-1,211816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}do{if((h|0)>0){while(1){if((a[37400]&1)==0){if((a[33232]&1)==0){k=1600;break}}e=c[10036]|0;f=e+(uA(e|0)|0)|0;w=59;a[f]=w&255;w=w>>8;a[f+1|0]=w&255;if((dF(76720,uA(c[10036]|0)|0)|0)!=0){k=1597;break}f=jl(40144,40136)|0;c[8272]=f;e=(c[10036]|0)+(c[(c[1054]|0)+(f*40&-1)+32>>2]|0)|0;if((a[e]|0)==35){a[e]=0}if((c[12918]|0)<=0){k=1601;break}}if((k|0)==1597){uf(-1,224544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((k|0)==1600){uf(-1,224544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((k|0)==1601){l=c[8272]|0;break}}else{l=g}}while(0);c[13898]=0;if((l|0)>0){m=0}else{j=0;i=b;return j|0}L2171:while(1){a[55600]=0;a[55651]=0;a[55702]=0;a[55753]=0;a[55804]=0;a[55855]=0;a[55906]=0;a[55957]=0;a[56008]=0;a[56059]=0;a[56110]=0;a[56161]=0;if((t9(m)|0)==0){l=c[13898]|0;g=c[1054]|0;h=c[g+(l*40&-1)+36>>2]|0;e=c[g+(l*40&-1)+32>>2]|0;f=(a[g+(l*40&-1)|0]&1)==0;l=(h|0)>0;g=c[10036]|0;n=53600;o=133488;L2176:while(1){L2178:do{if(!f){if(l){p=0;q=0;r=e;while(1){s=a[o+p|0]|0;if(s<<24>>24==(a[g+(p+r|0)|0]|0)){t=r;u=q}else{if(s<<24>>24!=36){break L2178}t=r-1|0;u=1}x=p+1|0;if((x|0)<(u+h|0)){p=x;q=u;r=t}else{break}}if((u|0)==0){y=x}else{z=n;break L2176}}else{y=0}r=a[o+y|0]|0;if((r<<24>>24|0)==36|(r<<24>>24|0)==0){z=n;break L2176}}}while(0);r=n+8|0;q=c[r>>2]|0;if((q|0)==0){z=r;break}else{n=r;o=q}}cS[c[z+4>>2]&511]()}else{dT()}if((c[13484]|0)!=0){k=1617;break}A=c[13898]|0;o=c[8272]|0;L2193:do{if((A|0)<(o|0)){n=c[1054]|0;h=(a[n+(A*40&-1)|0]&1)==0;if(h){k=1649;break L2171}g=c[n+(A*40&-1)+36>>2]|0;e=n+(A*40&-1)+32|0;l=c[10036]|0;f=0;while(1){if((f|0)>=(g|0)){k=1623;break}if((a[l+((c[e>>2]|0)+f|0)|0]|0)==(a[f+103664|0]|0)){f=f+1|0}else{break}}do{if((k|0)==1623){k=0;if((f|0)!=1){break}e=A+1|0;c[13898]=e;B=e;break L2193}}while(0);if(h){k=1651;break L2171}f=c[n+(A*40&-1)+36>>2]|0;e=n+(A*40&-1)+32|0;l=c[10036]|0;g=0;while(1){if((g|0)>=(f|0)){k=1630;break}if((a[l+((c[e>>2]|0)+g|0)|0]|0)==(a[g+126728|0]|0)){g=g+1|0}else{break}}do{if((k|0)==1630){k=0;if((g|0)!=1){break}c[13544]=(c[13544]|0)+1;e=A+1|0;c[13898]=e;B=e;break L2193}}while(0);if(h){k=1647;break L2171}g=c[n+(A*40&-1)+36>>2]|0;e=n+(A*40&-1)+32|0;l=c[10036]|0;f=0;while(1){if((f|0)>=(g|0)){break}if((a[l+((c[e>>2]|0)+f|0)|0]|0)==(a[f+125616|0]|0)){f=f+1|0}else{k=1650;break L2171}}if((f|0)!=1){k=1648;break L2171}e=c[13544]|0;if((e|0)==0){k=1638;break L2171}c[13544]=e-1;e=A+1|0;c[13898]=e;B=e}else{B=A}}while(0);if((B|0)<(o|0)){m=B}else{j=0;k=1646;break}}if((k|0)==1646){i=b;return j|0}else if((k|0)==1647){uf(A,205408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((k|0)==1648){uf(A,205408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((k|0)==1649){uf(A,205408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((k|0)==1638){uf(A,103896,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((k|0)==1617){c[13484]=0;j=1;i=b;return j|0}else if((k|0)==1650){uf(A,205408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((k|0)==1651){uf(A,205408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}return 0}function dK(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0;b=i;if((a8(c[10036]|0,64)|0)==0){d=0;i=b;return d|0}e=c[10034]|0;f=ut(e)|0;do{if((f|0)==0){gk();g=ut(e)|0;if((g|0)!=0){h=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=222744,v)|0);return 0}else{h=f}}while(0);f=c[10036]|0;e=uA(f|0)|0;g=c[10034]|0;j=e>>>0<g>>>0?e:g-1|0;uF(h|0,f|0,j|0);a[h+j|0]=0;L2237:do{if((j|0)==0|(h|0)==0){k=0;l=0}else{f=0;g=0;e=j;m=0;n=0;o=h;p=0;q=0;while(1){r=a[o]|0;if(r<<24>>24==0){k=m;l=n;break L2237}s=r<<24>>24;L2241:do{if((s|0)==64){do{if(q|p|g){t=r}else{u=o+1|0;if((bO(a[u]|0|0)|0)==0){t=a[o]|0;break}else{w=u}while(1){if((bs(a[w]|0|0)|0)==0){x=a[w]|0;if(x<<24>>24!=95){break}}w=w+1|0}a[w]=0;y=e6(u)|0;do{if((y|0)==0){z=1671}else{if((c[y+16>>2]|0)!=3){z=1671;break}A=n+1|0;B=c[y+24>>2]|0;C=m+e|0;D=C+(uA(B|0)|0)|0;if(D>>>0>(c[10034]|0)>>>0){do{dG();D=C+(uA(B|0)|0)|0;}while(D>>>0>(c[10034]|0)>>>0)}C=a[B]|0;if(C<<24>>24==0){E=A;F=m;break}else{G=m;H=B;I=C}while(1){C=H+1|0;D=G+1|0;a[(c[10036]|0)+G|0]=I;J=a[C]|0;if(J<<24>>24==0){E=A;F=D;break}else{G=D;H=C;I=J}}}}while(0);if((z|0)==1671){z=0;uh(-1,221968,(v=i,i=i+8|0,c[v>>2]=u,v)|0);E=n;F=m}a[w]=x;K=0;L=0;M=w-1|0;N=E;O=F;P=0;Q=0;break L2241}}while(0);a[(c[10036]|0)+m|0]=t;K=q;L=p;M=o;N=n;O=m+1|0;P=g;Q=0}else if((s|0)==34){a[(c[10036]|0)+m|0]=r;K=q;L=p^(f^1);M=o;N=n;O=m+1|0;P=g;Q=0}else if((s|0)==39){a[(c[10036]|0)+m|0]=r;K=q^1;L=p;M=o;N=n;O=m+1|0;P=g;Q=0}else if((s|0)==92){a[(c[10036]|0)+m|0]=r;K=q;L=p;M=o;N=n;O=m+1|0;P=g;Q=f^p}else if((s|0)==35){if(q){R=g;z=1679;break}R=g|p^1;z=1679}else{R=g;z=1679}}while(0);if((z|0)==1679){z=0;a[(c[10036]|0)+m|0]=r;K=q;L=p;M=o;N=n;O=m+1|0;P=R;Q=0}s=M+1|0;y=e-1|0;if((y|0)==0|(s|0)==0){k=O;l=N;break}else{f=Q;g=P;e=y;m=O;n=N;o=s;p=L;q=K}}}}while(0);a[(c[10036]|0)+k|0]=0;uu(h);d=l;i=b;return d|0}function dL(b){b=b|0;var d=0,e=0;d=i;if((c[8501]|0)!=0){e=c[m>>2]|0;cf(e|0,154696,(v=i,i=i+8|0,c[v>>2]=b,v)|0)}hx(0,0,b);e=c[10034]|0;if(e>>>0<((uA(b|0)|0)+1|0)>>>0){do{dG();e=c[10034]|0;}while(e>>>0<((uA(b|0)|0)+1|0)>>>0)}uB(c[10036]|0,b|0);a[25280]=0;c[13484]=dJ()|0;hy();i=d;return}function dM(b){b=b|0;var d=0,e=0;d=i;if((b|0)==0){e=0}else{e=bP(b|0)|0}dL(e);if((a[872]&1)!=0&(c[6928]|0)!=0){if((c[8501]|0)!=0){e=c[m>>2]|0;aI(196688,8,1,e|0)}dO();i=d;return}if((a[27704]&1)==0){dP();i=d;return}else{uh(-1,185416,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=d;return}}function dN(){var a=0,b=0;a=i;b=c[13544]|0;if((b|0)==0){uf(c[13898]|0,103896,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[13544]=b-1;c[13898]=(c[13898]|0)+1;i=a;return}}function dO(){var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0.0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0;b=i;d=c[10814]|0;e=c[6928]|0;if((d|0)==0&(e|0)==2){uf(-1,87408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[10828]|0;if((f|0)==0&(e|0)==3){uf(-1,87408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((e|0)==0){uh(-1,86328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);dP();i=b;return}g=c[16507]|0;c[16506]=g;do{if((g&1|0)==0){j=+h[8257];if((a[66164]&1)==0){k=j;break}l=+_(+j);k=l/+h[8272]}else{k=8.988465674311578e+304}}while(0);h[8255]=k;m=a[66164]|0;do{if((g&2|0)==0){k=+h[8258];if((m&1)==0){n=k;break}l=+_(+k);n=l/+h[8272]}else{n=-8.988465674311578e+304}}while(0);h[8256]=n;if((m&1)==0){o=0.0}else{o=+_(+(+h[8271]))}h[8272]=o;m=c[16335]|0;c[16334]=m;do{if((m&1|0)==0){o=+h[8171];if((a[65476]&1)==0){p=o;break}n=+_(+o);p=n/+h[8186]}else{p=8.988465674311578e+304}}while(0);h[8169]=p;g=a[65476]|0;do{if((m&2|0)==0){p=+h[8172];if((g&1)==0){q=p;break}n=+_(+p);q=n/+h[8186]}else{q=-8.988465674311578e+304}}while(0);h[8170]=q;if((g&1)==0){r=0.0}else{r=+_(+(+h[8185]))}h[8186]=r;g=c[17195]|0;c[17194]=g;do{if((g&1|0)==0){r=+h[8601];if((a[68916]&1)==0){s=r;break}q=+_(+r);s=q/+h[8616]}else{s=8.988465674311578e+304}}while(0);h[8599]=s;m=a[68916]|0;do{if((g&2|0)==0){s=+h[8602];if((m&1)==0){t=s;break}q=+_(+s);t=q/+h[8616]}else{t=-8.988465674311578e+304}}while(0);h[8600]=t;if((m&1)==0){u=0.0}else{u=+_(+(+h[8615]))}h[8616]=u;m=c[17023]|0;c[17022]=m;do{if((m&1|0)==0){u=+h[8515];if((a[68228]&1)==0){w=u;break}t=+_(+u);w=t/+h[8530]}else{w=8.988465674311578e+304}}while(0);h[8513]=w;g=a[68228]|0;do{if((m&2|0)==0){w=+h[8516];if((g&1)==0){x=w;break}t=+_(+w);x=t/+h[8530]}else{x=-8.988465674311578e+304}}while(0);h[8514]=x;if((g&1)==0){y=0.0}else{y=+_(+(+h[8529]))}h[8530]=y;g=c[17539]|0;if((g&1|0)==0){y=+h[8773];if((a[70292]&1)==0){z=y}else{x=+_(+y);z=x/+h[8788]}h[8771]=z}if((g&2|0)==0){z=+h[8774];if((a[70292]&1)==0){A=z}else{x=+_(+z);A=x/+h[8788]}h[8772]=A}g=c[17367]|0;if((g&1|0)==0){A=+h[8687];if((a[69604]&1)==0){B=A}else{x=+_(+A);B=x/+h[8702]}h[8685]=B}if((g&2|0)==0){B=+h[8688];if((a[69604]&1)==0){C=B}else{x=+_(+B);C=x/+h[8702]}h[8686]=C}g=c[16163]|0;if((g&1|0)==0){C=+h[8085];if((a[64788]&1)==0){D=C}else{x=+_(+C);D=x/+h[8100]}h[8083]=D}if((g&2|0)==0){D=+h[8086];if((a[64788]&1)==0){E=D}else{x=+_(+D);E=x/+h[8100]}h[8084]=E}g=c[16679]|0;if((g&1|0)==0){E=+h[8343];if((a[66852]&1)==0){F=E}else{x=+_(+E);F=x/+h[8358]}h[8341]=F}if((g&2|0)==0){F=+h[8344];if((a[66852]&1)==0){G=F}else{x=+_(+F);G=x/+h[8358]}h[8342]=G}if((e|0)==2){iU(d,c[6930]|0)}else if((e|0)==3){i$(f,c[6930]|0)}if((c[16508]&2|0)!=0){G=+h[8256];h[8256]=+h[8255];h[8255]=G}if((c[16336]&2|0)!=0){G=+h[8170];h[8170]=+h[8169];h[8169]=G}if((c[17196]&2|0)!=0){G=+h[8600];h[8600]=+h[8599];h[8599]=G}if((c[17024]&2|0)!=0){G=+h[8514];h[8514]=+h[8513];h[8513]=G}f=c[6928]|0;if((f|0)==2){fS(c[10814]|0,c[6930]|0);i=b;return}else if((f|0)==3){fv(c[10828]|0,c[6930]|0,0);i=b;return}else{uf(-1,85504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function dP(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0;b=i;d=c[13898]|0;e=c[8272]|0;f=(e|0)>(d|0);L2430:do{if(f){g=c[1054]|0;if((a[g+(d*40&-1)|0]&1)!=0){h=c[g+(d*40&-1)+36>>2]|0;j=g+(d*40&-1)+32|0;g=c[10036]|0;k=0;while(1){if((k|0)>=(h|0)){l=1801;break}if((a[g+((c[j>>2]|0)+k|0)|0]|0)==(a[k+78280|0]|0)){k=k+1|0}else{break}}do{if((l|0)==1801){if((k|0)!=1){break}uf(d,77152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if(!f){l=1804;break}}k=c[1054]|0;L2442:do{if((a[k+(d*40&-1)|0]&1)==0){m=k+(d*40&-1)+32|0}else{j=c[k+(d*40&-1)+36>>2]|0;g=k+(d*40&-1)+32|0;h=c[10036]|0;n=0;while(1){if((n|0)>=(j|0)){break}if((a[h+((c[g>>2]|0)+n|0)|0]|0)==(a[n+103664|0]|0)){n=n+1|0}else{m=g;break L2442}}if((n|0)==1){o=k;p=h;l=1811;break L2430}else{m=g}}}while(0);j=e-1|0;q=uA(c[6924]|0)|0;r=c[k+(j*40&-1)+32>>2]|0;s=c[k+(j*40&-1)+36>>2]|0;j=c[m>>2]|0;t=(((q+3|0)+r|0)+s|0)-j|0;q=s+r|0;r=db(0,(q+1|0)-j|0,116456)|0;j=c[(c[1054]|0)+(d*40&-1)+32>>2]|0;L2450:do{if((j|0)<(q|0)){s=j;u=r;while(1){w=a[(c[10036]|0)+s|0]|0;if(w<<24>>24==0){x=u;break L2450}y=u+1|0;a[u]=w;w=s+1|0;if((w|0)<(q|0)){s=w;u=y}else{x=y;break}}}else{x=r}}while(0);a[x]=0;if((c[10034]|0)>>>0<t>>>0){do{dG();}while((c[10034]|0)>>>0<t>>>0)}t=c[10036]|0;q=c[6924]|0;uB(t|0,q|0);q=c[10036]|0;t=q+(uA(q|0)|0)|0;a[t]=a[86120]|0;a[t+1|0]=a[86121|0]|0;a[t+2|0]=a[86122|0]|0;t=c[10036]|0;uC(t|0,r|0);uu(r)}else{l=1804}}while(0);if((l|0)==1804){o=c[1054]|0;p=c[10036]|0;l=1811}if((l|0)==1811){l=p+(c[o+(d*40&-1)+32>>2]|0)|0;d=uA(c[6924]|0)|0;o=uA(l|0)|0;x=o+1|0;uH(p|0,l|0,x|0);l=(d+1|0)+o|0;if((c[10034]|0)>>>0<l>>>0){do{dG();}while((c[10034]|0)>>>0<l>>>0)}l=c[10036]|0;uH(l+d|0,l|0,x|0);uD(c[10036]|0,c[6924]|0,d)}c[7738]=0;c[6928]=0;a[25280]=0;c[8272]=jl(40144,40136)|0;c[13898]=1;if((a[37384]&1)==0){iS();i=b;return}else{iZ();i=b;return}}function dQ(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;b=i;i=i+56|0;d=b|0;e=d|0;f=(c[13898]|0)+1|0;c[13898]=f;g=c[8272]|0;if((f|0)>=(g|0)){i=b;return}h=d|0;j=f;f=g;L2477:while(1){g=c[1054]|0;k=c[g+(j*40&-1)+36>>2]|0;L2479:do{if((a[g+(j*40&-1)|0]&1)==0){l=c[10036]|0;m=g+(j*40&-1)+32|0}else{n=g+(j*40&-1)+32|0;o=c[10036]|0;p=0;while(1){if((p|0)>=(k|0)){break}if((a[o+((c[n>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{l=o;m=n;break L2479}}if((p|0)==1){q=1848;break L2477}else{l=o;m=n}}}while(0);uD(e|0,l+(c[m>>2]|0)|0,(k|0)<49?k:49);a[d+((k|0)>49?49:k)|0]=0;r=j+1|0;L2487:do{if((f|0)>(r|0)){if((a[g+(r*40&-1)|0]&1)==0){s=0;t=j;break}u=c[g+(r*40&-1)+36>>2]|0;v=g+(r*40&-1)+32|0;w=0;while(1){if((w|0)>=(u|0)){break}if((a[l+((c[v>>2]|0)+w|0)|0]|0)==(a[w+134808|0]|0)){w=w+1|0}else{s=0;t=j;break L2487}}if((w|0)!=1){s=0;t=j;break}c[13898]=r;s=1;t=r}else{s=0;t=j}}while(0);do{if((a_(h|0,167320,6)|0)==0){x=t;y=f}else{if((a_(h|0,126208,6)|0)==0){x=t;y=f;break}e2(h,s);x=c[13898]|0;y=c[8272]|0}}while(0);r=x+1|0;c[13898]=r;if((r|0)<(y|0)){j=r;f=y}else{q=1847;break}}if((q|0)==1847){i=b;return}else if((q|0)==1848){i=b;return}}function dR(){dU(0);return}function dS(){dU(1);return}function dT(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;b=i;i=i+696|0;d=b|0;e=b+56|0;f=b+80|0;g=c[13898]|0;h=g+1|0;j=c[8272]|0;k=c[1054]|0;L2506:do{if((j|0)>(h|0)){if((a[k+(h*40&-1)|0]&1)==0){break}l=c[k+(h*40&-1)+36>>2]|0;m=k+(h*40&-1)+32|0;n=c[10036]|0;o=0;while(1){if((o|0)>=(l|0)){break}if((a[n+((c[m>>2]|0)+o|0)|0]|0)==(a[o+199040|0]|0)){o=o+1|0}else{break L2506}}if((o|0)!=1){break}uD(f|0,55600,612);m=0;l=g;L2514:while(1){p=l+2|0;c[13898]=p;q=c[k+(p*40&-1)+36>>2]|0;r=(q|0)>49?49:q;q=c[k+(p*40&-1)+32>>2]|0;s=0;while(1){t=s+1|0;a[55600+(m*51&-1)+s|0]=a[n+q|0]|0;if((t|0)==(r|0)){break}else{q=q+1|0;s=t}}u=m+1|0;a[55600+(m*51&-1)+r|0]=0;w=l+3|0;x=(j|0)>(w|0);if(!x){break}if((a[k+(w*40&-1)|0]&1)==0){y=1867;break}s=c[k+(w*40&-1)+36>>2]|0;q=k+(w*40&-1)+32|0;t=0;while(1){if((t|0)>=(s|0)){break}if((a[n+((c[q>>2]|0)+t|0)|0]|0)==(a[t+148464|0]|0)){t=t+1|0}else{y=1866;break L2514}}if((t|0)==1&(u|0)<12){m=u;l=p}else{y=1866;break}}if((y|0)==1866){if(x){y=1867}}L2527:do{if((y|0)==1867){if((a[k+(w*40&-1)|0]&1)==0){break}m=c[k+(w*40&-1)+36>>2]|0;o=k+(w*40&-1)+32|0;q=0;while(1){if((q|0)>=(m|0)){break}if((a[n+((c[o>>2]|0)+q|0)|0]|0)==(a[q+148464|0]|0)){q=q+1|0}else{break L2527}}if((q|0)!=1){break}uf(l+4|0,172072,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);o=l+5|0;c[13898]=o;if((o|0)>=(j|0)){uf(o,168144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L2539:do{if((a[k+(o*40&-1)|0]&1)!=0){m=c[k+(o*40&-1)+36>>2]|0;p=k+(o*40&-1)+32|0;t=0;while(1){if((t|0)>=(m|0)){break}if((a[n+((c[p>>2]|0)+t|0)|0]|0)==(a[t+103664|0]|0)){t=t+1|0}else{break L2539}}if((t|0)!=1){break}uf(o,168144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);o=iv(g)|0;c[11670]=o;c[o+16>>2]=u;it();n=c[18070]|0;l=db(n,c[n>>2]<<5|8,105624)|0;c[18070]=0;if((l|0)==0){uf(g,152776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=o+8|0;p=c[n>>2]|0;if((p|0)!=0){e1(p)}c[n>>2]=l;uD(55600,f|0,612);l=o+12|0;n=(c[13898]|0)-1|0;p=c[1054]|0;m=(c[p+(n*40&-1)+36>>2]|0)+(c[p+(n*40&-1)+32>>2]|0)|0;n=db(c[l>>2]|0,(m+1|0)-(c[p+(g*40&-1)+32>>2]|0)|0,116456)|0;c[l>>2]=n;p=c[(c[1054]|0)+(g*40&-1)+32>>2]|0;L2553:do{if((p|0)<(m|0)){q=p;s=n;while(1){r=a[(c[10036]|0)+q|0]|0;if(r<<24>>24==0){z=s;break L2553}A=s+1|0;a[s]=r;r=q+1|0;if((r|0)<(m|0)){q=r;s=A}else{z=A;break}}}else{z=n}}while(0);a[z]=0;c[11670]=0;n=o+4|0;m=(uA(c[n>>2]|0)|0)+8|0;p=ut(m)|0;do{if((p|0)==0){gk();s=ut(m)|0;if((s|0)!=0){B=s;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=154560,v)|0)}else{B=p}}while(0);a[B]=a[151320]|0;a[B+1|0]=a[151321|0]|0;a[B+2|0]=a[151322|0]|0;a[B+3|0]=a[151323|0]|0;a[B+4|0]=a[151324|0]|0;a[B+5|0]=a[151325|0]|0;a[B+6|0]=a[151326|0]|0;uC(B|0,c[n>>2]|0);p=c[l>>2]|0;m=e6(B)|0;L2562:do{if((m|0)!=0){o=m+8|0;do{if((a[o]&1)==0){s=c[m+24>>2]|0;if((aY(s|0,p|0)|0)==0){break L2562}q=m+16|0;if((c[q>>2]|0)!=3){break}uu(s);c[q>>2]=1}else{a[o]=0}}while(0);if((p|0)==0){C=0}else{C=bP(p|0)|0}c[m+16>>2]=3;c[m+24>>2]=C}}while(0);uu(B);i=b;return}}while(0);B=c[10036]|0;C=k+(g*40&-1)+32|0;z=B+(c[C>>2]|0)|0;if((a_(z|0,167320,6)|0)==0){uf(g,145392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a_(z|0,126208,6)|0)==0){uf(g,145392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=g+2;z=d|0;f=c[k+(g*40&-1)+36>>2]|0;g=(f|0)>49?49:f;f=c[C>>2]|0;C=0;while(1){k=C+1|0;a[d+C|0]=a[B+f|0]|0;if((k|0)==(g|0)){break}else{f=f+1|0;C=k}}a[d+g|0]=0;g=e6(z)|0;is(e);z=g+8|0;do{if((a[z]&1)==0){d=g+16|0;if((c[d>>2]|0)!=3){break}uu(c[g+24>>2]|0);c[d>>2]=1}}while(0);d=g+16|0;g=e;c[d>>2]=c[g>>2];c[d+4>>2]=c[g+4>>2];c[d+8>>2]=c[g+8>>2];c[d+12>>2]=c[g+12>>2];c[d+16>>2]=c[g+16>>2];c[d+20>>2]=c[g+20>>2];a[z]=0;i=b;return}function dU(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;d=i;i=i+24|0;e=d|0;f=c[13898]|0;g=f+1|0;c[13898]=g;h=c[8272]|0;if((g|0)>=(h|0)){i=d;return}j=c[1054]|0;k=(a[j+(g*40&-1)|0]&1)==0;L2594:do{if(k){l=g}else{m=c[j+(g*40&-1)+36>>2]|0;n=j+(g*40&-1)+32|0;o=c[10036]|0;p=0;while(1){if((p|0)>=(m|0)){q=1921;break}if((a[o+((c[n>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{break}}do{if((q|0)==1921){if((p|0)!=1){break}i=d;return}}while(0);if(k){l=g;break}p=c[j+(g*40&-1)+36>>2]|0;n=j+(g*40&-1)+32|0;o=c[10036]|0;m=0;while(1){if((m|0)>=(p|0)){q=1926;break}if((a[o+((c[n>>2]|0)+m|0)|0]|0)==(a[m+202112|0]|0)){m=m+1|0}else{q=1927;break}}if((q|0)==1926){if((m|0)!=1){q=1927}}if((q|0)==1927){if(k){l=g;break}n=c[j+(g*40&-1)+36>>2]|0;o=j+(g*40&-1)+32|0;p=c[10036]|0;r=0;while(1){if((r|0)>=(n|0)){break}if((a[p+((c[o>>2]|0)+r|0)|0]|0)==(a[r+129568|0]|0)){r=r+1|0}else{l=g;break L2594}}if((r|0)!=1){l=g;break}}o=f+2|0;c[13898]=o;l=o}}while(0);L2618:do{if((l|0)<(h|0)){f=(a[j+(l*40&-1)|0]&1)==0;do{if(!f){g=c[j+(l*40&-1)+36>>2]|0;k=j+(l*40&-1)+32|0;o=c[10036]|0;p=0;while(1){if((p|0)>=(g|0)){q=1938;break}if((a[o+((c[k>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{q=1939;break}}if((q|0)==1938){if((p|0)==1|f^1){break L2618}else{break}}else if((q|0)==1939){if(f){break}else{break L2618}}}}while(0);f=is(e)|0;r=c[f>>2]|0;if((r|0)==3){k=c[f+8>>2]|0;uz(k,0)}else if(!((r|0)==1|(r|0)==2)){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}r=e|0;if((c[r>>2]|0)==3){uu(c[e+8>>2]|0);c[r>>2]=1}c[13898]=(c[13898]|0)+1;i=d;return}}while(0);if((b|0)==0){uf(l,136616,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uf(l,138312,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function dV(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0;b=i;d=c[13898]|0;e=d+1|0;c[13898]=e;f=c[8272]|0;g=(e|0)<(f|0);L2642:do{if(g){h=c[1054]|0;j=(a[h+(e*40&-1)|0]&1)==0;if(!j){k=c[h+(e*40&-1)+36>>2]|0;l=h+(e*40&-1)+32|0;n=c[10036]|0;o=0;while(1){if((o|0)>=(k|0)){p=1958;break}if((a[n+((c[l>>2]|0)+o|0)|0]|0)==(a[o+103664|0]|0)){o=o+1|0}else{p=1959;break}}if((p|0)==1959){if(!j){p=1960}}else if((p|0)==1958){if(!((o|0)==1|j)){p=1960}}L2652:do{if((p|0)==1960){l=c[h+(e*40&-1)+36>>2]|0;n=h+(e*40&-1)+32|0;k=c[10036]|0;q=0;while(1){if((q|0)>=(l|0)){break}if((a[k+((c[n>>2]|0)+q|0)|0]|0)==(a[q+124624|0]|0)){q=q+1|0}else{break L2652}}if((q|0)!=1){break}n=c[14128]|0;if((n|0)==0){r=e}else{k=n;while(1){n=k+12|0;l=c[n>>2]|0;if((l|0)!=0){uu(l);c[n>>2]=0}n=c[k+24>>2]|0;uu(k);if((n|0)==0){break}else{k=n}}r=c[13898]|0}c[14128]=0;c[13898]=r+1;i=b;return}}while(0);if(!g){s=0;t=e;break}}h=c[1054]|0;j=(a[h+(e*40&-1)|0]&1)==0;if(j){s=0;t=e;break}o=c[h+(e*40&-1)+36>>2]|0;k=h+(e*40&-1)+32|0;h=c[10036]|0;q=0;while(1){if((q|0)>=(o|0)){p=1975;break}if((a[h+((c[k>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break}}if((p|0)==1975){if((q|0)==1){s=0;t=e;break}}if(!((o|0)>0&(j^1))){s=0;t=e;break}h=c[10036]|0;n=0;l=0;u=c[k>>2]|0;while(1){if((a[n+133296|0]|0)==(a[h+(n+u|0)|0]|0)){w=u;x=l}else{if((n|0)!=3){s=0;t=e;break L2642}w=u-1|0;x=1}y=n+1|0;if((y|0)<(x+o|0)){n=y;l=x;u=w}else{break}}if((x|0)==0){if(!((n|0)==2|(n|0)==10)){s=0;t=e;break}}u=d+2|0;c[13898]=u;s=1;t=u}else{s=0;t=e}}while(0);L2689:do{if((t|0)<(f|0)){e=c[1054]|0;L2691:do{if((a[e+(t*40&-1)|0]&1)==0){z=c[10036]|0;A=e+(t*40&-1)+32|0}else{d=c[e+(t*40&-1)+36>>2]|0;x=e+(t*40&-1)+32|0;w=c[10036]|0;g=0;while(1){if((g|0)>=(d|0)){break}if((a[w+((c[x>>2]|0)+g|0)|0]|0)==(a[g+103664|0]|0)){g=g+1|0}else{z=w;A=x;break L2691}}if((g|0)==1){B=0;C=t;D=f;break L2689}else{z=w;A=x}}}while(0);e=z+(c[A>>2]|0)|0;n=e;d=(a8(e|0,32)|0)-n|0;do{if((d|0)<0){r=(a8(e|0,0)|0)-n|0;if((r|0)>=0){E=r;break}r=c[m>>2]|0;cf(r|0,131904,(v=i,i=i+16|0,c[v>>2]=130592,c[v+8>>2]=754,v)|0);i=b;return}else{E=d}}while(0);d=E+1|0;n=ut(d)|0;do{if((n|0)==0){gk();e=ut(d)|0;if((e|0)!=0){F=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=129544,v)|0)}else{F=n}}while(0);n=c[13898]|0;d=c[1054]|0;e=a[d+(n*40&-1)|0]|0;do{if((e&1)==0){p=2e3}else{r=c[d+(n*40&-1)+32>>2]|0;u=c[10036]|0;l=a[u+r|0]|0;if(!((l<<24>>24|0)==39|(l<<24>>24|0)==34)){p=2e3;break}l=(c[d+(n*40&-1)+36>>2]|0)-2|0;L2711:do{if((l|0)>0){o=r;h=0;k=u;while(1){j=o+1|0;q=h+1|0;a[F+h|0]=a[k+j|0]|0;if((q|0)==(l|0)){G=l;break L2711}o=j;h=q;k=c[10036]|0}}else{G=0}}while(0);a[F+G|0]=0;if((a[(c[10036]|0)+(c[(c[1054]|0)+(n*40&-1)+32>>2]|0)|0]|0)==34){ua(F);break}else{H=F;I=F}while(1){l=a[I]|0;if((l<<24>>24|0)==0){break}else if((l<<24>>24|0)==39){u=I+1|0;r=(a[u]|0)==39?u:I;J=r;K=a[r]|0}else{J=I;K=l}a[H]=K;H=H+1|0;I=J+1|0}a[H]=0}}while(0);L2724:do{if((p|0)==2e3){if((n|0)<(c[8272]|0)){L=F;M=n;N=d;O=e}else{break}while(1){l=c[N+(M*40&-1)+36>>2]|0;r=N+(M*40&-1)+32|0;L2728:do{if((O&1)!=0){u=c[10036]|0;x=0;while(1){if((x|0)>=(l|0)){break}if((a[u+((c[r>>2]|0)+x|0)|0]|0)==(a[x+103664|0]|0)){x=x+1|0}else{break L2728}}if((x|0)==1){break L2724}}}while(0);u=c[r>>2]|0;w=0;while(1){g=w+1|0;a[L+w|0]=a[(c[10036]|0)+u|0]|0;if((g|0)==(l|0)){break}u=u+1|0;w=g}a[L+l|0]=0;w=c[13898]|0;u=c[1054]|0;r=c[u+(w*40&-1)+36>>2]|0;g=a[(c[10036]|0)+((c[u+(w*40&-1)+32>>2]|0)+r|0)|0]|0;if((g<<24>>24|0)==32|(g<<24>>24|0)==9|(g<<24>>24|0)==0){break L2724}g=w+1|0;c[13898]=g;if((g|0)>=(c[8272]|0)){break L2724}L=L+r|0;M=g;N=u;O=a[u+(g*40&-1)|0]|0}}}while(0);e=(c[13898]|0)+1|0;c[13898]=e;B=F;C=e;D=c[8272]|0}else{B=0;C=t;D=f}}while(0);L2743:do{if((C|0)<(D|0)){f=c[1054]|0;L2745:do{if((a[f+(C*40&-1)|0]&1)==0){P=c[10036]|0}else{t=c[f+(C*40&-1)+36>>2]|0;F=f+(C*40&-1)+32|0;O=c[10036]|0;N=0;while(1){if((N|0)>=(t|0)){break}if((a[O+((c[F>>2]|0)+N|0)|0]|0)==(a[N+103664|0]|0)){N=N+1|0}else{P=O;break L2745}}if((N|0)==1){Q=0;break L2743}else{P=O}}}while(0);f=(uA(P|0)|0)+1|0;F=ut(f)|0;do{if((F|0)==0){gk();t=ut(f)|0;if((t|0)!=0){R=t;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=128272,v)|0)}else{R=F}}while(0);F=c[13898]|0;f=c[1054]|0;t=a[f+(F*40&-1)|0]|0;do{if((t&1)!=0){M=c[f+(F*40&-1)+32>>2]|0;L=c[10036]|0;p=a[L+M|0]|0;if(!((p<<24>>24|0)==39|(p<<24>>24|0)==34)){break}p=(c[f+(F*40&-1)+36>>2]|0)-2|0;L2760:do{if((p|0)>0){H=M;J=0;I=L;while(1){K=H+1|0;G=J+1|0;a[R+J|0]=a[I+K|0]|0;if((G|0)==(p|0)){S=p;break L2760}H=K;J=G;I=c[10036]|0}}else{S=0}}while(0);a[R+S|0]=0;if((a[(c[10036]|0)+(c[(c[1054]|0)+(F*40&-1)+32>>2]|0)|0]|0)==34){ua(R)}else{p=R;L=R;while(1){M=a[L]|0;if((M<<24>>24|0)==39){O=L+1|0;N=(a[O]|0)==39?O:L;T=N;U=a[N]|0}else if((M<<24>>24|0)==0){break}else{T=L;U=M}a[p]=U;p=p+1|0;L=T+1|0}a[p]=0}c[13898]=(c[13898]|0)+1;Q=R;break L2743}}while(0);if((F|0)<(c[8272]|0)){V=R;W=F;X=f;Y=t}else{Q=R;break}while(1){L=c[X+(W*40&-1)+36>>2]|0;M=X+(W*40&-1)+32|0;L2777:do{if((Y&1)!=0){N=c[10036]|0;O=0;while(1){if((O|0)>=(L|0)){break}if((a[N+((c[M>>2]|0)+O|0)|0]|0)==(a[O+103664|0]|0)){O=O+1|0}else{break L2777}}if((O|0)==1){Q=R;break L2743}}}while(0);p=c[M>>2]|0;N=0;while(1){I=N+1|0;a[V+N|0]=a[(c[10036]|0)+p|0]|0;if((I|0)==(L|0)){break}p=p+1|0;N=I}a[V+L|0]=0;N=c[13898]|0;p=c[1054]|0;M=c[p+(N*40&-1)+36>>2]|0;I=V+M|0;J=a[(c[10036]|0)+((c[p+(N*40&-1)+32>>2]|0)+M|0)|0]|0;if((J<<24>>24|0)==32|(J<<24>>24|0)==9|(J<<24>>24|0)==0){J=V+(M+1|0)|0;a[I]=32;a[J]=0;Z=J;_=c[13898]|0}else{Z=I;_=N}N=_+1|0;c[13898]=N;if((N|0)>=(c[8272]|0)){Q=R;break L2743}I=c[1054]|0;V=Z;W=N;X=I;Y=a[I+(N*40&-1)|0]|0}}else{Q=0}}while(0);hR(B,Q,s);i=b;return}function dW(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;b=i;i=i+32|0;d=b|0;e=b+24|0;c[e>>2]=0;f=(c[13898]|0)+1|0;c[13898]=f;L2795:do{if((f|0)<(c[8272]|0)){g=c[1054]|0;L2797:do{if((a[g+(f*40&-1)|0]&1)!=0){h=c[g+(f*40&-1)+36>>2]|0;j=g+(f*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(h|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{break L2797}}if((l|0)==1){break L2795}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=f;break}g=c[d+8>>2]|0;c[e>>2]=g;if((g|0)!=0){iQ(e);g=c[e>>2]|0;hw(hA(g,193632)|0,g,1);i=b;return}m=c[13898]|0;uf(m,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[e>>2]=0;m=f;uf(m,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function dX(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;b=i;i=i+32|0;d=b|0;e=b+24|0;c[e>>2]=0;f=(c[13898]|0)+1|0;c[13898]=f;L2814:do{if((f|0)<(c[8272]|0)){g=c[1054]|0;L2816:do{if((a[g+(f*40&-1)|0]&1)!=0){h=c[g+(f*40&-1)+36>>2]|0;j=g+(f*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(h|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{break L2816}}if((l|0)==1){break L2814}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=f;break}g=c[d+8>>2]|0;c[e>>2]=g;if((g|0)==0){m=c[13898]|0;uf(m,124592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}iQ(e);g=c[e>>2]|0;if((bY(g|0)|0)==0){e7(5);uu(g);i=b;return}else{uf(c[13898]|0,123232,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);c[e>>2]=0;m=f;uf(m,124592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function dY(){var b=0,d=0,e=0,f=0,h=0,i=0,j=0.0,k=0.0;if((a[14080]&1)==0){lk()}do{if(a[14088]|0){if((a[33512]&1)==0){break}if(!(a[13032]|0)){break}b=c[(c[3524]|0)+104>>2]|0;if((b|0)!=0){cS[b&511]()}a[13032]=0}else{cS[c[(c[3524]|0)+52>>2]&511]();a[14088]=1}}while(0);cM[c[(c[3524]|0)+168>>2]&511](0);b=c[3524]|0;if((c[b+96>>2]&16|0)!=0){c[7271]=-1}c[13880]=0;d=b+8|0;c[13881]=(c[d>>2]|0)-1;c[13882]=0;e=b+12|0;c[13883]=(c[e>>2]|0)-1;if((a[33512]&1)==0){lh();a[25280]=0;f=c[13898]|0;h=f+1|0;c[13898]=h;return}i=c[b+108>>2]|0;if((i|0)==0){lh();a[25280]=0;f=c[13898]|0;h=f+1|0;c[13898]=h;return}j=+((c[d>>2]|0)>>>0>>>0);k=+((c[e>>2]|0)>>>0>>>0);cI[i&63](0,~~(+g[184]*j),~~(+g[44]*k),~~(j*+g[178]),~~(k*+g[38]));lh();a[25280]=0;f=c[13898]|0;h=f+1|0;c[13898]=h;return}function dZ(){var a=0;a=i;c[13898]=(c[13898]|0)+1;uh(-1,116984,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=a;return}function d_(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;b=i;i=i+24|0;d=b|0;e=(c[13898]|0)+1|0;c[13898]=e;L2860:do{if((e|0)<(c[8272]|0)){f=c[1054]|0;L2862:do{if((a[f+(e*40&-1)|0]&1)!=0){g=c[f+(e*40&-1)+36>>2]|0;h=f+(e*40&-1)+32|0;j=c[10036]|0;k=0;while(1){if((k|0)>=(g|0)){break}if((a[j+((c[h>>2]|0)+k|0)|0]|0)==(a[k+103664|0]|0)){k=k+1|0}else{break L2862}}if((k|0)==1){l=e;break L2860}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=e;l=e;break}f=c[d+8>>2]|0;if((f|0)==0){l=c[13898]|0;break}else{dL(f);i=b;return}}else{l=e}}while(0);uf(l,122e3,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function d$(){var b=0,d=0,e=0,f=0,g=0;b=(c[13898]|0)+1|0;if((c[8272]|0)<=(b|0)){c[13484]=1;return}d=c[1054]|0;if((a[d+(b*40&-1)|0]&1)==0){c[13484]=1;return}e=c[d+(b*40&-1)+36>>2]|0;f=d+(b*40&-1)+32|0;b=c[10036]|0;d=0;while(1){if((d|0)>=(e|0)){break}if((a[b+((c[f>>2]|0)+d|0)|0]|0)==(a[d+78208|0]|0)){d=d+1|0}else{g=2133;break}}if((g|0)==2133){c[13484]=1;return}if((d|0)==7){bd(0)}else{c[13484]=1;return}}function d0(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;b=i;i=i+24|0;d=b|0;e=(c[13898]|0)+1|0;c[13898]=e;if((c[8272]|0)<=(e|0)){uf(e,116032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[1054]|0;if((a[f+(e*40&-1)|0]&1)==0){uf(e,116032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=c[f+(e*40&-1)+36>>2]|0;j=f+(e*40&-1)+32|0;f=c[10036]|0;k=0;while(1){if((k|0)>=(g|0)){break}if((a[f+((c[j>>2]|0)+k|0)|0]|0)==(a[k+199040|0]|0)){k=k+1|0}else{l=2234;break}}if((l|0)==2234){uf(e,116032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((k|0)!=1){uf(e,116032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=is(d)|0;k=c[e>>2]|0;if((k|0)==1){m=+(c[e+8>>2]|0)}else if((k|0)==3){m=+uz(c[e+8>>2]|0,0)}else if((k|0)==2){m=+h[e+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d|0;if((c[e>>2]|0)==3){uu(c[d+8>>2]|0);c[e>>2]=1}e=c[13898]|0;d=c[8272]|0;k=(d|0)>(e|0);L2917:do{if(k){j=c[1054]|0;if((a[j+(e*40&-1)|0]&1)==0){break}f=c[j+(e*40&-1)+36>>2]|0;g=j+(e*40&-1)+32|0;n=c[10036]|0;o=0;while(1){if((o|0)>=(f|0)){break}if((a[n+((c[g>>2]|0)+o|0)|0]|0)==(a[o+126728|0]|0)){o=o+1|0}else{break L2917}}if((o|0)!=1){break}f=c[g>>2]|0;p=e+1|0;c[13898]=p;q=1;r=p;while(1){if((r|0)>=(d|0)){l=2159;break}L2928:do{if((a[j+(r*40&-1)|0]&1)==0){l=2166}else{p=c[j+(r*40&-1)+36>>2]|0;s=j+(r*40&-1)+32|0;t=0;while(1){if((t|0)>=(p|0)){break}if((a[n+((c[s>>2]|0)+t|0)|0]|0)==(a[t+126728|0]|0)){t=t+1|0}else{l=2166;break L2928}}if((t|0)!=1){l=2166;break}u=q+1|0}}while(0);if((l|0)==2166){l=0;L2937:do{if((a[j+(r*40&-1)|0]&1)==0){w=0}else{s=c[j+(r*40&-1)+36>>2]|0;p=j+(r*40&-1)+32|0;x=0;while(1){if((x|0)>=(s|0)){break}if((a[n+((c[p>>2]|0)+x|0)|0]|0)==(a[x+125616|0]|0)){x=x+1|0}else{w=0;break L2937}}w=((x|0)==1)<<31>>31}}while(0);u=w+q|0}p=r+1|0;if((u|0)==0){y=p;break}else{q=u;r=p}}if((l|0)==2159){y=r+1|0}q=c[j+(r*40&-1)+32>>2]|0;c[13898]=y;L2948:do{if((d|0)>(y|0)){if((a[j+(y*40&-1)|0]&1)==0){z=0;A=0;B=y;break}g=c[j+(y*40&-1)+36>>2]|0;o=j+(y*40&-1)+32|0;p=0;while(1){if((p|0)>=(g|0)){break}if((a[n+((c[o>>2]|0)+p|0)|0]|0)==(a[p+200904|0]|0)){p=p+1|0}else{z=0;A=0;B=y;break L2948}}if((p|0)!=4){z=0;A=0;B=y;break}o=y+1|0;c[13898]=o;if((d|0)<=(o|0)){uf(o,114208,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[j+(o*40&-1)|0]&1)==0){uf(o,114208,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=c[j+(o*40&-1)+36>>2]|0;s=j+(o*40&-1)+32|0;t=0;while(1){if((t|0)>=(g|0)){break}if((a[n+((c[s>>2]|0)+t|0)|0]|0)==(a[t+126728|0]|0)){t=t+1|0}else{l=2242;break}}if((l|0)==2242){uf(o,114208,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((t|0)!=1){uf(o,114208,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=c[s>>2]|0;p=y+2|0;c[13898]=p;C=1;D=p;while(1){if((D|0)>=(d|0)){l=2188;break}L2974:do{if((a[j+(D*40&-1)|0]&1)==0){l=2195}else{p=c[j+(D*40&-1)+36>>2]|0;E=j+(D*40&-1)+32|0;F=0;while(1){if((F|0)>=(p|0)){break}if((a[n+((c[E>>2]|0)+F|0)|0]|0)==(a[F+126728|0]|0)){F=F+1|0}else{l=2195;break L2974}}if((F|0)!=1){l=2195;break}G=C+1|0}}while(0);if((l|0)==2195){l=0;L2983:do{if((a[j+(D*40&-1)|0]&1)==0){H=0}else{x=c[j+(D*40&-1)+36>>2]|0;E=j+(D*40&-1)+32|0;p=0;while(1){if((p|0)>=(x|0)){break}if((a[n+((c[E>>2]|0)+p|0)|0]|0)==(a[p+125616|0]|0)){p=p+1|0}else{H=0;break L2983}}H=((p|0)==1)<<31>>31}}while(0);G=H+C|0}E=D+1|0;if((G|0)==0){I=E;break}else{C=G;D=E}}if((l|0)==2188){I=D+1|0}C=c[j+(D*40&-1)+32>>2]|0;c[13898]=I;z=C;A=g;B=I}else{z=0;A=0;B=y}}while(0);j=m!=0.0;n=j?f:A;a[37480]=j&1;r=(A|0)==0;a[37464]=r&1;c[13544]=(c[13544]|0)+1;if(j|r^1){r=(j?q:z)-n|0;j=ut(r)|0;do{if((j|0)==0){gk();C=ut(r)|0;if((C|0)!=0){J=C;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=110688,v)|0)}else{J=j}}while(0);uD(J|0,(c[10036]|0)+(n+1|0)|0,r);a[J+(r-1|0)|0]=0;dL(J);K=c[13898]|0}else{K=B}c[13898]=K-1;i=b;return}}while(0);if((c[13544]|0)>0){uf(e,107896,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9368]=(c[9368]|0)+1;if(m!=0.0){K=d-1|0;B=c[1054]|0;J=(c[B+(K*40&-1)+36>>2]|0)+(c[B+(K*40&-1)+32>>2]|0)|0;K=e-1|0;c[13898]=K;c[B+(K*40&-1)+36>>2]=1;K=J+2|0;c[(c[1054]|0)+((c[13898]|0)*40&-1)+32>>2]=K;a[(c[10036]|0)+K|0]=59;a[(c[10036]|0)+(J+3|0)|0]=0;a[37480]=1;i=b;return}L3011:do{if(k){J=e;L3012:while(1){L3014:do{if((J|0)<(d|0)){K=c[1054]|0;B=J;while(1){L3018:do{if((a[K+(B*40&-1)|0]&1)==0){l=2221}else{z=c[K+(B*40&-1)+36>>2]|0;A=K+(B*40&-1)+32|0;y=c[10036]|0;I=0;while(1){if((I|0)>=(z|0)){break}if((a[y+((c[A>>2]|0)+I|0)|0]|0)==(a[I+103664|0]|0)){I=I+1|0}else{l=2221;break L3018}}A=B+1|0;c[13898]=A;if((I|0)==1){L=A;break L3014}else{M=A}}}while(0);if((l|0)==2221){l=0;p=B+1|0;c[13898]=p;M=p}if((M|0)<(d|0)){B=M}else{N=M;l=2216;break}}}else{N=J;l=2216}}while(0);if((l|0)==2216){l=0;g=N+1|0;c[13898]=g;L=g}L3030:do{if((d|0)>(L|0)){g=c[1054]|0;if((a[g+(L*40&-1)|0]&1)==0){break}D=c[g+(L*40&-1)+36>>2]|0;B=g+(L*40&-1)+32|0;g=c[10036]|0;K=0;while(1){if((K|0)>=(D|0)){break}if((a[g+((c[B>>2]|0)+K|0)|0]|0)==(a[K+200904|0]|0)){K=K+1|0}else{break L3030}}if((K|0)==4){break L3012}}}while(0);if((L|0)<(d|0)){J=L}else{break L3011}}a[37480]=0;c[13898]=L-1;i=b;return}}while(0);c[8272]=0;c[13898]=0;i=b;return}function d1(){return}function d2(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0;b=i;d=c[13898]|0;e=d+1|0;f=c[8272]|0;L3044:do{if((f|0)>(e|0)){g=c[1054]|0;if((a[g+(e*40&-1)|0]&1)==0){break}h=c[g+(e*40&-1)+36>>2]|0;j=g+(e*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(h|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+126728|0]|0)){l=l+1|0}else{break L3044}}if((l|0)!=1){break}if((a[37464]&1)==0){uf(d,106968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}a[37464]=0;c[13898]=e;h=c[j>>2]|0;m=d+2|0;c[13898]=m;n=1;o=m;while(1){if((o|0)>=(f|0)){p=2256;break}L3058:do{if((a[g+(o*40&-1)|0]&1)==0){p=2263}else{m=c[g+(o*40&-1)+36>>2]|0;q=g+(o*40&-1)+32|0;r=0;while(1){if((r|0)>=(m|0)){break}if((a[k+((c[q>>2]|0)+r|0)|0]|0)==(a[r+126728|0]|0)){r=r+1|0}else{p=2263;break L3058}}if((r|0)!=1){p=2263;break}s=n+1|0}}while(0);if((p|0)==2263){p=0;L3067:do{if((a[g+(o*40&-1)|0]&1)==0){t=0}else{q=c[g+(o*40&-1)+36>>2]|0;m=g+(o*40&-1)+32|0;u=0;while(1){if((u|0)>=(q|0)){break}if((a[k+((c[m>>2]|0)+u|0)|0]|0)==(a[u+125616|0]|0)){u=u+1|0}else{t=0;break L3067}}t=((u|0)==1)<<31>>31}}while(0);s=t+n|0}m=o+1|0;if((s|0)==0){w=m;break}else{n=s;o=m}}if((p|0)==2256){w=o+1|0}n=c[g+(o*40&-1)+32>>2]|0;c[13898]=w-1;c[13544]=(c[13544]|0)+1;if((a[37480]&1)!=0){i=b;return}k=n-h|0;n=ut(k)|0;do{if((n|0)==0){gk();j=ut(k)|0;if((j|0)!=0){x=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=110688,v)|0)}else{x=n}}while(0);uD(x|0,(c[10036]|0)+(h+1|0)|0,k);a[x+(k-1|0)|0]=0;dL(x);i=b;return}}while(0);x=c[9368]|0;if((x|0)<1){uf(d,106008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9368]=x-1;if((a[37480]&1)==0){x=c[1054]|0;w=c[x+(d*40&-1)+32>>2]|0;c[x+(d*40&-1)+36>>2]=1;a[(c[10036]|0)+w|0]=59;a[(c[10036]|0)+(w+1|0)|0]=32;a[(c[10036]|0)+(w+2|0)|0]=32;a[(c[10036]|0)+(w+3|0)|0]=32;a[37480]=1;i=b;return}else{c[8272]=0;c[13898]=0;i=b;return}}function d3(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0;b=i;c[13898]=(c[13898]|0)+1;d=iw()|0;e=c[13898]|0;f=c[8272]|0;if((f|0)<=(e|0)){uf(e,104920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=c[1054]|0;if((a[g+(e*40&-1)|0]&1)==0){uf(e,104920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}h=c[g+(e*40&-1)+36>>2]|0;j=g+(e*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(h|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+126728|0]|0)){l=l+1|0}else{m=2323;break}}if((m|0)==2323){uf(e,104920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((l|0)!=1){uf(e,104920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=c[j>>2]|0;j=e+1|0;c[13898]=j;e=1;h=j;while(1){if((h|0)>=(f|0)){m=2295;break}L3114:do{if((a[g+(h*40&-1)|0]&1)==0){m=2302}else{j=c[g+(h*40&-1)+36>>2]|0;n=g+(h*40&-1)+32|0;o=0;while(1){if((o|0)>=(j|0)){break}if((a[k+((c[n>>2]|0)+o|0)|0]|0)==(a[o+126728|0]|0)){o=o+1|0}else{m=2302;break L3114}}if((o|0)!=1){m=2302;break}p=e+1|0}}while(0);if((m|0)==2302){m=0;L3123:do{if((a[g+(h*40&-1)|0]&1)==0){q=0}else{n=c[g+(h*40&-1)+36>>2]|0;j=g+(h*40&-1)+32|0;r=0;while(1){if((r|0)>=(n|0)){break}if((a[k+((c[j>>2]|0)+r|0)|0]|0)==(a[r+125616|0]|0)){r=r+1|0}else{q=0;break L3123}}q=((r|0)==1)<<31>>31}}while(0);p=q+e|0}j=h+1|0;if((p|0)==0){s=j;break}else{e=p;h=j}}if((m|0)==2295){s=h+1|0}m=c[g+(h*40&-1)+32>>2]|0;c[13544]=(c[13544]|0)+1;c[13898]=s-1;s=m-l|0;m=s+2|0;h=ut(m)|0;do{if((h|0)==0){gk();g=ut(m)|0;if((g|0)!=0){t=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=110688,v)|0)}else{t=h}}while(0);uD(t|0,(c[10036]|0)+(l+1|0)|0,s);a[t+(s-1|0)|0]=0;s=(d|0)==0;do{if(!s){if((a[d+38|0]&1)==0){break}l=t;w=59;a[l]=w&255;w=w>>8;a[l+1|0]=w&255}}while(0);l=(t|0)==0;do{if(l){u=0}else{u=bP(t|0)|0}dL(u);}while(ix(d)|0);uu(t);if(s){i=b;return}else{x=d}while(1){d=c[x>>2]|0;uu(c[x+12>>2]|0);uu(x);if((d|0)==0){break}else{x=d}}i=b;return}function d4(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0.0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0.0;b=i;i=i+48|0;d=b|0;e=b+24|0;f=(c[13898]|0)+1|0;c[13898]=f;g=is(e)|0;j=c[g>>2]|0;if((j|0)==1){k=+(c[g+8>>2]|0)}else if((j|0)==2){k=+h[g+8>>3]}else if((j|0)==3){k=+uz(c[g+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=e|0;if((c[g>>2]|0)==3){uu(c[e+8>>2]|0);c[g>>2]=1}g=c[13898]|0;e=c[8272]|0;if((e|0)<=(g|0)){uf(g,104360,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=c[1054]|0;if((a[j+(g*40&-1)|0]&1)==0){uf(g,104360,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=c[j+(g*40&-1)+36>>2]|0;m=j+(g*40&-1)+32|0;n=c[10036]|0;o=0;while(1){if((o|0)>=(l|0)){break}if((a[n+((c[m>>2]|0)+o|0)|0]|0)==(a[o+126728|0]|0)){o=o+1|0}else{p=2374;break}}if((p|0)==2374){uf(g,104360,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((o|0)!=1){uf(g,104360,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=c[m>>2]|0;m=g+1|0;c[13898]=m;g=1;l=m;while(1){if((l|0)>=(e|0)){p=2344;break}L3181:do{if((a[j+(l*40&-1)|0]&1)==0){p=2351}else{m=c[j+(l*40&-1)+36>>2]|0;q=j+(l*40&-1)+32|0;r=0;while(1){if((r|0)>=(m|0)){break}if((a[n+((c[q>>2]|0)+r|0)|0]|0)==(a[r+126728|0]|0)){r=r+1|0}else{p=2351;break L3181}}if((r|0)!=1){p=2351;break}s=g+1|0}}while(0);if((p|0)==2351){p=0;L3190:do{if((a[j+(l*40&-1)|0]&1)==0){t=0}else{q=c[j+(l*40&-1)+36>>2]|0;m=j+(l*40&-1)+32|0;u=0;while(1){if((u|0)>=(q|0)){break}if((a[n+((c[m>>2]|0)+u|0)|0]|0)==(a[u+125616|0]|0)){u=u+1|0}else{t=0;break L3190}}t=((u|0)==1)<<31>>31}}while(0);s=t+g|0}m=l+1|0;if((s|0)==0){w=m;break}else{g=s;l=m}}if((p|0)==2344){w=l+1|0}s=(c[j+(l*40&-1)+32>>2]|0)-o|0;l=ut(s)|0;do{if((l|0)==0){gk();j=ut(s)|0;if((j|0)!=0){x=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=110688,v)|0)}else{x=l}}while(0);uD(x|0,(c[10036]|0)+(o+1|0)|0,s);a[x+(s-1|0)|0]=0;c[13544]=(c[13544]|0)+1;if(k==0.0){uu(x);c[13898]=w;i=b;return}s=d|0;o=d+8|0;while(1){dL(bP(x|0)|0);c[13898]=f;l=is(d)|0;j=c[l>>2]|0;if((j|0)==1){y=+(c[l+8>>2]|0)}else if((j|0)==3){y=+uz(c[l+8>>2]|0,0)}else if((j|0)==2){y=+h[l+8>>3]}else{p=2367;break}if((c[s>>2]|0)==3){uu(c[o>>2]|0);c[s>>2]=1}if(y==0.0){p=2377;break}}if((p|0)==2377){uu(x);c[13898]=w;i=b;return}else if((p|0)==2367){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function d5(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,o=0;b=i;i=i+32|0;d=b|0;e=b+24|0;f=(c[13898]|0)+1|0;c[13898]=f;L3222:do{if((f|0)<(c[8272]|0)){g=c[1054]|0;L3224:do{if((a[g+(f*40&-1)|0]&1)!=0){h=c[g+(f*40&-1)+36>>2]|0;j=g+(f*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(h|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{break L3224}}if((l|0)==1){break L3222}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=f;break}g=c[d+8>>2]|0;c[e>>2]=g;if((g|0)==0){m=c[13898]|0;uf(m,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}iQ(e);g=c[e>>2]|0;if((aY(g|0,202112)|0)==0){o=c[n>>2]|0;hw(o,g,0);i=b;return}else{o=hA(g,193632)|0;hw(o,g,0);i=b;return}}}while(0);c[e>>2]=0;m=f;uf(m,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function d6(){var b=0;b=c[13898]|0;c[13898]=b+1;c[7738]=b;a[30664]=0;hY(1);a[(e6(159824)|0)+8|0]=1;a[(e6(159208)|0)+8|0]=1;a[(e6(158720)|0)+8|0]=1;a[(e6(158096)|0)+8|0]=1;a[(e6(164136)|0)+8|0]=1;a[(e6(156232)|0)+8|0]=1;a[(e6(155120)|0)+8|0]=1;a[(e6(154768)|0)+8|0]=1;iS();return}function d7(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;b=i;i=i+24|0;d=b|0;if((c[7262]|0)==0){c[7262]=c[m>>2]}a[25280]=0;e=d|0;f=d+8|0;g=0;h=c[13898]|0;L3250:while(1){c[13898]=h+1;is(d);do{if((c[e>>2]|0)==3){j=c[f>>2]|0;k=c[7262]|0;aK(j|0,k|0);if((c[e>>2]|0)!=3){l=0;break}uu(c[f>>2]|0);c[e>>2]=1;l=0}else{if((g|0)!=0){k=c[7262]|0;aF(32,k|0)}j5(c[7262]|0,d,0);l=1}}while(0);k=c[13898]|0;if((k|0)>=(c[8272]|0)){break}j=c[1054]|0;n=(a[j+(k*40&-1)|0]&1)==0;if(n){break}o=c[j+(k*40&-1)+36>>2]|0;p=j+(k*40&-1)+32|0;q=c[10036]|0;r=0;while(1){if((r|0)>=(o|0)){s=2413;break}if((a[q+((c[p>>2]|0)+r|0)|0]|0)==(a[r+103664|0]|0)){r=r+1|0}else{s=2414;break}}if((s|0)==2413){s=0;if((r|0)==1|n){break}}else if((s|0)==2414){s=0;if(n){break}}p=c[j+(k*40&-1)+36>>2]|0;q=j+(k*40&-1)+32|0;o=c[10036]|0;t=0;while(1){if((t|0)>=(p|0)){break}if((a[o+((c[q>>2]|0)+t|0)|0]|0)==(a[t+148464|0]|0)){t=t+1|0}else{break L3250}}if((t|0)==1){g=l;h=k}else{break}}aF(10,c[7262]|0);aD(c[7262]|0);i=b;return}function d8(){c[13898]=(c[13898]|0)+1;dO();return}function d9(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,p=0,q=0,r=0,s=0.0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0.0,O=0,P=0;b=i;i=i+48|0;d=b|0;e=b+24|0;f=c[13898]|0;g=f+1|0;c[13898]=g;c[8026]=0;j=c[8272]|0;L3276:do{if((j|0)>(g|0)){k=c[1054]|0;if((a[k+(g*40&-1)|0]&1)==0){l=2487;break}n=c[k+(g*40&-1)+36>>2]|0;p=k+(g*40&-1)+32|0;q=c[10036]|0;r=0;while(1){if((r|0)>=(n|0)){break}if((a[q+((c[p>>2]|0)+r|0)|0]|0)==(a[r+218536|0]|0)){r=r+1|0}else{l=2487;break L3276}}if((r|0)!=5){l=2487;break}p=f+2|0;c[13898]=p;if(!((c[8496]|0)!=0&(c[3524]|0)!=0)){uh(-1,96888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);s=-1.0;break}L3287:do{if((p|0)<(j|0)){n=0;t=p;while(1){u=t;L3290:while(1){w=(a[k+(u*40&-1)|0]&1)==0;x=c[k+(u*40&-1)+36>>2]|0;y=k+(u*40&-1)+32|0;L3292:do{if(w){z=c[y>>2]|0}else{A=0;while(1){if((A|0)>=(x|0)){l=2434;break}if((a[q+((c[y>>2]|0)+A|0)|0]|0)==(a[A+103664|0]|0)){A=A+1|0}else{break}}if((l|0)==2434){l=0;if((A|0)==1){B=n;break L3287}}C=c[y>>2]|0;if((x|0)>0&(w^1)){D=0;E=0;F=C}else{z=C;break}while(1){if((a[D+101312|0]|0)==(a[q+(D+F|0)|0]|0)){G=F;H=E}else{if((D|0)!=3){z=C;break L3292}G=F-1|0;H=1}I=D+1|0;if((I|0)<(H+x|0)){D=I;E=H;F=G}else{break}}if((H|0)!=0){l=2442;break L3290}if((D|0)==2|(D|0)==8){l=2442;break L3290}else{z=C}}}while(0);J=(j|0)>(u|0);if(!J){B=n;break L3287}if(w){l=2451;break}else{K=0}while(1){if((K|0)>=(x|0)){break}if((a[q+(z+K|0)|0]|0)==(a[K+148464|0]|0)){K=K+1|0}else{l=2450;break L3290}}if((K|0)!=1){l=2450;break}x=u+1|0;c[13898]=x;if((x|0)<(j|0)){u=x}else{B=n;break L3287}}if((l|0)==2450){l=0;if(J){l=2451}else{B=n;break L3287}}else if((l|0)==2442){l=0;x=u+1|0;c[13898]=x;L=n|8;M=x}L3318:do{if((l|0)==2451){l=0;L3320:do{if((a[k+(u*40&-1)|0]&1)!=0){x=c[k+(u*40&-1)+36>>2]|0;w=k+(u*40&-1)+32|0;y=0;while(1){if((y|0)>=(x|0)){break}if((a[q+((c[w>>2]|0)+y|0)|0]|0)==(a[y+100784|0]|0)){y=y+1|0}else{break L3320}}if((y|0)!=3){break}w=u+1|0;c[13898]=w;L=n|63;M=w;break L3318}}while(0);if(!J){B=n;break L3287}if((a[k+(u*40&-1)|0]&1)!=0){w=c[k+(u*40&-1)+36>>2]|0;x=k+(u*40&-1)+32|0;C=0;while(1){if((C|0)>=(w|0)){l=2462;break}if((a[q+((c[x>>2]|0)+C|0)|0]|0)==(a[C+100080|0]|0)){C=C+1|0}else{break}}do{if((l|0)==2462){l=0;if((C|0)!=7){break}x=u+1|0;c[13898]=x;L=n|1;M=x;break L3318}}while(0);if(!J){B=n;break L3287}}L3339:do{if((a[k+(u*40&-1)|0]&1)!=0){C=c[k+(u*40&-1)+36>>2]|0;x=k+(u*40&-1)+32|0;w=0;while(1){if((w|0)>=(C|0)){break}if((a[q+((c[x>>2]|0)+w|0)|0]|0)==(a[w+99640|0]|0)){w=w+1|0}else{break L3339}}if((w|0)!=7){break}x=u+1|0;c[13898]=x;L=n|2;M=x;break L3318}}while(0);if(!J){B=n;break L3287}if((a[k+(u*40&-1)|0]&1)!=0){x=c[k+(u*40&-1)+36>>2]|0;C=k+(u*40&-1)+32|0;y=0;while(1){if((y|0)>=(x|0)){l=2476;break}if((a[q+((c[C>>2]|0)+y|0)|0]|0)==(a[y+99208|0]|0)){y=y+1|0}else{break}}do{if((l|0)==2476){l=0;if((y|0)!=7){break}C=u+1|0;c[13898]=C;L=n|4;M=C;break L3318}}while(0);if(!J){B=n;break L3287}}if((a[k+(u*40&-1)|0]&1)==0){B=n;break L3287}y=c[k+(u*40&-1)+36>>2]|0;C=k+(u*40&-1)+32|0;x=0;while(1){if((x|0)>=(y|0)){break}if((a[q+((c[C>>2]|0)+x|0)|0]|0)==(a[x+98600|0]|0)){x=x+1|0}else{B=n;break L3287}}if((x|0)!=5){B=n;break L3287}C=u+1|0;c[13898]=C;L=n|16;M=C}}while(0);if((M|0)<(j|0)){n=L;t=M}else{B=L;break}}}else{B=0}}while(0);c[8026]=(B|0)==0?7:B;q=e6(163432)|0;a[q+8|0]=0;c[q+16>>2]=1;c[q+24>>2]=-1;q=e6(164136)|0;a[q+8|0]=0;c[q+16>>2]=1;c[q+24>>2]=-1;s=-1.0}else{l=2487}}while(0);do{if((l|0)==2487){B=is(d)|0;L=c[B>>2]|0;if((L|0)==1){N=+(c[B+8>>2]|0)}else if((L|0)==2){N=+h[B+8>>3]}else if((L|0)==3){N=+uz(c[B+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}B=d|0;if((c[B>>2]|0)!=3){s=N;break}uu(c[d+8>>2]|0);c[B>>2]=1;s=N}}while(0);d=c[13898]|0;L3376:do{if((d|0)<(c[8272]|0)){B=c[1054]|0;L3378:do{if((a[B+(d*40&-1)|0]&1)!=0){L=c[B+(d*40&-1)+36>>2]|0;M=B+(d*40&-1)+32|0;j=c[10036]|0;J=0;while(1){if((J|0)>=(L|0)){break}if((a[j+((c[M>>2]|0)+J|0)|0]|0)==(a[J+103664|0]|0)){J=J+1|0}else{break L3378}}if((J|0)==1){l=2500;break L3376}}}while(0);uu(c[8028]|0);B=c[13898]|0;L3385:do{if((B|0)<(c[8272]|0)){M=c[1054]|0;L3387:do{if((a[M+(B*40&-1)|0]&1)!=0){j=c[M+(B*40&-1)+36>>2]|0;L=M+(B*40&-1)+32|0;K=c[10036]|0;z=0;while(1){if((z|0)>=(j|0)){break}if((a[K+((c[L>>2]|0)+z|0)|0]|0)==(a[z+103664|0]|0)){z=z+1|0}else{break L3387}}if((z|0)==1){break L3385}}}while(0);a[14176]=1;is(e);a[14176]=0;if((c[e>>2]|0)!=3){c[13898]=B;break}M=c[e+8>>2]|0;c[8028]=M;if((M|0)!=0){J=c[m>>2]|0;aK(M|0,J|0);O=1;break L3376}P=c[13898]|0;uf(P,132744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[8028]=0;P=B;uf(P,132744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{l=2500}}while(0);if((l|0)==2500){uu(c[8028]|0);c[8028]=bP(96376)|0;O=0}L3406:do{if(s<0.0){l=c[3524]|0;do{if((l|0)!=0){P=c[l+116>>2]|0;if((P|0)==0){break}cL[P&7]();break L3406}}while(0);l=c[8028]|0;B=uA(l|0)|0;P=c[o>>2]|0;a6(l|0,B|0,P|0)}}while(0);if(s>0.0){P=~~(s*1.0e6);bH(P|0)}if((O|0)==0|s<0.0){a[25280]=0;i=b;return}aF(10,c[m>>2]|0);a[25280]=0;i=b;return}function ea(b,d){b=b|0;d=d|0;var e=0,f=0;e=i;f=c[7262]|0;do{if(!((f|0)==0|(f|0)==(c[m>>2]|0)|(f|0)==(c[n>>2]|0))){if((a[c[7260]|0]|0)==124){if((a2(f|0)|0)>=0){break}aS(c[7260]|0);break}else{if((az(f|0)|0)>=0){break}aS(c[7260]|0);break}}}while(0);f=c[7260]|0;if((f|0)!=0){uu(f)}c[7260]=0;if((b|0)==0){c[7262]=c[m>>2];i=e;return}if((aY(b|0,202112)|0)==0){c[7262]=c[n>>2];i=e;return}if((a[b]|0)!=124){f=bF(b|0,(d?205056:137896)|0)|0;c[7262]=f;if((f|0)==0){aS(b|0);i=e;return}else{c[7260]=b;i=e;return}}if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=bt(b+1|0,137896)|0;c[7262]=f;if((f|0)==0){aS(b|0);i=e;return}else{c[7260]=b;i=e;return}}function eb(){var a=0,b=0,d=0,e=0;a=i;b=ut(4096)|0;do{if((b|0)==0){gk();d=ut(4096)|0;if((d|0)!=0){e=d;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=88504,v)|0)}else{e=b}}while(0);bl(e|0,4096);cf(c[m>>2]|0,154696,(v=i,i=i+8|0,c[v>>2]=e,v)|0);uu(e);c[13898]=(c[13898]|0)+1;i=a;return}function ec(){var a=0,b=0;a=c[8804]|0;do{if((a|0)!=0){b=c[a>>2]|0;if((b|0)==0){break}cs(b|0)}}while(0);c[13898]=(c[13898]|0)+1;return}function ed(){c[13898]=(c[13898]|0)+1;aI(81576,27,1,c[m>>2]|0);return}function ee(){var b=0;b=c[13898]|0;c[13898]=b+1;c[7738]=b;a[30664]=0;hY(2);a[(e6(159824)|0)+8|0]=1;a[(e6(159208)|0)+8|0]=1;a[(e6(158720)|0)+8|0]=1;a[(e6(158096)|0)+8|0]=1;a[(e6(164136)|0)+8|0]=1;iZ();return}function ef(){k9();return}function eg(){var b=0,d=0,e=0;b=i;if((a[c[6924]|0]|0)==0){uf(c[13898]|0,84384,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[27704]&1)!=0){a[27704]=0;iL()}d=c[3524]|0;e=c[13898]|0;if((d|0)==0){uf(e,135392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=e+1;if((c[d+96>>2]&8|0)==0){dP();i=b;return}cS[c[d+36>>2]&511]();dP();i=b;return}function eh(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0;b=i;i=i+32|0;d=b|0;e=b+24|0;c[e>>2]=0;f=c[13898]|0;g=f+1|0;c[13898]=g;h=c[1054]|0;j=c[h+(g*40&-1)+36>>2]|0;k=c[h+(g*40&-1)+32>>2]|0;l=(a[h+(g*40&-1)|0]&1)==0;m=(j|0)>0;o=c[10036]|0;L3484:do{if(l){p=2648}else{L3486:do{if(m){q=0;r=0;s=k;while(1){if((a[q+98416|0]|0)==(a[o+(q+s|0)|0]|0)){t=s;u=r}else{if((q|0)!=1){break L3486}t=s-1|0;u=1}w=q+1|0;if((w|0)<(u+j|0)){q=w;r=u;s=t}else{break}}if((u|0)==0){x=w;p=2585}else{y=25296;break L3484}}else{x=0;p=2585}}while(0);if((p|0)==2585){s=a[x+98416|0]|0;if((s<<24>>24|0)==36|(s<<24>>24|0)==0){y=25296;break}}if(l){p=2648;break}L3498:do{if(m){s=0;r=0;q=k;while(1){if((a[s+97808|0]|0)==(a[o+(s+q|0)|0]|0)){z=q;A=r}else{if((s|0)!=1){break L3498}z=q-1|0;A=1}B=s+1|0;if((B|0)<(A+j|0)){s=B;r=A;q=z}else{break}}if((A|0)==0){C=B;p=2631}else{y=25304;break L3484}}else{C=0;p=2631}}while(0);if((p|0)==2631){q=a[C+97808|0]|0;if((q<<24>>24|0)==36|(q<<24>>24|0)==0){y=25304;break}}if(l){p=2648;break}L3510:do{if(m){q=0;r=0;s=k;while(1){if((a[q+97200|0]|0)==(a[o+(q+s|0)|0]|0)){D=s;E=r}else{if((q|0)!=1){break L3510}D=s-1|0;E=1}F=q+1|0;if((F|0)<(E+j|0)){q=F;r=E;s=D}else{break}}if((E|0)==0){G=F;p=2639}else{y=25312;break L3484}}else{G=0;p=2639}}while(0);if((p|0)==2639){s=a[G+97200|0]|0;if((s<<24>>24|0)==36|(s<<24>>24|0)==0){y=25312;break}}if(l){p=2648;break}if(m){s=0;r=0;q=k;while(1){if((a[s+96640|0]|0)==(a[o+(s+q|0)|0]|0)){H=q;I=r}else{if((s|0)!=1){p=2648;break L3484}H=q-1|0;I=1}J=s+1|0;if((J|0)<(I+j|0)){s=J;r=I;q=H}else{break}}if((I|0)==0){K=J}else{y=25320;break}}else{K=0}q=a[K+96640|0]|0;if((q<<24>>24|0)==36|(q<<24>>24|0)==0){y=25320}else{p=2648}}}while(0);if((p|0)==2648){y=25328}p=c[y+4>>2]|0;if((p-1|0)>>>0<4){y=f+2|0;c[13898]=y;L=y}else{L=g}L3537:do{if((L|0)<(c[8272]|0)){L3539:do{if((a[h+(L*40&-1)|0]&1)!=0){g=c[h+(L*40&-1)+36>>2]|0;y=h+(L*40&-1)+32|0;f=0;while(1){if((f|0)>=(g|0)){break}if((a[o+((c[y>>2]|0)+f|0)|0]|0)==(a[f+103664|0]|0)){f=f+1|0}else{break L3539}}if((f|0)==1){break L3537}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=L;break}y=c[d+8>>2]|0;c[e>>2]=y;if((y|0)==0){M=c[13898]|0;uf(M,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((a[y]|0)==124){if(a[14168]|0){N=bt(y+1|0,137896)|0;O=y;break}else{uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{iQ(e);g=c[e>>2]|0;if((aY(g|0,202112)|0)==0){N=c[n>>2]|0;O=g;break}else{N=hA(g,137896)|0;O=g;break}}}while(0);if((N|0)==0){uj(c[13898]|0,82848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((p|0)==4){jI(N);i8(N);aI(172e3,9,1,N|0)}else if((p|0)==1){jI(N);y=c[10812]|0;if((y|0)!=0){g=y;do{y=c[g+12>>2]|0;if((y|0)!=0){cf(N|0,154696,(v=i,i=i+8|0,c[v>>2]=y,v)|0)}g=c[g>>2]|0;}while((g|0)!=0)}aI(172e3,9,1,N|0)}else if((p|0)==3){jI(N);jb(N);aI(172e3,9,1,N|0)}else if((p|0)==2){jd(N)}else{jc(N)}if((c[n>>2]|0)==(N|0)){uu(O);i=b;return}if((a[O]|0)==124){a2(N|0);uu(O);i=b;return}else{az(N|0);uu(O);i=b;return}}}while(0);c[e>>2]=0;M=L;uf(M,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function ei(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;b=i;i=i+24|0;d=b|0;e=(c[13898]|0)+1|0;c[13898]=e;if((e|0)>=(c[8272]|0)){f=0;uu(f);i=b;return}g=c[1054]|0;L3594:do{if((a[g+(e*40&-1)|0]&1)!=0){h=c[g+(e*40&-1)+36>>2]|0;j=g+(e*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(h|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{break L3594}}if((l|0)==1){f=0}else{break}uu(f);i=b;return}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=e;f=0;uu(f);i=b;return}e=c[d+8>>2]|0;if((e|0)==0){f=0;uu(f);i=b;return}if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}bN(e|0);f=e;uu(f);i=b;return}function ej(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0.0,Y=0.0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0.0;b=i;i=i+8312|0;d=b|0;e=b+2048|0;f=b+8192|0;g=b+8240|0;j=b+8248|0;k=b+8256|0;l=b+8304|0;n=c[13898]|0;o=n+1|0;c[13898]=o;p=c[3524]|0;if((p|0)==0){uf(o,135392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L3617:do{if((o|0)<(c[8272]|0)){q=c[1054]|0;r=(a[q+(o*40&-1)|0]&1)==0;L3619:do{if(r){s=c[10036]|0;t=2765}else{u=c[q+(o*40&-1)+36>>2]|0;w=q+(o*40&-1)+32|0;x=c[10036]|0;y=0;while(1){if((y|0)>=(u|0)){t=2680;break}if((a[x+((c[w>>2]|0)+y|0)|0]|0)==(a[y+103664|0]|0)){y=y+1|0}else{break}}if((t|0)==2680){if((y|0)==1){break L3617}}z=c[w>>2]|0;A=(u|0)>0;if(r){s=x;t=2765;break}L3629:do{if(A){B=0;C=0;D=z;while(1){if((a[B+142016|0]|0)==(a[x+(B+D|0)|0]|0)){E=D;F=C}else{if((B|0)!=4){break L3629}E=D-1|0;F=1}G=B+1|0;if((G|0)<(F+u|0)){B=G;C=F;D=E}else{break}}if((F|0)==0){H=G;t=2689}else{I=5696;J=x;break L3619}}else{H=0;t=2689}}while(0);if((t|0)==2689){w=a[H+142016|0]|0;if((w<<24>>24|0)==36|(w<<24>>24|0)==0){I=5696;J=x;break}}if(r){s=x;t=2765;break}L3641:do{if(A){w=0;y=0;D=z;while(1){if((a[w+205488|0]|0)==(a[x+(w+D|0)|0]|0)){K=D;L=y}else{if((w|0)!=3){break L3641}K=D-1|0;L=1}M=w+1|0;if((M|0)<(L+u|0)){w=M;y=L;D=K}else{break}}if((L|0)==0){N=M;t=2759}else{I=5704;J=x;break L3619}}else{N=0;t=2759}}while(0);if((t|0)==2759){D=a[N+205488|0]|0;if((D<<24>>24|0)==36|(D<<24>>24|0)==0){I=5704;J=x;break}}if(r){s=x;t=2765;break}if(A){D=0;while(1){if((a[D+141856|0]|0)!=(a[x+(D+z|0)|0]|0)){s=x;t=2765;break L3619}y=D+1|0;if((y|0)<(u|0)){D=y}else{P=y;break}}}else{P=0}D=a[P+141856|0]|0;if((D<<24>>24|0)==36|(D<<24>>24|0)==0){I=5712;J=x}else{s=x;t=2765}}}while(0);if((t|0)==2765){I=5720;J=s}r=c[I+4>>2]|0;if((r|0)==2){if((c[p+136>>2]|0)==0){Q=0}else{Q=(c[p+144>>2]|0)!=0}D=a$()|0;u=c[13898]|0;z=u+1|0;c[13898]=z;L3666:do{if((z|0)<(c[8272]|0)){A=c[1054]|0;y=c[A+(z*40&-1)+36>>2]|0;L3668:do{if((a[A+(z*40&-1)|0]&1)==0){R=c[10036]|0;S=A+(z*40&-1)+32|0}else{w=A+(z*40&-1)+32|0;C=c[10036]|0;B=0;while(1){if((B|0)>=(y|0)){break}if((a[C+((c[w>>2]|0)+B|0)|0]|0)==(a[B+103664|0]|0)){B=B+1|0}else{R=C;S=w;break L3668}}if((B|0)==1){T=93664;break L3666}else{R=C;S=w}}}while(0);A=(y|0)!=3;x=A&1;U=R+(c[S>>2]|0)|0;if(A){V=x}else{A=((aZ(U|0,114,3)|0)==0&1)+x|0;x=A+((aZ(U|0,103,3)|0)==0&1)|0;V=x+((aZ(U|0,98,3)|0)==0&1)|0}if((V|0)==0){c[13898]=u+2;T=U;break}else{uf(z,215792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{T=93664}}while(0);if((D|0)==0){uf(-1,215320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{W=0}do{X=+(W|0)/255.0;h[d+(W<<3)>>3]=X;if((a[20668]|0)==110){Y=1.0-X}else{Y=X}fq(Y,e+(W*24&-1)|0);W=W+1|0;}while((W|0)<256);c[11254]=0;z=c[6924]|0;if((z|0)==0){Z=0}else{Z=bP(z|0)|0}z=a[37384]&1;aI(5992,208,1,D|0);if(Q){aI(5976,13,1,D|0)}aI(5728,243,1,D|0);aF(112,D|0);if((a[T]|0)!=0){u=0;do{if((u|0)>0){aF(44,D|0)}aI(214288,7,1,D|0);U=a[T+u|0]|0;if((U|0)==114){aI(213544,25,1,D|0)}else if((U|0)==103){aI(213024,29,1,D|0)}else if((U|0)==98){aI(212416,27,1,D|0)}u=u+1|0;}while(u>>>0<(uA(T|0)|0)>>>0)}aI(211904,28,1,D|0);aF(10,D|0);u=a[T]|0;U=0;do{if((u<<24>>24|0)==103){_=e+(U*24&-1)+8|0}else if((u<<24>>24|0)==114){_=e+(U*24&-1)|0}else{_=e+(U*24&-1)+16|0}X=+h[_>>3];cf(D|0,210888,(v=i,i=i+16|0,h[v>>3]=+h[d+(U<<3)>>3],h[v+8>>3]=X,v)|0);U=U+1|0;}while((U|0)<256);aI(210424,2,1,D|0);U=a[T+1|0]|0;u=0;do{if((U<<24>>24|0)==103){$=e+(u*24&-1)+8|0}else if((U<<24>>24|0)==114){$=e+(u*24&-1)|0}else{$=e+(u*24&-1)+16|0}X=+h[$>>3];cf(D|0,210888,(v=i,i=i+16|0,h[v>>3]=+h[d+(u<<3)>>3],h[v+8>>3]=X,v)|0);u=u+1|0;}while((u|0)<256);aI(210424,2,1,D|0);u=a[T+2|0]|0;U=0;do{if((u<<24>>24|0)==103){aa=e+(U*24&-1)+8|0}else if((u<<24>>24|0)==114){aa=e+(U*24&-1)|0}else{aa=e+(U*24&-1)+16|0}X=+h[aa>>3];cf(D|0,210888,(v=i,i=i+16|0,h[v>>3]=+h[d+(U<<3)>>3],h[v+8>>3]=X,v)|0);U=U+1|0;}while((U|0)<256);aI(210424,2,1,D|0);U=0;do{X=+h[e+(U*24&-1)>>3]*.299+ +h[e+(U*24&-1)+8>>3]*.587+ +h[e+(U*24&-1)+16>>3]*.114;cf(D|0,210888,(v=i,i=i+16|0,h[v>>3]=+h[d+(U<<3)>>3],h[v+8>>3]=X,v)|0);U=U+1|0;}while((U|0)<256);aI(210424,2,1,D|0);aI(6208,14,1,D|0);jI(D);jb(D);aI(172e3,9,1,D|0);cs(D|0);hw(D,0,0);c[11254]=1;uu(c[6924]|0);c[6924]=Z;a[37384]=z;c[8272]=0;c[13898]=0;i=b;return}else if((r|0)==3){U=k;c[g>>2]=0;c[j>>2]=0;u=n+2|0;c[13898]=u;if((a[q+(u*40&-1)|0]&1)==0){i=b;return}x=a[J+(c[q+(u*40&-1)+32>>2]|0)|0]|0;if(!((x<<24>>24|0)==39|(x<<24>>24|0)==34)){i=b;return}ub(g,u,u);u=(c[13898]|0)+1|0;c[13898]=u;x=c[1054]|0;do{if((a[x+(u*40&-1)|0]&1)!=0){A=a[(c[10036]|0)+(c[x+(u*40&-1)+32>>2]|0)|0]|0;if(!((A<<24>>24|0)==39|(A<<24>>24|0)==34)){break}ub(j,u,u);uE(U|0,0,44);A=c[j>>2]|0;ab=c[g>>2]|0;t1(A,ab,k,l);X=+t2(k);ac=c[m>>2]|0;ad=k+12|0;ae=c[ad>>2]|0;af=k+16|0;ag=(c[af>>2]|0)+1|0;ah=k+20|0;ai=(c[ah>>2]|0)%100&-1;aj=k+8|0;ak=c[aj>>2]|0;al=k+4|0;am=c[al>>2]|0;an=k|0;ao=c[an>>2]|0;ap=k+24|0;aq=c[ap>>2]|0;ar=k+28|0;as=c[ar>>2]|0;cf(ac|0,217600,(v=i,i=i+72|0,h[v>>3]=X,c[v+8>>2]=ae,c[v+16>>2]=ag,c[v+24>>2]=ai,c[v+32>>2]=ak,c[v+40>>2]=am,c[v+48>>2]=ao,c[v+56>>2]=aq,c[v+64>>2]=as,v)|0);uE(U|0,0,44);t3(k,X);as=uA(A|0)|0;t3(f,X);at=X- +O(+X);t4(A,as,ab,f,at);ab=c[m>>2]|0;as=c[ad>>2]|0;ad=(c[af>>2]|0)+1|0;af=(c[ah>>2]|0)%100&-1;ah=c[aj>>2]|0;aj=c[al>>2]|0;al=c[an>>2]|0;an=c[ap>>2]|0;ap=c[ar>>2]|0;cf(ab|0,216816,(v=i,i=i+72|0,c[v>>2]=A,c[v+8>>2]=as,c[v+16>>2]=ad,c[v+24>>2]=af,c[v+32>>2]=ah,c[v+40>>2]=aj,c[v+48>>2]=al,c[v+56>>2]=an,c[v+64>>2]=ap,v)|0);uu(A);c[13898]=(c[13898]|0)+1}}while(0);uu(c[g>>2]|0);i=b;return}else if((r|0)==1){lG();i=b;return}else{uf(o,80152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);lG();i=b;return}function ek(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;b=i;i=i+48|0;d=b|0;e=b+24|0;f=(c[13898]|0)+1|0;c[13898]=f;if((f|0)>=(c[8272]|0)){g=f;uf(g,79192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}h=c[1054]|0;L3758:do{if((a[h+(f*40&-1)|0]&1)!=0){j=c[h+(f*40&-1)+36>>2]|0;k=h+(f*40&-1)+32|0;l=c[10036]|0;m=0;while(1){if((m|0)>=(j|0)){break}if((a[l+((c[k>>2]|0)+m|0)|0]|0)==(a[m+103664|0]|0)){m=m+1|0}else{break L3758}}if((m|0)==1){g=f}else{break}uf(g,79192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);a[14176]=1;is(e);a[14176]=0;if((c[e>>2]|0)!=3){c[13898]=f;g=f;uf(g,79192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[e+8>>2]|0;e=c[13898]|0;if((f|0)==0){g=e;uf(g,79192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((e|0)>=(c[8272]|0)){n=0;fc(f,n);uu(n);uu(f);i=b;return}g=c[1054]|0;h=(a[g+(e*40&-1)|0]&1)==0;L3776:do{if(h){o=d}else{k=c[g+(e*40&-1)+36>>2]|0;l=g+(e*40&-1)+32|0;j=c[10036]|0;p=0;while(1){if((p|0)>=(k|0)){q=2788;break}if((a[j+((c[l>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{break}}do{if((q|0)==2788){if((p|0)==1){n=0}else{break}fc(f,n);uu(n);uu(f);i=b;return}}while(0);p=d;if(h){o=p;break}l=c[g+(e*40&-1)+36>>2]|0;j=g+(e*40&-1)+32|0;k=c[10036]|0;m=0;while(1){if((m|0)>=(l|0)){break}if((a[k+((c[j>>2]|0)+m|0)|0]|0)==(a[m+103664|0]|0)){m=m+1|0}else{o=p;break L3776}}if((m|0)==1){r=e}else{o=p;break}uf(r,78672,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=e;r=e;uf(r,78672,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=c[d+8>>2]|0;if((e|0)!=0){n=e;fc(f,n);uu(n);uu(f);i=b;return}r=c[13898]|0;uf(r,78672,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function el(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;b=c[13898]|0;d=c[8272]|0;L3802:do{if((b|0)<(d|0)){e=c[1054]|0;f=b;do{L3806:do{if((a[e+(f*40&-1)|0]&1)!=0){g=c[e+(f*40&-1)+36>>2]|0;h=e+(f*40&-1)+32|0;j=c[10036]|0;k=0;while(1){if((k|0)>=(g|0)){break}if((a[j+((c[h>>2]|0)+k|0)|0]|0)==(a[k+103664|0]|0)){k=k+1|0}else{break L3806}}if((k|0)==1){break L3802}}}while(0);f=f+1|0;c[13898]=f;}while((f|0)<(d|0))}}while(0);uf(b,78216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function em(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0;b=i;i=i+8|0;d=b|0;e=bU(140128)|0;f=(e|0)==0?138024:e;if((c[10010]|0)==0){e=ut(1024)|0;do{if((e|0)==0){gk();g=ut(1024)|0;if((g|0)!=0){h=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74912,v)|0)}else{h=e}}while(0);c[10010]=h;h=ut(1024)|0;do{if((h|0)==0){gk();e=ut(1024)|0;if((e|0)!=0){j=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74440,v)|0)}else{j=h}}while(0);c[10008]=j;a[j]=0;a[c[10010]|0]=0}if(!(a[40024]|0)){a[c[10008]|0]=0;a[c[10010]|0]=0}a[40024]=0;j=c[10010]|0;h=uA(j|0)|0;e=(c[13898]|0)+1|0;c[13898]=e;g=c[8272]|0;L3829:do{if((e|0)<(g|0)){k=c[1054]|0;l=e;while(1){L3833:do{if((a[k+(l*40&-1)|0]&1)!=0){m=c[k+(l*40&-1)+36>>2]|0;n=k+(l*40&-1)+32|0;o=c[10036]|0;p=0;while(1){if((p|0)>=(m|0)){break}if((a[o+((c[n>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{break L3833}}if((p|0)==1){q=l;break L3829}}}while(0);n=l+1|0;c[13898]=n;if((n|0)<(g|0)){l=n}else{q=n;break}}}else{q=e}}while(0);if((h|0)>0){a[j+h|0]=32;r=h+1|0;s=c[10010]|0;t=c[13898]|0}else{r=h;s=j;t=q}q=s+r|0;s=t-1|0;t=1024-r|0;r=c[1054]|0;j=(c[r+(s*40&-1)+36>>2]|0)+(c[r+(s*40&-1)+32>>2]|0)|0;s=c[r+(e*40&-1)+32>>2]|0;if((j-s|0)<(t|0)){u=j}else{u=(t-1|0)+s|0}L3847:do{if((s|0)<(u|0)){t=q;j=s;while(1){e=a[(c[10036]|0)+j|0]|0;if(e<<24>>24==0){w=t;break L3847}r=t+1|0;a[t]=e;e=j+1|0;if((e|0)<(u|0)){t=r;j=e}else{w=r;break}}}else{w=q}}while(0);a[w]=0;w=(c[10010]|0)+h|0;q=a[w]|0;if(q<<24>>24==0){x=w}else{u=w;s=w;w=0;j=q;while(1){do{if((aM(j&255|0)|0)==0){a[s]=a[u]|0;y=0;z=s+1|0}else{if(w){y=1;z=s;break}a[s]=32;y=1;z=s+1|0}}while(0);q=u+1|0;t=a[q]|0;if(t<<24>>24==0){x=z;break}else{u=q;s=z;w=y;j=t}}}a[x]=0;x=c[10010]|0;j=uA(x|0)|0;if((aY(x+((h|0)==0?0:h+1|0)|0,216168)|0)==0){a[d]=1;a[x+h|0]=0;A=1;B=c[10010]|0}else{a[d]=0;A=0;B=x}x=gm(B,f,d)|0;if((x|0)==1){B=c[10010]|0;bD(224888,(v=i,i=i+8|0,c[v>>2]=B,v)|0);C=c[10010]|0;D=C+h|0;a[D]=0;i=b;return}else if((x|0)==0){a[25280]=0;if((a[d]&1)==0|A){C=c[10010]|0;D=C+h|0;a[D]=0;i=b;return}A=(j|0)>0;L3873:while(1){j=c[10008]|0;if(A){uD(j|0,73672,13);B=c[10010]|0;ck(j|0,B|0,1008);B=c[10008]|0;y=B+(uA(B|0)|0)|0;a[y]=a[212304]|0;a[y+1|0]=a[212305|0]|0;a[y+2|0]=a[212306|0]|0;E=c[10008]|0}else{uD(j|0,225272,13);E=j}dF(E,0);j=jl(40144,40136)|0;c[8272]=j;c[13898]=0;if((j|0)<1){F=2870;break}j=c[1054]|0;L3880:do{if((a[j|0]&1)!=0){y=c[j+36>>2]|0;B=j+32|0;w=c[10036]|0;z=0;while(1){if((z|0)>=(y|0)){break}if((a[w+((c[B>>2]|0)+z|0)|0]|0)==(a[z+103664|0]|0)){z=z+1|0}else{break L3880}}if((z|0)==1){F=2871;break L3873}}}while(0);c[13898]=-1;a[40024]=1;em();if((a[d]&1)==0){F=2872;break}}if((F|0)==2870){C=c[10010]|0;D=C+h|0;a[D]=0;i=b;return}else if((F|0)==2871){C=c[10010]|0;D=C+h|0;a[D]=0;i=b;return}else if((F|0)==2872){C=c[10010]|0;D=C+h|0;a[D]=0;i=b;return}}else if((x|0)==(-1|0)){aS(f|0);C=c[10010]|0;D=C+h|0;a[D]=0;i=b;return}else{uf(-1,223936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function en(){var b=0,d=0,e=0,f=0,g=0;b=i;a[25280]=0;c[13898]=(c[13898]|0)+1;d=c[256]|0;if((d|0)==0){e=c[m>>2]|0;f=aF(10,e|0)|0;i=b;return}g=uF(46781,d|0,93)|0;if((uA(d|0)|0)>>>0>=93){a[46873]=0}if((bN(g|0)|0)==0){e=c[m>>2]|0;f=aF(10,e|0)|0;i=b;return}else{uj(-1,223328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function eo(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;e=i;if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}f=bt(b|0,193632|0)|0;if((f|0)==0){uj(-1,221448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}b=ut(1024)|0;do{if((b|0)==0){gk();g=ut(1024)|0;if((g|0)!=0){h=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=220968,v)|0);return 0}else{h=b}}while(0);a[h]=0;b=h;h=0;g=1024;L3914:while(1){j=h;while(1){k=a1(f|0)|0;if((k|0)==-1){l=2898;break L3914}m=j+1|0;n=b+j|0;a[n]=k&255;if((m|0)==(g|0)){break}else{j=m}}if((g|0)>1048575){l=2896;break}m=g+1024|0;b=db(b,m,218568)|0;h=g;g=m}if((l|0)==2898){o=b+j|0;a[o]=0;p=a2(f|0)|0;q=uA(b|0)|0;r=q+1|0;s=db(b,r,220968)|0;c[d>>2]=s;i=e;return p|0}else if((l|0)==2896){uh(-1,219104,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);o=n;a[o]=0;p=a2(f|0)|0;q=uA(b|0)|0;r=q+1|0;s=db(b,r,220968)|0;c[d>>2]=s;i=e;return p|0}return 0}function ep(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0,z=0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0,N=0.0,S=0.0,T=0,U=0.0,V=0,W=0.0,X=0.0,Y=0.0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0.0,bf=0.0,bg=0.0,bh=0,bi=0.0,bj=0.0,bk=0.0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0,bA=0.0,bB=0.0,bC=0.0,bD=0,bE=0,bF=0.0,bG=0,bH=0,bI=0,bJ=0.0,bK=0.0,bL=0,bM=0,bN=0,bO=0,bP=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0.0,b3=0,b4=0,b5=0,b6=0,b7=0,b8=0.0,b9=0,ca=0,cb=0.0,cc=0,cd=0,ce=0,cf=0,cg=0,ch=0,ci=0,cj=0,ck=0.0;e=i;i=i+24|0;f=e|0;g=e+8|0;j=e+16|0;k=c[13374]|0;c[9348]=c[13378];c[13370]=0;l=d+8|0;n=c[l>>2]|0;h[15]=8.988465674311579e+307;h[70]=8.988465674311579e+307;h[98]=8.988465674311579e+307;h[16]=-8.988465674311579e+307;h[71]=-8.988465674311579e+307;h[99]=-8.988465674311579e+307;if((b|0)>0){o=(n|0)>0;p=0;q=d;r=-8.988465674311579e+307;s=-8.988465674311579e+307;t=-8.988465674311579e+307;u=8.988465674311579e+307;w=8.988465674311579e+307;x=8.988465674311579e+307;while(1){y=c[q+12>>2]|0;if(o){z=0;A=r;B=s;C=t;D=u;E=w;F=x;while(1){do{if((c[y+(z<<6)>>2]|0)==2){G=A;H=B;I=C;J=D;K=E;L=F}else{M=y+(z<<6)+8|0;N=+h[M>>3];if(N>A){h[99]=N;S=N}else{S=A}T=y+(z<<6)+16|0;N=+h[T>>3];if(N>B){h[71]=N;U=N}else{U=B}V=y+(z<<6)+24|0;N=+h[V>>3];if(N>C){h[16]=N;W=N}else{W=C}N=+h[M>>3];if(N<D){h[98]=N;X=N}else{X=D}N=+h[T>>3];if(N<E){h[70]=N;Y=N}else{Y=E}N=+h[V>>3];if(N>=F){G=S;H=U;I=W;J=X;K=Y;L=F;break}h[15]=N;G=S;H=U;I=W;J=X;K=Y;L=N}}while(0);V=z+1|0;if((V|0)<(n|0)){z=V;A=G;B=H;C=I;D=J;E=K;F=L}else{ab=G;ac=H;ad=I;ae=J;af=K;ag=L;break}}}else{ab=r;ac=s;ad=t;ae=u;af=w;ag=x}z=p+1|0;if((z|0)<(b|0)){p=z;q=c[q>>2]|0;r=ab;s=ac;t=ad;u=ae;w=af;x=ag}else{break}}}q=c[d+12>>2]|0;p=(c[l>>2]|0)-1|0;l=(p|0)>0;L3955:do{if(l){n=0;o=0;z=0;L3956:while(1){y=q+(z<<6)|0;V=z+1|0;T=q+(V<<6)|0;do{if((c[y>>2]|0)==0){if((c[T>>2]|0)!=0){ah=o;ai=n;break}M=ut(28)|0;if((M|0)==0){gk();aj=ut(28)|0;if((aj|0)==0){break L3956}else{ak=aj}}else{ak=M}M=ak;c[ak>>2]=0;c[ak+4>>2]=0;c[ak+8>>2]=y;c[ak+12>>2]=T;c[ak+16>>2]=0;c[ak+24>>2]=1;if((o|0)==0){ah=M;ai=M;break}c[o+16>>2]=M;ah=M;ai=n}else{ah=o;ai=n}}while(0);if((V|0)<(p|0)){n=ai;o=ah;z=V}else{al=ai;am=ah;break L3955}}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=212672,v)|0);return 0}else{al=0;am=0}}while(0);L3968:do{if((b|0)>1){ah=al;ai=am;ak=0;z=2;o=q;n=d;T=al;y=0;L3969:while(1){M=c[n>>2]|0;aj=c[M+12>>2]|0;do{if((c[o>>2]|0)==0){if((c[aj>>2]|0)!=0){an=0;ao=ai;ap=T;break}aq=ut(28)|0;if((aq|0)==0){gk();ar=ut(28)|0;if((ar|0)==0){as=2935;break L3969}else{at=ar}}else{at=aq}aq=at;c[at>>2]=0;c[at+4>>2]=0;c[at+8>>2]=o;c[at+12>>2]=aj;c[at+16>>2]=0;c[at+24>>2]=1;if((ai|0)==0){an=aq;ao=aq;ap=aq;break}c[ai+16>>2]=aq;an=aq;ao=aq;ap=T}else{an=0;ao=ai;ap=T}}while(0);do{if(l){V=0;aq=ao;ar=0;au=ak;av=an;aw=ah;ax=0;ay=ap;az=y;while(1){do{if((aw|0)==0){aA=0;aB=0}else{if((c[aw+8>>2]|0)!=(o+(ax<<6)|0)){aA=aw;aB=0;break}aA=c[aw+16>>2]|0;aB=aw}}while(0);aC=ax+1|0;aD=o+(aC<<6)|0;aE=aj+(ax<<6)|0;aF=aD|0;aG=aE|0;do{if((c[aF>>2]|0)==0){if((c[aG>>2]|0)!=0){aH=au;aJ=0;aK=1;aL=aq;aM=ay;aN=az;break}aO=ut(28)|0;if((aO|0)==0){gk();aP=ut(28)|0;if((aP|0)==0){as=2946;break L3969}else{aQ=aP}}else{aQ=aO}aO=aQ;aP=aQ;c[aP>>2]=0;aR=aQ+4|0;c[aR>>2]=0;c[aQ+8>>2]=aD;c[aQ+12>>2]=aE;c[aQ+16>>2]=0;aS=aQ+24|0;c[aS>>2]=1;if((aq|0)==0){aT=aO}else{c[aq+16>>2]=aO;aT=ay}c[aS>>2]=3;if((av|0)==0|(aB|0)==0){aH=au;aJ=aO;aK=0;aL=aO;aM=aT;aN=az;break}aS=ut(16)|0;if((aS|0)==0){gk();aU=ut(16)|0;if((aU|0)==0){as=2952;break L3969}else{aV=aU}}else{aV=aS}aS=aV;c[aV>>2]=av;c[aV+4>>2]=aO;c[aV+8>>2]=aB;c[aV+12>>2]=0;aU=av|0;if((c[aU>>2]|0)==0){c[aU>>2]=aS}else{c[av+4>>2]=aS}if((c[aP>>2]|0)==0){c[aP>>2]=aS}else{c[aR>>2]=aS}aR=aB|0;if((c[aR>>2]|0)==0){c[aR>>2]=aS}else{c[aB+4>>2]=aS}if((au|0)==0){aH=aS;aJ=aO;aK=0;aL=aO;aM=aT;aN=aS;break}c[au+12>>2]=aS;aH=aS;aJ=aO;aK=0;aL=aO;aM=aT;aN=az}else{aH=au;aJ=0;aK=1;aL=aq;aM=ay;aN=az}}while(0);aO=aj+(aC<<6)|0;do{if((c[aG>>2]|0)==0){if((c[aO>>2]|0)!=0){aW=0;aX=ar;aY=V;break}aS=ut(28)|0;if((aS|0)==0){gk();aR=ut(28)|0;if((aR|0)==0){as=2968;break L3969}else{aZ=aR}}else{aZ=aS}aS=aZ;c[aZ>>2]=0;c[aZ+4>>2]=0;c[aZ+8>>2]=aE;c[aZ+12>>2]=aO;c[aZ+16>>2]=0;c[aZ+24>>2]=1;if((ar|0)==0){aW=aS;aX=aS;aY=aS;break}c[ar+16>>2]=aS;aW=aS;aX=aS;aY=V}else{aW=0;aX=ar;aY=V}}while(0);do{if((c[aF>>2]|0)==0){if((c[aO>>2]|0)!=0){a_=aH;a$=aL;a0=0;a1=aM;a2=aN;break}aE=ut(28)|0;if((aE|0)==0){gk();aG=ut(28)|0;if((aG|0)==0){as=2975;break L3969}else{a3=aG}}else{a3=aE}aE=a3;aG=a3;c[aG>>2]=0;aS=a3+4|0;c[aS>>2]=0;c[a3+8>>2]=aD;c[a3+12>>2]=aO;c[a3+16>>2]=0;c[a3+24>>2]=1;if((aL|0)==0){a4=aE}else{c[aL+16>>2]=aE;a4=aM}if(aK|(aW|0)==0){a_=aH;a$=aE;a0=aE;a1=a4;a2=aN;break}aR=ut(16)|0;if((aR|0)==0){gk();aP=ut(16)|0;if((aP|0)==0){as=2981;break L3969}else{a5=aP}}else{a5=aR}aR=a5;c[a5>>2]=aJ;c[a5+4>>2]=aW;c[a5+8>>2]=aE;c[a5+12>>2]=0;aP=aJ|0;if((c[aP>>2]|0)==0){c[aP>>2]=aR}else{c[aJ+4>>2]=aR}aP=aW|0;if((c[aP>>2]|0)==0){c[aP>>2]=aR}else{c[aW+4>>2]=aR}if((c[aG>>2]|0)==0){c[aG>>2]=aR}else{c[aS>>2]=aR}if((aH|0)==0){a_=aR;a$=aE;a0=aE;a1=a4;a2=aR;break}c[aH+12>>2]=aR;a_=aR;a$=aE;a0=aE;a1=a4;a2=aN}else{a_=aH;a$=aL;a0=0;a1=aM;a2=aN}}while(0);if((aC|0)<(p|0)){V=aY;aq=a$;ar=aX;au=a_;av=a0;aw=aA;ax=aC;ay=a1;az=a2}else{break}}if((aY|0)==0){a6=a$;a7=a_;a8=0;a9=a1;ba=a2;break}if((a1|0)==0){a6=aX;a7=a_;a8=aY;a9=aY;ba=a2;break}c[a$+16>>2]=aY;a6=aX;a7=a_;a8=aY;a9=a1;ba=a2}else{a6=ao;a7=ak;a8=0;a9=ap;ba=y}}while(0);if((z|0)>=(b|0)){bb=a9;bc=ba;break L3968}ah=a8;ai=a6;ak=a7;z=z+1|0;o=aj;n=M;T=a9;y=ba}if((as|0)==2968){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=212672,v)|0);return 0}else if((as|0)==2935){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=212672,v)|0);return 0}else if((as|0)==2952){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=170896,v)|0);return 0}else if((as|0)==2975){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=212672,v)|0);return 0}else if((as|0)==2981){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=170896,v)|0);return 0}else if((as|0)==2946){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=212672,v)|0);return 0}}else{bb=al;bc=0}}while(0);al=(bb|0)==0;if(!al){ba=bb;do{if((c[ba>>2]|0)==0){as=3001}else{if((c[ba+4>>2]|0)==0){as=3001}}if((as|0)==3001){as=0;c[ba+24>>2]=2}ba=c[ba+16>>2]|0;}while((ba|0)!=0)}c[12922]=0;if((c[13372]|0)==0){ag=+h[16]- +h[15];if(ag==0.0){bd=0;i=e;return bd|0}x=+P(+ag);ba=(c[13374]<<1)+2|0;ag=+R(10.0,+(+O(+(+bQ(+x)))));af=x/ag;x=+(ba|0)/af;do{if(x>40.0){be=.05}else{if(x>20.0){be=.1;break}if(x>10.0){be=.2;break}if(x>4.0){be=.5;break}if(x>2.0){be=1.0;break}if(x>.5){be=2.0;break}be=+$(+af)}}while(0);af=ag*be;be=af*+O(+(+h[15]/af));bf=af;bg=be;bh=~~+O(+((+h[16]-be)/af))}else{bf=0.0;bg=0.0;bh=k}L4084:do{if((bh|0)>0){af=bf*.01;k=0;be=bg;L4086:while(1){ba=c[13372]|0;do{if((ba|0)==2){ag=+h[(c[11662]|0)+(k<<3)>>3];if((a[64788]&1)==0){bi=ag;break}x=+_(+ag);bi=x/+h[8100]}else if((ba|0)==0){x=bf+be;bi=+P(+x)<af?0.0:x}else if((ba|0)==1){a9=c[11662]|0;x=+h[a9>>3];if((a[64788]&1)==0){bj=+h[a9+8>>3];bk=x}else{ag=+_(+x);x=+h[8100];bj=+_(+(+h[a9+8>>3]))/x;bk=ag/x}bi=bk+ +(k|0)*bj}else{bi=be}}while(0);h[6688]=bi;ba=c[13370]|0;x=+h[98];ag=+h[99];w=+h[70];ae=+h[71];do{if(al){c[j>>2]=0}else{M=bb;aj=0;while(1){a9=M+20|0;if(+h[(c[M+8>>2]|0)+24>>3]>=bi^+h[(c[M+12>>2]|0)+24>>3]>=bi){a[a9]=1;bl=aj+1|0}else{a[a9]=0;bl=aj}a9=c[M+16>>2]|0;if((a9|0)==0){break}else{M=a9;aj=bl}}c[j>>2]=bl;if((bl|0)<=0){break}u=ag-x;ad=ae-w;aj=0;while(1){L4110:do{if((aj&1)==0){M=bb;while(1){if((a[M+20|0]&1)!=0){if((c[M+24>>2]|0)==2){break}}a9=c[M+16>>2]|0;if((a9|0)==0){bm=1;as=3037;break L4110}else{M=a9}}bn=eu(M,bi,j,0)|0;bo=aj;as=3043}else{bm=aj;as=3037}}while(0);do{if((as|0)==3037){as=0;aC=bb;while(1){if((a[aC+20|0]&1)!=0){if((c[aC+24>>2]|0)!=2){as=3042;break}}a9=c[aC+16>>2]|0;if((a9|0)==0){as=3041;break}else{aC=a9}}if((as|0)==3041){as=0;c[j>>2]=0;M=c[m>>2]|0;aI(76408,34,1,M|0);bp=bm;break}else if((as|0)==3042){as=0;bn=eu(aC,bi,j,1)|0;bo=1;as=3043;break}}}while(0);do{if((as|0)==3043){as=0;M=(bo&1)!=0;if((bn|0)==0){bp=bo;break}a9=c[9348]|0;L4130:do{if((a9|0)==1){do{if(M){bq=1}else{a7=bn;while(1){a6=c[a7+16>>2]|0;if((a6|0)==0){break}else{a7=a6}}t=+P(+(+h[99]- +h[98]));if(+P(+(+h[a7>>3]- +h[bn>>3]))>=t*1.0e-5){bq=0;break}t=+P(+(+h[71]- +h[70]));bq=+P(+(+h[a7+8>>3]- +h[bn+8>>3]))<t*1.0e-5}}while(0);a6=bn;a8=0;while(1){br=a8+1|0;b=c[a6+16>>2]|0;if((b|0)==0){bs=bn;break}else{a6=b;a8=br}}while(1){bt=bs+16|0;a6=c[bt>>2]|0;if((a6|0)==0){break}else{bs=a6}}do{if(bq){t=+P(+(+h[99]- +h[98]));if(+P(+(+h[bs>>3]- +h[bn>>3]))<t*1.0e-5){t=+P(+(+h[71]- +h[70]));if(+P(+(+h[bs+8>>3]- +h[bn+8>>3]))<t*1.0e-5){bu=br;break}}c[bt>>2]=bn;bu=a8+2|0}else{bu=br}}while(0);a8=bu<<3;a6=ut(a8)|0;if((a6|0)==0){gk();b=ut(a8)|0;if((b|0)==0){as=3081;break L4086}else{bv=b}}else{bv=a6}a6=bv;b=ut(a8)|0;if((b|0)==0){gk();ap=ut(a8)|0;if((ap|0)==0){as=3084;break L4086}else{bw=ap}}else{bw=b}b=bw;ap=ut(a8)|0;if((ap|0)==0){gk();ao=ut(a8)|0;if((ao|0)==0){as=3087;break L4086}else{bx=ao}}else{bx=ap}ap=bx;t=+h[11];L4158:do{if((bu|0)>2){ac=ad>t?ad:t;s=u>t?u:t;ao=bu*24&-1;a8=ut(ao)|0;if((a8|0)==0){gk();a2=ut(ao)|0;if((a2|0)==0){as=3091;break L4086}else{by=a2}}else{by=a8}a8=by;a2=bu-1|0;if((a2|0)>0){ao=0;a1=bn;while(1){aY=a1+16|0;a_=b+(ao<<3)|0;h[a_>>3]=+h[c[aY>>2]>>3]- +h[a1>>3];ab=+h[(c[aY>>2]|0)+8>>3]- +h[a1+8>>3];aX=ap+(ao<<3)|0;h[aX>>3]=ab;r=+h[a_>>3]/s;L=ab/ac;ab=+Q(+(r*r+L*L));a$=a6+(ao<<3)|0;h[a$>>3]=ab;h[a_>>3]=+h[a_>>3]/ab;h[aX>>3]=+h[aX>>3]/+h[a$>>3];a$=ao+1|0;if((a$|0)<(a2|0)){ao=a$;a1=c[aY>>2]|0}else{break}}}if(bq){h[a6+(a2<<3)>>3]=+h[a6>>3];h[b+(a2<<3)>>3]=+h[b>>3];h[ap+(a2<<3)>>3]=+h[ap>>3];bz=a2}else{bz=bu-2|0}a1=(bz|0)>0;if(a1){ao=0;while(1){a7=a6+(ao<<3)|0;h[a8+(ao*24&-1)>>3]=+h[a7>>3];aY=ao+1|0;a$=a6+(aY<<3)|0;h[a8+(ao*24&-1)+8>>3]=(+h[a7>>3]+ +h[a$>>3])*2.0;h[a8+(ao*24&-1)+16>>3]=+h[a$>>3];a$=b+(ao<<3)|0;h[a$>>3]=(+h[b+(aY<<3)>>3]- +h[a$>>3])*6.0;a7=ap+(ao<<3)|0;ab=(+h[ap+(aY<<3)>>3]- +h[a7>>3])*6.0;h[a7>>3]=ab;L=+h[a$>>3];r=L/s;K=ab/ac;ab=+Q(+(r*r+K*K))/8.5;if(ab>1.0){h[a$>>3]=L/ab;h[a7>>3]=+h[a7>>3]/ab}if((aY|0)<(bz|0)){ao=aY}else{break}}}if(!bq){ao=by;aY=by+8|0;h[aY>>3]=+h[ao>>3]+ +h[aY>>3];h[ao>>3]=0.0;ao=bz-1|0;aY=a8+(ao*24&-1)+16|0;a7=a8+(ao*24&-1)+8|0;h[a7>>3]=+h[aY>>3]+ +h[a7>>3];h[aY>>3]=0.0}L4180:do{if((bz|0)>=1){ac=+h[by+8>>3];if(ac<=0.0){break}s=+h[by>>3];aY=a8+((bz-1|0)*24&-1)+8|0;ab=+h[aY>>3];a7=bz-2|0;if((a7|0)>0){ao=0;L=s;K=ab;r=ac;while(1){a$=a8+(ao*24&-1)+16|0;J=+h[a$>>3];I=J/r;h[a$>>3]=I;H=L/r;h[a8+(ao*24&-1)>>3]=H;a$=ao+1|0;aX=a8+(a$*24&-1)+8|0;G=+h[aX>>3]-J*I;if(G<=0.0){break L4180}J=L*(-0.0-I);I=K-L*H;h[aX>>3]=G;if((a$|0)<(a7|0)){ao=a$;L=J;K=I;r=G}else{bA=J;bB=I;bC=G;break}}}else{bA=s;bB=ab;bC=ac}if((bz|0)>1){r=bA+ +h[a8+(a7*24&-1)+16>>3];K=r/bC;h[a8+(a7*24&-1)>>3]=K;L=bB-K*r;h[aY>>3]=L;if(L<=0.0){break}}et(a8,b,bz);et(a8,ap,bz);if(a1){ao=bz;while(1){a$=ao-1|0;h[b+(ao<<3)>>3]=+h[b+(a$<<3)>>3];h[ap+(ao<<3)>>3]=+h[ap+(a$<<3)>>3];if((a$|0)>0){ao=a$}else{break}}}if(bq){h[b>>3]=+h[b+(bz<<3)>>3];h[ap>>3]=+h[ap+(bz<<3)>>3]}else{h[b>>3]=+h[bw+8>>3];h[ap>>3]=+h[bx+8>>3];ao=bz+1|0;h[b+(ao<<3)>>3]=+h[b+(bz<<3)>>3];h[ap+(ao<<3)>>3]=+h[ap+(bz<<3)>>3]}uu(by);bD=a2;break L4158}}while(0);uu(by);uu(bv);uu(bw);uu(bx);if(!bq){bE=bn;break L4130}c[bt>>2]=0;bE=bn;break L4130}else{if((bu|0)>1){h[b>>3]=0.0;h[ap>>3]=0.0;h[bw+8>>3]=0.0;h[bx+8>>3]=0.0;h[a6>>3]=1.0;bD=bu-1|0;break}uu(bv);uu(bw);uu(bx);if(!bq){bE=bn;break L4130}c[bt>>2]=0;bE=bn;break L4130}}while(0);a2=aa(c[13366]|0,bD);a1=(bD|0)>0;if(a1){t=0.0;a8=0;do{t=t+ +h[a6+(a8<<3)>>3];a8=a8+1|0;}while((a8|0)<(bD|0));bF=t*.9999999}else{bF=0.0}ac=bF/+(a2|0);ab=+h[bn>>3];s=+h[bn+8>>3];a8=c[12922]|0;if((a8|0)>98){ao=ut(56)|0;if((ao|0)==0){gk();aY=ut(56)|0;if((aY|0)==0){as=3127;break L4086}else{bG=aY}}else{bG=ao}ao=bG;aY=c[12922]<<6;a7=ut(aY)|0;if((a7|0)==0){gk();a$=ut(aY)|0;if((a$|0)==0){as=3130;break L4086}else{bH=a$}}else{bH=a7}a7=bH;a$=bG+4|0;c[a$>>2]=a7;aY=c[12922]|0;L4220:do{if((aY|0)>0){aX=0;a_=a7;while(1){aA=aX<<1;h[a_+(aX<<6)+8>>3]=+h[51696+(aA<<3)>>3];h[(c[a$>>2]|0)+(aX<<6)+16>>3]=+h[51696+((aA|1)<<3)>>3];h[(c[a$>>2]|0)+(aX<<6)+24>>3]=+h[6688];aA=aX+1|0;if((aA|0)>=(aY|0)){break L4220}aX=aA;a_=c[a$>>2]|0}}}while(0);c[bG+44>>2]=aY;c[bG>>2]=c[13370];c[13370]=ao;a[bG+8|0]=0;a$=a8<<1;h[6462]=+h[51696+(a$-2<<3)>>3];h[6463]=+h[51696+(a$-1<<3)>>3];c[12922]=1;bI=1}else{bI=a8}a$=bI<<1;h[51696+(a$<<3)>>3]=ab;h[51696+((a$|1)<<3)>>3]=s;a$=bI+1|0;c[12922]=a$;L4226:do{if(a1){t=s;L=ab;r=ac+0.0;a7=bn;a2=0;a_=a$;while(1){aX=c[a7+16>>2]|0;aA=a6+(a2<<3)|0;K=+h[aA>>3];G=+h[aX>>3];I=+h[aX+8>>3];J=(G-L)/K;H=(I-t)/K;a0=a2+1|0;Y=+h[b+(a0<<3)>>3];X=+h[b+(a2<<3)>>3];W=(Y+X*2.0)/6.0;U=+h[ap+(a0<<3)>>3];S=+h[ap+(a2<<3)>>3];F=(U+S*2.0)/6.0;E=K*6.0;D=(Y-X)/E;X=(U-S)/E;if(r>K){bJ=r;bK=K;bL=a_}else{E=r;p=a_;while(1){S=E-K;U=L+E*(J+S*(W+D*E));Y=t+E*(H+S*(F+X*E));if((p|0)>98){aN=ut(56)|0;if((aN|0)==0){gk();aM=ut(56)|0;if((aM|0)==0){as=3141;break L4086}else{bM=aM}}else{bM=aN}aN=bM;aM=c[12922]<<6;aL=ut(aM)|0;if((aL|0)==0){gk();aH=ut(aM)|0;if((aH|0)==0){as=3144;break L4086}else{bN=aH}}else{bN=aL}aL=bN;aH=bM+4|0;c[aH>>2]=aL;aM=c[12922]|0;L4241:do{if((aM|0)>0){a4=0;aW=aL;while(1){aJ=a4<<1;h[aW+(a4<<6)+8>>3]=+h[51696+(aJ<<3)>>3];h[(c[aH>>2]|0)+(a4<<6)+16>>3]=+h[51696+((aJ|1)<<3)>>3];h[(c[aH>>2]|0)+(a4<<6)+24>>3]=+h[6688];aJ=a4+1|0;if((aJ|0)>=(aM|0)){break L4241}a4=aJ;aW=c[aH>>2]|0}}}while(0);c[bM+44>>2]=aM;c[bM>>2]=c[13370];c[13370]=aN;a[bM+8|0]=0;aH=p<<1;h[6462]=+h[51696+(aH-2<<3)>>3];h[6463]=+h[51696+(aH-1<<3)>>3];c[12922]=1;bO=1}else{bO=p}aH=bO<<1;h[51696+(aH<<3)>>3]=U;h[51696+((aH|1)<<3)>>3]=Y;aH=bO+1|0;c[12922]=aH;S=ac+E;C=+h[aA>>3];if(S>C){bJ=S;bK=C;bL=aH;break}else{E=S;p=aH}}}if((a0|0)>=(bD|0)){break L4226}t=I;L=G;r=bJ-bK;a7=aX;a2=a0;a_=bL}}}while(0);uu(bv);uu(bw);uu(bx);if(bq){c[bt>>2]=0}ap=ut(56)|0;if((ap|0)==0){gk();b=ut(56)|0;if((b|0)==0){as=3156;break L4086}else{bP=b}}else{bP=ap}ap=bP;b=c[12922]<<6;a6=ut(b)|0;if((a6|0)==0){gk();a$=ut(b)|0;if((a$|0)==0){as=3159;break L4086}else{bR=a$}}else{bR=a6}a6=bR;a$=bP+4|0;c[a$>>2]=a6;b=c[12922]|0;L4259:do{if((b|0)>0){a1=0;a8=a6;while(1){ao=a1<<1;h[a8+(a1<<6)+8>>3]=+h[51696+(ao<<3)>>3];h[(c[a$>>2]|0)+(a1<<6)+16>>3]=+h[51696+((ao|1)<<3)>>3];h[(c[a$>>2]|0)+(a1<<6)+24>>3]=+h[6688];ao=a1+1|0;if((ao|0)>=(b|0)){break L4259}a1=ao;a8=c[a$>>2]|0}}}while(0);c[bP+44>>2]=b;c[bP>>2]=c[13370];c[13370]=ap;a[bP+8|0]=0;c[12922]=0;bE=bn}else if((a9|0)==0){a$=bn;a6=c[12922]|0;do{ac=+h[a$>>3];ab=+h[a$+8>>3];if((a6|0)>98){a8=ut(56)|0;if((a8|0)==0){gk();a1=ut(56)|0;if((a1|0)==0){as=3049;break L4086}else{bS=a1}}else{bS=a8}a8=bS;a1=c[12922]<<6;ao=ut(a1)|0;if((ao|0)==0){gk();aY=ut(a1)|0;if((aY|0)==0){as=3052;break L4086}else{bT=aY}}else{bT=ao}ao=bT;aY=bS+4|0;c[aY>>2]=ao;a1=c[12922]|0;L4275:do{if((a1|0)>0){a_=0;a2=ao;while(1){a7=a_<<1;h[a2+(a_<<6)+8>>3]=+h[51696+(a7<<3)>>3];h[(c[aY>>2]|0)+(a_<<6)+16>>3]=+h[51696+((a7|1)<<3)>>3];h[(c[aY>>2]|0)+(a_<<6)+24>>3]=+h[6688];a7=a_+1|0;if((a7|0)>=(a1|0)){break L4275}a_=a7;a2=c[aY>>2]|0}}}while(0);c[bS+44>>2]=a1;c[bS>>2]=c[13370];c[13370]=a8;a[bS+8|0]=0;aY=a6<<1;h[6462]=+h[51696+(aY-2<<3)>>3];h[6463]=+h[51696+(aY-1<<3)>>3];c[12922]=1;bU=1}else{bU=a6}aY=bU<<1;h[51696+(aY<<3)>>3]=ac;h[51696+((aY|1)<<3)>>3]=ab;a6=bU+1|0;c[12922]=a6;a$=c[a$+16>>2]|0;}while((a$|0)!=0);a$=ut(56)|0;if((a$|0)==0){gk();a6=ut(56)|0;if((a6|0)==0){as=3060;break L4086}else{bV=a6}}else{bV=a$}a$=bV;a6=c[12922]<<6;ap=ut(a6)|0;if((ap|0)==0){gk();b=ut(a6)|0;if((b|0)==0){as=3063;break L4086}else{bW=b}}else{bW=ap}ap=bW;b=bV+4|0;c[b>>2]=ap;a6=c[12922]|0;L4288:do{if((a6|0)>0){aY=0;ao=ap;while(1){a2=aY<<1;h[ao+(aY<<6)+8>>3]=+h[51696+(a2<<3)>>3];h[(c[b>>2]|0)+(aY<<6)+16>>3]=+h[51696+((a2|1)<<3)>>3];h[(c[b>>2]|0)+(aY<<6)+24>>3]=+h[6688];a2=aY+1|0;if((a2|0)>=(a6|0)){break L4288}aY=a2;ao=c[b>>2]|0}}}while(0);c[bV+44>>2]=a6;c[bV>>2]=c[13370];c[13370]=a$;a[bV+8|0]=0;c[12922]=0;bE=bn}else if((a9|0)==2){do{if(M){bX=1}else{b=bn;while(1){ap=c[b+16>>2]|0;if((ap|0)==0){break}else{b=ap}}ab=+P(+(+h[99]- +h[98]));if(+P(+(+h[b>>3]- +h[bn>>3]))>=ab*1.0e-5){bX=0;break}ab=+P(+(+h[71]- +h[70]));bX=+P(+(+h[b+8>>3]- +h[bn+8>>3]))<ab*1.0e-5}}while(0);a$=c[13368]|0;a6=bn;ap=0;while(1){bY=ap+1|0;ao=c[a6+16>>2]|0;if((ao|0)==0){break}else{a6=ao;ap=bY}}a6=a$-1|0;if((bY|0)<2){bE=bn;break}ao=(a6|0)>(ap|0)?ap:a6;do{if(bX){a6=bn;while(1){bZ=a6+16|0;aY=c[bZ>>2]|0;if((aY|0)==0){break}else{a6=aY}}ab=+P(+(+h[99]- +h[98]));do{if(+P(+(+h[a6>>3]- +h[bn>>3]))<ab*1.0e-5){ac=+P(+(+h[71]- +h[70]));if(+P(+(+h[a6+8>>3]- +h[bn+8>>3]))>=ac*1.0e-5){b_=bY;b$=bn;break}b_=ap;b$=c[bn+16>>2]|0}else{b_=bY;b$=bn}}while(0);c[bZ>>2]=b$;b=b_+ao|0;b0=a6;b1=b;b2=+(ao|0);b3=b;as=3179}else{if((bY|0)<=(ao|0)){b4=bn;b5=ao;b6=bY;b7=0;b8=0.0;break}b0=0;b1=bY;b2=0.0;b3=bY-ao|0;as=3179}}while(0);L4314:do{if((as|0)==3179){as=0;ab=+(b3|0);ac=1.0/+(c[13366]|0);if(b2>=ab){b4=bn;b5=ao;b6=b1;b7=b0;b8=ab;break}ap=b1-ao|0;a$=2;G=b2;I=b2+1.0;b=ao;aY=bn;while(1){if(G>I){b9=c[aY+16>>2]|0;ca=b+1|0;cb=I+1.0}else{b9=aY;ca=b;cb=I}ev(G,b9,b1,ao,ca,bX,f,g);s=+h[f>>3];r=+h[g>>3];a8=c[12922]|0;if((a8|0)>98){a1=ut(56)|0;if((a1|0)==0){gk();a2=ut(56)|0;if((a2|0)==0){as=3186;break L4086}else{cc=a2}}else{cc=a1}a1=cc;a2=c[12922]<<6;a_=ut(a2)|0;if((a_|0)==0){gk();a0=ut(a2)|0;if((a0|0)==0){as=3189;break L4086}else{cd=a0}}else{cd=a_}a_=cd;a0=cc+4|0;c[a0>>2]=a_;a2=c[12922]|0;L4330:do{if((a2|0)>0){aX=0;a7=a_;while(1){p=aX<<1;h[a7+(aX<<6)+8>>3]=+h[51696+(p<<3)>>3];h[(c[a0>>2]|0)+(aX<<6)+16>>3]=+h[51696+((p|1)<<3)>>3];h[(c[a0>>2]|0)+(aX<<6)+24>>3]=+h[6688];p=aX+1|0;if((p|0)>=(a2|0)){break L4330}aX=p;a7=c[a0>>2]|0}}}while(0);c[cc+44>>2]=a2;c[cc>>2]=c[13370];c[13370]=a1;a[cc+8|0]=0;a0=a8<<1;h[6462]=+h[51696+(a0-2<<3)>>3];h[6463]=+h[51696+(a0-1<<3)>>3];c[12922]=1;ce=1}else{ce=a8}a0=ce<<1;h[51696+(a0<<3)>>3]=s;h[51696+((a0|1)<<3)>>3]=r;c[12922]=ce+1;if((a$|0)==(aa(c[13366]|0,ap)+1|0)){b4=b9;b5=ca;b6=b1;b7=b0;b8=ab;break L4314}Y=ac+G;if(Y<ab){a$=a$+1|0;G=Y;I=cb;b=ca;aY=b9}else{b4=b9;b5=ca;b6=b1;b7=b0;b8=ab;break}}}}while(0);ev(b8+-1.0e-5,b4,b6,ao,b5,bX,f,g);ab=+h[f>>3];I=+h[g>>3];aY=c[12922]|0;if((aY|0)>98){b=ut(56)|0;if((b|0)==0){gk();a$=ut(56)|0;if((a$|0)==0){as=3199;break L4086}else{cf=a$}}else{cf=b}b=cf;a$=c[12922]<<6;ap=ut(a$)|0;if((ap|0)==0){gk();a6=ut(a$)|0;if((a6|0)==0){as=3202;break L4086}else{cg=a6}}else{cg=ap}ap=cg;a6=cf+4|0;c[a6>>2]=ap;a$=c[12922]|0;L4346:do{if((a$|0)>0){a0=0;a_=ap;while(1){a7=a0<<1;h[a_+(a0<<6)+8>>3]=+h[51696+(a7<<3)>>3];h[(c[a6>>2]|0)+(a0<<6)+16>>3]=+h[51696+((a7|1)<<3)>>3];h[(c[a6>>2]|0)+(a0<<6)+24>>3]=+h[6688];a7=a0+1|0;if((a7|0)>=(a$|0)){break L4346}a0=a7;a_=c[a6>>2]|0}}}while(0);c[cf+44>>2]=a$;c[cf>>2]=c[13370];c[13370]=b;a[cf+8|0]=0;a6=aY<<1;h[6462]=+h[51696+(a6-2<<3)>>3];h[6463]=+h[51696+(a6-1<<3)>>3];c[12922]=1;ch=1}else{ch=aY}a6=ch<<1;h[51696+(a6<<3)>>3]=ab;h[51696+((a6|1)<<3)>>3]=I;c[12922]=ch+1;if(bX){c[b7+16>>2]=0}a6=ut(56)|0;if((a6|0)==0){gk();ap=ut(56)|0;if((ap|0)==0){as=3211;break L4086}else{ci=ap}}else{ci=a6}a6=ci;ap=c[12922]<<6;ao=ut(ap)|0;if((ao|0)==0){gk();a_=ut(ap)|0;if((a_|0)==0){as=3214;break L4086}else{cj=a_}}else{cj=ao}ao=cj;a_=ci+4|0;c[a_>>2]=ao;ap=c[12922]|0;L4361:do{if((ap|0)>0){a0=0;a7=ao;while(1){aX=a0<<1;h[a7+(a0<<6)+8>>3]=+h[51696+(aX<<3)>>3];h[(c[a_>>2]|0)+(a0<<6)+16>>3]=+h[51696+((aX|1)<<3)>>3];h[(c[a_>>2]|0)+(a0<<6)+24>>3]=+h[6688];aX=a0+1|0;if((aX|0)>=(ap|0)){break L4361}a0=aX;a7=c[a_>>2]|0}}}while(0);c[ci+44>>2]=ap;c[ci>>2]=c[13370];c[13370]=a6;a[ci+8|0]=0;c[12922]=0;bE=bn}else{bE=bn}}while(0);while(1){M=c[bE+16>>2]|0;uu(bE);if((M|0)==0){bp=bo;break}else{bE=M}}}}while(0);if((c[j>>2]|0)>0){aj=bp}else{break}}}}while(0);aj=c[13370]|0;if((aj|0)!=(ba|0)){a[aj+8|0]=1;aj=(c[13370]|0)+9|0;if((a[64788]&1)==0){ck=bi}else{ck=+Z(+(bi*+h[8100]))}ud(aj,32,53520,1.0,ck);h[(c[13370]|0)+48>>3]=bi}aj=k+1|0;if((aj|0)<(bh|0)){k=aj;be=bi}else{break L4084}}if((as|0)==3052){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=135136,v)|0);return 0}else if((as|0)==3081){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=105064,v)|0);return 0}else if((as|0)==3084){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=98096,v)|0);return 0}else if((as|0)==3049){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155520,v)|0);return 0}else if((as|0)==3087){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=92152,v)|0);return 0}else if((as|0)==3091){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=84352,v)|0);return 0}else if((as|0)==3127){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155520,v)|0);return 0}else if((as|0)==3063){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=135136,v)|0);return 0}else if((as|0)==3060){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155520,v)|0);return 0}else if((as|0)==3130){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=135136,v)|0);return 0}else if((as|0)==3141){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155520,v)|0);return 0}else if((as|0)==3144){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=135136,v)|0);return 0}else if((as|0)==3156){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155520,v)|0);return 0}else if((as|0)==3159){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=135136,v)|0);return 0}else if((as|0)==3186){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155520,v)|0);return 0}else if((as|0)==3189){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=135136,v)|0);return 0}else if((as|0)==3199){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155520,v)|0);return 0}else if((as|0)==3202){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=135136,v)|0);return 0}else if((as|0)==3211){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155520,v)|0);return 0}else if((as|0)==3214){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=135136,v)|0);return 0}}}while(0);if((bc|0)!=0){as=bc;while(1){bc=c[as+12>>2]|0;uu(as);if((bc|0)==0){break}else{as=bc}}}if(!al){al=bb;while(1){bb=c[al+16>>2]|0;uu(al);if((bb|0)==0){break}else{al=bb}}}bd=c[13370]|0;i=e;return bd|0}function eq(){a[47544]=0;a[47856]=1;return}function er(){a[47544]=1;a[47856]=1;return}function es(){return}function et(a,b,c){a=a|0;b=b|0;c=c|0;var d=0,e=0.0,f=0,g=0,i=0.0,j=0.0,k=0,l=0,m=0.0,n=0.0,o=0.0;d=b+(c-1<<3)|0;e=+h[d>>3];f=c-2|0;if((f|0)>0){g=0;i=e;j=+h[b>>3];while(1){k=g+1|0;l=b+(k<<3)|0;m=+h[l>>3]- +h[a+(g*24&-1)+16>>3]*j;h[l>>3]=m;n=i- +h[a+(g*24&-1)>>3]*j;if((k|0)<(f|0)){g=k;i=n;j=m}else{o=n;break}}}else{o=e}g=(c|0)>1;if(g){h[d>>3]=o- +h[a+(f*24&-1)>>3]*+h[b+(f<<3)>>3]}if((c|0)>0){k=0;do{l=b+(k<<3)|0;h[l>>3]=+h[l>>3]/+h[a+(k*24&-1)+8>>3];k=k+1|0;}while((k|0)<(c|0))}o=+h[d>>3];if(g){g=b+(f<<3)|0;h[g>>3]=+h[g>>3]-o*+h[a+(f*24&-1)>>3]}g=c-3|0;if((g|0)<=-1){return}c=g;e=+h[b+(f<<3)>>3];while(1){f=b+(c<<3)|0;j=+h[f>>3]-(+h[a+(c*24&-1)+16>>3]*e+o*+h[a+(c*24&-1)>>3]);h[f>>3]=j;if((c|0)>0){c=c-1|0;e=j}else{break}}return}function eu(b,d,e,f){b=b|0;d=+d;e=e|0;f=f|0;var g=0,i=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0.0;if(!f){a[b+20|0]=0;c[e>>2]=(c[e>>2]|0)-1}do{if((c[b>>2]|0)==0){if((c[b+4>>2]|0)==0){g=0}else{break}return g|0}}while(0);f=ew(b,d)|0;i=0;j=b;k=f;while(1){l=c[j>>2]|0;if((l|0)==(i|0)){n=c[j+4>>2]|0}else{n=l}l=c[n>>2]|0;if((l|0)==(j|0)){o=0}else{o=(a[l+20|0]&1)==0?0:l}l=c[n+4>>2]|0;if((l|0)==(j|0)){p=o}else{p=(a[l+20|0]&1)==0?o:l}l=c[n+8>>2]|0;if((l|0)==(j|0)){q=p}else{q=(a[l+20|0]&1)==0?p:l}if((q|0)==0){r=3261;break}a[q+20|0]=0;c[e>>2]=(c[e>>2]|0)-1;l=q+24|0;do{if((c[l>>2]|0)==3){s=k}else{t=ew(q,d)|0;c[k+16>>2]=t;u=+P(+(+h[99]- +h[98]));if(+P(+(+h[k>>3]- +h[t>>3]))>=u*1.0e-5){s=t;break}u=+P(+(+h[71]- +h[70]));if(+P(+(+h[k+8>>3]- +h[t+8>>3]))>=u*1.0e-5){s=t;break}uu(t);s=k}}while(0);if((q|0)==(b|0)){r=3271;break}if((c[l>>2]|0)==2){r=3270;break}else{i=n;j=q;k=s}}if((r|0)==3270){c[s+16>>2]=0;g=f;return g|0}else if((r|0)==3261){c[k+16>>2]=0;if((f|0)!=0){k=f;while(1){q=c[k+16>>2]|0;uu(k);if((q|0)==0){break}else{k=q}}}aI(223848,41,1,c[m>>2]|0);g=0;return g|0}else if((r|0)==3271){c[s+16>>2]=0;h[f>>3]=+h[s>>3];h[f+8>>3]=+h[s+8>>3];g=f;return g|0}return 0}function ev(a,b,d,e,f,g,j,k){a=+a;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;j=j|0;k=k|0;var l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0,u=0,w=0.0,x=0,y=0,z=0.0,A=0,B=0,C=0.0,D=0.0,E=0.0,F=0,G=0,H=0.0,I=0.0,J=0,K=0.0;l=i;m=f+e<<3;n=ut(m)|0;do{if((n|0)==0){gk();o=ut(m)|0;if((o|0)!=0){p=o;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=122288,v)|0)}else{p=n}}while(0);n=p;o=ut(m)|0;do{if((o|0)==0){gk();q=ut(m)|0;if((q|0)!=0){r=q;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=122288,v)|0)}else{r=o}}while(0);o=r;m=f-e|0;if((e|0)<0){s=n+(f<<3)|0;t=+h[s>>3];h[j>>3]=t;u=o+(f<<3)|0;w=+h[u>>3];h[k>>3]=w;uu(p);uu(r);i=l;return}else{x=m;y=b}while(1){h[n+(x<<3)>>3]=+h[y>>3];h[o+(x<<3)>>3]=+h[y+8>>3];b=x+1|0;if((b|0)>(f|0)){break}else{x=b;y=c[y+16>>2]|0}}if((e|0)<1){s=n+(f<<3)|0;t=+h[s>>3];h[j>>3]=t;u=o+(f<<3)|0;w=+h[u>>3];h[k>>3]=w;uu(p);uu(r);i=l;return}y=e+1|0;z=+(d-e|0);x=1;b=-1;while(1){q=x+m|0;if((q|0)<=(f|0)){A=y+b|0;B=f;while(1){do{if(g){C=+(A+B|0);D=+(B|0)}else{do{if((B|0)>(e|0)){if((B|0)>(d|0)){E=z;break}E=+(B-e|0)}else{E=0.0}}while(0);F=A+B|0;if((F|0)<=(e|0)){C=0.0;D=E;break}if((F|0)>(d|0)){C=z;D=E;break}C=+(F-e|0);D=E}}while(0);if(D==C){G=B-1|0}else{F=n+(B<<3)|0;H=a-D;I=C-D;J=B-1|0;K=C-a;h[F>>3]=H*+h[F>>3]/I+K*+h[n+(J<<3)>>3]/I;F=o+(B<<3)|0;h[F>>3]=H*+h[F>>3]/I+K*+h[o+(J<<3)>>3]/I;G=J}if((G|0)<(q|0)){break}else{B=G}}}B=x+1|0;q=x^-1;if((B|0)>(e|0)){break}else{x=B;b=q}}s=n+(f<<3)|0;t=+h[s>>3];h[j>>3]=t;u=o+(f<<3)|0;w=+h[u>>3];h[k>>3]=w;uu(p);uu(r);i=l;return}function ew(a,b){a=a|0;b=+b;var d=0,e=0,f=0.0,g=0,j=0.0,k=0,l=0;d=i;e=a+8|0;f=+h[(c[e>>2]|0)+24>>3];g=a+12|0;j=(b-f)/(+h[(c[g>>2]|0)+24>>3]-f);f=j<0.0?0.0:j;j=f>1.0?1.0:f;a=ut(24)|0;do{if((a|0)==0){gk();k=ut(24)|0;if((k|0)!=0){l=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=216768,v)|0);return 0}else{l=a}}while(0);f=1.0-j;h[l>>3]=j*+h[(c[g>>2]|0)+8>>3]+f*+h[(c[e>>2]|0)+8>>3];h[l+8>>3]=j*+h[(c[g>>2]|0)+16>>3]+f*+h[(c[e>>2]|0)+16>>3];i=d;return l|0}function ex(){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0;d=i;i=i+16|0;e=d|0;f=d+8|0;g=hA(c[11932]|0,78360)|0;if((g|0)==0){uj(-1,146640,(v=i,i=i+8|0,c[v>>2]=c[11932],v)|0)}h=e;if((cd(h|0,4,1,g|0)|0)==0){uj(-1,146480,(v=i,i=i+8|0,c[v>>2]=c[11932],v)|0)}j=c[e>>2]|0;k=j>>>0>65535?3:0;l=(k-2|0)>>>0<2;L4523:do{if(l){m=0;n=3;o=j&255;while(1){p=h+n|0;a[h+m|0]=a[p]|0;a[p]=o;p=m+1|0;q=n-1|0;if((p|0)>=(q|0)){break L4523}m=p;n=q;o=a[h+p|0]|0}}}while(0);j=(k-1|0)>>>0<2;if(j){k=h+2|0;o=a[k]|0;n=h+3|0;a[k]=a[n]|0;a[n]=o;o=a[h]|0;n=h+1|0;a[h]=a[n]|0;a[n]=o}o=f;if((cd(o|0,4,1,g|0)|0)==0){uj(-1,146280,(v=i,i=i+8|0,c[v>>2]=c[11932],v)|0)}if(l){l=0;n=3;do{h=o+l|0;k=a[h]|0;m=o+n|0;a[h]=a[m]|0;a[m]=k;l=l+1|0;n=n-1|0;}while((l|0)<(n|0))}if(j){j=o+2|0;n=a[j]|0;l=o+3|0;a[j]=a[l]|0;a[l]=n;n=a[o]|0;l=o+1|0;a[o]=a[l]|0;a[l]=n}az(g|0);a[47544]=0;a[47856]=1;c[(c[12172]|0)+128>>2]=8;c[(c[12172]|0)+140>>2]=c[e>>2];c[(c[12172]|0)+144>>2]=c[f>>2];c[(c[12172]|0)+152>>2]=1;c[(c[12172]|0)+156>>2]=-1;a[(c[12172]|0)+124|0]=1;c[(c[12172]|0)+112>>2]=-3;c[(c[12172]|0)+116>>2]=-4;eQ(4);f=c[11950]|0;if((c[11884]|0)<1){e=db(f,12,215112)|0;c[11950]=e;c[11884]=1;r=e}else{r=f}c[r+4>>2]=1;b[(c[11950]|0)+8>>1]=b[24026]|0;r=c[11950]|0;if((c[11884]|0)<2){f=db(r,24,215112)|0;c[11950]=f;c[11884]=2;s=f}else{s=r}c[s+16>>2]=1;b[(c[11950]|0)+20>>1]=b[24026]|0;s=c[11950]|0;if((c[11884]|0)<3){r=db(s,36,215112)|0;c[11950]=r;c[11884]=3;t=r}else{t=s}c[t+28>>2]=1;b[(c[11950]|0)+32>>1]=b[24026]|0;t=c[11950]|0;if((c[11884]|0)<4){s=db(t,48,215112)|0;c[11950]=s;c[11884]=4;u=s}else{u=t}c[u+40>>2]=1;b[(c[11950]|0)+44>>1]=b[24026]|0;u=c[11950]|0;if((c[11884]|0)>=1){w=u;x=w|0;c[x>>2]=0;c[11870]=4;c[262]=2;c[265]=3;c[268]=4;c[271]=1;i=d;return}t=db(u,12,215112)|0;c[11950]=t;c[11884]=1;w=t;x=w|0;c[x>>2]=0;c[11870]=4;c[262]=2;c[265]=3;c[268]=4;c[271]=1;i=d;return}function ey(e,f,j){e=e|0;f=f|0;j=j|0;var k=0,l=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aN=0,aO=0,aP=0,aQ=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,a_=0,a$=0,a0=0,a1=0.0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a9=0.0,ba=0,bb=0.0,bd=0,be=0,bf=0,bg=0,bh=0.0,bi=0,bj=0,bk=0,bl=0.0,bm=0,bn=0,bo=0,bp=0.0,bq=0,br=0,bs=0,bu=0.0,bv=0,bw=0,bx=0,by=0.0,bz=0,bA=0.0,bB=0,bC=0,bD=0,bE=0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bM=0,bN=0,bO=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0,b7=0,b8=0,b9=0,ca=0,cb=0,cc=0,cd=0,ce=0,cf=0,cg=0,ch=0,ci=0,cj=0,ck=0,cl=0,cm=0,cn=0,co=0.0,cq=0,cr=0,cs=0,ct=0,cv=0,cw=0,cx=0,cy=0,cz=0,cA=0,cB=0,cC=0,cD=0,cE=0.0,cF=0,cG=0,cH=0,cI=0,cJ=0,cK=0,cL=0,cM=0,cN=0,cO=0,cP=0,cQ=0,cR=0,cS=0,cT=0,cU=0,cV=0,cW=0,cX=0,cY=0,cZ=0,c_=0,c$=0,c0=0,c1=0,c2=0,c3=0,c4=0,c5=0,c6=0,c7=0,c8=0,c9=0,da=0,dc=0,dd=0,de=0,df=0,dg=0,dh=0,di=0;k=i;i=i+504|0;l=k|0;p=k+24|0;q=k+48|0;r=k+72|0;s=k+96|0;t=k+120|0;u=k+144|0;x=k+168|0;y=k+192|0;z=k+216|0;A=k+240|0;B=k+264|0;C=k+288|0;D=k+312|0;E=k+336|0;F=k+424|0;G=k+432|0;H=(c[13898]|0)-1|0;a[43464]=1;if((c[12892]|0)!=0){ez();c[12892]=0}uu(c[11930]|0);c[11930]=0;c[11872]=0;uu(c[11902]|0);c[11902]=0;eA();c[11942]=-1;c[11900]=0;c[11898]=0;c[11904]=1;c[11762]=2147483647;uu(c[9356]|0);c[9356]=0;c[11946]=0;c[14126]=2;c[10874]=1;c[10872]=1;c[10808]=0;c[10806]=0;c[8834]=2147483647;c[8832]=2147483647;a[47544]=0;a[47856]=0;c[11858]=0;c[11864]=0;a[47584]=0;a[47464]=0;a[47736]=0;c[11944]=j;c[13488]=-99;a[32928]=0;a[48872]=0;if((f|0)>=8){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=95312,v)|0);return 0}if((e|0)==0){uf(c[13898]|0,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}I=c[11932]|0;do{if((a[e]|0)==0){if((I|0)==0){J=c[13898]|0;uf(J,94168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((a[I]|0)!=0){break}J=c[13898]|0;uf(J,94168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{uu(I);c[11932]=bP(e|0)|0}}while(0);e=c[60]|0;if((e|0)!=0){e1(e)}c[60]=0;e=c[13898]|0;I=c[8272]|0;L4579:do{if((e|0)<(I|0)){J=j+23|0;K=r|0;L=r+8|0;M=p|0;N=p+8|0;O=l|0;P=l+8|0;Q=q|0;R=q+8|0;S=z|0;T=z+8|0;U=y|0;V=y+8|0;W=x|0;X=x+8|0;Y=u|0;Z=u+8|0;_=t|0;$=t+8|0;ab=s|0;ac=s+8|0;ad=D|0;ae=D+8|0;af=C|0;ag=C+8|0;ah=B|0;ai=B+8|0;aj=A|0;ak=A+8|0;al=0;am=0;an=0;ao=0;ap=0;aq=e;ar=I;L4581:while(1){as=c[1054]|0;at=(a[as+(aq*40&-1)|0]&1)==0;if(at){break L4579}au=c[as+(aq*40&-1)+36>>2]|0;av=as+(aq*40&-1)+32|0;aw=c[10036]|0;ax=0;while(1){if((ax|0)>=(au|0)){ay=3362;break}if((a[aw+((c[av>>2]|0)+ax|0)|0]|0)==(a[ax+103664|0]|0)){ax=ax+1|0}else{break}}if((ay|0)==3362){ay=0;if((ax|0)==1){break L4579}}aw=c[av>>2]|0;if(!((au|0)>0&(at^1))){break L4579}az=c[10036]|0;aA=0;aB=0;aC=aw;while(1){if((a[aA+204608|0]|0)==(a[az+(aA+aC|0)|0]|0)){aD=aC;aF=aB}else{if((aA|0)!=3){ay=3393;break}aD=aC-1|0;aF=1}aG=aA+1|0;if((aG|0)<(aF+au|0)){aA=aG;aB=aF;aC=aD}else{ay=3369;break}}do{if((ay|0)==3369){ay=0;if((aF|0)==0){if(!((aA|0)==6|(aA|0)==2)){ay=3393;break}}aC=aq+1|0;c[13898]=aC;if((a[47856]&1)!=0){aH=aC;ay=4033;break L4581}a[47856]=1;a[47544]=1;c[12170]=0;c[12220]=0;c[12222]=0;c[12224]=0;c[11876]=0;aC=c[11950]|0;if((c[11884]|0)<1){aB=db(aC,12,215112)|0;c[11950]=aB;c[11884]=1;aI=aB}else{aI=aC}c[aI>>2]=0;aC=c[12172]|0;aB=c[11864]|0;if((aB|0)>0){az=0;av=aB;while(1){aB=aC+(az*232&-1)+224|0;ax=c[aB>>2]|0;if((ax|0)==0){aJ=av}else{uu(ax);c[aB>>2]=0;aJ=c[11864]|0}aB=az+1|0;if((aB|0)<(aJ|0)){az=aB;av=aJ}else{break}}}c[11864]=0;av=c[11862]|0;if((av|0)==0){c[12204]=c[12200];c[12208]=0;az=c[12172]|0;if((c[11880]|0)<1){aC=db(az,232,216568)|0;aB=aC;c[12172]=aB;if((aC|0)==0){ay=3388;break L4581}c[11880]=1;aK=c[11864]|0;aL=aB}else{aK=0;aL=az}uD(aL+(aK*232&-1)|0,48440,232);c[11864]=(c[11864]|0)+1}else{c[12204]=c[12202];c[12208]=c[12206];if((av|0)>(c[11880]|0)){az=db(c[12172]|0,av*232&-1,216568)|0;c[12172]=az;if((az|0)==0){ay=3381;break L4581}c[11880]=av}az=c[12172]|0;aB=c[11864]|0;if((av|0)>0){aC=0;ax=aB;while(1){uD(az+(ax*232&-1)|0,48440,232);aG=(c[11864]|0)+1|0;c[11864]=aG;aN=aC+1|0;if((aN|0)<(av|0)){aC=aN;ax=aG}else{aO=aG;break}}}else{aO=aB}uD(az|0,c[12168]|0,aO*232&-1)}eB(ap,0);aP=ap;aQ=ao;aS=an;aT=am;aU=al}}while(0);L4629:do{if((ay|0)==3393){ay=0;if(!((au|0)>0&(at^1))){break L4579}aA=c[10036]|0;ax=0;aC=0;av=aw;while(1){if((a[ax+151232|0]|0)==(a[aA+(ax+av|0)|0]|0)){aV=av;aW=aC}else{if((ax|0)!=3){break}aV=av-1|0;aW=1}aG=ax+1|0;if((aG|0)<(aW+au|0)){ax=aG;aC=aW;av=aV}else{ay=3399;break}}do{if((ay|0)==3399){ay=0;if((aW|0)==0){if(!((ax|0)==6|(ax|0)==2)){break}}aX=aq+1|0;c[13898]=aX;if(ap){aH=aX;ay=4035;break L4581}if((a[47544]&1)==0){if((a[47856]&1)!=0){ay=3404;break L4581}}a[47544]=1;a[43464]=0;aP=1;aQ=ao;aS=an;aT=am;aU=al;break L4629}}while(0);if(!((au|0)>0&(at^1))){break L4579}ax=c[10036]|0;av=0;aC=0;aA=aw;while(1){if((a[av+91224|0]|0)==(a[ax+(av+aA|0)|0]|0)){aY=aA;a_=aC}else{if((av|0)!=6){break}aY=aA-1|0;a_=1}az=av+1|0;if((az|0)<(a_+au|0)){av=az;aC=a_;aA=aY}else{ay=3412;break}}do{if((ay|0)==3412){ay=0;if((a_|0)==0){if(!((av|0)==10|(av|0)==5)){break}}c[13898]=aq+1;a[47544]=1;a[47464]=1;a[43464]=0;aP=ap;aQ=ao;aS=an;aT=am;aU=al;break L4629}}while(0);if(!((au|0)>0&(at^1))){break L4579}av=c[10036]|0;aA=0;aC=0;ax=aw;while(1){if((a[aA+90656|0]|0)==(a[av+(aA+ax|0)|0]|0)){a$=ax;a0=aC}else{if((aA|0)!=1){break}a$=ax-1|0;a0=1}az=aA+1|0;if((az|0)<(a0+au|0)){aA=az;aC=a0;ax=a$}else{ay=3421;break}}do{if((ay|0)==3421){ay=0;if((a0|0)==0){if(!((aA|0)==0|(aA|0)==5)){break}}if(al){aH=aq;ay=4034;break L4581}if((a[47856]&1)!=0){if((a[47544]&1)!=0){ay=3426;break L4581}}ax=aq+1|0;c[13898]=ax;L4677:do{if((ax|0)<(ar|0)){L4679:do{if((a[as+(ax*40&-1)|0]&1)!=0){aC=c[as+(ax*40&-1)+36>>2]|0;av=as+(ax*40&-1)+32|0;az=c[10036]|0;aB=0;while(1){if((aB|0)>=(aC|0)){break}if((a[az+((c[av>>2]|0)+aB|0)|0]|0)==(a[aB+103664|0]|0)){aB=aB+1|0}else{break L4679}}if((aB|0)==1){ay=3435;break L4677}}}while(0);a[14176]=1;is(D);a[14176]=0;if((c[ad>>2]|0)!=3){c[13898]=ax;ay=3435;break}av=c[ae>>2]|0;c[9356]=av;if((av|0)==0){break}a[37432]=0;aP=ap;aQ=ao;aS=an;aT=am;aU=1;break L4629}else{ay=3435}}while(0);if((ay|0)==3435){ay=0;c[9356]=0}ax=is(C)|0;av=c[ax>>2]|0;if((av|0)==1){a1=+(c[ax+8>>2]|0)}else if((av|0)==2){a1=+h[ax+8>>3]}else if((av|0)==3){a1=+uz(c[ax+8>>2]|0,0)}else{ay=3442;break L4581}if((c[af>>2]|0)==3){uu(c[ag>>2]|0);c[af>>2]=1}ax=~~a1;c[11898]=ax;av=c[13898]|0;az=c[8272]|0;L4701:do{if((az|0)>(av|0)){aC=c[1054]|0;if((a[aC+(av*40&-1)|0]&1)==0){break}aG=c[aC+(av*40&-1)+36>>2]|0;aN=aC+(av*40&-1)+32|0;a2=c[10036]|0;a3=0;while(1){if((a3|0)>=(aG|0)){break}if((a[a2+((c[aN>>2]|0)+a3|0)|0]|0)==(a[a3+183584|0]|0)){a3=a3+1|0}else{break L4701}}if((a3|0)!=1){break}aN=av+1|0;c[13898]=aN;L4709:do{if((az|0)>(aN|0)){if((a[aC+(aN*40&-1)|0]&1)==0){ay=3458;break}aG=c[aC+(aN*40&-1)+36>>2]|0;a4=aC+(aN*40&-1)+32|0;a5=0;while(1){if((a5|0)>=(aG|0)){break}if((a[a2+((c[a4>>2]|0)+a5|0)|0]|0)==(a[a5+183584|0]|0)){a5=a5+1|0}else{ay=3458;break L4709}}if((a5|0)!=1){ay=3458;break}c[11762]=2147483647;a6=aN;a7=az}else{ay=3458}}while(0);if((ay|0)==3458){ay=0;aN=is(B)|0;a2=c[aN>>2]|0;if((a2|0)==3){a9=+uz(c[aN+8>>2]|0,0)}else if((a2|0)==1){a9=+(c[aN+8>>2]|0)}else if((a2|0)==2){a9=+h[aN+8>>3]}else{ay=3462;break L4581}if((c[ah>>2]|0)==3){uu(c[ai>>2]|0);c[ah>>2]=1}aN=~~a9;c[11762]=aN;ba=c[13898]|0;if((aN|0)<(c[11898]|0)){ay=3467;break L4581}a6=ba;a7=c[8272]|0}if((a7|0)<=(a6|0)){aP=ap;aQ=ao;aS=an;aT=am;aU=1;break L4629}aN=c[1054]|0;if((a[aN+(a6*40&-1)|0]&1)==0){aP=ap;aQ=ao;aS=an;aT=am;aU=1;break L4629}a2=c[aN+(a6*40&-1)+36>>2]|0;aC=aN+(a6*40&-1)+32|0;aN=c[10036]|0;a3=0;while(1){if((a3|0)>=(a2|0)){break}if((a[aN+((c[aC>>2]|0)+a3|0)|0]|0)==(a[a3+183584|0]|0)){a3=a3+1|0}else{aP=ap;aQ=ao;aS=an;aT=am;aU=1;break L4629}}if((a3|0)!=1){aP=ap;aQ=ao;aS=an;aT=am;aU=1;break L4629}c[13898]=a6+1;aC=is(A)|0;aN=c[aC>>2]|0;if((aN|0)==1){bb=+(c[aC+8>>2]|0)}else if((aN|0)==2){bb=+h[aC+8>>3]}else if((aN|0)==3){bb=+uz(c[aC+8>>2]|0,0)}else{ay=3478;break L4581}if((c[aj>>2]|0)==3){uu(c[ak>>2]|0);c[aj>>2]=1}aC=~~bb;aN=(aC|0)>-1?aC:-aC|0;c[11904]=aN;if((aN|0)<1){ay=3482;break L4581}else{aP=ap;aQ=ao;aS=an;aT=am;aU=1;break L4629}}}while(0);c[11762]=ax;aP=ap;aQ=ao;aS=an;aT=am;aU=1;break L4629}}while(0);if(at){break L4579}L4747:do{if((au|0)>0){aA=c[10036]|0;az=0;av=0;aN=aw;while(1){if((a[az+90160|0]|0)==(a[aA+(az+aN|0)|0]|0)){bd=aN;be=av}else{if((az|0)!=2){break L4747}bd=aN-1|0;be=1}aC=az+1|0;if((aC|0)<(be+au|0)){az=aC;av=be;aN=bd}else{break}}if((be|0)==0){if(!((az|0)==1|(az|0)==5)){break}}if(am){aH=aq;ay=4036;break L4581}a[43464]=0;aN=aq+1|0;c[13898]=aN;L4760:do{if((ar|0)>(aN|0)){if((a[as+(aN*40&-1)|0]&1)==0){ay=3500;break}av=c[as+(aN*40&-1)+36>>2]|0;aA=as+(aN*40&-1)+32|0;ax=c[10036]|0;aC=0;while(1){if((aC|0)>=(av|0)){break}if((a[ax+((c[aA>>2]|0)+aC|0)|0]|0)==(a[aC+183584|0]|0)){aC=aC+1|0}else{ay=3500;break L4760}}if((aC|0)==1){bf=aN;bg=ar}else{ay=3500}}else{ay=3500}}while(0);if((ay|0)==3500){ay=0;aN=is(z)|0;az=c[aN>>2]|0;if((az|0)==1){bh=+(c[aN+8>>2]|0)}else if((az|0)==2){bh=+h[aN+8>>3]}else if((az|0)==3){bh=+uz(c[aN+8>>2]|0,0)}else{ay=3504;break L4581}if((c[S>>2]|0)==3){uu(c[T>>2]|0);c[S>>2]=1}aN=~~bh;c[10872]=aN;bi=c[13898]|0;if((aN|0)<1){ay=3509;break L4581}bf=bi;bg=c[8272]|0}L4779:do{if((bg|0)>(bf|0)){aN=c[1054]|0;if((a[aN+(bf*40&-1)|0]&1)==0){bj=bf;bk=bg;break}az=c[aN+(bf*40&-1)+36>>2]|0;aA=aN+(bf*40&-1)+32|0;ax=c[10036]|0;av=0;while(1){if((av|0)>=(az|0)){break}if((a[ax+((c[aA>>2]|0)+av|0)|0]|0)==(a[av+183584|0]|0)){av=av+1|0}else{bj=bf;bk=bg;break L4779}}if((av|0)!=1){bj=bf;bk=bg;break}aA=bf+1|0;c[13898]=aA;L4787:do{if((bg|0)>(aA|0)){if((a[aN+(aA*40&-1)|0]&1)==0){break}az=c[aN+(aA*40&-1)+36>>2]|0;aC=aN+(aA*40&-1)+32|0;a3=0;while(1){if((a3|0)>=(az|0)){break}if((a[ax+((c[aC>>2]|0)+a3|0)|0]|0)==(a[a3+183584|0]|0)){a3=a3+1|0}else{break L4787}}if((a3|0)==1){bj=aA;bk=bg;break L4779}}}while(0);aA=is(y)|0;ax=c[aA>>2]|0;if((ax|0)==1){bl=+(c[aA+8>>2]|0)}else if((ax|0)==2){bl=+h[aA+8>>3]}else if((ax|0)==3){bl=+uz(c[aA+8>>2]|0,0)}else{ay=3526;break L4581}if((c[U>>2]|0)==3){uu(c[V>>2]|0);c[U>>2]=1}aA=~~bl;c[10874]=aA;bm=c[13898]|0;if((aA|0)<1){ay=3531;break L4581}bj=bm;bk=c[8272]|0}else{bj=bf;bk=bg}}while(0);L4805:do{if((bk|0)>(bj|0)){aA=c[1054]|0;if((a[aA+(bj*40&-1)|0]&1)==0){bn=bj;bo=bk;break}ax=c[aA+(bj*40&-1)+36>>2]|0;aN=aA+(bj*40&-1)+32|0;av=c[10036]|0;aC=0;while(1){if((aC|0)>=(ax|0)){break}if((a[av+((c[aN>>2]|0)+aC|0)|0]|0)==(a[aC+183584|0]|0)){aC=aC+1|0}else{bn=bj;bo=bk;break L4805}}if((aC|0)!=1){bn=bj;bo=bk;break}aN=bj+1|0;c[13898]=aN;L4813:do{if((bk|0)>(aN|0)){if((a[aA+(aN*40&-1)|0]&1)==0){break}ax=c[aA+(aN*40&-1)+36>>2]|0;az=aA+(aN*40&-1)+32|0;a5=0;while(1){if((a5|0)>=(ax|0)){break}if((a[av+((c[az>>2]|0)+a5|0)|0]|0)==(a[a5+183584|0]|0)){a5=a5+1|0}else{break L4813}}if((a5|0)==1){bn=aN;bo=bk;break L4805}}}while(0);aN=is(x)|0;av=c[aN>>2]|0;if((av|0)==1){bp=+(c[aN+8>>2]|0)}else if((av|0)==2){bp=+h[aN+8>>3]}else if((av|0)==3){bp=+uz(c[aN+8>>2]|0,0)}else{ay=3548;break L4581}if((c[W>>2]|0)==3){uu(c[X>>2]|0);c[W>>2]=1}aN=~~bp;c[10806]=aN;bq=c[13898]|0;if((aN|0)<0){ay=3553;break L4581}bn=bq;bo=c[8272]|0}else{bn=bj;bo=bk}}while(0);L4831:do{if((bo|0)>(bn|0)){aN=c[1054]|0;if((a[aN+(bn*40&-1)|0]&1)==0){br=bn;bs=bo;break}av=c[aN+(bn*40&-1)+36>>2]|0;aA=aN+(bn*40&-1)+32|0;aC=c[10036]|0;az=0;while(1){if((az|0)>=(av|0)){break}if((a[aC+((c[aA>>2]|0)+az|0)|0]|0)==(a[az+183584|0]|0)){az=az+1|0}else{br=bn;bs=bo;break L4831}}if((az|0)!=1){br=bn;bs=bo;break}aA=bn+1|0;c[13898]=aA;L4839:do{if((bo|0)>(aA|0)){if((a[aN+(aA*40&-1)|0]&1)==0){break}av=c[aN+(aA*40&-1)+36>>2]|0;ax=aN+(aA*40&-1)+32|0;a3=0;while(1){if((a3|0)>=(av|0)){break}if((a[aC+((c[ax>>2]|0)+a3|0)|0]|0)==(a[a3+183584|0]|0)){a3=a3+1|0}else{break L4839}}if((a3|0)==1){br=aA;bs=bo;break L4831}}}while(0);aA=is(u)|0;aC=c[aA>>2]|0;if((aC|0)==1){bu=+(c[aA+8>>2]|0)}else if((aC|0)==2){bu=+h[aA+8>>3]}else if((aC|0)==3){bu=+uz(c[aA+8>>2]|0,0)}else{ay=3570;break L4581}if((c[Y>>2]|0)==3){uu(c[Z>>2]|0);c[Y>>2]=1}aA=~~bu;c[10808]=aA;bv=c[13898]|0;if((aA|0)<0){ay=3575;break L4581}br=bv;bs=c[8272]|0}else{br=bn;bs=bo}}while(0);L4857:do{if((bs|0)>(br|0)){aA=c[1054]|0;if((a[aA+(br*40&-1)|0]&1)==0){bw=br;bx=bs;break}aC=c[aA+(br*40&-1)+36>>2]|0;aN=aA+(br*40&-1)+32|0;az=c[10036]|0;ax=0;while(1){if((ax|0)>=(aC|0)){break}if((a[az+((c[aN>>2]|0)+ax|0)|0]|0)==(a[ax+183584|0]|0)){ax=ax+1|0}else{bw=br;bx=bs;break L4857}}if((ax|0)!=1){bw=br;bx=bs;break}aN=br+1|0;c[13898]=aN;L4865:do{if((bs|0)>(aN|0)){if((a[aA+(aN*40&-1)|0]&1)==0){break}aC=c[aA+(aN*40&-1)+36>>2]|0;av=aA+(aN*40&-1)+32|0;a5=0;while(1){if((a5|0)>=(aC|0)){break}if((a[az+((c[av>>2]|0)+a5|0)|0]|0)==(a[a5+183584|0]|0)){a5=a5+1|0}else{break L4865}}if((a5|0)==1){bw=aN;bx=bs;break L4857}}}while(0);aN=is(t)|0;az=c[aN>>2]|0;if((az|0)==1){by=+(c[aN+8>>2]|0)}else if((az|0)==2){by=+h[aN+8>>3]}else if((az|0)==3){by=+uz(c[aN+8>>2]|0,0)}else{ay=3592;break L4581}if((c[_>>2]|0)==3){uu(c[$>>2]|0);c[_>>2]=1}aN=~~by;c[8832]=aN;bz=c[13898]|0;if((aN|0)<(c[10806]|0)){ay=3597;break L4581}bw=bz;bx=c[8272]|0}else{bw=br;bx=bs}}while(0);if((bx|0)<=(bw|0)){aP=ap;aQ=ao;aS=an;aT=1;aU=al;break L4629}aN=c[1054]|0;if((a[aN+(bw*40&-1)|0]&1)==0){aP=ap;aQ=ao;aS=an;aT=1;aU=al;break L4629}az=c[aN+(bw*40&-1)+36>>2]|0;aA=aN+(bw*40&-1)+32|0;aN=c[10036]|0;ax=0;while(1){if((ax|0)>=(az|0)){break}if((a[aN+((c[aA>>2]|0)+ax|0)|0]|0)==(a[ax+183584|0]|0)){ax=ax+1|0}else{aP=ap;aQ=ao;aS=an;aT=1;aU=al;break L4629}}if((ax|0)!=1){aP=ap;aQ=ao;aS=an;aT=1;aU=al;break L4629}c[13898]=bw+1;aA=is(s)|0;aN=c[aA>>2]|0;if((aN|0)==1){bA=+(c[aA+8>>2]|0)}else if((aN|0)==2){bA=+h[aA+8>>3]}else if((aN|0)==3){bA=+uz(c[aA+8>>2]|0,0)}else{ay=3608;break L4581}if((c[ab>>2]|0)==3){uu(c[ac>>2]|0);c[ab>>2]=1}aA=~~bA;c[8834]=aA;if((aA|0)<(c[10808]|0)){ay=3612;break L4581}else{aP=ap;aQ=ao;aS=an;aT=1;aU=al;break L4629}}}while(0);if(!((au|0)>0&(at^1))){break L4579}aA=c[10036]|0;aN=0;az=0;av=aw;while(1){if((a[aN+89424|0]|0)==(a[aA+(aN+av|0)|0]|0)){bB=av;bC=az}else{if((aN|0)!=4){break}bB=av-1|0;bC=1}aC=aN+1|0;if((aC|0)<(bC+au|0)){aN=aC;az=bC;av=bB}else{ay=3619;break}}do{if((ay|0)==3619){ay=0;if((bC|0)==0){if((aN-3|0)>>>0>=2){break}}if(an){aH=aq;ay=4037;break L4581}c[13898]=aq+1;uB(55600,24544);c[11670]=232;w=121;a[55702]=w&255;w=w>>8;a[55703|0]=w&255;it();av=c[18070]|0;az=db(av,c[av>>2]<<5|8,105624)|0;c[18070]=0;c[60]=az;c[11670]=0;aP=ap;aQ=ao;aS=1;aT=am;aU=al;break L4629}}while(0);if(at){break L4579}L4914:do{if((au|0)>0){aN=c[10036]|0;az=0;av=0;aA=aw;while(1){if((a[az+89072|0]|0)==(a[aN+(az+aA|0)|0]|0)){bD=aA;bE=av}else{if((az|0)!=1){break L4914}bD=aA-1|0;bE=1}aC=az+1|0;if((aC|0)<(bE+au|0)){az=aC;av=bE;aA=bD}else{break}}if((bE|0)==0){if(!((az|0)==0|(az|0)==5)){break}}if(ao){aH=aq;ay=4038;break L4581}if((a[47856]&1)==0){bF=aq;bG=ar}else{eA();bF=c[13898]|0;bG=c[8272]|0}L4930:do{if((bF|0)<(bG|0)){aA=c[1054]|0;L4932:do{if((a[aA+(bF*40&-1)|0]&1)!=0){av=c[aA+(bF*40&-1)+36>>2]|0;aN=aA+(bF*40&-1)+32|0;ax=c[10036]|0;aC=0;while(1){if((aC|0)>=(av|0)){break}if((a[ax+((c[aN>>2]|0)+aC|0)|0]|0)==(a[aC+103664|0]|0)){aC=aC+1|0}else{break L4932}}if((aC|0)==1){bH=bF;bI=bG;break L4930}}}while(0);aN=bF+1|0;c[13898]=aN;if((a[aA+(aN*40&-1)|0]&1)==0){bH=aN;bI=bG;break}ax=c[aA+(aN*40&-1)+32>>2]|0;av=c[10036]|0;a5=a[av+ax|0]|0;if(!((a5<<24>>24|0)==39|(a5<<24>>24|0)==34)){bH=aN;bI=bG;break}L4941:do{if((aN|0)<(bG|0)){a5=c[aA+(aN*40&-1)+36>>2]|0;a3=0;while(1){if((a3|0)>=(a5|0)){ay=3647;break}if((a[av+(a3+ax|0)|0]|0)==(a[a3+103664|0]|0)){a3=a3+1|0}else{break}}if((ay|0)==3647){ay=0;if((a3|0)==1){ay=3650;break}}a[14176]=1;is(r);a[14176]=0;if((c[K>>2]|0)!=3){c[13898]=aN;ay=3650;break}a5=c[L>>2]|0;c[11930]=a5;if((a5|0)==0){bJ=0;break}else{bK=0;bM=a5}L4952:while(1){aC=a8(bM|0,37)|0;if((aC|0)==0){break}else{bN=aC}while(1){aC=bN+1|0;a2=a[aC]|0;if((aZ(140272,a2|0,15)|0)!=0){bN=aC;continue}if((a2|0)==108){break}else if(!((a2|0)==42|(a2|0)==37)){bJ=a5;break L4941}a2=a8(bN+2|0,37)|0;if((a2|0)==0){break L4952}else{bN=a2}}a2=bN+2|0;if((aZ(140120,a[a2]|0|0,7)|0)==0){bJ=a5;break L4941}else{bK=bK+1|0;bM=a2}}if((bK-1|0)>>>0<7){aP=ap;aQ=1;aS=an;aT=am;aU=al;break L4629}else{bJ=a5}}else{ay=3650}}while(0);if((ay|0)==3650){ay=0;c[11930]=0;bJ=0}uu(bJ);c[11930]=0;c[13898]=aN;bH=aN;bI=c[8272]|0}else{bH=bF;bI=bG}}while(0);L4964:do{if((bH|0)<(bI|0)){az=c[1054]|0;L4966:do{if((a[az+(bH*40&-1)|0]&1)==0){bO=0;bQ=bH;bR=bI;bS=az}else{ax=c[az+(bH*40&-1)+36>>2]|0;av=az+(bH*40&-1)+32|0;aA=c[10036]|0;a3=0;while(1){if((a3|0)>=(ax|0)){break}if((a[aA+((c[av>>2]|0)+a3|0)|0]|0)==(a[a3+103664|0]|0)){a3=a3+1|0}else{bO=0;bQ=bH;bR=bI;bS=az;break L4966}}if((a3|0)==1){bT=0;bU=bH;bV=bI;break L4964}else{bO=0;bQ=bH;bR=bI;bS=az}}}while(0);while(1){az=c[11870]|0;if((az|0)>=(f|0)){ay=3666;break L4581}aN=(bR|0)>(bQ|0);av=a[bS+(bQ*40&-1)|0]|0;L4975:do{if(aN){aA=(av&1)==0;if(aA){ay=3680;break}ax=c[bS+(bQ*40&-1)+36>>2]|0;a5=bS+(bQ*40&-1)+32|0;a2=c[10036]|0;aC=0;while(1){if((aC|0)>=(ax|0)){ay=3672;break}if((a[a2+((c[a5>>2]|0)+aC|0)|0]|0)==(a[aC+183584|0]|0)){aC=aC+1|0}else{break}}do{if((ay|0)==3672){ay=0;if((aC|0)!=1){break}c[1048+(az*12&-1)>>2]=az;c[11870]=az+1;bW=(az|0)>(bO|0)?az:bO;break L4975}}while(0);if(aA){ay=3680;break}aC=c[bS+(bQ*40&-1)+36>>2]|0;a5=bS+(bQ*40&-1)+32|0;a2=c[10036]|0;ax=0;while(1){if((ax|0)>=(aC|0)){break}if((a[a2+((c[a5>>2]|0)+ax|0)|0]|0)==(a[ax+199040|0]|0)){ax=ax+1|0}else{ay=3680;break L4975}}if((ax|0)!=1){ay=3680;break}a[43464]=0;c[11670]=0;c[18068]=-99;it();a5=c[18070]|0;a2=db(a5,c[a5>>2]<<5|8,105624)|0;c[18070]=0;a5=c[11870]|0;c[1056+(a5*12&-1)>>2]=a2;a2=c[18068]|0;c[11870]=a5+1;c[1048+(a5*12&-1)>>2]=a2;bW=(bO|0)<(a2|0)?a2:bO}else{ay=3680}}while(0);L4991:do{if((ay|0)==3680){ay=0;az=c[bS+(bQ*40&-1)+36>>2]|0;a3=c[bS+(bQ*40&-1)+32>>2]|0;a2=(av&1)==0;L4993:do{if((az|0)>0&(a2^1)){a5=c[10036]|0;aC=0;aA=0;a4=a3;while(1){if((a[aC+141928|0]|0)==(a[a5+(aC+a4|0)|0]|0)){bX=a4;bY=aA}else{if((aC|0)!=4){break}bX=a4-1|0;bY=1}aG=aC+1|0;if((aG|0)<(bY+az|0)){aC=aG;aA=bY;a4=bX}else{ay=3686;break}}do{if((ay|0)==3686){ay=0;if((bY|0)==0){if(!((aC|0)==3|(aC|0)==10)){break}}eY(3);bW=bO;break L4991}}while(0);if(a2){break}else{bZ=0;b_=0;b$=a3}while(1){if((a[bZ+141800|0]|0)==(a[a5+(bZ+b$|0)|0]|0)){b0=b$;b1=b_}else{if((bZ|0)!=5){b2=0;b3=0;b4=a3;break}b0=b$-1|0;b1=1}aC=bZ+1|0;if((aC|0)<(b1+az|0)){bZ=aC;b_=b1;b$=b0}else{ay=3694;break}}do{if((ay|0)==3694){ay=0;if((b1|0)==0){if(!((bZ|0)==4|(bZ|0)==11)){b2=0;b3=0;b4=a3;break}}eY(4);bW=bO;break L4991}}while(0);while(1){if((a[b2+141632|0]|0)==(a[a5+(b2+b4|0)|0]|0)){b5=b4;b6=b3}else{if((b2|0)!=4){b7=0;b8=0;b9=a3;break}b5=b4-1|0;b6=1}aC=b2+1|0;if((aC|0)<(b6+az|0)){b2=aC;b3=b6;b4=b5}else{ay=3701;break}}do{if((ay|0)==3701){ay=0;if((b6|0)==0){if(!((b2|0)==3|(b2|0)==10)){b7=0;b8=0;b9=a3;break}}eY(5);bW=bO;break L4991}}while(0);while(1){if((a[b7+141416|0]|0)==(a[a5+(b7+b9|0)|0]|0)){ca=b9;cb=b8}else{if((b7|0)!=5){cc=0;cd=0;ce=a3;break}ca=b9-1|0;cb=1}aC=b7+1|0;if((aC|0)<(cb+az|0)){b7=aC;b8=cb;b9=ca}else{ay=3708;break}}do{if((ay|0)==3708){ay=0;if((cb|0)==0){if(!((b7|0)==4|(b7|0)==11)){cc=0;cd=0;ce=a3;break}}eY(6);bW=bO;break L4991}}while(0);while(1){if((a[cc+141296|0]|0)==(a[a5+(cc+ce|0)|0]|0)){cf=ce;cg=cd}else{if((cc|0)!=4){ch=0;ci=0;cj=a3;break}cf=ce-1|0;cg=1}aC=cc+1|0;if((aC|0)<(cg+az|0)){cc=aC;cd=cg;ce=cf}else{ay=3715;break}}do{if((ay|0)==3715){ay=0;if((cg|0)==0){if(!((cc|0)==3|(cc|0)==10)){ch=0;ci=0;cj=a3;break}}eY(7);bW=bO;break L4991}}while(0);while(1){if((a[ch+141096|0]|0)==(a[a5+(ch+cj|0)|0]|0)){ck=cj;cl=ci}else{if((ch|0)!=5){cm=0;break}ck=cj-1|0;cl=1}aC=ch+1|0;if((aC|0)<(cl+az|0)){ch=aC;ci=cl;cj=ck}else{ay=3722;break}}do{if((ay|0)==3722){ay=0;if((cl|0)==0){if(!((ch|0)==4|(ch|0)==11)){cm=0;break}}eY(8);bW=bO;break L4991}}while(0);do{if((a[cm+128552|0]|0)!=(a[a5+(cm+a3|0)|0]|0)){break L4993}cm=cm+1|0;}while((cm|0)<(az|0));if((cm|0)!=3){break}eY(2);bW=bO;break L4991}}while(0);L5068:do{if(aN){L5070:do{if(!a2){ax=c[10036]|0;a5=0;while(1){if((a5|0)>=(az|0)){break}if((a[ax+(a5+a3|0)|0]|0)==(a[a5+103664|0]|0)){a5=a5+1|0}else{break L5070}}if((a5|0)==1){break L5068}}}while(0);a[14176]=1;is(p);a[14176]=0;if((c[M>>2]|0)!=3){c[13898]=bQ;break}ax=c[N>>2]|0;if((ax|0)==0){break}aC=ut(68)|0;if((aC|0)==0){gk();a4=ut(68)|0;if((a4|0)==0){ay=3740;break L4581}else{cn=a4}}else{cn=aC}c[cn>>2]=2;c[cn+8>>2]=1;c[cn+16>>2]=3;c[cn+24>>2]=ax;c[cn+40>>2]=42;c[cn+48>>2]=0;aC=c[11870]|0;c[1056+(aC*12&-1)>>2]=cn;a4=aC+1|0;c[11870]=a4;c[1048+(aC*12&-1)>>2]=-99;a[32928]=1;a[43464]=0;if((a4|0)!=2){bW=bO;break L4991}uu(c[11902]|0);c[11902]=bP(ax|0)|0;bW=bO;break L4991}}while(0);a3=is(l)|0;az=c[a3>>2]|0;if((az|0)==1){co=+(c[a3+8>>2]|0)}else if((az|0)==2){co=+h[a3+8>>3]}else if((az|0)==3){co=+uz(c[a3+8>>2]|0,0)}else{ay=3747;break L4581}if((c[O>>2]|0)==3){uu(c[P>>2]|0);c[O>>2]=1}a3=~~co;if((a3|0)<-2){ay=3751;break L4581}az=c[11870]|0;c[11870]=az+1;c[1048+(az*12&-1)>>2]=a3;bW=(a3|0)>(bO|0)?a3:bO}}while(0);aN=c[13898]|0;av=c[8272]|0;if((av|0)<=(aN|0)){bT=bW;bU=aN;bV=av;break L4964}a3=c[1054]|0;if((a[a3+(aN*40&-1)|0]&1)==0){bT=bW;bU=aN;bV=av;break L4964}az=c[a3+(aN*40&-1)+36>>2]|0;a2=a3+(aN*40&-1)+32|0;ax=c[10036]|0;a4=0;while(1){if((a4|0)>=(az|0)){break}if((a[ax+((c[a2>>2]|0)+a4|0)|0]|0)==(a[a4+183584|0]|0)){a4=a4+1|0}else{bT=bW;bU=aN;bV=av;break L4964}}if((a4|0)!=1){bT=bW;bU=aN;bV=av;break L4964}a2=aN+1|0;c[13898]=a2;if((a2|0)==0){bT=bW;bU=0;bV=av;break}else{bO=bW;bQ=a2;bR=av;bS=a3}}}else{bT=0;bU=bH;bV=bI}}while(0);if((a[47856]&1)==0){cq=bU;cr=bV}else{eQ(bT);cq=c[13898]|0;cr=c[8272]|0}if((cq|0)>=(cr|0)){aP=ap;aQ=1;aS=an;aT=am;aU=al;break L4629}a2=c[1054]|0;if((a[a2+(cq*40&-1)|0]&1)==0){aP=ap;aQ=1;aS=an;aT=am;aU=al;break L4629}ax=c[a2+(cq*40&-1)+36>>2]|0;az=a2+(cq*40&-1)+32|0;a2=c[10036]|0;aC=0;while(1){if((aC|0)>=(ax|0)){ay=3767;break}aA=c[az>>2]|0;if((a[a2+(aA+aC|0)|0]|0)==(a[aC+103664|0]|0)){aC=aC+1|0}else{cs=aA;break}}if((ay|0)==3767){ay=0;if((aC|0)==1){aP=ap;aQ=1;aS=an;aT=am;aU=al;break L4629}cs=c[az>>2]|0}aA=a[a2+cs|0]|0;if((aA<<24>>24|0)==39|(aA<<24>>24|0)==34){ct=0}else{aP=ap;aQ=1;aS=an;aT=am;aU=al;break L4629}while(1){if((ct|0)>=(ax|0)){ay=3772;break}if((a[a2+(ct+cs|0)|0]|0)==(a[ct+103664|0]|0)){ct=ct+1|0}else{break}}if((ay|0)==3772){ay=0;if((ct|0)==1){break L4581}}a[14176]=1;is(q);a[14176]=0;if((c[Q>>2]|0)!=3){ay=3774;break L4581}a2=c[R>>2]|0;c[11930]=a2;if((a2|0)==0){ay=4050;break L4581}else{cv=0;cw=a2}L5123:while(1){a2=a8(cw|0,37)|0;if((a2|0)==0){break}else{cx=a2}while(1){a2=cx+1|0;ax=a[a2]|0;if((aZ(140272,ax|0,15)|0)!=0){cx=a2;continue}if((ax|0)==108){break}else if(!((ax|0)==42|(ax|0)==37)){ay=4051;break L4581}ax=a8(cx+2|0,37)|0;if((ax|0)==0){break L5123}else{cx=ax}}ax=cx+2|0;if((aZ(140120,a[ax]|0|0,7)|0)==0){ay=4052;break L4581}else{cv=cv+1|0;cw=ax}}if((cv-1|0)>>>0<7){aP=ap;aQ=1;aS=an;aT=am;aU=al;break L4629}else{ay=4053;break L4581}}}while(0);if(!((au|0)>0&(at^1))){break L4579}ax=c[10036]|0;a2=0;while(1){if((a[a2+88368|0]|0)!=(a[ax+(a2+aw|0)|0]|0)){break}cy=a2+1|0;if((cy|0)<(au|0)){a2=cy}else{ay=3788;break}}do{if((ay|0)==3788){ay=0;if((cy|0)!=8){break}c[13898]=aq+1;a[872]=1;aP=ap;aQ=ao;aS=an;aT=am;aU=al;break L4629}}while(0);if(!((au|0)>0&(at^1))){break L4579}a2=c[10036]|0;ax=0;az=0;aC=aw;while(1){if((a[ax+87120|0]|0)==(a[a2+(ax+aC|0)|0]|0)){cz=aC;cA=az}else{if((ax|0)!=6){break L4579}cz=aC-1|0;cA=1}aA=ax+1|0;if((aA|0)<(cA+au|0)){ax=aA;az=cA;aC=cz}else{break}}if((cA|0)==0){if(!((ax|0)==5|(ax|0)==11)){break L4579}}c[13898]=aq+1;a[J]=1;aP=ap;aQ=ao;aS=an;aT=am;aU=al}}while(0);au=c[13898]|0;aw=c[8272]|0;if((au|0)<(aw|0)){al=aU;am=aT;an=aS;ao=aQ;ap=aP;aq=au;ar=aw}else{break L4579}}if((ay|0)==3462){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3509){uf(bi,139088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3388){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3478){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3404){uf(aX,138160,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3482){uf(c[13898]|0,139280,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3526){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3531){uf(bm,139088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3575){uf(bv,138968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3442){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3381){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3426){uf(aq,139624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3570){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3467){uf(ba,139480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3504){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3548){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3553){uf(bq,138968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3592){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3597){uf(bz,138672,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3608){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3612){uf(c[13898]|0,138528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3666){uf(bQ,82688,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3740){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=179864,v)|0);return 0}else if((ay|0)==3747){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3751){uf(c[13898]|0,140816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3774){c[13898]=cq}else if((ay|0)==4033){uf(aH,86056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4034){uf(aH,86056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4035){uf(aH,86056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4036){uf(aH,86056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4037){uf(aH,86056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4038){uf(aH,86056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4050){cB=c[13898]|0;uf(cB,140584,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4051){cB=c[13898]|0;uf(cB,140584,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4052){cB=c[13898]|0;uf(cB,140584,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4053){cB=c[13898]|0;uf(cB,140584,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}c[11930]=0;cB=c[13898]|0;uf(cB,140584,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);L5192:do{if((c[9056]|0)==2){if((c[11870]|0)==1){c[13488]=c[262];break}do{if((j|0)!=0){if((c[j+8>>2]|0)!=3){break}c[13488]=c[268];break L5192}}while(0);c[13488]=c[265]}}while(0);c[7638]=-1;c[8800]=0;c[11854]=0;c[11852]=0;c[11850]=0;if((c[8716]|0)>>>0<160){c[8716]=160;j=ut(160)|0;do{if((j|0)==0){gk();aq=ut(160)|0;if((aq|0)!=0){cC=aq;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=84816,v)|0);return 0}else{cC=j}}while(0);c[8802]=cC}cC=c[11932]|0;j=a[cC]|0;L5209:do{if((j<<24>>24|0)==60){do{if((uA(cC|0)|0)>>>0>1){if((a[cC+1|0]|0)!=38){if((j<<24>>24|0)==60){break}else if((j<<24>>24|0)==45){ay=3830;break L5209}else if((j<<24>>24|0)==43){ay=3836;break L5209}else{ay=3840;break L5209}}aq=aE(cC+2|0,F|0,10)|0;c[12894]=aq;cB=c[F>>2]|0;if((a[cB]|0)!=0|(aq|0)<0){uf(H,84216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((cB|0)==((c[11932]|0)+2|0)){uf(H,84216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((aq|0)==(aR(c[o>>2]|0)|0)){uf(H,83392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}aq=c[12894]|0;if((aq|0)==(aR(c[n>>2]|0)|0)){uf(H,83392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}aq=c[12894]|0;if((aq|0)==(aR(c[m>>2]|0)|0)){uf(H,83392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}aq=bL(c[12894]|0,193632)|0;c[12892]=aq;if((aq|0)!=0){break L5209}uf(H,80992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}aq=bt(cC+1|0,193632)|0;c[12892]=aq;if((aq|0)==0){uj(H,79632,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{a[47440]=1;break}}else if((j<<24>>24|0)==45){ay=3830}else if((j<<24>>24|0)==43){ay=3836}else{ay=3840}}while(0);do{if((ay|0)==3830){if((uA(cC|0)|0)!=1){ay=3840;break}a[30664]=1;a[872]=1;j=c[8804]|0;if((j|0)==0){c[12892]=0;ay=3834}else{F=c[j>>2]|0;c[12892]=F;if((F|0)==0){ay=3834}}if((ay|0)==3834){c[12892]=c[o>>2]}a[34104]=1}else if((ay|0)==3836){F=uA(cC|0)|0;if((F|0)==1){c[11854]=1;break}if(!((a[cC+1|0]|0)==43&(F|0)==2)){break}c[11854]=2}}while(0);do{if((ay|0)==3840){iQ(47728);do{if((cu(c[11932]|0,G|0)|0)>-1){if((c[G+8>>2]&61440|0)!=16384){break}uj(H,78896,(v=i,i=i+8|0,c[v>>2]=c[11932],v)|0);return 0}}while(0);cC=hA(c[11932]|0,(a[47856]&1)!=0?78360:193632)|0;c[12892]=cC;if((cC|0)!=0){break}uh(-1,77960,(v=i,i=i+8|0,c[v>>2]=c[11932],v)|0);a[47736]=1;cD=-1;i=k;return cD|0}}while(0);do{if((a[47544]&1)!=0){H=c[12892]|0;if((a[47856]&1)!=0){G=~~+eE(H);if((G|0)==0){uf(-1,146120,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((G|0)>1e8){uf(-1,145960,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}co=+eE(H);h[5944]=co;if((G|0)>1){cC=(G<<2)-8|0;cp(H|0,cC|0,1);cE=+eE(H)}else{cE=co}h[5946]=cE;h[5945]=+eE(H);cp(H|0,0,2);cC=(bc(H|0)|0)>>>2;F=G+1|0;j=(cC|0)/(F|0)&-1;if((aa(j,F)|0)==(cC|0)){cC=G<<2^-4;cp(H|0,cC|0,2);h[5947]=+eE(H);c[(c[12172]|0)+140>>2]=G;c[(c[12172]|0)+144>>2]=j;cp(H|0,0,0);break}else{uf(-1,145728,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}H=c[11940]|0;if((H|0)!=0){uu(H)}c[12170]=0;c[12220]=0;c[12222]=0;c[12224]=0;c[11876]=0;H=c[11950]|0;if((c[11884]|0)<1){j=db(H,12,215112)|0;c[11950]=j;c[11884]=1;cF=j}else{cF=H}c[cF>>2]=0;H=c[12172]|0;j=c[11864]|0;if((j|0)>0){G=0;cC=j;while(1){j=H+(G*232&-1)+224|0;F=c[j>>2]|0;if((F|0)==0){cG=cC}else{uu(F);c[j>>2]=0;cG=c[11864]|0}j=G+1|0;if((j|0)<(cG|0)){G=j;cC=cG}else{break}}}c[11864]=0;cC=c[11862]|0;if((cC|0)==0){c[12204]=c[12200];c[12208]=0;G=c[12172]|0;do{if((c[11880]|0)<1){H=db(G,232,216568)|0;j=H;c[12172]=j;if((H|0)==0){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{c[11880]=1;cH=c[11864]|0;cI=j;break}}else{cH=0;cI=G}}while(0);uD(cI+(cH*232&-1)|0,48440,232);G=(c[11864]|0)+1|0;c[11864]=G;cJ=G;cK=cI}else{c[12204]=c[12202];c[12208]=c[12206];G=c[12172]|0;do{if((cC|0)>(c[11880]|0)){j=db(G,cC*232&-1,216568)|0;H=j;c[12172]=H;if((j|0)==0){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{c[11880]=cC;cL=c[11864]|0;cM=H;break}}else{cL=0;cM=G}}while(0);if((cC|0)>0){G=0;H=cL;while(1){uD(cM+(H*232&-1)|0,48440,232);j=(c[11864]|0)+1|0;c[11864]=j;F=G+1|0;if((F|0)<(cC|0)){G=F;H=j}else{cN=j;break}}}else{cN=cL}uD(cM|0,c[12168]|0,cN*232&-1);cJ=c[11864]|0;cK=c[12172]|0}if((cJ|0)>0){H=0;G=cJ;while(1){cC=cK+(H*232&-1)+224|0;j=c[cC>>2]|0;if((j|0)==0){cO=G}else{uu(j);c[cC>>2]=0;cO=c[11864]|0}cC=H+1|0;if((cC|0)<(cO|0)){H=cC;G=cO}else{break}}}c[11864]=0;G=0;H=0;cC=0;j=0;F=0;aq=0;L5319:while(1){cB=cC;aH=j;cq=F;bQ=aq;L5321:while(1){cP=cB;while(1){bz=eV()|0;if((bz|0)==0){ay=3886;break L5321}else{cQ=bz}while(1){if((aM(d[cQ]|0|0)|0)==0){break}else{cQ=cQ+1|0}}cR=a[cQ]|0;if(cR<<24>>24!=0){if((a8(c[11948]|0,cR<<24>>24|0)|0)==0){break}}if((cP|0)==0){cP=0}else{ay=3891;break L5321}}if(a[34104]|0){if((cR<<24>>24|0)==101|(cR<<24>>24|0)==69){ay=3894;break}}cS=eX(cQ)|0;if((cS|0)==0){ay=3911;break}if(!((cq|0)==0|(cS|0)==(cq|0))){ay=3897;break L5319}cT=bQ+1|0;if((cT|0)>(aH|0)){ax=aH<<1;bz=(ax|0)>1?ax:1;cU=db(cP,aa(bz<<2,cS),145264)|0;cV=bz}else{cU=cP;cV=aH}if((cS|0)>0){cW=H;cX=G;cY=0;ay=3903;break}else{cB=cU;aH=cV;cq=cS;bQ=cT}}if((ay|0)==3886){ay=0;a[47736]=1;ay=3911}else if((ay|0)==3891){ay=0;c[11940]=cP}else if((ay|0)==3894){ay=0;a[47736]=1;ay=3911}else if((ay|0)==3903){while(1){ay=0;aH=c[11952]|0;do{if((cY|0)<(c[10806]|0)){if((c[aH+(cY*24&-1)+8>>2]|0)==1){ay=3906;break}g[cU+(cX<<2)>>2]=0.0}else{ay=3906}}while(0);if((ay|0)==3906){ay=0;g[cU+(cX<<2)>>2]=+h[aH+(cY*24&-1)>>3]}cB=cX+1|0;do{if((c[(c[11952]|0)+(cY*24&-1)+8>>2]|0)==1){cZ=cW}else{bz=cW+1|0;if((cW|0)!=0){cZ=bz;break}uh(-1,145016,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cZ=bz}}while(0);aH=cY+1|0;if((aH|0)<(cS|0)){cW=cZ;cX=cB;cY=aH;ay=3903}else{break}}G=cS+G|0;H=cZ;cC=cU;j=cV;F=cS;aq=cT;continue}if((ay|0)==3911){ay=0;c[11940]=cP;if((cP|0)==0){ay=3919;break}}aH=c[11864]|0;if((a[47464]&1)==0){c_=cq;c$=bQ}else{c_=cq-1|0;c$=bQ-1|0}bz=aH+1|0;ax=c[12172]|0;if((bz|0)>(c[11880]|0)){bq=db(ax,bz*232&-1,216568)|0;ba=bq;c[12172]=ba;if((bq|0)==0){ay=3916;break}c[11880]=bz;c0=c[11864]|0;c1=c[11940]|0;c2=ba}else{c0=aH;c1=cP;c2=ax}uD(c2+(c0*232&-1)|0,48440,232);c[11864]=(c[11864]|0)+1;c[c2+(aH*232&-1)+224>>2]=c1;c[11940]=0;c[(c[12172]|0)+(aH*232&-1)+140>>2]=c_;c[(c[12172]|0)+(aH*232&-1)+144>>2]=c$;c[(c[12172]|0)+(aH*232&-1)+148>>2]=0;c[12208]=0;G=0;H=0;cC=0;j=0;F=0;aq=0}if((ay|0)==3897){if((cP|0)==0){uf(-1,145560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}uu(cP);uf(-1,145560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3916){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==3919){a[47736]=0;break}}}while(0);cP=a[47856]|0;c$=a[47544]|0;do{if((cP&1)==0){if((c$&1)!=0){ay=3922;break}a[47392]=0}else{ay=3922}}while(0);L5379:do{if((ay|0)==3922){a[47392]=1;c_=E;c1=(c$&1)==0;if((c$&cP&1)!=0){a[47464]=1}c2=c[13898]|0;c0=c[8272]|0;L5384:do{if((c2|0)<(c0|0)){cT=c[1054]|0;cS=c2;while(1){L5388:do{if((a[cT+(cS*40&-1)|0]&1)!=0){cV=c[cT+(cS*40&-1)+36>>2]|0;cU=cT+(cS*40&-1)+32|0;cZ=c[10036]|0;cY=0;while(1){if((cY|0)>=(cV|0)){ay=3930;break}if((a[cZ+((c[cU>>2]|0)+cY|0)|0]|0)==(a[cY+103664|0]|0)){cY=cY+1|0}else{break}}if((ay|0)==3930){ay=0;if((cY|0)==1){c3=cS;break L5384}}if((cV|0)<=0){break}cX=0;cW=0;cQ=c[cU>>2]|0;while(1){if((a[cX+196448|0]|0)==(a[cZ+(cX+cQ|0)|0]|0)){c4=cQ;c5=cW}else{if((cX|0)!=1){break L5388}c4=cQ-1|0;c5=1}cR=cX+1|0;if((cR|0)<(c5+cV|0)){cX=cR;cW=c5;cQ=c4}else{break}}if((c5|0)!=0){c3=cS;break L5384}if((cX|0)==0|(cX|0)==4){c3=cS;break L5384}}}while(0);cB=cS+1|0;c[13898]=cB;if((cB|0)<(c0|0)){cS=cB}else{c3=cB;break}}}else{c3=c2}}while(0);L5407:do{if((c3|0)<(c0|0)){cS=c[1054]|0;L5409:do{if((a[cS+(c3*40&-1)|0]&1)==0){c6=c[10036]|0}else{cT=c[cS+(c3*40&-1)+36>>2]|0;bQ=cS+(c3*40&-1)+32|0;cq=c[10036]|0;cB=0;while(1){if((cB|0)>=(cT|0)){break}if((a[cq+((c[bQ>>2]|0)+cB|0)|0]|0)==(a[cB+103664|0]|0)){cB=cB+1|0}else{c6=cq;break L5409}}if((cB|0)==1){c7=1;break L5407}else{c6=cq}}}while(0);bQ=c3+1|0;c[13898]=bQ;cT=c[cS+(bQ*40&-1)+36>>2]|0;cQ=c[cS+(bQ*40&-1)+32>>2]|0;cW=(a[cS+(bQ*40&-1)|0]&1)==0;bQ=(cT|0)>0;cV=30672;cZ=116448;L5417:while(1){L5419:do{if(!cW){if(bQ){cU=0;cY=0;cR=cQ;while(1){cO=a[cZ+cU|0]|0;if(cO<<24>>24==(a[c6+(cU+cR|0)|0]|0)){c8=cR;c9=cY}else{if(cO<<24>>24!=36){break L5419}c8=cR-1|0;c9=1}da=cU+1|0;if((da|0)<(c9+cT|0)){cU=da;cY=c9;cR=c8}else{break}}if((c9|0)==0){dc=da}else{dd=cV;break L5417}}else{dc=0}cR=a[cZ+dc|0]|0;if((cR<<24>>24|0)==36|(cR<<24>>24|0)==0){dd=cV;break L5417}}}while(0);cq=cV+8|0;cB=c[cq>>2]|0;if((cB|0)==0){dd=cq;break}else{cV=cq;cZ=cB}}cZ=c[dd+4>>2]|0;cV=c3+2|0;c[13898]=cV;if((cZ|0)!=-1){c7=cZ;break}uf(cV,143640,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{c7=1}}while(0);c[13898]=c2;c0=0;while(1){if(c0>>>0>=31){break}if((c[48912+(c0<<3)>>2]|0)==(c7|0)){break}else{c0=c0+1|0}}if((c0|0)==31){uf(c2,148528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}do{if(!c1){if((c[11876]|0)>3){uf(-1,148288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{eQ(3);break}}}while(0);do{if((c[11870]|0)==0){if((a[47544]&1)!=0){c1=b[48918+(c0<<3)>>1]|0;if((c1<<16>>16|0)==2){if((b[48916+(c0<<3)>>1]|0)==1){c[11870]=3;break}else{uf(-1,147704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}else if((c1<<16>>16|0)==1){if((b[48916+(c0<<3)>>1]|0)!=1){uf(-1,147704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((c[11856]|0)==2){c[11870]=3;break}else{c[11870]=2;c[265]=3;break}}else{uf(-1,147704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}c1=b[48916+(c0<<3)>>1]|0;cV=c1<<16>>16;if(c1<<16>>16==0){uf(c2,148528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}do{if((c[11864]|0)==0){ay=3976}else{if((a[(c[12172]|0)+124|0]&1)==0){ay=3976;break}if((b[48918+(c0<<3)>>1]|0)!=0){de=cV;break}uf(c2,148080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);do{if((ay|0)==3976){c1=(b[48918+(c0<<3)>>1]|0)+cV|0;if((c[11856]|0)!=2){de=c1;break}de=c1+1|0}}while(0);if((de|0)<8){c[11870]=de;eQ(de);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=147920,v)|0);return 0}}}while(0);c2=c[11864]|0;if((c2|0)==0){break}cV=c[12172]|0;if((a[cV+124|0]&1)==0){break}if((a[47544]&1)!=0){break}uD(c_|0,1048,84);if((c[cV>>2]|0)==0){if((c[cV+140>>2]|0)==0){df=0}else{ay=3994}}else{ay=3994}do{if((ay|0)==3994){if((c[cV+4>>2]|0)==0){if((c[cV+144>>2]|0)==0){df=1;break}}if((c[cV+8>>2]|0)==0){if((c[cV+148>>2]|0)==0){df=2;break}}df=3}}while(0);c1=c[11870]|0;cZ=c1+df|0;if((cZ|0)>6){uf(-1,146856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}uD(1048+(df*12&-1)|0,c_|0,c1*12&-1);if((df|0)!=0){c1=0;do{c[1048+(c1*12&-1)>>2]=c[cV+112+(c1<<2)>>2];c[1056+(c1*12&-1)>>2]=0;c1=c1+1|0;}while((c1|0)<(df|0))}c[11870]=cZ;if(!((c[11856]|0)==2&(c2|0)>0)){break}c1=48918+(c0<<3)|0;c_=0;cT=cZ;L5502:while(1){do{if((c[cV+(c_*232&-1)+8>>2]|0)==0){if((c[cV+(c_*232&-1)+148>>2]|0)!=0){dg=cT;break}cQ=b[c1>>1]|0;if(cQ<<16>>16>2){ay=4004;break L5502}do{if((c[cV+(c_*232&-1)+4>>2]|0)==0){if((c[cV+(c_*232&-1)+144>>2]|0)!=0){dh=cT;break}if(cQ<<16>>16>1){ay=4008;break L5502}if((cT|0)>6){ay=4011;break L5502}if((cT|0)>1){bQ=cT;while(1){cW=bQ-1|0;cS=1048+(bQ*12&-1)|0;cB=1048+(cW*12&-1)|0;c[cS>>2]=c[cB>>2];c[cS+4>>2]=c[cB+4>>2];c[cS+8>>2]=c[cB+8>>2];if((cW|0)>1){bQ=cW}else{break}}}c[265]=-4;c[267]=0;bQ=cT+1|0;c[11870]=bQ;dh=bQ}else{dh=cT}}while(0);if((dh|0)>6){ay=4016;break L5502}if((dh|0)>2){cQ=dh;while(1){bQ=cQ-1|0;cW=1048+(cQ*12&-1)|0;cB=1048+(bQ*12&-1)|0;c[cW>>2]=c[cB>>2];c[cW+4>>2]=c[cB+4>>2];c[cW+8>>2]=c[cB+8>>2];if((bQ|0)>2){cQ=bQ}else{break}}}c[268]=-5;c[270]=0;cQ=dh+1|0;c[11870]=cQ;dg=cQ}else{dg=cT}}while(0);cQ=c_+1|0;if((cQ|0)<(c2|0)){c_=cQ;cT=dg}else{break L5379}}if((ay|0)==4004){uf(-1,147376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4008){uf(-1,147096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4011){uf(-1,146856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((ay|0)==4016){uf(-1,146856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}}while(0);do{if((a[47544]&1)==0){if((c[11864]|0)!=1){di=0;break}ay=c[12172]|0;if((c[ay+4>>2]|0)>0){di=1;break}di=(c[ay+144>>2]|0)>0&1}else{di=1}}while(0);a[47584]=di;cD=c[11870]|0;i=k;return cD|0}function ez(){var b=0,d=0,e=0;b=i;c[11874]=0;if((c[12892]|0)==0){i=b;return}d=c[60]|0;if((d|0)!=0){e1(d);c[60]=0}d=c[264]|0;if((d|0)!=0){e1(d);c[264]=0}d=c[267]|0;if((d|0)!=0){e1(d);c[267]=0}d=c[270]|0;if((d|0)!=0){e1(d);c[270]=0}d=c[273]|0;if((d|0)!=0){e1(d);c[273]=0}d=c[276]|0;if((d|0)!=0){e1(d);c[276]=0}d=c[279]|0;if((d|0)!=0){e1(d);c[279]=0}d=c[282]|0;if((d|0)!=0){e1(d);c[282]=0}if((a[47584]&1)!=0&(c[11864]|0)>0){d=0;do{uu(c[(c[12172]|0)+(d*232&-1)+224>>2]|0);c[(c[12172]|0)+(d*232&-1)+224>>2]=0;d=d+1|0;}while((d|0)<(c[11864]|0))}do{if(!(a[34104]|0)){d=c[12894]|0;if((d|0)==(aR(c[12892]|0)|0)){cs(c[12892]|0);d=c[m>>2]|0;e=c[12894]|0;cf(d|0,77344,(v=i,i=i+8|0,c[v>>2]=e,v)|0);break}e=c[12892]|0;if(a[47440]|0){a2(e|0);a[47440]=0;break}else{az(e|0);break}}}while(0);a[34104]=0;c[12892]=0;i=b;return}function eA(){var a=0;c[11870]=0;c[262]=1;c[263]=0;a=c[264]|0;if((a|0)!=0){e1(a);c[264]=0}c[12210]=99;c[265]=2;c[266]=0;a=c[267]|0;if((a|0)!=0){e1(a);c[267]=0}c[12211]=99;c[268]=3;c[269]=0;a=c[270]|0;if((a|0)!=0){e1(a);c[270]=0}c[12212]=99;c[271]=4;c[272]=0;a=c[273]|0;if((a|0)!=0){e1(a);c[273]=0}c[12213]=99;c[274]=5;c[275]=0;a=c[276]|0;if((a|0)!=0){e1(a);c[276]=0}c[12214]=99;c[277]=6;c[278]=0;a=c[279]|0;if((a|0)!=0){e1(a);c[279]=0}c[12215]=99;c[280]=7;c[281]=0;a=c[282]|0;if((a|0)==0){c[12216]=99;return}e1(a);c[282]=0;c[12216]=99;return}function eB(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0;e=i;i=i+56|0;f=e|0;g=e+24|0;j=e+48|0;k=j;l=i;i=i+47|0;i=i+7>>3<<3;m=c[12202]|0;if((m|0)>-1){c[12204]=m}m=c[13898]|0;n=c[1054]|0;o=n+(m*40&-1)+36|0;p=c[o>>2]|0;q=n+(m*40&-1)+32|0;r=c[q>>2]|0;s=n+(m*40&-1)|0;L5605:do{if((a[s]&1)==0){t=0;u=4113}else{if((p|0)>0){w=c[10036]|0;x=0;y=0;z=r;while(1){if((a[x+179688|0]|0)==(a[w+(x+z|0)|0]|0)){A=z;B=y}else{if((x|0)!=4){t=0;u=4113;break L5605}A=z-1|0;B=1}C=x+1|0;if((C|0)<(B+p|0)){x=C;y=B;z=A}else{break}}if((B|0)!=0|(C|0)==4){u=4117;break}else{D=C}}else{D=0}t=(D|0)==9;u=4113}}while(0);if((u|0)==4113){if(t|(c[12204]|0)>-1){u=4117}else{E=m}}if((u|0)==4117){c[j>>2]=0;c[j+4>>2]=0;if(b){uf(m,138160,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}a[47544]=0;j=c[o>>2]|0;L5624:do{if((a[s]&1)!=0&(j|0)>0){o=c[10036]|0;t=0;D=0;C=c[q>>2]|0;while(1){if((a[t+179688|0]|0)==(a[o+(t+C|0)|0]|0)){F=C;G=D}else{if((t|0)!=4){break L5624}F=C-1|0;G=1}B=t+1|0;if((B|0)<(G+j|0)){t=B;D=G;C=F}else{break}}if((G|0)==0){if(!((t|0)==3|(t|0)==8)){break}}C=m+1|0;c[13898]=C;if((c[8272]|0)<=(C|0)){H=c[10880]|0;uf(C,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[n+(C*40&-1)|0]&1)==0){H=c[10880]|0;uf(C,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}D=c[n+(C*40&-1)+36>>2]|0;o=n+(C*40&-1)+32|0;B=c[10036]|0;A=0;while(1){if((A|0)>=(D|0)){break}if((a[B+((c[o>>2]|0)+A|0)|0]|0)==(a[A+115e3|0]|0)){A=A+1|0}else{u=4519;break}}if((u|0)==4519){H=c[10880]|0;uf(C,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((A|0)!=1){H=c[10880]|0;uf(C,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=m+2|0;c[13898]=o;D=c[n+(o*40&-1)+36>>2]|0;uD(k|0,B+(c[n+(o*40&-1)+32>>2]|0)|0,(D|0)<7?D:7);a[k+((D|0)>7?7:D)|0]=0;D=0;while(1){t=c[48696+(D<<3)>>2]|0;if((t|0)==0){break}if((uK(k|0,t|0)|0)==0){u=4137;break}else{D=D+1|0}}if((u|0)==4137){c[14150]=c[48700+(D<<3)>>2];c[12204]=D}if((c[12204]|0)==(D|0)){c[13898]=m+3;break}else{uf(o,178808,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((c[11856]|0)!=0){do{if((aY(201e3,c[48696+(c[12204]<<3)>>2]|0)|0)==0){m=bh(c[11932]|0,46)|0;k=m+1|0;do{if((m|0)!=0){n=c[12174]|0;if((n|0)==0){break}else{I=0;J=n}do{if((uK(k|0,J|0)|0)==0){c[14150]=c[48700+(I<<3)>>2]}I=I+1|0;J=c[48696+(I<<3)>>2]|0;}while((J|0)!=0)}}while(0);if((c[14150]|0)!=64){break}uf(-1,178808,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);cS[c[14150]&511]()}if((c[11864]|0)>0){J=0;do{I=c[12172]|0;if((c[I+(J*232&-1)+140>>2]|0)==(c[12145]|0)){K=I}else{c[I+(J*232&-1)>>2]=0;c[(c[12172]|0)+(J*232&-1)+4>>2]=0;c[(c[12172]|0)+(J*232&-1)+8>>2]=0;K=c[12172]|0}do{if(+h[K+(J*232&-1)+168>>3]!=0.0){if((c[K+(J*232&-1)+112>>2]|0)==-3){h[K+(J*232&-1)+24>>3]=0.0}if((c[K+(J*232&-1)+116>>2]|0)==-3){h[K+(J*232&-1)+32>>3]=0.0}if((c[K+(J*232&-1)+120>>2]|0)!=-3){break}h[K+(J*232&-1)+40>>3]=0.0}}while(0);do{if(+h[K+(J*232&-1)+176>>3]!=0.0){if((c[K+(J*232&-1)+112>>2]|0)==-4){h[K+(J*232&-1)+24>>3]=0.0}if((c[K+(J*232&-1)+116>>2]|0)==-4){h[K+(J*232&-1)+32>>3]=0.0}if((c[K+(J*232&-1)+120>>2]|0)!=-4){break}h[K+(J*232&-1)+40>>3]=0.0}}while(0);do{if(+h[K+(J*232&-1)+184>>3]!=0.0){if((c[K+(J*232&-1)+112>>2]|0)==-5){h[K+(J*232&-1)+24>>3]=0.0}if((c[K+(J*232&-1)+116>>2]|0)==-5){h[K+(J*232&-1)+32>>3]=0.0}if((c[K+(J*232&-1)+120>>2]|0)!=-5){break}h[K+(J*232&-1)+40>>3]=0.0}}while(0);if((c[K+(J*232&-1)+192>>2]|0)!=0){c[K+(J*232&-1)+48>>2]=0}J=J+1|0;}while((J|0)<(c[11864]|0))}E=c[13898]|0}J=c[8272]|0;L5717:do{if((E|0)<(J|0)){K=l|0;I=f|0;k=f+8|0;m=g|0;o=g+8|0;D=0;n=0;H=0;G=0;F=0;j=0;q=0;s=0;B=0;C=0;A=0;t=0;p=0;r=0;z=0;y=0;x=E;w=J;L5719:while(1){L=c[1054]|0;M=(a[L+(x*40&-1)|0]&1)==0;N=c[L+(x*40&-1)+36>>2]|0;O=L+(x*40&-1)+32|0;L5721:do{if(M){uD(K|0,31040,47);P=c[O>>2]|0;u=4193}else{Q=c[10036]|0;R=0;while(1){if((R|0)>=(N|0)){u=4166;break}if((a[Q+((c[O>>2]|0)+R|0)|0]|0)==(a[R+103664|0]|0)){R=R+1|0}else{break}}if((u|0)==4166){u=0;if((R|0)==1){S=y;break L5717}}uD(K|0,31040,47);Q=c[O>>2]|0;if(!((N|0)>0&(M^1))){P=Q;u=4193;break}T=c[10036]|0;U=0;V=0;W=Q;while(1){if((a[U+178256|0]|0)==(a[T+(U+W|0)|0]|0)){X=W;Y=V}else{if((U|0)!=3){break}X=W-1|0;Y=1}Z=U+1|0;if((Z|0)<(Y+N|0)){U=Z;V=Y;W=X}else{u=4173;break}}do{if((u|0)==4173){u=0;if((Y|0)==0){if(!((U|0)==2|(U|0)==6)){break}}if(r){u=4538;break L5719}_=x+1|0;c[13898]=_;if(b){u=4177;break L5719}a[47544]=0;eS();$=y;aa=z;ab=1;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5721}}while(0);if(!((N|0)>0&(M^1))){P=Q;u=4193;break}U=c[10036]|0;W=0;V=0;T=Q;while(1){if((a[W+177976|0]|0)==(a[U+(W+T|0)|0]|0)){ap=T;aq=V}else{if((W|0)!=3){P=Q;u=4193;break L5721}ap=T-1|0;aq=1}R=W+1|0;if((R|0)<(aq+N|0)){W=R;V=aq;T=ap}else{break}}if((aq|0)==0){if(!((W|0)==2|(W|0)==5)){P=Q;u=4193;break}}if(H){u=4544;break L5719}ar=x+1|0;c[13898]=ar;if(b){u=4190;break L5719}a[47544]=0;eS();if((c[11864]|0)>0){as=0}else{$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=1;an=n;ao=D;break}while(1){a[(c[12172]|0)+(as*232&-1)+124|0]=1;T=as+1|0;if((T|0)<(c[11864]|0)){as=T}else{$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=1;an=n;ao=D;break}}}}while(0);L5760:do{if((u|0)==4193){u=0;O=(w|0)>(x|0);L5762:do{if(O){if(M){S=y;break L5717}Q=c[10036]|0;W=0;while(1){if((W|0)>=(N|0)){u=4198;break}if((a[Q+(P+W|0)|0]|0)==(a[W+177688|0]|0)){W=W+1|0}else{u=4199;break}}if((u|0)==4198){u=0;if((W|0)!=2){u=4199}}do{if((u|0)==4199){u=0;if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;T=0;while(1){if((T|0)>=(N|0)){u=4204;break}if((a[Q+(P+T|0)|0]|0)==(a[T+177448|0]|0)){T=T+1|0}else{break}}if((u|0)==4204){u=0;if((T|0)==2){break}}if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;V=0;while(1){if((V|0)>=(N|0)){u=4218;break}if((a[Q+(P+V|0)|0]|0)==(a[V+176840|0]|0)){V=V+1|0}else{u=4219;break}}if((u|0)==4218){u=0;if((V|0)!=2){u=4219}}do{if((u|0)==4219){u=0;if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;T=0;while(1){if((T|0)>=(N|0)){u=4224;break}if((a[Q+(P+T|0)|0]|0)==(a[T+176376|0]|0)){T=T+1|0}else{break}}if((u|0)==4224){u=0;if((T|0)==2){break}}if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;U=0;while(1){if((U|0)>=(N|0)){u=4236;break}if((a[Q+(P+U|0)|0]|0)==(a[U+175576|0]|0)){U=U+1|0}else{break}}if((u|0)==4236){u=0;if((U|0)==2){u=4237;break L5719}}if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;T=0;while(1){if((T|0)>=(N|0)){u=4243;break}if((a[Q+(P+T|0)|0]|0)==(a[T+174728|0]|0)){T=T+1|0}else{break}}do{if((u|0)==4243){u=0;if((T|0)!=5){break}if(t){u=4537;break L5719}U=x+1|0;c[13898]=U;L5814:do{if((w|0)>(U|0)){if((a[L+(U*40&-1)|0]&1)==0){break}R=c[L+(U*40&-1)+36>>2]|0;Z=L+(U*40&-1)+32|0;at=0;while(1){if((at|0)>=(R|0)){break}if((a[Q+((c[Z>>2]|0)+at|0)|0]|0)==(a[at+115e3|0]|0)){at=at+1|0}else{break L5814}}if((at|0)!=1){break}eT(1,0);$=y;aa=z;ab=r;ac=p;ad=1;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if((c[11864]|0)>0){au=0}else{$=y;aa=z;ab=r;ac=p;ad=1;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}while(1){c[(c[12172]|0)+(au*232&-1)+12>>2]=-1;U=au+1|0;if((U|0)<(c[11864]|0)){au=U}else{$=y;aa=z;ab=r;ac=p;ad=1;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}}}while(0);if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;T=0;while(1){if((T|0)>=(N|0)){u=4259;break}if((a[Q+(P+T|0)|0]|0)==(a[T+174440|0]|0)){T=T+1|0}else{break}}do{if((u|0)==4259){u=0;if((T|0)!=5){break}if(p){u=4549;break L5719}U=c[12172]|0;if(!(H|(U|0)!=0)){u=4262;break L5719}Z=x+1|0;c[13898]=Z;L5836:do{if((w|0)>(Z|0)){if((a[L+(Z*40&-1)|0]&1)==0){break}R=c[L+(Z*40&-1)+36>>2]|0;av=L+(Z*40&-1)+32|0;aw=0;while(1){if((aw|0)>=(R|0)){break}if((a[Q+((c[av>>2]|0)+aw|0)|0]|0)==(a[aw+115e3|0]|0)){aw=aw+1|0}else{break L5836}}if((aw|0)!=1){break}eT(1,1);$=y;aa=z;ab=r;ac=1;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if((c[11864]|0)>0){ax=0;ay=U}else{$=y;aa=z;ab=r;ac=1;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}while(1){c[ay+(ax*232&-1)+16>>2]=-1;Z=ax+1|0;if((Z|0)>=(c[11864]|0)){$=y;aa=z;ab=r;ac=1;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}ax=Z;ay=c[12172]|0}}}while(0);if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;T=0;while(1){if((T|0)>=(N|0)){u=4278;break}if((a[Q+(P+T|0)|0]|0)==(a[T+173304|0]|0)){T=T+1|0}else{break}}if((u|0)==4278){u=0;if((T|0)==5){u=4279;break L5719}}if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;U=0;while(1){if((U|0)>=(N|0)){u=4285;break}if((a[Q+(P+U|0)|0]|0)==(a[U+172952|0]|0)){U=U+1|0}else{break}}do{if((u|0)==4285){u=0;if((U|0)!=4){break}if(C){u=4535;break L5719}c[13898]=x+1;eT(2,-1);$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=1;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if(!O){break L5762}if(M){S=y;break L5717}U=c[10036]|0;Q=0;while(1){if((Q|0)>=(N|0)){u=4293;break}if((a[U+(P+Q|0)|0]|0)==(a[Q+172536|0]|0)){Q=Q+1|0}else{break}}do{if((u|0)==4293){u=0;if((Q|0)!=6){break}if(A){u=4546;break L5719}c[13898]=x+1;eT(2,1);$=y;aa=z;ab=r;ac=p;ad=t;ae=1;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if(!O){break L5762}if(M){S=y;break L5717}Q=c[10036]|0;U=0;while(1){if((U|0)>=(N|0)){break}if((a[Q+(P+U|0)|0]|0)==(a[U+172256|0]|0)){U=U+1|0}else{break L5762}}if((U|0)!=4){break L5762}if(z){u=4541;break L5719}c[13898]=x+1;eT(3,0);$=y;aa=1;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if(n){u=4536;break L5719}if(!(H|(c[12172]|0)!=0)){u=4227;break L5719}c[13898]=x+1;eT(0,1);if((c[11864]|0)<=0){$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=1;ao=D;break L5760}V=c[12172]|0;Q=0;while(1){h[V+(Q*232&-1)+40>>3]=+h[V+(Q*232&-1)+32>>3];T=Q+1|0;if((T|0)<(c[11864]|0)){Q=T}else{$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=1;ao=D;break L5760}}}}while(0);if(D){u=4540;break L5719}c[13898]=x+1;eT(0,0);W=c[11864]|0;if(n){az=W}else{if((W|0)<=0){$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=0;ao=1;break L5760}W=c[12172]|0;Q=0;while(1){h[W+(Q*232&-1)+32>>3]=+h[W+(Q*232&-1)+24>>3];V=Q+1|0;T=c[11864]|0;if((V|0)<(T|0)){Q=V}else{az=T;break}}}if((az|0)<=0){$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=1;break L5760}Q=c[12172]|0;W=0;while(1){h[Q+(W*232&-1)+40>>3]=+h[Q+(W*232&-1)+24>>3];T=W+1|0;if((T|0)<(c[11864]|0)){W=T}else{$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=1;break L5760}}}}while(0);if(!((N|0)>0&(M^1))){S=y;break L5717}O=c[10036]|0;W=0;Q=0;T=P;while(1){if((a[W+171944|0]|0)==(a[O+(W+T|0)|0]|0)){aA=T;aB=Q}else{if((W|0)!=5){break}aA=T-1|0;aB=1}V=W+1|0;if((V|0)<(aB+N|0)){W=V;Q=aB;T=aA}else{u=4310;break}}do{if((u|0)==4310){u=0;if((aB|0)==0){if(!((W|0)==4|(W|0)==9)){break}}if(z){u=4543;break L5719}c[13898]=x+1;if((c[11864]|0)<=0){$=y;aa=1;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}T=c[12172]|0;Q=0;while(1){O=T+(Q*232&-1)+112|0;c[O>>2]=c[12099];c[O+4>>2]=c[48400>>2];c[O+8>>2]=c[48404>>2];O=Q+1|0;if((O|0)<(c[11864]|0)){Q=O}else{$=y;aa=1;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}}}while(0);if(M){S=y;break L5717}L5921:do{if((N|0)>0){W=c[10036]|0;Q=0;T=0;O=P;while(1){if((a[Q+171624|0]|0)==(a[W+(Q+O|0)|0]|0)){aC=O;aD=T}else{if((Q|0)!=4){break L5921}aC=O-1|0;aD=1}V=Q+1|0;if((V|0)<(aD+N|0)){Q=V;T=aD;O=aC}else{break}}if((aD|0)==0){if(!((Q|0)==3|(Q|0)==6)){break}}if(G){u=4326;break L5719}if(F){u=4548;break L5719}c[13898]=x+1;eT(4,c[11856]|0);$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=1;al=0;am=H;an=n;ao=D;break L5760}}while(0);if(!((N|0)>0&(M^1))){S=y;break L5717}O=c[10036]|0;T=0;W=0;V=P;while(1){if((a[T+185064|0]|0)==(a[O+(T+V|0)|0]|0)){aE=V;aF=W}else{if((T|0)!=3){break}aE=V-1|0;aF=1}Z=T+1|0;if((Z|0)<(aF+N|0)){T=Z;W=aF;V=aE}else{u=4335;break}}do{if((u|0)==4335){u=0;if((aF|0)==0){if(!((T|0)==2|(T|0)==6)){break}}if(F){u=4338;break L5719}if(G){u=4539;break L5719}c[13898]=x+1;eT(5,c[11856]|0);$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=0;al=1;am=H;an=n;ao=D;break L5760}}while(0);if(M){S=y;break L5717}L5952:do{if((N|0)>0){T=c[10036]|0;V=0;W=0;O=P;while(1){if((a[V+170712|0]|0)==(a[T+(V+O|0)|0]|0)){aG=O;aH=W}else{if((V|0)!=3){u=4350;break L5952}aG=O-1|0;aH=1}Z=V+1|0;if((Z|0)<(aH+N|0)){V=Z;W=aH;O=aG}else{break}}if((aH|0)!=0){break}if(!((V|0)==2|(V|0)==8)){u=4350}}else{u=4350}}while(0);do{if((u|0)==4350){u=0;if(!((N|0)>0&(M^1))){S=y;break L5717}O=c[10036]|0;W=0;T=0;Q=P;while(1){if((a[W+90456|0]|0)==(a[O+(W+Q|0)|0]|0)){aI=Q;aJ=T}else{if((W|0)!=3){break}aI=Q-1|0;aJ=1}Z=W+1|0;if((Z|0)<(aJ+N|0)){W=Z;T=aJ;Q=aI}else{u=4356;break}}if((u|0)==4356){u=0;if((aJ|0)!=0){break}if((W|0)==2|(W|0)==6){break}}if(M){S=y;break L5717}L5976:do{if((N|0)>0){Q=c[10036]|0;T=0;O=0;V=P;while(1){if((a[T+170128|0]|0)==(a[Q+(T+V|0)|0]|0)){aK=V;aL=O}else{if((T|0)!=4){break L5976}aK=V-1|0;aL=1}Z=T+1|0;if((Z|0)<(aL+N|0)){T=Z;O=aL;V=aK}else{break}}if((aL|0)==0){if(!((T|0)==3|(T|0)==13)){break}}if((c[11856]|0)==1){u=4370;break L5719}if(B){u=4545;break L5719}c[13898]=x+1;eT(7,0);$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=1;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if(!((N|0)>0&(M^1))){S=y;break L5717}W=c[10036]|0;V=0;while(1){if((a[V+169288|0]|0)!=(a[W+(V+P|0)|0]|0)){break}aM=V+1|0;if((aM|0)<(N|0)){V=aM}else{u=4377;break}}do{if((u|0)==4377){u=0;if((aM|0)!=4){break}if(j){u=4550;break L5719}c[13898]=x+1;eT(8,0);$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=1;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if(M){S=y;break L5717}L6001:do{if((N|0)>0){V=c[10036]|0;W=0;O=0;Q=P;while(1){if((a[W+168800|0]|0)==(a[V+(W+Q|0)|0]|0)){aN=Q;aO=O}else{if((W|0)!=3){break L6001}aN=Q-1|0;aO=1}Z=W+1|0;if((Z|0)<(aO+N|0)){W=Z;O=aO;Q=aN}else{break}}if((aO|0)==0){if(!((W|0)==2|(W|0)==6)){break}}if(q){u=4551;break L5719}aP=x+1|0;c[13898]=aP;if((w|0)<=(aP|0)){u=4515;break L5719}if((a[L+(aP*40&-1)|0]&1)==0){u=4516;break L5719}Q=c[L+(aP*40&-1)+36>>2]|0;O=L+(aP*40&-1)+32|0;V=c[10036]|0;T=0;while(1){if((T|0)>=(Q|0)){break}if((a[V+((c[O>>2]|0)+T|0)|0]|0)==(a[T+115e3|0]|0)){T=T+1|0}else{u=4513;break L5719}}if((T|0)!=1){u=4514;break L5719}aQ=x+2|0;c[13898]=aQ;O=c[L+(aQ*40&-1)+36>>2]|0;Q=c[L+(aQ*40&-1)+32>>2]|0;W=(a[L+(aQ*40&-1)|0]&1)==0;L6021:do{if((O|0)>0&(W^1)){Z=0;av=0;R=Q;while(1){if((a[Z+184456|0]|0)==(a[V+(Z+R|0)|0]|0)){aR=R;aS=av}else{if((Z|0)!=3){u=4405;break L6021}aR=R-1|0;aS=1}at=Z+1|0;if((at|0)<(aS+O|0)){Z=at;av=aS;R=aR}else{break}}if((aS|0)==0){if(!((Z|0)==2|(Z|0)==7)){u=4405;break}}c[12208]=0}else{u=4405}}while(0);L6032:do{if((u|0)==4405){u=0;T=(w|0)>(aQ|0);L6034:do{if(T){if(W){u=4521;break L5719}else{aT=0}while(1){if((aT|0)>=(O|0)){u=4409;break}if((a[V+(Q+aT|0)|0]|0)==(a[aT+167960|0]|0)){aT=aT+1|0}else{u=4410;break}}if((u|0)==4409){u=0;if((aT|0)!=4){u=4410}}if((u|0)==4410){u=0;if(!T){break}if(W){u=4522;break L5719}else{aU=0}while(1){if((aU|0)>=(O|0)){break}if((a[V+(Q+aU|0)|0]|0)==(a[aU+167400|0]|0)){aU=aU+1|0}else{break L6034}}if((aU|0)!=4){break}}c[12208]=c[12208]&3^3;break L6032}}while(0);L6050:do{if((O|0)>0&(W^1)){Z=0;R=0;av=Q;while(1){if((a[Z+167056|0]|0)==(a[V+(Z+av|0)|0]|0)){aV=av;aW=R}else{if((Z|0)!=3){break L6050}aV=av-1|0;aW=1}U=Z+1|0;if((U|0)<(aW+O|0)){Z=U;R=aW;av=aV}else{break}}if((aW|0)==0){if(!((Z|0)==2|(Z|0)==6)){break}}c[12208]=0;break L6032}}while(0);L6062:do{if(T){if(W){u=4523;break L5719}else{aX=0}while(1){if((aX|0)>=(O|0)){break}if((a[V+(Q+aX|0)|0]|0)==(a[aX+172296|0]|0)){aX=aX+1|0}else{break L6062}}if((aX|0)!=3){break}c[12208]=3;break L6032}}while(0);L6070:do{if((O|0)>0&(W^1)){Z=0;av=0;R=Q;while(1){if((a[Z+166640|0]|0)==(a[V+(Z+R|0)|0]|0)){aZ=R;a_=av}else{if((Z|0)!=3){u=4437;break L6070}aZ=R-1|0;a_=1}U=Z+1|0;if((U|0)<(a_+O|0)){Z=U;av=a_;R=aZ}else{break}}if((a_|0)!=0){break}if(!((Z|0)==2|(Z|0)==6)){u=4437}}else{u=4437}}while(0);if((u|0)==4437){u=0;if(W|T^1){u=4524;break L5719}else{a$=0}while(1){if((a$|0)>=(O|0)){break}if((a[V+(Q+a$|0)|0]|0)==(a[a$+166368|0]|0)){a$=a$+1|0}else{u=4525;break L5719}}if((a$|0)!=3){u=4526;break L5719}}c[12208]=1}}while(0);c[13898]=x+3;$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=1;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if(!((N|0)>0&(M^1))){S=y;break L5717}Q=c[10036]|0;V=0;O=0;W=P;while(1){if((a[V+165520|0]|0)==(a[Q+(V+W|0)|0]|0)){a0=W;a1=O}else{if((V|0)!=4){S=y;break L5717}a0=W-1|0;a1=1}T=V+1|0;if((T|0)<(a1+N|0)){V=T;O=a1;W=a0}else{break}}if((a1|0)==0){if(!((V|0)==3|(V|0)==6)){S=y;break L5717}}if(y){u=4547;break L5719}a2=x+1|0;c[13898]=a2;if(b){u=4454;break L5719}a[47544]=0;if((w|0)<=(a2|0)){u=4527;break L5719}if((a[L+(a2*40&-1)|0]&1)==0){u=4528;break L5719}W=c[L+(a2*40&-1)+36>>2]|0;O=L+(a2*40&-1)+32|0;Q=c[10036]|0;T=0;while(1){if((T|0)>=(W|0)){break}if((a[Q+((c[O>>2]|0)+T|0)|0]|0)==(a[T+115e3|0]|0)){T=T+1|0}else{u=4529;break L5719}}if((T|0)!=1){u=4530;break L5719}a3=x+2|0;c[13898]=a3;if(!d){if((a3|0)>=(w|0)){a4=a3;u=4531;break L5719}L6111:do{if((a[L+(a3*40&-1)|0]&1)!=0){O=c[L+(a3*40&-1)+36>>2]|0;W=L+(a3*40&-1)+32|0;V=0;while(1){if((V|0)>=(O|0)){break}if((a[Q+((c[W>>2]|0)+V|0)|0]|0)==(a[V+103664|0]|0)){V=V+1|0}else{break L6111}}if((V|0)==1){a4=a3;u=4532;break L5719}}}while(0);a[14176]=1;is(g);a[14176]=0;if((c[m>>2]|0)!=3){u=4480;break L5719}Q=c[o>>2]|0;if((Q|0)==0){u=4482;break L5719}eU(Q);uu(Q);$=1;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}uu(c[11962]|0);Q=c[13898]|0;L6121:do{if((Q|0)<(c[8272]|0)){T=c[1054]|0;L6123:do{if((a[T+(Q*40&-1)|0]&1)!=0){W=c[T+(Q*40&-1)+36>>2]|0;O=T+(Q*40&-1)+32|0;R=c[10036]|0;av=0;while(1){if((av|0)>=(W|0)){break}if((a[R+((c[O>>2]|0)+av|0)|0]|0)==(a[av+103664|0]|0)){av=av+1|0}else{break L6123}}if((av|0)==1){a5=0;break L6121}}}while(0);a[14176]=1;is(f);a[14176]=0;if((c[I>>2]|0)==3){a5=c[k>>2]|0;break}else{c[13898]=Q;a5=0;break}}else{a5=0}}while(0);c[11962]=a5;$=1;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=s;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D;break L5760}}while(0);if(s){u=4542;break L5719}c[13898]=x+1;eT(6,0);$=y;aa=z;ab=r;ac=p;ad=t;ae=A;af=C;ag=B;ah=1;ai=q;aj=j;ak=F;al=G;am=H;an=n;ao=D}}while(0);L=c[13898]|0;N=c[8272]|0;if((L|0)<(N|0)){D=ao;n=an;H=am;G=al;F=ak;j=aj;q=ai;s=ah;B=ag;C=af;A=ae;t=ad;p=ac;r=ab;z=aa;y=$;x=L;w=N}else{S=$;break L5717}}if((u|0)==4227){uf(x,175848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4237){uf(x,175072,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4177){uf(_,138160,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4338){uf(x,K,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4279){uf(x,175072,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4190){uf(ar,138160,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4370){uf(x,169712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4513){a6=c[10880]|0;uf(aP,a6,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4514){a6=c[10880]|0;uf(aP,a6,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4515){a6=c[10880]|0;uf(aP,a6,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4516){a6=c[10880]|0;uf(aP,a6,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4521){uf(aQ,165880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4522){uf(aQ,165880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4523){uf(aQ,165880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4524){uf(aQ,165880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4525){uf(aQ,165880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4526){uf(aQ,165880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4527){a7=c[10880]|0;uf(a2,a7,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4528){a7=c[10880]|0;uf(a2,a7,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4529){a7=c[10880]|0;uf(a2,a7,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4530){a7=c[10880]|0;uf(a2,a7,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4531){uf(a4,165048,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4532){uf(a4,165048,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4535){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4536){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4537){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4538){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4539){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4540){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4541){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4542){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4543){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4544){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4545){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4546){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4547){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4548){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4549){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4550){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4551){uf(x,164608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4454){uf(a2,138160,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4326){uf(x,K,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4262){uf(x,173896,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4480){c[13898]=a3;a4=a3;uf(a4,165048,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==4482){a4=c[13898]|0;uf(a4,165048,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{S=0}}while(0);do{if(!d){if(!((c[11862]|0)!=0&(b^1))){break}uh(-1,163928,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if(S){i=e;return}S=c[11962]|0;if(!((a[47544]&1)==0&(S|0)!=0)){i=e;return}eU(S);uh(-1,163496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=e;return}function eC(f,j){f=f|0;j=j|0;var k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,R=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0.0,am=0.0,an=0.0,ao=0.0,ap=0.0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0.0,aD=0.0,aE=0,aF=0,aG=0,aH=0,aJ=0,aK=0,aL=0,aN=0,aO=0.0,aP=0.0,aQ=0.0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0.0,aX=0.0,aZ=0.0,a$=0.0,a0=0.0,a1=0.0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a9=0,ba=0,bb=0,bc=0,bd=0,bf=0,bg=0,bh=0,bi=0,bj=0.0,bk=0,bl=0.0,bm=0.0,bn=0,bo=0.0,bp=0,bq=0,br=0.0,bs=0.0,bt=0,bu=0,bv=0;k=i;i=i+216|0;l=k|0;n=k+24|0;o=k+48|0;p=k+96|0;q=k+104|0;r=k+152|0;s=k+160|0;t=k+168|0;u=k+192|0;x=c[12892]|0;if(!((x|0)!=0|(c[11854]|0)!=0)){y=-1;i=k;return y|0}if((a[47392]&1)==0){if((c[8716]|0)==0){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=143056,v)|0);return 0}if((j|0)>=8){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=186808,v)|0);return 0}if(a[47736]|0){y=-1;i=k;return y|0}z=eV()|0;L6212:do{if((z|0)!=0){A=l|0;B=l+8|0;C=n|0;D=n+8|0;E=D;F=D;D=n+8|0;G=z;L6214:while(1){c[11900]=(c[11900]|0)+1;c[11874]=0;H=G;while(1){if((aM(d[H]|0|0)|0)==0){break}else{H=H+1|0}}I=a[H]|0;J=I<<24>>24==0;do{if(J){K=4799}else{if((a8(c[11948]|0,I<<24>>24|0)|0)==0){K=4799;break}if((c[9356]|0)==0){break}else{L=H;M=I}while(1){N=M<<24>>24;if(M<<24>>24==0){K=4793}else{if((a8(c[11948]|0,N|0)|0)==0){K=4793}}if((K|0)==4793){K=0;if((aM(N|0)|0)==0){break}}N=L+1|0;L=N;M=a[N]|0}if((a[L]|0)==0){break}N=c[9356]|0;if((a_(L|0,N|0,uA(N|0)|0)|0)!=0){break}a[37432]=1}}while(0);L6232:do{if((K|0)==4799){K=0;N=a[34104]|0;if(N){if((I<<24>>24|0)==101|(I<<24>>24|0)==69){K=4801;break L6214}else if((I<<24>>24|0)==0){K=4803}}else{if(J){K=4803}}if((K|0)==4803){K=0;c[7638]=-1;O=c[14126]|0;R=O+1|0;c[14126]=R;if((O|0)==0){c[8800]=(c[8800]|0)+1}do{if((R|0)==2){U=(c[11946]|0)+1|0;c[11946]=U;c[8800]=0;c[11942]=-1;V=c[9356]|0;if((V|0)!=0){if(a[37432]|0){K=4809;break L6214}}if((U|0)<=(c[11898]|0)){break L6232}if((U|0)<=(c[11762]|0)){W=V;break}if(N){break L6232}else{K=4813;break L6214}}else{W=c[9356]|0}}while(0);if((W|0)!=0){if(!(a[37432]|0)){break}}if((c[11946]|0)<(c[11898]|0)|(R|0)>2){break}else{K=4817;break L6214}}c[14126]=0;if((c[9356]|0)!=0){if(!(a[37432]|0)){break}}N=c[11946]|0;V=c[11898]|0;if((N|0)<(V|0)|(N|0)>(c[11762]|0)){break}if(((N-V|0)%(c[11904]|0)&-1|0)!=0){break}if((a[32928]&1)!=0|(c[13488]|0)>0){if(a[48872]|0){K=4824}}else{K=4824}if((K|0)==4824){K=0;V=c[8800]|0;N=c[10808]|0;if((V|0)<(N|0)|(V|0)>(c[8834]|0)){break}if(((V-N|0)%(c[10874]|0)&-1|0)!=0){break}N=(c[7638]|0)+1|0;c[7638]=N;V=c[10806]|0;if((N|0)<(V|0)|(N|0)>(c[8832]|0)){break}if(((N-V|0)%(c[10872]|0)&-1|0)!=0){break}}c[11942]=(c[11942]|0)+1;do{if((c[11930]|0)==0){eX(H)}else{do{if((c[11882]|0)<7){V=db(c[11952]|0,168,180032)|0;c[11952]=V;N=c[11882]|0;if((N|0)>=7){break}h[V+(N*24&-1)>>3]=0.0;c[V+(N*24&-1)+16>>2]=0;N=(c[11882]|0)+1|0;c[11882]=N;if((N|0)<7){X=N}else{break}do{N=c[11952]|0;h[N+(X*24&-1)>>3]=0.0;c[N+(X*24&-1)+16>>2]=0;X=(c[11882]|0)+1|0;c[11882]=X;}while((X|0)<7)}}while(0);N=c[11952]|0;V=ca(c[8802]|0,c[11930]|0,(v=i,i=i+56|0,c[v>>2]=N,c[v+8>>2]=N+24,c[v+16>>2]=N+48,c[v+24>>2]=N+72,c[v+32>>2]=N+96,c[v+40>>2]=N+120,c[v+48>>2]=N+144,v)|0)|0;c[11874]=V;if((V|0)==-1){K=4835;break L6214}if((V|0)>0){Y=0}else{break}do{c[(c[11952]|0)+(Y*24&-1)+8>>2]=1;c[(c[11952]|0)+(Y*24&-1)+12>>2]=0;Y=Y+1|0;}while((Y|0)<(c[11874]|0))}}while(0);do{if((c[11942]|0)==0){if(a[48872]|0){break}R=c[11874]|0;if((R|0)>0){V=0;while(1){uu(c[(c[11952]|0)+(V*24&-1)+16>>2]|0);N=eD(c[(c[11952]|0)+(V*24&-1)+12>>2]|0)|0;c[(c[11952]|0)+(V*24&-1)+16>>2]=N;N=V+1|0;U=c[11874]|0;if((N|0)<(U|0)){V=N}else{Z=U;break}}}else{Z=R}a[48872]=1;V=c[13488]|0;if((V|0)>(Z|0)){c[13488]=Z;_=Z}else{_=V}if((_|0)>0){K=4845;break L6214}if((a[32928]&1)!=0){K=4850;break L6214}}}while(0);V=c[11870]|0;U=c[11872]|0;N=(V|0)==0?7:U+V|0;$=U+j|0;ab=(N|0)>($|0)?$:N;if((ab|0)>0){ac=0;ad=1}else{ae=0;af=U;ag=V;break L6214}L6293:while(1){V=c[1048+(ac*12&-1)>>2]|0;c[12902]=ac;U=1052+(ac*12&-1)|0;N=c[U>>2]|0;L6295:do{if((N|0)>2){$=c[11944]|0;ah=($|0)==0;if(!ah){if((c[$+12>>2]|0)==474){ai=ad;break}}if((N|0)==5){aj=1;ak=1}else if((N|0)==6){aj=1;ak=5}else if((N|0)==7){aj=2;ak=0}else if((N|0)==8){aj=3;ak=3}else if((N|0)==4){aj=0;ak=6}else{aj=0;ak=2}do{if((ac|0)==1){if((aj|0)==0){al=+(c[11942]|0);break}else{al=+h[f+(aj-1<<3)>>3];break}}else{al=+h[f+(aj<<3)>>3]}}while(0);am=al;do{if(ah){an=am}else{if((c[$+12>>2]|0)!=392){an=am;break}if((ac|0)>1){if((aj|0)==0){ao=+(c[11942]|0)}else{ao=+h[f+(aj-1<<3)>>3]}ap=ao}else{ap=am}an=ap+ +h[(c[$+276>>2]|0)+16>>3]}}while(0);$=c[1056+(ac*12&-1)>>2]|0;if(($|0)==0){ah=eD(c[47056+(ac<<2)>>2]|0)|0;dp(ak,ah,an,-1);uu(ah);aq=ad;K=4942;break}a[43504]=1;e4($,l);a[43504]=0;if((c[A>>2]|0)==3){$=c[B>>2]|0;dp(ak,$,an,-1);uu($);c[A>>2]=1;aq=ad;K=4942;break}else{$=c[m>>2]|0;aI(142872,39,1,$|0);aq=ad;K=4942;break}}else{if((N|0)==2){$=eD(c[47056+(ac<<2)>>2]|0)|0;if((c[11944]|0)!=0){ah=c[11942]|0;ar=ut(192)|0;if((ar|0)==0){gk();as=ut(192)|0;if((as|0)==0){K=4883;break L6214}else{at=as}}else{at=ar}ar=at;if((c[(c[11944]|0)+224>>2]|0)==0){as=ut(192)|0;if((as|0)==0){gk();au=ut(192)|0;if((au|0)==0){K=4887;break L6214}else{av=au}}else{av=as}c[(c[11944]|0)+224>>2]=av;as=(c[11944]|0)+224|0;uE(c[as>>2]|0,0,192);c[(c[as>>2]|0)+4>>2]=-1}if(($|0)==0){aw=0}else{aw=bP($|0)|0}c[at+60>>2]=aw;c[at+4>>2]=ah;c[at+64>>2]=0;c[at>>2]=c[c[(c[11944]|0)+224>>2]>>2];c[c[(c[11944]|0)+224>>2]>>2]=ar}uu($);aq=ad;K=4942;break}$=c[1056+(ac*12&-1)>>2]|0;if(($|0)==0){if((V|0)==(-2|0)){h[f+(ac<<3)>>3]=+(c[11946]|0);aq=ad;K=4942;break}else if((V|0)==(-1|0)){h[f+(ac<<3)>>3]=+(c[8800]|0);aq=ad;K=4942;break}else if((V|0)==0){h[f+(ac<<3)>>3]=+(c[11942]|0);aq=ad;K=4942;break}else{if((V|0)<1){K=4928;break L6214}ar=c[48840+(ac<<2)>>2]|0;do{if((ar|0)!=99){if((c[64808+(ar*688&-1)>>2]|0)!=1){break}h[r>>3]=0.0;if((V|0)>(c[11874]|0)){K=4935;break L6293}ah=V-1|0;as=c[11952]|0;if((c[as+(ah*24&-1)+8>>2]|0)==-5){K=4935;break L6293}au=c[as+(ah*24&-1)+12>>2]|0;if((au|0)==0){K=4935;break L6293}if((t1(au,64813+(ar*688&-1)|0,q,r)|0)==0){K=4935;break L6293}am=+t2(q);h[f+(ac<<3)>>3]=am+ +h[r>>3];aq=ad;K=4942;break L6295}}while(0);if((N|0)==1){ax=ad;K=4945;break}if((V|0)>(c[11874]|0)){K=4941;break L6293}ar=V-1|0;au=c[11952]|0;ah=c[au+(ar*24&-1)+8>>2]|0;if((ah|0)==(-5|0)){y=-5;K=4984;break L6214}else if((ah|0)!=1){K=4941;break L6293}h[f+(ac<<3)>>3]=+h[au+(ar*24&-1)>>3];aq=ad;K=4942;break}}a[43504]=1;e4($,n);a[43504]=0;if((a[1960]&1)!=0){y=-2;K=4981;break L6214}ar=48840+(ac<<2)|0;au=c[ar>>2]|0;do{if((au|0)==99){ay=0;K=4908}else{if((c[64808+(au*688&-1)>>2]|0)!=1){ay=0;K=4908;break}ah=c[C>>2]|0;if((ah|0)==3){az=1;break}if((aY(64813+(au*688&-1)|0,132824)|0)!=0){ay=1;K=4908;break}as=ut(20)|0;if((as|0)==0){gk();aA=ut(20)|0;if((aA|0)==0){K=4901;break L6214}else{aB=aA}}else{aB=as}if((ah|0)==1){aC=+(c[F>>2]|0)}else if((ah|0)==2){aC=+h[D>>3]}else if((ah|0)==3){aC=+uz(c[E>>2]|0,0)}else{K=4906;break L6214}be(aB|0,142616,(v=i,i=i+8|0,h[v>>3]=aC,v)|0);c[C>>2]=3;c[E>>2]=aB;ay=1;K=4908}}while(0);do{if((K|0)==4908){K=0;au=c[C>>2]|0;if((au|0)==3){az=ay;break}else if((au|0)==1){aD=+(c[F>>2]|0)}else if((au|0)==2){aD=+h[D>>3]}else{K=4921;break L6214}h[f+(ac<<3)>>3]=aD;aq=ad;K=4942;break L6295}}while(0);if((c[U>>2]|0)==1){au=c[E>>2]|0;$=(uA(au|0)|0)+3|0;ah=ut($)|0;if((ah|0)==0){gk();as=ut($)|0;if((as|0)==0){K=4912;break L6214}else{aE=as}}else{aE=ah}a[aE]=34;ah=aE+1|0;uB(ah|0,au|0);au=aE+(uA(aE|0)|0)|0;w=34;a[au]=w&255;w=w>>8;a[au+1|0]=w&255;au=47088+(ac<<2)|0;uu(c[au>>2]|0);c[au>>2]=aE;c[47056+(ac<<2)>>2]=aE}do{if(az){h[p>>3]=0.0;au=c[E>>2]|0;if((t1(au,64813+((c[ar>>2]|0)*688&-1)|0,o,p)|0)==0){aF=0;aG=au;break}am=+t2(o);h[f+(ac<<3)>>3]=am+ +h[p>>3];aF=ad;aG=au}else{aF=ad;aG=c[E>>2]|0}}while(0);uu(aG);c[C>>2]=1;aq=aF;K=4942}}while(0);do{if((K|0)==4942){K=0;if((c[U>>2]|0)!=1){ai=aq;break}if((c[1056+(ac*12&-1)>>2]|0)==0){ax=aq;K=4945;break}if((c[47056+(ac<<2)>>2]|0)==0){ax=aq;K=4945}else{ai=aq}}}while(0);do{if((K|0)==4945){K=0;if(!((V|0)==(-1|0)|(V|0)==(-2|0)|(V|0)==0)){ai=ax;break}U=ut(32)|0;if((U|0)==0){gk();N=ut(32)|0;if((N|0)==0){K=4948;break L6214}else{aH=N}}else{aH=U}U=~~+h[f+(ac<<3)>>3];be(aH|0,21e4,(v=i,i=i+8|0,c[v>>2]=U,v)|0);U=47088+(ac<<2)|0;uu(c[U>>2]|0);c[U>>2]=aH;c[47056+(ac<<2)>>2]=aH;ai=ax}}while(0);V=ac+1|0;if((V|0)<(ab|0)){ac=V;ad=ai}else{aJ=ai;aK=V;break}}if((K|0)==4935){K=0;aJ=(c[11870]|0)==0?ad:0;aK=ac}else if((K|0)==4941){K=0;aJ=(c[11870]|0)==0?ad:0;aK=ac}if((aJ|0)!=0){K=4952;break L6214}}}while(0);G=eV()|0;if((G|0)==0){break L6212}}if((K|0)==4984){i=k;return y|0}else if((K|0)==4883){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=142224,v)|0);return 0}else if((K|0)==4928){uf(-1,142440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((K|0)==4850){c[11942]=(c[11942]|0)-1;a[32928]=0;y=-9;i=k;return y|0}else if((K|0)==4887){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=142224,v)|0);return 0}else if((K|0)==4912){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=181200,v)|0);return 0}else if((K|0)==4845){G=c[(c[11952]|0)+((_-1|0)*24&-1)+16>>2]|0;if((G|0)==0){c[11902]=0;y=-7;i=k;return y|0}C=bP(G|0)|0;c[11902]=C;if((C|0)==0){y=-7;i=k;return y|0}c[11942]=(c[11942]|0)-1;c[13488]=-99;a[32928]=0;y=-6;i=k;return y|0}else if((K|0)==4801){a[47736]=1;y=-1;i=k;return y|0}else if((K|0)==4809){a[47736]=1;y=-1;i=k;return y|0}else if((K|0)==4813){a[47736]=1;y=-1;i=k;return y|0}else if((K|0)==4817){y=-3-O|0;i=k;return y|0}else if((K|0)==4835){a[47736]=1;y=-1;i=k;return y|0}else if((K|0)==4921){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((K|0)==4952){ae=aK;af=c[11872]|0;ag=c[11870]|0}else if((K|0)==4901){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=142728,v)|0);return 0}else if((K|0)==4981){i=k;return y|0}else if((K|0)==4906){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((K|0)==4948){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=142320,v)|0);return 0}C=ae-af|0;if((ag|0)==0|(C|0)==(ag|0)|(C|0)==(j|0)){y=C;i=k;return y|0}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=180528,v)|0);return 0}}}while(0);c[11874]=0;a[47736]=1;y=-1;i=k;return y|0}ag=s;if((x|0)==0){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=187320,v)|0);return 0}if((j|0)>=8){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=186808,v)|0);return 0}x=c[11876]|0;if((c[11884]|0)<=(x|0)){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=186248,v)|0);return 0}if((x|0)==0){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=185768,v)|0);return 0}if(a[47736]|0){y=-1;i=k;return y|0}do{if((c[11882]|0)<(x|0)){af=db(c[11952]|0,x*24&-1,180032)|0;c[11952]=af;ae=c[11882]|0;if((ae|0)>=(x|0)){break}h[af+(ae*24&-1)>>3]=0.0;c[af+(ae*24&-1)+16>>2]=0;ae=(c[11882]|0)+1|0;c[11882]=ae;if((ae|0)<(x|0)){aL=ae}else{break}do{ae=c[11952]|0;h[ae+(aL*24&-1)>>3]=0.0;c[ae+(aL*24&-1)+16>>2]=0;aL=(c[11882]|0)+1|0;c[11882]=aL;}while((aL|0)<(x|0))}}while(0);do{if((c[12222]|c[12224]|c[12220]|0)==0){x=c[12172]|0;aL=c[12170]|0;c[11788]=0;c[11787]=0;c[11786]=0;aD=+h[x+(aL*232&-1)+80>>3];if(+P(+aD)<.001){c[11822]=c[6578];c[11823]=c[6579];c[11824]=c[6580];c[11825]=c[6581];c[11826]=c[6582];c[11827]=c[6583];c[11828]=c[6584];c[11829]=c[6585];aN=0;aO=1.0;aP=0.0;aQ=0.0}else{aC=+S(+aD);h[5911]=aC;an=+T(+aD);aD=-0.0-an;h[5912]=aD;h[5913]=an;h[5914]=aC;aN=1;aO=aC;aP=aD;aQ=an}ae=x+(aL*232&-1)+88|0;an=+h[ae>>3];af=x+(aL*232&-1)+96|0;aD=+h[af>>3];aC=an*an+aD*aD;aK=x+(aL*232&-1)+104|0;aD=+h[aK>>3];ap=+Q(+(aC+aD*aD));ao=+Q(+aC);if(ap<1.0e-9|ao<ap*1.0e-4){uD(47320,26240,72);aR=0}else{aC=1.0/(ap*ao);h[5915]=an*aD*aC;h[5916]=aC*ap*(-0.0- +h[af>>3]);h[5917]=aC*ao*+h[ae>>3];h[5918]=aC*+h[af>>3]*+h[aK>>3];h[5919]=aC*ap*+h[ae>>3];h[5920]=aC*ao*+h[af>>3];h[5921]=ao*(-0.0-ao)*aC;h[5922]=0.0;h[5923]=aC*ao*+h[aK>>3];aR=1}aK=aN?1:aR;a[47128]=aK;if((a[47544]&1)==0){af=x+(aL*232&-1)+48|0;ae=x+(aL*232&-1)+192|0;O=x+(aL*232&-1)+124|0;_=0;do{aJ=x+(aL*232&-1)+(_<<2)|0;ac=c[aJ>>2]|0;do{if((ac|0)==0){if((c[x+(aL*232&-1)+140+(_<<2)>>2]|0)!=0){K=4589;break}aS=-3-(c[x+(aL*232&-1)+112+(_<<2)>>2]|0)|0;K=4595}else{K=4589}}while(0);do{if((K|0)==4589){K=0;ad=x+(aL*232&-1)+112+(_<<2)|0;if((a[O]&1)==0){aT=ac}else{c[1048+(_*12&-1)>>2]=c[ad>>2];aT=c[aJ>>2]|0}ai=-3-(c[ad>>2]|0)|0;if((aT|0)>0){c[47144+(ai<<2)>>2]=aT;aU=aT;aV=ai;break}if((aT|0)>=0){aS=ai;K=4595;break}c[47144+(ai<<2)>>2]=2147483647;aU=2147483647;aV=ai}}while(0);if((K|0)==4595){K=0;aJ=c[x+(aL*232&-1)+140+(aS<<2)>>2]|0;c[47144+(aS<<2)>>2]=aJ;aU=aJ;aV=aS}ao=+h[x+(aL*232&-1)+24+(_<<3)>>3];if(ao!=0.0){h[47240+(aV<<3)>>3]=ao;aW=ao}else{ao=+h[x+(aL*232&-1)+168+(aV<<3)>>3];h[47240+(aV<<3)>>3]=ao;aW=ao}ao=aW*+(aa(c[x+(aL*232&-1)+12+(_<<2)>>2]|0,c[x+(aL*232&-1)+152+(aV<<2)>>2]|0)|0);h[47240+(aV<<3)>>3]=ao;do{if((c[af>>2]|0)==0){if((c[ae>>2]|0)!=0){h[47176+(_<<3)>>3]=+h[x+(aL*232&-1)+200+(aV<<3)>>3];break}if((aU|0)>0){h[47176+(_<<3)>>3]=+(aU-1|0)*+P(+ao)*.5;break}else{h[47176+(_<<3)>>3]=0.0;break}}else{h[47176+(_<<3)>>3]=+h[x+(aL*232&-1)+56+(_<<3)>>3]}}while(0);aJ=c[af>>2]|0;if((aJ|0)==0){if((c[ae>>2]|0)==1){K=4608}else{K=4611}}else if((aJ|0)==1){K=4608}else{K=4611}do{if((K|0)==4608){K=0;if((aU|0)>0&ao<0.0){h[47264+(_<<3)>>3]=+(aU-1|0)*ao;break}else{h[47264+(_<<3)>>3]=0.0;break}}else if((K|0)==4611){K=0;if((aU|0)>0){h[47264+(_<<3)>>3]=+(aU-1|0)*ao*.5;break}else{h[47264+(_<<3)>>3]=0.0;break}}}while(0);_=_+1|0;}while((_|0)<3)}else{_=c[x+(aL*232&-1)+140>>2]|0;c[11786]=_;c[11787]=c[x+(aL*232&-1)+144>>2];if((_|0)==0){uf(-1,185128,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}_=x+(aL*232&-1)+12|0;h[5911]=aO*+(c[_>>2]|0);h[5912]=aP*+(c[_>>2]|0);_=x+(aL*232&-1)+16|0;h[5913]=aQ*+(c[_>>2]|0);h[5914]=aO*+(c[_>>2]|0);_=x+(aL*232&-1)+48|0;if((c[_>>2]|0)==0){aX=(+h[5946]+ +h[5944])*.5}else{aX=+h[x+(aL*232&-1)+56>>3]}h[5897]=aX;if((c[_>>2]|0)==0){aZ=(+h[5947]+ +h[5945])*.5}else{aZ=+h[x+(aL*232&-1)+64>>3]}h[5898]=aZ;if((c[_>>2]|0)==0){a$=0.0}else{a$=+h[x+(aL*232&-1)+72>>3]}h[5899]=a$;if((c[_>>2]|0)==1){a0=+h[5944]}else{a0=(+h[5946]+ +h[5944])*.5}h[5908]=a0;if((c[_>>2]|0)==1){a1=+h[5945]}else{a1=(+h[5947]+ +h[5945])*.5}h[5909]=a1;h[5910]=0.0;c[11802]=0}do{if(aK<<24>>24==0){if(+h[5908]!=+h[5897]){a2=1;break}if(+h[5909]!=+h[5898]){a2=1;break}a2=+h[5910]!=+h[5899]&1}else{a2=1}}while(0);a[47128]=a2;aK=x+(aL*232&-1)+224|0;_=c[aK>>2]|0;c[11800]=_;ae=c[12208]|0;c[11792]=a[47816+(ae>>>0<3?ae:3)|0]|0;ae=c[x+(aL*232&-1)+128>>2]|0;c[11790]=ae;a[47224]=0;a[47232]=0;c[7638]=-1;c[8800]=0;c[11946]=aL;if((_|0)!=0|(c[12204]|0)>0){break}if((a[47856]&1)==0){break}if((a[47584]&1)==0){break}if((a[47464]&1)!=0){break}_=c[11876]|0;af=c[11950]|0;if((_|0)>0){O=0;aJ=0;while(1){ac=((c[af+(aJ*12&-1)>>2]|0)+O|0)+(e[af+(aJ*12&-1)+8>>1]|0)|0;ai=aJ+1|0;if((ai|0)<(_|0)){O=ac;aJ=ai}else{a3=ac;break}}}else{a3=0}aJ=c[11786]|0;O=aa((aJ|0)<1?1:aJ,(c[af+(_*12&-1)>>2]|0)+a3|0);aJ=c[11787]|0;aL=aa(O,(aJ|0)<1?1:aJ);aJ=c[11788]|0;O=aa(aL,(aJ|0)<1?1:aJ)+ae|0;aJ=ut(O)|0;do{if((aJ|0)==0){gk();aL=ut(O)|0;if((aL|0)!=0){a4=aL;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=184632,v)|0);return 0}else{a4=aJ}}while(0);c[11800]=a4;c[aK>>2]=a4;aJ=cd(c[11800]|0,1,O|0,c[12892]|0)|0;if((aJ|0)==(O|0)){break}uh(-1,184408,(v=i,i=i+16|0,c[v>>2]=O,c[v+8>>2]=aJ,v)|0);a[47736]=1;y=-1;i=k;return y|0}}while(0);L6578:do{if(!(a[47736]|0)){a4=s;a3=s;a2=s|0;aU=s;aV=s;aS=f+8|0;aT=f+16|0;aR=t|0;aN=t+8|0;aJ=aN;ae=aN;aN=t+8|0;_=0;L6580:while(1){if(a[47224]|0){K=4629;break}if(a[47232]|0){K=4631;break}af=c[11790]|0;if((af|0)==0){a5=0}else{aL=c[11800]|0;if((aL|0)==0){if((eR(af)|0)!=0){y=-1;K=4966;break}}else{c[11800]=aL+af}c[11790]=0;a5=0}while(1){af=c[(c[11950]|0)+(a5*12&-1)>>2]|0;do{if((af|0)!=0){aL=c[11800]|0;if((aL|0)==0){if((eR(af)|0)==0){break}else{y=-1;K=4967;break L6580}}else{c[11800]=aL+af;break}}}while(0);if((a5|0)==(c[11876]|0)){K=4642;break}do{if((c[11858]|0)==0){af=c[11800]|0;aL=(c[11950]|0)+(a5*12&-1)+8|0;x=b[aL>>1]|0;if((af|0)==0){if((cd(ag|0,x&65535|0,1,c[12892]|0)|0)==1){break}else{K=4649;break L6580}}if(x<<16>>16==0){break}else{a6=0;a7=af}while(1){af=a7+1|0;c[11800]=af;a[ag+a6|0]=a[a7]|0;x=a6+1|0;if((x|0)<(e[aL>>1]|0|0)){a6=x;a7=af}else{break}}}else{a[ag]=0}}while(0);aL=c[11792]|0;af=c[11950]|0;x=e[af+(a5*12&-1)+8>>1]|0;do{if((aL-2|0)>>>0<2){ac=x-1|0;if((ac|0)>0){a9=0;ba=ac}else{break}do{ac=ag+a9|0;ai=a[ac]|0;ad=ag+ba|0;a[ac]=a[ad]|0;a[ad]=ai;a9=a9+1|0;ba=ba-1|0;}while((a9|0)<(ba|0))}}while(0);do{if((aL-1|0)>>>0<2){ai=x-1|0;if((ai|0)>0){bb=ai}else{break}do{ai=ag+(bb-1|0)|0;ad=a[ai]|0;ac=ag+bb|0;a[ai]=a[ac]|0;a[ac]=ad;bb=bb-2|0;}while((bb|0)>0)}}while(0);x=c[af+(a5*12&-1)+4>>2]|0;if((x|0)==0){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+(a[ag]|0|0);bc=aL}else if((x|0)==1){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+((d[ag]|0)>>>0);bc=aL}else if((x|0)==2){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+(b[a4>>1]|0|0);bc=aL}else if((x|0)==3){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+((e[a4>>1]|0)>>>0);bc=aL}else if((x|0)==4){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+(c[a3>>2]|0);bc=aL}else if((x|0)==5){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+((c[a3>>2]|0)>>>0>>>0);bc=aL}else if((x|0)==6){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+(c[a3>>2]|0);bc=aL}else if((x|0)==7){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+((c[a3>>2]|0)>>>0>>>0);bc=aL}else if((x|0)==10){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+((c[a2>>2]|0)>>>0)+ +(c[a2+4>>2]|0)*4294967296.0;bc=aL}else if((x|0)==11){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+((c[a2>>2]|0)>>>0)+ +((c[a2+4>>2]|0)>>>0)*4294967296.0;bc=aL}else if((x|0)==8){aL=c[11952]|0;h[aL+(a5*24&-1)>>3]=+g[aU>>2];bc=aL}else if((x|0)==9){x=c[11952]|0;h[x+(a5*24&-1)>>3]=+h[aV>>3];bc=x}else{K=4669;break L6580}c[bc+(a5*24&-1)+8>>2]=1;c[(c[11952]|0)+(a5*24&-1)+12>>2]=0;x=a[47544]|0;if((x&1)!=0){bd=x;break}a5=a5+1|0}if((K|0)==4642){K=0;bd=a[47544]|0}L6636:do{if((bd&1)==0){c[11942]=(c[7638]|0)+1;if((a5|0)==(c[11876]|0)){K=4702}else{K=4697;break L6580}}else{do{if((a[47464]&1)!=0){x=c[12224]|0;if(!((x|0)!=0|_)){h[5902]=+h[(c[11952]|0)+(a5*24&-1)>>3];bf=1;break L6636}if((c[12222]|x|c[12220]|0)!=0){break}x=c[11802]|0;aL=c[11786]|0;if((x|0)>=(aL|0)){break}ad=c[11784]|0;if((x|0)==0){ac=db(ad,aL<<2,183672)|0;aL=ac;c[11784]=aL;if((ac|0)==0){K=4682;break L6580}bg=c[11802]|0;bh=aL}else{bg=x;bh=ad}g[bh+(bg<<2)>>2]=+h[(c[11952]|0)+(a5*24&-1)>>3];ad=(c[11802]|0)+1|0;c[11802]=ad;bf=_&(ad|0)!=(c[11786]|0);break L6636}}while(0);c[11942]=~~+h[(c[11952]|0)+(a5*24&-1)>>3];af=c[11876]|0;if((af|0)>0){bi=af}else{K=4702;break}while(1){af=bi-1|0;if((bi|0)==1){ad=c[12224]|0;if((a[47464]&1)==0){bj=+(ad|0)}else{bj=+g[(c[11784]|0)+(ad<<2)>>2]}ad=c[11952]|0;h[ad+(af*24&-1)>>3]=bj;bk=ad}else if((bi|0)==2){if((a[47464]&1)==0){bl=+(c[12222]|0)}else{bl=+h[5902]}ad=c[11952]|0;h[ad+(af*24&-1)>>3]=bl;bk=ad}else{ad=c[11952]|0;h[ad+(af*24&-1)>>3]=+h[ad+(a5*24&-1)>>3];bk=ad}c[bk+(af*24&-1)+8>>2]=1;c[(c[11952]|0)+(af*24&-1)+12>>2]=0;if((af|0)>0){bi=af}else{K=4702;break}}}}while(0);do{if((K|0)==4702){K=0;af=c[12224]|0;ad=c[12222]|0;x=c[12220]|0;aL=af+1|0;c[12224]=aL;ac=c[11786]|0;do{if(!((ac|0)<1|(aL|0)<(ac|0))){c[12224]=0;ai=ad+1|0;c[12222]=ai;a[47224]=1;ax=c[11787]|0;if((ax|0)<0|(ai|0)<(ax|0)){break}c[12222]=0;ax=x+1|0;c[12220]=ax;ai=c[11788]|0;if((ai|0)<0|(ax|0)<(ai|0)){break}c[12220]=0;a[47232]=1;ai=(c[12170]|0)+1|0;c[12170]=ai;if((ai|0)<(c[11864]|0)){break}a[47736]=1}}while(0);ac=c[11946]|0;aL=c[11898]|0;if((ac|0)<(aL|0)|(ac|0)>(c[11762]|0)){bf=_;break}if(((ac-aL|0)%(c[11904]|0)&-1|0)!=0){bf=_;break}aL=c[8800]|0;ac=c[10808]|0;if((aL|0)<(ac|0)|(aL|0)>(c[8834]|0)){bf=_;break}if(((aL-ac|0)%(c[10874]|0)&-1|0)!=0){bf=_;break}ac=(c[7638]|0)+1|0;c[7638]=ac;aL=c[10806]|0;if((ac|0)<(aL|0)|(ac|0)>(c[8832]|0)){bf=_;break}if(((ac-aL|0)%(c[10872]|0)&-1|0)!=0){bf=_;break}c[11874]=c[11876];aL=c[11870]|0;ac=(aL|0)!=0?aL:7;aL=(ac|0)>(j|0)?j:ac;L6680:do{if((aL|0)>0){a1=+(x|0);a0=+(ad|0);a$=+(af|0);ac=0;L6682:while(1){ai=c[1048+(ac*12&-1)>>2]|0;ax=c[1056+(ac*12&-1)>>2]|0;do{if((ax|0)==0){if((ai|0)==(-5|0)){aH=c[(c[11944]|0)+12>>2]|0;if((aH|0)==400|(aH|0)==416){aZ=a1*+h[5907];h[f+(ac<<3)>>3]=aZ;bm=aZ;break}else{aZ=+h[c[11952]>>3];h[f+(ac<<3)>>3]=aZ;bm=aZ;break}}else if((ai|0)==(-4|0)){aZ=a0*+h[5906];h[f+(ac<<3)>>3]=aZ;bm=aZ;break}else if((ai|0)==(-3|0)){aZ=a$*+h[5905];h[f+(ac<<3)>>3]=aZ;bm=aZ;break}else if((ai|0)==(-2|0)){aZ=+(c[11946]|0);h[f+(ac<<3)>>3]=aZ;bm=aZ;break}else if((ai|0)==(-1|0)){aZ=+(c[8800]|0);h[f+(ac<<3)>>3]=aZ;bm=aZ;break}else if((ai|0)==0){aZ=+(c[11942]|0);h[f+(ac<<3)>>3]=aZ;bm=aZ;break}else{if((ai|0)<1){K=4738;break L6580}if((ai|0)>(c[11874]|0)){break L6682}aH=ai-1|0;aq=c[11952]|0;aF=c[aq+(aH*24&-1)+8>>2]|0;if((aF|0)==(-5|0)){y=-5;K=4971;break L6580}else if((aF|0)!=1){break L6682}aZ=+h[aq+(aH*24&-1)>>3];h[f+(ac<<3)>>3]=aZ;bm=aZ;break}}else{a[43504]=1;e4(ax,t);a[43504]=0;if((a[1960]&1)!=0){y=-2;K=4970;break L6580}aH=c[aR>>2]|0;if((aH|0)==3){aq=c[aJ>>2]|0;if((c[1052+(ac*12&-1)>>2]|0)==1){aF=(uA(aq|0)|0)+3|0;aG=ut(aF)|0;if((aG|0)==0){gk();p=ut(aF)|0;if((p|0)==0){K=4721;break L6580}else{bn=p}}else{bn=aG}a[bn]=34;aG=bn+1|0;uB(aG|0,aq|0);aG=bn+(uA(bn|0)|0)|0;w=34;a[aG]=w&255;w=w>>8;a[aG+1|0]=w&255;aG=47088+(ac<<2)|0;uu(c[aG>>2]|0);c[aG>>2]=bn;c[47056+(ac<<2)>>2]=bn}uu(aq);c[aR>>2]=1;bm=+h[f+(ac<<3)>>3];break}else if((aH|0)==1){bo=+(c[ae>>2]|0)}else if((aH|0)==2){bo=+h[aN>>3]}else{K=4726;break L6580}h[f+(ac<<3)>>3]=bo;bm=bo}}while(0);if((cg(+bm)|0)==0){if((a[47584]&1)==0){y=-2;K=4972;break L6580}}ax=ac+1|0;if((ax|0)<(aL|0)){ac=ax}else{bp=1;bq=ax;break L6680}}bp=(c[11870]|0)==0&1;bq=ac}else{bp=1;bq=0}}while(0);do{if((a[47128]&1)!=0){a$=+h[f>>3]- +h[5908];a0=+h[aS>>3]- +h[5909];a1=a$*+h[5911]+a0*+h[5912];h[f>>3]=a1;aZ=a$*+h[5913]+a0*+h[5914];h[aS>>3]=aZ;if((c[11856]|0)==2){a0=+h[aT>>3]- +h[5910];a$=a1*+h[5915]+aZ*+h[5916]+a0*+h[5917];h[f>>3]=a$;aX=a1*+h[5918]+aZ*+h[5919]+a0*+h[5920];h[aS>>3]=aX;h[aT>>3]=a1*+h[5921]+aZ*+h[5922]+a0*+h[5923];br=a$;bs=aX}else{br=a1;bs=aZ}h[f>>3]=br+ +h[5897];h[aS>>3]=bs+ +h[5898];if((c[11856]|0)!=2){break}h[aT>>3]=+h[5899]+ +h[aT>>3]}}while(0);if((bp|0)==0){bf=_}else{K=4752;break L6580}}}while(0);if(a[47736]|0){break L6578}else{_=bf}}if((K|0)==4629){a[47224]=0;c[7638]=-1;c[8800]=(c[8800]|0)+1;y=-3;i=k;return y|0}else if((K|0)==4631){a[47232]=0;c[8800]=0;y=-4;i=k;return y|0}else if((K|0)==4649){a[47736]=1;y=-1;i=k;return y|0}else if((K|0)==4669){uf(-1,184064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((K|0)==4682){uf(-1,183240,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((K|0)==4697){if((cj(c[12892]|0)|0)==0){uf(-1,c[6942]|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((a5|0)!=0){uf(-1,182696,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}a[47736]=1;y=-1;i=k;return y|0}else if((K|0)==4721){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=181200,v)|0);return 0}else if((K|0)==4726){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((K|0)==4738){uf(-1,180832,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((K|0)==4752){_=c[11870]|0;if((c[11872]|0)>0){aT=u|0;aS=u+8|0;aN=_;while(1){ae=1052+(aN*12&-1)|0;do{if((c[ae>>2]|0)>2){aR=c[1056+(aN*12&-1)>>2]|0;if((aR|0)==0){break}a[43504]=1;e4(aR,u);a[43504]=0;aR=c[ae>>2]|0;if((aR|0)==4){bt=0;bu=6}else if((aR|0)==5){bt=1;bu=1}else if((aR|0)==6){bt=1;bu=5}else if((aR|0)==7){bt=2;bu=0}else if((aR|0)==8){bt=2;bu=3}else{bt=0;bu=2}if((c[aT>>2]|0)!=3){break}aR=c[aS>>2]|0;dp(bu,aR,+h[f+(bt<<3)>>3],-1);uu(aR);c[aT>>2]=1}}while(0);ae=aN+1|0;aR=c[11870]|0;if((ae|0)<((c[11872]|0)+aR|0)){aN=ae}else{bv=aR;break}}}else{bv=_}if((bv|0)==0|(bq|0)==(bv|0)|(bq|0)==(j|0)){y=bq;i=k;return y|0}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=180528,v)|0);return 0}}else if((K|0)==4966){i=k;return y|0}else if((K|0)==4967){i=k;return y|0}else if((K|0)==4970){i=k;return y|0}else if((K|0)==4971){i=k;return y|0}else if((K|0)==4972){i=k;return y|0}}}while(0);a[47736]=1;y=-1;i=k;return y|0}function eD(b){b=b|0;var c=0,d=0,e=0,f=0,g=0,h=0,j=0;c=i;i=i+8|0;d=c|0;if((b|0)==0){e=0;i=c;return e|0}do{if((a[b]|0)==34){f=b+1|0;if((f|0)==0){g=0}else{g=bP(f|0)|0}a[g+(bk(g|0,132696)|0)|0]=0;h=g}else{f=a[47120]|0;if(f<<24>>24==0){j=bP(b|0)|0;a[j+(bk(j|0,84256)|0)|0]=0;h=j;break}else{j=d|0;a[j]=f;a[d+1|0]=34;a[d+2|0]=0;f=bP(b|0)|0;a[f+(bk(f|0,j|0)|0)|0]=0;h=f;break}}}while(0);ua(h);e=h;i=c;return e|0}function eE(b){b=b|0;var d=0,e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0.0;d=i;i=i+8|0;e=d|0;f=e;if((cd(f|0,4,1,b|0)|0)!=1){if((cj(b|0)|0)==0){uf(-1,c[6942]|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0.0}else{uf(-1,74632,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0.0}}b=c[12208]|0;h=a[47816+(b>>>0<3?b:3)|0]|0;if((h-2|0)>>>0<2){b=0;j=3;do{k=f+b|0;l=a[k]|0;m=f+j|0;a[k]=a[m]|0;a[m]=l;b=b+1|0;j=j-1|0;}while((b|0)<(j|0))}if((h-1|0)>>>0>=2){n=+g[e>>2];i=d;return+n}h=f+2|0;j=a[h]|0;b=f+3|0;a[h]=a[b]|0;a[b]=j;j=a[f]|0;b=f+1|0;a[f]=a[b]|0;a[b]=j;n=+g[e>>2];i=d;return+n}function eF(b){b=b|0;var d=0,e=0,f=0,g=0,j=0.0,k=0;d=i;e=b+8|0;f=c[e>>2]|0;g=f-1|0;if((f|0)==0){f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=+(c[11942]|0);k=f+1|0;c[6354]=k;c[14296+(k*24&-1)>>2]=2;h[14304+(k*24&-1)>>3]=j;h[14312+(k*24&-1)>>3]=0.0;i=d;return}do{if((g|0)<(c[11874]|0)){k=c[11952]|0;if((c[k+(g*24&-1)+8>>2]|0)!=1){break}f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=+h[k+(g*24&-1)>>3];k=f+1|0;c[6354]=k;c[14296+(k*24&-1)>>2]=2;h[14304+(k*24&-1)>>3]=j;h[14312+(k*24&-1)>>3]=0.0;i=d;return}}while(0);a[1960]=1;g=c[6354]|0;if((g|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=g+1|0;c[6354]=k;g=14296+(k*24&-1)|0;k=b;c[g>>2]=c[k>>2];c[g+4>>2]=c[k+4>>2];c[g+8>>2]=c[k+8>>2];c[g+12>>2]=c[k+12>>2];c[g+16>>2]=c[k+16>>2];c[g+20>>2]=c[k+20>>2];if((c[b>>2]|0)!=3){i=d;return}b=c[e>>2]|0;if((b|0)==0){i=d;return}e=bP(b|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=e;i=d;return}function eG(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0,m=0.0,n=0.0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;k=+h[f>>3];l=c[f>>2]|0;h[d>>3]=k;m=+h[14312+(e*24&-1)>>3];e=l;l=e;if((a[43504]&1)==0){uf((c[13898]|0)-1|0,74216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((g|0)==1){n=+(e|0);o=5045}else if((g|0)==2){n=k;o=5045}else if((g|0)==3){e=d;f=c[11874]|0;L6842:do{if((f|0)>0){p=0;q=f;while(1){r=c[(c[11952]|0)+(p*24&-1)+16>>2]|0;if((r|0)==0){s=q}else{if(ui(l,r+((a[r]|0)==34&1)|0)|0){break}s=c[11874]|0}r=p+1|0;if((r|0)<(s|0)){p=r;q=s}else{t=-9;break L6842}}q=p+1|0;if((c[11902]|0)!=0){t=q;break}r=c[(c[11952]|0)+(p*24&-1)+16>>2]|0;if((r|0)==0){u=0}else{u=bP(r|0)|0}c[11902]=u;t=q}else{t=-9}}while(0);uu(c[e>>2]|0);w=t;x=1}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((o|0)==5045){w=~~n;x=g}if((w|0)==(-2|0)){c[d>>2]=c[11946];g=c[6354]|0;if((g|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=g+1|0;c[6354]=o;g=14296+(o*24&-1)|0;c[g>>2]=1;c[g+4>>2]=j;h[14304+(o*24&-1)>>3]=+h[d>>3];h[14312+(o*24&-1)>>3]=m;i=b;return}else if((w|0)==(-1|0)){c[d>>2]=c[8800];o=c[6354]|0;if((o|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=o+1|0;c[6354]=g;o=14296+(g*24&-1)|0;c[o>>2]=1;c[o+4>>2]=j;h[14304+(g*24&-1)>>3]=+h[d>>3];h[14312+(g*24&-1)>>3]=m;i=b;return}else if((w|0)==0){n=+(c[11942]|0);h[d>>3]=n;g=c[6354]|0;if((g|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=g+1|0;c[6354]=o;g=14296+(o*24&-1)|0;c[g>>2]=2;c[g+4>>2]=j;h[14304+(o*24&-1)>>3]=n;h[14312+(o*24&-1)>>3]=0.0;i=b;return}else{do{if(!((w|0)<1|(w|0)>(c[11874]|0))){o=w-1|0;g=c[11952]|0;if((c[g+(o*24&-1)+8>>2]|0)!=1){break}n=+h[g+(o*24&-1)>>3];h[d>>3]=n;o=c[6354]|0;if((o|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=o+1|0;c[6354]=g;o=14296+(g*24&-1)|0;c[o>>2]=2;c[o+4>>2]=j;h[14304+(g*24&-1)>>3]=n;h[14312+(g*24&-1)>>3]=0.0;i=b;return}}while(0);a[1960]=1;w=c[6354]|0;if((w|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=w+1|0;c[6354]=g;w=14296+(g*24&-1)|0;c[w>>2]=x;c[w+4>>2]=j;j=c[d>>2]|0;h[14304+(g*24&-1)>>3]=+h[d>>3];h[14312+(g*24&-1)>>3]=m;g=j;if((x|0)!=3|(g|0)==0){i=b;return}x=bP(g|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=x;i=b;return}}function eH(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0.0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0.0;b=i;i=i+104|0;d=b|0;e=b+8|0;f=b+40|0;g=b+72|0;j=c[6354]|0;if((j|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=j-1;k=14296+(j*24&-1)|0;l=c[k>>2]|0;m=c[k+4>>2]|0;k=14304+(j*24&-1)|0;n=+h[k>>3];o=c[k>>2]|0;h[d>>3]=n;p=+h[14312+(j*24&-1)>>3];j=o;o=j;if((a[43504]&1)==0){q=c[13898]|0;r=q-1|0;uf(r,73448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[47584]&1)!=0){q=c[13898]|0;r=q-1|0;uf(r,73448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((l|0)==3){r=d;q=c[11874]|0;L6902:do{if((q|0)>0){k=0;s=q;while(1){t=c[(c[11952]|0)+(k*24&-1)+16>>2]|0;if((t|0)==0){u=s}else{if(ui(o,t+((a[t]|0)==34&1)|0)|0){break}u=c[11874]|0}t=k+1|0;if((t|0)<(u|0)){k=t;s=u}else{w=5088;break L6902}}s=k+1|0;if((c[11902]|0)==0){t=c[(c[11952]|0)+(k*24&-1)+16>>2]|0;if((t|0)==0){x=0}else{x=bP(t|0)|0}c[11902]=x}if((s|0)==-9){w=5088}else{y=s}}else{w=5088}}while(0);do{if((w|0)==5088){if((a[47040]&1)==0){y=-9;break}a[47040]=0;uh(-1,72840,(v=i,i=i+8|0,c[v>>2]=c[r>>2],v)|0);x=c[11874]|0;if((x|0)>0){z=0;A=x}else{y=-9;break}while(1){x=c[(c[11952]|0)+(z*24&-1)+16>>2]|0;do{if((x|0)==0){B=A}else{u=x+((a[x]|0)==34&1)|0;if((a_(o|0,u|0,uA(o|0)|0)|0)!=0){B=A;break}uh(-1,225080,(v=i,i=i+16|0,c[v>>2]=z+1,c[v+8>>2]=x,v)|0);B=c[11874]|0}}while(0);x=z+1|0;if((x|0)<(B|0)){z=x;A=B}else{y=-9;break}}}}while(0);uu(c[r>>2]|0);C=y;D=1}else if((l|0)==1){E=+(j|0);w=5097}else if((l|0)==2){E=n;w=5097}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((w|0)==5097){C=~~E;D=l}if((C|0)==(-2|0)){l=e|0;e=c[11946]|0;be(l|0,21e4,(v=i,i=i+8|0,c[v>>2]=e,v)|0);c[d>>2]=l;l=c[6354]|0;if((l|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=l+1|0;c[6354]=e;l=14296+(e*24&-1)|0;c[l>>2]=3;c[l+4>>2]=m;l=c[d>>2]|0;h[14304+(e*24&-1)>>3]=+h[d>>3];h[14312+(e*24&-1)>>3]=p;e=l;if((e|0)==0){i=b;return}l=bP(e|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=l;i=b;return}else if((C|0)==(-1|0)){l=f|0;be(l|0,21e4,(v=i,i=i+8|0,c[v>>2]=c[8800],v)|0);c[d>>2]=l;l=c[6354]|0;if((l|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=l+1|0;c[6354]=f;l=14296+(f*24&-1)|0;c[l>>2]=3;c[l+4>>2]=m;l=c[d>>2]|0;h[14304+(f*24&-1)>>3]=+h[d>>3];h[14312+(f*24&-1)>>3]=p;f=l;if((f|0)==0){i=b;return}l=bP(f|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=l;i=b;return}else if((C|0)==0){l=g|0;be(l|0,21e4,(v=i,i=i+8|0,c[v>>2]=c[11942],v)|0);c[d>>2]=l;l=c[6354]|0;if((l|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=l+1|0;c[6354]=g;l=14296+(g*24&-1)|0;c[l>>2]=3;c[l+4>>2]=m;l=c[d>>2]|0;h[14304+(g*24&-1)>>3]=+h[d>>3];h[14312+(g*24&-1)>>3]=p;g=l;if((g|0)==0){i=b;return}l=bP(g|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=l;i=b;return}else{if((C|0)<1|(C|0)>(c[11874]|0)){a[1960]=1;l=c[6354]|0;if((l|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=l+1|0;c[6354]=g;l=14296+(g*24&-1)|0;c[l>>2]=D;c[l+4>>2]=m;l=c[d>>2]|0;h[14304+(g*24&-1)>>3]=+h[d>>3];h[14312+(g*24&-1)>>3]=p;g=l;if((D|0)!=3|(g|0)==0){i=b;return}D=bP(g|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=D;i=b;return}else{D=eD(c[(c[11952]|0)+((C-1|0)*24&-1)+12>>2]|0)|0;c[d>>2]=D;C=c[6354]|0;if((C|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=C+1|0;c[6354]=g;C=14296+(g*24&-1)|0;c[C>>2]=3;c[C+4>>2]=m;m=c[d>>2]|0;h[14304+(g*24&-1)>>3]=+h[d>>3];h[14312+(g*24&-1)>>3]=p;g=m;if((g|0)!=0){m=bP(g|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=m}uu(D);i=b;return}}}function eI(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0;b=i;i=i+8|0;d=b|0;if((a[43504]&1)==0){uf((c[13898]|0)-1|0,223600,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=c[g>>2]|0;k=c[g+4>>2]|0;g=14304+(e*24&-1)|0;l=+h[g>>3];m=c[g>>2]|0;h[d>>3]=l;n=+h[14312+(e*24&-1)>>3];e=m;if((j|0)==2){o=l;p=f}else if((j|0)==1){o=+(e|0);p=f}else if((j|0)==3){l=+uz(e,0);o=l;p=c[6354]|0}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13488]=~~o;c[d>>2]=222976;if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=p+1|0;c[6354]=e;p=14296+(e*24&-1)|0;c[p>>2]=3;c[p+4>>2]=k;k=c[d>>2]|0;h[14304+(e*24&-1)>>3]=+h[d>>3];h[14312+(e*24&-1)>>3]=n;e=k;if((e|0)==0){i=b;return}k=bP(e|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=k;i=b;return}function eJ(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0,p=0.0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,w=0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=f|0;j=c[g>>2]|0;k=f+4|0;f=c[k>>2]|0;l=14304+(d*24&-1)|0;m=+h[l>>3];n=c[l>>2]|0;h[b>>3]=m;o=14312+(d*24&-1)|0;p=+h[o>>3];q=n;do{if((j|0)==2){r=+P(+m);s=+P(+p);if(p==0.0){t=r;break}if(r>s){u=s/r;t=r*+Q(+(u*u+1.0));break}else{u=r/s;t=s*+Q(+(u*u+1.0));break}}else if((j|0)==1){t=+(((q|0)>-1?q:-q|0)|0)}else{uf(-1,211136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);q=~~t;j=q-1|0;if((q|0)>0&(j|0)<(c[11874]|0)){w=(c[(c[11952]|0)+(j*24&-1)+8>>2]|0)==1&1}else{w=0}c[b>>2]=w;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=d;c[g>>2]=1;c[k>>2]=f;h[l>>3]=+h[b>>3];h[o>>3]=p;i=a;return}}function eK(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0,t=0,u=0,w=0,x=0;b=i;i=i+56|0;d=b|0;e=b+48|0;h[e>>3]=0.0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=14296+(f*24&-1)|0;k=c[j>>2]|0;l=c[j+4>>2]|0;j=14304+(f*24&-1)|0;m=+h[j>>3];n=+h[14312+(f*24&-1)>>3];f=c[j>>2]|0;j=f;do{if((k|0)==1){o=+(((f|0)>-1?f:-f|0)|0)}else if((k|0)==2){p=+P(+m);q=+P(+n);if(n==0.0){o=p;break}if(p>q){r=q/p;o=p*+Q(+(r*r+1.0));break}else{r=p/q;o=q*+Q(+(r*r+1.0));break}}else{uf(-1,211136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);s=~~o;if((a[43504]&1)==0){uf((c[13898]|0)-1|0,222536,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t=c[48840+(c[12902]<<2)>>2]|0;do{if((s|0)<1|(s|0)>(c[11874]|0)){u=g}else{w=c[(c[11952]|0)+((s-1|0)*24&-1)+12>>2]|0;if((w|0)==0){u=g;break}x=(t1(w,64813+(t*688&-1)|0,d,e)|0)==0;w=c[6354]|0;if(x){u=w;break}if((w|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=+h[e>>3];r=+t2(d)+o;x=w+1|0;c[6354]=x;w=14296+(x*24&-1)|0;c[w>>2]=2;c[w+4>>2]=l;h[14304+(x*24&-1)>>3]=r;h[14312+(x*24&-1)>>3]=0.0;i=b;return}}while(0);a[1960]=1;if((u|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=u+1|0;c[6354]=d;u=14296+(d*24&-1)|0;c[u>>2]=k;c[u+4>>2]=l;h[14304+(d*24&-1)>>3]=m;h[14312+(d*24&-1)>>3]=n;if((k|0)!=3|(f|0)==0){i=b;return}f=bP(j|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=f;i=b;return}function eL(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0;d=i;e=c[11902]|0;if((e|0)==0){i=d;return}if((c[b+12>>2]|0)==392&(c[9670]|0)==2){dp(2,e,+(c[b+280>>2]|0)+ +h[(c[b+276>>2]|0)+16>>3],-1);uu(c[11902]|0);c[11902]=0;i=d;return}f=b+16|0;g=c[f>>2]|0;do{if((g|0)!=0){if((a[b+21|0]&1)!=0){break}j=aQ(g|0,222976)|0;if((j|0)==0){i=d;return}k=uA(g|0)|0;l=(uA(e|0)|0)+k|0;k=ut(l)|0;do{if((k|0)==0){gk();m=ut(l)|0;if((m|0)!=0){n=m;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=221744,v)|0)}else{n=k}}while(0);a[j]=0;k=c[f>>2]|0;l=c[11902]|0;m=j+12|0;be(n|0,172512,(v=i,i=i+24|0,c[v>>2]=k,c[v+8>>2]=l,c[v+16>>2]=m,v)|0);uu(c[11902]|0);c[11902]=n}}while(0);if((a[b+22|0]&1)!=0){i=d;return}n=c[f>>2]|0;if((n|0)!=0){uu(n)}a[b+20|0]=a[36231]&1^1;c[f>>2]=c[11902];c[11902]=0;i=d;return}function eM(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0,s=0,t=0,u=0.0;d=i;i=i+48|0;e=d|0;f=d+24|0;g=c[13898]|0;j=g+1|0;c[13898]=j;k=(c[8272]|0)>(j|0);L7071:do{if(k){l=c[1054]|0;if((a[l+(j*40&-1)|0]&1)!=0){m=c[l+(j*40&-1)+36>>2]|0;n=l+(j*40&-1)+32|0;l=c[10036]|0;o=0;while(1){if((o|0)>=(m|0)){p=5212;break}if((a[l+((c[n>>2]|0)+o|0)|0]|0)==(a[o+199040|0]|0)){o=o+1|0}else{break}}do{if((p|0)==5212){if((o|0)!=1){break}c[13898]=g+2;n=is(f)|0;l=c[n>>2]|0;if((l|0)==2){q=+h[n+8>>3]}else if((l|0)==3){q=+uz(c[n+8>>2]|0,0)}else if((l|0)==1){q=+(c[n+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=f|0;if((c[n>>2]|0)==3){uu(c[f+8>>2]|0);c[n>>2]=1}n=~~q;c[13488]=n;c[13898]=(c[13898]|0)+1;r=n;break L7071}}while(0);if(!k){p=5236;break}}o=c[1054]|0;n=(a[o+(j*40&-1)|0]&1)==0;do{if(!n){l=c[o+(j*40&-1)+36>>2]|0;m=o+(j*40&-1)+32|0;s=c[10036]|0;t=0;while(1){if((t|0)>=(l|0)){p=5226;break}if((a[s+((c[m>>2]|0)+t|0)|0]|0)==(a[t+103664|0]|0)){t=t+1|0}else{p=5227;break}}if((p|0)==5226){if((t|0)==1|n^1){p=5236;break L7071}else{break}}else if((p|0)==5227){if(n){break}else{p=5236;break L7071}}}}while(0);n=is(e)|0;o=c[n>>2]|0;if((o|0)==1){u=+(c[n+8>>2]|0)}else if((o|0)==2){u=+h[n+8>>3]}else if((o|0)==3){u=+uz(c[n+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=e|0;if((c[n>>2]|0)==3){uu(c[e+8>>2]|0);c[n>>2]=1}n=~~u;c[13488]=n;r=n}else{p=5236}}while(0);do{if((p|0)==5236){if((c[11870]|0)==1){e=c[262]|0;c[13488]=e;r=e;break}if((c[b+8>>2]|0)==3){e=c[268]|0;c[13488]=e;r=e;break}else{e=c[265]|0;c[13488]=e;r=e;break}}}while(0);if((r|0)!=-99){i=d;return}c[b+16>>2]=bP(222976)|0;i=d;return}function eN(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0,u=0,w=0,x=0.0,y=0.0,z=0;d=i;aI(214576,68,1,b|0);e=c[11862]|0;f=(e|0)==0;g=f?1:e;e=f?48440:c[12168]|0;aI(213904,15,1,b|0);f=c[12202]|0;if((f|0)>-1){j=c[48696+(f<<3)>>2]|0;aK(j|0,b|0)}else{aI(135560,4,1,b|0)}cf(b|0,212192,(v=i,i=i+8|0,c[v>>2]=c[47744+(c[12206]<<2)>>2],v)|0);j=c[11962]|0;cf(b|0,211640,(v=i,i=i+8|0,c[v>>2]=(j|0)!=0?j:135560,v)|0);if((g|0)>0){k=0}else{i=d;return}do{cf(b|0,211256,(v=i,i=i+8|0,c[v>>2]=k,v)|0);aI(210760,16,1,b|0);j=c[e+(k*232&-1)>>2]|0;do{if((j|0)<0){aI(210016,3,1,b|0);l=1}else{cf(b|0,21e4,(v=i,i=i+8|0,c[v>>2]=j,v)|0);f=c[e+(k*232&-1)+4>>2]|0;if((f|0)<=0){l=1;break}cf(b|0,209320,(v=i,i=i+8|0,c[v>>2]=f,v)|0);f=c[e+(k*232&-1)+8>>2]|0;if((f|0)<=0){l=2;break}cf(b|0,209320,(v=i,i=i+8|0,c[v>>2]=f,v)|0);l=3}}while(0);j=e+(k*232&-1)+124|0;cf(b|0,209120,(v=i,i=i+8|0,c[v>>2]=(a[j]&1)!=0?136424:136208,v)|0);do{if((a[j]&1)!=0){aI(207296,17,1,b|0);if((c[e+(k*232&-1)+12>>2]|0)==-1){aI(206608,6,1,b|0);m=0}else{m=1}f=l>>>0>1;L7143:do{if(f){if((c[e+(k*232&-1)+16>>2]|0)==-1){n=m?179864:86120;cf(b|0,205848,(v=i,i=i+8|0,c[v>>2]=n,v)|0);o=0}else{o=m}do{if((l|0)==3){if((c[e+(k*232&-1)+20>>2]|0)!=-1){p=1;break}n=o?179864:86120;cf(b|0,204712,(v=i,i=i+8|0,c[v>>2]=n,v)|0);q=1;r=5268;break L7143}else{p=0}}while(0);if(o){s=p;r=5267}else{q=p;r=5268}}else{if(m){s=0;r=5267;break}t=+h[e+(k*232&-1)+24>>3];cf(b|0,204152,(v=i,i=i+8|0,h[v>>3]=t,v)|0);u=0}}while(0);if((r|0)==5267){r=0;aI(204448,11,1,b|0);q=s;r=5268}do{if((r|0)==5268){r=0;t=+h[e+(k*232&-1)+24>>3];cf(b|0,204152,(v=i,i=i+8|0,h[v>>3]=t,v)|0);if(f){t=+h[e+(k*232&-1)+32>>3];cf(b|0,203944,(v=i,i=i+8|0,h[v>>3]=t,v)|0);if(!q){u=0;break}}else{if(!q){u=0;break}}t=+h[e+(k*232&-1)+40>>3];cf(b|0,203696,(v=i,i=i+8|0,h[v>>3]=t,v)|0);u=1}}while(0);n=e+(k*232&-1)+48|0;w=c[n>>2]|0;if((w|0)==1){aI(203456,13,1,b|0)}else if((w|0)==2){aI(203128,13,1,b|0)}if(((c[n>>2]|0)-1|0)>>>0<2){t=+h[e+(k*232&-1)+56>>3];x=+h[e+(k*232&-1)+64>>3];y=+h[e+(k*232&-1)+72>>3];cf(b|0,202912,(v=i,i=i+24|0,h[v>>3]=t,h[v+8>>3]=x,h[v+16>>3]=y,v)|0)}y=+h[e+(k*232&-1)+80>>3];cf(b|0,202576,(v=i,i=i+8|0,h[v>>3]=y,v)|0);y=+h[e+(k*232&-1)+88>>3];x=+h[e+(k*232&-1)+96>>3];t=+h[e+(k*232&-1)+104>>3];cf(b|0,201912,(v=i,i=i+24|0,h[v>>3]=y,h[v+8>>3]=x,h[v+16>>3]=t,v)|0);n=e+(k*232&-1)+112|0;w=0;while(1){if(w>>>0>=12){break}if((a_(n|0,48188+(w<<4)|0,12)|0)==0){r=5280;break}else{w=w+1|0}}if((r|0)==5280){r=0;aI(201312,12,1,b|0);n=(c[e+(k*232&-1)+8>>2]|0)!=0?132824:200424;z=c[48184+(w<<4)>>2]|0;cf(b|0,n|0,(v=i,i=i+8|0,c[v>>2]=z,v)|0)}cf(b|0,199512,(v=i,i=i+8|0,c[v>>2]=c[e+(k*232&-1)+128>>2],v)|0);if(f){z=c[e+(k*232&-1)+132>>2]|0;cf(b|0,199136,(v=i,i=i+8|0,c[v>>2]=z,v)|0)}if(!u){break}cf(b|0,198848,(v=i,i=i+8|0,c[v>>2]=c[e+(k*232&-1)+136>>2],v)|0)}}while(0);aF(10,b|0);k=k+1|0;}while((k|0)<(g|0));i=d;return}function eO(a){a=a|0;var d=0,f=0,g=0,h=0,j=0;d=i;aI(198008,82,1,a|0);f=0;do{aI(197576,3,1,a|0);g=48028+(f<<4)|0;if((b[g>>1]|0)!=0){h=48024+(f<<4)|0;j=0;do{cf(a|0,103640,(v=i,i=i+8|0,c[v>>2]=c[(c[h>>2]|0)+(j<<2)>>2],v)|0);j=j+1|0;}while((j|0)<(e[g>>1]|0|0))}cf(a|0,196376,(v=i,i=i+8|0,c[v>>2]=e[48036+(f<<4)>>1]|0,v)|0);f=f+1|0;}while(f>>>0<10);aI(195800,95,1,a|0);f=0;do{aI(197576,3,1,a|0);g=47868+(f<<4)|0;if((b[g>>1]|0)!=0){j=47864+(f<<4)|0;h=0;do{cf(a|0,103640,(v=i,i=i+8|0,c[v>>2]=c[(c[j>>2]|0)+(h<<2)>>2],v)|0);h=h+1|0;}while((h|0)<(e[g>>1]|0|0))}cf(a|0,194088,(v=i,i=i+8|0,c[v>>2]=e[47876+(f<<4)>>1]|0,v)|0);if((c[47872+(f<<4)>>2]|0)==12){aI(193568,40,1,a|0)}aF(10,a|0);f=f+1|0;}while(f>>>0<10);i=d;return}function eP(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0;b=i;d=(c[13898]|0)+1|0;c[13898]=d;if((d|0)>=(c[8272]|0)){uf(d,217808,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=c[1054]|0;L7206:do{if((a[e+(d*40&-1)|0]&1)!=0){f=c[e+(d*40&-1)+36>>2]|0;g=e+(d*40&-1)+32|0;h=c[10036]|0;j=0;while(1){if((j|0)>=(f|0)){break}if((a[h+((c[g>>2]|0)+j|0)|0]|0)==(a[j+103664|0]|0)){j=j+1|0}else{break L7206}}if((j|0)!=1){break}uf(d,217808,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);d=c[12172]|0;e=c[11864]|0;if((e|0)>0){g=0;h=e;while(1){e=d+(g*232&-1)+224|0;f=c[e>>2]|0;if((f|0)==0){k=h}else{uu(f);c[e>>2]=0;k=c[11864]|0}e=g+1|0;if((e|0)<(k|0)){g=e;h=k}else{break}}}c[11864]=0;if((c[12168]|0)==0){c[12204]=c[12200];c[12208]=0;k=c[12172]|0;do{if((c[11880]|0)<1){h=db(k,232,216568)|0;g=h;c[12172]=g;if((h|0)==0){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[11880]=1;l=c[11864]|0;m=g;break}}else{l=0;m=k}}while(0);uD(m+(l*232&-1)|0,48440,232);c[11864]=(c[11864]|0)+1}else{c[12204]=c[12202];c[12208]=c[12206];l=c[11862]|0;do{if((l|0)>(c[11880]|0)){m=db(c[12172]|0,l*232&-1,216568)|0;c[12172]=m;if((m|0)==0){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[11880]=l;break}}}while(0);m=c[12172]|0;k=c[11864]|0;if((l|0)>0){g=0;h=k;while(1){uD(m+(h*232&-1)|0,48440,232);d=(c[11864]|0)+1|0;c[11864]=d;e=g+1|0;if((e|0)<(l|0)){g=e;h=d}else{n=d;break}}}else{n=k}uD(m|0,c[12168]|0,n*232&-1)}c[11856]=0;eB(0,1);c[12202]=c[12204];c[12206]=c[12208];n=c[12168]|0;m=c[11862]|0;if((m|0)>0){k=0;h=m;while(1){m=n+(k*232&-1)+224|0;g=c[m>>2]|0;if((g|0)==0){o=h}else{uu(g);c[m>>2]=0;o=c[11862]|0}m=k+1|0;if((m|0)<(o|0)){k=m;h=o}else{break}}}c[11862]=0;o=c[11864]|0;do{if((o|0)>(c[11878]|0)){h=db(c[12168]|0,o*232&-1,216568)|0;c[12168]=h;if((h|0)==0){c[11878]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[11878]=o;break}}}while(0);h=c[12168]|0;k=c[11862]|0;if((o|0)>0){p=0;q=k}else{r=k;s=h;t=c[12172]|0;u=t;w=r*232&-1;uD(s|0,u|0,w);i=b;return}while(1){uD(h+(q*232&-1)|0,48440,232);k=(c[11862]|0)+1|0;c[11862]=k;n=p+1|0;if((n|0)<(o|0)){p=n;q=k}else{r=k;break}}s=h;t=c[12172]|0;u=t;w=r*232&-1;uD(s|0,u|0,w);i=b;return}function eQ(a){a=a|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;d=i;e=c[11876]|0;if((e|0)>=(a|0)){i=d;return}if((e|0)>0){f=c[(c[11950]|0)+((e-1|0)*12&-1)+4>>2]|0}else{f=8}e=48036+(f<<4)|0;g=a;while(1){h=g+1|0;if((h|0)<=0){j=5346;break}k=c[11950]|0;if((c[11884]|0)<(h|0)){l=db(k,h*12&-1,215112)|0;c[11950]=l;c[11884]=h;m=l}else{m=k}c[m+(g*12&-1)>>2]=0;if((g|0)<=0){j=5350;break}k=c[11950]|0;if((c[11884]|0)<(g|0)){l=db(k,g*12&-1,215112)|0;c[11950]=l;c[11884]=g;n=l}else{n=k}k=g-1|0;c[n+(k*12&-1)+4>>2]=f;b[(c[11950]|0)+(k*12&-1)+8>>1]=b[e>>1]|0;if((k|0)>(c[11876]|0)){g=k}else{j=5354;break}}if((j|0)==5354){c[11876]=a;i=d;return}else if((j|0)==5350){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=215616,v)|0)}else if((j|0)==5346){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=215616,v)|0)}}function eR(b){b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;i=i+8|0;e=d|0;do{if(a[47440]|0){f=b}else{if((a[30664]&1)!=0){f=b;break}if((cp(c[12892]|0,b|0,1)|0)==0){g=0;i=d;return g|0}if((cj(c[12892]|0)|0)==0){uf(-1,c[6942]|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}a[47736]=1;g=-1;i=d;return g|0}}while(0);while(1){if((f|0)==0){g=0;h=5372;break}if((cd(e|0,1,1,c[12892]|0)|0)==1){f=f-1|0}else{break}}if((h|0)==5372){i=d;return g|0}if((cj(c[12892]|0)|0)==0){uf(-1,c[6942]|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}a[47736]=1;g=-1;i=d;return g|0}function eS(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0.0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0.0,V=0,W=0,X=0,Y=0.0;b=i;i=i+80|0;d=b|0;e=b+24|0;f=b+48|0;g=b+72|0;j=g|0;k=i;i=i+4|0;i=i+7>>3<<3;l=c[13898]|0;if((c[8272]|0)<=(l|0)){m=c[10880]|0;uf(l,m,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=c[1054]|0;if((a[n+(l*40&-1)|0]&1)==0){m=c[10880]|0;uf(l,m,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=c[n+(l*40&-1)+36>>2]|0;p=n+(l*40&-1)+32|0;q=c[10036]|0;r=0;while(1){if((r|0)>=(o|0)){break}if((a[q+((c[p>>2]|0)+r|0)|0]|0)==(a[r+115e3|0]|0)){r=r+1|0}else{s=5462;break}}if((s|0)==5462){m=c[10880]|0;uf(l,m,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((r|0)!=1){m=c[10880]|0;uf(l,m,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=c[12172]|0;r=c[11864]|0;if((r|0)>0){p=0;q=r;while(1){r=m+(p*232&-1)+224|0;o=c[r>>2]|0;if((o|0)==0){t=q}else{uu(o);c[r>>2]=0;t=c[11864]|0}r=p+1|0;if((r|0)<(t|0)){p=r;q=t}else{break}}u=c[13898]|0;w=c[1054]|0}else{u=l;w=n}c[11864]=0;n=e|0;l=e+8|0;t=g|0;q=g+1|0;p=d|0;m=d+8|0;r=f|0;o=f+8|0;x=0;y=u;u=w;L7326:while(1){w=y+1|0;c[13898]=w;L7328:do{if((a[u+(w*40&-1)|0]&1)==0){z=x+1|0;A=c[11864]|0;if((z|0)>(A|0)){B=A+1|0;if((B|0)>(c[11880]|0)){C=db(c[12172]|0,B*232&-1,216568)|0;c[12172]=C;if((C|0)==0){s=5391;break L7326}c[11880]=B;D=c[11864]|0}else{D=A}uD((c[12172]|0)+(D*232&-1)|0,48440,232);c[11864]=(c[11864]|0)+1}A=is(e)|0;B=c[A>>2]|0;if((B|0)==2){E=+h[A+8>>3]}else if((B|0)==1){E=+(c[A+8>>2]|0)}else if((B|0)==3){E=+uz(c[A+8>>2]|0,0)}else{s=5398;break L7326}if((c[n>>2]|0)==3){uu(c[l>>2]|0);c[n>>2]=1}c[(c[12172]|0)+(((c[11864]|0)-1|0)*232&-1)>>2]=~~E;A=c[13898]|0;B=c[8272]|0;if((A|0)>=(B|0)){F=z;G=A;H=B;break}C=c[1054]|0;L7346:do{if((a[C+(A*40&-1)|0]&1)==0){I=c[10036]|0;J=C+(A*40&-1)+32|0;K=C+(A*40&-1)+36|0}else{L=C+(A*40&-1)+36|0;M=c[L>>2]|0;N=C+(A*40&-1)+32|0;O=c[10036]|0;P=0;while(1){if((P|0)>=(M|0)){break}if((a[O+((c[N>>2]|0)+P|0)|0]|0)==(a[P+103664|0]|0)){P=P+1|0}else{I=O;J=N;K=L;break L7346}}if((P|0)==1){F=z;G=A;H=B;break L7328}else{I=O;J=N;K=L}}}while(0);c[k>>2]=0;C=c[K>>2]|0;uD(j|0,I+(c[J>>2]|0)|0,(C|0)<5?C:5);a[g+((C|0)>5?5:C)|0]=0;if((a[t]|0)!=120){F=z;G=A;H=B;break}ca(q|0,21e4,(v=i,i=i+8|0,c[v>>2]=k,v)|0);c[(c[12172]|0)+(((c[11864]|0)-1|0)*232&-1)+4>>2]=c[k>>2];C=(c[13898]|0)+1|0;c[13898]=C;F=z;G=C;H=c[8272]|0}else{C=c[8272]|0;if((C|0)<=(w|0)){F=x;G=w;H=C;break}M=c[u+(w*40&-1)+36>>2]|0;Q=u+(w*40&-1)+32|0;R=c[10036]|0;S=0;while(1){if((S|0)>=(M|0)){break}if((a[R+((c[Q>>2]|0)+S|0)|0]|0)==(a[S+199040|0]|0)){S=S+1|0}else{F=x;G=w;H=C;break L7328}}if((S|0)!=1){F=x;G=w;H=C;break}c[13898]=y+2;Q=x+1|0;R=c[11864]|0;if((Q|0)>(R|0)){M=R+1|0;if((M|0)>(c[11880]|0)){z=db(c[12172]|0,M*232&-1,216568)|0;c[12172]=z;if((z|0)==0){s=5418;break L7326}c[11880]=M;T=c[11864]|0}else{T=R}uD((c[12172]|0)+(T*232&-1)|0,48440,232);c[11864]=(c[11864]|0)+1}R=is(d)|0;M=c[R>>2]|0;if((M|0)==1){U=+(c[R+8>>2]|0)}else if((M|0)==2){U=+h[R+8>>3]}else if((M|0)==3){U=+uz(c[R+8>>2]|0,0)}else{s=5425;break L7326}if((c[p>>2]|0)==3){uu(c[m>>2]|0);c[p>>2]=1}c[(c[12172]|0)+(((c[11864]|0)-1|0)*232&-1)>>2]=~~U;R=c[13898]|0;M=c[8272]|0;L7377:do{if((M|0)>(R|0)){z=c[1054]|0;if((a[z+(R*40&-1)|0]&1)==0){V=R;W=M;break}B=c[z+(R*40&-1)+36>>2]|0;A=z+(R*40&-1)+32|0;z=c[10036]|0;X=0;while(1){if((X|0)>=(B|0)){break}if((a[z+((c[A>>2]|0)+X|0)|0]|0)==(a[X+148464|0]|0)){X=X+1|0}else{V=R;W=M;break L7377}}if((X|0)!=1){V=R;W=M;break}c[13898]=R+1;A=is(f)|0;z=c[A>>2]|0;if((z|0)==1){Y=+(c[A+8>>2]|0)}else if((z|0)==2){Y=+h[A+8>>3]}else if((z|0)==3){Y=+uz(c[A+8>>2]|0,0)}else{s=5438;break L7326}if((c[r>>2]|0)==3){uu(c[o>>2]|0);c[r>>2]=1}c[(c[12172]|0)+(((c[11864]|0)-1|0)*232&-1)+4>>2]=~~Y;V=c[13898]|0;W=c[8272]|0}else{V=R;W=M}}while(0);if((W|0)<=(V|0)){s=5459;break L7326}M=c[1054]|0;if((a[M+(V*40&-1)|0]&1)==0){s=5460;break L7326}R=c[M+(V*40&-1)+36>>2]|0;C=M+(V*40&-1)+32|0;M=c[10036]|0;S=0;while(1){if((S|0)>=(R|0)){break}if((a[M+((c[C>>2]|0)+S|0)|0]|0)==(a[S+131272|0]|0)){S=S+1|0}else{s=5457;break L7326}}if((S|0)!=1){s=5458;break L7326}C=V+1|0;c[13898]=C;F=Q;G=C;H=W}}while(0);if((H|0)<=(G|0)){s=5465;break}w=c[1054]|0;if((a[w+(G*40&-1)|0]&1)==0){s=5466;break}C=c[w+(G*40&-1)+36>>2]|0;M=w+(G*40&-1)+32|0;R=c[10036]|0;A=0;while(1){if((A|0)>=(C|0)){break}if((a[R+((c[M>>2]|0)+A|0)|0]|0)==(a[A+183584|0]|0)){A=A+1|0}else{s=5467;break L7326}}if((A|0)==1){x=F;y=G;u=w}else{s=5468;break}}if((s|0)==5391){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5457){uf(V,148760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5458){uf(V,148760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5460){uf(V,148760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5438){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5459){uf(V,148760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5466){i=b;return}else if((s|0)==5467){i=b;return}else if((s|0)==5468){i=b;return}else if((s|0)==5418){c[11880]=0;uf(c[13898]|0,216112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5425){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5398){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==5465){i=b;return}}function eT(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0.0,X=0,Y=0.0,Z=0.0,_=0.0,$=0.0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0;e=i;i=i+80|0;f=e|0;g=e+24|0;j=e+48|0;k=e+72|0;l=c[13898]|0;m=c[8272]|0;if((m|0)<=(l|0)){n=c[10880]|0;uf(l,n,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=c[1054]|0;if((a[o+(l*40&-1)|0]&1)==0){n=c[10880]|0;uf(l,n,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=c[o+(l*40&-1)+36>>2]|0;q=o+(l*40&-1)+32|0;r=c[10036]|0;s=0;while(1){if((s|0)>=(p|0)){break}if((a[r+((c[q>>2]|0)+s|0)|0]|0)==(a[s+115e3|0]|0)){s=s+1|0}else{t=5660;break}}if((t|0)==5660){n=c[10880]|0;uf(l,n,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((s|0)!=1){n=c[10880]|0;uf(l,n,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=l+1|0;c[13898]=n;if((n|0)>=(m|0)){i=e;return}l=f|0;s=f+8|0;q=j|0;p=k|0;u=(b|0)==4?1:2;w=j+16|0;x=j;y=j+8|0;j=g|0;z=g+8|0;A=0;B=n;n=o;o=m;m=r;L7441:while(1){r=(a[n+(B*40&-1)|0]&1)==0;L7443:do{if(!r){C=c[n+(B*40&-1)+36>>2]|0;D=n+(B*40&-1)+32|0;E=0;while(1){if((E|0)>=(C|0)){break}if((a[m+((c[D>>2]|0)+E|0)|0]|0)==(a[E+103664|0]|0)){E=E+1|0}else{break L7443}}if((E|0)==1){t=5677;break L7441}}}while(0);if((b|0)==4|(b|0)==5|(b|0)==7){if((o|0)<=(B|0)|r){F=B;t=5651;break}D=c[n+(B*40&-1)+36>>2]|0;C=n+(B*40&-1)+32|0;G=0;while(1){if((G|0)>=(D|0)){break}if((a[m+((c[C>>2]|0)+G|0)|0]|0)==(a[G+199040|0]|0)){G=G+1|0}else{F=B;t=5652;break L7441}}if((G|0)!=1){F=B;t=5654;break}C=B+1|0;c[13898]=C;if((C|0)<(o|0)){H=q;I=1;J=0;K=C;L=o;M=n}else{F=C;t=5653;break}L7467:while(1){C=I;N=K;while(1){D=(a[M+(N*40&-1)|0]&1)==0;L7471:do{if(!D){r=c[M+(N*40&-1)+36>>2]|0;O=M+(N*40&-1)+32|0;P=c[10036]|0;Q=0;while(1){if((Q|0)>=(r|0)){break}if((a[P+((c[O>>2]|0)+Q|0)|0]|0)==(a[Q+103664|0]|0)){Q=Q+1|0}else{break L7471}}if((Q|0)==1){F=N;t=5656;break L7441}}}while(0);if(C){break}if(D|(L|0)<=(N|0)){t=5648;break L7441}R=c[M+(N*40&-1)+36>>2]|0;S=M+(N*40&-1)+32|0;T=c[10036]|0;O=0;while(1){if((O|0)>=(R|0)){break}if((a[T+((c[S>>2]|0)+O|0)|0]|0)==(a[O+148464|0]|0)){O=O+1|0}else{U=0;break L7467}}if((O|0)!=1){U=0;break L7467}D=N+1|0;c[13898]=D;if((D|0)<(L|0)){C=1;N=D}else{F=D;t=5655;break L7441}}V=J+1|0;if((V|0)>=4){t=5507;break L7441}C=is(g)|0;E=c[C>>2]|0;if((E|0)==1){W=+(c[C+8>>2]|0)}else if((E|0)==3){W=+uz(c[C+8>>2]|0,0)}else if((E|0)==2){W=+h[C+8>>3]}else{t=5502;break L7441}if((c[j>>2]|0)==3){uu(c[z>>2]|0);c[j>>2]=1}h[H>>3]=W;C=c[13898]|0;E=c[8272]|0;if((C|0)>=(E|0)){F=C;t=5657;break L7441}H=H+8|0;I=0;J=V;K=C;L=E;M=c[1054]|0}while(1){if((U|0)>=(R|0)){break}if((a[T+((c[S>>2]|0)+U|0)|0]|0)==(a[U+131272|0]|0)){U=U+1|0}else{t=5649;break L7441}}if((U|0)!=1){t=5647;break}G=N+1|0;c[13898]=G;if((J|0)==0){F=G;t=5650;break}else{X=J}}else if((b|0)==3|(b|0)==2){X=1}else{G=is(f)|0;E=c[G>>2]|0;if((E|0)==2){Y=+h[G+8>>3]}else if((E|0)==3){Y=+uz(c[G+8>>2]|0,0)}else if((E|0)==1){Y=+(c[G+8>>2]|0)}else{t=5522;break}if((c[l>>2]|0)==3){uu(c[s>>2]|0);c[l>>2]=1}h[q>>3]=Y;X=1}if((A|0)>=(c[11864]|0)){t=5528;break}do{if((b|0)==7){if((X|0)!=3){t=5615;break L7441}Z=+h[q>>3];_=+h[y>>3];$=+h[w>>3];if(Z*Z+_*_+$*$<2.220446049250313e-14){t=5617;break L7441}G=(c[12172]|0)+(A*232&-1)+88|0;c[G>>2]=c[x>>2];c[G+4>>2]=c[x+4>>2];c[G+8>>2]=c[x+8>>2];c[G+12>>2]=c[x+12>>2];c[G+16>>2]=c[x+16>>2];c[G+20>>2]=c[x+20>>2]}else if((b|0)==2){aa=c[13898]|0;G=c[1054]|0;E=c[G+(aa*40&-1)+36>>2]|0;C=(E|0)>3?3:E;E=c[10036]|0;D=c[G+(aa*40&-1)+32>>2]|0;G=0;while(1){P=G+1|0;a[k+G|0]=a[E+D|0]|0;if((P|0)==(C|0)){break}else{D=D+1|0;G=P}}a[k+C|0]=0;G=uA(p|0)|0;if((G|0)!=(b4(p|0,154840)|0)){t=5542;break L7441}if((cy(p|0,154104)|0)!=0){G=c[12172]|0;if((c[G+(A*232&-1)>>2]|0)==0){t=5546;break L7441}c[G+(A*232&-1)+12>>2]=d}if((cy(p|0,153744)|0)!=0){G=c[12172]|0;if((c[G+(A*232&-1)+4>>2]|0)==0){t=5550;break L7441}c[G+(A*232&-1)+16>>2]=d}if((cy(p|0,153360)|0)!=0){G=c[12172]|0;if((c[G+(A*232&-1)+8>>2]|0)==0){t=5554;break L7441}c[G+(A*232&-1)+20>>2]=d}c[13898]=(c[13898]|0)+1}else if((b|0)==1){G=c[12172]|0;if((c[G+(A*232&-1)>>2]|0)==0){t=5538;break L7441}$=+h[q>>3];if($==0.0){c[G+(A*232&-1)+12+(d<<2)>>2]=0;break}if($!=1.0){t=5537;break L7441}c[G+(A*232&-1)+12+(d<<2)>>2]=1}else if((b|0)==0){$=+h[q>>3];h[(c[12172]|0)+(A*232&-1)+24+(d<<3)>>3]=$;if($<=0.0){t=5531;break L7441}}else if((b|0)==3){G=c[12172]|0;if((c[G+(A*232&-1)>>2]|0)==0){if((c[G+(A*232&-1)+140>>2]|0)==0){t=5659;break L7441}}if((c[G+(A*232&-1)+4>>2]|0)==0){if((c[G+(A*232&-1)+144>>2]|0)==0){t=5658;break L7441}}do{if((c[G+(A*232&-1)+8>>2]|0)==0){if((c[G+(A*232&-1)+148>>2]|0)!=0){t=5564;break}D=c[13898]|0;E=(c[8272]|0)>(D|0);P=c[1054]|0;r=P+(D*40&-1)|0;ab=P+(D*40&-1)+36|0;ac=P+(D*40&-1)+32|0;D=c[10036]|0;P=c[12094]|0;if(!E){t=5668;break L7441}do{if((a[r]&1)==0){t=5629}else{ad=c[ab>>2]|0;ae=0;while(1){if((ae|0)>=(ad|0)){t=5580;break}if((a[D+((c[ac>>2]|0)+ae|0)|0]|0)==(a[P+ae|0]|0)){ae=ae+1|0}else{break}}if((t|0)==5580){t=0;if((a[P+ae|0]|0)==0){af=0;break}}if(E){t=5629}else{t=5666;break L7441}}}while(0);L7551:do{if((t|0)==5629){t=0;P=c[12098]|0;L7553:do{if((a[r]&1)!=0){O=c[ab>>2]|0;ad=0;while(1){if((ad|0)>=(O|0)){break}if((a[D+((c[ac>>2]|0)+ad|0)|0]|0)==(a[P+ad|0]|0)){ad=ad+1|0}else{break L7553}}if((a[P+ad|0]|0)==0){af=1;break L7551}}}while(0);P=c[12102]|0;if(!E){t=5669;break L7441}if((a[r]&1)!=0){ae=c[ab>>2]|0;O=0;while(1){if((O|0)>=(ae|0)){t=5639;break}if((a[D+((c[ac>>2]|0)+O|0)|0]|0)==(a[P+O|0]|0)){O=O+1|0}else{break}}if((t|0)==5639){t=0;if((a[P+O|0]|0)==0){af=2;break}}if(!E){t=5667;break L7441}}ae=c[12106]|0;if((a[r]&1)==0){t=5670;break L7441}Q=c[ab>>2]|0;ag=0;while(1){if((ag|0)>=(Q|0)){break}if((a[D+((c[ac>>2]|0)+ag|0)|0]|0)==(a[ae+ag|0]|0)){ag=ag+1|0}else{t=5665;break L7441}}if((a[ae+ag|0]|0)==0){af=3}else{t=5664;break L7441}}}while(0);ac=G+(A*232&-1)+112|0;D=48380+(af<<4)|0;c[ac>>2]=c[D>>2];c[ac+4>>2]=c[D+4>>2];c[ac+8>>2]=c[D+8>>2]}else{t=5564}}while(0);if((t|0)==5564){t=0;C=c[13898]|0;D=(c[8272]|0)>(C|0);ac=c[1054]|0;ab=ac+(C*40&-1)|0;r=ac+(C*40&-1)+36|0;E=ac+(C*40&-1)+32|0;C=c[10036]|0;ac=0;L7579:while(1){Q=c[48184+(ac<<4)>>2]|0;L7581:do{if(D){if((a[ab]&1)==0){break}O=c[r>>2]|0;P=0;while(1){if((P|0)>=(O|0)){break}if((a[C+((c[E>>2]|0)+P|0)|0]|0)==(a[Q+P|0]|0)){P=P+1|0}else{break L7581}}if((a[Q+P|0]|0)==0){t=5571;break L7579}}}while(0);Q=ac+1|0;if(Q>>>0<12){ac=Q}else{ah=Q;break}}if((t|0)==5571){t=0;E=G+(A*232&-1)+112|0;C=48188+(ac<<4)|0;c[E>>2]=c[C>>2];c[E+4>>2]=c[C+4>>2];c[E+8>>2]=c[C+8>>2];ah=ac}if((ah|0)==12){t=5574;break L7441}}C=G+(A*232&-1)+152|0;c[C>>2]=c[12148];c[C+4>>2]=c[48596>>2];c[C+8>>2]=c[48600>>2];c[13898]=(c[13898]|0)+1}else if((b|0)==4|(b|0)==5){c[(c[12172]|0)+(A*232&-1)+48>>2]=u;if((d|0)==2){if((X|0)!=3){t=5592;break L7441}}else if((d|0)==0){if((X|0)!=3){t=5594;break L7441}}else if((d|0)==1){if((X|0)!=2){t=5589;break L7441}h[w>>3]=0.0}else{t=5595;break L7441}C=(c[12172]|0)+(A*232&-1)+56|0;c[C>>2]=c[x>>2];c[C+4>>2]=c[x+4>>2];c[C+8>>2]=c[x+8>>2];c[C+12>>2]=c[x+12>>2];c[C+16>>2]=c[x+16>>2];c[C+20>>2]=c[x+20>>2]}else if((b|0)==8){$=+h[q>>3];c[(c[12172]|0)+(A*232&-1)+128>>2]=~~$;C=c[(c[12172]|0)+(A*232&-1)+128>>2]|0;if(+(C|0)!=$|(C|0)<0){t=5586;break L7441}}else if((b|0)==6){C=c[13898]|0;E=c[1054]|0;r=a[E+(C*40&-1)|0]|0;L7602:do{if((c[8272]|0)>(C|0)){if((r&1)==0){t=5604;break}ab=c[E+(C*40&-1)+36>>2]|0;D=E+(C*40&-1)+32|0;Q=c[10036]|0;O=0;while(1){if((O|0)>=(ab|0)){break}if((a[Q+((c[D>>2]|0)+O|0)|0]|0)==(a[O+99680|0]|0)){O=O+1|0}else{t=5604;break L7602}}if((O|0)!=2){t=5604;break}h[q>>3]=+h[q>>3]*3.141592653589793;c[13898]=C+1}else{t=5604}}while(0);L7610:do{if((t|0)==5604){t=0;G=c[E+(C*40&-1)+36>>2]|0;if(!((r&1)!=0&(G|0)>0)){break}ac=c[10036]|0;D=0;Q=0;ab=c[E+(C*40&-1)+32>>2]|0;while(1){if((a[D+123032|0]|0)==(a[ac+(D+ab|0)|0]|0)){ai=ab;aj=Q}else{if((D|0)!=1){break L7610}ai=ab-1|0;aj=1}ag=D+1|0;if((ag|0)<(aj+G|0)){D=ag;Q=aj;ab=ai}else{break}}if((aj|0)==0){if(!((D|0)==0|(D|0)==7)){break}}h[q>>3]=+h[q>>3]*.017453292519943295;c[13898]=C+1}}while(0);h[(c[12172]|0)+(A*232&-1)+80>>3]=+h[q>>3]}else{t=5619;break L7441}}while(0);C=c[13898]|0;E=c[8272]|0;if((E|0)<=(C|0)){t=5674;break}r=c[1054]|0;if((a[r+(C*40&-1)|0]&1)==0){t=5675;break}ab=c[r+(C*40&-1)+36>>2]|0;Q=r+(C*40&-1)+32|0;G=c[10036]|0;ac=0;while(1){if((ac|0)>=(ab|0)){break}if((a[G+((c[Q>>2]|0)+ac|0)|0]|0)==(a[ac+183584|0]|0)){ac=ac+1|0}else{t=5676;break L7441}}if((ac|0)!=1){t=5671;break}Q=C+1|0;c[13898]=Q;if((Q|0)<(E|0)){A=A+1|0;B=Q;n=r;o=E;m=G}else{t=5672;break}}if((t|0)==5649){uf(N,148888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5650){uf(F,149968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5651){uf(F,149968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5652){uf(F,149968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5542){uf(aa,154448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5546){uf(aa,158792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5528){uf(c[13898]|0,158272,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5574){uf(c[13898]|0,152504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5592){uf(c[13898]|0,151520,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5615){uf(c[13898]|0,150440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5531){uf((c[13898]|0)-2|0,156288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5554){uf(c[13898]|0,158792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5550){uf(c[13898]|0,158792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5647){uf(N,148888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5648){uf(N,148888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5594){uf(c[13898]|0,151168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5595){uf(c[13898]|0,150960,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5666){ak=c[13898]|0;uf(ak,152080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5667){ak=c[13898]|0;uf(ak,152080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5668){ak=c[13898]|0;uf(ak,152080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5669){ak=c[13898]|0;uf(ak,152080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5537){uf((c[13898]|0)-1|0,155200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5538){uf(c[13898]|0,158792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5589){uf(c[13898]|0,151648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5653){uf(F,149968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5654){uf(F,149968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5655){uf(F,149968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5656){uf(F,149968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5657){uf(F,149968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5617){uf(c[13898]|0,150304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5619){uf(-1,150112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5670){ak=c[13898]|0;uf(ak,152080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5671){i=e;return}else if((t|0)==5672){i=e;return}else if((t|0)==5674){i=e;return}else if((t|0)==5675){i=e;return}else if((t|0)==5676){i=e;return}else if((t|0)==5586){uf(c[13898]|0,151928,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5502){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5522){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5507){uf(N-1|0,149640,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}else if((t|0)==5664){ak=c[13898]|0;uf(ak,152080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5665){ak=c[13898]|0;uf(ak,152080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5658){al=c[13898]|0;uf(al,152856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5659){al=c[13898]|0;uf(al,152856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==5677){i=e;return}}function eU(d){d=d|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0;f=i;i=i+8|0;g=f|0;c[g>>2]=d;h=8;j=0;k=d;L7680:while(1){d=k;while(1){l=a[d]|0;if((l<<24>>24|0)==0|(l<<24>>24|0)==34|(l<<24>>24|0)==39){m=5681;break L7680}else if((l<<24>>24|0)==37){break}else if((l<<24>>24|0)!=32){m=5715;break L7680}l=d+1|0;c[g>>2]=l;d=l}l=d+1|0;c[g>>2]=l;n=a[l]|0;o=n<<24>>24==42;if(o){p=d+2|0;c[g>>2]=p;q=p;r=a[p]|0}else{q=l;r=n}if(((r<<24>>24)-48|0)>>>0<10){n=aE(q|0,g|0,10)|0;s=n;t=c[g>>2]|0}else{s=1;t=q}n=b[23918]|0;L7692:do{if(n<<16>>16==0){u=0;m=5710}else{l=c[11958]|0;p=0;while(1){w=b[l+(p<<4)+4>>1]|0;if(w<<16>>16==0){x=0}else{y=c[l+(p<<4)>>2]|0;z=0;while(1){A=c[y+(z<<2)>>2]|0;B=uA(A|0)|0;if((a_(t|0,A|0,B|0)|0)==0){A=t+B|0;if((aZ(163032,a[A]|0|0,5)|0)!=0){C=A;D=p;E=47832;F=l;break L7692}}A=z+1|0;if((A|0)<(w&65535|0)){z=A}else{x=A;break}}}z=p+1|0;if((z|0)<(n&65535|0)){p=z}else{u=x;m=5710;break}}}}while(0);L7704:do{if((m|0)==5710){m=0;n=b[23922]|0;if(n<<16>>16==0){G=0;H=u}else{d=c[11960]|0;p=0;while(1){l=b[d+(p<<4)+4>>1]|0;if(l<<16>>16==0){I=0}else{z=c[d+(p<<4)>>2]|0;w=0;while(1){y=c[z+(w<<2)>>2]|0;A=uA(y|0)|0;if((a_(t|0,y|0,A|0)|0)==0){y=t+A|0;if((aZ(163032,a[y]|0|0,5)|0)!=0){C=y;D=p;E=47840;F=d;break L7704}}y=w+1|0;if((y|0)<(l&65535|0)){w=y}else{I=y;break}}}w=p+1|0;if((w|0)<(n&65535|0)){p=w}else{G=w;H=I;break}}}if((G|0)!=(e[23922]|0|0)){h=h;j=j;k=t;continue L7680}if((H|0)==(e[(c[11960]|0)+(G-1<<4)+4>>1]|0|0)){m=5714;break L7680}else{h=h;j=j;k=t;continue L7680}}}while(0);c[g>>2]=C;if(o){p=c[11950]|0;if((p|0)==0){m=5706;break}n=aa(e[F+(D<<4)+12>>1]|0,s);d=p+(j*12&-1)|0;c[d>>2]=n+(c[d>>2]|0);h=h;j=j;k=C;continue}if((s|0)>0){J=j;K=0}else{h=h;j=j;k=C;continue}while(1){L=J+1|0;d=J+2|0;if((d|0)<=0){m=5697;break L7680}n=c[11950]|0;if((c[11884]|0)<(d|0)){p=db(n,d*12&-1,215112)|0;c[11950]=p;c[11884]=d;M=p}else{M=n}c[M+(L*12&-1)>>2]=0;n=c[(c[E>>2]|0)+(D<<4)+8>>2]|0;if((L|0)<=0){m=5701;break L7680}p=c[11950]|0;if((c[11884]|0)<(L|0)){d=db(p,L*12&-1,215112)|0;c[11950]=d;c[11884]=L;N=d}else{N=p}c[N+(J*12&-1)+4>>2]=n;b[(c[11950]|0)+(J*12&-1)+8>>1]=b[48036+(n<<4)>>1]|0;n=K+1|0;if((n|0)<(s|0)){J=L;K=n}else{break}}h=c[(c[E>>2]|0)+(D<<4)+8>>2]|0;j=L;k=C}if((m|0)==5706){uf(-1,162376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((m|0)==5714){uf(c[13898]|0,160672,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((m|0)==5715){uf(c[13898]|0,159280,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((m|0)==5701){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=215616,v)|0)}else if((m|0)==5697){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=215616,v)|0)}else if((m|0)==5681){if((j|0)>=(c[11876]|0)){O=j;c[11876]=O;i=f;return}C=48036+(h<<4)|0;k=j;while(1){j=k+1|0;if((j|0)<=0){m=5717;break}L=c[11950]|0;if((c[11884]|0)<(j|0)){D=db(L,j*12&-1,215112)|0;c[11950]=D;c[11884]=j;P=D}else{P=L}c[P+(k*12&-1)>>2]=0;if((k|0)<=0){m=5721;break}L=c[11950]|0;if((c[11884]|0)<(k|0)){D=db(L,k*12&-1,215112)|0;c[11950]=D;c[11884]=k;Q=D}else{Q=L}L=k-1|0;c[Q+(L*12&-1)>>2]=0;D=c[11950]|0;if((c[11884]|0)<(k|0)){E=db(D,k*12&-1,215112)|0;c[11950]=E;c[11884]=k;R=E}else{R=D}c[R+(L*12&-1)+4>>2]=h;b[(c[11950]|0)+(L*12&-1)+8>>1]=b[C>>1]|0;if((j|0)<(c[11876]|0)){k=j}else{O=j;m=5735;break}}if((m|0)==5717){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=215616,v)|0)}else if((m|0)==5721){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=215616,v)|0)}else if((m|0)==5735){c[11876]=O;i=f;return}}}function eV(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0,E=0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0,L=0,M=0.0,N=0.0,O=0.0,P=0.0,Q=0,R=0.0,S=0.0;b=i;do{if(a[34104]|0){if((a[37400]&1)==0){break}d=c[m>>2]|0;aI(144664,24,1,d|0)}}while(0);d=c[11854]|0;if((d|0)==1){e=5741}else if((d|0)==0){if((a6(c[8802]|0,c[8716]|0,c[12892]|0)|0)==0){f=0;i=b;return f|0}if(a[34104]|0){c[9354]=(c[9354]|0)+1;g=0}else{g=0}while(1){j=c[8802]|0;k=(uA(j+g|0)|0)+g|0;if((k|0)>0){l=j+(k-1|0)|0;if((a[l]|0)==10){e=5792;break}}n=c[8716]|0;if((n-k|0)>>>0<32){o=n<<1;c[8716]=o;p=db(j,o,84816)|0;c[8802]=p;q=p;r=c[8716]|0}else{q=j;r=n}if((a6(q+k|0,r-k|0,c[12892]|0)|0)==0){e=5796;break}else{g=k}}if((e|0)==5792){a[l]=0;f=c[8802]|0;i=b;return f|0}else if((e|0)==5796){f=c[8802]|0;i=b;return f|0}}else{s=d}if((e|0)==5741){e=c[11852]|0;d=c[6352]|0;if((e|0)>=(d|0)){f=0;i=b;return f|0}if((e|0)==0){if((a[32936]&1)!=0){uf(-1,144512,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((a[30528]&1)!=0){uf(-1,144512,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if(+h[8256]==-8.988465674311579e+307){h[8256]=10.0}if(+h[8255]==8.988465674311579e+307){h[8255]=-10.0}l=c[200]|0;t=+h[64664+(l*688&-1)>>3];h[5960]=t;u=+h[64672+(l*688&-1)>>3];h[5961]=u;g=a[64788+(l*688&-1)|0]|0;do{if((g&1)==0){w=u;x=t}else{if(t>0.0&u>0.0){y=+_(+t);z=+h[64800+(l*688&-1)>>3];A=y/z;h[5960]=A;y=+_(+u)/z;h[5961]=y;w=y;x=A;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56832+(l*24&-1),v)|0);return 0}}}while(0);u=(w-x)/+(d-1|0);h[5959]=u;B=x;C=u;D=l;E=g}else{g=c[200]|0;B=+h[5960];C=+h[5959];D=g;E=a[64788+(g*688&-1)|0]|0}u=B+ +(e|0)*C;if((E&1)==0){F=u}else{F=+Z(+(u*+h[64800+(D*688&-1)>>3]))}D=c[8802]|0;be(D|0,170912,(v=i,i=i+8|0,h[v>>3]=F,v)|0);c[11852]=(c[11852]|0)+1;s=c[11854]|0}if((s|0)==2){s=c[11852]|0;if((s|0)>=(c[11926]|0)&(s|0)>0){c[11852]=0;D=(c[11850]|0)+1|0;c[11850]=D;f=(D|0)<(c[11924]|0)?179864:0;i=b;return f|0}D=c[11850]|0;if((D|0)==0){if((c[6352]|0)<2|(c[6350]|0)<2|(c[9344]|0)<2|(c[9342]|0)<2){uf(-1,138240,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}dc(2,145296);dc(1,143440);F=+h[8255];E=a[66164]|0;do{if((E&1)==0){h[5957]=F;G=+h[8256];H=F}else{if(F<=0.0){uk(210984,(v=i,i=i+24|0,c[v>>2]=141960,c[v+8>>2]=56880,h[v+16>>3]=F,v)|0);return 0}u=+_(+F);C=+h[8272];B=u/C;h[5957]=B;u=+h[8256];if(u>0.0){G=+_(+u)/C;H=B;break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=141960,c[v+8>>2]=56880,h[v+16>>3]=u,v)|0);return 0}}}while(0);h[5958]=G;F=+h[8169];do{if((a[65476]&1)==0){h[5953]=F;I=+h[8170];J=F}else{if(F<=0.0){uk(210984,(v=i,i=i+24|0,c[v>>2]=140304,c[v+8>>2]=56856,h[v+16>>3]=F,v)|0);return 0}u=+_(+F);B=+h[8186];C=u/B;h[5953]=C;u=+h[8170];if(u>0.0){I=+_(+u)/B;J=C;break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=140304,c[v+8>>2]=56856,h[v+16>>3]=u,v)|0);return 0}}}while(0);h[5954]=I;e=(a[38984]&1)==0?c[6352]|0:c[9344]|0;F=(G-H)/+(e-1|0);h[5956]=F;c[11926]=e;e=c[9342]|0;G=(I-J)/+(e-1|0);h[5955]=G;c[11924]=e;K=c[11852]|0;L=c[11850]|0;M=H;N=F;O=I;P=G;Q=E}else{K=s;L=D;M=+h[5957];N=+h[5956];O=+h[5954];P=+h[5955];Q=a[66164]|0}G=M+N*+(K|0);N=O-P*+(L|0);L=c[8802]|0;if((Q&1)==0){R=G}else{R=+Z(+(G*+h[8272]))}if((a[65476]&1)==0){S=N}else{S=+Z(+(N*+h[8186]))}be(L|0,143424,(v=i,i=i+16|0,h[v>>3]=R,h[v+8>>3]=S,v)|0);c[11852]=(c[11852]|0)+1}f=c[8802]|0;i=b;return f|0}function eW(a){a=a|0;c[9340]=c[a>>2];return}function eX(b){b=b|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0;e=i;i=i+16|0;f=e|0;g=e+8|0;uE(47056,0,28);c[11874]=0;if((a[b]|0)==0){j=0;i=e;return j|0}else{k=b;l=0}while(1){b=c[11882]|0;do{if((b|0)<=(l|0)){m=(b|0)<20?b+20|0:b<<1;n=db(c[11952]|0,m*24&-1,180032)|0;c[11952]=n;o=c[11882]|0;if((o|0)>=(m|0)){break}h[n+(o*24&-1)>>3]=0.0;c[n+(o*24&-1)+16>>2]=0;o=(c[11882]|0)+1|0;c[11882]=o;if((o|0)<(m|0)){p=o}else{break}do{o=c[11952]|0;h[o+(p*24&-1)>>3]=0.0;c[o+(p*24&-1)+16>>2]=0;p=(c[11882]|0)+1|0;c[11882]=p;}while((p|0)<(m|0))}}while(0);c[(c[11952]|0)+((c[11874]|0)*24&-1)+12>>2]=k;b=c[11874]|0;do{if((b|0)==((c[262]|0)-1|0)){c[11764]=k;if((c[263]|0)!=1){q=b;break}c[(c[11952]|0)+(b*24&-1)+8>>2]=1;q=c[11874]|0}else{q=b}}while(0);do{if((q|0)==((c[265]|0)-1|0)){c[11765]=k;if((c[266]|0)!=1){r=q;break}c[(c[11952]|0)+(q*24&-1)+8>>2]=1;r=c[11874]|0}else{r=q}}while(0);do{if((r|0)==((c[268]|0)-1|0)){c[11766]=k;if((c[269]|0)!=1){s=r;break}c[(c[11952]|0)+(r*24&-1)+8>>2]=1;s=c[11874]|0}else{s=r}}while(0);do{if((s|0)==((c[271]|0)-1|0)){c[11767]=k;if((c[272]|0)!=1){t=s;break}c[(c[11952]|0)+(s*24&-1)+8>>2]=1;t=c[11874]|0}else{t=s}}while(0);do{if((t|0)==((c[274]|0)-1|0)){c[11768]=k;if((c[275]|0)!=1){u=t;break}c[(c[11952]|0)+(t*24&-1)+8>>2]=1;u=c[11874]|0}else{u=t}}while(0);do{if((u|0)==((c[277]|0)-1|0)){c[11769]=k;if((c[278]|0)!=1){v=u;break}c[(c[11952]|0)+(u*24&-1)+8>>2]=1;v=c[11874]|0}else{v=u}}while(0);do{if((v|0)==((c[280]|0)-1|0)){c[11770]=k;if((c[281]|0)!=1){break}c[(c[11952]|0)+(v*24&-1)+8>>2]=1}}while(0);b=a[k]|0;if(b<<24>>24!=34|(a[47120]|0)==0){w=k;x=0;y=b}else{b=k+1|0;c[(c[11952]|0)+((c[11874]|0)*24&-1)+12>>2]=b;w=b;x=1;y=a[b]|0}L7903:do{if(y<<24>>24==34){c[(c[11952]|0)+((c[11874]|0)*24&-1)+8>>2]=-5;c[(c[11952]|0)+((c[11874]|0)*24&-1)+8>>2]=-8;c[11874]=(c[11874]|0)+1;if(x){z=w}else{A=w;B=5850}}else{b=c[8528]|0;do{if((b|0)!=0){m=uA(b|0)|0;if((a_(w|0,b|0,m|0)|0)!=0){break}o=w+m|0;if((aM(d[o]|0|0)|0)==0){if((a[o]|0)!=0){break}}c[(c[11952]|0)+((c[11874]|0)*24&-1)+8>>2]=-5;C=+uz(133568,0);o=c[11874]|0;h[(c[11952]|0)+(o*24&-1)>>3]=C;c[11874]=o+1;if(x){A=w;B=5850;break L7903}else{z=w;break L7903}}}while(0);b=(c[11874]|0)+1|0;o=c[11870]|0;L7912:do{if((o|0)==0|a[43464]^1){B=5835}else{do{if((o|0)>0){if((c[262]|0)==(b|0)){B=5835;break L7912}if((o|0)<=1){D=w;break}if((c[265]|0)==(b|0)){B=5835;break L7912}if((o|0)<=2){D=w;break}if((c[268]|0)==(b|0)){B=5835;break L7912}if((o|0)<=3){D=w;break}if((c[271]|0)==(b|0)){B=5835;break L7912}if((o|0)<=4){D=w;break}if((c[274]|0)==(b|0)|(o|0)>5){B=5835;break L7912}else{D=w}}else{D=w}}while(0);while(1){m=(aM(d[D]|0|0)|0)!=0;E=a[D]|0;F=a[47120]|0;G=E<<24>>24!=F<<24>>24;if(!(m&G)){break}D=D+1|0}m=E<<24>>24==0?0:G&1;if(F<<24>>24==0|x^1){H=D;I=x;J=E}else{n=D;while(1){K=n+1|0;L=a[K]|0;if((L<<24>>24|0)==0|(L<<24>>24|0)==34){H=K;I=0;J=L;break}else{n=K}}}n=H;K=J;while(1){if((aM(K&255|0)|0)!=0){M=n;N=I;O=m;P=0;break L7912}L=a[n]|0;Q=n+1|0;if(L<<24>>24==0|L<<24>>24==(a[47120]|0)){M=n;N=I;O=m;P=0;break L7912}n=Q;K=a[Q]|0}}}while(0);if((B|0)==5835){B=0;C=+uz(w,f);h[(c[11952]|0)+((c[11874]|0)*24&-1)>>3]=C;o=c[f>>2]|0;M=w;N=x;O=(o|0)!=(w|0)&1;P=o-w|0}do{if((a[47712]&1)!=0&(O|0)==1){o=M+P|0;b=a[o]|0;if(!((b<<24>>24|0)==100|(b<<24>>24|0)==68|(b<<24>>24|0)==113|(b<<24>>24|0)==81)){R=1;break}a[o]=101;C=+uz(M,g);h[(c[11952]|0)+((c[11874]|0)*24&-1)>>3]=C;K=(c[g>>2]|0)!=(M|0)&1;a[o]=b;R=K}else{R=O}}while(0);c[(c[11952]|0)+((c[11874]|0)*24&-1)+8>>2]=(R|0)==1&1;K=(cg(+(+h[(c[11952]|0)+((c[11874]|0)*24&-1)>>3]))|0)==0;b=c[11874]|0;if(K){c[(c[11952]|0)+(b*24&-1)+8>>2]=0;c[11874]=(c[11874]|0)+1;if(N){A=M;B=5850;break}else{z=M;break}}else{c[11874]=b+1;if(N){A=M;B=5850;break}else{z=M;break}}}}while(0);if((B|0)==5850){while(1){B=0;b=A+1|0;K=a[b]|0;if((K<<24>>24|0)==0|(K<<24>>24|0)==34){z=b;break}else{A=b;B=5850}}}b=a[47120]|0;L7951:do{if(b<<24>>24==0){K=z;while(1){if((aM(d[K]|0|0)|0)!=0){S=K;break}if((a[K]|0)==0){S=K;break}K=K+1|0}while(1){if((aM(d[S]|0|0)|0)==0){T=S;break L7951}S=S+1|0}}else{K=z;while(1){U=a[K]|0;if(!(U<<24>>24!=0&U<<24>>24!=b<<24>>24)){break}K=K+1|0}if(U<<24>>24==b<<24>>24){V=K}else{T=K;break}while(1){o=V+1|0;n=a[o]|0;if(n<<24>>24==0){T=o;break L7951}if((aM(n&255|0)|0)==0){T=o;break L7951}if((a[o]|0)==(a[47120]|0)){T=o;break}else{V=o}}}}while(0);b=c[11874]|0;if((a[T]|0)==0){j=b;break}else{k=T;l=b}}i=e;return j|0}function eY(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0;d=i;i=i+24|0;e=d|0;f=(c[13898]|0)+2|0;c[13898]=f;g=c[1054]|0;L7970:do{if((a[g+(f*40&-1)|0]&1)!=0){j=c[10810]|0;L7972:do{if((j|0)!=0){k=(c[8272]|0)>(f|0);l=g+(f*40&-1)+36|0;m=g+(f*40&-1)+32|0;n=c[10036]|0;o=j;L7974:while(1){p=c[o+4>>2]|0;L7976:do{if(k){q=c[l>>2]|0;r=0;while(1){if((r|0)>=(q|0)){break}if((a[n+((c[m>>2]|0)+r|0)|0]|0)==(a[p+r|0]|0)){r=r+1|0}else{break L7976}}if((a[p+r|0]|0)==0){break L7974}}}while(0);p=c[o>>2]|0;if((p|0)==0){break L7972}else{o=p}}if((a[o+8|0]&1)!=0){break}if((c[o+16>>2]|0)==1){break L7970}}}while(0);it();j=c[18070]|0;m=db(j,c[j>>2]<<5|8,105624)|0;c[18070]=0;j=c[11870]|0;n=c[11872]|0;c[1056+((n+j|0)*12&-1)>>2]=m;a[43464]=0;s=1;t=c[13898]|0;u=j;w=n;x=t+1|0;c[13898]=x;y=w+u|0;z=1052+(y*12&-1)|0;c[z>>2]=b;A=1048+(y*12&-1)|0;c[A>>2]=s;B=w+1|0;c[11872]=B;i=d;return}}while(0);f=is(e)|0;g=c[f>>2]|0;if((g|0)==3){C=+uz(c[f+8>>2]|0,0)}else if((g|0)==2){C=+h[f+8>>3]}else if((g|0)==1){C=+(c[f+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e|0;if((c[f>>2]|0)==3){uu(c[e+8>>2]|0);c[f>>2]=1}f=~~C;e=c[11870]|0;g=c[11872]|0;c[1056+((g+e|0)*12&-1)>>2]=0;n=c[13898]|0;if((f|0)<1){uf(n,140472,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{s=f;t=n;u=e;w=g;x=t+1|0;c[13898]=x;y=w+u|0;z=1052+(y*12&-1)|0;c[z>>2]=b;A=1048+(y*12&-1)|0;c[A>>2]=s;B=w+1|0;c[11872]=B;i=d;return}}function eZ(a){a=a|0;var b=0;a=c[6354]|0;if((c[14296+(a*24&-1)>>2]|0)==1){b=14304+(a*24&-1)|0;c[b>>2]=(c[b>>2]|0)!=0&1;i=i;return}else{uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function e_(a){a=a|0;var b=0,d=0;b=i;d=c[6354]|0;if((c[14296+(d*24&-1)>>2]|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[14304+(d*24&-1)>>2]|0)==0){c[9340]=c[a>>2];i=b;return}if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;i=b;return}function e$(a){a=a|0;var b=0,d=0;b=i;d=c[6354]|0;if((c[14296+(d*24&-1)>>2]|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[14304+(d*24&-1)>>2]|0)!=0){c[9340]=c[a>>2];i=b;return}if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;i=b;return}function e0(a){a=a|0;var b=0,d=0;b=i;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;if((c[14296+(d*24&-1)>>2]|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[14304+(d*24&-1)>>2]|0)!=0){i=b;return}c[9340]=c[a>>2];i=b;return}function e1(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;if((a|0)==0){return}b=a|0;if((c[b>>2]|0)>0){d=0;do{e=a+8+(d<<5)|0;f=c[e>>2]|0;do{if((f|0)==1|(f|0)==31){g=a+8+(d<<5)+8|0;if((c[g>>2]|0)!=3){h=f;break}uu(c[a+8+(d<<5)+16>>2]|0);c[g>>2]=1;h=c[e>>2]|0}else{h=f}}while(0);if((h|0)==8){f=a+8+(d<<5)+8|0;e1(c[(c[f>>2]|0)+8>>2]|0);uu(c[f>>2]|0)}d=d+1|0;}while((d|0)<(c[b>>2]|0))}uu(a);return}function e2(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=c[10810]|0;if((e|0)==0){return}else{f=e}L8058:while(1){e=c[f+4>>2]|0;do{if(d){if((a_(b|0,e|0,uA(b|0)|0)|0)!=0){break}a[f+8|0]=1;g=f+16|0;if((c[g>>2]|0)!=3){break}uu(c[f+24>>2]|0);c[g>>2]=1}else{if((aY(b|0,e|0)|0)==0){break L8058}}}while(0);e=c[f>>2]|0;if((e|0)==0){h=5965;break}else{f=e}}if((h|0)==5965){return}a[f+8|0]=1;h=f+16|0;if((c[h>>2]|0)!=3){return}uu(c[f+24>>2]|0);c[h>>2]=1;return}function e3(b,d){b=b|0;d=d|0;var e=0,f=0,g=0;e=e6(b)|0;if((e|0)==0){return}b=e+8|0;do{if((a[b]&1)==0){if((aY(c[e+24>>2]|0,d|0)|0)==0){return}f=e+16|0;if((c[f>>2]|0)!=3){break}uu(c[e+24>>2]|0);c[f>>2]=1}else{a[b]=0}}while(0);if((d|0)==0){g=0}else{g=bP(d|0)|0}c[e+16>>2]=3;c[e+24>>2]=g;return}function e4(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,t=0.0;e=i;f=1;g=0;j=i;i=i+168|0;c[j>>2]=0;while(1)switch(f|0){case 1:a[1960]=0;k=al(4)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;c[k>>2]=0;c[6354]=-1;if((a[43504]&1)==0){f=3;break}else{f=2;break};case 2:if((a[47472]&1)==0){f=3;break}else{f=5;break};case 3:l=uL(42120,f,j)|0;f=27;break;case 27:if((l|0)==0){f=4;break}else{f=26;break};case 4:av(2,8,136);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;f=5;break;case 5:n=c[9340]|0;o=0;f=6;break;case 6:if((o|0)<(c[b>>2]|0)){f=7;break}else{f=9;break};case 7:k=c[b+8+(o<<5)>>2]|0;c[9340]=1;am(c[41164+(k<<3)>>2]|0,b+8+(o<<5)+8|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;p=c[9340]|0;if((k-37|0)>>>0<4|(p|0)==1){o=p+o|0;f=6;break}else{f=8;break};case 8:ar(6,-1|0,113648,(v=i,i=i+8|0,c[v>>2]=206200,v)|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;case 9:c[9340]=n;if((a[43504]&1)==0){f=11;break}else{f=10;break};case 10:if((a[47472]&1)==0){f=11;break}else{f=12;break};case 11:av(2,8,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;f=12;break;case 12:p=al(4)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;if((c[p>>2]|0)==33){f=14;break}else{f=13;break};case 13:p=al(4)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;if((c[p>>2]|0)==34){f=14;break}else{f=15;break};case 14:a[1960]=1;f=26;break;case 15:if((a[1960]&1)==0){f=16;break}else{f=26;break};case 16:q=c[6354]|0;if((q|0)<0){f=17;break}else{f=18;break};case 17:ar(6,-1|0,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;case 18:c[6354]=q-1;p=d;k=14296+(q*24&-1)|0;c[p>>2]=c[k>>2];c[p+4>>2]=c[k+4>>2];c[p+8>>2]=c[k+8>>2];c[p+12>>2]=c[k+12>>2];c[p+16>>2]=c[k+16>>2];c[p+20>>2]=c[k+20>>2];if((q|0)==0){f=20;break}else{f=19;break};case 19:au(4,209896,98,1,c[m>>2]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;f=20;break;case 20:k=c[d>>2]|0;if((k|0)==1){f=21;break}else if((k|0)==2){f=22;break}else if((k|0)==3){t=0.0;f=24;break}else{f=23;break};case 21:t=+(c[d+8>>2]|0);f=24;break;case 22:t=+h[d+8>>3];f=24;break;case 23:ar(6,-1|0,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return}r=s=0;case 24:if(t>8.988465674311579e+307|t<-8.988465674311579e+307){f=25;break}else{f=26;break};case 25:a[1960]=1;f=26;break;case 26:i=e;return;case-1:if((g|0)==3){l=s;f=27}r=s=0;break}}function e5(b){b=b|0;aV(8,136);a[1960]=1;cv(42120|0,1)}function e6(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;e=43240;while(1){f=c[e>>2]|0;if((f|0)==0){break}if((aY(b|0,c[f+4>>2]|0)|0)==0){g=f;h=5992;break}else{e=f|0}}if((h|0)==5992){i=d;return g|0}h=ut(40)|0;do{if((h|0)==0){gk();f=ut(40)|0;if((f|0)!=0){j=f;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=213816,v)|0);return 0}else{j=h}}while(0);c[e>>2]=j;c[j>>2]=0;if((b|0)==0){k=0}else{k=bP(b|0)|0}c[(c[e>>2]|0)+4>>2]=k;a[(c[e>>2]|0)+8|0]=1;c[(c[e>>2]|0)+16>>2]=0;g=c[e>>2]|0;i=d;return g|0}function e7(b){b=b|0;var d=0,e=0,f=0.0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0;d=i;if((b|0)==3|(b|0)==2|(b|0)==0){e=6024}else if((b|0)==1){e8(2);e8(1);e8(6);e8(5);e8(0);e8(3);e8(8);e8(9);e8(10);f=+h[8685];j=e6(205392)|0;if((j|0)!=0){a[j+8|0]=0;c[j+16>>2]=2;h[j+24>>3]=f;h[j+32>>3]=0.0}f=+h[8701];j=e6(204792)|0;if((j|0)!=0){a[j+8|0]=0;c[j+16>>2]=2;h[j+24>>3]=f;h[j+32>>3]=0.0}j=c[16536]|0;f=+h[(c[3524]|0)+176>>3];k=e6(187496)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=~~(+(j|0)/f)}j=c[16537]|0;f=+h[(c[3524]|0)+176>>3];k=e6(186968)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=~~(+(j|0)/f)}j=c[16364]|0;f=+h[(c[3524]|0)+176>>3];k=e6(186448)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=~~(+(j|0)/f)}j=c[16365]|0;f=+h[(c[3524]|0)+176>>3];k=e6(185848)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=~~(+(j|0)/f)}j=c[13881]|0;k=e6(185384)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=j+1}j=c[13883]|0;k=e6(184936)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=j+1}j=a[37384]|0;k=e6(204536)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=(j&1^1)&255}j=a[37384]|0;k=e6(204264)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=j&1}j=c[5094]|0;k=e6(204008)|0;if((k|0)!=0){a[k+8|0]=0;c[k+16>>2]=1;c[k+24>>2]=(j|0)!=0&1}f=+g[3538];j=e6(196288)|0;if((j|0)!=0){a[j+8|0]=0;c[j+16>>2]=2;h[j+24>>3]=f;h[j+32>>3]=0.0}f=+g[3536];j=e6(195744)|0;if((j|0)!=0){a[j+8|0]=0;c[j+16>>2]=2;h[j+24>>3]=f;h[j+32>>3]=0.0}f=+g[3534];j=e6(148264)|0;if((j|0)!=0){a[j+8|0]=0;c[j+16>>2]=2;h[j+24>>3]=f;h[j+32>>3]=0.0}f=+g[3532];j=e6(148040)|0;if((j|0)==0){i=d;return}a[j+8|0]=0;c[j+16>>2]=2;h[j+24>>3]=f;h[j+32>>3]=0.0;i=d;return}do{if((e|0)==6024){j=c[3524]|0;L8156:do{if((j|0)==0){k=e6(202664)|0;if((k|0)==0){break}l=k+8|0;do{if((a[l]&1)==0){if((aY(c[k+24>>2]|0,225016)|0)==0){break L8156}m=k+16|0;if((c[m>>2]|0)!=3){n=m;break}uu(c[k+24>>2]|0);c[m>>2]=1;n=m}else{a[l]=0;n=k+16|0}}while(0);l=bP(225016)|0;c[n>>2]=3;c[k+24>>2]=l}else{e3(202664,c[j>>2]|0)}}while(0);j=e6(201560)|0;L8167:do{if((j|0)!=0){l=j+8|0;do{if((a[l]&1)==0){if((aY(c[j+24>>2]|0,13048)|0)==0){break L8167}m=j+16|0;if((c[m>>2]|0)!=3){o=m;break}uu(c[j+24>>2]|0);c[m>>2]=1;o=m}else{a[l]=0;o=j+16|0}}while(0);l=bP(13048)|0;c[o>>2]=3;c[j+24>>2]=l}}while(0);j=c[8244]|0;e3(200696,(j|0)!=0?j:179864);e3(199888,c[44936+(c[11252]<<2)>>2]|0);if((b|0)==3){p=43240}else if((b|0)==2){j=e6(199272)|0;if((j|0)==0){i=d;return}else{a[j+8|0]=0;c[j+16>>2]=1;c[j+24>>2]=1;break}}else{break}while(1){j=c[p>>2]|0;if((j|0)==0){e=6045;break}if((aY(198952,c[j+4>>2]|0)|0)==0){q=j;e=6049;break}else{p=j|0}}if((e|0)==6045){j=ut(40)|0;do{if((j|0)==0){gk();l=ut(40)|0;if((l|0)!=0){r=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=213816,v)|0)}else{r=j}}while(0);c[p>>2]=r;c[r>>2]=0;j=bP(198952)|0;c[(c[p>>2]|0)+4>>2]=j;a[(c[p>>2]|0)+8|0]=1;c[(c[p>>2]|0)+16>>2]=0;j=c[p>>2]|0;if((j|0)==0){s=43240}else{q=j;e=6049}}do{if((e|0)==6049){j=q+8|0;if((a[j]&1)==0){s=43240;break}a[j]=0;f=+uz(40152,0);c[q+16>>2]=2;h[q+24>>3]=f;h[q+32>>3]=0.0;s=43240}}while(0);while(1){j=c[s>>2]|0;if((j|0)==0){e=6053;break}if((aY(198528,c[j+4>>2]|0)|0)==0){t=j;e=6057;break}else{s=j|0}}if((e|0)==6053){j=ut(40)|0;do{if((j|0)==0){gk();l=ut(40)|0;if((l|0)!=0){u=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=213816,v)|0)}else{u=j}}while(0);c[s>>2]=u;c[u>>2]=0;j=bP(198528)|0;c[(c[s>>2]|0)+4>>2]=j;a[(c[s>>2]|0)+8|0]=1;c[(c[s>>2]|0)+16>>2]=0;j=c[s>>2]|0;if((j|0)==0){w=43240}else{t=j;e=6057}}L8202:do{if((e|0)==6057){if((a[t+8|0]&1)==0){w=43240;break}j=e6(198528)|0;if((j|0)==0){w=43240;break}l=j+8|0;do{if((a[l]&1)==0){if((aY(c[j+24>>2]|0,40160)|0)==0){w=43240;break L8202}k=j+16|0;if((c[k>>2]|0)!=3){x=k;break}uu(c[j+24>>2]|0);c[k>>2]=1;x=k}else{a[l]=0;x=j+16|0}}while(0);l=bP(40160)|0;c[x>>2]=3;c[j+24>>2]=l;w=43240}}while(0);while(1){l=c[w>>2]|0;if((l|0)==0){e=6067;break}if((aY(198248,c[l+4>>2]|0)|0)==0){y=l;e=6071;break}else{w=l|0}}if((e|0)==6067){l=ut(40)|0;do{if((l|0)==0){gk();k=ut(40)|0;if((k|0)!=0){z=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=213816,v)|0)}else{z=l}}while(0);c[w>>2]=z;c[z>>2]=0;l=bP(198248)|0;c[(c[w>>2]|0)+4>>2]=l;a[(c[w>>2]|0)+8|0]=1;c[(c[w>>2]|0)+16>>2]=0;l=c[w>>2]|0;if((l|0)!=0){y=l;e=6071}}do{if((e|0)==6071){if((a[y+8|0]&1)==0){break}e3(198248,c[13388]|0)}}while(0);l=e6(104120)|0;if((l|0)!=0){a[l+8|0]=0;c[l+16>>2]=1;c[l+24>>2]=0}l=e6(204536)|0;if((l|0)!=0){a[l+8|0]=0;c[l+16>>2]=1;c[l+24>>2]=0}l=e6(204264)|0;if((l|0)!=0){a[l+8|0]=0;c[l+16>>2]=1;c[l+24>>2]=0}l=lt()|0;e3(197288,l);uu(l);e3(199888,c[44936+(c[11252]<<2)>>2]|0);l=e6(196632)|0;if((l|0)!=0){a[l+8|0]=0;c[l+16>>2]=2;h[l+24>>3]=3.141592653589793;h[l+32>>3]=0.0}f=+uz(133568,0);l=e6(196016)|0;if((l|0)==0){break}a[l+8|0]=0;c[l+16>>2]=2;h[l+24>>3]=f;h[l+32>>3]=0.0}}while(0);L8239:do{if((b-3|0)>>>0<2){y=e6(199272)|0;if((y|0)!=0){a[y+8|0]=0;c[y+16>>2]=1;c[y+24>>2]=0}y=e6(137752)|0;if((y|0)==0){break}w=y+8|0;do{if((a[w]&1)==0){if((a[c[y+24>>2]|0]|0)==0){break L8239}z=y+16|0;if((c[z>>2]|0)!=3){A=z;break}uu(c[y+24>>2]|0);c[z>>2]=1;A=z}else{a[w]=0;A=y+16|0}}while(0);w=bP(179864)|0;c[A>>2]=3;c[y+24>>2]=w}}while(0);if((b|0)==5|(b|0)==3){e=6094}else if((b|0)!=6){i=d;return}do{if((e|0)==6094){A=ut(4096)|0;do{if((A|0)==0){gk();w=ut(4096)|0;if((w|0)!=0){B=w;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=193744,v)|0)}else{B=A}}while(0);bl(B|0,4096);e3(192848,B);uu(B);if((b|0)==6){break}i=d;return}}while(0);b=c[12900]|0;B=e6(191128)|0;if((B|0)==0){i=d;return}a[B+8|0]=0;c[B+16>>2]=1;c[B+24>>2]=b;i=d;return}function e8(b){b=b|0;var d=0,e=0,f=0.0,g=0.0,j=0.0,k=0.0,l=0.0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0.0;d=i;i=i+24|0;e=64788+(b*688&-1)|0;f=+h[64664+(b*688&-1)>>3];if((a[e]&1)==0){g=+h[64672+(b*688&-1)>>3];j=f}else{k=+h[64800+(b*688&-1)>>3];l=+Z(+(f*k));g=+Z(+(+h[64672+(b*688&-1)>>3]*k));j=l}m=(c[64656+(b*688&-1)>>2]&2|0)!=0&j>g;l=m?j:g;k=m?g:j;m=d|0;n=56832+(b*24&-1)|0;be(m|0,182200,(v=i,i=i+24|0,c[v>>2]=184544,c[v+8>>2]=n,c[v+16>>2]=184256,v)|0);o=a[m]|0;if(o<<24>>24!=0){p=m;q=o;do{a[p]=bC(q<<24>>24|0)&255;p=p+1|0;q=a[p]|0;}while(q<<24>>24!=0)}q=e6(m)|0;if((q|0)!=0){a[q+8|0]=0;c[q+16>>2]=2;h[q+24>>3]=k;h[q+32>>3]=0.0}be(m|0,182200,(v=i,i=i+24|0,c[v>>2]=184544,c[v+8>>2]=n,c[v+16>>2]=183776,v)|0);q=a[m]|0;if(q<<24>>24!=0){p=m;o=q;do{a[p]=bC(o<<24>>24|0)&255;p=p+1|0;o=a[p]|0;}while(o<<24>>24!=0)}o=e6(m)|0;if((o|0)!=0){a[o+8|0]=0;c[o+16>>2]=2;h[o+24>>3]=l;h[o+32>>3]=0.0}l=+h[64792+(b*688&-1)>>3];be(m|0,182200,(v=i,i=i+24|0,c[v>>2]=184544,c[v+8>>2]=n,c[v+16>>2]=183416,v)|0);o=a[m]|0;if(o<<24>>24!=0){p=m;q=o;do{a[p]=bC(q<<24>>24|0)&255;p=p+1|0;q=a[p]|0;}while(q<<24>>24!=0)}q=e6(m)|0;if((q|0)!=0){a[q+8|0]=0;c[q+16>>2]=2;h[q+24>>3]=l;h[q+32>>3]=0.0}if(b>>>0>=7){i=d;return}l=+h[64712+(b*688&-1)>>3];if((a[e]&1)==0){r=l}else{r=+Z(+(l*+h[64800+(b*688&-1)>>3]))}be(m|0,182200,(v=i,i=i+24|0,c[v>>2]=182880,c[v+8>>2]=n,c[v+16>>2]=184256,v)|0);q=a[m]|0;if(q<<24>>24!=0){p=m;o=q;do{a[p]=bC(o<<24>>24|0)&255;p=p+1|0;o=a[p]|0;}while(o<<24>>24!=0)}o=e6(m)|0;if((o|0)!=0){a[o+8|0]=0;c[o+16>>2]=2;h[o+24>>3]=r;h[o+32>>3]=0.0}r=+h[64720+(b*688&-1)>>3];if((a[e]&1)==0){s=r}else{s=+Z(+(r*+h[64800+(b*688&-1)>>3]))}be(m|0,182200,(v=i,i=i+24|0,c[v>>2]=182880,c[v+8>>2]=n,c[v+16>>2]=183776,v)|0);n=a[m]|0;if(n<<24>>24!=0){b=m;e=n;do{a[b]=bC(e<<24>>24|0)&255;b=b+1|0;e=a[b]|0;}while(e<<24>>24!=0)}e=e6(m)|0;if((e|0)==0){i=d;return}a[e+8|0]=0;c[e+16>>2]=2;h[e+24>>3]=s;h[e+32>>3]=0.0;i=d;return}function e9(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;b=i;i=i+16|0;d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}e=d+1|0;c[6354]=e;d=14296+(e*24&-1)|0;c[d>>2]=3;c[14304+(e*24&-1)>>2]=a;f=d+12|0;d=b|0;c[f>>2]=c[d>>2];c[f+4>>2]=c[d+4>>2];c[f+8>>2]=c[d+8>>2];if((a|0)==0){g=e}else{e=bP(a|0)|0;a=c[6354]|0;c[14304+(a*24&-1)>>2]=e;g=a}if((g|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}a=g+1|0;c[6354]=a;g=14296+(a*24&-1)|0;c[g>>2]=1;c[14304+(a*24&-1)>>2]=-1;a=g+12|0;c[a>>2]=c[d>>2];c[a+4>>2]=c[d+4>>2];c[a+8>>2]=c[d+8>>2];hb(0);d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{c[6354]=d-1;i=b;return c[14304+(d*24&-1)>>2]|0}return 0}function fa(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;i=i+16|0;e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}f=e+1|0;c[6354]=f;e=14296+(f*24&-1)|0;c[e>>2]=3;c[14304+(f*24&-1)>>2]=a;g=e+12|0;e=d|0;c[g>>2]=c[e>>2];c[g+4>>2]=c[e+4>>2];c[g+8>>2]=c[e+8>>2];if((a|0)==0){h=f}else{f=bP(a|0)|0;a=c[6354]|0;c[14304+(a*24&-1)>>2]=f;h=a}if((h|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}a=h+1|0;c[6354]=a;h=14296+(a*24&-1)|0;c[h>>2]=1;c[14304+(a*24&-1)>>2]=b;b=h+12|0;c[b>>2]=c[e>>2];c[b+4>>2]=c[e+4>>2];c[b+8>>2]=c[e+8>>2];hb(0);e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{c[6354]=e-1;i=d;return c[14304+(e*24&-1)>>2]|0}return 0}function fb(){var b=0,d=0;uD(42280,101104,9);b=a8(42280,0)|0;while(1){d=b-1|0;if((a[d]|0)==10){b=d}else{break}}d=b;w=10;a[d]=w&255;w=w>>8;a[d+1|0]=w&255;aK(42280,c[m>>2]|0);d=b+1|0;w=10;a[d]=w&255;w=w>>8;a[d+1|0]=w&255;d=c[8762]|0;if((d|0)!=0){cf(d|0,146328,(v=i,i=i+8|0,c[v>>2]=42280,v)|0);d=c[8762]|0;az(d|0);c[8762]=0}d=c[10062]|0;if((d|0)!=0){e1(d);c[10062]=0}aV(2,4);aV(13,1);uf(-1,130168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function fc(d,e){d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0.0,O=0,P=0.0,Q=0,R=0.0,S=0,T=0,U=0;f=i;i=i+968|0;g=f|0;j=f+256|0;k=f+512|0;l=f+576|0;m=f+704|0;n=f+960|0;o=j|0;do{if((e|0)==0){p=6169}else{if((a[e]|0)==0){p=6169;break}q=m|0;uF(q|0,d|0,256);if((uA(d|0)|0)>>>0<256){r=e;break}a[m+255|0]=0;r=e}}while(0);do{if((p|0)==6169){e=m|0;uB(e|0,d|0);q=m+(uA(e|0)|0)|0;a[q]=a[214408]|0;a[q+1|0]=a[214409|0]|0;a[q+2|0]=a[214410|0]|0;a[q+3|0]=a[214411|0]|0;a[q+4|0]=a[214412|0]|0;if((b8(d|0,e|0)|0)==0){r=d;break}be(42289,213616,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=e,v)|0);fb()}}while(0);d=m|0;e=g|0;g=(uA(d|0)|0)-1|0;q=m+g|0;L8361:do{if((g|0)<0){s=q}else{m=q;while(1){t=a[m]|0;if((t<<24>>24|0)==92|(t<<24>>24|0)==47|(t<<24>>24|0)==58){s=m;break L8361}t=m-1|0;if(t>>>0<d>>>0){s=t;break}else{m=t}}}}while(0);uB(e|0,s+1|0);s=hA(d,193632)|0;if((s|0)==0){be(42289,101576,(v=i,i=i+8|0,c[v>>2]=d,v)|0);fb()}d=bF(r|0,137896)|0;if((d|0)==0){be(42289,89920,(v=i,i=i+8|0,c[v>>2]=r,v)|0);fb()}L8372:do{if((a6(o|0,256,s|0)|0)!=0){r=l|0;q=l;g=l+126|0;m=k|0;t=k+63|0;u=0;L8374:while(1){w=o;while(1){x=a[w]|0;if((x<<24>>24|0)==35|(x<<24>>24|0)==0){aK(o|0,d|0);if((a6(o|0,256,s|0)|0)==0){break L8372}else{w=o;continue}}else if((x<<24>>24|0)==32|(x<<24>>24|0)==9|(x<<24>>24|0)==10){w=w+1|0;continue}else{break}}w=a8(o|0,35)|0;if((w|0)==0){b[q>>1]=10;y=o}else{uF(r|0,w|0,127);if((uA(w|0)|0)>>>0>=127){a[g]=0}a[w]=0;y=o}while(1){w=a[y]|0;if((w<<24>>24|0)==10|(w<<24>>24|0)==0){z=0;A=u;B=o;break}else if(!((w<<24>>24|0)==32|(w<<24>>24|0)==9|(w<<24>>24|0)==61)){p=6190;break}y=y+1|0}if((p|0)==6190){p=0;w=cy(y|0,213208)|0;if((w|0)==0){C=y+(uA(y|0)|0)|0}else{C=w}w=a[C]|0;a[C]=0;z=y;A=w;B=C+1|0}w=a[z]|0;if(w<<24>>24!=0){x=z;D=w;do{if((bs(D&255|0)|0)==0){if((a[x]|0)!=95){p=6244;break L8374}}x=x+1|0;D=a[x]|0;}while(D<<24>>24!=0)}if((uA(z|0)|0)>>>0>50){p=6243;break}uF(m|0,z|0,64);if((uA(z|0)|0)>>>0>=64){a[t]=0}if(A<<24>>24==61){E=B}else{D=a8(B|0,61)|0;if((D|0)==0){p=6203;break}E=D+1|0}D=E;while(1){x=a[D]|0;if((x<<24>>24|0)==10|(x<<24>>24|0)==0){F=0;G=A;H=E;break}else if(!((x<<24>>24|0)==32|(x<<24>>24|0)==9|(x<<24>>24|0)==61)){p=6208;break}D=D+1|0}if((p|0)==6208){p=0;x=cy(D|0,213208)|0;if((x|0)==0){I=D+(uA(D|0)|0)|0}else{I=x}x=a[I]|0;a[I]=0;F=D;G=x;H=I+1|0}if((ca(F|0,148448,(v=i,i=i+8|0,c[v>>2]=n,v)|0)|0)==0){p=6212;break}else{J=H}while(1){x=a[J]|0;if((x<<24>>24|0)==10|(x<<24>>24|0)==0){K=G;break}else if(!((x<<24>>24|0)==32|(x<<24>>24|0)==9|(x<<24>>24|0)==61)){p=6215;break}J=J+1|0}if((p|0)==6215){p=0;D=cy(J|0,213208)|0;if((D|0)==0){L=J+(uA(J|0)|0)|0}else{L=D}D=a[L]|0;a[L]=0;if((J|0)==0){K=D}else{p=6218;break}}D=43240;while(1){M=c[D>>2]|0;if((M|0)==0){p=6221;break}if((aY(m|0,c[M+4>>2]|0)|0)==0){p=6223;break}else{D=M|0}}if((p|0)==6223){p=0;D=c[M+16>>2]|0;if((D|0)==1){N=+(c[M+24>>2]|0)}else if((D|0)==3){N=+uz(c[M+24>>2]|0,0)}else if((D|0)==2){N=+h[M+24>>3]}else{p=6227;break}h[n>>3]=N;if(N==0.0){O=43240;p=6229}else{P=N}}else if((p|0)==6221){p=0;h[n>>3]=0.0;O=43240;p=6229}if((p|0)==6229){while(1){p=0;Q=c[O>>2]|0;if((Q|0)==0){R=0.0;break}if((aY(m|0,c[Q+4>>2]|0)|0)==0){p=6231;break}else{O=Q|0;p=6229}}do{if((p|0)==6231){p=0;if((c[Q+16>>2]|0)!=1){R=0.0;break}R=+(c[Q+24>>2]|0)}}while(0);h[n>>3]=R;P=R}be(o|0,170912,(v=i,i=i+8|0,h[v>>3]=P,v)|0);do{if((a8(o|0,46)|0)==0){if((a8(o|0,101)|0)!=0){break}D=j+(uA(o|0)|0)|0;a[D]=a[119960]|0;a[D+1|0]=a[119961|0]|0;a[D+2|0]=a[119962|0]|0}}while(0);cf(d|0,209168,(v=i,i=i+24|0,c[v>>2]=m,c[v+8>>2]=o,c[v+16>>2]=r,v)|0);if((a6(o|0,256,s|0)|0)==0){break L8372}else{u=K}}if((p|0)==6243){S=az(d|0)|0;T=az(s|0)|0;U=be(42289,80408,(v=i,i=i+8|0,c[v>>2]=e,v)|0)|0;fb()}else if((p|0)==6203){az(d|0);az(s|0);be(42289,80408,(v=i,i=i+8|0,c[v>>2]=e,v)|0);fb()}else if((p|0)==6212){az(d|0);az(s|0);be(42289,80408,(v=i,i=i+8|0,c[v>>2]=e,v)|0);fb()}else if((p|0)==6244){S=az(d|0)|0;T=az(s|0)|0;U=be(42289,80408,(v=i,i=i+8|0,c[v>>2]=e,v)|0)|0;fb()}else if((p|0)==6227){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((p|0)==6218){az(d|0);az(s|0);be(42289,80408,(v=i,i=i+8|0,c[v>>2]=e,v)|0);fb()}}}while(0);if((az(d|0)|0)!=0){uD(42289,203968,24);fb()}if((az(s|0)|0)==0){i=f;return}else{uD(42289,203968,24);fb()}}function fd(){var b=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0.0,B=0.0,C=0,D=0.0,E=0.0,F=0,G=0.0,H=0,I=0.0,J=0.0,K=0.0,L=0,M=0.0,N=0.0,R=0.0,S=0,T=0.0,U=0.0,V=0.0,W=0,X=0.0,Y=0.0,Z=0.0,$=0.0,ab=0.0,ac=0.0,ad=0.0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0.0,aq=0,ar=0,as=0,at=0,au=0.0,av=0,aw=0.0,ax=0,ay=0,aA=0,aB=0,aC=0,aD=0,aE=0,aG=0,aH=0,aJ=0,aL=0,aM=0,aN=0,aO=0,aP=0,aR=0,aS=0,aU=0.0,aW=0,aX=0,aZ=0,a_=0,a$=0,a0=0,a2=0,a3=0,a4=0,a5=0,a7=0,a9=0,ba=0,bb=0.0,bc=0,bd=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0,bB=0,bC=0.0,bE=0,bG=0.0,bH=0.0,bI=0.0,bK=0,bL=0.0,bM=0,bN=0.0,bQ=0,bR=0,bS=0.0,bT=0,bV=0.0,bW=0.0,bX=0.0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0.0;b=i;i=i+320|0;e=b|0;f=b+8|0;g=b+16|0;j=b+40|0;k=b+88|0;l=b+136|0;n=b+216|0;p=b+240|0;q=b+272|0;r=q;s=i;i=i+56|0;t=i;i=i+4|0;i=i+7>>3<<3;u=i;i=i+688|0;w=i;i=i+8|0;x=i;i=i+1025|0;i=i+7>>3<<3;uE(p|0,-1|0,28);y=(c[13898]|0)+1|0;c[13898]=y;z=c[16507]|0;c[16506]=z;if((z&3|0)==0){A=+h[8258];B=+h[8257];if(A<B){C=66056;D=A}else{E=B;F=6250}}else{E=+h[8257];F=6250}if((F|0)==6250){C=66064;D=E}h[8255]=D;h[8256]=+h[C>>3];if((a[66164]&1)==0){G=0.0}else{G=+_(+(+h[8271]))}h[8272]=G;h[8261]=8.988465674311579e+307;h[8262]=-8.988465674311579e+307;C=c[16335]|0;c[16334]=C;if((C&3|0)==0){G=+h[8172];D=+h[8171];if(G<D){H=65368;I=G}else{J=D;F=6256}}else{J=+h[8171];F=6256}if((F|0)==6256){H=65376;I=J}h[8169]=I;h[8170]=+h[H>>3];if((a[65476]&1)==0){K=0.0}else{K=+_(+(+h[8185]))}h[8186]=K;h[8175]=8.988465674311579e+307;h[8176]=-8.988465674311579e+307;H=c[17539]|0;c[17538]=H;if((H&3|0)==0){K=+h[8774];I=+h[8773];if(K<I){L=70184;M=K}else{N=I;F=6262}}else{N=+h[8773];F=6262}if((F|0)==6262){L=70192;M=N}h[8771]=M;h[8772]=+h[L>>3];if((a[70292]&1)==0){R=0.0}else{R=+_(+(+h[8787]))}h[8788]=R;h[8777]=8.988465674311579e+307;h[8778]=-8.988465674311579e+307;L=c[17711]|0;c[17710]=L;if((L&3|0)==0){R=+h[8860];M=+h[8859];if(R<M){S=70872;T=R}else{U=M;F=6268}}else{U=+h[8859];F=6268}if((F|0)==6268){S=70880;T=U}h[8857]=T;h[8858]=+h[S>>3];if((a[70980]&1)==0){V=0.0}else{V=+_(+(+h[8873]))}h[8874]=V;h[8863]=8.988465674311579e+307;h[8864]=-8.988465674311579e+307;S=c[17883]|0;c[17882]=S;if((S&3|0)==0){V=+h[8946];T=+h[8945];if(V<T){W=71560;X=V}else{Y=T;F=6274}}else{Y=+h[8945];F=6274}if((F|0)==6274){W=71568;X=Y}h[8943]=X;h[8944]=+h[W>>3];if((a[71668]&1)==0){Z=0.0}else{Z=+_(+(+h[8959]))}h[8960]=Z;h[8949]=8.988465674311579e+307;h[8950]=-8.988465674311579e+307;W=c[16163]|0;c[16162]=W;do{if((W&3|0)==0){Z=+h[8086];X=+h[8085];if(Z>=X){$=X;ab=Z;F=6281;break}h[8083]=Z;ac=X}else{$=+h[8085];ab=+h[8086];F=6281}}while(0);if((F|0)==6281){h[8083]=(W&1|0)==0?$:8.988465674311579e+307;ac=(W&2|0)==0?ab:-8.988465674311579e+307}h[8084]=ac;if((a[64788]&1)==0){ad=0.0}else{ad=+_(+(+h[8099]))}h[8100]=ad;h[8089]=8.988465674311579e+307;h[8090]=-8.988465674311579e+307;c[200]=2;c[144]=1;c[34]=0;L8524:do{if((c[8272]|0)>(y|0)){W=u;S=p+24|0;L=-1;H=0;C=y;while(1){z=c[1054]|0;if((a[z+(C*40&-1)|0]&1)==0){ae=L;af=H;ag=C;break L8524}ah=c[z+(C*40&-1)+36>>2]|0;ai=z+(C*40&-1)+32|0;z=c[10036]|0;aj=0;while(1){if((aj|0)>=(ah|0)){break}if((a[z+((c[ai>>2]|0)+aj|0)|0]|0)==(a[aj+78280|0]|0)){aj=aj+1|0}else{ae=L;af=H;ag=C;break L8524}}if((aj|0)!=1){ae=L;af=H;ag=C;break L8524}if((H|0)>5){break}ai=c[42584+(H<<2)>>2]|0;uD(W|0,64648+(ai*688&-1)|0,688);c[S>>2]=c[p+(ai<<2)>>2];c[p+(H<<2)>>2]=dr(ai,-1)|0;ai=c[13898]|0;z=H+1|0;if((c[8272]|0)>(ai|0)){L=ai;H=z;C=ai}else{ae=ai;af=z;ag=ai;break L8524}}uf(C,192712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{ae=-1;af=0;ag=y}}while(0);y=c[10062]|0;if((y|0)!=0){e1(y);c[10062]=0}c[11670]=40240;uB(42608,24544);uB(42659,24595);y=0;do{H=c[p+(y<<2)>>2]|0;if((H|0)>-1){L=c[1054]|0;S=c[L+(H*40&-1)+36>>2]|0;W=(S|0)>49?49:S;S=c[10036]|0;ai=c[L+(H*40&-1)+32>>2]|0;H=0;while(1){L=H+1|0;a[55600+(y*51&-1)+H|0]=a[S+ai|0]|0;if((L|0)==(W|0)){break}else{ai=ai+1|0;H=L}}a[55600+(y*51&-1)+W|0]=0}else{uB(55600+(y*51&-1)|0,42608+(y*51&-1)|0)}y=y+1|0;}while((y|0)<5);it();y=c[18070]|0;H=db(y,c[y>>2]<<5|8,105624)|0;c[18070]=0;c[10062]=H;c[11670]=0;H=c[13898]|0;if((H|0)>=(c[8272]|0)){ak=H;uf(ak,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}y=c[1054]|0;L8553:do{if((a[y+(H*40&-1)|0]&1)!=0){ai=c[y+(H*40&-1)+36>>2]|0;S=y+(H*40&-1)+32|0;C=c[10036]|0;L=0;while(1){if((L|0)>=(ai|0)){break}if((a[C+((c[S>>2]|0)+L|0)|0]|0)==(a[L+103664|0]|0)){L=L+1|0}else{break L8553}}if((L|0)==1){ak=H}else{break}uf(ak,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);a[14176]=1;is(n);a[14176]=0;if((c[n>>2]|0)!=3){c[13898]=H;ak=H;uf(ak,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}y=c[n+8>>2]|0;if((y|0)==0){ak=c[13898]|0;uf(ak,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[11856]=0;c[13486]=ey(y,7,0)|0;uu(y);y=c[13486]|0;if((y|0)<0){uf(-1,120240,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((y|0)==1){uf(c[13898]|0,174456,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}ak=(y|0)<3?1:y-2|0;c[8304]=ak;if((c[64808+((c[200]|0)*688&-1)>>2]|0)==1&(y|0)<2){uf(c[13898]|0,84448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[64808+((c[144]|0)*688&-1)>>2]|0)==1&(y|0)<1){uf(c[13898]|0,106024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((ak|0)>0){y=(ak|0)>1?ak:1;uD(48840,42584,y<<2);al=y}else{al=0}c[48840+(al<<2)>>2]=0;c[48840+(al+1<<2)>>2]=99;al=c[p+4>>2]|0;if((al|0)>-1&(ak|0)==1){uf(al,159352,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}al=ak+1|0;if((af|0)>(al|0)){uf(ae,152936,(v=i,i=i+8|0,c[v>>2]=ak,v)|0)}if((af|0)==(al|0)&(ak|0)<5){ak=af-1|0;af=c[42584+(ak<<2)>>2]|0;al=64648+(af*688&-1)|0;ae=al|0;y=c[34]|0;c[64648+(y*688&-1)>>2]=c[ae>>2];n=c[ae>>2]|0;if((n&1|0)==0){h[64664+(y*688&-1)>>3]=+h[64664+(af*688&-1)>>3];am=c[ae>>2]|0}else{am=n}if((am&2|0)==0){h[64672+(y*688&-1)>>3]=+h[64672+(af*688&-1)>>3]}uD(al|0,u|0,688);c[p+(ak<<2)>>2]=c[p+24>>2]}p=c[13898]|0;ak=43240;while(1){an=c[ak>>2]|0;if((an|0)==0){ao=43240;break}if((aY(214968,c[an+4>>2]|0)|0)==0){F=6337;break}else{ak=an|0}}do{if((F|0)==6337){ak=c[an+16>>2]|0;if((ak|0)==2){ap=+h[an+24>>3]}else if((ak|0)==3){ap=+uz(c[an+24>>2]|0,0)}else if((ak|0)==1){ap=+(c[an+24>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(!(ap<1.0&ap>0.0)){ao=43240;break}h[5479]=ap;ao=43240}}while(0);while(1){aq=c[ao>>2]|0;if((aq|0)==0){ar=0;break}if((aY(215432,c[aq+4>>2]|0)|0)==0){F=6346;break}else{ao=aq|0}}do{if((F|0)==6346){if((c[aq+16>>2]|0)!=1){ar=0;break}ar=c[aq+24>>2]|0}}while(0);c[8708]=ar;ar=43240;while(1){as=c[ar>>2]|0;if((as|0)==0){at=43240;break}if((aY(215960,c[as+4>>2]|0)|0)==0){F=6351;break}else{ar=as|0}}do{if((F|0)==6351){ar=c[as+16>>2]|0;if((ar|0)==1){au=+(c[as+24>>2]|0)}else if((ar|0)==2){au=+h[as+24>>3]}else if((ar|0)==3){au=+uz(c[as+24>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(au<=0.0){at=43240;break}h[1782]=au;bD(150480,(v=i,i=i+8|0,h[v>>3]=au,v)|0);at=43240}}while(0);while(1){av=c[at>>2]|0;if((av|0)==0){break}if((aY(216424,c[av+4>>2]|0)|0)==0){F=6360;break}else{at=av|0}}do{if((F|0)==6360){at=c[av+16>>2]|0;if((at|0)==1){aw=+(c[av+24>>2]|0)}else if((at|0)==2){aw=+h[av+24>>3]}else if((at|0)==3){aw=+uz(c[av+24>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(aw<=0.0){break}h[4485]=aw;h[4484]=aw;bD(148576,(v=i,i=i+8|0,h[v>>3]=aw,v)|0)}}while(0);uu(c[10638]|0);c[10638]=0;av=bU(217040)|0;if((av|0)!=0){c[10638]=bP(av|0)|0}av=fg()|0;at=c[8762]|0;do{if((at|0)==0){as=bF(av|0,205056)|0;c[8762]=as;if((as|0)!=0){ax=as;break}be(42289,144360,(v=i,i=i+8|0,c[v>>2]=av,v)|0);fb()}else{ax=at}}while(0);if((av|0)==0){ay=ax}else{uu(av);ay=c[8762]|0}aI(142744,82,1,ay|0);cG(t|0);ay=c[8762]|0;av=aT(t|0)|0;cf(ay|0,141312,(v=i,i=i+8|0,c[v>>2]=av,v)|0);av=p-1|0;p=c[1054]|0;ay=(c[p+(av*40&-1)+36>>2]|0)+(c[p+(av*40&-1)+32>>2]|0)|0;av=db(0,(ay+1|0)-(c[p+(H*40&-1)+32>>2]|0)|0,116456)|0;p=c[(c[1054]|0)+(H*40&-1)+32>>2]|0;L8653:do{if((p|0)<(ay|0)){t=p;ax=av;while(1){at=a[(c[10036]|0)+t|0]|0;if(at<<24>>24==0){aA=ax;break L8653}as=ax+1|0;a[ax]=at;at=t+1|0;if((at|0)<(ay|0)){t=at;ax=as}else{aA=as;break}}}else{aA=av}}while(0);a[aA]=0;cf(c[8762]|0,139528,(v=i,i=i+8|0,c[v>>2]=av,v)|0);aI(137608,17,1,c[8762]|0);uu(av);L8658:do{if((c[8304]|0)>0){av=0;while(1){aA=c[13486]|0;ay=c[8762]|0;if((av|0)>=(aA-1|0)){aB=ay;aC=aA;break L8658}cf(ay|0,135712,(v=i,i=i+8|0,c[v>>2]=55600+(av*51&-1),v)|0);ay=av+1|0;if((ay|0)<(c[8304]|0)){av=ay}else{F=6378;break}}}else{F=6378}}while(0);if((F|0)==6378){aB=c[8762]|0;aC=c[13486]|0}cf(aB|0,((aC|0)<3?134160:132576)|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);aC=c[8304]|0;if((aC|0)<0){aD=aC}else{aB=l|0;l=0;av=0;ay=aC;while(1){if((c[64648+(l*688&-1)>>2]&3|0)==3){aE=ay}else{aC=c[8762]|0;if((av|0)==0){aG=77416}else{aG=55600+(av*51&-1)|0}cf(aC|0,220408,(v=i,i=i+8|0,c[v>>2]=aG,v)|0);aA=64648+(l*688&-1)|0;do{if((c[aA>>2]&1|0)==0){if((c[64808+(l*688&-1)>>2]|0)==1){aF(34,aC|0);p=64813+(l*688&-1)|0;aw=+h[64664+(l*688&-1)>>3];t3(k,aw);au=aw- +O(+aw);t4(aB,80,p,k,au);aK(aB|0,aC|0);aF(34,aC|0);break}else{au=+h[64664+(l*688&-1)>>3];cf(aC|0,92736,(v=i,i=i+8|0,h[v>>3]=au,v)|0);break}}else{aF(42,aC|0)}}while(0);aI(223968,3,1,aC|0);do{if((c[aA>>2]&2|0)==0){if((c[64808+(l*688&-1)>>2]|0)==1){aF(34,aC|0);p=64813+(l*688&-1)|0;au=+h[64672+(l*688&-1)>>3];t3(j,au);aw=au- +O(+au);t4(aB,80,p,j,aw);aK(aB|0,aC|0);aF(34,aC|0);break}else{aw=+h[64672+(l*688&-1)>>3];cf(aC|0,92736,(v=i,i=i+8|0,h[v>>3]=aw,v)|0);break}}else{aF(42,aC|0)}}while(0);aI(89968,2,1,aC|0);aE=c[8304]|0}aA=av+1|0;if((aA|0)>(aE|0)){aD=aE;break}else{l=c[42584+(av<<2)>>2]|0;av=aA;ay=aE}}}c[8718]=2048;do{if((aD<<11|0)<1){c[10636]=0;aH=2048;F=6405}else{aE=aD<<14;ay=ut(aE)|0;do{if((ay|0)==0){gk();av=ut(aE)|0;if((av|0)!=0){aJ=av;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{aJ=ay}}while(0);ay=c[8718]|0;c[10636]=aJ;if((ay|0)>=1){aH=ay;F=6405;break}c[10634]=0;aL=0}}while(0);do{if((F|0)==6405){aJ=aH<<3;aD=ut(aJ)|0;do{if((aD|0)==0){gk();ay=ut(aJ)|0;if((ay|0)!=0){aM=ay;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{aM=aD}}while(0);aD=c[8718]|0;c[10634]=aM;if((aD|0)<1){aL=0;break}aJ=aD<<3;aD=ut(aJ)|0;do{if((aD|0)==0){gk();ay=ut(aJ)|0;if((ay|0)!=0){aN=ay;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{aN=aD}}while(0);aL=aN}}while(0);c[10878]=aL;c[8306]=0;uE(r|0,0,48);r=c[8270]|0;do{if((r|0)!=0){if((aY(r|0,139696)|0)==0){break}bA(4,r|0)}}while(0);r=s|0;aL=eC(r,7)|0;L8714:do{if((aL|0)==-1){aO=0}else{aN=q|0;aM=s+8|0;aH=0;aD=aL;while(1){aJ=c[8718]|0;if((c[8306]|0)>=(aJ|0)){ay=(aJ*3&-1|0)/2&-1;c[8718]=ay;aJ=aa(c[8304]|0,ay);if((aJ|0)<1){F=6420;break}ay=db(c[10636]|0,aJ<<3,215008)|0;c[10636]=ay;if((ay|0)==0){F=6711;break}ay=c[8718]|0;if((ay|0)<1){F=6423;break}aJ=db(c[10634]|0,ay<<3,215008)|0;c[10634]=aJ;if((aJ|0)==0){F=6714;break}aJ=c[8718]|0;if((aJ|0)<1){F=6426;break}ay=db(c[10878]|0,aJ<<3,215008)|0;c[10878]=ay;if((ay|0)==0){F=6716;break}ay=c[m>>2]|0;aJ=c[8718]|0;cf(ay|0,127672,(v=i,i=i+8|0,c[v>>2]=aJ,v)|0)}if((aD|0)==1){h[aM>>3]=+h[r>>3];h[r>>3]=+(c[11942]|0);F=6434}else if((aD|0)==(-5|0)|(aD|0)==(-2|0)|(aD|0)==(-3|0)|(aD|0)==(-4|0)){aP=aH}else if((aD|0)==0){F=6432;break}else{F=6434}L8729:do{if((F|0)==6434){F=0;aJ=aH+1|0;ay=c[8304]|0;L8731:do{if((ay|0)>0){aE=aa(ay,c[8306]|0);aC=c[10636]|0;av=0;while(1){aR=c[42584+(av<<2)>>2]|0;l=c[64648+(aR*688&-1)>>2]|0;if((l&1|0)==0){if(+h[s+(av<<3)>>3]<+h[64664+(aR*688&-1)>>3]){F=6438;break}}aw=+h[s+(av<<3)>>3];if((l&2|0)==0){if(aw>+h[64672+(aR*688&-1)>>3]){F=6441;break}}h[aC+(aE+av<<3)>>3]=aw;l=av+1|0;if((l|0)<(ay|0)){av=l}else{aS=l;break L8731}}if((F|0)==6441){F=0;av=q+(aR<<2)|0;c[av>>2]=(c[av>>2]|0)+1;aP=aJ;break L8729}else if((F|0)==6438){F=0;av=q+(aR<<2)|0;c[av>>2]=(c[av>>2]|0)+1;aP=aJ;break L8729}}else{aS=0}}while(0);ay=c[16162]|0;do{if((ay&1|0)==0){if(+h[s+(aS<<3)>>3]>=+h[8083]){break}c[aN>>2]=(c[aN>>2]|0)+1;aP=aJ;break L8729}}while(0);aw=+h[s+(aS<<3)>>3];if((ay&2|0)==0&aw>+h[8084]){c[aN>>2]=(c[aN>>2]|0)+1;aP=aJ;break}av=c[8306]|0;h[(c[10634]|0)+(av<<3)>>3]=aw;if((c[13486]|0)>2){aU=+h[s+(aS+1<<3)>>3]}else{aU=1.0}c[8306]=av+1;h[(c[10878]|0)+(av<<3)>>3]=aU;aP=aJ}}while(0);aj=eC(r,7)|0;if((aj|0)==-1){aO=aP;break L8714}else{aH=aP;aD=aj}}if((F|0)==6423){c[10634]=0;ez();aW=c[8718]|0;aX=be(42289,128848,(v=i,i=i+8|0,c[v>>2]=aW,v)|0)|0;fb()}else if((F|0)==6420){c[10636]=0;ez();aW=c[8718]|0;aX=be(42289,128848,(v=i,i=i+8|0,c[v>>2]=aW,v)|0)|0;fb()}else if((F|0)==6426){c[10878]=0;ez();aW=c[8718]|0;aX=be(42289,128848,(v=i,i=i+8|0,c[v>>2]=aW,v)|0)|0;fb()}else if((F|0)==6711){ez();aW=c[8718]|0;aX=be(42289,128848,(v=i,i=i+8|0,c[v>>2]=aW,v)|0)|0;fb()}else if((F|0)==6714){ez();aW=c[8718]|0;aX=be(42289,128848,(v=i,i=i+8|0,c[v>>2]=aW,v)|0)|0;fb()}else if((F|0)==6716){ez();aW=c[8718]|0;aX=be(42289,128848,(v=i,i=i+8|0,c[v>>2]=aW,v)|0)|0;fb()}else if((F|0)==6432){be(42289,126384,(v=i,i=i+8|0,c[v>>2]=c[11900],v)|0);fb()}}}while(0);ez();aW=c[8270]|0;do{if((aW|0)!=0){if((aY(aW|0,139696)|0)==0){break}bA(4,139696)}}while(0);aW=c[8306]|0;if((aW|0)<2){bD(125344,(v=i,i=i+8|0,c[v>>2]=aO,v)|0);aO=0;do{aX=c[42584+(aO<<2)>>2]|0;aP=c[q+(aX<<2)>>2]|0;do{if((aP|0)!=0){if((aO|0)<5){aZ=55600+(aO*51&-1)|0}else{aZ=77416}bD(124088,(v=i,i=i+16|0,c[v>>2]=aP,c[v+8>>2]=aZ,v)|0);r=64648+(aX*688&-1)|0;if((c[r>>2]&1|0)==0){aU=+h[64664+(aX*688&-1)>>3];bD(121384,(v=i,i=i+8|0,h[v>>3]=aU,v)|0)}else{bD(122672,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[r>>2]&2|0)==0){aU=+h[64672+(aX*688&-1)>>3];bD(116464,(v=i,i=i+8|0,h[v>>3]=aU,v)|0);break}else{bJ(14192);break}}}while(0);aO=aO+1|0;}while((aO|0)<6);uD(42289,115544,16);fb()}aO=aa(c[8304]|0,aW);do{if((aO|0)<1){c[10636]=0;a_=aW;F=6471}else{aZ=db(c[10636]|0,aO<<3,215008)|0;q=c[8306]|0;c[10636]=aZ;if((q|0)>=1){a_=q;F=6471;break}c[10634]=0;a$=0;a0=q}}while(0);do{if((F|0)==6471){aO=db(c[10634]|0,a_<<3,215008)|0;aW=c[8306]|0;c[10634]=aO;if((aW|0)<1){a$=0;a0=aW;break}aO=db(c[10878]|0,aW<<3,215008)|0;a$=aO;a0=c[8306]|0}}while(0);c[10878]=a$;cf(c[8762]|0,114744,(v=i,i=i+8|0,c[v>>2]=a0,v)|0);if((c[13486]|0)<3){a0=c[8762]|0;aI(113544,54,1,a0|0)}a0=H-1|0;H=c[1054]|0;a$=(c[H+(a0*40&-1)+36>>2]|0)+(c[H+(a0*40&-1)+32>>2]|0)|0;a0=db(0,(a$+1|0)-(c[H+(ag*40&-1)+32>>2]|0)|0,116456)|0;H=c[(c[1054]|0)+(ag*40&-1)+32>>2]|0;L8802:do{if((H|0)<(a$|0)){ag=H;a_=a0;while(1){aO=a[(c[10036]|0)+ag|0]|0;if(aO<<24>>24==0){a2=a_;break L8802}aW=a_+1|0;a[a_]=aO;aO=ag+1|0;if((aO|0)<(a$|0)){ag=aO;a_=aW}else{a2=aW;break}}}else{a2=a0}}while(0);a[a2]=0;cf(c[8762]|0,108456,(v=i,i=i+8|0,c[v>>2]=a0,v)|0);uu(a0);c[8714]=32;a0=c[13898]|0;a2=a0+1|0;c[13898]=a2;if((c[8272]|0)<=(a0|0)){uf(a2,106560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}a$=c[1054]|0;if((a[a$+(a0*40&-1)|0]&1)==0){uf(a2,106560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}H=c[a$+(a0*40&-1)+36>>2]|0;a_=a$+(a0*40&-1)+32|0;a0=c[10036]|0;a$=0;while(1){if((a$|0)>=(H|0)){break}if((a[a0+((c[a_>>2]|0)+a$|0)|0]|0)==(a[a$+107456|0]|0)){a$=a$+1|0}else{F=6705;break}}if((F|0)==6705){uf(a2,106560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a$|0)!=3){uf(a2,106560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}a2=ut(256)|0;do{if((a2|0)==0){gk();a$=ut(256)|0;if((a$|0)!=0){a3=a$;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{a3=a2}}while(0);a2=((c[8714]|0)*51&-1)+51|0;c[18130]=a3;a3=ut(a2)|0;do{if((a3|0)==0){gk();a$=ut(a2)|0;if((a$|0)!=0){a4=a$;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=105592,v)|0)}else{a4=a3}}while(0);c[8236]=a4;c[8302]=0;a4=c[13898]|0;a3=c[1054]|0;a2=(a[a3+(a4*40&-1)|0]&1)==0;if(a2){F=6493}else{a$=a[(c[10036]|0)+(c[a3+(a4*40&-1)+32>>2]|0)|0]|0;if((a$<<24>>24|0)==39|(a$<<24>>24|0)==34){F=6503}else{F=6493}}L8832:do{if((F|0)==6493){a$=c[10810]|0;L8834:do{if((a$|0)!=0){a_=c[8272]|0;a0=a3+(a4*40&-1)+36|0;H=a3+(a4*40&-1)+32|0;ag=c[10036]|0;aW=a$;L8836:while(1){aO=c[aW+4>>2]|0;L8838:do{if(!((a_|0)<=(a4|0)|a2)){q=c[a0>>2]|0;aZ=0;while(1){if((aZ|0)>=(q|0)){break}if((a[ag+((c[H>>2]|0)+aZ|0)|0]|0)==(a[aO+aZ|0]|0)){aZ=aZ+1|0}else{break L8838}}if((a[aO+aZ|0]|0)==0){break L8836}}}while(0);aO=c[aW>>2]|0;if((aO|0)==0){break L8834}else{aW=aO}}if((a[aW+8|0]&1)!=0){break}if((c[aW+16>>2]|0)==3){F=6503;break L8832}}}while(0);a$=c[8762]|0;aI(99808,60,1,a$|0);a$=c[13898]|0;H=c[1054]|0;L8848:while(1){if((a[H+(a$*40&-1)|0]&1)==0){F=6725;break}ag=(bO(d[(c[10036]|0)+(c[H+(a$*40&-1)+32>>2]|0)|0]|0|0)|0)==0;a0=c[1054]|0;if(ag){if((a[(c[10036]|0)+(c[a0+(a$*40&-1)+32>>2]|0)|0]|0)!=95){F=6726;break}}ag=(c[8236]|0)+((c[8302]|0)*51&-1)|0;a_=c[13898]|0;aO=c[a0+(a_*40&-1)+32>>2]|0;aJ=c[a0+(a_*40&-1)+36>>2]|0;a_=((aJ|0)>50?50:aJ)+aO|0;L8854:do{if((aO|0)<(a_|0)){aJ=ag;a0=aO;while(1){ay=a[(c[10036]|0)+a0|0]|0;if(ay<<24>>24==0){a5=aJ;break L8854}q=aJ+1|0;a[aJ]=ay;ay=a0+1|0;if((ay|0)<(a_|0)){aJ=q;a0=ay}else{a5=q;break}}}else{a5=ag}}while(0);a[a5]=0;ag=c[8302]|0;a_=c[8714]|0;if((ag|0)<(a_|0)){a7=ag;a9=c[8236]|0}else{ag=a_*3&-1;a_=(ag|0)/2&-1;c[8714]=a_;if((ag|0)<2){F=6577;break}ag=db(c[18130]|0,a_<<3,215008)|0;c[18130]=ag;if((ag|0)==0){F=6728;break}ag=db(c[8236]|0,((c[8714]|0)*51&-1)+51|0,101064)|0;a_=ag;c[8236]=a_;if((ag|0)==0){F=6729;break}a7=c[8302]|0;a9=a_}a_=a9+(a7*51&-1)|0;ag=43240;while(1){ba=c[ag>>2]|0;if((ba|0)==0){F=6592;break}if((aY(a_|0,c[ba+4>>2]|0)|0)==0){F=6585;break}else{ag=ba|0}}do{if((F|0)==6585){F=0;ag=ba+8|0;if((a[ag]&1)!=0){a[ag]=0;c[ba+16>>2]=2;h[ba+24>>3]=1.0;h[ba+32>>3]=0.0;bb=1.0;break}ag=ba+16|0;aO=c[ag>>2]|0;if((aO|0)==2){bb=+h[ba+24>>3];break}else if((aO|0)==1){aU=+(c[ba+24>>2]|0);c[ag>>2]=2;h[ba+24>>3]=aU;h[ba+32>>3]=0.0;bb=aU;break}else if((aO|0)==3){bb=+uz(c[ba+24>>2]|0,0);break}else{F=6591;break L8848}}else if((F|0)==6592){F=0;aO=e6(a_)|0;c[aO+16>>2]=2;h[aO+24>>3]=1.0;h[aO+32>>3]=0.0;a[aO+8|0]=0;bb=1.0}}while(0);a_=c[8302]|0;h[(c[18130]|0)+(a_<<3)>>3]=bb;aO=a_+1|0;c[8302]=aO;a_=c[13898]|0;ag=a_+1|0;c[13898]=ag;if((c[8272]|0)<=(ag|0)){bc=aO;break L8832}aW=c[1054]|0;if((a[aW+(ag*40&-1)|0]&1)==0){bc=aO;break L8832}a0=c[aW+(ag*40&-1)+36>>2]|0;aJ=aW+(ag*40&-1)+32|0;ag=c[10036]|0;q=0;while(1){if((q|0)>=(a0|0)){break}if((a[ag+((c[aJ>>2]|0)+q|0)|0]|0)==(a[q+148464|0]|0)){q=q+1|0}else{bc=aO;break L8832}}if((q|0)!=1){bc=aO;break L8832}aJ=a_+2|0;c[13898]=aJ;if((aJ|0)==0){bc=aO;break L8832}else{a$=aJ;H=aW}}if((F|0)==6725){uD(42289,99416,23);fb()}else if((F|0)==6726){uD(42289,99416,23);fb()}else if((F|0)==6728){uD(42289,100336,43);fb()}else if((F|0)==6729){uD(42289,100336,43);fb()}else if((F|0)==6591){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((F|0)==6577){c[18130]=0;uD(42289,100336,43);fb()}}}while(0);if((F|0)==6503){uu(c[10644]|0);ba=c[13898]|0;L8897:do{if((ba|0)<(c[8272]|0)){a7=c[1054]|0;L8899:do{if((a[a7+(ba*40&-1)|0]&1)!=0){a9=c[a7+(ba*40&-1)+36>>2]|0;a5=a7+(ba*40&-1)+32|0;a2=c[10036]|0;a4=0;while(1){if((a4|0)>=(a9|0)){break}if((a[a2+((c[a5>>2]|0)+a4|0)|0]|0)==(a[a4+103664|0]|0)){a4=a4+1|0}else{break L8899}}if((a4|0)==1){bd=0;break L8897}}}while(0);a[14176]=1;is(g);a[14176]=0;if((c[g>>2]|0)==3){bd=c[g+8>>2]|0;break}else{c[13898]=ba;bd=0;break}}else{bd=0}}while(0);c[10644]=bd;ba=c[8762]|0;cf(ba|0,104576,(v=i,i=i+8|0,c[v>>2]=bd,v)|0);bd=hA(c[10644]|0,193632)|0;if((bd|0)==0){ba=c[10644]|0;be(42289,104048,(v=i,i=i+8|0,c[v>>2]=ba,v)|0);fb()}ba=x|0;L8913:do{if((a6(ba|0,1025,bd|0)|0)!=0){x=0;L8914:while(1){L8916:while(1){g=aQ(ba|0,221168)|0;if((g|0)==0){bf=0}else{a[g]=0;g=c[8762]|0;cf(g|0,103672,(v=i,i=i+8|0,c[v>>2]=ba,v)|0);bf=1}g=a8(ba|0,35)|0;if((g|0)==0){bg=ba}else{a[g]=0;bg=ba}while(1){g=a[bg]|0;if((g<<24>>24|0)==35|(g<<24>>24|0)==0){break}else if(!((g<<24>>24|0)==32|(g<<24>>24|0)==9|(g<<24>>24|0)==10)){bh=ba;break L8916}bg=bg+1|0}if((a6(ba|0,1025,bd|0)|0)==0){break L8913}}while(1){a4=a[bh]|0;if((a4<<24>>24|0)==10|(a4<<24>>24|0)==0){bi=0;bj=ba;bk=x;break}else if(!((a4<<24>>24|0)==32|(a4<<24>>24|0)==9|(a4<<24>>24|0)==61)){F=6525;break}bh=bh+1|0}if((F|0)==6525){F=0;a4=cy(bh|0,213208)|0;if((a4|0)==0){bl=bh+(uA(bh|0)|0)|0}else{bl=a4}a4=a[bl]|0;a[bl]=0;bi=bh;bj=bl+1|0;bk=a4}a4=a[bi]|0;if(a4<<24>>24!=0){g=bi;a7=a4;do{if((bs(a7&255|0)|0)==0){if((a[g]|0)!=95){F=6720;break L8914}}g=g+1|0;a7=a[g]|0;}while(a7<<24>>24!=0)}if((uA(bi|0)|0)>>>0>50){F=6719;break}a7=c[8302]|0;g=c[8236]|0;uF(g+(a7*51&-1)|0,bi|0,51);if((uA(bi|0)|0)>>>0>=51){a[g+(a7*51&-1)+50|0]=0}if(bk<<24>>24==61){bm=bj}else{a7=a8(bj|0,61)|0;if((a7|0)==0){F=6538;break}bm=a7+1|0}a7=bm;while(1){g=a[a7]|0;if((g<<24>>24|0)==10|(g<<24>>24|0)==0){bn=0;bo=bm;bp=bk;break}else if(!((g<<24>>24|0)==32|(g<<24>>24|0)==9|(g<<24>>24|0)==61)){F=6543;break}a7=a7+1|0}if((F|0)==6543){F=0;g=cy(a7|0,213208)|0;if((g|0)==0){bq=a7+(uA(a7|0)|0)|0}else{bq=g}g=a[bq]|0;a[bq]=0;bn=a7;bo=bq+1|0;bp=g}if((ca(bn|0,148448,(v=i,i=i+8|0,c[v>>2]=w,v)|0)|0)!=1){F=6547;break}if(bf){bb=+h[w>>3];g=e6((c[8236]|0)+((c[8302]|0)*51&-1)|0)|0;c[g+16>>2]=2;c[g+20>>2]=br;h[g+24>>3]=bb;h[g+32>>3]=0.0;a[g+8|0]=0;bt=bo}else{g=c[8302]|0;a4=c[8714]|0;if((g|0)<(a4|0)){bu=g}else{g=a4*3&-1;a4=(g|0)/2&-1;c[8714]=a4;if((g|0)<2){F=6552;break}g=db(c[18130]|0,a4<<3,215008)|0;c[18130]=g;if((g|0)==0){F=6709;break}g=db(c[8236]|0,((c[8714]|0)*51&-1)+51|0,101064)|0;c[8236]=g;if((g|0)==0){F=6710;break}bu=c[8302]|0}bb=+h[w>>3];c[8302]=bu+1;h[(c[18130]|0)+(bu<<3)>>3]=bb;bt=bo}while(1){g=a[bt]|0;if((g<<24>>24|0)==10|(g<<24>>24|0)==0){bv=bp;break}else if(!((g<<24>>24|0)==32|(g<<24>>24|0)==9|(g<<24>>24|0)==61)){F=6560;break}bt=bt+1|0}if((F|0)==6560){F=0;a7=cy(bt|0,213208)|0;if((a7|0)==0){bw=bt+(uA(bt|0)|0)|0}else{bw=a7}a7=a[bw]|0;a[bw]=0;if((bt|0)==0){bv=a7}else{F=6564;break}}if((a6(ba|0,1025,bd|0)|0)==0){break L8913}else{x=bv}}if((F|0)==6709){bx=az(bd|0)|0;uD(42289,100336,43);fb()}else if((F|0)==6710){bx=az(bd|0)|0;uD(42289,100336,43);fb()}else if((F|0)==6719){by=az(bd|0)|0;uD(42289,101704,31);fb()}else if((F|0)==6720){by=az(bd|0)|0;uD(42289,101704,31);fb()}else if((F|0)==6552){c[18130]=0;bx=az(bd|0)|0;uD(42289,100336,43);fb()}else if((F|0)==6547){az(bd|0);uD(42289,101704,31);fb()}else if((F|0)==6564){az(bd|0);uD(42289,101704,31);fb()}else if((F|0)==6538){az(bd|0);uD(42289,101704,31);fb()}}}while(0);az(bd|0);bc=c[8302]|0}if((bc|0)<1){bz=0;bB=bc}else{bd=db(c[18130]|0,bc<<3,215008)|0;bz=bd;bB=c[8302]|0}c[18130]=bz;c[8236]=db(c[8236]|0,(bB*51&-1)+51|0,105592)|0;bB=c[8306]|0;bz=c[8302]|0;if((bB|0)<(bz|0)){uD(42289,98280,56);fb()}if((bz|0)>0){bd=c[18130]|0;bc=0;do{bx=bd+(bc<<3)|0;if(+h[bx>>3]==0.0){h[bx>>3]=1.0e-30}bc=bc+1|0;}while((bc|0)<(bz|0))}if((bz|0)==0){uh(-1,97624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{bc=c[18130]|0;h[e>>3]=1.0e+30;bd=hp(bz+bB|0,bz)|0;h[f>>3]=-1.0;aV(2,52);if((fh(bc,bd,e,f)|0)==1){uD(42289,96504,31);fb()}bb=+h[e>>3];aU=+h[f>>3];do{if((a[42560]&1)==0){bz=c[m>>2]|0;aw=bb-bb;if(bb>1.0e-30){bC=aw/bb}else{bC=0.0}au=+h[5479];cf(bz|0,224168,(v=i,i=i+56|0,c[v>>2]=0,h[v+8>>3]=bb,h[v+16>>3]=bC,h[v+24>>3]=aw,h[v+32>>3]=au,h[v+40>>3]=aU,c[v+48>>2]=222856,v)|0);if((c[8302]|0)>0){bE=0}else{bG=aw;break}while(1){au=+h[bc+(bE<<3)>>3];cf(bz|0,222224,(v=i,i=i+16|0,c[v>>2]=(c[8236]|0)+(bE*51&-1),h[v+8>>3]=au,v)|0);bB=bE+1|0;if((bB|0)<(c[8302]|0)){bE=bB}else{bG=aw;break}}}else{bG=bb-bb}}while(0);bE=c[8762]|0;if(bb>1.0e-30){bH=bG/bb}else{bH=0.0}bC=+h[5479];cf(bE|0,224168,(v=i,i=i+56|0,c[v>>2]=0,h[v+8>>3]=bb,h[v+16>>3]=bH,h[v+24>>3]=bG,h[v+32>>3]=bC,h[v+40>>3]=aU,c[v+48>>2]=222856,v)|0);if((c[8302]|0)>0){bz=0;do{bC=+h[bc+(bz<<3)>>3];cf(bE|0,222224,(v=i,i=i+16|0,c[v>>2]=(c[8236]|0)+(bz*51&-1),h[v+8>>3]=bC,v)|0);bz=bz+1|0;}while((bz|0)<(c[8302]|0))}bz=e6(96112)|0;a[bz+8|0]=0;c[bz+16>>2]=1;c[bz+24>>2]=0;a[1016]=0;bC=1.0e+30;bz=0;bE=2;bG=aU;L9027:while(1){if(a[51680]|0){aU=+h[e>>3];bB=c[m>>2]|0;bH=aU-bC;if(aU>1.0e-30){bI=bH/aU}else{bI=0.0}bb=+h[5479];bx=(bz|0)>0?223472:222856;cf(bB|0,224168,(v=i,i=i+56|0,c[v>>2]=bz,h[v+8>>3]=aU,h[v+16>>3]=bI,h[v+24>>3]=bH,h[v+32>>3]=bb,h[v+40>>3]=bG,c[v+48>>2]=bx,v)|0);if((c[8302]|0)>0){bx=0;do{bb=+h[bc+(bx<<3)>>3];cf(bB|0,222224,(v=i,i=i+16|0,c[v>>2]=(c[8236]|0)+(bx*51&-1),h[v+8>>3]=bb,v)|0);bx=bx+1|0;}while((bx|0)<(c[8302]|0))}a[51680]=0;while(1){aI(74040,49,1,c[m>>2]|0);bx=a1(c[o>>2]|0)|0;if((bx|0)==99|(bx|0)==67){break}else if((bx|0)==(-1|0)|(bx|0)==115|(bx|0)==83){F=6637;break L9027}else if(!((bx|0)==101|(bx|0)==69)){continue}bx=c[10638]|0;bB=(bx|0)!=0?bx:136352;cf(c[m>>2]|0,72672,(v=i,i=i+8|0,c[v>>2]=bB,v)|0);if((c[8302]|0)>0){bx=0;do{bb=+h[(c[18130]|0)+(bx<<3)>>3];by=e6((c[8236]|0)+(bx*51&-1)|0)|0;c[by+16>>2]=2;h[by+24>>3]=bb;h[by+32>>3]=0.0;a[by+8|0]=0;bx=bx+1|0;}while((bx|0)<(c[8302]|0))}if((bB|0)==0){bK=0}else{bK=bP(bB|0)|0}dL(bK)}aI(73232,9,1,c[m>>2]|0)}if((bE|0)==2){bL=+h[e>>3];bM=bz+1|0}else{bL=bC;bM=bz}bx=fh(bc,bd,e,f)|0;if((bx|0)==1){bN=bL;bQ=bM;bR=1;break}else if((bx|0)==2){do{if((a[42560]&1)==0){bb=+h[e>>3];bH=+h[f>>3];by=c[m>>2]|0;aU=bb-bL;if(bb>1.0e-30){bS=aU/bb}else{bS=0.0}aw=+h[5479];bv=(bM|0)>0?223472:222856;cf(by|0,224168,(v=i,i=i+56|0,c[v>>2]=bM,h[v+8>>3]=bb,h[v+16>>3]=bS,h[v+24>>3]=aU,h[v+32>>3]=aw,h[v+40>>3]=bH,c[v+48>>2]=bv,v)|0);if((c[8302]|0)>0){bT=0}else{bV=bH;break}while(1){aw=+h[bc+(bT<<3)>>3];cf(by|0,222224,(v=i,i=i+16|0,c[v>>2]=(c[8236]|0)+(bT*51&-1),h[v+8>>3]=aw,v)|0);bv=bT+1|0;if((bv|0)<(c[8302]|0)){bT=bv}else{bV=bH;break}}}else{bV=+h[f>>3]}}while(0);if(bV<1.0e20){bW=bV}else{bN=bL;bQ=bM;bR=2;break}}else{bH=+h[f>>3];if(bH<1.0e20){bW=bH}else{bN=bL;bQ=bM;bR=bx;break}}by=c[8708]|0;if((by|0)!=0&(bM|0)>(by|0)){bN=bL;bQ=bM;bR=bx;break}if((bx|0)==3){bC=bL;bz=bM;bE=3;bG=bW;continue}bH=+h[e>>3];aw=bL-bH;if(bH>1.0e-30){bX=aw/bH}else{bX=aw}if(bX>+h[5479]){bC=bL;bz=bM;bE=bx;bG=bW}else{bN=bL;bQ=bM;bR=bx;break}}if((F|0)==6637){bM=c[m>>2]|0;aI(73744,5,1,bM|0);a[1016]=1;bN=bC;bQ=bz;bR=bE}aV(2,4);aV(13,1);bE=c[8708]|0;do{if((bE|0)>0&(bQ|0)>(bE|0)){fe(95552,(v=i,i=i+8|0,c[v>>2]=bE,v)|0)}else{if(a[1016]|0){fe(95160,(v=i,i=i+8|0,c[v>>2]=bQ,v)|0);break}else{fe(94448,(v=i,i=i+8|0,c[v>>2]=bQ,v)|0);bz=e6(96112)|0;a[bz+8|0]=0;c[bz+16>>2]=1;c[bz+24>>2]=1;break}}}while(0);bC=+h[e>>3];fe(93944,(v=i,i=i+8|0,h[v>>3]=bC,v)|0);bL=bC-bN;if(bC>1.0e-30){fe(93376,(v=i,i=i+8|0,h[v>>3]=bL/bC,v)|0)}else{fe(92448,(v=i,i=i+8|0,h[v>>3]=bL,v)|0)}if((bR|0)==1){uD(42289,96504,31);fb()}bR=c[8302]|0;if((a[42568]&1)!=0&(bR|0)>0){bQ=0;while(1){fi((c[8236]|0)+(bQ*51&-1)|0,0.0);bE=bQ+1|0;bz=c[8302]|0;if((bE|0)<(bz|0)){bQ=bE}else{bY=bz;break}}}else{bY=bR}bR=c[8306]|0;L9092:do{if((bR|0)==(bY|0)){fe(91680,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);fe(90880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);fe(90496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);fe(89976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);bQ=c[8302]|0;if((bQ|0)>0){bZ=0}else{b_=bQ;break}while(1){bL=+h[bc+(bZ<<3)>>3];fe(89304,(v=i,i=i+16|0,c[v>>2]=(c[8236]|0)+(bZ*51&-1),h[v+8>>3]=bL,v)|0);bQ=bZ+1|0;bz=c[8302]|0;if((bQ|0)<(bz|0)){bZ=bQ}else{b_=bz;break}}}else{if(bC<1.0e-30){fe(88552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);fe(90496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);fe(89976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);bx=c[8302]|0;if((bx|0)>0){b$=0}else{b_=bx;break}while(1){bL=+h[bc+(b$<<3)>>3];fe(89304,(v=i,i=i+16|0,c[v>>2]=(c[8236]|0)+(b$*51&-1),h[v+8>>3]=bL,v)|0);bx=b$+1|0;bz=c[8302]|0;if((bx|0)<(bz|0)){b$=bx}else{b_=bz;break L9092}}}bz=bR-bY|0;bL=bC/+(bz|0);bN=+Q(+bL);fe(87584,(v=i,i=i+8|0,c[v>>2]=bz,v)|0);fe(86752,(v=i,i=i+8|0,h[v>>3]=bN,v)|0);fe(85792,(v=i,i=i+8|0,h[v>>3]=bL,v)|0);bx=e6(84600)|0;a[bx+8|0]=0;c[bx+16>>2]=1;c[bx+24>>2]=bz;bz=e6(84088)|0;a[bz+8|0]=0;c[bz+16>>2]=2;h[bz+24>>3]=bN;h[bz+32>>3]=0.0;bz=e6(83096)|0;a[bz+8|0]=0;c[bz+16>>2]=2;h[bz+24>>3]=bC;h[bz+32>>3]=0.0;hr(bd,0,0,0,c[8306]|0,c[8302]|0,0);bz=c[8306]|0;hs(bd,bd+(bz<<2)|0,c[8302]|0);bx=c[8302]|0;do{if((bx|0)<1){b0=bx;b1=0;b2=0}else{bQ=bx<<3;bE=ut(bQ)|0;do{if((bE|0)==0){gk();bM=ut(bQ)|0;if((bM|0)!=0){b3=bM;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{b3=bE}}while(0);bE=b3;bQ=c[8302]|0;if((bQ|0)>0){b4=0}else{b0=bQ;b1=0;b2=bE;break}while(1){bN=+h[(c[bd+(b4+bz<<2)>>2]|0)+(b4<<3)>>3];if(bN<=0.0){F=6678;break}h[bE+(b4<<3)>>3]=+Q(+bN);bM=b4+1|0;if((bM|0)<(bQ|0)){b4=bM}else{b5=0;break}}if((F|0)==6678){uD(42289,81952,66);fb()}while(1){bM=bE+(b5<<3)|0;bT=bd+(b5+bz<<2)|0;bK=0;do{by=(c[bT>>2]|0)+(bK<<3)|0;h[by>>3]=+h[by>>3]/(+h[bM>>3]*+h[bE+(bK<<3)>>3]);bK=bK+1|0;}while((bK|0)<=(b5|0));bK=b5+1|0;if((bK|0)<(bQ|0)){b5=bK}else{b0=bQ;b1=1;b2=bE;break}}}}while(0);bN=+Q(+(bC/+((c[8306]|0)-b0|0)));h[e>>3]=bN;if(b1){bx=0;do{bE=b2+(bx<<3)|0;h[bE>>3]=bN*+h[bE>>3];bx=bx+1|0;}while((bx|0)<(b0|0))}fe(80552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);fe(79488,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);if((c[8302]|0)>0){bx=0;do{bN=+h[bc+(bx<<3)>>3];bE=+P(+bN)<1.0e-30;bQ=b2+(bx<<3)|0;bL=+h[bQ>>3];if(bE){b6=0.0}else{b6=+P(+(bL*100.0/bN))}fe(78752,(v=i,i=i+40|0,c[v>>2]=(c[8236]|0)+(bx*51&-1),h[v+8>>3]=bN,c[v+16>>2]=78264,h[v+24>>3]=bL,h[v+32>>3]=b6,v)|0);if((a[42568]&1)!=0){fi((c[8236]|0)+(bx*51&-1)|0,+h[bQ>>3])}bx=bx+1|0;}while((bx|0)<(c[8302]|0))}fe(77728,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);fe(77224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);if((c[8302]|0)>0){bx=0;do{fe(76616,(v=i,i=i+8|0,c[v>>2]=(c[8236]|0)+(bx*51&-1),v)|0);bx=bx+1|0;}while((bx|0)<(c[8302]|0))}fe(140136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);if((c[8302]|0)>0){bx=0;do{fe(75944,(v=i,i=i+8|0,c[v>>2]=(c[8236]|0)+(bx*51&-1),v)|0);bQ=bd+(bx+bz<<2)|0;bE=0;do{fe(75392,(v=i,i=i+8|0,h[v>>3]=+h[(c[bQ>>2]|0)+(bE<<3)>>3],v)|0);bE=bE+1|0;}while((bE|0)<=(bx|0));fe(140136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);bx=bx+1|0;}while((bx|0)<(c[8302]|0))}uu(b2);b_=c[8302]|0}}while(0);b2=b_-1|0;b6=+h[bc+(b2<<3)>>3];b_=e6((c[8236]|0)+(b2*51&-1)|0)|0;c[b_+16>>2]=2;h[b_+24>>3]=b6;h[b_+32>>3]=0.0;a[b_+8|0]=0;h[f>>3]=-2.0;fh(bc,bd,e,f);uu(c[bd>>2]|0);uu(bd)}az(c[8762]|0);c[8762]=0;uu(c[10636]|0);uu(c[10634]|0);uu(c[10878]|0);uu(c[18130]|0);uu(c[8236]|0);bd=c[10062]|0;if((bd|0)!=0){e1(bd);c[10062]=0}bd=c[10036]|0;uF(35352,bd|0,512);if((uA(bd|0)|0)>>>0<512){i=b;return}a[35863]=0;i=b;return}function fe(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;i=i+16|0;f=e|0;g=f|0;h=f;c[h>>2]=d;c[h+4>>2]=0;if((a[42560]&1)==0){f=c[m>>2]|0;bX(f|0,b|0,g|0)}c[h>>2]=d;c[h+4>>2]=0;bX(c[8762]|0,b|0,g|0);i=e;return}function ff(b){b=b|0;aV(2,52);a[51680]=1;return}function fg(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0;b=i;d=c[10568]|0;if((d|0)!=0){e=bP(d|0)|0;i=b;return e|0}d=bU(236952)|0;do{if((d|0)!=0){if((a[d]|0)==0){break}f=a[d+((uA(d|0)|0)-1|0)|0]|0;if(!((f<<24>>24|0)==47|(f<<24>>24|0)==92)){e=bP(d|0)|0;i=b;return e|0}f=(uA(d|0)|0)+8|0;g=ut(f)|0;do{if((g|0)==0){gk();h=ut(f)|0;if((h|0)!=0){j=h;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=97072,v)|0);return 0}else{j=g}}while(0);uB(j|0,d|0);g=j+(uA(j|0)|0)|0;f=g|0;w=779381094;a[f]=w&255;w=w>>8;a[f+1|0]=w&255;w=w>>8;a[f+2|0]=w&255;w=w>>8;a[f+3|0]=w&255;f=g+4|0;w=6778732;a[f]=w&255;w=w>>8;a[f+1|0]=w&255;w=w>>8;a[f+2|0]=w&255;w=w>>8;a[f+3|0]=w&255;e=j;i=b;return e|0}}while(0);e=bP(42264)|0;i=b;return e|0}function fh(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0.0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0.0,A=0.0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,R=0,S=0,T=0.0,U=0,V=0.0,W=0.0,X=0.0,Y=0;g=i;i=i+8|0;j=g|0;k=+h[f>>3];if(k!=-1.0){if(k==-2.0){uu(c[8730]|0);uu(c[8720]|0);uu(c[8728]|0);uu(c[8724]|0);uu(c[8726]|0);l=c[8722]|0;uu(c[l>>2]|0);uu(l);n=0;i=g;return n|0}l=c[8306]|0;o=c[8302]|0;if((o+l|0)>0){p=0;q=o;while(1){uD(c[(c[8722]|0)+(p<<2)>>2]|0,c[d+(p<<2)>>2]|0,q<<3);o=p+1|0;r=c[8306]|0;s=c[8302]|0;if((o|0)<(s+r|0)){p=o;q=s}else{t=r;break}}}else{t=l}uD(c[8720]|0,c[8730]|0,t<<3);t=c[8302]|0;if((t|0)>0){l=c[8306]|0;q=c[8722]|0;p=c[8720]|0;r=0;while(1){s=l+r|0;h[(c[q+(s<<2)>>2]|0)+(r<<3)>>3]=+h[f>>3];h[p+(s<<3)>>3]=0.0;s=r+1|0;if((s|0)<(t|0)){r=s}else{u=q;w=p;x=l;break}}}else{u=c[8722]|0;w=c[8720]|0;x=c[8306]|0}hr(u,w,c[8728]|0,c[8726]|0,x+t|0,t,1);t=c[8302]|0;if((t|0)>0){x=c[8728]|0;w=c[8724]|0;u=0;while(1){h[w+(u<<3)>>3]=+h[b+(u<<3)>>3]+ +h[x+(u<<3)>>3];l=u+1|0;if((l|0)<(t|0)){u=l}else{y=w;break}}}else{y=c[8724]|0}fj(y,c[8722]|0,c[8720]|0,j);k=+h[j>>3];if(k>=+h[e>>3]){if((a[42560]&1)==0){j=c[m>>2]|0;aF(42,j|0)}h[f>>3]=+h[4484]*+h[f>>3];n=3;i=g;return n|0}z=+h[f>>3];if(z>1.0e-20){if((a[42560]&1)==0){j=c[m>>2]|0;aF(47,j|0);A=+h[f>>3]}else{A=z}h[f>>3]=A/+h[4485]}h[e>>3]=k;if((c[8306]|0)>0){j=0;do{uD(c[d+(j<<2)>>2]|0,c[(c[8722]|0)+(j<<2)>>2]|0,c[8302]<<3);h[(c[8730]|0)+(j<<3)>>3]=+h[(c[8720]|0)+(j<<3)>>3];j=j+1|0;}while((j|0)<(c[8306]|0))}j=c[8302]|0;if((j|0)<=0){n=2;i=g;return n|0}y=c[8724]|0;w=0;while(1){h[b+(w<<3)>>3]=+h[y+(w<<3)>>3];u=w+1|0;if((u|0)<(j|0)){w=u}else{n=2;break}}i=g;return n|0}w=c[8302]|0;if((w|0)<1){B=0;C=w}else{j=w<<3;w=ut(j)|0;do{if((w|0)==0){gk();y=ut(j)|0;if((y|0)!=0){D=y;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0);return 0}else{D=w}}while(0);B=D;C=c[8302]|0}c[8724]=B;B=c[8306]|0;D=C+B|0;if((D|0)<1){E=0;F=B;G=C}else{C=D<<3;D=ut(C)|0;do{if((D|0)==0){gk();B=ut(C)|0;if((B|0)!=0){H=B;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0);return 0}else{H=D}}while(0);E=H;F=c[8306]|0;G=c[8302]|0}c[8730]=E;E=G+F|0;if((E|0)<1){I=0;J=G}else{G=E<<3;E=ut(G)|0;do{if((E|0)==0){gk();F=ut(G)|0;if((F|0)!=0){K=F;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0);return 0}else{K=E}}while(0);I=K;J=c[8302]|0}c[8720]=I;if((J|0)<1){L=0;M=J}else{I=J<<3;J=ut(I)|0;do{if((J|0)==0){gk();K=ut(I)|0;if((K|0)!=0){N=K;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0);return 0}else{N=J}}while(0);L=N;M=c[8302]|0}c[8728]=L;L=c[8306]|0;N=M+L|0;if((N|0)<1){O=0;P=L;R=M}else{M=N<<3;N=ut(M)|0;do{if((N|0)==0){gk();L=ut(M)|0;if((L|0)!=0){S=L;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0);return 0}else{S=N}}while(0);O=S;P=c[8306]|0;R=c[8302]|0}c[8726]=O;c[8722]=hp(R+P|0,R)|0;fj(b,d,c[8730]|0,e);k=+h[1782];if(k!=0.0){T=k;U=c[8302]|0}else{h[f>>3]=0.0;e=c[8306]|0;b=c[8302]|0;if((e|0)>0){R=0;k=0.0;while(1){if((b|0)>0){P=d+(R<<2)|0;O=0;A=k;while(1){z=+h[(c[P>>2]|0)+(O<<3)>>3];V=A+z*z;h[f>>3]=V;S=O+1|0;if((S|0)<(b|0)){O=S;A=V}else{W=V;break}}}else{W=k}O=R+1|0;if((O|0)<(e|0)){R=O;k=W}else{X=W;break}}}else{X=0.0}T=+Q(+(X/+(e|0)/+(b|0)));U=b}h[f>>3]=T;if((U|0)>0){Y=0}else{n=0;i=g;return n|0}while(1){if((Y|0)>0){f=c[8306]|0;b=0;do{h[(c[d+(f+Y<<2)>>2]|0)+(b<<3)>>3]=0.0;h[(c[d+(f+b<<2)>>2]|0)+(Y<<3)>>3]=0.0;b=b+1|0;}while((b|0)<(Y|0))}b=Y+1|0;if((b|0)<(U|0)){Y=b}else{n=0;break}}i=g;return n|0}function fi(b,d){b=b|0;d=+d;var e=0,f=0,g=0,j=0,k=0;e=i;f=(uA(b|0)|0)+5|0;g=ut(f)|0;do{if((g|0)==0){gk();j=ut(f)|0;if((j|0)!=0){k=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=179864,v)|0)}else{k=g}}while(0);be(k|0,74544,(v=i,i=i+8|0,c[v>>2]=b,v)|0);b=e6(k)|0;c[b+16>>2]=2;h[b+24>>3]=d;h[b+32>>3]=0.0;a[b+8|0]=0;uu(k);i=e;return}function fj(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0.0,s=0,t=0;f=i;h[e>>3]=0.0;g=c[8306]|0;if((g|0)<1){j=0}else{k=g<<3;g=ut(k)|0;do{if((g|0)==0){gk();l=ut(k)|0;if((l|0)!=0){m=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{m=g}}while(0);j=m}m=c[8302]|0;if((m|0)<1){n=0}else{g=m<<3;m=ut(g)|0;do{if((m|0)==0){gk();k=ut(g)|0;if((k|0)!=0){o=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{o=m}}while(0);n=o}o=n;fk(a,d);m=c[8302]|0;if((m|0)>0){g=0;while(1){h[n+(g<<3)>>3]=+h[a+(g<<3)>>3];k=g+1|0;if((k|0)<(m|0)){g=k}else{p=0;break}}do{g=a+(p<<3)|0;q=+h[g>>3];r=+P(+q)<1.0e-30?1.0e-30:q;m=n+(p<<3)|0;h[m>>3]=r*1.001;fk(n,j);k=c[8306]|0;if((k|0)>0){q=r*.001;l=0;do{h[(c[b+(l<<2)>>2]|0)+(p<<3)>>3]=(+h[j+(l<<3)>>3]- +h[d+(l<<3)>>3])/q;l=l+1|0;}while((l|0)<(k|0))}h[m>>3]=+h[g>>3];p=p+1|0;}while((p|0)<(c[8302]|0))}uu(j);uu(o);o=c[8306]|0;if((o|0)<=0){i=f;return}j=c[10634]|0;p=c[10878]|0;n=c[8302]|0;a=0;do{k=d+(a<<3)|0;l=p+(a<<3)|0;q=(+h[k>>3]- +h[j+(a<<3)>>3])/+h[l>>3];h[k>>3]=q;h[e>>3]=+h[e>>3]+q*q;if((n|0)>0){k=b+(a<<2)|0;s=0;do{t=(c[k>>2]|0)+(s<<3)|0;h[t>>3]=+h[t>>3]/+h[l>>3];s=s+1|0;}while((s|0)<(n|0))}a=a+1|0;}while((a|0)<(o|0));i=f;return}function fk(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0,q=0,r=0.0,s=0,t=0.0;e=i;i=i+24|0;f=e|0;if((c[8302]|0)>0){g=f|0;j=f+8|0;k=f+16|0;l=f;m=0;do{n=+h[b+(m<<3)>>3];c[g>>2]=2;h[j>>3]=n;h[k>>3]=0.0;o=e6((c[8236]|0)+(m*51&-1)|0)|0;p=o+16|0;c[p>>2]=c[l>>2];c[p+4>>2]=c[l+4>>2];c[p+8>>2]=c[l+8>>2];c[p+12>>2]=c[l+12>>2];c[p+16>>2]=c[l+16>>2];c[p+20>>2]=c[l+20>>2];a[o+8|0]=0;m=m+1|0;}while((m|0)<(c[8302]|0))}if((c[8306]|0)<=0){i=e;return}m=f|0;l=f+8|0;k=l;j=f+8|0;g=l;l=0;L9333:while(1){b=0;do{o=55600+(b*51&-1)|0;L9337:do{if((a[(e6(o)|0)+8|0]&1)==0){p=43240;while(1){q=c[p>>2]|0;if((q|0)==0){r=0.0;break L9337}if((aY(o|0,c[q+4>>2]|0)|0)==0){break}else{p=q|0}}p=c[q+16>>2]|0;if((p|0)==3){r=+uz(c[q+24>>2]|0,0);break}else if((p|0)==1){r=+(c[q+24>>2]|0);break}else if((p|0)==2){r=+h[q+24>>3];break}else{s=6863;break L9333}}else{r=0.0}}while(0);c[40264+(b*24&-1)>>2]=2;h[40272+(b*24&-1)>>3]=r;h[40280+(b*24&-1)>>3]=0.0;b=b+1|0;}while((b|0)<5);b=c[8304]|0;if((b|0)>0){o=c[10636]|0;p=0;do{n=+h[o+(aa(b,l)+p<<3)>>3];c[40264+(p*24&-1)>>2]=2;h[40272+(p*24&-1)>>3]=n;h[40280+(p*24&-1)>>3]=0.0;p=p+1|0;}while((p|0)<(b|0))}e4(c[10062]|0,f);if((a[1960]&1)!=0){s=6869;break}b=c[m>>2]|0;if((b|0)==2){t=+h[j>>3]}else if((b|0)==3){t=+uz(c[g>>2]|0,0)}else if((b|0)==1){t=+(c[k>>2]|0)}else{s=6874;break}h[d+(l<<3)>>3]=t;b=l+1|0;if((b|0)<(c[8306]|0)){l=b}else{s=6877;break}}if((s|0)==6869){uD(42289,221560,43);fb()}else if((s|0)==6874){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((s|0)==6877){i=e;return}else if((s|0)==6863){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function fl(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;f=i;i=i+32|0;g=f|0;h=f+16|0;j=c[a>>2]|0;k=c[b>>2]|0;l=c[13542]|0;if((l|0)==0){m=0;n=0;o=0}else{p=c[l>>2]|0;q=(p|0)>(j|0)&1;r=c[l+4>>2]|0;s=(r|0)<(j|0)?q|2:q;q=c[l+8>>2]|0;t=(q|0)>(k|0)?s|4:s;s=c[l+12>>2]|0;u=c[d>>2]|0;v=c[e>>2]|0;w=(p|0)>(u|0)&1;p=(r|0)<(u|0)?w|2:w;w=(q|0)>(v|0)?p|4:p;m=(s|0)<(v|0)?w|8:w;n=(s|0)<(k|0)?t|8:t;o=l}l=(n|0)!=0;t=l^1;s=(m|0)==0;if(s&t){x=1;i=f;return x|0}if((m&n|0)!=0){x=0;i=f;return x|0}n=c[d>>2]|0;m=n-j|0;w=c[e>>2]|0;v=w-k|0;do{if((w|0)==(k|0)){y=0;z=k}else{p=c[o+8>>2]|0;q=((aa(p-w|0,m)|0)/(v|0)&-1)+n|0;u=c[o>>2]|0;do{if((q|0)<(u|0)){A=0}else{if((q|0)>(c[o+4>>2]|0)){A=0;break}c[g>>2]=q;c[h>>2]=p;A=1}}while(0);p=o+12|0;q=n+((aa((c[p>>2]|0)-w|0,m)|0)/(v|0)&-1)|0;if((q|0)<(u|0)){y=A;z=w;break}if((q|0)>(c[o+4>>2]|0)){y=A;z=w;break}c[g+(A<<2)>>2]=q;c[h+(A<<2)>>2]=c[p>>2];y=A+1|0;z=w}}while(0);do{if((n|0)==(j|0)){B=y;C=j}else{w=c[o>>2]|0;A=z+((aa(w-n|0,v)|0)/(m|0)&-1)|0;p=o+8|0;q=c[p>>2]|0;do{if((A|0)<(q|0)){D=y;E=q}else{if((A|0)>(c[o+12>>2]|0)){D=y;E=q;break}c[g+(y<<2)>>2]=w;c[h+(y<<2)>>2]=A;D=y+1|0;E=c[p>>2]|0}}while(0);p=c[o+4>>2]|0;A=z+((aa(p-n|0,v)|0)/(m|0)&-1)|0;if((A|0)<(E|0)){B=D;C=n;break}if((A|0)>(c[o+12>>2]|0)){B=D;C=n;break}c[g+(D<<2)>>2]=p;c[h+(D<<2)>>2]=A;B=D+1|0;C=n}}while(0);if((B|0)<2){x=0;i=f;return x|0}B=(j|0)<(C|0);n=B?C:j;D=B?j:C;B=(k|0)<(z|0);o=B?z:k;E=B?k:z;L9395:do{if(s|t){B=c[g>>2]|0;if(l){y=aa(C-B|0,m);A=c[h>>2]|0;if((aa(z-A|0,v)+y|0)>0){c[a>>2]=B;c[b>>2]=A;break}else{c[a>>2]=c[g+4>>2];c[b>>2]=c[h+4>>2];break}}else{A=aa(B-j|0,m);y=c[h>>2]|0;if((aa(y-k|0,v)+A|0)>0){c[d>>2]=B;c[e>>2]=y;break}else{c[d>>2]=c[g+4>>2];c[e>>2]=c[h+4>>2];break}}}else{y=c[g+4>>2]|0;B=c[g>>2]|0;A=(aa(y-B|0,m)|0)<0;p=c[h+4>>2]|0;w=c[h>>2]|0;do{if(!A){if((aa(p-w|0,v)|0)<0){break}c[a>>2]=B;c[b>>2]=w;c[d>>2]=y;c[e>>2]=p;break L9395}}while(0);c[a>>2]=y;c[b>>2]=p;c[d>>2]=B;c[e>>2]=w}}while(0);v=c[a>>2]|0;if((v|0)<(D|0)|(v|0)>(n|0)){x=0;i=f;return x|0}v=c[d>>2]|0;if((v|0)<(D|0)|(v|0)>(n|0)){x=0;i=f;return x|0}n=c[b>>2]|0;if((n|0)<(E|0)|(n|0)>(o|0)){x=0;i=f;return x|0}else{x=c[e>>2]|0;i=f;return((x|0)<=(o|0)&(x|0)>=(E|0))<<31>>31|0}return 0}function fm(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;g=i;i=i+32|0;h=g|0;j=g+8|0;k=g+16|0;l=g+24|0;c[h>>2]=a;c[j>>2]=b;c[k>>2]=d;c[l>>2]=e;m=c[3524]|0;n=c[13542]|0;if((n|0)==0){o=1;p=f}else{q=c[n>>2]|0;r=(q|0)>(a|0)&1;s=c[n+4>>2]|0;t=(s|0)<(a|0)?r|2:r;r=c[n+8>>2]|0;a=(r|0)>(b|0)?t|4:t;t=c[n+12>>2]|0;n=(q|0)>(d|0)&1;q=(s|0)<(d|0)?n|2:n;n=(r|0)>(e|0)?q|4:q;o=(((t|0)<(e|0)?n|8:n)|0)==0;p=(((t|0)<(b|0)?a|8:a)|0)==0?f:f&-3}fl(h,j,k,l);cI[c[m+84>>2]&63](c[h>>2]|0,c[j>>2]|0,c[k>>2]|0,c[l>>2]|0,o?p:p&-2);i=g;return}function fn(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0;e=i;i=i+104|0;f=e|0;g=e+16|0;j=e+32|0;k=e+48|0;l=c[b>>2]|0;if((l|0)==2){m=c[b+4>>2]|0;n=43280;while(1){o=c[n>>2]|0;if((o|0)==0){p=6930;break}if((c[o+4>>2]|0)==(m|0)){p=6928;break}else{n=o|0}}do{if((p|0)==6930){n=c[8798]|0;q=(n|0)>0;r=m;L9433:while(1){s=43264;while(1){t=c[s>>2]|0;if((t|0)==0){break}if((c[t+4>>2]|0)==(r|0)){break L9433}else{s=t|0}}u=r-1|0;if(!((r|0)>(n|0)&q)){p=6940;break}r=((u|0)%(n|0)&-1)+1|0}if((p|0)==6940){c[k+4>>2]=u;c[k+40>>2]=1;c[k+44>>2]=u;c[k+8>>2]=u;break}n=k;q=t+8|0;c[n>>2]=c[q>>2];c[n+4>>2]=c[q+4>>2];c[n+8>>2]=c[q+8>>2];c[n+12>>2]=c[q+12>>2];c[n+16>>2]=c[q+16>>2];c[n+20>>2]=c[q+20>>2];c[n+24>>2]=c[q+24>>2];c[n+28>>2]=c[q+28>>2];c[n+32>>2]=c[q+32>>2];c[n+36>>2]=c[q+36>>2];c[n+40>>2]=c[q+40>>2];c[n+44>>2]=c[q+44>>2];c[n+48>>2]=c[q+48>>2];c[n+52>>2]=c[q+52>>2];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[k+4>>2]=r;a[k+32|0]=0;break}if((a[k+32|0]&1)!=0){break}c[k+40>>2]=1;c[k+44>>2]=c[k+4>>2]}else if((p|0)==6928){q=k;n=o+8|0;c[q>>2]=c[n>>2];c[q+4>>2]=c[n+4>>2];c[q+8>>2]=c[n+8>>2];c[q+12>>2]=c[n+12>>2];c[q+16>>2]=c[n+16>>2];c[q+20>>2]=c[n+20>>2];c[q+24>>2]=c[n+24>>2];c[q+28>>2]=c[n+28>>2];c[q+32>>2]=c[n+32>>2];c[q+36>>2]=c[n+36>>2];c[q+40>>2]=c[n+40>>2];c[q+44>>2]=c[n+44>>2];c[q+48>>2]=c[n+48>>2];c[q+52>>2]=c[n+52>>2];if((a[k+32|0]&1)!=0){break}c[k+40>>2]=1;c[k+44>>2]=c[k+4>>2]}}while(0);o=k+40|0;v=o;w=c[o>>2]|0}else{v=b;w=l}do{if((w|0)==0){cM[c[d+64>>2]&511](-2);i=e;return}else if((w|0)==1){l=c[d+144>>2]|0;if((l|0)==0){cM[c[d+64>>2]&511](c[v+4>>2]|0);i=e;return}else{cM[l&511](v);i=e;return}}else if((w|0)==3){l=c[d+144>>2]|0;if((l|0)==0){break}cM[l&511](v);i=e;return}}while(0);do{if(a[31104]|0){if((c[d+144>>2]|0)==0){break}if((w|0)==6){x=+h[v+8>>3];l=c[34]|0;b=(a[66852]&1)==0;do{if((a[64788+(l*688&-1)|0]&1)==0){if(b){y=x;break}if(x>0.0){z=+_(+x);y=z/+h[8358];break}else{y=+h[8341];break}}else{if(b){y=+Z(+(x*+h[64800+(l*688&-1)>>3]));break}if(+h[64792+(l*688&-1)>>3]==+h[8357]){y=x;break}y=x*+h[64800+(l*688&-1)>>3]/+h[8358]}}while(0);x=+h[8341];do{if(x<y){z=+h[8342];if(z<=y){A=+((a[20668]|0)==112&1|0);break}B=(y-x)/(z-x);if((a[20668]|0)==112){A=B;break}A=1.0-B}else{A=+((a[20668]|0)!=112&1|0)}}while(0);l=(c[3524]|0)+144|0;if((c[l>>2]|0)==0){i=e;return}c[f>>2]=5;h[f+8>>3]=A;c[f+4>>2]=0;cM[c[l>>2]&511](f);i=e;return}else if((w|0)==4){x=+h[v+8>>3];B=+h[8341];do{if(B<x){z=+h[8342];if(z<=x){C=+((a[20668]|0)==112&1|0);break}D=(x-B)/(z-B);if((a[20668]|0)==112){C=D;break}C=1.0-D}else{C=+((a[20668]|0)!=112&1|0)}}while(0);l=(c[3524]|0)+144|0;if((c[l>>2]|0)==0){i=e;return}c[g>>2]=5;h[g+8>>3]=C;c[g+4>>2]=0;cM[c[l>>2]&511](g);i=e;return}else if((w|0)==5){B=+h[v+8>>3];if((a[20668]|0)==112){E=B}else{E=1.0-B}l=(c[3524]|0)+144|0;if((c[l>>2]|0)==0){i=e;return}c[j>>2]=5;h[j+8>>3]=E;c[j+4>>2]=0;cM[c[l>>2]&511](j);i=e;return}else{i=e;return}}}while(0);cM[c[d+64>>2]&511](-2);i=e;return}function fo(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0;g=i;i=i+32|0;j=g|0;k=g+8|0;l=g+16|0;m=g+24|0;if((c[b+88>>2]|0)==0){c[e>>2]=0;n=0}else{c[e>>2]=~~(+h[3817]*+((c[d+28>>2]|0)>>>0>>>0)*.5);n=~~(+h[3817]*+((c[d+24>>2]|0)>>>0>>>0)*.5)}c[f>>2]=n;n=b+144|0;if((a[37384]&1)==0){fN(n,l,m,91736);c[e>>2]=(c[e>>2]|0)+~~+h[l>>3];o=(c[f>>2]|0)+~~+h[m>>3]|0;c[f>>2]=o;i=g;return}else{fx(n,j,k,91736);c[e>>2]=(c[e>>2]|0)+(c[j>>2]|0);o=(c[f>>2]|0)+(c[k>>2]|0)|0;c[f>>2]=o;i=g;return}}function fp(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0;f=i;i=i+48|0;g=f|0;j=f+16|0;k=f+32|0;l=f+40|0;fn(e+72|0,c[3524]|0);m=a[e+184|0]&1;do{if(m<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;n=c[(c[3524]|0)+88>>2]|0;if((n|0)==0){break}cO[n&255](179864)}}while(0);a[37456]=m;fo(e,c[3524]|0,k,l);m=e+52|0;n=c[m>>2]|0;do{if((n|0)==0){o=7011}else{if((cO[c[(c[3524]|0)+72>>2]&255](n)|0)==0){o=7011;break}ln((c[k>>2]|0)+b|0,(c[l>>2]|0)+d|0,c[e+60>>2]|0,c[e+48>>2]|0,0,c[m>>2]|0,c[e+64>>2]|0);p=c[(c[3524]|0)+72>>2]|0;cO[p&255](0)}}while(0);if((o|0)==7011){ln((c[k>>2]|0)+b|0,(c[l>>2]|0)+d|0,c[e+60>>2]|0,c[e+48>>2]|0,0,0,c[e+64>>2]|0)}if((c[e+88>>2]|0)==0){a[37456]=0;i=f;return}l=c[3524]|0;do{if((c[l+96>>2]&128|0)==0){if((b|0)<=0){a[37456]=0;i=f;return}if(!((c[l+8>>2]|0)>>>0>b>>>0&(d|0)>0)){a[37456]=0;i=f;return}if((c[l+12>>2]|0)>>>0>d>>>0){break}a[37456]=0;i=f;return}}while(0);k=g;m=e+128|0;c[k>>2]=c[m>>2];c[k+4>>2]=c[m+4>>2];c[k+8>>2]=c[m+8>>2];c[k+12>>2]=c[m+12>>2];m=c[e+92>>2]|0;q=+h[e+112>>3];k=c[l+92>>2]|0;if(q<0.0){cK[k&63](+h[3817])}else{cK[k&63](q)}cK[c[(c[3524]|0)+112>>2]&63](+h[e+104>>3]);k=c[(c[3524]|0)+64>>2]|0;if((m|0)<-5){cM[k&511](-2)}else{cM[k&511](m)}k=c[3524]|0;do{if((a[e+120|0]&1)==0){if((c[k+96>>2]&1024|0)!=0){r=k;break}c[g>>2]=1;c[g+4>>2]=m;o=7026}else{o=7026}}while(0);if((o|0)==7026){fn(g,k);r=c[3524]|0}cR[c[r+80>>2]&127](b,d,c[e+96>>2]|0);e=j;c[e>>2]=c[14084];c[e+4>>2]=c[56340>>2];c[e+8>>2]=c[56344>>2];c[e+12>>2]=c[56348>>2];e=c[14075]|0;do{if((c[14074]|0)!=0){q=+h[7040];d=c[(c[3524]|0)+92>>2]|0;if(q<0.0){cK[d&63](+h[3817]);break}else{cK[d&63](q);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);d=c[(c[3524]|0)+64>>2]|0;if((e|0)<-5){cM[d&511](-2)}else{cM[d&511](e)}d=c[3524]|0;do{if((a[56328]&1)==0){if((c[d+96>>2]&1024|0)==0){c[j>>2]=1;c[j+4>>2]=e;break}else{a[37456]=0;i=f;return}}}while(0);fn(j,d);a[37456]=0;i=f;return}function fq(a,b){a=+a;b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0.0,n=0,o=0.0,p=0.0,q=0.0,r=0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0;d=i;ft(a,b);if((c[5163]|0)==103){i=d;return}e=c[5174]|0;if((e|0)==104){f=b|0;g=b+8|0;a=+h[g>>3];j=b+16|0;k=+h[j>>3];if(a==0.0){h[j>>3]=k;h[g>>3]=k;h[f>>3]=k;i=d;return}l=+h[f>>3]*6.0;n=~~+O(+l);o=l- +(n|0);l=k*(1.0-a);p=k*(1.0-a*o);q=k*(1.0-a*(1.0-o));r=(n|0)%6&-1;if((r|0)==1){h[f>>3]=p;h[g>>3]=k;h[j>>3]=l;i=d;return}else if((r|0)==0){h[f>>3]=k;h[g>>3]=q;h[j>>3]=l;i=d;return}else if((r|0)==4){h[f>>3]=q;h[g>>3]=l;h[j>>3]=k;i=d;return}else if((r|0)==3){h[f>>3]=l;h[g>>3]=p;h[j>>3]=k;i=d;return}else if((r|0)==2){h[f>>3]=l;h[g>>3]=k;h[j>>3]=q;i=d;return}else{h[f>>3]=k;h[g>>3]=l;h[j>>3]=p;i=d;return}}else if((e|0)==121){j=b|0;p=+h[j>>3];g=b+8|0;l=+h[g>>3];f=b+16|0;k=+h[f>>3];q=p-l*.956+k*.621;do{if(q<0.0){s=0.0}else{if(q>1.0){s=1.0;break}s=q}}while(0);h[j>>3]=s;s=p-l*.272-k*.647;do{if(s<0.0){t=0.0}else{if(s>1.0){t=1.0;break}t=s}}while(0);h[g>>3]=t;t=p-l*1.105-k*1.702;do{if(t<0.0){u=0.0}else{if(t>1.0){u=1.0;break}u=t}}while(0);h[f>>3]=u;i=d;return}else if((e|0)==99){f=b|0;g=b+8|0;u=+h[g>>3];j=b+16|0;t=+h[j>>3];k=1.0- +h[f>>3];do{if(k<0.0){w=0.0}else{if(k>1.0){w=1.0;break}w=k}}while(0);h[f>>3]=w;w=1.0-u;do{if(w<0.0){x=0.0}else{if(w>1.0){x=1.0;break}x=w}}while(0);h[g>>3]=x;x=1.0-t;do{if(x<0.0){y=0.0}else{if(x>1.0){y=1.0;break}y=x}}while(0);h[j>>3]=y;i=d;return}else if((e|0)==120){j=b|0;y=+h[j>>3];g=b+8|0;x=+h[g>>3];f=b+16|0;t=+h[f>>3];w=y*1.91-x*.5338-t*.2891;do{if(w<0.0){z=0.0}else{if(w>1.0){z=1.0;break}z=w}}while(0);h[j>>3]=z;z=y*-.9844+x*1.999-t*.0279;do{if(z<0.0){A=0.0}else{if(z>1.0){A=1.0;break}A=z}}while(0);h[g>>3]=A;A=y*.0585-x*.1187-t*.9017;do{if(A<0.0){B=0.0}else{if(A>1.0){B=1.0;break}B=A}}while(0);h[f>>3]=B;i=d;return}else if((e|0)==114){i=d;return}else{cf(c[m>>2]|0,91064,(v=i,i=i+24|0,c[v>>2]=179392,c[v+8>>2]=310,c[v+16>>2]=e<<24>>24,v)|0);i=d;return}}function fr(b,d){b=+b;d=d|0;var e=0,f=0,g=0,j=0.0,k=0.0,l=0,m=0,n=0,o=0.0,p=0,q=0.0,r=0,s=0.0;e=i;i=i+24|0;f=e|0;g=c[5168]|0;L9652:do{if((g|0)==0){j=b}else{k=+O(+(+(g|0)*b))/+(g-1|0);if((c[5163]|0)!=100){j=k;break}l=c[5172]|0;if((l|0)<3&k==0.0){j=k;break}m=c[5173]|0;n=0;o=k;while(1){if((n|0)>=(l|0)){j=o;break L9652}k=+h[m+(n<<5)>>3];p=n+1|0;q=+h[m+(p<<5)>>3];r=q>b;do{if(k<=b&r){if(!(o<k|o>q)){s=o;break}s=(k+q)*.5}else{s=o}}while(0);if(r){j=s;break}else{n=p;o=s}}}}while(0);fq(j,f);j=+h[f+8>>3];s=+h[f+16>>3];a[d|0]=~~(+h[f>>3]*255.0+.5);a[d+1|0]=~~(j*255.0+.5);a[d+2|0]=~~(s*255.0+.5);i=e;return}function fs(a,b,d,e){a=a|0;b=b|0;d=+d;e=e|0;var f=0,g=0,j=0.0,k=0,l=0,m=0,n=0,o=0.0,p=0,q=0,r=0,s=0,t=0,u=0.0,v=0,w=0,x=0,y=0,z=0.0,A=0.0,B=0,C=0,D=0,E=0,F=0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,Q=0.0,R=0.0,S=0.0,T=0,U=0,V=0,W=0,X=0,Y=0;a=i;i=i+24|0;f=a|0;g=(b|0)<1?2e3:b;j=d>0.0?d:.003;b=ut(1600)|0;k=b;l=ut(2400)|0;m=l;ft(0.0,m);h[b>>3]=0.0;n=b+8|0;c[n>>2]=c[l>>2];c[n+4>>2]=c[l+4>>2];c[n+8>>2]=c[l+8>>2];c[n+12>>2]=c[l+12>>2];c[n+16>>2]=c[l+16>>2];c[n+20>>2]=c[l+20>>2];d=+(g|0);ft(1.0/d,l+24|0);do{if((g|0)>0){l=0;o=0.0;n=50;b=k;p=100;q=m;r=1;while(1){s=q;t=p;u=o;v=2;while(1){w=v+l|0;if((w|0)>(g|0)){x=s;y=t;z=u;break}A=+(w|0)/d;if((v|0)==(t|0)){B=v+50|0;C=uv(s,B<<5)|0;D=B}else{C=s;D=t}B=C+(v*24&-1)|0;ft(A,B);E=v-2|0;F=v-1|0;G=+h[C+(E*24&-1)>>3];H=+h[C+(E*24&-1)+8>>3];I=+h[C+(E*24&-1)+16>>3];J=+h[C+(F*24&-1)>>3];K=+h[C+(F*24&-1)+8>>3];L=+h[C+(F*24&-1)+16>>3];M=+h[B>>3];N=+h[C+(v*24&-1)+8>>3];O=+h[C+(v*24&-1)+16>>3];if(G<J&J>M){x=C;y=D;z=A;break}if(H<K&K>N){x=C;y=D;z=A;break}if(I<L&L>O){x=C;y=D;z=A;break}if(G>J&J<M){x=C;y=D;z=A;break}if(H>K&K<N){x=C;y=D;z=A;break}if(I>L&L<O){x=C;y=D;z=A;break}L=+h[C>>3];I=+h[C+8>>3];K=+h[C+16>>3];H=+(v|0);J=(M-L)/H;M=(N-I)/H;N=(O-K)/H;H=0.0;B=1;while(1){if((B|0)>=(v|0)){Q=H;break}O=+(B|0);G=+P(+(L+J*O- +h[C+(B*24&-1)>>3]));R=+P(+(I+M*O- +h[C+(B*24&-1)+8>>3]));S=+P(+(K+N*O- +h[C+(B*24&-1)+16>>3]));O=G>H?G:H;G=R>O?R:O;O=S>G?S:G;if(O<j){H=O;B=B+1|0}else{Q=O;break}}if(Q>j){x=C;y=D;z=A;break}else{s=C;t=D;u=A;v=v+1|0}}if((r|0)==(n|0)){t=r+25|0;T=uv(b,t<<5)|0;U=t}else{T=b;U=n}h[T+(r<<5)>>3]=z;t=T+(r<<5)+8|0;s=x+((v-1|0)*24&-1)|0;c[t>>2]=c[s>>2];c[t+4>>2]=c[s+4>>2];c[t+8>>2]=c[s+8>>2];c[t+12>>2]=c[s+12>>2];c[t+16>>2]=c[s+16>>2];c[t+20>>2]=c[s+20>>2];V=r+1|0;t=x;c[t>>2]=c[s>>2];c[t+4>>2]=c[s+4>>2];c[t+8>>2]=c[s+8>>2];c[t+12>>2]=c[s+12>>2];c[t+16>>2]=c[s+16>>2];c[t+20>>2]=c[s+20>>2];s=x+24|0;t=x+(v*24&-1)|0;c[s>>2]=c[t>>2];c[s+4>>2]=c[t+4>>2];c[s+8>>2]=c[t+8>>2];c[s+12>>2]=c[t+12>>2];c[s+16>>2]=c[t+16>>2];c[s+20>>2]=c[t+20>>2];if((w|0)<(g|0)){l=w;o=z;n=U;b=T;p=y;q=x;r=V}else{break}}ft(1.0,f);if((V|0)!=(U|0)){W=T;X=V;Y=x;break}W=uv(T,(V<<5)+32|0)|0;X=V;Y=x}else{ft(1.0,f);W=k;X=1;Y=m}}while(0);h[W+(X<<5)>>3]=1.0;m=W+(X<<5)+8|0;k=f;c[m>>2]=c[k>>2];c[m+4>>2]=c[k+4>>2];c[m+8>>2]=c[k+8>>2];c[m+12>>2]=c[k+12>>2];c[m+16>>2]=c[k+16>>2];c[m+20>>2]=c[k+20>>2];uu(Y);c[e>>2]=X+1;i=a;return W|0}function ft(b,d){b=+b;d=d|0;var e=0,f=0,g=0.0,j=0,k=0.0,l=0.0,n=0.0,o=0.0,p=0,q=0.0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0;e=i;i=i+24|0;f=e|0;do{if(b<0.0){g=0.0}else{if(b<=1.0){g=b;break}g=1.0}}while(0);j=c[5163]|0;if((j|0)==99){b=(+h[2706]/3.0+g*+h[2707])*6.283185307179586;k=+h[2705];if(k!=1.0){l=+R(+g,+(1.0/k))}else{l=g}k=(1.0-l)*l*+h[2708]*.5;n=+S(+b);o=+T(+b);b=l+k*(n*-.14861+o*1.78277);p=d|0;h[p>>3]=b;q=l+k*(n*-.29227-o*.90649);r=d+8|0;h[r>>3]=q;o=l+n*1.97294*k;s=d+16|0;h[s>>3]=o;do{if(b>1.0){h[p>>3]=1.0}else{if(b>=0.0){break}h[p>>3]=0.0}}while(0);do{if(q>1.0){h[r>>3]=1.0}else{if(q>=0.0){break}h[r>>3]=0.0}}while(0);if(o>1.0){h[s>>3]=1.0;i=e;return}if(o>=0.0){i=e;return}h[s>>3]=0.0;i=e;return}else if((j|0)==100){if(g<0.0){h[d>>3]=+h[(c[5173]|0)+8>>3];h[d+8>>3]=+h[(c[5173]|0)+16>>3];h[d+16>>3]=+h[(c[5173]|0)+24>>3];i=e;return}s=c[5172]|0;if(g>1.0){r=s-1|0;h[d>>3]=+h[(c[5173]|0)+(r<<5)+8>>3];h[d+8>>3]=+h[(c[5173]|0)+(r<<5)+16>>3];h[d+16>>3]=+h[(c[5173]|0)+(r<<5)+24>>3];i=e;return}L9731:do{if((s|0)>1){r=c[5173]|0;p=0;t=s-1|0;L9733:while(1){u=p;while(1){if((u|0)==(t|0)){w=t;x=r;break L9731}y=(u+t|0)/2&-1;if(+h[r+(y<<5)>>3]<g){u=y+1|0}else{p=u;t=y;continue L9733}}}}else{w=0;x=c[5173]|0}}while(0);s=x+(w<<5)+8|0;o=+h[x+(w<<5)>>3];if(o==g){h[d>>3]=+h[s>>3];h[d+8>>3]=+h[x+(w<<5)+16>>3];h[d+16>>3]=+h[x+(w<<5)+24>>3];i=e;return}else{t=w-1|0;q=+h[x+(t<<5)>>3];b=(g-q)/(o-q);q=+h[x+(t<<5)+8>>3];h[d>>3]=q+b*(+h[s>>3]-q);q=+h[x+(t<<5)+16>>3];h[d+8>>3]=q+b*(+h[x+(w<<5)+16>>3]-q);q=+h[x+(t<<5)+24>>3];h[d+16>>3]=q+b*(+h[x+(w<<5)+24>>3]-q);i=e;return}}else if((j|0)==114){h[d>>3]=+fu(c[5164]|0,g);h[d+8>>3]=+fu(c[5165]|0,g);h[d+16>>3]=+fu(c[5166]|0,g);i=e;return}else if((j|0)==102){c[5182]=2;h[2592]=g;h[2593]=0.0;e4(c[5178]|0,f);if((a[1960]&1)!=0){uf(-1,86464,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}w=f|0;x=c[w>>2]|0;if((x|0)==1){z=+(c[f+8>>2]|0)}else if((x|0)==3){z=+uz(c[f+8>>2]|0,0)}else if((x|0)==2){z=+h[f+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(z<0.0){A=0.0}else{A=z>1.0?1.0:z}c[5260]=2;h[2631]=g;h[2632]=0.0;e4(c[5256]|0,f);if((a[1960]&1)!=0){uf(-1,85592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}x=c[w>>2]|0;if((x|0)==3){B=+uz(c[f+8>>2]|0,0)}else if((x|0)==1){B=+(c[f+8>>2]|0)}else if((x|0)==2){B=+h[f+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(B<0.0){C=0.0}else{C=B>1.0?1.0:B}c[5338]=2;h[2670]=g;h[2671]=0.0;e4(c[5334]|0,f);if((a[1960]&1)!=0){uf(-1,84488,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}x=c[w>>2]|0;if((x|0)==1){D=+(c[f+8>>2]|0)}else if((x|0)==3){D=+uz(c[f+8>>2]|0,0)}else if((x|0)==2){D=+h[f+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(D<0.0){E=0.0}else{E=D>1.0?1.0:D}h[d>>3]=A;h[d+8>>3]=C;h[d+16>>3]=E;i=e;return}else if((j|0)==103){E=+R(+g,+(1.0/+h[2705]));h[d+16>>3]=E;h[d+8>>3]=E;h[d>>3]=E;i=e;return}else{cf(c[m>>2]|0,87488,(v=i,i=i+24|0,c[v>>2]=179392,c[v+8>>2]=272,c[v+16>>2]=j<<24>>24,v)|0);i=e;return}}function fu(a,b){a=a|0;b=+b;var d=0,e=0.0,f=0,g=0.0,h=0.0;d=i;if((a|0)<0){e=1.0-b;f=-a|0}else{e=b;f=a}do{if((f|0)==18){g=+P(+(+S(+(e*360.0*.017453292519943295))))}else if((f|0)==33){g=+P(+(e*2.0+-.5))}else if((f|0)==0){h=0.0;i=d;return+h}else if((f|0)==25){g=+P(+(e*3.0+-2.0))}else if((f|0)==10){g=+S(+(e*90.0*.017453292519943295))}else if((f|0)==2){h=1.0;i=d;return+h}else if((f|0)==11){g=+P(+(e+-.5))}else if((f|0)==31){if(e<=.42){h=0.0;i=d;return+h}if(e<.92){g=e*2.0+-.84;break}else{h=1.0;i=d;return+h}}else if((f|0)==15){g=+T(+(e*360.0*.017453292519943295))}else if((f|0)==16){g=+S(+(e*360.0*.017453292519943295))}else if((f|0)==26){g=e*1.5+-.5}else if((f|0)==21){g=e*3.0}else if((f|0)==14){g=+P(+(+S(+(e*180.0*.017453292519943295))))}else if((f|0)==19){g=+P(+(+T(+(e*720.0*.017453292519943295))))}else if((f|0)==20){g=+P(+(+S(+(e*720.0*.017453292519943295))))}else if((f|0)==23){g=e*3.0+-2.0}else if((f|0)==24){g=+P(+(e*3.0+-1.0))}else if((f|0)==28){g=+P(+(e*1.5+-.5))}else if((f|0)==3){g=e}else if((f|0)==22){g=e*3.0+-1.0}else if((f|0)==29){g=+P(+(e*1.5+-1.0))}else if((f|0)==27){g=e*1.5+-1.0}else if((f|0)==1){h=.5;i=d;return+h}else if((f|0)==30){if(e<=.25){h=0.0;i=d;return+h}if(e<.57){g=e/.32+-.78125;break}else{h=1.0;i=d;return+h}}else if((f|0)==34){g=e*2.0}else if((f|0)==35){g=e*2.0+-.5}else if((f|0)==5){g=e*e*e}else if((f|0)==6){g=e*e*e*e}else if((f|0)==9){g=+T(+(e*90.0*.017453292519943295))}else if((f|0)==36){g=e*2.0+-1.0}else if((f|0)==4){g=e*e}else if((f|0)==12){b=e*2.0+-1.0;g=b*b}else if((f|0)==32){if(e<=.42){g=e*4.0;break}if(e>.92){g=e/.08-11.5;break}else{g=e*-2.0+1.84;break}}else if((f|0)==7){g=+Q(+e)}else if((f|0)==13){g=+T(+(e*180.0*.017453292519943295))}else if((f|0)==8){g=+Q(+(+Q(+e)))}else if((f|0)==17){g=+P(+(+T(+(e*360.0*.017453292519943295))))}else{cf(c[m>>2]|0,143136,(v=i,i=i+8|0,c[v>>2]=(c[5162]|0)-1,v)|0);bd(1);return 0.0}}while(0);if(g<=0.0){h=0.0;i=d;return+h}h=g<1.0?g:1.0;i=d;return+h}function fv(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,R=0,U=0,V=0,W=0,X=0,Y=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aJ=0.0,aK=0.0,aL=0.0,aM=0.0,aO=0.0,aP=0,aQ=0,aR=0.0,aS=0.0,aT=0.0,aU=0.0,aV=0.0,aW=0.0,aX=0,aY=0,aZ=0,a_=0.0,a$=0,a0=0,a1=0.0,a2=0.0,a3=0,a4=0.0,a5=0,a6=0,a7=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bx=0,by=0,bz=0,bA=0,bB=0.0,bC=0,bD=0,bE=0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bM=0,bN=0,bO=0,bP=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0.0,bY=0,bZ=0,b_=0.0,b$=0.0,b0=0.0,b1=0.0,b2=0.0,b3=0.0,b5=0.0,b6=0.0,b7=0.0,b8=0,b9=0,ca=0,cb=0.0,cc=0.0,cd=0.0,ce=0.0,cf=0.0,cg=0,ch=0,ci=0.0,cj=0,ck=0.0,cl=0.0,cm=0.0,cn=0.0,co=0.0,cp=0.0,cq=0.0,cr=0.0,cs=0.0,ct=0.0,cu=0.0,cv=0.0,cw=0.0,cx=0.0,cy=0.0,cz=0.0,cA=0.0,cB=0,cC=0,cD=0.0,cE=0,cF=0,cH=0,cI=0.0,cJ=0,cL=0,cP=0,cQ=0,cT=0,cU=0,cV=0,cW=0,cX=0,cY=0,cZ=0,c_=0,c$=0,c0=0,c1=0,c2=0,c3=0,c4=0,c5=0,c6=0.0,c7=0.0,c8=0,c9=0,da=0,db=0,dc=0,dd=0,de=0,df=0,dg=0,dh=0,di=0,dj=0.0,dk=0.0,dl=0,dm=0.0,dn=0,dp=0,dq=0.0,dr=0,ds=0,dt=0,du=0,dv=0,dw=0.0,dx=0.0,dy=0,dA=0,dB=0,dC=0,dE=0,dF=0,dG=0,dH=0,dI=0,dJ=0,dK=0,dL=0,dM=0,dN=0,dO=0,dP=0,dQ=0.0,dR=0.0,dS=0,dT=0,dU=0,dV=0,dW=0.0,dX=0,dY=0,dZ=0,d_=0;f=i;i=i+1992|0;j=f|0;k=f+48|0;l=f+96|0;n=f+104|0;o=f+112|0;p=f+120|0;q=f+128|0;r=f+144|0;s=f+160|0;t=f+168|0;u=f+176|0;w=f+184|0;x=f+192|0;y=f+200|0;z=f+208|0;A=f+216|0;B=f+224|0;C=f+240|0;D=f+256|0;E=f+264|0;F=f+272|0;G=f+312|0;H=f+328|0;I=f+336|0;J=f+344|0;K=f+384|0;L=f+400|0;M=f+424|0;N=f+432|0;O=f+440|0;Q=f+448|0;R=f+456|0;U=f+464|0;V=f+472|0;W=f+480|0;X=f+488|0;Y=f+496|0;$=f+504|0;ab=f+512|0;ac=f+520|0;ad=f+528|0;ae=f+536|0;af=f+544|0;ag=f+552|0;ah=f+560|0;ai=f+568|0;aj=f+576|0;ak=f+584|0;al=f+600|0;am=f+608|0;an=f+616|0;ao=f+624|0;ap=f+632|0;aq=f+648|0;ar=f+664|0;as=f+672|0;at=f+680|0;au=f+688|0;av=f+696|0;aw=f+704|0;ax=f+712|0;ay=f+728|0;az=f+736|0;aA=f+864|0;aB=f+872|0;aC=f+880|0;aD=f+1912|0;aE=f+1920|0;aF=f+1928|0;aG=f+1936|0;aH=c[3524]|0;aJ=+g[3536]*.017453292519943295;aK=+S(+aJ);aL=+T(+aJ);uE(3136,0,24);uE(3176,0,32);h[401]=1.0;uE(3216,0,32);h[406]=1.0;h[391]=aK;aJ=-0.0-aL;h[392]=aJ;h[395]=aL;h[396]=aK;aL=+g[3538]*.017453292519943295;aM=+S(+aL);aO=+T(+aL);aL=-0.0-aO;aP=az;aQ=0;aR=aK;aK=aJ;aJ=0.0;aS=0.0;while(1){aT=aK*0.0;aU=aJ*0.0;aV=aS*0.0;h[az+(aQ<<5)>>3]=aR+0.0+aT+aU+aV;aW=aR*0.0+0.0;h[az+(aQ<<5)+8>>3]=aW+aM*aK+aO*aJ+aV;h[az+(aQ<<5)+16>>3]=aW+aK*aL+aM*aJ+aV;h[az+(aQ<<5)+24>>3]=aS+(aW+aT+aU);aX=aQ+1|0;if((aX|0)>=4){break}aQ=aX;aR=+h[3128+(aX<<5)>>3];aK=+h[3136+(aX<<5)>>3];aJ=+h[3144+(aX<<5)>>3];aS=+h[3152+(aX<<5)>>3]}c[782]=c[aP>>2];c[783]=c[aP+4>>2];c[784]=c[aP+8>>2];c[785]=c[aP+12>>2];c[786]=c[aP+16>>2];c[787]=c[aP+20>>2];c[788]=c[aP+24>>2];c[789]=c[aP+28>>2];aQ=az+32|0;c[790]=c[aQ>>2];c[3164>>2]=c[aQ+4>>2];c[3168>>2]=c[aQ+8>>2];c[3172>>2]=c[aQ+12>>2];c[3176>>2]=c[aQ+16>>2];c[3180>>2]=c[aQ+20>>2];c[3184>>2]=c[aQ+24>>2];c[3188>>2]=c[aQ+28>>2];aX=az+64|0;c[798]=c[aX>>2];c[3196>>2]=c[aX+4>>2];c[3200>>2]=c[aX+8>>2];c[3204>>2]=c[aX+12>>2];c[3208>>2]=c[aX+16>>2];c[3212>>2]=c[aX+20>>2];c[3216>>2]=c[aX+24>>2];c[3220>>2]=c[aX+28>>2];aY=az+96|0;c[806]=c[aY>>2];c[3228>>2]=c[aY+4>>2];c[3232>>2]=c[aY+8>>2];c[3236>>2]=c[aY+12>>2];c[3240>>2]=c[aY+16>>2];c[3244>>2]=c[aY+20>>2];c[3248>>2]=c[aY+24>>2];c[3252>>2]=c[aY+28>>2];aS=+g[3534]*.5;aZ=0;do{aJ=+h[3128+(aZ<<5)>>3];aK=+h[3136+(aZ<<5)>>3];aR=aK*0.0;aM=+h[3144+(aZ<<5)>>3];aL=aM*0.0;aO=+h[3152+(aZ<<5)>>3];aU=aO*0.0;h[az+(aZ<<5)>>3]=aS*aJ+0.0+aR+aL+aU;aT=aJ*0.0+0.0;h[az+(aZ<<5)+8>>3]=aT+aS*aK+aL+aU;aK=aT+aR;h[az+(aZ<<5)+16>>3]=aK+aS*aM+aU;h[az+(aZ<<5)+24>>3]=aO+(aK+aL);aZ=aZ+1|0;}while((aZ|0)<4);c[782]=c[aP>>2];c[783]=c[aP+4>>2];c[784]=c[aP+8>>2];c[785]=c[aP+12>>2];c[786]=c[aP+16>>2];c[787]=c[aP+20>>2];c[788]=c[aP+24>>2];c[789]=c[aP+28>>2];c[790]=c[aQ>>2];c[3164>>2]=c[aQ+4>>2];c[3168>>2]=c[aQ+8>>2];c[3172>>2]=c[aQ+12>>2];c[3176>>2]=c[aQ+16>>2];c[3180>>2]=c[aQ+20>>2];c[3184>>2]=c[aQ+24>>2];c[3188>>2]=c[aQ+28>>2];c[798]=c[aX>>2];c[3196>>2]=c[aX+4>>2];c[3200>>2]=c[aX+8>>2];c[3204>>2]=c[aX+12>>2];c[3208>>2]=c[aX+16>>2];c[3212>>2]=c[aX+20>>2];c[3216>>2]=c[aX+24>>2];c[3220>>2]=c[aX+28>>2];c[806]=c[aY>>2];c[3228>>2]=c[aY+4>>2];c[3232>>2]=c[aY+8>>2];c[3236>>2]=c[aY+12>>2];c[3240>>2]=c[aY+16>>2];c[3244>>2]=c[aY+20>>2];c[3248>>2]=c[aY+24>>2];c[3252>>2]=c[aY+28>>2];if((a[30528]&1)!=0){uk(83256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((a[624]&1)==0){aY=c[34]|0;aS=+h[64664+(aY*688&-1)>>3];a_=aS-(+h[64672+(aY*688&-1)>>3]-aS)*+h[77]}else{aS=+h[77];if((a[64788]&1)==0){a_=aS;break}aL=+_(+aS);a_=aL/+h[8100]}}while(0);h[7076]=a_;aY=c[200]|0;L9870:do{if((c[64916+(aY*688&-1)>>2]|0)==0){if((c[64916+((c[144]|0)*688&-1)>>2]|0)==0){a$=0}else{a0=7282;break}while(1){if(a$>>>0>=8){break}if((a[65036+(a$*688&-1)|0]&1)!=0){a0=7282;break L9870}if((a[65037+(a$*688&-1)|0]&1)==0){a$=a$+1|0}else{a0=7282;break L9870}}aX=c[34]|0;aL=+h[64664+(aX*688&-1)>>3];h[5279]=aL;aS=+h[64672+(aX*688&-1)>>3];h[6915]=aS;a1=aL;a2=aS}else{a0=7282}}while(0);do{if((a0|0)==7282){a$=c[34]|0;aS=+h[64664+(a$*688&-1)>>3];if((c[64656+(a$*688&-1)>>2]&2|0)==0){h[5279]=aS<a_?aS:a_;aL=+h[64672+(a$*688&-1)>>3];h[6915]=aL>a_?aL:a_;a1=aS;a2=aL;break}else{h[5279]=aS>a_?aS:a_;aL=+h[64672+(a$*688&-1)>>3];h[6915]=aL<a_?aL:a_;a1=aS;a2=aL;break}}}while(0);if(+h[64664+(aY*688&-1)>>3]==+h[64672+(aY*688&-1)>>3]){uk(175032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}aY=c[144]|0;if(+h[64664+(aY*688&-1)>>3]==+h[64672+(aY*688&-1)>>3]){uk(141592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(a1==a2){uk(126608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[14080]&1)==0){lk()}do{if(a[14088]|0){if((a[33512]&1)==0){break}if(!(a[13032]|0)){break}aY=c[(c[3524]|0)+104>>2]|0;if((aY|0)!=0){cS[aY&511]()}a[13032]=0}else{cS[c[(c[3524]|0)+52>>2]&511]();a[14088]=1}}while(0);cM[c[(c[3524]|0)+168>>2]&511](0);aY=c[3524]|0;if((c[aY+96>>2]&16|0)!=0){c[7271]=-1}c[13880]=0;c[13881]=(c[aY+8>>2]|0)-1;c[13882]=0;c[13883]=(c[aY+12>>2]|0)-1;a[25280]=0;cM[c[aY+168>>2]&511](1);aY=c[3524]|0;c[1100]=0;a$=aY+24|0;aX=aY+28|0;a2=+h[4524];if(a2<0.0){a3=0;a4=+h[3817]}else{a1=+h[3817];a3=~~(a2*+((c[aY+20>>2]|0)>>>0>>>0)+a1*+((c[aX>>2]|0)>>>0>>>0));a4=a1}c[9002]=a3;a1=+h[4525];a3=~~(a1*a4*+((c[a$>>2]|0)>>>0>>>0)*1.25);c[9022]=a3;aQ=aY+16|0;aP=c[aQ>>2]|0;if(a3>>>0<aP>>>0){c[9022]=~~(a1*+(aP>>>0>>>0))}aP=(d|0)>0;if(aP){a3=0;aZ=0;az=0;a5=b;while(1){a6=c[a5+16>>2]|0;do{if((a6|0)==0){a7=aZ;a9=a3}else{if((a[a6]|0)==0){a7=aZ;a9=a3;break}if((a[a5+22|0]&1)!=0){a7=aZ;a9=a3;break}ba=lp(a6)|0;a7=(ba|0)>(aZ|0)?ba:aZ;a9=a3+1|0}}while(0);do{if((c[11690]|0)==0){bb=a7;bc=a9}else{a6=a[35888]|0;if((a6&1)==0){bb=a7;bc=a9;break}ba=c[a5+256>>2]|0;if((ba|0)==0){bb=a7;bc=a9;break}else{bd=0;be=0;bf=ba;bg=a6}while(1){do{if((bg&1)==0){bh=be;bi=bd}else{if((a[bf+8|0]|0)==0){bh=be;bi=bd;break}a6=bf+9|0;ba=lp(a6)|0;bj=b4(a6|0,73808)|0;a6=ba-bj|0;bh=((ba|0)!=(bj|0)&1)+be|0;bi=(a6|0)>(bd|0)?a6:bd}}while(0);a6=c[bf>>2]|0;if((a6|0)==0){break}bd=bi;be=bh;bf=a6;bg=a[35888]|0}bb=(bi|0)>(a7|0)?bi:a7;bc=bh+a9|0}}while(0);a6=az+1|0;if((a6|0)<(d|0)){a3=bc;aZ=bb;az=a6;a5=c[a5>>2]|0}else{bk=bc;bl=bb;break}}}else{bk=0;bl=0}c[6962]=bk;c[8712]=bl;a1=+(ga(36288,ay)|0);bl=~~(a1-(+h[4524]+2.0));a1=+(c[ay>>2]|0);h[4487]=a1;bk=c[8712]|0;if((bl|0)>(bk|0)){c[8712]=bl;bm=bl}else{bm=bk}bk=bm+4|0;bm=aY+20|0;bl=aa(bk,c[bm>>2]|0);bb=c[9002]|0;bc=bl+bb|0;c[9028]=bc;bl=c[8786]|0;a5=(bl|0)==3;a4=+h[4395];do{if(a5){az=~~(a4*+((c[aY+8>>2]|0)>>>0>>>0)+.5);c[7938]=az;bn=az}else{az=c[bm>>2]|0;if(a4<0.0){aZ=(c[aX>>2]|0)+(az<<1)|0;c[7938]=aZ;bn=aZ;break}else{aZ=~~(a4*+(az>>>0>>>0)+.5);c[7938]=aZ;bn=aZ;break}}}while(0);aZ=c[6598]|0;az=(aZ|0)==3;if(az){bo=~~(+h[3301]*+((c[aY+8>>2]|0)>>>0>>>0)+.5)}else{bo=~~(+g[178]*+((c[aY+8>>2]|0)>>>0>>>0)- +(c[bm>>2]<<1>>>0>>>0)- +((c[aX>>2]|0)>>>0>>>0))}c[7939]=bo;a3=c[6962]|0;c[9014]=a3;c[9024]=1;a9=c[9339]|0;bh=(a9|0)>0;if((a3|0)>(a9|0)&bh){c[9014]=a9;a7=((a3-1|0)/(a9|0)&-1)+1|0;c[9024]=a7;bp=a9;bq=a7}else{bp=a3;bq=1}a7=a[36120]&1;bi=a7<<24>>24==0;do{if(bi){br=bp;bs=bc;bt=bq}else{if(!(((c[9031]|0)-1|0)>>>0<2&(c[9032]|0)==1)){br=bp;bs=bc;bt=bq;break}if((a3|0)>0){bg=bo-bn|0;bf=(bg>>>0)/((aa(c[bm>>2]|0,bk)+bb|0)>>>0)>>>0;be=a3-1|0;bd=((be|0)/(((bf|0)==0?1:bf)|0)&-1)+1|0;bf=(bd|0)>(a9|0)&bh?a9:bd;c[9014]=bf;bd=((be|0)/(bf|0)&-1)+1|0;c[9024]=bd;be=(bg|0)/(bd|0)&-1;c[9028]=be;br=bf;bs=be;bt=bd;break}else{c[9028]=0;c[9024]=0;c[9014]=0;br=0;bs=0;bt=0;break}}}while(0);bb=(c[14100]|0)==3;do{if(bb){bk=~~(+h[7052]*+((c[aY+12>>2]|0)>>>0>>>0)+.5);c[7940]=bk;bu=bk}else{a4=+h[7052];bk=c[aQ>>2]|0;if((c[5094]|0)!=0&a4>=0.0){bq=~~(a4*+(bk>>>0>>>0));c[7940]=bq;bu=bq;break}else{bq=~~(+(bk>>>0>>>0)*2.5+1.0);c[7940]=bq;bu=bq;break}}}while(0);do{if(a7<<24>>24!=0&(br|0)!=0){if(!(((c[9031]|0)-1|0)>>>0<2&(c[9032]|0)==1)){bv=bu;break}a4=+(aa(c[9022]|0,br)|0);bq=~~(+(bu|0)+(a4+a1*+((c[aQ>>2]|0)>>>0>>>0)));c[7940]=bq;bv=bq}else{bv=bu}}while(0);bu=c[1119]|0;L9970:do{if((bu|0)!=0){br=(c[1100]|0)+1|0;c[1100]=br;c[ay>>2]=0;a7=a[bu]|0;if(a7<<24>>24==0){break}else{bx=1;by=br;bz=a7}while(1){if(bz<<24>>24==92){a7=by+1|0;c[1100]=a7;bA=a7}else{bA=by}c[ay>>2]=bx;if(bx>>>0>=(uA(bu|0)|0)>>>0){break L9970}a7=a[bu+bx|0]|0;bx=bx+1|0;by=bA;bz=a7}}}while(0);bz=(c[1066]|0)==3;if(bz){bB=+h[535]*+((c[aY+12>>2]|0)>>>0>>>0)+.5}else{bB=+g[38]*+((c[aY+12>>2]|0)>>>0>>>0)- +((c[aQ>>2]|0)>>>0>>>0)*(+(c[1100]|0)+1.5)+-1.0}bA=~~bB;c[7941]=bA;do{if(bi){bC=bn;bD=bl;bE=aZ;bF=bo}else{by=c[9031]|0;if((by|0)==0){a0=7360}else if((by|0)==1|(by|0)==2){if((c[9032]|0)==3){a0=7360}else{bG=bt}}else{bG=bt}do{if((a0|0)==7360){bx=~~(+(((((bA-bv|0)>>>0)/((c[aQ>>2]|0)>>>0)>>>0)-1|0)>>>0>>>0)-a1);bu=(bx|0)>(a9|0)&bh?a9:bx;bx=(bu|0)==0?1:bu;c[ay>>2]=bx;if((a3|0)<=(bx|0)){bG=bt;break}bu=a3-1|0;a7=((bu|0)/(bx|0)&-1)+1|0;c[9024]=a7;c[9014]=((bu|0)/(a7|0)&-1)+1;bG=a7}}while(0);a7=by-1|0;bu=c[9032]|0;bx=(bu|0)!=3|a7>>>0>1;if(bx|az){bH=bx?aZ:3;bI=bo}else{bx=c[bm>>2]|0;br=((bo-bs|0)-aa(bG-1|0,bs)|0)+(bx<<1)|0;c[7939]=br;bH=aZ;bI=br}if(!(a7>>>0<2&(bu|0)==2)){bC=bn;bD=bl;bE=bH;bF=bI;break}if(a5){bC=bn;bD=3;bE=bH;bF=bI;break}bu=c[bm>>2]|0;a7=((bs+bn|0)+aa(bG-1|0,bs)|0)-(bu<<1)|0;c[7938]=a7;bC=a7;bD=bl;bE=bH;bF=bI}}while(0);bI=(c[5094]|0)==0;bH=c[18072]|0;do{if(bI&(bH|0)>0){bl=bA-bv|0;bs=bF-bC|0;if((bl|0)>(bs|0)){bG=(bl-bs|0)/2&-1;bn=bG+bv|0;c[7940]=bn;bm=bA-bG|0;c[7941]=bm;bJ=bF;bK=bC;bL=bm;bM=bn;break}else{bn=(bs-bl|0)/2&-1;bl=bn+bC|0;c[7938]=bl;bs=bF-bn|0;c[7939]=bs;bJ=bs;bK=bl;bL=bA;bM=bv;break}}else{bJ=bF;bK=bC;bL=bA;bM=bv}}while(0);bv=(bD|0)==3;if(bv){bN=bK}else{bD=~~(+(bK|0)+ +((c[aY+8>>2]|0)>>>0>>>0)*+g[184]);c[7938]=bD;bN=bD}bD=(bE|0)==3;if(bD){bO=bJ}else{bE=~~(+(bJ|0)+ +((c[aY+8>>2]|0)>>>0>>>0)*+g[184]);c[7939]=bE;bO=bE}if(bz){bP=bL}else{bE=~~(+(bL|0)+ +((c[aY+12>>2]|0)>>>0>>>0)*+g[44]);c[7941]=bE;bP=bE}if(bb){bQ=bM}else{bE=~~(+(bM|0)+ +((c[aY+12>>2]|0)>>>0>>>0)*+g[44]);c[7940]=bE;bQ=bE}bE=(bO+bN|0)/2&-1;c[186]=bE;aY=(bQ+bP|0)/2&-1;c[46]=aY;bM=bO-bN|0;bL=(bM<<2|0)/7&-1;c[180]=bL;bJ=bP-bQ|0;bK=(bJ<<2|0)/7&-1;c[40]=bK;if(bz|bb){bb=~~(+(bJ|0)/+g[3534]);c[40]=bb;bR=bb}else{bR=bK}if(bD|bv){bv=~~(+(bM|0)/+g[3534]);c[180]=bv;bS=bv}else{bS=bL}if((bR|0)==0){c[40]=1;bT=1}else{bT=bR}if((bS|0)==0){c[180]=1;bU=1}else{bU=bS}do{if(bI){if((c[(c[3524]|0)+96>>2]&128|0)==0){c[13542]=55520;bV=bU;bW=bT;break}else{c[13542]=0;bV=bU;bW=bT;break}}else{a1=+g[18074];do{if(a1!=0.0){do{if(a1<0.0){bS=c[200]|0;bB=+h[64672+(bS*688&-1)>>3]- +h[64664+(bS*688&-1)>>3];if(bB==0.0){a0=7392;break}bS=c[144]|0;bX=(-0.0-a1)*+P(+((+h[64672+(bS*688&-1)>>3]- +h[64664+(bS*688&-1)>>3])/bB))}else{a0=7392}}while(0);if((a0|0)==7392){bX=a1}if(!(bX>=.01&bX<=100.0)){bY=bU;bZ=bT;break}bB=+(bT|0);a4=+(bU|0);a2=bX*+((c[a$>>2]|0)>>>0>>>0)/+((c[aX>>2]|0)>>>0>>>0);if(bB/a4>a2){bS=~~(a4*a2);c[40]=bS;bY=bU;bZ=bS;break}else{bS=~~(bB/a2);c[180]=bS;bY=bS;bZ=bT;break}}else{bY=bU;bZ=bT}}while(0);c[13542]=31752;bV=bY;bW=bZ}}while(0);c[16536]=bN;c[16537]=bO;c[16364]=bQ;c[16365]=bP;bX=+h[5279];a1=+h[6915];c[16192]=~~bX;c[16193]=~~a1;a2=a1-bX;a1=2.0/a2*+g[3532];h[2]=a1;bP=c[144]|0;bB=+h[64664+(bP*688&-1)>>3];a4=2.0/(+h[64672+(bP*688&-1)>>3]-bB);h[21]=a4;bP=c[200]|0;a_=+h[64664+(bP*688&-1)>>3];aL=2.0/(+h[64672+(bP*688&-1)>>3]-a_);h[91]=aL;h[12]=0.0;h[68]=0.0;h[96]=0.0;do{if((bH|0)>1){do{if(a4>aL){aS=1.0-aL/a4;h[68]=aS;h[21]=aL;b_=aL;b$=0.0;b0=aL;b1=aS}else{if(aL<=a4){b_=aL;b$=0.0;b0=a4;b1=0.0;break}aS=1.0-a4/aL;h[96]=aS;h[91]=a4;b_=a4;b$=aS;b0=a4;b1=0.0}}while(0);if((bH|0)<=2){b2=a1;b3=b_;b5=b$;b6=b0;b7=b1;break}h[2]=b_;b2=b_;b3=b_;b5=b$;b6=b0;b7=b1}else{b2=a1;b3=aL;b5=0.0;b6=a4;b7=0.0}}while(0);a4=b2*a2*-.5+1.0;h[12]=a4;if(!bI){a2=+h[8255];aL=+h[8169];a1=(a2-a_)*b3+b5+-1.0;b1=(aL-bB)*b6+b7+-1.0;b0=a4+(0.0-bX)*b2+-1.0;b2=+h[403];bX=+h[391];a4=+h[395];b$=b0*+h[399];b_=+h[404];aS=+h[392];aK=+h[396];aO=b0*+h[400];aU=+h[406];aM=+h[394];aR=+h[398];aT=b0*+h[402];b0=aU+a1*aM+b1*aR+aT;aJ=b0==0.0?1.0e-5:b0;b0=+(bV|0);bV=~~((b2+a1*bX+b1*a4+b$)/aJ*b0)+bE|0;aW=+(bW|0);bW=~~((b_+a1*aS+b1*aK+aO)/aJ*aW)+aY|0;aJ=+h[8256];b1=+h[8170];a1=b5+b3*(aJ-a_)+-1.0;a_=b7+b6*(b1-bB)+-1.0;bB=aT+(aU+aM*a1+aR*a_);aR=bB==0.0?1.0e-5:bB;bI=~~(b0*((b$+(b2+bX*a1+a4*a_))/aR))+bE|0;bE=~~(aW*((aO+(b_+aS*a1+aK*a_))/aR))+aY|0;h[8269]=+(bI-bV|0)/(aJ-a2);h[8183]=+(bE-bW|0)/(b1-aL);c[16536]=bV;c[16537]=bI;c[16364]=bW;c[16365]=bE}bE=(e|0)!=0;if(!bE){do{if(a[31104]|0){if((dz()|0)!=0){b8=0;break}b8=(c[(c[3524]|0)+144>>2]|0)!=0&1}else{b8=0}}while(0);a[55536]=b8}fO(c[10818]|0,-1,3);b8=ax;c[b8>>2]=c[14084];c[b8+4>>2]=c[56340>>2];c[b8+8>>2]=c[56344>>2];c[b8+12>>2]=c[56348>>2];b8=c[14075]|0;do{if((c[14074]|0)!=0){aL=+h[7040];e=c[(c[3524]|0)+92>>2]|0;if(aL<0.0){cK[e&63](+h[3817]);break}else{cK[e&63](aL);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);e=c[(c[3524]|0)+64>>2]|0;if((b8|0)<-5){cM[e&511](-2)}else{cM[e&511](b8)}e=c[3524]|0;do{if((a[56328]&1)==0){if((c[e+96>>2]&1024|0)!=0){break}c[ax>>2]=1;c[ax+4>>2]=b8;a0=7425}else{a0=7425}}while(0);if((a0|0)==7425){fn(ax,e)}e=~~(+g[3536]/90.0);ax=c[200]|0;if((e+1&2|0)==0){aL=+h[64664+(ax*688&-1)>>3];h[14]=aL;b1=+h[64672+(ax*688&-1)>>3];h[3305]=b1;b8=c[144]|0;a2=+h[64672+(b8*688&-1)>>3];h[7088]=a2;b9=64664+(b8*688&-1)|0;ca=b8;cb=aL;cc=a2;cd=b1}else{b1=+h[64672+(ax*688&-1)>>3];h[14]=b1;a2=+h[64664+(ax*688&-1)>>3];h[3305]=a2;ax=c[144]|0;aL=+h[64664+(ax*688&-1)>>3];h[7088]=aL;b9=64672+(ax*688&-1)|0;ca=ax;cb=a2;cc=aL;cd=b1}b1=+h[b9>>3];h[5263]=b1;if((e&2|0)==0){h[13]=+h[64664+(ca*688&-1)>>3];ce=cd;cf=cb;cg=64672+(ca*688&-1)|0}else{h[13]=+h[64672+(ca*688&-1)>>3];ce=cb;cf=cd;cg=64664+(ca*688&-1)|0}h[3304]=+h[cg>>3];h[7089]=cf;h[5264]=ce;cg=+g[3538]>90.0;h[69]=cg?cf:ce;h[97]=cg?cc:b1;do{if((a[38984]&1)==0&(c[10026]|0)==0){fw(b,d,0,0)}else{if(!((c[5094]|0)!=0&(c[14088]|0)==0)){break}fw(b,d,3,0)}}while(0);if((a[38984]&1)==0&(c[10026]|0)==-1){fw(b,d,2,0)}cg=(c[5094]|0)==0;if(cg){b1=+h[7076];ca=c[200]|0;cc=+h[64664+(ca*688&-1)>>3];ce=+h[91];cf=+h[96];cd=(+h[14]-cc)*ce+cf+-1.0;e=c[144]|0;cb=+h[64664+(e*688&-1)>>3];aL=+h[21];a2=+h[68];aJ=(+h[13]-cb)*aL+a2+-1.0;aR=+h[5279];a_=+h[2];aK=+h[12];a1=(b1-aR)*a_+aK+-1.0;aS=+h[403];b_=+h[391];aO=+h[395];aW=+h[399];a4=a1*aW;bX=+h[406];b2=+h[394];b$=+h[398];b0=+h[402];bB=a1*b0;a1=bX+cd*b2+aJ*b$+bB;b9=c[180]|0;aM=+(b9|0);ax=c[186]|0;b8=~~((aS+cd*b_+aJ*aO+a4)/(a1==0.0?1.0e-5:a1)*aM)+ax|0;a1=cf+ce*(+h[7089]-cc)+-1.0;aJ=a2+aL*(+h[7088]-cb)+-1.0;cd=bB+(bX+b2*a1+b$*aJ);bW=~~(aM*((a4+(aS+b_*a1+aO*aJ))/(cd==0.0?1.0e-5:cd)))+ax|0;cd=cf+ce*(+h[3305]-cc)+-1.0;aJ=a2+aL*(+h[3304]-cb)+-1.0;a1=bB+(bX+b2*cd+b$*aJ);bI=~~(aM*((a4+(aS+b_*cd+aO*aJ))/(a1==0.0?1.0e-5:a1)))+ax|0;c[7938]=(b8|0)<(bW|0)?b8:bW;c[7939]=(bW|0)>(bI|0)?bW:bI;ch=ca;ci=cc;cj=e;ck=cb;cl=b1;cm=ce;cn=cf;co=aL;cp=a2;cq=aR;cr=a_;cs=aK;ct=aS;cu=b_;cv=aO;cw=aW;cx=bX;cy=b2;cz=b$;cA=b0;cB=b9;cC=ax}else{ax=c[200]|0;b0=+h[64664+(ax*688&-1)>>3];b9=c[144]|0;b$=+h[64664+(b9*688&-1)>>3];b2=+h[7076];bX=+h[91];aW=+h[96];aO=(b0-b0)*bX+aW+-1.0;b_=+h[21];aS=+h[68];aK=(b$-b$)*b_+aS+-1.0;a_=+h[5279];aR=+h[2];a2=+h[12];aL=(b2-a_)*aR+a2+-1.0;cf=+h[403];ce=+h[391];b1=+h[395];cb=+h[399];cc=aL*cb;a1=+h[404];aJ=+h[392];cd=+h[396];a4=aL*+h[400];aM=+h[406];bB=+h[394];aU=+h[398];aT=+h[402];b6=aL*aT;aL=aM+aO*bB+aK*aU+b6;b7=aL==0.0?1.0e-5:aL;e=c[180]|0;aL=+(e|0);ca=c[186]|0;b3=+(c[40]|0);bI=c[46]|0;b5=aW+bX*(+h[64672+(ax*688&-1)>>3]-b0)+-1.0;aV=aS+b_*(+h[64672+(b9*688&-1)>>3]-b$)+-1.0;cD=b6+(aM+bB*b5+aU*aV);b6=cD==0.0?1.0e-5:cD;c[7938]=~~((cf+aO*ce+aK*b1+cc)/b7*aL)+ca;c[7939]=~~(aL*((cc+(cf+ce*b5+b1*aV))/b6))+ca;c[7940]=~~(b3*((a4+(a1+aJ*b5+cd*aV))/b6))+bI;c[7941]=~~((a1+aO*aJ+aK*cd+a4)/b7*b3)+bI;ch=ax;ci=b0;cj=b9;ck=b$;cl=b2;cm=bX;cn=aW;co=b_;cp=aS;cq=a_;cr=aR;cs=a2;ct=cf;cu=ce;cv=b1;cw=cb;cx=aM;cy=bB;cz=aU;cA=aT;cB=e;cC=ca}if((c[1119]|0)!=0){if(cg){fx(4560,aA,aB,109288);cg=(c[aA>>2]|0)+(((c[7939]|0)+(c[7938]|0)|0)/2&-1)|0;ca=(c[aB>>2]|0)+(c[7941]|0)|0;cE=cg;cF=ca+aa(c[aH+20>>2]|0,c[1100]|0)|0}else{if((c[64916+(ch*688&-1)>>2]&4|0)==0){cH=0}else{ca=~~(+((c[(c[3524]|0)+24>>2]|0)>>>0>>>0)*+h[65056+(ch*688&-1)>>3]*((a[65072+(ch*688&-1)|0]&1)!=0?-1.0:1.0));cH=(ca|0)<0?0:ca}aT=(ci-ci)*cm+cn+-1.0;aU=(ck-ck)*co+cp+-1.0;bB=(cl-cq)*cr+cs+-1.0;cs=bB*cw;cw=bB*cA;cA=cx+aT*cy+aU*cz+cw;cr=cA==0.0?1.0e-5:cA;cA=+(cB|0);cB=~~((+h[404]+aT*+h[392]+aU*+h[396]+bB*+h[400])/cr*+(c[40]|0));ca=c[46]|0;bB=cn+cm*(+h[64672+(ch*688&-1)>>3]-ci)+-1.0;ci=cp+co*(+h[64672+(cj*688&-1)>>3]-ck)+-1.0;ck=cw+(cx+cy*bB+cz*ci);fx(4560,aA,aB,109288);cE=(c[aA>>2]|0)+(((~~(cA*((cs+(ct+cu*bB+cv*ci))/(ck==0.0?1.0e-5:ck)))+~~((ct+aT*cu+aU*cv+cs)/cr*cA)|0)+(cC<<1)|0)/2&-1)|0;cF=~~(+(((ca+cH|0)+cB|0)+(c[aB>>2]|0)|0)+ +((c[aH+16>>2]|0)>>>0>>>0)*(+(c[1100]|0)+1.0+-.5))}aB=a[4600]&1;do{if(aB<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;cB=c[(c[3524]|0)+88>>2]|0;if((cB|0)==0){break}cO[cB&255](179864)}}while(0);a[37456]=aB;fn(4488,aH);ln(cE,cF,c[1119]|0,1,0,0,c[1120]|0);if((c[1122]|0)!=0){cM[c[aH+64>>2]&511](-2)}a[37456]=0}L10120:do{if((c[1183]|0)!=0){fx(4816,aE,aF,109288);cF=c[aH+16>>2]|0;cE=(c[aE>>2]|0)+cF|0;if((c[1166]|0)==0){cI=+((((c[7941]|0)-cF|0)+(c[aF>>2]|0)|0)>>>0>>>0)}else{cI=+(cF>>>0>>>0)+(+g[44]*+h[64672+((c[144]|0)*688&-1)>>3]+ +(c[aF>>2]|0))}cF=~~cI;cG(aD|0);aB=aC|0;cB=c[1183]|0;cH=aN(aD|0)|0;bw(aB|0,1024,cB|0,cH|0);do{if((c[1164]|0)!=0){cH=aH+72|0;if((cO[c[cH>>2]&255](-270)|0)==0){break}cB=c[1184]|0;if((c[1166]|0)==0){ln(cE,cF,aB,2,0,-270,cB)}else{ln(cE,cF,aB,0,0,-270,cB)}cB=c[cH>>2]|0;cO[cB&255](0);break L10120}}while(0);cB=c[1184]|0;if((c[1166]|0)==0){ln(cE,cF,aB,0,0,0,cB);break}else{ln(cE,cF,aB,0,2,0,cB);break}}}while(0);do{if(!bE){if((a[55536]&1)==0){break}if(!(a[31104]|0)){break}if(!((a[54016]|0)!=110&(c[13506]|0)==0)){break}dD(2)}}while(0);fO(c[10818]|0,0,3);aD=c[10822]|0;aC=c[(c[3524]|0)+92>>2]|0;if((aC|0)!=0){cK[aC&63](+h[3817])}if((aD|0)!=0){aC=aD;do{if((c[aC+56>>2]|0)==0){fK(aC+8|0,at,au,202256);fp(~~+h[at>>3],~~+h[au>>3],aC)}aC=c[aC>>2]|0;}while((aC|0)!=0)}fy(0);cM[c[(c[3524]|0)+168>>2]&511](2);do{if((a[38984]&1)!=0){if((a[46752]&1)==0|bE){break}gs();c[225]=0;c[11605]=0;c[7625]=0}}while(0);if((a[36229]&1)==0){c[9010]=0;aC=c[9002]|0;c[9006]=aC;au=aH+20|0;at=c[8712]|0;c[8990]=aa(at+1|0,-(c[au>>2]|0)|0);c[8986]=-(c[au>>2]|0);aD=~~(+((c[au>>2]|0)>>>0>>>0)*(+h[4526]+ +(at+2|0)));c[8998]=aD;at=aC+(c[au>>2]|0)|0;c[8994]=at;cJ=0;cL=aC;cP=aD;cQ=at}else{at=c[9002]|0;aD=-at|0;c[9010]=aD;c[9006]=0;aC=aH+20|0;c[8990]=c[aC>>2];au=c[8712]|0;c[8986]=aa(au+1|0,c[aC>>2]|0);aF=~~(+((c[aC>>2]|0)>>>0>>>0)*(+(au+2|0)+ +h[4526]));c[8994]=aF;au=at+(c[aC>>2]|0)|0;c[8998]=au;cJ=aD;cL=0;cP=au;cQ=aF}c[9018]=(cL+cJ|0)/2&-1;cJ=c[9031]|0;L10163:do{if((a[36120]&1)==0){cT=0;cU=cP;a0=7507}else{do{if((cJ|0)==0){a0=7493}else if((cJ|0)==3){a0=7508;break L10163}else{if((c[9032]|0)!=1){a0=7493;break}cL=c[6962]|0;if((cL|0)<=0){cV=0;cW=cP;cX=c[aH+16>>2]|0;break}aF=c[7938]|0;au=(c[7939]|0)-aF|0;aD=(au|0)/(c[9028]|0)&-1;aC=(aD|0)<1?1:aD;c[9024]=aC;aD=cL-1|0;cL=(aD+aC|0)/(aC|0)&-1;c[9014]=cL;aC=c[9339]|0;if((cL|0)>(aC|0)&(aC|0)>0){c[9014]=aC;cY=aC}else{cY=cL}cL=(aD+cY|0)/(cY|0)&-1;c[9024]=cL;c[9028]=(au|0)/(cL|0)&-1;aD=aa(au,cP);au=((aD|0)/(aa(cQ+cP|0,cL)|0)&-1)+aF|0;cI=+g[44]*+((c[aH+12>>2]|0)>>>0>>>0);cA=cI+ +(aa(c[9022]|0,cY)|0);aF=c[aH+16>>2]|0;cV=~~(cA+(+h[4487]+2.0)*+(aF>>>0>>>0));cW=au;cX=aF}}while(0);L10174:do{if((a0|0)==7493){if((c[9044]|0)==0){aB=c[aH+16>>2]|0;cZ=((c[7941]|0)-(c[aH+24>>2]|0)|0)-aB|0;c_=aB}else{aB=(c[aH+24>>2]|0)+(c[7940]|0)|0;cA=+((aB+aa(c[9014]|0,c[9022]|0)|0)>>>0>>>0);aB=c[aH+16>>2]|0;cZ=~~(cA+ +h[4487]*+(aB>>>0>>>0));c_=aB}aB=(cJ|0)!=0;cF=c[9032]|0;if(aB&(cF|0)==3){cV=cZ;cW=((c[aH+28>>2]|0)+(c[7939]|0)|0)+cP|0;cX=c_;break}if(aB&(cF|0)==2){cV=cZ;cW=(c[aH+20>>2]<<1)+cP|0;cX=c_;break}if((c[9045]|0)==0){cV=cZ;cW=((c[aH+28>>2]|0)+(c[7938]|0)|0)+cP|0;cX=c_;break}do{if((c[6598]|0)==3){if((cJ-1|0)>>>0>=2){break}cV=cZ;cW=(((c[7939]|0)-cQ|0)+(c[9028]|0)|0)-(c[aH+20>>2]<<1)|0;cX=c_;break L10174}}while(0);cF=(c[7939]|0)-cQ|0;cV=cZ;cW=cF-aa((c[9024]|0)-1|0,c[9028]|0)|0;cX=c_}}while(0);c[56]=~~(+(cV|0)- +h[4487]*+(cX>>>0>>>0));cT=cV;cU=cW;a0=7507}}while(0);if((a0|0)==7507){if((cJ|0)==3){a0=7508}else{c$=cT;c0=cU;c1=cQ;c2=cP}}if((a0|0)==7508){fK(36136,ar,as,128552);c$=~~+h[as>>3];c0=~~+h[ar>>3];c1=c[8994]|0;c2=c[8998]|0}ar=(c1+c0|0)+aa((c[9024]|0)-1|0,c[9028]|0)|0;c[9335]=ar;c1=c0-c2|0;c[9334]=c1;cA=+(c$|0);c2=aH+16|0;c[9337]=~~(cA+ +((c[c2>>2]|0)>>>0>>>0)*+h[4487]);c[9336]=c$-aa(c[9014]|0,c[9022]|0);if((c1|0)<0){c[9334]=0;c3=0}else{c3=c1}c1=a[36120]|0;if((c1&1)==0|(a[36288]|0)==0){c4=c$;c5=c1}else{if((c[9330]|0)==3&+h[4666]<0.0){fn(36272,aH)}else{fn(37320,aH)}c1=aH+96|0;if((c[c1>>2]&32|0)==0){c6=0.0}else{c6=(a8(36288,94)|0)==0?0.0:.51}ln((c3+ar|0)/2&-1,~~(+(c[9337]|0)-(c6*.5+.5)*+((c[c2>>2]|0)>>>0>>>0)),36288,1,0,0,c[9329]|0);do{if((c[c1>>2]&32|0)==0){c7=c6}else{if((a8(36288,95)|0)==0){c7=c6;break}c7=c6+.3}}while(0);h[4487]=c7+ +h[4487];c6=c7*+((c[c2>>2]|0)>>>0>>>0);c[9336]=~~(+(c[9336]|0)-c6);cM[c[aH+64>>2]&511](-2);c4=~~(cA-c6);c5=a[36120]|0}c1=c[9059]|0;do{if((c5&1)!=0&(c1|0)>-3){ar=c[9336]|0;if((c[9337]|0)==(ar|0)){c8=c4;break}c3=~~(+h[4527]*.5*+((c[c2>>2]|0)>>>0>>>0));c[9336]=ar-(c3<<1);ar=c4-c3|0;c3=aq;c[c3>>2]=c[9068];c[c3+4>>2]=c[36276>>2];c[c3+8>>2]=c[36280>>2];c[c3+12>>2]=c[36284>>2];do{if((c[9058]|0)!=0){c6=+h[4532];c3=c[(c[3524]|0)+92>>2]|0;if(c6<0.0){cK[c3&63](+h[3817]);break}else{cK[c3&63](c6);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[4531]);c3=c[(c[3524]|0)+64>>2]|0;if((c1|0)<-5){cM[c3&511](-2)}else{cM[c3&511](c1)}c3=c[3524]|0;do{if((a[36264]&1)==0){if((c[c3+96>>2]&1024|0)!=0){c9=c3;break}c[aq>>2]=1;c[aq+4>>2]=c1;a0=7533}else{a0=7533}}while(0);if((a0|0)==7533){fn(aq,c3);c9=c[3524]|0}c$=c[c9+172>>2]|0;if((c$|0)!=0){cM[c$&511](0)}c$=aH+56|0;cN[c[c$>>2]&255](c[9334]|0,c[9336]|0);as=aH+60|0;cN[c[as>>2]&255](c[9334]|0,c[9337]|0);cN[c[as>>2]&255](c[9335]|0,c[9337]|0);cN[c[as>>2]&255](c[9335]|0,c[9336]|0);cN[c[as>>2]&255](c[9334]|0,c[9336]|0);cP=c[(c[3524]|0)+172>>2]|0;if((cP|0)!=0){cM[cP&511](1)}cN[c[c$>>2]&255](c[9334]|0,~~(+(c[9337]|0)- +h[4487]*+((c[c2>>2]|0)>>>0>>>0)));cN[c[as>>2]&255](c[9335]|0,~~(+(c[9337]|0)- +h[4487]*+((c[c2>>2]|0)>>>0>>>0)));c8=ar}else{c8=c4}}while(0);do{if((a[38984]&1)!=0&(c[9744]|0)==0){if((a[46752]&1)==0|bE){break}cM[c[(c[3524]|0)+168>>2]&511](6);gv(b,d);cM[c[(c[3524]|0)+168>>2]&511](7)}}while(0);c4=c8-((c[9022]|0)/2&-1)|0;c[56]=c4;do{if((d|0)==1){if((c[b+252>>2]|0)!=1){break}if((a[55536]&1)==0){break}if(!((c[b+12>>2]|0)==352|(c[7662]|0)==1)){break}c8=c[m>>2]|0;aI(94544,137,1,c8|0)}}while(0);if((c[11690]|0)==0&(a[55536]&1)!=0&(a[30641]|0)==100){c8=c[6952]|0;if((c8|0)!=0){uu(c8)}c[6952]=0;c[18084]=0;c[12904]=0;da=1}else{da=0}L10258:do{if(!(bE|aP^1)){c8=ap;c2=ap|0;c9=ap+4|0;aq=aG;c1=aG+4|0;c5=C;as=aG+40|0;c$=as;cP=aG|0;cQ=aG+16|0;cU=aG+32|0;cT=C|0;cJ=C+4|0;cW=aH+64|0;cV=B;cX=B|0;c_=B+4|0;cZ=aG+24|0;cY=j|0;cF=j+8|0;aB=j+16|0;cE=j+32|0;aF=j+40|0;au=k|0;cL=k+8|0;aD=k+16|0;aC=k+32|0;at=k+40|0;aE=aG+40|0;cB=r|0;cH=r+8|0;ca=r+4|0;cC=aG+44|0;aA=aG+48|0;cj=q;ch=q|0;cg=q+4|0;e=ak|0;b9=ak+4|0;ax=ak+8|0;bI=L|0;bW=K;b8=L+7|0;bV=K|0;aY=K+4|0;bH=J;bP=J+4|0;bQ=J+8|0;bO=J+16|0;bN=J|0;bZ=G;bY=G|0;bT=G+4|0;bU=F;aX=F+4|0;a$=F+8|0;by=F+16|0;bS=F|0;bR=c0;bL=c4;bv=0;bM=b;bD=1;while(1){if((a[55536]&1)==0){db=0}else{db=(a[bM+56|0]&1)!=0}if((c[bM+8>>2]|0)==4){dc=bv;dd=bL;de=bR}else{cM[c[(c[3524]|0)+168>>2]&511](6);if((a[55536]&1)!=0&(c[7662]|0)==1){i5(bM)}do{if((a[36120]&1)==0){df=0}else{bK=bM+16|0;bb=c[bK>>2]|0;if((bb|0)==0){df=0;break}if((a[bb]|0)==0){df=0;break}if((a[bM+22|0]&1)!=0){df=0;break}if((c[9330]|0)==0){cM[c[cW>>2]&511](-2)}else{fn(37320,aH)}bb=a[bM+20|0]&1;do{if(bb<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;bJ=c[(c[3524]|0)+88>>2]|0;if((bJ|0)==0){break}cO[bJ&255](179864)}}while(0);a[37456]=bb;fz(bR,bL,c[bK>>2]|0);a[37456]=0;df=1}}while(0);bJ=bM+24|0;bz=bM+64|0;c[c8>>2]=c[bz>>2];c[c8+4>>2]=c[bz+4>>2];c[c8+8>>2]=c[bz+8>>2];c[c8+12>>2]=c[bz+12>>2];bz=c[bM+28>>2]|0;bA=bJ|0;do{if((c[bA>>2]|0)!=0){c6=+h[bM+48>>3];bC=c[(c[3524]|0)+92>>2]|0;if(c6<0.0){cK[bC&63](+h[3817]);break}else{cK[bC&63](c6);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[bM+40>>3]);bC=c[(c[3524]|0)+64>>2]|0;if((bz|0)<-5){cM[bC&511](-2)}else{cM[bC&511](bz)}bC=bM+56|0;bF=c[3524]|0;do{if((a[bC]&1)==0){if((c[bF+96>>2]&1024|0)!=0){break}c[c2>>2]=1;c[c9>>2]=bz;a0=7580}else{a0=7580}}while(0);if((a0|0)==7580){a0=0;fn(ap,bF)}bz=bM+12|0;bl=c[bz>>2]|0;L10301:do{if((bl|0)==64){if((a[46752]&1)==0){break}if((a[bM+238|0]&1)!=0){break}c[bM+32>>2]=-1;c[bA>>2]=1;do{if(df){if((a[bC]&1)!=0){fD(bM,bR,bL,-1);break}bs=c[13542]|0;bn=c[3524]|0;c[13542]=(c[bn+96>>2]&128|0)==0?55520:0;cM[c[bn+168>>2]&511](8);bn=(c[9018]|0)+bR|0;bm=c[13542]|0;if((bm|0)==0){a0=7690}else{bG=(c[bm>>2]|0)>(bn|0)&1;a5=(c[bm+4>>2]|0)<(bn|0)?bG|2:bG;bG=(c[bm+8>>2]|0)>(bL|0)?a5|4:a5;if((((c[bm+12>>2]|0)<(bL|0)?bG|8:bG)|0)==0){a0=7690}}if((a0|0)==7690){a0=0;cR[c[(c[3524]|0)+80>>2]&127](bn,bL,-1)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bs}}while(0);if((a[38984]&1)!=0){if((a[bM+236|0]&1)==0){break}}fE(bM,-1)}else if((bl|0)==225){do{if(df){if((a[bC]&1)!=0){fA(bM,bR,bL);break}bK=c[13542]|0;bb=c[3524]|0;c[13542]=(c[bb+96>>2]&128|0)==0?55520:0;cM[c[bb+168>>2]&511](8);bb=(c[9006]|0)+bR|0;c[M>>2]=(c[9010]|0)+bR;c[N>>2]=bL;c[O>>2]=bb;c[Q>>2]=bL;bb=c[3524]|0;if((fl(M,N,O,Q)|0)!=0){cN[c[bb+56>>2]&255](c[M>>2]|0,c[N>>2]|0);cN[c[bb+60>>2]&255](c[O>>2]|0,c[Q>>2]|0)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bK}}while(0);if((a[38984]&1)!=0){if((a[bM+236|0]&1)==0){break}}bK=bM+260|0;bb=c[bK>>2]|0;bs=c[bb+12>>2]|0;bn=c[(c[bb>>2]|0)+12>>2]|0;bb=c[bM+88>>2]|0;bG=c[bM+92>>2]|0;c6=+h[bM+104>>3];cA=+h[bM+112>>3];bm=a[bM+120|0]|0;uD(bI|0,bM+121|0,23);a5=c[bM+144>>2]|0;c7=+h[bM+152>>3];aZ=c[bM+160>>2]|0;cI=+h[bM+168>>3];cr=+h[bM+176>>3];bo=c[bM+184>>2]|0;uD(bW|0,b8|0,16);do{if((bb|0)!=0){az=c[(c[3524]|0)+92>>2]|0;if(cA<0.0){cK[az&63](+h[3817]);break}else{cK[az&63](cA);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](c6);bb=c[(c[3524]|0)+64>>2]|0;if((bG|0)<-5){cM[bb&511](-2)}else{cM[bb&511](bG)}bb=c[3524]|0;do{if((bm&1)==0){if((c[bb+96>>2]&1024|0)!=0){break}c[bV>>2]=1;c[aY>>2]=bG;a0=7713}else{a0=7713}}while(0);if((a0|0)==7713){a0=0;fn(K,bb)}bG=c[200]|0;h[64776+(bG*688&-1)>>3]=+((c[7939]|0)-(c[7938]|0)|0)/(+h[64672+(bG*688&-1)>>3]- +h[64664+(bG*688&-1)>>3]);c[12912]=bo;c[12910]=0;if(c7>0.0){uE(bH|0,0,40);c[bP>>2]=2;c[bQ>>2]=2;h[bO>>3]=c7;c[bN>>2]=aZ;fN(J,H,I,123984);h[6458]=cI;h[6457]=cr;c[12910]=~~+h[H>>3]}if((c[(c[bK>>2]|0)+8>>2]|0)<=0){break}bG=bM+80|0;bm=a5;az=0;while(1){a3=bs+(az<<6)|0;bt=a3|0;do{if((c[bt>>2]|0)==2){dg=bm}else{ay=bn+(az<<6)|0;if((c[ay>>2]|0)==2){dg=bm;break}do{if((c[bG>>2]|0)==-3){a9=~~+h[bs+(az<<6)+32>>3];bh=43296;while(1){dh=c[bh>>2]|0;if((dh|0)==0){a0=7724;break}if((c[dh+4>>2]|0)==(a9|0)){a0=7725;break}else{bh=dh|0}}do{if((a0|0)==7724){a0=0;uD(bI|0,51521,23);uh(-1,79128,(v=i,i=i+8|0,c[v>>2]=a9,v)|0);uD(bZ|0,b8|0,16);di=0;dj=90.0;dk=15.0;dl=0;dm=0.0;dn=1;dp=0;dq=1.0;dr=-2}else if((a0|0)==7725){a0=0;bh=c[dh+16>>2]|0;aQ=c[dh+20>>2]|0;c6=+h[dh+32>>3];cA=+h[dh+40>>3];bi=a[dh+48|0]|0;uD(bI|0,dh+49|0,23);a7=c[dh+72>>2]|0;cs=+h[dh+80>>3];bu=c[dh+88>>2]|0;cv=+h[dh+96>>3];aU=+h[dh+104>>3];br=c[dh+112>>2]|0;uD(bZ|0,b8|0,16);if((bh|0)==0){di=br;dj=aU;dk=cv;dl=bu;dm=cs;dn=a7;dp=bi;dq=c6;dr=aQ;break}bh=c[(c[3524]|0)+92>>2]|0;if(cA<0.0){cK[bh&63](+h[3817]);di=br;dj=aU;dk=cv;dl=bu;dm=cs;dn=a7;dp=bi;dq=c6;dr=aQ;break}else{cK[bh&63](cA);di=br;dj=aU;dk=cv;dl=bu;dm=cs;dn=a7;dp=bi;dq=c6;dr=aQ;break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](dq);a9=c[(c[3524]|0)+64>>2]|0;if((dr|0)<-5){cM[a9&511](-2)}else{cM[a9&511](dr)}a9=c[3524]|0;do{if((dp&1)==0){if((c[a9+96>>2]&1024|0)!=0){break}c[bY>>2]=1;c[bT>>2]=dr;a0=7735}else{a0=7735}}while(0);if((a0|0)==7735){a0=0;fn(G,a9)}c[12912]=di;c[12910]=0;if(dm<=0.0){ds=dn;break}uE(bU|0,0,40);c[aX>>2]=2;c[a$>>2]=2;h[by>>3]=dm;c[bS>>2]=dl;fN(F,D,E,123984);h[6458]=dk;h[6457]=dj;c[12910]=~~+h[D>>3];ds=dn}else{fL(bM,a3);ds=bm}}while(0);if((c[bt>>2]|0)!=0){dg=ds;break}if((c[ay>>2]|0)!=0){dg=ds;break}c6=+h[64664+((c[200]|0)*688&-1)>>3];cs=+h[91];cv=+h[96];aU=(+h[bs+(az<<6)+8>>3]-c6)*cs+cv+-1.0;cA=+h[64664+((c[144]|0)*688&-1)>>3];cu=+h[21];aT=+h[68];ct=(+h[bs+(az<<6)+16>>3]-cA)*cu+aT+-1.0;ck=+h[5279];ci=+h[2];bB=+h[12];cz=(+h[bs+(az<<6)+24>>3]-ck)*ci+bB+-1.0;cy=+h[403];cx=+h[391];cw=+h[395];co=+h[399];cp=+h[404];cm=+h[392];cn=+h[396];cq=+h[400];cl=+h[406];aM=+h[394];cb=+h[398];b1=+h[402];ce=cl+aU*aM+ct*cb+cz*b1;cf=ce==0.0?1.0e-5:ce;ce=+(c[180]|0);aQ=c[186]|0;a2=+(c[40]|0);bi=c[46]|0;aR=cv+cs*(+h[bn+(az<<6)+8>>3]-c6)+-1.0;c6=aT+cu*(+h[bn+(az<<6)+16>>3]-cA)+-1.0;cA=bB+ci*(+h[bn+(az<<6)+24>>3]-ck)+-1.0;ck=cl+aM*aR+cb*c6+b1*cA;b1=ck==0.0?1.0e-5:ck;fm(~~((cy+aU*cx+ct*cw+cz*co)/cf*ce)+aQ|0,~~((cp+aU*cm+ct*cn+cz*cq)/cf*a2)+bi|0,~~(ce*((cy+cx*aR+cw*c6+co*cA)/b1))+aQ|0,~~(a2*((cp+cm*aR+cn*c6+cq*cA)/b1))+bi|0,ds);dg=ds}}while(0);bt=az+1|0;if((bt|0)<(c[(c[bK>>2]|0)+8>>2]|0)){bm=dg;az=bt}else{break}}}else if((bl|0)==416){c[bM+228>>2]=1;f3(bM,0)}else if((bl|0)==432){c[bM+228>>2]=2;f3(bM,0)}else if((bl|0)==295|(bl|0)==279|(bl|0)==311|(bl|0)==102|(bl|0)==86|(bl|0)==118|(bl|0)==137|(bl|0)==169|(bl|0)==252|(bl|0)==474|(bl|0)==257|(bl|0)==457|(bl|0)==489|(bl|0)==18){if((a[46752]&1)==0){break}if((a[bM+238|0]&1)!=0){break}do{if(df){az=c[bM+32>>2]|0;if((a[bC]&1)!=0){fD(bM,bR,bL,az);break}bm=c[13542]|0;bK=c[3524]|0;c[13542]=(c[bK+96>>2]&128|0)==0?55520:0;cM[c[bK+168>>2]&511](8);bK=(c[9018]|0)+bR|0;bn=c[13542]|0;if((bn|0)==0){a0=7655}else{bs=(c[bn>>2]|0)>(bK|0)&1;bG=(c[bn+4>>2]|0)<(bK|0)?bs|2:bs;bs=(c[bn+8>>2]|0)>(bL|0)?bG|4:bG;if((((c[bn+12>>2]|0)<(bL|0)?bs|8:bs)|0)==0){a0=7655}}if((a0|0)==7655){a0=0;cR[c[(c[3524]|0)+80>>2]&127](bK,bL,az)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bm}}while(0);if((a[38984]&1)!=0){if((a[bM+236|0]&1)==0){break}}fE(bM,c[bM+32>>2]|0)}else if((bl|0)==368){if((a[38984]&1)!=0){if((a[bM+236|0]&1)==0){break}}bm=c[c[bM+224>>2]>>2]|0;az=c[(c[3524]|0)+92>>2]|0;if((az|0)!=0){cK[az&63](+h[3817])}if((bm|0)==0){break}else{dt=bm}do{L10414:do{if((c[dt+56>>2]|0)==99){cr=(+h[dt+24>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;cI=(+h[dt+32>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;c7=(+h[dt+40>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;b1=+h[406]+cr*+h[394]+cI*+h[398]+c7*+h[402];cA=b1==0.0?1.0e-5:b1;bm=~~((+h[403]+cr*+h[391]+cI*+h[395]+c7*+h[399])/cA*+(c[180]|0))+(c[186]|0)|0;az=~~((+h[404]+cr*+h[392]+cI*+h[396]+c7*+h[400])/cA*+(c[40]|0))+(c[46]|0)|0;do{if((c[5094]|0)!=0){bK=c[13542]|0;if((bK|0)==0){break}bs=(c[bK>>2]|0)>(bm|0)&1;bn=(c[bK+4>>2]|0)<(bm|0)?bs|2:bs;bs=(c[bK+8>>2]|0)>(az|0)?bn|4:bn;if((((c[bK+12>>2]|0)<(az|0)?bs|8:bs)|0)!=0){break L10414}}}while(0);fp(bm,az,dt)}}while(0);dt=c[dt>>2]|0;}while((dt|0)!=0)}else if((bl|0)==400){c[bM+228>>2]=0;f3(bM,0)}else if((bl|0)==352){if((a[46752]&1)==0){break}if((a[bM+238|0]&1)!=0){break}if(!((a[55536]&1)!=0&(c[7662]|0)!=1)){break}i5(bM);if(da){break}i3()}else if((bl|0)==153|(bl|0)==345|(bl|0)==33){if(df){ay=c[13542]|0;bs=c[3524]|0;c[13542]=(c[bs+96>>2]&128|0)==0?55520:0;cM[c[bs+168>>2]&511](8);bs=(c[9006]|0)+bR|0;c[al>>2]=(c[9010]|0)+bR;c[am>>2]=bL;c[an>>2]=bs;c[ao>>2]=bL;bs=c[3524]|0;if((fl(al,am,an,ao)|0)!=0){cN[c[bs+56>>2]&255](c[al>>2]|0,c[am>>2]|0);cN[c[bs+60>>2]&255](c[an>>2]|0,c[ao>>2]|0)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=ay}do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}if((a[bM+238|0]&1)==0){break L10301}}}while(0);ay=c[bM+260>>2]|0;bs=c[bM+64>>2]|0;do{if((a[55536]&1)==0){du=0}else{if((a[bM+239|0]&1)==0){du=0;break}du=+h[bM+72>>3]<0.0}}while(0);do{if(!((bs|0)!=3|du)){bK=c[bM+68>>2]|0;bn=(c[3524]|0)+144|0;if((c[bn>>2]|0)==0){break}c[e>>2]=3;c[b9>>2]=bK;h[ax>>3]=0.0;cM[c[bn>>2]&511](ak)}}while(0);if((ay|0)==0){break}else{dv=ay}do{bs=c[dv+12>>2]|0;bn=dv+8|0;if((c[bn>>2]|0)>0){bK=0;do{bG=bs+(bK<<6)|0;fL(bM,bG);a5=c[bG>>2]|0;L10451:do{if((a5|0)==0){cA=+h[bs+(bK<<6)+24>>3];c7=(+h[bs+(bK<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;cI=(+h[bs+(bK<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;cr=+h[5279];b1=+h[2];cq=+h[12];c6=(cA-cr)*b1+cq+-1.0;cn=+h[403]+c7*+h[391]+cI*+h[395];aR=+h[399];cm=+h[404]+c7*+h[392]+cI*+h[396];cp=+h[400];a2=+h[406]+c7*+h[394]+cI*+h[398];cI=+h[402];c7=a2+c6*cI;co=c7==0.0?1.0e-5:c7;c7=+(c[180]|0);bG=c[186]|0;aZ=~~((cn+c6*aR)/co*c7)+bG|0;cw=+(c[40]|0);bo=c[46]|0;bb=~~((cm+c6*cp)/co*cw)+bo|0;bt=c[34]|0;co=+h[64664+(bt*688&-1)>>3];c6=+h[64672+(bt*688&-1)>>3];if(co<c6){if(co>0.0|c6<0.0){a0=7602}else{a0=7601}}else{if(c6>0.0|co<0.0){a0=7602}else{a0=7601}}do{if((a0|0)==7601){a0=0;cx=(0.0-cr)*b1+cq+-1.0;cy=a2+cx*cI;ce=cy==0.0?1.0e-5:cy;dw=(cm+cx*cp)/ce;dx=(cn+cx*aR)/ce}else if((a0|0)==7602){a0=0;if(cA>0.0){if(co<0.0|co>cA){a0=7606}else{a0=7605}}else{if(co<cA|co>0.0){a0=7606}else{a0=7605}}if((a0|0)==7605){a0=0;ce=cq+b1*(co-cr)+-1.0;cx=a2+cI*ce;cy=cx==0.0?1.0e-5:cx;dw=(cm+cp*ce)/cy;dx=(cn+aR*ce)/cy;break}else if((a0|0)==7606){a0=0;cy=cq+b1*(c6-cr)+-1.0;ce=a2+cI*cy;cx=ce==0.0?1.0e-5:ce;dw=(cm+cp*cy)/cx;dx=(cn+aR*cy)/cx;break}}}while(0);a9=~~(cw*dw)+bo|0;bt=~~(c7*dx)+bG|0;c[8490]=bt;c[8488]=a9;c[ac>>2]=bt;c[ad>>2]=a9;c[ae>>2]=aZ;c[af>>2]=bb;a9=c[3524]|0;if((fl(ac,ad,ae,af)|0)!=0){cN[c[a9+56>>2]&255](c[ac>>2]|0,c[ad>>2]|0);cN[c[a9+60>>2]&255](c[ae>>2]|0,c[af>>2]|0)}c[8490]=aZ;c[8488]=bb}else if((a5|0)==1){a9=c[200]|0;aR=+h[64664+(a9*688&-1)>>3];cn=+h[64672+(a9*688&-1)>>3];cp=+h[bs+(bK<<6)+8>>3];if(aR<cn){if(cp<aR|cp>cn){break}}else{if(cp<cn|cp>aR){break}}a9=c[144]|0;cn=+h[64664+(a9*688&-1)>>3];cm=+h[64672+(a9*688&-1)>>3];cI=+h[bs+(bK<<6)+16>>3];if(cn<cm){if(cI<cn|cI>cm){break}}else{if(cI<cm|cI>cn){break}}a9=c[34]|0;cm=+h[64664+(a9*688&-1)>>3];a2=+h[64672+(a9*688&-1)>>3];if(cm<a2){if(cm>0.0|a2<0.0){a0=7624}else{a0=7619}}else{if(a2>0.0|cm<0.0){a0=7624}else{a0=7619}}do{if((a0|0)==7619){a0=0;cr=(cp-aR)*+h[91]+ +h[96]+-1.0;c6=(cI-cn)*+h[21]+ +h[68]+-1.0;b1=+h[5279];cq=+h[2];co=+h[12];cA=(0.0-b1)*cq+co+-1.0;cx=+h[403]+cr*+h[391]+c6*+h[395];cy=+h[399];ce=+h[404]+cr*+h[392]+c6*+h[396];cf=+h[400];cz=+h[406]+cr*+h[394]+c6*+h[398];c6=+h[402];cr=cz+cA*c6;ct=cr==0.0?1.0e-5:cr;cr=+(c[180]|0);a9=c[186]|0;bt=~~((cx+cA*cy)/ct*cr)+a9|0;aU=+(c[40]|0);a3=c[46]|0;bi=~~((ce+cA*cf)/ct*aU)+a3|0;ct=+h[bs+(bK<<6)+24>>3];if(ct>0.0){if(cm<=ct&cm>=0.0&cm!=0.0&cm!=ct){a0=7622}else{a0=7623}}else{if(cm<=0.0&cm>=ct&cm!=0.0&cm!=ct){a0=7622}else{a0=7623}}if((a0|0)==7622){a0=0;ct=(cm-b1)*cq+co+-1.0;cA=cz+ct*c6;ck=cA==0.0?1.0e-5:cA;dy=bi;dA=bt;dB=~~(aU*((ce+ct*cf)/ck))+a3|0;dC=~~(cr*((cx+ct*cy)/ck))+a9|0;break}else if((a0|0)==7623){a0=0;ck=(a2-b1)*cq+co+-1.0;co=cz+ck*c6;c6=co==0.0?1.0e-5:co;dy=bi;dA=bt;dB=~~(aU*((ce+ck*cf)/c6))+a3|0;dC=~~(cr*((cx+ck*cy)/c6))+a9|0;break}}else if((a0|0)==7624){a0=0;c6=+h[bs+(bK<<6)+24>>3];if(c6>0.0){if(cm<0.0|cm>c6){break L10451}if(a2<0.0|a2>c6){break L10451}}else{if(cm<c6|cm>0.0){break L10451}if(a2<c6|a2>0.0){break L10451}}c6=(cp-aR)*+h[91]+ +h[96]+-1.0;cy=(cI-cn)*+h[21]+ +h[68]+-1.0;ck=+h[5279];cx=+h[2];cr=+h[12];cf=(a2-ck)*cx+cr+-1.0;ce=+h[403]+c6*+h[391]+cy*+h[395];aU=+h[399];co=+h[404]+c6*+h[392]+cy*+h[396];cz=+h[400];cq=+h[406]+c6*+h[394]+cy*+h[398];cy=+h[402];c6=cq+cf*cy;b1=c6==0.0?1.0e-5:c6;c6=+(c[180]|0);a9=c[186]|0;ct=+(c[40]|0);a3=c[46]|0;cA=cr+cx*(cm-ck)+-1.0;ck=cq+cA*cy;cy=ck==0.0?1.0e-5:ck;dy=~~(ct*((co+cA*cz)/cy))+a3|0;dA=~~(c6*((ce+cA*aU)/cy))+a9|0;dB=~~((co+cf*cz)/b1*ct)+a3|0;dC=~~((ce+cf*aU)/b1*c6)+a9|0}}while(0);c[8490]=dA;c[8488]=dy;c[ag>>2]=dA;c[ah>>2]=dy;c[ai>>2]=dC;c[aj>>2]=dB;bb=c[3524]|0;if((fl(ag,ah,ai,aj)|0)!=0){cN[c[bb+56>>2]&255](c[ag>>2]|0,c[ah>>2]|0);cN[c[bb+60>>2]&255](c[ai>>2]|0,c[aj>>2]|0)}c[8490]=dC;c[8488]=dB}}while(0);bK=bK+1|0;}while((bK|0)<(c[bn>>2]|0))}dv=c[dv>>2]|0;}while((dv|0)!=0)}else if((bl|0)==51){if((a[46752]&1)==0){break}if((a[bM+238|0]&1)!=0){break}do{if(df){if((a[bC]&1)!=0){fA(bM,bR,bL);break}ay=c[13542]|0;bn=c[3524]|0;c[13542]=(c[bn+96>>2]&128|0)==0?55520:0;cM[c[bn+168>>2]&511](8);bn=(c[9006]|0)+bR|0;c[R>>2]=(c[9010]|0)+bR;c[U>>2]=bL;c[V>>2]=bn;c[W>>2]=bL;bn=c[3524]|0;if((fl(R,U,V,W)|0)!=0){cN[c[bn+56>>2]&255](c[R>>2]|0,c[U>>2]|0);cN[c[bn+60>>2]&255](c[V>>2]|0,c[W>>2]|0)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=ay}}while(0);if((a[38984]&1)==0){a0=7670}else{if((a[bM+236|0]&1)!=0){a0=7670}}do{if((a0|0)==7670){a0=0;if(db){fB(bM);break}else{fC(bM);break}}}while(0);do{if(df){ay=c[bM+32>>2]|0;if((a[bC]&1)!=0){fD(bM,bR,bL,ay);break}bn=c[13542]|0;bK=c[3524]|0;c[13542]=(c[bK+96>>2]&128|0)==0?55520:0;cM[c[bK+168>>2]&511](8);bK=(c[9018]|0)+bR|0;bs=c[13542]|0;if((bs|0)==0){a0=7678}else{a5=(c[bs>>2]|0)>(bK|0)&1;az=(c[bs+4>>2]|0)<(bK|0)?a5|2:a5;a5=(c[bs+8>>2]|0)>(bL|0)?az|4:az;if((((c[bs+12>>2]|0)<(bL|0)?a5|8:a5)|0)==0){a0=7678}}if((a0|0)==7678){a0=0;cR[c[(c[3524]|0)+80>>2]&127](bK,bL,ay)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bn}}while(0);if((a[38984]&1)!=0){if((a[bM+236|0]&1)==0){break}}fE(bM,c[bM+32>>2]|0)}else if((bl|0)==177|(bl|0)==184|(bl|0)==193|(bl|0)==209|(bl|0)==1){if((a[46752]&1)==0){break}if((a[bM+238|0]&1)!=0){break}do{if(df){if((a[bC]&1)!=0){fA(bM,bR,bL);break}bn=c[13542]|0;ay=c[3524]|0;c[13542]=(c[ay+96>>2]&128|0)==0?55520:0;cM[c[ay+168>>2]&511](8);ay=(c[9006]|0)+bR|0;c[X>>2]=(c[9010]|0)+bR;c[Y>>2]=bL;c[$>>2]=ay;c[ab>>2]=bL;ay=c[3524]|0;if((fl(X,Y,$,ab)|0)!=0){cN[c[ay+56>>2]&255](c[X>>2]|0,c[Y>>2]|0);cN[c[ay+60>>2]&255](c[$>>2]|0,c[ab>>2]|0)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bn}}while(0);if((a[38984]&1)!=0){if((a[bM+236|0]&1)==0){break}}if(db){fB(bM);break}else{fC(bM);break}}}while(0);do{if(df){bC=bv+1|0;if((bC|0)<(c[9014]|0)){dE=bC;dF=bL-(c[9022]|0)|0;dG=bR;break}else{dE=0;dF=c[56]|0;dG=(c[9028]|0)+bR|0;break}}else{dE=bv;dF=bL;dG=bR}}while(0);do{if((c[11690]|0)==0){dH=dE;dI=dF;dJ=dG}else{bC=c[bM+256>>2]|0;if((bC|0)==0){dH=dE;dI=dF;dJ=dG;break}bl=bJ;c[aq>>2]=c[bl>>2];c[aq+4>>2]=c[bl+4>>2];c[aq+8>>2]=c[bl+8>>2];c[aq+12>>2]=c[bl+12>>2];c[aq+16>>2]=c[bl+16>>2];c[aq+20>>2]=c[bl+20>>2];c[aq+24>>2]=c[bl+24>>2];c[aq+28>>2]=c[bl+28>>2];c[aq+32>>2]=c[bl+32>>2];c[aq+36>>2]=c[bl+36>>2];c[aq+40>>2]=c[bl+40>>2];c[aq+44>>2]=c[bl+44>>2];c[aq+48>>2]=c[bl+48>>2];c[aq+52>>2]=c[bl+52>>2];bl=(a[38984]&1)+(c[c1>>2]|0)|0;c[c1>>2]=bl;c[c5>>2]=c[c$>>2];c[c5+4>>2]=c[c$+4>>2];c[c5+8>>2]=c[c$+8>>2];c[c5+12>>2]=c[c$+12>>2];do{if((c[cP>>2]|0)!=0){cm=+h[cZ>>3];bA=c[(c[3524]|0)+92>>2]|0;if(cm<0.0){cK[bA&63](+h[3817]);break}else{cK[bA&63](cm);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[cQ>>3]);bA=c[(c[3524]|0)+64>>2]|0;if((bl|0)<-5){cM[bA&511](-2)}else{cM[bA&511](bl)}bA=c[3524]|0;do{if((a[cU]&1)==0){if((c[bA+96>>2]&1024|0)!=0){break}c[cT>>2]=1;c[cJ>>2]=bl;a0=7778}else{a0=7778}}while(0);if((a0|0)==7778){a0=0;fn(C,bA)}do{if((a[36120]&1)==0){dK=dE;dL=dF;dM=dG}else{bl=bM+16|0;bF=c[bl>>2]|0;if((bF|0)==0){dK=dE;dL=dF;dM=dG;break}if((a[bF]|0)==0){dK=dE;dL=dF;dM=dG;break}if((a[bM+22|0]&1)!=0){dK=dE;dL=dF;dM=dG;break}if((a[46752]&1)!=0){dK=dE;dL=dF;dM=dG;break}if((a[35888]&1)!=0){dK=dE;dL=dF;dM=dG;break}cM[c[cW>>2]&511](-2);fz(dG,dF,c[bl>>2]|0);c[cV>>2]=c[c$>>2];c[cV+4>>2]=c[c$+4>>2];c[cV+8>>2]=c[c$+8>>2];c[cV+12>>2]=c[c$+12>>2];bl=c[c1>>2]|0;do{if((c[cP>>2]|0)!=0){cm=+h[cZ>>3];bF=c[(c[3524]|0)+92>>2]|0;if(cm<0.0){cK[bF&63](+h[3817]);break}else{cK[bF&63](cm);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[cQ>>3]);bF=c[(c[3524]|0)+64>>2]|0;if((bl|0)<-5){cM[bF&511](-2)}else{cM[bF&511](bl)}bF=c[3524]|0;do{if((a[cU]&1)==0){if((c[bF+96>>2]&1024|0)!=0){break}c[cX>>2]=1;c[c_>>2]=bl;a0=7795}else{a0=7795}}while(0);if((a0|0)==7795){a0=0;fn(B,bF)}bl=c[bz>>2]|0;if((bl|0)==33|(bl|0)==1|(bl|0)==153|(bl|0)==345|(bl|0)==225|(bl|0)==177|(bl|0)==193|(bl|0)==209){bn=c[13542]|0;ay=c[3524]|0;c[13542]=(c[ay+96>>2]&128|0)==0?55520:0;cM[c[ay+168>>2]&511](8);ay=(c[9006]|0)+dG|0;c[x>>2]=(c[9010]|0)+dG;c[y>>2]=dF;c[z>>2]=ay;c[A>>2]=dF;ay=c[3524]|0;if((fl(x,y,z,A)|0)!=0){cN[c[ay+56>>2]&255](c[x>>2]|0,c[y>>2]|0);cN[c[ay+60>>2]&255](c[z>>2]|0,c[A>>2]|0)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bn}else if((bl|0)==295|(bl|0)==279|(bl|0)==311|(bl|0)==102|(bl|0)==86|(bl|0)==118|(bl|0)==169|(bl|0)==137|(bl|0)==252|(bl|0)==474|(bl|0)==257|(bl|0)==457|(bl|0)==489|(bl|0)==18){bn=c[bM+32>>2]|0;ay=c[13542]|0;bK=c[3524]|0;c[13542]=(c[bK+96>>2]&128|0)==0?55520:0;cM[c[bK+168>>2]&511](8);bK=(c[9018]|0)+dG|0;a5=c[13542]|0;if((a5|0)==0){a0=7802}else{bs=(c[a5>>2]|0)>(bK|0)&1;az=(c[a5+4>>2]|0)<(bK|0)?bs|2:bs;bs=(c[a5+8>>2]|0)>(dF|0)?az|4:az;if((((c[a5+12>>2]|0)<(dF|0)?bs|8:bs)|0)==0){a0=7802}}if((a0|0)==7802){a0=0;cR[c[(c[3524]|0)+80>>2]&127](bK,dF,bn)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=ay}else if((bl|0)==51){ay=c[13542]|0;bn=c[3524]|0;c[13542]=(c[bn+96>>2]&128|0)==0?55520:0;cM[c[bn+168>>2]&511](8);bn=(c[9006]|0)+dG|0;c[s>>2]=(c[9010]|0)+dG;c[t>>2]=dF;c[u>>2]=bn;c[w>>2]=dF;bn=c[3524]|0;if((fl(s,t,u,w)|0)!=0){cN[c[bn+56>>2]&255](c[s>>2]|0,c[t>>2]|0);cN[c[bn+60>>2]&255](c[u>>2]|0,c[w>>2]|0)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=ay}else if((bl|0)==64){bl=c[13542]|0;ay=c[3524]|0;c[13542]=(c[ay+96>>2]&128|0)==0?55520:0;cM[c[ay+168>>2]&511](8);ay=(c[9018]|0)+dG|0;bn=c[13542]|0;if((bn|0)==0){a0=7809}else{bK=(c[bn>>2]|0)>(ay|0)&1;bs=(c[bn+4>>2]|0)<(ay|0)?bK|2:bK;bK=(c[bn+8>>2]|0)>(dF|0)?bs|4:bs;if((((c[bn+12>>2]|0)<(dF|0)?bK|8:bK)|0)==0){a0=7809}}if((a0|0)==7809){a0=0;cR[c[(c[3524]|0)+80>>2]&127](ay,dF,-1)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bl}bl=dE+1|0;if((bl|0)<(c[9014]|0)){dK=bl;dL=dF-(c[9022]|0)|0;dM=dG;break}else{dK=0;dL=c[56]|0;dM=(c[9028]|0)+dG|0;break}}}while(0);bA=bM+22|0;bl=bM+32|0;ay=dM;bK=dL;bn=dK;bs=bC;while(1){do{if((a[35888]&1)==0){dN=bn;dO=bK;dP=ay}else{if((a[bs+8|0]|0)==0){dN=bn;dO=bK;dP=ay;break}do{if((a[36120]&1)!=0){if((a[bA]&1)!=0){break}cM[c[cW>>2]&511](-2);fz(ay,bK,bs+9|0)}}while(0);do{if(db){if((c[aE>>2]|0)!=6){a0=7839;break}cm=+h[bs+48>>3];a5=c[34]|0;az=(a[66852]&1)==0;do{if((a[64788+(a5*688&-1)|0]&1)==0){if(az){dQ=cm;break}if(cm>0.0){a2=+_(+cm);dQ=a2/+h[8358];break}else{dQ=+h[8341];break}}else{if(az){dQ=+Z(+(cm*+h[64800+(a5*688&-1)>>3]));break}if(+h[64792+(a5*688&-1)>>3]==+h[8357]){dQ=cm;break}dQ=cm*+h[64800+(a5*688&-1)>>3]/+h[8358]}}while(0);cm=+h[8341];do{if(cm<dQ){a2=+h[8342];if(a2<=dQ){dR=+((a[20668]|0)==112&1|0);break}cn=(dQ-cm)/(a2-cm);if((a[20668]|0)==112){dR=cn;break}dR=1.0-cn}else{dR=+((a[20668]|0)!=112&1|0)}}while(0);a5=(c[3524]|0)+144|0;if((c[a5>>2]|0)==0){break}c[cB>>2]=5;h[cH>>3]=dR;c[ca>>2]=0;cM[c[a5>>2]&511](r)}else{a0=7839}}while(0);do{if((a0|0)==7839){a0=0;cm=+h[aA>>3];a5=c[c1>>2]|0;if((a5|0)==-6){c[c1>>2]=0;a[cU]=1;dS=0}else{dS=a5}L10667:do{if((a[30080]&1)==0){a0=7855}else{if((a[35888]&1)==0){a0=7855;break}a5=dS+1|0;c[c1>>2]=a5;az=dS+2|0;bm=43280;while(1){dT=c[bm>>2]|0;if((dT|0)==0){break}if((c[dT+4>>2]|0)==(az|0)){a0=7846;break}else{bm=dT|0}}if((a0|0)==7846){a0=0;bm=(a[dT+40|0]&1)==0;dU=bm?1:c[dT+48>>2]|0;dV=bm?c[dT+12>>2]|0:c[dT+52>>2]|0;dW=+h[dT+56>>3];dX=a5;break}bm=c[8798]|0;bb=(bm|0)>0;aZ=az;L10676:while(1){bG=43264;while(1){dY=c[bG>>2]|0;if((dY|0)==0){break}if((c[dY+4>>2]|0)==(aZ|0)){break L10676}else{bG=dY|0}}bG=aZ-1|0;if(!((aZ|0)>(bm|0)&bb)){dU=1;dV=bG;dW=cm;dX=a5;break L10667}aZ=((bG|0)%(bm|0)&-1)+1|0}bm=c[dY+48>>2]|0;aZ=c[dY+52>>2]|0;cn=+h[dY+56>>3];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){dU=bm;dV=aZ;dW=cn;dX=a5;break}bb=(a[dY+40|0]&1)==0;dU=bb?1:bm;dV=bb?c[dY+12>>2]|0:aZ;dW=cn;dX=a5}}while(0);L10685:do{if((a0|0)==7855){a0=0;aZ=dS+1|0;c[c1>>2]=aZ;bb=c[8798]|0;bm=(bb|0)>0;az=dS+2|0;L10687:while(1){bG=43264;while(1){dZ=c[bG>>2]|0;if((dZ|0)==0){break}if((c[dZ+4>>2]|0)==(az|0)){break L10687}else{bG=dZ|0}}bG=az-1|0;if(!((az|0)>(bb|0)&bm)){dU=1;dV=bG;dW=cm;dX=aZ;break L10685}az=((bG|0)%(bb|0)&-1)+1|0}bb=c[dZ+48>>2]|0;az=c[dZ+52>>2]|0;cn=+h[dZ+56>>3];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){dU=bb;dV=az;dW=cn;dX=aZ;break}bm=(a[dZ+40|0]&1)==0;dU=bm?1:bb;dV=bm?c[dZ+12>>2]|0:az;dW=cn;dX=aZ}}while(0);c[aE>>2]=dU;c[cC>>2]=dV;h[aA>>3]=dW;c[cj>>2]=c[c$>>2];c[cj+4>>2]=c[c$+4>>2];c[cj+8>>2]=c[c$+8>>2];c[cj+12>>2]=c[c$+12>>2];do{if((c[cP>>2]|0)!=0){cm=+h[cZ>>3];az=c[(c[3524]|0)+92>>2]|0;if(cm<0.0){cK[az&63](+h[3817]);break}else{cK[az&63](cm);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[cQ>>3]);az=c[(c[3524]|0)+64>>2]|0;if((dX|0)<-5){cM[az&511](-2)}else{cM[az&511](dX)}az=c[3524]|0;if((a[cU]&1)==0){if((c[az+96>>2]&1024|0)!=0){break}c[ch>>2]=1;c[cg>>2]=dX}fn(q,az)}}while(0);if((a[36120]&1)==0){dN=bn;dO=bK;dP=ay;break}if((a[bA]&1)!=0){dN=bn;dO=bK;dP=ay;break}az=c[bz>>2]|0;if((az|0)==33|(az|0)==1|(az|0)==51|(az|0)==153|(az|0)==345|(az|0)==225|(az|0)==177|(az|0)==193|(az|0)==209|(az|0)==352){bm=c[13542]|0;bb=c[3524]|0;c[13542]=(c[bb+96>>2]&128|0)==0?55520:0;cM[c[bb+168>>2]&511](8);bb=(c[9006]|0)+ay|0;c[l>>2]=(c[9010]|0)+ay;c[n>>2]=bK;c[o>>2]=bb;c[p>>2]=bK;bb=c[3524]|0;if((fl(l,n,o,p)|0)!=0){cN[c[bb+56>>2]&255](c[l>>2]|0,c[n>>2]|0);cN[c[bb+60>>2]&255](c[o>>2]|0,c[p>>2]|0)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bm}else if((az|0)==295|(az|0)==279|(az|0)==311|(az|0)==102|(az|0)==86|(az|0)==118|(az|0)==169|(az|0)==137|(az|0)==252|(az|0)==474|(az|0)==257|(az|0)==457|(az|0)==489|(az|0)==18){bm=c[bl>>2]|0;bb=c[13542]|0;a5=c[3524]|0;c[13542]=(c[a5+96>>2]&128|0)==0?55520:0;cM[c[a5+168>>2]&511](8);a5=(c[9018]|0)+ay|0;bG=c[13542]|0;if((bG|0)==0){a0=7882}else{bo=(c[bG>>2]|0)>(a5|0)&1;a9=(c[bG+4>>2]|0)<(a5|0)?bo|2:bo;bo=(c[bG+8>>2]|0)>(bK|0)?a9|4:a9;if((((c[bG+12>>2]|0)<(bK|0)?bo|8:bo)|0)==0){a0=7882}}if((a0|0)==7882){a0=0;cR[c[(c[3524]|0)+80>>2]&127](a5,bK,bm)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=bb}else if((az|0)==64){az=c[13542]|0;bb=c[3524]|0;c[13542]=(c[bb+96>>2]&128|0)==0?55520:0;cM[c[bb+168>>2]&511](8);bb=(c[9018]|0)+ay|0;bm=c[13542]|0;if((bm|0)==0){a0=7886}else{a5=(c[bm>>2]|0)>(bb|0)&1;bo=(c[bm+4>>2]|0)<(bb|0)?a5|2:a5;a5=(c[bm+8>>2]|0)>(bK|0)?bo|4:bo;if((((c[bm+12>>2]|0)<(bK|0)?a5|8:a5)|0)==0){a0=7886}}if((a0|0)==7886){a0=0;cR[c[(c[3524]|0)+80>>2]&127](bb,bK,-1)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=az}az=bn+1|0;if((az|0)<(c[9014]|0)){dN=az;dO=bK-(c[9022]|0)|0;dP=ay;break}else{dN=0;dO=c[56]|0;dP=(c[9028]|0)+ay|0;break}}}while(0);bF=c[bz>>2]|0;do{if((bF|0)==153|(bF|0)==345|(bF|0)==225|(bF|0)==33){if((c[11690]&2|0)==0){fG(bs,aG);break}az=bs+44|0;if((c[az>>2]|0)<=0){break}bb=bs+4|0;a5=0;do{bm=c[bb>>2]|0;bo=bm+(a5<<6)+24|0;cm=+h[bo>>3];cn=(+h[bm+(a5<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;a2=(+h[bm+(a5<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;cI=+h[5279];aR=+h[2];cp=+h[12];c7=(cm-cI)*aR+cp+-1.0;cw=+h[403]+cn*+h[391]+a2*+h[395];c6=+h[399];b1=+h[404]+cn*+h[392]+a2*+h[396];aU=+h[400];cf=+h[405]+cn*+h[393]+a2*+h[397];ce=+h[401];ct=+h[406]+cn*+h[394]+a2*+h[398];a2=+h[402];cn=ct+c7*a2;cz=cn==0.0?1.0e-5:cn;h[cY>>3]=(cw+c7*c6)/cz;h[cF>>3]=(b1+c7*aU)/cz;h[aB>>3]=(cf+c7*ce)/cz;h[cE>>3]=cm;c[aF>>2]=0;cm=(0.0-cI)*aR+cp+-1.0;cp=ct+cm*a2;a2=cp==0.0?1.0e-5:cp;h[au>>3]=(cw+cm*c6)/a2;h[cL>>3]=(b1+cm*aU)/a2;h[aD>>3]=(cf+cm*ce)/a2;h[aC>>3]=0.0;c[at>>2]=0;h[aC>>3]=+h[bo>>3];do{if((a[38984]&1)==0){a0=7898}else{if((a[46752]&1)==0){a0=7898;break}gt(j,k,aG)}}while(0);if((a0|0)==7898){a0=0;un(j,k,aG,as)}a5=a5+1|0;}while((a5|0)<(c[az>>2]|0))}else if((bF|0)==177|(bF|0)==193|(bF|0)==209|(bF|0)==1|(bF|0)==352){fF(bs,aG)}else if((bF|0)==51){fF(bs,aG);a0=7903}else if((bF|0)==295|(bF|0)==279|(bF|0)==311|(bF|0)==102|(bF|0)==86|(bF|0)==118|(bF|0)==169|(bF|0)==137|(bF|0)==252|(bF|0)==257|(bF|0)==457|(bF|0)==489|(bF|0)==64|(bF|0)==18){a0=7903}}while(0);if((a0|0)==7903){a0=0;fG(bs,aG)}bF=c[bs>>2]|0;if((bF|0)==0){dH=dN;dI=dO;dJ=dP;break}else{ay=dP;bK=dO;bn=dN;bs=bF}}}}while(0);cM[c[(c[3524]|0)+168>>2]&511](7);dc=dH;dd=dI;de=dJ}if((bD|0)>=(d|0)){break L10258}bR=de;bL=dd;bv=dc;bM=c[bM>>2]|0;bD=bD+1|0}}}while(0);if(da){i3()}da=a[38984]|0;do{if((da&1)!=0&(c[9744]|0)==1){if((a[46752]&1)==0|bE){d_=da;break}cM[c[(c[3524]|0)+168>>2]&511](6);gv(b,d);cM[c[(c[3524]|0)+168>>2]&511](7);d_=a[38984]|0}else{d_=da}}while(0);da=c[10026]|0;do{if((d_&1)!=0|(da|0)==1){fw(b,d,0,1)}else{if((da|0)!=-1){break}fw(b,d,1,1)}}while(0);if((c[5094]|0)!=0&(c[14088]|0)==1){fw(b,d,3,1)}do{if(!bE){if((a[55536]&1)==0){break}if(!(a[31104]|0)){break}if(!((a[54016]|0)!=110&(c[13506]|0)==1)){break}dD(2)}}while(0);fO(c[10818]|0,1,3);bE=c[10822]|0;d=c[(c[3524]|0)+92>>2]|0;if((d|0)!=0){cK[d&63](+h[3817])}if((bE|0)!=0){d=bE;do{if((c[d+56>>2]|0)==1){fK(d+8|0,av,aw,202256);fp(~~+h[av>>3],~~+h[aw>>3],d)}d=c[d>>2]|0;}while((d|0)!=0)}fy(1);d=c[200]|0;dW=+h[64664+(d*688&-1)>>3];aw=c[144]|0;dR=+h[64664+(aw*688&-1)>>3];dQ=+h[91];dx=+h[96];dw=(dW-dW)*dQ+dx+-1.0;dj=+h[21];dk=+h[68];dm=(dR-dR)*dj+dk+-1.0;dq=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;a2=+h[403];ce=+h[391];cm=a2+dw*ce;cf=+h[395];aU=dm*cf;b1=dq*+h[399];c6=+h[404];cw=+h[392];cp=c6+dw*cw;ct=+h[396];aR=dm*ct;cI=dq*+h[400];cz=+h[406];c7=+h[394];cn=cz+dw*c7;dw=+h[398];co=dm*dw;dm=dq*+h[402];dq=cn+co+dm;cy=dq==0.0?1.0e-5:dq;dq=+(c[180]|0);av=c[186]|0;bE=~~((cm+aU+b1)/cy*dq)+av|0;cA=+(c[40]|0);b=c[46]|0;da=~~((cp+aR+cI)/cy*cA)+b|0;c[18064]=bE;c[18062]=da;cy=(+h[64672+(d*688&-1)>>3]-dW)*dQ+dx+-1.0;dx=cz+cy*c7+co+dm;co=dx==0.0?1.0e-5:dx;c[18060]=(~~((a2+cy*ce+aU+b1)/co*dq)+av|0)-bE;c[18058]=(~~((c6+cy*cw+aR+cI)/co*cA)+b|0)-da;co=(+h[64672+(aw*688&-1)>>3]-dR)*dj+dk+-1.0;dk=cn+co*dw+dm;dm=dk==0.0?1.0e-5:dk;c[18056]=(~~((cm+co*cf+b1)/dm*dq)+av|0)-bE;c[18054]=(~~((cp+co*ct+cI)/dm*cA)+b|0)-da;do{if(a[31104]|0){da=c[(c[3524]|0)+140>>2]|0;if((da|0)==0){break}cS[da&511]()}}while(0);lh();if((a[38984]&1)==0){i=f;return}if((a[46752]&1)==0){i=f;return}uu(c[7628]|0);c[7628]=0;c[7624]=0;c[7625]=0;uu(c[11608]|0);c[11608]=0;c[11604]=0;c[11605]=0;uu(c[228]|0);c[228]=0;c[224]=0;c[225]=0;i=f;return}function fw(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Z=0,_=0,$=0,aa=0.0,ab=0,ac=0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0,ai=0.0,aj=0.0,ak=0.0,al=0.0,am=0.0,an=0.0,ao=0.0,ap=0.0,aq=0.0,ar=0.0,as=0.0,at=0.0,au=0.0,av=0.0,aw=0.0,ax=0.0,ay=0.0,az=0.0,aA=0.0,aB=0.0,aC=0.0,aD=0.0,aE=0,aF=0,aG=0.0,aH=0.0,aI=0.0,aJ=0.0,aK=0.0,aL=0.0,aM=0.0,aN=0.0,aO=0.0,aP=0.0,aQ=0.0,aR=0.0,aS=0.0,aT=0.0,aU=0.0,aV=0.0,aW=0.0,aX=0.0,aY=0.0,aZ=0.0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0.0,bn=0.0,bo=0,bp=0.0,bq=0.0,br=0.0,bs=0,bt=0.0,bu=0.0,bv=0.0,bw=0,bx=0,by=0,bz=0.0,bA=0,bB=0,bC=0,bD=0.0,bE=0.0,bF=0.0,bG=0,bH=0.0,bI=0.0,bJ=0,bK=0,bL=0.0,bM=0.0,bN=0.0,bO=0,bP=0,bQ=0,bR=0,bS=0,bT=0.0,bU=0.0,bV=0.0,bW=0.0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0;j=i;i=i+1392|0;k=j|0;l=j+16|0;m=j+32|0;n=j+80|0;o=j+128|0;p=j+176|0;q=j+224|0;r=j+272|0;s=j+320|0;t=j+368|0;u=j+416|0;v=j+464|0;w=j+512|0;x=j+560|0;y=j+608|0;z=j+640|0;A=j+672|0;B=j+720|0;C=j+768|0;D=j+816|0;E=j+864|0;F=j+912|0;G=j+960|0;H=j+1008|0;I=j+1056|0;J=j+1064|0;K=j+1072|0;L=j+1080|0;M=j+1088|0;N=j+1136|0;Q=j+1184|0;R=j+1232|0;S=j+1280|0;T=j+1328|0;U=j+1376|0;V=j+1384|0;W=c[3524]|0;X=c[13542]|0;Z=c[11692]|0;_=(Z|0)!=0;L10803:do{if(_&(c[5094]|0)!=0){if((c[14088]|0)!=(f|0)){break}$=l;c[$>>2]=c[14084];c[$+4>>2]=c[56340>>2];c[$+8>>2]=c[56344>>2];c[$+12>>2]=c[56348>>2];$=c[14075]|0;do{if((c[14074]|0)!=0){aa=+h[7040];ab=c[W+92>>2]|0;if(aa<0.0){cK[ab&63](+h[3817]);break}else{cK[ab&63](aa);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);ab=c[(c[3524]|0)+64>>2]|0;if(($|0)<-5){cM[ab&511](-2)}else{cM[ab&511]($)}ab=c[3524]|0;do{if((a[56328]&1)==0){if((c[ab+96>>2]&1024|0)!=0){break}c[l>>2]=1;c[l+4>>2]=$;ac=7953}else{ac=7953}}while(0);if((ac|0)==7953){fn(l,ab)}do{if((c[11692]&15|0)==15){$=c[(c[3524]|0)+172>>2]|0;if(($|0)==0){break}cM[$&511](0)}}while(0);aa=(+h[14]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;ad=(+h[13]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ae=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;af=+h[406]+aa*+h[394]+ad*+h[398]+ae*+h[402];ag=af==0.0?1.0e-5:af;cN[c[(c[3524]|0)+56>>2]&255](~~((+h[403]+aa*+h[391]+ad*+h[395]+ae*+h[399])/ag*+(c[180]|0))+(c[186]|0)|0,~~((+h[404]+aa*+h[392]+ad*+h[396]+ae*+h[400])/ag*+(c[40]|0))+(c[46]|0)|0);ag=(+h[7089]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;ae=(+h[7088]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ad=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;aa=+h[406]+ag*+h[394]+ae*+h[398]+ad*+h[402];af=aa==0.0?1.0e-5:aa;ab=~~((+h[403]+ag*+h[391]+ae*+h[395]+ad*+h[399])/af*+(c[180]|0))+(c[186]|0)|0;$=~~((+h[404]+ag*+h[392]+ae*+h[396]+ad*+h[400])/af*+(c[40]|0))+(c[46]|0)|0;ah=c[3524]|0;if((c[11692]&2|0)==0){cN[c[ah+56>>2]&255](ab,$)}else{cN[c[ah+60>>2]&255](ab,$)}af=(+h[3305]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;ad=(+h[3304]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ae=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;ag=+h[406]+af*+h[394]+ad*+h[398]+ae*+h[402];aa=ag==0.0?1.0e-5:ag;$=~~((+h[403]+af*+h[391]+ad*+h[395]+ae*+h[399])/aa*+(c[180]|0))+(c[186]|0)|0;ab=~~((+h[404]+af*+h[392]+ad*+h[396]+ae*+h[400])/aa*+(c[40]|0))+(c[46]|0)|0;ah=c[3524]|0;if((c[11692]&8|0)==0){cN[c[ah+56>>2]&255]($,ab)}else{cN[c[ah+60>>2]&255]($,ab)}aa=(+h[5264]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;ae=(+h[5263]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ad=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;af=+h[406]+aa*+h[394]+ae*+h[398]+ad*+h[402];ag=af==0.0?1.0e-5:af;ab=~~((+h[403]+aa*+h[391]+ae*+h[395]+ad*+h[399])/ag*+(c[180]|0))+(c[186]|0)|0;$=~~((+h[404]+aa*+h[392]+ae*+h[396]+ad*+h[400])/ag*+(c[40]|0))+(c[46]|0)|0;ah=c[3524]|0;if((c[11692]&4|0)==0){cN[c[ah+56>>2]&255](ab,$)}else{cN[c[ah+60>>2]&255](ab,$)}ag=(+h[14]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;ad=(+h[13]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ae=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;aa=+h[406]+ag*+h[394]+ad*+h[398]+ae*+h[402];af=aa==0.0?1.0e-5:aa;$=~~((+h[403]+ag*+h[391]+ad*+h[395]+ae*+h[399])/af*+(c[180]|0))+(c[186]|0)|0;ab=~~((+h[404]+ag*+h[392]+ad*+h[396]+ae*+h[400])/af*+(c[40]|0))+(c[46]|0)|0;ah=c[3524]|0;if((c[11692]&1|0)==0){cN[c[ah+56>>2]&255]($,ab)}else{cN[c[ah+60>>2]&255]($,ab)}if((c[11692]&15|0)!=15){break}ab=c[(c[3524]|0)+172>>2]|0;if((ab|0)==0){break}cM[ab&511](1)}else{if(!_){break}af=+h[7076];ae=+h[64664+((c[200]|0)*688&-1)>>3];ad=+h[91];ag=+h[96];aa=(+h[14]-ae)*ad+ag+-1.0;ai=+h[64664+((c[144]|0)*688&-1)>>3];aj=+h[21];ak=+h[68];al=(+h[13]-ai)*aj+ak+-1.0;am=(af- +h[5279])*+h[2]+ +h[12]+-1.0;an=+h[403];ao=+h[391];ap=+h[395];aq=am*+h[399];ar=+h[404];as=+h[392];at=+h[396];au=am*+h[400];av=+h[405];aw=+h[393];ax=+h[397];ay=am*+h[401];az=+h[406];aA=+h[394];aB=+h[398];aC=am*+h[402];am=az+aa*aA+al*aB+aC;aD=am==0.0?1.0e-5:am;h[m>>3]=(an+aa*ao+al*ap+aq)/aD;h[m+8>>3]=(ar+aa*as+al*at+au)/aD;h[m+16>>3]=(av+aa*aw+al*ax+ay)/aD;h[m+32>>3]=af;c[m+40>>2]=0;aD=(+h[7089]-ae)*ad+ag+-1.0;al=(+h[7088]-ai)*aj+ak+-1.0;aa=az+aD*aA+al*aB+aC;am=aa==0.0?1.0e-5:aa;h[n>>3]=(an+aD*ao+al*ap+aq)/am;h[n+8>>3]=(ar+aD*as+al*at+au)/am;h[n+16>>3]=(av+aD*aw+al*ax+ay)/am;h[n+32>>3]=af;c[n+40>>2]=0;am=(+h[3305]-ae)*ad+ag+-1.0;al=(+h[3304]-ai)*aj+ak+-1.0;aD=az+am*aA+al*aB+aC;aa=aD==0.0?1.0e-5:aD;h[o>>3]=(an+am*ao+al*ap+aq)/aa;h[o+8>>3]=(ar+am*as+al*at+au)/aa;h[o+16>>3]=(av+am*aw+al*ax+ay)/aa;h[o+32>>3]=af;c[o+40>>2]=0;aa=(+h[5264]-ae)*ad+ag+-1.0;ag=(+h[5263]-ai)*aj+ak+-1.0;ak=az+aa*aA+ag*aB+aC;aC=ak==0.0?1.0e-5:ak;h[p>>3]=(an+aa*ao+ag*ap+aq)/aC;h[p+8>>3]=(ar+aa*as+ag*at+au)/aC;h[p+16>>3]=(av+aa*aw+ag*ax+ay)/aC;h[p+32>>3]=af;c[p+40>>2]=0;ab=(e|0)!=2;do{if(ab){L10808:do{if((Z&4|0)!=0){do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(o,p,56296);break L10808}}while(0);un(o,p,56296,56336)}}while(0);L10815:do{if((c[11692]&1|0)!=0){do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(m,p,56296);break L10815}}while(0);un(m,p,56296,56336)}}while(0);if((e|0)==1){aE=0;break}aF=c[11692]|0;ac=7986}else{aF=Z;ac=7986}}while(0);L10823:do{if((ac|0)==7986){L10825:do{if((aF&2|0)!=0){do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(m,n,56296);break L10825}}while(0);un(m,n,56296,56336)}}while(0);if((c[11692]&8|0)==0){aE=1;break}do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(o,n,56296);aE=1;break L10823}}while(0);un(o,n,56296,56336);aE=1}}while(0);do{if((a[46752]&1)==0){if((c[11690]&2|0)!=0){break}if((c[7662]|0)!=1){break L10803}if((cy(30632,213264)|0)==0){break L10803}}}while(0);af=+h[5279];$=c[200]|0;aC=+h[64664+($*688&-1)>>3];ay=+h[14]-aC;ax=+h[91];ag=+h[96];aw=ay*ax+ag+-1.0;ah=c[144]|0;aa=+h[64664+(ah*688&-1)>>3];av=+h[13]-aa;au=+h[21];at=+h[68];as=av*au+at+-1.0;ar=+h[2];aq=+h[12];ap=(af-af)*ar+aq+-1.0;ao=+h[403];an=+h[391];ak=+h[395];aB=ao+aw*an+as*ak;aA=+h[399];az=ap*aA;aj=+h[404];ai=+h[392];ad=+h[396];ae=aj+aw*ai+as*ad;al=+h[400];am=ap*al;aD=+h[405];aG=+h[393];aH=+h[397];aI=aD+aw*aG+as*aH;aJ=+h[401];aK=ap*aJ;aL=+h[406];aM=+h[394];aN=+h[398];aO=aL+aw*aM+as*aN;as=+h[402];aw=ap*as;ap=aO+aw;aP=ap==0.0?1.0e-5:ap;h[q>>3]=(aB+az)/aP;h[q+8>>3]=(ae+am)/aP;h[q+16>>3]=(aI+aK)/aP;h[q+32>>3]=af;c[q+40>>2]=0;aP=+h[7089]-aC;ap=aP*ax+ag+-1.0;aQ=+h[7088]-aa;aR=aQ*au+at+-1.0;aS=ao+ap*an+aR*ak;aT=aj+ap*ai+aR*ad;aU=aD+ap*aG+aR*aH;aV=aL+ap*aM+aR*aN;aR=aV+aw;ap=aR==0.0?1.0e-5:aR;h[r>>3]=(aS+az)/ap;h[r+8>>3]=(aT+am)/ap;h[r+16>>3]=(aU+aK)/ap;h[r+32>>3]=af;c[r+40>>2]=0;ap=(+h[3305]-aC)*ax+ag+-1.0;aR=(+h[3304]-aa)*au+at+-1.0;aW=ao+ap*an+aR*ak;aX=aj+ap*ai+aR*ad;aY=aD+ap*aG+aR*aH;aZ=aL+ap*aM+aR*aN;aR=aZ+aw;ap=aR==0.0?1.0e-5:aR;h[s>>3]=(aW+az)/ap;h[s+8>>3]=(aX+am)/ap;h[s+16>>3]=(aY+aK)/ap;h[s+32>>3]=af;c[s+40>>2]=0;ap=(+h[5264]-aC)*ax+ag+-1.0;ag=(+h[5263]-aa)*au+at+-1.0;at=ao+ap*an+ag*ak;ak=aj+ap*ai+ag*ad;ad=aD+ap*aG+ag*aH;aH=aL+ap*aM+ag*aN;aN=aH+aw;aw=aN==0.0?1.0e-5:aN;h[t>>3]=(at+az)/aw;h[t+8>>3]=(ak+am)/aw;h[t+16>>3]=(ad+aK)/aw;h[t+32>>3]=af;c[t+40>>2]=0;aw=+h[6915];aK=(aw-af)*ar+aq+-1.0;am=aK*aA;az=aK*al;aN=aK*aJ;ag=aK*as;aK=aO+ag;aM=aK==0.0?1.0e-5:aK;h[u>>3]=(aB+am)/aM;h[u+8>>3]=(ae+az)/aM;h[u+16>>3]=(aI+aN)/aM;h[u+32>>3]=aw;c[u+40>>2]=0;aM=aV+ag;aV=aM==0.0?1.0e-5:aM;h[v>>3]=(aS+am)/aV;h[v+8>>3]=(aT+az)/aV;h[v+16>>3]=(aU+aN)/aV;h[v+32>>3]=aw;c[v+40>>2]=0;aV=aZ+ag;aZ=aV==0.0?1.0e-5:aV;h[w>>3]=(aW+am)/aZ;h[w+8>>3]=(aX+az)/aZ;h[w+16>>3]=(aY+aN)/aZ;h[w+32>>3]=aw;c[w+40>>2]=0;aZ=aH+ag;ag=aZ==0.0?1.0e-5:aZ;h[x>>3]=(at+am)/ag;h[x+8>>3]=(ak+az)/ag;h[x+16>>3]=(ad+aN)/ag;h[x+32>>3]=aw;c[x+40>>2]=0;a_=c[11692]|0;L10843:do{if((a_&240|0)==240){L10965:do{if(aE){do{if((a[38984]&1)==0){ac=8005}else{if((a[46752]&1)==0){ac=8005;break}gt(q,u,56296)}}while(0);if((ac|0)==8005){un(q,u,56296,56336)}do{if((a[38984]&1)==0){ac=8009}else{if((a[46752]&1)==0){ac=8009;break}gt(r,v,56296)}}while(0);if((ac|0)==8009){un(r,v,56296,56336)}do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(s,w,56296);break L10965}}while(0);un(s,w,56296,56336)}}while(0);if(!ab){break}do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(t,x,56296);break L10843}}while(0);un(t,x,56296,56336)}else{aw=+h[64672+($*688&-1)>>3];ag=aw-aC;a$=~~(ay/ag+.1);aN=+h[64672+(ah*688&-1)>>3];ad=aN-aa;a0=~~(av/ad+.1);a1=~~(aP/ag+.1);a2=~~(aQ/ad+.1);az=+h[7076];h[y+24>>3]=az;h[y+16>>3]=az;h[y+8>>3]=az;h[y>>3]=az;h[z+24>>3]=az;h[z+16>>3]=az;h[z+8>>3]=az;h[z>>3]=az;if((d|0)>0){a3=b;a4=d;while(1){a5=a4-1|0;a6=c[a3+260>>2]|0;a7=c[a3+8>>2]|0;do{if((a7|0)==3){if((c[a3+244>>2]|0)==0){break}a9=a3+252|0;ac=8023}else if((a7|0)!=4){a9=37368;ac=8023}}while(0);do{if((ac|0)==8023){ac=0;a7=c[a9>>2]|0;ba=c[a6+8>>2]|0;bb=c[a6+12>>2]|0;bc=c[bb>>2]|0;do{if((bc|0)==0){az=+h[bb+8>>3];ak=az-aC;am=+P(+ak);at=+h[11];if(am>=at){if(+P(+(az-aw))>=at){break}}az=+h[bb+16>>3];am=az-aa;if(+P(+am)>=at){if(+P(+(az-aN))>=at){break}}bd=~~(ak/(aw-aC)+.1);be=~~(am/ad+.1);bf=y+(bd<<4)+(be<<3)|0;am=+h[bb+24>>3];if(+h[bf>>3]<am){h[bf>>3]=am}bf=z+(bd<<4)+(be<<3)|0;if(+h[bf>>3]<=am){break}h[bf>>3]=am}}while(0);bf=ba-1|0;do{if((c[bb+(bf<<6)>>2]|0)==0){am=+h[bb+(bf<<6)+8>>3];ak=am-aC;at=+P(+ak);az=+h[11];if(at>=az){if(+P(+(am-aw))>=az){break}}am=+h[bb+(bf<<6)+16>>3];at=am-aa;if(+P(+at)>=az){if(+P(+(am-aN))>=az){break}}be=~~(ak/(aw-aC)+.1);bd=~~(at/ad+.1);bg=y+(be<<4)+(bd<<3)|0;at=+h[bb+(bf<<6)+24>>3];if(+h[bg>>3]<at){h[bg>>3]=at}bg=z+(be<<4)+(bd<<3)|0;if(+h[bg>>3]<=at){break}h[bg>>3]=at}}while(0);ba=a7-1|0;if((ba|0)==0){bh=a6;bi=bb;bj=bc}else{bg=a6;bd=ba;do{bg=c[bg>>2]|0;bd=bd-1|0;}while((bd|0)!=0);bd=c[bg+12>>2]|0;bh=bg;bi=bd;bj=c[bd>>2]|0}bd=bh+12|0;do{if((bj|0)==0){at=+h[bi+8>>3];ak=at-aC;az=+P(+ak);am=+h[11];if(az>=am){if(+P(+(at-aw))>=am){break}}at=+h[bi+16>>3];az=at-aa;if(+P(+az)>=am){if(+P(+(at-aN))>=am){break}}bc=~~(ak/(aw-aC)+.1);bb=~~(az/ad+.1);a7=y+(bc<<4)+(bb<<3)|0;az=+h[bi+24>>3];if(+h[a7>>3]<az){h[a7>>3]=az}a7=z+(bc<<4)+(bb<<3)|0;if(+h[a7>>3]<=az){break}h[a7>>3]=az}}while(0);bg=c[bd>>2]|0;if((c[bg+(bf<<6)>>2]|0)!=0){break}az=+h[bg+(bf<<6)+8>>3];ak=az-aC;am=+P(+ak);at=+h[11];if(am>=at){if(+P(+(az-aw))>=at){break}}az=+h[bg+(bf<<6)+16>>3];am=az-aa;if(+P(+am)>=at){if(+P(+(az-aN))>=at){break}}a7=~~(ak/ag+.1);bb=~~(am/ad+.1);bc=y+(a7<<4)+(bb<<3)|0;am=+h[bg+(bf<<6)+24>>3];if(+h[bc>>3]<am){h[bc>>3]=am}bc=z+(a7<<4)+(bb<<3)|0;if(+h[bc>>3]<=am){break}h[bc>>3]=am}}while(0);if((a5|0)>0){a3=c[a3>>2]|0;a4=a5}else{break}}}L10910:do{if(aE){L10912:do{if((a_&16|0)==0){ad=+h[y+(a$<<4)+(a0<<3)>>3];ag=+h[z+(a$<<4)+(a0<<3)>>3];if(ad==ag){break}aN=(ag-af)*ar+aq+-1.0;aw=aO+aN*as;am=aw==0.0?1.0e-5:aw;h[A>>3]=(aB+aN*aA)/am;h[A+8>>3]=(ae+aN*al)/am;h[A+16>>3]=(aI+aN*aJ)/am;h[A+32>>3]=ag;c[A+40>>2]=0;ag=(ad-af)*ar+aq+-1.0;am=aO+ag*as;aN=am==0.0?1.0e-5:am;h[B>>3]=(aB+ag*aA)/aN;h[B+8>>3]=(ae+ag*al)/aN;h[B+16>>3]=(aI+ag*aJ)/aN;h[B+32>>3]=ad;c[B+40>>2]=0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(A,B,56296);break L10912}}while(0);un(A,B,56296,56336)}else{do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(q,u,56296);break L10912}}while(0);un(q,u,56296,56336)}}while(0);L10925:do{if((c[11692]&32|0)==0){ad=+h[y+(a1<<4)+(a2<<3)>>3];aN=+h[z+(a1<<4)+(a2<<3)>>3];if(ad==aN){break}ag=(+h[7089]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;am=(+h[7088]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aw=+h[5279];ak=+h[2];at=+h[12];az=(aN-aw)*ak+at+-1.0;aZ=+h[403]+ag*+h[391]+am*+h[395];aH=+h[399];aY=+h[404]+ag*+h[392]+am*+h[396];aX=+h[400];aW=+h[405]+ag*+h[393]+am*+h[397];aV=+h[401];aU=+h[406]+ag*+h[394]+am*+h[398];am=+h[402];ag=aU+az*am;aT=ag==0.0?1.0e-5:ag;h[C>>3]=(aZ+az*aH)/aT;h[C+8>>3]=(aY+az*aX)/aT;h[C+16>>3]=(aW+az*aV)/aT;h[C+32>>3]=aN;c[C+40>>2]=0;aN=(ad-aw)*ak+at+-1.0;at=aU+aN*am;am=at==0.0?1.0e-5:at;h[D>>3]=(aZ+aN*aH)/am;h[D+8>>3]=(aY+aN*aX)/am;h[D+16>>3]=(aW+aN*aV)/am;h[D+32>>3]=ad;c[D+40>>2]=0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(C,D,56296);break L10925}}while(0);un(C,D,56296,56336)}else{do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(r,v,56296);break L10925}}while(0);un(r,v,56296,56336)}}while(0);if((c[11692]&64|0)!=0){do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(s,w,56296);break L10910}}while(0);un(s,w,56296,56336);break}a5=1-a0|0;bf=1-a$|0;ad=+h[y+(bf<<4)+(a5<<3)>>3];am=+h[z+(bf<<4)+(a5<<3)>>3];if(ad==am){break}aV=(+h[3305]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aN=(+h[3304]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aW=+h[5279];aX=+h[2];aY=+h[12];aH=(am-aW)*aX+aY+-1.0;aZ=+h[403]+aV*+h[391]+aN*+h[395];at=+h[399];aU=+h[404]+aV*+h[392]+aN*+h[396];ak=+h[400];aw=+h[405]+aV*+h[393]+aN*+h[397];aT=+h[401];az=+h[406]+aV*+h[394]+aN*+h[398];aN=+h[402];aV=az+aH*aN;ag=aV==0.0?1.0e-5:aV;h[E>>3]=(aZ+aH*at)/ag;h[E+8>>3]=(aU+aH*ak)/ag;h[E+16>>3]=(aw+aH*aT)/ag;h[E+32>>3]=am;c[E+40>>2]=0;am=(ad-aW)*aX+aY+-1.0;aY=az+am*aN;aN=aY==0.0?1.0e-5:aY;h[F>>3]=(aZ+am*at)/aN;h[F+8>>3]=(aU+am*ak)/aN;h[F+16>>3]=(aw+am*aT)/aN;h[F+32>>3]=ad;c[F+40>>2]=0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(E,F,56296);break L10910}}while(0);un(E,F,56296,56336)}}while(0);if(!ab){break}if((c[11692]&128|0)!=0){do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(t,x,56296);break L10843}}while(0);un(t,x,56296,56336);break}a$=1-a2|0;a0=1-a1|0;ad=+h[y+(a0<<4)+(a$<<3)>>3];aN=+h[z+(a0<<4)+(a$<<3)>>3];if(ad==aN){break}aT=(+h[5264]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;am=(+h[5263]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aw=+h[5279];ak=+h[2];aU=+h[12];at=(aN-aw)*ak+aU+-1.0;aZ=+h[403]+aT*+h[391]+am*+h[395];aY=+h[399];az=+h[404]+aT*+h[392]+am*+h[396];aX=+h[400];aW=+h[405]+aT*+h[393]+am*+h[397];ag=+h[401];aH=+h[406]+aT*+h[394]+am*+h[398];am=+h[402];aT=aH+at*am;aV=aT==0.0?1.0e-5:aT;h[G>>3]=(aZ+at*aY)/aV;h[G+8>>3]=(az+at*aX)/aV;h[G+16>>3]=(aW+at*ag)/aV;h[G+32>>3]=aN;c[G+40>>2]=0;aN=(ad-aw)*ak+aU+-1.0;aU=aH+aN*am;am=aU==0.0?1.0e-5:aU;h[H>>3]=(aZ+aN*aY)/am;h[H+8>>3]=(az+aN*aX)/am;h[H+16>>3]=(aW+aN*ag)/am;h[H+32>>3]=ad;c[H+40>>2]=0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(G,H,56296);break L10843}}while(0);un(G,H,56296,56336)}}while(0);L10990:do{if(aE){L10992:do{if((c[11692]&256|0)!=0){do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(u,v,56296);break L10992}}while(0);un(u,v,56296,56336)}}while(0);if((c[11692]&512|0)==0){break}do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(w,v,56296);break L10990}}while(0);un(w,v,56296,56336)}}while(0);if(!ab){break}L11006:do{if((c[11692]&1024|0)!=0){do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(u,x,56296);break L11006}}while(0);un(u,x,56296,56336)}}while(0);if((c[11692]&2048|0)==0){break}do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(w,x,56296);break L10803}}while(0);un(w,x,56296,56336)}}while(0);do{if((c[5094]|0)==0){if((e|0)!=3){break}i=j;return}else{x=c[10026]|0;if((((x|0)>-1?x:-x|0)|0)!=(f|0)|(e|0)==3){i=j;return}else{c[13542]=0;break}}}while(0);f=c[200]|0;x=(c[64916+(f*688&-1)>>2]|0)==0;if(x){if((c[65140+(f*688&-1)>>2]|0)!=0){ac=8133}}else{ac=8133}L11069:do{if((ac|0)==8133){w=c[144]|0;aJ=+h[64664+(w*688&-1)>>3];aI=+h[97];al=aJ+ +h[64672+(w*688&-1)>>3]-aI;ae=+h[64664+(f*688&-1)>>3];aA=(+h[64672+(f*688&-1)>>3]+ae)*.5;aB=+h[96]+ +h[91]*(aA-ae)+-1.0;ae=+h[21];as=+h[68];aO=(aI-aJ)*ae+as+-1.0;aI=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;aq=+h[403]+aB*+h[391];ar=+h[395];af=aI*+h[399];aa=+h[404]+aB*+h[392];aC=+h[396];aQ=aI*+h[400];aP=+h[405]+aB*+h[393];av=+h[397];ay=aI*+h[401];ad=+h[406]+aB*+h[394];aB=+h[398];am=aI*+h[402];aI=ad+aO*aB+am;ag=aI==0.0?1.0e-5:aI;aI=(al-aJ)*ae+as+-1.0;as=ad+aI*aB+am;am=as==0.0?1.0e-5:as;as=+(c[40]|0);h[695]=((aq+aI*ar+af)/am-(aq+aO*ar+af)/ag)/as;h[694]=((aa+aI*aC+aQ)/am-(aa+aO*aC+aQ)/ag)/as;h[693]=((aP+aI*av+ay)/am-(aP+aO*av+ay)/ag)/as;as=+g[3538];if(as>90.0|(e|0)==1){if(as<=90.0|(e|0)==2|x){bk=f}else{ac=8136}}else{if(x){bk=f}else{ac=8136}}if((ac|0)==8136){de(2,10);bk=c[200]|0}if((c[65140+(bk*688&-1)>>2]|0)==0){break}as=+g[3538];do{if(as>90.0|(e|0)==2){if(as>90.0){w=c[5094]|0;if((e|0)!=1|(w|0)!=0){bl=w;ac=8144;break}else{break L11069}}if((c[5094]|0)==0){break L11069}bm=+h[97];ac=8145}else{bl=c[5094]|0;ac=8144}}while(0);do{if((ac|0)==8144){as=+h[97];if((bl|0)!=0){bm=as;ac=8145;break}ag=(as-al)*.25;do{if((c[65084+(bk*688&-1)>>2]|0)==-3){ay=+h[64664+(bk*688&-1)>>3];av=+h[91];aO=+h[96];aP=(ay-ay)*av+aO+-1.0;am=(as- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aI=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;aQ=+h[403];aC=+h[391];aa=am*+h[395];af=aI*+h[399];ar=+h[404];aq=+h[392];aB=am*+h[396];ad=aI*+h[400];ae=+h[406];aJ=+h[394];aN=am*+h[398];am=aI*+h[402];aI=ae+aP*aJ+aN+am;aW=aI==0.0?1.0e-5:aI;aI=+(c[180]|0);w=c[186]|0;aX=+(c[40]|0);u=c[46]|0;az=aO+av*(+h[64672+(bk*688&-1)>>3]-ay)+-1.0;ay=am+(aN+(ae+aJ*az));aJ=ay==0.0?1.0e-5:ay;ay=+Y(+(+(~~(aX*((ad+(aB+(ar+aq*az)))/aJ))+u|0)- +(~~((ar+aP*aq+aB+ad)/aW*aX)+u|0)),+(+(~~(aI*((af+(aa+(aQ+aC*az)))/aJ))+w|0)- +(~~((aQ+aP*aC+aa+af)/aW*aI)+w|0)))/.017453292519943295;if(ay>0.0){bn=ay+.5}else{bn=ay+-.5}w=~~+O(+bn);ay=ag*.5;if(ay<=0.0){bo=w;bp=ay;break}bo=w+180|0;bp=ay}else{bo=0;bp=ag}}while(0);if((c[64916+(bk*688&-1)>>2]&2|0)==0){ag=(aA- +h[64664+(bk*688&-1)>>3])*+h[91]+ +h[96]+-1.0;ay=(bp+as- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aI=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;aW=+h[406]+ag*+h[394]+ay*+h[398]+aI*+h[402];af=aW==0.0?1.0e-5:aW;bq=(+h[403]+ag*+h[391]+ay*+h[395]+aI*+h[399])/af;br=(+h[404]+ag*+h[392]+ay*+h[396]+aI*+h[400])/af;bs=a[65072+(bk*688&-1)|0]|0}else{w=a[65072+(bk*688&-1)|0]|0;if((w&1)==0){bt=-0.0-bp}else{bt=bp}af=(aA- +h[64664+(bk*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aI=(bt*.5- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ay=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;ag=+h[406]+af*+h[394]+aI*+h[398]+ay*+h[402];aW=ag==0.0?1.0e-5:ag;bq=(+h[403]+af*+h[391]+aI*+h[395]+ay*+h[399])/aW;br=(+h[404]+af*+h[392]+aI*+h[396]+ay*+h[400])/aW;bs=w}if((bs&1)==0){aW=+h[65056+(bk*688&-1)>>3];ay=+((c[W+24>>2]|0)>>>0>>>0);bu=bq- +h[695]*aW*ay;bv=br-ay*aW*+h[694]}else{bu=bq;bv=br}bw=bo;bx=~~(bu*+(c[180]|0))+(c[186]|0)|0;by=~~(bv*+(c[40]|0))+(c[46]|0)|0}}while(0);if((ac|0)==8145){al=(aA- +h[64664+(bk*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aW=(bm- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ay=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;aI=+h[406]+al*+h[394]+aW*+h[398]+ay*+h[402];af=aI==0.0?1.0e-5:aI;aI=+h[694];ag=+((c[W+16>>2]|0)>>>0>>>0);aa=(+h[404]+al*+h[392]+aW*+h[396]+ay*+h[400])/af-aI*ag;if((a[65072+(bk*688&-1)|0]&1)==0){bz=aa- +h[65056+(bk*688&-1)>>3]*aI*+((c[W+24>>2]|0)>>>0>>>0)}else{bz=aa}bw=c[65132+(bk*688&-1)>>2]|0;bx=~~((+h[403]+al*+h[391]+aW*+h[395]+ay*+h[399])/af*+(c[180]|0))+(c[186]|0)|0;by=(~~(bz*+(c[40]|0))+(c[46]|0)|0)-~~(ag*1.5)|0}fx(65224+(bk*688&-1)|0,I,J,207600);w=(c[I>>2]|0)+bx|0;u=(c[J>>2]|0)+by|0;v=c[200]|0;aE=a[65264+(v*688&-1)|0]&1;do{if(aE<<24>>24==0){bA=v}else{if((a[37456]&1)!=0){bA=v;break}a[37456]=1;H=c[(c[3524]|0)+88>>2]|0;if((H|0)==0){bA=v;break}cO[H&255](179864);bA=c[200]|0}}while(0);a[37456]=aE;fn(65152+(bA*688&-1)|0,W);do{if((bw|0)==0){ac=8169}else{if((cO[c[(c[3524]|0)+72>>2]&255](bw)|0)==0){ac=8169;break}v=c[200]|0;ln(w,u,c[65140+(v*688&-1)>>2]|0,1,0,bw,c[65144+(v*688&-1)>>2]|0);v=c[(c[3524]|0)+72>>2]|0;cO[v&255](0)}}while(0);if((ac|0)==8169){aE=c[200]|0;ln(w,u,c[65140+(aE*688&-1)>>2]|0,1,0,0,c[65144+(aE*688&-1)>>2]|0)}if((c[65152+((c[200]|0)*688&-1)>>2]|0)!=0){cM[c[W+64>>2]&511](-2)}a[37456]=0}}while(0);bw=c[144]|0;bA=(c[64916+(bw*688&-1)>>2]|0)==0;if(bA){if((c[65140+(bw*688&-1)>>2]|0)!=0){ac=8175}}else{ac=8175}L11129:do{if((ac|0)==8175){by=c[200]|0;bz=+h[64664+(by*688&-1)>>3];bm=+h[69];bv=bz+ +h[64672+(by*688&-1)>>3]-bm;bu=+h[64664+(bw*688&-1)>>3];br=(+h[64672+(bw*688&-1)>>3]+bu)*.5;bq=+h[91];bt=+h[96];bp=(bm-bz)*bq+bt+-1.0;bm=(br-bu)*+h[21]+ +h[68]+-1.0;bu=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;bn=+h[403];aA=+h[391];ag=bm*+h[395];af=bu*+h[399];ay=+h[404];aW=+h[392];al=bm*+h[396];aa=bu*+h[400];aI=+h[405];aC=+h[393];aP=bm*+h[397];aQ=bu*+h[401];aJ=+h[406];az=+h[394];aX=bm*+h[398];bm=bu*+h[402];bu=aJ+bp*az+aX+bm;ad=bu==0.0?1.0e-5:bu;bu=(bv-bz)*bq+bt+-1.0;bt=aJ+bu*az+aX+bm;bm=bt==0.0?1.0e-5:bt;bt=+(c[180]|0);h[695]=((bn+bu*aA+ag+af)/bm-(bn+bp*aA+ag+af)/ad)/bt;h[694]=((ay+bu*aW+al+aa)/bm-(ay+bp*aW+al+aa)/ad)/bt;h[693]=((aI+bu*aC+aP+aQ)/bm-(aI+bp*aC+aP+aQ)/ad)/bt;bt=+g[3538];if(bt>90.0|(e|0)==1){if(bt<=90.0|(e|0)==2|bA){bB=bw}else{ac=8178}}else{if(bA){bB=bw}else{ac=8178}}if((ac|0)==8178){de(1,12);bB=c[144]|0}if((c[65140+(bB*688&-1)>>2]|0)==0){break}bt=+g[3538];do{if(bt>90.0|(e|0)==2){if(bt>90.0){by=c[5094]|0;if((e|0)!=1|(by|0)!=0){bC=by;ac=8186;break}else{break L11129}}if((c[5094]|0)==0){break L11129}bD=+h[69];ac=8187}else{bC=c[5094]|0;ac=8186}}while(0);do{if((ac|0)==8186){bt=+h[69];if((bC|0)!=0){bD=bt;ac=8187;break}ad=(bv-bt)*.25;do{if((c[65084+(bB*688&-1)>>2]|0)==-3){aQ=+h[64664+(bB*688&-1)>>3];aP=(bt- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aC=+h[21];bp=+h[68];aI=(aQ-aQ)*aC+bp+-1.0;bm=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;bu=+h[403]+aP*+h[391];aa=+h[395];al=bm*+h[399];aW=+h[404]+aP*+h[392];ay=+h[396];af=bm*+h[400];ag=+h[406]+aP*+h[394];aP=+h[398];aA=bm*+h[402];bm=ag+aI*aP+aA;bn=bm==0.0?1.0e-5:bm;bm=+(c[180]|0);u=c[186]|0;aX=+(c[40]|0);w=c[46]|0;az=bp+aC*(+h[64672+(bB*688&-1)>>3]-aQ)+-1.0;aQ=aA+(ag+aP*az);aP=aQ==0.0?1.0e-5:aQ;aQ=+Y(+(+(~~(aX*((af+(aW+ay*az))/aP))+w|0)- +(~~((aW+aI*ay+af)/bn*aX)+w|0)),+(+(~~(bm*((al+(bu+aa*az))/aP))+u|0)- +(~~((bu+aI*aa+al)/bn*bm)+u|0)))/.017453292519943295;if(aQ>0.0){bE=aQ+.5}else{bE=aQ+-.5}u=~~+O(+bE);aQ=ad*.5;if(aQ<=0.0){bF=aQ;bG=u;break}bF=aQ;bG=u+180|0}else{bF=ad;bG=0}}while(0);if((c[64916+(bB*688&-1)>>2]&2|0)==0){u=c[200]|0;ad=(bt-bF- +h[64664+(u*688&-1)>>3])*+h[91]+ +h[96]+-1.0;as=(br- +h[64664+(bB*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aQ=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;bm=+h[406]+ad*+h[394]+as*+h[398]+aQ*+h[402];bn=bm==0.0?1.0e-5:bm;bH=(+h[403]+ad*+h[391]+as*+h[395]+aQ*+h[399])/bn;bI=(+h[404]+ad*+h[392]+as*+h[396]+aQ*+h[400])/bn;bJ=u;bK=a[65072+(u*688&-1)|0]|0}else{u=c[200]|0;w=a[65072+(u*688&-1)|0]|0;if((w&1)==0){bL=bF}else{bL=-0.0-bF}bn=(bL*.5- +h[64664+(u*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aQ=(br- +h[64664+(bB*688&-1)>>3])*+h[21]+ +h[68]+-1.0;as=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;ad=+h[406]+bn*+h[394]+aQ*+h[398]+as*+h[402];bm=ad==0.0?1.0e-5:ad;bH=(+h[403]+bn*+h[391]+aQ*+h[395]+as*+h[399])/bm;bI=(+h[404]+bn*+h[392]+aQ*+h[396]+as*+h[400])/bm;bJ=u;bK=w}if((bK&1)==0){bm=+h[65056+(bJ*688&-1)>>3];as=+((c[W+28>>2]|0)>>>0>>>0);bM=bH- +h[695]*bm*as;bN=bI-as*bm*+h[694]}else{bM=bH;bN=bI}bO=bG;bP=0;bQ=~~(bN*+(c[40]|0))+(c[46]|0)|0;bR=~~(bM*+(c[180]|0))+(c[186]|0)|0;bS=bB}}while(0);if((ac|0)==8187){w=c[200]|0;bv=+h[64664+(w*688&-1)>>3];bm=+h[91];as=+h[96];aQ=(bD-bv)*bm+as+-1.0;bn=+h[64664+(bB*688&-1)>>3];ad=+h[21];al=+h[68];aa=(br-bn)*ad+al+-1.0;aI=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;bu=+h[403];aP=+h[391];az=+h[395];aX=aI*+h[399];af=+h[404];ay=+h[392];aW=+h[396];ag=aI*+h[400];aA=+h[406];aC=+h[394];bp=+h[398];aJ=aI*+h[402];aI=aA+aQ*aC+aa*bp+aJ;bq=aI==0.0?1.0e-5:aI;aI=(bu+aQ*aP+aa*az+aX)/bq;bz=(af+aQ*ay+aa*aW+ag)/bq;u=c[64916+(bB*688&-1)>>2]|0;do{if((u&2|0)==0){bT=aI;bU=bz}else{if((a[64788+(w*688&-1)|0]&1)!=0){bT=aI;bU=bz;break}bq=+h[64672+(w*688&-1)>>3];if(bv<bq){if(bv>0.0|bq<0.0){bT=aI;bU=bz;break}}else{if(bq>0.0|bv<0.0){bT=aI;bU=bz;break}}bq=(0.0-bv)*bm+as+-1.0;aa=(bD-bn)*ad+al+-1.0;aQ=aA+bq*aC+aa*bp+aJ;aB=aQ==0.0?1.0e-5:aQ;bT=(bu+bq*aP+aa*az+aX)/aB;bU=(af+bq*ay+aa*aW+ag)/aB}}while(0);ag=+h[695];by=W+20|0;aW=bT-ag*+((c[by>>2]|0)>>>0>>>0);if((a[65072+(w*688&-1)|0]&1)==0){bV=aW- +h[65056+(w*688&-1)>>3]*ag*+((c[W+28>>2]|0)>>>0>>>0)}else{bV=aW}J=~~(bV*+(c[180]|0))+(c[186]|0)|0;bx=~~(bU*+(c[40]|0))+(c[46]|0)|0;c[216]=0;if((u&1|0)==0){bW=.5;bX=bB}else{c[216]=0;de(1,14);bW=+(c[216]|0)+.5;bX=c[144]|0}bO=c[65132+(bX*688&-1)>>2]|0;bP=2;bQ=bx;bR=J-~~(bW*+((c[by>>2]|0)>>>0>>>0))|0;bS=bX}fx(65224+(bS*688&-1)|0,K,L,207600);by=(c[K>>2]|0)+bR|0;J=(c[L>>2]|0)+bQ|0;bx=c[144]|0;I=a[65264+(bx*688&-1)|0]&1;do{if(I<<24>>24==0){bY=bx}else{if((a[37456]&1)!=0){bY=bx;break}a[37456]=1;bk=c[(c[3524]|0)+88>>2]|0;if((bk|0)==0){bY=bx;break}cO[bk&255](179864);bY=c[144]|0}}while(0);a[37456]=I;fn(65152+(bY*688&-1)|0,W);do{if((bO|0)==0){ac=8219}else{if((cO[c[(c[3524]|0)+72>>2]&255](bO)|0)==0){ac=8219;break}bx=c[144]|0;ln(by,J,c[65140+(bx*688&-1)>>2]|0,1,bP,bO,c[65144+(bx*688&-1)>>2]|0);bx=c[(c[3524]|0)+72>>2]|0;cO[bx&255](0)}}while(0);if((ac|0)==8219){I=c[144]|0;ln(by,J,c[65140+(I*688&-1)>>2]|0,1,bP,0,c[65144+(I*688&-1)>>2]|0)}if((c[65152+((c[144]|0)*688&-1)>>2]|0)!=0){cM[c[W+64>>2]&511](-2)}a[37456]=0}}while(0);L11198:do{if((c[64916+((c[34]|0)*688&-1)>>2]|0)!=0){if(!((e|0)!=1&(c[5094]|0)==0)){break}do{if((a[46752]&1)==0){if((c[11690]&2|0)!=0){break}if((a8(30632,115)|0)==0){break L11198}}}while(0);de(0,6)}}while(0);e=c[144]|0;L11206:do{if((c[65284+(e*688&-1)>>2]|0)>-3){J=c[200]|0;if((a[64788+(J*688&-1)|0]&1)!=0){break}bW=+h[64664+(J*688&-1)>>3];bU=+h[64672+(J*688&-1)>>3];if(bW<bU){if(bW>0.0|bU<0.0){break}}else{if(bU>0.0|bW<0.0){break}}bU=+h[64664+(e*688&-1)>>3];bV=+h[7076];bT=(0.0-bW)*+h[91]+ +h[96]+-1.0;bW=+h[21];bD=+h[68];bM=(bU-bU)*bW+bD+-1.0;bN=(bV- +h[5279])*+h[2]+ +h[12]+-1.0;bI=+h[403]+bT*+h[391];bH=+h[395];bL=bN*+h[399];bF=+h[404]+bT*+h[392];bE=+h[396];aW=bN*+h[400];ag=+h[405]+bT*+h[393];ay=+h[397];af=bN*+h[401];aX=+h[406]+bT*+h[394];bT=+h[398];az=bN*+h[402];bN=aX+bM*bT+az;aP=bN==0.0?1.0e-5:bN;h[M>>3]=(bI+bM*bH+bL)/aP;h[M+8>>3]=(bF+bM*bE+aW)/aP;h[M+16>>3]=(ag+bM*ay+af)/aP;h[M+32>>3]=bV;c[M+40>>2]=0;aP=(+h[64672+(e*688&-1)>>3]-bU)*bW+bD+-1.0;bD=aX+aP*bT+az;az=bD==0.0?1.0e-5:bD;h[N>>3]=(bI+aP*bH+bL)/az;h[N+8>>3]=(bF+aP*bE+aW)/az;h[N+16>>3]=(ag+aP*ay+af)/az;h[N+32>>3]=bV;c[N+40>>2]=0;J=65280+(e*688&-1)|0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(M,N,J);break L11206}}while(0);un(M,N,J,65320+(e*688&-1)|0)}}while(0);e=c[34]|0;L11218:do{if((c[65284+(e*688&-1)>>2]|0)>-3){N=c[200]|0;if((a[64788+(N*688&-1)|0]&1)!=0){break}bV=+h[64664+(N*688&-1)>>3];az=+h[64672+(N*688&-1)>>3];if(bV<az){if(bV>0.0|az<0.0){break}}else{if(az>0.0|bV<0.0){break}}az=+h[64664+(e*688&-1)>>3];af=(0.0-bV)*+h[91]+ +h[96]+-1.0;bV=(0.0- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ay=+h[5279];aP=+h[2];ag=+h[12];aW=(az-ay)*aP+ag+-1.0;bE=+h[403]+af*+h[391]+bV*+h[395];bF=+h[399];bL=+h[404]+af*+h[392]+bV*+h[396];bH=+h[400];bI=+h[405]+af*+h[393]+bV*+h[397];bD=+h[401];bT=+h[406]+af*+h[394]+bV*+h[398];bV=+h[402];af=bT+aW*bV;aX=af==0.0?1.0e-5:af;h[Q>>3]=(bE+aW*bF)/aX;h[Q+8>>3]=(bL+aW*bH)/aX;h[Q+16>>3]=(bI+aW*bD)/aX;h[Q+32>>3]=az;c[Q+40>>2]=0;az=+h[64672+(e*688&-1)>>3];aX=(az-ay)*aP+ag+-1.0;ag=bT+aX*bV;bV=ag==0.0?1.0e-5:ag;h[R>>3]=(bE+aX*bF)/bV;h[R+8>>3]=(bL+aX*bH)/bV;h[R+16>>3]=(bI+aX*bD)/bV;h[R+32>>3]=az;c[R+40>>2]=0;N=65280+(e*688&-1)|0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(Q,R,N);break L11218}}while(0);un(Q,R,N,65320+(e*688&-1)|0)}}while(0);e=c[200]|0;R=c[65284+(e*688&-1)>>2]|0;L11230:do{if((R|0)>-3){Q=c[144]|0;if((a[64788+(Q*688&-1)|0]&1)!=0){break}az=+h[64664+(Q*688&-1)>>3];bV=+h[64672+(Q*688&-1)>>3];if(az<bV){if(az>0.0|bV<0.0){break}}else{if(bV>0.0|az<0.0){break}}Q=k;J=65320+(e*688&-1)|0;c[Q>>2]=c[J>>2];c[Q+4>>2]=c[J+4>>2];c[Q+8>>2]=c[J+8>>2];c[Q+12>>2]=c[J+12>>2];do{if((c[65280+(e*688&-1)>>2]|0)!=0){az=+h[65304+(e*688&-1)>>3];J=c[(c[3524]|0)+92>>2]|0;if(az<0.0){cK[J&63](+h[3817]);break}else{cK[J&63](az);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[65296+(e*688&-1)>>3]);N=c[(c[3524]|0)+64>>2]|0;if((R|0)<-5){cM[N&511](-2)}else{cM[N&511](R)}N=c[3524]|0;do{if((a[65312+(e*688&-1)|0]&1)==0){if((c[N+96>>2]&1024|0)!=0){break}c[k>>2]=1;c[k+4>>2]=R;ac=8262}else{ac=8262}}while(0);if((ac|0)==8262){fn(k,N)}J=c[200]|0;az=+h[64664+(J*688&-1)>>3];bV=+h[7076];bD=+h[91];aX=+h[96];bI=(az-az)*bD+aX+-1.0;bH=(0.0- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;bL=(bV- +h[5279])*+h[2]+ +h[12]+-1.0;bF=+h[403];bE=+h[391];ag=bH*+h[395];bT=bL*+h[399];aP=+h[404];ay=+h[392];aW=bH*+h[396];af=bL*+h[400];bW=+h[405];bU=+h[393];bM=bH*+h[397];bN=bL*+h[401];bu=+h[406];aJ=+h[394];bp=bH*+h[398];bH=bL*+h[402];bL=bu+bI*aJ+bp+bH;aC=bL==0.0?1.0e-5:bL;h[S>>3]=(bF+bI*bE+ag+bT)/aC;h[S+8>>3]=(aP+bI*ay+aW+af)/aC;h[S+16>>3]=(bW+bI*bU+bM+bN)/aC;h[S+32>>3]=bV;c[S+40>>2]=0;aC=(+h[64672+(J*688&-1)>>3]-az)*bD+aX+-1.0;aX=bu+aC*aJ+bp+bH;bH=aX==0.0?1.0e-5:aX;h[T>>3]=(bF+aC*bE+ag+bT)/bH;h[T+8>>3]=(aP+aC*ay+aW+af)/bH;h[T+16>>3]=(bW+aC*bU+bM+bN)/bH;h[T+32>>3]=bV;c[T+40>>2]=0;Q=65280+(J*688&-1)|0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(S,T,Q);break L11230}}while(0);un(S,T,Q,65320+(J*688&-1)|0)}}while(0);T=c[34]|0;S=c[5094]|0;if((c[65140+(T*688&-1)>>2]|0)!=0&(S|0)==0){do{if((a[46752]&1)==0){if((c[11690]&2|0)!=0){break}if((cy(30632,213264)|0)!=0){break}i=j;return}}while(0);bV=(+h[64672+(T*688&-1)>>3]+ +h[64664+(T*688&-1)>>3])*.5;if((c[64916+(T*688&-1)>>2]&2|0)==0){bH=(+h[14]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;bN=(+h[13]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;bM=(bV- +h[5279])*+h[2]+ +h[12]+-1.0;bU=+h[406]+bH*+h[394]+bN*+h[398]+bM*+h[402];aC=bU==0.0?1.0e-5:bU;bZ=1;b_=(~~((+h[403]+bH*+h[391]+bN*+h[395]+bM*+h[399])/aC*+(c[180]|0))+(c[186]|0)|0)+((c[W+20>>2]|0)*-7&-1)|0;b$=~~((+h[404]+bH*+h[392]+bN*+h[396]+bM*+h[400])/aC*+(c[40]|0))+(c[46]|0)|0}else{aC=(0.0- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;bM=(0.0- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;bN=(bV- +h[5279])*+h[2]+ +h[12]+-1.0;bV=+h[406]+aC*+h[394]+bM*+h[398]+bN*+h[402];bH=bV==0.0?1.0e-5:bV;bZ=2;b_=(~~((+h[403]+aC*+h[391]+bM*+h[395]+bN*+h[399])/bH*+(c[180]|0))+(c[186]|0)|0)+((c[W+20>>2]|0)*-5&-1)|0;b$=~~((+h[404]+aC*+h[392]+bM*+h[396]+bN*+h[400])/bH*+(c[40]|0))+(c[46]|0)|0}fx(65224+(T*688&-1)|0,U,V,207600);T=(c[U>>2]|0)+b_|0;b_=(c[V>>2]|0)+b$|0;b$=c[34]|0;V=a[65264+(b$*688&-1)|0]&1;do{if(V<<24>>24==0){b0=b$}else{if((a[37456]&1)!=0){b0=b$;break}a[37456]=1;U=c[(c[3524]|0)+88>>2]|0;if((U|0)==0){b0=b$;break}cO[U&255](179864);b0=c[34]|0}}while(0);a[37456]=V;fn(65152+(b0*688&-1)|0,W);b0=c[34]|0;V=65132+(b0*688&-1)|0;if((c[65084+(b0*688&-1)>>2]|0)==-3){c[V>>2]=-270;b1=-270;ac=8281}else{b$=c[V>>2]|0;if((b$|0)==0){b2=b0;ac=8283}else{b1=b$;ac=8281}}do{if((ac|0)==8281){b$=(cO[c[(c[3524]|0)+72>>2]&255](b1)|0)==0;b0=c[34]|0;if(b$){b2=b0;ac=8283;break}ln(T,b_,c[65140+(b0*688&-1)>>2]|0,bZ,0,c[65132+(b0*688&-1)>>2]|0,c[65144+(b0*688&-1)>>2]|0);b0=c[(c[3524]|0)+72>>2]|0;cO[b0&255](0)}}while(0);if((ac|0)==8283){ln(T,b_,c[65140+(b2*688&-1)>>2]|0,bZ,0,0,c[65144+(b2*688&-1)>>2]|0)}if((c[65152+((c[34]|0)*688&-1)>>2]|0)!=0){cM[c[W+64>>2]&511](-2)}a[37456]=0;b3=c[5094]|0}else{b3=S}if((b3|0)==0){i=j;return}c[13542]=X;i=j;return}function fx(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0,u=0.0,v=0.0;f=i;i=i+24|0;g=f|0;j=f+8|0;k=f+16|0;h[g>>3]=+h[a+16>>3];h[j>>3]=+h[a+24>>3];h[k>>3]=+h[a+32>>3];l=(fM(a,e,g,j,k)|0)==0;m=+h[g>>3];if(!l){c[b>>2]=~~m;n=~~+h[j>>3];c[d>>2]=n;i=f;return}o=(m- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;m=(+h[j>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;p=(+h[k>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;q=+h[406]+o*+h[394]+m*+h[398]+p*+h[402];r=q==0.0?1.0e-5:q;l=~~((+h[404]+o*+h[392]+m*+h[396]+p*+h[400])/r*+(c[40]|0))+(c[46]|0)|0;c[b>>2]=~~((+h[403]+o*+h[391]+m*+h[395]+p*+h[399])/r*+(c[180]|0))+(c[186]|0);c[d>>2]=l;if((c[a>>2]|0)==2){s=+h[64664+((c[200]|0)*688&-1)>>3]}else{s=0.0}h[g>>3]=s;g=(c[5094]|0)!=0;do{if((c[a+4>>2]|0)==2){l=c[144]|0;if(g){t=64672+(l*688&-1)|0}else{t=64664+(l*688&-1)|0}u=+h[t>>3]}else{if(!g){u=0.0;break}u=+h[64672+((c[144]|0)*688&-1)>>3]}}while(0);h[j>>3]=u;if((c[a+8>>2]|0)==2){v=+h[64664+((c[34]|0)*688&-1)>>3]}else{v=0.0}h[k>>3]=v;r=(s- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;s=(u- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;u=(v- +h[5279])*+h[2]+ +h[12]+-1.0;v=+h[406]+r*+h[394]+s*+h[398]+u*+h[402];p=v==0.0?1.0e-5:v;k=~~((+h[404]+r*+h[392]+s*+h[396]+u*+h[400])/p*+(c[40]|0));a=c[46]|0;c[b>>2]=(c[b>>2]|0)-(~~((+h[403]+r*+h[391]+s*+h[395]+u*+h[399])/p*+(c[180]|0))+(c[186]|0)|0);n=(c[d>>2]|0)-(k+a|0)|0;c[d>>2]=n;i=f;return}function fy(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0.0,I=0;d=i;i=i+120|0;e=d|0;f=d+8|0;g=d+16|0;j=d+56|0;k=d+72|0;l=d+80|0;m=d+88|0;n=d+96|0;o=d+104|0;p=d+112|0;q=c[13542]|0;c[13542]=(c[(c[3524]|0)+96>>2]&128|0)==0?55520:0;r=c[10826]|0;if((r|0)==0){c[13542]=q;i=d;return}s=j;t=j|0;u=j+4|0;v=g;w=g+4|0;x=g+8|0;y=g+16|0;z=g|0;A=r;do{if((c[A+100>>2]|0)==(b|0)){fK(A+8|0,m,n,123984);r=~~+h[m>>3];B=~~+h[n>>3];C=A+48|0;if((a[A+88|0]&1)==0){fK(C,k,l,123984);D=~~+h[k>>3];c[o>>2]=D;E=~~+h[l>>3];c[p>>2]=E;F=D;G=E}else{fx(C,o,p,123984);C=(c[o>>2]|0)+r|0;c[o>>2]=C;E=(c[p>>2]|0)+B|0;c[p>>2]=E;F=C;G=E}E=A+144|0;c[s>>2]=c[E>>2];c[s+4>>2]=c[E+4>>2];c[s+8>>2]=c[E+8>>2];c[s+12>>2]=c[E+12>>2];E=c[A+108>>2]|0;do{if((c[A+104>>2]|0)!=0){H=+h[A+128>>3];C=c[(c[3524]|0)+92>>2]|0;if(H<0.0){cK[C&63](+h[3817]);break}else{cK[C&63](H);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[A+120>>3]);C=c[(c[3524]|0)+64>>2]|0;if((E|0)<-5){cM[C&511](-2)}else{cM[C&511](E)}C=c[3524]|0;do{if((a[A+136|0]&1)==0){if((c[C+96>>2]&1024|0)!=0){break}c[t>>2]=1;c[u>>2]=E;I=8328}else{I=8328}}while(0);if((I|0)==8328){I=0;fn(j,C)}E=c[200]|0;h[64776+(E*688&-1)>>3]=+((c[7939]|0)-(c[7938]|0)|0)/(+h[64672+(E*688&-1)>>3]- +h[64664+(E*688&-1)>>3]);c[12912]=c[A+200>>2];c[12910]=0;H=+h[A+168>>3];if(H>0.0){uE(v|0,0,40);c[w>>2]=2;c[x>>2]=2;h[y>>3]=H;c[z>>2]=c[A+176>>2];fN(g,e,f,123984);h[6458]=+h[A+184>>3];h[6457]=+h[A+192>>3];c[12910]=~~+h[e>>3]}fm(r,B,F,G,c[A+160>>2]|0)}A=c[A>>2]|0;}while((A|0)!=0);c[13542]=q;i=d;return}function fz(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0;cM[c[(c[3524]|0)+168>>2]&511](8);e=c[(c[3524]|0)+76>>2]|0;do{if((c[9046]|0)!=0|(c[9031]|0)==3){if((cO[e&255](2)|0)==0){f=(c[8986]|0)+a|0;g=c[(c[3524]|0)+20>>2]|0;h=f-aa(lp(d)|0,g)|0;cR[c[(c[3524]|0)+68>>2]&127](h,b,d);break}else{cR[c[(c[3524]|0)+68>>2]&127]((c[8986]|0)+a|0,b,d);break}}else{cO[e&255](0);cR[c[(c[3524]|0)+68>>2]&127]((c[8990]|0)+a|0,b,d)}}while(0);cM[c[(c[3524]|0)+168>>2]&511](9);return}function fA(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0.0,y=0.0,z=0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,P=0.0,Q=0.0,R=0.0,S=0.0,T=0,U=0.0,V=0,W=0,X=0;f=i;i=i+96|0;g=f|0;j=f+8|0;k=f+16|0;l=f+24|0;m=f+32|0;n=f+48|0;o=f+64|0;p=f+72|0;q=f+80|0;r=f+88|0;s=c[9006]|0;t=c[9010]|0;u=s-t|0;v=(u|0)>-1?u:-u|0;w=(v|0)>24?24:v;v=s+d|0;x=+(w|0);y=+(u|0)/x;u=t+d|0;t=b+64|0;s=c[t>>2]|0;if((s|0)==1){z=8345}else if((s|0)==3){if(+h[b+72>>3]>=0.0){z=8345}}else if((s|0)==2){if((c[b+28>>2]|0)!=-6){z=8345}}if((z|0)==8345){fn(t,c[3524]|0);t=c[13542]|0;z=c[3524]|0;c[13542]=(c[z+96>>2]&128|0)==0?55520:0;cM[c[z+168>>2]&511](8);z=(c[9006]|0)+d|0;c[o>>2]=(c[9010]|0)+d;c[p>>2]=e;c[q>>2]=z;c[r>>2]=e;z=c[3524]|0;if((fl(o,p,q,r)|0)!=0){cN[c[z+56>>2]&255](c[o>>2]|0,c[p>>2]|0);cN[c[z+60>>2]&255](c[q>>2]|0,c[r>>2]|0)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=t;i=f;return}t=(a[b+239|0]&1)==0;r=c[b+260>>2]|0;if((r|0)==0){i=f;return}q=c[b+252>>2]|0;b=0;z=r;A=-8.988465674311579e+307;B=-8.988465674311579e+307;C=8.988465674311579e+307;while(1){if((b|0)>=(q|0)){D=B;E=C;break}r=c[z+12>>2]|0;p=c[z+8>>2]|0;if((p|0)>0){o=0;F=A;G=B;H=C;while(1){do{if((c[r+(o<<6)>>2]|0)==0){if(t){I=r+(o<<6)+24|0}else{I=r+(o<<6)+32|0}J=+h[I>>3];d=J<H;K=d?J:H;L=d?G:F;if(J<=L){M=L;N=G;O=K;break}M=J;N=J;O=K}else{M=F;N=G;O=H}}while(0);d=o+1|0;if((d|0)<(p|0)){o=d;F=M;G=N;H=O}else{P=M;Q=N;R=O;break}}}else{P=A;Q=B;R=C}o=c[z>>2]|0;if((o|0)==0){D=Q;E=R;break}else{b=b+1|0;z=o;A=P;B=Q;C=R}}if(E>D){i=f;return}R=+h[8341];C=E>R?E:R;E=+h[8342];Q=D<E?D:E;do{if(R<C){if(E<=C){z=a[20668]|0;S=+(z<<24>>24==112&1|0);T=z;break}D=(C-R)/(E-R);z=a[20668]|0;if(z<<24>>24==112){S=D;T=112;break}S=1.0-D;T=z}else{z=a[20668]|0;S=+(z<<24>>24!=112&1|0);T=z}}while(0);do{if(R<Q){if(E<=Q){U=+(T<<24>>24==112&1|0);break}C=(Q-R)/(E-R);if(T<<24>>24==112){U=C;break}U=1.0-C}else{U=+(T<<24>>24!=112&1|0)}}while(0);R=(U-S)/x;if((c[9031]|0)==3){c[8490]=u;c[8488]=e}else{cN[c[(c[3524]|0)+56>>2]&255](u,e)}if((w|0)<1){i=f;return}T=m|0;z=m+8|0;b=m+4|0;I=n|0;t=n+8|0;q=n+4|0;o=u;p=1;while(1){if((p|0)==(w|0)){r=c[3524]|0;d=r+144|0;if((c[d>>2]|0)==0){V=r}else{c[T>>2]=5;h[z>>3]=U;c[b>>2]=0;cM[c[d>>2]&511](m);V=c[3524]|0}cN[c[V+56>>2]&255](o,e);W=v}else{x=+(p|0);d=c[3524]|0;r=d+144|0;if((c[r>>2]|0)==0){X=d}else{c[I>>2]=5;h[t>>3]=S+R*x;c[q>>2]=0;cM[c[r>>2]&511](n);X=c[3524]|0}cN[c[X+56>>2]&255](o,e);W=~~(y*x+.5)+u|0}if((c[9031]|0)==3){r=c[8488]|0;c[g>>2]=c[8490];c[j>>2]=r;c[k>>2]=W;c[l>>2]=e;r=c[3524]|0;if((fl(g,j,k,l)|0)!=0){cN[c[r+56>>2]&255](c[g>>2]|0,c[j>>2]|0);cN[c[r+60>>2]&255](c[k>>2]|0,c[l>>2]|0)}c[8490]=W;c[8488]=e}else{cN[c[(c[3524]|0)+60>>2]&255](W,e)}r=p+1|0;if((r|0)>(w|0)){break}else{o=W;p=r}}i=f;return}function fB(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0.0,au=0.0,av=0.0,aw=0.0,ax=0.0,ay=0.0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0.0,aG=0.0,aH=0.0,aI=0.0,aJ=0.0,aK=0.0,aL=0.0,aM=0,aN=0,aO=0,aP=0.0,aQ=0.0,aR=0.0,aS=0.0,aT=0.0,aU=0.0,aV=0.0,aW=0.0,aX=0.0,aY=0.0,aZ=0.0,a_=0,a$=0,a0=0,a1=0,a2=0.0,a3=0.0,a4=0.0,a5=0.0,a6=0.0,a7=0.0,a8=0.0,a9=0.0,ba=0.0,bb=0.0,bc=0.0,bd=0,be=0.0,bf=0.0,bg=0.0,bh=0.0,bi=0.0,bj=0.0,bk=0.0,bl=0.0,bm=0.0,bn=0.0,bo=0.0,bp=0.0,bq=0.0,br=0.0,bs=0.0,bt=0.0,bu=0.0,bv=0.0,bw=0.0,bx=0.0,by=0.0,bz=0.0,bA=0.0,bB=0.0,bC=0.0,bD=0.0,bE=0.0,bF=0.0,bG=0.0,bH=0.0,bI=0.0,bJ=0.0,bK=0,bL=0,bM=0;d=i;i=i+208|0;e=d|0;f=d+8|0;g=d+16|0;j=d+24|0;k=d+32|0;l=d+48|0;m=d+56|0;n=d+64|0;o=d+72|0;p=d+80|0;q=d+96|0;r=d+104|0;s=d+112|0;t=d+120|0;u=d+128|0;w=d+144|0;x=d+152|0;y=d+160|0;z=d+168|0;A=d+176|0;B=d+192|0;C=d+200|0;D=C;E=i;i=i+8|0;F=E;G=i;i=i+8|0;H=i;i=i+8|0;I=i;i=i+8|0;J=i;i=i+16|0;K=i;i=i+16|0;L=i;i=i+16|0;c[C>>2]=0;c[C+4>>2]=0;c[E>>2]=0;c[E+4>>2]=0;M=a[b+239|0]|0;N=b+64|0;O=c[N>>2]|0;if((O|0)==3){fn(N,c[3524]|0);fC(b);i=d;return}else if((O|0)==2){fC(b);i=d;return}else{do{if((c[b+244>>2]|0)!=0){if((a[38984]&1)==0){break}i=d;return}}while(0);O=B|0;N=B+4|0;P=F+4|0;Q=D+4|0;R=b+260|0;S=b+252|0;i4(c[R>>2]|0,c[S>>2]|0,O,C);C=c[S>>2]|0;c[E>>2]=C;E=c[R>>2]|0;if((C|0)>0){R=0;S=E;while(1){b=R+1|0;T=c[S>>2]|0;if((b|0)<(C|0)){R=b;S=T}else{U=T;break}}}else{U=E}do{if((U|0)==0){V=8408}else{E=U;S=0;do{S=S+1|0;E=c[E>>2]|0;}while((E|0)!=0);if((S|0)<=0){V=8408;break}c[P>>2]=S;i4(U,S,N,Q)}}while(0);if((V|0)==8408){c[N>>2]=0}Q=(M&1)==0;M=A|0;U=A+8|0;P=A+4|0;E=u|0;R=u+8|0;C=u+4|0;T=p|0;b=p+8|0;W=p+4|0;X=J|0;Y=K|0;$=L|0;aa=J+8|0;J=K+8|0;K=L+8|0;L=k|0;ab=k+8|0;ac=k+4|0;ad=0;L11450:do{ae=(c[D+(ad<<2)>>2]|0)==0;af=ae?1:-1;ag=c[F+(ad<<2)>>2]|0;L11452:do{if((ag|0)>0){ah=c[B+(ad<<2)>>2]|0;ai=(ah|0)==0;aj=0;ak=0;do{if(ai){break L11452}al=c[ah+(aj<<2)>>2]|0;am=c[al+8>>2]|0;ak=ae?ak:am-1|0;an=c[al+12>>2]|0;ao=al+8|0;if((am|0)>0){am=2;al=0;ap=ak;while(1){aq=an+(ap<<6)|0;ar=c[aq>>2]|0;do{if((ar|0)==0){as=an+(ap<<6)+24|0;at=+h[as>>3];au=(+h[an+(ap<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;av=(+h[an+(ap<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aw=(at- +h[5279])*+h[2]+ +h[12]+-1.0;ax=+h[406]+au*+h[394]+av*+h[398]+aw*+h[402];ay=ax==0.0?1.0e-5:ax;az=~~((+h[403]+au*+h[391]+av*+h[395]+aw*+h[399])/ay*+(c[180]|0))+(c[186]|0)|0;aA=~~((+h[404]+au*+h[392]+av*+h[396]+aw*+h[400])/ay*+(c[40]|0))+(c[46]|0)|0;if((am|0)==0){aB=ap-af|0;if(Q){ay=+h[an+(aB<<6)+24>>3];aC=c[34]|0;aD=(a[64788+(aC*688&-1)|0]&1)==0;aE=(a[66852]&1)==0;do{if(aD){if(aE){aF=at;aG=ay;break}if(ay>0.0){aw=+_(+ay);aH=aw/+h[8358];V=8428;break}else{aI=+h[8341];V=8427;break}}else{if(aE){aI=+Z(+(ay*+h[64800+(aC*688&-1)>>3]));V=8427;break}if(+h[64792+(aC*688&-1)>>3]==+h[8357]){aJ=ay;V=8434;break}aJ=ay*+h[64800+(aC*688&-1)>>3]/+h[8358];V=8434}}while(0);do{if((V|0)==8427){V=0;if(aD){aH=aI;V=8428;break}if(!aE){aJ=aI;V=8434;break}aF=+Z(+(at*+h[64800+(aC*688&-1)>>3]));aG=aI}}while(0);do{if((V|0)==8428){V=0;if(aE){aF=at;aG=aH;break}if(at>0.0){ay=+_(+at);aF=ay/+h[8358];aG=aH;break}else{aF=+h[8341];aG=aH;break}}else if((V|0)==8434){V=0;if(+h[64792+(aC*688&-1)>>3]==+h[8357]){aF=at;aG=aJ;break}aF=at*+h[64800+(aC*688&-1)>>3]/+h[8358];aG=aJ}}while(0);aK=aG+aF}else{aK=+h[an+(aB<<6)+32>>3]+ +h[an+(ap<<6)+32>>3]}at=aK*.5;ay=+h[8341];do{if(ay<at){aw=+h[8342];if(aw<=at){aL=+((a[20668]|0)==112&1|0);break}av=(at-ay)/(aw-ay);if((a[20668]|0)==112){aL=av;break}aL=1.0-av}else{aL=+((a[20668]|0)!=112&1|0)}}while(0);aB=c[3524]|0;aC=aB+144|0;if((c[aC>>2]|0)==0){aM=aB}else{c[M>>2]=5;h[U>>3]=aL;c[P>>2]=0;cM[c[aC>>2]&511](A);aM=c[3524]|0}aC=c[8488]|0;c[w>>2]=c[8490];c[x>>2]=aC;c[y>>2]=az;c[z>>2]=aA;if((fl(w,x,y,z)|0)!=0){cN[c[aM+56>>2]&255](c[w>>2]|0,c[x>>2]|0);cN[c[aM+60>>2]&255](c[y>>2]|0,c[z>>2]|0)}c[8490]=az;c[8488]=aA;break}else if((am|0)==1){if((a[54160]&1)==0){c[8490]=az;c[8488]=aA;break}aC=ap-af|0;ul(an,aC,ap,G,H,I);ay=(+h[G>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;at=(+h[H>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;av=(+h[I>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;aw=+h[406]+ay*+h[394]+at*+h[398]+av*+h[402];au=aw==0.0?1.0e-5:aw;aB=~~((+h[403]+ay*+h[391]+at*+h[395]+av*+h[399])/au*+(c[180]|0))+(c[186]|0)|0;aE=~~((+h[404]+ay*+h[392]+at*+h[396]+av*+h[400])/au*+(c[40]|0))+(c[46]|0)|0;c[8490]=aB;c[8488]=aE;if(Q){au=+h[an+(aC<<6)+24>>3];aD=c[34]|0;aN=(a[64788+(aD*688&-1)|0]&1)==0;aO=(a[66852]&1)==0;L11513:do{if(aN){do{if(aO){aP=au}else{if(au>0.0){av=+_(+au);aP=av/+h[8358];break}else{aQ=+h[8341];V=8463;break L11513}}}while(0);aR=aP;aS=+h[as>>3];V=8464}else{if(aO){aQ=+Z(+(au*+h[64800+(aD*688&-1)>>3]));V=8463;break}if(+h[64792+(aD*688&-1)>>3]==+h[8357]){aT=au}else{aT=au*+h[64800+(aD*688&-1)>>3]/+h[8358]}aU=aT;aV=+h[as>>3];V=8465}}while(0);if((V|0)==8463){V=0;au=+h[as>>3];if(aN){aR=aQ;aS=au;V=8464}else{aU=aQ;aV=au;V=8465}}do{if((V|0)==8464){V=0;if(aO){aW=aS;aX=aR;break}if(aS>0.0){au=+_(+aS);aW=au/+h[8358];aX=aR;break}else{aW=+h[8341];aX=aR;break}}else if((V|0)==8465){V=0;if(aO){aW=+Z(+(aV*+h[64800+(aD*688&-1)>>3]));aX=aU;break}if(+h[64792+(aD*688&-1)>>3]==+h[8357]){aW=aV;aX=aU;break}aW=aV*+h[64800+(aD*688&-1)>>3]/+h[8358];aX=aU}}while(0);aY=aX+aW}else{aY=+h[an+(aC<<6)+32>>3]+ +h[an+(ap<<6)+32>>3]}au=aY*.5;av=+h[8341];do{if(av<au){at=+h[8342];if(at<=au){aZ=+((a[20668]|0)==112&1|0);break}ay=(au-av)/(at-av);if((a[20668]|0)==112){aZ=ay;break}aZ=1.0-ay}else{aZ=+((a[20668]|0)!=112&1|0)}}while(0);aC=c[3524]|0;aD=aC+144|0;if((c[aD>>2]|0)==0){a_=aB;a$=aE;a0=aC}else{c[E>>2]=5;h[R>>3]=aZ;c[C>>2]=0;cM[c[aD>>2]&511](u);a_=c[8490]|0;a$=c[8488]|0;a0=c[3524]|0}c[q>>2]=a_;c[r>>2]=a$;c[s>>2]=az;c[t>>2]=aA;if((fl(q,r,s,t)|0)!=0){cN[c[a0+56>>2]&255](c[q>>2]|0,c[r>>2]|0);cN[c[a0+60>>2]&255](c[s>>2]|0,c[t>>2]|0)}c[8490]=az;c[8488]=aA;break}else{c[8490]=az;c[8488]=aA;break}}else if((ar|0)==1){if((am|0)==0){if((a[54160]&1)==0){break}aD=ap-af|0;ul(an,aD,ap,G,H,I);av=(+h[G>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;au=(+h[H>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ay=(+h[I>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;at=+h[406]+av*+h[394]+au*+h[398]+ay*+h[402];aw=at==0.0?1.0e-5:at;aC=~~((+h[403]+av*+h[391]+au*+h[395]+ay*+h[399])/aw*+(c[180]|0))+(c[186]|0)|0;aO=~~((+h[404]+av*+h[392]+au*+h[396]+ay*+h[400])/aw*+(c[40]|0))+(c[46]|0)|0;if(Q){aw=+h[an+(aD<<6)+24>>3];aN=c[34]|0;as=(a[64788+(aN*688&-1)|0]&1)==0;a1=(a[66852]&1)==0;L11565:do{if(as){do{if(a1){a2=aw}else{if(aw>0.0){ay=+_(+aw);a2=ay/+h[8358];break}else{a3=+h[8341];V=8500;break L11565}}}while(0);a4=a2;a5=+h[an+(ap<<6)+24>>3];V=8501}else{if(a1){a3=+Z(+(aw*+h[64800+(aN*688&-1)>>3]));V=8500;break}if(+h[64792+(aN*688&-1)>>3]==+h[8357]){a6=aw}else{a6=aw*+h[64800+(aN*688&-1)>>3]/+h[8358]}a7=a6;a8=+h[an+(ap<<6)+24>>3];V=8502}}while(0);if((V|0)==8500){V=0;aw=+h[an+(ap<<6)+24>>3];if(as){a4=a3;a5=aw;V=8501}else{a7=a3;a8=aw;V=8502}}do{if((V|0)==8501){V=0;if(a1){a9=a5;ba=a4;break}if(a5>0.0){aw=+_(+a5);a9=aw/+h[8358];ba=a4;break}else{a9=+h[8341];ba=a4;break}}else if((V|0)==8502){V=0;if(a1){a9=+Z(+(a8*+h[64800+(aN*688&-1)>>3]));ba=a7;break}if(+h[64792+(aN*688&-1)>>3]==+h[8357]){a9=a8;ba=a7;break}a9=a8*+h[64800+(aN*688&-1)>>3]/+h[8358];ba=a7}}while(0);bb=ba+a9}else{bb=+h[an+(aD<<6)+32>>3]+ +h[an+(ap<<6)+32>>3]}aw=bb*.5;ay=+h[8341];do{if(ay<aw){au=+h[8342];if(au<=aw){bc=+((a[20668]|0)==112&1|0);break}av=(aw-ay)/(au-ay);if((a[20668]|0)==112){bc=av;break}bc=1.0-av}else{bc=+((a[20668]|0)!=112&1|0)}}while(0);aD=c[3524]|0;aN=aD+144|0;if((c[aN>>2]|0)==0){bd=aD}else{c[T>>2]=5;h[b>>3]=bc;c[W>>2]=0;cM[c[aN>>2]&511](p);bd=c[3524]|0}aN=c[8488]|0;c[l>>2]=c[8490];c[m>>2]=aN;c[n>>2]=aC;c[o>>2]=aO;if((fl(l,m,n,o)|0)!=0){cN[c[bd+56>>2]&255](c[l>>2]|0,c[m>>2]|0);cN[c[bd+60>>2]&255](c[n>>2]|0,c[o>>2]|0)}c[8490]=aC;c[8488]=aO;break}else if((am|0)!=1){break}if((a[54152]&1)==0){break}aN=ap-af|0;if(!(um(an,aN,ap,X,Y,$)|0)){break}ay=+h[64664+((c[200]|0)*688&-1)>>3];aw=+h[91];av=+h[96];au=(+h[X>>3]-ay)*aw+av+-1.0;at=+h[64664+((c[144]|0)*688&-1)>>3];ax=+h[21];be=+h[68];bf=(+h[Y>>3]-at)*ax+be+-1.0;bg=+h[5279];bh=+h[2];bi=+h[12];bj=(+h[$>>3]-bg)*bh+bi+-1.0;bk=+h[403];bl=+h[391];bm=+h[395];bn=+h[399];bo=+h[404];bp=+h[392];bq=+h[396];br=+h[400];bs=+h[406];bt=+h[394];bu=+h[398];bv=+h[402];bw=bs+au*bt+bf*bu+bj*bv;bx=bw==0.0?1.0e-5:bw;bw=+(c[180]|0);aD=c[186]|0;a1=~~((bk+au*bl+bf*bm+bj*bn)/bx*bw)+aD|0;by=+(c[40]|0);as=c[46]|0;aA=~~((bo+au*bp+bf*bq+bj*br)/bx*by)+as|0;bx=av+aw*(+h[aa>>3]-ay)+-1.0;ay=be+ax*(+h[J>>3]-at)+-1.0;at=bi+bh*(+h[K>>3]-bg)+-1.0;bg=bs+bt*bx+bu*ay+bv*at;bv=bg==0.0?1.0e-5:bg;az=~~(bw*((bk+bl*bx+bm*ay+bn*at)/bv))+aD|0;aD=~~(by*((bo+bp*bx+bq*ay+br*at)/bv))+as|0;c[8490]=a1;c[8488]=aA;if(Q){bv=+h[an+(aN<<6)+24>>3];as=c[34]|0;aE=(a[64788+(as*688&-1)|0]&1)==0;aB=(a[66852]&1)==0;L11615:do{if(aE){do{if(aB){bz=bv}else{if(bv>0.0){at=+_(+bv);bz=at/+h[8358];break}else{bA=+h[8341];V=8536;break L11615}}}while(0);bB=bz;bC=+h[an+(ap<<6)+24>>3];V=8537}else{if(aB){bA=+Z(+(bv*+h[64800+(as*688&-1)>>3]));V=8536;break}if(+h[64792+(as*688&-1)>>3]==+h[8357]){bD=bv}else{bD=bv*+h[64800+(as*688&-1)>>3]/+h[8358]}bE=bD;bF=+h[an+(ap<<6)+24>>3];V=8538}}while(0);if((V|0)==8536){V=0;bv=+h[an+(ap<<6)+24>>3];if(aE){bB=bA;bC=bv;V=8537}else{bE=bA;bF=bv;V=8538}}do{if((V|0)==8537){V=0;if(aB){bG=bC;bH=bB;break}if(bC>0.0){bv=+_(+bC);bG=bv/+h[8358];bH=bB;break}else{bG=+h[8341];bH=bB;break}}else if((V|0)==8538){V=0;if(aB){bG=+Z(+(bF*+h[64800+(as*688&-1)>>3]));bH=bE;break}if(+h[64792+(as*688&-1)>>3]==+h[8357]){bG=bF;bH=bE;break}bG=bF*+h[64800+(as*688&-1)>>3]/+h[8358];bH=bE}}while(0);bI=bH+bG}else{bI=+h[an+(aN<<6)+32>>3]+ +h[an+(ap<<6)+32>>3]}bv=bI*.5;at=+h[8341];do{if(at<bv){br=+h[8342];if(br<=bv){bJ=+((a[20668]|0)==112&1|0);break}ay=(bv-at)/(br-at);if((a[20668]|0)==112){bJ=ay;break}bJ=1.0-ay}else{bJ=+((a[20668]|0)!=112&1|0)}}while(0);aN=c[3524]|0;as=aN+144|0;if((c[as>>2]|0)==0){bK=a1;bL=aA;bM=aN}else{c[L>>2]=5;h[ab>>3]=bJ;c[ac>>2]=0;cM[c[as>>2]&511](k);bK=c[8490]|0;bL=c[8488]|0;bM=c[3524]|0}c[e>>2]=bK;c[f>>2]=bL;c[g>>2]=az;c[j>>2]=aD;if((fl(e,f,g,j)|0)!=0){cN[c[bM+56>>2]&255](c[e>>2]|0,c[f>>2]|0);cN[c[bM+60>>2]&255](c[g>>2]|0,c[j>>2]|0)}c[8490]=az;c[8488]=aD}else if((ar|0)!=2){V=8557;break L11450}}while(0);ar=al+1|0;if((ar|0)<(c[ao>>2]|0)){am=c[aq>>2]|0;al=ar;ap=ap+af|0}else{break}}}aj=aj+1|0;}while((aj|0)<(ag|0))}}while(0);ad=ad+1|0;}while((ad|0)<2);if((V|0)==8557){uk(203224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}V=c[O>>2]|0;if((V|0)!=0){uu(V)}V=c[N>>2]|0;if((V|0)==0){i=d;return}uu(V);i=d;return}}function fC(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0.0,V=0.0,W=0.0,X=0.0,Y=0.0,Z=0,_=0,$=0,aa=0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0.0,ai=0.0,aj=0.0,ak=0.0,al=0.0,am=0.0,an=0.0,ao=0.0,ap=0.0,aq=0.0,ar=0.0,as=0.0,at=0.0,au=0.0,av=0.0,aw=0.0,ax=0,ay=0;d=i;i=i+216|0;e=d|0;f=d+8|0;g=d+16|0;j=d+24|0;k=d+32|0;l=d+40|0;m=d+48|0;n=d+56|0;o=d+64|0;p=d+72|0;q=d+80|0;r=d+88|0;s=d+96|0;t=d+104|0;u=d+112|0;w=d+120|0;x=d+128|0;y=d+144|0;z=d+152|0;A=d+160|0;B=d+168|0;C=d+184|0;D=d+200|0;E=c[b+260>>2]|0;do{if((c[b+244>>2]|0)!=0){if((a[38984]&1)==0){break}i=d;return}}while(0);do{if((a[b+239|0]&1)==0){F=0}else{if((c[b+64>>2]|0)!=3){F=0;break}F=+h[b+72>>3]<0.0}}while(0);if((E|0)==0){i=d;return}G=x|0;H=x+4|0;I=x+8|0;J=B|0;K=C|0;L=D|0;M=B+8|0;B=C+8|0;C=D+8|0;D=b+64|0;N=D|0;O=b+68|0;b=E;L11686:while(1){E=c[b+12>>2]|0;P=b+8|0;if((c[P>>2]|0)>0){Q=0;R=2;while(1){do{if(F){S=(c[3524]|0)+144|0;if((c[S>>2]|0)==0){break}T=~~+h[E+(Q<<6)+32>>3];c[G>>2]=3;c[H>>2]=T;h[I>>3]=0.0;cM[c[S>>2]&511](x)}else{if((c[N>>2]|0)!=2){break}c[O>>2]=~~+h[E+(Q<<6)+32>>3];fn(D,c[3524]|0)}}while(0);S=E+(Q<<6)|0;T=c[S>>2]|0;do{if((T|0)==0){U=(+h[E+(Q<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;V=(+h[E+(Q<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;W=(+h[E+(Q<<6)+24>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;X=+h[406]+U*+h[394]+V*+h[398]+W*+h[402];Y=X==0.0?1.0e-5:X;Z=~~((+h[403]+U*+h[391]+V*+h[395]+W*+h[399])/Y*+(c[180]|0))+(c[186]|0)|0;_=~~((+h[404]+U*+h[392]+V*+h[396]+W*+h[400])/Y*+(c[40]|0))+(c[46]|0)|0;if((R|0)==0){$=c[8488]|0;c[s>>2]=c[8490];c[t>>2]=$;c[u>>2]=Z;c[w>>2]=_;$=c[3524]|0;if((fl(s,t,u,w)|0)!=0){cN[c[$+56>>2]&255](c[s>>2]|0,c[t>>2]|0);cN[c[$+60>>2]&255](c[u>>2]|0,c[w>>2]|0)}c[8490]=Z;c[8488]=_;break}else if((R|0)==1){if((a[54160]&1)==0){c[8490]=Z;c[8488]=_;break}ul(E,Q-1|0,Q,y,z,A);Y=(+h[y>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;W=(+h[z>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;V=(+h[A>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;U=+h[406]+Y*+h[394]+W*+h[398]+V*+h[402];X=U==0.0?1.0e-5:U;$=~~((+h[403]+Y*+h[391]+W*+h[395]+V*+h[399])/X*+(c[180]|0))+(c[186]|0)|0;aa=~~((+h[404]+Y*+h[392]+W*+h[396]+V*+h[400])/X*+(c[40]|0))+(c[46]|0)|0;c[8490]=$;c[8488]=aa;c[k>>2]=$;c[l>>2]=aa;c[m>>2]=Z;c[n>>2]=_;aa=c[3524]|0;if((fl(k,l,m,n)|0)!=0){cN[c[aa+56>>2]&255](c[k>>2]|0,c[l>>2]|0);cN[c[aa+60>>2]&255](c[m>>2]|0,c[n>>2]|0)}c[8490]=Z;c[8488]=_;break}else{c[8490]=Z;c[8488]=_;break}}else if((T|0)==1){if((R|0)==0){if((a[54160]&1)==0){break}ul(E,Q-1|0,Q,y,z,A);X=(+h[y>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;V=(+h[z>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;W=(+h[A>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;Y=+h[406]+X*+h[394]+V*+h[398]+W*+h[402];U=Y==0.0?1.0e-5:Y;_=~~((+h[403]+X*+h[391]+V*+h[395]+W*+h[399])/U*+(c[180]|0))+(c[186]|0)|0;Z=~~((+h[404]+X*+h[392]+V*+h[396]+W*+h[400])/U*+(c[40]|0))+(c[46]|0)|0;aa=c[8488]|0;c[e>>2]=c[8490];c[f>>2]=aa;c[g>>2]=_;c[j>>2]=Z;aa=c[3524]|0;if((fl(e,f,g,j)|0)!=0){cN[c[aa+56>>2]&255](c[e>>2]|0,c[f>>2]|0);cN[c[aa+60>>2]&255](c[g>>2]|0,c[j>>2]|0)}c[8490]=_;c[8488]=Z;break}else if((R|0)!=1){break}if((a[54152]&1)==0){break}if(!(um(E,Q-1|0,Q,J,K,L)|0)){break}U=+h[64664+((c[200]|0)*688&-1)>>3];W=+h[91];V=+h[96];X=(+h[J>>3]-U)*W+V+-1.0;Y=+h[64664+((c[144]|0)*688&-1)>>3];ab=+h[21];ac=+h[68];ad=(+h[K>>3]-Y)*ab+ac+-1.0;ae=+h[5279];af=+h[2];ag=+h[12];ah=(+h[L>>3]-ae)*af+ag+-1.0;ai=+h[403];aj=+h[391];ak=+h[395];al=+h[399];am=+h[404];an=+h[392];ao=+h[396];ap=+h[400];aq=+h[406];ar=+h[394];as=+h[398];at=+h[402];au=aq+X*ar+ad*as+ah*at;av=au==0.0?1.0e-5:au;au=+(c[180]|0);Z=c[186]|0;_=~~((ai+X*aj+ad*ak+ah*al)/av*au)+Z|0;aw=+(c[40]|0);aa=c[46]|0;$=~~((am+X*an+ad*ao+ah*ap)/av*aw)+aa|0;av=V+W*(+h[M>>3]-U)+-1.0;U=ac+ab*(+h[B>>3]-Y)+-1.0;Y=ag+af*(+h[C>>3]-ae)+-1.0;ae=aq+ar*av+as*U+at*Y;at=ae==0.0?1.0e-5:ae;ax=~~(au*((ai+aj*av+ak*U+al*Y)/at))+Z|0;Z=~~(aw*((am+an*av+ao*U+ap*Y)/at))+aa|0;c[8490]=_;c[8488]=$;c[o>>2]=_;c[p>>2]=$;c[q>>2]=ax;c[r>>2]=Z;$=c[3524]|0;if((fl(o,p,q,r)|0)!=0){cN[c[$+56>>2]&255](c[o>>2]|0,c[p>>2]|0);cN[c[$+60>>2]&255](c[q>>2]|0,c[r>>2]|0)}c[8490]=ax;c[8488]=Z}else if((T|0)!=2){ay=8605;break L11686}}while(0);T=Q+1|0;if((T|0)<(c[P>>2]|0)){Q=T;R=c[S>>2]|0}else{break}}}R=c[b>>2]|0;if((R|0)==0){ay=8611;break}else{b=R}}if((ay|0)==8605){uk(203224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ay|0)==8611){i=d;return}}function fD(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0.0,u=0.0,v=0,w=0,x=0.0,y=0.0,z=0.0,A=0.0,B=0,C=0.0,D=0.0,E=0.0,F=0,G=0.0,H=0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,P=0.0,Q=0.0,R=0,S=0.0,T=0.0,U=0;g=i;i=i+16|0;j=g|0;k=c[13542]|0;l=c[9006]|0;m=l+d|0;n=c[9010]|0;o=n+d|0;p=b+64|0;q=c[p>>2]|0;r=c[3524]|0;if((f|0)==-1){s=1.0}else{s=((+h[3817]+-1.0)*.5+1.0)*3.0}t=+(l-n|0);n=~~(t/(+((c[r+20>>2]|0)>>>0>>>0)*s)+.5);l=(n|0)<2?2:n;s=+(l|0);u=t/s;if((q|0)==1){v=8617}else if((q|0)==3){if(+h[b+72>>3]>=0.0){v=8617}}else if((q|0)==2){if((c[b+28>>2]|0)!=-6){v=8617}}if((v|0)==8617){fn(p,r);p=c[13542]|0;q=c[3524]|0;c[13542]=(c[q+96>>2]&128|0)==0?55520:0;cM[c[q+168>>2]&511](8);q=(c[9018]|0)+d|0;d=c[13542]|0;if((d|0)==0){v=8619}else{n=(c[d>>2]|0)>(q|0)&1;w=(c[d+4>>2]|0)<(q|0)?n|2:n;n=(c[d+8>>2]|0)>(e|0)?w|4:w;if((((c[d+12>>2]|0)<(e|0)?n|8:n)|0)==0){v=8619}}if((v|0)==8619){cR[c[(c[3524]|0)+80>>2]&127](q,e,f)}cM[c[(c[3524]|0)+168>>2]&511](9);c[13542]=p;i=g;return}p=(a[b+239|0]&1)==0;q=c[b+260>>2]|0;if((q|0)==0){i=g;return}n=c[b+252>>2]|0;b=0;d=q;t=-8.988465674311579e+307;x=-8.988465674311579e+307;y=8.988465674311579e+307;while(1){if((b|0)>=(n|0)){z=x;A=y;break}q=c[d+12>>2]|0;w=c[d+8>>2]|0;if((w|0)>0){B=0;C=t;D=x;E=y;while(1){do{if((c[q+(B<<6)>>2]|0)==0){if(p){F=q+(B<<6)+24|0}else{F=q+(B<<6)+32|0}G=+h[F>>3];H=G<E;I=H?G:E;J=H?D:C;if(G<=J){K=J;L=D;M=I;break}K=G;L=G;M=I}else{K=C;L=D;M=E}}while(0);H=B+1|0;if((H|0)<(w|0)){B=H;C=K;D=L;E=M}else{N=K;O=L;P=M;break}}}else{N=t;O=x;P=y}B=c[d>>2]|0;if((B|0)==0){z=O;A=P;break}else{b=b+1|0;d=B;t=N;x=O;y=P}}if(A>z){i=g;return}P=+h[8341];y=A>P?A:P;A=+h[8342];O=z<A?z:A;do{if(P<y){if(A<=y){d=a[20668]|0;Q=+(d<<24>>24==112&1|0);R=d;break}z=(y-P)/(A-P);d=a[20668]|0;if(d<<24>>24==112){Q=z;R=112;break}Q=1.0-z;R=d}else{d=a[20668]|0;Q=+(d<<24>>24!=112&1|0);R=d}}while(0);do{if(P<O){if(A<=O){S=+(R<<24>>24==112&1|0);break}y=(O-P)/(A-P);if(R<<24>>24==112){S=y;break}S=1.0-y}else{S=+(R<<24>>24!=112&1|0)}}while(0);P=(S-Q)/s;c[13542]=(c[r+96>>2]&128|0)==0?55520:0;if((l|0)>=0){r=j|0;R=j+8|0;d=j+4|0;b=0;do{F=(b|0)==(l|0);if(F){T=S}else{T=Q+P*+(b|0)}p=(c[3524]|0)+144|0;if((c[p>>2]|0)!=0){c[r>>2]=5;h[R>>3]=T;c[d>>2]=0;cM[c[p>>2]&511](j)}p=(b|0)==0;if(p|F){U=p?o:m}else{U=~~(u*+(b|0)+.5)+o|0}p=c[13542]|0;if((p|0)==0){v=8657}else{F=(c[p>>2]|0)>(U|0)&1;n=(c[p+4>>2]|0)<(U|0)?F|2:F;F=(c[p+8>>2]|0)>(e|0)?n|4:n;if((((c[p+12>>2]|0)<(e|0)?F|8:F)|0)==0){v=8657}}if((v|0)==8657){v=0;cR[c[(c[3524]|0)+80>>2]&127](U,e,f)}b=b+1|0;}while((b|0)<=(l|0))}c[13542]=k;i=g;return}function fE(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0,H=0,I=0,J=0,K=0,L=0;e=i;i=i+16|0;f=e|0;g=c[3524]|0;j=c[b+260>>2]|0;if((j|0)==0){i=e;return}k=b+64|0;l=b+239|0;m=b+72|0;n=b+68|0;o=f|0;p=f+4|0;q=f+8|0;r=b+12|0;s=g+80|0;t=b+48|0;u=g+92|0;g=j;do{j=(c[k>>2]|0)==3;if((a[l]&1)!=0&j){v=+h[m>>3]<0.0}else{v=0}do{if(!(v|j^1)){w=c[n>>2]|0;x=(c[3524]|0)+144|0;if((c[x>>2]|0)==0){break}c[o>>2]=3;c[p>>2]=w;h[q>>3]=0.0;cM[c[x>>2]&511](f)}}while(0);j=g+8|0;x=c[j>>2]|0;if((x|0)>0){w=g+12|0;y=0;z=x;while(1){x=c[w>>2]|0;A=x+(y<<6)|0;do{if((c[A>>2]|0)==0){B=(+h[x+(y<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;C=(+h[x+(y<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;D=(+h[x+(y<<6)+24>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;E=+h[406]+B*+h[394]+C*+h[398]+D*+h[402];F=E==0.0?1.0e-5:E;G=~~((+h[403]+B*+h[391]+C*+h[395]+D*+h[399])/F*+(c[180]|0))+(c[186]|0)|0;H=~~((+h[404]+B*+h[392]+C*+h[396]+D*+h[400])/F*+(c[40]|0))+(c[46]|0)|0;I=c[13542]|0;if((I|0)!=0){J=(c[I>>2]|0)>(G|0)&1;K=(c[I+4>>2]|0)<(G|0)?J|2:J;J=(c[I+8>>2]|0)>(H|0)?K|4:K;if((((c[I+12>>2]|0)<(H|0)?J|8:J)|0)!=0){L=z;break}}fL(b,A);J=c[r>>2]|0;do{if((J|0)==18|(J|0)==51){if(+h[t>>3]!=-3.0){break}cK[c[u>>2]&63](+h[3817]*+h[x+(y<<6)+48>>3])}}while(0);cR[c[s>>2]&127](G,H,d);L=c[j>>2]|0}else{L=z}}while(0);x=y+1|0;if((x|0)<(L|0)){y=x;z=L}else{break}}}g=c[g>>2]|0;}while((g|0)!=0);i=e;return}function fF(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;e=i;i=i+48|0;f=e|0;g=c[13542]|0;if((c[5094]|0)!=0){c[13542]=31752}j=c[11690]|0;if((j&2|0)==0){k=j}else{j=b+4|0;l=c[j>>2]|0;m=+h[l+24>>3];n=(+h[l+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;o=(+h[l+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;p=(m- +h[5279])*+h[2]+ +h[12]+-1.0;q=+h[404]+n*+h[392]+o*+h[396]+p*+h[400];r=+h[405]+n*+h[393]+o*+h[397]+p*+h[401];s=+h[406]+n*+h[394]+o*+h[398]+p*+h[402];t=s==0.0?1.0e-5:s;s=(+h[403]+n*+h[391]+o*+h[395]+p*+h[399])/t;l=f|0;h[l>>3]=s;p=q/t;u=f+8|0;h[u>>3]=p;q=r/t;v=f+16|0;h[v>>3]=q;w=f+32|0;h[w>>3]=m;x=f+40|0;c[x>>2]=0;if((a[38984]&1)==0){y=f;c[7612]=c[y>>2];c[7613]=c[y+4>>2];c[7614]=c[y+8>>2];c[7615]=c[y+12>>2];c[7616]=c[y+16>>2];c[7617]=c[y+20>>2];c[7618]=c[y+24>>2];c[7619]=c[y+28>>2];c[7620]=c[y+32>>2];c[7621]=c[y+36>>2];c[7622]=c[y+40>>2];c[7623]=c[y+44>>2];z=8695}else{if(q==-2.0){y=f;c[7612]=c[y>>2];c[7613]=c[y+4>>2];c[7614]=c[y+8>>2];c[7615]=c[y+12>>2];c[7616]=c[y+16>>2];c[7617]=c[y+20>>2];c[7618]=c[y+24>>2];c[7619]=c[y+28>>2];c[7620]=c[y+32>>2];c[7621]=c[y+36>>2];c[7622]=c[y+40>>2];c[7623]=c[y+44>>2]}else{h[v>>3]=q+.01;y=f;c[7612]=c[y>>2];c[7613]=c[y+4>>2];c[7614]=c[y+8>>2];c[7615]=c[y+12>>2];c[7616]=c[y+16>>2];c[7617]=c[y+20>>2];c[7618]=c[y+24>>2];c[7619]=c[y+28>>2];c[7620]=c[y+32>>2];c[7621]=c[y+36>>2];c[7622]=c[y+40>>2];c[7623]=c[y+44>>2]}if((a[46752]&1)==0){z=8695}}if((z|0)==8695){cN[c[(c[3524]|0)+56>>2]&255](~~(s*+(c[180]|0))+(c[186]|0)|0,~~(p*+(c[40]|0))+(c[46]|0)|0)}y=b+44|0;if((c[y>>2]|0)>1){A=1;do{B=c[j>>2]|0;p=+h[B+(A<<6)+24>>3];s=(+h[B+(A<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;q=(+h[B+(A<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;m=(p- +h[5279])*+h[2]+ +h[12]+-1.0;t=+h[404]+s*+h[392]+q*+h[396]+m*+h[400];r=+h[405]+s*+h[393]+q*+h[397]+m*+h[401];o=+h[406]+s*+h[394]+q*+h[398]+m*+h[402];n=o==0.0?1.0e-5:o;h[l>>3]=(+h[403]+s*+h[391]+q*+h[395]+m*+h[399])/n;h[u>>3]=t/n;t=r/n;h[v>>3]=t;h[w>>3]=p;c[x>>2]=0;if(!((a[38984]&1)==0|t==-2.0)){h[v>>3]=t+.01}up(f,d);A=A+1|0;}while((A|0)<(c[y>>2]|0))}k=c[11690]|0}do{if((k&1|0)!=0){y=b+4|0;A=c[y>>2]|0;t=+h[7076];p=(+h[A+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;n=(+h[A+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;r=(t- +h[5279])*+h[2]+ +h[12]+-1.0;m=+h[404]+p*+h[392]+n*+h[396]+r*+h[400];q=+h[405]+p*+h[393]+n*+h[397]+r*+h[401];s=+h[406]+p*+h[394]+n*+h[398]+r*+h[402];o=s==0.0?1.0e-5:s;s=(+h[403]+p*+h[391]+n*+h[395]+r*+h[399])/o;v=f|0;h[v>>3]=s;r=m/o;x=f+8|0;h[x>>3]=r;w=f+16|0;h[w>>3]=q/o;u=f+32|0;h[u>>3]=t;l=f+40|0;c[l>>2]=0;h[u>>3]=+h[A+24>>3];A=f;c[7612]=c[A>>2];c[7613]=c[A+4>>2];c[7614]=c[A+8>>2];c[7615]=c[A+12>>2];c[7616]=c[A+16>>2];c[7617]=c[A+20>>2];c[7618]=c[A+24>>2];c[7619]=c[A+28>>2];c[7620]=c[A+32>>2];c[7621]=c[A+36>>2];c[7622]=c[A+40>>2];c[7623]=c[A+44>>2];if((a[38984]&1)==0){z=8704}else{if((a[46752]&1)==0){z=8704}}if((z|0)==8704){cN[c[(c[3524]|0)+56>>2]&255](~~(s*+(c[180]|0))+(c[186]|0)|0,~~(r*+(c[40]|0))+(c[46]|0)|0)}A=b+44|0;if((c[A>>2]|0)>1){C=1}else{break}do{j=c[y>>2]|0;r=+h[7076];s=(+h[j+(C<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;t=(+h[j+(C<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;o=(r- +h[5279])*+h[2]+ +h[12]+-1.0;q=+h[404]+s*+h[392]+t*+h[396]+o*+h[400];m=+h[405]+s*+h[393]+t*+h[397]+o*+h[401];n=+h[406]+s*+h[394]+t*+h[398]+o*+h[402];p=n==0.0?1.0e-5:n;h[v>>3]=(+h[403]+s*+h[391]+t*+h[395]+o*+h[399])/p;h[x>>3]=q/p;h[w>>3]=m/p;h[u>>3]=r;c[l>>2]=0;h[u>>3]=+h[j+(C<<6)+24>>3];up(f,d);C=C+1|0;}while((C|0)<(c[A>>2]|0))}}while(0);if((c[5094]|0)==0){i=e;return}c[13542]=g;i=e;return}function fG(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0,u=0.0,v=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0;e=i;i=i+48|0;f=e|0;g=c[11690]|0;do{if((g&2|0)==0){j=g}else{k=b+44|0;if((c[k>>2]|0)<=0){j=g;break}l=b+4|0;m=f|0;n=f+8|0;o=f+16|0;p=f+32|0;q=f+40|0;r=0;do{s=c[l>>2]|0;t=+h[s+(r<<6)+24>>3];u=(+h[s+(r<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;v=(+h[s+(r<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;w=(t- +h[5279])*+h[2]+ +h[12]+-1.0;x=+h[404]+u*+h[392]+v*+h[396]+w*+h[400];y=+h[405]+u*+h[393]+v*+h[397]+w*+h[401];z=+h[406]+u*+h[394]+v*+h[398]+w*+h[402];A=z==0.0?1.0e-5:z;h[m>>3]=(+h[403]+u*+h[391]+v*+h[395]+w*+h[399])/A;h[n>>3]=x/A;x=y/A;h[o>>3]=x;h[p>>3]=t;c[q>>2]=0;if(!((a[38984]&1)==0|x==-2.0)){h[o>>3]=x+.01}uo(f,d);r=r+1|0;}while((r|0)<(c[k>>2]|0));j=c[11690]|0}}while(0);if((j&1|0)==0){i=e;return}j=b+44|0;if((c[j>>2]|0)<=0){i=e;return}g=b+4|0;b=f|0;k=f+8|0;r=f+16|0;o=f+32|0;q=f+40|0;p=0;do{n=c[g>>2]|0;x=(+h[n+(p<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;t=(+h[n+(p<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;A=(+h[7076]- +h[5279])*+h[2]+ +h[12]+-1.0;y=+h[404]+x*+h[392]+t*+h[396]+A*+h[400];w=+h[405]+x*+h[393]+t*+h[397]+A*+h[401];v=+h[406]+x*+h[394]+t*+h[398]+A*+h[402];u=v==0.0?1.0e-5:v;h[b>>3]=(+h[403]+x*+h[391]+t*+h[395]+A*+h[399])/u;h[k>>3]=y/u;h[r>>3]=w/u;c[q>>2]=0;h[o>>3]=+h[n+(p<<6)+24>>3];uo(f,d);p=p+1|0;}while((p|0)<(c[j>>2]|0));i=e;return}function fH(b,d,e,f,g){b=b|0;d=+d;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0,u=0.0,v=0.0,w=0.0,x=0.0,y=0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,R=0,S=0,T=0.0,U=0.0,V=0,W=0,X=0;j=i;i=i+144|0;k=f;f=i;i=i+56|0;uD(f,k,56);k=j|0;l=j+16|0;m=j+32|0;n=j+80|0;o=j+128|0;p=j+136|0;q=(e|0)!=0;if(q){r=65056+(b*688&-1)|0}else{r=65064+(b*688&-1)|0}s=65072+(b*688&-1)|0;t=+h[r>>3]*((a[s]&1)!=0?1.0:-1.0);r=c[144]|0;u=+h[64664+(r*688&-1)>>3];v=+h[97];w=u+ +h[64672+(r*688&-1)>>3]-v;r=c[3524]|0;x=+h[7076];y=c[200]|0;z=(d- +h[64664+(y*688&-1)>>3])*+h[91]+ +h[96]+-1.0;A=(v-u)*+h[21]+ +h[68]+-1.0;u=(x- +h[5279])*+h[2]+ +h[12]+-1.0;v=+h[404]+z*+h[392]+A*+h[396]+u*+h[400];B=+h[405]+z*+h[393]+A*+h[397]+u*+h[401];C=+h[406]+z*+h[394]+A*+h[398]+u*+h[402];D=C==0.0?1.0e-5:C;E=m|0;h[E>>3]=(+h[403]+z*+h[391]+A*+h[395]+u*+h[399])/D;F=m+8|0;h[F>>3]=v/D;G=m+16|0;h[G>>3]=B/D;H=m+32|0;h[H>>3]=x;I=m+40|0;c[I>>2]=0;if((c[f+4>>2]|0)>-3){J=r+168|0;cM[c[J>>2]&511](3);x=+h[7076];D=(d- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;B=(w- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;v=(x- +h[5279])*+h[2]+ +h[12]+-1.0;u=+h[404]+D*+h[392]+B*+h[396]+v*+h[400];A=+h[405]+D*+h[393]+B*+h[397]+v*+h[401];z=+h[406]+D*+h[394]+B*+h[398]+v*+h[402];C=z==0.0?1.0e-5:z;h[n>>3]=(+h[403]+D*+h[391]+B*+h[395]+v*+h[399])/C;h[n+8>>3]=u/C;h[n+16>>3]=A/C;h[n+32>>3]=x;c[n+40>>2]=0;do{if((a[38984]&1)==0){K=8734}else{if((a[46752]&1)==0){K=8734;break}gt(m,n,f)}}while(0);if((K|0)==8734){un(m,n,f,f+40|0)}cM[c[J>>2]&511](4);L=c[200]|0}else{L=y}do{if((c[64916+(L*688&-1)>>2]&2|0)!=0){y=c[144]|0;if((a[64788+(y*688&-1)|0]&1)!=0){break}x=+h[64664+(y*688&-1)>>3];C=+h[64672+(y*688&-1)>>3];if(x<C){if(x>0.0|C<0.0){break}}else{if(C>0.0|x<0.0){break}}C=+h[7076];A=(d- +h[64664+(L*688&-1)>>3])*+h[91]+ +h[96]+-1.0;u=(0.0-x)*+h[21]+ +h[68]+-1.0;x=(C- +h[5279])*+h[2]+ +h[12]+-1.0;v=+h[404]+A*+h[392]+u*+h[396]+x*+h[400];B=+h[405]+A*+h[393]+u*+h[397]+x*+h[401];D=+h[406]+A*+h[394]+u*+h[398]+x*+h[402];z=D==0.0?1.0e-5:D;h[E>>3]=(+h[403]+A*+h[391]+u*+h[395]+x*+h[399])/z;h[F>>3]=v/z;h[G>>3]=B/z;h[H>>3]=C;c[I>>2]=0}}while(0);L=r+24|0;y=n|0;h[y>>3]=+h[E>>3]+t*+h[695]*+((c[L>>2]|0)>>>0>>>0);J=n+8|0;h[J>>3]=+h[F>>3]+t*+h[694]*+((c[L>>2]|0)>>>0>>>0);f=n+16|0;h[f>>3]=+h[G>>3]+t*+h[693]*+((c[L>>2]|0)>>>0>>>0);M=n+32|0;h[M>>3]=+h[H>>3];do{if((a[38984]&1)==0){K=8745}else{if((a[46752]&1)==0){K=8745;break}gt(m,n,56296)}}while(0);if((K|0)==8745){un(m,n,56296,56336)}N=l;c[N>>2]=c[14084];c[N+4>>2]=c[56340>>2];c[N+8>>2]=c[56344>>2];c[N+12>>2]=c[56348>>2];N=c[14075]|0;do{if((c[14074]|0)!=0){C=+h[7040];O=c[(c[3524]|0)+92>>2]|0;if(C<0.0){cK[O&63](+h[3817]);break}else{cK[O&63](C);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);O=c[(c[3524]|0)+64>>2]|0;if((N|0)<-5){cM[O&511](-2)}else{cM[O&511](N)}O=c[3524]|0;do{if((a[56328]&1)==0){if((c[O+96>>2]&1024|0)!=0){break}c[l>>2]=1;c[l+4>>2]=N;K=8756}else{K=8756}}while(0);if((K|0)==8756){fn(l,O)}do{if(q){L11938:do{if((g|0)==0){Q=e}else{O=c[200]|0;C=+h[64672+(O*688&-1)>>3]- +h[64664+(O*688&-1)>>3];O=g;while(1){if(+P(+((d- +h[O>>3])/C))<=.001){Q=0;break L11938}l=c[O+16>>2]|0;if((l|0)==0){Q=e;break}else{O=l}}}}while(0);fx(64984+(b*688&-1)|0,o,p,99744);C=+h[695];z=+(c[180]|0);B=C*z;if(B<-.9){R=0}else{R=B<.9?1:2}O=c[5094]|0;do{if((O|0)==0){S=R}else{if((a[65272+(b*688&-1)|0]&1)==0){S=R;break}S=c[65128+(b*688&-1)>>2]|0}}while(0);B=+h[E>>3]-C*+((c[r+20>>2]|0)>>>0>>>0);h[y>>3]=B;v=+h[694];x=+h[F>>3]-v*+((c[r+16>>2]|0)>>>0>>>0);h[J>>3]=x;if((a[s]&1)==0){u=+h[65056+(b*688&-1)>>3];A=B-u*C*+((c[L>>2]|0)>>>0>>>0);h[y>>3]=A;D=x-u*v*+((c[L>>2]|0)>>>0>>>0);h[J>>3]=D;T=A;U=D}else{T=B;U=x}l=~~(T*z)+(c[186]|0)|0;N=~~(U*+(c[40]|0))+(c[46]|0)|0;V=64928+(b*688&-1)|0;if((c[V>>2]|0)==0){W=O}else{fn(V,r);W=c[5094]|0}V=c[65032+(b*688&-1)>>2]|0;if((W|0)==0|(V|0)==0){K=8773}else{if((cO[c[(c[3524]|0)+72>>2]&255](V)|0)==0){K=8773}else{X=V}}if((K|0)==8773){X=0}ln(l+(c[o>>2]|0)|0,N+(c[p>>2]|0)|0,Q,S,0,X,c[64924+(b*688&-1)>>2]|0);N=c[(c[3524]|0)+72>>2]|0;cO[N&255](0);N=k;c[N>>2]=c[14084];c[N+4>>2]=c[56340>>2];c[N+8>>2]=c[56344>>2];c[N+12>>2]=c[56348>>2];N=c[14075]|0;do{if((c[14074]|0)!=0){x=+h[7040];l=c[(c[3524]|0)+92>>2]|0;if(x<0.0){cK[l&63](+h[3817]);break}else{cK[l&63](x);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);O=c[(c[3524]|0)+64>>2]|0;if((N|0)<-5){cM[O&511](-2)}else{cM[O&511](N)}O=c[3524]|0;if((a[56328]&1)==0){if((c[O+96>>2]&1024|0)!=0){break}c[k>>2]=1;c[k+4>>2]=N}fn(k,O)}}while(0);k=c[200]|0;if((c[64916+(k*688&-1)>>2]&4|0)==0){i=j;return}U=+h[7076];T=(d- +h[64664+(k*688&-1)>>3])*+h[91]+ +h[96]+-1.0;d=(w- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;w=(U- +h[5279])*+h[2]+ +h[12]+-1.0;z=+h[404]+T*+h[392]+d*+h[396]+w*+h[400];C=+h[405]+T*+h[393]+d*+h[397]+w*+h[401];x=+h[406]+T*+h[394]+d*+h[398]+w*+h[402];B=x==0.0?1.0e-5:x;x=(+h[403]+T*+h[391]+d*+h[395]+w*+h[399])/B;h[E>>3]=x;w=z/B;h[F>>3]=w;z=C/B;h[G>>3]=z;h[H>>3]=U;c[I>>2]=0;h[y>>3]=x-t*+h[695]*+((c[L>>2]|0)>>>0>>>0);h[J>>3]=w-t*+h[694]*+((c[L>>2]|0)>>>0>>>0);h[f>>3]=z-t*+h[693]*+((c[L>>2]|0)>>>0>>>0);h[M>>3]=U;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(m,n,56296);i=j;return}}while(0);un(m,n,56296,56336);i=j;return}function fI(b,d,e,f,g){b=b|0;d=+d;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0.0,u=0.0,v=0.0,w=0.0,x=0.0,y=0,z=0.0,A=0.0,B=0.0,C=0.0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,R=0,S=0.0,T=0.0,U=0,V=0.0,W=0,X=0.0,Y=0,Z=0,_=0,$=0;j=i;i=i+128|0;k=f;f=i;i=i+56|0;uD(f,k,56);k=j|0;l=j+16|0;m=j+64|0;n=j+112|0;o=j+120|0;p=(e|0)!=0;if(p){q=65056+(b*688&-1)|0}else{q=65064+(b*688&-1)|0}r=65072+(b*688&-1)|0;s=+h[q>>3]*((a[r]&1)!=0?1.0:-1.0);q=c[200]|0;t=+h[64664+(q*688&-1)>>3];u=+h[69];v=t+ +h[64672+(q*688&-1)>>3]-u;q=c[3524]|0;w=+h[7076];x=(u-t)*+h[91]+ +h[96]+-1.0;y=c[144]|0;t=(d- +h[64664+(y*688&-1)>>3])*+h[21]+ +h[68]+-1.0;u=(w- +h[5279])*+h[2]+ +h[12]+-1.0;z=+h[404]+x*+h[392]+t*+h[396]+u*+h[400];A=+h[405]+x*+h[393]+t*+h[397]+u*+h[401];B=+h[406]+x*+h[394]+t*+h[398]+u*+h[402];C=B==0.0?1.0e-5:B;D=l|0;h[D>>3]=(+h[403]+x*+h[391]+t*+h[395]+u*+h[399])/C;E=l+8|0;h[E>>3]=z/C;F=l+16|0;h[F>>3]=A/C;G=l+32|0;h[G>>3]=w;H=l+40|0;c[H>>2]=0;if((c[f+4>>2]|0)>-3){I=q+168|0;cM[c[I>>2]&511](3);w=+h[7076];C=(v- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;A=(d- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;z=(w- +h[5279])*+h[2]+ +h[12]+-1.0;u=+h[404]+C*+h[392]+A*+h[396]+z*+h[400];t=+h[405]+C*+h[393]+A*+h[397]+z*+h[401];x=+h[406]+C*+h[394]+A*+h[398]+z*+h[402];B=x==0.0?1.0e-5:x;h[m>>3]=(+h[403]+C*+h[391]+A*+h[395]+z*+h[399])/B;h[m+8>>3]=u/B;h[m+16>>3]=t/B;h[m+32>>3]=w;c[m+40>>2]=0;do{if((a[38984]&1)==0){J=8801}else{if((a[46752]&1)==0){J=8801;break}gt(l,m,f)}}while(0);if((J|0)==8801){un(l,m,f,f+40|0)}cM[c[I>>2]&511](4);K=c[144]|0}else{K=y}do{if((c[64916+(K*688&-1)>>2]&2|0)!=0){y=c[200]|0;if((a[64788+(y*688&-1)|0]&1)!=0){break}w=+h[64664+(y*688&-1)>>3];B=+h[64672+(y*688&-1)>>3];if(w<B){if(w>0.0|B<0.0){break}}else{if(B>0.0|w<0.0){break}}B=+h[7076];t=(0.0-w)*+h[91]+ +h[96]+-1.0;w=(d- +h[64664+(K*688&-1)>>3])*+h[21]+ +h[68]+-1.0;u=(B- +h[5279])*+h[2]+ +h[12]+-1.0;z=+h[404]+t*+h[392]+w*+h[396]+u*+h[400];A=+h[405]+t*+h[393]+w*+h[397]+u*+h[401];C=+h[406]+t*+h[394]+w*+h[398]+u*+h[402];x=C==0.0?1.0e-5:C;h[D>>3]=(+h[403]+t*+h[391]+w*+h[395]+u*+h[399])/x;h[E>>3]=z/x;h[F>>3]=A/x;h[G>>3]=B;c[H>>2]=0}}while(0);K=q+28|0;y=m|0;h[y>>3]=+h[D>>3]+s*+h[695]*+((c[K>>2]|0)>>>0>>>0);I=m+8|0;h[I>>3]=+h[E>>3]+s*+h[694]*+((c[K>>2]|0)>>>0>>>0);f=m+16|0;h[f>>3]=+h[F>>3]+s*+h[693]*+((c[K>>2]|0)>>>0>>>0);L=m+32|0;h[L>>3]=+h[G>>3];do{if((a[38984]&1)==0){J=8812}else{if((a[46752]&1)==0){J=8812;break}gt(l,m,56296)}}while(0);if((J|0)==8812){un(l,m,56296,56336)}do{if(p){L12016:do{if((g|0)==0){M=e}else{N=c[144]|0;B=+h[64672+(N*688&-1)>>3]- +h[64664+(N*688&-1)>>3];N=g;while(1){if(+P(+((d- +h[N>>3])/B))<=.001){M=0;break L12016}O=c[N+16>>2]|0;if((O|0)==0){M=e;break}else{N=O}}}}while(0);fx(64984+(b*688&-1)|0,n,o,94352);B=+h[695];N=c[180]|0;x=B*+(N|0);if(x<-.9){Q=0}else{Q=x<.9?1:2}O=c[5094]|0;do{if((O|0)==0){R=Q}else{if((a[65272+(b*688&-1)|0]&1)==0){R=Q;break}R=c[65128+(b*688&-1)>>2]|0}}while(0);x=+h[D>>3]-B*+((c[q+20>>2]|0)>>>0>>>0);h[y>>3]=x;A=+h[694];z=+h[E>>3]-A*+((c[q+16>>2]|0)>>>0>>>0);h[I>>3]=z;if((a[r]&1)==0){u=+h[65056+(b*688&-1)>>3];w=x-u*B*+((c[K>>2]|0)>>>0>>>0);h[y>>3]=w;t=z-u*A*+((c[q+24>>2]|0)>>>0>>>0);h[I>>3]=t;S=w;T=t}else{S=x;T=z}U=64928+(b*688&-1)|0;if((c[U>>2]|0)==0){V=S;W=N;X=T;Y=O}else{fn(U,q);V=+h[y>>3];W=c[180]|0;X=+h[I>>3];Y=c[5094]|0}U=~~(V*+(W|0))+(c[186]|0)|0;Z=~~(X*+(c[40]|0))+(c[46]|0)|0;_=c[65032+(b*688&-1)>>2]|0;if((Y|0)==0|(_|0)==0){J=8829}else{if((cO[c[(c[3524]|0)+72>>2]&255](_)|0)==0){J=8829}else{$=_}}if((J|0)==8829){$=0}ln(U+(c[n>>2]|0)|0,Z+(c[o>>2]|0)|0,M,R,0,$,c[64924+(b*688&-1)>>2]|0);Z=c[(c[3524]|0)+72>>2]|0;cO[Z&255](0);Z=k;c[Z>>2]=c[14084];c[Z+4>>2]=c[56340>>2];c[Z+8>>2]=c[56344>>2];c[Z+12>>2]=c[56348>>2];Z=c[14075]|0;do{if((c[14074]|0)!=0){z=+h[7040];U=c[(c[3524]|0)+92>>2]|0;if(z<0.0){cK[U&63](+h[3817]);break}else{cK[U&63](z);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);O=c[(c[3524]|0)+64>>2]|0;if((Z|0)<-5){cM[O&511](-2)}else{cM[O&511](Z)}O=c[3524]|0;if((a[56328]&1)==0){if((c[O+96>>2]&1024|0)!=0){break}c[k>>2]=1;c[k+4>>2]=Z}fn(k,O)}}while(0);k=c[144]|0;if((c[64916+(k*688&-1)>>2]&4|0)==0){i=j;return}X=+h[7076];V=(v- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;v=(d- +h[64664+(k*688&-1)>>3])*+h[21]+ +h[68]+-1.0;d=(X- +h[5279])*+h[2]+ +h[12]+-1.0;T=+h[404]+V*+h[392]+v*+h[396]+d*+h[400];S=+h[405]+V*+h[393]+v*+h[397]+d*+h[401];B=+h[406]+V*+h[394]+v*+h[398]+d*+h[402];z=B==0.0?1.0e-5:B;B=(+h[403]+V*+h[391]+v*+h[395]+d*+h[399])/z;h[D>>3]=B;d=T/z;h[E>>3]=d;T=S/z;h[F>>3]=T;h[G>>3]=X;c[H>>2]=0;h[y>>3]=B-s*+h[695]*+((c[K>>2]|0)>>>0>>>0);h[I>>3]=d-s*+h[694]*+((c[K>>2]|0)>>>0>>>0);h[f>>3]=T-s*+h[693]*+((c[K>>2]|0)>>>0>>>0);h[L>>3]=X;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(l,m,56296);i=j;return}}while(0);un(l,m,56296,56336);i=j;return}function fJ(b,d,e,f,g){b=b|0;d=+d;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,Q=0.0,R=0.0,S=0.0,T=0.0,U=0.0,V=0.0,W=0.0,X=0.0,Y=0.0,Z=0,_=0,$=0,aa=0,ab=0,ac=0.0,ad=0.0,ae=0.0,af=0.0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0;j=i;i=i+176|0;k=f;f=i;i=i+56|0;uD(f,k,56);k=j|0;l=j+16|0;m=j+64|0;n=j+112|0;o=j+160|0;p=j+168|0;q=(e|0)!=0;if(q){r=65056+(b*688&-1)|0}else{r=65064+(b*688&-1)|0}s=65072+(b*688&-1)|0;t=c[3524]|0;u=~~(+((c[t+28>>2]|0)>>>0>>>0)*+h[r>>3]*((a[s]&1)!=0?1.0:-1.0));if((c[64916+(b*688&-1)>>2]&2|0)==0){v=(+h[14]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;w=(+h[13]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;x=(d- +h[5279])*+h[2]+ +h[12]+-1.0;y=+h[404]+v*+h[392]+w*+h[396]+x*+h[400];z=+h[405]+v*+h[393]+w*+h[397]+x*+h[401];A=+h[406]+v*+h[394]+w*+h[398]+x*+h[402];B=A==0.0?1.0e-5:A;A=(+h[403]+v*+h[391]+w*+h[395]+x*+h[399])/B;h[l>>3]=A;x=y/B;h[l+8>>3]=x;y=z/B;h[l+16>>3]=y;h[l+32>>3]=d;c[l+40>>2]=0;C=A;D=x;E=y}else{y=(0.0- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;x=(0.0- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;A=(d- +h[5279])*+h[2]+ +h[12]+-1.0;B=+h[404]+y*+h[392]+x*+h[396]+A*+h[400];z=+h[405]+y*+h[393]+x*+h[397]+A*+h[401];w=+h[406]+y*+h[394]+x*+h[398]+A*+h[402];v=w==0.0?1.0e-5:w;w=(+h[403]+y*+h[391]+x*+h[395]+A*+h[399])/v;h[l>>3]=w;A=B/v;h[l+8>>3]=A;B=z/v;h[l+16>>3]=B;h[l+32>>3]=d;c[l+40>>2]=0;C=w;D=A;E=B}if((c[f+4>>2]|0)>-3){r=t+168|0;cM[c[r>>2]&511](3);B=+h[64664+((c[200]|0)*688&-1)>>3];A=+h[91];w=+h[96];v=(+h[7089]-B)*A+w+-1.0;z=+h[64664+((c[144]|0)*688&-1)>>3];x=+h[21];y=+h[68];F=(+h[7088]-z)*x+y+-1.0;G=(d- +h[5279])*+h[2]+ +h[12]+-1.0;H=+h[403];I=+h[391];J=+h[395];K=G*+h[399];L=+h[404];M=+h[392];N=+h[396];O=G*+h[400];Q=+h[405];R=+h[393];S=+h[397];T=G*+h[401];U=+h[406];V=+h[394];W=+h[398];X=G*+h[402];G=U+v*V+F*W+X;Y=G==0.0?1.0e-5:G;t=m|0;h[t>>3]=(H+v*I+F*J+K)/Y;Z=m+8|0;h[Z>>3]=(L+v*M+F*N+O)/Y;_=m+16|0;h[_>>3]=(Q+v*R+F*S+T)/Y;$=m+32|0;h[$>>3]=d;c[m+40>>2]=0;Y=(+h[3305]-B)*A+w+-1.0;w=(+h[3304]-z)*x+y+-1.0;y=U+Y*V+w*W+X;X=y==0.0?1.0e-5:y;h[n>>3]=(H+Y*I+w*J+K)/X;h[n+8>>3]=(L+Y*M+w*N+O)/X;h[n+16>>3]=(Q+Y*R+w*S+T)/X;h[n+32>>3]=d;c[n+40>>2]=0;do{if((a[38984]&1)==0){aa=8861}else{if((a[46752]&1)==0){aa=8861;break}gt(l,m,f)}}while(0);if((aa|0)==8861){un(l,m,f,f+40|0)}do{if((a[38984]&1)==0){aa=8865}else{if((a[46752]&1)==0){aa=8865;break}gt(m,n,f)}}while(0);if((aa|0)==8865){un(m,n,f,f+40|0)}cM[c[r>>2]&511](4);r=l|0;f=l+8|0;n=l+16|0;ab=l+32|0;ac=+h[r>>3];ad=+h[f>>3];ae=+h[n>>3];af=+h[ab>>3];ag=r;ah=t;ai=f;aj=Z;ak=n;al=_;am=ab;an=$}else{ac=C;ad=D;ae=E;af=d;ag=l|0;ah=m|0;ai=l+8|0;aj=m+8|0;ak=l+16|0;al=m+16|0;am=l+32|0;an=m+32|0}E=+(u|0);h[ah>>3]=ac+E/+(c[180]|0);h[aj>>3]=ad;h[al>>3]=ae;h[an>>3]=af;do{if((a[38984]&1)==0){aa=8870}else{if((a[46752]&1)==0){aa=8870;break}gt(l,m,56296)}}while(0);if((aa|0)==8870){un(l,m,56296,56336)}do{if(q){L12097:do{if((g|0)==0){ao=e}else{u=c[34]|0;af=+h[64672+(u*688&-1)>>3]- +h[64664+(u*688&-1)>>3];u=g;while(1){if(+P(+((d- +h[u>>3])/af))<=.001){ao=0;break L12097}$=c[u+16>>2]|0;if(($|0)==0){ao=e;break}else{u=$}}}}while(0);fx(64984+(b*688&-1)|0,o,p,73288);u=~~(+h[ai>>3]*+(c[40]|0))+(c[46]|0)|0;$=c[3524]|0;ab=c[$+28>>2]|0;_=(~~(+h[ag>>3]*+(c[180]|0))+(c[186]|0)|0)-(ab<<1)|0;if((a[s]&1)==0){ap=~~(+(_>>>0>>>0)- +(ab>>>0>>>0)*+h[65056+(b*688&-1)>>3])}else{ap=_}_=64928+(b*688&-1)|0;ab=c[_>>2]|0;if((ab|0)==6){h[64936+(b*688&-1)>>3]=d;aa=8880}else if((ab|0)!=0){aa=8880}if((aa|0)==8880){fn(_,$)}ln((c[o>>2]|0)+ap|0,u+(c[p>>2]|0)|0,ao,2,1,0,c[64924+(b*688&-1)>>2]|0);u=k;c[u>>2]=c[14084];c[u+4>>2]=c[56340>>2];c[u+8>>2]=c[56344>>2];c[u+12>>2]=c[56348>>2];u=c[14075]|0;do{if((c[14074]|0)!=0){af=+h[7040];$=c[(c[3524]|0)+92>>2]|0;if(af<0.0){cK[$&63](+h[3817]);break}else{cK[$&63](af);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);$=c[(c[3524]|0)+64>>2]|0;if((u|0)<-5){cM[$&511](-2)}else{cM[$&511](u)}$=c[3524]|0;if((a[56328]&1)==0){if((c[$+96>>2]&1024|0)!=0){break}c[k>>2]=1;c[k+4>>2]=u}fn(k,$)}}while(0);if((c[64916+((c[34]|0)*688&-1)>>2]&4|0)==0){i=j;return}af=(+h[3305]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;ae=(+h[3304]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;ad=(d- +h[5279])*+h[2]+ +h[12]+-1.0;ac=+h[404]+af*+h[392]+ae*+h[396]+ad*+h[400];D=+h[405]+af*+h[393]+ae*+h[397]+ad*+h[401];C=+h[406]+af*+h[394]+ae*+h[398]+ad*+h[402];X=C==0.0?1.0e-5:C;C=(+h[403]+af*+h[391]+ae*+h[395]+ad*+h[399])/X;h[ag>>3]=C;ad=ac/X;h[ai>>3]=ad;ac=D/X;h[ak>>3]=ac;h[am>>3]=d;c[l+40>>2]=0;h[ah>>3]=C-E/+(c[180]|0);h[aj>>3]=ad;h[al>>3]=ac;h[an>>3]=d;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(l,m,56296);i=j;return}}while(0);un(l,m,56296,56336);i=j;return}function fK(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0;f=i;i=i+24|0;g=f|0;j=f+8|0;k=f+16|0;h[g>>3]=+h[a+16>>3];h[j>>3]=+h[a+24>>3];h[k>>3]=+h[a+32>>3];l=(fM(a,e,g,j,k)|0)==0;m=+h[g>>3];if(l){n=(m- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;o=(+h[j>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;p=(+h[k>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;q=+h[406]+n*+h[394]+o*+h[398]+p*+h[402];r=q==0.0?1.0e-5:q;q=(+h[404]+n*+h[392]+o*+h[396]+p*+h[400])/r;h[b>>3]=+(~~((+h[403]+n*+h[391]+o*+h[395]+p*+h[399])/r*+(c[180]|0))+(c[186]|0)|0);s=+(~~(q*+(c[40]|0))+(c[46]|0)|0);h[d>>3]=s;i=f;return}else{h[b>>3]=m;s=+h[j>>3];h[d>>3]=s;i=f;return}}function fL(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0,s=0.0,t=0.0;e=i;i=i+48|0;f=e|0;g=e+16|0;j=e+32|0;k=b+64|0;l=c[k>>2]|0;if((l|0)==6|(l|0)==0){if((a[55536]&1)==0){i=e;return}if((a[b+56|0]&1)==0){i=e;return}if((a[b+239|0]&1)!=0){m=+h[d+32>>3];n=+h[8341];do{if(n<m){o=+h[8342];if(o<=m){p=+((a[20668]|0)==112&1|0);break}q=(m-n)/(o-n);if((a[20668]|0)==112){p=q;break}p=1.0-q}else{p=+((a[20668]|0)!=112&1|0)}}while(0);r=(c[3524]|0)+144|0;if((c[r>>2]|0)==0){i=e;return}c[g>>2]=5;h[g+8>>3]=p;c[g+4>>2]=0;cM[c[r>>2]&511](g);i=e;return}p=+h[d+24>>3];g=c[34]|0;r=(a[66852]&1)==0;do{if((a[64788+(g*688&-1)|0]&1)==0){if(r){s=p;break}if(p>0.0){n=+_(+p);s=n/+h[8358];break}else{s=+h[8341];break}}else{if(r){s=+Z(+(p*+h[64800+(g*688&-1)>>3]));break}if(+h[64792+(g*688&-1)>>3]==+h[8357]){s=p;break}s=p*+h[64800+(g*688&-1)>>3]/+h[8358]}}while(0);p=+h[8341];do{if(p<s){n=+h[8342];if(n<=s){t=+((a[20668]|0)==112&1|0);break}m=(s-p)/(n-p);if((a[20668]|0)==112){t=m;break}t=1.0-m}else{t=+((a[20668]|0)!=112&1|0)}}while(0);g=(c[3524]|0)+144|0;if((c[g>>2]|0)==0){i=e;return}c[f>>2]=5;h[f+8>>3]=t;c[f+4>>2]=0;cM[c[g>>2]&511](f);i=e;return}else if((l|0)==3){if((a[b+239|0]&1)==0){i=e;return}f=(c[3524]|0)+144|0;if((c[f>>2]|0)==0){i=e;return}g=~~+h[d+32>>3];c[j>>2]=3;c[j+4>>2]=g;h[j+8>>3]=0.0;cM[c[f>>2]&511](j);i=e;return}else if((l|0)==2){c[b+68>>2]=~~+h[d+32>>3];fn(k,c[3524]|0);i=e;return}else{i=e;return}}function fM(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0.0,s=0.0,t=0,u=0,w=0,x=0.0,y=0,z=0,A=0,B=0.0;j=i;k=c[b>>2]|0;if((k|0)==4){h[e>>3]=+h[e>>3]*+((c[(c[3524]|0)+20>>2]|0)>>>0>>>0)+.5;l=0;m=1;n=0}else if((k|0)==2){o=c[200]|0;p=+h[64664+(o*688&-1)>>3];h[e>>3]=p+ +h[e>>3]*(+h[64672+(o*688&-1)>>3]-p);l=1;m=0;n=0}else if((k|0)==3){h[e>>3]=+h[e>>3]*+(((c[(c[3524]|0)+8>>2]|0)-1|0)>>>0>>>0)+.5;l=0;m=0;n=1}else if((k|0)==0|(k|0)==1){p=+h[e>>3];do{if((a[66164]&1)==0){q=p}else{if(p>0.0){r=+_(+p);q=r/+h[8272];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=d,c[v+8>>2]=56880,h[v+16>>3]=p,v)|0);return 0}}}while(0);h[e>>3]=q;l=1;m=0;n=0}else{l=0;m=0;n=0}e=c[b+4>>2]|0;if((e|0)==2){k=c[144]|0;if((c[5094]|0)==0){q=+h[64664+(k*688&-1)>>3];s=q+ +h[f>>3]*(+h[64672+(k*688&-1)>>3]-q)}else{q=+h[64672+(k*688&-1)>>3];s=q- +h[f>>3]*(q- +h[64664+(k*688&-1)>>3])}h[f>>3]=s;t=1;u=m;w=n}else if((e|0)==4){h[f>>3]=+h[f>>3]*+((c[(c[3524]|0)+16>>2]|0)>>>0>>>0)+.5;t=l;u=1;w=n}else if((e|0)==0|(e|0)==1){s=+h[f>>3];do{if((a[65476]&1)==0){x=s}else{if(s>0.0){q=+_(+s);x=q/+h[8186];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=d,c[v+8>>2]=56856,h[v+16>>3]=s,v)|0);return 0}}}while(0);h[f>>3]=x;t=1;u=m;w=n}else if((e|0)==3){h[f>>3]=+h[f>>3]*+(((c[(c[3524]|0)+12>>2]|0)-1|0)>>>0>>>0)+.5;t=l;u=m;w=1}else{t=l;u=m;w=n}n=c[b+8>>2]|0;if((n|0)==2){b=c[34]|0;x=+h[64664+(b*688&-1)>>3];h[g>>3]=x+ +h[g>>3]*(+h[64672+(b*688&-1)>>3]-x);y=1;z=u;A=w}else if((n|0)==4){y=t;z=1;A=w}else if((n|0)==0|(n|0)==1){x=+h[g>>3];do{if((a[64788]&1)==0){B=x}else{if(x>0.0){s=+_(+x);B=s/+h[8100];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=d,c[v+8>>2]=56832,h[v+16>>3]=x,v)|0);return 0}}}while(0);h[g>>3]=B;y=1;z=u;A=w}else if((n|0)==3){y=t;z=u;A=1}else{y=t;z=u;A=w}w=A|z;if(y&w){uk(218688,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{i=j;return w&1|0}return 0}function fN(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0;g=i;j=c[b>>2]|0;if((j|0)==2){h[d>>3]=+h[b+16>>3]*+((c[7939]|0)-(c[7938]|0)|0)}else if((j|0)==3){h[d>>3]=+h[b+16>>3]*+(((c[(c[3524]|0)+8>>2]|0)-1|0)>>>0>>>0)}else if((j|0)==4){h[d>>3]=+h[b+16>>3]*+((c[(c[3524]|0)+20>>2]|0)>>>0>>>0)}else if((j|0)==0){k=+h[b+16>>3];do{if((a[66164]&1)==0){l=k}else{if(k>0.0){m=+_(+k);l=m/+h[8272];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=56880,h[v+16>>3]=k,v)|0)}}}while(0);h[d>>3]=l*+h[8269]}else if((j|0)==1){l=+h[b+16>>3];do{if((a[68916]&1)==0){n=l}else{if(l>0.0){k=+_(+l);n=k/+h[8616];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=56976,h[v+16>>3]=l,v)|0)}}}while(0);h[d>>3]=n*+h[8613]}d=c[b+4>>2]|0;if((d|0)==4){h[e>>3]=+h[b+24>>3]*+((c[(c[3524]|0)+16>>2]|0)>>>0>>>0);i=g;return}else if((d|0)==1){n=+h[b+24>>3];do{if((a[68228]&1)==0){o=n}else{if(n>0.0){l=+_(+n);o=l/+h[8530];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=56952,h[v+16>>3]=n,v)|0)}}}while(0);h[e>>3]=o*+h[8527];i=g;return}else if((d|0)==0){o=+h[b+24>>3];do{if((a[65476]&1)==0){p=o}else{if(o>0.0){n=+_(+o);p=n/+h[8186];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=56856,h[v+16>>3]=o,v)|0)}}}while(0);h[e>>3]=p*+h[8183];i=g;return}else if((d|0)==2){h[e>>3]=+h[b+24>>3]*+((c[7941]|0)-(c[7940]|0)|0);i=g;return}else if((d|0)==3){h[e>>3]=+h[b+24>>3]*+(((c[(c[3524]|0)+12>>2]|0)-1|0)>>>0>>>0);i=g;return}else{i=g;return}}function fO(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0.0,X=0.0,Y=0,Z=0,_=0,$=0.0,aa=0.0,ab=0,ac=0,ad=0,ae=0,af=0.0,ag=0.0,ah=0.0,ai=0.0,aj=0,ak=0,al=0.0,am=0.0,an=0.0,ao=0.0,ap=0,aq=0.0,ar=0.0,as=0.0,at=0.0,au=0,av=0.0,aw=0.0,ax=0.0,ay=0.0,az=0.0,aA=0.0,aB=0.0,aC=0.0,aD=0.0,aE=0.0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0.0,aO=0.0,aP=0,aQ=0.0,aR=0;f=i;i=i+224|0;g=f|0;j=f+16|0;k=f+32|0;l=f+48|0;m=f+64|0;n=f+72|0;o=f+80|0;p=f+88|0;q=f+96|0;r=f+120|0;s=f+128|0;t=f+136|0;u=f+144|0;v=f+152|0;w=f+160|0;x=f+168|0;y=f+192|0;z=f+200|0;A=f+208|0;B=f+216|0;if((b|0)==0){i=f;return}C=x|0;D=(e|0)==2;E=k;F=x+7|0;x=k|0;G=k+4|0;H=j;I=j|0;J=j+4|0;K=g;L=g|0;M=g+4|0;N=q|0;O=l;P=q+7|0;q=l|0;Q=l+4|0;R=b;do{L12294:do{if((c[R+8>>2]|0)==(d|0)){b=R+52|0;S=c[b>>2]|0;do{if((S|0)==-7){if((c[R+12>>2]|0)!=1){T=9026;break}U=c[12302]|0;V=c[12303]|0;W=+h[6153];X=+h[6154];Y=a[49240]|0;uD(C|0,49241,23);Z=U;_=V;$=W;aa=X;ab=Y}else{T=9026}}while(0);if((T|0)==9026){T=0;Y=c[R+48>>2]|0;X=+h[R+64>>3];W=+h[R+72>>3];V=a[R+80|0]|0;uD(C|0,R+81|0,23);Z=Y;_=S;$=X;aa=W;ab=V}V=R+16|0;Y=V|0;if((c[Y>>2]|0)==3){if((c[R+12>>2]|0)==1){ac=49176}else{T=9029}}else{T=9029}if((T|0)==9029){T=0;ac=V}U=c[ac>>2]|0;if((U|0)==1|(U|0)==4){ad=(c[ac+4>>2]<<4&65520)+U|0}else if((U|0)==2|(U|0)==5){ad=(c[ac+8>>2]<<4&65520)+U|0}else{ad=0}U=c[R+12>>2]|0;if((U|0)==1){do{if((c[R+104>>2]|0)==1){ae=R+112|0;do{if(D){T=9089}else{if((c[ae>>2]|0)==3){T=9089;break}if((c[5094]|0)==0){break L12294}fK(ae,m,n,173728);fx(R+152|0,t,u,173728);W=+(c[t>>2]|0);h[r>>3]=W;X=+(c[u>>2]|0);h[s>>3]=X;af=W;ag=X}}while(0);if((T|0)==9089){T=0;fR(ae,m,n,173728);fN(R+152|0,r,s,173728);af=+h[r>>3];ag=+h[s>>3]}X=+h[m>>3]-af*.5;h[m>>3]=X;W=+h[n>>3]-ag*.5;h[n>>3]=W;ah=af+X;h[o>>3]=ah;ai=ag+W;h[p>>3]=ai;aj=(c[R+156>>2]|0)>>>0<2;ak=(c[R+152>>2]|0)>>>0<2;al=ah;am=X;an=ai;ao=W}else{ap=R+192|0;do{if(D){T=9096}else{if((c[ap>>2]|0)==3){if((c[R+232>>2]|0)==3){T=9096;break}}if((c[5094]|0)==0){break L12294}fK(ap,m,n,173728);fK(R+232|0,o,p,173728)}}while(0);if((T|0)==9096){T=0;fR(ap,m,n,173728);fR(R+232|0,o,p,173728)}W=+h[m>>3];ai=+h[o>>3];if(W>ai){h[m>>3]=ai;h[o>>3]=W;aq=W;ar=ai}else{aq=ai;ar=W}W=+h[n>>3];ai=+h[p>>3];if(W>ai){h[n>>3]=ai;h[p>>3]=W;as=W;at=ai}else{as=ai;at=W}if((c[R+192>>2]|0)==3){au=0}else{au=(c[R+232>>2]|0)!=3}if((c[R+196>>2]|0)==3){aj=0;ak=au;al=aq;am=ar;an=as;ao=at;break}aj=(c[R+236>>2]|0)!=3;ak=au;al=aq;am=ar;an=as;ao=at}}while(0);S=c[13542]|0;if((S|0)==0){av=al;aw=am;ax=an;ay=ao}else{do{if(ak){W=+(c[S>>2]|0);if(am<W){h[m>>3]=W;az=W}else{az=am}W=+(c[S+4>>2]|0);if(al<=W){aA=az;aB=al;break}h[o>>3]=W;aA=az;aB=W}else{aA=am;aB=al}}while(0);do{if(aj){W=+(c[S+8>>2]|0);if(ao<W){h[n>>3]=W;aC=W}else{aC=ao}W=+(c[S+12>>2]|0);if(an<=W){aD=aC;aE=an;break}h[p>>3]=W;aD=aC;aE=W}else{aD=ao;aE=an}}while(0);if(aA>aB|aD>aE){break}else{av=aB;aw=aA;ax=aE;ay=aD}}S=~~(av-aw);ae=~~(ax-ay);aF=~~aw;aG=~~ay;if((S|0)==0|(ae|0)==0){break}aH=c[b>>2]|0;if((aH|0)==-7){aI=c[12302]|0;aJ=c[12303]|0;W=+h[6153];ai=+h[6154];aK=a[49240]|0;uD(N|0,49241,23);aL=aI;aM=aJ;aN=W;aO=ai;aP=aK}else{aK=c[R+48>>2]|0;ai=+h[R+64>>3];W=+h[R+72>>3];aJ=a[R+80|0]|0;uD(N|0,R+81|0,23);aL=aK;aM=aH;aN=ai;aO=W;aP=aJ}if(aN>0.0){aQ=+h[R+64>>3]}else{aQ=aN}aJ=(c[Y>>2]|0)==3?49176:V;uD(O|0,P|0,16);do{if((aL|0)!=0){aH=c[(c[3524]|0)+92>>2]|0;if(aO<0.0){cK[aH&63](+h[3817]);break}else{cK[aH&63](aO);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](aQ);V=c[(c[3524]|0)+64>>2]|0;if((aM|0)<-5){cM[V&511](-2)}else{cM[V&511](aM)}V=c[3524]|0;do{if((aP&1)==0){if((c[V+96>>2]&1024|0)!=0){break}c[q>>2]=1;c[Q>>2]=aM;T=9135}else{T=9135}}while(0);if((T|0)==9135){T=0;fn(l,V)}Y=c[aJ>>2]|0;if((Y|0)==1|(Y|0)==4){aR=aJ+4|0;T=9139}else if((Y|0)==2|(Y|0)==5){aR=aJ+8|0;T=9139}do{if((T|0)==9139){T=0;b=(c[aR>>2]<<4&65520)+Y|0;if((b|0)==0){break}aH=c[(c[3524]|0)+108>>2]|0;if((aH|0)==0){break}cI[aH&63](b,aF,aG,S,ae)}}while(0);if(!(hG(aJ)|0)){break}cN[c[(c[3524]|0)+56>>2]&255](aF,aG);Y=ae+aG|0;cN[c[(c[3524]|0)+60>>2]&255](aF,Y);V=S+aF|0;cN[c[(c[3524]|0)+60>>2]&255](V,Y);cN[c[(c[3524]|0)+60>>2]&255](V,aG);cN[c[(c[3524]|0)+60>>2]&255](aF,aG);break}else if((U|0)==2){V=c[13542]|0;Y=R+112|0;do{if(D){T=9036}else{if((c[Y>>2]|0)==3){T=9036;break}if((c[5094]|0)==0){break L12294}fK(Y,v,w,173728);fx(R+152|0,A,B,173728);h[y>>3]=+(c[A>>2]|0)}}while(0);if((T|0)==9036){T=0;fR(Y,v,w,173728);fN(R+152|0,y,z,173728)}uD(E|0,F|0,16);do{if((Z|0)!=0){aG=c[(c[3524]|0)+92>>2]|0;if(aa<0.0){cK[aG&63](+h[3817]);break}else{cK[aG&63](aa);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63]($);Y=c[(c[3524]|0)+64>>2]|0;if((_|0)<-5){cM[Y&511](-2)}else{cM[Y&511](_)}Y=c[3524]|0;do{if((ab&1)==0){if((c[Y+96>>2]&1024|0)!=0){break}c[x>>2]=1;c[G>>2]=_;T=9049}else{T=9049}}while(0);if((T|0)==9049){T=0;fn(k,Y)}do{if((c[R+112>>2]|0)==3){if((c[R+116>>2]|0)!=3){break}c[13542]=55520}}while(0);Y=R+192|0;aG=R+200|0;lq(~~+h[v>>3],~~+h[w>>3],+h[y>>3],+h[Y>>3],+h[aG>>3],ad);if(hG(ac)|0){lq(~~+h[v>>3],~~+h[w>>3],+h[y>>3],+h[Y>>3],+h[aG>>3],0)}c[13542]=V;break}else if((U|0)==3){uD(H|0,F|0,16);do{if((Z|0)!=0){aG=c[(c[3524]|0)+92>>2]|0;if(aa<0.0){cK[aG&63](+h[3817]);break}else{cK[aG&63](aa);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63]($);V=c[(c[3524]|0)+64>>2]|0;if((_|0)<-5){cM[V&511](-2)}else{cM[V&511](_)}V=c[3524]|0;do{if((ab&1)==0){if((c[V+96>>2]&1024|0)!=0){break}c[I>>2]=1;c[J>>2]=_;T=9066}else{T=9066}}while(0);if((T|0)==9066){T=0;fn(j,V)}if(D){fP(2,R+104|0,ad,1)}else{if((c[5094]|0)==0){break}fP(3,R+104|0,ad,1)}if(!(hG(ac)|0)){break}fP(e,R+104|0,0,1);break}else if((U|0)==4){uD(K|0,F|0,16);do{if((Z|0)!=0){aG=c[(c[3524]|0)+92>>2]|0;if(aa<0.0){cK[aG&63](+h[3817]);break}else{cK[aG&63](aa);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63]($);U=c[(c[3524]|0)+64>>2]|0;if((_|0)<-5){cM[U&511](-2)}else{cM[U&511](_)}U=c[3524]|0;do{if((ab&1)==0){if((c[U+96>>2]&1024|0)!=0){break}c[L>>2]=1;c[M>>2]=_;T=9083}else{T=9083}}while(0);if((T|0)==9083){T=0;fn(g,U)}V=R+104|0;fQ(e,V,ad);if(!(hG(ac)|0)){break}fQ(e,V,0);break}else{break}}}while(0);R=c[R>>2]|0;}while((R|0)!=0);i=f;return}function fP(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0.0,w=0.0,x=0.0,y=0.0,z=0,A=0,B=0.0,C=0.0,D=0.0,E=0.0,F=0,G=0,H=0,I=0.0,J=0.0,K=0;f=i;i=i+1568|0;g=f|0;j=f+8|0;k=f+16|0;l=f+24|0;m=f+32|0;n=f+1472|0;o=f+1480|0;p=f+1488|0;q=f+1496|0;r=f+1504|0;s=f+1512|0;t=f+1520|0;u=f+1528|0;v=+h[b+88>>3]*.017453292519943295;w=+S(+v);x=+T(+v);v=+h[b+64>>3]*.5;y=+h[b+72>>3]*.5;z=u;A=b+48|0;c[z>>2]=c[A>>2];c[z+4>>2]=c[A+4>>2];c[z+8>>2]=c[A+8>>2];c[z+12>>2]=c[A+12>>2];c[z+16>>2]=c[A+16>>2];c[z+20>>2]=c[A+20>>2];c[z+24>>2]=c[A+24>>2];c[z+28>>2]=c[A+28>>2];c[z+32>>2]=c[A+32>>2];c[z+36>>2]=c[A+36>>2];A=c[3524]|0;B=+((c[A+24>>2]|0)>>>0>>>0)/+((c[A+28>>2]|0)>>>0>>>0);do{if(e){A=b+8|0;if((a|0)==2){fR(A,n,o,73152);break}else{fK(A,n,o,73152);break}}else{h[n>>3]=+h[b+24>>3];h[o>>3]=+h[b+32>>3]}}while(0);c[m+8>>2]=d;C=w*v;D=x*y;A=u+16|0;E=x*v;v=w*y;z=u+24|0;F=(a|0)==2;a=b|0;b=n;G=o;y=0.0;H=0;while(1){w=+S(+y);x=+T(+y);I=C*w-D*x;h[A>>3]=I;J=E*w+v*x;h[z>>3]=J;do{if(e){K=c[a>>2]|0;if(F){if((K|0)==2){fN(u,r,q,73152);h[z>>3]=+h[A>>3];fN(u,r,p,73152);break}else if((K|0)==0){fN(u,p,q,73152);break}else if((K|0)==1){fN(u,p,r,73152);h[A>>3]=+h[z>>3];fN(u,q,r,73152);break}else{break}}else{if((K|0)==2){fx(u,s,t,73152);h[q>>3]=+(c[t>>2]|0);h[z>>3]=+h[A>>3];fx(u,t,s,73152);h[p>>3]=+(c[s>>2]|0);break}else if((K|0)==0){fx(u,s,t,73152);h[p>>3]=+(c[s>>2]|0);h[q>>3]=+(c[t>>2]|0);break}else if((K|0)==1){fx(u,s,t,73152);h[p>>3]=+(c[s>>2]|0);h[A>>3]=+h[z>>3];fx(u,t,s,73152);h[q>>3]=+(c[t>>2]|0);break}else{break}}}else{h[p>>3]=I;h[q>>3]=J}}while(0);K=m+(H*12&-1)|0;c[K>>2]=~~(+h[n>>3]+ +h[p>>3]);J=+h[o>>3];I=+h[q>>3];if(e){c[m+(H*12&-1)+4>>2]=~~(J+I)}else{c[m+(H*12&-1)+4>>2]=~~(J+B*I)}fl(b,G,K,m+(H*12&-1)+4|0);K=H+1|0;if((K|0)<73){y=y+.08726646259971647;H=K}else{break}}H=m|0;if((d|0)!=0){d=c[(c[3524]|0)+148>>2]|0;if((d|0)==0){i=f;return}cN[d&255](72,H);i=f;return}H=0;while(1){d=c[m+(H*12&-1)+4>>2]|0;G=H+1|0;b=c[m+(G*12&-1)>>2]|0;e=c[m+(G*12&-1)+4>>2]|0;c[g>>2]=c[m+(H*12&-1)>>2];c[j>>2]=d;c[k>>2]=b;c[l>>2]=e;e=c[3524]|0;if((fl(g,j,k,l)|0)!=0){cN[c[e+56>>2]&255](c[g>>2]|0,c[j>>2]|0);cN[c[e+60>>2]&255](c[k>>2]|0,c[l>>2]|0)}if((G|0)<72){H=G}else{break}}i=f;return}function fQ(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0;e=i;i=i+112|0;f=e|0;g=e+8|0;j=e+16|0;k=e+24|0;l=e+32|0;m=e+40|0;n=e+48|0;o=e+56|0;p=e+64|0;q=e+72|0;r=e+80|0;s=e+88|0;t=e+96|0;u=c[13542]|0;v=b+4|0;if((c[v>>2]|0)==0){i=e;return}w=b|0;b=c[w>>2]|0;if((b|0)<2){i=e;return}c[11720]=db(c[11720]|0,b*12&-1,218624)|0;c[11722]=db(c[11722]|0,(c[w>>2]|0)*24&-1,218624)|0;do{if((c[w>>2]|0)>0){b=(a|0)==3;x=0;y=0;z=c[v>>2]|0;while(1){A=z+(y*40&-1)|0;B=c[11720]|0;C=B+(y*12&-1)|0;D=B+(y*12&-1)+4|0;if(b){fK(A,r,s,213112);c[C>>2]=~~+h[r>>3];c[D>>2]=~~+h[s>>3]}else{fR(A,p,q,213112);c[C>>2]=~~+h[p>>3];c[D>>2]=~~+h[q>>3]}D=c[v>>2]|0;C=c[D+(y*40&-1)>>2]|0;if((C|0)==3){E=9189}else{if((c[D+(y*40&-1)+4>>2]|0)==3){E=9189}else{F=x}}if((E|0)==9189){E=0;F=1}if((C|0)==2){E=9192}else{if((c[D+(y*40&-1)+4>>2]|0)==2){E=9192}else{G=F}}if((E|0)==9192){E=0;G=1}C=y+1|0;if((C|0)<(c[w>>2]|0)){x=G;y=C;z=D}else{break}}if(!G){H=y;break}c[13542]=55520;H=y}else{H=-1}}while(0);G=c[3524]|0;do{if((c[G+148>>2]|0)==0|(d|0)==0){w=c[G+172>>2]|0;if((w|0)!=0){cM[w&511](0)}w=c[11720]|0;F=c[w>>2]|0;do{if((H|0)>0){v=0;q=w;p=F;do{s=c[q+(v*12&-1)+4>>2]|0;v=v+1|0;r=c[q+(v*12&-1)>>2]|0;a=c[q+(v*12&-1)+4>>2]|0;c[l>>2]=p;c[m>>2]=s;c[n>>2]=r;c[o>>2]=a;a=c[3524]|0;if((fl(l,m,n,o)|0)!=0){cN[c[a+56>>2]&255](c[l>>2]|0,c[m>>2]|0);cN[c[a+60>>2]&255](c[n>>2]|0,c[o>>2]|0)}q=c[11720]|0;p=c[q+(v*12&-1)>>2]|0}while((v|0)<(H|0));v=c[q>>2]|0;if((p|0)==(v|0)){I=v;J=q;K=H;E=9216;break}L=c[q+(H*12&-1)+4>>2]|0;M=c[q+4>>2]|0;N=q;O=v;P=H;E=9217}else{I=F;J=w;K=0;E=9216}}while(0);if((E|0)==9216){w=c[J+(K*12&-1)+4>>2]|0;F=c[J+4>>2]|0;if((w|0)!=(F|0)){L=w;M=F;N=J;O=I;P=K;E=9217}}do{if((E|0)==9217){c[f>>2]=c[N+(P*12&-1)>>2];c[g>>2]=L;c[j>>2]=O;c[k>>2]=M;F=c[3524]|0;if((fl(f,g,j,k)|0)==0){break}cN[c[F+56>>2]&255](c[f>>2]|0,c[g>>2]|0);cN[c[F+60>>2]&255](c[j>>2]|0,c[k>>2]|0)}}while(0);F=c[(c[3524]|0)+172>>2]|0;if((F|0)==0){break}cM[F&511](1)}else{F=t;w=1;y=0;v=0;L12560:while(1){Q=w;R=v;while(1){if((R|0)>=(H|0)){break L12560}a=c[11720]|0;r=(c[11722]|0)+(y*12&-1)|0;s=a+(R*12&-1)|0;c[r>>2]=c[s>>2];c[r+4>>2]=c[s+4>>2];c[r+8>>2]=c[s+8>>2];S=R+1|0;s=a+(S*12&-1)|0;c[F>>2]=c[s>>2];c[F+4>>2]=c[s+4>>2];c[F+8>>2]=c[s+8>>2];T=fl(a+(R*12&-1)|0,a+(R*12&-1)+4|0,a+(S*12&-1)|0,a+(S*12&-1)+4|0)|0;if((T|0)==0){Q=0;R=S}else{break}}q=((T|0)>0&1)+y|0;if((T|0)>=0){w=T;y=q;v=S;continue}p=c[11722]|0;a=c[11720]|0;s=p+(q*12&-1)|0;r=a+(R*12&-1)|0;c[s>>2]=c[r>>2];c[s+4>>2]=c[r+4>>2];c[s+8>>2]=c[r+8>>2];r=p+((q+1|0)*12&-1)|0;p=a+(S*12&-1)|0;c[r>>2]=c[p>>2];c[r+4>>2]=c[p+4>>2];c[r+8>>2]=c[p+8>>2];c[p>>2]=c[F>>2];c[p+4>>2]=c[F+4>>2];c[p+8>>2]=c[F+8>>2];w=T;y=q+2|0;v=S}if((Q|0)==1){v=c[11722]|0;w=v+(y*12&-1)|0;F=(c[11720]|0)+(R*12&-1)|0;c[w>>2]=c[F>>2];c[w+4>>2]=c[F+4>>2];c[w+8>>2]=c[F+8>>2];U=y+1|0;V=v}else{U=y;V=c[11722]|0}c[V+8>>2]=d;cN[c[(c[3524]|0)+148>>2]&255](U,c[11722]|0)}}while(0);c[13542]=u;i=e;return}function fR(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0;g=i;j=c[b>>2]|0;if((j|0)==4){h[d>>3]=+h[b+16>>3]*+((c[(c[3524]|0)+20>>2]|0)>>>0>>>0)}else if((j|0)==2){k=c[7938]|0;h[d>>3]=+(k|0)+ +h[b+16>>3]*+((c[7939]|0)-k|0)}else if((j|0)==1){l=+h[b+16>>3];do{if((a[68916]&1)==0){m=l}else{if(l>0.0){n=+_(+l);m=n/+h[8616];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=56976,h[v+16>>3]=l,v)|0)}}}while(0);h[d>>3]=+(~~(+(c[17224]|0)+(m- +h[8599])*+h[8613]+.5)|0)}else if((j|0)==3){h[d>>3]=+h[b+16>>3]*+(((c[(c[3524]|0)+8>>2]|0)-1|0)>>>0>>>0)}else if((j|0)==0){m=+h[b+16>>3];do{if((a[66164]&1)==0){o=m}else{if(m>0.0){l=+_(+m);o=l/+h[8272];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=56880,h[v+16>>3]=m,v)|0)}}}while(0);h[d>>3]=+(~~(+(c[16536]|0)+(o- +h[8255])*+h[8269]+.5)|0)}j=c[b+4>>2]|0;if((j|0)==2){k=c[7940]|0;h[e>>3]=+(k|0)+ +h[b+24>>3]*+((c[7941]|0)-k|0)}else if((j|0)==3){h[e>>3]=+h[b+24>>3]*+(((c[(c[3524]|0)+12>>2]|0)-1|0)>>>0>>>0)}else if((j|0)==1){o=+h[b+24>>3];do{if((a[68228]&1)==0){p=o}else{if(o>0.0){m=+_(+o);p=m/+h[8530];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=56952,h[v+16>>3]=o,v)|0)}}}while(0);h[e>>3]=+(~~(+(c[17052]|0)+(p- +h[8513])*+h[8527]+.5)|0)}else if((j|0)==0){p=+h[b+24>>3];do{if((a[65476]&1)==0){q=p}else{if(p>0.0){o=+_(+p);q=o/+h[8186];break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=56856,h[v+16>>3]=p,v)|0)}}}while(0);h[e>>3]=+(~~(+(c[16364]|0)+(q- +h[8169])*+h[8183]+.5)|0)}else if((j|0)==4){h[e>>3]=+h[b+24>>3]*+((c[(c[3524]|0)+16>>2]|0)>>>0>>>0)}h[d>>3]=+h[d>>3]+.5;h[e>>3]=+h[e>>3]+.5;i=g;return}function fS(b,d){b=b|0;d=d|0;var e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,R=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0.0,a6=0.0,a7=0.0,a8=0.0,a9=0.0,ba=0.0,bb=0.0,bc=0.0,bd=0.0,be=0.0,bf=0.0,bg=0.0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bx=0,by=0,bz=0,bA=0,bB=0,bC=0,bD=0.0,bE=0.0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bN=0,bO=0,bP=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0,b7=0,b8=0,b9=0,ca=0,cb=0,cc=0,cd=0,ce=0,cf=0,cg=0,ch=0,ci=0,cj=0,ck=0,cl=0,cm=0,cn=0,co=0,cp=0,cq=0,cr=0,cs=0.0,ct=0.0,cu=0,cv=0,cw=0,cx=0,cz=0,cA=0.0,cB=0,cC=0.0,cD=0,cE=0,cF=0.0,cH=0.0,cJ=0.0,cL=0,cP=0,cQ=0,cT=0,cU=0,cV=0,cW=0,cX=0,cY=0,cZ=0,c_=0,c$=0,c0=0,c1=0,c2=0,c3=0,c4=0,c5=0,c6=0,c7=0,c8=0,c9=0,da=0,df=0,dh=0,di=0,dj=0,dl=0,dm=0,dn=0,dp=0,dq=0,dr=0.0,ds=0.0,dt=0.0,du=0.0,dv=0.0,dw=0.0,dx=0.0,dy=0.0,dA=0,dB=0,dC=0,dE=0,dF=0,dG=0,dH=0,dI=0,dJ=0.0,dK=0.0,dL=0,dM=0,dN=0,dO=0,dP=0,dQ=0,dR=0,dS=0,dT=0,dU=0,dV=0.0,dW=0.0,dX=0.0,dY=0.0,dZ=0.0,d_=0,d$=0,d0=0,d1=0.0,d2=0.0,d3=0,d4=0,d5=0.0,d6=0.0,d7=0.0,d8=0.0,d9=0.0,ea=0,eb=0,ec=0,ed=0,ee=0,ef=0.0,eg=0.0,eh=0,ei=0.0,ej=0,ek=0,el=0.0,em=0,en=0,eo=0,ep=0,eq=0,er=0,es=0.0,et=0,eu=0,ev=0.0,ew=0.0,ex=0,ey=0,ez=0.0,eA=0.0,eB=0.0,eC=0,eD=0,eE=0,eF=0.0,eG=0,eH=0,eI=0,eJ=0,eK=0.0,eL=0.0,eM=0.0,eN=0,eO=0.0,eP=0.0,eQ=0.0,eR=0.0,eS=0.0,eT=0,eU=0.0,eV=0.0,eW=0,eX=0,eY=0.0,eZ=0.0,e_=0,e$=0,e0=0,e1=0,e2=0,e3=0,e4=0,e5=0,e6=0,e7=0,e8=0,e9=0,fa=0,fb=0.0,fc=0,fd=0,fe=0,ff=0,fg=0,fh=0.0,fi=0.0,fj=0;e=i;i=i+1424|0;f=e|0;j=e+8|0;k=e+16|0;l=e+24|0;m=e+32|0;n=e+40|0;o=e+48|0;p=e+56|0;q=e+64|0;r=e+80|0;s=e+352|0;t=e+368|0;u=e+384|0;w=e+392|0;x=e+400|0;y=e+416|0;z=e+424|0;A=e+432|0;B=e+496|0;C=e+504|0;D=e+512|0;E=e+552|0;F=e+568|0;G=e+576|0;H=e+584|0;I=e+624|0;J=e+640|0;K=e+768|0;L=e+776|0;M=e+784|0;N=e+808|0;O=e+816|0;Q=e+824|0;R=e+840|0;U=e+856|0;V=e+864|0;W=e+880|0;X=e+888|0;Y=e+896|0;Z=e+904|0;$=e+912|0;ab=e+920|0;ac=e+928|0;ad=e+936|0;ae=e+944|0;af=e+952|0;ag=e+960|0;ah=e+968|0;ai=e+976|0;aj=e+984|0;ak=e+992|0;al=e+1008|0;am=e+1016|0;an=e+1024|0;ao=e+1032|0;ap=e+1040|0;aq=e+1048|0;ar=e+1056|0;as=e+1064|0;at=e+1072|0;au=e+1080|0;av=e+1088|0;aw=e+1096|0;ax=e+1104|0;ay=e+1112|0;az=e+1120|0;aA=e+1128|0;aB=e+1136|0;aC=e+1144|0;aD=e+1152|0;aE=e+1160|0;aF=e+1168|0;aG=e+1176|0;aH=e+1184|0;aI=e+1192|0;aJ=e+1200|0;aK=e+1208|0;aL=e+1216|0;aM=e+1224|0;aO=e+1232|0;aP=e+1240|0;aQ=e+1248|0;aR=e+1256|0;aS=e+1264|0;aT=e+1272|0;aU=e+1280|0;aV=e+1288|0;aW=e+1296|0;aX=e+1304|0;aY=e+1312|0;aZ=e+1320|0;a_=e+1328|0;a$=e+1336|0;a0=e+1344|0;a1=e+1352|0;a2=e+1360|0;a3=e+1368|0;a4=c[3524]|0;c[aS>>2]=0;c[aT>>2]=0;c[200]=2;c[144]=1;if((c[14091]|0)==2){a5=+P(+(+h[8170]- +h[8169]));a6=a5*+h[7048]}else{a6=+h[7048]}if((c[1057]|0)==2){a5=+P(+(+h[8170]- +h[8169]));a7=a5*+h[531]}else{a7=+h[531]}if((c[8764]|0)==2){a5=+P(+(+h[8256]- +h[8255]));a8=a5*+h[4384]}else{a8=+h[4384]}if((c[6588]|0)==2){a5=+P(+(+h[8256]- +h[8255]));a9=a5*+h[3296]}else{a9=+h[3296]}a5=+h[8169];ba=+h[8170];if(a5<ba){bb=a5-a6;h[8169]=bb;bc=a7+ba;h[8170]=bc;bd=bb;be=bc}else{bc=ba-a6;h[8170]=bc;a6=a7+a5;h[8169]=a6;bd=a6;be=bc}bc=+h[8255];a6=+h[8256];if(bc<a6){a5=bc-a8;h[8255]=a5;a7=a9+a6;h[8256]=a7;bf=a5;bg=a7}else{a7=a6-a8;h[8256]=a7;a8=a9+bc;h[8255]=a8;bf=a8;bg=a7}if(bf==bg){uf(-1,126120,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(bd==be){uf(-1,124848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}lk();if((a[14080]&1)==0){lk()}do{if(a[14088]|0){if((a[33512]&1)==0){break}if(!(a[13032]|0)){break}bh=c[(c[3524]|0)+104>>2]|0;if((bh|0)!=0){cS[bh&511]()}a[13032]=0}else{cS[c[(c[3524]|0)+52>>2]&511]();a[14088]=1}}while(0);cM[c[(c[3524]|0)+168>>2]&511](0);bh=c[3524]|0;if((c[bh+96>>2]&16|0)!=0){c[7271]=-1}c[13880]=0;c[13881]=(c[bh+8>>2]|0)-1;c[13882]=0;c[13883]=(c[bh+12>>2]|0)-1;i6(0,1);c[ap>>2]=0;c[aq>>2]=0;c[ar>>2]=0;bh=c[3524]|0;bi=bh+72|0;bj=(cO[c[bi>>2]&255](-270)|0)!=0;bk=bj?c[1164]|0:0;bl=c[16602]|0;bm=c[17290]|0;bn=bj?c[16430]|0:0;bo=bj?c[17118]|0:0;a[35184]=a[36120]&1;c[1098]=0;c[206]=0;c[188]=0;c[148]=0;c[48]=0;c[174]=0;bp=c[1119]|0;if((bp|0)!=0){ga(bp,4392)}bp=c[16629]|0;if((bp|0)!=0){ga(bp,752)}do{if((c[(c[3524]|0)+96>>2]&32|0)!=0){bp=c[16629]|0;if((bp|0)==0){break}if((cy(bp|0,137224)|0)==0){break}c[188]=(c[188]|0)+1}}while(0);bp=c[17317]|0;if((bp|0)!=0){ga(bp,824)}bp=c[16457]|0;if((bp|0)!=0){ga(bp,192)}bp=c[17145]|0;if((bp|0)!=0){ga(bp,592)}do{if((c[16573]|0)!=0){ga(66240,696);if((c[174]|0)!=0|(c[16580]|0)==0){break}c[174]=1}}while(0);if((c[17261]|0)!=0){ga(68992,808)}if((c[16401]|0)!=0){ga(65552,ap)}if((c[17089]|0)!=0){ga(68304,aq)}bp=c[1183]|0;if((bp|0)!=0){ga(bp,ar)}if((c[1098]|0)==0){bq=0}else{fN(4560,as,at,135256);be=+(aa((c[1098]|0)+1|0,c[bh+16>>2]|0)>>>0>>>0);bq=~~(+h[at>>3]+be)}do{if((c[206]|0)==0){br=0;bs=c[17261]|0;bt=9319}else{fN(69352,au,av,135256);at=c[bh+16>>2]|0;be=+(aa(at,c[206]|0)>>>0>>>0);as=~~(+h[av>>3]+be);bp=c[17261]|0;if((bp|0)!=0){br=as;bs=bp;bt=9319;break}bu=0;bv=~~(+(at>>>0>>>0)*.5+ +(as|0));bx=1}}while(0);do{if((bt|0)==9319){if((bs&1|0)==0){bu=0;bv=br;bx=1;break}bu=aa(c[bh+16>>2]|0,c[202]|0);bv=br;bx=0}}while(0);do{if((a[69200]&1)==0){if(bx){if((c[16573]&5|0)!=5){by=0;break}}by=~~(+((c[bh+24>>2]|0)>>>0>>>0)*+h[8648])}else{by=0}}while(0);if((c[1183]|0)==0|(c[1166]|0)!=0){bz=0}else{fN(4816,aw,ax,135256);be=+(aa((c[ar>>2]|0)+2|0,c[bh+16>>2]|0)>>>0>>>0);bz=~~(+h[ax>>3]+be)}if((c[16457]|0)==0|bj){bA=0}else{fN(65912,ay,az,135256);be=+(aa(c[bh+16>>2]|0,c[48]|0)>>>0>>>0);bA=~~(+h[az>>3]+be)}if((c[17145]|0)==0|bj){bB=0}else{fN(68664,aA,aB,135256);be=+(aa(c[bh+16>>2]|0,c[148]|0)>>>0>>>0);bB=~~(+h[aB>>3]+be)}be=+g[38]+ +g[44];aB=bh+12|0;aA=~~(be*+(((c[aB>>2]|0)-1|0)>>>0>>>0)+.5);c[7941]=aA;bd=+h[535];do{if((c[1066]|0)==3){c[7941]=~~(bd*+(((c[aB>>2]|0)-1|0)>>>0>>>0))}else{if(bd>=0.0){c[7941]=aA-~~(bd*+((c[bh+16>>2]|0)>>>0>>>0)+.5);break}az=bv+bq|0;ay=bA+bz|0;ax=(ay|0)>(az|0)?ay:az;az=(by+bu|0)+((bB|0)>(ax|0)?bB:ax)|0;if((az|0)>(by|0)){bC=(c[bh+16>>2]|0)+az|0}else{bC=az}az=aA-bC|0;c[7941]=az;if((az|0)!=(~~(be*+(((c[aB>>2]|0)-1|0)>>>0>>>0)+.5)|0)){break}c[7941]=az-(c[bh+20>>2]<<1)}}while(0);if((c[8786]|0)==3){bD=+h[4395]*+((c[bh+8>>2]|0)>>>0>>>0)}else{be=+h[4395];bD=+g[184]*+((c[bh+8>>2]|0)>>>0>>>0)+ +((c[bh+20>>2]|0)>>>0>>>0)*(be>=0.0?be:1.0)}c[7938]=~~bD;if((c[6598]|0)==3){bE=+h[3301]*+(((c[bh+8>>2]|0)-1|0)>>>0>>>0)}else{bD=+h[3301];bE=(+g[178]+ +g[184])*+(((c[bh+8>>2]|0)-1|0)>>>0>>>0)- +((c[bh+20>>2]|0)>>>0>>>0)*(bD>=0.0?bD:2.0)}c[7939]=~~bE;bC=c[16573]|0;if((bC&2|0)==0){bF=0;bt=9351}else{bE=+h[8169];bD=+h[8170];if(bE<bD){if(bE>0.0|bD<0.0){bt=9349}else{bG=0}}else{if(bD>0.0|bE<0.0){bt=9349}else{bG=0}}if((bt|0)==9349){bG=1}if(+P(+(bE/(bD-bE)))<.05){bt=9352}else{bF=bG;bt=9351}}if((bt|0)==9351){if((bC&1|0)!=0|bF){bt=9352}else{bH=0}}if((bt|0)==9352){bH=aa((c[174]|0)+1|0,c[bh+16>>2]|0)}do{if((a[66448]&1)==0){if((bC&1|0)==0){if((c[17261]&5|0)!=5){bI=0;break}}bI=~~(+((c[bh+24>>2]|0)>>>0>>>0)*+h[8304])}else{bI=0}}while(0);do{if((c[188]|0)==0){bJ=0}else{fN(66600,aC,aD,135256);bE=+((c[bh+16>>2]|0)>>>0>>>0);bC=~~(bE*(+(c[188]|0)+.2)- +h[aD>>3]);if((c[16573]|0)!=0){bJ=bC;break}bJ=~~(bE*.5+ +(bC|0))}}while(0);if((c[1183]|0)!=0&(c[1166]|0)!=0){fN(4816,aE,aF,135256);bE=+(aa(c[bh+16>>2]|0,c[ar>>2]|0)>>>0>>>0);bK=~~(bE- +h[aF>>3])}else{bK=0}bE=+g[44];aF=~~(bE*+((c[aB>>2]|0)>>>0>>>0));c[7940]=aF;bD=+h[7052];do{if((c[14100]|0)==3){c[7940]=~~(bD*+((c[aB>>2]|0)>>>0>>>0))}else{if(bD>=0.0){c[7940]=~~(+(aF|0)+(bD*+((c[bh+16>>2]|0)>>>0>>>0)+.5));break}aE=(((bI+bH|0)+((bJ|0)>0?bJ:0)|0)+((bK|0)>0?bK:0)|0)+aF|0;c[7940]=aE;if((aE|0)!=(~~(bE*+((c[aB>>2]|0)>>>0>>>0))|0)){break}c[7940]=(c[bh+20>>2]<<1)+aE}}while(0);L12774:do{if((a[35184]&1)==0){bL=1;bN=0}else{bE=+h[3817];aF=~~(bE*+((c[bh+28>>2]|0)>>>0>>>0));c[8240]=aF;aE=~~(bE*+((c[bh+24>>2]|0)>>>0>>>0));c[8242]=aE;bE=+h[4524];if(bE<0.0){bO=0}else{bO=~~(+(aF|0)+bE*+((c[bh+20>>2]|0)>>>0>>>0))}c[9e3]=bO;bE=+h[4525];aF=~~(+(aE|0)*1.25*bE);c[9020]=aF;aE=bh+16|0;aD=c[aE>>2]|0;if(aF>>>0<aD>>>0){aC=~~(bE*+(aD>>>0>>>0));c[9020]=aC;bP=aC}else{bP=aF}if((bP|0)==0){c[9020]=1}if((d|0)>0){aF=0;aC=0;aD=0;bC=0;bF=b;while(1){bG=bF+16|0;aA=c[bG>>2]|0;do{if((aA|0)==0){bQ=aD;bR=aF}else{if((a[bF+22|0]&1)!=0){bQ=aD;bR=aF;break}az=a[bF+20|0]&1;do{if(az<<24>>24==0){bS=aA}else{if((a[37456]&1)!=0){bS=aA;break}a[37456]=1;ax=c[(c[3524]|0)+88>>2]|0;if((ax|0)==0){bS=aA;break}cO[ax&255](179864);bS=c[bG>>2]|0}}while(0);a[37456]=az;ax=lp(bS)|0;if((ax|0)==0){bT=aD;bU=aF}else{bT=aD+1|0;bU=(ax|0)>(aF|0)?ax:aF}a[37456]=0;bQ=bT;bR=bU}}while(0);bG=bF+12|0;aA=c[bG>>2]|0;ax=(aA|0)==392;if(ax&(bC|0)==392){bV=((c[bF+280>>2]|0)==0&(bQ|0)>1&1)+bQ|0}else{bV=bQ}do{if(ax){ay=c[bF+224>>2]|0;if((ay|0)==0){bW=bV;bX=bR;bY=392;break}aw=c[ay>>2]|0;if((aw|0)==0){bW=bV;bX=bR;bY=392;break}else{bZ=bR;b_=bV;b$=aw}do{b_=b_+1|0;aw=c[b$+60>>2]|0;if((aw|0)==0){b0=0}else{b0=lp(aw)|0}bZ=(b0|0)>(bZ|0)?b0:bZ;b$=c[b$>>2]|0;}while((b$|0)!=0);bW=b_;bX=bZ;bY=c[bG>>2]|0}else{bW=bV;bX=bR;bY=aA}}while(0);aA=aC+1|0;if((aA|0)<(d|0)){aF=bX;aC=aA;aD=bW;bC=bY;bF=c[bF>>2]|0}else{b1=bX;b2=bW;break}}}else{b1=0;b2=0}c[6960]=b2;c[8710]=b1;bF=ga(36288,aG)|0;bC=~~(+(bF|0)-(+h[4524]+2.0));bF=c[8710]|0;aD=(c[9047]|0)==1;if((bC|0)<=(bF|0)|aD){b3=bF}else{c[8710]=bC;b3=bC}bE=+(c[aG>>2]|0);h[4488]=bE;if((a[36229]&1)==0){c[9008]=0;bC=c[9e3]|0;c[9004]=bC;bF=bh+20|0;aC=~~(+((c[bF>>2]|0)>>>0>>>0)*(+(b3+1|0)+ +h[4526]));c[8988]=-aC;c[8984]=-(c[bF>>2]|0);c[8996]=aC;b4=(c[bF>>2]|0)+bC|0;b5=0;b6=bC;b7=aC}else{aC=c[9e3]|0;bC=-aC|0;c[9008]=bC;c[9004]=0;bF=bh+20|0;c[8988]=c[bF>>2];aF=~~(+((c[bF>>2]|0)>>>0>>>0)*(+(b3+1|0)+ +h[4526]));c[8984]=aF;aA=(c[bF>>2]|0)+aC|0;c[8996]=aA;b4=aF;b5=bC;b6=0;b7=aA}c[8992]=b4;c[9016]=(b5+b6|0)/2&-1;aA=b4+b7|0;c[9026]=aA;bC=c[6960]|0;c[9012]=bC;do{if(aD){aF=(c[7939]|0)-(c[7938]|0)|0;aC=(aF|0)/(aA|0)&-1;bF=c[9338]|0;bG=(bF|0)>0&(aC|0)>(bF|0)?bF:aC;if((bG|0)==0){c[9026]=aF;b8=1;b9=1;ca=aF}else{b8=0;b9=bG;ca=aA}bG=bC-1|0;aF=(bG+b9|0)/(b9|0)&-1;c[9012]=aF;if((aF|0)==0){cb=1}else{cb=(bG+aF|0)/(aF|0)&-1}bG=(cb|0)==0;cc=bG|b8<<24>>24!=0;cd=bG?1:cb;ce=aF;cf=ca}else{bD=+((c[aE>>2]|0)>>>0>>>0);aF=(~~(+((c[7941]|0)-(c[7940]|0)|0)- +h[4527]*bD-(bE+1.0)*bD)|0)/(c[9020]|0)&-1;bG=c[9339]|0;aC=(bG|0)>0&(aF|0)>(bG|0)?bG:aF;aF=(aC|0)==0;bG=aF?1:aC;if((bC|0)<=(bG|0)){cc=aF;cd=1;ce=bC;cf=aA;break}aC=bC-1|0;bF=(bG+aC|0)/(bG|0)&-1;bG=(bF|0)==0;ax=bG?1:bF;bF=(ax+aC|0)/(ax|0)&-1;c[9012]=bF;cc=bG|aF;cd=ax;ce=bF;cf=aA}}while(0);aA=c[9031]|0;if((aA|0)==1){if((c[9045]|0)!=1|(c[9044]|0)!=1){bt=9410}else{bt=9421}}else if((aA|0)==2){bt=9410}else{bt=9421}do{if((bt|0)==9410){aA=c[9032]|0;if((aA|0)==1&+h[7052]<0.0){bC=aa(c[9020]|0,ce);bD=+((c[aE>>2]|0)>>>0>>>0);aD=(((c[7940]|0)+bC|0)+~~((bE+1.0)*bD)|0)+~~(bD*+h[4527])|0;if((aD|0)>(c[7941]|0)){cg=0;break}c[7940]=aD;if(cc){cg=0;break}else{bL=cd;bN=0;break L12774}}if((aA|0)==0&+h[535]<0.0){aD=aa(c[9020]|0,ce);bD=+((c[aE>>2]|0)>>>0>>>0);bC=(((c[7941]|0)-aD|0)-~~((bE+1.0)*bD)|0)+~~(bD*+h[4527])|0;if((bC|0)<(c[7940]|0)){cg=0;break}c[7941]=bC;if(cc){cg=0;break}else{bL=cd;bN=0;break L12774}}if((aA|0)==2&+h[4395]<0.0){bC=aa(cd,cf);aD=c[7938]|0;bF=(aD+bC|0)>(c[7939]|0);ax=bF?0:bC;c[7938]=ax+aD;if(bF|cc){cg=ax;break}else{bL=cd;bN=ax;break L12774}}if(!((aA|0)==3&+h[3301]<0.0)){bt=9421;break}aA=aa(cd,cf);ax=(c[7939]|0)-aA|0;if((ax|0)<(c[7938]|0)){cg=0;break}c[7939]=ax;if(cc){cg=0}else{bL=cd;bN=0;break L12774}}}while(0);if((bt|0)==9421){if(cc){cg=0}else{bL=cd;bN=0;break}}uh(-1,133720,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);bL=cd;bN=cg}}while(0);dd(1,20);dd(5,20);do{if(a[31104]|0){dk();dc(3,132208);if((a[54016]|0)==110){break}dd(3,20)}}while(0);cg=c[16401]|0;if((cg&2|0)==0){ch=0;bt=9432}else{bE=+h[8255];bD=+h[8256];if(bE<bD){if(bE>0.0|bD<0.0){bt=9430}else{ci=0}}else{if(bD>0.0|bE<0.0){bt=9430}else{ci=0}}if((bt|0)==9430){ci=1}if(+P(+(bE/(bD-bE)))<.1){bt=9433}else{ch=ci;bt=9432}}if((bt|0)==9432){if((cg&1|0)!=0|ch){bt=9433}else{cj=0}}do{if((bt|0)==9433){if((bn|0)==0){c[216]=0;de(1,14);cj=aa((c[216]|0)+2|0,c[bh+20>>2]|0);break}else{cj=aa((c[ap>>2]|0)+2|0,c[bh+16>>2]|0);break}}}while(0);do{if((a[65760]&1)==0){if((c[16401]&1|0)==0){if((c[17089]&5|0)!=5){ck=0;break}}ck=~~(+((c[bh+28>>2]|0)>>>0>>>0)*+h[8218])}else{ck=0}}while(0);ap=bj^1;do{if((c[16457]|0)==0|ap){cl=0}else{fN(65912,aH,aI,135256);bj=c[bh+16>>2]|0;bE=+(aa(bj,c[48]|0)>>>0>>>0);ch=~~(bE- +h[aH>>3]);if((c[16401]|0)!=0){cl=ch;break}cl=~~(+(bj>>>0>>>0)*.5+ +(ch|0))}}while(0);aH=(bk|0)==0;if((c[1183]|0)==0|aH){cm=0}else{fN(4816,aJ,aK,135256);cm=~~((+(c[ar>>2]|0)+1.5)*+((c[bh+16>>2]|0)>>>0>>>0)- +h[aJ>>3])}if(+h[4395]<0.0){aJ=(bN|0)<(cm|0)?cm:bN;bN=bh+8|0;ar=ck+cj|0;c[7938]=(((aJ|0)<(cl|0)?cl:aJ)+ar|0)+~~(+g[184]*+((c[bN>>2]|0)>>>0>>>0));fN(4816,aL,aM,135256);aM=c[7938]|0;do{if(aH){aJ=~~+h[aL>>3];if((aM-ar|0)>=(-aJ|0)){cn=aM;break}aK=ar-aJ|0;c[7938]=aK;cn=aK}else{cn=aM}}while(0);aM=bh+20|0;if((cn|0)==(~~(+((c[bN>>2]|0)>>>0>>>0)*+g[184])|0)){bN=(c[aM>>2]<<1)+cn|0;c[7938]=bN;co=bN}else{co=cn}c[7938]=~~(+(co|0)+ +((c[aM>>2]|0)>>>0>>>0)*.5)}do{if((c[17089]&1|0)==0){cp=0}else{if((bo|0)==0){c[216]=0;de(5,14);cp=aa((c[216]|0)+2|0,c[bh+20>>2]|0);break}else{cp=aa((c[aq>>2]|0)+2|0,c[bh+16>>2]|0);break}}}while(0);aq=c[16580]|0;do{if((aq|0)==0){cq=0}else{aM=c[7939]|0;co=c[7938]|0;h[8269]=+(aM-co|0)/(+h[8256]- +h[8255]);c[16536]=co;c[16537]=aM;co=aq;cn=aM;L12904:while(1){aM=c[co+8>>2]|0;do{if((aM|0)==0){cr=cn}else{bE=+(lp(aM)|0);bN=c[16602]|0;bD=bE*+S(+(+(bN|0)*.017453292519943295));ar=~~(bD*+((c[(c[3524]|0)+20>>2]|0)>>>0>>>0));bD=+h[8257];bE=+h[8258];cs=+h[co>>3];if(bD<bE){if(cs<bD|cs>bE){cr=cn;break}}else{if(cs<bE|cs>bD){cr=cn;break}}if((a[66164]&1)==0){ct=cs}else{if(cs<=0.0){bt=9464;break L12904}bD=+_(+cs);ct=bD/+h[8272]}if((bN|0)==0){cu=(ar|0)/2&-1}else{cu=ar}bD=+(~~(+(c[16536]|0)+(ct- +h[8255])*+h[8269]+.5)|0)+ +(cu|0);if(+(cn|0)>=bD){cr=cn;break}cr=~~bD}}while(0);aM=c[co+16>>2]|0;if((aM|0)==0){break}else{co=aM;cn=cr}}if((bt|0)==9464){uk(210984,(v=i,i=i+24|0,c[v>>2]=130992,c[v+8>>2]=56880,h[v+16>>3]=cs,v)|0)}cn=cr-(c[7939]|0)|0;co=(c[(c[3524]|0)+8>>2]|0)>>>2;if(cn>>>0<=co>>>0){cq=cn;break}uh(-1,129936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cq=co}}while(0);do{if((a[68512]&1)==0){if((c[17089]&1|0)==0){if((c[16401]&5|0)!=5){cv=0;break}}cv=~~(+((c[bh+28>>2]|0)>>>0>>>0)*+h[8562])}else{cv=0}}while(0);do{if((c[17145]|0)==0|ap){cw=0}else{fN(68664,aO,aP,135256);cr=c[bh+16>>2]|0;cs=+(aa(cr,c[148]|0)>>>0>>>0);cu=~~(+h[aO>>3]+cs);if((c[17089]|0)!=0){cw=cu;break}cw=~~(+(cr>>>0>>>0)*.5+ +(cu|0))}}while(0);do{if((c[6598]|0)!=3){aO=a[54016]|0;if(a[31104]&aO<<24>>24!=110){if(!((aO<<24>>24|0)==110|(aO<<24>>24|0)==117)){aO=c[7939]|0;aP=~~(+(aO|0)- +(aO-(c[7938]|0)|0)*.1);c[7939]=aP;c[7939]=aP+((c[bh+20>>2]|0)*-3&-1)}c[13507]=0}if(+h[3301]>=0.0){break}aP=c[7939]|0;c[13507]=aP;aO=(((cw|0)>0?-cw|0:0)-(cv+cp|0)|0)+aP|0;c[7939]=aO;cu=bh+20|0;cs=(+g[178]+ +g[184])*+(((c[bh+8>>2]|0)-1|0)>>>0>>>0)- +(c[cu>>2]<<1>>>0>>>0);if(+(aO|0)>cs){cr=~~cs;c[7939]=cr;cx=cr}else{cx=aO}c[13507]=aP-cx;aP=c[(c[3524]|0)+8>>2]|0;if((aP-cx|0)>>>0<cq>>>0){aO=aP-cq|0;c[7939]=aO;cz=aO}else{cz=cx}c[7939]=~~(+(cz|0)- +((c[cu>>2]|0)>>>0>>>0))}}while(0);dd(2,20);dd(6,20);if((a[30528]&1)!=0){dd(7,10)}cs=+g[18074];do{if(cs!=0.0){do{if(cs<0.0){cz=c[200]|0;ct=+h[64672+(cz*688&-1)>>3]- +h[64664+(cz*688&-1)>>3];if(ct==0.0){bt=9497;break}cz=c[144]|0;cA=(-0.0-cs)*+P(+((+h[64672+(cz*688&-1)>>3]- +h[64664+(cz*688&-1)>>3])/ct))}else{bt=9497}}while(0);if((bt|0)==9497){cA=cs}if(!(cA>=.01&cA<=100.0)){break}cz=c[7941]|0;cx=c[7940]|0;cq=cz-cx|0;ct=+(cq|0);cu=c[7939]|0;aO=c[7938]|0;aP=cu-aO|0;bD=+(aP|0);bE=cA*+((c[bh+24>>2]|0)>>>0>>>0)/+((c[bh+28>>2]|0)>>>0>>>0);if(ct/bD>bE){cr=~~(bD*bE);if((c[14100]|0)==3){c[7941]=cr+cx;break}if((c[1066]|0)==3){c[7940]=cz-cr;break}else{aq=(cq-cr|0)/2&-1;c[7940]=aq+cx;c[7941]=cz-aq;break}}else{aq=~~(ct/bE);if((c[8786]|0)==3){c[7939]=aq+aO;break}if((c[6598]|0)==3){c[7938]=cu-aq;break}else{cz=(aP-aq|0)/2&-1;c[7938]=cz+aO;c[7939]=cu-cz;break}}}}while(0);cz=(bm|0)==0|ap;do{if((c[17261]&1|0)==0|cz){cB=bu}else{cA=+T(+(+(c[17290]|0)*.017453292519943295));bm=c[17314]|0;if((bm|0)==2){cC=cA*-1.0}else if((bm|0)==1){cC=+P(+cA)*.5}else{cC=cA}c[216]=0;de(6,14);bm=+h[535]<0.0;if(bm){c[7941]=(c[7941]|0)+bu}if(cC>0.0){cD=~~(+(bu|0)+cC*+(aa(c[216]|0,c[bh+20>>2]|0)|0))}else{cD=bu}if(!bm){cB=cD;break}c[7941]=(c[7941]|0)-cD;cB=cD}}while(0);cD=(bl|0)==0|ap;do{if((c[16573]&1|0)==0|cD){cE=bH}else{bl=c[16602]|0;do{if((bl|0)==(-270|0)){if((a[66648]&1)!=0){cF=-1.0;bt=9525;break}c[16626]=2;cH=-1.0;bt=9526}else if((bl|0)==90){cF=-1.0;bt=9525}else{cF=-0.0- +T(+(+(bl|0)*.017453292519943295));bt=9525}}while(0);if((bt|0)==9525){if((c[16626]|0)==2){cH=cF;bt=9526}else{cJ=cF}}if((bt|0)==9526){cJ=cH*-1.0}c[216]=0;de(2,14);bl=+h[7052]<0.0;if(bl){c[7940]=(c[7940]|0)-bH}if(cJ>0.0){cC=cJ*+(aa(c[216]|0,c[bh+20>>2]|0)|0);cL=~~(+((c[bh+16>>2]|0)>>>0>>>0)+cC)}else{cL=bH}if(!bl){cE=cL;break}c[7940]=(c[7940]|0)+cL;cE=cL}}while(0);cL=c[7941]|0;bH=c[7940]|0;if((cL|0)<(bH|0)){c[7941]=bH;c[7940]=cL;cP=bH;cQ=cL}else{cP=cL;cQ=bH}bH=((by+bv|0)+cB|0)+cP|0;c[208]=bH;do{if((cB|0)==0){cT=bH}else{if((bv|bq|0)==0){cT=bH;break}cL=(c[bh+16>>2]|0)+bH|0;c[208]=cL;cT=cL}}while(0);bH=cT+bq|0;c[1102]=bH;cT=((bA+by|0)+cB|0)+cP|0;c[50]=cT;c[150]=((bB+by|0)+cB|0)+cP;bB=bJ+bI|0;cL=bh+16|0;bl=~~(+(cQ-(cE+bB|0)|0)+ +((c[cL>>2]|0)>>>0>>>0)*(+(c[188]|0)+.2));c[190]=bl;bu=c[7938]|0;bm=(bu-ck|0)-cj|0;c[52]=bm;if(!((c[16457]|0)==0|ap)){c[52]=bm-cl}cl=(cv+cp|0)+(c[7939]|0)|0;c[152]=cl;if(!((c[17145]|0)==0|ap)){c[152]=(cl+cw|0)-aa(c[cL>>2]|0,c[148]|0)}cw=(bk|0)!=0;bk=(c[1166]|0)!=0;do{if(cw){if(bk){cU=(bJ-bK|0)+bl|0;bt=9551;break}else{c[1216]=(bz-(bv+bq|0)|0)+bH;bt=9552;break}}else{if(bk){c[1216]=(cQ-((bK+bB|0)+cE|0)|0)+(c[cL>>2]|0);bt=9553;break}if((bA|0)>0){c[1216]=cT+bz;bt=9553;break}else{cU=(((bz+by|0)+cB|0)+cP|0)+(c[bh+20>>2]|0)|0;bt=9551;break}}}while(0);if((bt|0)==9551){c[1216]=cU;if(cw){bt=9552}else{bt=9553}}if((bt|0)==9553){fN(4816,aQ,aR,135256);aR=c[7938]|0;cV=(aR-(ck+cj|0)|0)+~~+h[aQ>>3]|0;cW=c[7940]|0;cX=c[7941]|0;cY=aR}else if((bt|0)==9552){cV=bu-((ck+cj|0)+cm|0)|0;cW=cQ;cX=cP;cY=bu}c[1218]=cV;cV=bh+20|0;c[176]=(cW-bI|0)-(c[(cD?cL:cV)>>2]|0);if(cz){cZ=cB}else{cZ=c[cV>>2]|0}c[204]=(cX+by|0)+cZ;if((bn|0)==0){c_=c[cV>>2]|0}else{c_=cj-(c[cL>>2]|0)|0}c[36]=(cY-ck|0)-c_;c[146]=((c[7939]|0)+cv|0)+(c[((bo|0)==0?cV:cL)>>2]|0);cO[c[bi>>2]&255](0);bi=c[7941]|0;bo=c[7940]|0;cJ=+(bi-bo|0);h[8183]=cJ/(+h[8170]- +h[8169]);h[8527]=cJ/(+h[8514]- +h[8513]);cv=c[7939]|0;c_=c[7938]|0;cJ=+(cv-c_|0);h[8269]=cJ/(+h[8256]- +h[8255]);h[8613]=cJ/(+h[8600]- +h[8599]);c[16536]=c_;c[16537]=cv;c[16364]=bo;c[16365]=bi;c[17224]=c_;c[17225]=cv;c[17052]=bo;c[17053]=bi;ck=aa(c[9026]|0,bL);cJ=+((c[cL>>2]|0)>>>0>>>0);cH=+h[4488]*cJ;cL=~~(cH+ +(aa(c[9020]|0,c[9012]|0)|0));bL=cL+~~(cJ*+h[4527])|0;cL=c[9031]|0;do{if((cL|0)==1){if((c[9044]|0)==1&(c[9045]|0)==1){bt=9564}else{bt=9570}}else if((cL|0)==0){cY=c[9044]|0;if((cY|0)==0){cj=bi-(c[bh+24>>2]|0)|0;c[9337]=cj;c[9336]=cj-bL;bt=9565;break}else if((cY|0)==2){cY=(c[bh+24>>2]|0)+bo|0;c[9336]=cY;c[9337]=cY+bL;bt=9565;break}else{bt=9564;break}}else{if((cL-1|0)>>>0<2){bt=9570;break}fR(36136,an,ao,128552);cY=~~+h[an>>3];cj=~~+h[ao>>3];c[9334]=cY;bn=c[9045]|0;if((bn|0)==1){cZ=cY-((ck|0)/2&-1)|0;c[9334]=cZ;c$=cZ}else if((bn|0)==2){bn=cY-ck|0;c[9334]=bn;c$=bn}else{c$=cY}c[9335]=c$+ck;c[9337]=cj;cY=c[9044]|0;if((cY|0)==1){bn=((bL|0)/2&-1)+cj|0;c[9337]=bn;c0=bn}else if((cY|0)==2){cY=cj+bL|0;c[9337]=cY;c0=cY}else{c0=cj}c[9336]=c0-bL;c1=c[7939]|0;c2=c[7938]|0}}while(0);L13067:do{if((bt|0)==9564){c0=(bL|0)/2&-1;c$=(bo+bi|0)/2&-1;c[9336]=c$-c0;c[9337]=c$+c0;bt=9565}else if((bt|0)==9570){c0=c[9032]|0;do{if((c0|0)==0){c$=~~((+g[38]+ +g[44])*+((c[aB>>2]|0)>>>0>>>0)- +((c[bh+24>>2]|0)>>>0>>>0));c[9337]=c$;c[9336]=c$-bL}else if((c0|0)==1){c$=~~(+g[44]*+((c[aB>>2]|0)>>>0>>>0)+ +((c[bh+24>>2]|0)>>>0>>>0));c[9336]=c$;c[9337]=c$+bL}else{c$=c[9044]|0;if((c$|0)==0){c[9337]=bi;c[9336]=bi-bL}else if((c$|0)==1){c$=(bL|0)/2&-1;ao=(bo+bi|0)/2&-1;c[9336]=ao-c$;c[9337]=ao+c$}else{c[9336]=bo;c[9337]=bL+bo}if((c0|0)==2){c$=~~(+g[184]*+((c[bh+8>>2]|0)>>>0>>>0)+ +((c[cV>>2]|0)>>>0>>>0));c[9334]=c$;c[9335]=c$+ck;c1=cv;c2=c_;break L13067}else if((c0|0)==3){c$=~~((+g[178]+ +g[184])*+(((c[bh+8>>2]|0)-1|0)>>>0>>>0)- +((c[cV>>2]|0)>>>0>>>0));c[9335]=c$;c[9334]=c$-ck;c1=cv;c2=c_;break L13067}else{break}}}while(0);c0=c[9045]|0;if((c0|0)==0){c[9334]=c_;c[9335]=ck+c_;c1=cv;c2=c_;break}else if((c0|0)==1){c0=(ck|0)/2&-1;c$=(c_+cv|0)/2&-1;c[9334]=c$-c0;c[9335]=c$+c0;c1=cv;c2=c_;break}else{c[9335]=cv;c[9334]=cv-ck;c1=cv;c2=c_;break}}}while(0);do{if((bt|0)==9565){bh=c[9045]|0;if((bh|0)==0){bo=(c[cV>>2]|0)+c_|0;c[9334]=bo;c[9335]=bo+ck;c1=cv;c2=c_;break}else if((bh|0)==2){bh=cv-(c[cV>>2]|0)|0;c[9335]=bh;c[9334]=bh-ck;c1=cv;c2=c_;break}else{bh=(ck|0)/2&-1;bo=(c_+cv|0)/2&-1;c[9334]=bo-bh;c[9335]=bo+bh;c1=cv;c2=c_;break}}}while(0);c[13542]=31752;if((c1|0)<(c2|0)){bt=9593}else{if((c[7941]|0)<(c[7940]|0)){bt=9593}}if((bt|0)==9593){uh(-1,127328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(a[31104]|0){dz()}fO(c[10818]|0,-1,2);a[25280]=0;cM[c[(c[3524]|0)+168>>2]&511](1);c2=c[10026]|0;if((c2|0)==(-1|0)|(c2|0)==0){fT();c3=c[10026]|0}else{c3=c2}if((c3|0)==(-1|0)|(c3|0)==0){c[200]=2;c[144]=1;dg(2,1);dg(1,2);c[200]=6;c[144]=5;dg(6,5);dg(5,6)}if((c[11692]|0)!=0){fU()}if((c[16457]|0)!=0){c3=a[65952]&1;do{if(c3<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;c2=c[(c[3524]|0)+88>>2]|0;if((c2|0)==0){break}cO[c2&255](179864)}}while(0);a[37456]=c3;fn(65840,a4);c3=a4+72|0;if((cO[c[c3>>2]&255](c[16455]|0)|0)==0){ln(c[52]|0,c[50]|0,c[16457]|0,0,0,0,c[16458]|0)}else{fN(65912,aU,aV,141e3);ln(((c[a4+16>>2]|0)>>>1)+(c[52]|0)|0,~~(+(((c[7940]|0)+(c[7941]|0)|0)/2&-1|0)+ +h[aV>>3]),c[16457]|0,1,0,c[16455]|0,c[16458]|0);aV=c[c3>>2]|0;cO[aV&255](0)}if((c[16460]|0)!=0){cM[c[a4+64>>2]&511](-2)}a[37456]=0}if((c[17145]|0)!=0){aV=a[68704]&1;do{if(aV<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;c3=c[(c[3524]|0)+88>>2]|0;if((c3|0)==0){break}cO[c3&255](179864)}}while(0);a[37456]=aV;fn(68592,a4);aV=a4+72|0;if((cO[c[aV>>2]&255](c[17143]|0)|0)==0){ln(c[152]|0,c[150]|0,c[17145]|0,2,0,0,c[17146]|0)}else{fN(68664,aW,aX,141e3);ln(((c[152]|0)-1|0)+((c[a4+16>>2]|0)>>>1)|0,~~(+(((c[7940]|0)+(c[7941]|0)|0)/2&-1|0)+ +h[aX>>3]),c[17145]|0,1,0,c[17143]|0,c[17146]|0);aX=c[aV>>2]|0;cO[aX&255](0)}if((c[17148]|0)!=0){cM[c[a4+64>>2]&511](-2)}a[37456]=0}if((c[16629]|0)!=0){fN(66600,aY,aZ,141e3);aZ=~~(+(((c[7938]|0)+(c[7939]|0)|0)/2&-1|0)+ +h[aY>>3]);aY=(c[190]|0)-((c[a4+16>>2]|0)>>>1)|0;aX=a[66640]&1;do{if(aX<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;aV=c[(c[3524]|0)+88>>2]|0;if((aV|0)==0){break}cO[aV&255](179864)}}while(0);a[37456]=aX;fn(66528,a4);ln(aZ,aY,c[16629]|0,1,0,0,c[16630]|0);if((c[16632]|0)!=0){cM[c[a4+64>>2]&511](-2)}a[37456]=0}if((c[1119]|0)!=0){fN(4560,a_,a$,141e3);a$=~~(+(((c[7939]|0)+(c[7938]|0)|0)/2&-1|0)+ +h[a_>>3]);a_=(c[1102]|0)-((c[a4+16>>2]|0)>>>1)|0;aY=a[4600]&1;do{if(aY<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;aZ=c[(c[3524]|0)+88>>2]|0;if((aZ|0)==0){break}cO[aZ&255](179864)}}while(0);a[37456]=aY;fn(4488,a4);ln(a$,a_,c[1119]|0,1,0,0,c[1120]|0);if((c[1122]|0)!=0){cM[c[a4+64>>2]&511](-2)}a[37456]=0}if((c[17317]|0)!=0){fN(69352,a0,a1,141e3);a1=~~(+(((c[7938]|0)+(c[7939]|0)|0)/2&-1|0)+ +h[a0>>3]);a0=((c[208]|0)-1|0)-((c[a4+16>>2]|0)>>>1)|0;a_=a[69392]&1;do{if(a_<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;a$=c[(c[3524]|0)+88>>2]|0;if((a$|0)==0){break}cO[a$&255](179864)}}while(0);a[37456]=a_;fn(69280,a4);ln(a1,a0,c[17317]|0,1,0,0,c[17318]|0);if((c[17320]|0)!=0){cM[c[a4+64>>2]&511](-2)}a[37456]=0}if((c[1183]|0)!=0){a0=c[1218]|0;a1=c[1216]|0;cG(a2|0);a_=ut(1025)|0;do{if((a_|0)==0){gk();a$=ut(1025)|0;if((a$|0)!=0){c4=a$;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=126240,v)|0)}else{c4=a_}}while(0);a_=c[1183]|0;a$=aN(a2|0)|0;bw(c4|0,1024,a_|0,a$|0);do{if((c[1164]|0)==0){bt=9658}else{a$=a4+72|0;if((cO[c[a$>>2]&255](-270)|0)==0){bt=9658;break}a_=((c[a4+16>>2]|0)>>>1)+a0|0;a2=c[1184]|0;if((c[1166]|0)==0){ln(a_,a1,c4,2,0,-270,a2)}else{ln(a_,a1,c4,0,0,-270,a2)}a2=c[a$>>2]|0;cO[a2&255](0)}}while(0);do{if((bt|0)==9658){a2=a1-((c[a4+16>>2]|0)>>>1)|0;a$=c[1184]|0;if((c[1166]|0)==0){ln(a0,a2,c4,0,0,0,a$);break}else{ln(a0,a2,c4,0,2,0,a$);break}}}while(0);uu(c4)}do{if(a[31104]&(a[54016]|0)!=110){if(!((c[(c[3524]|0)+144>>2]|0)!=0&(c[13506]|0)==0)){break}dD(1)}}while(0);fO(c[10818]|0,0,2);c4=c[10822]|0;a0=c[(c[3524]|0)+92>>2]|0;if((a0|0)!=0){cK[a0&63](+h[3817])}if((c4|0)!=0){a0=c4;do{if((c[a0+56>>2]|0)==0){fR(a0+8|0,al,am,202256);fp(~~+h[al>>3],~~+h[am>>3],a0)}a0=c[a0>>2]|0;}while((a0|0)!=0)}fV(0);cM[c[(c[3524]|0)+168>>2]&511](2);if((a[35184]&1)!=0){fW(0,aS,aT)}a0=a4+168|0;am=(d|0)>0;al=ak;c4=ak|0;a1=ak+4|0;a$=a3;a3=a4+144|0;a2=a4+92|0;a_=a4+80|0;aY=s;aZ=s|0;aX=s+4|0;aV=V;aW=V|0;c3=V+4|0;aU=Q|0;c2=R|0;c1=Q+8|0;Q=R+8|0;R=J;c_=M|0;cv=I;ck=M+7|0;M=I|0;cV=I+4|0;bh=H;bo=H+4|0;bL=H+8|0;bi=H+16|0;aB=H|0;c0=J|0;c$=J|0;ao=J+72|0;an=J+80|0;cL=E;cj=E|0;cY=E+4|0;bn=D;cZ=D+4|0;by=D+8|0;cX=D+16|0;cB=D|0;cz=J+64|0;cD=J+8|0;bI=J+16|0;J=A+8|0;cW=A+40|0;bu=A+32|0;cP=A|0;cQ=A+16|0;cm=A+24|0;aR=A+48|0;aQ=A+56|0;cw=x;cU=x|0;bz=x+4|0;cT=t;bA=t|0;cE=t+4|0;bB=0;bK=0;L13212:while(1){cM[c[a0>>2]&511](10);bk=a[35184]|0;if(am){bH=0;bq=bB;bv=b;bl=0;bJ=bk;while(1){cM[c[(c[3524]|0)+168>>2]&511](6);cl=bv+304|0;c[200]=c[cl>>2];ap=bv+308|0;c[144]=c[ap>>2];cp=bv+24|0;bm=bv+64|0;c[al>>2]=c[bm>>2];c[al+4>>2]=c[bm+4>>2];c[al+8>>2]=c[bm+8>>2];c[al+12>>2]=c[bm+12>>2];cu=bv+28|0;aO=c[cu>>2]|0;aq=cp|0;do{if((c[aq>>2]|0)!=0){cJ=+h[bv+48>>3];aP=c[(c[3524]|0)+92>>2]|0;if(cJ<0.0){cK[aP&63](+h[3817]);break}else{cK[aP&63](cJ);break}}}while(0);aP=bv+40|0;cK[c[(c[3524]|0)+112>>2]&63](+h[aP>>3]);cx=c[(c[3524]|0)+64>>2]|0;if((aO|0)<-5){cM[cx&511](-2)}else{cM[cx&511](aO)}cx=bv+56|0;cr=c[3524]|0;do{if((a[cx]&1)==0){if((c[cr+96>>2]&1024|0)!=0){break}c[c4>>2]=1;c[a1>>2]=aO;bt=9686}else{bt=9686}}while(0);if((bt|0)==9686){bt=0;fn(ak,cr)}aO=bv+12|0;do{if((c[aO>>2]|0)==392){if((a[30080]&1)==0){c5=1}else{lK(cp,(c[cu>>2]|0)+1|0);c5=(c[aO>>2]|0)==392}if(!(c5&(bH|0)==392)){c6=bq;break}if((c[bv+280>>2]|0)!=0){c6=bq;break}cq=c[aT>>2]|0;co=c[54]|0;if((cq|0)==(co|0)){c6=bq;break}cn=bq+1|0;if((cn|0)<(c[9012]|0)){c[aT>>2]=cq-(c[9020]|0);c6=cn;break}else{c[aT>>2]=co;c[aS>>2]=(c[aS>>2]|0)+(c[9026]|0);c6=0;break}}else{c6=bq}}while(0);do{if((c[aO>>2]|0)==392&(c[9670]|0)==2){cr=bv+224|0;co=c[cr>>2]|0;if((co|0)==0){c7=0;c8=c6;break}if(!bK){if((a[36228]&1)!=0){c7=0;c8=c6;break}}cn=cp;c[a$>>2]=c[cn>>2];c[a$+4>>2]=c[cn+4>>2];c[a$+8>>2]=c[cn+8>>2];c[a$+12>>2]=c[cn+12>>2];c[a$+16>>2]=c[cn+16>>2];c[a$+20>>2]=c[cn+20>>2];c[a$+24>>2]=c[cn+24>>2];c[a$+28>>2]=c[cn+28>>2];c[a$+32>>2]=c[cn+32>>2];c[a$+36>>2]=c[cn+36>>2];c[a$+40>>2]=c[cn+40>>2];c[a$+44>>2]=c[cn+44>>2];c[a$+48>>2]=c[cn+48>>2];c[a$+52>>2]=c[cn+52>>2];cq=c[co>>2]|0;if((cq|0)==0){c9=c6;da=co}else{co=bv+200|0;aM=bv+64|0;ar=bv+68|0;bN=bv+32|0;aL=c6;aH=cq;while(1){cq=aH+4|0;c[cu>>2]=c[cq>>2];c[co>>2]=c[cq>>2];aK=aH+60|0;if((c[aK>>2]|0)!=0){aJ=(c[cq>>2]|0)+1|0;do{if((a[30080]&1)==0){cq=c[aq>>2]|0;aI=c[8798]|0;ch=(aI|0)>0;bj=aJ;L13260:while(1){cg=43264;while(1){df=c[cg>>2]|0;if((df|0)==0){break}if((c[df+4>>2]|0)==(bj|0)){break L13260}else{cg=df|0}}dh=bj-1|0;if(!((bj|0)>(aI|0)&ch)){bt=9715;break}bj=((dh|0)%(aI|0)&-1)+1|0}if((bt|0)==9715){bt=0;c[cu>>2]=dh;c[aM>>2]=1;c[ar>>2]=dh;c[bN>>2]=dh;break}aI=df+8|0;c[cn>>2]=c[aI>>2];c[cn+4>>2]=c[aI+4>>2];c[cn+8>>2]=c[aI+8>>2];c[cn+12>>2]=c[aI+12>>2];c[cn+16>>2]=c[aI+16>>2];c[cn+20>>2]=c[aI+20>>2];c[cn+24>>2]=c[aI+24>>2];c[cn+28>>2]=c[aI+28>>2];c[cn+32>>2]=c[aI+32>>2];c[cn+36>>2]=c[aI+36>>2];c[cn+40>>2]=c[aI+40>>2];c[cn+44>>2]=c[aI+44>>2];c[cn+48>>2]=c[aI+48>>2];c[cn+52>>2]=c[aI+52>>2];c[aq>>2]=cq;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[cu>>2]=bj;a[cx]=0;break}if((a[cx]&1)!=0){break}c[aM>>2]=1;c[ar>>2]=c[cu>>2]}else{lK(cp,aJ)}}while(0);fY(bv,c[aK>>2]|0,a4,c[aS>>2]|0,c[aT>>2]|0)}aJ=aL+1|0;if((aJ|0)<(c[9012]|0)){c[aT>>2]=(c[aT>>2]|0)-(c[9020]|0);di=aJ}else{c[aT>>2]=c[54];c[aS>>2]=(c[aS>>2]|0)+(c[9026]|0);di=0}aJ=c[aH>>2]|0;if((aJ|0)==0){break}else{aL=di;aH=aJ}}c9=di;da=c[cr>>2]|0}aH=c[da+64>>2]|0;if((aH|0)==0){dj=da}else{uu(aH);dj=da}while(1){aL=c[dj+60>>2]|0;if((aL|0)!=0){uu(aL)}aL=c[dj+64>>2]|0;if(!((aL|0)==0|(aL|0)==(aH|0))){uu(aL)}aL=c[dj>>2]|0;uu(dj);if((aL|0)==0){break}else{dj=aL}}c[cr>>2]=0;c[cn>>2]=c[a$>>2];c[cn+4>>2]=c[a$+4>>2];c[cn+8>>2]=c[a$+8>>2];c[cn+12>>2]=c[a$+12>>2];c[cn+16>>2]=c[a$+16>>2];c[cn+20>>2]=c[a$+20>>2];c[cn+24>>2]=c[a$+24>>2];c[cn+28>>2]=c[a$+28>>2];c[cn+32>>2]=c[a$+32>>2];c[cn+36>>2]=c[a$+36>>2];c[cn+40>>2]=c[a$+40>>2];c[cn+44>>2]=c[a$+44>>2];c[cn+48>>2]=c[a$+48>>2];c[cn+52>>2]=c[a$+52>>2];c7=0;c8=c9}else{aH=bv+16|0;aL=c[aH>>2]|0;if((aL|0)!=0){if((a[aL]|0)==0){c7=0;c8=c6;break}}if((c[bv+8>>2]|0)==4){c7=0;c8=c6;break}if(!bK){if((a[36228]&1)!=0){c7=(bJ&1)!=0;c8=c6;break}}aL=a[bv+20|0]&1;do{if(aL<<24>>24!=0){if((a[37456]&1)!=0){break}a[37456]=1;ar=c[(c[3524]|0)+88>>2]|0;if((ar|0)==0){break}cO[ar&255](179864)}}while(0);a[37456]=aL;cn=bJ&1;do{if(cn<<24>>24==0){dl=c6}else{cr=c[aH>>2]|0;if((cr|0)==0){dl=c6;break}if((a[bv+22|0]&1)!=0){dl=c6;break}if((a[36230]&1)==0){dm=cr;dn=c[aT>>2]|0}else{cr=(((c[54]|0)+(c[9336]|0)|0)+((c[9020]|0)/2&-1)|0)-(c[aT>>2]|0)|0;c[aT>>2]=cr;dm=c[aH>>2]|0;dn=cr}fY(bv,dm,a4,c[aS>>2]|0,dn);dl=c6+1|0}}while(0);a[37456]=0;c7=cn<<24>>24!=0;c8=dl}}while(0);cp=bv+23|0;aH=a[cp]|0;L13316:do{if(!((aH&1)==0|bK)){aL=bv+300|0;if((c[aL>>2]|0)<=0){break}cr=bv+320|0;ar=0;aM=aH;while(1){do{if((aM&1)!=0){c[(c[cr>>2]|0)+(ar<<6)>>2]=0;bN=c[cl>>2]|0;cJ=+h[64664+(bN*688&-1)>>3];cH=+h[64672+(bN*688&-1)>>3];bN=c[cr>>2]|0;cF=+h[bN+(ar<<6)+8>>3];if(cJ<cH){if(cF<cJ|cF>cH){bt=9754}else{dp=bN}}else{if(cF<cH|cF>cJ){bt=9754}else{dp=bN}}if((bt|0)==9754){bt=0;c[bN+(ar<<6)>>2]=1;dp=c[cr>>2]|0}bN=c[ap>>2]|0;cJ=+h[64664+(bN*688&-1)>>3];cF=+h[64672+(bN*688&-1)>>3];cH=+h[dp+(ar<<6)+16>>3];if(cJ<cF){if(!(cH<cJ|cH>cF)){break}}else{if(!(cH<cF|cH>cJ)){break}}c[dp+(ar<<6)>>2]=1}}while(0);aK=ar+1|0;if((aK|0)>=(c[aL>>2]|0)){break L13316}ar=aK;aM=a[cp]|0}}}while(0);cp=bv+8|0;do{if(!((c[cp>>2]|0)==4|bK)){aH=c[aO>>2]|0;if((aH|0)==18){fZ(bv);break}else if((aH|0)==51){fX(bv);fZ(bv);break}else if((aH|0)==64){aM=bv+300|0;ar=c[aM>>2]|0;if((ar|0)<=0){break}aL=bv+320|0;cr=bv+316|0;cn=(c[3524]|0)+80|0;aK=0;bN=ar;while(1){ar=c[aL>>2]|0;if((c[ar+(aK<<6)>>2]|0)==0){co=c[200]|0;aJ=~~(+(c[64768+(co*688&-1)>>2]|0)+(+h[ar+(aK<<6)+8>>3]- +h[64664+(co*688&-1)>>3])*+h[64776+(co*688&-1)>>3]+.5);co=c[144]|0;aI=~~(+(c[64768+(co*688&-1)>>2]|0)+(+h[ar+(aK<<6)+16>>3]- +h[64664+(co*688&-1)>>3])*+h[64776+(co*688&-1)>>3]+.5);co=(c[cr>>2]|0)+(aK<<3)|0;f9(bv,co);cR[c[cn>>2]&127](aJ,aI,-1);dq=c[aM>>2]|0}else{dq=bN}aI=aK+1|0;if((aI|0)<(dq|0)){aK=aI;bN=dq}else{break}}}else if((aH|0)==295|(aH|0)==279|(aH|0)==311){fX(bv);f_(bv);fZ(bv);break}else if((aH|0)==102|(aH|0)==86|(aH|0)==118){f_(bv);fZ(bv);break}else if((aH|0)==137|(aH|0)==153){f$(bv,c[64784+((c[144]|0)*688&-1)>>2]|0);break}else if((aH|0)==392){if((c[14156]|0)==1){f$(bv,c[64784+((c[144]|0)*688&-1)>>2]|0)}do{if((c[9670]|0)==4){cK[c[(c[3524]|0)+112>>2]&63](+h[4840]);if(!(hG(49488)|0)){cM[c[(c[3524]|0)+64>>2]&511](c[cu>>2]|0)}f_(bv);c[aV>>2]=c[bm>>2];c[aV+4>>2]=c[bm+4>>2];c[aV+8>>2]=c[bm+8>>2];c[aV+12>>2]=c[bm+12>>2];bN=c[cu>>2]|0;do{if((c[aq>>2]|0)!=0){cJ=+h[bv+48>>3];aK=c[(c[3524]|0)+92>>2]|0;if(cJ<0.0){cK[aK&63](+h[3817]);break}else{cK[aK&63](cJ);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[aP>>3]);aK=c[(c[3524]|0)+64>>2]|0;if((bN|0)<-5){cM[aK&511](-2)}else{cM[aK&511](bN)}aK=c[3524]|0;if((a[cx]&1)==0){if((c[aK+96>>2]&1024|0)!=0){break}c[aW>>2]=1;c[c3>>2]=bN}fn(V,aK)}}while(0);if((c[14156]|0)==1){break}f$(bv,c[64784+((c[144]|0)*688&-1)>>2]|0);break}else if((aH|0)==33){aK=c[64784+((c[200]|0)*688&-1)>>2]|0;aM=c[64784+((c[144]|0)*688&-1)>>2]|0;cn=bv+300|0;if((c[cn>>2]|0)<=0){break}cr=bv+320|0;aL=bv+316|0;aI=0;do{aJ=c[cr>>2]|0;L13380:do{if((c[aJ+(aI<<6)>>2]|0)!=2){co=c[200]|0;do{if((a[30528]&1)==0){cJ=+h[64664+(co*688&-1)>>3];cH=+h[64672+(co*688&-1)>>3];cF=+h[aJ+(aI<<6)+8>>3];if(cJ<cH){if(cF<cJ|cF>cH){break L13380}else{dr=cF;ds=cJ;break}}else{if(cF<cH|cF>cJ){break L13380}else{dr=cF;ds=cJ;break}}}else{dr=+h[aJ+(aI<<6)+8>>3];ds=+h[64664+(co*688&-1)>>3]}}while(0);bj=~~(+(c[64768+(co*688&-1)>>2]|0)+(dr-ds)*+h[64776+(co*688&-1)>>3]+.5);cq=c[144]|0;ar=~~(+(c[64768+(cq*688&-1)>>2]|0)+(+h[aJ+(aI<<6)+16>>3]- +h[64664+(cq*688&-1)>>3])*+h[64776+(cq*688&-1)>>3]+.5);cq=(c[aL>>2]|0)+(aI<<3)|0;f9(bv,cq);if((a[30528]&1)==0){c[ac>>2]=bj;c[ad>>2]=aM;c[ae>>2]=bj;c[af>>2]=ar;cq=c[3524]|0;if((fl(ac,ad,ae,af)|0)==0){break}cN[c[cq+56>>2]&255](c[ac>>2]|0,c[ad>>2]|0);cN[c[cq+60>>2]&255](c[ae>>2]|0,c[af>>2]|0);break}else{c[ag>>2]=aK;c[ah>>2]=aM;c[ai>>2]=bj;c[aj>>2]=ar;ar=c[3524]|0;if((fl(ag,ah,ai,aj)|0)==0){break}cN[c[ar+56>>2]&255](c[ag>>2]|0,c[ah>>2]|0);cN[c[ar+60>>2]&255](c[ai>>2]|0,c[aj>>2]|0);break}}}while(0);aI=aI+1|0;}while((aI|0)<(c[cn>>2]|0))}else if((aH|0)==169){if((c[14156]|0)!=1){f_(bv)}f$(bv,c[64784+((c[144]|0)*688&-1)>>2]|0);if((c[14156]|0)!=1){break}f_(bv);break}else if((aH|0)==345){cn=bv+244|0;aI=c[cn>>2]|0;if((aI|0)==13){f0(bv);break}else if((aI|0)==10){f0(bv);break}else{aI=c[3524]|0;c[U>>2]=0;if((c[aI+148>>2]|0)==0){fX(bv)}else{aI=bv+240|0;do{if((c[aI>>2]|0)==0){aM=aI;if((c[cp>>2]|0)==1){c[aM>>2]=c[10858];c[aM+4>>2]=c[10859];c[aM+8>>2]=c[10860];c[aM+12>>2]=c[10861];c[aM+16>>2]=c[10862];c[aM+20>>2]=c[10863];c[aM+24>>2]=c[10864];c[aM+28>>2]=c[10865];break}else{c[aM>>2]=c[10850];c[aM+4>>2]=c[10851];c[aM+8>>2]=c[10852];c[aM+12>>2]=c[10853];c[aM+16>>2]=c[10854];c[aM+20>>2]=c[10855];c[aM+24>>2]=c[10856];c[aM+28>>2]=c[10857];break}}}while(0);aI=c[cn>>2]|0;do{if((aI|0)==5){aM=bv+248|0;cJ=+h[aM>>3];cF=+h[8255];if(cJ<cF){h[aM>>3]=cF;dt=cF}else{dt=cJ}cJ=+h[8256];if(dt<=cJ){break}h[aM>>3]=cJ}else if((aI|0)==7){aM=bv+248|0;cJ=+h[aM>>3];cF=+h[8599];if(cJ<cF){h[aM>>3]=cF;du=cF}else{du=cJ}cJ=+h[8600];if(du<=cJ){break}h[aM>>3]=cJ}else if((aI|0)==6){aM=bv+248|0;cJ=+h[aM>>3];cF=+h[8169];if(cJ<cF){h[aM>>3]=cF;dv=cF}else{dv=cJ}cJ=+h[8170];if(dv<=cJ){break}h[aM>>3]=cJ}else if((aI|0)==8){aM=bv+248|0;cJ=+h[aM>>3];cF=+h[8513];if(cJ<cF){h[aM>>3]=cF;dw=cF}else{dw=cJ}cJ=+h[8514];if(dw<=cJ){break}h[aM>>3]=cJ}else if((aI|0)==9){aM=bv+248|0;cJ=+h[aM>>3];cF=+h[8255];if(cJ<cF){h[aM>>3]=cF;dx=cF}else{dx=cJ}cJ=+h[8256];if(dx>cJ){h[aM>>3]=cJ}aM=bv+256|0;cJ=+h[aM>>3];cF=+h[8169];if(cJ<cF){h[aM>>3]=cF;dy=cF}else{dy=cJ}cJ=+h[8170];if(dy<=cJ){break}h[aM>>3]=cJ}}while(0);aM=bv+300|0;if((c[aM>>2]|0)>0){aK=bv+320|0;aL=bv+264|0;cr=bv+248|0;aJ=2;bN=0;ar=0;bj=0;cq=0;ch=0;cg=2;while(1){ci=c[7934]|0;if((cg|0)>=(ci|0)){cd=ci+128|0;c[7934]=cd;c[7936]=db(c[7936]|0,cd*12&-1,140784)|0}cd=c[aK>>2]|0;ci=c[cd+(ch<<6)>>2]|0;L13454:do{if((ci|0)==1){cc=(c[cn>>2]|0)==0;if(cc){bt=10021}else{if((a[54152]&1)==0){dA=bj;dB=ar}else{bt=10021}}if((bt|0)==10021){bt=0;cf=c[200]|0;ce=~~(+(c[64768+(cf*688&-1)>>2]|0)+(+h[cd+(ch<<6)+8>>3]- +h[64664+(cf*688&-1)>>3])*+h[64776+(cf*688&-1)>>3]+.5);cf=c[144]|0;ca=~~(+(c[64768+(cf*688&-1)>>2]|0)+(+h[cd+(ch<<6)+16>>3]- +h[64664+(cf*688&-1)>>3])*+h[64776+(cf*688&-1)>>3]+.5);cf=c[13542]|0;if((cf|0)==0){dC=0}else{cb=(c[cf>>2]|0)>(ce|0)&1;b8=(c[cf+4>>2]|0)<(ce|0)?cb|2:cb;cb=(c[cf+8>>2]|0)>(ca|0)?b8|4:b8;dC=(c[cf+12>>2]|0)<(ca|0)?cb|8:cb}cb=dC&3;ca=dC&12;dA=(cb|0)==0?bj:cb;dB=(ca|0)==0?ar:ca}if((aJ|0)==0){if(!((a[54160]&1)!=0|cc)){if((a[54152]&1)==0){dE=cq;dF=dA;dG=dB;dH=bN;break}}ca=gb(cd,ch,N,O)|0;cb=c[200]|0;c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=~~(+(c[64768+(cb*688&-1)>>2]|0)+(+h[N>>3]- +h[64664+(cb*688&-1)>>3])*+h[64776+(cb*688&-1)>>3]+.5);cb=c[144]|0;cf=~~(+(c[64768+(cb*688&-1)>>2]|0)+(+h[O>>3]- +h[64664+(cb*688&-1)>>3])*+h[64776+(cb*688&-1)>>3]+.5);cb=c[U>>2]|0;c[U>>2]=cb+1;c[(c[7936]|0)+(cb*12&-1)+4>>2]=cf;dE=cq;dF=dA;dG=dB;dH=ca;break}else if((aJ|0)!=1){dE=cq;dF=dA;dG=dB;dH=bN;break}if(!cc){if((a[54152]&1)==0){dE=cq;dF=dA;dG=dB;dH=bN;break}}if(!(gc(cd,ch,aU,c2)|0)){dE=cq;dF=dA;dG=dB;dH=bN;break}cc=c[200]|0;cJ=+h[aU>>3];c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=~~(+(c[64768+(cc*688&-1)>>2]|0)+(cJ- +h[64664+(cc*688&-1)>>3])*+h[64776+(cc*688&-1)>>3]+.5);cc=c[144]|0;cF=+h[c2>>3];ca=~~(+(c[64768+(cc*688&-1)>>2]|0)+(cF- +h[64664+(cc*688&-1)>>3])*+h[64776+(cc*688&-1)>>3]+.5);cc=c[U>>2]|0;c[U>>2]=cc+1;c[(c[7936]|0)+(cc*12&-1)+4>>2]=ca;ca=c[aK>>2]|0;cc=ca+(ch<<6)+8|0;cH=+h[cc>>3];cC=+h[ca+(ch<<6)+16>>3];cA=+h[c1>>3];h[cc>>3]=cA;cs=+h[Q>>3];h[(c[aK>>2]|0)+(ch<<6)+16>>3]=cs;cc=gb(c[aK>>2]|0,ch,N,O)|0;h[(c[aK>>2]|0)+(ch<<6)+8>>3]=cH;h[(c[aK>>2]|0)+(ch<<6)+16>>3]=cC;do{if((bN|0)==0){dI=cc}else{if((cc|0)==(bN|0)){dI=cq;break}gi(c[7936]|0,U,bN,cc,dB,dA);dI=cq}}while(0);cc=c[200]|0;c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=~~(+(c[64768+(cc*688&-1)>>2]|0)+(cA- +h[64664+(cc*688&-1)>>3])*+h[64776+(cc*688&-1)>>3]+.5);cc=c[144]|0;co=~~(+(c[64768+(cc*688&-1)>>2]|0)+(cs- +h[64664+(cc*688&-1)>>3])*+h[64776+(cc*688&-1)>>3]+.5);cc=c[U>>2]|0;c[U>>2]=cc+1;c[(c[7936]|0)+(cc*12&-1)+4>>2]=co;co=ch-1|0;cc=c[aK>>2]|0;ca=cc+(co<<6)+8|0;cC=+h[ca>>3];cH=+h[cc+(co<<6)+16>>3];h[ca>>3]=cJ;h[(c[aK>>2]|0)+(co<<6)+16>>3]=cF;ca=gb(c[aK>>2]|0,ch,N,O)|0;h[(c[aK>>2]|0)+(co<<6)+8>>3]=cC;h[(c[aK>>2]|0)+(co<<6)+16>>3]=cH;dE=dI;dF=dA;dG=dB;dH=ca}else if((ci|0)==2){if((aJ|0)==2){dE=cq;dF=bj;dG=ar;dH=bN;break}if(!((cq|0)==0|(cq|0)==(bN|0))){gi(c[7936]|0,U,bN,cq,ar,bj)}gj(c[U>>2]|0,c[7936]|0,bv);c[U>>2]=0;dE=0;dF=bj;dG=ar;dH=0}else if((ci|0)==0){ca=c[200]|0;cH=+(c[64768+(ca*688&-1)>>2]|0);cC=+h[cd+(ch<<6)+8>>3];bE=+h[64664+(ca*688&-1)>>3];ct=+h[64776+(ca*688&-1)>>3];ca=~~(cH+(cC-bE)*ct+.5);co=c[144]|0;bD=+h[cd+(ch<<6)+16>>3];cc=~~(+(c[64768+(co*688&-1)>>2]|0)+(bD- +h[64664+(co*688&-1)>>3])*+h[64776+(co*688&-1)>>3]+.5);if((aJ|0)==0){do{if((c[aL>>2]|0)!=0){co=c[cn>>2]|0;if((co|0)==5|(co|0)==7){be=+h[cr>>3];cf=ch-1|0;bd=be- +h[cd+(cf<<6)+8>>3];bg=be-cC;if(bg*bd>=0.0){break}bf=+h[cd+(cf<<6)+16>>3];h[N>>3]=be;dJ=bf+bd*(bD-bf)/(bd-bg);dK=be}else if((co|0)==6|(co|0)==8){be=+h[cr>>3];co=ch-1|0;bg=be- +h[cd+(co<<6)+16>>3];bd=be-bD;bf=+h[cd+(co<<6)+8>>3];if(bd*bg>=0.0){break}a7=bf+bg*(cC-bf)/(bg-bd);h[N>>3]=a7;dJ=be;dK=a7}else{break}h[O>>3]=dJ;c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=~~(cH+ct*(dK-bE)+.5);co=c[144]|0;cf=~~(+(c[64768+(co*688&-1)>>2]|0)+(dJ- +h[64664+(co*688&-1)>>3])*+h[64776+(co*688&-1)>>3]+.5);co=c[U>>2]|0;c[U>>2]=co+1;c[(c[7936]|0)+(co*12&-1)+4>>2]=cf;gj(c[U>>2]|0,c[7936]|0,bv);c[U>>2]=0;cf=c[200]|0;c[c[7936]>>2]=~~(+(c[64768+(cf*688&-1)>>2]|0)+(dK- +h[64664+(cf*688&-1)>>3])*+h[64776+(cf*688&-1)>>3]+.5);cf=c[144]|0;co=~~(+(c[64768+(cf*688&-1)>>2]|0)+(dJ- +h[64664+(cf*688&-1)>>3])*+h[64776+(cf*688&-1)>>3]+.5);cf=c[U>>2]|0;c[U>>2]=cf+1;c[(c[7936]|0)+(cf*12&-1)+4>>2]=co}}while(0);c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=ca;co=c[U>>2]|0;c[U>>2]=co+1;c[(c[7936]|0)+(co*12&-1)+4>>2]=cc;dE=cq;dF=bj;dG=ar;dH=bN;break}else if((aJ|0)==1){do{if((c[cn>>2]|0)!=0){if((a[54152]&1)!=0){break}co=(a[54160]&1)==0;gj(c[U>>2]|0,c[7936]|0,bv);c[U>>2]=0;if(co){c[c[7936]>>2]=ca;co=c[U>>2]|0;c[U>>2]=co+1;c[(c[7936]|0)+(co*12&-1)+4>>2]=cc;dE=cq;dF=bj;dG=ar;dH=bN;break L13454}else{co=c[aK>>2]|0;gb(co,ch,N,O);co=c[200]|0;c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=~~(+(c[64768+(co*688&-1)>>2]|0)+(+h[N>>3]- +h[64664+(co*688&-1)>>3])*+h[64776+(co*688&-1)>>3]+.5);co=c[144]|0;cf=~~(+(c[64768+(co*688&-1)>>2]|0)+(+h[O>>3]- +h[64664+(co*688&-1)>>3])*+h[64776+(co*688&-1)>>3]+.5);co=c[U>>2]|0;c[U>>2]=co+1;c[(c[7936]|0)+(co*12&-1)+4>>2]=cf;c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=ca;cf=c[U>>2]|0;c[U>>2]=cf+1;c[(c[7936]|0)+(cf*12&-1)+4>>2]=cc;dE=cq;dF=bj;dG=ar;dH=bN;break L13454}}}while(0);cf=gb(cd,ch,N,O)|0;do{if((bN|0)==0){dL=cf}else{if((cf|0)==(bN|0)){dL=cq;break}gi(c[7936]|0,U,bN,cf,ar,bj);dL=cq}}while(0);cf=c[200]|0;c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=~~(+(c[64768+(cf*688&-1)>>2]|0)+(+h[N>>3]- +h[64664+(cf*688&-1)>>3])*+h[64776+(cf*688&-1)>>3]+.5);cf=c[144]|0;co=~~(+(c[64768+(cf*688&-1)>>2]|0)+(+h[O>>3]- +h[64664+(cf*688&-1)>>3])*+h[64776+(cf*688&-1)>>3]+.5);cf=c[U>>2]|0;c[U>>2]=cf+1;c[(c[7936]|0)+(cf*12&-1)+4>>2]=co;c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=ca;co=c[U>>2]|0;c[U>>2]=co+1;c[(c[7936]|0)+(co*12&-1)+4>>2]=cc;dE=dL;dF=bj;dG=ar;dH=bN;break}else{gj(c[U>>2]|0,c[7936]|0,bv);c[U>>2]=0;c[c[7936]>>2]=ca;co=c[U>>2]|0;c[U>>2]=co+1;c[(c[7936]|0)+(co*12&-1)+4>>2]=cc;c[(c[7936]|0)+((c[U>>2]|0)*12&-1)>>2]=ca;co=c[U>>2]|0;c[U>>2]=co+1;c[(c[7936]|0)+(co*12&-1)+4>>2]=cc;dE=cq;dF=bj;dG=ar;dH=bN;break}}else{dE=cq;dF=bj;dG=ar;dH=bN}}while(0);cd=ch+1|0;if((cd|0)>=(c[aM>>2]|0)){break}aJ=c[(c[aK>>2]|0)+(ch<<6)>>2]|0;bN=dH;ar=dG;bj=dF;cq=dE;ch=cd;cg=(c[U>>2]|0)+2|0}dM=dH;dN=dG;dO=dF;dP=dE;dQ=c[cn>>2]|0}else{dM=0;dN=0;dO=0;dP=0;dQ=aI}if((dQ|0)==0){if(!((dP|0)==0|(dP|0)==(dM|0))){bt=10045}}else{if(!((a[54152]&1)==0|(dP|0)==0|(dP|0)==(dM|0))){bt=10045}}if((bt|0)==10045){bt=0;gi(c[7936]|0,U,dM,dP,dN,dO)}gj(c[U>>2]|0,c[7936]|0,bv)}if(!(hG(bv+192|0)|0)){break}fX(bv);break}}else if((aH|0)==193){cg=c[3524]|0;ch=bv+300|0;if((c[ch>>2]|0)<=0){break}cq=bv+320|0;bj=cg+56|0;ar=cg+60|0;cg=0;bN=0;aK=0;aJ=2;aM=c[cq>>2]|0;while(1){cr=c[aM+(aK<<6)>>2]|0;if((cr|0)==0){aL=c[200]|0;cd=~~(+(c[64768+(aL*688&-1)>>2]|0)+(+h[aM+(aK<<6)+8>>3]- +h[64664+(aL*688&-1)>>3])*+h[64776+(aL*688&-1)>>3]+.5);aL=c[144]|0;ci=~~(+(c[64768+(aL*688&-1)>>2]|0)+(+h[aM+(aK<<6)+16>>3]- +h[64664+(aL*688&-1)>>3])*+h[64776+(aL*688&-1)>>3]+.5);do{if((aJ|0)==0){cN[c[ar>>2]&255](cg,ci);cN[c[ar>>2]&255](cd,ci)}else if((aJ|0)==1){if((a[54160]&1)==0){break}gf(aM,aK,Y,Z);aL=c[200]|0;co=~~(+(c[64768+(aL*688&-1)>>2]|0)+(+h[Y>>3]- +h[64664+(aL*688&-1)>>3])*+h[64776+(aL*688&-1)>>3]+.5);aL=c[144]|0;cN[c[bj>>2]&255](co,~~(+(c[64768+(aL*688&-1)>>2]|0)+(+h[Z>>3]- +h[64664+(aL*688&-1)>>3])*+h[64776+(aL*688&-1)>>3]+.5));cN[c[ar>>2]&255](co,ci);cN[c[ar>>2]&255](cd,ci)}}while(0);cN[c[bj>>2]&255](cd,ci);dR=ci;dS=cd}else if((cr|0)==1){L13532:do{if((aJ|0)==0){if((a[54160]&1)==0){dT=bN;dU=cg;break}gf(aM,aK,Y,Z);co=c[200]|0;aL=~~(+(c[64768+(co*688&-1)>>2]|0)+(+h[Y>>3]- +h[64664+(co*688&-1)>>3])*+h[64776+(co*688&-1)>>3]+.5);co=c[144]|0;cf=~~(+(c[64768+(co*688&-1)>>2]|0)+(+h[Z>>3]- +h[64664+(co*688&-1)>>3])*+h[64776+(co*688&-1)>>3]+.5);cN[c[ar>>2]&255](cg,cf);cN[c[ar>>2]&255](aL,cf);dT=cf;dU=aL}else if((aJ|0)==1){if((a[54152]&1)==0){dT=bN;dU=cg;break}aL=aK-1|0;bE=+h[aM+(aL<<6)+8>>3];ct=+h[aM+(aL<<6)+16>>3];cH=+h[aM+(aK<<6)+8>>3];cC=+h[aM+(aK<<6)+16>>3];aL=c[200]|0;bD=+h[64664+(aL*688&-1)>>3];if((bE>cH?bE:cH)<bD){dT=bN;dU=cg;break}cF=+h[64672+(aL*688&-1)>>3];if((bE<cH?bE:cH)>cF){dT=bN;dU=cg;break}cf=c[144]|0;cJ=+h[64664+(cf*688&-1)>>3];if((ct>cC?ct:cC)<cJ){dT=bN;dU=cg;break}cs=+h[64672+(cf*688&-1)>>3];if((ct<cC?ct:cC)>cs){dT=bN;dU=cg;break}co=cJ<cs;if(co){if(cC<cJ|cC>cs){bt=9876}else{bt=9880}}else{if(cC<cs|cC>cJ){bt=9876}else{bt=9881}}if((bt|0)==9876){bt=0;if(bD<cF){if(bE<bD|bE>cF){dT=bN;dU=cg;break}}else{if(bE<cF|bE>bD){dT=bN;dU=cg;break}}if(co){bt=9880}else{bt=9881}}if((bt|0)==9880){bt=0;if(cC<cJ|cC>cs){bt=9900}else{bt=9882}}else if((bt|0)==9881){bt=0;if(cC<cs|cC>cJ){bt=9901}else{bt=9882}}do{if((bt|0)==9882){bt=0;cb=bD<cF;if(cb){if(bE<bD|bE>cF){bt=9899}}else{if(bE<cF|bE>bD){bt=9899}}if((bt|0)==9899){bt=0;if(co){bt=9900;break}else{bt=9901;break}}do{if(co){if(ct>cs){dV=cs;break}if(ct>=cJ){dV=ct;break}dV=cJ}else{if(ct>cJ){dV=cJ;break}if(ct>=cs){dV=ct;break}dV=cs}}while(0);if(cb){if(cH>cF){dW=dV;dX=cC;dY=bE;dZ=cF;break}if(cH>=bD){dW=dV;dX=cC;dY=bE;dZ=cH;break}dW=dV;dX=cC;dY=bE;dZ=bD;break}else{if(cH>bD){dW=dV;dX=cC;dY=bE;dZ=bD;break}if(cH>=cF){dW=dV;dX=cC;dY=bE;dZ=cH;break}dW=dV;dX=cC;dY=bE;dZ=cF;break}}}while(0);if((bt|0)==9900){bt=0;if(cC<cJ|cC>cs){bt=9902}else{dW=cC;dX=cC;dY=bD;dZ=cF}}else if((bt|0)==9901){bt=0;if(cC<cs|cC>cJ){bt=9902}else{dW=cC;dX=cC;dY=bD;dZ=cF}}do{if((bt|0)==9902){bt=0;if(bD<cF){if(bE<bD|bE>cF){dT=bN;dU=cg;break L13532}else{dW=cJ;dX=cs;dY=bE;dZ=bE;break}}else{if(bE<cF|bE>bD){dT=bN;dU=cg;break L13532}else{dW=cJ;dX=cs;dY=bE;dZ=bE;break}}}}while(0);bE=+(c[64768+(aL*688&-1)>>2]|0);cs=+h[64776+(aL*688&-1)>>3];co=~~(bE+(dY-bD)*cs+.5);cF=+(c[64768+(cf*688&-1)>>2]|0);cC=+h[64776+(cf*688&-1)>>3];cc=~~(bE+(dZ-bD)*cs+.5);ca=~~(cF+(dX-cJ)*cC+.5);cN[c[bj>>2]&255](co,~~(cF+(dW-cJ)*cC+.5));cN[c[ar>>2]&255](co,ca);cN[c[ar>>2]&255](cc,ca);dT=ca;dU=cc}else{dT=bN;dU=cg}}while(0);cN[c[bj>>2]&255](dU,dT);dR=dT;dS=dU}else{dR=bN;dS=cg}cr=c[cq>>2]|0;cd=c[cr+(aK<<6)>>2]|0;ci=aK+1|0;if((ci|0)<(c[ch>>2]|0)){cg=dS;bN=dR;aK=ci;aJ=cd;aM=cr}else{break}}}else if((aH|0)==1){fX(bv);break}else if((aH|0)==177|(aH|0)==184){aM=c[3524]|0;do{if((aH&8|0)==0){d_=0;d$=0}else{if((c[aM+108>>2]|0)==0){d_=0;d$=0;break}aJ=c[bv+192>>2]|0;if((aJ|0)==1|(aJ|0)==4){d0=(c[bv+196>>2]<<4&65520)+aJ|0}else if((aJ|0)==2|(aJ|0)==5){d0=(c[bv+200>>2]<<4&65520)+aJ|0}else{d0=0}aJ=c[144]|0;do{if((a[64788+(aJ*688&-1)|0]&1)==0){h[ab>>3]=0.0;cC=+h[64664+(aJ*688&-1)>>3];cF=+h[64672+(aJ*688&-1)>>3];if(cC<cF){if(cF<0.0){h[ab>>3]=cF;d1=cF;d2=cC;break}if(cC<=0.0){d1=0.0;d2=cC;break}h[ab>>3]=cC;d1=cC;d2=cC;break}else{if(cC<0.0){h[ab>>3]=cC;d1=cC;d2=cC;break}if(cF<=0.0){d1=0.0;d2=cC;break}h[ab>>3]=cF;d1=cF;d2=cC;break}}else{cC=+h[64664+(aJ*688&-1)>>3];h[ab>>3]=cC;d1=cC;d2=cC}}while(0);d_=d0;d$=~~(+(c[64768+(aJ*688&-1)>>2]|0)+(d1-d2)*+h[64776+(aJ*688&-1)>>3]+.5)}}while(0);aK=bv+300|0;if((c[aK>>2]|0)<=0){break}bN=bv+320|0;cg=aM+56|0;ch=(d_|0)==0;cq=aM+60|0;bj=aM+108|0;ar=0;aI=0;cn=0;cr=2;cd=c[bN>>2]|0;while(1){ci=c[cd+(cn<<6)>>2]|0;if((ci|0)==1){L13617:do{if((cr|0)==0){if((a[54160]&1)==0){d3=aI;d4=ar;break}gg(cd,cn,$,ab);cc=c[200]|0;ca=~~(+(c[64768+(cc*688&-1)>>2]|0)+(+h[$>>3]- +h[64664+(cc*688&-1)>>3])*+h[64776+(cc*688&-1)>>3]+.5);cc=c[144]|0;co=~~(+(c[64768+(cc*688&-1)>>2]|0)+(+h[ab>>3]- +h[64664+(cc*688&-1)>>3])*+h[64776+(cc*688&-1)>>3]+.5);if(ch){cN[c[cq>>2]&255](ca,aI);cN[c[cq>>2]&255](ca,co);d3=co;d4=ca;break}else{cI[c[bj>>2]&63](d_,ar,d$,ca-ar|0,aI-d$|0);d3=co;d4=ca;break}}else if((cr|0)==1){if((a[54152]&1)==0){d3=aI;d4=ar;break}ca=cn-1|0;cC=+h[cd+(ca<<6)+8>>3];cF=+h[cd+(ca<<6)+16>>3];cs=+h[cd+(cn<<6)+8>>3];bE=+h[cd+(cn<<6)+16>>3];ca=c[200]|0;cH=+h[64664+(ca*688&-1)>>3];if((cC>cs?cC:cs)<cH){d3=aI;d4=ar;break}ct=+h[64672+(ca*688&-1)>>3];if((cC<cs?cC:cs)>ct){d3=aI;d4=ar;break}co=c[144]|0;cA=+h[64664+(co*688&-1)>>3];if((cF>bE?cF:bE)<cA){d3=aI;d4=ar;break}a7=+h[64672+(co*688&-1)>>3];if((cF<bE?cF:bE)>a7){d3=aI;d4=ar;break}cc=cA<a7;if(cc){if(cF<cA|cF>a7){bt=9824}else{bt=9828}}else{if(cF<a7|cF>cA){bt=9824}else{bt=9829}}if((bt|0)==9824){bt=0;if(cH<ct){if(cs<cH|cs>ct){d3=aI;d4=ar;break}}else{if(cs<ct|cs>cH){d3=aI;d4=ar;break}}if(cc){bt=9828}else{bt=9829}}if((bt|0)==9828){bt=0;if(cF<cA|cF>a7){bt=9847}else{bt=9830}}else if((bt|0)==9829){bt=0;if(cF<a7|cF>cA){bt=9848}else{bt=9830}}do{if((bt|0)==9830){bt=0;do{if(cH<ct){if(cs<cH|cs>ct){bt=9846;break}if(cC>ct){d5=ct;break}if(cC>=cH){d5=cC;break}d5=cH}else{if(cs<ct|cs>cH){bt=9846;break}if(cC>cH){d5=cH;break}if(cC>=ct){d5=cC;break}d5=ct}}while(0);if((bt|0)==9846){bt=0;if(cc){bt=9847;break}else{bt=9848;break}}if(cc){if(bE>a7){d6=cF;d7=a7;d8=d5;d9=cs;break}if(bE>=cA){d6=cF;d7=bE;d8=d5;d9=cs;break}d6=cF;d7=cA;d8=d5;d9=cs;break}else{if(bE>cA){d6=cF;d7=cA;d8=d5;d9=cs;break}if(bE>=a7){d6=cF;d7=bE;d8=d5;d9=cs;break}d6=cF;d7=a7;d8=d5;d9=cs;break}}}while(0);if((bt|0)==9847){bt=0;if(cF<cA|cF>a7){bt=9849}else{d6=cF;d7=cF;d8=cH;d9=ct}}else if((bt|0)==9848){bt=0;if(cF<a7|cF>cA){bt=9849}else{d6=cF;d7=cF;d8=cH;d9=ct}}do{if((bt|0)==9849){bt=0;if(cH<ct){if(cC<cH|cC>ct){d3=aI;d4=ar;break L13617}else{d6=cA;d7=a7;d8=cs;d9=cs;break}}else{if(cC<ct|cC>cH){d3=aI;d4=ar;break L13617}else{d6=cA;d7=a7;d8=cs;d9=cs;break}}}}while(0);cs=+(c[64768+(ca*688&-1)>>2]|0);a7=+h[64776+(ca*688&-1)>>3];cc=~~(cs+(d8-cH)*a7+.5);cC=+(c[64768+(co*688&-1)>>2]|0);ct=+h[64776+(co*688&-1)>>3];cf=~~(cC+(d6-cA)*ct+.5);aL=~~(cs+(d9-cH)*a7+.5);cb=~~(cC+(d7-cA)*ct+.5);if(ch){cN[c[cg>>2]&255](cc,cf);cN[c[cq>>2]&255](aL,cf);cN[c[cq>>2]&255](aL,cb);d3=cb;d4=aL;break}else{cI[c[bj>>2]&63](d_,cc,d$,aL-cc|0,cf-d$|0);d3=cb;d4=aL;break}}else{d3=aI;d4=ar}}while(0);cN[c[cg>>2]&255](d4,d3);ea=d3;eb=d4}else if((ci|0)==0){aJ=c[200]|0;aL=~~(+(c[64768+(aJ*688&-1)>>2]|0)+(+h[cd+(cn<<6)+8>>3]- +h[64664+(aJ*688&-1)>>3])*+h[64776+(aJ*688&-1)>>3]+.5);aJ=c[144]|0;cb=~~(+(c[64768+(aJ*688&-1)>>2]|0)+(+h[cd+(cn<<6)+16>>3]- +h[64664+(aJ*688&-1)>>3])*+h[64776+(aJ*688&-1)>>3]+.5);do{if((cr|0)==1){if((a[54160]&1)==0){break}gg(cd,cn,$,ab);aJ=c[200]|0;cf=~~(+(c[64768+(aJ*688&-1)>>2]|0)+(+h[$>>3]- +h[64664+(aJ*688&-1)>>3])*+h[64776+(aJ*688&-1)>>3]+.5);aJ=c[144]|0;cc=~~(+(c[64768+(aJ*688&-1)>>2]|0)+(+h[ab>>3]- +h[64664+(aJ*688&-1)>>3])*+h[64776+(aJ*688&-1)>>3]+.5);if(ch){cN[c[cg>>2]&255](cf,cc);cN[c[cq>>2]&255](aL,cc);cN[c[cq>>2]&255](aL,cb);break}aJ=cc-d$|0;b8=c[bj>>2]|0;ce=aL-cf|0;if((aJ|0)<0){cI[b8&63](d_,cf,cc,ce,d$-cc|0);break}else{cI[b8&63](d_,cf,d$,ce,aJ);break}}else if((cr|0)==0){if(ch){cN[c[cq>>2]&255](aL,aI);cN[c[cq>>2]&255](aL,cb);break}aJ=aI-d$|0;ce=c[bj>>2]|0;cf=aL-ar|0;if((aJ|0)<0){cI[ce&63](d_,ar,aI,cf,d$-aI|0);break}else{cI[ce&63](d_,ar,d$,cf,aJ);break}}}while(0);cN[c[cg>>2]&255](aL,cb);ea=cb;eb=aL}else{ea=aI;eb=ar}ci=c[bN>>2]|0;aJ=c[ci+(cn<<6)>>2]|0;cf=cn+1|0;if((cf|0)<(c[aK>>2]|0)){ar=eb;aI=ea;cn=cf;cr=aJ;cd=ci}else{break}}}else if((aH|0)==225){cd=c[3524]|0;cr=c[bv+88>>2]|0;cn=c[bv+92>>2]|0;ct=+h[bv+104>>3];cC=+h[bv+112>>3];aI=a[bv+120|0]|0;uD(c_|0,bv+121|0,23);ar=c[bv+144>>2]|0;a7=+h[bv+152>>3];aK=c[bv+160>>2]|0;cs=+h[bv+168>>3];cF=+h[bv+176>>3];bN=c[bv+184>>2]|0;uD(cv|0,ck|0,16);do{if((cr|0)!=0){cg=c[cd+92>>2]|0;if(cC<0.0){cK[cg&63](+h[3817]);break}else{cK[cg&63](cC);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](ct);cr=c[(c[3524]|0)+64>>2]|0;if((cn|0)<-5){cM[cr&511](-2)}else{cM[cr&511](cn)}cr=c[3524]|0;do{if((aI&1)==0){if((c[cr+96>>2]&1024|0)!=0){break}c[M>>2]=1;c[cV>>2]=cn;bt=10059}else{bt=10059}}while(0);if((bt|0)==10059){bt=0;fn(I,cr)}c[12912]=bN;c[12910]=0;if(a7>0.0){uE(bh|0,0,40);c[bo>>2]=2;c[bL>>2]=2;h[bi>>3]=a7;c[aB>>2]=aK;fN(H,F,G,123984);h[6458]=cs;h[6457]=cF;c[12910]=~~+h[F>>3]}cn=bv+300|0;if((c[cn>>2]|0)<=0){break}aI=bv+320|0;cg=bv+80|0;bj=bv+316|0;cq=cd+84|0;ch=ar;aM=0;while(1){ci=c[aI>>2]|0;uD(R|0,ci+(aM<<6)|0,64);aJ=c[c$>>2]|0;L13717:do{if((aJ|0)==2){ec=ch}else{ct=+h[ci+(aM<<6)+56>>3];h[ao>>3]=ct;cC=+h[ci+(aM<<6)+40>>3];h[an>>3]=cC;do{if((c[cg>>2]|0)==-3){cf=~~+h[ci+(aM<<6)+24>>3];ce=43296;while(1){ed=c[ce>>2]|0;if((ed|0)==0){bt=10069;break}if((c[ed+4>>2]|0)==(cf|0)){bt=10070;break}else{ce=ed|0}}do{if((bt|0)==10069){bt=0;uD(c_|0,51521,23);uh(-1,79128,(v=i,i=i+8|0,c[v>>2]=cf,v)|0);uD(cL|0,ck|0,16);ee=0;ef=90.0;eg=15.0;eh=0;ei=0.0;ej=1;ek=0;el=1.0;em=-2}else if((bt|0)==10070){bt=0;ce=c[ed+16>>2]|0;b8=c[ed+20>>2]|0;bE=+h[ed+32>>3];cJ=+h[ed+40>>3];cc=a[ed+48|0]|0;uD(c_|0,ed+49|0,23);b9=c[ed+72>>2]|0;bD=+h[ed+80>>3];b7=c[ed+88>>2]|0;be=+h[ed+96>>3];bd=+h[ed+104>>3];b4=c[ed+112>>2]|0;uD(cL|0,ck|0,16);if((ce|0)==0){ee=b4;ef=bd;eg=be;eh=b7;ei=bD;ej=b9;ek=cc;el=bE;em=b8;break}ce=c[(c[3524]|0)+92>>2]|0;if(cJ<0.0){cK[ce&63](+h[3817]);ee=b4;ef=bd;eg=be;eh=b7;ei=bD;ej=b9;ek=cc;el=bE;em=b8;break}else{cK[ce&63](cJ);ee=b4;ef=bd;eg=be;eh=b7;ei=bD;ej=b9;ek=cc;el=bE;em=b8;break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](el);cf=c[(c[3524]|0)+64>>2]|0;if((em|0)<-5){cM[cf&511](-2)}else{cM[cf&511](em)}cf=c[3524]|0;do{if((ek&1)==0){if((c[cf+96>>2]&1024|0)!=0){break}c[cj>>2]=1;c[cY>>2]=em;bt=10080}else{bt=10080}}while(0);if((bt|0)==10080){bt=0;fn(E,cf)}c[12912]=ee;c[12910]=0;if(ei<=0.0){en=ej;break}uE(bn|0,0,40);c[cZ>>2]=2;c[by>>2]=2;h[cX>>3]=ei;c[cB>>2]=eh;fN(D,B,C,123984);h[6458]=eg;h[6457]=ef;c[12910]=~~+h[B>>3];en=ej}else{en=ch}}while(0);co=(c[bj>>2]|0)+(aM<<3)|0;f9(bv,co);co=c[200]|0;cA=+h[64664+(co*688&-1)>>3];cH=+h[64672+(co*688&-1)>>3];if(cA<cH){if(!(ct<cA|ct>cH)){bt=10086}}else{if(!(ct<cH|ct>cA)){bt=10086}}do{if((bt|0)==10086){bt=0;ca=c[144]|0;cH=+h[64664+(ca*688&-1)>>3];bE=+h[64672+(ca*688&-1)>>3];if(cH<bE){if(cC<cH|cC>bE){break}}else{if(cC<bE|cC>cH){break}}c[cz>>2]=0;bE=+(c[64768+(co*688&-1)>>2]|0);bD=+h[64776+(co*688&-1)>>3];b8=~~(bE+(ct-cA)*bD+.5);be=+(c[64768+(ca*688&-1)>>2]|0);bd=+h[64776+(ca*688&-1)>>3];ca=~~(be+(cC-cH)*bd+.5);if((aJ|0)==0){cI[c[cq>>2]&63](~~(bE+bD*(+h[cD>>3]-cA)+.5),~~(be+bd*(+h[bI>>3]-cH)+.5),b8,ca,en);ec=en;break L13717}else if((aJ|0)!=1){ec=en;break L13717}if((a[54160]&1)==0){ec=en;break L13717}gb(c0,1,K,L);cc=c[200]|0;b9=~~(+(c[64768+(cc*688&-1)>>2]|0)+(+h[K>>3]- +h[64664+(cc*688&-1)>>3])*+h[64776+(cc*688&-1)>>3]+.5);cc=c[144]|0;b7=~~(+(c[64768+(cc*688&-1)>>2]|0)+(+h[L>>3]- +h[64664+(cc*688&-1)>>3])*+h[64776+(cc*688&-1)>>3]+.5);cc=c[cq>>2]|0;if((en&1|0)==0){cI[cc&63](b9,b7,b8,ca,0);ec=en;break L13717}else{cI[cc&63](b9,b7,b8,ca,1);ec=en;break L13717}}}while(0);c[cz>>2]=1;if((aJ|0)==1){if((a[54152]&1)==0){ec=en;break}if(!(gc(c0,1,aU,c2)|0)){ec=en;break}ca=c[200]|0;cC=+(c[64768+(ca*688&-1)>>2]|0);ct=+h[64664+(ca*688&-1)>>3];cH=+h[64776+(ca*688&-1)>>3];ca=c[144]|0;bd=+(c[64768+(ca*688&-1)>>2]|0);be=+h[64664+(ca*688&-1)>>3];bD=+h[64776+(ca*688&-1)>>3];cI[c[cq>>2]&63](~~(cC+(+h[aU>>3]-ct)*cH+.5),~~(bd+(+h[c2>>3]-be)*bD+.5),~~(cC+cH*(+h[c1>>3]-ct)+.5),~~(bd+bD*(+h[Q>>3]-be)+.5),0);ec=en;break}else if((aJ|0)!=0){ec=en;break}if((a[54160]&1)==0){ec=en;break}ca=~~(+(c[64768+(co*688&-1)>>2]|0)+(+h[cD>>3]-cA)*+h[64776+(co*688&-1)>>3]+.5);b8=c[144]|0;b7=~~(+(c[64768+(b8*688&-1)>>2]|0)+(+h[bI>>3]- +h[64664+(b8*688&-1)>>3])*+h[64776+(b8*688&-1)>>3]+.5);gb(c0,1,K,L);b8=c[200]|0;b9=~~(+(c[64768+(b8*688&-1)>>2]|0)+(+h[K>>3]- +h[64664+(b8*688&-1)>>3])*+h[64776+(b8*688&-1)>>3]+.5);b8=c[144]|0;cc=~~(+(c[64768+(b8*688&-1)>>2]|0)+(+h[L>>3]- +h[64664+(b8*688&-1)>>3])*+h[64776+(b8*688&-1)>>3]+.5);b8=c[cq>>2]|0;if((en&2|0)==0){cI[b8&63](ca,b7,b9,cc,0);ec=en;break}else{cI[b8&63](b9,cc,ca,b7,2);ec=en;break}}}while(0);aJ=aM+1|0;if((aJ|0)<(c[cn>>2]|0)){ch=ec;aM=aJ}else{break}}}else if((aH|0)==209){aM=c[3524]|0;ch=bv+300|0;cn=c[ch>>2]|0;if((cn|0)<=0){break}cq=bv+320|0;bj=c[cq>>2]|0;cg=0;aI=0;do{aI=((c[bj+(cg<<6)>>2]|0)>>>0<2&1)+aI|0;cg=cg+1|0;}while((cg|0)<(cn|0));if((aI|0)<2){break}cn=aI<<2;cg=ut(cn)|0;if((cg|0)==0){gk();bj=ut(cn)|0;if((bj|0)==0){bt=9914;break L13212}else{eo=bj}}else{eo=cg}cg=eo;bj=c[ch>>2]|0;if((bj|0)>0){cn=0;ar=0;cd=bj;while(1){if((c[(c[cq>>2]|0)+(cn<<6)>>2]|0)>>>0<2){c[cg+(ar<<2)>>2]=cn;ep=ar+1|0;eq=c[ch>>2]|0}else{ep=ar;eq=cd}bj=cn+1|0;if((bj|0)<(eq|0)){cn=bj;ar=ep;cd=eq}else{er=ep;break}}}else{er=0}c[9732]=bv;bM(eo|0,er|0,4,30);c[9732]=0;cd=c[144]|0;cF=+h[64664+(cd*688&-1)>>3];if((a[64788+(cd*688&-1)|0]&1)==0){es=0.0}else{cs=+h[64672+(cd*688&-1)>>3];es=cF<cs?cF:cs}ar=c[cg>>2]|0;cn=c[cq>>2]|0;cs=(+h[cn+(ar<<6)+8>>3]*3.0- +h[cn+(c[eo+4>>2]<<6)+8>>3])*.5;ch=c[200]|0;aI=~~(+(c[64768+(ch*688&-1)>>2]|0)+(cs- +h[64664+(ch*688&-1)>>3])*+h[64776+(ch*688&-1)>>3]+.5);c[W>>2]=aI;ch=~~(+(c[64768+(cd*688&-1)>>2]|0)+(es-cF)*+h[64776+(cd*688&-1)>>3]+.5);c[X>>2]=ch;cd=c[13542]|0;if((cd|0)==0){bt=9923}else{bj=(c[cd>>2]|0)>(aI|0)&1;aK=(c[cd+4>>2]|0)<(aI|0)?bj|2:bj;bj=(c[cd+8>>2]|0)>(ch|0)?aK|4:aK;if((((c[cd+12>>2]|0)<(ch|0)?bj|8:bj)|0)==0){bt=9923}else{et=ar;eu=cn}}if((bt|0)==9923){bt=0;cN[c[aM+56>>2]&255](aI,ch);et=c[cg>>2]|0;eu=c[cq>>2]|0}ch=er-1|0;cF=+h[eu+(et<<6)+16>>3];if((ch|0)>0){a7=cs;be=es;aI=1;cn=et;ar=eu;bD=cF;while(1){ev=(a[64788+((c[144]|0)*688&-1)|0]&1)!=0&bD<es?es:bD;bj=cg+(aI<<2)|0;ew=(+h[ar+(cn<<6)+8>>3]+ +h[ar+(c[bj>>2]<<6)+8>>3])*.5;gd(W,X,a7,be,ev);ge(W,X,a7,ew,ev);ex=c[bj>>2]|0;ey=c[cq>>2]|0;ez=+h[ey+(ex<<6)+16>>3];if((aI|0)>=(ch|0)){break}a7=ew;be=ev;aI=aI+1|0;cn=ex;ar=ey;bD=ez}eA=ew;eB=ev;eC=er-2|0;eD=ex;eE=ey;eF=ez}else{eA=cs;eB=es;eC=-1;eD=et;eE=eu;eF=cF}bD=(+h[eE+(eD<<6)+8>>3]*3.0- +h[eE+(c[cg+(eC<<2)>>2]<<6)+8>>3])*.5;gd(W,X,eA,eB,eF);ge(W,X,eA,bD,eF);gd(W,X,bD,eF,es);uu(eo);break}else if((aH|0)==257){f1(bv);break}else if((aH|0)==252){f2(bv);break}else if((aH|0)==474){ar=bv+320|0;cn=c[ar>>2]|0;aI=c[bv+288>>2]|0;ch=(aI|0)==0?1:aI;aI=bv+300|0;cq=c[aI>>2]|0;if((ch|0)<=0){break}aM=(ch|0)>1;bj=(cq|0)>0;cd=bv+292|0;aK=bv+48|0;bN=bv+32|0;bD=0.0;be=0.0;cr=0;aJ=cq;while(1){if(aM){c[aI>>2]=cq;do{if((a[56292]&1)==0){eG=cr}else{ci=c[cd>>2]|0;if((ci|0)==0){eG=cr;break}eG=c[ci+(cr<<2)>>2]|0}}while(0);ci=c[ar>>2]|0;if(bj){a7=+(eG|0);aL=0;cb=ci;while(1){h[cb+(aL<<6)+16>>3]=+h[cb+(aL<<6)+40>>3];c[(c[ar>>2]|0)+(aL<<6)>>2]=0;b7=c[ar>>2]|0;if(+h[b7+(aL<<6)+32>>3]!=a7){c[b7+(aL<<6)>>2]=2;h[(c[ar>>2]|0)+(aL<<6)+16>>3]=8.988465674311579e+307;eH=c[ar>>2]|0}else{eH=b7}b7=aL+1|0;if((b7|0)<(cq|0)){aL=b7;cb=eH}else{eI=eH;break}}}else{eI=ci}bM(eI|0,cq|0,64,12);cb=c[ar>>2]|0;aL=cq;while(1){b7=aL-1|0;if((c[cb+(b7<<6)>>2]|0)==2){aL=b7}else{break}}c[aI>>2]=aL;eJ=aL}else{eJ=aJ}if((eJ|0)<4){h[J>>3]=+h[(c[ar>>2]|0)+8>>3]+ +(cr|0)*+h[7035];h[cW>>3]=-8.988465674311579e+307;h[bu>>3]=8.988465674311579e+307;eK=be;eL=bD}else{if((eJ&1|0)==0){cb=(eJ|0)/2&-1;ci=c[ar>>2]|0;eM=(+h[ci+(cb-1<<6)+16>>3]+ +h[ci+(cb<<6)+16>>3])*.5;eN=ci}else{ci=c[ar>>2]|0;eM=+h[ci+(((eJ-1|0)/2&-1)<<6)+16>>3];eN=ci}if((eJ&3|0)==0){ci=(eJ|0)/4&-1;cb=eJ-ci|0;eO=(+h[eN+(cb<<6)+16>>3]+ +h[eN+(cb-1<<6)+16>>3])*.5;eP=(+h[eN+(ci-1<<6)+16>>3]+ +h[eN+(ci<<6)+16>>3])*.5}else{ci=(eJ+3|0)/4&-1;eO=+h[eN+(eJ-ci<<6)+16>>3];eP=+h[eN+(ci-1<<6)+16>>3]}L13836:do{if((c[14062]|0)==0){a7=(eO-eP)*+h[7032];bd=eP-a7;ci=0;while(1){if((ci|0)>=(eJ|0)){eQ=bd;break}ct=+h[eN+(ci<<6)+16>>3];if(ct<bd){ci=ci+1|0}else{eQ=ct;break}}bd=eO+a7;ci=eJ;while(1){co=ci-1|0;if((ci|0)<=0){eR=eQ;eS=bd;eT=eN;break L13836}cA=+h[eN+(co<<6)+16>>3];if(cA>bd){ci=co}else{eR=eQ;eS=cA;eT=eN;break}}}else{bd=+(eJ|0);a7=+h[7032];ci=0;co=eJ-1|0;cA=be;ct=bD;cb=eN;L13846:while(1){b7=1-ci|0;ca=co;cH=cA;cC=ct;cc=cb;while(1){if(+(b7+ca|0)/bd<a7){eR=cH;eS=cC;eT=cc;break L13836}eU=+h[cc+(ca<<6)+16>>3];eV=+h[cc+(ci<<6)+16>>3];bE=eU-eM;cJ=eM-eV;L13851:do{if(bE<cJ){eW=ca;eX=cc}else{b9=ca;b8=cc;while(1){b4=b9-1|0;if((b4|0)<=0){eW=b4;eX=b8;break L13851}if(+h[eN+(b4<<6)+16>>3]==+h[eN+(b9-2<<6)+16>>3]){b9=b4;b8=eN}else{eW=b4;eX=eN;break}}}}while(0);if(bE>cJ){ca=eW;cH=eV;cC=eU;cc=eX}else{break}}cc=ci+1|0;while(1){if((cc|0)>=(eW|0)){ci=cc;co=eW;cA=eV;ct=eU;cb=eX;continue L13846}ca=cc+1|0;if(+h[eX+(cc<<6)+16>>3]==+h[eX+(ca<<6)+16>>3]){cc=ca}else{ci=cc;co=eW;cA=eV;ct=eU;cb=eX;continue L13846}}}}}while(0);c[cP>>2]=0;ct=+h[eT+8>>3];if((c[cp>>2]|0)==0){h[J>>3]=(ct+ +h[eT+(eJ-1<<6)+8>>3])*.5;eY=+h[7035];eZ=+(cr|0)}else{cA=+h[7035];a7=+(cr|0);h[J>>3]=ct+a7*cA;eY=cA;eZ=a7}h[cQ>>3]=eP;h[cm>>3]=eO;h[bu>>3]=eR;h[cW>>3]=eS;h[aR>>3]=+h[eT+48>>3]+eZ*eY;h[aQ>>3]=eM;c[ar>>2]=A;c[aI>>2]=1;if((c[14068]|0)==257){f1(bv)}else{f2(bv)}c[ar>>2]=cn;c[aI>>2]=eJ;eK=eR;eL=eS}do{if((a[56264]&1)==0){e_=eJ}else{c[8240]=~~(+h[aK>>3]*+((c[(c[3524]|0)+28>>2]|0)>>>0>>>0));aL=c[aI>>2]|0;if((aL|0)>0){e$=0;e0=aL}else{e_=aL;break}while(1){aL=c[ar>>2]|0;a7=+h[aL+(e$<<6)+16>>3];if(a7<+h[bu>>3]){bt=10156}else{if(a7>+h[cW>>3]){bt=10156}else{e1=e0}}do{if((bt|0)==10156){bt=0;if((c[aL+(e$<<6)>>2]|0)!=0){e1=e0;break}cb=c[200]|0;co=~~(+(c[64768+(cb*688&-1)>>2]|0)+(+h[J>>3]- +h[64664+(cb*688&-1)>>3])*+h[64776+(cb*688&-1)>>3]+.5);cb=c[144]|0;ci=~~(+(c[64768+(cb*688&-1)>>2]|0)+(a7- +h[64664+(cb*688&-1)>>3])*+h[64776+(cb*688&-1)>>3]+.5);if((a[54144]&1)!=0){cb=c[8240]|0;if((co|0)<(cb+(c[7938]|0)|0)){e1=e0;break}cc=c[8242]|0;if((ci|0)<(cc+(c[7940]|0)|0)){e1=e0;break}if((co|0)>((c[7939]|0)-cb|0)){e1=e0;break}if((ci|0)>((c[7941]|0)-cc|0)){e1=e0;break}}L13885:do{if((e$|0)<1){e2=co}else{cc=c[8240]|0;cb=1;ca=co;while(1){if(a7!=+h[aL+(e$-cb<<6)+16>>3]){e2=ca;break L13885}b7=aa((cb&1|0)==0?-cb|0:cb,cc)+ca|0;cf=cb+1|0;if((e$|0)<(cf|0)){e2=b7;break}else{cb=cf;ca=b7}}}}while(0);cR[c[(c[3524]|0)+80>>2]&127](e2,ci,c[bN>>2]|0);e1=c[aI>>2]|0}}while(0);aL=e$+1|0;if((aL|0)<(e1|0)){e$=aL;e0=e1}else{e_=e1;break}}}}while(0);aL=cr+1|0;if((aL|0)<(ch|0)){bD=eL;be=eK;cr=aL;aJ=e_}else{break}}}else if((aH|0)==352){uh(-1,108328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);break}else if((aH|0)==368){aJ=c[c[bv+224>>2]>>2]|0;cr=c[(c[3524]|0)+92>>2]|0;if((cr|0)!=0){cK[cr&63](+h[3817])}if((aJ|0)==0){break}else{e3=aJ}do{L13900:do{if((c[e3+56>>2]|0)==99){aJ=c[200]|0;be=+h[e3+24>>3];cr=c[144]|0;bD=+h[e3+32>>3];ch=~~(+(c[64768+(cr*688&-1)>>2]|0)+(bD- +h[64664+(cr*688&-1)>>3])*+h[64776+(cr*688&-1)>>3]+.5);cr=~~(+(c[64768+(aJ*688&-1)>>2]|0)+(be- +h[64664+(aJ*688&-1)>>3])*+h[64776+(aJ*688&-1)>>3]+.5);aJ=c[e3+8>>2]|0;do{if((aJ|0)==0){cF=+h[8255];cs=+h[8256];if(cF<cs){if(be<cF|be>cs){break L13900}else{break}}else{if(be<cs|be>cF){break L13900}else{break}}}else if((aJ|0)==1){cF=+h[8599];cs=+h[8600];if(cF<cs){if(be<cF|be>cs){break L13900}else{break}}else{if(be<cs|be>cF){break L13900}else{break}}}}while(0);aJ=c[e3+12>>2]|0;do{if((aJ|0)==0){be=+h[8169];cF=+h[8170];if(be<cF){if(bD<be|bD>cF){break L13900}else{break}}else{if(bD<cF|bD>be){break L13900}else{break}}}else if((aJ|0)==1){be=+h[8513];cF=+h[8514];if(be<cF){if(bD<be|bD>cF){break L13900}else{break}}else{if(bD<cF|bD>be){break L13900}else{break}}}}while(0);fp(cr,ch,e3)}}while(0);e3=c[e3>>2]|0;}while((e3|0)!=0)}else if((aH|0)==400){c[bv+228>>2]=0;f3(bv,0);break}else if((aH|0)==416){c[bv+228>>2]=1;f3(bv,0);break}else if((aH|0)==432){c[bv+228>>2]=2;f3(bv,0);break}else if((aH|0)==457){aJ=bv+192|0;aI=c[aJ>>2]|0;if((aI|0)==1|(aI|0)==4){e4=(c[bv+196>>2]<<4&65520)+aI|0}else if((aI|0)==2|(aI|0)==5){e4=(c[bv+200>>2]<<4&65520)+aI|0}else{e4=0}if((c[bv+208>>2]|0)==1){if((c[bv+212>>2]|0)==-3){e5=0}else{bt=10198}}else{bt=10198}if((bt|0)==10198){bt=0;e5=1}aI=bv+300|0;if((c[aI>>2]|0)<=0){break}bN=bv+320|0;ar=bv+316|0;aK=e5^1;cn=bv+24|0;cq=bv+48|0;bj=0;do{cd=c[bN>>2]|0;do{if((c[cd+(bj<<6)>>2]|0)==0){aM=c[200]|0;bD=+(c[64768+(aM*688&-1)>>2]|0);be=+h[64664+(aM*688&-1)>>3];cF=+h[64776+(aM*688&-1)>>3];aM=~~(bD+(+h[cd+(bj<<6)+8>>3]-be)*cF+.5);cg=c[144]|0;aL=~~(+(c[64768+(cg*688&-1)>>2]|0)+(+h[cd+(bj<<6)+16>>3]- +h[64664+(cg*688&-1)>>3])*+h[64776+(cg*688&-1)>>3]+.5);h[y>>3]=+(aM-~~(bD+cF*(+h[cd+(bj<<6)+48>>3]-be)+.5)|0);if(+h[cd+(bj<<6)+24>>3]==-1.0){fN(50952,y,z,184696);e6=c[bN>>2]|0}else{e6=cd}be=+h[e6+(bj<<6)+32>>3];cF=+h[e6+(bj<<6)+56>>3];do{if(!(f9(bv,(c[ar>>2]|0)+(bj<<3)|0)|aK)){c[cw>>2]=c[bm>>2];c[cw+4>>2]=c[bm+4>>2];c[cw+8>>2]=c[bm+8>>2];c[cw+12>>2]=c[bm+12>>2];cg=c[cu>>2]|0;do{if((c[cn>>2]|0)!=0){bD=+h[cq>>3];co=c[(c[3524]|0)+92>>2]|0;if(bD<0.0){cK[co&63](+h[3817]);break}else{cK[co&63](bD);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[aP>>3]);co=c[(c[3524]|0)+64>>2]|0;if((cg|0)<-5){cM[co&511](-2)}else{cM[co&511](cg)}co=c[3524]|0;if((a[cx]&1)==0){if((c[co+96>>2]&1024|0)!=0){break}c[cU>>2]=1;c[bz>>2]=cg}fn(x,co)}}while(0);bD=+h[y>>3];lq(aM,aL,bD,be,cF,e4);if(!e5){break}hG(aJ);lq(aM,aL,bD,be,cF,0)}}while(0);bj=bj+1|0;}while((bj|0)<(c[aI>>2]|0))}else if((aH|0)==489){aI=ut(96)|0;if((aI|0)==0){gk();bj=ut(96)|0;if((bj|0)==0){bt=10221;break L13212}else{e7=bj}}else{e7=aI}aI=e7;bj=bv+192|0;aJ=c[bj>>2]|0;if((aJ|0)==1|(aJ|0)==4){e8=(c[bv+196>>2]<<4&65520)+aJ|0}else if((aJ|0)==2|(aJ|0)==5){e8=(c[bv+200>>2]<<4&65520)+aJ|0}else{e8=0}if((c[bv+208>>2]|0)==1){if((c[bv+212>>2]|0)==-3){e9=0}else{bt=10227}}else{bt=10227}if((bt|0)==10227){bt=0;e9=1}aJ=e7+48|0;cq=aJ;c[aJ>>2]=(c[cl>>2]|0)==6&1;c[e7+52>>2]=(c[ap>>2]|0)==5&1;aJ=e7;c[aJ>>2]=c[bv+272>>2];cn=bv+300|0;if((c[cn>>2]|0)>0){aK=bv+320|0;ar=e7+24|0;bN=e7+32|0;cd=e7+64|0;ch=e7+72|0;cr=e7+88|0;co=bv+316|0;ca=e9^1;cb=bv+24|0;cc=bv+48|0;b7=0;do{cf=c[aK>>2]|0;do{if((c[cf+(b7<<6)>>2]|0)==0){b8=c[200]|0;h[ar>>3]=+(~~(+(c[64768+(b8*688&-1)>>2]|0)+(+h[cf+(b7<<6)+8>>3]- +h[64664+(b8*688&-1)>>3])*+h[64776+(b8*688&-1)>>3]+.5)|0);b8=c[144]|0;h[bN>>3]=+(~~(+(c[64768+(b8*688&-1)>>2]|0)+(+h[(c[aK>>2]|0)+(b7<<6)+16>>3]- +h[64664+(b8*688&-1)>>3])*+h[64776+(b8*688&-1)>>3]+.5)|0);h[cd>>3]=+h[(c[aK>>2]|0)+(b7<<6)+48>>3];h[ch>>3]=+h[(c[aK>>2]|0)+(b7<<6)+56>>3];b8=c[aJ>>2]|0;if((b8|0)==0){fN(cq,u,w,73152);h[cd>>3]=+h[u>>3];h[ch>>3]=+h[w>>3]}else if((b8|0)==1){fN(cq,u,w,73152);bD=+h[u>>3];h[cd>>3]=+h[ch>>3];fN(cq,w,u,73152);h[cd>>3]=bD;h[ch>>3]=+h[w>>3]}else if((b8|0)==2){fN(cq,u,w,73152);bD=+h[w>>3];h[ch>>3]=+h[cd>>3];fN(cq,w,u,73152);h[cd>>3]=+h[u>>3];h[ch>>3]=bD}b8=c[aK>>2]|0;bD=+h[b8+(b7<<6)+24>>3];if(bD>-1.0){fa=b8;fb=bD}else{fN(49672,cd,ch,73152);b8=c[aK>>2]|0;fa=b8;fb=+h[b8+(b7<<6)+24>>3]}if(fb==-2.0){fc=49712}else{fc=fa+(b7<<6)+32|0}h[cr>>3]=+h[fc>>3];do{if(!(f9(bv,(c[co>>2]|0)+(b7<<3)|0)|ca)){c[cT>>2]=c[bm>>2];c[cT+4>>2]=c[bm+4>>2];c[cT+8>>2]=c[bm+8>>2];c[cT+12>>2]=c[bm+12>>2];b8=c[cu>>2]|0;do{if((c[cb>>2]|0)!=0){bD=+h[cc>>3];b9=c[(c[3524]|0)+92>>2]|0;if(bD<0.0){cK[b9&63](+h[3817]);break}else{cK[b9&63](bD);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[aP>>3]);cg=c[(c[3524]|0)+64>>2]|0;if((b8|0)<-5){cM[cg&511](-2)}else{cM[cg&511](b8)}cg=c[3524]|0;if((a[cx]&1)==0){if((c[cg+96>>2]&1024|0)!=0){break}c[bA>>2]=1;c[cE>>2]=b8}fn(t,cg)}}while(0);fP(2,aI,e8,0);if(!e9){break}hG(bj);fP(2,aI,0,0)}}while(0);b7=b7+1|0;}while((b7|0)<(c[cn>>2]|0))}uu(e7);break}else{break}}}while(0);do{if(((a[36228]&1)==0|bK)&c7){if((c[bv+16>>2]|0)==0){fd=c8;break}if((a[bv+22|0]&1)!=0){fd=c8;break}ap=c[aO>>2]|0;if((ap|0)==51){do{if((c[bv+36>>2]|0)<0){cl=c[a3>>2]|0;if((cl|0)!=0){cM[cl&511](56688)}cK[c[a2>>2]&63](+h[3817]*+h[3818]);cR[c[a_>>2]&127]((c[9016]|0)+(c[aS>>2]|0)|0,c[aT>>2]|0,6);c[aY>>2]=c[bm>>2];c[aY+4>>2]=c[bm+4>>2];c[aY+8>>2]=c[bm+8>>2];c[aY+12>>2]=c[bm+12>>2];cl=c[cu>>2]|0;do{if((c[aq>>2]|0)!=0){cF=+h[bv+48>>3];cp=c[(c[3524]|0)+92>>2]|0;if(cF<0.0){cK[cp&63](+h[3817]);break}else{cK[cp&63](cF);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[aP>>3]);cp=c[(c[3524]|0)+64>>2]|0;if((cl|0)<-5){cM[cp&511](-2)}else{cM[cp&511](cl)}cp=c[3524]|0;if((a[cx]&1)==0){if((c[cp+96>>2]&1024|0)!=0){break}c[aZ>>2]=1;c[aX>>2]=cl}fn(s,cp)}}while(0);fe=c[aO>>2]|0}else{fe=ap}do{if((fe|0)==64){cp=(c[9016]|0)+(c[aS>>2]|0)|0;cn=c[aT>>2]|0;b7=c[3524]|0;if((c[b7+96>>2]&128|0)==0){if((cp|0)<=0){break}if(!((c[b7+8>>2]|0)>>>0>cp>>>0&(cn|0)>0)){break}if((c[b7+12>>2]|0)>>>0<=cn>>>0){break}}cR[c[a_>>2]&127](cp,cn,-1)}else if((fe|0)!=474){if((fe&2|0)==0){break}if(+h[bv+48>>3]==-3.0){cK[c[a2>>2]&63](+h[3817])}cM[c[a0>>2]&511](8);cn=(c[9016]|0)+(c[aS>>2]|0)|0;cp=c[aT>>2]|0;b7=c[3524]|0;do{if((c[b7+96>>2]&128|0)==0){if((cn|0)<=0){break}if(!((c[b7+8>>2]|0)>>>0>cn>>>0&(cp|0)>0)){break}if((c[b7+12>>2]|0)>>>0>cp>>>0){bt=10287}}else{bt=10287}}while(0);if((bt|0)==10287){bt=0;cR[c[a_>>2]&127](cn,cp,c[bv+32>>2]|0)}cM[c[a0>>2]&511](9)}}while(0);if((a[36230]&1)!=0){c[aT>>2]=(((c[54]|0)+(c[9336]|0)|0)+((c[9020]|0)/2&-1)|0)-(c[aT>>2]|0)}if((c8|0)<(c[9012]|0)){c[aT>>2]=(c[aT>>2]|0)-(c[9020]|0);fd=c8;break}else{c[aT>>2]=c[54];c[aS>>2]=(c[aS>>2]|0)+(c[9026]|0);fd=0;break}}else{fd=c8}}while(0);cM[c[(c[3524]|0)+168>>2]&511](7);cx=bl+1|0;aP=a[35184]|0;if((cx|0)<(d|0)){bH=c[aO>>2]|0;bq=fd;bv=c[bv>>2]|0;bl=cx;bJ=aP}else{ff=fd;fg=aP;break}}}else{ff=bB;fg=bk}if((fg&1)==0){bt=10298;break}if((a[36228]&1)==0|bK){bt=10298;break}fW(1,aS,aT);bB=ff;bK=1}if((bt|0)==9914){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=142288,v)|0)}else if((bt|0)==10221){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=148064,v)|0)}else if((bt|0)==10298){if((c[10026]|0)==1){fT()}do{if((a[30528]&1)!=0){if((a[27776]&1)==0){break}uD(r|0,31816,272);bK=c[200]|0;fb=+(c[64768+(bK*688&-1)>>2]|0);eK=+h[64664+(bK*688&-1)>>3];eL=+h[64776+(bK*688&-1)>>3];bK=~~(fb+(0.0-eK)*eL+.5);ff=c[144]|0;bB=~~(+(c[64768+(ff*688&-1)>>2]|0)+(0.0- +h[64664+(ff*688&-1)>>3])*+h[64776+(ff*688&-1)>>3]+.5);eS=(c[17366]&2|0)==0?+h[8688]:+h[8686];if((a[69604]&1)==0){fh=+h[8687];fi=eS}else{eR=+_(+eS);eS=+h[8702];fh=+_(+(+h[8687]))/eS;fi=eR/eS}ff=~~(fb+eL*(fi-fh-eK)+.5);aT=q;c[aT>>2]=c[14084];c[aT+4>>2]=c[56340>>2];c[aT+8>>2]=c[56344>>2];c[aT+12>>2]=c[56348>>2];aT=c[14075]|0;do{if((c[14074]|0)!=0){eK=+h[7040];aS=c[(c[3524]|0)+92>>2]|0;if(eK<0.0){cK[aS&63](+h[3817]);break}else{cK[aS&63](eK);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);bk=c[(c[3524]|0)+64>>2]|0;if((aT|0)<-5){cM[bk&511](-2)}else{cM[bk&511](aT)}bk=c[3524]|0;do{if((a[56328]&1)==0){if((c[bk+96>>2]&1024|0)!=0){fj=bk;break}c[q>>2]=1;c[q+4>>2]=aT;bt=10315}else{bt=10315}}while(0);if((bt|0)==10315){fn(q,bk);fj=c[3524]|0}c[m>>2]=bK;c[n>>2]=bB;c[o>>2]=ff;c[p>>2]=bB;if((fl(m,n,o,p)|0)!=0){cN[c[fj+56>>2]&255](c[m>>2]|0,c[n>>2]|0);cN[c[fj+60>>2]&255](c[o>>2]|0,c[p>>2]|0)}if(!((c[17366]&1|0)==0&+h[8687]!=0.0)){break}fO(r,1,2)}}while(0);if((c[10026]|0)==1){c[200]=2;c[144]=1;dg(2,1);dg(1,2);c[200]=6;c[144]=5;dg(6,5);dg(5,6)}if((c[11692]|0)!=0&(c[14088]|0)==1){fU()}do{if(a[31104]&(a[54016]|0)!=110){if(!((c[(c[3524]|0)+144>>2]|0)!=0&(c[13506]|0)==1)){break}dD(1)}}while(0);fO(c[10818]|0,1,2);r=c[10822]|0;p=c[(c[3524]|0)+92>>2]|0;if((p|0)!=0){cK[p&63](+h[3817])}if((r|0)!=0){p=r;do{if((c[p+56>>2]|0)==1){fR(p+8|0,k,l,202256);fp(~~+h[k>>3],~~+h[l>>3],p)}p=c[p>>2]|0;}while((p|0)!=0)}p=c[9682]|0;if((p|0)!=0){l=p;do{p=l+116|0;k=c[p>>2]|0;do{if((k|0)!=0){if((a[k]|0)==0){break}fN(38880,f,j,149952);r=c[200]|0;o=~~(+(c[64768+(r*688&-1)>>2]|0)+ +h[64776+(r*688&-1)>>3]*((+h[l+16>>3]+ +h[l+24>>3])*.5- +h[64664+(r*688&-1)>>3])+.5)+~~+h[f>>3]|0;r=c[3524]|0;fj=~~(+((c[190]|0)>>>0>>>0)+(+(~~+h[j>>3]|0)+ +((c[r+16>>2]|0)>>>0>>>0)*.25));n=l+128|0;fn(n,r);ln(o,fj,c[p>>2]|0,1,2,0,c[l+120>>2]|0);if((c[n>>2]|0)==0){break}cM[c[(c[3524]|0)+64>>2]&511](-2)}}while(0);l=c[l+48>>2]|0;}while((l|0)!=0)}fV(1);if(!(a[31104]|0)){lh();i=e;return}l=c[(c[3524]|0)+140>>2]|0;if((l|0)==0){lh();i=e;return}cS[l&511]();lh();i=e;return}}function fT(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0,q=0.0,r=0.0,s=0.0;b=i;i=i+64|0;d=b|0;e=b+8|0;f=b+16|0;g=b+24|0;j=b+32|0;k=b+48|0;l=c[3524]|0;m=k;c[m>>2]=c[14084];c[m+4>>2]=c[56340>>2];c[m+8>>2]=c[56344>>2];c[m+12>>2]=c[56348>>2];m=c[14075]|0;do{if((c[14074]|0)!=0){n=+h[7040];o=c[l+92>>2]|0;if(n<0.0){cK[o&63](+h[3817]);break}else{cK[o&63](n);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);o=c[(c[3524]|0)+64>>2]|0;if((m|0)<-5){cM[o&511](-2)}else{cM[o&511](m)}o=c[3524]|0;do{if((a[56328]&1)==0){if((c[o+96>>2]&1024|0)!=0){break}c[k>>2]=1;c[k+4>>2]=m;p=10357}else{p=10357}}while(0);if((p|0)==10357){fn(k,o)}h[4483]=0.0;c[200]=2;c[144]=1;df(1,144,2,4);df(2,704,1,2);c[200]=6;c[144]=5;df(5,584,6,4);df(6,816,5,2);c[200]=2;c[144]=1;if(!((a[30528]&1)==0|(c[17433]|0)==0)){o=~~(+(c[16364]|0)+(0.0- +h[8169])*+h[8183]+.5);c[1394]=o;c[1396]=o;c[1392]=o-(c[l+16>>2]|0);o=c[17462]|0;c[6586]=o;do{if((o|0)==0){c[1398]=1}else{if((cO[c[l+72>>2]&255](o)|0)==0){break}c[1398]=(c[6586]|0)==-270?2:0}}while(0);if((a[70088]&1)!=0){c[1398]=c[17486]}de(7,2);o=c[l+72>>2]|0;cO[o&255](0)}if(+h[3815]==0.0){i=b;return}o=c[200]|0;l=~~(+(c[64768+(o*688&-1)>>2]|0)+(0.0- +h[64664+(o*688&-1)>>3])*+h[64776+(o*688&-1)>>3]+.5);o=c[144]|0;k=~~(+(c[64768+(o*688&-1)>>2]|0)+(0.0- +h[64664+(o*688&-1)>>3])*+h[64776+(o*688&-1)>>3]+.5);o=j;c[o>>2]=c[10022];c[o+4>>2]=c[40092>>2];c[o+8>>2]=c[40096>>2];c[o+12>>2]=c[40100>>2];o=c[10013]|0;do{if((c[10012]|0)!=0){n=+h[5009];m=c[(c[3524]|0)+92>>2]|0;if(n<0.0){cK[m&63](+h[3817]);break}else{cK[m&63](n);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[5008]);m=c[(c[3524]|0)+64>>2]|0;if((o|0)<-5){cM[m&511](-2)}else{cM[m&511](o)}m=c[3524]|0;do{if((a[40080]&1)==0){if((c[m+96>>2]&1024|0)!=0){break}c[j>>2]=1;c[j+4>>2]=o;p=10377}else{p=10377}}while(0);if((p|0)==10377){fn(j,m)}n=0.0;while(1){m=c[200]|0;q=+(c[64768+(m*688&-1)>>2]|0);r=+h[4483];s=r*+S(+n);j=~~(q+(s- +h[64664+(m*688&-1)>>3])*+h[64776+(m*688&-1)>>3]+.5);m=c[144]|0;s=+(c[64768+(m*688&-1)>>2]|0);q=r*+T(+n);p=~~(s+(q- +h[64664+(m*688&-1)>>3])*+h[64776+(m*688&-1)>>3]+.5);c[d>>2]=l;c[e>>2]=k;c[f>>2]=j;c[g>>2]=p;p=c[3524]|0;if((fl(d,e,f,g)|0)!=0){cN[c[p+56>>2]&255](c[d>>2]|0,c[e>>2]|0);cN[c[p+60>>2]&255](c[f>>2]|0,c[g>>2]|0)}if(n>=6.29){break}n=n+ +h[3815]}i=b;return}function fU(){var b=0,d=0,e=0,f=0.0,g=0,j=0,k=0.0,l=0.0;b=i;i=i+16|0;d=b|0;e=d;c[e>>2]=c[14084];c[e+4>>2]=c[56340>>2];c[e+8>>2]=c[56344>>2];c[e+12>>2]=c[56348>>2];e=c[14075]|0;do{if((c[14074]|0)!=0){f=+h[7040];g=c[(c[3524]|0)+92>>2]|0;if(f<0.0){cK[g&63](+h[3817]);break}else{cK[g&63](f);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);g=c[(c[3524]|0)+64>>2]|0;if((e|0)<-5){cM[g&511](-2)}else{cM[g&511](e)}g=c[3524]|0;do{if((a[56328]&1)==0){if((c[g+96>>2]&1024|0)!=0){break}c[d>>2]=1;c[d+4>>2]=e;j=10396}else{j=10396}}while(0);if((j|0)==10396){fn(d,g)}do{if((c[11692]&15|0)==15){g=c[(c[3524]|0)+172>>2]|0;if((g|0)==0){break}cM[g&511](0)}}while(0);cN[c[(c[3524]|0)+56>>2]&255](c[7938]|0,c[7941]|0);do{if((c[11692]&2|0)==0){cN[c[(c[3524]|0)+56>>2]&255](c[7938]|0,c[7940]|0)}else{if((a[65712]&1)==0){cN[c[(c[3524]|0)+60>>2]&255](c[7938]|0,c[7940]|0);break}else{f=+(c[16364]|0);k=+h[8169];l=+h[8183];g=~~(f+l*(+h[8175]-k)+.5);cN[c[(c[3524]|0)+56>>2]&255](c[7938]|0,~~(f+(+h[8176]-k)*l+.5));cN[c[(c[3524]|0)+60>>2]&255](c[7938]|0,g);cN[c[(c[3524]|0)+56>>2]&255](c[7938]|0,c[7940]|0);break}}}while(0);do{if((c[11692]&1|0)==0){cN[c[(c[3524]|0)+56>>2]&255](c[7939]|0,c[7940]|0)}else{if((a[66400]&1)==0){cN[c[(c[3524]|0)+60>>2]&255](c[7939]|0,c[7940]|0);break}else{l=+(c[16536]|0);k=+h[8255];f=+h[8269];g=~~(l+(+h[8262]-k)*f+.5);cN[c[(c[3524]|0)+56>>2]&255](~~(l+f*(+h[8261]-k)+.5),c[7940]|0);cN[c[(c[3524]|0)+60>>2]&255](g,c[7940]|0);cN[c[(c[3524]|0)+56>>2]&255](c[7939]|0,c[7940]|0);break}}}while(0);do{if((c[11692]&8|0)==0){cN[c[(c[3524]|0)+56>>2]&255](c[7939]|0,c[7941]|0)}else{if((a[68464]&1)==0){cN[c[(c[3524]|0)+60>>2]&255](c[7939]|0,c[7941]|0);break}else{k=+(c[17052]|0);f=+h[8513];l=+h[8527];g=~~(k+l*(+h[8519]-f)+.5);cN[c[(c[3524]|0)+56>>2]&255](c[7939]|0,~~(k+(+h[8520]-f)*l+.5));cN[c[(c[3524]|0)+60>>2]&255](c[7939]|0,g);cN[c[(c[3524]|0)+56>>2]&255](c[7939]|0,c[7940]|0);break}}}while(0);do{if((c[11692]&4|0)==0){cN[c[(c[3524]|0)+56>>2]&255](c[7938]|0,c[7941]|0)}else{if((a[69152]&1)==0){cN[c[(c[3524]|0)+60>>2]&255](c[7938]|0,c[7941]|0);break}else{l=+(c[17224]|0);f=+h[8599];k=+h[8613];g=~~(l+(+h[8606]-f)*k+.5);cN[c[(c[3524]|0)+56>>2]&255](~~(l+k*(+h[8605]-f)+.5),c[7941]|0);cN[c[(c[3524]|0)+60>>2]&255](g,c[7941]|0);cN[c[(c[3524]|0)+56>>2]&255](c[7939]|0,c[7941]|0);break}}}while(0);if((c[11692]&15|0)!=15){i=b;return}g=c[(c[3524]|0)+172>>2]|0;if((g|0)==0){i=b;return}cM[g&511](1);i=b;return}function fV(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0.0,B=0.0,C=0,D=0,E=0.0,F=0.0,G=0,H=0,I=0,J=0;d=i;i=i+120|0;e=d|0;f=d+16|0;g=d+24|0;j=d+32|0;k=d+72|0;l=d+88|0;m=d+96|0;n=d+104|0;o=d+112|0;p=c[13542]|0;c[13542]=(c[(c[3524]|0)+96>>2]&128|0)==0?55520:0;q=c[10826]|0;if((q|0)!=0){r=k;s=k|0;t=k+4|0;u=j;v=j+4|0;w=j+8|0;x=j+16|0;y=j|0;z=q;do{if((c[z+100>>2]|0)==(b|0)){fR(z+8|0,l,m,123984);A=+h[l>>3];q=~~A;B=+h[m>>3];C=~~B;D=z+48|0;if((a[z+88|0]&1)==0){fR(D,n,o,123984);E=+h[o>>3];F=+h[n>>3]}else{fN(D,n,o,123984);E=B+ +h[o>>3];F=A+ +h[n>>3]}D=~~F;G=~~E;H=z+144|0;c[r>>2]=c[H>>2];c[r+4>>2]=c[H+4>>2];c[r+8>>2]=c[H+8>>2];c[r+12>>2]=c[H+12>>2];H=c[z+108>>2]|0;do{if((c[z+104>>2]|0)!=0){A=+h[z+128>>3];I=c[(c[3524]|0)+92>>2]|0;if(A<0.0){cK[I&63](+h[3817]);break}else{cK[I&63](A);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[z+120>>3]);I=c[(c[3524]|0)+64>>2]|0;if((H|0)<-5){cM[I&511](-2)}else{cM[I&511](H)}I=c[3524]|0;do{if((a[z+136|0]&1)==0){if((c[I+96>>2]&1024|0)!=0){break}c[s>>2]=1;c[t>>2]=H;J=10443}else{J=10443}}while(0);if((J|0)==10443){J=0;fn(k,I)}c[12912]=c[z+200>>2];c[12910]=0;A=+h[z+168>>3];if(A>0.0){uE(u|0,0,40);c[v>>2]=2;c[w>>2]=2;h[x>>3]=A;c[y>>2]=c[z+176>>2];fN(j,f,g,123984);h[6458]=+h[z+184>>3];h[6457]=+h[z+192>>3];c[12910]=~~+h[f>>3]}fm(q,C,D,G,c[z+160>>2]|0)}z=c[z>>2]|0;}while((z|0)!=0)}z=e;c[z>>2]=c[14084];c[z+4>>2]=c[56340>>2];c[z+8>>2]=c[56344>>2];c[z+12>>2]=c[56348>>2];z=c[14075]|0;do{if((c[14074]|0)!=0){E=+h[7040];f=c[(c[3524]|0)+92>>2]|0;if(E<0.0){cK[f&63](+h[3817]);break}else{cK[f&63](E);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);f=c[(c[3524]|0)+64>>2]|0;if((z|0)<-5){cM[f&511](-2)}else{cM[f&511](z)}f=c[3524]|0;do{if((a[56328]&1)==0){if((c[f+96>>2]&1024|0)==0){c[e>>2]=1;c[e+4>>2]=z;break}else{c[13542]=p;i=d;return}}}while(0);fn(e,f);c[13542]=p;i=d;return}function fW(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0.0,K=0.0,L=0,M=0,N=0,O=0,P=0.0,Q=0,R=0,S=0.0,T=0,U=0;f=i;i=i+176|0;g=f|0;j=f+8|0;k=f+16|0;l=f+24|0;m=f+32|0;n=f+40|0;o=f+48|0;p=f+56|0;q=f+64|0;r=f+72|0;s=f+80|0;t=f+88|0;u=f+96|0;v=f+104|0;w=f+112|0;x=f+120|0;y=f+128|0;z=f+136|0;A=f+144|0;B=f+152|0;C=f+160|0;D=c[3524]|0;E=(c[8996]|0)+(c[9334]|0)|0;F=c[9337]|0;do{if(b){G=D+108|0;if((c[G>>2]|0)==0){break}H=c[9336]|0;if((a[36288]|0)==0){I=H}else{do{if((c[D+96>>2]&32|0)==0){J=0.0}else{K=(a8(36288,94)|0)==0?0.0:.51;if((a8(36288,95)|0)==0){J=K;break}J=K+.3}}while(0);I=~~(+(H|0)-J*+((c[D+16>>2]|0)>>>0>>>0))}cM[c[D+144>>2]&511](56688);L=c[9334]|0;cI[c[G>>2]&63](1601,L,I,(c[9335]|0)-L|0,(c[9337]|0)-I|0)}}while(0);if((a[36288]|0)==0){M=F;N=c[D+16>>2]|0}else{I=((c[9335]|0)+(c[9334]|0)|0)/2&-1;L=D+96|0;O=c[L>>2]|0;if((O&32|0)==0){P=0.0}else{P=(a8(36288,94)|0)==0?0.0:.51}if(b){Q=10476}else{if((a[36228]&1)==0){Q=10476}else{R=O}}if((Q|0)==10476){if((c[9330]|0)==3&+h[4666]<0.0){fn(36272,D)}else{fn(37320,D)}ln(I,~~(+(F|0)-(P*.5+.5)*+((c[D+16>>2]|0)>>>0>>>0)),36288,1,0,0,c[9329]|0);cM[c[D+64>>2]&511](-2);R=c[L>>2]|0}do{if((R&32|0)==0){S=P}else{if((a8(36288,95)|0)==0){S=P;break}S=P+.3}}while(0);P=S+ +h[4488];h[4488]=P;R=D+16|0;c[9336]=~~(+(c[9336]|0)-S*+((c[R>>2]|0)>>>0>>>0));L=c[R>>2]|0;M=~~(+(F|0)- +(L>>>0>>>0)*P);N=L}L=D+16|0;D=(M-~~(+h[4527]*.5*+(N>>>0>>>0))|0)+((c[9020]|0)/-2&-1)|0;c[54]=D;N=c[9059]|0;if((N|0)<=-3){c[d>>2]=E;c[e>>2]=D;i=f;return}M=c[13542]|0;F=c[3524]|0;c[13542]=(c[F+96>>2]&128|0)==0?55520:0;R=C;c[R>>2]=c[9068];c[R+4>>2]=c[36276>>2];c[R+8>>2]=c[36280>>2];c[R+12>>2]=c[36284>>2];do{if((c[9058]|0)!=0){P=+h[4532];R=c[F+92>>2]|0;if(P<0.0){cK[R&63](+h[3817]);break}else{cK[R&63](P);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[4531]);F=c[(c[3524]|0)+64>>2]|0;if((N|0)<-5){cM[F&511](-2)}else{cM[F&511](N)}F=c[3524]|0;do{if((a[36264]&1)==0){if((c[F+96>>2]&1024|0)!=0){T=F;break}c[C>>2]=1;c[C+4>>2]=N;Q=10495}else{Q=10495}}while(0);if((Q|0)==10495){fn(C,F);T=c[3524]|0}F=c[T+172>>2]|0;if((F|0)==0){U=T}else{cM[F&511](0);U=c[3524]|0}F=c[9334]|0;T=c[9336]|0;C=c[9337]|0;c[y>>2]=F;c[z>>2]=T;c[A>>2]=F;c[B>>2]=C;if((fl(y,z,A,B)|0)!=0){cN[c[U+56>>2]&255](c[y>>2]|0,c[z>>2]|0);cN[c[U+60>>2]&255](c[A>>2]|0,c[B>>2]|0)}B=c[9337]|0;A=c[9335]|0;c[u>>2]=c[9334];c[v>>2]=B;c[w>>2]=A;c[x>>2]=B;B=c[3524]|0;if((fl(u,v,w,x)|0)!=0){cN[c[B+56>>2]&255](c[u>>2]|0,c[v>>2]|0);cN[c[B+60>>2]&255](c[w>>2]|0,c[x>>2]|0)}x=c[9335]|0;w=c[9337]|0;B=c[9336]|0;c[q>>2]=x;c[r>>2]=w;c[s>>2]=x;c[t>>2]=B;B=c[3524]|0;if((fl(q,r,s,t)|0)!=0){cN[c[B+56>>2]&255](c[q>>2]|0,c[r>>2]|0);cN[c[B+60>>2]&255](c[s>>2]|0,c[t>>2]|0)}t=c[9336]|0;s=c[9334]|0;c[m>>2]=c[9335];c[n>>2]=t;c[o>>2]=s;c[p>>2]=t;t=c[3524]|0;if((fl(m,n,o,p)|0)!=0){cN[c[t+56>>2]&255](c[m>>2]|0,c[n>>2]|0);cN[c[t+60>>2]&255](c[o>>2]|0,c[p>>2]|0)}p=c[(c[3524]|0)+172>>2]|0;if((p|0)!=0){cM[p&511](1)}do{if((a[36288]|0)!=0){p=~~(+(c[9337]|0)- +h[4488]*+((c[L>>2]|0)>>>0>>>0));o=c[9335]|0;c[g>>2]=c[9334];c[j>>2]=p;c[k>>2]=o;c[l>>2]=p;p=c[3524]|0;if((fl(g,j,k,l)|0)==0){break}cN[c[p+56>>2]&255](c[g>>2]|0,c[j>>2]|0);cN[c[p+60>>2]&255](c[k>>2]|0,c[l>>2]|0)}}while(0);c[13542]=M;c[d>>2]=E;c[e>>2]=D;i=f;return}function fX(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0;d=i;i=i+48|0;e=d|0;f=d+8|0;g=d+16|0;j=d+32|0;k=c[3524]|0;l=b+300|0;if((c[l>>2]|0)<=0){i=d;return}m=b+316|0;n=b+320|0;o=k+56|0;p=k+60|0;k=g|0;q=j|0;r=g+8|0;g=j+8|0;j=0;s=2;do{f9(b,(c[m>>2]|0)+(j<<3)|0);t=c[n>>2]|0;u=c[t+(j<<6)>>2]|0;do{if((u|0)==0){v=c[200]|0;w=~~(+(c[64768+(v*688&-1)>>2]|0)+(+h[t+(j<<6)+8>>3]- +h[64664+(v*688&-1)>>3])*+h[64776+(v*688&-1)>>3]+.5);v=c[144]|0;x=~~(+(c[64768+(v*688&-1)>>2]|0)+(+h[t+(j<<6)+16>>3]- +h[64664+(v*688&-1)>>3])*+h[64776+(v*688&-1)>>3]+.5);if((s|0)==0){cN[c[p>>2]&255](w,x);break}else if((s|0)==1){if((a[54160]&1)==0){cN[c[o>>2]&255](w,x);break}else{gb(t,j,e,f);v=c[200]|0;y=c[144]|0;cN[c[o>>2]&255](~~(+(c[64768+(v*688&-1)>>2]|0)+(+h[e>>3]- +h[64664+(v*688&-1)>>3])*+h[64776+(v*688&-1)>>3]+.5),~~(+(c[64768+(y*688&-1)>>2]|0)+(+h[f>>3]- +h[64664+(y*688&-1)>>3])*+h[64776+(y*688&-1)>>3]+.5));cN[c[p>>2]&255](w,x);break}}else{cN[c[o>>2]&255](w,x);cN[c[p>>2]&255](w,x);break}}else if((u|0)==1){if((s|0)==0){if((a[54160]&1)==0){break}gb(t,j,e,f);x=c[200]|0;w=c[144]|0;cN[c[p>>2]&255](~~(+(c[64768+(x*688&-1)>>2]|0)+(+h[e>>3]- +h[64664+(x*688&-1)>>3])*+h[64776+(x*688&-1)>>3]+.5),~~(+(c[64768+(w*688&-1)>>2]|0)+(+h[f>>3]- +h[64664+(w*688&-1)>>3])*+h[64776+(w*688&-1)>>3]+.5));break}else if((s|0)!=1){break}if((a[54152]&1)==0){break}if(!(gc(t,j,k,q)|0)){break}w=c[200]|0;x=c[144]|0;cN[c[o>>2]&255](~~(+(c[64768+(w*688&-1)>>2]|0)+(+h[k>>3]- +h[64664+(w*688&-1)>>3])*+h[64776+(w*688&-1)>>3]+.5),~~(+(c[64768+(x*688&-1)>>2]|0)+(+h[q>>3]- +h[64664+(x*688&-1)>>3])*+h[64776+(x*688&-1)>>3]+.5));x=c[200]|0;w=c[144]|0;cN[c[p>>2]&255](~~(+(c[64768+(x*688&-1)>>2]|0)+(+h[r>>3]- +h[64664+(x*688&-1)>>3])*+h[64776+(x*688&-1)>>3]+.5),~~(+(c[64768+(w*688&-1)>>2]|0)+(+h[g>>3]- +h[64664+(w*688&-1)>>3])*+h[64776+(w*688&-1)>>3]+.5))}}while(0);s=c[(c[n>>2]|0)+(j<<6)>>2]|0;j=j+1|0;}while((j|0)<(c[l>>2]|0));i=d;return}function fY(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0.0,an=0;j=i;i=i+280|0;k=j|0;l=j+8|0;m=j+16|0;n=j+24|0;o=j+32|0;p=j+40|0;q=j+48|0;r=j+56|0;s=j+64|0;t=j+72|0;u=j+80|0;w=j+88|0;x=j+96|0;y=j+104|0;z=j+112|0;A=j+152|0;B=j+160|0;C=j+168|0;D=j+176|0;E=j+184|0;F=j+192|0;G=j+200|0;H=j+208|0;I=j+216|0;J=j+224|0;K=j+232|0;L=j+240|0;M=j+248|0;N=j+256|0;O=j+264|0;P=j+272|0;Q=c[13542]|0;c[13542]=(c[(c[3524]|0)+96>>2]&128|0)==0?55520:0;R=e+168|0;cM[c[R>>2]&511](8);S=c[9330]|0;if((S|0)==0){cM[c[e+64>>2]&511](-2)}else if((S|0)!=7){fn(37320,e)}L14405:do{if((c[9046]|0)==0){ln((c[8988]|0)+f|0,g,d,0,0,0,c[9329]|0)}else{S=(cO[c[e+76>>2]&255](2)|0)==0;T=(c[8984]|0)+f|0;if(!S){ln(T,g,d,2,0,0,c[9329]|0);break}S=c[e+20>>2]|0;U=T-aa(lp(d)|0,S)|0;do{if(((c[9031]|0)-1|0)>>>0>=2){S=c[7938]|0;T=c[7939]|0;if((S|0)<(T|0)){if((U|0)<(S|0)|(U|0)>(T|0)){break L14405}else{break}}else{if((U|0)<(T|0)|(U|0)>(S|0)){break L14405}else{break}}}}while(0);ln(U,g,d,0,0,0,c[9329]|0)}}while(0);d=e+64|0;S=b+28|0;cM[c[d>>2]&511](c[S>>2]|0);T=b+56|0;if((a[T]&1)!=0){fn(b+64|0,e)}V=b+12|0;W=c[V>>2]|0;do{if((W&8|0)==0){X=10585}else{Y=c[e+108>>2]|0;if((Y|0)==0){X=10585;break}Z=b+192|0;_=Z|0;$=c[_>>2]|0;if(($|0)==1|($|0)==4){ab=(c[b+196>>2]<<4&65520)+$|0}else if(($|0)==2|($|0)==5){ab=(c[b+200>>2]<<4&65520)+$|0}else{ab=0}$=c[9008]|0;ac=$+f|0;ad=c[9020]|0;ae=(ad|0)/4&-1;af=g-ae|0;ag=c[9004]|0;ah=ag-$|0;ai=(ad|0)/2&-1;ad=(ag|0)==($|0);if(!((W|0)!=457|ad)){lq((c[9016]|0)+f|0,g,+(ae|0),0.0,360.0,ab);break}if(!((W|0)!=489|ad)){ae=ut(96)|0;do{if((ae|0)==0){gk();$=ut(96)|0;if(($|0)!=0){aj=$;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=151888,v)|0)}else{aj=ae}}while(0);h[aj+24>>3]=+((c[9016]|0)+f|0);h[aj+32>>3]=+(g|0);h[aj+64>>3]=+((ah<<1>>>0)/3>>>0>>>0>>>0);h[aj+72>>3]=+(ai>>>0>>>0);h[aj+88>>3]=0.0;fP(2,aj,ab,0);uu(aj);break}if(ad){break}ae=(ab|0)==0;if(ae){ak=W}else{cI[Y&63](ab,ac,af,ah,ai);ak=c[V>>2]|0}do{if((ak|0)==252){if((c[b+208>>2]|0)!=1){X=10566;break}if((c[b+212>>2]|0)==-3|ae){X=10568}else{X=10567}}else{X=10566}}while(0);if((X|0)==10566){if(ae){X=10568}else{X=10567}}if((X|0)==10567){if(hG(Z)|0){X=10568}}do{if((X|0)==10568){ai=c[3524]|0;ah=c[ai+172>>2]|0;if((ah|0)==0){al=ai}else{cM[ah&511](0);al=c[3524]|0}ah=g-((c[9020]|0)/4&-1)|0;ai=(c[9004]|0)+f|0;c[M>>2]=(c[9008]|0)+f;c[N>>2]=ah;c[O>>2]=ai;c[P>>2]=ah;if((fl(M,N,O,P)|0)!=0){cN[c[al+56>>2]&255](c[M>>2]|0,c[N>>2]|0);cN[c[al+60>>2]&255](c[O>>2]|0,c[P>>2]|0)}ah=(c[9004]|0)+f|0;ai=(c[9020]|0)/4&-1;c[I>>2]=ah;c[J>>2]=g-ai;c[K>>2]=ah;c[L>>2]=ai+g;ai=c[3524]|0;if((fl(I,J,K,L)|0)!=0){cN[c[ai+56>>2]&255](c[I>>2]|0,c[J>>2]|0);cN[c[ai+60>>2]&255](c[K>>2]|0,c[L>>2]|0)}ai=((c[9020]|0)/4&-1)+g|0;ah=(c[9008]|0)+f|0;c[E>>2]=(c[9004]|0)+f;c[F>>2]=ai;c[G>>2]=ah;c[H>>2]=ai;ai=c[3524]|0;if((fl(E,F,G,H)|0)!=0){cN[c[ai+56>>2]&255](c[E>>2]|0,c[F>>2]|0);cN[c[ai+60>>2]&255](c[G>>2]|0,c[H>>2]|0)}ai=(c[9008]|0)+f|0;ah=(c[9020]|0)/4&-1;c[A>>2]=ai;c[B>>2]=ah+g;c[C>>2]=ai;c[D>>2]=g-ah;ah=c[3524]|0;if((fl(A,B,C,D)|0)!=0){cN[c[ah+56>>2]&255](c[A>>2]|0,c[B>>2]|0);cN[c[ah+60>>2]&255](c[C>>2]|0,c[D>>2]|0)}ah=c[(c[3524]|0)+172>>2]|0;if((ah|0)==0){break}cM[ah&511](1)}}while(0);Z=c[_>>2]|0;if((Z|0)==0|(Z|0)==3){break}if((c[b+208>>2]|0)==1){if((c[b+212>>2]|0)==-3){break}}cM[c[d>>2]&511](c[S>>2]|0);if((a[T]&1)==0){break}fn(b+64|0,e)}}while(0);L14473:do{if((X|0)==10585){do{if((W|0)==225){if((c[e+84>>2]|0)==0){break}c[12912]=c[b+184>>2];c[12910]=0;am=+h[b+152>>3];if(am>0.0){uE(z|0,0,40);c[z+4>>2]=2;c[z+8>>2]=2;h[z+16>>3]=am;c[z>>2]=c[b+160>>2];fN(z,x,y,123984);h[6458]=+h[b+168>>3];h[6457]=+h[b+176>>3];c[12910]=~~+h[x>>3]}c[12910]=-1;fm((c[9008]|0)+f|0,g,(c[9004]|0)+f|0,g,c[b+144>>2]|0);break L14473}else{if((W&1|0)!=0){break}if((W&4|0)==0){break L14473}if((c[b+8>>2]|0)!=1){break L14473}}}while(0);_=(c[9004]|0)+f|0;c[s>>2]=(c[9008]|0)+f;c[t>>2]=g;c[u>>2]=_;c[w>>2]=g;_=c[3524]|0;if((fl(s,t,u,w)|0)==0){break}cN[c[_+56>>2]&255](c[s>>2]|0,c[t>>2]|0);cN[c[_+60>>2]&255](c[u>>2]|0,c[w>>2]|0)}}while(0);if((c[b+8>>2]|0)!=1){an=c[R>>2]|0;cM[an&511](9);c[13542]=Q;i=j;return}b=c[V>>2]|0;if((b&4|0)==0){an=c[R>>2]|0;cM[an&511](9);c[13542]=Q;i=j;return}if(!((b|0)!=252&+h[7077]>0.0)){an=c[R>>2]|0;cM[an&511](9);c[13542]=Q;i=j;return}b=(c[9008]|0)+f|0;V=e+28|0;e=c[V>>2]|0;w=e>>>0>3?e>>>1:1;c[o>>2]=b;c[p>>2]=w+g;c[q>>2]=b;c[r>>2]=g-w;w=c[3524]|0;if((fl(o,p,q,r)|0)!=0){cN[c[w+56>>2]&255](c[o>>2]|0,c[p>>2]|0);cN[c[w+60>>2]&255](c[q>>2]|0,c[r>>2]|0)}r=(c[9004]|0)+f|0;f=c[V>>2]|0;V=f>>>0>3?f>>>1:1;c[k>>2]=r;c[l>>2]=V+g;c[m>>2]=r;c[n>>2]=g-V;V=c[3524]|0;if((fl(k,l,m,n)|0)==0){an=c[R>>2]|0;cM[an&511](9);c[13542]=Q;i=j;return}cN[c[V+56>>2]&255](c[k>>2]|0,c[l>>2]|0);cN[c[V+60>>2]&255](c[m>>2]|0,c[n>>2]|0);an=c[R>>2]|0;cM[an&511](9);c[13542]=Q;i=j;return}function fZ(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0.0;d=i;i=i+16|0;e=d|0;f=c[b+36>>2]|0;g=c[3524]|0;j=b+300|0;k=c[j>>2]|0;if((k|0)<=0){i=d;return}l=b+12|0;m=(f|0)==0;n=b+320|0;o=(f|0)<0;p=g+144|0;q=g+92|0;r=g+80|0;g=e;s=b+64|0;t=b+28|0;u=b+24|0;v=b+40|0;w=b+56|0;x=e|0;y=e+4|0;z=b+48|0;A=b+316|0;B=b+32|0;C=0;D=k;while(1){k=c[l>>2]|0;if((k|0)!=51|m){E=10612}else{if(((C|0)%(f|0)&-1|0)==0){E=10612}else{F=D}}do{if((E|0)==10612){E=0;G=c[n>>2]|0;if((c[G+(C<<6)>>2]|0)!=0){F=D;break}H=c[200]|0;I=~~(+(c[64768+(H*688&-1)>>2]|0)+(+h[G+(C<<6)+8>>3]- +h[64664+(H*688&-1)>>3])*+h[64776+(H*688&-1)>>3]+.5);H=c[144]|0;J=~~(+(c[64768+(H*688&-1)>>2]|0)+(+h[G+(C<<6)+16>>3]- +h[64664+(H*688&-1)>>3])*+h[64776+(H*688&-1)>>3]+.5);if((a[54144]&1)!=0){H=c[8240]|0;if((I|0)<(H+(c[7938]|0)|0)){F=D;break}K=c[8242]|0;if((J|0)<(K+(c[7940]|0)|0)){F=D;break}if((I|0)>((c[7939]|0)-H|0)){F=D;break}if((J|0)>((c[7941]|0)-K|0)){F=D;break}}do{if((k|0)==18|(k|0)==51){if(+h[z>>3]==-3.0){cK[c[q>>2]&63](+h[3817]*+h[G+(C<<6)+24>>3]);L=c[l>>2]|0}else{L=k}if(!((L|0)==51&o)){break}K=c[p>>2]|0;if((K|0)!=0){cM[K&511](56688)}cK[c[q>>2]&63](+h[3817]*+h[3818]);cR[c[r>>2]&127](I,J,6);c[g>>2]=c[s>>2];c[g+4>>2]=c[s+4>>2];c[g+8>>2]=c[s+8>>2];c[g+12>>2]=c[s+12>>2];K=c[t>>2]|0;do{if((c[u>>2]|0)!=0){M=+h[z>>3];H=c[(c[3524]|0)+92>>2]|0;if(M<0.0){cK[H&63](+h[3817]);break}else{cK[H&63](M);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[v>>3]);H=c[(c[3524]|0)+64>>2]|0;if((K|0)<-5){cM[H&511](-2)}else{cM[H&511](K)}H=c[3524]|0;if((a[w]&1)==0){if((c[H+96>>2]&1024|0)!=0){break}c[x>>2]=1;c[y>>2]=K}fn(e,H)}}while(0);G=(c[A>>2]|0)+(C<<3)|0;f9(b,G);cR[c[r>>2]&127](I,J,c[B>>2]|0);F=c[j>>2]|0}}while(0);k=C+1|0;if((k|0)<(F|0)){C=k;D=F}else{break}}i=d;return}function f_(b){b=b|0;var d=0,e=0,f=0.0,g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0,u=0,v=0.0,w=0.0,x=0,y=0,z=0,A=0,B=0,C=0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0,P=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0.0,_=0,$=0,ab=0.0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0.0,an=0,ao=0,ap=0.0,aq=0,ar=0,as=0,at=0,au=0,av=0.0,aw=0,ax=0,ay=0,az=0.0,aA=0,aB=0,aC=0,aD=0,aE=0.0;d=c[3524]|0;e=c[d+28>>2]|0;if(e>>>0>3){f=+(e>>>1|0)}else{f=1.0}e=b+12|0;g=c[e>>2]|0;do{if((g|0)==102|(g|0)==118|(g|0)==169|(g|0)==295|(g|0)==311|(g|0)==392|(g|0)==345){i=b+300|0;if((c[i>>2]|0)<=0){j=g;break}k=b+320|0;l=b+276|0;m=b+280|0;n=b+316|0;o=d+108|0;p=b+192|0;q=d+56|0;r=d+60|0;s=0.0;t=0;while(1){u=c[k>>2]|0;L14555:do{if((c[u+(t<<6)>>2]|0)==2){v=s}else{w=+h[u+(t<<6)+8>>3];x=c[e>>2]|0;y=(x|0)==392;if(y){z=c[l>>2]|0;A=c[9671]|0;B=A+(c[z+8>>2]|0)|0;C=aa(B-1|0,t-1|0);D=+(B|0);E=(+h[u+(t<<6)+56>>3]- +h[u+(t<<6)+48>>3])/(D*2.0);F=(+((A|0)/2&-1|0)+(w+ +(C+(c[m>>2]|0)|0)))/D+(+h[z+16>>3]+.5)}else{E=s;F=w}z=c[200]|0;w=+h[64664+(z*688&-1)>>3];D=+h[64672+(z*688&-1)>>3];C=w<D;if(C){if(F<w|F>D){v=E;break}}else{if(F<D|F>w){v=E;break}}G=+(c[64768+(z*688&-1)>>2]|0);H=+h[64776+(z*688&-1)>>3];z=~~(G+(F-w)*H+.5);I=+h[u+(t<<6)+16>>3];A=c[144]|0;J=+h[64664+(A*688&-1)>>3];K=+h[64672+(A*688&-1)>>3];B=J<K;if(B){if(I<J|I>K){v=E;break}}else{if(I<K|I>J){v=E;break}}L=+(c[64768+(A*688&-1)>>2]|0);M=+h[64776+(A*688&-1)>>3];A=~~(L+(I-J)*M+.5);I=+h[u+(t<<6)+40>>3];N=+h[u+(t<<6)+32>>3];do{if(B){if(I<J){O=0}else{O=I<=K}if(N<J){P=O;R=10663;break}S=N<=K;if(O){T=S;R=10665}else{U=S;R=10666}}else{if(I<K){V=0}else{V=I<=J}if(N<K){P=V;R=10663;break}S=N<=J;if(V){T=S;R=10665}else{U=S;R=10666}}}while(0);do{if((R|0)==10663){R=0;if(!P){U=0;R=10666;break}W=0;X=~~(L+(I-J)*M+.5);R=10670}else if((R|0)==10665){R=0;B=~~(L+(I-J)*M+.5);if(T){Y=B;R=10669}else{W=0;X=B;R=10670}}}while(0);do{if((R|0)==10666){R=0;Z=K-J;if((I-K)*Z<0.0){B=~~(L+(J-J)*M+.5);if(U){Y=B;R=10669;break}else{W=1;X=B;R=10670;break}}else{B=~~(L+Z*M+.5);if(U){Y=B;R=10669;break}else{W=1;X=B;R=10670;break}}}}while(0);if((R|0)==10669){R=0;_=~~(L+(N-J)*M+.5);$=Y}else if((R|0)==10670){R=0;I=K-J;if((N-K)*I<0.0){ab=J-J}else{ab=I}B=~~(L+ab*M+.5);if((B|0)==(X|0)&W){v=E;break}else{_=B;$=X}}I=+h[u+(t<<6)+56>>3];Z=+h[u+(t<<6)+48>>3];do{if(y){ac=~~(G+(F-E-w)*H+.5);ad=~~(G+(F+E-w)*H+.5)}else{do{if(C){if(I<w){ae=0}else{ae=I<=D}if(Z<w){af=ae;R=10684;break}B=Z<=D;if(ae){ag=B;R=10686}else{ah=B;R=10687}}else{if(I<D){ai=0}else{ai=I<=w}if(Z<D){af=ai;R=10684;break}B=Z<=w;if(ai){ag=B;R=10686}else{ah=B;R=10687}}}while(0);do{if((R|0)==10684){R=0;if(!af){ah=0;R=10687;break}aj=0;ak=~~(G+(I-w)*H+.5);R=10691}else if((R|0)==10686){R=0;B=~~(G+(I-w)*H+.5);if(ag){al=B;R=10690}else{aj=0;ak=B;R=10691}}}while(0);do{if((R|0)==10687){R=0;am=D-w;if((I-D)*am<0.0){B=~~(G+(w-w)*H+.5);if(ah){al=B;R=10690;break}else{aj=1;ak=B;R=10691;break}}else{B=~~(G+am*H+.5);if(ah){al=B;R=10690;break}else{aj=1;ak=B;R=10691;break}}}}while(0);if((R|0)==10690){R=0;an=al;ao=~~(G+(Z-w)*H+.5)}else if((R|0)==10691){R=0;am=D-w;if((Z-D)*am<0.0){ap=w-w}else{ap=am}B=~~(G+ap*H+.5);if((B|0)==(ak|0)&aj){v=E;break L14555}else{an=ak;ao=B}}if((x|0)==392|(x|0)==345){ac=ao;ad=an;break}B=(c[n>>2]|0)+(t<<3)|0;f9(b,B);if((c[e>>2]|0)!=169){ac=ao;ad=an;break}if((c[o>>2]|0)==0){ac=ao;ad=an;break}hG(p);ac=ao;ad=an}}while(0);if((a[30528]&1)==0){cN[c[q>>2]&255](z,_);cN[c[r>>2]&255](z,$);H=+h[7077];if(H<0.0){cN[c[q>>2]&255](ac,_);cN[c[r>>2]&255](ad,_);cN[c[q>>2]&255](ac,$);cN[c[r>>2]&255](ad,$);v=E;break}if(H<=0.0){v=E;break}G=+(z>>>0>>>0);cN[c[q>>2]&255](~~(G-f*H),_);cN[c[r>>2]&255](~~(G+f*+h[7077]),_);cN[c[q>>2]&255](~~(G-f*+h[7077]),$);cN[c[r>>2]&255](~~(G+f*+h[7077]),$);v=E;break}x=ad-ac|0;C=aa(x,x);x=$-_|0;G=+((C+aa(x,x)|0)>>>0>>>0);H=+h[3817];if(G<=f*H*f*H*4.5){v=E;break}x=c[q>>2]|0;if((ac|0)==(ad|0)){cN[x&255](z,_);H=+(A>>>0>>>0);cN[c[r>>2]&255](z,~~(H-f*+h[3817]));cN[c[q>>2]&255](z,~~(H+f*+h[3817]));cN[c[r>>2]&255](z,$)}else{cN[x&255](ac,_);cN[c[r>>2]&255](ad,$)}H=+h[7077];if(H<=0.0){v=E;break}G=+(ac>>>0>>>0);w=+(ad>>>0>>>0);D=+($>>>0>>>0);Z=+(_>>>0>>>0);I=(G-w)/(D-Z+1.0e-10);M=+Q(+(I*I+1.0));L=f*H/M;H=G+L;J=G-L;cN[c[q>>2]&255](~~H,~~(Z+I*(H-G)));cN[c[r>>2]&255](~~J,~~(Z+I*(J-G)));G=f*+h[7077]/M;M=w+G;J=w-G;cN[c[q>>2]&255](~~M,~~(D+I*(M-w)));cN[c[r>>2]&255](~~J,~~(D+I*(J-w)));v=E}}while(0);u=t+1|0;if((u|0)<(c[i>>2]|0)){s=v;t=u}else{break}}j=c[e>>2]|0}else{j=g}}while(0);if(!((j|0)==86|(j|0)==118|(j|0)==279|(j|0)==311)){return}j=b+300|0;if((c[j>>2]|0)<=0){return}g=b+320|0;e=b+316|0;_=d+56|0;$=d+60|0;d=0;do{ad=c[g>>2]|0;do{if((c[ad+(d<<6)>>2]|0)!=2){v=+h[ad+(d<<6)+16>>3];ac=c[144]|0;E=+h[64664+(ac*688&-1)>>3];ap=+h[64672+(ac*688&-1)>>3];if(E<ap){if(v<E|v>ap){break}}else{if(v<ap|v>E){break}}an=~~(+(c[64768+(ac*688&-1)>>2]|0)+(v-E)*+h[64776+(ac*688&-1)>>3]+.5);E=+h[ad+(d<<6)+56>>3];v=+h[ad+(d<<6)+48>>3];ac=c[200]|0;ap=+h[64664+(ac*688&-1)>>3];F=+h[64672+(ac*688&-1)>>3];do{if(ap<F){if(E<ap){aq=0}else{aq=E<=F}if(v<ap){ar=aq;R=10727;break}ao=v<=F;if(aq){as=ao;R=10728}else{at=ao;R=10729}}else{if(E<F){au=0}else{au=E<=ap}if(v<F){ar=au;R=10727;break}ao=v<=ap;if(au){as=ao;R=10728}else{at=ao;R=10729}}}while(0);if((R|0)==10727){R=0;if(ar){as=0;R=10728}else{at=0;R=10729}}do{if((R|0)==10728){R=0;ao=c[64768+(ac*688&-1)>>2]|0;ab=+h[64776+(ac*688&-1)>>3];ak=~~(+(ao|0)+(E-ap)*ab+.5);if(as){av=ab;aw=ao;ax=ak;R=10732}else{ay=0;az=ab;aA=ao;aB=ak;R=10733}}else if((R|0)==10729){R=0;ab=F-ap;ak=c[64768+(ac*688&-1)>>2]|0;s=+(ak|0);if((E-F)*ab<0.0){w=+h[64776+(ac*688&-1)>>3];ao=~~(s+(ap-ap)*w+.5);if(at){av=w;aw=ak;ax=ao;R=10732;break}else{ay=1;az=w;aA=ak;aB=ao;R=10733;break}}else{w=+h[64776+(ac*688&-1)>>3];ao=~~(s+ab*w+.5);if(at){av=w;aw=ak;ax=ao;R=10732;break}else{ay=1;az=w;aA=ak;aB=ao;R=10733;break}}}}while(0);if((R|0)==10732){R=0;aC=~~(+(aw|0)+(v-ap)*av+.5);aD=ax}else if((R|0)==10733){R=0;E=F-ap;if((v-F)*E<0.0){aE=ap-ap}else{aE=E}ac=~~(+(aA|0)+aE*az+.5);if((ac|0)==(aB|0)&ay){break}else{aC=ac;aD=aB}}ac=(c[e>>2]|0)+(d<<3)|0;f9(b,ac);cN[c[_>>2]&255](aC,an);cN[c[$>>2]&255](aD,an);E=+h[7077];if(E<=0.0){break}w=+(an>>>0>>>0);cN[c[_>>2]&255](aC,~~(w-f*E));cN[c[$>>2]&255](aC,~~(w+f*+h[7077]));cN[c[_>>2]&255](aD,~~(w-f*+h[7077]));cN[c[$>>2]&255](aD,~~(w+f*+h[7077]))}}while(0);d=d+1|0;}while((d|0)<(c[j>>2]|0));return}function f$(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0.0,V=0,W=0,X=0,Y=0.0,Z=0.0,_=0,$=0.0,ab=0.0,ac=0.0,ad=0.0,ae=0,af=0.0,ag=0.0,ah=0.0,ai=0,aj=0.0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0.0,ar=0.0,as=0.0,at=0.0,au=0.0,av=0.0,aw=0.0,ax=0.0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0.0,aL=0.0,aM=0.0,aN=0.0,aO=0.0,aP=0.0,aQ=0.0,aR=0.0,aS=0.0,aT=0,aU=0.0,aV=0,aW=0,aX=0;e=i;i=i+112|0;f=e|0;g=e+56|0;j=c[3524]|0;k=b+12|0;l=b+300|0;do{if((c[k>>2]|0)==392){m=c[l>>2]|0;n=c[9670]|0;do{if((n|0)==1){if((c[b+280>>2]|0)!=0){break}c[3572]=0}else if((n|0)==2){c[3572]=0}}while(0);n=c[3568]|0;if((n|0)!=0){if((c[3572]|0)>=(m|0)){break}o=db(n,m<<6,91376)|0;c[3568]=o;n=c[3572]|0;if((n|0)<(m|0)){p=n;while(1){n=p+1|0;uE(o+(p<<6)+32|0,0,16);if((n|0)<(m|0)){p=n}else{break}}}c[3572]=m;break}p=m<<6;o=ut(p)|0;do{if((o|0)==0){gk();n=ut(p)|0;if((n|0)!=0){q=n;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=91376,v)|0)}else{q=o}}while(0);o=q;c[3568]=o;if((m|0)>0){p=0;while(1){n=p+1|0;uE(o+(p<<6)+32|0,0,16);if((n|0)<(m|0)){p=n}else{break}}}c[3572]=m}}while(0);q=c[l>>2]|0;if((q|0)<=0){i=e;return}p=b+320|0;o=b+276|0;n=g|0;r=g+4|0;s=g+40|0;t=g+44|0;u=g+8|0;w=g+40|0;x=g;y=g+32|0;g=b+200|0;z=f|0;A=f+4|0;B=f+40|0;C=f+44|0;D=f+8|0;E=f;F=f+32|0;G=f+40|0;f=b+280|0;H=b+192|0;I=H|0;J=j+56|0;K=j+60|0;L=j+108|0;M=b+208|0;N=j+64|0;O=b+28|0;P=b+56|0;Q=b+64|0;R=b+196|0;S=b+316|0;T=2;U=0.0;V=0;W=d;d=c[p>>2]|0;X=q;while(1){L14718:do{if((c[d+(V<<6)>>2]|0)>>>0<2){if(+h[d+(V<<6)+24>>3]<0.0){q=(T|0)==2;Y=+h[7030];do{if(q){if(Y<=0.0){Z=0.0;break}if((a[56232]&1)==0){Z=0.0;break}Z=Y*-.5}else{if(Y<0.0){Z=(+h[d+(V-1<<6)+8>>3]- +h[d+(V<<6)+8>>3])*.5;break}if((a[56232]&1)==0){Z=Y*(+h[d+(V-1<<6)+8>>3]- +h[d+(V<<6)+8>>3])*.5;break}else{Z=Y*-.5;break}}}while(0);do{if((V|0)<(X-1|0)){_=V+1|0;if((c[d+(_<<6)>>2]|0)==2){$=-0.0-Z;break}if(Y<0.0){$=(+h[d+(_<<6)+8>>3]- +h[d+(V<<6)+8>>3])*.5;break}if((a[56232]&1)==0){$=Y*(+h[d+(_<<6)+8>>3]- +h[d+(V<<6)+8>>3])*.5;break}else{$=Y*.5;break}}else{$=-0.0-Z}}while(0);if(q){ab=-0.0-$}else{ab=Z}Y=+h[d+(V<<6)+8>>3];ac=ab+Y;ad=$+Y}else{ac=+h[d+(V<<6)+48>>3];ad=+h[d+(V<<6)+56>>3]}_=c[k>>2]|0;if((_|0)==137){Y=+h[d+(V<<6)+32>>3];ae=c[144]|0;af=+h[64664+(ae*688&-1)>>3];ag=+h[64672+(ae*688&-1)>>3];do{if(af<ag){if(Y>ag){ah=ag;break}if(Y>=af){ah=Y;break}ah=af}else{if(Y>af){ah=af;break}if(Y>=ag){ah=Y;break}ah=ag}}while(0);ai=~~(+(c[64768+(ae*688&-1)>>2]|0)+(ah-af)*+h[64776+(ae*688&-1)>>3]+.5);aj=ah;ak=d+(V<<6)+40|0}else{ai=W;aj=U;ak=d+(V<<6)+16|0}ag=+h[ak>>3];do{if((_|0)==392){q=c[o>>2]|0;al=c[q+32>>2]|0;am=((al|0)>0?al:0)+V|0;al=c[9670]|0;if((al|0)==3|(al|0)==4){an=c[9671]|0;ao=an+(c[q+8>>2]|0)|0;ap=aa(ao-1|0,~~+h[d+(V<<6)+8>>3]-1|0);Y=+(ap+(c[f>>2]|0)|0);aq=+((an|0)/2&-1|0);ar=+(ao|0);as=+h[q+16>>3]+.5;at=(aq+(ac+Y))/ar+as;au=aj;av=(aq+(ad+Y))/ar+as;aw=ag;break}else if((al|0)==2){as=+h[q+16>>3];ar=+h[7030]*.5;Y=+(c[f>>2]|0);aq=as-ar+Y;ax=as+ar+Y;if((a[30080]&1)==0){ao=c[n>>2]|0;an=c[8798]|0;ap=(an|0)>0;ay=am;L14771:while(1){az=43264;while(1){aA=c[az>>2]|0;if((aA|0)==0){break}if((c[aA+4>>2]|0)==(ay|0)){aB=10821;break L14771}else{az=aA|0}}aC=ay-1|0;if(!((ay|0)>(an|0)&ap)){aB=10827;break}ay=((aC|0)%(an|0)&-1)+1|0}do{if((aB|0)==10821){aB=0;an=aA+8|0;c[x>>2]=c[an>>2];c[x+4>>2]=c[an+4>>2];c[x+8>>2]=c[an+8>>2];c[x+12>>2]=c[an+12>>2];c[x+16>>2]=c[an+16>>2];c[x+20>>2]=c[an+20>>2];c[x+24>>2]=c[an+24>>2];c[x+28>>2]=c[an+28>>2];c[x+32>>2]=c[an+32>>2];c[x+36>>2]=c[an+36>>2];c[x+40>>2]=c[an+40>>2];c[x+44>>2]=c[an+44>>2];c[x+48>>2]=c[an+48>>2];c[x+52>>2]=c[an+52>>2];c[n>>2]=ao;an=c[3524]|0;if((c[an+96>>2]&1024|0)!=0){c[r>>2]=ay;a[y]=0;aD=an;break}if((a[y]&1)!=0){aD=an;break}c[s>>2]=1;c[t>>2]=c[r>>2];aD=an}else if((aB|0)==10827){aB=0;c[r>>2]=aC;c[s>>2]=1;c[t>>2]=aC;c[u>>2]=aC;aD=c[3524]|0}}while(0);fn(w,aD)}else{ay=am+1|0;ao=c[z>>2]|0;an=43280;while(1){aE=c[an>>2]|0;if((aE|0)==0){aB=10805;break}if((c[aE+4>>2]|0)==(ay|0)){aB=10803;break}else{an=aE|0}}do{if((aB|0)==10803){aB=0;an=aE+8|0;c[E>>2]=c[an>>2];c[E+4>>2]=c[an+4>>2];c[E+8>>2]=c[an+8>>2];c[E+12>>2]=c[an+12>>2];c[E+16>>2]=c[an+16>>2];c[E+20>>2]=c[an+20>>2];c[E+24>>2]=c[an+24>>2];c[E+28>>2]=c[an+28>>2];c[E+32>>2]=c[an+32>>2];c[E+36>>2]=c[an+36>>2];c[E+40>>2]=c[an+40>>2];c[E+44>>2]=c[an+44>>2];c[E+48>>2]=c[an+48>>2];c[E+52>>2]=c[an+52>>2];c[z>>2]=ao;if((a[F]&1)!=0){break}c[B>>2]=1;c[C>>2]=c[A>>2]}else if((aB|0)==10805){aB=0;an=c[8798]|0;ap=(an|0)>0;az=ay;L14794:while(1){aF=43264;while(1){aG=c[aF>>2]|0;if((aG|0)==0){break}if((c[aG+4>>2]|0)==(az|0)){break L14794}else{aF=aG|0}}aH=az-1|0;if(!((az|0)>(an|0)&ap)){aB=10815;break}az=((aH|0)%(an|0)&-1)+1|0}if((aB|0)==10815){aB=0;c[A>>2]=aH;c[B>>2]=1;c[C>>2]=aH;c[D>>2]=aH;break}an=aG+8|0;c[E>>2]=c[an>>2];c[E+4>>2]=c[an+4>>2];c[E+8>>2]=c[an+8>>2];c[E+12>>2]=c[an+12>>2];c[E+16>>2]=c[an+16>>2];c[E+20>>2]=c[an+20>>2];c[E+24>>2]=c[an+24>>2];c[E+28>>2]=c[an+28>>2];c[E+32>>2]=c[an+32>>2];c[E+36>>2]=c[an+36>>2];c[E+40>>2]=c[an+40>>2];c[E+44>>2]=c[an+44>>2];c[E+48>>2]=c[an+48>>2];c[E+52>>2]=c[an+52>>2];c[z>>2]=ao;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[A>>2]=az;a[F]=0;break}if((a[F]&1)!=0){break}c[B>>2]=1;c[C>>2]=c[A>>2]}}while(0);fn(G,c[3524]|0)}c[g>>2]=am;aI=0;aJ=c[p>>2]|0;aK=ax;aL=aq}else if((al|0)==1){Y=+h[q+16>>3];aI=V;aJ=d;aK=ad+Y;aL=ac+Y}else{at=ac;au=aj;av=ad;aw=ag;break}Y=+h[aJ+(V<<6)+16>>3];ao=c[3568]|0;if(Y<0.0){ay=ao+(aI<<6)+32|0;ar=+h[ay>>3];h[ay>>3]=Y+ar;aM=ar}else{ay=ao+(aI<<6)+40|0;ar=+h[ay>>3];h[ay>>3]=Y+ar;aM=ar}ar=ag+aM;ay=c[144]|0;Y=+h[64664+(ay*688&-1)>>3];as=+h[64672+(ay*688&-1)>>3];ay=Y<as;if(ay&aM<Y){aB=10835}else{if(as<Y&aM>Y){aB=10835}else{aN=aM}}if((aB|0)==10835){aB=0;aN=Y}if(!(ay&aN>as)){if(!(as<Y&aN<as)){at=aL;au=aN;av=aK;aw=ar;break}}at=aL;au=as;av=aK;aw=ar}else{at=ac;au=aj;av=ad;aw=ag}}while(0);_=c[144]|0;ag=+h[64664+(_*688&-1)>>3];af=+h[64672+(_*688&-1)>>3];do{if(ag<af){if(aw>af){aO=af;break}if(aw>=ag){aO=aw;break}aO=ag}else{if(aw>ag){aO=ag;break}if(aw>=af){aO=aw;break}aO=af}}while(0);ae=c[200]|0;ar=+h[64664+(ae*688&-1)>>3];as=+h[64672+(ae*688&-1)>>3];do{if(ar<as){do{if(av>as){aP=as}else{if(av>=ar){aP=av;break}aP=ar}}while(0);if(at>as){aQ=as;aR=aP;break}if(at>=ar){aQ=at;aR=aP;break}aQ=ar;aR=aP}else{do{if(av>ar){aS=ar}else{if(av>=as){aS=av;break}aS=as}}while(0);if(at>ar){aQ=ar;aR=aS;break}if(at>=as){aQ=at;aR=aS;break}aQ=as;aR=aS}}while(0);if(aR==aQ){if(aR==ar|aR==as){aT=ai;aU=au;break}}aq=+(c[64768+(ae*688&-1)>>2]|0);ax=+h[64776+(ae*688&-1)>>3];q=~~(aq+(aQ-ar)*ax+.5);al=~~(aq+(aR-ar)*ax+.5);ax=+(c[64768+(_*688&-1)>>2]|0);aq=+h[64776+(_*688&-1)>>3];am=~~(ax+(aO-ag)*aq+.5);if((ai|0)==(am|0)){if(aO==ag|aO==af){aT=ai;aU=au;break}}ay=c[k>>2]|0;do{if((ay|0)==153|(ay|0)==137|(ay|0)==169){ao=(c[S>>2]|0)+(V<<3)|0;f9(b,ao);aV=ai}else if((ay|0)==392){if(((c[9670]|0)-1|0)>>>0>=2){aV=ai;break}aV=~~(ax+(au-ag)*aq+.5)}else{aV=ai}}while(0);ay=c[I>>2]|0;do{if((ay|0)!=0){_=c[L>>2]|0;if((_|0)==0){break}ae=al-q|0;ao=am-aV|0;an=(ae|0)<0;ap=an?al:q;aF=(an?q-al|0:ae)+1|0;ae=(ao|0)<0;an=ae?am:aV;aW=(ae?aV-am|0:ao)+1|0;if((ay|0)==1|(ay|0)==4){aX=(c[R>>2]<<4&65520)+ay|0}else if((ay|0)==2|(ay|0)==5){aX=(c[g>>2]<<4&65520)+ay|0}else{aX=0}cI[_&63](aX,ap,an,aF,aW);if(!(hG(H)|0)){aT=ai;aU=au;break L14718}}}while(0);ay=c[(c[3524]|0)+172>>2]|0;if((ay|0)!=0){cM[ay&511](0)}cN[c[J>>2]&255](q,aV);cN[c[K>>2]&255](q,am);cN[c[K>>2]&255](al,am);cN[c[K>>2]&255](al,aV);cN[c[K>>2]&255](q,aV);ay=c[(c[3524]|0)+172>>2]|0;if((ay|0)!=0){cM[ay&511](1)}if((c[L>>2]|0)==0){aT=ai;aU=au;break}if((c[M>>2]|0)==0){aT=ai;aU=au;break}cM[c[N>>2]&511](c[O>>2]|0);if((a[P]&1)==0){aT=ai;aU=au;break}fn(Q,j);aT=ai;aU=au}else{aT=W;aU=U}}while(0);m=c[p>>2]|0;ay=V+1|0;aW=c[l>>2]|0;if((ay|0)<(aW|0)){T=c[m+(V<<6)>>2]|0;U=aU;V=ay;W=aT;d=m;X=aW}else{break}}i=e;return}function f0(b){b=b|0;var d=0,e=0,f=0,g=0,i=0,j=0.0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0;if((c[(c[3524]|0)+148>>2]|0)==0){f_(b);return}d=b+300|0;if(((c[d>>2]|0)-1|0)<=0){return}e=b+320|0;f=0;while(1){g=c[e>>2]|0;i=f+1|0;do{if((c[g+(f<<6)>>2]|0)!=2){if((c[g+(i<<6)>>2]|0)==2){break}j=+h[g+(f<<6)+8>>3];k=+h[g+(f<<6)+56>>3];l=+h[g+(f<<6)+16>>3];m=+h[g+(f<<6)+40>>3];n=+h[g+(i<<6)+8>>3];o=+h[g+(i<<6)+56>>3];p=+h[g+(i<<6)+16>>3];q=+h[g+(i<<6)+40>>3];if((a[30528]&1)==0){r=m-l;if(r*(q-p)<0.0){s=p-q;t=(n*r+j*s)/(r+s);s=m+(q-m)*(t-j)/(n-j);gh(j,k,l,m,t,t,s,s,b);gh(t,t,s,s,n,o,p,q,b);break}else{gh(j,k,l,m,n,o,p,q,b);break}}else{s=(p-l)/(n-j);t=(q-m)/(o-k);r=l-j*s;u=(m-k*t-r)/(s-t);t=r+s*u;if((j-u)*(u-n)>0.0){gh(j,k,l,m,u,u,t,t,b);gh(u,u,t,t,n,o,p,q,b);break}else{gh(j,k,l,m,n,o,p,q,b);break}}}}while(0);if((i|0)<((c[d>>2]|0)-1|0)){f=i}else{break}}return}function f1(a){a=a|0;var b=0,d=0,e=0,f=0.0,g=0,i=0,j=0,k=0,l=0,m=0.0,n=0,o=0.0,p=0.0,q=0,r=0.0,s=0.0,t=0.0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0.0,C=0,D=0.0,E=0,F=0,G=0,H=0.0,I=0,J=0,K=0.0,L=0.0,M=0.0,N=0,O=0;b=c[3524]|0;d=c[b+28>>2]|0;e=d>>>0<8;if(e){f=e?1.0:0.0}else{f=+(d>>>2|0)}d=a+300|0;if((c[d>>2]|0)<=0){return}e=a+320|0;g=a+316|0;i=b+56|0;j=b+60|0;b=a+12|0;k=0;do{l=c[e>>2]|0;do{if((c[l+(k<<6)>>2]|0)!=2){m=+h[l+(k<<6)+8>>3];n=c[200]|0;o=+h[64664+(n*688&-1)>>3];p=+h[64672+(n*688&-1)>>3];if(o<p){if(m<o|m>p){break}}else{if(m<p|m>o){break}}q=~~(+(c[64768+(n*688&-1)>>2]|0)+(m-o)*+h[64776+(n*688&-1)>>3]+.5);o=+h[l+(k<<6)+40>>3];m=+h[l+(k<<6)+32>>3];p=+h[l+(k<<6)+24>>3];r=+h[l+(k<<6)+16>>3];n=c[144]|0;s=+h[64664+(n*688&-1)>>3];t=+h[64672+(n*688&-1)>>3];do{if(s<t){if(o<s){u=0}else{u=o<=t}if(m<s){v=u;w=10920;break}x=m<=t;if(u){y=x;w=10921}else{z=x;w=10922}}else{if(o<t){A=0}else{A=o<=s}if(m<t){v=A;w=10920;break}x=m<=s;if(A){y=x;w=10921}else{z=x;w=10922}}}while(0);if((w|0)==10920){w=0;if(v){y=0;w=10921}else{z=0;w=10922}}do{if((w|0)==10921){w=0;x=c[64768+(n*688&-1)>>2]|0;B=+h[64776+(n*688&-1)>>3];C=~~(+(x|0)+(o-s)*B+.5);if(y){D=B;E=x;F=C;w=10925}else{G=0;H=B;I=x;J=C;w=10926}}else if((w|0)==10922){w=0;B=t-s;C=c[64768+(n*688&-1)>>2]|0;K=+(C|0);if((o-t)*B<0.0){L=+h[64776+(n*688&-1)>>3];x=~~(K+(s-s)*L+.5);if(z){D=L;E=C;F=x;w=10925;break}else{G=1;H=L;I=C;J=x;w=10926;break}}else{L=+h[64776+(n*688&-1)>>3];x=~~(K+B*L+.5);if(z){D=L;E=C;F=x;w=10925;break}else{G=1;H=L;I=C;J=x;w=10926;break}}}}while(0);if((w|0)==10926){w=0;o=t-s;if((m-t)*o<0.0){M=s-s}else{M=o}n=~~(+(I|0)+M*H+.5);if((n|0)==(J|0)&G){break}else{N=n;O=J}}else if((w|0)==10925){w=0;N=~~(+(E|0)+(m-s)*D+.5);O=F}n=(c[g>>2]|0)+(k<<3)|0;f9(a,n);cN[c[i>>2]&255](q,N);cN[c[j>>2]&255](q,O);o=+(q>>>0>>>0);n=c[144]|0;cN[c[i>>2]&255](~~(o-f*+h[7077]),~~(+(c[64768+(n*688&-1)>>2]|0)+(r- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]+.5));n=c[144]|0;cN[c[j>>2]&255](q,~~(+(c[64768+(n*688&-1)>>2]|0)+(r- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]+.5));n=c[144]|0;cN[c[i>>2]&255](~~(o+f*+h[7077]),~~(+(c[64768+(n*688&-1)>>2]|0)+(p- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]+.5));n=c[144]|0;cN[c[j>>2]&255](q,~~(+(c[64768+(n*688&-1)>>2]|0)+(p- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]+.5));if((c[b>>2]|0)!=474){break}n=c[144]|0;x=~~(+(c[64768+(n*688&-1)>>2]|0)+(+h[(c[e>>2]|0)+(k<<6)+56>>3]- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]+.5);cN[c[i>>2]&255](~~(o-f*+h[7077]),x);cN[c[j>>2]&255](~~(o+f*+h[7077]),x)}}while(0);k=k+1|0;}while((k|0)<(c[d>>2]|0));return}function f2(b){b=b|0;var d=0,e=0,f=0,g=0.0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0.0,I=0.0,J=0.0,K=0,L=0.0,M=0.0,N=0.0,O=0.0,P=0.0,Q=0.0,R=0,S=0.0,T=0.0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0.0,ab=0,ac=0,ad=0.0,ae=0,af=0,ag=0,ah=0.0,ai=0,aj=0,ak=0.0,al=0.0,am=0.0,an=0,ao=0,ap=0,aq=0.0,ar=0,as=0,at=0.0,au=0.0,av=0.0,aw=0.0,ax=0.0,ay=0.0,az=0.0,aA=0.0,aB=0.0,aC=0.0,aD=0.0,aE=0.0,aF=0.0,aG=0.0,aH=0,aI=0,aJ=0,aK=0,aL=0.0,aM=0.0,aN=0,aO=0,aP=0.0,aQ=0,aR=0,aS=0.0,aT=0,aU=0,aV=0,aW=0;d=c[3524]|0;e=c[d+28>>2]|0;f=e>>>0<8;if(f){g=f?1.0:0.0}else{g=+(e>>>2|0)}e=b+300|0;f=c[e>>2]|0;if((f|0)<=0){return}i=b+320|0;j=b+192|0;k=b+208|0;l=d+64|0;m=b+28|0;n=b+56|0;o=b+64|0;p=b+316|0;q=d+56|0;r=d+60|0;s=b+12|0;t=b+144|0;u=b+152|0;v=j|0;w=d+108|0;x=b+196|0;y=b+200|0;z=b+212|0;A=2;B=0;C=c[i>>2]|0;D=f;while(1){do{if((c[C+(B<<6)>>2]|0)==2){E=A;F=C;G=D}else{H=+h[C+(B<<6)+8>>3];f=c[200]|0;I=+h[64664+(f*688&-1)>>3];J=+h[64672+(f*688&-1)>>3];K=I<J;if(K){if(H<I|H>J){E=A;F=C;G=D;break}}else{if(H<J|H>I){E=A;F=C;G=D;break}}L=+(c[64768+(f*688&-1)>>2]|0);M=+h[64776+(f*688&-1)>>3];f=~~(L+(H-I)*M+.5);N=+h[C+(B<<6)+40>>3];O=+h[C+(B<<6)+32>>3];P=+h[C+(B<<6)+24>>3];Q=+h[C+(B<<6)+16>>3];R=N<O;S=R?O:N;T=R?N:O;R=c[144]|0;O=+h[64664+(R*688&-1)>>3];N=+h[64672+(R*688&-1)>>3];U=O<N;do{if(U){if(S<O){V=0}else{V=S<=N}if(T<O){W=V;X=10952;break}Y=T<=N;if(V){Z=Y;X=10953}else{_=Y;X=10954}}else{if(S<N){$=0}else{$=S<=O}if(T<N){W=$;X=10952;break}Y=T<=O;if($){Z=Y;X=10953}else{_=Y;X=10954}}}while(0);if((X|0)==10952){X=0;if(W){Z=0;X=10953}else{_=0;X=10954}}do{if((X|0)==10953){X=0;Y=c[64768+(R*688&-1)>>2]|0;aa=+h[64776+(R*688&-1)>>3];ab=~~(+(Y|0)+(S-O)*aa+.5);if(Z){ac=1;ad=aa;ae=Y;af=ab;X=10957}else{ag=1;ah=aa;ai=Y;aj=ab;X=10958}}else if((X|0)==10954){X=0;aa=N-O;ab=c[64768+(R*688&-1)>>2]|0;ak=+(ab|0);if((S-N)*aa<0.0){al=+h[64776+(R*688&-1)>>3];Y=~~(ak+(O-O)*al+.5);if(_){ac=0;ad=al;ae=ab;af=Y;X=10957;break}else{ag=0;ah=al;ai=ab;aj=Y;X=10958;break}}else{al=+h[64776+(R*688&-1)>>3];Y=~~(ak+aa*al+.5);if(_){ac=0;ad=al;ae=ab;af=Y;X=10957;break}else{ag=0;ah=al;ai=ab;aj=Y;X=10958;break}}}}while(0);if((X|0)==10958){X=0;S=N-O;if((T-N)*S<0.0){am=O-O}else{am=S}R=~~(+(ai|0)+am*ah+.5);if((R|0)==(aj|0)&(ag^1)){E=A;F=C;G=D;break}else{an=R;ao=0;ap=ag;aq=ah;ar=ai;as=aj}}else if((X|0)==10957){X=0;an=~~(+(ae|0)+(T-O)*ad+.5);ao=1;ap=ac;aq=ad;ar=ae;as=af}S=+h[C+(B<<6)+48>>3];do{if(S!=H){al=H*2.0-S;do{if(K){do{if(al>J){at=J}else{if(al>=I){at=al;break}at=I}}while(0);if(S>J){au=J;av=at;break}if(S>=I){au=S;av=at;break}au=I;av=at}else{do{if(al>I){aw=I}else{if(al>=J){aw=al;break}aw=J}}while(0);if(S>I){au=I;av=aw;break}if(S>=J){au=S;av=aw;break}au=J;av=aw}}while(0);ax=L+(au-I)*M+.5;ay=L+(av-I)*M+.5}else{if((c[s>>2]|0)==474){al=+h[7030];if((a[56232]&1)!=0&al>0.0){az=al*.5}else{az=.25}ax=L+(H-az-I)*M+.5;ay=L+(H+az-I)*M+.5;break}al=+h[7030];if(al<0.0){aa=+(f|0);ak=g*+h[7077];ax=aa-ak;ay=aa+ak;break}ak=al*-.5;R=(A|0)==2;do{if(R){aA=ak}else{if((a[56232]&1)!=0){aA=ak;break}aA=al*(+h[C+(B-1<<6)+8>>3]-H)*.5}}while(0);ak=-0.0-aA;do{if((B|0)<(D-1|0)){Y=B+1|0;if((c[C+(Y<<6)>>2]|0)==2){aB=ak;break}if((a[56232]&1)==0){aB=(+h[C+(Y<<6)+8>>3]-H)*al*.5;break}else{aB=al*.5;break}}else{aB=ak}}while(0);if(R){aC=-0.0-aB}else{aC=aA}ak=H+aC;al=H+aB;do{if(K){do{if(al>J){aD=J}else{if(al>=I){aD=al;break}aD=I}}while(0);if(ak>J){aE=J;aF=aD;break}if(ak>=I){aE=ak;aF=aD;break}aE=I;aF=aD}else{do{if(al>I){aG=I}else{if(al>=J){aG=al;break}aG=J}}while(0);if(ak>I){aE=I;aF=aG;break}if(ak>=J){aE=ak;aF=aG;break}aE=J;aF=aG}}while(0);ax=L+(aE-I)*M+.5;ay=L+(aF-I)*M+.5}}while(0);K=~~ax;R=~~ay;do{if((R-K&1|0)==0){aH=R;aI=f}else{Y=R+1|0;ab=(((f-K|0)>(Y-f|0))<<31>>31)+f|0;if((ab-K|0)>=(Y-ab|0)){aH=Y;aI=ab;break}aH=Y;aI=ab+1|0}}while(0);do{if(U){if(Q<O){aJ=0}else{aJ=Q<=N}if(P<O){aK=0}else{aK=P<=N}do{if(Q>N){aL=N}else{if(Q>=O){aL=Q;break}aL=O}}while(0);if(P>N){aM=N;aN=aK;aO=aJ;aP=aL;break}if(P>=O){aM=P;aN=aK;aO=aJ;aP=aL;break}aM=O;aN=aK;aO=aJ;aP=aL}else{if(Q<N){aQ=0}else{aQ=Q<=O}if(P<N){aR=0}else{aR=P<=O}do{if(Q>O){aS=O}else{if(Q>=N){aS=Q;break}aS=N}}while(0);if(P>O){aM=O;aN=aR;aO=aQ;aP=aS;break}if(P>=N){aM=P;aN=aR;aO=aQ;aP=aS;break}aM=N;aN=aR;aO=aQ;aP=aS}}while(0);N=+(ar|0);U=~~(N+(aP-O)*aq+.5);f=~~(N+(aM-O)*aq+.5);R=(U|0)<(f|0);ab=R?U:f;Y=R?f:U;U=(ab|0)==(Y|0)&((aO|aN)^1);f=c[k>>2]|0;if((f|0)==1){if((c[z>>2]|0)!=-3){X=11032}}else if((f|0)!=0){X=11032}do{if((X|0)==11032){X=0;cM[c[l>>2]&511](c[m>>2]|0);if((a[n]&1)==0){break}fn(o,d)}}while(0);f=(c[p>>2]|0)+(B<<3)|0;f9(b,f);do{if(!((c[(c[3524]|0)+108>>2]|0)==0|U)){f=c[v>>2]|0;if((f|0)==2|(f|0)==5){aT=(c[y>>2]<<4&65520)+f|0}else if((f|0)==1|(f|0)==4){aT=(c[x>>2]<<4&65520)+f|0}else{aT=0}if(!((aT|0)!=0|aP>aM)){break}if((aT|0)==0){aU=(c[s>>2]|0)==474?0:1601}else{aU=aT}cI[c[w>>2]&63](aU,K,ab,aH-K|0,Y-ab|0);f=c[v>>2]|0;if((f|0)==2|(f|0)==5){aV=y}else if((f|0)==1|(f|0)==4){aV=x}else{break}if((c[aV>>2]<<4&65520|0)==(-f|0)){break}hG(j)}}while(0);cN[c[q>>2]&255](aI,an);cN[c[r>>2]&255](aI,ab);cN[c[q>>2]&255](aI,Y);cN[c[r>>2]&255](aI,as);do{if(!U){f=c[(c[3524]|0)+172>>2]|0;if((f|0)!=0){cM[f&511](0)}f=c[144]|0;cN[c[q>>2]&255](K,~~(+(c[64768+(f*688&-1)>>2]|0)+(aP- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5));f=c[144]|0;cN[c[r>>2]&255](aH,~~(+(c[64768+(f*688&-1)>>2]|0)+(aP- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5));f=c[144]|0;cN[c[r>>2]&255](aH,~~(+(c[64768+(f*688&-1)>>2]|0)+(aM- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5));f=c[144]|0;cN[c[r>>2]&255](K,~~(+(c[64768+(f*688&-1)>>2]|0)+(aM- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5));f=c[144]|0;cN[c[r>>2]&255](K,~~(+(c[64768+(f*688&-1)>>2]|0)+(aP- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5));f=c[(c[3524]|0)+172>>2]|0;if((f|0)==0){break}cM[f&511](1)}}while(0);do{if((c[s>>2]|0)==474){O=+h[7077];if(O<0.0){aW=0;X=11056;break}aW=~~(+(aH-K|0)*.5-O*+((c[(c[3524]|0)+28>>2]|0)>>>0>>>0));X=11056}else{if((c[t>>2]|0)!=3){break}O=+h[u>>3];if(O<=0.0){aW=0;X=11056;break}aW=~~(+(aH-K|0)*(1.0-O)*.5);X=11056}}while(0);do{if((X|0)==11056){X=0;if(ap){cN[c[q>>2]&255](aW+K|0,as);cN[c[r>>2]&255](aH-aW|0,as)}if(!ao){break}cN[c[q>>2]&255](aW+K|0,an);cN[c[r>>2]&255](aH-aW|0,an)}}while(0);if((c[s>>2]|0)==474){U=c[144]|0;f=~~(+(c[64768+(U*688&-1)>>2]|0)+(+h[(c[i>>2]|0)+(B<<6)+56>>3]- +h[64664+(U*688&-1)>>3])*+h[64776+(U*688&-1)>>3]+.5);cN[c[q>>2]&255](K,f);cN[c[r>>2]&255](aH,f)}do{if(aP>aM){if((c[(c[3524]|0)+108>>2]|0)!=0){break}cN[c[q>>2]&255](aI,ab);cN[c[r>>2]&255](aI,Y);f=(aI+K|0)/2&-1;cN[c[q>>2]&255](f,ab);cN[c[r>>2]&255](f,Y);f=(aH+aI|0)/2&-1;cN[c[q>>2]&255](f,ab);cN[c[r>>2]&255](f,Y)}}while(0);Y=c[i>>2]|0;E=c[Y+(B<<6)>>2]|0;F=Y;G=c[e>>2]|0}}while(0);Y=B+1|0;if((Y|0)<(G|0)){A=E;B=Y;C=F;D=G}else{break}}return}function f3(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,Q=0.0,R=0.0,S=0.0,T=0.0,U=0.0,V=0.0,W=0.0,X=0.0,Y=0,$=0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0,ai=0.0,aj=0,ak=0.0,al=0,am=0,an=0.0,ao=0.0,ap=0,aq=0.0,ar=0.0,as=0.0,at=0.0,au=0,av=0,aw=0.0,ax=0.0,ay=0,az=0.0,aA=0.0,aB=0.0,aC=0,aD=0.0,aE=0.0,aF=0.0,aG=0.0,aH=0.0,aI=0.0,aJ=0.0,aK=0.0,aL=0.0,aM=0.0,aN=0.0,aO=0,aP=0,aQ=0.0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0.0,a5=0.0,a6=0.0,a7=0.0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0.0,bq=0.0,br=0.0,bs=0.0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0.0,bA=0.0,bB=0.0,bC=0.0,bD=0.0,bE=0.0,bF=0.0,bG=0.0,bH=0.0,bI=0.0,bJ=0.0,bK=0.0,bL=0.0,bM=0.0,bN=0.0,bO=0.0,bP=0.0,bQ=0.0,bR=0.0,bS=0.0,bT=0.0,bU=0.0,bV=0.0,bW=0.0,bX=0.0,bY=0.0,bZ=0.0,b_=0,b$=0,b0=0,b1=0.0,b2=0.0,b3=0,b4=0,b5=0.0,b6=0.0,b7=0,b8=0.0,b9=0,ca=0.0,cb=0;e=i;i=i+256|0;f=e|0;g=e+16|0;j=e+32|0;k=e+48|0;l=e+96|0;m=e+192|0;uE(j|0,-1|0,16);n=((c[b+8>>2]|0)-2|0)>>>0<2;if(n){o=c[b+260>>2]|0;p=o+12|0;q=o+8|0}else{p=b+320|0;q=b+300|0}o=c[p>>2]|0;p=c[q>>2]|0;q=c[b+228>>2]|0;if((p|0)<1){uh(-1,207376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=e;return}if((p|0)<4){uh(-1,203152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=e;return}r=c[200]|0;s=a[64788+(r*688&-1)|0]|0;t=(s&1)==0;L15142:do{if(n){do{if(t){u=c[144]|0;if((a[64788+(u*688&-1)|0]&1)!=0){break}if((a[64788+((c[34]|0)*688&-1)|0]&1)!=0){break}w=+h[64664+(r*688&-1)>>3];x=+h[91];y=+h[96];z=(+h[o+8>>3]-w)*x+y+-1.0;A=+h[64664+(u*688&-1)>>3];B=+h[21];C=+h[68];D=(+h[o+16>>3]-A)*B+C+-1.0;E=+h[5279];F=+h[2];G=+h[12];H=(+h[o+24>>3]-E)*F+G+-1.0;I=+h[403];J=+h[391];K=+h[395];L=+h[399];M=+h[404];N=+h[392];O=+h[396];Q=+h[400];R=+h[406];S=+h[394];T=+h[398];U=+h[402];V=R+z*S+D*T+H*U;W=V==0.0?1.0e-5:V;V=+(c[180]|0);u=c[186]|0;X=+(c[40]|0);Y=c[46]|0;$=p-1|0;ab=y+x*(+h[o+($<<6)+8>>3]-w)+-1.0;w=C+B*(+h[o+($<<6)+16>>3]-A)+-1.0;A=G+F*(+h[o+($<<6)+24>>3]-E)+-1.0;E=R+S*ab+T*w+U*A;U=E==0.0?1.0e-5:E;ac=+(~~(V*((I+J*ab+K*w+L*A)/U))+u|0);ad=+(~~(X*((M+N*ab+O*w+Q*A)/U))+Y|0);ae=+(~~((I+z*J+D*K+H*L)/W*V)+u|0);af=+(~~((M+z*N+D*O+H*Q)/W*X)+Y|0);break L15142}}while(0);uh(-1,198096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=e;return}else{do{if(t){if((a[64788+((c[144]|0)*688&-1)|0]&1)!=0){break}Y=p-1|0;ac=+h[o+(Y<<6)+8>>3];ad=+h[o+(Y<<6)+16>>3];ae=+h[o+8>>3];af=+h[o+16>>3];break L15142}}while(0);Y=c[b+304>>2]|0;u=(a[64788+(Y*688&-1)|0]&1)==0;X=+h[o+8>>3];if(u){ag=X}else{ag=+Z(+(X*+h[64800+(Y*688&-1)>>3]))}$=c[b+308>>2]|0;ah=(a[64788+($*688&-1)|0]&1)==0;X=+h[o+16>>3];if(ah){ai=X}else{ai=+Z(+(X*+h[64800+($*688&-1)>>3]))}aj=p-1|0;X=+h[o+(aj<<6)+8>>3];if(u){ak=X}else{ak=+Z(+(X*+h[64800+(Y*688&-1)>>3]))}X=+h[o+(aj<<6)+16>>3];if(ah){ac=ak;ad=X;ae=ag;af=ai;break}ac=ak;ad=+Z(+(X*+h[64800+($*688&-1)>>3]));ae=ag;af=ai}}while(0);ai=af-ad;ag=ac-ae;L15166:do{if((p|0)>1){t=64664+(r*688&-1)|0;ak=+h[91];X=+h[96];$=c[144]|0;ah=64664+($*688&-1)|0;W=+h[21];Q=+h[68];H=+h[5279];O=+h[2];D=+h[12];N=+h[403];z=+h[391];M=+h[395];V=+h[399];L=+h[404];K=+h[392];J=+h[396];I=+h[400];U=+h[406];A=+h[394];w=+h[398];ab=+h[402];E=+(c[180]|0);aj=c[186]|0;T=+(c[40]|0);Y=c[46]|0;u=64788+($*688&-1)|0;$=b+304|0;al=b+308|0;S=ag;R=ai;F=-0.0-(ae*ai+af*ag);am=1;L15168:while(1){L15170:do{if(n){G=(+h[o+(am<<6)+8>>3]- +h[t>>3])*ak+X+-1.0;B=(+h[o+(am<<6)+16>>3]- +h[ah>>3])*W+Q+-1.0;C=(+h[o+(am<<6)+24>>3]-H)*O+D+-1.0;x=U+G*A+B*w+C*ab;y=x==0.0?1.0e-5:x;an=+(~~((N+G*z+B*M+C*V)/y*E)+aj|0);ao=+(~~((L+G*K+B*J+C*I)/y*T)+Y|0)}else{do{if((s&1)==0){if((a[u]&1)!=0){break}an=+h[o+(am<<6)+8>>3];ao=+h[o+(am<<6)+16>>3];break L15170}}while(0);ap=c[$>>2]|0;y=+h[o+(am<<6)+8>>3];if((a[64788+(ap*688&-1)|0]&1)==0){aq=y}else{aq=+Z(+(y*+h[64800+(ap*688&-1)>>3]))}ap=c[al>>2]|0;y=+h[o+(am<<6)+16>>3];if((a[64788+(ap*688&-1)|0]&1)==0){an=aq;ao=y;break}an=aq;ao=+Z(+(y*+h[64800+(ap*688&-1)>>3]))}}while(0);ap=F+(S*ao+R*an)<0.0;do{if((am|0)==1){if(!ap){ar=F;as=R;at=S;break}ar=-0.0-F;as=-0.0-R;at=-0.0-S}else{if(ap){break L15168}else{ar=F;as=R;at=S}}}while(0);ap=am+1|0;if((ap|0)<(p|0)){S=at;R=as;F=ar;am=ap}else{au=11111;break L15166}}if((am|0)==(p|0)){au=11111}else{av=am}}else{au=11111}}while(0);if((au|0)==11111){uh(-1,186832,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);av=p}s=(p|0)/(av|0)&-1;if(+(s|0)!=+(p|0)/+(av|0)){uh(-1,181552,(v=i,i=i+16|0,c[v>>2]=p,c[v+8>>2]=av,v)|0);i=e;return}c[j>>2]=0;r=av-1|0;c[j+4>>2]=r;c[j+12>>2]=p-1;al=p-av|0;c[j+8>>2]=al;L15195:do{if(n){$=c[200]|0;ar=(+h[o+(r<<6)+8>>3]- +h[64664+($*688&-1)>>3])*+h[91]+ +h[96]+-1.0;as=(+h[o+(r<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;at=(+h[o+(r<<6)+24>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;an=+h[406]+ar*+h[394]+as*+h[398]+at*+h[402];ao=an==0.0?1.0e-5:an;aw=+(~~((+h[403]+ar*+h[391]+as*+h[395]+at*+h[399])/ao*+(c[180]|0))+(c[186]|0)|0);ax=+(~~((+h[404]+ar*+h[392]+as*+h[396]+at*+h[400])/ao*+(c[40]|0))+(c[46]|0)|0);ay=$}else{$=c[200]|0;do{if((a[64788+($*688&-1)|0]&1)==0){if((a[64788+((c[144]|0)*688&-1)|0]&1)!=0){break}aw=+h[o+(r<<6)+8>>3];ax=+h[o+(r<<6)+16>>3];ay=$;break L15195}}while(0);am=c[b+304>>2]|0;ao=+h[o+(r<<6)+8>>3];if((a[64788+(am*688&-1)|0]&1)==0){az=ao}else{az=+Z(+(ao*+h[64800+(am*688&-1)>>3]))}am=c[b+308>>2]|0;ao=+h[o+(r<<6)+16>>3];if((a[64788+(am*688&-1)|0]&1)==0){aw=az;ax=ao;ay=$;break}aw=az;ax=+Z(+(ao*+h[64800+(am*688&-1)>>3]));ay=$}}while(0);az=+(r|0);ao=(aw-ae)/az;ae=(ax-af)/az;am=s-1|0;af=+(am|0);at=(ac-aw)/af;aw=(ad-ax)/af;if(d){ax=+(r<<1|0);ad=+(am<<1|0);am=b+304|0;d=b+308|0;u=b+23|0;Y=0;do{do{if((a[64788+(ay*688&-1)|0]&1)==0){if((a[64788+((c[144]|0)*688&-1)|0]&1)!=0){au=11127;break}aj=c[j+(Y<<2)>>2]|0;ac=+h[o+(aj<<6)+8>>3];as=+h[o+(aj<<6)+16>>3];aj=c[j+(((5-Y|0)%4&-1)<<2)>>2]|0;ah=c[j+(((Y+2|0)%4&-1)<<2)>>2]|0;aA=ac-(+h[o+(aj<<6)+8>>3]-ac)/ax-(+h[o+(ah<<6)+8>>3]-ac)/ad;aB=as-(+h[o+(aj<<6)+16>>3]-as)/ax-(+h[o+(ah<<6)+16>>3]-as)/ad;aC=c[am>>2]|0}else{au=11127}}while(0);if((au|0)==11127){au=0;$=c[am>>2]|0;ah=(a[64788+($*688&-1)|0]&1)==0;as=+h[o+(Y<<6)+8>>3];if(ah){aD=as}else{aD=+Z(+(as*+h[64800+($*688&-1)>>3]))}aj=c[d>>2]|0;t=(a[64788+(aj*688&-1)|0]&1)==0;ac=+h[o+(Y<<6)+16>>3];if(t){aE=ac}else{aE=+Z(+(ac*+h[64800+(aj*688&-1)>>3]))}ap=(5-Y|0)%4&-1;ar=+h[o+(ap<<6)+8>>3];if(ah){aF=as;aG=ar}else{an=+h[64800+($*688&-1)>>3];aq=+Z(+(ar*an));aF=+Z(+(as*an));aG=aq}aq=+h[o+(ap<<6)+16>>3];if(t){aH=ac;aI=aq}else{an=+h[64800+(aj*688&-1)>>3];ar=+Z(+(aq*an));aH=+Z(+(ac*an));aI=ar}ap=(Y+2|0)%4&-1;ar=+h[o+(ap<<6)+8>>3];if(ah){aJ=as;aK=ar}else{an=+h[64800+($*688&-1)>>3];aq=+Z(+(ar*an));aJ=+Z(+(as*an));aK=aq}aq=+h[o+(ap<<6)+16>>3];if(t){aL=ac;aM=aq}else{an=+h[64800+(aj*688&-1)>>3];as=+Z(+(aq*an));aL=+Z(+(ac*an));aM=as}aA=aD-(aG-aF)/ax-(aK-aJ)/ad;aB=aE-(aI-aH)/ax-(aM-aL)/ad;aC=$}L15235:do{if((aC|0)!=99){if(!(aA>-8.988465674311579e+307&aA<8.988465674311579e+307)){break}if((a[64788+(aC*688&-1)|0]&1)==0){aN=aA}else{if(aA<=0.0){break}as=+_(+aA);aN=as/+h[64800+(aC*688&-1)>>3]}if((a[u]&1)!=0|(aC|0)<0){break}$=64712+(aC*688&-1)|0;if(aN<+h[$>>3]){h[$>>3]=aN;aO=c[am>>2]|0}else{aO=aC}$=64664+(aO*688&-1)|0;do{if(aN<+h[$>>3]){if((c[64648+(aO*688&-1)>>2]&1|0)==0){break L15235}if((c[64728+(aO*688&-1)>>2]&1|0)==0){h[$>>3]=aN;break}as=+h[64736+(aO*688&-1)>>3];if(as>aN){h[$>>3]=as;break L15235}else{h[$>>3]=aN;break}}}while(0);$=c[am>>2]|0;aj=64720+($*688&-1)|0;if(aN>+h[aj>>3]){h[aj>>3]=aN;aP=c[am>>2]|0}else{aP=$}$=64672+(aP*688&-1)|0;if(aN<=+h[$>>3]){break}if((c[64648+(aP*688&-1)>>2]&2|0)==0){break}if((c[64732+(aP*688&-1)>>2]&2|0)==0){h[$>>3]=aN;break}as=+h[64760+(aP*688&-1)>>3];if(as<aN){h[$>>3]=as;break}else{h[$>>3]=aN;break}}}while(0);$=c[d>>2]|0;L15268:do{if(($|0)!=99){if(!(aB>-8.988465674311579e+307&aB<8.988465674311579e+307)){break}if((a[64788+($*688&-1)|0]&1)==0){aQ=aB}else{if(aB<=0.0){break}as=+_(+aB);aQ=as/+h[64800+($*688&-1)>>3]}if((a[u]&1)!=0|($|0)<0){break}aj=64712+($*688&-1)|0;if(aQ<+h[aj>>3]){h[aj>>3]=aQ;aR=c[d>>2]|0}else{aR=$}aj=64664+(aR*688&-1)|0;do{if(aQ<+h[aj>>3]){if((c[64648+(aR*688&-1)>>2]&1|0)==0){break L15268}if((c[64728+(aR*688&-1)>>2]&1|0)==0){h[aj>>3]=aQ;break}as=+h[64736+(aR*688&-1)>>3];if(as>aQ){h[aj>>3]=as;break L15268}else{h[aj>>3]=aQ;break}}}while(0);aj=c[d>>2]|0;t=64720+(aj*688&-1)|0;if(aQ>+h[t>>3]){h[t>>3]=aQ;aS=c[d>>2]|0}else{aS=aj}aj=64672+(aS*688&-1)|0;if(aQ<=+h[aj>>3]){break}if((c[64648+(aS*688&-1)>>2]&2|0)==0){break}if((c[64732+(aS*688&-1)>>2]&2|0)==0){h[aj>>3]=aQ;break}as=+h[64760+(aS*688&-1)>>3];if(as<aQ){h[aj>>3]=as;break}else{h[aj>>3]=aQ;break}}}while(0);Y=Y+1|0;}while((Y|0)<4);i=e;return}aQ=+P(+ao);aB=+P(+at);do{if(aQ<aB*.01|aB<aQ*.01){aN=+P(+ae);aA=+P(+aw);if(!(aN<aA*.01|aA<aN*.01)){aT=0;aU=0;break}if(!n){aT=1;aU=(a[b+232|0]&1)!=0;break}if((c[5094]|0)==0){aT=1;aU=1;break}aT=1;aU=(a[b+232|0]&1)!=0}else{aT=0;aU=0}}while(0);Y=(q|0)==0;do{if(Y){if((dz()|0)==0){aV=aU;aW=0;aX=0;aY=c[200]|0;break}else{i=e;return}}else{aS=(q|0)==1;d=(q|0)==2;do{if((q-1|0)>>>0<2){if((c[(c[3524]|0)+144>>2]|0)!=0){break}i=e;return}}while(0);if(!d){aV=aU;aW=0;aX=aS;aY=ay;break}aV=(c[(c[3524]|0)+96>>2]&512|0)==0|aU;aW=1;aX=aS;aY=ay}}while(0);ay=(a[64788+(aY*688&-1)|0]&1)==0;if(ay){if((a[64788+((c[144]|0)*688&-1)|0]&1)==0){aZ=aV}else{au=11205}}else{au=11205}if((au|0)==11205){aZ=1}aV=c[64652+(aY*688&-1)>>2]|0;if((aV&1|0)==0){a_=64680+(aY*688&-1)|0}else{a_=64664+(aY*688&-1)|0}aN=+h[a_>>3];if((aV&2|0)==0){a$=64688+(aY*688&-1)|0}else{a$=64672+(aY*688&-1)|0}aA=+h[a$>>3];a$=c[144]|0;aV=c[64652+(a$*688&-1)>>2]|0;if((aV&1|0)==0){a0=64680+(a$*688&-1)|0}else{a0=64664+(a$*688&-1)|0}ad=+h[a0>>3];if((aV&2|0)==0){a1=64688+(a$*688&-1)|0}else{a1=64672+(a$*688&-1)|0}aL=+h[a1>>3];if(n){a1=c[34]|0;aV=c[64652+(a1*688&-1)>>2]|0;if((aV&1|0)==0){a2=64680+(a1*688&-1)|0}else{a2=64664+(a1*688&-1)|0}if((aV&2|0)==0){a3=64688+(a1*688&-1)|0}else{a3=64672+(a1*688&-1)|0}a4=+h[a3>>3];a5=+h[a2>>3]}else{a4=0.0;a5=0.0}do{if(aT){if((c[(c[3524]|0)+152>>2]|0)==0|aZ){break}if(n){a6=1.0;a7=1.0}else{a6=+h[64776+(aY*688&-1)>>3]>0.0?1.0:-1.0;a7=+h[64776+(a$*688&-1)>>3]>0.0?1.0:-1.0}if(aQ>aB){a2=aw*a7>0.0;a3=ao*a6>0.0;a1=aa(a2?s:1,av)-(a3?av:1)|0;aV=-av|0;a8=av;a9=a3?1:-1;ba=(a3?aV:av)+(a2?aV:av)|0;bb=a1;bc=aa(s,av)}else{a1=at*a6>0.0;aV=ae*a7>0.0;a2=aa(a1?1:s,av)-(aV?1:av)|0;a3=aa(s,av);a8=s;a9=a1?av:-av|0;ba=aa(a3,a1?-1:1)+(aV?-1:1)|0;bb=a2;bc=a3}if(aX){bd=bc*3&-1}else{bd=aW?bc<<2:bc}a3=bd<<3;a2=ut(a3)|0;do{if((a2|0)==0){gk();aV=ut(a3)|0;if((aV|0)!=0){be=aV;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=177456,v)|0)}else{be=a2}}while(0);a2=be;aM=+h[o+8>>3];ax=((aM- +h[o+(r<<6)+8>>3])/az+(aM- +h[o+(al<<6)+8>>3])/af)*.5;aM=+h[o+16>>3];aH=((aM- +h[o+(r<<6)+16>>3])/az+(aM- +h[o+(al<<6)+16>>3])/af)*.5;aM=+h[o+24>>3];aI=((aM- +h[o+(r<<6)+24>>3])/az+(aM- +h[o+(al<<6)+24>>3])/af)*.5;do{if((p|0)>0){a3=aN<aA;aS=ad<aL;d=a5<a4;aV=a8;a1=0;a0=0;a_=bb;aU=0;aR=-1;u=0;aP=-1;am=0;L15375:while(1){aM=+h[o+(a_<<6)+8>>3];aE=+h[o+(a_<<6)+16>>3];aJ=+h[o+(a_<<6)+24>>3];aK=aM-ax;aF=ax+aM;aM=aE-aH;aG=aH+aE;aE=aJ-aI;aD=aI+aJ;do{if(a3){if(!(aK<aN|aK>aA)){au=11246;break}if(aF<aN|aF>aA){au=11257}else{au=11246}}else{if(!(aK<aA|aK>aN)){au=11246;break}if(aF<aA|aF>aN){au=11257}else{au=11246}}}while(0);L15382:do{if((au|0)==11246){au=0;do{if(aS){if(!(aM<ad|aM>aL)){break}if(aG<ad|aG>aL){au=11257;break L15382}}else{if(!(aM<aL|aM>ad)){break}if(aG<aL|aG>ad){au=11257;break L15382}}}while(0);if(!n){au=11273;break}if(d){if(!(aE<a5|aE>a4)){au=11273;break}if(aD<a5|aD>a4){au=11257;break}else{au=11273;break}}else{if(!(aE<a4|aE>a5)){au=11273;break}if(aD<a4|aD>a5){au=11257;break}else{au=11273;break}}}}while(0);L15396:do{if((au|0)==11257){au=0;do{if(aK<aF){if(!(aN<aK|aN>aF)){break}if(aA<aK|aA>aF){bf=aP;bg=u;bh=aR;bi=aU;bj=a0;bk=a1;break L15396}}else{if(!(aN<aF|aN>aK)){break}if(aA<aF|aA>aK){bf=aP;bg=u;bh=aR;bi=aU;bj=a0;bk=a1;break L15396}}}while(0);do{if(aM<aG){if(!(ad<aM|ad>aG)){break}if(aL<aM|aL>aG){bf=aP;bg=u;bh=aR;bi=aU;bj=a0;bk=a1;break L15396}}else{if(!(ad<aG|ad>aM)){break}if(aL<aG|aL>aM){bf=aP;bg=u;bh=aR;bi=aU;bj=a0;bk=a1;break L15396}}}while(0);if(!n){au=11273;break}if(aE<aD){if(!(a5<aE|a5>aD)){au=11273;break}if(a4<aE|a4>aD){bf=aP;bg=u;bh=aR;bi=aU;bj=a0;bk=a1;break}else{au=11273;break}}else{if(!(a5<aD|a5>aE)){au=11273;break}if(a4<aD|a4>aE){bf=aP;bg=u;bh=aR;bi=aU;bj=a0;bk=a1;break}else{au=11273;break}}}}while(0);do{if((au|0)==11273){au=0;if((aP|0)<0){bl=a_;bm=1;bn=1;bo=0}else{aO=((u|0)==0&1)+a0|0;aC=u+1|0;if((aO|0)!=1&(aC|0)>(a1|0)){au=11275;break L15375}else{bl=aP;bm=aC;bn=aO;bo=a1}}if(Y){aE=+h[o+(a_<<6)+32>>3];aD=+h[8341];do{if(aD<aE){aM=+h[8342];if(aM<=aE){bp=+((a[20668]|0)==112&1|0);break}aG=(aE-aD)/(aM-aD);if((a[20668]|0)==112){bp=aG;break}bp=1.0-aG}else{bp=+((a[20668]|0)!=112&1|0)}}while(0);h[a2+(aU<<3)>>3]=bp;bf=bl;bg=bm;bh=a_;bi=aU+1|0;bj=bn;bk=bo;break}aD=+h[o+(a_<<6)+40>>3];aE=+h[8341];do{if(aE<aD){aG=+h[8342];if(aG<=aD){bq=+((a[20668]|0)==112&1|0);break}aM=(aD-aE)/(aG-aE);if((a[20668]|0)==112){bq=aM;break}bq=1.0-aM}else{bq=+((a[20668]|0)!=112&1|0)}}while(0);aO=aU+1|0;h[a2+(aU<<3)>>3]=bq;aE=+h[o+(a_<<6)+48>>3];aD=+h[8341];do{if(aD<aE){aM=+h[8342];if(aM<=aE){br=+((a[20668]|0)==112&1|0);break}aG=(aE-aD)/(aM-aD);if((a[20668]|0)==112){br=aG;break}br=1.0-aG}else{br=+((a[20668]|0)!=112&1|0)}}while(0);aC=aU+2|0;h[a2+(aO<<3)>>3]=br;aD=+h[o+(a_<<6)+56>>3];aE=+h[8341];do{if(aE<aD){aG=+h[8342];if(aG<=aD){bs=+((a[20668]|0)==112&1|0);break}aM=(aD-aE)/(aG-aE);if((a[20668]|0)==112){bs=aM;break}bs=1.0-aM}else{bs=+((a[20668]|0)!=112&1|0)}}while(0);aO=aU+3|0;h[a2+(aC<<3)>>3]=bs;if(!aW){bf=bl;bg=bm;bh=a_;bi=aO;bj=bn;bk=bo;break}h[a2+(aO<<3)>>3]=+h[o+(a_<<6)+32>>3];bf=bl;bg=bm;bh=a_;bi=aU+4|0;bj=bn;bk=bo}}while(0);aO=a_+a9|0;j=aV-1|0;if((j|0)==0){if((bk|0)==0){bt=bg}else{if((bg|0)<1|(bg|0)==(bk|0)){bt=bk}else{au=11307;break}}bu=0;bv=aO+ba|0;bw=bt;bx=a8}else{bu=bg;bv=aO;bw=bk;bx=j}j=am+1|0;if((j|0)<(p|0)){aV=bx;a1=bw;a0=bj;a_=bv;aU=bi;aR=bh;u=bu;aP=bf;am=j}else{au=11310;break}}if((au|0)==11307){uh(-1,169296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=e;return}else if((au|0)==11310){if(!((bw|0)>0&(bj|0)>0)){break}if(n){aE=+h[64664+((c[200]|0)*688&-1)>>3];aD=+h[91];aM=+h[96];aG=(+h[o+(bf<<6)+8>>3]-aE)*aD+aM+-1.0;aK=+h[64664+((c[144]|0)*688&-1)>>3];aF=+h[21];aJ=+h[68];as=(+h[o+(bf<<6)+16>>3]-aK)*aF+aJ+-1.0;an=+h[5279];ac=+h[2];aq=+h[12];ar=(+h[o+(bf<<6)+24>>3]-an)*ac+aq+-1.0;ag=+h[403];ai=+h[391];F=+h[395];R=+h[399];S=+h[404];T=+h[392];I=+h[396];J=+h[400];K=+h[406];L=+h[394];E=+h[398];V=+h[402];M=K+aG*L+as*E+ar*V;z=M==0.0?1.0e-5:M;M=+(c[180]|0);am=c[186]|0;N=+(c[40]|0);aP=c[46]|0;ab=+P(+(ao+at))*.5;c[k>>2]=~~(+(~~((ag+aG*ai+as*F+ar*R)/z*M)+am|0)-ab);w=+P(+(ae+aw))*.5;c[k+4>>2]=~~(w+ +(~~((S+aG*T+as*I+ar*J)/z*N)+aP|0));z=(+h[o+(bh<<6)+8>>3]-aE)*aD+aM+-1.0;ar=(+h[o+(bh<<6)+16>>3]-aK)*aF+aJ+-1.0;as=(+h[o+(bh<<6)+24>>3]-an)*ac+aq+-1.0;aG=K+z*L+ar*E+as*V;A=aG==0.0?1.0e-5:aG;c[k+12>>2]=~~(ab+ +(~~((ag+z*ai+ar*F+as*R)/A*M)+am|0));c[k+16>>2]=~~(+(~~((S+z*T+ar*I+as*J)/A*N)+aP|0)-w);w=(aN-aE)*aD+aM+-1.0;A=(ad-aK)*aF+aJ+-1.0;as=(a5-an)*ac+aq+-1.0;ar=K+w*L+A*E+as*V;z=ar==0.0?1.0e-5:ar;c[k+24>>2]=~~((ag+w*ai+A*F+as*R)/z*M)+am;c[k+28>>2]=~~((S+w*T+A*I+as*J)/z*N)+aP;z=(aA-aE)*aD+aM+-1.0;aM=(aL-aK)*aF+aJ+-1.0;aJ=(a4-an)*ac+aq+-1.0;aq=K+z*L+aM*E+aJ*V;V=aq==0.0?1.0e-5:aq;c[k+36>>2]=~~((ag+z*ai+aM*F+aJ*R)/V*M)+am;c[k+40>>2]=~~((S+z*T+aM*I+aJ*J)/V*N)+aP}else{aP=c[200]|0;N=+(c[64768+(aP*688&-1)>>2]|0);V=+h[o+(bf<<6)+8>>3];J=a6*+P(+ax);aJ=+h[64664+(aP*688&-1)>>3];I=+h[64776+(aP*688&-1)>>3];c[k>>2]=~~(N+I*(V-J-aJ)+.5);aP=c[144]|0;V=+(c[64768+(aP*688&-1)>>2]|0);aM=+h[o+(bf<<6)+16>>3];T=a7*+P(+aH);z=+h[64664+(aP*688&-1)>>3];S=+h[64776+(aP*688&-1)>>3];c[k+4>>2]=~~(V+S*(aM+T-z)+.5);c[k+12>>2]=~~(N+(J+ +h[o+(bh<<6)+8>>3]-aJ)*I+.5);c[k+16>>2]=~~(V+(+h[o+(bh<<6)+16>>3]-T-z)*S+.5);c[k+24>>2]=~~(N+(aN-aJ)*I+.5);c[k+28>>2]=~~(V+(aL-z)*S+.5);c[k+36>>2]=~~(N+(aA-aJ)*I+.5);c[k+40>>2]=~~(V+(ad-z)*S+.5)}cI[c[(c[3524]|0)+152>>2]&63](bw,bj,a2,k|0,q);break}else if((au|0)==11275){uh(-1,172960,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=e;return}}}while(0);uu(be);i=e;return}}while(0);if(ay){by=(a[64788+(a$*688&-1)|0]&1)!=0}else{by=1}if((c[(c[3524]|0)+148>>2]|0)==0){uf(-1,156360,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(by){a$=c[b+304>>2]|0;ay=(a[64788+(a$*688&-1)|0]&1)==0;a7=+h[o+(r<<6)+8>>3];if(ay){a6=+h[o+8>>3];bz=a6;bA=a7;bB=a6}else{a6=+h[64800+(a$*688&-1)>>3];aw=+Z(+(a7*a6));a7=+h[o+8>>3];bz=+Z(+(a7*a6));bA=aw;bB=a7}be=c[b+308>>2]|0;q=(a[64788+(be*688&-1)|0]&1)==0;a7=+h[o+(r<<6)+16>>3];if(q){aw=+h[o+16>>3];bC=aw;bD=a7;bE=aw}else{aw=+h[64800+(be*688&-1)>>3];a6=+Z(+(a7*aw));a7=+h[o+16>>3];bC=+Z(+(a7*aw));bD=a6;bE=a7}k=c[b+312>>2]|0;bj=(a[64788+(k*688&-1)|0]&1)==0;a7=+h[o+(r<<6)+24>>3];if(bj){a6=+h[o+24>>3];bF=a6;bG=a7;bH=a6}else{a6=+h[64800+(k*688&-1)>>3];aw=+Z(+(a7*a6));a7=+h[o+24>>3];bF=+Z(+(a7*a6));bG=aw;bH=a7}a7=+h[o+(al<<6)+8>>3];if(ay){bI=bB;bJ=a7}else{aw=+h[64800+(a$*688&-1)>>3];a6=+Z(+(a7*aw));bI=+Z(+(bB*aw));bJ=a6}a6=+h[o+(al<<6)+16>>3];if(q){bK=bE;bL=a6}else{aw=+h[64800+(be*688&-1)>>3];bB=+Z(+(a6*aw));bK=+Z(+(bE*aw));bL=bB}bB=+h[o+(al<<6)+24>>3];if(bj){bM=bH;bN=bB}else{aw=+h[64800+(k*688&-1)>>3];bE=+Z(+(bB*aw));bM=+Z(+(bH*aw));bN=bE}bO=bA-bz;bP=bD-bC;bQ=bG-bF;bR=bJ-bI;bS=bL-bK;bT=bN-bM}else{bM=+h[o+8>>3];bN=+h[o+16>>3];bK=+h[o+24>>3];bO=+h[o+(r<<6)+8>>3]-bM;bP=+h[o+(r<<6)+16>>3]-bN;bQ=+h[o+(r<<6)+24>>3]-bK;bR=+h[o+(al<<6)+8>>3]-bM;bS=+h[o+(al<<6)+16>>3]-bN;bT=+h[o+(al<<6)+24>>3]-bK}bK=bO/az;bO=bP/az;bP=bQ/az;az=bR/af;bR=bS/af;bS=bT/af;af=(bK+az)*.5;bT=(bO+bR)*.5;bQ=(bP+bS)*.5;bN=(bK-az)*.5;bM=(bO-bR)*.5;bL=(bP-bS)*.5;if((s|0)<=0){i=e;return}al=b+304|0;r=o+8|0;k=b+308|0;bj=o+16|0;be=b+312|0;b=o+24|0;q=(av|0)>0;a$=l|0;ay=l+8|0;bw=l+16|0;bh=l+24|0;bf=l+32|0;bu=l+40|0;bi=l+48|0;bv=l+56|0;bx=l+64|0;p=l+72|0;bk=l+80|0;bg=l+88|0;a8=aN<aA;bt=ad<aL;ba=a5<a4;a9=n^1;bo=m|0;bn=m+8|0;bm=f|0;bl=f+8|0;bb=f+4|0;bd=m|0;bc=m+24|0;aX=m+4|0;aY=m+28|0;aZ=g|0;a2=g+4|0;aP=g+8|0;am=0;u=0;while(1){if(by){aR=c[al>>2]|0;bI=+h[r>>3];if((a[64788+(aR*688&-1)|0]&1)==0){bU=bI}else{bU=+Z(+(bI*+h[64800+(aR*688&-1)>>3]))}bI=+(am|0);aR=c[k>>2]|0;bJ=+h[bj>>3];if((a[64788+(aR*688&-1)|0]&1)==0){bV=bJ}else{bV=+Z(+(bJ*+h[64800+(aR*688&-1)>>3]))}aR=c[be>>2]|0;bJ=+h[b>>3];if((a[64788+(aR*688&-1)|0]&1)==0){bW=bJ}else{bW=+Z(+(bJ*+h[64800+(aR*688&-1)>>3]))}bX=bS*bI+bW;bY=bR*bI+bV;bZ=az*bI+bU}else{bI=+(am|0);bX=bS*bI+ +h[b>>3];bY=bR*bI+ +h[bj>>3];bZ=az*bI+ +h[r>>3]}if(q){aR=u;aU=0;while(1){do{if(aW){bI=+h[o+(aR<<6)+32>>3];if(bI==0.0){break}if(bI>=128.0){au=11354;break}if((c[(c[3524]|0)+96>>2]&512|0)!=0){au=11354}}else{au=11354}}while(0);L15534:do{if((au|0)==11354){au=0;bI=+(aU|0);bJ=bZ+bK*bI;bF=bY+bO*bI;bG=bX+bP*bI;bI=af+bJ;h[a$>>3]=bI;h[ay>>3]=bT+bF;h[bw>>3]=bQ+bG;h[bh>>3]=bN+bJ;h[bf>>3]=bM+bF;h[bu>>3]=bL+bG;h[bi>>3]=bJ-af;h[bv>>3]=bF-bT;h[bx>>3]=bG-bQ;h[p>>3]=bJ-bN;h[bk>>3]=bF-bM;h[bg>>3]=bG-bL;a_=(c[5094]|0)!=0|a9;a0=0;a1=0;bG=bI;while(1){if(a8){if(bG<aN|bG>aA){b_=a0}else{au=11358}}else{if(bG<aA|bG>aN){b_=a0}else{au=11358}}do{if((au|0)==11358){au=0;bI=+h[l+(a1*24&-1)+8>>3];if(bt){if(bI<ad|bI>aL){b_=a0;break}}else{if(bI<aL|bI>ad){b_=a0;break}}bI=+h[l+(a1*24&-1)+16>>3];if(ba){if(!(bI<=a4&bI>=a5|a_)){b_=a0;break}}else{if(!(bI<=a5&bI>=a4|a_)){b_=a0;break}}b_=a0+1|0}}while(0);aC=a1+1|0;if((aC|0)>=4){break}a0=b_;a1=aC;bG=+h[l+(aC*24&-1)>>3]}if((b_|0)<=0){break}c[bn>>2]=3;a1=(b_|0)<4;a0=0;do{do{if(n){bG=(+h[l+(a0*24&-1)>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;bI=(+h[l+(a0*24&-1)+8>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;bF=(+h[l+(a0*24&-1)+16>>3]- +h[5279])*+h[2]+ +h[12]+-1.0;bJ=+h[406]+bG*+h[394]+bI*+h[398]+bF*+h[402];bC=bJ==0.0?1.0e-5:bJ;a_=~~((+h[403]+bG*+h[391]+bI*+h[395]+bF*+h[399])/bC*+(c[180]|0))+(c[186]|0)|0;aC=~~((+h[404]+bG*+h[392]+bI*+h[396]+bF*+h[400])/bC*+(c[40]|0))+(c[46]|0)|0;c[m+(a0*12&-1)>>2]=a_;c[m+(a0*12&-1)+4>>2]=aC;b$=a_;b0=aC}else{aC=c[200]|0;bC=+(c[64768+(aC*688&-1)>>2]|0);if(!by){a_=~~(bC+(+h[l+(a0*24&-1)>>3]- +h[64664+(aC*688&-1)>>3])*+h[64776+(aC*688&-1)>>3]+.5);c[m+(a0*12&-1)>>2]=a_;aV=c[144]|0;d=~~(+(c[64768+(aV*688&-1)>>2]|0)+(+h[l+(a0*24&-1)+8>>3]- +h[64664+(aV*688&-1)>>3])*+h[64776+(aV*688&-1)>>3]+.5);c[m+(a0*12&-1)+4>>2]=d;b$=a_;b0=d;break}bF=+h[l+(a0*24&-1)>>3];if((a[64788+(aC*688&-1)|0]&1)==0){b1=bF}else{bI=+_(+bF);b1=bI/+h[64800+(aC*688&-1)>>3]}d=~~(bC+(b1- +h[64664+(aC*688&-1)>>3])*+h[64776+(aC*688&-1)>>3]+.5);c[m+(a0*12&-1)>>2]=d;aC=c[144]|0;bC=+(c[64768+(aC*688&-1)>>2]|0);bI=+h[l+(a0*24&-1)+8>>3];if((a[64788+(aC*688&-1)|0]&1)==0){b2=bI}else{bF=+_(+bI);b2=bF/+h[64800+(aC*688&-1)>>3]}a_=~~(bC+(b2- +h[64664+(aC*688&-1)>>3])*+h[64776+(aC*688&-1)>>3]+.5);c[m+(a0*12&-1)+4>>2]=a_;b$=d;b0=a_}}while(0);do{if(aT){if(!((c[(c[3524]|0)+108>>2]|0)!=0&a1)){break}a_=m+(a0*12&-1)|0;d=c[13542]|0;aC=c[d>>2]|0;if((b$|0)<(aC|0)){c[a_>>2]=aC;b3=aC}else{b3=b$}aC=c[d+4>>2]|0;if((b3|0)>(aC|0)){c[a_>>2]=aC}aC=m+(a0*12&-1)+4|0;a_=c[d+12>>2]|0;if((b0|0)>(a_|0)){c[aC>>2]=a_;b4=a_}else{b4=b0}a_=c[d+8>>2]|0;if((b4|0)>=(a_|0)){break}c[aC>>2]=a_}}while(0);a0=a0+1|0;}while((a0|0)<4);do{if(Y){if((c[o+(aR<<6)>>2]|0)==2){break L15534}a0=o+(aR<<6)+32|0;if((cg(+(+h[a0>>3]))|0)==0){break L15534}bC=+h[a0>>3];bF=+h[8341];do{if(bF<bC){bI=+h[8342];if(bI<=bC){b5=+((a[20668]|0)==112&1|0);break}bG=(bC-bF)/(bI-bF);if((a[20668]|0)==112){b5=bG;break}b5=1.0-bG}else{b5=+((a[20668]|0)!=112&1|0)}}while(0);a0=(c[3524]|0)+144|0;if((c[a0>>2]|0)==0){break}c[bm>>2]=5;h[bl>>3]=b5;c[bb>>2]=0;cM[c[a0>>2]&511](f)}else{bF=+h[o+(aR<<6)+40>>3];bC=+h[8341];do{if(bC<bF){bG=+h[8342];if(bG<=bF){a0=a[20668]|0;b6=+(a0<<24>>24==112&1|0);b7=a0;break}bI=(bF-bC)/(bG-bC);a0=a[20668]|0;if(a0<<24>>24==112){b6=bI;b7=112;break}b6=1.0-bI;b7=a0}else{a0=a[20668]|0;b6=+(a0<<24>>24!=112&1|0);b7=a0}}while(0);a0=~~(b6*255.0+.5);bF=+h[o+(aR<<6)+48>>3];do{if(bC<bF){bI=+h[8342];if(bI<=bF){b8=+(b7<<24>>24==112&1|0);b9=b7;break}bG=(bF-bC)/(bI-bC);if(b7<<24>>24==112){b8=bG;b9=112;break}b8=1.0-bG;b9=b7}else{b8=+(b7<<24>>24!=112&1|0);b9=b7}}while(0);a1=~~(b8*255.0+.5);bF=+h[o+(aR<<6)+56>>3];do{if(bC<bF){bG=+h[8342];if(bG<=bF){ca=+(b9<<24>>24==112&1|0);break}bI=(bF-bC)/(bG-bC);if(b9<<24>>24==112){ca=bI;break}ca=1.0-bI}else{ca=+(b9<<24>>24!=112&1|0)}}while(0);a_=(c[3524]|0)+144|0;if((c[a_>>2]|0)==0){break}c[aZ>>2]=3;c[a2>>2]=((a1<<8)+(a0<<16)|0)+~~(ca*255.0+.5);h[aP>>3]=0.0;cM[c[a_>>2]&511](g)}}while(0);do{if(aW){a_=~~(+h[o+(aR<<6)+32>>3]*100.0/255.0);if((a_|0)==0){break L15534}if((c[(c[3524]|0)+96>>2]&512|0)==0){break}c[bn>>2]=a_<<4|4}}while(0);a_=c[3524]|0;do{if(aT){aC=c[a_+108>>2]|0;if((aC|0)==0){break}d=c[bd>>2]|0;aV=c[bc>>2]|0;aS=c[aX>>2]|0;a3=c[aY>>2]|0;j=aV-d|0;aO=a3-aS|0;cI[aC&63](c[bn>>2]|0,(d|0)<(aV|0)?d:aV,(aS|0)<(a3|0)?aS:a3,(j|0)>-1?j:-j|0,(aO|0)>-1?aO:-aO|0);break L15534}}while(0);cN[c[a_+148>>2]&255](4,bo)}}while(0);aO=aU+1|0;if((aO|0)<(av|0)){aR=aR+1|0;aU=aO}else{break}}cb=av+u|0}else{cb=u}aU=am+1|0;if((aU|0)<(s|0)){am=aU;u=cb}else{break}}i=e;return}function f4(a,b){a=a|0;b=b|0;var d=0,e=0.0,f=0.0,g=0;d=c[(c[9732]|0)+320>>2]|0;e=+h[d+(c[a>>2]<<6)+8>>3];f=+h[d+(c[b>>2]<<6)+8>>3];if(e<f){g=-1;return g|0}g=e>f&1;return g|0}function f5(a,b){a=a|0;b=b|0;var c=0.0,d=0.0,e=0;c=+h[a+16>>3];d=+h[b+16>>3];if(c>d){e=1;return e|0}e=(c<d)<<31>>31;return e|0}function f6(b,d,e,f,g){b=b|0;d=+d;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0.0,v=0,w=0,x=0.0,y=0.0,z=0,A=0,B=0,C=0,D=0.0,E=0.0,F=0.0,G=0,H=0,I=0,J=0.0,K=0.0,L=0,M=0.0,N=0.0,O=0.0,P=0.0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0;j=i;i=i+96|0;k=f;f=i;i=i+56|0;uD(f,k,56);k=j|0;l=j+16|0;m=j+32|0;n=j+40|0;o=j+48|0;p=j+56|0;q=j+64|0;r=j+80|0;s=j+88|0;t=c[3524]|0;u=+(aa(c[t+24>>2]|0,c[1400]|0)|0);if((e|0)==0){v=65064+(b*688&-1)|0}else{v=65056+(b*688&-1)|0}w=~~(u*+h[v>>3]);v=c[200]|0;u=+(c[64768+(v*688&-1)>>2]|0);x=+h[64664+(v*688&-1)>>3];y=+h[64776+(v*688&-1)>>3];v=~~(u+(d-x)*y+.5);L15653:do{if((g|0)==0){z=e}else{A=(a[64788+(b*688&-1)|0]&1)==0;B=64800+(b*688&-1)|0;C=g;while(1){D=+h[C>>3];if(A){E=D}else{F=+_(+D);E=F/+h[B>>3]}G=~~(u+(E-x)*y+.5)-v|0;if((((G|0)>-1?G:-G|0)|0)<3){z=0;break L15653}G=c[C+16>>2]|0;if((G|0)==0){z=e;break}else{C=G}}}}while(0);e=c[f+4>>2]|0;if((e|0)>-3){g=t+168|0;cM[c[g>>2]&511](3);C=q;B=f+40|0;c[C>>2]=c[B>>2];c[C+4>>2]=c[B+4>>2];c[C+8>>2]=c[B+8>>2];c[C+12>>2]=c[B+12>>2];do{if((c[f>>2]|0)!=0){y=+h[f+24>>3];B=c[(c[3524]|0)+92>>2]|0;if(y<0.0){cK[B&63](+h[3817]);break}else{cK[B&63](y);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[f+16>>3]);B=c[(c[3524]|0)+64>>2]|0;if((e|0)<-5){cM[B&511](-2)}else{cM[B&511](e)}B=c[3524]|0;do{if((a[f+32|0]&1)==0){if((c[B+96>>2]&1024|0)!=0){break}c[q>>2]=1;c[q+4>>2]=e;H=11474}else{H=11474}}while(0);if((H|0)==11474){fn(q,B)}L15680:do{if(+h[3815]!=0.0){B=c[200]|0;q=c[64768+(B*688&-1)>>2]|0;y=+h[64664+(B*688&-1)>>3];x=+h[64776+(B*688&-1)>>3];B=~~(+(q|0)+(d-y)*x+.5);e=c[144]|0;f=c[64768+(e*688&-1)>>2]|0;E=+h[64664+(e*688&-1)>>3];u=+h[64776+(e*688&-1)>>3];e=~~(+(f|0)+(0.0-E)*u+.5);F=+h[4483];do{if(F<d){h[4483]=d}else{D=-0.0-d;if(F>=D){break}h[4483]=D}}while(0);C=2;A=B;G=e;F=0.0;D=d;I=q;J=y;K=x;L=f;M=E;N=u;while(1){O=D*.9950041652780258-F*.09983341664682815;P=F*.9950041652780258+D*.09983341664682815;Q=~~(+(I|0)+(O-J)*K+.5);R=~~(+(L|0)+(P-M)*N+.5);c[m>>2]=A;c[n>>2]=G;c[o>>2]=Q;c[p>>2]=R;S=c[3524]|0;if((fl(m,n,o,p)|0)!=0){cN[c[S+56>>2]&255](c[m>>2]|0,c[n>>2]|0);cN[c[S+60>>2]&255](c[o>>2]|0,c[p>>2]|0)}if((C|0)>=64){break L15680}S=c[200]|0;T=c[144]|0;C=C+1|0;A=Q;G=R;F=P;D=O;I=c[64768+(S*688&-1)>>2]|0;J=+h[64664+(S*688&-1)>>3];K=+h[64776+(S*688&-1)>>3];L=c[64768+(T*688&-1)>>2]|0;M=+h[64664+(T*688&-1)>>3];N=+h[64776+(T*688&-1)>>3]}}else{do{if((a[35184]&1)!=0&(v|0)<(c[9335]|0)&(v|0)>(c[9334]|0)){L=c[9337]|0;I=c[7940]|0;if((L|0)<=(I|0)){U=I;break}G=c[9336]|0;A=c[7941]|0;if((G|0)>=(A|0)){U=I;break}if((G|0)>(I|0)){cN[c[t+56>>2]&255](v,I);cN[c[t+60>>2]&255](v,c[9336]|0);V=c[9337]|0;W=c[7941]|0}else{V=L;W=A}if((V|0)>=(W|0)){break L15680}cN[c[t+56>>2]&255](v,V);cN[c[t+60>>2]&255](v,c[7941]|0);break L15680}else{U=c[7940]|0}}while(0);cN[c[t+56>>2]&255](v,U);cN[c[t+60>>2]&255](v,c[7941]|0)}}while(0);U=l;c[U>>2]=c[14084];c[U+4>>2]=c[56340>>2];c[U+8>>2]=c[56344>>2];c[U+12>>2]=c[56348>>2];U=c[14075]|0;do{if((c[14074]|0)!=0){d=+h[7040];V=c[(c[3524]|0)+92>>2]|0;if(d<0.0){cK[V&63](+h[3817]);break}else{cK[V&63](d);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);V=c[(c[3524]|0)+64>>2]|0;if((U|0)<-5){cM[V&511](-2)}else{cM[V&511](U)}V=c[3524]|0;do{if((a[56328]&1)==0){if((c[V+96>>2]&1024|0)!=0){break}c[l>>2]=1;c[l+4>>2]=U;H=11504}else{H=11504}}while(0);if((H|0)==11504){fn(l,V)}cM[c[g>>2]&511](4)}g=t+56|0;cN[c[g>>2]&255](v,c[1394]|0);V=t+60|0;cN[c[V>>2]&255](v,(c[1394]|0)+w|0);l=c[1396]|0;if((l|0)>-1){cN[c[g>>2]&255](v,l);cN[c[V>>2]&255](v,(c[1396]|0)-w|0)}if((z|0)==0){i=j;return}fN(64984+(b*688&-1)|0,r,s,99744);w=64928+(b*688&-1)|0;if((c[w>>2]|0)!=0){fn(w,t)}ln(~~+h[r>>3]+v|0,~~+h[s>>3]+(c[1392]|0)|0,z,c[1398]|0,c[1384]|0,c[6586]|0,c[64924+(b*688&-1)>>2]|0);b=k;c[b>>2]=c[14084];c[b+4>>2]=c[56340>>2];c[b+8>>2]=c[56344>>2];c[b+12>>2]=c[56348>>2];b=c[14075]|0;do{if((c[14074]|0)!=0){d=+h[7040];z=c[(c[3524]|0)+92>>2]|0;if(d<0.0){cK[z&63](+h[3817]);break}else{cK[z&63](d);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);z=c[(c[3524]|0)+64>>2]|0;if((b|0)<-5){cM[z&511](-2)}else{cM[z&511](b)}z=c[3524]|0;do{if((a[56328]&1)==0){if((c[z+96>>2]&1024|0)==0){c[k>>2]=1;c[k+4>>2]=b;break}else{i=j;return}}}while(0);fn(k,z);i=j;return}function f7(b,d,e,f,g){b=b|0;d=+d;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0.0,v=0,w=0,x=0.0,y=0.0,z=0,A=0,B=0,C=0,D=0.0,E=0.0,F=0.0,G=0,H=0,I=0.0,J=0.0,K=0,L=0,M=0,N=0;j=i;i=i+96|0;k=f;f=i;i=i+56|0;uD(f,k,56);k=j|0;l=j+16|0;m=j+32|0;n=j+40|0;o=j+48|0;p=j+56|0;q=j+64|0;r=j+80|0;s=j+88|0;t=c[3524]|0;u=+(aa(c[t+28>>2]|0,c[1400]|0)|0);if((e|0)==0){v=65064+(b*688&-1)|0}else{v=65056+(b*688&-1)|0}w=~~(u*+h[v>>3]);v=c[144]|0;u=+(c[64768+(v*688&-1)>>2]|0);x=+h[64664+(v*688&-1)>>3];y=+h[64776+(v*688&-1)>>3];v=~~(u+(d-x)*y+.5);L15753:do{if((g|0)==0){z=e}else{A=(a[64788+(b*688&-1)|0]&1)==0;B=64800+(b*688&-1)|0;C=g;while(1){D=+h[C>>3];if(A){E=D}else{F=+_(+D);E=F/+h[B>>3]}G=~~(u+(E-x)*y+.5)-v|0;if((((G|0)>-1?G:-G|0)|0)<3){z=0;break L15753}G=c[C+16>>2]|0;if((G|0)==0){z=e;break}else{C=G}}}}while(0);e=c[f+4>>2]|0;if((e|0)>-3){g=t+168|0;cM[c[g>>2]&511](3);C=q;B=f+40|0;c[C>>2]=c[B>>2];c[C+4>>2]=c[B+4>>2];c[C+8>>2]=c[B+8>>2];c[C+12>>2]=c[B+12>>2];do{if((c[f>>2]|0)!=0){y=+h[f+24>>3];B=c[(c[3524]|0)+92>>2]|0;if(y<0.0){cK[B&63](+h[3817]);break}else{cK[B&63](y);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[f+16>>3]);B=c[(c[3524]|0)+64>>2]|0;if((e|0)<-5){cM[B&511](-2)}else{cM[B&511](e)}B=c[3524]|0;do{if((a[f+32|0]&1)==0){if((c[B+96>>2]&1024|0)!=0){break}c[q>>2]=1;c[q+4>>2]=e;H=11546}else{H=11546}}while(0);if((H|0)==11546){fn(q,B)}L15780:do{if(+h[3815]!=0.0){y=+h[4483];do{if(y<d){h[4483]=d}else{x=-0.0-d;if(y>=x){break}h[4483]=x}}while(0);B=c[200]|0;q=c[64768+(B*688&-1)>>2]|0;y=+h[64664+(B*688&-1)>>3];x=+h[64776+(B*688&-1)>>3];B=~~(+(q|0)+(0.0-y)*x+.5);e=c[144]|0;f=c[64768+(e*688&-1)>>2]|0;E=+h[64664+(e*688&-1)>>3];u=+h[64776+(e*688&-1)>>3];e=~~(+(f|0)+(d-E)*u+.5);c[8490]=B;c[8488]=e;F=d;D=0.0;C=2;A=B;B=e;e=q;I=y;y=x;q=f;x=E;E=u;while(1){u=D*.9950041652780258-F*.09983341664682815;J=F*.9950041652780258+D*.09983341664682815;f=~~(+(e|0)+(u-I)*y+.5);G=~~(+(q|0)+(J-x)*E+.5);c[m>>2]=A;c[n>>2]=B;c[o>>2]=f;c[p>>2]=G;K=c[3524]|0;if((fl(m,n,o,p)|0)!=0){cN[c[K+56>>2]&255](c[m>>2]|0,c[n>>2]|0);cN[c[K+60>>2]&255](c[o>>2]|0,c[p>>2]|0)}c[8490]=f;c[8488]=G;if((C|0)>=64){break L15780}K=c[200]|0;L=c[144]|0;F=J;D=u;C=C+1|0;A=f;B=G;e=c[64768+(K*688&-1)>>2]|0;I=+h[64664+(K*688&-1)>>3];y=+h[64776+(K*688&-1)>>3];q=c[64768+(L*688&-1)>>2]|0;x=+h[64664+(L*688&-1)>>3];E=+h[64776+(L*688&-1)>>3]}}else{do{if((a[35184]&1)!=0&(v|0)<(c[9337]|0)&(v|0)>(c[9336]|0)){q=c[9334]|0;e=c[7939]|0;if((q|0)>=(e|0)){break}B=c[9335]|0;A=c[7938]|0;if((B|0)<=(A|0)){break}if((q|0)>(A|0)){cN[c[t+56>>2]&255](A,v);cN[c[t+60>>2]&255](c[9334]|0,v);M=c[9335]|0;N=c[7939]|0}else{M=B;N=e}if((M|0)>=(N|0)){break L15780}cN[c[t+56>>2]&255](M,v);cN[c[t+60>>2]&255](c[7939]|0,v);break L15780}}while(0);cN[c[t+56>>2]&255](c[7938]|0,v);cN[c[t+60>>2]&255](c[7939]|0,v)}}while(0);M=l;c[M>>2]=c[14084];c[M+4>>2]=c[56340>>2];c[M+8>>2]=c[56344>>2];c[M+12>>2]=c[56348>>2];M=c[14075]|0;do{if((c[14074]|0)!=0){d=+h[7040];N=c[(c[3524]|0)+92>>2]|0;if(d<0.0){cK[N&63](+h[3817]);break}else{cK[N&63](d);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);N=c[(c[3524]|0)+64>>2]|0;if((M|0)<-5){cM[N&511](-2)}else{cM[N&511](M)}N=c[3524]|0;do{if((a[56328]&1)==0){if((c[N+96>>2]&1024|0)!=0){break}c[l>>2]=1;c[l+4>>2]=M;H=11575}else{H=11575}}while(0);if((H|0)==11575){fn(l,N)}cM[c[g>>2]&511](4)}g=t+56|0;cN[c[g>>2]&255](c[1394]|0,v);N=t+60|0;cN[c[N>>2]&255]((c[1394]|0)+w|0,v);l=c[1396]|0;if((l|0)>-1){cN[c[g>>2]&255](l,v);cN[c[N>>2]&255]((c[1396]|0)-w|0,v)}if((z|0)==0){i=j;return}fN(64984+(b*688&-1)|0,r,s,94352);w=64928+(b*688&-1)|0;if((c[w>>2]|0)!=0){fn(w,t)}ln(~~+h[r>>3]+(c[1392]|0)|0,~~+h[s>>3]+v|0,z,c[1398]|0,c[1384]|0,c[6586]|0,c[64924+(b*688&-1)>>2]|0);b=k;c[b>>2]=c[14084];c[b+4>>2]=c[56340>>2];c[b+8>>2]=c[56344>>2];c[b+12>>2]=c[56348>>2];b=c[14075]|0;do{if((c[14074]|0)!=0){d=+h[7040];z=c[(c[3524]|0)+92>>2]|0;if(d<0.0){cK[z&63](+h[3817]);break}else{cK[z&63](d);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[7039]);z=c[(c[3524]|0)+64>>2]|0;if((b|0)<-5){cM[z&511](-2)}else{cM[z&511](b)}z=c[3524]|0;do{if((a[56328]&1)==0){if((c[z+96>>2]&1024|0)==0){c[k>>2]=1;c[k+4>>2]=b;break}else{i=j;return}}}while(0);fn(k,z);i=j;return}function f8(a){a=a|0;var b=0;if((a|0)==0){return}b=c[a+116>>2]|0;if((b|0)!=0){uu(b)}b=a+48|0;a=c[b>>2]|0;if((a|0)==0){return}else{f8(a);uu(c[b>>2]|0);c[b>>2]=0;return}}function f9(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0,u=0,v=0,w=0;e=i;i=i+88|0;f=e|0;g=e+16|0;j=e+32|0;if((c[b+316>>2]|0)==0){k=0;i=e;return k|0}l=c[b+64>>2]|0;if(+h[b+72>>3]<0.0&(l|0)==3){m=(c[3524]|0)+144|0;if((c[m>>2]|0)==0){k=1;i=e;return k|0}n=~~+h[d>>3];c[g>>2]=3;c[g+4>>2]=n;h[g+8>>3]=0.0;cM[c[m>>2]&511](g);k=1;i=e;return k|0}if((l|0)==6){o=+h[d>>3];p=+h[8341];do{if(p<o){q=+h[8342];if(q<=o){r=+((a[20668]|0)==112&1|0);break}s=(o-p)/(q-p);if((a[20668]|0)==112){r=s;break}r=1.0-s}else{r=+((a[20668]|0)!=112&1|0)}}while(0);l=(c[3524]|0)+144|0;if((c[l>>2]|0)==0){k=1;i=e;return k|0}c[f>>2]=5;h[f+8>>3]=r;c[f+4>>2]=0;cM[c[l>>2]&511](f);k=1;i=e;return k|0}if((c[b+28>>2]|0)!=-6){k=0;i=e;return k|0}b=~~+h[d>>3];d=43280;while(1){t=c[d>>2]|0;if((t|0)==0){u=11624;break}if((c[t+4>>2]|0)==(b|0)){u=11622;break}else{d=t|0}}do{if((u|0)==11624){d=c[8798]|0;f=(d|0)>0;l=b;L15891:while(1){g=43264;while(1){v=c[g>>2]|0;if((v|0)==0){break}if((c[v+4>>2]|0)==(l|0)){break L15891}else{g=v|0}}w=l-1|0;if(!((l|0)>(d|0)&f)){u=11634;break}l=((w|0)%(d|0)&-1)+1|0}if((u|0)==11634){c[j+4>>2]=w;c[j+40>>2]=1;c[j+44>>2]=w;c[j+8>>2]=w;break}d=j;f=v+8|0;c[d>>2]=c[f>>2];c[d+4>>2]=c[f+4>>2];c[d+8>>2]=c[f+8>>2];c[d+12>>2]=c[f+12>>2];c[d+16>>2]=c[f+16>>2];c[d+20>>2]=c[f+20>>2];c[d+24>>2]=c[f+24>>2];c[d+28>>2]=c[f+28>>2];c[d+32>>2]=c[f+32>>2];c[d+36>>2]=c[f+36>>2];c[d+40>>2]=c[f+40>>2];c[d+44>>2]=c[f+44>>2];c[d+48>>2]=c[f+48>>2];c[d+52>>2]=c[f+52>>2];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[j+4>>2]=l;a[j+32|0]=0;break}if((a[j+32|0]&1)!=0){break}c[j+40>>2]=1;c[j+44>>2]=c[j+4>>2]}else if((u|0)==11622){f=j;d=t+8|0;c[f>>2]=c[d>>2];c[f+4>>2]=c[d+4>>2];c[f+8>>2]=c[d+8>>2];c[f+12>>2]=c[d+12>>2];c[f+16>>2]=c[d+16>>2];c[f+20>>2]=c[d+20>>2];c[f+24>>2]=c[d+24>>2];c[f+28>>2]=c[d+28>>2];c[f+32>>2]=c[d+32>>2];c[f+36>>2]=c[d+36>>2];c[f+40>>2]=c[d+40>>2];c[f+44>>2]=c[d+44>>2];c[f+48>>2]=c[d+48>>2];c[f+52>>2]=c[d+52>>2];if((a[j+32|0]&1)!=0){break}c[j+40>>2]=1;c[j+44>>2]=c[j+4>>2]}}while(0);fn(j+40|0,c[3524]|0);k=1;i=e;return k|0}function ga(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;e=i;do{if((b|0)!=0){if((a[b]|0)==0){break}f=(uA(b|0)|0)+2|0;g=ut(f)|0;do{if((g|0)==0){gk();h=ut(f)|0;if((h|0)!=0){j=h;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=87472,v)|0);return 0}else{j=g}}while(0);uB(j|0,b|0);g=j+(uA(j|0)|0)|0;w=10;a[g]=w&255;w=w>>8;a[g+1|0]=w&255;g=a8(j|0,10)|0;if((g|0)==0){k=0;l=0}else{f=j;h=0;m=0;n=g;while(1){a[n]=0;g=lp(f)|0;o=(g|0)>(h|0)?g:h;if((g|m|0)==0){if((a[b]|0)==10){p=11654}else{q=m}}else{p=11654}if((p|0)==11654){p=0;q=m+1|0}g=n+1|0;r=a8(g|0,10)|0;if((r|0)==0){k=o;l=q;break}else{f=g;h=o;m=q;n=r}}}if((d|0)!=0){c[d>>2]=l}uu(j);s=k;i=e;return s|0}}while(0);if((d|0)==0){s=0;i=e;return s|0}c[d>>2]=0;s=0;i=e;return s|0}function gb(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0.0,i=0.0,j=0.0,k=0.0,l=0.0,m=0.0,n=0.0,o=0,p=0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,v=0.0,w=0.0,x=0.0,y=0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0;f=b-1|0;g=+h[a+(f<<6)+8>>3];i=+h[a+(f<<6)+16>>3];j=+h[a+(b<<6)+8>>3];k=+h[a+(b<<6)+16>>3];f=(c[a+(b<<6)>>2]|0)==0;l=f?j:g;m=f?k:i;n=f?g:j;j=f?i:k;f=n==-8.988465674311579e+307;b=j==-8.988465674311579e+307;if(f|b){h[d>>3]=l;h[e>>3]=m;if(!f){h[e>>3]=+h[64664+((c[144]|0)*688&-1)>>3];o=4;return o|0}if(b){o=5;return o|0}h[d>>3]=+h[64664+((c[200]|0)*688&-1)>>3];o=1;return o|0}do{if(m==j){h[e>>3]=m;b=l<n;f=c[200]|0;k=+h[64672+(f*688&-1)>>3];do{if(b){if(!(k<l|k>n)){p=11674;break}q=+h[64664+(f*688&-1)>>3];p=11677}else{if(!(k<n|k>l)){p=11674;break}r=+h[64664+(f*688&-1)>>3];p=11678}}while(0);do{if((p|0)==11674){if(k==l){i=+h[64664+(f*688&-1)>>3];if(b){q=i;p=11677;break}else{r=i;p=11678;break}}h[d>>3]=k;o=2;return o|0}}while(0);if((p|0)==11677){if(q<l|q>n){break}else{s=q}}else if((p|0)==11678){if(r<n|r>l){break}else{s=r}}if(s==l){break}h[d>>3]=s;o=1;return o|0}else{if(l==n){h[d>>3]=l;b=m<j;f=c[144]|0;k=+h[64672+(f*688&-1)>>3];do{if(b){if(!(k<m|k>j)){p=11687;break}t=+h[64664+(f*688&-1)>>3];p=11690}else{if(!(k<j|k>m)){p=11687;break}u=+h[64664+(f*688&-1)>>3];p=11691}}while(0);do{if((p|0)==11687){if(k==m){i=+h[64664+(f*688&-1)>>3];if(b){t=i;p=11690;break}else{u=i;p=11691;break}}h[e>>3]=k;o=8;return o|0}}while(0);if((p|0)==11691){if(u<j|u>m){break}else{v=u}}else if((p|0)==11690){if(t<m|t>j){break}else{v=t}}if(v==m){break}h[e>>3]=v;o=4;return o|0}b=m<j;f=c[144]|0;a=64664+(f*688&-1)|0;k=+h[a>>3];do{if(b){if(!(k<m|k>j)){p=11699;break}w=+h[64672+(f*688&-1)>>3];p=11705}else{if(!(k<j|k>m)){p=11699;break}x=+h[64672+(f*688&-1)>>3];p=11706}}while(0);if((p|0)==11699){do{if(k!=m&k!=j){i=l+(k-m)*((n-l)/(j-m));y=c[200]|0;g=+h[64664+(y*688&-1)>>3];z=+h[64672+(y*688&-1)>>3];if(g<z){if(i<g|i>z){break}}else{if(i<z|i>g){break}}h[d>>3]=i;h[e>>3]=+h[a>>3];o=4;return o|0}}while(0);i=+h[64672+(f*688&-1)>>3];if(b){w=i;p=11705}else{x=i;p=11706}}if((p|0)==11706){if(x<j|x>m){A=x}else{B=x;p=11707}}else if((p|0)==11705){if(w<m|w>j){A=w}else{B=w;p=11707}}do{if((p|0)==11707){a=64672+(f*688&-1)|0;if(!(B!=m&B!=j)){A=B;break}i=l+(B-m)*((n-l)/(j-m));y=c[200]|0;g=+h[64664+(y*688&-1)>>3];z=+h[64672+(y*688&-1)>>3];if(g<z){if(i<g|i>z){A=B;break}}else{if(i<z|i>g){A=B;break}}h[d>>3]=i;h[e>>3]=+h[a>>3];o=8;return o|0}}while(0);f=l<n;b=c[200]|0;i=+h[64664+(b*688&-1)>>3];do{if(f){if(!(i<l|i>n)){p=11717;break}C=+h[64672+(b*688&-1)>>3];p=11723}else{if(!(i<n|i>l)){p=11717;break}D=+h[64672+(b*688&-1)>>3];p=11724}}while(0);if((p|0)==11717){do{if(i!=l&i!=n){g=m+(i-l)*((j-m)/(n-l));if(k<A){if(g<k|g>A){break}}else{if(g<A|g>k){break}}h[d>>3]=i;h[e>>3]=g;o=1;return o|0}}while(0);i=+h[64672+(b*688&-1)>>3];if(f){C=i;p=11723}else{D=i;p=11724}}if((p|0)==11724){if(D<n|D>l){break}else{E=D}}else if((p|0)==11723){if(C<l|C>n){break}else{E=C}}if(!(E!=l&E!=n)){break}i=m+(E-l)*((j-m)/(n-l));if(k<A){if(i<k|i>A){break}}else{if(i<A|i>k){break}}h[d>>3]=E;h[e>>3]=i;o=2;return o|0}}while(0);h[d>>3]=l;h[e>>3]=m;o=0;return o|0}function gc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0.0,i=0.0,j=0.0,k=0.0,l=0,m=0,n=0.0,o=0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0;f=b-1|0;g=+h[a+(f<<6)+8>>3];i=+h[a+(f<<6)+16>>3];j=+h[a+(b<<6)+8>>3];k=+h[a+(b<<6)+16>>3];b=g==-8.988465674311579e+307;a=j==-8.988465674311579e+307;f=i==-8.988465674311579e+307;l=k==-8.988465674311579e+307;if((((a?b?2:1:b&1)+(f&1)|0)+(l&1)|0)>1){m=0;return m|0}if(a|b){n=b?k:i;a=c[200]|0;o=64672+(a*688&-1)|0;p=+h[o>>3];q=+h[64664+(a*688&-1)>>3];if((b?j:g)<=(p>q?p:q)){m=0;return m|0}b=c[144]|0;p=+h[64664+(b*688&-1)>>3];r=+h[64672+(b*688&-1)>>3];do{if(p<r){if(n<p|n>r){m=0}else{break}return m|0}else{if(n<r|n>p){m=0}else{break}return m|0}}while(0);h[d>>3]=q;h[e>>3]=n;h[d+8>>3]=+h[o>>3];h[e+8>>3]=n;m=1;return m|0}if(l|f){n=f?j:g;l=c[144]|0;o=64664+(l*688&-1)|0;q=+h[o>>3];b=64672+(l*688&-1)|0;p=+h[b>>3];if((f?k:i)<=(q>p?q:p)){m=0;return m|0}f=c[200]|0;p=+h[64664+(f*688&-1)>>3];q=+h[64672+(f*688&-1)>>3];do{if(p<q){if(n<p|n>q){m=0}else{break}return m|0}else{if(n<q|n>p){m=0}else{break}return m|0}}while(0);h[d>>3]=n;h[e>>3]=+h[o>>3];h[d+8>>3]=n;h[e+8>>3]=+h[b>>3];m=1;return m|0}b=g==j;o=i==k;if(b&o){m=0;return m|0}if(b){b=c[200]|0;n=+h[64664+(b*688&-1)>>3];p=+h[64672+(b*688&-1)>>3];do{if(n<p){if(g<n|g>p){m=0}else{break}return m|0}else{if(g<p|g>n){m=0}else{break}return m|0}}while(0);b=c[144]|0;f=64664+(b*688&-1)|0;n=+h[f>>3];do{if(i<k){if(n<i|n>k){m=0}else{break}return m|0}else{if(n<k|n>i){m=0}else{break}return m|0}}while(0);h[d>>3]=g;h[e>>3]=+h[f>>3];h[d+8>>3]=g;h[e+8>>3]=+h[64672+(b*688&-1)>>3];m=1;return m|0}if(o){o=c[144]|0;n=+h[64664+(o*688&-1)>>3];p=+h[64672+(o*688&-1)>>3];do{if(n<p){if(i<n|i>p){m=0}else{break}return m|0}else{if(i<p|i>n){m=0}else{break}return m|0}}while(0);o=c[200]|0;n=+h[64664+(o*688&-1)>>3];do{if(g<j){if(n<g|n>j){m=0}else{break}return m|0}else{if(n<j|n>g){m=0}else{break}return m|0}}while(0);h[d>>3]=n;h[e>>3]=i;h[d+8>>3]=+h[64672+(o*688&-1)>>3];h[e+8>>3]=i;m=1;return m|0}else{o=c[200]|0;b=64664+(o*688&-1)|0;n=j-g;j=(+h[b>>3]-g)/n;f=64672+(o*688&-1)|0;p=(+h[f>>3]-g)/n;o=j>p;q=o?j:p;r=o?p:j;o=c[144]|0;l=64664+(o*688&-1)|0;j=k-i;k=(+h[l>>3]-i)/j;a=64672+(o*688&-1)|0;p=(+h[a>>3]-i)/j;o=k>p;s=o?k:p;t=o?p:k;k=r>t?r:t;t=k>0.0?k:0.0;k=q<s?q:s;s=k<1.0?k:1.0;if(t>s){m=0;return m|0}h[d>>3]=g+n*t;h[e>>3]=i+j*t;h[d+8>>3]=g+n*s;h[e+8>>3]=i+j*s;s=+h[b>>3];j=+h[f>>3];i=(j-s)*1.0e-5;n=s-i;s=j+i;i=+h[d>>3];if(n<s){if(!(i<n|i>s)){u=11778}}else{if(!(i<s|i>n)){u=11778}}do{if((u|0)==11778){n=+h[l>>3];i=+h[a>>3];s=(i-n)*1.0e-5;j=n-s;n=i+s;s=+h[e>>3];if(j<n){if(s<j|s>n){break}else{m=1}return m|0}else{if(s<n|s>j){break}else{m=1}return m|0}}}while(0);m=0;return m|0}return 0}function gd(a,b,d,e,f){a=a|0;b=b|0;d=+d;e=+e;f=+f;var g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0;g=i;i=i+24|0;j=g|0;k=g+8|0;l=g+16|0;m=c[3524]|0;n=c[200]|0;c[j>>2]=~~(+(c[64768+(n*688&-1)>>2]|0)+(d- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]+.5);n=c[144]|0;d=+(c[64768+(n*688&-1)>>2]|0);o=+h[64664+(n*688&-1)>>3];p=+h[64776+(n*688&-1)>>3];c[k>>2]=~~(d+(e-o)*p+.5);c[l>>2]=~~(d+(f-o)*p+.5);if((fl(j,k,j,l)|0)==0){i=g;return}n=c[j>>2]|0;cN[c[m+56>>2]&255](n,c[k>>2]|0);k=c[l>>2]|0;cN[c[m+60>>2]&255](n,k);c[a>>2]=n;c[b>>2]=k;i=g;return}function ge(a,b,d,e,f){a=a|0;b=b|0;d=+d;e=+e;f=+f;var g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0;g=i;i=i+24|0;j=g|0;k=g+8|0;l=g+16|0;m=c[3524]|0;n=c[200]|0;o=+(c[64768+(n*688&-1)>>2]|0);p=+h[64664+(n*688&-1)>>3];q=+h[64776+(n*688&-1)>>3];c[j>>2]=~~(o+(d-p)*q+.5);c[k>>2]=~~(o+(e-p)*q+.5);n=c[144]|0;c[l>>2]=~~(+(c[64768+(n*688&-1)>>2]|0)+(f- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]+.5);if((fl(j,l,k,l)|0)==0){i=g;return}n=c[l>>2]|0;cN[c[m+56>>2]&255](c[j>>2]|0,n);j=c[k>>2]|0;cN[c[m+60>>2]&255](j,n);c[a>>2]=j;c[b>>2]=n;i=g;return}function gf(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0.0,i=0.0,j=0.0,k=0.0,l=0.0,m=0.0,n=0,o=0.0,p=0.0,q=0.0,r=0.0;f=b-1|0;g=+h[a+(f<<6)+8>>3];i=+h[a+(f<<6)+16>>3];j=+h[a+(b<<6)+8>>3];k=+h[a+(b<<6)+16>>3];if((c[a+(b<<6)>>2]|0)==0){b=c[200]|0;a=64664+(b*688&-1)|0;l=+h[a>>3];f=64672+(b*688&-1)|0;m=+h[f>>3];if(l<m){if(g<l|g>m){n=11829}else{n=11821}}else{if(g<m|g>l){n=11829}else{n=11821}}if((n|0)==11829){h[e>>3]=k;l=+h[a>>3];m=+h[f>>3];do{if(l<m){if(j>m){o=m;break}if(j>=l){o=j;break}o=l}else{if(j>l){o=l;break}if(j>=m){o=j;break}o=m}}while(0);h[d>>3]=o;return}else if((n|0)==11821){h[d>>3]=g;f=c[144]|0;o=+h[64664+(f*688&-1)>>3];m=+h[64672+(f*688&-1)>>3];do{if(o<m){if(i>m){p=m;break}if(i>=o){p=i;break}p=o}else{if(i>o){p=o;break}if(i>=m){p=i;break}p=m}}while(0);h[e>>3]=p;return}}else{f=c[144]|0;a=64664+(f*688&-1)|0;p=+h[a>>3];b=64672+(f*688&-1)|0;m=+h[b>>3];if(p<m){if(k<p|k>m){n=11848}else{n=11840}}else{if(k<m|k>p){n=11848}else{n=11840}}if((n|0)==11840){h[e>>3]=k;f=c[200]|0;p=+h[64664+(f*688&-1)>>3];m=+h[64672+(f*688&-1)>>3];do{if(p<m){if(j>m){q=m;break}if(j>=p){q=j;break}q=p}else{if(j>p){q=p;break}if(j>=m){q=j;break}q=m}}while(0);h[d>>3]=q;return}else if((n|0)==11848){h[d>>3]=g;g=+h[a>>3];q=+h[b>>3];do{if(g<q){if(k>q){r=q;break}if(k>=g){r=k;break}r=g}else{if(k>g){r=g;break}if(k>=q){r=k;break}r=q}}while(0);h[e>>3]=r;return}}}function gg(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0.0,i=0.0,j=0.0,k=0.0,l=0.0,m=0.0,n=0,o=0.0,p=0.0,q=0.0,r=0.0;f=b-1|0;g=+h[a+(f<<6)+8>>3];i=+h[a+(f<<6)+16>>3];j=+h[a+(b<<6)+8>>3];k=+h[a+(b<<6)+16>>3];if((c[a+(b<<6)>>2]|0)==0){b=c[144]|0;a=64664+(b*688&-1)|0;l=+h[a>>3];f=64672+(b*688&-1)|0;m=+h[f>>3];if(l<m){if(i<l|i>m){n=11873}else{n=11865}}else{if(i<m|i>l){n=11873}else{n=11865}}if((n|0)==11865){h[e>>3]=i;b=c[200]|0;l=+h[64664+(b*688&-1)>>3];m=+h[64672+(b*688&-1)>>3];do{if(l<m){if(g>m){o=m;break}if(g>=l){o=g;break}o=l}else{if(g>l){o=l;break}if(g>=m){o=g;break}o=m}}while(0);h[d>>3]=o;return}else if((n|0)==11873){h[d>>3]=j;o=+h[a>>3];m=+h[f>>3];do{if(o<m){if(i>m){p=m;break}if(i>=o){p=i;break}p=o}else{if(i>o){p=o;break}if(i>=m){p=i;break}p=m}}while(0);h[e>>3]=p;return}}else{f=c[200]|0;a=64664+(f*688&-1)|0;p=+h[a>>3];b=64672+(f*688&-1)|0;m=+h[b>>3];if(p<m){if(j<p|j>m){n=11892}else{n=11884}}else{if(j<m|j>p){n=11892}else{n=11884}}if((n|0)==11892){h[e>>3]=i;i=+h[a>>3];p=+h[b>>3];do{if(i<p){if(j>p){q=p;break}if(j>=i){q=j;break}q=i}else{if(j>i){q=i;break}if(j>=p){q=j;break}q=p}}while(0);h[d>>3]=q;return}else if((n|0)==11884){h[d>>3]=j;d=c[144]|0;j=+h[64664+(d*688&-1)>>3];q=+h[64672+(d*688&-1)>>3];do{if(j<q){if(k>q){r=q;break}if(k>=j){r=k;break}r=j}else{if(k>j){r=j;break}if(k>=q){r=k;break}r=q}}while(0);h[e>>3]=r;return}}}function gh(b,d,e,f,g,j,k,l,m){b=+b;d=+d;e=+e;f=+f;g=+g;j=+j;k=+k;l=+l;m=m|0;var n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,v=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0,C=0.0,D=0.0,E=0.0,F=0.0,G=0,H=0,I=0.0,J=0.0,K=0,L=0,M=0.0;n=i;i=i+224|0;o=n|0;p=n+96|0;q=c[m+304>>2]|0;r=+h[64664+(q*688&-1)>>3];s=+h[64672+(q*688&-1)>>3];t=r<s?r:s;u=r>s?r:s;do{if(t<u){if(!(t>b|u<b)){break}if(!(t>g|u<g)){break}i=n;return}else{if(!(u>b|t<b)){break}if(!(u>g|t<g)){break}i=n;return}}while(0);s=g-b;if(t>b){r=t-b;v=t;w=(k-e)*r/s+e;x=(l-f)*r/s+f}else{v=b;w=e;x=f}if(u<g){f=u-g;y=u;z=f*(k-w)/s+k;A=f*(l-x)/s+l}else{y=g;z=k;A=l}q=(a[30528]&1)==0;l=y-v;B=c[m+308>>2]|0;k=+h[64664+(B*688&-1)>>3];g=+h[64672+(B*688&-1)>>3];s=k<g?k:g;f=k>g?k:g;if(w<s&x<s&z<s&A<s){i=n;return}if(w>f&x>f&z>f&A>f){i=n;return}B=c[200]|0;g=+(c[64768+(B*688&-1)>>2]|0);k=+h[64664+(B*688&-1)>>3];u=+h[64776+(B*688&-1)>>3];e=+(~~(g+(v-k)*u+.5)|0);h[p>>3]=e;B=c[144]|0;b=+(c[64768+(B*688&-1)>>2]|0);r=+h[64664+(B*688&-1)>>3];t=+h[64776+(B*688&-1)>>3];C=+(~~(b+(w-r)*t+.5)|0);h[p+8>>3]=C;D=+(~~(g+((q?v:d)-k)*u+.5)|0);h[p+16>>3]=D;d=+(~~(b+(x-r)*t+.5)|0);h[p+24>>3]=d;E=s-x;F=s-A;if(E!=F&E*F<0.0){h[p+40>>3]=+(~~(b+(s-r)*t+.5)|0);h[p+32>>3]=+(~~(g+(v+l*E/(E-F)-k)*u+.5)|0);G=3}else{G=2}F=f-x;E=f-A;if(F!=E&F*E<0.0){h[p+(G<<4)+8>>3]=+(~~(b+(f-r)*t+.5)|0);h[p+(G<<4)>>3]=+(~~(g+(v+l*F/(F-E)-k)*u+.5)|0);H=G+1|0}else{H=G}E=+(~~(g+((q?y:j)-k)*u+.5)|0);h[p+(H<<4)>>3]=E;j=+(~~(b+(A-r)*t+.5)|0);G=H+1|0;h[p+(H<<4)+8>>3]=j;F=+(~~(g+(y-k)*u+.5)|0);h[p+(G<<4)>>3]=F;y=+(~~(b+(z-r)*t+.5)|0);B=H+2|0;h[p+(G<<4)+8>>3]=y;I=s-w;J=s-z;if(I!=J&I*J<0.0){h[p+(B<<4)+8>>3]=+(~~(b+(s-r)*t+.5)|0);h[p+(B<<4)>>3]=+(~~(g+(v+l*I/(I-J)-k)*u+.5)|0);K=H+3|0}else{K=B}J=f-w;I=f-z;if(J!=I&J*I<0.0){h[p+(K<<4)+8>>3]=+(~~(b+(f-r)*t+.5)|0);h[p+(K<<4)>>3]=+(~~(g+(v+l*J/(J-I)-k)*u+.5)|0);L=K+1|0}else{L=K}if((L|0)>0){K=~~(b+(s-r)*t+.5);B=~~(b+(f-r)*t+.5);H=(K|0)<(B|0);f=+(B|0);s=+(K|0);K=0;do{c[o+(K*12&-1)>>2]=~~+h[p+(K<<4)>>3];B=p+(K<<4)+8|0;I=+h[B>>3];do{if(H){if(I>f){h[B>>3]=f;M=f;break}if(I>=s){M=I;break}h[B>>3]=s;M=s}else{if(I>s){h[B>>3]=s;M=s;break}if(I>=f){M=I;break}h[B>>3]=f;M=f}}while(0);c[o+(K*12&-1)+4>>2]=~~M;K=K+1|0;}while((K|0)<(L|0))}if(q){c[o+(L*12&-1)>>2]=x-w+(A-z)<0.0&1}else{z=+(~~(g+(0.0-k)*u+.5)|0);u=+(~~(b+(0.0-r)*t+.5)|0);t=e-z;e=C-u;C=D-z;D=d-u;d=F-z;F=y-u;y=E-z;z=j-u;c[o+(L*12&-1)>>2]=C*C+D*D-(t*t+e*e)+(y*y+z*z-(d*d+F*F))<0.0&1}gj(L,o|0,m);i=n;return}function gi(a,b,d,e,f,g){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var i=0,j=0,k=0.0,l=0.0,m=0.0,n=0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,v=0.0;i=e|d;if((i|0)==12){j=(g&1|0)!=0;g=c[200]|0;if(j){k=+h[64664+(g*688&-1)>>3];l=(k-k)*+h[64776+(g*688&-1)>>3]}else{l=(+h[64672+(g*688&-1)>>3]- +h[64664+(g*688&-1)>>3])*+h[64776+(g*688&-1)>>3]}c[a+((c[b>>2]|0)*12&-1)>>2]=~~(+(c[64768+(g*688&-1)>>2]|0)+l+.5);g=c[144]|0;if((d&8|0)==0){l=+h[64664+(g*688&-1)>>3];m=(l-l)*+h[64776+(g*688&-1)>>3]}else{m=(+h[64672+(g*688&-1)>>3]- +h[64664+(g*688&-1)>>3])*+h[64776+(g*688&-1)>>3]}n=~~(+(c[64768+(g*688&-1)>>2]|0)+m+.5);g=c[b>>2]|0;c[b>>2]=g+1;c[a+(g*12&-1)+4>>2]=n;n=c[200]|0;if(j){m=+h[64664+(n*688&-1)>>3];o=(m-m)*+h[64776+(n*688&-1)>>3]}else{o=(+h[64672+(n*688&-1)>>3]- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]}c[a+((c[b>>2]|0)*12&-1)>>2]=~~(+(c[64768+(n*688&-1)>>2]|0)+o+.5);n=c[144]|0;if((e&8|0)==0){o=+h[64664+(n*688&-1)>>3];p=(o-o)*+h[64776+(n*688&-1)>>3]}else{p=(+h[64672+(n*688&-1)>>3]- +h[64664+(n*688&-1)>>3])*+h[64776+(n*688&-1)>>3]}j=~~(+(c[64768+(n*688&-1)>>2]|0)+p+.5);n=c[b>>2]|0;c[b>>2]=n+1;c[a+(n*12&-1)+4>>2]=j;return}else if((i|0)==3){j=c[200]|0;if((d&1|0)==0){q=(+h[64672+(j*688&-1)>>3]- +h[64664+(j*688&-1)>>3])*+h[64776+(j*688&-1)>>3]}else{p=+h[64664+(j*688&-1)>>3];q=(p-p)*+h[64776+(j*688&-1)>>3]}c[a+((c[b>>2]|0)*12&-1)>>2]=~~(+(c[64768+(j*688&-1)>>2]|0)+q+.5);j=(f&8|0)!=0;f=c[144]|0;if(j){r=(+h[64672+(f*688&-1)>>3]- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]}else{q=+h[64664+(f*688&-1)>>3];r=(q-q)*+h[64776+(f*688&-1)>>3]}d=~~(+(c[64768+(f*688&-1)>>2]|0)+r+.5);f=c[b>>2]|0;c[b>>2]=f+1;c[a+(f*12&-1)+4>>2]=d;d=c[200]|0;if((e&1|0)==0){s=(+h[64672+(d*688&-1)>>3]- +h[64664+(d*688&-1)>>3])*+h[64776+(d*688&-1)>>3]}else{r=+h[64664+(d*688&-1)>>3];s=(r-r)*+h[64776+(d*688&-1)>>3]}c[a+((c[b>>2]|0)*12&-1)>>2]=~~(+(c[64768+(d*688&-1)>>2]|0)+s+.5);d=c[144]|0;if(j){t=(+h[64672+(d*688&-1)>>3]- +h[64664+(d*688&-1)>>3])*+h[64776+(d*688&-1)>>3]}else{s=+h[64664+(d*688&-1)>>3];t=(s-s)*+h[64776+(d*688&-1)>>3]}j=~~(+(c[64768+(d*688&-1)>>2]|0)+t+.5);d=c[b>>2]|0;c[b>>2]=d+1;c[a+(d*12&-1)+4>>2]=j;return}else{j=c[200]|0;if((i&1|0)==0){u=(+h[64672+(j*688&-1)>>3]- +h[64664+(j*688&-1)>>3])*+h[64776+(j*688&-1)>>3]}else{t=+h[64664+(j*688&-1)>>3];u=(t-t)*+h[64776+(j*688&-1)>>3]}c[a+((c[b>>2]|0)*12&-1)>>2]=~~(+(c[64768+(j*688&-1)>>2]|0)+u+.5);j=c[144]|0;if((i&8|0)==0){u=+h[64664+(j*688&-1)>>3];v=(u-u)*+h[64776+(j*688&-1)>>3]}else{v=(+h[64672+(j*688&-1)>>3]- +h[64664+(j*688&-1)>>3])*+h[64776+(j*688&-1)>>3]}i=~~(+(c[64768+(j*688&-1)>>2]|0)+v+.5);j=c[b>>2]|0;c[b>>2]=j+1;c[a+(j*12&-1)+4>>2]=i;return}}function gj(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;if((a|0)<1){return}e=c[d+244>>2]|0;do{if((e|0)==9){f=c[200]|0;c[b+(a*12&-1)>>2]=~~(+(c[64768+(f*688&-1)>>2]|0)+(+h[d+248>>3]- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5);f=c[144]|0;c[b+(a*12&-1)+4>>2]=~~(+(c[64768+(f*688&-1)>>2]|0)+(+h[d+256>>3]- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5);g=a+1|0}else if((e|0)==1){c[b+(a*12&-1)>>2]=c[b+((a-1|0)*12&-1)>>2];f=a+1|0;c[b+(f*12&-1)>>2]=c[b>>2];i=c[16364]|0;c[b+(f*12&-1)+4>>2]=i;c[b+(a*12&-1)+4>>2]=i;g=a+2|0}else if((e|0)==4){c[b+(a*12&-1)+4>>2]=c[b+((a-1|0)*12&-1)+4>>2];i=a+1|0;c[b+(i*12&-1)+4>>2]=c[b+4>>2];f=c[16537]|0;c[b+(i*12&-1)>>2]=f;c[b+(a*12&-1)>>2]=f;g=a+2|0}else if((e|0)==5|(e|0)==7){f=c[200]|0;i=~~(+(c[64768+(f*688&-1)>>2]|0)+(+h[d+248>>3]- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5);f=a+1|0;c[b+(f*12&-1)>>2]=i;c[b+(a*12&-1)>>2]=i;c[b+(a*12&-1)+4>>2]=c[b+((a-1|0)*12&-1)+4>>2];c[b+(f*12&-1)+4>>2]=c[b+4>>2];if((a|0)>0){f=0;j=0;while(1){k=((c[b+(j*12&-1)>>2]|0)+f|0)-i|0;l=j+1|0;if((l|0)<(a|0)){f=k;j=l}else{m=k;break}}}else{m=0}n=a+2|0;o=m;p=12004}else if((e|0)==3){c[b+(a*12&-1)>>2]=c[b+((a-1|0)*12&-1)>>2];j=a+1|0;c[b+(j*12&-1)>>2]=c[b>>2];f=c[16365]|0;c[b+(j*12&-1)+4>>2]=f;c[b+(a*12&-1)+4>>2]=f;g=a+2|0}else if((e|0)==6|(e|0)==8){f=c[144]|0;j=~~(+(c[64768+(f*688&-1)>>2]|0)+(+h[d+248>>3]- +h[64664+(f*688&-1)>>3])*+h[64776+(f*688&-1)>>3]+.5);f=a+1|0;c[b+(f*12&-1)+4>>2]=j;c[b+(a*12&-1)+4>>2]=j;c[b+(a*12&-1)>>2]=c[b+((a-1|0)*12&-1)>>2];c[b+(f*12&-1)>>2]=c[b>>2];if((a|0)>0){f=0;i=0;while(1){k=((c[b+(i*12&-1)+4>>2]|0)+f|0)-j|0;l=i+1|0;if((l|0)<(a|0)){f=k;i=l}else{q=k;break}}}else{q=0}n=a+2|0;o=q;p=12004}else if((e|0)==2){c[b+(a*12&-1)+4>>2]=c[b+((a-1|0)*12&-1)+4>>2];i=a+1|0;c[b+(i*12&-1)+4>>2]=c[b+4>>2];f=c[16536]|0;c[b+(i*12&-1)>>2]=f;c[b+(a*12&-1)>>2]=f;g=a+2|0}else if((e|0)==10|(e|0)==13){f=(c[b+(a*12&-1)>>2]|0)>0?1:-1;if((c[d+192>>2]|0)!=4){n=a;o=f;p=12004;break}i=b+24|0;j=c[i>>2]|0;k=(j|0)<(c[b>>2]|0)?-1:1;if((a|0)<=3){n=a;o=f;p=12004;break}l=b+36|0;r=c[l>>2]|0;if((j|0)==(r|0)){s=j-k|0;c[i>>2]=s;c[l>>2]=s;n=a;o=f;p=12004;break}if((a|0)<=4){n=a;o=f;p=12004;break}s=b+48|0;if((r|0)!=(c[s>>2]|0)){n=a;o=f;p=12004;break}i=r-k|0;c[l>>2]=i;c[s>>2]=i;n=a;o=f;p=12004}else{g=a}}while(0);do{if((p|0)==12004){a=c[d+264>>2]|0;if((a|0)>0&(o|0)<0){return}if(!((a|0)<0&(o|0)>0)){g=n;break}return}}while(0);n=c[d+192>>2]|0;if((n|0)==1|(n|0)==4){t=(c[d+196>>2]<<4&65520)+n|0}else if((n|0)==2|(n|0)==5){t=(c[d+200>>2]<<4&65520)+n|0}else{t=0}c[b+8>>2]=t;cN[c[(c[3524]|0)+148>>2]&255](g,b);return}function gk(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;b=c[8978]|0;if((b|0)==0){return}if((c[8982]|0)>0){d=0;e=b;while(1){uu(c[e+(d<<4)>>2]|0);f=c[8978]|0;do{if((a[f+(d<<4)+12|0]&1)==0){g=f}else{h=c[f+(d<<4)+8>>2]|0;if((h|0)==0){g=f;break}else{i=h}while(1){uu(c[i>>2]|0);h=c[i+4>>2]|0;uu(i);if((h|0)==0){break}else{i=h}}g=c[8978]|0}}while(0);f=d+1|0;if((f|0)<(c[8982]|0)){d=f;e=g}else{j=g;break}}}else{j=b}uu(j);c[8978]=0;c[8982]=0;return}function gl(a,b){a=a|0;b=b|0;return aY(c[a>>2]|0,c[b>>2]|0)|0}function gm(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0;f=i;i=i+2048|0;g=f|0;h=f+1024|0;c[b0()>>2]=0;if((a_(39e3,d|0,1024)|0)!=0){gk()}do{if((c[8978]|0)==0){j=h|0;k=bF(d|0,193632)|0;c[9748]=k;if((k|0)==0){l=-1;i=f;return l|0}n=(a6(j|0,1023,k|0)|0)!=0;if(!(n&(a[j]|0)==63)){l=-1;i=f;return l|0}L16406:do{if((cj(c[9748]|0)|0)==0){n=h+1|0;k=1;p=0;q=0;L16408:while(1){if((a[j]|0)==63){a[h+(uA(n|0)|0)|0]=0;r=ut(20)|0;if((r|0)==0){gk();s=ut(20)|0;if((s|0)==0){t=12038;break}else{u=s}}else{u=r}r=u;c[u>>2]=bP(n|0)|0;c[u+16>>2]=c[8980];c[8980]=r;c[8982]=(c[8982]|0)+1;a[u+12|0]=k;c[u+8>>2]=0;c[u+4>>2]=0;s=bc(c[9748]|0)|0;if((a6(j|0,1023,c[9748]|0)|0)==0){x=s;y=r}else{k=0;p=s;q=r;continue}}else{x=p;y=q}r=ut(8)|0;if((r|0)==0){gk();s=ut(8)|0;if((s|0)==0){t=12042;break}else{z=s}}else{z=r}r=z;c[z>>2]=bP(j|0)|0;c[z+4>>2]=0;s=(a6(j|0,1023,c[9748]|0)|0)==0;if(s|(a[j]|0)==63){A=y}else{s=r;while(1){B=ut(8)|0;if((B|0)==0){gk();C=ut(8)|0;if((C|0)==0){t=12046;break L16408}else{D=C}}else{D=B}B=D;c[D>>2]=bP(j|0)|0;c[D+4>>2]=0;c[s+4>>2]=B;C=(a6(j|0,1023,c[9748]|0)|0)==0;if(C|(a[j]|0)==63){A=y;break}else{s=B}}}while(1){c[A+4>>2]=x;c[A+8>>2]=r;E=c[A+16>>2]|0;if((a[A+12|0]&1)!=0|(E|0)==0){break}else{A=E}}if((cj(c[9748]|0)|0)==0){k=1;p=x;q=E}else{break L16406}}if((t|0)==12038){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=197768,v)|0);return 0}else if((t|0)==12042){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=202984,v)|0);return 0}else if((t|0)==12046){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=202984,v)|0);return 0}}}while(0);j=c[9748]|0;az(j|0);j=(c[8982]<<4)+16|0;q=ut(j)|0;do{if((q|0)==0){gk();p=ut(j)|0;if((p|0)!=0){F=p;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=206864,v)|0);return 0}else{F=q}}while(0);q=F;c[8978]=q;j=c[8980]|0;if((j|0)==0){G=q}else{p=j;j=0;k=q;while(1){c[k+(j<<4)>>2]=c[p>>2];c[(c[8978]|0)+(j<<4)+4>>2]=c[p+4>>2];c[(c[8978]|0)+(j<<4)+8>>2]=c[p+8>>2];a[(c[8978]|0)+(j<<4)+12|0]=a[p+12|0]&1;q=c[p+16>>2]|0;uu(p);if((q|0)==0){break}p=q;j=j+1|0;k=c[8978]|0}G=c[8978]|0}c[G+(c[8982]<<4)>>2]=0;c[(c[8978]|0)+(c[8982]<<4)+4>>2]=0;c[(c[8978]|0)+(c[8982]<<4)+8>>2]=0;bM(c[8978]|0,c[8982]|0,16,32);uF(39e3,d|0,1024);if((uA(d|0)|0)>>>0<1024){break}a[40023]=0}}while(0);d=c[8978]|0;G=d|0;F=c[G>>2]|0;if((F|0)==0){l=1;i=f;return l|0}E=bk(b|0,73808)|0;x=d;d=G;G=F;L16448:while(1){if((a_(b|0,G|0,E|0)|0)==0){do{if((a[G+E|0]|0)==0){H=G}else{F=x+16|0;A=F|0;y=c[A>>2]|0;if((y|0)==0){H=G;break}else{I=G;J=0;K=F;L=A;M=y}L16454:while(1){y=I+E|0;A=K;F=L;D=M;while(1){if((a_(G|0,D|0,E|0)|0)!=0){break L16454}if((a_(D|0,I|0,(bk(y|0,73808)|0)+E|0)|0)!=0){break}z=A+16|0;u=z|0;k=c[u>>2]|0;if((k|0)==0){break L16454}else{A=z;F=u;D=k}}if(J){N=D}else{y=c[m>>2]|0;cf(y|0,72704,(v=i,i=i+16|0,c[v>>2]=E,c[v+8>>2]=G,v)|0);y=c[m>>2]|0;cf(y|0,123448,(v=i,i=i+8|0,c[v>>2]=I,v)|0);N=c[F>>2]|0}cf(c[m>>2]|0,123448,(v=i,i=i+8|0,c[v>>2]=N,v)|0);y=A+16|0;k=y|0;u=c[k>>2]|0;if((u|0)==0){O=45032;break L16448}else{I=c[F>>2]|0;J=1;K=y;L=k;M=u}}if(J){O=45032;break L16448}H=c[d>>2]|0}}while(0);u=uA(H|0)|0;if((u|0)==(E|0)){P=E}else{k=u-E|0;y=(uA(b|0)|0)+k|0;if(y>>>0<E>>>0){Q=H}else{z=y;do{a[b+z|0]=a[b+(z-k|0)|0]|0;z=z-1|0;}while(z>>>0>=E>>>0);Q=c[d>>2]|0}uF(b|0,Q|0,u|0);P=u}if((a[b+P|0]|0)!=32){t=12080;break}z=P+1|0;R=x-16|0;S=(bk(b+z|0,73808)|0)+z|0}else{R=x;S=E}z=R+16|0;k=z|0;y=c[k>>2]|0;if((y|0)==0){l=1;t=12159;break}else{E=S;x=z;d=k;G=y}}if((t|0)==12159){i=f;return l|0}do{if((t|0)==12080){if((x|0)==0){l=1}else{O=x;break}i=f;return l|0}}while(0);x=O+8|0;if((c[x>>2]|0)==0){l=0;i=f;return l|0}gq();G=(e|0)==0;if(G){t=12084}else{if((a[e]&1)==0){t=12084}}do{if((t|0)==12084){d=c[x>>2]|0;if((d|0)==0){break}else{T=d}do{d=c[T>>2]|0;S=c[8250]|0;if((S|0)==(c[m>>2]|0)){if((c[8238]|0)<((c[6318]|0)-2|0)){U=S}else{aI(125440,23,1,S|0);do{E=a1(c[o>>2]|0)|0}while(!((E|0)==(-1|0)|(E|0)==10));c[8238]=0;U=c[m>>2]|0}aK(d|0,U|0);c[8238]=(c[8238]|0)+1}else{aK(d|0,S|0)}T=c[T+4>>2]|0;}while((T|0)!=0)}}while(0);T=h|0;a[T]=0;U=O|0;x=c[U>>2]|0;u=uA(x|0)|0;E=O+16|0;R=c[E>>2]|0;do{if((R|0)==0){V=0}else{b=(u|0)==0;P=u+1|0;Q=O;H=0;J=0;M=0;L=E;K=R;I=x;L16505:while(1){N=(J|0)==0;y=Q;k=L;z=K;while(1){if((a_(z|0,I|0,u|0)|0)!=0){W=H;X=M;break L16505}j=z+u|0;if(b){if((a[y+28|0]&1)!=0){Y=j;t=12102}}else{if((a[j]|0)!=32){W=H;X=M;break L16505}Y=z+P|0;t=12102}if((t|0)==12102){t=0;if(N){break}if((a_(Y|0,J|0,bk(Y|0,73808)|0)|0)!=0){break}}j=k+16|0;p=c[j>>2]|0;if((p|0)==0){W=H;X=M;break L16505}else{y=k;k=j;z=p}}if((H|0)==0){if(b){uD(T|0,77784,25)}else{uD(T|0,94064,26);ck(T|0,I|0,996);z=h+(uA(T|0)|0)|0;a[z]=a[86832]|0;a[z+1|0]=a[86833|0]|0;a[z+2|0]=a[86834|0]|0}z=c[8250]|0;y=c[m>>2]|0;if((z|0)==(y|0)){if((c[8238]|0)<((c[6318]|0)-2|0)){Z=z}else{aI(125440,23,1,z|0);do{z=a1(c[o>>2]|0)|0}while(!((z|0)==(-1|0)|(z|0)==10));c[8238]=0;Z=c[m>>2]|0}aK(T|0,Z|0);c[8238]=(c[8238]|0)+1}else{aK(T|0,y|0)}a[T]=0;_=1}else{_=H}z=M+1|0;c[g+(M<<2)>>2]=Y;N=k+16|0;S=c[N>>2]|0;if((S|0)==0){W=_;X=z;break}Q=k;H=_;J=Y;M=z;L=N;K=S;I=c[U>>2]|0}if((X|0)>0){$=0;aa=0;ab=0}else{V=W;break}while(1){I=c[g+($<<2)>>2]|0;K=bk(I|0,73808)|0;L=(ab|0)==0?4:aa;if((L|0)>0){M=0;do{J=h+(uA(T|0)|0)|0;w=32;a[J]=w&255;w=w>>8;a[J+1|0]=w&255;M=M+1|0;}while((M|0)<(L|0))}ck(T|0,I|0,K|0);L=18-K|0;if((L|0)<1){M=((K-18|0)>>>0)/18>>>0;ac=(36-K|0)+(M*18&-1)|0;ad=(ab+1|0)+M|0}else{ac=L;ad=ab}L=ad+1|0;if((L|0)>3){M=h+(uA(T|0)|0)|0;w=10;a[M]=w&255;w=w>>8;a[M+1|0]=w&255;M=c[8250]|0;k=c[m>>2]|0;if((M|0)==(k|0)){if((c[8238]|0)<((c[6318]|0)-2|0)){ae=M}else{aI(125440,23,1,M|0);do{M=a1(c[o>>2]|0)|0}while(!((M|0)==(-1|0)|(M|0)==10));c[8238]=0;ae=c[m>>2]|0}aK(T|0,ae|0);c[8238]=(c[8238]|0)+1}else{aK(T|0,k|0)}a[T]=0;af=0}else{af=L}K=$+1|0;if((K|0)<(X|0)){$=K;aa=ac;ab=af}else{break}}if(!((W|0)>0&(af|0)>0)){V=W;break}K=h+(uA(T|0)|0)|0;w=10;a[K]=w&255;w=w>>8;a[K+1|0]=w&255;K=c[8250]|0;I=c[m>>2]|0;if((K|0)!=(I|0)){aK(T|0,I|0);V=W;break}if((c[8238]|0)<((c[6318]|0)-2|0)){ag=K}else{aI(125440,23,1,K|0);do{K=a1(c[o>>2]|0)|0}while(!((K|0)==(-1|0)|(K|0)==10));c[8238]=0;ag=c[m>>2]|0}aK(T|0,ag|0);c[8238]=(c[8238]|0)+1;V=W}}while(0);if(!G){a[e]=(V|0)!=0&1}V=c[8250]|0;e=c[m>>2]|0;if((V|0)==(e|0)){if((c[8238]|0)<((c[6318]|0)-2|0)){ah=V}else{aI(125440,23,1,V|0);do{V=a1(c[o>>2]|0)|0}while(!((V|0)==(-1|0)|(V|0)==10));c[8238]=0;ah=c[m>>2]|0}aF(10,ah|0);c[8238]=(c[8238]|0)+1}else{aF(10,e|0)}e=c[8250]|0;if((e|0)==(c[m>>2]|0)){l=0;i=f;return l|0}a2(e|0);l=0;i=f;return l|0}function gn(a,b){a=a|0;b=b|0;var d=0,e=0,f=0.0,g=0;d=c[11608]|0;e=c[228]|0;f=+h[e+((c[d+((c[a>>2]|0)*20&-1)+4>>2]|0)*48&-1)+16>>3]- +h[e+((c[d+((c[b>>2]|0)*20&-1)+4>>2]|0)*48&-1)+16>>3];if(f<-1.0e-5){g=-1;return g|0}g=f>1.0e-5&1;return g|0}function go(a,b){a=a|0;b=b|0;var d=0,e=0.0,f=0;d=c[7628]|0;e=+h[d+((c[a>>2]|0)*104&-1)+56>>3]- +h[d+((c[b>>2]|0)*104&-1)+56>>3];if(e<-1.0e-5){f=-1;return f|0}f=e>1.0e-5&1;return f|0}function gp(){var b=0,d=0,e=0;b=i;cf(c[m>>2]|0,125144,(v=i,i=i+8|0,c[v>>2]=(c[9744]|0)==0?150704:150856,v)|0);d=c[9734]|0;e=c[9738]|0;cf(c[m>>2]|0,93824,(v=i,i=i+24|0,c[v>>2]=c[9742],c[v+8>>2]=d,c[v+16>>2]=e,v)|0);e=c[9738]|0;if((e|0)==2){d=c[m>>2]|0;aI(77648,56,1,d|0)}else if((e|0)==1){aI(86680,65,1,c[m>>2]|0)}else if((e|0)==3){aI(72584,61,1,c[m>>2]|0)}else{aI(218160,60,1,c[m>>2]|0)}e=a[38960]|0?211688:179864;cf(c[m>>2]|0,212512,(v=i,i=i+16|0,c[v>>2]=a[38944]|0?211688:179864,c[v+8>>2]=e,v)|0);i=b;return}function gq(){var b=0,d=0,e=0,f=0,g=0;b=i;d=bU(75160)|0;do{if((d|0)!=0){if((a[d]|0)==0){break}if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=bt(d|0,137896)|0;c[8250]=e;if((e|0)==0){break}i=b;return}}while(0);c[8250]=c[m>>2];d=bU(139752)|0;if((d|0)==0){f=12184}else{e=aE(d|0,0,0)|0;if((e|0)<3){f=12184}else{g=e}}if((f|0)==12184){g=24}c[6318]=g;c[8238]=0;i=b;return}function gr(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0.0,P=0.0,Q=0.0;b=i;i=i+72|0;d=b|0;e=b+24|0;f=b+48|0;g=c[13898]|0;j=c[8272]|0;if((g|0)>=(j|0)){i=b;return}k=e|0;l=e+8|0;m=d|0;n=d+8|0;o=f|0;p=f+8|0;q=g;g=j;L16625:while(1){r=c[1054]|0;j=(a[r+(q*40&-1)|0]&1)==0;s=c[r+(q*40&-1)+36>>2]|0;L16627:do{if(j){t=c[10036]|0;u=r+(q*40&-1)+32|0}else{w=r+(q*40&-1)+32|0;x=c[10036]|0;y=0;while(1){if((y|0)>=(s|0)){break}if((a[x+((c[w>>2]|0)+y|0)|0]|0)==(a[y+103664|0]|0)){y=y+1|0}else{t=x;u=w;break L16627}}if((y|0)==1){z=12257;break L16625}else{t=x;u=w}}}while(0);A=c[u>>2]|0;B=(s|0)>0;C=24304;D=153928;L16635:while(1){L16637:do{if(!j){if(B){E=0;F=0;G=A;while(1){H=a[D+E|0]|0;if(H<<24>>24==(a[t+(E+G|0)|0]|0)){I=G;J=F}else{if(H<<24>>24!=36){break L16637}I=G-1|0;J=1}K=E+1|0;if((K|0)<(J+s|0)){E=K;F=J;G=I}else{break}}if((J|0)==0){L=K}else{M=C;break L16635}}else{L=0}G=a[D+L|0]|0;if((G<<24>>24|0)==36|(G<<24>>24|0)==0){M=C;break L16635}}}while(0);w=C+8|0;x=c[w>>2]|0;if((x|0)==0){M=w;break}else{C=w;D=x}}D=c[M+4>>2]|0;if((D|0)==0){z=12247;break}else if((D|0)==1){z=12208;break}else if((D|0)==3){c[9742]=0;N=q}else if((D|0)==6){c[9738]=3;N=q}else if((D|0)==2){c[13898]=q+1;C=is(e)|0;s=c[C>>2]|0;if((s|0)==3){O=+uz(c[C+8>>2]|0,0)}else if((s|0)==2){O=+h[C+8>>3]}else if((s|0)==1){O=+(c[C+8>>2]|0)}else{z=12219;break}if((c[k>>2]|0)==3){uu(c[l>>2]|0);c[k>>2]=1}c[9742]=~~O;C=(c[13898]|0)-1|0;c[13898]=C;N=C}else if((D|0)==5){c[13898]=q+1;C=is(f)|0;s=c[C>>2]|0;if((s|0)==2){P=+h[C+8>>3]}else if((s|0)==1){P=+(c[C+8>>2]|0)}else if((s|0)==3){P=+uz(c[C+8>>2]|0,0)}else{z=12236;break}if((c[o>>2]|0)==3){uu(c[p>>2]|0);c[o>>2]=1}C=~~P;c[9738]=(C-1|0)>>>0>2?3:C;C=(c[13898]|0)-1|0;c[13898]=C;N=C}else if((D|0)==7){a[38944]=0;N=q}else if((D|0)==8){a[38944]=1;N=q}else if((D|0)==12){c[9744]=0;N=q}else if((D|0)==11){c[9744]=1;N=q}else if((D|0)==9){a[38960]=0;N=q}else if((D|0)==10){a[38960]=1;N=q}else if((D|0)==4){c[13898]=q+1;D=is(d)|0;C=c[D>>2]|0;if((C|0)==1){Q=+(c[D+8>>2]|0)}else if((C|0)==2){Q=+h[D+8>>3]}else if((C|0)==3){Q=+uz(c[D+8>>2]|0,0)}else{z=12228;break}if((c[m>>2]|0)==3){uu(c[n>>2]|0);c[m>>2]=1}c[9734]=~~Q;D=(c[13898]|0)-1|0;c[13898]=D;N=D}else{N=q}D=N+1|0;c[13898]=D;C=c[8272]|0;if((D|0)<(C|0)){q=D;g=C}else{z=12255;break}}if((z|0)==12257){i=b;return}else if((z|0)==12247){uf(q,139344,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==12208){c[9742]=1;c[9734]=3;c[9738]=1;a[38944]=0;a[38960]=0;c[9744]=0;N=q+1|0;c[13898]=N;if((N|0)>=(g|0)){i=b;return}if((a[r+(N*40&-1)|0]&1)==0){uf(N,170328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=c[r+(N*40&-1)+36>>2]|0;q=r+(N*40&-1)+32|0;r=0;while(1){if((r|0)>=(g|0)){break}if((a[t+((c[q>>2]|0)+r|0)|0]|0)==(a[r+103664|0]|0)){r=r+1|0}else{z=12251;break}}if((z|0)==12251){uf(N,170328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((r|0)==1){i=b;return}else{uf(N,170328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else if((z|0)==12255){i=b;return}else if((z|0)==12228){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==12219){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==12236){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function gs(){var a=0,b=0,d=0,e=0,f=0,g=0;a=i;if((c[9738]|0)<1){c[9738]=3}c[228]=0;b=ut(4800)|0;do{if((b|0)==0){gk();d=ut(4800)|0;if((d|0)!=0){e=d;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=129728,v)|0)}else{e=b}}while(0);c[228]=e;c[224]=100;c[225]=0;c[226]=100;c[227]=48;c[11608]=0;e=ut(2e3)|0;do{if((e|0)==0){gk();b=ut(2e3)|0;if((b|0)!=0){f=b;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=129728,v)|0)}else{f=e}}while(0);c[11608]=f;c[11604]=100;c[11605]=0;c[11606]=100;c[11607]=20;c[7628]=0;f=ut(10400)|0;if((f|0)!=0){g=f;c[7628]=g;c[7624]=100;c[7625]=0;c[7626]=100;c[7627]=104;i=a;return}gk();f=ut(10400)|0;if((f|0)==0){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=129728,v)|0)}else{g=f;c[7628]=g;c[7624]=100;c[7625]=0;c[7626]=100;c[7627]=104;i=a;return}}function gt(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;e=i;i=i+8|0;f=e|0;if((c[7625]|0)==0){un(a,b,d,d+40|0);i=e;return}g=c[228]|0;if((g|0)==0){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=c[225]|0;k=c[224]|0;do{if((j|0)<(k|0)){l=j;m=g}else{n=(c[226]|0)+k|0;if((n|0)==0){uu(g);c[228]=0;c[224]=0;c[225]=0;l=0;m=0;break}else{o=db(g,aa(c[227]|0,n),150384)|0;c[228]=o;c[224]=n;l=c[225]|0;m=o;break}}}while(0);c[225]=l+1;g=m;m=g+(l*48&-1)|0;k=a;c[m>>2]=c[k>>2];c[m+4>>2]=c[k+4>>2];c[m+8>>2]=c[k+8>>2];c[m+12>>2]=c[k+12>>2];c[m+16>>2]=c[k+16>>2];c[m+20>>2]=c[k+20>>2];c[m+24>>2]=c[k+24>>2];c[m+28>>2]=c[k+28>>2];c[m+32>>2]=c[k+32>>2];c[m+36>>2]=c[k+36>>2];c[m+40>>2]=c[k+40>>2];c[m+44>>2]=c[k+44>>2];k=(b|0)!=0;m=g+(l*48&-1)+24|0;if(k){c[m>>2]=0;g=c[228]|0;if((g|0)==0){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}a=c[225]|0;j=c[224]|0;do{if((a|0)<(j|0)){p=a;q=g}else{o=(c[226]|0)+j|0;if((o|0)==0){uu(g);c[228]=0;c[224]=0;c[225]=0;p=0;q=0;break}else{n=db(g,aa(c[227]|0,o),150384)|0;c[228]=n;c[224]=o;p=c[225]|0;q=n;break}}}while(0);c[225]=p+1;g=q;q=g+(p*48&-1)|0;j=b;c[q>>2]=c[j>>2];c[q+4>>2]=c[j+4>>2];c[q+8>>2]=c[j+8>>2];c[q+12>>2]=c[j+12>>2];c[q+16>>2]=c[j+16>>2];c[q+20>>2]=c[j+20>>2];c[q+24>>2]=c[j+24>>2];c[q+28>>2]=c[j+28>>2];c[q+32>>2]=c[j+32>>2];c[q+36>>2]=c[j+36>>2];c[q+40>>2]=c[j+40>>2];c[q+44>>2]=c[j+44>>2];c[g+(p*48&-1)+24>>2]=0;r=p}else{c[m>>2]=d;r=l}m=c[d+4>>2]|0;p=c[11608]|0;if((p|0)==0){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=c[11605]|0;j=c[11604]|0;do{if((g|0)<(j|0)){s=p;t=g}else{q=(c[11606]|0)+j|0;if((q|0)==0){uu(p);c[11608]=0;c[11604]=0;c[11605]=0;s=0;t=0;break}else{b=db(p,aa(c[11607]|0,q),150384)|0;c[11608]=b;c[11604]=q;s=b;t=c[11605]|0;break}}}while(0);p=c[11607]|0;c[11605]=t+1;j=aa(p,t);t=s+j|0;p=c[228]|0;g=s+(j+8|0)|0;c[g>>2]=m;c[s+(j+12|0)>>2]=d;c[s+(j+16|0)>>2]=-1;m=t;do{if(+h[p+(l*48&-1)+16>>3]<+h[p+(r*48&-1)+16>>3]){c[m>>2]=r;c[s+(j+4|0)>>2]=l;if((c[d+8>>2]|0)!=-10){break}c[g>>2]=-11}else{c[m>>2]=l;c[s+(j+4|0)>>2]=r;if((c[d+8>>2]|0)!=-10){break}c[g>>2]=-10}}while(0);g=c[11608]|0;d=(t-g|0)/20&-1;c[f>>2]=c[8022];t=g;gu(d,c[t+(d*20&-1)>>2]|0,c[t+(d*20&-1)+4>>2]|0,f);if((c[11608]|0)==0){uk(119400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[11605]|0;if((f|0)!=0){c[11605]=f-1}if((c[228]|0)==0){uk(119400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[225]|0;if((f|0)==0){u=0}else{d=f-1|0;c[225]=d;u=d}if((u|0)==0|k^1){i=e;return}c[225]=u-1;i=e;return}function gu(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0,w=0,x=0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0.0,P=0.0,Q=0.0,R=0.0,S=0.0,T=0.0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0.0,ai=0.0,aj=0.0,ak=0.0,al=0.0,am=0.0,an=0.0,ao=0.0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0.0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0;f=i;i=i+56|0;g=f|0;j=f+8|0;k=c[228]|0;l=k;m=l+(b*48&-1)|0;n=l+(d*48&-1)|0;o=+h[l+(b*48&-1)+16>>3]>+h[l+(d*48&-1)+16>>3];d=o?n:m;l=o?m:n;n=k;m=(l-n|0)/48&-1;o=(d-n|0)/48&-1;p=+h[d+16>>3];q=+h[l>>3];r=+h[d>>3];n=q>r;s=+h[l+8>>3];t=+h[d+8>>3];b=s>t;u=c[225]|0;w=c[e>>2]|0;do{if((w|0)>-1){x=j|0;y=p;z=b?s:t;A=b?t:s;B=n?q:r;C=n?r:q;D=d;E=l;F=o;G=m;H=w;L16789:while(1){I=c[7628]|0;J=I+(H*104&-1)|0;L16791:do{if(+h[I+(H*104&-1)+24>>3]<C){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y}else{if(+h[I+(H*104&-1)+16>>3]>B){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}if(+h[I+(H*104&-1)+40>>3]<A){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}if(+h[I+(H*104&-1)+32>>3]>z){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}T=+h[I+(H*104&-1)+56>>3];if(T<y){if(T>=p){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}c[e>>2]=H;K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}U=c[J>>2]|0;V=c[11608]|0;W=c[V+(a*20&-1)>>2]|0;do{if((U|0)==(W|0)){X=12326}else{Y=c[I+(H*104&-1)+4>>2]|0;if((Y|0)==(W|0)){X=12326;break}Z=c[I+(H*104&-1)+8>>2]|0;if((Z|0)==(W|0)){X=12326}else{_=Y;$=Z}}}while(0);if((X|0)==12326){X=0;W=c[V+(a*20&-1)+4>>2]|0;if((U|0)==(W|0)){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}Z=c[I+(H*104&-1)+4>>2]|0;if((Z|0)==(W|0)){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}Y=c[I+(H*104&-1)+8>>2]|0;if((Y|0)==(W|0)){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}else{_=Z;$=Y}}aa=c[228]|0;Y=aa;Z=E|0;T=+h[Z>>3];W=E+8|0;ab=+h[W>>3];ac=+h[E+16>>3];ad=+h[I+(H*104&-1)+64>>3];ae=+h[I+(H*104&-1)+72>>3];af=+h[I+(H*104&-1)+80>>3];ag=+h[I+(H*104&-1)+88>>3]+(T*ad+ab*ae+ac*af);if(ag==0.0){ah=0.0}else{ah=ag/(ad*(T- +h[D>>3])+ae*(ab- +h[D+8>>3])+af*(ac- +h[D+16>>3]))}ac=+h[Z>>3];af=+h[W>>3];ab=+h[Y+(U*48&-1)>>3];ae=+h[Y+(U*48&-1)+8>>3];T=+h[Y+(_*48&-1)>>3];ad=+h[Y+(_*48&-1)+8>>3];ag=T-ab;ai=ad-ae;aj=(af-ae)*ag-(ac-ab)*ai;if(aj==0.0){ak=0.0}else{ak=aj/(ai*(+h[D>>3]-ac)-ag*(+h[D+8>>3]-af))}af=+h[Z>>3];ag=+h[W>>3];ac=+h[Y+($*48&-1)>>3];ai=+h[Y+($*48&-1)+8>>3];aj=ac-T;al=ai-ad;am=(ag-ad)*aj-(af-T)*al;if(am==0.0){an=0.0}else{an=am/(al*(+h[D>>3]-af)-aj*(+h[D+8>>3]-ag))}ag=+h[Z>>3];aj=+h[W>>3];af=ab-ac;ab=ae-ai;ae=(aj-ai)*af-(ag-ac)*ab;if(ae==0.0){ao=0.0}else{ao=ae/(ab*(+h[D>>3]-ag)-af*(+h[D+8>>3]-aj))}h[x>>3]=0.0;if(ah>0.0&ah<1.0){h[j+8>>3]=ah;ap=2}else{ap=1}if(ak>0.0&ak<1.0){h[j+(ap<<3)>>3]=ak;aq=ap+1|0}else{aq=ap}if(an>0.0&an<1.0){h[j+(aq<<3)>>3]=an;ar=aq+1|0}else{ar=aq}if(ao>0.0&ao<1.0){h[j+(ar<<3)>>3]=ao;as=ar+1|0}else{as=ar}h[j+(as<<3)>>3]=1.0;L16832:do{if((as|0)>1){W=1;while(1){Z=W+1|0;Y=(Z|0)<(as|0);if(!Y){at=0;break L16832}au=j+(W<<3)|0;av=Z;do{aj=+h[au>>3];aw=j+(av<<3)|0;af=+h[aw>>3];if(aj>af){h[au>>3]=af;h[aw>>3]=aj}av=av+1|0;}while((av|0)<(as|0));if(Y){W=Z}else{at=0;break}}}else{at=0}}while(0);while(1){if((at|0)>=(as|0)){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break L16791}ax=+h[j+(at<<3)>>3];ay=gB(E,D,ax,J)|0;if((ay|0)==0){at=at+1|0}else{az=0;aA=at;break}}while(1){if((aA|0)>=(as|0)){break}U=aA+1|0;V=gB(E,D,+h[j+(U<<3)>>3],J)|0;if((V|0)==0){break}else{az=V;aA=U}}if((at|0)==(aA|0)){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}if((ay|0)==2&(az|0)==2){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}U=(aA|0)==(as|0);if((at|0)==0){if(U){break L16789}V=gC(G,F,+h[j+(aA<<3)>>3])|0;W=c[228]|0;av=W;au=av+(V*48&-1)|0;aw=av+(F*48&-1)|0;aB=+h[av+(V*48&-1)+16>>3]>+h[av+(F*48&-1)+16>>3];av=aB?aw:au;V=aB?au:aw;aw=W;aj=+h[V>>3];af=+h[av>>3];W=aj>af;ag=+h[V+8>>3];ab=+h[av+8>>3];au=ag>ab;K=(V-aw|0)/48&-1;L=(av-aw|0)/48&-1;M=V;N=av;O=W?af:aj;P=W?aj:af;Q=au?ab:ag;R=au?ag:ab;S=+h[av+16>>3];break}av=gC(G,F,ax)|0;if(U){U=c[228]|0;au=U;W=au+(G*48&-1)|0;V=au+(av*48&-1)|0;aw=+h[au+(G*48&-1)+16>>3]>+h[au+(av*48&-1)+16>>3];au=aw?V:W;aB=aw?W:V;V=U;ab=+h[aB>>3];ag=+h[au>>3];U=ab>ag;af=+h[aB+8>>3];aj=+h[au+8>>3];W=af>aj;K=(aB-V|0)/48&-1;L=(au-V|0)/48&-1;M=aB;N=au;O=U?ag:ab;P=U?ab:ag;Q=W?aj:af;R=W?af:aj;S=+h[au+16>>3];break}au=gC(G,F,+h[j+(aA<<3)>>3])|0;if((au|0)==(G|0)){K=G;L=F;M=E;N=D;O=C;P=B;Q=A;R=z;S=y;break}c[g>>2]=c[(c[7628]|0)+(H*104&-1)+100>>2];gu(a,au,F,g);au=c[228]|0;W=au;U=W+(G*48&-1)|0;aB=W+(av*48&-1)|0;V=+h[W+(G*48&-1)+16>>3]>+h[W+(av*48&-1)+16>>3];av=V?aB:U;W=V?U:aB;aB=au;aj=+h[W>>3];af=+h[av>>3];au=aj>af;ag=+h[W+8>>3];ab=+h[av+8>>3];U=ag>ab;K=(W-aB|0)/48&-1;L=(av-aB|0)/48&-1;M=W;N=av;O=au?af:aj;P=au?aj:af;Q=U?ab:ag;R=U?ag:ab;S=+h[av+16>>3]}}while(0);J=c[I+(H*104&-1)+100>>2]|0;if((J|0)>-1){y=S;z=R;A=Q;B=P;C=O;D=N;E=M;F=L;G=K;H=J}else{X=12367;break}}if((X|0)==12367){aC=L;aD=K;aE=c[228]|0;break}H=c[225]|0;if((H|0)<=(u|0)){i=f;return}G=(aa|0)==0;F=H;while(1){if(G){X=12358;break}if((F|0)==0){if((u|0)<0){F=0;continue}else{X=12385;break}}else{H=F-1|0;c[225]=H;if((H|0)>(u|0)){F=H;continue}else{X=12382;break}}}if((X|0)==12358){uk(119400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((X|0)==12385){i=f;return}else if((X|0)==12382){i=f;return}}else{aC=o;aD=m;aE=k}}while(0);k=aE;gw((c[11608]|0)+(a*20&-1)|0,k+(aD*48&-1)|0,k+(aC*48&-1)|0);aC=c[225]|0;if((aC|0)<=(u|0)){i=f;return}k=(c[228]|0)==0;aD=aC;while(1){if(k){X=12372;break}if((aD|0)==0){if((u|0)<0){aD=0;continue}else{X=12386;break}}else{aC=aD-1|0;c[225]=aC;if((aC|0)>(u|0)){aD=aC;continue}else{X=12384;break}}}if((X|0)==12384){i=f;return}else if((X|0)==12386){i=f;return}else if((X|0)==12372){uk(119400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function gv(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0.0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0.0,aC=0.0,aD=0.0,aE=0.0,aF=0.0,aG=0.0,aH=0.0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0.0,aW=0,aX=0.0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0;e=i;i=i+128|0;f=e|0;g=e+8|0;j=e+16|0;k=e+56|0;l=e+120|0;do{if((d|0)>0){m=0;n=0;o=0;p=-1;q=0;r=b;L16892:while(1){s=c[r+8>>2]|0;do{if((s|0)==4){t=p;u=o;w=n;x=m}else{y=c[r+260>>2]|0;z=c[y+8>>2]|0;if((a[r+236|0]&1)!=0){t=p;u=o;w=n;x=m;break}A=(z|0)>(p|0)?z:p;do{if((s|0)==2){if((y|0)==0){B=0}else{C=0;D=y;while(1){E=C+1|0;F=c[D>>2]|0;if((F|0)==0){B=E;break}else{C=E;D=F}}}G=B;H=aa(B,z)}else if((s|0)==3){D=c[r+252>>2]|0;if((c[r+244>>2]|0)!=0){G=D;H=aa(D,z);break}if((c[r+12>>2]|0)==225){G=D;H=z;break}if((y|0)==0){G=D;H=0;break}C=c[y>>2]|0;if((C|0)==0){G=D;H=z;break}else{I=z;J=C}while(1){C=I+(c[J+8>>2]|0)|0;F=c[J>>2]|0;if((F|0)==0){G=D;H=C;break}else{I=C;J=F}}}else{K=12401;break L16892}}while(0);y=c[r+12>>2]|0;if((y|0)==153|(y|0)==345|(y|0)==33|(y|0)==225){t=A;u=(H<<1)+o|0;w=H+n|0;x=m;break}else if((y|0)==352|(y|0)==51|(y|0)==177|(y|0)==193|(y|0)==209|(y|0)==1){y=H+o|0;D=(H-G|0)+n|0;if((c[r+244>>2]|0)==0){t=A;u=y;w=D;x=m;break}t=A;u=y;w=(((-(z<<1)|1)-G|0)+(H<<1)|0)+D|0;x=aa((G<<1)-2|0,z-1|0)+m|0;break}else{t=A;u=H+o|0;w=H+n|0;x=m;break}}}while(0);s=q+1|0;if((s|0)<(d|0)){m=x;n=w;o=u;p=t;q=s;r=c[r>>2]|0}else{break}}if((K|0)==12401){uk(164696,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((t|0)<1){break}r=c[228]|0;if((r|0)==0){uk(203872,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((u|0)==0){uu(r);c[228]=0;c[224]=0;c[225]=0}else{c[228]=db(r,aa(c[227]|0,u),150384)|0;c[224]=u}r=c[11608]|0;if((r|0)==0){uk(203872,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((w|0)==0){uu(r);c[11608]=0;c[11604]=0;c[11605]=0}else{c[11608]=db(r,aa(c[11607]|0,w),150384)|0;c[11604]=w}r=c[7628]|0;if((r|0)==0){uk(203872,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((x|0)==0){uu(r);c[7628]=0;c[7624]=0;c[7625]=0}else{c[7628]=db(r,aa(c[7627]|0,x),150384)|0;c[7624]=x}r=t<<3;q=ut(r)|0;do{if((q|0)==0){gk();p=ut(r)|0;if((p|0)!=0){L=p;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=155272,v)|0)}else{L=q}}while(0);q=L;p=ut(r)|0;do{if((p|0)==0){gk();o=ut(r)|0;if((o|0)!=0){M=o;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=151696,v)|0)}else{M=p}}while(0);p=M;r=t*12&-1;o=ut(r)|0;do{if((o|0)==0){gk();n=ut(r)|0;if((n|0)!=0){N=n;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=149768,v)|0)}else{N=o}}while(0);o=N;n=ut(r)|0;do{if((n|0)==0){gk();m=ut(r)|0;if((m|0)!=0){O=m;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=147944,v)|0)}else{O=n}}while(0);n=j;r=j+4|0;m=j+8|0;s=j+16|0;D=j|0;y=k|0;F=k+8|0;C=k+16|0;E=k+24|0;P=k+32|0;Q=O;R=o;S=p;T=q;U=0;V=b;L16959:while(1){W=a[V+239|0]|0;X=V+24|0;L16961:do{if((c[V+8>>2]|0)==4){Y=T;Z=S;_=R;$=Q}else{ab=V+260|0;ac=c[(c[ab>>2]|0)+8>>2]|0;if((a[V+236|0]&1)!=0){Y=T;Z=S;_=R;$=Q;break}ad=c[V+240>>2]|0;ae=(ad|0)==-9;af=V+12|0;ag=c[af>>2]|0;ah=(ag|0)==352;ai=ah?-3:ae?-8:(c[9742]|0)+ad|0;aj=ah?-3:ae?-8:ad;do{if((ag|0)==225){c[V+32>>2]=-10;ad=V+152|0;if((c[V+144>>2]|0)==0){h[ad>>3]=1.0;h[V+168>>3]=0.0}ae=c[200]|0;h[64776+(ae*688&-1)>>3]=+((c[7939]|0)-(c[7938]|0)|0)/(+h[64672+(ae*688&-1)>>3]- +h[64664+(ae*688&-1)>>3]);c[12912]=c[V+184>>2];c[12910]=0;ak=+h[ad>>3];if(ak<=0.0){break}uE(n|0,0,40);c[r>>2]=2;c[m>>2]=2;h[s>>3]=ak;c[D>>2]=c[V+160>>2];fN(j,f,g,123984);h[6458]=+h[V+168>>3];h[6457]=+h[V+176>>3];c[12910]=~~+h[f>>3]}}while(0);ag=c[ab>>2]|0;if((c[V+244>>2]|0)!=0){if((c[ag+8>>2]|0)>0){A=0;do{z=A*3&-1;c[R+(z+2<<2)>>2]=-3;c[R+(z+1<<2)>>2]=-3;c[R+(z<<2)>>2]=-3;z=A<<1;c[T+((z|1)<<2)>>2]=-3;c[T+(z<<2)>>2]=-3;A=A+1|0;al=c[ab>>2]|0;}while((A|0)<(c[al+8>>2]|0));if((al|0)==0){Y=T;Z=S;_=R;$=Q;break}else{am=al}}else{am=ag}A=(W&1)!=0;ab=V+68|0;z=am;ad=Q;ae=R;ah=S;an=T;ao=0;while(1){ap=c[z+12>>2]|0;aq=z+8|0;if((c[aq>>2]|0)>0){ar=(ao|0)>0;as=0;do{at=ap+(as<<6)|0;au=c[228]|0;if((au|0)==0){K=12531;break L16959}av=c[225]|0;aw=c[224]|0;do{if((av|0)<(aw|0)){ax=au;ay=av}else{az=(c[226]|0)+aw|0;if((az|0)==0){uu(au);c[228]=0;c[224]=0;c[225]=0;ax=0;ay=0;break}else{aA=db(au,aa(c[227]|0,az),150384)|0;c[228]=aA;c[224]=az;ax=aA;ay=c[225]|0;break}}}while(0);au=c[227]|0;c[225]=ay+1;aw=aa(au,ay);au=ax+aw|0;c[ax+(aw+24|0)>>2]=X;av=at|0;if((c[av>>2]|0)<(c[9738]|0)){aA=ap+(as<<6)+24|0;ak=+h[aA>>3];aB=(+h[ap+(as<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aC=(+h[ap+(as<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aD=(ak- +h[5279])*+h[2]+ +h[12]+-1.0;aE=+h[404]+aB*+h[392]+aC*+h[396]+aD*+h[400];aF=+h[405]+aB*+h[393]+aC*+h[397]+aD*+h[401];aG=+h[406]+aB*+h[394]+aC*+h[398]+aD*+h[402];aH=aG==0.0?1.0e-5:aG;h[au>>3]=(+h[403]+aB*+h[391]+aC*+h[395]+aD*+h[399])/aH;h[ax+(aw+8|0)>>3]=aE/aH;h[ax+(aw+16|0)>>3]=aF/aH;az=ax+(aw+32|0)|0;h[az>>3]=ak;c[ax+(aw+40|0)>>2]=0;if(A){h[az>>3]=+h[ap+(as<<6)+32>>3];c[ab>>2]=-6}else{h[az>>3]=+h[aA>>3]}c[ax+(aw+44|0)>>2]=at;aI=(au-(c[228]|0)|0)/48&-1}else{h[ax+(aw+16|0)>>3]=-2.0;aI=-1}aw=as*3&-1;au=ad+(aw+2<<2)|0;c[au>>2]=-3;aA=ad+(aw+1<<2)|0;c[aA>>2]=-3;az=ad+(aw<<2)|0;c[az>>2]=-3;aJ=as<<1;aK=ah+((aJ|1)<<2)|0;c[aK>>2]=-3;aL=ah+(aJ<<2)|0;c[aL>>2]=-3;aM=c[af>>2]|0;do{if((aM|0)==352|(aM|0)==51|(aM|0)==177|(aM|0)==193|(aM|0)==209|(aM|0)==1){if((as|0)<=0){if(!(ar&(aI|0)>-1)){break}c[aA>>2]=gy(aI,1,ac,X,aj)|0;break}if((aI|0)<0){if(!(ar&(a[38944]^1))){break}aN=gy((c[225]|0)-1|0,3,ac,X,aj)|0;c[au>>2]=aN;if((aN|0)<=-2){break}aO=gz((c[225]|0)-1|0,3,ac)|0;aP=as-1|0;gA(aN,c[ad+((aP*3&-1)+1<<2)>>2]|0,aO,c[ah+((aP<<1|1)<<2)>>2]|0,aj,ai);gA(aN,c[ae+(aw<<2)>>2]|0,aO,c[an+(aJ<<2)>>2]|0,aj,ai);break}aO=gy(aI,0,ac,X,aj)|0;c[az>>2]=aO;if(!ar){break}aN=gy(aI,1,ac,X,aj)|0;c[aA>>2]=aN;aP=gy(aI,2,ac,X,aj)|0;c[au>>2]=aP;aQ=(aO|0)>-2;if((aP|0)>-2){if(aQ){aR=gz(aI,2,ac)|0;c[aL>>2]=aR;aS=as-1|0;gA(aO,c[ad+((aS*3&-1)+1<<2)>>2]|0,aR,c[ah+((aS<<1|1)<<2)>>2]|0,aj,ai)}if((aN|0)>-2){aS=gz(aI,0,ac)|0;c[aK>>2]=aS;gA(aN,c[ae+(aw<<2)>>2]|0,aS,c[an+(aJ<<2)>>2]|0,aj,ai)}gA(aP,aP,c[aL>>2]|0,c[aK>>2]|0,aj,ai);break}else{if(!aQ){break}if(!((aN|0)>-2&(a[38944]^1))){break}aQ=gy(aI,3,ac,X,aj)|0;c[au>>2]=aQ;if((aQ|0)<=-2){break}aP=gz(aI,1,ac)|0;c[aK>>2]=aP;c[aL>>2]=aP;if((a[(c[7628]|0)+(aP*104&-1)+96|0]&1)!=0){break}c[(c[11608]|0)+(aQ*20&-1)+8>>2]=ai;c[(c[11608]|0)+(aN*20&-1)+8>>2]=ai;c[(c[11608]|0)+(aO*20&-1)+8>>2]=ai;break}}else if((aM|0)==153|(aM|0)==345|(aM|0)==33){if((aI|0)<0){break}aO=ap+(as<<6)+24|0;ak=+h[aO>>3];h[aO>>3]=(aM|0)==33?0.0:+h[8083];aN=c[228]|0;if((aN|0)==0){K=12564;break L16959}aQ=c[225]|0;aP=c[224]|0;do{if((aQ|0)<(aP|0)){aT=aN;aU=aQ}else{aS=(c[226]|0)+aP|0;if((aS|0)==0){uu(aN);c[228]=0;c[224]=0;c[225]=0;aT=0;aU=0;break}else{aR=db(aN,aa(c[227]|0,aS),150384)|0;c[228]=aR;c[224]=aS;aT=aR;aU=c[225]|0;break}}}while(0);aN=c[227]|0;c[225]=aU+1;aP=aa(aN,aU);aN=aT+aP|0;c[aT+(aP+24|0)>>2]=X;if((c[av>>2]|0)>=(c[9738]|0)){h[aT+(aP+16|0)>>3]=-2.0;h[aO>>3]=ak;break}aH=+h[aO>>3];aF=(+h[ap+(as<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aE=(+h[ap+(as<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aD=(aH- +h[5279])*+h[2]+ +h[12]+-1.0;aC=+h[404]+aF*+h[392]+aE*+h[396]+aD*+h[400];aB=+h[405]+aF*+h[393]+aE*+h[397]+aD*+h[401];aG=+h[406]+aF*+h[394]+aE*+h[398]+aD*+h[402];aV=aG==0.0?1.0e-5:aG;h[aN>>3]=(+h[403]+aF*+h[391]+aE*+h[395]+aD*+h[399])/aV;h[aT+(aP+8|0)>>3]=aC/aV;h[aT+(aP+16|0)>>3]=aB/aV;aQ=aT+(aP+32|0)|0;h[aQ>>3]=aH;c[aT+(aP+40|0)>>2]=0;if(A){h[aQ>>3]=+h[ap+(as<<6)+32>>3];c[ab>>2]=-6}else{h[aQ>>3]=+h[aO>>3]}c[aT+(aP+44|0)>>2]=at;aP=aN-(c[228]|0)|0;h[aO>>3]=ak;if((aP|0)<=0){break}gy((aP|0)/48&-1,4,0,X,aj)}else{if((aI|0)<0){break}gy(aI,5,ac,X,aj)}}while(0);as=as+1|0;}while((as|0)<(c[aq>>2]|0))}aq=c[z>>2]|0;if((aq|0)==0){Y=ah;Z=an;_=ad;$=ae;break L16961}else{as=ah;ap=ad;ad=ae;ah=an;z=aq;ao=ao+1|0;an=as;ae=ap}}}if((ag|0)==0){Y=T;Z=S;_=R;$=Q;break}ae=X|0;an=V+224|0;ao=W&1;z=ao<<24>>24==0;ah=ao<<24>>24!=0;ao=V+68|0;ad=ag;while(1){ab=c[ad+12>>2]|0;do{if((c[af>>2]|0)==368){c[ae>>2]=1;c[y>>2]=0;A=c[c[an>>2]>>2]|0;if((A|0)==0){break}else{aW=A}do{h[F>>3]=+h[aW+24>>3];h[C>>3]=+h[aW+32>>3];h[E>>3]=+h[aW+40>>3];if((c[aW+72>>2]|0)==6){aX=+h[aW+80>>3]}else{aX=+(c[aW+76>>2]|0)}h[P>>3]=aX;A=c[228]|0;if((A|0)==0){K=12456;break L16959}ai=c[225]|0;ap=c[224]|0;do{if((ai|0)<(ap|0)){aY=A;aZ=ai}else{as=(c[226]|0)+ap|0;if((as|0)==0){uu(A);c[228]=0;c[224]=0;c[225]=0;aY=0;aZ=0;break}else{aq=db(A,aa(c[227]|0,as),150384)|0;c[228]=aq;c[224]=as;aY=aq;aZ=c[225]|0;break}}}while(0);A=c[227]|0;c[225]=aZ+1;ap=aa(A,aZ);A=aY+ap|0;c[aY+(ap+24|0)>>2]=X;do{if((c[y>>2]|0)<(c[9738]|0)){ak=+h[E>>3];aH=(+h[F>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aV=(+h[C>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aB=(ak- +h[5279])*+h[2]+ +h[12]+-1.0;aC=+h[404]+aH*+h[392]+aV*+h[396]+aB*+h[400];aD=+h[405]+aH*+h[393]+aV*+h[397]+aB*+h[401];aE=+h[406]+aH*+h[394]+aV*+h[398]+aB*+h[402];aF=aE==0.0?1.0e-5:aE;h[A>>3]=(+h[403]+aH*+h[391]+aV*+h[395]+aB*+h[399])/aF;h[aY+(ap+8|0)>>3]=aC/aF;h[aY+(ap+16|0)>>3]=aD/aF;ai=aY+(ap+32|0)|0;h[ai>>3]=ak;c[aY+(ap+40|0)>>2]=0;if(z){h[ai>>3]=+h[E>>3]}else{h[ai>>3]=+h[P>>3];c[ao>>2]=-6}c[aY+(ap+44|0)>>2]=k;ai=c[228]|0;aO=A-ai|0;aq=(aO|0)/48&-1;if((aO|0)<0){break}c[ai+(aq*48&-1)+40>>2]=aW;gy(aq,5,ac,X,aj)}else{h[aY+(ap+16|0)>>3]=-2.0}}while(0);aW=c[aW>>2]|0;}while((aW|0)!=0)}else{ap=ad+8|0;if((c[ap>>2]|0)<=0){break}A=ad|0;aq=-1;ai=0;while(1){aO=ab+(ai<<6)|0;as=c[228]|0;if((as|0)==0){K=12470;break L16959}ar=c[225]|0;at=c[224]|0;do{if((ar|0)<(at|0)){a_=as;a$=ar}else{av=(c[226]|0)+at|0;if((av|0)==0){uu(as);c[228]=0;c[224]=0;c[225]=0;a_=0;a$=0;break}else{aM=db(as,aa(c[227]|0,av),150384)|0;c[228]=aM;c[224]=av;a_=aM;a$=c[225]|0;break}}}while(0);as=c[227]|0;c[225]=a$+1;at=aa(as,a$);as=a_+at|0;c[a_+(at+24|0)>>2]=X;ar=aO|0;if((c[ar>>2]|0)<(c[9738]|0)){aM=ab+(ai<<6)+24|0;ak=+h[aM>>3];aF=(+h[ab+(ai<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aD=(+h[ab+(ai<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aC=(ak- +h[5279])*+h[2]+ +h[12]+-1.0;aB=+h[404]+aF*+h[392]+aD*+h[396]+aC*+h[400];aV=+h[405]+aF*+h[393]+aD*+h[397]+aC*+h[401];aH=+h[406]+aF*+h[394]+aD*+h[398]+aC*+h[402];aE=aH==0.0?1.0e-5:aH;h[as>>3]=(+h[403]+aF*+h[391]+aD*+h[395]+aC*+h[399])/aE;h[a_+(at+8|0)>>3]=aB/aE;h[a_+(at+16|0)>>3]=aV/aE;av=a_+(at+32|0)|0;h[av>>3]=ak;c[a_+(at+40|0)>>2]=0;if(ah){h[av>>3]=+h[ab+(ai<<6)+32>>3];c[ao>>2]=-6}else{h[av>>3]=+h[aM>>3]}c[a_+(at+44|0)>>2]=aO;a0=(as-(c[228]|0)|0)/48&-1}else{h[a_+(at+16|0)>>3]=-2.0;a0=-1}do{if((c[af>>2]|0)==225){at=c[(c[A>>2]|0)+12>>2]|0;as=at+(ai<<6)|0;aM=c[228]|0;if((aM|0)==0){K=12483;break L16959}av=c[225]|0;aL=c[224]|0;do{if((av|0)<(aL|0)){a1=aM;a2=av}else{aK=(c[226]|0)+aL|0;if((aK|0)==0){uu(aM);c[228]=0;c[224]=0;c[225]=0;a1=0;a2=0;break}else{au=db(aM,aa(c[227]|0,aK),150384)|0;c[228]=au;c[224]=aK;a1=au;a2=c[225]|0;break}}}while(0);aM=c[227]|0;c[225]=a2+1;aL=aa(aM,a2);c[a1+(aL+24|0)>>2]=0;if((c[as>>2]|0)<(c[9738]|0)){aM=at+(ai<<6)+24|0;ak=+h[aM>>3];aE=(+h[at+(ai<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aV=(+h[at+(ai<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aB=(ak- +h[5279])*+h[2]+ +h[12]+-1.0;aC=+h[404]+aE*+h[392]+aV*+h[396]+aB*+h[400];aD=+h[405]+aE*+h[393]+aV*+h[397]+aB*+h[401];aF=+h[406]+aE*+h[394]+aV*+h[398]+aB*+h[402];aH=aF==0.0?1.0e-5:aF;h[a1+aL>>3]=(+h[403]+aE*+h[391]+aV*+h[395]+aB*+h[399])/aH;h[a1+(aL+8|0)>>3]=aC/aH;h[a1+(aL+16|0)>>3]=aD/aH;av=a1+(aL+32|0)|0;h[av>>3]=ak;c[a1+(aL+40|0)>>2]=0;h[av>>3]=+h[aM>>3];c[a1+(aL+44|0)>>2]=as;break}else{h[a1+(aL+16|0)>>3]=-2.0;break}}}while(0);do{if((a0|0)>=0){aL=c[af>>2]|0;if((aL|0)==225){gy(a0,6,0,X,aj);break}else if((aL|0)==153|(aL|0)==345){aM=ab+(ai<<6)+24|0;ak=+h[aM>>3];h[aM>>3]=+h[8083];av=c[228]|0;if((av|0)==0){K=12497;break L16959}au=c[225]|0;aK=c[224]|0;do{if((au|0)<(aK|0)){a3=av;a4=au}else{aJ=(c[226]|0)+aK|0;if((aJ|0)==0){uu(av);c[228]=0;c[224]=0;c[225]=0;a3=0;a4=0;break}else{aw=db(av,aa(c[227]|0,aJ),150384)|0;c[228]=aw;c[224]=aJ;a3=aw;a4=c[225]|0;break}}}while(0);av=c[227]|0;c[225]=a4+1;aK=aa(av,a4);av=a3+aK|0;c[a3+(aK+24|0)>>2]=X;if((c[ar>>2]|0)>=(c[9738]|0)){h[a3+(aK+16|0)>>3]=-2.0;h[aM>>3]=ak;break}aH=+h[aM>>3];aD=(+h[ab+(ai<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aC=(+h[ab+(ai<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aB=(aH- +h[5279])*+h[2]+ +h[12]+-1.0;aV=+h[404]+aD*+h[392]+aC*+h[396]+aB*+h[400];aE=+h[405]+aD*+h[393]+aC*+h[397]+aB*+h[401];aF=+h[406]+aD*+h[394]+aC*+h[398]+aB*+h[402];aG=aF==0.0?1.0e-5:aF;h[av>>3]=(+h[403]+aD*+h[391]+aC*+h[395]+aB*+h[399])/aG;h[a3+(aK+8|0)>>3]=aV/aG;h[a3+(aK+16|0)>>3]=aE/aG;au=a3+(aK+32|0)|0;h[au>>3]=aH;c[a3+(aK+40|0)>>2]=0;if(ah){h[au>>3]=+h[ab+(ai<<6)+32>>3];c[ao>>2]=-6}else{h[au>>3]=+h[aM>>3]}c[a3+(aK+44|0)>>2]=aO;aK=av-(c[228]|0)|0;h[aM>>3]=ak;if((aK|0)<=0){break}gy((aK|0)/48&-1,4,0,X,aj);break}else if((aL|0)==33){aK=ab+(ai<<6)+24|0;aH=+h[aK>>3];h[aK>>3]=0.0;av=c[228]|0;if((av|0)==0){K=12510;break L16959}au=c[225]|0;as=c[224]|0;do{if((au|0)<(as|0)){a5=av;a6=au}else{at=(c[226]|0)+as|0;if((at|0)==0){uu(av);c[228]=0;c[224]=0;c[225]=0;a5=0;a6=0;break}else{aw=db(av,aa(c[227]|0,at),150384)|0;c[228]=aw;c[224]=at;a5=aw;a6=c[225]|0;break}}}while(0);av=c[227]|0;c[225]=a6+1;as=aa(av,a6);av=a5+as|0;c[a5+(as+24|0)>>2]=X;if((c[ar>>2]|0)>=(c[9738]|0)){h[a5+(as+16|0)>>3]=-2.0;h[aK>>3]=aH;break}ak=+h[aK>>3];aG=(+h[ab+(ai<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;aE=(+h[ab+(ai<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;aV=(ak- +h[5279])*+h[2]+ +h[12]+-1.0;aB=+h[404]+aG*+h[392]+aE*+h[396]+aV*+h[400];aC=+h[405]+aG*+h[393]+aE*+h[397]+aV*+h[401];aD=+h[406]+aG*+h[394]+aE*+h[398]+aV*+h[402];aF=aD==0.0?1.0e-5:aD;h[av>>3]=(+h[403]+aG*+h[391]+aE*+h[395]+aV*+h[399])/aF;h[a5+(as+8|0)>>3]=aB/aF;h[a5+(as+16|0)>>3]=aC/aF;au=a5+(as+32|0)|0;h[au>>3]=ak;c[a5+(as+40|0)>>2]=0;if(ah){h[au>>3]=+h[ab+(ai<<6)+32>>3];c[ao>>2]=-6}else{h[au>>3]=+h[aK>>3]}c[a5+(as+44|0)>>2]=aO;as=av-(c[228]|0)|0;h[aK>>3]=aH;if((as|0)<=0){break}gy((as|0)/48&-1,4,0,X,aj);break}else if((aL|0)==352|(aL|0)==51|(aL|0)==177|(aL|0)==193|(aL|0)==209|(aL|0)==1){if((aq|0)<=-1){break}gy(a0,0,0,X,aj);break}else{gy(a0,5,ac,X,aj);break}}}while(0);aO=ai+1|0;if((aO|0)<(c[ap>>2]|0)){aq=a0;ai=aO}else{break}}}}while(0);ab=c[ad>>2]|0;if((ab|0)==0){Y=T;Z=S;_=R;$=Q;break}else{ad=ab}}}}while(0);X=U+1|0;if((X|0)<(d|0)){Q=$;R=_;S=Z;T=Y;U=X;V=c[V>>2]|0}else{K=12581;break}}if((K|0)==12497){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((K|0)==12510){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((K|0)==12581){uu(Z);uu(Y);uu($);uu(_);break}else if((K|0)==12470){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((K|0)==12483){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((K|0)==12456){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((K|0)==12531){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((K|0)==12564){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);K=c[11605]|0;if((K|0)==0){uu(c[7628]|0);c[7628]=0;c[7624]=0;c[7625]=0;uu(c[11608]|0);c[11608]=0;c[11604]=0;c[11605]=0;uu(c[228]|0);c[228]=0;c[224]=0;c[225]=0;uk(176896,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}_=(c[7625]|0)==0;$=K<<2;K=ut($)|0;do{if((K|0)==0){gk();Y=ut($)|0;if((Y|0)!=0){a7=Y;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=168848,v)|0)}else{a7=K}}while(0);K=a7;$=c[11605]|0;if(($|0)>0){Y=0;while(1){c[K+(Y<<2)>>2]=Y;Z=Y+1|0;d=c[11605]|0;if((Z|0)<(d|0)){Y=Z}else{a8=d;break}}}else{a8=$}bM(a7|0,a8|0,4,6);a8=(c[11608]|0)+((c[K>>2]|0)*20&-1)|0;if((c[11605]|0)>1){$=1;Y=a8;while(1){d=K+($<<2)|0;c[Y+16>>2]=c[d>>2];Z=(c[11608]|0)+((c[d>>2]|0)*20&-1)|0;d=$+1|0;if((d|0)<(c[11605]|0)){$=d;Y=Z}else{a9=Z;break}}}else{a9=a8}c[a9+16>>2]=-1;c[11566]=c[K>>2];uu(a7);if(_){_=c[11566]|0;if((_|0)<=-1){i=e;return}a7=_;_=c[11608]|0;do{K=_;a9=K+(a7*20&-1)|0;a8=c[228]|0;gw(a9,a8+((c[a9>>2]|0)*48&-1)|0,a8+((c[K+(a7*20&-1)+4>>2]|0)*48&-1)|0);_=c[11608]|0;a7=c[_+((c[11566]|0)*20&-1)+16>>2]|0;c[11566]=a7;}while((a7|0)>-1);i=e;return}a7=c[7625]|0;if((a7|0)!=0){_=a7<<2;a7=ut(_)|0;do{if((a7|0)==0){gk();K=ut(_)|0;if((K|0)!=0){ba=K;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=172600,v)|0)}else{ba=a7}}while(0);a7=ba;_=c[7625]|0;if((_|0)>0){K=0;while(1){c[a7+(K<<2)>>2]=K;a8=K+1|0;a9=c[7625]|0;if((a8|0)<(a9|0)){K=a8}else{bb=a9;break}}}else{bb=_}bM(ba|0,bb|0,4,14);bb=(c[7628]|0)+((c[a7>>2]|0)*104&-1)|0;if((c[7625]|0)>1){_=1;K=bb;while(1){a9=a7+(_<<2)|0;c[K+100>>2]=c[a9>>2];a8=(c[7628]|0)+((c[a9>>2]|0)*104&-1)|0;a9=_+1|0;if((a9|0)<(c[7625]|0)){_=a9;K=a8}else{bc=a8;break}}}else{bc=bb}c[bc+100>>2]=-1;c[8022]=c[a7>>2];uu(ba)}c[l>>2]=c[8022];ba=c[11566]|0;if((ba|0)<=-1){i=e;return}a7=ba;ba=c[11608]|0;while(1){bc=ba;if((c[bc+(a7*20&-1)+8>>2]|0)==-3){bd=a7;be=ba}else{gu(a7,c[bc+(a7*20&-1)>>2]|0,c[bc+(a7*20&-1)+4>>2]|0,l);bd=c[11566]|0;be=c[11608]|0}bc=c[be+(bd*20&-1)+16>>2]|0;c[11566]=bc;if((bc|0)>-1){a7=bc;ba=be}else{break}}i=e;return}function gw(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0;f=i;i=i+72|0;g=f|0;j=f+16|0;k=b+12|0;l=c[k>>2]|0;m=g;n=l+40|0;c[m>>2]=c[n>>2];c[m+4>>2]=c[n+4>>2];c[m+8>>2]=c[n+8>>2];c[m+12>>2]=c[n+12>>2];n=j;o=l;c[n>>2]=c[o>>2];c[n+4>>2]=c[o+4>>2];c[n+8>>2]=c[o+8>>2];c[n+12>>2]=c[o+12>>2];c[n+16>>2]=c[o+16>>2];c[n+20>>2]=c[o+20>>2];c[n+24>>2]=c[o+24>>2];c[n+28>>2]=c[o+28>>2];c[n+32>>2]=c[o+32>>2];c[n+36>>2]=c[o+36>>2];c[n+40>>2]=c[o+40>>2];c[n+44>>2]=c[o+44>>2];c[n+48>>2]=c[o+48>>2];c[n+52>>2]=c[o+52>>2];p=j+8|0;q=((c[p>>2]|0)+11|0)>>>0<2;if(q){if((c[b+8>>2]|0)==-11){r=e}else{s=12616}}else{s=12616}if((s|0)==12616){r=d}t=~~+h[r+32>>3];do{if((c[g>>2]|0)==3){if((c[g+4>>2]|0)==-6){c[j+44>>2]=t;s=12651;break}if((c[b+8>>2]|0)==-8){s=12651;break}if((l|0)!=56296){s=12623;break}c[j+44>>2]=t}else{s=12623}}while(0);do{if((s|0)==12623){r=j+4|0;if((c[r>>2]|0)==-6){u=j|0;v=c[u>>2]|0;w=c[8798]|0;x=(w|0)>0;y=t;L17222:while(1){z=43264;while(1){A=c[z>>2]|0;if((A|0)==0){break}if((c[A+4>>2]|0)==(y|0)){break L17222}else{z=A|0}}B=y-1|0;if(!((y|0)>(w|0)&x)){s=12634;break}y=((B|0)%(w|0)&-1)+1|0}if((s|0)==12634){c[r>>2]=B;c[j+40>>2]=1;c[j+44>>2]=B;c[p>>2]=B;s=12651;break}w=A+8|0;c[n>>2]=c[w>>2];c[n+4>>2]=c[w+4>>2];c[n+8>>2]=c[w+8>>2];c[n+12>>2]=c[w+12>>2];c[n+16>>2]=c[w+16>>2];c[n+20>>2]=c[w+20>>2];c[n+24>>2]=c[w+24>>2];c[n+28>>2]=c[w+28>>2];c[n+32>>2]=c[w+32>>2];c[n+36>>2]=c[w+36>>2];c[n+40>>2]=c[w+40>>2];c[n+44>>2]=c[w+44>>2];c[n+48>>2]=c[w+48>>2];c[n+52>>2]=c[w+52>>2];c[u>>2]=v;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[r>>2]=y;a[j+32|0]=0;s=12651;break}if((a[j+32|0]&1)!=0){s=12651;break}c[j+40>>2]=1;c[j+44>>2]=c[r>>2];s=12651;break}if(q){c[p>>2]=c[b+8>>2];break}if((c[9742]|0)==0){break}if((c[l+40>>2]|0)==6){break}w=j|0;x=c[w>>2]|0;z=c[8798]|0;C=(z|0)>0;D=c[b+8>>2]|0;L17242:while(1){E=D+1|0;F=43264;while(1){G=c[F>>2]|0;if((G|0)==0){break}if((c[G+4>>2]|0)==(E|0)){s=12643;break L17242}else{F=G|0}}if(!((E|0)>(z|0)&C)){s=12649;break}D=(D|0)%(z|0)&-1}do{if((s|0)==12643){z=G+8|0;c[n>>2]=c[z>>2];c[n+4>>2]=c[z+4>>2];c[n+8>>2]=c[z+8>>2];c[n+12>>2]=c[z+12>>2];c[n+16>>2]=c[z+16>>2];c[n+20>>2]=c[z+20>>2];c[n+24>>2]=c[z+24>>2];c[n+28>>2]=c[z+28>>2];c[n+32>>2]=c[z+32>>2];c[n+36>>2]=c[z+36>>2];c[n+40>>2]=c[z+40>>2];c[n+44>>2]=c[z+44>>2];c[n+48>>2]=c[z+48>>2];c[n+52>>2]=c[z+52>>2];c[w>>2]=x;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[r>>2]=E;a[j+32|0]=0;break}if((a[j+32|0]&1)!=0){break}c[j+40>>2]=1;c[j+44>>2]=c[r>>2]}else if((s|0)==12649){c[r>>2]=D;c[j+40>>2]=1;c[j+44>>2]=D;c[p>>2]=D}}while(0);D=j+40|0;c[m>>2]=c[D>>2];c[m+4>>2]=c[D+4>>2];c[m+8>>2]=c[D+8>>2];c[m+12>>2]=c[D+12>>2];s=12651}}while(0);do{if((s|0)==12651){E=j+40|0;c[m>>2]=c[E>>2];c[m+4>>2]=c[E+4>>2];c[m+8>>2]=c[E+8>>2];c[m+12>>2]=c[E+12>>2];c[n>>2]=c[o>>2];c[n+4>>2]=c[o+4>>2];c[n+8>>2]=c[o+8>>2];c[n+12>>2]=c[o+12>>2];c[n+16>>2]=c[o+16>>2];c[n+20>>2]=c[o+20>>2];c[n+24>>2]=c[o+24>>2];c[n+28>>2]=c[o+28>>2];c[n+32>>2]=c[o+32>>2];c[n+36>>2]=c[o+36>>2];c[E>>2]=c[m>>2];c[E+4>>2]=c[m+4>>2];c[E+8>>2]=c[m+8>>2];c[E+12>>2]=c[m+12>>2];a[j+32|0]=1;if(!q){break}c[p>>2]=c[b+8>>2]}}while(0);un(d,e,j,g);if((c[c[k>>2]>>2]|0)==0){i=f;return}gx(d);gx(e);i=f;return}function gx(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0.0,u=0.0,v=0.0,w=0.0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0.0,H=0.0,I=0;d=i;i=i+120|0;e=d|0;f=d+16|0;g=d+32|0;j=d+48|0;k=d+64|0;l=~~(+h[b>>3]*+(c[180]|0))+(c[186]|0)|0;m=~~(+h[b+8>>3]*+(c[40]|0))+(c[46]|0)|0;n=b+24|0;o=c[n>>2]|0;if((o|0)==0){i=d;return}if((c[o+8>>2]|0)<=-2){i=d;return}p=c[13542]|0;do{if((p|0)!=0){q=(c[p>>2]|0)>(l|0)&1;r=(c[p+4>>2]|0)<(l|0)?q|2:q;q=(c[p+8>>2]|0)>(m|0)?r|4:r;if((((c[p+12>>2]|0)<(m|0)?q|8:q)|0)==0){break}i=d;return}}while(0);p=c[b+40>>2]|0;if((p|0)!=0){fp(l,m,p);c[n>>2]=0;i=d;return}p=c[o+40>>2]|0;do{if((p|0)==6){s=+h[b+32>>3];q=c[34]|0;r=(a[66852]&1)==0;do{if((a[64788+(q*688&-1)|0]&1)==0){if(r){t=s;break}if(s>0.0){u=+_(+s);t=u/+h[8358];break}else{t=+h[8341];break}}else{if(r){t=+Z(+(s*+h[64800+(q*688&-1)>>3]));break}if(+h[64792+(q*688&-1)>>3]==+h[8357]){t=s;break}t=s*+h[64800+(q*688&-1)>>3]/+h[8358]}}while(0);s=+h[8341];do{if(s<t){u=+h[8342];if(u<=t){v=+((a[20668]|0)==112&1|0);break}w=(t-s)/(u-s);if((a[20668]|0)==112){v=w;break}v=1.0-w}else{v=+((a[20668]|0)!=112&1|0)}}while(0);q=(c[3524]|0)+144|0;if((c[q>>2]|0)==0){break}c[f>>2]=5;h[f+8>>3]=v;c[f+4>>2]=0;cM[c[q>>2]&511](f)}else if((p|0)==2){if((c[o+44>>2]|0)!=-6){break}q=k;r=o;c[q>>2]=c[r>>2];c[q+4>>2]=c[r+4>>2];c[q+8>>2]=c[r+8>>2];c[q+12>>2]=c[r+12>>2];c[q+16>>2]=c[r+16>>2];c[q+20>>2]=c[r+20>>2];c[q+24>>2]=c[r+24>>2];c[q+28>>2]=c[r+28>>2];c[q+32>>2]=c[r+32>>2];c[q+36>>2]=c[r+36>>2];c[q+40>>2]=c[r+40>>2];c[q+44>>2]=c[r+44>>2];c[q+48>>2]=c[r+48>>2];c[q+52>>2]=c[r+52>>2];r=k|0;x=c[r>>2]|0;y=c[8798]|0;z=(y|0)>0;A=~~+h[b+32>>3];L17305:while(1){B=43264;while(1){C=c[B>>2]|0;if((C|0)==0){break}if((c[C+4>>2]|0)==(A|0)){D=12670;break L17305}else{B=C|0}}E=A-1|0;if(!((A|0)>(y|0)&z)){D=12676;break}A=((E|0)%(y|0)&-1)+1|0}do{if((D|0)==12670){y=C+8|0;c[q>>2]=c[y>>2];c[q+4>>2]=c[y+4>>2];c[q+8>>2]=c[y+8>>2];c[q+12>>2]=c[y+12>>2];c[q+16>>2]=c[y+16>>2];c[q+20>>2]=c[y+20>>2];c[q+24>>2]=c[y+24>>2];c[q+28>>2]=c[y+28>>2];c[q+32>>2]=c[y+32>>2];c[q+36>>2]=c[y+36>>2];c[q+40>>2]=c[y+40>>2];c[q+44>>2]=c[y+44>>2];c[q+48>>2]=c[y+48>>2];c[q+52>>2]=c[y+52>>2];c[r>>2]=x;y=c[3524]|0;if((c[y+96>>2]&1024|0)!=0){c[k+4>>2]=A;a[k+32|0]=0;F=y;break}if((a[k+32|0]&1)!=0){F=y;break}c[k+40>>2]=1;c[k+44>>2]=c[k+4>>2];F=y}else if((D|0)==12676){c[k+4>>2]=E;c[k+40>>2]=1;c[k+44>>2]=E;c[k+8>>2]=E;F=c[3524]|0}}while(0);fn(k+40|0,F)}else if((p|0)==3){if((c[o+44>>2]|0)==-6){A=(c[3524]|0)+144|0;if((c[A>>2]|0)==0){break}x=~~+h[b+32>>3];c[j>>2]=3;c[j+4>>2]=x;h[j+8>>3]=0.0;cM[c[A>>2]&511](j);break}else{A=c[o+44>>2]|0;x=(c[3524]|0)+144|0;if((c[x>>2]|0)==0){break}c[g>>2]=3;c[g+4>>2]=A;h[g+8>>3]=0.0;cM[c[x>>2]&511](g);break}}else if((p|0)==4){s=+h[b+32>>3];w=+h[8341];do{if(w<s){u=+h[8342];if(u<=s){G=+((a[20668]|0)==112&1|0);break}H=(s-w)/(u-w);if((a[20668]|0)==112){G=H;break}G=1.0-H}else{G=+((a[20668]|0)!=112&1|0)}}while(0);x=(c[3524]|0)+144|0;if((c[x>>2]|0)==0){break}c[e>>2]=5;h[e+8>>3]=G;c[e+4>>2]=0;cM[c[x>>2]&511](e)}}while(0);e=c[n>>2]|0;if(+h[e+24>>3]==-3.0){cK[c[(c[3524]|0)+92>>2]&63](+h[3817]*+h[(c[b+44>>2]|0)+48>>3]);I=c[n>>2]|0}else{I=e}cR[c[(c[3524]|0)+80>>2]&127](l,m,c[I+8>>2]|0);c[n>>2]=0;i=d;return}function gy(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;g=i;j=c[228]|0;k=j;l=k+(a*48&-1)|0;m=1<<b;if((b|0)==0){n=m;o=k+((a-1|0)*48&-1)|0;p=l}else if((b|0)==6){n=0;o=k+((a+1|0)*48&-1)|0;p=l}else if((b|0)==5){n=0;o=l;p=l}else if((b|0)==3){n=m>>>1;o=k+((a-d|0)*48&-1)|0;p=k+((a-1|0)*48&-1)|0}else if((b|0)==4){n=0;o=k+((a-1|0)*48&-1)|0;p=l}else if((b|0)==2){n=m;o=k+(((a-1|0)-d|0)*48&-1)|0;p=l}else if((b|0)==1){n=m;o=k+((a-d|0)*48&-1)|0;p=l}else{n=m;o=0;p=l}l=(o-j|0)/48&-1;if(+h[p+16>>3]==-2.0){q=-2;i=g;return q|0}if(+h[o+16>>3]==-2.0){q=-2;i=g;return q|0}if((n|0)==0){r=f}else{r=(c[9734]&n|0)==0?-3:f}f=c[11608]|0;if((f|0)==0){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}n=c[11605]|0;o=c[11604]|0;do{if((n|0)<(o|0)){s=f;t=n}else{p=(c[11606]|0)+o|0;if((p|0)==0){uu(f);c[11608]=0;c[11604]=0;c[11605]=0;s=0;t=0;break}else{j=db(f,aa(c[11607]|0,p),150384)|0;c[11608]=j;c[11604]=p;s=j;t=c[11605]|0;break}}}while(0);f=c[11607]|0;c[11605]=t+1;o=aa(f,t);t=s+o|0;f=c[228]|0;n=s+(o+8|0)|0;c[n>>2]=r;c[s+(o+12|0)>>2]=e;c[s+(o+16|0)>>2]=-1;r=t;do{if(+h[f+(a*48&-1)+16>>3]<+h[f+(l*48&-1)+16>>3]){c[r>>2]=l;c[s+(o+4|0)>>2]=a;if((c[e+8>>2]|0)!=-10){break}c[n>>2]=-11}else{c[r>>2]=a;c[s+(o+4|0)>>2]=l;if((c[e+8>>2]|0)!=-10){break}c[n>>2]=-10}}while(0);q=(t-(c[11608]|0)|0)/20&-1;i=g;return q|0}function gz(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0,A=0,B=0,C=0,D=0.0,E=0.0,F=0,G=0.0,H=0.0,I=0,J=0.0,K=0.0,L=0,M=0.0,N=0.0,O=0,R=0.0,S=0.0,T=0,U=0.0,V=0.0,W=0,X=0.0,Y=0,Z=0.0,_=0.0,$=0.0,ab=0,ac=0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0.0,ai=0,aj=0.0;f=i;if((d|0)==2){j=b-1|0;k=j-e|0;l=j;m=b}else if((d|0)==0){j=b-e|0;k=j;l=j-1|0;m=b}else if((d|0)==3){j=b-1|0;k=b-e|0;l=j-e|0;m=j}else if((d|0)==1){k=b-e|0;l=b-1|0;m=b}else{k=0;l=0;m=0}b=c[228]|0;n=+h[b+(m*48&-1)+16>>3];if(n==-2.0){o=-2;i=f;return o|0}p=+h[b+(l*48&-1)+16>>3];if(p==-2.0){o=-2;i=f;return o|0}q=+h[b+(k*48&-1)+16>>3];if(q==-2.0){o=-2;i=f;return o|0}r=+h[b+(m*48&-1)>>3];s=+h[b+(l*48&-1)>>3];t=+P(+(r-s));u=+h[b+(m*48&-1)+8>>3];w=+h[b+(l*48&-1)+8>>3];x=t+ +P(+(u-w));if(x+ +P(+(n-p))+-1.0e-5<=0.0){o=-2;i=f;return o|0}x=+h[b+(k*48&-1)>>3];t=+P(+(s-x));s=+h[b+(k*48&-1)+8>>3];y=t+ +P(+(w-s));if(y+ +P(+(p-q))+-1.0e-5<=0.0){o=-2;i=f;return o|0}p=+P(+(x-r));r=p+ +P(+(s-u));if(r+ +P(+(q-n))+-1.0e-5<=0.0){o=-2;i=f;return o|0}b=c[7628]|0;if((b|0)==0){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}e=c[7625]|0;d=c[7624]|0;do{if((e|0)<(d|0)){z=b;A=e}else{j=(c[7626]|0)+d|0;if((j|0)==0){uu(b);c[7628]=0;c[7624]=0;c[7625]=0;z=0;A=0;break}else{B=db(b,aa(c[7627]|0,j),150384)|0;c[7628]=B;c[7624]=j;z=B;A=c[7625]|0;break}}}while(0);b=c[7627]|0;c[7625]=A+1;d=aa(b,A);A=z+d|0;c[A>>2]=m;c[z+(d+4|0)>>2]=l;c[z+(d+8|0)>>2]=k;c[z+(d+100|0)>>2]=-1;n=+h[(c[228]|0)+(m*48&-1)>>3];b=z+(d+16|0)|0;h[b>>3]=n;e=c[228]|0;q=+h[e+(l*48&-1)>>3];if(q<n){h[b>>3]=q;C=c[228]|0;D=q}else{C=e;D=n}n=+h[C+(k*48&-1)>>3];if(n<D){h[b>>3]=n;E=n}else{E=D}b=A;D=+g[3534];n=-0.0-D;if(E<n){a[46904]=1}E=+h[(c[228]|0)+(m*48&-1)+8>>3];C=z+(d+32|0)|0;h[C>>3]=E;e=c[228]|0;q=+h[e+(l*48&-1)+8>>3];if(q<E){h[C>>3]=q;F=c[228]|0;G=q}else{F=e;G=E}E=+h[F+(k*48&-1)+8>>3];if(E<G){h[C>>3]=E;H=E}else{H=G}if(H<n){a[46904]=1}H=+h[(c[228]|0)+(m*48&-1)+16>>3];C=z+(d+48|0)|0;h[C>>3]=H;F=c[228]|0;G=+h[F+(l*48&-1)+16>>3];if(G<H){h[C>>3]=G;I=c[228]|0;J=G}else{I=F;J=H}H=+h[I+(k*48&-1)+16>>3];if(H<J){h[C>>3]=H;K=H}else{K=J}if(K<n){a[46904]=1}n=+h[(c[228]|0)+(m*48&-1)>>3];C=z+(d+24|0)|0;h[C>>3]=n;I=c[228]|0;K=+h[I+(l*48&-1)>>3];if(K>n){h[C>>3]=K;L=c[228]|0;M=K}else{L=I;M=n}n=+h[L+(k*48&-1)>>3];if(n>M){h[C>>3]=n;N=n}else{N=M}M=D;if(N>M){a[46904]=1}N=+h[(c[228]|0)+(m*48&-1)+8>>3];C=z+(d+40|0)|0;h[C>>3]=N;L=c[228]|0;D=+h[L+(l*48&-1)+8>>3];if(D>N){h[C>>3]=D;O=c[228]|0;R=D}else{O=L;R=N}N=+h[O+(k*48&-1)+8>>3];if(N>R){h[C>>3]=N;S=N}else{S=R}if(S>M){a[46904]=1}S=+h[(c[228]|0)+(m*48&-1)+16>>3];C=z+(d+56|0)|0;h[C>>3]=S;O=c[228]|0;R=+h[O+(l*48&-1)+16>>3];if(R>S){h[C>>3]=R;T=c[228]|0;U=R}else{T=O;U=S}S=+h[T+(k*48&-1)+16>>3];if(S>U){h[C>>3]=S;V=S}else{V=U}if(V>M){a[46904]=1}C=z+(d+64|0)|0;T=c[228]|0;O=T+(k*48&-1)+8|0;L=T+(m*48&-1)+8|0;I=T+(k*48&-1)+16|0;F=T+(m*48&-1)+16|0;M=(+h[O>>3]- +h[L>>3])*(+h[I>>3]+ +h[F>>3]);h[C>>3]=M;e=T+(k*48&-1)|0;B=T+(m*48&-1)|0;V=(+h[I>>3]- +h[F>>3])*(+h[e>>3]+ +h[B>>3]);I=z+(d+72|0)|0;h[I>>3]=V;U=(+h[e>>3]- +h[B>>3])*(+h[O>>3]+ +h[L>>3]);O=z+(d+80|0)|0;h[O>>3]=U;e=c[228]|0;T=e+(l*48&-1)|0;j=e+(l*48&-1)+8|0;W=e+(l*48&-1)+16|0;S=M+(+h[L>>3]- +h[j>>3])*(+h[F>>3]+ +h[W>>3]);h[C>>3]=S;l=T|0;M=V+(+h[F>>3]- +h[W>>3])*(+h[B>>3]+ +h[l>>3]);h[I>>3]=M;V=U+(+h[B>>3]- +h[l>>3])*(+h[L>>3]+ +h[j>>3]);h[O>>3]=V;L=c[228]|0;B=L+(k*48&-1)|0;F=L+(k*48&-1)+8|0;e=L+(k*48&-1)+16|0;U=S+(+h[j>>3]- +h[F>>3])*(+h[W>>3]+ +h[e>>3]);h[C>>3]=U;k=B|0;S=M+(+h[W>>3]- +h[e>>3])*(+h[l>>3]+ +h[k>>3]);h[I>>3]=S;M=V+(+h[l>>3]- +h[k>>3])*(+h[j>>3]+ +h[F>>3]);h[O>>3]=M;V=+Q(+(U*U+S*S+M*M));if(V+-1.0e-5>0.0){X=V;Y=T;Z=M;_=U;$=S}else{T=c[228]|0;F=T+(m*48&-1)|0;j=F|0;k=T+(m*48&-1)+8|0;l=T+(m*48&-1)+16|0;m=1;e=B;while(1){if((m|0)>=3){ab=12789;break}B=c[b+(m<<2)>>2]|0;W=T+(B*48&-1)|0;S=+h[j>>3];U=+P(+(S- +h[W>>3]));M=+h[k>>3];V=U+ +P(+(M- +h[T+(B*48&-1)+8>>3]));U=+h[l>>3];if(V+ +P(+(U- +h[T+(B*48&-1)+16>>3]))+-1.0e-5>0.0){ac=W;ad=S;ae=M;af=U;break}else{m=m+1|0;e=W}}if((ab|0)==12789){ac=e;ad=+h[j>>3];ae=+h[k>>3];af=+h[l>>3]}e=ac+8|0;U=+h[e>>3];if(+P(+(ae-U))<1.0e-5){ag=ae+1.0;ah=ad}else{ag=ae;ah=ad+1.0}ab=ac+16|0;ad=+h[ab>>3];M=ag*(af-ad)+((af-af)*U+ae*(ad-af));h[C>>3]=M;m=ac|0;ad=+h[m>>3];ae=+h[j>>3];U=af*(ae-ad)+(+h[l>>3]*(ad-ah)+ +h[ab>>3]*(ah-ae));h[I>>3]=U;ae=+h[e>>3];ad=+h[k>>3];af=ah*(ad-ae)+(+h[j>>3]*(ae-ag)+ +h[m>>3]*(ag-ad));h[O>>3]=af;X=+Q(+(M*M+U*U+af*af));Y=F;Z=af;_=M;$=U}if(Z<0.0){ai=0;aj=X*-1.0}else{ai=1;aj=X}X=_/aj;h[C>>3]=X;_=$/aj;h[I>>3]=_;$=Z/aj;h[O>>3]=$;h[z+(d+88|0)>>3]=+h[Y>>3]*(-0.0-X)-_*+h[Y+8>>3]-$*+h[Y+16>>3];a[z+(d+96|0)|0]=ai;o=(A-(c[7628]|0)|0)/104&-1;i=f;return o|0}function gA(b,d,e,f,g,j){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;k=i;if((e|0)<=-2){i=k;return}l=(f|0)<-1?e:f;f=c[7628]|0;m=(a[f+(l*104&-1)+96|0]&1)<<1|a[f+(e*104&-1)+96|0]&1;do{if((m|0)==0){f=c[11608]|0;n=f+(b*20&-1)+8|0;if((c[n>>2]|0)==-3){o=f}else{c[n>>2]=j;o=c[11608]|0}n=o+(d*20&-1)+8|0;if((c[n>>2]|0)==-3){i=k;return}c[n>>2]=j;i=k;return}else if((m|0)==2){n=(c[11608]|0)+(b*20&-1)+8|0;if((c[n>>2]|0)==-3){break}c[n>>2]=j}else if((m|0)!=1){i=k;return}}while(0);m=c[11608]|0;do{if(!((b|0)==(d|0)&(a[38960]^1))){if((c[m+(d*20&-1)+8>>2]|0)!=-3){break}i=k;return}}while(0);b=m;m=c[b+(d*20&-1)>>2]|0;o=c[b+(d*20&-1)+4>>2]|0;n=c[7628]|0;f=c[n+(e*104&-1)>>2]|0;p=c[n+(e*104&-1)+4>>2]|0;do{if((f|0)==(m|0)){q=c[n+(e*104&-1)+8>>2]|0;if((p|0)==(o|0)){r=q;break}r=(q|0)==(o|0)?p:-1}else{if((p|0)==(m|0)){q=c[n+(e*104&-1)+8>>2]|0;if((f|0)==(o|0)){r=q;break}r=(q|0)==(o|0)?f:-1;break}if((c[n+(e*104&-1)+8>>2]|0)!=(m|0)){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=143816,v)|0)}if((f|0)==(o|0)){r=p;break}r=(p|0)==(o|0)?f:-1}}while(0);if((r|0)<=-1){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=143816,v)|0)}f=c[228]|0;if(+h[n+(l*104&-1)+88>>3]+(+h[f+(r*48&-1)>>3]*+h[n+(l*104&-1)+64>>3]+ +h[f+(r*48&-1)+8>>3]*+h[n+(l*104&-1)+72>>3]+ +h[f+(r*48&-1)+16>>3]*+h[n+(l*104&-1)+80>>3])>0.0){c[b+(d*20&-1)+8>>2]=(a[n+(e*104&-1)+96|0]&1)!=0?g:j;i=k;return}else{c[b+(d*20&-1)+8>>2]=(a[n+(l*104&-1)+96|0]&1)!=0?g:j;i=k;return}}function gB(a,b,d,e){a=a|0;b=b|0;d=+d;e=e|0;var f=0.0,g=0,i=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,v=0.0;f=+h[e+80>>3];if(f==0.0){g=0;return g|0}i=c[228]|0;j=c[e>>2]|0;k=c[e+4>>2]|0;l=c[e+8>>2]|0;m=+h[a>>3];n=m+(+h[b>>3]-m)*d;m=+h[a+8>>3];o=m+(+h[b+8>>3]-m)*d;m=+h[a+16>>3];p=m+(+h[b+16>>3]-m)*d;d=+h[i+(j*48&-1)>>3];m=+h[i+(j*48&-1)+8>>3];q=+h[i+(k*48&-1)>>3];r=+h[i+(k*48&-1)+8>>3];s=(o-m)*(q-d)-(n-d)*(r-m);t=+h[i+(l*48&-1)>>3];u=+h[i+(l*48&-1)+8>>3];v=(o-r)*(t-q)-(n-q)*(u-r);r=(d-t)*(o-u)-(n-t)*(m-u);do{if(s<-1.0e-5|v<-1.0e-5|r<-1.0e-5){if(s+-1.0e-5>0.0){g=0;return g|0}if(v+-1.0e-5>0.0){g=0;return g|0}if(r+-1.0e-5>0.0){g=0}else{break}return g|0}}while(0);r=(-0.0-(+h[e+88>>3]+(n*+h[e+64>>3]+o*+h[e+72>>3])))/f;if(r<p+-1.0e-5){g=0;return g|0}g=p<r+-1.0e-5?1:2;return g|0}function gC(a,b,d){a=a|0;b=b|0;d=+d;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0,r=0.0,s=0;e=i;f=c[228]|0;if((f|0)==0){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}g=c[225]|0;j=c[224]|0;do{if((g|0)<(j|0)){k=f;l=g}else{m=(c[226]|0)+j|0;if((m|0)==0){uu(f);c[228]=0;c[224]=0;c[225]=0;k=0;l=0;break}else{n=db(f,aa(c[227]|0,m),150384)|0;c[228]=n;c[224]=m;k=n;l=c[225]|0;break}}}while(0);f=c[227]|0;c[225]=l+1;j=aa(f,l);l=k+j|0;f=k;o=+h[f+(a*48&-1)>>3];p=o+(+h[f+(b*48&-1)>>3]-o)*d;h[l>>3]=p;f=c[228]|0;o=+h[f+(a*48&-1)+8>>3];q=o+(+h[f+(b*48&-1)+8>>3]-o)*d;h[k+(j+8|0)>>3]=q;f=c[228]|0;o=+h[f+(a*48&-1)+16>>3];r=o+(+h[f+(b*48&-1)+16>>3]-o)*d;h[k+(j+16|0)>>3]=r;f=c[228]|0;o=+h[f+(a*48&-1)+32>>3];h[k+(j+32|0)>>3]=o+(+h[f+(b*48&-1)+32>>3]-o)*d;c[k+(j+24|0)>>2]=0;j=c[228]|0;k=j;d=+P(+(p- +h[k+(a*48&-1)>>3]));o=d+ +P(+(q- +h[k+(a*48&-1)+8>>3]));if(o+ +P(+(r- +h[k+(a*48&-1)+16>>3]))+-1.0e-5<=0.0){if((j|0)==0){uk(119400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}f=c[225]|0;if((f|0)==0){s=a;i=e;return s|0}c[225]=f-1;s=a;i=e;return s|0}o=+P(+(p- +h[k+(b*48&-1)>>3]));p=o+ +P(+(q- +h[k+(b*48&-1)+8>>3]));if(p+ +P(+(r- +h[k+(b*48&-1)+16>>3]))+-1.0e-5>0.0){s=(l-j|0)/48&-1;i=e;return s|0}j=c[225]|0;if((j|0)==0){s=b;i=e;return s|0}c[225]=j-1;s=b;i=e;return s|0}function gD(b){b=b|0;var d=0,e=0,f=0,g=0;d=i;e=c[b>>2]|0;if((a[e+8|0]&1)!=0){uf(-1,72648,(v=i,i=i+8|0,c[v>>2]=c[e+4>>2],v)|0)}b=e+16|0;f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f+1|0;c[6354]=g;f=14296+(g*24&-1)|0;g=b;c[f>>2]=c[g>>2];c[f+4>>2]=c[g+4>>2];c[f+8>>2]=c[g+8>>2];c[f+12>>2]=c[g+12>>2];c[f+16>>2]=c[g+16>>2];c[f+20>>2]=c[g+20>>2];if((c[b>>2]|0)!=3){i=d;return}b=c[e+24>>2]|0;if((b|0)==0){i=d;return}e=bP(b|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=e;i=d;return}function gE(a){a=a|0;var b=0,d=0,e=0;b=i;d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d+1|0;c[6354]=e;d=14296+(e*24&-1)|0;e=a;c[d>>2]=c[e>>2];c[d+4>>2]=c[e+4>>2];c[d+8>>2]=c[e+8>>2];c[d+12>>2]=c[e+12>>2];c[d+16>>2]=c[e+16>>2];c[d+20>>2]=c[e+20>>2];if((c[a>>2]|0)!=3){i=b;return}e=c[a+8>>2]|0;if((e|0)==0){i=b;return}a=bP(e|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=a;i=b;return}function gF(a){a=a|0;var b=0,d=0,e=0,f=0;b=i;d=c[a>>2]|0;a=d+24|0;e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e+1|0;c[6354]=f;e=14296+(f*24&-1)|0;f=a;c[e>>2]=c[f>>2];c[e+4>>2]=c[f+4>>2];c[e+8>>2]=c[f+8>>2];c[e+12>>2]=c[f+12>>2];c[e+16>>2]=c[f+16>>2];c[e+20>>2]=c[f+20>>2];if((c[a>>2]|0)!=3){i=b;return}a=c[d+32>>2]|0;if((a|0)==0){i=b;return}d=bP(a|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=d;i=b;return}function gG(a){a=a|0;var b=0;a=i;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=b-1;if((c[14296+(b*24&-1)>>2]|0)!=3){i=a;return}uu(c[14304+(b*24&-1)>>2]|0);i=a;return}function gH(a){a=a|0;var b=0,d=0,e=0,f=0;b=i;d=c[a>>2]|0;a=d+48|0;e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e+1|0;c[6354]=f;e=14296+(f*24&-1)|0;f=a;c[e>>2]=c[f>>2];c[e+4>>2]=c[f+4>>2];c[e+8>>2]=c[f+8>>2];c[e+12>>2]=c[f+12>>2];c[e+16>>2]=c[f+16>>2];c[e+20>>2]=c[f+20>>2];if((c[a>>2]|0)!=3){i=b;return}a=c[d+56>>2]|0;if((a|0)==0){i=b;return}d=bP(a|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=d;i=b;return}function gI(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;b=i;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=c[14304+(d*24&-1)>>2]|0;g=c[a>>2]|0;a=g+24+(f*24&-1)|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d;e=14296+(d*24&-1)|0;d=a;c[e>>2]=c[d>>2];c[e+4>>2]=c[d+4>>2];c[e+8>>2]=c[d+8>>2];c[e+12>>2]=c[d+12>>2];c[e+16>>2]=c[d+16>>2];c[e+20>>2]=c[d+20>>2];if((c[a>>2]|0)!=3){i=b;return}a=c[g+24+(f*24&-1)+8>>2]|0;if((a|0)==0){i=b;return}f=bP(a|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=f;i=b;return}function gJ(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;b=i;i=i+24|0;d=c[a>>2]|0;a=d+8|0;if((c[a>>2]|0)==0){uf(-1,168872,(v=i,i=i+8|0,c[v>>2]=c[d+4>>2],v)|0)}e=d+24|0;f=b|0;g=e;c[f>>2]=c[g>>2];c[f+4>>2]=c[g+4>>2];c[f+8>>2]=c[g+8>>2];c[f+12>>2]=c[g+12>>2];c[f+16>>2]=c[g+16>>2];c[f+20>>2]=c[g+20>>2];h=c[6354]|0;if((h|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=h-1;j=14296+(h*24&-1)|0;c[g>>2]=c[j>>2];c[g+4>>2]=c[j+4>>2];c[g+8>>2]=c[j+8>>2];c[g+12>>2]=c[j+12>>2];c[g+16>>2]=c[j+16>>2];c[g+20>>2]=c[j+20>>2];j=c[d+16>>2]|0;if((j|0)!=1){uf(-1,138760,(v=i,i=i+16|0,c[v>>2]=c[d+4>>2],c[v+8>>2]=j,v)|0)}j=c[6932]|0;c[6932]=j+1;if((j|0)>250){uf(-1,137088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=c[a>>2]|0;a=c[9340]|0;h=c[j>>2]|0;k=0;while(1){if((k|0)>=(h|0)){break}l=c[j+8+(k<<5)>>2]|0;c[9340]=1;cM[c[41164+(l<<3)>>2]&511](j+8+(k<<5)+8|0);m=c[9340]|0;if((l-37|0)>>>0<4|(m|0)==1){k=m+k|0}else{n=12954;break}}if((n|0)==12954){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=206200,v)|0)}c[9340]=a;a=e|0;if((c[a>>2]|0)!=3){c[g>>2]=c[f>>2];c[g+4>>2]=c[f+4>>2];c[g+8>>2]=c[f+8>>2];c[g+12>>2]=c[f+12>>2];c[g+16>>2]=c[f+16>>2];c[g+20>>2]=c[f+20>>2];o=c[6932]|0;p=o-1|0;c[6932]=p;i=b;return}uu(c[d+32>>2]|0);c[a>>2]=1;c[g>>2]=c[f>>2];c[g+4>>2]=c[f+4>>2];c[g+8>>2]=c[f+8>>2];c[g+12>>2]=c[f+12>>2];c[g+16>>2]=c[f+16>>2];c[g+20>>2]=c[f+20>>2];o=c[6932]|0;p=o-1|0;c[6932]=p;i=b;return}function gK(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;b=i;i=i+288|0;d=b|0;e=c[a>>2]|0;a=e+8|0;if((c[a>>2]|0)==0){uf(-1,168872,(v=i,i=i+8|0,c[v>>2]=c[e+4>>2],v)|0)}f=d;g=e+24|0;c[f>>2]=c[g>>2];c[f+4>>2]=c[g+4>>2];c[f+8>>2]=c[g+8>>2];c[f+12>>2]=c[g+12>>2];c[f+16>>2]=c[g+16>>2];c[f+20>>2]=c[g+20>>2];f=d+24|0;h=e+48|0;c[f>>2]=c[h>>2];c[f+4>>2]=c[h+4>>2];c[f+8>>2]=c[h+8>>2];c[f+12>>2]=c[h+12>>2];c[f+16>>2]=c[h+16>>2];c[f+20>>2]=c[h+20>>2];h=d+48|0;f=e+72|0;c[h>>2]=c[f>>2];c[h+4>>2]=c[f+4>>2];c[h+8>>2]=c[f+8>>2];c[h+12>>2]=c[f+12>>2];c[h+16>>2]=c[f+16>>2];c[h+20>>2]=c[f+20>>2];f=d+72|0;h=e+96|0;c[f>>2]=c[h>>2];c[f+4>>2]=c[h+4>>2];c[f+8>>2]=c[h+8>>2];c[f+12>>2]=c[h+12>>2];c[f+16>>2]=c[h+16>>2];c[f+20>>2]=c[h+20>>2];h=d+96|0;f=e+120|0;c[h>>2]=c[f>>2];c[h+4>>2]=c[f+4>>2];c[h+8>>2]=c[f+8>>2];c[h+12>>2]=c[f+12>>2];c[h+16>>2]=c[f+16>>2];c[h+20>>2]=c[f+20>>2];f=d+120|0;h=e+144|0;c[f>>2]=c[h>>2];c[f+4>>2]=c[h+4>>2];c[f+8>>2]=c[h+8>>2];c[f+12>>2]=c[h+12>>2];c[f+16>>2]=c[h+16>>2];c[f+20>>2]=c[h+20>>2];h=d+144|0;f=e+168|0;c[h>>2]=c[f>>2];c[h+4>>2]=c[f+4>>2];c[h+8>>2]=c[f+8>>2];c[h+12>>2]=c[f+12>>2];c[h+16>>2]=c[f+16>>2];c[h+20>>2]=c[f+20>>2];f=d+168|0;h=e+192|0;c[f>>2]=c[h>>2];c[f+4>>2]=c[h+4>>2];c[f+8>>2]=c[h+8>>2];c[f+12>>2]=c[h+12>>2];c[f+16>>2]=c[h+16>>2];c[f+20>>2]=c[h+20>>2];h=d+192|0;f=e+216|0;c[h>>2]=c[f>>2];c[h+4>>2]=c[f+4>>2];c[h+8>>2]=c[f+8>>2];c[h+12>>2]=c[f+12>>2];c[h+16>>2]=c[f+16>>2];c[h+20>>2]=c[f+20>>2];f=d+216|0;h=e+240|0;c[f>>2]=c[h>>2];c[f+4>>2]=c[h+4>>2];c[f+8>>2]=c[h+8>>2];c[f+12>>2]=c[h+12>>2];c[f+16>>2]=c[h+16>>2];c[f+20>>2]=c[h+20>>2];h=d+240|0;f=e+264|0;c[h>>2]=c[f>>2];c[h+4>>2]=c[f+4>>2];c[h+8>>2]=c[f+8>>2];c[h+12>>2]=c[f+12>>2];c[h+16>>2]=c[f+16>>2];c[h+20>>2]=c[f+20>>2];f=d+264|0;h=e+288|0;c[f>>2]=c[h>>2];c[f+4>>2]=c[h+4>>2];c[f+8>>2]=c[h+8>>2];c[f+12>>2]=c[h+12>>2];c[f+16>>2]=c[h+16>>2];c[f+20>>2]=c[h+20>>2];h=c[6354]|0;if((h|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=h-1|0;c[6354]=f;j=c[14304+(h*24&-1)>>2]|0;h=c[e+16>>2]|0;if((j|0)!=(h|0)){uf(-1,123608,(v=i,i=i+24|0,c[v>>2]=c[e+4>>2],c[v+8>>2]=h,c[v+16>>2]=(h|0)==1?0:115,v)|0)}L17671:do{if((j|0)>12){h=j-12|0;if((h|0)>0){k=0;l=f}else{m=11;n=f;o=12972;break}while(1){if((l|0)<0){break}p=l-1|0;c[6354]=p;q=14296+(l*24&-1)|0;c[g>>2]=c[q>>2];c[g+4>>2]=c[q+4>>2];c[g+8>>2]=c[q+8>>2];c[g+12>>2]=c[q+12>>2];c[g+16>>2]=c[q+16>>2];c[g+20>>2]=c[q+20>>2];q=k+1|0;if((q|0)<(h|0)){k=q;l=p}else{m=11;n=p;o=12972;break L17671}}uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h=j-1|0;if((h|0)>-1){m=h;n=f;o=12972}}}while(0);L17678:do{if((o|0)==12972){while(1){o=0;if((n|0)<0){break}f=n-1|0;c[6354]=f;j=e+24+(m*24&-1)|0;l=14296+(n*24&-1)|0;c[j>>2]=c[l>>2];c[j+4>>2]=c[l+4>>2];c[j+8>>2]=c[l+8>>2];c[j+12>>2]=c[l+12>>2];c[j+16>>2]=c[l+16>>2];c[j+20>>2]=c[l+20>>2];if((m|0)>0){m=m-1|0;n=f;o=12972}else{break L17678}}uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);n=c[6932]|0;c[6932]=n+1;if((n|0)>250){uf(-1,137088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=c[a>>2]|0;a=c[9340]|0;m=c[n>>2]|0;f=0;while(1){if((f|0)>=(m|0)){break}l=c[n+8+(f<<5)>>2]|0;c[9340]=1;cM[c[41164+(l<<3)>>2]&511](n+8+(f<<5)+8|0);j=c[9340]|0;if((l-37|0)>>>0<4|(j|0)==1){f=j+f|0}else{o=12980;break}}if((o|0)==12980){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=206200,v)|0)}c[9340]=a;c[6932]=(c[6932]|0)-1;a=0;do{o=e+24+(a*24&-1)|0;f=o|0;if((c[f>>2]|0)==3){uu(c[e+24+(a*24&-1)+8>>2]|0);c[f>>2]=1}f=o;o=d+(a*24&-1)|0;c[f>>2]=c[o>>2];c[f+4>>2]=c[o+4>>2];c[f+8>>2]=c[o+8>>2];c[f+12>>2]=c[o+12>>2];c[f+16>>2]=c[o+16>>2];c[f+20>>2]=c[o+20>>2];a=a+1|0;}while((a|0)<12);i=b;return}function gL(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0.0,s=0,t=0.0,u=0.0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0,D=0.0,E=0.0,F=0.0;d=i;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=c[14296+(e*24&-1)>>2]|0;j=c[14304+(e*24&-1)>>2]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=e-2|0;c[6354]=k;l=c[14304+(f*24&-1)>>2]|0;if((k|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=c[14296+(f*24&-1)>>2]|0;c[6354]=e-3;e=c[14304+(k*24&-1)>>2]|0;if(!((m|0)==1&(g|0)==1)){uf(-1,106264,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[14296+(k*24&-1)>>2]|0)==3){n=43240}else{uf(-1,98792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}while(1){o=c[n>>2]|0;if((o|0)==0){p=12998;break}if((aY(e|0,c[o+4>>2]|0)|0)==0){break}else{n=o|0}}if((p|0)==12998){uf(-1,93136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}a[o+8|0]=0;n=c[b>>2]|0;if((n|0)==0){uf(-1,85336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L17723:do{if((l|0)>(j|0)){q=0.0;r=0.0;s=0}else{b=o+16|0;k=o+24|0;g=n+8|0;t=0.0;m=l;u=0.0;L17725:while(1){c[b>>2]=1;c[k>>2]=m;f=c[g>>2]|0;w=c[9340]|0;x=c[f>>2]|0;y=0;while(1){if((y|0)>=(x|0)){break}z=c[f+8+(y<<5)>>2]|0;c[9340]=1;cM[c[41164+(z<<3)>>2]&511](f+8+(y<<5)+8|0);A=c[9340]|0;if((z-37|0)>>>0<4|(A|0)==1){y=A+y|0}else{p=13006;break L17725}}c[9340]=w;y=c[6354]|0;if((y|0)<0){p=13008;break}c[6354]=y-1;f=14296+(y*24&-1)|0;x=c[f>>2]|0;A=c[f+4>>2]|0;f=14304+(y*24&-1)|0;B=c[f>>2]|0;if((x|0)==2){C=+h[14312+(y*24&-1)>>3];D=+h[f>>3]}else if((x|0)==3){p=13013;break}else if((x|0)==1){C=0.0;D=+(B|0)}else{p=13011;break}E=u+D;F=t+C;x=m+1|0;if((x|0)>(j|0)){q=F;r=E;s=A;break L17723}else{t=F;m=x;u=E}}if((p|0)==13013){m=B;uz(m,0);uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((p|0)==13011){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((p|0)==13006){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=206200,v)|0)}else if((p|0)==13008){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);uu(e);e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{p=e+1|0;c[6354]=p;e=14296+(p*24&-1)|0;c[e>>2]=2;c[e+4>>2]=s;h[14304+(p*24&-1)>>3]=r;h[14312+(p*24&-1)>>3]=q;i=d;return}}function gM(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=14296+(b*24&-1)|0;f=e;g=e|0;e=c[g>>2]|0;h=f+4|0;j=c[h>>2]|0;k=14304+(b*24&-1)|0;l=c[k>>2]|0;m=f+12|0;f=a|0;c[f>>2]=c[m>>2];c[f+4>>2]=c[m+4>>2];c[f+8>>2]=c[m+8>>2];if((e|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=b;c[g>>2]=1;c[h>>2]=j;c[k>>2]=(l|0)==0&1;c[m>>2]=c[f>>2];c[m+4>>2]=c[f+4>>2];c[m+8>>2]=c[f+8>>2];i=a;return}}function gN(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=14296+(b*24&-1)|0;f=e;g=e|0;e=c[g>>2]|0;h=f+4|0;j=c[h>>2]|0;k=14304+(b*24&-1)|0;l=c[k>>2]|0;m=f+12|0;f=a|0;c[f>>2]=c[m>>2];c[f+4>>2]=c[m+4>>2];c[f+8>>2]=c[m+8>>2];if((e|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=b;c[g>>2]=1;c[h>>2]=j;c[k>>2]=l^-1;c[m>>2]=c[f>>2];c[m+4>>2]=c[f+4>>2];c[m+8>>2]=c[f+8>>2];i=a;return}}function gO(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=c[14304+(b*24&-1)>>2]|0;if((c[14296+(b*24&-1)>>2]|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=b-2|0;c[6354]=f;b=14296+(d*24&-1)|0;g=b;h=b|0;b=c[h>>2]|0;j=g+4|0;k=c[j>>2]|0;l=14304+(d*24&-1)|0;m=c[l>>2]|0;n=g+12|0;g=a|0;c[g>>2]=c[n>>2];c[g+4>>2]=c[n+4>>2];c[g+8>>2]=c[n+8>>2];if((b|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((m|0)==0){o=(e|0)!=0&1}else{o=1}if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=d;c[h>>2]=1;c[j>>2]=k;c[l>>2]=o;c[n>>2]=c[g>>2];c[n+4>>2]=c[g+4>>2];c[n+8>>2]=c[g+8>>2];i=a;return}}function gP(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=c[14304+(b*24&-1)>>2]|0;if((c[14296+(b*24&-1)>>2]|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=b-2|0;c[6354]=f;b=14296+(d*24&-1)|0;g=b;h=b|0;b=c[h>>2]|0;j=g+4|0;k=c[j>>2]|0;l=14304+(d*24&-1)|0;m=c[l>>2]|0;n=g+12|0;g=a|0;c[g>>2]=c[n>>2];c[g+4>>2]=c[n+4>>2];c[g+8>>2]=c[n+8>>2];if((b|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((m|0)==0){o=0}else{o=(e|0)!=0&1}if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=d;c[h>>2]=1;c[j>>2]=k;c[l>>2]=o;c[n>>2]=c[g>>2];c[n+4>>2]=c[g+4>>2];c[n+8>>2]=c[g+8>>2];i=a;return}}function gQ(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=c[14304+(b*24&-1)>>2]|0;if((c[14296+(b*24&-1)>>2]|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=b-2|0;c[6354]=f;b=14296+(d*24&-1)|0;g=b;h=b|0;b=c[h>>2]|0;j=g+4|0;k=c[j>>2]|0;l=14304+(d*24&-1)|0;m=c[l>>2]|0;n=g+12|0;g=a|0;c[g>>2]=c[n>>2];c[g+4>>2]=c[n+4>>2];c[g+8>>2]=c[n+8>>2];if((b|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=d;c[h>>2]=1;c[j>>2]=k;c[l>>2]=m|e;c[n>>2]=c[g>>2];c[n+4>>2]=c[g+4>>2];c[n+8>>2]=c[g+8>>2];i=a;return}}function gR(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=c[14304+(b*24&-1)>>2]|0;if((c[14296+(b*24&-1)>>2]|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=b-2|0;c[6354]=f;b=14296+(d*24&-1)|0;g=b;h=b|0;b=c[h>>2]|0;j=g+4|0;k=c[j>>2]|0;l=14304+(d*24&-1)|0;m=c[l>>2]|0;n=g+12|0;g=a|0;c[g>>2]=c[n>>2];c[g+4>>2]=c[n+4>>2];c[g+8>>2]=c[n+8>>2];if((b|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=d;c[h>>2]=1;c[j>>2]=k;c[l>>2]=m^e;c[n>>2]=c[g>>2];c[n+4>>2]=c[g+4>>2];c[n+8>>2]=c[g+8>>2];i=a;return}}function gS(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=c[14304+(b*24&-1)>>2]|0;if((c[14296+(b*24&-1)>>2]|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=b-2|0;c[6354]=f;b=14296+(d*24&-1)|0;g=b;h=b|0;b=c[h>>2]|0;j=g+4|0;k=c[j>>2]|0;l=14304+(d*24&-1)|0;m=c[l>>2]|0;n=g+12|0;g=a|0;c[g>>2]=c[n>>2];c[g+4>>2]=c[n+4>>2];c[g+8>>2]=c[n+8>>2];if((b|0)!=1){uf(-1,207704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=d;c[h>>2]=1;c[j>>2]=k;c[l>>2]=m&e;c[n>>2]=c[g>>2];c[n+4>>2]=c[g+4>>2];c[n+8>>2]=c[g+8>>2];i=a;return}}function gT(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0,s=0,t=0.0,u=0,w=0;a=i;i=i+16|0;b=a|0;d=a+8|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=c[g>>2]|0;k=c[g+4>>2]|0;g=14304+(e*24&-1)|0;l=+h[g>>3];m=c[g>>2]|0;h[d>>3]=l;n=+h[14312+(e*24&-1)>>3];e=m;m=e;do{if((j|0)==3){o=+uz(m,b);g=(m|0)==(c[b>>2]|0);uu(m);if(g){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[d>>3]=o;p=0.0;q=o;r=c[6354]|0;s=13099;break}}else{if((j|0)==2){p=n;q=l;r=f;s=13099;break}else if((j|0)==1){c[d>>2]=-e;t=n;u=1;w=f;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((s|0)==13099){h[d>>3]=-0.0-q;t=-0.0-p;u=2;w=r}if((w|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{r=w+1|0;c[6354]=r;w=14296+(r*24&-1)|0;c[w>>2]=u;c[w+4>>2]=k;h[14304+(r*24&-1)>>3]=+h[d>>3];h[14312+(r*24&-1)>>3]=t;i=a;return}}function gU(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0.0,p=0,q=0,r=0.0,s=0,t=0.0,u=0,w=0.0,x=0.0,y=0,z=0,A=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];n=+h[14312+(f*24&-1)>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){o=+uz(l,d);p=(l|0)==(c[d>>2]|0);uu(l);if(p){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=o;q=2;r=0.0;s=c[6354]|0;t=o;u=c[k>>2]|0;break}}else{q=j;r=n;s=g;t=m;u=f}}while(0);if((s|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=s-1;f=14296+(s*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(s*24&-1)|0;m=+h[f>>3];l=c[f>>2]|0;h[e>>3]=m;n=+h[14312+(s*24&-1)>>3];s=l;l=s;do{if((g|0)==3){o=+uz(l,b);f=(l|0)==(c[b>>2]|0);uu(l);if(f){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[e>>3]=o;w=0.0;x=o;y=13122;break}}else{if((g|0)==2){w=n;x=m;y=13122;break}else if((g|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((q|0)==1){z=(s|0)==(u|0)&1;A=n;break}else if((q|0)==2){if(+(s|0)!=t){z=0;A=n;break}z=r==0.0&1;A=n;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((y|0)==13122){if((q|0)==1){if(+(u|0)!=x){z=0;A=w;break}z=w==0.0&1;A=w;break}else if((q|0)==2){if(x!=t){z=0;A=w;break}z=w==r&1;A=w;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);c[e>>2]=z;z=c[6354]|0;if((z|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{q=z+1|0;c[6354]=q;z=14296+(q*24&-1)|0;c[z>>2]=1;c[z+4>>2]=j;h[14304+(q*24&-1)>>3]=+h[e>>3];h[14312+(q*24&-1)>>3]=A;i=a;return}}function gV(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0.0,p=0,q=0,r=0.0,s=0,t=0.0,u=0,w=0.0,x=0.0,y=0,z=0,A=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];n=+h[14312+(f*24&-1)>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){o=+uz(l,d);p=(l|0)==(c[d>>2]|0);uu(l);if(p){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=o;q=2;r=0.0;s=c[6354]|0;t=o;u=c[k>>2]|0;break}}else{q=j;r=n;s=g;t=m;u=f}}while(0);if((s|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=s-1;f=14296+(s*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(s*24&-1)|0;m=+h[f>>3];l=c[f>>2]|0;h[e>>3]=m;n=+h[14312+(s*24&-1)>>3];s=l;l=s;do{if((g|0)==3){o=+uz(l,b);f=(l|0)==(c[b>>2]|0);uu(l);if(f){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[e>>3]=o;w=0.0;x=o;y=13150;break}}else{if((g|0)==2){w=n;x=m;y=13150;break}else if((g|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((q|0)==2){if(+(s|0)!=t){z=1;A=n;break}z=r!=0.0&1;A=n;break}else if((q|0)==1){z=(s|0)!=(u|0)&1;A=n;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((y|0)==13150){if((q|0)==2){if(x!=t){z=1;A=w;break}z=w!=r&1;A=w;break}else if((q|0)==1){if(+(u|0)!=x){z=1;A=w;break}z=w!=0.0&1;A=w;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);c[e>>2]=z;z=c[6354]|0;if((z|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{u=z+1|0;c[6354]=u;z=14296+(u*24&-1)|0;c[z>>2]=1;c[z+4>>2]=j;h[14304+(u*24&-1)>>3]=+h[e>>3];h[14312+(u*24&-1)>>3]=A;i=a;return}}function gW(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0,p=0,q=0,r=0.0,s=0,t=0.0,u=0.0,w=0.0,x=0,y=0,z=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){n=+uz(l,d);o=(l|0)==(c[d>>2]|0);uu(l);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=2;q=c[6354]|0;r=n;s=c[k>>2]|0;break}}else{p=j;q=g;r=m;s=f}}while(0);if((q|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q-1;f=14296+(q*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(q*24&-1)|0;m=+h[f>>3];l=c[f>>2]|0;h[e>>3]=m;n=+h[14312+(q*24&-1)>>3];q=l;l=q;do{if((g|0)==3){t=+uz(l,b);f=(l|0)==(c[b>>2]|0);uu(l);if(f){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[e>>3]=t;u=0.0;w=t;x=13177;break}}else{if((g|0)==2){u=n;w=m;x=13177;break}else if((g|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((p|0)==2){y=+(q|0)>r;z=n;break}else if((p|0)==1){y=(q|0)>(s|0);z=n;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((x|0)==13177){if((p|0)==1){y=w>+(s|0);z=u;break}else if((p|0)==2){y=w>r;z=u;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);c[e>>2]=y&1;y=c[6354]|0;if((y|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{p=y+1|0;c[6354]=p;y=14296+(p*24&-1)|0;c[y>>2]=1;c[y+4>>2]=j;h[14304+(p*24&-1)>>3]=+h[e>>3];h[14312+(p*24&-1)>>3]=z;i=a;return}}function gX(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0,p=0,q=0,r=0.0,s=0,t=0.0,u=0.0,w=0.0,x=0,y=0,z=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){n=+uz(l,d);o=(l|0)==(c[d>>2]|0);uu(l);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=2;q=c[6354]|0;r=n;s=c[k>>2]|0;break}}else{p=j;q=g;r=m;s=f}}while(0);if((q|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q-1;f=14296+(q*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(q*24&-1)|0;m=+h[f>>3];l=c[f>>2]|0;h[e>>3]=m;n=+h[14312+(q*24&-1)>>3];q=l;l=q;do{if((g|0)==3){t=+uz(l,b);f=(l|0)==(c[b>>2]|0);uu(l);if(f){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[e>>3]=t;u=0.0;w=t;x=13202;break}}else{if((g|0)==2){u=n;w=m;x=13202;break}else if((g|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((p|0)==1){y=(q|0)<(s|0);z=n;break}else if((p|0)==2){y=+(q|0)<r;z=n;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((x|0)==13202){if((p|0)==2){y=w<r;z=u;break}else if((p|0)==1){y=w<+(s|0);z=u;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);c[e>>2]=y&1;y=c[6354]|0;if((y|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{s=y+1|0;c[6354]=s;y=14296+(s*24&-1)|0;c[y>>2]=1;c[y+4>>2]=j;h[14304+(s*24&-1)>>3]=+h[e>>3];h[14312+(s*24&-1)>>3]=z;i=a;return}}function gY(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0,p=0,q=0,r=0.0,s=0,t=0.0,u=0.0,w=0.0,x=0,y=0,z=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){n=+uz(l,d);o=(l|0)==(c[d>>2]|0);uu(l);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=2;q=c[6354]|0;r=n;s=c[k>>2]|0;break}}else{p=j;q=g;r=m;s=f}}while(0);if((q|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q-1;f=14296+(q*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(q*24&-1)|0;m=+h[f>>3];l=c[f>>2]|0;h[e>>3]=m;n=+h[14312+(q*24&-1)>>3];q=l;l=q;do{if((g|0)==3){t=+uz(l,b);f=(l|0)==(c[b>>2]|0);uu(l);if(f){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[e>>3]=t;u=0.0;w=t;x=13227;break}}else{if((g|0)==2){u=n;w=m;x=13227;break}else if((g|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((p|0)==2){y=+(q|0)>=r;z=n;break}else if((p|0)==1){y=(q|0)>=(s|0);z=n;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((x|0)==13227){if((p|0)==1){y=w>=+(s|0);z=u;break}else if((p|0)==2){y=w>=r;z=u;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);c[e>>2]=y&1;y=c[6354]|0;if((y|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{p=y+1|0;c[6354]=p;y=14296+(p*24&-1)|0;c[y>>2]=1;c[y+4>>2]=j;h[14304+(p*24&-1)>>3]=+h[e>>3];h[14312+(p*24&-1)>>3]=z;i=a;return}}function gZ(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0,p=0,q=0,r=0.0,s=0,t=0.0,u=0.0,w=0.0,x=0,y=0,z=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){n=+uz(l,d);o=(l|0)==(c[d>>2]|0);uu(l);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=2;q=c[6354]|0;r=n;s=c[k>>2]|0;break}}else{p=j;q=g;r=m;s=f}}while(0);if((q|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q-1;f=14296+(q*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(q*24&-1)|0;m=+h[f>>3];l=c[f>>2]|0;h[e>>3]=m;n=+h[14312+(q*24&-1)>>3];q=l;l=q;do{if((g|0)==3){t=+uz(l,b);f=(l|0)==(c[b>>2]|0);uu(l);if(f){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[e>>3]=t;u=0.0;w=t;x=13252;break}}else{if((g|0)==2){u=n;w=m;x=13252;break}else if((g|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((p|0)==1){y=(q|0)<=(s|0);z=n;break}else if((p|0)==2){y=+(q|0)<=r;z=n;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((x|0)==13252){if((p|0)==2){y=w<=r;z=u;break}else if((p|0)==1){y=w<=+(s|0);z=u;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);c[e>>2]=y&1;y=c[6354]|0;if((y|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{s=y+1|0;c[6354]=s;y=14296+(s*24&-1)|0;c[y>>2]=1;c[y+4>>2]=j;h[14304+(s*24&-1)>>3]=+h[e>>3];h[14312+(s*24&-1)>>3]=z;i=a;return}}function g_(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0.0,p=0,q=0,r=0.0,s=0,t=0.0,u=0,w=0.0,x=0.0,y=0,z=0,A=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];n=+h[14312+(f*24&-1)>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){o=+uz(l,d);p=(l|0)==(c[d>>2]|0);uu(l);if(p){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=o;q=2;r=0.0;s=c[6354]|0;t=o;u=c[k>>2]|0;break}}else{q=j;r=n;s=g;t=m;u=f}}while(0);if((s|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=s-1;f=c[14296+(s*24&-1)>>2]|0;g=14304+(s*24&-1)|0;m=+h[g>>3];j=c[g>>2]|0;g=j;do{if((f|0)==3){n=+uz(g,b);l=(g|0)==(c[b>>2]|0);uu(g);if(!l){w=0.0;x=n;y=13276;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){w=+h[14312+(s*24&-1)>>3];x=m;y=13276;break}else if((f|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((q|0)==1){c[e>>2]=u+j;z=1;A=0.0;break}else if((q|0)==2){h[e>>3]=+(j|0)+t;z=2;A=r;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((y|0)==13276){if((q|0)==2){h[e>>3]=x+t;z=2;A=r+w;break}else if((q|0)==1){h[e>>3]=+(u|0)+x;z=2;A=w;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);u=c[6354]|0;if((u|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{q=u+1|0;c[6354]=q;c[14296+(q*24&-1)>>2]=z;h[14304+(q*24&-1)>>3]=+h[e>>3];h[14312+(q*24&-1)>>3]=A;i=a;return}}function g$(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0.0,p=0,q=0,r=0.0,s=0,t=0.0,u=0,w=0.0,x=0.0,y=0,z=0,A=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];n=+h[14312+(f*24&-1)>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){o=+uz(l,d);p=(l|0)==(c[d>>2]|0);uu(l);if(p){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=o;q=2;r=0.0;s=c[6354]|0;t=o;u=c[k>>2]|0;break}}else{q=j;r=n;s=g;t=m;u=f}}while(0);if((s|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=s-1;f=c[14296+(s*24&-1)>>2]|0;g=14304+(s*24&-1)|0;m=+h[g>>3];j=c[g>>2]|0;g=j;do{if((f|0)==3){n=+uz(g,b);l=(g|0)==(c[b>>2]|0);uu(g);if(!l){w=0.0;x=n;y=17;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){w=+h[14312+(s*24&-1)>>3];x=m;y=17;break}else if((f|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((q|0)==1){c[e>>2]=j-u;z=1;A=0.0;break}else if((q|0)==2){h[e>>3]=+(j|0)-t;z=2;A=-0.0-r;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((y|0)==17){if((q|0)==1){h[e>>3]=x- +(u|0);z=2;A=w;break}else if((q|0)==2){h[e>>3]=x-t;z=2;A=w-r;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{u=q+1|0;c[6354]=u;c[14296+(u*24&-1)>>2]=z;h[14304+(u*24&-1)>>3]=+h[e>>3];h[14312+(u*24&-1)>>3]=A;i=a;return}}function g0(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,l=0,m=0.0,n=0.0,o=0.0,p=0,q=0,r=0.0,s=0,t=0.0,u=0,w=0.0,x=0.0,y=0,z=0,A=0.0;a=i;i=i+24|0;b=a|0;d=a+8|0;e=a+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=c[14296+(f*24&-1)>>2]|0;l=14304+(f*24&-1)|0;m=+h[l>>3];n=+h[14312+(f*24&-1)>>3];f=c[l>>2]|0;l=f;do{if((j|0)==3){o=+uz(l,d);p=(l|0)==(c[d>>2]|0);uu(l);if(p){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=o;q=2;r=0.0;s=c[6354]|0;t=o;u=c[k>>2]|0;break}}else{q=j;r=n;s=g;t=m;u=f}}while(0);if((s|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=s-1;f=c[14296+(s*24&-1)>>2]|0;g=14304+(s*24&-1)|0;m=+h[g>>3];j=c[g>>2]|0;g=j;do{if((f|0)==3){n=+uz(g,b);l=(g|0)==(c[b>>2]|0);uu(g);if(!l){w=0.0;x=n;y=43;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){w=+h[14312+(s*24&-1)>>3];x=m;y=43;break}else if((f|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((q|0)==1){n=+(j|0)*+(u|0);if(+P(+n)<2147483647.0){c[e>>2]=aa(u,j);z=1;A=0.0;break}else{h[e>>3]=n;z=2;A=0.0;break}}else if((q|0)==2){n=+(j|0);h[e>>3]=n*t;z=2;A=r*n;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((y|0)==43){if((q|0)==1){m=+(u|0);h[e>>3]=m*x;z=2;A=w*m;break}else if((q|0)==2){h[e>>3]=x*t-r*w;z=2;A=r*x+w*t;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{u=q+1|0;c[6354]=u;c[14296+(u*24&-1)>>2]=z;h[14304+(u*24&-1)>>3]=+h[e>>3];h[14312+(u*24&-1)>>3]=A;i=a;return}}function g1(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0,m=0,n=0.0,o=0.0,p=0.0,q=0,r=0,s=0.0,t=0,u=0.0,w=0,x=0.0,y=0.0,z=0,A=0,B=0.0;b=i;i=i+24|0;d=b|0;e=b+8|0;f=b+16|0;g=c[6354]|0;if((g|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=g-1|0;c[6354]=j;l=c[14296+(g*24&-1)>>2]|0;m=14304+(g*24&-1)|0;n=+h[m>>3];o=+h[14312+(g*24&-1)>>3];g=c[m>>2]|0;m=g;do{if((l|0)==3){p=+uz(m,e);q=(m|0)==(c[e>>2]|0);uu(m);if(q){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=p;r=2;s=0.0;t=c[6354]|0;u=p;w=c[k>>2]|0;break}}else{r=l;s=o;t=j;u=n;w=g}}while(0);if((t|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=t-1;g=c[14296+(t*24&-1)>>2]|0;j=14304+(t*24&-1)|0;n=+h[j>>3];l=c[j>>2]|0;j=l;do{if((g|0)==3){o=+uz(j,d);m=(j|0)==(c[d>>2]|0);uu(j);if(!m){x=0.0;y=o;z=71;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((g|0)==2){x=+h[14312+(t*24&-1)>>3];y=n;z=71;break}else if((g|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((r|0)==1){if((w|0)==0){c[f>>2]=0;a[1960]=1;A=1;B=0.0;break}else{c[f>>2]=(l|0)/(w|0)&-1;A=1;B=0.0;break}}else if((r|0)==2){o=s*s+u*u;if(o!=0.0){h[f>>3]=u*+(l|0)/o;A=2;B=s*+(-l|0)/o;break}else{h[f>>3]=0.0;a[1960]=1;A=2;B=0.0;break}}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((z|0)==71){if((r|0)==1){if((w|0)==0){h[f>>3]=0.0;a[1960]=1;A=2;B=0.0;break}else{n=+(w|0);h[f>>3]=y/n;A=2;B=x/n;break}}else if((r|0)==2){n=s*s+u*u;if(n!=0.0){h[f>>3]=(s*x+u*y)/n;A=2;B=(x*u-s*y)/n;break}else{h[f>>3]=0.0;a[1960]=1;A=2;B=0.0;break}}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);r=c[6354]|0;if((r|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{w=r+1|0;c[6354]=w;c[14296+(w*24&-1)>>2]=A;h[14304+(w*24&-1)>>3]=+h[f>>3];h[14312+(w*24&-1)>>3]=B;i=b;return}}function g2(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0,m=0,n=0.0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0.0,x=0;b=i;i=i+24|0;d=b|0;e=b+8|0;f=b+16|0;g=c[6354]|0;if((g|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=g-1|0;c[6354]=j;l=c[14296+(g*24&-1)>>2]|0;m=c[14304+(g*24&-1)>>2]|0;g=m;do{if((l|0)==3){n=+uz(g,e);o=(g|0)==(c[e>>2]|0);uu(g);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=2;q=c[6354]|0;r=c[k>>2]|0;break}}else{p=l;q=j;r=m}}while(0);if((q|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=q-1|0;c[6354]=m;j=14296+(q*24&-1)|0;l=j|0;g=c[l>>2]|0;e=j+4|0;j=c[e>>2]|0;o=14304+(q*24&-1)|0;s=c[o>>2]|0;h[f>>3]=+h[o>>3];t=14312+(q*24&-1)|0;n=+h[t>>3];u=s;s=u;if((g|0)==3){w=+uz(s,d);x=(s|0)==(c[d>>2]|0);uu(s);if(x){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}h[f>>3]=w;uf(-1,224848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(!((g|0)==1&(p|0)==1)){uf(-1,224848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=f;if((r|0)==0){c[p>>2]=0;if((m|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q;c[l>>2]=1;c[e>>2]=j;h[o>>3]=+h[f>>3];h[t>>3]=n;a[1960]=1;i=b;return}else{c[p>>2]=(u|0)%(r|0)&-1;if((m|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q;c[l>>2]=1;c[e>>2]=j;h[o>>3]=+h[f>>3];h[t>>3]=n;i=b;return}}function g3(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0,m=0,n=0.0,o=0.0,p=0.0,q=0,r=0,s=0.0,t=0,u=0.0,w=0,x=0.0,y=0.0,z=0,A=0,B=0.0,C=0,D=0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,U=0.0,V=0.0;b=i;i=i+24|0;d=b|0;e=b+8|0;f=b+16|0;g=c[6354]|0;if((g|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=g-1|0;c[6354]=j;l=c[14296+(g*24&-1)>>2]|0;m=14304+(g*24&-1)|0;n=+h[m>>3];o=+h[14312+(g*24&-1)>>3];g=c[m>>2]|0;m=g;do{if((l|0)==3){p=+uz(m,e);q=(m|0)==(c[e>>2]|0);uu(m);if(q){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=p;r=2;s=0.0;t=c[6354]|0;u=p;w=c[k>>2]|0;break}}else{r=l;s=o;t=j;u=n;w=g}}while(0);if((t|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=t-1;g=c[14296+(t*24&-1)>>2]|0;j=14304+(t*24&-1)|0;n=+h[j>>3];l=c[j>>2]|0;j=l;do{if((g|0)==3){o=+uz(j,d);m=(j|0)==(c[d>>2]|0);uu(j);if(!m){x=0.0;y=o;z=143;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((g|0)==2){x=+h[14312+(t*24&-1)>>3];y=n;z=143;break}else if((g|0)!=1){uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((r|0)==1){if((l|0)==0){if((w|0)<0){a[1960]=1}c[f>>2]=(w|0)==0&1;A=1;B=0.0;break}o=+R(+(+(l|0)),+(+(w|0)));if(o>2147483647.0|(w|0)<0){h[f>>3]=o;A=2;B=0.0;break}if((w|0)>0){m=0;e=1;while(1){q=aa(l,e);C=m+1|0;if((C|0)<(w|0)){m=C;e=q}else{D=q;break}}}else{D=1}c[f>>2]=D;A=1;B=0.0;break}else if((r|0)==2){if((l|0)==0){if(s!=0.0|u<0.0){a[1960]=1}h[f>>3]=u==0.0?1.0:0.0;A=2;B=0.0;break}o=+R(+(+(((l|0)>-1?l:-l|0)|0)),+(+P(+u)));do{if(u<0.0){if(o!=0.0){E=1.0/o;break}else{a[1960]=1;E=o;break}}else{E=o}}while(0);o=((l|0)>-1?0.0:3.141592653589793)*(-0.0-s);if(o<-706.893623549172){F=0.0}else{F=+Z(+o)}o=E*F;e=(l|0)>-1;p=u*(e?0.0:3.141592653589793)+s*+_(+(+((e?l:-l|0)|0)));G=o*+S(+p);H=o*+T(+p);h[f>>3]=G;A=2;B=H;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((z|0)==143){if((r|0)==1){if(x==0.0){F=+R(+y,+(+(((w|0)>-1?w:-w|0)|0)));do{if((w|0)<0){if(F!=0.0){I=1.0/F;break}else{a[1960]=1;I=F;break}}else{I=F}}while(0);h[f>>3]=I;A=2;B=0.0;break}F=+P(+y);E=+P(+x);if(F>E){n=E/F;J=F*+Q(+(n*n+1.0))}else{n=F/E;J=E*+Q(+(n*n+1.0))}n=+R(+J,+(+(((w|0)>-1?w:-w|0)|0)));do{if((w|0)<0){if(n!=0.0){K=1.0/n;break}else{a[1960]=1;K=n;break}}else{K=n}}while(0);n=+Y(+x,+y)*+(w|0);E=K*+S(+n);F=K*+T(+n);h[f>>3]=E;A=2;B=F;break}else if((r|0)==2){l=x==0.0;if(y==0.0&l){if(s!=0.0|u<0.0){a[1960]=1}h[f>>3]=u==0.0?1.0:0.0;A=2;B=0.0;break}F=+P(+y);E=+P(+x);do{if(l){L=F}else{if(F>E){n=E/F;L=F*+Q(+(n*n+1.0));break}else{n=F/E;L=E*+Q(+(n*n+1.0));break}}}while(0);n=+R(+L,+(+P(+u)));do{if(u<0.0){if(n!=0.0){M=1.0/n;break}else{a[1960]=1;M=n;break}}else{M=n}}while(0);if(l){N=y<0.0?3.141592653589793:0.0}else{N=+Y(+x,+y)}n=N*(-0.0-s);if(n<-706.893623549172){O=0.0}else{O=+Z(+n)}n=M*O;do{if(l){U=F;V=u*(y<0.0?3.141592653589793:0.0)}else{H=u*+Y(+x,+y);if(F>E){G=E/F;U=F*+Q(+(G*G+1.0));V=H;break}else{G=F/E;U=E*+Q(+(G*G+1.0));V=H;break}}}while(0);E=V+s*+_(+U);F=n*+S(+E);H=n*+T(+E);h[f>>3]=F;A=2;B=H;break}else{uf(-1,77088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);r=c[6354]|0;if((r|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{w=r+1|0;c[6354]=w;c[14296+(w*24&-1)>>2]=A;h[14304+(w*24&-1)>>3]=+h[f>>3];h[14312+(w*24&-1)>>3]=B;i=b;return}}function g4(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=f|0;j=c[g>>2]|0;k=f+4|0;f=c[k>>2]|0;l=14304+(d*24&-1)|0;m=c[l>>2]|0;n=m;if((j|0)==3){uz(n,b);o=(n|0)==(c[b>>2]|0);uu(n);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uf(-1,217536,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}if((j|0)!=1){uf(-1,217536,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((m|0)>1){j=m;p=1.0;while(1){q=p*+(j|0);m=j-1|0;if((m|0)>1){j=m;p=q}else{r=q;break}}}else{r=1.0}if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{c[6354]=d;c[g>>2]=2;c[k>>2]=f;h[l>>3]=r;h[14312+(d*24&-1)>>3]=0.0;i=a;return}}function g5(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;a=i;i=i+16|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=c[14296+(d*24&-1)>>2]|0;g=c[14304+(d*24&-1)>>2]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-2;d=c[14296+(e*24&-1)>>2]|0;h=c[14304+(e*24&-1)>>2]|0;if((f|0)==1){e=ut(32)|0;do{if((e|0)==0){gk();j=ut(32)|0;if((j|0)!=0){k=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=211880,v)|0)}else{k=e}}while(0);e=k;a0(k|0,32,21e4,(v=i,i=i+8|0,c[v>>2]=g,v)|0);l=3;m=e}else{l=f;m=g}if(!((d|0)==3&(l|0)==3)){uf(-1,202496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=m;m=uc(h,l)|0;uu(h);uu(l);l=c[6354]|0;if((l|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}h=l+1|0;c[6354]=h;l=14296+(h*24&-1)|0;d=l;c[l>>2]=3;c[d+4>>2]=n;c[14304+(h*24&-1)>>2]=m;h=d+12|0;d=b|0;c[h>>2]=c[d>>2];c[h+4>>2]=c[d+4>>2];c[h+8>>2]=c[d+8>>2];if((m|0)==0){uu(m);i=a;return}d=bP(m|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=d;uu(m);i=a;return}function g6(a){a=a|0;var b=0,d=0,e=0,f=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=c[14304+(b*24&-1)>>2]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[14296+(b*24&-1)>>2]|0;c[6354]=b-2;b=c[14304+(d*24&-1)>>2]|0;if(!((c[14296+(d*24&-1)>>2]|0)==3&(f|0)==3)){uf(-1,202496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=aY(b|0,e|0)|0;uu(b);uu(e);e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{b=e+1|0;c[6354]=b;e=14296+(b*24&-1)|0;c[e>>2]=1;c[14304+(b*24&-1)>>2]=(f|0)==0&1;f=e+12|0;e=a|0;c[f>>2]=c[e>>2];c[f+4>>2]=c[e+4>>2];c[f+8>>2]=c[e+8>>2];i=a;return}}function g7(a){a=a|0;var b=0,d=0,e=0,f=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=c[14304+(b*24&-1)>>2]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[14296+(b*24&-1)>>2]|0;c[6354]=b-2;b=c[14304+(d*24&-1)>>2]|0;if(!((c[14296+(d*24&-1)>>2]|0)==3&(f|0)==3)){uf(-1,202496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=aY(b|0,e|0)|0;uu(b);uu(e);e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{b=e+1|0;c[6354]=b;e=14296+(b*24&-1)|0;c[e>>2]=1;c[14304+(b*24&-1)>>2]=(f|0)!=0&1;f=e+12|0;e=a|0;c[f>>2]=c[e>>2];c[f+4>>2]=c[e+4>>2];c[f+8>>2]=c[e+8>>2];i=a;return}}function g8(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;b=i;i=i+16|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=c[14304+(e*24&-1)>>2]|0;if((c[14296+(e*24&-1)>>2]|0)!=3){uf(-1,197056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((c[11252]|0)==15){e=a[f]|0;if(e<<24>>24==0){g=0;break}else{h=0;j=0;k=e}while(1){e=((k&-64)<<24>>24!=-128&1)+j|0;l=h+1|0;m=a[f+l|0]|0;if(m<<24>>24==0){g=e;break}else{h=l;j=e;k=m}}}else{g=uA(f|0)|0}}while(0);uu(f);f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{k=f+1|0;c[6354]=k;f=14296+(k*24&-1)|0;c[f>>2]=1;c[14304+(k*24&-1)>>2]=g;g=f+12|0;f=d|0;c[g>>2]=c[f>>2];c[g+4>>2]=c[f+4>>2];c[g+8>>2]=c[f+8>>2];i=b;return}}function g9(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;a=i;i=i+16|0;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=c[14304+(b*24&-1)>>2]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[14296+(b*24&-1)>>2]|0;c[6354]=b-2;b=c[14304+(d*24&-1)>>2]|0;if((f|0)!=3){uf(-1,185704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[14296+(d*24&-1)>>2]|0)!=3){uf(-1,185704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=aQ(b|0,e|0)|0;if((d|0)==0){g=0}else{g=(1-b|0)+d|0}uu(e);uu(b);b=c[6354]|0;if((b|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{e=b+1|0;c[6354]=e;b=14296+(e*24&-1)|0;c[b>>2]=1;c[14304+(e*24&-1)>>2]=g;g=b+12|0;b=a|0;c[g>>2]=c[b>>2];c[g+4>>2]=c[b+4>>2];c[g+8>>2]=c[b+8>>2];i=a;return}}function ha(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0;b=i;i=i+16|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=c[14296+(e*24&-1)>>2]|0;h=c[14304+(e*24&-1)>>2]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=e-2|0;c[6354]=j;k=c[14304+(f*24&-1)>>2]|0;if((j|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=c[14296+(f*24&-1)>>2]|0;f=e-3|0;c[6354]=f;m=c[14304+(j*24&-1)>>2]|0;if(!((g|0)==1&(l|0)==1)){uf(-1,180448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[14296+(j*24&-1)>>2]|0)!=3){uf(-1,176264,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=(c[11252]|0)==15;do{if(j){l=a[m]|0;if(l<<24>>24==0){n=0;break}else{o=0;p=0;q=l}while(1){l=((q&-64)<<24>>24!=-128&1)+p|0;g=o+1|0;r=a[m+g|0]|0;if(r<<24>>24==0){n=l;break}else{o=g;p=l;q=r}}}else{n=uA(m|0)|0}}while(0);do{if(h>>>0>n>>>0){if(!j){s=uA(m|0)|0;break}q=a[m]|0;if(q<<24>>24==0){s=0;break}else{t=0;u=0;w=q}while(1){q=((w&-64)<<24>>24!=-128&1)+u|0;p=t+1|0;o=a[m+p|0]|0;if(o<<24>>24==0){s=q;break}else{t=p;u=q;w=o}}}else{s=h}}while(0);h=(k|0)<1?1:k;if((h|0)>(s|0)){if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-2|0;c[6354]=f;e=14296+(f*24&-1)|0;c[e>>2]=3;c[14304+(f*24&-1)>>2]=179864;f=e+12|0;e=d|0;c[f>>2]=c[e>>2];c[f+4>>2]=c[e+4>>2];c[f+8>>2]=c[e+8>>2];e=bP(179864)|0;c[14304+((c[6354]|0)*24&-1)>>2]=e;uu(m);i=b;return}e=h-1|0;L406:do{if(j){if((e|0)<1){x=m}else{h=a[m]|0;L411:do{if(h<<24>>24==0){y=m}else{f=0;k=0;w=m;u=h;while(1){if((u&-64)<<24>>24==-128){z=k}else{if((k|0)==(e|0)){y=w;break L411}else{z=k+1|0}}t=f+1|0;n=m+t|0;o=a[n]|0;if(o<<24>>24==0){y=n;break}else{f=t;k=z;w=n;u=o}}}}while(0);if(j){x=y}else{A=y;B=297;break}}if((s|0)<1){C=m;D=x;break}h=a[m]|0;if(h<<24>>24==0){C=m;D=x;break}else{E=0;F=0;G=m;H=h}while(1){if((H&-64)<<24>>24==-128){I=F}else{if((F|0)==(s|0)){C=G;D=x;break L406}else{I=F+1|0}}h=E+1|0;u=m+h|0;w=a[u]|0;if(w<<24>>24==0){C=u;D=x;break}else{E=h;F=I;G=u;H=w}}}else{A=m+e|0;B=297}}while(0);if((B|0)==297){C=m+s|0;D=A}a[C]=0;C=c[6354]|0;if((C|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}A=C+1|0;c[6354]=A;C=14296+(A*24&-1)|0;s=C;c[C>>2]=3;c[s+4>>2]=J;c[14304+(A*24&-1)>>2]=D;A=s+12|0;s=d|0;c[A>>2]=c[s>>2];c[A+4>>2]=c[s+4>>2];c[A+8>>2]=c[s+8>>2];if((D|0)==0){uu(m);i=b;return}s=bP(D|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=s;uu(m);i=b;return}function hb(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0;b=i;i=i+16|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=c[14304+(e*24&-1)>>2]|0;if((c[14296+(e*24&-1)>>2]|0)!=1){uf(-1,168392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-2;e=c[14304+(f*24&-1)>>2]|0;if((c[14296+(f*24&-1)>>2]|0)==3){h=e;j=0;k=179864}else{uf(-1,164176,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}while(1){f=a[h]|0;if(f<<24>>24==0){l=j;m=k;break}else{n=h;o=f}while(1){f=n+1|0;if((aM(o<<24>>24|0)|0)==0){break}n=f;o=a[f]|0}f=a[n]|0;if(f<<24>>24==0){l=j;m=k;break}p=j+1|0;q=(p|0)==(g|0);if(q){r=n}else{r=k}s=n;t=f;while(1){if(t<<24>>24==0){break}f=s+1|0;if((aM(t<<24>>24|0)|0)!=0){break}s=f;t=a[f]|0}if(q){u=325;break}else{h=s;j=p;k=r}}if((u|0)==325){a[s]=0;l=g;m=r}r=(g|0)<0?l:m;m=g>>31&-2;g=c[6354]|0;if((g|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=g+1|0;c[6354]=l;g=14296+(l*24&-1)|0;s=g;c[g>>2]=m+3;c[s+4>>2]=w;c[14304+(l*24&-1)>>2]=r;l=s+12|0;s=d|0;c[l>>2]=c[s>>2];c[l+4>>2]=c[s+4>>2];c[l+8>>2]=c[s+8>>2];if((m|0)!=0|(r|0)==0){uu(e);i=b;return}m=bP(r|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=m;uu(e);i=b;return}function hc(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0.0,Z=0.0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0;b=i;i=i+256|0;d=b|0;e=b+240|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=f-1;g=c[14304+(f*24&-1)>>2]|0;if((g|0)>10){f=g*24&-1;j=ut(f)|0;do{if((j|0)==0){gk();k=ut(f)|0;if((k|0)!=0){l=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=154800,v)|0)}else{l=j}}while(0);m=l}else{m=d|0}l=(g|0)>0;L484:do{if(l){j=0;f=c[6354]|0;while(1){if((f|0)<0){break}k=f-1|0;c[6354]=k;n=m+(j*24&-1)|0;o=14296+(f*24&-1)|0;c[n>>2]=c[o>>2];c[n+4>>2]=c[o+4>>2];c[n+8>>2]=c[o+8>>2];c[n+12>>2]=c[o+12>>2];c[n+16>>2]=c[o+16>>2];c[n+20>>2]=c[o+20>>2];o=j+1|0;if((o|0)<(g|0)){j=o;f=k}else{break L484}}uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);f=g-1|0;if((c[m+(f*24&-1)>>2]|0)!=3){uf(-1,151464,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=m+(f*24&-1)+8|0;k=(uA(c[j>>2]|0)|0)+80|0;o=ut(k)|0;do{if((o|0)==0){gk();n=ut(k)|0;if((n|0)!=0){p=n;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=149624,v)|0)}else{p=o}}while(0);o=c[j>>2]|0;j=bk(o|0,191528)|0;uF(p|0,o|0,j|0);n=o+j|0;o=p+j|0;q=c[8270]|0;do{if((q|0)!=0){if((aY(q|0,139696)|0)==0){break}bA(4,q|0)}}while(0);L502:do{if((f|0)>0){q=o;r=n;s=j;t=k;u=p;w=g-2|0;L504:while(1){x=m+(w*24&-1)|0;y=q;z=t;A=u;while(1){B=y;C=r;D=a[r]|0;L508:while(1){if(D<<24>>24==0){E=B;F=A;break L502}G=C+1|0;H=a[G]|0;if(H<<24>>24==0){I=B;J=C;K=z;L=A;M=D;N=393;break L502}if((a_(C|0,143664,2)|0)==0){O=G;P=B;Q=H}else{break}while(1){H=O+1|0;R=P+1|0;a[P]=Q;S=a[H]|0;if((S<<24>>24|0)==0|(S<<24>>24|0)==37){B=R;C=H;D=S;continue L508}else{O=H;P=R;Q=S}}}T=C+((bk(G|0,191528)|0)+1|0)|0;D=a[T]|0;a[T]=0;if((a[C]|0)!=37){N=363;break L504}S=bk(C|0,20296)|0;R=bk(C|0,20304)|0;H=bk(C|0,20320)|0;U=bk(C|0,20328)|0;if((U|0)<(H|0)&(U|0)<(R|0)&(U|0)<(S|0)){N=365;break L504}do{if((S|0)<(R|0)&(S|0)<(H|0)){if((c[x>>2]|0)!=3){N=372;break L504}b0();U=(z-B|0)+A|0;V=c[m+(w*24&-1)+8>>2]|0;a0(B|0,U|0,C|0,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}else{if((R|0)<(H|0)){W=2}else{if(H>>>0<(uA(C|0)|0)>>>0){W=1}else{N=369;break L504}}V=x|0;if((c[V>>2]|0)==3){N=374;break L504}b0();if((W|0)==2){U=(z-B|0)+A|0;X=c[V>>2]|0;if((X|0)==1){Y=+(c[m+(w*24&-1)+8>>2]|0)}else if((X|0)==2){Y=+h[m+(w*24&-1)+8>>3]}else if((X|0)==3){Y=+uz(c[m+(w*24&-1)+8>>2]|0,0)}else{N=386;break L504}a0(B|0,U|0,C|0,(v=i,i=i+8|0,h[v>>3]=Y,v)|0);break}else if((W|0)==1){U=(z-B|0)+A|0;X=c[V>>2]|0;if((X|0)==1){Z=+(c[m+(w*24&-1)+8>>2]|0)}else if((X|0)==2){Z=+h[m+(w*24&-1)+8>>3]}else if((X|0)==3){Z=+uz(c[m+(w*24&-1)+8>>2]|0,0)}else{N=380;break L504}a0(B|0,U|0,C|0,(v=i,i=i+8|0,c[v>>2]=~~Z,v)|0);break}else{N=388;break L504}}}while(0);a[T]=D;_=uA(A|0)|0;if(_>>>0<(z-2|0)>>>0){break}C=z<<1;B=db(A,C,149624)|0;y=B+s|0;z=C;A=B}y=A+_|0;if((w|0)>0){q=y;r=T;s=_;t=z;u=A;w=w-1|0}else{$=y;aa=T;ab=z;ac=A;N=392;break L502}}if((N|0)==369){uf(-1,106184,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((N|0)==372){uf(-1,142064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((N|0)==386){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((N|0)==388){uf(-1,138488,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((N|0)==363){uf(-1,108e3,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((N|0)==365){uf(-1,107040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((N|0)==374){uf(-1,140392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((N|0)==380){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{$=o;aa=n;ab=k;ac=p;N=392}}while(0);if((N|0)==392){I=$;J=aa;K=ab;L=ac;M=a[aa]|0;N=393}L552:do{if((N|0)==393){if(M<<24>>24==0){E=I;F=L;break}aa=I;ac=J;ab=(K-I|0)+L|0;$=M;while(1){p=ab-1|0;if((p|0)<=0){E=aa;F=L;break L552}if($<<24>>24==37){k=ac+1|0;n=(a[k]|0)==37?k:ac;ad=n;ae=a[n]|0}else{ad=ac;ae=$}n=ad+1|0;k=aa+1|0;a[aa]=ae;o=a[n]|0;if(o<<24>>24==0){E=k;F=L;break}else{aa=k;ac=n;ab=p;$=o}}}}while(0);a[E]=0;E=c[6354]|0;if((E|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L=E+1|0;c[6354]=L;E=14296+(L*24&-1)|0;ae=E;c[E>>2]=3;c[ae+4>>2]=af;c[14304+(L*24&-1)>>2]=F;L=ae+12|0;ae=e|0;c[L>>2]=c[ae>>2];c[L+4>>2]=c[ae+4>>2];c[L+8>>2]=c[ae+8>>2];if((F|0)!=0){ae=bP(F|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=ae}uu(F);if(l){l=0;do{F=m+(l*24&-1)|0;if((c[F>>2]|0)==3){uu(c[m+(l*24&-1)+8>>2]|0);c[F>>2]=1}l=l+1|0;}while((l|0)<(g|0))}if((m|0)!=(d|0)){uu(m)}m=c[8270]|0;if((m|0)==0){i=b;return}if((aY(m|0,139696)|0)==0){i=b;return}bA(4,139696);i=b;return}function hd(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0,m=0,n=0,o=0.0;a=i;i=i+16|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=c[14296+(d*24&-1)>>2]|0;g=14304+(d*24&-1)|0;j=+h[g>>3];k=c[g>>2]|0;g=k;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-2;d=c[14304+(e*24&-1)>>2]|0;if((c[14296+(e*24&-1)>>2]|0)!=3){uf(-1,136912,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=(uA(d|0)|0)+80|0;l=ut(e)|0;do{if((l|0)==0){gk();m=ut(e)|0;if((m|0)!=0){n=m;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=134920,v)|0)}else{n=l}}while(0);if((f|0)==1){o=+(k|0)}else if((f|0)==3){o=+uz(g,0)}else if((f|0)==2){o=j}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}ud(n,e,d,10.0,o);e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e+1|0;c[6354]=f;e=14296+(f*24&-1)|0;c[e>>2]=3;c[14304+(f*24&-1)>>2]=n;f=e+12|0;e=b|0;c[f>>2]=c[e>>2];c[f+4>>2]=c[e+4>>2];c[f+8>>2]=c[e+8>>2];if((n|0)==0){uu(d);uu(n);i=a;return}e=bP(n|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=e;uu(d);uu(n);i=a;return}function he(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0.0,p=0,q=0,r=0,s=0,t=0,u=0.0;b=i;i=i+56|0;d=b|0;e=b+48|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=14296+(f*24&-1)|0;k=c[j>>2]|0;l=c[j+4>>2]|0;j=14304+(f*24&-1)|0;m=+h[j>>3];n=c[j>>2]|0;h[e>>3]=m;o=+h[14312+(f*24&-1)>>3];j=n;n=j;if((g|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=f-2;f=c[14304+(g*24&-1)>>2]|0;if((c[14296+(g*24&-1)>>2]|0)!=3){uf(-1,133408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=uA(f|0)|0;p=g+1|0;q=g+2|0;g=ut(q)|0;do{if((g|0)==0){gk();r=ut(q)|0;if((r|0)!=0){s=r;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=132008,v)|0)}else{s=g}}while(0);uF(s|0,f|0,p|0);ck(s|0,73808,p|0);g=(p<<1)+80|0;p=ut(g)|0;do{if((p|0)==0){gk();q=ut(g)|0;if((q|0)!=0){t=q;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=129704,v)|0)}else{t=p}}while(0);if((k|0)==1){u=+(j|0)}else if((k|0)==3){u=+uz(n,0)}else if((k|0)==2){u=m}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(d,u);j=t4(t,g,s,d,u- +O(+u))|0;if(!((j|0)!=0&(j|0)<(g|0))){uf(-1,128368,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=t+(j-1|0)|0;if((a[g]|0)!=32){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=125936,v)|0)}a[g]=0;if((k|0)==3){uu(n)}uu(f);uu(s);c[e>>2]=t;s=c[6354]|0;if((s|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=s+1|0;c[6354]=f;s=14296+(f*24&-1)|0;c[s>>2]=3;c[s+4>>2]=l;l=c[e>>2]|0;h[14304+(f*24&-1)>>3]=+h[e>>3];h[14312+(f*24&-1)>>3]=o;f=l;if((f|0)==0){uu(t);i=b;return}l=bP(f|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=l;uu(t);i=b;return}function hf(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0.0;a=i;i=i+56|0;b=a|0;d=a+48|0;h[d>>3]=0.0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=c[g+4>>2]|0;k=c[14304+(e*24&-1)>>2]|0;l=k;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=c[g>>2]|0;c[6354]=e-2;e=c[14304+(f*24&-1)>>2]|0;if(!((c[14296+(f*24&-1)>>2]|0)==3&(m|0)==3)){uf(-1,124672,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((e|0)==0|(k|0)==0){uf(-1,123472,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t1(l,e,b,d);n=+t2(b);o=+h[d>>3];uu(l);uu(e);e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{l=e+1|0;c[6354]=l;e=14296+(l*24&-1)|0;c[e>>2]=2;c[e+4>>2]=j;h[14304+(l*24&-1)>>3]=n+o;h[14312+(l*24&-1)>>3]=0.0;i=a;return}}function hg(a){a=a|0;var b=0,d=0,e=0,f=0,g=0.0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0,q=0.0,r=0,s=0,t=0;a=i;i=i+16|0;b=a|0;d=a+8|0;bV(d|0,0);e=d|0;f=~~(+(c[e>>2]|0)+-946684800.0);c[e>>2]=f;g=+(c[d+4>>2]|0)/1.0e6+ +(f|0);f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=f-1|0;c[6354]=d;e=14296+(f*24&-1)|0;j=e|0;k=c[j>>2]|0;l=e+4|0;e=c[l>>2]|0;m=14304+(f*24&-1)|0;n=+h[m>>3];o=c[m>>2]|0;h[b>>3]=n;p=14312+(f*24&-1)|0;q=+h[p>>3];r=o;o=r;if((k|0)==3){if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=f;c[j>>2]=3;c[l>>2]=e;h[m>>3]=n;h[p>>3]=q;if((r|0)==0){s=f}else{r=bP(o|0)|0;o=c[6354]|0;c[14304+(o*24&-1)>>2]=r;s=o}if((s|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=s+1|0;c[6354]=o;s=14296+(o*24&-1)|0;c[s>>2]=2;c[s+4>>2]=t;h[14304+(o*24&-1)>>3]=g;h[14312+(o*24&-1)>>3]=0.0;he(0);i=a;return}else if((k|0)==2){h[b>>3]=g;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=f;c[j>>2]=2;c[l>>2]=e;h[m>>3]=g;h[p>>3]=0.0;i=a;return}else if((k|0)==1){c[b>>2]=~~g;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=f;c[j>>2]=1;c[l>>2]=e;h[m>>3]=+h[b>>3];h[p>>3]=q;i=a;return}else{uf(-1,122096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function hh(b){b=b|0;var d=0,e=0,f=0,g=0,h=0;b=i;i=i+24|0;d=b|0;e=b+16|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=f-1;g=c[14304+(f*24&-1)>>2]|0;if((c[14296+(f*24&-1)>>2]|0)!=3){uf(-1,120384,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=eo(g,e)|0;h=e6(119096)|0;if((h|0)!=0){a[h+8|0]=0;c[h+16>>2]=1;c[h+24>>2]=f}f=c[e>>2]|0;e=uA(f|0)|0;do{if((e|0)>0){h=f+(e-1|0)|0;if((a[h]|0)!=10){break}a[h]=0}}while(0);e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}h=e+1|0;c[6354]=h;e=14296+(h*24&-1)|0;c[e>>2]=3;c[14304+(h*24&-1)>>2]=f;h=e+12|0;e=d|0;c[h>>2]=c[e>>2];c[h+4>>2]=c[e+4>>2];c[h+8>>2]=c[e+8>>2];if((f|0)==0){uu(f);uu(g);i=b;return}e=bP(f|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=e;uu(f);uu(g);i=b;return}function hi(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;b=i;i=i+16|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=f;h=c[f>>2]|0;f=c[g+4>>2]|0;j=c[14304+(d*24&-1)>>2]|0;k=g+12|0;g=b|0;c[g>>2]=c[k>>2];c[g+4>>2]=c[k+4>>2];c[g+8>>2]=c[k+8>>2];if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-2;d=c[14304+(e*24&-1)>>2]|0;if((c[14296+(e*24&-1)>>2]|0)!=3){uf(-1,111072,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a_(d|0,167320,6)|0)==0){uf(-1,114320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a_(d|0,126208,6)|0)==0){uf(-1,114320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=e6(d)|0;uu(d);d=e+8|0;k=e+16|0;do{if((a[d]&1)==0){if((c[k>>2]|0)!=3){break}uu(c[e+24>>2]|0);c[k>>2]=1}}while(0);c[k>>2]=h;k=e;c[k+20>>2]=f;c[e+24>>2]=j;e=k+28|0;c[e>>2]=c[g>>2];c[e+4>>2]=c[g+4>>2];c[e+8>>2]=c[g+8>>2];a[d]=0;d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d+1|0;c[6354]=e;d=14296+(e*24&-1)|0;k=d;c[d>>2]=h;c[k+4>>2]=f;c[14304+(e*24&-1)>>2]=j;e=k+12|0;c[e>>2]=c[g>>2];c[e+4>>2]=c[g+4>>2];c[e+8>>2]=c[g+8>>2];if((h|0)!=3|(j|0)==0){i=b;return}h=bP(j|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=h;i=b;return}function hj(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0,t=0,u=0.0,w=0,x=0,y=0,z=0.0,A=0,B=0,C=0.0;b=i;i=i+24|0;d=b+16|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=c[10810]|0;g=e-1|0;c[6354]=g;j=14296+(e*24&-1)|0;k=j;l=j|0;j=c[l>>2]|0;m=k+4|0;n=c[m>>2]|0;o=14304+(e*24&-1)|0;p=c[o>>2]|0;q=k+12|0;k=b|0;c[k>>2]=c[q>>2];c[k+4>>2]=c[q+4>>2];c[k+8>>2]=c[q+8>>2];if((j|0)!=3){if((g|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e;c[l>>2]=j;c[m>>2]=n;c[o>>2]=p;c[q>>2]=c[k>>2];c[q+4>>2]=c[k+4>>2];c[q+8>>2]=c[k+8>>2];i=b;return}L752:do{if((f|0)==0){r=0.0;s=0;t=548}else{k=f;while(1){if((aY(c[k+4>>2]|0,p|0)|0)==0){break}q=c[k>>2]|0;if((q|0)==0){r=0.0;s=0;t=548;break L752}else{k=q}}q=c[k+16>>2]|0;o=c[k+20>>2]|0;n=k+24|0;m=c[n>>2]|0;h[d>>3]=+h[n>>3];u=+h[k+32>>3];n=m;m=n;if((a[k+8|0]&1)!=0){r=u;s=o;t=548;break}if((q|0)==3){if((n|0)==0){w=0}else{w=bP(m|0)|0}c[d>>2]=w;x=3}else{x=q}uu(p);if((k|0)==0){y=o;z=u;t=550}else{A=x;B=o;C=u}}}while(0);if((t|0)==548){uu(p);y=s;z=r;t=550}if((t|0)==550){h[d>>3]=+uz(133568,0);A=2;B=y;C=z}y=c[6354]|0;if((y|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t=y+1|0;c[6354]=t;y=14296+(t*24&-1)|0;c[y>>2]=A;c[y+4>>2]=B;B=c[d>>2]|0;h[14304+(t*24&-1)>>3]=+h[d>>3];h[14312+(t*24&-1)>>3]=C;t=B;if((A|0)!=3|(t|0)==0){i=b;return}A=bP(t|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=A;i=b;return}function hk(b){b=b|0;var d=0,e=0,f=0,g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0,s=0,t=0.0,u=0.0,v=0.0,w=0.0,x=0,y=0,z=0,A=0.0,B=0.0,C=0,D=0,E=0,F=0.0,G=0.0,H=0.0,I=0.0,J=0,K=0,L=0,M=0.0,N=0.0,O=0,P=0,Q=0,R=0,S=0,T=0.0,U=0,V=0.0,W=0,X=0.0,Y=0.0,$=0.0,aa=0.0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0,ag=0.0,ah=0.0,ai=0.0,aj=0.0;d=b+320|0;e=b+300|0;f=c[e>>2]|0;g=0;i=0;while(1){L779:do{if((i|0)<(f|0)){j=c[d>>2]|0;k=i;while(1){if((c[j+(k<<6)>>2]|0)!=2){l=k;break L779}m=k+1|0;if((m|0)<(f|0)){k=m}else{l=m;break}}}else{l=i}}while(0);k=0;while(1){n=k+l|0;if((n|0)>=(f|0)){break}if((c[(c[d>>2]|0)+(n<<6)>>2]|0)==2){break}else{k=k+1|0}}if((k|0)>0){g=g+1|0;i=n}else{break}}n=b+284|0;if((c[n>>2]|0)==9){if((g|0)>0){o=0;p=0;q=0.0}else{return}while(1){L796:do{if((o|0)<(f|0)){i=c[d>>2]|0;l=o;while(1){if((c[i+(l<<6)>>2]|0)!=2){r=l;break L796}j=l+1|0;if((j|0)<(f|0)){l=j}else{r=j;break}}}else{r=o}}while(0);k=0;while(1){s=k+r|0;if((s|0)>=(f|0)){break}if((c[(c[d>>2]|0)+(s<<6)>>2]|0)==2){break}else{k=k+1|0}}if((k|0)>0){l=c[d>>2]|0;i=r;t=q;while(1){if((c[l+(i<<6)>>2]|0)==2){u=t}else{u=t+ +h[l+(i<<6)+16>>3]}j=i+1|0;if((j|0)<(s|0)){i=j;t=u}else{v=u;break}}}else{v=q}i=p+1|0;if((i|0)<(g|0)){o=(r+1|0)+k|0;p=i;q=v}else{w=v;break}}}else{w=0.0}if((g|0)<=0){return}p=b+304|0;r=b+308|0;b=0;o=1;s=f;while(1){L820:do{if((b|0)<(s|0)){f=c[d>>2]|0;i=b;while(1){if((c[f+(i<<6)>>2]|0)!=2){x=i;break L820}l=i+1|0;if((l|0)<(s|0)){i=l}else{x=l;break}}}else{x=b}}while(0);k=0;while(1){y=k+x|0;if((y|0)>=(s|0)){break}if((c[(c[d>>2]|0)+(y<<6)>>2]|0)==2){break}else{k=k+1|0}}i=c[n>>2]|0;do{if((i|0)==7){if((k|0)>0){z=x;A=0.0}else{break}while(1){f=c[d>>2]|0;if((c[f+(z<<6)>>2]|0)==2){B=A}else{l=f+(z<<6)+16|0;v=A+ +h[l>>3];h[l>>3]=v;B=v}l=z+1|0;if((l|0)<(y|0)){z=l;A=B}else{break}}C=c[n>>2]|0;D=598}else{C=i;D=598}}while(0);do{if((D|0)==598){D=0;if((C|0)==9&(k|0)>0){E=x;F=0.0}else{break}while(1){i=c[d>>2]|0;if((c[i+(E<<6)>>2]|0)==2){G=F}else{l=i+(E<<6)+16|0;v=F+ +h[l>>3];h[l>>3]=v/w;G=v}l=E+1|0;if((l|0)<(y|0)){E=l;F=G}else{break}}}}while(0);l=c[p>>2]|0;i=c[r>>2]|0;f=64788+(l*688&-1)|0;j=64664+(l*688&-1)|0;v=+h[j>>3];if((a[f]&1)==0){m=64672+(l*688&-1)|0;H=+h[m>>3];I=v;J=m}else{q=+_(+v);v=+h[64800+(l*688&-1)>>3];m=64672+(l*688&-1)|0;H=+_(+(+h[m>>3]))/v;I=q/v;J=m}m=64788+(i*688&-1)|0;K=64664+(i*688&-1)|0;v=+h[K>>3];if((a[m]&1)==0){L=64672+(i*688&-1)|0;M=+h[L>>3];N=v;O=L}else{q=+_(+v);v=+h[64800+(i*688&-1)>>3];L=64672+(i*688&-1)|0;M=+_(+(+h[L>>3]))/v;N=q/v;O=L}L=c[d>>2]|0;do{if((k|0)>0){P=64648+(l*688&-1)|0;Q=64648+(i*688&-1)|0;v=M;q=N;u=H;t=I;R=0;while(1){S=R+x|0;T=+h[L+(S<<6)+8>>3];U=L+(S<<6)+16|0;V=+h[U>>3];W=L+(S<<6)|0;c[W>>2]=0;if(T<t){if((c[P>>2]&1|0)==0){X=t;D=614}else{Y=T;D=612}}else{Y=t;D=612}L860:do{if((D|0)==612){D=0;if(T>u){if((c[P>>2]&2|0)==0){X=Y;D=614;break}else{$=T}}else{$=u}h[U>>3]=V;do{if(V<q){if((c[Q>>2]&1|0)!=0){aa=V;break}c[W>>2]=1;ab=q;ac=v;ad=$;ae=Y;break L860}else{aa=q}}while(0);if(V<=v){ab=aa;ac=v;ad=$;ae=Y;break}if((c[Q>>2]&2|0)!=0){ab=aa;ac=V;ad=$;ae=Y;break}c[W>>2]=1;ab=aa;ac=v;ad=$;ae=Y}}while(0);if((D|0)==614){D=0;c[W>>2]=1;h[U>>3]=V;ab=q;ac=v;ad=u;ae=X}h[L+(S<<6)+56>>3]=T;h[L+(S<<6)+48>>3]=T;h[L+(S<<6)+40>>3]=V;h[L+(S<<6)+32>>3]=V;h[L+(S<<6)+24>>3]=-1.0;af=R+1|0;if((af|0)<(k|0)){v=ac;q=ab;u=ad;t=ae;R=af}else{break}}if(ad>H){if((a[f]&1)==0){ag=ad}else{ag=+Z(+(ad*+h[64800+(l*688&-1)>>3]))}h[J>>3]=ag}if(ae<I){if((a[f]&1)==0){ah=ae}else{ah=+Z(+(ae*+h[64800+(l*688&-1)>>3]))}h[j>>3]=ah}if(ac>M){if((a[m]&1)==0){ai=ac}else{ai=+Z(+(ac*+h[64800+(i*688&-1)>>3]))}h[O>>3]=ai}if(ab>=N){break}if((a[m]&1)==0){aj=ab}else{aj=+Z(+(ab*+h[64800+(i*688&-1)>>3]))}h[K>>3]=aj}}while(0);if((o|0)>=(g|0)){break}b=(x+1|0)+k|0;o=o+1|0;s=c[e>>2]|0}return}function hl(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0.0,$=0.0,ab=0,ac=0,ad=0.0,ae=0,af=0,ag=0.0,ah=0,ai=0.0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0.0,aq=0.0,ar=0,as=0.0,at=0.0,au=0.0,av=0.0,aw=0.0,ax=0.0,ay=0,az=0.0,aA=0.0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0.0,aJ=0.0,aK=0.0,aL=0.0,aM=0,aN=0.0,aO=0.0,aP=0.0,aQ=0.0,aR=0.0,aS=0.0,aT=0.0,aU=0.0,aV=0.0,aW=0.0,aX=0.0,aY=0.0,aZ=0.0,a_=0.0,a$=0.0,a0=0.0,a1=0.0,a2=0.0,a3=0.0,a4=0.0,a5=0.0,a6=0.0,a7=0.0,a8=0.0,a9=0.0,ba=0.0,bb=0.0,bc=0.0,bd=0.0,be=0.0,bf=0.0,bg=0.0,bh=0.0,bi=0.0,bj=0.0,bk=0.0,bl=0.0,bm=0.0,bn=0.0,bo=0.0,bp=0.0,bq=0.0,br=0,bs=0,bt=0,bu=0;d=i;e=b+320|0;f=b+300|0;g=c[f>>2]|0;j=0;k=0;while(1){L903:do{if((k|0)<(g|0)){l=c[e>>2]|0;m=k;while(1){if((c[l+(m<<6)>>2]|0)!=2){n=m;break L903}o=m+1|0;if((o|0)<(g|0)){m=o}else{n=o;break}}}else{n=k}}while(0);m=0;while(1){p=m+n|0;if((p|0)>=(g|0)){break}if((c[(c[e>>2]|0)+(p<<6)>>2]|0)==2){break}else{m=m+1|0}}if((m|0)>0){j=j+1|0;k=p}else{break}}p=aa(j<<6,(c[6352]|0)+1|0);k=ut(p)|0;do{if((k|0)==0){gk();g=ut(p)|0;if((g|0)!=0){q=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215872,v)|0)}else{q=k}}while(0);k=q;if((j|0)<=0){r=c[e>>2]|0;s=r;uu(s);c[e>>2]=k;t=c[6352]|0;u=t+1|0;w=aa(u,j);x=b+296|0;c[x>>2]=w;y=w-1|0;c[f>>2]=y;i=d;return}q=b+284|0;p=b+304|0;g=b+308|0;n=0;l=0;L921:while(1){o=c[f>>2]|0;L923:do{if((n|0)<(o|0)){z=c[e>>2]|0;A=n;while(1){if((c[z+(A<<6)>>2]|0)!=2){B=A;break L923}C=A+1|0;if((C|0)<(o|0)){A=C}else{B=C;break}}}else{B=n}}while(0);m=0;while(1){D=m+B|0;if((D|0)>=(o|0)){break}if((c[(c[e>>2]|0)+(D<<6)>>2]|0)==2){break}else{m=m+1|0}}o=c[q>>2]|0;do{if((o|0)==1){c[200]=c[p>>2];c[144]=c[g>>2];A=m<<5;z=ut(A)|0;if((z|0)==0){gk();C=ut(A)|0;if((C|0)==0){E=727;break L921}else{F=C}}else{F=z}z=F;if((m|0)<4){E=729;break L921}C=c[e>>2]|0;A=0;while(1){if((A|0)>=(m|0)){break}if(+h[C+(A+B<<6)+24>>3]>0.0){A=A+1|0}else{E=733;break L921}}A=m-2|0;G=A*40&-1;H=ut(G)|0;if((H|0)==0){gk();I=ut(G)|0;if((I|0)==0){E=736;break L921}else{J=I}}else{J=H}H=J;I=A<<3;K=ut(I)|0;if((K|0)==0){gk();L=ut(I)|0;if((L|0)==0){E=739;break L921}else{M=L}}else{M=K}K=M;L=ut(I)|0;if((L|0)==0){gk();N=ut(I)|0;if((N|0)==0){E=742;break L921}else{O=N}}else{O=L}L=O;N=m-1|0;I=N<<3;P=ut(I)|0;if((P|0)==0){gk();S=ut(I)|0;if((S|0)==0){E=745;break L921}else{T=S}}else{T=P}P=T;S=m<<3;I=ut(S)|0;if((I|0)==0){gk();U=ut(S)|0;if((U|0)==0){E=748;break L921}else{V=U}}else{V=I}I=V;U=ut(S)|0;if((U|0)==0){gk();W=ut(S)|0;if((W|0)==0){E=751;break L921}else{X=W}}else{X=U}U=X;W=c[200]|0;S=64788+(W*688&-1)|0;Y=+h[C+(B<<6)+8>>3];if((a[S]&1)==0){$=Y}else{$=+Z(+(Y*+h[64800+(W*688&-1)>>3]))}h[I>>3]=$;ab=c[144]|0;ac=64788+(ab*688&-1)|0;Y=+h[C+(B<<6)+16>>3];if((a[ac]&1)==0){ad=Y}else{ad=+Z(+(Y*+h[64800+(ab*688&-1)>>3]))}h[U>>3]=ad;if((m|0)>1){ae=64800+(ab*688&-1)|0;ab=64800+(W*688&-1)|0;W=1;do{af=W+B|0;Y=+h[C+(af<<6)+8>>3];if((a[S]&1)==0){ag=Y}else{ag=+Z(+(Y*+h[ab>>3]))}ah=I+(W<<3)|0;h[ah>>3]=ag;Y=+h[C+(af<<6)+16>>3];if((a[ac]&1)==0){ai=Y}else{ai=+Z(+(Y*+h[ae>>3]))}h[U+(W<<3)>>3]=ai;af=W-1|0;h[P+(af<<3)>>3]=+h[ah>>3]- +h[I+(af<<3)>>3];W=W+1|0;}while((W|0)<(m|0))}W=m-3|0;if((W|0)>=0){I=m-4|0;ae=m-5|0;ac=0;while(1){ab=ac+2|0;S=ac+1|0;Y=+h[U+(S<<3)>>3];af=P+(S<<3)|0;ah=P+(ac<<3)|0;h[K+(ac<<3)>>3]=((+h[U+(ab<<3)>>3]-Y)/+h[af>>3]-(Y- +h[U+(ac<<3)>>3])/+h[ah>>3])*3.0;do{if((ac|0)<2){h[H+(ac*40&-1)>>3]=0.0;if((ac|0)<1){h[H+(ac*40&-1)+8>>3]=0.0;aj=C+(ac+B<<6)+24|0;ak=C+(S+B<<6)+24|0;break}else{al=C+(ac+B<<6)+24|0;am=P+(ac-1<<3)|0;E=770;break}}else{an=C+(ac+B<<6)+24|0;ao=P+(ac-1<<3)|0;h[H+(ac*40&-1)>>3]=6.0/+h[an>>3]/+h[ao>>3]/+h[ah>>3];al=an;am=ao;E=770}}while(0);if((E|0)==770){E=0;Y=+h[ah>>3];ap=1.0/Y;ao=C+(S+B<<6)+24|0;h[H+(ac*40&-1)+8>>3]=Y-6.0/+h[al>>3]/Y*(1.0/+h[am>>3]+ap)-6.0/+h[ao>>3]/Y*(ap+1.0/+h[af>>3]);aj=al;ak=ao}ap=+h[ah>>3];Y=+h[af>>3];aq=1.0/ap+1.0/Y;ao=C+(ab+B<<6)+24|0;h[H+(ac*40&-1)+16>>3]=(ap+Y)*2.0+6.0/+h[aj>>3]/ap/ap+aq*6.0/+h[ak>>3]*aq+6.0/+h[ao>>3]/Y/Y;if((ac|0)>(I|0)){h[H+(ac*40&-1)+24>>3]=0.0}else{Y=+h[af>>3];aq=1.0/Y;h[H+(ac*40&-1)+24>>3]=Y-6.0/+h[ak>>3]/Y*(1.0/+h[ah>>3]+aq)-6.0/+h[ao>>3]/Y*(aq+1.0/+h[P+(ab<<3)>>3])}if((ac|0)>(ae|0)){h[H+(ac*40&-1)+32>>3]=0.0}else{h[H+(ac*40&-1)+32>>3]=6.0/+h[ao>>3]/+h[af>>3]/+h[P+(ab<<3)>>3]}if((S|0)>(W|0)){break}else{ac=S}}}ac=G+40|0;ae=ut(ac)|0;if((ae|0)==0){gk();I=ut(ac)|0;if((I|0)==0){E=780;break L921}else{ar=I}}else{ar=ae}ae=ar;aq=+h[J+16>>3];I=ar;h[I>>3]=aq;if(aq==0.0){E=782;break L921}Y=+h[J+24>>3]/aq;h[ar+8>>3]=Y;ap=+h[J+32>>3]/aq;h[ar+16>>3]=ap;as=+h[J+48>>3];h[ar+64>>3]=as;at=+h[J+56>>3]-Y*as;h[ar+40>>3]=at;if(at==0.0){E=784;break L921}au=(+h[J+64>>3]-ap*as)/at;h[ar+48>>3]=au;h[ar+56>>3]=+h[J+72>>3]/at;if((A|0)>2){ac=2;at=Y;Y=ap;ap=au;while(1){ao=H+(ac*40&-1)|0;au=+h[H+(ac*40&-1)+8>>3]-at*+h[ao>>3];h[ae+(ac*40&-1)+24>>3]=au;as=+h[H+(ac*40&-1)+16>>3]-Y*+h[ao>>3]-ap*au;h[ae+(ac*40&-1)>>3]=as;if(as==0.0){E=787;break L921}av=+h[ae+((ac-1|0)*40&-1)+16>>3];aw=(+h[H+(ac*40&-1)+24>>3]-au*av)/as;h[ae+(ac*40&-1)+8>>3]=aw;h[ae+(ac*40&-1)+16>>3]=+h[H+(ac*40&-1)+32>>3]/as;ao=ac+1|0;if((ao|0)<(A|0)){ac=ao;at=ap;Y=av;ap=aw}else{break}}ax=+h[I>>3]}else{ax=aq}h[ar+32>>3]=0.0;ap=+h[K>>3]/ax;h[ar+72>>3]=ap;if((A|0)>1){ac=1;Y=0.0;at=ap;while(1){ap=(+h[K+(ac<<3)>>3]-Y*+h[H+(ac*40&-1)>>3]-at*+h[ae+(ac*40&-1)+24>>3])/+h[ae+(ac*40&-1)>>3];G=ac+1|0;h[ae+(G*40&-1)+32>>3]=ap;if((G|0)<(A|0)){ac=G;Y=at;at=ap}else{break}}}at=+h[ae+(A*40&-1)+32>>3];h[L+(W<<3)>>3]=at;ac=m-4|0;Y=+h[ae+(W*40&-1)+32>>3]-at*+h[ae+(ac*40&-1)+8>>3];h[L+(ac<<3)>>3]=Y;H=m-5|0;L1015:do{if((H|0)>-1){aq=+h[ae+(ac*40&-1)+32>>3]-Y*+h[ae+(H*40&-1)+8>>3]-at*+h[ae+(H*40&-1)+16>>3];h[L+(H<<3)>>3]=aq;if((H|0)<=0){break}K=m-6|0;ap=+h[ae+(H*40&-1)+32>>3]-aq*+h[ae+(K*40&-1)+8>>3]-Y*+h[ae+(K*40&-1)+16>>3];h[L+(K<<3)>>3]=ap;if((K|0)>0){ay=K;az=ap;aA=aq}else{break}while(1){K=ay-1|0;aq=+h[ae+(ay*40&-1)+32>>3]-az*+h[ae+(K*40&-1)+8>>3]-aA*+h[ae+(K*40&-1)+16>>3];h[L+(K<<3)>>3]=aq;if((K|0)<=0){break L1015}ap=+h[L+(ay<<3)>>3];ay=K;az=aq;aA=ap}}}while(0);uu(ar);ae=F+16|0;h[ae>>3]=0.0;H=(A|0)<1;if(!H){ac=1;do{h[z+(ac<<5)+16>>3]=+h[L+(ac-1<<3)>>3];ac=ac+1|0;}while((ac|0)<=(A|0))}h[z+(N<<5)+16>>3]=0.0;Y=+h[ae>>3];at=+h[F+48>>3];ac=F;h[ac>>3]=+h[U>>3]+2.0/+h[C+(B<<6)+24>>3]/+h[P>>3]*(Y-at);if(!H){L=1;ap=Y;aq=at;while(1){at=+h[P+(L-1<<3)>>3];aw=+h[P+(L<<3)>>3];W=L+1|0;av=+h[z+(W<<5)+16>>3];h[z+(L<<5)>>3]=+h[U+(L<<3)>>3]-2.0/+h[C+(L+B<<6)+24>>3]*(av/aw+(ap/at-aq*(1.0/at+1.0/aw)));if((W|0)>(A|0)){break}else{L=W;ap=aq;aq=av}}}h[z+(N<<5)>>3]=+h[U+(N<<3)>>3]-2.0/+h[C+(N+B<<6)+24>>3]/+h[P+(A<<3)>>3]*+h[z+(A<<5)+16>>3];if((A|0)>=0){L=0;aq=+h[ac>>3];ap=Y;while(1){H=L+1|0;av=+h[z+(H<<5)>>3];ae=P+(L<<3)|0;aw=+h[ae>>3];at=+h[z+(H<<5)+16>>3];h[z+(L<<5)+8>>3]=(av-aq)/aw-aw/3.0*(ap*2.0+at);h[z+(L<<5)+24>>3]=(at-ap)/3.0/+h[ae>>3];if((H|0)>(A|0)){break}else{L=H;aq=av;ap=at}}}uu(T);uu(O);uu(M);uu(J);uu(V);uu(X);ho(b,z,B,m,k+(aa((c[6352]|0)+1|0,l)<<6)|0);uu(F)}else if((o|0)==3){c[200]=c[p>>2];c[144]=c[g>>2];if((m|0)<3){E=667;break L921}L=c[e>>2]|0;A=m<<5;P=ut(A)|0;if((P|0)==0){gk();ac=ut(A)|0;if((ac|0)==0){E=670;break L921}else{aB=ac}}else{aB=P}P=aB;ac=m-2|0;A=ac*24&-1;N=ut(A)|0;if((N|0)==0){gk();C=ut(A)|0;if((C|0)==0){E=673;break L921}else{aC=C}}else{aC=N}N=aC;C=ac<<3;A=ut(C)|0;if((A|0)==0){gk();U=ut(C)|0;if((U|0)==0){E=676;break L921}else{aD=U}}else{aD=A}A=aD;U=ut(C)|0;if((U|0)==0){gk();H=ut(C)|0;if((H|0)==0){E=679;break L921}else{aE=H}}else{aE=U}U=aE;H=m-1|0;C=H<<3;ae=ut(C)|0;if((ae|0)==0){gk();W=ut(C)|0;if((W|0)==0){E=682;break L921}else{aF=W}}else{aF=ae}ae=aF;W=m<<3;C=ut(W)|0;if((C|0)==0){gk();S=ut(W)|0;if((S|0)==0){E=685;break L921}else{aG=S}}else{aG=C}C=aG;S=ut(W)|0;if((S|0)==0){gk();ab=ut(W)|0;if((ab|0)==0){E=688;break L921}else{aH=ab}}else{aH=S}S=aH;ab=c[200]|0;W=64788+(ab*688&-1)|0;ap=+h[L+(B<<6)+8>>3];if((a[W]&1)==0){aI=ap}else{aI=+Z(+(ap*+h[64800+(ab*688&-1)>>3]))}h[C>>3]=aI;af=c[144]|0;ah=64788+(af*688&-1)|0;ap=+h[L+(B<<6)+16>>3];if((a[ah]&1)==0){aJ=ap}else{aJ=+Z(+(ap*+h[64800+(af*688&-1)>>3]))}h[S>>3]=aJ;if((m|0)>1){K=64800+(af*688&-1)|0;af=64800+(ab*688&-1)|0;ab=1;do{I=ab+B|0;ap=+h[L+(I<<6)+8>>3];if((a[W]&1)==0){aK=ap}else{aK=+Z(+(ap*+h[af>>3]))}G=C+(ab<<3)|0;h[G>>3]=aK;ap=+h[L+(I<<6)+16>>3];if((a[ah]&1)==0){aL=ap}else{aL=+Z(+(ap*+h[K>>3]))}h[S+(ab<<3)>>3]=aL;I=ab-1|0;h[ae+(I<<3)>>3]=+h[G>>3]- +h[C+(I<<3)>>3];ab=ab+1|0;}while((ab|0)<(m|0))}ab=m-3|0;if((ab|0)>=0){C=m-4|0;K=0;while(1){ah=K+1|0;ap=+h[S+(ah<<3)>>3];L=ae+(ah<<3)|0;af=ae+(K<<3)|0;h[A+(K<<3)>>3]=((+h[S+(K+2<<3)>>3]-ap)/+h[L>>3]-(ap- +h[S+(K<<3)>>3])/+h[af>>3])*3.0;if((K|0)<1){h[N+(K*24&-1)>>3]=0.0}else{h[N+(K*24&-1)>>3]=+h[af>>3]}h[N+(K*24&-1)+8>>3]=(+h[af>>3]+ +h[L>>3])*2.0;if((K|0)>(C|0)){h[N+(K*24&-1)+16>>3]=0.0}else{h[N+(K*24&-1)+16>>3]=+h[L>>3]}if((ah|0)>(ab|0)){break}else{K=ah}}}if((ac|0)>1){K=1;do{C=K-1|0;ap=+h[N+(C*24&-1)+8>>3];if(ap==0.0){E=898;break L921}aq=+h[N+(K*24&-1)>>3]/ap;ah=N+(K*24&-1)+8|0;h[ah>>3]=+h[ah>>3]-aq*+h[N+(C*24&-1)+16>>3];ah=A+(K<<3)|0;h[ah>>3]=+h[ah>>3]-aq*+h[A+(C<<3)>>3];K=K+1|0;}while((K|0)<(ac|0))}aq=+h[N+(ab*24&-1)+8>>3];if(aq==0.0){E=899;break L921}ap=+h[A+(ab<<3)>>3]/aq;h[U+(ab<<3)>>3]=ap;K=m-4|0;if((K|0)>-1){C=K;aq=ap;while(1){ap=+h[N+(C*24&-1)+8>>3];if(ap==0.0){E=900;break L921}Y=(+h[A+(C<<3)>>3]-aq*+h[N+(C*24&-1)+16>>3])/ap;h[U+(C<<3)>>3]=Y;if((C|0)>0){C=C-1|0;aq=Y}else{break}}}C=aB+16|0;h[C>>3]=0.0;if((ac|0)>=1){N=1;do{h[P+(N<<5)+16>>3]=+h[U+(N-1<<3)>>3];N=N+1|0;}while((N|0)<=(ac|0))}h[P+(H<<5)+16>>3]=0.0;if((H|0)>=0){N=0;do{h[P+(N<<5)>>3]=+h[S+(N<<3)>>3];N=N+1|0;}while((N|0)<=(H|0))}if((ac|0)>=0){H=0;aq=+h[aB>>3];Y=+h[C>>3];while(1){N=H+1|0;ap=+h[P+(N<<5)>>3];S=ae+(H<<3)|0;at=+h[S>>3];av=+h[P+(N<<5)+16>>3];h[P+(H<<5)+8>>3]=(ap-aq)/at-at/3.0*(Y*2.0+av);h[P+(H<<5)+24>>3]=(av-Y)/3.0/+h[S>>3];if((N|0)>(ac|0)){break}else{H=N;aq=ap;Y=av}}}uu(aF);uu(aE);uu(aD);uu(aC);uu(aG);uu(aH);ho(b,P,B,m,k+(aa((c[6352]|0)+1|0,l)<<6)|0);uu(aB)}else if((o|0)==2|(o|0)==4){H=m<<3;ac=ut(H)|0;if((ac|0)==0){gk();ae=ut(H)|0;if((ae|0)==0){E=808;break L921}else{aM=ae}}else{aM=ac}ac=aM;ae=m-1|0;H=(ae|0)/2&-1;h[ac>>3]=0.0;if((ae|0)>1){C=0;Y=0.0;while(1){N=C+1|0;aq=Y+ +_(+(+(ae-C|0)/+(N|0)));h[ac+(N<<3)>>3]=aq;if((N|0)<(H|0)){C=N;Y=aq}else{break}}}if((ae|0)>=(H|0)){C=ae;do{h[ac+(C<<3)>>3]=+h[ac+(ae-C<<3)>>3];C=C-1|0;}while((C|0)>=(H|0))}H=c[6352]|0;C=aa(H+1|0,l);P=c[p>>2]|0;c[200]=P;N=c[g>>2]|0;c[144]=N;Y=+h[64664+(P*688&-1)>>3];if((a[64788+(P*688&-1)|0]&1)==0){aN=+h[64672+(P*688&-1)>>3];aO=Y}else{aq=+_(+Y);Y=+h[64800+(P*688&-1)>>3];aN=+_(+(+h[64672+(P*688&-1)>>3]))/Y;aO=aq/Y}Y=+h[64664+(N*688&-1)>>3];if((a[64788+(N*688&-1)|0]&1)==0){aP=+h[64672+(N*688&-1)>>3];aQ=Y}else{aq=+_(+Y);Y=+h[64800+(N*688&-1)>>3];aP=+_(+(+h[64672+(N*688&-1)>>3]))/Y;aQ=aq/Y}do{if((H|0)>0){N=ae+B|0;Y=+(ae>>>0>>>0);aq=aP;av=aQ;ap=aN;at=aO;P=0;S=H;while(1){aw=+(P|0)/+(S-1|0);U=c[e>>2]|0;do{if(aw==0.0){aR=+h[U+(B<<6)+16>>3];aS=+h[U+(B<<6)+8>>3]}else{if(aw==1.0){aR=+h[U+(N<<6)+16>>3];aS=+h[U+(N<<6)+8>>3];break}as=+_(+(1.0-aw));au=Y*as;aT=+_(+aw)-as;as=0.0;aU=0.0;A=0;while(1){aV=+Z(+(aT*+(A>>>0>>>0)+(au+ +h[ac+(A<<3)>>3])));ab=A+B|0;aW=aU+aV*+h[U+(ab<<6)+8>>3];aX=as+aV*+h[U+(ab<<6)+16>>3];ab=A+1|0;if(ab>>>0>ae>>>0){aR=aX;aS=aW;break}else{as=aX;aU=aW;A=ab}}}}while(0);U=P+C|0;A=k+(U<<6)|0;c[A>>2]=0;h[k+(U<<6)+8>>3]=aS;if(aS<at){if((c[64648+((c[200]|0)*688&-1)>>2]&1|0)==0){aY=at;E=831}else{aZ=aS;E=829}}else{aZ=at;E=829}L1147:do{if((E|0)==829){E=0;if(aS>ap){if((c[64648+((c[200]|0)*688&-1)>>2]&2|0)==0){aY=aZ;E=831;break}else{a_=aS}}else{a_=ap}h[k+(U<<6)+16>>3]=aR;do{if(aR<av){if((c[64648+((c[144]|0)*688&-1)>>2]&1|0)!=0){a$=aR;break}c[A>>2]=1;a0=av;a1=aq;a2=a_;a3=aZ;break L1147}else{a$=av}}while(0);if(aR<=aq){a0=a$;a1=aq;a2=a_;a3=aZ;break}if((c[64648+((c[144]|0)*688&-1)>>2]&2|0)!=0){a0=a$;a1=aR;a2=a_;a3=aZ;break}c[A>>2]=1;a0=a$;a1=aq;a2=a_;a3=aZ}}while(0);if((E|0)==831){E=0;c[A>>2]=1;h[k+(U<<6)+16>>3]=aR;a0=av;a1=aq;a2=ap;a3=aY}h[k+(U<<6)+56>>3]=aS;h[k+(U<<6)+48>>3]=aS;h[k+(U<<6)+40>>3]=aR;h[k+(U<<6)+32>>3]=aR;h[k+(U<<6)+24>>3]=-1.0;ab=P+1|0;K=c[6352]|0;if((ab|0)<(K|0)){aq=a1;av=a0;ap=a2;at=a3;P=ab;S=K}else{break}}if(a2>aN){S=c[200]|0;if((a[64788+(S*688&-1)|0]&1)==0){a4=a2}else{a4=+Z(+(a2*+h[64800+(S*688&-1)>>3]))}h[64672+(S*688&-1)>>3]=a4}if(a3<aO){S=c[200]|0;if((a[64788+(S*688&-1)|0]&1)==0){a5=a3}else{a5=+Z(+(a3*+h[64800+(S*688&-1)>>3]))}h[64664+(S*688&-1)>>3]=a5}if(a1>aP){S=c[144]|0;if((a[64788+(S*688&-1)|0]&1)==0){a6=a1}else{a6=+Z(+(a1*+h[64800+(S*688&-1)>>3]))}h[64672+(S*688&-1)>>3]=a6}if(a0>=aQ){break}S=c[144]|0;if((a[64788+(S*688&-1)|0]&1)==0){a7=a0}else{a7=+Z(+(a0*+h[64800+(S*688&-1)>>3]))}h[64664+(S*688&-1)>>3]=a7}}while(0);uu(aM)}else if((o|0)==8){C=c[6352]|0;ae=aa(C+1|0,l);ac=c[p>>2]|0;c[200]=ac;H=c[g>>2]|0;c[144]=H;at=+h[64664+(ac*688&-1)>>3];if((a[64788+(ac*688&-1)|0]&1)==0){a8=+h[64672+(ac*688&-1)>>3];a9=at}else{ap=+_(+at);at=+h[64800+(ac*688&-1)>>3];a8=+_(+(+h[64672+(ac*688&-1)>>3]))/at;a9=ap/at}at=+h[64664+(H*688&-1)>>3];if((a[64788+(H*688&-1)|0]&1)==0){ba=+h[64672+(H*688&-1)>>3];bb=at}else{ap=+_(+at);at=+h[64800+(H*688&-1)>>3];ba=+_(+(+h[64672+(H*688&-1)>>3]))/at;bb=ap/at}if((C|0)<=0){break}H=m-1|0;at=+(m|0);ap=4.0/(at*3.0);av=ba;aq=bb;Y=a8;aw=a9;ac=0;S=C;while(1){C=S-1|0;P=c[e>>2]|0;aU=0.0;as=0.0;au=1.7976931348623157e+308;aT=-1.7976931348623157e+308;N=0;do{aW=+h[P+(N+B<<6)+8>>3];aU=aU+aW;as=as+aW*aW;au=aW<au?aW:au;aT=aW>aT?aW:aT;N=N+1|0;}while(N>>>0<=H>>>0);aW=aU/at;aX=+Q(+(as/at-aW*aW));aW=aX*+R(+ap,.2);aX=au+(aT-au)*(+(ac|0)/+(C|0));aV=0.0;N=0;do{K=N+B|0;bc=+h[P+(K<<6)+24>>3];bd=bc>0.0?bc:aW;bc=(aX- +h[P+(K<<6)+8>>3])/bd;be=+h[P+(K<<6)+16>>3];aV=aV+be*+Z(+(bc*bc*-.5))/bd;N=N+1|0;}while(N>>>0<=H>>>0);aW=aV/2.5066282746310002;N=ac+ae|0;P=k+(N<<6)|0;c[P>>2]=0;h[k+(N<<6)+8>>3]=aX;if(aX<aw){if((c[64648+((c[200]|0)*688&-1)>>2]&1|0)==0){bf=aw;E=872}else{bg=aX;E=870}}else{bg=aw;E=870}L1205:do{if((E|0)==870){E=0;if(aX>Y){if((c[64648+((c[200]|0)*688&-1)>>2]&2|0)==0){bf=bg;E=872;break}else{bh=aX}}else{bh=Y}h[k+(N<<6)+16>>3]=aW;do{if(aW<aq){if((c[64648+((c[144]|0)*688&-1)>>2]&1|0)!=0){bi=aW;break}c[P>>2]=1;bj=aq;bk=av;bl=bh;bm=bg;break L1205}else{bi=aq}}while(0);if(aW<=av){bj=bi;bk=av;bl=bh;bm=bg;break}if((c[64648+((c[144]|0)*688&-1)>>2]&2|0)!=0){bj=bi;bk=aW;bl=bh;bm=bg;break}c[P>>2]=1;bj=bi;bk=av;bl=bh;bm=bg}}while(0);if((E|0)==872){E=0;c[P>>2]=1;h[k+(N<<6)+16>>3]=aW;bj=aq;bk=av;bl=Y;bm=bf}h[k+(N<<6)+56>>3]=aX;h[k+(N<<6)+48>>3]=aX;h[k+(N<<6)+40>>3]=aW;h[k+(N<<6)+32>>3]=aW;h[k+(N<<6)+24>>3]=-1.0;U=ac+1|0;A=c[6352]|0;if((U|0)<(A|0)){av=bk;aq=bj;Y=bl;aw=bm;ac=U;S=A}else{break}}if(bl>a8){S=c[200]|0;if((a[64788+(S*688&-1)|0]&1)==0){bn=bl}else{bn=+Z(+(bl*+h[64800+(S*688&-1)>>3]))}h[64672+(S*688&-1)>>3]=bn}if(bm<a9){S=c[200]|0;if((a[64788+(S*688&-1)|0]&1)==0){bo=bm}else{bo=+Z(+(bm*+h[64800+(S*688&-1)>>3]))}h[64664+(S*688&-1)>>3]=bo}if(bk>ba){S=c[144]|0;if((a[64788+(S*688&-1)|0]&1)==0){bp=bk}else{bp=+Z(+(bk*+h[64800+(S*688&-1)>>3]))}h[64672+(S*688&-1)>>3]=bp}if(bj>=bb){break}S=c[144]|0;if((a[64788+(S*688&-1)|0]&1)==0){bq=bj}else{bq=+Z(+(bj*+h[64800+(S*688&-1)>>3]))}h[64664+(S*688&-1)>>3]=bq}}while(0);m=l+1|0;c[k+(aa((c[6352]|0)+1|0,m)-1<<6)>>2]=2;if((m|0)<(j|0)){n=D;l=m}else{E=905;break}}if((E|0)==727){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=163872,v)|0)}else if((E|0)==729){uf(c[b+4>>2]|0,136680,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==733){uf(c[b+4>>2]|0,123304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==736){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=106104,v)|0)}else if((E|0)==673){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=106104,v)|0)}else if((E|0)==676){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=98688,v)|0)}else if((E|0)==739){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=98688,v)|0)}else if((E|0)==742){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=93064,v)|0)}else if((E|0)==745){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=85160,v)|0)}else if((E|0)==748){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=77016,v)|0)}else if((E|0)==751){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=224768,v)|0)}else if((E|0)==688){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=224768,v)|0)}else if((E|0)==679){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=93064,v)|0)}else if((E|0)==682){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=85160,v)|0)}else if((E|0)==685){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=77016,v)|0)}else if((E|0)==667){uf(c[b+4>>2]|0,202432,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==670){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=163872,v)|0)}else if((E|0)==780){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=211832,v)|0)}else if((E|0)==782){uu(ar);uu(T);uu(O);uu(M);uu(J);uu(V);uu(X);br=b+4|0;bs=c[br>>2]|0;uf(bs,217416,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==784){uu(ar);uu(T);uu(O);uu(M);uu(J);uu(V);uu(X);br=b+4|0;bs=c[br>>2]|0;uf(bs,217416,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==787){uu(ar);uu(T);uu(O);uu(M);uu(J);uu(V);uu(X);br=b+4|0;bs=c[br>>2]|0;uf(bs,217416,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==808){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=217128,v)|0)}else if((E|0)==898){uu(aF);uu(aE);uu(aD);uu(aC);uu(aG);uu(aH);bt=b+4|0;bu=c[bt>>2]|0;uf(bu,196920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==899){uu(aF);uu(aE);uu(aD);uu(aC);uu(aG);uu(aH);bt=b+4|0;bu=c[bt>>2]|0;uf(bu,196920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==900){uu(aF);uu(aE);uu(aD);uu(aC);uu(aG);uu(aH);bt=b+4|0;bu=c[bt>>2]|0;uf(bu,196920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((E|0)==905){r=c[e>>2]|0;s=r;uu(s);c[e>>2]=k;t=c[6352]|0;u=t+1|0;w=aa(u,j);x=b+296|0;c[x>>2]=w;y=w-1|0;c[f>>2]=y;i=d;return}}function hm(a,b){a=a|0;b=b|0;var c=0.0,d=0.0,e=0;c=+h[a+8>>3];d=+h[b+8>>3];if(c>d){e=1;return e|0}e=(c<d)<<31>>31;return e|0}function hn(b){b=b|0;var d=0,e=0,f=0,g=0,i=0.0,j=0.0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0,I=0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,P=0.0,Q=0,R=0,S=0,T=0.0,U=0,V=0.0,W=0.0,X=0.0,Y=0.0,_=0,$=0,aa=0.0,ab=0.0,ac=0.0,ad=0,ae=0.0,af=0.0,ag=0.0,ah=0.0;c[200]=c[b+304>>2];c[144]=c[b+308>>2];d=b+320|0;e=b+300|0;f=b+284|0;g=0;i=0.0;j=0.0;k=0.0;l=0.0;m=0.0;n=0.0;o=0.0;p=0;q=0;while(1){r=c[e>>2]|0;L1281:do{if((q|0)<(r|0)){s=c[d>>2]|0;t=q;while(1){if((c[s+(t<<6)>>2]|0)!=2){u=t;break L1281}v=t+1|0;if((v|0)<(r|0)){t=v}else{u=v;break}}}else{u=q}}while(0);t=0;while(1){w=t+u|0;if((w|0)>=(r|0)){break}if((c[(c[d>>2]|0)+(w<<6)>>2]|0)==2){break}else{t=t+1|0}}if((t|0)>0){x=u;y=p;z=0;A=o;B=n;C=m;D=l;E=k;F=j;G=i;H=g}else{break}while(1){r=c[d>>2]|0;s=c[r+(x<<6)>>2]|0;do{if((s|0)==2){I=H;J=G;K=F;L=E;M=D;N=C;O=B;P=A;Q=z;R=y;S=x}else{T=+h[r+(x<<6)+8>>3];if((z|0)==0){I=(s|0)==0;J=+h[r+(x<<6)+24>>3];K=+h[r+(x<<6)+32>>3];L=+h[r+(x<<6)+40>>3];M=+h[r+(x<<6)+48>>3];N=+h[r+(x<<6)+56>>3];O=+h[r+(x<<6)+16>>3];P=T;Q=1;R=y;S=x;break}if(T==A){I=(s|0)==0&H;J=G+ +h[r+(x<<6)+24>>3];K=F+ +h[r+(x<<6)+32>>3];L=E+ +h[r+(x<<6)+40>>3];M=D+ +h[r+(x<<6)+48>>3];N=C+ +h[r+(x<<6)+56>>3];O=B+ +h[r+(x<<6)+16>>3];P=A;Q=z+1|0;R=y;S=x;break}h[r+(y<<6)+8>>3]=A;v=c[f>>2]|0;if((v|0)==6|(v|0)==7|(v|0)==9){U=1}else{U=z}T=+(U|0);V=B/T;h[(c[d>>2]|0)+(y<<6)+16>>3]=V;h[(c[d>>2]|0)+(y<<6)+56>>3]=C/T;h[(c[d>>2]|0)+(y<<6)+48>>3]=D/T;h[(c[d>>2]|0)+(y<<6)+40>>3]=E/T;h[(c[d>>2]|0)+(y<<6)+32>>3]=F/T;h[(c[d>>2]|0)+(y<<6)+24>>3]=G/T;c[(c[d>>2]|0)+(y<<6)>>2]=0;L1304:do{if(H){W=V;X=A}else{v=c[200]|0;do{if((a[64788+(v*688&-1)|0]&1)==0){Y=A}else{if(A>-8.988465674311579e+307){Y=+Z(+(A*+h[64800+(v*688&-1)>>3]));break}else{c[(c[d>>2]|0)+(y<<6)>>2]=1;W=V;X=A;break L1304}}}while(0);if(Y<+h[64664+(v*688&-1)>>3]){if((c[64648+(v*688&-1)>>2]&1|0)!=0){_=934}}else{_=934}do{if((_|0)==934){_=0;if(Y>+h[64672+(v*688&-1)>>3]){if((c[64648+(v*688&-1)>>2]&2|0)==0){break}}$=c[144]|0;do{if((a[64788+($*688&-1)|0]&1)==0){aa=V}else{if(V>-8.988465674311579e+307){aa=+Z(+(V*+h[64800+($*688&-1)>>3]));break}else{c[(c[d>>2]|0)+(y<<6)>>2]=1;W=V;X=Y;break L1304}}}while(0);if(aa<+h[64664+($*688&-1)>>3]){if((c[64648+($*688&-1)>>2]&1|0)!=0){_=943}}else{_=943}if((_|0)==943){_=0;if(aa<=+h[64672+($*688&-1)>>3]){W=aa;X=Y;break L1304}if((c[64648+($*688&-1)>>2]&2|0)!=0){W=aa;X=Y;break L1304}}c[(c[d>>2]|0)+(y<<6)>>2]=1;W=aa;X=Y;break L1304}}while(0);c[(c[d>>2]|0)+(y<<6)>>2]=1;W=V;X=Y}}while(0);I=H;J=G;K=F;L=E;M=D;N=C;O=W;P=X;Q=0;R=y+1|0;S=x-1|0}}while(0);r=S+1|0;if((r|0)<(w|0)){x=r;y=R;z=Q;A=P;B=O;C=N;D=M;E=L;F=K;G=J;H=I}else{break}}if((Q|0)==0){ab=O;ac=P;ad=R}else{h[(c[d>>2]|0)+(R<<6)+8>>3]=P;V=((c[f>>2]|0)-6|0)>>>0<2?1.0:+(Q|0);T=O/V;h[(c[d>>2]|0)+(R<<6)+16>>3]=T;h[(c[d>>2]|0)+(R<<6)+56>>3]=N/V;h[(c[d>>2]|0)+(R<<6)+48>>3]=M/V;h[(c[d>>2]|0)+(R<<6)+40>>3]=L/V;h[(c[d>>2]|0)+(R<<6)+32>>3]=K/V;h[(c[d>>2]|0)+(R<<6)+24>>3]=J/V;c[(c[d>>2]|0)+(R<<6)>>2]=0;L1337:do{if(I){ae=T;af=P}else{t=c[200]|0;do{if((a[64788+(t*688&-1)|0]&1)==0){ag=P}else{if(P>-8.988465674311579e+307){ag=+Z(+(P*+h[64800+(t*688&-1)>>3]));break}else{c[(c[d>>2]|0)+(R<<6)>>2]=1;ae=T;af=P;break L1337}}}while(0);if(ag<+h[64664+(t*688&-1)>>3]){if((c[64648+(t*688&-1)>>2]&1|0)!=0){_=956}}else{_=956}do{if((_|0)==956){_=0;if(ag>+h[64672+(t*688&-1)>>3]){if((c[64648+(t*688&-1)>>2]&2|0)==0){break}}r=c[144]|0;do{if((a[64788+(r*688&-1)|0]&1)==0){ah=T}else{if(T>-8.988465674311579e+307){ah=+Z(+(T*+h[64800+(r*688&-1)>>3]));break}else{c[(c[d>>2]|0)+(R<<6)>>2]=1;ae=T;af=ag;break L1337}}}while(0);if(ah<+h[64664+(r*688&-1)>>3]){if((c[64648+(r*688&-1)>>2]&1|0)!=0){_=965}}else{_=965}if((_|0)==965){_=0;if(ah<=+h[64672+(r*688&-1)>>3]){ae=ah;af=ag;break L1337}if((c[64648+(r*688&-1)>>2]&2|0)!=0){ae=ah;af=ag;break L1337}}c[(c[d>>2]|0)+(R<<6)>>2]=1;ae=ah;af=ag;break L1337}}while(0);c[(c[d>>2]|0)+(R<<6)>>2]=1;ae=T;af=ag}}while(0);ab=ae;ac=af;ad=R+1|0}if((ad|0)>=(c[e>>2]|0)){g=I;i=J;j=K;k=L;l=M;m=N;n=ab;o=ac;p=ad;q=w;continue}c[(c[d>>2]|0)+(ad<<6)>>2]=2;g=I;i=J;j=K;k=L;l=M;m=N;n=ab;o=ac;p=ad+1|0;q=w}c[e>>2]=p;iR(b,p);return}function ho(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0,A=0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,P=0,Q=0.0,R=0.0,S=0.0,T=0.0;j=i;k=c[b+304>>2]|0;c[200]=k;l=c[b+308>>2]|0;c[144]=l;m=+h[64664+(k*688&-1)>>3];if((a[64788+(k*688&-1)|0]&1)==0){n=+h[64672+(k*688&-1)>>3];o=m}else{p=+_(+m);m=+h[64800+(k*688&-1)>>3];n=+_(+(+h[64672+(k*688&-1)>>3]))/m;o=p/m}m=+h[64664+(l*688&-1)>>3];if((a[64788+(l*688&-1)|0]&1)==0){q=+h[64672+(l*688&-1)>>3];r=m}else{p=+_(+m);m=+h[64800+(l*688&-1)>>3];q=+_(+(+h[64672+(l*688&-1)>>3]))/m;r=p/m}l=c[b+320>>2]|0;m=+h[l+(e<<6)+8>>3];p=m>o?m:o;m=+h[l+((e-1|0)+f<<6)+8>>3];s=m<n?m:n;if(p>=s){uf(c[b+4>>2]|0,205704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}b=c[6352]|0;m=(s-p)/+(b-1|0);if((b|0)<=0){i=j;return}b=f-2|0;s=r-(q-r);t=n;u=r;w=q;x=o;f=0;k=0;while(1){y=p+m*+(k|0);z=f;while(1){A=z+1|0;if(y>=+h[l+(A+e<<6)+8>>3]&(z|0)<(b|0)){z=A}else{break}}A=c[200]|0;if((a[64788+(A*688&-1)|0]&1)==0){B=+h[l+(z+e<<6)+8>>3];C=y}else{D=+Z(+(y*+h[64800+(A*688&-1)>>3]));B=+Z(+(+h[l+(z+e<<6)+8>>3]*+h[64800+(A*688&-1)>>3]));C=D}D=C-B;E=+h[d+(z<<5)>>3]+D*(+h[d+(z<<5)+8>>3]+D*(+h[d+(z<<5)+24>>3]*D+ +h[d+(z<<5)+16>>3]));A=c[144]|0;do{if((a[64788+(A*688&-1)|0]&1)==0){F=E}else{if(E<=0.0){F=s;break}D=+_(+E);F=D/+h[64800+(A*688&-1)>>3]}}while(0);A=g+(k<<6)|0;c[A>>2]=0;h[g+(k<<6)+8>>3]=y;do{if(y<x){if((c[64648+((c[200]|0)*688&-1)>>2]&1|0)!=0){G=y;H=993;break}I=x;H=996}else{G=x;H=993}}while(0);L1400:do{if((H|0)==993){H=0;do{if(y>t){if((c[64648+((c[200]|0)*688&-1)>>2]&2|0)!=0){J=y;break}I=G;H=996;break L1400}else{J=t}}while(0);h[g+(k<<6)+16>>3]=F;do{if(F<u){if((c[64648+((c[144]|0)*688&-1)>>2]&1|0)!=0){K=F;break}c[A>>2]=1;L=w;M=u;N=J;O=G;break L1400}else{K=u}}while(0);if(F<=w){L=w;M=K;N=J;O=G;break}if((c[64648+((c[144]|0)*688&-1)>>2]&2|0)!=0){L=F;M=K;N=J;O=G;break}c[A>>2]=1;L=w;M=K;N=J;O=G}}while(0);if((H|0)==996){H=0;c[A>>2]=1;h[g+(k<<6)+16>>3]=F;L=w;M=u;N=t;O=I}h[g+(k<<6)+56>>3]=y;h[g+(k<<6)+48>>3]=y;h[g+(k<<6)+40>>3]=F;h[g+(k<<6)+32>>3]=F;h[g+(k<<6)+24>>3]=-1.0;P=k+1|0;if((P|0)<(c[6352]|0)){t=N;u=M;w=L;x=O;f=z;k=P}else{break}}if(N>n){k=c[200]|0;if((a[64788+(k*688&-1)|0]&1)==0){Q=N}else{Q=+Z(+(N*+h[64800+(k*688&-1)>>3]))}h[64672+(k*688&-1)>>3]=Q}if(O<o){k=c[200]|0;if((a[64788+(k*688&-1)|0]&1)==0){R=O}else{R=+Z(+(O*+h[64800+(k*688&-1)>>3]))}h[64664+(k*688&-1)>>3]=R}if(L>q){k=c[144]|0;if((a[64788+(k*688&-1)|0]&1)==0){S=L}else{S=+Z(+(L*+h[64800+(k*688&-1)>>3]))}h[64672+(k*688&-1)>>3]=S}if(M>=r){i=j;return}k=c[144]|0;if((a[64788+(k*688&-1)|0]&1)==0){T=M}else{T=+Z(+(M*+h[64800+(k*688&-1)>>3]))}h[64664+(k*688&-1)>>3]=T;i=j;return}function hp(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;d=i;if((a|0)<1|(b|0)<1){e=0;i=d;return e|0}f=a<<2;g=ut(f)|0;do{if((g|0)==0){gk();h=ut(f)|0;if((h|0)!=0){j=h;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=217072,v)|0);return 0}else{j=g}}while(0);g=j;j=aa(a<<3,b);f=ut(j)|0;do{if((f|0)==0){gk();h=ut(j)|0;if((h|0)!=0){k=h;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=163832,v)|0);return 0}else{k=f}}while(0);f=k;c[g>>2]=f;if((a|0)>1){l=1;m=f}else{e=g;i=d;return e|0}while(1){f=m+(b<<3)|0;c[g+(l<<2)>>2]=f;k=l+1|0;if((k|0)<(a|0)){l=k;m=f}else{e=g;break}}i=d;return e|0}function hq(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,i=0,j=0,k=0,l=0.0,m=0,n=0.0,o=0,p=0,q=0.0,r=0,s=0,t=0.0,u=0,v=0,w=0,x=0.0;f=(b|0)>0;if(f){g=-1;i=0;j=a}else{return}while(1){k=e+(c[d+(i<<2)>>2]<<3)|0;l=+h[k>>3];m=e+(i<<3)|0;h[k>>3]=+h[m>>3];do{if((g|0)>-1){k=i-1|0;if((g|0)>(k|0)){n=l;o=g;break}p=g;q=l;r=e+(g<<3)|0;s=(c[j>>2]|0)+(g<<3)|0;while(1){t=q- +h[s>>3]*+h[r>>3];u=p+1|0;if((u|0)>(k|0)){n=t;o=g;break}else{p=u;q=t;r=r+8|0;s=s+8|0}}}else{if(l==0.0){n=l;o=g;break}n=l;o=i}}while(0);h[m>>3]=n;s=i+1|0;if((s|0)<(b|0)){g=o;i=s;j=j+4|0}else{break}}if(f){v=a+(b<<2)|0;w=b}else{return}while(1){a=v-4|0;f=w-1|0;j=e+(f<<3)|0;L1476:do{if((w|0)<(b|0)){i=w;o=e+(w<<3)|0;g=(c[a>>2]|0)+(w<<3)|0;n=+h[j>>3];while(1){l=n- +h[g>>3]*+h[o>>3];h[j>>3]=l;d=i+1|0;if((d|0)>=(b|0)){x=l;break L1476}i=d;o=o+8|0;g=g+8|0;n=l}}else{x=+h[j>>3]}}while(0);h[j>>3]=x/+h[(c[a>>2]|0)+(f<<3)>>3];if((f|0)>0){v=a;w=f}else{break}}return}function hr(a,b,d,e,f,g,j){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0.0,w=0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0,J=0,K=0.0,L=0.0,M=0.0,N=0.0;k=i;l=(g|0)>0;L1485:do{if(l){m=(b|0)==0;n=0;L1487:while(1){o=n+1|0;if((o|0)<(f|0)){p=a+(n<<2)|0;q=(o|0)<(g|0);r=b+(n<<3)|0;s=o;do{t=a+(s<<2)|0;u=+h[(c[t>>2]|0)+(n<<3)>>3];do{if(u!=0.0){w=(c[p>>2]|0)+(n<<3)|0;x=+h[w>>3];y=+P(+x);do{if(y<+P(+u)*2.220446049250313e-16){z=1.0;A=1.0;B=0.0;C=-0.0-u}else{if(x>0.0){D=1.0}else{D=+((x<0.0)<<31>>31|0)}E=D*+Q(+(x*x+u*u));if(E==0.0){break L1487}F=x/E;G=(-0.0-u)/E;if(+P(+G)<F){z=G;A=G;B=F;C=E;break}if(G>0.0){H=1.0}else{H=+((G<0.0)<<31>>31|0)}z=H/F;A=G;B=F;C=E}}while(0);h[w>>3]=C;h[(c[t>>2]|0)+(n<<3)>>3]=z;if(q){I=o;do{y=+h[(c[p>>2]|0)+(I<<3)>>3];J=(c[t>>2]|0)+(I<<3)|0;E=+h[J>>3];h[J>>3]=A*y+B*E;h[(c[p>>2]|0)+(I<<3)>>3]=B*y-A*E;I=I+1|0;}while((I|0)<(g|0))}if(m){break}E=+h[r>>3];I=b+(s<<3)|0;y=+h[I>>3];h[I>>3]=A*E+B*y;h[r>>3]=B*E-A*y}}while(0);s=s+1|0;}while((s|0)<(f|0))}if((o|0)<(g|0)){n=o}else{break L1485}}be(42289,136640,(v=i,i=i+16|0,h[v>>3]=x,h[v+8>>3]=u,v)|0);fb()}}while(0);if((b|0)==0){i=k;return}n=g-1|0;L1519:do{if(l){m=n;while(1){u=+h[b+(m<<3)>>3];h[e+(m<<3)>>3]=0.0;s=m+1|0;r=c[a+(m<<2)>>2]|0;if((s|0)<(g|0)){x=u;p=s;while(1){A=x+ +h[r+(p<<3)>>3]*+h[d+(p<<3)>>3];s=p+1|0;if((s|0)<(g|0)){x=A;p=s}else{K=A;break}}}else{K=u}x=+h[r+(m<<3)>>3];if(x==0.0){break}h[d+(m<<3)>>3]=(-0.0-K)/x;if((m|0)>0){m=m-1|0}else{break L1519}}uD(42289,123272,28);fb()}}while(0);if((g|0)<(f|0)){d=g;do{h[e+(d<<3)>>3]=+h[b+(d<<3)>>3];d=d+1|0;}while((d|0)<(f|0))}if((j|0)==0|l^1){i=k;return}l=(f|0)>0;j=n;while(1){if(l){n=e+(j<<3)|0;d=f;do{d=d-1|0;K=+h[(c[a+(d<<2)>>2]|0)+(j<<3)>>3];do{if(K==1.0){L=1.0;M=0.0}else{x=+P(+K);if(x<1.0){L=K;M=+Q(+(1.0-K*K));break}A=1.0/x;if(K>0.0){N=1.0}else{N=+((K<0.0)<<31>>31|0)}L=N*+Q(+(1.0-A*A));M=A}}while(0);K=+h[n>>3];r=e+(d<<3)|0;u=+h[r>>3];h[r>>3]=K*(-0.0-L)+M*u;h[n>>3]=M*K+L*u;}while((d|0)>0)}if((j|0)>0){j=j-1|0}else{break}}i=k;return}function hs(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0;e=0;while(1){if((e|0)>=(d|0)){break}f=b+(e<<2)|0;g=c[f>>2]|0;if((e|0)>0){i=0;j=g;while(1){h[j+(i<<3)>>3]=0.0;k=i+1|0;l=c[f>>2]|0;if((k|0)<(e|0)){i=k;j=l}else{m=l;break}}}else{m=g}h[m+(e<<3)>>3]=1.0;if(+h[(c[a+(e<<2)>>2]|0)+(e<<3)>>3]!=0.0){e=e+1|0}else{n=1108;break}}if((n|0)==1108){uD(42289,106064,30);fb()}n=(d|0)>0;if(n){o=0}else{return}do{e=o;do{m=(c[b+(e<<2)>>2]|0)+(o<<3)|0;p=+h[m>>3];if((o|0)<(e|0)){j=o;q=p;while(1){r=q- +h[(c[a+(j<<2)>>2]|0)+(e<<3)>>3]*+h[(c[b+(j<<2)>>2]|0)+(o<<3)>>3];i=j+1|0;if((i|0)<(e|0)){j=i;q=r}else{s=r;break}}}else{s=p}h[m>>3]=s/+h[(c[a+(e<<2)>>2]|0)+(e<<3)>>3];e=e+1|0;}while((e|0)<(d|0));o=o+1|0;}while((o|0)<(d|0));if(!n){return}n=d-1|0;o=0;do{if((n|0)>=(o|0)){e=d;g=n;while(1){j=(c[b+(g<<2)>>2]|0)+(o<<3)|0;s=+h[j>>3];i=c[a+(g<<2)>>2]|0;if((e|0)<(d|0)){f=e;q=s;while(1){r=q- +h[i+(f<<3)>>3]*+h[(c[b+(f<<2)>>2]|0)+(o<<3)>>3];l=f+1|0;if((l|0)<(d|0)){f=l;q=r}else{t=r;break}}}else{t=s}h[j>>3]=t/+h[i+(g<<3)>>3];f=g-1|0;if((f|0)<(o|0)){break}else{e=g;g=f}}}o=o+1|0;}while((o|0)<(d|0));return}function ht(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0,t=0.0,u=0,w=0,x=0,y=0,z=0.0,A=0,B=0,C=0,D=0.0,E=0,F=0.0,G=0,H=0,I=0;f=i;if((b|0)<1){h[e>>3]=1.0;g=0;j=g;uu(j);i=f;return}k=b<<3;l=ut(k)|0;do{if((l|0)==0){gk();m=ut(k)|0;if((m|0)!=0){n=m;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{n=l}}while(0);l=n;h[e>>3]=1.0;n=a+(b<<2)|0;k=a;m=l;while(1){o=c[k>>2]|0;p=o+(b<<3)|0;q=o;r=0.0;L1601:while(1){o=q;while(1){if(o>>>0>=p>>>0){break L1601}s=o+8|0;t=+P(+(+h[o>>3]));if(t>r){q=s;r=t;continue L1601}else{o=s}}}if(r==0.0){u=1137;break}h[m>>3]=1.0/r;q=k+4|0;if(q>>>0<n>>>0){k=q;m=m+8|0}else{break}}if((u|0)==1137){uf(-1,98640,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}u=b-1|0;m=a;k=0;n=-1;while(1){q=(k|0)>0;if(q){p=0;do{o=a+(p<<2)|0;s=c[o>>2]|0;w=s+(k<<3)|0;L1616:do{if((p|0)>0){x=0;t=+h[w>>3];y=s;while(1){z=t- +h[y+(x<<3)>>3]*+h[(c[a+(x<<2)>>2]|0)+(k<<3)>>3];h[w>>3]=z;A=x+1|0;if((A|0)>=(p|0)){break L1616}x=A;t=z;y=c[o>>2]|0}}}while(0);p=p+1|0;}while((p|0)<(k|0))}p=l+(k<<3)|0;r=0.0;o=p;w=n;s=k;while(1){y=a+(s<<2)|0;x=c[y>>2]|0;A=x+(k<<3)|0;t=+h[A>>3];L1625:do{if(q){B=0;z=t;C=x;while(1){D=z- +h[C+(B<<3)>>3]*+h[(c[a+(B<<2)>>2]|0)+(k<<3)>>3];h[A>>3]=D;E=B+1|0;if((E|0)>=(k|0)){F=D;break L1625}B=E;z=D;C=c[y>>2]|0}}else{F=t}}while(0);t=+h[o>>3];z=t*+P(+F);y=z<r;G=y?w:s;A=s+1|0;if((A|0)<(b|0)){r=y?r:z;o=o+8|0;w=G;s=A}else{break}}if((k|0)!=(G|0)){s=0;w=c[a+(G<<2)>>2]|0;o=c[m>>2]|0;while(1){r=+h[w>>3];h[w>>3]=+h[o>>3];h[o>>3]=r;q=s+1|0;if((q|0)<(b|0)){s=q;w=w+8|0;o=o+8|0}else{break}}h[e>>3]=-0.0- +h[e>>3];h[l+(G<<3)>>3]=+h[p>>3]}c[d+(k<<2)>>2]=G;o=(c[m>>2]|0)+(k<<3)|0;if(+h[o>>3]==0.0){h[o>>3]=1.0e-30}do{if((k|0)==(u|0)){H=k+1|0}else{r=1.0/+h[(c[m>>2]|0)+(k<<3)>>3];o=k+1|0;if((o|0)<(b|0)){I=o}else{H=o;break}while(1){w=(c[a+(I<<2)>>2]|0)+(k<<3)|0;h[w>>3]=r*+h[w>>3];w=I+1|0;if((w|0)<(b|0)){I=w}else{H=o;break}}}}while(0);if((H|0)<(b|0)){m=m+4|0;k=H;n=G}else{g=l;break}}j=g;uu(j);i=f;return}function hu(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0;b=i;d=ut(16)|0;do{if((d|0)==0){gk();e=ut(16)|0;if((e|0)!=0){f=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=213800,v)|0);return 0}else{f=d}}while(0);d=f;c[f+4>>2]=(a|0)<0?0:a;c[f+8>>2]=0;if((a|0)<=0){c[f+12>>2]=0;g=f;c[g>>2]=0;i=b;return d|0}e=a<<6;a=ut(e)|0;do{if((a|0)==0){gk();h=ut(e)|0;if((h|0)!=0){j=h;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=217008,v)|0);return 0}else{j=a}}while(0);c[f+12>>2]=j;uE(j|0,0,e|0);g=f;c[g>>2]=0;i=b;return d|0}function hv(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;e=a+4|0;if((c[e>>2]|0)==(b|0)){i=d;return}f=a+12|0;a=c[f>>2]|0;g=(a|0)==0;if((b|0)<=0){if(!g){uu(a)}c[f>>2]=0;c[e>>2]=0;i=d;return}do{if(g){h=b<<6;j=ut(h)|0;if((j|0)!=0){k=j;break}gk();j=ut(h)|0;if((j|0)!=0){k=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=217008,v)|0)}else{k=db(a,b<<6,214200)|0}}while(0);a=k;c[f>>2]=a;f=c[e>>2]|0;if((f|0)<(b|0)){uE(a+(f<<6)|0,0,b-f<<6|0)}c[e>>2]=b;i=d;return}function hw(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0;f=i;hx(b,d,0);a[46896]=e&1;if((b|0)==0){uj(-1,76976,(v=i,i=i+16|0,c[v>>2]=e?224760:217408,c[v+8>>2]=d,v)|0)}if((c[o>>2]|0)==(b|0)){a[37400]=1;do{}while((dI()|0)==0);g=hy()|0;i=f;return}c[13896]=0;if(e){c[13896]=0;d=c[13898]|0;h=c[8272]|0;L1694:do{if((d|0)<(h|0)){j=d;k=h;while(1){l=c[1054]|0;m=(a[l+(j*40&-1)|0]&1)==0;L1697:do{if(!m){n=c[l+(j*40&-1)+36>>2]|0;p=l+(j*40&-1)+32|0;q=c[10036]|0;r=0;while(1){if((r|0)>=(n|0)){break}if((a[q+((c[p>>2]|0)+r|0)|0]|0)==(a[r+103664|0]|0)){r=r+1|0}else{break L1697}}if((r|0)==1){s=k;t=j;break L1694}}}while(0);p=c[13896]|0;if((p|0)>=10){s=k;t=j;break L1694}do{if(m){c[13896]=p+1;u=55544+(p<<2)|0;w=l+(j*40&-1)+32|0;x=1212}else{q=l+(j*40&-1)+32|0;n=a[(c[10036]|0)+(c[q>>2]|0)|0]|0;if(n<<24>>24==39){c[13896]=p+1;y=55544+(p<<2)|0}else{c[13896]=p+1;z=55544+(p<<2)|0;if(n<<24>>24==34){y=z}else{u=z;w=q;x=1212;break}}ub(y,j,j)}}while(0);if((x|0)==1212){x=0;p=c[l+(j*40&-1)+36>>2]|0;m=p+(c[w>>2]|0)|0;q=db(c[u>>2]|0,p+1|0,116456)|0;c[u>>2]=q;p=c[(c[1054]|0)+(j*40&-1)+32>>2]|0;L1714:do{if((p|0)<(m|0)){z=p;n=q;while(1){A=a[(c[10036]|0)+z|0]|0;if(A<<24>>24==0){B=n;break L1714}C=n+1|0;a[n]=A;A=z+1|0;if((A|0)<(m|0)){z=A;n=C}else{B=C;break}}}else{B=q}}while(0);a[B]=0}q=(c[13898]|0)+1|0;c[13898]=q;m=c[8272]|0;if((q|0)<(m|0)){j=q;k=m}else{s=m;t=q;break}}}else{s=h;t=d}}while(0);do{if((t|0)<(s|0)){d=c[1054]|0;if((a[d+(t*40&-1)|0]&1)==0){D=t+1|0;c[13898]=D;uf(D,221400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}h=c[d+(t*40&-1)+36>>2]|0;B=d+(t*40&-1)+32|0;d=c[10036]|0;u=0;while(1){if((u|0)>=(h|0)){break}if((a[d+((c[B>>2]|0)+u|0)|0]|0)==(a[u+103664|0]|0)){u=u+1|0}else{x=1274;break}}if((x|0)==1274){D=t+1|0;c[13898]=D;uf(D,221400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((u|0)==1){break}D=t+1|0;c[13898]=D;uf(D,221400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[(c[8804]|0)+44>>2]=t}c[9354]=0;a[37400]=0;L1735:while(1){t=0;D=c[10034]|0;s=c[10036]|0;L1737:while(1){if((a6(s+t|0,D|0,b|0)|0)==0){x=1228;break}c[9354]=(c[9354]|0)+1;B=c[10036]|0;d=uA(B|0)|0;h=d-1|0;w=B+h|0;B=a[w]|0;do{if(B<<24>>24==10){a[w]=0;y=(h|0)>0?d-2|0:h;k=(c[10036]|0)+y|0;j=a[k]|0;if(j<<24>>24!=13){E=y;F=j;x=1234;break}a[k]=0;k=(((y|0)>0)<<31>>31)+y|0;E=k;F=a[(c[10036]|0)+k|0]|0;x=1234}else{if((d+1|0)<(D|0)){E=h;F=B;x=1234;break}dG();G=((1-d|0)-1|0)+(c[10034]|0)|0;H=d}}while(0);do{if((x|0)==1234){x=0;if(F<<24>>24==92){G=(c[10034]|0)-E|0;H=E;break}if((a[43472]&1)!=0){dK()}d=jl(40144,40136)|0;c[8272]=d;B=(c[10036]|0)+(c[(c[1054]|0)+(d*40&-1)+32>>2]|0)|0;if((a[B]|0)==35){a[B]=0}B=c[12918]|0;if((B|0)<0){x=1242;break L1735}if((B|0)<=0){x=1244;break L1737}if((E+4|0)>>>0>(c[10034]|0)>>>0){dG()}B=c[10036]|0;d=B+(uA(B|0)|0)|0;a[d]=a[125976]|0;a[d+1|0]=a[125977|0]|0;a[d+2|0]=a[125978|0]|0;d=uA(c[10036]|0)|0;G=(c[10034]|0)-d|0;H=d}}while(0);d=c[10036]|0;if(0){I=0;J=d;break}else{t=H;D=G;s=d}}if((x|0)==1244){x=0;I=0;J=c[10036]|0}else if((x|0)==1228){x=0;a[(c[10036]|0)+t|0]=0;I=1;J=c[10036]|0}if((a[J]|0)==0){K=I}else{if(e){s=bP(J|0)|0;a[c[10036]|0]=0;D=0;u=s;while(1){d=a[u]|0;if((d<<24>>24|0)==36){B=u+1|0;L=a[B]|0;do{if((L|0)==35){h=c[13896]|0;if(h>>>0>=10){x=1255;break L1735}be(43480,98608,(v=i,i=i+8|0,c[v>>2]=h,v)|0);M=43480}else if((L|0)==36){M=180784}else{h=L-48|0;if(h>>>0>=10){x=1260;break L1735}if((h|0)>=(c[13896]|0)){M=179864;break}M=c[55544+(h<<2)>>2]|0}}while(0);h=uA(M|0)|0;w=h+1|0;if(((c[10034]|0)-D|0)>>>0<w>>>0){do{dG();}while(((c[10034]|0)-D|0)>>>0<w>>>0)}w=(c[10036]|0)+D|0;uB(w|0,M|0);N=h+D|0;O=B}else if((d<<24>>24|0)==0){break}else{w=D+1|0;if(w>>>0>(c[10034]|0)>>>0){dG();P=a[u]|0}else{P=d}a[(c[10036]|0)+D|0]=P;N=w;O=u}D=N;u=O+1|0}if((D+1|0)>>>0>(c[10034]|0)>>>0){dG()}a[(c[10036]|0)+D|0]=0;uu(s)}a[25280]=0;K=(dJ()|0)==0?I:1}if((K|0)!=0){x=1276;break}}if((x|0)==1255){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=105968,v)|0)}else if((x|0)==1260){uf(-1,85080,(v=i,i=i+8|0,c[v>>2]=L,v)|0)}else if((x|0)==1242){uf(-1,211816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((x|0)==1276){g=hy()|0;i=f;return}}function hx(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;f=i;g=ut(96)|0;do{if((g|0)==0){gk();h=ut(96)|0;if((h|0)!=0){j=h;break}if((b|0)==0){k=c[13898]|0;uf(k,196888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}az(b|0);k=c[13898]|0;uf(k,196888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=g}}while(0);c[j>>2]=b;c[j+4>>2]=d;c[j+8>>2]=e;a[j+12|0]=a[37400]&1;c[j+16>>2]=c[9354];a[j+13|0]=a[46896]&1;c[j+52>>2]=c[13896];c[j+56>>2]=c[13886];c[13886]=0;c[j+60>>2]=c[13887];c[13887]=0;c[j+64>>2]=c[13888];c[13888]=0;c[j+68>>2]=c[13889];c[13889]=0;c[j+72>>2]=c[13890];c[13890]=0;c[j+76>>2]=c[13891];c[13891]=0;c[j+80>>2]=c[13892];c[13892]=0;c[j+84>>2]=c[13893];c[13893]=0;c[j+88>>2]=c[13894];c[13894]=0;c[j+92>>2]=c[13895];c[13895]=0;e=j;d=c[8804]|0;do{if((d|0)==0){c[j+20>>2]=0}else{b=(c[d+20>>2]|0)+1|0;c[j+20>>2]=b;if((b|0)<=250){break}uf(-1,185608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[j+24>>2]=c[9368];a[j+28|0]=a[37464]&1;a[j+29|0]=a[37480]&1;c[j+44>>2]=c[13898];c[j+40>>2]=c[8272];d=((c[8272]|0)*40&-1)+40|0;b=ut(d)|0;do{if((b|0)==0){gk();g=ut(d)|0;if((g|0)!=0){l=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=180384,v)|0)}else{l=b}}while(0);c[j+36>>2]=l;uD(l|0,c[1054]|0,((c[8272]|0)*40&-1)+40|0);l=c[10036]|0;if((l|0)==0){m=0;n=j+32|0;o=n;c[o>>2]=m;p=c[8804]|0;q=j+48|0;r=q;c[r>>2]=p;c[8804]=e;i=f;return}m=bP(l|0)|0;n=j+32|0;o=n;c[o>>2]=m;p=c[8804]|0;q=j+48|0;r=q;c[r>>2]=p;c[8804]=e;i=f;return}function hy(){var b=0,d=0,e=0,f=0,g=0,h=0;b=i;d=c[8804]|0;if((d|0)==0){e=0;i=b;return e|0}f=c[d>>2]|0;L1827:do{if(!((f|0)==0|(f|0)==(c[o>>2]|0))){g=c[d+4>>2]|0;do{if((g|0)!=0){if((a[g]|0)!=60){break}a2(f|0);break L1827}}while(0);az(f|0)}}while(0);f=c[13886]|0;if((f|0)!=0){uu(f)}c[13886]=c[d+56>>2];f=c[13887]|0;if((f|0)!=0){uu(f)}c[13887]=c[d+60>>2];f=c[13888]|0;if((f|0)!=0){uu(f)}c[13888]=c[d+64>>2];f=c[13889]|0;if((f|0)!=0){uu(f)}c[13889]=c[d+68>>2];f=c[13890]|0;if((f|0)!=0){uu(f)}c[13890]=c[d+72>>2];f=c[13891]|0;if((f|0)!=0){uu(f)}c[13891]=c[d+76>>2];f=c[13892]|0;if((f|0)!=0){uu(f)}c[13892]=c[d+80>>2];f=c[13893]|0;if((f|0)!=0){uu(f)}c[13893]=c[d+84>>2];f=c[13894]|0;if((f|0)!=0){uu(f)}c[13894]=c[d+88>>2];f=c[13895]|0;if((f|0)!=0){uu(f)}c[13895]=c[d+92>>2];c[13896]=c[d+52>>2];a[46896]=a[d+13|0]&1;a[37400]=a[d+12|0]&1;c[9354]=c[d+16>>2];c[9368]=c[d+24>>2];a[37480]=a[d+29|0]&1;a[37464]=a[d+28|0]&1;f=d+36|0;do{if((c[f>>2]|0)!=0){g=d+40|0;c[8272]=c[g>>2];c[13898]=c[d+44>>2];h=(c[g>>2]|0)+1|0;if((c[1052]|0)<(h|0)){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=202392,v)|0);return 0}else{uD(c[1054]|0,c[f>>2]|0,h*40&-1);uu(c[f>>2]|0);break}}}while(0);f=d+32|0;h=c[f>>2]|0;if((h|0)!=0){g=c[10036]|0;uB(g|0,h|0);uu(c[f>>2]|0)}uu(c[d+4>>2]|0);uu(c[d+8>>2]|0);c[8804]=c[d+48>>2];uu(d);e=1;i=b;return e|0}function hz(a){a=a|0;var b=0,d=0,e=0;b=i;if((c[3524]|0)==0){if((a|0)==0){i=b;return}d=c[m>>2]|0;aI(154728,34,1,d|0);i=b;return}uu(c[6958]|0);uu(c[6956]|0);d=c[c[3524]>>2]|0;if((d|0)==0){e=0}else{e=bP(d|0)|0}c[6958]=e;e=bP(13048)|0;c[6956]=e;if((a|0)==0){i=b;return}cf(c[m>>2]|0,164104,(v=i,i=i+16|0,c[v>>2]=c[6958],c[v+8>>2]=e,v)|0);i=b;return}function hA(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;e=i;L1890:do{if((a[b]|0)==60){if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}f=bt(b+1|0,193632)|0;if((f|0)==0){g=0}else{h=f;break}i=e;return g|0}else{f=bF(b|0,d|0)|0;if((f|0)==0){j=0}else{h=f;break}while(1){f=c[8776]|0;if((f|0)==0){break}k=c[8774]|0;do{if((k|0)==0){c[8774]=f;l=c[8778]|0;if((l|0)!=0){m=f;n=l;break}l=c[8780]|0;c[8778]=l;m=f;n=l}else{l=k+((uA(k|0)|0)+1|0)|0;c[8774]=l;m=l;n=c[8778]|0}}while(0);if(m>>>0>=n>>>0){o=1356;break}if((m|0)==0){break}k=uA(m|0)|0;p=db(j,(k+2|0)+(uA(b|0)|0)|0,172208)|0;uB(p|0,m|0);k=uA(p|0)|0;f=(k|0)==0?0:k-1|0;k=a[p+f|0]|0;if(!((k<<24>>24|0)==47|(k<<24>>24|0)==0)){a[p+(f+1|0)|0]=47;a[p+(f+2|0)|0]=0}uC(p|0,b|0);q=bF(p|0,d|0)|0;if((q|0)==0){j=p}else{o=1361;break}}if((o|0)==1356){c[8774]=0;c[8778]=0}else if((o|0)==1361){uu(p);f=c[8776]|0;if((f|0)==0){h=q;break}while(1){k=c[8774]|0;do{if((k|0)==0){c[8774]=f;l=c[8778]|0;if((l|0)!=0){r=f;s=l;break}l=c[8780]|0;c[8778]=l;r=f;s=l}else{l=k+((uA(k|0)|0)+1|0)|0;c[8774]=l;r=l;s=c[8778]|0}}while(0);if(r>>>0>=s>>>0){break}if((r|0)==0|(f|0)==0){h=q;break L1890}}c[8774]=0;c[8778]=0;h=q;break}if((j|0)==0){h=0;break}uu(j);h=0}}while(0);g=h;i=e;return g|0}function hB(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;if((a[b]|0)==60){uj(-1,168312,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}L1930:do{if((bF(b|0,193632)|0)==0){e=ur(8,0)|0;if((e|0)==0){f=0;break}else{g=e}while(1){h=bP(g|0)|0;e=h+((uA(h|0)|0)-1|0)|0;if((a[e]|0)==33){a[e]=0;j=1}else{j=0}k=hC(h,b,j)|0;if((k|0)!=0){break}uu(h);e=ur(8,0)|0;if((e|0)==0){f=0;break L1930}else{g=e}}do{}while((ur(8,0)|0)!=0);uu(h);f=k}else{if((b|0)==0){f=0;break}f=bP(b|0)|0}}while(0);i=d;return f|0}function hC(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;f=i;i=i+72|0;g=f|0;h=uA(b|0)|0;j=(h+2|0)+(uA(d|0)|0)|0;h=ut(j)|0;do{if((h|0)==0){gk();k=ut(j)|0;if((k|0)!=0){l=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=223784,v)|0);return 0}else{l=h}}while(0);uB(l|0,b|0);h=uA(l|0)|0;j=(h|0)==0?0:h-1|0;h=a[l+j|0]|0;if(!((h<<24>>24|0)==47|(h<<24>>24|0)==0)){a[l+(j+1|0)|0]=47;a[l+(j+2|0)|0]=0}uC(l|0,d|0);j=bF(l|0,193632)|0;if((j|0)!=0){az(j|0);m=l;i=f;return m|0}uu(l);if(!e){m=0;i=f;return m|0}e=aW(b|0)|0;if((e|0)==0){m=0;i=f;return m|0}l=a3(e|0)|0;L1962:do{if((l|0)==0){n=0}else{j=g+8|0;h=l;while(1){k=uA(b|0)|0;o=h+4|0;p=(k+2|0)+(uA(o|0)|0)|0;k=ut(p)|0;if((k|0)==0){gk();q=ut(p)|0;if((q|0)==0){break}else{r=q}}else{r=k}uB(r|0,b|0);k=uA(r|0)|0;q=(k|0)==0?0:k-1|0;k=a[r+q|0]|0;if(!((k<<24>>24|0)==47|(k<<24>>24|0)==0)){a[r+(q+1|0)|0]=47;a[r+(q+2|0)|0]=0}uC(r|0,o|0);cu(r|0,g|0);do{if((c[j>>2]&61440|0)==16384){if((aY(o|0,202200)|0)==0){break}if((aY(o|0,221856)|0)==0){break}q=hC(r,d,1)|0;if((q|0)!=0){n=q;break L1962}}}while(0);uu(r);o=a3(e|0)|0;if((o|0)==0){n=0;break L1962}else{h=o}}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=223216,v)|0);return 0}}while(0);bR(e|0);m=n;i=f;return m|0}function hD(){var b=0,d=0,e=0,f=0,g=0,h=0;b=i;d=c[6958]|0;if((d|0)==0){e=c[m>>2]|0;aI(145512,32,1,e|0);i=b;return}e=(uA(d|0)|0)+11|0;d=c[6956]|0;if((d|0)==0){f=e}else{g=d;while(1){d=a[g]|0;if((d<<24>>24|0)==92|(d<<24>>24|0)==10){a[g]=32}else if((d<<24>>24|0)==0){break}g=g+1|0}f=(uA(c[6956]|0)|0)+e|0}e=ut(f)|0;do{if((e|0)==0){gk();g=ut(f)|0;if((g|0)!=0){h=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=216216,v)|0)}else{h=e}}while(0);e=a[37400]&1;a[37400]=0;f=c[6956]|0;be(h|0,149608,(v=i,i=i+16|0,c[v>>2]=c[6958],c[v+8>>2]=(f|0)!=0?f:179864,v)|0);dL(h);a[37400]=e;if(e<<24>>24==0){i=b;return}e=(a[13048]|0)!=0?13048:179864;cf(c[m>>2]|0,147624,(v=i,i=i+16|0,c[v>>2]=c[c[3524]>>2],c[v+8>>2]=e,v)|0);i=b;return}function hE(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0.0,N=0.0;d=i;i=i+48|0;e=d|0;f=d+24|0;g=c[13898]|0;j=c[1054]|0;k=c[j+(g*40&-1)+36>>2]|0;l=c[j+(g*40&-1)+32>>2]|0;m=(a[j+(g*40&-1)|0]&1)==0;g=(k|0)>0;j=c[10036]|0;n=43320;o=108448;L2002:while(1){L2004:do{if(!m){if(g){p=0;q=0;r=l;while(1){s=a[o+p|0]|0;if(s<<24>>24==(a[j+(p+r|0)|0]|0)){t=r;u=q}else{if(s<<24>>24!=36){break L2004}t=r-1|0;u=1}w=p+1|0;if((w|0)<(u+k|0)){p=w;q=u;r=t}else{break}}if((u|0)==0){x=w}else{y=n;break L2002}}else{x=0}r=a[o+x|0]|0;if((r<<24>>24|0)==36|(r<<24>>24|0)==0){y=n;break L2002}}}while(0);r=n+8|0;q=c[r>>2]|0;if((q|0)==0){y=r;break}else{n=r;o=q}}o=c[y+4>>2]|0;do{if((o|0)==11){c[b+24>>2]=1;y=(c[13898]|0)+1|0;c[13898]=y;n=c[1054]|0;x=c[n+(y*40&-1)+36>>2]|0;w=c[n+(y*40&-1)+32>>2]|0;u=(a[n+(y*40&-1)|0]&1)==0;y=(x|0)>0;n=c[10036]|0;t=43320;k=108448;L2019:while(1){L2021:do{if(!u){if(y){j=0;l=0;g=w;while(1){m=a[k+j|0]|0;if(m<<24>>24==(a[n+(j+g|0)|0]|0)){z=g;A=l}else{if(m<<24>>24!=36){break L2021}z=g-1|0;A=1}B=j+1|0;if((B|0)<(A+x|0)){j=B;l=A;g=z}else{break}}if((A|0)==0){C=B}else{D=t;break L2019}}else{C=0}g=a[k+C|0]|0;if((g<<24>>24|0)==36|(g<<24>>24|0)==0){D=t;break L2019}}}while(0);g=t+8|0;l=c[g>>2]|0;if((l|0)==0){D=g;break}else{t=g;k=l}}E=c[D+4>>2]|0}else{k=b+24|0;if((o|0)!=12){c[k>>2]=0;E=o;break}c[k>>2]=-1;k=(c[13898]|0)+1|0;c[13898]=k;t=c[1054]|0;x=c[t+(k*40&-1)+36>>2]|0;n=c[t+(k*40&-1)+32>>2]|0;w=(a[t+(k*40&-1)|0]&1)==0;k=(x|0)>0;t=c[10036]|0;y=43320;u=108448;L2038:while(1){L2040:do{if(!w){if(k){l=0;g=0;j=n;while(1){m=a[u+l|0]|0;if(m<<24>>24==(a[t+(l+j|0)|0]|0)){F=j;G=g}else{if(m<<24>>24!=36){break L2040}F=j-1|0;G=1}H=l+1|0;if((H|0)<(G+x|0)){l=H;g=G;j=F}else{break}}if((G|0)==0){I=H}else{J=y;break L2038}}else{I=0}j=a[u+I|0]|0;if((j<<24>>24|0)==36|(j<<24>>24|0)==0){J=y;break L2038}}}while(0);j=y+8|0;g=c[j>>2]|0;if((g|0)==0){J=j;break}else{y=j;u=g}}E=c[J+4>>2]|0}}while(0);J=b|0;if((E|0)==-1){c[J>>2]=0;i=d;return}c[J>>2]=1;c[13898]=(c[13898]|0)+1;J=b+4|0;c[J>>2]=E;I=b+8|0;h[I>>3]=0.0;H=c[13898]|0;if((c[8272]|0)<=(H|0)){i=d;return}G=c[1054]|0;if((a[G+(H*40&-1)|0]&1)==0){i=d;return}F=c[G+(H*40&-1)+36>>2]|0;o=G+(H*40&-1)+32|0;G=c[10036]|0;D=0;while(1){if((D|0)>=(F|0)){break}if((a[G+((c[o>>2]|0)+D|0)|0]|0)==(a[D+115e3|0]|0)){D=D+1|0}else{K=1504;break}}if((K|0)==1504){i=d;return}if((D|0)!=1){i=d;return}if((E|0)<9){c[J>>2]=E+4;L=c[13898]|0}else{L=H}c[13898]=L+1;L=is(e)|0;H=c[L>>2]|0;if((H|0)==1){M=+(c[L+8>>2]|0)}else if((H|0)==2){M=+h[L+8>>3]}else if((H|0)==3){M=+uz(c[L+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L=e|0;if((c[L>>2]|0)==3){uu(c[e+8>>2]|0);c[L>>2]=1}h[I>>3]=M;if((E|0)!=9){i=d;return}E=c[13898]|0;if((c[8272]|0)<=(E|0)){uf(E,138448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}I=c[1054]|0;if((a[I+(E*40&-1)|0]&1)==0){uf(E,138448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L=c[I+(E*40&-1)+36>>2]|0;e=I+(E*40&-1)+32|0;I=c[10036]|0;H=0;while(1){if((H|0)>=(L|0)){break}if((a[I+((c[e>>2]|0)+H|0)|0]|0)==(a[H+148464|0]|0)){H=H+1|0}else{K=1507;break}}if((K|0)==1507){uf(E,138448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((H|0)!=1){uf(E,138448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=E+1;E=is(f)|0;H=c[E>>2]|0;if((H|0)==1){N=+(c[E+8>>2]|0)}else if((H|0)==2){N=+h[E+8>>3]}else if((H|0)==3){N=+uz(c[E+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}E=f|0;if((c[E>>2]|0)==3){uu(c[f+8>>2]|0);c[E>>2]=1}h[b+16>>3]=N;i=d;return}function hF(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0.0;d=i;if((c[a>>2]|0)==0){i=d;return}e=c[a+24>>2]|0;if((e|0)!=0){f=(e|0)>0?136888:134896;aI(f|0,6,1,b|0)}f=c[a+4>>2]|0;if((f|0)==0){aI(133384,6,1,b|0);i=d;return}if((f|0)<5){e=c[43320+(f<<3)>>2]|0;aK(e|0,b|0);i=d;return}if((f|0)<9){e=c[43320+(f-4<<3)>>2]|0;g=+h[a+8>>3];cf(b|0,131984,(v=i,i=i+16|0,c[v>>2]=e,h[v+8>>3]=g,v)|0);i=d;return}if((f|0)!=9){i=d;return}g=+h[a+16>>3];cf(b|0,130704,(v=i,i=i+16|0,h[v>>3]=+h[a+8>>3],h[v+8>>3]=g,v)|0);i=d;return}function hG(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;d=i;i=i+56|0;e=d|0;f=e+40|0;g=f;h=b+16|0;c[g>>2]=c[h>>2];c[g+4>>2]=c[h+4>>2];c[g+8>>2]=c[h+8>>2];c[g+12>>2]=c[h+12>>2];h=f|0;g=c[h>>2]|0;if((g|0)==1){b=e+44|0;j=c[b>>2]|0;if((j|0)==-3){k=0;i=d;return k|0}l=c[8798]|0;m=(l|0)>0;n=j;L2142:while(1){o=n+1|0;j=43264;while(1){p=c[j>>2]|0;if((p|0)==0){break}if((c[p+4>>2]|0)==(o|0)){q=1535;break L2142}else{j=p|0}}if(!((o|0)>(l|0)&m)){q=1541;break}n=(n|0)%(l|0)&-1}do{if((q|0)==1535){l=e;m=p+8|0;c[l>>2]=c[m>>2];c[l+4>>2]=c[m+4>>2];c[l+8>>2]=c[m+8>>2];c[l+12>>2]=c[m+12>>2];c[l+16>>2]=c[m+16>>2];c[l+20>>2]=c[m+20>>2];c[l+24>>2]=c[m+24>>2];c[l+28>>2]=c[m+28>>2];c[l+32>>2]=c[m+32>>2];c[l+36>>2]=c[m+36>>2];c[l+40>>2]=c[m+40>>2];c[l+44>>2]=c[m+44>>2];c[l+48>>2]=c[m+48>>2];c[l+52>>2]=c[m+52>>2];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[e+4>>2]=o;a[e+32|0]=0;break}if((a[e+32|0]&1)!=0){break}c[e+40>>2]=1;c[b>>2]=c[e+4>>2]}else if((q|0)==1541){c[e+4>>2]=n;c[e+40>>2]=1;c[b>>2]=n;c[e+8>>2]=n}}while(0);r=c[h>>2]|0}else{r=g}if((r|0)==0){k=1;i=d;return k|0}fn(f,c[3524]|0);k=1;i=d;return k|0}
+function hH(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0.0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0.0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0.0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0.0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0.0,bp=0.0,bq=0.0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0.0,bz=0.0,bA=0,bB=0,bC=0,bD=0.0,bE=0.0,bF=0;f=i;i=i+272|0;g=f|0;j=f+24|0;k=f+48|0;l=f+72|0;m=f+96|0;n=f+120|0;o=f+144|0;p=f+168|0;q=f+192|0;r=f+216|0;s=r;t=b;c[s>>2]=c[t>>2];c[s+4>>2]=c[t+4>>2];c[s+8>>2]=c[t+8>>2];c[s+12>>2]=c[t+12>>2];c[s+16>>2]=c[t+16>>2];c[s+20>>2]=c[t+20>>2];c[s+24>>2]=c[t+24>>2];c[s+28>>2]=c[t+28>>2];c[s+32>>2]=c[t+32>>2];c[s+36>>2]=c[t+36>>2];c[s+40>>2]=c[t+40>>2];c[s+44>>2]=c[t+44>>2];c[s+48>>2]=c[t+48>>2];c[s+52>>2]=c[t+52>>2];L2163:do{if(d){s=c[13898]|0;u=c[1054]|0;w=c[u+(s*40&-1)+36>>2]|0;x=c[u+(s*40&-1)+32>>2]|0;y=(a[u+(s*40&-1)|0]&1)==0;L2165:do{if((w|0)>0&(y^1)){u=c[10036]|0;z=0;A=0;B=x;while(1){if((a[z+73824|0]|0)==(a[u+(z+B|0)|0]|0)){C=B;D=A}else{if((z|0)!=5){E=1558;break L2165}C=B-1|0;D=1}F=z+1|0;if((F|0)<(D+w|0)){z=F;A=D;B=C}else{break}}if((D|0)!=0){break}if(!((z|0)==4|(z|0)==9)){E=1558}}else{E=1558}}while(0);if((E|0)==1558){if((c[8272]|0)<=(s|0)|y){break}B=c[10036]|0;A=0;while(1){if((A|0)>=(w|0)){break}if((a[B+(x+A|0)|0]|0)==(a[A+72816|0]|0)){A=A+1|0}else{break L2163}}if((A|0)!=2){break}}c[13898]=s+1;x=is(p)|0;B=c[x>>2]|0;if((B|0)==3){G=+uz(c[x+8>>2]|0,0)}else if((B|0)==1){G=+(c[x+8>>2]|0)}else if((B|0)==2){G=+h[x+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}x=p|0;if((c[x>>2]|0)==3){uu(c[p+8>>2]|0);c[x>>2]=1}lK(b,~~G)}}while(0);p=c[13898]|0;D=c[8272]|0;L2193:do{if((p|0)<(D|0)){C=n|0;x=n+8|0;B=r+12|0;w=q|0;y=q+8|0;u=m|0;F=m+8|0;H=r+24|0;I=g|0;J=g+8|0;K=r+8|0;L=j|0;M=j+8|0;N=r+16|0;O=r+32|0;P=r+40|0;Q=r+44|0;R=l|0;S=l+8|0;T=r+48|0;U=k|0;V=k+8|0;W=r+4|0;X=r+40|0;Y=o|0;Z=o+8|0;_=b+4|0;$=d^1;aa=b|0;ab=b+40|0;ac=b+44|0;ad=b+8|0;ae=b+32|0;G=0.0;af=0;ag=0;ah=0;ai=0;aj=0;ak=0;al=0;am=p;an=D;L2195:while(1){ao=c[1054]|0;ap=(a[ao+(am*40&-1)|0]&1)==0;aq=c[ao+(am*40&-1)+36>>2]|0;ar=ao+(am*40&-1)+32|0;L2197:do{if(ap){as=c[ar>>2]|0;E=1587}else{at=c[10036]|0;au=0;while(1){if((au|0)>=(aq|0)){E=1578;break}if((a[at+((c[ar>>2]|0)+au|0)|0]|0)==(a[au+103664|0]|0)){au=au+1|0}else{break}}if((E|0)==1578){E=0;if((au|0)==1){av=al;aw=aj;ax=ai;ay=ah;az=ag;aA=af;aB=ak;aC=am;break L2193}}at=c[ar>>2]|0;if(!((aq|0)>0&(ap^1))){as=at;E=1587;break}aD=c[10036]|0;aE=0;aF=0;aG=at;while(1){if((a[aE+139232|0]|0)==(a[aD+(aE+aG|0)|0]|0)){aH=aG;aI=aF}else{if((aE|0)!=5){as=at;E=1587;break L2197}aH=aG-1|0;aI=1}aJ=aE+1|0;if((aJ|0)<(aI+aq|0)){aE=aJ;aF=aI;aG=aH}else{break}}if((aI|0)!=0){E=1592;break}if((aE|0)==4|(aE|0)==8){E=1592}else{as=at;E=1587}}}while(0);L2215:do{if((E|0)==1587){E=0;if((an|0)<=(am|0)|ap){aK=al;aL=ag;aM=af;break}ar=c[10036]|0;z=0;while(1){if((z|0)>=(aq|0)){break}if((a[ar+(as+z|0)|0]|0)==(a[z+225056|0]|0)){z=z+1|0}else{aK=al;aL=ag;aM=af;break L2215}}if((z|0)==2){E=1592}else{aK=al;aL=ag;aM=af}}}while(0);L2222:do{if((E|0)==1592){E=0;aq=af+1|0;if((af|0)!=0){av=al;aw=aj;ax=ai;ay=ah;az=ag;aA=aq;aB=ak;aC=am;break L2193}ap=am+1|0;c[13898]=ap;ar=c[ao+(ap*40&-1)+36>>2]|0;at=c[ao+(ap*40&-1)+32>>2]|0;aE=(a[ao+(ap*40&-1)|0]&1)==0;L2225:do{if((ar|0)>0&(aE^1)){aG=c[10036]|0;aF=0;aD=0;au=at;while(1){if((a[aF+123456|0]|0)==(a[aG+(aF+au|0)|0]|0)){aN=au;aO=aD}else{if((aF|0)!=3){break}aN=au-1|0;aO=1}aJ=aF+1|0;if((aJ|0)<(aO+ar|0)){aF=aJ;aD=aO;au=aN}else{E=1599;break}}do{if((E|0)==1599){E=0;if((aO|0)==0){if(!((aF|0)==2|(aF|0)==8)){break}}au=ag+1|0;if((ag|0)!=0){av=al;aw=aj;ax=ai;ay=ah;az=au;aA=aq;aB=ak;aC=ap;break L2193}c[13898]=am;hI(X,3);a[O]=1;aK=al;aL=au;aM=aq;break L2222}}while(0);if(!((ar|0)>0&(aE^1))){break}aF=c[10036]|0;au=0;aD=0;aG=at;while(1){if((a[au+205488|0]|0)==(a[aF+(au+aG|0)|0]|0)){aP=aG;aQ=aD}else{if((au|0)!=3){break L2225}aP=aG-1|0;aQ=1}aJ=au+1|0;if((aJ|0)<(aQ+ar|0)){au=aJ;aD=aQ;aG=aP}else{break}}if((aQ|0)==0){if(!((au|0)==2|(au|0)==7)){break}}aG=ag+1|0;if((ag|0)!=0){av=al;aw=aj;ax=ai;ay=ah;az=aG;aA=aq;aB=ak;aC=ap;break L2193}c[13898]=am;hI(X,6);a[O]=1;aK=al;aL=aG;aM=aq;break L2222}}while(0);L2253:do{if(!((an|0)<=(ap|0)|aE)){z=c[10036]|0;aG=0;while(1){if((aG|0)>=(ar|0)){break}if((a[z+(at+aG|0)|0]|0)==(a[aG+120376|0]|0)){aG=aG+1|0}else{break L2253}}if((aG|0)!=4){break}c[t>>2]=c[14158];c[t+4>>2]=c[14159];c[t+8>>2]=c[14160];c[t+12>>2]=c[14161];c[t+16>>2]=c[14162];c[t+20>>2]=c[14163];c[t+24>>2]=c[14164];c[t+28>>2]=c[14165];c[t+32>>2]=c[14166];c[t+36>>2]=c[14167];c[t+40>>2]=c[14168];c[t+44>>2]=c[14169];c[t+48>>2]=c[14170];c[t+52>>2]=c[14171];c[13898]=(c[13898]|0)+1;aK=al;aL=ag;aM=aq;break L2222}}while(0);at=is(o)|0;ar=c[at>>2]|0;if((ar|0)==1){aR=+(c[at+8>>2]|0)}else if((ar|0)==2){aR=+h[at+8>>3]}else if((ar|0)==3){aR=+uz(c[at+8>>2]|0,0)}else{E=1623;break L2195}if((c[Y>>2]|0)==3){uu(c[Z>>2]|0);c[Y>>2]=1}at=~~aR;c[_>>2]=at-1;if(!((a[30080]&1)==0|$)){lK(b,at);aK=at;aL=ag;aM=aq;break}ar=c[aa>>2]|0;aE=c[8798]|0;ap=(aE|0)>0;z=at;L2272:while(1){au=43264;while(1){aS=c[au>>2]|0;if((aS|0)==0){break}if((c[aS+4>>2]|0)==(z|0)){break L2272}else{au=aS|0}}aT=z-1|0;if(!((z|0)>(aE|0)&ap)){E=1638;break}z=((aT|0)%(aE|0)&-1)+1|0}if((E|0)==1638){E=0;c[_>>2]=aT;c[ab>>2]=1;c[ac>>2]=aT;c[ad>>2]=aT;aK=at;aL=ag;aM=aq;break}aE=aS+8|0;c[t>>2]=c[aE>>2];c[t+4>>2]=c[aE+4>>2];c[t+8>>2]=c[aE+8>>2];c[t+12>>2]=c[aE+12>>2];c[t+16>>2]=c[aE+16>>2];c[t+20>>2]=c[aE+20>>2];c[t+24>>2]=c[aE+24>>2];c[t+28>>2]=c[aE+28>>2];c[t+32>>2]=c[aE+32>>2];c[t+36>>2]=c[aE+36>>2];c[t+40>>2]=c[aE+40>>2];c[t+44>>2]=c[aE+44>>2];c[t+48>>2]=c[aE+48>>2];c[t+52>>2]=c[aE+52>>2];c[aa>>2]=ar;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[_>>2]=z;a[ae]=0;aK=at;aL=ag;aM=aq;break}if((a[ae]&1)!=0){aK=at;aL=ag;aM=aq;break}c[ab>>2]=1;c[ac>>2]=c[_>>2];aK=at;aL=ag;aM=aq}}while(0);ao=c[13898]|0;aE=c[1054]|0;ap=c[aE+(ao*40&-1)+36>>2]|0;au=c[aE+(ao*40&-1)+32>>2]|0;aG=(a[aE+(ao*40&-1)|0]&1)==0;L2287:do{if((ap|0)>0&(aG^1)){aD=c[10036]|0;aF=0;aJ=0;aU=au;while(1){if((a[aF+205488|0]|0)==(a[aD+(aF+aU|0)|0]|0)){aV=aU;aW=aJ}else{if((aF|0)!=3){E=1650;break L2287}aV=aU-1|0;aW=1}aX=aF+1|0;if((aX|0)<(aW+ap|0)){aF=aX;aJ=aW;aU=aV}else{break}}if((aW|0)==0){if(!((aF|0)==2|(aF|0)==7)){E=1650;break}}aU=aL+1|0;if((aL|0)!=0){av=aK;aw=aj;ax=ai;ay=ah;az=aU;aA=aM;aB=ak;aC=ao;break L2193}c[13898]=ao-1;hI(X,6);a[O]=1;aY=ak;aZ=aj;a_=ai;a$=ah;a0=aU;a1=aM;a2=G}else{E=1650}}while(0);L2300:do{if((E|0)==1650){E=0;aU=c[8272]|0;aJ=(aU|0)>(ao|0);L2302:do{if(aJ){if(aG){av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}aD=c[10036]|0;aq=0;while(1){if((aq|0)>=(ap|0)){break}if((a[aD+(au+aq|0)|0]|0)==(a[aq+119088|0]|0)){aq=aq+1|0}else{E=1656;break L2302}}if((aq|0)!=2){E=1656}}else{E=1656}}while(0);L2309:do{if((E|0)==1656){E=0;L2311:do{if((ap|0)>0&(aG^1)){aF=c[10036]|0;aD=0;at=0;z=au;while(1){if((a[aD+116144|0]|0)==(a[aF+(aD+z|0)|0]|0)){a3=z;a4=at}else{if((aD|0)!=5){break L2311}a3=z-1|0;a4=1}ar=aD+1|0;if((ar|0)<(a4+ap|0)){aD=ar;at=a4;z=a3}else{break}}if((a4|0)!=0){break L2309}if((aD|0)==4|(aD|0)==9){break L2309}}}while(0);L2322:do{if(aJ){if(aG){E=1746;break}aq=c[10036]|0;z=0;while(1){if((z|0)>=(ap|0)){break}if((a[aq+(au+z|0)|0]|0)==(a[z+179168|0]|0)){z=z+1|0}else{E=1670;break L2322}}if((z|0)==2){break L2309}else{E=1670}}else{E=1670}}while(0);L2329:do{if((E|0)==1670){E=0;if(!((ap|0)>0&(aG^1))){E=1746;break}aq=c[10036]|0;aD=0;at=0;aF=au;while(1){if((a[aD+178592|0]|0)==(a[aq+(aD+aF|0)|0]|0)){a5=aF;a6=at}else{if((aD|0)!=5){break}a5=aF-1|0;a6=1}ar=aD+1|0;if((ar|0)<(a6+ap|0)){aD=ar;at=a6;aF=a5}else{E=1676;break}}if((E|0)==1676){E=0;if((a6|0)!=0){break L2309}if((aD|0)==4|(aD|0)==9){break L2309}}if(!((ap|0)>0&(aG^1))){E=1746;break}aF=c[10036]|0;at=0;aq=0;z=au;while(1){if((a[at+128400|0]|0)==(a[aF+(at+z|0)|0]|0)){a7=z;a8=aq}else{if((at|0)!=5){E=1746;break L2329}a7=z-1|0;a8=1}ar=at+1|0;if((ar|0)<(a8+ap|0)){at=ar;aq=a8;z=a7}else{break}}if((a8|0)!=0){break}if(!((at|0)==4|(at|0)==9)){E=1746}}}while(0);L2351:do{if((E|0)==1746){E=0;L2353:do{if(aJ){if(aG){av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}z=c[10036]|0;aq=0;while(1){if((aq|0)>=(ap|0)){E=1751;break}if((a[z+(au+aq|0)|0]|0)==(a[aq+128296|0]|0)){aq=aq+1|0}else{break}}if((E|0)==1751){E=0;if((aq|0)==2){break L2351}}if(!aJ){break}if(aG){av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}z=c[10036]|0;aF=0;while(1){if((aF|0)>=(ap|0)){break}if((a[z+(au+aF|0)|0]|0)==(a[aF+120376|0]|0)){aF=aF+1|0}else{break L2353}}if((aF|0)!=4){break}z=aM+1|0;if((aM|0)!=0){av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=z;aB=ak;aC=ao;break L2193}c[13898]=ao+1;c[t>>2]=c[14158];c[t+4>>2]=c[14159];c[t+8>>2]=c[14160];c[t+12>>2]=c[14161];c[t+16>>2]=c[14162];c[t+20>>2]=c[14163];c[t+24>>2]=c[14164];c[t+28>>2]=c[14165];c[t+32>>2]=c[14166];c[t+36>>2]=c[14167];c[t+40>>2]=c[14168];c[t+44>>2]=c[14169];c[t+48>>2]=c[14170];c[t+52>>2]=c[14171];aY=ak;aZ=aj;a_=ai;a$=ah;a0=aL;a1=z;a2=G;break L2300}}while(0);L2371:do{if((ap|0)>0&(aG^1)){at=c[10036]|0;z=0;aq=0;aD=au;while(1){if((a[z+139064|0]|0)==(a[at+(z+aD|0)|0]|0)){a9=aD;ba=aq}else{if((z|0)!=6){E=1778;break L2371}a9=aD-1|0;ba=1}ar=z+1|0;if((ar|0)<(ba+ap|0)){z=ar;aq=ba;aD=a9}else{break}}if((ba|0)!=0){break}if(!((z|0)==5|(z|0)==9)){E=1778}}else{E=1778}}while(0);L2381:do{if((E|0)==1778){E=0;L2383:do{if(aJ){if(aG){av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}aD=c[10036]|0;aq=0;while(1){if((aq|0)>=(ap|0)){break}if((a[aD+(au+aq|0)|0]|0)==(a[aq+205624|0]|0)){aq=aq+1|0}else{break L2383}}if((aq|0)==2){break L2381}}}while(0);L2391:do{if((ap|0)>0&(aG^1)){z=c[10036]|0;aD=0;at=0;aF=au;while(1){if((a[aD+103928|0]|0)==(a[z+(aD+aF|0)|0]|0)){bb=aF;bc=at}else{if((aD|0)!=6){E=1803;break L2391}bb=aF-1|0;bc=1}ar=aD+1|0;if((ar|0)<(bc+ap|0)){aD=ar;at=bc;aF=bb}else{break}}if((bc|0)!=0){break}if(!((aD|0)==5|(aD|0)==9)){E=1803}}else{E=1803}}while(0);L2401:do{if((E|0)==1803){E=0;L2403:do{if(aJ){if(aG){E=1847;break}aF=c[10036]|0;at=0;while(1){if((at|0)>=(ap|0)){break}if((a[aF+(au+at|0)|0]|0)==(a[at+103560|0]|0)){at=at+1|0}else{E=1839;break L2403}}if((at|0)==2){break L2401}else{E=1839}}else{E=1839}}while(0);L2410:do{if((E|0)==1839){E=0;if(!((ap|0)>0&(aG^1))){E=1847;break}aD=c[10036]|0;aF=0;z=0;aq=au;while(1){if((a[aF+100112|0]|0)==(a[aD+(aF+aq|0)|0]|0)){bd=aq;be=z}else{if((aF|0)!=6){E=1847;break L2410}bd=aq-1|0;be=1}ar=aF+1|0;if((ar|0)<(be+ap|0)){aF=ar;z=be;aq=bd}else{break}}if((be|0)!=0){break}if(!((aF|0)==5|(aF|0)==13)){E=1847}}}while(0);if((E|0)==1847){E=0;if(!aJ){av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}if(aG){av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}aq=c[10036]|0;z=0;while(1){if((z|0)>=(ap|0)){break}if((a[aq+(au+z|0)|0]|0)==(a[z+99680|0]|0)){z=z+1|0}else{av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}}if((z|0)!=2){av=aK;aw=aj;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}}aq=ao+1|0;c[13898]=aq;if(!e){uh(aq,99224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);aq=is(q)|0;aD=c[aq>>2]|0;if((aD|0)==3){at=c[aq+8>>2]|0;uz(at,0)}else if(!((aD|0)==1|(aD|0)==2)){E=1864;break L2195}if((c[w>>2]|0)!=3){aY=ak;aZ=aj;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}uu(c[y>>2]|0);c[w>>2]=1;aY=ak;aZ=aj;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}aD=is(n)|0;at=c[aD>>2]|0;if((at|0)==2){bf=+h[aD+8>>3]}else if((at|0)==3){bf=+uz(c[aD+8>>2]|0,0)}else if((at|0)==1){bf=+(c[aD+8>>2]|0)}else{E=1858;break L2195}if((c[C>>2]|0)==3){uu(c[x>>2]|0);c[C>>2]=1}c[B>>2]=~~bf;aY=1;aZ=aj;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}}while(0);if(!e){uh(ao,100856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[13898]=(c[13898]|0)+2;aY=ak;aZ=aj;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}aD=aj+1|0;if((aj|0)!=0){av=aK;aw=aD;ax=ai;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}at=ao+1|0;c[13898]=at;aq=c[aE+(at*40&-1)+36>>2]|0;ar=c[aE+(at*40&-1)+32>>2]|0;aX=(a[aE+(at*40&-1)|0]&1)==0;L2450:do{if((aq|0)>0&(aX^1)){at=c[10036]|0;bg=0;bh=0;bi=ar;while(1){if((a[bg+111056|0]|0)==(a[at+(bg+bi|0)|0]|0)){bj=bi;bk=bh}else{if((bg|0)!=3){break}bj=bi-1|0;bk=1}bl=bg+1|0;if((bl|0)<(bk+aq|0)){bg=bl;bh=bk;bi=bj}else{E=1817;break}}do{if((E|0)==1817){E=0;if((bk|0)==0){if(!((bg|0)==2|(bg|0)==8)){break}}h[H>>3]=-3.0;c[13898]=ao+2;aY=ak;aZ=aD;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}}while(0);if(!((aq|0)>0&(aX^1))){break}bg=c[10036]|0;bi=0;bh=0;at=ar;while(1){if((a[bi+184456|0]|0)==(a[bg+(bi+at|0)|0]|0)){bm=at;bn=bh}else{if((bi|0)!=3){break L2450}bm=at-1|0;bn=1}z=bi+1|0;if((z|0)<(bn+aq|0)){bi=z;bh=bn;at=bm}else{break}}if((bn|0)==0){if(!((bi|0)==2|(bi|0)==7)){break}}h[H>>3]=-2.0;c[13898]=ao+2;aY=ak;aZ=aD;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}}while(0);aq=is(m)|0;ar=c[aq>>2]|0;if((ar|0)==1){bo=+(c[aq+8>>2]|0)}else if((ar|0)==2){bo=+h[aq+8>>3]}else if((ar|0)==3){bo=+uz(c[aq+8>>2]|0,0)}else{E=1833;break L2195}if((c[u>>2]|0)==3){uu(c[F>>2]|0);c[u>>2]=1}h[H>>3]=bo;if(bo>=0.0){aY=ak;aZ=aD;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}h[H>>3]=0.0;aY=ak;aZ=aD;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}}while(0);if(!e){uh(ao,104432,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[13898]=(c[13898]|0)+2;aY=ak;aZ=aj;a_=ai;a$=ah;a0=aL;a1=aM;a2=G;break L2300}aq=ai+1|0;if((ai|0)!=0){av=aK;aw=aj;ax=aq;ay=ah;az=aL;aA=aM;aB=ak;aC=ao;break L2193}c[13898]=ao+1;ar=is(g)|0;aX=c[ar>>2]|0;if((aX|0)==1){bp=+(c[ar+8>>2]|0)}else if((aX|0)==2){bp=+h[ar+8>>3]}else if((aX|0)==3){bp=+uz(c[ar+8>>2]|0,0)}else{E=1790;break L2195}if((c[I>>2]|0)==3){uu(c[J>>2]|0);c[I>>2]=1}c[K>>2]=~~bp-1;aY=ak;aZ=aj;a_=aq;a$=ah;a0=aL;a1=aM;a2=G;break L2300}}while(0);aq=ah+1|0;if((ah|0)!=0){av=aK;aw=aj;ax=ai;ay=aq;az=aL;aA=aM;aB=ak;aC=ao;break L2193}c[13898]=ao+1;ar=is(j)|0;aX=c[ar>>2]|0;if((aX|0)==1){bq=+(c[ar+8>>2]|0)}else if((aX|0)==2){bq=+h[ar+8>>3]}else if((aX|0)==3){bq=+uz(c[ar+8>>2]|0,0)}else{E=1757;break L2195}if((c[L>>2]|0)==3){uu(c[M>>2]|0);c[L>>2]=1}h[N>>3]=bq;if(bq>=0.0){aY=ak;aZ=aj;a_=ai;a$=aq;a0=aL;a1=aM;a2=G;break L2300}h[N>>3]=0.0;aY=ak;aZ=aj;a_=ai;a$=aq;a0=aL;a1=aM;a2=G;break L2300}}while(0);a[O]=1;aJ=aL+1|0;if((aL|0)!=0){av=aK;aw=aj;ax=ai;ay=ah;az=aJ;aA=aM;aB=ak;aC=ao;break L2193}aq=ao+1|0;c[13898]=aq;ar=c[aE+(aq*40&-1)+36>>2]|0;aX=c[aE+(aq*40&-1)+32>>2]|0;at=(a[aE+(aq*40&-1)|0]&1)==0;L2511:do{if((ar|0)>0&(at^1)){bh=c[10036]|0;bg=0;z=0;bl=aX;while(1){if((a[bg+123456|0]|0)==(a[bh+(bg+bl|0)|0]|0)){br=bl;bs=z}else{if((bg|0)!=3){break}br=bl-1|0;bs=1}bt=bg+1|0;if((bt|0)<(bs+ar|0)){bg=bt;z=bs;bl=br}else{E=1685;break}}do{if((E|0)==1685){E=0;if((bs|0)==0){if(!((bg|0)==8|(bg|0)==2)){break}}c[13898]=ao;hI(X,3);aY=ak;aZ=aj;a_=ai;a$=ah;a0=aJ;a1=aM;a2=G;break L2300}}while(0);if(!((ar|0)>0&(at^1))){break}bg=c[10036]|0;bl=0;z=0;bh=aX;while(1){if((a[bl+205488|0]|0)==(a[bg+(bl+bh|0)|0]|0)){bu=bh;bv=z}else{if((bl|0)!=3){break L2511}bu=bh-1|0;bv=1}bt=bl+1|0;if((bt|0)<(bv+ar|0)){bl=bt;z=bv;bh=bu}else{break}}if((bv|0)==0){if(!((bl|0)==7|(bl|0)==2)){break}}c[13898]=ao;hI(X,6);aY=ak;aZ=aj;a_=ai;a$=ah;a0=aJ;a1=aM;a2=G;break L2300}}while(0);L2537:do{if((aU|0)>(aq|0)){if(at){break}bh=c[10036]|0;z=0;while(1){if((z|0)>=(ar|0)){break}if((a[bh+(aX+z|0)|0]|0)==(a[z+120376|0]|0)){z=z+1|0}else{E=1704;break L2537}}if((z|0)!=4){E=1704;break}c[P>>2]=1;c[Q>>2]=-4;c[13898]=ao+2;aY=ak;aZ=aj;a_=ai;a$=ah;a0=aJ;a1=aM;a2=G;break L2300}else{E=1704}}while(0);L2545:do{if((E|0)==1704){E=0;if(!((ar|0)>0&(at^1))){break}aq=c[10036]|0;aU=0;bh=0;bl=aX;while(1){if((a[aU+111056|0]|0)==(a[aq+(aU+bl|0)|0]|0)){bw=bl;bx=bh}else{if((aU|0)!=3){break L2545}bw=bl-1|0;bx=1}bg=aU+1|0;if((bg|0)<(bx+ar|0)){aU=bg;bh=bx;bl=bw}else{break}}if((bx|0)==0){if(!((aU|0)==2|(aU|0)==8)){break}}c[13898]=ao+2;c[W>>2]=-6;c[P>>2]=2;aY=ak;aZ=aj;a_=ai;a$=ah;a0=aJ;a1=aM;a2=G;break L2300}}while(0);if(!d){c[P>>2]=1;ar=is(k)|0;aX=c[ar>>2]|0;if((aX|0)==1){by=+(c[ar+8>>2]|0)}else if((aX|0)==2){by=+h[ar+8>>3]}else if((aX|0)==3){by=+uz(c[ar+8>>2]|0,0)}else{E=1734;break L2195}if((c[U>>2]|0)==3){uu(c[V>>2]|0);c[U>>2]=1}c[Q>>2]=~~by-1;aY=ak;aZ=aj;a_=ai;a$=ah;a0=aJ;a1=aM;a2=G;break}ar=is(l)|0;aX=c[ar>>2]|0;if((aX|0)==1){bz=+(c[ar+8>>2]|0)}else if((aX|0)==2){bz=+h[ar+8>>3]}else if((aX|0)==3){bz=+uz(c[ar+8>>2]|0,0)}else{E=1718;break L2195}if((c[R>>2]|0)==3){uu(c[S>>2]|0);c[R>>2]=1}ar=c[8798]|0;aX=(ar|0)>0;at=~~bz;L2578:while(1){bl=43264;while(1){bA=c[bl>>2]|0;if((bA|0)==0){break}if((c[bA+4>>2]|0)==(at|0)){E=1725;break L2578}else{bl=bA|0}}bl=at-1|0;if(!((at|0)>(ar|0)&aX)){bB=1;bC=bl;bD=G;break}at=((bl|0)%(ar|0)&-1)+1|0}do{if((E|0)==1725){E=0;ar=c[bA+48>>2]|0;at=c[bA+52>>2]|0;bE=+h[bA+56>>3];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){bB=ar;bC=at;bD=bE;break}aX=(a[bA+40|0]&1)==0;bB=aX?1:ar;bC=aX?c[bA+12>>2]|0:at;bD=bE}}while(0);c[P>>2]=bB;c[Q>>2]=bC;h[T>>3]=bD;aY=ak;aZ=aj;a_=ai;a$=ah;a0=aJ;a1=aM;a2=bD}}while(0);ao=c[13898]|0;aE=c[8272]|0;if((ao|0)<(aE|0)){G=a2;af=a1;ag=a0;ah=a$;ai=a_;aj=aZ;ak=aY;al=aK;am=ao;an=aE}else{av=aK;aw=aZ;ax=a_;ay=a$;az=a0;aA=a1;aB=aY;aC=ao;break L2193}}if((E|0)==1757){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((E|0)==1623){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((E|0)==1718){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((E|0)==1858){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((E|0)==1864){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((E|0)==1790){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((E|0)==1734){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((E|0)==1833){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}else{av=0;aw=0;ax=0;ay=0;az=0;aA=0;aB=0;aC=p}}while(0);if((aA|0)>1|(az|0)>1|(ay|0)>1|(ax|0)>1|(aw|0)>1){uf(aC,98720,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((az|0)==0){bF=av}else{av=b+40|0;az=r+40|0;c[av>>2]=c[az>>2];c[av+4>>2]=c[az+4>>2];c[av+8>>2]=c[az+8>>2];c[av+12>>2]=c[az+12>>2];a[b+32|0]=a[r+32|0]&1;bF=-8}if((ay|0)!=0){h[b+16>>3]=+h[r+16>>3]}if((ax|0)!=0){c[b+8>>2]=c[r+8>>2]}if((aw|0)!=0){h[b+24>>3]=+h[r+24>>3]}if((aB|0)!=0){c[b+12>>2]=c[r+12>>2]}if((c[r+4>>2]|0)!=-6){i=f;return bF|0}c[b+4>>2]=-6;i=f;return bF|0}function hI(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0.0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0.0,T=0,U=0,V=0.0,W=0.0,X=0,Y=0;e=i;i=i+96|0;f=e|0;g=e+24|0;j=e+48|0;k=e+72|0;l=c[13898]|0;m=l+1|0;c[13898]=m;n=c[8272]|0;if((m|0)>=(n|0)){uf(m,92096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=c[1054]|0;p=(a[o+(m*40&-1)|0]&1)==0;q=c[o+(m*40&-1)+36>>2]|0;r=o+(m*40&-1)+32|0;L2626:do{if(p){s=c[r>>2]|0}else{t=c[10036]|0;u=0;while(1){if((u|0)>=(q|0)){w=1890;break}if((a[t+((c[r>>2]|0)+u|0)|0]|0)==(a[u+103664|0]|0)){u=u+1|0}else{break}}do{if((w|0)==1890){if((u|0)!=1){break}uf(m,92096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);u=c[r>>2]|0;if(p){s=u;break}if((q|0)>0){t=c[10036]|0;x=0;y=0;z=u;while(1){if((a[x+184456|0]|0)==(a[t+(x+z|0)|0]|0)){A=z;B=y}else{if((x|0)!=3){break}A=z-1|0;B=1}C=x+1|0;if((C|0)<(B+q|0)){x=C;y=B;z=A}else{w=1899;break}}do{if((w|0)==1899){if((B|0)==0){if(!((x|0)==7|(x|0)==2)){break}}c[13898]=l+2;c[b>>2]=0;i=e;return}}while(0);if(p){s=u;break}}x=c[10036]|0;z=0;while(1){if((z|0)>=(q|0)){w=1906;break}if((a[x+(u+z|0)|0]|0)==(a[z+120376|0]|0)){z=z+1|0}else{break}}do{if((w|0)==1906){if((z|0)!=4){break}c[13898]=l+2;c[b>>2]=1;c[b+4>>2]=-4;i=e;return}}while(0);if(p){s=u;break}z=c[10036]|0;x=0;while(1){if((x|0)>=(q|0)){break}if((a[z+(u+x|0)|0]|0)==(a[x+225056|0]|0)){x=x+1|0}else{s=u;break L2626}}if((x|0)!=2){s=u;break}y=l+2|0;c[13898]=y;if((y|0)>=(n|0)){uf(y,91408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L2669:do{if((a[o+(y*40&-1)|0]&1)!=0){t=c[o+(y*40&-1)+36>>2]|0;C=o+(y*40&-1)+32|0;D=0;while(1){if((D|0)>=(t|0)){break}if((a[z+((c[C>>2]|0)+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{break L2669}}if((D|0)!=1){break}uf(y,91408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);y=b|0;c[y>>2]=1;z=is(j)|0;u=c[z>>2]|0;if((u|0)==1){E=+(c[z+8>>2]|0)}else if((u|0)==2){E=+h[z+8>>3]}else if((u|0)==3){E=+uz(c[z+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}z=j|0;if((c[z>>2]|0)==3){uu(c[j+8>>2]|0);c[z>>2]=1}z=~~E-1|0;c[b+4>>2]=z;if((z|0)>=-4){i=e;return}c[y>>2]=0;uh(c[13898]|0,90768,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=e;return}}while(0);if((d|0)<2){c[b>>2]=0;uf(c[13898]|0,90352,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L2695:do{if(!p){j=c[10036]|0;B=0;while(1){if((B|0)>=(q|0)){w=1935;break}if((a[j+(s+B|0)|0]|0)==(a[B+72816|0]|0)){B=B+1|0}else{w=1936;break}}if((w|0)==1935){if((B|0)!=2){w=1936}}do{if((w|0)==1936){if(!((q|0)>0&(p^1))){break L2695}j=c[10036]|0;A=0;r=0;y=s;while(1){if((a[A+73824|0]|0)==(a[j+(A+y|0)|0]|0)){F=y;G=r}else{if((A|0)!=5){break}F=y-1|0;G=1}z=A+1|0;if((z|0)<(G+q|0)){A=z;r=G;y=F}else{w=1942;break}}if((w|0)==1942){if((G|0)!=0){break}if((A|0)==4|(A|0)==9){break}}if(p){break L2695}L2716:do{if((q|0)>0){y=c[10036]|0;r=0;j=0;D=s;while(1){if((a[r+123456|0]|0)==(a[y+(r+D|0)|0]|0)){H=D;I=j}else{if((r|0)!=3){break L2716}H=D-1|0;I=1}z=r+1|0;if((z|0)<(I+q|0)){r=z;j=I;D=H}else{break}}if((I|0)==0){if(!((r|0)==8|(r|0)==2)){break}}c[13898]=l+2;c[b>>2]=3;D=c[13898]|0;j=c[1054]|0;y=c[j+(D*40&-1)+36>>2]|0;z=c[j+(D*40&-1)+32>>2]|0;L2728:do{if((a[j+(D*40&-1)|0]&1)==0){w=1970}else{if((y|0)<=0){J=b+8|0;break}u=c[10036]|0;x=0;C=0;t=z;while(1){if((a[x+111056|0]|0)==(a[u+(x+t|0)|0]|0)){K=t;L=C}else{if((x|0)!=3){w=1970;break L2728}K=t-1|0;L=1}M=x+1|0;if((M|0)<(L+y|0)){x=M;C=L;t=K}else{break}}if((L|0)!=0|(M|0)==3){N=b+8|0}else{t=b+8|0;if((M|0)==9){N=t}else{J=t;break}}h[N>>3]=-1.0;c[13898]=(c[13898]|0)+1;i=e;return}}while(0);if((w|0)==1970){J=b+8|0}h[J>>3]=0.0;c[b+4>>2]=hK()|0;i=e;return}}while(0);if(!((q|0)>0&(p^1))){break L2695}A=c[10036]|0;y=0;z=0;D=s;while(1){if((a[y+205488|0]|0)==(a[A+(y+D|0)|0]|0)){O=D;P=z}else{if((y|0)!=3){break L2695}O=D-1|0;P=1}j=y+1|0;if((j|0)<(P+q|0)){y=j;z=P;D=O}else{break}}if((P|0)==0){if(!((y|0)==2|(y|0)==7)){break L2695}}D=l+2|0;c[13898]=D;z=(n|0)>(D|0);L2761:do{if(z){if((a[o+(D*40&-1)|0]&1)!=0){A=c[o+(D*40&-1)+36>>2]|0;j=o+(D*40&-1)+32|0;r=c[10036]|0;t=0;while(1){if((t|0)>=(A|0)){w=1988;break}if((a[r+((c[j>>2]|0)+t|0)|0]|0)==(a[t+77416|0]|0)){t=t+1|0}else{break}}do{if((w|0)==1988){if((t|0)!=1){break}j=b|0;if((d|0)<=5){c[j>>2]=0;uf(c[13898]|0,89200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[j>>2]=6;c[13898]=(c[13898]|0)+1;i=e;return}}while(0);if(!z){w=1993;break}}t=a[o+(D*40&-1)|0]|0;if((t&1)==0){Q=t;break}j=c[o+(D*40&-1)+36>>2]|0;r=o+(D*40&-1)+32|0;A=c[10036]|0;C=0;while(1){if((C|0)>=(j|0)){break}if((a[A+((c[r>>2]|0)+C|0)|0]|0)==(a[C+88416|0]|0)){C=C+1|0}else{Q=t;break L2761}}if((C|0)!=2){Q=t;break}c[b>>2]=4;r=(c[13898]|0)+1|0;c[13898]=r;if((r|0)>=(c[8272]|0)){uf(r,87312,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}A=c[1054]|0;L2786:do{if((a[A+(r*40&-1)|0]&1)!=0){j=c[A+(r*40&-1)+36>>2]|0;x=A+(r*40&-1)+32|0;u=c[10036]|0;R=0;while(1){if((R|0)>=(j|0)){break}if((a[u+((c[x>>2]|0)+R|0)|0]|0)==(a[R+103664|0]|0)){R=R+1|0}else{break L2786}}if((R|0)!=1){break}uf(r,87312,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);r=is(f)|0;A=c[r>>2]|0;if((A|0)==1){S=+(c[r+8>>2]|0)}else if((A|0)==2){S=+h[r+8>>3]}else if((A|0)==3){S=+uz(c[r+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}r=f|0;if((c[r>>2]|0)==3){uu(c[f+8>>2]|0);c[r>>2]=1}h[b+8>>3]=S;i=e;return}else{w=1993}}while(0);if((w|0)==1993){Q=a[o+(D*40&-1)|0]|0}z=c[o+(D*40&-1)+36>>2]|0;L2807:do{if((Q&1)!=0&(z|0)>0){y=c[10036]|0;r=0;A=0;t=c[o+(D*40&-1)+32>>2]|0;while(1){if((a[r+204616|0]|0)==(a[y+(r+t|0)|0]|0)){T=t;U=A}else{if((r|0)!=4){break L2807}T=t-1|0;U=1}C=r+1|0;if((C|0)<(U+z|0)){r=C;A=U;t=T}else{break}}if((U|0)==0){if(!((r|0)==3|(r|0)==8)){break}}c[b>>2]=5;t=(c[13898]|0)+1|0;c[13898]=t;if((t|0)>=(c[8272]|0)){uf(t,85200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}A=c[1054]|0;L2822:do{if((a[A+(t*40&-1)|0]&1)!=0){y=c[A+(t*40&-1)+36>>2]|0;C=A+(t*40&-1)+32|0;x=c[10036]|0;u=0;while(1){if((u|0)>=(y|0)){break}if((a[x+((c[C>>2]|0)+u|0)|0]|0)==(a[u+103664|0]|0)){u=u+1|0}else{break L2822}}if((u|0)!=1){break}uf(t,85200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);t=is(k)|0;A=c[t>>2]|0;if((A|0)==1){V=+(c[t+8>>2]|0)}else if((A|0)==2){V=+h[t+8>>3]}else if((A|0)==3){V=+uz(c[t+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t=k|0;if((c[t>>2]|0)==3){uu(c[k+8>>2]|0);c[t>>2]=1}h[b+8>>3]=V;if(V<0.0|V>1.0){uf(c[13898]|0,84296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{i=e;return}}}while(0);if((d|0)<=5){i=e;return}c[b>>2]=6;i=e;return}}while(0);c[13898]=l+2;c[b>>2]=2;B=is(g)|0;z=c[B>>2]|0;if((z|0)==1){W=+(c[B+8>>2]|0)}else if((z|0)==2){W=+h[B+8>>3]}else if((z|0)==3){W=+uz(c[B+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}B=g|0;if((c[B>>2]|0)==3){uu(c[g+8>>2]|0);c[B>>2]=1}c[b+4>>2]=~~W;i=e;return}}while(0);if(!((q|0)>0&(((d|0)<7|p)^1))){uf(m,83704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=c[10036]|0;d=0;g=0;l=s;while(1){if((a[d+111056|0]|0)==(a[p+(d+l|0)|0]|0)){X=l;Y=g}else{if((d|0)!=3){w=2058;break}X=l-1|0;Y=1}s=d+1|0;if((s|0)<(Y+q|0)){d=s;g=Y;l=X}else{break}}if((w|0)==2058){uf(m,83704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((Y|0)==0){if((d|0)==2|(d|0)==8){break}uf(m,83704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[b>>2]=7;c[13898]=(c[13898]|0)+1;i=e;return}function hJ(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0.0,as=0,at=0,au=0,av=0.0,aw=0.0;j=i;i=i+72|0;k=g;g=i;i=i+16|0;c[g>>2]=c[k>>2];c[g+4>>2]=c[k+4>>2];c[g+8>>2]=c[k+8>>2];c[g+12>>2]=c[k+12>>2];k=j|0;l=j+24|0;m=j+48|0;n=b|0;c[n>>2]=d;d=b+4|0;c[d>>2]=e;e=b+8|0;c[e>>2]=f;f=b+16|0;o=f;p=g;c[o>>2]=c[p>>2];c[o+4>>2]=c[p+4>>2];c[o+8>>2]=c[p+8>>2];c[o+12>>2]=c[p+12>>2];p=c[13898]|0;o=c[8272]|0;if((p|0)>=(o|0)){i=j;return}g=c[1054]|0;q=(a[g+(p*40&-1)|0]&1)==0;if(q){i=j;return}r=c[g+(p*40&-1)+36>>2]|0;s=g+(p*40&-1)+32|0;t=c[10036]|0;u=0;while(1){if((u|0)>=(r|0)){w=2080;break}if((a[t+((c[s>>2]|0)+u|0)|0]|0)==(a[u+103664|0]|0)){u=u+1|0}else{w=2081;break}}do{if((w|0)==2080){if(!((u|0)==1|q)){break}i=j;return}else if((w|0)==2081){if(!q){break}i=j;return}}while(0);u=c[g+(p*40&-1)+36>>2]|0;s=g+(p*40&-1)+32|0;t=c[10036]|0;r=0;while(1){if((r|0)>=(u|0)){w=2085;break}if((a[t+((c[s>>2]|0)+r|0)|0]|0)==(a[r+116360|0]|0)){r=r+1|0}else{w=2086;break}}if((w|0)==2085){if((r|0)!=2){w=2086}}do{if((w|0)==2086){r=c[g+(p*40&-1)+36>>2]|0;if(!((r|0)>0&(q^1))){i=j;return}s=c[10036]|0;t=0;u=0;x=c[g+(p*40&-1)+32>>2]|0;while(1){if((a[t+121984|0]|0)==(a[s+(t+x|0)|0]|0)){y=x;z=u}else{if((t|0)!=4){w=2253;break}y=x-1|0;z=1}A=t+1|0;if((A|0)<(z+r|0)){t=A;u=z;x=y}else{break}}if((w|0)==2253){i=j;return}if((z|0)!=0){break}if((t|0)==3|(t|0)==9){break}i=j;return}}while(0);z=p+1|0;c[13898]=z;if((z|0)>=(o|0)){i=j;return}o=l|0;p=l+8|0;y=m|0;q=m+8|0;x=f|0;u=b+20|0;b=k|0;r=k+8|0;s=0;A=0;B=0;C=z;z=g;L2917:while(1){g=a[z+(C*40&-1)|0]|0;D=(g&1)==0;E=c[z+(C*40&-1)+36>>2]|0;F=z+(C*40&-1)+32|0;L2919:do{if(D){G=B;H=C;I=E;J=c[F>>2]|0;K=g}else{L=c[10036]|0;M=0;while(1){if((M|0)>=(E|0)){w=2101;break}if((a[L+((c[F>>2]|0)+M|0)|0]|0)==(a[M+103664|0]|0)){M=M+1|0}else{break}}if((w|0)==2101){w=0;if((M|0)==1){w=2256;break L2917}}L=c[F>>2]|0;if(!((E|0)>0&(D^1))){G=B;H=C;I=E;J=L;K=g;break}N=c[10036]|0;O=0;P=0;Q=L;while(1){if((a[O+96808|0]|0)==(a[N+(O+Q|0)|0]|0)){R=Q;S=P}else{if((O|0)!=5){G=B;H=C;I=E;J=L;K=g;break L2919}R=Q-1|0;S=1}T=O+1|0;if((T|0)<(S+E|0)){O=T;P=S;Q=R}else{break}}if((S|0)==0){if(!((O|0)==4|(O|0)==11)){G=B;H=C;I=E;J=L;K=g;break}}Q=C+1|0;c[13898]=Q;G=1;H=Q;I=c[z+(Q*40&-1)+36>>2]|0;J=c[z+(Q*40&-1)+32>>2]|0;K=a[z+(Q*40&-1)|0]|0}}while(0);g=(K&1)==0;L2940:do{if((I|0)>0&(g^1)){E=c[10036]|0;D=0;F=0;t=J;while(1){if((a[D+96336|0]|0)==(a[E+(D+t|0)|0]|0)){U=t;V=F}else{if((D|0)!=1){break}U=t-1|0;V=1}Q=D+1|0;if((Q|0)<(V+I|0)){D=Q;F=V;t=U}else{w=2117;break}}do{if((w|0)==2117){w=0;if((V|0)==0){if(!((D|0)==0|(D|0)==5)){break}}c[n>>2]=0;t=(c[13898]|0)+1|0;c[13898]=t;W=s;X=t;break L2940}}while(0);if(g){W=s;X=H;break}L2955:do{if((I|0)>0){D=c[10036]|0;t=0;F=0;E=J;while(1){if((a[t+115496|0]|0)==(a[D+(t+E|0)|0]|0)){Y=E;Z=F}else{if((t|0)!=1){break L2955}Y=E-1|0;Z=1}L=t+1|0;if((L|0)<(Z+I|0)){t=L;F=Z;E=Y}else{break}}if((Z|0)==0){if(!((t|0)==0|(t|0)==5)){break}}c[n>>2]=G?4:1;E=(c[13898]|0)+1|0;c[13898]=E;W=1;X=E;break L2940}}while(0);if(!((I|0)>0&(g^1))){W=s;X=H;break}E=c[10036]|0;F=0;D=0;L=J;while(1){if((a[F+95432|0]|0)==(a[E+(F+L|0)|0]|0)){_=L;$=D}else{if((F|0)!=1){W=s;X=H;break L2940}_=L-1|0;$=1}O=F+1|0;if((O|0)<($+I|0)){F=O;D=$;L=_}else{break}}if(($|0)==0){if(!((F|0)==0|(F|0)==7)){W=s;X=H;break}}c[n>>2]=G?5:2;L=(c[13898]|0)+1|0;c[13898]=L;W=1;X=L}else{W=s;X=H}}while(0);L2980:do{if((X|0)<(c[8272]|0)){g=c[1054]|0;L=(a[g+(X*40&-1)|0]&1)==0;D=c[g+(X*40&-1)+36>>2]|0;E=g+(X*40&-1)+32|0;L2982:do{if(L){aa=c[E>>2]|0}else{g=c[10036]|0;O=0;while(1){if((O|0)>=(D|0)){w=2145;break}if((a[g+((c[E>>2]|0)+O|0)|0]|0)==(a[O+103664|0]|0)){O=O+1|0}else{break}}if((w|0)==2145){w=0;if((O|0)==1){ab=A;break L2980}}g=c[E>>2]|0;if(!((D|0)>0&(L^1))){aa=g;break}t=c[10036]|0;Q=0;P=0;N=g;while(1){if((a[Q+141136|0]|0)==(a[t+(Q+N|0)|0]|0)){ac=N;ad=P}else{if((Q|0)!=2){break}ac=N-1|0;ad=1}M=Q+1|0;if((M|0)<(ad+D|0)){Q=M;P=ad;N=ac}else{w=2152;break}}do{if((w|0)==2152){w=0;if((ad|0)==0){if(!((Q|0)==1|(Q|0)==6)){break}}c[x>>2]=0;N=c[13898]|0;P=N+1|0;c[13898]=P;t=c[8272]|0;O=(t|0)>(P|0);M=c[1054]|0;T=a[M+(P*40&-1)|0]|0;L3003:do{if(O){if((T&1)==0){w=2160;break}ae=c[M+(P*40&-1)+36>>2]|0;af=M+(P*40&-1)+32|0;ag=c[10036]|0;ah=0;while(1){if((ah|0)>=(ae|0)){break}if((a[ag+((c[af>>2]|0)+ah|0)|0]|0)==(a[ah+202112|0]|0)){ah=ah+1|0}else{w=2160;break L3003}}if((ah|0)!=1){w=2160}}else{w=2160}}while(0);do{if((w|0)==2160){w=0;if((T&1)==0){break}af=c[M+(P*40&-1)+36>>2]|0;ag=M+(P*40&-1)+32|0;L3013:do{if((t|0)>(P|0)){ae=c[10036]|0;ai=0;while(1){if((ai|0)>=(af|0)){break}if((a[ae+((c[ag>>2]|0)+ai|0)|0]|0)==(a[ai+119088|0]|0)){ai=ai+1|0}else{w=2174;break L3013}}if((ai|0)!=2){w=2174}}else{w=2174}}while(0);L3019:do{if((w|0)==2174){w=0;ah=c[ag>>2]|0;L3021:do{if((af|0)>0){ae=c[10036]|0;aj=0;ak=0;al=ah;while(1){if((a[aj+116144|0]|0)==(a[ae+(aj+al|0)|0]|0)){am=al;an=ak}else{if((aj|0)!=5){break L3021}am=al-1|0;an=1}ao=aj+1|0;if((ao|0)<(an+af|0)){aj=ao;ak=an;al=am}else{break}}if((an|0)!=0){break L3019}if((aj|0)==4|(aj|0)==9){break L3019}}}while(0);L3032:do{if(O){ai=c[10036]|0;al=0;while(1){if((al|0)>=(af|0)){w=2187;break}if((a[ai+(ah+al|0)|0]|0)==(a[al+93664|0]|0)){al=al+1|0}else{break}}if((w|0)==2187){w=0;if((al|0)==3){break}}if(!O){w=2193;break}ai=c[10036]|0;aj=0;while(1){if((aj|0)>=(af|0)){break}if((a[ai+(ah+aj|0)|0]|0)==(a[aj+225056|0]|0)){aj=aj+1|0}else{w=2193;break L3032}}if((aj|0)!=2){w=2193}}else{w=2193}}while(0);do{if((w|0)==2193){w=0;if((af|0)<=0){ab=A;break L2980}ai=c[10036]|0;al=0;ak=0;ae=ah;while(1){if((a[al+139232|0]|0)==(a[ai+(al+ae|0)|0]|0)){ap=ae;aq=ak}else{if((al|0)!=5){ab=A;break L2980}ap=ae-1|0;aq=1}ao=al+1|0;if((ao|0)<(aq+af|0)){al=ao;ak=aq;ae=ap}else{break}}if((aq|0)!=0){break}if(!((al|0)==4|(al|0)==8)){ab=A;break L2980}}}while(0);c[13898]=N;hI(f,6);ab=A;break L2980}}while(0);hI(f,6);ab=A;break L2980}}while(0);c[x>>2]=1;N=is(k)|0;O=c[N>>2]|0;if((O|0)==1){ar=+(c[N+8>>2]|0)}else if((O|0)==2){ar=+h[N+8>>3]}else if((O|0)==3){ar=+uz(c[N+8>>2]|0,0)}else{w=2165;break L2917}if((c[b>>2]|0)==3){uu(c[r>>2]|0);c[b>>2]=1}c[u>>2]=~~ar-1;ab=A;break L2980}}while(0);if(!((D|0)>0&(L^1))){aa=g;break}Q=c[10036]|0;N=0;O=0;P=g;while(1){if((a[N+140856|0]|0)==(a[Q+(N+P|0)|0]|0)){as=P;at=O}else{if((N|0)!=4){aa=g;break L2982}as=P-1|0;at=1}t=N+1|0;if((t|0)<(at+D|0)){N=t;O=at;P=as}else{break}}if((at|0)==0){if(!((N|0)==3|(N|0)==8)){aa=g;break}}c[x>>2]=1;c[u>>2]=-3;c[13898]=(c[13898]|0)+1;ab=A;break L2980}}while(0);if(A|W^1){w=2258;break L2917}L3081:do{if(!L){E=c[10810]|0;if((E|0)==0){w=2259;break L2917}F=c[10036]|0;P=E;while(1){O=c[P+4>>2]|0;Q=0;while(1){if((Q|0)>=(D|0)){w=2220;break}if((a[F+(aa+Q|0)|0]|0)==(a[O+Q|0]|0)){Q=Q+1|0}else{break}}if((w|0)==2220){w=0;if((a[O+Q|0]|0)==0){w=2221;break}}t=c[P>>2]|0;if((t|0)==0){au=E;break}else{P=t}}do{if((w|0)==2221){w=0;if((a[P+8|0]&1)!=0){au=E;break}if((c[P+16>>2]|0)==1){break L3081}else{au=E}}}while(0);while(1){E=c[au+4>>2]|0;P=0;while(1){if((P|0)>=(D|0)){w=2227;break}if((a[F+(aa+P|0)|0]|0)==(a[E+P|0]|0)){P=P+1|0}else{break}}if((w|0)==2227){w=0;if((a[E+P|0]|0)==0){break}}Q=c[au>>2]|0;if((Q|0)==0){w=2261;break L2917}else{au=Q}}if((a[au+8|0]&1)!=0){w=2260;break L2917}if((c[au+16>>2]|0)!=2){w=2262;break L2917}}}while(0);D=c[n>>2]|0;if((D|0)==1|(D|0)==4){L=is(l)|0;F=c[L>>2]|0;if((F|0)==1){av=+(c[L+8>>2]|0)}else if((F|0)==2){av=+h[L+8>>3]}else if((F|0)==3){av=+uz(c[L+8>>2]|0,0)}else{w=2236;break L2917}if((c[o>>2]|0)==3){uu(c[p>>2]|0);c[o>>2]=1}L=~~(av*100.0+.5);F=(L|0)<0?0:L;c[d>>2]=(F|0)>100?100:F;ab=1;break}else if((D|0)==2|(D|0)==5){D=is(m)|0;F=c[D>>2]|0;if((F|0)==1){aw=+(c[D+8>>2]|0)}else if((F|0)==2){aw=+h[D+8>>3]}else if((F|0)==3){aw=+uz(c[D+8>>2]|0,0)}else{w=2244;break L2917}if((c[y>>2]|0)==3){uu(c[q>>2]|0);c[y>>2]=1}D=~~aw;c[e>>2]=(D|0)<0?0:D;ab=1;break}else{ab=0;break}}else{ab=A}}while(0);D=c[13898]|0;if((D|0)>=(c[8272]|0)){w=2257;break}s=W;A=ab;B=G;C=D;z=c[1054]|0}if((w|0)==2256){i=j;return}else if((w|0)==2257){i=j;return}else if((w|0)==2258){i=j;return}else if((w|0)==2259){i=j;return}else if((w|0)==2165){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((w|0)==2236){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((w|0)==2244){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((w|0)==2260){i=j;return}else if((w|0)==2261){i=j;return}else if((w|0)==2262){i=j;return}}function hK(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0.0;b=i;i=i+56|0;d=b|0;e=b+24|0;f=b+48|0;c[f>>2]=-2;g=c[13898]|0;j=c[1054]|0;k=c[j+(g*40&-1)+36>>2]|0;L3139:do{if((a[j+(g*40&-1)|0]&1)!=0&(k|0)>0){l=c[10036]|0;m=0;n=0;o=c[j+(g*40&-1)+32>>2]|0;while(1){if((a[m+123456|0]|0)==(a[l+(m+o|0)|0]|0)){p=o;q=n}else{if((m|0)!=3){r=g;break L3139}p=o-1|0;q=1}s=m+1|0;if((s|0)<(q+k|0)){m=s;n=q;o=p}else{break}}if((q|0)==0){if(!((m|0)==2|(m|0)==8)){r=g;break}}o=g-1|0;n=c[j+(o*40&-1)+36>>2]|0;if(!((a[j+(o*40&-1)|0]&1)!=0&(n|0)>0)){r=g;break}l=c[10036]|0;s=0;t=0;u=c[j+(o*40&-1)+32>>2]|0;while(1){if((a[s+170936|0]|0)==(a[l+(s+u|0)|0]|0)){w=u;x=t}else{if((s|0)!=4){r=g;break L3139}w=u-1|0;x=1}o=s+1|0;if((o|0)<(x+n|0)){s=o;t=x;u=w}else{break}}if((x|0)==0){if(!((s|0)==3|(s|0)==10)){r=g;break}}u=g+1|0;c[13898]=u;r=u}else{r=g}}while(0);L3163:do{if((r|0)<(c[8272]|0)){L3165:do{if((a[j+(r*40&-1)|0]&1)!=0){g=c[j+(r*40&-1)+36>>2]|0;x=j+(r*40&-1)+32|0;w=c[10036]|0;q=0;while(1){if((q|0)>=(g|0)){break}if((a[w+((c[x>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break L3165}}if((q|0)==1){break L3163}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=r;break}s=c[d+8>>2]|0;if((s|0)==0){break}x=c[7642]|0;w=c[x>>2]|0;do{if((w|0)==0){c[f>>2]=-1;y=2296}else{g=0;p=-1;k=w;while(1){if((aY(s|0,k|0)|0)==0){z=g;break}u=(a_(s|0,k|0,uA(k|0)|0)|0)==0&(p|0)<0?g:p;t=g+1|0;n=c[x+(t<<3)>>2]|0;if((n|0)==0){z=u;break}else{g=t;p=u;k=n}}c[f>>2]=z;if((z|0)<=-1){y=2296;break}c[f>>2]=c[x+(z<<3)+4>>2]}}while(0);if((y|0)==2296){ca(s|0,81360,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}uu(s);x=c[f>>2]|0;if((x|0)==-2){uf(c[13898]|0,79872,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{A=x;i=b;return A|0}}}while(0);y=is(e)|0;z=c[y>>2]|0;if((z|0)==1){B=+(c[y+8>>2]|0)}else if((z|0)==2){B=+h[y+8>>3]}else if((z|0)==3){B=+uz(c[y+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}y=e|0;if((c[y>>2]|0)==3){uu(c[e+8>>2]|0);c[y>>2]=1}y=~~B;c[f>>2]=y;A=y;i=b;return A|0}function hL(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0.0;e=i;i=i+64|0;f=e|0;g=e+24|0;j=c[13898]|0;L3201:do{if(d){k=c[1054]|0;l=c[k+(j*40&-1)+36>>2]|0;m=c[k+(j*40&-1)+32>>2]|0;n=(a[k+(j*40&-1)|0]&1)==0;L3204:do{if((l|0)>0&(n^1)){o=c[10036]|0;p=0;q=0;r=m;while(1){if((a[p+78616|0]|0)==(a[o+(p+r|0)|0]|0)){s=r;t=q}else{if((p|0)!=6){u=2320;break L3204}s=r-1|0;t=1}w=p+1|0;if((w|0)<(t+l|0)){p=w;q=t;r=s}else{break}}if((t|0)!=0){break}if(!((p|0)==5|(p|0)==10)){u=2320}}else{u=2320}}while(0);if((u|0)==2320){r=c[8272]|0;if((r|0)<=(j|0)|n){x=r;break}q=c[10036]|0;o=0;while(1){if((o|0)>=(l|0)){break}if((a[q+(m+o|0)|0]|0)==(a[o+124464|0]|0)){o=o+1|0}else{x=r;break L3201}}if((o|0)!=2){x=r;break}}m=j+1|0;c[13898]=m;q=c[k+(m*40&-1)+36>>2]|0;L3222:do{if((a[k+(m*40&-1)|0]&1)!=0&(q|0)>0){l=c[10036]|0;n=0;w=0;y=c[k+(m*40&-1)+32>>2]|0;while(1){if((a[n+111056|0]|0)==(a[l+(n+y|0)|0]|0)){z=y;A=w}else{if((n|0)!=3){break L3222}z=y-1|0;A=1}B=n+1|0;if((B|0)<(A+q|0)){n=B;w=A;y=z}else{break}}if((A|0)==0){if(!((n|0)==2|(n|0)==8)){break}}c[b>>2]=-3;c[13898]=(c[13898]|0)+1;i=e;return}}while(0);q=is(f)|0;m=c[q>>2]|0;if((m|0)==3){C=+uz(c[q+8>>2]|0,0)}else if((m|0)==2){C=+h[q+8>>3]}else if((m|0)==1){C=+(c[q+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}q=f|0;if((c[q>>2]|0)==3){uu(c[f+8>>2]|0);c[q>>2]=1}q=~~C;m=43296;while(1){D=c[m>>2]|0;if((D|0)==0){u=2347;break}if((c[D+4>>2]|0)==(q|0)){u=2346;break}else{m=D|0}}if((u|0)==2346){uD(b|0,D+8|0,112);i=e;return}else if((u|0)==2347){c[b>>2]=-1;c[b+4>>2]=0;m=b+8|0;c[m>>2]=c[12872];c[m+4>>2]=c[12873];c[m+8>>2]=c[12874];c[m+12>>2]=c[12875];c[m+16>>2]=c[12876];c[m+20>>2]=c[12877];c[m+24>>2]=c[12878];c[m+28>>2]=c[12879];c[m+32>>2]=c[12880];c[m+36>>2]=c[12881];c[m+40>>2]=c[12882];c[m+44>>2]=c[12883];c[m+48>>2]=c[12884];c[m+52>>2]=c[12885];c[b+64>>2]=1;h[b+72>>3]=0.0;c[b+80>>2]=0;h[b+88>>3]=15.0;h[b+96>>3]=90.0;c[b+104>>2]=0;uh(-1,79128,(v=i,i=i+8|0,c[v>>2]=q,v)|0);i=e;return}}else{x=c[8272]|0}}while(0);L3254:do{if((j|0)<(x|0)){D=b+64|0;f=g+8|0;A=g+4|0;z=g|0;t=g+16|0;s=b+72|0;d=b+80|0;m=g+24|0;k=b+88|0;r=g+32|0;o=b+96|0;y=b+4|0;w=b+8|0;l=b+104|0;p=0;B=0;E=0;F=0;G=0;H=j;I=x;L3256:while(1){J=c[1054]|0;K=(a[J+(H*40&-1)|0]&1)==0;L3258:do{if(!K){L=c[J+(H*40&-1)+36>>2]|0;M=J+(H*40&-1)+32|0;N=c[10036]|0;O=0;while(1){if((O|0)>=(L|0)){break}if((a[N+((c[M>>2]|0)+O|0)|0]|0)==(a[O+103664|0]|0)){O=O+1|0}else{break L3258}}if((O|0)==1){P=G;Q=F;R=E;S=B;T=p;U=H;break L3254}}}while(0);n=(I|0)>(H|0);L3265:do{if(n){do{if(!K){M=c[J+(H*40&-1)+36>>2]|0;N=J+(H*40&-1)+32|0;L=c[10036]|0;V=0;while(1){if((V|0)>=(M|0)){u=2358;break}if((a[L+((c[N>>2]|0)+V|0)|0]|0)==(a[V+150816|0]|0)){V=V+1|0}else{break}}do{if((u|0)==2358){u=0;if((V|0)!=6){break}N=B+1|0;if((B|0)!=0){P=G;Q=F;R=E;S=N;T=p;U=H;break L3254}c[13898]=H+1;c[D>>2]=0;W=G;X=F;Y=E;Z=N;_=p;break L3265}}while(0);if(!n){u=2387;break L3265}if(K){break}V=c[J+(H*40&-1)+36>>2]|0;N=J+(H*40&-1)+32|0;L=c[10036]|0;M=0;while(1){if((M|0)>=(V|0)){u=2367;break}if((a[L+((c[N>>2]|0)+M|0)|0]|0)==(a[M+148912|0]|0)){M=M+1|0}else{break}}do{if((u|0)==2367){u=0;if((M|0)!=4){break}N=B+1|0;if((B|0)!=0){P=G;Q=F;R=E;S=N;T=p;U=H;break L3254}c[13898]=H+1;c[D>>2]=1;W=G;X=F;Y=E;Z=N;_=p;break L3265}}while(0);if(!n){u=2387;break L3265}if(K){break}M=c[J+(H*40&-1)+36>>2]|0;N=J+(H*40&-1)+32|0;L=c[10036]|0;V=0;while(1){if((V|0)>=(M|0)){u=2375;break}if((a[L+((c[N>>2]|0)+V|0)|0]|0)==(a[V+146928|0]|0)){V=V+1|0}else{break}}do{if((u|0)==2375){u=0;if((V|0)!=8){break}N=B+1|0;if((B|0)!=0){P=G;Q=F;R=E;S=N;T=p;U=H;break L3254}c[13898]=H+1;c[D>>2]=2;W=G;X=F;Y=E;Z=N;_=p;break L3265}}while(0);if(!n){u=2387;break L3265}if(K){break}V=c[J+(H*40&-1)+36>>2]|0;N=J+(H*40&-1)+32|0;L=c[10036]|0;M=0;while(1){if((M|0)>=(V|0)){break}if((a[L+((c[N>>2]|0)+M|0)|0]|0)==(a[M+144768|0]|0)){M=M+1|0}else{u=2387;break L3265}}if((M|0)!=5){u=2387;break L3265}N=B+1|0;if((B|0)!=0){P=G;Q=F;R=E;S=N;T=p;U=H;break L3254}c[13898]=H+1;c[D>>2]=3;W=G;X=F;Y=E;Z=N;_=p;break L3265}}while(0);$=c[J+(H*40&-1)+36>>2]|0;aa=c[J+(H*40&-1)+32>>2]|0;u=2397}else{u=2387}}while(0);L3306:do{if((u|0)==2387){u=0;O=c[J+(H*40&-1)+36>>2]|0;N=c[J+(H*40&-1)+32>>2]|0;if(!((O|0)>0&(K^1))){ab=O;ac=N;u=2415;break}L=c[10036]|0;V=0;ad=0;ae=N;while(1){if((a[V+75200|0]|0)==(a[L+(V+ae|0)|0]|0)){af=ae;ag=ad}else{if((V|0)!=4){$=O;aa=N;u=2397;break L3306}af=ae-1|0;ag=1}ah=V+1|0;if((ah|0)<(ag+O|0)){V=ah;ad=ag;ae=af}else{break}}if((ag|0)==0){if(!((V|0)==3|(V|0)==6)){$=O;aa=N;u=2397;break}}ae=F+1|0;if((F|0)!=0){P=G;Q=ae;R=E;S=B;T=p;U=H;break L3254}c[13898]=H+1;c[l>>2]=2;W=G;X=ae;Y=E;Z=B;_=p}}while(0);L3320:do{if((u|0)==2397){u=0;if(K){ab=$;ac=aa;u=2415;break}L3323:do{if(($|0)>0){ae=c[10036]|0;ad=0;do{if((a[ad+127904|0]|0)!=(a[ae+(ad+aa|0)|0]|0)){break L3323}ad=ad+1|0;}while((ad|0)<($|0));if((ad|0)!=5){break}ae=F+1|0;if((F|0)!=0){P=G;Q=ae;R=E;S=B;T=p;U=H;break L3254}c[13898]=H+1;c[l>>2]=1;W=G;X=ae;Y=E;Z=B;_=p;break L3320}}while(0);if(!(($|0)>0&(K^1))){ab=$;ac=aa;u=2415;break}N=c[10036]|0;O=0;V=0;ae=aa;while(1){if((a[O+74368|0]|0)==(a[N+(O+ae|0)|0]|0)){ai=ae;aj=V}else{if((O|0)!=6){ab=$;ac=aa;u=2415;break L3320}ai=ae-1|0;aj=1}M=O+1|0;if((M|0)<(aj+$|0)){O=M;V=aj;ae=ai}else{break}}if((aj|0)==0){if(!((O|0)==5|(O|0)==8)){ab=$;ac=aa;u=2415;break}}ae=F+1|0;if((F|0)!=0){P=G;Q=ae;R=E;S=B;T=p;U=H;break L3254}c[13898]=H+1;c[l>>2]=0;W=G;X=ae;Y=E;Z=B;_=p}}while(0);L3344:do{if((u|0)==2415){u=0;L3346:do{if(!(K|n^1)){ae=c[10036]|0;V=0;while(1){if((V|0)>=(ab|0)){u=2419;break}if((a[ae+(ac+V|0)|0]|0)==(a[V+130496|0]|0)){V=V+1|0}else{break}}do{if((u|0)==2419){u=0;if((V|0)!=4){break}ad=E+1|0;if((E|0)!=0){P=G;Q=F;R=ad;S=B;T=p;U=H;break L3254}c[f>>2]=0;c[A>>2]=0;c[z>>2]=0;ak=H+1|0;c[13898]=ak;if((ak|0)>=(I|0)){u=2454;break L3256}L3356:do{if((a[J+(ak*40&-1)|0]&1)!=0){N=c[J+(ak*40&-1)+36>>2]|0;M=J+(ak*40&-1)+32|0;L=0;while(1){if((L|0)>=(N|0)){break}if((a[ae+((c[M>>2]|0)+L|0)|0]|0)==(a[L+103664|0]|0)){L=L+1|0}else{break L3356}}if((L|0)==1){u=2453;break L3256}}}while(0);dl(g,0);h[s>>3]=+h[t>>3];c[d>>2]=c[z>>2];C=+h[m>>3];h[k>>3]=C;al=+h[r>>3];h[o>>3]=al;if(al>C){W=G;X=F;Y=ad;Z=B;_=p;break L3344}h[o>>3]=90.0;W=G;X=F;Y=ad;Z=B;_=p;break L3344}}while(0);if(K|n^1){break}ae=c[10036]|0;V=0;while(1){if((V|0)>=(ab|0)){u=2434;break}if((a[ae+(ac+V|0)|0]|0)==(a[V+150704|0]|0)){V=V+1|0}else{break}}do{if((u|0)==2434){u=0;if((V|0)!=4){break}ae=G+1|0;if((G|0)!=0){P=ae;Q=F;R=E;S=B;T=p;U=H;break L3254}c[13898]=H+1;c[y>>2]=0;W=ae;X=F;Y=E;Z=B;_=p;break L3344}}while(0);if(K|n^1){break}V=c[10036]|0;ae=0;while(1){if((ae|0)>=(ab|0)){break}if((a[V+(ac+ae|0)|0]|0)==(a[ae+150856|0]|0)){ae=ae+1|0}else{break L3346}}if((ae|0)!=5){break}V=G+1|0;if((G|0)!=0){P=V;Q=F;R=E;S=B;T=p;U=H;break L3254}c[13898]=H+1;c[y>>2]=1;W=V;X=F;Y=E;Z=B;_=p;break L3344}}while(0);hH(w,1,0);O=c[13898]|0;if((H|0)==(O|0)){P=G;Q=F;R=E;S=B;T=p;U=H;break L3254}V=p+1|0;if((p|0)==0){W=G;X=F;Y=E;Z=B;_=V}else{P=G;Q=F;R=E;S=B;T=V;U=O;break L3254}}}while(0);n=c[13898]|0;K=c[8272]|0;if((n|0)<(K|0)){p=_;B=Z;E=Y;F=X;G=W;H=n;I=K}else{P=W;Q=X;R=Y;S=Z;T=_;U=n;break L3254}}if((u|0)==2453){uf(ak,73632,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==2454){uf(ak,73632,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{P=0;Q=0;R=0;S=0;T=0;U=j}}while(0);if((P|0)>1|(T|0)>1|(S|0)>1|(R|0)>1|(Q|0)>1){uf(U,98720,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{i=e;return}}function hM(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,x=0,y=0,z=0,A=0.0,B=0.0,C=0.0,D=0.0,E=0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Z=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0.0,af=0,ag=0.0,ah=0,ai=0.0,aj=0.0;b=i;i=i+1032|0;d=b|0;e=b+48|0;f=b+96|0;j=b+144|0;k=b+192|0;l=b+448|0;m=b+520|0;n=b+776|0;o=m|0;p=n|0;if((a[14080]&1)==0){i=b;return}L3395:do{if((c[8496]|0)==0){a[o]=0}else{do{if((a[37384]&1)!=0){if(+P(+(+aX(+(+g[3536]),90.0)))<.1){if(+P(+(+aX(+(+g[3538]),180.0)))<.1){break}}a[p]=0;q=n+(uA(p|0)|0)|0;a[q]=a[147088]|0;a[q+1|0]=a[147089|0]|0;a[q+2|0]=a[147090|0]|0;a[q+3|0]=a[147091|0]|0;a[q+4|0]=a[147092|0]|0;a[q+5|0]=a[147093|0]|0;a[q+6|0]=a[147094|0]|0;q=c[8503]|0;uC(p|0,q|0);q=n+(uA(p|0)|0)|0;a[q]=a[86120]|0;a[q+1|0]=a[86121|0]|0;a[q+2|0]=a[86122|0]|0;q=c[8503]|0;uC(p|0,q|0);uD(n+(uA(p|0)|0)|0,146840,11);q=c[8503]|0;uC(p|0,q|0);q=n+(uA(p|0)|0)|0;a[q]=a[86120]|0;a[q+1|0]=a[86121|0]|0;a[q+2|0]=a[86122|0]|0;q=c[8503]|0;uC(p|0,q|0);r=+g[3538];s=+g[3536];t=+g[3534];u=+g[3532];be(o|0,p|0,(v=i,i=i+32|0,h[v>>3]=r,h[v+8>>3]=s,h[v+16>>3]=t,h[v+24>>3]=u,v)|0);break L3395}}while(0);do{if((c[17261]&3|0)==0){if((c[17089]&3|0)!=0){break}q=iq(o,+h[3470],+h[3468],c[8506]|0,c[8510]|0)|0;if((a[26192]&1)==0){break L3395}u=+h[3470];t=+h[3468];x=k|0;y=l|0;uD(x|0,236632,255);z=c[8503]|0;uC(x|0,z|0);z=k+(uA(x|0)|0)|0;a[z]=a[86120]|0;a[z+1|0]=a[86121|0]|0;a[z+2|0]=a[86122|0]|0;z=c[8503]|0;uC(x|0,z|0);uD(k+(uA(x|0)|0)|0,145e3,14);z=c[8503]|0;uC(x|0,z|0);z=k+(uA(x|0)|0)|0;a[z]=a[86120]|0;a[z+1|0]=a[86121|0]|0;a[z+2|0]=a[86122|0]|0;z=c[8503]|0;uC(x|0,z|0);s=+h[3275];do{if((a[66164]&1)==0){A=u-s}else{if(s==0.0){A=99999.0;break}A=u/s}}while(0);r=+h[3276];do{if((a[65476]&1)==0){B=t-r}else{if(r==0.0){B=99999.0;break}B=t/r}}while(0);be(q|0,x|0,(v=i,i=i+32|0,h[v>>3]=s,h[v+8>>3]=r,h[v+16>>3]=A,h[v+24>>3]=B,v)|0);if((c[8500]|0)==0){break L3395}z=(a[66164]&1)==0;if(z){C=u}else{D=+_(+u);C=D/+h[8272]}E=(a[65476]&1)==0;if(E){F=t}else{D=+_(+t);F=D/+h[8186]}D=+h[3275];if(z){G=D}else{H=+_(+D);G=H/+h[8272]}H=+h[3276];if(E){I=H}else{D=+_(+H);I=D/+h[8186]}a[x]=0;E=k+(uA(x|0)|0)|0;a[E]=a[126656]|0;a[E+1|0]=a[126657|0]|0;a[E+2|0]=a[126658|0]|0;E=c[8503]|0;uC(x|0,E|0);D=C-G;H=F-I;J=+Q(+(D*D+H*H));if((c[8500]|0)==1){K=+Y(+H,+D)*57.29577951308232;uD(k+(uA(x|0)|0)|0,144496,13);L=K}else{if(D==0.0){M=H>0.0?8.988465674311579e+307:-8.988465674311579e+307}else{M=H/D}E=k+(uA(x|0)|0)|0;z=c[8503]|0;be(E|0,144344,(v=i,i=i+8|0,c[v>>2]=z,v)|0);L=M}be(y|0,x|0,(v=i,i=i+16|0,h[v>>3]=J,h[v+8>>3]=L,v)|0);uC(q|0,y|0);break L3395}}while(0);if((c[16573]&3|0)==0){N=o}else{z=m+2|0;a[o]=a[146632]|0;a[o+1|0]=a[146633|0]|0;a[o+2|0]=a[146634|0]|0;J=+h[3470];if(J<8.988465674311579e+307){if((c[16546]|0)==1){E=c9(2)|0;R=a8(E|0,10)|0;if((R|0)!=0){S=R;do{a[S]=32;S=a8(E|0,10)|0;}while((S|0)!=0)}D=+h[3470];t3(j,D);H=D- +O(+D);t4(z,40,E,j,H)}else{S=c[8503]|0;be(z|0,S|0,(v=i,i=i+8|0,h[v>>3]=J,v)|0)}T=m+((uA(z|0)|0)+2|0)|0}else{T=z}a[T]=32;N=T+1|0}if((c[16401]&3|0)==0){U=N}else{S=N+2|0;a[N]=a[146472]|0;a[N+1|0]=a[146473|0]|0;a[N+2|0]=a[146474|0]|0;H=+h[3468];if(H<8.988465674311579e+307){if((c[16374]|0)==1){R=c9(1)|0;V=a8(R|0,10)|0;if((V|0)!=0){W=V;do{a[W]=32;W=a8(R|0,10)|0;}while((W|0)!=0)}J=+h[3468];t3(f,J);D=J- +O(+J);t4(S,40,R,f,D)}else{W=c[8503]|0;be(S|0,W|0,(v=i,i=i+8|0,h[v>>3]=H,v)|0)}X=N+((uA(S|0)|0)+2|0)|0}else{X=S}a[X]=32;U=X+1|0}if((c[17261]&3|0)==0){Z=U}else{W=U+3|0;z=U;w=4010616;a[z]=w&255;w=w>>8;a[z+1|0]=w&255;w=w>>8;a[z+2|0]=w&255;w=w>>8;a[z+3|0]=w&255;D=+h[3469];if(D<8.988465674311579e+307){if((c[17234]|0)==1){z=c9(6)|0;E=a8(z|0,10)|0;if((E|0)!=0){V=E;do{a[V]=32;V=a8(z|0,10)|0;}while((V|0)!=0)}H=+h[3469];t3(e,H);J=H- +O(+H);t4(W,40,z,e,J)}else{V=c[8503]|0;be(W|0,V|0,(v=i,i=i+8|0,h[v>>3]=D,v)|0)}$=U+((uA(W|0)|0)+3|0)|0}else{$=W}a[$]=32;Z=$+1|0}if((c[17089]&3|0)==0){aa=Z}else{V=Z+3|0;S=Z;w=4010617;a[S]=w&255;w=w>>8;a[S+1|0]=w&255;w=w>>8;a[S+2|0]=w&255;w=w>>8;a[S+3|0]=w&255;J=+h[3467];if(J<8.988465674311579e+307){if((c[17062]|0)==1){S=c9(5)|0;R=a8(S|0,10)|0;if((R|0)!=0){E=R;do{a[E]=32;E=a8(S|0,10)|0;}while((E|0)!=0)}D=+h[3467];t3(d,D);H=D- +O(+D);t4(V,40,S,d,H)}else{E=c[8503]|0;be(V|0,E|0,(v=i,i=i+8|0,h[v>>3]=J,v)|0)}ab=Z+((uA(V|0)|0)+3|0)|0}else{ab=V}a[ab]=32;aa=ab+1|0}do{if((a[26192]&1)==0){ac=aa}else{if((c[16573]&3|0)==0){ad=aa}else{E=aa;w=4028516;a[E]=w&255;w=w>>8;a[E+1|0]=w&255;w=w>>8;a[E+2|0]=w&255;w=w>>8;a[E+3|0]=w&255;E=aa+3|0;W=c[8503]|0;do{if((a[66164]&1)==0){ae=+h[3470]- +h[3275]}else{H=+h[3275];if(H==0.0){ae=99999.0;break}ae=+h[3470]/H}}while(0);be(E|0,W|0,(v=i,i=i+8|0,h[v>>3]=ae,v)|0);ad=aa+(uA(aa|0)|0)|0}if((c[16401]&3|0)==0){af=ad}else{y=ad;w=4028772;a[y]=w&255;w=w>>8;a[y+1|0]=w&255;w=w>>8;a[y+2|0]=w&255;w=w>>8;a[y+3|0]=w&255;y=ad+3|0;q=c[8503]|0;do{if((a[65476]&1)==0){ag=+h[3468]- +h[3276]}else{t=+h[3276];if(t==0.0){ag=99999.0;break}ag=+h[3468]/t}}while(0);be(y|0,q|0,(v=i,i=i+8|0,h[v>>3]=ag,v)|0);af=ad+(uA(ad|0)|0)|0}if((c[17261]&3|0)==0){ah=af}else{a[af]=a[145552]|0;a[af+1|0]=a[145553|0]|0;a[af+2|0]=a[145554|0]|0;a[af+3|0]=a[145555|0]|0;a[af+4|0]=a[145556|0]|0;W=af+4|0;E=c[8503]|0;do{if((a[68916]&1)==0){ai=+h[3469]- +h[3277]}else{t=+h[3277];if(t==0.0){ai=99999.0;break}ai=+h[3469]/t}}while(0);be(W|0,E|0,(v=i,i=i+8|0,h[v>>3]=ai,v)|0);ah=af+(uA(af|0)|0)|0}if((c[17089]&3|0)==0){ac=ah;break}a[ah]=a[145256]|0;a[ah+1|0]=a[145257|0]|0;a[ah+2|0]=a[145258|0]|0;a[ah+3|0]=a[145259|0]|0;a[ah+4|0]=a[145260|0]|0;q=ah+4|0;y=c[8503]|0;do{if((a[68228]&1)==0){aj=+h[3467]- +h[3278]}else{t=+h[3278];if(t==0.0){aj=99999.0;break}aj=+h[3467]/t}}while(0);be(q|0,y|0,(v=i,i=i+8|0,h[v>>3]=aj,v)|0);ac=ah+(uA(ah|0)|0)|0}}while(0);a[ac-1|0]=0}}while(0);ac=c[(c[3524]|0)+120>>2]|0;if((ac|0)==0){i=b;return}cN[ac&255](0,o);i=b;return}function hN(b,d,e,f,g,i){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;i=i|0;var j=0,k=0.0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0.0,s=0.0;j=(a[37384]&1)==0;do{if(j){if((c[7939]|0)==(c[7938]|0)){h[g>>3]=8.988465674311579e+307;h[e>>3]=8.988465674311579e+307}else{k=+(b|0);h[e>>3]=(k- +(c[16536]|0))/+h[8269]+ +h[8255];h[g>>3]=(k- +(c[17224]|0))/+h[8613]+ +h[8599]}if((c[7941]|0)==(c[7940]|0)){h[i>>3]=8.988465674311579e+307;h[f>>3]=8.988465674311579e+307;break}else{k=+(d|0);h[f>>3]=(k- +(c[16364]|0))/+h[8183]+ +h[8169];h[i>>3]=(k- +(c[17052]|0))/+h[8527]+ +h[8513];break}}else{l=b-(c[18064]|0)|0;m=d-(c[18062]|0)|0;n=c[18060]|0;o=c[18058]|0;do{if((((n|0)>-1?n:-n|0)|0)>(((o|0)>-1?o:-o|0)|0)){k=+h[8255];h[e>>3]=k+ +(l|0)/+(n|0)*(+h[8256]-k)}else{if((o|0)==0){h[e>>3]=8.988465674311579e+307;break}else{k=+h[8255];h[e>>3]=k+ +(m|0)/+(o|0)*(+h[8256]-k);break}}}while(0);o=c[18056]|0;n=c[18054]|0;do{if((((o|0)>-1?o:-o|0)|0)>(((n|0)>-1?n:-n|0)|0)){k=+h[8169];h[f>>3]=k+ +(l|0)/+(o|0)*(+h[8170]-k)}else{if((n|0)==0){h[f>>3]=8.988465674311579e+307;break}else{k=+h[8169];h[f>>3]=k+ +(m|0)/+(n|0)*(+h[8170]-k);break}}}while(0);h[i>>3]=8.988465674311579e+307;h[g>>3]=8.988465674311579e+307}}while(0);k=+h[e>>3];if((a[66164]&1)==0){p=k}else{p=+Z(+(k*+h[8272]))}h[e>>3]=p;p=+h[f>>3];if((a[65476]&1)==0){q=p}else{q=+Z(+(p*+h[8186]))}h[f>>3]=q;if(!j){return}q=+h[g>>3];if((a[68916]&1)==0){r=q}else{r=+Z(+(q*+h[8616]))}h[g>>3]=r;r=+h[i>>3];if((a[68228]&1)==0){s=r}else{s=+Z(+(r*+h[8530]))}h[i>>3]=s;return}function hO(b){b=b|0;var d=0,e=0;c[8512]=0;c[14056]=0;do{if((b|0)!=0){if(!(a[21856]|0)){break}d=c[(c[3524]|0)+128>>2]|0;if((d|0)!=0){cR[d&127](0,0,0)}a[21856]=0;if((c[8501]|0)==0){break}d=c[m>>2]|0;aI(147352,19,1,d|0)}}while(0);d=c[3524]|0;do{if((d|0)!=0){e=c[d+128>>2]|0;if((e|0)==0){break}cR[e&127](0,0,0);if((c[8498]|0)==0){break}e=c[(c[3524]|0)+120>>2]|0;if((e|0)==0){break}cN[e&255](1,179864);cN[c[(c[3524]|0)+120>>2]&255](2,179864)}}while(0);do{if((c[8026]|0)!=0){c[8026]=0;d=c[3524]|0;if((d|0)==0){break}e=c[d>>2]|0;if((a_(222936,e|0,3)|0)!=0){if((a_(163e3,e|0,3)|0)!=0){break}}e=c[o>>2]|0;b6(10,e|0)}}while(0);if((b|0)==1){return}c[b+12>>2]=1062;c[b+16>>2]=0;hQ(b,1);return}function hP(b){b=b|0;var d=0;if((b|0)==0){d=147656;return d|0}if(!(a[21856]|0)){d=0;return d|0}b=c[(c[3524]|0)+128>>2]|0;if((b|0)!=0){cR[b&127](0,0,0)}a[21856]=0;if((c[8501]|0)==0){d=0;return d|0}aI(147352,19,1,c[m>>2]|0);d=0;return d|0}function hQ(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;e=i;f=c[b+12>>2]|0;g=c[b+16>>2]|0;h=c[b+4>>2]|0;j=c[b+8>>2]|0;if((c[14128]|0)==0){hS()}k=c[8512]|0;do{if((k&1|0)==0){l=f;n=k&255}else{if((f&255|0)!=0){l=f;n=k&255;break}o=bC(f|0)|0;l=o;n=c[8512]&255}}while(0);if(!((l|0)<1|(c[8026]&8|0)==0|d^1)){ir(+(h|0),+(j|0),0,l);i=e;return}f=c[14128]|0;if((f|0)==0){i=e;return}k=(g&1|0)==0;g=b+20|0;o=f;L3631:while(1){do{if((l|0)==(c[o+4>>2]|0)){if(((a[o+8|0]^n)&6)!=0){break}f=e6(147896)|0;if((f|0)!=0){a[f+8|0]=0;p=c[g>>2]|0;c[f+16>>2]=1;c[f+24>>2]=p}if((a[o+20|0]&1)!=0){q=o+12|0;if((c[q>>2]|0)!=0){r=2635;break L3631}}if(!d){r=2655;break L3631}if(k){s=c[o+12>>2]|0;if((s|0)!=0){r=2644;break L3631}}p=c[o+16>>2]|0;if((p|0)==0){f=c[m>>2]|0;cf(f|0,123008,(v=i,i=i+16|0,c[v>>2]=105800,c[v+8>>2]=1370,v)|0);break}else{cO[p&255](b);break}}}while(0);p=c[o+24>>2]|0;if((p|0)==0){r=2652;break}else{o=p}}if((r|0)==2635){if(d){ir(+(h|0),+(j|0),0,l)}else{ir(0.0,0.0,0,l)}l=c[q>>2]|0;if((l|0)==0){t=0}else{t=bP(l|0)|0}dL(t);c[b>>2]=3;i=e;return}else if((r|0)==2655){i=e;return}else if((r|0)==2644){dL(bP(s|0)|0);i=e;return}else if((r|0)==2652){i=e;return}}function hR(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;i=i+32|0;g=f|0;if((c[14128]|0)==0){hS()}do{if((d|0)==0){hT(b)}else{hV(b,d,0);if(!e){break}if((hZ(g,b)|0)==0){break}h=c[14128]|0;if((h|0)==0){break}j=c[g+4>>2]|0;k=a[g+8|0]|0;l=h;do{do{if((j|0)==(c[l+4>>2]|0)){if(((a[l+8|0]^k)&6)!=0){break}a[l+20|0]=1}}while(0);l=c[l+24>>2]|0;}while((l|0)!=0)}}while(0);if((b|0)==0){i=f;return}uu(b);i=f;return}function hS(){var a=0,b=0,d=0;a=c[14128]|0;if((a|0)!=0){b=a;while(1){a=b+12|0;d=c[a>>2]|0;if((d|0)!=0){uu(d);c[a>>2]=0}a=c[b+24>>2]|0;uu(b);if((a|0)==0){break}else{b=a}}}c[14128]=0;hV(205056,0,182);hV(204680,0,172);hV(204424,0,14);hV(204128,0,120);hV(203920,0,40);hV(203672,0,86);hV(203432,0,104);hV(164840,0,156);hV(193632,0,122);hV(170288,0,66);hV(170224,0,100);hV(40160,0,32);hV(170216,0,202);hV(170208,0,92);hV(174448,0,44);hV(169624,0,78);hV(198400,0,196);hV(197952,0,206);hV(122560,0,150);hV(164216,0,46);hV(151360,0,134);hV(178184,0,174);hV(147552,0,24);hV(83696,0,74);return}function hT(b){b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;i=i+48|0;e=d|0;if((c[14128]|0)==0){hS()}if((b|0)==0){f=d+32|0;uD(f|0,56584,12);g=c[m>>2]|0;aF(10,g|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=79120,c[v+8>>2]=78520,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=78176,c[v+8>>2]=77480,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=179864,c[v+8>>2]=77024,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=76336,c[v+8>>2]=75728,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=75192,c[v+8>>2]=74816,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=74352,c[v+8>>2]=73928,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=73616,c[v+8>>2]=72992,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=225216,c[v+8>>2]=224776,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=223768,c[v+8>>2]=223184,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=222680,c[v+8>>2]=221840,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=221376,c[v+8>>2]=220792,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=219032,c[v+8>>2]=218520,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=217968,c[v+8>>2]=217472,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=216736,c[v+8>>2]=216288,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=215744,c[v+8>>2]=215256,v)|0);g=c[m>>2]|0;cf(g|0,f|0,(v=i,i=i+16|0,c[v>>2]=214784,c[v+8>>2]=214224,v)|0);f=c[m>>2]|0;aF(10,f|0);f=c[m>>2]|0;cf(f|0,213488,(v=i,i=i+16|0,c[v>>2]=212928,c[v+8>>2]=212352,v)|0);f=c[m>>2]|0;cf(f|0,211856,(v=i,i=i+16|0,c[v>>2]=211392,c[v+8>>2]=210904,v)|0);f=c[m>>2]|0;aF(10,f|0);f=c[14128]|0;if((f|0)!=0){g=f;do{h$(g);g=c[g+24>>2]|0;}while((g|0)!=0)}g=c[m>>2]|0;aF(10,g|0);g=c[m>>2]|0;aI(210240,67,1,g|0);g=c[m>>2]|0;aF(10,g|0);i=d;return}if((hZ(e,b)|0)==0){i=d;return}b=c[14128]|0;if((b|0)==0){i=d;return}g=c[e+4>>2]|0;f=a[e+8|0]|0;e=b;while(1){if((g|0)==(c[e+4>>2]|0)){if(((a[e+8|0]^f)&6)==0){break}}b=c[e+24>>2]|0;if((b|0)==0){h=2693;break}else{e=b}}if((h|0)==2693){i=d;return}h$(e);i=d;return}function hU(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0;f=i;if(!((b|0)!=0&(c[6928]|0)!=0)){dP();i=f;return}g=c[200]|0;do{if((a[64788+(g*688&-1)|0]&1)==0){j=2704}else{k=c[64652+(g*688&-1)>>2]|0;if((k&1|0)==0){if(+h[64680+(g*688&-1)>>3]<=0.0){break}}if((k&2|0)!=0){j=2704;break}if(+h[64688+(g*688&-1)>>3]>0.0){j=2704}}}while(0);L3731:do{if((j|0)==2704){g=c[144]|0;do{if((a[64788+(g*688&-1)|0]&1)!=0){k=c[64652+(g*688&-1)>>2]|0;if((k&1|0)==0){if(+h[64680+(g*688&-1)>>3]<=0.0){break L3731}}if((k&2|0)!=0){break}if(+h[64688+(g*688&-1)>>3]<=0.0){break L3731}}}while(0);g=c[34]|0;do{if((a[64788+(g*688&-1)|0]&1)!=0){k=c[64652+(g*688&-1)>>2]|0;if((k&1|0)==0){if(+h[64680+(g*688&-1)>>3]<=0.0){break L3731}}if((k&2|0)!=0){break}if(+h[64688+(g*688&-1)>>3]<=0.0){break L3731}}}while(0);if((a[66852]&1)!=0){g=c[16679]|0;if(!((g&1|0)!=0|+h[8343]>0.0)){break}if(!((g&2|0)!=0|+h[8344]>0.0)){break}}fv(b,d,e);i=f;return}}while(0);uk(164960,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function hV(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;f=i;g=ut(28)|0;do{if((g|0)==0){gk();h=ut(28)|0;if((h|0)!=0){j=h;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=81336,v)|0)}else{j=g}}while(0);g=j;if((hZ(g,b)|0)==0){uu(j);i=f;return}b=c[14128]|0;do{if((b|0)==0){c[14128]=g;k=g}else{h=c[j+4>>2]|0;l=j+8|0;m=b;while(1){if((h|0)==(c[m+4>>2]|0)){if(((a[m+8|0]^a[l])&6)==0){break}}n=c[m+24>>2]|0;if((n|0)==0){o=2741;break}else{m=n}}if((o|0)==2741){c[(c[b>>2]|0)+24>>2]=g;c[j>>2]=c[c[14128]>>2];k=c[14128]|0;break}do{if((d|0)==0){c[m+16>>2]=e}else{if((a[d]|0)==0){h_(m);break}l=m+12|0;h=c[l>>2]|0;if((h|0)!=0){uu(h);c[l>>2]=0}c[l>>2]=d}}while(0);uu(j);i=f;return}}while(0);c[k>>2]=g;c[j+24>>2]=0;a[j+20|0]=0;if((d|0)==0){c[j+16>>2]=e;i=f;return}if((a[d]|0)==0){h_(g);i=f;return}else{c[j+12>>2]=d;i=f;return}}function hW(){var b=0,d=0,e=0,f=0.0,g=0,j=0.0,k=0.0,l=0,m=0.0,n=0,o=0,p=0.0;b=i;i=i+8|0;d=b|0;e=c[(c[3524]|0)+124>>2]|0;if((e|0)==0){i=b;return}if((a[26192]&1)==0){i=b;return}cN[e&255](-1,-1);if((a[37384]&1)==0){e=(a[66164]&1)!=0;f=+h[3275];if(e&f<0.0){g=-1}else{if(e){j=+_(+f);k=j/+h[8272]}else{k=f}g=~~(+(c[16536]|0)+(k- +h[8255])*+h[8269]+.5)}c[6558]=g;e=(a[65476]&1)!=0;k=+h[3276];if(e&k<0.0){l=-1}else{if(e){f=+_(+k);m=f/+h[8186]}else{m=k}l=~~(+(c[16364]|0)+(m- +h[8169])*+h[8183]+.5)}c[6559]=l;hN(g,l,d,d,26216,26224);n=c[6558]|0;o=c[6559]|0}else{h[d>>3]=1.0;m=(+h[3275]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;k=(+h[3276]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;f=(1.0- +h[5279])*+h[2]+ +h[12]+-1.0;j=+h[406]+m*+h[394]+k*+h[398]+f*+h[402];p=j==0.0?1.0e-5:j;d=~~((+h[403]+m*+h[391]+k*+h[395]+f*+h[399])/p*+(c[180]|0))+(c[186]|0)|0;l=~~((+h[404]+m*+h[392]+k*+h[396]+f*+h[400])/p*+(c[40]|0))+(c[46]|0)|0;c[6558]=d;c[6559]=l;n=d;o=l}cN[c[(c[3524]|0)+124>>2]&255](n,o);i=b;return}function hX(b){b=b|0;var d=0,e=0,f=0,i=0,j=0,k=0.0;if((b|0)==0){d=76768;return d|0}e=c[3524]|0;f=e+124|0;if((c[f>>2]|0)==0){d=0;return d|0}do{if((a[26192]&1)==0){if((a[37384]&1)!=0){if(+P(+(+aX(+(+g[3536]),90.0)))>=.1){break}if(+P(+(+aX(+(+g[3538]),180.0)))>=.1){break}}a[26192]=1;i=c[b+4>>2]|0;c[6558]=i;j=c[b+8>>2]|0;c[6559]=j;hN(i,j,26200,26208,26216,26224);cN[c[(c[3524]|0)+124>>2]&255](c[6558]|0,c[6559]|0);j=e6(98488)|0;if((j|0)!=0){a[j+8|0]=0;k=+h[3275];c[j+16>>2]=2;h[j+24>>3]=k;h[j+32>>3]=0.0}j=e6(92688)|0;if((j|0)!=0){a[j+8|0]=0;k=+h[3276];c[j+16>>2]=2;h[j+24>>3]=k;h[j+32>>3]=0.0}if((c[8501]|0)==0){break}j=c[m>>2]|0;aI(224616,18,1,j|0)}else{a[26192]=0;do{if((e|0)!=0){j=c[f>>2]|0;if((j|0)==0){break}cN[j&255](-1,-1)}}while(0);j=e6(98488)|0;if((j|0)!=0){a[j+8|0]=1}j=e6(92688)|0;if((j|0)!=0){a[j+8|0]=1}if((c[8501]|0)==0){break}j=c[m>>2]|0;aI(84792,19,1,j|0);if((c[8501]|0)==0){break}j=c[m>>2]|0;aI(84792,19,1,j|0)}}while(0);hM();d=0;return d|0}function hY(b){b=b|0;var d=0,e=0,f=0,g=0;d=c[7772]|0;if((b-1|0)>>>0>=2){e=d;return e|0}do{if((d|0)!=(b|0)){if((a[26192]&1)==0){break}a[26192]=0;f=c[3524]|0;do{if((f|0)!=0){g=c[f+124>>2]|0;if((g|0)==0){break}cN[g&255](-1,-1)}}while(0);f=e6(98488)|0;if((f|0)!=0){a[f+8|0]=1}f=e6(92688)|0;if((f|0)!=0){a[f+8|0]=1}if((c[8501]|0)==0){break}f=c[m>>2]|0;aI(84792,19,1,f|0)}}while(0);c[7772]=b;e=b;return e|0}function hZ(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0;e=i;f=b+4|0;c[f>>2]=-1;g=b+8|0;a[g]=0;c[b+12>>2]=0;c[b+16>>2]=0;c[b>>2]=0;c[b+24>>2]=0;if((d|0)==0){h=0;i=e;return h|0}else{j=d;k=-1;l=0}L3876:while(1){b=a[j]|0;if(b<<24>>24==0){n=k;o=2836;break}L3879:do{if((uJ(j|0,217264,4)|0)==0){p=l|4;a[g]=p;q=j+4|0;r=k;s=p}else{if((uJ(j|0,211672,5)|0)==0){p=l|2;a[g]=p;q=j+5|0;r=k;s=p;break}L3884:do{if((aY(j|0,87296)|0)==0){t=0;o=2824}else{p=(a_(j|0,87296,9)|0)!=0;if((aY(j|0,86224)|0)==0){t=1;o=2824;break}u=(a_(j|0,86224,3)|0)==0&p?1:p<<31>>31;if((aY(j|0,85184)|0)==0){t=2;o=2824;break}p=(a_(j|0,85184,8)|0)==0&(u|0)<0?2:u;if((aY(j|0,84288)|0)==0){t=3;o=2824;break}u=(a_(j|0,84288,6)|0)==0&(p|0)<0?3:p;if((aY(j|0,83696)|0)==0){t=4;o=2824;break}p=(a_(j|0,83696,6)|0)==0&(u|0)<0?4:u;if((aY(j|0,82728)|0)==0){t=5;o=2824;break}u=(a_(j|0,82728,6)|0)==0&(p|0)<0?5:p;if((u|0)>-1){t=u;o=2824;break}else{w=20384}while(1){u=c[w>>2]|0;if((u|0)==0){break L3884}if((aY(j|0,u|0)|0)==0){break}else{w=w+4|0}}x=(w-20384>>2)+1e3|0;y=uA(j|0)|0;o=2832}}while(0);if((o|0)==2824){o=0;u=uA(c[960+(t<<3)>>2]|0)|0;x=c[964+(t<<3)>>2]|0;y=u;o=2832}do{if((o|0)==2832){o=0;if((x|0)==-1){break}c[f>>2]=x;q=j+y|0;r=x;s=l;break L3879}}while(0);u=j+1|0;p=b<<24>>24;c[f>>2]=p;z=a[u]|0;if((z<<24>>24|0)==0|(z<<24>>24|0)==45){q=u;r=p;s=l}else{o=2835;break L3876}}}while(0);if((q|0)==0){n=r;o=2836;break}else{j=q;k=r;l=s}}if((o|0)==2836){h=(n|0)!=-1&1;i=e;return h|0}else if((o|0)==2835){cf(c[m>>2]|0,205504,(v=i,i=i+8|0,c[v>>2]=d,v)|0);h=0;i=e;return h|0}return 0}function h_(a){a=a|0;var b=0,d=0,e=0,f=0;if((a|0)==0){return}if((c[a+16>>2]|0)!=0){b=a+12|0;d=c[b>>2]|0;if((d|0)==0){return}uu(d);c[b>>2]=0;return}b=a|0;d=c[b>>2]|0;e=a+24|0;if((d|0)==0){f=0}else{c[d+24>>2]=c[e>>2];f=c[b>>2]|0}b=c[e>>2]|0;if((b|0)==0){c[c[14128]>>2]=f}else{c[b>>2]=f}f=a+12|0;b=c[f>>2]|0;if((b|0)!=0){uu(b);c[f>>2]=0}do{if((c[14128]|0)==(a|0)){f=c[e>>2]|0;c[14128]=f;if((f|0)==0){break}b=c[f>>2]|0;if((b|0)==0){break}c[b+24>>2]=0}}while(0);uu(a);return}function h$(d){d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0;e=i;i=i+8|0;f=e|0;g=c[m>>2]|0;h=f;a[56520]=0;do{if((d|0)!=0){j=d+8|0;k=a[j]|0;if((k&2)==0){l=k}else{a[56520]=a[206584]|0;a[56521|0]=a[206585|0]|0;a[56522|0]=a[206586|0]|0;a[56523|0]=a[206587|0]|0;a[56524|0]=a[206588|0]|0;a[56525|0]=a[206589|0]|0;l=a[j]|0}if((l&4)!=0){j=56520+(uA(56520)|0)|0;a[j]=a[205752]|0;a[j+1|0]=a[205753|0]|0;a[j+2|0]=a[205754|0]|0;a[j+3|0]=a[205755|0]|0;a[j+4|0]=a[205756|0]|0;a[j+5|0]=a[205757|0]|0;a[j+6|0]=a[205758|0]|0}j=c[d+4>>2]|0;if((j-1001|0)>>>0<63){k=c[20384+(j-1e3<<2)>>2]|0;uC(56520,k|0);break}else{n=0}while(1){if((n|0)==6){o=2872;break}if((c[964+(n<<3)>>2]|0)==(j|0)){o=2871;break}else{n=n+1|0}}if((o|0)==2871){k=c[960+(n<<3)>>2]|0;uC(56520,k|0);break}else if((o|0)==2872){b[f>>1]=0;a[h]=j&255;uC(56520,h|0);break}}}while(0);cf(g|0,209560,(v=i,i=i+8|0,c[v>>2]=56520,v)|0);cf(c[m>>2]|0,209048,(v=i,i=i+8|0,c[v>>2]=(a[d+20|0]&1)!=0?42:32,v)|0);g=c[d+12>>2]|0;if((g|0)!=0){h=c[m>>2]|0;cf(h|0,208640,(v=i,i=i+8|0,c[v>>2]=g,v)|0);i=e;return}g=c[d+16>>2]|0;d=c[m>>2]|0;if((g|0)==0){cf(d|0,207152,(v=i,i=i+16|0,c[v>>2]=105800,c[v+8>>2]=2288,v)|0);i=e;return}else{h=cO[g&255](0)|0;cf(d|0,154696,(v=i,i=i+8|0,c[v>>2]=h,v)|0);i=e;return}}function h0(a){a=a|0;var b=0;if((a|0)==0){b=165760}else{dM(165400);b=0}return b|0}function h1(b){b=b|0;var d=0,e=0,f=0,g=0;if((b|0)==0){d=166224;return d|0}b=c[11692]|0;e=c[260]|0;do{if((b|0)==0){if((e|0)==0){f=0;g=2889;break}c[11692]=e}else{f=e;g=2889}}while(0);do{if((g|0)==2889){if((b|0)==(f|0)&(b|0)!=31){c[11692]=31;break}if((a[37384]&1)!=0&(b|0)==31){c[11692]=4095;break}else{c[11692]=0;break}}}while(0);dM(179864);d=0;return d|0}function h2(a){a=a|0;var b=0;if((a|0)==0){b=166576}else{dM(179864);b=0}return b|0}function h3(b){b=b|0;var c=0,d=0,e=0;if((b|0)==0){c=167768;return c|0}else{d=0}while(1){if(d>>>0>=8){e=2905;break}if((a[65036+(d*688&-1)|0]&1)!=0){e=2906;break}if((a[65037+(d*688&-1)|0]&1)==0){d=d+1|0}else{e=2906;break}}if((e|0)==2906){dM(166984);c=0;return c|0}else if((e|0)==2905){dM(196824);c=0;return c|0}return 0}function h4(b){b=b|0;var d=0;if((b|0)==0){d=168352;return d|0}aF(10,c[m>>2]|0);hT(0);if((a[37400]&1)==0){d=0;return d|0}aI(105808,9,1,c[m>>2]|0);aD(c[m>>2]|0);d=0;return d|0}function h5(b){b=b|0;var d=0,e=0,f=0;d=i;if((b|0)==0){e=169176;i=d;return e|0}if((a[872]&1)!=0){uh(-1,168720,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);e=0;i=d;return e|0}b=c[8494]|0;do{if((c[13528]|0)<(b|0)&(b|0)<(c[13529]|0)){f=c[8492]|0;if(!((c[13530]|0)<(f|0)&(f|0)<(c[13531]|0))){break}dM((a[66852]&1)!=0?173736:173264);e=0;i=d;return e|0}}while(0);if((a[37384]&1)==0|(c[5094]|0)!=0){dM((a[65476]&1)!=0?171552:171296);e=0;i=d;return e|0}else{dM((a[64788+((c[34]|0)*688&-1)|0]&1)!=0?172888:172496);e=0;i=d;return e|0}return 0}function h6(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;if((b|0)==0){d=174336;return d|0}b=c[8494]|0;do{if((c[13528]|0)<(b|0)&(b|0)<(c[13529]|0)){e=c[8492]|0;if(!((c[13530]|0)<(e|0)&(e|0)<(c[13531]|0))){break}dM((a[66852]&1)!=0?173736:173264);d=0;return d|0}}while(0);if(!((a[37384]&1)==0|(c[5094]|0)!=0)){dM((a[64788+((c[34]|0)*688&-1)|0]&1)!=0?172888:172496);d=0;return d|0}e=c[8492]|0;f=c[7940]|0;g=c[7941]|0;h=(g-f|0)/4&-1;i=c[7938]|0;if((e|0)<(h+f|0)){j=c[7939]|0;k=(b|0)>(i|0)&(b|0)<(j|0);l=j}else{k=0;l=c[7939]|0}j=(l-i|0)/4&-1;if((b|0)<(j+i|0)){m=(e|0)>(f|0)&(e|0)<(g|0)}else{m=0}if((e|0)>(g-h|0)){n=(b|0)>(i|0)&(b|0)<(l|0)}else{n=0}if((b|0)>(l-j|0)){o=(e|0)>(f|0)&(e|0)<(g|0)}else{o=0}if(k){dL(bP(((a[66164]&1)!=0?172224:171904)|0)|0)}if(m){dL(bP(((a[65476]&1)!=0?171552:171296)|0)|0)}g=c[5094]|0;if((g|0)!=0|n^1){p=g}else{dL(bP(((a[68916]&1)!=0?170840:170464)|0)|0);p=c[5094]|0}if(!((p|0)!=0|o^1)){dL(bP(((a[68228]&1)!=0?170088:169608)|0)|0)}do{if(!k){if((c[5094]|0)!=0&(m^1)){dM((a[64788+((c[34]|0)*688&-1)|0]&1)!=0?172888:172496)}if(m|n|o){break}else{d=0}return d|0}}while(0);dM(179864);d=0;return d|0}function h7(a){a=a|0;var b=0;if((a|0)==0){b=175488;return b|0}do{if((c[8496]|0)==0){c[8496]=1;if((c[8501]|0)==0){break}a=c[m>>2]|0;aI(174984,18,1,a|0)}else{c[8496]=0;if((c[8501]|0)==0){break}a=c[m>>2]|0;aI(174688,19,1,a|0)}}while(0);hM();b=0;return b|0}function h8(b){b=b|0;var d=0,e=0,f=0,g=0;d=i;if((b|0)==0){e=175784;i=d;return e|0}b=c[8506]|0;f=b-1|0;c[8506]=f;g=c[8510]|0;do{if((f|0)!=7|(g|0)!=0){if((f|0)>7){c[8506]=1;break}if((f|0)>=1){break}c[8506]=7;if((g|0)!=0){break}if((a[30528]&1)!=0){break}c[8506]=6}else{if((a[30528]&1)!=0){break}c[8506]=6}}while(0);hM();if((c[8501]|0)==0){e=0;i=d;return e|0}g=c[8506]|0;cf(c[m>>2]|0,176192,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=g,v)|0);e=0;i=d;return e|0}function h9(b){b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;if((b|0)==0){e=176776;i=d;return e|0}b=c[8506]|0;f=b+1|0;c[8506]=f;g=c[8510]|0;do{if((f|0)!=7|(g|0)!=0){if((f|0)>7){h=2998;break}if((b|0)>=0){break}c[8506]=7;if((g|0)!=0){break}if((a[30528]&1)!=0){break}c[8506]=6}else{if((a[30528]&1)!=0){break}c[8506]=8;h=2998}}while(0);if((h|0)==2998){c[8506]=1}hM();if((c[8501]|0)==0){e=0;i=d;return e|0}h=c[8506]|0;cf(c[m>>2]|0,176192,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=h,v)|0);e=0;i=d;return e|0}function ia(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;b=i;if((a|0)==0){d=177368;i=b;return d|0}a=c[13532]|0;e=a-1|0;c[13532]=e;f=c[13534]|0;do{if((e|0)!=7|(f|0)!=0){if((e|0)>7){c[13532]=0;g=0;break}if((e|0)>=0){g=e;break}c[13532]=7;if((f|0)!=0){g=7;break}c[13532]=6;g=6}else{c[13532]=6;g=6}}while(0);if((c[8501]|0)==0){d=0;i=b;return d|0}cf(c[m>>2]|0,177616,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=g,v)|0);d=0;i=b;return d|0}function ib(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;b=i;if((a|0)==0){d=177920;i=b;return d|0}a=c[13532]|0;e=a+1|0;c[13532]=e;f=c[13534]|0;do{if((e|0)!=7|(f|0)!=0){if((e|0)>7){g=3027;break}if((e|0)>=0){h=e;break}c[13532]=7;if((f|0)!=0){h=7;break}c[13532]=6;h=6}else{c[13532]=8;g=3027}}while(0);if((g|0)==3027){c[13532]=0;h=0}if((c[8501]|0)==0){d=0;i=b;return d|0}cf(c[m>>2]|0,177616,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=h,v)|0);d=0;i=b;return d|0}function ic(a){a=a|0;var b=0,d=0,e=0,f=0;b=i;if((a|0)==0){d=179216;i=b;return d|0}e=(c[8500]|0)+1|0;f=(e|0)>2?0:e;c[8500]=f;cR[c[(c[3524]|0)+128>>2]&127]((f|0)!=0?-3:-4,c[a+4>>2]|0,c[a+8>>2]|0);hM();if((c[8501]|0)==0){d=0;i=b;return d|0}cf(c[m>>2]|0,178656,(v=i,i=i+8|0,c[v>>2]=(c[8500]|0)!=0?179864:178216,v)|0);d=0;i=b;return d|0}function id(a){a=a|0;var b=0;if((a|0)==0){b=180400;return b|0}do{if((c[8501]|0)==0){c[8501]=1}else{a=c[m>>2]|0;aI(179928,49,1,a|0);a=(c[8501]|0)==0;c[8501]=a&1;if(a){break}else{b=0}return b|0}}while(0);aI(179616,39,1,c[m>>2]|0);b=0;return b|0}function ie(a){a=a|0;var b=0,c=0.0;do{if((a|0)==0){b=183160}else{c=+g[18074];if(c==0.0){dM(182624);b=0;break}if(c==1.0){dM(181152);b=0;break}else{dM(180792);b=0;break}}}while(0);return b|0}function ig(a){a=a|0;var b=0,d=0,e=0;if((a|0)==0){b=183976;return b|0}a=c[6]|0;do{if((a|0)==0){d=3063}else{e=c[a+72>>2]|0;if((e|0)==0){d=3063;break}ip(e)}}while(0);if((d|0)==3063){d=c[m>>2]|0;aF(7,d|0)}if((c[8501]|0)==0){b=0;return b|0}aI(183608,11,1,c[m>>2]|0);b=0;return b|0}function ih(a){a=a|0;var b=0,d=0,e=0;if((a|0)==0){b=184736;return b|0}a=c[6]|0;do{if((a|0)==0){d=3074}else{e=c[a+68>>2]|0;if((e|0)==0){d=3074;break}ip(e)}}while(0);if((d|0)==3074){d=c[m>>2]|0;aF(7,d|0)}if((c[8501]|0)==0){b=0;return b|0}aI(184360,15,1,c[m>>2]|0);b=0;return b|0}function ii(a){a=a|0;var b=0,d=0;if((a|0)==0){b=187240;return b|0}a=c[8]|0;if((a|0)==0|(c[6]|0)==(a|0)){d=c[m>>2]|0;aF(7,d|0)}else{ip(a)}if((c[8501]|0)==0){b=0;return b|0}aI(186768,8,1,c[m>>2]|0);b=0;return b|0}function ij(b){b=b|0;var c=0;do{if((b|0)==0){c=189200}else{if((a[37384]&1)==0){c=0;break}io(0,-1);c=0}}while(0);return c|0}function ik(b){b=b|0;var c=0;do{if((b|0)==0){c=191536}else{if((a[37384]&1)==0){c=0;break}io(1,0);c=0}}while(0);return c|0}function il(b){b=b|0;var c=0;do{if((b|0)==0){c=193488}else{if((a[37384]&1)==0){c=0;break}io(0,1);c=0}}while(0);return c|0}function im(b){b=b|0;var c=0;do{if((b|0)==0){c=196952}else{if((a[37384]&1)==0){c=0;break}io(-1,0);c=0}}while(0);return c|0}function io(b,d){b=b|0;d=d|0;var e=0,f=0,j=0,k=0.0,l=0.0,n=0.0;e=i;if((c[8512]&1|0)==0){f=d;j=b}else{f=d*10&-1;j=b*10&-1}do{if((j|0)!=0){k=+(j|0)+ +g[3538];if(k<0.0){l=k+360.0}else{l=k}g[3538]=l;if(l<=360.0){break}g[3538]=l+-360.0}}while(0);do{if((f|0)!=0){l=+(f|0)+ +g[3536];if(l<0.0){n=l+360.0}else{n=l}g[3536]=n;if(n<=360.0){break}g[3536]=n+-360.0}}while(0);do{if((f|j|0)!=0){n=+g[3538];b=e6(196288)|0;if((b|0)!=0){a[b+8|0]=0;c[b+16>>2]=2;h[b+24>>3]=n;h[b+32>>3]=0.0}n=+g[3536];b=e6(195744)|0;if((b|0)==0){break}a[b+8|0]=0;c[b+16>>2]=2;h[b+24>>3]=n;h[b+32>>3]=0.0}}while(0);if((c[8501]|0)!=0){j=c[m>>2]|0;n=+g[3538];l=+g[3536];cf(j|0,193976,(v=i,i=i+16|0,h[v>>3]=n,h[v+8>>3]=l,v)|0)}hU(c[10828]|0,c[7952]|0,0);do{if((a[37384]&1)!=0){if(+P(+(+aX(+(+g[3536]),90.0)))>=.1){i=e;return}if(+P(+(+aX(+(+g[3538]),180.0)))<.1){break}i=e;return}}while(0);if((c[(c[3524]|0)+120>>2]|0)==0){i=e;return}hN(c[8494]|0,c[8492]|0,27760,27744,27752,27736);hM();i=e;return}function ip(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0.0,o=0.0,p=0.0,q=0.0;d=i;i=i+1024|0;e=d|0;if((a[37384]&1)==0){f=0}else{f=(c[5094]|0)==1}g=c[6]|0;do{if((g|0)==0){j=0}else{h[g>>3]=+h[8257];h[g+16>>3]=+h[8258];h[g+32>>3]=+h[8601];h[g+48>>3]=+h[8602];c[g+64>>2]=f&1;if(f){k=c[6]|0;h[k+8>>3]=+h[8172];h[k+24>>3]=+h[8171];h[k+40>>3]=+h[8516];h[k+56>>3]=+h[8515];j=k;break}else{k=c[6]|0;h[k+8>>3]=+h[8171];h[k+24>>3]=+h[8172];h[k+40>>3]=+h[8515];h[k+56>>3]=+h[8516];j=k;break}}}while(0);g=c[8]|0;if(!((j|0)!=(g|0)|(g|0)==(b|0))){uD(57080,64648,7568);c[16506]=0;c[16334]=0;c[17194]=0;c[17022]=0}c[6]=b;if((b|0)==0){b=c[m>>2]|0;aF(7,b|0);i=d;return}cM[c[(c[3524]|0)+168>>2]&511](11);b=c[6]|0;if(f){l=(c[b+64>>2]|0)!=0}else{l=0}g=e|0;n=+h[b+16>>3];j=b+24|0;k=b+8|0;o=+h[(l?j:k)>>3];p=+h[(l?k:j)>>3];be(g|0,186136,(v=i,i=i+32|0,h[v>>3]=+h[b>>3],h[v+8>>3]=n,h[v+16>>3]=o,h[v+24>>3]=p,v)|0);if((a[37384]&1)==0){b=e+(uA(g|0)|0)|0;e=c[6]|0;p=+h[e+32>>3];o=+h[e+48>>3];n=+h[e+40>>3];q=+h[e+56>>3];be(b|0,185640,(v=i,i=i+32|0,h[v>>3]=p,h[v+8>>3]=o,h[v+16>>3]=n,h[v+24>>3]=q,v)|0)}do{if((c[6]|0)==(c[8]|0)){b=0;do{uD(57512+(b*688&-1)|0,65080+(b*688&-1)|0,192);c[57376+(b*688&-1)>>2]=c[64944+(b*688&-1)>>2];c[57356+(b*688&-1)>>2]=c[64924+(b*688&-1)>>2];b=b+1|0;}while((b|0)<11);uD(64648,57080,7568);a[g]=0;if((a[872]&1)==0){break}b=c[6928]|0;if((b|0)==2){dO();i=d;return}if(!(f&(b|0)==3)){break}dO();i=d;return}else{a[37408]=1}}while(0);dM(g);a[37408]=0;i=d;return}function iq(b,d,e,f,g){b=b|0;d=+d;e=+e;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0;j=i;i=i+1080|0;k=j|0;l=j+48|0;m=j+312|0;n=j+568|0;o=j+824|0;if((f-4|0)>>>0<3|(f|0)==3){p=m|0;uE(p|0,0,255);a[p]=91;a[m+1|0]=37;a[m+2|0]=115;a[m+3|0]=44;a[m+4|0]=32;q=c[8503]|0;uC(p|0,q|0);q=m+(uA(p|0)|0)|0;w=93;a[q]=w&255;w=w>>8;a[q+1|0]=w&255;q=j+56|0;c[l>>2]=~~(d+946684800.0);m=bm(l|0)|0;if((f|0)==4){l=c[m+12>>2]|0;r=(c[m+16>>2]|0)+1|0;s=c[m+20>>2]|0;t=((s|0)<69?2e3:1900)+s|0;be(q|0,151152,(v=i,i=i+24|0,c[v>>2]=l,c[v+8>>2]=r,c[v+16>>2]=t,v)|0)}else if((f|0)==3){t3(k,d);t4(q,255,66189,k,d- +O(+d))}else if((f|0)==6){k=(c[m+16>>2]|0)+1|0;t=c[m+20>>2]|0;r=c[m+8>>2]|0;l=c[m+4>>2]|0;be(q|0,150792,(v=i,i=i+40|0,c[v>>2]=c[m+12>>2],c[v+8>>2]=k,c[v+16>>2]=((t|0)<69?2e3:1900)+t,c[v+24>>2]=r,c[v+32>>2]=l,v)|0)}else if((f|0)==5){l=c[m+4>>2]|0;be(q|0,150944,(v=i,i=i+16|0,c[v>>2]=c[m+8>>2],c[v+8>>2]=l,v)|0)}else{be(q|0,c[8503]|0,(v=i,i=i+8|0,h[v>>3]=d,v)|0)}be(b|0,p|0,(v=i,i=i+16|0,c[v>>2]=q,h[v+8>>3]=e,v)|0);u=b;x=uA(u|0)|0;y=u+x|0;i=j;return y|0}do{if((f|0)==1){a[632]=0;q=c[8503]|0;ck(632,q|0,30);q=632+(uA(632)|0)|0;a[q]=a[86120]|0;a[q+1|0]=a[86121|0]|0;a[q+2|0]=a[86122|0]|0;q=c[8503]|0;ck(632,q|0,30);be(b|0,632,(v=i,i=i+16|0,h[v>>3]=d,h[v+8>>3]=e,v)|0);u=b;x=uA(u|0)|0;y=u+x|0;i=j;return y|0}else if((f|0)==2){z=+h[8256]- +h[8255];A=+h[8170]- +h[8169];if(z!=0.0){q=n|0;uE(q|0,0,255);a[q]=47;p=c[8503]|0;uC(q|0,p|0);B=(d- +h[8255])/z;be(b|0,q|0,(v=i,i=i+8|0,h[v>>3]=B,v)|0)}else{uD(b|0,152448,13)}q=b+(uA(b|0)|0)|0;if(A!=0.0){p=o|0;uE(p|0,0,255);a[p]=44;a[o+1|0]=32;l=c[8503]|0;uC(p|0,l|0);l=o+(uA(p|0)|0)|0;w=47;a[l]=w&255;w=w>>8;a[l+1|0]=w&255;B=(e- +h[8169])/A;be(q|0,p|0,(v=i,i=i+8|0,h[v>>3]=B,v)|0);u=q;x=uA(u|0)|0;y=u+x|0;i=j;return y|0}else{uD(q|0,151872,15);u=q;x=uA(u|0)|0;y=u+x|0;i=j;return y|0}}else if((f|0)==7){q=(g|0)!=0;p=a[30528]|0;if(!q){if((p&1)==0){break}}if((p&1)==0){be(b|0,g|0,(v=i,i=i+16|0,h[v>>3]=d,h[v+8>>3]=e,v)|0);u=b;x=uA(u|0)|0;y=u+x|0;i=j;return y|0}B=(c[17366]&1|0)==0?+h[8687]:0.0;A=+Y(+e,+d);p=(a[69604]&1)==0;z=d/+S(+A);if(p){C=B+z}else{D=+_(+B);B=+h[8702];C=+Z(+(B*(z+D/B)))}if(q){B=A/+h[9040];be(b|0,g|0,(v=i,i=i+16|0,h[v>>3]=B,h[v+8>>3]=C,v)|0);u=b;x=uA(u|0)|0;y=u+x|0;i=j;return y|0}else{q=b;p=q|0;w=1634496368;a[p]=w&255;w=w>>8;a[p+1|0]=w&255;w=w>>8;a[p+2|0]=w&255;w=w>>8;a[p+3|0]=w&255;p=q+4|0;w=2112114;a[p]=w&255;w=w>>8;a[p+1|0]=w&255;w=w>>8;a[p+2|0]=w&255;w=w>>8;a[p+3|0]=w&255;p=b+(uA(b|0)|0)|0;a[632]=0;q=c[8503]|0;ck(632,q|0,30);q=632+(uA(632)|0)|0;a[q]=a[86120]|0;a[q+1|0]=a[86121|0]|0;a[q+2|0]=a[86122|0]|0;q=c[8503]|0;ck(632,q|0,30);B=A/+h[9040];be(p|0,632,(v=i,i=i+16|0,h[v>>3]=B,h[v+8>>3]=C,v)|0);u=p;x=uA(u|0)|0;y=u+x|0;i=j;return y|0}}}while(0);a[632]=0;ck(632,c[8503]|0,30);g=632+(uA(632)|0)|0;a[g]=a[86120]|0;a[g+1|0]=a[86121|0]|0;a[g+2|0]=a[86122|0]|0;ck(632,c[8503]|0,30);be(b|0,632,(v=i,i=i+16|0,h[v>>3]=d,h[v+8>>3]=e,v)|0);u=b;x=uA(u|0)|0;y=u+x|0;i=j;return y|0}function ir(b,d,e,f){b=+b;d=+d;e=e|0;f=f|0;var g=0,j=0,k=0,l=0;g=i;hN(~~b,~~d,27760,27744,27752,27736);j=e6(164136)|0;if((j|0)!=0){a[j+8|0]=e&1^1;c[j+16>>2]=1;c[j+24>>2]=e?f:-1}e=e6(163432)|0;if((e|0)!=0){a[e+8|0]=0;c[e+16>>2]=1;c[e+24>>2]=f}e=e6(162952)|0;if((e|0)!=0){j=ut(2)|0;do{if((j|0)==0){gk();k=ut(2)|0;if((k|0)!=0){l=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=162328,v)|0)}else{l=j}}while(0);a[l]=f&255;a[l+1|0]=0;f=e+8|0;j=e+16|0;do{if((a[f]&1)==0){if((c[j>>2]|0)!=3){break}uu(c[e+24>>2]|0);c[j>>2]=1}}while(0);a[f]=0;c[j>>2]=3;c[e+24>>2]=l}l=e6(159824)|0;if((l|0)!=0){a[l+8|0]=0;d=+h[3470];c[l+16>>2]=2;h[l+24>>3]=d;h[l+32>>3]=0.0}l=e6(159208)|0;if((l|0)!=0){a[l+8|0]=0;d=+h[3468];c[l+16>>2]=2;h[l+24>>3]=d;h[l+32>>3]=0.0}l=e6(158720)|0;if((l|0)!=0){a[l+8|0]=0;d=+h[3469];c[l+16>>2]=2;h[l+24>>3]=d;h[l+32>>3]=0.0}l=e6(158096)|0;if((l|0)!=0){a[l+8|0]=0;d=+h[3467];c[l+16>>2]=2;h[l+24>>3]=d;h[l+32>>3]=0.0}l=e6(156232)|0;if((l|0)!=0){a[l+8|0]=0;e=c[8512]&1;c[l+16>>2]=1;c[l+24>>2]=e}e=e6(155120)|0;if((e|0)!=0){a[e+8|0]=0;l=c[8512]&4;c[e+16>>2]=1;c[e+24>>2]=l}l=e6(154768)|0;if((l|0)==0){i=g;return}a[l+8|0]=0;e=c[8512]&2;c[l+16>>2]=1;c[l+24>>2]=e;i=g;return}function is(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;e=c[13898]|0;if((e|0)>=(c[8272]|0)){uf(e,214888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}f=c[1054]|0;L4406:do{if((a[f+(e*40&-1)|0]&1)!=0){g=c[f+(e*40&-1)+36>>2]|0;h=f+(e*40&-1)+32|0;j=c[10036]|0;k=0;while(1){if((k|0)>=(g|0)){break}if((a[j+((c[h>>2]|0)+k|0)|0]|0)==(a[k+103664|0]|0)){k=k+1|0}else{break L4406}}if((k|0)!=1){break}uf(e,214888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);c[11670]=0;e4(it()|0,b);if((a[1960]&1)==0){i=d;return b|0}else{uf(e,159392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}return 0}function it(){var a=0,b=0,d=0,e=0,f=0;a=i;b=c[18070]|0;if((b|0)!=0){e1(b)}b=ut(4808)|0;do{if((b|0)==0){gk();d=ut(4808)|0;if((d|0)!=0){e=d;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=122712,v)|0);return 0}else{e=b}}while(0);c[18070]=e;uE(e|0,0,4808);c[18066]=150;c[8230]=0;if((iy()|0)!=0){f=c[18070]|0;i=a;return f|0}c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1;f=c[18070]|0;i=a;return f|0}function iu(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;d=i;i=i+48|0;e=d|0;f=d+24|0;uu(c[3546]|0);c[3546]=0;g=(b|0)!=0;if(g){c[b>>2]=0}h=c[13898]|0;if((h|0)>=(c[8272]|0)){uf(h,135720,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}j=c[1054]|0;k=(a[j+(h*40&-1)|0]&1)==0;do{if(!k){l=c[j+(h*40&-1)+36>>2]|0;m=j+(h*40&-1)+32|0;n=c[10036]|0;o=0;while(1){if((o|0)>=(l|0)){p=3259;break}if((a[n+((c[m>>2]|0)+o|0)|0]|0)==(a[o+103664|0]|0)){o=o+1|0}else{break}}do{if((p|0)==3259){if((o|0)!=1){break}uf(h,135720,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);if(k){break}o=c[j+(h*40&-1)+32>>2]|0;m=c[10036]|0;n=a[m+o|0]|0;if(!((n<<24>>24|0)==39|(n<<24>>24|0)==34)){break}n=c[j+(h*40&-1)+36>>2]|0;l=0;while(1){if((l|0)>=(n|0)){p=3266;break}if((a[m+(o+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{p=3267;break}}if((p|0)==3266){if((l|0)==1){q=0}else{p=3267}}do{if((p|0)==3267){a[14176]=1;is(e);a[14176]=0;if((c[e>>2]|0)==3){q=c[e+8>>2]|0;break}else{c[13898]=h;q=0;break}}}while(0);c[3546]=q;r=q;i=d;return r|0}}while(0);it();q=c[18070]|0;h=c[q>>2]|0;e=0;while(1){if((e|0)>=(h|0)){p=3274;break}j=c[q+8+(e<<5)>>2]|0;if((j-2|0)>>>0<3|(j|0)==8){break}else{e=e+1|0}}do{if((p|0)==3274){e4(q,f);if((a[1960]&1)!=0){break}if((c[f>>2]|0)!=3){break}c[3546]=c[f+8>>2]}}while(0);if(g){c[b>>2]=c[18070]}r=c[3546]|0;i=d;return r|0}function iv(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0;d=i;e=c[10812]|0;f=c[8272]|0;L4473:do{if((e|0)==0){g=43248;h=c[1054]|0;j=c[10036]|0}else{k=(f|0)>(b|0);l=c[1054]|0;m=l+(b*40&-1)|0;n=l+(b*40&-1)+36|0;o=l+(b*40&-1)+32|0;p=c[10036]|0;q=e;L4475:while(1){r=c[q+4>>2]|0;L4477:do{if(k){if((a[m]&1)==0){break}s=c[n>>2]|0;t=0;while(1){if((t|0)>=(s|0)){break}if((a[p+((c[o>>2]|0)+t|0)|0]|0)==(a[r+t|0]|0)){t=t+1|0}else{break L4477}}if((a[r+t|0]|0)==0){u=q;break L4475}}}while(0);r=q|0;s=c[r>>2]|0;if((s|0)==0){g=r;h=l;j=p;break L4473}else{q=s}}i=d;return u|0}}while(0);e=(f|0)>(b|0);f=h+(b*40&-1)|0;q=h+(b*40&-1)+36|0;p=h+(b*40&-1)+32|0;h=41;L4488:while(1){l=c[41160+(h<<3)>>2]|0;L4490:do{if(e){if((a[f]&1)==0){break}o=c[q>>2]|0;n=0;while(1){if((n|0)>=(o|0)){break}if((a[j+((c[p>>2]|0)+n|0)|0]|0)==(a[l+n|0]|0)){n=n+1|0}else{break L4490}}if((a[l+n|0]|0)==0){w=3303;break L4488}}}while(0);l=h+1|0;if((l|0)==117){break}else{h=l}}do{if((w|0)==3303){if((h|0)==0){break}uh(b,92536,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);h=ut(312)|0;do{if((h|0)==0){gk();w=ut(312)|0;if((w|0)!=0){x=w;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=144328,v)|0);return 0}else{x=h}}while(0);c[g>>2]=x;c[x>>2]=0;c[(c[g>>2]|0)+12>>2]=0;c[(c[g>>2]|0)+8>>2]=0;x=(c[(c[1054]|0)+(b*40&-1)+36>>2]|0)+1|0;h=ut(x)|0;do{if((h|0)==0){gk();w=ut(x)|0;if((w|0)!=0){y=w;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=76632,v)|0);return 0}else{y=h}}while(0);c[(c[g>>2]|0)+4>>2]=y;y=c[(c[g>>2]|0)+4>>2]|0;h=c[1054]|0;x=c[h+(b*40&-1)+36>>2]|0;w=c[h+(b*40&-1)+32>>2]|0;b=0;while(1){h=b+1|0;a[y+b|0]=a[(c[10036]|0)+w|0]|0;if((h|0)==(x|0)){break}else{w=w+1|0;b=h}}a[y+x|0]=0;x=c[g>>2]|0;c[x+24>>2]=1;c[x+32>>2]=0;x=c[g>>2]|0;c[x+48>>2]=1;c[x+56>>2]=0;x=c[g>>2]|0;c[x+72>>2]=1;c[x+80>>2]=0;x=c[g>>2]|0;c[x+96>>2]=1;c[x+104>>2]=0;x=c[g>>2]|0;c[x+120>>2]=1;c[x+128>>2]=0;x=c[g>>2]|0;c[x+144>>2]=1;c[x+152>>2]=0;x=c[g>>2]|0;c[x+168>>2]=1;c[x+176>>2]=0;x=c[g>>2]|0;c[x+192>>2]=1;c[x+200>>2]=0;x=c[g>>2]|0;c[x+216>>2]=1;c[x+224>>2]=0;x=c[g>>2]|0;c[x+240>>2]=1;c[x+248>>2]=0;x=c[g>>2]|0;c[x+264>>2]=1;c[x+272>>2]=0;x=c[g>>2]|0;c[x+288>>2]=1;c[x+296>>2]=0;u=c[g>>2]|0;i=d;return u|0}function iw(){var b=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0.0,Q=0,R=0,S=0,T=0.0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0.0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0;b=i;i=i+152|0;e=b|0;f=b+24|0;g=b+48|0;j=b+72|0;k=b+96|0;l=c[13898]|0;m=c[8272]|0;if((m|0)<=(l|0)){n=0;i=b;return n|0}o=k|0;p=j|0;q=j+8|0;r=g|0;s=g+8|0;t=f|0;u=f+8|0;w=e|0;x=e+8|0;y=0;z=0;A=l;l=m;L4518:while(1){m=A;B=l;while(1){C=c[1054]|0;if((a[C+(m*40&-1)|0]&1)==0){n=y;D=3435;break L4518}E=c[C+(m*40&-1)+36>>2]|0;F=C+(m*40&-1)+32|0;G=c[10036]|0;H=0;while(1){if((H|0)>=(E|0)){break}if((a[G+((c[F>>2]|0)+H|0)|0]|0)==(a[H+217064|0]|0)){H=H+1|0}else{n=y;D=3438;break L4518}}if((H|0)!=3){n=y;D=3436;break L4518}F=m+1|0;E=m+2|0;c[13898]=E;if((B|0)<=(F|0)){I=E;D=3452;break L4518}if((a[C+(F*40&-1)|0]&1)==0){I=E;D=3455;break L4518}J=c[C+(F*40&-1)+36>>2]|0;K=C+(F*40&-1)+32|0;F=0;while(1){if((F|0)>=(J|0)){break}if((a[G+((c[K>>2]|0)+F|0)|0]|0)==(a[F+78280|0]|0)){F=F+1|0}else{I=E;D=3456;break L4518}}if((F|0)!=1){I=E;D=3454;break L4518}if((a[C+(E*40&-1)|0]&1)==0){I=E;D=3457;break L4518}K=(bO(d[G+(c[C+(E*40&-1)+32>>2]|0)|0]|0|0)|0)==0;J=c[1054]|0;if(K){K=c[10036]|0;if((a[K+(c[J+(E*40&-1)+32>>2]|0)|0]|0)==95){L=K}else{D=3335;break L4518}}else{L=c[10036]|0}K=c[13898]|0;c[13898]=K+1;H=c[J+(K*40&-1)+36>>2]|0;M=(H|0)>49?49:H;H=c[J+(K*40&-1)+32>>2]|0;K=0;while(1){J=K+1|0;a[k+K|0]=a[L+H|0]|0;if((J|0)==(M|0)){break}else{H=H+1|0;K=J}}a[k+M|0]=0;N=e6(o)|0;K=c[13898]|0;H=c[8272]|0;E=(H|0)>(K|0);L4543:do{if(E){C=c[1054]|0;if((a[C+(K*40&-1)|0]&1)==0){D=3344;break}G=c[C+(K*40&-1)+36>>2]|0;F=C+(K*40&-1)+32|0;C=c[10036]|0;J=0;while(1){if((J|0)>=(G|0)){break}if((a[C+((c[F>>2]|0)+J|0)|0]|0)==(a[J+115e3|0]|0)){J=J+1|0}else{D=3344;break L4543}}F=K+1|0;c[13898]=F;if((J|0)!=1){O=F;D=3392;break}F=is(j)|0;C=c[F>>2]|0;if((C|0)==1){P=+(c[F+8>>2]|0)}else if((C|0)==2){P=+h[F+8>>3]}else if((C|0)==3){P=+uz(c[F+8>>2]|0,0)}else{D=3350;break L4518}if((c[p>>2]|0)==3){uu(c[q>>2]|0);c[p>>2]=1}F=~~P;Q=c[13898]|0;c[13898]=Q+1;if((c[8272]|0)<=(Q|0)){D=3447;break L4518}C=c[1054]|0;if((a[C+(Q*40&-1)|0]&1)==0){D=3445;break L4518}G=c[C+(Q*40&-1)+36>>2]|0;R=C+(Q*40&-1)+32|0;C=c[10036]|0;S=0;while(1){if((S|0)>=(G|0)){break}if((a[C+((c[R>>2]|0)+S|0)|0]|0)==(a[S+183584|0]|0)){S=S+1|0}else{D=3444;break L4518}}if((S|0)!=1){D=3446;break L4518}R=is(g)|0;C=c[R>>2]|0;if((C|0)==1){T=+(c[R+8>>2]|0)}else if((C|0)==2){T=+h[R+8>>3]}else if((C|0)==3){T=+uz(c[R+8>>2]|0,0)}else{D=3364;break L4518}if((c[r>>2]|0)==3){uu(c[s>>2]|0);c[r>>2]=1}R=~~T;C=c[13898]|0;G=c[8272]|0;L4574:do{if((G|0)>(C|0)){J=c[1054]|0;if((a[J+(C*40&-1)|0]&1)==0){U=1;V=C;W=G;break}X=c[J+(C*40&-1)+36>>2]|0;Y=J+(C*40&-1)+32|0;J=c[10036]|0;Z=0;while(1){if((Z|0)>=(X|0)){break}if((a[J+((c[Y>>2]|0)+Z|0)|0]|0)==(a[Z+183584|0]|0)){Z=Z+1|0}else{U=1;V=C;W=G;break L4574}}if((Z|0)!=1){U=1;V=C;W=G;break}c[13898]=C+1;Y=is(f)|0;J=c[Y>>2]|0;if((J|0)==1){_=+(c[Y+8>>2]|0)}else if((J|0)==2){_=+h[Y+8>>3]}else if((J|0)==3){_=+uz(c[Y+8>>2]|0,0)}else{D=3377;break L4518}if((c[t>>2]|0)==3){uu(c[u>>2]|0);c[t>>2]=1}U=~~_;V=c[13898]|0;W=c[8272]|0}else{U=1;V=C;W=G}}while(0);c[13898]=V+1;if((W|0)<=(V|0)){D=3458;break L4518}G=c[1054]|0;if((a[G+(V*40&-1)|0]&1)==0){D=3459;break L4518}C=c[G+(V*40&-1)+36>>2]|0;S=G+(V*40&-1)+32|0;G=c[10036]|0;Y=0;while(1){if((Y|0)>=(C|0)){break}if((a[G+((c[S>>2]|0)+Y|0)|0]|0)==(a[Y+78864|0]|0)){Y=Y+1|0}else{D=3460;break L4518}}if((Y|0)!=1){D=3461;break L4518}S=N+8|0;G=N+16|0;do{if((a[S]&1)==0){if((c[G>>2]|0)!=3){break}uu(c[N+24>>2]|0);c[G>>2]=1}}while(0);c[G>>2]=1;c[N+24>>2]=F;a[S]=0;$=U;ab=R;ac=F;ad=0}else{D=3344}}while(0);if((D|0)==3344){D=0;M=K+1|0;c[13898]=M;O=M;D=3392}if((D|0)==3392){D=0;if(!E){D=3441;break L4518}M=c[1054]|0;if((a[M+(K*40&-1)|0]&1)==0){D=3442;break L4518}Y=c[M+(K*40&-1)+36>>2]|0;C=M+(K*40&-1)+32|0;J=c[10036]|0;X=0;while(1){if((X|0)>=(Y|0)){break}if((a[J+((c[C>>2]|0)+X|0)|0]|0)==(a[X+145600|0]|0)){X=X+1|0}else{D=3443;break L4518}}if((X|0)!=2){D=3440;break L4518}if((O|0)>=(H|0)){ae=O;D=3462;break L4518}L4614:do{if((a[M+(O*40&-1)|0]&1)!=0){C=c[M+(O*40&-1)+36>>2]|0;Y=M+(O*40&-1)+32|0;K=0;while(1){if((K|0)>=(C|0)){break}if((a[J+((c[Y>>2]|0)+K|0)|0]|0)==(a[K+103664|0]|0)){K=K+1|0}else{break L4614}}if((K|0)==1){ae=O;D=3463;break L4518}}}while(0);a[14176]=1;is(e);a[14176]=0;if((c[w>>2]|0)!=3){D=3405;break L4518}J=c[x>>2]|0;af=c[13898]|0;if((J|0)==0){ae=af;D=3465;break L4518}c[13898]=af+1;if((c[8272]|0)<=(af|0)){D=3450;break L4518}M=c[1054]|0;if((a[M+(af*40&-1)|0]&1)==0){D=3451;break L4518}H=c[M+(af*40&-1)+36>>2]|0;X=M+(af*40&-1)+32|0;M=c[10036]|0;Y=0;while(1){if((Y|0)>=(H|0)){break}if((a[M+((c[X>>2]|0)+Y|0)|0]|0)==(a[Y+78864|0]|0)){Y=Y+1|0}else{D=3448;break L4518}}if((Y|0)!=1){D=3449;break L4518}X=e9(J)|0;M=N+8|0;H=N+16|0;do{if((a[M]&1)==0){if((c[H>>2]|0)!=3){break}uu(c[N+24>>2]|0);c[H>>2]=1}}while(0);Y=fa(J,1)|0;c[H>>2]=3;c[N+24>>2]=Y;a[M]=0;$=1;ab=X;ac=1;ad=J}if((N|0)==0){ag=0}else{ag=aa(ab-ac|0,$)>>>31&255}if(!(($|0)==0|(ac|0)==(ab|0))){Y=ab-ac|0;if((((Y|0)>-1?Y:-Y|0)|0)>=((($|0)>-1?$:-$|0)|0)){break}}Y=c[13898]|0;C=c[8272]|0;if((C|0)>(Y|0)){m=Y;B=C}else{n=y;D=3437;break L4518}}B=ut(40)|0;if((B|0)==0){gk();m=ut(40)|0;if((m|0)==0){D=3427;break}else{ah=m}}else{ah=B}B=ah;c[ah+8>>2]=N;c[ah+12>>2]=ad;c[ah+16>>2]=ac;c[ah+20>>2]=ab;c[ah+24>>2]=$;c[ah+28>>2]=ac;c[ah>>2]=0;m=ah+4|0;c[m>>2]=0;uE(ah+32|0,0,7);if((z|0)==0){ai=B}else{C=y+4|0;c[c[C>>2]>>2]=B;c[m>>2]=c[C>>2];ai=y}c[ai+4>>2]=B;B=ai+38|0;if((a[B]&1)==0){a[B]=ag}B=c[13898]|0;C=c[8272]|0;if((C|0)>(B|0)){y=ai;z=z+1|0;A=B;l=C}else{n=ai;D=3434;break}}if((D|0)==3455){aj=I-1|0;uf(aj,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3456){aj=I-1|0;uf(aj,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3457){aj=I-1|0;uf(aj,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3458){uf(V,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3459){uf(V,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3460){uf(V,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3461){uf(V,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3462){ak=ae-1|0;uf(ak,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3463){ak=ae-1|0;uf(ak,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3465){ak=ae-1|0;uf(ak,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3427){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=180144,v)|0);return 0}else if((D|0)==3434){i=b;return n|0}else if((D|0)==3435){i=b;return n|0}else if((D|0)==3436){i=b;return n|0}else if((D|0)==3437){i=b;return n|0}else if((D|0)==3438){i=b;return n|0}else if((D|0)==3440){al=O-1|0;uf(al,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3335){I=c[13898]|0;aj=I-1|0;uf(aj,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3364){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3350){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3377){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3441){al=O-1|0;uf(al,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3442){al=O-1|0;uf(al,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3443){al=O-1|0;uf(al,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3444){uf(Q,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3445){uf(Q,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3446){uf(Q,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3447){uf(Q,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3448){uf(af,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3449){uf(af,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3450){uf(af,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3451){uf(af,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3452){aj=I-1|0;uf(aj,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3454){aj=I-1|0;uf(aj,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((D|0)==3405){c[13898]=O;ae=O;ak=ae-1|0;uf(ak,224336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}return 0}function ix(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;if((b|0)==0){d=0;return d|0}if((a[b+38|0]&1)!=0){d=0;return d|0}e=c[b+4>>2]|0;if((e|0)==0){d=0;return d|0}f=b+37|0;g=a[f]|0;L4699:do{if((g&1)!=0|(e|0)==(b|0)){h=e;i=g}else{j=e;k=g;while(1){l=j+36|0;if((a[l]&1)==0){h=j;i=k;break L4699}m=c[j+16>>2]|0;n=j+28|0;c[n>>2]=m;a[l]=0;l=j+12|0;o=j+8|0;p=(c[o>>2]|0)+24|0;if((c[l>>2]|0)==0){c[p>>2]=m}else{uu(c[p>>2]|0);p=fa(c[l>>2]|0,c[n>>2]|0)|0;c[(c[o>>2]|0)+24>>2]=p}p=c[j+4>>2]|0;o=a[f]|0;if((o&1)!=0|(p|0)==(b|0)){h=p;i=o;break}else{j=p;k=o}}}}while(0);g=h+8|0;if((c[g>>2]|0)==0){c[h+32>>2]=0;d=0;return d|0}e=b+32|0;c[e>>2]=(c[e>>2]|0)+1;if((i&1)==0){i=h+28|0;c[i>>2]=(c[i>>2]|0)+(c[h+24>>2]|0)}i=h+12|0;if((c[i>>2]|0)==0){c[(c[g>>2]|0)+24>>2]=c[h+28>>2]}else{uu(c[(c[g>>2]|0)+24>>2]|0);e=fa(c[i>>2]|0,c[h+28>>2]|0)|0;c[(c[g>>2]|0)+24>>2]=e}e=c[h+24>>2]|0;a[h+36|0]=aa(((c[h+20>>2]|0)-(c[h+28>>2]|0)|0)-e|0,e)>>>31&255;e=b;b=0;while(1){if(b){q=1}else{q=(a[e+36|0]&1)==0}h=c[e>>2]|0;if((h|0)==0){break}else{e=h;b=q}}if(q){d=1;return d|0}if((a[f]&1)!=0){d=0;return d|0}a[f]=1;d=1;return d|0}function iy(){var b=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;b=c[13898]|0;e=c[1054]|0;if((a[e+(b*40&-1)|0]&1)==0){f=0;return f|0}do{if((bO(d[(c[10036]|0)+(c[e+(b*40&-1)+32>>2]|0)|0]|0|0)|0)==0){if((a[(c[10036]|0)+(c[(c[1054]|0)+(b*40&-1)+32>>2]|0)|0]|0)==95){break}else{f=0}return f|0}}while(0);b=(c[13898]|0)+1|0;if((c[8272]|0)<=(b|0)){f=0;return f|0}e=c[1054]|0;if((a[e+(b*40&-1)|0]&1)==0){f=0;return f|0}g=c[e+(b*40&-1)+36>>2]|0;h=e+(b*40&-1)+32|0;b=c[10036]|0;e=0;while(1){if((e|0)>=(g|0)){break}if((a[b+((c[h>>2]|0)+e|0)|0]|0)==(a[e+115e3|0]|0)){e=e+1|0}else{f=0;i=3519;break}}if((i|0)==3519){return f|0}if((e|0)!=1){f=0;return f|0}e=c[18070]|0;i=c[e>>2]|0;h=c[18066]|0;if((i|0)<(h|0)){j=e;k=i}else{i=db(e,(h<<5)+4808|0,168072)|0;h=i;c[18070]=h;c[18066]=(c[18066]|0)+150;j=h;k=c[i>>2]|0}c[j+8+(k<<5)>>2]=1;k=c[18070]|0;j=c[k>>2]|0;c[k>>2]=j+1;k=c[18070]|0;i=c[13898]|0;h=c[1054]|0;e=c[h+(i*40&-1)+36>>2]|0;b=e+(c[h+(i*40&-1)+32>>2]|0)|0;h=db(0,e+1|0,116456)|0;e=c[(c[1054]|0)+(i*40&-1)+32>>2]|0;L4758:do{if((e|0)<(b|0)){i=e;g=h;while(1){l=a[(c[10036]|0)+i|0]|0;if(l<<24>>24==0){m=g;break L4758}n=g+1|0;a[g]=l;l=i+1|0;if((l|0)<(b|0)){i=l;g=n}else{m=n;break}}}else{m=h}}while(0);a[m]=0;c[k+8+(j<<5)+8>>2]=3;c[k+8+(j<<5)+16>>2]=h;c[13898]=(c[13898]|0)+2;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}h=c[18070]|0;j=c[h>>2]|0;k=c[18066]|0;if((j|0)<(k|0)){o=h;p=j}else{j=db(h,(k<<5)+4808|0,168072)|0;k=j;c[18070]=k;c[18066]=(c[18066]|0)+150;o=k;p=c[j>>2]|0}c[o+8+(p<<5)>>2]=36;p=c[18070]|0;c[p>>2]=(c[p>>2]|0)+1;f=1;return f|0}function iz(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;iA();b=c[13898]|0;if((c[8272]|0)>(b|0)){d=b}else{return}L4773:while(1){b=c[1054]|0;if((a[b+(d*40&-1)|0]&1)==0){e=3540;break}f=c[b+(d*40&-1)+36>>2]|0;g=b+(d*40&-1)+32|0;b=c[10036]|0;h=0;while(1){if((h|0)>=(f|0)){break}if((a[b+((c[g>>2]|0)+h|0)|0]|0)==(a[h+163800|0]|0)){h=h+1|0}else{e=3539;break L4773}}if((h|0)!=2){e=3538;break}c[13898]=d+1;g=c[18070]|0;b=c[g>>2]|0;f=c[18066]|0;if((b|0)<(f|0)){i=g;j=b}else{k=db(g,(f<<5)+4808|0,168072)|0;f=k;c[18070]=f;c[18066]=(c[18066]|0)+150;i=f;j=c[k>>2]|0}c[i+8+(j<<5)>>2]=39;k=c[18070]|0;c[k>>2]=(c[k>>2]|0)+1;iA();k=c[18070]|0;c[k+8+(b<<5)+8>>2]=(c[k>>2]|0)-b;b=c[18070]|0;k=c[b>>2]|0;f=c[18066]|0;if((k|0)<(f|0)){l=b;m=k}else{k=db(b,(f<<5)+4808|0,168072)|0;f=k;c[18070]=f;c[18066]=(c[18066]|0)+150;l=f;m=c[k>>2]|0}c[l+8+(m<<5)>>2]=30;k=c[18070]|0;c[k>>2]=(c[k>>2]|0)+1;k=c[13898]|0;if((c[8272]|0)>(k|0)){d=k}else{e=3536;break}}if((e|0)==3539){return}else if((e|0)==3538){return}else if((e|0)==3536){return}else if((e|0)==3540){return}}function iA(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;iB();b=c[13898]|0;if((c[8272]|0)>(b|0)){d=b}else{return}L4795:while(1){b=c[1054]|0;if((a[b+(d*40&-1)|0]&1)==0){e=3555;break}f=c[b+(d*40&-1)+36>>2]|0;g=b+(d*40&-1)+32|0;b=c[10036]|0;h=0;while(1){if((h|0)>=(f|0)){break}if((a[b+((c[g>>2]|0)+h|0)|0]|0)==(a[h+154536|0]|0)){h=h+1|0}else{e=3556;break L4795}}if((h|0)!=2){e=3554;break}c[13898]=d+1;g=c[18070]|0;b=c[g>>2]|0;f=c[18066]|0;if((b|0)<(f|0)){i=g;j=b}else{k=db(g,(f<<5)+4808|0,168072)|0;f=k;c[18070]=f;c[18066]=(c[18066]|0)+150;i=f;j=c[k>>2]|0}c[i+8+(j<<5)>>2]=38;k=c[18070]|0;c[k>>2]=(c[k>>2]|0)+1;iB();k=c[18070]|0;c[k+8+(b<<5)+8>>2]=(c[k>>2]|0)-b;b=c[18070]|0;k=c[b>>2]|0;f=c[18066]|0;if((k|0)<(f|0)){l=b;m=k}else{k=db(b,(f<<5)+4808|0,168072)|0;f=k;c[18070]=f;c[18066]=(c[18066]|0)+150;l=f;m=c[k>>2]|0}c[l+8+(m<<5)>>2]=30;k=c[18070]|0;c[k>>2]=(c[k>>2]|0)+1;k=c[13898]|0;if((c[8272]|0)>(k|0)){d=k}else{e=3553;break}}if((e|0)==3554){return}else if((e|0)==3555){return}else if((e|0)==3553){return}else if((e|0)==3556){return}}function iB(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;iD();b=c[13898]|0;if((c[8272]|0)>(b|0)){d=b}else{return}L4817:while(1){b=c[1054]|0;if((a[b+(d*40&-1)|0]&1)==0){e=3572;break}f=c[b+(d*40&-1)+36>>2]|0;g=b+(d*40&-1)+32|0;b=c[10036]|0;h=0;while(1){if((h|0)>=(f|0)){break}if((a[b+((c[g>>2]|0)+h|0)|0]|0)==(a[h+151312|0]|0)){h=h+1|0}else{e=3571;break L4817}}if((h|0)!=1){e=3570;break}c[13898]=d+1;iD();g=c[18070]|0;b=c[g>>2]|0;f=c[18066]|0;if((b|0)<(f|0)){i=g;j=b}else{b=db(g,(f<<5)+4808|0,168072)|0;f=b;c[18070]=f;c[18066]=(c[18066]|0)+150;i=f;j=c[b>>2]|0}c[i+8+(j<<5)>>2]=14;b=c[18070]|0;c[b>>2]=(c[b>>2]|0)+1;b=c[13898]|0;if((c[8272]|0)>(b|0)){d=b}else{e=3568;break}}if((e|0)==3571){return}else if((e|0)==3572){return}else if((e|0)==3568){return}else if((e|0)==3570){return}}function iC(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;b=i;d=c[13898]|0;if((c[8272]|0)<=(d|0)){i=b;return}e=c[1054]|0;if((a[e+(d*40&-1)|0]&1)==0){i=b;return}f=c[e+(d*40&-1)+36>>2]|0;g=e+(d*40&-1)+32|0;e=c[10036]|0;h=0;while(1){if((h|0)>=(f|0)){break}if((a[e+((c[g>>2]|0)+h|0)|0]|0)==(a[h+216168|0]|0)){h=h+1|0}else{j=3603;break}}if((j|0)==3603){i=b;return}if((h|0)!=1){i=b;return}c[8230]=(c[8230]|0)-1;c[13898]=d+1;d=c[18070]|0;h=c[d>>2]|0;g=c[18066]|0;if((h|0)<(g|0)){k=d;l=h}else{e=db(d,(g<<5)+4808|0,168072)|0;g=e;c[18070]=g;c[18066]=(c[18066]|0)+150;k=g;l=c[e>>2]|0}c[k+8+(l<<5)>>2]=40;l=c[18070]|0;c[l>>2]=(c[l>>2]|0)+1;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}l=c[13898]|0;if((c[8272]|0)<=(l|0)){uf(l,172040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=c[1054]|0;if((a[k+(l*40&-1)|0]&1)==0){uf(l,172040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=c[k+(l*40&-1)+36>>2]|0;g=k+(l*40&-1)+32|0;k=c[10036]|0;d=0;while(1){if((d|0)>=(e|0)){break}if((a[k+((c[g>>2]|0)+d|0)|0]|0)==(a[d+183584|0]|0)){d=d+1|0}else{j=3598;break}}if((j|0)==3598){uf(l,172040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((d|0)!=1){uf(l,172040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=l+1;l=c[18070]|0;d=c[l>>2]|0;j=c[18066]|0;if((d|0)<(j|0)){m=l;n=d}else{g=db(l,(j<<5)+4808|0,168072)|0;j=g;c[18070]=j;c[18066]=(c[18066]|0)+150;m=j;n=c[g>>2]|0}c[m+8+(n<<5)>>2]=37;n=c[18070]|0;c[n>>2]=(c[n>>2]|0)+1;n=c[18070]|0;c[n+8+(h<<5)+8>>2]=(c[n>>2]|0)-h;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}h=c[18070]|0;c[h+8+(d<<5)+8>>2]=(c[h>>2]|0)-d;c[8230]=(c[8230]|0)+1;i=b;return}function iD(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;iE();b=c[13898]|0;if((c[8272]|0)>(b|0)){d=b}else{return}L4880:while(1){b=c[1054]|0;if((a[b+(d*40&-1)|0]&1)==0){e=3617;break}f=c[b+(d*40&-1)+36>>2]|0;g=b+(d*40&-1)+32|0;b=c[10036]|0;h=0;while(1){if((h|0)>=(f|0)){break}if((a[b+((c[g>>2]|0)+h|0)|0]|0)==(a[h+149504|0]|0)){h=h+1|0}else{e=3618;break L4880}}if((h|0)!=1){e=3616;break}c[13898]=d+1;iE();g=c[18070]|0;b=c[g>>2]|0;f=c[18066]|0;if((b|0)<(f|0)){i=g;j=b}else{b=db(g,(f<<5)+4808|0,168072)|0;f=b;c[18070]=f;c[18066]=(c[18066]|0)+150;i=f;j=c[b>>2]|0}c[i+8+(j<<5)>>2]=15;b=c[18070]|0;c[b>>2]=(c[b>>2]|0)+1;b=c[13898]|0;if((c[8272]|0)>(b|0)){d=b}else{e=3615;break}}if((e|0)==3616){return}else if((e|0)==3615){return}else if((e|0)==3618){return}else if((e|0)==3617){return}}function iE(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0;iF();b=c[13898]|0;if((c[8272]|0)>(b|0)){d=b}else{return}L4899:while(1){b=c[1054]|0;if((a[b+(d*40&-1)|0]&1)==0){e=3632;break}f=c[b+(d*40&-1)+36>>2]|0;g=b+(d*40&-1)+32|0;b=c[10036]|0;h=0;while(1){if((h|0)>=(f|0)){break}if((a[b+((c[g>>2]|0)+h|0)|0]|0)==(a[h+147520|0]|0)){h=h+1|0}else{e=3630;break L4899}}if((h|0)!=1){e=3633;break}c[13898]=d+1;iF();g=c[18070]|0;b=c[g>>2]|0;f=c[18066]|0;if((b|0)<(f|0)){i=g;j=b}else{b=db(g,(f<<5)+4808|0,168072)|0;f=b;c[18070]=f;c[18066]=(c[18066]|0)+150;i=f;j=c[b>>2]|0}c[i+8+(j<<5)>>2]=16;b=c[18070]|0;c[b>>2]=(c[b>>2]|0)+1;b=c[13898]|0;if((c[8272]|0)>(b|0)){d=b}else{e=3634;break}}if((e|0)==3633){return}else if((e|0)==3632){return}else if((e|0)==3630){return}else if((e|0)==3634){return}}function iF(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;iI();iJ();iH();iG();L4916:while(1){b=c[13898]|0;d=(c[8272]|0)>(b|0);if(!d){e=3675;break}f=c[1054]|0;if((a[f+(b*40&-1)|0]&1)!=0){g=c[f+(b*40&-1)+36>>2]|0;h=f+(b*40&-1)+32|0;f=c[10036]|0;i=0;while(1){if((i|0)>=(g|0)){e=3641;break}if((a[f+((c[h>>2]|0)+i|0)|0]|0)==(a[i+145384|0]|0)){i=i+1|0}else{break}}do{if((e|0)==3641){e=0;if((i|0)!=2){break}c[13898]=b+1;iI();iJ();iH();iG();h=c[18070]|0;f=c[h>>2]|0;g=c[18066]|0;if((f|0)<(g|0)){j=h;k=f}else{f=db(h,(g<<5)+4808|0,168072)|0;g=f;c[18070]=g;c[18066]=(c[18066]|0)+150;j=g;k=c[f>>2]|0}c[j+8+(k<<5)>>2]=17;f=c[18070]|0;c[f>>2]=(c[f>>2]|0)+1;continue L4916}}while(0);if(!d){e=3674;break}}i=c[1054]|0;L4932:do{if((a[i+(b*40&-1)|0]&1)!=0){f=c[i+(b*40&-1)+36>>2]|0;g=i+(b*40&-1)+32|0;h=c[10036]|0;l=0;while(1){if((l|0)>=(f|0)){break}if((a[h+((c[g>>2]|0)+l|0)|0]|0)==(a[l+143464|0]|0)){l=l+1|0}else{break L4932}}if((l|0)!=2){break}c[13898]=b+1;iI();iJ();iH();iG();g=c[18070]|0;h=c[g>>2]|0;f=c[18066]|0;if((h|0)<(f|0)){m=g;n=h}else{h=db(g,(f<<5)+4808|0,168072)|0;f=h;c[18070]=f;c[18066]=(c[18066]|0)+150;m=f;n=c[h>>2]|0}c[m+8+(n<<5)>>2]=18;h=c[18070]|0;c[h>>2]=(c[h>>2]|0)+1;continue L4916}}while(0);if(!d){e=3673;break}i=c[1054]|0;if((a[i+(b*40&-1)|0]&1)!=0){h=c[i+(b*40&-1)+36>>2]|0;f=i+(b*40&-1)+32|0;i=c[10036]|0;g=0;while(1){if((g|0)>=(h|0)){e=3659;break}if((a[i+((c[f>>2]|0)+g|0)|0]|0)==(a[g+141992|0]|0)){g=g+1|0}else{break}}do{if((e|0)==3659){e=0;if((g|0)!=2){break}c[13898]=b+1;iI();iJ();iH();iG();f=c[18070]|0;i=c[f>>2]|0;h=c[18066]|0;if((i|0)<(h|0)){o=f;p=i}else{i=db(f,(h<<5)+4808|0,168072)|0;h=i;c[18070]=h;c[18066]=(c[18066]|0)+150;o=h;p=c[i>>2]|0}c[o+8+(p<<5)>>2]=33;i=c[18070]|0;c[i>>2]=(c[i>>2]|0)+1;continue L4916}}while(0);if(!d){e=3676;break}}g=c[1054]|0;if((a[g+(b*40&-1)|0]&1)==0){e=3677;break}i=c[g+(b*40&-1)+36>>2]|0;h=g+(b*40&-1)+32|0;g=c[10036]|0;f=0;while(1){if((f|0)>=(i|0)){break}if((a[g+((c[h>>2]|0)+f|0)|0]|0)==(a[f+140312|0]|0)){f=f+1|0}else{e=3678;break L4916}}if((f|0)!=2){e=3679;break}c[13898]=b+1;iI();iJ();iH();iG();h=c[18070]|0;g=c[h>>2]|0;i=c[18066]|0;if((g|0)<(i|0)){q=h;r=g}else{g=db(h,(i<<5)+4808|0,168072)|0;i=g;c[18070]=i;c[18066]=(c[18066]|0)+150;q=i;r=c[g>>2]|0}c[q+8+(r<<5)>>2]=34;g=c[18070]|0;c[g>>2]=(c[g>>2]|0)+1}if((e|0)==3679){return}else if((e|0)==3673){return}else if((e|0)==3674){return}else if((e|0)==3675){return}else if((e|0)==3676){return}else if((e|0)==3677){return}else if((e|0)==3678){return}}function iG(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;L4975:while(1){b=c[13898]|0;d=c[8272]|0;e=(d|0)>(b|0);if(!e){f=3726;break}g=c[1054]|0;if((a[g+(b*40&-1)|0]&1)!=0){h=c[g+(b*40&-1)+36>>2]|0;i=g+(b*40&-1)+32|0;g=c[10036]|0;j=0;while(1){if((j|0)>=(h|0)){f=3686;break}if((a[g+((c[i>>2]|0)+j|0)|0]|0)==(a[j+138304|0]|0)){j=j+1|0}else{break}}do{if((f|0)==3686){f=0;if((j|0)!=1){break}c[13898]=b+1;iI();iJ();iH();i=c[18070]|0;g=c[i>>2]|0;h=c[18066]|0;if((g|0)<(h|0)){k=i;l=g}else{g=db(i,(h<<5)+4808|0,168072)|0;h=g;c[18070]=h;c[18066]=(c[18066]|0)+150;k=h;l=c[g>>2]|0}c[k+8+(l<<5)>>2]=19;g=c[18070]|0;c[g>>2]=(c[g>>2]|0)+1;continue L4975}}while(0);if(!e){f=3729;break}}j=c[1054]|0;L4991:do{if((a[j+(b*40&-1)|0]&1)!=0){g=c[j+(b*40&-1)+36>>2]|0;h=j+(b*40&-1)+32|0;i=c[10036]|0;m=0;while(1){if((m|0)>=(g|0)){break}if((a[i+((c[h>>2]|0)+m|0)|0]|0)==(a[m+136608|0]|0)){m=m+1|0}else{break L4991}}if((m|0)!=1){break}h=b+1|0;L4998:do{if((a[25288]&1)!=0&(d|0)>(h|0)){if((a[j+(h*40&-1)|0]&1)==0){break}g=c[j+(h*40&-1)+36>>2]|0;n=j+(h*40&-1)+32|0;o=0;while(1){if((o|0)>=(g|0)){break}if((a[i+((c[n>>2]|0)+o|0)|0]|0)==(a[o+134808|0]|0)){o=o+1|0}else{break L4998}}if((o|0)==1){f=3728;break L4975}}}while(0);c[13898]=h;iI();iJ();iH();i=c[18070]|0;m=c[i>>2]|0;n=c[18066]|0;if((m|0)<(n|0)){p=i;q=m}else{m=db(i,(n<<5)+4808|0,168072)|0;n=m;c[18070]=n;c[18066]=(c[18066]|0)+150;p=n;q=c[m>>2]|0}c[p+8+(q<<5)>>2]=20;m=c[18070]|0;c[m>>2]=(c[m>>2]|0)+1;continue L4975}}while(0);if(!e){f=3730;break}j=c[1054]|0;if((a[j+(b*40&-1)|0]&1)!=0){d=c[j+(b*40&-1)+36>>2]|0;m=j+(b*40&-1)+32|0;j=c[10036]|0;n=0;while(1){if((n|0)>=(d|0)){f=3710;break}if((a[j+((c[m>>2]|0)+n|0)|0]|0)==(a[n+133312|0]|0)){n=n+1|0}else{break}}do{if((f|0)==3710){f=0;if((n|0)!=2){break}c[13898]=b+1;iI();iJ();iH();m=c[18070]|0;j=c[m>>2]|0;d=c[18066]|0;if((j|0)<(d|0)){r=m;s=j}else{j=db(m,(d<<5)+4808|0,168072)|0;d=j;c[18070]=d;c[18066]=(c[18066]|0)+150;r=d;s=c[j>>2]|0}c[r+8+(s<<5)>>2]=21;j=c[18070]|0;c[j>>2]=(c[j>>2]|0)+1;continue L4975}}while(0);if(!e){f=3727;break}}n=c[1054]|0;if((a[n+(b*40&-1)|0]&1)==0){f=3731;break}j=c[n+(b*40&-1)+36>>2]|0;d=n+(b*40&-1)+32|0;n=c[10036]|0;m=0;while(1){if((m|0)>=(j|0)){break}if((a[n+((c[d>>2]|0)+m|0)|0]|0)==(a[m+131928|0]|0)){m=m+1|0}else{f=3724;break L4975}}if((m|0)!=2){f=3725;break}c[13898]=b+1;iI();iJ();iH();d=c[18070]|0;n=c[d>>2]|0;j=c[18066]|0;if((n|0)<(j|0)){t=d;u=n}else{n=db(d,(j<<5)+4808|0,168072)|0;j=n;c[18070]=j;c[18066]=(c[18066]|0)+150;t=j;u=c[n>>2]|0}c[t+8+(u<<5)>>2]=22;n=c[18070]|0;c[n>>2]=(c[n>>2]|0)+1}if((f|0)==3726){return}else if((f|0)==3727){return}else if((f|0)==3728){return}else if((f|0)==3724){return}else if((f|0)==3725){return}else if((f|0)==3729){return}else if((f|0)==3730){return}else if((f|0)==3731){return}}function iH(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;L5043:while(1){b=c[13898]|0;d=(c[8272]|0)>(b|0);L5045:do{if(d){e=c[1054]|0;if((a[e+(b*40&-1)|0]&1)==0){break}f=c[e+(b*40&-1)+36>>2]|0;g=e+(b*40&-1)+32|0;e=c[10036]|0;h=0;while(1){if((h|0)>=(f|0)){break}if((a[e+((c[g>>2]|0)+h|0)|0]|0)==(a[h+202200|0]|0)){h=h+1|0}else{break L5045}}if((h|0)!=1){break}c[13898]=b+1;iI();iJ();g=c[18070]|0;e=c[g>>2]|0;f=c[18066]|0;if((e|0)<(f|0)){i=g;j=e}else{e=db(g,(f<<5)+4808|0,168072)|0;f=e;c[18070]=f;c[18066]=(c[18066]|0)+150;i=f;j=c[e>>2]|0}c[i+8+(j<<5)>>2]=32;e=c[18070]|0;c[e>>2]=(c[e>>2]|0)+1;continue L5043}}while(0);if(a[14176]&(c[8230]|0)==1|d^1){k=3763;break}e=c[1054]|0;if((a[e+(b*40&-1)|0]&1)!=0){f=c[e+(b*40&-1)+36>>2]|0;g=e+(b*40&-1)+32|0;e=c[10036]|0;l=0;while(1){if((l|0)>=(f|0)){k=3747;break}if((a[e+((c[g>>2]|0)+l|0)|0]|0)==(a[l+129568|0]|0)){l=l+1|0}else{break}}do{if((k|0)==3747){k=0;if((l|0)!=1){break}c[13898]=b+1;iI();iJ();g=c[18070]|0;e=c[g>>2]|0;f=c[18066]|0;if((e|0)<(f|0)){m=g;n=e}else{e=db(g,(f<<5)+4808|0,168072)|0;f=e;c[18070]=f;c[18066]=(c[18066]|0)+150;m=f;n=c[e>>2]|0}c[m+8+(n<<5)>>2]=23;e=c[18070]|0;c[e>>2]=(c[e>>2]|0)+1;continue L5043}}while(0);if(!d){k=3764;break}}l=c[1054]|0;if((a[l+(b*40&-1)|0]&1)==0){k=3765;break}e=c[l+(b*40&-1)+36>>2]|0;f=l+(b*40&-1)+32|0;l=c[10036]|0;g=0;while(1){if((g|0)>=(e|0)){break}if((a[l+((c[f>>2]|0)+g|0)|0]|0)==(a[g+202112|0]|0)){g=g+1|0}else{k=3761;break L5043}}if((g|0)!=1){k=3762;break}c[13898]=b+1;iI();iJ();f=c[18070]|0;l=c[f>>2]|0;e=c[18066]|0;if((l|0)<(e|0)){o=f;p=l}else{l=db(f,(e<<5)+4808|0,168072)|0;e=l;c[18070]=e;c[18066]=(c[18066]|0)+150;o=e;p=c[l>>2]|0}c[o+8+(p<<5)>>2]=24;l=c[18070]|0;c[l>>2]=(c[l>>2]|0)+1}if((k|0)==3762){return}else if((k|0)==3763){return}else if((k|0)==3761){return}else if((k|0)==3764){return}else if((k|0)==3765){return}}function iI(){var b=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0;b=i;i=i+104|0;e=b|0;f=b+56|0;g=b+72|0;h=b+88|0;j=c[13898]|0;k=(c[8272]|0)>(j|0);L5087:do{if(k){l=c[1054]|0;if((a[l+(j*40&-1)|0]&1)!=0){m=c[l+(j*40&-1)+36>>2]|0;n=l+(j*40&-1)+32|0;l=c[10036]|0;o=0;while(1){if((o|0)>=(m|0)){p=3771;break}if((a[l+((c[n>>2]|0)+o|0)|0]|0)==(a[o+124624|0]|0)){o=o+1|0}else{break}}do{if((p|0)==3771){if((o|0)!=1){break}c[13898]=j+1;iI();n=c[18070]|0;l=c[n>>2]|0;m=c[18066]|0;if((l|0)<(m|0)){q=n;r=l}else{l=db(n,(m<<5)+4808|0,168072)|0;m=l;c[18070]=m;c[18066]=(c[18066]|0)+150;q=m;r=c[l>>2]|0}c[q+8+(r<<5)>>2]=9;l=c[18070]|0;c[l>>2]=(c[l>>2]|0)+1;i=b;return}}while(0);if(!k){break}}o=c[1054]|0;L5103:do{if((a[o+(j*40&-1)|0]&1)!=0){l=c[o+(j*40&-1)+36>>2]|0;m=o+(j*40&-1)+32|0;n=c[10036]|0;s=0;while(1){if((s|0)>=(l|0)){break}if((a[n+((c[m>>2]|0)+s|0)|0]|0)==(a[s+123264|0]|0)){s=s+1|0}else{break L5103}}if((s|0)!=1){break}c[13898]=j+1;iI();m=c[18070]|0;n=c[m>>2]|0;l=c[18066]|0;if((n|0)<(l|0)){t=m;u=n}else{n=db(m,(l<<5)+4808|0,168072)|0;l=n;c[18070]=l;c[18066]=(c[18066]|0)+150;t=l;u=c[n>>2]|0}c[t+8+(u<<5)>>2]=10;n=c[18070]|0;c[n>>2]=(c[n>>2]|0)+1;i=b;return}}while(0);if(!k){break}o=c[1054]|0;if((a[o+(j*40&-1)|0]&1)!=0){n=c[o+(j*40&-1)+36>>2]|0;l=o+(j*40&-1)+32|0;o=c[10036]|0;m=0;while(1){if((m|0)>=(n|0)){p=3789;break}if((a[o+((c[l>>2]|0)+m|0)|0]|0)==(a[m+202112|0]|0)){m=m+1|0}else{break}}do{if((p|0)==3789){if((m|0)!=1){break}c[13898]=j+1;iI();l=c[18070]|0;o=c[l>>2]|0;n=c[18066]|0;if((o|0)<(n|0)){w=l;x=o}else{o=db(l,(n<<5)+4808|0,168072)|0;n=o;c[18070]=n;c[18066]=(c[18066]|0)+150;w=n;x=c[o>>2]|0}c[w+8+(x<<5)>>2]=11;o=c[18070]|0;c[o>>2]=(c[o>>2]|0)+1;i=b;return}}while(0);if(!k){break}}m=c[1054]|0;if((a[m+(j*40&-1)|0]&1)==0){break}o=c[m+(j*40&-1)+36>>2]|0;n=m+(j*40&-1)+32|0;m=c[10036]|0;l=0;while(1){if((l|0)>=(o|0)){break}if((a[m+((c[n>>2]|0)+l|0)|0]|0)==(a[l+129568|0]|0)){l=l+1|0}else{break L5087}}if((l|0)!=1){break}c[13898]=j+1;iI();i=b;return}}while(0);x=f|0;f=g|0;g=h|0;L5138:do{if(k){h=c[1054]|0;if((a[h+(j*40&-1)|0]&1)!=0){w=c[h+(j*40&-1)+36>>2]|0;u=h+(j*40&-1)+32|0;h=c[10036]|0;t=0;while(1){if((t|0)>=(w|0)){p=3805;break}if((a[h+((c[u>>2]|0)+t|0)|0]|0)==(a[t+199040|0]|0)){t=t+1|0}else{break}}do{if((p|0)==3805){if((t|0)!=1){break}c[13898]=j+1;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}u=c[13898]|0;h=c[8272]|0;L5151:do{if((h|0)>(u|0)){w=u;l=h;while(1){r=c[1054]|0;if((a[r+(w*40&-1)|0]&1)==0){y=w;z=l;break L5151}q=c[r+(w*40&-1)+36>>2]|0;n=r+(w*40&-1)+32|0;r=c[10036]|0;m=0;while(1){if((m|0)>=(q|0)){break}if((a[r+((c[n>>2]|0)+m|0)|0]|0)==(a[m+148464|0]|0)){m=m+1|0}else{y=w;z=l;break L5151}}if((m|0)!=1){y=w;z=l;break L5151}c[13898]=w+1;n=c[18070]|0;r=c[n>>2]|0;q=c[18066]|0;if((r|0)<(q|0)){A=n;B=r}else{r=db(n,(q<<5)+4808|0,168072)|0;q=r;c[18070]=q;c[18066]=(c[18066]|0)+150;A=q;B=c[r>>2]|0}c[A+8+(B<<5)>>2]=5;r=c[18070]|0;c[r>>2]=(c[r>>2]|0)+1;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}r=c[13898]|0;q=c[8272]|0;if((q|0)>(r|0)){w=r;l=q}else{y=r;z=q;break}}}else{y=u;z=h}}while(0);if((z|0)<=(y|0)){uf(y,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}h=c[1054]|0;if((a[h+(y*40&-1)|0]&1)==0){uf(y,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}u=c[h+(y*40&-1)+36>>2]|0;s=h+(y*40&-1)+32|0;h=c[10036]|0;l=0;while(1){if((l|0)>=(u|0)){break}if((a[h+((c[s>>2]|0)+l|0)|0]|0)==(a[l+131272|0]|0)){l=l+1|0}else{p=4123;break}}if((p|0)==4123){uf(y,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((l|0)==1){c[13898]=y+1;break L5138}else{uf(y,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if(!k){p=3828;break}}t=c[1054]|0;s=a[t+(j*40&-1)|0]|0;if((s&1)==0){C=t;D=s;p=3843;break}h=c[t+(j*40&-1)+36>>2]|0;u=t+(j*40&-1)+32|0;w=c[10036]|0;q=0;while(1){if((q|0)>=(h|0)){break}if((a[w+((c[u>>2]|0)+q|0)|0]|0)==(a[q+180784|0]|0)){q=q+1|0}else{C=t;D=s;p=3843;break L5138}}if((q|0)!=1){C=t;D=s;p=3843;break}u=j+1|0;c[13898]=u;if((a[t+(u*40&-1)|0]&1)!=0){uf(u,114232,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}w=j+2|0;c[13898]=w;h=t+(u*40&-1)+8|0;r=h;n=c[h>>2]|0;h=c[r+4>>2]|0;o=c[t+(u*40&-1)+16>>2]|0;u=r+12|0;c[x>>2]=c[u>>2];c[x+4>>2]=c[u+4>>2];c[x+8>>2]=c[u+8>>2];if((n|0)!=1|(o|0)<0){uf(w,110760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[18068]|0)<(o|0)){c[18068]=o}w=c[18070]|0;n=c[w>>2]|0;u=c[18066]|0;if((n|0)<(u|0)){E=w;F=n}else{n=db(w,(u<<5)+4808|0,168072)|0;u=n;c[18070]=u;c[18066]=(c[18066]|0)+150;E=u;F=c[n>>2]|0}c[E+8+(F<<5)>>2]=31;n=c[18070]|0;u=c[n>>2]|0;c[n>>2]=u+1;n=c[18070]|0;w=n+8+(u<<5)+8|0;r=w;c[w>>2]=1;c[r+4>>2]=h;c[n+8+(u<<5)+16>>2]=o;o=r+12|0;c[o>>2]=c[x>>2];c[o+4>>2]=c[x+4>>2];c[o+8>>2]=c[x+8>>2]}else{p=3828}}while(0);if((p|0)==3828){x=c[1054]|0;C=x;D=a[x+(j*40&-1)|0]|0;p=3843}L5204:do{if((p|0)==3843){if((D&1)==0){x=c[18070]|0;F=c[x>>2]|0;E=c[18066]|0;if((F|0)<(E|0)){G=x;H=F}else{F=db(x,(E<<5)+4808|0,168072)|0;E=F;c[18070]=E;c[18066]=(c[18066]|0)+150;G=E;H=c[F>>2]|0}c[G+8+(H<<5)>>2]=1;F=c[18070]|0;E=c[F>>2]|0;c[F>>2]=E+1;F=(c[18070]|0)+8+(E<<5)+8|0;E=(c[1054]|0)+((c[13898]|0)*40&-1)+8|0;c[F>>2]=c[E>>2];c[F+4>>2]=c[E+4>>2];c[F+8>>2]=c[E+8>>2];c[F+12>>2]=c[E+12>>2];c[F+16>>2]=c[E+16>>2];c[F+20>>2]=c[E+20>>2];c[13898]=(c[13898]|0)+1;break}do{if((bO(d[(c[10036]|0)+(c[C+(j*40&-1)+32>>2]|0)|0]|0|0)|0)==0){E=c[1054]|0;F=c[10036]|0;x=c[13898]|0;if((a[F+(c[E+(j*40&-1)+32>>2]|0)|0]|0)==95){I=x;break}if((a[E+(x*40&-1)|0]&1)==0){uf(x,104392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=a[F+(c[E+(x*40&-1)+32>>2]|0)|0]|0;if(!((k<<24>>24|0)==39|(k<<24>>24|0)==34)){uf(x,104392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}x=c[18070]|0;k=c[x>>2]|0;E=c[18066]|0;if((k|0)<(E|0)){J=x;K=k}else{k=db(x,(E<<5)+4808|0,168072)|0;E=k;c[18070]=E;c[18066]=(c[18066]|0)+150;J=E;K=c[k>>2]|0}c[J+8+(K<<5)>>2]=1;k=c[18070]|0;E=c[k>>2]|0;c[k>>2]=E+1;k=c[18070]|0;c[k+8+(E<<5)+8>>2]=3;x=k+8+(E<<5)+16|0;c[x>>2]=0;E=c[13898]|0;ub(x,E,E);c[13898]=(c[13898]|0)+1;break L5204}else{I=c[13898]|0}}while(0);t=I+1|0;s=c[8272]|0;q=(s|0)>(t|0);L5226:do{if(q){E=c[1054]|0;if((a[E+(t*40&-1)|0]&1)==0){break}x=c[E+(t*40&-1)+36>>2]|0;k=E+(t*40&-1)+32|0;F=c[10036]|0;y=0;while(1){if((y|0)>=(x|0)){break}if((a[F+((c[k>>2]|0)+y|0)|0]|0)==(a[y+199040|0]|0)){y=y+1|0}else{break L5226}}if((y|0)!=1){break}k=(s|0)>(I|0);x=E+(I*40&-1)|0;l=E+(I*40&-1)+36|0;z=E+(I*40&-1)+32|0;B=41;L5234:while(1){A=c[41160+(B<<3)>>2]|0;L5236:do{if(k){if((a[x]&1)==0){break}o=c[l>>2]|0;r=0;while(1){if((r|0)>=(o|0)){break}if((a[F+((c[z>>2]|0)+r|0)|0]|0)==(a[A+r|0]|0)){r=r+1|0}else{break L5236}}if((a[A+r|0]|0)==0){L=B;break L5234}}}while(0);A=B+1|0;if((A|0)==117){L=0;break}else{B=A}}c[13898]=I+2;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}B=c[13898]|0;z=c[8272]|0;F=(z|0)>(B|0);if((L|0)==0){L5250:do{if(F){l=c[1054]|0;x=a[l+(B*40&-1)|0]|0;if((x&1)==0){M=6;N=B;O=z;break}k=c[l+(B*40&-1)+36>>2]|0;E=l+(B*40&-1)+32|0;y=c[10036]|0;A=0;while(1){if((A|0)>=(k|0)){break}if((a[y+((c[E>>2]|0)+A|0)|0]|0)==(a[A+148464|0]|0)){A=A+1|0}else{M=6;N=B;O=z;break L5250}}if((A|0)==1){P=1;Q=B;R=l;S=x}else{M=6;N=B;O=z;break}L5257:while(1){if((S&1)==0){T=P;break}E=c[R+(Q*40&-1)+36>>2]|0;y=R+(Q*40&-1)+32|0;k=c[10036]|0;o=0;while(1){if((o|0)>=(E|0)){break}if((a[k+((c[y>>2]|0)+o|0)|0]|0)==(a[o+148464|0]|0)){o=o+1|0}else{T=P;break L5257}}if((o|0)!=1){T=P;break}y=P+1|0;c[13898]=Q+1;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}k=c[13898]|0;if((c[8272]|0)<=(k|0)){T=y;break}E=c[1054]|0;P=y;Q=k;R=E;S=a[E+(k*40&-1)|0]|0}x=c[18070]|0;l=c[x>>2]|0;A=c[18066]|0;if((l|0)<(A|0)){U=x;V=l}else{l=db(x,(A<<5)+4808|0,168072)|0;A=l;c[18070]=A;c[18066]=(c[18066]|0)+150;U=A;V=c[l>>2]|0}c[U+8+(V<<5)>>2]=1;l=c[18070]|0;A=c[l>>2]|0;c[l>>2]=A+1;l=c[18070]|0;x=l+8+(A<<5)+8|0;c[x>>2]=1;c[l+8+(A<<5)+16>>2]=T;A=x+12|0;c[A>>2]=c[f>>2];c[A+4>>2]=c[f+4>>2];c[A+8>>2]=c[f+8>>2];M=7;N=c[13898]|0;O=c[8272]|0}else{M=6;N=B;O=z}}while(0);if((O|0)<=(N|0)){uf(N,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}A=c[1054]|0;if((a[A+(N*40&-1)|0]&1)==0){uf(N,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}x=c[A+(N*40&-1)+36>>2]|0;l=A+(N*40&-1)+32|0;A=c[10036]|0;k=0;while(1){if((k|0)>=(x|0)){break}if((a[A+((c[l>>2]|0)+k|0)|0]|0)==(a[k+131272|0]|0)){k=k+1|0}else{p=4154;break}}if((p|0)==4154){uf(N,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((k|0)!=1){uf(N,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=N+1;l=iv(I)|0;A=c[18070]|0;x=c[A>>2]|0;E=c[18066]|0;if((x|0)<(E|0)){W=A;X=x}else{x=db(A,(E<<5)+4808|0,168072)|0;E=x;c[18070]=E;c[18066]=(c[18066]|0)+150;W=E;X=c[x>>2]|0}c[W+8+(X<<5)>>2]=M;x=c[18070]|0;E=c[x>>2]|0;c[x>>2]=E+1;c[(c[18070]|0)+8+(E<<5)+8>>2]=l;break L5204}L5293:do{if(F){l=1;E=B;x=z;while(1){A=c[1054]|0;if((a[A+(E*40&-1)|0]&1)==0){Y=E;Z=l;_=x;break L5293}y=c[A+(E*40&-1)+36>>2]|0;r=A+(E*40&-1)+32|0;A=c[10036]|0;m=0;while(1){if((m|0)>=(y|0)){break}if((a[A+((c[r>>2]|0)+m|0)|0]|0)==(a[m+148464|0]|0)){m=m+1|0}else{Y=E;Z=l;_=x;break L5293}}if((m|0)!=1){Y=E;Z=l;_=x;break L5293}c[13898]=E+1;r=l+1|0;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}A=c[13898]|0;y=c[8272]|0;if((y|0)>(A|0)){l=r;E=A;x=y}else{Y=A;Z=r;_=y;break}}}else{Y=B;Z=1;_=z}}while(0);if((_|0)<=(Y|0)){uf(Y,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}z=c[1054]|0;if((a[z+(Y*40&-1)|0]&1)==0){uf(Y,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}B=c[z+(Y*40&-1)+36>>2]|0;F=z+(Y*40&-1)+32|0;z=c[10036]|0;k=0;while(1){if((k|0)>=(B|0)){break}if((a[z+((c[F>>2]|0)+k|0)|0]|0)==(a[k+131272|0]|0)){k=k+1|0}else{p=4166;break}}if((p|0)==4166){uf(Y,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((k|0)!=1){uf(Y,116056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=Y+1;F=c[41160+(L<<3)>>2]|0;if((aY(F|0,107968)|0)==0){z=c[18070]|0;B=c[z>>2]|0;x=c[18066]|0;if((B|0)<(x|0)){$=z;aa=B}else{B=db(z,(x<<5)+4808|0,168072)|0;x=B;c[18070]=x;c[18066]=(c[18066]|0)+150;$=x;aa=c[B>>2]|0}c[$+8+(aa<<5)>>2]=1;B=c[18070]|0;x=c[B>>2]|0;c[B>>2]=x+1;B=c[18070]|0;z=B+8+(x<<5)+8|0;c[z>>2]=1;c[B+8+(x<<5)+16>>2]=Z;x=z+12|0;c[x>>2]=c[f>>2];c[x+4>>2]=c[f+4>>2];c[x+8>>2]=c[f+8>>2]}if((aY(F|0,106992)|0)==0){x=c[18070]|0;z=c[x>>2]|0;B=c[18066]|0;if((z|0)<(B|0)){ab=x;ac=z}else{z=db(x,(B<<5)+4808|0,168072)|0;B=z;c[18070]=B;c[18066]=(c[18066]|0)+150;ab=B;ac=c[z>>2]|0}c[ab+8+(ac<<5)>>2]=1;z=c[18070]|0;B=c[z>>2]|0;c[z>>2]=B+1;z=c[18070]|0;x=z+8+(B<<5)+8|0;c[x>>2]=1;c[z+8+(B<<5)+16>>2]=-1;B=x+12|0;c[B>>2]=c[f>>2];c[B+4>>2]=c[f+4>>2];c[B+8>>2]=c[f+8>>2]}B=(aY(F|0,106056)|0)==0;F=c[18070]|0;do{if(B){x=(c[F>>2]|0)-1|0;z=F+8+(x<<5)|0;E=c[z>>2]|0;if((E|0)==1){l=c[F+8+(x<<5)+8>>2]|0;if((l|0)==3){a[32928]=1;break}else if((l|0)!=1){break}l=c[F+8+(x<<5)+16>>2]|0;if((c[18068]|0)>=(l|0)){break}c[18068]=l;ad=c[z>>2]|0}else{ad=E}if((ad|0)!=0){break}E=c[F+8+(x<<5)+8>>2]|0;if((c[E+16>>2]|0)!=1){break}x=c[E+24>>2]|0;if((c[18068]|0)>=(x|0)){break}c[18068]=x}}while(0);B=c[F>>2]|0;k=c[18066]|0;if((B|0)<(k|0)){ae=F;af=B}else{B=db(F,(k<<5)+4808|0,168072)|0;k=B;c[18070]=k;c[18066]=(c[18066]|0)+150;ae=k;af=c[B>>2]|0}c[ae+8+(af<<5)>>2]=L;B=c[18070]|0;c[B>>2]=(c[B>>2]|0)+1;break L5204}}while(0);B=(s|0)>(I|0);L5350:do{if(B){k=c[1054]|0;if((a[k+(I*40&-1)|0]&1)==0){break}x=c[k+(I*40&-1)+36>>2]|0;E=k+(I*40&-1)+32|0;z=c[10036]|0;l=0;while(1){if((l|0)>=(x|0)){break}if((a[z+((c[E>>2]|0)+l|0)|0]|0)==(a[l+104152|0]|0)){l=l+1|0}else{break L5350}}if((l|0)!=3|q^1){break}if((a[k+(t*40&-1)|0]&1)==0){break}E=c[k+(t*40&-1)+36>>2]|0;x=k+(t*40&-1)+32|0;F=0;while(1){if((F|0)>=(E|0)){break}if((a[z+((c[x>>2]|0)+F|0)|0]|0)==(a[F+78280|0]|0)){F=F+1|0}else{break L5350}}if((F|0)!=1){break}x=I+2|0;c[13898]=x;if((a[k+(x*40&-1)|0]&1)==0){ag=x;uf(ag,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((bO(d[z+(c[k+(x*40&-1)+32>>2]|0)|0]|0|0)|0)==0){E=c[1054]|0;l=c[13898]|0;if((a[(c[10036]|0)+(c[E+(x*40&-1)+32>>2]|0)|0]|0)==95){ah=l;ai=E;break}else{ag=l}uf(ag,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{ah=c[13898]|0;ai=c[1054]|0}}while(0);x=c[ai+(ah*40&-1)+36>>2]|0;k=x+(c[ai+(ah*40&-1)+32>>2]|0)|0;z=db(0,x+1|0,116456)|0;x=c[(c[1054]|0)+(ah*40&-1)+32>>2]|0;L5372:do{if((x|0)<(k|0)){F=x;l=z;while(1){E=a[(c[10036]|0)+F|0]|0;if(E<<24>>24==0){aj=l;break L5372}y=l+1|0;a[l]=E;E=F+1|0;if((E|0)<(k|0)){F=E;l=y}else{aj=y;break}}}else{aj=z}}while(0);a[aj]=0;k=c[13898]|0;x=e|0;l=c[1054]|0;F=c[l+(k*40&-1)+36>>2]|0;y=(F|0)>49?49:F;F=c[10036]|0;E=c[l+(k*40&-1)+32>>2]|0;k=0;while(1){l=k+1|0;a[e+k|0]=a[F+E|0]|0;if((l|0)==(y|0)){break}else{E=E+1|0;k=l}}a[e+y|0]=0;e6(x);k=c[18070]|0;E=c[k>>2]|0;F=c[18066]|0;if((E|0)<(F|0)){ak=k;al=E}else{E=db(k,(F<<5)+4808|0,168072)|0;F=E;c[18070]=F;c[18066]=(c[18066]|0)+150;ak=F;al=c[E>>2]|0}c[ak+8+(al<<5)>>2]=1;E=c[18070]|0;F=c[E>>2]|0;c[E>>2]=F+1;E=c[18070]|0;c[E+8+(F<<5)+8>>2]=3;c[E+8+(F<<5)+16>>2]=z;F=c[13898]|0;E=F+1|0;c[13898]=E;if((c[8272]|0)<=(E|0)){uf(E,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=c[1054]|0;if((a[k+(E*40&-1)|0]&1)==0){uf(E,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=c[k+(E*40&-1)+36>>2]|0;r=k+(E*40&-1)+32|0;k=c[10036]|0;A=0;while(1){if((A|0)>=(l|0)){break}if((a[k+((c[r>>2]|0)+A|0)|0]|0)==(a[A+115e3|0]|0)){A=A+1|0}else{p=4127;break}}if((p|0)==4127){uf(E,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((A|0)!=1){uf(E,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=F+2;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}r=c[13898]|0;if((c[8272]|0)<=(r|0)){uf(r,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=c[1054]|0;if((a[k+(r*40&-1)|0]&1)==0){uf(r,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=c[k+(r*40&-1)+36>>2]|0;z=k+(r*40&-1)+32|0;k=c[10036]|0;x=0;while(1){if((x|0)>=(l|0)){break}if((a[k+((c[z>>2]|0)+x|0)|0]|0)==(a[x+183584|0]|0)){x=x+1|0}else{p=4133;break}}if((p|0)==4133){uf(r,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((x|0)!=1){uf(r,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=r+1;if((iy()|0)==0){c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}z=c[13898]|0;if((c[8272]|0)<=(z|0)){uf(z,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=c[1054]|0;if((a[k+(z*40&-1)|0]&1)==0){uf(z,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=c[k+(z*40&-1)+36>>2]|0;F=k+(z*40&-1)+32|0;k=c[10036]|0;E=0;while(1){if((E|0)>=(l|0)){break}if((a[k+((c[F>>2]|0)+E|0)|0]|0)==(a[E+78864|0]|0)){E=E+1|0}else{p=4162;break}}if((p|0)==4162){uf(z,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((E|0)!=1){uf(z,100792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=z+1;F=c[18070]|0;k=c[18066]|0;c[18070]=0;l=ut(312)|0;do{if((l|0)==0){gk();r=ut(312)|0;if((r|0)!=0){am=r;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=104152,v)|0)}else{am=l}}while(0);c[am>>2]=0;c[am+4>>2]=0;it();l=c[18070]|0;c[am+8>>2]=db(l,c[l>>2]<<5|8,105624)|0;c[am+12>>2]=0;c[am+16>>2]=0;c[am+24>>2]=1;c[am+32>>2]=0;c[am+48>>2]=1;c[am+56>>2]=0;c[am+72>>2]=1;c[am+80>>2]=0;c[am+96>>2]=1;c[am+104>>2]=0;c[am+120>>2]=1;c[am+128>>2]=0;c[am+144>>2]=1;c[am+152>>2]=0;c[am+168>>2]=1;c[am+176>>2]=0;c[am+192>>2]=1;c[am+200>>2]=0;c[am+216>>2]=1;c[am+224>>2]=0;c[am+240>>2]=1;c[am+248>>2]=0;c[am+264>>2]=1;c[am+272>>2]=0;c[am+288>>2]=1;c[am+296>>2]=0;c[18070]=F;c[18066]=k;l=c[F>>2]|0;if((l|0)<(k|0)){an=F;ao=l}else{l=db(F,(k<<5)+4808|0,168072)|0;z=l;c[18070]=z;c[18066]=(c[18066]|0)+150;an=z;ao=c[l>>2]|0}c[an+8+(ao<<5)>>2]=8;l=c[18070]|0;z=c[l>>2]|0;c[l>>2]=z+1;c[(c[18070]|0)+8+(z<<5)+8>>2]=am;break L5204}}while(0);q=c[11670]|0;if((q|0)==0){s=e|0;z=c[1054]|0;l=c[z+(I*40&-1)+36>>2]|0;E=(l|0)>49?49:l;l=c[10036]|0;r=c[z+(I*40&-1)+32>>2]|0;z=0;while(1){x=z+1|0;a[e+z|0]=a[l+r|0]|0;if((x|0)==(E|0)){break}else{r=r+1|0;z=x}}a[e+E|0]=0;z=e6(s)|0;r=c[18070]|0;l=c[r>>2]|0;x=c[18066]|0;if((l|0)<(x|0)){ap=r;aq=l}else{l=db(r,(x<<5)+4808|0,168072)|0;x=l;c[18070]=x;c[18066]=(c[18066]|0)+150;ap=x;aq=c[l>>2]|0}c[ap+8+(aq<<5)>>2]=0;l=c[18070]|0;x=c[l>>2]|0;c[l>>2]=x+1;c[(c[18070]|0)+8+(x<<5)+8>>2]=z;c[13898]=(c[13898]|0)+1;break}L5451:do{if(B){z=c[1054]|0;if((a[z+(I*40&-1)|0]&1)!=0){x=c[z+(I*40&-1)+36>>2]|0;l=z+(I*40&-1)+32|0;z=c[10036]|0;r=0;while(1){if((r|0)>=(x|0)){p=3992;break}if((a[z+((c[l>>2]|0)+r|0)|0]|0)==(a[r+55600|0]|0)){r=r+1|0}else{break}}do{if((p|0)==3992){if((a[r+55600|0]|0)!=0){break}c[13898]=t;l=c[18070]|0;z=c[l>>2]|0;x=c[18066]|0;if((z|0)<(x|0)){ar=l;as=z}else{z=db(l,(x<<5)+4808|0,168072)|0;x=z;c[18070]=x;c[18066]=(c[18066]|0)+150;ar=x;as=c[z>>2]|0}c[ar+8+(as<<5)>>2]=2;z=c[18070]|0;x=c[z>>2]|0;c[z>>2]=x+1;c[(c[18070]|0)+8+(x<<5)+8>>2]=q;break L5204}}while(0);if(!B){p=3997;break}}r=c[1054]|0;if((a[r+(I*40&-1)|0]&1)==0){at=r;break}x=c[r+(I*40&-1)+36>>2]|0;z=r+(I*40&-1)+32|0;l=c[10036]|0;k=0;while(1){if((k|0)>=(x|0)){break}if((a[l+((c[z>>2]|0)+k|0)|0]|0)==(a[k+55651|0]|0)){k=k+1|0}else{at=r;break L5451}}if((a[k+55651|0]|0)!=0){at=r;break}c[13898]=t;z=c[18070]|0;l=c[z>>2]|0;x=c[18066]|0;if((l|0)<(x|0)){au=z;av=l}else{l=db(z,(x<<5)+4808|0,168072)|0;x=l;c[18070]=x;c[18066]=(c[18066]|0)+150;au=x;av=c[l>>2]|0}c[au+8+(av<<5)>>2]=3;l=c[18070]|0;x=c[l>>2]|0;c[l>>2]=x+1;c[(c[18070]|0)+8+(x<<5)+8>>2]=q;break L5204}else{p=3997}}while(0);if((p|0)==3997){at=c[1054]|0}q=c[10036]|0;s=2;L5478:while(1){L5480:do{if(B){if((a[at+(I*40&-1)|0]&1)==0){break}E=c[at+(I*40&-1)+36>>2]|0;x=at+(I*40&-1)+32|0;l=0;while(1){if((l|0)>=(E|0)){break}if((a[q+((c[x>>2]|0)+l|0)|0]|0)==(a[55600+(s*51&-1)+l|0]|0)){l=l+1|0}else{break L5480}}if((a[55600+(s*51&-1)+l|0]|0)==0){p=4013;break L5478}}}while(0);r=s+1|0;if((r|0)<12){s=r}else{p=4019;break}}if((p|0)==4019){B=e|0;r=c[at+(I*40&-1)+36>>2]|0;k=(r|0)>49?49:r;r=c[at+(I*40&-1)+32>>2]|0;x=0;while(1){E=x+1|0;a[e+x|0]=a[q+r|0]|0;if((E|0)==(k|0)){break}else{r=r+1|0;x=E}}a[e+k|0]=0;x=e6(B)|0;r=c[18070]|0;q=c[r>>2]|0;E=c[18066]|0;if((q|0)<(E|0)){aw=r;ax=q}else{q=db(r,(E<<5)+4808|0,168072)|0;E=q;c[18070]=E;c[18066]=(c[18066]|0)+150;aw=E;ax=c[q>>2]|0}c[aw+8+(ax<<5)>>2]=0;q=c[18070]|0;E=c[q>>2]|0;c[q>>2]=E+1;c[(c[18070]|0)+8+(E<<5)+8>>2]=x;c[13898]=(c[13898]|0)+1;break}else if((p|0)==4013){c[13898]=t;x=c[18070]|0;E=c[x>>2]|0;q=c[18066]|0;if((E|0)<(q|0)){ay=x;az=E}else{E=db(x,(q<<5)+4808|0,168072)|0;q=E;c[18070]=q;c[18066]=(c[18066]|0)+150;ay=q;az=c[E>>2]|0}c[ay+8+(az<<5)>>2]=1;E=c[18070]|0;q=c[E>>2]|0;c[E>>2]=q+1;E=c[18070]|0;x=E+8+(q<<5)+8|0;c[x>>2]=1;c[E+8+(q<<5)+16>>2]=s;q=x+12|0;c[q>>2]=c[g>>2];c[q+4>>2]=c[g+4>>2];c[q+8>>2]=c[g+8>>2];q=c[11670]|0;x=c[18070]|0;E=c[x>>2]|0;r=c[18066]|0;if((E|0)<(r|0)){aA=x;aB=E}else{E=db(x,(r<<5)+4808|0,168072)|0;r=E;c[18070]=r;c[18066]=(c[18066]|0)+150;aA=r;aB=c[E>>2]|0}c[aA+8+(aB<<5)>>2]=4;E=c[18070]|0;r=c[E>>2]|0;c[E>>2]=r+1;c[(c[18070]|0)+8+(r<<5)+8>>2]=q;break}}}while(0);aB=c[13898]|0;aA=c[8272]|0;L5504:do{if((aA|0)>(aB|0)){g=aB;az=aA;while(1){ay=c[1054]|0;if((a[ay+(g*40&-1)|0]&1)==0){aC=g;aD=az;break L5504}ax=c[ay+(g*40&-1)+36>>2]|0;aw=ay+(g*40&-1)+32|0;ay=c[10036]|0;e=0;while(1){if((e|0)>=(ax|0)){break}if((a[ay+((c[aw>>2]|0)+e|0)|0]|0)==(a[e+124624|0]|0)){e=e+1|0}else{aC=g;aD=az;break L5504}}if((e|0)!=1){aC=g;aD=az;break L5504}c[13898]=g+1;aw=c[18070]|0;ay=c[aw>>2]|0;ax=c[18066]|0;if((ay|0)<(ax|0)){aE=aw;aF=ay}else{ay=db(aw,(ax<<5)+4808|0,168072)|0;ax=ay;c[18070]=ax;c[18066]=(c[18066]|0)+150;aE=ax;aF=c[ay>>2]|0}c[aE+8+(aF<<5)>>2]=29;ay=c[18070]|0;c[ay>>2]=(c[ay>>2]|0)+1;ay=c[13898]|0;ax=c[8272]|0;if((ax|0)>(ay|0)){g=ay;az=ax}else{aC=ay;aD=ax;break}}}else{aC=aB;aD=aA}}while(0);L5517:do{if((aD|0)>(aC|0)){aA=c[1054]|0;if((a[aA+(aC*40&-1)|0]&1)==0){aG=aC;aH=aD;break}aB=c[aA+(aC*40&-1)+36>>2]|0;aF=aA+(aC*40&-1)+32|0;aA=c[10036]|0;aE=0;while(1){if((aE|0)>=(aB|0)){break}if((a[aA+((c[aF>>2]|0)+aE|0)|0]|0)==(a[aE+103912|0]|0)){aE=aE+1|0}else{aG=aC;aH=aD;break L5517}}if((aE|0)!=2){aG=aC;aH=aD;break}c[13898]=aC+1;iI();aF=c[18070]|0;aA=c[aF>>2]|0;aB=c[18066]|0;if((aA|0)<(aB|0)){aI=aF;aJ=aA}else{aA=db(aF,(aB<<5)+4808|0,168072)|0;aB=aA;c[18070]=aB;c[18066]=(c[18066]|0)+150;aI=aB;aJ=c[aA>>2]|0}c[aI+8+(aJ<<5)>>2]=28;aA=c[18070]|0;c[aA>>2]=(c[aA>>2]|0)+1;aG=c[13898]|0;aH=c[8272]|0}else{aG=aC;aH=aD}}while(0);if((aH|0)<=(aG|0)){i=b;return}aD=c[1054]|0;if((a[aD+(aG*40&-1)|0]&1)==0){i=b;return}aC=c[aD+(aG*40&-1)+36>>2]|0;aJ=aD+(aG*40&-1)+32|0;aI=c[10036]|0;aA=0;while(1){if((aA|0)>=(aC|0)){break}if((a[aI+((c[aJ>>2]|0)+aA|0)|0]|0)==(a[aA+78280|0]|0)){aA=aA+1|0}else{p=4147;break}}if((p|0)==4147){i=b;return}if((aA|0)!=1){i=b;return}aA=aG+1|0;c[13898]=aA;aG=(aH|0)>(aA|0);L5544:do{if(aG){do{if((a[aD+(aA*40&-1)|0]&1)==0){p=4066}else{aH=c[aD+(aA*40&-1)+36>>2]|0;aJ=aD+(aA*40&-1)+32|0;aC=0;while(1){if((aC|0)>=(aH|0)){p=4064;break}if((a[aI+((c[aJ>>2]|0)+aC|0)|0]|0)==(a[aC+134808|0]|0)){aC=aC+1|0}else{break}}if((p|0)==4064){if((aC|0)==1){break}}if(aG){p=4066}else{p=4080;break L5544}}}while(0);if((p|0)==4066){if((a[aD+(aA*40&-1)|0]&1)==0){p=4080;break}aE=c[aD+(aA*40&-1)+36>>2]|0;aJ=aD+(aA*40&-1)+32|0;aH=0;while(1){if((aH|0)>=(aE|0)){break}if((a[aI+((c[aJ>>2]|0)+aH|0)|0]|0)==(a[aH+183584|0]|0)){aH=aH+1|0}else{p=4080;break L5544}}if((aH|0)!=1){p=4080;break}}aJ=c[18070]|0;aE=c[aJ>>2]|0;e=c[18066]|0;if((aE|0)<(e|0)){aK=aJ;aL=aE}else{aE=db(aJ,(e<<5)+4808|0,168072)|0;e=aE;c[18070]=e;c[18066]=(c[18066]|0)+150;aK=e;aL=c[aE>>2]|0}c[aK+8+(aL<<5)>>2]=1;aE=c[18070]|0;e=c[aE>>2]|0;c[aE>>2]=e+1;aE=c[18070]|0;c[aE+8+(e<<5)+8>>2]=1;c[aE+8+(e<<5)+16>>2]=1;e=c[13898]|0;if((c[8272]|0)<=(e|0)){break}aE=c[1054]|0;if((a[aE+(e*40&-1)|0]&1)==0){break}aJ=c[aE+(e*40&-1)+36>>2]|0;aB=aE+(e*40&-1)+32|0;aE=c[10036]|0;aF=0;while(1){if((aF|0)>=(aJ|0)){break}if((a[aE+((c[aB>>2]|0)+aF|0)|0]|0)==(a[aF+134808|0]|0)){aF=aF+1|0}else{break L5544}}if((aF|0)!=1){break}c[13898]=e+1}else{p=4080}}while(0);do{if((p|0)==4080){if((iy()|0)!=0){break}c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}}while(0);aL=c[13898]|0;aK=c[8272]|0;if((aK|0)<=(aL|0)){uf(aL,103536,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}aI=c[1054]|0;if((a[aI+(aL*40&-1)|0]&1)==0){uf(aL,103536,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}aA=c[aI+(aL*40&-1)+36>>2]|0;aD=aI+(aL*40&-1)+32|0;aG=c[10036]|0;aB=0;while(1){if((aB|0)>=(aA|0)){break}if((a[aG+((c[aD>>2]|0)+aB|0)|0]|0)==(a[aB+183584|0]|0)){aB=aB+1|0}else{p=4137;break}}if((p|0)==4137){uf(aL,103536,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((aB|0)!=1){uf(aL,103536,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}aB=aL+1|0;c[13898]=aB;aL=(aK|0)>(aB|0);L5591:do{if(aL){do{if((a[aI+(aB*40&-1)|0]&1)==0){p=4096}else{aK=c[aI+(aB*40&-1)+36>>2]|0;aD=aI+(aB*40&-1)+32|0;aA=0;while(1){if((aA|0)>=(aK|0)){p=4094;break}if((a[aG+((c[aD>>2]|0)+aA|0)|0]|0)==(a[aA+134808|0]|0)){aA=aA+1|0}else{break}}if((p|0)==4094){if((aA|0)==1){break}}if(aL){p=4096}else{p=4110;break L5591}}}while(0);if((p|0)==4096){if((a[aI+(aB*40&-1)|0]&1)==0){p=4110;break}e=c[aI+(aB*40&-1)+36>>2]|0;aF=aI+(aB*40&-1)+32|0;aD=0;while(1){if((aD|0)>=(e|0)){break}if((a[aG+((c[aF>>2]|0)+aD|0)|0]|0)==(a[aD+78864|0]|0)){aD=aD+1|0}else{p=4110;break L5591}}if((aD|0)!=1){p=4110;break}}aF=c[18070]|0;e=c[aF>>2]|0;aK=c[18066]|0;if((e|0)<(aK|0)){aM=aF;aN=e}else{e=db(aF,(aK<<5)+4808|0,168072)|0;aK=e;c[18070]=aK;c[18066]=(c[18066]|0)+150;aM=aK;aN=c[e>>2]|0}c[aM+8+(aN<<5)>>2]=1;e=c[18070]|0;aK=c[e>>2]|0;c[e>>2]=aK+1;e=c[18070]|0;c[e+8+(aK<<5)+8>>2]=1;c[e+8+(aK<<5)+16>>2]=65535;aK=c[13898]|0;if((c[8272]|0)<=(aK|0)){break}e=c[1054]|0;if((a[e+(aK*40&-1)|0]&1)==0){break}aF=c[e+(aK*40&-1)+36>>2]|0;aC=e+(aK*40&-1)+32|0;e=c[10036]|0;aE=0;while(1){if((aE|0)>=(aF|0)){break}if((a[e+((c[aC>>2]|0)+aE|0)|0]|0)==(a[aE+134808|0]|0)){aE=aE+1|0}else{break L5591}}if((aE|0)!=1){break}c[13898]=aK+1}else{p=4110}}while(0);do{if((p|0)==4110){if((iy()|0)!=0){break}c[8230]=(c[8230]|0)+1;iz();iC();c[8230]=(c[8230]|0)-1}}while(0);aN=c[13898]|0;if((c[8272]|0)<=(aN|0)){uf(aN,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}aM=c[1054]|0;if((a[aM+(aN*40&-1)|0]&1)==0){uf(aN,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}aG=c[aM+(aN*40&-1)+36>>2]|0;aB=aM+(aN*40&-1)+32|0;aM=c[10036]|0;aI=0;while(1){if((aI|0)>=(aG|0)){break}if((a[aM+((c[aB>>2]|0)+aI|0)|0]|0)==(a[aI+78864|0]|0)){aI=aI+1|0}else{p=4141;break}}if((p|0)==4141){uf(aN,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((aI|0)!=1){uf(aN,101328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=aN+1;aN=c[18070]|0;aI=c[aN>>2]|0;p=c[18066]|0;if((aI|0)<(p|0)){aO=aN;aP=aI}else{aI=db(aN,(p<<5)+4808|0,168072)|0;p=aI;c[18070]=p;c[18066]=(c[18066]|0)+150;aO=p;aP=c[aI>>2]|0}c[aO+8+(aP<<5)>>2]=35;aP=c[18070]|0;c[aP>>2]=(c[aP>>2]|0)+1;i=b;return}function iJ(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;L5643:while(1){b=c[13898]|0;d=(c[8272]|0)>(b|0);if(!d){e=4202;break}f=c[1054]|0;if((a[f+(b*40&-1)|0]&1)!=0){g=c[f+(b*40&-1)+36>>2]|0;h=f+(b*40&-1)+32|0;f=c[10036]|0;i=0;while(1){if((i|0)>=(g|0)){e=4174;break}if((a[f+((c[h>>2]|0)+i|0)|0]|0)==(a[i+134808|0]|0)){i=i+1|0}else{break}}do{if((e|0)==4174){e=0;if((i|0)!=1){break}c[13898]=b+1;iI();h=c[18070]|0;f=c[h>>2]|0;g=c[18066]|0;if((f|0)<(g|0)){j=h;k=f}else{f=db(h,(g<<5)+4808|0,168072)|0;g=f;c[18070]=g;c[18066]=(c[18066]|0)+150;j=g;k=c[f>>2]|0}c[j+8+(k<<5)>>2]=25;f=c[18070]|0;c[f>>2]=(c[f>>2]|0)+1;continue L5643}}while(0);if(!d){e=4198;break}}i=c[1054]|0;L5659:do{if((a[i+(b*40&-1)|0]&1)!=0){f=c[i+(b*40&-1)+36>>2]|0;g=i+(b*40&-1)+32|0;h=c[10036]|0;l=0;while(1){if((l|0)>=(f|0)){break}if((a[h+((c[g>>2]|0)+l|0)|0]|0)==(a[l+126944|0]|0)){l=l+1|0}else{break L5659}}if((l|0)!=1){break}c[13898]=b+1;iI();g=c[18070]|0;h=c[g>>2]|0;f=c[18066]|0;if((h|0)<(f|0)){m=g;n=h}else{h=db(g,(f<<5)+4808|0,168072)|0;f=h;c[18070]=f;c[18066]=(c[18066]|0)+150;m=f;n=c[h>>2]|0}c[m+8+(n<<5)>>2]=26;h=c[18070]|0;c[h>>2]=(c[h>>2]|0)+1;continue L5643}}while(0);if(!d){e=4199;break}i=c[1054]|0;if((a[i+(b*40&-1)|0]&1)==0){e=4200;break}h=c[i+(b*40&-1)+36>>2]|0;f=i+(b*40&-1)+32|0;i=c[10036]|0;g=0;while(1){if((g|0)>=(h|0)){break}if((a[i+((c[f>>2]|0)+g|0)|0]|0)==(a[g+191528|0]|0)){g=g+1|0}else{e=4201;break L5643}}if((g|0)!=1){e=4197;break}c[13898]=b+1;iI();f=c[18070]|0;i=c[f>>2]|0;h=c[18066]|0;if((i|0)<(h|0)){o=f;p=i}else{i=db(f,(h<<5)+4808|0,168072)|0;h=i;c[18070]=h;c[18066]=(c[18066]|0)+150;o=h;p=c[i>>2]|0}c[o+8+(p<<5)>>2]=27;i=c[18070]|0;c[i>>2]=(c[i>>2]|0)+1}if((e|0)==4201){return}else if((e|0)==4202){return}else if((e|0)==4198){return}else if((e|0)==4197){return}else if((e|0)==4199){return}else if((e|0)==4200){return}}function iK(){var b=0,d=0;b=i;do{if((c[258]|0)==0){d=bU(172032)|0;if((d|0)!=0){c[258]=bP(d|0)|0;break}if((a[37400]&1)==0){break}uh(-1,168056,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if((c[256]|0)!=0){i=b;return}d=bU(163792)|0;c[256]=bP(((d|0)==0?154528:d)|0)|0;i=b;return}function iL(){cv(53560|0,1)}function iM(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,p=0,q=0,t=0,u=0.0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0;e=i;i=i+160|0;f=1;g=0;j=i;i=i+168|0;c[j>>2]=0;while(1)switch(f|0){case 1:k=e|0;if((b|0)>1){l=1;f=2;break}else{f=16;break};case 2:p=c[d+(l<<2)>>2]|0;if((p|0)==0){f=15;break}else{f=3;break};case 3:q=aw(8,p|0,183208)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=5;break}else{f=4;break};case 4:q=aw(8,p|0,214328)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=5;break}else{f=6;break};case 5:aw(26,158880,(v=i,i=i+16|0,c[v>>2]=40152,c[v+8>>2]=40160,v)|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;t=0;f=81;break;case 6:q=aw(8,p|0,135600)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=8;break}else{f=7;break};case 7:q=aw(8,p|0,122640)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=8;break}else{f=9;break};case 8:aw(26,105384,(v=i,i=i+16|0,c[v>>2]=40152,c[v+8>>2]=40160,v)|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;t=0;f=81;break;case 9:q=aq(2,p|0,98264,2)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=11;break}else{f=10;break};case 10:q=aw(8,p|0,92432)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=11;break}else{f=12;break};case 11:a[32096]=1;f=15;break;case 12:q=aq(2,p|0,84592,2)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=14;break}else{f=13;break};case 13:q=aw(8,p|0,76592)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=14;break}else{f=15;break};case 14:a[21672]=1;f=15;break;case 15:q=l+1|0;if((q|0)<(b|0)){l=q;f=2;break}else{f=16;break};case 16:an(88,c[m>>2]|0,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;q=au(2,c[n>>2]|0,0,1,1024)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=18;break}else{f=17;break};case 17:au(4,224136,28,1,c[m>>2]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;f=18;break;case 18:au(2,c[o>>2]|0,0,2,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;c[10030]=c[n>>2];c[500]=2;h[251]=3.141592653589793;h[252]=0.0;q=ao(4,133568)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;c[506]=q;u=+av(4,133568,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;c[q+16>>2]=2;h[q+24>>3]=u;h[q+32>>3]=0.0;a[(c[506]|0)+8|0]=0;as(384);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;as(44);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;q=ao(56,1)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=19;break}else{w=q;f=21;break};case 19:as(424);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;q=ao(56,1)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((q|0)==0){f=20;break}else{w=q;f=21;break};case 20:ar(6,-1|0,225296,(v=i,i=i+8|0,c[v>>2]=116456,v)|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;return 0;case 21:c[6924]=w;a[w]=0;a[37400]=0;as(108);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((c[3524]|0)==0){f=25;break}else{f=22;break};case 22:am(98,c[6958]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;am(98,c[6956]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;x=c[c[3524]>>2]|0;if((x|0)==0){y=0;f=24;break}else{f=23;break};case 23:q=ao(162,x|0)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;y=q;f=24;break;case 24:c[6958]=y;q=ao(162,13048)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;c[6956]=q;f=25;break;case 25:ao(102,92);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;q=ao(58,c[o>>2]|0)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;z=ao(18,q|0)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;a[37400]=(z|0)!=0&1;A=1;f=26;break;case 26:if((A|0)<(b|0)){f=27;break}else{f=30;break};case 27:B=c[d+(A<<2)>>2]|0;if((a[B]|0)==45){f=28;break}else{f=29;break};case 28:if((a[B+1|0]|0)==101){f=29;break}else{A=A+1|0;f=26;break};case 29:a[37400]=0;f=30;break;case 30:z=ao(108,k|0)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)>-1){f=31;break}else{f=32;break};case 31:aw(28,33040,k|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;aw(28,33008,k+128|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;f=32;break;case 32:if((a[37400]&1)==0){f=34;break}else{f=33;break};case 33:am(272,c[m>>2]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;f=35;break;case 34:am(272,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;f=35;break;case 35:am(200,3);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;C=uL(53560,f,j)|0;f=82;break;case 82:if((C|0)==0){f=36;break}else{f=42;break};case 36:av(2,2,4);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;av(2,13,1);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;as(418);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;aw(24,1,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;am(98,c[12908]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;aw(10,5,179864);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;aw(10,2,179864);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;D=aw(10,5,0)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((D|0)==0){E=0;f=38;break}else{f=37;break};case 37:z=ao(162,D|0)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;E=z;f=38;break;case 38:c[12908]=E;as(138);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;c[5162]=37;c[5164]=7;c[5165]=5;c[5166]=15;a[20668]=112;c[5172]=0;c[5173]=0;uE(20672,0,13);c[5174]=114;c[5334]=0;c[5256]=0;c[5178]=0;h[2705]=1.5;uD(54016,50688,112);am(92,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((a[21672]&1)==0){f=39;break}else{f=40;break};case 39:am(98,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;f=40;break;case 40:c[10062]=0;a[14168]=1;am(92,2);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;F=c[3524]|0;if((a[37400]&1)!=0&(F|0)!=0){f=41;break}else{G=0;f=48;break};case 41:aq(4,c[m>>2]|0,211496,(v=i,i=i+8|0,c[v>>2]=c[F>>2],v)|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;G=0;f=48;break;case 42:if(a[14168]|0){f=44;break}else{f=43;break};case 43:a[14168]=1;au(4,205272,38,1,c[m>>2]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;f=44;break;case 44:H=a[37400]&1^1;f=45;break;case 45:z=al(2)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if(z){f=45;break}else{f=46;break};case 46:I=H&255;if((a[37400]&1)==0){f=47;break}else{G=I;f=48;break};case 47:if((a[33232]&1)==0){f=49;break}else{G=I;f=48;break};case 48:z=b-1|0;if((z|0)>0){J=b;K=d;L=z;f=59;break}else{f=79;break};case 49:c[8026]=0;if((a[14080]&1)==0){f=58;break}else{f=50;break};case 50:if(a[13032]|0){f=51;break}else{f=54;break};case 51:M=c[(c[3524]|0)+104>>2]|0;if((M|0)==0){f=53;break}else{f=52;break};case 52:as(M|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;f=53;break;case 53:a[13032]=0;f=54;break;case 54:if(a[14088]|0){f=55;break}else{f=56;break};case 55:as(c[(c[3524]|0)+44>>2]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;a[14088]=0;f=56;break;case 56:if((a[14080]&1)==0){f=58;break}else{f=57;break};case 57:as(c[(c[3524]|0)+40>>2]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;a[14080]=0;c[10028]=0;f=58;break;case 58:am(356,1);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;return 0;case 59:N=K+4|0;c[13898]=0;O=c[N>>2]|0;z=aq(2,O|0,98264,2)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)==0){P=N;Q=L;f=65;break}else{f=60;break};case 60:z=aw(8,O|0,92432)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)==0){P=N;Q=L;f=65;break}else{f=61;break};case 61:z=aw(8,O|0,202112)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)==0){f=62;break}else{f=66;break};case 62:a[37400]=1;f=63;break;case 63:z=al(6)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)==0){f=63;break}else{f=64;break};case 64:a[37400]=0;P=N;Q=L;f=65;break;case 65:z=Q-1|0;if((z|0)>0){J=Q;K=P;L=z;f=59;break}else{f=79;break};case 66:z=aw(8,O|0,196560)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)==0){f=67;break}else{f=72;break};case 67:R=J-2|0;S=K+8|0;if((R|0)<1){f=68;break}else{f=69;break};case 68:au(4,185336,31,1,c[m>>2]|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;t=0;f=81;break;case 69:a[37400]=0;a[33232]=0;T=c[S>>2]|0;if((T|0)==0){U=0;f=71;break}else{f=70;break};case 70:z=ao(162,T|0)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;U=z;f=71;break;case 71:am(150,U|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;P=S;Q=R;f=65;break;case 72:z=aq(2,O|0,84592,2)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)==0){P=N;Q=L;f=65;break}else{f=73;break};case 73:z=aw(8,O|0,76592)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)==0){P=N;Q=L;f=65;break}else{f=74;break};case 74:if((a[O]|0)==45){f=75;break}else{f=76;break};case 75:aq(4,c[m>>2]|0,180120,(v=i,i=i+8|0,c[v>>2]=O,v)|0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;P=N;Q=L;f=65;break;case 76:a[37400]=0;a[33232]=0;V=aw(20,O|0,193632)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;W=c[N>>2]|0;if((W|0)==0){X=0;f=78;break}else{f=77;break};case 77:z=ao(162,W|0)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;X=z;f=78;break;case 78:ar(76,V|0,X|0,0);if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;P=N;Q=L;f=65;break;case 79:if((a[33232]&1)==0){t=G;f=81;break}else{f=80;break};case 80:z=al(6)|0;if((r|0)!=0&(s|0)!=0){g=uM(c[r>>2]|0,j)|0;if((g|0)>0){f=-1;break}else return 0}r=s=0;if((z|0)==0){f=80;break}else{t=G;f=81;break};case 81:i=e;return t|0;case-1:if((g|0)==35){C=s;f=82}r=s=0;break}return 0}function iN(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;d=i;if((a[21672]&1)!=0){i=d;return}do{if((b|0)==1){e=0}else if((b|0)==0){f=ut(39)|0;do{if((f|0)==0){gk();g=ut(39)|0;if((g|0)!=0){h=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=141984,v)|0)}else{h=f}}while(0);uD(h|0,74944,29);f=uA(h|0)|0;g=(f|0)==0?0:f-1|0;f=a[h+g|0]|0;if(!((f<<24>>24|0)==47|(f<<24>>24|0)==0)){a[h+(g+1|0)|0]=47;a[h+(g+2|0)|0]=0}uD(h+(uA(h|0)|0)|0,138288,10);j=bF(h|0,193632)|0;k=h;l=4229}else{g=c[258]|0;if(!((b|0)==2&(g|0)!=0)){e=0;break}f=(uA(g|0)|0)+10|0;g=ut(f)|0;do{if((g|0)==0){gk();m=ut(f)|0;if((m|0)!=0){n=m;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=141984,v)|0)}else{n=g}}while(0);uB(n|0,c[258]|0);g=uA(n|0)|0;f=(g|0)==0?0:g-1|0;g=a[n+f|0]|0;if(!((g<<24>>24|0)==47|(g<<24>>24|0)==0)){a[n+(f+1|0)|0]=47;a[n+(f+2|0)|0]=0}uD(n+(uA(n|0)|0)|0,136592,9);j=bF(n|0,193632)|0;k=n;l=4229}}while(0);do{if((l|0)==4229){if((j|0)==0){e=k;break}n=(k|0)!=0?k:136592;if((n|0)==0){o=0}else{o=bP(n|0)|0}hw(j,o,0);if((c[3524]|0)==0){e=k;break}uu(c[6958]|0);uu(c[6956]|0);n=c[c[3524]>>2]|0;if((n|0)==0){p=0}else{p=bP(n|0)|0}c[6958]=p;c[6956]=bP(13048)|0;e=k}}while(0);uu(e);i=d;return}function iO(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;if((a|0)==0){return}else{b=a}while(1){a=c[b>>2]|0;d=b+16|0;uu(c[d>>2]|0);c[d>>2]=0;d=b+320|0;uu(c[d>>2]|0);c[d>>2]=0;d=b+316|0;uu(c[d>>2]|0);c[d>>2]=0;d=b+224|0;e=c[d>>2]|0;if((e|0)!=0){f=c[e+64>>2]|0;if((f|0)==0){g=e}else{uu(f);g=e}while(1){e=c[g+60>>2]|0;if((e|0)!=0){uu(e)}e=c[g+64>>2]|0;if(!((e|0)==0|(e|0)==(f|0))){uu(e)}e=c[g>>2]|0;uu(g);if((e|0)==0){break}else{g=e}}}c[d>>2]=0;f=b+292|0;uu(c[f>>2]|0);c[f>>2]=0;uu(b);if((a|0)==0){break}else{b=a}}return}function iP(b){b=b|0;aV(2,4);aV(8,0);c[8026]=0;do{if((a[14080]&1)!=0){if(a[13032]|0){b=c[(c[3524]|0)+104>>2]|0;if((b|0)!=0){cS[b&511]()}a[13032]=0}if(a[14088]|0){cS[c[(c[3524]|0)+44>>2]&511]();a[14088]=0}if((a[14080]&1)==0){break}cS[c[(c[3524]|0)+40>>2]&511]();a[14080]=0;c[10028]=0}}while(0);aF(10,c[m>>2]|0);iL()}function iQ(b){b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;e=c[b>>2]|0;if((e|0)==0){uf(-1,151280,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[e]|0)!=126){i=d;return}if((a[e+1|0]|0)!=47){i=d;return}f=c[258]|0;if((f|0)==0){uh(-1,147480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=d;return}else{g=uA(e|0)|0;h=db(e,(uA(f|0)|0)+g|0,149488)|0;c[b>>2]=h;uH(h+((uA(c[258]|0)|0)-1|0)|0,h|0,g+1|0);g=c[b>>2]|0;b=c[258]|0;uD(g|0,b|0,uA(b|0)|0);i=d;return}}function iR(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;d=i;e=a+296|0;if((c[e>>2]|0)==(b|0)){i=d;return}f=a+320|0;g=c[f>>2]|0;if((b|0)<=0){uu(g);c[f>>2]=0;c[e>>2]=0;h=a+316|0;uu(c[h>>2]|0);c[h>>2]=0;i=d;return}do{if((g|0)==0){h=b<<6;j=ut(h)|0;do{if((j|0)==0){gk();k=ut(h)|0;if((k|0)!=0){l=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=180104,v)|0)}else{l=j}}while(0);c[f>>2]=l}else{c[f>>2]=db(g,b<<6,214200)|0;j=a+316|0;h=c[j>>2]|0;if((h|0)==0){break}c[j>>2]=db(h,b<<3,158688)|0}}while(0);c[e>>2]=b;i=d;return}function iS(){var d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0,s=0,t=0,u=0,w=0;d=i;i=i+24|0;e=d|0;if((c[3524]|0)==0){uf(c[13898]|0,135392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}a[37384]=0;if(a[20368]|0){a[20368]=0;g[3538]=+g[5090];g[3536]=+g[5088];g[3534]=+g[5086];c[16336]=c[16336]^2;c[17024]=c[17024]^2}do{if((a[32936]&1)!=0){if((aY(24544,122560)|0)!=0){break}b[12272]=116}}while(0);c[16506]=c[16507];h[8255]=+h[8257];h[8256]=+h[8258];h[8261]=8.988465674311579e+307;h[8262]=-8.988465674311579e+307;f=c[16335]|0;c[16334]=f;h[8169]=(f&1|0)==0?+h[8171]:8.988465674311579e+307;h[8170]=(f&2|0)==0?+h[8172]:-8.988465674311579e+307;h[8175]=8.988465674311579e+307;h[8176]=-8.988465674311579e+307;c[17194]=c[17195];h[8599]=+h[8601];h[8600]=+h[8602];h[8605]=8.988465674311579e+307;h[8606]=-8.988465674311579e+307;f=c[17023]|0;c[17022]=f;h[8513]=(f&1|0)==0?+h[8515]:8.988465674311579e+307;h[8514]=(f&2|0)==0?+h[8516]:-8.988465674311579e+307;h[8519]=8.988465674311579e+307;h[8520]=-8.988465674311579e+307;c[17538]=c[17539];h[8771]=+h[8773];h[8772]=+h[8774];h[8777]=8.988465674311579e+307;h[8778]=-8.988465674311579e+307;f=c[17367]|0;c[17366]=f;h[8685]=(f&1|0)==0?+h[8687]:8.988465674311579e+307;h[8686]=(f&2|0)==0?+h[8688]:-8.988465674311579e+307;h[8691]=8.988465674311579e+307;h[8692]=-8.988465674311579e+307;f=c[16679]|0;c[16678]=f;h[8341]=(f&1|0)==0?+h[8343]:8.988465674311579e+307;h[8342]=(f&2|0)==0?+h[8344]:-8.988465674311579e+307;h[8347]=8.988465674311579e+307;h[8348]=-8.988465674311579e+307;L5811:do{if((a[37408]&1)!=0){f=c[13898]|0;if((c[8272]|0)>(f|0)){j=f}else{break}do{f=c[1054]|0;if((a[f+(j*40&-1)|0]&1)==0){break L5811}k=c[f+(j*40&-1)+36>>2]|0;l=f+(j*40&-1)+32|0;f=c[10036]|0;m=0;while(1){if((m|0)>=(k|0)){break}if((a[f+((c[l>>2]|0)+m|0)|0]|0)==(a[m+78280|0]|0)){m=m+1|0}else{break L5811}}if((m|0)!=1){break L5811}dn();j=c[13898]|0;}while((c[8272]|0)>(j|0))}}while(0);if((a[32936]&1)==0){n=(a[30528]&1)!=0?8:2}else{n=8}j=dr(n,-1)|0;if((a[32936]&1)==0){if((a[30528]&1)!=0){o=4309}}else{o=4309}if((o|0)==4309){dq(2)}do{if((c[16506]&3|0)==0){p=+h[8256];q=+h[8255];if(p>=q){o=4313;break}h[8255]=p;h[8256]=q;r=1}else{o=4313}}while(0);if((o|0)==4313){r=(c[16508]|0)>>>1&1}a[66036]=r;dq(1);do{if((c[16334]&3|0)==0){q=+h[8170];p=+h[8169];if(q>=p){o=4317;break}h[8169]=q;h[8170]=p;s=1}else{o=4317}}while(0);if((o|0)==4317){s=(c[16336]|0)>>>1&1}a[65348]=s;dq(6);do{if((c[17194]&3|0)==0){p=+h[8600];q=+h[8599];if(p>=q){o=4321;break}h[8599]=p;h[8600]=q;t=1}else{o=4321}}while(0);if((o|0)==4321){t=(c[17196]|0)>>>1&1}a[68788]=t;dq(5);do{if((c[17022]&3|0)==0){q=+h[8514];p=+h[8513];if(q>=p){o=4325;break}h[8513]=q;h[8514]=p;u=1}else{o=4325}}while(0);if((o|0)==4325){u=(c[17024]|0)>>>1&1}a[68100]=u;u=e;t=e+16|0;s=0;do{r=64944+(s*688&-1)|0;n=c[r>>2]|0;if((n|0)==0){o=4334}else{c[u>>2]=c[7254];c[u+4>>2]=c[7255];c[u+8>>2]=c[7256];c[u+12>>2]=c[7257];c[u+16>>2]=c[7258];c[u+20>>2]=c[7259];l=e;f=n;L5858:while(1){n=f;while(1){if((c[n+12>>2]|0)>=0){break}uu(c[n+8>>2]|0);k=c[n+16>>2]|0;uu(n);if((k|0)==0){w=l;break L5858}else{n=k}}c[l+16>>2]=n;m=c[n+16>>2]|0;if((m|0)==0){w=n;break}else{l=n;f=m}}c[w+16>>2]=0;f=c[t>>2]|0;c[r>>2]=f;if((f|0)==0){o=4334}}do{if((o|0)==4334){o=0;f=64920+(s*688&-1)|0;if((c[f>>2]|0)!=3){break}c[f>>2]=1}}while(0);s=s+1|0;}while((s|0)<11);if((j|0)<=-1){uB(55600,24544);iT();i=d;return}s=c[1054]|0;o=c[s+(j*40&-1)+36>>2]|0;t=(o|0)>49?49:o;o=c[10036]|0;w=c[s+(j*40&-1)+32>>2]|0;j=0;while(1){s=j+1|0;a[j+55600|0]=a[o+w|0]|0;if((s|0)==(t|0)){break}else{w=w+1|0;j=s}}a[t+55600|0]=0;iT();i=d;return}function iT(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,U=0,V=0,W=0,X=0,Y=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0.0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0.0,aX=0,aZ=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0,bB=0,bC=0,bD=0,bE=0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bN=0,bO=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0,b7=0,b8=0,b9=0,ca=0,cb=0,cc=0,cd=0,ce=0,cf=0,cg=0,ch=0,ci=0,cj=0,ck=0,cl=0,cm=0,cn=0,co=0,cp=0,cq=0,cr=0,cs=0,ct=0,cu=0,cv=0,cw=0,cx=0,cy=0,cz=0,cA=0,cB=0,cC=0,cD=0,cE=0,cF=0,cG=0,cH=0,cI=0,cJ=0,cK=0,cL=0,cM=0,cN=0,cO=0,cP=0,cQ=0,cR=0,cS=0,cT=0,cU=0,cV=0,cW=0,cX=0,cY=0,cZ=0,c_=0,c$=0,c0=0.0,c1=0,c2=0,c3=0,c4=0,c5=0,c6=0,c7=0,c8=0,c9=0,da=0,dd=0,de=0,df=0,dg=0,dh=0,di=0,dj=0,dk=0,dl=0,dm=0,dn=0,dq=0,dr=0,ds=0,dt=0,du=0,dv=0,dw=0,dx=0,dy=0,dz=0,dA=0,dB=0,dC=0,dD=0,dE=0,dF=0,dG=0,dH=0,dI=0,dJ=0,dK=0,dL=0,dM=0,dN=0,dO=0,dP=0,dQ=0,dR=0,dS=0,dU=0,dV=0,dW=0,dX=0,dY=0,dZ=0,d_=0,d$=0,d0=0,d1=0,d2=0,d3=0,d4=0,d5=0,d6=0,d7=0,d8=0,d9=0,ea=0,eb=0,ec=0,ed=0.0,ee=0.0,ef=0.0,eg=0.0,eh=0.0,ei=0.0,ej=0.0,ek=0,el=0.0,em=0.0,en=0.0,eo=0.0,ep=0.0,eq=0.0,er=0.0,es=0.0,et=0,eu=0,ev=0,ew=0,ex=0.0,eA=0,eB=0.0,eE=0.0,eF=0.0,eG=0.0,eH=0,eI=0,eJ=0,eK=0,eN=0.0,eO=0.0,eP=0,eQ=0,eR=0,eS=0.0,eT=0.0,eU=0,eV=0,eW=0,eX=0,eY=0,eZ=0,e_=0,e$=0,e0=0,e1=0,e2=0,e3=0,e5=0,e8=0.0,e9=0,fa=0,fb=0,fc=0,fd=0,fe=0,ff=0,fg=0,fh=0,fi=0,fj=0,fk=0,fl=0.0,fm=0,fn=0,fo=0,fp=0,fq=0,fr=0,fs=0,ft=0,fu=0,fv=0.0,fw=0,fx=0.0,fy=0.0,fz=0,fA=0,fB=0,fC=0,fD=0,fE=0.0,fF=0,fG=0,fH=0,fI=0,fJ=0,fK=0,fL=0,fM=0,fN=0,fO=0,fP=0,fQ=0,fR=0,fT=0.0,fU=0,fV=0,fW=0,fX=0,fY=0,fZ=0,f_=0,f$=0,f0=0,f1=0,f2=0,f4=0,f5=0,f6=0,f7=0,f9=0,ga=0,gb=0.0,gc=0.0,gd=0,ge=0,gf=0,gg=0,gh=0,gi=0,gj=0.0,gl=0.0,gm=0.0,gn=0.0,go=0,gp=0.0,gq=0.0,gr=0.0,gs=0.0,gt=0.0,gu=0.0,gv=0,gw=0.0,gx=0.0,gy=0.0,gz=0.0,gA=0,gB=0,gC=0.0,gD=0,gE=0,gF=0.0,gG=0,gH=0,gI=0.0,gJ=0,gK=0.0,gL=0,gM=0,gN=0,gO=0,gP=0,gQ=0,gR=0,gS=0,gT=0,gU=0,gV=0,gW=0,gX=0,gY=0,gZ=0,g_=0,g$=0,g0=0,g1=0,g2=0.0,g3=0,g4=0,g5=0,g6=0,g7=0,g8=0,g9=0,ha=0,hb=0,hc=0,hd=0,he=0.0,hf=0.0,hg=0,hh=0.0,hi=0.0,hj=0,hm=0,ho=0.0;b=i;i=i+512|0;d=b|0;e=b+8|0;f=b+96|0;g=b+120|0;j=b+144|0;k=b+216|0;l=b+240|0;n=b+264|0;o=b+288|0;p=b+336|0;q=b+368|0;r=b+424|0;s=b+480|0;t=b+488|0;u=c[13898]|0;h[4834]=0.0;f8(38680);w=c[3568]|0;if((w|0)!=0){uu(w)}c[3568]=0;w=o+20|0;c[w>>2]=0;x=o+24|0;c[x>>2]=0;y=o+4|0;c[y>>2]=0;z=o+8|0;c[z>>2]=0;A=c[10814]|0;if((A|0)!=0){iO(A)}c[10814]=0;A=c[12374]|0;a[872]=0;c[7774]=iw()|0;B=c[13898]|0;if((B|0)>=(c[8272]|0)){C=B;uf(C,105216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}D=b+168|0;E=b+64|0;F=e|0;G=e+8|0;H=e+16|0;I=e+24|0;J=e+32|0;K=e+40|0;L=f|0;M=f+8|0;N=r+40|0;O=q;Q=q+4|0;U=q+16|0;V=q+24|0;W=q+8|0;X=q|0;Y=q+40|0;$=q+44|0;aa=q+32|0;ab=g|0;ac=j|0;ad=j+8|0;ae=b+192|0;af=p+8|0;ag=n|0;ah=n+8|0;ai=l|0;aj=l+8|0;ak=k|0;al=k+8|0;am=0;an=-5;ao=1;ap=-1;aq=0.0;ar=0;as=0;at=A;A=0;au=0;av=0;aw=0;ax=0;ay=43256;az=B;L5888:while(1){B=ar;aA=A;aB=az;L5890:while(1){aC=c[1054]|0;aD=(a[aC+(aB*40&-1)|0]&1)==0;L5892:do{if(!aD){aE=c[aC+(aB*40&-1)+36>>2]|0;aF=aC+(aB*40&-1)+32|0;aG=c[10036]|0;aH=0;while(1){if((aH|0)>=(aE|0)){break}if((a[aG+((c[aF>>2]|0)+aH|0)|0]|0)==(a[aH+103664|0]|0)){aH=aH+1|0}else{break L5892}}if((aH|0)==1){aJ=B;aK=aA;aL=am;aM=au;aN=ax;aO=aB;aP=4357;break L5888}}}while(0);if(au){aQ=B}else{aQ=aA?B:aB}aF=c[aC+(aB*40&-1)+36>>2]|0;L5902:do{if((aF|0)>0&(aD^1)){aG=c[10036]|0;aE=0;aR=0;aS=c[aC+(aB*40&-1)+32>>2]|0;while(1){if((a[aE+205224|0]|0)==(a[aG+(aE+aS|0)|0]|0)){aT=aS;aU=aR}else{if((aE|0)!=7){break L5902}aT=aS-1|0;aU=1}aV=aE+1|0;if((aV|0)<(aU+aF|0)){aE=aV;aR=aU;aS=aT}else{break}}if((aU|0)!=0){aP=4369;break L5890}if((aE|0)==12|(aE|0)==6){aP=4369;break L5890}}}while(0);if((t9(aB)|0)==0){aP=4446;break}dT();aF=c[13898]|0;aC=c[8272]|0;aD=(aC|0)>(aF|0);if(!aD){aJ=aQ;aK=1;aL=am;aM=au;aN=ax;aO=aF;aP=4357;break L5888}aS=c[1054]|0;if((a[aS+(aF*40&-1)|0]&1)==0){B=aQ;aA=1;aB=aF;continue}aR=c[aS+(aF*40&-1)+36>>2]|0;aG=aS+(aF*40&-1)+32|0;aS=c[10036]|0;aH=0;while(1){if((aH|0)>=(aR|0)){aP=4445;break}if((a[aS+((c[aG>>2]|0)+aH|0)|0]|0)==(a[aH+148464|0]|0)){aH=aH+1|0}else{break}}if((aP|0)==4445){aP=0;if((aH|0)==1){aW=aq;aX=ap;aZ=ao;a$=an;a0=aF;a1=aC;aP=5304;break}}if(aD){B=aQ;aA=1;aB=aF}else{aJ=aQ;aK=1;aL=am;aM=au;aN=ax;aO=aF;aP=4357;break L5888}}do{if((aP|0)==4446){aP=0;B=am+1|0;c[11670]=31424;aG=iu(0)|0;c[11670]=0;aS=(a[32936]&1)==0;aR=au^1;if((aG|0)==0){aV=aS?au:aR;a2=c[ay>>2]|0;a3=(c[6352]|0)+1|0;if((a2|0)==0){uE(D|0,0,24);a4=ut(328)|0;if((a4|0)==0){gk();a5=ut(328)|0;if((a5|0)==0){aP=4462;break L5888}else{a6=a5}}else{a6=a4}a4=a6;uE(a6|0,0,328);c[a6+296>>2]=(a3|0)<0?0:a3;if((a3|0)>0){a5=a3<<6;a7=ut(a5)|0;if((a7|0)==0){gk();a8=ut(a5)|0;if((a8|0)==0){aP=4466;break L5888}else{a9=a8}}else{a9=a7}c[a6+320>>2]=a9}c[a6+24>>2]=0;c[a6+28>>2]=-2;a7=a6+32|0;c[a7>>2]=0;c[a7+4>>2]=0;h[a6+40>>3]=1.0;h[a6+48>>3]=-2.0;a7=a6+56|0;c[a7>>2]=c[D>>2];c[a7+4>>2]=c[D+4>>2];c[a7+8>>2]=c[D+8>>2];c[a7+12>>2]=c[D+12>>2];c[a7+16>>2]=c[D+16>>2];c[a7+20>>2]=c[D+20>>2];c[a6+80>>2]=-1;c[a6+84>>2]=0;a7=a6+88|0;c[a7>>2]=c[12872];c[a7+4>>2]=c[12873];c[a7+8>>2]=c[12874];c[a7+12>>2]=c[12875];c[a7+16>>2]=c[12876];c[a7+20>>2]=c[12877];c[a7+24>>2]=c[12878];c[a7+28>>2]=c[12879];c[a7+32>>2]=c[12880];c[a7+36>>2]=c[12881];c[a7+40>>2]=c[12882];c[a7+44>>2]=c[12883];c[a7+48>>2]=c[12884];c[a7+52>>2]=c[12885];c[a6+144>>2]=1;h[a6+152>>3]=0.0;c[a6+160>>2]=0;h[a6+168>>3]=15.0;h[a6+176>>3]=90.0;c[a6+184>>2]=0;a7=a6+192|0;c[a7>>2]=c[12372];c[a7+4>>2]=c[12373];c[a7+8>>2]=c[12374];c[a7+12>>2]=c[12375];c[a7+16>>2]=c[12376];c[a7+20>>2]=c[12377];c[a7+24>>2]=c[12378];c[a7+28>>2]=c[12379];c[ay>>2]=a4;ba=a4}else{iR(a2,a3);ba=a2}c[ba+8>>2]=0;c[ba+12>>2]=c[10058];c[ba+240>>2]=0;a[ba+56|0]=0;a2=c[13898]|0;bb=ba;bc=1;bd=aV;be=a2-1|0;bf=0;bg=a2}else{if(!(aS|aR)){aP=4448;break L5888}aR=c[ay>>2]|0;if((aR|0)==0){uE(ae|0,0,24);aS=ut(328)|0;if((aS|0)==0){gk();a2=ut(328)|0;if((a2|0)==0){aP=4452;break L5888}else{bh=a2}}else{bh=aS}aS=bh;uE(bh|0,0,328);c[bh+296>>2]=100;a2=ut(6400)|0;if((a2|0)==0){gk();aV=ut(6400)|0;if((aV|0)==0){aP=4455;break L5888}else{bi=aV}}else{bi=a2}c[bh+320>>2]=bi;c[bh+24>>2]=0;c[bh+28>>2]=-2;a2=bh+32|0;c[a2>>2]=0;c[a2+4>>2]=0;h[bh+40>>3]=1.0;h[bh+48>>3]=-2.0;a2=bh+56|0;c[a2>>2]=c[ae>>2];c[a2+4>>2]=c[ae+4>>2];c[a2+8>>2]=c[ae+8>>2];c[a2+12>>2]=c[ae+12>>2];c[a2+16>>2]=c[ae+16>>2];c[a2+20>>2]=c[ae+20>>2];c[bh+80>>2]=-1;c[bh+84>>2]=0;a2=bh+88|0;c[a2>>2]=c[12872];c[a2+4>>2]=c[12873];c[a2+8>>2]=c[12874];c[a2+12>>2]=c[12875];c[a2+16>>2]=c[12876];c[a2+20>>2]=c[12877];c[a2+24>>2]=c[12878];c[a2+28>>2]=c[12879];c[a2+32>>2]=c[12880];c[a2+36>>2]=c[12881];c[a2+40>>2]=c[12882];c[a2+44>>2]=c[12883];c[a2+48>>2]=c[12884];c[a2+52>>2]=c[12885];c[bh+144>>2]=1;h[bh+152>>3]=0.0;c[bh+160>>2]=0;h[bh+168>>3]=15.0;h[bh+176>>3]=90.0;c[bh+184>>2]=0;a2=bh+192|0;c[a2>>2]=c[12372];c[a2+4>>2]=c[12373];c[a2+8>>2]=c[12374];c[a2+12>>2]=c[12375];c[a2+16>>2]=c[12376];c[a2+20>>2]=c[12377];c[a2+24>>2]=c[12378];c[a2+28>>2]=c[12379];c[ay>>2]=aS;bj=aS}else{bj=aR}c[bj+8>>2]=1;c[bj+12>>2]=c[12890];c[bj+284>>2]=0;c[bj+240>>2]=0;a[bj+56|0]=0;c[11856]=1;aR=ey(aG,7,bj)|0;aG=(c[13898]|0)-1|0;c[bj+4>>2]=aG;bb=bj;bc=ax;bd=au;be=aG;bf=aR;bg=c[13898]|0}c[200]=2;c[144]=1;aR=c[8272]|0;if((bg|0)<(aR|0)){aG=bb+12|0;aS=bb+144|0;a2=bb+152|0;aV=bb+80|0;a3=bb+80|0;a4=bb+84|0;a7=bb+88|0;a8=a7;a5=bb+160|0;bk=bb+168|0;bl=bb+176|0;bm=bb+184|0;bn=aw+1|0;bo=a7|0;bp=bb+92|0;bq=bb+128|0;br=bb+132|0;bs=bb+96|0;bt=bb+120|0;bu=bb+272|0;bv=bb+224|0;bw=bb+64|0;bx=bb+56|0;by=bb+192|0;bz=by|0;bB=bb+24|0;bC=bd^1;bD=bb+240|0;bE=bb+8|0;bF=bb+232|0;bG=bb+22|0;bH=(as|0)==0;bI=bb+20|0;bJ=bb+16|0;bK=bb+284|0;bL=0;bN=0;bO=0;bQ=0;bR=0;bS=0;bT=0;bU=0;bV=bg;bW=aR;L5954:while(1){aR=c[1054]|0;bX=(a[aR+(bV*40&-1)|0]&1)==0;bY=c[aR+(bV*40&-1)+36>>2]|0;bZ=aR+(bV*40&-1)+32|0;L5956:do{if(bX){b_=c[bZ>>2]|0;aP=4648}else{b$=c[10036]|0;b0=0;while(1){if((b0|0)>=(bY|0)){aP=4478;break}if((a[b$+((c[bZ>>2]|0)+b0|0)|0]|0)==(a[b0+103664|0]|0)){b0=b0+1|0}else{break}}if((aP|0)==4478){aP=0;if((b0|0)==1){b1=bS;b2=bR;b3=bO;break L5954}}b$=c[bZ>>2]|0;if(!((bY|0)>0&(bX^1))){b_=b$;aP=4648;break}b4=c[10036]|0;b5=0;b6=0;b7=b$;while(1){if((a[b5+168032|0]|0)==(a[b4+(b5+b7|0)|0]|0)){b8=b7;b9=b6}else{if((b5|0)!=1){break}b8=b7-1|0;b9=1}ca=b5+1|0;if((ca|0)<(b9+bY|0)){b5=ca;b6=b9;b7=b8}else{aP=4485;break}}do{if((aP|0)==4485){aP=0;if((b9|0)==0){if(!((b5|0)==0|(b5|0)==6)){break}}if(bL){cb=bV;aP=6078;break L5888}cc=bV+1|0;c[13898]=cc;b7=c[aR+(cc*40&-1)+36>>2]|0;b6=c[aR+(cc*40&-1)+32>>2]|0;b4=(a[aR+(cc*40&-1)|0]&1)==0;b0=(b7|0)>0;ca=c[10036]|0;cd=30960;ce=115840;L5978:while(1){L5980:do{if(!b4){if(b0){cf=0;cg=0;ch=b6;while(1){ci=a[ce+cf|0]|0;if(ci<<24>>24==(a[ca+(cf+ch|0)|0]|0)){cj=ch;ck=cg}else{if(ci<<24>>24!=36){break L5980}cj=ch-1|0;ck=1}cl=cf+1|0;if((cl|0)<(ck+b7|0)){cf=cl;cg=ck;ch=cj}else{break}}if((ck|0)==0){cm=cl}else{cn=cd;break L5978}}else{cm=0}ch=a[ce+cm|0]|0;if((ch<<24>>24|0)==36|(ch<<24>>24|0)==0){cn=cd;break L5978}}}while(0);ch=cd+8|0;cg=c[ch>>2]|0;if((cg|0)==0){cn=ch;break}else{cd=ch;ce=cg}}ce=c[cn+4>>2]|0;if((ce-1|0)>>>0>=9){aP=4501;break L5888}c[bK>>2]=ce;c[aG>>2]=1;c[13898]=(c[13898]|0)+1;co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=bO;cu=bN;cv=1;break L5956}}while(0);if(bX){b_=b$;aP=4648;break}L5996:do{if((bY|0)>0){b5=c[10036]|0;ce=0;cd=0;b7=b$;while(1){if((a[ce+154504|0]|0)==(a[b5+(ce+b7|0)|0]|0)){cw=b7;cx=cd}else{if((ce|0)!=2){aP=4511;break L5996}cw=b7-1|0;cx=1}ca=ce+1|0;if((ca|0)<(cx+bY|0)){ce=ca;cd=cx;b7=cw}else{break}}if((cx|0)!=0){break}if(!((ce|0)==1|(ce|0)==4)){aP=4511}}else{aP=4511}}while(0);do{if((aP|0)==4511){aP=0;if(!((bY|0)>0&(bX^1))){b_=b$;aP=4648;break L5956}b7=c[10036]|0;cd=0;b5=0;ca=b$;while(1){if((a[cd+116896|0]|0)==(a[b7+(cd+ca|0)|0]|0)){cy=ca;cz=b5}else{if((cd|0)!=2){break}cy=ca-1|0;cz=1}b6=cd+1|0;if((b6|0)<(cz+bY|0)){cd=b6;b5=cz;ca=cy}else{aP=4517;break}}if((aP|0)==4517){aP=0;if((cz|0)!=0){break}if((cd|0)==1|(cd|0)==4){break}}if(bX){b_=b$;aP=4648;break L5956}L6020:do{if((bY|0)>0){ca=c[10036]|0;b5=0;b7=0;ce=b$;while(1){if((a[b5+224032|0]|0)==(a[ca+(b5+ce|0)|0]|0)){cA=ce;cB=b7}else{if((b5|0)!=1){break L6020}cA=ce-1|0;cB=1}b6=b5+1|0;if((b6|0)<(cB+bY|0)){b5=b6;b7=cB;ce=cA}else{break}}if((cB|0)==0){if(!((b5|0)==0|(b5|0)==5)){break}}if(bO){cb=bV;aP=6075;break L5888}a[bI]=a[36231]&1^1;do{if((a[32936]&1)!=0){if(bd){aP=4546;break L5888}if(bH){break}a[as]=0}}while(0);b5=c[13898]|0;cC=b5+1|0;c[13898]=cC;ce=c[1054]|0;b7=c[ce+(cC*40&-1)+36>>2]|0;ca=c[ce+(cC*40&-1)+32>>2]|0;b6=ce+(cC*40&-1)|0;L6038:do{if((a[b6]&1)!=0&(b7|0)>0){b0=c[10036]|0;b4=0;cg=0;ch=ca;while(1){if((a[b4+137208|0]|0)==(a[b0+(b4+ch|0)|0]|0)){cD=ch;cE=cg}else{if((b4|0)!=3){break L6038}cD=ch-1|0;cE=1}cf=b4+1|0;if((cf|0)<(cE+b7|0)){b4=cf;cg=cE;ch=cD}else{break}}if((cE|0)==0){if(!((b4|0)==2|(b4|0)==12)){break}}ch=c[8272]|0;L6050:do{if((ch|0)>(cC|0)){cg=c[10036]|0;b0=0;while(1){if((b0|0)>=(b7|0)){break}if((a[cg+(ca+b0|0)|0]|0)==(a[b0+141968|0]|0)){b0=b0+1|0}else{break L6050}}if((b0|0)!=10){break}cf=b5+2|0;if((ch|0)<=(cf|0)){break}if((a[ce+(cf*40&-1)|0]&1)==0){break}ci=c[ce+(cf*40&-1)+36>>2]|0;cF=ce+(cf*40&-1)+32|0;cf=0;while(1){if((cf|0)>=(ci|0)){break}if((a[cg+((c[cF>>2]|0)+cf|0)|0]|0)==(a[cf+199040|0]|0)){cf=cf+1|0}else{break L6050}}if((cf|0)==1){break L6038}}}while(0);eM(bb);co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=1;cu=bN;cv=bL;break L5956}}while(0);a[43504]=1;if((cC|0)>=(c[8272]|0)){aP=4577;break L5888}L6066:do{if((a[b6]&1)!=0){ce=c[10036]|0;b5=0;while(1){if((b5|0)>=(b7|0)){break}if((a[ce+(ca+b5|0)|0]|0)==(a[b5+103664|0]|0)){b5=b5+1|0}else{break L6066}}if((b5|0)==1){aP=4577;break L5888}}}while(0);a[14176]=1;is(j);a[14176]=0;if((c[ac>>2]|0)!=3){aP=4576;break L5888}ca=c[ad>>2]|0;c[bJ>>2]=ca;if((ca|0)==0){aP=6068;break L5888}a[43504]=0;co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=1;cu=bN;cv=bL;break L5956}}while(0);if(!((bY|0)>0&(bX^1))){b_=b$;aP=4648;break L5956}cd=c[10036]|0;ca=0;b7=0;b6=b$;while(1){if((a[ca+202e3|0]|0)==(a[cd+(ca+b6|0)|0]|0)){cG=b6;cH=b7}else{if((ca|0)!=3){break}cG=b6-1|0;cH=1}ce=ca+1|0;if((ce|0)<(cH+bY|0)){ca=ce;b7=cH;b6=cG}else{aP=4587;break}}do{if((aP|0)==4587){aP=0;if((cH|0)==0){if(!((ca|0)==2|(ca|0)==7)){break}}if(bO){cb=bV;aP=6077;break L5888}b6=bV+1|0;c[13898]=b6;b7=(a[aR+(b6*40&-1)|0]&1)==0;if(b7){aP=4592}else{cd=c[10036]|0;ce=a[cd+(c[aR+(b6*40&-1)+32>>2]|0)|0]|0;if((ce<<24>>24|0)==39|(ce<<24>>24|0)==34){cI=cd;aP=4602}else{aP=4592}}L6091:do{if((aP|0)==4592){aP=0;cd=c[10810]|0;if((cd|0)==0){break}ce=aR+(b6*40&-1)+36|0;ch=aR+(b6*40&-1)+32|0;b4=c[10036]|0;cF=cd;L6094:while(1){cd=c[cF+4>>2]|0;L6096:do{if(!((bW|0)<=(b6|0)|b7)){cg=c[ce>>2]|0;ci=0;while(1){if((ci|0)>=(cg|0)){break}if((a[b4+((c[ch>>2]|0)+ci|0)|0]|0)==(a[cd+ci|0]|0)){ci=ci+1|0}else{break L6096}}if((a[cd+ci|0]|0)==0){break L6094}}}while(0);cd=c[cF>>2]|0;if((cd|0)==0){break L6091}else{cF=cd}}if((a[cF+8|0]&1)!=0){break}if((c[cF+16>>2]|0)==3){cI=b4;aP=4602}}}while(0);L6105:do{if((aP|0)==4602){aP=0;if((b6|0)>=(bW|0)){break}L6108:do{if(!b7){ch=c[aR+(b6*40&-1)+36>>2]|0;ce=aR+(b6*40&-1)+32|0;b5=0;while(1){if((b5|0)>=(ch|0)){break}if((a[cI+((c[ce>>2]|0)+b5|0)|0]|0)==(a[b5+103664|0]|0)){b5=b5+1|0}else{break L6108}}if((b5|0)==1){break L6105}}}while(0);a[14176]=1;is(g);a[14176]=0;if((c[ab>>2]|0)==3){break}c[13898]=b6}}while(0);a[bG]=1;if(bH){co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=1;cu=bN;cv=bL;break L5956}a[as]=0;co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=1;cu=bN;cv=bL;break L5956}}while(0);if(!((bY|0)>0&(bX^1))){b_=b$;aP=4648;break L5956}ca=c[10036]|0;b6=0;b7=0;b4=b$;while(1){if((a[b6+196448|0]|0)==(a[ca+(b6+b4|0)|0]|0)){cJ=b4;cK=b7}else{if((b6|0)!=1){b_=b$;aP=4648;break L5956}cJ=b4-1|0;cK=1}cF=b6+1|0;if((cF|0)<(cK+bY|0)){b6=cF;b7=cK;b4=cJ}else{break}}if((cK|0)==0){if(!((b6|0)==0|(b6|0)==4)){b_=b$;aP=4648;break L5956}}if(bQ){cb=bV;aP=6082;break L5888}if(!((a[32936]&1)==0|bC)){aP=4622;break L5888}b4=bV+1|0;c[13898]=b4;b7=c[aR+(b4*40&-1)+36>>2]|0;ca=c[aR+(b4*40&-1)+32>>2]|0;cF=(a[aR+(b4*40&-1)|0]&1)==0;b4=(b7|0)>0;ce=c[10036]|0;ch=30672;cd=116448;L6132:while(1){L6134:do{if(!cF){if(b4){cf=0;cg=0;b0=ca;while(1){cL=a[cd+cf|0]|0;if(cL<<24>>24==(a[ce+(cf+b0|0)|0]|0)){cM=b0;cN=cg}else{if(cL<<24>>24!=36){break L6134}cM=b0-1|0;cN=1}cO=cf+1|0;if((cO|0)<(cN+b7|0)){cf=cO;cg=cN;b0=cM}else{break}}if((cN|0)==0){cP=cO}else{cQ=ch;break L6132}}else{cP=0}b0=a[cd+cP|0]|0;if((b0<<24>>24|0)==36|(b0<<24>>24|0)==0){cQ=ch;break L6132}}}while(0);b0=ch+8|0;cg=c[b0>>2]|0;if((cg|0)==0){cQ=b0;break}else{ch=b0;cd=cg}}cd=c[cQ+4>>2]|0;cR=bV+2|0;c[13898]=cR;if((cd|0)==-1){aP=4634;break L5888}c[aG>>2]=cd;if((cd|0)==345){hE(bD);cS=c[aG>>2]|0}else{cS=cd}L6151:do{if((cS|0)==400|(cS|0)==416|(cS|0)==432){cd=c[13898]|0;if((c[8272]|0)<=(cd|0)){break}ch=c[1054]|0;if((a[ch+(cd*40&-1)|0]&1)==0){break}b7=c[ch+(cd*40&-1)+36>>2]|0;ce=ch+(cd*40&-1)+32|0;ch=c[10036]|0;ca=0;while(1){if((ca|0)>=(b7|0)){break}if((a[ch+((c[ce>>2]|0)+ca|0)|0]|0)==(a[ca+224816|0]|0)){ca=ca+1|0}else{break L6151}}if((ca|0)!=8){break}c[13898]=cd+1;a[bF]=1}}while(0);if((c[bE>>2]|0)!=0){co=bU;cp=bT;cq=bS;cr=bR;cs=1;ct=bO;cu=bN;cv=bL;break L5956}ce=c[aG>>2]|0;if(!((ce&4|0)!=0|(ce|0)==368)){co=bU;cp=bT;cq=bS;cr=bR;cs=1;ct=bO;cu=bN;cv=bL;break L5956}uh(c[13898]|0,131840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[aG>>2]=18;co=bU;cp=bT;cq=bS;cr=bR;cs=1;ct=bO;cu=bN;cv=bL;break L5956}}while(0);if(bN){cb=bV;aP=6081;break L5888}if(!((a[32936]&1)==0|bC)){aP=4521;break L5888}cT=bV+1|0;c[13898]=cT;b$=c[aR+(cT*40&-1)+36>>2]|0;ce=c[aR+(cT*40&-1)+32>>2]|0;ch=(a[aR+(cT*40&-1)|0]&1)==0;b7=(b$|0)>0;b4=c[10036]|0;L6166:do{if(ch){aP=6064}else{L6168:do{if(b7){cF=0;while(1){if((a[cF+122952|0]|0)!=(a[b4+(cF+ce|0)|0]|0)){break L6168}b6=cF+1|0;if((b6|0)<(b$|0)){cF=b6}else{cU=b6;aP=4526;break}}}else{cU=0;aP=4526}}while(0);if((aP|0)==4526){aP=0;cF=a[cU+122952|0]|0;if((cF<<24>>24|0)==36|(cF<<24>>24|0)==0){cV=31768;break}}if(ch){aP=6064;break}L6176:do{if(b7){cF=0;while(1){if((a[cF+121816|0]|0)!=(a[b4+(cF+ce|0)|0]|0)){break L6176}cd=cF+1|0;if((cd|0)<(b$|0)){cF=cd}else{cW=cd;aP=6053;break}}}else{cW=0;aP=6053}}while(0);if((aP|0)==6053){aP=0;cF=a[cW+121816|0]|0;if((cF<<24>>24|0)==36|(cF<<24>>24|0)==0){cV=31776;break}}if(ch){aP=6064;break}L6184:do{if(b7){cF=0;while(1){if((a[cF+119872|0]|0)!=(a[b4+(cF+ce|0)|0]|0)){break L6184}cd=cF+1|0;if((cd|0)<(b$|0)){cF=cd}else{cX=cd;aP=6058;break}}}else{cX=0;aP=6058}}while(0);if((aP|0)==6058){aP=0;cF=a[cX+119872|0]|0;if((cF<<24>>24|0)==36|(cF<<24>>24|0)==0){cV=31784;break}}if(ch){aP=6064;break}if(b7){cF=0;while(1){if((a[cF+116712|0]|0)!=(a[b4+(cF+ce|0)|0]|0)){aP=6064;break L6166}cd=cF+1|0;if((cd|0)<(b$|0)){cF=cd}else{cY=cd;break}}}else{cY=0}cF=a[cY+116712|0]|0;if((cF<<24>>24|0)==36|(cF<<24>>24|0)==0){cV=31792}else{aP=6064}}}while(0);if((aP|0)==6064){aP=0;cV=31800}b$=c[cV+4>>2]|0;if((b$|0)==0){c[200]=2;c[144]=1;c[13898]=bV+2;co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=bO;cu=1;cv=bL;break}else if((b$|0)==1){c[200]=6;c[144]=5;c[13898]=bV+2;co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=bO;cu=1;cv=bL;break}else if((b$|0)==2){c[200]=2;c[144]=5;c[13898]=bV+2;co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=bO;cu=1;cv=bL;break}else if((b$|0)==3){c[200]=6;c[144]=1;c[13898]=bV+2;co=bU;cp=bT;cq=bS;cr=bR;cs=bQ;ct=bO;cu=1;cv=bL;break}else{aP=4533;break L5888}}}while(0);L6205:do{if((aP|0)==4648){aP=0;aR=c[aG>>2]|0;if((aR|0)==252){L6209:do{if((bY|0)>0&(bX^1)){bZ=c[10036]|0;aE=0;b$=0;ce=b_;while(1){if((a[aE+130576|0]|0)==(a[bZ+(aE+ce|0)|0]|0)){cZ=ce;c_=b$}else{if((aE|0)!=7){break L6209}cZ=ce-1|0;c_=1}b4=aE+1|0;if((b4|0)<(c_+bY|0)){aE=b4;b$=c_;ce=cZ}else{break}}if((c_|0)==0){if(!((aE|0)==6|(aE|0)==11)){break}}c[aS>>2]=3;ce=(c[13898]|0)+1|0;c[13898]=ce;b$=c[1054]|0;L6221:do{if((a[b$+(ce*40&-1)|0]&1)!=0){bZ=c[10810]|0;if((bZ|0)==0){break L6209}b4=(c[8272]|0)>(ce|0);b7=b$+(ce*40&-1)+36|0;ch=b$+(ce*40&-1)+32|0;cF=c[10036]|0;cd=bZ;L6224:while(1){ca=c[cd+4>>2]|0;L6226:do{if(b4){b6=c[b7>>2]|0;cg=0;while(1){if((cg|0)>=(b6|0)){break}if((a[cF+((c[ch>>2]|0)+cg|0)|0]|0)==(a[ca+cg|0]|0)){cg=cg+1|0}else{break L6226}}if((a[ca+cg|0]|0)==0){aP=4665;break L6224}}}while(0);ca=c[cd>>2]|0;if((ca|0)==0){c$=bZ;break}else{cd=ca}}do{if((aP|0)==4665){aP=0;if((a[cd+8|0]&1)!=0){c$=bZ;break}if((c[cd+16>>2]|0)==1){break L6221}else{c$=bZ}}}while(0);L6236:while(1){bZ=c[c$+4>>2]|0;L6238:do{if(b4){cd=c[b7>>2]|0;ca=0;while(1){if((ca|0)>=(cd|0)){break}if((a[cF+((c[ch>>2]|0)+ca|0)|0]|0)==(a[bZ+ca|0]|0)){ca=ca+1|0}else{break L6238}}if((a[bZ+ca|0]|0)==0){break L6236}}}while(0);bZ=c[c$>>2]|0;if((bZ|0)==0){break L6209}else{c$=bZ}}if((a[c$+8|0]&1)!=0){break L6209}if((c[c$+16>>2]|0)!=2){break L6209}}}while(0);ce=is(f)|0;b$=c[ce>>2]|0;if((b$|0)==1){c0=+(c[ce+8>>2]|0)}else if((b$|0)==2){c0=+h[ce+8>>3]}else if((b$|0)==3){c0=+uz(c[ce+8>>2]|0,0)}else{aP=4680;break L5888}if((c[L>>2]|0)==3){uu(c[M>>2]|0);c[L>>2]=1}h[a2>>3]=c0}}while(0);c1=c[aG>>2]|0}else{c1=aR}do{if((c1|0)==225){ce=c[13898]|0;do{if(!bR){c[a3>>2]=-1;c[a4>>2]=0;c[a8>>2]=c[12872];c[a8+4>>2]=c[12873];c[a8+8>>2]=c[12874];c[a8+12>>2]=c[12875];c[a8+16>>2]=c[12876];c[a8+20>>2]=c[12877];c[a8+24>>2]=c[12878];c[a8+28>>2]=c[12879];c[a8+32>>2]=c[12880];c[a8+36>>2]=c[12881];c[a8+40>>2]=c[12882];c[a8+44>>2]=c[12883];c[a8+48>>2]=c[12884];c[a8+52>>2]=c[12885];c[aS>>2]=1;h[a2>>3]=0.0;c[a5>>2]=0;h[bk>>3]=15.0;h[bl>>3]=90.0;c[bm>>2]=0;if((a[30080]&1)!=0){lK(a7,bn);break}b$=c[8798]|0;aE=(b$|0)>0;ch=bn;L6265:while(1){cF=43264;while(1){c2=c[cF>>2]|0;if((c2|0)==0){break}if((c[c2+4>>2]|0)==(ch|0)){break L6265}else{cF=c2|0}}c3=ch-1|0;if(!((ch|0)>(b$|0)&aE)){aP=4699;break}ch=((c3|0)%(b$|0)&-1)+1|0}if((aP|0)==4699){aP=0;c[bp>>2]=c3;c[bq>>2]=1;c[br>>2]=c3;c[bs>>2]=c3;break}b$=c2+8|0;c[a8>>2]=c[b$>>2];c[a8+4>>2]=c[b$+4>>2];c[a8+8>>2]=c[b$+8>>2];c[a8+12>>2]=c[b$+12>>2];c[a8+16>>2]=c[b$+16>>2];c[a8+20>>2]=c[b$+20>>2];c[a8+24>>2]=c[b$+24>>2];c[a8+28>>2]=c[b$+28>>2];c[a8+32>>2]=c[b$+32>>2];c[a8+36>>2]=c[b$+36>>2];c[a8+40>>2]=c[b$+40>>2];c[a8+44>>2]=c[b$+44>>2];c[a8+48>>2]=c[b$+48>>2];c[a8+52>>2]=c[b$+52>>2];c[bo>>2]=0;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[bp>>2]=ch;a[bt]=0;break}if((a[bt]&1)!=0){break}c[bq>>2]=1;c[br>>2]=c[bp>>2]}}while(0);hL(aV,1);b$=c[13898]|0;if((ce|0)==(b$|0)){c4=c[aG>>2]|0;break}else{if(bR){cb=b$;aP=6076;break L5888}else{co=bU;cp=bT;cq=bS;cr=1;cs=bQ;ct=bO;cu=bN;cv=bL;break L6205}}}else{c4=c1}}while(0);aR=c[13898]|0;do{if((c4|0)==489){if(bU){c5=aR}else{c[bu>>2]=c[12406];c5=c[13898]|0}b$=c[1054]|0;aE=c[b$+(c5*40&-1)+36>>2]|0;L6289:do{if((a[b$+(c5*40&-1)|0]&1)!=0&(aE|0)>0){cF=c[10036]|0;b7=0;b4=0;bZ=c[b$+(c5*40&-1)+32>>2]|0;while(1){if((a[b7+211736|0]|0)==(a[cF+(b7+bZ|0)|0]|0)){c6=bZ;c7=b4}else{if((b7|0)!=4){c8=c5;break L6289}c6=bZ-1|0;c7=1}cd=b7+1|0;if((cd|0)<(c7+aE|0)){b7=cd;b4=c7;bZ=c6}else{break}}if((c7|0)==0){if(!((b7|0)==3|(b7|0)==5)){c8=c5;break}}c9=c5+1|0;c[13898]=c9;bZ=(c[8272]|0)>(c9|0);if(!bZ){aP=6074;break L5888}L6302:do{if((a[b$+(c9*40&-1)|0]&1)==0){aP=4722}else{b4=c[b$+(c9*40&-1)+36>>2]|0;cF=b$+(c9*40&-1)+32|0;ch=c[10036]|0;cd=0;while(1){if((cd|0)>=(b4|0)){aP=4719;break}if((a[ch+((c[cF>>2]|0)+cd|0)|0]|0)==(a[cd+150688|0]|0)){cd=cd+1|0}else{break}}do{if((aP|0)==4719){aP=0;if((cd|0)!=2){break}c[bu>>2]=0;break L6302}}while(0);if(bZ){aP=4722}else{aP=6071;break L5888}}}while(0);L6311:do{if((aP|0)==4722){aP=0;L6313:do{if((a[b$+(c9*40&-1)|0]&1)!=0){b7=c[b$+(c9*40&-1)+36>>2]|0;cd=b$+(c9*40&-1)+32|0;cF=c[10036]|0;ch=0;while(1){if((ch|0)>=(b7|0)){break}if((a[cF+((c[cd>>2]|0)+ch|0)|0]|0)==(a[ch+211336|0]|0)){ch=ch+1|0}else{break L6313}}if((ch|0)!=2){break}c[bu>>2]=1;break L6311}}while(0);if(!bZ){aP=6072;break L5888}if((a[b$+(c9*40&-1)|0]&1)==0){aP=6073;break L5888}cd=c[b$+(c9*40&-1)+36>>2]|0;cF=b$+(c9*40&-1)+32|0;b7=c[10036]|0;ca=0;while(1){if((ca|0)>=(cd|0)){break}if((a[b7+((c[cF>>2]|0)+ca|0)|0]|0)==(a[ca+210848|0]|0)){ca=ca+1|0}else{aP=6069;break L5888}}if((ca|0)!=2){aP=6070;break L5888}c[bu>>2]=2}}while(0);bZ=(c[13898]|0)+1|0;c[13898]=bZ;c8=bZ}else{c8=c5}}while(0);if((aR|0)==(c8|0)){da=c[aG>>2]|0;break}else{if(bU){cb=c8;aP=6079;break L5888}else{co=1;cp=bT;cq=bS;cr=bR;cs=bQ;ct=bO;cu=bN;cv=bL;break L6205}}}else{da=c4}}while(0);do{if((da|0)==368){b$=c[bv>>2]|0;if((b$|0)==0){uE(E|0,0,28);aE=ut(192)|0;if((aE|0)==0){gk();ce=ut(192)|0;if((ce|0)==0){aP=4744;break L5888}else{dd=ce}}else{dd=aE}c[dd>>2]=0;c[dd+4>>2]=-1;c[dd+72>>2]=0;c[dd+88>>2]=0;uE(dd+8|0,0,60);c[dd+96>>2]=1;c[dd+144>>2]=4;c[dd+148>>2]=4;c[dd+152>>2]=4;aE=dd+156|0;c[aE>>2]=c[E>>2];c[aE+4>>2]=c[E+4>>2];c[aE+8>>2]=c[E+8>>2];c[aE+12>>2]=c[E+12>>2];c[aE+16>>2]=c[E+16>>2];c[aE+20>>2]=c[E+20>>2];c[aE+24>>2]=c[E+24>>2];a[dd+184|0]=0;c[bv>>2]=dd;c[dd+48>>2]=1;c[(c[bv>>2]|0)+56>>2]=99;de=c[bv>>2]|0}else{de=b$}jw(de);b$=c[13898]|0;if((aR|0)==(b$|0)){break}if(bT){cb=b$;aP=6080;break L5888}else{co=bU;cp=1;cq=bS;cr=bR;cs=bQ;ct=bO;cu=bN;cv=bL;break L6205}}else{uE(O|0,0,56);h[U>>3]=1.0;h[V>>3]=-2.0;c[Q>>2]=aw;c[W>>2]=av;do{if((a[30080]&1)==0){b$=c[8798]|0;aE=(b$|0)>0;ce=bn;L6346:while(1){bZ=43264;while(1){df=c[bZ>>2]|0;if((df|0)==0){break}if((c[df+4>>2]|0)==(ce|0)){break L6346}else{bZ=df|0}}dg=ce-1|0;if(!((ce|0)>(b$|0)&aE)){aP=4774;break}ce=((dg|0)%(b$|0)&-1)+1|0}if((aP|0)==4774){aP=0;c[Q>>2]=dg;c[Y>>2]=1;c[$>>2]=dg;c[W>>2]=dg;break}b$=df+8|0;c[O>>2]=c[b$>>2];c[O+4>>2]=c[b$+4>>2];c[O+8>>2]=c[b$+8>>2];c[O+12>>2]=c[b$+12>>2];c[O+16>>2]=c[b$+16>>2];c[O+20>>2]=c[b$+20>>2];c[O+24>>2]=c[b$+24>>2];c[O+28>>2]=c[b$+28>>2];c[O+32>>2]=c[b$+32>>2];c[O+36>>2]=c[b$+36>>2];c[O+40>>2]=c[b$+40>>2];c[O+44>>2]=c[b$+44>>2];c[O+48>>2]=c[b$+48>>2];c[O+52>>2]=c[b$+52>>2];c[X>>2]=0;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[Q>>2]=ce;a[aa]=0;break}if((a[aa]&1)!=0){break}c[Y>>2]=1;c[$>>2]=c[Q>>2]}else{b$=43280;while(1){dh=c[b$>>2]|0;if((dh|0)==0){break}if((c[dh+4>>2]|0)==(bn|0)){aP=4751;break}else{b$=dh|0}}if((aP|0)==4751){aP=0;b$=dh+8|0;c[O>>2]=c[b$>>2];c[O+4>>2]=c[b$+4>>2];c[O+8>>2]=c[b$+8>>2];c[O+12>>2]=c[b$+12>>2];c[O+16>>2]=c[b$+16>>2];c[O+20>>2]=c[b$+20>>2];c[O+24>>2]=c[b$+24>>2];c[O+28>>2]=c[b$+28>>2];c[O+32>>2]=c[b$+32>>2];c[O+36>>2]=c[b$+36>>2];c[O+40>>2]=c[b$+40>>2];c[O+44>>2]=c[b$+44>>2];c[O+48>>2]=c[b$+48>>2];c[O+52>>2]=c[b$+52>>2];c[X>>2]=0;if((a[aa]&1)!=0){break}c[Y>>2]=1;c[$>>2]=c[Q>>2];break}b$=c[8798]|0;ce=(b$|0)>0;aE=bn;L6367:while(1){bZ=43264;while(1){di=c[bZ>>2]|0;if((di|0)==0){break}if((c[di+4>>2]|0)==(aE|0)){break L6367}else{bZ=di|0}}dj=aE-1|0;if(!((aE|0)>(b$|0)&ce)){aP=4763;break}aE=((dj|0)%(b$|0)&-1)+1|0}if((aP|0)==4763){aP=0;c[Q>>2]=dj;c[Y>>2]=1;c[$>>2]=dj;c[W>>2]=dj;break}b$=di+8|0;c[O>>2]=c[b$>>2];c[O+4>>2]=c[b$+4>>2];c[O+8>>2]=c[b$+8>>2];c[O+12>>2]=c[b$+12>>2];c[O+16>>2]=c[b$+16>>2];c[O+20>>2]=c[b$+20>>2];c[O+24>>2]=c[b$+24>>2];c[O+28>>2]=c[b$+28>>2];c[O+32>>2]=c[b$+32>>2];c[O+36>>2]=c[b$+36>>2];c[O+40>>2]=c[b$+40>>2];c[O+44>>2]=c[b$+44>>2];c[O+48>>2]=c[b$+48>>2];c[O+52>>2]=c[b$+52>>2];c[X>>2]=0;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[Q>>2]=aE;a[aa]=0;break}if((a[aa]&1)!=0){break}c[Y>>2]=1;c[$>>2]=c[Q>>2]}}while(0);b$=c[aG>>2]|0;if((b$|0)==474){c[W>>2]=c[14067];dk=c[aG>>2]|0}else{dk=b$}b$=(dk&2|0)!=0;hH(q,1,b$);b$=c[13898]|0;if((aR|0)==(b$|0)){break}if(bR){cb=b$;aP=6083;break L5888}c[bB>>2]=c[O>>2];c[bB+4>>2]=c[O+4>>2];c[bB+8>>2]=c[O+8>>2];c[bB+12>>2]=c[O+12>>2];c[bB+16>>2]=c[O+16>>2];c[bB+20>>2]=c[O+20>>2];c[bB+24>>2]=c[O+24>>2];c[bB+28>>2]=c[O+28>>2];c[bB+32>>2]=c[O+32>>2];c[bB+36>>2]=c[O+36>>2];c[bB+40>>2]=c[O+40>>2];c[bB+44>>2]=c[O+44>>2];c[bB+48>>2]=c[O+48>>2];c[bB+52>>2]=c[O+52>>2];co=bU;cp=bT;cq=bS;cr=1;cs=bQ;ct=bO;cu=bN;cv=bL;break L6205}}while(0);if((c[aG>>2]&8|0)==0){b1=bS;b2=bR;b3=bO;break L5954}b$=c[1054]|0;ce=a[b$+(aR*40&-1)|0]|0;L6389:do{if((c[8272]|0)>(aR|0)){if((ce&1)==0){aP=4787;break}bZ=c[b$+(aR*40&-1)+36>>2]|0;ca=b$+(aR*40&-1)+32|0;cF=c[10036]|0;b7=0;while(1){if((b7|0)>=(bZ|0)){break}if((a[cF+((c[ca>>2]|0)+b7|0)|0]|0)==(a[b7+116360|0]|0)){b7=b7+1|0}else{aP=4787;break L6389}}if((b7|0)==2){aP=4795}else{aP=4787}}else{aP=4787}}while(0);L6396:do{if((aP|0)==4787){aP=0;ca=c[b$+(aR*40&-1)+36>>2]|0;if(!((ce&1)!=0&(ca|0)>0)){dl=bS;break}cF=c[10036]|0;bZ=0;cd=0;b4=c[b$+(aR*40&-1)+32>>2]|0;while(1){if((a[bZ+121984|0]|0)==(a[cF+(bZ+b4|0)|0]|0)){dm=b4;dn=cd}else{if((bZ|0)!=4){dl=bS;break L6396}dm=b4-1|0;dn=1}cg=bZ+1|0;if((cg|0)<(dn+ca|0)){bZ=cg;cd=dn;b4=dm}else{break}}if((dn|0)!=0){aP=4795;break}if((bZ|0)==3|(bZ|0)==9){aP=4795}else{dl=bS}}}while(0);do{if((aP|0)==4795){aP=0;hJ(by,c[12372]|0,c[12373]|0,at,49504);if((c[aG>>2]|0)!=345){dl=1;break}if((c[bz>>2]|0)!=0){dl=1;break}c[bz>>2]=1;dl=1}}while(0);b$=c[13898]|0;ce=c[1054]|0;b4=a[ce+(b$*40&-1)|0]|0;L6412:do{if((c[8272]|0)>(b$|0)){if((b4&1)==0){aP=4804;break}cd=c[ce+(b$*40&-1)+36>>2]|0;ca=ce+(b$*40&-1)+32|0;cF=c[10036]|0;b7=0;while(1){if((b7|0)>=(cd|0)){break}if((a[cF+((c[ca>>2]|0)+b7|0)|0]|0)==(a[b7+179168|0]|0)){b7=b7+1|0}else{aP=4804;break L6412}}if((b7|0)==2){aP=4812}else{aP=4804}}else{aP=4804}}while(0);L6419:do{if((aP|0)==4804){aP=0;ca=c[ce+(b$*40&-1)+36>>2]|0;if(!((b4&1)!=0&(ca|0)>0)){dq=bR;dr=b$;break}cF=c[10036]|0;cd=0;bZ=0;cg=c[ce+(b$*40&-1)+32>>2]|0;while(1){if((a[cd+178592|0]|0)==(a[cF+(cd+cg|0)|0]|0)){ds=cg;dt=bZ}else{if((cd|0)!=5){dq=bR;dr=b$;break L6419}ds=cg-1|0;dt=1}b6=cd+1|0;if((b6|0)<(dt+ca|0)){cd=b6;bZ=dt;cg=ds}else{break}}if((dt|0)!=0){aP=4812;break}if((cd|0)==4|(cd|0)==9){aP=4812}else{dq=bR;dr=b$}}}while(0);if((aP|0)==4812){aP=0;hH(r,0,0);c[bw>>2]=c[N>>2];c[bw+4>>2]=c[N+4>>2];c[bw+8>>2]=c[N+8>>2];c[bw+12>>2]=c[N+12>>2];a[bx]=1;dq=1;dr=c[13898]|0}if((aR|0)==(dr|0)){b1=dl;b2=dq;b3=bO;break L5954}else{co=bU;cp=bT;cq=dl;cr=dq;cs=bQ;ct=bO;cu=bN;cv=bL}}}while(0);bY=c[13898]|0;bX=c[8272]|0;if((bY|0)<(bX|0)){bL=cv;bN=cu;bO=ct;bQ=cs;bR=cr;bS=cq;bT=cp;bU=co;bV=bY;bW=bX}else{b1=cq;b2=cr;b3=ct;break}}bW=bb+21|0;a[bW]=0;if(b3){du=as;dv=b2;dw=b1}else{dx=b2;dy=b1;dz=bW;aP=4816}}else{bW=bb+21|0;a[bW]=0;dx=0;dy=0;dz=bW;aP=4816}do{if((aP|0)==4816){aP=0;a[bb+20|0]=1;if((c[9056]|0)!=1){if((as|0)==0){du=0;dv=dx;dw=dy;break}a[as]=0;du=as;dv=dx;dw=dy;break}bW=bb+16|0;bU=c[1054]|0;bT=(c[bU+(be*40&-1)+36>>2]|0)+(c[bU+(be*40&-1)+32>>2]|0)|0;bS=db(c[bW>>2]|0,(bT+1|0)-(c[bU+(aQ*40&-1)+32>>2]|0)|0,116456)|0;c[bW>>2]=bS;bU=c[(c[1054]|0)+(aQ*40&-1)+32>>2]|0;L6441:do{if((bU|0)<(bT|0)){bR=bU;bQ=bS;while(1){bO=a[(c[10036]|0)+bR|0]|0;if(bO<<24>>24==0){dA=bQ;break L6441}bN=bQ+1|0;a[bQ]=bO;bO=bR+1|0;if((bO|0)<(bT|0)){bR=bO;bQ=bN}else{dA=bN;break}}}else{dA=bS}}while(0);a[dA]=0;if(bd){dB=c[bW>>2]|0}else{dB=as}a[dz]=1;du=dB;dv=dx;dw=dy}}while(0);bS=bb+12|0;bT=c[bS>>2]|0;do{if((bT|0)==225){if(dv){dC=bb+88|0}else{bU=bb+80|0;bQ=bb+88|0;bR=aw+1|0;do{if((a[30080]&1)==0){aR=bQ|0;bN=c[aR>>2]|0;bO=c[8798]|0;bL=(bO|0)>0;bx=bR;L6458:while(1){bw=43264;while(1){dD=c[bw>>2]|0;if((dD|0)==0){break}if((c[dD+4>>2]|0)==(bx|0)){break L6458}else{bw=dD|0}}dE=bx-1|0;if(!((bx|0)>(bO|0)&bL)){aP=4840;break}bx=((dE|0)%(bO|0)&-1)+1|0}if((aP|0)==4840){aP=0;c[bb+92>>2]=dE;c[bb+128>>2]=1;c[bb+132>>2]=dE;c[bb+96>>2]=dE;break}bO=bQ;bL=dD+8|0;c[bO>>2]=c[bL>>2];c[bO+4>>2]=c[bL+4>>2];c[bO+8>>2]=c[bL+8>>2];c[bO+12>>2]=c[bL+12>>2];c[bO+16>>2]=c[bL+16>>2];c[bO+20>>2]=c[bL+20>>2];c[bO+24>>2]=c[bL+24>>2];c[bO+28>>2]=c[bL+28>>2];c[bO+32>>2]=c[bL+32>>2];c[bO+36>>2]=c[bL+36>>2];c[bO+40>>2]=c[bL+40>>2];c[bO+44>>2]=c[bL+44>>2];c[bO+48>>2]=c[bL+48>>2];c[bO+52>>2]=c[bL+52>>2];c[aR>>2]=bN;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[bb+92>>2]=bx;a[bb+120|0]=0;break}if((a[bb+120|0]&1)!=0){break}c[bb+128>>2]=1;c[bb+132>>2]=c[bb+92>>2]}else{lK(bQ,bR)}}while(0);hL(bU,1);dC=bQ}bR=bb+24|0;bW=dC;c[bR>>2]=c[bW>>2];c[bR+4>>2]=c[bW+4>>2];c[bR+8>>2]=c[bW+8>>2];c[bR+12>>2]=c[bW+12>>2];c[bR+16>>2]=c[bW+16>>2];c[bR+20>>2]=c[bW+20>>2];c[bR+24>>2]=c[bW+24>>2];c[bR+28>>2]=c[bW+28>>2];c[bR+32>>2]=c[bW+32>>2];c[bR+36>>2]=c[bW+36>>2];c[bR+40>>2]=c[bW+40>>2];c[bR+44>>2]=c[bW+44>>2];c[bR+48>>2]=c[bW+48>>2];c[bR+52>>2]=c[bW+52>>2];dF=1}else{if(dv){dF=1;break}bW=bb+24|0;bR=bb+28|0;c[bR>>2]=aw;h[bb+40>>3]=1.0;bL=bb+32|0;c[bL>>2]=av;h[bb+48>>3]=+h[3817];bO=bb+56|0;a[bO]=0;bw=aw+1|0;do{if((a[30080]&1)==0){cd=bW|0;bz=c[cd>>2]|0;aG=c[8798]|0;by=(aG|0)>0;bB=bw;L6479:while(1){bn=43264;while(1){dG=c[bn>>2]|0;if((dG|0)==0){break}if((c[dG+4>>2]|0)==(bB|0)){break L6479}else{bn=dG|0}}dH=bB-1|0;if(!((bB|0)>(aG|0)&by)){aP=4856;break}bB=((dH|0)%(aG|0)&-1)+1|0}if((aP|0)==4856){aP=0;c[bR>>2]=dH;c[bb+64>>2]=1;c[bb+68>>2]=dH;c[bL>>2]=dH;dI=bT;break}aG=bW;by=dG+8|0;c[aG>>2]=c[by>>2];c[aG+4>>2]=c[by+4>>2];c[aG+8>>2]=c[by+8>>2];c[aG+12>>2]=c[by+12>>2];c[aG+16>>2]=c[by+16>>2];c[aG+20>>2]=c[by+20>>2];c[aG+24>>2]=c[by+24>>2];c[aG+28>>2]=c[by+28>>2];c[aG+32>>2]=c[by+32>>2];c[aG+36>>2]=c[by+36>>2];c[aG+40>>2]=c[by+40>>2];c[aG+44>>2]=c[by+44>>2];c[aG+48>>2]=c[by+48>>2];c[aG+52>>2]=c[by+52>>2];c[cd>>2]=bz;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[bR>>2]=bB;a[bO]=0;dI=bT;break}if((a[bO]&1)!=0){dI=bT;break}c[bb+64>>2]=1;c[bb+68>>2]=c[bR>>2];dI=bT}else{lK(bW,bw);dI=c[bS>>2]|0}}while(0);if((dI|0)==474){c[bL>>2]=c[14067]}bw=(dI&2|0)!=0;hH(bW,1,bw);dF=0}}while(0);bT=c[bS>>2]|0;bw=(bT&2|0)==0;if(!bw){c[bb+24>>2]=1}bR=bb+8|0;do{if(!((c[bR>>2]|0)!=0|bw)){bO=bb+48|0;if(+h[bO>>3]!=-3.0){break}h[bO>>3]=1.0}}while(0);if((a[30528]&1)!=0){if(!((bT|0)==1|(bT|0)==18|(bT|0)==33|(bT|0)==51|(bT|0)==64|(bT|0)==225|(bT|0)==345|(bT|0)==368|(bT|0)==457|(bT|0)==102|(bT|0)==295)){aP=4867;break L5888}}do{if((bT&8|0)==0){dJ=at;dK=bT}else{if(!dw){hJ(bb+192|0,c[12372]|0,c[12373]|0,at,49504)}bw=bb+192|0;bO=c[bw>>2]|0;if((bO|0)==2|(bO|0)==5){dL=(c[bb+200>>2]|0)+1|0}else{dL=at}bQ=c[bS>>2]|0;if((bQ|0)!=345){dJ=dL;dK=bQ;break}if((bO|0)!=0){dJ=dL;dK=345;break}c[bw>>2]=1;dJ=dL;dK=345}}while(0);bT=bb+304|0;c[bT>>2]=c[200];bw=bb+308|0;c[bw>>2]=c[144];if((dK|0)==368){bO=bb+224|0;bQ=c[bO>>2]|0;if((bQ|0)==0){uE(E|0,0,28);bU=ut(192)|0;if((bU|0)==0){gk();by=ut(192)|0;if((by|0)==0){aP=4880;break L5888}else{dM=by}}else{dM=bU}c[dM>>2]=0;c[dM+4>>2]=-1;c[dM+72>>2]=0;c[dM+88>>2]=0;uE(dM+8|0,0,60);c[dM+96>>2]=1;c[dM+144>>2]=4;c[dM+148>>2]=4;c[dM+152>>2]=4;bU=dM+156|0;c[bU>>2]=c[E>>2];c[bU+4>>2]=c[E+4>>2];c[bU+8>>2]=c[E+8>>2];c[bU+12>>2]=c[E+12>>2];c[bU+16>>2]=c[E+16>>2];c[bU+20>>2]=c[E+20>>2];c[bU+24>>2]=c[E+24>>2];a[dM+184|0]=0;c[bO>>2]=dM;c[dM+48>>2]=1;c[(c[bO>>2]|0)+56>>2]=99;dN=c[bO>>2]|0}else{dN=bQ}c[dN+8>>2]=(c[200]|0)==6&1;c[(c[bO>>2]|0)+12>>2]=(c[144]|0)==5&1;bQ=bb+64|0;bU=c[bO>>2]|0;bO=bQ;by=bU+72|0;c[bO>>2]=c[by>>2];c[bO+4>>2]=c[by+4>>2];c[bO+8>>2]=c[by+8>>2];c[bO+12>>2]=c[by+12>>2];by=bU+72|0;if((c[by>>2]|0)==7){c[bb+28>>2]=-6}do{if((c[bU+88>>2]|0)>0){aG=c[by>>2]|0;if((aG|0)==3){if(+h[bU+80>>3]<0.0){break}}else if((aG|0)==6|(aG|0)==7){break}aG=bU+128|0;bx=c[aG>>2]|0;do{if((bx|0)==3){if(+h[bU+136>>3]>=0.0){break}bN=aG;c[bO>>2]=c[bN>>2];c[bO+4>>2]=c[bN+4>>2];c[bO+8>>2]=c[bN+8>>2];c[bO+12>>2]=c[bN+12>>2];dO=c[bU+128>>2]|0;aP=4890}else{dO=bx;aP=4890}}while(0);do{if((aP|0)==4890){aP=0;if((dO|0)!=6){break}c[bQ>>2]=6}}while(0);if((c[bU+92>>2]|0)!=-6){break}c[bb+28>>2]=-6}}while(0);dP=c[bS>>2]|0}else{dP=dK}do{if((dP|0)==474){bU=bb+224|0;if((c[bU>>2]|0)!=0){dQ=474;dR=ap;break}uE(E|0,0,28);bQ=ut(192)|0;if((bQ|0)==0){gk();bO=ut(192)|0;if((bO|0)==0){aP=4899;break L5888}else{dS=bO}}else{dS=bQ}c[dS>>2]=0;c[dS+4>>2]=-1;c[dS+72>>2]=0;c[dS+88>>2]=0;uE(dS+8|0,0,60);c[dS+96>>2]=1;c[dS+144>>2]=4;c[dS+148>>2]=4;c[dS+152>>2]=4;bQ=dS+156|0;c[bQ>>2]=c[E>>2];c[bQ+4>>2]=c[E+4>>2];c[bQ+8>>2]=c[E+8>>2];c[bQ+12>>2]=c[E+12>>2];c[bQ+16>>2]=c[E+16>>2];c[bQ+20>>2]=c[E+20>>2];c[bQ+24>>2]=c[E+24>>2];a[dS+184|0]=0;c[bU>>2]=dS;dU=c[bS>>2]|0;aP=4901}else{dU=dP;aP=4901}}while(0);do{if((aP|0)==4901){aP=0;if((dU|0)==392){if((a[64788+((c[200]|0)*688&-1)|0]&1)!=0){aP=4903;break L5888}if(((c[9670]|0)-1|0)>>>0<2){if((a[64788+((c[144]|0)*688&-1)|0]&1)!=0){aP=4906;break L5888}}bU=ap+1|0;bQ=bb+280|0;c[bQ>>2]=bU;if((bU|0)==0){bO=ut(248)|0;if((bO|0)==0){gk();by=ut(248)|0;if((by|0)==0){aP=4910;break L5888}else{dV=by}}else{dV=bO}bO=dV;by=bb+276|0;c[by>>2]=bO;bx=c[9666]|0;aG=c[3568]|0;if((aG|0)!=0){uu(aG)}c[3568]=0;c[dV>>2]=c[9670];c[dV+4>>2]=c[9671];c[dV+8>>2]=c[9672];c[dV+12>>2]=c[9673];c[dV+16>>2]=c[9674];c[dV+20>>2]=c[9675];c[dV+24>>2]=c[9676];c[dV+28>>2]=c[9677];c[dV+32>>2]=c[9678];c[dV+36>>2]=c[9679];c[dV+40>>2]=c[9680];c[dV+44>>2]=c[9681];c[dV+48>>2]=c[9682];c[dV+52>>2]=c[9683];uE(dV+56|0,0,192);c[9682]=bO;c[dV+116>>2]=bx;c[9666]=0;h[(c[by>>2]|0)+16>>3]=aq;c[(c[by>>2]|0)+32>>2]=ao;c[(c[by>>2]|0)+36>>2]=an}else{by=c[9682]|0;c[bb+276>>2]=by;bx=by+8|0;c[bx>>2]=(c[bx>>2]|0)+1}bx=c[bb+276>>2]|0;do{if(!dF){by=c[bx+32>>2]|0;if((by|0)==-5){break}bO=bb+24|0;aG=bO|0;bW=c[aG>>2]|0;bL=c[8798]|0;bN=(bL|0)>0;aR=(c[bQ>>2]|0)+by|0;L6573:while(1){by=43264;while(1){dW=c[by>>2]|0;if((dW|0)==0){break}if((c[dW+4>>2]|0)==(aR|0)){break L6573}else{by=dW|0}}dX=aR-1|0;if(!((aR|0)>(bL|0)&bN)){aP=4928;break}aR=((dX|0)%(bL|0)&-1)+1|0}if((aP|0)==4928){aP=0;c[bb+28>>2]=dX;c[bb+64>>2]=1;c[bb+68>>2]=dX;c[bb+32>>2]=dX;break}bL=bO;bN=dW+8|0;c[bL>>2]=c[bN>>2];c[bL+4>>2]=c[bN+4>>2];c[bL+8>>2]=c[bN+8>>2];c[bL+12>>2]=c[bN+12>>2];c[bL+16>>2]=c[bN+16>>2];c[bL+20>>2]=c[bN+20>>2];c[bL+24>>2]=c[bN+24>>2];c[bL+28>>2]=c[bN+28>>2];c[bL+32>>2]=c[bN+32>>2];c[bL+36>>2]=c[bN+36>>2];c[bL+40>>2]=c[bN+40>>2];c[bL+44>>2]=c[bN+44>>2];c[bL+48>>2]=c[bN+48>>2];c[bL+52>>2]=c[bN+52>>2];c[aG>>2]=bW;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[bb+28>>2]=aR;a[bb+56|0]=0;break}if((a[bb+56|0]&1)!=0){break}c[bb+64>>2]=1;c[bb+68>>2]=c[bb+28>>2]}}while(0);bN=c[bx+36>>2]|0;if((bN|0)!=-5){c[bb+200>>2]=(c[bQ>>2]|0)+bN}dY=bU;dZ=c[bS>>2]|0}else{dY=ap;dZ=dU}if((dZ|0)!=400){dQ=dZ;dR=dY;break}a[bb+56|0]=1;dQ=400;dR=dY}}while(0);do{if((c[bR>>2]|0)==1){if((bf|0)<0){c[bR>>2]=4;d_=aw+1|0;d$=(dQ>>>1&1)+av|0;break}bN=c[200]|0;bL=o+(bN<<2)|0;bB=c[bL>>2]|0;do{if((bB&1|0)==0){bz=c[64648+(bN*688&-1)>>2]|0;if((bz|0)==0){break}if((bz&1|0)!=0){h[64664+(bN*688&-1)>>3]=8.988465674311579e+307}if((bz&2|0)==0){break}h[64672+(bN*688&-1)>>3]=-8.988465674311579e+307}}while(0);if((c[64808+(bN*688&-1)>>2]|0)==1&(bf|0)<2){aP=4944;break L5888}bU=c[144]|0;if((c[64808+(bU*688&-1)>>2]|0)==1&(bf|0)<1){aP=4946;break L5888}c[12210]=bN;c[12211]=bU;c[bL>>2]=bB|1;bQ=o+(bU<<2)|0;c[bQ>>2]=c[bQ>>2]|1;aP=4950}else{if(!((a[32936]&1)==0|bd^1)){aP=4950;break}bQ=o+(c[200]<<2)|0;c[bQ>>2]=c[bQ>>2]|2;bQ=o+(c[144]<<2)|0;c[bQ>>2]=c[bQ>>2]|2;aP=4950}}while(0);do{if((aP|0)==4950){aP=0;do{if(bd){d0=aw;d1=av}else{bQ=c[bS>>2]|0;if((bQ|0)==400|(bQ|0)==416|(bQ|0)==432){d0=aw;d1=av;break}d0=aw+1|0;d1=(bQ>>>1&1)+av|0}}while(0);if((c[bR>>2]|0)!=1){d_=d0;d$=d1;break}d2=bb+4|0;bB=c[d2>>2]|0;bL=bb+316|0;do{if((c[bL>>2]|0)==0){bN=c[bb+64>>2]|0;do{if((bN|0)==3){if(+h[bb+72>>3]>=0.0){d3=0;break}d3=1}else{d3=0}}while(0);if(!((c[bb+284>>2]|0)==0&((c[bb+28>>2]|0)==-6|((bN|0)==6|d3)))){break}aR=c[bb+296>>2]<<3;bW=ut(aR)|0;if((bW|0)==0){gk();aG=ut(aR)|0;if((aG|0)==0){aP=4961;break L5888}else{d4=aG}}else{d4=bW}c[bL>>2]=d4}}while(0);bW=bb+312|0;c[bW>>2]=99;aG=c[11870]|0;if((aG|0)==1){c[12210]=c[12211]}aR=c[bS>>2]|0;do{if((aR|0)==311|(aR|0)==118|(aR|0)==137){if((aG|0)<=5){d5=4;d6=7;break}bO=c[12210]|0;c[12213]=bO;c[12212]=bO;bO=c[12211]|0;c[12215]=bO;c[12214]=bO;d5=4;d6=7}else if((aR|0)==257){c[bW>>2]=c[bw>>2];bO=c[12211]|0;c[12214]=bO;c[12213]=bO;c[12212]=bO;d5=5;d6=6}else if((aR|0)==474){c[272]=1;bO=c[273]|0;if((bO|0)==0){d5=2;d6=4;break}if((c[bO>>2]|0)!=2){d5=2;d6=4;break}bQ=bO+40|0;if((c[bQ>>2]|0)!=42){d5=2;d6=4;break}c[bQ>>2]=43;d5=2;d6=4}else if((aR|0)==252){c[bW>>2]=c[bw>>2];bQ=c[12211]|0;c[12214]=bQ;c[12213]=bQ;c[12212]=bQ;d5=5;d6=7}else if((aR|0)==169){if(!((aG|0)==4&+h[7030]==-2.0|(aG|0)>4)){d5=3;d6=6;break}bQ=c[12211]|0;c[12213]=bQ;c[12212]=bQ;d5=3;d6=6}else if((aR|0)==279|(aR|0)==86){if((aG|0)<=3){d5=3;d6=5;break}bQ=c[12210]|0;c[12213]=bQ;c[12212]=bQ;d5=3;d6=5}else if((aR|0)==295|(aR|0)==102){if((aG|0)<=3){d5=2;d6=5;break}bQ=c[12211]|0;c[12213]=bQ;c[12212]=bQ;d5=2;d6=5}else if((aR|0)==392){d5=1;d6=3}else if((aR|0)==153){d5=1;d6=4}else if((aR|0)==345){c[12212]=c[12211];d5=1;d6=3}else if((aR|0)==33|(aR|0)==1|(aR|0)==64){d5=1;d6=3}else if((aR|0)==368){c[269]=1;bQ=c[270]|0;if((bQ|0)==0){d5=3;d6=4;break}if((c[bQ>>2]|0)!=2){d5=3;d6=4;break}bO=bQ+40|0;if((c[bO>>2]|0)!=42){d5=3;d6=4;break}c[bO>>2]=43;d5=3;d6=4}else if((aR|0)==400){d5=3;d6=3}else if((aR|0)==416){d5=5;d6=6}else if((aR|0)==432){d5=6;d6=6}else if((aR|0)==457){d5=2;d6=6}else if((aR|0)==489){d5=2;d6=6}else if((aR|0)==18|(aR|0)==51){d5=1;d6=4}else if((aR|0)==225){d5=4;d6=5}else{d5=1;d6=2}}while(0);aR=bb+284|0;if((c[aR>>2]|0)==1){c[bW>>2]=0;c[12212]=0;d7=3}else{d7=d6}aG=c[11870]|0;if((aG|0)>(d7|0)){aP=4998;break L5888}if((aG|0)>0&(aG|0)<(d5|0)){aP=5e3;break L5888}aG=c[8270]|0;do{if((aG|0)!=0){if((aY(aG|0,139696)|0)==0){break}bA(4,aG|0)}}while(0);a[47040]=1;aG=eC(F,d7)|0;do{if((aG|0)==-1){d8=0;d9=0}else{bW=bb+296|0;bO=bb+320|0;bQ=bb+280|0;bU=bb+276|0;bx=bb+244|0;bz=bb+224|0;cd=bb+23|0;by=bb+288|0;bn=0;bv=0;bu=aG;while(1){if((bv|0)>=(c[bW>>2]|0)){iR(bb,(bv<<1)+1e3|0)}do{if((bu|0)>0){aV=bn+1|0;bp=c[bL>>2]|0;if((bp|0)==0){ea=bu;eb=aV;break}br=c[bS>>2]|0;if((br|0)==252|(br|0)==257){if((bu|0)<6){aP=5012;break L5888}}else if((br|0)==311|(br|0)==118|(br|0)==137){if(!((bu|0)==7|(bu|0)==5)){aP=5014;break L5888}}else if((br|0)==225){if((bu|0)<5){aP=5016;break L5888}}else if((br|0)==368|(br|0)==169|(br|0)==279|(br|0)==86|(br|0)==295|(br|0)==102){if((bu|0)<4){aP=5018;break L5888}}else if((br|0)==457){if((bu|0)==5|(bu|0)<3){aP=5020;break L5888}}else if((br|0)==489|(br|0)==153|(br|0)==18|(br|0)==51|(br|0)==33|(br|0)==1|(br|0)==64){if((bu|0)<3){aP=5022;break L5888}}br=bu-1|0;h[bp+(bv<<3)>>3]=+h[e+(br<<3)>>3];ea=br;eb=aV}else{ea=bu;eb=bn}}while(0);do{if((ea|0)==(-5|0)){if((c[bS>>2]|0)!=392){ec=bv;break}c[(c[bO>>2]|0)+(bv<<6)>>2]=2;ec=bv+1|0}else if((ea|0)==(-4|0)|(ea|0)==(-9|0)){ec=bv}else if((ea|0)==(-2|0)){c[(c[bO>>2]|0)+(bv<<6)>>2]=2;ec=bv+1|0}else if((ea|0)==(-3|0)){aV=c[bS>>2]|0;if((aV|0)==400|(aV|0)==416|(aV|0)==432){ec=bv;break}uD((c[bO>>2]|0)+(bv<<6)|0,56440,64);ec=bv+1|0}else if((ea|0)==(-6|0)){eL(bb);ec=bv}else if((ea|0)==(-7|0)){aI(115120,50,1,c[m>>2]|0);ec=bv}else if((ea|0)==0){aP=5034;break L5888}else if((ea|0)==1){h[G>>3]=+h[F>>3];h[F>>3]=+(c[11942]|0);aP=5036}else if((ea|0)==2){aP=5036}else if((ea|0)==3){if((c[aR>>2]|0)==1){ed=+h[F>>3];ee=+h[G>>3];iX(bb,bv,ed,ee,ed,ed,ee,ee,+h[H>>3]);ec=bv+1|0;break}aV=c[bS>>2]|0;if((aV|0)==392){if((c[9670]|0)==4){aP=5036;break}else{aP=5068}}else if((aV|0)==295|(aV|0)==102|(aV|0)==169){ee=+h[F>>3];ed=+h[G>>3];ef=+h[H>>3];iX(bb,bv,ee,ed,ee,ee,ed-ef,ed+ef,-1.0);ec=bv+1|0;break}else if((aV|0)==279|(aV|0)==86){ef=+h[F>>3];ed=+h[G>>3];ee=+h[H>>3];iX(bb,bv,ef,ed,ef-ee,ef+ee,ed,ed,0.0);ec=bv+1|0;break}else if((aV|0)==153){ed=+h[F>>3];ee=+h[G>>3];ef=+h[H>>3]*.5;iX(bb,bv,ed,ee,ed-ef,ed+ef,ee,ee,0.0);ec=bv+1|0;break}else if((aV|0)==368){ee=+h[F>>3];ef=+h[G>>3];iX(bb,bv,ee,ef,ee,ee,ef,ef,-1.0);br=(c[bO>>2]|0)+(bv<<6)|0;if((c[br>>2]|0)!=2){bp=c[bL>>2]|0;if((bp|0)==0){eg=0.0}else{eg=+h[bp+(bv<<3)>>3]}iW(c[bz>>2]|0,br,bv,c[11766]|0,eg)}ec=bv+1|0;break}else if((aV|0)==400){ef=+h[F>>3];ee=+h[G>>3];iX(bb,bv,ef,ee,ef,ef,ee,ee,+h[H>>3]);br=c[bO>>2]|0;bp=c[br+(bv<<6)>>2]|0;ee=+h[H>>3];L6717:do{if(ee>-8.988465674311579e+307&ee<8.988465674311579e+307){do{if((a[66852]&1)==0){h[br+(bv<<6)+32>>3]=ee}else{if(ee<0.0){h[br+(bv<<6)+32>>3]=ee;break L6717}if(ee==0.0){h[br+(bv<<6)+32>>3]=-8.988465674311579e+307;break L6717}else{ef=+_(+ee);h[br+(bv<<6)+32>>3]=ef/+h[8358];break}}}while(0);if(!((a[cd]&1)==0&(bp|0)==0)){break}ef=+h[H>>3];if(ef<+h[8347]){h[8347]=ef}do{if(ef<+h[8341]){if((c[16678]&1|0)==0){break L6717}if((c[16698]&1|0)==0){h[8341]=ef;break}ed=+h[8350];if(ed>ef){h[8341]=ed;break L6717}else{h[8341]=ef;break}}}while(0);if(ef>+h[8348]){h[8348]=ef}if(ef<=+h[8342]){break}if((c[16678]&2|0)==0){break}if((c[16699]&2|0)==0){h[8342]=ef;break}ed=+h[8353];if(ed<ef){h[8342]=ed;break}else{h[8342]=ef;break}}else{h[br+(bv<<6)+32>>3]=ee}}while(0);ec=bv+1|0;break}else if((aV|0)==18|(aV|0)==51|(aV|0)==33|(aV|0)==1|(aV|0)==64){ee=+h[F>>3];ed=+h[G>>3];iX(bb,bv,ee,ed,ee,ee,ed,ed,+h[H>>3]);ec=bv+1|0;break}else if((aV|0)==474){ed=+h[F>>3];ee=+h[G>>3];eh=+h[H>>3];ei=eh*.5;iX(bb,bv,ed,ee,ed-ei,ed+ei,ee,ee,eh);ec=bv+1|0;break}else if((aV|0)==457){eh=+h[F>>3];ee=+h[H>>3];iX(bb,bv,eh,+h[G>>3],eh-ee,eh+ee,0.0,360.0,ee>=0.0?0.0:-1.0);ec=bv+1|0;break}else if((aV|0)==489){ee=+h[F>>3];eh=+h[G>>3];ei=+h[H>>3];ed=+P(+ei);iX(bb,bv,ee,eh,ed,ed,0.0,ei,ei>=0.0?0.0:-1.0);ec=bv+1|0;break}else if((aV|0)!=345){aP=5068}if((aP|0)==5068){aP=0;uh(bB,79800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[bS>>2]=102}c[bx>>2]=13;ei=+h[F>>3];ed=+h[G>>3];iX(bb,bv,ei,ed,ei,ei,ed,+h[H>>3],-1.0);ec=bv+1|0}else if((ea|0)==4){br=c[bS>>2]|0;if((br|0)==137|(br|0)==311|(br|0)==118){ed=+h[F>>3];ei=+h[G>>3];eh=+h[H>>3];ee=+h[I>>3];iX(bb,bv,ed,ei,ed-eh,ed+eh,ei-ee,ei+ee,0.0);ec=bv+1|0;break}else if((br|0)==153){ee=+h[G>>3];iX(bb,bv,+h[F>>3],ee,+h[H>>3],+h[I>>3],ee,ee,0.0);ec=bv+1|0;break}else if((br|0)==279|(br|0)==86){ee=+h[G>>3];iX(bb,bv,+h[F>>3],ee,+h[H>>3],+h[I>>3],ee,ee,0.0);ec=bv+1|0;break}else if((br|0)==169){bp=bv+1|0;ee=+h[F>>3];ei=+h[G>>3];if(+h[7030]==-2.0){iX(bb,bv,ee,ei,ee,ee,+h[H>>3],+h[I>>3],-1.0);ec=bp;break}else{eh=+h[I>>3]*.5;ed=+h[H>>3];iX(bb,bv,ee,ei,ee-eh,ee+eh,ei-ed,ei+ed,0.0);ec=bp;break}}else if((br|0)==474){bp=bv+1|0;ed=+h[F>>3];ei=+h[G>>3];eh=+h[H>>3]*.5;ee=ed-eh;ej=ed+eh;aE=c[11767]|0;bq=c[by>>2]|0;L6774:do{if((aE|0)==0){ek=0}else{bt=c[bz>>2]|0;bo=eD(aE)|0;a8=uA(bo|0)|0;L6776:do{if((bt|0)!=0){bs=bt;while(1){a7=c[bs+60>>2]|0;if((a7|0)!=0){if((a_(bo|0,a7|0,a8|0)|0)==0){break}}a7=c[bs>>2]|0;if((a7|0)==0){break L6776}else{bs=a7}}uu(bo);ek=c[bs+4>>2]|0;break L6774}}while(0);c[by>>2]=bq+1;iW(c[bz>>2]|0,c[bO>>2]|0,bq,bo,0.0);uu(bo);ek=bq}}while(0);iX(bb,bv,ed,ei,ee,ej,+(ek|0),+h[G>>3],+h[H>>3]);ec=bp;break}else if((br|0)==225){ef=+h[F>>3];eh=+h[G>>3];iX(bb,bv,ef,eh,ef,ef+ +h[H>>3],eh,eh+ +h[I>>3],0.0);ec=bv+1|0;break}else if((br|0)==18|(br|0)==51|(br|0)==368){eh=+h[F>>3];ef=+h[G>>3];iX(bb,bv,eh,ef,eh,eh,ef,ef,+h[H>>3]);ec=bv+1|0;break}else if((br|0)==489){ef=+h[F>>3];eh=+h[G>>3];el=+h[H>>3];em=+P(+el);en=+h[I>>3];eo=+P(+en);if(el<0.0){ep=-1.0}else{ep=en>=0.0?0.0:-1.0}iX(bb,bv,ef,eh,em,eo,0.0,el,ep);ec=bv+1|0;break}else if(!((br|0)==295|(br|0)==102)){uh(bB,79024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[bS>>2]=102}el=+h[F>>3];iX(bb,bv,el,+h[G>>3],el,el,+h[H>>3],+h[I>>3],-1.0);ec=bv+1|0}else if((ea|0)==5){bq=c[bS>>2]|0;if((bq|0)==257|(bq|0)==252){el=+h[F>>3];iX(bb,bv,el,+h[G>>3],el,el,+h[H>>3],+h[I>>3],+h[J>>3]);ec=bv+1|0;break}else if((bq|0)==225){el=+h[F>>3];eo=+h[G>>3];iX(bb,bv,el,eo,el,el+ +h[H>>3],eo,eo+ +h[I>>3],+h[J>>3]);ec=bv+1|0;break}else if((bq|0)==457){eo=+h[F>>3];el=+h[H>>3];iX(bb,bv,eo,+h[G>>3],eo-el,eo+el,+h[I>>3],+h[J>>3],el>=0.0?0.0:-1.0);ec=bv+1|0;break}else if((bq|0)==489){el=+h[F>>3];eo=+h[G>>3];em=+h[H>>3];eh=+P(+em);ef=+h[I>>3];en=+P(+ef);if(em<0.0){eq=-1.0}else{eq=ef>=0.0?0.0:-1.0}iX(bb,bv,el,eo,eh,en,+h[J>>3],em,eq);ec=bv+1|0;break}else if((bq|0)==416){aP=5148;break}else if((bq|0)!=169){uh(bB,78440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[bS>>2]=169}em=+h[F>>3];en=+h[J>>3]*.5;iX(bb,bv,em,+h[G>>3],em-en,em+en,+h[H>>3],+h[I>>3],0.0);ec=bv+1|0}else if((ea|0)==7|(ea|0)==6){bq=c[bS>>2]|0;if((bq|0)==252){en=+h[F>>3];em=+h[K>>3];if(em>0.0){er=en-em*.5}else{er=en}iX(bb,bv,en,+h[G>>3],er,en,+h[H>>3],+h[I>>3],+h[J>>3]);ec=bv+1|0;break}else if((bq|0)==432|(bq|0)==416){aP=5148;break}else if(!((bq|0)==311|(bq|0)==118|(bq|0)==137)){uh(bB,78104,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[bS>>2]=118}iX(bb,bv,+h[F>>3],+h[G>>3],+h[H>>3],+h[I>>3],+h[J>>3],+h[K>>3],0.0);ec=bv+1|0}else{aP=5025;break L5888}}while(0);L6811:do{if((aP|0)==5036){aP=0;bq=c[bS>>2]|0;if((bq|0)!=392){en=+h[7030];do{if((bq|0)==153&en>0.0){if((a[56232]&1)==0){break}aE=c[bT>>2]|0;if((a[64788+(aE*688&-1)|0]&1)==0){em=+h[F>>3];eh=+h[G>>3];eo=en*.5;iX(bb,bv,em,eh,em-eo,eo+em,eh,eh,0.0);ec=bv+1|0;break L6811}else{eh=+h[64792+(aE*688&-1)>>3];em=+h[F>>3];eo=+h[G>>3];el=em*+R(+eh,+(en*-.5));iX(bb,bv,em,eo,el,em*+R(+eh,+(en*.5)),eo,eo,0.0);ec=bv+1|0;break L6811}}}while(0);if((bq|0)==457){en=+h[F>>3];iX(bb,bv,en,+h[G>>3],en,en,0.0,360.0,-1.0);ec=bv+1|0;break}else if((bq|0)==489){iX(bb,bv,+h[F>>3],+h[G>>3],0.0,0.0,0.0,0.0,-2.0);ec=bv+1|0;break}else if((bq|0)==102){en=+h[G>>3];h[H>>3]=en;ej=+h[F>>3];h[G>>3]=ej;ee=+(c[11942]|0);h[F>>3]=ee;iX(bb,bv,ee,ej,ee,ee,ej-en,en+ej,-1.0);ec=bv+1|0;break}else if((bq|0)==252|(bq|0)==257){uh(bB,81264,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[bS>>2]=18}ej=+h[F>>3];en=+h[G>>3];iX(bb,bv,ej,en,ej,ej,en,en,-1.0);ec=bv+1|0;break}br=c[9670]|0;if((br|0)==4){if((ea|0)==1){aP=5039;break L5888}else if((ea|0)==2){en=+h[F>>3];ej=+h[G>>3];h[I>>3]=en+ej;h[H>>3]=en-ej;es=en}else{h[I>>3]=+h[H>>3];h[H>>3]=+h[G>>3];es=+h[F>>3]}h[G>>3]=es;h[F>>3]=+(c[11942]|0)}else{if((ea|0)>1){aP=5044;break L5888}en=+h[G>>3];h[I>>3]=en;h[H>>3]=en}do{if((br|0)==2){en=+(c[bQ>>2]|0)+ +h[(c[bU>>2]|0)+16>>3];h[4834]=en;h[(c[bU>>2]|0)+24>>3]=en}else{en=+h[F>>3]+ +h[(c[bU>>2]|0)+16>>3];if(en<=+h[4834]){break}h[4834]=en;h[(c[bU>>2]|0)+24>>3]=en}}while(0);en=+h[7030];br=bv+1|0;ej=+h[F>>3];ee=+h[G>>3];if(en>0.0){ei=en*.5;iX(bb,bv,ej,ee,ej-ei,ej+ei,+h[H>>3],+h[I>>3],0.0);ec=br;break}else{iX(bb,bv,ej,ee,ej+-.5,ej+.5,+h[H>>3],+h[I>>3],0.0);ec=br;break}}else if((aP|0)==5148){aP=0;ej=+h[F>>3];ee=+h[G>>3];iX(bb,bv,ej,ee,ej,ej,ee,ee,+h[H>>3]);br=c[bO>>2]|0;h[br+(bv<<6)+40>>3]=+h[H>>3];h[br+(bv<<6)+48>>3]=+h[I>>3];h[br+(bv<<6)+56>>3]=+h[J>>3];h[br+(bv<<6)+32>>3]=+h[K>>3];ec=bv+1|0}}while(0);br=eC(F,d7)|0;if((br|0)==-1){break}else{bn=eb;bv=ec;bu=br}}if((ec|0)<=0){d8=ec;d9=eb;break}bu=ec-1|0;d8=(c[(c[bO>>2]|0)+(bu<<6)>>2]|0)==2?bu:ec;d9=eb}}while(0);bB=bb+300|0;c[bB>>2]=d8;iR(bb,d8);eL(bb);ez();bL=c[8270]|0;do{if((bL|0)!=0){if((aY(bL|0,139696)|0)==0){break}bA(4,139696)}}while(0);if((d9|0)==0){uh(-1,104952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[bR>>2]=4;d_=d0;d$=d1;break}bL=c[bS>>2]|0;if((bL|0)==392){aG=c[9670]|0;L6860:do{if((aG|0)==1){if((c[64648+((c[bw>>2]|0)*688&-1)>>2]&2|0)==0){aP=5181;break}do{if((c[bb+280>>2]|0)==0){bu=c[3566]|0;if((bu|0)!=0){uu(bu)}bu=c[bB>>2]<<6;bv=ut(bu)|0;if((bv|0)==0){gk();bn=ut(bu)|0;if((bn|0)==0){aP=5164;break L5888}else{et=bn}}else{et=bv}bv=et;c[3566]=bv;c[3570]=0;if((c[bB>>2]|0)>0){eu=0}else{aP=5181;break L6860}while(1){bn=eu+1|0;uE(bv+(eu<<6)+32|0,0,16);c[3570]=bn;if((bn|0)<(c[bB>>2]|0)){eu=bn}else{ev=bn;break}}}else{bv=c[bB>>2]|0;bn=c[3570]|0;if((bv|0)<=(bn|0)){ev=bn;break}bn=db(c[3566]|0,bv<<6,91376)|0;c[3566]=bn;bv=c[3570]|0;if((bv|0)<(c[bB>>2]|0)){ew=bv}else{ev=bv;break}while(1){bv=ew+1|0;uE(bn+(ew<<6)+32|0,0,16);c[3570]=bv;if((bv|0)<(c[bB>>2]|0)){ew=bv}else{ev=bv;break}}}}while(0);if((ev|0)<=0){aP=5181;break}bO=bb+320|0;bn=0;while(1){bv=c[bO>>2]|0;do{if((c[bv+(bn<<6)>>2]|0)!=2){ee=+h[bv+(bn<<6)+16>>3];bu=c[3566]|0;if(ee<0.0){bU=bu+(bn<<6)+32|0;h[bU>>3]=ee+ +h[bU>>3];ex=+h[bu+(bn<<6)+40>>3]}else{bU=bu+(bn<<6)+40|0;ej=ee+ +h[bU>>3];h[bU>>3]=ej;ex=ej}bU=c[bw>>2]|0;bQ=64672+(bU*688&-1)|0;if(+h[bQ>>3]<ex){h[bQ>>3]=ex;eA=c[bw>>2]|0}else{eA=bU}bU=64664+(eA*688&-1)|0;ej=+h[bu+(bn<<6)+32>>3];if(+h[bU>>3]<=ej){break}h[bU>>3]=ej}}while(0);bv=bn+1|0;if((bv|0)<(ev|0)){bn=bv}else{aP=5181;break}}}else if((aG|0)==3|(aG|0)==4){aP=5181}else if((aG|0)==2){do{if((c[16507]|0)!=0){ej=+(c[bb+280>>2]|0)+(+h[(c[bb+276>>2]|0)+16>>3]+1.0);if(+h[8255]>-1.0){h[8255]=-1.0}if(+h[8256]==ej){break}h[8256]=ej}}while(0);bn=c[16335]|0;if((bn|0)==0){break}bO=c[bB>>2]|0;if((bO|0)>0){bv=c[bb+320>>2]|0;bU=0;ej=0.0;ee=0.0;while(1){do{if((c[bv+(bU<<6)>>2]|0)==2){eB=ee;eE=ej}else{ei=+h[bv+(bU<<6)+16>>3];if(ei<0.0){eB=ee;eE=ej+ei;break}else{eB=ee+ei;eE=ej;break}}}while(0);bu=bU+1|0;if((bu|0)<(bO|0)){bU=bu;ej=eE;ee=eB}else{eF=eE;eG=eB;break}}}else{eF=0.0;eG=0.0}do{if((bn&2|0)!=0){bU=64672+((c[bw>>2]|0)*688&-1)|0;if(+h[bU>>3]>=eG){break}h[bU>>3]=eG}}while(0);if((bn&1|0)==0){break}bU=64664+((c[bw>>2]|0)*688&-1)|0;if(+h[bU>>3]<=eF){break}h[bU>>3]=eF}}while(0);do{if((aP|0)==5181){aP=0;aG=c[16506]|0;if((aG|0)==0){break}do{if((aG&1|0)!=0){ee=+h[(c[bb+276>>2]|0)+16>>3]+-1.0;if(+h[8255]<=ee){break}h[8255]=ee}}while(0);if((aG&2|0)==0){break}bn=c[bb+320>>2]|0;bU=c[bB>>2]|0;while(1){eH=bU-1|0;if((c[bn+(eH<<6)>>2]|0)!=2){break}c[bB>>2]=eH;if((eH|0)==0){aP=5189;break L5888}else{bU=eH}}ee=+h[bn+(eH<<6)+8>>3]+(+h[(c[bb+276>>2]|0)+16>>3]+1.0);if(+h[8256]>=ee){break}h[8256]=ee}}while(0);eI=c[bS>>2]|0}else{eI=bL}do{if((eI|0)==153){bU=c[bB>>2]|0;aG=bU-1|0;if((aG|0)==0){break}bO=c[bT>>2]|0;bv=c[64648+(bO*688&-1)>>2]|0;do{if((bv&1|0)==0){eJ=bO;eK=bv}else{bu=c[bb+320>>2]|0;if((c[bu>>2]|0)==2){eJ=bO;eK=bv;break}if((c[bu+64>>2]|0)==2){eJ=bO;eK=bv;break}ee=+h[bu+8>>3];ej=ee-(+h[bu+72>>3]-ee)*.5;if((a[64788+(bO*688&-1)|0]&1)==0){eN=ej}else{eN=+Z(+(ej*+h[64800+(bO*688&-1)>>3]))}bu=64664+(bO*688&-1)|0;if(+h[bu>>3]<=eN){eJ=bO;eK=bv;break}h[bu>>3]=eN;bu=c[bT>>2]|0;eJ=bu;eK=c[64648+(bu*688&-1)>>2]|0}}while(0);if((eK&2|0)==0){break}bv=c[bb+320>>2]|0;if((c[bv+(aG<<6)>>2]|0)==2){break}bO=bU-2|0;if((c[bv+(bO<<6)>>2]|0)==2){break}ej=+h[bv+(aG<<6)+8>>3];ee=ej+(ej- +h[bv+(bO<<6)+8>>3])*.5;if((a[64788+(eJ*688&-1)|0]&1)==0){eO=ee}else{eO=+Z(+(ee*+h[64800+(eJ*688&-1)>>3]))}bO=64672+(eJ*688&-1)|0;if(+h[bO>>3]>=eO){break}h[bO>>3]=eO}}while(0);bL=c[bS>>2]|0;if((bL|0)==474){bO=c[bB>>2]|0;bv=bb+320|0;bn=c[bv>>2]|0;if((bO|0)>0){bu=0;bQ=bn;while(1){if((c[bQ+(bu<<6)>>2]|0)==2){h[bQ+(bu<<6)+16>>3]=8.988465674311579e+307;eP=c[bv>>2]|0}else{eP=bQ}bz=bu+1|0;if((bz|0)<(bO|0)){bu=bz;bQ=eP}else{eQ=eP;break}}}else{eQ=bn}bM(eQ|0,bO|0,64,12);bQ=c[bv>>2]|0;bu=bO;while(1){eR=bu-1|0;if((c[bQ+(eR<<6)>>2]|0)==2){bu=eR}else{break}}c[bB>>2]=bu;if((c[bQ>>2]|0)==2){aP=5236;break L5888}ee=+h[bQ+56>>3]- +h[bQ+48>>3];do{if(ee==0.0){ej=+h[7030];if(ej<=0.0){eS=.5;break}eT=(a[56232]&1)!=0?ej:.5;aP=5240}else{eT=ee;aP=5240}}while(0);do{if((aP|0)==5240){aP=0;if(eT>=0.0){eS=eT;break}eS=-0.0-eT}}while(0);bu=c[bT>>2]|0;do{if((c[64648+(bu*688&-1)>>2]&1|0)!=0){bO=64664+(bu*688&-1)|0;ee=+h[bO>>3];ej=+h[bQ+8>>3];if(ee>=ej){h[bO>>3]=ee-eS*1.5;break}if(ee<ej-eS){break}h[bO>>3]=ee-eS}}while(0);bQ=c[bT>>2]|0;do{if((c[64648+(bQ*688&-1)>>2]&2|0)!=0){bu=64672+(bQ*688&-1)|0;ee=+h[bu>>3];ej=+h[(c[bv>>2]|0)+(eR<<6)+8>>3];do{if(ee>ej){if(ee>eS+ej){break}h[bu>>3]=eS+ee}else{h[bu>>3]=eS*1.5+ee}}while(0);bu=c[bb+288>>2]|0;if((bu|0)<=1){break}aG=64672+((c[bT>>2]|0)*688&-1)|0;h[aG>>3]=+(bu-1|0)*+h[7035]+ +h[aG>>3]}}while(0);eU=c[bS>>2]|0}else{eU=bL}do{if((eU|0)==416|(eU|0)==432){bv=c[16678]|0;if((bv&1|0)!=0){h[8341]=0.0}if((bv&2|0)==0){break}h[8342]=255.0}}while(0);L6997:do{if((c[bS>>2]|0)==474){bL=bb+288|0;bv=c[bL>>2]|0;if((bv|0)<=0){break}do{if(!((a[56292]&1)==0|(bv|0)<2)){bQ=bb+224|0;aG=c[bQ>>2]|0;if((aG|0)==0){break}if((c[aG>>2]|0)==0){break}aG=bv<<2;bu=ut(aG)|0;if((bu|0)==0){gk();bU=ut(aG)|0;if((bU|0)==0){aP=5267;break L5888}else{eV=bU}}else{eV=bu}bu=eV;bU=c[bL>>2]<<2;aG=ut(bU)|0;if((aG|0)==0){gk();bO=ut(bU)|0;if((bO|0)==0){aP=5270;break L5888}else{eW=bO}}else{eW=aG}aG=eW;bO=c[bL>>2]|0;if((bO|0)>0){bU=0;bn=c[bQ>>2]|0;while(1){bz=c[bn>>2]|0;c[bu+(bU<<2)>>2]=bz;by=bU+1|0;bx=c[bL>>2]|0;if((by|0)<(bx|0)){bU=by;bn=bz}else{eX=bx;break}}}else{eX=bO}bM(eV|0,eX|0,4,22);if((c[bL>>2]|0)>0){bn=0;do{c[aG+(bn<<2)>>2]=c[(c[bu+(bn<<2)>>2]|0)+4>>2];bn=bn+1|0;}while((bn|0)<(c[bL>>2]|0))}bn=c[bu>>2]|0;c[c[bQ>>2]>>2]=bn;if(((c[bL>>2]|0)-1|0)>0){bO=bn;bU=0;while(1){bx=bU+1|0;bz=c[bu+(bx<<2)>>2]|0;c[bO>>2]=bz;if((bx|0)<((c[bL>>2]|0)-1|0)){bO=bz;bU=bx}else{eY=bz;break}}}else{eY=bn}c[eY>>2]=0;uu(eV);c[bb+292>>2]=aG}}while(0);bL=c[14072]|0;if((bL|0)==2){eZ=2}else if((bL|0)==0){break}else{eZ=(bL|0)==3?6:c[200]|0}bL=c[c[bb+224>>2]>>2]|0;if((bL|0)==0){break}bv=bb+320|0;bU=0;bO=bL;while(1){dp(eZ,c[bO+60>>2]|0,+h[(c[bv>>2]|0)+8>>3]+ +(bU|0)*+h[7035],-1);bL=c[bO>>2]|0;if((bL|0)==0){break L6997}bU=bU+1|0;bO=bL}}}while(0);bO=c[aR>>2]|0;if((bO|0)==5|(bO|0)==6|(bO|0)==7|(bO|0)==9|(bO|0)==3|(bO|0)==1|(bO|0)==4){bU=bb+320|0;bv=0;while(1){bL=c[bB>>2]|0;L7036:do{if((bv|0)<(bL|0)){bu=c[bU>>2]|0;bQ=bv;while(1){if((c[bu+(bQ<<6)>>2]|0)!=2){e_=bQ;break L7036}bz=bQ+1|0;if((bz|0)<(bL|0)){bQ=bz}else{e_=bz;break}}}else{e_=bv}}while(0);bQ=0;while(1){e$=bQ+e_|0;if((e$|0)>=(bL|0)){break}if((c[(c[bU>>2]|0)+(e$<<6)>>2]|0)==2){break}else{bQ=bQ+1|0}}if((bQ|0)<=0){break}bM((c[bU>>2]|0)+(e_<<6)|0,bQ|0,64,18);bv=e$}hn(bb);e0=c[aR>>2]|0}else{e0=bO}if((e0|0)==6|(e0|0)==7|(e0|0)==9){hk(bb)}else if((e0|0)==3|(e0|0)==1|(e0|0)==2|(e0|0)==4|(e0|0)==8){hl(bb)}bv=c[bS>>2]|0;if(!((bv|0)==400|(bv|0)==416|(bv|0)==432)){d_=d0;d$=d1;break}c[bb+228>>2]=0;f3(bb,1);d_=d0;d$=d1}}while(0);c[bb+4>>2]=c[13898];bS=bb|0;if(!bd){e1=B;e2=an;e3=ao;e5=dR;e8=aq;e9=du;fa=dJ;fb=0;fc=d$;fd=d_;fe=bc;ff=bS;fg=bb;aP=5313;break}fh=B;fi=an;fj=ao;fk=dR;fl=aq;fm=du;fn=dJ;fo=0;fp=d$;fq=d_;fr=bc;fs=bS;ft=c[13898]|0;fu=c[8272]|0;aP=5305}else if((aP|0)==4369){aP=0;c[13898]=aB+1;uu(c[9666]|0);c[9666]=0;ee=+h[4834];if(ee>0.0){fv=ee+2.0}else{fv=aq}c[af>>2]=-5;ee=fv;bS=aw+1|0;fw=c[13898]|0;while(1){L7062:do{if((c[8272]|0)>(fw|0)){bT=c[1054]|0;if((a[bT+(fw*40&-1)|0]&1)==0){fx=ee;break}bw=c[bT+(fw*40&-1)+36>>2]|0;bR=bT+(fw*40&-1)+32|0;bT=c[10036]|0;bv=0;while(1){if((bv|0)>=(bw|0)){break}if((a[bT+((c[bR>>2]|0)+bv|0)|0]|0)==(a[bv+95280|0]|0)){bv=bv+1|0}else{fx=ee;break L7062}}if((bv|0)!=2){fx=ee;break}c[13898]=fw+1;bR=is(n)|0;bT=c[bR>>2]|0;if((bT|0)==1){fy=+(c[bR+8>>2]|0)}else if((bT|0)==3){fy=+uz(c[bR+8>>2]|0,0)}else if((bT|0)==2){fy=+h[bR+8>>3]}else{aP=4382;break L5888}if((c[ag>>2]|0)!=3){fx=fy;break}uu(c[ah>>2]|0);c[ag>>2]=1;fx=fy}else{fx=ee}}while(0);L7077:do{if((c[9666]|0)==0){bO=c[13898]|0;aR=c[1054]|0;bR=(a[aR+(bO*40&-1)|0]&1)==0;if(bR){aP=4388}else{bT=c[10036]|0;bw=a[bT+(c[aR+(bO*40&-1)+32>>2]|0)|0]|0;if((bw<<24>>24|0)==39|(bw<<24>>24|0)==34){fz=bT}else{aP=4388}}if((aP|0)==4388){aP=0;bT=c[10810]|0;if((bT|0)==0){break}bw=c[8272]|0;bQ=aR+(bO*40&-1)+36|0;bU=aR+(bO*40&-1)+32|0;bB=c[10036]|0;bL=bT;L7084:while(1){bT=c[bL+4>>2]|0;L7086:do{if(!((bw|0)<=(bO|0)|bR)){bu=c[bQ>>2]|0;aG=0;while(1){if((aG|0)>=(bu|0)){break}if((a[bB+((c[bU>>2]|0)+aG|0)|0]|0)==(a[bT+aG|0]|0)){aG=aG+1|0}else{break L7086}}if((a[bT+aG|0]|0)==0){break L7084}}}while(0);bT=c[bL>>2]|0;if((bT|0)==0){break L7077}else{bL=bT}}if((a[bL+8|0]&1)!=0){break}if((c[bL+16>>2]|0)==3){fz=bB}else{break}}L7096:do{if((bO|0)<(c[8272]|0)){L7098:do{if(!bR){bU=c[aR+(bO*40&-1)+36>>2]|0;bQ=aR+(bO*40&-1)+32|0;bw=0;while(1){if((bw|0)>=(bU|0)){break}if((a[fz+((c[bQ>>2]|0)+bw|0)|0]|0)==(a[bw+103664|0]|0)){bw=bw+1|0}else{break L7098}}if((bw|0)==1){fA=0;break L7096}}}while(0);a[14176]=1;is(l);a[14176]=0;if((c[ai>>2]|0)==3){fA=c[aj>>2]|0;break}else{c[13898]=bO;fA=0;break}}else{fA=0}}while(0);c[9666]=fA}}while(0);bO=c[13898]|0;aR=c[1054]|0;bR=a[aR+(bO*40&-1)|0]|0;L7110:do{if((c[8272]|0)>(bO|0)){if((bR&1)==0){aP=4414;break}bB=c[aR+(bO*40&-1)+36>>2]|0;bL=aR+(bO*40&-1)+32|0;bQ=c[10036]|0;bU=0;while(1){if((bU|0)>=(bB|0)){break}if((a[bQ+((c[bL>>2]|0)+bU|0)|0]|0)==(a[bU+225056|0]|0)){bU=bU+1|0}else{aP=4414;break L7110}}if((bU|0)==2){aP=4422}else{aP=4414}}else{aP=4414}}while(0);L7117:do{if((aP|0)==4414){aP=0;bL=c[aR+(bO*40&-1)+36>>2]|0;if(!((bR&1)!=0&(bL|0)>0)){fB=bS;break}bQ=c[10036]|0;bB=0;aG=0;bv=c[aR+(bO*40&-1)+32>>2]|0;while(1){if((a[bB+139232|0]|0)==(a[bQ+(bB+bv|0)|0]|0)){fC=bv;fD=aG}else{if((bB|0)!=5){fB=bS;break L7117}fC=bv-1|0;fD=1}bT=bB+1|0;if((bT|0)<(fD+bL|0)){bB=bT;aG=fD;bv=fC}else{break}}if((fD|0)!=0){aP=4422;break}if((bB|0)==4|(bB|0)==8){aP=4422}else{fB=bS}}}while(0);if((aP|0)==4422){aP=0;c[13898]=bO+1;aR=is(k)|0;bR=c[aR>>2]|0;if((bR|0)==1){fE=+(c[aR+8>>2]|0)}else if((bR|0)==2){fE=+h[aR+8>>3]}else if((bR|0)==3){fE=+uz(c[aR+8>>2]|0,0)}else{aP=4426;break L5888}if((c[ak>>2]|0)==3){uu(c[al>>2]|0);c[ak>>2]=1}fB=~~fE}hJ(p,1,100,c[af>>2]|0,49504);aR=c[13898]|0;if((aR|0)==(fw|0)){break}else{ee=fx;bS=fB;fw=aR}}bS=c[af>>2]|0;B=c[8272]|0;if((B|0)<=(fw|0)){aP=6084;break L5888}aR=c[1054]|0;if((a[aR+(fw*40&-1)|0]&1)==0){aP=6085;break L5888}bR=c[aR+(fw*40&-1)+36>>2]|0;bv=aR+(fw*40&-1)+32|0;aR=c[10036]|0;aG=0;while(1){if((aG|0)>=(bR|0)){break}if((a[aR+((c[bv>>2]|0)+aG|0)|0]|0)==(a[aG+148464|0]|0)){aG=aG+1|0}else{aP=6086;break L5888}}if((aG|0)==1){aW=fx;aX=-1;aZ=fB;a$=bS;a0=fw;a1=B;aP=5304}else{aP=6087;break L5888}}}while(0);if((aP|0)==5304){aP=0;if(au){fh=am;fi=a$;fj=aZ;fk=aX;fl=aW;fm=as;fn=at;fo=aA;fp=av;fq=aw;fr=ax;fs=ay;ft=a0;fu=a1;aP=5305}else{e1=am;e2=a$;e3=aZ;e5=aX;e8=aW;e9=as;fa=at;fb=aA;fc=av;fd=aw;fe=ax;ff=ay;fg=0;aP=5313}}L7148:do{if((aP|0)==5305){aP=0;if((fu|0)<=(ft|0)){fF=fr;fG=1;fH=fo;fI=aQ;fJ=fh;aP=5328;break L5888}aB=c[1054]|0;if((a[aB+(ft*40&-1)|0]&1)==0){fF=fr;fG=1;fH=fo;fI=aQ;fJ=fh;aP=5328;break L5888}bv=c[aB+(ft*40&-1)+36>>2]|0;aR=aB+(ft*40&-1)+32|0;aB=c[10036]|0;bR=0;while(1){if((bR|0)>=(bv|0)){break}if((a[aB+((c[aR>>2]|0)+bR|0)|0]|0)==(a[bR+148464|0]|0)){bR=bR+1|0}else{fF=fr;fG=1;fH=fo;fI=aQ;fJ=fh;aP=5328;break L5888}}if((bR|0)!=1){fF=fr;fG=1;fH=fo;fI=aQ;fJ=fh;aP=5328;break L5888}aR=ft+1|0;c[13898]=aR;fK=fs;fL=fr;fM=fq;fN=fp;fO=1;fP=fo;fQ=fn;fR=fm;fT=fl;fU=fk;fV=fj;fW=fi;fX=fh;fY=aR}else if((aP|0)==5313){aP=0;aR=c[7774]|0;do{if((aR|0)==0){aP=5316}else{if((a[aR+38|0]&1)==0|(fg|0)==0){aP=5316;break}c[fg+8>>2]=4}}while(0);do{if((aP|0)==5316){aP=0;if(!(ix(aR)|0)){break}c[13898]=aQ;fK=ff;fL=fe;fM=fd;fN=fc;fO=0;fP=fb;fQ=fa;fR=e9;fT=e8;fU=e5;fV=e3;fW=e2;fX=e1;fY=aQ;break L7148}}while(0);aR=c[7774]|0;if((aR|0)!=0){bR=aR;while(1){aR=c[bR>>2]|0;uu(c[bR+12>>2]|0);uu(bR);if((aR|0)==0){break}else{bR=aR}}}c[7774]=0;bR=c[13898]|0;if((c[8272]|0)<=(bR|0)){aP=5327;break L5888}aR=c[1054]|0;if((a[aR+(bR*40&-1)|0]&1)==0){aP=5327;break L5888}aB=c[aR+(bR*40&-1)+36>>2]|0;bv=aR+(bR*40&-1)+32|0;aR=c[10036]|0;B=0;while(1){if((B|0)>=(aB|0)){break}if((a[aR+((c[bv>>2]|0)+B|0)|0]|0)==(a[B+148464|0]|0)){B=B+1|0}else{aP=5327;break L5888}}if((B|0)!=1){aP=5327;break L5888}c[13898]=bR+1;c[7774]=iw()|0;fK=ff;fL=fe;fM=fd;fN=fc;fO=0;fP=fb;fQ=fa;fR=e9;fT=e8;fU=e5;fV=e3;fW=e2;fX=e1;fY=c[13898]|0}}while(0);if((fY|0)<(c[8272]|0)){am=fX;an=fW;ao=fV;ap=fU;aq=fT;ar=aQ;as=fR;at=fQ;A=fP;au=fO;av=fN;aw=fM;ax=fL;ay=fK;az=fY}else{aJ=aQ;aK=fP;aL=fX;aM=fO;aN=fL;aO=fY;aP=4357;break}}do{if((aP|0)==4501){uf(cc,163648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4426){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4357){if((aL|0)==0){C=aO}else{fF=aN;fG=aM;fH=aK;fI=aJ;fJ=aL;aP=5328;break}uf(C,105216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4680){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4906){uf(c[13898]|0,110696,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4910){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=107952,v)|0)}else if((aP|0)==4944){uf(c[13898]|0,84448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4946){uf(c[13898]|0,106024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4744){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=201712,v)|0)}else if((aP|0)==4867){uf(-1,115176,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4880){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=201712,v)|0)}else if((aP|0)==4899){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=201712,v)|0)}else if((aP|0)==4903){uf(c[13898]|0,114152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4576){c[13898]=cC;aP=4577}else if((aP|0)==4634){uf(cR,143640,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4466){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=180104,v)|0)}else if((aP|0)==4546){uf(c[13898]|0,216936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4455){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=180104,v)|0)}else if((aP|0)==4533){uf(cT,149448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4382){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4521){uf(bV,92272,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4622){uf(bV,133232,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4462){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=77008,v)|0)}else if((aP|0)==4452){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=77008,v)|0)}else if((aP|0)==4448){uf(c[13898]|0,92272,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==4961){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=90328,v)|0)}else if((aP|0)==4998){uf(-1,89648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5e3){uf(-1,89152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5012){uf(-1,87256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5014){uf(-1,87256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5016){uf(-1,87256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5018){uf(-1,87256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5020){uf(-1,87256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5022){uf(-1,87256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5025){ez();fY=c[11900]|0;uf(c[13898]|0,86160,(v=i,i=i+16|0,c[v>>2]=ea,c[v+8>>2]=fY,v)|0)}else if((aP|0)==5034){ez();fY=c[11932]|0;uf(c[d2>>2]|0,116952,(v=i,i=i+16|0,c[v>>2]=c[11900],c[v+8>>2]=(fY|0)!=0?fY:179864,v)|0)}else if((aP|0)==5039){uf(c[13898]|0,83648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5044){uf(c[13898]|0,82688,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5164){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=91376,v)|0)}else if((aP|0)==5189){uf(-1,90720,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5236){uf(-1,92048,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5267){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=93632,v)|0)}else if((aP|0)==5270){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=92920,v)|0)}else if((aP|0)==5327){if((a[32936]&1)==0){fZ=fe;f_=0;f$=fb;f0=aQ;f1=e1;aP=5331}else{f2=fe;f4=0;f5=fb;f6=aQ;f7=e1}}else if((aP|0)==6068){f9=c[13898]|0;uf(f9,205152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6069){uf(c9,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6070){uf(c9,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6071){uf(c9,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6072){uf(c9,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6073){uf(c9,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6074){uf(c9,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6075){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6076){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6077){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6078){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6079){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6080){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6081){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6082){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6083){uf(cb,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6084){uf(fw,175992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6085){uf(fw,175992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6086){uf(fw,175992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==6087){uf(fw,175992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if((aP|0)==4577){c[bJ>>2]=0;f9=c[13898]|0;uf(f9,205152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((aP|0)==5328){f9=(a[32936]&1)==0;if(f9|fG^1){if(f9){fZ=fF;f_=fG;f$=fH;f0=fI;f1=fJ;aP=5331;break}else{f2=fF;f4=fG;f5=fH;f6=fI;f7=fJ;break}}else{uf(-1,147440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((aP|0)==5331){if((a[30528]&1)!=0){f2=fZ;f4=f_;f5=f$;f6=f0;f7=f1;break}do{if((fZ|0)!=0){if((c[z>>2]|0)==0){break}if(!(+h[8256]==-8.988465674311579e+307|+h[8255]==8.988465674311579e+307)){break}h[8255]=-10.0;h[8256]=10.0}}while(0);dc(2,145296);if((c[x>>2]&1|0)!=0){dc(6,103512);f2=fZ;f4=f_;f5=f$;f6=f0;f7=f1;break}fJ=c[17194]|0;if((fJ|0)==0){f2=fZ;f4=f_;f5=f$;f6=f0;f7=f1;break}if((fJ&1|0)!=0){h[8599]=+h[8255]}if((fJ&2|0)==0){f2=fZ;f4=f_;f5=f$;f6=f0;f7=f1;break}h[8600]=+h[8256];f2=fZ;f4=f_;f5=f$;f6=f0;f7=f1}}while(0);do{if((f2|0)==0){ga=f7}else{f1=(a[32936]&1)==0;if(f1){if((a[30528]&1)==0){aP=5357}else{aP=5346}}else{aP=5346}if((aP|0)==5346){do{if((c[z>>2]&1|0)==0){f0=c[16506]|0;if((f0&1|0)!=0){h[8255]=8.988465674311579e+307}if((f0&2|0)==0){break}h[8256]=-8.988465674311579e+307}}while(0);do{if((c[x>>2]&1|0)==0){f0=c[17194]|0;if((f0&1|0)!=0){h[8599]=8.988465674311579e+307}if((f0&2|0)==0){break}h[8600]=-8.988465674311579e+307}}while(0);if(f1){aP=5357}else{aP=5358}}if((aP|0)==5357){if((a[30528]&1)==0){gb=0.0;gc=0.0}else{aP=5358}}if((aP|0)==5358){fT=+h[8771];gb=(+h[8772]-fT)/+((c[6352]|0)-1|0);gc=fT}f0=c[10814]|0;c[13898]=u;c[7774]=iw()|0;f$=t|0;f_=t+8|0;fZ=f_;fJ=t+8|0;fI=f_;f_=t+16|0;fH=f0;fT=gb;f0=f4;fG=f5;fF=f6;aq=gc;f9=0;L7293:while(1){bJ=fG;fw=fF;L7295:while(1){cb=c[13898]|0;if(f0){gd=fw}else{gd=bJ?fw:cb}if((t9(cb)|0)==0){aP=5370;break}dT();cb=c[13898]|0;if((c[8272]|0)<=(cb|0)){bJ=1;fw=gd;continue}c9=c[1054]|0;if((a[c9+(cb*40&-1)|0]&1)==0){bJ=1;fw=gd;continue}e1=c[c9+(cb*40&-1)+36>>2]|0;aQ=c9+(cb*40&-1)+32|0;cb=c[10036]|0;c9=0;while(1){if((c9|0)>=(e1|0)){break}if((a[cb+((c[aQ>>2]|0)+c9|0)|0]|0)==(a[c9+148464|0]|0)){c9=c9+1|0}else{bJ=1;fw=gd;continue L7295}}if((c9|0)==1){aP=5664;break}else{bJ=1;fw=gd}}L7307:do{if((aP|0)==5370){aP=0;ge=c[13898]|0;if((ge|0)>=(c[8272]|0)){aP=5377;break L7293}fw=c[1054]|0;bR=fw+(ge*40&-1)|0;L7310:do{if((a[bR]&1)==0){gf=0}else{B=c[fw+(ge*40&-1)+36>>2]|0;aQ=fw+(ge*40&-1)+32|0;cb=c[10036]|0;e1=0;while(1){if((e1|0)>=(B|0)){break}if((a[cb+((c[aQ>>2]|0)+e1|0)|0]|0)==(a[e1+103664|0]|0)){e1=e1+1|0}else{gf=0;break L7310}}gf=(e1|0)==1}}while(0);if(gf|(fH|0)==0){aP=5377;break L7293}c9=fH+304|0;c[200]=c[c9>>2];c[144]=c[fH+308>>2];aQ=f9+1|0;c[11670]=31424;cb=c[fw+(ge*40&-1)+36>>2]|0;L7318:do{if((a[bR]&1)!=0&(cb|0)>0){B=c[10036]|0;bB=0;bO=0;fb=c[fw+(ge*40&-1)+32>>2]|0;while(1){if((a[bB+205224|0]|0)==(a[B+(bB+fb|0)|0]|0)){gg=fb;gh=bO}else{if((bB|0)!=7){aP=5386;break L7318}gg=fb-1|0;gh=1}fe=bB+1|0;if((fe|0)<(gh+cb|0)){bB=fe;bO=gh;fb=gg}else{break}}if((gh|0)!=0){break}if(!((bB|0)==6|(bB|0)==12)){aP=5386}}else{aP=5386}}while(0);do{if((aP|0)==5386){aP=0;if((iu(s)|0)!=0){break}cb=a[32936]&1;fw=f0^cb<<24>>24!=0;c[7858]=c[s>>2];do{if(cb<<24>>24==0){if((a[30528]&1)!=0){aP=5394;break}gi=c[200]|0;e8=+h[64664+(gi*688&-1)>>3];fl=+h[64672+(gi*688&-1)>>3];if((a[64788+(gi*688&-1)|0]&1)==0){gj=fl;gl=e8}else{if(!(e8>0.0&fl>0.0)){aP=5391;break L7293}aW=+_(+e8);e8=+h[64800+(gi*688&-1)>>3];gj=+_(+fl)/e8;gl=aW/e8}bR=c[6352]|0;gm=(gj-gl)/+(bR-1|0);gn=gl;go=bR}else{aP=5394}}while(0);if((aP|0)==5394){aP=0;gm=fT;gn=aq;go=c[6352]|0}if((go|0)>0){cb=fH+320|0;bB=fH+12|0;bR=fH+23|0;fb=fw^1;bO=fH+244|0;B=fH+248|0;e1=0;while(1){e8=gn+gm*+(e1|0);do{if(+P(+e8)<1.0e-9){if(+P(+gm)<=1.0e-6){gp=e8;break}gp=0.0}else{gp=e8}}while(0);do{if((a[32936]&1)==0){if((a[30528]&1)!=0){gq=gp;break}fe=c[200]|0;if((a[64788+(fe*688&-1)|0]&1)==0){gq=gp;break}gq=+Z(+(gp*+h[64800+(fe*688&-1)>>3]))}else{gq=gp}}while(0);c[7862]=2;h[3932]=gq;h[3933]=0.0;e4(c[7858]|0,t);L7354:do{if((a[1960]&1)==0){fe=c[f$>>2]|0;if((fe|0)==2){gr=+h[f_>>3]}else if((fe|0)==3){aP=5407;break L7293}else if((fe|0)==1){gr=0.0}else{aP=5408;break L7293}e8=+P(+gr);if(e8>+h[11]){aP=5410;break}uE((c[cb>>2]|0)+(e1<<6)|0,0,64);if((fe|0)==1){gs=+(c[fZ>>2]|0)}else if((fe|0)==2){gs=+h[fJ>>3]}else if((fe|0)==3){gs=+uz(c[fI>>2]|0,0)}else{aP=5415;break L7293}h[(c[cb>>2]|0)+(e1<<6)+24>>3]=-1.0;c[(c[cb>>2]|0)+(e1<<6)>>2]=0;if((a[32936]&1)!=0){h[(c[cb>>2]|0)+(e1<<6)+8>>3]=gp;h[(c[cb>>2]|0)+(e1<<6)+16>>3]=gs;if(+h[7030]<0.0){break}if((a[56232]&1)==0){break}h[(c[cb>>2]|0)+(e1<<6)+24>>3]=0.0;break}if((a[30528]&1)==0){h[(c[cb>>2]|0)+(e1<<6)+8>>3]=gp;fe=c[bB>>2]|0;if((fe|0)==153&+h[7030]>=0.0){L7374:do{if((a[56232]&1)!=0){h[(c[cb>>2]|0)+(e1<<6)+24>>3]=0.0;d2=c[c9>>2]|0;if((a[64788+(d2*688&-1)|0]&1)==0){e8=+h[7030]*.5;gt=gq+e8;gu=gq-e8}else{e8=+h[64792+(d2*688&-1)>>3];aW=+h[7030];fl=gq*+R(+e8,+(aW*-.5));gt=gq*+R(+e8,+(aW*.5));gu=fl}d2=c[200]|0;if((d2|0)==99){break}L7381:do{if(gu>-8.988465674311579e+307&gu<8.988465674311579e+307){do{if((a[64788+(d2*688&-1)|0]&1)==0){h[(c[cb>>2]|0)+(e1<<6)+48>>3]=gu}else{if(gu<0.0){break L7381}if(gu==0.0){h[(c[cb>>2]|0)+(e1<<6)+48>>3]=-8.988465674311579e+307;break L7381}else{fl=+_(+gu);h[(c[cb>>2]|0)+(e1<<6)+48>>3]=fl/+h[64800+(d2*688&-1)>>3];break}}}while(0);if((a[bR]&1)!=0|(d2|0)<0){break}bs=64712+(d2*688&-1)|0;if(gu<+h[bs>>3]){h[bs>>3]=gu}bs=64664+(d2*688&-1)|0;do{if(gu<+h[bs>>3]){if((c[64648+(d2*688&-1)>>2]&1|0)==0){break L7381}if((c[64728+(d2*688&-1)>>2]&1|0)==0){h[bs>>3]=gu;break}fl=+h[64736+(d2*688&-1)>>3];if(fl>gu){h[bs>>3]=fl;break L7381}else{h[bs>>3]=gu;break}}}while(0);bs=64720+(d2*688&-1)|0;if(gu>+h[bs>>3]){h[bs>>3]=gu}bs=64672+(d2*688&-1)|0;if(gu<=+h[bs>>3]){break}if((c[64648+(d2*688&-1)>>2]&2|0)==0){break}if((c[64732+(d2*688&-1)>>2]&2|0)==0){h[bs>>3]=gu;break}fl=+h[64760+(d2*688&-1)>>3];if(fl<gu){h[bs>>3]=fl;break}else{h[bs>>3]=gu;break}}}while(0);if(!(gt>-8.988465674311579e+307&gt<8.988465674311579e+307)){break}do{if((a[64788+(d2*688&-1)|0]&1)==0){h[(c[cb>>2]|0)+(e1<<6)+56>>3]=gt}else{if(gt<0.0){break L7374}if(gt==0.0){h[(c[cb>>2]|0)+(e1<<6)+56>>3]=-8.988465674311579e+307;break L7374}else{fl=+_(+gt);h[(c[cb>>2]|0)+(e1<<6)+56>>3]=fl/+h[64800+(d2*688&-1)>>3];break}}}while(0);if((a[bR]&1)!=0|(d2|0)<0){break}bo=64712+(d2*688&-1)|0;if(gt<+h[bo>>3]){h[bo>>3]=gt}bo=64664+(d2*688&-1)|0;do{if(gt<+h[bo>>3]){if((c[64648+(d2*688&-1)>>2]&1|0)==0){break L7374}if((c[64728+(d2*688&-1)>>2]&1|0)==0){h[bo>>3]=gt;break}fl=+h[64736+(d2*688&-1)>>3];if(fl>gt){h[bo>>3]=fl;break L7374}else{h[bo>>3]=gt;break}}}while(0);bo=64720+(d2*688&-1)|0;if(gt>+h[bo>>3]){h[bo>>3]=gt}bo=64672+(d2*688&-1)|0;if(gt<=+h[bo>>3]){break}if((c[64648+(d2*688&-1)>>2]&2|0)==0){break}if((c[64732+(d2*688&-1)>>2]&2|0)==0){h[bo>>3]=gt;break}fl=+h[64760+(d2*688&-1)>>3];if(fl<gt){h[bo>>3]=fl;break}else{h[bo>>3]=gt;break}}}while(0);gv=c[bB>>2]|0}else{gv=fe}if((gv|0)==457){h[(c[cb>>2]|0)+(e1<<6)+24>>3]=-1.0;h[(c[cb>>2]|0)+(e1<<6)+32>>3]=0.0;h[(c[cb>>2]|0)+(e1<<6)+56>>3]=360.0}else if((gv|0)==489){h[(c[cb>>2]|0)+(e1<<6)+24>>3]=-1.0;h[(c[cb>>2]|0)+(e1<<6)+32>>3]=+h[6214]}if(fw){if((c[200]|0)!=0){break}}else{if((c[144]|0)==99){break}}if(!(gs>-8.988465674311579e+307&gs<8.988465674311579e+307)){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break}bw=c[200]|0;bo=fw?bw:c[144]|0;do{if((a[64788+(bo*688&-1)|0]&1)==0){h[(c[cb>>2]|0)+(e1<<6)+16>>3]=gs}else{if(gs<0.0){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break L7354}if(gs==0.0){h[(c[cb>>2]|0)+(e1<<6)+16>>3]=-8.988465674311579e+307;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7354}else{fl=+_(+gs);h[(c[cb>>2]|0)+(e1<<6)+16>>3]=fl/+h[64800+(bo*688&-1)>>3];break}}}while(0);if((a[bR]&1)!=0){break}if((c[(c[cb>>2]|0)+(e1<<6)>>2]|0)!=0){break}if(!((bw|0)==0|fb)){break}fe=64712+(bo*688&-1)|0;if(gs<+h[fe>>3]){h[fe>>3]=gs}fe=64664+(bo*688&-1)|0;do{if(gs<+h[fe>>3]){if((c[64648+(bo*688&-1)>>2]&1|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7354}if((c[64728+(bo*688&-1)>>2]&1|0)==0){h[fe>>3]=gs;break}fl=+h[64736+(bo*688&-1)>>3];if(fl>gs){h[fe>>3]=fl;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7354}else{h[fe>>3]=gs;break}}}while(0);fe=64720+(bo*688&-1)|0;if(gs>+h[fe>>3]){h[fe>>3]=gs}fe=64672+(bo*688&-1)|0;if(gs<=+h[fe>>3]){break}if((c[64648+(bo*688&-1)>>2]&2|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}if((c[64732+(bo*688&-1)>>2]&2|0)==0){h[fe>>3]=gs;break}fl=+h[64760+(bo*688&-1)>>3];if(fl<gs){h[fe>>3]=fl;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}else{h[fe>>3]=gs;break}}do{if(gs>+h[8686]){if((c[17366]&2|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}else{h[8686]=gs;break}}}while(0);fl=+h[8685];do{if(gs<fl){if((c[17366]&1|0)==0){gw=fl;break}h[8685]=0.0;gw=0.0}else{gw=fl}}while(0);bo=(a[69604]&1)==0;do{if(bo){if((c[17366]&1|0)!=0){gx=gs;break}gx=gs-gw}else{fl=+_(+gs);aW=+h[8702];gx=fl/aW- +_(+gw)/aW}}while(0);aW=gq*+h[9040];fl=+T(+aW);e8=gx*fl;fx=+S(+aW);aW=gx*fx;L7521:do{if((c[bB>>2]|0)==345){if((c[bO>>2]|0)!=10){break}fE=+h[B>>3];if(bo){gy=gw;gz=fE}else{fy=+_(+fE);fE=+h[8702];gy=+_(+gw)/fE;gz=fy/fE}fE=gz-gy;fy=fE*fl;fv=fE*fx;fe=c[200]|0;L7527:do{if((fe|0)!=99){if(!(fv>-8.988465674311579e+307&fv<8.988465674311579e+307)){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break L7354}do{if((a[64788+(fe*688&-1)|0]&1)==0){h[(c[cb>>2]|0)+(e1<<6)+56>>3]=fv}else{if(fv<0.0){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break L7354}if(fv==0.0){h[(c[cb>>2]|0)+(e1<<6)+56>>3]=-8.988465674311579e+307;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7527}else{fE=+_(+fv);h[(c[cb>>2]|0)+(e1<<6)+56>>3]=fE/+h[64800+(fe*688&-1)>>3];break}}}while(0);if((a[bR]&1)!=0){break}if((c[(c[cb>>2]|0)+(e1<<6)>>2]|0)!=0|(fe|0)<0){break}bw=64712+(fe*688&-1)|0;if(fv<+h[bw>>3]){h[bw>>3]=fv}bw=64664+(fe*688&-1)|0;do{if(fv<+h[bw>>3]){if((c[64648+(fe*688&-1)>>2]&1|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7527}if((c[64728+(fe*688&-1)>>2]&1|0)==0){h[bw>>3]=fv;break}fE=+h[64736+(fe*688&-1)>>3];if(fE>fv){h[bw>>3]=fE;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7527}else{h[bw>>3]=fv;break}}}while(0);bw=64720+(fe*688&-1)|0;if(fv>+h[bw>>3]){h[bw>>3]=fv}bw=64672+(fe*688&-1)|0;if(fv<=+h[bw>>3]){break}if((c[64648+(fe*688&-1)>>2]&2|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}if((c[64732+(fe*688&-1)>>2]&2|0)==0){h[bw>>3]=fv;break}fE=+h[64760+(fe*688&-1)>>3];if(fE<fv){h[bw>>3]=fE;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}else{h[bw>>3]=fv;break}}}while(0);fe=c[144]|0;if((fe|0)==99){break}if(!(fy>-8.988465674311579e+307&fy<8.988465674311579e+307)){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break L7354}do{if((a[64788+(fe*688&-1)|0]&1)==0){h[(c[cb>>2]|0)+(e1<<6)+40>>3]=fy}else{if(fy<0.0){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break L7354}if(fy==0.0){h[(c[cb>>2]|0)+(e1<<6)+40>>3]=-8.988465674311579e+307;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7521}else{fv=+_(+fy);h[(c[cb>>2]|0)+(e1<<6)+40>>3]=fv/+h[64800+(fe*688&-1)>>3];break}}}while(0);if((a[bR]&1)!=0){break}if((c[(c[cb>>2]|0)+(e1<<6)>>2]|0)!=0|(fe|0)<0){break}d2=64712+(fe*688&-1)|0;if(fy<+h[d2>>3]){h[d2>>3]=fy}d2=64664+(fe*688&-1)|0;do{if(fy<+h[d2>>3]){if((c[64648+(fe*688&-1)>>2]&1|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7521}if((c[64728+(fe*688&-1)>>2]&1|0)==0){h[d2>>3]=fy;break}fv=+h[64736+(fe*688&-1)>>3];if(fv>fy){h[d2>>3]=fv;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7521}else{h[d2>>3]=fy;break}}}while(0);d2=64720+(fe*688&-1)|0;if(fy>+h[d2>>3]){h[d2>>3]=fy}d2=64672+(fe*688&-1)|0;if(fy<=+h[d2>>3]){break}if((c[64648+(fe*688&-1)>>2]&2|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}if((c[64732+(fe*688&-1)>>2]&2|0)==0){h[d2>>3]=fy;break}fv=+h[64760+(fe*688&-1)>>3];if(fv<fy){h[d2>>3]=fv;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}else{h[d2>>3]=fy;break}}}while(0);bo=c[200]|0;L7618:do{if((bo|0)!=99){if(!(aW>-8.988465674311579e+307&aW<8.988465674311579e+307)){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break L7354}do{if((a[64788+(bo*688&-1)|0]&1)==0){h[(c[cb>>2]|0)+(e1<<6)+8>>3]=aW}else{if(aW<0.0){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break L7354}if(aW==0.0){h[(c[cb>>2]|0)+(e1<<6)+8>>3]=-8.988465674311579e+307;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7618}else{fx=+_(+aW);h[(c[cb>>2]|0)+(e1<<6)+8>>3]=fx/+h[64800+(bo*688&-1)>>3];break}}}while(0);if((a[bR]&1)!=0){break}if((c[(c[cb>>2]|0)+(e1<<6)>>2]|0)!=0|(bo|0)<0){break}fe=64712+(bo*688&-1)|0;if(aW<+h[fe>>3]){h[fe>>3]=aW}fe=64664+(bo*688&-1)|0;do{if(aW<+h[fe>>3]){if((c[64648+(bo*688&-1)>>2]&1|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7618}if((c[64728+(bo*688&-1)>>2]&1|0)==0){h[fe>>3]=aW;break}fy=+h[64736+(bo*688&-1)>>3];if(fy>aW){h[fe>>3]=fy;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7618}else{h[fe>>3]=aW;break}}}while(0);fe=64720+(bo*688&-1)|0;if(aW>+h[fe>>3]){h[fe>>3]=aW}fe=64672+(bo*688&-1)|0;if(aW<=+h[fe>>3]){break}if((c[64648+(bo*688&-1)>>2]&2|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}if((c[64732+(bo*688&-1)>>2]&2|0)==0){h[fe>>3]=aW;break}fy=+h[64760+(bo*688&-1)>>3];if(fy<aW){h[fe>>3]=fy;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}else{h[fe>>3]=aW;break}}}while(0);bo=c[144]|0;if((bo|0)==99){break}if(!(e8>-8.988465674311579e+307&e8<8.988465674311579e+307)){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break}do{if((a[64788+(bo*688&-1)|0]&1)==0){h[(c[cb>>2]|0)+(e1<<6)+16>>3]=e8}else{if(e8<0.0){c[(c[cb>>2]|0)+(e1<<6)>>2]=2;break L7354}if(e8==0.0){h[(c[cb>>2]|0)+(e1<<6)+16>>3]=-8.988465674311579e+307;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7354}else{aW=+_(+e8);h[(c[cb>>2]|0)+(e1<<6)+16>>3]=aW/+h[64800+(bo*688&-1)>>3];break}}}while(0);if((a[bR]&1)!=0){break}if((c[(c[cb>>2]|0)+(e1<<6)>>2]|0)!=0|(bo|0)<0){break}fe=64712+(bo*688&-1)|0;if(e8<+h[fe>>3]){h[fe>>3]=e8}fe=64664+(bo*688&-1)|0;do{if(e8<+h[fe>>3]){if((c[64648+(bo*688&-1)>>2]&1|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7354}if((c[64728+(bo*688&-1)>>2]&1|0)==0){h[fe>>3]=e8;break}aW=+h[64736+(bo*688&-1)>>3];if(aW>e8){h[fe>>3]=aW;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break L7354}else{h[fe>>3]=e8;break}}}while(0);fe=64720+(bo*688&-1)|0;if(e8>+h[fe>>3]){h[fe>>3]=e8}fe=64672+(bo*688&-1)|0;if(e8<=+h[fe>>3]){break}if((c[64648+(bo*688&-1)>>2]&2|0)==0){c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}if((c[64732+(bo*688&-1)>>2]&2|0)==0){h[fe>>3]=e8;break}aW=+h[64760+(bo*688&-1)>>3];if(aW<e8){h[fe>>3]=aW;c[(c[cb>>2]|0)+(e1<<6)>>2]=1;break}else{h[fe>>3]=e8;break}}else{aP=5410}}while(0);if((aP|0)==5410){aP=0;c[(c[cb>>2]|0)+(e1<<6)>>2]=2}fe=e1+1|0;if((fe|0)<(c[6352]|0)){e1=fe}else{gA=fe;break}}}else{gA=0}c[fH+300>>2]=gA;c[13898]=c[fH+4>>2];e1=c[fH>>2]|0;if(fw){gB=aQ;gC=gn;gD=0;gE=1;gF=gm;gG=e1;break L7307}else{gH=aQ;gI=gn;gJ=0;gK=gm;gL=e1;aP=5665;break L7307}}}while(0);c[13898]=c[fH+4>>2];c9=c[fH>>2]|0;if(f0){gB=aQ;gC=aq;gD=0;gE=1;gF=fT;gG=c9}else{gH=aQ;gI=aq;gJ=0;gK=fT;gL=c9;aP=5665}}else if((aP|0)==5664){aP=0;if(f0){gB=f9;gC=aq;gD=bJ;gE=1;gF=fT;gG=fH}else{gH=f9;gI=aq;gJ=bJ;gK=fT;gL=fH;aP=5665}}}while(0);do{if((aP|0)==5665){aP=0;if(!(ix(c[7774]|0)|0)){gB=gH;gC=gI;gD=gJ;gE=0;gF=gK;gG=gL;break}c[13898]=gd;fH=gL;fT=gK;f0=0;fG=gJ;fF=gd;aq=gI;f9=gH;continue L7293}}while(0);bJ=c[7774]|0;if((bJ|0)!=0){c9=bJ;while(1){bJ=c[c9>>2]|0;uu(c[c9+12>>2]|0);uu(c9);if((bJ|0)==0){break}else{c9=bJ}}}c[7774]=0;c9=c[13898]|0;if((c[8272]|0)<=(c9|0)){gM=gB;break}bJ=c[1054]|0;if((a[bJ+(c9*40&-1)|0]&1)==0){gM=gB;break}e1=c[bJ+(c9*40&-1)+36>>2]|0;cb=bJ+(c9*40&-1)+32|0;bJ=c[10036]|0;bR=0;while(1){if((bR|0)>=(e1|0)){break}if((a[bJ+((c[cb>>2]|0)+bR|0)|0]|0)==(a[bR+148464|0]|0)){bR=bR+1|0}else{gM=gB;break L7293}}if((bR|0)!=1){gM=gB;break}c[13898]=c9+1;if(gE){fH=gG;fT=gF;f0=1;fG=gD;fF=gd;aq=gC;f9=gB;continue}c[7774]=iw()|0;fH=gG;fT=gF;f0=0;fG=gD;fF=gd;aq=gC;f9=gB}if((aP|0)==5377){uh(ge,136552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);gM=f9}else if((aP|0)==5391){uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56832+(gi*24&-1),v)|0)}else if((aP|0)==5407){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5408){uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aP|0)==5415){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[32936]&1)==0){ga=gM;break}fF=c[10814]|0;c[d>>2]=fF;L7738:do{if((gM|0)<1){gN=d;gO=0;gP=gM}else{fG=d;f0=0;fH=fF;fI=1;fJ=gM;while(1){fZ=fG;f_=fH;f$=fI;while(1){gQ=f_|0;gR=c[gQ>>2]|0;if((c[f_+8>>2]|0)==0){break}f1=f$+1|0;if((f1|0)>(fJ|0)){gN=gQ;gO=f0;gP=fJ;break L7738}else{fZ=gQ;f_=gR;f$=f1}}aQ=fJ-1|0;f1=c[f_+300>>2]|0;cb=gR+300|0;if((f1|0)!=(c[cb>>2]|0)){break}if((f1|0)>0){f1=f_+320|0;bJ=gR+320|0;e1=gR+304|0;B=gR+308|0;bO=f_+23|0;bB=f_+304|0;fb=f_+308|0;fe=(a[30528]&1)==0;d2=0;do{L7750:do{if(fe){aq=+h[(c[f1>>2]|0)+(d2<<6)+16>>3];bw=c[bJ>>2]|0;fT=+h[bw+(d2<<6)+16>>3];aW=+h[7030];L7933:do{if(aW<0.0){aP=5874}else{if((a[56232]&1)==0){aP=5874;break}bs=c[e1>>2]|0;if((bs|0)==99){break}fy=aq-aW*.5;L7937:do{if(fy>-8.988465674311579e+307&fy<8.988465674311579e+307){do{if((a[64788+(bs*688&-1)|0]&1)==0){h[bw+(d2<<6)+48>>3]=fy}else{if(fy<0.0){aP=5845;break L7937}if(fy==0.0){h[bw+(d2<<6)+48>>3]=-8.988465674311579e+307;aP=5845;break L7937}else{fx=+_(+fy);h[bw+(d2<<6)+48>>3]=fx/+h[64800+(bs*688&-1)>>3];break}}}while(0);if((a[bO]&1)!=0){aP=5845;break}ea=c[e1>>2]|0;if((ea|0)<0){gS=ea;break}fx=aq- +h[7030]*.5;bV=64712+(ea*688&-1)|0;if(fx<+h[bV>>3]){h[bV>>3]=fx;gT=c[e1>>2]|0}else{gT=ea}ea=64664+(gT*688&-1)|0;do{if(fx<+h[ea>>3]){if((c[64648+(gT*688&-1)>>2]&1|0)==0){aP=5845;break L7937}if((c[64728+(gT*688&-1)>>2]&1|0)==0){h[ea>>3]=fx;break}fl=+h[64736+(gT*688&-1)>>3];if(fl>fx){h[ea>>3]=fl;aP=5845;break L7937}else{h[ea>>3]=fx;break}}}while(0);ea=c[e1>>2]|0;bV=64720+(ea*688&-1)|0;if(fx>+h[bV>>3]){h[bV>>3]=fx;gU=c[e1>>2]|0}else{gU=ea}ea=64672+(gU*688&-1)|0;if(fx<=+h[ea>>3]){gV=gU;aP=5846;break}if((c[64648+(gU*688&-1)>>2]&2|0)==0){aP=5845;break}if((c[64732+(gU*688&-1)>>2]&2|0)==0){h[ea>>3]=fx;aP=5845;break}fl=+h[64760+(gU*688&-1)>>3];if(fl<fx){h[ea>>3]=fl;aP=5845;break}else{h[ea>>3]=fx;aP=5845;break}}else{aP=5845}}while(0);if((aP|0)==5845){aP=0;gV=c[e1>>2]|0;aP=5846}if((aP|0)==5846){aP=0;if((gV|0)==99){aP=5874;break}else{gS=gV}}fy=aq+ +h[7030]*.5;if(!(fy>-8.988465674311579e+307&fy<8.988465674311579e+307)){aP=5874;break}do{if((a[64788+(gS*688&-1)|0]&1)==0){h[(c[bJ>>2]|0)+(d2<<6)+56>>3]=fy}else{if(fy<0.0){aP=5874;break L7933}if(fy==0.0){h[(c[bJ>>2]|0)+(d2<<6)+56>>3]=-8.988465674311579e+307;aP=5874;break L7933}else{e8=+_(+fy);h[(c[bJ>>2]|0)+(d2<<6)+56>>3]=e8/+h[64800+(gS*688&-1)>>3];break}}}while(0);if((a[bO]&1)!=0){aP=5874;break}bs=c[e1>>2]|0;if((bs|0)<0){gW=bs;aP=5875;break}fy=aq+ +h[7030]*.5;bo=64712+(bs*688&-1)|0;if(fy<+h[bo>>3]){h[bo>>3]=fy;gX=c[e1>>2]|0}else{gX=bs}bs=64664+(gX*688&-1)|0;do{if(fy<+h[bs>>3]){if((c[64648+(gX*688&-1)>>2]&1|0)==0){aP=5874;break L7933}if((c[64728+(gX*688&-1)>>2]&1|0)==0){h[bs>>3]=fy;break}e8=+h[64736+(gX*688&-1)>>3];if(e8>fy){h[bs>>3]=e8;aP=5874;break L7933}else{h[bs>>3]=fy;break}}}while(0);bs=c[e1>>2]|0;bo=64720+(bs*688&-1)|0;if(fy>+h[bo>>3]){h[bo>>3]=fy;gY=c[e1>>2]|0}else{gY=bs}bs=64672+(gY*688&-1)|0;if(fy<=+h[bs>>3]){aP=5874;break}if((c[64648+(gY*688&-1)>>2]&2|0)==0){aP=5874;break}if((c[64732+(gY*688&-1)>>2]&2|0)==0){h[bs>>3]=fy;aP=5874;break}e8=+h[64760+(gY*688&-1)>>3];if(e8<fy){h[bs>>3]=e8;aP=5874;break}else{h[bs>>3]=fy;aP=5874;break}}}while(0);if((aP|0)==5874){aP=0;bw=c[e1>>2]|0;if((bw|0)!=99){gW=bw;aP=5875}}L8015:do{if((aP|0)==5875){aP=0;if(!(aq>-8.988465674311579e+307&aq<8.988465674311579e+307)){c[(c[bJ>>2]|0)+(d2<<6)>>2]=2;break}do{if((a[64788+(gW*688&-1)|0]&1)==0){h[(c[bJ>>2]|0)+(d2<<6)+8>>3]=aq}else{if(aq<0.0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=2;break L8015}if(aq==0.0){h[(c[bJ>>2]|0)+(d2<<6)+8>>3]=-8.988465674311579e+307;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L8015}else{aW=+_(+aq);h[(c[bJ>>2]|0)+(d2<<6)+8>>3]=aW/+h[64800+(gW*688&-1)>>3];break}}}while(0);if((a[bO]&1)!=0){break}if((c[(c[bJ>>2]|0)+(d2<<6)>>2]|0)!=0){break}bw=c[e1>>2]|0;if((bw|0)<0){break}bs=64712+(bw*688&-1)|0;if(aq<+h[bs>>3]){h[bs>>3]=aq;gZ=c[e1>>2]|0}else{gZ=bw}bw=64664+(gZ*688&-1)|0;do{if(aq<+h[bw>>3]){if((c[64648+(gZ*688&-1)>>2]&1|0)==0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L8015}if((c[64728+(gZ*688&-1)>>2]&1|0)==0){h[bw>>3]=aq;break}fy=+h[64736+(gZ*688&-1)>>3];if(fy>aq){h[bw>>3]=fy;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L8015}else{h[bw>>3]=aq;break}}}while(0);bw=c[e1>>2]|0;bs=64720+(bw*688&-1)|0;if(aq>+h[bs>>3]){h[bs>>3]=aq;g_=c[e1>>2]|0}else{g_=bw}bw=64672+(g_*688&-1)|0;if(aq<=+h[bw>>3]){break}if((c[64648+(g_*688&-1)>>2]&2|0)==0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break}if((c[64732+(g_*688&-1)>>2]&2|0)==0){h[bw>>3]=aq;break}fy=+h[64760+(g_*688&-1)>>3];if(fy<aq){h[bw>>3]=fy;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break}else{h[bw>>3]=aq;break}}}while(0);bw=c[B>>2]|0;if((bw|0)==99){break}if(!(fT>-8.988465674311579e+307&fT<8.988465674311579e+307)){c[(c[bJ>>2]|0)+(d2<<6)>>2]=2;break}do{if((a[64788+(bw*688&-1)|0]&1)==0){h[(c[bJ>>2]|0)+(d2<<6)+16>>3]=fT}else{if(fT<0.0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=2;break L7750}if(fT==0.0){h[(c[bJ>>2]|0)+(d2<<6)+16>>3]=-8.988465674311579e+307;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7750}else{aq=+_(+fT);h[(c[bJ>>2]|0)+(d2<<6)+16>>3]=aq/+h[64800+(bw*688&-1)>>3];break}}}while(0);if((a[bO]&1)!=0){break}if((c[(c[bJ>>2]|0)+(d2<<6)>>2]|0)!=0){break}bw=c[B>>2]|0;if((bw|0)<0){break}bs=64712+(bw*688&-1)|0;if(fT<+h[bs>>3]){h[bs>>3]=fT;g$=c[B>>2]|0}else{g$=bw}bw=64664+(g$*688&-1)|0;do{if(fT<+h[bw>>3]){if((c[64648+(g$*688&-1)>>2]&1|0)==0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7750}if((c[64728+(g$*688&-1)>>2]&1|0)==0){h[bw>>3]=fT;break}aq=+h[64736+(g$*688&-1)>>3];if(aq>fT){h[bw>>3]=aq;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7750}else{h[bw>>3]=fT;break}}}while(0);bw=c[B>>2]|0;bs=64720+(bw*688&-1)|0;if(fT>+h[bs>>3]){h[bs>>3]=fT;g0=c[B>>2]|0}else{g0=bw}bw=64672+(g0*688&-1)|0;if(fT<=+h[bw>>3]){break}if((c[64648+(g0*688&-1)>>2]&2|0)==0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break}if((c[64732+(g0*688&-1)>>2]&2|0)==0){h[bw>>3]=fT;break}aq=+h[64760+(g0*688&-1)>>3];if(aq<fT){h[bw>>3]=aq;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break}else{h[bw>>3]=fT;break}}else{bw=c[bJ>>2]|0;aq=+h[bw+(d2<<6)+16>>3];fy=+h[(c[f1>>2]|0)+(d2<<6)+16>>3]*+h[9040];bs=c[17366]|0;if((bs&2|0)==0&aq>+h[8686]){c[bw+(d2<<6)>>2]=1;g1=c[17366]|0}else{g1=bs}if((g1&1|0)==0){g2=aq- +h[8685]}else{g2=aq}aq=g2*+S(+fy);aW=g2*+T(+fy);fy=+h[7030];L7758:do{if(fy<0.0){aP=5749}else{if((a[56232]&1)==0){aP=5749;break}bs=c[bB>>2]|0;if((bs|0)==99){break}e8=aq-fy*.5;L7762:do{if(e8>-8.988465674311579e+307&e8<8.988465674311579e+307){do{if((a[64788+(bs*688&-1)|0]&1)==0){h[(c[bJ>>2]|0)+(d2<<6)+48>>3]=e8}else{if(e8<0.0){aP=5720;break L7762}if(e8==0.0){h[(c[bJ>>2]|0)+(d2<<6)+48>>3]=-8.988465674311579e+307;aP=5720;break L7762}else{fl=+_(+e8);h[(c[bJ>>2]|0)+(d2<<6)+48>>3]=fl/+h[64800+(bs*688&-1)>>3];break}}}while(0);if((a[bO]&1)!=0){aP=5720;break}bw=c[bB>>2]|0;if((bw|0)<0){g3=bw;break}fx=aq- +h[7030]*.5;bo=64712+(bw*688&-1)|0;if(fx<+h[bo>>3]){h[bo>>3]=fx;g4=c[bB>>2]|0}else{g4=bw}bw=64664+(g4*688&-1)|0;do{if(fx<+h[bw>>3]){if((c[64648+(g4*688&-1)>>2]&1|0)==0){aP=5720;break L7762}if((c[64728+(g4*688&-1)>>2]&1|0)==0){h[bw>>3]=fx;break}fl=+h[64736+(g4*688&-1)>>3];if(fl>fx){h[bw>>3]=fl;aP=5720;break L7762}else{h[bw>>3]=fx;break}}}while(0);bw=c[bB>>2]|0;bo=64720+(bw*688&-1)|0;if(fx>+h[bo>>3]){h[bo>>3]=fx;g5=c[bB>>2]|0}else{g5=bw}bw=64672+(g5*688&-1)|0;if(fx<=+h[bw>>3]){g6=g5;aP=5721;break}if((c[64648+(g5*688&-1)>>2]&2|0)==0){aP=5720;break}if((c[64732+(g5*688&-1)>>2]&2|0)==0){h[bw>>3]=fx;aP=5720;break}fl=+h[64760+(g5*688&-1)>>3];if(fl<fx){h[bw>>3]=fl;aP=5720;break}else{h[bw>>3]=fx;aP=5720;break}}else{aP=5720}}while(0);if((aP|0)==5720){aP=0;g6=c[bB>>2]|0;aP=5721}if((aP|0)==5721){aP=0;if((g6|0)==99){aP=5749;break}else{g3=g6}}e8=aq+ +h[7030]*.5;if(!(e8>-8.988465674311579e+307&e8<8.988465674311579e+307)){aP=5749;break}do{if((a[64788+(g3*688&-1)|0]&1)==0){h[(c[bJ>>2]|0)+(d2<<6)+56>>3]=e8}else{if(e8<0.0){aP=5749;break L7758}if(e8==0.0){h[(c[bJ>>2]|0)+(d2<<6)+56>>3]=-8.988465674311579e+307;aP=5749;break L7758}else{fl=+_(+e8);h[(c[bJ>>2]|0)+(d2<<6)+56>>3]=fl/+h[64800+(g3*688&-1)>>3];break}}}while(0);if((a[bO]&1)!=0){aP=5749;break}bs=c[bB>>2]|0;if((bs|0)<0){g7=bs;aP=5750;break}e8=aq+ +h[7030]*.5;bw=64712+(bs*688&-1)|0;if(e8<+h[bw>>3]){h[bw>>3]=e8;g8=c[bB>>2]|0}else{g8=bs}bs=64664+(g8*688&-1)|0;do{if(e8<+h[bs>>3]){if((c[64648+(g8*688&-1)>>2]&1|0)==0){aP=5749;break L7758}if((c[64728+(g8*688&-1)>>2]&1|0)==0){h[bs>>3]=e8;break}fl=+h[64736+(g8*688&-1)>>3];if(fl>e8){h[bs>>3]=fl;aP=5749;break L7758}else{h[bs>>3]=e8;break}}}while(0);bs=c[bB>>2]|0;bw=64720+(bs*688&-1)|0;if(e8>+h[bw>>3]){h[bw>>3]=e8;g9=c[bB>>2]|0}else{g9=bs}bs=64672+(g9*688&-1)|0;if(e8<=+h[bs>>3]){aP=5749;break}if((c[64648+(g9*688&-1)>>2]&2|0)==0){aP=5749;break}if((c[64732+(g9*688&-1)>>2]&2|0)==0){h[bs>>3]=e8;aP=5749;break}fl=+h[64760+(g9*688&-1)>>3];if(fl<e8){h[bs>>3]=fl;aP=5749;break}else{h[bs>>3]=e8;aP=5749;break}}}while(0);if((aP|0)==5749){aP=0;bs=c[bB>>2]|0;if((bs|0)!=99){g7=bs;aP=5750}}L7840:do{if((aP|0)==5750){aP=0;if(!(aq>-8.988465674311579e+307&aq<8.988465674311579e+307)){c[(c[bJ>>2]|0)+(d2<<6)>>2]=2;break}do{if((a[64788+(g7*688&-1)|0]&1)==0){h[(c[bJ>>2]|0)+(d2<<6)+8>>3]=aq}else{if(aq<0.0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=2;break L7840}if(aq==0.0){h[(c[bJ>>2]|0)+(d2<<6)+8>>3]=-8.988465674311579e+307;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7840}else{fy=+_(+aq);h[(c[bJ>>2]|0)+(d2<<6)+8>>3]=fy/+h[64800+(g7*688&-1)>>3];break}}}while(0);if((a[bO]&1)!=0){break}if((c[(c[bJ>>2]|0)+(d2<<6)>>2]|0)!=0){break}bs=c[bB>>2]|0;if((bs|0)<0){break}bw=64712+(bs*688&-1)|0;if(aq<+h[bw>>3]){h[bw>>3]=aq;ha=c[bB>>2]|0}else{ha=bs}bs=64664+(ha*688&-1)|0;do{if(aq<+h[bs>>3]){if((c[64648+(ha*688&-1)>>2]&1|0)==0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7840}if((c[64728+(ha*688&-1)>>2]&1|0)==0){h[bs>>3]=aq;break}e8=+h[64736+(ha*688&-1)>>3];if(e8>aq){h[bs>>3]=e8;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7840}else{h[bs>>3]=aq;break}}}while(0);bs=c[bB>>2]|0;bw=64720+(bs*688&-1)|0;if(aq>+h[bw>>3]){h[bw>>3]=aq;hb=c[bB>>2]|0}else{hb=bs}bs=64672+(hb*688&-1)|0;if(aq<=+h[bs>>3]){break}if((c[64648+(hb*688&-1)>>2]&2|0)==0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break}if((c[64732+(hb*688&-1)>>2]&2|0)==0){h[bs>>3]=aq;break}e8=+h[64760+(hb*688&-1)>>3];if(e8<aq){h[bs>>3]=e8;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break}else{h[bs>>3]=aq;break}}}while(0);bs=c[fb>>2]|0;if((bs|0)==99){break}if(!(aW>-8.988465674311579e+307&aW<8.988465674311579e+307)){c[(c[bJ>>2]|0)+(d2<<6)>>2]=2;break}do{if((a[64788+(bs*688&-1)|0]&1)==0){h[(c[bJ>>2]|0)+(d2<<6)+16>>3]=aW}else{if(aW<0.0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=2;break L7750}if(aW==0.0){h[(c[bJ>>2]|0)+(d2<<6)+16>>3]=-8.988465674311579e+307;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7750}else{aq=+_(+aW);h[(c[bJ>>2]|0)+(d2<<6)+16>>3]=aq/+h[64800+(bs*688&-1)>>3];break}}}while(0);if((a[bO]&1)!=0){break}if((c[(c[bJ>>2]|0)+(d2<<6)>>2]|0)!=0){break}bs=c[fb>>2]|0;if((bs|0)<0){break}bw=64712+(bs*688&-1)|0;if(aW<+h[bw>>3]){h[bw>>3]=aW;hc=c[fb>>2]|0}else{hc=bs}bs=64664+(hc*688&-1)|0;do{if(aW<+h[bs>>3]){if((c[64648+(hc*688&-1)>>2]&1|0)==0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7750}if((c[64728+(hc*688&-1)>>2]&1|0)==0){h[bs>>3]=aW;break}aq=+h[64736+(hc*688&-1)>>3];if(aq>aW){h[bs>>3]=aq;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break L7750}else{h[bs>>3]=aW;break}}}while(0);bs=c[fb>>2]|0;bw=64720+(bs*688&-1)|0;if(aW>+h[bw>>3]){h[bw>>3]=aW;hd=c[fb>>2]|0}else{hd=bs}bs=64672+(hd*688&-1)|0;if(aW<=+h[bs>>3]){break}if((c[64648+(hd*688&-1)>>2]&2|0)==0){c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break}if((c[64732+(hd*688&-1)>>2]&2|0)==0){h[bs>>3]=aW;break}aq=+h[64760+(hd*688&-1)>>3];if(aq<aW){h[bs>>3]=aq;c[(c[bJ>>2]|0)+(d2<<6)>>2]=1;break}else{h[bs>>3]=aW;break}}}while(0);d2=d2+1|0;}while((d2|0)<(c[cb>>2]|0))}c[gQ>>2]=f0;c[fZ>>2]=gR;cb=gR|0;d2=f$+1|0;if((d2|0)>(aQ|0)){gN=cb;gO=f_;gP=aQ;break L7738}else{fG=cb;f0=f_;fH=c[cb>>2]|0;fI=d2;fJ=aQ}}uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=94952,v)|0)}}while(0);c[10814]=c[d>>2];c[gN>>2]=gO;dc(2,0);if((c[x>>2]|0)==0){ga=gP;break}dc(6,0);ga=gP}}while(0);gP=c[10814]|0;if((ga|0)==0|(gP|0)==0){uf(c[13898]|0,134776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}gO=(c[z>>2]|0)==0;do{if(gO){if((c[x>>2]|0)!=0){aP=5960;break}if((c[gP+8>>2]|0)!=4){aP=5959;break}uf(-1,100088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{g2=+h[8256];gC=+h[8255];if(g2==-8.988465674311579e+307|gC==8.988465674311579e+307){uf(-1,99648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[66036]&1)==0){he=gC;hf=g2}else{h[8255]=g2;h[8256]=gC;he=g2;hf=gC}if((a[66164]&1)==0){aP=5959;break}if(he>0.0&hf>0.0){gC=+_(+he);g2=+h[8272];h[8255]=gC/g2;h[8256]=+_(+hf)/g2;aP=5959;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}}}while(0);do{if((aP|0)==5959){gP=(c[x>>2]|0)==0;if(!gP){aP=5960;break}if(gO){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=98616,v)|0)}z=c[17194]|0;if((z&1|0)!=0){h[8599]=+h[8255]}if((z&2|0)!=0){h[8600]=+h[8256]}if((z|0)!=0){hg=gP;break}if((a[68788]&1)!=0){hf=+h[8599];h[8599]=+h[8600];h[8600]=hf}if((a[68916]&1)==0){hg=gP;break}hf=+h[8599];if(hf<=0.0){uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}he=+h[8600];if(he>0.0){g2=+_(+hf);hf=+h[8616];h[8599]=g2/hf;h[8600]=+_(+he)/hf;hg=gP;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}}}while(0);do{if((aP|0)==5960){hf=+h[8600];he=+h[8599];if(hf==-8.988465674311579e+307|he==8.988465674311579e+307){uf(-1,99648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[68788]&1)==0){hh=he;hi=hf}else{h[8599]=hf;h[8600]=he;hh=hf;hi=he}if((a[68916]&1)==0){hg=0;break}if(hh>0.0&hi>0.0){he=+_(+hh);hf=+h[8616];h[8599]=he/hf;h[8600]=+_(+hi)/hf;hg=0;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}}}while(0);do{if(gO){if(hg){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=98024,v)|0)}aP=c[16506]|0;if((aP&1|0)!=0){h[8255]=+h[8599]}if((aP&2|0)==0){break}h[8256]=+h[8600]}}while(0);hg=(c[y>>2]|0)==0;do{if(!hg){dc(1,97344);if((a[65348]&1)!=0){hi=+h[8169];h[8169]=+h[8170];h[8170]=hi}if((a[65476]&1)==0){break}hi=+h[8169];if(hi<=0.0){uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}hh=+h[8170];if(hh>0.0){hf=+_(+hi);hi=+h[8186];h[8169]=hf/hi;h[8170]=+_(+hh)/hi;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}}}while(0);y=(c[w>>2]|0)==0;do{if(y){if(hg){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=96304,v)|0)}w=c[17022]|0;if((w&1|0)!=0){h[8513]=+h[8169]}if((w&2|0)!=0){h[8514]=+h[8170]}if((w|0)!=0){break}if((a[68100]&1)!=0){hi=+h[8513];h[8513]=+h[8514];h[8514]=hi}if((a[68228]&1)==0){break}hi=+h[8513];if(hi<=0.0){uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}hh=+h[8514];if(hh>0.0){hf=+_(+hi);hi=+h[8530];h[8513]=hf/hi;h[8514]=+_(+hh)/hi;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}}else{dc(5,96768);if((a[68100]&1)!=0){hi=+h[8513];h[8513]=+h[8514];h[8514]=hi}if((a[68228]&1)==0){break}hi=+h[8513];if(hi<=0.0){uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}hh=+h[8514];if(hh>0.0){hf=+_(+hi);hi=+h[8530];h[8513]=hf/hi;h[8514]=+_(+hh)/hi;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}}}while(0);do{if(hg){if(y){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=95912,v)|0)}w=c[16334]|0;if((w&1|0)!=0){h[8169]=+h[8513]}if((w&2|0)==0){break}h[8170]=+h[8514]}}while(0);y=c[7738]|0;L8234:do{if((y|0)!=-1){hg=(c[13898]|0)-1|0;w=c[1054]|0;gO=(c[w+(hg*40&-1)+36>>2]|0)+(c[w+(hg*40&-1)+32>>2]|0)|0;hg=db(c[6924]|0,(gO+1|0)-(c[w+(y*40&-1)+32>>2]|0)|0,116456)|0;c[6924]=hg;w=c[(c[1054]|0)+(y*40&-1)+32>>2]|0;L8236:do{if((w|0)<(gO|0)){aP=w;x=hg;while(1){gP=a[(c[10036]|0)+aP|0]|0;if(gP<<24>>24==0){hj=x;break L8236}z=x+1|0;a[x]=gP;gP=aP+1|0;if((gP|0)<(gO|0)){aP=gP;x=z}else{hj=z;break}}}else{hj=hg}}while(0);a[hj]=0;c[7738]=-1;hg=c[6924]|0;gO=e6(126928)|0;if((gO|0)==0){break}w=gO+8|0;do{if((a[w]&1)==0){x=c[gO+24>>2]|0;if((aY(x|0,hg|0)|0)==0){break L8234}aP=gO+16|0;if((c[aP>>2]|0)!=3){break}uu(x);c[aP>>2]=1}else{a[w]=0}}while(0);if((hg|0)==0){hm=0}else{hm=bP(hg|0)|0}c[gO+16>>2]=3;c[gO+24>>2]=hm}}while(0);hm=c[10814]|0;if((a[14112]&1)!=0){lf(hm,ga);e7(1);i=b;return}fS(hm,ga);hm=0;do{if((c[64656+(hm*688&-1)>>2]&1|0)!=0){hi=+h[64664+(hm*688&-1)>>3];if((a[64788+(hm*688&-1)|0]&1)==0){h[64696+(hm*688&-1)>>3]=hi;ho=+h[64672+(hm*688&-1)>>3]}else{hh=+h[64800+(hm*688&-1)>>3];h[64696+(hm*688&-1)>>3]=+Z(+(hi*hh));ho=+Z(+(+h[64672+(hm*688&-1)>>3]*hh))}h[64704+(hm*688&-1)>>3]=ho}hm=hm+1|0;}while(hm>>>0<11);c[6930]=ga;c[6928]=2;e7(1);i=b;return}function iU(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0.0,z=0.0,A=0.0,B=0,C=0,D=0,E=0;e=(d|0)>0;if(e){f=0;g=b}else{return}while(1){i=c[g+304>>2]|0;j=c[g+308>>2]|0;k=c[g+12>>2]|0;do{if((k|0)==400|(k|0)==416){if((c[64652+(i*688&-1)>>2]|0)==0){if((c[64652+(j*688&-1)>>2]|0)==0){break}}f3(g,1)}else{l=g+300|0;if((c[l>>2]|0)<=0){break}m=g+320|0;n=g+23|0;o=64652+(i*688&-1)|0;p=64672+(i*688&-1)|0;q=64664+(i*688&-1)|0;r=64652+(j*688&-1)|0;s=64672+(j*688&-1)|0;t=64664+(j*688&-1)|0;u=0;do{v=c[m>>2]|0;w=v+(u<<6)|0;do{if((c[w>>2]|0)!=2){c[w>>2]=0;do{if((a[n]&1)==0){x=c[o>>2]|0;do{if((x&1|0)!=0){y=+h[v+(u<<6)+8>>3];if(y>=+h[q>>3]){break}h[q>>3]=y}}while(0);if((x&2|0)==0){break}y=+h[v+(u<<6)+8>>3];if(y<=+h[p>>3]){break}h[p>>3]=y}}while(0);y=+h[q>>3];z=+h[p>>3];A=+h[v+(u<<6)+8>>3];if(y<z){if(A<y|A>z){B=6117}}else{if(A<z|A>y){B=6117}}if((B|0)==6117){B=0;c[w>>2]=1;break}do{if((a[n]&1)==0){C=c[r>>2]|0;do{if((C&1|0)!=0){y=+h[v+(u<<6)+16>>3];if(y>=+h[t>>3]){break}h[t>>3]=y}}while(0);if((C&2|0)==0){break}y=+h[v+(u<<6)+16>>3];if(y<=+h[s>>3]){break}h[s>>3]=y}}while(0);y=+h[t>>3];A=+h[s>>3];z=+h[v+(u<<6)+16>>3];if(y<A){if(!(z<y|z>A)){break}}else{if(!(z<A|z>y)){break}}c[w>>2]=1}}while(0);u=u+1|0;}while((u|0)<(c[l>>2]|0))}}while(0);j=f+1|0;if((j|0)<(d|0)){f=j;g=c[g>>2]|0}else{break}}if(e){D=0;E=b}else{return}while(1){dc(c[E+304>>2]|0,0);dc(c[E+308>>2]|0,0);b=D+1|0;if((b|0)<(d|0)){D=b;E=c[E>>2]|0}else{break}}return}function iV(a,b){a=a|0;b=b|0;return a_(c[(c[a>>2]|0)+60>>2]|0,c[(c[b>>2]|0)+60>>2]|0,64)|0}function iW(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=+g;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0,u=0.0,w=0,x=0,y=0,z=0.0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0;j=i;if((b|0)==0){uf(-1,92360,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{k=b}while(1){l=k|0;m=c[l>>2]|0;if((m|0)==0){break}else{k=m}}m=ut(192)|0;do{if((m|0)==0){gk();n=ut(192)|0;if((n|0)!=0){o=n;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=84552,v)|0)}else{o=m}}while(0);c[l>>2]=o;uD(o|0,k|0,192);k=c[l>>2]|0;c[k>>2]=0;c[k+4>>2]=e;h[k+24>>3]=+h[d+8>>3];h[k+32>>3]=+h[d+16>>3];h[k+40>>3]=+h[d+24>>3];d=k+72|0;do{if((c[d>>2]|0)==6){h[k+80>>3]=g}else{e=c[b+72>>2]|0;if((e|0)==3){if(+h[b+80>>3]>=0.0){break}c[k+76>>2]=~~g;break}else if((e|0)!=7){break}e=c[8798]|0;l=(e|0)>0;o=~~g;L8336:while(1){m=43264;while(1){p=c[m>>2]|0;if((p|0)==0){break}if((c[p+4>>2]|0)==(o|0)){q=6152;break L8336}else{m=p|0}}m=o-1|0;if(!((o|0)>(e|0)&l)){r=1;s=m;t=0.0;break}o=((m|0)%(e|0)&-1)+1|0}do{if((q|0)==6152){e=c[p+48>>2]|0;o=c[p+52>>2]|0;u=+h[p+56>>3];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){r=e;s=o;t=u;break}l=(a[p+40|0]&1)==0;r=l?1:e;s=l?c[p+12>>2]|0:o;t=u}}while(0);c[d>>2]=r;c[k+76>>2]=s;h[k+80>>3]=t}}while(0);L8349:do{if((c[b+88>>2]|0)>0){s=k+128|0;if((c[s>>2]|0)==6){h[k+136>>3]=g;break}do{if((c[b+128>>2]|0)==3){if(+h[b+136>>3]>=0.0){break}c[k+132>>2]=~~g;break L8349}}while(0);if((c[b+92>>2]|0)!=-6){break}r=c[8798]|0;d=(r|0)>0;p=~~g;L8359:while(1){o=43264;while(1){w=c[o>>2]|0;if((w|0)==0){break}if((c[w+4>>2]|0)==(p|0)){q=6168;break L8359}else{o=w|0}}o=p-1|0;if(!((p|0)>(r|0)&d)){x=1;y=o;z=0.0;break}p=((o|0)%(r|0)&-1)+1|0}do{if((q|0)==6168){r=c[w+48>>2]|0;p=c[w+52>>2]|0;t=+h[w+56>>3];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){x=r;y=p;z=t;break}d=(a[w+40|0]&1)==0;x=d?1:r;y=d?c[w+12>>2]|0:p;z=t}}while(0);c[s>>2]=x;c[k+132>>2]=y;h[k+136>>3]=z}}while(0);y=(f|0)==0?179864:f;f=a[47120]|0;L8371:do{if(f<<24>>24==0){L8382:do{if((a[y]|0)==34){x=1;while(1){w=a[y+x|0]|0;if((w<<24>>24|0)==0|(w<<24>>24|0)==34){A=x;break L8382}x=x+1|0}}else{A=0}}while(0);while(1){s=a[y+A|0]|0;if(s<<24>>24==0){B=A;break L8371}if((aM(s<<24>>24|0)|0)==0){A=A+1|0}else{B=A;break}}}else{s=0;x=0;while(1){w=a[y+x|0]|0;if((w<<24>>24|0)==34){C=s^1}else if((w<<24>>24|0)==0){D=x;break}else{if(w<<24>>24!=f<<24>>24|s){C=s}else{D=x;break}}s=C;x=x+1|0}while(1){if((D|0)<=0){B=D;break L8371}x=D-1|0;if((aM(a[y+x|0]|0|0)|0)==0){B=D;break}else{D=x}}}}while(0);do{if((a[y]|0)==34){if((a[y+(B-1|0)|0]|0)!=34){E=B;F=y;break}E=B-2|0;F=y+1|0}else{E=B;F=y}}while(0);y=E+1|0;B=ut(y)|0;if((B|0)!=0){G=B;H=k+60|0;c[H>>2]=G;I=uF(G|0,F|0,E|0)|0;J=c[H>>2]|0;K=J+E|0;a[K]=0;L=c[H>>2]|0;ua(L);i=j;return}gk();B=ut(y)|0;if((B|0)==0){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=224080,v)|0)}else{G=B;H=k+60|0;c[H>>2]=G;I=uF(G|0,F|0,E|0)|0;J=c[H>>2]|0;K=J+E|0;a[K]=0;L=c[H>>2]|0;ua(L);i=j;return}}function iX(b,d,e,f,g,j,k,l,m){b=b|0;d=d|0;e=+e;f=+f;g=+g;j=+j;k=+k;l=+l;m=+m;var n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0.0,u=0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0,G=0,H=0.0,I=0,J=0,K=0.0,L=0.0,M=0,N=0,O=0,Q=0,R=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0.0,bm=0.0,bn=0.0;n=i;i=i+24|0;o=n|0;p=c[b+320>>2]|0;q=c[60]|0;do{if((q|0)==0){r=f;s=l;t=k}else{c[64]=2;h[33]=f;h[34]=0.0;c[76]=c[64];c[308>>2]=c[260>>2];c[312>>2]=c[264>>2];c[316>>2]=c[268>>2];c[320>>2]=c[272>>2];c[324>>2]=c[276>>2];e4(q,o);do{if((a[1960]&1)==0){u=c[o>>2]|0;if((u|0)==2){w=+h[o+8>>3];break}else if((u|0)==1){w=+(c[o+8>>2]|0);break}else if((u|0)==3){w=+uz(c[o+8>>2]|0,0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{w=0.0}}while(0);c[64]=2;h[33]=k;h[34]=0.0;c[76]=c[64];c[308>>2]=c[260>>2];c[312>>2]=c[264>>2];c[316>>2]=c[268>>2];c[320>>2]=c[272>>2];c[324>>2]=c[276>>2];e4(c[60]|0,o);do{if((a[1960]&1)==0){u=c[o>>2]|0;if((u|0)==3){x=+uz(c[o+8>>2]|0,0);break}else if((u|0)==1){x=+(c[o+8>>2]|0);break}else if((u|0)==2){x=+h[o+8>>3];break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{x=0.0}}while(0);c[64]=2;h[33]=l;h[34]=0.0;c[76]=c[64];c[308>>2]=c[260>>2];c[312>>2]=c[264>>2];c[316>>2]=c[268>>2];c[320>>2]=c[272>>2];c[324>>2]=c[276>>2];e4(c[60]|0,o);if((a[1960]&1)!=0){r=w;s=0.0;t=x;break}u=c[o>>2]|0;if((u|0)==3){r=w;s=+uz(c[o+8>>2]|0,0);t=x;break}else if((u|0)==2){r=w;s=+h[o+8>>3];t=x;break}else if((u|0)==1){r=w;s=+(c[o+8>>2]|0);t=x;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);o=p+(d<<6)|0;c[o>>2]=0;do{if((a[30528]&1)==0){y=e;z=r;A=g;B=s;C=t;D=j}else{if(r<+h[8691]){h[8691]=r}do{if(r<+h[8685]){if((c[17366]&1|0)==0){break}h[8685]=r>0.0?0.0:r}}while(0);if(r>+h[8692]){h[8692]=r}do{if(r>+h[8686]){if((c[17366]&2|0)==0){c[o>>2]=1;break}if((c[17387]&2|0)==0){h[8686]=r;break}x=+h[8697];if(x<r){h[8686]=x;break}else{h[8686]=r;break}}}while(0);q=a[69604]|0;do{if((q&1)==0){u=c[17366]|0;if((u&1|0)!=0){E=r;F=u;break}E=r- +h[8685];F=u}else{x=+h[8685];if(x<=0.0){uf(-1,77440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}u=c[17366]|0;if((u&1|0)==0){w=+_(+r);l=+h[8702];E=w/l- +_(+x)/l;F=u;break}else{uf(-1,77440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);l=+h[9040]*e;x=E*+S(+l);w=E*+T(+l);if((c[b+12>>2]|0)==457){l=(j-g)*.5;y=x;z=w;A=x-l;B=s;C=t;D=l+x;break}if((F&2|0)==0&s>+h[8686]){c[o>>2]=1;G=a[69604]|0}else{G=q}do{if((G&1)==0){u=c[17366]|0;if((u&1|0)!=0){H=s;I=u;break}H=s- +h[8685];I=u}else{l=+_(+s);k=+h[8702];f=l/k- +_(+(+h[8685]))/k;H=f;I=c[17366]|0}}while(0);f=+h[9040]*j;k=H*+S(+f);l=H*+T(+f);if((I&2|0)==0&t>+h[8686]){c[o>>2]=1;J=a[69604]|0}else{J=G}do{if((J&1)==0){if((c[17366]&1|0)!=0){K=t;break}K=t- +h[8685]}else{f=+_(+t);L=+h[8702];K=f/L- +_(+(+h[8685]))/L}}while(0);L=+h[9040]*g;f=K*+S(+L);y=x;z=w;A=f;B=l;C=K*+T(+L);D=k}}while(0);J=b+304|0;G=c[J>>2]|0;L8481:do{if((G|0)!=99){if(!(y>-8.988465674311579e+307&y<8.988465674311579e+307)){c[o>>2]=2;i=n;return}do{if((a[64788+(G*688&-1)|0]&1)==0){h[p+(d<<6)+8>>3]=y}else{if(y<0.0){c[o>>2]=2;i=n;return}if(y==0.0){h[p+(d<<6)+8>>3]=-8.988465674311579e+307;c[o>>2]=1;break L8481}else{K=+_(+y);h[p+(d<<6)+8>>3]=K/+h[64800+(G*688&-1)>>3];break}}}while(0);if((a[b+23|0]&1)!=0){break}if((c[o>>2]|0)!=0){break}I=c[J>>2]|0;if((I|0)<0){break}F=64712+(I*688&-1)|0;if(y<+h[F>>3]){h[F>>3]=y;M=c[J>>2]|0}else{M=I}I=64664+(M*688&-1)|0;do{if(y<+h[I>>3]){if((c[64648+(M*688&-1)>>2]&1|0)==0){c[o>>2]=1;break L8481}if((c[64728+(M*688&-1)>>2]&1|0)==0){h[I>>3]=y;break}k=+h[64736+(M*688&-1)>>3];if(k>y){h[I>>3]=k;c[o>>2]=1;break L8481}else{h[I>>3]=y;break}}}while(0);I=c[J>>2]|0;F=64720+(I*688&-1)|0;if(y>+h[F>>3]){h[F>>3]=y;N=c[J>>2]|0}else{N=I}I=64672+(N*688&-1)|0;if(y<=+h[I>>3]){break}if((c[64648+(N*688&-1)>>2]&2|0)==0){c[o>>2]=1;break}if((c[64732+(N*688&-1)>>2]&2|0)==0){h[I>>3]=y;break}k=+h[64760+(N*688&-1)>>3];if(k<y){h[I>>3]=k;c[o>>2]=1;break}else{h[I>>3]=y;break}}}while(0);N=b+308|0;M=c[N>>2]|0;L8530:do{if((M|0)!=99){if(!(z>-8.988465674311579e+307&z<8.988465674311579e+307)){c[o>>2]=2;i=n;return}do{if((a[64788+(M*688&-1)|0]&1)==0){h[p+(d<<6)+16>>3]=z}else{if(z<0.0){c[o>>2]=2;i=n;return}if(z==0.0){h[p+(d<<6)+16>>3]=-8.988465674311579e+307;c[o>>2]=1;break L8530}else{k=+_(+z);h[p+(d<<6)+16>>3]=k/+h[64800+(M*688&-1)>>3];break}}}while(0);if((a[b+23|0]&1)!=0){break}if((c[o>>2]|0)!=0){break}G=c[N>>2]|0;if((G|0)<0){break}I=64712+(G*688&-1)|0;if(z<+h[I>>3]){h[I>>3]=z;O=c[N>>2]|0}else{O=G}G=64664+(O*688&-1)|0;do{if(z<+h[G>>3]){if((c[64648+(O*688&-1)>>2]&1|0)==0){c[o>>2]=1;break L8530}if((c[64728+(O*688&-1)>>2]&1|0)==0){h[G>>3]=z;break}k=+h[64736+(O*688&-1)>>3];if(k>z){h[G>>3]=k;c[o>>2]=1;break L8530}else{h[G>>3]=z;break}}}while(0);G=c[N>>2]|0;I=64720+(G*688&-1)|0;if(z>+h[I>>3]){h[I>>3]=z;Q=c[N>>2]|0}else{Q=G}G=64672+(Q*688&-1)|0;if(z<=+h[G>>3]){break}if((c[64648+(Q*688&-1)>>2]&2|0)==0){c[o>>2]=1;break}if((c[64732+(Q*688&-1)>>2]&2|0)==0){h[G>>3]=z;break}k=+h[64760+(Q*688&-1)>>3];if(k<z){h[G>>3]=k;c[o>>2]=1;break}else{h[G>>3]=z;break}}}while(0);Q=c[b+12>>2]|0;L8579:do{if((Q|0)==474){h[p+(d<<6)+32>>3]=C;O=c[N>>2]|0;L8581:do{if((O|0)==99){R=0}else{if(!(B>-8.988465674311579e+307&B<8.988465674311579e+307)){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;R=2;break}do{if((a[64788+(O*688&-1)|0]&1)==0){h[p+(d<<6)+40>>3]=B}else{if(B<0.0){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;R=2;break L8581}if(B==0.0){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;R=1;break L8581}else{k=+_(+B);h[p+(d<<6)+40>>3]=k/+h[64800+(O*688&-1)>>3];break}}}while(0);if((a[b+23|0]&1)!=0){R=0;break}M=c[N>>2]|0;if((M|0)<0){R=0;break}G=64712+(M*688&-1)|0;if(B<+h[G>>3]){h[G>>3]=B;U=c[N>>2]|0}else{U=M}M=64664+(U*688&-1)|0;do{if(B<+h[M>>3]){if((c[64648+(U*688&-1)>>2]&1|0)==0){R=1;break L8581}if((c[64728+(U*688&-1)>>2]&1|0)==0){h[M>>3]=B;break}k=+h[64736+(U*688&-1)>>3];if(k>B){h[M>>3]=k;R=1;break L8581}else{h[M>>3]=B;break}}}while(0);M=c[N>>2]|0;G=64720+(M*688&-1)|0;if(B>+h[G>>3]){h[G>>3]=B;V=c[N>>2]|0}else{V=M}M=64672+(V*688&-1)|0;if(B<=+h[M>>3]){R=0;break}if((c[64648+(V*688&-1)>>2]&2|0)==0){R=1;break}if((c[64732+(V*688&-1)>>2]&2|0)==0){h[M>>3]=B;R=0;break}k=+h[64760+(V*688&-1)>>3];if(k<B){h[M>>3]=k;R=1;break}else{h[M>>3]=B;R=0;break}}}while(0);O=c[J>>2]|0;if((O|0)==99){W=R;break}L8624:do{if(A>-8.988465674311579e+307&A<8.988465674311579e+307){do{if((a[64788+(O*688&-1)|0]&1)==0){h[p+(d<<6)+48>>3]=A}else{if(A<0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;X=2;Y=6440;break L8624}if(A==0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;X=1;Y=6440;break L8624}else{k=+_(+A);h[p+(d<<6)+48>>3]=k/+h[64800+(O*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(R|0)==0)){X=R;Y=6440;break}M=c[J>>2]|0;if((M|0)<0){Z=M;$=0;break}G=64712+(M*688&-1)|0;if(A<+h[G>>3]){h[G>>3]=A;aa=c[J>>2]|0}else{aa=M}M=64664+(aa*688&-1)|0;do{if(A<+h[M>>3]){if((c[64648+(aa*688&-1)>>2]&1|0)==0){X=1;Y=6440;break L8624}if((c[64728+(aa*688&-1)>>2]&1|0)==0){h[M>>3]=A;break}k=+h[64736+(aa*688&-1)>>3];if(k>A){h[M>>3]=k;X=1;Y=6440;break L8624}else{h[M>>3]=A;break}}}while(0);M=c[J>>2]|0;G=64720+(M*688&-1)|0;if(A>+h[G>>3]){h[G>>3]=A;ab=c[J>>2]|0}else{ab=M}M=64672+(ab*688&-1)|0;if(A<=+h[M>>3]){ac=0;ad=ab;Y=6441;break}if((c[64648+(ab*688&-1)>>2]&2|0)==0){X=1;Y=6440;break}if((c[64732+(ab*688&-1)>>2]&2|0)==0){h[M>>3]=A;X=0;Y=6440;break}k=+h[64760+(ab*688&-1)>>3];if(k<A){h[M>>3]=k;X=1;Y=6440;break}else{h[M>>3]=A;X=0;Y=6440;break}}else{h[p+(d<<6)+48>>3]=-8.988465674311579e+307;X=2;Y=6440}}while(0);if((Y|0)==6440){ac=X;ad=c[J>>2]|0;Y=6441}if((Y|0)==6441){if((ad|0)==99){W=ac;break}else{Z=ad;$=ac}}if(!(D>-8.988465674311579e+307&D<8.988465674311579e+307)){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;W=2;break}do{if((a[64788+(Z*688&-1)|0]&1)==0){h[p+(d<<6)+56>>3]=D}else{if(D<0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;W=2;break L8579}if(D==0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;W=1;break L8579}else{k=+_(+D);h[p+(d<<6)+56>>3]=k/+h[64800+(Z*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&($|0)==0)){W=$;break}O=c[J>>2]|0;if((O|0)<0){W=0;break}M=64712+(O*688&-1)|0;if(D<+h[M>>3]){h[M>>3]=D;ae=c[J>>2]|0}else{ae=O}O=64664+(ae*688&-1)|0;do{if(D<+h[O>>3]){if((c[64648+(ae*688&-1)>>2]&1|0)==0){W=1;break L8579}if((c[64728+(ae*688&-1)>>2]&1|0)==0){h[O>>3]=D;break}k=+h[64736+(ae*688&-1)>>3];if(k>D){h[O>>3]=k;W=1;break L8579}else{h[O>>3]=D;break}}}while(0);O=c[J>>2]|0;M=64720+(O*688&-1)|0;if(D>+h[M>>3]){h[M>>3]=D;af=c[J>>2]|0}else{af=O}O=64672+(af*688&-1)|0;if(D<=+h[O>>3]){W=0;break}if((c[64648+(af*688&-1)>>2]&2|0)==0){W=1;break}if((c[64732+(af*688&-1)>>2]&2|0)==0){h[O>>3]=D;W=0;break}k=+h[64760+(af*688&-1)>>3];if(k<D){h[O>>3]=k;W=1;break}else{h[O>>3]=D;W=0;break}}else if((Q|0)==18|(Q|0)==1|(Q|0)==51|(Q|0)==368|(Q|0)==64|(Q|0)==33|(Q|0)==177|(Q|0)==193|(Q|0)==209){h[p+(d<<6)+48>>3]=A;h[p+(d<<6)+56>>3]=D;h[p+(d<<6)+32>>3]=C;h[p+(d<<6)+40>>3]=B;W=0}else if((Q|0)==153){h[p+(d<<6)+32>>3]=C;h[p+(d<<6)+40>>3]=B;O=c[J>>2]|0;if((O|0)==99){W=0;break}L8710:do{if(A>-8.988465674311579e+307&A<8.988465674311579e+307){do{if((a[64788+(O*688&-1)|0]&1)==0){h[p+(d<<6)+48>>3]=A}else{if(A<0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;ag=2;Y=6349;break L8710}if(A==0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;ag=1;Y=6349;break L8710}else{k=+_(+A);h[p+(d<<6)+48>>3]=k/+h[64800+(O*688&-1)>>3];break}}}while(0);if((a[b+23|0]&1)!=0){ag=0;Y=6349;break}M=c[J>>2]|0;if((M|0)<0){ah=M;ai=0;break}G=64712+(M*688&-1)|0;if(A<+h[G>>3]){h[G>>3]=A;aj=c[J>>2]|0}else{aj=M}M=64664+(aj*688&-1)|0;do{if(A<+h[M>>3]){if((c[64648+(aj*688&-1)>>2]&1|0)==0){ag=1;Y=6349;break L8710}if((c[64728+(aj*688&-1)>>2]&1|0)==0){h[M>>3]=A;break}k=+h[64736+(aj*688&-1)>>3];if(k>A){h[M>>3]=k;ag=1;Y=6349;break L8710}else{h[M>>3]=A;break}}}while(0);M=c[J>>2]|0;G=64720+(M*688&-1)|0;if(A>+h[G>>3]){h[G>>3]=A;ak=c[J>>2]|0}else{ak=M}M=64672+(ak*688&-1)|0;if(A<=+h[M>>3]){al=0;am=ak;Y=6350;break}if((c[64648+(ak*688&-1)>>2]&2|0)==0){ag=1;Y=6349;break}if((c[64732+(ak*688&-1)>>2]&2|0)==0){h[M>>3]=A;ag=0;Y=6349;break}k=+h[64760+(ak*688&-1)>>3];if(k<A){h[M>>3]=k;ag=1;Y=6349;break}else{h[M>>3]=A;ag=0;Y=6349;break}}else{h[p+(d<<6)+48>>3]=-8.988465674311579e+307;ag=2;Y=6349}}while(0);if((Y|0)==6349){al=ag;am=c[J>>2]|0;Y=6350}if((Y|0)==6350){if((am|0)==99){W=al;break}else{ah=am;ai=al}}if(!(D>-8.988465674311579e+307&D<8.988465674311579e+307)){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;W=2;break}do{if((a[64788+(ah*688&-1)|0]&1)==0){h[p+(d<<6)+56>>3]=D}else{if(D<0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;W=2;break L8579}if(D==0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;W=1;break L8579}else{k=+_(+D);h[p+(d<<6)+56>>3]=k/+h[64800+(ah*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(ai|0)==0)){W=ai;break}O=c[J>>2]|0;if((O|0)<0){W=0;break}M=64712+(O*688&-1)|0;if(D<+h[M>>3]){h[M>>3]=D;an=c[J>>2]|0}else{an=O}O=64664+(an*688&-1)|0;do{if(D<+h[O>>3]){if((c[64648+(an*688&-1)>>2]&1|0)==0){W=1;break L8579}if((c[64728+(an*688&-1)>>2]&1|0)==0){h[O>>3]=D;break}k=+h[64736+(an*688&-1)>>3];if(k>D){h[O>>3]=k;W=1;break L8579}else{h[O>>3]=D;break}}}while(0);O=c[J>>2]|0;M=64720+(O*688&-1)|0;if(D>+h[M>>3]){h[M>>3]=D;ao=c[J>>2]|0}else{ao=O}O=64672+(ao*688&-1)|0;if(D<=+h[O>>3]){W=0;break}if((c[64648+(ao*688&-1)>>2]&2|0)==0){W=1;break}if((c[64732+(ao*688&-1)>>2]&2|0)==0){h[O>>3]=D;W=0;break}k=+h[64760+(ao*688&-1)>>3];if(k<D){h[O>>3]=k;W=1;break}else{h[O>>3]=D;W=0;break}}else if((Q|0)==457){h[p+(d<<6)+40>>3]=B;O=c[J>>2]|0;L8794:do{if((O|0)==99){ap=0}else{L8796:do{if(A>-8.988465674311579e+307&A<8.988465674311579e+307){do{if((a[64788+(O*688&-1)|0]&1)==0){h[p+(d<<6)+48>>3]=A}else{if(A<0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;aq=2;Y=6501;break L8796}if(A==0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;aq=1;Y=6501;break L8796}else{k=+_(+A);h[p+(d<<6)+48>>3]=k/+h[64800+(O*688&-1)>>3];break}}}while(0);if((a[b+23|0]&1)!=0){aq=0;Y=6501;break}M=c[J>>2]|0;if((M|0)<0){ar=M;as=0;break}G=64712+(M*688&-1)|0;if(A<+h[G>>3]){h[G>>3]=A;at=c[J>>2]|0}else{at=M}M=64664+(at*688&-1)|0;do{if(A<+h[M>>3]){if((c[64648+(at*688&-1)>>2]&1|0)==0){aq=1;Y=6501;break L8796}if((c[64728+(at*688&-1)>>2]&1|0)==0){h[M>>3]=A;break}k=+h[64736+(at*688&-1)>>3];if(k>A){h[M>>3]=k;aq=1;Y=6501;break L8796}else{h[M>>3]=A;break}}}while(0);M=c[J>>2]|0;G=64720+(M*688&-1)|0;if(A>+h[G>>3]){h[G>>3]=A;au=c[J>>2]|0}else{au=M}M=64672+(au*688&-1)|0;if(A<=+h[M>>3]){av=0;aw=au;Y=6502;break}if((c[64648+(au*688&-1)>>2]&2|0)==0){aq=1;Y=6501;break}if((c[64732+(au*688&-1)>>2]&2|0)==0){h[M>>3]=A;aq=0;Y=6501;break}k=+h[64760+(au*688&-1)>>3];if(k<A){h[M>>3]=k;aq=1;Y=6501;break}else{h[M>>3]=A;aq=0;Y=6501;break}}else{h[p+(d<<6)+48>>3]=-8.988465674311579e+307;aq=2;Y=6501}}while(0);if((Y|0)==6501){av=aq;aw=c[J>>2]|0;Y=6502}if((Y|0)==6502){if((aw|0)==99){ap=av;break}else{ar=aw;as=av}}if(!(D>-8.988465674311579e+307&D<8.988465674311579e+307)){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;ap=2;break}do{if((a[64788+(ar*688&-1)|0]&1)==0){h[p+(d<<6)+56>>3]=D}else{if(D<0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;ap=2;break L8794}if(D==0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;ap=1;break L8794}else{k=+_(+D);h[p+(d<<6)+56>>3]=k/+h[64800+(ar*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(as|0)==0)){ap=as;break}M=c[J>>2]|0;if((M|0)<0){ap=0;break}G=64712+(M*688&-1)|0;if(D<+h[G>>3]){h[G>>3]=D;ax=c[J>>2]|0}else{ax=M}M=64664+(ax*688&-1)|0;do{if(D<+h[M>>3]){if((c[64648+(ax*688&-1)>>2]&1|0)==0){ap=1;break L8794}if((c[64728+(ax*688&-1)>>2]&1|0)==0){h[M>>3]=D;break}k=+h[64736+(ax*688&-1)>>3];if(k>D){h[M>>3]=k;ap=1;break L8794}else{h[M>>3]=D;break}}}while(0);M=c[J>>2]|0;G=64720+(M*688&-1)|0;if(D>+h[G>>3]){h[G>>3]=D;ay=c[J>>2]|0}else{ay=M}M=64672+(ay*688&-1)|0;if(D<=+h[M>>3]){ap=0;break}if((c[64648+(ay*688&-1)>>2]&2|0)==0){ap=1;break}if((c[64732+(ay*688&-1)>>2]&2|0)==0){h[M>>3]=D;ap=0;break}k=+h[64760+(ay*688&-1)>>3];if(k<D){h[M>>3]=k;ap=1;break}else{h[M>>3]=D;ap=0;break}}}while(0);h[p+(d<<6)+32>>3]=C;h[p+(d<<6)+56>>3]=B;if(+P(+C)<=1.0e3){if(+P(+B)<=1.0e3){W=ap;break}}c[o>>2]=2;W=ap}else if((Q|0)==489){O=c[J>>2]|0;L9055:do{if((O|0)==99){az=0}else{k=(A>D?A:D)*.5;M=b+272|0;l=y-k*((c[M>>2]|0)==1?1.1:1.0);L9057:do{if(l>-8.988465674311579e+307&l<8.988465674311579e+307){do{if((a[64788+(O*688&-1)|0]&1)==0){h[p+(d<<6)+48>>3]=l}else{if(l<0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;aA=2;Y=6565;break L9057}if(l==0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;aA=1;Y=6565;break L9057}else{w=+_(+l);h[p+(d<<6)+48>>3]=w/+h[64800+(O*688&-1)>>3];break}}}while(0);if((a[b+23|0]&1)!=0){aA=0;Y=6565;break}G=c[J>>2]|0;if((G|0)<0){aB=G;aC=0;break}I=c[M>>2]|0;w=y-k*((I|0)==1?1.1:1.0);F=64712+(G*688&-1)|0;if(w<+h[F>>3]){h[F>>3]=w;aD=c[M>>2]|0;aE=c[J>>2]|0}else{aD=I;aE=G}w=y-k*((aD|0)==1?1.1:1.0);G=64664+(aE*688&-1)|0;do{if(w<+h[G>>3]){if((c[64648+(aE*688&-1)>>2]&1|0)==0){aA=1;Y=6565;break L9057}if((c[64728+(aE*688&-1)>>2]&1|0)==0){h[G>>3]=w;break}x=+h[64736+(aE*688&-1)>>3];if(x>w){h[G>>3]=x;aA=1;Y=6565;break L9057}else{h[G>>3]=w;break}}}while(0);G=c[M>>2]|0;w=y-k*((G|0)==1?1.1:1.0);I=c[J>>2]|0;F=64720+(I*688&-1)|0;if(w>+h[F>>3]){h[F>>3]=w;aF=c[M>>2]|0;aG=c[J>>2]|0}else{aF=G;aG=I}w=y-k*((aF|0)==1?1.1:1.0);I=64672+(aG*688&-1)|0;if(w<=+h[I>>3]){aH=0;aI=aG;Y=6566;break}if((c[64648+(aG*688&-1)>>2]&2|0)==0){aA=1;Y=6565;break}if((c[64732+(aG*688&-1)>>2]&2|0)==0){h[I>>3]=w;aA=0;Y=6565;break}x=+h[64760+(aG*688&-1)>>3];if(x<w){h[I>>3]=x;aA=1;Y=6565;break}else{h[I>>3]=w;aA=0;Y=6565;break}}else{h[p+(d<<6)+48>>3]=-8.988465674311579e+307;aA=2;Y=6565}}while(0);if((Y|0)==6565){aH=aA;aI=c[J>>2]|0;Y=6566}if((Y|0)==6566){if((aI|0)==99){az=aH;break}else{aB=aI;aC=aH}}l=y+k*((c[M>>2]|0)==1?1.1:1.0);if(!(l>-8.988465674311579e+307&l<8.988465674311579e+307)){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;az=2;break}do{if((a[64788+(aB*688&-1)|0]&1)==0){h[p+(d<<6)+56>>3]=l}else{if(l<0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;az=2;break L9055}if(l==0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;az=1;break L9055}else{w=+_(+l);h[p+(d<<6)+56>>3]=w/+h[64800+(aB*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(aC|0)==0)){az=aC;break}I=c[J>>2]|0;if((I|0)<0){az=0;break}G=c[M>>2]|0;l=y+k*((G|0)==1?1.1:1.0);F=64712+(I*688&-1)|0;if(l<+h[F>>3]){h[F>>3]=l;aJ=c[M>>2]|0;aK=c[J>>2]|0}else{aJ=G;aK=I}l=y+k*((aJ|0)==1?1.1:1.0);I=64664+(aK*688&-1)|0;do{if(l<+h[I>>3]){if((c[64648+(aK*688&-1)>>2]&1|0)==0){az=1;break L9055}if((c[64728+(aK*688&-1)>>2]&1|0)==0){h[I>>3]=l;break}w=+h[64736+(aK*688&-1)>>3];if(w>l){h[I>>3]=w;az=1;break L9055}else{h[I>>3]=l;break}}}while(0);I=c[M>>2]|0;l=y+k*((I|0)==1?1.1:1.0);G=c[J>>2]|0;F=64720+(G*688&-1)|0;if(l>+h[F>>3]){h[F>>3]=l;aL=c[M>>2]|0;aM=c[J>>2]|0}else{aL=I;aM=G}l=y+k*((aL|0)==1?1.1:1.0);G=64672+(aM*688&-1)|0;if(l<=+h[G>>3]){az=0;break}if((c[64648+(aM*688&-1)>>2]&2|0)==0){az=1;break}if((c[64732+(aM*688&-1)>>2]&2|0)==0){h[G>>3]=l;az=0;break}w=+h[64760+(aM*688&-1)>>3];if(w<l){h[G>>3]=w;az=1;break}else{h[G>>3]=l;az=0;break}}}while(0);O=c[N>>2]|0;L9141:do{if((O|0)==99){aN=az}else{l=(A>D?A:D)*.5;G=b+272|0;w=z-l*((c[G>>2]|0)==2?1.0:1.4);L9143:do{if(w>-8.988465674311579e+307&w<8.988465674311579e+307){do{if((a[64788+(O*688&-1)|0]&1)==0){h[p+(d<<6)+32>>3]=w}else{if(w<0.0){h[p+(d<<6)+32>>3]=-8.988465674311579e+307;aO=2;Y=6626;break L9143}if(w==0.0){h[p+(d<<6)+32>>3]=-8.988465674311579e+307;aO=1;Y=6626;break L9143}else{x=+_(+w);h[p+(d<<6)+32>>3]=x/+h[64800+(O*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(az|0)==0)){aO=az;Y=6626;break}I=c[N>>2]|0;if((I|0)<0){aP=I;aQ=0;break}F=c[G>>2]|0;x=z-l*((F|0)==2?1.0:1.4);q=64712+(I*688&-1)|0;if(x<+h[q>>3]){h[q>>3]=x;aR=c[G>>2]|0;aS=c[N>>2]|0}else{aR=F;aS=I}x=z-l*((aR|0)==2?1.0:1.4);I=64664+(aS*688&-1)|0;do{if(x<+h[I>>3]){if((c[64648+(aS*688&-1)>>2]&1|0)==0){aO=1;Y=6626;break L9143}if((c[64728+(aS*688&-1)>>2]&1|0)==0){h[I>>3]=x;break}K=+h[64736+(aS*688&-1)>>3];if(K>x){h[I>>3]=K;aO=1;Y=6626;break L9143}else{h[I>>3]=x;break}}}while(0);I=c[G>>2]|0;x=z-l*((I|0)==2?1.0:1.4);F=c[N>>2]|0;q=64720+(F*688&-1)|0;if(x>+h[q>>3]){h[q>>3]=x;aT=c[G>>2]|0;aU=c[N>>2]|0}else{aT=I;aU=F}x=z-l*((aT|0)==2?1.0:1.4);F=64672+(aU*688&-1)|0;if(x<=+h[F>>3]){aV=0;aW=aU;Y=6627;break}if((c[64648+(aU*688&-1)>>2]&2|0)==0){aO=1;Y=6626;break}if((c[64732+(aU*688&-1)>>2]&2|0)==0){h[F>>3]=x;aO=0;Y=6626;break}K=+h[64760+(aU*688&-1)>>3];if(K<x){h[F>>3]=K;aO=1;Y=6626;break}else{h[F>>3]=x;aO=0;Y=6626;break}}else{h[p+(d<<6)+32>>3]=-8.988465674311579e+307;aO=2;Y=6626}}while(0);if((Y|0)==6626){aV=aO;aW=c[N>>2]|0;Y=6627}if((Y|0)==6627){if((aW|0)==99){aN=aV;break}else{aP=aW;aQ=aV}}w=z+l*((c[G>>2]|0)==2?1.0:1.4);if(!(w>-8.988465674311579e+307&w<8.988465674311579e+307)){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;aN=2;break}do{if((a[64788+(aP*688&-1)|0]&1)==0){h[p+(d<<6)+40>>3]=w}else{if(w<0.0){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;aN=2;break L9141}if(w==0.0){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;aN=1;break L9141}else{k=+_(+w);h[p+(d<<6)+40>>3]=k/+h[64800+(aP*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(aQ|0)==0)){aN=aQ;break}M=c[N>>2]|0;if((M|0)<0){aN=0;break}F=c[G>>2]|0;w=z+l*((F|0)==2?1.0:1.4);I=64712+(M*688&-1)|0;if(w<+h[I>>3]){h[I>>3]=w;aX=c[G>>2]|0;aY=c[N>>2]|0}else{aX=F;aY=M}w=z+l*((aX|0)==2?1.0:1.4);M=64664+(aY*688&-1)|0;do{if(w<+h[M>>3]){if((c[64648+(aY*688&-1)>>2]&1|0)==0){aN=1;break L9141}if((c[64728+(aY*688&-1)>>2]&1|0)==0){h[M>>3]=w;break}k=+h[64736+(aY*688&-1)>>3];if(k>w){h[M>>3]=k;aN=1;break L9141}else{h[M>>3]=w;break}}}while(0);M=c[G>>2]|0;w=z+l*((M|0)==2?1.0:1.4);F=c[N>>2]|0;I=64720+(F*688&-1)|0;if(w>+h[I>>3]){h[I>>3]=w;aZ=c[G>>2]|0;a_=c[N>>2]|0}else{aZ=M;a_=F}w=z+l*((aZ|0)==2?1.0:1.4);F=64672+(a_*688&-1)|0;if(w<=+h[F>>3]){aN=0;break}if((c[64648+(a_*688&-1)>>2]&2|0)==0){aN=1;break}if((c[64732+(a_*688&-1)>>2]&2|0)==0){h[F>>3]=w;aN=0;break}k=+h[64760+(a_*688&-1)>>3];if(k<w){h[F>>3]=k;aN=1;break}else{h[F>>3]=w;aN=0;break}}}while(0);h[p+(d<<6)+48>>3]=A;h[p+(d<<6)+56>>3]=D;h[p+(d<<6)+32>>3]=C;W=aN}else{O=c[J>>2]|0;L8884:do{if((O|0)==99){a$=0}else{L8886:do{if(A>-8.988465674311579e+307&A<8.988465674311579e+307){do{if((a[64788+(O*688&-1)|0]&1)==0){h[p+(d<<6)+48>>3]=A}else{if(A<0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;a0=2;Y=6688;break L8886}if(A==0.0){h[p+(d<<6)+48>>3]=-8.988465674311579e+307;a0=1;Y=6688;break L8886}else{w=+_(+A);h[p+(d<<6)+48>>3]=w/+h[64800+(O*688&-1)>>3];break}}}while(0);if((a[b+23|0]&1)!=0){a0=0;Y=6688;break}F=c[J>>2]|0;if((F|0)<0){a1=F;a2=0;break}M=64712+(F*688&-1)|0;if(A<+h[M>>3]){h[M>>3]=A;a3=c[J>>2]|0}else{a3=F}F=64664+(a3*688&-1)|0;do{if(A<+h[F>>3]){if((c[64648+(a3*688&-1)>>2]&1|0)==0){a0=1;Y=6688;break L8886}if((c[64728+(a3*688&-1)>>2]&1|0)==0){h[F>>3]=A;break}w=+h[64736+(a3*688&-1)>>3];if(w>A){h[F>>3]=w;a0=1;Y=6688;break L8886}else{h[F>>3]=A;break}}}while(0);F=c[J>>2]|0;M=64720+(F*688&-1)|0;if(A>+h[M>>3]){h[M>>3]=A;a4=c[J>>2]|0}else{a4=F}F=64672+(a4*688&-1)|0;if(A<=+h[F>>3]){a5=0;a6=a4;Y=6689;break}if((c[64648+(a4*688&-1)>>2]&2|0)==0){a0=1;Y=6688;break}if((c[64732+(a4*688&-1)>>2]&2|0)==0){h[F>>3]=A;a0=0;Y=6688;break}w=+h[64760+(a4*688&-1)>>3];if(w<A){h[F>>3]=w;a0=1;Y=6688;break}else{h[F>>3]=A;a0=0;Y=6688;break}}else{h[p+(d<<6)+48>>3]=-8.988465674311579e+307;a0=2;Y=6688}}while(0);if((Y|0)==6688){a5=a0;a6=c[J>>2]|0;Y=6689}if((Y|0)==6689){if((a6|0)==99){a$=a5;break}else{a1=a6;a2=a5}}if(!(D>-8.988465674311579e+307&D<8.988465674311579e+307)){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;a$=2;break}do{if((a[64788+(a1*688&-1)|0]&1)==0){h[p+(d<<6)+56>>3]=D}else{if(D<0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;a$=2;break L8884}if(D==0.0){h[p+(d<<6)+56>>3]=-8.988465674311579e+307;a$=1;break L8884}else{l=+_(+D);h[p+(d<<6)+56>>3]=l/+h[64800+(a1*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(a2|0)==0)){a$=a2;break}G=c[J>>2]|0;if((G|0)<0){a$=0;break}F=64712+(G*688&-1)|0;if(D<+h[F>>3]){h[F>>3]=D;a7=c[J>>2]|0}else{a7=G}G=64664+(a7*688&-1)|0;do{if(D<+h[G>>3]){if((c[64648+(a7*688&-1)>>2]&1|0)==0){a$=1;break L8884}if((c[64728+(a7*688&-1)>>2]&1|0)==0){h[G>>3]=D;break}l=+h[64736+(a7*688&-1)>>3];if(l>D){h[G>>3]=l;a$=1;break L8884}else{h[G>>3]=D;break}}}while(0);G=c[J>>2]|0;F=64720+(G*688&-1)|0;if(D>+h[F>>3]){h[F>>3]=D;a8=c[J>>2]|0}else{a8=G}G=64672+(a8*688&-1)|0;if(D<=+h[G>>3]){a$=0;break}if((c[64648+(a8*688&-1)>>2]&2|0)==0){a$=1;break}if((c[64732+(a8*688&-1)>>2]&2|0)==0){h[G>>3]=D;a$=0;break}l=+h[64760+(a8*688&-1)>>3];if(l<D){h[G>>3]=l;a$=1;break}else{h[G>>3]=D;a$=0;break}}}while(0);O=c[N>>2]|0;if((O|0)==99){W=a$;break}L8971:do{if(C>-8.988465674311579e+307&C<8.988465674311579e+307){do{if((a[64788+(O*688&-1)|0]&1)==0){h[p+(d<<6)+32>>3]=C}else{if(C<0.0){h[p+(d<<6)+32>>3]=-8.988465674311579e+307;a9=2;Y=6749;break L8971}if(C==0.0){h[p+(d<<6)+32>>3]=-8.988465674311579e+307;a9=1;Y=6749;break L8971}else{l=+_(+C);h[p+(d<<6)+32>>3]=l/+h[64800+(O*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(a$|0)==0)){a9=a$;Y=6749;break}G=c[N>>2]|0;if((G|0)<0){ba=G;bb=0;break}F=64712+(G*688&-1)|0;if(C<+h[F>>3]){h[F>>3]=C;bc=c[N>>2]|0}else{bc=G}G=64664+(bc*688&-1)|0;do{if(C<+h[G>>3]){if((c[64648+(bc*688&-1)>>2]&1|0)==0){a9=1;Y=6749;break L8971}if((c[64728+(bc*688&-1)>>2]&1|0)==0){h[G>>3]=C;break}l=+h[64736+(bc*688&-1)>>3];if(l>C){h[G>>3]=l;a9=1;Y=6749;break L8971}else{h[G>>3]=C;break}}}while(0);G=c[N>>2]|0;F=64720+(G*688&-1)|0;if(C>+h[F>>3]){h[F>>3]=C;bd=c[N>>2]|0}else{bd=G}G=64672+(bd*688&-1)|0;if(C<=+h[G>>3]){be=0;bf=bd;Y=6750;break}if((c[64648+(bd*688&-1)>>2]&2|0)==0){a9=1;Y=6749;break}if((c[64732+(bd*688&-1)>>2]&2|0)==0){h[G>>3]=C;a9=0;Y=6749;break}l=+h[64760+(bd*688&-1)>>3];if(l<C){h[G>>3]=l;a9=1;Y=6749;break}else{h[G>>3]=C;a9=0;Y=6749;break}}else{h[p+(d<<6)+32>>3]=-8.988465674311579e+307;a9=2;Y=6749}}while(0);if((Y|0)==6749){be=a9;bf=c[N>>2]|0;Y=6750}if((Y|0)==6750){if((bf|0)==99){W=be;break}else{ba=bf;bb=be}}if(!(B>-8.988465674311579e+307&B<8.988465674311579e+307)){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;W=2;break}do{if((a[64788+(ba*688&-1)|0]&1)==0){h[p+(d<<6)+40>>3]=B}else{if(B<0.0){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;W=2;break L8579}if(B==0.0){h[p+(d<<6)+40>>3]=-8.988465674311579e+307;W=1;break L8579}else{l=+_(+B);h[p+(d<<6)+40>>3]=l/+h[64800+(ba*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(bb|0)==0)){W=bb;break}O=c[N>>2]|0;if((O|0)<0){W=0;break}G=64712+(O*688&-1)|0;if(B<+h[G>>3]){h[G>>3]=B;bg=c[N>>2]|0}else{bg=O}O=64664+(bg*688&-1)|0;do{if(B<+h[O>>3]){if((c[64648+(bg*688&-1)>>2]&1|0)==0){W=1;break L8579}if((c[64728+(bg*688&-1)>>2]&1|0)==0){h[O>>3]=B;break}l=+h[64736+(bg*688&-1)>>3];if(l>B){h[O>>3]=l;W=1;break L8579}else{h[O>>3]=B;break}}}while(0);O=c[N>>2]|0;G=64720+(O*688&-1)|0;if(B>+h[G>>3]){h[G>>3]=B;bh=c[N>>2]|0}else{bh=O}O=64672+(bh*688&-1)|0;if(B<=+h[O>>3]){W=0;break}if((c[64648+(bh*688&-1)>>2]&2|0)==0){W=1;break}if((c[64732+(bh*688&-1)>>2]&2|0)==0){h[O>>3]=B;W=0;break}l=+h[64760+(bh*688&-1)>>3];if(l<B){h[O>>3]=l;W=1;break}else{h[O>>3]=B;W=0;break}}}while(0);bh=b+312|0;N=c[bh>>2]|0;L9228:do{if((N|0)==99){h[p+(d<<6)+24>>3]=m;bi=W}else{if(!(m>-8.988465674311579e+307&m<8.988465674311579e+307)){h[p+(d<<6)+24>>3]=-8.988465674311579e+307;bi=2;break}do{if((a[64788+(N*688&-1)|0]&1)==0){h[p+(d<<6)+24>>3]=m}else{if(m<0.0){h[p+(d<<6)+24>>3]=-8.988465674311579e+307;bi=2;break L9228}if(m==0.0){h[p+(d<<6)+24>>3]=-8.988465674311579e+307;bi=1;break L9228}else{B=+_(+m);h[p+(d<<6)+24>>3]=B/+h[64800+(N*688&-1)>>3];break}}}while(0);if(!((a[b+23|0]&1)==0&(W|0)==0)){bi=W;break}bg=c[bh>>2]|0;if((bg|0)<0){bi=0;break}bb=64712+(bg*688&-1)|0;if(+h[bb>>3]>m){h[bb>>3]=m;bj=c[bh>>2]|0}else{bj=bg}bg=64664+(bj*688&-1)|0;do{if(+h[bg>>3]>m){if((c[64648+(bj*688&-1)>>2]&1|0)==0){bi=1;break L9228}if((c[64728+(bj*688&-1)>>2]&1|0)==0){h[bg>>3]=m;break}B=+h[64736+(bj*688&-1)>>3];if(B>m){h[bg>>3]=B;bi=1;break L9228}else{h[bg>>3]=m;break}}}while(0);bg=c[bh>>2]|0;bb=64720+(bg*688&-1)|0;if(+h[bb>>3]<m){h[bb>>3]=m;bk=c[bh>>2]|0}else{bk=bg}bg=64672+(bk*688&-1)|0;if(+h[bg>>3]>=m){bi=0;break}if((c[64648+(bk*688&-1)>>2]&2|0)==0){bi=1;break}if((c[64732+(bk*688&-1)>>2]&2|0)==0){h[bg>>3]=m;bi=0;break}B=+h[64760+(bk*688&-1)>>3];if(B<m){h[bg>>3]=B;bi=1;break}else{h[bg>>3]=m;bi=0;break}}}while(0);if((c[b+64>>2]|0)!=6){i=n;return}bk=b+316|0;bh=c[bk>>2]|0;if((bh|0)==0){i=n;return}bj=bh+(d<<3)|0;m=+h[bj>>3];if(!(m>-8.988465674311579e+307&m<8.988465674311579e+307)){i=n;return}do{if((a[66852]&1)==0){bl=m}else{if(m<0.0){i=n;return}if(m!=0.0){B=+_(+m);bl=B/+h[8358];break}h[bj>>3]=-8.988465674311579e+307;i=n;return}}while(0);h[bj>>3]=bl;if(!((a[b+23|0]&1)==0&(bi|0)==0)){i=n;return}bl=+h[(c[bk>>2]|0)+(d<<3)>>3];if(bl<+h[8347]){h[8347]=bl;bm=+h[(c[bk>>2]|0)+(d<<3)>>3]}else{bm=bl}do{if(bm<+h[8341]){if((c[16678]&1|0)==0){i=n;return}if((c[16698]&1|0)==0){h[8341]=bm;break}bl=+h[8350];if(bl<=bm){h[8341]=bm;break}h[8341]=bl;i=n;return}}while(0);bm=+h[(c[bk>>2]|0)+(d<<3)>>3];if(bm>+h[8348]){h[8348]=bm;bn=+h[(c[bk>>2]|0)+(d<<3)>>3]}else{bn=bm}if(bn<=+h[8342]){i=n;return}if((c[16678]&2|0)==0){i=n;return}if((c[16699]&2|0)==0){h[8342]=bn;i=n;return}bm=+h[8353];if(bm<bn){h[8342]=bm;i=n;return}else{h[8342]=bn;i=n;return}}function iY(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0;if((a|0)==0){return}else{b=a}while(1){a=c[b>>2]|0;d=c[b+16>>2]|0;if((d|0)!=0){uu(d)}d=b+256|0;e=c[d>>2]|0;if((e|0)!=0){f=e;while(1){e=c[f>>2]|0;uu(c[f+4>>2]|0);uu(c[d>>2]|0);c[d>>2]=e;if((e|0)==0){break}else{f=e}}}f=b+260|0;d=c[f>>2]|0;if((d|0)!=0){e=d;while(1){d=c[e>>2]|0;g=c[e+12>>2]|0;if((g|0)!=0){uu(g)}uu(e);c[f>>2]=d;if((d|0)==0){break}else{e=d}}}e=b+224|0;f=c[e>>2]|0;if((f|0)!=0){d=c[f+64>>2]|0;if((d|0)==0){h=f}else{uu(d);h=f}while(1){f=c[h+60>>2]|0;if((f|0)!=0){uu(f)}f=c[h+64>>2]|0;if(!((f|0)==0|(f|0)==(d|0))){uu(f)}f=c[h>>2]|0;uu(h);if((f|0)==0){break}else{h=f}}c[e>>2]=0}uu(b);if((a|0)==0){break}else{b=a}}return}function iZ(){var d=0,e=0,f=0,j=0,k=0.0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0,s=0.0,t=0.0,u=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0,C=0.0,D=0.0,E=0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0,M=0,N=0,O=0,P=0;d=i;i=i+24|0;e=d|0;a[37384]=1;do{if((c[5094]|0)==1){if(a[20368]|0){break}a[20368]=1;g[5090]=+g[3538];g[5088]=+g[3536];g[5086]=+g[3534];g[3538]=180.0;g[3536]=0.0;g[3534]=1.2999999523162842;c[16336]=c[16336]^2;c[17024]=c[17024]^2}}while(0);f=a[32936]&1;do{if(f<<24>>24!=0){if((aY(24544,175824)|0)!=0){break}b[12272]=117;w=118;a[24595]=w&255;w=w>>8;a[24596|0]=w&255}}while(0);j=c[16507]|0;c[16506]=j;if((j&3|0)==0){k=+h[8258];l=+h[8257];if(k<l){m=66056;n=k}else{o=l;p=6889}}else{o=+h[8257];p=6889}if((p|0)==6889){m=66064;n=o}h[8255]=n;h[8256]=+h[m>>3];if((a[66164]&1)==0){q=0.0}else{q=+_(+(+h[8271]))}h[8272]=q;h[8261]=8.988465674311579e+307;h[8262]=-8.988465674311579e+307;m=c[16335]|0;c[16334]=m;if((m&3|0)==0){q=+h[8172];n=+h[8171];if(q<n){r=65368;s=q}else{t=n;p=6895}}else{t=+h[8171];p=6895}if((p|0)==6895){r=65376;s=t}h[8169]=s;h[8170]=+h[r>>3];if((a[65476]&1)==0){u=0.0}else{u=+_(+(+h[8185]))}h[8186]=u;h[8175]=8.988465674311579e+307;h[8176]=-8.988465674311579e+307;r=c[16163]|0;c[16162]=r;do{if((r&3|0)==0){u=+h[8086];s=+h[8085];if(u>=s){x=s;y=u;p=6902;break}h[8083]=u;z=s}else{x=+h[8085];y=+h[8086];p=6902}}while(0);if((p|0)==6902){h[8083]=(r&1|0)==0?x:8.988465674311579e+307;z=(r&2|0)==0?y:-8.988465674311579e+307}h[8084]=z;if((a[64788]&1)==0){A=0.0}else{A=+_(+(+h[8099]))}h[8100]=A;h[8089]=8.988465674311579e+307;h[8090]=-8.988465674311579e+307;r=c[17711]|0;c[17710]=r;if((r&3|0)==0){A=+h[8860];z=+h[8859];if(A<z){B=70872;C=A}else{D=z;p=6908}}else{D=+h[8859];p=6908}if((p|0)==6908){B=70880;C=D}h[8857]=C;h[8858]=+h[B>>3];a[70980]=0;h[8873]=1.0;h[8874]=0.0;h[8863]=8.988465674311579e+307;h[8864]=-8.988465674311579e+307;B=c[17883]|0;c[17882]=B;if((B&3|0)==0){C=+h[8946];D=+h[8945];if(C<D){E=71560;F=C}else{G=D;p=6912}}else{G=+h[8945];p=6912}if((p|0)==6912){E=71568;F=G}h[8943]=F;h[8944]=+h[E>>3];a[71668]=0;h[8959]=1.0;h[8960]=0.0;h[8949]=8.988465674311579e+307;h[8950]=-8.988465674311579e+307;E=c[16679]|0;c[16678]=E;do{if((E&3|0)==0){F=+h[8344];G=+h[8343];if(F>=G){H=G;I=F;p=6917;break}h[8341]=F;J=G}else{H=+h[8343];I=+h[8344];p=6917}}while(0);if((p|0)==6917){h[8341]=(E&1|0)==0?H:8.988465674311579e+307;J=(E&2|0)==0?I:-8.988465674311579e+307}h[8342]=J;if((a[66852]&1)==0){K=0.0}else{K=+_(+(+h[8357]))}h[8358]=K;h[8347]=8.988465674311579e+307;h[8348]=-8.988465674311579e+307;if((c[3524]|0)==0){uf(c[13898]|0,135392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}E=f<<24>>24!=0;f=E?10:1;B=dr(E?9:2,-1)|0;do{if((c[5094]|0)==1){if((a[32936]&1)!=0){break}if(!(a[20368]|0)){break}a[20368]=0;g[3538]=+g[5090];g[3536]=+g[5088];g[3534]=+g[5086];c[16336]=c[16336]^2;c[17024]=c[17024]^2}}while(0);E=dr(f,-1)|0;f=a[32936]|0;do{if((c[5094]|0)==1){if((f&1)!=0){break}if(a[20368]|0){break}a[20368]=1;g[5090]=+g[3538];g[5088]=+g[3536];g[5086]=+g[3534];g[3538]=180.0;g[3536]=0.0;g[3534]=1.2999999523162842;c[16336]=c[16336]^2;c[17024]=c[17024]^2}}while(0);do{if((f&1)!=0){dq(2);do{if((c[5094]|0)==1){if(!(a[20368]|0)){break}a[20368]=0;g[3538]=+g[5090];g[3536]=+g[5088];g[3534]=+g[5086];c[16336]=c[16336]^2;c[17024]=c[17024]^2}}while(0);dq(1);if((c[5094]|0)!=1){break}if(a[20368]|0){break}a[20368]=1;g[5090]=+g[3538];g[5088]=+g[3536];g[5086]=+g[3534];g[3538]=180.0;g[3536]=0.0;g[3534]=1.2999999523162842;c[16336]=c[16336]^2;c[17024]=c[17024]^2}}while(0);dq(0);do{if((c[16506]&3|0)==0){K=+h[8256];J=+h[8255];if(K>=J){p=6940;break}h[8255]=K;h[8256]=J;L=1}else{p=6940}}while(0);if((p|0)==6940){L=(c[16508]|0)>>>1&1}a[66036]=L;do{if((c[16334]&3|0)==0){J=+h[8170];K=+h[8169];if(J>=K){p=6944;break}h[8169]=J;h[8170]=K;M=1}else{p=6944}}while(0);if((p|0)==6944){M=(c[16336]|0)>>>1&1}a[65348]=M;do{if((c[16162]&3|0)==0){K=+h[8084];J=+h[8083];if(K>=J){p=6948;break}h[8083]=K;h[8084]=J;N=1}else{p=6948}}while(0);if((p|0)==6948){N=(c[16164]|0)>>>1&1}a[64660]=N;N=e;M=e+16|0;L=0;do{f=64944+(L*688&-1)|0;r=c[f>>2]|0;if((r|0)==0){p=6957}else{c[N>>2]=c[7254];c[N+4>>2]=c[7255];c[N+8>>2]=c[7256];c[N+12>>2]=c[7257];c[N+16>>2]=c[7258];c[N+20>>2]=c[7259];m=e;j=r;L9464:while(1){r=j;while(1){if((c[r+12>>2]|0)>=0){break}uu(c[r+8>>2]|0);O=c[r+16>>2]|0;uu(r);if((O|0)==0){P=m;break L9464}else{r=O}}c[m+16>>2]=r;O=c[r+16>>2]|0;if((O|0)==0){P=r;break}else{m=r;j=O}}c[P+16>>2]=0;j=c[M>>2]|0;c[f>>2]=j;if((j|0)==0){p=6957}}do{if((p|0)==6957){p=0;j=64920+(L*688&-1)|0;if((c[j>>2]|0)!=3){break}c[j>>2]=1}}while(0);L=L+1|0;}while(L>>>0<11);if((B|0)>-1){L=c[1054]|0;p=c[L+(B*40&-1)+36>>2]|0;M=(p|0)>49?49:p;p=c[10036]|0;P=c[L+(B*40&-1)+32>>2]|0;B=0;while(1){L=B+1|0;a[B+55600|0]=a[p+P|0]|0;if((L|0)==(M|0)){break}else{P=P+1|0;B=L}}a[M+55600|0]=0}else{uB(55600,24544)}if((E|0)<=-1){uB(55651,24595);i_();i=d;return}M=c[1054]|0;B=c[M+(E*40&-1)+36>>2]|0;P=(B|0)>49?49:B;B=c[10036]|0;p=c[M+(E*40&-1)+32>>2]|0;E=0;while(1){M=E+1|0;a[E+55651|0]=a[B+p|0]|0;if((M|0)==(P|0)){break}else{p=p+1|0;E=M}}a[P+55651|0]=0;i_();i=d;return}function i_(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,U=0,V=0,W=0,X=0,Y=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0,bB=0,bC=0,bD=0,bE=0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bM=0,bN=0,bO=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0,b7=0,b8=0,b9=0,ca=0,cb=0,cc=0,cd=0,ce=0,cf=0,cg=0,ch=0,ci=0,cj=0,ck=0,cl=0,cm=0,cn=0,co=0,cp=0,cq=0,cr=0,cs=0,ct=0,cu=0,cv=0,cw=0,cx=0,cy=0,cz=0,cA=0,cB=0,cC=0,cD=0,cE=0,cF=0,cG=0,cH=0.0,cI=0,cJ=0,cK=0,cL=0,cM=0,cN=0,cO=0.0,cP=0,cQ=0,cR=0.0,cS=0.0,cT=0.0,cU=0.0,cV=0,cW=0.0,cX=0.0,cY=0.0,cZ=0.0,c_=0.0,c$=0.0,c0=0,c1=0.0,c2=0,c3=0.0,c4=0,c5=0.0,c6=0,c7=0.0,c8=0.0,c9=0.0,da=0.0,de=0,df=0.0,dg=0.0,dh=0,di=0,dj=0.0,dl=0.0,dm=0.0,dn=0.0,dp=0.0,dq=0.0,dr=0.0,ds=0.0,dt=0.0,du=0.0,dv=0.0,dw=0.0,dx=0.0,dy=0.0,dz=0.0,dA=0.0,dB=0.0,dC=0.0,dD=0,dE=0,dF=0,dG=0,dH=0,dI=0,dJ=0,dK=0,dL=0,dM=0,dN=0.0,dO=0,dP=0,dQ=0,dR=0,dS=0,dU=0,dV=0,dW=0,dX=0,dY=0,dZ=0,d_=0.0,d$=0.0,d0=0.0,d1=0.0,d2=0,d3=0.0,d4=0.0,d5=0.0,d6=0.0,d7=0.0,d8=0.0,d9=0.0,ea=0.0,eb=0.0,ec=0.0,ed=0.0,ee=0.0,ef=0,eg=0.0,eh=0,ei=0,ej=0,ek=0,el=0,em=0,en=0,eo=0,eq=0,er=0,es=0,et=0,eu=0,ev=0,ew=0,ex=0,eA=0,eB=0,eD=0,eE=0,eF=0,eG=0,eH=0,eI=0,eJ=0,eK=0,eN=0,eO=0,eP=0,eQ=0,eR=0,eS=0,eT=0,eU=0,eV=0,eW=0.0,eX=0.0,eY=0.0,eZ=0.0,e_=0,e$=0,e0=0,e1=0,e2=0,e3=0,e4=0,e5=0,e8=0,e9=0,fa=0,fb=0,fc=0,fd=0,fe=0,ff=0,fg=0,fh=0,fi=0,fj=0,fk=0.0,fl=0,fm=0,fn=0.0;b=i;i=i+272|0;d=b|0;e=b+32|0;f=b+40|0;g=b+128|0;j=b+152|0;k=b+200|0;l=b+256|0;n=b+264|0;o=c[10828]|0;if((o|0)!=0&(c[7952]|0)>0){iY(o)}c[7952]=0;c[10828]=0;c[200]=2;c[144]=1;c[34]=0;a[872]=0;o=c[13898]|0;c[7774]=iw()|0;p=c[13898]|0;if((p|0)>=(c[8272]|0)){q=p;uf(q,105216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}r=b+96|0;s=k;t=k+4|0;u=k+16|0;w=k+24|0;x=k+8|0;y=k|0;z=k+40|0;A=k+44|0;B=k+32|0;C=g|0;D=j|0;E=j+8|0;F=f|0;G=f+8|0;H=f+16|0;I=f+24|0;J=f+32|0;K=f+40|0;L=f+48|0;f=b+8|0;M=b+176|0;N=0;O=0;U=0;V=0;W=0;X=0;Y=0;$=0;ab=0;ac=0;ad=43312;ae=p;L9498:while(1){p=(V|0)==0;af=Y;ag=ac;ah=ae;while(1){ai=c[1054]|0;L9502:do{if((a[ai+(ah*40&-1)|0]&1)!=0){aj=c[ai+(ah*40&-1)+36>>2]|0;ak=ai+(ah*40&-1)+32|0;al=c[10036]|0;am=0;while(1){if((am|0)>=(aj|0)){break}if((a[al+((c[ak>>2]|0)+am|0)|0]|0)==(a[am+103664|0]|0)){am=am+1|0}else{break L9502}}if((am|0)==1){an=af;ao=ag;ap=N;aq=V;ar=$;as=ab;at=ah;au=6983;break L9498}}}while(0);if(p){av=af?ag:ah}else{av=ag}if((t9(ah)|0)==0){au=6995;break}dT();ai=c[13898]|0;ak=(c[8272]|0)>(ai|0);if(!ak){an=1;ao=av;ap=N;aq=V;ar=$;as=ab;at=ai;au=6983;break L9498}al=c[1054]|0;if((a[al+(ai*40&-1)|0]&1)==0){af=1;ag=av;ah=ai;continue}aj=c[al+(ai*40&-1)+36>>2]|0;aw=al+(ai*40&-1)+32|0;al=c[10036]|0;ax=0;while(1){if((ax|0)>=(aj|0)){au=6994;break}if((a[al+((c[aw>>2]|0)+ax|0)|0]|0)==(a[ax+148464|0]|0)){ax=ax+1|0}else{break}}if((au|0)==6994){au=0;if((ax|0)==1){ay=ad;az=av;aA=ab;aB=$;aC=af;aD=X;aE=W;aF=V;aG=U;aH=O;aJ=N;break}}if(ak){af=1;ag=av;ah=ai}else{an=1;ao=av;ap=N;aq=V;ar=$;as=ab;at=ai;au=6983;break L9498}}do{if((au|0)==6995){au=0;if(af){aK=av}else{aK=(a[32936]&1)==0|p?c[13898]|0:av}c[11670]=31112;ah=iu(0)|0;c[11670]=0;if((ah|0)==0){ag=N+1|0;if((a[32936]&1)==0){aL=V}else{aL=(V+2|0)%3&-1}aw=c[ad>>2]|0;al=(a[38984]&1)==0;L9562:do{if((aw|0)==0){if(al){aM=i0(c[6352]|0,c[9344]|0,c[6350]|0,c[9342]|0)|0}else{aM=i0(c[9344]|0,0,0,c[9342]|0)|0}c[ad>>2]=aM;aN=aM}else{if(!al){aj=c[9344]|0;aO=c[9342]|0;aP=aw+260|0;aQ=c[aP>>2]|0;if((aQ|0)!=0){aR=aQ;while(1){aQ=c[aR>>2]|0;if((aR|0)!=0){aS=c[aR+12>>2]|0;if((aS|0)!=0){uu(aS)}uu(aR)}if((aQ|0)==0){break}else{aR=aQ}}}c[aP>>2]=0;if((aO|0)>0&(aj|0)>0){aR=0;while(1){am=hu(aj)|0;c[am>>2]=c[aP>>2];c[aP>>2]=am;am=aR+1|0;if((am|0)<(aO|0)){aR=am}else{aN=aw;break L9562}}}else{c[aP>>2]=0;aN=aw;break}}aR=c[6352]|0;aO=c[9344]|0;aj=c[6350]|0;am=c[9342]|0;aQ=aw+260|0;aS=c[aQ>>2]|0;if((aS|0)!=0){aT=aS;while(1){aS=c[aT>>2]|0;if((aT|0)!=0){aU=c[aT+12>>2]|0;if((aU|0)!=0){uu(aU)}uu(aT)}if((aS|0)==0){break}else{aT=aS}}}c[aQ>>2]=0;if(!((am|0)>0&(aR|0)>0)){c[aQ>>2]=0;aN=aw;break}if((aO|0)>0){aT=0;while(1){aP=hu(aj)|0;c[aP>>2]=c[aQ>>2];c[aQ>>2]=aP;aP=aT+1|0;if((aP|0)<(aO|0)){aT=aP}else{aV=0;break}}}else{aV=0}while(1){aT=hu(aR)|0;c[aT>>2]=c[aQ>>2];c[aQ>>2]=aT;aT=aV+1|0;if((aT|0)<(am|0)){aV=aT}else{aN=aw;break}}}}while(0);c[aN+8>>2]=2;c[aN+244>>2]=1;c[aN+12>>2]=c[10058];c[aN+252>>2]=c[9342];aW=(c[13898]|0)-1|0;aX=ab;aZ=1;a_=aL;a$=aN;a0=ag}else{if(!((a[32936]&1)==0|p)){au=6999;break L9498}do{if(!ab){aw=c[16506]|0;if((aw&1|0)!=0){h[8255]=8.988465674311579e+307}if((aw&2|0)!=0){h[8256]=-8.988465674311579e+307}aw=c[16334]|0;if((aw&1|0)!=0){h[8169]=8.988465674311579e+307}if((aw&2|0)==0){break}h[8170]=-8.988465674311579e+307}}while(0);ag=c[ad>>2]|0;if((ag|0)==0){uE(M|0,0,24);aw=ut(272)|0;if((aw|0)==0){gk();al=ut(272)|0;if((al|0)==0){au=7012;break L9498}else{a1=al}}else{a1=aw}aw=a1;uE(a1|0,0,272);c[a1+28>>2]=-2;al=a1+32|0;c[al>>2]=0;c[al+4>>2]=0;h[a1+40>>3]=1.0;h[a1+48>>3]=-2.0;al=a1+56|0;c[al>>2]=c[M>>2];c[al+4>>2]=c[M+4>>2];c[al+8>>2]=c[M+8>>2];c[al+12>>2]=c[M+12>>2];c[al+16>>2]=c[M+16>>2];c[al+20>>2]=c[M+20>>2];c[a1+80>>2]=-1;c[a1+84>>2]=0;al=a1+88|0;c[al>>2]=c[12872];c[al+4>>2]=c[12873];c[al+8>>2]=c[12874];c[al+12>>2]=c[12875];c[al+16>>2]=c[12876];c[al+20>>2]=c[12877];c[al+24>>2]=c[12878];c[al+28>>2]=c[12879];c[al+32>>2]=c[12880];c[al+36>>2]=c[12881];c[al+40>>2]=c[12882];c[al+44>>2]=c[12883];c[al+48>>2]=c[12884];c[al+52>>2]=c[12885];c[a1+144>>2]=1;h[a1+152>>3]=0.0;c[a1+160>>2]=0;h[a1+168>>3]=15.0;h[a1+176>>3]=90.0;c[a1+184>>2]=0;c[ad>>2]=aw;a2=aw}else{a2=ag}c[a2+8>>2]=3;c[a2+12>>2]=c[12890];c[11856]=2;ag=ey(ah,7,a2)|0;if((a[47584]&1)!=0){c[a2+244>>2]=1}aw=(c[13898]|0)-1|0;c[a2+4>>2]=aw;al=c[7774]|0;if((al|0)==0){a3=0}else{a3=c[al+32>>2]|0}c[a2+248>>2]=a3;if((ag|0)<3){if((c[16546]|0)==1){au=7020;break L9498}if((c[16374]|0)==1){au=7022;break L9498}}c[12210]=2;c[12211]=1;c[12212]=0;aW=aw;aX=1;aZ=$;a_=V;a$=a2;a0=N}a4=a$+16|0;aw=c[a4>>2]|0;if((aw|0)!=0){uu(aw);c[a4>>2]=0}aw=a$+24|0;ag=a$+56|0;a[ag]=0;al=a$+28|0;c[al>>2]=X;ai=a$+32|0;c[ai>>2]=W;ak=a$+240|0;c[ak>>2]=X;ax=X+1|0;do{if((a[30080]&1)==0){am=aw|0;aQ=c[am>>2]|0;aR=c[8798]|0;aT=(aR|0)>0;aO=ax;L9612:while(1){aj=43264;while(1){a5=c[aj>>2]|0;if((a5|0)==0){break}if((c[a5+4>>2]|0)==(aO|0)){break L9612}else{aj=a5|0}}a6=aO-1|0;if(!((aO|0)>(aR|0)&aT)){au=7067;break}aO=((a6|0)%(aR|0)&-1)+1|0}if((au|0)==7067){au=0;c[al>>2]=a6;c[a$+64>>2]=1;c[a$+68>>2]=a6;c[ai>>2]=a6;break}aR=aw;aT=a5+8|0;c[aR>>2]=c[aT>>2];c[aR+4>>2]=c[aT+4>>2];c[aR+8>>2]=c[aT+8>>2];c[aR+12>>2]=c[aT+12>>2];c[aR+16>>2]=c[aT+16>>2];c[aR+20>>2]=c[aT+20>>2];c[aR+24>>2]=c[aT+24>>2];c[aR+28>>2]=c[aT+28>>2];c[aR+32>>2]=c[aT+32>>2];c[aR+36>>2]=c[aT+36>>2];c[aR+40>>2]=c[aT+40>>2];c[aR+44>>2]=c[aT+44>>2];c[aR+48>>2]=c[aT+48>>2];c[aR+52>>2]=c[aT+52>>2];c[am>>2]=aQ;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[al>>2]=aO;a[ag]=0;break}if((a[ag]&1)!=0){break}c[a$+64>>2]=1;c[a$+68>>2]=c[al>>2]}else{lK(aw,ax)}}while(0);ah=a$+12|0;aT=a$+224|0;aR=a$+80|0;aj=a$+88|0;aP=aw;aS=aj;aU=a$+80|0;a7=a$+84|0;a8=a$+144|0;a9=a$+152|0;ba=a$+160|0;bb=a$+168|0;bc=a$+176|0;bd=a$+184|0;be=aj|0;aj=a$+92|0;bf=a$+128|0;bg=a$+132|0;bh=a$+96|0;bi=a$+120|0;bj=a$+238|0;bk=a$+237|0;bl=a$+236|0;bm=a$+8|0;bn=a$+232|0;bo=a$+264|0;bp=a$+22|0;bq=(U|0)==0;br=(O|0)==0;bs=a$+20|0;bt=a$;bu=(a_|0)==0;bv=0;bw=0;bx=0;by=0;bz=0;L9627:while(1){bB=c[13898]|0;bC=c[8272]|0;L9629:do{if((bB|0)<(bC|0)){bD=c[1054]|0;bE=a[bD+(bB*40&-1)|0]|0;if((bE&1)==0){bF=bD;bG=bE;break}bH=c[bD+(bB*40&-1)+36>>2]|0;bI=bD+(bB*40&-1)+32|0;bJ=c[10036]|0;bK=0;while(1){if((bK|0)>=(bH|0)){break}if((a[bJ+((c[bI>>2]|0)+bK|0)|0]|0)==(a[bK+103664|0]|0)){bK=bK+1|0}else{bF=bD;bG=bE;break L9629}}if((bK|0)!=1|bw^1){bF=bD;bG=bE}else{break L9627}}else{if(bw){break L9627}bI=c[1054]|0;bF=bI;bG=a[bI+(bB*40&-1)|0]|0}}while(0);aO=c[bF+(bB*40&-1)+36>>2]|0;aQ=c[bF+(bB*40&-1)+32>>2]|0;am=(bG&1)==0;L9639:do{if((aO|0)>0&(am^1)){bI=c[10036]|0;bJ=0;bH=0;bL=aQ;while(1){if((a[bJ+224032|0]|0)==(a[bI+(bJ+bL|0)|0]|0)){bM=bL;bN=bH}else{if((bJ|0)!=1){break}bM=bL-1|0;bN=1}bO=bJ+1|0;if((bO|0)<(bN+aO|0)){bJ=bO;bH=bN;bL=bM}else{au=7083;break}}do{if((au|0)==7083){au=0;if((bN|0)==0){if(!((bJ|0)==0|(bJ|0)==5)){break}}if(bz){bQ=bB;au=8109;break L9498}a[bs]=a[36231]&1^1;do{if((a[32936]&1)!=0){if(!bu){au=7088;break L9498}if(!bq){a[U]=0}if(br){break}a[O]=0}}while(0);bR=(c[13898]|0)+1|0;c[13898]=bR;bL=c[1054]|0;bH=c[bL+(bR*40&-1)+36>>2]|0;bI=c[bL+(bR*40&-1)+32>>2]|0;bE=(a[bL+(bR*40&-1)|0]&1)==0;L9661:do{if((bH|0)>0&(bE^1)){bL=c[10036]|0;bD=0;bK=0;bO=bI;while(1){if((a[bD+137208|0]|0)==(a[bL+(bD+bO|0)|0]|0)){bS=bO;bT=bK}else{if((bD|0)!=3){break L9661}bS=bO-1|0;bT=1}bU=bD+1|0;if((bU|0)<(bT+bH|0)){bD=bU;bK=bT;bO=bS}else{break}}if((bT|0)==0){if(!((bD|0)==2|(bD|0)==12)){break}}eM(bt);bv=bv;bw=bw;bx=bx;by=by;bz=1;continue L9627}}while(0);if((bR|0)>=(c[8272]|0)){au=7110;break L9498}L9675:do{if(!bE){bO=c[10036]|0;bK=0;while(1){if((bK|0)>=(bH|0)){break}if((a[bO+(bI+bK|0)|0]|0)==(a[bK+103664|0]|0)){bK=bK+1|0}else{break L9675}}if((bK|0)==1){au=7110;break L9498}}}while(0);a[14176]=1;is(j);a[14176]=0;if((c[D>>2]|0)!=3){au=7109;break L9498}bI=c[E>>2]|0;c[a4>>2]=bI;if((bI|0)==0){au=8108;break L9498}else{bv=bv;bw=bw;bx=bx;by=by;bz=1;continue L9627}}}while(0);if(am){break}L9685:do{if((aO|0)>0){bJ=c[10036]|0;bI=0;bH=0;bE=aQ;while(1){if((a[bI+202e3|0]|0)==(a[bJ+(bI+bE|0)|0]|0)){bV=bE;bW=bH}else{if((bI|0)!=3){break L9685}bV=bE-1|0;bW=1}bO=bI+1|0;if((bO|0)<(bW+aO|0)){bI=bO;bH=bW;bE=bV}else{break}}if((bW|0)==0){if(!((bI|0)==2|(bI|0)==7)){break}}if(bz){bQ=bB;au=8110;break L9498}bE=bB+1|0;c[13898]=bE;bH=(a[bF+(bE*40&-1)|0]&1)==0;if(bH){au=7125}else{bJ=c[10036]|0;bO=a[bJ+(c[bF+(bE*40&-1)+32>>2]|0)|0]|0;if((bO<<24>>24|0)==39|(bO<<24>>24|0)==34){bX=bJ;au=7135}else{au=7125}}L9700:do{if((au|0)==7125){au=0;bJ=c[10810]|0;if((bJ|0)==0){break}bO=bF+(bE*40&-1)+36|0;bD=bF+(bE*40&-1)+32|0;bL=c[10036]|0;bU=bJ;L9703:while(1){bJ=c[bU+4>>2]|0;L9705:do{if(!((bC|0)<=(bE|0)|bH)){bY=c[bO>>2]|0;bZ=0;while(1){if((bZ|0)>=(bY|0)){break}if((a[bL+((c[bD>>2]|0)+bZ|0)|0]|0)==(a[bJ+bZ|0]|0)){bZ=bZ+1|0}else{break L9705}}if((a[bJ+bZ|0]|0)==0){break L9703}}}while(0);bJ=c[bU>>2]|0;if((bJ|0)==0){break L9700}else{bU=bJ}}if((a[bU+8|0]&1)!=0){break}if((c[bU+16>>2]|0)==3){bX=bL;au=7135}}}while(0);L9714:do{if((au|0)==7135){au=0;if((bE|0)>=(bC|0)){break}L9717:do{if(!bH){bI=c[bF+(bE*40&-1)+36>>2]|0;bD=bF+(bE*40&-1)+32|0;bO=0;while(1){if((bO|0)>=(bI|0)){break}if((a[bX+((c[bD>>2]|0)+bO|0)|0]|0)==(a[bO+103664|0]|0)){bO=bO+1|0}else{break L9717}}if((bO|0)==1){break L9714}}}while(0);a[14176]=1;is(g);a[14176]=0;if((c[C>>2]|0)==3){break}c[13898]=bE}}while(0);a[bp]=1;if(!bq){a[U]=0}if(br){bv=bv;bw=bw;bx=bx;by=by;bz=1;continue L9627}a[O]=0;bv=bv;bw=bw;bx=bx;by=by;bz=1;continue L9627}}while(0);if(!((aO|0)>0&(am^1))){break}bE=c[10036]|0;bH=0;bL=0;bU=aQ;while(1){if((a[bH+196448|0]|0)==(a[bE+(bH+bU|0)|0]|0)){b_=bU;b$=bL}else{if((bH|0)!=1){break}b_=bU-1|0;b$=1}bD=bH+1|0;if((bD|0)<(b$+aO|0)){bH=bD;bL=b$;bU=b_}else{au=7153;break}}do{if((au|0)==7153){au=0;if((b$|0)==0){if(!((bH|0)==0|(bH|0)==4)){break}}if(by){bQ=bB;au=8111;break L9498}bU=bB+1|0;c[13898]=bU;bL=c[bF+(bU*40&-1)+36>>2]|0;bE=c[bF+(bU*40&-1)+32>>2]|0;bD=(a[bF+(bU*40&-1)|0]&1)==0;bU=(bL|0)>0;bI=c[10036]|0;bK=30672;bJ=116448;L9744:while(1){L9746:do{if(!bD){if(bU){bY=0;b0=0;b1=bE;while(1){b2=a[bJ+bY|0]|0;if(b2<<24>>24==(a[bI+(bY+b1|0)|0]|0)){b3=b1;b4=b0}else{if(b2<<24>>24!=36){break L9746}b3=b1-1|0;b4=1}b5=bY+1|0;if((b5|0)<(b4+bL|0)){bY=b5;b0=b4;b1=b3}else{break}}if((b4|0)==0){b6=b5}else{b7=bK;break L9744}}else{b6=0}b1=a[bJ+b6|0]|0;if((b1<<24>>24|0)==36|(b1<<24>>24|0)==0){b7=bK;break L9744}}}while(0);b1=bK+8|0;b0=c[b1>>2]|0;if((b0|0)==0){b7=b1;break}else{bK=b1;bJ=b0}}bJ=c[b7+4>>2]|0;b8=bB+2|0;c[13898]=b8;if((bJ|0)==-1){au=7167;break L9498}c[ah>>2]=bJ;do{if((c[bm>>2]|0)==2){if(!((bJ&4|0)!=0|(bJ|0)==368)){b9=bJ;break}uh(c[13898]|0,185192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[ah>>2]=18;b9=18}else{b9=bJ}}while(0);L9764:do{if(((c[12890]|b9)&352|0)==0){ca=b9}else{bJ=c[13898]|0;if((c[8272]|0)<=(bJ|0)){ca=b9;break}bK=c[1054]|0;if((a[bK+(bJ*40&-1)|0]&1)==0){ca=b9;break}bL=c[bK+(bJ*40&-1)+36>>2]|0;bI=bK+(bJ*40&-1)+32|0;bK=c[10036]|0;bE=0;while(1){if((bE|0)>=(bL|0)){break}if((a[bK+((c[bI>>2]|0)+bE|0)|0]|0)==(a[bE+95280|0]|0)){bE=bE+1|0}else{ca=b9;break L9764}}if((bE|0)!=2){ca=b9;break}c[13898]=bJ+1;if((i9(bo)|0)!=0){au=8116;break L9498}ca=c[ah>>2]|0}}while(0);if(!((ca|0)==400|(ca|0)==432|(ca|0)==416)){bv=bv;bw=bw;bx=bx;by=1;bz=bz;continue L9627}bI=c[13898]|0;if((c[8272]|0)<=(bI|0)){bv=bv;bw=bw;bx=bx;by=1;bz=bz;continue L9627}bK=c[1054]|0;if((a[bK+(bI*40&-1)|0]&1)==0){bv=bv;bw=bw;bx=bx;by=1;bz=bz;continue L9627}bL=c[bK+(bI*40&-1)+36>>2]|0;bU=bK+(bI*40&-1)+32|0;bK=c[10036]|0;bD=0;while(1){if((bD|0)>=(bL|0)){break}if((a[bK+((c[bU>>2]|0)+bD|0)|0]|0)==(a[bD+224816|0]|0)){bD=bD+1|0}else{bv=bv;bw=bw;bx=bx;by=1;bz=bz;continue L9627}}if((bD|0)!=8){bv=bv;bw=bw;bx=bx;by=1;bz=bz;continue L9627}c[13898]=bI+1;a[bn]=1;bv=bv;bw=bw;bx=bx;by=1;bz=bz;continue L9627}}while(0);if(am){break}L9785:do{if((aO|0)>0){bH=c[10036]|0;bU=0;bK=0;bL=aQ;while(1){if((a[bU+175968|0]|0)==(a[bH+(bU+bL|0)|0]|0)){cb=bL;cc=bK}else{if((bU|0)!=8){break L9785}cb=bL-1|0;cc=1}b0=bU+1|0;if((b0|0)<(cc+aO|0)){bU=b0;bK=cc;bL=cb}else{break}}if((cc|0)==0){if(!((bU|0)==10|(bU|0)==7)){break}}c[13898]=bB+1;a[bl]=1;bv=bv;bw=bw;bx=bx;by=by;bz=bz;continue L9627}}while(0);if(!((aO|0)>0&(am^1))){break}bL=c[10036]|0;bK=0;bH=0;bI=aQ;while(1){if((a[bK+171984|0]|0)==(a[bL+(bK+bI|0)|0]|0)){cd=bI;ce=bH}else{if((bK|0)!=5){break}cd=bI-1|0;ce=1}bD=bK+1|0;if((bD|0)<(ce+aO|0)){bK=bD;bH=ce;bI=cd}else{au=7204;break}}do{if((au|0)==7204){au=0;if((ce|0)==0){if(!((bK|0)==10|(bK|0)==4)){break}}c[13898]=bB+1;a[bk]=1;bv=bv;bw=bw;bx=bx;by=by;bz=bz;continue L9627}}while(0);if(!((aO|0)>0&(am^1))){break}bK=c[10036]|0;bI=0;bH=0;bL=aQ;while(1){if((a[bI+168e3|0]|0)==(a[bK+(bI+bL|0)|0]|0)){cf=bL;cg=bH}else{if((bI|0)!=5){break L9639}cf=bL-1|0;cg=1}bD=bI+1|0;if((bD|0)<(cg+aO|0)){bI=bD;bH=cg;bL=cf}else{break}}if((cg|0)==0){if(!((bI|0)==4|(bI|0)==9)){break}}c[13898]=bB+1;a[bj]=1;bv=bv;bw=bw;bx=bx;by=by;bz=bz;continue L9627}}while(0);aO=c[ah>>2]|0;do{if((aO|0)==368){aQ=c[aT>>2]|0;if((aQ|0)==0){uE(r|0,0,28);am=ut(192)|0;if((am|0)==0){gk();bC=ut(192)|0;if((bC|0)==0){au=7220;break L9498}else{ch=bC}}else{ch=am}c[ch>>2]=0;c[ch+4>>2]=-1;c[ch+72>>2]=0;c[ch+88>>2]=0;uE(ch+8|0,0,60);c[ch+96>>2]=1;c[ch+144>>2]=4;c[ch+148>>2]=4;c[ch+152>>2]=4;am=ch+156|0;c[am>>2]=c[r>>2];c[am+4>>2]=c[r+4>>2];c[am+8>>2]=c[r+8>>2];c[am+12>>2]=c[r+12>>2];c[am+16>>2]=c[r+16>>2];c[am+20>>2]=c[r+20>>2];c[am+24>>2]=c[r+24>>2];a[ch+184|0]=0;c[aT>>2]=ch;c[ch+48>>2]=1;c[(c[aT>>2]|0)+56>>2]=99;ci=c[aT>>2]|0}else{ci=aQ}jw(ci);aQ=c[13898]|0;if((bB|0)==(aQ|0)){cj=1;ck=c[ah>>2]|0;break}else{if(bv){bQ=aQ;au=8112;break L9498}else{bv=1;bw=1;bx=bx;by=by;bz=bz;continue L9627}}}else{cj=bw;ck=aO}}while(0);if((ck|0)==225){do{if(!cj){c[aU>>2]=-1;c[a7>>2]=0;c[aS>>2]=c[12872];c[aS+4>>2]=c[12873];c[aS+8>>2]=c[12874];c[aS+12>>2]=c[12875];c[aS+16>>2]=c[12876];c[aS+20>>2]=c[12877];c[aS+24>>2]=c[12878];c[aS+28>>2]=c[12879];c[aS+32>>2]=c[12880];c[aS+36>>2]=c[12881];c[aS+40>>2]=c[12882];c[aS+44>>2]=c[12883];c[aS+48>>2]=c[12884];c[aS+52>>2]=c[12885];c[a8>>2]=1;h[a9>>3]=0.0;c[ba>>2]=0;h[bb>>3]=15.0;h[bc>>3]=90.0;c[bd>>2]=0;aO=c[8798]|0;aQ=(aO|0)>0;am=ax;L9839:while(1){bC=43264;while(1){cl=c[bC>>2]|0;if((cl|0)==0){break}if((c[cl+4>>2]|0)==(am|0)){break L9839}else{bC=cl|0}}cm=am-1|0;if(!((am|0)>(aO|0)&aQ)){au=7237;break}am=((cm|0)%(aO|0)&-1)+1|0}if((au|0)==7237){au=0;c[aj>>2]=cm;c[bf>>2]=1;c[bg>>2]=cm;c[bh>>2]=cm;break}aO=cl+8|0;c[aS>>2]=c[aO>>2];c[aS+4>>2]=c[aO+4>>2];c[aS+8>>2]=c[aO+8>>2];c[aS+12>>2]=c[aO+12>>2];c[aS+16>>2]=c[aO+16>>2];c[aS+20>>2]=c[aO+20>>2];c[aS+24>>2]=c[aO+24>>2];c[aS+28>>2]=c[aO+28>>2];c[aS+32>>2]=c[aO+32>>2];c[aS+36>>2]=c[aO+36>>2];c[aS+40>>2]=c[aO+40>>2];c[aS+44>>2]=c[aO+44>>2];c[aS+48>>2]=c[aO+48>>2];c[aS+52>>2]=c[aO+52>>2];c[be>>2]=0;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[aj>>2]=am;a[bi]=0;break}if((a[bi]&1)!=0){break}c[bf>>2]=1;c[bg>>2]=c[aj>>2]}}while(0);hL(aR,1);aO=c[13898]|0;if((bB|0)==(aO|0)){break}if(bx){bQ=aO;au=8113;break L9498}c[aP>>2]=c[aS>>2];c[aP+4>>2]=c[aS+4>>2];c[aP+8>>2]=c[aS+8>>2];c[aP+12>>2]=c[aS+12>>2];c[aP+16>>2]=c[aS+16>>2];c[aP+20>>2]=c[aS+20>>2];c[aP+24>>2]=c[aS+24>>2];c[aP+28>>2]=c[aS+28>>2];c[aP+32>>2]=c[aS+32>>2];c[aP+36>>2]=c[aS+36>>2];c[aP+40>>2]=c[aS+40>>2];c[aP+44>>2]=c[aS+44>>2];c[aP+48>>2]=c[aS+48>>2];c[aP+52>>2]=c[aS+52>>2];bv=bv;bw=1;bx=1;by=by;bz=bz;continue}uE(s|0,0,56);h[u>>3]=1.0;h[w>>3]=-2.0;c[t>>2]=X;c[x>>2]=W;do{if((a[30080]&1)==0){aO=c[8798]|0;aQ=(aO|0)>0;bI=ax;L9880:while(1){bC=43264;while(1){cn=c[bC>>2]|0;if((cn|0)==0){break}if((c[cn+4>>2]|0)==(bI|0)){break L9880}else{bC=cn|0}}co=bI-1|0;if(!((bI|0)>(aO|0)&aQ)){au=7267;break}bI=((co|0)%(aO|0)&-1)+1|0}if((au|0)==7267){au=0;c[t>>2]=co;c[z>>2]=1;c[A>>2]=co;c[x>>2]=co;break}aO=cn+8|0;c[s>>2]=c[aO>>2];c[s+4>>2]=c[aO+4>>2];c[s+8>>2]=c[aO+8>>2];c[s+12>>2]=c[aO+12>>2];c[s+16>>2]=c[aO+16>>2];c[s+20>>2]=c[aO+20>>2];c[s+24>>2]=c[aO+24>>2];c[s+28>>2]=c[aO+28>>2];c[s+32>>2]=c[aO+32>>2];c[s+36>>2]=c[aO+36>>2];c[s+40>>2]=c[aO+40>>2];c[s+44>>2]=c[aO+44>>2];c[s+48>>2]=c[aO+48>>2];c[s+52>>2]=c[aO+52>>2];c[y>>2]=0;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[t>>2]=bI;a[B]=0;break}if((a[B]&1)!=0){break}c[z>>2]=1;c[A>>2]=c[t>>2]}else{aO=43280;while(1){cp=c[aO>>2]|0;if((cp|0)==0){break}if((c[cp+4>>2]|0)==(ax|0)){au=7244;break}else{aO=cp|0}}if((au|0)==7244){au=0;aO=cp+8|0;c[s>>2]=c[aO>>2];c[s+4>>2]=c[aO+4>>2];c[s+8>>2]=c[aO+8>>2];c[s+12>>2]=c[aO+12>>2];c[s+16>>2]=c[aO+16>>2];c[s+20>>2]=c[aO+20>>2];c[s+24>>2]=c[aO+24>>2];c[s+28>>2]=c[aO+28>>2];c[s+32>>2]=c[aO+32>>2];c[s+36>>2]=c[aO+36>>2];c[s+40>>2]=c[aO+40>>2];c[s+44>>2]=c[aO+44>>2];c[s+48>>2]=c[aO+48>>2];c[s+52>>2]=c[aO+52>>2];c[y>>2]=0;if((a[B]&1)!=0){break}c[z>>2]=1;c[A>>2]=c[t>>2];break}aO=c[8798]|0;bI=(aO|0)>0;aQ=ax;L9865:while(1){am=43264;while(1){cq=c[am>>2]|0;if((cq|0)==0){break}if((c[cq+4>>2]|0)==(aQ|0)){break L9865}else{am=cq|0}}cr=aQ-1|0;if(!((aQ|0)>(aO|0)&bI)){au=7256;break}aQ=((cr|0)%(aO|0)&-1)+1|0}if((au|0)==7256){au=0;c[t>>2]=cr;c[z>>2]=1;c[A>>2]=cr;c[x>>2]=cr;break}aO=cq+8|0;c[s>>2]=c[aO>>2];c[s+4>>2]=c[aO+4>>2];c[s+8>>2]=c[aO+8>>2];c[s+12>>2]=c[aO+12>>2];c[s+16>>2]=c[aO+16>>2];c[s+20>>2]=c[aO+20>>2];c[s+24>>2]=c[aO+24>>2];c[s+28>>2]=c[aO+28>>2];c[s+32>>2]=c[aO+32>>2];c[s+36>>2]=c[aO+36>>2];c[s+40>>2]=c[aO+40>>2];c[s+44>>2]=c[aO+44>>2];c[s+48>>2]=c[aO+48>>2];c[s+52>>2]=c[aO+52>>2];c[y>>2]=0;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[t>>2]=aQ;a[B]=0;break}if((a[B]&1)!=0){break}c[z>>2]=1;c[A>>2]=c[t>>2]}}while(0);aO=hH(k,1,(c[ah>>2]&2|0)!=0)|0;bI=c[13898]|0;if((bB|0)==(bI|0)){break}if(bx){bQ=bI;au=8114;break L9498}c[aP>>2]=c[s>>2];c[aP+4>>2]=c[s+4>>2];c[aP+8>>2]=c[s+8>>2];c[aP+12>>2]=c[s+12>>2];c[aP+16>>2]=c[s+16>>2];c[aP+20>>2]=c[s+20>>2];c[aP+24>>2]=c[s+24>>2];c[aP+28>>2]=c[s+28>>2];c[aP+32>>2]=c[s+32>>2];c[aP+36>>2]=c[s+36>>2];c[aP+40>>2]=c[s+40>>2];c[aP+44>>2]=c[s+44>>2];c[aP+48>>2]=c[s+48>>2];c[aP+52>>2]=c[s+52>>2];if((aO|0)==0){bv=bv;bw=1;bx=1;by=by;bz=bz;continue}c[ak>>2]=aO-1;bv=bv;bw=1;bx=1;by=by;bz=bz}by=a$+21|0;a[by]=0;do{if(bz){cs=U;ct=O}else{a[bs]=1;if((c[9056]|0)!=1){if(!bq){a[U]=0}if(br){cs=U;ct=0;break}a[O]=0;cs=U;ct=O;break}bw=c[1054]|0;bv=(c[bw+(aW*40&-1)+36>>2]|0)+(c[bw+(aW*40&-1)+32>>2]|0)|0;bg=db(c[a4>>2]|0,(bv+1|0)-(c[bw+(aK*40&-1)+32>>2]|0)|0,116456)|0;c[a4>>2]=bg;bw=c[(c[1054]|0)+(aK*40&-1)+32>>2]|0;L9908:do{if((bw|0)<(bv|0)){bf=bw;bi=bg;while(1){be=a[(c[10036]|0)+bf|0]|0;if(be<<24>>24==0){cu=bi;break L9908}bh=bi+1|0;a[bi]=be;be=bf+1|0;if((be|0)<(bv|0)){bf=be;bi=bh}else{cu=bh;break}}}else{cu=bg}}while(0);a[cu]=0;if((a_|0)==2){cv=c[a4>>2]|0;cw=O}else if((a_|0)==1){cv=U;cw=c[a4>>2]|0}else{cv=U;cw=O}a[by]=1;cs=cv;ct=cw}}while(0);do{if(!bx){by=c[ah>>2]|0;if((by|0)==225){c[aj>>2]=X;hL(aR,1);c[aP>>2]=c[aS>>2];c[aP+4>>2]=c[aS+4>>2];c[aP+8>>2]=c[aS+8>>2];c[aP+12>>2]=c[aS+12>>2];c[aP+16>>2]=c[aS+16>>2];c[aP+20>>2]=c[aS+20>>2];c[aP+24>>2]=c[aS+24>>2];c[aP+28>>2]=c[aS+28>>2];c[aP+32>>2]=c[aS+32>>2];c[aP+36>>2]=c[aS+36>>2];c[aP+40>>2]=c[aS+40>>2];c[aP+44>>2]=c[aS+44>>2];c[aP+48>>2]=c[aS+48>>2];c[aP+52>>2]=c[aS+52>>2];break}c[al>>2]=X;h[a$+40>>3]=1.0;c[ai>>2]=W;h[a$+48>>3]=+h[3817];a[ag]=0;do{if((a[30080]&1)==0){br=aw|0;bq=c[br>>2]|0;bs=c[8798]|0;bz=(bs|0)>0;bg=ax;L9926:while(1){bv=43264;while(1){cx=c[bv>>2]|0;if((cx|0)==0){break}if((c[cx+4>>2]|0)==(bg|0)){break L9926}else{bv=cx|0}}cy=bg-1|0;if(!((bg|0)>(bs|0)&bz)){au=7301;break}bg=((cy|0)%(bs|0)&-1)+1|0}if((au|0)==7301){au=0;c[al>>2]=cy;c[a$+64>>2]=1;c[a$+68>>2]=cy;c[ai>>2]=cy;cz=by;break}bs=cx+8|0;c[aP>>2]=c[bs>>2];c[aP+4>>2]=c[bs+4>>2];c[aP+8>>2]=c[bs+8>>2];c[aP+12>>2]=c[bs+12>>2];c[aP+16>>2]=c[bs+16>>2];c[aP+20>>2]=c[bs+20>>2];c[aP+24>>2]=c[bs+24>>2];c[aP+28>>2]=c[bs+28>>2];c[aP+32>>2]=c[bs+32>>2];c[aP+36>>2]=c[bs+36>>2];c[aP+40>>2]=c[bs+40>>2];c[aP+44>>2]=c[bs+44>>2];c[aP+48>>2]=c[bs+48>>2];c[aP+52>>2]=c[bs+52>>2];c[br>>2]=bq;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[al>>2]=bg;a[ag]=0;cz=by;break}if((a[ag]&1)!=0){cz=by;break}c[a$+64>>2]=1;c[a$+68>>2]=c[al>>2];cz=by}else{lK(aw,ax);cz=c[ah>>2]|0}}while(0);by=hH(aw,1,(cz&2|0)!=0)|0;if((by|0)==0){c[ak>>2]=X;break}else{c[ak>>2]=by-1;break}}}while(0);ak=c[ah>>2]|0;ax=(ak&2|0)==0;if(!ax){c[aw>>2]=1}do{if(!((c[bm>>2]|0)!=2|ax)){al=a$+48|0;if(+h[al>>3]!=-3.0){break}h[al>>3]=1.0}}while(0);if((ak|0)==1){a[bl]=0}do{if(bu){if((ak|0)==352|(ak|0)==400|(ak|0)==416|(ak|0)==432){cA=X;cB=W;break}aw=c[11690]|0;if(ax){cC=W;cD=a[38984]|0}else{al=a[38984]|0;cC=(((aw|0)!=0?2:1)+W|0)+(al&1)|0;cD=al}cA=(((aw|0)!=0?2:1)+X|0)+(cD&1)|0;cB=cC}else{cA=X;cB=W}}while(0);do{if((ak|0)==400){a[ag]=1}else if((ak|0)==416|(ak|0)==432){ax=c[16678]|0;if((ax&1|0)!=0){h[8341]=0.0}if((ax&2|0)==0){break}h[8342]=255.0}}while(0);if((a$|0)!=(c[ad>>2]|0)){au=7325;break L9498}if((c[bm>>2]|0)==3){cE=c[a$+4>>2]|0;ak=a0;ag=a$;while(1){if((ag|0)==0){au=7329;break L9498}cF=ag+4|0;c[cF>>2]=cE;ax=c[11870]|0;do{if((c[8732]|0)==0){if((ax|0)==2){au=7332;break L9498}else if((ax|0)!=1){break}c[12210]=0}else{if((ax|0)==1){au=7335;break L9498}}}while(0);ax=ag+252|0;c[ax>>2]=0;bu=ag+244|0;c[bu>>2]=1;bl=ag+239|0;a[bl]=0;aw=ag+260|0;al=c[aw>>2]|0;if((al|0)!=0){ai=al;while(1){al=c[ai>>2]|0;aS=c[ai+12>>2]|0;if((aS|0)!=0){uu(aS)}uu(ai);if((al|0)==0){break}else{ai=al}}c[aw>>2]=0}if((a[47584]&1)!=0){c[bu>>2]=1}ai=hu(c[6352]|0)|0;al=ag+12|0;aS=c[al>>2]|0;if((aS|0)==368){c[272]=1;aR=c[273]|0;do{if((aR|0)!=0){if((c[aR>>2]|0)!=2){break}aj=aR+40|0;if((c[aj>>2]|0)!=42){break}c[aj>>2]=43}}while(0);cG=c[al>>2]|0}else{cG=aS}if((cG|0)==225){aR=hu(c[6352]|0)|0;c[ai>>2]=aR;c[aR+8>>2]=0}aR=c[8270]|0;do{if((aR|0)!=0){if((aY(aR|0,139696)|0)==0){break}bA(4,aR|0)}}while(0);a[47040]=1;aR=ag+64|0;aS=ag+48|0;aj=ag+23|0;bx=ag+28|0;by=ag+56|0;bs=ag+224|0;bz=ag;bv=0;bw=0;bB=0;cH=8.988465674311579e+307;bi=0;bf=0;aQ=ai;L10008:while(1){cI=eC(F,7)|0;if((cI|0)==(-9|0)){bv=bv;bw=bw;bB=bB;cH=cH;bi=bi;bf=bf;aQ=aQ;continue}else if((cI|0)==0){bh=c[11932]|0;uh(-1,116952,(v=i,i=i+16|0,c[v>>2]=c[11900],c[v+8>>2]=(bh|0)!=0?bh:179864,v)|0)}else if((cI|0)==(-3|0)){bh=c[al>>2]|0;if((bh|0)==400|(bh|0)==416|(bh|0)==432|(bh|0)==225){bv=bv;bw=bw;bB=bB;cH=cH;bi=bi;bf=bf;aQ=aQ;continue}if((bf|0)==0){if((bv|0)==0){bv=0;bw=bw;bB=bB;cH=cH;bi=bi;bf=0;aQ=aQ;continue}else{cJ=bv}}else{cJ=bf}if((bv|0)<=0){bv=bv;bw=bw;bB=bB;cH=cH;bi=bi;bf=cJ;aQ=aQ;continue}c[aQ+8>>2]=bv;c[aQ>>2]=c[aw>>2];c[aw>>2]=aQ;c[ax>>2]=(c[ax>>2]|0)+1;if((bv|0)!=(cJ|0)){c[bu>>2]=0}bv=0;bw=bw+1|0;bB=bB;cH=cH;bi=bi;bf=cJ;aQ=hu(cJ)|0;continue}else if((cI|0)==(-6|0)){eL(bz);bv=bv;bw=bw;bB=bB;cH=cH;bi=bi;bf=bf;aQ=aQ;continue}else if((cI|0)==(-7|0)){aI(115120,50,1,c[m>>2]|0);bv=bv;bw=bw;bB=bB;cH=cH;bi=bi;bf=bf;aQ=aQ;continue}else if((cI|0)==(-1|0)|(cI|0)==(-4|0)){break}do{if((bv|0)<(c[aQ+4>>2]|0)){au=7370}else{bh=(bv<<1)+1e3|0;hv(aQ,bh);if((c[al>>2]|0)==225){be=aQ|0;hv(c[be>>2]|0,bh);c[(c[be>>2]|0)+8>>2]=0;au=7370;break}else{be=c[aQ+12>>2]|0;cK=bi;cL=be;cM=be+(bv<<6)|0;break}}}while(0);do{if((au|0)==7370){au=0;bg=c[aQ+12>>2]|0;bq=bg+(bv<<6)|0;if((c[al>>2]|0)!=225){cK=bi;cL=bg;cM=bq;break}if((cI|0)<6){c[bq>>2]=2;bv=bv;bw=bw;bB=bB;cH=cH;bi=bi;bf=bf;aQ=aQ;continue L10008}else{cK=(c[(c[aQ>>2]|0)+12>>2]|0)+(bv<<6)|0;cL=bg;cM=bq;break}}}while(0);bq=cM|0;L10036:do{if((cI|0)==(-2|0)|(cI|0)==(-5|0)){c[bq>>2]=2;cN=bB;cO=cH}else{c[bq>>2]=0;bg=c[8732]|0;do{if((bg|0)==0){if((cI|0)==1){cP=3;cQ=bB;cR=cH;cS=+h[F>>3];cT=+(bw|0);cU=+(bv|0);break}else if((cI|0)==2){if((c[al>>2]|0)!=352){au=7380;break L9498}cP=3;cQ=1;cR=+h[G>>3];cS=+h[F>>3];cT=+(bw|0);cU=+(bv|0);break}else{if((cI|0)<=2){cP=cI;cQ=bB;cR=cH;cS=0.0;cT=0.0;cU=0.0;break}cP=cI;cQ=bB;cR=cH;cS=+h[H>>3];cT=+h[G>>3];cU=+h[F>>3];break}}else if((bg|0)==1){if((cI|0)<2){au=7385;break L9498}if((cI|0)<3){h[H>>3]=1.0;cV=3;cW=1.0}else{cV=cI;cW=+h[H>>3]}cX=+h[9040];cY=cX*+h[F>>3];h[F>>3]=cY;cZ=cX*+h[G>>3];h[G>>3]=cZ;cX=cW*+S(+cY);c_=+S(+cZ);c$=c_*cW*+T(+cY);cP=cV;cQ=bB;cR=cH;cS=cW*+T(+cZ);cT=c$;cU=cX*c_}else if((bg|0)==2){if((cI|0)<2){au=7391;break L9498}if((cI|0)<3){h[H>>3]=1.0;c0=3;c1=1.0}else{c0=cI;c1=+h[H>>3]}c_=+h[9040]*+h[F>>3];h[F>>3]=c_;cX=c1*+S(+c_);c$=c1*+T(+c_);cP=c0;cQ=bB;cR=cH;cS=+h[G>>3];cT=c$;cU=cX}else{au=7396;break L9498}}while(0);if((cP|0)<(c[11870]|0)){au=7398;break L9498}bg=c[al>>2]|0;if((bg|0)==33){br=(c[aR>>2]|0)==6;c2=br?1:cQ;c3=br?cS:cR}else{c2=cQ;c3=cR}do{if((cP|0)>3){do{if((bg|0)==18|(bg|0)==51){if(+h[aS>>3]==-3.0){h[cL+(bv<<6)+48>>3]=+h[I>>3];c4=0;c5=cS;break}else{if((bg|0)==368){c4=0;c5=cS;break}else{au=7406;break}}}else if((bg|0)==368){c4=0;c5=cS}else{au=7406}}while(0);if((au|0)==7406){au=0;c4=1;c5=+h[I>>3]}if((cP|0)<=4){c6=c4;c7=c5;c8=0.0;c9=0.0;da=0.0;break}bJ=c[al>>2]|0;if((bJ|0)==18|(bJ|0)==51){if(+h[aS>>3]!=-3.0){c6=c4;c7=c5;c8=0.0;c9=0.0;da=0.0;break}cX=+h[J>>3];de=1;df=cX;dg=cX}else{de=c4;df=c5;dg=+h[J>>3]}bE=(bJ|0)==368;if(!((cP|0)>5&(bJ|0)==225)){c6=bE?1:de;c7=bE?dg:df;c8=0.0;c9=0.0;da=0.0;break}bE=(cP|0)>6;c6=bE&1;c7=bE?+h[L>>3]:cS;c8=cS+ +h[K>>3];c9=cT+dg;da=cU+ +h[I>>3]}else{c6=c2;c7=c3;c8=0.0;c9=0.0;da=0.0}}while(0);c[bq>>2]=0;bg=c[200]|0;L10082:do{if((bg|0)!=99){if(!(cU>-8.988465674311579e+307&cU<8.988465674311579e+307)){c[bq>>2]=2;cN=c6;cO=c7;break L10036}do{if((a[64788+(bg*688&-1)|0]&1)==0){h[cL+(bv<<6)+8>>3]=cU}else{if(cU<0.0){c[bq>>2]=2;cN=c6;cO=c7;break L10036}if(cU==0.0){h[cL+(bv<<6)+8>>3]=-8.988465674311579e+307;c[bq>>2]=1;break L10082}else{cX=+_(+cU);h[cL+(bv<<6)+8>>3]=cX/+h[64800+(bg*688&-1)>>3];break}}}while(0);if((a[aj]&1)!=0){break}if((c[bq>>2]|0)!=0|(bg|0)<0){break}bE=64712+(bg*688&-1)|0;if(cU<+h[bE>>3]){h[bE>>3]=cU}bE=64664+(bg*688&-1)|0;do{if(cU<+h[bE>>3]){if((c[64648+(bg*688&-1)>>2]&1|0)==0){c[bq>>2]=1;break L10082}if((c[64728+(bg*688&-1)>>2]&1|0)==0){h[bE>>3]=cU;break}cX=+h[64736+(bg*688&-1)>>3];if(cX>cU){h[bE>>3]=cX;c[bq>>2]=1;break L10082}else{h[bE>>3]=cU;break}}}while(0);bE=64720+(bg*688&-1)|0;if(cU>+h[bE>>3]){h[bE>>3]=cU}bE=64672+(bg*688&-1)|0;if(cU<=+h[bE>>3]){break}if((c[64648+(bg*688&-1)>>2]&2|0)==0){c[bq>>2]=1;break}if((c[64732+(bg*688&-1)>>2]&2|0)==0){h[bE>>3]=cU;break}cX=+h[64760+(bg*688&-1)>>3];if(cX<cU){h[bE>>3]=cX;c[bq>>2]=1;break}else{h[bE>>3]=cU;break}}}while(0);bg=c[144]|0;L10128:do{if((bg|0)!=99){if(!(cT>-8.988465674311579e+307&cT<8.988465674311579e+307)){c[bq>>2]=2;cN=c6;cO=c7;break L10036}do{if((a[64788+(bg*688&-1)|0]&1)==0){h[cL+(bv<<6)+16>>3]=cT}else{if(cT<0.0){c[bq>>2]=2;cN=c6;cO=c7;break L10036}if(cT==0.0){h[cL+(bv<<6)+16>>3]=-8.988465674311579e+307;c[bq>>2]=1;break L10128}else{cX=+_(+cT);h[cL+(bv<<6)+16>>3]=cX/+h[64800+(bg*688&-1)>>3];break}}}while(0);if((a[aj]&1)!=0){break}if((c[bq>>2]|0)!=0|(bg|0)<0){break}bE=64712+(bg*688&-1)|0;if(cT<+h[bE>>3]){h[bE>>3]=cT}bE=64664+(bg*688&-1)|0;do{if(cT<+h[bE>>3]){if((c[64648+(bg*688&-1)>>2]&1|0)==0){c[bq>>2]=1;break L10128}if((c[64728+(bg*688&-1)>>2]&1|0)==0){h[bE>>3]=cT;break}cX=+h[64736+(bg*688&-1)>>3];if(cX>cT){h[bE>>3]=cX;c[bq>>2]=1;break L10128}else{h[bE>>3]=cT;break}}}while(0);bE=64720+(bg*688&-1)|0;if(cT>+h[bE>>3]){h[bE>>3]=cT}bE=64672+(bg*688&-1)|0;if(cT<=+h[bE>>3]){break}if((c[64648+(bg*688&-1)>>2]&2|0)==0){c[bq>>2]=1;break}if((c[64732+(bg*688&-1)>>2]&2|0)==0){h[bE>>3]=cT;break}cX=+h[64760+(bg*688&-1)>>3];if(cX<cT){h[bE>>3]=cX;c[bq>>2]=1;break}else{h[bE>>3]=cT;break}}}while(0);L10174:do{if((c[al>>2]|0)==225){c[cK>>2]=0;bg=c[200]|0;L10176:do{if((bg|0)!=99){if(!(da>-8.988465674311579e+307&da<8.988465674311579e+307)){c[bq>>2]=2;cN=c6;cO=c7;break L10036}do{if((a[64788+(bg*688&-1)|0]&1)==0){h[cK+8>>3]=da}else{if(da<0.0){c[bq>>2]=2;cN=c6;cO=c7;break L10036}if(da==0.0){h[cK+8>>3]=-8.988465674311579e+307;c[bq>>2]=1;break L10176}else{cX=+_(+da);h[cK+8>>3]=cX/+h[64800+(bg*688&-1)>>3];break}}}while(0);if((a[aj]&1)!=0){break}if((c[bq>>2]|0)!=0|(bg|0)<0){break}bE=64712+(bg*688&-1)|0;if(da<+h[bE>>3]){h[bE>>3]=da}bE=64664+(bg*688&-1)|0;do{if(da<+h[bE>>3]){if((c[64648+(bg*688&-1)>>2]&1|0)==0){c[bq>>2]=1;break L10176}if((c[64728+(bg*688&-1)>>2]&1|0)==0){h[bE>>3]=da;break}cX=+h[64736+(bg*688&-1)>>3];if(cX>da){h[bE>>3]=cX;c[bq>>2]=1;break L10176}else{h[bE>>3]=da;break}}}while(0);bE=64720+(bg*688&-1)|0;if(da>+h[bE>>3]){h[bE>>3]=da}bE=64672+(bg*688&-1)|0;if(da<=+h[bE>>3]){break}if((c[64648+(bg*688&-1)>>2]&2|0)==0){c[bq>>2]=1;break}if((c[64732+(bg*688&-1)>>2]&2|0)==0){h[bE>>3]=da;break}cX=+h[64760+(bg*688&-1)>>3];if(cX<da){h[bE>>3]=cX;c[bq>>2]=1;break}else{h[bE>>3]=da;break}}}while(0);bg=c[144]|0;if((bg|0)==99){break}if(!(c9>-8.988465674311579e+307&c9<8.988465674311579e+307)){c[bq>>2]=2;cN=c6;cO=c7;break L10036}do{if((a[64788+(bg*688&-1)|0]&1)==0){h[cK+16>>3]=c9}else{if(c9<0.0){c[bq>>2]=2;cN=c6;cO=c7;break L10036}if(c9==0.0){h[cK+16>>3]=-8.988465674311579e+307;c[bq>>2]=1;break L10174}else{cX=+_(+c9);h[cK+16>>3]=cX/+h[64800+(bg*688&-1)>>3];break}}}while(0);if((a[aj]&1)!=0){break}if((c[bq>>2]|0)!=0|(bg|0)<0){break}bE=64712+(bg*688&-1)|0;if(c9<+h[bE>>3]){h[bE>>3]=c9}bE=64664+(bg*688&-1)|0;do{if(c9<+h[bE>>3]){if((c[64648+(bg*688&-1)>>2]&1|0)==0){c[bq>>2]=1;break L10174}if((c[64728+(bg*688&-1)>>2]&1|0)==0){h[bE>>3]=c9;break}cX=+h[64736+(bg*688&-1)>>3];if(cX>c9){h[bE>>3]=cX;c[bq>>2]=1;break L10174}else{h[bE>>3]=c9;break}}}while(0);bE=64720+(bg*688&-1)|0;if(c9>+h[bE>>3]){h[bE>>3]=c9}bE=64672+(bg*688&-1)|0;if(c9<=+h[bE>>3]){break}if((c[64648+(bg*688&-1)>>2]&2|0)==0){c[bq>>2]=1;break}if((c[64732+(bg*688&-1)>>2]&2|0)==0){h[bE>>3]=c9;break}cX=+h[64760+(bg*688&-1)>>3];if(cX<c9){h[bE>>3]=cX;c[bq>>2]=1;break}else{h[bE>>3]=c9;break}}}while(0);L10267:do{if((a[47032]&1)==0){if((c[al>>2]|0)==400){h[cL+(bv<<6)+32>>3]=cS}bE=c[34]|0;L10274:do{if((bE|0)!=99){if(!(cS>-8.988465674311579e+307&cS<8.988465674311579e+307)){c[bq>>2]=2;cN=c6;cO=c7;break L10036}do{if((a[64788+(bE*688&-1)|0]&1)==0){h[cL+(bv<<6)+24>>3]=cS}else{if(cS<0.0){c[bq>>2]=2;cN=c6;cO=c7;break L10036}if(cS==0.0){h[cL+(bv<<6)+24>>3]=-8.988465674311579e+307;c[bq>>2]=1;break L10274}else{cX=+_(+cS);h[cL+(bv<<6)+24>>3]=cX/+h[64800+(bE*688&-1)>>3];break}}}while(0);if((a[aj]&1)!=0){break}if((c[bq>>2]|0)!=0|(bE|0)<0){break}bJ=64712+(bE*688&-1)|0;if(cS<+h[bJ>>3]){h[bJ>>3]=cS}bJ=64664+(bE*688&-1)|0;do{if(cS<+h[bJ>>3]){if((c[64648+(bE*688&-1)>>2]&1|0)==0){c[bq>>2]=1;break L10274}if((c[64728+(bE*688&-1)>>2]&1|0)==0){h[bJ>>3]=cS;break}cX=+h[64736+(bE*688&-1)>>3];if(cX>cS){h[bJ>>3]=cX;c[bq>>2]=1;break L10274}else{h[bJ>>3]=cS;break}}}while(0);bJ=64720+(bE*688&-1)|0;if(cS>+h[bJ>>3]){h[bJ>>3]=cS}bJ=64672+(bE*688&-1)|0;if(cS<=+h[bJ>>3]){break}if((c[64648+(bE*688&-1)>>2]&2|0)==0){c[bq>>2]=1;break}if((c[64732+(bE*688&-1)>>2]&2|0)==0){h[bJ>>3]=cS;break}cX=+h[64760+(bE*688&-1)>>3];if(cX<cS){h[bJ>>3]=cX;c[bq>>2]=1;break}else{h[bJ>>3]=cS;break}}}while(0);bE=c[34]|0;L10320:do{if(!((c[al>>2]|0)!=225|(bE|0)==99)){if(!(c8>-8.988465674311579e+307&c8<8.988465674311579e+307)){c[bq>>2]=2;cN=c6;cO=c7;break L10036}do{if((a[64788+(bE*688&-1)|0]&1)==0){h[cK+24>>3]=c8}else{if(c8<0.0){c[bq>>2]=2;cN=c6;cO=c7;break L10036}if(c8==0.0){h[cK+24>>3]=-8.988465674311579e+307;c[bq>>2]=1;break L10320}else{cX=+_(+c8);h[cK+24>>3]=cX/+h[64800+(bE*688&-1)>>3];break}}}while(0);if((a[aj]&1)!=0){break}if((c[bq>>2]|0)!=0|(bE|0)<0){break}bg=64712+(bE*688&-1)|0;if(c8<+h[bg>>3]){h[bg>>3]=c8}bg=64664+(bE*688&-1)|0;do{if(c8<+h[bg>>3]){if((c[64648+(bE*688&-1)>>2]&1|0)==0){c[bq>>2]=1;break L10320}if((c[64728+(bE*688&-1)>>2]&1|0)==0){h[bg>>3]=c8;break}cX=+h[64736+(bE*688&-1)>>3];if(cX>c8){h[bg>>3]=cX;c[bq>>2]=1;break L10320}else{h[bg>>3]=c8;break}}}while(0);bg=64720+(bE*688&-1)|0;if(c8>+h[bg>>3]){h[bg>>3]=c8}bg=64672+(bE*688&-1)|0;if(c8<=+h[bg>>3]){break}if((c[64648+(bE*688&-1)>>2]&2|0)==0){c[bq>>2]=1;break}if((c[64732+(bE*688&-1)>>2]&2|0)==0){h[bg>>3]=c8;break}cX=+h[64760+(bE*688&-1)>>3];if(cX<c8){h[bg>>3]=cX;c[bq>>2]=1;break}else{h[bg>>3]=c8;break}}}while(0);if((c[bx>>2]|0)==-6){h[cL+(bv<<6)+32>>3]=c7}if(!((c[al>>2]|0)==352|(c[7662]|0)==1)){if((a[by]&1)==0){au=7669;break}}bE=c[bq>>2]|0;if((c6|0)==0){if(!(cS>-8.988465674311579e+307&cS<8.988465674311579e+307)){cN=0;cO=c7;break L10036}do{if((a[66852]&1)==0){h[cL+(bv<<6)+32>>3]=cS}else{if(cS<0.0){cN=0;cO=c7;break L10036}if(cS==0.0){h[cL+(bv<<6)+32>>3]=-8.988465674311579e+307;au=7669;break L10267}else{cX=+_(+cS);h[cL+(bv<<6)+32>>3]=cX/+h[8358];break}}}while(0);if(!((a[aj]&1)==0&(bE|0)==0)){au=7669;break}if(cS<+h[8347]){h[8347]=cS}do{if(cS<+h[8341]){if((c[16678]&1|0)==0){au=7669;break L10267}if((c[16698]&1|0)==0){h[8341]=cS;break}cX=+h[8350];if(cX>cS){h[8341]=cX;au=7669;break L10267}else{h[8341]=cS;break}}}while(0);if(cS>+h[8348]){h[8348]=cS}if(cS<=+h[8342]){au=7669;break}if((c[16678]&2|0)==0){au=7669;break}if((c[16699]&2|0)==0){h[8342]=cS;au=7669;break}cX=+h[8353];if(cX<cS){h[8342]=cX;au=7669;break}else{h[8342]=cS;au=7669;break}}else{if(!(c7>-8.988465674311579e+307&c7<8.988465674311579e+307)){cN=c6;cO=c7;break L10036}do{if((a[66852]&1)==0){h[cL+(bv<<6)+32>>3]=c7}else{if(c7<0.0){cN=c6;cO=c7;break L10036}if(c7==0.0){h[cL+(bv<<6)+32>>3]=-8.988465674311579e+307;au=7669;break L10267}else{cX=+_(+c7);h[cL+(bv<<6)+32>>3]=cX/+h[8358];break}}}while(0);if(!((a[aj]&1)==0&(bE|0)==0)){au=7669;break}if(c7<+h[8347]){h[8347]=c7}do{if(c7<+h[8341]){if((c[16678]&1|0)==0){au=7669;break L10267}if((c[16698]&1|0)==0){h[8341]=c7;break}cX=+h[8350];if(cX>c7){h[8341]=cX;au=7669;break L10267}else{h[8341]=c7;break}}}while(0);if(c7>+h[8348]){h[8348]=c7}if(c7<=+h[8342]){au=7669;break}if((c[16678]&2|0)==0){au=7669;break}if((c[16699]&2|0)==0){h[8342]=c7;au=7669;break}cX=+h[8353];if(cX<c7){h[8342]=cX;au=7669;break}else{h[8342]=c7;au=7669;break}}}else{h[cL+(bv<<6)+24>>3]=cS;bE=c[al>>2]|0;if((bE|0)!=225){dh=bE;break}h[cK+24>>3]=c8;au=7669}}while(0);if((au|0)==7669){au=0;dh=c[al>>2]|0}if((dh|0)==368){iW(c[bs>>2]|0,cM,bv,c[11767]|0,c7);di=c[al>>2]|0}else{di=dh}if(!((di|0)==416|(di|0)==432)){cN=c6;cO=c7;break}h[cL+(bv<<6)+40>>3]=+h[I>>3];h[cL+(bv<<6)+48>>3]=+h[J>>3];h[cL+(bv<<6)+56>>3]=+h[K>>3];h[cL+(bv<<6)+32>>3]=+h[L>>3];cN=c6;cO=c7}}while(0);bv=bv+1|0;bw=bw;bB=cN;cH=cO;bi=cK;bf=bf;aQ=aQ}bi=c[8270]|0;do{if((bi|0)!=0){if((aY(bi|0,139696)|0)==0){break}bA(4,139696)}}while(0);if((bB|0)!=0){a[bl]=1}do{if((bv|0)>0){c[ax>>2]=(c[ax>>2]|0)+1;c[aQ+8>>2]=bv;if((c[al>>2]|0)!=225){c[aQ>>2]=c[aw>>2]}c[aw>>2]=aQ;if((bv|0)==(bf|0)){break}c[bu>>2]=0}else{do{if((c[al>>2]|0)==225){bi=c[aQ>>2]|0;if((bi|0)==0){break}bw=c[bi+12>>2]|0;if((bw|0)!=0){uu(bw)}uu(bi)}}while(0);if((aQ|0)==0){break}bi=c[aQ+12>>2]|0;if((bi|0)!=0){uu(bi)}uu(aQ)}}while(0);do{if((a[47032]&1)!=0){if((c[ax>>2]|0)<=0){break}aQ=c[aw>>2]|0;al=c[aQ+12>>2]|0;cH=+h[al+8>>3];cX=+h[al+16>>3];al=(aQ|0)==0;if(al){dj=cH;dl=cH;dm=cX;dn=cX}else{c$=cH;c_=cH;cH=cX;cZ=cX;bf=aQ;while(1){bv=c[bf+8>>2]|0;if((bv|0)>0){cX=c$;cY=c_;dp=cH;dq=cZ;bB=c[bf+12>>2]|0;bi=0;while(1){do{if((c[bB>>2]|0)==2){dr=dq;ds=dp;dt=cY;du=cX}else{dv=+h[bB+8>>3];dw=cX>dv?dv:cX;dx=cY<dv?dv:cY;dv=+h[bB+16>>3];dy=dp>dv?dv:dp;if(dq>=dv){dr=dq;ds=dy;dt=dx;du=dw;break}dr=dv;ds=dy;dt=dx;du=dw}}while(0);bw=bi+1|0;if((bw|0)<(bv|0)){cX=du;cY=dt;dp=ds;dq=dr;bB=bB+64|0;bi=bw}else{dz=du;dA=dt;dB=ds;dC=dr;break}}}else{dz=c$;dA=c_;dB=cH;dC=cZ}bi=c[bf>>2]|0;if((bi|0)==0){dj=dz;dl=dA;dm=dB;dn=dC;break}else{c$=dz;c_=dA;cH=dB;cZ=dC;bf=bi}}}cZ=(dl-dj)/+((c[11756]|0)-1|0);cH=(dn-dm)/+((c[11732]|0)-1|0);c[aw>>2]=0;c[ax>>2]=c[11756];c[bu>>2]=1;if((c[11752]|0)==2){if(al){dD=0}else{bf=0;bi=aQ;while(1){bB=(c[bi+8>>2]|0)+bf|0;bv=c[bi>>2]|0;if((bv|0)==0){dD=bB;break}else{bf=bB;bi=bv}}}bi=dD+3|0;bf=aa(bi<<3,dD+8|0);bv=ut(bf)|0;if((bv|0)==0){gk();bB=ut(bf)|0;if((bB|0)==0){au=7709;break L9498}else{dE=bB}}else{dE=bv}bv=dE;bB=bi<<2;bi=ut(bB)|0;if((bi|0)==0){gk();bf=ut(bB)|0;if((bf|0)==0){au=7712;break L9498}else{dF=bf}}else{dF=bi}bi=dF;bf=dD<<1;bB=bf+dD|0;bw=bv+(bB<<3)|0;if(al){dG=0;dH=3;au=7715}else{bs=0;by=aQ;while(1){bx=by+8|0;aS=c[bx>>2]|0;if((aS|0)>0){aR=bs;bz=0;ai=c[by+12>>2]|0;bq=aS;while(1){if((c[ai>>2]|0)==2){dI=aR;dJ=bq}else{h[bv+(aR<<3)>>3]=+h[ai+8>>3];h[bv+(aR+dD<<3)>>3]=+h[ai+16>>3];h[bv+(aR+bf<<3)>>3]=+h[ai+24>>3];dI=aR+1|0;dJ=c[bx>>2]|0}aS=bz+1|0;if((aS|0)<(dJ|0)){aR=dI;bz=aS;ai=ai+64|0;bq=dJ}else{dK=dI;break}}}else{dK=bs}bq=c[by>>2]|0;if((bq|0)==0){break}else{bs=dK;by=bq}}by=dK+3|0;if((by|0)>0){dG=dK;dH=by;au=7715}else{dL=dK;dM=by}}if((au|0)==7715){au=0;by=0;while(1){bs=by+1|0;c[bi+(by<<2)>>2]=bv+(aa(bs,dH)+bB<<3);if((bs|0)<(dH|0)){by=bs}else{dL=dG;dM=dH;break}}}if((dL|0)>0){by=0;while(1){bs=by+1|0;bq=(bs|0)<(dL|0);if(bq){ai=bv+(by<<3)|0;bz=bv+(by+dD<<3)|0;aR=bi+(by<<2)|0;bx=bs;while(1){c_=+h[ai>>3]- +h[bv+(bx<<3)>>3];c$=+h[bz>>3]- +h[bv+(bx+dD<<3)>>3];dq=+Q(+(c_*c_+c$*c$));if(dq>0.0){dN=dq*dq*+_(+dq)}else{dN=0.0}dq=-0.0-dN;h[(c[bi+(bx<<2)>>2]|0)+(by<<3)>>3]=dq;h[(c[aR>>2]|0)+(bx<<3)>>3]=dq;aS=bx+1|0;if((aS|0)<(dL|0)){bx=aS}else{dO=aR;break}}}else{dO=bi+(by<<2)|0}h[(c[dO>>2]|0)+(by<<3)>>3]=0.0;h[bv+(by+bB<<3)>>3]=+h[bv+(by+bf<<3)>>3];if(bq){by=bs}else{break}}by=bi+(dL<<2)|0;bf=dL+1|0;aR=bi+(bf<<2)|0;bx=dL+2|0;bz=bi+(bx<<2)|0;ai=0;while(1){h[(c[by>>2]|0)+(ai<<3)>>3]=1.0;aS=bi+(ai<<2)|0;h[(c[aS>>2]|0)+(dL<<3)>>3]=1.0;dq=+h[bv+(ai<<3)>>3];h[(c[aR>>2]|0)+(ai<<3)>>3]=dq;h[(c[aS>>2]|0)+(bf<<3)>>3]=dq;dq=+h[bv+(ai+dD<<3)>>3];h[(c[bz>>2]|0)+(ai<<3)>>3]=dq;h[(c[aS>>2]|0)+(bx<<3)>>3]=dq;aS=ai+1|0;if((aS|0)<(dL|0)){ai=aS}else{dP=bf;dQ=bx;dR=by;dS=aR;dU=bz;break}}}else{bz=dL+1|0;aR=dL+2|0;dP=bz;dQ=aR;dR=bi+(dL<<2)|0;dS=bi+(bz<<2)|0;dU=bi+(aR<<2)|0}h[bv+(dL+bB<<3)>>3]=0.0;h[bv+(dP+bB<<3)>>3]=0.0;h[bv+(dQ+bB<<3)>>3]=0.0;h[(c[dR>>2]|0)+(dL<<3)>>3]=0.0;h[(c[dR>>2]|0)+(dP<<3)>>3]=0.0;h[(c[dR>>2]|0)+(dQ<<3)>>3]=0.0;h[(c[dS>>2]|0)+(dL<<3)>>3]=0.0;h[(c[dS>>2]|0)+(dP<<3)>>3]=0.0;h[(c[dS>>2]|0)+(dQ<<3)>>3]=0.0;h[(c[dU>>2]|0)+(dL<<3)>>3]=0.0;h[(c[dU>>2]|0)+(dP<<3)>>3]=0.0;h[(c[dU>>2]|0)+(dQ<<3)>>3]=0.0;aR=dM<<2;bz=ut(aR)|0;if((bz|0)==0){gk();by=ut(aR)|0;if((by|0)==0){au=7736;break L9498}else{dV=by}}else{dV=bz}bz=dV;ht(bi,dM,bz,e);hq(bi,dM,bz,bw);uu(dF);uu(dV);dW=bv+((dL*3&-1)<<3)|0;dX=bv+(dL<<3)|0;dY=dL;dZ=bv}else{dW=0;dX=0;dY=0;dZ=0}if((c[11756]|0)>0){bz=dW+(dY<<3)|0;by=(dY|0)>0;aR=dW+(dY+1<<3)|0;bx=dW+(dY+2<<3)|0;dq=dj;bf=0;ai=c[11732]|0;while(1){aS=hu(ai+1|0)|0;c[aS+8>>2]=c[11732];c[aS>>2]=c[aw>>2];c[aw>>2]=aS;bE=c[11732]|0;if((bE|0)>0){c$=dm;bg=c[aS+12>>2]|0;aS=0;while(1){bJ=bg|0;c[bJ>>2]=0;br=c[11752]|0;L10552:do{if((br|0)==2){c_=+h[bz>>3];if(by){dp=c_;be=0;while(1){cY=+h[dZ+(be<<3)>>3]-dq;cX=+h[dX+(be<<3)>>3]-c$;dw=+h[dW+(be<<3)>>3];dx=+Q(+(cY*cY+cX*cX));if(dx>0.0){d_=dx*dx*+_(+dx)}else{d_=0.0}dx=dp-dw*d_;bh=be+1|0;if((bh|0)<(dY|0)){dp=dx;be=bh}else{d$=dx;break}}}else{d$=c_}d0=d$+dq*+h[aR>>3]+c$*+h[bx>>3];d1=0.0}else{if(al){d0=0.0;d1=0.0;break}be=(br|0)==1;bh=c[11734]|0;dp=+(bh|0);dx=+h[5865];dw=+h[5864];cX=0.0;cY=0.0;bd=aQ;L10555:while(1){bc=c[bd+8>>2]|0;if((bc|0)>0){dy=cX;dv=cY;bb=0;d2=c[bd+12>>2]|0;while(1){d3=+h[d2+8>>3]-dq;if(be){d4=+P(+d3);d5=+P(+(+h[d2+16>>3]-c$));if((bh|0)==1){d6=d4+d5}else if((bh|0)==2){d6=d4*d4+d5*d5}else if((bh|0)==4){d7=d4*d4+d5*d5;d6=d7*d7}else if((bh|0)==8){d7=d4*d4+d5*d5;d8=d7*d7;d6=d8*d8}else if((bh|0)==16){d8=d4*d4+d5*d5;d7=d8*d8;d8=d7*d7;d6=d8*d8}else{d8=+R(+d4,+dp);d6=d8+ +R(+d5,+dp)}if(d6==0.0){break L10555}d9=+h[d2+24>>3]/d6;ea=1.0/d6}else{d5=(+h[d2+16>>3]-c$)/dw;d8=+P(+(d3/dx));d3=+P(+d5);do{if(d8>d3){eb=d8*+Q(+(d3*d3/(d8*d8)+1.0))}else{if(d5==0.0){eb=0.0;break}eb=d3*+Q(+(d8*d8/(d3*d3)+1.0))}}while(0);do{if((br|0)==3){ec=+Z(+(eb*(-0.0-eb)))}else if((br|0)==5){ec=1.0/(eb*eb+1.0)}else if((br|0)==4){ec=+Z(+(-0.0-eb))}else if((br|0)==6){ec=eb<1.0?1.0:0.0}else if((br|0)==7){if(eb>=1.0){ec=0.0;break}ec=(1.0- +S(+(eb*6.283185307179586)))*.5}else{ec=0.0}}while(0);d9=ec*+h[d2+24>>3];ea=ec}d3=dv+d9;d8=dy+ea;ba=bb+1|0;if((ba|0)<(bc|0)){dy=d8;dv=d3;bb=ba;d2=d2+64|0}else{ed=d8;ee=d3;break}}}else{ed=cX;ee=cY}bb=c[bd>>2]|0;if((bb|0)==0){d0=ee;d1=ed;break L10552}else{cX=ed;cY=ee;bd=bb}}c[bJ>>2]=2;d0=+h[d2+24>>3];d1=1.0}}while(0);c[bJ>>2]=0;h[bg+8>>3]=dq;h[bg+16>>3]=c$;br=c[200]|0;if(dq<+h[64664+(br*688&-1)>>3]){if((c[64648+(br*688&-1)>>2]&1|0)==0){au=7785}else{au=7779}}else{au=7779}do{if((au|0)==7779){au=0;if(dq>+h[64672+(br*688&-1)>>3]){if((c[64648+(br*688&-1)>>2]&2|0)==0){au=7785;break}}bd=c[144]|0;if(c$<+h[64664+(bd*688&-1)>>3]){if((c[64648+(bd*688&-1)>>2]&1|0)==0){au=7785;break}}if(c$<=+h[64672+(bd*688&-1)>>3]){ef=0;break}if((c[64648+(bd*688&-1)>>2]&2|0)==0){au=7785}else{ef=0}}}while(0);if((au|0)==7785){au=0;c[bJ>>2]=1;ef=1}do{if((c[11752]|0)==2){eg=d0}else{if((a[47016]&1)!=0){eg=d0;break}eg=d0/d1}}while(0);br=c[34]|0;L10616:do{if((br|0)!=99){if(!(eg>-8.988465674311579e+307&eg<8.988465674311579e+307)){c[bJ>>2]=2;break}do{if((a[64788+(br*688&-1)|0]&1)==0){h[bg+24>>3]=eg}else{if(eg<0.0){c[bJ>>2]=2;break L10616}if(eg==0.0){h[bg+24>>3]=-8.988465674311579e+307;c[bJ>>2]=1;break L10616}else{cY=+_(+eg);h[bg+24>>3]=cY/+h[64800+(br*688&-1)>>3];break}}}while(0);if((a[aj]&1)!=0){break}if((ef|0)!=0|(br|0)<0){break}bd=64712+(br*688&-1)|0;if(eg<+h[bd>>3]){h[bd>>3]=eg}bd=64664+(br*688&-1)|0;do{if(eg<+h[bd>>3]){if((c[64648+(br*688&-1)>>2]&1|0)==0){c[bJ>>2]=1;break L10616}if((c[64728+(br*688&-1)>>2]&1|0)==0){h[bd>>3]=eg;break}cY=+h[64736+(br*688&-1)>>3];if(cY>eg){h[bd>>3]=cY;c[bJ>>2]=1;break L10616}else{h[bd>>3]=eg;break}}}while(0);bd=64720+(br*688&-1)|0;if(eg>+h[bd>>3]){h[bd>>3]=eg}bd=64672+(br*688&-1)|0;if(eg<=+h[bd>>3]){break}if((c[64648+(br*688&-1)>>2]&2|0)==0){c[bJ>>2]=1;break}if((c[64732+(br*688&-1)>>2]&2|0)==0){h[bd>>3]=eg;break}cY=+h[64760+(br*688&-1)>>3];if(cY<eg){h[bd>>3]=cY;c[bJ>>2]=1;break}else{h[bd>>3]=eg;break}}}while(0);if((a[bl]&1)!=0){au=7822;break L9498}br=c[bJ>>2]|0;L10663:do{if(eg>-8.988465674311579e+307&eg<8.988465674311579e+307){do{if((a[66852]&1)==0){h[bg+32>>3]=eg}else{if(eg<0.0){break L10663}if(eg==0.0){h[bg+32>>3]=-8.988465674311579e+307;break L10663}else{cY=+_(+eg);h[bg+32>>3]=cY/+h[8358];break}}}while(0);if(!((a[aj]&1)==0&(br|0)==0)){break}if(eg<+h[8347]){h[8347]=eg}do{if(eg<+h[8341]){if((c[16678]&1|0)==0){break L10663}if((c[16698]&1|0)==0){h[8341]=eg;break}cY=+h[8350];if(cY>eg){h[8341]=cY;break L10663}else{h[8341]=eg;break}}}while(0);if(eg>+h[8348]){h[8348]=eg}if(eg<=+h[8342]){break}if((c[16678]&2|0)==0){break}if((c[16699]&2|0)==0){h[8342]=eg;break}cY=+h[8353];if(cY<eg){h[8342]=cY;break}else{h[8342]=eg;break}}}while(0);br=aS+1|0;bJ=c[11732]|0;if((br|0)<(bJ|0)){c$=cH+c$;bg=bg+64|0;aS=br}else{eh=bJ;break}}}else{eh=bE}aS=bf+1|0;if((aS|0)<(c[11756]|0)){dq=cZ+dq;bf=aS;ai=eh}else{break}}}uu(dZ);if(al){break}else{ei=aQ}while(1){ai=c[ei>>2]|0;bf=c[ei+12>>2]|0;if((bf|0)!=0){uu(bf)}uu(ei);if((ai|0)==0){break}else{ei=ai}}}}while(0);aj=c[200]|0;do{if(+h[64664+(aj*688&-1)>>3]==8.988465674311579e+307){au=7861}else{if(+h[64672+(aj*688&-1)>>3]==-8.988465674311579e+307){au=7861;break}bl=c[144]|0;if(+h[64664+(bl*688&-1)>>3]==8.988465674311579e+307){au=7861;break}if(+h[64672+(bl*688&-1)>>3]==-8.988465674311579e+307){au=7861;break}bl=c[34]|0;if(+h[64664+(bl*688&-1)>>3]==8.988465674311579e+307){au=7861;break}if(+h[64672+(bl*688&-1)>>3]==-8.988465674311579e+307){au=7861}}}while(0);if((au|0)==7861){au=0;uh(-1,105912,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}aj=c[ax>>2]|0;do{if((aj|0)<2){c[bu>>2]=0}else{if((c[bu>>2]|0)==0){break}if((a[38984]&1)!=0){break}bl=c[aw>>2]|0;aQ=c[bl+8>>2]|0;if((aQ|0)>0){al=0;ai=0;while(1){ej=hu(aj)|0;c[ej+8>>2]=aj;bf=c[aw>>2]|0;if((bf|0)!=0){bx=ej+12|0;aR=0;by=bf;while(1){uD((c[bx>>2]|0)+(aR<<6)|0,(c[by+12>>2]|0)+(ai<<6)|0,64);bf=c[by>>2]|0;if((bf|0)==0){break}else{aR=aR+1|0;by=bf}}}c[ej>>2]=al;by=ai+1|0;if((by|0)<(aQ|0)){al=ej;ai=by}else{break}}ek=ej;el=c[aw>>2]|0}else{ek=0;el=bl}ai=el;do{em=ai|0;ai=c[em>>2]|0;}while((ai|0)!=0);c[em>>2]=ek}}while(0);c[cF>>2]=c[13898];aw=c[7774]|0;if((aw|0)==0){en=0}else{en=c[aw+32>>2]|0}c[ag+248>>2]=en;if((c[ax>>2]|0)==0){c[ag+8>>2]=4}if((ag|0)!=(a$|0)){aw=ag+264|0;uB(aw|0,bo|0)}eo=ak+1|0;eq=ag|0;if((cI|0)==-1){break}aw=c[eq>>2]|0;do{if((aw|0)==0){uE(f|0,0,24);aj=ut(272)|0;if((aj|0)==0){gk();bu=ut(272)|0;if((bu|0)==0){au=7887;break L9498}else{er=bu}}else{er=aj}aj=er;uE(er|0,0,272);c[er+28>>2]=-2;bu=er+32|0;c[bu>>2]=0;c[bu+4>>2]=0;h[er+40>>3]=1.0;h[er+48>>3]=-2.0;bu=er+56|0;c[bu>>2]=c[f>>2];c[bu+4>>2]=c[f+4>>2];c[bu+8>>2]=c[f+8>>2];c[bu+12>>2]=c[f+12>>2];c[bu+16>>2]=c[f+16>>2];c[bu+20>>2]=c[f+20>>2];c[er+80>>2]=-1;c[er+84>>2]=0;bu=er+88|0;c[bu>>2]=c[12872];c[bu+4>>2]=c[12873];c[bu+8>>2]=c[12874];c[bu+12>>2]=c[12875];c[bu+16>>2]=c[12876];c[bu+20>>2]=c[12877];c[bu+24>>2]=c[12878];c[bu+28>>2]=c[12879];c[bu+32>>2]=c[12880];c[bu+36>>2]=c[12881];c[bu+40>>2]=c[12882];c[bu+44>>2]=c[12883];c[bu+48>>2]=c[12884];c[bu+52>>2]=c[12885];c[er+144>>2]=1;h[er+152>>3]=0.0;c[er+160>>2]=0;h[er+168>>3]=15.0;h[er+176>>3]=90.0;c[er+184>>2]=0;c[eq>>2]=aj;es=aj}else{aj=aw+16|0;bu=c[aj>>2]|0;if((bu|0)==0){es=aw;break}uu(bu);c[aj>>2]=0;es=aw}}while(0);c[es+8>>2]=3;aw=c[7774]|0;if((aw|0)==0){et=0}else{et=c[aw+32>>2]|0}c[es+248>>2]=et;aw=c[ah>>2]|0;c[es+12>>2]=aw;ax=es+24|0;c[ax>>2]=c[aP>>2];c[ax+4>>2]=c[aP+4>>2];c[ax+8>>2]=c[aP+8>>2];c[ax+12>>2]=c[aP+12>>2];c[ax+16>>2]=c[aP+16>>2];c[ax+20>>2]=c[aP+20>>2];c[ax+24>>2]=c[aP+24>>2];c[ax+28>>2]=c[aP+28>>2];c[ax+32>>2]=c[aP+32>>2];c[ax+36>>2]=c[aP+36>>2];c[ax+40>>2]=c[aP+40>>2];c[ax+44>>2]=c[aP+44>>2];c[ax+48>>2]=c[aP+48>>2];c[ax+52>>2]=c[aP+52>>2];if((aw|0)==368){uE(r|0,0,28);aw=ut(192)|0;if((aw|0)==0){gk();ax=ut(192)|0;if((ax|0)==0){au=7894;break L9498}else{eu=ax}}else{eu=aw}c[eu>>2]=0;c[eu+4>>2]=-1;c[eu+72>>2]=0;c[eu+88>>2]=0;uE(eu+8|0,0,60);c[eu+96>>2]=1;c[eu+144>>2]=4;c[eu+148>>2]=4;c[eu+152>>2]=4;aw=eu+156|0;c[aw>>2]=c[r>>2];c[aw+4>>2]=c[r+4>>2];c[aw+8>>2]=c[r+8>>2];c[aw+12>>2]=c[r+12>>2];c[aw+16>>2]=c[r+16>>2];c[aw+20>>2]=c[r+20>>2];c[aw+24>>2]=c[r+24>>2];a[eu+184|0]=0;aw=es+224|0;c[aw>>2]=eu;uD(eu|0,c[aT>>2]|0,192);c[c[aw>>2]>>2]=0}ak=eo;ag=c[eq>>2]|0}ez();ev=eq;ew=ag;ex=eo}else{c[a$+4>>2]=c[13898];ak=c[7774]|0;if((ak|0)==0){eA=0}else{eA=c[ak+32>>2]|0}c[a$+248>>2]=eA;ev=a$|0;ew=a$;ex=a0}ak=c[7774]|0;if((ak|0)==0){ay=ev;az=aK;aA=aX;aB=aZ;aC=0;aD=cA;aE=cB;aF=a_;aG=cs;aH=ct;aJ=ex;break}if((a[ak+38|0]&1)==0){ay=ev;az=aK;aA=aX;aB=aZ;aC=0;aD=cA;aE=cB;aF=a_;aG=cs;aH=ct;aJ=ex;break}c[ew+8>>2]=4;ay=ev;az=aK;aA=aX;aB=aZ;aC=0;aD=cA;aE=cB;aF=a_;aG=cs;aH=ct;aJ=ex}}while(0);do{if((aF|0)==0){if(ix(c[7774]|0)|0){c[13898]=az;eB=0;eD=az;break}p=c[7774]|0;if((p|0)!=0){af=p;while(1){p=c[af>>2]|0;uu(c[af+12>>2]|0);uu(af);if((p|0)==0){break}else{af=p}}}c[7774]=0;af=c[13898]|0;if((c[8272]|0)<=(af|0)){au=7924;break L9498}ag=c[1054]|0;if((a[ag+(af*40&-1)|0]&1)==0){au=7924;break L9498}p=c[ag+(af*40&-1)+36>>2]|0;ak=ag+(af*40&-1)+32|0;ag=c[10036]|0;aT=0;while(1){if((aT|0)>=(p|0)){break}if((a[ag+((c[ak>>2]|0)+aT|0)|0]|0)==(a[aT+148464|0]|0)){aT=aT+1|0}else{au=7924;break L9498}}if((aT|0)!=1){au=7924;break L9498}c[13898]=af+1;c[7774]=iw()|0;eB=0;eD=c[13898]|0}else{ak=c[13898]|0;if((c[8272]|0)<=(ak|0)){eE=az;eF=aA;eG=aB;eH=aC;eI=aF;eJ=aJ;au=7925;break L9498}ag=c[1054]|0;if((a[ag+(ak*40&-1)|0]&1)==0){eE=az;eF=aA;eG=aB;eH=aC;eI=aF;eJ=aJ;au=7925;break L9498}p=c[ag+(ak*40&-1)+36>>2]|0;aP=ag+(ak*40&-1)+32|0;ag=c[10036]|0;ah=0;while(1){if((ah|0)>=(p|0)){break}if((a[ag+((c[aP>>2]|0)+ah|0)|0]|0)==(a[ah+148464|0]|0)){ah=ah+1|0}else{eE=az;eF=aA;eG=aB;eH=aC;eI=aF;eJ=aJ;au=7925;break L9498}}if((ah|0)!=1){eE=az;eF=aA;eG=aB;eH=aC;eI=aF;eJ=aJ;au=7925;break L9498}aP=ak+1|0;c[13898]=aP;eB=aF;eD=aP}}while(0);if((eD|0)<(c[8272]|0)){N=aJ;O=aH;U=aG;V=eB;W=aE;X=aD;Y=aC;$=aB;ab=aA;ac=az;ad=ay;ae=eD}else{an=aC;ao=az;ap=aJ;aq=eB;ar=aB;as=aA;at=eD;au=6983;break}}do{if((au|0)==6983){if((ap|0)==0){q=at}else{eE=ao;eF=as;eG=ar;eH=an;eI=aq;eJ=ap;au=7925;break}uf(q,105216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==8108){eK=c[13898]|0;uf(eK,205152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==8109){uf(bQ,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==8110){uf(bQ,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==8111){uf(bQ,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==8112){uf(bQ,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==8113){uf(bQ,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==8114){uf(bQ,163568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==8116){i=b;return}else if((au|0)==6999){uf(c[13898]|0,92272,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7012){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=101304,v)|0)}else if((au|0)==7020){uf(c[13898]|0,84448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7022){uf(c[13898]|0,76512,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7088){uf(c[13898]|0,216936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7109){c[13898]=bR;au=7110}else if((au|0)==7167){uf(b8,143640,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7220){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=201712,v)|0)}else if((au|0)==7325){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=151256,v)|0)}else if((au|0)==7329){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=149424,v)|0)}else if((au|0)==7332){uf(cE,123192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7335){uf(cE,121944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7380){uf(c[cF>>2]|0,114088,(v=i,i=i+8|0,c[v>>2]=c[11900],v)|0)}else if((au|0)==7385){uf(c[cF>>2]|0,110664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7391){uf(c[cF>>2]|0,110664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7396){uf(-1,107856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7398){uf(c[cF>>2]|0,106920,(v=i,i=i+8|0,c[v>>2]=c[11900],v)|0)}else if((au|0)==7709){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=104328,v)|0)}else if((au|0)==7712){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=103864,v)|0)}else if((au|0)==7736){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=103496,v)|0)}else if((au|0)==7822){uf(-1,104872,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==7887){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=101304,v)|0)}else if((au|0)==7894){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=201712,v)|0)}else if((au|0)==7924){eD=a[32936]|0;if(aB){eN=eD&1;eO=eD;eP=az;eQ=aA;eR=aC;eS=0;eT=aJ;au=7928}else{eU=aJ}}}while(0);if((au|0)==7110){c[a4>>2]=0;eK=c[13898]|0;uf(eK,205152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((au|0)==7925){eK=a[32936]|0;a4=eK&1;if(a4<<24>>24==0|(eI|0)==0){if(eG){eN=a4;eO=eK;eP=eE;eQ=eF;eR=eH;eS=eI;eT=eJ;au=7928;break}else{eU=eJ;break}}else{uf(-1,147440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((au|0)==7928){eJ=eN<<24>>24!=0;eI=eJ?9:2;eH=eJ?10:1;if(eJ){eV=eO}else{dc(2,145296);dc(1,143440);eV=a[32936]|0}do{if(!((eV&1)==0|eQ)){eJ=c[16506]|0;if((eJ&1|0)!=0){h[8255]=8.988465674311579e+307}if((eJ&2|0)!=0){h[8256]=-8.988465674311579e+307}eJ=c[16334]|0;if((eJ&1|0)!=0){h[8169]=8.988465674311579e+307}if((eJ&2|0)==0){break}h[8170]=-8.988465674311579e+307}}while(0);eg=+h[64664+(eI*688&-1)>>3];eJ=(a[64788+(eI*688&-1)|0]&1)==0;do{if(eJ){eW=+h[64672+(eI*688&-1)>>3];eX=eg}else{if(eg<=0.0){uk(210984,(v=i,i=i+24|0,c[v>>2]=141960,c[v+8>>2]=56832+(eI*24&-1),h[v+16>>3]=eg,v)|0)}d1=+_(+eg);d0=d1/+h[64800+(eI*688&-1)>>3];d1=+h[64672+(eI*688&-1)>>3];if(eJ){eW=d1;eX=d0;break}if(d1>0.0){ee=+_(+d1);eW=ee/+h[64800+(eI*688&-1)>>3];eX=d0;break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=141960,c[v+8>>2]=56832+(eI*24&-1),h[v+16>>3]=d1,v)|0)}}}while(0);eg=+h[64664+(eH*688&-1)>>3];eJ=(a[64788+(eH*688&-1)|0]&1)==0;do{if(eJ){eY=+h[64672+(eH*688&-1)>>3];eZ=eg}else{if(eg<=0.0){uk(210984,(v=i,i=i+24|0,c[v>>2]=140304,c[v+8>>2]=56832+(eH*24&-1),h[v+16>>3]=eg,v)|0)}d1=+_(+eg);d0=d1/+h[64800+(eH*688&-1)>>3];d1=+h[64672+(eH*688&-1)>>3];if(eJ){eY=d1;eZ=d0;break}if(d1>0.0){ee=+_(+d1);eY=ee/+h[64800+(eH*688&-1)>>3];eZ=d0;break}else{uk(210984,(v=i,i=i+24|0,c[v>>2]=140304,c[v+8>>2]=56832+(eH*24&-1),h[v+16>>3]=d1,v)|0)}}}while(0);if((c[6352]|0)<2|(c[6350]|0)<2|(c[9344]|0)<2|(c[9342]|0)<2){uf(-1,138240,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}eJ=c[10828]|0;c[13898]=o;c[7774]=iw()|0;eF=(a[38984]&1)==0;eg=eW-eX;d1=eY-eZ;eE=c[9342]|0;d0=d1/+((eF?c[6350]|0:eE)-1|0);eG=c[9344]|0;ee=eg/+((eF?c[6352]|0:eG)-1|0);ed=eg/+(eG-1|0);eg=d1/+(eE-1|0);eE=eP;eG=eJ;eJ=eR;eF=eS;L10882:while(1){eK=(eF|0)==0;a4=eE;aJ=eJ;L10884:while(1){aC=c[13898]|0;if(eK){e_=aJ?a4:aC}else{e_=a4}if((t9(aC)|0)==0){au=7968;break}dT();aC=c[13898]|0;if((c[8272]|0)<=(aC|0)){a4=e_;aJ=1;continue}aA=c[1054]|0;if((a[aA+(aC*40&-1)|0]&1)==0){a4=e_;aJ=1;continue}az=c[aA+(aC*40&-1)+36>>2]|0;aB=aA+(aC*40&-1)+32|0;aA=c[10036]|0;cF=0;while(1){if((cF|0)>=(az|0)){break}if((a[aA+((c[aB>>2]|0)+cF|0)|0]|0)==(a[cF+148464|0]|0)){cF=cF+1|0}else{a4=e_;aJ=1;continue L10884}}if((cF|0)==1){e$=eG;e0=aJ;e1=eF;e2=aC;break}else{a4=e_;aJ=1}}L10896:do{if((au|0)==7968){au=0;e3=c[13898]|0;if((e3|0)>=(c[8272]|0)){au=7975;break L10882}aJ=c[1054]|0;L10899:do{if((a[aJ+(e3*40&-1)|0]&1)==0){e4=0}else{a4=c[aJ+(e3*40&-1)+36>>2]|0;eK=aJ+(e3*40&-1)+32|0;ak=c[10036]|0;ah=0;while(1){if((ah|0)>=(a4|0)){break}if((a[ak+((c[eK>>2]|0)+ah|0)|0]|0)==(a[ah+103664|0]|0)){ah=ah+1|0}else{e4=0;break L10899}}e4=(ah|0)==1}}while(0);if(e4|(eG|0)==0){au=7975;break L10882}c[11670]=31112;do{if((iu(l)|0)==0){c[n>>2]=c[eG+260>>2];if((a[32936]&1)==0){e5=eF}else{e5=(eF+2|0)%3&-1}c[7780]=c[l>>2];aJ=eG+12|0;if((c[aJ>>2]|0)==352|(c[7662]|0)==1){e8=1}else{e8=(a[eG+56|0]&1)!=0}i1(e5,0,n,eH,eZ,eg,c[9342]|0,eI,eX,ee,(a[38984]&1)!=0?c[9344]|0:c[6352]|0,e8);if((a[38984]&1)!=0){e9=e5;break}if((c[aJ>>2]|0)==352|(c[7662]|0)==1){fa=1}else{fa=(a[eG+56|0]&1)!=0}i1(e5,1,n,eI,eX,ed,c[9344]|0,eH,eZ,d0,c[6350]|0,fa);e9=e5}else{e9=eF}}while(0);aJ=c[eG+4>>2]|0;c[13898]=aJ;aC=c[eG+248>>2]|0;cF=eG;while(1){eK=c[cF>>2]|0;if((eK|0)==0){e$=0;e0=0;e1=e9;e2=aJ;break L10896}if((c[eK+4>>2]|0)!=(aJ|0)){e$=eK;e0=0;e1=e9;e2=aJ;break L10896}if((c[eK+248>>2]|0)==(aC|0)){cF=eK}else{e$=eK;e0=0;e1=e9;e2=aJ;break}}}}while(0);aJ=(e1|0)==0;if(aJ){if(ix(c[7774]|0)|0){c[13898]=e_;eE=e_;eG=e$;eJ=e0;eF=0;continue}cF=c[7774]|0;if((cF|0)!=0){aC=cF;while(1){cF=c[aC>>2]|0;uu(c[aC+12>>2]|0);uu(aC);if((cF|0)==0){break}else{aC=cF}}}c[7774]=0;fb=c[13898]|0}else{fb=e2}if((c[8272]|0)<=(fb|0)){break}aC=c[1054]|0;if((a[aC+(fb*40&-1)|0]&1)==0){break}cF=c[aC+(fb*40&-1)+36>>2]|0;eK=aC+(fb*40&-1)+32|0;aC=c[10036]|0;ak=0;while(1){if((ak|0)>=(cF|0)){break}if((a[aC+((c[eK>>2]|0)+ak|0)|0]|0)==(a[ak+148464|0]|0)){ak=ak+1|0}else{break L10882}}if((ak|0)!=1){break}c[13898]=fb+1;if(!aJ){eE=e_;eG=e$;eJ=e0;eF=e1;continue}c[7774]=iw()|0;eE=e_;eG=e$;eJ=e0;eF=0}if((au|0)==7975){uh(e3,136552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[32936]&1)==0){eU=eT;break}eF=c[10828]|0;c[d>>2]=eF;L10947:do{if((eT|0)>0){eJ=0;eG=d;eE=0;eH=eF;eI=eT;eK=eT;while(1){if((c[eH+8>>2]|0)==2){aC=eH|0;cF=c[aC>>2]|0;a4=cF|0;aB=c[a4>>2]|0;aA=c[aB+260>>2]|0;az=eI-2|0;if((aA|0)!=0){cE=eH+260|0;b8=cF+260|0;bR=aA;while(1){aA=c[cE>>2]|0;bQ=c[b8>>2]|0;q=c[aA+12>>2]|0;ap=c[bQ+12>>2]|0;aq=c[bR+12>>2]|0;an=bR+8|0;if((c[an>>2]|0)>0){ar=0;do{h[aq+(ar<<6)+8>>3]=+h[q+(ar<<6)+24>>3];h[aq+(ar<<6)+16>>3]=+h[ap+(ar<<6)+24>>3];as=aq+(ar<<6)|0;ao=c[as>>2]|0;at=c[q+(ar<<6)>>2]|0;if(ao>>>0<at>>>0){c[as>>2]=at;fc=at}else{fc=ao}ao=c[ap+(ar<<6)>>2]|0;if(fc>>>0<ao>>>0){c[as>>2]=ao}ar=ar+1|0;}while((ar|0)<(c[an>>2]|0))}an=c[bR>>2]|0;if((an|0)==0){break}else{cE=aA|0;b8=bQ|0;bR=an}}if((c[aC>>2]|0)!=(cF|0)){break}}c[a4>>2]=eE;c[eG>>2]=aB;fd=eH;fe=aB;ff=az;fg=az}else{fd=eE;fe=eH;ff=eI;fg=eK}bR=fe|0;b8=eJ+1|0;if((b8|0)<(ff|0)){eJ=b8;eG=bR;eE=fd;eH=c[bR>>2]|0;eI=ff;eK=fg}else{fh=bR;fi=fd;fj=fg;break L10947}}uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=125824,v)|0)}else{fh=d;fi=0;fj=eT}}while(0);c[fh>>2]=fi;c[10828]=c[d>>2];eU=fj}}while(0);if((eU|0)==0|(c[10828]|0)==0){uf(c[13898]|0,134776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}dc(2,133200);if((a[66036]&1)!=0){eZ=+h[8255];h[8255]=+h[8256];h[8256]=eZ}do{if((a[66164]&1)!=0){eZ=+h[8255];if(eZ<=0.0){uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}eX=+h[8256];if(eX>0.0){eY=+_(+eZ);eZ=+h[8272];h[8255]=eY/eZ;h[8256]=+_(+eX)/eZ;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}}}while(0);dc(1,131808);if((a[65348]&1)!=0){eZ=+h[8169];h[8169]=+h[8170];h[8170]=eZ}do{if((a[65476]&1)!=0){eZ=+h[8169];if(eZ<=0.0){uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}eX=+h[8170];if(eX>0.0){eY=+_(+eZ);eZ=+h[8186];h[8169]=eY/eZ;h[8170]=+_(+eX)/eZ;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}}}while(0);do{if((c[5094]|0)==0){dc(0,130544)}else{eZ=+h[8083];eX=+h[8084];if(eX-eZ!=0.0){break}fj=c[16162]|0;if((fj|0)==0){uf(-1,197584,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}if(eX==0.0){fk=1.0}else{fk=+P(+eX)*.01}if((fj&1|0)!=0){h[8083]=eZ-fk}if((fj&2|0)==0){break}h[8084]=fk+eX}}while(0);if((a[64660]&1)!=0){fk=+h[8083];h[8083]=+h[8084];h[8084]=fk}do{if((a[64788]&1)!=0){fk=+h[8083];if(fk<=0.0){uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}eX=+h[8084];if(eX>0.0){eZ=+_(+fk);fk=+h[8100];h[8083]=eZ/fk;h[8084]=+_(+eX)/fk;break}else{uf(-1,218096,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}}}while(0);dd(2,20);dd(1,20);dd(0,20);i6(eU,2);if(a[31104]|0){dk();dc(3,129504);dd(3,20)}do{if((c[16508]&1|0)!=0){fj=c[16506]|0;if((fj&1|0)!=0){h[8257]=+h[8255]}if((fj&2|0)==0){break}h[8258]=+h[8256]}}while(0);fj=c[10828]|0;if((fj|0)==0){uf(c[13898]|0,134776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[11690]|0)!=0&(eU|0)>0){d=fj;fj=0;while(1){fi=d+256|0;fh=c[fi>>2]|0;if((fh|0)!=0){eT=fh;while(1){fh=c[eT>>2]|0;uu(c[eT+4>>2]|0);uu(eT);if((fh|0)==0){break}else{eT=fh}}c[fi>>2]=0}eT=c[d+12>>2]|0;do{if(!((eT|0)==368|(eT|0)==225|(eT|0)==400|(eT|0)==416|(eT|0)==432)){if((a[d+237|0]&1)!=0){break}if((c[d+244>>2]|0)==0){uh(-1,128216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);break}if((c[d+8>>2]|0)==3){c[fi>>2]=ep(c[d+252>>2]|0,c[d+260>>2]|0)|0;break}else{c[fi>>2]=ep(c[9342]|0,c[d+260>>2]|0)|0;break}}}while(0);fi=fj+1|0;if((fi|0)<(eU|0)){d=c[d>>2]|0;fj=fi}else{break}}}fj=c[7738]|0;L11058:do{if((fj|0)!=-1){d=(c[13898]|0)-1|0;fi=c[1054]|0;eT=(c[fi+(d*40&-1)+36>>2]|0)+(c[fi+(d*40&-1)+32>>2]|0)|0;d=db(c[6924]|0,(eT+1|0)-(c[fi+(fj*40&-1)+32>>2]|0)|0,116456)|0;c[6924]=d;fi=c[(c[1054]|0)+(fj*40&-1)+32>>2]|0;L11060:do{if((fi|0)<(eT|0)){fh=fi;fg=d;while(1){fd=a[(c[10036]|0)+fh|0]|0;if(fd<<24>>24==0){fl=fg;break L11060}ff=fg+1|0;a[fg]=fd;fd=fh+1|0;if((fd|0)<(eT|0)){fh=fd;fg=ff}else{fl=ff;break}}}else{fl=d}}while(0);a[fl]=0;c[7738]=-1;d=c[6924]|0;eT=e6(126928)|0;if((eT|0)==0){break}fi=eT+8|0;do{if((a[fi]&1)==0){fg=c[eT+24>>2]|0;if((aY(fg|0,d|0)|0)==0){break L11058}fh=eT+16|0;if((c[fh>>2]|0)!=3){break}uu(fg);c[fh>>2]=1}else{a[fi]=0}}while(0);if((d|0)==0){fm=0}else{fm=bP(d|0)|0}c[eT+16>>2]=3;c[eT+24>>2]=fm}}while(0);c[7952]=eU;if((a[14112]&1)!=0){li(eU);i=b;return}fv(c[10828]|0,eU,0);fm=0;do{if((c[64656+(fm*688&-1)>>2]&1|0)!=0){fk=+h[64664+(fm*688&-1)>>3];if((a[64788+(fm*688&-1)|0]&1)==0){h[64696+(fm*688&-1)>>3]=fk;fn=+h[64672+(fm*688&-1)>>3]}else{eX=+h[64800+(fm*688&-1)>>3];h[64696+(fm*688&-1)>>3]=+Z(+(fk*eX));fn=+Z(+(+h[64672+(fm*688&-1)>>3]*eX))}h[64704+(fm*688&-1)>>3]=fn}fm=fm+1|0;}while(fm>>>0<11);e7(1);c[6930]=eU;c[6928]=3;i=b;return}function i$(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0.0,u=0,w=0,x=0.0;e=i;if((d|0)>0){f=0;g=b;while(1){b=c[g+12>>2]|0;do{if((b|0)==400|(b|0)==416|(b|0)==432){if((c[16507]|0)==0){break}f3(g,1)}else{j=c[g+260>>2]|0;if((j|0)==0){break}k=g+23|0;l=j;do{j=l+8|0;if((c[j>>2]|0)>0){m=l+12|0;n=0;do{o=c[m>>2]|0;p=o+(n<<6)|0;do{if((c[p>>2]|0)!=2){c[p>>2]=0;do{if((a[k]&1)==0){q=c[16507]|0;do{if((q&1|0)!=0){r=+h[o+(n<<6)+8>>3];if(r>=+h[8255]){break}h[8255]=r}}while(0);if((q&2|0)==0){break}r=+h[o+(n<<6)+8>>3];if(r<=+h[8256]){break}h[8256]=r}}while(0);r=+h[8255];s=+h[8256];t=+h[o+(n<<6)+8>>3];if(r<s){if(t<r|t>s){u=8137}}else{if(t<s|t>r){u=8137}}if((u|0)==8137){u=0;c[p>>2]=1;break}do{if((a[k]&1)==0){w=c[16335]|0;do{if((w&1|0)!=0){r=+h[o+(n<<6)+16>>3];if(r>=+h[8169]){break}h[8169]=r}}while(0);if((w&2|0)==0){break}r=+h[o+(n<<6)+16>>3];if(r<=+h[8170]){break}h[8170]=r}}while(0);r=+h[8169];t=+h[8170];s=+h[o+(n<<6)+16>>3];if(r<t){if(s<r|s>t){u=8148}}else{if(s<t|s>r){u=8148}}if((u|0)==8148){u=0;c[p>>2]=1;break}do{if((a[k]&1)==0){q=c[16163]|0;do{if((q&1|0)!=0){r=+h[o+(n<<6)+24>>3];if(r>=+h[8083]){break}h[8083]=r}}while(0);if((q&2|0)==0){break}r=+h[o+(n<<6)+24>>3];if(r<=+h[8084]){break}h[8084]=r}}while(0);r=+h[8083];s=+h[8084];t=+h[o+(n<<6)+24>>3];if(r<s){if(!(t<r|t>s)){break}}else{if(!(t<s|t>r)){break}}c[p>>2]=1}}while(0);n=n+1|0;}while((n|0)<(c[j>>2]|0))}l=c[l>>2]|0;}while((l|0)!=0)}}while(0);b=f+1|0;if((b|0)<(d|0)){f=b;g=c[g>>2]|0}else{break}}}dc(2,0);dc(1,0);r=+h[8083];t=+h[8084];if(t-r!=0.0){i=e;return}g=c[16162]|0;if((g|0)==0){uf(-1,197584,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}if(t==0.0){x=1.0}else{x=+P(+t)*.01}if((g&1|0)!=0){h[8083]=r-x}if((g&2|0)==0){i=e;return}h[8084]=x+t;i=e;return}function i0(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0;f=i;i=i+24|0;g=f|0;uE(g|0,0,24);j=ut(272)|0;do{if((j|0)==0){gk();k=ut(272)|0;if((k|0)!=0){l=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=101304,v)|0);return 0}else{l=j}}while(0);j=l;uE(l|0,0,272);c[l+28>>2]=-2;k=l+32|0;c[k>>2]=0;c[k+4>>2]=0;h[l+40>>3]=1.0;h[l+48>>3]=-2.0;k=l+56|0;c[k>>2]=c[g>>2];c[k+4>>2]=c[g+4>>2];c[k+8>>2]=c[g+8>>2];c[k+12>>2]=c[g+12>>2];c[k+16>>2]=c[g+16>>2];c[k+20>>2]=c[g+20>>2];c[l+80>>2]=-1;c[l+84>>2]=0;g=l+88|0;c[g>>2]=c[12872];c[g+4>>2]=c[12873];c[g+8>>2]=c[12874];c[g+12>>2]=c[12875];c[g+16>>2]=c[12876];c[g+20>>2]=c[12877];c[g+24>>2]=c[12878];c[g+28>>2]=c[12879];c[g+32>>2]=c[12880];c[g+36>>2]=c[12881];c[g+40>>2]=c[12882];c[g+44>>2]=c[12883];c[g+48>>2]=c[12884];c[g+52>>2]=c[12885];c[l+144>>2]=1;h[l+152>>3]=0.0;c[l+160>>2]=0;h[l+168>>3]=15.0;h[l+176>>3]=90.0;c[l+184>>2]=0;if(!((e|0)>0&(a|0)>0)){i=f;return j|0}if((b|0)>0){g=l+260|0;k=0;do{m=hu(d)|0;c[m>>2]=c[g>>2];c[g>>2]=m;k=k+1|0;}while((k|0)<(b|0))}b=l+260|0;l=0;do{k=hu(a)|0;c[k>>2]=c[b>>2];c[b>>2]=k;l=l+1|0;}while((l|0)<(e|0));i=f;return j|0}function i1(b,d,e,f,g,j,k,l,m,n,o,p){b=b|0;d=d|0;e=e|0;f=f|0;g=+g;j=+j;k=k|0;l=l|0;m=+m;n=+n;o=o|0;p=p|0;var q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0.0,X=0.0,Y=0,$=0.0,aa=0.0,ab=0,ac=0.0,ad=0,ae=0.0,af=0,ag=0;q=i;i=i+24|0;r=q|0;if(p){s=(b|0)==0|(a[32936]&1)==0}else{s=0}if((k|0)<=0){i=q;return}p=d&1;t=p^1;u=64788+(f*688&-1)|0;w=31136+(t*24&-1)|0;x=31144+(t*24&-1)|0;y=31152+(t*24&-1)|0;t=(o|0)>0;z=64788+(l*688&-1)|0;A=31136+(p*24&-1)|0;B=31144+(p*24&-1)|0;C=31152+(p*24&-1)|0;p=r|0;D=r+8|0;E=D;F=(b|0)==99;G=64788+(b*688&-1)|0;H=(b|0)<0;I=64712+(b*688&-1)|0;J=64664+(b*688&-1)|0;K=64648+(b*688&-1)|0;L=64728+(b*688&-1)|0;M=64736+(b*688&-1)|0;N=64720+(b*688&-1)|0;O=64672+(b*688&-1)|0;Q=64732+(b*688&-1)|0;R=64760+(b*688&-1)|0;S=64800+(b*688&-1)|0;b=r+8|0;T=D;D=r+16|0;U=64800+(l*688&-1)|0;l=64800+(f*688&-1)|0;f=c[(c[e>>2]|0)+12>>2]|0;V=0;L11194:while(1){W=+(V|0)*j+g;if((a[u]&1)==0){X=W}else{X=+Z(+(W*+h[l>>3]))}c[w>>2]=2;h[x>>3]=X;h[y>>3]=0.0;if(t){Y=0;do{$=+(Y|0)*n+m;if((a[z]&1)==0){aa=$}else{aa=+Z(+($*+h[U>>3]))}c[A>>2]=2;h[B>>3]=aa;h[C>>3]=0.0;ab=f+(Y<<6)+8|0;if(d){h[ab>>3]=W;h[f+(Y<<6)+16>>3]=$}else{h[ab>>3]=$;h[f+(Y<<6)+16>>3]=W}e4(c[7780]|0,r);L11209:do{if((a[1960]&1)==0){ab=c[p>>2]|0;if((ab|0)==2){ac=+h[D>>3]}else if((ab|0)==3){ad=8203;break L11194}else if((ab|0)==1){ac=0.0}else{ad=8204;break L11194}$=+P(+ac);if($>+h[11]){ad=8206;break}if((ab|0)==1){ae=+(c[E>>2]|0)}else if((ab|0)==2){ae=+h[b>>3]}else if((ab|0)==3){ae=+uz(c[T>>2]|0,0)}else{ad=8211;break L11194}ab=f+(Y<<6)|0;c[ab>>2]=0;L11220:do{if(!F){if(!(ae>-8.988465674311579e+307&ae<8.988465674311579e+307)){c[ab>>2]=2;break}do{if((a[G]&1)==0){h[f+(Y<<6)+24>>3]=ae}else{if(ae<0.0){c[ab>>2]=2;break L11220}if(ae==0.0){h[f+(Y<<6)+24>>3]=-8.988465674311579e+307;c[ab>>2]=1;break L11220}else{$=+_(+ae);h[f+(Y<<6)+24>>3]=$/+h[S>>3];break}}}while(0);if(H){break}if(ae<+h[I>>3]){h[I>>3]=ae}do{if(ae<+h[J>>3]){if((c[K>>2]&1|0)==0){c[ab>>2]=1;break L11220}if((c[L>>2]&1|0)==0){h[J>>3]=ae;break}$=+h[M>>3];if($>ae){h[J>>3]=$;c[ab>>2]=1;break L11220}else{h[J>>3]=ae;break}}}while(0);if(ae>+h[N>>3]){h[N>>3]=ae}if(ae<=+h[O>>3]){break}if((c[K>>2]&2|0)==0){c[ab>>2]=1;break}if((c[Q>>2]&2|0)==0){h[O>>3]=ae;break}$=+h[R>>3];if($<ae){h[O>>3]=$;c[ab>>2]=1;break}else{h[O>>3]=ae;break}}}while(0);if(!s){break}af=c[ab>>2]|0;if(!(ae>-8.988465674311579e+307&ae<8.988465674311579e+307)){break}do{if((a[66852]&1)==0){h[f+(Y<<6)+32>>3]=ae}else{if(ae<0.0){break L11209}if(ae==0.0){h[f+(Y<<6)+32>>3]=-8.988465674311579e+307;break L11209}else{$=+_(+ae);h[f+(Y<<6)+32>>3]=$/+h[8358];break}}}while(0);if((af|0)!=0){break}if(ae<+h[8347]){h[8347]=ae}do{if(ae<+h[8341]){if((c[16678]&1|0)==0){break L11209}if((c[16698]&1|0)==0){h[8341]=ae;break}$=+h[8350];if($>ae){h[8341]=$;break L11209}else{h[8341]=ae;break}}}while(0);if(ae>+h[8348]){h[8348]=ae}if(ae<=+h[8342]){break}if((c[16678]&2|0)==0){break}if((c[16699]&2|0)==0){h[8342]=ae;break}$=+h[8353];if($<ae){h[8342]=$;break}else{h[8342]=ae;break}}else{ad=8206}}while(0);if((ad|0)==8206){ad=0;c[f+(Y<<6)>>2]=2}Y=Y+1|0;}while((Y|0)<(o|0))}c[(c[e>>2]|0)+8>>2]=o;Y=c[c[e>>2]>>2]|0;c[e>>2]=Y;if((Y|0)==0){ag=0}else{ag=c[Y+12>>2]|0}Y=V+1|0;if((Y|0)<(k|0)){f=ag;V=Y}else{ad=8276;break}}if((ad|0)==8211){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ad|0)==8204){uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ad|0)==8203){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ad|0)==8276){i=q;return}}function i2(a,b){a=a|0;b=b|0;var c=0.0,d=0.0,e=0;c=+h[a+8>>3];d=+h[b+8>>3];if(c>d){e=1;return e|0}e=(c<d)<<31>>31;return e|0}function i3(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,v=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0,L=0,M=0,N=0,O=0,P=0;b=i;i=i+32|0;d=b|0;e=b+16|0;if((a[30641]|0)!=100){i=b;return}f=c[12904]|0;g=c[6952]|0;do{if((f|0)>0&(g|0)!=0){j=g+(f*152&-1)|0;if((f|0)!=0){k=64664+((c[200]|0)*688&-1)|0;l=64664+((c[144]|0)*688&-1)|0;m=g;do{n=+h[k>>3];o=+h[91];p=+h[96];q=+h[l>>3];r=+h[21];s=+h[68];t=+h[5279];u=+h[2];v=+h[12];w=+h[405];x=+h[393];y=+h[397];z=+h[401];A=+h[406];B=+h[394];C=+h[398];D=+h[402];E=(+h[m+16>>3]-n)*o+p+-1.0;F=(+h[m+24>>3]-q)*r+s+-1.0;G=(+h[m+32>>3]-t)*u+v+-1.0;H=A+E*B+F*C+G*D;I=(w+E*x+F*y+G*z)/(H==0.0?1.0e-5:H);H=(+h[m+48>>3]-n)*o+p+-1.0;G=(+h[m+56>>3]-q)*r+s+-1.0;F=(+h[m+64>>3]-t)*u+v+-1.0;E=A+H*B+G*C+F*D;J=(w+H*x+G*y+F*z)/(E==0.0?1.0e-5:E);E=J>I?J:I;I=(+h[m+80>>3]-n)*o+p+-1.0;J=(+h[m+88>>3]-q)*r+s+-1.0;F=(+h[m+96>>3]-t)*u+v+-1.0;G=A+I*B+J*C+F*D;H=(w+I*x+J*y+F*z)/(G==0.0?1.0e-5:G);G=H>E?H:E;E=(+h[m+112>>3]-n)*o+p+-1.0;p=(+h[m+120>>3]-q)*r+s+-1.0;s=(+h[m+128>>3]-t)*u+v+-1.0;v=A+E*B+p*C+s*D;D=(w+E*x+p*y+s*z)/(v==0.0?1.0e-5:v);h[m+8>>3]=D>G?D:G;m=m+152|0;}while((m|0)!=(j|0))}bM(g|0,f|0,152,16);j=c[6952]|0;m=c[12904]|0;l=j+(m*152&-1)|0;if((m|0)!=0){m=d|0;k=d+8|0;K=d+4|0;L=e|0;M=e+4|0;N=e+8|0;O=j;do{G=+h[O>>3];do{if((a[54008]&1)==0){j=(c[3524]|0)+144|0;if((c[j>>2]|0)==0){break}c[m>>2]=5;h[k>>3]=G;c[K>>2]=0;cM[c[j>>2]&511](d)}else{j=(c[3524]|0)+144|0;if((c[j>>2]|0)==0){break}c[L>>2]=3;c[M>>2]=~~G;h[N>>3]=0.0;cM[c[j>>2]&511](e)}}while(0);if((c[7661]|0)<0){j=c[O+144>>2]|0;c[7654]=c[j>>2];c[30620>>2]=c[j+4>>2];c[30624>>2]=c[j+8>>2];c[30628>>2]=c[j+12>>2]}dy(O+16|0);O=O+152|0;}while((O|0)!=(l|0))}if((a[30641]|0)==100){P=c[6952]|0;break}else{i=b;return}}else{P=g}}while(0);if((P|0)!=0){uu(P)}c[6952]=0;c[18084]=0;c[12904]=0;i=b;return}function i4(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0,r=0.0,s=0.0,t=0,u=0.0,w=0,x=0,y=0.0,z=0.0,A=0.0,B=0.0,C=0,D=0.0,E=0,F=0,G=0.0,H=0.0,I=0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,P=0.0,Q=0.0,R=0.0,S=0.0,T=0,U=0,V=0,W=0,X=0;g=i;j=d<<2;k=ut(j)|0;do{if((k|0)==0){gk();l=ut(j)|0;if((l|0)!=0){m=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=224016,v)|0)}else{m=k}}while(0);k=m;c[e>>2]=k;L11355:do{if((a[30641]|0)!=97|(b|0)==0){n=0}else{o=0.0;p=0.0;e=b;m=d;L11356:while(1){j=(c[e+8>>2]|0)-1|0;do{if((j|0)<1){q=m;r=p;s=o}else{l=e+12|0;t=0;while(1){if((t|0)>(j|0)){u=o;break}w=c[l>>2]|0;if((c[w+(t<<6)>>2]|0)==2){t=t+1|0}else{x=8314;break}}if((x|0)==8314){x=0;y=(+h[w+(t<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;z=(+h[w+(t<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;A=(0.0- +h[5279])*+h[2]+ +h[12]+-1.0;B=+h[406]+y*+h[394]+z*+h[398]+A*+h[402];u=(+h[405]+y*+h[393]+z*+h[397]+A*+h[401])/(B==0.0?1.0e-5:B)}C=j;while(1){if((C|0)<=(t|0)){D=p;break}E=c[l>>2]|0;if((c[E+(C<<6)>>2]|0)==2){C=C-1|0}else{x=8318;break}}if((x|0)==8318){x=0;B=(+h[E+(C<<6)+8>>3]- +h[64664+((c[200]|0)*688&-1)>>3])*+h[91]+ +h[96]+-1.0;A=(+h[E+(C<<6)+16>>3]- +h[64664+((c[144]|0)*688&-1)>>3])*+h[21]+ +h[68]+-1.0;z=(0.0- +h[5279])*+h[2]+ +h[12]+-1.0;y=+h[406]+B*+h[394]+A*+h[398]+z*+h[402];D=(+h[405]+B*+h[393]+A*+h[397]+z*+h[401])/(y==0.0?1.0e-5:y)}if(+(C-t|0)<=+(j|0)*.1){q=m;r=D;s=u;break}c[f>>2]=D<=u&1;if((m|0)<3){q=m;r=D;s=u;break}F=64664+((c[200]|0)*688&-1)|0;G=+h[91];H=+h[96];I=64664+((c[144]|0)*688&-1)|0;J=+h[21];K=+h[68];y=(0.0- +h[5279])*+h[2]+ +h[12]+-1.0;L=+h[405];M=+h[393];N=+h[397];O=y*+h[401];P=+h[406];Q=+h[394];R=+h[398];S=y*+h[402];l=m;while(1){T=l-2|0;U=e;while(1){V=c[U>>2]|0;if((V|0)!=0&(T|0)!=0){T=T-1|0;U=V}else{break}}L11379:do{if((V|0)!=0){U=c[V+8>>2]|0;if((U|0)==0){break}T=V+12|0;W=t;while(1){if((W|0)>=(U|0)){break L11379}X=c[T>>2]|0;if((c[X+(W<<6)>>2]|0)==2){W=W+1|0}else{break L11356}}}}while(0);T=l-1|0;if((T|0)<3){q=T;r=D;s=u;break}else{l=T}}}}while(0);j=c[e>>2]|0;if((j|0)==0){n=0;break L11355}else{o=s;p=r;e=j;m=q-1|0}}p=(+h[X+(W<<6)+8>>3]- +h[F>>3])*G+H+-1.0;o=(+h[X+(W<<6)+16>>3]- +h[I>>3])*J+K+-1.0;y=P+p*Q+o*R+S;n=(L+p*M+o*N+O)/(y==0.0?1.0e-5:y)<=u&1}}while(0);if((d|0)<=0){i=g;return}I=(n|0)==1;n=0;W=b;b=d;while(1){d=b-1|0;X=a[30641]|0;do{if((X<<24>>24|0)==97){if(I){c[k+(d<<2)>>2]=W;break}else{c[k+(n<<2)>>2]=W;break}}else if((X<<24>>24|0)==102){c[k+(d<<2)>>2]=W}else{c[k+(n<<2)>>2]=W}}while(0);if((d|0)<=0){break}n=n+1|0;W=c[W>>2]|0;b=d}i=g;return}function i5(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0.0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0.0,aS=0,aT=0,aU=0.0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a9=0,ba=0,bb=0.0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0.0,bn=0,bo=0,bp=0,bq=0.0,br=0.0,bs=0.0,bt=0.0,bu=0.0,bv=0.0,bw=0.0,bx=0.0,by=0.0,bz=0.0,bA=0.0,bB=0.0,bC=0.0,bD=0.0,bE=0.0,bF=0.0,bG=0.0,bH=0.0,bI=0.0,bJ=0.0,bK=0.0,bL=0.0,bM=0,bN=0.0,bO=0,bP=0.0,bQ=0.0,bR=0,bS=0,bT=0,bU=0.0,bV=0.0,bW=0.0,bX=0.0,bY=0.0,bZ=0.0,b_=0.0,b$=0.0,b0=0.0,b1=0.0,b2=0.0,b3=0.0,b4=0.0,b5=0,b6=0,b7=0,b8=0,b9=0.0,ca=0.0,cb=0.0,cc=0.0,cd=0.0,ce=0.0,cf=0.0,cg=0.0,ch=0.0,ci=0.0,cj=0.0,ck=0.0,cl=0.0,cm=0.0,cn=0.0,co=0.0,cp=0.0,cq=0.0,cr=0.0,cs=0.0,ct=0.0,cu=0.0,cv=0.0,cw=0.0,cx=0.0,cy=0.0,cz=0.0,cA=0,cB=0.0,cC=0,cD=0;d=i;i=i+224|0;e=d|0;f=d+16|0;g=d+32|0;j=d+48|0;k=d+64|0;l=d+80|0;m=d+88|0;n=d+96|0;o=b+264|0;p=(a[o]|0)==0?30632:o;o=a[p]|0;if(o<<24>>24==0){i=d;return}if((a[30641]|0)==100){q=o;r=8348}else{cM[c[(c[3524]|0)+168>>2]&511](12);o=a[p]|0;if(o<<24>>24!=0){q=o;r=8348}}L11410:do{if((r|0)==8348){o=n;s=b+239|0;t=b+64|0;u=t|0;w=b+72|0;x=b+24|0;y=k;z=k|0;A=k+4|0;B=b+260|0;C=b+252|0;D=g|0;E=g+8|0;F=g+4|0;G=j|0;H=j+4|0;I=j+8|0;J=n|0;K=n|0;L=n+8|0;M=n+32|0;N=n+40|0;R=n+64|0;S=n+72|0;T=n+96|0;U=n+104|0;V=n+16|0;W=n+48|0;X=n+80|0;Y=n+112|0;$=n+24|0;ab=n+56|0;ac=n+88|0;ad=n+120|0;ae=e|0;af=e+8|0;ag=e+4|0;ah=f|0;ai=f+4|0;aj=f+8|0;ak=0;al=q;L11412:while(1){am=al<<24>>24;c[l>>2]=0;an=a[s]|0;if((c[u>>2]|0)==3){ao=+h[w>>3]==-1.0&1}else{ao=0}a[54008]=ao;c[7644]=c[x>>2];c[7645]=c[x+4>>2];c[7646]=c[x+8>>2];c[7647]=c[x+12>>2];c[7648]=c[x+16>>2];c[7649]=c[x+20>>2];c[7650]=c[x+24>>2];c[7651]=c[x+28>>2];c[7652]=c[x+32>>2];c[7653]=c[x+36>>2];c[7654]=c[x+40>>2];c[7655]=c[x+44>>2];c[7656]=c[x+48>>2];c[7657]=c[x+52>>2];ap=c[7661]|0;if((ap|0)>0){lK(30576,ap)}c[y>>2]=c[7654];c[y+4>>2]=c[30620>>2];c[y+8>>2]=c[30624>>2];c[y+12>>2]=c[30628>>2];ap=c[7645]|0;do{if((c[7644]|0)!=0){aq=+h[3825];ar=c[(c[3524]|0)+92>>2]|0;if(aq<0.0){cK[ar&63](+h[3817]);break}else{cK[ar&63](aq);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[3824]);ar=c[(c[3524]|0)+64>>2]|0;if((ap|0)<-5){cM[ar&511](-2)}else{cM[ar&511](ap)}ar=c[3524]|0;do{if((a[30608]&1)==0){if((c[ar+96>>2]&1024|0)!=0){break}c[z>>2]=1;c[A>>2]=ap;r=8363}else{r=8363}}while(0);if((r|0)==8363){r=0;fn(k,ar)}do{if((am|0)==116|(am|0)==115|(am|0)==98){if((c[(c[3524]|0)+148>>2]|0)==0){break}if((am|0)==98){aq=+h[7076];h[Y>>3]=aq;h[X>>3]=aq;h[W>>3]=aq;h[V>>3]=aq}else if((am|0)==116){aq=+h[6915];h[Y>>3]=aq;h[X>>3]=aq;h[W>>3]=aq;h[V>>3]=aq}i4(c[B>>2]|0,c[C>>2]|0,m,l);ap=c[7664]|0;as=c[7665]|0;at=(ap|0)<1;au=(as|0)<1;do{if(at|au){av=(c[C>>2]|0)-1|0;if((av|0)>0){aw=c[m>>2]|0;ax=0;ay=0;az=0;while(1){aA=c[aw+(az<<2)>>2]|0;aB=c[aA+12>>2]|0;aC=c[aA+8>>2]|0;do{if((aC|0)>0){aA=0;aD=0;do{aA=((c[aB+(aD<<6)>>2]|0)==0&1)+aA|0;aD=aD+1|0;}while((aD|0)<(aC|0));if((aA|0)<=0){aE=ay;aF=ax;break}aE=ay+1|0;aF=(ax|0)>(aA|0)?ax:aA}else{aE=ay;aF=ax}}while(0);aC=az+1|0;if((aC|0)<(av|0)){ax=aF;ay=aE;az=aC}else{aG=aF;aH=aE;break}}}else{aG=0;aH=0}if(at){aI=~~(+O(+(+((((ap|0)==0?200:-ap|0)|0)/(aG|0)&-1|0)))+1.0)}else{aI=ap}if(!au){aJ=as;aK=aI;break}aJ=~~(+O(+(+((((as|0)==0?200:-as|0)|0)/(aH|0)&-1|0)))+1.0);aK=aI}else{aJ=as;aK=ap}}while(0);if((a[30641]|0)==100){if(((c[C>>2]|0)-1|0)>0){ap=c[m>>2]|0;as=(a[30640]|0)==0;au=0;while(1){at=au+1|0;az=c[(c[ap+(au<<2)>>2]|0)+8>>2]|0;ay=c[(c[ap+(at<<2)>>2]|0)+8>>2]|0;do{if(as){aL=ay;r=8387}else{if((az|0)==(ay|0)){aL=az;r=8387;break}aM=(((az|0)>(ay|0)?az:ay)-1|0)+(c[18084]|0)|0}}while(0);if((r|0)==8387){r=0;aM=((c[18084]|0)-1|0)+((az|0)<(aL|0)?az:aL)|0}c[18084]=aM;if((at|0)<((c[C>>2]|0)-1|0)){au=at}else{aN=aM;break}}}else{aN=c[18084]|0}au=aa(aa((aJ|0)>1?aJ:1,(aK|0)>1?aK:1),aN);c[18084]=au;c[6952]=db(c[6952]|0,au*152&-1,98184)|0}au=(aJ|0)>1;as=(aK|0)>1|au;do{if(as){ap=(aK<<2)+4|0;ay=ut(ap)|0;if((ay|0)==0){gk();ax=ut(ap)|0;if((ax|0)==0){r=8394;break L11412}else{aO=ax}}else{aO=ay}ay=aO;if((aK|0)<0){aP=ay;break}ax=(aJ<<5)+32|0;ap=0;while(1){av=ut(ax)|0;if((av|0)==0){gk();aw=ut(ax)|0;if((aw|0)==0){r=8399;break L11412}else{aQ=aw}}else{aQ=av}c[ay+(ap<<2)>>2]=aQ;av=ap+1|0;if((av|0)>(aK|0)){aP=ay;break}else{ap=av}}}else{aP=0}}while(0);ap=c[C>>2]|0;if((ap-1|0)>0){ay=c[m>>2]|0;ax=(aK|0)<2&(aJ|0)<2;at=an&1;az=at<<24>>24==0;av=as|al<<24>>24==115;aw=(aK|0)<0;aC=(aK|0)>0;aB=(aJ|0)>0;aq=+(aK|0);aD=at<<24>>24!=0;aR=+(aJ|0);at=(c[l>>2]|0)==0;aS=-999;aT=-999;aU=0.0;aV=0;aW=ap;while(1){ap=c[ay+(aV<<2)>>2]|0;aX=aV+1|0;aY=c[ay+(aX<<2)>>2]|0;aZ=c[ap+12>>2]|0;a_=c[aY+12>>2]|0;a$=a[30639]|0;if((a$<<24>>24|0)==114){a0=c[ap+8>>2]|0;a1=c[aY+8>>2]|0;a2=a0-a1|0;a3=(a2|0)>-1?a2:-a2|0;a4=a0;a5=a1}else if((a$<<24>>24|0)==99){a$=c[ap+8>>2]|0;a1=c[aY+8>>2]|0;a0=a$-a1|0;a3=(((a0|0)>-1?a0:-a0|0)|0)/2&-1;a4=a$;a5=a1}else{a3=0;a4=c[ap+8>>2]|0;a5=c[aY+8>>2]|0}a1=ap+8|0;ap=aY+8|0;aY=(a4|0)<(a5|0)?a4:a5;a$=aY-2|0;if((a[30640]|0)!=0&(a4|0)!=(a5|0)){a6=(a4|0)>(a5|0)?a4:a5;a7=a$+a3|0;a9=a3;ba=1}else{a6=aY;a7=aT;a9=aS;ba=0}aY=a6-1|0;if((aY|0)>0){a0=(1-a3|0)+a7|0;a2=ba?a3:0;bb=aU;bc=0;while(1){do{if(ba){if(!((bc|0)<(a9|0)|(bc|0)>(a7|0))){r=8416;break}bd=(bc|0)>(a9|0)?a0:0;be=bd;bf=bd;bg=bc}else{r=8416}}while(0);if((r|0)==8416){r=0;bd=bc-a2|0;if((a[30641]|0)==97){bh=at?bd:a$-bd|0}else{bh=bd}be=bh;bf=bh+1|0;bg=bh+a3|0}bd=bg+1|0;bi=(c[a1>>2]|0)>(c[ap>>2]|0);bj=bi?bf:bd;bk=bi?be:bg;bl=bi?bd:bf;bd=bi?bg:be;bi=c[aZ+(bd<<6)>>2]|0;do{if((a[30642]|0)==52){if((bi|0)!=0){bm=bb;break}if((c[aZ+(bl<<6)>>2]|0)!=0){bm=bb;break}if((c[a_+(bk<<6)>>2]|0)!=0){bm=bb;break}if((c[a_+(bj<<6)>>2]|0)==0){r=8429}else{bm=bb}}else{if((bi|0)==2){bm=bb;break}bn=c[aZ+(bl<<6)>>2]|0;if((bn|0)==2){bm=bb;break}bo=c[a_+(bk<<6)>>2]|0;if((bo|0)==2){bm=bb;break}bp=c[a_+(bj<<6)>>2]|0;if((bp|0)==2){bm=bb;break}if((bi|0)==1&(bn|0)==1&(bo|0)==1&(bp|0)==1){bm=bb}else{r=8429}}}while(0);do{if((r|0)==8429){r=0;bi=(a[30641]|0)==100;do{if(ax|bi){do{if(az){bq=+h[aZ+(bd<<6)+24>>3];bp=c[34]|0;bo=(a[64788+(bp*688&-1)|0]&1)==0;bn=(a[66852]&1)==0;L11530:do{if(bo){do{if(bn){br=bq}else{if(bq>0.0){bs=+_(+bq);br=bs/+h[8358];break}else{bt=+h[8341];r=8443;break L11530}}}while(0);bu=br;bv=+h[aZ+(bl<<6)+24>>3];r=8444}else{if(bn){bt=+Z(+(bq*+h[64800+(bp*688&-1)>>3]));r=8443;break}if(+h[64792+(bp*688&-1)>>3]==+h[8357]){bw=bq}else{bw=bq*+h[64800+(bp*688&-1)>>3]/+h[8358]}bx=bw;by=+h[aZ+(bl<<6)+24>>3];r=8445}}while(0);if((r|0)==8443){r=0;bq=+h[aZ+(bl<<6)+24>>3];if(bo){bu=bt;bv=bq;r=8444}else{bx=bt;by=bq;r=8445}}do{if((r|0)==8444){r=0;if(bn){bz=bv;bA=bu;break}if(bv>0.0){bq=+_(+bv);bz=bq/+h[8358];bA=bu;break}else{bz=+h[8341];bA=bu;break}}else if((r|0)==8445){r=0;if(bn){bz=+Z(+(by*+h[64800+(bp*688&-1)>>3]));bA=bx;break}if(+h[64792+(bp*688&-1)>>3]==+h[8357]){bz=by;bA=bx;break}bz=by*+h[64800+(bp*688&-1)>>3]/+h[8358];bA=bx}}while(0);bq=+h[a_+(bk<<6)+24>>3];L11559:do{if(bo){do{if(bn){bB=bq}else{if(bq>0.0){bs=+_(+bq);bB=bs/+h[8358];break}else{bC=+h[8341];r=8463;break L11559}}}while(0);bD=bB;bE=+h[a_+(bj<<6)+24>>3];r=8464}else{if(bn){bC=+Z(+(bq*+h[64800+(bp*688&-1)>>3]));r=8463;break}if(+h[64792+(bp*688&-1)>>3]==+h[8357]){bF=bq}else{bF=bq*+h[64800+(bp*688&-1)>>3]/+h[8358]}bG=bF;bH=+h[a_+(bj<<6)+24>>3];r=8465}}while(0);if((r|0)==8463){r=0;bq=+h[a_+(bj<<6)+24>>3];if(bo){bD=bC;bE=bq;r=8464}else{bG=bC;bH=bq;r=8465}}if((r|0)==8464){r=0;if(bn){bI=bE;bJ=bD;bK=bz;bL=bA;break}if(bE>0.0){bq=+_(+bE);bI=bq/+h[8358];bJ=bD;bK=bz;bL=bA;break}else{bI=+h[8341];bJ=bD;bK=bz;bL=bA;break}}else if((r|0)==8465){r=0;if(bn){bI=+Z(+(bH*+h[64800+(bp*688&-1)>>3]));bJ=bG;bK=bz;bL=bA;break}if(+h[64792+(bp*688&-1)>>3]==+h[8357]){bI=bH;bJ=bG;bK=bz;bL=bA;break}bI=bH*+h[64800+(bp*688&-1)>>3]/+h[8358];bJ=bG;bK=bz;bL=bA;break}}else{bI=+h[a_+(bj<<6)+32>>3];bJ=+h[a_+(bk<<6)+32>>3];bK=+h[aZ+(bl<<6)+32>>3];bL=+h[aZ+(bd<<6)+32>>3]}}while(0);bM=c[7663]|0;do{if((bM|0)==5){bq=bL*bK*bJ*bI;if(bq==0.0){bN=0.0;break}bs=+Q(+(+Q(+(+P(+bq)))));if(((((bK<0.0&1)+(bL<0.0&1)|0)+(bJ<0.0&1)|0)+(bI<0.0&1)|0)<3){bN=bs;break}bN=-0.0-bs}else if((bM|0)==6){bO=bL>bK;bs=bO?bK:bL;bq=bO?bL:bK;bO=bJ>bI;bP=bO?bI:bJ;bQ=bO?bJ:bI;bN=((bs<bP?bP:bs)+(bq<bQ?bq:bQ))*.5}else if((bM|0)==7){bQ=bL<bK?bL:bK;bq=bJ<bI?bJ:bI;bN=bQ<bq?bQ:bq}else if((bM|0)==8){bq=bL>bK?bL:bK;bQ=bJ>bI?bJ:bI;bN=bq>bQ?bq:bQ}else if((bM|0)==1){bN=bK}else if((bM|0)==2){bN=bJ}else if((bM|0)==3){bN=bI}else if((bM|0)==0){bN=bL}else{if((a[54008]&1)==0){bN=(bL+bK+bJ+bI)*.25;break}else{bO=~~bL;bR=~~bK;bS=~~bJ;bT=~~bI;bN=+(((((bR&16711680)+(bO&16711680)|0)+(bS&16711680)|0)+(bT&16711680)|0)>>>2&16711680|((((bR&65280)+(bO&65280)|0)+(bS&65280)|0)+(bT&65280)|0)>>>2&65280|((((bR&255)+(bO&255)|0)+(bS&255)|0)+(bT&255)|0)>>>2&255|0);break}}}while(0);bM=(a[54008]&1)==0;do{if(bM){bQ=+h[8341];if(bQ>=bN){bU=+((a[20668]|0)!=112&1|0);break}bq=+h[8342];if(bq<=bN){bU=+((a[20668]|0)==112&1|0);break}bs=(bN-bQ)/(bq-bQ);if((a[20668]|0)==112){bU=bs;break}bU=1.0-bs}else{bU=bN}}while(0);if(bi){bV=bU;break}if(bM){bT=(c[3524]|0)+144|0;if((c[bT>>2]|0)==0){bV=bU;break}c[D>>2]=5;h[E>>3]=bU;c[F>>2]=0;cM[c[bT>>2]&511](g);bV=bU;break}else{bT=(c[3524]|0)+144|0;if((c[bT>>2]|0)==0){bV=bU;break}c[G>>2]=3;c[H>>2]=~~bU;h[I>>3]=0.0;cM[c[bT>>2]&511](j);bV=bU;break}}else{bV=bb}}while(0);bs=+h[aZ+(bd<<6)+8>>3];h[K>>3]=bs;bQ=+h[aZ+(bd<<6)+16>>3];h[L>>3]=bQ;bq=+h[a_+(bk<<6)+8>>3];h[M>>3]=bq;bP=+h[a_+(bk<<6)+16>>3];h[N>>3]=bP;bW=+h[a_+(bj<<6)+8>>3];h[R>>3]=bW;bX=+h[a_+(bj<<6)+16>>3];h[S>>3]=bX;bY=+h[aZ+(bl<<6)+8>>3];h[T>>3]=bY;bZ=+h[aZ+(bl<<6)+16>>3];h[U>>3]=bZ;do{if(av){h[V>>3]=+h[aZ+(bd<<6)+24>>3];h[W>>3]=+h[a_+(bk<<6)+24>>3];h[X>>3]=+h[a_+(bj<<6)+24>>3];h[Y>>3]=+h[aZ+(bl<<6)+24>>3];if(az){break}h[$>>3]=+h[aZ+(bd<<6)+32>>3];h[ab>>3]=+h[a_+(bk<<6)+32>>3];h[ac>>3]=+h[a_+(bj<<6)+32>>3];h[ad>>3]=+h[aZ+(bl<<6)+32>>3]}}while(0);if(!as){if((a[30641]|0)==100){bi=c[6952]|0;aA=c[12904]|0;uD(bi+(aA*152&-1)+16|0,o|0,128);h[bi+(aA*152&-1)>>3]=bV;c[bi+(aA*152&-1)+144>>2]=t;c[12904]=(c[12904]|0)+1;bm=bV;break}else{dy(J);bm=bV;break}}if(!aw){b_=+h[Y>>3];b$=+h[V>>3];b0=+h[X>>3];b1=+h[W>>3];aA=0;do{b2=+(aA|0);bi=aP+(aA<<2)|0;h[c[bi>>2]>>3]=bs+b2*((bY-bs)/aq);h[(c[bi>>2]|0)+(aJ<<5)>>3]=bq+b2*((bW-bq)/aq);h[(c[bi>>2]|0)+8>>3]=bQ+b2*((bZ-bQ)/aq);h[(c[bi>>2]|0)+(aJ<<5)+8>>3]=bP+b2*((bX-bP)/aq);h[(c[bi>>2]|0)+16>>3]=b$+b2*((b_-b$)/aq);h[(c[bi>>2]|0)+(aJ<<5)+16>>3]=b1+b2*((b0-b1)/aq);if(aD){b3=+h[$>>3];h[(c[bi>>2]|0)+24>>3]=b3+b2*((+h[ad>>3]-b3)/aq);b3=+h[ab>>3];h[(c[bi>>2]|0)+(aJ<<5)+24>>3]=b3+b2*((+h[ac>>3]-b3)/aq)}if(au){bT=1;do{bS=c[bi>>2]|0;b3=+h[bS>>3];b2=+(bT|0);h[bS+(bT<<5)>>3]=b3+b2*((+h[bS+(aJ<<5)>>3]-b3)/aR);bS=c[bi>>2]|0;b3=+h[bS+8>>3];h[bS+(bT<<5)+8>>3]=b3+b2*((+h[bS+(aJ<<5)+8>>3]-b3)/aR);bS=c[bi>>2]|0;b3=+h[bS+16>>3];h[bS+(bT<<5)+16>>3]=b3+b2*((+h[bS+(aJ<<5)+16>>3]-b3)/aR);if(aD){bS=c[bi>>2]|0;b3=+h[bS+24>>3];h[bS+(bT<<5)+24>>3]=b3+b2*((+h[bS+(aJ<<5)+24>>3]-b3)/aR)}bT=bT+1|0;}while((bT|0)<(aJ|0))}aA=aA+1|0;}while((aA|0)<=(aK|0))}if(aC){b4=bV;b5=0}else{bm=bV;break}while(1){if(aB){aA=aP+(b5<<2)|0;bT=b5+1|0;bi=aP+(bT<<2)|0;bM=0;while(1){bS=c[aA>>2]|0;h[K>>3]=+h[bS+(bM<<5)>>3];h[L>>3]=+h[bS+(bM<<5)+8>>3];b1=+h[bS+(bM<<5)+16>>3];h[V>>3]=b1;bO=c[bi>>2]|0;h[M>>3]=+h[bO+(bM<<5)>>3];h[N>>3]=+h[bO+(bM<<5)+8>>3];b0=+h[bO+(bM<<5)+16>>3];h[W>>3]=b0;bR=bM+1|0;h[R>>3]=+h[bO+(bR<<5)>>3];h[S>>3]=+h[bO+(bR<<5)+8>>3];b$=+h[bO+(bR<<5)+16>>3];h[X>>3]=b$;h[T>>3]=+h[bS+(bR<<5)>>3];h[U>>3]=+h[bS+(bR<<5)+8>>3];b_=+h[bS+(bR<<5)+16>>3];h[Y>>3]=b_;L11652:do{if(az){b6=c[34]|0;b7=(a[64788+(b6*688&-1)|0]&1)==0;b8=(a[66852]&1)==0;do{if(b7){if(b8){b9=b_;ca=b$;cb=b0;cc=b1;break L11652}if(b1>0.0){bP=+_(+b1);cd=bP/+h[8358];r=8531;break}else{ce=+h[8341];r=8526;break}}else{if(b8){ce=+Z(+(b1*+h[64800+(b6*688&-1)>>3]));r=8526;break}bP=+h[64792+(b6*688&-1)>>3];bX=+h[8357];if(bP==bX){cf=b1;cg=bP;ch=bX;r=8534;break}cf=b1*+h[64800+(b6*688&-1)>>3]/+h[8358];cg=bP;ch=bX;r=8534}}while(0);do{if((r|0)==8526){r=0;if(b7){if(b8){b9=b_;ca=b$;cb=b0;cc=ce;break L11652}else{cd=ce;r=8531;break}}if(b8){ci=+Z(+(b0*+h[64800+(b6*688&-1)>>3]));cj=ce;r=8539;break}else{cf=ce;cg=+h[64792+(b6*688&-1)>>3];ch=+h[8357];r=8534;break}}}while(0);do{if((r|0)==8531){r=0;if(b0>0.0){bX=+_(+b0);ck=bX/+h[8358];cl=cd;r=8536;break}else{ck=+h[8341];cl=cd;r=8536;break}}else if((r|0)==8534){r=0;if(cg==ch){ck=b0;cl=cf;r=8536;break}ck=b0*+h[64800+(b6*688&-1)>>3]/+h[8358];cl=cf;r=8536}}while(0);do{if((r|0)==8536){r=0;if(!b7){if(b8){ci=ck;cj=cl;r=8539;break}bX=+h[64792+(b6*688&-1)>>3];bP=+h[8357];if(bX==bP){cm=b$;cn=cl;co=ck;cp=bX;cq=bP;r=8553;break}cm=b$*+h[64800+(b6*688&-1)>>3]/+h[8358];cn=cl;co=ck;cp=bX;cq=bP;r=8553;break}if(b8){b9=b_;ca=b$;cb=ck;cc=cl;break L11652}if(b$>0.0){bP=+_(+b$);cr=ck;cs=cl;ct=bP/+h[8358];break}else{cu=+h[8341];cv=cl;cw=ck;r=8545;break}}}while(0);if((r|0)==8539){r=0;cu=+Z(+(b$*+h[64800+(b6*688&-1)>>3]));cv=cj;cw=ci;r=8545}do{if((r|0)==8545){r=0;if(b7){if(b8){b9=b_;ca=cu;cb=cw;cc=cv;break L11652}else{cr=cw;cs=cv;ct=cu;break}}if(b8){b9=+Z(+(b_*+h[64800+(b6*688&-1)>>3]));ca=cu;cb=cw;cc=cv;break L11652}else{cm=cu;cn=cv;co=cw;cp=+h[64792+(b6*688&-1)>>3];cq=+h[8357];r=8553;break}}}while(0);if((r|0)==8553){r=0;if(cp==cq){b9=b_;ca=cm;cb=co;cc=cn;break}b9=b_*+h[64800+(b6*688&-1)>>3]/+h[8358];ca=cm;cb=co;cc=cn;break}if(b_>0.0){bP=+_(+b_);b9=bP/+h[8358];ca=ct;cb=cr;cc=cs;break}else{b9=+h[8341];ca=ct;cb=cr;cc=cs;break}}else{bP=+h[bS+(bM<<5)+24>>3];h[$>>3]=bP;bX=+h[bO+(bM<<5)+24>>3];h[ab>>3]=bX;bQ=+h[bO+(bR<<5)+24>>3];h[ac>>3]=bQ;bZ=+h[bS+(bR<<5)+24>>3];h[ad>>3]=bZ;b9=bZ;ca=bQ;cb=bX;cc=bP}}while(0);bS=c[7663]|0;do{if((bS|0)==4){cx=(cc+cb+ca+b9)*.25}else if((bS|0)==5){b_=cc*cb*ca*b9;if(b_==0.0){cx=0.0;break}b$=+Q(+(+Q(+(+P(+b_)))));if(((((cb<0.0&1)+(cc<0.0&1)|0)+(ca<0.0&1)|0)+(b9<0.0&1)|0)<3){cx=b$;break}cx=-0.0-b$}else if((bS|0)==6){bO=cc>cb;b$=bO?cb:cc;b_=bO?cc:cb;bO=ca>b9;b0=bO?b9:ca;b1=bO?ca:b9;cx=((b$<b0?b0:b$)+(b_<b1?b_:b1))*.5}else if((bS|0)==7){b1=cc<cb?cc:cb;b_=ca<b9?ca:b9;cx=b1<b_?b1:b_}else if((bS|0)==8){b_=cc>cb?cc:cb;b1=ca>b9?ca:b9;cx=b_>b1?b_:b1}else if((bS|0)==1){cx=cb}else if((bS|0)==2){cx=ca}else if((bS|0)==3){cx=b9}else if((bS|0)==0){cx=cc}else{r=8566;break L11412}}while(0);bS=(a[54008]&1)==0;do{if(bS){b1=+h[8341];if(b1>=cx){cy=+((a[20668]|0)!=112&1|0);break}b_=+h[8342];if(b_<=cx){cy=+((a[20668]|0)==112&1|0);break}b$=(cx-b1)/(b_-b1);if((a[20668]|0)==112){cy=b$;break}cy=1.0-b$}else{cy=cx}}while(0);if((a[30641]|0)==100){bO=c[6952]|0;bp=c[12904]|0;uD(bO+(bp*152&-1)+16|0,o|0,128);h[bO+(bp*152&-1)>>3]=cy;c[bO+(bp*152&-1)+144>>2]=t;c[12904]=(c[12904]|0)+1}else{do{if(bS){bp=(c[3524]|0)+144|0;if((c[bp>>2]|0)==0){break}c[ae>>2]=5;h[af>>3]=cy;c[ag>>2]=0;cM[c[bp>>2]&511](e)}else{bp=(c[3524]|0)+144|0;if((c[bp>>2]|0)==0){break}c[ah>>2]=3;c[ai>>2]=~~cy;h[aj>>3]=0.0;cM[c[bp>>2]&511](f)}}while(0);dy(J)}if((bR|0)<(aJ|0)){bM=bR}else{cz=cy;cA=bT;break}}}else{cz=b4;cA=b5+1|0}if((cA|0)<(aK|0)){b4=cz;b5=cA}else{bm=cz;break}}}}while(0);bl=bc+1|0;if((bl|0)<(aY|0)){bb=bm;bc=bl}else{break}}cB=bm;cC=c[C>>2]|0}else{cB=aU;cC=aW}if((aX|0)<(cC-1|0)){aS=a9;aT=a7;aU=cB;aV=aX;aW=cC}else{break}}}if((aP|0)!=0){if((aK|0)>=0){aW=0;do{uu(c[aP+(aW<<2)>>2]|0);aW=aW+1|0;}while((aW|0)<=(aK|0))}uu(aP)}uu(c[m>>2]|0)}}while(0);ak=ak+1|0;al=a[p+ak|0]|0;if(al<<24>>24==0){break L11410}}if((r|0)==8394){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=92248,v)|0)}else if((r|0)==8399){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=84424,v)|0)}else if((r|0)==8566){uf(-1,76496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((a8(p|0,67)|0)!=0){r=c[11690]|0;if((r&2|0)==0){cD=r}else{i7(b,2);cD=c[11690]|0}if((cD&1|0)==0){break}i7(b,1)}}while(0);if((a[30641]|0)==100){i=d;return}cM[c[(c[3524]|0)+168>>2]&511](13);i=d;return}function i6(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;e=c[10828]|0;f=c[10814]|0;g=c[10822]|0;a[31104]=1;if((c[7662]|0)==1){return}L11775:do{if((d|0)==1){if((f|0)==0){h=0;break}else{i=f}while(1){if((c[i+12>>2]|0)==400){j=8642;break}if((a[i+56|0]&1)!=0){if((c[i+64>>2]|0)>3){j=8637;break}}k=c[i+224>>2]|0;if((k|0)!=0){if(((c[k+72>>2]|0)-4|0)>>>0<3){j=8640;break}}k=c[i>>2]|0;if((k|0)==0){j=8614;break L11775}else{i=k}}if((j|0)==8637){return}else if((j|0)==8640){return}else if((j|0)==8642){return}}else{j=8614}}while(0);L11790:do{if((j|0)==8614){i=(d|0)==2;if(!i){h=0;break}if((b|0)>0){l=e;m=1}else{h=1;break}while(1){f=c[l+12>>2]|0;if((f|0)==352|(f|0)==400){j=8638;break}if((a[l+56|0]&1)!=0){if(((c[l+64>>2]|0)-1|0)>>>0>=3){j=8644;break}}f=c[l+224>>2]|0;if((f|0)!=0){if((c[f+72>>2]|0)>3){j=8645;break}}if((m|0)<(b|0)){l=c[l>>2]|0;m=m+1|0}else{h=i;break L11790}}if((j|0)==8638){return}else if((j|0)==8644){return}else if((j|0)==8645){return}}}while(0);L11807:do{if((g|0)!=0){j=g;while(1){if(((c[j+72>>2]|0)-4|0)>>>0<3){break}j=c[j>>2]|0;if((j|0)==0){break L11807}}return}}while(0);if(((c[1122]|0)-4|0)>>>0<3){return}if(((c[16632]|0)-4|0)>>>0<3){return}if(((c[16460]|0)-4|0)>>>0<3){return}if(((c[17320]|0)-4|0)>>>0<3){return}if(((c[17148]|0)-4|0)>>>0<3){return}do{if(h){if(((c[16288]|0)-4|0)>>>0>=3){break}return}}while(0);if(((c[16804]|0)-4|0)>>>0<3){return}a[31104]=0;return}function i7(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0,q=0,r=0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0;e=i;i=i+16|0;f=e|0;g=(a[b+239|0]&1)==0;if((b|0)==0){i=e;return}j=c[b+256>>2]|0;if((j|0)==0){i=e;return}if((d-1|0)>>>0>1){i=e;return}if((c[(c[3524]|0)+148>>2]|0)==0){i=e;return}b=(d|0)==2;d=f|0;k=f+8|0;l=f+4|0;m=j;do{j=m+44|0;n=m+4|0;o=+h[(c[n>>2]|0)+24>>3];p=m+9|0;bD(135272,(v=i,i=i+24|0,c[v>>2]=c[j>>2],h[v+8>>3]=o,c[v+16>>2]=(p|0)==0?122416:p,v)|0);do{if((a[m+8|0]|0)!=0){bJ(14200);p=c[n>>2]|0;do{if(g){o=+h[p+24>>3];q=c[34]|0;r=(a[66852]&1)==0;do{if((a[64788+(q*688&-1)|0]&1)==0){if(r){s=o;break}if(o>0.0){t=+_(+o);s=t/+h[8358];break}else{s=+h[8341];break}}else{if(r){s=+Z(+(o*+h[64800+(q*688&-1)>>3]));break}if(+h[64792+(q*688&-1)>>3]==+h[8357]){s=o;break}s=o*+h[64800+(q*688&-1)>>3]/+h[8358]}}while(0);o=+h[8341];if(o>=s){u=+((a[20668]|0)!=112&1|0);break}t=+h[8342];if(t<=s){u=+((a[20668]|0)==112&1|0);break}w=(s-o)/(t-o);if((a[20668]|0)==112){u=w;break}u=1.0-w}else{w=+h[p+32>>3];o=+h[8341];if(o>=w){u=+((a[20668]|0)!=112&1|0);break}t=+h[8342];if(t<=w){u=+((a[20668]|0)==112&1|0);break}x=(w-o)/(t-o);if((a[20668]|0)==112){u=x;break}u=1.0-x}}while(0);p=(c[3524]|0)+144|0;if((c[p>>2]|0)==0){break}c[d>>2]=5;h[k>>3]=u;c[l>>2]=0;cM[c[p>>2]&511](f)}}while(0);p=c[j>>2]|0;q=c[n>>2]|0;if(b){dA(p,q)}else{dB(p,q,+h[7076])}m=c[m>>2]|0;}while((m|0)!=0);i=e;return}function i8(b){b=b|0;var d=0,e=0,f=0;d=i;e=c[c[10810]>>2]|0;if((e|0)==0){i=d;return}else{f=e}do{do{if((a[f+8|0]&1)==0){e=c[f+4>>2]|0;if((a_(e|0,167320,6)|0)==0){break}if((a_(e|0,126208,6)|0)==0){break}if((a_(e|0,133568,4)|0)==0){break}cf(b|0,126032,(v=i,i=i+8|0,c[v>>2]=e,v)|0);j5(b,f+16|0,1);aF(10,b|0)}}while(0);f=c[f>>2]|0;}while((f|0)!=0);i=d;return}function i9(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0;d=c[13898]|0;if((d|0)>=(c[8272]|0)){ja();return 0}e=c[1054]|0;f=c[e+(d*40&-1)+36>>2]|0;L11906:do{if((a[e+(d*40&-1)|0]&1)!=0){g=e+(d*40&-1)+32|0;h=c[10036]|0;i=0;while(1){if((i|0)>=(f|0)){break}if((a[h+((c[g>>2]|0)+i|0)|0]|0)==(a[i+103664|0]|0)){i=i+1|0}else{break L11906}}if((i|0)!=1){break}ja();return 0}}while(0);if(f>>>0>6){ja();return 0}uD(b|0,(c[10036]|0)+(c[e+(d*40&-1)+32>>2]|0)|0,f);a[b+(c[(c[1054]|0)+((c[13898]|0)*40&-1)+36>>2]|0)|0]=0;f=b;while(1){b=a[f]|0;if((b<<24>>24|0)==0){j=8713;break}else if(!((b<<24>>24|0)==67|(b<<24>>24|0)==98|(b<<24>>24|0)==116|(b<<24>>24|0)==115)){j=8711;break}f=f+1|0}if((j|0)==8713){c[13898]=(c[13898]|0)+1;return 0}else if((j|0)==8711){ja();return 0}return 0}function ja(){uf(c[13898]|0,156480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function jb(b){b=b|0;var d=0,e=0,f=0,j=0.0,k=0,l=0,n=0,o=0,p=0,q=0.0,r=0,s=0.0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0.0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0.0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aG=0,aH=0,aJ=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aZ=0,a_=0;d=i;e=c[3524]|0;if((e|0)==0){aI(214736,23,1,b|0)}else{f=c[e>>2]|0;cf(b|0,215208,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=13048,v)|0)}f=c[8244]|0;if((f|0)==0){aI(98152,13,1,b|0)}else{cf(b|0,105136,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}f=(a[54160]&1)!=0?179864:213448;e=(a[54152]&1)!=0?179864:213448;j=+h[7077];k=(c[14156]|0)==0?150704:150856;cf(b|0,214064,(v=i,i=i+40|0,c[v>>2]=(a[54144]&1)!=0?179864:213448,c[v+8>>2]=f,c[v+16>>2]=e,h[v+24>>3]=j,c[v+32>>2]=k,v)|0);k=c[11692]|0;if((k|0)==0){aI(212312,13,1,b|0);l=0}else{e=(c[14088]|0)==0?150704:150856;cf(b|0,212872,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=e,v)|0);ji(b,56296,0);aF(10,b|0);l=0}L11936:while(1){do{if((l|0)!=4){e=64813+(l*688&-1)|0;if((a[e]|0)!=0){k=56832+(l*24&-1)|0;f=j3(e)|0;cf(b|0,211776,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=f,v)|0)}if((l|0)==3){break}f=56832+(l*24&-1)|0;k=(c[64808+(l*688&-1)>>2]|0)==1?141856:179864;cf(b|0,211344,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=k,v)|0);k=l+1|0;if((k|0)<7){l=k;continue L11936}else{break L11936}}}while(0);l=l+1|0}j=+h[7030];if(j<0.0){aI(210168,13,1,b|0)}else{l=(a[56232]&1)!=0?106640:106608;cf(b|0,209528,(v=i,i=i+16|0,h[v>>3]=j,c[v+8>>2]=l,v)|0)}aI(207904,15,1,b|0);jf(b,49488);l=c[12292]|0;if((l|0)>0){n=150856}else{n=(l|0)<0?121920:150704}cf(b|0,207056,(v=i,i=i+8|0,c[v>>2]=n,v)|0);if((a[49240]&1)==0){n=(c[12303]|0)+1|0;cf(b|0,121528,(v=i,i=i+8|0,c[v>>2]=n,v)|0)}else{jg(b,49248)}aI(220776,11,1,b|0);jf(b,49176);aI(206464,24,1,b|0);n=c[12738]|0;if((n|0)==0){o=179864}else{o=c[53424+(n<<2)>>2]|0}j=+h[6371];l=c[12739]|0;if((l|0)==(n|0)){p=179864}else{p=c[53424+(l<<2)>>2]|0}q=+h[6372];n=c[12740]|0;if((n|0)==(l|0)){r=179864}else{r=c[53424+(n<<2)>>2]|0}s=+h[6373];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=o,h[v+8>>3]=j,c[v+16>>2]=p,h[v+24>>3]=q,c[v+32>>2]=r,h[v+40>>3]=s,v)|0);aI(205664,2,1,b|0);aI(205016,23,1,b|0);r=c[12418]|0;if((r|0)==0){t=179864}else{t=c[53424+(r<<2)>>2]|0}s=+h[6211];p=c[12419]|0;if((p|0)==(r|0)){u=179864}else{u=c[53424+(p<<2)>>2]|0}q=+h[6212];r=c[12420]|0;if((r|0)==(p|0)){w=179864}else{w=c[53424+(r<<2)>>2]|0}j=+h[6213];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=t,h[v+8>>3]=s,c[v+16>>2]=u,h[v+24>>3]=q,c[v+32>>2]=w,h[v+40>>3]=j,v)|0);cf(b|0,204656,(v=i,i=i+8|0,h[v>>3]=+h[6214],v)|0);aI(204408,6,1,b|0);w=c[12406]|0;if((w|0)==0){aI(204112,3,1,b|0)}else if((w|0)==1){aI(203864,3,1,b|0)}else if((w|0)==2){aI(203656,3,1,b|0)}do{if((a[47032]&1)!=0){w=c[11752]|0;if((w|0)==1){u=c[11732]|0;t=c[11756]|0;r=c[11734]|0;cf(b|0,203400,(v=i,i=i+24|0,c[v>>2]=u,c[v+8>>2]=t,c[v+16>>2]=r,v)|0);break}r=c[11732]|0;t=c[11756]|0;if((w|0)==2){cf(b|0,203056,(v=i,i=i+16|0,c[v>>2]=r,c[v+8>>2]=t,v)|0);break}else{x=-1}while(1){u=x+1|0;if((u|0)==7){y=0;break}if((c[46948+(u<<3)>>2]|0)==(w|0)){y=c[46944+(u<<3)>>2]|0;break}else{x=u}}w=(a[47016]&1)!=0?202360:179864;j=+h[5865];q=+h[5864];cf(b|0,202800,(v=i,i=i+48|0,c[v>>2]=r,c[v+8>>2]=t,c[v+16>>2]=y,c[v+24>>2]=w,h[v+32>>3]=j,h[v+40>>3]=q,v)|0)}}while(0);cf(b|0,201728,(v=i,i=i+16|0,c[v>>2]=24544,c[v+8>>2]=24595,v)|0);y=j3(66240)|0;cf(b|0,201048,(v=i,i=i+16|0,c[v>>2]=56880,c[v+8>>2]=y,v)|0);y=j3(65552)|0;cf(b|0,201048,(v=i,i=i+16|0,c[v>>2]=56856,c[v+8>>2]=y,v)|0);y=j3(68992)|0;cf(b|0,201048,(v=i,i=i+16|0,c[v>>2]=56976,c[v+8>>2]=y,v)|0);y=j3(68304)|0;cf(b|0,201048,(v=i,i=i+16|0,c[v>>2]=56952,c[v+8>>2]=y,v)|0);y=j3(64864)|0;cf(b|0,201048,(v=i,i=i+16|0,c[v>>2]=56832,c[v+8>>2]=y,v)|0);y=j3(66928)|0;cf(b|0,201048,(v=i,i=i+16|0,c[v>>2]=56904,c[v+8>>2]=y,v)|0);y=j3(69680)|0;cf(b|0,201048,(v=i,i=i+16|0,c[v>>2]=57e3,c[v+8>>2]=y,v)|0);cf(b|0,200232,(v=i,i=i+8|0,c[v>>2]=+h[9040]==1.0?124080:123976,v)|0);y=c[10026]|0;if((y|0)>-1){x=(y|0)==0?150704:150856;cf(b|0,198776,(v=i,i=i+8|0,c[v>>2]=x,v)|0);z=0}else{z=0}while(1){if(z>>>0>=8){A=8770;break}if((a[65036+(z*688&-1)|0]&1)!=0){A=8771;break}if((a[65037+(z*688&-1)|0]&1)==0){z=z+1|0}else{A=8771;break}}if((A|0)==8770){aI(198368,11,1,b|0)}else if((A|0)==8771){q=+h[3815];if(q!=0.0){j=q/+h[9040];cf(b|0,197920,(v=i,i=i+8|0,h[v>>3]=j,v)|0)}else{aI(197464,17,1,b|0)}aI(196824,8,1,b|0);z=(a[66413]&1)!=0?179864:136208;cf(b|0,196192,(v=i,i=i+32|0,c[v>>2]=(a[66412]&1)!=0?179864:136208,c[v+8>>2]=56880,c[v+16>>2]=z,c[v+24>>2]=56880,v)|0);z=(a[65725]&1)!=0?179864:136208;cf(b|0,196192,(v=i,i=i+32|0,c[v>>2]=(a[65724]&1)!=0?179864:136208,c[v+8>>2]=56856,c[v+16>>2]=z,c[v+24>>2]=56856,v)|0);z=(a[65037]&1)!=0?179864:136208;cf(b|0,196192,(v=i,i=i+32|0,c[v>>2]=(a[65036]&1)!=0?179864:136208,c[v+8>>2]=56832,c[v+16>>2]=z,c[v+24>>2]=56832,v)|0);aI(195696,3,1,b|0);z=(a[69165]&1)!=0?179864:136208;cf(b|0,196192,(v=i,i=i+32|0,c[v>>2]=(a[69164]&1)!=0?179864:136208,c[v+8>>2]=56976,c[v+16>>2]=z,c[v+24>>2]=56976,v)|0);z=(a[68477]&1)!=0?179864:136208;cf(b|0,196192,(v=i,i=i+32|0,c[v>>2]=(a[68476]&1)!=0?179864:136208,c[v+8>>2]=56952,c[v+16>>2]=z,c[v+24>>2]=56952,v)|0);z=(a[67101]&1)!=0?179864:136208;cf(b|0,196192,(v=i,i=i+32|0,c[v>>2]=(a[67100]&1)!=0?179864:136208,c[v+8>>2]=56904,c[v+16>>2]=z,c[v+24>>2]=56904,v)|0);aF(10,b|0);z=c[10026]|0;if((z|0)==-1){B=193464}else{B=(z|0)==0?150704:150856}cf(b|0,193936,(v=i,i=i+8|0,c[v>>2]=B,v)|0);ji(b,40048,0);aI(86120,2,1,b|0);ji(b,34776,0);aF(10,b|0)}cf(b|0,188584,(v=i,i=i+8|0,c[v>>2]=(a[27776]&1)!=0?179864:213448,v)|0);B=j3(36288)|0;cf(b|0,187184,(v=i,i=i+8|0,c[v>>2]=B,v)|0);B=c[9329]|0;if((B|0)!=0){cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=B,v)|0)}B=c[9330]|0;if(!((B|0)==1&(c[9331]|0)==-2|(B|0)==0)){aI(145280,10,1,b|0);jg(b,37320)}aF(10,b|0);aI(186104,8,1,b|0);B=c[9031]|0;do{if((B|0)==0){aI(185592,6,1,b|0);A=8796}else if((B|0)==1){aI(185072,7,1,b|0);A=8796}else if((B|0)==2){z=c[9032]|0;if((z|0)==0){aI(184712,7,1,b|0);A=8796;break}else if((z|0)==1){aI(184336,7,1,b|0);A=8796;break}else if((z|0)==2){aI(183952,7,1,b|0);A=8796;break}else if((z|0)==3){aI(183592,7,1,b|0);A=8796;break}else{A=8796;break}}else if((B|0)==3){aI(183128,3,1,b|0);z=c[9034]|0;if((z|0)==0){C=179864}else{C=c[53424+(z<<2)>>2]|0}j=+h[4519];x=c[9035]|0;if((x|0)==(z|0)){D=179864}else{D=c[53424+(x<<2)>>2]|0}q=+h[4520];z=c[9036]|0;if((z|0)==(x|0)){E=179864}else{E=c[53424+(z<<2)>>2]|0}s=+h[4521];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=C,h[v+8>>3]=j,c[v+16>>2]=D,h[v+24>>3]=q,c[v+32>>2]=E,h[v+40>>3]=s,v)|0);A=8796}else{A=8798}}while(0);do{if((A|0)==8796){if((c[9031]|0)!=2){A=8798;break}if(((c[9032]|0)-2|0)>>>0>=2){A=8798}}}while(0);do{if((A|0)==8798){E=c[9045]|0;if((E|0)==2){aI(131280,6,1,b|0);break}else if((E|0)==0){aI(131440,5,1,b|0);break}else if((E|0)==1){aI(131216,7,1,b|0);break}else{break}}}while(0);do{if(!((c[9031]|0)==2&(c[9032]|0)>>>0<2)){E=c[9044]|0;if((E|0)==0){aI(180360,4,1,b|0);break}else if((E|0)==2){aI(179904,7,1,b|0);break}else if((E|0)==1){aI(131216,7,1,b|0);break}else{break}}}while(0);E=(c[9046]|0)==0?178184:164216;D=(a[36229]&1)!=0?179864:136208;C=(a[36231]&1)!=0?179864:136208;B=c[9056]|0;if((B|0)==2){F=177584}else{F=(B|0)==1?177096:176744}cf(b|0,179568,(v=i,i=i+40|0,c[v>>2]=(c[9047]|0)==0?179176:178608,c[v+8>>2]=E,c[v+16>>2]=D,c[v+24>>2]=C,c[v+32>>2]=F,v)|0);if((c[9059]|0)>-3){aI(99520,3,1,b|0);ji(b,36232,0)}else{aI(175760,5,1,b|0)}s=+h[4524];q=+h[4525];j=+h[4526];G=+h[4527];cf(b|0,175416,(v=i,i=i+40|0,c[v>>2]=(a[36230]&1)!=0?179864:136208,h[v+8>>3]=s,h[v+16>>3]=q,h[v+24>>3]=j,h[v+32>>3]=G,v)|0);F=c[9339]|0;cf(b|0,174936,(v=i,i=i+16|0,c[v>>2]=c[9338],c[v+8>>2]=F,v)|0);aF(10,b|0);cf(b|0,174656,(v=i,i=i+8|0,c[v>>2]=(a[36228]&1)!=0?179864:136208,v)|0);if((a[36120]&1)==0){aI(174304,10,1,b|0)}aI(173712,12,1,b|0);F=c[10822]|0;if((F|0)!=0){C=F;do{F=c[C+4>>2]|0;D=j3(c[C+60>>2]|0)|0;cf(b|0,173208,(v=i,i=i+16|0,c[v>>2]=F,c[v+8>>2]=D,v)|0);D=c[C+8>>2]|0;if((D|0)==0){H=179864}else{H=c[53424+(D<<2)>>2]|0}G=+h[C+24>>3];F=c[C+12>>2]|0;if((F|0)==(D|0)){I=179864}else{I=c[53424+(F<<2)>>2]|0}j=+h[C+32>>3];D=c[C+16>>2]|0;if((D|0)==(F|0)){J=179864}else{J=c[53424+(D<<2)>>2]|0}q=+h[C+40>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=H,h[v+8>>3]=G,c[v+16>>2]=I,h[v+24>>3]=j,c[v+32>>2]=J,h[v+40>>3]=q,v)|0);D=c[C+48>>2]|0;if((D|0)==0){aI(131440,5,1,b|0)}else if((D|0)==1){aI(126448,7,1,b|0)}else if((D|0)==2){aI(131280,6,1,b|0)}D=c[C+52>>2]|0;if((D|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=D,v)|0)}D=c[C+64>>2]|0;if((D|0)!=0){cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=D,v)|0)}cf(b|0,124744,(v=i,i=i+8|0,c[v>>2]=(c[C+56>>2]|0)==0?150704:150856,v)|0);if((a[C+184|0]&1)!=0){aI(199048,11,1,b|0)}D=C+72|0;if((c[D>>2]|0)!=0){aI(145280,10,1,b|0);jg(b,D)}D=C+88|0;if((c[D>>2]|0)==0){aI(126e3,8,1,b|0)}else{aI(170816,6,1,b|0);ji(b,D,1)}aI(216256,8,1,b|0);D=c[C+144>>2]|0;if((D|0)==0){K=179864}else{K=c[53424+(D<<2)>>2]|0}q=+h[C+160>>3];F=c[C+148>>2]|0;if((F|0)==(D|0)){L=179864}else{L=c[53424+(F<<2)>>2]|0}j=+h[C+168>>3];D=c[C+152>>2]|0;if((D|0)==(F|0)){M=179864}else{M=c[53424+(D<<2)>>2]|0}G=+h[C+176>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=K,h[v+8>>3]=q,c[v+16>>2]=L,h[v+24>>3]=j,c[v+32>>2]=M,h[v+40>>3]=G,v)|0);aF(10,b|0);C=c[C>>2]|0;}while((C|0)!=0)}aI(170408,12,1,b|0);C=c[10826]|0;if((C|0)!=0){M=C;do{cf(b|0,170056,(v=i,i=i+8|0,c[v>>2]=c[M+4>>2],v)|0);C=c[M+8>>2]|0;if((C|0)==0){N=179864}else{N=c[53424+(C<<2)>>2]|0}G=+h[M+24>>3];L=c[M+12>>2]|0;if((L|0)==(C|0)){O=179864}else{O=c[53424+(L<<2)>>2]|0}j=+h[M+32>>3];C=c[M+16>>2]|0;if((C|0)==(L|0)){P=179864}else{P=c[53424+(C<<2)>>2]|0}q=+h[M+40>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=N,h[v+8>>3]=G,c[v+16>>2]=O,h[v+24>>3]=j,c[v+32>>2]=P,h[v+40>>3]=q,v)|0);aK(((a[M+88|0]&1)!=0?127520:127448)|0,b|0);C=c[M+48>>2]|0;if((C|0)==0){Q=179864}else{Q=c[53424+(C<<2)>>2]|0}q=+h[M+64>>3];L=c[M+52>>2]|0;if((L|0)==(C|0)){R=179864}else{R=c[53424+(L<<2)>>2]|0}j=+h[M+72>>3];C=c[M+56>>2]|0;if((C|0)==(L|0)){S=179864}else{S=c[53424+(C<<2)>>2]|0}G=+h[M+80>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=Q,h[v+8>>3]=q,c[v+16>>2]=R,h[v+24>>3]=j,c[v+32>>2]=S,h[v+40>>3]=G,v)|0);C=(c[M+100>>2]|0)==0?150704:150856;L=c[M+200>>2]|0;if((L|0)==2){T=128e3}else{T=(L|0)==1?127904:127768}cf(b|0,169144,(v=i,i=i+24|0,c[v>>2]=c[72304+(c[M+160>>2]<<2)>>2],c[v+8>>2]=C,c[v+16>>2]=T,v)|0);ji(b,M+104|0,0);G=+h[M+168>>3];if(G>0.0){C=c[25376+(c[M+176>>2]<<2)>>2]|0;j=+h[M+184>>3];q=+h[M+192>>3];cf(b|0,165352,(v=i,i=i+32|0,c[v>>2]=C,h[v+8>>3]=G,h[v+16>>3]=j,h[v+24>>3]=q,v)|0)}aF(10,b|0);M=c[M>>2]|0;}while((M|0)!=0)}cf(b|0,164920,(v=i,i=i+8|0,c[v>>2]=(a[30080]&1)!=0?164520:126024,v)|0);aI(164048,17,1,b|0);M=c[10820]|0;if((M|0)!=0){T=M;do{cf(b|0,163384,(v=i,i=i+8|0,c[v>>2]=c[T+4>>2],v)|0);ji(b,T+8|0,1);aF(10,b|0);T=c[T>>2]|0;}while((T|0)!=0)}aI(162896,18,1,b|0);T=c[10824]|0;if((T|0)!=0){M=T;do{cf(b|0,162248,(v=i,i=i+8|0,c[v>>2]=c[M+4>>2],v)|0);T=(c[M+12>>2]|0)==0?150704:150856;S=c[M+112>>2]|0;if((S|0)==2){U=128e3}else{U=(S|0)==1?127904:127768}cf(b|0,169144,(v=i,i=i+24|0,c[v>>2]=c[72304+(c[M+72>>2]<<2)>>2],c[v+8>>2]=T,c[v+16>>2]=U,v)|0);ji(b,M+16|0,0);q=+h[M+80>>3];if(q>0.0){T=c[25376+(c[M+88>>2]<<2)>>2]|0;j=+h[M+96>>3];G=+h[M+104>>3];cf(b|0,165352,(v=i,i=i+32|0,c[v>>2]=T,h[v+8>>3]=q,h[v+16>>3]=j,h[v+24>>3]=G,v)|0)}aF(10,b|0);M=c[M>>2]|0;}while((M|0)!=0)}aI(159152,20,1,b|0);M=c[9670]|0;if((M|0)==4){U=c[9671]|0;G=+h[4840];cf(b|0,158040,(v=i,i=i+16|0,c[v>>2]=U,h[v+8>>3]=G,v)|0)}else if((M|0)==1){aI(156128,11,1,b|0)}else if((M|0)==2){aI(155064,14,1,b|0)}else{cf(b|0,158640,(v=i,i=i+8|0,c[v>>2]=c[9671],v)|0)}aI(154704,6,1,b|0);aI(216256,8,1,b|0);M=c[9720]|0;if((M|0)==0){V=179864}else{V=c[53424+(M<<2)>>2]|0}G=+h[4862];U=c[9721]|0;if((U|0)==(M|0)){W=179864}else{W=c[53424+(U<<2)>>2]|0}j=+h[4863];M=c[9722]|0;if((M|0)==(U|0)){X=179864}else{X=c[53424+(M<<2)>>2]|0}q=+h[4864];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=V,h[v+8>>3]=G,c[v+16>>2]=W,h[v+24>>3]=j,c[v+32>>2]=X,h[v+40>>3]=q,v)|0);aF(10,b|0);jj(b,0);aI(154408,15,1,b|0);if((a[66164]&1)!=0){q=+h[8271];cf(b|0,154056,(v=i,i=i+16|0,c[v>>2]=56880,h[v+8>>3]=q,v)|0)}if((a[65476]&1)!=0){q=+h[8185];cf(b|0,154056,(v=i,i=i+16|0,c[v>>2]=56856,h[v+8>>3]=q,v)|0)}if((a[68916]&1)!=0){q=+h[8615];cf(b|0,154056,(v=i,i=i+16|0,c[v>>2]=56976,h[v+8>>3]=q,v)|0)}if((a[68228]&1)!=0){q=+h[8529];cf(b|0,154056,(v=i,i=i+16|0,c[v>>2]=56952,h[v+8>>3]=q,v)|0)}if((a[64788]&1)!=0){q=+h[8099];cf(b|0,154056,(v=i,i=i+16|0,c[v>>2]=56832,h[v+8>>3]=q,v)|0)}if((a[66852]&1)!=0){q=+h[8357];cf(b|0,154056,(v=i,i=i+16|0,c[v>>2]=56904,h[v+8>>3]=q,v)|0)}if((a[69604]&1)!=0){q=+h[8701];cf(b|0,154056,(v=i,i=i+16|0,c[v>>2]=57e3,h[v+8>>3]=q,v)|0)}X=(c[8764]|0)==2?88392:179864;q=+h[4384];W=(c[6588]|0)==2?88392:179864;j=+h[3296];V=(c[1057]|0)==2?88392:179864;G=+h[531];M=(c[14091]|0)==2?88392:179864;s=+h[7048];cf(b|0,89120,(v=i,i=i+72|0,c[v>>2]=153688,c[v+8>>2]=X,h[v+16>>3]=q,c[v+24>>2]=W,h[v+32>>3]=j,c[v+40>>2]=V,h[v+48>>3]=G,c[v+56>>2]=M,h[v+64>>3]=s,v)|0);s=+h[3818];M=c[44936+(c[11252]<<2)>>2]|0;V=(a[30528]&1)!=0?179864:213448;W=(a[32936]&1)!=0?179864:213448;cf(b|0,153216,(v=i,i=i+40|0,h[v>>3]=+h[3817],h[v+8>>3]=s,c[v+16>>2]=M,c[v+24>>2]=V,c[v+32>>2]=W,v)|0);W=c[8270]|0;if((W|0)!=0){cf(b|0,152808,(v=i,i=i+8|0,c[v>>2]=W,v)|0)}W=c[12886]|0;if((W|0)==0){Y=0}else{cf(b|0,152408,(v=i,i=i+8|0,c[v>>2]=W,v)|0);Y=(c[12886]|0)!=0}if(!((c[8270]|0)!=0|Y)){aI(152040,18,1,b|0)}aI(151856,9,1,b|0);if((c[5094]|0)==1){aI(134552,3,1,b|0)}else{s=+g[3538];G=+g[3536];j=+g[3534];q=+g[3532];cf(b|0,151408,(v=i,i=i+32|0,h[v>>3]=s,h[v+8>>3]=G,h[v+16>>3]=j,h[v+24>>3]=q,v)|0)}Y=c[18072]|0;if((Y|0)==2){Z=150928;A=8906}else if((Y|0)!=0){Z=(Y|0)==3?150776:179864;A=8906}if((A|0)==8906){cf(b|0,151136,(v=i,i=i+8|0,c[v>>2]=Z,v)|0)}Z=c[6350]|0;A=c[9344]|0;Y=c[9342]|0;W=(a[46752]&1)!=0?179864:213448;V=(c[11690]|0)!=0?179864:213448;cf(b|0,150608,(v=i,i=i+48|0,c[v>>2]=c[6352],c[v+8>>2]=Z,c[v+16>>2]=A,c[v+24>>2]=Y,c[v+32>>2]=W,c[v+40>>2]=V,v)|0);V=c[11690]|0;if((V|0)==0){aF(10,b|0)}else if((V|0)==1){aI(150432,6,1,b|0)}else if((V|0)==2){aI(150288,9,1,b|0)}else if((V|0)==3){aI(150104,6,1,b|0)}if((a[35888]&1)==0){aI(149752,13,1,b|0)}else{cf(b|0,149912,(v=i,i=i+8|0,c[v>>2]=53520,v)|0)}if((a[43472]&1)!=0){aI(149592,11,1,b|0)}aI(149400,12,1,b|0);V=c[8732]|0;if((V|0)==1){aI(113784,10,1,b|0)}else if((V|0)==2){aI(113712,12,1,b|0)}else{aI(113904,10,1,b|0)}V=c[8528]|0;if((V|0)!=0){cf(b|0,148496,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}V=a[47120]|0;if(V<<24>>24==0){aI(148224,34,1,b|0)}else{W=V<<24>>24;cf(b|0,148416,(v=i,i=i+8|0,c[v>>2]=W,v)|0)}W=c[11948]|0;if((aY(W|0,199184)|0)!=0){cf(b|0,147856,(v=i,i=i+8|0,c[v>>2]=W,v)|0)}if((a[47712]&1)!=0){aI(147600,21,1,b|0)}if((a[47472]&1)!=0){aI(147320,24,1,b|0)}if((a[38984]&1)==0){aI(197656,15,1,b|0)}else{W=(c[9744]|0)==0?150704:150856;V=c[9742]|0;Y=c[9734]|0;A=c[9738]|0;Z=a[38944]|0?136208:179864;M=a[38960]|0?136208:179864;cf(b|0,186344,(v=i,i=i+48|0,c[v>>2]=W,c[v+8>>2]=V,c[v+16>>2]=Y,c[v+24>>2]=A,c[v+32>>2]=Z,c[v+40>>2]=M,v)|0)}cf(b|0,147064,(v=i,i=i+8|0,c[v>>2]=c[13368],v)|0);aI(146824,14,1,b|0);M=c[13378]|0;if((M|0)==0){aI(146624,7,1,b|0)}else if((M|0)==1){aI(146456,12,1,b|0)}else if((M|0)==2){aI(146264,8,1,b|0)}aI(146096,21,1,b|0);M=c[13372]|0;if((M|0)==0){Z=c[13374]|0;cf(b|0,145944,(v=i,i=i+8|0,c[v>>2]=Z,v)|0)}else if((M|0)==1){Z=c[11662]|0;q=+h[Z>>3];j=+h[Z+8>>3];G=q+j*+(c[13374]|0);cf(b|0,145704,(v=i,i=i+24|0,h[v>>3]=q,h[v+8>>3]=j,h[v+16>>3]=G,v)|0)}else if((M|0)==2){cf(b|0,145496,(v=i,i=i+8|0,h[v>>3]=+h[c[11662]>>3],v)|0);if((c[13374]|0)>1){M=1;do{cf(b|0,107648,(v=i,i=i+8|0,h[v>>3]=+h[(c[11662]|0)+(M<<3)>>3],v)|0);M=M+1|0;}while((M|0)<(c[13374]|0))}aF(10,b|0)}G=+g[18074];j=+g[178];q=+g[38];s=+g[184];_=+g[44];cf(b|0,144928,(v=i,i=i+48|0,c[v>>2]=c[13366],h[v+8>>3]=G,h[v+16>>3]=j,h[v+24>>3]=q,h[v+32>>3]=s,h[v+40>>3]=_,v)|0);aI(144752,15,1,b|0);jh(b,107848,c[12890]|0);aI(144472,19,1,b|0);jh(b,144328,c[10058]|0);cf(b|0,126416,(v=i,i=i+8|0,c[v>>2]=56880,v)|0);ji(b,66656,0);aF(10,b|0);cf(b|0,126416,(v=i,i=i+8|0,c[v>>2]=56856,v)|0);ji(b,65968,0);aF(10,b|0);cf(b|0,126416,(v=i,i=i+8|0,c[v>>2]=56832,v)|0);ji(b,65280,0);aF(10,b|0);cf(b|0,126416,(v=i,i=i+8|0,c[v>>2]=56976,v)|0);ji(b,69408,0);aF(10,b|0);cf(b|0,126416,(v=i,i=i+8|0,c[v>>2]=56952,v)|0);ji(b,68720,0);aF(10,b|0);_=+h[77];if((a[624]&1)==0){cf(b|0,143976,(v=i,i=i+8|0,h[v>>3]=_,v)|0)}else{cf(b|0,144112,(v=i,i=i+8|0,h[v>>3]=_,v)|0)}M=c[16604]&3;if((M|0)==0){cf(b|0,143896,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}else if((M|0)==3){cf(b|0,143776,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}else if((M|0)==1){cf(b|0,143616,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}else if((M|0)==2){_=+h[8303];cf(b|0,143408,(v=i,i=i+16|0,c[v>>2]=56880,h[v+8>>3]=_,v)|0)}M=c[16432]&3;if((M|0)==0){cf(b|0,143896,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}else if((M|0)==3){cf(b|0,143776,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}else if((M|0)==1){cf(b|0,143616,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}else if((M|0)==2){_=+h[8217];cf(b|0,143408,(v=i,i=i+16|0,c[v>>2]=56856,h[v+8>>3]=_,v)|0)}M=c[16260]&3;if((M|0)==0){cf(b|0,143896,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}else if((M|0)==3){cf(b|0,143776,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}else if((M|0)==1){cf(b|0,143616,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}else if((M|0)==2){_=+h[8131];cf(b|0,143408,(v=i,i=i+16|0,c[v>>2]=56832,h[v+8>>3]=_,v)|0)}M=c[17292]&3;if((M|0)==0){cf(b|0,143896,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}else if((M|0)==3){cf(b|0,143776,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}else if((M|0)==1){cf(b|0,143616,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}else if((M|0)==2){_=+h[8647];cf(b|0,143408,(v=i,i=i+16|0,c[v>>2]=56976,h[v+8>>3]=_,v)|0)}M=c[17120]&3;if((M|0)==0){cf(b|0,143896,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}else if((M|0)==3){cf(b|0,143776,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}else if((M|0)==1){cf(b|0,143616,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}else if((M|0)==2){_=+h[8561];cf(b|0,143408,(v=i,i=i+16|0,c[v>>2]=56952,h[v+8>>3]=_,v)|0)}M=c[16776]&3;if((M|0)==0){cf(b|0,143896,(v=i,i=i+8|0,c[v>>2]=56904,v)|0)}else if((M|0)==3){cf(b|0,143776,(v=i,i=i+8|0,c[v>>2]=56904,v)|0)}else if((M|0)==1){cf(b|0,143616,(v=i,i=i+8|0,c[v>>2]=56904,v)|0)}else if((M|0)==2){_=+h[8389];cf(b|0,143408,(v=i,i=i+16|0,c[v>>2]=56904,h[v+8>>3]=_,v)|0)}jk(b,2);jk(b,1);jk(b,0);jk(b,6);jk(b,5);jk(b,3);jk(b,7);M=c[1119]|0;if((M|0)==0){$=179864}else{$=j3(M)|0}cf(b|0,143304,(v=i,i=i+24|0,c[v>>2]=179864,c[v+8>>2]=154440,c[v+16>>2]=$,v)|0);cf(b|0,142960,(v=i,i=i+16|0,c[v>>2]=179864,c[v+8>>2]=154440,v)|0);aI(216256,8,1,b|0);$=c[1140]|0;if(($|0)==0){aa=179864}else{aa=c[53424+($<<2)>>2]|0}_=+h[572];M=c[1141]|0;if((M|0)==($|0)){ab=179864}else{ab=c[53424+(M<<2)>>2]|0}s=+h[573];$=c[1142]|0;if(($|0)==(M|0)){ac=179864}else{ac=c[53424+($<<2)>>2]|0}q=+h[574];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=aa,h[v+8>>3]=_,c[v+16>>2]=ab,h[v+24>>3]=s,c[v+32>>2]=ac,h[v+40>>3]=q,v)|0);ac=c[1120]|0;if((ac|0)==0){ad=179864}else{ad=j3(ac)|0}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=ad,v)|0);if((c[1122]|0)!=0){aI(145280,10,1,b|0);jg(b,4488)}if((c[1105]|0)==-3){aI(142704,16,1,b|0)}ad=c[1117]|0;if((ad|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=ad,v)|0)}cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=(a[4600]&1)!=0?199048:179864,v)|0);cf(b|0,142592,(v=i,i=i+8|0,c[v>>2]=(c[1166]|0)!=0?196800:196168,v)|0);ad=c[1183]|0;if((ad|0)==0){ae=179864}else{ae=j3(ad)|0}cf(b|0,143304,(v=i,i=i+24|0,c[v>>2]=179864,c[v+8>>2]=133504,c[v+16>>2]=ae,v)|0);cf(b|0,142960,(v=i,i=i+16|0,c[v>>2]=179864,c[v+8>>2]=133504,v)|0);aI(216256,8,1,b|0);ae=c[1204]|0;if((ae|0)==0){af=179864}else{af=c[53424+(ae<<2)>>2]|0}q=+h[604];ad=c[1205]|0;if((ad|0)==(ae|0)){ag=179864}else{ag=c[53424+(ad<<2)>>2]|0}s=+h[605];ae=c[1206]|0;if((ae|0)==(ad|0)){ah=179864}else{ah=c[53424+(ae<<2)>>2]|0}_=+h[606];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=af,h[v+8>>3]=q,c[v+16>>2]=ag,h[v+24>>3]=s,c[v+32>>2]=ah,h[v+40>>3]=_,v)|0);ah=c[1184]|0;if((ah|0)==0){ai=179864}else{ai=j3(ah)|0}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=ai,v)|0);if((c[1186]|0)!=0){aI(145280,10,1,b|0);jg(b,4744)}if((c[1169]|0)==-3){aI(142704,16,1,b|0)}ai=c[1181]|0;if((ai|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=ai,v)|0)}cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=(a[4856]&1)!=0?199048:179864,v)|0);je(b,7);je(b,8);je(b,9);je(b,10);ai=c[16629]|0;if((ai|0)==0){aj=179864}else{aj=j3(ai)|0}cf(b|0,143304,(v=i,i=i+24|0,c[v>>2]=56880,c[v+8>>2]=202256,c[v+16>>2]=aj,v)|0);cf(b|0,142960,(v=i,i=i+16|0,c[v>>2]=56880,c[v+8>>2]=202256,v)|0);aI(216256,8,1,b|0);aj=c[16650]|0;if((aj|0)==0){ak=179864}else{ak=c[53424+(aj<<2)>>2]|0}_=+h[8327];ai=c[16651]|0;if((ai|0)==(aj|0)){al=179864}else{al=c[53424+(ai<<2)>>2]|0}s=+h[8328];aj=c[16652]|0;if((aj|0)==(ai|0)){am=179864}else{am=c[53424+(aj<<2)>>2]|0}q=+h[8329];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=ak,h[v+8>>3]=_,c[v+16>>2]=al,h[v+24>>3]=s,c[v+32>>2]=am,h[v+40>>3]=q,v)|0);am=c[16630]|0;if((am|0)==0){an=179864}else{an=j3(am)|0}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=an,v)|0);if((c[16632]|0)!=0){aI(145280,10,1,b|0);jg(b,66528)}if((c[16615]|0)==-3){aI(142704,16,1,b|0)}an=c[16627]|0;if((an|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=an,v)|0)}cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=(a[66640]&1)!=0?199048:179864,v)|0);an=c[17317]|0;if((an|0)==0){ao=179864}else{ao=j3(an)|0}cf(b|0,143304,(v=i,i=i+24|0,c[v>>2]=56976,c[v+8>>2]=202256,c[v+16>>2]=ao,v)|0);cf(b|0,142960,(v=i,i=i+16|0,c[v>>2]=56976,c[v+8>>2]=202256,v)|0);aI(216256,8,1,b|0);ao=c[17338]|0;if((ao|0)==0){ap=179864}else{ap=c[53424+(ao<<2)>>2]|0}q=+h[8671];an=c[17339]|0;if((an|0)==(ao|0)){aq=179864}else{aq=c[53424+(an<<2)>>2]|0}s=+h[8672];ao=c[17340]|0;if((ao|0)==(an|0)){ar=179864}else{ar=c[53424+(ao<<2)>>2]|0}_=+h[8673];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=ap,h[v+8>>3]=q,c[v+16>>2]=aq,h[v+24>>3]=s,c[v+32>>2]=ar,h[v+40>>3]=_,v)|0);ar=c[17318]|0;if((ar|0)==0){as=179864}else{as=j3(ar)|0}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=as,v)|0);if((c[17320]|0)!=0){aI(145280,10,1,b|0);jg(b,69280)}if((c[17303]|0)==-3){aI(142704,16,1,b|0)}as=c[17315]|0;if((as|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=as,v)|0)}cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=(a[69392]&1)!=0?199048:179864,v)|0);je(b,2);je(b,6);as=c[16457]|0;if((as|0)==0){at=179864}else{at=j3(as)|0}cf(b|0,143304,(v=i,i=i+24|0,c[v>>2]=56856,c[v+8>>2]=202256,c[v+16>>2]=at,v)|0);cf(b|0,142960,(v=i,i=i+16|0,c[v>>2]=56856,c[v+8>>2]=202256,v)|0);aI(216256,8,1,b|0);at=c[16478]|0;if((at|0)==0){au=179864}else{au=c[53424+(at<<2)>>2]|0}_=+h[8241];as=c[16479]|0;if((as|0)==(at|0)){av=179864}else{av=c[53424+(as<<2)>>2]|0}s=+h[8242];at=c[16480]|0;if((at|0)==(as|0)){aw=179864}else{aw=c[53424+(at<<2)>>2]|0}q=+h[8243];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=au,h[v+8>>3]=_,c[v+16>>2]=av,h[v+24>>3]=s,c[v+32>>2]=aw,h[v+40>>3]=q,v)|0);aw=c[16458]|0;if((aw|0)==0){ax=179864}else{ax=j3(aw)|0}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=ax,v)|0);if((c[16460]|0)!=0){aI(145280,10,1,b|0);jg(b,65840)}if((c[16443]|0)==-3){aI(142704,16,1,b|0)}ax=c[16455]|0;if((ax|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=ax,v)|0)}cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=(a[65952]&1)!=0?199048:179864,v)|0);ax=c[17145]|0;if((ax|0)==0){ay=179864}else{ay=j3(ax)|0}cf(b|0,143304,(v=i,i=i+24|0,c[v>>2]=56952,c[v+8>>2]=202256,c[v+16>>2]=ay,v)|0);cf(b|0,142960,(v=i,i=i+16|0,c[v>>2]=56952,c[v+8>>2]=202256,v)|0);aI(216256,8,1,b|0);ay=c[17166]|0;if((ay|0)==0){az=179864}else{az=c[53424+(ay<<2)>>2]|0}q=+h[8585];ax=c[17167]|0;if((ax|0)==(ay|0)){aA=179864}else{aA=c[53424+(ax<<2)>>2]|0}s=+h[8586];ay=c[17168]|0;if((ay|0)==(ax|0)){aB=179864}else{aB=c[53424+(ay<<2)>>2]|0}_=+h[8587];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=az,h[v+8>>3]=q,c[v+16>>2]=aA,h[v+24>>3]=s,c[v+32>>2]=aB,h[v+40>>3]=_,v)|0);aB=c[17146]|0;if((aB|0)==0){aC=179864}else{aC=j3(aB)|0}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=aC,v)|0);if((c[17148]|0)!=0){aI(145280,10,1,b|0);jg(b,68592)}if((c[17131]|0)==-3){aI(142704,16,1,b|0)}aC=c[17143]|0;if((aC|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=aC,v)|0)}cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=(a[68704]&1)!=0?199048:179864,v)|0);do{if((c[5094]|0)==0){je(b,1);je(b,5)}else{if(a[20368]|0){a[20368]=0;g[3538]=+g[5090];g[3536]=+g[5088];g[3534]=+g[5086];c[16336]=c[16336]^2;c[17024]=c[17024]^2}je(b,1);je(b,5);if(a[20368]|0){break}a[20368]=1;g[5090]=+g[3538];g[5088]=+g[3536];g[5086]=+g[3534];g[3538]=180.0;g[3536]=0.0;g[3534]=1.2999999523162842;c[16336]=c[16336]^2;c[17024]=c[17024]^2}}while(0);aC=c[16285]|0;if((aC|0)==0){aD=179864}else{aD=j3(aC)|0}cf(b|0,143304,(v=i,i=i+24|0,c[v>>2]=56832,c[v+8>>2]=202256,c[v+16>>2]=aD,v)|0);cf(b|0,142960,(v=i,i=i+16|0,c[v>>2]=56832,c[v+8>>2]=202256,v)|0);aI(216256,8,1,b|0);aD=c[16306]|0;if((aD|0)==0){aE=179864}else{aE=c[53424+(aD<<2)>>2]|0}_=+h[8155];aC=c[16307]|0;if((aC|0)==(aD|0)){aG=179864}else{aG=c[53424+(aC<<2)>>2]|0}s=+h[8156];aD=c[16308]|0;if((aD|0)==(aC|0)){aH=179864}else{aH=c[53424+(aD<<2)>>2]|0}q=+h[8157];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=aE,h[v+8>>3]=_,c[v+16>>2]=aG,h[v+24>>3]=s,c[v+32>>2]=aH,h[v+40>>3]=q,v)|0);aH=c[16286]|0;if((aH|0)==0){aJ=179864}else{aJ=j3(aH)|0}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=aJ,v)|0);if((c[16288]|0)!=0){aI(145280,10,1,b|0);jg(b,65152)}if((c[16271]|0)==-3){aI(142704,16,1,b|0)}aJ=c[16283]|0;if((aJ|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=aJ,v)|0)}cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=(a[65264]&1)!=0?199048:179864,v)|0);je(b,0);aJ=c[16801]|0;if((aJ|0)==0){aL=179864}else{aL=j3(aJ)|0}cf(b|0,143304,(v=i,i=i+24|0,c[v>>2]=56904,c[v+8>>2]=202256,c[v+16>>2]=aL,v)|0);cf(b|0,142960,(v=i,i=i+16|0,c[v>>2]=56904,c[v+8>>2]=202256,v)|0);aI(216256,8,1,b|0);aL=c[16822]|0;if((aL|0)==0){aM=179864}else{aM=c[53424+(aL<<2)>>2]|0}q=+h[8413];aJ=c[16823]|0;if((aJ|0)==(aL|0)){aN=179864}else{aN=c[53424+(aJ<<2)>>2]|0}s=+h[8414];aL=c[16824]|0;if((aL|0)==(aJ|0)){aO=179864}else{aO=c[53424+(aL<<2)>>2]|0}_=+h[8415];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=aM,h[v+8>>3]=q,c[v+16>>2]=aN,h[v+24>>3]=s,c[v+32>>2]=aO,h[v+40>>3]=_,v)|0);aO=c[16802]|0;if((aO|0)==0){aP=179864}else{aP=j3(aO)|0}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=aP,v)|0);if((c[16804]|0)!=0){aI(145280,10,1,b|0);jg(b,67216)}if((c[16787]|0)==-3){aI(142704,16,1,b|0)}aP=c[16799]|0;if((aP|0)==0){aI(90568,9,1,b|0)}else{cf(b|0,172464,(v=i,i=i+8|0,c[v>>2]=aP,v)|0)}cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=(a[67328]&1)!=0?199048:179864,v)|0);je(b,3);cf(b|0,141912,(v=i,i=i+8|0,h[v>>3]=+h[11],v)|0);_=+h[4395];cf(b|0,141776,(v=i,i=i+16|0,c[v>>2]=(c[8786]|0)==3?141576:179864,h[v+8>>3]=_,v)|0);_=+h[7052];cf(b|0,141392,(v=i,i=i+16|0,c[v>>2]=(c[14100]|0)==3?141576:179864,h[v+8>>3]=_,v)|0);_=+h[3301];cf(b|0,141272,(v=i,i=i+16|0,c[v>>2]=(c[6598]|0)==3?141576:179864,h[v+8>>3]=_,v)|0);_=+h[535];cf(b|0,141072,(v=i,i=i+16|0,c[v>>2]=(c[1066]|0)==3?141576:179864,h[v+8>>3]=_,v)|0);cf(b|0,140928,(v=i,i=i+8|0,c[v>>2]=c[12908],v)|0);aI(140768,9,1,b|0);aI(((c[7662]|0)==1?140568:140376)|0,8,1,b|0);cf(b|0,140264,(v=i,i=i+8|0,c[v>>2]=30632,v)|0);aI(140768,9,1,b|0);aP=a[30641]|0;if((aP|0)==97){aI(140104,15,1,b|0)}else if((aP|0)==102){aI(139848,13,1,b|0)}else if((aP|0)==98){aI(139608,14,1,b|0)}else if((aP|0)==100){aI(139464,11,1,b|0)}aP=c[7665]|0;cf(b|0,139248,(v=i,i=i+16|0,c[v>>2]=c[7664],c[v+8>>2]=aP,v)|0);aI(139080,7,1,b|0);aP=a[30639]|0;if((aP|0)==99){aI(89896,6,1,b|0)}else if((aP|0)==98){aI(138664,5,1,b|0)}else if((aP|0)==114){aI(74552,3,1,b|0)}aK(((a[30640]|0)!=0?73808:138016)|0,b|0);aI(137864,10,1,b|0);aP=c[7661]|0;if((aP|0)>0){cf(b|0,137704,(v=i,i=i+8|0,c[v>>2]=aP,v)|0)}else{aO=(aP|0)!=0?137592:137416;aK(aO|0,b|0)}aI(137296,15,1,b|0);aO=c[7663]|0;if((aO|0)==4){aI(105792,4,1,b|0)}else if((aO|0)==5){aI(167728,7,1,b|0)}else if((aO|0)==6){aI(100616,6,1,b|0)}else if((aO|0)==7){aI(103256,3,1,b|0)}else if((aO|0)==8){aI(99528,3,1,b|0)}else{cf(b|0,136120,(v=i,i=i+8|0,c[v>>2]=aO+1,v)|0)}aF(10,b|0);aO=(a[20684]|0)!=0?137784:137920;aP=c[5168]|0;cf(b|0,135880,(v=i,i=i+24|0,c[v>>2]=(a[20668]|0)==112?135696:135584,c[v+8>>2]=aO,c[v+16>>2]=aP,v)|0);cf(b|0,135120,(v=i,i=i+8|0,h[v>>3]=+h[2705],v)|0);do{if((c[5163]|0)==103){aI(134936,5,1,b|0)}else{aI(134744,12,1,b|0);aP=c[5174]|0;if((aP|0)==114){aI(134624,4,1,b|0)}else if((aP|0)==104){aI(134504,4,1,b|0)}else if((aP|0)==99){aI(134336,4,1,b|0)}else if((aP|0)==121){aI(134152,4,1,b|0)}else if((aP|0)==120){aI(133992,4,1,b|0)}else{cf(c[m>>2]|0,133856,(v=i,i=i+24|0,c[v>>2]=133776,c[v+8>>2]=871,c[v+16>>2]=aP<<24>>24,v)|0)}aI(133576,13,1,b|0);aP=c[5163]|0;if((aP|0)==114){aO=c[5164]|0;aN=c[5165]|0;aM=c[5166]|0;cf(b|0,133464,(v=i,i=i+24|0,c[v>>2]=aO,c[v+8>>2]=aN,c[v+16>>2]=aM,v)|0);break}else if((aP|0)==100){aI(133112,9,1,b|0);if((c[5172]|0)>0){aM=0;do{aN=c[5173]|0;_=+h[aN+(aM<<5)+8>>3];s=+h[aN+(aM<<5)+16>>3];q=+h[aN+(aM<<5)+24>>3];cf(b|0,132952,(v=i,i=i+32|0,h[v>>3]=+h[aN+(aM<<5)>>3],h[v+8>>3]=_,h[v+16>>3]=s,h[v+24>>3]=q,v)|0);do{if((aM|0)<((c[5172]|0)-1|0)){aF(44,b|0);if(!((aM|0)==2|((aM|0)%4&-1|0)==2)){break}aI(132648,6,1,b|0)}}while(0);aM=aM+1|0;}while((aM|0)<(c[5172]|0))}aI(132568,3,1,b|0);break}else if((aP|0)==102){aM=c[5257]|0;t=c[5335]|0;cf(b|0,132440,(v=i,i=i+24|0,c[v>>2]=c[5179],c[v+8>>2]=aM,c[v+16>>2]=t,v)|0);break}else if((aP|0)==99){q=+h[2707];s=+h[2708];cf(b|0,132312,(v=i,i=i+24|0,h[v>>3]=+h[2706],h[v+8>>3]=q,h[v+16>>3]=s,v)|0);break}else{cf(c[m>>2]|0,142392,(v=i,i=i+24|0,c[v>>2]=133776,c[v+8>>2]=905,c[v+16>>2]=aP<<24>>24,v)|0);break}}}while(0);t=a[54016]|0;if(t<<24>>24!=110){aM=t<<24>>24==100?126024:132024;cf(b|0,132088,(v=i,i=i+8|0,c[v>>2]=aM,v)|0)}cf(b|0,131760,(v=i,i=i+8|0,c[v>>2]=(a[54017]|0)==118?131648:131544,v)|0);aM=c[13508]|0;if((aM|0)==0){aQ=179864}else{aQ=c[53424+(aM<<2)>>2]|0}s=+h[6756];t=c[13509]|0;if((t|0)==(aM|0)){aR=179864}else{aR=c[53424+(t<<2)>>2]|0}q=+h[6757];aM=c[13510]|0;if((aM|0)==(t|0)){aS=179864}else{aS=c[53424+(aM<<2)>>2]|0}_=+h[6758];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=aQ,h[v+8>>3]=s,c[v+16>>2]=aR,h[v+24>>3]=q,c[v+32>>2]=aS,h[v+40>>3]=_,v)|0);aI(83624,6,1,b|0);aS=c[13518]|0;if((aS|0)==0){aT=179864}else{aT=c[53424+(aS<<2)>>2]|0}_=+h[6761];aR=c[13519]|0;if((aR|0)==(aS|0)){aU=179864}else{aU=c[53424+(aR<<2)>>2]|0}q=+h[6762];aS=c[13520]|0;if((aS|0)==(aR|0)){aV=179864}else{aV=c[53424+(aS<<2)>>2]|0}s=+h[6763];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=aT,h[v+8>>3]=_,c[v+16>>2]=aU,h[v+24>>3]=q,c[v+32>>2]=aV,h[v+40>>3]=s,v)|0);cf(b|0,126160,(v=i,i=i+8|0,c[v>>2]=(c[13506]|0)==1?150856:150704,v)|0);do{if((a[54018]|0)==0){aI(131352,8,1,b|0)}else{aV=c[13505]|0;if((aV|0)<0){aI(131224,8,1,b|0);break}else{cf(b|0,131128,(v=i,i=i+8|0,c[v>>2]=aV,v)|0);break}}}while(0);if((a[54016]|0)==110){aI(131e3,16,1,b|0)}else{aF(10,b|0)}aV=(c[14068]|0)==257?130728:130528;aU=(c[14062]|0)==1?130472:205008;s=+h[7032];aT=(a[56264]&1)!=0?179864:136208;aS=(c[14067]|0)+1|0;q=+h[7035];aR=c[14072]|0;do{if((aR|0)==2){aW=202352}else{if((aR|0)==3){aW=108208;break}aW=(aR|0)==1?201e3:150208}}while(0);aR=(a[56292]&1)!=0?179864:213448;cf(b|0,130824,(v=i,i=i+64|0,c[v>>2]=aV,c[v+8>>2]=aU,h[v+16>>3]=s,c[v+24>>2]=aT,c[v+32>>2]=aS,h[v+40>>3]=q,c[v+48>>2]=aW,c[v+56>>2]=aR,v)|0);aI(129976,13,1,b|0);aR=c[8782]|0;c[8778]=aR;aW=c[8776]|0;L12586:do{if((aW|0)!=0){aS=aW;aT=aR;while(1){aU=c[8774]|0;do{if((aU|0)==0){c[8774]=aS;if((aT|0)!=0){aX=aS;aZ=aT;break}aV=c[8780]|0;c[8778]=aV;aX=aS;aZ=aV}else{aV=aU+((uA(aU|0)|0)+1|0)|0;c[8774]=aV;aX=aV;aZ=aT}}while(0);if(aX>>>0>=aZ>>>0){break}if((aX|0)==0){break L12586}cf(b|0,103640,(v=i,i=i+8|0,c[v>>2]=aX,v)|0);aT=c[8782]|0;c[8778]=aT;aS=c[8776]|0;if((aS|0)==0){break L12586}}c[8774]=0;c[8778]=0}}while(0);aF(10,b|0);aI(129680,13,1,b|0);aX=ur(16,0)|0;if((aX|0)!=0){aZ=aX;do{cf(b|0,103640,(v=i,i=i+8|0,c[v>>2]=aZ,v)|0);aZ=ur(16,0)|0;}while((aZ|0)!=0)}aF(10,b|0);aZ=c[57238]|0;if((aZ|0)==0){aI(129352,10,1,b|0)}else{cf(b|0,129480,(v=i,i=i+8|0,c[v>>2]=aZ,v)|0)}cf(b|0,129048,(v=i,i=i+8|0,c[v>>2]=(a[42568]&1)!=0?179864:136208,v)|0);aZ=c[10568]|0;if((aZ|0)==0){a_=aF(10,b|0)|0;i=d;return}cf(b|0,128984,(v=i,i=i+8|0,c[v>>2]=aZ,v)|0);a_=aF(10,b|0)|0;i=d;return}function jc(b){b=b|0;var d=0,e=0,f=0,g=0;d=i;jI(b);jb(b);e=c[10812]|0;if((e|0)!=0){f=e;do{e=c[f+12>>2]|0;if((e|0)!=0){cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}f=c[f>>2]|0;}while((f|0)!=0)}i8(b);cf(b|0,154696,(v=i,i=i+8|0,c[v>>2]=c[6924],v)|0);if((a[35352]|0)==0){g=aI(172e3,9,1,b|0)|0;i=d;return}aI(156144,3,1,b|0);if((b|0)!=0){aK(35352,b|0)}aF(10,b|0);g=aI(172e3,9,1,b|0)|0;i=d;return}function jd(a){a=a|0;var b=0,d=0,e=0,f=0;b=i;jI(a);d=c[3524]|0;if((d|0)==0){aI(122336,21,1,a|0)}else{e=c[d>>2]|0;cf(a|0,135232,(v=i,i=i+16|0,c[v>>2]=e,c[v+8>>2]=13048,v)|0)}e=c[8244]|0;if((e|0)==0){aI(98152,13,1,a|0);f=aI(172e3,9,1,a|0)|0;i=b;return}else{cf(a|0,105136,(v=i,i=i+8|0,c[v>>2]=e,v)|0);f=aI(172e3,9,1,a|0)|0;i=b;return}}function je(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0,D=0.0,E=0,F=0;d=i;i=i+1024|0;e=d|0;f=d+48|0;g=d+96|0;j=d+144|0;k=d+192|0;l=d+240|0;n=d+288|0;o=d+336|0;p=d+384|0;q=d+464|0;r=d+544|0;s=d+624|0;t=d+704|0;u=d+784|0;w=d+864|0;x=d+944|0;y=56832+(b*24&-1)|0;cf(a|0,92200,(v=i,i=i+8|0,c[v>>2]=y,v)|0);z=64652+(b*688&-1)|0;do{if((c[z>>2]&1|0)==0){if((c[64808+(b*688&-1)>>2]|0)==1){aF(34,a|0);A=r|0;B=64813+(b*688&-1)|0;C=+h[64680+(b*688&-1)>>3];t3(l,C);D=C- +O(+C);t4(A,80,B,l,D);B=j3(A)|0;aK(B|0,a|0);aF(34,a|0);break}else{D=+h[64680+(b*688&-1)>>3];cf(a|0,92736,(v=i,i=i+8|0,h[v>>3]=D,v)|0);break}}else{B=64728+(b*688&-1)|0;if((c[B>>2]&1|0)!=0){if((c[64808+(b*688&-1)>>2]|0)==1){aF(34,a|0);A=p|0;E=64813+(b*688&-1)|0;D=+h[64736+(b*688&-1)>>3];t3(o,D);C=D- +O(+D);t4(A,80,E,o,C);E=j3(A)|0;aK(E|0,a|0);aF(34,a|0)}else{C=+h[64736+(b*688&-1)>>3];cf(a|0,92736,(v=i,i=i+8|0,h[v>>3]=C,v)|0)}aI(76472,3,1,a|0)}aF(42,a|0);if((c[B>>2]&2|0)==0){break}aI(76472,3,1,a|0);if((c[64808+(b*688&-1)>>2]|0)==1){aF(34,a|0);B=q|0;E=64813+(b*688&-1)|0;C=+h[64744+(b*688&-1)>>3];t3(n,C);D=C- +O(+C);t4(B,80,E,n,D);E=j3(B)|0;aK(E|0,a|0);aF(34,a|0);break}else{D=+h[64744+(b*688&-1)>>3];cf(a|0,92736,(v=i,i=i+8|0,h[v>>3]=D,v)|0);break}}}while(0);aI(223968,3,1,a|0);do{if((c[z>>2]&2|0)==0){if((c[64808+(b*688&-1)>>2]|0)==1){aF(34,a|0);n=u|0;q=64813+(b*688&-1)|0;D=+h[64688+(b*688&-1)>>3];t3(g,D);C=D- +O(+D);t4(n,80,q,g,C);q=j3(n)|0;aK(q|0,a|0);aF(34,a|0);break}else{C=+h[64688+(b*688&-1)>>3];cf(a|0,92736,(v=i,i=i+8|0,h[v>>3]=C,v)|0);break}}else{q=64732+(b*688&-1)|0;if((c[q>>2]&1|0)!=0){if((c[64808+(b*688&-1)>>2]|0)==1){aF(34,a|0);n=s|0;o=64813+(b*688&-1)|0;C=+h[64752+(b*688&-1)>>3];t3(k,C);D=C- +O(+C);t4(n,80,o,k,D);o=j3(n)|0;aK(o|0,a|0);aF(34,a|0)}else{D=+h[64752+(b*688&-1)>>3];cf(a|0,92736,(v=i,i=i+8|0,h[v>>3]=D,v)|0)}aI(76472,3,1,a|0)}aF(42,a|0);if((c[q>>2]&2|0)==0){break}aI(76472,3,1,a|0);if((c[64808+(b*688&-1)>>2]|0)==1){aF(34,a|0);q=t|0;o=64813+(b*688&-1)|0;D=+h[64760+(b*688&-1)>>3];t3(j,D);C=D- +O(+D);t4(q,80,o,j,C);o=j3(q)|0;aK(o|0,a|0);aF(34,a|0);break}else{C=+h[64760+(b*688&-1)>>3];cf(a|0,92736,(v=i,i=i+8|0,h[v>>3]=C,v)|0);break}}}while(0);j=c[64656+(b*688&-1)>>2]|0;cf(a|0,216880,(v=i,i=i+16|0,c[v>>2]=(j&2|0)!=0?179864:136208,c[v+8>>2]=(j&1|0)!=0?179864:136208,v)|0);if(!((c[z>>2]|0)!=0&(c[m>>2]|0)==(a|0))){aF(10,a|0);i=d;return}aI(201952,16,1,a|0);do{if((c[z>>2]&1|0)!=0){if((c[64808+(b*688&-1)>>2]|0)==1){aF(34,a|0);j=w|0;t=64813+(b*688&-1)|0;C=+h[64664+(b*688&-1)>>3];t3(f,C);D=C- +O(+C);t4(j,80,t,f,D);t=j3(j)|0;aK(t|0,a|0);aF(34,a|0);break}else{D=+h[64664+(b*688&-1)>>3];cf(a|0,92736,(v=i,i=i+8|0,h[v>>3]=D,v)|0);break}}}while(0);aF(58,a|0);do{if((c[z>>2]&2|0)!=0){if((c[64808+(b*688&-1)>>2]|0)==1){aF(34,a|0);f=x|0;w=64813+(b*688&-1)|0;D=+h[64672+(b*688&-1)>>3];t3(e,D);C=D- +O(+D);t4(f,80,w,e,C);w=j3(f)|0;aK(w|0,a|0);aF(34,a|0);break}else{C=+h[64672+(b*688&-1)>>3];cf(a|0,92736,(v=i,i=i+8|0,h[v>>3]=C,v)|0);break}}}while(0);aI(196384,4,1,a|0);b=c[z>>2]|0;if((b&4|0)==0){F=b}else{cf(a|0,185160,(v=i,i=i+8|0,c[v>>2]=y,v)|0);F=c[z>>2]|0}if((F&8|0)==0){i=d;return}cf(a|0,180048,(v=i,i=i+8|0,c[v>>2]=y,v)|0);i=d;return}function jf(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0.0;d=i;e=c[b>>2]|0;if((e|0)==3){aI(163528,9,1,a|0);i=d;return}else if((e|0)==2|(e|0)==5){f=c[b+8>>2]|0;cf(a|0,167968,(v=i,i=i+16|0,c[v>>2]=(e|0)==2?179864:114984,c[v+8>>2]=f,v)|0)}else if((e|0)==1|(e|0)==4){g=+(c[b+4>>2]|0)/100.0;cf(a|0,175936,(v=i,i=i+16|0,c[v>>2]=(e|0)==1?179864:114984,h[v+8>>3]=g,v)|0)}else{aI(154480,7,1,a|0)}e=b+16|0;do{if((c[e>>2]|0)==1){if((c[b+20>>2]|0)!=-3){break}aI(151240,9,1,a|0);i=d;return}}while(0);aI(149416,6,1,a|0);jg(a,e);aF(10,a|0);i=d;return}function jg(a,b){a=a|0;b=b|0;var d=0,e=0,f=0.0,g=0,j=0,k=0,l=0,m=0,n=0;d=i;e=c[b>>2]|0;if((e|0)==5){f=+h[b+8>>3];cf(a|0,136528,(v=i,i=i+8|0,h[v>>3]=f,v)|0);i=d;return}else if((e|0)==4){cf(a|0,138224,(v=i,i=i+8|0,h[v>>3]=+h[b+8>>3],v)|0);i=d;return}else if((e|0)==6){aI(140288,10,1,a|0);i=d;return}else if((e|0)==2){cf(a|0,141944,(v=i,i=i+8|0,c[v>>2]=c[b+4>>2],v)|0);i=d;return}else if((e|0)==3){g=c[7642]|0;j=c[b+4>>2]|0;k=-1;while(1){l=k+1|0;m=c[g+(l<<3)>>2]|0;if((m|0)==0){n=0;break}if((c[g+(l<<3)+4>>2]|0)==(j|0)){n=m;break}else{k=l}}if(+h[b+8>>3]<0.0){aI(134760,14,1,a|0);i=d;return}if((n|0)==0){cf(a|0,131792,(v=i,i=i+8|0,c[v>>2]=j,v)|0);i=d;return}else{cf(a|0,133184,(v=i,i=i+8|0,c[v>>2]=n,v)|0);i=d;return}}else if((e|0)==1){cf(a|0,143432,(v=i,i=i+8|0,c[v>>2]=(c[b+4>>2]|0)+1,v)|0);i=d;return}else{i=d;return}}function jh(a,b,c){a=a|0;b=b|0;c=c|0;var d=0;if((c|0)==311){aI(121928,13,1,a|0);return}else if((c|0)==18){aI(129496,7,1,a|0);return}else if((c|0)==209){aI(103480,8,1,a|0);return}else if((c|0)==102){aI(120312,11,1,a|0);return}else if((c|0)==33){aI(128200,9,1,a|0);return}else if((c|0)==392){aI(114072,11,1,a|0);return}else if((c|0)==137){aI(104856,15,1,a|0);return}else if((c|0)==177){aI(104320,6,1,a|0);return}else if((c|0)==368){aI(98592,7,1,a|0);return}else if((c|0)==51){aI(126912,12,1,a|0);return}else if((c|0)==400){aI(98008,6,1,a|0);return}else if((c|0)==295){aI(124576,12,1,a|0);return}else if((c|0)==86){aI(116936,11,1,a|0);return}else if((c|0)==118){aI(116016,12,1,a|0);return}else if((c|0)==252){aI(100064,13,1,a|0);return}else if((c|0)==345){aI(110648,13,1,a|0);do{if((aY(b|0,107848)|0)==0){d=9320}else{if((aY(b|0,122960)|0)==0){d=9320;break}hF(43400,a)}}while(0);if((d|0)==9320){hF(43432,a)}aF(10,a|0);return}else if((c|0)==279){aI(123176,12,1,a|0);return}else if((c|0)==257){aI(100768,12,1,a|0);return}else if((c|0)==64){aI(125816,5,1,a|0);return}else if((c|0)==153){aI(115112,6,1,a|0);return}else if((c|0)==1){aI(130536,6,1,a|0);return}else if((c|0)==193){aI(103856,7,1,a|0);return}else if((c|0)==489){aI(96288,9,1,a|0);return}else if((c|0)==416){aI(97328,9,1,a|0);return}else if((c|0)==225){aI(101296,7,1,a|0);return}else if((c|0)==169){aI(105896,13,1,a|0);return}else if((c|0)==457){aI(96752,8,1,a|0);return}else if((c|0)==474){aI(99624,8,1,a|0);return}else if((c|0)==352){aI(99200,5,1,a|0);return}else{aI(95896,13,1,a|0);return}}function ji(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0.0;f=i;g=d+4|0;cf(b|0,95408,(v=i,i=i+8|0,c[v>>2]=(c[g>>2]|0)+1,v)|0);L12805:do{if((a[d+32|0]&1)!=0){aI(94936,10,1,b|0);j=d+40|0;k=c[j>>2]|0;if((k|0)==2){l=9373}else if((k|0)==1){cf(b|0,95144,(v=i,i=i+8|0,c[v>>2]=(c[d+44>>2]|0)+1,v)|0);break}do{if((l|0)==9373){if((c[g>>2]|0)!=-6){break}aI(93616,9,1,b|0);break L12805}}while(0);jg(b,j)}}while(0);cf(b|0,92904,(v=i,i=i+8|0,h[v>>3]=+h[d+16>>3],v)|0);if(!e){i=f;return}cf(b|0,92032,(v=i,i=i+8|0,c[v>>2]=(c[d+8>>2]|0)+1,v)|0);m=+h[d+24>>3];do{if(m==-3.0){aI(91352,19,1,b|0)}else{if(m==-2.0){aI(90696,18,1,b|0);break}else{cf(b|0,90312,(v=i,i=i+8|0,h[v>>3]=m,v)|0);break}}}while(0);cf(b|0,89624,(v=i,i=i+8|0,c[v>>2]=c[d+12>>2],v)|0);i=f;return}function jj(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0,u=0,w=0.0,x=0,y=0.0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0;e=i;f=c[10818]|0;if((f|0)==0){g=0}else{j=(d|0)==0;k=0;l=f;while(1){f=c[l+12>>2]|0;L12830:do{if((f|0)==1){n=c[l+4>>2]|0;do{if(j){o=n}else{if((n|0)==(d|0)){o=d;break}if((f|0)==4){p=9449;break L12830}else if((f|0)==3){p=9431;break L12830}else if((f|0)==2){p=9419;break L12830}else{p=9448;break L12830}}}while(0);n=(c[m>>2]|0)==(b|0)?86152:85072;q=l+4|0;cf(b|0,87232,(v=i,i=i+16|0,c[v>>2]=n,c[v+8>>2]=o,v)|0);if((c[l+104>>2]|0)==1){aI(84272,7,1,b|0);n=c[l+112>>2]|0;if((n|0)==0){r=179864}else{r=c[53424+(n<<2)>>2]|0}s=+h[l+128>>3];t=c[l+116>>2]|0;if((t|0)==(n|0)){u=179864}else{u=c[53424+(t<<2)>>2]|0}w=+h[l+136>>3];n=c[l+120>>2]|0;if((n|0)==(t|0)){x=179864}else{x=c[53424+(n<<2)>>2]|0}y=+h[l+144>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=r,h[v+8>>3]=s,c[v+16>>2]=u,h[v+24>>3]=w,c[v+32>>2]=x,h[v+40>>3]=y,v)|0);aI(83624,6,1,b|0);n=c[l+152>>2]|0;if((n|0)==0){z=179864}else{z=c[53424+(n<<2)>>2]|0}y=+h[l+168>>3];t=c[l+156>>2]|0;if((t|0)==(n|0)){A=179864}else{A=c[53424+(t<<2)>>2]|0}w=+h[l+176>>3];n=c[l+160>>2]|0;if((n|0)==(t|0)){B=179864}else{B=c[53424+(n<<2)>>2]|0}s=+h[l+184>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=z,h[v+8>>3]=y,c[v+16>>2]=A,h[v+24>>3]=w,c[v+32>>2]=B,h[v+40>>3]=s,v)|0);C=1;D=q;break}else{aI(82592,5,1,b|0);n=c[l+192>>2]|0;if((n|0)==0){E=179864}else{E=c[53424+(n<<2)>>2]|0}s=+h[l+208>>3];t=c[l+196>>2]|0;if((t|0)==(n|0)){F=179864}else{F=c[53424+(t<<2)>>2]|0}w=+h[l+216>>3];n=c[l+200>>2]|0;if((n|0)==(t|0)){G=179864}else{G=c[53424+(n<<2)>>2]|0}y=+h[l+224>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=E,h[v+8>>3]=s,c[v+16>>2]=F,h[v+24>>3]=w,c[v+32>>2]=G,h[v+40>>3]=y,v)|0);aI(127448,4,1,b|0);n=c[l+232>>2]|0;if((n|0)==0){H=179864}else{H=c[53424+(n<<2)>>2]|0}y=+h[l+248>>3];t=c[l+236>>2]|0;if((t|0)==(n|0)){I=179864}else{I=c[53424+(t<<2)>>2]|0}w=+h[l+256>>3];n=c[l+240>>2]|0;if((n|0)==(t|0)){J=179864}else{J=c[53424+(n<<2)>>2]|0}s=+h[l+264>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=H,h[v+8>>3]=y,c[v+16>>2]=I,h[v+24>>3]=w,c[v+32>>2]=J,h[v+40>>3]=s,v)|0);C=1;D=q;break}}else if((f|0)==4){p=9449}else if((f|0)==3){p=9431}else if((f|0)==2){p=9419}else{p=9448}}while(0);L12875:do{if((p|0)==9419){p=0;q=c[l+4>>2]|0;do{if(j){K=q}else{if((q|0)==(d|0)){K=d;break}if((f|0)==4){p=9449;break L12875}else if((f|0)==3){p=9431;break L12875}else{p=9448;break L12875}}}while(0);q=(c[m>>2]|0)==(b|0)?86152:85072;n=l+4|0;cf(b|0,79696,(v=i,i=i+16|0,c[v>>2]=q,c[v+8>>2]=K,v)|0);aI(84272,7,1,b|0);q=c[l+112>>2]|0;if((q|0)==0){L=179864}else{L=c[53424+(q<<2)>>2]|0}s=+h[l+128>>3];t=c[l+116>>2]|0;if((t|0)==(q|0)){M=179864}else{M=c[53424+(t<<2)>>2]|0}w=+h[l+136>>3];q=c[l+120>>2]|0;if((q|0)==(t|0)){N=179864}else{N=c[53424+(q<<2)>>2]|0}y=+h[l+144>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=L,h[v+8>>3]=s,c[v+16>>2]=M,h[v+24>>3]=w,c[v+32>>2]=N,h[v+40>>3]=y,v)|0);aI(83624,6,1,b|0);q=c[l+152>>2]|0;if((q|0)==0){O=179864}else{O=c[53424+(q<<2)>>2]|0}y=+h[l+168>>3];cf(b|0,78968,(v=i,i=i+16|0,c[v>>2]=O,h[v+8>>3]=y,v)|0);y=+h[l+192>>3];w=+h[l+200>>3];cf(b|0,78400,(v=i,i=i+16|0,h[v>>3]=y,h[v+8>>3]=w,v)|0);C=1;D=n}}while(0);L12893:do{if((p|0)==9431){p=0;n=c[l+4>>2]|0;do{if(j){P=n}else{if((n|0)==(d|0)){P=d;break}if((f|0)==4){p=9449;break L12893}else{p=9448;break L12893}}}while(0);n=(c[m>>2]|0)==(b|0)?86152:85072;q=l+4|0;cf(b|0,78048,(v=i,i=i+16|0,c[v>>2]=n,c[v+8>>2]=P,v)|0);aI(84272,7,1,b|0);n=c[l+112>>2]|0;if((n|0)==0){Q=179864}else{Q=c[53424+(n<<2)>>2]|0}w=+h[l+128>>3];t=c[l+116>>2]|0;if((t|0)==(n|0)){R=179864}else{R=c[53424+(t<<2)>>2]|0}y=+h[l+136>>3];n=c[l+120>>2]|0;if((n|0)==(t|0)){S=179864}else{S=c[53424+(n<<2)>>2]|0}s=+h[l+144>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=Q,h[v+8>>3]=w,c[v+16>>2]=R,h[v+24>>3]=y,c[v+32>>2]=S,h[v+40>>3]=s,v)|0);aI(83624,6,1,b|0);n=l+152|0;t=c[n>>2]|0;if((t|0)==0){T=179864}else{T=c[53424+(t<<2)>>2]|0}s=+h[l+168>>3];cf(b|0,78968,(v=i,i=i+16|0,c[v>>2]=T,h[v+8>>3]=s,v)|0);t=c[l+156>>2]|0;if((t|0)==(c[n>>2]|0)){U=179864}else{U=c[53424+(t<<2)>>2]|0}s=+h[l+176>>3];cf(b|0,77424,(v=i,i=i+16|0,c[v>>2]=U,h[v+8>>3]=s,v)|0);s=+h[l+192>>3];cf(b|0,76888,(v=i,i=i+8|0,h[v>>3]=s,v)|0);aI(76320,7,1,b|0);t=c[l+104>>2]|0;if((t|0)==2){aI(210848,2,1,b|0);C=1;D=q;break}else if((t|0)==0){aI(150688,2,1,b|0);C=1;D=q;break}else if((t|0)==1){aI(211336,2,1,b|0);C=1;D=q;break}else{C=1;D=q;break}}}while(0);do{if((p|0)==9448){p=0;C=k;D=l+4|0}else if((p|0)==9449){p=0;f=l+4|0;q=c[f>>2]|0;if(j){V=q}else{if((q|0)==(d|0)){V=d}else{C=k;D=f;break}}f=l+104|0;q=(c[m>>2]|0)==(b|0)?86152:85072;t=l+4|0;cf(b|0,74328,(v=i,i=i+16|0,c[v>>2]=q,c[v+8>>2]=V,v)|0);q=f+4|0;if((c[q>>2]|0)!=0){aI(82592,5,1,b|0);n=c[q>>2]|0;W=c[n>>2]|0;if((W|0)==0){X=179864}else{X=c[53424+(W<<2)>>2]|0}s=+h[n+16>>3];Y=c[n+4>>2]|0;if((Y|0)==(W|0)){Z=179864}else{Z=c[53424+(Y<<2)>>2]|0}y=+h[n+24>>3];W=c[n+8>>2]|0;if((W|0)==(Y|0)){_=179864}else{_=c[53424+(W<<2)>>2]|0}w=+h[n+32>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=X,h[v+8>>3]=s,c[v+16>>2]=Z,h[v+24>>3]=y,c[v+32>>2]=_,h[v+40>>3]=w,v)|0)}n=f|0;if((c[n>>2]|0)>1){$=1}else{C=1;D=t;break}while(1){cf(b|0,((c[m>>2]|0)==(b|0)?73888:127448)|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);f=c[q>>2]|0;W=c[f+($*40&-1)>>2]|0;if((W|0)==0){aa=179864}else{aa=c[53424+(W<<2)>>2]|0}w=+h[f+($*40&-1)+16>>3];Y=c[f+($*40&-1)+4>>2]|0;if((Y|0)==(W|0)){ab=179864}else{ab=c[53424+(Y<<2)>>2]|0}y=+h[f+($*40&-1)+24>>3];W=c[f+($*40&-1)+8>>2]|0;if((W|0)==(Y|0)){ac=179864}else{ac=c[53424+(W<<2)>>2]|0}s=+h[f+($*40&-1)+32>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=aa,h[v+8>>3]=w,c[v+16>>2]=ab,h[v+24>>3]=y,c[v+32>>2]=ac,h[v+40>>3]=s,v)|0);f=$+1|0;if((f|0)<(c[n>>2]|0)){$=f}else{C=1;D=t;break}}}}while(0);t=(c[m>>2]|0)==(b|0)?86152:85072;n=c[D>>2]|0;cf(b|0,73592,(v=i,i=i+16|0,c[v>>2]=t,c[v+8>>2]=n,v)|0);n=c[l+8>>2]|0;if((n|0)>0){ad=150856}else{ad=(n|0)<0?121920:150704}cf(b|0,73368,(v=i,i=i+8|0,c[v>>2]=ad,v)|0);s=+h[l+64>>3];if(s!=0.0){cf(b|0,223096,(v=i,i=i+8|0,h[v>>3]=s,v)|0)}aI(222648,3,1,b|0);n=c[l+52>>2]|0;do{if((n|0)==-7){aI(126024,7,1,b|0)}else{if((a[l+80|0]&1)==0){t=n+1|0;cf(b|0,121528,(v=i,i=i+8|0,c[v>>2]=t,v)|0);break}else{jg(b,l+88|0);break}}}while(0);aI(220776,11,1,b|0);jf(b,l+16|0);n=c[l>>2]|0;if((n|0)==0){g=C;break}else{k=C;l=n}}}if((d|0)<1|g){i=e;return}else{uf(c[13898]|0,218968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function jk(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0,u=0,w=0,x=0,y=0,z=0.0,A=0;e=i;i=i+384|0;f=e|0;g=e+48|0;j=e+96|0;k=e+144|0;l=e+224|0;m=e+304|0;n=c[64916+(d*688&-1)>>2]|0;o=n&3;p=56832+(d*24&-1)|0;if((o|0)==0){cf(b|0,128832,(v=i,i=i+8|0,c[v>>2]=p,v)|0);i=e;return}q=(a[65072+(d*688&-1)|0]&1)!=0?145600:128472;r=+h[65056+(d*688&-1)>>3];s=+h[65064+(d*688&-1)>>3];t=65032+(d*688&-1)|0;u=(c[t>>2]|0)!=0?185408:185368;cf(b|0,128728,(v=i,i=i+56|0,c[v>>2]=p,c[v+8>>2]=(o|0)==2?128640:149416,c[v+16>>2]=q,h[v+24>>3]=r,h[v+32>>3]=s,c[v+40>>2]=(n&4|0)!=0?179864:136208,c[v+48>>2]=u,v)|0);u=c[t>>2]|0;if((u|0)!=0){cf(b|0,128080,(v=i,i=i+8|0,c[v>>2]=u,v)|0)}aI(216256,8,1,b|0);u=c[64984+(d*688&-1)>>2]|0;if((u|0)==0){w=179864}else{w=c[53424+(u<<2)>>2]|0}s=+h[65e3+(d*688&-1)>>3];t=c[64988+(d*688&-1)>>2]|0;if((t|0)==(u|0)){x=179864}else{x=c[53424+(t<<2)>>2]|0}r=+h[65008+(d*688&-1)>>3];u=c[64992+(d*688&-1)>>2]|0;if((u|0)==(t|0)){y=179864}else{y=c[53424+(u<<2)>>2]|0}z=+h[65016+(d*688&-1)>>3];cf(b|0,215704,(v=i,i=i+48|0,c[v>>2]=w,h[v+8>>3]=s,c[v+16>>2]=x,h[v+24>>3]=r,c[v+32>>2]=y,h[v+40>>3]=z,v)|0);do{if((a[65272+(d*688&-1)|0]&1)==0){aI(127936,12,1,b|0)}else{y=c[65128+(d*688&-1)>>2]|0;if((y|0)==2){aI(131280,6,1,b|0);break}else if((y|0)==0){aI(131440,5,1,b|0);break}else if((y|0)==1){aI(131216,7,1,b|0);break}else{break}}}while(0);cf(b|0,127832,(v=i,i=i+8|0,c[v>>2]=p,v)|0);y=64920+(d*688&-1)|0;x=c[y>>2]|0;do{if((x|0)==2){w=64952+(d*688&-1)|0;z=+h[w>>3];if(z!=-8.988465674311579e+307){if((c[64808+(d*688&-1)>>2]|0)==1){aF(34,b|0);u=k|0;t=64813+(d*688&-1)|0;r=+h[w>>3];t3(j,r);s=r- +O(+r);t4(u,80,t,j,s);t=j3(u)|0;aK(t|0,b|0);aF(34,b|0)}else{cf(b|0,92736,(v=i,i=i+8|0,h[v>>3]=z,v)|0)}aF(44,b|0)}z=+h[64960+(d*688&-1)>>3];cf(b|0,170912,(v=i,i=i+8|0,h[v>>3]=z,v)|0);t=64968+(d*688&-1)|0;if(+h[t>>3]==8.988465674311579e+307){break}aF(44,b|0);if((c[64808+(d*688&-1)>>2]|0)==1){aF(34,b|0);u=l|0;w=64813+(d*688&-1)|0;z=+h[t>>3];t3(g,z);s=z- +O(+z);t4(u,80,w,g,s);w=j3(u)|0;aK(w|0,b|0);aF(34,b|0);break}else{s=+h[t>>3];cf(b|0,92736,(v=i,i=i+8|0,h[v>>3]=s,v)|0);break}}else if((x|0)==1){aI(127656,9,1,b|0)}else if((x|0)==4){cf(b|0,127576,(v=i,i=i+8|0,c[v>>2]=p,v)|0)}else if((x|0)==5){cf(b|0,127464,(v=i,i=i+8|0,c[v>>2]=p,v)|0)}}while(0);cf(b|0,((a[65024+(d*688&-1)|0]&1)!=0?127160:127080)|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);x=c[64924+(d*688&-1)>>2]|0;do{if((x|0)!=0){if((a[x]|0)==0){break}cf(b|0,126104,(v=i,i=i+8|0,c[v>>2]=x,v)|0)}}while(0);x=64928+(d*688&-1)|0;if((c[x>>2]|0)!=0){aI(145280,10,1,b|0);jg(b,x)}aF(10,b|0);x=64944+(d*688&-1)|0;if((c[x>>2]|0)==0){i=e;return}g=(c[y>>2]|0)==3?179864:186440;cf(b|0,126896,(v=i,i=i+16|0,c[v>>2]=p,c[v+8>>2]=g,v)|0);aI(126656,2,1,b|0);g=c[x>>2]|0;if((g|0)!=0){x=64808+(d*688&-1)|0;p=m|0;m=64813+(d*688&-1)|0;d=g;do{g=d+12|0;do{if((c[g>>2]|0)<0){A=d+16|0}else{y=c[d+8>>2]|0;if((y|0)!=0){l=j3(y)|0;cf(b|0,103640,(v=i,i=i+8|0,c[v>>2]=l,v)|0)}if((c[x>>2]|0)==1){aF(34,b|0);s=+h[d>>3];t3(f,s);z=s- +O(+s);t4(p,80,m,f,z);l=j3(p)|0;aK(l|0,b|0);aF(34,b|0)}else{z=+h[d>>3];cf(b|0,92736,(v=i,i=i+8|0,h[v>>3]=z,v)|0)}l=c[g>>2]|0;if((l|0)!=0){cf(b|0,95144,(v=i,i=i+8|0,c[v>>2]=l,v)|0)}l=d+16|0;if((c[l>>2]|0)==0){A=l;break}aI(86120,2,1,b|0);A=l}}while(0);d=c[A>>2]|0;}while((d|0)!=0)}aI(84944,2,1,b|0);i=e;return}function jl(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0;f=i;i=i+8|0;g=f|0;h=c[b>>2]|0;c[12918]=0;c[3530]=0;j=a[h]|0;if(j<<24>>24==0){k=0;l=c[3530]|0;m=c[1054]|0;n=m+(l*40&-1)+32|0;c[n>>2]=k;o=c[3530]|0;p=c[1054]|0;q=p+(o*40&-1)+36|0;c[q>>2]=0;r=c[3530]|0;s=c[1054]|0;t=s+(r*40&-1)|0;a[t]=1;u=c[3530]|0;i=f;return u|0}else{w=h;x=0;y=h;z=j;A=1}L13041:while(1){if((A|0)<(c[1052]|0)){B=z}else{dH();B=a[y]|0}L13046:do{if((aM(B&255|0)|0)==0){c[(c[1054]|0)+((c[3530]|0)*40&-1)+32>>2]=x;c[(c[1054]|0)+((c[3530]|0)*40&-1)+36>>2]=1;a[(c[1054]|0)+((c[3530]|0)*40&-1)|0]=1;j=a[y]|0;if(j<<24>>24==96){jm(b,e,x);C=x-1|0;D=c[b>>2]|0;break}L13051:do{if((bO(j&255|0)|0)==0){h=a[y]|0;if(h<<24>>24==95){E=x;F=9542;break}if(((h&255)-48|0)>>>0<10){a[(c[1054]|0)+((c[3530]|0)*40&-1)|0]=0;G=jn(y)|0;c[(c[1054]|0)+((c[3530]|0)*40&-1)+36>>2]=G;H=(x-1|0)+(c[(c[1054]|0)+((c[3530]|0)*40&-1)+36>>2]|0)|0;I=w;break}J=h<<24>>24;if((h<<24>>24|0)==46){if(((a[w+(x+1|0)|0]|0)-48|0)>>>0>=10){H=x;I=w;break}a[(c[1054]|0)+((c[3530]|0)*40&-1)|0]=0;G=jn(y)|0;c[(c[1054]|0)+((c[3530]|0)*40&-1)+36>>2]=G;H=(x-1|0)+(c[(c[1054]|0)+((c[3530]|0)*40&-1)+36>>2]|0)|0;I=w;break}else if((h<<24>>24|0)==39|(h<<24>>24|0)==34){G=(c[1054]|0)+((c[3530]|0)*40&-1)+36|0;c[G>>2]=(c[G>>2]|0)+1;G=a[y]|0;K=x+1|0;L=w+K|0;M=a[L]|0;if(M<<24>>24==G<<24>>24){H=K;I=w;break}N=G<<24>>24==34;O=G<<24>>24==39;P=w;Q=x;R=K;K=L;L=M;L13062:while(1){S=Q;T=R;U=K;M=L;L13064:while(1){if(M<<24>>24==0){break L13062}do{if(N&M<<24>>24==92){V=S+2|0;if((a[P+V|0]|0)==0){F=9567;break}W=(c[1054]|0)+((c[3530]|0)*40&-1)+36|0;c[W>>2]=(c[W>>2]|0)+2;X=V}else{F=9567}}while(0);L13070:do{if((F|0)==9567){F=0;do{if(N){if(M<<24>>24==96){break L13064}}else{if(!O){break}if((a[P+(S+2|0)|0]|0)!=39){break}V=S+3|0;if((a[P+V|0]|0)!=39){break}W=(c[1054]|0)+((c[3530]|0)*40&-1)+36|0;c[W>>2]=(c[W>>2]|0)+3;X=V;break L13070}}while(0);V=(c[1054]|0)+((c[3530]|0)*40&-1)+36|0;c[V>>2]=(c[V>>2]|0)+1;X=T}}while(0);V=X+1|0;W=P+V|0;Y=a[W]|0;if(Y<<24>>24==G<<24>>24){H=V;I=P;break L13051}else{S=X;T=V;U=W;M=Y}}jm(b,e,T);M=c[b>>2]|0;Y=S+1|0;W=M+Y|0;V=a[W]|0;if(V<<24>>24==34){H=Y;I=M;break L13051}else{P=M;Q=S;R=Y;K=W;L=V}}a[U]=G;a[P+(S+2|0)|0]=0;H=T;I=P;break}else if((h<<24>>24|0)==123){a[(c[1054]|0)+((c[3530]|0)*40&-1)|0]=0;c[(c[1054]|0)+((c[3530]|0)*40&-1)+8>>2]=2;L=x+1|0;K=c[3530]|0;R=c[1054]|0;Q=ca(w+L|0,153808,(v=i,i=i+24|0,c[v>>2]=R+(K*40&-1)+16,c[v+8>>2]=R+(K*40&-1)+24,c[v+16>>2]=g,v)|0)|0;if((Q|0)<1){c[12918]=(c[12918]|0)+1;K=c[3530]|0;c[3530]=K+1;a[(c[1054]|0)+(K*40&-1)|0]=1;C=x;D=w;break L13046}Z=c[3530]|0;if(!((Q|0)==3&(a[g]|0)==125)){F=9553;break L13041}Q=(c[1054]|0)+(Z*40&-1)+36|0;c[Q>>2]=(c[Q>>2]|0)+2;Q=L;while(1){L=Q+1|0;K=w+L|0;if((a[K]|0)==125){H=L;I=w;break L13051}R=(c[1054]|0)+((c[3530]|0)*40&-1)+36|0;c[R>>2]=(c[R>>2]|0)+1;if((a[K]|0)==0){F=9557;break L13041}else{Q=L}}}else{if((J|0)==35){k=x;F=9588;break L13041}else if((J|0)==125){c[12918]=(c[12918]|0)-1;H=x;I=w;break}else if((J|0)==94|(J|0)==43|(J|0)==45|(J|0)==47|(J|0)==37|(J|0)==126|(J|0)==40|(J|0)==41|(J|0)==91|(J|0)==93|(J|0)==59|(J|0)==58|(J|0)==63|(J|0)==44|(J|0)==36){H=x;I=w;break}else if((J|0)==33|(J|0)==60|(J|0)==62){Q=x+1|0;if((a[w+Q|0]|0)!=61){H=x;I=w;break}P=(c[1054]|0)+((c[3530]|0)*40&-1)+36|0;c[P>>2]=(c[P>>2]|0)+1;H=Q;I=w;break}else if((J|0)==38|(J|0)==124|(J|0)==61|(J|0)==42){Q=x+1|0;if(h<<24>>24!=(a[w+Q|0]|0)){H=x;I=w;break}P=(c[1054]|0)+((c[3530]|0)*40&-1)+36|0;c[P>>2]=(c[P>>2]|0)+1;H=Q;I=w;break}else{F=9581;break L13041}}}else{E=x;F=9542}}while(0);L13097:do{if((F|0)==9542){while(1){F=0;j=E+1|0;Q=w+j|0;if((bs(d[Q]|0|0)|0)==0){if((a[Q]|0)!=95){H=E;I=w;break L13097}}Q=(c[1054]|0)+((c[3530]|0)*40&-1)+36|0;c[Q>>2]=(c[Q>>2]|0)+1;E=j;F=9542}}}while(0);c[3530]=(c[3530]|0)+1;C=H;D=I}else{C=x;D=w}}while(0);h=C+1|0;j=D+h|0;Q=a[j]|0;if(Q<<24>>24==0){k=h;F=9587;break}w=D;x=h;y=j;z=Q;A=(c[3530]|0)+1|0}if((F|0)==9553){uf(Z,211216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((F|0)==9581){uf(c[3530]|0,134656,(v=i,i=i+8|0,c[v>>2]=J,v)|0);return 0}else if((F|0)==9588){l=c[3530]|0;m=c[1054]|0;n=m+(l*40&-1)+32|0;c[n>>2]=k;o=c[3530]|0;p=c[1054]|0;q=p+(o*40&-1)+36|0;c[q>>2]=0;r=c[3530]|0;s=c[1054]|0;t=s+(r*40&-1)|0;a[t]=1;u=c[3530]|0;i=f;return u|0}else if((F|0)==9557){uf(c[3530]|0,154312,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((F|0)==9587){l=c[3530]|0;m=c[1054]|0;n=m+(l*40&-1)+32|0;c[n>>2]=k;o=c[3530]|0;p=c[1054]|0;q=p+(o*40&-1)+36|0;c[q>>2]=0;r=c[3530]|0;s=c[1054]|0;t=s+(r*40&-1)|0;a[t]=1;u=c[3530]|0;i=f;return u|0}return 0}function jm(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;f=i;i=i+8|0;g=f|0;h=c[b>>2]|0;j=h+e|0;k=j;while(1){l=k+1|0;m=a[l]|0;if((m<<24>>24|0)==0|(m<<24>>24|0)==96){break}else{k=l}}m=l-j|0;n=ut(m)|0;do{if((n|0)==0){gk();o=ut(m)|0;if((o|0)!=0){p=o;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=121896,v)|0)}else{p=n}}while(0);n=h+(e+1|0)|0;uF(p|0,n|0,m|0);if((uA(n|0)|0)>>>0>=m>>>0){a[p+((l|0)==(j|0)?0:m-1|0)|0]=0}do{if((a[l]|0)==0){q=0;r=0}else{m=k+2|0;j=(uA(m|0)|0)+1|0;if(j>>>0<=1){q=j;r=0;break}n=ut(j)|0;do{if((n|0)==0){gk();h=ut(j)|0;if((h|0)!=0){s=h;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=104824,v)|0)}else{s=n}}while(0);uB(s|0,m|0);q=j;r=s}}while(0);eo(p,g);uu(p);p=c[g>>2]|0;g=0;s=e;while(1){e=g+1|0;k=a[p+g|0]|0;if((k<<24>>24|0)==0){break}else if((k<<24>>24|0)==10|(k<<24>>24|0)==13){t=s}else{a[(c[b>>2]|0)+s|0]=k;t=s+1|0}if((t|0)!=(c[d>>2]|0)){g=e;s=t;continue}dG();g=e;s=t}a[(c[b>>2]|0)+s|0]=0;uu(p);if((r|0)==0){a[25280]=0;i=f;return}p=s+q|0;if(p>>>0>(c[d>>2]|0)>>>0){do{dG();}while(p>>>0>(c[d>>2]|0)>>>0)}uB((c[b>>2]|0)+s|0,r|0);uu(r);a[25280]=0;i=f;return}function jn(b){b=b|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0.0;e=i;i=i+8|0;f=e|0;a[(c[1054]|0)+((c[3530]|0)*40&-1)|0]=0;c[(c[1054]|0)+((c[3530]|0)*40&-1)+8>>2]=1;g=0;while(1){j=a[b+g|0]|0;if(((j&255)-48|0)>>>0<10){g=g+1|0}else{break}}if(j<<24>>24==46){c[(c[1054]|0)+((c[3530]|0)*40&-1)+8>>2]=2;k=g;while(1){l=k+1|0;m=a[b+l|0]|0;if(((m&255)-48|0)>>>0<10){k=l}else{n=l;o=m;break}}}else{n=g;o=j}if((o<<24>>24|0)==101|(o<<24>>24|0)==69){c[(c[1054]|0)+((c[3530]|0)*40&-1)+8>>2]=2;o=n+1|0;j=a[b+o|0]|0;if((j<<24>>24|0)==45|(j<<24>>24|0)==43){g=n+2|0;p=g;q=a[b+g|0]|0}else{p=o;q=j}if(((q&255)-48|0)>>>0<10){q=p;while(1){j=q+1|0;if(((d[b+j|0]|0)-48|0)>>>0<10){q=j}else{r=j;break}}}else{q=(c[1054]|0)+((c[3530]|0)*40&-1)+32|0;c[q>>2]=(c[q>>2]|0)+p;uf(c[3530]|0,97936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}else{r=n}n=c[3530]|0;p=c[1054]|0;do{if((c[p+(n*40&-1)+8>>2]|0)==1){c[b0()>>2]=0;q=aE(b|0,f|0,0)|0;j=(c[b0()>>2]|0)==0;o=c[3530]|0;if(!j){uh(o,91904,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[(c[1054]|0)+((c[3530]|0)*40&-1)+8>>2]=2;s=c[3530]|0;t=c[1054]|0;break}c[(c[1054]|0)+(o*40&-1)+16>>2]=q;u=(c[f>>2]|0)-b|0;i=e;return u|0}else{s=n;t=p}}while(0);h[t+(s*40&-1)+24>>3]=0.0;w=+uz(b,0);h[(c[1054]|0)+((c[3530]|0)*40&-1)+16>>3]=w;u=r;i=e;return u|0}function jo(){var d=0,e=0,f=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aH=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aZ=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,bf=0,bg=0,bh=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0,bB=0,bC=0,bD=0,bE=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bM=0,bN=0,bO=0,bQ=0,bR=0,bS=0,bT=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0,b7=0,b8=0,b9=0,cb=0,cc=0,cd=0,ce=0,cg=0,ch=0,ci=0,cj=0,ck=0,cl=0,cm=0,cn=0,co=0,cp=0,cq=0,cr=0,cs=0,ct=0,cu=0,cv=0,cw=0,cx=0,cy=0,cA=0,cC=0,cD=0,cE=0,cF=0,cG=0,cH=0,cI=0,cJ=0,cK=0,cL=0,cM=0,cN=0,cO=0,cP=0,cQ=0,cR=0,cT=0,cU=0,cV=0,cW=0,cX=0,cY=0,cZ=0,c_=0,c$=0,c0=0,c1=0,c2=0,c3=0,c4=0,c5=0,c6=0,c7=0,c8=0,c9=0,da=0,dc=0,dd=0,de=0,df=0,dg=0,dh=0,di=0,dj=0,dk=0,dm=0,dn=0,dp=0,dq=0,dr=0,ds=0,dt=0,du=0,dv=0,dw=0,dx=0,dy=0,dz=0,dA=0,dB=0,dC=0,dD=0,dE=0,dF=0,dG=0,dH=0,dI=0,dJ=0,dK=0,dL=0,dM=0,dN=0,dO=0,dP=0,dQ=0,dR=0,dS=0,dT=0,dU=0,dV=0,dW=0,dX=0,dY=0,dZ=0,d_=0,d$=0,d0=0,d1=0,d2=0,d3=0,d4=0,d5=0,d6=0,d7=0,d8=0,d9=0,eb=0,ec=0,ed=0,ee=0,ef=0,eg=0,eh=0,ei=0,ej=0,ek=0,el=0,em=0,en=0,eo=0,ep=0,eq=0,er=0,es=0,et=0,eu=0,ev=0,ew=0,ex=0,eA=0,eB=0,eD=0,eE=0,eF=0,eG=0,eH=0,eI=0,eJ=0,eK=0,eL=0,eM=0,eN=0,eO=0,eQ=0,eR=0,eS=0,eT=0,eU=0,eV=0,eW=0,eX=0,eY=0,eZ=0,e_=0,e$=0,e0=0,e2=0,e3=0,e4=0,e5=0,e6=0,e8=0,e9=0,fa=0,fb=0,fc=0,fd=0,fe=0,ff=0,fg=0,fh=0,fi=0,fj=0,fk=0,fl=0,fm=0,fn=0,fo=0,fp=0,fq=0,fr=0,fs=0,ft=0,fu=0,fv=0,fw=0,fx=0,fy=0,fz=0,fA=0,fB=0,fC=0,fD=0,fE=0,fF=0,fG=0,fH=0,fI=0,fJ=0,fK=0,fL=0,fM=0,fN=0,fO=0,fP=0,fQ=0,fS=0,fT=0,fU=0,fV=0,fW=0,fX=0,fY=0,fZ=0,f_=0,f$=0,f0=0,f1=0,f2=0,f3=0,f4=0,f5=0,f6=0,f7=0,f8=0,f9=0,ga=0,gb=0,gc=0,gd=0,ge=0,gf=0,gg=0,gh=0,gi=0,gj=0,gl=0,gm=0,gn=0,go=0,gp=0,gq=0,gs=0,gt=0,gu=0,gv=0,gw=0,gx=0,gy=0,gz=0,gA=0,gB=0,gC=0,gD=0,gE=0,gF=0,gG=0,gH=0,gI=0,gJ=0,gK=0,gL=0,gM=0,gN=0,gO=0,gP=0,gQ=0,gR=0,gS=0,gT=0,gU=0,gV=0,gW=0,gX=0.0,gY=0,gZ=0,g_=0,g$=0,g0=0,g1=0,g2=0,g3=0,g4=0,g5=0,g6=0,g7=0,g8=0,g9=0,ha=0,hb=0,hc=0.0,hd=0,he=0,hf=0,hg=0,hh=0,hi=0,hj=0,hk=0,hl=0,hm=0,hn=0,ho=0,hp=0,hq=0,hr=0,hs=0,ht=0,hu=0,hv=0,hw=0,hx=0,hy=0,hA=0,hB=0,hC=0,hF=0,hG=0,hK=0,hN=0,hP=0,hQ=0,hR=0,hS=0,hT=0,hU=0,hV=0,hW=0,hY=0,hZ=0,h_=0,h$=0,h0=0,h1=0,h2=0,h3=0,h4=0,h5=0,h6=0,h7=0,h8=0,h9=0,ia=0,ib=0,ic=0,id=0,ie=0,ig=0,ih=0,ii=0,ij=0,ik=0,il=0,im=0,io=0,ip=0,iq=0,ir=0,iu=0,iv=0,iy=0,iz=0,iA=0,iB=0,iC=0,iD=0,iE=0,iF=0,iG=0,iH=0,iI=0,iJ=0,iK=0,iL=0,iM=0,iN=0,iP=0,iR=0,iS=0,iT=0,iU=0,iV=0,iW=0,iX=0,iZ=0,i_=0,i$=0,i0=0,i1=0,i2=0,i3=0,i4=0,i5=0,i6=0,i7=0,i8=0,ja=0,jb=0,jc=0,jd=0,je=0,jf=0,jg=0,jh=0,ji=0,jj=0,jk=0,jl=0,jm=0,jn=0,jo=0,jy=0,jz=0,jA=0,jB=0,jC=0,jD=0,jE=0,jF=0,jG=0,jH=0,jI=0,jJ=0,jK=0,jL=0,jM=0,jN=0,jO=0,jP=0,jQ=0,jR=0,jS=0,jT=0,jU=0,jV=0,jW=0,jX=0,jY=0,jZ=0,j_=0,j$=0,j0=0,j1=0,j2=0,j3=0,j4=0,j5=0,j6=0,j7=0,j8=0,j9=0,ka=0,kb=0,kc=0,kd=0,ke=0,kf=0,kg=0,kh=0,ki=0,kj=0,kk=0,kl=0,km=0,kn=0,ko=0,kp=0,kq=0,kr=0,ks=0,kt=0,ku=0,kv=0,kw=0,kx=0,ky=0,kz=0,kA=0,kB=0,kC=0,kD=0,kE=0,kF=0,kG=0,kH=0,kI=0,kJ=0,kK=0,kL=0,kM=0,kN=0,kO=0,kP=0,kQ=0,kR=0,kS=0,kT=0,kU=0,kV=0,kW=0,kX=0,kY=0,kZ=0,k_=0,k$=0,k0=0,k1=0,k2=0,k3=0.0,k4=0,k5=0,k6=0,k7=0,k8=0,k9=0,la=0.0,lb=0.0,lc=0,ld=0,le=0,lf=0,lg=0,lh=0,li=0,lk=0,lm=0,ln=0,lo=0,lp=0,lq=0,lr=0,lt=0,lu=0,lv=0,lw=0,lx=0,ly=0,lA=0,lB=0,lC=0,lD=0,lE=0,lF=0,lG=0,lH=0,lI=0,lJ=0,lK=0,lL=0,lM=0,lN=0,lO=0,lP=0,lQ=0,lR=0,lS=0,lT=0,lU=0,lV=0.0,lW=0,lX=0,lY=0,lZ=0,l_=0,l$=0,l0=0,l1=0,l2=0,l3=0,l4=0,l5=0,l6=0,l7=0,l8=0,l9=0,ma=0,mb=0,mc=0,md=0,me=0,mf=0,mg=0,mh=0,mi=0,mj=0,mk=0.0,ml=0,mm=0,mn=0,mo=0,mp=0.0,mq=0,mr=0,ms=0,mt=0,mu=0,mv=0,mw=0,mx=0,my=0,mz=0,mA=0.0,mB=0,mC=0.0,mD=0,mE=0,mF=0.0,mG=0,mH=0,mI=0.0,mJ=0.0,mK=0,mL=0,mM=0.0,mN=0,mO=0,mP=0,mQ=0,mR=0,mS=0,mT=0,mU=0,mV=0,mW=0,mX=0,mY=0,mZ=0,m_=0,m$=0,m0=0,m1=0,m2=0.0,m3=0.0,m4=0.0,m5=0.0,m6=0,m7=0,m8=0,m9=0,na=0,nb=0.0,nc=0.0,nd=0,ne=0,nf=0,ng=0,nh=0,ni=0,nj=0,nk=0,nl=0,nm=0,nn=0,no=0,np=0,nq=0.0,nr=0.0,ns=0,nt=0,nu=0,nv=0.0,nw=0,nx=0,ny=0,nz=0.0,nA=0.0,nB=0.0,nC=0.0,nD=0.0,nE=0,nF=0,nG=0,nH=0,nI=0,nJ=0,nK=0,nL=0,nM=0,nN=0,nO=0,nP=0,nQ=0,nR=0,nS=0,nT=0,nU=0,nV=0,nW=0,nX=0,nY=0,nZ=0,n_=0,n$=0,n0=0,n1=0,n2=0,n3=0,n4=0,n5=0,n6=0,n7=0,n8=0,n9=0,oa=0,ob=0,oc=0,od=0,oe=0,of=0,og=0,oh=0,oi=0,oj=0,ok=0,ol=0,om=0,on=0,oo=0,op=0,oq=0,or=0,os=0,ot=0,ou=0,ov=0,ow=0,ox=0,oy=0,oz=0,oA=0,oB=0,oC=0,oD=0,oE=0,oF=0,oG=0,oH=0,oI=0,oJ=0,oK=0,oL=0,oM=0,oN=0,oO=0,oP=0,oQ=0,oR=0,oS=0,oT=0,oU=0,oV=0,oW=0,oX=0,oY=0,oZ=0,o_=0,o$=0,o0=0,o1=0,o2=0,o3=0,o4=0,o5=0,o6=0,o7=0,o8=0,o9=0,pa=0,pb=0,pc=0,pd=0,pe=0,pf=0,pg=0,ph=0,pi=0,pj=0,pk=0,pl=0,pm=0,pn=0,po=0,pp=0,pq=0,pr=0,ps=0,pt=0,pu=0,pv=0,pw=0,px=0,py=0,pz=0,pA=0,pB=0,pC=0,pD=0,pE=0,pF=0,pG=0,pH=0,pI=0,pJ=0,pK=0,pL=0,pM=0,pN=0,pO=0,pP=0,pQ=0,pR=0,pS=0,pT=0,pU=0,pV=0,pW=0,pX=0,pY=0,pZ=0,p_=0,p$=0,p0=0,p1=0,p2=0,p3=0,p4=0,p5=0,p6=0,p7=0,p8=0,p9=0,qa=0,qb=0,qc=0,qd=0,qe=0,qf=0,qg=0,qh=0,qi=0,qj=0,qk=0,ql=0,qm=0,qn=0,qo=0,qp=0,qq=0,qr=0,qs=0,qt=0,qu=0,qv=0,qw=0,qx=0,qy=0,qz=0,qA=0,qB=0,qC=0,qD=0,qE=0,qF=0,qG=0.0,qH=0,qI=0,qJ=0,qK=0,qL=0,qM=0,qN=0,qO=0.0,qP=0,qQ=0,qR=0,qS=0.0,qT=0,qU=0,qV=0,qW=0,qX=0,qY=0,qZ=0,q_=0,q$=0,q0=0,q1=0,q2=0,q3=0,q4=0,q5=0,q6=0.0,q7=0.0,q8=0.0,q9=0.0,ra=0.0,rb=0,rc=0,rd=0,re=0,rf=0,rg=0,rh=0,ri=0,rj=0,rk=0.0,rl=0,rm=0,rn=0,ro=0,rp=0.0,rq=0,rr=0.0,rs=0,rt=0,ru=0,rv=0,rw=0,rx=0,ry=0,rz=0,rA=0,rB=0,rC=0,rD=0,rE=0,rF=0,rG=0,rH=0,rI=0,rJ=0,rK=0,rL=0,rM=0,rN=0,rO=0,rP=0,rQ=0,rR=0.0,rS=0,rT=0,rU=0,rV=0,rW=0,rX=0,rY=0,rZ=0,r_=0,r$=0,r0=0,r1=0,r2=0,r3=0,r4=0.0,r5=0,r6=0,r7=0,r8=0,r9=0,sa=0,sb=0,sc=0,sd=0,se=0,sf=0,sg=0,sh=0,si=0,sj=0,sk=0,sl=0,sm=0,sn=0,so=0,sp=0,sq=0,sr=0,ss=0,st=0,su=0,sv=0,sw=0,sx=0,sy=0.0,sz=0.0,sA=0,sB=0,sC=0,sD=0,sE=0,sF=0,sG=0,sH=0,sI=0,sJ=0,sK=0,sL=0,sM=0,sN=0,sO=0,sP=0,sQ=0,sR=0,sS=0,sT=0,sU=0,sV=0,sW=0,sX=0,sY=0,sZ=0,s_=0,s$=0,s0=0,s1=0,s2=0,s3=0,s4=0,s5=0,s6=0,s7=0,s8=0,s9=0,ta=0,tb=0,tc=0,td=0,te=0,tf=0,tg=0,th=0,ti=0,tj=0,tk=0,tl=0,tm=0,tn=0,to=0,tp=0,tq=0,tr=0,ts=0,tt=0,tu=0,tv=0,tw=0,tx=0,ty=0,tz=0,tA=0,tB=0,tC=0,tD=0,tE=0,tF=0,tG=0.0,tH=0,tI=0,tJ=0,tK=0,tL=0,tM=0,tN=0,tO=0,tP=0,tQ=0,tR=0,tS=0,tT=0,tU=0,tV=0,tW=0,tX=0,tY=0,tZ=0,t_=0,t$=0,t0=0,t1=0,t2=0,t3=0,t4=0,t6=0,t7=0,t8=0,t9=0,ub=0,uc=0,ue=0,ug=0,ui=0,ul=0,um=0,un=0,uo=0,up=0,uv=0,uw=0,ux=0,uy=0,uC=0,uG=0,uH=0,uI=0,uJ=0,uK=0,uL=0,uM=0,uN=0,uO=0,uP=0,uQ=0,uR=0,uS=0,uT=0,uU=0,uV=0,uW=0,uX=0,uY=0,uZ=0,u_=0,u$=0,u0=0,u1=0,u2=0,u3=0,u4=0,u5=0,u6=0,u7=0.0,u8=0,u9=0,va=0,vb=0,vc=0,vd=0,ve=0.0,vf=0,vg=0,vh=0,vi=0,vj=0,vk=0,vl=0,vm=0,vn=0,vo=0,vp=0,vq=0,vr=0,vs=0,vt=0.0,vu=0,vv=0,vw=0.0,vx=0,vy=0,vz=0.0,vA=0,vB=0,vC=0.0,vD=0.0,vE=0,vF=0.0,vG=0.0,vH=0,vI=0,vJ=0,vK=0,vL=0,vM=0,vN=0,vO=0,vP=0,vQ=0,vR=0,vS=0,vT=0,vU=0.0,vV=0,vW=0.0,vX=0,vY=0,vZ=0,v_=0,v$=0,v0=0,v1=0,v2=0,v3=0,v4=0,v5=0,v6=0,v7=0,v8=0,v9=0,wa=0,wb=0.0,wc=0,wd=0,we=0,wf=0,wg=0,wh=0,wi=0,wj=0,wk=0,wl=0,wm=0,wn=0,wo=0,wp=0,wq=0,wr=0,ws=0,wt=0,wu=0,wv=0.0,ww=0.0,wx=0.0,wy=0.0,wz=0,wA=0,wB=0,wC=0,wD=0.0,wE=0,wF=0,wG=0,wH=0,wI=0,wJ=0.0,wK=0,wL=0,wM=0.0,wN=0,wO=0,wP=0,wQ=0,wR=0,wS=0,wT=0,wU=0,wV=0,wW=0,wX=0,wY=0,wZ=0,w_=0,w$=0,w0=0.0,w1=0,w2=0,w3=0,w4=0,w5=0,w6=0.0,w7=0.0,w8=0.0,w9=0.0,xa=0.0,xb=0.0,xc=0,xd=0,xe=0,xf=0,xg=0,xh=0,xi=0,xj=0,xk=0,xl=0,xm=0,xn=0,xo=0,xp=0.0,xq=0.0,xr=0.0,xs=0.0,xt=0.0,xu=0.0,xv=0,xw=0,xx=0,xy=0,xz=0,xA=0,xB=0,xC=0,xD=0,xE=0,xF=0,xG=0,xH=0,xI=0,xJ=0,xK=0,xL=0,xM=0,xN=0.0,xO=0,xP=0,xQ=0,xR=0,xS=0,xT=0,xU=0,xV=0,xW=0,xX=0,xY=0,xZ=0,x_=0.0,x$=0,x0=0,x1=0.0,x2=0.0,x3=0.0,x4=0.0,x5=0,x6=0,x7=0,x8=0,x9=0,ya=0,yb=0,yc=0,yd=0,ye=0,yf=0,yg=0,yh=0,yi=0,yj=0,yk=0,yl=0,ym=0,yn=0,yo=0,yp=0,yq=0,yr=0,ys=0,yt=0,yu=0,yv=0,yw=0,yx=0,yy=0.0,yz=0,yA=0,yB=0,yC=0,yD=0,yE=0,yF=0,yG=0,yH=0,yI=0,yJ=0,yK=0,yL=0,yM=0,yN=0,yO=0,yP=0,yQ=0,yR=0,yS=0,yT=0.0,yU=0,yV=0,yW=0,yX=0.0,yY=0,yZ=0,y_=0,y$=0,y0=0,y1=0,y2=0,y3=0,y4=0,y5=0,y6=0,y7=0,y8=0,y9=0,za=0,zb=0,zc=0.0,zd=0,ze=0,zf=0,zg=0,zh=0,zi=0,zj=0,zk=0.0,zl=0.0,zm=0,zn=0,zo=0,zp=0,zq=0,zr=0,zs=0,zt=0,zu=0,zv=0,zw=0,zx=0,zy=0,zz=0,zA=0,zB=0,zC=0.0,zD=0,zE=0,zF=0,zG=0,zH=0,zI=0,zJ=0,zK=0,zL=0,zM=0,zN=0,zO=0,zP=0,zQ=0,zR=0,zS=0,zT=0,zU=0.0,zV=0,zW=0,zX=0,zY=0,zZ=0,z_=0,z$=0,z0=0,z1=0,z2=0,z3=0,z4=0,z5=0,z6=0,z7=0,z8=0,z9=0,Aa=0,Ab=0,Ac=0,Ad=0,Ae=0,Af=0,Ag=0,Ah=0,Ai=0,Aj=0,Ak=0,Al=0,Am=0,An=0,Ao=0,Ap=0,Aq=0,Ar=0,As=0,At=0,Au=0,Av=0,Aw=0,Ax=0,Ay=0,Az=0,AA=0,AB=0,AC=0,AD=0.0,AE=0,AF=0,AG=0,AH=0,AI=0,AJ=0,AK=0,AL=0.0,AM=0,AN=0,AO=0,AP=0,AQ=0,AR=0,AS=0,AT=0,AU=0,AV=0,AW=0,AX=0,AY=0,AZ=0,A_=0,A$=0,A0=0,A1=0,A2=0,A3=0,A4=0,A5=0,A6=0,A7=0,A8=0,A9=0,Ba=0,Bb=0,Bc=0,Bd=0,Be=0,Bf=0,Bg=0.0,Bh=0,Bi=0,Bj=0,Bk=0,Bl=0,Bm=0.0,Bn=0,Bo=0,Bp=0,Bq=0,Br=0,Bs=0,Bt=0,Bu=0,Bv=0,Bw=0,Bx=0,By=0,Bz=0,BA=0,BB=0,BC=0.0,BD=0,BE=0,BF=0,BG=0,BH=0,BI=0,BJ=0,BK=0,BL=0,BM=0,BN=0,BO=0,BP=0,BQ=0,BR=0,BS=0.0,BT=0,BU=0,BV=0,BW=0,BX=0,BY=0,BZ=0,B_=0,B$=0,B0=0,B1=0,B2=0,B3=0,B4=0,B5=0,B6=0,B7=0,B8=0,B9=0,Ca=0,Cb=0,Cc=0,Cd=0,Ce=0,Cf=0,Cg=0,Ch=0,Ci=0,Cj=0,Ck=0,Cl=0,Cm=0,Cn=0,Co=0,Cp=0,Cq=0,Cr=0,Cs=0,Ct=0,Cu=0,Cv=0,Cw=0,Cx=0,Cy=0,Cz=0,CA=0,CB=0,CC=0,CD=0,CE=0,CF=0,CG=0,CH=0,CI=0,CJ=0,CK=0,CL=0,CM=0,CN=0,CO=0,CP=0,CQ=0,CR=0,CS=0,CT=0,CU=0,CV=0,CW=0,CX=0,CY=0,CZ=0,C_=0,C$=0,C0=0,C1=0,C2=0,C3=0.0,C4=0.0,C5=0.0,C6=0,C7=0,C8=0,C9=0,Da=0,Db=0.0,Dc=0,Dd=0,De=0,Df=0,Dg=0,Dh=0,Di=0,Dj=0,Dk=0,Dl=0,Dm=0,Dn=0,Do=0,Dp=0,Dq=0,Dr=0,Ds=0,Dt=0,Du=0,Dv=0,Dw=0,Dx=0,Dy=0,Dz=0,DA=0,DB=0,DC=0,DD=0,DE=0,DF=0,DG=0,DH=0,DI=0,DJ=0,DK=0,DL=0,DM=0,DN=0,DO=0,DP=0,DQ=0,DR=0,DS=0,DT=0,DU=0,DV=0,DW=0,DX=0,DY=0,DZ=0,D_=0,D$=0.0,D0=0.0,D1=0.0,D2=0,D3=0.0,D4=0,D5=0,D6=0,D7=0,D8=0,D9=0,Ea=0,Eb=0,Ec=0,Ed=0,Ee=0,Ef=0,Eg=0,Eh=0,Ei=0,Ej=0,Ek=0,El=0,Em=0,En=0,Eo=0,Ep=0,Eq=0,Er=0,Es=0,Et=0,Eu=0,Ev=0,Ew=0,Ex=0,Ey=0,Ez=0,EA=0,EB=0,EC=0,ED=0,EE=0,EF=0,EG=0,EH=0,EI=0,EJ=0,EK=0,EL=0,EM=0,EN=0,EO=0,EP=0,EQ=0,ER=0,ES=0,ET=0,EU=0,EV=0.0,EW=0.0,EX=0,EY=0,EZ=0,E_=0,E$=0,E0=0,E1=0,E2=0,E3=0,E4=0,E5=0,E6=0,E7=0,E8=0.0,E9=0.0,Fa=0.0,Fb=0.0,Fc=0.0,Fd=0.0,Fe=0,Ff=0,Fg=0,Fh=0.0,Fi=0,Fj=0.0,Fk=0,Fl=0,Fm=0,Fn=0.0,Fo=0.0,Fp=0,Fq=0,Fr=0,Fs=0,Ft=0,Fu=0,Fv=0,Fw=0,Fx=0,Fy=0,Fz=0;d=i;i=i+3552|0;e=d|0;f=d+24|0;j=d+48|0;k=d+72|0;l=d+96|0;n=d+120|0;o=d+144|0;p=d+168|0;q=d+192|0;r=d+216|0;s=d+240|0;t=d+264|0;u=d+288|0;x=d+312|0;y=d+336|0;z=d+360|0;A=d+384|0;B=d+408|0;C=d+432|0;D=d+456|0;E=d+480|0;F=d+504|0;G=d+544|0;H=d+560|0;I=d+584|0;J=d+608|0;K=d+632|0;L=d+656|0;M=d+680|0;N=d+704|0;O=d+728|0;R=d+752|0;S=d+776|0;T=d+968|0;U=d+992|0;V=d+1016|0;W=d+1040|0;X=d+1064|0;Y=d+1088|0;Z=d+1112|0;$=d+1136|0;ab=d+1160|0;ac=d+1184|0;ad=d+1208|0;ae=d+1216|0;af=d+1240|0;ag=d+1264|0;ah=d+1288|0;ai=d+1312|0;aj=d+1368|0;ak=d+1392|0;al=d+1424|0;am=d+1448|0;an=d+1472|0;ao=d+1496|0;ap=d+1520|0;aq=d+1544|0;ar=d+1552|0;as=d+1560|0;at=d+1568|0;au=d+1592|0;av=d+1616|0;aw=d+1640|0;ax=d+1664|0;ay=d+1688|0;aA=d+1712|0;aB=d+1736|0;aC=d+1760|0;aD=d+1784|0;aE=d+1808|0;aF=d+1832|0;aH=d+1840|0;aJ=d+1864|0;aK=d+1888|0;aL=d+1912|0;aM=d+1936|0;aN=d+1960|0;aO=d+1984|0;aP=d+2008|0;aR=d+2032|0;aS=d+2056|0;aT=d+2080|0;aU=d+2104|0;aV=d+2128|0;aW=d+2152|0;aX=d+2176|0;aZ=d+2184|0;a$=d+2192|0;a0=d+2216|0;a1=d+2240|0;a2=d+2280|0;a3=d+2304|0;a4=d+2328|0;a5=d+2352|0;a6=d+2368|0;a7=d+2392|0;a8=d+2416|0;a9=d+2424|0;ba=d+2448|0;bb=d+2472|0;bc=d+2496|0;bd=d+2528|0;bf=d+2552|0;bg=d+2576|0;bh=d+2600|0;bj=d+2624|0;bk=d+2648|0;bl=d+2672|0;bm=d+2696|0;bn=d+2720|0;bo=d+2744|0;bp=d+2768|0;bq=d+2792|0;br=d+2808|0;bs=d+2832|0;bt=d+2856|0;bu=d+2880|0;bv=d+2904|0;bw=d+2912|0;bx=d+2936|0;by=d+2944|0;bz=d+2952|0;bB=d+2960|0;bC=d+2968|0;bD=d+2976|0;bE=d+3e3|0;bG=d+3024|0;bH=d+3048|0;bI=d+3072|0;bJ=d+3096|0;bK=d+3120|0;bL=d+3144|0;bM=d+3168|0;bN=d+3192|0;bO=d+3216|0;bQ=d+3240|0;bR=d+3264|0;bS=d+3288|0;bT=d+3312|0;bV=d+3336|0;bW=d+3360|0;bX=d+3384|0;bY=d+3408|0;bZ=d+3432|0;b_=d+3456|0;b0=d+3480|0;b1=d+3504|0;b2=d+3528|0;b3=(c[13898]|0)+1|0;c[13898]=b3;b4=c[1054]|0;b5=c[b4+(b3*40&-1)+32>>2]|0;b6=c[10036]|0;do{if((a[b6+b5|0]|0)==110){if((a[b6+(b5+1|0)|0]|0)!=111){b7=6;break}if((a[37400]&1)==0){b8=b3;b9=b4;cb=b5}else{uh(b3,153488,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cc=c[13898]|0;cd=c[1054]|0;b8=cc;b9=cd;cb=c[cd+(cc*40&-1)+32>>2]|0}c[b9+(b8*40&-1)+32>>2]=cb+2;cc=(c[1054]|0)+((c[13898]|0)*40&-1)+36|0;c[cc>>2]=(c[cc>>2]|0)-2;c[13898]=(c[13898]|0)-1;t5()}else{b7=6}}while(0);L7:do{if((b7|0)==6){cb=iw()|0;c[6074]=cb;do{if((cb|0)!=0){if((a[cb+38|0]&1)==0){break}b8=c[13898]|0;b9=c[8272]|0;L12:do{if((b8|0)<(b9|0)){b3=c[1054]|0;b5=b8;while(1){L16:do{if((a[b3+(b5*40&-1)|0]&1)!=0){b4=c[b3+(b5*40&-1)+36>>2]|0;b6=b3+(b5*40&-1)+32|0;cc=c[10036]|0;cd=0;while(1){if((cd|0)>=(b4|0)){break}if((a[cc+((c[b6>>2]|0)+cd|0)|0]|0)==(a[cd+103664|0]|0)){cd=cd+1|0}else{break L16}}if((cd|0)==1){ce=cb;break L12}}}while(0);b6=b5+1|0;c[13898]=b6;if((b6|0)<(b9|0)){b5=b6}else{ce=cb;break}}}else{ce=cb}}while(0);while(1){b9=c[ce>>2]|0;uu(c[ce+12>>2]|0);uu(ce);if((b9|0)==0){break}else{ce=b9}}i=d;return}}while(0);cb=c[13898]|0;b9=b2|0;b8=b2+8|0;b5=b0|0;b3=b1|0;b6=b_|0;cc=b_+8|0;b4=bZ|0;cg=bZ+8|0;ch=bY|0;ci=bY+8|0;cj=bX|0;ck=bX+8|0;cl=bW|0;cm=bW+8|0;cn=bT|0;co=bT+8|0;cp=bS|0;cq=bS+8|0;cr=bR|0;cs=bR+8|0;ct=bQ|0;cu=bQ+8|0;cv=bO|0;cw=bO+8|0;cx=bN|0;cy=bN+8|0;cA=bM|0;cC=bM+8|0;cD=bV|0;cE=bV+8|0;cF=bI|0;cG=bI+8|0;cH=bH|0;cI=bH+8|0;cJ=bG|0;cK=bG+8|0;cL=bL|0;cM=bL+8|0;cN=bK|0;cO=bK+8|0;cP=bJ|0;cQ=bJ+8|0;cR=bE|0;cT=bE+8|0;cU=bD|0;cV=bD+8|0;cW=bx|0;cX=bw|0;cY=bw+8|0;cZ=bu|0;c_=bu+8|0;c$=bt|0;c0=bt+8|0;c1=bs|0;c2=bs+8|0;c3=br|0;c4=br+8|0;c5=bq;c6=bp|0;c7=bp+8|0;c8=bo|0;c9=bo+8|0;da=bn|0;dc=bn+8|0;dd=bm|0;de=bm+8|0;df=bl|0;dg=bl+8|0;dh=bk|0;di=bk+8|0;dj=bj|0;dk=bj+8|0;dm=bq|0;dn=bq+8|0;dp=bh|0;dq=bh+8|0;dr=bg|0;ds=bg+8|0;dt=bf|0;du=bf+8|0;dv=bd|0;dw=bd+8|0;dx=bc|0;dy=bb|0;dz=bb+8|0;dA=dz;dB=dz;dz=bb+8|0;dC=a9|0;dD=a9+8|0;dE=ba|0;dF=ba+8|0;dG=a7|0;dH=a7+8|0;dI=a6|0;dJ=a6+8|0;dK=a5|0;dL=a4|0;dM=a4+8|0;dN=a2|0;dO=a2+8|0;dP=a3|0;dQ=a3+8|0;dR=a0+4|0;dS=a0+8|0;dT=aW+4|0;dU=aW+8|0;dV=aT|0;dW=aT+8|0;dX=aS|0;dY=aS+8|0;dZ=aP|0;d_=aP+8|0;d$=aO|0;d0=aO+8|0;d1=aR|0;d2=aR+8|0;d3=aV|0;d4=aV+8|0;d5=a$|0;d6=a$+8|0;d7=aM|0;d8=aM+8|0;d9=aL|0;eb=aL+8|0;ec=aK|0;ed=aK+8|0;ee=aN|0;ef=aN+8|0;eg=aH|0;eh=aH+8|0;ei=aJ|0;ej=aJ+8|0;ek=aE|0;el=aE+8|0;em=aC|0;en=aC+8|0;eo=aB|0;ep=aB+8|0;eq=aD|0;er=aD+8|0;es=aA|0;et=aA+8|0;eu=ay|0;ev=ay+8|0;ew=ax|0;ex=ax+8|0;eA=aw|0;eB=aw+8|0;eD=av|0;eE=av+8|0;eF=at|0;eG=at+8|0;eH=au|0;eI=au+8|0;eJ=ap|0;eK=ap+8|0;eL=ao|0;eM=ao+8|0;eN=an|0;eO=an+8|0;eQ=am|0;eR=am+8|0;eS=al|0;eT=al+8|0;eU=aj|0;eV=aj+8|0;eW=ak|0;eX=ak+8|0;eY=ak+16|0;eZ=ak+24|0;e_=ai|0;e$=ah|0;e0=ah+8|0;e2=ag|0;e3=ag+8|0;e4=af|0;e5=af+8|0;e6=ae|0;e8=ae+8|0;e9=ac|0;fa=ac+8|0;fb=ab|0;fc=ab+8|0;fd=$|0;fe=$+8|0;ff=Z|0;fg=Z+8|0;fh=Y|0;fi=Y+8|0;fj=V|0;fk=V+8|0;fl=X|0;fm=X+8|0;fn=W|0;fo=W+8|0;fp=U|0;fq=U+8|0;fr=T|0;fs=T+8|0;ft=S;fu=O|0;fv=O+8|0;fw=R|0;fx=R+8|0;fy=S+144|0;fz=S+72|0;fA=L|0;fB=L+8|0;fC=N|0;fD=N+8|0;fE=K|0;fF=K+8|0;fG=M|0;fH=M+8|0;fI=J|0;fJ=J+8|0;fK=I|0;fL=I+8|0;fM=H|0;fN=H+8|0;fO=F;fP=G;fQ=E|0;fS=E+8|0;fT=D|0;fU=D+8|0;fV=C|0;fW=C+8|0;fX=B|0;fY=B+8|0;fZ=z|0;f_=z+8|0;f$=A|0;f0=A+8|0;f1=y|0;f2=y+8|0;f3=x|0;f4=x+8|0;f5=t|0;f6=t+8|0;f7=u|0;f8=u+8|0;f9=s|0;ga=s+8|0;gb=q|0;gc=q+8|0;gd=p|0;ge=p+8|0;gf=o|0;gg=o+8|0;gh=n|0;gi=n+8|0;gj=l|0;gl=l+8|0;gm=k|0;gn=k+8|0;go=f|0;gp=f+8|0;gq=j|0;gs=j+8|0;gt=e|0;gu=e+8|0;gv=c[13898]|0;L27:while(1){gw=c[1054]|0;gx=c[gw+(gv*40&-1)+36>>2]|0;gy=c[gw+(gv*40&-1)+32>>2]|0;gz=(a[gw+(gv*40&-1)|0]&1)==0;gA=(gx|0)>0;gB=c[10036]|0;gC=21960;gD=96232;L29:while(1){L31:do{if(!gz){if(gA){gE=0;gF=0;gG=gy;while(1){gH=a[gD+gE|0]|0;if(gH<<24>>24==(a[gB+(gE+gG|0)|0]|0)){gI=gG;gJ=gF}else{if(gH<<24>>24!=36){break L31}gI=gG-1|0;gJ=1}gK=gE+1|0;if((gK|0)<(gJ+gx|0)){gE=gK;gF=gJ;gG=gI}else{break}}if((gJ|0)==0){gL=gK}else{gM=gC;break L29}}else{gL=0}gG=a[gD+gL|0]|0;if((gG<<24>>24|0)==36|(gG<<24>>24|0)==0){gM=gC;break L29}}}while(0);gG=gC+8|0;gF=c[gG>>2]|0;if((gF|0)==0){gM=gG;break}else{gC=gG;gD=gF}}gD=c[gM+4>>2]|0;L44:do{if((gD|0)==3){gN=gv+1|0;c[13898]=gN;L46:do{if((gN|0)<(c[8272]|0)){if((a[gw+(gN*40&-1)|0]&1)==0){b7=5107;break L27}gC=c[gw+(gN*40&-1)+36>>2]|0;gx=gw+(gN*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(gC|0)){b7=34;break}gA=c[gx>>2]|0;if((a[gB+(gA+gy|0)|0]|0)==(a[gy+103664|0]|0)){gy=gy+1|0}else{gO=gA;break}}if((b7|0)==34){b7=0;if((gy|0)==1){b7=36;break}gO=c[gx>>2]|0}if((gC|0)>0){gP=0;gQ=0;gR=gO}else{b7=5108;break L27}while(1){if((a[gP+123160|0]|0)==(a[gB+(gP+gR|0)|0]|0)){gS=gR;gT=gQ}else{if((gP|0)!=1){gU=0;gV=0;gW=gO;break}gS=gR-1|0;gT=1}gA=gP+1|0;if((gA|0)<(gT+gC|0)){gP=gA;gQ=gT;gR=gS}else{b7=42;break}}do{if((b7|0)==42){b7=0;if((gT|0)==0){if(!((gP|0)==7|(gP|0)==0)){gU=0;gV=0;gW=gO;break}}c[13898]=gv+2;h[9040]=1.0;gX=1.0;break L46}}while(0);while(1){if((a[gU+123032|0]|0)==(a[gB+(gU+gW|0)|0]|0)){gY=gW;gZ=gV}else{if((gU|0)!=1){b7=5109;break L27}gY=gW-1|0;gZ=1}gx=gU+1|0;if((gx|0)<(gZ+gC|0)){gU=gx;gV=gZ;gW=gY}else{break}}if((gZ|0)==0){if(!((gU|0)==7|(gU|0)==0)){b7=5110;break L27}}c[13898]=gv+2;h[9040]=.017453292519943295;gX=.017453292519943295}else{b7=36}}while(0);if((b7|0)==36){b7=0;h[9040]=1.0;gX=1.0}if((a[30528]&1)==0|(c[17539]|0)==0){break}h[8773]=0.0;h[8774]=6.283185307179586/gX}else if((gD|0)==4){gC=gv+1|0;c[13898]=gC;gx=c[gw+(gC*40&-1)+36>>2]|0;gy=c[gw+(gC*40&-1)+32>>2]|0;gA=(a[gw+(gC*40&-1)|0]&1)==0;gz=(gx|0)>0&(gA^1);L82:do{if(gz){gF=0;gG=0;gE=gy;while(1){if((a[gF+124752|0]|0)==(a[gB+(gF+gE|0)|0]|0)){g_=gE;g$=gG}else{if((gF|0)!=4){b7=62;break L82}g_=gE-1|0;g$=1}gH=gF+1|0;if((gH|0)<(g$+gx|0)){gF=gH;gG=g$;gE=g_}else{break}}if((g$|0)!=0){b7=102;break}if((gF|0)==3|(gF|0)==8){b7=102}else{b7=62}}else{b7=62}}while(0);L91:do{if((b7|0)==62){b7=0;gE=(c[8272]|0)>(gC|0);L93:do{if(gE){if(gA){break}else{g0=0}while(1){if((g0|0)>=(gx|0)){b7=66;break}if((a[gB+(g0+gy|0)|0]|0)==(a[g0+150856|0]|0)){g0=g0+1|0}else{g1=0;break}}if((b7|0)==66){b7=0;if((g0|0)==5){b7=102;break L91}else{g1=0}}while(1){if((g1|0)>=(gx|0)){b7=69;break}if((a[gB+(g1+gy|0)|0]|0)==(a[g1+188576|0]|0)){g1=g1+1|0}else{g2=0;break}}if((b7|0)==69){b7=0;if((g1|0)==4){b7=102;break L91}else{g2=0}}while(1){if((g2|0)>=(gx|0)){b7=72;break}if((a[gB+(g2+gy|0)|0]|0)==(a[g2+130496|0]|0)){g2=g2+1|0}else{g3=0;break}}if((b7|0)==72){b7=0;if((g2|0)==4){b7=102;break L91}else{g3=0}}while(1){if((g3|0)>=(gx|0)){b7=75;break}if((a[gB+(g3+gy|0)|0]|0)==(a[g3+187176|0]|0)){g3=g3+1|0}else{g4=0;break}}if((b7|0)==75){b7=0;if((g3|0)==2){b7=102;break L91}else{g4=0}}while(1){if((g4|0)>=(gx|0)){b7=78;break}if((a[gB+(g4+gy|0)|0]|0)==(a[g4+186688|0]|0)){g4=g4+1|0}else{g5=0;break}}if((b7|0)==78){b7=0;if((g4|0)==3){b7=102;break L91}else{g5=0}}while(1){if((g5|0)>=(gx|0)){b7=81;break}if((a[gB+(g5+gy|0)|0]|0)==(a[g5+128e3|0]|0)){g5=g5+1|0}else{g6=0;break}}if((b7|0)==81){b7=0;if((g5|0)==6){b7=102;break L91}else{g6=0}}while(1){if((g6|0)>=(gx|0)){b7=84;break}if((a[gB+(g6+gy|0)|0]|0)==(a[g6+127904|0]|0)){g6=g6+1|0}else{g7=0;break}}if((b7|0)==84){b7=0;if((g6|0)==5){b7=102;break L91}else{g7=0}}while(1){if((g7|0)>=(gx|0)){b7=87;break}if((a[gB+(g7+gy|0)|0]|0)==(a[g7+124464|0]|0)){g7=g7+1|0}else{g8=0;break}}if((b7|0)==87){b7=0;if((g7|0)==2){b7=102;break L91}else{g8=0}}while(1){if((g8|0)>=(gx|0)){break}if((a[gB+(g8+gy|0)|0]|0)==(a[g8+176728|0]|0)){g8=g8+1|0}else{b7=91;break L93}}if((g8|0)==10){b7=102;break L91}else{b7=91}}else{b7=91}}while(0);L139:do{if((b7|0)==91){b7=0;L141:do{if(gz){gF=0;cd=0;gG=gy;while(1){if((a[gF+124344|0]|0)==(a[gB+(gF+gG|0)|0]|0)){g9=gG;ha=cd}else{if((gF|0)!=4){break L141}g9=gG-1|0;ha=1}gH=gF+1|0;if((gH|0)<(ha+gx|0)){gF=gH;cd=ha;gG=g9}else{break}}if((ha|0)!=0){b7=102;break L91}if((gF|0)==3|(gF|0)==5){b7=102;break L91}}}while(0);if(gA|gE^1){break}else{hb=0}while(1){if((hb|0)>=(gx|0)){break}if((a[gB+(hb+gy|0)|0]|0)==(a[hb+150816|0]|0)){hb=hb+1|0}else{break L139}}if((hb|0)==6){b7=102;break L91}}}while(0);gE=is(b2)|0;gG=c[gE>>2]|0;if((gG|0)==1){hc=+(c[gE+8>>2]|0)}else if((gG|0)==2){hc=+h[gE+8>>3]}else if((gG|0)==3){hc=+uz(c[gE+8>>2]|0,0)}else{b7=109;break L27}if((c[b9>>2]|0)==3){uu(c[b8>>2]|0);c[b9>>2]=1}hd=~~hc;b7=113}}while(0);L164:do{if((b7|0)==102){b7=0;gy=c[10826]|0;if((gy|0)==0){he=1;hf=0;hg=0;b7=119;break}else{hh=1;hi=gy}while(1){if((c[hi+4>>2]|0)!=(hh|0)){hd=hh;b7=113;break L164}gy=hh+1|0;gx=c[hi>>2]|0;if((gx|0)==0){hd=gy;b7=113;break}else{hh=gy;hi=gx}}}}while(0);L169:do{if((b7|0)==113){b7=0;if((hd|0)<1){b7=114;break L27}gx=c[10826]|0;if((gx|0)==0){he=hd;hf=0;hg=0;b7=119;break}else{hj=0;hk=gx}while(1){hl=c[hk+4>>2]|0;if((hd|0)<=(hl|0)){break}gx=c[hk>>2]|0;if((gx|0)==0){he=hd;hf=hk;hg=0;b7=119;break L169}else{hj=hk;hk=gx}}if((hd|0)==(hl|0)){hm=hk}else{he=hd;hf=hj;hg=hk;b7=119}}}while(0);if((b7|0)==119){b7=0;gx=ut(208)|0;if((gx|0)==0){gk();gy=ut(208)|0;if((gy|0)==0){b7=121;break L27}else{hn=gy}}else{hn=gx}gx=hn;if((hf|0)==0){c[10826]=gx}else{c[hf>>2]=gx}c[hn+4>>2]=he;c[hn>>2]=hg;uE(hn+8|0,0,80);c[hn+96>>2]=-1;c[hn+100>>2]=0;gy=hn+104|0;c[gy>>2]=c[12872];c[gy+4>>2]=c[12873];c[gy+8>>2]=c[12874];c[gy+12>>2]=c[12875];c[gy+16>>2]=c[12876];c[gy+20>>2]=c[12877];c[gy+24>>2]=c[12878];c[gy+28>>2]=c[12879];c[gy+32>>2]=c[12880];c[gy+36>>2]=c[12881];c[gy+40>>2]=c[12882];c[gy+44>>2]=c[12883];c[gy+48>>2]=c[12884];c[gy+52>>2]=c[12885];c[hn+160>>2]=1;h[hn+168>>3]=0.0;c[hn+176>>2]=0;h[hn+184>>3]=15.0;h[hn+192>>3]=90.0;c[hn+200>>2]=0;hm=gx}gx=c[13898]|0;gy=c[8272]|0;if((gx|0)>=(gy|0)){break}gA=hm+96|0;gz=hm+88|0;gC=hm+48|0;gE=hm+8|0;gG=0;cd=0;gH=gx;gx=gy;while(1){gy=cd;ho=gH;hp=gx;L189:while(1){hq=ho;hr=hp;L191:while(1){hs=c[1054]|0;L193:do{if((a[hs+(hq*40&-1)|0]&1)!=0){ht=c[hs+(hq*40&-1)+36>>2]|0;hu=hs+(hq*40&-1)+32|0;hv=c[10036]|0;hw=0;while(1){if((hw|0)>=(ht|0)){b7=134;break}if((a[hv+((c[hu>>2]|0)+hw|0)|0]|0)==(a[hw+103664|0]|0)){hw=hw+1|0}else{break}}if((b7|0)==134){b7=0;if((hw|0)==1){break L44}}if((hr|0)>(hq|0)){hx=0}else{break}while(1){if((hx|0)>=(ht|0)){b7=138;break}if((a[hv+((c[hu>>2]|0)+hx|0)|0]|0)==(a[hx+188576|0]|0)){hx=hx+1|0}else{hy=0;break}}if((b7|0)==138){b7=0;if((hx|0)==4){break L189}else{hy=0}}while(1){if((hy|0)>=(ht|0)){b7=150;break}if((a[hv+((c[hu>>2]|0)+hy|0)|0]|0)==(a[hy+187176|0]|0)){hy=hy+1|0}else{hA=0;break}}if((b7|0)==150){b7=0;if((hy|0)==2){break L191}else{hA=0}}while(1){if((hA|0)>=(ht|0)){break}if((a[hv+((c[hu>>2]|0)+hA|0)|0]|0)==(a[hA+186688|0]|0)){hA=hA+1|0}else{break L193}}if((hA|0)==3){break L191}}}while(0);hL(gA,1);gF=c[13898]|0;hw=c[8272]|0;hB=(gF|0)<(hw|0);if((hq|0)==(gF|0)&hB){hC=c[1054]|0;if((a[hC+(hq*40&-1)|0]&1)==0){b7=5115;break L27}hF=c[hC+(hq*40&-1)+36>>2]|0;hG=hC+(hq*40&-1)+32|0;hC=c[10036]|0;hK=0;while(1){if((hK|0)>=(hF|0)){break}if((a[hC+((c[hG>>2]|0)+hK|0)|0]|0)==(a[hK+103664|0]|0)){hK=hK+1|0}else{b7=5116;break L27}}if((hK|0)!=1){b7=5117;break L27}}if(hB){hq=gF;hr=hw}else{break L44}}if(gy){b7=5119;break L27}else{hN=0}while(1){if((hN|0)>=(ht|0)){b7=157;break}if((a[hv+((c[hu>>2]|0)+hN|0)|0]|0)==(a[hN+186688|0]|0)){hN=hN+1|0}else{hP=0;break}}if((b7|0)==157){b7=0;hP=(hN|0)==3&1}a[gz]=hP;hQ=(c[13898]|0)+1|0;c[13898]=hQ;if((hQ|0)>=(c[8272]|0)){b7=5113;break L27}hG=c[1054]|0;L232:do{if((a[hG+(hQ*40&-1)|0]&1)!=0){hC=c[hG+(hQ*40&-1)+36>>2]|0;hF=hG+(hQ*40&-1)+32|0;hR=c[10036]|0;hS=0;while(1){if((hS|0)>=(hC|0)){break}if((a[hR+((c[hF>>2]|0)+hS|0)|0]|0)==(a[hS+103664|0]|0)){hS=hS+1|0}else{break L232}}if((hS|0)==1){b7=5114;break L27}}}while(0);dl(gC,0);hG=c[13898]|0;hF=c[8272]|0;if((hG|0)<(hF|0)){gy=1;ho=hG;hp=hF}else{break L44}}if(gG){b7=5118;break L27}hT=hq+1|0;c[13898]=hT;if((hT|0)>=(hr|0)){b7=5111;break L27}L242:do{if((a[hs+(hT*40&-1)|0]&1)!=0){hp=c[hs+(hT*40&-1)+36>>2]|0;ho=hs+(hT*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(hp|0)){break}if((a[hv+((c[ho>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{break L242}}if((hF|0)==1){b7=5112;break L27}}}while(0);dl(gE,0);ho=c[13898]|0;hp=c[8272]|0;if((ho|0)<(hp|0)){gG=1;cd=gy;gH=ho;gx=hp}else{break}}}else if((gD|0)==5){gx=gv+1|0;c[13898]=gx;L250:do{if((gx|0)<(c[8272]|0)){gH=(a[gw+(gx*40&-1)|0]&1)==0;L252:do{if(!gH){cd=c[gw+(gx*40&-1)+36>>2]|0;gG=gw+(gx*40&-1)+32|0;gE=0;while(1){if((gE|0)>=(cd|0)){b7=180;break}if((a[gB+((c[gG>>2]|0)+gE|0)|0]|0)==(a[gE+103664|0]|0)){gE=gE+1|0}else{hU=0;break}}if((b7|0)==180){b7=0;if((gE|0)==1){break L250}else{hU=0}}while(1){if((hU|0)>=(cd|0)){b7=184;break}if((a[gB+((c[gG>>2]|0)+hU|0)|0]|0)==(a[hU+150688|0]|0)){hU=hU+1|0}else{b7=185;break}}if((b7|0)==184){b7=0;if((hU|0)!=2){b7=185}}do{if((b7|0)==185){b7=0;if(gH){break L252}gG=c[gw+(gx*40&-1)+36>>2]|0;cd=gw+(gx*40&-1)+32|0;gE=0;while(1){if((gE|0)>=(gG|0)){b7=189;break}if((a[gB+((c[cd>>2]|0)+gE|0)|0]|0)==(a[gE+148800|0]|0)){gE=gE+1|0}else{break}}if((b7|0)==189){b7=0;if((gE|0)==2){break}}if(gH){break L252}cd=c[gw+(gx*40&-1)+36>>2]|0;gG=gw+(gx*40&-1)+32|0;hS=0;while(1){if((hS|0)>=(cd|0)){b7=195;break}if((a[gB+((c[gG>>2]|0)+hS|0)|0]|0)==(a[hS+125808|0]|0)){hS=hS+1|0}else{break}}do{if((b7|0)==195){b7=0;if((hS|0)!=3){break}c[16163]=c[16163]|12;c[16335]=c[16335]|12;c[16507]=c[16507]|12;c[16679]=c[16679]|12;c[16851]=c[16851]|12;c[17023]=c[17023]|12;c[17195]=c[17195]|12;c[17367]=c[17367]|12;c[17539]=c[17539]|12;c[17711]=c[17711]|12;c[17883]=c[17883]|12;c[13898]=gv+2;break L44}}while(0);hS=c[gw+(gx*40&-1)+36>>2]|0;gG=c[gw+(gx*40&-1)+32>>2]|0;if(gH){break L252}if((hS|0)>0){cd=0;gE=0;hF=gG;while(1){if((a[cd+125680|0]|0)==(a[gB+(cd+hF|0)|0]|0)){hV=hF;hW=gE}else{if((cd|0)!=2){break}hV=hF-1|0;hW=1}gC=cd+1|0;if((gC|0)<(hW+hS|0)){cd=gC;gE=hW;hF=hV}else{b7=203;break}}do{if((b7|0)==203){b7=0;if((hW|0)==0){if(!((cd|0)==1|(cd|0)==7)){break}}c[16163]=c[16163]|3;c[16335]=c[16335]|3;c[16507]=c[16507]|3;c[16679]=c[16679]|3;c[16851]=c[16851]|3;c[17023]=c[17023]|3;c[17195]=c[17195]|3;c[17367]=c[17367]|3;c[17539]=c[17539]|3;c[17711]=c[17711]|3;c[17883]=c[17883]|3;c[13898]=gv+2;break L44}}while(0);if(gH){break L252}else{hY=0}}else{hY=0}while(1){if((hY|0)>=(hS|0)){break}if((a[gB+(gG+hY|0)|0]|0)==(a[hY+57e3|0]|0)){hY=hY+1|0}else{break L252}}if((a[hY+57e3|0]|0)!=0){break L252}c[17367]=3;c[17386]=0;c[17387]=0;c[13898]=gv+2;break L44}}while(0);c[16335]=3;c[16507]=3;c[16355]=0;c[16354]=0;c[16527]=0;c[16526]=0;c[13898]=gv+2;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=57e3,v)|0);gH=c[13898]|0;gy=c[1054]|0;gG=c[gy+(gH*40&-1)+36>>2]|0;hS=c[gy+(gH*40&-1)+32>>2]|0;L302:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gG|0)>0){cd=c[10036]|0;hF=0;gE=0;gC=hS;while(1){gz=a[b0+hF|0]|0;if(gz<<24>>24==(a[cd+(hF+gC|0)|0]|0)){hZ=gC;h_=gE}else{if(gz<<24>>24!=36){break L302}hZ=gC-1|0;h_=1}h$=hF+1|0;if((h$|0)<(h_+gG|0)){hF=h$;gE=h_;gC=hZ}else{break}}if((h_|0)==0){h0=h$;b7=219}}else{h0=0;b7=219}if((b7|0)==219){b7=0;gC=a[b0+h0|0]|0;if(!((gC<<24>>24|0)==36|(gC<<24>>24|0)==0)){break}}c[17367]=c[17367]|1;c[17386]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=57e3,v)|0);gH=c[13898]|0;gG=c[1054]|0;hS=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L317:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((hS|0)>0){gC=c[10036]|0;gE=0;hF=0;cd=gy;while(1){gz=a[b1+gE|0]|0;if(gz<<24>>24==(a[gC+(gE+cd|0)|0]|0)){h1=cd;h2=hF}else{if(gz<<24>>24!=36){break L317}h1=cd-1|0;h2=1}h3=gE+1|0;if((h3|0)<(h2+hS|0)){gE=h3;hF=h2;cd=h1}else{break}}if((h2|0)==0){h4=h3;b7=229}}else{h4=0;b7=229}if((b7|0)==229){b7=0;cd=a[b1+h4|0]|0;if(!((cd<<24>>24|0)==36|(cd<<24>>24|0)==0)){break}}c[17367]=c[17367]|2;c[17387]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=57e3,v)|0);gH=c[13898]|0;L332:do{if((c[8272]|0)>(gH|0)){hS=c[1054]|0;if((a[hS+(gH*40&-1)|0]&1)==0){break}gy=c[hS+(gH*40&-1)+36>>2]|0;gG=hS+(gH*40&-1)+32|0;hS=c[10036]|0;cd=0;while(1){if((cd|0)>=(gy|0)){break}if((a[hS+((c[gG>>2]|0)+cd|0)|0]|0)==(a[b0+cd|0]|0)){cd=cd+1|0}else{break L332}}if((a[b0+cd|0]|0)!=0){break}c[17367]=c[17367]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=57e3,v)|0);gH=c[13898]|0;gG=c[1054]|0;hS=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L341:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((hS|0)>0){hF=c[10036]|0;gE=0;gC=0;gz=gy;while(1){gA=a[b0+gE|0]|0;if(gA<<24>>24==(a[hF+(gE+gz|0)|0]|0)){h5=gz;h6=gC}else{if(gA<<24>>24!=36){break L341}h5=gz-1|0;h6=1}h7=gE+1|0;if((h7|0)<(h6+hS|0)){gE=h7;gC=h6;gz=h5}else{break}}if((h6|0)==0){h8=h7;b7=246}}else{h8=0;b7=246}if((b7|0)==246){b7=0;gz=a[b0+h8|0]|0;if(!((gz<<24>>24|0)==36|(gz<<24>>24|0)==0)){break}}c[17367]=c[17367]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=57e3,v)|0);gH=c[13898]|0;hS=c[1054]|0;gy=c[hS+(gH*40&-1)+36>>2]|0;gG=c[hS+(gH*40&-1)+32>>2]|0;gz=(a[hS+(gH*40&-1)|0]&1)==0;L356:do{if(!gz){if((gy|0)>0){hS=c[10036]|0;gC=0;gE=0;hF=gG;while(1){cd=a[b1+gC|0]|0;if(cd<<24>>24==(a[hS+(gC+hF|0)|0]|0)){h9=hF;ia=gE}else{if(cd<<24>>24!=36){break L356}h9=hF-1|0;ia=1}ib=gC+1|0;if((ib|0)<(ia+gy|0)){gC=ib;gE=ia;hF=h9}else{break}}if((ia|0)==0){ic=ib;b7=256}}else{ic=0;b7=256}if((b7|0)==256){b7=0;hF=a[b1+ic|0]|0;if(!((hF<<24>>24|0)==36|(hF<<24>>24|0)==0)){break}}c[17367]=c[17367]|8;c[13898]=gH+1;break L44}}while(0);L371:do{if(!((c[8272]|0)<=(gH|0)|gz)){hF=c[10036]|0;gE=0;while(1){if((gE|0)>=(gy|0)){break}if((a[hF+(gG+gE|0)|0]|0)==(a[gE+57024|0]|0)){gE=gE+1|0}else{break L371}}if((a[gE+57024|0]|0)!=0){break}c[17539]=3;c[17558]=0;c[17559]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=57024,v)|0);gH=c[13898]|0;gG=c[1054]|0;gy=c[gG+(gH*40&-1)+36>>2]|0;gz=c[gG+(gH*40&-1)+32>>2]|0;L379:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gy|0)>0){hF=c[10036]|0;gC=0;hS=0;cd=gz;while(1){gA=a[b0+gC|0]|0;if(gA<<24>>24==(a[hF+(gC+cd|0)|0]|0)){id=cd;ie=hS}else{if(gA<<24>>24!=36){break L379}id=cd-1|0;ie=1}ig=gC+1|0;if((ig|0)<(ie+gy|0)){gC=ig;hS=ie;cd=id}else{break}}if((ie|0)==0){ih=ig;b7=272}}else{ih=0;b7=272}if((b7|0)==272){b7=0;cd=a[b0+ih|0]|0;if(!((cd<<24>>24|0)==36|(cd<<24>>24|0)==0)){break}}c[17539]=c[17539]|1;c[17558]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=57024,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L394:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){cd=c[10036]|0;hS=0;gC=0;hF=gG;while(1){gE=a[b1+hS|0]|0;if(gE<<24>>24==(a[cd+(hS+hF|0)|0]|0)){ii=hF;ij=gC}else{if(gE<<24>>24!=36){break L394}ii=hF-1|0;ij=1}ik=hS+1|0;if((ik|0)<(ij+gz|0)){hS=ik;gC=ij;hF=ii}else{break}}if((ij|0)==0){il=ik;b7=282}}else{il=0;b7=282}if((b7|0)==282){b7=0;hF=a[b1+il|0]|0;if(!((hF<<24>>24|0)==36|(hF<<24>>24|0)==0)){break}}c[17539]=c[17539]|2;c[17559]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=57024,v)|0);gH=c[13898]|0;L409:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gG=c[gz+(gH*40&-1)+36>>2]|0;gy=gz+(gH*40&-1)+32|0;gz=c[10036]|0;hF=0;while(1){if((hF|0)>=(gG|0)){break}if((a[gz+((c[gy>>2]|0)+hF|0)|0]|0)==(a[b0+hF|0]|0)){hF=hF+1|0}else{break L409}}if((a[b0+hF|0]|0)!=0){break}c[17539]=c[17539]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=57024,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L418:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){gC=c[10036]|0;hS=0;cd=0;gE=gG;while(1){gA=a[b0+hS|0]|0;if(gA<<24>>24==(a[gC+(hS+gE|0)|0]|0)){im=gE;io=cd}else{if(gA<<24>>24!=36){break L418}im=gE-1|0;io=1}ip=hS+1|0;if((ip|0)<(io+gz|0)){hS=ip;cd=io;gE=im}else{break}}if((io|0)==0){iq=ip;b7=299}}else{iq=0;b7=299}if((b7|0)==299){b7=0;gE=a[b0+iq|0]|0;if(!((gE<<24>>24|0)==36|(gE<<24>>24|0)==0)){break}}c[17539]=c[17539]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=57024,v)|0);gH=c[13898]|0;gz=c[1054]|0;gG=c[gz+(gH*40&-1)+36>>2]|0;gy=c[gz+(gH*40&-1)+32>>2]|0;L433:do{if((a[gz+(gH*40&-1)|0]&1)!=0){L435:do{if((gG|0)>0){gE=c[10036]|0;cd=0;hS=0;gC=gy;while(1){hF=a[b1+cd|0]|0;if(hF<<24>>24==(a[gE+(cd+gC|0)|0]|0)){ir=gC;iu=hS}else{if(hF<<24>>24!=36){break L435}ir=gC-1|0;iu=1}iv=cd+1|0;if((iv|0)<(iu+gG|0)){cd=iv;hS=iu;gC=ir}else{break}}if((iu|0)==0){iy=iv;b7=309}else{b7=310}}else{iy=0;b7=309}}while(0);if((b7|0)==309){b7=0;gC=a[b1+iy|0]|0;if((gC<<24>>24|0)==36|(gC<<24>>24|0)==0){b7=310}}if((b7|0)==310){b7=0;c[17539]=c[17539]|8;c[13898]=gH+1;break L44}if((c[8272]|0)<=(gH|0)){break}gC=c[10036]|0;hS=0;while(1){if((hS|0)>=(gG|0)){break}if((a[gC+(hS+gy|0)|0]|0)==(a[hS+57048|0]|0)){hS=hS+1|0}else{break L433}}if((a[hS+57048|0]|0)!=0){break}c[17711]=3;c[17730]=0;c[17731]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=57048,v)|0);gH=c[13898]|0;gy=c[1054]|0;gG=c[gy+(gH*40&-1)+36>>2]|0;gz=c[gy+(gH*40&-1)+32>>2]|0;L456:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gG|0)>0){gC=c[10036]|0;cd=0;gE=0;hF=gz;while(1){gA=a[b0+cd|0]|0;if(gA<<24>>24==(a[gC+(cd+hF|0)|0]|0)){iz=hF;iA=gE}else{if(gA<<24>>24!=36){break L456}iz=hF-1|0;iA=1}iB=cd+1|0;if((iB|0)<(iA+gG|0)){cd=iB;gE=iA;hF=iz}else{break}}if((iA|0)==0){iC=iB;b7=325}}else{iC=0;b7=325}if((b7|0)==325){b7=0;hF=a[b0+iC|0]|0;if(!((hF<<24>>24|0)==36|(hF<<24>>24|0)==0)){break}}c[17711]=c[17711]|1;c[17730]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=57048,v)|0);gH=c[13898]|0;gG=c[1054]|0;gz=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L471:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){hF=c[10036]|0;gE=0;cd=0;gC=gy;while(1){hS=a[b1+gE|0]|0;if(hS<<24>>24==(a[hF+(gE+gC|0)|0]|0)){iD=gC;iE=cd}else{if(hS<<24>>24!=36){break L471}iD=gC-1|0;iE=1}iF=gE+1|0;if((iF|0)<(iE+gz|0)){gE=iF;cd=iE;gC=iD}else{break}}if((iE|0)==0){iG=iF;b7=335}}else{iG=0;b7=335}if((b7|0)==335){b7=0;gC=a[b1+iG|0]|0;if(!((gC<<24>>24|0)==36|(gC<<24>>24|0)==0)){break}}c[17711]=c[17711]|2;c[17731]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=57048,v)|0);gH=c[13898]|0;L486:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gy=c[gz+(gH*40&-1)+36>>2]|0;gG=gz+(gH*40&-1)+32|0;gz=c[10036]|0;gC=0;while(1){if((gC|0)>=(gy|0)){break}if((a[gz+((c[gG>>2]|0)+gC|0)|0]|0)==(a[b0+gC|0]|0)){gC=gC+1|0}else{break L486}}if((a[b0+gC|0]|0)!=0){break}c[17711]=c[17711]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=57048,v)|0);gH=c[13898]|0;gG=c[1054]|0;gz=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L495:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){cd=c[10036]|0;gE=0;hF=0;hS=gy;while(1){gA=a[b0+gE|0]|0;if(gA<<24>>24==(a[cd+(gE+hS|0)|0]|0)){iH=hS;iI=hF}else{if(gA<<24>>24!=36){break L495}iH=hS-1|0;iI=1}iJ=gE+1|0;if((iJ|0)<(iI+gz|0)){gE=iJ;hF=iI;hS=iH}else{break}}if((iI|0)==0){iK=iJ;b7=352}}else{iK=0;b7=352}if((b7|0)==352){b7=0;hS=a[b0+iK|0]|0;if(!((hS<<24>>24|0)==36|(hS<<24>>24|0)==0)){break}}c[17711]=c[17711]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=57048,v)|0);gH=c[13898]|0;gz=c[1054]|0;gy=c[gz+(gH*40&-1)+36>>2]|0;gG=c[gz+(gH*40&-1)+32>>2]|0;L510:do{if((a[gz+(gH*40&-1)|0]&1)!=0){L512:do{if((gy|0)>0){hS=c[10036]|0;hF=0;gE=0;cd=gG;while(1){gC=a[b1+hF|0]|0;if(gC<<24>>24==(a[hS+(hF+cd|0)|0]|0)){iL=cd;iM=gE}else{if(gC<<24>>24!=36){break L512}iL=cd-1|0;iM=1}iN=hF+1|0;if((iN|0)<(iM+gy|0)){hF=iN;gE=iM;cd=iL}else{break}}if((iM|0)==0){iP=iN;b7=362}else{b7=363}}else{iP=0;b7=362}}while(0);if((b7|0)==362){b7=0;cd=a[b1+iP|0]|0;if((cd<<24>>24|0)==36|(cd<<24>>24|0)==0){b7=363}}if((b7|0)==363){b7=0;c[17711]=c[17711]|8;c[13898]=gH+1;break L44}if((c[8272]|0)<=(gH|0)){break}cd=c[10036]|0;gE=0;while(1){if((gE|0)>=(gy|0)){break}if((a[cd+(gE+gG|0)|0]|0)==(a[gE+57072|0]|0)){gE=gE+1|0}else{break L510}}if((a[gE+57072|0]|0)!=0){break}c[17883]=3;c[17902]=0;c[17903]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=57072,v)|0);gH=c[13898]|0;gG=c[1054]|0;gy=c[gG+(gH*40&-1)+36>>2]|0;gz=c[gG+(gH*40&-1)+32>>2]|0;L533:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gy|0)>0){cd=c[10036]|0;hF=0;hS=0;gC=gz;while(1){gA=a[b0+hF|0]|0;if(gA<<24>>24==(a[cd+(hF+gC|0)|0]|0)){iR=gC;iS=hS}else{if(gA<<24>>24!=36){break L533}iR=gC-1|0;iS=1}iT=hF+1|0;if((iT|0)<(iS+gy|0)){hF=iT;hS=iS;gC=iR}else{break}}if((iS|0)==0){iU=iT;b7=378}}else{iU=0;b7=378}if((b7|0)==378){b7=0;gC=a[b0+iU|0]|0;if(!((gC<<24>>24|0)==36|(gC<<24>>24|0)==0)){break}}c[17883]=c[17883]|1;c[17902]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=57072,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L548:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){gC=c[10036]|0;hS=0;hF=0;cd=gG;while(1){gE=a[b1+hS|0]|0;if(gE<<24>>24==(a[gC+(hS+cd|0)|0]|0)){iV=cd;iW=hF}else{if(gE<<24>>24!=36){break L548}iV=cd-1|0;iW=1}iX=hS+1|0;if((iX|0)<(iW+gz|0)){hS=iX;hF=iW;cd=iV}else{break}}if((iW|0)==0){iZ=iX;b7=388}}else{iZ=0;b7=388}if((b7|0)==388){b7=0;cd=a[b1+iZ|0]|0;if(!((cd<<24>>24|0)==36|(cd<<24>>24|0)==0)){break}}c[17883]=c[17883]|2;c[17903]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=57072,v)|0);gH=c[13898]|0;L563:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gG=c[gz+(gH*40&-1)+36>>2]|0;gy=gz+(gH*40&-1)+32|0;gz=c[10036]|0;cd=0;while(1){if((cd|0)>=(gG|0)){break}if((a[gz+((c[gy>>2]|0)+cd|0)|0]|0)==(a[b0+cd|0]|0)){cd=cd+1|0}else{break L563}}if((a[b0+cd|0]|0)!=0){break}c[17883]=c[17883]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=57072,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L572:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){hF=c[10036]|0;hS=0;gC=0;gE=gG;while(1){gA=a[b0+hS|0]|0;if(gA<<24>>24==(a[hF+(hS+gE|0)|0]|0)){i_=gE;i$=gC}else{if(gA<<24>>24!=36){break L572}i_=gE-1|0;i$=1}i0=hS+1|0;if((i0|0)<(i$+gz|0)){hS=i0;gC=i$;gE=i_}else{break}}if((i$|0)==0){i1=i0;b7=405}}else{i1=0;b7=405}if((b7|0)==405){b7=0;gE=a[b0+i1|0]|0;if(!((gE<<24>>24|0)==36|(gE<<24>>24|0)==0)){break}}c[17883]=c[17883]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=57072,v)|0);gH=c[13898]|0;gz=c[1054]|0;gG=c[gz+(gH*40&-1)+36>>2]|0;gy=c[gz+(gH*40&-1)+32>>2]|0;L587:do{if((a[gz+(gH*40&-1)|0]&1)!=0){L589:do{if((gG|0)>0){gE=c[10036]|0;gC=0;hS=0;hF=gy;while(1){cd=a[b1+gC|0]|0;if(cd<<24>>24==(a[gE+(gC+hF|0)|0]|0)){i2=hF;i3=hS}else{if(cd<<24>>24!=36){break L589}i2=hF-1|0;i3=1}i4=gC+1|0;if((i4|0)<(i3+gG|0)){gC=i4;hS=i3;hF=i2}else{break}}if((i3|0)==0){i5=i4;b7=415}else{b7=416}}else{i5=0;b7=415}}while(0);if((b7|0)==415){b7=0;hF=a[b1+i5|0]|0;if((hF<<24>>24|0)==36|(hF<<24>>24|0)==0){b7=416}}if((b7|0)==416){b7=0;c[17883]=c[17883]|8;c[13898]=gH+1;break L44}if((c[8272]|0)<=(gH|0)){break}hF=c[10036]|0;hS=0;while(1){if((hS|0)>=(gG|0)){break}if((a[hF+(hS+gy|0)|0]|0)==(a[hS+56880|0]|0)){hS=hS+1|0}else{break L587}}if((a[hS+56880|0]|0)!=0){break}c[16507]=3;c[16526]=0;c[16527]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=56880,v)|0);gH=c[13898]|0;gy=c[1054]|0;gG=c[gy+(gH*40&-1)+36>>2]|0;gz=c[gy+(gH*40&-1)+32>>2]|0;L610:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gG|0)>0){hF=c[10036]|0;gC=0;gE=0;cd=gz;while(1){gA=a[b0+gC|0]|0;if(gA<<24>>24==(a[hF+(gC+cd|0)|0]|0)){i6=cd;i7=gE}else{if(gA<<24>>24!=36){break L610}i6=cd-1|0;i7=1}i8=gC+1|0;if((i8|0)<(i7+gG|0)){gC=i8;gE=i7;cd=i6}else{break}}if((i7|0)==0){ja=i8;b7=431}}else{ja=0;b7=431}if((b7|0)==431){b7=0;cd=a[b0+ja|0]|0;if(!((cd<<24>>24|0)==36|(cd<<24>>24|0)==0)){break}}c[16507]=c[16507]|1;c[16526]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=56880,v)|0);gH=c[13898]|0;gG=c[1054]|0;gz=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L625:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){cd=c[10036]|0;gE=0;gC=0;hF=gy;while(1){hS=a[b1+gE|0]|0;if(hS<<24>>24==(a[cd+(gE+hF|0)|0]|0)){jb=hF;jc=gC}else{if(hS<<24>>24!=36){break L625}jb=hF-1|0;jc=1}jd=gE+1|0;if((jd|0)<(jc+gz|0)){gE=jd;gC=jc;hF=jb}else{break}}if((jc|0)==0){je=jd;b7=441}}else{je=0;b7=441}if((b7|0)==441){b7=0;hF=a[b1+je|0]|0;if(!((hF<<24>>24|0)==36|(hF<<24>>24|0)==0)){break}}c[16507]=c[16507]|2;c[16527]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=56880,v)|0);gH=c[13898]|0;L640:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gy=c[gz+(gH*40&-1)+36>>2]|0;gG=gz+(gH*40&-1)+32|0;gz=c[10036]|0;hF=0;while(1){if((hF|0)>=(gy|0)){break}if((a[gz+((c[gG>>2]|0)+hF|0)|0]|0)==(a[b0+hF|0]|0)){hF=hF+1|0}else{break L640}}if((a[b0+hF|0]|0)!=0){break}c[16507]=c[16507]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=56880,v)|0);gH=c[13898]|0;gG=c[1054]|0;gz=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L649:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){gC=c[10036]|0;gE=0;cd=0;hS=gy;while(1){gA=a[b0+gE|0]|0;if(gA<<24>>24==(a[gC+(gE+hS|0)|0]|0)){jf=hS;jg=cd}else{if(gA<<24>>24!=36){break L649}jf=hS-1|0;jg=1}jh=gE+1|0;if((jh|0)<(jg+gz|0)){gE=jh;cd=jg;hS=jf}else{break}}if((jg|0)==0){ji=jh;b7=458}}else{ji=0;b7=458}if((b7|0)==458){b7=0;hS=a[b0+ji|0]|0;if(!((hS<<24>>24|0)==36|(hS<<24>>24|0)==0)){break}}c[16507]=c[16507]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=56880,v)|0);gH=c[13898]|0;gz=c[1054]|0;gy=c[gz+(gH*40&-1)+36>>2]|0;gG=c[gz+(gH*40&-1)+32>>2]|0;L664:do{if((a[gz+(gH*40&-1)|0]&1)!=0){L666:do{if((gy|0)>0){hS=c[10036]|0;cd=0;gE=0;gC=gG;while(1){hF=a[b1+cd|0]|0;if(hF<<24>>24==(a[hS+(cd+gC|0)|0]|0)){jj=gC;jk=gE}else{if(hF<<24>>24!=36){break L666}jj=gC-1|0;jk=1}jl=cd+1|0;if((jl|0)<(jk+gy|0)){cd=jl;gE=jk;gC=jj}else{break}}if((jk|0)==0){jm=jl;b7=468}else{b7=469}}else{jm=0;b7=468}}while(0);if((b7|0)==468){b7=0;gC=a[b1+jm|0]|0;if((gC<<24>>24|0)==36|(gC<<24>>24|0)==0){b7=469}}if((b7|0)==469){b7=0;c[16507]=c[16507]|8;c[13898]=gH+1;break L44}if((c[8272]|0)<=(gH|0)){break}gC=c[10036]|0;gE=0;while(1){if((gE|0)>=(gy|0)){break}if((a[gC+(gE+gG|0)|0]|0)==(a[gE+56856|0]|0)){gE=gE+1|0}else{break L664}}if((a[gE+56856|0]|0)!=0){break}c[16335]=3;c[16354]=0;c[16355]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=56856,v)|0);gH=c[13898]|0;gG=c[1054]|0;gy=c[gG+(gH*40&-1)+36>>2]|0;gz=c[gG+(gH*40&-1)+32>>2]|0;L687:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gy|0)>0){gC=c[10036]|0;cd=0;hS=0;hF=gz;while(1){gA=a[b0+cd|0]|0;if(gA<<24>>24==(a[gC+(cd+hF|0)|0]|0)){jn=hF;jo=hS}else{if(gA<<24>>24!=36){break L687}jn=hF-1|0;jo=1}jy=cd+1|0;if((jy|0)<(jo+gy|0)){cd=jy;hS=jo;hF=jn}else{break}}if((jo|0)==0){jz=jy;b7=484}}else{jz=0;b7=484}if((b7|0)==484){b7=0;hF=a[b0+jz|0]|0;if(!((hF<<24>>24|0)==36|(hF<<24>>24|0)==0)){break}}c[16335]=c[16335]|1;c[16354]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=56856,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L702:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){hF=c[10036]|0;hS=0;cd=0;gC=gG;while(1){gE=a[b1+hS|0]|0;if(gE<<24>>24==(a[hF+(hS+gC|0)|0]|0)){jA=gC;jB=cd}else{if(gE<<24>>24!=36){break L702}jA=gC-1|0;jB=1}jC=hS+1|0;if((jC|0)<(jB+gz|0)){hS=jC;cd=jB;gC=jA}else{break}}if((jB|0)==0){jD=jC;b7=494}}else{jD=0;b7=494}if((b7|0)==494){b7=0;gC=a[b1+jD|0]|0;if(!((gC<<24>>24|0)==36|(gC<<24>>24|0)==0)){break}}c[16335]=c[16335]|2;c[16355]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=56856,v)|0);gH=c[13898]|0;L717:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gG=c[gz+(gH*40&-1)+36>>2]|0;gy=gz+(gH*40&-1)+32|0;gz=c[10036]|0;gC=0;while(1){if((gC|0)>=(gG|0)){break}if((a[gz+((c[gy>>2]|0)+gC|0)|0]|0)==(a[b0+gC|0]|0)){gC=gC+1|0}else{break L717}}if((a[b0+gC|0]|0)!=0){break}c[16335]=c[16335]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=56856,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L726:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){cd=c[10036]|0;hS=0;hF=0;gE=gG;while(1){gA=a[b0+hS|0]|0;if(gA<<24>>24==(a[cd+(hS+gE|0)|0]|0)){jE=gE;jF=hF}else{if(gA<<24>>24!=36){break L726}jE=gE-1|0;jF=1}jG=hS+1|0;if((jG|0)<(jF+gz|0)){hS=jG;hF=jF;gE=jE}else{break}}if((jF|0)==0){jH=jG;b7=511}}else{jH=0;b7=511}if((b7|0)==511){b7=0;gE=a[b0+jH|0]|0;if(!((gE<<24>>24|0)==36|(gE<<24>>24|0)==0)){break}}c[16335]=c[16335]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=56856,v)|0);gH=c[13898]|0;gz=c[1054]|0;gG=c[gz+(gH*40&-1)+36>>2]|0;gy=c[gz+(gH*40&-1)+32>>2]|0;L741:do{if((a[gz+(gH*40&-1)|0]&1)!=0){L743:do{if((gG|0)>0){gE=c[10036]|0;hF=0;hS=0;cd=gy;while(1){gC=a[b1+hF|0]|0;if(gC<<24>>24==(a[gE+(hF+cd|0)|0]|0)){jI=cd;jJ=hS}else{if(gC<<24>>24!=36){break L743}jI=cd-1|0;jJ=1}jK=hF+1|0;if((jK|0)<(jJ+gG|0)){hF=jK;hS=jJ;cd=jI}else{break}}if((jJ|0)==0){jL=jK;b7=521}else{b7=522}}else{jL=0;b7=521}}while(0);if((b7|0)==521){b7=0;cd=a[b1+jL|0]|0;if((cd<<24>>24|0)==36|(cd<<24>>24|0)==0){b7=522}}if((b7|0)==522){b7=0;c[16335]=c[16335]|8;c[13898]=gH+1;break L44}if((c[8272]|0)<=(gH|0)){break}cd=c[10036]|0;hS=0;while(1){if((hS|0)>=(gG|0)){break}if((a[cd+(hS+gy|0)|0]|0)==(a[hS+56832|0]|0)){hS=hS+1|0}else{break L741}}if((a[hS+56832|0]|0)!=0){break}c[16163]=3;c[16182]=0;c[16183]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=56832,v)|0);gH=c[13898]|0;gy=c[1054]|0;gG=c[gy+(gH*40&-1)+36>>2]|0;gz=c[gy+(gH*40&-1)+32>>2]|0;L764:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gG|0)>0){cd=c[10036]|0;hF=0;gE=0;gC=gz;while(1){gA=a[b0+hF|0]|0;if(gA<<24>>24==(a[cd+(hF+gC|0)|0]|0)){jM=gC;jN=gE}else{if(gA<<24>>24!=36){break L764}jM=gC-1|0;jN=1}jO=hF+1|0;if((jO|0)<(jN+gG|0)){hF=jO;gE=jN;gC=jM}else{break}}if((jN|0)==0){jP=jO;b7=537}}else{jP=0;b7=537}if((b7|0)==537){b7=0;gC=a[b0+jP|0]|0;if(!((gC<<24>>24|0)==36|(gC<<24>>24|0)==0)){break}}c[16163]=c[16163]|1;c[16182]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=56832,v)|0);gH=c[13898]|0;gG=c[1054]|0;gz=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L779:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){gC=c[10036]|0;gE=0;hF=0;cd=gy;while(1){hS=a[b1+gE|0]|0;if(hS<<24>>24==(a[gC+(gE+cd|0)|0]|0)){jQ=cd;jR=hF}else{if(hS<<24>>24!=36){break L779}jQ=cd-1|0;jR=1}jS=gE+1|0;if((jS|0)<(jR+gz|0)){gE=jS;hF=jR;cd=jQ}else{break}}if((jR|0)==0){jT=jS;b7=547}}else{jT=0;b7=547}if((b7|0)==547){b7=0;cd=a[b1+jT|0]|0;if(!((cd<<24>>24|0)==36|(cd<<24>>24|0)==0)){break}}c[16163]=c[16163]|2;c[16183]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=56832,v)|0);gH=c[13898]|0;L794:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gy=c[gz+(gH*40&-1)+36>>2]|0;gG=gz+(gH*40&-1)+32|0;gz=c[10036]|0;cd=0;while(1){if((cd|0)>=(gy|0)){break}if((a[gz+((c[gG>>2]|0)+cd|0)|0]|0)==(a[b0+cd|0]|0)){cd=cd+1|0}else{break L794}}if((a[b0+cd|0]|0)!=0){break}c[16163]=c[16163]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=56832,v)|0);gH=c[13898]|0;gG=c[1054]|0;gz=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L803:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){hF=c[10036]|0;gE=0;gC=0;hS=gy;while(1){gA=a[b0+gE|0]|0;if(gA<<24>>24==(a[hF+(gE+hS|0)|0]|0)){jU=hS;jV=gC}else{if(gA<<24>>24!=36){break L803}jU=hS-1|0;jV=1}jW=gE+1|0;if((jW|0)<(jV+gz|0)){gE=jW;gC=jV;hS=jU}else{break}}if((jV|0)==0){jX=jW;b7=564}}else{jX=0;b7=564}if((b7|0)==564){b7=0;hS=a[b0+jX|0]|0;if(!((hS<<24>>24|0)==36|(hS<<24>>24|0)==0)){break}}c[16163]=c[16163]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=56832,v)|0);gH=c[13898]|0;gz=c[1054]|0;gy=c[gz+(gH*40&-1)+36>>2]|0;gG=c[gz+(gH*40&-1)+32>>2]|0;L818:do{if((a[gz+(gH*40&-1)|0]&1)!=0){L820:do{if((gy|0)>0){hS=c[10036]|0;gC=0;gE=0;hF=gG;while(1){cd=a[b1+gC|0]|0;if(cd<<24>>24==(a[hS+(gC+hF|0)|0]|0)){jY=hF;jZ=gE}else{if(cd<<24>>24!=36){break L820}jY=hF-1|0;jZ=1}j_=gC+1|0;if((j_|0)<(jZ+gy|0)){gC=j_;gE=jZ;hF=jY}else{break}}if((jZ|0)==0){j$=j_;b7=574}else{b7=575}}else{j$=0;b7=574}}while(0);if((b7|0)==574){b7=0;hF=a[b1+j$|0]|0;if((hF<<24>>24|0)==36|(hF<<24>>24|0)==0){b7=575}}if((b7|0)==575){b7=0;c[16163]=c[16163]|8;c[13898]=gH+1;break L44}if((c[8272]|0)<=(gH|0)){break}hF=c[10036]|0;gE=0;while(1){if((gE|0)>=(gy|0)){break}if((a[hF+(gE+gG|0)|0]|0)==(a[gE+56976|0]|0)){gE=gE+1|0}else{break L818}}if((a[gE+56976|0]|0)!=0){break}c[17195]=3;c[17214]=0;c[17215]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=56976,v)|0);gH=c[13898]|0;gG=c[1054]|0;gy=c[gG+(gH*40&-1)+36>>2]|0;gz=c[gG+(gH*40&-1)+32>>2]|0;L841:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gy|0)>0){hF=c[10036]|0;gC=0;hS=0;cd=gz;while(1){gA=a[b0+gC|0]|0;if(gA<<24>>24==(a[hF+(gC+cd|0)|0]|0)){j0=cd;j1=hS}else{if(gA<<24>>24!=36){break L841}j0=cd-1|0;j1=1}j2=gC+1|0;if((j2|0)<(j1+gy|0)){gC=j2;hS=j1;cd=j0}else{break}}if((j1|0)==0){j3=j2;b7=590}}else{j3=0;b7=590}if((b7|0)==590){b7=0;cd=a[b0+j3|0]|0;if(!((cd<<24>>24|0)==36|(cd<<24>>24|0)==0)){break}}c[17195]=c[17195]|1;c[17214]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=56976,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L856:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){cd=c[10036]|0;hS=0;gC=0;hF=gG;while(1){gE=a[b1+hS|0]|0;if(gE<<24>>24==(a[cd+(hS+hF|0)|0]|0)){j4=hF;j5=gC}else{if(gE<<24>>24!=36){break L856}j4=hF-1|0;j5=1}j6=hS+1|0;if((j6|0)<(j5+gz|0)){hS=j6;gC=j5;hF=j4}else{break}}if((j5|0)==0){j7=j6;b7=600}}else{j7=0;b7=600}if((b7|0)==600){b7=0;hF=a[b1+j7|0]|0;if(!((hF<<24>>24|0)==36|(hF<<24>>24|0)==0)){break}}c[17195]=c[17195]|2;c[17215]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=56976,v)|0);gH=c[13898]|0;L871:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gG=c[gz+(gH*40&-1)+36>>2]|0;gy=gz+(gH*40&-1)+32|0;gz=c[10036]|0;hF=0;while(1){if((hF|0)>=(gG|0)){break}if((a[gz+((c[gy>>2]|0)+hF|0)|0]|0)==(a[b0+hF|0]|0)){hF=hF+1|0}else{break L871}}if((a[b0+hF|0]|0)!=0){break}c[17195]=c[17195]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=56976,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L880:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){gC=c[10036]|0;hS=0;cd=0;gE=gG;while(1){gA=a[b0+hS|0]|0;if(gA<<24>>24==(a[gC+(hS+gE|0)|0]|0)){j8=gE;j9=cd}else{if(gA<<24>>24!=36){break L880}j8=gE-1|0;j9=1}ka=hS+1|0;if((ka|0)<(j9+gz|0)){hS=ka;cd=j9;gE=j8}else{break}}if((j9|0)==0){kb=ka;b7=617}}else{kb=0;b7=617}if((b7|0)==617){b7=0;gE=a[b0+kb|0]|0;if(!((gE<<24>>24|0)==36|(gE<<24>>24|0)==0)){break}}c[17195]=c[17195]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=56976,v)|0);gH=c[13898]|0;gz=c[1054]|0;gG=c[gz+(gH*40&-1)+36>>2]|0;gy=c[gz+(gH*40&-1)+32>>2]|0;L895:do{if((a[gz+(gH*40&-1)|0]&1)!=0){L897:do{if((gG|0)>0){gE=c[10036]|0;cd=0;hS=0;gC=gy;while(1){hF=a[b1+cd|0]|0;if(hF<<24>>24==(a[gE+(cd+gC|0)|0]|0)){kc=gC;kd=hS}else{if(hF<<24>>24!=36){break L897}kc=gC-1|0;kd=1}ke=cd+1|0;if((ke|0)<(kd+gG|0)){cd=ke;hS=kd;gC=kc}else{break}}if((kd|0)==0){kf=ke;b7=627}else{b7=628}}else{kf=0;b7=627}}while(0);if((b7|0)==627){b7=0;gC=a[b1+kf|0]|0;if((gC<<24>>24|0)==36|(gC<<24>>24|0)==0){b7=628}}if((b7|0)==628){b7=0;c[17195]=c[17195]|8;c[13898]=gH+1;break L44}if((c[8272]|0)<=(gH|0)){break}gC=c[10036]|0;hS=0;while(1){if((hS|0)>=(gG|0)){break}if((a[gC+(hS+gy|0)|0]|0)==(a[hS+56952|0]|0)){hS=hS+1|0}else{break L895}}if((a[hS+56952|0]|0)!=0){break}c[17023]=3;c[17042]=0;c[17043]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=56952,v)|0);gH=c[13898]|0;gy=c[1054]|0;gG=c[gy+(gH*40&-1)+36>>2]|0;gz=c[gy+(gH*40&-1)+32>>2]|0;L918:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gG|0)>0){gC=c[10036]|0;cd=0;gE=0;hF=gz;while(1){gA=a[b0+cd|0]|0;if(gA<<24>>24==(a[gC+(cd+hF|0)|0]|0)){kg=hF;kh=gE}else{if(gA<<24>>24!=36){break L918}kg=hF-1|0;kh=1}ki=cd+1|0;if((ki|0)<(kh+gG|0)){cd=ki;gE=kh;hF=kg}else{break}}if((kh|0)==0){kj=ki;b7=643}}else{kj=0;b7=643}if((b7|0)==643){b7=0;hF=a[b0+kj|0]|0;if(!((hF<<24>>24|0)==36|(hF<<24>>24|0)==0)){break}}c[17023]=c[17023]|1;c[17042]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=56952,v)|0);gH=c[13898]|0;gG=c[1054]|0;gz=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L933:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){hF=c[10036]|0;gE=0;cd=0;gC=gy;while(1){hS=a[b1+gE|0]|0;if(hS<<24>>24==(a[hF+(gE+gC|0)|0]|0)){kk=gC;kl=cd}else{if(hS<<24>>24!=36){break L933}kk=gC-1|0;kl=1}km=gE+1|0;if((km|0)<(kl+gz|0)){gE=km;cd=kl;gC=kk}else{break}}if((kl|0)==0){kn=km;b7=653}}else{kn=0;b7=653}if((b7|0)==653){b7=0;gC=a[b1+kn|0]|0;if(!((gC<<24>>24|0)==36|(gC<<24>>24|0)==0)){break}}c[17023]=c[17023]|2;c[17043]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=56952,v)|0);gH=c[13898]|0;L948:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gy=c[gz+(gH*40&-1)+36>>2]|0;gG=gz+(gH*40&-1)+32|0;gz=c[10036]|0;gC=0;while(1){if((gC|0)>=(gy|0)){break}if((a[gz+((c[gG>>2]|0)+gC|0)|0]|0)==(a[b0+gC|0]|0)){gC=gC+1|0}else{break L948}}if((a[b0+gC|0]|0)!=0){break}c[17023]=c[17023]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=56952,v)|0);gH=c[13898]|0;gG=c[1054]|0;gz=c[gG+(gH*40&-1)+36>>2]|0;gy=c[gG+(gH*40&-1)+32>>2]|0;L957:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){cd=c[10036]|0;gE=0;hF=0;hS=gy;while(1){gA=a[b0+gE|0]|0;if(gA<<24>>24==(a[cd+(gE+hS|0)|0]|0)){ko=hS;kp=hF}else{if(gA<<24>>24!=36){break L957}ko=hS-1|0;kp=1}kq=gE+1|0;if((kq|0)<(kp+gz|0)){gE=kq;hF=kp;hS=ko}else{break}}if((kp|0)==0){kr=kq;b7=670}}else{kr=0;b7=670}if((b7|0)==670){b7=0;hS=a[b0+kr|0]|0;if(!((hS<<24>>24|0)==36|(hS<<24>>24|0)==0)){break}}c[17023]=c[17023]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=56952,v)|0);gH=c[13898]|0;gz=c[1054]|0;gy=c[gz+(gH*40&-1)+36>>2]|0;gG=c[gz+(gH*40&-1)+32>>2]|0;L972:do{if((a[gz+(gH*40&-1)|0]&1)!=0){L974:do{if((gy|0)>0){hS=c[10036]|0;hF=0;gE=0;cd=gG;while(1){gC=a[b1+hF|0]|0;if(gC<<24>>24==(a[hS+(hF+cd|0)|0]|0)){ks=cd;kt=gE}else{if(gC<<24>>24!=36){break L974}ks=cd-1|0;kt=1}ku=hF+1|0;if((ku|0)<(kt+gy|0)){hF=ku;gE=kt;cd=ks}else{break}}if((kt|0)==0){kv=ku;b7=680}else{b7=681}}else{kv=0;b7=680}}while(0);if((b7|0)==680){b7=0;cd=a[b1+kv|0]|0;if((cd<<24>>24|0)==36|(cd<<24>>24|0)==0){b7=681}}if((b7|0)==681){b7=0;c[17023]=c[17023]|8;c[13898]=gH+1;break L44}if((c[8272]|0)<=(gH|0)){break}cd=c[10036]|0;gE=0;while(1){if((gE|0)>=(gy|0)){break}if((a[cd+(gE+gG|0)|0]|0)==(a[gE+56904|0]|0)){gE=gE+1|0}else{break L972}}if((a[gE+56904|0]|0)!=0){break}c[16679]=3;c[16698]=0;c[16699]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125560,(v=i,i=i+8|0,c[v>>2]=56904,v)|0);gH=c[13898]|0;gG=c[1054]|0;gy=c[gG+(gH*40&-1)+36>>2]|0;gz=c[gG+(gH*40&-1)+32>>2]|0;L995:do{if((a[gG+(gH*40&-1)|0]&1)!=0){if((gy|0)>0){cd=c[10036]|0;hF=0;hS=0;gC=gz;while(1){gA=a[b0+hF|0]|0;if(gA<<24>>24==(a[cd+(hF+gC|0)|0]|0)){kw=gC;kx=hS}else{if(gA<<24>>24!=36){break L995}kw=gC-1|0;kx=1}ky=hF+1|0;if((ky|0)<(kx+gy|0)){hF=ky;hS=kx;gC=kw}else{break}}if((kx|0)==0){kz=ky;b7=696}}else{kz=0;b7=696}if((b7|0)==696){b7=0;gC=a[b0+kz|0]|0;if(!((gC<<24>>24|0)==36|(gC<<24>>24|0)==0)){break}}c[16679]=c[16679]|1;c[16698]=0;c[13898]=gH+1;break L44}}while(0);be(b3|0,125432,(v=i,i=i+8|0,c[v>>2]=56904,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L1010:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){gC=c[10036]|0;hS=0;hF=0;cd=gG;while(1){gE=a[b1+hS|0]|0;if(gE<<24>>24==(a[gC+(hS+cd|0)|0]|0)){kA=cd;kB=hF}else{if(gE<<24>>24!=36){break L1010}kA=cd-1|0;kB=1}kC=hS+1|0;if((kC|0)<(kB+gz|0)){hS=kC;hF=kB;cd=kA}else{break}}if((kB|0)==0){kD=kC;b7=706}}else{kD=0;b7=706}if((b7|0)==706){b7=0;cd=a[b1+kD|0]|0;if(!((cd<<24>>24|0)==36|(cd<<24>>24|0)==0)){break}}c[16679]=c[16679]|2;c[16699]=0;c[13898]=gH+1;break L44}}while(0);be(b5|0,125376,(v=i,i=i+8|0,c[v>>2]=56904,v)|0);gH=c[13898]|0;L1025:do{if((c[8272]|0)>(gH|0)){gz=c[1054]|0;if((a[gz+(gH*40&-1)|0]&1)==0){break}gG=c[gz+(gH*40&-1)+36>>2]|0;gy=gz+(gH*40&-1)+32|0;gz=c[10036]|0;cd=0;while(1){if((cd|0)>=(gG|0)){break}if((a[gz+((c[gy>>2]|0)+cd|0)|0]|0)==(a[b0+cd|0]|0)){cd=cd+1|0}else{break L1025}}if((a[b0+cd|0]|0)!=0){break}c[16679]=c[16679]|12;c[13898]=gH+1;break L44}}while(0);be(b5|0,125128,(v=i,i=i+8|0,c[v>>2]=56904,v)|0);gH=c[13898]|0;gy=c[1054]|0;gz=c[gy+(gH*40&-1)+36>>2]|0;gG=c[gy+(gH*40&-1)+32>>2]|0;L1034:do{if((a[gy+(gH*40&-1)|0]&1)!=0){if((gz|0)>0){hF=c[10036]|0;hS=0;gC=0;gE=gG;while(1){gA=a[b0+hS|0]|0;if(gA<<24>>24==(a[hF+(hS+gE|0)|0]|0)){kE=gE;kF=gC}else{if(gA<<24>>24!=36){break L1034}kE=gE-1|0;kF=1}kG=hS+1|0;if((kG|0)<(kF+gz|0)){hS=kG;gC=kF;gE=kE}else{break}}if((kF|0)==0){kH=kG;b7=723}}else{kH=0;b7=723}if((b7|0)==723){b7=0;gE=a[b0+kH|0]|0;if(!((gE<<24>>24|0)==36|(gE<<24>>24|0)==0)){break}}c[16679]=c[16679]|4;c[13898]=gH+1;break L44}}while(0);be(b3|0,124936,(v=i,i=i+8|0,c[v>>2]=56904,v)|0);kI=c[13898]|0;gH=c[1054]|0;gz=c[gH+(kI*40&-1)+36>>2]|0;if((a[gH+(kI*40&-1)|0]&1)==0){b7=5120;break L27}if((gz|0)>0){gG=c[10036]|0;gy=0;gE=0;gC=c[gH+(kI*40&-1)+32>>2]|0;while(1){gH=a[b1+gy|0]|0;if(gH<<24>>24==(a[gG+(gy+gC|0)|0]|0)){kJ=gC;kK=gE}else{if(gH<<24>>24!=36){b7=5121;break L27}kJ=gC-1|0;kK=1}kL=gy+1|0;if((kL|0)<(kK+gz|0)){gy=kL;gE=kK;gC=kJ}else{break}}if((kK|0)==0){kM=kL;b7=733}}else{kM=0;b7=733}if((b7|0)==733){b7=0;gC=a[b1+kM|0]|0;if(!((gC<<24>>24|0)==36|(gC<<24>>24|0)==0)){b7=5122;break L27}}c[16679]=c[16679]|8;c[13898]=kI+1;break L44}}while(0);c[16163]=3;c[16335]=3;c[16507]=3;c[16679]=3;c[16851]=3;c[17023]=3;c[17195]=3;c[17367]=3;c[17539]=3;c[17711]=3;c[17883]=3}else if((gD|0)==6){gx=gv+1|0;c[13898]=gx;gC=c[8272]|0;L1064:do{if((gx|0)<(gC|0)){gE=gx;gy=gC;gz=gw;while(1){gG=c[gz+(gE*40&-1)+36>>2]|0;gH=gz+(gE*40&-1)+32|0;L1067:do{if((a[gz+(gE*40&-1)|0]&1)==0){b7=776}else{hS=c[10036]|0;hF=0;while(1){if((hF|0)>=(gG|0)){b7=741;break}cd=c[gH>>2]|0;if((a[hS+(cd+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{kN=cd;break}}if((b7|0)==741){b7=0;if((hF|0)==1){kO=gE;break L1064}kN=c[gH>>2]|0}L1076:do{if((gG|0)>0){cd=0;gA=0;hp=kN;while(1){if((a[cd+95520|0]|0)==(a[hS+(cd+hp|0)|0]|0)){kP=hp;kQ=gA}else{if((cd|0)!=1){kR=0;kS=0;kT=kN;break}kP=hp-1|0;kQ=1}ho=cd+1|0;if((ho|0)<(kQ+gG|0)){cd=ho;gA=kQ;hp=kP}else{b7=748;break}}do{if((b7|0)==748){b7=0;if((kQ|0)==0){if(!((cd|0)==0|(cd|0)==5)){kR=0;kS=0;kT=kN;break}}h[7077]=0.0;hp=gE+1|0;c[13898]=hp;kU=hp;kV=gy;break L1067}}while(0);while(1){if((a[kR+126016|0]|0)==(a[hS+(kR+kT|0)|0]|0)){kW=kT;kX=kS}else{if((kR|0)!=1){kY=0;kZ=0;k_=kN;break}kW=kT-1|0;kX=1}cd=kR+1|0;if((cd|0)<(kX+gG|0)){kR=cd;kS=kX;kT=kW}else{b7=755;break}}do{if((b7|0)==755){b7=0;if((kX|0)==0){if(!((kR|0)==0|(kR|0)==5)){kY=0;kZ=0;k_=kN;break}}h[7077]=1.0;cd=gE+1|0;c[13898]=cd;kU=cd;kV=gy;break L1067}}while(0);while(1){if((a[kY+125960|0]|0)==(a[hS+(kY+k_|0)|0]|0)){k$=k_;k0=kZ}else{if((kY|0)!=4){break L1076}k$=k_-1|0;k0=1}cd=kY+1|0;if((cd|0)<(k0+gG|0)){kY=cd;kZ=k0;k_=k$}else{break}}if((k0|0)==0){if(!((kY|0)==3|(kY|0)==9)){break}}h[7077]=-1.0;cd=gE+1|0;c[13898]=cd;kU=cd;kV=gy;break L1067}}while(0);if((gy|0)>(gE|0)){k1=0}else{b7=776;break}while(1){if((k1|0)>=(gG|0)){b7=770;break}if((a[hS+(k1+kN|0)|0]|0)==(a[k1+150856|0]|0)){k1=k1+1|0}else{k2=0;break}}do{if((b7|0)==770){b7=0;if((k1|0)!=5){k2=0;break}c[14156]=1;hF=gE+1|0;c[13898]=hF;kU=hF;kV=gy;break L1067}}while(0);while(1){if((k2|0)>=(gG|0)){break}if((a[hS+(k2+kN|0)|0]|0)==(a[k2+150704|0]|0)){k2=k2+1|0}else{b7=776;break L1067}}if((k2|0)!=4){b7=776;break}c[14156]=0;hS=gE+1|0;c[13898]=hS;kU=hS;kV=gy}}while(0);if((b7|0)==776){b7=0;gG=is(b_)|0;gH=c[gG>>2]|0;if((gH|0)==1){k3=+(c[gG+8>>2]|0)}else if((gH|0)==2){k3=+h[gG+8>>3]}else if((gH|0)==3){k3=+uz(c[gG+8>>2]|0,0)}else{b7=780;break L27}if((c[b6>>2]|0)==3){uu(c[cc>>2]|0);c[b6>>2]=1}h[7077]=k3;kU=c[13898]|0;kV=c[8272]|0}if((kU|0)>=(kV|0)){kO=kU;break L1064}gE=kU;gy=kV;gz=c[1054]|0}}else{kO=gx}}while(0);if((gx|0)!=(kO|0)){break}h[7077]=1.0}else if((gD|0)==8){gC=gv+1|0;c[13898]=gC;gz=c[8272]|0;gy=(gC|0)<(gz|0);L1136:do{if(gy){if((a[gw+(gC*40&-1)|0]&1)==0){k4=gC;k5=gz;k6=gw;b7=794;break}gE=c[gw+(gC*40&-1)+36>>2]|0;gG=gw+(gC*40&-1)+32|0;gH=0;while(1){if((gH|0)>=(gE|0)){break}if((a[gB+((c[gG>>2]|0)+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{b7=793;break L1136}}if((gH|0)==1){b7=792}else{b7=793}}else{b7=792}}while(0);if((b7|0)==792){b7=0;c[11692]=31;c[14088]=1;c[14074]=c[12768];c[14075]=c[12769];c[14076]=c[12770];c[14077]=c[12771];c[14078]=c[12772];c[14079]=c[12773];c[14080]=c[12774];c[14081]=c[12775];c[14082]=c[12776];c[14083]=c[12777];c[14084]=c[12778];c[14085]=c[12779];c[14086]=c[12780];c[14087]=c[12781];b7=793}if((b7|0)==793){b7=0;if(gy){k4=gC;k5=gz;k6=gw;b7=794}}L1147:do{if((b7|0)==794){while(1){b7=0;L1150:do{if((a[k6+(k4*40&-1)|0]&1)==0){b7=810}else{gx=c[k6+(k4*40&-1)+36>>2]|0;gG=k6+(k4*40&-1)+32|0;gE=c[10036]|0;hS=0;while(1){if((hS|0)>=(gx|0)){b7=798;break}if((a[gE+((c[gG>>2]|0)+hS|0)|0]|0)==(a[hS+103664|0]|0)){hS=hS+1|0}else{break}}if((b7|0)==798){b7=0;if((hS|0)==1){break L1147}}if((k5|0)>(k4|0)){k7=0}else{b7=810;break}while(1){if((k7|0)>=(gx|0)){b7=802;break}if((a[gE+((c[gG>>2]|0)+k7|0)|0]|0)==(a[k7+150856|0]|0)){k7=k7+1|0}else{k8=0;break}}do{if((b7|0)==802){b7=0;if((k7|0)!=5){k8=0;break}c[14088]=1;hS=k4+1|0;c[13898]=hS;k9=hS;break L1150}}while(0);while(1){if((k8|0)>=(gx|0)){break}if((a[gE+((c[gG>>2]|0)+k8|0)|0]|0)==(a[k8+150704|0]|0)){k8=k8+1|0}else{b7=810;break L1150}}if((k8|0)!=4){b7=810;break}c[14088]=0;gG=k4+1|0;c[13898]=gG;k9=gG}}while(0);do{if((b7|0)==810){b7=0;hH(56296,1,0);gG=c[13898]|0;if((k4|0)!=(gG|0)){k9=gG;break}gG=is(bZ)|0;gE=c[gG>>2]|0;if((gE|0)==1){la=+(c[gG+8>>2]|0)}else if((gE|0)==2){la=+h[gG+8>>3]}else if((gE|0)==3){la=+uz(c[gG+8>>2]|0,0)}else{b7=815;break L27}if((c[b4>>2]|0)==3){uu(c[cg>>2]|0);c[b4>>2]=1}c[11692]=~~la;k9=c[13898]|0}}while(0);gG=c[8272]|0;if((k9|0)>=(gG|0)){break L1147}k4=k9;k5=gG;k6=c[1054]|0;b7=794}}}while(0);c[260]=c[11692]}else if((gD|0)==9){gz=gv+1|0;c[13898]=gz;gC=c[8272]|0;L1184:do{if((gz|0)<(gC|0)){L1186:do{if((a[gw+(gz*40&-1)|0]&1)!=0){gy=c[gw+(gz*40&-1)+36>>2]|0;gH=gw+(gz*40&-1)+32|0;gG=0;while(1){if((gG|0)>=(gy|0)){break}if((a[gB+((c[gH>>2]|0)+gG|0)|0]|0)==(a[gG+103664|0]|0)){gG=gG+1|0}else{break L1186}}if((gG|0)==1){b7=826;break L1184}}}while(0);gH=is(bY)|0;gy=c[gH>>2]|0;if((gy|0)==1){lb=+(c[gH+8>>2]|0)}else if((gy|0)==2){lb=+h[gH+8>>3]}else if((gy|0)==3){lb=+uz(c[gH+8>>2]|0,0)}else{b7=831;break L27}if((c[ch>>2]|0)==3){uu(c[ci>>2]|0);c[ch>>2]=1}h[7030]=lb;lc=c[13898]|0;ld=c[8272]|0}else{b7=826}}while(0);if((b7|0)==826){b7=0;h[7030]=-1.0;a[56232]=1;lc=gz;ld=gC}if((lc|0)>=(ld|0)){break}gH=c[1054]|0;if((a[gH+(lc*40&-1)|0]&1)==0){b7=5123;break L27}gy=c[gH+(lc*40&-1)+36>>2]|0;gE=gH+(lc*40&-1)+32|0;gH=c[10036]|0;gx=0;while(1){if((gx|0)>=(gy|0)){b7=840;break}hS=c[gE>>2]|0;if((a[gH+(hS+gx|0)|0]|0)==(a[gx+103664|0]|0)){gx=gx+1|0}else{le=hS;break}}if((b7|0)==840){b7=0;if((gx|0)==1){break}le=c[gE>>2]|0}if((gy|0)>0){lf=0;lg=0;lh=le}else{b7=5124;break L27}while(1){if((a[lf+126368|0]|0)==(a[gH+(lf+lh|0)|0]|0)){li=lh;lk=lg}else{if((lf|0)!=1){lm=0;ln=0;lo=le;b7=849;break}li=lh-1|0;lk=1}gC=lf+1|0;if((gC|0)<(lk+gy|0)){lf=gC;lg=lk;lh=li}else{b7=847;break}}do{if((b7|0)==847){b7=0;if((lk|0)!=0){lp=1;break}if((lf|0)==0|(lf|0)==8){lp=1}else{lm=0;ln=0;lo=le;b7=849}}}while(0);do{if((b7|0)==849){while(1){b7=0;if((a[lm+126280|0]|0)==(a[gH+(lm+lo|0)|0]|0)){lq=lo;lr=ln}else{if((lm|0)!=1){b7=5125;break L27}lq=lo-1|0;lr=1}gE=lm+1|0;if((gE|0)<(lr+gy|0)){lm=gE;ln=lr;lo=lq;b7=849}else{break}}if((lr|0)!=0){lp=0;break}if((lm|0)==0|(lm|0)==8){lp=0}else{b7=5126;break L27}}}while(0);a[56232]=lp;c[13898]=lc+1}else if((gD|0)==10){gy=gv+1|0;c[13898]=gy;a[35888]=1;if((gy|0)>=(c[8272]|0)){break}L1234:do{if((a[gw+(gy*40&-1)|0]&1)!=0){gH=c[gw+(gy*40&-1)+36>>2]|0;gE=gw+(gy*40&-1)+32|0;gx=0;while(1){if((gx|0)>=(gH|0)){break}if((a[gB+((c[gE>>2]|0)+gx|0)|0]|0)==(a[gx+103664|0]|0)){gx=gx+1|0}else{break L1234}}if((gx|0)==1){break L44}}}while(0);a[14176]=1;is(bX);a[14176]=0;if((c[cj>>2]|0)!=3){c[13898]=gy;break}gE=c[ck>>2]|0;if((gE|0)==0){break}uF(53520,gE|0,32);uu(gE)}else if((gD|0)==11){lt=gv+1|0;c[13898]=lt;L1246:do{if((lt|0)<(c[8272]|0)){if((a[gw+(lt*40&-1)|0]&1)==0){b7=5127;break L27}gE=c[gw+(lt*40&-1)+36>>2]|0;gH=gw+(lt*40&-1)+32|0;gC=0;while(1){if((gC|0)>=(gE|0)){b7=872;break}gz=c[gH>>2]|0;if((a[gB+(gz+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{lu=gz;break}}if((b7|0)==872){b7=0;if((gC|0)==1){b7=874;break}lu=c[gH>>2]|0}if((gE|0)>0){lv=0;lw=0;lx=lu}else{b7=5128;break L27}while(1){if((a[lv+144616|0]|0)==(a[gB+(lv+lx|0)|0]|0)){ly=lx;lA=lw}else{if((lv|0)!=1){lB=0;lC=0;lD=lu;break}ly=lx-1|0;lA=1}gx=lv+1|0;if((gx|0)<(lA+gE|0)){lv=gx;lw=lA;lx=ly}else{b7=880;break}}do{if((b7|0)==880){b7=0;if((lA|0)==0){if(!((lv|0)==0|(lv|0)==6)){lB=0;lC=0;lD=lu;break}}a[54144]=1;break L1246}}while(0);while(1){if((a[lB+143008|0]|0)==(a[gB+(lB+lD|0)|0]|0)){lE=lD;lF=lC}else{if((lB|0)!=1){lG=0;lH=0;lI=lu;break}lE=lD-1|0;lF=1}gH=lB+1|0;if((gH|0)<(lF+gE|0)){lB=gH;lC=lF;lD=lE}else{b7=887;break}}do{if((b7|0)==887){b7=0;if((lF|0)==0){if(!((lB|0)==0|(lB|0)==3)){lG=0;lH=0;lI=lu;break}}a[54160]=1;break L1246}}while(0);while(1){if((a[lG+141464|0]|0)==(a[gB+(lG+lI|0)|0]|0)){lJ=lI;lK=lH}else{if((lG|0)!=1){b7=5129;break L27}lJ=lI-1|0;lK=1}gH=lG+1|0;if((gH|0)<(lK+gE|0)){lG=gH;lH=lK;lI=lJ}else{break}}if((lK|0)==0){if(!((lG|0)==0|(lG|0)==3)){b7=5130;break L27}}a[54152]=1}else{b7=874}}while(0);if((b7|0)==874){b7=0;a[54144]=1}c[13898]=gv+2}else if((gD|0)==12){lL=gv+1|0;c[13898]=lL;do{if((lL|0)<(c[8272]|0)){if((a[gw+(lL*40&-1)|0]&1)==0){b7=5144;break L27}gy=c[gw+(lL*40&-1)+36>>2]|0;gE=gw+(lL*40&-1)+32|0;gH=0;while(1){if((gH|0)>=(gy|0)){b7=904;break}gC=c[gE>>2]|0;if((a[gB+(gC+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{lM=gC;break}}if((b7|0)==904){b7=0;if((gH|0)==1){break}lM=c[gE>>2]|0}if((gy|0)>0){lN=0;lO=0;lP=lM}else{b7=5145;break L27}while(1){if((a[lN+144616|0]|0)==(a[gB+(lN+lP|0)|0]|0)){lQ=lP;lR=lO}else{if((lN|0)!=1){lS=0;lT=0;lU=lM;break}lQ=lP-1|0;lR=1}gC=lN+1|0;if((gC|0)<(lR+gy|0)){lN=gC;lO=lR;lP=lQ}else{b7=912;break}}do{if((b7|0)==912){b7=0;if((lR|0)==0){if(!((lN|0)==6|(lN|0)==0)){lS=0;lT=0;lU=lM;break}}c[13898]=gv+2;gE=is(bW)|0;gH=c[gE>>2]|0;if((gH|0)==1){lV=+(c[gE+8>>2]|0)}else if((gH|0)==2){lV=+h[gE+8>>3]}else if((gH|0)==3){lV=+uz(c[gE+8>>2]|0,0)}else{b7=918;break L27}if((c[cl>>2]|0)==3){uu(c[cm>>2]|0);c[cl>>2]=1}c[13366]=~~lV;break L44}}while(0);while(1){if((a[lS+128720|0]|0)==(a[gB+(lS+lU|0)|0]|0)){lW=lU;lX=lT}else{if((lS|0)!=2){lY=0;lZ=0;l_=lM;break}lW=lU-1|0;lX=1}gE=lS+1|0;if((gE|0)<(lX+gy|0)){lS=gE;lT=lX;lU=lW}else{b7=926;break}}do{if((b7|0)==926){b7=0;if((lX|0)==0){if(!((lS|0)==6|(lS|0)==1)){lY=0;lZ=0;l_=lM;break}}c[13898]=gv+2;c[13378]=0;break L44}}while(0);while(1){if((a[lY+128624|0]|0)==(a[gB+(lY+l_|0)|0]|0)){l$=l_;l0=lZ}else{if((lY|0)!=1){l1=0;l2=0;l3=lM;break}l$=l_-1|0;l0=1}gE=lY+1|0;if((gE|0)<(l0+gy|0)){lY=gE;lZ=l0;l_=l$}else{b7=933;break}}do{if((b7|0)==933){b7=0;if((l0|0)==0){if(!((lY|0)==11|(lY|0)==0)){l1=0;l2=0;l3=lM;break}}c[13898]=gv+2;c[13378]=1;break L44}}while(0);while(1){if((a[l1+128536|0]|0)==(a[gB+(l1+l3|0)|0]|0)){l4=l3;l5=l2}else{if((l1|0)!=1){l6=0;l7=0;l8=lM;break}l4=l3-1|0;l5=1}gE=l1+1|0;if((gE|0)<(l5+gy|0)){l1=gE;l2=l5;l3=l4}else{b7=940;break}}do{if((b7|0)==940){b7=0;if((l5|0)==0){if(!((l1|0)==7|(l1|0)==0)){l6=0;l7=0;l8=lM;break}}c[13898]=gv+2;c[13378]=2;break L44}}while(0);while(1){if((a[l6+128464|0]|0)==(a[gB+(l6+l8|0)|0]|0)){l9=l8;ma=l7}else{if((l6|0)!=2){mb=0;mc=0;md=lM;break}l9=l8-1|0;ma=1}gE=l6+1|0;if((gE|0)<(ma+gy|0)){l6=gE;l7=ma;l8=l9}else{b7=947;break}}do{if((b7|0)==947){b7=0;if((ma|0)==0){if(!((l6|0)==6|(l6|0)==1)){mb=0;mc=0;md=lM;break}}gE=gv+2|0;c[13898]=gE;gH=c[6074]|0;if((gH|0)==0){b7=951}else{if((c[gH+32>>2]|0)==0){b7=951}else{me=gE;mf=gw}}if((b7|0)==951){b7=0;uu(c[11662]|0);c[11658]=0;c[11659]=0;c[11662]=0;gE=ut(40)|0;if((gE|0)==0){gk();gH=ut(40)|0;if((gH|0)==0){b7=953;break L27}else{mg=gH}}else{mg=gE}c[11662]=mg;c[11658]=5;c[11659]=0;c[11660]=10;c[11661]=8;me=c[13898]|0;mf=c[1054]|0}gE=c[mf+(me*40&-1)+36>>2]|0;gH=c[mf+(me*40&-1)+32>>2]|0;gC=(a[mf+(me*40&-1)|0]&1)==0;L1373:do{if((gE|0)>0&(gC^1)){gx=c[10036]|0;gz=0;hS=0;hF=gH;while(1){if((a[gz+128352|0]|0)==(a[gx+(gz+hF|0)|0]|0)){mh=hF;mi=hS}else{if((gz|0)!=2){break}mh=hF-1|0;mi=1}cd=gz+1|0;if((cd|0)<(mi+gE|0)){gz=cd;hS=mi;hF=mh}else{b7=961;break}}do{if((b7|0)==961){b7=0;if((mi|0)==0){if(!((gz|0)==1|(gz|0)==8)){break}}c[13372]=2;mj=me+1|0;c[13898]=mj;if((mj|0)>=(c[8272]|0)){b7=5131;break L27}L1387:do{if((a[mf+(mj*40&-1)|0]&1)!=0){hF=c[mf+(mj*40&-1)+36>>2]|0;hS=mf+(mj*40&-1)+32|0;cd=0;while(1){if((cd|0)>=(hF|0)){break}if((a[gx+((c[hS>>2]|0)+cd|0)|0]|0)==(a[cd+103664|0]|0)){cd=cd+1|0}else{break L1387}}if((cd|0)==1){b7=5132;break L27}}}while(0);hS=is(bT)|0;hF=c[hS>>2]|0;if((hF|0)==1){mk=+(c[hS+8>>2]|0)}else if((hF|0)==2){mk=+h[hS+8>>3]}else if((hF|0)==3){mk=+uz(c[hS+8>>2]|0,0)}else{b7=974;break L27}if((c[cn>>2]|0)==3){uu(c[co>>2]|0);c[cn>>2]=1}hS=c[11662]|0;if((hS|0)==0){b7=978;break L27}hF=c[11659]|0;hp=c[11658]|0;do{if((hF|0)<(hp|0)){ml=hS;mm=hF}else{gA=(c[11660]|0)+hp|0;if((gA|0)==0){uu(hS);c[11662]=0;c[11658]=0;c[11659]=0;ml=0;mm=0;break}else{ho=db(hS,aa(c[11661]|0,gA),150384)|0;c[11662]=ho;c[11658]=gA;ml=ho;mm=c[11659]|0;break}}}while(0);hS=c[11661]|0;c[11659]=mm+1;h[ml+aa(hS,mm)>>3]=mk;hS=c[13898]|0;hp=c[8272]|0;L1409:do{if((hS|0)<(hp|0)){mn=hS;hF=hp;while(1){ho=c[1054]|0;if((a[ho+(mn*40&-1)|0]&1)==0){b7=5133;break L27}gA=c[ho+(mn*40&-1)+36>>2]|0;hG=ho+(mn*40&-1)+32|0;ho=c[10036]|0;hR=0;while(1){if((hR|0)>=(gA|0)){b7=988;break}if((a[ho+((c[hG>>2]|0)+hR|0)|0]|0)==(a[hR+103664|0]|0)){hR=hR+1|0}else{break}}if((b7|0)==988){b7=0;if((hR|0)==1){break L1409}}if((hF|0)>(mn|0)){mo=0}else{b7=5134;break L27}while(1){if((mo|0)>=(gA|0)){break}if((a[ho+((c[hG>>2]|0)+mo|0)|0]|0)==(a[mo+148464|0]|0)){mo=mo+1|0}else{b7=5135;break L27}}if((mo|0)!=1){b7=5136;break L27}c[13898]=mn+1;hG=is(bS)|0;ho=c[hG>>2]|0;if((ho|0)==1){mp=+(c[hG+8>>2]|0)}else if((ho|0)==2){mp=+h[hG+8>>3]}else if((ho|0)==3){mp=+uz(c[hG+8>>2]|0,0)}else{b7=998;break L27}if((c[cp>>2]|0)==3){uu(c[cq>>2]|0);c[cp>>2]=1}hG=c[11662]|0;if((hG|0)==0){b7=1002;break L27}ho=c[11659]|0;gA=c[11658]|0;do{if((ho|0)<(gA|0)){mq=hG;mr=ho}else{hR=(c[11660]|0)+gA|0;if((hR|0)==0){uu(hG);c[11662]=0;c[11658]=0;c[11659]=0;mq=0;mr=0;break}else{hC=db(hG,aa(c[11661]|0,hR),150384)|0;c[11662]=hC;c[11658]=hR;mq=hC;mr=c[11659]|0;break}}}while(0);hG=c[11661]|0;c[11659]=mr+1;h[mq+aa(hG,mr)>>3]=mp;hG=c[13898]|0;gA=c[8272]|0;if((hG|0)<(gA|0)){mn=hG;hF=gA}else{break}}}}while(0);c[13374]=c[11659];break L44}}while(0);if(gC){break}else{ms=0;mt=0;mu=gH}while(1){if((a[ms+127912|0]|0)==(a[gx+(ms+mu|0)|0]|0)){mv=mu;mw=mt}else{if((ms|0)!=2){mx=0;my=0;mz=gH;break}mv=mu-1|0;mw=1}gz=ms+1|0;if((gz|0)<(mw+gE|0)){ms=gz;mt=mw;mu=mv}else{b7=1014;break}}do{if((b7|0)==1014){b7=0;if((mw|0)==0){if(!((ms|0)==1|(ms|0)==11)){mx=0;my=0;mz=gH;break}}c[13372]=1;c[13898]=me+1;gz=is(bR)|0;hp=c[gz>>2]|0;if((hp|0)==1){mA=+(c[gz+8>>2]|0)}else if((hp|0)==2){mA=+h[gz+8>>3]}else if((hp|0)==3){mA=+uz(c[gz+8>>2]|0,0)}else{b7=1020;break L27}if((c[cr>>2]|0)==3){uu(c[cs>>2]|0);c[cr>>2]=1}h[c[11662]>>3]=mA;mB=c[13898]|0;if((c[8272]|0)<=(mB|0)){b7=5137;break L27}gz=c[1054]|0;if((a[gz+(mB*40&-1)|0]&1)==0){b7=5138;break L27}hp=c[gz+(mB*40&-1)+36>>2]|0;hS=gz+(mB*40&-1)+32|0;gz=c[10036]|0;hF=0;while(1){if((hF|0)>=(hp|0)){break}if((a[gz+((c[hS>>2]|0)+hF|0)|0]|0)==(a[hF+148464|0]|0)){hF=hF+1|0}else{b7=5139;break L27}}if((hF|0)!=1){b7=5140;break L27}c[13898]=mB+1;hS=is(bQ)|0;gz=c[hS>>2]|0;if((gz|0)==1){mC=+(c[hS+8>>2]|0)}else if((gz|0)==2){mC=+h[hS+8>>3]}else if((gz|0)==3){mC=+uz(c[hS+8>>2]|0,0)}else{b7=1034;break L27}if((c[ct>>2]|0)==3){uu(c[cu>>2]|0);c[ct>>2]=1}h[(c[11662]|0)+8>>3]=mC;mD=c[13898]|0;if(mC==0.0){b7=1038;break L27}if((mD|0)>=(c[8272]|0)){break L44}hS=c[1054]|0;if((a[hS+(mD*40&-1)|0]&1)==0){b7=5141;break L27}gz=c[hS+(mD*40&-1)+36>>2]|0;hp=hS+(mD*40&-1)+32|0;hS=c[10036]|0;cd=0;while(1){if((cd|0)>=(gz|0)){b7=1044;break}if((a[hS+((c[hp>>2]|0)+cd|0)|0]|0)==(a[cd+103664|0]|0)){cd=cd+1|0}else{mE=0;break}}if((b7|0)==1044){b7=0;if((cd|0)==1){break L44}else{mE=0}}while(1){if((mE|0)>=(gz|0)){break}if((a[hS+((c[hp>>2]|0)+mE|0)|0]|0)==(a[mE+148464|0]|0)){mE=mE+1|0}else{b7=5142;break L27}}if((mE|0)!=1){b7=5143;break L27}c[13898]=mD+1;c[11659]=2;hp=is(bO)|0;hS=c[hp>>2]|0;if((hS|0)==1){mF=+(c[hp+8>>2]|0)}else if((hS|0)==2){mF=+h[hp+8>>3]}else if((hS|0)==3){mF=+uz(c[hp+8>>2]|0,0)}else{b7=1053;break L27}if((c[cv>>2]|0)==3){uu(c[cw>>2]|0);c[cv>>2]=1}hp=c[11662]|0;c[13374]=~~((mF- +h[hp>>3])/+h[hp+8>>3]+1.0);break L44}}while(0);while(1){if((a[mx+127456|0]|0)==(a[gx+(mx+mz|0)|0]|0)){mG=mz;mH=my}else{if((mx|0)!=2){break L1373}mG=mz-1|0;mH=1}hp=mx+1|0;if((hp|0)<(mH+gE|0)){mx=hp;my=mH;mz=mG}else{break}}if((mH|0)==0){if(!((mx|0)==1|(mx|0)==4)){break}}c[13372]=0;hp=me+1|0;c[13898]=hp;if((hp|0)>=(c[8272]|0)){break L44}L1507:do{if((a[mf+(hp*40&-1)|0]&1)!=0){hS=c[mf+(hp*40&-1)+36>>2]|0;gz=mf+(hp*40&-1)+32|0;cd=0;while(1){if((cd|0)>=(hS|0)){break}if((a[gx+((c[gz>>2]|0)+cd|0)|0]|0)==(a[cd+103664|0]|0)){cd=cd+1|0}else{break L1507}}if((cd|0)==1){break L44}}}while(0);gx=is(bN)|0;hp=c[gx>>2]|0;if((hp|0)==1){mI=+(c[gx+8>>2]|0)}else if((hp|0)==2){mI=+h[gx+8>>3]}else if((hp|0)==3){mI=+uz(c[gx+8>>2]|0,0)}else{b7=1073;break L27}if((c[cx>>2]|0)==3){uu(c[cy>>2]|0);c[cx>>2]=1}c[13374]=~~mI;break L44}}while(0);if((c[13372]|0)==2){b7=1078;break L27}gE=is(bM)|0;gH=c[gE>>2]|0;if((gH|0)==1){mJ=+(c[gE+8>>2]|0)}else if((gH|0)==2){mJ=+h[gE+8>>3]}else if((gH|0)==3){mJ=+uz(c[gE+8>>2]|0,0)}else{b7=1083;break L27}if((c[cA>>2]|0)==3){uu(c[cC>>2]|0);c[cA>>2]=1}c[13374]=~~mJ;break L44}}while(0);while(1){if((a[mb+127152|0]|0)==(a[gB+(mb+md|0)|0]|0)){mK=md;mL=mc}else{if((mb|0)!=1){b7=5146;break L27}mK=md-1|0;mL=1}gE=mb+1|0;if((gE|0)<(mL+gy|0)){mb=gE;mc=mL;md=mK}else{break}}if((mL|0)==0){if(!((mb|0)==5|(mb|0)==0)){b7=5147;break L27}}c[13898]=gv+2;gy=is(bV)|0;gE=c[gy>>2]|0;if((gE|0)==1){mM=+(c[gy+8>>2]|0)}else if((gE|0)==2){mM=+h[gy+8>>3]}else if((gE|0)==3){mM=+uz(c[gy+8>>2]|0,0)}else{b7=1097;break L27}if((c[cD>>2]|0)==3){uu(c[cE>>2]|0);c[cD>>2]=1}gy=~~mM;if((gy-2|0)>>>0>8){b7=1101;break L27}c[13368]=gy;break L44}}while(0);c[13366]=5;c[13378]=0;c[13368]=4;c[13374]=5;c[13372]=0}else if((gD|0)==13){mN=gv+1|0;c[13898]=mN;do{if((mN|0)<(c[8272]|0)){if((a[gw+(mN*40&-1)|0]&1)==0){b7=5148;break L27}gy=c[gw+(mN*40&-1)+36>>2]|0;gE=gw+(mN*40&-1)+32|0;gH=0;while(1){if((gH|0)>=(gy|0)){b7=1109;break}gC=c[gE>>2]|0;if((a[gB+(gC+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{mO=gC;break}}if((b7|0)==1109){b7=0;if((gH|0)==1){break}mO=c[gE>>2]|0}if((gy|0)>0){mP=0;mQ=0;mR=mO}else{b7=5149;break L27}while(1){if((a[mP+129472|0]|0)==(a[gB+(mP+mR|0)|0]|0)){mS=mR;mT=mQ}else{if((mP|0)!=2){mU=0;mV=0;mW=mO;b7=1120;break}mS=mR-1|0;mT=1}gC=mP+1|0;if((gC|0)<(mT+gy|0)){mP=gC;mQ=mT;mR=mS}else{b7=1117;break}}do{if((b7|0)==1117){b7=0;if((mT|0)==0){if(!((mP|0)==1|(mP|0)==4)){mU=0;mV=0;mW=mO;b7=1120;break}}c[11690]=1}}while(0);L1574:do{if((b7|0)==1120){while(1){b7=0;if((a[mU+129312|0]|0)==(a[gB+(mU+mW|0)|0]|0)){mX=mW;mY=mV}else{if((mU|0)!=1){mZ=0;m_=0;m$=mO;break}mX=mW-1|0;mY=1}gE=mU+1|0;if((gE|0)<(mY+gy|0)){mU=gE;mV=mY;mW=mX;b7=1120}else{b7=1124;break}}do{if((b7|0)==1124){b7=0;if((mY|0)==0){if(!((mU|0)==0|(mU|0)==7)){mZ=0;m_=0;m$=mO;break}}c[11690]=2;break L1574}}while(0);while(1){if((a[mZ+129032|0]|0)==(a[gB+(mZ+m$|0)|0]|0)){m0=m$;m1=m_}else{if((mZ|0)!=2){b7=5150;break L27}m0=m$-1|0;m1=1}gE=mZ+1|0;if((gE|0)<(m1+gy|0)){mZ=gE;m_=m1;m$=m0}else{break}}if((m1|0)==0){if(!((mZ|0)==1|(mZ|0)==4)){b7=5151;break L27}}c[11690]=3}}while(0);c[13898]=gv+2;break L44}}while(0);c[11690]=1}else if((gD|0)==17){gy=c[11732]|0;gE=c[11756]|0;gH=c[11734]|0;m2=+h[5865];m3=+h[5864];c[11752]=0;a[47016]=0;gC=gv+1|0;c[13898]=gC;gG=c[8272]|0;L1599:do{if((gC|0)<(gG|0)){gx=gy;hp=gE;gz=gH;m4=m2;m5=m3;hS=0;hF=gC;gA=gG;hG=gw;while(1){ho=(a[hG+(hF*40&-1)|0]&1)==0;hC=c[hG+(hF*40&-1)+36>>2]|0;L1602:do{if(ho){m6=c[10036]|0;m7=hG+(hF*40&-1)+32|0}else{hR=hG+(hF*40&-1)+32|0;hw=c[10036]|0;gF=0;while(1){if((gF|0)>=(hC|0)){break}if((a[hw+((c[hR>>2]|0)+gF|0)|0]|0)==(a[gF+103664|0]|0)){gF=gF+1|0}else{m6=hw;m7=hR;break L1602}}if((gF|0)==1){m8=gx;m9=hp;na=gz;nb=m4;nc=m5;break L1599}else{m6=hw;m7=hR}}}while(0);hB=c[m7>>2]|0;hK=(hC|0)>0;nd=46944;ne=103784;L1610:while(1){L1612:do{if(!ho){if(hK){nf=0;ng=0;nh=hB;while(1){ni=a[ne+nf|0]|0;if(ni<<24>>24==(a[m6+(nf+nh|0)|0]|0)){nj=nh;nk=ng}else{if(ni<<24>>24!=36){break L1612}nj=nh-1|0;nk=1}nl=nf+1|0;if((nl|0)<(nk+hC|0)){nf=nl;ng=nk;nh=nj}else{break}}if((nk|0)==0){nm=nl}else{nn=nd;break L1610}}else{nm=0}nh=a[ne+nm|0]|0;if((nh<<24>>24|0)==36|(nh<<24>>24|0)==0){nn=nd;break L1610}}}while(0);hR=nd+8|0;hw=c[hR>>2]|0;if((hw|0)==0){nn=hR;break}else{nd=hR;ne=hw}}ne=c[nn+4>>2]|0;L1625:do{if((ne|0)==8){no=hF;b7=1210}else{c[11752]=ne;nd=hF+1|0;c[13898]=nd;if((ne|0)==1){if((nd|0)>=(gA|0)){np=hS;nq=m5;nr=m4;ns=gz;nt=hp;nu=gx;break}L1630:do{if((a[hG+(nd*40&-1)|0]&1)!=0){hC=c[hG+(nd*40&-1)+36>>2]|0;hB=hG+(nd*40&-1)+32|0;hK=0;while(1){if((hK|0)>=(hC|0)){break}if((a[m6+((c[hB>>2]|0)+hK|0)|0]|0)==(a[hK+103664|0]|0)){hK=hK+1|0}else{break L1630}}if((hK|0)==1){np=hS;nq=m5;nr=m4;ns=gz;nt=hp;nu=gx;break L1625}}}while(0);hB=is(bL)|0;hC=c[hB>>2]|0;if((hC|0)==1){nv=+(c[hB+8>>2]|0)}else if((hC|0)==2){nv=+h[hB+8>>3]}else if((hC|0)==3){nv=+uz(c[hB+8>>2]|0,0)}else{b7=1165;break L27}if((c[cL>>2]|0)==3){uu(c[cM>>2]|0);c[cL>>2]=1}np=hS;nq=m5;nr=m4;ns=~~nv;nt=hp;nu=gx;break}else if((ne|0)==2){np=hS;nq=m5;nr=m4;ns=gz;nt=hp;nu=gx;break}else if(!((ne|0)==3|(ne|0)==5|(ne|0)==4|(ne|0)==6|(ne|0)==7)){no=nd;b7=1210;break}L1646:do{if((nd|0)<(gA|0)){if((a[hG+(nd*40&-1)|0]&1)==0){nw=nd;break}hB=c[hG+(nd*40&-1)+36>>2]|0;hC=hG+(nd*40&-1)+32|0;ho=0;while(1){if((ho|0)>=(hB|0)){b7=1174;break}if((a[m6+((c[hC>>2]|0)+ho|0)|0]|0)==(a[ho+103664|0]|0)){ho=ho+1|0}else{b7=1175;break}}if((b7|0)==1174){b7=0;if(!((ho|0)!=1&(hB|0)>0)){nw=nd;break}}else if((b7|0)==1175){b7=0;if((hB|0)<=0){nw=nd;break}}hK=0;hw=0;hR=c[hC>>2]|0;while(1){if((a[hK+130016|0]|0)==(a[m6+(hK+hR|0)|0]|0)){nx=hR;ny=hw}else{if((hK|0)!=5){nw=nd;break L1646}nx=hR-1|0;ny=1}gF=hK+1|0;if((gF|0)<(ny+hB|0)){hK=gF;hw=ny;hR=nx}else{break}}if((ny|0)==0){if(!((hK|0)==4|(hK|0)==10)){nw=nd;break}}a[47016]=1;hR=hF+2|0;c[13898]=hR;nw=hR}else{nw=nd}}while(0);if((nw|0)>=(gA|0)){np=hS;nq=m5;nr=m4;ns=gz;nt=hp;nu=gx;break}L1668:do{if((a[hG+(nw*40&-1)|0]&1)!=0){nd=c[hG+(nw*40&-1)+36>>2]|0;hR=hG+(nw*40&-1)+32|0;hw=0;while(1){if((hw|0)>=(nd|0)){break}if((a[m6+((c[hR>>2]|0)+hw|0)|0]|0)==(a[hw+103664|0]|0)){hw=hw+1|0}else{break L1668}}if((hw|0)==1){np=hS;nq=m5;nr=m4;ns=gz;nt=hp;nu=gx;break L1625}}}while(0);hR=is(bK)|0;nd=c[hR>>2]|0;if((nd|0)==1){nz=+(c[hR+8>>2]|0)}else if((nd|0)==2){nz=+h[hR+8>>3]}else if((nd|0)==3){nz=+uz(c[hR+8>>2]|0,0)}else{b7=1194;break L27}if((c[cN>>2]|0)==3){uu(c[cO>>2]|0);c[cN>>2]=1}hR=c[13898]|0;if((c[8272]|0)<=(hR|0)){np=hS;nq=nz;nr=nz;ns=gz;nt=hp;nu=gx;break}nd=c[1054]|0;if((a[nd+(hR*40&-1)|0]&1)==0){np=hS;nq=nz;nr=nz;ns=gz;nt=hp;nu=gx;break}hK=c[nd+(hR*40&-1)+36>>2]|0;hB=nd+(hR*40&-1)+32|0;nd=c[10036]|0;hC=0;while(1){if((hC|0)>=(hK|0)){break}if((a[nd+((c[hB>>2]|0)+hC|0)|0]|0)==(a[hC+148464|0]|0)){hC=hC+1|0}else{np=hS;nq=nz;nr=nz;ns=gz;nt=hp;nu=gx;break L1625}}if((hC|0)!=1){np=hS;nq=nz;nr=nz;ns=gz;nt=hp;nu=gx;break}c[13898]=hR+1;hB=is(bJ)|0;nd=c[hB>>2]|0;if((nd|0)==1){nA=+(c[hB+8>>2]|0)}else if((nd|0)==2){nA=+h[hB+8>>3]}else if((nd|0)==3){nA=+uz(c[hB+8>>2]|0,0)}else{b7=1207;break L27}if((c[cP>>2]|0)!=3){np=hS;nq=nA;nr=nz;ns=gz;nt=hp;nu=gx;break}uu(c[cQ>>2]|0);c[cP>>2]=1;np=hS;nq=nA;nr=nz;ns=gz;nt=hp;nu=gx}}while(0);L1696:do{if((b7|0)==1210){b7=0;L1698:do{if((gA|0)>(no|0)){if((a[hG+(no*40&-1)|0]&1)==0){break}ne=c[hG+(no*40&-1)+36>>2]|0;hB=hG+(no*40&-1)+32|0;nd=0;while(1){if((nd|0)>=(ne|0)){break}if((a[m6+((c[hB>>2]|0)+nd|0)|0]|0)==(a[nd+148464|0]|0)){nd=nd+1|0}else{break L1698}}if((nd|0)!=1){break}c[13898]=no+1;np=hS+1|0;nq=m5;nr=m4;ns=gz;nt=hp;nu=gx;break L1696}}while(0);if((hS|0)==0){hR=is(bI)|0;hC=c[hR>>2]|0;if((hC|0)==1){nB=+(c[hR+8>>2]|0)}else if((hC|0)==2){nB=+h[hR+8>>3]}else if((hC|0)==3){nB=+uz(c[hR+8>>2]|0,0)}else{b7=1224;break L27}if((c[cF>>2]|0)==3){uu(c[cG>>2]|0);c[cF>>2]=1}hR=~~nB;np=0;nq=m5;nr=m4;ns=gz;nt=hR;nu=hR;break}else if((hS|0)==1){hR=is(bH)|0;hC=c[hR>>2]|0;if((hC|0)==1){nC=+(c[hR+8>>2]|0)}else if((hC|0)==2){nC=+h[hR+8>>3]}else if((hC|0)==3){nC=+uz(c[hR+8>>2]|0,0)}else{b7=1232;break L27}if((c[cH>>2]|0)==3){uu(c[cI>>2]|0);c[cH>>2]=1}np=1;nq=m5;nr=m4;ns=gz;nt=~~nC;nu=gx;break}else if((hS|0)==2){hR=is(bG)|0;hC=c[hR>>2]|0;if((hC|0)==1){nD=+(c[hR+8>>2]|0)}else if((hC|0)==2){nD=+h[hR+8>>3]}else if((hC|0)==3){nD=+uz(c[hR+8>>2]|0,0)}else{b7=1240;break L27}if((c[cJ>>2]|0)==3){uu(c[cK>>2]|0);c[cJ>>2]=1}np=2;nq=m5;nr=m4;ns=~~nD;nt=hp;nu=gx;break}else{b7=1244;break L27}}}while(0);hR=c[13898]|0;hC=c[8272]|0;if((hR|0)>=(hC|0)){m8=nu;m9=nt;na=ns;nb=nr;nc=nq;break L1599}gx=nu;hp=nt;gz=ns;m4=nr;m5=nq;hS=np;hF=hR;gA=hC;hG=c[1054]|0}}else{m8=gy;m9=gE;na=gH;nb=m2;nc=m3}}while(0);if((m8-2|0)>>>0>998){b7=5152;break L27}if((m9-2|0)>>>0>998){b7=5153;break L27}if((c[11752]|0)==0){c[11752]=1}if(nb<0.0|nc<0.0){b7=1251;break L27}c[11732]=m8;c[11756]=m9;c[11734]=na;h[5865]=nb;h[5864]=nc;a[47032]=1}else if((gD|0)==20){c[13898]=gv+1;uu(c[12886]|0);c[12886]=0;nE=c[13898]|0;gH=c[8272]|0;L1745:do{if((nE|0)<(gH|0)){gE=c[1054]|0;L1747:do{if((a[gE+(nE*40&-1)|0]&1)!=0){gy=c[gE+(nE*40&-1)+36>>2]|0;gG=gE+(nE*40&-1)+32|0;gC=c[10036]|0;hG=0;while(1){if((hG|0)>=(gy|0)){b7=1258;break}if((a[gC+((c[gG>>2]|0)+hG|0)|0]|0)==(a[hG+103664|0]|0)){hG=hG+1|0}else{nF=0;break}}if((b7|0)==1258){b7=0;if((hG|0)==1){break L1745}else{nF=0}}while(1){if((nF|0)>=(gy|0)){b7=1265;break}if((a[gC+((c[gG>>2]|0)+nF|0)|0]|0)==(a[nF+132080|0]|0)){nF=nF+1|0}else{nG=0;break}}do{if((b7|0)==1265){b7=0;if((nF|0)!=6){nG=0;break}hG=nE+1|0;c[13898]=hG;L1760:do{if((hG|0)<(gH|0)){L1762:do{if((a[gE+(hG*40&-1)|0]&1)!=0){gA=c[gE+(hG*40&-1)+36>>2]|0;hF=gE+(hG*40&-1)+32|0;hS=0;while(1){if((hS|0)>=(gA|0)){break}if((a[gC+((c[hF>>2]|0)+hS|0)|0]|0)==(a[hS+103664|0]|0)){hS=hS+1|0}else{break L1762}}if((hS|0)==1){b7=1275;break L1760}}}while(0);a[14176]=1;is(bD);a[14176]=0;if((c[cU>>2]|0)==3){nd=c[cV>>2]|0;if((nd|0)==0){b7=1275;break}else{nH=nd;break}}else{c[13898]=hG;b7=1275;break}}else{b7=1275}}while(0);do{if((b7|0)==1275){b7=0;hG=bA(4,179864)|0;if((hG|0)!=0){nd=bP(hG|0)|0;if((nd|0)!=0){nH=nd;break}}nd=bU(130448)|0;if((nd|0)!=0){hG=bP(nd|0)|0;if((hG|0)!=0){nH=hG;break}}hG=bU(130352)|0;if((hG|0)!=0){nd=bP(hG|0)|0;if((nd|0)!=0){nH=nd;break}}nd=bU(130304)|0;if((nd|0)==0){nH=0;break}nH=bP(nd|0)|0}}while(0);if((bA(4,((nH|0)!=0?nH:179864)|0)|0)==0){b7=1284;break L27}nd=c[b$()>>2]|0;if((nd|0)==0){nI=0}else{nI=bP(nd|0)|0}c[12886]=nI;nd=c[m>>2]|0;cf(nd|0,130136,(v=i,i=i+8|0,c[v>>2]=nI,v)|0);uu(c[8270]|0);c[8270]=nH;bA(4,139696);break L44}}while(0);while(1){if((nG|0)>=(gy|0)){break}if((a[gC+((c[gG>>2]|0)+nG|0)|0]|0)==(a[nG+103664|0]|0)){nG=nG+1|0}else{break L1747}}if((nG|0)==1){b7=1293;break L27}}}while(0);a[14176]=1;is(bE);a[14176]=0;if((c[cR>>2]|0)!=3){b7=1292;break L27}gE=c[cT>>2]|0;c[12886]=gE;if((gE|0)==0){b7=1295;break L27}else{break L44}}}while(0);gH=c[8270]|0;do{if((gH|0)==0){nJ=0}else{if((aY(gH|0,139696)|0)==0){nJ=gH;break}bA(4,139696);nJ=c[8270]|0}}while(0);uu(nJ);c[8270]=0}else if((gD|0)==18){nK=gv+1|0;c[13898]=nK;gH=c[8272]|0;if((nK|0)>=(gH|0)){b7=5156;break L27}L1802:do{if((a[gw+(nK*40&-1)|0]&1)==0){nL=gw+(nK*40&-1)+32|0;nM=gw+(nK*40&-1)+36|0;b7=1308}else{gE=gw+(nK*40&-1)+36|0;gG=c[gE>>2]|0;gC=gw+(nK*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(gG|0)){b7=1303;break}if((a[gB+((c[gC>>2]|0)+gy|0)|0]|0)==(a[gy+103664|0]|0)){gy=gy+1|0}else{nN=0;break}}if((b7|0)==1303){b7=0;if((gy|0)==1){b7=5157;break L27}else{nN=0}}while(1){if((nN|0)>=(gG|0)){break}if((a[gB+((c[gC>>2]|0)+nN|0)|0]|0)==(a[nN+148464|0]|0)){nN=nN+1|0}else{nL=gC;nM=gE;b7=1308;break L1802}}if((nN|0)==1){nO=nK}else{nL=gC;nM=gE;b7=1308}}}while(0);if((b7|0)==1308){b7=0;gG=gv+2|0;c[13898]=gG;gy=c[nM>>2]|0;nd=(gy|0)>49?49:gy;gy=c[nL>>2]|0;hG=0;while(1){hF=hG+1|0;a[hG+24544|0]=a[gB+gy|0]|0;if((hF|0)==(nd|0)){break}else{gy=gy+1|0;hG=hF}}a[nd+24544|0]=0;nO=gG}if((nO|0)>=(gH|0)){break}if((a[gw+(nO*40&-1)|0]&1)==0){break}hG=c[gw+(nO*40&-1)+36>>2]|0;gy=gw+(nO*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(hG|0)){b7=1316;break}if((a[gB+((c[gy>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{nP=0;break}}if((b7|0)==1316){b7=0;if((hF|0)==1){break}else{nP=0}}while(1){if((nP|0)>=(hG|0)){break}if((a[gB+((c[gy>>2]|0)+nP|0)|0]|0)==(a[nP+148464|0]|0)){nP=nP+1|0}else{break L44}}if((nP|0)!=1){break}nQ=nO+1|0;c[13898]=nQ;if((nQ|0)>=(gH|0)){b7=5158;break L27}L1833:do{if((a[gw+(nQ*40&-1)|0]&1)==0){nR=gw+(nQ*40&-1)+32|0;nS=gw+(nQ*40&-1)+36|0}else{gy=gw+(nQ*40&-1)+36|0;hG=c[gy>>2]|0;hF=gw+(nQ*40&-1)+32|0;gG=0;while(1){if((gG|0)>=(hG|0)){break}if((a[gB+((c[hF>>2]|0)+gG|0)|0]|0)==(a[gG+103664|0]|0)){gG=gG+1|0}else{nR=hF;nS=gy;break L1833}}if((gG|0)==1){b7=5159;break L27}else{nR=hF;nS=gy}}}while(0);c[13898]=nO+2;gH=c[nS>>2]|0;hG=(gH|0)>49?49:gH;gH=c[nR>>2]|0;gE=0;while(1){gC=gE+1|0;a[gE+24595|0]=a[gB+gH|0]|0;if((gC|0)==(hG|0)){break}else{gH=gH+1|0;gE=gC}}a[hG+24595|0]=0}else if((gD|0)==19){nT=gv+1|0;c[13898]=nT;L1845:do{if((nT|0)<(c[8272]|0)){gE=(a[gw+(nT*40&-1)|0]&1)==0;gH=c[gw+(nT*40&-1)+36>>2]|0;gC=gw+(nT*40&-1)+32|0;L1847:do{if(!gE){nd=0;while(1){if((nd|0)>=(gH|0)){b7=1335;break}if((a[gB+((c[gC>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{nU=0;break}}if((b7|0)==1335){b7=0;if((nd|0)==1){b7=1336;break L1845}else{nU=0}}while(1){if((nU|0)>=(gH|0)){break}if((a[gB+((c[gC>>2]|0)+nU|0)|0]|0)==(a[nU+132080|0]|0)){nU=nU+1|0}else{break L1847}}if((nU|0)!=6){break}nd=bA(2,179864)|0;gA=(nd|0)==0;L1858:do{if(!gA){if((aQ(nd|0,131976)|0)==0){if((aQ(nd|0,131752)|0)!=0){b7=1343}}else{b7=1343}if((b7|0)==1343){b7=0;c[11252]=15}do{if((aQ(nd|0,200432)|0)==0){if((aQ(nd|0,131528)|0)!=0){break}if((aQ(nd|0,131448)|0)==0){break L1858}}}while(0);c[11252]=14}}while(0);c[13898]=(c[13898]|0)+1;a[cW]=a[25160]|0;a[cW+1|0]=a[25161|0]|0;a[cW+2|0]=a[25162|0]|0;c[by>>2]=3;c[bz>>2]=8;c[bB>>2]=cW;c[bC>>2]=48904;if(gA){break L1845}nd=cB(0)|0;if((nd|0)==0){break L44}if((aY(nd|0,171384)|0)==0){uB(48904,cW|0);break L44}gz=bi(nd|0,171384)|0;if((gz|0)==-1){uh(-1,131096,(v=i,i=i+8|0,c[v>>2]=nd,v)|0);break L44}if((aG(gz|0,bB|0,by|0,bC|0,bz|0)|0)==-1){uh(-1,130952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}cz(gz|0);break L44}}while(0);gy=c[gC>>2]|0;hF=(gH|0)>0;gG=24408;gz=184456;L1882:while(1){L1884:do{if(!gE){if(hF){nd=0;hp=0;gx=gy;while(1){hC=a[gz+nd|0]|0;if(hC<<24>>24==(a[gB+(nd+gx|0)|0]|0)){nV=gx;nW=hp}else{if(hC<<24>>24!=36){break L1884}nV=gx-1|0;nW=1}nX=nd+1|0;if((nX|0)<(nW+gH|0)){nd=nX;hp=nW;gx=nV}else{break}}if((nW|0)==0){nY=nX}else{nZ=gG;break L1882}}else{nY=0}gx=a[gz+nY|0]|0;if((gx<<24>>24|0)==36|(gx<<24>>24|0)==0){nZ=gG;break L1882}}}while(0);gA=gG+8|0;gx=c[gA>>2]|0;if((gx|0)==0){nZ=gA;break}else{gG=gA;gz=gx}}gz=c[nZ+4>>2]|0;if((gz|0)==16){b7=1360;break L27}c[11252]=gz;c[13898]=gv+2;a[cW]=a[25160]|0;a[cW+1|0]=a[25161|0]|0;a[cW+2|0]=a[25162|0]|0;c[by>>2]=3;c[bz>>2]=8;c[bB>>2]=cW;c[bC>>2]=48904}else{b7=1336}}while(0);if((b7|0)==1336){b7=0;c[11252]=0;a[cW]=a[25160]|0;a[cW+1|0]=a[25161|0]|0;a[cW+2|0]=a[25162|0]|0;c[by>>2]=3;c[bz>>2]=8;c[bB>>2]=cW;c[bC>>2]=48904}hG=48904;c[hG>>2]=0;c[hG+4>>2]=0;hG=c[11252]|0;if((hG|0)==15){a[48904]=-62;a[48905]=-80;break}else if((hG|0)==12|(hG|0)==13){a[48904]=-100;break}else if((hG|0)==5|(hG|0)==6|(hG|0)==7){a[48904]=-8;break}else if((hG|0)==14|(hG|0)==8){break}else{a[48904]=-80;break}}else if((gD|0)==21){hG=gv+1|0;c[13898]=hG;gz=c[8272]|0;if((hG|0)<(gz|0)){n_=hG;n$=gz;n0=gw}else{break}while(1){gz=c[n0+(n_*40&-1)+36>>2]|0;hG=n0+(n_*40&-1)+32|0;if((a[n0+(n_*40&-1)|0]&1)==0){b7=5162;break L27}gG=c[10036]|0;gH=0;while(1){if((gH|0)>=(gz|0)){b7=1380;break}gy=c[hG>>2]|0;if((a[gG+(gy+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{n1=gy;break}}if((b7|0)==1380){b7=0;if((gH|0)==1){break L44}n1=c[hG>>2]|0}L1917:do{if((gz|0)>0){gy=0;hF=0;gE=n1;while(1){if((a[gy+132904|0]|0)==(a[gG+(gy+gE|0)|0]|0)){n2=gE;n3=hF}else{if((gy|0)!=3){n4=0;n5=0;n6=n1;break}n2=gE-1|0;n3=1}gC=gy+1|0;if((gC|0)<(n3+gz|0)){gy=gC;hF=n3;gE=n2}else{b7=1387;break}}do{if((b7|0)==1387){b7=0;if((n3|0)==0){if(!((gy|0)==7|(gy|0)==2)){n4=0;n5=0;n6=n1;break}}n7=n_+1|0;c[13898]=n7;L1929:do{if((n7|0)<(n$|0)){L1931:do{if((a[n0+(n7*40&-1)|0]&1)!=0){gE=c[n0+(n7*40&-1)+36>>2]|0;hF=n0+(n7*40&-1)+32|0;gC=0;while(1){if((gC|0)>=(gE|0)){b7=1394;break}if((a[gG+((c[hF>>2]|0)+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{n8=0;break}}if((b7|0)==1394){b7=0;if((gC|0)==1){break L1929}else{n8=0}}while(1){if((n8|0)>=(gE|0)){break}if((a[gG+((c[hF>>2]|0)+n8|0)|0]|0)==(a[n8+103664|0]|0)){n8=n8+1|0}else{break L1931}}if((n8|0)==1){b7=1403;break L27}}}while(0);a[14176]=1;is(bw);a[14176]=0;if((c[cX>>2]|0)!=3){b7=1402;break L27}hF=c[cY>>2]|0;c[10568]=hF;if((hF|0)==0){b7=1405;break L27}else{break L1917}}}while(0);hF=c[10568]|0;if((hF|0)!=0){uu(hF)}c[10568]=0;break L1917}}while(0);while(1){if((a[n4+132632|0]|0)==(a[gG+(n4+n6|0)|0]|0)){n9=n6;oa=n5}else{if((n4|0)!=3){ob=0;oc=0;od=n1;break}n9=n6-1|0;oa=1}gy=n4+1|0;if((gy|0)<(oa+gz|0)){n4=gy;n5=oa;n6=n9}else{b7=1411;break}}do{if((b7|0)==1411){b7=0;if((oa|0)==0){if(!((n4|0)==2|(n4|0)==14)){ob=0;oc=0;od=n1;break}}a[42568]=1;c[13898]=n_+1;break L1917}}while(0);while(1){if((a[ob+132544|0]|0)==(a[gG+(ob+od|0)|0]|0)){oe=od;of=oc}else{if((ob|0)!=5){b7=1423;break L1917}oe=od-1|0;of=1}gy=ob+1|0;if((gy|0)<(of+gz|0)){ob=gy;oc=of;od=oe}else{break}}if((of|0)==0){if(!((ob|0)==4|(ob|0)==16)){b7=1423;break}}a[42568]=0;c[13898]=n_+1}else{b7=1423}}while(0);L1969:do{if((b7|0)==1423){b7=0;if((n$|0)>(n_|0)){og=0}else{b7=5163;break L27}while(1){if((og|0)>=(gz|0)){b7=1426;break}if((a[gG+(og+n1|0)|0]|0)==(a[og+132432|0]|0)){og=og+1|0}else{oh=0;break}}do{if((b7|0)==1426){b7=0;if((og|0)!=5){oh=0;break}a[42560]=1;c[13898]=n_+1;break L1969}}while(0);while(1){if((oh|0)>=(gz|0)){break}if((a[gG+(oh+n1|0)|0]|0)==(a[oh+132304|0]|0)){oh=oh+1|0}else{b7=5164;break L27}}if((oh|0)!=7){b7=5165;break L27}a[42560]=0;c[13898]=n_+1}}while(0);gG=c[13898]|0;gz=c[8272]|0;if((gG|0)>=(gz|0)){break L44}n_=gG;n$=gz;n0=c[1054]|0}}else if((gD|0)==22){gz=gv+1|0;c[13898]=gz;gG=c[8272]|0;L1985:do{if((gz|0)<(gG|0)){hG=a[gw+(gz*40&-1)|0]|0;L1987:do{if((hG&1)==0){oi=0;oj=gz;ok=gG;ol=gw;om=hG}else{gH=c[gw+(gz*40&-1)+36>>2]|0;gy=gw+(gz*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(gH|0)){break}if((a[gB+((c[gy>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{oi=0;oj=gz;ok=gG;ol=gw;om=hG;break L1987}}if((hF|0)==1){break L1985}else{oi=0;oj=gz;ok=gG;ol=gw;om=hG}}}while(0);L1993:while(1){hG=(om&1)==0;L1995:do{if(!hG){gy=c[ol+(oj*40&-1)+36>>2]|0;gH=ol+(oj*40&-1)+32|0;gE=c[10036]|0;gC=0;while(1){if((gC|0)>=(gy|0)){break}if((a[gE+((c[gH>>2]|0)+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{break L1995}}if((gC|0)==1){on=oi;break L1993}}}while(0);if((oj|0)>=(ok|0)){b7=1453;break L27}L2003:do{if(!hG){hF=c[ol+(oj*40&-1)+36>>2]|0;gH=ol+(oj*40&-1)+32|0;gE=c[10036]|0;gy=0;while(1){if((gy|0)>=(hF|0)){break}if((a[gE+((c[gH>>2]|0)+gy|0)|0]|0)==(a[gy+103664|0]|0)){gy=gy+1|0}else{break L2003}}if((gy|0)==1){b7=1453;break L27}}}while(0);a[14176]=1;is(bu);a[14176]=0;if((c[cZ>>2]|0)!=3){b7=1452;break L27}hG=c[c_>>2]|0;c[bv>>2]=hG;if((hG|0)==0){b7=1462;break L27}if((oi|0)==0){oo=0}else{oo=uA(oi|0)|0}iQ(bv);hG=c[bv>>2]|0;gH=db(oi,(oo+2|0)+(uA(hG|0)|0)|0,140552)|0;if((oo|0)==0){uB(gH|0,hG|0)}else{gE=gH+oo|0;hF=gH+(oo+1|0)|0;uB(hF|0,hG|0);a[gE]=58}uu(hG);hG=c[13898]|0;gE=c[8272]|0;if((hG|0)>=(gE|0)){on=gH;break}hF=c[1054]|0;oi=gH;oj=hG;ok=gE;ol=hF;om=a[hF+(hG*40&-1)|0]|0}if((on|0)==0){break L44}ur(4,on);uu(on);break L44}}while(0);ur(32,0)}else if((gD|0)==23){jp()}else if((gD|0)==24){gG=gv+1|0;c[13898]=gG;gz=c[8272]|0;if((gG|0)<(gz|0)){hG=0;hF=gG;gG=gz;gz=gw;L2026:while(1){gE=(a[gz+(hF*40&-1)|0]&1)==0;gH=c[gz+(hF*40&-1)+36>>2]|0;gC=gz+(hF*40&-1)+32|0;L2028:do{if(gE){op=c[gC>>2]|0;b7=1704}else{hS=c[10036]|0;gx=0;while(1){if((gx|0)>=(gH|0)){b7=1473;break}gA=c[gC>>2]|0;if((a[hS+(gA+gx|0)|0]|0)==(a[gx+103664|0]|0)){gx=gx+1|0}else{oq=gA;break}}if((b7|0)==1473){b7=0;if((gx|0)==1){or=hG;break L2026}oq=c[gC>>2]|0}if((gH|0)>0){os=0;ot=0;ou=oq}else{op=oq;b7=1704;break}while(1){gA=os+2|0;if((a[gA+136104|0]|0)==(a[hS+(os+ou|0)|0]|0)){ov=ou;ow=ot}else{if((gA|0)!=3){ox=0;oy=0;oz=oq;break}ov=ou-1|0;ow=1}gA=os+1|0;if((gA|0)<(ow+gH|0)){os=gA;ot=ow;ou=ov}else{b7=1480;break}}do{if((b7|0)==1480){b7=0;if((ow|0)==0){if(!((os|0)==0|(os|0)==5)){ox=0;oy=0;oz=oq;break}}a[66412]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[ox+136104|0]|0)==(a[hS+(ox+oz|0)|0]|0)){oC=oz;oD=oy}else{if((ox|0)!=3){oE=0;oF=0;oG=oq;break}oC=oz-1|0;oD=1}gx=ox+1|0;if((gx|0)<(oD+gH|0)){ox=gx;oy=oD;oz=oC}else{b7=1487;break}}do{if((b7|0)==1487){b7=0;if((oD|0)==0){if(!((ox|0)==2|(ox|0)==7)){oE=0;oF=0;oG=oq;break}}a[66412]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=oE+2|0;if((a[gx+135864|0]|0)==(a[hS+(oE+oG|0)|0]|0)){oH=oG;oI=oF}else{if((gx|0)!=3){oJ=0;oK=0;oL=oq;break}oH=oG-1|0;oI=1}gx=oE+1|0;if((gx|0)<(oI+gH|0)){oE=gx;oF=oI;oG=oH}else{b7=1496;break}}do{if((b7|0)==1496){b7=0;if((oI|0)==0){if(!((oE|0)==0|(oE|0)==5)){oJ=0;oK=0;oL=oq;break}}a[65724]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[oJ+135864|0]|0)==(a[hS+(oJ+oL|0)|0]|0)){oM=oL;oN=oK}else{if((oJ|0)!=3){oO=0;oP=0;oQ=oq;break}oM=oL-1|0;oN=1}gx=oJ+1|0;if((gx|0)<(oN+gH|0)){oJ=gx;oK=oN;oL=oM}else{b7=1503;break}}do{if((b7|0)==1503){b7=0;if((oN|0)==0){if(!((oJ|0)==2|(oJ|0)==7)){oO=0;oP=0;oQ=oq;break}}a[65724]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=oO+2|0;if((a[gx+135680|0]|0)==(a[hS+(oO+oQ|0)|0]|0)){oR=oQ;oS=oP}else{if((gx|0)!=3){oT=0;oU=0;oV=oq;break}oR=oQ-1|0;oS=1}gx=oO+1|0;if((gx|0)<(oS+gH|0)){oO=gx;oP=oS;oQ=oR}else{b7=1510;break}}do{if((b7|0)==1510){b7=0;if((oS|0)==0){if(!((oO|0)==0|(oO|0)==5)){oT=0;oU=0;oV=oq;break}}a[65036]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[oT+135680|0]|0)==(a[hS+(oT+oV|0)|0]|0)){oW=oV;oX=oU}else{if((oT|0)!=3){oY=0;oZ=0;o_=oq;break}oW=oV-1|0;oX=1}gx=oT+1|0;if((gx|0)<(oX+gH|0)){oT=gx;oU=oX;oV=oW}else{b7=1517;break}}do{if((b7|0)==1517){b7=0;if((oX|0)==0){if(!((oT|0)==2|(oT|0)==7)){oY=0;oZ=0;o_=oq;break}}a[65036]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=oY+2|0;if((a[gx+135568|0]|0)==(a[hS+(oY+o_|0)|0]|0)){o$=o_;o0=oZ}else{if((gx|0)!=4){o1=0;o2=0;o3=oq;break}o$=o_-1|0;o0=1}gx=oY+1|0;if((gx|0)<(o0+gH|0)){oY=gx;oZ=o0;o_=o$}else{b7=1524;break}}do{if((b7|0)==1524){b7=0;if((o0|0)==0){if(!((oY|0)==1|(oY|0)==6)){o1=0;o2=0;o3=oq;break}}a[69164]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[o1+135568|0]|0)==(a[hS+(o1+o3|0)|0]|0)){o4=o3;o5=o2}else{if((o1|0)!=4){o6=0;o7=0;o8=oq;break}o4=o3-1|0;o5=1}gx=o1+1|0;if((gx|0)<(o5+gH|0)){o1=gx;o2=o5;o3=o4}else{b7=1531;break}}do{if((b7|0)==1531){b7=0;if((o5|0)==0){if(!((o1|0)==3|(o1|0)==8)){o6=0;o7=0;o8=oq;break}}a[69164]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=o6+2|0;if((a[gx+135376|0]|0)==(a[hS+(o6+o8|0)|0]|0)){o9=o8;pa=o7}else{if((gx|0)!=4){pb=0;pc=0;pd=oq;break}o9=o8-1|0;pa=1}gx=o6+1|0;if((gx|0)<(pa+gH|0)){o6=gx;o7=pa;o8=o9}else{b7=1538;break}}do{if((b7|0)==1538){b7=0;if((pa|0)==0){if(!((o6|0)==1|(o6|0)==6)){pb=0;pc=0;pd=oq;break}}a[68476]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[pb+135376|0]|0)==(a[hS+(pb+pd|0)|0]|0)){pe=pd;pf=pc}else{if((pb|0)!=4){pg=0;ph=0;pi=oq;break}pe=pd-1|0;pf=1}gx=pb+1|0;if((gx|0)<(pf+gH|0)){pb=gx;pc=pf;pd=pe}else{b7=1545;break}}do{if((b7|0)==1545){b7=0;if((pf|0)==0){if(!((pb|0)==3|(pb|0)==8)){pg=0;ph=0;pi=oq;break}}a[68476]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=pg+2|0;if((a[gx+135216|0]|0)==(a[hS+(pg+pi|0)|0]|0)){pj=pi;pk=ph}else{if((gx|0)!=4){pl=0;pm=0;pn=oq;break}pj=pi-1|0;pk=1}gx=pg+1|0;if((gx|0)<(pk+gH|0)){pg=gx;ph=pk;pi=pj}else{b7=1552;break}}do{if((b7|0)==1552){b7=0;if((pk|0)==0){if(!((pg|0)==1|(pg|0)==6)){pl=0;pm=0;pn=oq;break}}a[66413]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[pl+135216|0]|0)==(a[hS+(pl+pn|0)|0]|0)){po=pn;pp=pm}else{if((pl|0)!=4){pq=0;pr=0;ps=oq;break}po=pn-1|0;pp=1}gx=pl+1|0;if((gx|0)<(pp+gH|0)){pl=gx;pm=pp;pn=po}else{b7=1559;break}}do{if((b7|0)==1559){b7=0;if((pp|0)==0){if(!((pl|0)==3|(pl|0)==8)){pq=0;pr=0;ps=oq;break}}a[66413]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=pq+2|0;if((a[gx+135104|0]|0)==(a[hS+(pq+ps|0)|0]|0)){pt=ps;pu=pr}else{if((gx|0)!=4){pv=0;pw=0;px=oq;break}pt=ps-1|0;pu=1}gx=pq+1|0;if((gx|0)<(pu+gH|0)){pq=gx;pr=pu;ps=pt}else{b7=1566;break}}do{if((b7|0)==1566){b7=0;if((pu|0)==0){if(!((pq|0)==1|(pq|0)==6)){pv=0;pw=0;px=oq;break}}a[65725]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[pv+135104|0]|0)==(a[hS+(pv+px|0)|0]|0)){py=px;pz=pw}else{if((pv|0)!=4){pA=0;pB=0;pC=oq;break}py=px-1|0;pz=1}gx=pv+1|0;if((gx|0)<(pz+gH|0)){pv=gx;pw=pz;px=py}else{b7=1573;break}}do{if((b7|0)==1573){b7=0;if((pz|0)==0){if(!((pv|0)==3|(pv|0)==8)){pA=0;pB=0;pC=oq;break}}a[65725]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=pA+2|0;if((a[gx+134880|0]|0)==(a[hS+(pA+pC|0)|0]|0)){pD=pC;pE=pB}else{if((gx|0)!=4){pF=0;pG=0;pH=oq;break}pD=pC-1|0;pE=1}gx=pA+1|0;if((gx|0)<(pE+gH|0)){pA=gx;pB=pE;pC=pD}else{b7=1580;break}}do{if((b7|0)==1580){b7=0;if((pE|0)==0){if(!((pA|0)==1|(pA|0)==6)){pF=0;pG=0;pH=oq;break}}a[65037]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[pF+134880|0]|0)==(a[hS+(pF+pH|0)|0]|0)){pI=pH;pJ=pG}else{if((pF|0)!=4){pK=0;pL=0;pM=oq;break}pI=pH-1|0;pJ=1}gx=pF+1|0;if((gx|0)<(pJ+gH|0)){pF=gx;pG=pJ;pH=pI}else{b7=1587;break}}do{if((b7|0)==1587){b7=0;if((pJ|0)==0){if(!((pF|0)==3|(pF|0)==8)){pK=0;pL=0;pM=oq;break}}a[65037]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=pK+2|0;if((a[gx+134728|0]|0)==(a[hS+(pK+pM|0)|0]|0)){pN=pM;pO=pL}else{if((gx|0)!=5){pP=0;pQ=0;pR=oq;break}pN=pM-1|0;pO=1}gx=pK+1|0;if((gx|0)<(pO+gH|0)){pK=gx;pL=pO;pM=pN}else{b7=1594;break}}do{if((b7|0)==1594){b7=0;if((pO|0)==0){if(!((pK|0)==2|(pK|0)==7)){pP=0;pQ=0;pR=oq;break}}a[69165]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[pP+134728|0]|0)==(a[hS+(pP+pR|0)|0]|0)){pS=pR;pT=pQ}else{if((pP|0)!=5){pU=0;pV=0;pW=oq;break}pS=pR-1|0;pT=1}gx=pP+1|0;if((gx|0)<(pT+gH|0)){pP=gx;pQ=pT;pR=pS}else{b7=1601;break}}do{if((b7|0)==1601){b7=0;if((pT|0)==0){if(!((pP|0)==4|(pP|0)==9)){pU=0;pV=0;pW=oq;break}}a[69165]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=pU+2|0;if((a[gx+134600|0]|0)==(a[hS+(pU+pW|0)|0]|0)){pX=pW;pY=pV}else{if((gx|0)!=5){pZ=0;p_=0;p$=oq;break}pX=pW-1|0;pY=1}gx=pU+1|0;if((gx|0)<(pY+gH|0)){pU=gx;pV=pY;pW=pX}else{b7=1608;break}}do{if((b7|0)==1608){b7=0;if((pY|0)==0){if(!((pU|0)==2|(pU|0)==7)){pZ=0;p_=0;p$=oq;break}}a[68477]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[pZ+134600|0]|0)==(a[hS+(pZ+p$|0)|0]|0)){p0=p$;p1=p_}else{if((pZ|0)!=5){p2=0;p3=0;p4=oq;break}p0=p$-1|0;p1=1}gx=pZ+1|0;if((gx|0)<(p1+gH|0)){pZ=gx;p_=p1;p$=p0}else{b7=1615;break}}do{if((b7|0)==1615){b7=0;if((p1|0)==0){if(!((pZ|0)==4|(pZ|0)==9)){p2=0;p3=0;p4=oq;break}}a[68477]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=p2+2|0;if((a[gx+134488|0]|0)==(a[hS+(p2+p4|0)|0]|0)){p5=p4;p6=p3}else{if((gx|0)!=4){p7=0;p8=0;p9=oq;break}p5=p4-1|0;p6=1}gx=p2+1|0;if((gx|0)<(p6+gH|0)){p2=gx;p3=p6;p4=p5}else{b7=1622;break}}do{if((b7|0)==1622){b7=0;if((p6|0)==0){if(!((p2|0)==1|(p2|0)==6)){p7=0;p8=0;p9=oq;break}}a[67100]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[p7+134488|0]|0)==(a[hS+(p7+p9|0)|0]|0)){qa=p9;qb=p8}else{if((p7|0)!=4){qc=0;qd=0;qe=oq;break}qa=p9-1|0;qb=1}gx=p7+1|0;if((gx|0)<(qb+gH|0)){p7=gx;p8=qb;p9=qa}else{b7=1629;break}}do{if((b7|0)==1629){b7=0;if((qb|0)==0){if(!((p7|0)==3|(p7|0)==8)){qc=0;qd=0;qe=oq;break}}a[67100]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=qc+2|0;if((a[gx+134320|0]|0)==(a[hS+(qc+qe|0)|0]|0)){qf=qe;qg=qd}else{if((gx|0)!=5){qh=0;qi=0;qj=oq;break}qf=qe-1|0;qg=1}gx=qc+1|0;if((gx|0)<(qg+gH|0)){qc=gx;qd=qg;qe=qf}else{b7=1636;break}}do{if((b7|0)==1636){b7=0;if((qg|0)==0){if(!((qc|0)==2|(qc|0)==7)){qh=0;qi=0;qj=oq;break}}a[67101]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[qh+134320|0]|0)==(a[hS+(qh+qj|0)|0]|0)){qk=qj;ql=qi}else{if((qh|0)!=5){qm=0;qn=0;qo=oq;break}qk=qj-1|0;ql=1}gx=qh+1|0;if((gx|0)<(ql+gH|0)){qh=gx;qi=ql;qj=qk}else{b7=1643;break}}do{if((b7|0)==1643){b7=0;if((ql|0)==0){if(!((qh|0)==4|(qh|0)==9)){qm=0;qn=0;qo=oq;break}}a[67101]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){gx=qm+2|0;if((a[gx+134136|0]|0)==(a[hS+(qm+qo|0)|0]|0)){qp=qo;qq=qn}else{if((gx|0)!=3){qr=0;qs=0;qt=oq;break}qp=qo-1|0;qq=1}gx=qm+1|0;if((gx|0)<(qq+gH|0)){qm=gx;qn=qq;qo=qp}else{b7=1650;break}}do{if((b7|0)==1650){b7=0;if((qq|0)==0){if(!((qm|0)==0|(qm|0)==5)){qr=0;qs=0;qt=oq;break}}a[69852]=1;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[qr+134136|0]|0)==(a[hS+(qr+qt|0)|0]|0)){qu=qt;qv=qs}else{if((qr|0)!=3){qw=0;qx=0;qy=oq;break}qu=qt-1|0;qv=1}gx=qr+1|0;if((gx|0)<(qv+gH|0)){qr=gx;qs=qv;qt=qu}else{b7=1657;break}}do{if((b7|0)==1657){b7=0;if((qv|0)==0){if(!((qr|0)==2|(qr|0)==7)){qw=0;qx=0;qy=oq;break}}a[69852]=0;gx=hF+1|0;c[13898]=gx;oA=1;oB=gx;break L2028}}while(0);while(1){if((a[qw+133984|0]|0)==(a[hS+(qw+qy|0)|0]|0)){qz=qy;qA=qx}else{if((qw|0)!=2){qB=0;qC=0;qD=oq;break}qz=qy-1|0;qA=1}gx=qw+1|0;if((gx|0)<(qA+gH|0)){qw=gx;qx=qA;qy=qz}else{b7=1664;break}}do{if((b7|0)==1664){b7=0;if((qA|0)==0){if((qw|0)==1|(qw|0)==5){qE=0}else{qB=0;qC=0;qD=oq;break}}else{qE=0}while(1){if(qE>>>0>=8){b7=1669;break}if((a[65036+(qE*688&-1)|0]&1)!=0){break}if((a[65037+(qE*688&-1)|0]&1)==0){qE=qE+1|0}else{break}}if((b7|0)==1669){b7=0;a[69852]=1}h[3815]=.5235987755982988;gy=hF+1|0;c[13898]=gy;L2341:do{if((a[gz+(gy*40&-1)|0]&1)!=0){gx=c[10810]|0;if((gx|0)==0){oA=hG;oB=gy;break L2028}gA=(gG|0)>(gy|0);hp=gz+(gy*40&-1)+36|0;nd=gz+(gy*40&-1)+32|0;hC=gx;L2344:while(1){hR=c[hC+4>>2]|0;L2346:do{if(gA){hB=c[hp>>2]|0;ne=0;while(1){if((ne|0)>=(hB|0)){break}if((a[hS+((c[nd>>2]|0)+ne|0)|0]|0)==(a[hR+ne|0]|0)){ne=ne+1|0}else{break L2346}}if((a[hR+ne|0]|0)==0){b7=1678;break L2344}}}while(0);hR=c[hC>>2]|0;if((hR|0)==0){qF=gx;break}else{hC=hR}}do{if((b7|0)==1678){b7=0;if((a[hC+8|0]&1)!=0){qF=gx;break}if((c[hC+16>>2]|0)==1){break L2341}else{qF=gx}}}while(0);L2356:while(1){gx=c[qF+4>>2]|0;L2358:do{if(gA){hC=c[hp>>2]|0;hR=0;while(1){if((hR|0)>=(hC|0)){break}if((a[hS+((c[nd>>2]|0)+hR|0)|0]|0)==(a[gx+hR|0]|0)){hR=hR+1|0}else{break L2358}}if((a[gx+hR|0]|0)==0){break L2356}}}while(0);gx=c[qF>>2]|0;if((gx|0)==0){oA=hG;oB=gy;break L2028}else{qF=gx}}if((a[qF+8|0]&1)!=0){oA=hG;oB=gy;break L2028}if((c[qF+16>>2]|0)!=2){oA=hG;oB=gy;break L2028}}}while(0);m3=+h[9040];gy=is(bt)|0;nd=c[gy>>2]|0;if((nd|0)==1){qG=+(c[gy+8>>2]|0)}else if((nd|0)==2){qG=+h[gy+8>>3]}else if((nd|0)==3){qG=+uz(c[gy+8>>2]|0,0)}else{b7=1693;break L27}if((c[c$>>2]|0)==3){uu(c[c0>>2]|0);c[c$>>2]=1}h[3815]=m3*qG;oA=hG;oB=c[13898]|0;break L2028}}while(0);while(1){if((a[qB+133840|0]|0)==(a[hS+(qB+qD|0)|0]|0)){qH=qD;qI=qC}else{if((qB|0)!=4){op=oq;b7=1704;break L2028}qH=qD-1|0;qI=1}gy=qB+1|0;if((gy|0)<(qI+gH|0)){qB=gy;qC=qI;qD=qH}else{break}}if((qI|0)==0){if(!((qB|0)==3|(qB|0)==7)){op=oq;b7=1704;break}}h[3815]=0.0;hS=hF+1|0;c[13898]=hS;oA=hG;oB=hS}}while(0);L2386:do{if((b7|0)==1704){b7=0;L2388:do{if((gG|0)>(hF|0)){if(gE){break}gC=c[10036]|0;hS=0;while(1){if((hS|0)>=(gH|0)){b7=1709;break}if((a[gC+(hS+op|0)|0]|0)==(a[hS+150704|0]|0)){hS=hS+1|0}else{qJ=0;break}}do{if((b7|0)==1709){b7=0;if((hS|0)!=4){qJ=0;break}c[10026]=0;gy=hF+1|0;c[13898]=gy;oA=hG;oB=gy;break L2386}}while(0);while(1){if((qJ|0)>=(gH|0)){break}if((a[gC+(qJ+op|0)|0]|0)==(a[qJ+150856|0]|0)){qJ=qJ+1|0}else{b7=1715;break L2388}}if((qJ|0)!=5){b7=1715;break}c[10026]=1;gC=hF+1|0;c[13898]=gC;oA=hG;oB=gC;break L2386}else{b7=1715}}while(0);L2402:do{if((b7|0)==1715){b7=0;if(!((gH|0)>0&(gE^1))){break}gC=c[10036]|0;hS=0;gy=0;nd=op;while(1){if((a[hS+133704|0]|0)==(a[gC+(hS+nd|0)|0]|0)){qK=nd;qL=gy}else{if((hS|0)!=6){break L2402}qK=nd-1|0;qL=1}hp=hS+1|0;if((hp|0)<(qL+gH|0)){hS=hp;gy=qL;nd=qK}else{break}}if((qL|0)==0){if(!((hS|0)==5|(hS|0)==12)){break}}c[10026]=-1;nd=hF+1|0;c[13898]=nd;oA=hG;oB=nd;break L2386}}while(0);hH(40048,1,0);nd=c[13898]|0;L2416:do{if((c[8272]|0)>(nd|0)){gy=c[1054]|0;if((a[gy+(nd*40&-1)|0]&1)==0){b7=1731;break}gC=c[gy+(nd*40&-1)+36>>2]|0;hp=gy+(nd*40&-1)+32|0;gy=c[10036]|0;gA=0;while(1){if((gA|0)>=(gC|0)){break}if((a[gy+((c[hp>>2]|0)+gA|0)|0]|0)==(a[gA+148464|0]|0)){gA=gA+1|0}else{b7=1731;break L2416}}if((gA|0)!=1){b7=1731;break}c[13898]=nd+1;hH(34776,1,0);qM=c[13898]|0}else{b7=1731}}while(0);do{if((b7|0)==1731){b7=0;if((hF|0)==(nd|0)){qM=hF;break}c[8694]=c[10012];c[8695]=c[10013];c[8696]=c[10014];c[8697]=c[10015];c[8698]=c[10016];c[8699]=c[10017];c[8700]=c[10018];c[8701]=c[10019];c[8702]=c[10020];c[8703]=c[10021];c[8704]=c[10022];c[8705]=c[10023];c[8706]=c[10024];c[8707]=c[10025];qM=nd}}while(0);if((hF|0)==(qM|0)){or=hG;break L2026}else{oA=hG;oB=qM}}}while(0);gH=c[8272]|0;if((oB|0)>=(gH|0)){or=oA;break}hG=oA;hF=oB;gG=gH;gz=c[1054]|0}if(or){break}else{qN=0}}else{qN=0}while(1){if(qN>>>0>=8){break}if((a[65036+(qN*688&-1)|0]&1)!=0){break L44}if((a[65037+(qN*688&-1)|0]&1)==0){qN=qN+1|0}else{break L44}}if((a[30528]&1)==0){a[66412]=1;a[65724]=1;break}else{a[69852]=1;break}}else if((gD|0)==25){c[13898]=gv+1;gr();a[38984]=1}else if((gD|0)==26){b7=1742;break L27}else if((gD|0)==27){c[13898]=gv+1;gz=is(bs)|0;gG=c[gz>>2]|0;if((gG|0)==1){qO=+(c[gz+8>>2]|0)}else if((gG|0)==2){qO=+h[gz+8>>3]}else if((gG|0)==3){qO=+uz(c[gz+8>>2]|0,0)}else{b7=1747;break L27}if((c[c1>>2]|0)==3){uu(c[c2>>2]|0);c[c1>>2]=1}gz=~~qO;gG=(gz|0)>-1?gz:-gz|0;qP=c[13898]|0;do{if((qP|0)<(c[8272]|0)){gz=c[1054]|0;if((a[gz+(qP*40&-1)|0]&1)==0){b7=5168;break L27}hF=c[gz+(qP*40&-1)+36>>2]|0;hG=gz+(qP*40&-1)+32|0;gz=c[10036]|0;gH=0;while(1){if((gH|0)>=(hF|0)){b7=1755;break}if((a[gz+((c[hG>>2]|0)+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{qQ=0;break}}if((b7|0)==1755){b7=0;if((gH|0)==1){qR=gG;break}else{qQ=0}}while(1){if((qQ|0)>=(hF|0)){break}if((a[gz+((c[hG>>2]|0)+qQ|0)|0]|0)==(a[qQ+148464|0]|0)){qQ=qQ+1|0}else{b7=5169;break L27}}if((qQ|0)!=1){b7=5170;break L27}c[13898]=qP+1;hG=is(br)|0;gz=c[hG>>2]|0;if((gz|0)==1){qS=+(c[hG+8>>2]|0)}else if((gz|0)==2){qS=+h[hG+8>>3]}else if((gz|0)==3){qS=+uz(c[hG+8>>2]|0,0)}else{b7=1764;break L27}if((c[c3>>2]|0)==3){uu(c[c4>>2]|0);c[c3>>2]=1}hG=~~qS;qR=(hG|0)>-1?hG:-hG|0}else{qR=gG}}while(0);if((gG|0)<2|(qR|0)<2){b7=1769;break L27}hG=c[10814]|0;gz=c[10828]|0;c[10814]=0;c[10828]=0;iO(hG);iY(gz);c[9344]=gG;c[9342]=qR}else if((gD|0)==28){gz=gv+1|0;c[13898]=gz;a[36120]=1;hG=c[8272]|0;L2473:do{if((gz|0)<(hG|0)){hF=0;gH=0;gE=0;nd=0;qT=gz;hp=hG;gy=gw;while(1){gC=(a[gy+(qT*40&-1)|0]&1)==0;hS=c[gy+(qT*40&-1)+36>>2]|0;L2476:do{if(gC){qU=c[10036]|0;qV=gy+(qT*40&-1)+32|0}else{gx=gy+(qT*40&-1)+32|0;hC=c[10036]|0;ne=0;while(1){if((ne|0)>=(hS|0)){break}if((a[hC+((c[gx>>2]|0)+ne|0)|0]|0)==(a[ne+103664|0]|0)){ne=ne+1|0}else{qU=hC;qV=gx;break L2476}}if((ne|0)==1){qW=gE;qX=nd;break L2473}else{qU=hC;qV=gx}}}while(0);gA=c[qV>>2]|0;hB=(hS|0)>0;hw=23912;hK=184456;L2484:while(1){L2486:do{if(!gC){if(hB){ho=0;gF=0;nh=gA;while(1){ng=a[hK+ho|0]|0;if(ng<<24>>24==(a[qU+(ho+nh|0)|0]|0)){qY=nh;qZ=gF}else{if(ng<<24>>24!=36){break L2486}qY=nh-1|0;qZ=1}q_=ho+1|0;if((q_|0)<(qZ+hS|0)){ho=q_;gF=qZ;nh=qY}else{break}}if((qZ|0)==0){q$=q_}else{q0=hw;break L2484}}else{q$=0}nh=a[hK+q$|0]|0;if((nh<<24>>24|0)==36|(nh<<24>>24|0)==0){q0=hw;break L2484}}}while(0);gx=hw+8|0;hC=c[gx>>2]|0;if((hC|0)==0){q0=gx;break}else{hw=gx;hK=hC}}hK=c[q0+4>>2]|0;L2499:do{if((hK|0)==40){a[36120]=1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==41){a[36120]=0;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==39){uu(c[9329]|0);uD(36120,26456,1240);q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==1){if(nd){uh(qT,137664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9044]=0;q1=1;q2=gE;q3=gH;q4=hF}else if((hK|0)==2){if(nd){uh(qT,137664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9044]=2;q1=1;q2=gE;q3=gH;q4=hF}else if((hK|0)==3){if(gE){uh(qT,137552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9045]=0;q1=nd;q2=1;q3=gH;q4=hF}else if((hK|0)==4){if(gE){uh(qT,137552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9045]=2;q1=nd;q2=1;q3=gH;q4=hF}else if((hK|0)==5){if(!nd){c[9044]=1}if(!gE){c[9045]=1}hw=nd|gE;q1=hw;q2=hw;q3=gH;q4=hF}else if((hK|0)==6){if(hF){uh(qT,137256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9047]=0;q1=nd;q2=gE;q3=gH;q4=1}else if((hK|0)==7){if(hF){uh(qT,137256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9047]=1;q1=nd;q2=gE;q3=gH;q4=1}else if((hK|0)==8){if(!gH){b7=1817;break}uh(qT,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);b7=1817}else if((hK|0)==13){b7=1817}else if((hK|0)==9){if(!gH){b7=1824;break}uh(qT,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);b7=1824}else if((hK|0)==14){b7=1824}else if((hK|0)==11){if(gH){uh(qT,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9031]=0;q1=nd;q2=gE;q3=1;q4=hF}else if((hK|0)==12){if(gH){uh(qT,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9031]=1;q1=nd;q2=gE;q3=1;q4=hF}else if((hK|0)==15){if(gH){uh(qT,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9031]=2;c[9032]=0;q1=nd;q2=gE;q3=1;q4=hF}else if((hK|0)==16){if(gH){uh(qT,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9031]=2;c[9032]=1;q1=nd;q2=gE;q3=1;q4=hF}else if((hK|0)==17){if(gH){uh(qT,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9031]=2;c[9032]=2;q1=nd;q2=gE;q3=1;q4=hF}else if((hK|0)==18){if(gH){uh(qT,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[9031]=2;c[9032]=3;q1=nd;q2=gE;q3=1;q4=hF}else if((hK|0)==19){c[9046]=0;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==20){c[9046]=1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==21){a[36229]=1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==22){a[36229]=0;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==23){a[36230]=1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==24){a[36230]=0;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==25){a[36231]=1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==26){a[36231]=0;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==27){hw=qT+1|0;c[13898]=hw;c[9059]=-2;L2571:do{if((hw|0)<(hp|0)){L2573:do{if((a[gy+(hw*40&-1)|0]&1)!=0){hS=c[gy+(hw*40&-1)+36>>2]|0;gA=gy+(hw*40&-1)+32|0;hB=0;while(1){if((hB|0)>=(hS|0)){break}if((a[qU+((c[gA>>2]|0)+hB|0)|0]|0)==(a[hB+103664|0]|0)){hB=hB+1|0}else{break L2573}}if((hB|0)==1){q5=hw;break L2571}}}while(0);hH(36232,1,0);gA=c[13898]|0;if((hw|0)!=(gA|0)){q5=gA;break}if((a[(c[1054]|0)+(hw*40&-1)|0]&1)!=0){q5=hw;break}gA=is(bp)|0;hS=c[gA>>2]|0;if((hS|0)==1){q6=+(c[gA+8>>2]|0)}else if((hS|0)==2){q6=+h[gA+8>>3]}else if((hS|0)==3){q6=+uz(c[gA+8>>2]|0,0)}else{b7=1867;break L27}if((c[c6>>2]|0)==3){uu(c[c7>>2]|0);c[c6>>2]=1}c[9059]=~~q6-1;gA=(c[13898]|0)+1|0;c[13898]=gA;q5=gA}else{q5=hw}}while(0);c[13898]=q5-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==28){c[9059]=-3;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==29){c[13898]=qT+1;hw=is(bo)|0;gA=c[hw>>2]|0;if((gA|0)==1){q7=+(c[hw+8>>2]|0)}else if((gA|0)==2){q7=+h[hw+8>>3]}else if((gA|0)==3){q7=+uz(c[hw+8>>2]|0,0)}else{b7=1877;break L27}if((c[c8>>2]|0)==3){uu(c[c9>>2]|0);c[c8>>2]=1}h[4524]=q7;c[13898]=(c[13898]|0)-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==30){c[13898]=qT+1;hw=is(bn)|0;gA=c[hw>>2]|0;if((gA|0)==1){q8=+(c[hw+8>>2]|0)}else if((gA|0)==2){q8=+h[hw+8>>3]}else if((gA|0)==3){q8=+uz(c[hw+8>>2]|0,0)}else{b7=1885;break L27}if((c[da>>2]|0)==3){uu(c[dc>>2]|0);c[da>>2]=1}h[4525]=q8<0.0?0.0:q8;c[13898]=(c[13898]|0)-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==31){c[13898]=qT+1;hw=is(bm)|0;gA=c[hw>>2]|0;if((gA|0)==1){q9=+(c[hw+8>>2]|0)}else if((gA|0)==2){q9=+h[hw+8>>3]}else if((gA|0)==3){q9=+uz(c[hw+8>>2]|0,0)}else{b7=1893;break L27}if((c[dd>>2]|0)==3){uu(c[de>>2]|0);c[dd>>2]=1}h[4526]=q9;c[13898]=(c[13898]|0)-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==32){c[13898]=qT+1;hw=is(bl)|0;gA=c[hw>>2]|0;if((gA|0)==1){ra=+(c[hw+8>>2]|0)}else if((gA|0)==2){ra=+h[hw+8>>3]}else if((gA|0)==3){ra=+uz(c[hw+8>>2]|0,0)}else{b7=1901;break L27}if((c[df>>2]|0)==3){uu(c[dg>>2]|0);c[df>>2]=1}h[4527]=ra;c[13898]=(c[13898]|0)-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==37){hw=qT+1|0;c[13898]=hw;gA=c[gy+(hw*40&-1)+36>>2]|0;L2629:do{if((a[gy+(hw*40&-1)|0]&1)!=0&(gA|0)>0){hS=0;gC=0;hC=c[gy+(hw*40&-1)+32>>2]|0;while(1){if((a[hS+137208|0]|0)==(a[qU+(hS+hC|0)|0]|0)){rb=hC;rc=gC}else{if((hS|0)!=3){break L2629}rb=hC-1|0;rc=1}gx=hS+1|0;if((gx|0)<(rc+gA|0)){hS=gx;gC=rc;hC=rb}else{break}}if((rc|0)==0){if(!((hS|0)==2|(hS|0)==12)){break}}c[9056]=2;q1=nd;q2=gE;q3=gH;q4=hF;break L2499}}while(0);c[9056]=1;c[13898]=qT;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==38){c[9056]=0;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==33){gA=qT+1|0;c[13898]=gA;L2644:do{if((gA|0)<(hp|0)){L2646:do{if((a[gy+(gA*40&-1)|0]&1)!=0){hw=c[gy+(gA*40&-1)+36>>2]|0;hC=gy+(gA*40&-1)+32|0;gC=0;while(1){if((gC|0)>=(hw|0)){break}if((a[qU+((c[hC>>2]|0)+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{break L2646}}if((gC|0)==1){b7=1926;break L2644}}}while(0);a[14176]=1;is(bk);a[14176]=0;if((c[dh>>2]|0)!=3){c[13898]=gA;b7=1926;break}hS=c[di>>2]|0;if((hS|0)==0){b7=1926;break}uF(36288,hS|0,1025);uu(hS)}else{b7=1926}}while(0);if((b7|0)==1926){b7=0;a[36288]=0}c[13898]=(c[13898]|0)-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==34){a[36288]=0;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==35){rd=qT+1|0;c[13898]=rd;gA=(a[gy+(rd*40&-1)|0]&1)==0;if(gA){b7=1931}else{hS=a[qU+(c[gy+(rd*40&-1)+32>>2]|0)|0]|0;if(!((hS<<24>>24|0)==39|(hS<<24>>24|0)==34)){b7=1931}}if((b7|0)==1931){b7=0;hS=c[10810]|0;if((hS|0)==0){b7=5171;break L27}hC=gy+(rd*40&-1)+36|0;hw=gy+(rd*40&-1)+32|0;hB=(hp|0)<=(rd|0)|gA;gA=hS;L2667:while(1){hS=c[gA+4>>2]|0;L2669:do{if(!hB){gx=c[hC>>2]|0;ne=0;while(1){if((ne|0)>=(gx|0)){break}if((a[qU+((c[hw>>2]|0)+ne|0)|0]|0)==(a[hS+ne|0]|0)){ne=ne+1|0}else{break L2669}}if((a[hS+ne|0]|0)==0){break L2667}}}while(0);hS=c[gA>>2]|0;if((hS|0)==0){b7=5173;break L27}else{gA=hS}}if((a[gA+8|0]&1)!=0){b7=5172;break L27}if((c[gA+16>>2]|0)!=3){b7=5174;break L27}}uu(c[9329]|0);hw=c[13898]|0;L2679:do{if((hw|0)<(c[8272]|0)){hC=c[1054]|0;L2681:do{if((a[hC+(hw*40&-1)|0]&1)!=0){hB=c[hC+(hw*40&-1)+36>>2]|0;hS=hC+(hw*40&-1)+32|0;gx=c[10036]|0;gC=0;while(1){if((gC|0)>=(hB|0)){break}if((a[gx+((c[hS>>2]|0)+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{break L2681}}if((gC|0)==1){re=0;rf=hw;break L2679}}}while(0);a[14176]=1;is(bj);a[14176]=0;if((c[dj>>2]|0)==3){re=c[dk>>2]|0;rf=c[13898]|0;break}else{c[13898]=hw;re=0;rf=hw;break}}else{re=0;rf=hw}}while(0);c[9329]=re;c[13898]=rf-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==36){uE(c5|0,0,16);hI(bq,7);do{if((c[dm>>2]|0)==3){if(+h[dn>>3]!=-1.0){break}c[dm>>2]=7}}while(0);c[9330]=c[c5>>2];c[37324>>2]=c[c5+4>>2];c[37328>>2]=c[c5+8>>2];c[37332>>2]=c[c5+12>>2];c[13898]=(c[13898]|0)-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==42){hw=qT+1|0;c[13898]=hw;L2698:do{if((hw|0)<(hp|0)){L2700:do{if((a[gy+(hw*40&-1)|0]&1)!=0){gA=c[gy+(hw*40&-1)+36>>2]|0;hC=gy+(hw*40&-1)+32|0;hS=0;while(1){if((hS|0)>=(gA|0)){b7=1961;break}if((a[qU+((c[hC>>2]|0)+hS|0)|0]|0)==(a[hS+103664|0]|0)){hS=hS+1|0}else{break}}if((b7|0)==1961){b7=0;if((hS|0)==1){rg=0;rh=hw;break L2698}}if((gA|0)<=0){break}gC=0;gx=0;hB=c[hC>>2]|0;while(1){if((a[gC+137072|0]|0)==(a[qU+(gC+hB|0)|0]|0)){ri=hB;rj=gx}else{if((gC|0)!=1){break L2700}ri=hB-1|0;rj=1}ne=gC+1|0;if((ne|0)<(rj+gA|0)){gC=ne;gx=rj;hB=ri}else{break}}if((rj|0)!=0){rg=0;rh=hw;break L2698}if((gC|0)==0|(gC|0)==9){rg=0;rh=hw;break L2698}}}while(0);hB=is(bh)|0;gx=c[hB>>2]|0;if((gx|0)==1){rk=+(c[hB+8>>2]|0)}else if((gx|0)==2){rk=+h[hB+8>>3]}else if((gx|0)==3){rk=+uz(c[hB+8>>2]|0,0)}else{b7=1974;break L27}if((c[dp>>2]|0)==3){uu(c[dq>>2]|0);c[dp>>2]=1}rg=~~rk;rh=c[13898]|0}else{rg=0;rh=hw}}while(0);c[9338]=(rg|0)<0?0:rg;c[13898]=rh-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==43){hw=qT+1|0;c[13898]=hw;L2728:do{if((hw|0)<(hp|0)){L2730:do{if((a[gy+(hw*40&-1)|0]&1)!=0){hB=c[gy+(hw*40&-1)+36>>2]|0;gx=gy+(hw*40&-1)+32|0;gA=0;while(1){if((gA|0)>=(hB|0)){b7=1984;break}if((a[qU+((c[gx>>2]|0)+gA|0)|0]|0)==(a[gA+103664|0]|0)){gA=gA+1|0}else{break}}if((b7|0)==1984){b7=0;if((gA|0)==1){rl=0;rm=hw;break L2728}}if((hB|0)<=0){break}gC=0;hC=0;hS=c[gx>>2]|0;while(1){if((a[gC+137072|0]|0)==(a[qU+(gC+hS|0)|0]|0)){rn=hS;ro=hC}else{if((gC|0)!=1){break L2730}rn=hS-1|0;ro=1}ne=gC+1|0;if((ne|0)<(ro+hB|0)){gC=ne;hC=ro;hS=rn}else{break}}if((ro|0)!=0){rl=0;rm=hw;break L2728}if((gC|0)==0|(gC|0)==9){rl=0;rm=hw;break L2728}}}while(0);hS=is(bg)|0;hC=c[hS>>2]|0;if((hC|0)==1){rp=+(c[hS+8>>2]|0)}else if((hC|0)==2){rp=+h[hS+8>>3]}else if((hC|0)==3){rp=+uz(c[hS+8>>2]|0,0)}else{b7=1997;break L27}if((c[dr>>2]|0)==3){uu(c[ds>>2]|0);c[dr>>2]=1}rl=~~rp;rm=c[13898]|0}else{rl=0;rm=hw}}while(0);c[9339]=(rl|0)<0?0:rl;c[13898]=rm-1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==44){a[36228]=1;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==45){a[36228]=0;q1=nd;q2=gE;q3=gH;q4=hF}else if((hK|0)==10){hw=qT+1|0;c[13898]=hw;if(gH){uh(hw,137376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}dl(36136,0);c[9031]=3;c[13898]=(c[13898]|0)-1;q1=nd;q2=gE;q3=1;q4=hF}else{b7=2007;break L27}}while(0);if((b7|0)==1817){b7=0;if(!gE){c[9045]=1}if(!hF){c[9047]=1}c[9031]=2;c[9032]=0;q1=nd;q2=gE;q3=1;q4=hF}else if((b7|0)==1824){b7=0;if(!gE){c[9045]=1}if(!hF){c[9047]=1}c[9031]=2;c[9032]=1;q1=nd;q2=gE;q3=1;q4=hF}hK=(c[13898]|0)+1|0;c[13898]=hK;hw=c[8272]|0;if((hK|0)>=(hw|0)){qW=q2;qX=q1;break L2473}hF=q4;gH=q3;gE=q2;nd=q1;qT=hK;hp=hw;gy=c[1054]|0}}else{qW=0;qX=0}}while(0);hG=c[9031]|0;if((hG|0)==2){gz=c[9032]|0;if(qX&gz>>>0<2){uh(-1,136464,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);break}if(!qW){break}if((gz-2|0)>>>0>=2){break}uh(-1,136272,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);break}else if((hG|0)!=1){break}if((c[9047]|0)==0){hG=c[9045]|0;if((hG|0)==0){c[9032]=2;break}else if((hG|0)==1){if((c[9044]|0)==0){c[9032]=0;break}else{c[9032]=1;break}}else if((hG|0)==2){c[9032]=3;break}else{break}}else{hG=c[9044]|0;if((hG|0)==0){c[9032]=0;break}else if((hG|0)==1){if((c[9045]|0)==0){c[9032]=2;break}else{c[9032]=3;break}}else if((hG|0)==2){c[9032]=1;break}else{break}}}else if((gD|0)==29){hG=gv+1|0;c[13898]=hG;L2807:do{if((hG|0)<(c[8272]|0)){L2809:do{if((a[gw+(hG*40&-1)|0]&1)!=0){gz=c[gw+(hG*40&-1)+36>>2]|0;gG=gw+(hG*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(gz|0)){b7=2034;break}if((a[gB+((c[gG>>2]|0)+gy|0)|0]|0)==(a[gy+103664|0]|0)){gy=gy+1|0}else{rq=0;break}}if((b7|0)==2034){b7=0;if((gy|0)==1){break L2807}else{rq=0}}while(1){if((rq|0)>=(gz|0)){break}if((a[gB+((c[gG>>2]|0)+rq|0)|0]|0)==(a[rq+103664|0]|0)){rq=rq+1|0}else{break L2809}}if((rq|0)==1){break L44}}}while(0);a[14176]=1;is(bf);a[14176]=0;if((c[dt>>2]|0)!=3){c[13898]=hG;break L44}gG=c[du>>2]|0;if((gG|0)==0){break L44}uF(36288,gG|0,1025);uu(gG);break L44}}while(0);a[36288]=0}else if((gD|0)==31){jr(43280)}else if((gD|0)==32){hG=gv+1|0;L2828:do{if((c[8272]|0)>(hG|0)){if((a[gw+(hG*40&-1)|0]&1)==0){break}gG=c[gw+(hG*40&-1)+36>>2]|0;gz=gw+(hG*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(gG|0)){break}if((a[gB+((c[gz>>2]|0)+gy|0)|0]|0)==(a[gy+166752|0]|0)){gy=gy+1|0}else{break L2828}}if((gy|0)!=5){break}c[13898]=gv+2;gz=is(bd)|0;gG=c[gz>>2]|0;if((gG|0)==1){rr=+(c[gz+8>>2]|0)}else if((gG|0)==2){rr=+h[gz+8>>3]}else if((gG|0)==3){rr=+uz(c[gz+8>>2]|0,0)}else{b7=2054;break L27}if((c[dv>>2]|0)==3){uu(c[dw>>2]|0);c[dv>>2]=1}c[8798]=~~rr;break L44}}while(0);jr(43264)}else if((gD|0)==30){hG=gv+1|0;c[13898]=hG;L2846:do{if((hG|0)<(c[8272]|0)){gz=(a[gw+(hG*40&-1)|0]&1)==0;if(!gz){gG=c[gw+(hG*40&-1)+36>>2]|0;hp=gw+(hG*40&-1)+32|0;nd=0;while(1){if((nd|0)>=(gG|0)){b7=2064;break}if((a[gB+((c[hp>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{break}}if((b7|0)==2064){b7=0;if((nd|0)==1){b7=2154;break}}gG=a[gB+(c[hp>>2]|0)|0]|0;if((gG<<24>>24|0)==39|(gG<<24>>24|0)==34){b7=2154;break}}gG=c[10810]|0;L2857:do{if((gG|0)!=0){gy=gw+(hG*40&-1)+36|0;gE=gw+(hG*40&-1)+32|0;gH=gG;L2859:while(1){hF=c[gH+4>>2]|0;L2861:do{if(!gz){hw=c[gy>>2]|0;hK=0;while(1){if((hK|0)>=(hw|0)){break}if((a[gB+((c[gE>>2]|0)+hK|0)|0]|0)==(a[hF+hK|0]|0)){hK=hK+1|0}else{break L2861}}if((a[hF+hK|0]|0)==0){break L2859}}}while(0);hF=c[gH>>2]|0;if((hF|0)==0){break L2857}else{gH=hF}}if((a[gH+8|0]&1)!=0){break}if((c[gH+16>>2]|0)==3){b7=2154;break L2846}}}while(0);L2871:do{if(!gz){gG=c[gw+(hG*40&-1)+36>>2]|0;hp=gw+(hG*40&-1)+32|0;nd=0;while(1){if((nd|0)>=(gG|0)){b7=2080;break}if((a[gB+((c[hp>>2]|0)+nd|0)|0]|0)==(a[nd+95280|0]|0)){nd=nd+1|0}else{rs=0;break}}if((b7|0)==2080){b7=0;if((nd|0)==2){b7=2154;break L2846}else{rs=0}}while(1){if((rs|0)>=(gG|0)){b7=2083;break}if((a[gB+((c[hp>>2]|0)+rs|0)|0]|0)==(a[rs+130008|0]|0)){rs=rs+1|0}else{rt=0;break}}if((b7|0)==2083){b7=0;if((rs|0)==4){b7=2154;break L2846}else{rt=0}}while(1){if((rt|0)>=(gG|0)){b7=2086;break}if((a[gB+((c[hp>>2]|0)+rt|0)|0]|0)==(a[rt+89896|0]|0)){rt=rt+1|0}else{ru=0;break}}if((b7|0)==2086){b7=0;if((rt|0)==6){b7=2154;break L2846}else{ru=0}}while(1){if((ru|0)>=(gG|0)){b7=2089;break}if((a[gB+((c[hp>>2]|0)+ru|0)|0]|0)==(a[ru+139840|0]|0)){ru=ru+1|0}else{rv=0;break}}if((b7|0)==2089){b7=0;if((ru|0)==6){b7=2154;break L2846}else{rv=0}}while(1){if((rv|0)>=(gG|0)){b7=2092;break}if((a[gB+((c[hp>>2]|0)+rv|0)|0]|0)==(a[rv+129888|0]|0)){rv=rv+1|0}else{rw=0;break}}if((b7|0)==2092){b7=0;if((rv|0)==5){b7=2154;break L2846}else{rw=0}}while(1){if((rw|0)>=(gG|0)){b7=2095;break}if((a[gB+((c[hp>>2]|0)+rw|0)|0]|0)==(a[rw+150856|0]|0)){rw=rw+1|0}else{rx=0;break}}if((b7|0)==2095){b7=0;if((rw|0)==5){b7=2154;break L2846}else{rx=0}}while(1){if((rx|0)>=(gG|0)){b7=2098;break}if((a[gB+((c[hp>>2]|0)+rx|0)|0]|0)==(a[rx+150704|0]|0)){rx=rx+1|0}else{break}}if((b7|0)==2098){b7=0;if((rx|0)==4){b7=2154;break L2846}}nd=c[hp>>2]|0;gH=(gG|0)>0;L2909:do{if(gH){gE=0;gy=0;hF=nd;while(1){if((a[gE+90456|0]|0)==(a[gB+(gE+hF|0)|0]|0)){ry=hF;rz=gy}else{if((gE|0)!=3){rA=0;rB=0;rC=nd;break}ry=hF-1|0;rz=1}hw=gE+1|0;if((hw|0)<(rz+gG|0)){gE=hw;gy=rz;hF=ry}else{b7=2104;break}}if((b7|0)==2104){b7=0;if((rz|0)!=0){b7=2154;break L2846}if((gE|0)==2|(gE|0)==6){b7=2154;break L2846}else{rA=0;rB=0;rC=nd}}while(1){if((a[rA+149328|0]|0)==(a[gB+(rA+rC|0)|0]|0)){rD=rC;rE=rB}else{if((rA|0)!=5){rF=0;break L2909}rD=rC-1|0;rE=1}hF=rA+1|0;if((hF|0)<(rE+gG|0)){rA=hF;rB=rE;rC=rD}else{break}}if((rE|0)!=0){b7=2154;break L2846}if((rA|0)==4|(rA|0)==8){b7=2154;break L2846}else{rF=0}}else{rF=0}}while(0);while(1){if((rF|0)>=(gG|0)){b7=2114;break}if((a[gB+(rF+nd|0)|0]|0)==(a[rF+225056|0]|0)){rF=rF+1|0}else{break}}if((b7|0)==2114){b7=0;if((rF|0)==2){b7=2154;break L2846}}L2933:do{if(gH){hp=0;gE=0;hF=nd;while(1){if((a[hp+139232|0]|0)==(a[gB+(hp+hF|0)|0]|0)){rG=hF;rH=gE}else{if((hp|0)!=5){rI=0;break L2933}rG=hF-1|0;rH=1}gy=hp+1|0;if((gy|0)<(rH+gG|0)){hp=gy;gE=rH;hF=rG}else{break}}if((rH|0)!=0){b7=2154;break L2846}if((hp|0)==4|(hp|0)==8){b7=2154;break L2846}else{rI=0}}else{rI=0}}while(0);while(1){if((rI|0)>=(gG|0)){b7=2124;break}if((a[gB+(rI+nd|0)|0]|0)==(a[rI+205624|0]|0)){rI=rI+1|0}else{break}}if((b7|0)==2124){b7=0;if((rI|0)==2){b7=2154;break L2846}}L2948:do{if(gH){hF=0;gE=0;gy=nd;while(1){if((a[hF+139064|0]|0)==(a[gB+(hF+gy|0)|0]|0)){rJ=gy;rK=gE}else{if((hF|0)!=6){rL=0;break L2948}rJ=gy-1|0;rK=1}hw=hF+1|0;if((hw|0)<(rK+gG|0)){hF=hw;gE=rK;gy=rJ}else{break}}if((rK|0)!=0){b7=2154;break L2846}if((hF|0)==5|(hF|0)==9){b7=2154;break L2846}else{rL=0}}else{rL=0}}while(0);while(1){if((rL|0)>=(gG|0)){b7=2134;break}if((a[gB+(rL+nd|0)|0]|0)==(a[rL+143040|0]|0)){rL=rL+1|0}else{break}}if((b7|0)==2134){b7=0;if((rL|0)==2){b7=2154;break L2846}}L2963:do{if(gH){gy=0;gE=0;hp=nd;while(1){if((a[gy+142856|0]|0)==(a[gB+(gy+hp|0)|0]|0)){rM=hp;rN=gE}else{if((gy|0)!=4){rO=0;break L2963}rM=hp-1|0;rN=1}hw=gy+1|0;if((hw|0)<(rN+gG|0)){gy=hw;gE=rN;hp=rM}else{break}}if((rN|0)!=0){b7=2154;break L2846}if((gy|0)==3|(gy|0)==9){b7=2154;break L2846}else{rO=0}}else{rO=0}}while(0);while(1){if((rO|0)>=(gG|0)){break}if((a[gB+(rO+nd|0)|0]|0)==(a[rO+124352|0]|0)){rO=rO+1|0}else{break L2871}}if((rO|0)==4){b7=2154;break L2846}}}while(0);is(bb);gz=c[dy>>2]|0;if((gz|0)==3){c[13898]=hG;nd=c[10822]|0;L2979:do{if((nd|0)==0){rP=1}else{gG=1;gH=nd;while(1){if((c[gH+4>>2]|0)!=(gG|0)){rP=gG;break L2979}hp=gG+1|0;gE=c[gH>>2]|0;if((gE|0)==0){rP=hp;break}else{gG=hp;gH=gE}}}}while(0);uu(c[dA>>2]|0);c[dy>>2]=1;rQ=rP;b7=2157;break}else if((gz|0)==1){rR=+(c[dB>>2]|0)}else if((gz|0)==2){rR=+h[dz>>3]}else{b7=2152;break L27}rQ=~~rR;b7=2157}else{b7=2154}}while(0);L2987:do{if((b7|0)==2154){b7=0;hG=c[10822]|0;if((hG|0)==0){rS=1;rT=0;rU=0;b7=2163;break}else{rV=1;rW=hG}while(1){if((c[rW+4>>2]|0)!=(rV|0)){rQ=rV;b7=2157;break L2987}hG=rV+1|0;nd=c[rW>>2]|0;if((nd|0)==0){rQ=hG;b7=2157;break}else{rV=hG;rW=nd}}}}while(0);L2992:do{if((b7|0)==2157){b7=0;if((rQ|0)<1){b7=2158;break L27}gz=c[10822]|0;if((gz|0)==0){rS=rQ;rT=0;rU=0;b7=2163;break}else{rX=0;rY=gz}while(1){rZ=c[rY+4>>2]|0;if((rQ|0)<=(rZ|0)){break}gz=c[rY>>2]|0;if((gz|0)==0){rS=rQ;rT=rY;rU=0;b7=2163;break L2992}else{rX=rY;rY=gz}}if((rQ|0)==(rZ|0)){r_=rY}else{rS=rQ;rT=rX;rU=rY;b7=2163}}}while(0);if((b7|0)==2163){b7=0;uE(dx|0,0,28);gz=ut(192)|0;if((gz|0)==0){gk();nd=ut(192)|0;if((nd|0)==0){b7=2165;break L27}else{r$=nd}}else{r$=gz}gz=r$;nd=r$;c[nd>>2]=0;c[r$+4>>2]=rS;c[r$+72>>2]=0;c[r$+88>>2]=0;uE(r$+8|0,0,60);c[r$+96>>2]=1;hG=r$+156|0;a[r$+184|0]=0;c[r$+144>>2]=4;c[r$+148>>2]=4;c[r$+152>>2]=4;c[hG>>2]=c[dx>>2];c[hG+4>>2]=c[dx+4>>2];c[hG+8>>2]=c[dx+8>>2];c[hG+12>>2]=c[dx+12>>2];c[hG+16>>2]=c[dx+16>>2];c[hG+20>>2]=c[dx+20>>2];c[hG+24>>2]=c[dx+24>>2];if((rT|0)==0){c[10822]=gz}else{c[rT>>2]=gz}c[nd>>2]=rU;r_=gz}gz=c[13898]|0;L3009:do{if((gz|0)<(c[8272]|0)){nd=c[1054]|0;L3011:do{if((a[nd+(gz*40&-1)|0]&1)!=0){hG=c[nd+(gz*40&-1)+36>>2]|0;gH=nd+(gz*40&-1)+32|0;gG=c[10036]|0;gE=0;while(1){if((gE|0)>=(hG|0)){break}if((a[gG+((c[gH>>2]|0)+gE|0)|0]|0)==(a[gE+103664|0]|0)){gE=gE+1|0}else{break L3011}}if((gE|0)==1){break L3009}}}while(0);jw(r_);nd=c[13898]|0;L3018:do{if((nd|0)<(c[8272]|0)){gH=c[1054]|0;L3020:do{if((a[gH+(nd*40&-1)|0]&1)!=0){gG=c[gH+(nd*40&-1)+36>>2]|0;hG=gH+(nd*40&-1)+32|0;hp=c[10036]|0;hF=0;while(1){if((hF|0)>=(gG|0)){break}if((a[hp+((c[hG>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{break L3020}}if((hF|0)==1){break L3018}}}while(0);a[14176]=1;is(a9);a[14176]=0;if((c[dC>>2]|0)!=3){c[13898]=nd;break}gH=c[dD>>2]|0;if((gH|0)==0){break}gE=r_+60|0;uu(c[gE>>2]|0);c[gE>>2]=gH}}while(0);nd=c[13898]|0;if((nd|0)>=(c[8272]|0)){break}gH=c[1054]|0;if((a[gH+(nd*40&-1)|0]&1)==0){break}gE=c[gH+(nd*40&-1)+36>>2]|0;hG=gH+(nd*40&-1)+32|0;nd=c[10036]|0;gH=0;while(1){if((gH|0)>=(gE|0)){b7=2191;break}if((a[nd+((c[hG>>2]|0)+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{r0=0;break}}if((b7|0)==2191){b7=0;if((gH|0)==1){break}else{r0=0}}while(1){if((r0|0)>=(gE|0)){break}if((a[nd+((c[hG>>2]|0)+r0|0)|0]|0)==(a[r0+148464|0]|0)){r0=r0+1|0}else{break L3009}}if((r0|0)!=1){break}hG=r_+60|0;nd=c[hG>>2]|0;gE=(uA(nd|0)|0)+1|0;gH=ut(gE)|0;if((gH|0)==0){gk();hp=ut(gE)|0;if((hp|0)==0){b7=2198;break L27}else{r1=hp}}else{r1=gH}r2=nd;nd=0;r3=r1;gH=gE;hp=gE;gE=c[13898]|0;gG=c[1054]|0;L3047:while(1){gy=gE+1|0;c[13898]=gy;if((a[gG+(gy*40&-1)|0]&1)!=0){hw=a[(c[10036]|0)+(c[gG+(gy*40&-1)+32>>2]|0)|0]|0;if((hw<<24>>24|0)==39|(hw<<24>>24|0)==34){b7=2201;break L27}}hw=is(ba)|0;gy=c[hw>>2]|0;if((gy|0)==1){r4=+(c[hw+8>>2]|0)}else if((gy|0)==2){r4=+h[hw+8>>3]}else if((gy|0)==3){r4=+uz(c[hw+8>>2]|0,0)}else{b7=2206;break L27}if((c[dE>>2]|0)==3){uu(c[dF>>2]|0);c[dE>>2]=1}r5=(hp+30|0)+gH|0;r6=db(r3,r5,138416)|0;hw=r6+nd|0;ud(hw,r5-nd|0,r2,1.0,r4);r7=(uA(hw|0)|0)+nd|0;hw=c[13898]|0;gy=c[8272]|0;if((hw|0)>=(gy|0)){b7=2218;break}hS=c[1054]|0;if((a[hS+(hw*40&-1)|0]&1)==0){b7=2218;break}hC=c[hS+(hw*40&-1)+36>>2]|0;hB=hS+(hw*40&-1)+32|0;gx=c[10036]|0;gA=0;while(1){if((gA|0)>=(hC|0)){b7=2214;break}if((a[gx+((c[hB>>2]|0)+gA|0)|0]|0)==(a[gA+103664|0]|0)){gA=gA+1|0}else{r8=0;break}}if((b7|0)==2214){b7=0;if((gA|0)==1){b7=2218;break}else{r8=0}}while(1){if((r8|0)>=(hC|0)){break}if((a[gx+((c[hB>>2]|0)+r8|0)|0]|0)==(a[r8+148464|0]|0)){r8=r8+1|0}else{b7=2218;break L3047}}if((r8|0)!=1){b7=2218;break}hB=hw+1|0;c[13898]=hB;if((hB|0)>=(gy|0)){b7=5175;break L27}if((a[hS+(hB*40&-1)|0]&1)==0){b7=5176;break L27}hC=c[hS+(hB*40&-1)+36>>2]|0;gA=hS+(hB*40&-1)+32|0;ne=0;while(1){if((ne|0)>=(hC|0)){b7=2224;break}nh=c[gA>>2]|0;if((a[gx+(nh+ne|0)|0]|0)==(a[ne+103664|0]|0)){ne=ne+1|0}else{r9=nh;break}}if((b7|0)==2224){b7=0;if((ne|0)==1){b7=5177;break L27}r9=c[gA>>2]|0}gy=a[gx+r9|0]|0;if(!((gy<<24>>24|0)==39|(gy<<24>>24|0)==34)){b7=5178;break L27}if(hC>>>0<hp>>>0){sa=r2;sb=hp;sc=hB;sd=hS;se=hC}else{gy=db(r2,hC,138128)|0;hw=c[13898]|0;nh=c[1054]|0;sa=gy;sb=hC;sc=hw;sd=nh;se=c[nh+(hw*40&-1)+36>>2]|0}hw=se-2|0;nh=(hw|0)<(hC|0)?hw:hC-1|0;if((nh|0)>0){hw=c[sd+(sc*40&-1)+32>>2]|0;gy=0;while(1){gF=hw+1|0;ho=gy+1|0;a[sa+gy|0]=a[(c[10036]|0)+gF|0]|0;if((ho|0)==(nh|0)){sf=nh;break}else{hw=gF;gy=ho}}}else{sf=0}a[sa+sf|0]=0;if((a[(c[10036]|0)+(c[(c[1054]|0)+(sc*40&-1)+32>>2]|0)|0]|0)==34){ua(sa)}else{gy=sa;hw=sa;while(1){nh=a[hw]|0;if((nh<<24>>24|0)==39){hC=hw+1|0;hS=(a[hC]|0)==39?hC:hw;sg=hS;sh=a[hS]|0}else if((nh<<24>>24|0)==0){break}else{sg=hw;sh=nh}a[gy]=sh;gy=gy+1|0;hw=sg+1|0}a[gy]=0}hw=(c[13898]|0)+1|0;c[13898]=hw;if((hw|0)>=(c[8272]|0)){b7=2248;break}nh=c[1054]|0;if((a[nh+(hw*40&-1)|0]&1)==0){b7=2248;break}hS=c[nh+(hw*40&-1)+36>>2]|0;hC=nh+(hw*40&-1)+32|0;hB=c[10036]|0;gx=0;while(1){if((gx|0)>=(hS|0)){b7=2244;break}if((a[hB+((c[hC>>2]|0)+gx|0)|0]|0)==(a[gx+103664|0]|0)){gx=gx+1|0}else{si=0;break}}if((b7|0)==2244){b7=0;if((gx|0)==1){b7=2248;break}else{si=0}}while(1){if((si|0)>=(hS|0)){break}if((a[hB+((c[hC>>2]|0)+si|0)|0]|0)==(a[si+148464|0]|0)){si=si+1|0}else{b7=2248;break L3047}}if((si|0)==1){r2=sa;nd=r7;r3=r6;gH=r5;hp=sb;gE=hw;gG=nh}else{b7=2248;break}}if((b7|0)==2218){b7=0;uu(r2);sj=r6}else if((b7|0)==2248){b7=0;gG=db(r6,sb+r5|0,137840)|0;uB(gG+r7|0,sa|0);uu(sa);sj=gG}c[hG>>2]=sj}}while(0);jw(r_);}else if((gD|0)==33){gz=gv+1|0;c[13898]=gz;gG=c[8272]|0;L3116:do{if((gz|0)<(gG|0)){gE=a[gw+(gz*40&-1)|0]|0;L3118:do{if((gE&1)==0){sk=0;sl=gz;sm=gG;sn=gw;so=gE}else{hp=c[gw+(gz*40&-1)+36>>2]|0;gH=gw+(gz*40&-1)+32|0;nd=0;while(1){if((nd|0)>=(hp|0)){break}if((a[gB+((c[gH>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{sk=0;sl=gz;sm=gG;sn=gw;so=gE;break L3118}}if((nd|0)==1){break L3116}else{sk=0;sl=gz;sm=gG;sn=gw;so=gE}}}while(0);L3124:while(1){gE=(so&1)==0;L3126:do{if(!gE){hG=c[sn+(sl*40&-1)+36>>2]|0;gH=sn+(sl*40&-1)+32|0;hp=c[10036]|0;nh=0;while(1){if((nh|0)>=(hG|0)){break}if((a[hp+((c[gH>>2]|0)+nh|0)|0]|0)==(a[nh+103664|0]|0)){nh=nh+1|0}else{break L3126}}if((nh|0)==1){sp=sk;break L3124}}}while(0);if((sl|0)>=(sm|0)){b7=2271;break L27}L3134:do{if(!gE){nd=c[sn+(sl*40&-1)+36>>2]|0;gH=sn+(sl*40&-1)+32|0;hp=c[10036]|0;hG=0;while(1){if((hG|0)>=(nd|0)){break}if((a[hp+((c[gH>>2]|0)+hG|0)|0]|0)==(a[hG+103664|0]|0)){hG=hG+1|0}else{break L3134}}if((hG|0)==1){b7=2271;break L27}}}while(0);a[14176]=1;is(a7);a[14176]=0;if((c[dG>>2]|0)!=3){b7=2270;break L27}gE=c[dH>>2]|0;c[a8>>2]=gE;if((gE|0)==0){b7=2280;break L27}if((sk|0)==0){sq=0}else{sq=uA(sk|0)|0}iQ(a8);gE=c[a8>>2]|0;gH=db(sk,(sq+2|0)+(uA(gE|0)|0)|0,140360)|0;if((sq|0)==0){uB(gH|0,gE|0)}else{hp=gH+sq|0;nd=gH+(sq+1|0)|0;uB(nd|0,gE|0);a[hp]=58}uu(gE);gE=c[13898]|0;hp=c[8272]|0;if((gE|0)>=(hp|0)){sp=gH;break}nd=c[1054]|0;sk=gH;sl=gE;sm=hp;sn=nd;so=a[nd+(gE*40&-1)|0]|0}if((sp|0)==0){break L44}uq(4,sp);uu(sp);break L44}}while(0);uq(32,0)}else if((gD|0)==34){sr=gv+1|0;c[13898]=sr;L3155:do{if((sr|0)<(c[8272]|0)){L3157:do{if((a[gw+(sr*40&-1)|0]&1)!=0){gG=c[gw+(sr*40&-1)+36>>2]|0;gz=gw+(sr*40&-1)+32|0;gE=0;while(1){if((gE|0)>=(gG|0)){b7=2289;break}if((a[gB+((c[gz>>2]|0)+gE|0)|0]|0)==(a[gE+103664|0]|0)){gE=gE+1|0}else{ss=0;break}}if((b7|0)==2289){b7=0;if((gE|0)==1){break L3155}else{ss=0}}while(1){if((ss|0)>=(gG|0)){break}if((a[gB+((c[gz>>2]|0)+ss|0)|0]|0)==(a[ss+103664|0]|0)){ss=ss+1|0}else{break L3157}}if((ss|0)==1){st=sr;b7=5181;break L27}}}while(0);a[14176]=1;is(a6);a[14176]=0;if((c[dI>>2]|0)!=3){b7=2297;break L27}gz=c[dJ>>2]|0;if((gz|0)==0){b7=2299;break L27}us(4,gz);uu(gz);break L44}}while(0);uu(c[12908]|0);bA(5,179864);bA(2,179864);gz=bA(5,0)|0;if((gz|0)==0){su=0}else{su=bP(gz|0)|0}c[12908]=su}else if((gD|0)==35){uE(dK|0,0,11);gz=gv+1|0;c[13898]=gz;gG=c[8272]|0;L3176:do{if((gz|0)<(gG|0)){gE=c[gw+(gz*40&-1)+36>>2]|0;L3178:do{if((a[gw+(gz*40&-1)|0]&1)!=0){nd=gw+(gz*40&-1)+32|0;hp=0;while(1){if((hp|0)>=(gE|0)){break}if((a[gB+((c[nd>>2]|0)+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{break L3178}}if((hp|0)==1){b7=2308;break L3176}}}while(0);if((gE|0)>0){sv=gw+(gz*40&-1)+32|0;nd=c[sv>>2]|0;gH=0;do{sw=gH+nd|0;nh=gB+sw|0;hF=a[nh]|0;do{if(hF<<24>>24==114){sx=7}else{if((a_(nh|0,108208,2)|0)==0){sx=6;break}if((a_(nh|0,108120,2)|0)==0){sx=5;break}if((a_(nh|0,124768,2)|0)==0){sx=4;break}if((a_(nh|0,88416,2)|0)==0){sx=3;break}if(hF<<24>>24==120){sx=2;break}if(hF<<24>>24==121){sx=1;break}if(hF<<24>>24==122){sx=0}else{b7=2312;break L27}}}while(0);a[a5+(c[56724+(sx<<3)>>2]|0)|0]=1;gH=(uA(c[56720+(sx<<3)>>2]|0)|0)+gH|0;}while((gH|0)<(gE|0))}gE=gv+2|0;c[13898]=gE;if((gE|0)>=(gG|0)){sy=10.0;break}L3200:do{if((a[gw+(gE*40&-1)|0]&1)!=0){gH=c[gw+(gE*40&-1)+36>>2]|0;nd=gw+(gE*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(gH|0)){break}if((a[gB+((c[nd>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{break L3200}}if((hF|0)==1){sy=10.0;break L3176}}}while(0);gE=is(a4)|0;nd=c[gE>>2]|0;if((nd|0)==1){sz=+(c[gE+8>>2]|0)}else if((nd|0)==2){sz=+h[gE+8>>3]}else if((nd|0)==3){sz=+uz(c[gE+8>>2]|0,0)}else{b7=2324;break L27}if((c[dL>>2]|0)==3){uu(c[dM>>2]|0);c[dL>>2]=1}m3=+P(+sz);if(m3>1.0){sy=m3}else{b7=2328;break L27}}else{b7=2308}}while(0);if((b7|0)==2308){b7=0;uE(dK|0,1,7);sy=10.0}gG=0;L3218:do{do{if((a[a5+gG|0]&1)!=0){a[64788+(gG*688&-1)|0]=1;h[64792+(gG*688&-1)>>3]=sy;h[64800+(gG*688&-1)>>3]=+_(+sy);if((gG|0)!=7){break}if((a[30528]&1)==0){break L3218}ju()}}while(0);gG=gG+1|0;}while((gG|0)<8);c[6928]=0}else if((gD|0)==36){c[13898]=gv+1;a[43472]=1}else if((gD|0)==37){sA=gv+1|0;c[13898]=sA;L3228:do{if((sA|0)<(c[8272]|0)){if((a[gw+(sA*40&-1)|0]&1)==0){b7=5184;break L27}gG=c[gw+(sA*40&-1)+36>>2]|0;gz=gw+(sA*40&-1)+32|0;gE=0;while(1){if((gE|0)>=(gG|0)){b7=2349;break}nd=c[gz>>2]|0;if((a[gB+(nd+gE|0)|0]|0)==(a[gE+103664|0]|0)){gE=gE+1|0}else{sB=nd;break}}if((b7|0)==2349){b7=0;if((gE|0)==1){b7=2351;break}sB=c[gz>>2]|0}if((gG|0)>0){sC=0;sD=0;sE=sB}else{b7=5185;break L27}while(1){if((a[sC+141752|0]|0)==(a[gB+(sC+sE|0)|0]|0)){sF=sE;sG=sD}else{if((sC|0)!=2){sH=0;sI=0;sJ=sB;break}sF=sE-1|0;sG=1}nd=sC+1|0;if((nd|0)<(sG+gG|0)){sC=nd;sD=sG;sE=sF}else{b7=2357;break}}do{if((b7|0)==2357){b7=0;if((sG|0)==0){if(!((sC|0)==1|(sC|0)==9)){sH=0;sI=0;sJ=sB;break}}c[8732]=0;break L3228}}while(0);while(1){if((a[sH+141560|0]|0)==(a[gB+(sH+sJ|0)|0]|0)){sK=sJ;sL=sI}else{if((sH|0)!=1){sM=0;sN=0;sO=sB;break}sK=sJ-1|0;sL=1}gz=sH+1|0;if((gz|0)<(sL+gG|0)){sH=gz;sI=sL;sJ=sK}else{b7=2364;break}}do{if((b7|0)==2364){b7=0;if((sL|0)==0){if(!((sH|0)==0|(sH|0)==9)){sM=0;sN=0;sO=sB;break}}c[8732]=1;break L3228}}while(0);while(1){if((a[sM+141376|0]|0)==(a[gB+(sM+sO|0)|0]|0)){sP=sO;sQ=sN}else{if((sM|0)!=2){b7=5186;break L27}sP=sO-1|0;sQ=1}gz=sM+1|0;if((gz|0)<(sQ+gG|0)){sM=gz;sN=sQ;sO=sP}else{break}}if((sQ|0)==0){if(!((sM|0)==1|(sM|0)==11)){b7=5187;break L27}}c[8732]=2}else{b7=2351}}while(0);if((b7|0)==2351){b7=0;c[8732]=0}c[13898]=gv+2}else if((gD|0)==42){js(56400)}else if((gD|0)==39){js(35144)}else if((gD|0)==40){js(26392)}else if((gD|0)==41){js(4264)}else if((gD|0)==15){sR=gv+1|0;c[13898]=sR;gG=c[gw+(sR*40&-1)+36>>2]|0;gz=c[gw+(sR*40&-1)+32>>2]|0;gE=(a[gw+(sR*40&-1)|0]&1)==0;if((gG|0)>0&(gE^1)){sS=0;sT=0;sU=gz}else{b7=5195;break L27}while(1){if((a[sS+210832|0]|0)==(a[gB+(sS+sU|0)|0]|0)){sV=sU;sW=sT}else{if((sS|0)!=4){break}sV=sU-1|0;sW=1}nd=sS+1|0;if((nd|0)<(sW+gG|0)){sS=nd;sT=sW;sU=sV}else{b7=2385;break}}do{if((b7|0)==2385){b7=0;if((sW|0)==0){if(!((sS|0)==3|(sS|0)==7)){break}}sX=gv+2|0;c[13898]=sX;L3289:do{if((sX|0)<(c[8272]|0)){L3291:do{if((a[gw+(sX*40&-1)|0]&1)!=0){nd=c[gw+(sX*40&-1)+36>>2]|0;gH=gw+(sX*40&-1)+32|0;nh=0;while(1){if((nh|0)>=(nd|0)){b7=2392;break}if((a[gB+((c[gH>>2]|0)+nh|0)|0]|0)==(a[nh+103664|0]|0)){nh=nh+1|0}else{sY=0;break}}if((b7|0)==2392){b7=0;if((nh|0)==1){break L3289}else{sY=0}}while(1){if((sY|0)>=(nd|0)){break}if((a[gB+((c[gH>>2]|0)+sY|0)|0]|0)==(a[sY+103664|0]|0)){sY=sY+1|0}else{break L3291}}if((sY|0)==1){b7=2399;break L27}}}while(0);a[14176]=1;is(a3);a[14176]=0;if((c[dP>>2]|0)!=3){b7=2398;break L27}hF=c[dQ>>2]|0;c[8528]=hF;if((hF|0)==0){b7=2401;break L27}else{break L44}}}while(0);uu(c[8528]|0);c[8528]=0;break L44}}while(0);if(gE){b7=5196;break L27}L3307:do{if((gG|0)>0){hF=0;gH=0;nd=gz;while(1){if((a[hF+209008|0]|0)==(a[gB+(hF+nd|0)|0]|0)){sZ=nd;s_=gH}else{if((hF|0)!=3){break L3307}sZ=nd-1|0;s_=1}nh=hF+1|0;if((nh|0)<(s_+gG|0)){hF=nh;gH=s_;nd=sZ}else{break}}if((s_|0)==0){if(!((hF|0)==2|(hF|0)==9)){break}}s$=gv+2|0;c[13898]=s$;do{if((s$|0)<(c[8272]|0)){if((a[gw+(s$*40&-1)|0]&1)==0){b7=5190;break L27}nd=c[gw+(s$*40&-1)+36>>2]|0;gH=gw+(s$*40&-1)+32|0;nh=0;while(1){if((nh|0)>=(nd|0)){b7=2416;break}hG=c[gH>>2]|0;if((a[gB+(hG+nh|0)|0]|0)==(a[nh+103664|0]|0)){nh=nh+1|0}else{s0=hG;break}}if((b7|0)==2416){b7=0;if((nh|0)==1){break}s0=c[gH>>2]|0}L3328:do{if((nd|0)>0){hG=0;hp=0;hw=s0;while(1){if((a[hG+142688|0]|0)==(a[gB+(hG+hw|0)|0]|0)){s1=hw;s2=hp}else{if((hG|0)!=5){b7=2427;break L3328}s1=hw-1|0;s2=1}hC=hG+1|0;if((hC|0)<(s2+nd|0)){hG=hC;hp=s2;hw=s1}else{break}}if((s2|0)==0){if(!((hG|0)==4|(hG|0)==10)){b7=2427;break}}a[47120]=0}else{b7=2427}}while(0);L3339:do{if((b7|0)==2427){b7=0;gH=a[gB+s0|0]|0;if((gH<<24>>24|0)==39|(gH<<24>>24|0)==34){s3=0}else{b7=5191;break L27}while(1){if((s3|0)>=(nd|0)){b7=2431;break}if((a[gB+(s3+s0|0)|0]|0)==(a[s3+142432|0]|0)){s3=s3+1|0}else{s4=0;b7=2432;break}}if((b7|0)==2431){b7=0;if((s3|0)!=4){s4=0;b7=2432}}do{if((b7|0)==2432){while(1){b7=0;if((s4|0)>=(nd|0)){b7=2434;break}if((a[gB+(s4+s0|0)|0]|0)==(a[s4+142280|0]|0)){s4=s4+1|0;b7=2432}else{break}}if((b7|0)==2434){b7=0;if((s4|0)==4){break}}if(gH<<24>>24!=(a[gB+(s0+2|0)|0]|0)){b7=2437;break L27}a[47120]=a[gB+(s0+1|0)|0]|0;break L3339}}while(0);a[47120]=9}}while(0);c[13898]=gv+3;break L44}}while(0);a[47120]=0;break L44}}while(0);if((gG|0)>0&(gE^1)){s5=0;s6=0;s7=gz}else{b7=5197;break L27}while(1){if((a[s5+97920|0]|0)==(a[gB+(s5+s7|0)|0]|0)){s8=s7;s9=s6}else{if((s5|0)!=3){break}s8=s7-1|0;s9=1}hF=s5+1|0;if((hF|0)<(s9+gG|0)){s5=hF;s6=s9;s7=s8}else{b7=2445;break}}do{if((b7|0)==2445){b7=0;if((s9|0)==0){if(!((s5|0)==2|(s5|0)==13)){break}}ta=gv+2|0;c[13898]=ta;L3369:do{if((ta|0)<(c[8272]|0)){L3371:do{if((a[gw+(ta*40&-1)|0]&1)!=0){hF=c[gw+(ta*40&-1)+36>>2]|0;nd=gw+(ta*40&-1)+32|0;gH=0;while(1){if((gH|0)>=(hF|0)){b7=2452;break}if((a[gB+((c[nd>>2]|0)+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{tb=0;break}}if((b7|0)==2452){b7=0;if((gH|0)==1){b7=2453;break L3369}else{tb=0}}while(1){if((tb|0)>=(hF|0)){break}if((a[gB+((c[nd>>2]|0)+tb|0)|0]|0)==(a[tb+103664|0]|0)){tb=tb+1|0}else{break L3371}}if((tb|0)==1){tc=ta;b7=5192;break L27}}}while(0);a[14176]=1;is(a2);a[14176]=0;if((c[dN>>2]|0)!=3){b7=2458;break L27}nd=c[dO>>2]|0;if((nd|0)==0){b7=2460;break L27}uu(c[11948]|0);td=nd}else{b7=2453}}while(0);if((b7|0)==2453){b7=0;uu(c[11948]|0);td=bP(199184)|0}c[11948]=td;break L44}}while(0);if(gE){b7=5198;break L27}L3390:do{if((gG|0)>0){nd=0;hF=0;gH=gz;while(1){if((a[nd+204608|0]|0)==(a[gB+(nd+gH|0)|0]|0)){te=gH;tf=hF}else{if((nd|0)!=3){break L3390}te=gH-1|0;tf=1}hK=nd+1|0;if((hK|0)<(tf+gG|0)){nd=hK;hF=tf;gH=te}else{break}}if((tf|0)==0){if(!((nd|0)==2|(nd|0)==6)){break}}eP();break L44}}while(0);if((gG|0)>0&(gE^1)){tg=0;th=0;ti=gz}else{b7=5199;break L27}while(1){if((a[tg+115448|0]|0)==(a[gB+(tg+ti|0)|0]|0)){tj=ti;tk=th}else{if((tg|0)!=4){break}tj=ti-1|0;tk=1}gH=tg+1|0;if((gH|0)<(tk+gG|0)){tg=gH;th=tk;ti=tj}else{b7=2478;break}}do{if((b7|0)==2478){b7=0;if((tk|0)==0){if(!((tg|0)==3|(tg|0)==7)){break}}a[47712]=1;c[13898]=gv+2;break L44}}while(0);if(gE){b7=5200;break L27}L3415:do{if((gG|0)>0){gH=0;hF=0;hK=gz;while(1){if((a[gH+76296|0]|0)==(a[gB+(gH+hK|0)|0]|0)){tl=hK;tm=hF}else{if((gH|0)!=6){break L3415}tl=hK-1|0;tm=1}hG=gH+1|0;if((hG|0)<(tm+gG|0)){gH=hG;hF=tm;hK=tl}else{break}}if((tm|0)==0){if(!((gH|0)==5|(gH|0)==9)){break}}a[47712]=0;c[13898]=gv+2;break L44}}while(0);if((gG|0)>0&(gE^1)){tn=0}else{b7=5201;break L27}while(1){if((a[tn+223696|0]|0)!=(a[gB+(tn+gz|0)|0]|0)){break}to=tn+1|0;if((to|0)<(gG|0)){tn=to}else{b7=2493;break}}do{if((b7|0)==2493){b7=0;if((to|0)!=8){break}a[47472]=0;c[13898]=gv+2;break L44}}while(0);if((gG|0)>0&(gE^1)){tp=0}else{b7=5202;break L27}do{if((a[tp+216640|0]|0)!=(a[gB+(tp+gz|0)|0]|0)){b7=5203;break L27}tp=tp+1|0;}while((tp|0)<(gG|0));if((tp|0)!=10){b7=5204;break L27}a[47472]=1;c[13898]=gv+2}else if((gD|0)==44){gG=gv+1|0;c[13898]=gG;c[8496]=1;gz=c[8272]|0;if((gG|0)<(gz|0)){tq=gG;tr=gz;ts=gw}else{break}L3440:while(1){tt=c[ts+(tq*40&-1)+36>>2]|0;tu=ts+(tq*40&-1)+32|0;if((a[ts+(tq*40&-1)|0]&1)==0){b7=2503;break}gz=c[10036]|0;gG=0;while(1){if((gG|0)>=(tt|0)){b7=2507;break}gE=c[tu>>2]|0;if((a[gz+(gE+gG|0)|0]|0)==(a[gG+103664|0]|0)){gG=gG+1|0}else{tv=gE;break}}if((b7|0)==2507){b7=0;if((gG|0)==1){break L44}tv=c[tu>>2]|0}if((tt|0)>0){tw=0;tx=0;ty=tv}else{tz=tv;tA=0;break}while(1){if((a[tw+148208|0]|0)==(a[gz+(tw+ty|0)|0]|0)){tB=ty;tC=tx}else{if((tw|0)!=2){tD=0;tE=0;tF=tv;b7=2525;break}tB=ty-1|0;tC=1}gH=tw+1|0;if((gH|0)<(tC+tt|0)){tw=gH;tx=tC;ty=tB}else{b7=2514;break}}do{if((b7|0)==2514){b7=0;if((tC|0)==0){if(!((tw|0)==11|(tw|0)==1)){tD=0;tE=0;tF=tv;b7=2525;break}}c[13898]=tq+1;gG=is(a$)|0;gH=c[gG>>2]|0;if((gH|0)==1){tG=+(c[gG+8>>2]|0)}else if((gH|0)==2){tG=+h[gG+8>>3]}else if((gH|0)==3){tG=+uz(c[gG+8>>2]|0,0)}else{b7=2520;break L27}if((c[d5>>2]|0)==3){uu(c[d6>>2]|0);c[d5>>2]=1}gG=~~tG;c[8497]=gG;if((gG|0)>=0){break}c[8497]=0}}while(0);L3470:do{if((b7|0)==2525){while(1){b7=0;if((a[tD+148024|0]|0)==(a[gz+(tD+tF|0)|0]|0)){tH=tF;tI=tE}else{if((tD|0)!=4){tJ=0;tK=0;tL=tv;break}tH=tF-1|0;tI=1}gG=tD+1|0;if((gG|0)<(tI+tt|0)){tD=gG;tE=tI;tF=tH;b7=2525}else{b7=2529;break}}do{if((b7|0)==2529){b7=0;if((tI|0)==0){if(!((tD|0)==3|(tD|0)==13)){tJ=0;tK=0;tL=tv;break}}c[8497]=0;c[13898]=tq+1;break L3470}}while(0);while(1){if((a[tJ+147832|0]|0)==(a[gz+(tJ+tL|0)|0]|0)){tM=tL;tN=tK}else{if((tJ|0)!=6){tO=0;tP=0;tQ=tv;break}tM=tL-1|0;tN=1}gG=tJ+1|0;if((gG|0)<(tN+tt|0)){tJ=gG;tK=tN;tL=tM}else{b7=2538;break}}do{if((b7|0)==2538){b7=0;if((tN|0)==0){if(!((tJ|0)==5|(tJ|0)==15)){tO=0;tP=0;tQ=tv;break}}c[8498]=1;c[13898]=tq+1;break L3470}}while(0);while(1){if((a[tO+147576|0]|0)==(a[gz+(tO+tQ|0)|0]|0)){tR=tQ;tS=tP}else{if((tO|0)!=8){tT=0;tU=0;tV=tv;break}tR=tQ-1|0;tS=1}gG=tO+1|0;if((gG|0)<(tS+tt|0)){tO=gG;tP=tS;tQ=tR}else{b7=2545;break}}do{if((b7|0)==2545){b7=0;if((tS|0)==0){if(!((tO|0)==7|(tO|0)==17)){tT=0;tU=0;tV=tv;break}}c[8498]=0;c[13898]=tq+1;break L3470}}while(0);while(1){if((a[tT+147296|0]|0)==(a[gz+(tT+tV|0)|0]|0)){tW=tV;tX=tU}else{if((tT|0)!=2){tY=0;tZ=0;t_=tv;break}tW=tV-1|0;tX=1}gG=tT+1|0;if((gG|0)<(tX+tt|0)){tT=gG;tU=tX;tV=tW}else{b7=2552;break}}do{if((b7|0)==2552){b7=0;if((tX|0)==0){if(!((tT|0)==1|(tT|0)==16)){tY=0;tZ=0;t_=tv;break}}c[8500]=1;hM();c[13898]=(c[13898]|0)+1;break L3470}}while(0);while(1){if((a[tY+147040|0]|0)==(a[gz+(tY+t_|0)|0]|0)){t$=t_;t0=tZ}else{if((tY|0)!=14){t1=0;t2=0;t3=tv;break}t$=t_-1|0;t0=1}gG=tY+1|0;if((gG|0)<(t0+tt|0)){tY=gG;tZ=t0;t_=t$}else{b7=2559;break}}do{if((b7|0)==2559){b7=0;if((t0|0)==0){if(!((tY|0)==13|(tY|0)==16)){t1=0;t2=0;t3=tv;break}}c[8500]=2;hM();c[13898]=(c[13898]|0)+1;break L3470}}while(0);while(1){if((a[t1+146800|0]|0)==(a[gz+(t1+t3|0)|0]|0)){t4=t3;t6=t2}else{if((t1|0)!=4){t7=0;t8=0;t9=tv;break}t4=t3-1|0;t6=1}gG=t1+1|0;if((gG|0)<(t6+tt|0)){t1=gG;t2=t6;t3=t4}else{b7=2566;break}}do{if((b7|0)==2566){b7=0;if((t6|0)==0){if(!((t1|0)==3|(t1|0)==15)){t7=0;t8=0;t9=tv;break}}c[8500]=0;hM();c[13898]=(c[13898]|0)+1;break L3470}}while(0);while(1){if((a[t7+146616|0]|0)==(a[gz+(t7+t9|0)|0]|0)){ub=t9;uc=t8}else{if((t7|0)!=5){ue=0;ug=0;ui=tv;break}ub=t9-1|0;uc=1}gG=t7+1|0;if((gG|0)<(uc+tt|0)){t7=gG;t8=uc;t9=ub}else{b7=2573;break}}do{if((b7|0)==2573){b7=0;if((uc|0)==0){if(!((t7|0)==4|(t7|0)==6)){ue=0;ug=0;ui=tv;break}}c[8499]=1;gG=tq+1|0;c[13898]=gG;gH=(a[ts+(gG*40&-1)|0]&1)==0;if(gH){b7=2577}else{gE=a[gz+(c[ts+(gG*40&-1)+32>>2]|0)|0]|0;if(!((gE<<24>>24|0)==39|(gE<<24>>24|0)==34)){b7=2577}}if((b7|0)==2577){b7=0;gE=c[10810]|0;if((gE|0)==0){break L3470}hK=ts+(gG*40&-1)+36|0;hF=ts+(gG*40&-1)+32|0;nd=(tr|0)<=(gG|0)|gH;gH=gE;L3553:while(1){gE=c[gH+4>>2]|0;L3555:do{if(!nd){gG=c[hK>>2]|0;hG=0;while(1){if((hG|0)>=(gG|0)){break}if((a[gz+((c[hF>>2]|0)+hG|0)|0]|0)==(a[gE+hG|0]|0)){hG=hG+1|0}else{break L3555}}if((a[gE+hG|0]|0)==0){break L3553}}}while(0);gE=c[gH>>2]|0;if((gE|0)==0){break L3470}else{gH=gE}}if((a[gH+8|0]&1)!=0){break L3470}if((c[gH+16>>2]|0)!=3){break L3470}}uu(c[8504]|0);hF=c[13898]|0;L3565:do{if((hF|0)<(c[8272]|0)){hK=c[1054]|0;L3567:do{if((a[hK+(hF*40&-1)|0]&1)!=0){nd=c[hK+(hF*40&-1)+36>>2]|0;gE=hK+(hF*40&-1)+32|0;gG=c[10036]|0;gC=0;while(1){if((gC|0)>=(nd|0)){break}if((a[gG+((c[gE>>2]|0)+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{break L3567}}if((gC|0)==1){ul=0;break L3565}}}while(0);a[14176]=1;is(aV);a[14176]=0;if((c[d3>>2]|0)==3){ul=c[d4>>2]|0;break}else{c[13898]=hF;ul=0;break}}else{ul=0}}while(0);c[8504]=ul;break L3470}}while(0);while(1){if((a[ue+146440|0]|0)==(a[gz+(ue+ui|0)|0]|0)){um=ui;un=ug}else{if((ue|0)!=4){uo=0;up=0;uv=tv;break}um=ui-1|0;un=1}hF=ue+1|0;if((hF|0)<(un+tt|0)){ue=hF;ug=un;ui=um}else{b7=2601;break}}do{if((b7|0)==2601){b7=0;if((un|0)==0){if(!((ue|0)==3|(ue|0)==8)){uo=0;up=0;uv=tv;break}}c[8499]=0;c[13898]=tq+1;break L3470}}while(0);while(1){if((a[uo+146248|0]|0)==(a[gz+(uo+uv|0)|0]|0)){uw=uv;ux=up}else{if((uo|0)!=2){uy=0;uC=0;uG=tv;break}uw=uv-1|0;ux=1}hF=uo+1|0;if((hF|0)<(ux+tt|0)){uo=hF;up=ux;uv=uw}else{b7=2608;break}}do{if((b7|0)==2608){b7=0;if((ux|0)==0){if(!((uo|0)==1|(uo|0)==7)){uy=0;uC=0;uG=tv;break}}c[8501]=1;c[13898]=tq+1;break L3470}}while(0);while(1){if((a[uy+146080|0]|0)==(a[gz+(uy+uG|0)|0]|0)){uH=uG;uI=uC}else{if((uy|0)!=4){uJ=0;uK=0;uL=tv;break}uH=uG-1|0;uI=1}hF=uy+1|0;if((hF|0)<(uI+tt|0)){uy=hF;uC=uI;uG=uH}else{b7=2615;break}}do{if((b7|0)==2615){b7=0;if((uI|0)==0){if(!((uy|0)==3|(uy|0)==9)){uJ=0;uK=0;uL=tv;break}}c[8501]=0;c[13898]=tq+1;break L3470}}while(0);while(1){if((a[uJ+145928|0]|0)==(a[gz+(uJ+uL|0)|0]|0)){uM=uL;uN=uK}else{if((uJ|0)!=6){uO=0;uP=0;uQ=tv;break}uM=uL-1|0;uN=1}hF=uJ+1|0;if((hF|0)<(uN+tt|0)){uJ=hF;uK=uN;uL=uM}else{b7=2622;break}}do{if((b7|0)==2622){b7=0;if((uN|0)==0){if(!((uJ|0)==5|(uJ|0)==8)){uO=0;uP=0;uQ=tv;break}}c[8502]=1;c[13898]=tq+1;break L3470}}while(0);while(1){if((a[uO+145688|0]|0)==(a[gz+(uO+uQ|0)|0]|0)){uR=uQ;uS=uP}else{if((uO|0)!=8){uT=0;uU=0;uV=tv;break}uR=uQ-1|0;uS=1}hF=uO+1|0;if((hF|0)<(uS+tt|0)){uO=hF;uP=uS;uQ=uR}else{b7=2629;break}}do{if((b7|0)==2629){b7=0;if((uS|0)==0){if(!((uO|0)==7|(uO|0)==10)){uT=0;uU=0;uV=tv;break}}c[8502]=0;c[13898]=tq+1;break L3470}}while(0);while(1){if((a[uT+78872|0]|0)==(a[gz+(uT+uV|0)|0]|0)){uW=uV;uX=uU}else{if((uT|0)!=2){uY=0;uZ=0;u_=tv;break}uW=uV-1|0;uX=1}hF=uT+1|0;if((hF|0)<(uX+tt|0)){uT=hF;uU=uX;uV=uW}else{b7=2636;break}}do{if((b7|0)==2636){b7=0;if((uX|0)==0){if(!((uT|0)==6|(uT|0)==1)){uY=0;uZ=0;u_=tv;break}}hF=tq+1|0;c[13898]=hF;gH=(a[ts+(hF*40&-1)|0]&1)==0;if(gH){b7=2640}else{hK=a[gz+(c[ts+(hF*40&-1)+32>>2]|0)|0]|0;if(!((hK<<24>>24|0)==39|(hK<<24>>24|0)==34)){b7=2640}}L3646:do{if((b7|0)==2640){b7=0;hK=c[10810]|0;L3648:do{if((hK|0)!=0){gE=ts+(hF*40&-1)+36|0;gG=ts+(hF*40&-1)+32|0;nd=(tr|0)<=(hF|0)|gH;hG=hK;L3650:while(1){nh=c[hG+4>>2]|0;L3652:do{if(!nd){hw=c[gE>>2]|0;hp=0;while(1){if((hp|0)>=(hw|0)){break}if((a[gz+((c[gG>>2]|0)+hp|0)|0]|0)==(a[nh+hp|0]|0)){hp=hp+1|0}else{break L3652}}if((a[nh+hp|0]|0)==0){break L3650}}}while(0);nh=c[hG>>2]|0;if((nh|0)==0){break L3648}else{hG=nh}}if((a[hG+8|0]&1)!=0){break}if((c[hG+16>>2]|0)==3){break L3646}}}while(0);c[8503]=34032;break L3470}}while(0);gH=c[8503]|0;if((gH|0)==34032){u$=hF;u0=tr}else{uu(gH);u$=c[13898]|0;u0=c[8272]|0}L3666:do{if((u$|0)<(u0|0)){gH=c[1054]|0;L3668:do{if((a[gH+(u$*40&-1)|0]&1)!=0){hK=c[gH+(u$*40&-1)+36>>2]|0;gG=gH+(u$*40&-1)+32|0;gE=c[10036]|0;nd=0;while(1){if((nd|0)>=(hK|0)){break}if((a[gE+((c[gG>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{break L3668}}if((nd|0)==1){u1=0;break L3666}}}while(0);a[14176]=1;is(aR);a[14176]=0;if((c[d1>>2]|0)==3){u1=c[d2>>2]|0;break}else{c[13898]=u$;u1=0;break}}else{u1=0}}while(0);c[8503]=u1;break L3470}}while(0);while(1){if((a[uY+145232|0]|0)==(a[gz+(uY+u_|0)|0]|0)){u2=u_;u3=uZ}else{if((uY|0)!=2){u4=0;u5=0;u6=tv;break}u2=u_-1|0;u3=1}hF=uY+1|0;if((hF|0)<(u3+tt|0)){uY=hF;uZ=u3;u_=u2}else{b7=2667;break}}do{if((b7|0)==2667){b7=0;if((u3|0)==0){if(!((uY|0)==15|(uY|0)==1)){u4=0;u5=0;u6=tv;break}}hF=tq+1|0;c[13898]=hF;gH=(a[ts+(hF*40&-1)|0]&1)==0;if(gH){b7=2671}else{gG=a[gz+(c[ts+(hF*40&-1)+32>>2]|0)|0]|0;if(!((gG<<24>>24|0)==39|(gG<<24>>24|0)==34)){b7=2671}}L3692:do{if((b7|0)==2671){b7=0;gG=c[10810]|0;L3694:do{if((gG|0)!=0){gE=ts+(hF*40&-1)+36|0;hK=ts+(hF*40&-1)+32|0;hG=(tr|0)<=(hF|0)|gH;gC=gG;L3696:while(1){nh=c[gC+4>>2]|0;L3698:do{if(!hG){hR=c[gE>>2]|0;hw=0;while(1){if((hw|0)>=(hR|0)){break}if((a[gz+((c[hK>>2]|0)+hw|0)|0]|0)==(a[nh+hw|0]|0)){hw=hw+1|0}else{break L3698}}if((a[nh+hw|0]|0)==0){break L3696}}}while(0);nh=c[gC>>2]|0;if((nh|0)==0){break L3694}else{gC=nh}}if((a[gC+8|0]&1)!=0){break}if((c[gC+16>>2]|0)==3){break L3692}}}while(0);gG=is(aP)|0;hK=c[gG>>2]|0;if((hK|0)==1){u7=+(c[gG+8>>2]|0)}else if((hK|0)==2){u7=+h[gG+8>>3]}else if((hK|0)==3){u7=+uz(c[gG+8>>2]|0,0)}else{b7=2698;break L27}if((c[dZ>>2]|0)==3){uu(c[d_>>2]|0);c[dZ>>2]=1}gG=~~u7;if(gG>>>0<7){c[13532]=gG;break L3470}else{gG=c[m>>2]|0;cf(gG|0,144712,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=6,v)|0);break L3470}}}while(0);uu(c[13534]|0);gH=c[13898]|0;L3720:do{if((gH|0)<(c[8272]|0)){hF=c[1054]|0;L3722:do{if((a[hF+(gH*40&-1)|0]&1)!=0){gG=c[hF+(gH*40&-1)+36>>2]|0;hK=hF+(gH*40&-1)+32|0;gE=c[10036]|0;hG=0;while(1){if((hG|0)>=(gG|0)){break}if((a[gE+((c[hK>>2]|0)+hG|0)|0]|0)==(a[hG+103664|0]|0)){hG=hG+1|0}else{break L3722}}if((hG|0)==1){u8=0;break L3720}}}while(0);a[14176]=1;is(aO);a[14176]=0;if((c[d$>>2]|0)==3){u8=c[d0>>2]|0;break}else{c[13898]=gH;u8=0;break}}else{u8=0}}while(0);c[13534]=u8;if((a[u8]|0)!=0){c[13532]=7;break L3470}uu(u8);c[13534]=0;if((c[8506]|0)!=7){break L3470}c[8506]=0;break L3470}}while(0);while(1){if((a[u4+144456|0]|0)==(a[gz+(u4+u6|0)|0]|0)){u9=u6;va=u5}else{if((u4|0)!=2){vb=0;vc=0;vd=tv;break}u9=u6-1|0;va=1}gH=u4+1|0;if((gH|0)<(va+tt|0)){u4=gH;u5=va;u6=u9}else{b7=2708;break}}do{if((b7|0)==2708){b7=0;if((va|0)==0){if(!((u4|0)==11|(u4|0)==1)){vb=0;vc=0;vd=tv;break}}gH=tq+1|0;c[13898]=gH;hF=(a[ts+(gH*40&-1)|0]&1)==0;if(hF){b7=2712}else{hK=a[gz+(c[ts+(gH*40&-1)+32>>2]|0)|0]|0;if(!((hK<<24>>24|0)==39|(hK<<24>>24|0)==34)){b7=2712}}L3750:do{if((b7|0)==2712){b7=0;hK=c[10810]|0;L3752:do{if((hK|0)!=0){gE=ts+(gH*40&-1)+36|0;gG=ts+(gH*40&-1)+32|0;gC=(tr|0)<=(gH|0)|hF;nd=hK;L3754:while(1){nh=c[nd+4>>2]|0;L3756:do{if(!gC){hR=c[gE>>2]|0;hp=0;while(1){if((hp|0)>=(hR|0)){break}if((a[gz+((c[gG>>2]|0)+hp|0)|0]|0)==(a[nh+hp|0]|0)){hp=hp+1|0}else{break L3756}}if((a[nh+hp|0]|0)==0){break L3754}}}while(0);nh=c[nd>>2]|0;if((nh|0)==0){break L3752}else{nd=nh}}if((a[nd+8|0]&1)!=0){break}if((c[nd+16>>2]|0)==3){break L3750}}}while(0);hK=is(aT)|0;gG=c[hK>>2]|0;if((gG|0)==1){ve=+(c[hK+8>>2]|0)}else if((gG|0)==2){ve=+h[hK+8>>3]}else if((gG|0)==3){ve=+uz(c[hK+8>>2]|0,0)}else{b7=2740;break L27}if((c[dV>>2]|0)==3){uu(c[dW>>2]|0);c[dV>>2]=1}hK=~~ve;if(hK>>>0>=8){gG=c[m>>2]|0;cf(gG|0,144072,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=7,v)|0);break L3470}if((hK|0)!=7|(c[8510]|0)!=0){c[8506]=hK;break L3470}else{hK=c[m>>2]|0;aI(144280,44,1,hK|0);break L3470}}}while(0);uu(c[8510]|0);hF=c[13898]|0;L3781:do{if((hF|0)<(c[8272]|0)){gH=c[1054]|0;L3783:do{if((a[gH+(hF*40&-1)|0]&1)!=0){hK=c[gH+(hF*40&-1)+36>>2]|0;gG=gH+(hF*40&-1)+32|0;gE=c[10036]|0;gC=0;while(1){if((gC|0)>=(hK|0)){break}if((a[gE+((c[gG>>2]|0)+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{break L3783}}if((gC|0)==1){vf=0;break L3781}}}while(0);a[14176]=1;is(aS);a[14176]=0;if((c[dX>>2]|0)==3){vf=c[dY>>2]|0;break}else{c[13898]=hF;vf=0;break}}else{vf=0}}while(0);c[8510]=vf;do{if((a[vf]|0)==0){uu(vf);c[8510]=0;if((c[8506]|0)!=7){break}c[8506]=0}else{c[8506]=7}}while(0);c[13898]=(c[13898]|0)+1;break L3470}}while(0);while(1){if((a[vb+143960|0]|0)==(a[gz+(vb+vd|0)|0]|0)){vg=vd;vh=vc}else{if((vb|0)!=4){vi=0;vj=0;vk=tv;break}vg=vd-1|0;vh=1}hF=vb+1|0;if((hF|0)<(vh+tt|0)){vb=hF;vc=vh;vd=vg}else{b7=2752;break}}do{if((b7|0)==2752){b7=0;if((vh|0)==0){if(!((vb|0)==7|(vb|0)==3)){vi=0;vj=0;vk=tv;break}}c[13898]=tq+1;if((a[26192]&1)==0){break L3470}hX(aU);break L3470}}while(0);while(1){if((a[vi+143888|0]|0)==(a[gz+(vi+vk|0)|0]|0)){vl=vk;vm=vj}else{if((vi|0)!=2){tz=tv;tA=0;break L3440}vl=vk-1|0;vm=1}hF=vi+1|0;if((hF|0)<(vm+tt|0)){vi=hF;vj=vm;vk=vl}else{break}}if((vm|0)==0){if(!((vi|0)==5|(vi|0)==1)){tz=tv;tA=0;break L3440}}hF=tq+1|0;c[13898]=hF;L3821:do{if((hF|0)<(tr|0)){if((a[ts+(hF*40&-1)|0]&1)==0){break}gH=c[ts+(hF*40&-1)+36>>2]|0;gG=ts+(hF*40&-1)+32|0;gE=0;while(1){if((gE|0)>=(gH|0)){b7=2767;break}if((a[gz+((c[gG>>2]|0)+gE|0)|0]|0)==(a[gE+103664|0]|0)){gE=gE+1|0}else{vn=0;break}}if((b7|0)==2767){b7=0;if((gE|0)==1){break}else{vn=0}}while(1){if((vn|0)>=(gH|0)){break}if((a[gz+((c[gG>>2]|0)+vn|0)|0]|0)==(a[vn+95280|0]|0)){vn=vn+1|0}else{break L3821}}if((vn|0)!=2){break}vo=tq+2|0;c[13898]=vo;if((vo|0)>=(tr|0)){b7=5205;break L27}L3835:do{if((a[ts+(vo*40&-1)|0]&1)!=0){gG=c[ts+(vo*40&-1)+36>>2]|0;gH=ts+(vo*40&-1)+32|0;gE=0;while(1){if((gE|0)>=(gG|0)){break}if((a[gz+((c[gH>>2]|0)+gE|0)|0]|0)==(a[gE+103664|0]|0)){gE=gE+1|0}else{break L3835}}if((gE|0)==1){b7=5206;break L27}}}while(0);dl(a1,0);fR(a1,aX,aZ,143600);gH=~~+h[aX>>3];gG=~~+h[aZ>>3];hK=gG|gH;if((a[26192]&1)!=0){if((hK|0)<0){break L3470}hX(a0)}if((hK|0)>-1){c[dR>>2]=gH;vp=gG}else{c[dR>>2]=c[6558];vp=c[6559]|0}c[dS>>2]=vp;hX(a0);break L3470}}while(0);if((a[26192]&1)!=0){break}c[dT>>2]=c[6558];c[dU>>2]=c[6559];hX(aW)}}while(0);gz=c[13898]|0;hF=c[8272]|0;if((gz|0)>=(hF|0)){break L44}tq=gz;tr=hF;ts=c[1054]|0}if((b7|0)==2503){b7=0;tz=c[tu>>2]|0;tA=1}if((tq|0)>=(tr|0)){break}if(tA){b7=5207;break L27}hF=c[10036]|0;gz=0;while(1){if((gz|0)>=(tt|0)){break}if((a[hF+(gz+tz|0)|0]|0)==(a[gz+103664|0]|0)){gz=gz+1|0}else{b7=5208;break L27}}if((gz|0)!=1){b7=5209;break L27}}else if((gD|0)==45){ll()}else if((gD|0)==56){hF=gv+1|0;c[13898]=hF;L3865:do{if((hF|0)<(c[8272]|0)){gG=gw+(hF*40&-1)|0;gH=gw+(hF*40&-1)+36|0;L3867:do{if((a[gG]&1)==0){vq=gw+(hF*40&-1)+32|0}else{hK=c[gH>>2]|0;nd=gw+(hF*40&-1)+32|0;hG=0;while(1){if((hG|0)>=(hK|0)){break}if((a[gB+((c[nd>>2]|0)+hG|0)|0]|0)==(a[hG+103664|0]|0)){hG=hG+1|0}else{vq=nd;break L3867}}if((hG|0)==1){break L3865}else{vq=nd}}}while(0);c[8764]=0;hK=c[gH>>2]|0;L3875:do{if((a[gG]&1)!=0&(hK|0)>0){nh=0;hR=0;hw=c[vq>>2]|0;while(1){if((a[nh+148408|0]|0)==(a[gB+(nh+hw|0)|0]|0)){vr=hw;vs=hR}else{if((nh|0)!=2){break L3875}vr=hw-1|0;vs=1}hC=nh+1|0;if((hC|0)<(vs+hK|0)){nh=hC;hR=vs;hw=vr}else{break}}if((vs|0)==0){if(!((nh|0)==1|(nh|0)==5)){break}}c[8764]=2;c[13898]=gv+2}}while(0);hK=is(aM)|0;gG=c[hK>>2]|0;if((gG|0)==1){vt=+(c[hK+8>>2]|0)}else if((gG|0)==2){vt=+h[hK+8>>3]}else if((gG|0)==3){vt=+uz(c[hK+8>>2]|0,0)}else{b7=2816;break L27}if((c[d7>>2]|0)==3){uu(c[d8>>2]|0);c[d7>>2]=1}h[4384]=vt;hK=c[13898]|0;if((c[8272]|0)<=(hK|0)){break L44}gG=c[1054]|0;if((a[gG+(hK*40&-1)|0]&1)==0){break L44}gH=c[gG+(hK*40&-1)+36>>2]|0;hw=gG+(hK*40&-1)+32|0;hR=c[10036]|0;nd=0;while(1){if((nd|0)>=(gH|0)){break}if((a[hR+((c[hw>>2]|0)+nd|0)|0]|0)==(a[nd+148464|0]|0)){nd=nd+1|0}else{break L44}}if((nd|0)!=1){break L44}c[6588]=0;hw=hK+1|0;c[13898]=hw;gH=c[gG+(hw*40&-1)+36>>2]|0;L3903:do{if((a[gG+(hw*40&-1)|0]&1)!=0&(gH|0)>0){hG=0;hC=0;hB=c[gG+(hw*40&-1)+32>>2]|0;while(1){if((a[hG+148408|0]|0)==(a[hR+(hG+hB|0)|0]|0)){vu=hB;vv=hC}else{if((hG|0)!=2){break L3903}vu=hB-1|0;vv=1}hS=hG+1|0;if((hS|0)<(vv+gH|0)){hG=hS;hC=vv;hB=vu}else{break}}if((vv|0)==0){if(!((hG|0)==1|(hG|0)==5)){break}}c[6588]=2;c[13898]=hK+2}}while(0);hK=is(aL)|0;gH=c[hK>>2]|0;if((gH|0)==1){vw=+(c[hK+8>>2]|0)}else if((gH|0)==2){vw=+h[hK+8>>3]}else if((gH|0)==3){vw=+uz(c[hK+8>>2]|0,0)}else{b7=2838;break L27}if((c[d9>>2]|0)==3){uu(c[eb>>2]|0);c[d9>>2]=1}h[3296]=vw;hK=c[13898]|0;if((c[8272]|0)<=(hK|0)){break L44}gH=c[1054]|0;if((a[gH+(hK*40&-1)|0]&1)==0){break L44}hR=c[gH+(hK*40&-1)+36>>2]|0;hw=gH+(hK*40&-1)+32|0;gG=c[10036]|0;nd=0;while(1){if((nd|0)>=(hR|0)){break}if((a[gG+((c[hw>>2]|0)+nd|0)|0]|0)==(a[nd+148464|0]|0)){nd=nd+1|0}else{break L44}}if((nd|0)!=1){break L44}c[1057]=0;hw=hK+1|0;c[13898]=hw;hR=c[gH+(hw*40&-1)+36>>2]|0;L3931:do{if((a[gH+(hw*40&-1)|0]&1)!=0&(hR|0)>0){hB=0;hC=0;nh=c[gH+(hw*40&-1)+32>>2]|0;while(1){if((a[hB+148408|0]|0)==(a[gG+(hB+nh|0)|0]|0)){vx=nh;vy=hC}else{if((hB|0)!=2){break L3931}vx=nh-1|0;vy=1}hS=hB+1|0;if((hS|0)<(vy+hR|0)){hB=hS;hC=vy;nh=vx}else{break}}if((vy|0)==0){if(!((hB|0)==1|(hB|0)==5)){break}}c[1057]=2;c[13898]=hK+2}}while(0);hK=is(aK)|0;hR=c[hK>>2]|0;if((hR|0)==1){vz=+(c[hK+8>>2]|0)}else if((hR|0)==2){vz=+h[hK+8>>3]}else if((hR|0)==3){vz=+uz(c[hK+8>>2]|0,0)}else{b7=2860;break L27}if((c[ec>>2]|0)==3){uu(c[ed>>2]|0);c[ec>>2]=1}h[531]=vz;hK=c[13898]|0;if((c[8272]|0)<=(hK|0)){break L44}hR=c[1054]|0;if((a[hR+(hK*40&-1)|0]&1)==0){break L44}gG=c[hR+(hK*40&-1)+36>>2]|0;hw=hR+(hK*40&-1)+32|0;gH=c[10036]|0;nd=0;while(1){if((nd|0)>=(gG|0)){break}if((a[gH+((c[hw>>2]|0)+nd|0)|0]|0)==(a[nd+148464|0]|0)){nd=nd+1|0}else{break L44}}if((nd|0)!=1){break L44}c[14091]=0;hw=hK+1|0;c[13898]=hw;gG=c[hR+(hw*40&-1)+36>>2]|0;L3959:do{if((a[hR+(hw*40&-1)|0]&1)!=0&(gG|0)>0){nh=0;hC=0;hG=c[hR+(hw*40&-1)+32>>2]|0;while(1){if((a[nh+148408|0]|0)==(a[gH+(nh+hG|0)|0]|0)){vA=hG;vB=hC}else{if((nh|0)!=2){break L3959}vA=hG-1|0;vB=1}hS=nh+1|0;if((hS|0)<(vB+gG|0)){nh=hS;hC=vB;hG=vA}else{break}}if((vB|0)==0){if(!((nh|0)==1|(nh|0)==5)){break}}c[14091]=2;c[13898]=hK+2}}while(0);hK=is(aN)|0;gG=c[hK>>2]|0;if((gG|0)==1){vC=+(c[hK+8>>2]|0)}else if((gG|0)==2){vC=+h[hK+8>>3]}else if((gG|0)==3){vC=+uz(c[hK+8>>2]|0,0)}else{b7=2882;break L27}if((c[ee>>2]|0)==3){uu(c[ef>>2]|0);c[ee>>2]=1}h[7048]=vC;break L44}}while(0);h[7048]=0.0;h[531]=0.0;h[3296]=0.0;h[4384]=0.0}else if((gD|0)==57){hF=gv+1|0;c[13898]=hF;L3982:do{if((hF|0)<(c[8272]|0)){L3984:do{if((a[gw+(hF*40&-1)|0]&1)!=0){gz=c[gw+(hF*40&-1)+36>>2]|0;hK=gw+(hF*40&-1)+32|0;gG=0;while(1){if((gG|0)>=(gz|0)){break}if((a[gB+((c[hK>>2]|0)+gG|0)|0]|0)==(a[gG+103664|0]|0)){gG=gG+1|0}else{break L3984}}if((gG|0)==1){b7=2892;break L3982}}}while(0);hK=is(aH)|0;gz=c[hK>>2]|0;if((gz|0)==1){vD=+(c[hK+8>>2]|0)}else if((gz|0)==2){vD=+h[hK+8>>3]}else if((gz|0)==3){vD=+uz(c[hK+8>>2]|0,0)}else{b7=2897;break L27}if((c[eg>>2]|0)==3){uu(c[eh>>2]|0);c[eg>>2]=1}g[184]=vD;vE=c[13898]|0;if((c[8272]|0)<=(vE|0)){b7=5210;break L27}hK=c[1054]|0;if((a[hK+(vE*40&-1)|0]&1)==0){b7=5211;break L27}gz=c[hK+(vE*40&-1)+36>>2]|0;nh=hK+(vE*40&-1)+32|0;hK=c[10036]|0;gH=0;while(1){if((gH|0)>=(gz|0)){break}if((a[hK+((c[nh>>2]|0)+gH|0)|0]|0)==(a[gH+148464|0]|0)){gH=gH+1|0}else{b7=5212;break L27}}if((gH|0)!=1){b7=5213;break L27}c[13898]=vE+1;nh=is(aJ)|0;hK=c[nh>>2]|0;if((hK|0)==1){vF=+(c[nh+8>>2]|0)}else if((hK|0)==2){vF=+h[nh+8>>3]}else if((hK|0)==3){vF=+uz(c[nh+8>>2]|0,0)}else{b7=2911;break L27}if((c[ei>>2]|0)==3){uu(c[ej>>2]|0);c[ei>>2]=1}vG=vF}else{b7=2892}}while(0);if((b7|0)==2892){b7=0;g[184]=0.0;vG=0.0}g[44]=vG}else if((gD|0)==58){vH=gv+1|0;c[13898]=vH;if((a[33512]&1)!=0){b7=2917;break L27}L4019:do{if((vH|0)<(c[8272]|0)){L4021:do{if((a[gw+(vH*40&-1)|0]&1)!=0){hF=c[gw+(vH*40&-1)+36>>2]|0;nh=gw+(vH*40&-1)+32|0;hK=0;while(1){if((hK|0)>=(hF|0)){b7=2923;break}if((a[gB+((c[nh>>2]|0)+hK|0)|0]|0)==(a[hK+103664|0]|0)){hK=hK+1|0}else{vI=0;break}}if((b7|0)==2923){b7=0;if((hK|0)==1){b7=2924;break L4019}else{vI=0}}while(1){if((vI|0)>=(hF|0)){break}if((a[gB+((c[nh>>2]|0)+vI|0)|0]|0)==(a[vI+103664|0]|0)){vI=vI+1|0}else{break L4021}}if((vI|0)==1){b7=2931;break L27}}}while(0);a[14176]=1;is(aE);a[14176]=0;if((c[ek>>2]|0)!=3){b7=2930;break L27}gH=c[el>>2]|0;c[aF>>2]=gH;if((gH|0)==0){b7=2933;break L27}iQ(aF);gH=c[aF>>2]|0;lj(gH);nh=c[8244]|0;if((gH|0)==(nh|0)){break}if((gH|0)==0){vJ=nh}else{uu(gH);vJ=c[8244]|0}c[aF>>2]=vJ}else{b7=2924}}while(0);do{if((b7|0)==2924){b7=0;lj(0);gH=c[8244]|0;if((gH|0)==0){break}uu(gH);c[8244]=0}}while(0);c[7271]=-1}else if((gD|0)==59){c[13898]=gv+1;if((a[32936]&1)!=0){break}a[32936]=1;if((a[30528]&1)!=0){break}b[12272]=116;w=121;a[24595]=w&255;w=w>>8;a[24596|0]=w&255;if((a[37400]&1)==0){break}aI(149344,51,1,c[m>>2]|0)}else if((gD|0)==61){gH=gv+1|0;c[13898]=gH;nh=c[8272]|0;L4048:do{if((gH|0)<(nh|0)){hF=a[gw+(gH*40&-1)|0]|0;L4050:do{if((hF&1)!=0){hK=c[gw+(gH*40&-1)+36>>2]|0;gG=gw+(gH*40&-1)+32|0;gz=0;while(1){if((gz|0)>=(hK|0)){break}if((a[gB+((c[gG>>2]|0)+gz|0)|0]|0)==(a[gz+103664|0]|0)){gz=gz+1|0}else{break L4050}}if((gz|0)==1){break L4048}}}while(0);gG=gv+2|0;vK=gH;hK=nh;hw=gw;hR=hF;L4057:while(1){nd=(hR&1)==0;hG=c[hw+(vK*40&-1)+36>>2]|0;L4059:do{if(nd){vL=c[10036]|0;vM=hw+(vK*40&-1)+32|0}else{hC=hw+(vK*40&-1)+32|0;hB=c[10036]|0;hS=0;while(1){if((hS|0)>=(hG|0)){break}if((a[hB+((c[hC>>2]|0)+hS|0)|0]|0)==(a[hS+103664|0]|0)){hS=hS+1|0}else{vL=hB;vM=hC;break L4059}}if((hS|0)==1){break L4057}else{vL=hB;vM=hC}}}while(0);gz=c[vM>>2]|0;gx=(hG|0)>0;gy=23264;gA=95280;L4067:while(1){L4069:do{if(!nd){if(gx){ne=0;ho=0;gF=gz;while(1){ng=a[gA+ne|0]|0;if(ng<<24>>24==(a[vL+(ne+gF|0)|0]|0)){vN=gF;vO=ho}else{if(ng<<24>>24!=36){break L4069}vN=gF-1|0;vO=1}vP=ne+1|0;if((vP|0)<(vO+hG|0)){ne=vP;ho=vO;gF=vN}else{break}}if((vO|0)==0){vQ=vP}else{vR=gy;break L4067}}else{vQ=0}gF=a[gA+vQ|0]|0;if((gF<<24>>24|0)==36|(gF<<24>>24|0)==0){vR=gy;break L4067}}}while(0);hC=gy+8|0;hB=c[hC>>2]|0;if((hB|0)==0){vR=hC;break}else{gy=hC;gA=hB}}gA=c[vR+4>>2]|0;L4082:do{if((gA|0)==1){c[13898]=vK+1;if((i9(30632)|0)!=0){break L44}gy=(c[13898]|0)-1|0;c[13898]=gy;if((gy|0)!=(gG|0)){vS=gy;break}c[7662]=1;vS=gG}else if((gA|0)==2){vT=vK+1|0;c[13898]=vT;if((vT|0)>=(hK|0)){b7=5216;break L27}L4088:do{if((a[hw+(vT*40&-1)|0]&1)!=0){gy=c[hw+(vT*40&-1)+36>>2]|0;hG=hw+(vT*40&-1)+32|0;gz=0;while(1){if((gz|0)>=(gy|0)){break}if((a[vL+((c[hG>>2]|0)+gz|0)|0]|0)==(a[gz+103664|0]|0)){gz=gz+1|0}else{break L4088}}if((gz|0)==1){b7=5217;break L27}}}while(0);hG=is(aC)|0;gy=c[hG>>2]|0;if((gy|0)==1){vU=+(c[hG+8>>2]|0)}else if((gy|0)==2){vU=+h[hG+8>>3]}else if((gy|0)==3){vU=+uz(c[hG+8>>2]|0,0)}else{b7=2983;break L27}if((c[em>>2]|0)==3){uu(c[en>>2]|0);c[em>>2]=1}c[7664]=~~vU;vV=c[13898]|0;if((c[8272]|0)<=(vV|0)){b7=5218;break L27}hG=c[1054]|0;if((a[hG+(vV*40&-1)|0]&1)==0){b7=5219;break L27}gy=c[hG+(vV*40&-1)+36>>2]|0;gx=hG+(vV*40&-1)+32|0;hG=c[10036]|0;nd=0;while(1){if((nd|0)>=(gy|0)){break}if((a[hG+((c[gx>>2]|0)+nd|0)|0]|0)==(a[nd+148464|0]|0)){nd=nd+1|0}else{b7=5220;break L27}}if((nd|0)!=1){b7=5221;break L27}c[13898]=vV+1;gx=is(aB)|0;hG=c[gx>>2]|0;if((hG|0)==1){vW=+(c[gx+8>>2]|0)}else if((hG|0)==2){vW=+h[gx+8>>3]}else if((hG|0)==3){vW=+uz(c[gx+8>>2]|0,0)}else{b7=2997;break L27}if((c[eo>>2]|0)==3){uu(c[ep>>2]|0);c[eo>>2]=1}c[7665]=~~vW;gx=(c[13898]|0)-1|0;c[13898]=gx;vS=gx}else if((gA|0)==3){a[30641]=102;vS=vK}else if((gA|0)==4){a[30641]=98;vS=vK}else if((gA|0)==5){a[30641]=97;vS=vK}else if((gA|0)==6){a[30641]=100;vS=vK}else if((gA|0)==7){vX=vK+1|0;c[13898]=vX;gx=c[hw+(vX*40&-1)+36>>2]|0;hG=c[hw+(vX*40&-1)+32>>2]|0;gy=(a[hw+(vX*40&-1)|0]&1)==0;if((gx|0)>0&(gy^1)){vY=0;vZ=0;v_=hG}else{b7=5222;break L27}while(1){if((a[vY+170048|0]|0)==(a[vL+(vY+v_|0)|0]|0)){v$=v_;v0=vZ}else{if((vY|0)!=1){break}v$=v_-1|0;v0=1}hB=vY+1|0;if((hB|0)<(v0+gx|0)){vY=hB;vZ=v0;v_=v$}else{b7=3010;break}}do{if((b7|0)==3010){b7=0;if((v0|0)==0){if(!((vY|0)==0|(vY|0)==5)){break}}a[30639]=98;vS=vX;break L4082}}while(0);if(gy){b7=5223;break L27}else{v1=0;v2=0;v3=hG}while(1){if((a[v1+149304|0]|0)==(a[vL+(v1+v3|0)|0]|0)){v4=v3;v5=v2}else{if((v1|0)!=1){v6=0;v7=0;v8=hG;break}v4=v3-1|0;v5=1}nd=v1+1|0;if((nd|0)<(v5+gx|0)){v1=nd;v2=v5;v3=v4}else{b7=3018;break}}do{if((b7|0)==3018){b7=0;if((v5|0)==0){if(!((v1|0)==0|(v1|0)==6)){v6=0;v7=0;v8=hG;break}}a[30639]=99;vS=vX;break L4082}}while(0);while(1){if((a[v6+169576|0]|0)==(a[vL+(v6+v8|0)|0]|0)){v9=v8;wa=v7}else{if((v6|0)!=1){b7=5224;break L27}v9=v8-1|0;wa=1}hG=v6+1|0;if((hG|0)<(wa+gx|0)){v6=hG;v7=wa;v8=v9}else{break}}if((wa|0)==0){if(!((v6|0)==0|(v6|0)==3)){b7=5225;break L27}}a[30639]=114;vS=vX}else if((gA|0)==10){a[30642]=49;vS=vK}else if((gA|0)==11){a[30642]=52;vS=vK}else if((gA|0)==12){a[30632]=98;a[30633]=0;c[12890]=352;c[10058]=352;c[5094]=1;vS=vK}else if((gA|0)==8){a[30640]=1;vS=vK}else if((gA|0)==9){a[30640]=0;vS=vK}else if((gA|0)==13){gx=vK+1|0;c[13898]=gx;c[7661]=-1;L4162:do{if((a[hw+(gx*40&-1)|0]&1)==0){b7=3045}else{hG=c[10810]|0;if((hG|0)==0){break}gy=(hK|0)>(gx|0);nd=hw+(gx*40&-1)+36|0;hB=hw+(gx*40&-1)+32|0;hC=hG;L4165:while(1){hG=c[hC+4>>2]|0;L4167:do{if(gy){hS=c[nd>>2]|0;gF=0;while(1){if((gF|0)>=(hS|0)){break}if((a[vL+((c[hB>>2]|0)+gF|0)|0]|0)==(a[hG+gF|0]|0)){gF=gF+1|0}else{break L4167}}if((a[hG+gF|0]|0)==0){break L4165}}}while(0);hG=c[hC>>2]|0;if((hG|0)==0){break L4162}else{hC=hG}}if((a[hC+8|0]&1)!=0){break}if((c[hC+16>>2]|0)==1){b7=3045}}}while(0);do{if((b7|0)==3045){b7=0;gx=is(aD)|0;hB=c[gx>>2]|0;if((hB|0)==1){wb=+(c[gx+8>>2]|0)}else if((hB|0)==2){wb=+h[gx+8>>3]}else if((hB|0)==3){wb=+uz(c[gx+8>>2]|0,0)}else{b7=3049;break L27}if((c[eq>>2]|0)==3){uu(c[er>>2]|0);c[eq>>2]=1}gx=~~wb;c[7661]=gx;if((gx|0)>=-1){break}c[7661]=-1}}while(0);gx=(c[13898]|0)-1|0;c[13898]=gx;vS=gx}else if((gA|0)==14){c[7661]=0;vS=vK}else if((gA|0)==15|(gA|0)==16|(gA|0)==17|(gA|0)==18){if((a[37400]&1)==0){wc=vK;b7=3058;break}uh(vK,168664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);wc=c[13898]|0;b7=3058}else if((gA|0)==19|(gA|0)==20){wc=vK;b7=3058}else if((gA|0)==21|(gA|0)==22){c[7662]=0;vS=vK}else if((gA|0)==23){wd=vK+1|0;c[13898]=wd;if((hK|0)<=(wd|0)){b7=5226;break L27}gx=(a[hw+(wd*40&-1)|0]&1)==0;if(gx){b7=5227;break L27}hB=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(hB|0)){b7=3065;break}if((a[vL+((c[nd>>2]|0)+gy|0)|0]|0)==(a[gy+105792|0]|0)){gy=gy+1|0}else{break}}do{if((b7|0)==3065){b7=0;if((gy|0)!=4){break}c[7663]=4;vS=wd;break L4082}}while(0);if(gx){b7=5228;break L27}gy=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;hB=0;while(1){if((hB|0)>=(gy|0)){b7=3071;break}if((a[vL+((c[nd>>2]|0)+hB|0)|0]|0)==(a[hB+167728|0]|0)){hB=hB+1|0}else{break}}do{if((b7|0)==3071){b7=0;if((hB|0)!=7){break}c[7663]=5;vS=wd;break L4082}}while(0);if(gx){b7=5229;break L27}hB=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(hB|0)){b7=3077;break}if((a[vL+((c[nd>>2]|0)+gy|0)|0]|0)==(a[gy+100616|0]|0)){gy=gy+1|0}else{break}}do{if((b7|0)==3077){b7=0;if((gy|0)!=6){break}c[7663]=6;vS=wd;break L4082}}while(0);if(gx){b7=5230;break L27}gy=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;hB=0;while(1){if((hB|0)>=(gy|0)){b7=3083;break}if((a[vL+((c[nd>>2]|0)+hB|0)|0]|0)==(a[hB+103256|0]|0)){hB=hB+1|0}else{break}}do{if((b7|0)==3083){b7=0;if((hB|0)!=3){break}c[7663]=7;vS=wd;break L4082}}while(0);if(gx){b7=5231;break L27}hB=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(hB|0)){b7=3089;break}if((a[vL+((c[nd>>2]|0)+gy|0)|0]|0)==(a[gy+99528|0]|0)){gy=gy+1|0}else{break}}do{if((b7|0)==3089){b7=0;if((gy|0)!=3){break}c[7663]=8;vS=wd;break L4082}}while(0);if(gx){b7=5232;break L27}gy=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;hB=0;while(1){if((hB|0)>=(gy|0)){b7=3095;break}if((a[vL+((c[nd>>2]|0)+hB|0)|0]|0)==(a[hB+166192|0]|0)){hB=hB+1|0}else{break}}do{if((b7|0)==3095){b7=0;if((hB|0)!=2){break}c[7663]=0;vS=wd;break L4082}}while(0);if(gx){b7=5233;break L27}hB=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(hB|0)){b7=3101;break}if((a[vL+((c[nd>>2]|0)+gy|0)|0]|0)==(a[gy+165720|0]|0)){gy=gy+1|0}else{break}}do{if((b7|0)==3101){b7=0;if((gy|0)!=2){break}c[7663]=1;vS=wd;break L4082}}while(0);if(gx){b7=5234;break L27}gy=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;hB=0;while(1){if((hB|0)>=(gy|0)){b7=3107;break}if((a[vL+((c[nd>>2]|0)+hB|0)|0]|0)==(a[hB+165344|0]|0)){hB=hB+1|0}else{break}}do{if((b7|0)==3107){b7=0;if((hB|0)!=2){break}c[7663]=2;vS=wd;break L4082}}while(0);if(gx){b7=5235;break L27}hB=c[hw+(wd*40&-1)+36>>2]|0;nd=hw+(wd*40&-1)+32|0;gy=0;while(1){if((gy|0)>=(hB|0)){break}if((a[vL+((c[nd>>2]|0)+gy|0)|0]|0)==(a[gy+164912|0]|0)){gy=gy+1|0}else{b7=5236;break L27}}if((gy|0)!=2){b7=5237;break L27}c[7663]=3;vS=wd}else{b7=3116;break L27}}while(0);if((b7|0)==3058){b7=0;c[7662]=1;vS=wc}gA=vS+1|0;c[13898]=gA;nd=c[8272]|0;if((gA|0)>=(nd|0)){break}hB=c[1054]|0;vK=gA;hK=nd;hw=hB;hR=a[hB+(gA*40&-1)|0]|0}if((a[30641]|0)!=97|(a[30639]|0)==98){break L44}a[30641]=102;break L44}}while(0);b[15316]=115;a[30639]=98;a[30640]=0;a[30641]=97;a[30642]=52;c[7661]=0;c[7663]=4;c[7664]=1;c[7665]=1;c[7662]=1}else if((gD|0)==60){nh=gv+1|0;c[13898]=nh;gH=c[8272]|0;L4272:do{if((nh|0)<(gH|0)){hR=a[gw+(nh*40&-1)|0]|0;L4274:do{if((hR&1)==0){we=0;wf=0;wg=nh;wh=gH;wi=gw;wj=hR}else{hw=c[gw+(nh*40&-1)+36>>2]|0;hK=gw+(nh*40&-1)+32|0;gG=0;while(1){if((gG|0)>=(hw|0)){break}if((a[gB+((c[hK>>2]|0)+gG|0)|0]|0)==(a[gG+103664|0]|0)){gG=gG+1|0}else{we=0;wf=0;wg=nh;wh=gH;wi=gw;wj=hR;break L4274}}if((gG|0)==1){b7=3127;break L4272}else{we=0;wf=0;wg=nh;wh=gH;wi=gw;wj=hR}}}while(0);L4280:while(1){hR=(wj&1)==0;hK=c[wi+(wg*40&-1)+36>>2]|0;L4282:do{if(hR){wk=c[10036]|0;wl=wi+(wg*40&-1)+32|0}else{hw=wi+(wg*40&-1)+32|0;hF=c[10036]|0;gA=0;while(1){if((gA|0)>=(hK|0)){break}if((a[hF+((c[hw>>2]|0)+gA|0)|0]|0)==(a[gA+103664|0]|0)){gA=gA+1|0}else{wk=hF;wl=hw;break L4282}}if((gA|0)==1){wm=we;break L4280}else{wk=hF;wl=hw}}}while(0);gG=c[wl>>2]|0;gy=(hK|0)>0;hB=23456;nd=140016;L4290:while(1){L4292:do{if(!hR){if(gy){gx=0;gz=0;hG=gG;while(1){gC=a[nd+gx|0]|0;if(gC<<24>>24==(a[wk+(gx+hG|0)|0]|0)){wn=hG;wo=gz}else{if(gC<<24>>24!=36){break L4292}wn=hG-1|0;wo=1}wp=gx+1|0;if((wp|0)<(wo+hK|0)){gx=wp;gz=wo;hG=wn}else{break}}if((wo|0)==0){wq=wp}else{wr=hB;break L4290}}else{wq=0}hG=a[nd+wq|0]|0;if((hG<<24>>24|0)==36|(hG<<24>>24|0)==0){wr=hB;break L4290}}}while(0);hw=hB+8|0;hF=c[hw>>2]|0;if((hF|0)==0){wr=hw;break}else{hB=hw;nd=hF}}nd=c[wr+4>>2]|0;L4305:do{if((nd|0)==1){a[20668]=112;ws=wf;wt=we;wu=wg}else if((nd|0)==2){a[20668]=110;ws=wf;wt=we;wu=wg}else if((nd|0)==3){c[5163]=103;ws=wf;wt=we;wu=wg}else if((nd|0)==13){c[13898]=wg+1;hB=is(aA)|0;hK=c[hB>>2]|0;if((hK|0)==1){wv=+(c[hB+8>>2]|0)}else if((hK|0)==2){wv=+h[hB+8>>3]}else if((hK|0)==3){wv=+uz(c[hB+8>>2]|0,0)}else{b7=3153;break L27}if((c[es>>2]|0)==3){uu(c[et>>2]|0);c[es>>2]=1}h[2705]=wv;hB=(c[13898]|0)-1|0;c[13898]=hB;ws=wf;wt=we;wu=hB}else if((nd|0)==4){hB=c[7640]|0;if((hB|0)==48){c[5163]=114;ws=wf;wt=we;wu=wg;break}else{c[5163]=hB;ws=wf;wt=we;wu=wg;break}}else if((nd|0)==5){if((wf|0)!=0){b7=3161;break L27}c[13898]=wg+1;hB=is(ay)|0;hK=c[hB>>2]|0;if((hK|0)==1){ww=+(c[hB+8>>2]|0)}else if((hK|0)==2){ww=+h[hB+8>>3]}else if((hK|0)==3){ww=+uz(c[hB+8>>2]|0,0)}else{b7=3166;break L27}if((c[eu>>2]|0)==3){uu(c[ev>>2]|0);c[eu>>2]=1}hB=~~ww;if((((hB|0)>-1?hB:-hB|0)|0)>=(c[5162]|0)){b7=3170;break L27}c[5164]=hB;hB=c[13898]|0;L4333:do{if((c[8272]|0)>(hB|0)){hK=c[1054]|0;if((a[hK+(hB*40&-1)|0]&1)==0){break}gG=c[hK+(hB*40&-1)+36>>2]|0;gy=hK+(hB*40&-1)+32|0;hK=c[10036]|0;hR=0;while(1){if((hR|0)>=(gG|0)){break}if((a[hK+((c[gy>>2]|0)+hR|0)|0]|0)==(a[hR+148464|0]|0)){hR=hR+1|0}else{break L4333}}if((hR|0)!=1){break}c[13898]=hB+1;gy=is(ax)|0;hK=c[gy>>2]|0;if((hK|0)==1){wx=+(c[gy+8>>2]|0)}else if((hK|0)==2){wx=+h[gy+8>>3]}else if((hK|0)==3){wx=+uz(c[gy+8>>2]|0,0)}else{b7=3182;break L27}if((c[ew>>2]|0)==3){uu(c[ex>>2]|0);c[ew>>2]=1}gy=~~wx;if((((gy|0)>-1?gy:-gy|0)|0)>=(c[5162]|0)){b7=3186;break L27}c[5165]=gy;gy=c[13898]|0;L4350:do{if((c[8272]|0)>(gy|0)){hK=c[1054]|0;if((a[hK+(gy*40&-1)|0]&1)==0){break}gG=c[hK+(gy*40&-1)+36>>2]|0;hF=hK+(gy*40&-1)+32|0;hK=c[10036]|0;hw=0;while(1){if((hw|0)>=(gG|0)){break}if((a[hK+((c[hF>>2]|0)+hw|0)|0]|0)==(a[hw+148464|0]|0)){hw=hw+1|0}else{break L4350}}if((hw|0)!=1){break}c[13898]=gy+1;hF=is(aw)|0;hK=c[hF>>2]|0;if((hK|0)==1){wy=+(c[hF+8>>2]|0)}else if((hK|0)==2){wy=+h[hF+8>>3]}else if((hK|0)==3){wy=+uz(c[hF+8>>2]|0,0)}else{b7=3198;break L27}if((c[eA>>2]|0)==3){uu(c[eB>>2]|0);c[eA>>2]=1}hF=~~wy;if((((hF|0)>-1?hF:-hF|0)|0)>=(c[5162]|0)){b7=3202;break L27}c[5166]=hF;hF=(c[13898]|0)-1|0;c[13898]=hF;c[5163]=114;c[7640]=114;ws=1;wt=we;wu=hF;break L4305}}while(0);hR=gy-1|0;c[13898]=hR;ws=1;wt=we;wu=hR;break L4305}}while(0);hR=hB-1|0;c[13898]=hR;ws=1;wt=we;wu=hR}else if((nd|0)==14){if((wf|0)!=0){b7=3205;break L27}c[5163]=99;h[2706]=.5;h[2707]=-1.5;h[2708]=1.0;hR=wg+1|0;c[13898]=hR;hF=hR;hR=wh;hK=wi;L4371:while(1){gG=a[hK+(hF*40&-1)|0]&1;L4373:do{if((hR|0)>(hF|0)){if(gG<<24>>24==0){wz=0;b7=3212;break}gA=c[hK+(hF*40&-1)+36>>2]|0;hG=hK+(hF*40&-1)+32|0;gz=c[10036]|0;gx=0;while(1){if((gx|0)>=(gA|0)){break}if((a[gz+((c[hG>>2]|0)+gx|0)|0]|0)==(a[gx+74592|0]|0)){gx=gx+1|0}else{wz=gG;b7=3212;break L4373}}if((gx|0)!=5){wA=gA;wB=hG;wC=gG;b7=3222;break}c[13898]=hF+1;gz=is(av)|0;hw=c[gz>>2]|0;if((hw|0)==1){wD=+(c[gz+8>>2]|0)}else if((hw|0)==2){wD=+h[gz+8>>3]}else if((hw|0)==3){wD=+uz(c[gz+8>>2]|0,0)}else{b7=3218;break L27}if((c[eD>>2]|0)==3){uu(c[eE>>2]|0);c[eD>>2]=1}h[2706]=wD}else{wz=gG;b7=3212}}while(0);if((b7|0)==3212){b7=0;wA=c[hK+(hF*40&-1)+36>>2]|0;wB=hK+(hF*40&-1)+32|0;wC=wz;b7=3222}L4391:do{if((b7|0)==3222){b7=0;gG=c[wB>>2]|0;if(!((wA|0)>0&wC<<24>>24!=0)){break L4371}gy=c[10036]|0;gz=0;hw=0;hC=gG;while(1){if((a[gz+158032|0]|0)==(a[gy+(gz+hC|0)|0]|0)){wE=hC;wF=hw}else{if((gz|0)!=3){wG=0;wH=0;wI=gG;break}wE=hC-1|0;wF=1}gC=gz+1|0;if((gC|0)<(wF+wA|0)){gz=gC;hw=wF;hC=wE}else{b7=3228;break}}do{if((b7|0)==3228){b7=0;if((wF|0)==0){if(!((gz|0)==6|(gz|0)==2)){wG=0;wH=0;wI=gG;break}}c[13898]=hF+1;hC=is(au)|0;hw=c[hC>>2]|0;if((hw|0)==1){wJ=+(c[hC+8>>2]|0)}else if((hw|0)==2){wJ=+h[hC+8>>3]}else if((hw|0)==3){wJ=+uz(c[hC+8>>2]|0,0)}else{b7=3234;break L27}if((c[eH>>2]|0)==3){uu(c[eI>>2]|0);c[eH>>2]=1}h[2707]=wJ;break L4391}}while(0);while(1){if((a[wG+156112|0]|0)==(a[gy+(wG+wI|0)|0]|0)){wK=wI;wL=wH}else{if((wG|0)!=3){break L4371}wK=wI-1|0;wL=1}gG=wG+1|0;if((gG|0)<(wL+wA|0)){wG=gG;wH=wL;wI=wK}else{break}}if((wL|0)==0){if(!((wG|0)==2|(wG|0)==10)){break L4371}}c[13898]=hF+1;gy=is(at)|0;gG=c[gy>>2]|0;if((gG|0)==1){wM=+(c[gy+8>>2]|0)}else if((gG|0)==2){wM=+h[gy+8>>3]}else if((gG|0)==3){wM=+uz(c[gy+8>>2]|0,0)}else{b7=3249;break L27}if((c[eF>>2]|0)==3){uu(c[eG>>2]|0);c[eF>>2]=1}h[2708]=wM}}while(0);hF=c[13898]|0;hR=c[8272]|0;hK=c[1054]|0}hK=hF-1|0;c[13898]=hK;ws=1;wt=we;wu=hK}else if((nd|0)==9){if((wf|0)!=0){b7=3255;break L27}c[13898]=wg+1;c[7271]=-1;uu(c[5173]|0);hK=ut(256)|0;if((hK|0)==0){gk();hR=ut(256)|0;if((hR|0)==0){b7=3258;break L27}else{wN=hR}}else{wN=hK}hK=wN;c[5173]=hK;wO=c[13898]|0;hR=c[8272]|0;do{if((wO|0)<(hR|0)){hB=c[1054]|0;if((a[hB+(wO*40&-1)|0]&1)==0){b7=5238;break L27}gy=c[hB+(wO*40&-1)+36>>2]|0;gG=hB+(wO*40&-1)+32|0;gz=c[10036]|0;hC=0;while(1){if((hC|0)>=(gy|0)){b7=3264;break}if((a[gz+((c[gG>>2]|0)+hC|0)|0]|0)==(a[hC+103664|0]|0)){hC=hC+1|0}else{wP=0;break}}if((b7|0)==3264){b7=0;if((hC|0)==1){wQ=0;wR=hK;b7=3265;break}else{wP=0}}while(1){if((wP|0)>=(gy|0)){break}if((a[gz+((c[gG>>2]|0)+wP|0)|0]|0)==(a[wP+199040|0]|0)){wP=wP+1|0}else{b7=5239;break L27}}if((wP|0)==1){wS=8;wT=0;wU=-1;wV=wO;wW=hR;wX=hB;wY=gz}else{b7=5240;break L27}L4450:while(1){gG=wV+1|0;c[13898]=gG;if((gG|0)>=(wW|0)){wZ=wT;w_=wU;w$=gG;break}L4453:do{if((a[wX+(gG*40&-1)|0]&1)!=0){gy=c[wX+(gG*40&-1)+36>>2]|0;hC=wX+(gG*40&-1)+32|0;hw=0;while(1){if((hw|0)>=(gy|0)){break}if((a[wY+((c[hC>>2]|0)+hw|0)|0]|0)==(a[hw+103664|0]|0)){hw=hw+1|0}else{break L4453}}if((hw|0)==1){wZ=wT;w_=wU;w$=gG;break L4450}}}while(0);gG=is(ap)|0;hC=c[gG>>2]|0;if((hC|0)==1){w0=+(c[gG+8>>2]|0)}else if((hC|0)==2){w0=+h[gG+8>>3]}else if((hC|0)==3){w0=+uz(c[gG+8>>2]|0,0)}else{b7=3282;break L27}if((c[eJ>>2]|0)==3){uu(c[eK>>2]|0);c[eJ>>2]=1}gG=c[13898]|0;L4468:do{if((gG|0)<(c[8272]|0)){hC=c[1054]|0;L4470:do{if((a[hC+(gG*40&-1)|0]&1)!=0){gy=c[hC+(gG*40&-1)+36>>2]|0;gF=hC+(gG*40&-1)+32|0;hG=c[10036]|0;gA=0;while(1){if((gA|0)>=(gy|0)){break}if((a[hG+((c[gF>>2]|0)+gA|0)|0]|0)==(a[gA+103664|0]|0)){gA=gA+1|0}else{break L4470}}if((gA|0)==1){b7=3304;break L4468}}}while(0);a[14176]=1;is(ao);a[14176]=0;if((c[eL>>2]|0)!=3){c[13898]=gG;b7=3304;break}hC=c[eM>>2]|0;if((hC|0)==0){b7=3304;break}if((a[hC]|0)==35){if((ca(hC|0,150416,(v=i,i=i+24|0,c[v>>2]=aq,c[v+8>>2]=ar,c[v+16>>2]=as,v)|0)|0)!=3){b7=3296;break L27}w1=wT;w2=c[as>>2]|0;w3=c[ar>>2]|0;w4=c[aq>>2]|0}else{hw=c[7642]|0;while(1){gF=c[hw>>2]|0;if((gF|0)==0){b7=3302;break L27}if((aY(hC|0,gF|0)|0)==0){break}else{hw=hw+8|0}}gF=c[hw+4>>2]|0;w1=1;w2=gF&255;w3=gF>>>8&255;w4=gF>>>16&255}uu(hC);w5=w1;w6=+(w2|0)/255.0;w7=+(w3|0)/255.0;w8=+(w4|0)/255.0}else{b7=3304}}while(0);if((b7|0)==3304){b7=0;gG=is(an)|0;gF=c[gG>>2]|0;if((gF|0)==1){w9=+(c[gG+8>>2]|0)}else if((gF|0)==2){w9=+h[gG+8>>3]}else if((gF|0)==3){w9=+uz(c[gG+8>>2]|0,0)}else{b7=3308;break L27}if((c[eN>>2]|0)==3){uu(c[eO>>2]|0);c[eN>>2]=1}if(w9<0.0|w9>1.0){b7=3312;break L27}gG=is(am)|0;gF=c[gG>>2]|0;if((gF|0)==1){xa=+(c[gG+8>>2]|0)}else if((gF|0)==2){xa=+h[gG+8>>3]}else if((gF|0)==3){xa=+uz(c[gG+8>>2]|0,0)}else{b7=3317;break L27}if((c[eQ>>2]|0)==3){uu(c[eR>>2]|0);c[eQ>>2]=1}if(xa<0.0|xa>1.0){b7=3321;break L27}gG=is(al)|0;gF=c[gG>>2]|0;if((gF|0)==1){xb=+(c[gG+8>>2]|0)}else if((gF|0)==2){xb=+h[gG+8>>3]}else if((gF|0)==3){xb=+uz(c[gG+8>>2]|0,0)}else{b7=3326;break L27}if((c[eS>>2]|0)==3){uu(c[eT>>2]|0);c[eS>>2]=1}if(xb<0.0|xb>1.0){b7=3330;break L27}else{w5=wT;w6=xb;w7=xa;w8=w9}}gG=wU+1|0;if((gG|0)<(wS|0)){xc=wS;xd=c[5173]|0}else{gF=wS+10|0;hG=db(c[5173]|0,gF<<5,151392)|0;c[5173]=hG;xc=gF;xd=hG}h[xd+(gG<<5)>>3]=w0;h[(c[5173]|0)+(gG<<5)+8>>3]=w8;h[(c[5173]|0)+(gG<<5)+16>>3]=w7;h[(c[5173]|0)+(gG<<5)+24>>3]=w6;xe=c[13898]|0;hG=c[8272]|0;if((hG|0)<=(xe|0)){b7=5241;break L27}gF=c[1054]|0;if((a[gF+(xe*40&-1)|0]&1)==0){b7=5242;break L27}gy=c[gF+(xe*40&-1)+36>>2]|0;hp=gF+(xe*40&-1)+32|0;gx=c[10036]|0;gC=0;while(1){if((gC|0)>=(gy|0)){b7=3339;break}if((a[gx+((c[hp>>2]|0)+gC|0)|0]|0)==(a[gC+131272|0]|0)){gC=gC+1|0}else{xf=0;break}}if((b7|0)==3339){b7=0;if((gC|0)==1){wZ=w5;w_=gG;w$=xe;break}else{xf=0}}while(1){if((xf|0)>=(gy|0)){break}if((a[gx+((c[hp>>2]|0)+xf|0)|0]|0)==(a[xf+148464|0]|0)){xf=xf+1|0}else{b7=5243;break L27}}if((xf|0)==1){wS=xc;wT=w5;wU=gG;wV=xe;wW=hG;wX=gF;wY=gx}else{b7=5244;break L27}}c[5172]=w_+1;gz=c[5173]|0;hB=0;while(1){if((hB|0)>=(w_|0)){break}hp=hB+1|0;if(+h[gz+(hB<<5)>>3]>+h[gz+(hp<<5)>>3]){b7=3347;break L27}else{hB=hp}}m3=+h[gz>>3];m2=1.0/(+h[gz+(w_<<5)>>3]-m3);if((w_|0)>1){hB=1;hp=gz;while(1){gy=hp+(hB<<5)|0;h[gy>>3]=m2*(+h[gy>>3]-m3);gy=hB+1|0;gC=c[5173]|0;if((gy|0)<((c[5172]|0)-1|0)){hB=gy;hp=gC}else{xg=gC;break}}}else{xg=gz}h[xg>>3]=0.0;h[(c[5173]|0)+((c[5172]|0)-1<<5)>>3]=1.0;xh=wZ}else{wQ=0;wR=hK;b7=3265}}while(0);if((b7|0)==3265){while(1){b7=0;h[wR+(wQ<<5)>>3]=+h[23584+(wQ<<5)>>3];h[(c[5173]|0)+(wQ<<5)+8>>3]=+h[23592+(wQ<<5)>>3];h[(c[5173]|0)+(wQ<<5)+16>>3]=+h[23600+(wQ<<5)>>3];h[(c[5173]|0)+(wQ<<5)+24>>3]=+h[23608+(wQ<<5)>>3];hK=wQ+1|0;if((hK|0)>=8){break}wQ=hK;wR=c[5173]|0;b7=3265}c[5172]=8;c[5174]=114;xh=0}c[5163]=100;c[7640]=100;ws=1;wt=xh;wu=c[13898]|0}else if((nd|0)==10){if((wf|0)!=0){b7=3353;break L27}xi=wg+1|0;c[13898]=xi;if((xi|0)>=(wh|0)){xj=xi;b7=5245;break L27}L4552:do{if((a[wi+(xi*40&-1)|0]&1)!=0){hK=c[wi+(xi*40&-1)+36>>2]|0;hR=wi+(xi*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(hK|0)){break}if((a[wk+((c[hR>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{break L4552}}if((hF|0)==1){xj=xi;b7=5246;break L27}}}while(0);a[14176]=1;is(aj);a[14176]=0;if((c[eU>>2]|0)!=3){b7=3361;break L27}hR=c[eV>>2]|0;if((hR|0)==0){b7=3363;break L27}c[11856]=0;hK=ey(hR,4,0)|0;uu(hR);if((hK-1|0)>>>0<2){b7=3366;break L27}hK=c[5173]|0;if((hK|0)!=0){uu(hK);c[5173]=0}hK=ut(320)|0;if((hK|0)==0){gk();hR=ut(320)|0;if((hR|0)==0){b7=3371;break L27}else{xk=hR}}else{xk=hK}c[5173]=xk;hK=eC(eW,4)|0;if((hK|0)==-1){b7=3373;break L27}else{xl=10;xm=0;xn=hK}while(1){if((xm|0)<(xl|0)){xo=xl}else{hK=xl+10|0;c[5173]=db(c[5173]|0,hK<<5,151392)|0;xo=hK}if((xn|0)==3){m3=+h[eW>>3];do{if(m3<0.0){xp=0.0}else{if(m3>1.0){xp=1.0;break}xp=m3}}while(0);h[(c[5173]|0)+(xm<<5)+8>>3]=xp;m3=+h[eX>>3];do{if(m3<0.0){xq=0.0}else{if(m3>1.0){xq=1.0;break}xq=m3}}while(0);h[(c[5173]|0)+(xm<<5)+16>>3]=xq;m3=+h[eY>>3];do{if(m3<0.0){xr=0.0}else{if(m3>1.0){xr=1.0;break}xr=m3}}while(0);h[(c[5173]|0)+(xm<<5)+24>>3]=xr;h[(c[5173]|0)+(xm<<5)>>3]=+(xm|0)}else if((xn|0)==4){m3=+h[eX>>3];do{if(m3<0.0){xs=0.0}else{if(m3>1.0){xs=1.0;break}xs=m3}}while(0);h[(c[5173]|0)+(xm<<5)+8>>3]=xs;m3=+h[eY>>3];do{if(m3<0.0){xt=0.0}else{if(m3>1.0){xt=1.0;break}xt=m3}}while(0);h[(c[5173]|0)+(xm<<5)+16>>3]=xt;m3=+h[eZ>>3];do{if(m3<0.0){xu=0.0}else{if(m3>1.0){xu=1.0;break}xu=m3}}while(0);h[(c[5173]|0)+(xm<<5)+24>>3]=xu;h[(c[5173]|0)+(xm<<5)>>3]=+h[eW>>3]}else{b7=3397;break L27}xv=xm+1|0;hF=eC(eW,4)|0;if((hF|0)==-1){break}else{xl=xo;xm=xv;xn=hF}}ez();if((xv|0)==0){b7=5250;break L27}c[5172]=xv;hF=c[5173]|0;hK=0;while(1){if((hK|0)>=(xm|0)){break}hR=hK+1|0;if(+h[hF+(hK<<5)>>3]>+h[hF+(hR<<5)>>3]){b7=3404;break L27}else{hK=hR}}m3=+h[hF>>3];m2=1.0/(+h[hF+(xm<<5)>>3]-m3);if((xm|0)>1){hK=1;hR=hF;while(1){gz=hR+(hK<<5)|0;h[gz>>3]=m2*(+h[gz>>3]-m3);gz=hK+1|0;hp=c[5173]|0;if((gz|0)<((c[5172]|0)-1|0)){hK=gz;hR=hp}else{xw=hp;break}}}else{xw=hF}h[xw>>3]=0.0;h[(c[5173]|0)+((c[5172]|0)-1<<5)>>3]=1.0;c[5163]=100;c[7640]=100;hR=(c[13898]|0)-1|0;c[13898]=hR;ws=1;wt=we;wu=hR}else if((nd|0)==11){if((wf|0)!=0){b7=3409;break L27}c[13898]=wg+1;uF(e_|0,55600,50);uF(55600,89352,50);xx=c[13898]|0;hR=c[5178]|0;if((hR|0)!=0){e1(hR);c[5178]=0}c[11670]=20704;it();hR=c[18070]|0;hK=db(hR,c[hR>>2]<<5|8,105624)|0;c[18070]=0;c[5178]=hK;if((hK|0)==0){b7=3413;break L27}hK=(c[13898]|0)-1|0;hR=c[1054]|0;hp=(c[hR+(hK*40&-1)+36>>2]|0)+(c[hR+(hK*40&-1)+32>>2]|0)|0;hK=db(c[5179]|0,(hp+1|0)-(c[hR+(xx*40&-1)+32>>2]|0)|0,116456)|0;c[5179]=hK;hR=c[(c[1054]|0)+(xx*40&-1)+32>>2]|0;L4617:do{if((hR|0)<(hp|0)){gz=hR;hB=hK;while(1){gC=a[(c[10036]|0)+gz|0]|0;if(gC<<24>>24==0){xy=hB;break L4617}gy=hB+1|0;a[hB]=gC;gC=gz+1|0;if((gC|0)<(hp|0)){gz=gC;hB=gy}else{xy=gy;break}}}else{xy=hK}}while(0);a[xy]=0;c[11670]=0;xz=c[13898]|0;if((c[8272]|0)<=(xz|0)){b7=5251;break L27}hK=c[1054]|0;if((a[hK+(xz*40&-1)|0]&1)==0){b7=5252;break L27}hp=c[hK+(xz*40&-1)+36>>2]|0;hR=hK+(xz*40&-1)+32|0;hK=c[10036]|0;hF=0;while(1){if((hF|0)>=(hp|0)){break}if((a[hK+((c[hR>>2]|0)+hF|0)|0]|0)==(a[hF+148464|0]|0)){hF=hF+1|0}else{b7=5253;break L27}}if((hF|0)!=1){b7=5254;break L27}xA=xz+1|0;c[13898]=xA;hR=c[5256]|0;if((hR|0)!=0){e1(hR);c[5256]=0}c[11670]=21016;it();hR=c[18070]|0;hK=db(hR,c[hR>>2]<<5|8,105624)|0;c[18070]=0;c[5256]=hK;if((hK|0)==0){b7=3427;break L27}hK=(c[13898]|0)-1|0;hR=c[1054]|0;hp=(c[hR+(hK*40&-1)+36>>2]|0)+(c[hR+(hK*40&-1)+32>>2]|0)|0;hK=db(c[5257]|0,(hp+1|0)-(c[hR+(xA*40&-1)+32>>2]|0)|0,116456)|0;c[5257]=hK;hR=c[(c[1054]|0)+(xA*40&-1)+32>>2]|0;L4633:do{if((hR|0)<(hp|0)){hB=hR;gz=hK;while(1){gy=a[(c[10036]|0)+hB|0]|0;if(gy<<24>>24==0){xB=gz;break L4633}gC=gz+1|0;a[gz]=gy;gy=hB+1|0;if((gy|0)<(hp|0)){hB=gy;gz=gC}else{xB=gC;break}}}else{xB=hK}}while(0);a[xB]=0;c[11670]=0;xC=c[13898]|0;if((c[8272]|0)<=(xC|0)){b7=5255;break L27}hK=c[1054]|0;if((a[hK+(xC*40&-1)|0]&1)==0){b7=5256;break L27}hp=c[hK+(xC*40&-1)+36>>2]|0;hR=hK+(xC*40&-1)+32|0;hK=c[10036]|0;hF=0;while(1){if((hF|0)>=(hp|0)){break}if((a[hK+((c[hR>>2]|0)+hF|0)|0]|0)==(a[hF+148464|0]|0)){hF=hF+1|0}else{b7=5257;break L27}}if((hF|0)!=1){b7=5258;break L27}xD=xC+1|0;c[13898]=xD;hR=c[5334]|0;if((hR|0)!=0){e1(hR);c[5334]=0}c[11670]=21328;it();hR=c[18070]|0;hK=db(hR,c[hR>>2]<<5|8,105624)|0;c[18070]=0;c[5334]=hK;if((hK|0)==0){b7=3441;break L27}hK=(c[13898]|0)-1|0;hR=c[1054]|0;hp=(c[hR+(hK*40&-1)+36>>2]|0)+(c[hR+(hK*40&-1)+32>>2]|0)|0;hK=db(c[5335]|0,(hp+1|0)-(c[hR+(xD*40&-1)+32>>2]|0)|0,116456)|0;c[5335]=hK;hR=c[(c[1054]|0)+(xD*40&-1)+32>>2]|0;L4649:do{if((hR|0)<(hp|0)){gz=hR;hB=hK;while(1){gC=a[(c[10036]|0)+gz|0]|0;if(gC<<24>>24==0){xE=hB;break L4649}gy=hB+1|0;a[hB]=gC;gC=gz+1|0;if((gC|0)<(hp|0)){gz=gC;hB=gy}else{xE=gy;break}}}else{xE=hK}}while(0);a[xE]=0;c[11670]=0;uF(55600,e_|0,50);c[5163]=102;c[7640]=102;hK=(c[13898]|0)-1|0;c[13898]=hK;ws=1;wt=we;wu=hK}else if((nd|0)==12){xF=wg+1|0;c[13898]=xF;if((xF|0)>=(wh|0)){b7=5259;break L27}L4656:do{if((a[wi+(xF*40&-1)|0]&1)==0){b7=3495}else{hK=c[wi+(xF*40&-1)+36>>2]|0;hp=wi+(xF*40&-1)+32|0;hR=0;while(1){if((hR|0)>=(hK|0)){b7=3451;break}hF=c[hp>>2]|0;if((a[wk+(hF+hR|0)|0]|0)==(a[hR+103664|0]|0)){hR=hR+1|0}else{xG=hF;break}}if((b7|0)==3451){b7=0;if((hR|0)==1){b7=5260;break L27}xG=c[hp>>2]|0}hF=(hK|0)>0;L4665:do{if(hF){hB=0;while(1){if((a[hB+137240|0]|0)!=(a[wk+(hB+xG|0)|0]|0)){break L4665}gz=hB+1|0;if((gz|0)<(hK|0)){hB=gz}else{xH=gz;b7=3457;break}}}else{xH=0;b7=3457}}while(0);if((b7|0)==3457){b7=0;hp=a[xH+137240|0]|0;if((hp<<24>>24|0)==36|(hp<<24>>24|0)==0){xI=53960;break}}L4672:do{if(hF){hp=0;while(1){if((a[hp+137176|0]|0)!=(a[wk+(hp+xG|0)|0]|0)){break L4672}hR=hp+1|0;if((hR|0)<(hK|0)){hp=hR}else{xJ=hR;b7=3482;break}}}else{xJ=0;b7=3482}}while(0);if((b7|0)==3482){b7=0;hp=a[xJ+137176|0]|0;if((hp<<24>>24|0)==36|(hp<<24>>24|0)==0){xI=53968;break}}L4679:do{if(hF){hp=0;while(1){if((a[hp+137048|0]|0)!=(a[wk+(hp+xG|0)|0]|0)){break L4679}hR=hp+1|0;if((hR|0)<(hK|0)){hp=hR}else{xK=hR;b7=3486;break}}}else{xK=0;b7=3486}}while(0);if((b7|0)==3486){b7=0;hp=a[xK+137048|0]|0;if((hp<<24>>24|0)==36|(hp<<24>>24|0)==0){xI=53976;break}}L4686:do{if(hF){hp=0;while(1){if((a[hp+136776|0]|0)!=(a[wk+(hp+xG|0)|0]|0)){break L4686}hR=hp+1|0;if((hR|0)<(hK|0)){hp=hR}else{xL=hR;b7=3490;break}}}else{xL=0;b7=3490}}while(0);if((b7|0)==3490){b7=0;hp=a[xL+136776|0]|0;if((hp<<24>>24|0)==36|(hp<<24>>24|0)==0){xI=53984;break}}if(hF){hp=0;while(1){if((a[hp+136432|0]|0)!=(a[wk+(hp+xG|0)|0]|0)){b7=3495;break L4656}hR=hp+1|0;if((hR|0)<(hK|0)){hp=hR}else{xM=hR;break}}}else{xM=0}hp=a[xM+136432|0]|0;if((hp<<24>>24|0)==36|(hp<<24>>24|0)==0){xI=53992}else{b7=3495}}}while(0);if((b7|0)==3495){b7=0;xI=54e3}hp=c[xI+4>>2]|0;if((hp|0)==-1){b7=3459;break L27}c[5174]=hp;ws=wf;wt=we;wu=xF}else if((nd|0)==6){a[20684]=0;ws=wf;wt=we;wu=wg}else if((nd|0)==7){a[20684]=1;ws=wf;wt=we;wu=wg}else if((nd|0)==8){c[13898]=wg+1;hp=is(ah)|0;hK=c[hp>>2]|0;if((hK|0)==1){xN=+(c[hp+8>>2]|0)}else if((hK|0)==2){xN=+h[hp+8>>3]}else if((hK|0)==3){xN=+uz(c[hp+8>>2]|0,0)}else{b7=3467;break L27}if((c[e$>>2]|0)==3){uu(c[e0>>2]|0);c[e$>>2]=1}hp=~~xN;if((hp|0)<0){b7=3471;break L27}c[5168]=hp;hp=(c[13898]|0)-1|0;c[13898]=hp;ws=wf;wt=we;wu=hp}else{b7=3473;break L27}}while(0);nd=wu+1|0;c[13898]=nd;hp=c[8272]|0;if((nd|0)>=(hp|0)){wm=wt;break}hK=c[1054]|0;we=wt;wf=ws;wg=nd;wh=hp;wi=hK;wj=a[hK+(nd*40&-1)|0]|0}if(!((wm|0)!=0&(c[5174]|0)!=114)){break}if((a[37400]&1)==0){break}uh(-1,153616,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{b7=3127}}while(0);do{if((b7|0)==3127){b7=0;if((c[11254]|0)==0){break}c[5163]=114;c[5164]=7;c[5165]=5;c[5166]=15;a[20668]=112;a[20684]=0;c[5168]=0;c[5172]=0;uu(c[5173]|0);c[5173]=0;uu(c[5170]|0);c[5170]=0;c[5174]=114;h[2705]=1.5;c[7640]=48}}while(0);c[7271]=-1}else if((gD|0)==62){gH=gv+1|0;c[13898]=gH;nh=c[8272]|0;L4724:do{if((gH|0)<(nh|0)){nd=a[gw+(gH*40&-1)|0]|0;L4726:do{if((nd&1)==0){xO=gH;xP=nh;xQ=gw;xR=nd}else{hK=c[gw+(gH*40&-1)+36>>2]|0;hp=gw+(gH*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(hK|0)){break}if((a[gB+((c[hp>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{xO=gH;xP=nh;xQ=gw;xR=nd;break L4726}}if((hF|0)==1){break L4724}else{xO=gH;xP=nh;xQ=gw;xR=nd}}}while(0);L4732:while(1){nd=(xR&1)==0;hp=c[xQ+(xO*40&-1)+36>>2]|0;L4734:do{if(nd){xS=c[10036]|0;xT=xQ+(xO*40&-1)+32|0}else{hK=xQ+(xO*40&-1)+32|0;hR=c[10036]|0;hB=0;while(1){if((hB|0)>=(hp|0)){break}if((a[hR+((c[hK>>2]|0)+hB|0)|0]|0)==(a[hB+103664|0]|0)){hB=hB+1|0}else{xS=hR;xT=hK;break L4734}}if((hB|0)==1){break L4732}else{xS=hR;xT=hK}}}while(0);hF=c[xT>>2]|0;gx=(hp|0)>0;gF=25168;hG=141728;L4742:while(1){L4744:do{if(!nd){if(gx){gG=0;gz=0;gy=hF;while(1){gC=a[hG+gG|0]|0;if(gC<<24>>24==(a[xS+(gG+gy|0)|0]|0)){xU=gy;xV=gz}else{if(gC<<24>>24!=36){break L4744}xU=gy-1|0;xV=1}xW=gG+1|0;if((xW|0)<(xV+hp|0)){gG=xW;gz=xV;gy=xU}else{break}}if((xV|0)==0){xX=xW}else{xY=gF;break L4742}}else{xX=0}gy=a[hG+xX|0]|0;if((gy<<24>>24|0)==36|(gy<<24>>24|0)==0){xY=gF;break L4742}}}while(0);hK=gF+8|0;hR=c[hK>>2]|0;if((hR|0)==0){xY=hK;break}else{gF=hK;hG=hR}}hG=c[xY+4>>2]|0;L4757:do{if((hG|0)==1){a[54017]=118;xZ=xO}else if((hG|0)==2){a[54017]=104;xZ=xO}else if((hG|0)==3){a[54016]=100;xZ=xO}else if((hG|0)==4){a[54016]=117;xZ=xO}else if((hG|0)==10){c[13506]=1;xZ=xO}else if((hG|0)==11){c[13506]=0;xZ=xO}else if((hG|0)==5){a[54018]=1;gF=xO+1|0;c[13898]=gF;if((gF|0)>=(xP|0)){xZ=gF;break}L4766:do{if((a[xQ+(gF*40&-1)|0]&1)!=0){hp=c[xQ+(gF*40&-1)+36>>2]|0;hF=xQ+(gF*40&-1)+32|0;gx=0;while(1){if((gx|0)>=(hp|0)){break}if((a[xS+((c[hF>>2]|0)+gx|0)|0]|0)==(a[gx+103664|0]|0)){gx=gx+1|0}else{break L4766}}if((gx|0)==1){xZ=gF;break L4757}}}while(0);gF=is(ag)|0;hF=c[gF>>2]|0;if((hF|0)==1){x_=+(c[gF+8>>2]|0)}else if((hF|0)==2){x_=+h[gF+8>>3]}else if((hF|0)==3){x_=+uz(c[gF+8>>2]|0,0)}else{b7=3537;break L27}if((c[e2>>2]|0)==3){uu(c[e3>>2]|0);c[e2>>2]=1}gF=~~x_;c[13505]=gF;if((gF|0)<1){b7=3541;break L27}gF=(c[13898]|0)-1|0;c[13898]=gF;xZ=gF}else if((hG|0)==6){c[13505]=-1;xZ=xO}else if((hG|0)==7){a[54018]=0;xZ=xO}else if((hG|0)==8){x$=xO+1|0;c[13898]=x$;if((x$|0)>=(xP|0)){b7=5261;break L27}L4786:do{if((a[xQ+(x$*40&-1)|0]&1)!=0){gF=c[xQ+(x$*40&-1)+36>>2]|0;hF=xQ+(x$*40&-1)+32|0;hp=0;while(1){if((hp|0)>=(gF|0)){break}if((a[xS+((c[hF>>2]|0)+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{break L4786}}if((hp|0)==1){b7=5262;break L27}}}while(0);dl(54032,3);hF=(c[13898]|0)-1|0;c[13898]=hF;xZ=hF}else if((hG|0)==9){x0=xO+1|0;c[13898]=x0;if((x0|0)>=(xP|0)){b7=5263;break L27}L4795:do{if((a[xQ+(x0*40&-1)|0]&1)!=0){hF=c[xQ+(x0*40&-1)+36>>2]|0;gF=xQ+(x0*40&-1)+32|0;gx=0;while(1){if((gx|0)>=(hF|0)){break}if((a[xS+((c[gF>>2]|0)+gx|0)|0]|0)==(a[gx+103664|0]|0)){gx=gx+1|0}else{break L4795}}if((gx|0)==1){b7=5264;break L27}}}while(0);dl(54072,3);gF=(c[13898]|0)-1|0;c[13898]=gF;xZ=gF}else{b7=3561;break L27}}while(0);hG=xZ+1|0;c[13898]=hG;gF=c[8272]|0;if((hG|0)>=(gF|0)){break}hF=c[1054]|0;xO=hG;xP=gF;xQ=hF;xR=a[hF+(hG*40&-1)|0]|0}if((a[54016]|0)!=110){break L44}a[54016]=100;break L44}}while(0);a[54016]=100}else if((gD|0)==77){nh=gv+1|0;c[13898]=nh;L4808:do{if((nh|0)<(c[8272]|0)){L4810:do{if((a[gw+(nh*40&-1)|0]&1)!=0){gH=c[gw+(nh*40&-1)+36>>2]|0;hG=gw+(nh*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(gH|0)){break}if((a[gB+((c[hG>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{break L4810}}if((hF|0)==1){x1=1.0;break L4808}}}while(0);hG=is(af)|0;gH=c[hG>>2]|0;if((gH|0)==1){x2=+(c[hG+8>>2]|0)}else if((gH|0)==2){x2=+h[hG+8>>3]}else if((gH|0)==3){x2=+uz(c[hG+8>>2]|0,0)}else{b7=3576;break L27}if((c[e4>>2]|0)!=3){x1=x2;break}uu(c[e5>>2]|0);c[e4>>2]=1;x1=x2}else{x1=1.0}}while(0);h[3818]=x1>0.0?x1:1.0}else if((gD|0)==78){nh=gv+1|0;c[13898]=nh;L4825:do{if((nh|0)<(c[8272]|0)){L4827:do{if((a[gw+(nh*40&-1)|0]&1)!=0){hG=c[gw+(nh*40&-1)+36>>2]|0;gH=gw+(nh*40&-1)+32|0;gF=0;while(1){if((gF|0)>=(hG|0)){break}if((a[gB+((c[gH>>2]|0)+gF|0)|0]|0)==(a[gF+103664|0]|0)){gF=gF+1|0}else{break L4827}}if((gF|0)==1){x3=1.0;break L4825}}}while(0);gH=is(ae)|0;hG=c[gH>>2]|0;if((hG|0)==1){x4=+(c[gH+8>>2]|0)}else if((hG|0)==2){x4=+h[gH+8>>3]}else if((hG|0)==3){x4=+uz(c[gH+8>>2]|0,0)}else{b7=3590;break L27}if((c[e6>>2]|0)!=3){x3=x4;break}uu(c[e8>>2]|0);c[e6>>2]=1;x3=x4}else{x3=1.0}}while(0);h[3817]=x3>0.0?x3:1.0}else if((gD|0)==79){c[13898]=gv+1;if((a[30528]&1)!=0){break}a[30528]=1;if((a[32936]&1)==0){if((a[37400]&1)!=0){nh=c[m>>2]|0;aI(171240,33,1,nh|0)}b[12272]=116}if((c[17539]|0)!=0){h[8773]=0.0;h[8774]=6.283185307179586/+h[9040]}if((c[17367]|0)==3){break}ju()}else if((gD|0)==80){c[ad>>2]=0;x5=gv+1|0;c[13898]=x5;L4854:do{if((x5|0)<(c[8272]|0)){L4856:do{if((a[gw+(x5*40&-1)|0]&1)!=0){nh=c[gw+(x5*40&-1)+36>>2]|0;gH=gw+(x5*40&-1)+32|0;hG=0;while(1){if((hG|0)>=(nh|0)){b7=3608;break}if((a[gB+((c[gH>>2]|0)+hG|0)|0]|0)==(a[hG+103664|0]|0)){hG=hG+1|0}else{x6=0;break}}if((b7|0)==3608){b7=0;if((hG|0)==1){break L4854}else{x6=0}}while(1){if((x6|0)>=(nh|0)){break}if((a[gB+((c[gH>>2]|0)+x6|0)|0]|0)==(a[x6+103664|0]|0)){x6=x6+1|0}else{break L4856}}if((x6|0)==1){b7=3615;break L27}}}while(0);a[14176]=1;is(ac);a[14176]=0;if((c[e9>>2]|0)!=3){b7=3614;break L27}gH=c[fa>>2]|0;c[ad>>2]=gH;if((gH|0)==0){b7=3617;break L27}iQ(ad);x7=c[13898]|0;do{if((x7|0)<(c[8272]|0)){gH=c[1054]|0;if((a[gH+(x7*40&-1)|0]&1)==0){b7=5265;break L27}nh=c[gH+(x7*40&-1)+36>>2]|0;hG=gH+(x7*40&-1)+32|0;gH=c[10036]|0;gF=0;while(1){if((gF|0)>=(nh|0)){b7=3623;break}if((a[gH+((c[hG>>2]|0)+gF|0)|0]|0)==(a[gF+103664|0]|0)){gF=gF+1|0}else{x8=0;break}}if((b7|0)==3623){b7=0;if((gF|0)==1){x9=0;break}else{x8=0}}while(1){if((x8|0)>=(nh|0)){break}if((a[gH+((c[hG>>2]|0)+x8|0)|0]|0)==(a[x8+148880|0]|0)){x8=x8+1|0}else{b7=5266;break L27}}if((x8|0)!=6){b7=5267;break L27}c[13898]=x7+1;x9=1}else{x9=0}}while(0);ea(c[ad>>2]|0,x9);break L44}}while(0);ea(0,0)}else if((gD|0)==81){ya=gv+1|0;c[13898]=ya;L4886:do{if((ya|0)<(c[8272]|0)){L4888:do{if((a[gw+(ya*40&-1)|0]&1)!=0){hG=c[gw+(ya*40&-1)+36>>2]|0;gH=gw+(ya*40&-1)+32|0;nh=0;while(1){if((nh|0)>=(hG|0)){b7=3636;break}if((a[gB+((c[gH>>2]|0)+nh|0)|0]|0)==(a[nh+103664|0]|0)){nh=nh+1|0}else{yb=0;break}}if((b7|0)==3636){b7=0;if((nh|0)==1){break L4886}else{yb=0}}while(1){if((yb|0)>=(hG|0)){break}if((a[gB+((c[gH>>2]|0)+yb|0)|0]|0)==(a[yb+103664|0]|0)){yb=yb+1|0}else{break L4888}}if((yb|0)==1){b7=3643;break L27}}}while(0);a[14176]=1;is(ab);a[14176]=0;if((c[fb>>2]|0)!=3){b7=3642;break L27}gH=c[fc>>2]|0;c[57238]=gH;if((gH|0)==0){b7=3645;break L27}iQ(228952);break L44}}while(0);uu(c[57238]|0);c[57238]=0}else if((gD|0)==75){gH=gv+1|0;c[13898]=gH;hG=c[gw+(gH*40&-1)+36>>2]|0;nh=c[gw+(gH*40&-1)+32>>2]|0;gF=a[gw+(gH*40&-1)|0]|0;hF=(gF&1)==0;L4904:do{if((hG|0)>0&(hF^1)){hp=0;nd=0;hR=nh;while(1){if((a[hp+115824|0]|0)==(a[gB+(hp+hR|0)|0]|0)){yc=hR;yd=nd}else{if((hp|0)!=4){break}yc=hR-1|0;yd=1}hK=hp+1|0;if((hK|0)<(yd+hG|0)){hp=hK;nd=yd;hR=yc}else{b7=3653;break}}if((b7|0)==3653){b7=0;if((yd|0)!=0){ye=-1;yf=gH;yg=hG;yh=nh;yi=gF;break}if((hp|0)==3|(hp|0)==9){ye=-1;yf=gH;yg=hG;yh=nh;yi=gF;break}}if(hF){b7=3674;break}else{yj=0;yk=0;yl=nh}while(1){if((a[yj+115976|0]|0)==(a[gB+(yj+yl|0)|0]|0)){ym=yl;yn=yk}else{if((yj|0)!=3){yo=0;yp=0;yq=nh;break}ym=yl-1|0;yn=1}hR=yj+1|0;if((hR|0)<(yn+hG|0)){yj=hR;yk=yn;yl=ym}else{b7=3660;break}}if((b7|0)==3660){b7=0;if((yn|0)!=0){ye=-1;yf=gH;yg=hG;yh=nh;yi=gF;break}if((yj|0)==2|(yj|0)==7){ye=-1;yf=gH;yg=hG;yh=nh;yi=gF;break}else{yo=0;yp=0;yq=nh}}while(1){if((a[yo+116120|0]|0)==(a[gB+(yo+yq|0)|0]|0)){yr=yq;ys=yp}else{if((yo|0)!=4){yt=0;yu=0;yv=nh;break}yr=yq-1|0;ys=1}hp=yo+1|0;if((hp|0)<(ys+hG|0)){yo=hp;yp=ys;yq=yr}else{b7=3666;break}}if((b7|0)==3666){b7=0;if((ys|0)!=0){ye=-1;yf=gH;yg=hG;yh=nh;yi=gF;break}if((yo|0)==3|(yo|0)==6){ye=-1;yf=gH;yg=hG;yh=nh;yi=gF;break}else{yt=0;yu=0;yv=nh}}while(1){if((a[yt+172184|0]|0)==(a[gB+(yt+yv|0)|0]|0)){yw=yv;yx=yu}else{if((yt|0)!=4){b7=3674;break L4904}yw=yv-1|0;yx=1}hp=yt+1|0;if((hp|0)<(yx+hG|0)){yt=hp;yu=yx;yv=yw}else{break}}if((yx|0)!=0){ye=-1;yf=gH;yg=hG;yh=nh;yi=gF;break}if((yt|0)==3|(yt|0)==7){ye=-1;yf=gH;yg=hG;yh=nh;yi=gF}else{b7=3674}}else{b7=3674}}while(0);if((b7|0)==3674){b7=0;gF=is($)|0;nh=c[gF>>2]|0;if((nh|0)==1){yy=+(c[gF+8>>2]|0)}else if((nh|0)==2){yy=+h[gF+8>>3]}else if((nh|0)==3){yy=+uz(c[gF+8>>2]|0,0)}else{b7=3678;break L27}if((c[fd>>2]|0)==3){uu(c[fe>>2]|0);c[fd>>2]=1}gF=~~yy;yz=c[13898]|0;if((gF|0)<1){b7=3683;break L27}nh=c[1054]|0;ye=gF;yf=yz;yg=c[nh+(yz*40&-1)+36>>2]|0;yh=c[nh+(yz*40&-1)+32>>2]|0;yi=a[nh+(yz*40&-1)|0]|0}nh=(yi&1)==0;L4953:do{if((yg|0)>0&(nh^1)){gF=c[10036]|0;hG=0;gH=0;hF=yh;while(1){if((a[hG+115824|0]|0)==(a[gF+(hG+hF|0)|0]|0)){yA=hF;yB=gH}else{if((hG|0)!=4){break}yA=hF-1|0;yB=1}hp=hG+1|0;if((hp|0)<(yB+yg|0)){hG=hp;gH=yB;hF=yA}else{b7=3690;break}}do{if((b7|0)==3690){b7=0;if((yB|0)==0){if(!((hG|0)==3|(hG|0)==9)){break}}jx(ye,1);break L44}}while(0);if(nh){break}else{yC=0;yD=0;yE=yh}while(1){if((a[yC+115976|0]|0)==(a[gF+(yC+yE|0)|0]|0)){yF=yE;yG=yD}else{if((yC|0)!=3){yH=0;yI=0;yJ=yh;break}yF=yE-1|0;yG=1}hG=yC+1|0;if((hG|0)<(yG+yg|0)){yC=hG;yD=yG;yE=yF}else{b7=3698;break}}do{if((b7|0)==3698){b7=0;if((yG|0)==0){if(!((yC|0)==2|(yC|0)==7)){yH=0;yI=0;yJ=yh;break}}jx(ye,3);break L44}}while(0);while(1){if((a[yH+116120|0]|0)==(a[gF+(yH+yJ|0)|0]|0)){yK=yJ;yL=yI}else{if((yH|0)!=4){yM=0;yN=0;yO=yh;break}yK=yJ-1|0;yL=1}hG=yH+1|0;if((hG|0)<(yL+yg|0)){yH=hG;yI=yL;yJ=yK}else{b7=3705;break}}do{if((b7|0)==3705){b7=0;if((yL|0)==0){if(!((yH|0)==3|(yH|0)==6)){yM=0;yN=0;yO=yh;break}}jx(ye,2);break L44}}while(0);while(1){if((a[yM+172184|0]|0)==(a[gF+(yM+yO|0)|0]|0)){yP=yO;yQ=yN}else{if((yM|0)!=4){break L4953}yP=yO-1|0;yQ=1}hG=yM+1|0;if((hG|0)<(yQ+yg|0)){yM=hG;yN=yQ;yO=yP}else{break}}if((yQ|0)==0){if(!((yM|0)==3|(yM|0)==7)){break}}jx(ye,4);break L44}}while(0);if((ye|0)>0){yR=43272}else{b7=3720;break L27}while(1){yS=c[yR>>2]|0;if((yS|0)==0){b7=3719;break L27}if((ye|0)==(c[yS+4>>2]|0)){break}else{yR=yS|0}}c[13898]=yf-1;jx(ye,c[yS+12>>2]|0)}else if((gD|0)==82){c[13898]=gv+1;nh=is(Z)|0;gF=c[nh>>2]|0;if((gF|0)==1){yT=+(c[nh+8>>2]|0)}else if((gF|0)==2){yT=+h[nh+8>>3]}else if((gF|0)==3){yT=+uz(c[nh+8>>2]|0,0)}else{b7=3725;break L27}if((c[ff>>2]|0)==3){uu(c[fg>>2]|0);c[ff>>2]=1}nh=~~yT;gF=(nh|0)>-1?nh:-nh|0;yU=c[13898]|0;do{if((yU|0)<(c[8272]|0)){nh=c[1054]|0;if((a[nh+(yU*40&-1)|0]&1)==0){b7=5272;break L27}hG=c[nh+(yU*40&-1)+36>>2]|0;hF=nh+(yU*40&-1)+32|0;nh=c[10036]|0;gH=0;while(1){if((gH|0)>=(hG|0)){b7=3733;break}if((a[nh+((c[hF>>2]|0)+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{yV=0;break}}if((b7|0)==3733){b7=0;if((gH|0)==1){yW=gF;break}else{yV=0}}while(1){if((yV|0)>=(hG|0)){break}if((a[nh+((c[hF>>2]|0)+yV|0)|0]|0)==(a[yV+148464|0]|0)){yV=yV+1|0}else{b7=5273;break L27}}if((yV|0)!=1){b7=5274;break L27}c[13898]=yU+1;hF=is(Y)|0;nh=c[hF>>2]|0;if((nh|0)==1){yX=+(c[hF+8>>2]|0)}else if((nh|0)==2){yX=+h[hF+8>>3]}else if((nh|0)==3){yX=+uz(c[hF+8>>2]|0,0)}else{b7=3742;break L27}if((c[fh>>2]|0)==3){uu(c[fi>>2]|0);c[fh>>2]=1}hF=~~yX;yW=(hF|0)>-1?hF:-hF|0}else{yW=gF}}while(0);if((gF|0)<2|(yW|0)<2){b7=3747;break L27}hF=c[10828]|0;c[10828]=0;iY(hF);c[6352]=gF;c[6350]=yW}else if((gD|0)==83){hF=gv+1|0;c[13898]=hF;nh=c[8272]|0;L5037:do{if((hF|0)<(nh|0)){L5039:do{if((a[gw+(hF*40&-1)|0]&1)==0){yY=hF;yZ=nh}else{hG=c[gw+(hF*40&-1)+36>>2]|0;gH=gw+(hF*40&-1)+32|0;hp=0;while(1){if((hp|0)>=(hG|0)){b7=3754;break}hR=c[gH>>2]|0;if((a[gB+(hR+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{y_=hR;break}}if((b7|0)==3754){b7=0;if((hp|0)==1){b7=3756;break L5037}y_=c[gH>>2]|0}if((hG|0)>0){y$=0;y0=0;y1=y_}else{yY=hF;yZ=nh;break}while(1){if((a[y$+175752|0]|0)==(a[gB+(y$+y1|0)|0]|0)){y2=y1;y3=y0}else{if((y$|0)!=2){y4=0;y5=0;y6=y_;break}y2=y1-1|0;y3=1}hR=y$+1|0;if((hR|0)<(y3+hG|0)){y$=hR;y0=y3;y1=y2}else{b7=3762;break}}do{if((b7|0)==3762){b7=0;if((y3|0)==0){if(!((y$|0)==1|(y$|0)==6)){y4=0;y5=0;y6=y_;break}}g[18074]=1.0;gH=gv+2|0;c[13898]=gH;yY=gH;yZ=nh;break L5039}}while(0);while(1){if((a[y4+175400|0]|0)==(a[gB+(y4+y6|0)|0]|0)){y7=y6;y8=y5}else{if((y4|0)!=2){y9=0;za=0;zb=y_;break}y7=y6-1|0;y8=1}gH=y4+1|0;if((gH|0)<(y8+hG|0)){y4=gH;y5=y8;y6=y7}else{b7=3769;break}}do{if((b7|0)==3769){b7=0;if((y8|0)==0){if(!((y4|0)==5|(y4|0)==1)){y9=0;za=0;zb=y_;break}}c[13898]=gv+2;gH=is(W)|0;hp=c[gH>>2]|0;if((hp|0)==1){zc=+(c[gH+8>>2]|0)}else if((hp|0)==2){zc=+h[gH+8>>3]}else if((hp|0)==3){zc=+uz(c[gH+8>>2]|0,0)}else{b7=3775;break L27}if((c[fn>>2]|0)==3){uu(c[fo>>2]|0);c[fn>>2]=1}g[18074]=zc;yY=c[13898]|0;yZ=c[8272]|0;break L5039}}while(0);while(1){if((a[y9+174920|0]|0)==(a[gB+(y9+zb|0)|0]|0)){zd=zb;ze=za}else{if((y9|0)!=4){zf=0;zg=0;zh=y_;b7=3785;break}zd=zb-1|0;ze=1}gH=y9+1|0;if((gH|0)<(ze+hG|0)){y9=gH;za=ze;zb=zd}else{b7=3783;break}}do{if((b7|0)==3783){b7=0;if((ze|0)!=0){break}if(!((y9|0)==3|(y9|0)==7)){zf=0;zg=0;zh=y_;b7=3785}}}while(0);do{if((b7|0)==3785){while(1){b7=0;if((a[zf+174640|0]|0)==(a[gB+(zf+zh|0)|0]|0)){zi=zh;zj=zg}else{if((zf|0)!=4){yY=hF;yZ=nh;break L5039}zi=zh-1|0;zj=1}gH=zf+1|0;if((gH|0)<(zj+hG|0)){zf=gH;zg=zj;zh=zi;b7=3785}else{break}}if((zj|0)!=0){break}if(!((zf|0)==3|(zf|0)==8)){yY=hF;yZ=nh;break L5039}}}while(0);g[18074]=0.0;hG=gv+2|0;c[13898]=hG;yY=hG;yZ=nh}}while(0);if((yY|0)>=(yZ|0)){break}hG=c[1054]|0;L5099:do{if((a[hG+(yY*40&-1)|0]&1)!=0){gH=c[hG+(yY*40&-1)+36>>2]|0;hp=hG+(yY*40&-1)+32|0;hR=c[10036]|0;nd=0;while(1){if((nd|0)>=(gH|0)){break}if((a[hR+((c[hp>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{break L5099}}if((nd|0)==1){break L5037}}}while(0);hG=is(V)|0;hp=c[hG>>2]|0;if((hp|0)==1){zk=+(c[hG+8>>2]|0)}else if((hp|0)==2){zk=+h[hG+8>>3]}else if((hp|0)==3){zk=+uz(c[hG+8>>2]|0,0)}else{b7=3802;break L27}if((c[fj>>2]|0)==3){uu(c[fk>>2]|0);c[fj>>2]=1}m3=zk;g[178]=m3;hG=c[13898]|0;L5114:do{if((c[8272]|0)>(hG|0)){hp=c[1054]|0;if((a[hp+(hG*40&-1)|0]&1)==0){break}hR=c[hp+(hG*40&-1)+36>>2]|0;gH=hp+(hG*40&-1)+32|0;hp=c[10036]|0;hK=0;while(1){if((hK|0)>=(hR|0)){break}if((a[hp+((c[gH>>2]|0)+hK|0)|0]|0)==(a[hK+148464|0]|0)){hK=hK+1|0}else{break L5114}}if((hK|0)!=1){break}c[13898]=hG+1;gH=is(X)|0;hp=c[gH>>2]|0;if((hp|0)==1){zl=+(c[gH+8>>2]|0)}else if((hp|0)==2){zl=+h[gH+8>>3]}else if((hp|0)==3){zl=+uz(c[gH+8>>2]|0,0)}else{b7=3815;break L27}if((c[fl>>2]|0)==3){uu(c[fm>>2]|0);c[fl>>2]=1}g[38]=zl;break L5037}}while(0);g[38]=m3}else{b7=3756}}while(0);if((b7|0)==3756){b7=0;g[178]=1.0;g[38]=1.0}if(+g[178]<=0.0|+g[38]<=0.0){b7=3821;break L27}}else if((gD|0)==85){zm=gv+1|0;c[13898]=zm;nh=gw+(zm*40&-1)+36|0;hF=c[nh>>2]|0;gF=gw+(zm*40&-1)+32|0;hG=c[gF>>2]|0;gH=gw+(zm*40&-1)|0;hp=(a[gH]&1)==0;hR=(hF|0)>0;nd=21680;hB=116616;L5135:while(1){L5137:do{if(!hp){if(hR){gy=0;gz=0;gG=hG;while(1){gC=a[hB+gy|0]|0;if(gC<<24>>24==(a[gB+(gy+gG|0)|0]|0)){zn=gG;zo=gz}else{if(gC<<24>>24!=36){break L5137}zn=gG-1|0;zo=1}zp=gy+1|0;if((zp|0)<(zo+hF|0)){gy=zp;gz=zo;gG=zn}else{break}}if((zo|0)==0){zq=zp}else{zr=nd;break L5135}}else{zq=0}gG=a[hB+zq|0]|0;if((gG<<24>>24|0)==36|(gG<<24>>24|0)==0){zr=nd;break L5135}}}while(0);gG=nd+8|0;gz=c[gG>>2]|0;if((gz|0)==0){zr=gG;break}else{nd=gG;hB=gz}}hB=c[zr+4>>2]|0;if((hB|0)==7){nd=gv+2|0;c[13898]=nd;hF=c[8272]|0;if((nd|0)<(hF|0)){zs=nd;zt=hF;zu=gw}else{break}while(1){hF=(a[zu+(zs*40&-1)|0]&1)==0;if(hF){b7=5284;break L27}nd=c[zu+(zs*40&-1)+36>>2]|0;hG=zu+(zs*40&-1)+32|0;hR=c[10036]|0;hp=0;while(1){if((hp|0)>=(nd|0)){b7=3937;break}if((a[hR+((c[hG>>2]|0)+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{break}}if((b7|0)==3937){b7=0;if((hp|0)==1){break L44}}L5161:do{if((zt|0)>(zs|0)){gz=0;while(1){if((gz|0)>=(nd|0)){break}if((a[hR+((c[hG>>2]|0)+gz|0)|0]|0)==(a[gz+130496|0]|0)){gz=gz+1|0}else{b7=3943;break L5161}}if((gz|0)!=4){b7=3943;break}c[13898]=zs+1;dl(49672,0);gG=(c[13898]|0)-1|0;c[13898]=gG;zv=gG}else{b7=3943}}while(0);L5167:do{if((b7|0)==3943){b7=0;hR=c[hG>>2]|0;if(!((nd|0)>0&(hF^1))){b7=5285;break L27}hp=c[10036]|0;gG=0;gy=0;hK=hR;while(1){if((a[gG+212296|0]|0)==(a[hp+(gG+hK|0)|0]|0)){zw=hK;zx=gy}else{if((gG|0)!=3){zy=0;zz=0;zA=hR;break}zw=hK-1|0;zx=1}gC=gG+1|0;if((gC|0)<(zx+nd|0)){gG=gC;gy=zx;hK=zw}else{b7=3949;break}}do{if((b7|0)==3949){b7=0;if((zx|0)==0){if(!((gG|0)==5|(gG|0)==2)){zy=0;zz=0;zA=hR;break}}hK=zs+1|0;c[13898]=hK;L5181:do{if((a[zu+(hK*40&-1)|0]&1)!=0){gy=c[10810]|0;if((gy|0)==0){zv=hK;break L5167}gz=(zt|0)>(hK|0);gC=zu+(hK*40&-1)+36|0;hS=zu+(hK*40&-1)+32|0;ho=gy;L5184:while(1){ne=c[ho+4>>2]|0;L5186:do{if(gz){gE=c[gC>>2]|0;ng=0;while(1){if((ng|0)>=(gE|0)){break}if((a[hp+((c[hS>>2]|0)+ng|0)|0]|0)==(a[ne+ng|0]|0)){ng=ng+1|0}else{break L5186}}if((a[ne+ng|0]|0)==0){b7=3959;break L5184}}}while(0);ne=c[ho>>2]|0;if((ne|0)==0){zB=gy;break}else{ho=ne}}do{if((b7|0)==3959){b7=0;if((a[ho+8|0]&1)!=0){zB=gy;break}if((c[ho+16>>2]|0)==1){break L5181}else{zB=gy}}}while(0);L5196:while(1){gy=c[zB+4>>2]|0;L5198:do{if(gz){ho=c[gC>>2]|0;gx=0;while(1){if((gx|0)>=(ho|0)){break}if((a[hp+((c[hS>>2]|0)+gx|0)|0]|0)==(a[gy+gx|0]|0)){gx=gx+1|0}else{break L5198}}if((a[gy+gx|0]|0)==0){break L5196}}}while(0);gy=c[zB>>2]|0;if((gy|0)==0){zv=hK;break L5167}else{zB=gy}}if((a[zB+8|0]&1)!=0){zv=hK;break L5167}if((c[zB+16>>2]|0)!=2){zv=hK;break L5167}}}while(0);hK=is(T)|0;hS=c[hK>>2]|0;if((hS|0)==1){zC=+(c[hK+8>>2]|0)}else if((hS|0)==2){zC=+h[hK+8>>3]}else if((hS|0)==3){zC=+uz(c[hK+8>>2]|0,0)}else{b7=3974;break L27}if((c[fr>>2]|0)==3){uu(c[fs>>2]|0);c[fr>>2]=1}h[6214]=zC;hK=(c[13898]|0)-1|0;c[13898]=hK;zv=hK;break L5167}}while(0);while(1){if((a[zy+211736|0]|0)==(a[hp+(zy+zA|0)|0]|0)){zD=zA;zE=zz}else{if((zy|0)!=4){b7=5286;break L27}zD=zA-1|0;zE=1}hR=zy+1|0;if((hR|0)<(zE+nd|0)){zy=hR;zz=zE;zA=zD}else{break}}if((zE|0)==0){if(!((zy|0)==5|(zy|0)==3)){b7=5287;break L27}}zF=zs+1|0;c[13898]=zF;do{if((zt|0)>(zF|0)){hR=(a[zu+(zF*40&-1)|0]&1)==0;if(hR){b7=5280;break L27}gG=c[zu+(zF*40&-1)+36>>2]|0;hK=zu+(zF*40&-1)+32|0;hS=0;while(1){if((hS|0)>=(gG|0)){b7=3991;break}if((a[hp+((c[hK>>2]|0)+hS|0)|0]|0)==(a[hS+150688|0]|0)){hS=hS+1|0}else{break}}if((b7|0)==3991){b7=0;if((hS|0)==2){break}}if(hR){b7=5281;break L27}hK=c[zu+(zF*40&-1)+36>>2]|0;gG=zu+(zF*40&-1)+32|0;gC=0;while(1){if((gC|0)>=(hK|0)){b7=3996;break}if((a[hp+((c[gG>>2]|0)+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{zG=0;break}}if((b7|0)==3996){b7=0;if((gC|0)==1){break}else{zG=0}}while(1){if((zG|0)>=(hK|0)){b7=4e3;break}if((a[hp+((c[gG>>2]|0)+zG|0)|0]|0)==(a[zG+211336|0]|0)){zG=zG+1|0}else{zH=0;break}}do{if((b7|0)==4e3){b7=0;if((zG|0)!=2){zH=0;break}c[12406]=1;zv=zF;break L5167}}while(0);while(1){if((zH|0)>=(hK|0)){break}if((a[hp+((c[gG>>2]|0)+zH|0)|0]|0)==(a[zH+210848|0]|0)){zH=zH+1|0}else{b7=5282;break L27}}if((zH|0)!=2){b7=5283;break L27}c[12406]=2;zv=zF;break L5167}}while(0);c[12406]=0;zv=zF}}while(0);nd=zv+1|0;c[13898]=nd;hF=c[8272]|0;if((nd|0)>=(hF|0)){break L44}zs=nd;zt=hF;zu=c[1054]|0}}else if((hB|0)==1){hF=gv+2|0;c[13898]=hF;nd=c[gw+(hF*40&-1)+36>>2]|0;hG=c[gw+(hF*40&-1)+32>>2]|0;hp=(a[gw+(hF*40&-1)|0]&1)==0;hF=(nd|0)>0;gG=30672;hK=116448;L5256:while(1){L5258:do{if(!hp){if(hF){gC=0;hR=0;hS=hG;while(1){gz=a[hK+gC|0]|0;if(gz<<24>>24==(a[gB+(gC+hS|0)|0]|0)){zI=hS;zJ=hR}else{if(gz<<24>>24!=36){break L5258}zI=hS-1|0;zJ=1}zK=gC+1|0;if((zK|0)<(zJ+nd|0)){gC=zK;hR=zJ;hS=zI}else{break}}if((zJ|0)==0){zL=zK}else{zM=gG;break L5256}}else{zL=0}hS=a[hK+zL|0]|0;if((hS<<24>>24|0)==36|(hS<<24>>24|0)==0){zM=gG;break L5256}}}while(0);hS=gG+8|0;hR=c[hS>>2]|0;if((hR|0)==0){zM=hS;break}else{gG=hS;hK=hR}}hK=c[zM+4>>2]|0;zN=gv+3|0;c[13898]=zN;if((hK|0)==-1){b7=3845;break L27}c[12890]=hK;if((hK|0)!=345){break}hE(43432);if((c[10858]|0)!=0){break}c[10859]=0;break}else if((hB|0)==2){hK=gv+2|0;c[13898]=hK;gG=c[gw+(hK*40&-1)+36>>2]|0;nd=c[gw+(hK*40&-1)+32>>2]|0;hG=(a[gw+(hK*40&-1)|0]&1)==0;hK=(gG|0)>0;hF=30672;hp=116448;L5275:while(1){L5277:do{if(!hG){if(hK){hR=0;hS=0;gC=nd;while(1){gz=a[hp+hR|0]|0;if(gz<<24>>24==(a[gB+(hR+gC|0)|0]|0)){zO=gC;zP=hS}else{if(gz<<24>>24!=36){break L5277}zO=gC-1|0;zP=1}zQ=hR+1|0;if((zQ|0)<(zP+gG|0)){hR=zQ;hS=zP;gC=zO}else{break}}if((zP|0)==0){zR=zQ}else{zS=hF;break L5275}}else{zR=0}gC=a[hp+zR|0]|0;if((gC<<24>>24|0)==36|(gC<<24>>24|0)==0){zS=hF;break L5275}}}while(0);gC=hF+8|0;hS=c[gC>>2]|0;if((hS|0)==0){zS=gC;break}else{hF=gC;hp=hS}}hp=c[zS+4>>2]|0;zT=gv+3|0;c[13898]=zT;if((hp|0)==-1){b7=3860;break L27}if((hp&4|0)!=0){b7=5275;break L27}if((hp|0)==392|(hp|0)==368){b7=5276;break L27}c[10058]=hp;if((hp|0)!=345){break}hE(43400);if((c[10850]|0)!=0){break}c[10851]=0;break}else if((hB|0)==3){jr(43280);break}else if((hB|0)==4){hJ(49488,c[12372]|0,c[12373]|0,c[12374]|0,49504);break}else if((hB|0)==5){hp=gv+2|0;c[13898]=hp;L5298:do{if((hp|0)<(c[8272]|0)){L5300:do{if((a[gw+(hp*40&-1)|0]&1)!=0){hF=c[gw+(hp*40&-1)+36>>2]|0;gG=gw+(hp*40&-1)+32|0;nd=0;while(1){if((nd|0)>=(hF|0)){break}if((a[gB+((c[gG>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{break L5300}}if((nd|0)==1){b7=3885;break L5298}}}while(0);gG=is(U)|0;hF=c[gG>>2]|0;if((hF|0)==1){zU=+(c[gG+8>>2]|0)}else if((hF|0)==2){zU=+h[gG+8>>3]}else if((hF|0)==3){zU=+uz(c[gG+8>>2]|0,0)}else{b7=3879;break L27}if((c[fp>>2]|0)==3){uu(c[fq>>2]|0);c[fp>>2]=1}gG=~~zU;if((gG|0)<1){b7=3884;break L27}zV=gG;zW=c[10824]|0;b7=3888}else{b7=3885}}while(0);L5316:do{if((b7|0)==3885){b7=0;hp=c[10824]|0;if((hp|0)==0){zX=1;zY=0;zZ=0;b7=3892;break}else{z_=1;z$=hp}while(1){if((c[z$+4>>2]|0)!=(z_|0)){zV=z_;zW=hp;b7=3888;break L5316}gG=z_+1|0;hF=c[z$>>2]|0;if((hF|0)==0){zV=gG;zW=hp;b7=3888;break}else{z_=gG;z$=hF}}}}while(0);L5321:do{if((b7|0)==3888){b7=0;if((zW|0)==0){zX=zV;zY=0;zZ=0;b7=3892;break}else{z0=zW;z1=0}while(1){z2=c[z0+4>>2]|0;if((zV|0)<=(z2|0)){break}hp=c[z0>>2]|0;if((hp|0)==0){zX=zV;zY=0;zZ=z0;b7=3892;break L5321}else{z1=z0;z0=hp}}if((zV|0)==(z2|0)){z3=z0}else{zX=zV;zY=z0;zZ=z1;b7=3892}}}while(0);if((b7|0)==3892){b7=0;hp=ut(120)|0;if((hp|0)==0){gk();hF=ut(120)|0;if((hF|0)==0){b7=3894;break L27}else{z4=hF}}else{z4=hp}hp=z4;hF=z4+8|0;c[hF>>2]=-1;c[z4+12>>2]=0;gG=z4+16|0;c[gG>>2]=c[12872];c[gG+4>>2]=c[12873];c[gG+8>>2]=c[12874];c[gG+12>>2]=c[12875];c[gG+16>>2]=c[12876];c[gG+20>>2]=c[12877];c[gG+24>>2]=c[12878];c[gG+28>>2]=c[12879];c[gG+32>>2]=c[12880];c[gG+36>>2]=c[12881];c[gG+40>>2]=c[12882];c[gG+44>>2]=c[12883];c[gG+48>>2]=c[12884];c[gG+52>>2]=c[12885];c[z4+72>>2]=1;h[z4+80>>3]=0.0;c[z4+88>>2]=0;h[z4+96>>3]=15.0;h[z4+104>>3]=90.0;c[z4+112>>2]=0;if((zZ|0)==0){c[10824]=hp}else{c[zZ>>2]=hp}c[hF>>2]=zX;c[z4+4>>2]=zX;c[z4>>2]=zY;z3=hp}hp=c[13898]|0;L5337:do{if((hp|0)<(c[8272]|0)){hF=c[1054]|0;L5339:do{if((a[hF+(hp*40&-1)|0]&1)!=0){gG=c[hF+(hp*40&-1)+36>>2]|0;hK=hF+(hp*40&-1)+32|0;hG=c[10036]|0;hS=0;while(1){if((hS|0)>=(gG|0)){b7=3904;break}if((a[hG+((c[hK>>2]|0)+hS|0)|0]|0)==(a[hS+103664|0]|0)){hS=hS+1|0}else{break}}if((b7|0)==3904){b7=0;if((hS|0)==1){b7=3905;break L5337}}if((gG|0)<=0){break}nd=0;gC=0;hR=c[hK>>2]|0;while(1){if((a[nd+184456|0]|0)==(a[hG+(nd+hR|0)|0]|0)){z5=hR;z6=gC}else{if((nd|0)!=3){break L5339}z5=hR-1|0;z6=1}gz=nd+1|0;if((gz|0)<(z6+gG|0)){nd=gz;gC=z6;hR=z5}else{break}}if((z6|0)==0){if(!((nd|0)==2|(nd|0)==7)){break}}c[z3+8>>2]=-1;c[z3+12>>2]=0;hR=z3+16|0;c[hR>>2]=c[12872];c[hR+4>>2]=c[12873];c[hR+8>>2]=c[12874];c[hR+12>>2]=c[12875];c[hR+16>>2]=c[12876];c[hR+20>>2]=c[12877];c[hR+24>>2]=c[12878];c[hR+28>>2]=c[12879];c[hR+32>>2]=c[12880];c[hR+36>>2]=c[12881];c[hR+40>>2]=c[12882];c[hR+44>>2]=c[12883];c[hR+48>>2]=c[12884];c[hR+52>>2]=c[12885];c[z3+72>>2]=1;h[z3+80>>3]=0.0;c[z3+88>>2]=0;h[z3+96>>3]=15.0;h[z3+104>>3]=90.0;c[z3+112>>2]=0;c[13898]=(c[13898]|0)+1;break L5337}}while(0);hL(z3+8|0,0)}else{b7=3905}}while(0);if((b7|0)==3905){b7=0;c[z3+8>>2]=-1;c[z3+12>>2]=0;hp=z3+16|0;c[hp>>2]=c[12872];c[hp+4>>2]=c[12873];c[hp+8>>2]=c[12874];c[hp+12>>2]=c[12875];c[hp+16>>2]=c[12876];c[hp+20>>2]=c[12877];c[hp+24>>2]=c[12878];c[hp+28>>2]=c[12879];c[hp+32>>2]=c[12880];c[hp+36>>2]=c[12881];c[hp+40>>2]=c[12882];c[hp+44>>2]=c[12883];c[hp+48>>2]=c[12884];c[hp+52>>2]=c[12885];c[z3+72>>2]=1;h[z3+80>>3]=0.0;c[z3+88>>2]=0;h[z3+96>>3]=15.0;h[z3+104>>3]=90.0;c[z3+112>>2]=0}z7=c[13898]|0;if((z7|0)>=(c[8272]|0)){break}hp=c[1054]|0;if((a[hp+(z7*40&-1)|0]&1)==0){b7=5277;break L27}hF=c[hp+(z7*40&-1)+36>>2]|0;hR=hp+(z7*40&-1)+32|0;hp=c[10036]|0;gC=0;while(1){if((gC|0)>=(hF|0)){break}if((a[hp+((c[hR>>2]|0)+gC|0)|0]|0)==(a[gC+103664|0]|0)){gC=gC+1|0}else{b7=5278;break L27}}if((gC|0)==1){break}else{b7=5279;break L27}}else if((hB|0)==8){c[13898]=gv+2;jx(-2,1);break}else if((hB|0)==6){hR=gv+2|0;c[13898]=gv+3;hp=c[gw+(hR*40&-1)+36>>2]|0;if(!((a[gw+(hR*40&-1)|0]&1)!=0&(hp|0)>0)){break}hF=0;gG=0;hG=c[gw+(hR*40&-1)+32>>2]|0;while(1){if((a[hF+213440|0]|0)==(a[gB+(hF+hG|0)|0]|0)){z8=hG;z9=gG}else{if((hF|0)!=1){break L44}z8=hG-1|0;z9=1}hR=hF+1|0;if((hR|0)<(z9+hp|0)){hF=hR;gG=z9;hG=z8}else{break}}if((z9|0)==0){if(!((hF|0)==0|(hF|0)==6)){break}}dl(50952,0);break}else if((hB|0)==10){uD(ft|0,51296,192);c[9670]=3;hG=c[8272]|0;if((zm|0)>=(hG|0)){break}if((a[gH]&1)==0){break}gG=c[nh>>2]|0;hp=0;while(1){if((hp|0)>=(gG|0)){b7=4013;break}if((a[gB+((c[gF>>2]|0)+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{Aa=0;break}}if((b7|0)==4013){b7=0;if((hp|0)==1){break}else{Aa=0}}while(1){if((Aa|0)>=(gG|0)){b7=4016;break}if((a[gB+((c[gF>>2]|0)+Aa|0)|0]|0)==(a[Aa+198768|0]|0)){Aa=Aa+1|0}else{b7=4017;break}}if((b7|0)==4016){b7=0;if((Aa|0)!=2){b7=4017}}do{if((b7|0)==4017){b7=0;if((gG|0)<=0){break L44}hp=0;nh=0;gH=c[gF>>2]|0;while(1){if((a[hp+116200|0]|0)==(a[gB+(hp+gH|0)|0]|0)){Ab=gH;Ac=nh}else{if((hp|0)!=4){break L44}Ab=gH-1|0;Ac=1}hF=hp+1|0;if((hF|0)<(Ac+gG|0)){hp=hF;nh=Ac;gH=Ab}else{break}}if((Ac|0)!=0){break}if(!((hp|0)==3|(hp|0)==9)){break L44}}}while(0);gG=gv+2|0;c[13898]=gG;if((gG|0)<(hG|0)){Ad=gG;Ae=hG;Af=gw}else{break}while(1){gG=(a[Af+(Ad*40&-1)|0]&1)==0;gF=c[Af+(Ad*40&-1)+36>>2]|0;gH=Af+(Ad*40&-1)+32|0;L5408:do{if(gG){Ag=c[gH>>2]|0;b7=4064}else{nh=c[10036]|0;hF=0;while(1){if((hF|0)>=(gF|0)){b7=4031;break}gC=c[gH>>2]|0;if((a[nh+(gC+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{Ah=gC;break}}if((b7|0)==4031){b7=0;if((hF|0)==1){break L44}Ah=c[gH>>2]|0}if((gF|0)>0){Ai=0;Aj=0;Ak=Ah}else{Ag=Ah;b7=4064;break}while(1){if((a[Ai+197904|0]|0)==(a[nh+(Ai+Ak|0)|0]|0)){Al=Ak;Am=Aj}else{if((Ai|0)!=5){An=0;Ao=0;Ap=Ah;break}Al=Ak-1|0;Am=1}nd=Ai+1|0;if((nd|0)<(Am+gF|0)){Ai=nd;Aj=Am;Ak=Al}else{b7=4038;break}}do{if((b7|0)==4038){b7=0;if((Am|0)==0){if(!((Ai|0)==4|(Ai|0)==9)){An=0;Ao=0;Ap=Ah;break}}c[9670]=3;c[13898]=Ad+1;break L5408}}while(0);while(1){if((a[An+197448|0]|0)==(a[nh+(An+Ap|0)|0]|0)){Aq=Ap;Ar=Ao}else{if((An|0)!=5){As=0;At=0;Au=Ah;break}Aq=Ap-1|0;Ar=1}hF=An+1|0;if((hF|0)<(Ar+gF|0)){An=hF;Ao=Ar;Ap=Aq}else{b7=4045;break}}do{if((b7|0)==4045){b7=0;if((Ar|0)==0){if(!((An|0)==4|(An|0)==9)){As=0;At=0;Au=Ah;break}}c[9670]=4;c[13898]=Ad+1;break L5408}}while(0);while(1){if((a[As+196808|0]|0)==(a[nh+(As+Au|0)|0]|0)){Av=Au;Aw=At}else{if((As|0)!=4){Ax=0;Ay=0;Az=Ah;break}Av=Au-1|0;Aw=1}hF=As+1|0;if((hF|0)<(Aw+gF|0)){As=hF;At=Aw;Au=Av}else{b7=4054;break}}do{if((b7|0)==4054){b7=0;if((Aw|0)==0){if(!((As|0)==3|(As|0)==10)){Ax=0;Ay=0;Az=Ah;break}}c[9670]=1;c[13898]=Ad+1;break L5408}}while(0);while(1){if((a[Ax+196176|0]|0)==(a[nh+(Ax+Az|0)|0]|0)){AA=Az;AB=Ay}else{if((Ax|0)!=7){Ag=Ah;b7=4064;break L5408}AA=Az-1|0;AB=1}hF=Ax+1|0;if((hF|0)<(AB+gF|0)){Ax=hF;Ay=AB;Az=AA}else{break}}if((AB|0)==0){if(!((Ax|0)==6|(Ax|0)==13)){Ag=Ah;b7=4064;break}}c[9670]=2;c[13898]=Ad+1}}while(0);L5461:do{if((b7|0)==4064){b7=0;gH=(Ae|0)>(Ad|0);L5463:do{if(gH){if(gG){break L44}hp=c[10036]|0;nh=0;while(1){if((nh|0)>=(gF|0)){break}if((a[hp+(nh+Ag|0)|0]|0)==(a[nh+195688|0]|0)){nh=nh+1|0}else{break L5463}}if((nh|0)!=3){break}AC=Ad+1|0;c[13898]=AC;if((a[Af+(AC*40&-1)|0]&1)!=0){b7=4079;break L27}hp=is(O)|0;hF=c[hp>>2]|0;if((hF|0)==1){AD=+(c[hp+8>>2]|0)}else if((hF|0)==2){AD=+h[hp+8>>3]}else if((hF|0)==3){AD=+uz(c[hp+8>>2]|0,0)}else{b7=4075;break L27}if((c[fu>>2]|0)==3){uu(c[fv>>2]|0);c[fu>>2]=1}c[9671]=~~AD;break L5461}}while(0);hp=(gF|0)>0;L5481:do{if(hp&(gG^1)){hF=c[10036]|0;nd=0;gC=0;hR=Ag;while(1){if((a[nd+125568|0]|0)==(a[hF+(nd+hR|0)|0]|0)){AE=hR;AF=gC}else{if((nd|0)!=2){break L5481}AE=hR-1|0;AF=1}hK=nd+1|0;if((hK|0)<(AF+gF|0)){nd=hK;gC=AF;hR=AE}else{break}}if((AF|0)==0){if(!((nd|0)==1|(nd|0)==5)){break}}c[fy>>2]=c[9720];c[fy+4>>2]=c[38884>>2];c[fy+8>>2]=c[38888>>2];c[fy+12>>2]=c[38892>>2];c[fy+16>>2]=c[38896>>2];c[fy+20>>2]=c[38900>>2];c[fy+24>>2]=c[38904>>2];c[fy+28>>2]=c[38908>>2];c[fy+32>>2]=c[38912>>2];c[fy+36>>2]=c[38916>>2];jq(S);c[9702]=c[fz>>2];c[38812>>2]=c[fz+4>>2];c[38816>>2]=c[fz+8>>2];c[38820>>2]=c[fz+12>>2];c[9720]=c[fy>>2];c[38884>>2]=c[fy+4>>2];c[38888>>2]=c[fy+8>>2];c[38892>>2]=c[fy+12>>2];c[38896>>2]=c[fy+16>>2];c[38900>>2]=c[fy+20>>2];c[38904>>2]=c[fy+24>>2];c[38908>>2]=c[fy+28>>2];c[38912>>2]=c[fy+32>>2];c[38916>>2]=c[fy+36>>2];c[9700]=c[16630];break L5461}}while(0);L5494:do{if(gH){if(gG){break L44}hR=c[10036]|0;gC=0;while(1){if((gC|0)>=(gF|0)){break}if((a[hR+(gC+Ag|0)|0]|0)==(a[gC+128296|0]|0)){gC=gC+1|0}else{b7=4095;break L5494}}if((gC|0)!=2){b7=4095;break}if((c[9670]|0)!=4){break L44}}else{b7=4095}}while(0);if((b7|0)==4095){b7=0;if(gG){break L44}if(hp){gH=c[10036]|0;hR=0;nd=0;hF=Ag;while(1){if((a[hR+128400|0]|0)==(a[gH+(hR+hF|0)|0]|0)){AG=hF;AH=nd}else{if((hR|0)!=5){break L44}AG=hF-1|0;AH=1}AI=hR+1|0;if((AI|0)<(AH+gF|0)){hR=AI;nd=AH;hF=AG}else{break}}if((AH|0)==0){AJ=AI;b7=4103}else{AK=1}}else{AJ=0;b7=4103}if((b7|0)==4103){b7=0;AK=(AJ|0)==5|(AJ|0)==10}if(!(AK&(c[9670]|0)==4)){break L44}}c[13898]=Ad+1;hF=is(R)|0;nd=c[hF>>2]|0;if((nd|0)==1){AL=+(c[hF+8>>2]|0)}else if((nd|0)==2){AL=+h[hF+8>>3]}else if((nd|0)==3){AL=+uz(c[hF+8>>2]|0,0)}else{b7=4110;break L27}if((c[fw>>2]|0)==3){uu(c[fx>>2]|0);c[fw>>2]=1}h[4840]=AL;if(AL>0.0){break}h[4840]=1.0}}while(0);gF=c[13898]|0;gG=c[8272]|0;if((gF|0)>=(gG|0)){break L44}Ad=gF;Ae=gG;Af=c[1054]|0}}else if((hB|0)==9){hG=gv+2|0;c[13898]=hG;L5530:do{if((hG|0)<(c[8272]|0)){if((a[gw+(hG*40&-1)|0]&1)==0){break}gG=c[gw+(hG*40&-1)+36>>2]|0;gF=gw+(hG*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(gG|0)){b7=4120;break}nd=c[gF>>2]|0;if((a[gB+(nd+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{AM=nd;break}}if((b7|0)==4120){b7=0;if((hF|0)==1){b7=4129;break}AM=c[gF>>2]|0}if((gG|0)>0){AN=0;AO=0;AP=AM}else{break}while(1){if((a[AN+184456|0]|0)==(a[gB+(AN+AP|0)|0]|0)){AQ=AP;AR=AO}else{if((AN|0)!=3){AS=0;AT=0;AU=AM;break}AQ=AP-1|0;AR=1}nd=AN+1|0;if((nd|0)<(AR+gG|0)){AN=nd;AO=AR;AP=AQ}else{b7=4127;break}}if((b7|0)==4127){b7=0;if((AR|0)!=0){b7=4129;break}if((AN|0)==2|(AN|0)==7){b7=4129;break}else{AS=0;AT=0;AU=AM}}while(1){if((a[AS+209024|0]|0)==(a[gB+(AS+AU|0)|0]|0)){AV=AU;AW=AT}else{if((AS|0)!=1){break L5530}AV=AU-1|0;AW=1}gF=AS+1|0;if((gF|0)<(AW+gG|0)){AS=gF;AT=AW;AU=AV}else{break}}if((AW|0)==0){if(!((AS|0)==0|(AS|0)==10)){break}}a[30080]=1}else{b7=4129}}while(0);if((b7|0)==4129){b7=0;a[30080]=0}c[13898]=gv+3;break}else if((hB|0)==11){hG=gv+2|0;c[13898]=hG;gG=c[8272]|0;gF=(hG|0)<(gG|0);L5563:do{if(gF){if((a[gw+(hG*40&-1)|0]&1)==0){AX=hG;AY=gG;AZ=gw;break}hF=c[gw+(hG*40&-1)+36>>2]|0;nd=gw+(hG*40&-1)+32|0;hR=0;while(1){if((hR|0)>=(hF|0)){break}if((a[gB+((c[nd>>2]|0)+hR|0)|0]|0)==(a[hR+103664|0]|0)){hR=hR+1|0}else{b7=4145;break L5563}}if((hR|0)==1){b7=4144}else{b7=4145}}else{b7=4144}}while(0);if((b7|0)==4144){b7=0;uE(cW|0,0,3);hB=56248;c[hB>>2]=0;c[hB+4>>2]=0;h[7032]=1.5;a[56264]=1;a[56265]=a[cW]|0;a[56266|0]=a[cW+1|0]|0;a[56267|0]=a[cW+2|0]|0;c[14067]=6;c[14068]=252;c[14069]=0;h[7035]=1.0;c[14072]=1;c[14073]=0;b7=4145}if((b7|0)==4145){b7=0;if(gF){AX=hG;AY=gG;AZ=gw}else{break}}while(1){hB=(a[AZ+(AX*40&-1)|0]&1)==0;nd=c[AZ+(AX*40&-1)+36>>2]|0;hF=AZ+(AX*40&-1)+32|0;L5576:do{if(hB){A_=c[hF>>2]|0;b7=4176}else{gH=c[10036]|0;hp=0;while(1){if((hp|0)>=(nd|0)){b7=4151;break}nh=c[hF>>2]|0;if((a[gH+(nh+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{A$=nh;break}}if((b7|0)==4151){b7=0;if((hp|0)==1){break L44}A$=c[hF>>2]|0}if((nd|0)>0){A0=0;A1=0;A2=A$}else{A_=A$;b7=4176;break}while(1){if((a[A0+207888|0]|0)==(a[gH+(A0+A2|0)|0]|0)){A3=A2;A4=A1}else{if((A0|0)!=5){A5=0;A6=0;A7=A$;break}A3=A2-1|0;A4=1}nh=A0+1|0;if((nh|0)<(A4+nd|0)){A0=nh;A1=A4;A2=A3}else{b7=4158;break}}do{if((b7|0)==4158){b7=0;if((A4|0)==0){if(!((A0|0)==4|(A0|0)==10)){A5=0;A6=0;A7=A$;break}}a[56264]=0;c[13898]=AX+1;break L5576}}while(0);while(1){if((a[A5+207040|0]|0)==(a[gH+(A5+A7|0)|0]|0)){A8=A7;A9=A6}else{if((A5|0)!=3){Ba=0;Bb=0;Bc=A$;break}A8=A7-1|0;A9=1}hp=A5+1|0;if((hp|0)<(A9+nd|0)){A5=hp;A6=A9;A7=A8}else{b7=4165;break}}do{if((b7|0)==4165){b7=0;if((A9|0)==0){if(!((A5|0)==2|(A5|0)==8)){Ba=0;Bb=0;Bc=A$;break}}a[56264]=1;c[13898]=AX+1;break L5576}}while(0);while(1){if((a[Ba+206448|0]|0)==(a[gH+(Ba+Bc|0)|0]|0)){Bd=Bc;Be=Bb}else{if((Ba|0)!=5){A_=A$;b7=4176;break L5576}Bd=Bc-1|0;Be=1}hp=Ba+1|0;if((hp|0)<(Be+nd|0)){Ba=hp;Bb=Be;Bc=Bd}else{break}}if((Be|0)!=0){b7=4182;break}if((Ba|0)==4|(Ba|0)==9){b7=4182}else{A_=A$;b7=4176}}}while(0);L5616:do{if((b7|0)==4176){b7=0;L5618:do{if((AY|0)>(AX|0)){if(hB){b7=5292;break L27}hF=c[10036]|0;hR=0;while(1){if((hR|0)>=(nd|0)){b7=4181;break}if((a[hF+(hR+A_|0)|0]|0)==(a[hR+205624|0]|0)){hR=hR+1|0}else{Bf=0;break}}if((b7|0)==4181){b7=0;if((hR|0)==2){b7=4182;break L5616}else{Bf=0}}while(1){if((Bf|0)>=(nd|0)){break}if((a[hF+(Bf+A_|0)|0]|0)==(a[Bf+205008|0]|0)){Bf=Bf+1|0}else{break L5618}}if((Bf|0)!=5){break}c[13898]=AX+1;c[14062]=0;hF=is(L)|0;hR=c[hF>>2]|0;if((hR|0)==1){Bg=+(c[hF+8>>2]|0)}else if((hR|0)==2){Bg=+h[hF+8>>3]}else if((hR|0)==3){Bg=+uz(c[hF+8>>2]|0,0)}else{b7=4197;break L27}if((c[fA>>2]|0)==3){uu(c[fB>>2]|0);c[fA>>2]=1}h[7032]=Bg;break L5616}}while(0);if(!((nd|0)>0&(hB^1))){b7=5293;break L27}hF=c[10036]|0;hR=0;gC=0;gH=A_;while(1){if((a[hR+204616|0]|0)==(a[hF+(hR+gH|0)|0]|0)){Bh=gH;Bi=gC}else{if((hR|0)!=4){Bj=0;Bk=0;Bl=A_;break}Bh=gH-1|0;Bi=1}hp=hR+1|0;if((hp|0)<(Bi+nd|0)){hR=hp;gC=Bi;gH=Bh}else{b7=4207;break}}do{if((b7|0)==4207){b7=0;if((Bi|0)==0){if(!((hR|0)==8|(hR|0)==3)){Bj=0;Bk=0;Bl=A_;break}}c[13898]=AX+1;gH=is(K)|0;gC=c[gH>>2]|0;if((gC|0)==1){Bm=+(c[gH+8>>2]|0)}else if((gC|0)==2){Bm=+h[gH+8>>3]}else if((gC|0)==3){Bm=+uz(c[gH+8>>2]|0,0)}else{b7=4213;break L27}if((c[fE>>2]|0)==3){uu(c[fF>>2]|0);c[fE>>2]=1}h[7032]=Bm;if(Bm<0.0|Bm>1.0){b7=4217;break L27}c[14062]=1;break L5616}}while(0);while(1){if((a[Bj+204096|0]|0)==(a[hF+(Bj+Bl|0)|0]|0)){Bn=Bl;Bo=Bk}else{if((Bj|0)!=6){Bp=0;Bq=0;Br=A_;break}Bn=Bl-1|0;Bo=1}hR=Bj+1|0;if((hR|0)<(Bo+nd|0)){Bj=hR;Bk=Bo;Bl=Bn}else{b7=4223;break}}do{if((b7|0)==4223){b7=0;if((Bo|0)==0){if(!((Bj|0)==12|(Bj|0)==5)){Bp=0;Bq=0;Br=A_;break}}c[13898]=AX+1;c[14068]=252;break L5616}}while(0);while(1){if((a[Bp+203848|0]|0)==(a[hF+(Bp+Br|0)|0]|0)){Bs=Br;Bt=Bq}else{if((Bp|0)!=7){Bu=0;Bv=0;Bw=A_;break}Bs=Br-1|0;Bt=1}hR=Bp+1|0;if((hR|0)<(Bt+nd|0)){Bp=hR;Bq=Bt;Br=Bs}else{b7=4230;break}}do{if((b7|0)==4230){b7=0;if((Bt|0)==0){if(!((Bp|0)==11|(Bp|0)==6)){Bu=0;Bv=0;Bw=A_;break}}c[13898]=AX+1;c[14068]=257;break L5616}}while(0);while(1){if((a[Bu+203640|0]|0)==(a[hF+(Bu+Bw|0)|0]|0)){Bx=Bw;By=Bv}else{if((Bu|0)!=3){Bz=0;BA=0;BB=A_;break}Bx=Bw-1|0;By=1}hR=Bu+1|0;if((hR|0)<(By+nd|0)){Bu=hR;Bv=By;Bw=Bx}else{b7=4237;break}}do{if((b7|0)==4237){b7=0;if((By|0)==0){if(!((Bu|0)==10|(Bu|0)==2)){Bz=0;BA=0;BB=A_;break}}c[13898]=AX+1;hR=is(N)|0;gH=c[hR>>2]|0;if((gH|0)==1){BC=+(c[hR+8>>2]|0)}else if((gH|0)==2){BC=+h[hR+8>>3]}else if((gH|0)==3){BC=+uz(c[hR+8>>2]|0,0)}else{b7=4243;break L27}if((c[fC>>2]|0)==3){uu(c[fD>>2]|0);c[fC>>2]=1}h[7035]=BC;if(BC<0.0){b7=4247;break L27}else{break L5616}}}while(0);while(1){if((a[Bz+203048|0]|0)==(a[hF+(Bz+BB|0)|0]|0)){BD=BB;BE=BA}else{if((Bz|0)!=3){BF=0;BG=0;BH=A_;break}BD=BB-1|0;BE=1}hR=Bz+1|0;if((hR|0)<(BE+nd|0)){Bz=hR;BA=BE;BB=BD}else{b7=4252;break}}do{if((b7|0)==4252){b7=0;if((BE|0)==0){if(!((Bz|0)==6|(Bz|0)==2)){BF=0;BG=0;BH=A_;break}}hR=AX+1|0;c[13898]=hR;if((AY|0)<=(hR|0)){b7=5288;break L27}if((a[AZ+(hR*40&-1)|0]&1)==0){b7=5289;break L27}gH=c[AZ+(hR*40&-1)+36>>2]|0;gC=AZ+(hR*40&-1)+32|0;hR=0;while(1){if((hR|0)>=(gH|0)){b7=4259;break}if((a[hF+((c[gC>>2]|0)+hR|0)|0]|0)==(a[hR+150208|0]|0)){hR=hR+1|0}else{BI=0;b7=4261;break}}do{if((b7|0)==4259){b7=0;if((hR|0)!=3){BI=0;b7=4261;break}c[14072]=0}}while(0);L5721:do{if((b7|0)==4261){while(1){b7=0;if((BI|0)>=(gH|0)){b7=4263;break}if((a[hF+((c[gC>>2]|0)+BI|0)|0]|0)==(a[BI+202352|0]|0)){BI=BI+1|0;b7=4261}else{BJ=0;break}}do{if((b7|0)==4263){b7=0;if((BI|0)!=1){BJ=0;break}c[14072]=2;break L5721}}while(0);while(1){if((BJ|0)>=(gH|0)){b7=4267;break}if((a[hF+((c[gC>>2]|0)+BJ|0)|0]|0)==(a[BJ+108208|0]|0)){BJ=BJ+1|0}else{BK=0;break}}do{if((b7|0)==4267){b7=0;if((BJ|0)!=2){BK=0;break}c[14072]=3;break L5721}}while(0);while(1){if((BK|0)>=(gH|0)){break}if((a[hF+((c[gC>>2]|0)+BK|0)|0]|0)==(a[BK+201e3|0]|0)){BK=BK+1|0}else{b7=5290;break L27}}if((BK|0)!=4){b7=5291;break L27}c[14072]=1}}while(0);c[13898]=AX+2;break L5616}}while(0);while(1){if((a[BF+199432|0]|0)==(a[hF+(BF+BH|0)|0]|0)){BL=BH;BM=BG}else{if((BF|0)!=2){BN=0;BO=0;BP=A_;break}BL=BH-1|0;BM=1}gC=BF+1|0;if((gC|0)<(BM+nd|0)){BF=gC;BG=BM;BH=BL}else{b7=4279;break}}do{if((b7|0)==4279){b7=0;if((BM|0)==0){if(!((BF|0)==1|(BF|0)==6)){BN=0;BO=0;BP=A_;break}}a[56292]=1;c[13898]=AX+1;break L5616}}while(0);while(1){if((a[BN+199064|0]|0)==(a[hF+(BN+BP|0)|0]|0)){BQ=BP;BR=BO}else{if((BN|0)!=2){b7=5294;break L27}BQ=BP-1|0;BR=1}gC=BN+1|0;if((gC|0)<(BR+nd|0)){BN=gC;BO=BR;BP=BQ}else{break}}if((BR|0)==0){if(!((BN|0)==1|(BN|0)==8)){b7=5295;break L27}}a[56292]=0;c[13898]=AX+1}}while(0);if((b7|0)==4182){b7=0;c[13898]=AX+1;nd=is(M)|0;hB=c[nd>>2]|0;if((hB|0)==1){BS=+(c[nd+8>>2]|0)}else if((hB|0)==2){BS=+h[nd+8>>3]}else if((hB|0)==3){BS=+uz(c[nd+8>>2]|0,0)}else{b7=4186;break L27}if((c[fG>>2]|0)==3){uu(c[fH>>2]|0);c[fG>>2]=1}c[14067]=~~BS-1}nd=c[13898]|0;hB=c[8272]|0;if((nd|0)>=(hB|0)){break L44}AX=nd;AY=hB;AZ=c[1054]|0}}else{b7=4290;break L27}}else if((gD|0)==84){c[13898]=gv+1;a[46752]=1}else if((gD|0)==86){gG=gv+1|0;c[13898]=gG;hG=c[3526]|0;if((hG|0)==0){BT=gG}else{az(hG|0);c[3526]=0;BT=c[13898]|0}L5778:do{if((BT|0)<(c[8272]|0)){hG=c[1054]|0;L5780:do{if((a[hG+(BT*40&-1)|0]&1)!=0){gG=c[hG+(BT*40&-1)+36>>2]|0;gF=hG+(BT*40&-1)+32|0;hB=c[10036]|0;nd=0;while(1){if((nd|0)>=(gG|0)){break}if((a[hB+((c[gF>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{break L5780}}if((nd|0)==1){break L5778}}}while(0);a[14176]=1;is(J);a[14176]=0;if((c[fI>>2]|0)!=3){c[13898]=BT;break}hG=c[fJ>>2]|0;if((hG|0)==0){break}gF=bF(hG|0,137896)|0;c[3526]=gF;if((gF|0)==0){b7=4304;break L27}uu(hG)}}while(0);a[14112]=1}else if((gD|0)==87){BU=gv+1|0;c[13898]=BU;if((a[33512]&1)!=0){b7=4308;break L27}L5795:do{if((BU|0)<(c[8272]|0)){L5797:do{if((a[gw+(BU*40&-1)|0]&1)!=0){hG=c[gw+(BU*40&-1)+36>>2]|0;gF=gw+(BU*40&-1)+32|0;hB=0;while(1){if((hB|0)>=(hG|0)){b7=4314;break}if((a[gB+((c[gF>>2]|0)+hB|0)|0]|0)==(a[hB+103664|0]|0)){hB=hB+1|0}else{BV=0;break}}if((b7|0)==4314){b7=0;if((hB|0)==1){break L5795}else{BV=0}}while(1){if((BV|0)>=(hG|0)){break}if((a[gB+((c[gF>>2]|0)+BV|0)|0]|0)==(a[BV+216664|0]|0)){BV=BV+1|0}else{break L5797}}if((BV|0)!=4){break}hz(a[37400]&1);c[13898]=(c[13898]|0)+1;break L44}}while(0);hO(1);c[8026]=0;do{if((a[14080]&1)!=0){if(a[13032]|0){gF=c[(c[3524]|0)+104>>2]|0;if((gF|0)!=0){cS[gF&511]()}a[13032]=0}if(a[14088]|0){cS[c[(c[3524]|0)+44>>2]&511]();a[14088]=0}if((a[14080]&1)==0){break}cS[c[(c[3524]|0)+40>>2]&511]();a[14080]=0;c[10028]=0}}while(0);gF=c[13898]|0;L5823:do{if((c[8272]|0)>(gF|0)){hG=c[1054]|0;if((a[hG+(gF*40&-1)|0]&1)==0){break}hB=c[hG+(gF*40&-1)+36>>2]|0;nd=hG+(gF*40&-1)+32|0;hG=c[10036]|0;gG=0;while(1){if((gG|0)>=(hB|0)){break}if((a[hG+((c[nd>>2]|0)+gG|0)|0]|0)==(a[gG+216216|0]|0)){gG=gG+1|0}else{break L5823}}if((gG|0)!=3){break}hD();c[13898]=(c[13898]|0)+1;break L44}}while(0);c[3524]=0;gF=lz()|0;c[3524]=gF;a[13048]=0;if((gF|0)==0){break L44}cS[c[gF+32>>2]&511]();if((a[37400]&1)==0|(a[13048]|0)==0){break L44}gF=c[m>>2]|0;cf(gF|0,215680,(v=i,i=i+8|0,c[v>>2]=13048,v)|0);break L44}}while(0);ls();a[25280]=0}else if((gD|0)==88){gF=c[8272]|0;BW=gv+1|0;c[13898]=BW;if((BW|0)>=(gF|0)){break}nd=(a[gw+(BW*40&-1)|0]&1)==0;L5837:do{if(nd){BX=1}else{hG=c[gw+(BW*40&-1)+36>>2]|0;hB=gw+(BW*40&-1)+32|0;hF=0;while(1){if((hF|0)>=(hG|0)){break}if((a[gB+((c[hB>>2]|0)+hF|0)|0]|0)==(a[hF+103664|0]|0)){hF=hF+1|0}else{BX=1;break L5837}}BX=(hF|0)!=1}}while(0);hB=c[3524]|0;if(!(BX&(hB|0)!=0)){break}hG=c[gw+(BW*40&-1)+36>>2]|0;gC=c[gw+(BW*40&-1)+32>>2]|0;L5845:do{if(nd){b7=4406}else{gH=(hG|0)>0;L5847:do{if(gH){hR=0;hp=0;nh=gC;while(1){if((a[hR+122880|0]|0)==(a[gB+(hR+nh|0)|0]|0)){BY=nh;BZ=hp}else{if((hR|0)!=3){B_=0;B$=0;B0=gC;b7=4354;break}BY=nh-1|0;BZ=1}hK=hR+1|0;if((hK|0)<(BZ+hG|0)){hR=hK;hp=BZ;nh=BY}else{b7=4352;break}}do{if((b7|0)==4352){b7=0;if((BZ|0)!=0){break}if(!((hR|0)==2|(hR|0)==8)){B_=0;B$=0;B0=gC;b7=4354}}}while(0);do{if((b7|0)==4354){while(1){b7=0;if((a[B_+121536|0]|0)==(a[gB+(B_+B0|0)|0]|0)){B1=B0;B2=B$}else{if((B_|0)!=5){B3=0;break L5847}B1=B0-1|0;B2=1}hR=B_+1|0;if((hR|0)<(B2+hG|0)){B_=hR;B$=B2;B0=B1;b7=4354}else{break}}if((B2|0)!=0){break}if(!((B_|0)==4|(B_|0)==10)){B3=0;break L5847}}}while(0);hR=gv+2|0;c[8272]=(gF|0)<(hR|0)?gF:hR;if((c[hB+156>>2]|0)!=0){b7=4411;break L5845}c[13898]=hR;break L5845}else{B3=0}}while(0);while(1){if((B3|0)>=(hG|0)){b7=4364;break}if((a[gB+(B3+gC|0)|0]|0)==(a[B3+124352|0]|0)){B3=B3+1|0}else{B4=0;b7=4365;break}}if((b7|0)==4364){b7=0;if((B3|0)!=4){B4=0;b7=4365}}do{if((b7|0)==4365){while(1){b7=0;if((B4|0)>=(hG|0)){b7=4367;break}if((a[gB+(B4+gC|0)|0]|0)==(a[B4+223712|0]|0)){B4=B4+1|0;b7=4365}else{B5=0;break}}if((b7|0)==4367){b7=0;if((B4|0)==5){break}else{B5=0}}while(1){if((B5|0)>=(hG|0)){b7=4372;break}if((a[gB+(B5+gC|0)|0]|0)==(a[B5+128096|0]|0)){B5=B5+1|0}else{B6=0;break}}do{if((b7|0)==4372){b7=0;if((B5|0)!=9){B6=0;break}if((c[hB+96>>2]&4096|0)!=0){b7=4411;break L5845}c[13898]=gv+2;hF=is(H)|0;hR=c[hF>>2]|0;if((hR|0)==3){nh=c[hF+8>>2]|0;uz(nh,0)}else if(!((hR|0)==1|(hR|0)==2)){b7=4376;break L27}if((c[fM>>2]|0)!=3){break L5845}uu(c[fN>>2]|0);c[fM>>2]=1;break L5845}}while(0);while(1){if((B6|0)>=(hG|0)){b7=4381;break}if((a[gB+(B6+gC|0)|0]|0)==(a[B6+128296|0]|0)){B6=B6+1|0}else{b7=4382;break}}if((b7|0)==4381){b7=0;if((B6|0)!=2){b7=4382}}L5895:do{if((b7|0)==4382){b7=0;L5897:do{if(gH){hR=0;nh=0;hF=gC;while(1){if((a[hR+128400|0]|0)==(a[gB+(hR+hF|0)|0]|0)){B7=hF;B8=nh}else{if((hR|0)!=5){B9=0;Ca=0;Cb=gC;break}B7=hF-1|0;B8=1}hp=hR+1|0;if((hp|0)<(B8+hG|0)){hR=hp;nh=B8;hF=B7}else{b7=4387;break}}if((b7|0)==4387){b7=0;if((B8|0)!=0){break L5895}if((hR|0)==4|(hR|0)==9){break L5895}else{B9=0;Ca=0;Cb=gC}}while(1){if((a[B9+217752|0]|0)==(a[gB+(B9+Cb|0)|0]|0)){Cc=Cb;Cd=Ca}else{if((B9|0)!=4){Ce=0;b7=4401;break L5897}Cc=Cb-1|0;Cd=1}hF=B9+1|0;if((hF|0)<(Cd+hG|0)){B9=hF;Ca=Cd;Cb=Cc}else{break}}if((Cd|0)!=0){break}if(!((B9|0)==3|(B9|0)==6)){Ce=0;b7=4401}}else{Ce=0;b7=4401}}while(0);if((b7|0)==4401){while(1){b7=0;if((Ce|0)>=(hG|0)){break}if((a[gB+(Ce+gC|0)|0]|0)==(a[Ce+131504|0]|0)){Ce=Ce+1|0;b7=4401}else{b7=4406;break L5845}}if((Ce|0)!=5){b7=4406;break L5845}}if((c[hB+96>>2]&256|0)!=0){b7=4411;break L5845}c[13898]=gv+2;break L5845}}while(0);if((c[hB+96>>2]&2048|0)!=0){b7=4411;break L5845}c[13898]=gv+2;hR=is(I)|0;hF=c[hR>>2]|0;if((hF|0)==3){nh=c[hR+8>>2]|0;uz(nh,0)}else if(!((hF|0)==1|(hF|0)==2)){b7=4392;break L27}if((c[fK>>2]|0)!=3){break L5845}uu(c[fL>>2]|0);c[fK>>2]=1;break L5845}}while(0);gH=gv+3|0;c[8272]=(gF|0)<(gH|0)?gF:gH;if((c[hB+88>>2]|0)!=0){b7=4411;break}c[13898]=gH}}while(0);if((b7|0)==4406){b7=0;if((aY(c[hB>>2]|0,218960)|0)!=0|nd){b7=5296;break L27}else{Cf=0}while(1){if((Cf|0)>=(hG|0)){break}if((a[gB+(Cf+gC|0)|0]|0)==(a[Cf+218488|0]|0)){Cf=Cf+1|0}else{b7=5297;break L27}}if((Cf|0)==5&(gF|0)==4){b7=4411}else{b7=5298;break L27}}if((b7|0)==4411){b7=0;a[13048]=0;cS[c[hB+32>>2]&511]()}c[8272]=gF}else if((gD|0)==89){gC=gv+1|0;c[13898]=gC;hG=c[8272]|0;nd=(gC|0)<(hG|0);L5940:do{if(nd){if((a[gw+(gC*40&-1)|0]&1)==0){Cg=0;Ch=0;Ci=gC;Cj=hG;Ck=gw;b7=4421;break}gH=c[gw+(gC*40&-1)+36>>2]|0;hF=gw+(gC*40&-1)+32|0;nh=0;while(1){if((nh|0)>=(gH|0)){break}if((a[gB+((c[hF>>2]|0)+nh|0)|0]|0)==(a[nh+103664|0]|0)){nh=nh+1|0}else{b7=4420;break L5940}}if((nh|0)==1){b7=4419}else{b7=4420}}else{b7=4419}}while(0);if((b7|0)==4419){b7=0;a[65072]=1;a[65760]=1;a[66448]=1;a[67136]=1;a[67824]=1;a[68512]=1;a[69200]=1;a[69888]=1;a[70576]=1;a[71264]=1;a[71952]=1;b7=4420}if((b7|0)==4420){b7=0;if(nd){Cg=0;Ch=0;Ci=gC;Cj=hG;Ck=gw;b7=4421}else{Cl=0;Cm=0}}L5951:do{if((b7|0)==4421){while(1){b7=0;gF=(a[Ck+(Ci*40&-1)|0]&1)==0;hB=c[Ck+(Ci*40&-1)+36>>2]|0;hF=Ck+(Ci*40&-1)+32|0;L5954:do{if(gF){Cn=c[hF>>2]|0;b7=4620}else{gH=c[10036]|0;hR=0;while(1){if((hR|0)>=(hB|0)){b7=4426;break}hp=c[hF>>2]|0;if((a[gH+(hp+hR|0)|0]|0)==(a[hR+103664|0]|0)){hR=hR+1|0}else{Co=hp;break}}if((b7|0)==4426){b7=0;if((hR|0)==1){Cl=Cg;Cm=Ch;break L5951}Co=c[hF>>2]|0}if((hB|0)>0){Cp=0;Cq=0;Cr=Co}else{Cn=Co;b7=4620;break}while(1){if((a[Cp+116896|0]|0)==(a[gH+(Cp+Cr|0)|0]|0)){Cs=Cr;Ct=Cq}else{if((Cp|0)!=2){Cu=0;Cv=0;Cw=Co;break}Cs=Cr-1|0;Ct=1}hp=Cp+1|0;if((hp|0)<(Ct+hB|0)){Cp=hp;Cq=Ct;Cr=Cs}else{b7=4434;break}}do{if((b7|0)==4434){b7=0;if((Ct|0)==0){if(!((Cp|0)==1|(Cp|0)==4)){Cu=0;Cv=0;Cw=Co;break}}c[16229]=c[16229]&-4|2;c[16401]=c[16401]&-4|2;c[16573]=c[16573]&-4|2;c[16745]=c[16745]&-4|2;c[16917]=c[16917]&-4|2;c[17089]=c[17089]&-4|2;c[17261]=c[17261]&-4|2;c[17433]=c[17433]&-4|2;c[17605]=c[17605]&-4|2;c[17777]=c[17777]&-4|2;c[17949]=c[17949]&-4|2;c[13898]=Ci+1;Cx=1;Cy=Cg;break L5954}}while(0);while(1){if((a[Cu+141136|0]|0)==(a[gH+(Cu+Cw|0)|0]|0)){Cz=Cw;CA=Cv}else{if((Cu|0)!=2){CB=0;CC=0;CD=Co;break}Cz=Cw-1|0;CA=1}hR=Cu+1|0;if((hR|0)<(CA+hB|0)){Cu=hR;Cv=CA;Cw=Cz}else{b7=4441;break}}do{if((b7|0)==4441){b7=0;if((CA|0)==0){if(!((Cu|0)==1|(Cu|0)==6)){CB=0;CC=0;CD=Co;break}}c[16229]=c[16229]&-4|1;c[16401]=c[16401]&-4|1;c[16573]=c[16573]&-4|1;c[16745]=c[16745]&-4|1;c[16917]=c[16917]&-4|1;c[17089]=c[17089]&-4|1;c[17261]=c[17261]&-4|1;c[17433]=c[17433]&-4|1;c[17605]=c[17605]&-4|1;c[17777]=c[17777]&-4|1;c[17949]=c[17949]&-4|1;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[CB+115048|0]|0)==(a[gH+(CB+CD|0)|0]|0)){CE=CD;CF=CC}else{if((CB|0)!=2){CG=0;CH=0;CI=Co;break}CE=CD-1|0;CF=1}hR=CB+1|0;if((hR|0)<(CF+hB|0)){CB=hR;CC=CF;CD=CE}else{b7=4450;break}}do{if((b7|0)==4450){b7=0;if((CF|0)==0){if(!((CB|0)==1|(CB|0)==6)){CG=0;CH=0;CI=Co;break}}c[16229]=c[16229]|4;c[16401]=c[16401]|4;c[16573]=c[16573]|4;c[16745]=c[16745]|4;c[16917]=c[16917]|4;c[17089]=c[17089]|4;c[17261]=c[17261]|4;c[17433]=c[17433]|4;c[17605]=c[17605]|4;c[17777]=c[17777]|4;c[17949]=c[17949]|4;c[13898]=Ci+1;Cx=Ch;Cy=1;break L5954}}while(0);while(1){if((a[CG+114024|0]|0)==(a[gH+(CG+CI|0)|0]|0)){CJ=CI;CK=CH}else{if((CG|0)!=4){CL=0;CM=0;CN=Co;break}CJ=CI-1|0;CK=1}hR=CG+1|0;if((hR|0)<(CK+hB|0)){CG=hR;CH=CK;CI=CJ}else{b7=4457;break}}do{if((b7|0)==4457){b7=0;if((CK|0)==0){if(!((CG|0)==3|(CG|0)==8)){CL=0;CM=0;CN=Co;break}}c[16229]=c[16229]&-5;c[16401]=c[16401]&-5;c[16573]=c[16573]&-5;c[16745]=c[16745]&-5;c[16917]=c[16917]&-5;c[17089]=c[17089]&-5;c[17261]=c[17261]&-5;c[17433]=c[17433]&-5;c[17605]=c[17605]&-5;c[17777]=c[17777]&-5;c[17949]=c[17949]&-5;c[13898]=Ci+1;Cx=Ch;Cy=1;break L5954}}while(0);while(1){if((a[CL+110600|0]|0)==(a[gH+(CL+CN|0)|0]|0)){CO=CN;CP=CM}else{if((CL|0)!=2){CQ=0;CR=0;CS=Co;break}CO=CN-1|0;CP=1}hR=CL+1|0;if((hR|0)<(CP+hB|0)){CL=hR;CM=CP;CN=CO}else{b7=4464;break}}do{if((b7|0)==4464){b7=0;if((CP|0)==0){if(!((CL|0)==1|(CL|0)==7)){CQ=0;CR=0;CS=Co;break}}a[65072]=1;a[65760]=1;a[66448]=1;a[67136]=1;a[67824]=1;a[68512]=1;a[69200]=1;a[69888]=1;a[70576]=1;a[71264]=1;a[71952]=1;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[CQ+107800|0]|0)==(a[gH+(CQ+CS|0)|0]|0)){CT=CS;CU=CR}else{if((CQ|0)!=3){CV=0;CW=0;CX=Co;break}CT=CS-1|0;CU=1}hR=CQ+1|0;if((hR|0)<(CU+hB|0)){CQ=hR;CR=CU;CS=CT}else{b7=4471;break}}do{if((b7|0)==4471){b7=0;if((CU|0)==0){if(!((CQ|0)==2|(CQ|0)==8)){CV=0;CW=0;CX=Co;break}}a[65072]=0;a[65760]=0;a[66448]=0;a[67136]=0;a[67824]=0;a[68512]=0;a[69200]=0;a[69888]=0;a[70576]=0;a[71264]=0;a[71952]=0;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[CV+106864|0]|0)==(a[gH+(CV+CX|0)|0]|0)){CY=CX;CZ=CW}else{if((CV|0)!=2){C_=0;C$=0;C0=Co;break}CY=CX-1|0;CZ=1}hR=CV+1|0;if((hR|0)<(CZ+hB|0)){CV=hR;CW=CZ;CX=CY}else{b7=4478;break}}do{if((b7|0)==4478){b7=0;if((CZ|0)==0){if(!((CV|0)==5|(CV|0)==1)){C_=0;C$=0;C0=Co;break}}hR=Ci+1|0;c[13898]=hR;hp=c[Ck+(hR*40&-1)+36>>2]|0;L6041:do{if((a[Ck+(hR*40&-1)|0]&1)!=0&(hp|0)>0){gG=0;hK=0;hS=c[Ck+(hR*40&-1)+32>>2]|0;while(1){if((a[gG+184456|0]|0)==(a[gH+(gG+hS|0)|0]|0)){C1=hS;C2=hK}else{if((gG|0)!=3){break L6041}C1=hS-1|0;C2=1}gz=gG+1|0;if((gz|0)<(C2+hp|0)){gG=gz;hK=C2;hS=C1}else{break}}if((C2|0)==0){if(!((gG|0)==2|(gG|0)==7)){break}}h[8132]=1.0;h[8133]=.5;h[8218]=1.0;h[8219]=.5;h[8304]=1.0;h[8305]=.5;h[8390]=1.0;h[8391]=.5;h[8476]=1.0;h[8477]=.5;h[8562]=1.0;h[8563]=.5;h[8648]=1.0;h[8649]=.5;h[8734]=1.0;h[8735]=.5;h[8820]=1.0;h[8821]=.5;h[8906]=1.0;h[8907]=.5;h[8992]=1.0;h[8993]=.5;c[13898]=Ci+2;Cx=Ch;Cy=Cg;break L5954}}while(0);hp=is(C)|0;hR=c[hp>>2]|0;if((hR|0)==1){C3=+(c[hp+8>>2]|0)}else if((hR|0)==2){C3=+h[hp+8>>3]}else if((hR|0)==3){C3=+uz(c[hp+8>>2]|0,0)}else{b7=4493;break L27}if((c[fV>>2]|0)==3){uu(c[fW>>2]|0);c[fV>>2]=1}hp=c[13898]|0;L6062:do{if((c[8272]|0)>(hp|0)){hR=c[1054]|0;if((a[hR+(hp*40&-1)|0]&1)==0){b7=4509;break}hS=c[hR+(hp*40&-1)+36>>2]|0;hK=hR+(hp*40&-1)+32|0;hR=c[10036]|0;gz=0;while(1){if((gz|0)>=(hS|0)){break}if((a[hR+((c[hK>>2]|0)+gz|0)|0]|0)==(a[gz+148464|0]|0)){gz=gz+1|0}else{b7=4509;break L6062}}if((gz|0)!=1){b7=4509;break}c[13898]=hp+1;hK=is(B)|0;hR=c[hK>>2]|0;if((hR|0)==1){C4=+(c[hK+8>>2]|0)}else if((hR|0)==2){C4=+h[hK+8>>3]}else if((hR|0)==3){C4=+uz(c[hK+8>>2]|0,0)}else{b7=4506;break L27}if((c[fX>>2]|0)!=3){C5=C4;break}uu(c[fY>>2]|0);c[fX>>2]=1;C5=C4}else{b7=4509}}while(0);if((b7|0)==4509){b7=0;C5=C3*.5}h[8132]=C3;h[8133]=C5;h[8218]=C3;h[8219]=C5;h[8304]=C3;h[8305]=C5;h[8390]=C3;h[8391]=C5;h[8476]=C3;h[8477]=C5;h[8562]=C3;h[8563]=C5;h[8648]=C3;h[8649]=C5;h[8734]=C3;h[8735]=C5;h[8820]=C3;h[8821]=C5;h[8906]=C3;h[8907]=C5;h[8992]=C3;h[8993]=C5;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[C_+104840|0]|0)==(a[gH+(C_+C0|0)|0]|0)){C6=C0;C7=C$}else{if((C_|0)!=2){C8=0;C9=0;Da=Co;break}C6=C0-1|0;C7=1}hp=C_+1|0;if((hp|0)<(C7+hB|0)){C_=hp;C$=C7;C0=C6}else{b7=4515;break}}do{if((b7|0)==4515){b7=0;if((C7|0)==0){if(!((C_|0)==1|(C_|0)==6)){C8=0;C9=0;Da=Co;break}}c[16258]=-270;c[16430]=-270;c[16602]=-270;c[16774]=-270;c[16946]=-270;c[17118]=-270;c[17290]=-270;c[17462]=-270;c[17634]=-270;c[17806]=-270;c[17978]=-270;hp=Ci+1|0;c[13898]=hp;if((Cj|0)<=(hp|0)){Cx=Ch;Cy=Cg;break L5954}if((a[Ck+(hp*40&-1)|0]&1)==0){Cx=Ch;Cy=Cg;break L5954}hK=c[Ck+(hp*40&-1)+36>>2]|0;hR=Ck+(hp*40&-1)+32|0;hp=0;while(1){if((hp|0)>=(hK|0)){break}if((a[gH+((c[hR>>2]|0)+hp|0)|0]|0)==(a[hp+163312|0]|0)){hp=hp+1|0}else{Cx=Ch;Cy=Cg;break L5954}}if((hp|0)!=2){Cx=Ch;Cy=Cg;break L5954}c[13898]=Ci+2;hR=is(D)|0;hK=c[hR>>2]|0;if((hK|0)==1){Db=+(c[hR+8>>2]|0)}else if((hK|0)==2){Db=+h[hR+8>>3]}else if((hK|0)==3){Db=+uz(c[hR+8>>2]|0,0)}else{b7=4527;break L27}if((c[fT>>2]|0)==3){uu(c[fU>>2]|0);c[fT>>2]=1}hR=~~Db;c[16258]=hR;c[16430]=hR;c[16602]=hR;c[16774]=hR;c[16946]=hR;c[17118]=hR;c[17290]=hR;c[17462]=hR;c[17634]=hR;c[17806]=hR;c[17978]=hR;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[C8+104240|0]|0)==(a[gH+(C8+Da|0)|0]|0)){Dc=Da;Dd=C9}else{if((C8|0)!=4){De=0;Df=0;Dg=Co;break}Dc=Da-1|0;Dd=1}hR=C8+1|0;if((hR|0)<(Dd+hB|0)){C8=hR;C9=Dd;Da=Dc}else{b7=4535;break}}do{if((b7|0)==4535){b7=0;if((Dd|0)==0){if(!((C8|0)==3|(C8|0)==8)){De=0;Df=0;Dg=Co;break}}c[16258]=0;c[16430]=0;c[16602]=0;c[16774]=0;c[16946]=0;c[17118]=0;c[17290]=0;c[17462]=0;c[17634]=0;c[17806]=0;c[17978]=0;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[De+149696|0]|0)==(a[gH+(De+Dg|0)|0]|0)){Dh=Dg;Di=Df}else{if((De|0)!=1){Dj=0;Dk=0;Dl=Co;break}Dh=Dg-1|0;Di=1}hR=De+1|0;if((hR|0)<(Di+hB|0)){De=hR;Df=Di;Dg=Dh}else{b7=4542;break}}do{if((b7|0)==4542){b7=0;if((Di|0)==0){if(!((De|0)==0|(De|0)==4)){Dj=0;Dk=0;Dl=Co;break}}c[16282]=0;a[65272]=1;c[16454]=0;a[65960]=1;c[16626]=0;a[66648]=1;c[16798]=0;a[67336]=1;c[16970]=0;a[68024]=1;c[17142]=0;a[68712]=1;c[17314]=0;a[69400]=1;c[17486]=0;a[70088]=1;c[17658]=0;a[70776]=1;c[17830]=0;a[71464]=1;c[18002]=0;a[72152]=1;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[Dj+179896|0]|0)==(a[gH+(Dj+Dl|0)|0]|0)){Dm=Dl;Dn=Dk}else{if((Dj|0)!=1){Do=0;Dp=0;Dq=Co;b7=4551;break}Dm=Dl-1|0;Dn=1}hR=Dj+1|0;if((hR|0)<(Dn+hB|0)){Dj=hR;Dk=Dn;Dl=Dm}else{b7=4549;break}}do{if((b7|0)==4549){b7=0;if((Dn|0)!=0){break}if(!((Dj|0)==0|(Dj|0)==6)){Do=0;Dp=0;Dq=Co;b7=4551}}}while(0);do{if((b7|0)==4551){while(1){b7=0;if((a[Do+149304|0]|0)==(a[gH+(Do+Dq|0)|0]|0)){Dr=Dq;Ds=Dp}else{if((Do|0)!=1){Dt=0;Du=0;Dv=Co;break}Dr=Dq-1|0;Ds=1}hR=Do+1|0;if((hR|0)<(Ds+hB|0)){Do=hR;Dp=Ds;Dq=Dr;b7=4551}else{b7=4555;break}}if((b7|0)==4555){b7=0;if((Ds|0)!=0){break}if((Do|0)==0|(Do|0)==6){break}else{Dt=0;Du=0;Dv=Co}}while(1){if((a[Dt+101272|0]|0)==(a[gH+(Dt+Dv|0)|0]|0)){Dw=Dv;Dx=Du}else{if((Dt|0)!=2){Dy=0;Dz=0;DA=Co;break}Dw=Dv-1|0;Dx=1}hp=Dt+1|0;if((hp|0)<(Dx+hB|0)){Dt=hp;Du=Dx;Dv=Dw}else{b7=4562;break}}do{if((b7|0)==4562){b7=0;if((Dx|0)==0){if(!((Dt|0)==1|(Dt|0)==5)){Dy=0;Dz=0;DA=Co;break}}c[16282]=2;a[65272]=1;c[16454]=2;a[65960]=1;c[16626]=2;a[66648]=1;c[16798]=2;a[67336]=1;c[16970]=2;a[68024]=1;c[17142]=2;a[68712]=1;c[17314]=2;a[69400]=1;c[17486]=2;a[70088]=1;c[17658]=2;a[70776]=1;c[17830]=2;a[71464]=1;c[18002]=2;a[72152]=1;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[Dy+100712|0]|0)==(a[gH+(Dy+DA|0)|0]|0)){DB=DA;DC=Dz}else{if((Dy|0)!=5){DD=0;DE=0;DF=Co;break}DB=DA-1|0;DC=1}hp=Dy+1|0;if((hp|0)<(DC+hB|0)){Dy=hp;Dz=DC;DA=DB}else{b7=4569;break}}do{if((b7|0)==4569){b7=0;if((DC|0)==0){if(!((Dy|0)==4|(Dy|0)==11)){DD=0;DE=0;DF=Co;break}}a[65272]=0;a[65960]=0;a[66648]=0;a[67336]=0;a[68024]=0;a[68712]=0;a[69400]=0;a[70088]=0;a[70776]=0;a[71464]=0;a[72152]=0;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[DD+152064|0]|0)==(a[gH+(DD+DF|0)|0]|0)){DG=DF;DH=DE}else{if((DD|0)!=3){DI=0;DJ=0;DK=Co;break}DG=DF-1|0;DH=1}hp=DD+1|0;if((hp|0)<(DH+hB|0)){DD=hp;DE=DH;DF=DG}else{b7=4576;break}}do{if((b7|0)==4576){b7=0;if((DH|0)==0){if(!((DD|0)==6|(DD|0)==2)){DI=0;DJ=0;DK=Co;break}}c[13898]=Ci+1;dl(F,4);c[16246]=c[fO>>2];c[64988>>2]=c[fO+4>>2];c[64992>>2]=c[fO+8>>2];c[64996>>2]=c[fO+12>>2];c[65e3>>2]=c[fO+16>>2];c[65004>>2]=c[fO+20>>2];c[65008>>2]=c[fO+24>>2];c[65012>>2]=c[fO+28>>2];c[65016>>2]=c[fO+32>>2];c[65020>>2]=c[fO+36>>2];c[16418]=c[fO>>2];c[65676>>2]=c[fO+4>>2];c[65680>>2]=c[fO+8>>2];c[65684>>2]=c[fO+12>>2];c[65688>>2]=c[fO+16>>2];c[65692>>2]=c[fO+20>>2];c[65696>>2]=c[fO+24>>2];c[65700>>2]=c[fO+28>>2];c[65704>>2]=c[fO+32>>2];c[65708>>2]=c[fO+36>>2];c[16590]=c[fO>>2];c[66364>>2]=c[fO+4>>2];c[66368>>2]=c[fO+8>>2];c[66372>>2]=c[fO+12>>2];c[66376>>2]=c[fO+16>>2];c[66380>>2]=c[fO+20>>2];c[66384>>2]=c[fO+24>>2];c[66388>>2]=c[fO+28>>2];c[66392>>2]=c[fO+32>>2];c[66396>>2]=c[fO+36>>2];c[16762]=c[fO>>2];c[67052>>2]=c[fO+4>>2];c[67056>>2]=c[fO+8>>2];c[67060>>2]=c[fO+12>>2];c[67064>>2]=c[fO+16>>2];c[67068>>2]=c[fO+20>>2];c[67072>>2]=c[fO+24>>2];c[67076>>2]=c[fO+28>>2];c[67080>>2]=c[fO+32>>2];c[67084>>2]=c[fO+36>>2];c[16934]=c[fO>>2];c[67740>>2]=c[fO+4>>2];c[67744>>2]=c[fO+8>>2];c[67748>>2]=c[fO+12>>2];c[67752>>2]=c[fO+16>>2];c[67756>>2]=c[fO+20>>2];c[67760>>2]=c[fO+24>>2];c[67764>>2]=c[fO+28>>2];c[67768>>2]=c[fO+32>>2];c[67772>>2]=c[fO+36>>2];c[17106]=c[fO>>2];c[68428>>2]=c[fO+4>>2];c[68432>>2]=c[fO+8>>2];c[68436>>2]=c[fO+12>>2];c[68440>>2]=c[fO+16>>2];c[68444>>2]=c[fO+20>>2];c[68448>>2]=c[fO+24>>2];c[68452>>2]=c[fO+28>>2];c[68456>>2]=c[fO+32>>2];c[68460>>2]=c[fO+36>>2];c[17278]=c[fO>>2];c[69116>>2]=c[fO+4>>2];c[69120>>2]=c[fO+8>>2];c[69124>>2]=c[fO+12>>2];c[69128>>2]=c[fO+16>>2];c[69132>>2]=c[fO+20>>2];c[69136>>2]=c[fO+24>>2];c[69140>>2]=c[fO+28>>2];c[69144>>2]=c[fO+32>>2];c[69148>>2]=c[fO+36>>2];c[17450]=c[fO>>2];c[69804>>2]=c[fO+4>>2];c[69808>>2]=c[fO+8>>2];c[69812>>2]=c[fO+12>>2];c[69816>>2]=c[fO+16>>2];c[69820>>2]=c[fO+20>>2];c[69824>>2]=c[fO+24>>2];c[69828>>2]=c[fO+28>>2];c[69832>>2]=c[fO+32>>2];c[69836>>2]=c[fO+36>>2];c[17622]=c[fO>>2];c[70492>>2]=c[fO+4>>2];c[70496>>2]=c[fO+8>>2];c[70500>>2]=c[fO+12>>2];c[70504>>2]=c[fO+16>>2];c[70508>>2]=c[fO+20>>2];c[70512>>2]=c[fO+24>>2];c[70516>>2]=c[fO+28>>2];c[70520>>2]=c[fO+32>>2];c[70524>>2]=c[fO+36>>2];c[17794]=c[fO>>2];c[71180>>2]=c[fO+4>>2];c[71184>>2]=c[fO+8>>2];c[71188>>2]=c[fO+12>>2];c[71192>>2]=c[fO+16>>2];c[71196>>2]=c[fO+20>>2];c[71200>>2]=c[fO+24>>2];c[71204>>2]=c[fO+28>>2];c[71208>>2]=c[fO+32>>2];c[71212>>2]=c[fO+36>>2];c[17966]=c[fO>>2];c[71868>>2]=c[fO+4>>2];c[71872>>2]=c[fO+8>>2];c[71876>>2]=c[fO+12>>2];c[71880>>2]=c[fO+16>>2];c[71884>>2]=c[fO+20>>2];c[71888>>2]=c[fO+24>>2];c[71892>>2]=c[fO+28>>2];c[71896>>2]=c[fO+32>>2];c[71900>>2]=c[fO+36>>2];Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[DI+153128|0]|0)==(a[gH+(DI+DK|0)|0]|0)){DL=DK;DM=DJ}else{if((DI|0)!=5){DN=0;break}DL=DK-1|0;DM=1}hp=DI+1|0;if((hp|0)<(DM+hB|0)){DI=hp;DJ=DM;DK=DL}else{b7=4583;break}}do{if((b7|0)==4583){b7=0;if((DM|0)==0){if(!((DI|0)==4|(DI|0)==8)){DN=0;break}}uE(dx|0,0,28);c[13898]=Ci+1;c[16246]=4;c[16247]=4;c[16248]=4;c[16249]=c[dx>>2];c[65e3>>2]=c[dx+4>>2];c[65004>>2]=c[dx+8>>2];c[65008>>2]=c[dx+12>>2];c[65012>>2]=c[dx+16>>2];c[65016>>2]=c[dx+20>>2];c[65020>>2]=c[dx+24>>2];c[16418]=4;c[16419]=4;c[16420]=4;c[65684>>2]=c[dx>>2];c[(65684|0)+4>>2]=c[dx+4>>2];c[(65684|0)+8>>2]=c[dx+8>>2];c[(65684|0)+12>>2]=c[dx+12>>2];c[(65684|0)+16>>2]=c[dx+16>>2];c[(65684|0)+20>>2]=c[dx+20>>2];c[(65684|0)+24>>2]=c[dx+24>>2];c[16590]=4;c[16591]=4;c[16592]=4;c[66372>>2]=c[dx>>2];c[(66372|0)+4>>2]=c[dx+4>>2];c[(66372|0)+8>>2]=c[dx+8>>2];c[(66372|0)+12>>2]=c[dx+12>>2];c[(66372|0)+16>>2]=c[dx+16>>2];c[(66372|0)+20>>2]=c[dx+20>>2];c[(66372|0)+24>>2]=c[dx+24>>2];c[16762]=4;c[16763]=4;c[16764]=4;c[67060>>2]=c[dx>>2];c[(67060|0)+4>>2]=c[dx+4>>2];c[(67060|0)+8>>2]=c[dx+8>>2];c[(67060|0)+12>>2]=c[dx+12>>2];c[(67060|0)+16>>2]=c[dx+16>>2];c[(67060|0)+20>>2]=c[dx+20>>2];c[(67060|0)+24>>2]=c[dx+24>>2];c[16934]=4;c[16935]=4;c[16936]=4;c[67748>>2]=c[dx>>2];c[(67748|0)+4>>2]=c[dx+4>>2];c[(67748|0)+8>>2]=c[dx+8>>2];c[(67748|0)+12>>2]=c[dx+12>>2];c[(67748|0)+16>>2]=c[dx+16>>2];c[(67748|0)+20>>2]=c[dx+20>>2];c[(67748|0)+24>>2]=c[dx+24>>2];c[17106]=4;c[17107]=4;c[17108]=4;c[68436>>2]=c[dx>>2];c[(68436|0)+4>>2]=c[dx+4>>2];c[(68436|0)+8>>2]=c[dx+8>>2];c[(68436|0)+12>>2]=c[dx+12>>2];c[(68436|0)+16>>2]=c[dx+16>>2];c[(68436|0)+20>>2]=c[dx+20>>2];c[(68436|0)+24>>2]=c[dx+24>>2];c[17278]=4;c[17279]=4;c[17280]=4;c[69124>>2]=c[dx>>2];c[(69124|0)+4>>2]=c[dx+4>>2];c[(69124|0)+8>>2]=c[dx+8>>2];c[(69124|0)+12>>2]=c[dx+12>>2];c[(69124|0)+16>>2]=c[dx+16>>2];c[(69124|0)+20>>2]=c[dx+20>>2];c[(69124|0)+24>>2]=c[dx+24>>2];c[17450]=4;c[17451]=4;c[17452]=4;c[69812>>2]=c[dx>>2];c[(69812|0)+4>>2]=c[dx+4>>2];c[(69812|0)+8>>2]=c[dx+8>>2];c[(69812|0)+12>>2]=c[dx+12>>2];c[(69812|0)+16>>2]=c[dx+16>>2];c[(69812|0)+20>>2]=c[dx+20>>2];c[(69812|0)+24>>2]=c[dx+24>>2];c[17622]=4;c[17623]=4;c[17624]=4;c[70500>>2]=c[dx>>2];c[(70500|0)+4>>2]=c[dx+4>>2];c[(70500|0)+8>>2]=c[dx+8>>2];c[(70500|0)+12>>2]=c[dx+12>>2];c[(70500|0)+16>>2]=c[dx+16>>2];c[(70500|0)+20>>2]=c[dx+20>>2];c[(70500|0)+24>>2]=c[dx+24>>2];c[17794]=4;c[17795]=4;c[17796]=4;c[71188>>2]=c[dx>>2];c[(71188|0)+4>>2]=c[dx+4>>2];c[(71188|0)+8>>2]=c[dx+8>>2];c[(71188|0)+12>>2]=c[dx+12>>2];c[(71188|0)+16>>2]=c[dx+16>>2];c[(71188|0)+20>>2]=c[dx+20>>2];c[(71188|0)+24>>2]=c[dx+24>>2];c[17966]=4;c[17967]=4;c[17968]=4;c[71876>>2]=c[dx>>2];c[(71876|0)+4>>2]=c[dx+4>>2];c[(71876|0)+8>>2]=c[dx+8>>2];c[(71876|0)+12>>2]=c[dx+12>>2];c[(71876|0)+16>>2]=c[dx+16>>2];c[(71876|0)+20>>2]=c[dx+20>>2];c[(71876|0)+24>>2]=c[dx+24>>2];Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[DN+97960|0]|0)!=(a[gH+(DN+Co|0)|0]|0)){DO=0;DP=0;DQ=Co;break}DR=DN+1|0;if((DR|0)<(hB|0)){DN=DR}else{b7=4588;break}}do{if((b7|0)==4588){b7=0;if((DR|0)!=6){DO=0;DP=0;DQ=Co;break}jp();Cx=Ch;Cy=Cg;break L5954}}while(0);while(1){if((a[DO+175984|0]|0)==(a[gH+(DO+DQ|0)|0]|0)){DS=DQ;DT=DP}else{if((DO|0)!=1){Cn=Co;b7=4620;break L5954}DS=DQ-1|0;DT=1}hp=DO+1|0;if((hp|0)<(DT+hB|0)){DO=hp;DP=DT;DQ=DS}else{break}}if((DT|0)==0){if(!((DO|0)==4|(DO|0)==0)){Cn=Co;b7=4620;break L5954}}DU=Ci+1|0;c[13898]=DU;hp=(a[Ck+(DU*40&-1)|0]&1)==0;if(hp){b7=4598}else{hR=a[gH+(c[Ck+(DU*40&-1)+32>>2]|0)|0]|0;if(!((hR<<24>>24|0)==39|(hR<<24>>24|0)==34)){b7=4598}}if((b7|0)==4598){b7=0;hR=c[10810]|0;if((hR|0)==0){b7=5299;break L27}hK=Ck+(DU*40&-1)+36|0;hS=Ck+(DU*40&-1)+32|0;gG=(Cj|0)<=(DU|0)|hp;gy=hR;L6211:while(1){hR=c[gy+4>>2]|0;L6213:do{if(!gG){ho=c[hK>>2]|0;ng=0;while(1){if((ng|0)>=(ho|0)){break}if((a[gH+((c[hS>>2]|0)+ng|0)|0]|0)==(a[hR+ng|0]|0)){ng=ng+1|0}else{break L6213}}if((a[hR+ng|0]|0)==0){break L6211}}}while(0);hR=c[gy>>2]|0;if((hR|0)==0){b7=5301;break L27}else{gy=hR}}if((a[gy+8|0]&1)!=0){b7=5300;break L27}if((c[gy+16>>2]|0)!=3){b7=5302;break L27}}L6223:do{if((DU|0)<(Cj|0)){L6225:do{if(!hp){hS=c[Ck+(DU*40&-1)+36>>2]|0;hK=Ck+(DU*40&-1)+32|0;gG=0;while(1){if((gG|0)>=(hS|0)){break}if((a[gH+((c[hK>>2]|0)+gG|0)|0]|0)==(a[gG+103664|0]|0)){gG=gG+1|0}else{break L6225}}if((gG|0)==1){DV=0;break L6223}}}while(0);a[14176]=1;is(E);a[14176]=0;if((c[fQ>>2]|0)==3){DV=c[fS>>2]|0;break}else{c[13898]=DU;DV=0;break}}else{DV=0}}while(0);uu(c[16231]|0);if((DV|0)==0){c[16231]=0;uu(c[16403]|0);c[16403]=0;uu(c[16575]|0);c[16575]=0;uu(c[16747]|0);c[16747]=0;uu(c[16919]|0);c[16919]=0;uu(c[17091]|0);c[17091]=0;uu(c[17263]|0);c[17263]=0;uu(c[17435]|0);c[17435]=0;uu(c[17607]|0);c[17607]=0;uu(c[17779]|0);c[17779]=0;uu(c[17951]|0);DW=0}else{c[16231]=bP(DV|0)|0;uu(c[16403]|0);c[16403]=bP(DV|0)|0;uu(c[16575]|0);c[16575]=bP(DV|0)|0;uu(c[16747]|0);c[16747]=bP(DV|0)|0;uu(c[16919]|0);c[16919]=bP(DV|0)|0;uu(c[17091]|0);c[17091]=bP(DV|0)|0;uu(c[17263]|0);c[17263]=bP(DV|0)|0;uu(c[17435]|0);c[17435]=bP(DV|0)|0;uu(c[17607]|0);c[17607]=bP(DV|0)|0;uu(c[17779]|0);c[17779]=bP(DV|0)|0;uu(c[17951]|0);DW=bP(DV|0)|0}c[17951]=DW;uu(DV);Cx=Ch;Cy=Cg;break L5954}}while(0);c[16282]=1;a[65272]=1;c[16454]=1;a[65960]=1;c[16626]=1;a[66648]=1;c[16798]=1;a[67336]=1;c[16970]=1;a[68024]=1;c[17142]=1;a[68712]=1;c[17314]=1;a[69400]=1;c[17486]=1;a[70088]=1;c[17658]=1;a[70776]=1;c[17830]=1;a[71464]=1;c[18002]=1;a[72152]=1;c[13898]=Ci+1;Cx=Ch;Cy=Cg}}while(0);L6241:do{if((b7|0)==4620){b7=0;hF=(Cj|0)>(Ci|0);L6243:do{if(hF){if(gF){b7=5303;break L27}gH=c[10036]|0;hp=0;while(1){if((hp|0)>=(hB|0)){break}if((a[gH+(hp+Cn|0)|0]|0)==(a[hp+143040|0]|0)){hp=hp+1|0}else{b7=4626;break L6243}}if((hp|0)!=2){b7=4626}}else{b7=4626}}while(0);L6250:do{if((b7|0)==4626){b7=0;L6252:do{if((hB|0)>0&(gF^1)){gH=c[10036]|0;gy=0;hK=0;hS=Cn;while(1){if((a[gy+142856|0]|0)==(a[gH+(gy+hS|0)|0]|0)){DX=hS;DY=hK}else{if((gy|0)!=4){break L6252}DX=hS-1|0;DY=1}ng=gy+1|0;if((ng|0)<(DY+hB|0)){gy=ng;hK=DY;hS=DX}else{break}}if((DY|0)!=0){break L6250}if((gy|0)==3|(gy|0)==9){break L6250}}}while(0);if(!hF){Cx=Ch;Cy=Cg;break L6241}if(gF){b7=5304;break L27}hp=c[10036]|0;hS=0;while(1){if((hS|0)>=(hB|0)){b7=4640;break}if((a[hp+(hS+Cn|0)|0]|0)==(a[hS+150856|0]|0)){hS=hS+1|0}else{DZ=0;break}}do{if((b7|0)==4640){b7=0;if((hS|0)!=5){DZ=0;break}c[10026]=1;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L6241}}while(0);while(1){if((DZ|0)>=(hB|0)){b7=4644;break}if((a[hp+(DZ+Cn|0)|0]|0)==(a[DZ+150704|0]|0)){DZ=DZ+1|0}else{D_=0;break}}do{if((b7|0)==4644){b7=0;if((DZ|0)!=4){D_=0;break}c[10026]=0;c[13898]=Ci+1;Cx=Ch;Cy=Cg;break L6241}}while(0);while(1){if((D_|0)>=(hB|0)){break}if((a[hp+(D_+Cn|0)|0]|0)==(a[D_+103664|0]|0)){D_=D_+1|0}else{b7=5305;break L27}}if((D_|0)==1){Cx=Ch;Cy=Cg;break L6241}else{b7=5306;break L27}}}while(0);hI(G,5);c[16232]=c[fP>>2];c[64932>>2]=c[fP+4>>2];c[64936>>2]=c[fP+8>>2];c[64940>>2]=c[fP+12>>2];c[16404]=c[fP>>2];c[65620>>2]=c[fP+4>>2];c[65624>>2]=c[fP+8>>2];c[65628>>2]=c[fP+12>>2];c[16576]=c[fP>>2];c[66308>>2]=c[fP+4>>2];c[66312>>2]=c[fP+8>>2];c[66316>>2]=c[fP+12>>2];c[16748]=c[fP>>2];c[66996>>2]=c[fP+4>>2];c[67e3>>2]=c[fP+8>>2];c[67004>>2]=c[fP+12>>2];c[16920]=c[fP>>2];c[67684>>2]=c[fP+4>>2];c[67688>>2]=c[fP+8>>2];c[67692>>2]=c[fP+12>>2];c[17092]=c[fP>>2];c[68372>>2]=c[fP+4>>2];c[68376>>2]=c[fP+8>>2];c[68380>>2]=c[fP+12>>2];c[17264]=c[fP>>2];c[69060>>2]=c[fP+4>>2];c[69064>>2]=c[fP+8>>2];c[69068>>2]=c[fP+12>>2];c[17436]=c[fP>>2];c[69748>>2]=c[fP+4>>2];c[69752>>2]=c[fP+8>>2];c[69756>>2]=c[fP+12>>2];c[17608]=c[fP>>2];c[70436>>2]=c[fP+4>>2];c[70440>>2]=c[fP+8>>2];c[70444>>2]=c[fP+12>>2];c[17780]=c[fP>>2];c[71124>>2]=c[fP+4>>2];c[71128>>2]=c[fP+8>>2];c[71132>>2]=c[fP+12>>2];c[17952]=c[fP>>2];c[71812>>2]=c[fP+4>>2];c[71816>>2]=c[fP+8>>2];c[71820>>2]=c[fP+12>>2];Cx=Ch;Cy=Cg}}while(0);hB=c[13898]|0;gF=c[8272]|0;if((hB|0)>=(gF|0)){Cl=Cy;Cm=Cx;break L5951}Cg=Cy;Ch=Cx;Ci=hB;Cj=gF;Ck=c[1054]|0;b7=4421}}}while(0);hG=0;do{gC=64916+(hG*688&-1)|0;do{if(!((c[gC>>2]&3|0)!=0|Cm)){if((hG-5|0)>>>0<2){break}c[gC>>2]=1;if(Cl){break}if((hG-1|0)>>>0>=3){break}c[gC>>2]=5}}while(0);hG=hG+1|0;}while(hG>>>0<11)}else if((gD|0)==90){uh(gv,225144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);hG=(c[13898]|0)+1|0;c[13898]=hG;L6294:do{if((hG|0)<(c[8272]|0)){gC=c[1054]|0;L6296:do{if((a[gC+(hG*40&-1)|0]&1)!=0){nd=c[gC+(hG*40&-1)+36>>2]|0;nh=gC+(hG*40&-1)+32|0;gF=c[10036]|0;hB=0;while(1){if((hB|0)>=(nd|0)){break}if((a[gF+((c[nh>>2]|0)+hB|0)|0]|0)==(a[hB+103664|0]|0)){hB=hB+1|0}else{break L6296}}if((hB|0)==1){D$=.5;D0=1.0;break L6294}}}while(0);gC=is(z)|0;nh=c[gC>>2]|0;if((nh|0)==1){D1=+(c[gC+8>>2]|0)}else if((nh|0)==2){D1=+h[gC+8>>3]}else if((nh|0)==3){D1=+uz(c[gC+8>>2]|0,0)}else{b7=4668;break L27}if((c[fZ>>2]|0)==3){uu(c[f_>>2]|0);c[fZ>>2]=1}gC=c[13898]|0;L6311:do{if((gC|0)<(c[8272]|0)){nh=c[1054]|0;L6313:do{if((a[nh+(gC*40&-1)|0]&1)!=0){gF=c[nh+(gC*40&-1)+36>>2]|0;nd=nh+(gC*40&-1)+32|0;hF=c[10036]|0;hp=0;while(1){if((hp|0)>=(gF|0)){b7=4676;break}if((a[hF+((c[nd>>2]|0)+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{D2=0;break}}if((b7|0)==4676){b7=0;if((hp|0)==1){break L6311}else{D2=0}}while(1){if((D2|0)>=(gF|0)){break}if((a[hF+((c[nd>>2]|0)+D2|0)|0]|0)==(a[D2+148464|0]|0)){D2=D2+1|0}else{break L6313}}if((D2|0)!=1){break}c[13898]=gC+1}}while(0);nh=is(A)|0;hB=c[nh>>2]|0;if((hB|0)==1){D3=+(c[nh+8>>2]|0)}else if((hB|0)==2){D3=+h[nh+8>>3]}else if((hB|0)==3){D3=+uz(c[nh+8>>2]|0,0)}else{b7=4686;break L27}if((c[f$>>2]|0)!=3){D$=D3;D0=D1;break L6294}uu(c[f0>>2]|0);c[f$>>2]=1;D$=D3;D0=D1;break L6294}}while(0);D$=D1*.5;D0=D1}else{D$=.5;D0=1.0}}while(0);h[8132]=D0;h[8133]=D$;h[8218]=D0;h[8219]=D$;h[8304]=D0;h[8305]=D$;h[8390]=D0;h[8391]=D$;h[8476]=D0;h[8477]=D$;h[8562]=D0;h[8563]=D$;h[8648]=D0;h[8649]=D$;h[8734]=D0;h[8735]=D$;h[8820]=D0;h[8821]=D$;h[8906]=D0;h[8907]=D$;h[8992]=D0;h[8993]=D$}else if((gD|0)==92){D4=gv+1|0;c[13898]=D4;hG=c[8272]|0;L6335:do{if((D4|0)<(hG|0)){gC=(a[gw+(D4*40&-1)|0]&1)==0;nh=c[gw+(D4*40&-1)+36>>2]|0;hB=gw+(D4*40&-1)+32|0;L6337:do{if(!gC){nd=0;while(1){if((nd|0)>=(nh|0)){break}if((a[gB+((c[hB>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{break L6337}}if((nd|0)==1){break L6335}}}while(0);hF=c[hB>>2]|0;gF=(nh|0)>0;hp=56720;hS=77416;L6343:while(1){L6345:do{if(!gC){if(gF){hK=0;gH=0;ng=hF;while(1){hR=a[hS+hK|0]|0;if(hR<<24>>24==(a[gB+(hK+ng|0)|0]|0)){D5=ng;D6=gH}else{if(hR<<24>>24!=36){break L6345}D5=ng-1|0;D6=1}D7=hK+1|0;if((D7|0)<(D6+nh|0)){hK=D7;gH=D6;ng=D5}else{break}}if((D6|0)==0){D8=D7}else{D9=hp;break L6343}}else{D8=0}ng=a[hS+D8|0]|0;if((ng<<24>>24|0)==36|(ng<<24>>24|0)==0){D9=hp;break L6343}}}while(0);nd=hp+8|0;ng=c[nd>>2]|0;if((ng|0)==0){D9=nd;break}else{hp=nd;hS=ng}}hS=c[D9+4>>2]|0;if((hS|0)>-1){Ea=gv+2|0;c[13898]=Ea;if((Ea|0)>=(hG|0)){b7=4725;break L27}L6361:do{if((a[gw+(Ea*40&-1)|0]&1)!=0){hp=c[gw+(Ea*40&-1)+36>>2]|0;gF=gw+(Ea*40&-1)+32|0;hB=0;while(1){if((hB|0)>=(hp|0)){break}if((a[gB+((c[gF>>2]|0)+hB|0)|0]|0)==(a[hB+103664|0]|0)){hB=hB+1|0}else{break L6361}}if((hB|0)==1){b7=4725;break L27}}}while(0);a[14176]=1;is(y);a[14176]=0;if((c[f1>>2]|0)!=3){b7=4714;break L27}gF=c[f2>>2]|0;if((gF|0)==0){b7=4725;break L27}hp=64813+(hS*688&-1)|0;uF(hp|0,gF|0,50);Eb=gF}else{L6371:do{if(!gC){gF=0;while(1){if((gF|0)>=(nh|0)){break}if((a[gB+(gF+hF|0)|0]|0)==(a[gF+103664|0]|0)){gF=gF+1|0}else{break L6371}}if((gF|0)==1){b7=4725;break L27}}}while(0);a[14176]=1;is(x);a[14176]=0;if((c[f3>>2]|0)!=3){b7=4722;break L27}hF=c[f4>>2]|0;if((hF|0)==0){b7=4725;break L27}uF(64813,hF|0,50);uF(65501,hF|0,50);uF(66189,hF|0,50);uF(66877,hF|0,50);uF(67565,hF|0,50);uF(68253,hF|0,50);uF(68941,hF|0,50);uF(69629,hF|0,50);uF(70317,hF|0,50);uF(71005,hF|0,50);uF(71693,hF|0,50);Eb=hF}uu(Eb);break L44}}while(0);uD(64813,82192,15);uD(65501,82192,15);uD(66189,82192,15);uD(66877,82192,15);uD(67565,82192,15);uD(68253,82192,15);uD(68941,82192,15);uD(69629,82192,15);uD(70317,82192,15);uD(71005,82192,15);uD(71693,82192,15)}else if((gD|0)==93){hG=gv+1|0;c[13898]=hG;hF=c[8272]|0;L6382:do{if((hG|0)<(hF|0)){nh=0;gC=hG;hS=hF;while(1){Ec=gC;hB=hS;L6385:while(1){hp=c[1054]|0;Ed=c[hp+(Ec*40&-1)+36>>2]|0;Ee=hp+(Ec*40&-1)+32|0;if((a[hp+(Ec*40&-1)|0]&1)==0){b7=4773;break}ng=c[10036]|0;nd=0;while(1){if((nd|0)>=(Ed|0)){b7=4734;break}gH=c[Ee>>2]|0;if((a[ng+(gH+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{Ef=gH;break}}if((b7|0)==4734){b7=0;if((nd|0)==1){break L6382}Ef=c[Ee>>2]|0}L6395:do{if((Ed|0)>0){gH=0;hK=0;hR=Ef;while(1){if((a[gH+150048|0]|0)==(a[ng+(gH+hR|0)|0]|0)){Eg=hR;Eh=hK}else{if((gH|0)!=1){Ei=0;Ej=0;Ek=Ef;break}Eg=hR-1|0;Eh=1}gz=gH+1|0;if((gz|0)<(Eh+Ed|0)){gH=gz;hK=Eh;hR=Eg}else{b7=4741;break}}do{if((b7|0)==4741){b7=0;if((Eh|0)==0){if(!((gH|0)==0|(gH|0)==3)){Ei=0;Ej=0;Ek=Ef;break}}c[1166]=0;c[13898]=Ec+1;break L6395}}while(0);while(1){if((a[Ei+149848|0]|0)==(a[ng+(Ei+Ek|0)|0]|0)){El=Ek;Em=Ej}else{if((Ei|0)!=1){En=0;Eo=0;Ep=Ef;break}El=Ek-1|0;Em=1}gH=Ei+1|0;if((gH|0)<(Em+Ed|0)){Ei=gH;Ej=Em;Ek=El}else{b7=4749;break}}do{if((b7|0)==4749){b7=0;if((Em|0)==0){if(!((Ei|0)==0|(Ei|0)==6)){En=0;Eo=0;Ep=Ef;break}}c[1166]=1;c[13898]=Ec+1;break L6395}}while(0);while(1){if((a[En+95544|0]|0)==(a[ng+(En+Ep|0)|0]|0)){Eq=Ep;Er=Eo}else{if((En|0)!=1){Es=0;Et=0;Eu=Ef;break}Eq=Ep-1|0;Er=1}gH=En+1|0;if((gH|0)<(Er+Ed|0)){En=gH;Eo=Er;Ep=Eq}else{b7=4756;break}}do{if((b7|0)==4756){b7=0;if((Er|0)==0){if(!((En|0)==0|(En|0)==6)){Es=0;Et=0;Eu=Ef;break}}c[1164]=1;c[13898]=Ec+1;break L6395}}while(0);while(1){if((a[Es+184048|0]|0)==(a[ng+(Es+Eu|0)|0]|0)){Ev=Eu;Ew=Et}else{if((Es|0)!=1){Ex=0;Ey=0;Ez=Ef;break}Ev=Eu-1|0;Ew=1}gH=Es+1|0;if((gH|0)<(Ew+Ed|0)){Es=gH;Et=Ew;Eu=Ev}else{b7=4763;break}}do{if((b7|0)==4763){b7=0;if((Ew|0)==0){if(!((Es|0)==0|(Es|0)==8)){Ex=0;Ey=0;Ez=Ef;break}}c[1164]=0;c[13898]=Ec+1;break L6395}}while(0);while(1){if((a[Ex+152064|0]|0)==(a[ng+(Ex+Ez|0)|0]|0)){EA=Ez;EB=Ey}else{if((Ex|0)!=3){b7=4774;break L6395}EA=Ez-1|0;EB=1}gH=Ex+1|0;if((gH|0)<(EB+Ed|0)){Ex=gH;Ey=EB;Ez=EA}else{break}}if((EB|0)==0){if(!((Ex|0)==6|(Ex|0)==2)){b7=4774;break}}c[13898]=Ec+1;dl(4816,4)}else{b7=4774}}while(0);if((b7|0)==4774){b7=0;nd=(hB|0)>(Ec|0);if(nd){EC=0}else{ED=Ef;EE=0;EF=0;break}while(1){if((EC|0)>=(Ed|0)){break}if((a[ng+(EC+Ef|0)|0]|0)==(a[EC+124352|0]|0)){EC=EC+1|0}else{ED=Ef;EE=nd;EF=0;break L6385}}if((EC|0)!=4){ED=Ef;EE=nd;EF=0;break}gH=Ec+1|0;c[13898]=gH;L6457:do{if((gH|0)<(hB|0)){L6459:do{if((a[hp+(gH*40&-1)|0]&1)!=0){hR=c[hp+(gH*40&-1)+36>>2]|0;hK=hp+(gH*40&-1)+32|0;gz=0;while(1){if((gz|0)>=(hR|0)){break}if((a[ng+((c[hK>>2]|0)+gz|0)|0]|0)==(a[gz+103664|0]|0)){gz=gz+1|0}else{break L6459}}if((gz|0)==1){EG=0;break L6457}}}while(0);a[14176]=1;is(t);a[14176]=0;if((c[f5>>2]|0)==3){EG=c[f6>>2]|0;break}else{c[13898]=gH;EG=0;break}}else{EG=0}}while(0);uu(c[1184]|0);c[1184]=EG}gH=c[13898]|0;ng=c[8272]|0;if((gH|0)<(ng|0)){Ec=gH;hB=ng}else{break L6382}}if((b7|0)==4773){b7=0;ED=c[Ee>>2]|0;EE=(hB|0)>(Ec|0);EF=1}if(nh){EH=Ec;b7=5310;break L27}if(!EE){EH=Ec;b7=5311;break L27}L6476:do{if(!EF){gF=c[10036]|0;ng=0;while(1){if((ng|0)>=(Ed|0)){break}if((a[gF+(ng+ED|0)|0]|0)==(a[ng+103664|0]|0)){ng=ng+1|0}else{break L6476}}if((ng|0)==1){EH=Ec;b7=5312;break L27}}}while(0);a[14176]=1;is(u);a[14176]=0;if((c[f7>>2]|0)!=3){b7=4796;break L27}hB=c[f8>>2]|0;if((hB|0)==0){b7=4798;break L27}uu(c[1183]|0);c[1183]=hB;hB=c[13898]|0;gF=c[8272]|0;if((hB|0)<(gF|0)){nh=1;gC=hB;hS=gF}else{break L44}}}}while(0);if((c[1183]|0)!=0){break}c[1183]=bP(74304)|0}else if((gD|0)==94){jq(4416)}else if((gD|0)==99){hF=gv+1|0;c[13898]=hF;hG=c[8272]|0;hS=(hG|0)>(hF|0);L6489:do{if(hS){if((a[gw+(hF*40&-1)|0]&1)==0){break}gC=c[gw+(hF*40&-1)+36>>2]|0;nh=gw+(hF*40&-1)+32|0;gF=0;while(1){if((gF|0)>=(gC|0)){break}if((a[gB+((c[nh>>2]|0)+gF|0)|0]|0)==(a[gF+134552|0]|0)){gF=gF+1|0}else{break L6489}}if((gF|0)!=3){break}c[5094]=1;c[13898]=gv+2;break L44}}while(0);if((c[5094]|0)==1){if(a[20368]|0){a[20368]=0;g[3538]=+g[5090];g[3536]=+g[5088];g[3534]=+g[5086];c[16336]=c[16336]^2;c[17024]=c[17024]^2}c[5094]=0}nh=c[gw+(hF*40&-1)+36>>2]|0;gC=c[gw+(hF*40&-1)+32>>2]|0;L6504:do{if((nh|0)>0&(a[gw+(hF*40&-1)|0]&1)!=0){hB=0;gH=0;hp=gC;while(1){if((a[hB+82576|0]|0)==(a[gB+(hB+hp|0)|0]|0)){EI=hp;EJ=gH}else{if((hB|0)!=5){EK=0;EL=0;EM=gC;break}EI=hp-1|0;EJ=1}nd=hB+1|0;if((nd|0)<(EJ+nh|0)){hB=nd;gH=EJ;hp=EI}else{b7=4820;break}}do{if((b7|0)==4820){b7=0;if((EJ|0)==0){if(!((hB|0)==10|(hB|0)==4)){EK=0;EL=0;EM=gC;break}}hp=gv+2|0;c[13898]=hp;do{if((hp|0)<(hG|0)){if((a[gw+(hp*40&-1)|0]&1)==0){break L44}gH=c[gw+(hp*40&-1)+36>>2]|0;gF=gw+(hp*40&-1)+32|0;nd=0;while(1){if((nd|0)>=(gH|0)){b7=4827;break}if((a[gB+((c[gF>>2]|0)+nd|0)|0]|0)==(a[nd+103664|0]|0)){nd=nd+1|0}else{EN=0;break}}if((b7|0)==4827){b7=0;if((nd|0)==1){break}else{EN=0}}while(1){if((EN|0)>=(gH|0)){b7=4830;break}if((a[gB+((c[gF>>2]|0)+EN|0)|0]|0)==(a[EN+150688|0]|0)){EN=EN+1|0}else{EO=0;break}}if((b7|0)==4830){b7=0;if((EN|0)==2){break}else{EO=0}}while(1){if((EO|0)>=(gH|0)){break}if((a[gB+((c[gF>>2]|0)+EO|0)|0]|0)==(a[EO+79688|0]|0)){EO=EO+1|0}else{break L44}}if((EO|0)!=3){break L44}c[18072]=3;c[13898]=gv+3;break L44}}while(0);c[18072]=2;c[13898]=gv+3;break L44}}while(0);while(1){if((a[EK+78952|0]|0)==(a[gB+(EK+EM|0)|0]|0)){EP=EM;EQ=EL}else{if((EK|0)!=7){break L6504}EP=EM-1|0;EQ=1}hB=EK+1|0;if((hB|0)<(EQ+nh|0)){EK=hB;EL=EQ;EM=EP}else{break}}if((EQ|0)==0){if(!((EK|0)==6|(EK|0)==12)){break}}c[18072]=0;c[13898]=gv+2;break L44}}while(0);h[eW>>3]=+g[3538];h[eX>>3]=+g[3536];h[eY>>3]=+g[3534];h[eZ>>3]=+g[3532];L6546:do{if(hS){ER=hF;nh=1;hG=0;gC=gw;while(1){L6549:do{if((a[gC+(ER*40&-1)|0]&1)==0){b7=4853}else{hB=c[gC+(ER*40&-1)+36>>2]|0;hp=gC+(ER*40&-1)+32|0;gF=c[10036]|0;gH=0;while(1){if((gH|0)>=(hB|0)){b7=4848;break}if((a[gF+((c[hp>>2]|0)+gH|0)|0]|0)==(a[gH+103664|0]|0)){gH=gH+1|0}else{ES=0;break}}if((b7|0)==4848){b7=0;if((gH|0)==1){break L6546}else{ES=0}}while(1){if((ES|0)>=(hB|0)){break}if((a[gF+((c[hp>>2]|0)+ES|0)|0]|0)==(a[ES+148464|0]|0)){ES=ES+1|0}else{b7=4853;break L6549}}if((ES|0)!=1){b7=4853;break}c[13898]=ER+1;ET=1;EU=(nh&1)+hG|0}}while(0);if((b7|0)==4853){b7=0;if(!nh){b7=4854;break L27}hp=is(s)|0;gF=c[hp>>2]|0;if((gF|0)==1){EV=+(c[hp+8>>2]|0)}else if((gF|0)==2){EV=+h[hp+8>>3]}else if((gF|0)==3){EV=+uz(c[hp+8>>2]|0,0)}else{b7=4859;break L27}if((c[f9>>2]|0)==3){uu(c[ga>>2]|0);c[f9>>2]=1}h[ak+(hG<<3)>>3]=EV;ET=0;EU=hG+1|0}if((EU|0)>=4){break L6546}hp=c[13898]|0;if((hp|0)>=(c[8272]|0)){break L6546}ER=hp;nh=ET;hG=EU;gC=c[1054]|0}}}while(0);m3=+h[eW>>3];if(m3<0.0|m3>360.0){b7=4867;break L27}m2=+h[eX>>3];if(m2<0.0|m2>360.0){b7=4869;break L27}m5=+h[eY>>3];if(m5<1.0e-6){b7=4871;break L27}m4=+h[eZ>>3];if(m4<1.0e-6){b7=4873;break L27}g[3538]=m3;g[3536]=m2;m2=m5;g[3534]=m2;g[3532]=m4;g[3540]=+_(+m2)}else if((gD|0)==151){c[13898]=gv+1;hF=is(r)|0;hS=c[hF>>2]|0;do{if((hS|0)==1){gC=c[hF+8>>2]|0;EW=+(((gC|0)>-1?gC:-gC|0)|0)}else if((hS|0)==2){m2=+P(+(+h[hF+8>>3]));m4=+h[hF+16>>3];m5=+P(+m4);if(m4==0.0){EW=m2;break}if(m2>m5){m4=m5/m2;EW=m2*+Q(+(m4*m4+1.0));break}else{m4=m2/m5;EW=m5*+Q(+(m4*m4+1.0));break}}else{b7=4881;break L27}}while(0);h[11]=EW}else if((gD|0)==48|(gD|0)==49|(gD|0)==117|(gD|0)==118|(gD|0)==111|(gD|0)==112|(gD|0)==114|(gD|0)==115){jt(2)}else if((gD|0)==52|(gD|0)==53|(gD|0)==136|(gD|0)==137|(gD|0)==130|(gD|0)==131|(gD|0)==133|(gD|0)==134){jt(1)}else if((gD|0)==46|(gD|0)==47|(gD|0)==108|(gD|0)==109|(gD|0)==102|(gD|0)==103|(gD|0)==105|(gD|0)==106){jt(6)}else if((gD|0)==50|(gD|0)==51|(gD|0)==127|(gD|0)==128|(gD|0)==121|(gD|0)==122|(gD|0)==124|(gD|0)==125){jt(5)}else if((gD|0)==54|(gD|0)==55|(gD|0)==145|(gD|0)==146|(gD|0)==139|(gD|0)==140|(gD|0)==142|(gD|0)==143){jt(0)}else if((gD|0)==68|(gD|0)==69|(gD|0)==66|(gD|0)==67|(gD|0)==71|(gD|0)==72|(gD|0)==73|(gD|0)==74){jt(3)}else if((gD|0)==147|(gD|0)==148){jt(7)}else if((gD|0)==110){hF=gv+1|0;c[13898]=hF;c[16546]=0;hS=c[gw+(hF*40&-1)+36>>2]|0;L6597:do{if((a[gw+(hF*40&-1)|0]&1)!=0&(hS|0)>0){gC=0;hG=0;nh=c[gw+(hF*40&-1)+32>>2]|0;while(1){if((a[gC+84264|0]|0)==(a[gB+(gC+nh|0)|0]|0)){EX=nh;EY=hG}else{if((gC|0)!=1){EZ=0;break L6597}EX=nh-1|0;EY=1}hp=gC+1|0;if((hp|0)<(EY+hS|0)){gC=hp;hG=EY;nh=EX}else{break}}if((EY|0)==0){if(!((gC|0)==0|(gC|0)==4)){EZ=0;break}}c[16546]=1;c[13898]=gv+2;EZ=1}else{EZ=0}}while(0);c[17750]=EZ;c[17578]=EZ}else if((gD|0)==129){hS=gv+1|0;c[13898]=hS;c[16374]=0;hF=c[gw+(hS*40&-1)+36>>2]|0;L6611:do{if((a[gw+(hS*40&-1)|0]&1)!=0&(hF|0)>0){nh=0;hG=0;hp=c[gw+(hS*40&-1)+32>>2]|0;while(1){if((a[nh+84264|0]|0)==(a[gB+(nh+hp|0)|0]|0)){E_=hp;E$=hG}else{if((nh|0)!=1){break L6611}E_=hp-1|0;E$=1}gF=nh+1|0;if((gF|0)<(E$+hF|0)){nh=gF;hG=E$;hp=E_}else{break}}if((E$|0)==0){if(!((nh|0)==0|(nh|0)==4)){break}}c[16374]=1;c[13898]=gv+2}}while(0);c[17922]=c[16546]}else if((gD|0)==138){hF=gv+1|0;c[13898]=hF;c[16202]=0;hS=c[gw+(hF*40&-1)+36>>2]|0;if(!((a[gw+(hF*40&-1)|0]&1)!=0&(hS|0)>0)){break}hp=0;hG=0;gC=c[gw+(hF*40&-1)+32>>2]|0;while(1){if((a[hp+84264|0]|0)==(a[gB+(hp+gC|0)|0]|0)){E0=gC;E1=hG}else{if((hp|0)!=1){break L44}E0=gC-1|0;E1=1}hF=hp+1|0;if((hF|0)<(E1+hS|0)){hp=hF;hG=E1;gC=E0}else{break}}if((E1|0)==0){if(!((hp|0)==0|(hp|0)==4)){break}}c[16202]=1;c[13898]=gv+2}else if((gD|0)==70){gC=gv+1|0;c[13898]=gC;c[16718]=0;hG=c[gw+(gC*40&-1)+36>>2]|0;if(!((a[gw+(gC*40&-1)|0]&1)!=0&(hG|0)>0)){break}hS=0;hF=0;gF=c[gw+(gC*40&-1)+32>>2]|0;while(1){if((a[hS+84264|0]|0)==(a[gB+(hS+gF|0)|0]|0)){E2=gF;E3=hF}else{if((hS|0)!=1){break L44}E2=gF-1|0;E3=1}gC=hS+1|0;if((gC|0)<(E3+hG|0)){hS=gC;hF=E3;gF=E2}else{break}}if((E3|0)==0){if(!((hS|0)==0|(hS|0)==4)){break}}c[16718]=1;c[13898]=gv+2}else if((gD|0)==101){gF=gv+1|0;c[13898]=gF;c[17234]=0;hF=c[gw+(gF*40&-1)+36>>2]|0;if(!((a[gw+(gF*40&-1)|0]&1)!=0&(hF|0)>0)){break}hG=0;hp=0;gC=c[gw+(gF*40&-1)+32>>2]|0;while(1){if((a[hG+84264|0]|0)==(a[gB+(hG+gC|0)|0]|0)){E4=gC;E5=hp}else{if((hG|0)!=1){break L44}E4=gC-1|0;E5=1}gF=hG+1|0;if((gF|0)<(E5+hF|0)){hG=gF;hp=E5;gC=E4}else{break}}if((E5|0)==0){if(!((hG|0)==0|(hG|0)==4)){break}}c[17234]=1;c[13898]=gv+2}else if((gD|0)==120){gC=gv+1|0;c[13898]=gC;c[17062]=0;hp=c[gw+(gC*40&-1)+36>>2]|0;if(!((a[gw+(gC*40&-1)|0]&1)!=0&(hp|0)>0)){break}hF=0;hS=0;gF=c[gw+(gC*40&-1)+32>>2]|0;while(1){if((a[hF+84264|0]|0)==(a[gB+(hF+gF|0)|0]|0)){E6=gF;E7=hS}else{if((hF|0)!=1){break L44}E6=gF-1|0;E7=1}gC=hF+1|0;if((gC|0)<(E7+hp|0)){hF=gC;hS=E7;gF=E6}else{break}}if((E7|0)==0){if(!((hF|0)==0|(hF|0)==4)){break}}c[17062]=1;c[13898]=gv+2}else if((gD|0)==113){jq(66456)}else if((gD|0)==132){jq(65768)}else if((gD|0)==141){jq(65080)}else if((gD|0)==64){jq(67144)}else if((gD|0)==104){jq(69208)}else if((gD|0)==123){jq(68520)}else if((gD|0)==116){jv(2)}else if((gD|0)==107){jv(6)}else if((gD|0)==135){jv(1)}else if((gD|0)==126){jv(5)}else if((gD|0)==144){jv(0)}else if((gD|0)==65){jv(3)}else if((gD|0)==149){jv(7);if((a[30528]&1)==0){break}ju()}else if((gD|0)==95){jv(8)}else if((gD|0)==96){jv(9)}else if((gD|0)==100){jv(10)}else if((gD|0)==150){a[27776]=1;c[13898]=gv+1}else if((gD|0)==153){gF=gv+1|0;c[13898]=gF;L6691:do{if((gF|0)<(c[8272]|0)){L6693:do{if((a[gw+(gF*40&-1)|0]&1)!=0){hS=c[gw+(gF*40&-1)+36>>2]|0;hp=gw+(gF*40&-1)+32|0;hG=0;while(1){if((hG|0)>=(hS|0)){break}if((a[gB+((c[hp>>2]|0)+hG|0)|0]|0)==(a[hG+103664|0]|0)){hG=hG+1|0}else{break L6693}}if((hG|0)==1){break L6691}}}while(0);c[16665]=-1;hH(66656,1,0);if((gF|0)!=(c[13898]|0)){break L44}nh=is(q)|0;hp=c[nh>>2]|0;if((hp|0)==1){E8=+(c[nh+8>>2]|0)}else if((hp|0)==2){E8=+h[nh+8>>3]}else if((hp|0)==3){E8=+uz(c[nh+8>>2]|0,0)}else{b7=4976;break L27}if((c[gb>>2]|0)==3){uu(c[gc>>2]|0);c[gb>>2]=1}c[16665]=~~E8-1;break L44}}while(0);c[16665]=-1}else if((gD|0)==155){gF=gv+1|0;c[13898]=gF;L6711:do{if((gF|0)<(c[8272]|0)){L6713:do{if((a[gw+(gF*40&-1)|0]&1)!=0){hF=c[gw+(gF*40&-1)+36>>2]|0;nh=gw+(gF*40&-1)+32|0;hp=0;while(1){if((hp|0)>=(hF|0)){break}if((a[gB+((c[nh>>2]|0)+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{break L6713}}if((hp|0)==1){break L6711}}}while(0);c[16493]=-1;hH(65968,1,0);if((gF|0)!=(c[13898]|0)){break L44}nh=is(p)|0;hF=c[nh>>2]|0;if((hF|0)==1){E9=+(c[nh+8>>2]|0)}else if((hF|0)==2){E9=+h[nh+8>>3]}else if((hF|0)==3){E9=+uz(c[nh+8>>2]|0,0)}else{b7=4992;break L27}if((c[gd>>2]|0)==3){uu(c[ge>>2]|0);c[gd>>2]=1}c[16493]=~~E9-1;break L44}}while(0);c[16493]=-1}else if((gD|0)==157){gF=gv+1|0;c[13898]=gF;L6731:do{if((gF|0)<(c[8272]|0)){L6733:do{if((a[gw+(gF*40&-1)|0]&1)!=0){nh=c[gw+(gF*40&-1)+36>>2]|0;hF=gw+(gF*40&-1)+32|0;hG=0;while(1){if((hG|0)>=(nh|0)){break}if((a[gB+((c[hF>>2]|0)+hG|0)|0]|0)==(a[hG+103664|0]|0)){hG=hG+1|0}else{break L6733}}if((hG|0)==1){break L6731}}}while(0);c[16321]=-1;hH(65280,1,0);if((gF|0)!=(c[13898]|0)){break L44}hF=is(o)|0;nh=c[hF>>2]|0;if((nh|0)==1){Fa=+(c[hF+8>>2]|0)}else if((nh|0)==2){Fa=+h[hF+8>>3]}else if((nh|0)==3){Fa=+uz(c[hF+8>>2]|0,0)}else{b7=5008;break L27}if((c[gf>>2]|0)==3){uu(c[gg>>2]|0);c[gf>>2]=1}c[16321]=~~Fa-1;break L44}}while(0);c[16321]=-1}else if((gD|0)==154){gF=gv+1|0;c[13898]=gF;L6751:do{if((gF|0)<(c[8272]|0)){L6753:do{if((a[gw+(gF*40&-1)|0]&1)!=0){hF=c[gw+(gF*40&-1)+36>>2]|0;nh=gw+(gF*40&-1)+32|0;hp=0;while(1){if((hp|0)>=(hF|0)){break}if((a[gB+((c[nh>>2]|0)+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{break L6753}}if((hp|0)==1){break L6751}}}while(0);c[17353]=-1;hH(69408,1,0);if((gF|0)!=(c[13898]|0)){break L44}nh=is(n)|0;hF=c[nh>>2]|0;if((hF|0)==1){Fb=+(c[nh+8>>2]|0)}else if((hF|0)==2){Fb=+h[nh+8>>3]}else if((hF|0)==3){Fb=+uz(c[nh+8>>2]|0,0)}else{b7=5024;break L27}if((c[gh>>2]|0)==3){uu(c[gi>>2]|0);c[gh>>2]=1}c[17353]=~~Fb-1;break L44}}while(0);c[17353]=-1}else if((gD|0)==156){gF=gv+1|0;c[13898]=gF;L6771:do{if((gF|0)<(c[8272]|0)){L6773:do{if((a[gw+(gF*40&-1)|0]&1)!=0){nh=c[gw+(gF*40&-1)+36>>2]|0;hF=gw+(gF*40&-1)+32|0;hG=0;while(1){if((hG|0)>=(nh|0)){break}if((a[gB+((c[hF>>2]|0)+hG|0)|0]|0)==(a[hG+103664|0]|0)){hG=hG+1|0}else{break L6773}}if((hG|0)==1){break L6771}}}while(0);c[17181]=-1;hH(68720,1,0);if((gF|0)!=(c[13898]|0)){break L44}hF=is(l)|0;nh=c[hF>>2]|0;if((nh|0)==1){Fc=+(c[hF+8>>2]|0)}else if((nh|0)==2){Fc=+h[hF+8>>3]}else if((nh|0)==3){Fc=+uz(c[hF+8>>2]|0,0)}else{b7=5040;break L27}if((c[gj>>2]|0)==3){uu(c[gl>>2]|0);c[gj>>2]=1}c[17181]=~~Fc-1;break L44}}while(0);c[17181]=-1}else if((gD|0)==152){gF=gv+1|0;c[13898]=gF;L6791:do{if((gF|0)<(c[8272]|0)){L6793:do{if((a[gw+(gF*40&-1)|0]&1)!=0){hF=c[gw+(gF*40&-1)+36>>2]|0;nh=gw+(gF*40&-1)+32|0;hp=0;while(1){if((hp|0)>=(hF|0)){break}if((a[gB+((c[nh>>2]|0)+hp|0)|0]|0)==(a[hp+103664|0]|0)){hp=hp+1|0}else{break L6793}}if((hp|0)==1){b7=5050;break L6791}}}while(0);c[16665]=-1;hH(66656,1,0);if((gF|0)!=(c[13898]|0)){break}nh=is(k)|0;hF=c[nh>>2]|0;if((hF|0)==1){Fd=+(c[nh+8>>2]|0)}else if((hF|0)==2){Fd=+h[nh+8>>3]}else if((hF|0)==3){Fd=+uz(c[nh+8>>2]|0,0)}else{b7=5056;break L27}if((c[gm>>2]|0)==3){uu(c[gn>>2]|0);c[gm>>2]=1}c[16665]=~~Fd-1}else{b7=5050}}while(0);if((b7|0)==5050){b7=0;c[16665]=-1}c[16492]=c[16664];c[65972>>2]=c[66660>>2];c[65976>>2]=c[66664>>2];c[65980>>2]=c[66668>>2];c[65984>>2]=c[66672>>2];c[65988>>2]=c[66676>>2];c[65992>>2]=c[66680>>2];c[65996>>2]=c[66684>>2];c[66e3>>2]=c[66688>>2];c[66004>>2]=c[66692>>2];c[66008>>2]=c[66696>>2];c[66012>>2]=c[66700>>2];c[66016>>2]=c[66704>>2];c[66020>>2]=c[66708>>2];c[16320]=c[16664];c[65284>>2]=c[66660>>2];c[65288>>2]=c[66664>>2];c[65292>>2]=c[66668>>2];c[65296>>2]=c[66672>>2];c[65300>>2]=c[66676>>2];c[65304>>2]=c[66680>>2];c[65308>>2]=c[66684>>2];c[65312>>2]=c[66688>>2];c[65316>>2]=c[66692>>2];c[65320>>2]=c[66696>>2];c[65324>>2]=c[66700>>2];c[65328>>2]=c[66704>>2];c[65332>>2]=c[66708>>2]}else if((gD|0)==119){gF=gv+1|0;c[13898]=gF;nh=a[gw+(gF*40&-1)|0]&1;L6813:do{if((c[8272]|0)>(gF|0)){if(nh<<24>>24==0){Fe=0;b7=5066;break}hF=c[gw+(gF*40&-1)+36>>2]|0;hG=gw+(gF*40&-1)+32|0;hS=0;while(1){if((hS|0)>=(hF|0)){break}if((a[gB+((c[hG>>2]|0)+hS|0)|0]|0)==(a[hS+95280|0]|0)){hS=hS+1|0}else{Fe=nh;b7=5066;break L6813}}if((hS|0)!=2){Ff=hF;Fg=nh;b7=5075;break}c[13898]=gv+2;hG=is(f)|0;gC=c[hG>>2]|0;if((gC|0)==1){Fh=+(c[hG+8>>2]|0)}else if((gC|0)==2){Fh=+h[hG+8>>3]}else if((gC|0)==3){Fh=+uz(c[hG+8>>2]|0,0)}else{b7=5072;break L27}if((c[go>>2]|0)!=3){Fi=1;Fj=Fh;break}uu(c[gp>>2]|0);c[go>>2]=1;Fi=1;Fj=Fh}else{Fe=nh;b7=5066}}while(0);if((b7|0)==5066){b7=0;Ff=c[gw+(gF*40&-1)+36>>2]|0;Fg=Fe;b7=5075}do{if((b7|0)==5075){b7=0;L6831:do{if(Fg<<24>>24!=0&(Ff|0)>0){nh=0;hG=0;gC=c[gw+(gF*40&-1)+32>>2]|0;while(1){if((a[nh+72944|0]|0)==(a[gB+(nh+gC|0)|0]|0)){Fk=gC;Fl=hG}else{if((nh|0)!=3){b7=5083;break L6831}Fk=gC-1|0;Fl=1}hB=nh+1|0;if((hB|0)<(Fl+Ff|0)){nh=hB;hG=Fl;gC=Fk}else{break}}if((Fl|0)!=0){Fm=gF;break}if((nh|0)==2|(nh|0)==8){Fm=gF}else{b7=5083}}else{b7=5083}}while(0);if((b7|0)==5083){b7=0;c[13898]=gv;Fm=gv}c[13898]=Fm+1;hF=is(j)|0;hS=c[hF>>2]|0;if((hS|0)==1){Fn=+(c[hF+8>>2]|0)}else if((hS|0)==2){Fn=+h[hF+8>>3]}else if((hS|0)==3){Fn=+uz(c[hF+8>>2]|0,0)}else{b7=5088;break L27}if((c[gq>>2]|0)!=3){Fi=0;Fj=Fn;break}uu(c[gs>>2]|0);c[gq>>2]=1;Fi=0;Fj=Fn}}while(0);h[77]=Fj;a[624]=Fi}else if((gD|0)==91){c[13898]=gv+1;gF=is(e)|0;hF=c[gF>>2]|0;if((hF|0)==1){Fo=+(c[gF+8>>2]|0)}else if((hF|0)==2){Fo=+h[gF+8>>3]}else if((hF|0)==3){Fo=+uz(c[gF+8>>2]|0,0)}else{b7=5096;break L27}if((c[gt>>2]|0)==3){uu(c[gu>>2]|0);c[gt>>2]=1}h[77]=Fo;a[624]=0}else{b7=5100;break L27}}while(0);if(!(ix(c[6074]|0)|0)){break L7}c[13898]=cb;gv=cb}if((b7|0)==109){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==114){uf(c[13898]|0,124136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==121){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=123984,v)|0)}else if((b7|0)==780){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==815){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==831){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==918){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==953){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=129728,v)|0)}else if((b7|0)==974){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==978){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==998){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1002){uk(132480,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1020){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1034){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1038){uf(mD,127632,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1053){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1073){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1078){uf(me,127264,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1083){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1097){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1101){uf(c[13898]|0,127040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1165){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1194){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1207){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1224){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1232){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1240){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1244){uf(no,129896,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1251){uf(-1,129624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1284){uf((c[13898]|0)-1|0,130240,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1292){c[13898]=nE;b7=1293}else if((b7|0)==1295){Fp=c[13898]|0;uf(Fp,132744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1360){uf(nT,131288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1402){c[13898]=n7;b7=1403}else if((b7|0)==1405){Fq=c[13898]|0;uf(Fq,132744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1452){c[13898]=oj;b7=1453}else if((b7|0)==1462){Fr=c[13898]|0;uf(Fr,140752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1693){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1742){uf(gv,154168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1747){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1764){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1769){uf(c[13898]|0,173664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1867){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1877){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1885){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1893){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1901){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1974){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1997){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2007){uf(qT,136864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2054){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2152){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2158){uf(c[13898]|0,208936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2165){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=201712,v)|0)}else if((b7|0)==2198){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=138936,v)|0)}else if((b7|0)==2201){uu(r3);uu(r2);uf(c[13898]|0,138632,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2206){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2270){c[13898]=sl;b7=2271}else if((b7|0)==2280){Fs=c[13898]|0;uf(Fs,140752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2297){c[13898]=sr;st=sr;uf(st,140752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2299){st=c[13898]|0;uf(st,140752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2312){c[sv>>2]=sw;uf(c[13898]|0,174808,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2324){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2328){uf(c[13898]|0,140880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2398){c[13898]=sX;b7=2399}else if((b7|0)==2401){Ft=c[13898]|0;uf(Ft,143264,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2437){uf(s$,142184,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2458){c[13898]=ta;tc=ta;uf(tc,142920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2460){tc=c[13898]|0;uf(tc,142920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2520){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2698){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2740){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2816){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2838){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2860){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2882){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2897){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2911){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2917){uf(vH,179040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2930){c[13898]=vH;b7=2931}else if((b7|0)==2933){Fu=c[13898]|0;uf(Fu,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2983){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2997){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3049){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3116){uf(vK,164e3,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3153){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3161){uf(wg,159752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3166){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3170){uf(c[13898]|0,159064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3182){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3186){uf(c[13898]|0,159064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3198){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3202){uf(c[13898]|0,159064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3205){uf(wg,159752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3218){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3234){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3249){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3255){uf(wg,159752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3258){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=151392,v)|0)}else if((b7|0)==3282){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3296){uf((c[13898]|0)-1|0,150248,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3302){uf((c[13898]|0)-1|0,150080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3308){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3312){uf((c[13898]|0)-1|0,149880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3317){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3321){uf((c[13898]|0)-1|0,149880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3326){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3330){uf((c[13898]|0)-1|0,149880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3347){uf(w$,150736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3353){uf(wg,159752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3361){c[13898]=xi;xj=xi;uf(xj,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3363){xj=c[13898]|0;uf(xj,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3366){uf(c[13898]|0,151816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3371){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=151632,v)|0)}else if((b7|0)==3373){ez();Fv=c[13898]|0;uf(Fv,150896,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3397){ez();uf(c[13898]|0,151112,(v=i,i=i+8|0,c[v>>2]=c[11900],v)|0)}else if((b7|0)==3404){uf(c[13898]|0,150736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3409){uf(wg,159752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3413){uf(xx,152776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3427){uf(xA,152776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3441){uf(xD,152776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3459){uf(xF,154672,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3467){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3471){uf(c[13898]|0,154376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3473){uf(wg,154e3,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3537){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3541){c[13505]=0;uf(c[13898]|0,163320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3561){uf(xO,162224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3576){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3590){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3614){c[13898]=x5;b7=3615}else if((b7|0)==3617){Fw=c[13898]|0;uf(Fw,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3642){c[13898]=ya;b7=3643}else if((b7|0)==3645){Fx=c[13898]|0;uf(Fx,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3678){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3683){uf(yz,208936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3719){uf(yf,171872,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3720){uf(yf,171504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3725){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3742){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3747){uf(c[13898]|0,173664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3775){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3802){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3815){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3821){g[38]=1.0;g[178]=1.0;uf(-1,174280,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3845){uf(zN,143640,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3860){uf(zT,143640,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3879){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3884){uf(c[13898]|0,208936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3894){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=176728,v)|0)}else if((b7|0)==3974){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4075){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4079){uf(AC,193912,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4110){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4186){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4197){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4213){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4217){uf((c[13898]|0)-1|0,204376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4243){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4247){uf((c[13898]|0)-1|0,203376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4290){uf(zm,208496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4304){uj(c[13898]|0,214704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4308){uf(BU,217328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4376){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4392){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4493){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4506){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4527){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4668){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4686){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4714){c[13898]=Ea;Fy=Ea;uf(Fy,73560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4722){c[13898]=D4;Fy=D4;uf(Fy,73560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4725){Fy=c[13898]|0;uf(Fy,73560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4796){c[13898]=Ec;EH=Ec;uf(EH,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4798){EH=c[13898]|0;uf(EH,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4854){uf(ER,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4859){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4867){uf(c[13898]|0,21904,(v=i,i=i+16|0,c[v>>2]=120,c[v+8>>2]=360,v)|0)}else if((b7|0)==4869){uf(c[13898]|0,21904,(v=i,i=i+16|0,c[v>>2]=122,c[v+8>>2]=360,v)|0)}else if((b7|0)==4871){uf(c[13898]|0,21864,(v=i,i=i+8|0,c[v>>2]=179864,v)|0)}else if((b7|0)==4873){uf(c[13898]|0,21864,(v=i,i=i+8|0,c[v>>2]=77416,v)|0)}else if((b7|0)==4881){uf(-1,211136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4976){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==4992){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5008){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5024){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5040){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5056){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5072){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5088){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5096){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5100){uf(gv,204968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5107){uf(gN,122840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5108){uf(gN,122840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5109){uf(gN,122840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5110){uf(gN,122840,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5111){uf(hT,123864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5112){uf(hT,123864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5113){uf(hQ,123776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5114){uf(hQ,123776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5115){uf(hq,123648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5116){uf(hq,123648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5117){uf(hq,123648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5118){uf(hq,123512,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5119){uf(hq,123512,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5120){uf(kI,124880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5121){uf(kI,124880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5122){uf(kI,124880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5123){uf(lc,126168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5124){uf(lc,126168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5125){uf(lc,126168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5126){uf(lc,126168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5127){uf(lt,139712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5128){uf(lt,139712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5129){uf(lt,139712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5130){uf(lt,139712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5131){uf(mj,128168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5132){uf(mj,128168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5133){uf(mn,128008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5134){uf(mn,128008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5135){uf(mn,128008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5136){uf(mn,128008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5137){uf(mB,127784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5138){uf(mB,127784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5139){uf(mB,127784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5140){uf(mB,127784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5141){uf(mD,127528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5142){uf(mD,127528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5143){uf(mD,127528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5144){uf(lL,126816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5145){uf(lL,126816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5146){uf(lL,126816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5147){uf(lL,126816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5148){uf(mN,128944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5149){uf(mN,128944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5150){uf(mN,128944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5151){uf(mN,128944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5152){uf(-1,129776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5153){uf(-1,129776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5156){uf(nK,130792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5157){uf(nK,130792,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5158){uf(nQ,130664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5159){uf(nQ,130664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5162){uf(n_,132152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5163){uf(n_,132152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5164){uf(n_,132152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5165){uf(n_,132152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5168){uf(qP,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5169){uf(qP,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5170){uf(qP,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5171){uf(rd,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5172){uf(rd,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5173){uf(rd,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5174){uf(rd,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5175){uu(r2);uu(r6);Fz=c[13898]|0;uf(Fz,137984,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5176){uu(r2);uu(r6);Fz=c[13898]|0;uf(Fz,137984,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5177){uu(r2);uu(r6);Fz=c[13898]|0;uf(Fz,137984,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5178){uu(r2);uu(r6);Fz=c[13898]|0;uf(Fz,137984,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5181){uf(st,140752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5184){uf(sA,141216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5185){uf(sA,141216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5186){uf(sA,141216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5187){uf(sA,141216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5190){uf(s$,142560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5191){uf(s$,142560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5192){uf(tc,142920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5195){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5196){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5197){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5198){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5199){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5200){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5201){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5202){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5203){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5204){uf(sR,211296,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5205){uf(vo,143744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5206){uf(vo,143744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5207){uf(tq,143392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5208){uf(tq,143392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5209){uf(tq,143392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5210){uf(vE,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5211){uf(vE,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5212){uf(vE,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5213){uf(vE,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5216){uf(vT,170376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5217){uf(vT,170376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5218){uf(vV,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5219){uf(vV,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5220){uf(vV,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5221){uf(vV,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5222){uf(vX,169096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5223){uf(vX,169096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5224){uf(vX,169096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5225){uf(vX,169096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5226){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5227){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5228){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5229){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5230){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5231){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5232){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5233){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5234){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5235){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5236){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5237){uf(wd,164440,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5238){uf(wO,150560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5239){uf(wO,150560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5240){uf(wO,150560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5241){uf(xe,149736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5242){uf(xe,149736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5243){uf(xe,149736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5244){uf(xe,149736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5245){uf(xj,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5246){uf(xj,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5250){Fv=c[13898]|0;uf(Fv,150896,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5251){uf(xz,152392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5252){uf(xz,152392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5253){uf(xz,152392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5254){uf(xz,152392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5255){uf(xC,152392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5256){uf(xC,152392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5257){uf(xC,152392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5258){uf(xC,152392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5259){uf(xF,155040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5260){uf(xF,155040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5261){uf(x$,162848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5262){uf(x$,162848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5263){uf(x0,162848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5264){uf(x0,162848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5265){uf(x7,148728,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5266){uf(x7,148728,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5267){uf(x7,148728,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5272){uf(yU,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5273){uf(yU,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5274){uf(yU,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5275){uf(zT,214008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5276){uf(zT,214008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5277){uf(z7,176128,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5278){uf(z7,176128,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5279){uf(z7,176128,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5280){uf(zF,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5281){uf(zF,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5282){uf(zF,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5283){uf(zF,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5284){uf(zs,209456,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5285){uf(zs,209456,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5286){uf(zs,209456,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5287){uf(zs,209456,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5288){uf(AX,200192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5289){uf(AX,200192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5290){uf(AX,200192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5291){uf(AX,200192,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5292){uf(AX,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5293){uf(AX,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5294){uf(AX,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5295){uf(AX,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5296){uf(BW,217888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5297){uf(BW,217888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5298){uf(BW,217888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5299){uf(DU,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5300){uf(DU,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5301){uf(DU,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5302){uf(DU,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5303){uf(Ci,224656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5304){uf(Ci,224656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5305){uf(Ci,224656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5306){uf(Ci,224656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5310){uf(EH,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5311){uf(EH,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==5312){uf(EH,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((b7|0)==1293){c[12886]=0;Fp=nE;uf(Fp,132744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1403){c[10568]=0;Fq=n7;uf(Fq,132744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==1453){c[bv>>2]=0;Fr=oj;uf(Fr,140752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2271){c[a8>>2]=0;Fs=sl;uf(Fs,140752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2399){c[8528]=0;Ft=sX;uf(Ft,143264,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==2931){c[aF>>2]=0;Fu=vH;uf(Fu,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3615){c[ad>>2]=0;Fw=x5;uf(Fw,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b7|0)==3643){c[57238]=0;Fx=ya;uf(Fx,149040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);e7(0);Fx=c[6074]|0;if((Fx|0)!=0){ya=Fx;while(1){Fx=c[ya>>2]|0;uu(c[ya+12>>2]|0);uu(ya);if((Fx|0)==0){break}else{ya=Fx}}}c[6074]=0;i=d;return}function jp(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0;b=i;i=i+40|0;d=b|0;e=b+24|0;f=e|0;g=e|0;uE(g|0,0,11);h=c[13898]|0;j=h+1|0;c[13898]=j;k=c[1054]|0;l=c[k+(j*40&-1)+36>>2]|0;m=c[k+(j*40&-1)+32>>2]|0;n=(a[k+(j*40&-1)|0]&1)==0;o=(l|0)>0;p=c[10036]|0;q=56720;r=77416;L7279:while(1){L7281:do{if(!n){if(o){s=0;t=0;u=m;while(1){w=a[r+s|0]|0;if(w<<24>>24==(a[p+(s+u|0)|0]|0)){x=u;y=t}else{if(w<<24>>24!=36){break L7281}x=u-1|0;y=1}z=s+1|0;if((z|0)<(y+l|0)){s=z;t=y;u=x}else{break}}if((y|0)==0){A=z}else{B=q;break L7279}}else{A=0}u=a[r+A|0]|0;if((u<<24>>24|0)==36|(u<<24>>24|0)==0){B=q;break L7279}}}while(0);u=q+8|0;t=c[u>>2]|0;if((t|0)==0){B=u;break}else{q=u;r=t}}r=c[B+4>>2]|0;L7294:do{if((r|0)>-1){a[e+r|0]=1;B=h+2|0;c[13898]=B;C=B;D=c[8272]|0}else{B=c[8272]|0;q=(B|0)>(j|0);L7296:do{if(!(n|q^1)){A=0;while(1){if((A|0)>=(l|0)){E=5332;break}if((a[p+(m+A|0)|0]|0)==(a[A+150688|0]|0)){A=A+1|0}else{E=5333;break}}if((E|0)==5332){if((A|0)!=2){E=5333}}if((E|0)==5333){if(n|q^1){break}else{F=0}while(1){if((F|0)>=(l|0)){break}if((a[p+(m+F|0)|0]|0)==(a[F+148800|0]|0)){F=F+1|0}else{break L7296}}if((F|0)!=2){break}}a[e+1|0]=1;a[e+2|0]=1;A=h+2|0;c[13898]=A;C=A;D=B;break L7294}}while(0);uE(f|0,1,11);C=j;D=B}}while(0);L7312:do{if((C|0)<(D|0)){j=(a[k+(C*40&-1)|0]&1)==0;L7314:do{if(j){G=d}else{f=c[k+(C*40&-1)+36>>2]|0;h=k+(C*40&-1)+32|0;F=0;while(1){if((F|0)>=(f|0)){E=5345;break}if((a[p+((c[h>>2]|0)+F|0)|0]|0)==(a[F+103664|0]|0)){F=F+1|0}else{break}}if((E|0)==5345){if((F|0)==1){break L7312}}h=d;if(j){G=h;break}f=c[k+(C*40&-1)+36>>2]|0;m=k+(C*40&-1)+32|0;l=0;while(1){if((l|0)>=(f|0)){break}if((a[p+((c[m>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{G=h;break L7314}}if((l|0)==1){H=C}else{G=h;break}uf(H,133088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)!=3){c[13898]=C;H=C;uf(H,133088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=c[d+8>>2]|0;if((j|0)==0){H=c[13898]|0;uf(H,133088,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[e+2|0]&1)!=0){uF(66240,j|0,50);B=a8(j|0,37)|0;do{if((B|0)==0){I=0}else{m=B+1|0;L7342:do{if((m|0)==0){J=0}else{f=m;while(1){F=a[f]|0;if(!((F<<24>>24|0)==32|(F<<24>>24|0)==45|(F<<24>>24|0)==43|(F<<24>>24|0)==35)){J=f;break L7342}F=f+1|0;if((F|0)==0){J=0;break}else{f=F}}}}while(0);while(1){K=a[J]|0;if(!(((K&255)-48|0)>>>0<10|K<<24>>24==46)){break}J=J+1|0}if((K-102&255)<2){I=1;break}I=K<<24>>24==101&1}}while(0);a[66188]=I}if((a[e+1|0]&1)!=0){uF(65552,j|0,50);B=a8(j|0,37)|0;do{if((B|0)==0){L=0}else{m=B+1|0;L7357:do{if((m|0)==0){M=0}else{h=m;while(1){l=a[h]|0;if(!((l<<24>>24|0)==32|(l<<24>>24|0)==45|(l<<24>>24|0)==43|(l<<24>>24|0)==35)){M=h;break L7357}l=h+1|0;if((l|0)==0){M=0;break}else{h=l}}}}while(0);while(1){N=a[M]|0;if(!(((N&255)-48|0)>>>0<10|N<<24>>24==46)){break}M=M+1|0}if((N-102&255)<2){L=1;break}L=N<<24>>24==101&1}}while(0);a[65500]=L}if((a[g]&1)!=0){uF(64864,j|0,50);B=a8(j|0,37)|0;do{if((B|0)==0){O=0}else{m=B+1|0;L7372:do{if((m|0)==0){P=0}else{h=m;while(1){l=a[h]|0;if(!((l<<24>>24|0)==32|(l<<24>>24|0)==45|(l<<24>>24|0)==43|(l<<24>>24|0)==35)){P=h;break L7372}l=h+1|0;if((l|0)==0){P=0;break}else{h=l}}}}while(0);while(1){Q=a[P]|0;if(!(((Q&255)-48|0)>>>0<10|Q<<24>>24==46)){break}P=P+1|0}if((Q-102&255)<2){O=1;break}O=Q<<24>>24==101&1}}while(0);a[64812]=O}if((a[e+6|0]&1)!=0){uF(68992,j|0,50);B=a8(j|0,37)|0;do{if((B|0)==0){R=0}else{m=B+1|0;L7387:do{if((m|0)==0){S=0}else{h=m;while(1){l=a[h]|0;if(!((l<<24>>24|0)==32|(l<<24>>24|0)==45|(l<<24>>24|0)==43|(l<<24>>24|0)==35)){S=h;break L7387}l=h+1|0;if((l|0)==0){S=0;break}else{h=l}}}}while(0);while(1){T=a[S]|0;if(!(((T&255)-48|0)>>>0<10|T<<24>>24==46)){break}S=S+1|0}if((T-102&255)<2){R=1;break}R=T<<24>>24==101&1}}while(0);a[68940]=R}if((a[e+5|0]&1)!=0){uF(68304,j|0,50);B=a8(j|0,37)|0;do{if((B|0)==0){U=0}else{m=B+1|0;L7402:do{if((m|0)==0){V=0}else{h=m;while(1){l=a[h]|0;if(!((l<<24>>24|0)==32|(l<<24>>24|0)==45|(l<<24>>24|0)==43|(l<<24>>24|0)==35)){V=h;break L7402}l=h+1|0;if((l|0)==0){V=0;break}else{h=l}}}}while(0);while(1){W=a[V]|0;if(!(((W&255)-48|0)>>>0<10|W<<24>>24==46)){break}V=V+1|0}if((W-102&255)<2){U=1;break}U=W<<24>>24==101&1}}while(0);a[68252]=U}if((a[e+3|0]&1)!=0){uF(66928,j|0,50);B=a8(j|0,37)|0;do{if((B|0)==0){X=0}else{m=B+1|0;L7417:do{if((m|0)==0){Y=0}else{h=m;while(1){l=a[h]|0;if(!((l<<24>>24|0)==32|(l<<24>>24|0)==45|(l<<24>>24|0)==43|(l<<24>>24|0)==35)){Y=h;break L7417}l=h+1|0;if((l|0)==0){Y=0;break}else{h=l}}}}while(0);while(1){Z=a[Y]|0;if(!(((Z&255)-48|0)>>>0<10|Z<<24>>24==46)){break}Y=Y+1|0}if((Z-102&255)<2){X=1;break}X=Z<<24>>24==101&1}}while(0);a[66876]=X}if((a[e+7|0]&1)!=0){uF(69680,j|0,50);B=a8(j|0,37)|0;do{if((B|0)==0){_=0}else{m=B+1|0;L7432:do{if((m|0)==0){$=0}else{h=m;while(1){l=a[h]|0;if(!((l<<24>>24|0)==32|(l<<24>>24|0)==45|(l<<24>>24|0)==43|(l<<24>>24|0)==35)){$=h;break L7432}l=h+1|0;if((l|0)==0){$=0;break}else{h=l}}}}while(0);while(1){aa=a[$]|0;if(!(((aa&255)-48|0)>>>0<10|aa<<24>>24==46)){break}$=$+1|0}if((aa-102&255)<2){_=1;break}_=aa<<24>>24==101&1}}while(0);a[69628]=_}uu(j);i=b;return}}while(0);if((a[e+2|0]&1)!=0){c[16560]=6758437;a[66188]=1}if((a[e+1|0]&1)!=0){c[16388]=6758437;a[65500]=1}if((a[g]&1)!=0){c[16216]=6758437;a[64812]=1}if((a[e+6|0]&1)!=0){c[17248]=6758437;a[68940]=1}if((a[e+5|0]&1)!=0){c[17076]=6758437;a[68252]=1}if((a[e+3|0]&1)!=0){c[16732]=6758437;a[66876]=1}if((a[e+7|0]&1)==0){i=b;return}c[17420]=6758437;a[69628]=1;i=b;return}function jq(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;d=i;i=i+24|0;e=d|0;f=(c[13898]|0)+1|0;c[13898]=f;L7468:do{if((f|0)<(c[8272]|0)){g=c[1054]|0;L7470:do{if((a[g+(f*40&-1)|0]&1)!=0){h=c[g+(f*40&-1)+36>>2]|0;j=g+(f*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(h|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{break L7470}}if((l|0)==1){break L7468}}}while(0);jw(b);g=c[13898]|0;L7477:do{if((g|0)<(c[8272]|0)){j=c[1054]|0;k=(a[j+(g*40&-1)|0]&1)==0;L7479:do{if(k){m=e}else{h=c[j+(g*40&-1)+36>>2]|0;n=j+(g*40&-1)+32|0;o=c[10036]|0;p=0;while(1){if((p|0)>=(h|0)){q=5461;break}if((a[o+((c[n>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{break}}if((q|0)==5461){if((p|0)==1){break L7477}}n=e;if(k){m=n;break}o=c[j+(g*40&-1)+36>>2]|0;h=j+(g*40&-1)+32|0;r=c[10036]|0;s=0;while(1){if((s|0)>=(o|0)){break}if((a[r+((c[h>>2]|0)+s|0)|0]|0)==(a[s+103664|0]|0)){s=s+1|0}else{m=n;break L7479}}if((s|0)==1){break L7477}else{m=n}}}while(0);a[14176]=1;is(e);a[14176]=0;if((c[e>>2]|0)!=3){c[13898]=g;break}j=c[e+8>>2]|0;if((j|0)==0){break}k=b+60|0;uu(c[k>>2]|0);c[k>>2]=j}}while(0);jw(b);i=d;return}}while(0);e=b+60|0;uu(c[e>>2]|0);c[e>>2]=0;i=d;return}function jr(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;d=i;i=i+48|0;e=d|0;f=d+24|0;g=(c[13898]|0)+1|0;c[13898]=g;if((g|0)>=(c[8272]|0)){j=g;uf(j,208936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=c[1054]|0;L7506:do{if((a[k+(g*40&-1)|0]&1)!=0){l=c[k+(g*40&-1)+36>>2]|0;m=k+(g*40&-1)+32|0;n=c[10036]|0;o=0;while(1){if((o|0)>=(l|0)){break}if((a[n+((c[m>>2]|0)+o|0)|0]|0)==(a[o+103664|0]|0)){o=o+1|0}else{break L7506}}if((o|0)==1){j=g}else{break}uf(j,208936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);g=is(e)|0;k=c[g>>2]|0;if((k|0)==3){p=+uz(c[g+8>>2]|0,0)}else if((k|0)==2){p=+h[g+8>>3]}else if((k|0)==1){p=+(c[g+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=e|0;if((c[g>>2]|0)==3){uu(c[e+8>>2]|0);c[g>>2]=1}g=~~p;if((g|0)<1){j=c[13898]|0;uf(j,208936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{q=b;r=0}while(1){s=c[q>>2]|0;if((s|0)==0){t=0;u=5494;break}w=c[s+4>>2]|0;if((g|0)>(w|0)){q=s|0;r=s}else{u=5493;break}}if((u|0)==5493){if((g|0)==(w|0)){x=s}else{t=s;u=5494}}if((u|0)==5494){s=f|0;uE(s|0,0,24);f=g-1|0;w=ut(64)|0;do{if((w|0)==0){gk();q=ut(64)|0;if((q|0)!=0){y=q;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=128152,v)|0)}else{y=w}}while(0);w=y;if((r|0)==0){c[b>>2]=w}else{c[r>>2]=w}c[y+4>>2]=g;c[y>>2]=t;c[y+8>>2]=0;c[y+12>>2]=f;c[y+16>>2]=f;c[y+20>>2]=0;h[y+24>>3]=1.0;h[y+32>>3]=-2.0;f=y+40|0;c[f>>2]=c[s>>2];c[f+4>>2]=c[s+4>>2];c[f+8>>2]=c[s+8>>2];c[f+12>>2]=c[s+12>>2];c[f+16>>2]=c[s+16>>2];c[f+20>>2]=c[s+20>>2];x=w}w=c[13898]|0;s=c[1054]|0;f=c[s+(w*40&-1)+36>>2]|0;L7542:do{if((a[s+(w*40&-1)|0]&1)!=0&(f|0)>0){y=c[10036]|0;t=0;g=0;q=c[s+(w*40&-1)+32>>2]|0;while(1){if((a[t+184456|0]|0)==(a[y+(t+q|0)|0]|0)){z=q;A=g}else{if((t|0)!=3){u=5515;break L7542}z=q-1|0;A=1}j=t+1|0;if((j|0)<(A+f|0)){t=j;g=A;q=z}else{break}}if((A|0)==0){if(!((t|0)==2|(t|0)==7)){u=5515;break}}if((x|0)==0){B=w}else{q=c[x>>2]|0;if((c[b>>2]|0)==(x|0)){c[b>>2]=q}else{c[r>>2]=q}uu(x);B=c[13898]|0}q=B+1|0;c[13898]=q;C=q}else{u=5515}}while(0);if((u|0)==5515){B=x+8|0;hH(B,0,1);C=c[13898]|0}if((C|0)>=(c[8272]|0)){i=d;return}B=c[1054]|0;if((a[B+(C*40&-1)|0]&1)==0){D=(b|0)==43264;E=D?124560:123144;uf(C,125776,(v=i,i=i+8|0,c[v>>2]=E,v)|0)}x=c[B+(C*40&-1)+36>>2]|0;r=B+(C*40&-1)+32|0;B=c[10036]|0;w=0;while(1){if((w|0)>=(x|0)){break}if((a[B+((c[r>>2]|0)+w|0)|0]|0)==(a[w+103664|0]|0)){w=w+1|0}else{u=5530;break}}if((u|0)==5530){D=(b|0)==43264;E=D?124560:123144;uf(C,125776,(v=i,i=i+8|0,c[v>>2]=E,v)|0)}if((w|0)==1){i=d;return}else{D=(b|0)==43264;E=D?124560:123144;uf(C,125776,(v=i,i=i+8|0,c[v>>2]=E,v)|0)}}function js(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0.0,F=0.0;d=i;i=i+24|0;e=d|0;f=b|0;c[f>>2]=4;g=b+16|0;h[g>>3]=-1.0;b=c[13898]|0;j=b+1|0;c[13898]=j;if((j|0)>=(c[8272]|0)){i=d;return}k=c[1054]|0;l=a[k+(j*40&-1)|0]|0;m=(l&1)==0;L7583:do{if(m){n=j;o=l}else{p=c[k+(j*40&-1)+36>>2]|0;q=k+(j*40&-1)+32|0;r=c[10036]|0;s=0;while(1){if((s|0)>=(p|0)){t=5537;break}if((a[r+((c[q>>2]|0)+s|0)|0]|0)==(a[s+103664|0]|0)){s=s+1|0}else{break}}do{if((t|0)==5537){if((s|0)!=1){break}i=d;return}}while(0);if(m){n=j;o=l;break}s=c[k+(j*40&-1)+36>>2]|0;q=k+(j*40&-1)+32|0;r=c[10036]|0;p=0;while(1){if((p|0)>=(s|0)){break}if((a[r+((c[q>>2]|0)+p|0)|0]|0)==(a[p+95280|0]|0)){p=p+1|0}else{n=j;o=l;break L7583}}if((p|0)!=2){n=j;o=l;break}q=b+2|0;c[13898]=q;s=c[k+(q*40&-1)+36>>2]|0;u=a[k+(q*40&-1)|0]|0;if((u&1)!=0&(s|0)>0){w=0;x=0;y=c[k+(q*40&-1)+32>>2]|0}else{uf(q,141880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}while(1){if((a[w+142056|0]|0)==(a[r+(w+y|0)|0]|0)){z=y;A=x}else{if((w|0)!=2){t=5575;break}z=y-1|0;A=1}B=w+1|0;if((B|0)<(A+s|0)){w=B;x=A;y=z}else{break}}if((t|0)==5575){uf(q,141880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((A|0)!=0){n=q;o=u;break}if((w|0)==1|(w|0)==6){n=q;o=u;break}uf(q,141880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);w=c[k+(n*40&-1)+36>>2]|0;L7612:do{if((o&1)!=0&(w|0)>0){A=c[10036]|0;t=0;z=0;y=c[k+(n*40&-1)+32>>2]|0;while(1){if((a[t+142056|0]|0)==(a[A+(t+y|0)|0]|0)){C=y;D=z}else{if((t|0)!=2){break L7612}C=y-1|0;D=1}x=t+1|0;if((x|0)<(D+w|0)){t=x;z=D;y=C}else{break}}if((D|0)==0){if(!((t|0)==1|(t|0)==6)){break}}c[f>>2]=3;c[13898]=(c[13898]|0)+1}}while(0);D=is(e)|0;C=c[D>>2]|0;if((C|0)==1){E=+(c[D+8>>2]|0)}else if((C|0)==2){E=+h[D+8>>3]}else if((C|0)==3){E=+uz(c[D+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}D=e|0;if((c[D>>2]|0)==3){uu(c[e+8>>2]|0);c[D>>2]=1}D=E<0.0;F=D?-1.0:E;h[g>>3]=F;if((c[f>>2]|0)!=3){i=d;return}if(D){h[g>>3]=0.0;i=d;return}if(F<=1.0){i=d;return}h[g>>3]=1.0;i=d;return}function jt(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0.0,by=0.0,bz=0,bA=0,bB=0.0,bC=0,bD=0,bE=0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bM=0,bN=0,bO=0,bP=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0,b7=0,b8=0,b9=0,ca=0,cb=0,cc=0,cd=0,ce=0,cf=0,cg=0,ch=0,ci=0,cj=0,ck=0,cl=0,cm=0,cn=0,co=0,cp=0,cq=0,cr=0.0,cs=0.0,ct=0.0,cu=0,cv=0.0,cw=0,cx=0,cy=0,cz=0.0,cA=0,cB=0,cC=0,cD=0,cE=0.0,cF=0.0,cG=0.0,cH=0.0,cI=0.0,cJ=0.0,cK=0,cL=0,cM=0.0,cN=0.0,cO=0.0,cP=0.0,cQ=0,cR=0.0,cS=0.0,cT=0,cU=0,cV=0,cW=0,cX=0,cY=0,cZ=0,c_=0,c$=0,c0=0,c1=0,c2=0,c3=0,c4=0,c5=0,c6=0,c7=0,c8=0,c9=0,da=0,db=0,dc=0,dd=0,de=0,df=0,dg=0,dh=0,di=0,dj=0,dk=0,dm=0,dn=0,dq=0,dr=0,ds=0,dt=0,du=0,dv=0,dw=0,dx=0,dy=0,dz=0,dA=0,dB=0,dC=0,dD=0,dE=0,dF=0,dG=0,dH=0,dI=0.0,dJ=0,dK=0,dL=0,dM=0;d=i;i=i+656|0;e=d|0;f=d+24|0;g=d+48|0;j=d+72|0;k=d+96|0;l=d+120|0;m=d+144|0;n=d+168|0;o=d+192|0;p=d+216|0;q=d+264|0;r=d+272|0;s=d+296|0;t=d+320|0;u=d+344|0;w=d+368|0;x=d+392|0;y=d+416|0;z=d+464|0;A=d+472|0;B=d+520|0;C=d+528|0;D=d+576|0;E=d+584|0;F=d+608|0;G=d+624|0;H=F|0;a[H]=a[136208]|0;a[H+1|0]=a[136209|0]|0;a[H+2|0]=a[136210|0]|0;I=F+2|0;J=56832+(b*24&-1)|0;uB(I|0,J|0);K=F+(uA(H|0)|0)|0;a[K]=a[120232]|0;a[K+1|0]=a[120233|0]|0;a[K+2|0]=a[120234|0]|0;a[K+3|0]=a[120235|0]|0;a[K+4|0]=a[120236|0]|0;a[K+5|0]=a[120237|0]|0;H=c[13898]|0;L=c[1054]|0;M=c[L+(H*40&-1)+36>>2]|0;N=c[L+(H*40&-1)+32>>2]|0;L7646:do{if((a[L+(H*40&-1)|0]&1)==0){P=H}else{if((M|0)>0){Q=c[10036]|0;R=0;S=0;T=N;while(1){U=a[F+(R+2|0)|0]|0;if(U<<24>>24==(a[Q+(R+T|0)|0]|0)){V=T;W=S}else{if(U<<24>>24!=36){P=H;break L7646}V=T-1|0;W=1}X=R+1|0;if((X|0)<(W+M|0)){R=X;S=W;T=V}else{break}}if((W|0)==0){Y=X;Z=5590}}else{Y=0;Z=5590}if((Z|0)==5590){T=a[F+(Y+2|0)|0]|0;if(!((T<<24>>24|0)==36|(T<<24>>24|0)==0)){P=H;break}}T=64976+(b*688&-1)|0;a[T]=0;S=H+1|0;c[13898]=S;R=e|0;Q=e+8|0;U=64864+(b*688&-1)|0;_=64812+(b*688&-1)|0;$=64944+(b*688&-1)|0;aa=o|0;ab=o+8|0;ac=64808+(b*688&-1)|0;ad=n|0;ae=n+8|0;af=64813+(b*688&-1)|0;ag=m|0;ah=m+8|0;ai=l|0;aj=l+8|0;ak=x|0;al=x+8|0;am=w|0;an=w+8|0;ao=u|0;ap=u+8|0;aq=t|0;ar=t+8|0;as=s|0;at=s+8|0;au=r|0;av=r+8|0;aw=64944+(b*688&-1)|0;ax=64920+(b*688&-1)|0;ay=64952+(b*688&-1)|0;az=64960+(b*688&-1)|0;aA=64968+(b*688&-1)|0;aB=64928+(b*688&-1)|0;aC=(b|0)==0?6:5;aD=64924+(b*688&-1)|0;aE=f|0;aF=f+8|0;aG=65024+(b*688&-1)|0;aH=65272+(b*688&-1)|0;aI=65128+(b*688&-1)|0;aJ=G|0;aK=64984+(b*688&-1)|0;aL=64988+(b*688&-1)|0;aM=64992+(b*688&-1)|0;aN=(64648+(b*688&-1)|0)+348|0;aO=64984+(b*688&-1)|0;aP=65032+(b*688&-1)|0;aQ=g|0;aR=g+8|0;aS=k|0;aT=k+8|0;aU=65056+(b*688&-1)|0;aV=j|0;aW=j+8|0;aX=65064+(b*688&-1)|0;aY=65072+(b*688&-1)|0;aZ=64916+(b*688&-1)|0;a_=0;a$=0;a0=S;a1=L;a2=a[L+(S*40&-1)|0]|0;L7660:while(1){S=c[a1+(a0*40&-1)+36>>2]|0;a3=c[a1+(a0*40&-1)+32>>2]|0;a4=(a2&1)==0;L7662:do{if((S|0)>0&(a4^1)){a5=c[10036]|0;a6=0;a7=0;a9=a3;while(1){if((a[a6+116896|0]|0)==(a[a5+(a6+a9|0)|0]|0)){ba=a9;bb=a7}else{if((a6|0)!=2){break}ba=a9-1|0;bb=1}bc=a6+1|0;if((bc|0)<(bb+S|0)){a6=bc;a7=bb;a9=ba}else{Z=5598;break}}do{if((Z|0)==5598){Z=0;if((bb|0)==0){if(!((a6|0)==1|(a6|0)==4)){break}}c[aZ>>2]=c[aZ>>2]&-4|2;c[13898]=a0+1;bd=1;be=a$;break L7662}}while(0);if(a4){Z=5835;break}L7677:do{if((S|0)>0){a6=c[10036]|0;a9=0;a7=0;a5=a3;while(1){if((a[a9+141136|0]|0)==(a[a6+(a9+a5|0)|0]|0)){bf=a5;bg=a7}else{if((a9|0)!=2){break L7677}bf=a5-1|0;bg=1}bc=a9+1|0;if((bc|0)<(bg+S|0)){a9=bc;a7=bg;a5=bf}else{break}}if((bg|0)==0){if(!((a9|0)==1|(a9|0)==6)){break}}c[aZ>>2]=c[aZ>>2]&-4|1;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break}a5=c[10036]|0;a7=0;a6=0;bc=a3;while(1){if((a[a7+115048|0]|0)==(a[a5+(a7+bc|0)|0]|0)){bh=bc;bi=a6}else{if((a7|0)!=2){break}bh=bc-1|0;bi=1}bj=a7+1|0;if((bj|0)<(bi+S|0)){a7=bj;a6=bi;bc=bh}else{Z=5617;break}}do{if((Z|0)==5617){Z=0;if((bi|0)==0){if(!((a7|0)==1|(a7|0)==6)){break}}c[aZ>>2]=c[aZ>>2]|4;c[13898]=a0+1;bd=a_;be=1;break L7662}}while(0);if(a4){Z=5835;break}L7704:do{if((S|0)>0){a7=c[10036]|0;bc=0;a6=0;a5=a3;while(1){if((a[bc+114024|0]|0)==(a[a7+(bc+a5|0)|0]|0)){bk=a5;bl=a6}else{if((bc|0)!=4){break L7704}bk=a5-1|0;bl=1}bj=bc+1|0;if((bj|0)<(bl+S|0)){bc=bj;a6=bl;a5=bk}else{break}}if((bl|0)==0){if(!((bc|0)==3|(bc|0)==8)){break}}c[aZ>>2]=c[aZ>>2]&-5;c[13898]=a0+1;bd=a_;be=1;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break}a5=c[10036]|0;a6=0;a7=0;a9=a3;while(1){if((a[a6+110600|0]|0)==(a[a5+(a6+a9|0)|0]|0)){bm=a9;bn=a7}else{if((a6|0)!=2){break}bm=a9-1|0;bn=1}bj=a6+1|0;if((bj|0)<(bn+S|0)){a6=bj;a7=bn;a9=bm}else{Z=5636;break}}do{if((Z|0)==5636){Z=0;if((bn|0)==0){if(!((a6|0)==1|(a6|0)==7)){break}}a[aY]=1;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(a4){Z=5835;break}L7731:do{if((S|0)>0){a6=c[10036]|0;a9=0;a7=0;a5=a3;while(1){if((a[a9+107800|0]|0)==(a[a6+(a9+a5|0)|0]|0)){bo=a5;bp=a7}else{if((a9|0)!=3){break L7731}bo=a5-1|0;bp=1}bj=a9+1|0;if((bj|0)<(bp+S|0)){a9=bj;a7=bp;a5=bo}else{break}}if((bp|0)==0){if(!((a9|0)==2|(a9|0)==8)){break}}a[aY]=0;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break}a5=c[10036]|0;a7=0;a6=0;bc=a3;while(1){if((a[a7+106864|0]|0)==(a[a5+(a7+bc|0)|0]|0)){bq=bc;br=a6}else{if((a7|0)!=2){break}bq=bc-1|0;br=1}bj=a7+1|0;if((bj|0)<(br+S|0)){a7=bj;a6=br;bc=bq}else{Z=5655;break}}do{if((Z|0)==5655){Z=0;if((br|0)==0){if(!((a7|0)==5|(a7|0)==1)){break}}bc=a0+1|0;c[13898]=bc;a6=c[a1+(bc*40&-1)+36>>2]|0;L7756:do{if((a[a1+(bc*40&-1)|0]&1)!=0&(a6|0)>0){a5=c[10036]|0;bj=0;bs=0;bt=c[a1+(bc*40&-1)+32>>2]|0;while(1){if((a[bj+184456|0]|0)==(a[a5+(bj+bt|0)|0]|0)){bu=bt;bv=bs}else{if((bj|0)!=3){break L7756}bu=bt-1|0;bv=1}bw=bj+1|0;if((bw|0)<(bv+a6|0)){bj=bw;bs=bv;bt=bu}else{break}}if((bv|0)==0){if(!((bj|0)==2|(bj|0)==7)){break}}h[aU>>3]=1.0;h[aX>>3]=.5;c[13898]=a0+2;bd=a_;be=a$;break L7662}}while(0);a6=is(k)|0;bc=c[a6>>2]|0;if((bc|0)==1){bx=+(c[a6+8>>2]|0)}else if((bc|0)==2){bx=+h[a6+8>>3]}else if((bc|0)==3){bx=+uz(c[a6+8>>2]|0,0)}else{Z=5670;break L7660}if((c[aS>>2]|0)==3){uu(c[aT>>2]|0);c[aS>>2]=1}h[aU>>3]=bx;a6=c[13898]|0;L7777:do{if((c[8272]|0)>(a6|0)){bc=c[1054]|0;if((a[bc+(a6*40&-1)|0]&1)==0){break}a9=c[bc+(a6*40&-1)+36>>2]|0;bt=bc+(a6*40&-1)+32|0;bc=c[10036]|0;bs=0;while(1){if((bs|0)>=(a9|0)){break}if((a[bc+((c[bt>>2]|0)+bs|0)|0]|0)==(a[bs+148464|0]|0)){bs=bs+1|0}else{break L7777}}if((bs|0)!=1){break}c[13898]=a6+1;bt=is(j)|0;bc=c[bt>>2]|0;if((bc|0)==1){by=+(c[bt+8>>2]|0)}else if((bc|0)==2){by=+h[bt+8>>3]}else if((bc|0)==3){by=+uz(c[bt+8>>2]|0,0)}else{Z=5683;break L7660}if((c[aV>>2]|0)==3){uu(c[aW>>2]|0);c[aV>>2]=1}h[aX>>3]=by;bd=a_;be=a$;break L7662}}while(0);h[aX>>3]=bx*.5;bd=a_;be=a$;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break}a7=c[10036]|0;a6=0;bt=0;bc=a3;while(1){if((a[a6+104840|0]|0)==(a[a7+(a6+bc|0)|0]|0)){bz=bc;bA=bt}else{if((a6|0)!=2){break}bz=bc-1|0;bA=1}a9=a6+1|0;if((a9|0)<(bA+S|0)){a6=a9;bt=bA;bc=bz}else{Z=5694;break}}do{if((Z|0)==5694){Z=0;if((bA|0)==0){if(!((a6|0)==1|(a6|0)==6)){break}}c[aP>>2]=-270;bc=a0+1|0;c[13898]=bc;if((c[8272]|0)<=(bc|0)){bd=a_;be=a$;break L7662}if((a[a1+(bc*40&-1)|0]&1)==0){bd=a_;be=a$;break L7662}bt=c[a1+(bc*40&-1)+36>>2]|0;a7=a1+(bc*40&-1)+32|0;bc=c[10036]|0;a9=0;while(1){if((a9|0)>=(bt|0)){break}if((a[bc+((c[a7>>2]|0)+a9|0)|0]|0)==(a[a9+163312|0]|0)){a9=a9+1|0}else{bd=a_;be=a$;break L7662}}if((a9|0)!=2){bd=a_;be=a$;break L7662}c[13898]=a0+2;a7=is(g)|0;bc=c[a7>>2]|0;if((bc|0)==2){bB=+h[a7+8>>3]}else if((bc|0)==3){bB=+uz(c[a7+8>>2]|0,0)}else if((bc|0)==1){bB=+(c[a7+8>>2]|0)}else{Z=5706;break L7660}if((c[aQ>>2]|0)==3){uu(c[aR>>2]|0);c[aQ>>2]=1}c[aP>>2]=~~bB;bd=a_;be=a$;break L7662}}while(0);if(a4){Z=5835;break}L7824:do{if((S|0)>0){a6=c[10036]|0;a7=0;bc=0;bt=a3;while(1){if((a[a7+104240|0]|0)==(a[a6+(a7+bt|0)|0]|0)){bC=bt;bD=bc}else{if((a7|0)!=4){break L7824}bC=bt-1|0;bD=1}bj=a7+1|0;if((bj|0)<(bD+S|0)){a7=bj;bc=bD;bt=bC}else{break}}if((bD|0)==0){if(!((a7|0)==3|(a7|0)==8)){break}}c[aP>>2]=0;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break}bt=c[10036]|0;bc=0;a6=0;a9=a3;while(1){if((a[bc+152064|0]|0)==(a[bt+(bc+a9|0)|0]|0)){bE=a9;bF=a6}else{if((bc|0)!=3){break}bE=a9-1|0;bF=1}bj=bc+1|0;if((bj|0)<(bF+S|0)){bc=bj;a6=bF;a9=bE}else{Z=5726;break}}do{if((Z|0)==5726){Z=0;if((bF|0)==0){if(!((bc|0)==6|(bc|0)==2)){break}}c[13898]=a0+1;dl(aO,4);bd=a_;be=a$;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break}bc=c[10036]|0;a9=0;a6=0;bt=a3;while(1){if((a[a9+153128|0]|0)==(a[bc+(a9+bt|0)|0]|0)){bG=bt;bH=a6}else{if((a9|0)!=5){break}bG=bt-1|0;bH=1}bj=a9+1|0;if((bj|0)<(bH+S|0)){a9=bj;a6=bH;bt=bG}else{Z=5735;break}}do{if((Z|0)==5735){Z=0;if((bH|0)==0){if(!((a9|0)==4|(a9|0)==8)){break}}uE(aJ|0,0,28);c[13898]=a0+1;c[aK>>2]=4;c[aL>>2]=4;c[aM>>2]=4;c[aN>>2]=c[aJ>>2];c[aN+4>>2]=c[aJ+4>>2];c[aN+8>>2]=c[aJ+8>>2];c[aN+12>>2]=c[aJ+12>>2];c[aN+16>>2]=c[aJ+16>>2];c[aN+20>>2]=c[aJ+20>>2];c[aN+24>>2]=c[aJ+24>>2];bd=a_;be=a$;break L7662}}while(0);if(a4){Z=5835;break}L7864:do{if((S|0)>0){a9=c[10036]|0;bt=0;a6=0;bc=a3;while(1){if((a[bt+149696|0]|0)==(a[a9+(bt+bc|0)|0]|0)){bI=bc;bJ=a6}else{if((bt|0)!=1){break L7864}bI=bc-1|0;bJ=1}bj=bt+1|0;if((bj|0)<(bJ+S|0)){bt=bj;a6=bJ;bc=bI}else{break}}if((bJ|0)==0){if(!((bt|0)==0|(bt|0)==4)){break}}c[aI>>2]=0;a[aH]=1;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break}bc=c[10036]|0;a6=0;a9=0;a7=a3;while(1){if((a[a6+179896|0]|0)==(a[bc+(a6+a7|0)|0]|0)){bK=a7;bL=a9}else{if((a6|0)!=1){Z=5756;break}bK=a7-1|0;bL=1}bj=a6+1|0;if((bj|0)<(bL+S|0)){a6=bj;a9=bL;a7=bK}else{Z=5754;break}}do{if((Z|0)==5754){Z=0;if((bL|0)!=0){break}if(!((a6|0)==0|(a6|0)==6)){Z=5756}}}while(0);L7887:do{if((Z|0)==5756){Z=0;if(a4){Z=5835;break L7662}L7890:do{if((S|0)>0){a6=c[10036]|0;a7=0;a9=0;bc=a3;while(1){if((a[a7+149304|0]|0)==(a[a6+(a7+bc|0)|0]|0)){bM=bc;bN=a9}else{if((a7|0)!=1){break L7890}bM=bc-1|0;bN=1}bj=a7+1|0;if((bj|0)<(bN+S|0)){a7=bj;a9=bN;bc=bM}else{break}}if((bN|0)!=0){break L7887}if((a7|0)==0|(a7|0)==6){break L7887}}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break L7662}bt=c[10036]|0;bc=0;a9=0;a6=a3;while(1){if((a[bc+101272|0]|0)==(a[bt+(bc+a6|0)|0]|0)){bO=a6;bP=a9}else{if((bc|0)!=2){break}bO=a6-1|0;bP=1}bs=bc+1|0;if((bs|0)<(bP+S|0)){bc=bs;a9=bP;a6=bO}else{Z=5772;break}}do{if((Z|0)==5772){Z=0;if((bP|0)==0){if(!((bc|0)==1|(bc|0)==5)){break}}c[aI>>2]=2;a[aH]=1;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(a4){Z=5835;break L7662}L7915:do{if((S|0)>0){bc=c[10036]|0;a6=0;a9=0;bt=a3;while(1){if((a[a6+100712|0]|0)==(a[bc+(a6+bt|0)|0]|0)){bQ=bt;bR=a9}else{if((a6|0)!=5){break L7915}bQ=bt-1|0;bR=1}bs=a6+1|0;if((bs|0)<(bR+S|0)){a6=bs;a9=bR;bt=bQ}else{break}}if((bR|0)==0){if(!((a6|0)==4|(a6|0)==11)){break}}a[aH]=0;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break L7662}bt=c[10036]|0;a9=0;bc=0;a7=a3;while(1){if((a[a9+100040|0]|0)==(a[bt+(a9+a7|0)|0]|0)){bS=a7;bT=bc}else{if((a9|0)!=5){break}bS=a7-1|0;bT=1}bs=a9+1|0;if((bs|0)<(bT+S|0)){a9=bs;bc=bT;a7=bS}else{Z=5791;break}}do{if((Z|0)==5791){Z=0;if((bT|0)==0){if(!((a9|0)==4|(a9|0)==12)){break}}a[aG]=1;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(a4){Z=5835;break L7662}L7942:do{if((S|0)>0){a9=c[10036]|0;a7=0;bc=0;bt=a3;while(1){if((a[a7+99600|0]|0)==(a[a9+(a7+bt|0)|0]|0)){bU=bt;bV=bc}else{if((a7|0)!=7){break L7942}bU=bt-1|0;bV=1}bs=a7+1|0;if((bs|0)<(bV+S|0)){a7=bs;bc=bV;bt=bU}else{break}}if((bV|0)==0){if(!((a7|0)==6|(a7|0)==14)){break}}a[aG]=0;c[13898]=a0+1;bd=a_;be=a$;break L7662}}while(0);if(!((S|0)>0&(a4^1))){Z=5835;break L7662}bt=c[10036]|0;bc=0;a9=0;a6=a3;while(1){if((a[bc+175984|0]|0)==(a[bt+(bc+a6|0)|0]|0)){bW=a6;bX=a9}else{if((bc|0)!=1){Z=5835;break L7662}bW=a6-1|0;bX=1}bs=bc+1|0;if((bs|0)<(bX+S|0)){bc=bs;a9=bX;a6=bW}else{break}}if((bX|0)==0){if(!((bc|0)==4|(bc|0)==0)){Z=5835;break L7662}}bY=a0+1|0;c[13898]=bY;a6=(a[a1+(bY*40&-1)|0]&1)==0;if(a6){Z=5814}else{a9=a[(c[10036]|0)+(c[a1+(bY*40&-1)+32>>2]|0)|0]|0;if(!((a9<<24>>24|0)==39|(a9<<24>>24|0)==34)){Z=5814}}if((Z|0)==5814){Z=0;a9=c[10810]|0;if((a9|0)==0){Z=6274;break L7660}bt=c[8272]|0;bs=a1+(bY*40&-1)+36|0;bj=a1+(bY*40&-1)+32|0;a5=c[10036]|0;bw=a9;L7971:while(1){a9=c[bw+4>>2]|0;L7973:do{if(!((bt|0)<=(bY|0)|a6)){bZ=c[bs>>2]|0;b_=0;while(1){if((b_|0)>=(bZ|0)){break}if((a[a5+((c[bj>>2]|0)+b_|0)|0]|0)==(a[a9+b_|0]|0)){b_=b_+1|0}else{break L7973}}if((a[a9+b_|0]|0)==0){break L7971}}}while(0);a9=c[bw>>2]|0;if((a9|0)==0){Z=6272;break L7660}else{bw=a9}}if((a[bw+8|0]&1)!=0){Z=6271;break L7660}if((c[bw+16>>2]|0)!=3){Z=6273;break L7660}}uu(c[aD>>2]|0);c[aD>>2]=0;bj=c[13898]|0;L7983:do{if((bj|0)<(c[8272]|0)){a5=c[1054]|0;L7985:do{if((a[a5+(bj*40&-1)|0]&1)!=0){bs=c[a5+(bj*40&-1)+36>>2]|0;a6=a5+(bj*40&-1)+32|0;bt=c[10036]|0;bc=0;while(1){if((bc|0)>=(bs|0)){break}if((a[bt+((c[a6>>2]|0)+bc|0)|0]|0)==(a[bc+103664|0]|0)){bc=bc+1|0}else{break L7985}}if((bc|0)==1){b$=0;break L7983}}}while(0);a[14176]=1;is(f);a[14176]=0;if((c[aE>>2]|0)==3){b$=c[aF>>2]|0;break}else{c[13898]=bj;b$=0;break}}else{b$=0}}while(0);c[aD>>2]=b$;bd=a_;be=a$;break L7662}}while(0);c[aI>>2]=1;a[aH]=1;c[13898]=a0+1;bd=a_;be=a$}else{Z=5835}}while(0);L7997:do{if((Z|0)==5835){Z=0;bj=c[8272]|0;bw=(bj|0)>(a0|0);L7999:do{if(bw){if(a4){break}a5=c[10036]|0;a6=0;while(1){if((a6|0)>=(S|0)){Z=5840;break}if((a[a5+(a3+a6|0)|0]|0)==(a[a6+97960|0]|0)){a6=a6+1|0}else{break}}do{if((Z|0)==5840){Z=0;if((a6|0)!=6){break}b0=a0+1|0;c[13898]=b0;if((b0|0)>=(bj|0)){b1=b0;Z=6280;break L7660}L8009:do{if((a[a1+(b0*40&-1)|0]&1)!=0){bt=c[a1+(b0*40&-1)+36>>2]|0;bs=a1+(b0*40&-1)+32|0;b_=0;while(1){if((b_|0)>=(bt|0)){break}if((a[a5+((c[bs>>2]|0)+b_|0)|0]|0)==(a[b_+103664|0]|0)){b_=b_+1|0}else{break L8009}}if((b_|0)==1){b1=b0;Z=6277;break L7660}}}while(0);a[14176]=1;is(e);a[14176]=0;if((c[R>>2]|0)!=3){Z=5848;break L7660}bs=c[Q>>2]|0;if((bs|0)==0){Z=5850;break L7660}uF(U|0,bs|0,51);uu(bs);bs=a8(U|0,37)|0;do{if((bs|0)==0){b2=0}else{bt=bs+1|0;L8020:do{if((bt|0)==0){b3=0}else{bc=bt;while(1){a9=a[bc]|0;if(!((a9<<24>>24|0)==32|(a9<<24>>24|0)==45|(a9<<24>>24|0)==43|(a9<<24>>24|0)==35)){b3=bc;break L8020}a9=bc+1|0;if((a9|0)==0){b3=0;break}else{bc=a9}}}}while(0);while(1){b4=a[b3]|0;if(!(((b4&255)-48|0)>>>0<10|b4<<24>>24==46)){break}b3=b3+1|0}if((b4-102&255)<2){b2=1;break}b2=b4<<24>>24==101&1}}while(0);a[_]=b2;bd=a_;be=a$;break L7997}}while(0);if(!bw){Z=5867;break}if(a4){break}a5=c[10036]|0;a6=0;while(1){if((a6|0)>=(S|0)){break}if((a[a5+(a3+a6|0)|0]|0)==(a[a6+143040|0]|0)){a6=a6+1|0}else{Z=5867;break L7999}}if((a6|0)==2){Z=5875}else{Z=5867}}else{Z=5867}}while(0);L8037:do{if((Z|0)==5867){Z=0;L8039:do{if((S|0)>0&(a4^1)){a5=c[10036]|0;bs=0;bt=0;b_=a3;while(1){if((a[bs+142856|0]|0)==(a[a5+(bs+b_|0)|0]|0)){b5=b_;b6=bt}else{if((bs|0)!=4){break}b5=b_-1|0;b6=1}bc=bs+1|0;if((bc|0)<(b6+S|0)){bs=bc;bt=b6;b_=b5}else{Z=5873;break}}if((Z|0)==5873){Z=0;if((b6|0)!=0){Z=5875;break L8037}if((bs|0)==3|(bs|0)==9){Z=5875;break L8037}}if(!((S|0)>0&(a4^1))){break}b_=c[10036]|0;bt=0;a5=0;bc=a3;while(1){if((a[bt+96704|0]|0)==(a[b_+(bt+bc|0)|0]|0)){b7=bc;b8=a5}else{if((bt|0)!=2){break L8039}b7=bc-1|0;b8=1}a9=bt+1|0;if((a9|0)<(b8+S|0)){bt=a9;a5=b8;bc=b7}else{break}}if((b8|0)==0){if(!((bt|0)==8|(bt|0)==1)){break}}c[13898]=a0+1;if((a[T]&1)==0){bc=c[aw>>2]|0;if((bc|0)!=0){a5=bc;while(1){bc=c[a5+16>>2]|0;b_=c[a5+8>>2]|0;if((b_|0)!=0){uu(b_)}uu(a5);if((bc|0)==0){break}else{a5=bc}}}c[aw>>2]=0}c[ax>>2]=1;bd=a_;be=a$;break L7997}}while(0);if(!bw){bd=a_;be=a$;break L7997}if(a4){break}a6=c[10036]|0;a5=0;while(1){if((a5|0)>=(S|0)){Z=5896;break}if((a[a6+(a3+a5|0)|0]|0)==(a[a5+186440|0]|0)){a5=a5+1|0}else{break}}do{if((Z|0)==5896){Z=0;if((a5|0)!=3){break}c[13898]=a0+1;a[T]=1;bd=a_;be=a$;break L7997}}while(0);if(!bw){bd=a_;be=a$;break L7997}if(a4){break}a5=c[10036]|0;a6=0;while(1){if((a6|0)>=(S|0)){Z=5903;break}if((a[a5+(a3+a6|0)|0]|0)==(a[a6+103664|0]|0)){a6=a6+1|0}else{break}}if((Z|0)==5903){Z=0;if((a6|0)==1){bd=a_;be=a$;break L7997}}if(a4){break}a5=c[10036]|0;bt=0;while(1){if((bt|0)>=(S|0)){break}if((a[a5+(a3+bt|0)|0]|0)==(a[bt+199040|0]|0)){bt=bt+1|0}else{break L8037}}if((bt|0)!=1){break}a6=a0+1|0;c[13898]=a6;L8096:do{if((bj|0)>(a6|0)){if((a[a1+(a6*40&-1)|0]&1)==0){break}bc=c[a1+(a6*40&-1)+36>>2]|0;b_=a1+(a6*40&-1)+32|0;bs=0;while(1){if((bs|0)>=(bc|0)){break}if((a[a5+((c[b_>>2]|0)+bs|0)|0]|0)==(a[bs+131272|0]|0)){bs=bs+1|0}else{break L8096}}if((bs|0)!=1){break}c[13898]=a0+2;bd=a_;be=a$;break L7997}}while(0);do{if((a[T]&1)==0){a5=c[6074]|0;if((a5|0)!=0){if((c[a5+32>>2]|0)!=0){b9=a6;ca=bj;break}}a5=c[$>>2]|0;if((a5|0)==0){cb=a6;cc=bj}else{bt=a5;while(1){a5=c[bt+16>>2]|0;b_=c[bt+8>>2]|0;if((b_|0)!=0){uu(b_)}uu(bt);if((a5|0)==0){break}else{bt=a5}}cb=c[13898]|0;cc=c[8272]|0}c[$>>2]=0;b9=cb;ca=cc}else{b9=a6;ca=bj}}while(0);L8119:do{if((b9|0)<(ca|0)){a6=b9;bt=ca;bs=c[1054]|0;while(1){a5=(a[bs+(a6*40&-1)|0]&1)==0;L8123:do{if(!a5){b_=c[bs+(a6*40&-1)+36>>2]|0;bc=bs+(a6*40&-1)+32|0;a9=c[10036]|0;a7=0;while(1){if((a7|0)>=(b_|0)){break}if((a[a9+((c[bc>>2]|0)+a7|0)|0]|0)==(a[a7+103664|0]|0)){a7=a7+1|0}else{break L8123}}if((a7|0)==1){cd=a6;ce=bt;break L8119}}}while(0);L8130:do{if((a6|0)<(bt|0)){L8132:do{if(!a5){bc=c[bs+(a6*40&-1)+36>>2]|0;a9=bs+(a6*40&-1)+32|0;b_=c[10036]|0;bZ=0;while(1){if((bZ|0)>=(bc|0)){break}if((a[b_+((c[a9>>2]|0)+bZ|0)|0]|0)==(a[bZ+103664|0]|0)){bZ=bZ+1|0}else{break L8132}}if((bZ|0)==1){cf=0;Z=5952;break L8130}}}while(0);a[14176]=1;is(o);a[14176]=0;if((c[aa>>2]|0)!=3){c[13898]=a6;cf=0;Z=5952;break}a7=c[ab>>2]|0;if((a7|0)==0){cf=0;Z=5952;break}if((c[ac>>2]|0)!=1){cg=a7;Z=5977;break}a9=c[13898]|0;if((c[8272]|0)<=(a9|0)){cf=a7;Z=5952;break}b_=c[1054]|0;if((a[b_+(a9*40&-1)|0]&1)==0){cf=a7;Z=5952;break}bc=c[b_+(a9*40&-1)+36>>2]|0;ch=b_+(a9*40&-1)+32|0;a9=c[10036]|0;b_=0;while(1){if((b_|0)>=(bc|0)){Z=5947;break}if((a[a9+((c[ch>>2]|0)+b_|0)|0]|0)==(a[b_+148464|0]|0)){b_=b_+1|0}else{ci=0;Z=5948;break}}if((Z|0)==5947){Z=0;if((b_|0)!=1){ci=0;Z=5948}}if((Z|0)==5948){while(1){Z=0;if((ci|0)>=(bc|0)){break}if((a[a9+((c[ch>>2]|0)+ci|0)|0]|0)==(a[ci+131272|0]|0)){ci=ci+1|0;Z=5948}else{cf=a7;Z=5952;break L8130}}if((ci|0)!=1){cf=a7;Z=5952;break}}c[13898]=a6;uu(a7);cf=0;Z=5952}else{cf=0;Z=5952}}while(0);L8157:do{if((Z|0)==5952){Z=0;if((c[ac>>2]|0)!=1){cg=cf;Z=5977;break}a5=c[13898]|0;ch=c[1054]|0;a9=(a[ch+(a5*40&-1)|0]&1)==0;if(a9){Z=5955}else{bc=c[10036]|0;b_=a[bc+(c[ch+(a5*40&-1)+32>>2]|0)|0]|0;if((b_<<24>>24|0)==39|(b_<<24>>24|0)==34){cj=bc}else{Z=5955}}if((Z|0)==5955){Z=0;bc=c[10810]|0;if((bc|0)==0){cg=cf;Z=5977;break}b_=ch+(a5*40&-1)+36|0;ck=ch+(a5*40&-1)+32|0;cl=c[10036]|0;cm=(c[8272]|0)<=(a5|0)|a9;cn=bc;L8165:while(1){bc=c[cn+4>>2]|0;L8167:do{if(!cm){co=c[b_>>2]|0;cp=0;while(1){if((cp|0)>=(co|0)){break}if((a[cl+((c[ck>>2]|0)+cp|0)|0]|0)==(a[bc+cp|0]|0)){cp=cp+1|0}else{break L8167}}if((a[bc+cp|0]|0)==0){break L8165}}}while(0);bc=c[cn>>2]|0;if((bc|0)==0){cg=cf;Z=5977;break L8157}else{cn=bc}}if((a[cn+8|0]&1)!=0){cg=cf;Z=5977;break}if((c[cn+16>>2]|0)==3){cj=cl}else{cg=cf;Z=5977;break}}L8177:do{if((a5|0)<(c[8272]|0)){L8179:do{if(!a9){ck=c[ch+(a5*40&-1)+36>>2]|0;b_=ch+(a5*40&-1)+32|0;cm=0;while(1){if((cm|0)>=(ck|0)){break}if((a[cj+((c[b_>>2]|0)+cm|0)|0]|0)==(a[cm+103664|0]|0)){cm=cm+1|0}else{break L8179}}if((cm|0)==1){cq=0;break L8177}}}while(0);a[14176]=1;is(n);a[14176]=0;if((c[ad>>2]|0)==3){cq=c[ae>>2]|0;break}else{c[13898]=a5;cq=0;break}}else{cq=0}}while(0);if((t1(cq,af,p,q)|0)==0){cr=0.0}else{cs=+t2(p);cr=cs+ +h[q>>3]}uu(cq);ct=cr;cu=cf}}while(0);do{if((Z|0)==5977){Z=0;a5=is(m)|0;ch=c[a5>>2]|0;if((ch|0)==1){cv=+(c[a5+8>>2]|0)}else if((ch|0)==2){cv=+h[a5+8>>3]}else if((ch|0)==3){cv=+uz(c[a5+8>>2]|0,0)}else{Z=5981;break L7660}if((c[ag>>2]|0)!=3){ct=cv;cu=cg;break}uu(c[ah>>2]|0);c[ag>>2]=1;ct=cv;cu=cg}}while(0);a5=c[13898]|0;L8202:do{if((a5|0)<(c[8272]|0)){ch=c[1054]|0;L8204:do{if((a[ch+(a5*40&-1)|0]&1)!=0){a9=c[ch+(a5*40&-1)+36>>2]|0;cl=ch+(a5*40&-1)+32|0;cn=c[10036]|0;b_=0;while(1){if((b_|0)>=(a9|0)){Z=5989;break}if((a[cn+((c[cl>>2]|0)+b_|0)|0]|0)==(a[b_+103664|0]|0)){b_=b_+1|0}else{cw=0;break}}if((Z|0)==5989){Z=0;if((b_|0)==1){cx=0;break L8202}else{cw=0}}while(1){if((cw|0)>=(a9|0)){Z=5992;break}if((a[cn+((c[cl>>2]|0)+cw|0)|0]|0)==(a[cw+148464|0]|0)){cw=cw+1|0}else{cy=0;break}}if((Z|0)==5992){Z=0;if((cw|0)==1){cx=0;break L8202}else{cy=0}}while(1){if((cy|0)>=(a9|0)){break}if((a[cn+((c[cl>>2]|0)+cy|0)|0]|0)==(a[cy+131272|0]|0)){cy=cy+1|0}else{break L8204}}if((cy|0)==1){cx=0;break L8202}}}while(0);ch=is(l)|0;cl=c[ch>>2]|0;if((cl|0)==1){cz=+(c[ch+8>>2]|0)}else if((cl|0)==2){cz=+h[ch+8>>3]}else if((cl|0)==3){cz=+uz(c[ch+8>>2]|0,0)}else{Z=6e3;break L7660}if((c[ai>>2]|0)==3){uu(c[aj>>2]|0);c[ai>>2]=1}cx=~~cz}else{cx=0}}while(0);dp(b,cu,ct,cx);uu(cu);a5=c[13898]|0;ch=c[8272]|0;if((a5|0)>=(ch|0)){cd=a5;ce=ch;break L8119}cl=c[1054]|0;if((a[cl+(a5*40&-1)|0]&1)==0){cd=a5;ce=ch;break L8119}cn=c[cl+(a5*40&-1)+36>>2]|0;a9=cl+(a5*40&-1)+32|0;b_=c[10036]|0;ck=0;while(1){if((ck|0)>=(cn|0)){Z=6009;break}if((a[b_+((c[a9>>2]|0)+ck|0)|0]|0)==(a[ck+103664|0]|0)){ck=ck+1|0}else{cA=0;break}}if((Z|0)==6009){Z=0;if((ck|0)==1){cd=a5;ce=ch;break L8119}else{cA=0}}while(1){if((cA|0)>=(cn|0)){break}if((a[b_+((c[a9>>2]|0)+cA|0)|0]|0)==(a[cA+148464|0]|0)){cA=cA+1|0}else{cd=a5;ce=ch;break L8119}}if((cA|0)!=1){cd=a5;ce=ch;break L8119}a9=a5+1|0;c[13898]=a9;if((a9|0)<(ch|0)){a6=a9;bt=ch;bs=cl}else{cd=a9;ce=ch;break}}}else{cd=b9;ce=ca}}while(0);if((cd|0)>=(ce|0)){Z=6023;break L7660}bs=c[1054]|0;if((a[bs+(cd*40&-1)|0]&1)==0){Z=6023;break L7660}bt=c[bs+(cd*40&-1)+36>>2]|0;a6=bs+(cd*40&-1)+32|0;bs=c[10036]|0;a9=0;while(1){if((a9|0)>=(bt|0)){Z=6019;break}if((a[bs+((c[a6>>2]|0)+a9|0)|0]|0)==(a[a9+103664|0]|0)){a9=a9+1|0}else{cB=0;break}}if((Z|0)==6019){Z=0;if((a9|0)==1){Z=6023;break L7660}else{cB=0}}while(1){if((cB|0)>=(bt|0)){break}if((a[bs+((c[a6>>2]|0)+cB|0)|0]|0)==(a[cB+131272|0]|0)){cB=cB+1|0}else{Z=6023;break L7660}}if((cB|0)!=1){Z=6023;break L7660}c[13898]=cd+1;bd=a_;be=a$;break L7997}}while(0);if((Z|0)==5875){Z=0;hI(aB,aC);bd=a_;be=a$;break}L8258:do{if((c[ac>>2]|0)==1){if(a4){Z=6033}else{bj=c[10036]|0;bw=a[bj+a3|0]|0;if((bw<<24>>24|0)==39|(bw<<24>>24|0)==34){cC=bj;Z=6043}else{Z=6033}}if((Z|0)==6033){Z=0;bj=c[10810]|0;if((bj|0)==0){Z=6053;break}bw=c[10036]|0;a6=bj;L8265:while(1){bj=c[a6+4>>2]|0;L8267:do{if(!a4){bs=0;while(1){if((bs|0)>=(S|0)){break}if((a[bw+(a3+bs|0)|0]|0)==(a[bj+bs|0]|0)){bs=bs+1|0}else{break L8267}}if((a[bj+bs|0]|0)==0){break L8265}}}while(0);bj=c[a6>>2]|0;if((bj|0)==0){Z=6053;break L8258}else{a6=bj}}if((a[a6+8|0]&1)!=0){Z=6053;break}if((c[a6+16>>2]|0)!=3){Z=6053;break}if(a4){Z=6047}else{cC=bw;Z=6043}}L8276:do{if((Z|0)==6043){Z=0;bj=0;while(1){if((bj|0)>=(S|0)){break}if((a[cC+(a3+bj|0)|0]|0)==(a[bj+103664|0]|0)){bj=bj+1|0}else{Z=6047;break L8276}}if((bj|0)==1){cD=0}else{Z=6047}}}while(0);do{if((Z|0)==6047){Z=0;a[14176]=1;is(x);a[14176]=0;if((c[ak>>2]|0)==3){cD=c[al>>2]|0;break}else{c[13898]=a0;cD=0;break}}}while(0);if((t1(cD,af,y,z)|0)==0){cE=0.0}else{cs=+t2(y);cE=cs+ +h[z>>3]}uu(cD);cF=cE}else{Z=6053}}while(0);do{if((Z|0)==6053){Z=0;bw=is(w)|0;a6=c[bw>>2]|0;if((a6|0)==1){cG=+(c[bw+8>>2]|0)}else if((a6|0)==2){cG=+h[bw+8>>3]}else if((a6|0)==3){cG=+uz(c[bw+8>>2]|0,0)}else{Z=6057;break L7660}if((c[am>>2]|0)!=3){cF=cG;break}uu(c[an>>2]|0);c[am>>2]=1;cF=cG}}while(0);bw=c[13898]|0;a6=c[8272]|0;L8300:do{if((a6|0)>(bw|0)){ch=c[1054]|0;if((a[ch+(bw*40&-1)|0]&1)==0){cH=-8.988465674311579e+307;cI=cF;cJ=8.988465674311579e+307;break}cl=c[ch+(bw*40&-1)+36>>2]|0;a5=ch+(bw*40&-1)+32|0;bt=c[10036]|0;a9=0;while(1){if((a9|0)>=(cl|0)){break}if((a[bt+((c[a5>>2]|0)+a9|0)|0]|0)==(a[a9+148464|0]|0)){a9=a9+1|0}else{cH=-8.988465674311579e+307;cI=cF;cJ=8.988465674311579e+307;break L8300}}if((a9|0)!=1){cH=-8.988465674311579e+307;cI=cF;cJ=8.988465674311579e+307;break}cK=bw+1|0;c[13898]=cK;L8308:do{if((c[ac>>2]|0)==1){a5=(a[ch+(cK*40&-1)|0]&1)==0;if(a5){Z=6069}else{cl=a[bt+(c[ch+(cK*40&-1)+32>>2]|0)|0]|0;if(!((cl<<24>>24|0)==39|(cl<<24>>24|0)==34)){Z=6069}}if((Z|0)==6069){Z=0;cl=c[10810]|0;if((cl|0)==0){Z=6091;break}b_=ch+(cK*40&-1)+36|0;cn=ch+(cK*40&-1)+32|0;ck=(a6|0)<=(cK|0)|a5;cp=cl;L8315:while(1){cl=c[cp+4>>2]|0;L8317:do{if(!ck){a7=c[b_>>2]|0;bc=0;while(1){if((bc|0)>=(a7|0)){break}if((a[bt+((c[cn>>2]|0)+bc|0)|0]|0)==(a[cl+bc|0]|0)){bc=bc+1|0}else{break L8317}}if((a[cl+bc|0]|0)==0){break L8315}}}while(0);cl=c[cp>>2]|0;if((cl|0)==0){Z=6091;break L8308}else{cp=cl}}if((a[cp+8|0]&1)!=0){Z=6091;break}if((c[cp+16>>2]|0)!=3){Z=6091;break}}L8327:do{if((cK|0)<(a6|0)){L8329:do{if(!a5){cn=c[ch+(cK*40&-1)+36>>2]|0;b_=ch+(cK*40&-1)+32|0;ck=0;while(1){if((ck|0)>=(cn|0)){break}if((a[bt+((c[b_>>2]|0)+ck|0)|0]|0)==(a[ck+103664|0]|0)){ck=ck+1|0}else{break L8329}}if((ck|0)==1){cL=0;break L8327}}}while(0);a[14176]=1;is(u);a[14176]=0;if((c[ao>>2]|0)==3){cL=c[ap>>2]|0;break}else{c[13898]=cK;cL=0;break}}else{cL=0}}while(0);if((t1(cL,af,A,B)|0)==0){cM=0.0}else{cs=+t2(A);cM=cs+ +h[B>>3]}uu(cL);cN=cM}else{Z=6091}}while(0);do{if((Z|0)==6091){Z=0;bt=is(t)|0;ch=c[bt>>2]|0;if((ch|0)==1){cO=+(c[bt+8>>2]|0)}else if((ch|0)==2){cO=+h[bt+8>>3]}else if((ch|0)==3){cO=+uz(c[bt+8>>2]|0,0)}else{Z=6095;break L7660}if((c[aq>>2]|0)!=3){cN=cO;break}uu(c[ar>>2]|0);c[aq>>2]=1;cN=cO}}while(0);bt=c[13898]|0;ch=c[8272]|0;L8352:do{if((ch|0)>(bt|0)){a9=c[1054]|0;if((a[a9+(bt*40&-1)|0]&1)==0){cP=8.988465674311579e+307;break}a5=c[a9+(bt*40&-1)+36>>2]|0;cp=a9+(bt*40&-1)+32|0;b_=c[10036]|0;cn=0;while(1){if((cn|0)>=(a5|0)){break}if((a[b_+((c[cp>>2]|0)+cn|0)|0]|0)==(a[cn+148464|0]|0)){cn=cn+1|0}else{cP=8.988465674311579e+307;break L8352}}if((cn|0)!=1){cP=8.988465674311579e+307;break}cp=bt+1|0;c[13898]=cp;L8360:do{if((c[ac>>2]|0)==1){a5=(a[a9+(cp*40&-1)|0]&1)==0;if(a5){Z=6107}else{bc=a[b_+(c[a9+(cp*40&-1)+32>>2]|0)|0]|0;if(!((bc<<24>>24|0)==39|(bc<<24>>24|0)==34)){Z=6107}}if((Z|0)==6107){Z=0;bc=c[10810]|0;if((bc|0)==0){break}bj=a9+(cp*40&-1)+36|0;cl=a9+(cp*40&-1)+32|0;bs=(ch|0)<=(cp|0)|a5;a7=bc;L8367:while(1){bc=c[a7+4>>2]|0;L8369:do{if(!bs){bZ=c[bj>>2]|0;co=0;while(1){if((co|0)>=(bZ|0)){break}if((a[b_+((c[cl>>2]|0)+co|0)|0]|0)==(a[bc+co|0]|0)){co=co+1|0}else{break L8369}}if((a[bc+co|0]|0)==0){break L8367}}}while(0);bc=c[a7>>2]|0;if((bc|0)==0){break L8360}else{a7=bc}}if((a[a7+8|0]&1)!=0){break}if((c[a7+16>>2]|0)!=3){break}}L8379:do{if((cp|0)<(ch|0)){L8381:do{if(!a5){cl=c[a9+(cp*40&-1)+36>>2]|0;bj=a9+(cp*40&-1)+32|0;bs=0;while(1){if((bs|0)>=(cl|0)){break}if((a[b_+((c[bj>>2]|0)+bs|0)|0]|0)==(a[bs+103664|0]|0)){bs=bs+1|0}else{break L8381}}if((bs|0)==1){cQ=0;break L8379}}}while(0);a[14176]=1;is(s);a[14176]=0;if((c[as>>2]|0)==3){cQ=c[at>>2]|0;break}else{c[13898]=cp;cQ=0;break}}else{cQ=0}}while(0);if((t1(cQ,af,C,D)|0)==0){cR=0.0}else{cs=+t2(C);cR=cs+ +h[D>>3]}uu(cQ);cP=cR;break L8352}}while(0);cp=is(r)|0;b_=c[cp>>2]|0;if((b_|0)==1){cS=+(c[cp+8>>2]|0)}else if((b_|0)==2){cS=+h[cp+8>>3]}else if((b_|0)==3){cS=+uz(c[cp+8>>2]|0,0)}else{Z=6133;break L7660}if((c[au>>2]|0)!=3){cP=cS;break}uu(c[av>>2]|0);c[au>>2]=1;cP=cS}else{cP=8.988465674311579e+307}}while(0);if(!(cF>=cP|cN>0.0)){Z=6137;break L7660}ch=cF>cP;if(!(cN<0.0|ch^1)){Z=6139;break L7660}if(!ch){cH=cF;cI=cN;cJ=cP;break}cH=cF+cN*+O(+((cP*1.01-cF)/cN));cI=-0.0-cN;cJ=cF}else{cH=-8.988465674311579e+307;cI=cF;cJ=8.988465674311579e+307}}while(0);if((a[T]&1)==0){a6=c[aw>>2]|0;if((a6|0)!=0){bw=a6;while(1){a6=c[bw+16>>2]|0;ch=c[bw+8>>2]|0;if((ch|0)!=0){uu(ch)}uu(bw);if((a6|0)==0){break}else{bw=a6}}}c[aw>>2]=0}c[ax>>2]=2;h[ay>>3]=cH;h[az>>3]=cI;h[aA>>3]=cJ;bd=a_;be=a$}}while(0);cT=c[13898]|0;if((cT|0)>=(c[8272]|0)){Z=6155;break}a3=c[1054]|0;S=a[a3+(cT*40&-1)|0]|0;if((S&1)==0){a_=bd;a$=be;a0=cT;a1=a3;a2=S;continue}a4=c[a3+(cT*40&-1)+36>>2]|0;bw=a3+(cT*40&-1)+32|0;a6=c[10036]|0;ch=0;while(1){if((ch|0)>=(a4|0)){break}if((a[a6+((c[bw>>2]|0)+ch|0)|0]|0)==(a[ch+103664|0]|0)){ch=ch+1|0}else{a_=bd;a$=be;a0=cT;a1=a3;a2=S;continue L7660}}if((ch|0)==1){Z=6155;break}else{a_=bd;a$=be;a0=cT;a1=a3;a2=S}}if((Z|0)==5670){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==5706){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6271){uf(bY,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6272){uf(bY,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6273){uf(bY,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6274){uf(bY,98544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6277){uf(b1,97288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6280){uf(b1,97288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6e3){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6155){a2=c[aZ>>2]|0;if((a2&3|0)!=0|bd){P=cT;break}c[aZ>>2]=a2|1;if(be){P=cT;break}if((b-1|0)>>>0>=3){P=cT;break}c[aZ>>2]=a2|5;P=cT;break}else if((Z|0)==6023){a2=c[$>>2]|0;if((a2|0)==0){cU=cd;c[$>>2]=0;uf(cU,91952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{cV=a2}while(1){a2=c[cV+16>>2]|0;a1=c[cV+8>>2]|0;if((a1|0)!=0){uu(a1)}uu(cV);if((a2|0)==0){break}else{cV=a2}}cU=c[13898]|0;c[$>>2]=0;uf(cU,91952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==5848){c[13898]=b0;b1=b0;uf(b1,97288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==5850){b1=c[13898]|0;uf(b1,97288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==5981){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==5683){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6095){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6057){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6133){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6137){uf(cK,93560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Z|0)==6139){uf(cK,92744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);cK=c[1054]|0;b1=c[cK+(P*40&-1)+36>>2]|0;b0=c[cK+(P*40&-1)+32>>2]|0;L8460:do{if((a[cK+(P*40&-1)|0]&1)==0){cW=P}else{if((b1|0)>0){cU=c[10036]|0;cV=0;cd=0;cT=b0;while(1){be=a[F+cV|0]|0;if(be<<24>>24==(a[cU+(cV+cT|0)|0]|0)){cX=cT;cY=cd}else{if(be<<24>>24!=36){cW=P;break L8460}cX=cT-1|0;cY=1}cZ=cV+1|0;if((cZ|0)<(cY+b1|0)){cV=cZ;cd=cY;cT=cX}else{break}}if((cY|0)==0){c_=cZ;Z=6167}}else{c_=0;Z=6167}if((Z|0)==6167){cT=a[F+c_|0]|0;if(!((cT<<24>>24|0)==36|(cT<<24>>24|0)==0)){cW=P;break}}cT=64916+(b*688&-1)|0;c[cT>>2]=c[cT>>2]&-4;cT=P+1|0;c[13898]=cT;cW=cT}}while(0);a[K]=a[95856]|0;a[K+1|0]=a[95857|0]|0;a[K+2|0]=a[95858|0]|0;a[K+3|0]=a[95859|0]|0;a[K+4|0]=a[95860|0]|0;a[K+5|0]=a[95861|0]|0;a[K+6|0]=a[95862|0]|0;P=c[cK+(cW*40&-1)+36>>2]|0;c_=c[cK+(cW*40&-1)+32>>2]|0;cZ=a[cK+(cW*40&-1)|0]|0;L8475:do{if((cZ&1)==0){c$=cW;c0=cK;c1=P;c2=c_;c3=cZ}else{if((P|0)>0){cY=c[10036]|0;cX=0;b1=0;b0=c_;while(1){cT=a[F+(cX+2|0)|0]|0;if(cT<<24>>24==(a[cY+(cX+b0|0)|0]|0)){c4=b0;c5=b1}else{if(cT<<24>>24!=36){c$=cW;c0=cK;c1=P;c2=c_;c3=cZ;break L8475}c4=b0-1|0;c5=1}c6=cX+1|0;if((c6|0)<(c5+P|0)){cX=c6;b1=c5;b0=c4}else{break}}if((c5|0)==0){c7=c6;Z=6177}}else{c7=0;Z=6177}if((Z|0)==6177){b0=a[F+(c7+2|0)|0]|0;if(!((b0<<24>>24|0)==36|(b0<<24>>24|0)==0)){c$=cW;c0=cK;c1=P;c2=c_;c3=cZ;break}}if((a[64976+(b*688&-1)|0]&1)==0){b0=64944+(b*688&-1)|0;b1=c[b0>>2]|0;if((b1|0)==0){c8=cW;c9=cK}else{cX=b1;while(1){b1=c[cX+16>>2]|0;cY=c[cX+8>>2]|0;if((cY|0)!=0){uu(cY)}uu(cX);if((b1|0)==0){break}else{cX=b1}}c8=c[13898]|0;c9=c[1054]|0}c[b0>>2]=0;da=c8;db=c9}else{da=cW;db=cK}c[64920+(b*688&-1)>>2]=4;cX=da+1|0;c[13898]=cX;c$=cX;c0=db;c1=c[db+(cX*40&-1)+36>>2]|0;c2=c[db+(cX*40&-1)+32>>2]|0;c3=a[db+(cX*40&-1)|0]|0}}while(0);L8501:do{if((c3&1)==0){dc=c$}else{if((c1|0)>0){db=c[10036]|0;da=0;cK=0;cW=c2;while(1){c9=a[F+da|0]|0;if(c9<<24>>24==(a[db+(da+cW|0)|0]|0)){dd=cW;de=cK}else{if(c9<<24>>24!=36){dc=c$;break L8501}dd=cW-1|0;de=1}df=da+1|0;if((df|0)<(de+c1|0)){da=df;cK=de;cW=dd}else{break}}if((de|0)==0){dg=df;Z=6194}}else{dg=0;Z=6194}if((Z|0)==6194){cW=a[F+dg|0]|0;if(!((cW<<24>>24|0)==36|(cW<<24>>24|0)==0)){dc=c$;break}}c[64920+(b*688&-1)>>2]=1;cW=c$+1|0;c[13898]=cW;dc=cW}}while(0);a[K]=a[95384]|0;a[K+1|0]=a[95385|0]|0;a[K+2|0]=a[95386|0]|0;a[K+3|0]=a[95387|0]|0;a[K+4|0]=a[95388|0]|0;a[K+5|0]=a[95389|0]|0;a[K+6|0]=a[95390|0]|0;K=c[c0+(dc*40&-1)+36>>2]|0;c$=c[c0+(dc*40&-1)+32>>2]|0;dg=a[c0+(dc*40&-1)|0]|0;L8516:do{if((dg&1)==0){dh=dc;di=K;dj=c$;dk=dg}else{if((K|0)>0){df=c[10036]|0;de=0;dd=0;c1=c$;while(1){c2=a[F+(de+2|0)|0]|0;if(c2<<24>>24==(a[df+(de+c1|0)|0]|0)){dm=c1;dn=dd}else{if(c2<<24>>24!=36){dh=dc;di=K;dj=c$;dk=dg;break L8516}dm=c1-1|0;dn=1}dq=de+1|0;if((dq|0)<(dn+K|0)){de=dq;dd=dn;c1=dm}else{break}}if((dn|0)==0){dr=dq;Z=6204}}else{dr=0;Z=6204}if((Z|0)==6204){c1=a[F+(dr+2|0)|0]|0;if(!((c1<<24>>24|0)==36|(c1<<24>>24|0)==0)){dh=dc;di=K;dj=c$;dk=dg;break}}if((a[64976+(b*688&-1)|0]&1)==0){c1=64944+(b*688&-1)|0;dd=c[c1>>2]|0;if((dd|0)==0){ds=dc;dt=c0}else{de=dd;while(1){dd=c[de+16>>2]|0;df=c[de+8>>2]|0;if((df|0)!=0){uu(df)}uu(de);if((dd|0)==0){break}else{de=dd}}ds=c[13898]|0;dt=c[1054]|0}c[c1>>2]=0;du=ds;dv=dt}else{du=dc;dv=c0}c[64920+(b*688&-1)>>2]=5;de=du+1|0;c[13898]=de;dh=de;di=c[dv+(de*40&-1)+36>>2]|0;dj=c[dv+(de*40&-1)+32>>2]|0;dk=a[dv+(de*40&-1)|0]|0}}while(0);L8542:do{if((dk&1)!=0){if((di|0)>0){dv=c[10036]|0;du=0;c0=0;dc=dj;while(1){dt=a[F+du|0]|0;if(dt<<24>>24==(a[dv+(du+dc|0)|0]|0)){dw=dc;dx=c0}else{if(dt<<24>>24!=36){break L8542}dw=dc-1|0;dx=1}dy=du+1|0;if((dy|0)<(dx+di|0)){du=dy;c0=dx;dc=dw}else{break}}if((dx|0)==0){dz=dy;Z=6221}}else{dz=0;Z=6221}if((Z|0)==6221){dc=a[F+dz|0]|0;if(!((dc<<24>>24|0)==36|(dc<<24>>24|0)==0)){break}}c[64920+(b*688&-1)>>2]=1;c[13898]=dh+1}}while(0);a[I]=109;uB(F+3|0,J|0);J=F+((uA(I|0)|0)+2|0)|0;a[J]=a[120232]|0;a[J+1|0]=a[120233|0]|0;a[J+2|0]=a[120234|0]|0;a[J+3|0]=a[120235|0]|0;a[J+4|0]=a[120236|0]|0;a[J+5|0]=a[120237|0]|0;J=c[13898]|0;I=c[1054]|0;dh=c[I+(J*40&-1)+36>>2]|0;dz=c[I+(J*40&-1)+32>>2]|0;L8557:do{if((a[I+(J*40&-1)|0]&1)==0){dA=J;dB=I}else{if((dh|0)>0){dy=c[10036]|0;dx=0;dw=0;di=dz;while(1){dj=a[F+(dx+2|0)|0]|0;if(dj<<24>>24==(a[dy+(dx+di|0)|0]|0)){dC=di;dD=dw}else{if(dj<<24>>24!=36){dA=J;dB=I;break L8557}dC=di-1|0;dD=1}dE=dx+1|0;if((dE|0)<(dD+dh|0)){dx=dE;dw=dD;di=dC}else{break}}if((dD|0)==0){dF=dE;Z=6231}}else{dF=0;Z=6231}if((Z|0)==6231){di=a[F+(dF+2|0)|0]|0;if(!((di<<24>>24|0)==36|(di<<24>>24|0)==0)){dA=J;dB=I;break}}di=J+1|0;c[13898]=di;L8571:do{if((di|0)<(c[8272]|0)){dw=(a[I+(di*40&-1)|0]&1)==0;L8573:do{if(!dw){dx=c[I+(di*40&-1)+36>>2]|0;dy=I+(di*40&-1)+32|0;dj=c[10036]|0;dk=0;while(1){if((dk|0)>=(dx|0)){Z=6237;break}if((a[dj+((c[dy>>2]|0)+dk|0)|0]|0)==(a[dk+103664|0]|0)){dk=dk+1|0}else{break}}if((Z|0)==6237){if((dk|0)==1){break L8571}}if(!((dx|0)>0&(dw^1))){break}dj=c[10036]|0;dc=0;c0=0;du=c[dy>>2]|0;while(1){if((a[dc+184456|0]|0)==(a[dj+(dc+du|0)|0]|0)){dG=du;dH=c0}else{if((dc|0)!=3){break L8573}dG=du-1|0;dH=1}dv=dc+1|0;if((dv|0)<(dH+dx|0)){dc=dv;c0=dH;du=dG}else{break}}if((dH|0)==0){if(!((dc|0)==2|(dc|0)==7)){break}}c[65040+(b*688&-1)>>2]=1;du=J+2|0;c[13898]=du;dA=du;dB=I;break L8557}}while(0);dw=is(E)|0;S=c[dw>>2]|0;if((S|0)==1){dI=+(c[dw+8>>2]|0)}else if((S|0)==2){dI=+h[dw+8>>3]}else if((S|0)==3){dI=+uz(c[dw+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}dw=E|0;if((c[dw>>2]|0)==3){uu(c[E+8>>2]|0);c[dw>>2]=1}h[65048+(b*688&-1)>>3]=+O(+dI);c[65040+(b*688&-1)>>2]=2;dA=c[13898]|0;dB=c[1054]|0;break L8557}}while(0);c[65040+(b*688&-1)>>2]=3;dA=di;dB=I}}while(0);I=c[dB+(dA*40&-1)+36>>2]|0;if((a[dB+(dA*40&-1)|0]&1)==0){i=d;return}do{if((I|0)>0){E=c[10036]|0;J=0;dH=0;dG=c[dB+(dA*40&-1)+32>>2]|0;while(1){dF=a[F+J|0]|0;if(dF<<24>>24==(a[E+(J+dG|0)|0]|0)){dJ=dG;dK=dH}else{if(dF<<24>>24!=36){Z=6267;break}dJ=dG-1|0;dK=1}dL=J+1|0;if((dL|0)<(dK+I|0)){J=dL;dH=dK;dG=dJ}else{Z=6263;break}}if((Z|0)==6267){i=d;return}else if((Z|0)==6263){if((dK|0)==0){dM=dL;Z=6264;break}else{break}}}else{dM=0;Z=6264}}while(0);do{if((Z|0)==6264){dL=a[F+dM|0]|0;if((dL<<24>>24|0)==36|(dL<<24>>24|0)==0){break}i=d;return}}while(0);c[65040+(b*688&-1)>>2]=0;c[13898]=dA+1;i=d;return}function ju(){var b=0,d=0.0,e=0,f=0,g=0.0,i=0.0,j=0.0,k=0.0;b=c[17367]|0;d=(b&1|0)==0?+h[8687]:0.0;e=c[200]|0;f=64652+(e*688&-1)|0;if((b&2|0)!=0){c[f>>2]=3;c[64652+((c[144]|0)*688&-1)>>2]=3;return}c[f>>2]=0;f=c[144]|0;c[64652+(f*688&-1)>>2]=0;g=+h[8688];if((a[69604]&1)==0){i=g-d;h[64688+(e*688&-1)>>3]=i;j=i}else{i=+_(+g);g=+h[8702];k=i/g- +_(+d)/g;h[64688+(e*688&-1)>>3]=k;j=k}h[64688+(f*688&-1)>>3]=j;j=-0.0- +h[64688+(e*688&-1)>>3];h[64680+(e*688&-1)>>3]=j;h[64680+(f*688&-1)>>3]=j;return}function jv(b){b=b|0;var d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0;d=i;e=c[13898]|0;f=e+1|0;c[13898]=f;do{if((c[5094]|0)!=0){if(!(a[20368]|0)){break}a[20368]=0;g[3538]=+g[5090];g[3536]=+g[5088];g[3534]=+g[5086];c[16336]=c[16336]^2;c[17024]=c[17024]^2}}while(0);j=c[1054]|0;k=c[j+(f*40&-1)+36>>2]|0;l=c[j+(f*40&-1)+32>>2]|0;m=(a[j+(f*40&-1)|0]&1)==0;L8638:do{if((k|0)>0&(m^1)){j=c[10036]|0;n=0;o=0;p=l;while(1){if((a[n+91248|0]|0)==(a[j+(n+p|0)|0]|0)){q=p;r=o}else{if((n|0)!=2){s=6302;break L8638}q=p-1|0;r=1}t=n+1|0;if((t|0)<(r+k|0)){n=t;o=r;p=q}else{break}}if((r|0)==0){if(!((n|0)==7|(n|0)==1)){s=6302;break}}c[13898]=e+2;h[64680+(b*688&-1)>>3]=+h[64696+(b*688&-1)>>3];h[64688+(b*688&-1)>>3]=+h[64704+(b*688&-1)>>3];c[64652+(b*688&-1)>>2]=0}else{s=6302}}while(0);L8650:do{if((s|0)==6302){if((c[8272]|0)<=(f|0)|m){uf(f,90200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}r=c[10036]|0;q=0;while(1){if((q|0)>=(k|0)){break}if((a[r+(l+q|0)|0]|0)==(a[q+78280|0]|0)){q=q+1|0}else{s=6352;break}}if((s|0)==6352){uf(f,90200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((q|0)!=1){uf(f,90200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13898]=e+2;r=64652+(b*688&-1)|0;c[r>>2]=dj(b,64680+(b*688&-1)|0,64688+(b*688&-1)|0,c[r>>2]|0)|0;r=c[13898]|0;if((c[8272]|0)<=(r|0)){uf(r,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=c[1054]|0;if((a[n+(r*40&-1)|0]&1)==0){uf(r,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=c[n+(r*40&-1)+36>>2]|0;o=n+(r*40&-1)+32|0;j=c[10036]|0;t=0;while(1){if((t|0)>=(p|0)){break}if((a[j+((c[o>>2]|0)+t|0)|0]|0)==(a[t+78864|0]|0)){t=t+1|0}else{s=6356;break}}if((s|0)==6356){uf(r,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((t|0)!=1){uf(r,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=r+1|0;c[13898]=o;p=c[n+(o*40&-1)+36>>2]|0;q=c[n+(o*40&-1)+32>>2]|0;u=(a[n+(o*40&-1)|0]&1)==0;L8679:do{if((p|0)>0&(u^1)){w=0;x=0;y=q;while(1){if((a[w+146208|0]|0)==(a[j+(w+y|0)|0]|0)){z=y;A=x}else{if((w|0)!=3){break}z=y-1|0;A=1}B=w+1|0;if((B|0)<(A+p|0)){w=B;x=A;y=z}else{s=6320;break}}do{if((s|0)==6320){if((A|0)==0){if(!((w|0)==7|(w|0)==2)){break}}y=r+2|0;c[13898]=y;x=64656+(b*688&-1)|0;c[x>>2]=c[x>>2]|2;C=y;break L8679}}while(0);if((p|0)>0&(u^1)){D=0;E=0;F=q}else{C=o;break}while(1){if((a[D+146040|0]|0)==(a[j+(D+F|0)|0]|0)){G=F;H=E}else{if((D|0)!=5){C=o;break L8679}G=F-1|0;H=1}w=D+1|0;if((w|0)<(H+p|0)){D=w;E=H;F=G}else{break}}if((H|0)==0){if(!((D|0)==4|(D|0)==9)){C=o;break}}w=r+2|0;c[13898]=w;y=64656+(b*688&-1)|0;c[y>>2]=c[y>>2]&-3;C=w}else{C=o}}while(0);o=c[n+(C*40&-1)+36>>2]|0;r=c[n+(C*40&-1)+32>>2]|0;p=(a[n+(C*40&-1)|0]&1)==0;if((o|0)>0&(p^1)){I=0;J=0;K=r}else{break}while(1){if((a[I+86128|0]|0)==(a[j+(I+K|0)|0]|0)){L=K;M=J}else{if((I|0)!=2){break}L=K-1|0;M=1}q=I+1|0;if((q|0)<(M+o|0)){I=q;J=M;K=L}else{s=6336;break}}do{if((s|0)==6336){if((M|0)==0){if(!((I|0)==9|(I|0)==1)){break}}c[13898]=C+1;n=64656+(b*688&-1)|0;c[n>>2]=c[n>>2]|1;break L8650}}while(0);if((o|0)>0&(p^1)){N=0;O=0;P=r}else{break}while(1){if((a[N+84952|0]|0)==(a[j+(N+P|0)|0]|0)){Q=P;R=O}else{if((N|0)!=5){break L8650}Q=P-1|0;R=1}n=N+1|0;if((n|0)<(R+o|0)){N=n;O=R;P=Q}else{break}}if((R|0)==0){if(!((N|0)==4|(N|0)==11)){break}}c[13898]=C+1;o=64656+(b*688&-1)|0;c[o>>2]=c[o>>2]&-2}}while(0);if((c[5094]|0)==0){i=d;return}if(a[20368]|0){i=d;return}a[20368]=1;g[5090]=+g[3538];g[5088]=+g[3536];g[5086]=+g[3534];g[3538]=180.0;g[3536]=0.0;g[3534]=1.2999999523162842;c[16336]=c[16336]^2;c[17024]=c[17024]^2;i=d;return}function jw(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0.0,T=0.0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0.0,aq=0.0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0.0,aA=0.0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0.0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0.0,bf=0.0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0.0,bx=0.0,by=0,bz=0,bA=0,bB=0,bC=0,bD=0,bE=0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bM=0.0,bN=0.0,bO=0,bP=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0,b7=0.0,b8=0.0,b9=0,ca=0,cb=0,cc=0,cd=0,ce=0,cf=0,cg=0,ch=0,ci=0,cj=0,ck=0,cl=0.0,cm=0.0,cn=0,co=0,cp=0,cq=0,cr=0,cs=0,ct=0.0,cu=0.0,cv=0,cw=0,cx=0,cy=0,cz=0,cA=0,cB=0,cC=0,cD=0,cE=0,cF=0,cG=0.0,cH=0.0,cI=0,cJ=0,cK=0,cL=0,cM=0,cN=0,cO=0,cP=0,cQ=0,cR=0,cS=0,cT=0,cU=0,cV=0.0,cW=0.0,cX=0,cY=0,cZ=0,c_=0,c$=0,c0=0,c1=0,c2=0,c3=0.0,c4=0.0,c5=0,c6=0,c7=0,c8=0,c9=0,da=0,db=0.0,dc=0.0,dd=0,de=0,df=0,dg=0,dh=0,di=0,dj=0.0,dk=0.0,dm=0,dn=0,dp=0,dq=0,dr=0,ds=0,dt=0,du=0.0,dv=0.0,dw=0,dx=0,dy=0,dz=0,dA=0,dB=0.0,dC=0.0,dD=0,dE=0;d=i;i=i+224|0;e=d|0;f=d+24|0;g=d+48|0;j=d+88|0;k=d+128|0;l=d+168|0;m=b+4|0;n=(c[m>>2]|0)==-2;o=j;uE(o|0,0,40);c[j>>2]=4;c[j+4>>2]=4;c[j+8>>2]=4;p=k;uE(p|0,0,16);q=d+144|0;uE(q|0,0,24);r=c[13898]|0;s=c[8272]|0;do{if((r|0)<(s|0)){t=b+184|0;u=l|0;w=l+4|0;x=l+8|0;y=l+16|0;z=l+24|0;A=l+32|0;B=f|0;C=f+8|0;D=b+52|0;E=e|0;F=e+8|0;G=0;H=0;I=0;J=0;K=0;L=0;M=0;N=0;O=0;P=0;Q=0;R=0;S=-2.0;T=1.0;U=-2;V=-2;W=0;X=r;Y=s;L8736:while(1){Z=c[1054]|0;_=(a[Z+(X*40&-1)|0]&1)==0;L8738:do{if(!_){$=c[Z+(X*40&-1)+36>>2]|0;aa=Z+(X*40&-1)+32|0;ab=c[10036]|0;ac=0;while(1){if((ac|0)>=($|0)){break}if((a[ab+((c[aa>>2]|0)+ac|0)|0]|0)==(a[ac+103664|0]|0)){ac=ac+1|0}else{break L8738}}if((ac|0)==1){ad=G;ae=H;af=I;ag=J;ah=K;ai=L;aj=M;ak=N;al=O;am=P;an=Q;ao=R;ap=S;aq=T;ar=U;as=V;at=W;au=6556;break L8736}}}while(0);L8745:do{if(J|(Y|0)<=(X|0)|_){au=6376}else{aa=c[Z+(X*40&-1)+36>>2]|0;ab=Z+(X*40&-1)+32|0;$=c[10036]|0;av=0;while(1){if((av|0)>=(aa|0)){break}if((a[$+((c[ab>>2]|0)+av|0)|0]|0)==(a[av+95280|0]|0)){av=av+1|0}else{au=6376;break L8745}}if(n|(av|0)!=2){au=6376;break}c[13898]=X+1;dl(g,0);aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=K;aJ=1;aK=I;aL=G;aM=H}}while(0);L8752:do{if((au|0)==6376){au=0;ab=c[Z+(X*40&-1)+36>>2]|0;$=c[Z+(X*40&-1)+32>>2]|0;L8754:do{if(K){au=6413}else{if(!((ab|0)>0&(_^1))){break}aa=c[10036]|0;ac=0;aN=0;aO=$;while(1){if((a[ac+149696|0]|0)==(a[aa+(ac+aO|0)|0]|0)){aP=aO;aQ=aN}else{if((ac|0)!=1){break}aP=aO-1|0;aQ=1}aR=ac+1|0;if((aR|0)<(aQ+ab|0)){ac=aR;aN=aQ;aO=aP}else{au=6383;break}}do{if((au|0)==6383){au=0;if((aQ|0)==0){if(!((ac|0)==4|(ac|0)==0)){break}}c[13898]=X+1;aw=W;ax=V;ay=U;az=T;aA=S;aB=0;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=1;aJ=J;aK=I;aL=G;aM=H;break L8752}}while(0);if(!((ab|0)>0&(_^1))){break}ac=c[10036]|0;aO=0;aN=0;aa=$;while(1){if((a[aO+179896|0]|0)==(a[ac+(aO+aa|0)|0]|0)){aS=aa;aT=aN}else{if((aO|0)!=1){au=6394;break}aS=aa-1|0;aT=1}aR=aO+1|0;if((aR|0)<(aT+ab|0)){aO=aR;aN=aT;aa=aS}else{au=6392;break}}do{if((au|0)==6392){au=0;if((aT|0)!=0){break}if(!((aO|0)==0|(aO|0)==6)){au=6394}}}while(0);L8779:do{if((au|0)==6394){au=0;if(_){break L8754}L8782:do{if((ab|0)>0){aO=c[10036]|0;aa=0;aN=0;ac=$;while(1){if((a[aa+149304|0]|0)==(a[aO+(aa+ac|0)|0]|0)){aU=ac;aV=aN}else{if((aa|0)!=1){break L8782}aU=ac-1|0;aV=1}aR=aa+1|0;if((aR|0)<(aV+ab|0)){aa=aR;aN=aV;ac=aU}else{break}}if((aV|0)!=0){break L8779}if((aa|0)==0|(aa|0)==6){break L8779}}}while(0);if(!((ab|0)>0&(_^1))){break L8754}ac=c[10036]|0;aN=0;aO=0;aR=$;while(1){if((a[aN+149568|0]|0)==(a[ac+(aN+aR|0)|0]|0)){aW=aR;aX=aO}else{if((aN|0)!=1){au=6413;break L8754}aW=aR-1|0;aX=1}aY=aN+1|0;if((aY|0)<(aX+ab|0)){aN=aY;aO=aX;aR=aW}else{break}}if((aX|0)==0){if(!((aN|0)==0|(aN|0)==5)){au=6413;break L8754}}c[13898]=X+1;aw=W;ax=V;ay=U;az=T;aA=S;aB=2;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=1;aJ=J;aK=I;aL=G;aM=H;break L8752}}while(0);c[13898]=X+1;aw=W;ax=V;ay=U;az=T;aA=S;aB=1;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=1;aJ=J;aK=I;aL=G;aM=H;break L8752}}while(0);L8805:do{if((au|0)==6413){au=0;if(_){break}L8808:do{if((ab|0)>0){av=c[10036]|0;aR=0;aO=0;ac=$;while(1){if((a[aR+90456|0]|0)==(a[av+(aR+ac|0)|0]|0)){aZ=ac;a_=aO}else{if((aR|0)!=3){break L8808}aZ=ac-1|0;a_=1}aY=aR+1|0;if((aY|0)<(a_+ab|0)){aR=aY;aO=a_;ac=aZ}else{break}}if((a_|0)==0){if(!((aR|0)==6|(aR|0)==2)){break}}a$=X+1|0;c[13898]=a$;ac=c[D>>2]|0;aO=a[Z+(a$*40&-1)|0]|0;L8820:do{if((Y|0)>(a$|0)){if((aO&1)==0){au=6427;break}av=c[Z+(a$*40&-1)+36>>2]|0;aN=Z+(a$*40&-1)+32|0;aY=c[10036]|0;a0=0;while(1){if((a0|0)>=(av|0)){break}if((a[aY+((c[aN>>2]|0)+a0|0)|0]|0)==(a[a0+163312|0]|0)){a0=a0+1|0}else{au=6427;break L8820}}if((a0|0)!=2){a1=av;a2=aN;break}c[13898]=X+2;aY=is(e)|0;aa=c[aY>>2]|0;if((aa|0)==1){a3=+(c[aY+8>>2]|0)}else if((aa|0)==2){a3=+h[aY+8>>3]}else if((aa|0)==3){a3=+uz(c[aY+8>>2]|0,0)}else{au=6433;break L8736}if((c[E>>2]|0)==3){uu(c[F>>2]|0);c[E>>2]=1}aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=1;aI=K;aJ=J;aK=~~a3;aL=G;aM=H;break L8752}else{au=6427}}while(0);if((au|0)==6427){au=0;a1=c[Z+(a$*40&-1)+36>>2]|0;a2=Z+(a$*40&-1)+32|0}if(!((aO&1)!=0&(a1|0)>0)){aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=1;aI=K;aJ=J;aK=-270;aL=G;aM=H;break L8752}aR=c[10036]|0;aY=0;aa=0;a4=c[a2>>2]|0;while(1){if((a[aY+154360|0]|0)==(a[aR+(aY+a4|0)|0]|0)){a5=a4;a6=aa}else{if((aY|0)!=4){aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=1;aI=K;aJ=J;aK=-270;aL=G;aM=H;break L8752}a5=a4-1|0;a6=1}a7=aY+1|0;if((a7|0)<(a6+a1|0)){aY=a7;aa=a6;a4=a5}else{break}}if((a6|0)==0){if(!((aY|0)==3|(aY|0)==8)){aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=1;aI=K;aJ=J;aK=-270;aL=G;aM=H;break L8752}}if((c[m>>2]|0)>-1){au=6446;break L8736}c[13898]=X+2;c[m>>2]=-3;aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=1;aI=K;aJ=J;aK=ac;aL=G;aM=H;break L8752}}while(0);if(!((ab|0)>0&(_^1))){break}a4=c[10036]|0;aa=0;aR=0;aO=$;while(1){if((a[aa+149328|0]|0)==(a[a4+(aa+aO|0)|0]|0)){a8=aO;a9=aR}else{if((aa|0)!=5){break L8805}a8=aO-1|0;a9=1}a7=aa+1|0;if((a7|0)<(a9+ab|0)){aa=a7;aR=a9;aO=a8}else{break}}if((a9|0)==0){if(!((aa|0)==4|(aa|0)==8)){break}}c[13898]=X+1;if((c[m>>2]|0)!=-3){aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=1;aI=K;aJ=J;aK=0;aL=G;aM=H;break L8752}c[m>>2]=-2;aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=1;aI=K;aJ=J;aK=0;aL=G;aM=H;break L8752}}while(0);L8865:do{if(!(M|(Y|0)<=(X|0)|_)){aO=c[10036]|0;aR=0;while(1){if((aR|0)>=(ab|0)){break}if((a[aO+($+aR|0)|0]|0)==(a[aR+124352|0]|0)){aR=aR+1|0}else{break L8865}}if((aR|0)!=4){break}ba=X+1|0;c[13898]=ba;if((ba|0)>=(Y|0)){bb=ba;au=6577;break L8736}L8873:do{if((a[Z+(ba*40&-1)|0]&1)!=0){aa=c[Z+(ba*40&-1)+36>>2]|0;a4=Z+(ba*40&-1)+32|0;a7=0;while(1){if((a7|0)>=(aa|0)){break}if((a[aO+((c[a4>>2]|0)+a7|0)|0]|0)==(a[a7+103664|0]|0)){a7=a7+1|0}else{break L8873}}if((a7|0)==1){bb=ba;au=6578;break L8736}}}while(0);a[14176]=1;is(f);a[14176]=0;if((c[B>>2]|0)!=3){au=6470;break L8736}aO=c[C>>2]|0;if((aO|0)==0){au=6472;break L8736}else{aw=aO;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=1;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H;break L8752}}}while(0);L8882:do{if(!(O|n)){aO=(Y|0)>(X|0);if(_|aO^1){break}aR=c[10036]|0;a4=0;while(1){if((a4|0)>=(ab|0)){au=6479;break}if((a[aR+($+a4|0)|0]|0)==(a[a4+150704|0]|0)){a4=a4+1|0}else{break}}do{if((au|0)==6479){au=0;if((a4|0)!=4){break}c[13898]=X+1;aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=0;aD=P;aE=1;aF=N;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H;break L8752}}while(0);if(_|aO^1){break}a4=c[10036]|0;aR=0;while(1){if((aR|0)>=(ab|0)){break}if((a[a4+($+aR|0)|0]|0)==(a[aR+150856|0]|0)){aR=aR+1|0}else{break L8882}}if((aR|0)!=5){break}c[13898]=X+1;aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=1;aD=P;aE=1;aF=N;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H;break L8752}}while(0);L8899:do{if((ab|0)>0&(((V|0)!=-2|n|_)^1)){a4=c[10036]|0;aO=0;aa=0;ac=$;while(1){if((a[aO+140256|0]|0)==(a[a4+(aO+ac|0)|0]|0)){bc=ac;bd=aa}else{if((aO|0)!=2){break}bc=ac-1|0;bd=1}aY=aO+1|0;if((aY|0)<(bd+ab|0)){aO=aY;aa=bd;ac=bc}else{au=6493;break}}do{if((au|0)==6493){au=0;if((bd|0)==0){if(!((aO|0)==5|(aO|0)==1)){break}}ac=X+1|0;c[13898]=ac;c[u>>2]=1;c[w>>2]=U;c[x>>2]=H;c[x+4>>2]=G;h[y>>3]=T;h[z>>3]=S;c[A>>2]=c[q>>2];c[A+4>>2]=c[q+4>>2];c[A+8>>2]=c[q+8>>2];c[A+12>>2]=c[q+12>>2];c[A+16>>2]=c[q+16>>2];c[A+20>>2]=c[q+20>>2];hH(l,1,1);if((ac|0)==(c[13898]|0)){aw=W;ax=1;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H;break L8752}ac=c[u>>2]|0;aa=c[w>>2]|0;a4=c[x>>2]|0;aR=c[x+4>>2]|0;be=+h[y>>3];bf=+h[z>>3];c[q>>2]=c[A>>2];c[q+4>>2]=c[A+4>>2];c[q+8>>2]=c[A+8>>2];c[q+12>>2]=c[A+12>>2];c[q+16>>2]=c[A+16>>2];c[q+20>>2]=c[A+20>>2];aw=W;ax=ac;ay=aa;az=be;aA=bf;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=aR;aM=a4;break L8752}}while(0);if(!((ab|0)>0&(_^1))){break}aO=c[10036]|0;a4=0;aR=0;aa=$;while(1){if((a[a4+138112|0]|0)==(a[aO+(a4+aa|0)|0]|0)){bg=aa;bh=aR}else{if((a4|0)!=4){break L8899}bg=aa-1|0;bh=1}ac=a4+1|0;if((ac|0)<(bh+ab|0)){a4=ac;aR=bh;aa=bg}else{break}}if((bh|0)==0){if(!((a4|0)==3|(a4|0)==7)){break}}c[13898]=X+1;aw=W;ax=0;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H;break L8752}}while(0);L8926:do{if((ab|0)>0&((N|_)^1)){aa=c[10036]|0;aR=0;aO=0;ac=$;while(1){if((a[aR+136456|0]|0)==(a[aa+(aR+ac|0)|0]|0)){bi=ac;bj=aO}else{if((aR|0)!=2){break L8926}bi=ac-1|0;bj=1}aY=aR+1|0;if((aY|0)<(bj+ab|0)){aR=aY;aO=bj;ac=bi}else{break}}if((bj|0)==0){if(!((aR|0)==1|(aR|0)==6)){break}}c[13898]=X+1;dl(j,4);aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=1;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H;break L8752}}while(0);L8939:do{if((Y|0)>(X|0)){if(_){au=6544;break L8736}ac=c[10036]|0;aO=0;while(1){if((aO|0)>=(ab|0)){break}if((a[ac+($+aO|0)|0]|0)==(a[aO+143040|0]|0)){aO=aO+1|0}else{au=6521;break L8939}}if((aO|0)!=2){au=6521;break}if(!P){au=6532}}else{au=6521}}while(0);L8947:do{if((au|0)==6521){au=0;if(_){au=6544;break L8736}if((ab|0)>0){ac=c[10036]|0;aR=0;aa=0;a4=$;while(1){if((a[aR+142856|0]|0)==(a[ac+(aR+a4|0)|0]|0)){bk=a4;bl=aa}else{if((aR|0)!=4){break L8947}bk=a4-1|0;bl=1}bm=aR+1|0;if((bm|0)<(bl+ab|0)){aR=bm;aa=bl;a4=bk}else{break}}if((bl|0)==0){bn=bm;au=6529}else{bo=0}}else{bn=0;au=6529}if((au|0)==6529){au=0;bo=(bn|0)!=10&(bn|0)!=4}if(!(bo|P)){au=6532}}}while(0);if((au|0)==6532){au=0;hI(k,7);aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=1;aE=O;aF=N;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H;break}if(_){au=6534;break L8736}if((ab|0)<=0){au=6547;break L8736}a4=c[10036]|0;aa=0;aR=0;ac=$;while(1){if((a[aa+121536|0]|0)==(a[a4+(aa+ac|0)|0]|0)){bp=ac;bq=aR}else{if((aa|0)!=5){break}bp=ac-1|0;bq=1}aO=aa+1|0;if((aO|0)<(bq+ab|0)){aa=aO;aR=bq;ac=bp}else{au=6541;break}}do{if((au|0)==6541){au=0;if((bq|0)==0){if(!((aa|0)==4|(aa|0)==10)){break}}a[t]=1;c[13898]=(c[13898]|0)+1;aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H;break L8752}}while(0);if(_){ad=G;ae=H;af=I;ag=J;ah=K;ai=L;aj=M;ak=N;al=O;am=P;an=Q;ao=R;ap=S;aq=T;ar=U;as=V;at=W;au=6556;break L8736}if((ab|0)<=0){au=6547;break L8736}aa=c[10036]|0;ac=0;aR=0;a4=$;while(1){if((a[ac+122880|0]|0)==(a[aa+(ac+a4|0)|0]|0)){br=a4;bs=aR}else{if((ac|0)!=3){ad=G;ae=H;af=I;ag=J;ah=K;ai=L;aj=M;ak=N;al=O;am=P;an=Q;ao=R;ap=S;aq=T;ar=U;as=V;at=W;au=6556;break L8736}br=a4-1|0;bs=1}aO=ac+1|0;if((aO|0)<(bs+ab|0)){ac=aO;aR=bs;a4=br}else{break}}if((bs|0)==0){if(!((ac|0)==2|(ac|0)==8)){ad=G;ae=H;af=I;ag=J;ah=K;ai=L;aj=M;ak=N;al=O;am=P;an=Q;ao=R;ap=S;aq=T;ar=U;as=V;at=W;au=6556;break L8736}}a[t]=0;c[13898]=(c[13898]|0)+1;aw=W;ax=V;ay=U;az=T;aA=S;aB=R;aC=Q;aD=P;aE=O;aF=N;aG=M;aH=L;aI=K;aJ=J;aK=I;aL=G;aM=H}}while(0);_=c[13898]|0;Z=c[8272]|0;if((_|0)<(Z|0)){G=aL;H=aM;I=aK;J=aJ;K=aI;L=aH;M=aG;N=aF;O=aE;P=aD;Q=aC;R=aB;S=aA;T=az;U=ay;V=ax;W=aw;X=_;Y=Z}else{ad=aL;ae=aM;af=aK;ag=aJ;ah=aI;ai=aH;aj=aG;ak=aF;al=aE;am=aD;an=aC;ao=aB;ap=aA;aq=az;ar=ay;as=ax;at=aw;au=6556;break}}if((au|0)==6577){uf(bb,145200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==6578){uf(bb,145200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==6534){if(J){bt=W;bu=V;bv=U;bw=T;bx=S;by=R;bz=Q;bA=P;bB=O;bC=N;bD=M;bE=L;bF=K;bG=I;bH=G;bI=H;au=6558}else{bJ=W;bK=V;bL=U;bM=T;bN=S;bO=R;bP=Q;bQ=P;bR=O;bS=N;bT=M;bU=L;bV=K;bW=I;bX=G;bY=H;au=6557}}else if((au|0)==6544){if(J){bt=W;bu=V;bv=U;bw=T;bx=S;by=R;bz=Q;bA=P;bB=O;bC=N;bD=M;bE=L;bF=K;bG=I;bH=G;bI=H;au=6558}else{bJ=W;bK=V;bL=U;bM=T;bN=S;bO=R;bP=Q;bQ=P;bR=O;bS=N;bT=M;bU=L;bV=K;bW=I;bX=G;bY=H;au=6557}}else if((au|0)==6547){if(J){bt=W;bu=V;bv=U;bw=T;bx=S;by=R;bz=Q;bA=P;bB=O;bC=N;bD=M;bE=L;bF=K;bG=I;bH=G;bI=H;au=6558}else{bJ=W;bK=V;bL=U;bM=T;bN=S;bO=R;bP=Q;bQ=P;bR=O;bS=N;bT=M;bU=L;bV=K;bW=I;bX=G;bY=H;au=6557}}else if((au|0)==6556){if(ag){bt=at;bu=as;bv=ar;bw=aq;bx=ap;by=ao;bz=an;bA=am;bB=al;bC=ak;bD=aj;bE=ai;bF=ah;bG=af;bH=ad;bI=ae;au=6558}else{bJ=at;bK=as;bL=ar;bM=aq;bN=ap;bO=ao;bP=an;bQ=am;bR=al;bS=ak;bT=aj;bU=ai;bV=ah;bW=af;bX=ad;bY=ae;au=6557}}else if((au|0)==6470){c[13898]=ba;bb=ba;uf(bb,145200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==6472){bb=c[13898]|0;uf(bb,145200,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==6446){uf(a$,151096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((au|0)==6433){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((au|0)==6558){Y=b+8|0;X=g;c[Y>>2]=c[X>>2];c[Y+4>>2]=c[X+4>>2];c[Y+8>>2]=c[X+8>>2];c[Y+12>>2]=c[X+12>>2];c[Y+16>>2]=c[X+16>>2];c[Y+20>>2]=c[X+20>>2];c[Y+24>>2]=c[X+24>>2];c[Y+28>>2]=c[X+28>>2];c[Y+32>>2]=c[X+32>>2];c[Y+36>>2]=c[X+36>>2];if(bF){bZ=bH;b_=bI;b$=bG;b0=bE;b1=bD;b2=bC;b3=bB;b4=bA;b5=bz;b6=by;b7=bx;b8=bw;b9=bv;ca=bu;cb=bt;au=6559}else{cc=bH;cd=bI;ce=bG;cf=bE;cg=bD;ch=bC;ci=bB;cj=bA;ck=bz;cl=bx;cm=bw;cn=bv;co=bu;cp=bt;au=6560}}else if((au|0)==6557){uE(g|0,0,40);if(bV){bZ=bX;b_=bY;b$=bW;b0=bU;b1=bT;b2=bS;b3=bR;b4=bQ;b5=bP;b6=bO;b7=bN;b8=bM;b9=bL;ca=bK;cb=bJ;au=6559}else{cc=bX;cd=bY;ce=bW;cf=bU;cg=bT;ch=bS;ci=bR;cj=bQ;ck=bP;cl=bN;cm=bM;cn=bL;co=bK;cp=bJ;au=6560}}if((au|0)==6559){c[b+48>>2]=b6;if(b0){cq=cb;cr=ca;cs=b9;ct=b8;cu=b7;cv=b5;cw=b4;cx=b3;cy=b2;cz=b1;cA=b$;cB=bZ;cC=b_;au=6561}else{cD=cb;cE=ca;cF=b9;cG=b8;cH=b7;cI=b5;cJ=b4;cK=b3;cL=b2;cM=b1;cN=bZ;cO=b_;au=6562}}else if((au|0)==6560){if(cf){cq=cp;cr=co;cs=cn;ct=cm;cu=cl;cv=ck;cw=cj;cx=ci;cy=ch;cz=cg;cA=ce;cB=cc;cC=cd;au=6561}else{cD=cp;cE=co;cF=cn;cG=cm;cH=cl;cI=ck;cJ=cj;cK=ci;cL=ch;cM=cg;cN=cc;cO=cd;au=6562}}if((au|0)==6561){c[b+52>>2]=cA;if(cx){cP=cB;cQ=cC;cR=cz;cS=cy;cT=cw;cU=cv;cV=cu;cW=ct;cX=cs;cY=cr;cZ=cq;au=6563}else{c_=cB;c$=cC;c0=cz;c1=cy;c2=cw;c3=cu;c4=ct;c5=cs;c6=cr;c7=cq;au=6564}}else if((au|0)==6562){if(cK){cP=cN;cQ=cO;cR=cM;cS=cL;cT=cJ;cU=cI;cV=cH;cW=cG;cX=cF;cY=cE;cZ=cD;au=6563}else{c_=cN;c$=cO;c0=cM;c1=cL;c2=cJ;c3=cH;c4=cG;c5=cF;c6=cE;c7=cD;au=6564}}if((au|0)==6563){c[b+56>>2]=cU;if(cR){c8=cZ;c9=cY;da=cX;db=cW;dc=cV;dd=cT;de=cS;df=cP;dg=cQ;au=6565}else{dh=cY;di=cX;dj=cW;dk=cV;dm=cT;dn=cS;dp=cP;dq=cQ;au=6566}}else if((au|0)==6564){if(c0){c8=c7;c9=c6;da=c5;db=c4;dc=c3;dd=c2;de=c1;df=c_;dg=c$;au=6565}else{dh=c6;di=c5;dj=c4;dk=c3;dm=c2;dn=c1;dp=c_;dq=c$;au=6566}}if((au|0)==6565){c[b+64>>2]=c8;if(dd){dr=df;ds=dg;dt=de;du=dc;dv=db;dw=da;dx=c9;au=6567}else{dy=df;dz=dg;dA=de;dB=dc;dC=db;dD=da;dE=c9}}else if((au|0)==6566){if(dm){dr=dp;ds=dq;dt=dn;du=dk;dv=dj;dw=di;dx=dh;au=6567}else{dy=dp;dz=dq;dA=dn;dB=dk;dC=dj;dD=di;dE=dh}}if((au|0)==6567){X=b+72|0;c[X>>2]=c[p>>2];c[X+4>>2]=c[p+4>>2];c[X+8>>2]=c[p+8>>2];c[X+12>>2]=c[p+12>>2];dy=dr;dz=ds;dA=dt;dB=du;dC=dv;dD=dw;dE=dx}if((dE|0)>-1){c[b+88>>2]=dE;c[b+92>>2]=dD;X=b+96|0;c[X>>2]=dz;c[X+4>>2]=dy;h[b+104>>3]=dC;h[b+112>>3]=dB;X=b+120|0;c[X>>2]=c[q>>2];c[X+4>>2]=c[q+4>>2];c[X+8>>2]=c[q+8>>2];c[X+12>>2]=c[q+12>>2];c[X+16>>2]=c[q+16>>2];c[X+20>>2]=c[q+20>>2];if(!dA){break}}else{if(!dA){break}}X=b+144|0;c[X>>2]=c[o>>2];c[X+4>>2]=c[o+4>>2];c[X+8>>2]=c[o+8>>2];c[X+12>>2]=c[o+12>>2];c[X+16>>2]=c[o+16>>2];c[X+20>>2]=c[o+20>>2];c[X+24>>2]=c[o+24>>2];c[X+28>>2]=c[o+28>>2];c[X+32>>2]=c[o+32>>2];c[X+36>>2]=c[o+36>>2]}else{uE(g|0,0,40)}}while(0);if((c[b+72>>2]|0)!=6){i=d;return}h[b+80>>3]=+h[b+40>>3];i=d;return}
+function jx(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0.0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0.0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aJ=0,aK=0,aL=0.0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0.0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0.0,a7=0.0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0.0,bu=0,bv=0,bw=0.0,bx=0,by=0,bz=0;e=i;i=i+96|0;f=e|0;g=e+24|0;j=e+48|0;k=e+72|0;l=c[13898]|0;c[13898]=l+1;do{if((b|0)<-1){c[13898]=l;if((d|0)==1){n=b;o=49264;p=0;q=0;r=49160;s=0;t=0;u=l;break}uf(l,191504,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{w=c[10818]|0;L9036:do{if((w|0)==0){x=0;y=0;z=6591}else{A=(b|0)>0;B=0;C=w;while(1){if(A){D=c[C+4>>2]|0;if((D|0)>=(b|0)){break}}E=c[C>>2]|0;if((E|0)==0){x=C;y=0;z=6591;break L9036}else{B=C;C=E}}if((C|0)==0){F=b;G=B;H=0;z=6594;break}if((D|0)!=(b|0)){x=B;y=C;z=6591;break}A=c[C+12>>2]|0;if((A|0)==(d|0)){I=b;J=0;K=C;break}E=C|0;L=c[E>>2]|0;if((A|0)==4){uu(c[(C+104|0)+4>>2]|0)}if((d|0)==4){uD(C|0,33240,272)}else if((d|0)==1){uD(C|0,1144,272);c[C+52>>2]=-7;c[C+16>>2]=3}else if((d|0)==3){uD(C|0,1416,272)}else if((d|0)==2){uD(C|0,1688,272)}else{uf(-1,177064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[C+4>>2]=b;c[C+12>>2]=d;c[E>>2]=L;I=b;J=C;K=C}}while(0);do{if((z|0)==6591){if((b|0)!=-1){F=b;G=x;H=y;z=6594;break}if((x|0)==0){F=1;G=0;H=y;z=6594;break}F=(c[x+4>>2]|0)+1|0;G=x;H=y;z=6594}}while(0);if((z|0)==6594){w=ut(272)|0;do{if((w|0)==0){gk();L=ut(272)|0;if((L|0)!=0){M=L;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=177576,v)|0)}else{M=w}}while(0);w=M;if((d|0)==1){uD(M|0,1144,272);c[M+52>>2]=-7;c[M+16>>2]=3}else if((d|0)==3){uD(M|0,1416,272)}else if((d|0)==2){uD(M|0,1688,272)}else if((d|0)==4){uD(M|0,33240,272)}else{uf(-1,177064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[M+4>>2]=F;c[M+12>>2]=d;if((G|0)==0){c[10818]=w}else{c[G>>2]=w}c[M>>2]=H;I=F;J=w;K=w}w=K+104|0;n=I;o=w|0;p=w;q=J;r=K;s=w;t=w;u=c[13898]|0}}while(0);K=c[8272]|0;if((u|0)>=(K|0)){i=e;return}J=o+88|0;I=o+128|0;F=J|0;H=I|0;M=o+92|0;G=o+132|0;y=o+104|0;x=o+144|0;b=o+112|0;D=o+152|0;l=o|0;w=o+48|0;L=o+8|0;o=r+8|0;E=k|0;A=k+8|0;N=r+80|0;O=r+52|0;Q=r+88|0;R=Q|0;S=r+12|0;T=(q|0)==0;q=r+16|0;U=q|0;V=r+20|0;W=r+24|0;X=r+32|0;Y=(n|0)<0;n=r+16|0;Z=j|0;_=j+8|0;$=t+88|0;aa=g|0;ab=g+8|0;ac=t+96|0;ad=t+48|0;ae=t+8|0;t=p+48|0;af=p|0;ag=f|0;ah=f+8|0;ai=p+88|0;aj=p+8|0;p=s+4|0;ak=s|0;s=0;al=0;am=0;an=0;ao=0;ap=0;aq=1.0;ar=u;u=K;L9085:while(1){K=c[1054]|0;as=(a[K+(ar*40&-1)|0]&1)==0;L9087:do{if(!as){at=c[K+(ar*40&-1)+36>>2]|0;au=K+(ar*40&-1)+32|0;av=c[10036]|0;aw=0;while(1){if((aw|0)>=(at|0)){break}if((a[av+((c[au>>2]|0)+aw|0)|0]|0)==(a[aw+103664|0]|0)){aw=aw+1|0}else{break L9087}}if((aw|0)==1){ax=am;ay=an;az=ao;aA=aq;z=6939;break L9085}}}while(0);L9094:do{if((d|0)==3){au=(u|0)>(ar|0);L9096:do{if(au){av=c[K+(ar*40&-1)+36>>2]|0;at=K+(ar*40&-1)+32|0;if(as){aB=c[at>>2]|0;aC=av;z=6764;break}C=c[10036]|0;B=0;while(1){if((B|0)>=(av|0)){break}if((a[C+((c[at>>2]|0)+B|0)|0]|0)==(a[B+95280|0]|0)){B=B+1|0}else{z=6754;break L9096}}if((B|0)==2){z=6762}else{z=6754}}else{z=6754}}while(0);L9105:do{if((z|0)==6754){z=0;aw=c[K+(ar*40&-1)+36>>2]|0;at=c[K+(ar*40&-1)+32>>2]|0;L9107:do{if((aw|0)>0&(as^1)){C=c[10036]|0;av=0;aD=0;aE=at;while(1){if((a[av+185064|0]|0)==(a[C+(av+aE|0)|0]|0)){aF=aE;aG=aD}else{if((av|0)!=3){break L9107}aF=aE-1|0;aG=1}aH=av+1|0;if((aH|0)<(aG+aw|0)){av=aH;aD=aG;aE=aF}else{break}}if((aG|0)!=0){z=6762;break L9105}if((av|0)==2|(av|0)==6){z=6762;break L9105}}}while(0);if(au){aB=at;aC=aw;z=6764}else{aJ=aw;aK=at}}}while(0);if((z|0)==6762){z=0;c[13898]=ar+1;dl(aj,0);aL=aq;aM=ap;aN=ao;aO=an;aP=am;aQ=al;aR=s;break}L9120:do{if((z|0)==6764){z=0;if(as){aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}au=c[10036]|0;B=0;while(1){if((B|0)>=(aC|0)){break}if((a[au+(aB+B|0)|0]|0)==(a[B+130496|0]|0)){B=B+1|0}else{aJ=aC;aK=aB;break L9120}}if((B|0)!=4){aJ=aC;aK=aB;break}c[13898]=ar+1;dl(t,0);aL=aq;aM=ap;aN=ao;aO=an;aP=am;aQ=al;aR=s;break L9094}}while(0);if(!((aJ|0)>0&(as^1))){aS=ap;aT=an;aU=ar;aV=u;z=6859;break}au=c[10036]|0;at=0;aw=0;aE=aK;while(1){if((a[at+212296|0]|0)==(a[au+(at+aE|0)|0]|0)){aW=aE;aX=aw}else{if((at|0)!=3){break}aW=aE-1|0;aX=1}aD=at+1|0;if((aD|0)<(aX+aJ|0)){at=aD;aw=aX;aE=aW}else{z=6776;break}}do{if((z|0)==6776){z=0;if((aX|0)==0){if(!((at|0)==5|(at|0)==2)){break}}c[13898]=ar+1;aE=is(f)|0;aw=c[aE>>2]|0;if((aw|0)==1){aY=+(c[aE+8>>2]|0)}else if((aw|0)==2){aY=+h[aE+8>>3]}else if((aw|0)==3){aY=+uz(c[aE+8>>2]|0,0)}else{z=6782;break L9085}if((c[ag>>2]|0)==3){uu(c[ah>>2]|0);c[ag>>2]=1}h[ai>>3]=aY;aL=aq;aM=ap;aN=ao;aO=an;aP=am;aQ=al;aR=s;break L9094}}while(0);if(!((aJ|0)>0&(as^1))){aS=ap;aT=an;aU=ar;aV=u;z=6859;break}at=c[10036]|0;aE=0;aw=0;au=aK;while(1){if((a[aE+211736|0]|0)==(a[at+(aE+au|0)|0]|0)){aZ=au;a_=aw}else{if((aE|0)!=4){aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}aZ=au-1|0;a_=1}aD=aE+1|0;if((aD|0)<(a_+aJ|0)){aE=aD;aw=a_;au=aZ}else{break}}if((a_|0)==0){if(!((aE|0)==3|(aE|0)==5)){aS=ap;aT=an;aU=ar;aV=u;z=6859;break}}a$=ar+1|0;c[13898]=a$;au=(u|0)>(a$|0);L9161:do{if(au){do{if((a[K+(a$*40&-1)|0]&1)!=0){aw=c[K+(a$*40&-1)+36>>2]|0;at=K+(a$*40&-1)+32|0;aD=c[10036]|0;C=0;while(1){if((C|0)>=(aw|0)){z=6799;break}if((a[aD+((c[at>>2]|0)+C|0)|0]|0)==(a[C+150688|0]|0)){C=C+1|0}else{z=6800;break}}if((z|0)==6799){z=0;if((C|0)==2|au^1){z=6806;break L9161}else{break}}else if((z|0)==6800){z=0;if(au){break}else{z=6806;break L9161}}}}while(0);B=(a[K+(a$*40&-1)|0]&1)==0;if(B){z=6963;break L9085}at=c[K+(a$*40&-1)+36>>2]|0;aD=K+(a$*40&-1)+32|0;aw=c[10036]|0;av=0;while(1){if((av|0)>=(at|0)){z=6805;break}if((a[aw+((c[aD>>2]|0)+av|0)|0]|0)==(a[av+103664|0]|0)){av=av+1|0}else{break}}if((z|0)==6805){z=0;if((av|0)==1){z=6806;break}}if(B){z=6962;break L9085}aD=c[K+(a$*40&-1)+36>>2]|0;aw=K+(a$*40&-1)+32|0;at=c[10036]|0;aH=0;while(1){if((aH|0)>=(aD|0)){z=6811;break}if((a[at+((c[aw>>2]|0)+aH|0)|0]|0)==(a[aH+211336|0]|0)){aH=aH+1|0}else{break}}do{if((z|0)==6811){z=0;if((aH|0)!=2){break}c[af>>2]=1;break L9161}}while(0);if(B){z=6964;break L9085}aH=c[K+(a$*40&-1)+36>>2]|0;aw=K+(a$*40&-1)+32|0;at=c[10036]|0;aD=0;while(1){if((aD|0)>=(aH|0)){break}if((a[at+((c[aw>>2]|0)+aD|0)|0]|0)==(a[aD+210848|0]|0)){aD=aD+1|0}else{z=6965;break L9085}}if((aD|0)!=2){z=6966;break L9085}c[af>>2]=2}else{z=6806}}while(0);if((z|0)==6806){z=0;c[af>>2]=0}c[13898]=(c[13898]|0)+1;aL=aq;aM=ap;aN=ao;aO=an;aP=am;aQ=al;aR=s}else if((d|0)==2){au=(u|0)>(ar|0);L9197:do{if(au){aE=c[K+(ar*40&-1)+36>>2]|0;aw=K+(ar*40&-1)+32|0;if(as){a0=c[aw>>2]|0;a1=aE;break}at=c[10036]|0;aH=0;while(1){if((aH|0)>=(aE|0)){break}if((a[at+((c[aw>>2]|0)+aH|0)|0]|0)==(a[aH+95280|0]|0)){aH=aH+1|0}else{z=6682;break L9197}}if((aH|0)==2){z=6690}else{z=6682}}else{z=6682}}while(0);L9206:do{if((z|0)==6682){z=0;aw=c[K+(ar*40&-1)+36>>2]|0;at=c[K+(ar*40&-1)+32>>2]|0;L9208:do{if((aw|0)>0&(as^1)){aE=c[10036]|0;aD=0;B=0;av=at;while(1){if((a[aD+185064|0]|0)==(a[aE+(aD+av|0)|0]|0)){a2=av;a3=B}else{if((aD|0)!=3){break L9208}a2=av-1|0;a3=1}a4=aD+1|0;if((a4|0)<(a3+aw|0)){aD=a4;B=a3;av=a2}else{break}}if((a3|0)!=0){z=6690;break L9206}if((aD|0)==2|(aD|0)==6){z=6690;break L9206}}}while(0);if(au){a0=at;a1=aw}else{aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}}}while(0);if((z|0)==6690){z=0;c[13898]=ar+1;dl(ae,0);aL=aq;aM=ap;aN=ao;aO=an;aP=am;aQ=al;aR=s;break}L9222:do{if(as){z=6697}else{aH=c[10036]|0;av=0;while(1){if((av|0)>=(a1|0)){break}if((a[aH+(a0+av|0)|0]|0)==(a[av+130496|0]|0)){av=av+1|0}else{z=6697;break L9222}}if((av|0)!=4){z=6697}}}while(0);do{if((z|0)==6697){z=0;if(!au){aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}if(as){aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}aH=c[10036]|0;aw=0;while(1){if((aw|0)>=(a1|0)){z=6702;break}if((a[aH+(a0+aw|0)|0]|0)==(a[aw+184696|0]|0)){aw=aw+1|0}else{break}}if((z|0)==6702){z=0;if((aw|0)==6){break}}if(!au){aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}if(as){aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}aH=c[10036]|0;av=0;while(1){if((av|0)>=(a1|0)){break}if((a[aH+(a0+av|0)|0]|0)==(a[av+184328|0]|0)){av=av+1|0}else{aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}}if((av|0)!=3){aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}aw=ar+1|0;c[13898]=aw;if((u|0)<=(aw|0)){a5=aw;z=6948;break L9085}if((a[K+(aw*40&-1)|0]&1)==0){a5=aw;z=6946;break L9085}at=c[K+(aw*40&-1)+36>>2]|0;B=K+(aw*40&-1)+32|0;aE=0;while(1){if((aE|0)>=(at|0)){break}if((a[aH+((c[B>>2]|0)+aE|0)|0]|0)==(a[aE+78280|0]|0)){aE=aE+1|0}else{a5=aw;z=6952;break L9085}}if((aE|0)!=1){a5=aw;z=6953;break L9085}c[13898]=ar+2;B=is(j)|0;aH=c[B>>2]|0;if((aH|0)==1){a6=+(c[B+8>>2]|0)}else if((aH|0)==2){a6=+h[B+8>>3]}else if((aH|0)==3){a6=+uz(c[B+8>>2]|0,0)}else{z=6720;break L9085}if((c[Z>>2]|0)==3){uu(c[_>>2]|0);c[Z>>2]=1}if(+P(+a6)>1.0e3){z=6724;break L9085}h[$>>3]=a6;B=c[13898]|0;aH=B+1|0;c[13898]=aH;if((c[8272]|0)<=(B|0)){a5=aH;z=6956;break L9085}at=c[1054]|0;if((a[at+(B*40&-1)|0]&1)==0){a5=aH;z=6957;break L9085}av=c[at+(B*40&-1)+36>>2]|0;C=at+(B*40&-1)+32|0;B=c[10036]|0;at=0;while(1){if((at|0)>=(av|0)){break}if((a[B+((c[C>>2]|0)+at|0)|0]|0)==(a[at+183584|0]|0)){at=at+1|0}else{a5=aH;z=6954;break L9085}}if((at|0)!=1){a5=aH;z=6955;break L9085}C=is(g)|0;B=c[C>>2]|0;if((B|0)==1){a7=+(c[C+8>>2]|0)}else if((B|0)==2){a7=+h[C+8>>3]}else if((B|0)==3){a7=+uz(c[C+8>>2]|0,0)}else{z=6735;break L9085}if((c[aa>>2]|0)==3){uu(c[ab>>2]|0);c[aa>>2]=1}if(+P(+a7)>1.0e3){z=6739;break L9085}h[ac>>3]=a7;C=c[13898]|0;B=C+1|0;c[13898]=B;if((c[8272]|0)<=(C|0)){a5=B;z=6947;break L9085}av=c[1054]|0;if((a[av+(C*40&-1)|0]&1)==0){a5=B;z=6951;break L9085}aw=c[av+(C*40&-1)+36>>2]|0;aE=av+(C*40&-1)+32|0;C=c[10036]|0;av=0;while(1){if((av|0)>=(aw|0)){break}if((a[C+((c[aE>>2]|0)+av|0)|0]|0)==(a[av+78864|0]|0)){av=av+1|0}else{a5=B;z=6949;break L9085}}if((av|0)==1){aL=aq;aM=ap;aN=ao;aO=an;aP=am;aQ=al;aR=s;break L9094}else{a5=B;z=6950;break L9085}}}while(0);c[13898]=ar+1;dl(ad,0);aL=aq;aM=ap;aN=ao;aO=an;aP=am;aQ=al;aR=s}else if((d|0)==1){au=(u|0)>(ar|0);L9285:do{if(au){do{if(!as){aE=c[K+(ar*40&-1)+36>>2]|0;C=K+(ar*40&-1)+32|0;aw=c[10036]|0;aH=0;while(1){if((aH|0)>=(aE|0)){z=6631;break}if((a[aw+((c[C>>2]|0)+aH|0)|0]|0)==(a[aH+188576|0]|0)){aH=aH+1|0}else{break}}do{if((z|0)==6631){z=0;if((aH|0)!=4){break}c[13898]=ar+1;dl(J,0);a8=c[13898]|0;C=(c[8272]|0)>(a8|0);if(!C){z=6967;break L9085}aw=c[1054]|0;L9296:do{if((a[aw+(a8*40&-1)|0]&1)==0){z=6640}else{aE=c[aw+(a8*40&-1)+36>>2]|0;aD=aw+(a8*40&-1)+32|0;at=c[10036]|0;a4=0;while(1){if((a4|0)>=(aE|0)){z=6637;break}if((a[at+((c[aD>>2]|0)+a4|0)|0]|0)==(a[a4+187176|0]|0)){a4=a4+1|0}else{break}}do{if((z|0)==6637){z=0;if((a4|0)!=2){break}c[13898]=a8+1;dl(I,0);break L9296}}while(0);if(C){z=6640}else{z=6970;break L9085}}}while(0);if((z|0)==6640){z=0;C=c[1054]|0;if((a[C+(a8*40&-1)|0]&1)==0){z=6971;break L9085}aw=c[C+(a8*40&-1)+36>>2]|0;a4=C+(a8*40&-1)+32|0;C=c[10036]|0;aD=0;while(1){if((aD|0)>=(aw|0)){break}if((a[C+((c[a4>>2]|0)+aD|0)|0]|0)==(a[aD+186688|0]|0)){aD=aD+1|0}else{z=6968;break L9085}}if((aD|0)!=3){z=6969;break L9085}c[13898]=a8+1;dl(I,c[F>>2]|0);if((c[F>>2]|0)!=(c[H>>2]|0)){z=6958;break L9085}if((c[M>>2]|0)!=(c[G>>2]|0)){z=6959;break L9085}h[x>>3]=+h[y>>3]+ +h[x>>3];h[D>>3]=+h[b>>3]+ +h[D>>3]}c[l>>2]=0;aL=aq;aM=ap;aN=ao;aO=1;aP=am;aQ=al;aR=s;break L9094}}while(0);if(!au){z=6659;break L9285}if(as){break}aH=c[K+(ar*40&-1)+36>>2]|0;a4=K+(ar*40&-1)+32|0;C=c[10036]|0;aw=0;while(1){if((aw|0)>=(aH|0)){break}if((a[C+((c[a4>>2]|0)+aw|0)|0]|0)==(a[aw+95280|0]|0)){aw=aw+1|0}else{z=6659;break L9285}}if((aw|0)==2){z=6667;break L9285}else{z=6659;break L9285}}}while(0);a9=c[K+(ar*40&-1)+36>>2]|0;ba=c[K+(ar*40&-1)+32>>2]|0}else{z=6659}}while(0);L9324:do{if((z|0)==6659){z=0;B=c[K+(ar*40&-1)+36>>2]|0;av=c[K+(ar*40&-1)+32>>2]|0;if(!((B|0)>0&(as^1))){a9=B;ba=av;break}a4=c[10036]|0;C=0;aH=0;at=av;while(1){if((a[C+185064|0]|0)==(a[a4+(C+at|0)|0]|0)){bb=at;bc=aH}else{if((C|0)!=3){a9=B;ba=av;break L9324}bb=at-1|0;bc=1}aE=C+1|0;if((aE|0)<(bc+B|0)){C=aE;aH=bc;at=bb}else{break}}if((bc|0)!=0){z=6667;break}if((C|0)==2|(C|0)==6){z=6667}else{a9=B;ba=av}}}while(0);if((z|0)==6667){z=0;c[13898]=ar+1;dl(L,0);c[l>>2]=1;aL=aq;aM=ap;aN=1;aO=an;aP=am;aQ=al;aR=s;break}if(!au){aS=ap;aT=an;aU=ar;aV=u;z=6859;break}if(as){aS=ap;aT=an;aU=ar;aV=u;z=6859;break}at=c[10036]|0;aH=0;while(1){if((aH|0)>=(a9|0)){break}if((a[at+(ba+aH|0)|0]|0)==(a[aH+130496|0]|0)){aH=aH+1|0}else{aS=ap;aT=an;aU=ar;aV=u;z=6859;break L9094}}if((aH|0)!=4){aS=ap;aT=an;aU=ar;aV=u;z=6859;break}c[13898]=ar+1;dl(w,0);c[l>>2]=1;aL=aq;aM=ap;aN=1;aO=an;aP=am;aQ=al;aR=s}else if((d|0)==4){L9346:do{if((u|0)<=(ar|0)|as){bd=ap}else{at=c[K+(ar*40&-1)+36>>2]|0;au=K+(ar*40&-1)+32|0;a4=c[10036]|0;aE=0;while(1){if((aE|0)>=(at|0)){break}if((a[a4+((c[au>>2]|0)+aE|0)|0]|0)==(a[aE+188576|0]|0)){aE=aE+1|0}else{bd=ap;break L9346}}if((aE|0)!=4){bd=ap;break}c[13898]=ar+1;au=db(c[p>>2]|0,40,182592)|0;c[p>>2]=au;dl(au,0);c[ak>>2]=1;bd=1}}while(0);aH=an;L9354:while(1){be=c[13898]|0;bf=c[8272]|0;au=(bf|0)>(be|0);if(!au){break}a4=c[1054]|0;do{if((a[a4+(be*40&-1)|0]&1)==0){z=6835}else{at=c[a4+(be*40&-1)+36>>2]|0;av=a4+(be*40&-1)+32|0;B=c[10036]|0;C=0;while(1){if((C|0)>=(at|0)){z=6833;break}if((a[B+((c[av>>2]|0)+C|0)|0]|0)==(a[C+187176|0]|0)){C=C+1|0}else{break}}if((z|0)==6833){z=0;if((C|0)==2){break}}if(au){z=6835}else{break L9354}}}while(0);if((z|0)==6835){z=0;au=c[1054]|0;if((a[au+(be*40&-1)|0]&1)==0){break}a4=c[au+(be*40&-1)+36>>2]|0;aE=au+(be*40&-1)+32|0;au=c[10036]|0;av=0;while(1){if((av|0)>=(a4|0)){break}if((a[au+((c[aE>>2]|0)+av|0)|0]|0)==(a[av+186688|0]|0)){av=av+1|0}else{break L9354}}if((av|0)!=3){break}}bg=c[p>>2]|0;if(!bd){z=6857;break L9085}c[p>>2]=db(bg,((c[ak>>2]|0)*40&-1)+40|0,182592)|0;aE=c[13898]|0;c[13898]=aE+1;L9374:do{if((c[8272]|0)>(aE|0)){au=c[1054]|0;if((a[au+(aE*40&-1)|0]&1)==0){z=6846;break}a4=c[au+(aE*40&-1)+36>>2]|0;B=au+(aE*40&-1)+32|0;au=c[10036]|0;at=0;while(1){if((at|0)>=(a4|0)){break}if((a[au+((c[B>>2]|0)+at|0)|0]|0)==(a[at+187176|0]|0)){at=at+1|0}else{z=6846;break L9374}}B=c[ak>>2]|0;au=c[p>>2]|0;a4=au+(B*40&-1)|0;if((at|0)!=2){bh=B;bi=au;bj=a4;z=6849;break}dl(a4,0)}else{z=6846}}while(0);if((z|0)==6846){z=0;aE=c[ak>>2]|0;av=c[p>>2]|0;bh=aE;bi=av;bj=av+(aE*40&-1)|0;z=6849}if((z|0)==6849){z=0;dl(bj,c[bi>>2]|0);aE=c[p>>2]|0;av=bh-1|0;if((c[aE+(bh*40&-1)>>2]|0)!=(c[aE+(av*40&-1)>>2]|0)){z=6972;break L9085}if((c[aE+(bh*40&-1)+4>>2]|0)!=(c[aE+(av*40&-1)+4>>2]|0)){z=6973;break L9085}a4=aE+(bh*40&-1)+16|0;h[a4>>3]=+h[aE+(av*40&-1)+16>>3]+ +h[a4>>3];a4=c[p>>2]|0;aE=a4+(bh*40&-1)+24|0;h[aE>>3]=+h[a4+(av*40&-1)+24>>3]+ +h[aE>>3]}c[ak>>2]=(c[ak>>2]|0)+1;aH=1}if(!aH){aS=bd;aT=0;aU=be;aV=bf;z=6859;break}aE=c[p>>2]|0;if((uG(aE+(((c[ak>>2]|0)-1|0)*40&-1)|0,aE|0,40)|0)==0){aS=bd;aT=1;aU=be;aV=bf;z=6859;break}aI(181096,44,1,c[m>>2]|0);aE=db(c[p>>2]|0,((c[ak>>2]|0)*40&-1)+40|0,182592)|0;av=aE;c[p>>2]=av;a4=av+((c[ak>>2]|0)*40&-1)|0;c[a4>>2]=c[aE>>2];c[a4+4>>2]=c[aE+4>>2];c[a4+8>>2]=c[aE+8>>2];c[a4+12>>2]=c[aE+12>>2];c[a4+16>>2]=c[aE+16>>2];c[a4+20>>2]=c[aE+20>>2];c[a4+24>>2]=c[aE+24>>2];c[a4+28>>2]=c[aE+28>>2];c[a4+32>>2]=c[aE+32>>2];c[a4+36>>2]=c[aE+36>>2];c[ak>>2]=(c[ak>>2]|0)+1;aS=bd;aT=1;aU=c[13898]|0;aV=c[8272]|0;z=6859}else{z=6858;break L9085}}while(0);L9392:do{if((z|0)==6859){z=0;K=(aV|0)>(aU|0);L9394:do{if(K){as=c[1054]|0;if((a[as+(aU*40&-1)|0]&1)!=0){aE=c[as+(aU*40&-1)+36>>2]|0;a4=as+(aU*40&-1)+32|0;as=c[10036]|0;av=0;while(1){if((av|0)>=(aE|0)){z=6864;break}if((a[as+((c[a4>>2]|0)+av|0)|0]|0)==(a[av+150856|0]|0)){av=av+1|0}else{break}}do{if((z|0)==6864){z=0;if((av|0)!=5){break}c[o>>2]=1;c[13898]=(c[13898]|0)+1;aL=aq;aM=aS;aN=ao;aO=aT;aP=am;aQ=al;aR=s;break L9392}}while(0);if(!K){z=6874;break}}av=c[1054]|0;L9406:do{if((a[av+(aU*40&-1)|0]&1)!=0){a4=c[av+(aU*40&-1)+36>>2]|0;as=av+(aU*40&-1)+32|0;aE=c[10036]|0;au=0;while(1){if((au|0)>=(a4|0)){break}if((a[aE+((c[as>>2]|0)+au|0)|0]|0)==(a[au+150704|0]|0)){au=au+1|0}else{break L9406}}if((au|0)!=4){break}c[o>>2]=0;c[13898]=(c[13898]|0)+1;aL=aq;aM=aS;aN=ao;aO=aT;aP=am;aQ=al;aR=s;break L9392}}while(0);if(!K){z=6874;break}av=c[1054]|0;as=a[av+(aU*40&-1)|0]|0;if((as&1)==0){bk=av;bl=as;break}aE=c[av+(aU*40&-1)+36>>2]|0;a4=av+(aU*40&-1)+32|0;at=c[10036]|0;B=0;while(1){if((B|0)>=(aE|0)){break}if((a[at+((c[a4>>2]|0)+B|0)|0]|0)==(a[B+121920|0]|0)){B=B+1|0}else{bk=av;bl=as;break L9394}}if((B|0)!=6){bk=av;bl=as;break}c[o>>2]=-1;c[13898]=(c[13898]|0)+1;aL=aq;aM=aS;aN=ao;aO=aT;aP=am;aQ=al;aR=s;break L9392}else{z=6874}}while(0);if((z|0)==6874){z=0;K=c[1054]|0;bk=K;bl=a[K+(aU*40&-1)|0]|0}K=c[bk+(aU*40&-1)+36>>2]|0;L9424:do{if((bl&1)!=0&(K|0)>0){aH=c[10036]|0;a4=0;at=0;aE=c[bk+(aU*40&-1)+32>>2]|0;while(1){if((a[a4+184456|0]|0)==(a[aH+(a4+aE|0)|0]|0)){bm=aE;bn=at}else{if((a4|0)!=3){break L9424}bm=aE-1|0;bn=1}C=a4+1|0;if((C|0)<(bn+K|0)){a4=C;at=bn;aE=bm}else{break}}if((bn|0)==0){if(!((a4|0)==2|(a4|0)==7)){break}}if(Y){z=6890;break L9085}c[O>>2]=-7;c[n>>2]=3;c[13898]=(c[13898]|0)+1;aL=aq;aM=aS;aN=ao;aO=aT;aP=am;aQ=1;aR=1;break L9392}}while(0);if(s){bo=aU}else{K=(c[S>>2]|0)==1?49176:49488;if(T){hJ(q,c[U>>2]|0,c[V>>2]|0,c[W>>2]|0,X)}else{hJ(q,c[K>>2]|0,c[K+4>>2]|0,c[K+8>>2]|0,K+16|0)}if((c[13898]|0)==(ar|0)){bo=ar}else{aL=aq;aM=aS;aN=ao;aO=aT;aP=am;aQ=al;aR=1;break}}if(al){bp=bo}else{K=c[1054]|0;aE=a[K+(bo*40&-1)|0]|0;L9447:do{if((c[8272]|0)>(bo|0)){if((aE&1)==0){z=6904;break}at=c[K+(bo*40&-1)+36>>2]|0;aH=K+(bo*40&-1)+32|0;as=c[10036]|0;av=0;while(1){if((av|0)>=(at|0)){break}if((a[as+((c[aH>>2]|0)+av|0)|0]|0)==(a[av+179168|0]|0)){av=av+1|0}else{z=6904;break L9447}}if((av|0)==2){z=6912}else{z=6904}}else{z=6904}}while(0);L9454:do{if((z|0)==6904){z=0;aH=c[K+(bo*40&-1)+36>>2]|0;if(!((aE&1)!=0&(aH|0)>0)){break}as=c[10036]|0;at=0;a4=0;B=c[K+(bo*40&-1)+32>>2]|0;while(1){if((a[at+178592|0]|0)==(a[as+(at+B|0)|0]|0)){bq=B;br=a4}else{if((at|0)!=5){break L9454}bq=B-1|0;br=1}C=at+1|0;if((C|0)<(br+aH|0)){at=C;a4=br;B=bq}else{break}}if((br|0)!=0){z=6912;break}if((at|0)==4|(at|0)==9){z=6912}}}while(0);do{if((z|0)==6912){z=0;a[N]=1;c[O>>2]=-2;hI(Q,5);if((c[R>>2]|0)!=0){break}c[O>>2]=-7}}while(0);if((c[13898]|0)==(ar|0)){bp=ar}else{aL=aq;aM=aS;aN=ao;aO=aT;aP=am;aQ=1;aR=s;break}}if(am){bs=bp;z=6960;break L9085}K=c[1054]|0;aE=a[K+(bp*40&-1)|0]|0;L9471:do{if((c[8272]|0)>(bp|0)){if((aE&1)==0){z=6922;break}B=c[K+(bp*40&-1)+36>>2]|0;a4=K+(bp*40&-1)+32|0;aH=c[10036]|0;as=0;while(1){if((as|0)>=(B|0)){break}if((a[aH+((c[a4>>2]|0)+as|0)|0]|0)==(a[as+128296|0]|0)){as=as+1|0}else{z=6922;break L9471}}if((as|0)==2){z=6930}else{z=6922}}else{z=6922}}while(0);L9478:do{if((z|0)==6922){z=0;a4=c[K+(bp*40&-1)+36>>2]|0;if(!((aE&1)!=0&(a4|0)>0)){bt=aq;break}aH=c[10036]|0;B=0;at=0;av=c[K+(bp*40&-1)+32>>2]|0;while(1){if((a[B+128400|0]|0)==(a[aH+(B+av|0)|0]|0)){bu=av;bv=at}else{if((B|0)!=5){bt=aq;break L9478}bu=av-1|0;bv=1}C=B+1|0;if((C|0)<(bv+a4|0)){B=C;at=bv;av=bu}else{break}}if((bv|0)!=0){z=6930;break}if((B|0)==4|(B|0)==9){z=6930}else{bt=aq}}}while(0);do{if((z|0)==6930){z=0;c[13898]=bp+1;K=is(k)|0;aE=c[K>>2]|0;if((aE|0)==2){bw=+h[K+8>>3]}else if((aE|0)==3){bw=+uz(c[K+8>>2]|0,0)}else if((aE|0)==1){bw=+(c[K+8>>2]|0)}else{z=6934;break L9085}if((c[E>>2]|0)!=3){bt=bw;break}uu(c[A>>2]|0);c[E>>2]=1;bt=bw}}while(0);if((c[13898]|0)==(ar|0)){bs=ar;z=6961;break L9085}else{aL=bt;aM=aS;aN=ao;aO=aT;aP=1;aQ=al;aR=s}}}while(0);K=c[13898]|0;aE=c[8272]|0;if((K|0)<(aE|0)){s=aR;al=aQ;am=aP;an=aO;ao=aN;ap=aM;aq=aL;ar=K;u=aE}else{ax=aP;ay=aO;az=aN;aA=aL;z=6939;break}}if((z|0)==6739){uf((c[13898]|0)-1|0,183928,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6962){uf(a$,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6963){uf(a$,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6964){uf(a$,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6965){uf(a$,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6966){uf(a$,210136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6967){uf(a8,185568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6968){uf(a8,185568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6969){uf(a8,185568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6970){uf(a8,185568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6971){uf(a8,185568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6972){bx=c[13898]|0;uf(bx,186064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6973){bx=c[13898]|0;uf(bx,186064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6857){uu(bg);c[p>>2]=0;c[ak>>2]=0;uf(c[13898]|0,180752,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6858){uf(ar,180328,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6952){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6953){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6954){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6955){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6956){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6957){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6958){bz=c[13898]|0;uf(bz,186064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6959){bz=c[13898]|0;uf(bz,186064,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6960){uf(bs,178144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6961){uf(bs,178144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6934){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6939){if(ax){h[r+64>>3]=aA}if(az&ay){uf(-1,177888,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{i=e;return}}else if((z|0)==6720){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6946){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6947){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6948){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6949){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6950){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6951){by=a5-1|0;c[13898]=by;uf(by,183096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6782){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6724){uf((c[13898]|0)-1|0,183928,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6735){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==6890){uf(aU,179496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function jy(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0.0,L=0,M=0.0,N=0,O=0,P=0,Q=0,R=0,S=0.0,T=0,U=0,V=0.0,W=0,X=0,Y=0,Z=0,_=0,$=0.0,aa=0,ab=0.0,ac=0.0,ad=0.0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0.0;b=i;i=i+120|0;d=b|0;e=b+24|0;f=b+48|0;j=b+72|0;k=b+96|0;l=c[13898]|0;o=l+1|0;c[13898]=o;p=c[1054]|0;q=c[p+(o*40&-1)+36>>2]|0;r=c[p+(o*40&-1)+32>>2]|0;s=(a[p+(o*40&-1)|0]&1)==0;t=(q|0)>0;u=c[10036]|0;w=21960;x=96232;L9545:while(1){L9547:do{if(!s){if(t){y=0;z=0;A=r;while(1){B=a[x+y|0]|0;if(B<<24>>24==(a[u+(y+A|0)|0]|0)){C=A;D=z}else{if(B<<24>>24!=36){break L9547}C=A-1|0;D=1}E=y+1|0;if((E|0)<(D+q|0)){y=E;z=D;A=C}else{break}}if((D|0)==0){F=E}else{G=w;break L9545}}else{F=0}A=a[x+F|0]|0;if((A<<24>>24|0)==36|(A<<24>>24|0)==0){G=w;break L9545}}}while(0);A=w+8|0;z=c[A>>2]|0;if((z|0)==0){G=A;break}else{w=A;x=z}}x=c[G+4>>2]|0;if((x|0)==0){H=75960;I=c[13898]|0;uf(I,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}G=l+2|0;c[13898]=G;do{if((x|0)==95){if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}if((c[17578]|0)==1){w=c[m>>2]|0;cf(w|0,198344,(v=i,i=i+8|0,c[v>>2]=57024,v)|0)}w=c[m>>2]|0;aF(9,w|0);je(c[m>>2]|0,8)}else if((x|0)==30){L9572:do{if((G|0)<(c[8272]|0)){L9574:do{if((a[p+(G*40&-1)|0]&1)!=0){w=c[p+(G*40&-1)+36>>2]|0;F=p+(G*40&-1)+32|0;E=0;while(1){if((E|0)>=(w|0)){break}if((a[u+((c[F>>2]|0)+E|0)|0]|0)==(a[E+103664|0]|0)){E=E+1|0}else{break L9574}}if((E|0)==1){J=0;break L9572}}}while(0);F=is(k)|0;w=c[F>>2]|0;if((w|0)==2){K=+h[F+8>>3]}else if((w|0)==3){K=+uz(c[F+8>>2]|0,0)}else if((w|0)==1){K=+(c[F+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}F=k|0;if((c[F>>2]|0)==3){uu(c[k+8>>2]|0);c[F>>2]=1}F=~~K;if((F|0)<1){H=208936}else{J=F;break}I=c[13898]|0;uf(I,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{J=0}}while(0);aF(10,c[m>>2]|0);jM(J)}else if((x|0)==65){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}if((c[16718]|0)==1){F=c[m>>2]|0;cf(F|0,198344,(v=i,i=i+8|0,c[v>>2]=56904,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,3)}else if((x|0)==18){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}cf(c[m>>2]|0,114368,(v=i,i=i+16|0,c[v>>2]=24544,c[v+8>>2]=24595,v)|0)}else if((x|0)==32){L9604:do{if((G|0)<(c[8272]|0)){L9606:do{if((a[p+(G*40&-1)|0]&1)!=0){F=c[p+(G*40&-1)+36>>2]|0;w=p+(G*40&-1)+32|0;D=0;while(1){if((D|0)>=(F|0)){break}if((a[u+((c[w>>2]|0)+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{break L9606}}if((D|0)==1){L=7233;break L9604}}}while(0);w=is(e)|0;F=c[w>>2]|0;if((F|0)==2){M=+h[w+8>>3]}else if((F|0)==3){M=+uz(c[w+8>>2]|0,0)}else if((F|0)==1){M=+(c[w+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}w=e|0;if((c[w>>2]|0)==3){uu(c[e+8>>2]|0);c[w>>2]=1}w=~~M;if((w|0)<1){H=208936;I=c[13898]|0;uf(I,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{F=c[m>>2]|0;aF(10,F|0);N=w;O=0;break}}else{L=7233}}while(0);if((L|0)==7233){w=c[m>>2]|0;aF(10,w|0);w=c[m>>2]|0;F=c[8798]|0;cf(w|0,76832,(v=i,i=i+8|0,c[v>>2]=F,v)|0);N=0;O=1}F=c[10816]|0;if((F|0)==0){P=0}else{w=0;E=F;while(1){F=c[E+4>>2]|0;if(O){Q=F;L=7237}else{if((F|0)==(N|0)){Q=N;L=7237}else{R=w}}if((L|0)==7237){L=0;F=c[m>>2]|0;cf(F|0,76280,(v=i,i=i+8|0,c[v>>2]=Q,v)|0);ji(c[m>>2]|0,E+8|0,1);F=c[m>>2]|0;aF(10,F|0);R=1}F=c[E>>2]|0;if((F|0)==0){P=R;break}else{w=R;E=F}}}if((N|0)<1|P){break}uf(c[13898]|0,75640,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((x|0)==39|(x|0)==40|(x|0)==41|(x|0)==42|(x|0)==38){jO()}else if((x|0)==58){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}E=c[8244]|0;w=c[m>>2]|0;if((E|0)==0){aI(134568,26,1,w|0);break}else{cf(w|0,134696,(v=i,i=i+8|0,c[v>>2]=E,v)|0);break}}else if((x|0)==60){jQ()}else if((x|0)==62){jW()}else if((x|0)==79){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}cf(c[m>>2]|0,172840,(v=i,i=i+8|0,c[v>>2]=(a[30528]&1)!=0?172456:172176,v)|0)}else if((x|0)==63){c[13898]=o;jR()}else if((x|0)==77){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}cf(c[m>>2]|0,167288,(v=i,i=i+8|0,h[v>>3]=+h[3818],v)|0)}else if((x|0)==59){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}cf(c[m>>2]|0,136080,(v=i,i=i+8|0,c[v>>2]=(a[32936]&1)!=0?172456:172176,v)|0)}else if((x|0)==61){jP()}else if((x|0)==110){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}E=(c[16546]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56880,c[v+8>>2]=E,v)|0)}else if((x|0)==129){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}E=(c[16374]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56856,c[v+8>>2]=E,v)|0)}else if((x|0)==101){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}E=(c[17234]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56976,c[v+8>>2]=E,v)|0)}else if((x|0)==149){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}if((c[17406]|0)==1){E=c[m>>2]|0;cf(E|0,198344,(v=i,i=i+8|0,c[v>>2]=57e3,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,7)}else if((x|0)==19){if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}cf(c[m>>2]|0,168248,(v=i,i=i+8|0,c[v>>2]=c[44936+(c[11252]<<2)>>2],v)|0);E=c[m>>2]|0;w=bA(2,0)|0;cf(E|0,167680,(v=i,i=i+8|0,c[v>>2]=w,v)|0)}else if((x|0)==21){jT()}else if((x|0)==22){if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}ur(2,0)}else if((x|0)==144){if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}if((c[16202]|0)==1){w=c[m>>2]|0;cf(w|0,198344,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,0)}else if((x|0)==57){if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}S=+g[44];cf(c[m>>2]|0,183064,(v=i,i=i+16|0,h[v>>3]=+g[184],h[v+8>>3]=S,v)|0)}else if((x|0)==87){if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=c[3524]|0;E=c[m>>2]|0;if((w|0)==0){aI(183552,26,1,E|0);break}else{F=c[w>>2]|0;cf(E|0,183896,(v=i,i=i+16|0,c[v>>2]=F,c[v+8>>2]=13048,v)|0);break}}else if((x|0)==29){jJ()}else if((x|0)==153){jH(2)}else if((x|0)==14){H=134e3;I=c[13898]|0;uf(I,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((x|0)==107){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}if((c[17234]|0)==1){F=c[m>>2]|0;cf(F|0,198344,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,6)}else if((x|0)==3){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}aI(174264,15,1,c[m>>2]|0);F=c[m>>2]|0;if(+h[9040]==1.0){aI(173624,8,1,F|0);break}else{aI(173192,8,1,F|0);break}}else if((x|0)==78){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}cf(c[m>>2]|0,166928,(v=i,i=i+8|0,h[v>>3]=+h[3817],v)|0)}else if((x|0)==20){jS()}else if((x|0)==126){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}if((c[17062]|0)==1){F=c[m>>2]|0;cf(F|0,198344,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,5)}else if((x|0)==89|(x|0)==91|(x|0)==90|(x|0)==119){jZ(1,1,1,1,1,1)}else if((x|0)==48){j$(2)}else if((x|0)==52){j$(1)}else if((x|0)==54){j$(0)}else if((x|0)==68){j$(3)}else if((x|0)==46){j$(6)}else if((x|0)==50){j$(5)}else if((x|0)==93){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}j6(179864,141856,4672);cf(c[m>>2]|0,197424,(v=i,i=i+8|0,c[v>>2]=(c[1166]|0)!=0?196800:196168,v)|0);F=c[m>>2]|0;if((c[1164]|0)==0){aI(193896,14,1,F|0);break}else{aI(195648,36,1,F|0);break}}else if((x|0)==88){cf(c[m>>2]|0,91616,(v=i,i=i+8|0,c[v>>2]=(a[13048]|0)!=0?13048:84080,v)|0)}else if((x|0)==96){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}if((c[17750]|0)==1){F=c[m>>2]|0;cf(F|0,198344,(v=i,i=i+8|0,c[v>>2]=57048,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,9)}else if((x|0)==15){j0()}else if((x|0)==70){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}F=(c[16718]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56904,c[v+8>>2]=F,v)|0)}else if((x|0)==92){j_()}else if((x|0)==116){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}if((c[16546]|0)==1){F=c[m>>2]|0;cf(F|0,198344,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,2)}else if((x|0)==44){j1()}else if((x|0)==76){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}cf(c[m>>2]|0,222616,(v=i,i=i+8|0,c[v>>2]=c[6924],v)|0)}else if((x|0)==97){j2()}else if((x|0)==117|(x|0)==111|(x|0)==114){jZ(1,0,0,1,0,0)}else if((x|0)==136|(x|0)==130|(x|0)==133){jZ(0,1,0,0,1,0)}else if((x|0)==145|(x|0)==139|(x|0)==142){jZ(0,0,1,0,0,0)}else if((x|0)==66|(x|0)==71|(x|0)==73){jZ(0,0,0,0,0,1)}else if((x|0)==147){j4(7)}else if((x|0)==108|(x|0)==102|(x|0)==105){jZ(0,0,0,1,0,0)}else if((x|0)==127|(x|0)==121|(x|0)==124){jZ(0,0,0,0,1,0)}else if((x|0)==45){cf(c[m>>2]|0,121216,(v=i,i=i+8|0,c[v>>2]=(a[33512]&1)!=0?150376:150208,v)|0)}else if((x|0)==23){jF()}else if((x|0)==16){F=c[10812]|0;aI(123576,26,1,c[m>>2]|0);if((F|0)==0){break}else{T=F}do{F=c[T+12>>2]|0;E=c[m>>2]|0;if((F|0)==0){w=c[T+4>>2]|0;cf(E|0,123120,(v=i,i=i+8|0,c[v>>2]=w,v)|0)}else{cf(E|0,123448,(v=i,i=i+8|0,c[v>>2]=F,v)|0)}T=c[T>>2]|0;}while((T|0)!=0)}else if((x|0)==82){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}F=c[6350]|0;cf(c[m>>2]|0,174608,(v=i,i=i+16|0,c[v>>2]=c[6352],c[v+8>>2]=F,v)|0)}else if((x|0)==27){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}F=c[9342]|0;cf(c[m>>2]|0,174888,(v=i,i=i+16|0,c[v>>2]=c[9344],c[v+8>>2]=F,v)|0)}else if((x|0)==135){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}if((c[16374]|0)==1){F=c[m>>2]|0;cf(F|0,198344,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,1)}else if((x|0)==34){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}F=c[m>>2]|0;E=bA(2,0)|0;cf(F|0,146672,(v=i,i=i+8|0,c[v>>2]=E,v)|0);cf(c[m>>2]|0,144576,(v=i,i=i+8|0,c[v>>2]=c[44936+(c[11252]<<2)>>2],v)|0);E=c[m>>2]|0;F=bA(5,0)|0;cf(E|0,142976,(v=i,i=i+8|0,c[v>>2]=F,v)|0);F=c[8270]|0;cf(c[m>>2]|0,141432,(v=i,i=i+8|0,c[v>>2]=(F|0)!=0?F:139696,v)|0)}else if((x|0)==33){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}uq(2,0)}else if((x|0)==151){if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}cf(c[m>>2]|0,203824,(v=i,i=i+8|0,h[v>>3]=+h[11],v)|0)}else if((x|0)==31){L9824:do{if((G|0)<(c[8272]|0)){L9826:do{if((a[p+(G*40&-1)|0]&1)!=0){F=c[p+(G*40&-1)+36>>2]|0;E=p+(G*40&-1)+32|0;w=0;while(1){if((w|0)>=(F|0)){break}if((a[u+((c[E>>2]|0)+w|0)|0]|0)==(a[w+103664|0]|0)){w=w+1|0}else{break L9826}}if((w|0)==1){U=0;break L9824}}}while(0);E=is(f)|0;F=c[E>>2]|0;if((F|0)==1){V=+(c[E+8>>2]|0)}else if((F|0)==3){V=+uz(c[E+8>>2]|0,0)}else if((F|0)==2){V=+h[E+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}E=f|0;if((c[E>>2]|0)==3){uu(c[f+8>>2]|0);c[E>>2]=1}E=~~V;if((E|0)<1){H=208936}else{U=E;break}I=c[13898]|0;uf(I,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{U=0}}while(0);aF(10,c[m>>2]|0);E=c[10820]|0;if((E|0)==0){W=0}else{F=(U|0)==0;D=0;C=E;while(1){E=c[C+4>>2]|0;if(F){X=E;L=7214}else{if((E|0)==(U|0)){X=U;L=7214}else{Y=D}}if((L|0)==7214){L=0;E=c[m>>2]|0;cf(E|0,75168,(v=i,i=i+8|0,c[v>>2]=X,v)|0);ji(c[m>>2]|0,C+8|0,1);E=c[m>>2]|0;aF(10,E|0);Y=1}E=c[C>>2]|0;if((E|0)==0){W=Y;break}else{D=Y;C=E}}}if((U|0)<1|W){break}uf(c[13898]|0,74736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((x|0)==94){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}j6(179864,154440,4416)}else if((x|0)==113){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}j6(56880,202256,66456)}else if((x|0)==132){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}j6(56856,202256,65768)}else if((x|0)==80){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}C=c[m>>2]|0;D=c[7262]|0;do{if((D|0)==(c[n>>2]|0)){Z=89880}else{if((D|0)==0){Z=89256;break}F=c[7260]|0;Z=(D|0)!=(C|0)&(F|0)!=0?F:89256}}while(0);cf(C|0,134848,(v=i,i=i+8|0,c[v>>2]=Z,v)|0)}else if((x|0)==11){jB()}else if((x|0)==10|(x|0)==13|(x|0)==12){jC()}else if((x|0)==17){jD()}else if((x|0)==36){if(!(a[928]|0)){D=c[m>>2]|0;aF(10,D|0)}cf(c[m>>2]|0,113600,(v=i,i=i+8|0,c[v>>2]=(a[43472]&1)!=0?179864:211688,v)|0)}else if((x|0)==37){jE()}else if((x|0)==4){L9884:do{if((G|0)<(c[8272]|0)){L9886:do{if((a[p+(G*40&-1)|0]&1)!=0){D=c[p+(G*40&-1)+36>>2]|0;F=p+(G*40&-1)+32|0;E=0;while(1){if((E|0)>=(D|0)){break}if((a[u+((c[F>>2]|0)+E|0)|0]|0)==(a[E+103664|0]|0)){E=E+1|0}else{break L9886}}if((E|0)==1){_=0;break L9884}}}while(0);F=is(j)|0;D=c[F>>2]|0;if((D|0)==2){$=+h[F+8>>3]}else if((D|0)==3){$=+uz(c[F+8>>2]|0,0)}else if((D|0)==1){$=+(c[F+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}F=j|0;if((c[F>>2]|0)==3){uu(c[j+8>>2]|0);c[F>>2]=1}F=~~$;if((F|0)<1){H=208936}else{_=F;break}I=c[13898]|0;uf(I,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{_=0}}while(0);aF(10,c[m>>2]|0);jN(_)}else if((x|0)==5){jz()}else if((x|0)==6){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}S=+h[7077];C=c[m>>2]|0;if(S>0.0){F=(c[14156]|0)==0?150704:150856;cf(C|0,106368,(v=i,i=i+16|0,c[v>>2]=F,h[v+8>>3]=S,v)|0);break}else{aI(106320,33,1,C|0);break}}else if((x|0)==8){jA()}else if((x|0)==9){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}S=+h[7030];C=c[m>>2]|0;if(S<0.0){aI(106792,18,1,C|0);break}else{F=(a[56232]&1)!=0?106640:106608;cf(C|0,106688,(v=i,i=i+16|0,h[v>>3]=S,c[v+8>>2]=F,v)|0);break}}else if((x|0)==98){jI(c[m>>2]|0)}else if((x|0)==7){F=c[8272]|0;L9922:do{if((G|0)<(F|0)){C=G;while(1){L9925:do{if((a[p+(C*40&-1)|0]&1)!=0){D=c[p+(C*40&-1)+36>>2]|0;w=p+(C*40&-1)+32|0;q=0;while(1){if((q|0)>=(D|0)){break}if((a[u+((c[w>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break L9925}}if((q|0)==1){aa=C;break L9922}}}while(0);E=C+1|0;c[13898]=E;if((E|0)<(F|0)){C=E}else{aa=E;break}}}else{aa=G}}while(0);c[13898]=aa-1;dV()}else if((x|0)==1){aF(10,c[m>>2]|0);kc(it()|0,0);c[13898]=(c[13898]|0)+1}else if((x|0)==2){a[928]=1;jI(c[m>>2]|0);jz();if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}S=+h[7077];F=c[m>>2]|0;if(S>0.0){C=(c[14156]|0)==0?150704:150856;cf(F|0,106368,(v=i,i=i+16|0,c[v>>2]=C,h[v+8>>3]=S,v)|0)}else{aI(106320,33,1,F|0)}jA();if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}S=+h[7030];F=c[m>>2]|0;if(S<0.0){aI(106792,18,1,F|0)}else{C=(a[56232]&1)!=0?106640:106608;cf(F|0,106688,(v=i,i=i+16|0,h[v>>3]=S,c[v+8>>2]=C,v)|0)}jB();C=c[m>>2]|0;if((a[35888]&1)==0){aI(107400,37,1,C|0)}else{cf(C|0,107464,(v=i,i=i+8|0,c[v>>2]=53520,v)|0)}jC();jD();if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}cf(c[m>>2]|0,113600,(v=i,i=i+8|0,c[v>>2]=(a[43472]&1)!=0?179864:211688,v)|0);jE();if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}cf(c[m>>2]|0,114368,(v=i,i=i+16|0,c[v>>2]=24544,c[v+8>>2]=24595,v)|0);jF();jX();jG();cf(c[m>>2]|0,125408,(v=i,i=i+8|0,c[v>>2]=(a[27776]&1)!=0?179864:211688,v)|0);jH(2);jH(1);jH(0);jM(0);jN(0);jK();jL();if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}C=(c[8764]|0)==2?88392:179864;S=+h[4384];F=(c[6588]|0)==2?88392:179864;ab=+h[3296];E=(c[1057]|0)==2?88392:179864;ac=+h[531];w=(c[14091]|0)==2?88392:179864;ad=+h[7048];cf(c[m>>2]|0,89120,(v=i,i=i+72|0,c[v>>2]=132528,c[v+8>>2]=C,h[v+16>>3]=S,c[v+24>>2]=F,h[v+32>>3]=ab,c[v+40>>2]=E,h[v+48>>3]=ac,c[v+56>>2]=w,h[v+64>>3]=ad,v)|0);jO();if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=c[8244]|0;E=c[m>>2]|0;if((w|0)==0){aI(134568,26,1,E|0)}else{cf(E|0,134696,(v=i,i=i+8|0,c[v>>2]=w,v)|0)}if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=c[m>>2]|0;E=c[7262]|0;do{if((E|0)==(c[n>>2]|0)){ae=89880}else{if((E|0)==0){ae=89256;break}F=c[7260]|0;ae=(E|0)!=(w|0)&(F|0)!=0?F:89256}}while(0);cf(w|0,134848,(v=i,i=i+8|0,c[v>>2]=ae,v)|0);if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}cf(c[m>>2]|0,136080,(v=i,i=i+8|0,c[v>>2]=(a[32936]&1)!=0?172456:172176,v)|0);jQ();jW();jP();if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}cf(c[m>>2]|0,166928,(v=i,i=i+8|0,h[v>>3]=+h[3817],v)|0);if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}cf(c[m>>2]|0,167288,(v=i,i=i+8|0,h[v>>3]=+h[3818],v)|0);if(!(a[928]|0)){E=c[m>>2]|0;aF(10,E|0)}cf(c[m>>2]|0,168248,(v=i,i=i+8|0,c[v>>2]=c[44936+(c[11252]<<2)>>2],v)|0);E=c[m>>2]|0;F=bA(2,0)|0;cf(E|0,167680,(v=i,i=i+8|0,c[v>>2]=F,v)|0);jS();jT();if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}cf(c[m>>2]|0,172840,(v=i,i=i+8|0,c[v>>2]=(a[30528]&1)!=0?172456:172176,v)|0);if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}aI(174264,15,1,c[m>>2]|0);F=c[m>>2]|0;if(+h[9040]==1.0){aI(173624,8,1,F|0)}else{aI(173192,8,1,F|0)}jj(c[m>>2]|0,0);if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}F=c[6350]|0;cf(c[m>>2]|0,174608,(v=i,i=i+16|0,c[v>>2]=c[6352],c[v+8>>2]=F,v)|0);if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}F=c[9342]|0;cf(c[m>>2]|0,174888,(v=i,i=i+16|0,c[v>>2]=c[9344],c[v+8>>2]=F,v)|0);jV();if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}cf(c[m>>2]|0,178568,(v=i,i=i+8|0,c[v>>2]=(a[46752]&1)!=0?179864:211688,v)|0);if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}cf(c[m>>2]|0,179872,(v=i,i=i+8|0,c[v>>2]=(a[38984]&1)!=0?179488:179160,v)|0);gp();jY();if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}ad=+g[44];cf(c[m>>2]|0,183064,(v=i,i=i+16|0,h[v>>3]=+g[184],h[v+8>>3]=ad,v)|0);if(!(a[928]|0)){F=c[m>>2]|0;aF(10,F|0)}F=c[3524]|0;E=c[m>>2]|0;if((F|0)==0){aI(183552,26,1,E|0)}else{C=c[F>>2]|0;cf(E|0,183896,(v=i,i=i+16|0,c[v>>2]=C,c[v+8>>2]=13048,v)|0)}jZ(1,1,1,1,1,1);j$(2);j$(1);j$(0);j$(6);j$(5);j6(179864,141856,4672);if((a[32936]&1)==0){if((a[30528]&1)!=0){L=7049}}else{L=7049}do{if((L|0)==7049){C=a[928]|0;if((a[37384]&1)==0){if(!C){E=c[m>>2]|0;aF(10,E|0)}if((c[17578]|0)==1){E=c[m>>2]|0;cf(E|0,198344,(v=i,i=i+8|0,c[v>>2]=57024,v)|0)}E=c[m>>2]|0;aF(9,E|0);je(c[m>>2]|0,8);break}if(!C){C=c[m>>2]|0;aF(10,C|0)}if((c[17750]|0)==1){C=c[m>>2]|0;cf(C|0,198344,(v=i,i=i+8|0,c[v>>2]=57048,v)|0)}C=c[m>>2]|0;aF(9,C|0);je(c[m>>2]|0,9);if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}if((c[17922]|0)==1){C=c[m>>2]|0;cf(C|0,198344,(v=i,i=i+8|0,c[v>>2]=57072,v)|0)}C=c[m>>2]|0;aF(9,C|0);je(c[m>>2]|0,10)}}while(0);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}if((c[16546]|0)==1){w=c[m>>2]|0;cf(w|0,198344,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,2);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}if((c[16374]|0)==1){w=c[m>>2]|0;cf(w|0,198344,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,1);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}if((c[17234]|0)==1){w=c[m>>2]|0;cf(w|0,198344,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,6);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}if((c[17062]|0)==1){w=c[m>>2]|0;cf(w|0,198344,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,5);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}if((c[16202]|0)==1){w=c[m>>2]|0;cf(w|0,198344,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,0);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}j6(179864,154440,4416);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}j6(56880,202256,66456);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}j6(56856,202256,65768);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}j6(56832,202256,65080);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}j6(56976,202256,69208);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}j6(56952,202256,68520);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=(c[16546]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56880,c[v+8>>2]=w,v)|0);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=(c[16374]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56856,c[v+8>>2]=w,v)|0);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=(c[17234]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56976,c[v+8>>2]=w,v)|0);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=(c[17062]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56952,c[v+8>>2]=w,v)|0);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=(c[16202]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56832,c[v+8>>2]=w,v)|0);j_();if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}uq(2,0);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}ur(2,0);jU();if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=c[m>>2]|0;C=bA(2,0)|0;cf(w|0,146672,(v=i,i=i+8|0,c[v>>2]=C,v)|0);cf(c[m>>2]|0,144576,(v=i,i=i+8|0,c[v>>2]=c[44936+(c[11252]<<2)>>2],v)|0);C=c[m>>2]|0;w=bA(5,0)|0;cf(C|0,142976,(v=i,i=i+8|0,c[v>>2]=w,v)|0);w=c[8270]|0;cf(c[m>>2]|0,141432,(v=i,i=i+8|0,c[v>>2]=(w|0)!=0?w:139696,v)|0);if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}cf(c[m>>2]|0,203824,(v=i,i=i+8|0,h[v>>3]=+h[11],v)|0);j0();j1();if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}cf(c[m>>2]|0,222616,(v=i,i=i+8|0,c[v>>2]=c[6924],v)|0);j2();w=c[10812]|0;aI(123576,26,1,c[m>>2]|0);if((w|0)!=0){C=w;do{w=c[C+12>>2]|0;E=c[m>>2]|0;if((w|0)==0){F=c[C+4>>2]|0;cf(E|0,123120,(v=i,i=i+8|0,c[v>>2]=F,v)|0)}else{cf(E|0,123448,(v=i,i=i+8|0,c[v>>2]=w,v)|0)}C=c[C>>2]|0;}while((C|0)!=0)}a[928]=0}else if((x|0)==99){jV()}else if((x|0)==85){jX()}else if((x|0)==84){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}cf(c[m>>2]|0,178568,(v=i,i=i+8|0,c[v>>2]=(a[46752]&1)!=0?179864:211688,v)|0)}else if((x|0)==25){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}cf(c[m>>2]|0,179872,(v=i,i=i+8|0,c[v>>2]=(a[38984]&1)!=0?179488:179160,v)|0);gp()}else if((x|0)==83){jY()}else if((x|0)==141){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}j6(56832,202256,65080)}else if((x|0)==64){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}j6(56904,202256,67144)}else if((x|0)==104){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}j6(56976,202256,69208)}else if((x|0)==123){if(!(a[928]|0)){C=c[m>>2]|0;aF(10,C|0)}j6(56952,202256,68520)}else if((x|0)==81){jU()}else if((x|0)==75){C=c[p+(G*40&-1)+36>>2]|0;L10161:do{if((a[p+(G*40&-1)|0]&1)!=0&(C|0)>0){w=0;E=0;F=c[p+(G*40&-1)+32>>2]|0;while(1){if((a[w+115824|0]|0)==(a[u+(w+F|0)|0]|0)){af=F;ag=E}else{if((w|0)!=4){ah=G;break L10161}af=F-1|0;ag=1}D=w+1|0;if((D|0)<(ag+C|0)){w=D;E=ag;F=af}else{break}}if((ag|0)==0){if(!((w|0)==3|(w|0)==9)){ah=G;break}}F=l+3|0;c[13898]=F;ah=F}else{ah=G}}while(0);L10173:do{if((ah|0)<(c[8272]|0)){L10175:do{if((a[p+(ah*40&-1)|0]&1)!=0){C=c[p+(ah*40&-1)+36>>2]|0;F=p+(ah*40&-1)+32|0;E=0;while(1){if((E|0)>=(C|0)){break}if((a[u+((c[F>>2]|0)+E|0)|0]|0)==(a[E+103664|0]|0)){E=E+1|0}else{break L10175}}if((E|0)==1){ai=0;break L10173}}}while(0);w=is(d)|0;F=c[w>>2]|0;if((F|0)==1){aj=+(c[w+8>>2]|0)}else if((F|0)==2){aj=+h[w+8>>3]}else if((F|0)==3){aj=+uz(c[w+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}w=d|0;if((c[w>>2]|0)==3){uu(c[d+8>>2]|0);c[w>>2]=1}w=~~aj;if((w|0)<1){H=208936}else{ai=w;break}I=c[13898]|0;uf(I,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{ai=0}}while(0);aF(10,c[m>>2]|0);jj(c[m>>2]|0,ai)}else if((x|0)==120){if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=(c[17062]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56952,c[v+8>>2]=w,v)|0)}else if((x|0)==138){if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=(c[16202]|0)==1?141856:202784;cf(c[m>>2]|0,203352,(v=i,i=i+16|0,c[v>>2]=56832,c[v+8>>2]=w,v)|0)}else if((x|0)==24){jG()}else if((x|0)==150){cf(c[m>>2]|0,125408,(v=i,i=i+8|0,c[v>>2]=(a[27776]&1)!=0?179864:211688,v)|0)}else if((x|0)==152){jH(2);jH(1);jH(0)}else if((x|0)==155){jH(1)}else if((x|0)==154){jH(6)}else if((x|0)==156){jH(5)}else if((x|0)==157){jH(0)}else if((x|0)==28){jK()}else if((x|0)==35){jL()}else if((x|0)==56){if(!(a[928]|0)){w=c[m>>2]|0;aF(10,w|0)}w=(c[8764]|0)==2?88392:179864;ad=+h[4384];F=(c[6588]|0)==2?88392:179864;ac=+h[3296];C=(c[1057]|0)==2?88392:179864;ab=+h[531];D=(c[14091]|0)==2?88392:179864;S=+h[7048];cf(c[m>>2]|0,89120,(v=i,i=i+72|0,c[v>>2]=132528,c[v+8>>2]=w,h[v+16>>3]=ad,c[v+24>>2]=F,h[v+32>>3]=ac,c[v+40>>2]=C,h[v+48>>3]=ab,c[v+56>>2]=D,h[v+64>>3]=S,v)|0)}else if((x|0)==100){if(!(a[928]|0)){D=c[m>>2]|0;aF(10,D|0)}if((c[17922]|0)==1){D=c[m>>2]|0;cf(D|0,198344,(v=i,i=i+8|0,c[v>>2]=57072,v)|0)}aF(9,c[m>>2]|0);je(c[m>>2]|0,10)}else{H=223488;I=c[13898]|0;uf(I,H,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);a[25280]=0;aF(10,c[m>>2]|0);i=b;return}function jz(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}aI(106224,16,1,c[m>>2]|0);do{if((a[32936]&1)!=0){if((a[37384]&1)==0){d=c[17711]|0;e=c[m>>2]|0;f=d&3;g=(f|0)!=0?172456:172176;h=(f|0)==1?105824:179864;j=(f|0)==2?105728:179864;f=(d&4|0)!=0?105664:179864;k=(d&8|0)!=0?105608:179864;cf(e|0,105872,(v=i,i=i+48|0,c[v>>2]=57048,c[v+8>>2]=g,c[v+16>>2]=h,c[v+24>>2]=j,c[v+32>>2]=f,c[v+40>>2]=k,v)|0);k=c[17883]|0;f=c[m>>2]|0;j=k&3;h=(j|0)!=0?172456:172176;g=(j|0)==1?105824:179864;e=(j|0)==2?105728:179864;j=(k&4|0)!=0?105664:179864;d=(k&8|0)!=0?105608:179864;cf(f|0,105872,(v=i,i=i+48|0,c[v>>2]=57072,c[v+8>>2]=h,c[v+16>>2]=g,c[v+24>>2]=e,c[v+32>>2]=j,c[v+40>>2]=d,v)|0);break}else{d=c[17539]|0;j=c[m>>2]|0;e=d&3;g=(e|0)!=0?172456:172176;h=(e|0)==1?105824:179864;f=(e|0)==2?105728:179864;e=(d&4|0)!=0?105664:179864;k=(d&8|0)!=0?105608:179864;cf(j|0,105872,(v=i,i=i+48|0,c[v>>2]=57024,c[v+8>>2]=g,c[v+16>>2]=h,c[v+24>>2]=f,c[v+32>>2]=e,c[v+40>>2]=k,v)|0);break}}}while(0);if((a[30528]&1)!=0){k=c[17367]|0;e=c[m>>2]|0;f=k&3;h=(f|0)!=0?172456:172176;g=(f|0)==1?105824:179864;j=(f|0)==2?105728:179864;f=(k&4|0)!=0?105664:179864;d=(k&8|0)!=0?105608:179864;cf(e|0,105872,(v=i,i=i+48|0,c[v>>2]=57e3,c[v+8>>2]=h,c[v+16>>2]=g,c[v+24>>2]=j,c[v+32>>2]=f,c[v+40>>2]=d,v)|0)}d=c[16507]|0;f=d&3;cf(c[m>>2]|0,105872,(v=i,i=i+48|0,c[v>>2]=56880,c[v+8>>2]=(f|0)!=0?172456:172176,c[v+16>>2]=(f|0)==1?105824:179864,c[v+24>>2]=(f|0)==2?105728:179864,c[v+32>>2]=(d&4|0)!=0?105664:179864,c[v+40>>2]=(d&8|0)!=0?105608:179864,v)|0);d=c[16335]|0;f=d&3;cf(c[m>>2]|0,105872,(v=i,i=i+48|0,c[v>>2]=56856,c[v+8>>2]=(f|0)!=0?172456:172176,c[v+16>>2]=(f|0)==1?105824:179864,c[v+24>>2]=(f|0)==2?105728:179864,c[v+32>>2]=(d&4|0)!=0?105664:179864,c[v+40>>2]=(d&8|0)!=0?105608:179864,v)|0);aI(105360,17,1,c[m>>2]|0);d=c[17195]|0;f=d&3;cf(c[m>>2]|0,105872,(v=i,i=i+48|0,c[v>>2]=56976,c[v+8>>2]=(f|0)!=0?172456:172176,c[v+16>>2]=(f|0)==1?105824:179864,c[v+24>>2]=(f|0)==2?105728:179864,c[v+32>>2]=(d&4|0)!=0?105664:179864,c[v+40>>2]=(d&8|0)!=0?105608:179864,v)|0);d=c[17023]|0;f=d&3;cf(c[m>>2]|0,105872,(v=i,i=i+48|0,c[v>>2]=56952,c[v+8>>2]=(f|0)!=0?172456:172176,c[v+16>>2]=(f|0)==1?105824:179864,c[v+24>>2]=(f|0)==2?105728:179864,c[v+32>>2]=(d&4|0)!=0?105664:179864,c[v+40>>2]=(d&8|0)!=0?105608:179864,v)|0);aI(105360,17,1,c[m>>2]|0);d=c[16163]|0;f=d&3;cf(c[m>>2]|0,105872,(v=i,i=i+48|0,c[v>>2]=56832,c[v+8>>2]=(f|0)!=0?172456:172176,c[v+16>>2]=(f|0)==1?105824:179864,c[v+24>>2]=(f|0)==2?105728:179864,c[v+32>>2]=(d&4|0)!=0?105664:179864,c[v+40>>2]=(d&8|0)!=0?105608:179864,v)|0);d=c[16679]|0;f=d&3;cf(c[m>>2]|0,105872,(v=i,i=i+48|0,c[v>>2]=56904,c[v+8>>2]=(f|0)!=0?172456:172176,c[v+16>>2]=(f|0)==1?105824:179864,c[v+24>>2]=(f|0)==2?105728:179864,c[v+32>>2]=(d&4|0)!=0?105664:179864,c[v+40>>2]=(d&8|0)!=0?105608:179864,v)|0);i=b;return}function jA(){var b=0,d=0,e=0,f=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}d=c[11692]|0;e=c[m>>2]|0;if((d|0)==0){aI(106520,21,1,e|0);i=b;return}else{f=(c[14088]|0)==0?150704:150856;cf(e|0,106440,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=f,v)|0);ji(c[m>>2]|0,56296,0);f=c[m>>2]|0;aF(10,f|0);i=b;return}}function jB(){var b=0,d=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}cf(c[m>>2]|0,107344,(v=i,i=i+8|0,c[v>>2]=(a[54144]&1)!=0?172456:172176,v)|0);d=c[m>>2]|0;if((a[54160]&1)==0){aI(107184,55,1,d|0)}else{aI(107256,64,1,d|0)}d=c[m>>2]|0;if((a[54152]&1)==0){aI(106872,47,1,d|0);i=b;return}else{aI(107096,56,1,d|0);i=b;return}}function jC(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0.0,o=0.0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}cf(c[m>>2]|0,110616,(v=i,i=i+8|0,c[v>>2]=(c[11690]|0)!=0?179160:109736,v)|0);if((c[11690]|0)==0){i=b;return}cf(c[m>>2]|0,109312,(v=i,i=i+8|0,c[v>>2]=c[13374],v)|0);d=c[11690]|0;if((d|0)==1){e=c[m>>2]|0;aI(108736,10,1,e|0)}else if((d|0)==2){aI(108488,8,1,c[m>>2]|0)}else if((d|0)==3){aI(108392,22,1,c[m>>2]|0)}d=c[13378]|0;if((d|0)==0){e=c[m>>2]|0;aI(108304,21,1,e|0)}else if((d|0)==1){cf(c[m>>2]|0,108216,(v=i,i=i+8|0,c[v>>2]=c[13366],v)|0)}else if((d|0)==2){d=c[13366]|0;cf(c[m>>2]|0,108128,(v=i,i=i+16|0,c[v>>2]=c[13368],c[v+8>>2]=d,v)|0)}d=c[13372]|0;if((d|0)==0){e=c[m>>2]|0;f=c[13374]|0;cf(e|0,108056,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}else if((d|0)==2){cf(c[m>>2]|0,107816,(v=i,i=i+8|0,c[v>>2]=c[13374],v)|0);cf(c[m>>2]|0,170912,(v=i,i=i+8|0,h[v>>3]=+h[c[11662]>>3],v)|0);f=c[m>>2]|0;if((c[13374]|0)>1){e=1;g=f;while(1){cf(g|0,107648,(v=i,i=i+8|0,h[v>>3]=+h[(c[11662]|0)+(e<<3)>>3],v)|0);j=e+1|0;k=c[m>>2]|0;if((j|0)<(c[13374]|0)){e=j;g=k}else{l=k;break}}}else{l=f}aF(10,l|0)}else if((d|0)==1){d=c[13374]|0;l=c[11662]|0;n=+h[l>>3];o=+h[l+8>>3];cf(c[m>>2]|0,107544,(v=i,i=i+32|0,c[v>>2]=d,h[v+8>>3]=n,h[v+16>>3]=o,h[v+24>>3]=n+ +(d-1|0)*o,v)|0)}d=c[m>>2]|0;if((a[35888]&1)==0){aI(107400,37,1,d|0);i=b;return}else{cf(d|0,107464,(v=i,i=i+8|0,c[v>>2]=53520,v)|0);i=b;return}}function jD(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0.0,o=0.0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}if((a[47032]&1)==0){d=c[m>>2]|0;aI(111136,25,1,d|0);i=b;return}d=c[11752]|0;if((d|0)==1){e=c[m>>2]|0;f=c[11732]|0;g=c[11756]|0;j=c[11734]|0;cf(e|0,113384,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=g,c[v+16>>2]=j,v)|0);i=b;return}j=c[m>>2]|0;g=c[11732]|0;f=c[11756]|0;if((d|0)==2){cf(j|0,113272,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=f,v)|0);i=b;return}else{k=-1}while(1){e=k+1|0;if((e|0)==7){l=0;break}if((c[46948+(e<<3)>>2]|0)==(d|0)){l=c[46944+(e<<3)>>2]|0;break}else{k=e}}n=+h[5865];o=+h[5864];k=(a[47016]&1)!=0?112728:179864;cf(j|0,113e3,(v=i,i=i+48|0,c[v>>2]=g,c[v+8>>2]=f,c[v+16>>2]=l,h[v+24>>3]=n,h[v+32>>3]=o,c[v+40>>2]=k,v)|0);i=b;return}function jE(){var b=0,d=0;if(!(a[928]|0)){b=c[m>>2]|0;aF(10,b|0)}aI(114040,25,1,c[m>>2]|0);b=c[8732]|0;if((b|0)==1){d=c[m>>2]|0;aI(113784,10,1,d|0);return}else if((b|0)==0){aI(113904,10,1,c[m>>2]|0);return}else if((b|0)==2){aI(113712,12,1,c[m>>2]|0);return}else{return}}function jF(){var b=0,d=0,e=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}aI(114560,16,1,c[m>>2]|0);d=c[m>>2]|0;e=j3(66240)|0;cf(d|0,114480,(v=i,i=i+16|0,c[v>>2]=56880,c[v+8>>2]=e,v)|0);e=c[m>>2]|0;d=j3(65552)|0;cf(e|0,114480,(v=i,i=i+16|0,c[v>>2]=56856,c[v+8>>2]=d,v)|0);d=c[m>>2]|0;e=j3(68992)|0;cf(d|0,114480,(v=i,i=i+16|0,c[v>>2]=56976,c[v+8>>2]=e,v)|0);e=c[m>>2]|0;d=j3(68304)|0;cf(e|0,114480,(v=i,i=i+16|0,c[v>>2]=56952,c[v+8>>2]=d,v)|0);d=c[m>>2]|0;e=j3(64864)|0;cf(d|0,114480,(v=i,i=i+16|0,c[v>>2]=56832,c[v+8>>2]=e,v)|0);e=c[m>>2]|0;d=j3(66928)|0;cf(e|0,114480,(v=i,i=i+16|0,c[v>>2]=56904,c[v+8>>2]=d,v)|0);d=c[m>>2]|0;e=j3(69680)|0;cf(d|0,114480,(v=i,i=i+16|0,c[v>>2]=57e3,c[v+8>>2]=e,v)|0);i=b;return}function jG(){var b=0,d=0,e=0,f=0,g=0.0,j=0.0,k=0.0,l=0;b=i;if(a[928]|0){d=0}else{e=c[m>>2]|0;aF(10,e|0);d=0}while(1){if(d>>>0>=8){f=7559;break}if((a[65036+(d*688&-1)|0]&1)!=0){break}if((a[65037+(d*688&-1)|0]&1)==0){d=d+1|0}else{break}}if((f|0)==7559){f=c[m>>2]|0;aI(125328,13,1,f|0);i=b;return}cf(c[m>>2]|0,125104,(v=i,i=i+8|0,c[v>>2]=+h[3815]!=0.0?124928:124832,v)|0);if((a[66412]&1)!=0){f=c[m>>2]|0;cf(f|0,124744,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}if((a[66413]&1)!=0){f=c[m>>2]|0;cf(f|0,124664,(v=i,i=i+8|0,c[v>>2]=56880,v)|0)}if((a[65724]&1)!=0){f=c[m>>2]|0;cf(f|0,124744,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}if((a[65725]&1)!=0){f=c[m>>2]|0;cf(f|0,124664,(v=i,i=i+8|0,c[v>>2]=56856,v)|0)}if((a[69164]&1)!=0){f=c[m>>2]|0;cf(f|0,124744,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}if((a[69165]&1)!=0){f=c[m>>2]|0;cf(f|0,124664,(v=i,i=i+8|0,c[v>>2]=56976,v)|0)}if((a[68476]&1)!=0){f=c[m>>2]|0;cf(f|0,124744,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}if((a[68477]&1)!=0){f=c[m>>2]|0;cf(f|0,124664,(v=i,i=i+8|0,c[v>>2]=56952,v)|0)}if((a[65036]&1)!=0){f=c[m>>2]|0;cf(f|0,124744,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}if((a[65037]&1)!=0){f=c[m>>2]|0;cf(f|0,124664,(v=i,i=i+8|0,c[v>>2]=56832,v)|0)}if((a[67100]&1)!=0){f=c[m>>2]|0;cf(f|0,124744,(v=i,i=i+8|0,c[v>>2]=56904,v)|0)}if((a[67101]&1)!=0){f=c[m>>2]|0;cf(f|0,124664,(v=i,i=i+8|0,c[v>>2]=56904,v)|0)}if((a[69852]&1)!=0){f=c[m>>2]|0;cf(f|0,124744,(v=i,i=i+8|0,c[v>>2]=57e3,v)|0)}if((a[69853]&1)!=0){f=c[m>>2]|0;cf(f|0,124664,(v=i,i=i+8|0,c[v>>2]=57e3,v)|0)}aI(124552,6,1,c[m>>2]|0);aI(124400,22,1,c[m>>2]|0);ji(c[m>>2]|0,40048,0);aI(124312,23,1,c[m>>2]|0);ji(c[m>>2]|0,34776,0);aF(10,c[m>>2]|0);g=+h[3815];if(g!=0.0){f=c[m>>2]|0;j=+h[9040];k=g/j;d=j==1.0?124080:123976;cf(f|0,124200,(v=i,i=i+16|0,h[v>>3]=k,c[v+8>>2]=d,v)|0)}d=c[10026]|0;if((d|0)==-1){l=123760}else{l=(d|0)==0?150704:150856}cf(c[m>>2]|0,123840,(v=i,i=i+8|0,c[v>>2]=l,v)|0);i=b;return}function jH(b){b=b|0;var d=0,e=0,f=0,g=0;d=i;e=b;while(1){if(!(a[928]|0)){b=c[m>>2]|0;aF(10,b|0)}b=c[m>>2]|0;f=56832+(e*24&-1)|0;if((c[65284+(e*688&-1)>>2]|0)>-3){g=65280+(e*688&-1)|0;cf(b|0,125624,(v=i,i=i+8|0,c[v>>2]=f,v)|0);ji(c[m>>2]|0,g,0);g=c[m>>2]|0;aF(10,g|0)}else{cf(b|0,125528,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}if(e>>>0<4){e=e+4|0}else{break}}i=d;return}function jI(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0;d=i;i=i+2056|0;e=d|0;f=d+8|0;g=e|0;a[g]=35;h=e+5|0;j=e+1|0;w=538976288;a[j]=w&255;w=w>>8;a[j+1|0]=w&255;w=w>>8;a[j+2|0]=w&255;w=w>>8;a[j+3|0]=w&255;a[h]=0;if((c[13388]|0)==0){j=ut(1024)|0;do{if((j|0)==0){gk();e=ut(1024)|0;if((e|0)!=0){k=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=216448,v)|0)}else{k=j}}while(0);c[13388]=k;be(k|0,163128,(v=i,i=i+88|0,c[v>>2]=211088,c[v+8>>2]=204752,c[v+16>>2]=184880,c[v+24>>2]=179760,c[v+32>>2]=201520,c[v+40>>2]=179864,c[v+48>>2]=175632,c[v+56>>2]=179864,c[v+64>>2]=171680,c[v+72>>2]=179864,c[v+80>>2]=167488,v)|0);k=c[13388]|0;c[13388]=db(k,(uA(k|0)|0)+1|0,216448)|0}if((b|0)==0){i=d;return}if((c[m>>2]|0)==(b|0)){l=h}else{cf(b|0,154128,(v=i,i=i+8|0,c[v>>2]=151008,v)|0);l=g}g=f|0;uD(g|0,149072,187);uD(f+(uA(g|0)|0)|0,147160,95);cf(b|0,g|0,(v=i,i=i+144|0,c[v>>2]=l,c[v+8>>2]=l,c[v+16>>2]=145064,c[v+24>>2]=l,c[v+32>>2]=40152,c[v+40>>2]=40160,c[v+48>>2]=40168,c[v+56>>2]=l,c[v+64>>2]=33040,c[v+72>>2]=33008,c[v+80>>2]=l,c[v+88>>2]=l,c[v+96>>2]=40184,c[v+104>>2]=l,c[v+112>>2]=l,c[v+120>>2]=l,c[v+128>>2]=l,c[v+136>>2]=l,v)|0);l=c[13898]|0;g=c[1054]|0;b=c[g+(l*40&-1)+36>>2]|0;if(!((a[g+(l*40&-1)|0]&1)!=0&(b|0)>0)){i=d;return}f=c[10036]|0;h=0;k=0;j=c[g+(l*40&-1)+32>>2]|0;while(1){if((a[h+143320|0]|0)==(a[f+(h+j|0)|0]|0)){n=j;o=k}else{if((h|0)!=1){p=7623;break}n=j-1|0;o=1}g=h+1|0;if((g|0)<(o+b|0)){h=g;k=o;j=n}else{break}}if((p|0)==7623){i=d;return}do{if((o|0)==0){if((h|0)==0|(h|0)==4){break}i=d;return}}while(0);c[13898]=l+1;cf(c[m>>2]|0,141816,(v=i,i=i+8|0,c[v>>2]=c[13388],v)|0);l=bU(140128)|0;cf(c[m>>2]|0,136384,(v=i,i=i+8|0,c[v>>2]=195896,v)|0);cf(c[m>>2]|0,132976,(v=i,i=i+8|0,c[v>>2]=(l|0)==0?138024:l,v)|0);i=d;return}function jJ(){var b=0,d=0,e=0,f=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}d=c[m>>2]|0;e=j3(36288)|0;cf(d|0,128328,(v=i,i=i+8|0,c[v>>2]=e,v)|0);e=c[9329]|0;do{if((e|0)!=0){if((a[e]|0)==0){break}d=c[m>>2]|0;cf(d|0,82560,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}}while(0);if((c[9330]|0)==1&(c[9331]|0)==-2){i=b;return}aI(84256,2,1,c[m>>2]|0);e=c[m>>2]|0;if((c[9330]|0)==0){f=e}else{aI(145280,10,1,e|0);jg(e,37320);f=c[m>>2]|0}aF(10,f|0);i=b;return}function jK(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0.0,l=0,n=0.0,o=0,p=0.0,q=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}if((a[36120]&1)==0){d=c[m>>2]|0;aI(131736,12,1,d|0);i=b;return}d=c[9031]|0;if((d|0)==0|(d|0)==1|(d|0)==2){e=c[m>>2]|0;aI(131624,22,1,e|0);if((c[9031]|0)==2&(c[9032]|0)>>>0<2){f=7651}else{e=c[9044]|0;do{if((e|0)==0){g=c[m>>2]|0;aI(196168,3,1,g|0)}else{g=c[m>>2]|0;if((e|0)==2){aI(196800,6,1,g|0);break}else{aI(89896,6,1,g|0);break}}}while(0);if((c[9031]|0)==2){f=7651}else{f=7652}}if((f|0)==7651){if(((c[9032]|0)-2|0)>>>0>=2){f=7652}}do{if((f|0)==7652){e=c[9045]|0;if((e|0)==0){g=c[m>>2]|0;aI(131440,5,1,g|0);break}else if((e|0)==2){aI(131280,6,1,c[m>>2]|0);break}else{if((c[9044]|0)==1){break}aI(131216,7,1,c[m>>2]|0);break}}}while(0);f=c[m>>2]|0;if((c[9047]|0)==0){aI(131080,9,1,f|0)}else{aI(130936,11,1,f|0)}f=c[9031]|0;do{if((f|0)==0){e=c[m>>2]|0;aI(130784,7,1,e|0)}else if((f|0)==1){aI(130648,8,1,c[m>>2]|0)}else{e=c[9032]|0;if((e|0)==1){g=c[m>>2]|0;aI(130432,8,1,g|0);break}else if((e|0)==2){aI(130336,8,1,c[m>>2]|0);break}else if((e|0)==0){aI(130512,8,1,c[m>>2]|0);break}else if((e|0)==3){aI(130288,8,1,c[m>>2]|0);break}else{break}}}while(0);f=c[m>>2]|0;aF(10,f|0)}else if((d|0)==3){aI(130224,11,1,c[m>>2]|0);d=c[9034]|0;if((d|0)==0){j=179864}else{j=c[21784+(d<<2)>>2]|0}k=+h[4519];f=c[9035]|0;if((f|0)==(d|0)){l=179864}else{l=c[21784+(f<<2)>>2]|0}n=+h[4520];d=c[9036]|0;if((d|0)==(f|0)){o=179864}else{o=c[21784+(d<<2)>>2]|0}p=+h[4521];cf(c[m>>2]|0,77392,(v=i,i=i+48|0,c[v>>2]=j,h[v+8>>3]=k,c[v+16>>2]=l,h[v+24>>3]=n,c[v+32>>2]=o,h[v+40>>3]=p,v)|0);aF(10,c[m>>2]|0)}o=(a[36229]&1)!=0?179864:211688;l=(a[36230]&1)!=0?179864:211688;j=(a[36231]&1)!=0?179864:211688;cf(c[m>>2]|0,130072,(v=i,i=i+32|0,c[v>>2]=(c[9046]|0)==0?130008:129888,c[v+8>>2]=o,c[v+16>>2]=l,c[v+24>>2]=j,v)|0);j=c[m>>2]|0;if((c[9059]|0)>-3){aI(129760,12,1,j|0);ji(c[m>>2]|0,36232,0);l=c[m>>2]|0;aF(10,l|0)}else{aI(129608,10,1,j|0)}if((a[36228]&1)!=0){j=c[m>>2]|0;aI(129416,51,1,j|0)}p=+h[4525];n=+h[4526];k=+h[4527];j=c[9056]|0;if((j|0)==1){q=129016}else{q=(j|0)==2?128920:179864}cf(c[m>>2]|0,129128,(v=i,i=i+48|0,h[v>>3]=+h[4524],h[v+8>>3]=p,h[v+16>>3]=n,h[v+24>>3]=k,c[v+32>>2]=(j|0)!=0?179864:171496,c[v+40>>2]=q,v)|0);aI(128800,30,1,c[m>>2]|0);q=c[9338]|0;j=c[m>>2]|0;if((q|0)>0){cf(j|0,128688,(v=i,i=i+8|0,c[v>>2]=q,v)|0)}else{aI(128592,25,1,j|0)}aI(128504,27,1,c[m>>2]|0);j=c[9339]|0;q=c[m>>2]|0;if((j|0)>0){cf(q|0,128432,(v=i,i=i+8|0,c[v>>2]=j,v)|0)}else{aI(128592,25,1,q|0)}jJ();i=b;return}function jL(){var b=0,d=0,e=0,f=0.0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}if((a[66164]&1)==0){e=0}else{d=c[m>>2]|0;f=+h[8271];cf(d|0,132416,(v=i,i=i+24|0,c[v>>2]=132288,c[v+8>>2]=56880,h[v+16>>3]=f,v)|0);e=1}if((a[65476]&1)==0){g=e}else{d=c[m>>2]|0;j=e+1|0;k=(e|0)==0?132288:132144;f=+h[8185];cf(d|0,132416,(v=i,i=i+24|0,c[v>>2]=k,c[v+8>>2]=56856,h[v+16>>3]=f,v)|0);g=j}if((a[64788]&1)==0){l=g}else{j=c[m>>2]|0;k=g+1|0;d=(g|0)==0?132288:132144;f=+h[8099];cf(j|0,132416,(v=i,i=i+24|0,c[v>>2]=d,c[v+8>>2]=56832,h[v+16>>3]=f,v)|0);l=k}if((a[68916]&1)==0){n=l}else{k=c[m>>2]|0;d=l+1|0;j=(l|0)==0?132288:132144;f=+h[8615];cf(k|0,132416,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=56976,h[v+16>>3]=f,v)|0);n=d}if((a[68228]&1)==0){o=n}else{d=c[m>>2]|0;j=n+1|0;k=(n|0)==0?132288:132144;f=+h[8529];cf(d|0,132416,(v=i,i=i+24|0,c[v>>2]=k,c[v+8>>2]=56952,h[v+16>>3]=f,v)|0);o=j}if((a[66852]&1)==0){p=o}else{j=c[m>>2]|0;k=o+1|0;d=(o|0)==0?132288:132144;f=+h[8357];cf(j|0,132416,(v=i,i=i+24|0,c[v>>2]=d,c[v+8>>2]=56904,h[v+16>>3]=f,v)|0);p=k}if((a[69604]&1)==0){q=p}else{k=c[m>>2]|0;d=p+1|0;j=(p|0)==0?132288:132144;f=+h[8701];cf(k|0,132416,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=57e3,h[v+16>>3]=f,v)|0);q=d}if((q|0)==0){d=c[m>>2]|0;aI(132064,15,1,d|0);i=b;return}d=c[m>>2]|0;if((q|0)==1){aI(131968,6,1,d|0);i=b;return}else{aF(10,d|0);i=b;return}}function jM(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0,u=0,w=0.0,x=0,y=0.0,z=0,A=0,B=0;d=i;e=c[10822]|0;if((e|0)==0){f=0}else{g=(b|0)==0;j=0;k=e;while(1){e=c[k+4>>2]|0;if(g){l=e;n=7722}else{if((e|0)==(b|0)){l=b;n=7722}else{o=j}}if((n|0)==7722){n=0;e=c[m>>2]|0;p=c[k+60>>2]|0;if((p|0)==0){q=179864}else{q=j3(p)|0}cf(e|0,126584,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=q,v)|0);e=c[m>>2]|0;p=c[k+8>>2]|0;if((p|0)==0){r=179864}else{r=c[21784+(p<<2)>>2]|0}s=+h[k+24>>3];t=c[k+12>>2]|0;if((t|0)==(p|0)){u=179864}else{u=c[21784+(t<<2)>>2]|0}w=+h[k+32>>3];p=c[k+16>>2]|0;if((p|0)==(t|0)){x=179864}else{x=c[21784+(p<<2)>>2]|0}y=+h[k+40>>3];cf(e|0,77392,(v=i,i=i+48|0,c[v>>2]=r,h[v+8>>3]=s,c[v+16>>2]=u,h[v+24>>3]=w,c[v+32>>2]=x,h[v+40>>3]=y,v)|0);e=c[k+48>>2]|0;if((e|0)==1){p=c[m>>2]|0;aI(126448,7,1,p|0)}else if((e|0)==2){aI(131280,6,1,c[m>>2]|0)}else if((e|0)==0){aI(131440,5,1,c[m>>2]|0)}e=c[k+52>>2]|0;p=c[m>>2]|0;if((e|0)==0){aI(126264,12,1,p|0)}else{cf(p|0,126328,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}e=c[m>>2]|0;p=(c[k+56>>2]|0)!=0?150856:150704;cf(e|0,126160,(v=i,i=i+8|0,c[v>>2]=p,v)|0);p=c[k+64>>2]|0;if((p|0)!=0){e=c[m>>2]|0;cf(e|0,126104,(v=i,i=i+8|0,c[v>>2]=p,v)|0)}p=k+72|0;if((c[p>>2]|0)!=0){e=c[m>>2]|0;aI(145280,10,1,e|0);jg(e,p)}if((a[k+184|0]&1)!=0){p=c[m>>2]|0;aI(199048,11,1,p|0)}p=k+88|0;e=c[m>>2]|0;if((c[p>>2]|0)==0){aI(126e3,8,1,e|0)}else{aI(125912,20,1,e|0);ji(c[m>>2]|0,p,1);p=c[m>>2]|0;e=c[k+144>>2]|0;if((e|0)==0){z=179864}else{z=c[21784+(e<<2)>>2]|0}y=+h[k+160>>3];t=c[k+148>>2]|0;if((t|0)==(e|0)){A=179864}else{A=c[21784+(t<<2)>>2]|0}w=+h[k+168>>3];e=c[k+152>>2]|0;if((e|0)==(t|0)){B=179864}else{B=c[21784+(e<<2)>>2]|0}s=+h[k+176>>3];cf(p|0,77392,(v=i,i=i+48|0,c[v>>2]=z,h[v+8>>3]=y,c[v+16>>2]=A,h[v+24>>3]=w,c[v+32>>2]=B,h[v+40>>3]=s,v)|0)}p=c[m>>2]|0;aF(10,p|0);o=1}p=c[k>>2]|0;if((p|0)==0){f=o;break}else{j=o;k=p}}}if((b|0)<1|f){i=d;return}else{uf(c[13898]|0,125760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function jN(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0.0,w=0,x=0.0,y=0,z=0.0,A=0,B=0,C=0,D=0;d=i;e=c[10826]|0;if((e|0)==0){f=0}else{g=(b|0)==0;j=0;k=e;while(1){e=c[k+4>>2]|0;if(g){l=e;n=7761}else{if((e|0)==(b|0)){l=b;n=7761}else{o=j}}if((n|0)==7761){n=0;e=c[m>>2]|0;p=c[72304+(c[k+160>>2]<<2)>>2]|0;q=k+200|0;r=c[q>>2]|0;if((r|0)==2){s=128e3}else{s=(r|0)==1?127904:127768}r=(c[k+100>>2]|0)!=0?150856:150704;cf(e|0,128128,(v=i,i=i+32|0,c[v>>2]=l,c[v+8>>2]=p,c[v+16>>2]=s,c[v+24>>2]=r,v)|0);ji(c[m>>2]|0,k+104|0,0);r=c[m>>2]|0;aI(127616,9,1,r|0);r=c[m>>2]|0;p=c[k+8>>2]|0;if((p|0)==0){t=179864}else{t=c[21784+(p<<2)>>2]|0}u=+h[k+24>>3];e=c[k+12>>2]|0;if((e|0)==(p|0)){w=179864}else{w=c[21784+(e<<2)>>2]|0}x=+h[k+32>>3];p=c[k+16>>2]|0;if((p|0)==(e|0)){y=179864}else{y=c[21784+(p<<2)>>2]|0}z=+h[k+40>>3];cf(r|0,77392,(v=i,i=i+48|0,c[v>>2]=t,h[v+8>>3]=u,c[v+16>>2]=w,h[v+24>>3]=x,c[v+32>>2]=y,h[v+40>>3]=z,v)|0);r=(a[k+88|0]&1)!=0?127520:127448;p=c[m>>2]|0;aK(r|0,p|0);p=c[m>>2]|0;r=c[k+48>>2]|0;if((r|0)==0){A=179864}else{A=c[21784+(r<<2)>>2]|0}z=+h[k+64>>3];e=c[k+52>>2]|0;if((e|0)==(r|0)){B=179864}else{B=c[21784+(e<<2)>>2]|0}x=+h[k+72>>3];r=c[k+56>>2]|0;if((r|0)==(e|0)){C=179864}else{C=c[21784+(r<<2)>>2]|0}u=+h[k+80>>3];cf(p|0,77392,(v=i,i=i+48|0,c[v>>2]=A,h[v+8>>3]=z,c[v+16>>2]=B,h[v+24>>3]=x,c[v+32>>2]=C,h[v+40>>3]=u,v)|0);u=+h[k+168>>3];do{if(u>0.0){p=c[m>>2]|0;r=c[k+176>>2]|0;if((r|0)==0){D=179864}else{D=c[21832+(r<<2)>>2]|0}x=+h[k+184>>3];cf(p|0,126992,(v=i,i=i+24|0,c[v>>2]=D,h[v+8>>3]=u,h[v+16>>3]=x,v)|0);if((c[q>>2]|0)==0){break}p=c[m>>2]|0;x=+h[k+192>>3];cf(p|0,126792,(v=i,i=i+8|0,h[v>>3]=x,v)|0)}}while(0);q=c[m>>2]|0;aF(10,q|0);o=1}q=c[k>>2]|0;if((q|0)==0){f=o;break}else{j=o;k=q}}}if((b|0)<1|f){i=d;return}else{uf(c[13898]|0,134376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function jO(){var b=0,d=0,e=0.0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}do{if((c[8786]|0)==3){d=c[m>>2]|0;e=+h[4395];cf(d|0,134456,(v=i,i=i+8|0,h[v>>3]=e,v)|0)}else{e=+h[4395];d=c[m>>2]|0;if(e<0.0){aI(134096,35,1,d|0);break}else{cf(d|0,134296,(v=i,i=i+8|0,h[v>>3]=e,v)|0);break}}}while(0);do{if((c[14100]|0)==3){d=c[m>>2]|0;e=+h[7052];cf(d|0,133952,(v=i,i=i+8|0,h[v>>3]=e,v)|0)}else{e=+h[7052];d=c[m>>2]|0;if(e<0.0){aI(133664,35,1,d|0);break}else{cf(d|0,133816,(v=i,i=i+8|0,h[v>>3]=e,v)|0);break}}}while(0);do{if((c[6598]|0)==3){d=c[m>>2]|0;e=+h[3301];cf(d|0,133536,(v=i,i=i+8|0,h[v>>3]=e,v)|0)}else{e=+h[3301];d=c[m>>2]|0;if(e<0.0){aI(133048,35,1,d|0);break}else{cf(d|0,133360,(v=i,i=i+8|0,h[v>>3]=e,v)|0);break}}}while(0);if((c[1066]|0)==3){d=c[m>>2]|0;e=+h[535];cf(d|0,132872,(v=i,i=i+8|0,h[v>>3]=e,v)|0);i=b;return}e=+h[535];d=c[m>>2]|0;if(e<0.0){aI(132592,35,1,d|0);i=b;return}else{cf(d|0,132720,(v=i,i=i+8|0,h[v>>3]=e,v)|0);i=b;return}}function jP(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0;b=i;c[13898]=(c[13898]|0)+1;cf(c[m>>2]|0,166536,(v=i,i=i+8|0,c[v>>2]=(c[7662]|0)==1?166152:165672,v)|0);aI(165320,17,1,c[m>>2]|0);d=a[30632]|0;if(d<<24>>24!=0){e=0;f=30632;g=d;do{if((e|0)>0){d=c[m>>2]|0;aI(164904,7,1,d|0);h=a[f]|0}else{h=g}d=h<<24>>24;if((d|0)==98){j=c[m>>2]|0;aI(164432,6,1,j|0)}else if((d|0)==115){aI(163992,7,1,c[m>>2]|0)}else if((d|0)==116){aI(163304,3,1,c[m>>2]|0)}e=e+1|0;f=e+30632|0;g=a[f]|0;}while(g<<24>>24!=0)}aF(10,c[m>>2]|0);g=a[30641]|0;do{if(g<<24>>24==100){f=c[m>>2]|0;aI(162824,21,1,f|0)}else{f=c[m>>2]|0;if(g<<24>>24==97){aI(158600,38,1,f|0);break}else{e=g<<24>>24==102?159744:159048;cf(f|0,162192,(v=i,i=i+8|0,c[v>>2]=e,v)|0);break}}}while(0);aI(157984,47,1,c[m>>2]|0);g=a[30639]|0;e=c[m>>2]|0;if(g<<24>>24==99){aI(156096,9,1,e|0)}else{f=g<<24>>24==98?154664:154352;cf(e|0,155016,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}cf(c[m>>2]|0,153960,(v=i,i=i+8|0,c[v>>2]=(a[30640]|0)!=0?179864:211688,v)|0);aI(153600,11,1,c[m>>2]|0);f=c[m>>2]|0;if((a[30642]|0)==49){aI(153160,49,1,f|0)}else{aI(152728,45,1,f|0)}f=c[7661]|0;e=c[m>>2]|0;if((f|0)>0){cf(e|0,152344,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}else{g=(f|0)!=0?150376:150208;cf(e|0,152016,(v=i,i=i+8|0,c[v>>2]=g,v)|0)}g=c[7665]|0;cf(c[m>>2]|0,151768,(v=i,i=i+16|0,c[v>>2]=c[7664],c[v+8>>2]=g,v)|0);aI(151600,31,1,c[m>>2]|0);g=c[7663]|0;if((g|0)==8){e=c[m>>2]|0;aI(150536,21,1,e|0);i=b;return}else if((g|0)==7){aI(150712,21,1,c[m>>2]|0);i=b;return}else if((g|0)==4){aI(151368,19,1,c[m>>2]|0);i=b;return}else if((g|0)==5){aI(151064,30,1,c[m>>2]|0);i=b;return}else if((g|0)==6){aI(150872,20,1,c[m>>2]|0);i=b;return}else{cf(c[m>>2]|0,150400,(v=i,i=i+8|0,c[v>>2]=g+1,v)|0);i=b;return}}function jQ(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0.0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0.0,U=0.0,V=0.0,W=0.0,X=0.0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0.0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0.0,ax=0.0,ay=0.0,az=0,aA=0,aB=0;b=i;i=i+48|0;d=b|0;e=b+24|0;f=c[13898]|0;g=c[8272]|0;do{if((f|0)<(g|0)){j=c[1054]|0;k=(a[j+(f*40&-1)|0]&1)==0;if(k){uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=c[j+(f*40&-1)+36>>2]|0;o=j+(f*40&-1)+32|0;p=c[10036]|0;q=0;while(1){if((q|0)>=(l|0)){r=7852;break}if((a[p+((c[o>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break}}if((r|0)==7852){if((q|0)==1){break}}p=c[o>>2]|0;if(k){uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((l|0)>0){s=c[10036]|0;t=0;u=0;w=p;while(1){if((a[t+205488|0]|0)==(a[s+(t+w|0)|0]|0)){x=w;y=u}else{if((t|0)!=3){break}x=w-1|0;y=1}z=t+1|0;if((z|0)<(y+l|0)){t=z;u=y;w=x}else{r=7882;break}}do{if((r|0)==7882){if((y|0)==0){if(!((t|0)==2|(t|0)==7)){break}}w=f+1|0;c[13898]=w;if((w|0)>=(g|0)){uf(w,138608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L10757:do{if((a[j+(w*40&-1)|0]&1)!=0){u=c[j+(w*40&-1)+36>>2]|0;s=j+(w*40&-1)+32|0;z=c[10036]|0;A=0;while(1){if((A|0)>=(u|0)){break}if((a[z+((c[s>>2]|0)+A|0)|0]|0)==(a[A+103664|0]|0)){A=A+1|0}else{break L10757}}if((A|0)!=1){break}uf(w,138608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);w=is(d)|0;s=c[w>>2]|0;if((s|0)==1){B=+(c[w+8>>2]|0)}else if((s|0)==2){B=+h[w+8>>3]}else if((s|0)==3){B=+uz(c[w+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}w=d|0;if((c[w>>2]|0)==3){uu(c[d+8>>2]|0);c[w>>2]=1}w=~~B;s=(w|0)<2?128:w;w=c[13898]|0;do{if((w|0)<(c[8272]|0)){z=c[1054]|0;if((a[z+(w*40&-1)|0]&1)==0){uf(w,137944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}u=c[z+(w*40&-1)+36>>2]|0;C=z+(w*40&-1)+32|0;z=c[10036]|0;D=0;while(1){if((D|0)>=(u|0)){r=7903;break}E=c[C>>2]|0;if((a[z+(E+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{F=E;break}}if((r|0)==7903){if((D|0)==1){G=0;break}F=c[C>>2]|0}if((u|0)>0){H=0;I=0;J=F}else{uf(w,137944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}while(1){if((a[H+138408|0]|0)==(a[z+(H+J|0)|0]|0)){K=J;L=I}else{if((H|0)!=1){M=0;N=0;O=F;r=7912;break}K=J-1|0;L=1}A=H+1|0;if((A|0)<(L+u|0)){H=A;I=L;J=K}else{r=7910;break}}do{if((r|0)==7910){if((L|0)!=0){P=1;break}if((H|0)==0|(H|0)==5){P=1}else{M=0;N=0;O=F;r=7912}}}while(0);do{if((r|0)==7912){while(1){r=0;if((a[M+138104|0]|0)==(a[z+(M+O|0)|0]|0)){Q=O;R=N}else{if((M|0)!=1){r=8045;break}Q=O-1|0;R=1}C=M+1|0;if((C|0)<(R+u|0)){M=C;N=R;O=Q;r=7912}else{break}}if((r|0)==8045){uf(w,137944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((R|0)!=0){P=2;break}if((M|0)==0|(M|0)==3){P=2;break}uf(w,137944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[13898]=w+1;G=P}else{G=0}}while(0);w=c[7262]|0;u=c[m>>2]|0;if((w|0)==0|(w|0)==(u|0)){S=1}else{S=(w|0)==(c[n>>2]|0)}z=(w|0)!=0?w:u;w=(c[5163]|0)==103?137656:137544;cf(u|0,137800,(v=i,i=i+16|0,c[v>>2]=w,c[v+8>>2]=s,v)|0);w=c[m>>2]|0;if(S){aI(137248,2,1,w|0)}else{u=c[7260]|0;cf(w|0,137360,(v=i,i=i+8|0,c[v>>2]=u,v)|0)}if((s|0)<=0){i=b;return}T=+(s-1|0);u=e|0;w=e+8|0;C=e+16|0;D=0;do{U=+(D|0)/T;if((a[20668]|0)==110){V=1.0-U}else{V=U}fq(V,e);U=+h[u>>3];W=+h[w>>3];X=+h[C>>3];A=~~(U*255.0+.5);E=~~(W*255.0+.5);Y=~~(X*255.0+.5);if((G|0)==1){cf(z|0,137184,(v=i,i=i+24|0,h[v>>3]=U,h[v+8>>3]=W,h[v+16>>3]=X,v)|0)}else if((G|0)==2){cf(z|0,137056,(v=i,i=i+24|0,c[v>>2]=A&255,c[v+8>>2]=E&255,c[v+16>>2]=Y&255,v)|0)}else{Z=A&255;A=E&255;E=Y&255;cf(z|0,138856,(v=i,i=i+88|0,c[v>>2]=D,h[v+8>>3]=V,h[v+16>>3]=U,h[v+24>>3]=W,h[v+32>>3]=X,c[v+40>>2]=Z,c[v+48>>2]=A,c[v+56>>2]=E,c[v+64>>2]=Z,c[v+72>>2]=A,c[v+80>>2]=E,v)|0)}D=D+1|0;}while((D|0)<(s|0));i=b;return}}while(0);if(k){uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}L10836:do{if((l|0)>0){t=c[10036]|0;s=0;D=0;z=p;while(1){if((a[s+142040|0]|0)==(a[t+(s+z|0)|0]|0)){_=z;$=D}else{if((s|0)!=3){break L10836}_=z-1|0;$=1}C=s+1|0;if((C|0)<($+l|0)){s=C;D=$;z=_}else{break}}if(($|0)==0){if(!((s|0)==2|(s|0)==8)){break}}c[13898]=f+1;if((c[5163]|0)!=100){z=c[m>>2]|0;aI(139016,47,1,z|0);i=b;return}if((c[5172]|0)>0){aa=0}else{i=b;return}do{z=c[5173]|0;T=+h[z+(aa<<5)>>3];X=+h[z+(aa<<5)+8>>3];W=+h[z+(aa<<5)+16>>3];U=+h[z+(aa<<5)+24>>3];z=~~(X*255.0+.5);D=~~(W*255.0+.5);t=~~(U*255.0+.5);cf(c[m>>2]|0,138856,(v=i,i=i+88|0,c[v>>2]=aa,h[v+8>>3]=T,h[v+16>>3]=X,h[v+24>>3]=W,h[v+32>>3]=U,c[v+40>>2]=z,c[v+48>>2]=D,c[v+56>>2]=t,c[v+64>>2]=z,c[v+72>>2]=D,c[v+80>>2]=t,v)|0);aa=aa+1|0;}while((aa|0)<(c[5172]|0));i=b;return}}while(0);if(k){uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((l|0)<=0){break}s=c[10036]|0;t=0;D=0;z=p;while(1){if((a[t+141864|0]|0)==(a[s+(t+z|0)|0]|0)){ab=z;ac=D}else{if((t|0)!=6){break}ab=z-1|0;ac=1}C=t+1|0;if((C|0)<(ac+l|0)){t=C;D=ac;z=ab}else{r=7954;break}}do{if((r|0)==7954){if((ac|0)==0){if(!((t|0)==5|(t|0)==11)){break}}z=c[m>>2]|0;D=c[5162]|0;cf(z|0,140040,(v=i,i=i+8|0,c[v>>2]=D,v)|0);if((a[c[7122]|0]|0)!=0){D=0;z=0;do{if(((D|0)%3&-1|0)==0){s=c[m>>2]|0;aI(139832,6,1,s|0)}s=c[28488+((z|1)<<2)>>2]|0;cf(c[m>>2]|0,139592,(v=i,i=i+16|0,c[v>>2]=D,c[v+8>>2]=s,v)|0);D=D+1|0;z=D<<1;}while((a[c[28488+(z<<2)>>2]|0]|0)!=0)}z=c[m>>2]|0;aF(10,z|0);z=c[m>>2]|0;aI(139400,62,1,z|0);z=c[m>>2]|0;D=(c[5162]|0)-1|0;cf(z|0,139168,(v=i,i=i+16|0,c[v>>2]=D,c[v+8>>2]=D,v)|0);c[13898]=(c[13898]|0)+1;i=b;return}}while(0);if(!k){break}uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);j=c[10036]|0;o=0;while(1){if((o|0)>=(l|0)){r=7965;break}if((a[j+(p+o|0)|0]|0)==(a[o+204344|0]|0)){o=o+1|0}else{r=7966;break}}if((r|0)==7965){if((o|0)!=6){r=7966}}do{if((r|0)==7966){if(!((l|0)>0&(k^1))){uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=c[10036]|0;q=0;t=0;D=p;while(1){if((a[q+141544|0]|0)==(a[j+(q+D|0)|0]|0)){ad=D;ae=t}else{if((q|0)!=5){break}ad=D-1|0;ae=1}z=q+1|0;if((z|0)<(ae+l|0)){q=z;t=ae;D=ad}else{r=7972;break}}if((r|0)==7972){if((ae|0)!=0){break}if((q|0)==4|(q|0)==10){break}}if(!((l|0)>0&(k^1))){uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}D=c[10036]|0;t=0;j=0;z=p;while(1){if((a[t+141352|0]|0)==(a[D+(t+z|0)|0]|0)){af=z;ag=j}else{if((t|0)!=7){r=8027;break}af=z-1|0;ag=1}s=t+1|0;if((s|0)<(ag+l|0)){t=s;j=ag;z=af}else{break}}if((r|0)==8027){uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((ag|0)==0){if((t|0)==6|(t|0)==15){break}uf(f,141144,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);t=(c[5162]|0)-1|0;c[13898]=f+1;if((c[5163]|0)==114&(c[5174]|0)==114){z=c[m>>2]|0;j=c[5164]|0;D=c[5165]|0;q=c[5166]|0;cf(z|0,141008,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=D,c[v+16>>2]=q,v)|0);i=b;return}q=ut(768)|0;do{if((q|0)==0){gk();D=ut(768)|0;if((D|0)!=0){ah=D;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=140872,v)|0)}else{ah=q}}while(0);q=ah;D=0;do{fq(+(D|0)/31.0,q+(D*24&-1)|0);D=D+1|0;}while((D|0)<32);D=t<<1|1;j=D<<2;z=ut(j)|0;do{if((z|0)==0){gk();s=ut(j)|0;if((s|0)!=0){ai=s;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=140736,v)|0)}else{ai=z}}while(0);z=ai;do{if((t|0)>=0){s=0;do{c[z+(s<<2)>>2]=s;s=s+1|0;}while((s|0)<=(t|0));if((t|0)<1){break}else{aj=1}do{c[z+(aj+t<<2)>>2]=-aj;aj=aj+1|0;}while((aj|0)<=(t|0))}}while(0);t=ut(j)|0;do{if((t|0)==0){gk();s=ut(j)|0;if((s|0)!=0){ak=s;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=140536,v)|0)}else{ak=t}}while(0);t=ak;if((D|0)>0){j=0;while(1){s=ut(256)|0;if((s|0)==0){gk();C=ut(256)|0;if((C|0)==0){r=8003;break}else{al=C}}else{al=s}s=t+(j<<2)|0;c[s>>2]=al;C=z+(j<<2)|0;w=0;do{U=+fu(c[C>>2]|0,+(w|0)/31.0);h[(c[s>>2]|0)+(w<<3)>>3]=U;w=w+1|0;}while((w|0)<32);w=j+1|0;if((w|0)<(D|0)){j=w}else{am=0;an=0;ao=0;ap=8.988465674311579e+307;aq=0;break}}if((r|0)==8003){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=140344,v)|0)}while(1){j=z+(aq<<2)|0;w=c[t+(aq<<2)>>2]|0;s=am;C=an;u=ao;U=ap;E=0;while(1){A=c[t+(E<<2)>>2]|0;Z=z+(E<<2)|0;Y=s;ar=C;as=u;W=U;at=0;while(1){au=c[t+(at<<2)>>2]|0;X=0.0;av=0;do{T=+h[q+(av*24&-1)>>3]- +h[w+(av<<3)>>3];aw=+h[q+(av*24&-1)+8>>3]- +h[A+(av<<3)>>3];ax=+h[q+(av*24&-1)+16>>3]- +h[au+(av<<3)>>3];X=X+(T*T+aw*aw+ax*ax);av=av+1|0;}while((av|0)<32);if(X<W){ay=X;az=c[j>>2]|0;aA=c[Z>>2]|0;aB=c[z+(at<<2)>>2]|0}else{ay=W;az=as;aA=ar;aB=Y}av=at+1|0;if((av|0)<(D|0)){Y=aB;ar=aA;as=az;W=ay;at=av}else{break}}at=E+1|0;if((at|0)<(D|0)){s=aB;C=aA;u=az;U=ay;E=at}else{break}}E=aq+1|0;if((E|0)<(D|0)){am=aB;an=aA;ao=az;ap=ay;aq=E}else{break}}cf(c[m>>2]|0,140160,(v=i,i=i+24|0,c[v>>2]=az,c[v+8>>2]=aA,c[v+16>>2]=aB,v)|0);z=0;do{uu(c[t+(z<<2)>>2]|0);z=z+1|0;}while((z|0)<(D|0))}else{cf(c[m>>2]|0,140160,(v=i,i=i+24|0,c[v>>2]=0,c[v+8>>2]=0,c[v+16>>2]=0,v)|0)}uu(ak);uu(ai);uu(ah);i=b;return}}while(0);jR();i=b;return}}while(0);cf(c[m>>2]|0,147560,(v=i,i=i+8|0,c[v>>2]=(c[5163]|0)==103?147288:147032,v)|0);ah=c[5163]|0;do{if((ah|0)==100){ai=c[m>>2]|0;aI(146576,35,1,ai|0)}else if((ah|0)==114){ai=c[5165]|0;ak=c[5166]|0;cf(c[m>>2]|0,146752,(v=i,i=i+24|0,c[v>>2]=c[5164],c[v+8>>2]=ai,c[v+16>>2]=ak,v)|0)}else if((ah|0)==102){aI(146384,49,1,c[m>>2]|0);ak=c[5179]|0;if((c[5178]|0)!=0&(ak|0)!=0){ai=c[m>>2]|0;cf(ai|0,146224,(v=i,i=i+8|0,c[v>>2]=ak,v)|0)}ak=c[5257]|0;if((c[5256]|0)!=0&(ak|0)!=0){ai=c[m>>2]|0;cf(ai|0,146056,(v=i,i=i+8|0,c[v>>2]=ak,v)|0)}ak=c[5335]|0;if(!((c[5334]|0)!=0&(ak|0)!=0)){break}cf(c[m>>2]|0,145904,(v=i,i=i+8|0,c[v>>2]=ak,v)|0)}else if((ah|0)==99){ay=+h[2707];ap=+h[2708];cf(c[m>>2]|0,145624,(v=i,i=i+24|0,h[v>>3]=+h[2706],h[v+8>>3]=ay,h[v+16>>3]=ap,v)|0)}else if((ah|0)!=103){cf(c[m>>2]|0,145456,(v=i,i=i+24|0,c[v>>2]=145192,c[v+8>>2]=2321,c[v+16>>2]=ah<<24>>24,v)|0)}}while(0);cf(c[m>>2]|0,144904,(v=i,i=i+8|0,c[v>>2]=(a[20668]|0)==112?144696:144440,v)|0);cf(c[m>>2]|0,144216,(v=i,i=i+8|0,c[v>>2]=(a[20684]|0)==0?144064:179864,v)|0);aI(143944,12,1,c[m>>2]|0);ah=c[5168]|0;ak=c[m>>2]|0;if((ah|0)==0){aI(143728,13,1,ak|0)}else{cf(ak|0,143880,(v=i,i=i+8|0,c[v>>2]=ah,v)|0)}aI(143544,48,1,c[m>>2]|0);aI(143376,14,1,c[m>>2]|0);ah=c[5174]|0;if((ah|0)==114){ak=c[m>>2]|0;aI(143256,4,1,ak|0)}else if((ah|0)==120){aI(142552,4,1,c[m>>2]|0)}else if((ah|0)==99){aI(142912,4,1,c[m>>2]|0)}else if((ah|0)==104){aI(143048,4,1,c[m>>2]|0)}else if((ah|0)==121){aI(142680,4,1,c[m>>2]|0)}else{cf(c[m>>2]|0,142392,(v=i,i=i+24|0,c[v>>2]=145192,c[v+8>>2]=2343,c[v+16>>2]=ah<<24>>24,v)|0)}cf(c[m>>2]|0,142264,(v=i,i=i+8|0,h[v>>3]=+h[2705],v)|0);i=b;return}function jR(){var a=0,b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0;a=i;cf(c[m>>2]|0,136824,(v=i,i=i+8|0,c[v>>2]=112,v)|0);b=c[7642]|0;d=c[b>>2]|0;if((d|0)==0){e=c[m>>2]|0;f=aF(10,e|0)|0;g=c[13898]|0;h=g+1|0;c[13898]=h;i=a;return}else{j=b;k=d}do{d=c[j+4>>2]|0;b=d>>>16&255;l=d>>>8&255;n=d&255;cf(c[m>>2]|0,136440,(v=i,i=i+8|0,c[v>>2]=k,v)|0);cf(c[m>>2]|0,136240,(v=i,i=i+48|0,c[v>>2]=b,c[v+8>>2]=l,c[v+16>>2]=n,c[v+24>>2]=b,c[v+32>>2]=l,c[v+40>>2]=n,v)|0);j=j+8|0;k=c[j>>2]|0;}while((k|0)!=0);e=c[m>>2]|0;f=aF(10,e|0)|0;g=c[13898]|0;h=g+1|0;c[13898]=h;i=a;return}function jS(){var b=0,d=0,e=0,f=0,g=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}d=c[8270]|0;do{if((d|0)!=0){if((aY(d|0,139696)|0)==0){break}bA(4,d|0)}}while(0);d=c[m>>2]|0;e=c[b$()>>2]|0;cf(d|0,170016,(v=i,i=i+8|0,c[v>>2]=e,v)|0);e=c[8270]|0;do{if((e|0)!=0){if((aY(e|0,139696)|0)==0){break}bA(4,139696)}}while(0);e=c[12886]|0;d=c[m>>2]|0;if((e|0)==0){aI(169e3,57,1,d|0);f=c[m>>2]|0;g=cf(f|0,168632,(v=i,i=i+8|0,c[v>>2]=48904,v)|0)|0;i=b;return}else{cf(d|0,169544,(v=i,i=i+8|0,c[v>>2]=e,v)|0);f=c[m>>2]|0;g=cf(f|0,168632,(v=i,i=i+8|0,c[v>>2]=48904,v)|0)|0;i=b;return}}function jT(){var b=0,d=0,e=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}cf(c[m>>2]|0,171816,(v=i,i=i+8|0,c[v>>2]=(a[42568]&1)!=0?179864:171496,v)|0);d=c[10568]|0;if((d|0)!=0){e=c[m>>2]|0;cf(e|0,171184,(v=i,i=i+8|0,c[v>>2]=d,v)|0);i=b;return}d=fg()|0;if((d|0)==0){i=b;return}cf(c[m>>2]|0,170744,(v=i,i=i+8|0,c[v>>2]=d,v)|0);uu(d);i=b;return}function jU(){var b=0,d=0,e=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}aI(135832,29,1,c[m>>2]|0);d=c[57238]|0;cf(c[m>>2]|0,154696,(v=i,i=i+8|0,c[v>>2]=(d|0)!=0?d:135560,v)|0);aI(135336,38,1,c[m>>2]|0);d=c[m>>2]|0;e=bU(195768)|0;cf(d|0,154696,(v=i,i=i+8|0,c[v>>2]=(e|0)==0?135560:e,v)|0);cf(c[m>>2]|0,135072,(v=i,i=i+8|0,c[v>>2]=195896,v)|0);i=b;return}function jV(){var b=0,d=0,e=0.0,f=0.0,j=0.0,k=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}aI(178128,9,1,c[m>>2]|0);d=c[m>>2]|0;if((c[5094]|0)==1){aI(177880,4,1,d|0);i=b;return}e=+g[3536];f=+g[3534];j=+g[3532];cf(d|0,177528,(v=i,i=i+32|0,h[v>>3]=+g[3538],h[v+8>>3]=e,h[v+16>>3]=f,h[v+24>>3]=j,v)|0);d=c[18072]|0;if((d|0)==2){k=176720}else{k=(d|0)==3?176120:179864}cf(c[m>>2]|0,177040,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=(d|0)>1?175728:175376,v)|0);i=b;return}function jW(){var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0,n=0.0,o=0,p=0.0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0;b=i;c[13898]=(c[13898]|0)+1;d=c[m>>2]|0;do{if((a[54018]|0)==0){aI(149704,29,1,d|0)}else{aI(150216,24,1,d|0);e=c[13505]|0;f=c[m>>2]|0;if((e|0)>-1){cf(f|0,150056,(v=i,i=i+8|0,c[v>>2]=e,v)|0);break}else{aI(149856,21,1,f|0);break}}}while(0);do{if((a[54016]|0)!=110){d=c[m>>2]|0;if((c[13506]|0)==1){aI(149576,13,1,d|0);break}else{aI(149312,12,1,d|0);break}}}while(0);d=a[54016]|0;if((d|0)==117){f=c[m>>2]|0;aI(148704,16,1,f|0);f=c[m>>2]|0;e=c[13508]|0;if((e|0)==0){g=179864}else{g=c[21784+(e<<2)>>2]|0}j=+h[6756];k=c[13509]|0;if((k|0)==(e|0)){l=179864}else{l=c[21784+(k<<2)>>2]|0}n=+h[6757];e=c[13510]|0;if((e|0)==(k|0)){o=179864}else{o=c[21784+(e<<2)>>2]|0}p=+h[6758];cf(f|0,77392,(v=i,i=i+48|0,c[v>>2]=g,h[v+8>>3]=j,c[v+16>>2]=l,h[v+24>>3]=n,c[v+32>>2]=o,h[v+40>>3]=p,v)|0);o=c[m>>2]|0;aI(148472,18,1,o|0);o=c[m>>2]|0;l=c[13518]|0;if((l|0)==0){q=179864}else{q=c[21784+(l<<2)>>2]|0}p=+h[6761];g=c[13519]|0;if((g|0)==(l|0)){r=179864}else{r=c[21784+(g<<2)>>2]|0}n=+h[6762];l=c[13520]|0;if((l|0)==(g|0)){s=179864}else{s=c[21784+(l<<2)>>2]|0}j=+h[6763];cf(o|0,77392,(v=i,i=i+48|0,c[v>>2]=q,h[v+8>>3]=p,c[v+16>>2]=r,h[v+24>>3]=n,c[v+32>>2]=s,h[v+40>>3]=j,v)|0);s=c[m>>2]|0;aF(10,s|0);t=c[m>>2]|0;u=a[54017]|0;w=u<<24>>24==118;x=w?148008:147816;y=cf(t|0,148168,(v=i,i=i+8|0,c[v>>2]=x,v)|0)|0;i=b;return}else if((d|0)==100){aI(148856,20,1,c[m>>2]|0);t=c[m>>2]|0;u=a[54017]|0;w=u<<24>>24==118;x=w?148008:147816;y=cf(t|0,148168,(v=i,i=i+8|0,c[v>>2]=x,v)|0)|0;i=b;return}else if((d|0)==110){aI(149024,10,1,c[m>>2]|0);t=c[m>>2]|0;u=a[54017]|0;w=u<<24>>24==118;x=w?148008:147816;y=cf(t|0,148168,(v=i,i=i+8|0,c[v>>2]=x,v)|0)|0;i=b;return}else{uf(-1,148400,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function jX(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0,D=0,E=0,F=0,G=0,H=0,I=0.0,J=0,K=0,L=0,M=0.0,N=0,O=0,P=0;b=i;i=i+48|0;d=b|0;e=b+24|0;f=c[13898]|0;g=c[1054]|0;j=c[g+(f*40&-1)+36>>2]|0;k=c[g+(f*40&-1)+32>>2]|0;l=(a[g+(f*40&-1)|0]&1)==0;n=(j|0)>0;o=c[10036]|0;p=21680;q=116616;L11099:while(1){L11101:do{if(!l){if(n){r=0;s=0;t=k;while(1){u=a[q+r|0]|0;if(u<<24>>24==(a[o+(r+t|0)|0]|0)){w=t;x=s}else{if(u<<24>>24!=36){break L11101}w=t-1|0;x=1}y=r+1|0;if((y|0)<(x+j|0)){r=y;s=x;t=w}else{break}}if((x|0)==0){z=y}else{A=p;break L11099}}else{z=0}t=a[q+z|0]|0;if((t<<24>>24|0)==36|(t<<24>>24|0)==0){A=p;break L11099}}}while(0);t=p+8|0;s=c[t>>2]|0;if((s|0)==0){A=t;break}else{p=t;q=s}}q=c[A+4>>2]|0;if((q|0)==1){if(!(a[928]|0)){A=c[m>>2]|0;aF(10,A|0)}A=c[12890]|0;p=c[m>>2]|0;cf(p|0,114608,(v=i,i=i+8|0,c[v>>2]=122960,v)|0);jh(c[m>>2]|0,122960,A);c[13898]=(c[13898]|0)+1;i=b;return}else if((q|0)==2){if(!(a[928]|0)){A=c[m>>2]|0;aF(10,A|0)}A=c[10058]|0;cf(c[m>>2]|0,114608,(v=i,i=i+8|0,c[v>>2]=122816,v)|0);jh(c[m>>2]|0,122816,A);c[13898]=(c[13898]|0)+1;i=b;return}else if((q|0)==4){j7();c[13898]=(c[13898]|0)+1;i=b;return}else if((q|0)==9){aI(115400,27,1,c[m>>2]|0);A=c[m>>2]|0;if((a[30080]&1)==0){aI(115272,18,1,A|0)}else{aI(115320,56,1,A|0)}c[13898]=(c[13898]|0)+1;i=b;return}else if((q|0)==3){A=f+1|0;c[13898]=A;L11134:do{if((A|0)<(c[8272]|0)){L11136:do{if((a[g+(A*40&-1)|0]&1)!=0){p=c[g+(A*40&-1)+36>>2]|0;z=g+(A*40&-1)+32|0;y=0;while(1){if((y|0)>=(p|0)){break}if((a[o+((c[z>>2]|0)+y|0)|0]|0)==(a[y+103664|0]|0)){y=y+1|0}else{break L11136}}if((y|0)==1){B=0;break L11134}}}while(0);z=is(e)|0;p=c[z>>2]|0;if((p|0)==1){C=+(c[z+8>>2]|0)}else if((p|0)==3){C=+uz(c[z+8>>2]|0,0)}else if((p|0)==2){C=+h[z+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}z=e|0;if((c[z>>2]|0)==3){uu(c[e+8>>2]|0);c[z>>2]=1}z=~~C;if((z|0)>=1){B=z;break}uf(c[13898]|0,208936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{B=0}}while(0);e=c[10820]|0;if((e|0)==0){D=0}else{A=(B|0)==0;z=0;p=e;while(1){e=c[p+4>>2]|0;if(A){E=e;F=8155}else{if((e|0)==(B|0)){E=B;F=8155}else{G=z}}if((F|0)==8155){F=0;e=c[m>>2]|0;cf(e|0,75168,(v=i,i=i+8|0,c[v>>2]=E,v)|0);ji(c[m>>2]|0,p+8|0,1);e=c[m>>2]|0;aF(10,e|0);G=1}e=c[p>>2]|0;if((e|0)==0){D=G;break}else{z=G;p=e}}}if((B|0)<1|D){i=b;return}else{uf(c[13898]|0,74736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else if((q|0)==8){ka();c[13898]=(c[13898]|0)+1;i=b;return}else if((q|0)==7){kb();c[13898]=(c[13898]|0)+1;i=b;return}else if((q|0)==11){j9();c[13898]=(c[13898]|0)+1;i=b;return}else if((q|0)==10){j8();c[13898]=(c[13898]|0)+1;i=b;return}else if((q|0)==5){D=f+1|0;c[13898]=D;L11198:do{if((D|0)<(c[8272]|0)){L11200:do{if((a[g+(D*40&-1)|0]&1)!=0){f=c[g+(D*40&-1)+36>>2]|0;B=g+(D*40&-1)+32|0;p=0;while(1){if((p|0)>=(f|0)){break}if((a[o+((c[B>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{break L11200}}if((p|0)==1){H=0;break L11198}}}while(0);B=is(d)|0;f=c[B>>2]|0;if((f|0)==2){I=+h[B+8>>3]}else if((f|0)==3){I=+uz(c[B+8>>2]|0,0)}else if((f|0)==1){I=+(c[B+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}B=d|0;if((c[B>>2]|0)==3){uu(c[d+8>>2]|0);c[B>>2]=1}B=~~I;if((B|0)>=1){H=B;break}uf(c[13898]|0,208936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{H=0}}while(0);kd(H);i=b;return}else if((q|0)==6){if(!(a[928]|0)){q=c[m>>2]|0;aF(10,q|0)}aI(122192,33,1,c[m>>2]|0);q=c[12738]|0;if((q|0)==0){J=179864}else{J=c[21784+(q<<2)>>2]|0}I=+h[6371];H=c[12739]|0;if((H|0)==(q|0)){K=179864}else{K=c[21784+(H<<2)>>2]|0}C=+h[6372];q=c[12740]|0;if((q|0)==(H|0)){L=179864}else{L=c[21784+(q<<2)>>2]|0}M=+h[6373];cf(c[m>>2]|0,77392,(v=i,i=i+48|0,c[v>>2]=J,h[v+8>>3]=I,c[v+16>>2]=K,h[v+24>>3]=C,c[v+32>>2]=L,h[v+40>>3]=M,v)|0);aF(10,c[m>>2]|0);c[13898]=(c[13898]|0)+1;i=b;return}else{L=c[12890]|0;cf(c[m>>2]|0,114608,(v=i,i=i+8|0,c[v>>2]=122960,v)|0);jh(c[m>>2]|0,122960,L);L=c[10058]|0;cf(c[m>>2]|0,114608,(v=i,i=i+8|0,c[v>>2]=122816,v)|0);jh(c[m>>2]|0,122816,L);L=c[10820]|0;if((L|0)!=0){K=L;do{cf(c[m>>2]|0,75168,(v=i,i=i+8|0,c[v>>2]=c[K+4>>2],v)|0);ji(c[m>>2]|0,K+8|0,1);aF(10,c[m>>2]|0);K=c[K>>2]|0;}while((K|0)!=0)}j7();aI(115400,27,1,c[m>>2]|0);K=c[m>>2]|0;if((a[30080]&1)==0){aI(115272,18,1,K|0)}else{aI(115320,56,1,K|0)}j8();kd(0);j9();ka();if(!(a[928]|0)){K=c[m>>2]|0;aF(10,K|0)}aI(122192,33,1,c[m>>2]|0);K=c[12738]|0;if((K|0)==0){N=179864}else{N=c[21784+(K<<2)>>2]|0}M=+h[6371];L=c[12739]|0;if((L|0)==(K|0)){O=179864}else{O=c[21784+(L<<2)>>2]|0}C=+h[6372];K=c[12740]|0;if((K|0)==(L|0)){P=179864}else{P=c[21784+(K<<2)>>2]|0}I=+h[6373];cf(c[m>>2]|0,77392,(v=i,i=i+48|0,c[v>>2]=N,h[v+8>>3]=M,c[v+16>>2]=O,h[v+24>>3]=C,c[v+32>>2]=P,h[v+40>>3]=I,v)|0);aF(10,c[m>>2]|0);kb();i=b;return}}function jY(){var b=0,d=0,e=0.0,f=0.0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}e=+g[38];cf(c[m>>2]|0,182560,(v=i,i=i+16|0,h[v>>3]=+g[178],h[v+8>>3]=e,v)|0);e=+g[18074];if(e>0.0){d=c[m>>2]|0;f=e;cf(d|0,181056,(v=i,i=i+8|0,h[v>>3]=f,v)|0);i=b;return}d=c[m>>2]|0;if(e==0.0){aI(180712,36,1,d|0);i=b;return}else{f=-0.0-e;cf(d|0,180280,(v=i,i=i+8|0,h[v>>3]=f,v)|0);i=b;return}}function jZ(b,d,e,f,g,j){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;j=j|0;var k=0,l=0,n=0.0,o=0,p=0;k=i;if(!(a[928]|0)){l=c[m>>2]|0;aF(10,l|0)}l=c[m>>2]|0;n=+h[77];if((a[624]&1)==0){cf(l|0,185536,(v=i,i=i+8|0,h[v>>3]=n,v)|0)}else{cf(l|0,186024,(v=i,i=i+8|0,h[v>>3]=n,v)|0)}l=c[10026]|0;if((l|0)>-1){o=c[m>>2]|0;p=(l|0)==0?150704:150856;cf(o|0,185040,(v=i,i=i+8|0,c[v>>2]=p,v)|0)}if(b){j4(2)}if(f){j4(6)}if(d){j4(1)}if(g){j4(5)}if(e){j4(0)}if(!j){a[25280]=0;i=k;return}j4(3);a[25280]=0;i=k;return}function j_(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}d=c[13898]|0;e=c[1054]|0;f=c[e+(d*40&-1)+36>>2]|0;g=c[e+(d*40&-1)+32>>2]|0;h=(a[e+(d*40&-1)|0]&1)==0;e=(f|0)>0;j=c[10036]|0;k=56720;l=77416;L11280:while(1){L11282:do{if(!h){if(e){n=0;o=0;p=g;while(1){q=a[l+n|0]|0;if(q<<24>>24==(a[j+(n+p|0)|0]|0)){r=p;s=o}else{if(q<<24>>24!=36){break L11282}r=p-1|0;s=1}t=n+1|0;if((t|0)<(s+f|0)){n=t;o=s;p=r}else{break}}if((s|0)==0){u=t}else{w=k;break L11280}}else{u=0}p=a[l+u|0]|0;if((p<<24>>24|0)==36|(p<<24>>24|0)==0){w=k;break L11280}}}while(0);p=k+8|0;o=c[p>>2]|0;if((o|0)==0){w=p;break}else{k=p;l=o}}l=c[w+4>>2]|0;if((l|0)>-1){c[13898]=d+1;d=c[m>>2]|0;w=56832+(l*24&-1)|0;k=j3(64813+(l*688&-1)|0)|0;cf(d|0,203592,(v=i,i=i+16|0,c[v>>2]=w,c[v+8>>2]=k,v)|0);i=b;return}else{x=0}do{if((c[64808+(x*688&-1)>>2]|0)==1){k=c[m>>2]|0;w=56832+(x*24&-1)|0;d=j3(64813+(x*688&-1)|0)|0;cf(k|0,203592,(v=i,i=i+16|0,c[v>>2]=w,c[v+8>>2]=d,v)|0)}x=x+1|0;}while((x|0)<11);i=b;return}function j$(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;b=i;d=c[65040+(a*688&-1)>>2]|0;if((d|0)==2){e=c[m>>2]|0;f=56832+(a*24&-1)|0;g=~~+h[65048+(a*688&-1)>>3];cf(e|0,187104,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=g,v)|0);i=b;return}else if((d|0)==1){g=56832+(a*24&-1)|0;cf(c[m>>2]|0,191400,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=b;return}else if((d|0)==0){cf(c[m>>2]|0,193440,(v=i,i=i+8|0,c[v>>2]=56832+(a*24&-1),v)|0);i=b;return}else if((d|0)==3){cf(c[m>>2]|0,188528,(v=i,i=i+8|0,c[v>>2]=56832+(a*24&-1),v)|0);i=b;return}else{uf(-1,186648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function j0(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}d=c[13898]|0;L11319:do{if((d|0)<(c[8272]|0)){e=c[1054]|0;f=(a[e+(d*40&-1)|0]&1)==0;if(f){break}g=c[e+(d*40&-1)+36>>2]|0;h=e+(d*40&-1)+32|0;e=c[10036]|0;j=0;while(1){if((j|0)>=(g|0)){k=8291;break}if((a[e+((c[h>>2]|0)+j|0)|0]|0)==(a[j+103664|0]|0)){j=j+1|0}else{break}}if((k|0)==8291){if((j|0)==1){k=8300;break}}if(!((g|0)>0&(f^1))){break}e=c[10036]|0;l=0;n=0;o=c[h>>2]|0;while(1){if((a[l+210832|0]|0)==(a[e+(l+o|0)|0]|0)){p=o;q=n}else{if((l|0)!=4){break L11319}p=o-1|0;q=1}r=l+1|0;if((r|0)<(q+g|0)){l=r;n=q;o=p}else{break}}if((q|0)!=0){k=8300;break}if((l|0)==3|(l|0)==7){k=8300}}else{k=8300}}while(0);do{if((k|0)==8300){q=c[8528]|0;p=c[m>>2]|0;if((q|0)==0){aI(210088,41,1,p|0);break}else{cf(p|0,209400,(v=i,i=i+8|0,c[v>>2]=q,v)|0);break}}}while(0);q=c[13898]|0;L11343:do{if((q|0)<(c[8272]|0)){p=c[1054]|0;d=(a[p+(q*40&-1)|0]&1)==0;if(d){break}o=c[p+(q*40&-1)+36>>2]|0;n=p+(q*40&-1)+32|0;p=c[10036]|0;g=0;while(1){if((g|0)>=(o|0)){k=8308;break}if((a[p+((c[n>>2]|0)+g|0)|0]|0)==(a[g+103664|0]|0)){g=g+1|0}else{break}}if((k|0)==8308){if((g|0)==1){k=8317;break}}if(!((o|0)>0&(d^1))){break}p=c[10036]|0;l=0;e=0;h=c[n>>2]|0;while(1){if((a[l+209008|0]|0)==(a[p+(l+h|0)|0]|0)){s=h;t=e}else{if((l|0)!=3){break L11343}s=h-1|0;t=1}f=l+1|0;if((f|0)<(t+o|0)){l=f;e=t;h=s}else{break}}if((t|0)!=0){k=8317;break}if((l|0)==2|(l|0)==9){k=8317}}else{k=8317}}while(0);do{if((k|0)==8317){t=a[47120]|0;s=c[m>>2]|0;if(t<<24>>24==0){aI(207840,41,1,s|0);break}else{q=t<<24>>24;cf(s|0,208456,(v=i,i=i+8|0,c[v>>2]=q,v)|0);break}}}while(0);q=c[13898]|0;L11367:do{if((q|0)<(c[8272]|0)){s=c[1054]|0;t=(a[s+(q*40&-1)|0]&1)==0;if(t){break}h=c[s+(q*40&-1)+36>>2]|0;e=s+(q*40&-1)+32|0;s=c[10036]|0;o=0;while(1){if((o|0)>=(h|0)){k=8325;break}if((a[s+((c[e>>2]|0)+o|0)|0]|0)==(a[o+103664|0]|0)){o=o+1|0}else{break}}if((k|0)==8325){if((o|0)==1){k=8334;break}}if(!((h|0)>0&(t^1))){break}s=c[10036]|0;l=0;p=0;n=c[e>>2]|0;while(1){if((a[l+207024|0]|0)==(a[s+(l+n|0)|0]|0)){u=n;w=p}else{if((l|0)!=3){break L11367}u=n-1|0;w=1}d=l+1|0;if((d|0)<(w+h|0)){l=d;p=w;n=u}else{break}}if((w|0)!=0){k=8334;break}if((l|0)==2|(l|0)==8){k=8334}}else{k=8334}}while(0);if((k|0)==8334){w=c[m>>2]|0;u=c[11948]|0;cf(w|0,206416,(v=i,i=i+8|0,c[v>>2]=u,v)|0)}if((a[47712]&1)!=0){u=c[m>>2]|0;aI(205568,55,1,u|0)}if((a[47472]&1)!=0){u=c[m>>2]|0;aI(204904,55,1,u|0)}u=c[13898]|0;w=c[1054]|0;q=c[w+(u*40&-1)+36>>2]|0;n=c[w+(u*40&-1)+32>>2]|0;L11394:do{if((a[w+(u*40&-1)|0]&1)!=0&(q|0)>0){p=c[10036]|0;h=0;s=0;e=n;while(1){if((a[h+204608|0]|0)==(a[p+(h+e|0)|0]|0)){x=e;y=s}else{if((h|0)!=3){z=u;break L11394}x=e-1|0;y=1}t=h+1|0;if((t|0)<(y+q|0)){h=t;s=y;e=x}else{break}}if((y|0)==0){if(!((h|0)==2|(h|0)==6)){z=u;break}}e=c[8272]|0;do{if((u|0)<(e|0)){s=c[10036]|0;p=0;while(1){if((p|0)>=(q|0)){k=8351;break}if((a[s+(n+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{break}}if((k|0)==8351){if((p|0)==1){A=u;break}}s=u+1|0;c[13898]=s;A=s}else{A=u}}while(0);L11415:do{if((A|0)<(e|0)){if((a[w+(A*40&-1)|0]&1)==0){B=A;C=e;break}h=c[w+(A*40&-1)+36>>2]|0;s=w+(A*40&-1)+32|0;l=c[10036]|0;t=0;while(1){if((t|0)>=(h|0)){break}if((a[l+((c[s>>2]|0)+t|0)|0]|0)==(a[t+103664|0]|0)){t=t+1|0}else{B=A;C=e;break L11415}}if((t|0)==1){k=8359}else{B=A;C=e}}else{k=8359}}while(0);if((k|0)==8359){eN(c[m>>2]|0);e=c[m>>2]|0;aF(10,e|0);B=c[13898]|0;C=c[8272]|0}L11425:do{if((B|0)<(C|0)){e=c[1054]|0;s=(a[e+(B*40&-1)|0]&1)==0;if(s){D=B;E=C;break}l=c[e+(B*40&-1)+36>>2]|0;h=e+(B*40&-1)+32|0;e=c[10036]|0;p=0;while(1){if((p|0)>=(l|0)){k=8365;break}if((a[e+((c[h>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{break}}if((k|0)==8365){if((p|0)==1){k=8374;break}}if(!((l|0)>0&(s^1))){D=B;E=C;break}e=c[10036]|0;t=0;o=0;d=c[h>>2]|0;while(1){if((a[t+204360|0]|0)==(a[e+(t+d|0)|0]|0)){F=d;G=o}else{if((t|0)!=5){D=B;E=C;break L11425}F=d-1|0;G=1}g=t+1|0;if((g|0)<(G+l|0)){t=g;o=G;d=F}else{break}}if((G|0)!=0){k=8374;break}if((t|0)==4|(t|0)==9){k=8374}else{D=B;E=C}}else{k=8374}}while(0);if((k|0)==8374){eO(c[m>>2]|0);D=c[13898]|0;E=c[8272]|0}L11446:do{if((D|0)<(E|0)){d=c[1054]|0;if((a[d+(D*40&-1)|0]&1)==0){H=D;I=E;break}o=c[d+(D*40&-1)+36>>2]|0;l=d+(D*40&-1)+32|0;d=c[10036]|0;e=0;while(1){if((e|0)>=(o|0)){break}if((a[d+((c[l>>2]|0)+e|0)|0]|0)==(a[e+103664|0]|0)){e=e+1|0}else{H=D;I=E;break L11446}}if((e|0)==1){k=8381}else{H=D;I=E}}else{k=8381}}while(0);if((k|0)==8381){l=c[m>>2]|0;aF(10,l|0);H=c[13898]|0;I=c[8272]|0}do{if((H|0)<(I|0)){l=c[1054]|0;d=(a[l+(H*40&-1)|0]&1)==0;if(d){z=H;break L11394}o=c[l+(H*40&-1)+36>>2]|0;t=l+(H*40&-1)+32|0;l=c[10036]|0;h=0;while(1){if((h|0)>=(o|0)){k=8387;break}if((a[l+((c[t>>2]|0)+h|0)|0]|0)==(a[h+103664|0]|0)){h=h+1|0}else{break}}if((k|0)==8387){if((h|0)==1){break}}if(!((o|0)>0&(d^1))){z=H;break L11394}l=c[10036]|0;e=0;s=0;p=c[t>>2]|0;while(1){if((a[e+204080|0]|0)==(a[l+(e+p|0)|0]|0)){J=p;K=s}else{if((e|0)!=5){z=H;break L11394}J=p-1|0;K=1}g=e+1|0;if((g|0)<(K+o|0)){e=g;s=K;p=J}else{break}}if((K|0)!=0){break}if(!((e|0)==4|(e|0)==9)){z=H;break L11394}}}while(0);p=c[m>>2]|0;aI(192576,70,1,p|0);s=c[12174]|0;if((s|0)!=0){o=0;l=s;do{o=o+1|0;cf(p|0,189936,(v=i,i=i+8|0,c[v>>2]=l,v)|0);l=c[48696+(o<<3)>>2]|0;}while((l|0)!=0)}aF(10,p|0);z=c[13898]|0}else{z=u}}while(0);if((z|0)>=(c[8272]|0)){i=b;return}u=c[1054]|0;L11483:do{if((a[u+(z*40&-1)|0]&1)!=0){H=c[u+(z*40&-1)+36>>2]|0;K=u+(z*40&-1)+32|0;J=c[10036]|0;k=0;while(1){if((k|0)>=(H|0)){break}if((a[J+((c[K>>2]|0)+k|0)|0]|0)==(a[k+103664|0]|0)){k=k+1|0}else{break L11483}}if((k|0)!=1){break}i=b;return}}while(0);c[13898]=z+1;i=b;return}function j1(){var b=0,d=0,e=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}d=c[m>>2]|0;if((c[8496]|0)==0){aI(211280,14,1,d|0);i=b;return}aI(221800,13,1,d|0);d=c[m>>2]|0;if((c[8498]|0)==0){aI(220672,35,1,d|0)}else{aI(221272,32,1,d|0)}d=c[m>>2]|0;if((c[8500]|0)==0){aI(218440,42,1,d|0)}else{aI(218904,53,1,d|0)}d=c[8497]|0;e=c[m>>2]|0;if((d|0)>0){cf(e|0,217848,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}else{aI(217288,32,1,e|0)}cf(c[m>>2]|0,216608,(v=i,i=i+8|0,c[v>>2]=c[8503],v)|0);cf(c[m>>2]|0,216184,(v=i,i=i+8|0,c[v>>2]=c[13532],v)|0);e=c[13534]|0;if((e|0)!=0){d=c[m>>2]|0;cf(d|0,215632,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}cf(c[m>>2]|0,215176,(v=i,i=i+8|0,c[v>>2]=c[8506],v)|0);e=c[8510]|0;if((e|0)!=0){d=c[m>>2]|0;cf(d|0,214656,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}e=c[m>>2]|0;if((c[8499]|0)==0){aI(213400,33,1,e|0)}else{d=c[8504]|0;cf(e|0,213952,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}cf(c[m>>2]|0,212848,(v=i,i=i+8|0,c[v>>2]=(c[8502]|0)!=0?150376:150208,v)|0);cf(c[m>>2]|0,212248,(v=i,i=i+8|0,c[v>>2]=(c[8501]|0)!=0?179864:211688,v)|0);i=b;return}function j2(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;b=i;i=i+56|0;d=b|0;e=c[10810]|0;f=d|0;uE(f|0,0,51);if(!(a[928]|0)){g=c[m>>2]|0;aF(10,g|0)}g=c[13898]|0;L11527:do{if((g|0)<(c[8272]|0)){h=c[1054]|0;j=(a[h+(g*40&-1)|0]&1)==0;k=c[h+(g*40&-1)+36>>2]|0;l=h+(g*40&-1)+32|0;L11529:do{if(j){n=c[l>>2]|0}else{h=c[10036]|0;o=0;while(1){if((o|0)>=(k|0)){p=8442;break}if((a[h+((c[l>>2]|0)+o|0)|0]|0)==(a[o+103664|0]|0)){o=o+1|0}else{break}}if((p|0)==8442){if((o|0)==1){p=8452;break L11527}}h=c[l>>2]|0;if(!((k|0)>0&(j^1))){n=h;break}q=c[10036]|0;r=0;do{if((a[r+74272|0]|0)!=(a[q+(r+h|0)|0]|0)){n=h;break L11529}r=r+1|0;}while((r|0)<(k|0));if((r|0)!=3){n=h;break}c[13898]=g+1;aI(73856,27,1,c[m>>2]|0);s=1;break L11527}}while(0);j=(k|0)>49?49:k;l=c[10036]|0;q=n;o=0;while(1){t=o+1|0;a[d+o|0]=a[l+q|0]|0;if((t|0)==(j|0)){break}else{q=q+1|0;o=t}}a[d+j|0]=0;c[13898]=g+1;p=8452}else{p=8452}}while(0);do{if((p|0)==8452){g=c[m>>2]|0;if((a[f]|0)==0){aI(72912,30,1,g|0);s=0;break}else{cf(g|0,73512,(v=i,i=i+8|0,c[v>>2]=f,v)|0);s=0;break}}}while(0);if((e|0)==0){i=b;return}else{u=e}do{e=c[u+4>>2]|0;g=bk(e|0,73808)|0;d=(a[f]|0)==0;if(d){p=8459}else{if((a_(e|0,f|0,uA(f|0)|0)|0)==0){p=8459}}do{if((p|0)==8459){p=0;if(!s){if(!((a_(e|0,167320,6)|0)!=0|d^1)){break}}if((a[u+8|0]&1)!=0){break}n=c[m>>2]|0;cf(n|0,223688,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=e,v)|0);n=c[m>>2]|0;aI(223040,2,1,n|0);j5(c[m>>2]|0,u+16|0,1);n=c[m>>2]|0;aF(10,n|0)}}while(0);u=c[u>>2]|0;}while((u|0)!=0);i=b;return}function j3(b){b=b|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;e=i;if((b|0)==0){f=0;i=e;return f|0}g=c[13364]|0;h=db(g,(uA(b|0)<<2)+4|0,124472)|0;c[13364]=h;c[13362]=h;g=a[b]|0;if(g<<24>>24==0){j=h}else{h=b;b=g;do{g=b<<24>>24;do{if((g|0)==13){k=c[13362]|0;c[13362]=k+1;a[k]=92;k=c[13362]|0;c[13362]=k+1;a[k]=114}else if((g|0)==10){k=c[13362]|0;c[13362]=k+1;a[k]=92;k=c[13362]|0;c[13362]=k+1;a[k]=110}else if((g|0)==34|(g|0)==92){k=c[13362]|0;c[13362]=k+1;a[k]=92;k=a[h]|0;l=c[13362]|0;c[13362]=l+1;a[l]=k}else if((g|0)==9){k=c[13362]|0;c[13362]=k+1;a[k]=92;k=c[13362]|0;c[13362]=k+1;a[k]=116}else{if((c[11252]|0)==15){k=c[13362]|0;c[13362]=k+1;a[k]=b;break}if((bB(b&255|0)|0)!=0){k=a[h]|0;l=c[13362]|0;c[13362]=l+1;a[l]=k;break}k=c[13362]|0;c[13362]=k+1;a[k]=92;be(c[13362]|0,123064,(v=i,i=i+8|0,c[v>>2]=d[h]|0,v)|0);k=c[13362]|0;if((a[k]|0)==0){break}else{m=k}do{m=m+1|0;c[13362]=m;}while((a[m]|0)!=0)}}while(0);h=h+1|0;b=a[h]|0;}while(b<<24>>24!=0);j=c[13362]|0}a[j]=0;f=c[13364]|0;i=e;return f|0}function j4(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0,u=0,w=0,x=0,y=0,z=0.0,A=0,B=0;d=i;i=i+384|0;e=d|0;f=d+48|0;g=d+96|0;j=d+144|0;k=d+224|0;l=d+304|0;n=j3(64864+(b*688&-1)|0)|0;o=56832+(b*24&-1)|0;p=65072+(b*688&-1)|0;q=(a[p]&1)!=0?115944:115008;r=+h[65056+(b*688&-1)>>3];s=+h[65064+(b*688&-1)>>3];cf(c[m>>2]|0,116800,(v=i,i=i+32|0,c[v>>2]=o,c[v+8>>2]=q,h[v+16>>3]=r,h[v+24>>3]=s,v)|0);cf(c[m>>2]|0,113920,(v=i,i=i+8|0,c[v>>2]=o,v)|0);o=64916+(b*688&-1)|0;q=c[o>>2]&3;do{if((q|0)==2){t=c[m>>2]|0;aI(107752,7,1,t|0);if((c[o>>2]&4|0)==0){break}t=c[m>>2]|0;u=(a[p]&1)!=0?115008:115944;cf(t|0,106816,(v=i,i=i+8|0,c[v>>2]=u,v)|0)}else if((q|0)==0){aI(109752,4,1,c[m>>2]|0);i=d;return}else if((q|0)==1){aI(105832,9,1,c[m>>2]|0);if((c[o>>2]&4|0)==0){break}aI(104760,32,1,c[m>>2]|0)}}while(0);if((a[65024+(b*688&-1)|0]&1)!=0){o=c[m>>2]|0;aI(104192,34,1,o|0)}aI(103824,15,1,c[m>>2]|0);do{if((a[65272+(b*688&-1)|0]&1)==0){o=c[m>>2]|0;aI(100008,25,1,o|0)}else{o=c[65128+(b*688&-1)>>2]|0;if((o|0)==0){q=c[m>>2]|0;aI(103432,16,1,q|0);break}else if((o|0)==2){aI(101240,17,1,c[m>>2]|0);break}else if((o|0)==1){aI(100688,18,1,c[m>>2]|0);break}else{break}}}while(0);cf(c[m>>2]|0,99584,(v=i,i=i+8|0,c[v>>2]=n,v)|0);n=65032+(b*688&-1)|0;o=c[m>>2]|0;if((c[n>>2]|0)==0){aI(97264,23,1,o|0)}else{aI(99160,8,1,o|0);o=c[m>>2]|0;q=c[n>>2]|0;cf(o|0,98536,(v=i,i=i+8|0,c[v>>2]=q,v)|0);q=c[m>>2]|0;aI(97880,35,1,q|0)}aI(96688,11,1,c[m>>2]|0);q=c[64984+(b*688&-1)>>2]|0;if((q|0)==0){w=179864}else{w=c[21784+(q<<2)>>2]|0}s=+h[65e3+(b*688&-1)>>3];o=c[64988+(b*688&-1)>>2]|0;if((o|0)==(q|0)){x=179864}else{x=c[21784+(o<<2)>>2]|0}r=+h[65008+(b*688&-1)>>3];q=c[64992+(b*688&-1)>>2]|0;if((q|0)==(o|0)){y=179864}else{y=c[21784+(q<<2)>>2]|0}z=+h[65016+(b*688&-1)>>3];cf(c[m>>2]|0,77392,(v=i,i=i+48|0,c[v>>2]=w,h[v+8>>3]=s,c[v+16>>2]=x,h[v+24>>3]=r,c[v+32>>2]=y,h[v+40>>3]=z,v)|0);aI(84248,2,1,c[m>>2]|0);y=c[64920+(b*688&-1)>>2]|0;if((y|0)==5){x=c[m>>2]|0;aI(94856,30,1,x|0)}else if((y|0)==4){aI(95344,32,1,c[m>>2]|0)}else if((y|0)==2){aI(94192,8,1,c[m>>2]|0);x=64952+(b*688&-1)|0;do{if(+h[x>>3]!=-8.988465674311579e+307){w=c[m>>2]|0;aI(93552,6,1,w|0);w=64808+(b*688&-1)|0;q=c[m>>2]|0;if((c[w>>2]|0)==1){aF(34,q|0);o=j|0;n=64813+(b*688&-1)|0;z=+h[x>>3];t3(g,z);r=z- +O(+z);t4(o,80,n,g,r);n=j3(o)|0;o=c[m>>2]|0;aK(n|0,o|0);o=c[m>>2]|0;aF(34,o|0);A=w;break}else{r=+h[x>>3];cf(q|0,92736,(v=i,i=i+8|0,h[v>>3]=r,v)|0);A=w;break}}else{A=64808+(b*688&-1)|0}}while(0);x=(c[A>>2]|0)==1?91240:179864;cf(c[m>>2]|0,91888,(v=i,i=i+16|0,h[v>>3]=+h[64960+(b*688&-1)>>3],c[v+8>>2]=x,v)|0);x=64968+(b*688&-1)|0;do{if(+h[x>>3]!=8.988465674311579e+307){g=c[m>>2]|0;aI(90664,7,1,g|0);g=c[m>>2]|0;if((c[A>>2]|0)==1){aF(34,g|0);j=k|0;w=64813+(b*688&-1)|0;r=+h[x>>3];t3(f,r);z=r- +O(+r);t4(j,80,w,f,z);w=j3(j)|0;j=c[m>>2]|0;aK(w|0,j|0);j=c[m>>2]|0;aF(34,j|0);break}else{z=+h[x>>3];cf(g|0,92736,(v=i,i=i+8|0,h[v>>3]=z,v)|0);break}}}while(0);aF(10,c[m>>2]|0)}else if((y|0)==1){aI(95816,35,1,c[m>>2]|0)}else if((y|0)==3){aI(90168,25,1,c[m>>2]|0)}else{uf(-1,89432,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}y=64944+(b*688&-1)|0;if((c[y>>2]|0)!=0){x=c[m>>2]|0;aI(89080,18,1,x|0);x=c[y>>2]|0;L11649:do{if((x|0)!=0){y=64808+(b*688&-1)|0;f=l|0;k=64813+(b*688&-1)|0;A=x;do{g=c[A+8>>2]|0;if((g|0)!=0){j=c[m>>2]|0;w=j3(g)|0;cf(j|0,103640,(v=i,i=i+8|0,c[v>>2]=w,v)|0)}w=c[m>>2]|0;if((c[y>>2]|0)==1){aF(34,w|0);z=+h[A>>3];t3(e,z);r=z- +O(+z);t4(f,80,k,e,r);j=j3(f)|0;g=c[m>>2]|0;aK(j|0,g|0);g=c[m>>2]|0;aF(34,g|0)}else{r=+h[A>>3];cf(w|0,92736,(v=i,i=i+8|0,h[v>>3]=r,v)|0)}w=c[A+12>>2]|0;if((w|0)!=0){g=c[m>>2]|0;cf(g|0,95144,(v=i,i=i+8|0,c[v>>2]=w,v)|0)}w=A+16|0;if((c[w>>2]|0)==0){break L11649}aI(86120,2,1,c[m>>2]|0);A=c[w>>2]|0;}while((A|0)!=0)}}while(0);e=c[m>>2]|0;aI(84944,2,1,e|0)}e=64928+(b*688&-1)|0;x=e|0;if((c[x>>2]|0)!=0){l=c[m>>2]|0;aI(84256,2,1,l|0);l=c[m>>2]|0;if((c[x>>2]|0)==0){B=l}else{aI(145280,10,1,l|0);jg(l,e);B=c[m>>2]|0}aF(10,B|0)}B=c[64924+(b*688&-1)>>2]|0;if((B|0)==0){i=d;return}if((a[B]|0)==0){i=d;return}cf(c[m>>2]|0,82560,(v=i,i=i+8|0,c[v>>2]=B,v)|0);i=d;return}function j5(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0;f=i;g=c[d>>2]|0;if((g|0)==2){j=d+8|0;if((cg(+(+h[j>>3]))|0)==0){aI(133568,3,1,b|0);i=f;return}k=d+16|0;l=+h[k>>3]!=0.0;m=+h[j>>3];j=c[8300]|0;n=j+1|0;c[8300]=(n|0)>3?0:n;n=33096+(j*25&-1)|0;be(n|0,121864,(v=i,i=i+8|0,h[v>>3]=m,v)|0);do{if((a8(n|0,46)|0)==0){if((a8(n|0,101)|0)!=0){break}if((a8(n|0,69)|0)!=0){break}o=(uA(n|0)|0)+(33096+(j*25&-1))|0;a[o]=a[119960]|0;a[o+1|0]=a[119961|0]|0;a[o+2|0]=a[119962|0]|0}}while(0);if(!l){aK(n|0,b|0);i=f;return}m=+h[k>>3];k=c[8300]|0;l=k+1|0;c[8300]=(l|0)>3?0:l;l=33096+(k*25&-1)|0;be(l|0,121864,(v=i,i=i+8|0,h[v>>3]=m,v)|0);do{if((a8(l|0,46)|0)==0){if((a8(l|0,101)|0)!=0){break}if((a8(l|0,69)|0)!=0){break}j=(uA(l|0)|0)+(33096+(k*25&-1))|0;a[j]=a[119960]|0;a[j+1|0]=a[119961|0]|0;a[j+2|0]=a[119962|0]|0}}while(0);cf(b|0,129368,(v=i,i=i+16|0,c[v>>2]=n,c[v+8>>2]=l,v)|0);i=f;return}else if((g|0)==1){cf(b|0,21e4,(v=i,i=i+8|0,c[v>>2]=c[d+8>>2],v)|0);i=f;return}else if((g|0)==3){g=c[d+8>>2]|0;if((g|0)==0){i=f;return}if(e){e=j3(g)|0;cf(b|0,126760,(v=i,i=i+8|0,c[v>>2]=e,v)|0);i=f;return}else{aK(g|0,b|0);i=f;return}}else{uf(-1,125696,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function j6(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,n=0.0,o=0,p=0.0,q=0,r=0.0;f=i;if((e|0)==0){i=f;return}g=c[m>>2]|0;j=c[e+60>>2]|0;if((j|0)==0){k=179864}else{k=j3(j)|0}cf(g|0,201680,(v=i,i=i+24|0,c[v>>2]=b,c[v+8>>2]=d,c[v+16>>2]=k,v)|0);k=c[e+144>>2]|0;if((k|0)==0){l=179864}else{l=c[21784+(k<<2)>>2]|0}n=+h[e+160>>3];d=c[e+148>>2]|0;if((d|0)==(k|0)){o=179864}else{o=c[21784+(d<<2)>>2]|0}p=+h[e+168>>3];k=c[e+152>>2]|0;if((k|0)==(d|0)){q=179864}else{q=c[21784+(k<<2)>>2]|0}r=+h[e+176>>3];cf(c[m>>2]|0,77392,(v=i,i=i+48|0,c[v>>2]=l,h[v+8>>3]=n,c[v+16>>2]=o,h[v+24>>3]=p,c[v+32>>2]=q,h[v+40>>3]=r,v)|0);q=c[e+64>>2]|0;if((q|0)!=0){o=c[m>>2]|0;l=j3(q)|0;cf(o|0,200976,(v=i,i=i+8|0,c[v>>2]=l,v)|0)}do{if((c[e+4>>2]|0)==-3){l=c[m>>2]|0;aI(200160,30,1,l|0)}else{l=c[e+52>>2]|0;if((l|0)==0){break}o=c[m>>2]|0;cf(o|0,199392,(v=i,i=i+8|0,c[v>>2]=l,v)|0)}}while(0);l=e+72|0;if((c[l>>2]|0)!=0){o=c[m>>2]|0;aI(145280,10,1,o|0);jg(o,l)}if((a[e+184|0]&1)!=0){e=c[m>>2]|0;aI(199048,11,1,e|0)}aF(10,c[m>>2]|0);i=f;return}function j7(){var b=0,d=0,e=0,f=0,g=0.0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}d=c[12372]|0;if((d|0)==1|(d|0)==4){e=c[m>>2]|0;f=(d|0)==1?179864:114984;g=+(c[12373]|0)/100.0;cf(e|0,115056,(v=i,i=i+16|0,c[v>>2]=f,h[v+8>>3]=g,v)|0)}else if((d|0)==2|(d|0)==5){f=c[12374]|0;cf(c[m>>2]|0,114872,(v=i,i=i+16|0,c[v>>2]=(d|0)==2?179864:114984,c[v+8>>2]=f,v)|0)}else{aI(114808,20,1,c[m>>2]|0)}f=c[m>>2]|0;if((c[12376]|0)==1&(c[12377]|0)==-3){aI(114720,16,1,f|0);i=b;return}else{aI(114672,13,1,f|0);jg(c[m>>2]|0,49504);f=c[m>>2]|0;aF(10,f|0);i=b;return}}function j8(){var a=0,b=0,d=0,e=0,f=0.0,g=0,j=0,k=0.0,l=0,n=0.0,o=0,p=0;a=i;b=c[9670]|0;if((b|0)==4){d=c[m>>2]|0;e=c[9671]|0;f=+h[4840];cf(d|0,115720,(v=i,i=i+16|0,c[v>>2]=e,h[v+8>>3]=f,v)|0)}else if((b|0)==3){cf(c[m>>2]|0,115896,(v=i,i=i+8|0,c[v>>2]=c[9671],v)|0)}else if((b|0)==1){aI(115656,31,1,c[m>>2]|0)}else if((b|0)==2){aI(115560,34,1,c[m>>2]|0)}aI(115520,14,1,c[m>>2]|0);b=c[9720]|0;if((b|0)==0){g=179864}else{g=c[21784+(b<<2)>>2]|0}f=+h[4862];e=c[9721]|0;if((e|0)==(b|0)){j=179864}else{j=c[21784+(e<<2)>>2]|0}k=+h[4863];b=c[9722]|0;if((b|0)==(e|0)){l=179864}else{l=c[21784+(b<<2)>>2]|0}n=+h[4864];cf(c[m>>2]|0,77392,(v=i,i=i+48|0,c[v>>2]=g,h[v+8>>3]=f,c[v+16>>2]=j,h[v+24>>3]=k,c[v+32>>2]=l,h[v+40>>3]=n,v)|0);if((c[9702]|0)!=1){o=c[m>>2]|0;p=aF(10,o|0)|0;i=a;return}cf(c[m>>2]|0,115464,(v=i,i=i+8|0,c[v>>2]=(c[9703]|0)+1,v)|0);o=c[m>>2]|0;p=aF(10,o|0)|0;i=a;return}function j9(){var b=0,d=0,e=0.0,f=0,g=0;b=i;cf(c[m>>2]|0,121184,(v=i,i=i+8|0,c[v>>2]=(c[14068]|0)==257?120960:120872,v)|0);aI(120712,32,1,c[m>>2]|0);d=c[m>>2]|0;e=+h[7032];if((c[14062]|0)==1){cf(d|0,120416,(v=i,i=i+8|0,h[v>>3]=e,v)|0)}else{cf(d|0,120264,(v=i,i=i+8|0,h[v>>3]=e,v)|0)}d=c[m>>2]|0;if((a[56264]&1)==0){aI(119752,28,1,d|0)}else{f=(c[14067]|0)+1|0;cf(d|0,119912,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}cf(c[m>>2]|0,119616,(v=i,i=i+8|0,h[v>>3]=+h[7035],v)|0);f=c[m>>2]|0;d=c[14072]|0;do{if((d|0)==2){g=119368}else{if((d|0)==3){g=119280;break}g=(d|0)==1?119216:119160}}while(0);cf(f|0,119552,(v=i,i=i+8|0,c[v>>2]=g,v)|0);cf(c[m>>2]|0,119104,(v=i,i=i+8|0,c[v>>2]=(a[56292]&1)!=0?116904:116760,v)|0);i=b;return}function ka(){var b=0,d=0,e=0,f=0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}d=c[12292]|0;if((d|0)>0){e=150856}else{e=(d|0)<0?121920:150704}cf(c[m>>2]|0,122136,(v=i,i=i+8|0,c[v>>2]=e,v)|0);do{if((a[49240]&1)==0){e=c[12303]|0;d=c[m>>2]|0;if((e|0)==-4){aI(121848,10,1,d|0);break}else{f=e+1|0;cf(d|0,121528,(v=i,i=i+8|0,c[v>>2]=f,v)|0);break}}else{jg(c[m>>2]|0,49248)}}while(0);cf(c[m>>2]|0,121456,(v=i,i=i+8|0,h[v>>3]=+h[6153],v)|0);aI(121392,11,1,c[m>>2]|0);jf(c[m>>2]|0,49176);i=b;return}function kb(){var b=0,d=0,e=0,f=0.0,g=0,j=0,k=0.0,l=0,n=0.0;b=i;if(!(a[928]|0)){d=c[m>>2]|0;aF(10,d|0)}aI(122752,32,1,c[m>>2]|0);d=c[12418]|0;if((d|0)==0){e=179864}else{e=c[21784+(d<<2)>>2]|0}f=+h[6211];g=c[12419]|0;if((g|0)==(d|0)){j=179864}else{j=c[21784+(g<<2)>>2]|0}k=+h[6212];d=c[12420]|0;if((d|0)==(g|0)){l=179864}else{l=c[21784+(d<<2)>>2]|0}n=+h[6213];cf(c[m>>2]|0,77392,(v=i,i=i+48|0,c[v>>2]=e,h[v+8>>3]=f,c[v+16>>2]=j,h[v+24>>3]=k,c[v+32>>2]=l,h[v+40>>3]=n,v)|0);cf(c[m>>2]|0,122680,(v=i,i=i+8|0,h[v>>3]=+h[6214],v)|0);l=c[12406]|0;if((l|0)==1){j=c[m>>2]|0;aI(122448,53,1,j|0);i=b;return}else if((l|0)==0){aI(122568,66,1,c[m>>2]|0);i=b;return}else if((l|0)==2){aI(122360,53,1,c[m>>2]|0);i=b;return}else{i=b;return}}function kc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0;e=i;f=b|0;if((c[f>>2]|0)<=0){i=e;return}g=(d|0)>0;h=(d|0)<6;j=d+2|0;k=0;do{aF(9,c[m>>2]|0);if(g){l=0;do{aF(32,c[m>>2]|0);l=l+1|0;}while((l|0)<(d|0))}l=b+8+(k<<5)|0;aK(c[41160+(c[l>>2]<<3)>>2]|0,c[m>>2]|0);n=b+8+(k<<5)+8|0;o=c[l>>2]|0;do{if((o|0)==37|(o|0)==38|(o|0)==39|(o|0)==40){l=c[m>>2]|0;p=c[n>>2]|0;cf(l|0,105024,(v=i,i=i+8|0,c[v>>2]=p,v)|0)}else if((o|0)==6){p=n;cf(c[m>>2]|0,124744,(v=i,i=i+8|0,c[v>>2]=c[(c[p>>2]|0)+4>>2],v)|0);if(!h){l=c[m>>2]|0;aF(10,l|0);break}l=c[m>>2]|0;if((c[(c[p>>2]|0)+8>>2]|0)==0){aI(105080,13,1,l|0);break}else{aF(10,l|0);kc(c[(c[p>>2]|0)+8>>2]|0,j);break}}else if((o|0)==2){cf(c[m>>2]|0,105160,(v=i,i=i+8|0,c[v>>2]=a[c[(c[n>>2]|0)+4>>2]|0]|0,v)|0)}else if((o|0)==0){cf(c[m>>2]|0,105248,(v=i,i=i+8|0,c[v>>2]=c[(c[n>>2]|0)+4>>2],v)|0)}else if((o|0)==3){cf(c[m>>2]|0,105160,(v=i,i=i+8|0,c[v>>2]=a[(c[(c[n>>2]|0)+4>>2]|0)+1|0]|0,v)|0)}else if((o|0)==1){aF(32,c[m>>2]|0);j5(c[m>>2]|0,n|0,1);aF(10,c[m>>2]|0)}else if((o|0)==31){cf(c[m>>2]|0,104848,(v=i,i=i+8|0,c[v>>2]=c[b+8+(k<<5)+16>>2],v)|0)}else if((o|0)==7|(o|0)==8){p=n;cf(c[m>>2]|0,124744,(v=i,i=i+8|0,c[v>>2]=c[(c[p>>2]|0)+4>>2],v)|0);if(!h){l=c[m>>2]|0;aF(10,l|0);break}l=c[m>>2]|0;if((c[(c[p>>2]|0)+8>>2]|0)==0){aI(105080,13,1,l|0);break}else{aF(10,l|0);kc(c[(c[p>>2]|0)+8>>2]|0,j);break}}else{aF(10,c[m>>2]|0)}}while(0);k=k+1|0;}while((k|0)<(c[f>>2]|0));i=e;return}function kd(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0,u=0.0;b=i;d=c[10824]|0;if((d|0)==0){e=0}else{f=(a|0)==0;g=0;j=d;while(1){d=c[j+4>>2]|0;if(f){k=d;l=8695}else{if((d|0)==(a|0)){k=a;l=8695}else{n=g}}do{if((l|0)==8695){l=0;d=c[m>>2]|0;cf(d|0,116640,(v=i,i=i+8|0,c[v>>2]=k,v)|0);d=c[m>>2]|0;aD(d|0);d=c[m>>2]|0;o=j+72|0;p=c[o>>2]|0;if((p|0)==0){q=116368}else{q=(p|0)==2?116472:116424}p=(c[j+12>>2]|0)!=0?150856:150704;cf(d|0,116584,(v=i,i=i+16|0,c[v>>2]=q,c[v+8>>2]=p,v)|0);ji(c[m>>2]|0,j+16|0,0);p=c[m>>2]|0;aF(10,p|0);if((c[o>>2]|0)==0){n=1;break}o=c[m>>2]|0;p=j+112|0;d=c[p>>2]|0;if((d|0)==2){r=128e3}else{r=(d|0)==1?127904:127768}cf(o|0,116328,(v=i,i=i+8|0,c[v>>2]=r,v)|0);s=+h[j+80>>3];o=c[m>>2]|0;if(s<=0.0){aI(116160,29,1,o|0);n=1;break}d=c[j+88>>2]|0;if((d|0)==0){t=179864}else{t=c[21808+(d<<2)>>2]|0}u=+h[j+96>>3];cf(o|0,116216,(v=i,i=i+24|0,c[v>>2]=t,h[v+8>>3]=s,h[v+16>>3]=u,v)|0);if((c[p>>2]|0)!=0){p=c[m>>2]|0;u=+h[j+104>>3];cf(p|0,126792,(v=i,i=i+8|0,h[v>>3]=u,v)|0)}p=c[m>>2]|0;aF(10,p|0);n=1}}while(0);p=c[j>>2]|0;if((p|0)==0){e=n;break}else{g=n;j=p}}}if((a|0)<1|e){i=b;return}else{uf(c[13898]|0,115992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function ke(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0,m=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];d=c[e>>2]|0;e=d;do{if((f|0)==3){k=+uz(e,b);l=(e|0)==(c[b>>2]|0);uu(e);if(!l){m=k;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){m=j;break}else if((f|0)==1){m=+(d|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);j=+b3(+m);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{f=d+1|0;c[6354]=f;d=14296+(f*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(f*24&-1)>>3]=j;h[14312+(f*24&-1)>>3]=0.0;i=a;return}}function kf(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0,m=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];d=c[e>>2]|0;e=d;do{if((f|0)==3){k=+uz(e,b);l=(e|0)==(c[b>>2]|0);uu(e);if(!l){m=k;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==1){m=+(d|0);break}else if((f|0)==2){m=j;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);j=+bu(+m);f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{d=f+1|0;c[6354]=d;f=14296+(d*24&-1)|0;c[f>>2]=2;c[f+4>>2]=g;h[14304+(d*24&-1)>>3]=j;h[14312+(d*24&-1)>>3]=0.0;i=a;return}}function kg(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0,s=0.0,t=0,u=0.0,w=0.0,x=0.0,y=0.0;b=i;i=i+32|0;d=b|0;e=b+8|0;f=b+16|0;g=b+24|0;j=c[6354]|0;if((j|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=j-1|0;c[6354]=k;l=c[14296+(j*24&-1)>>2]|0;m=14304+(j*24&-1)|0;n=+h[m>>3];j=c[m>>2]|0;h[g>>3]=n;m=j;j=m;do{if((l|0)==3){o=+uz(j,f);p=(j|0)==(c[f>>2]|0);uu(j);if(p){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[g>>3]=o;q=o;r=c[6354]|0;break}}else{if((l|0)==1){q=+(m|0);r=k;break}else if((l|0)==2){q=n;r=k;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((r|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=r-1|0;c[6354]=k;l=c[14296+(r*24&-1)>>2]|0;m=14304+(r*24&-1)|0;n=+h[m>>3];r=c[m>>2]|0;h[g>>3]=n;m=r;r=m;do{if((l|0)==3){o=+uz(r,e);j=(r|0)==(c[e>>2]|0);uu(r);if(j){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[g>>3]=o;s=o;t=c[6354]|0;break}}else{if((l|0)==2){s=n;t=k;break}else if((l|0)==1){s=+(m|0);t=k;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((t|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=t-1;k=14296+(t*24&-1)|0;m=c[k>>2]|0;l=c[k+4>>2]|0;k=14304+(t*24&-1)|0;n=+h[k>>3];r=c[k>>2]|0;h[g>>3]=n;o=+h[14312+(t*24&-1)>>3];t=r;r=t;do{if((m|0)==3){u=+uz(r,d);k=(r|0)==(c[d>>2]|0);uu(r);if(k){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[g>>3]=u;w=u;x=0.0;break}}else{if((m|0)==2){w=n;x=o;break}else if((m|0)==1){w=+(t|0);x=o;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if(w>0.0&s>0.0){if(q<0.0|q>1.0){break}do{if(q==0.0|q==1.0){y=q}else{if(q*(s+w)<=w){y=+kr(w,s,q);break}o=+kr(s,w,1.0-q);if(o<0.0){y=o;break}y=1.0-o}}while(0);if(y==-1.0){break}h[g>>3]=y;t=c[6354]|0;if((t|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=t+1|0;c[6354]=m;t=14296+(m*24&-1)|0;c[t>>2]=2;c[t+4>>2]=l;h[14304+(m*24&-1)>>3]=y;h[14312+(m*24&-1)>>3]=0.0;i=b;return}}while(0);a[1960]=1;c[g>>2]=0;m=c[6354]|0;if((m|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t=m+1|0;c[6354]=t;m=14296+(t*24&-1)|0;c[m>>2]=1;c[m+4>>2]=l;h[14304+(t*24&-1)>>3]=+h[g>>3];h[14312+(t*24&-1)>>3]=x;i=b;return}function kh(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0,p=0.0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0,z=0.0,A=0.0,B=0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,Q=0.0,R=0.0,S=0.0,T=0.0;b=i;i=i+24|0;d=b|0;e=b+8|0;f=b+16|0;g=c[6354]|0;if((g|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=g-1|0;c[6354]=j;k=c[14296+(g*24&-1)>>2]|0;l=14304+(g*24&-1)|0;m=+h[l>>3];g=c[l>>2]|0;h[f>>3]=m;l=g;g=l;do{if((k|0)==3){n=+uz(g,e);o=(g|0)==(c[e>>2]|0);uu(g);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[f>>3]=n;p=n;q=c[6354]|0;break}}else{if((k|0)==1){p=+(l|0);q=j;break}else if((k|0)==2){p=m;q=j;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q-1;j=14296+(q*24&-1)|0;k=c[j>>2]|0;l=c[j+4>>2]|0;j=14304+(q*24&-1)|0;m=+h[j>>3];g=c[j>>2]|0;h[f>>3]=m;n=+h[14312+(q*24&-1)>>3];q=g;g=q;do{if((k|0)==3){r=+uz(g,d);j=(g|0)==(c[d>>2]|0);uu(g);if(j){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[f>>3]=r;s=r;t=0.0;break}}else{if((k|0)==1){s=+(q|0);t=n;break}else if((k|0)==2){s=m;t=n;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);L12007:do{if(p>=0.0&s>0.0){do{if(p==0.0){u=0.0}else{if(p>1.0e8){u=1.0;break}n=s*+_(+p)-p;m=n- +b2(+(s+1.0));if(m<-706.893623549172){w=0.0}else{w=+Z(+m)}do{if(p>1.0){if(s+2.0>p){x=s;y=0;z=1.0;A=1.0;B=8809;break}m=1.0-s;n=p+m+1.0;r=p+1.0;C=p*n;D=n;k=1;n=1.0;E=p;F=r;G=C;H=m;m=r/C;while(1){C=H+1.0;r=D+2.0;I=C*+(k|0);J=F*r-n*I;K=G*r-E*I;if(K!=0.0){L=J/K;I=L*1.1920928955078125e-7;if(+P(+(m-L))>(I>1.1920928955078125e-7?1.1920928955078125e-7:I)){M=L}else{break}}else{M=m}if(+P(+J)<3.4028234663852886e+38){N=K;O=J;Q=G;R=F}else{N=K/3.4028234663852886e+38;O=J/3.4028234663852886e+38;Q=G/3.4028234663852886e+38;R=F/3.4028234663852886e+38}q=k+1|0;if((q|0)<201){D=r;k=q;n=R;E=Q;F=O;G=N;H=C;m=M}else{break L12007}}S=1.0-s*w*L}else{x=s;y=0;z=1.0;A=1.0;B=8809}}while(0);if((B|0)==8809){while(1){B=0;if((y|0)>=201){break L12007}m=x+1.0;H=A*(p/m);T=z+H;if(H<T*1.1920928955078125e-7){break}else{x=m;y=y+1|0;z=T;A=H;B=8809}}S=w*T}if(S==-1.0){break L12007}else{u=S}}}while(0);h[f>>3]=u;k=c[6354]|0;if((k|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}q=k+1|0;c[6354]=q;k=14296+(q*24&-1)|0;c[k>>2]=2;c[k+4>>2]=l;h[14304+(q*24&-1)>>3]=u;h[14312+(q*24&-1)>>3]=0.0;i=b;return}}while(0);a[1960]=1;c[f>>2]=0;B=c[6354]|0;if((B|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}y=B+1|0;c[6354]=y;B=14296+(y*24&-1)|0;c[B>>2]=1;c[B+4>>2]=l;h[14304+(y*24&-1)>>3]=+h[f>>3];h[14312+(y*24&-1)>>3]=t;i=b;return}function ki(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0;b=i;i=i+16|0;d=b|0;e=b+8|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=f-1;g=14296+(f*24&-1)|0;j=c[g>>2]|0;k=c[g+4>>2]|0;g=14304+(f*24&-1)|0;l=+h[g>>3];m=c[g>>2]|0;h[e>>3]=l;n=+h[14312+(f*24&-1)>>3];f=m;m=f;do{if((j|0)==3){o=+uz(m,d);g=(m|0)==(c[d>>2]|0);uu(m);if(g){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[e>>3]=o;p=o;q=0.0;break}}else{if((j|0)==1){p=+(f|0);q=n;break}else if((j|0)==2){p=l;q=n;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);n=+b2(+p);if(n>709.1962086421661){a[1960]=1;c[e>>2]=0;j=c[6354]|0;if((j|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=j+1|0;c[6354]=f;j=14296+(f*24&-1)|0;c[j>>2]=1;c[j+4>>2]=k;h[14304+(f*24&-1)>>3]=+h[e>>3];h[14312+(f*24&-1)>>3]=q;i=b;return}q=+(c[aH()>>2]|0);if(n<-706.893623549172){r=0.0}else{r=+Z(+n)}n=q*r;h[e>>3]=n;e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e+1|0;c[6354]=f;e=14296+(f*24&-1)|0;c[e>>2]=2;c[e+4>>2]=k;h[14304+(f*24&-1)>>3]=n;h[14312+(f*24&-1)>>3]=0.0;i=b;return}function kj(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0,m=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];d=c[e>>2]|0;e=d;do{if((f|0)==3){k=+uz(e,b);l=(e|0)==(c[b>>2]|0);uu(e);if(!l){m=k;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){m=j;break}else if((f|0)==1){m=+(d|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);j=+b2(+m);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{f=d+1|0;c[6354]=f;d=14296+(f*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(f*24&-1)>>3]=j;h[14312+(f*24&-1)>>3]=0.0;i=a;return}}function kk(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0.0,m=0.0,n=0.0,o=0,p=0.0,q=0.0,r=0,s=0,t=0.0,u=0,w=0.0,x=0,y=0,z=0,A=0,B=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;l=+h[f>>3];m=+h[14312+(e*24&-1)>>3];e=c[f>>2]|0;f=e;do{if((g|0)==3){n=+uz(f,d);o=(f|0)==(c[d>>2]|0);uu(f);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=n;q=0.0;r=2;s=c[k>>2]|0;t=n;break}}else{if((g|0)==2){p=l;q=m;r=2;s=e;t=l;break}else if((g|0)==1){p=+(e|0);q=m;r=1;s=e;t=l;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if(p!=0.0){if((r|0)==2){u=8870;break}else if((r|0)==1){w=+(s|0);x=0;y=s;u=8871;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{if((r|0)==2){if(q!=0.0){u=8870;break}}else if((r|0)!=1){uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(!(a[27800]|0)){u=8886;break}z=c[6948]|0;A=c[6946]|0}}while(0);if((u|0)==8870){w=t;x=~~q;y=~~t;u=8871}L12113:do{if((u|0)==8871){if(w!=+(y|0)){uf(-1,216320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((r|0)==3){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((r|0)==1){B=0.0}else if((r|0)==2){B=q}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(B!=+(x|0)){uf(-1,216320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if((y|0)<1){if((x|0)!=0){uf(-1,216320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((y|0)>=0){break}a[27800]=0;u=8886;break L12113}else{if((x|0)>=0){break}uf(-1,216320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[6948]=y;s=(x|0)!=0?x:y;c[6946]=s;a[27800]=1;z=y;A=s}}while(0);if((u|0)==8886){a[27800]=1;c[6948]=1234567890;c[6946]=1234567890;z=1234567890;A=1234567890}u=(z|0)/53668&-1;y=(((u*-53668&-1)+z|0)*40014&-1)+(u*-12211&-1)|0;u=(y|0)<0?y+2147483563|0:y;c[6948]=u;y=(A|0)/52774&-1;z=(((y*-52774&-1)+A|0)*40692&-1)+(y*-3791&-1)|0;y=(z|0)<0?z+2147483399|0:z;c[6946]=y;z=u-y|0;y=c[6354]|0;if((y|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{u=y+1|0;c[6354]=u;y=14296+(u*24&-1)|0;c[y>>2]=2;c[y+4>>2]=j;h[14304+(u*24&-1)>>3]=+(((z|0)<1?z+2147483562|0:z)|0)*4.656613057e-10;h[14312+(u*24&-1)>>3]=0.0;i=b;return}}function kl(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0.0,u=0,w=0.0,x=0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0;a=i;i=i+400|0;b=a|0;d=a+48|0;e=a+96|0;f=a+144|0;g=a+192|0;j=a+240|0;k=a+288|0;l=a+336|0;m=a+384|0;n=a+392|0;o=c[6354]|0;if((o|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=o-1|0;c[6354]=p;q=c[14296+(o*24&-1)>>2]|0;r=14304+(o*24&-1)|0;s=+h[r>>3];o=c[r>>2]|0;r=o;do{if((q|0)==3){t=+uz(r,n);u=(r|0)==(c[n>>2]|0);uu(r);if(u){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{w=t;x=c[6354]|0;break}}else{if((q|0)==2){w=s;x=p;break}else if((q|0)==1){w=+(o|0);x=p;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((x|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=x-1;p=14296+(x*24&-1)|0;o=c[p>>2]|0;q=c[p+4>>2]|0;p=14304+(x*24&-1)|0;s=+h[p>>3];x=c[p>>2]|0;p=x;do{if((o|0)==3){t=+uz(p,m);r=(p|0)==(c[m>>2]|0);uu(p);if(!r){y=t;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((o|0)==1){y=+(x|0);break}else if((o|0)==2){y=s;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);s=w*w;t=w*.56418958;z=+P(+y);A=z*z;do{if(w<70.55){B=+Q(+(w*(40.0-w*3.6)+15100.0));if(w<8.425){C=+Q(+(164.0-w*(w*1.8+4.3)))}else{C=0.0}D=w*18.1+1.65;o=w>1.0e-6;if(z>=B){E=t/(s+A);break}if(z>=(o?C:B)){F=s+.5;E=(F+A)*w*(.56418958/(F*F+A*(s+s+-1.0+A)));break}if(z>(o?6.8-w:B)){B=s*4.0;F=B+-6.0;E=(s*(s*(s+5.5)+8.25)+1.875+A*(s*(s*3.0+1.0)+5.25+A*(F*.75+A)))*w*(.56418958/(s*(s*(s*(s+6.0)+10.5)+4.5)+.5625+A*(s*(s*(B+6.0)+9.0)-4.5+A*(10.5-s*(6.0-s*6.0)+A*(F+A)))));break}if(z<w*2.4){F=w*5.0;B=w*10.0;G=w*.3183291;E=(w*(w*(w*(w*(w*(w*(w*(w*(G+4.264678)+27.93941)+115.3772)+328.2151)+662.8097)+946.897)+919.4955)+549.3954)+153.5168+A*(w*(w*(w*(w*(w*(w*(w*1.2733163+12.79458)+56.81652)+139.4665)+189.773)+124.5975)-1.322256)-34.16955+A*(w*(w*(w*(w*(w*1.9099744+12.79568)+29.81482)+24.01655)+10.46332)+2.584042+A*(w*(w*(w*1.273316+4.266322)+.9377051)+-.07272979+(G+.0005480304)*A))))*(1.7724538/(w*(w*(w*(w*(w*(w*(w*(w*(w*(w+13.3988)+88.26741)+369.1989)+1074.409)+2256.981)+3447.629)+3764.966)+2802.87)+1280.829)+272.1014+A*(w*(w*(w*(w*(w*(w*(w*(F+53.59518)+266.2987)+793.4273)+1549.675)+2037.31)+1758.336)+902.3066)+211.678+A*(w*(w*(w*(w*(w*(B+80.39278)+269.2916)+479.2576)+497.3014)+308.1852)+78.86585+A*(w*(w*(w*(B+53.59518)+92.75679)+55.02933)+22.03523+A*(w*(F+13.3988)+1.49646+A))))));break}F=w+1.5;B=F*F;o=0;do{G=+h[37496+(o<<3)>>3];H=y-G;I=H*H;h[e+(o<<3)>>3]=I;J=1.0/(B+I);h[b+(o<<3)>>3]=J;h[g+(o<<3)>>3]=J*H;h[j+(o<<3)>>3]=F*J;J=y+G;G=J*J;h[f+(o<<3)>>3]=G;H=1.0/(B+G);h[d+(o<<3)>>3]=H;h[k+(o<<3)>>3]=J*H;h[l+(o<<3)>>3]=F*H;o=o+1|0;}while((o|0)<6);if(z<=D){E=(+h[j>>3]+ +h[l>>3])*1.0117281-(+h[g>>3]- +h[k>>3])*1.393237+0.0+((+h[j+8>>3]+ +h[l+8>>3])*-.75197147-(+h[g+8>>3]- +h[k+8>>3])*.23115241)+((+h[j+16>>3]+ +h[l+16>>3])*.012557727-(+h[g+16>>3]- +h[k+16>>3])*-.15535147)+((+h[j+24>>3]+ +h[l+24>>3])*.010022008-(+h[g+24>>3]- +h[k+24>>3])*.0062183662)+((+h[j+32>>3]+ +h[l+32>>3])*-.00024206814-(+h[g+32>>3]- +h[k+32>>3])*91908299.0e-12)+((+h[j+40>>3]+ +h[l+40>>3])*5.0084806e-7-(+h[g+40>>3]- +h[k+40>>3])*-6.2752596e-7);break}F=w+3.0;B=0.0;o=0;do{H=+h[37592+(o<<3)>>3];J=+h[e+(o<<3)>>3];G=F*+h[37544+(o<<3)>>3];I=+h[f+(o<<3)>>3];B=B+((H*(J*+h[b+(o<<3)>>3]- +h[j+(o<<3)>>3]*1.5)+G*+h[g+(o<<3)>>3])/(J+2.25)+(H*(I*+h[d+(o<<3)>>3]- +h[l+(o<<3)>>3]*1.5)-G*+h[k+(o<<3)>>3])/(I+2.25));o=o+1|0;}while((o|0)<6);E=w*B+ +Z(+(-0.0-A))}else{E=t/(s+A)}}while(0);k=c[6354]|0;if((k|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{l=k+1|0;c[6354]=l;k=14296+(l*24&-1)|0;c[k>>2]=2;c[k+4>>2]=q;h[14304+(l*24&-1)>>3]=E;h[14312+(l*24&-1)>>3]=0.0;i=a;return}}function km(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0,m=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];d=c[e>>2]|0;e=d;do{if((f|0)==3){k=+uz(e,b);l=(e|0)==(c[b>>2]|0);uu(e);if(!l){m=k;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){m=j;break}else if((f|0)==1){m=+(d|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);j=+bu(+(-0.0-m*.7071067811865476));d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{f=d+1|0;c[6354]=f;d=14296+(f*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(f*24&-1)>>3]=j*.5;h[14312+(f*24&-1)>>3]=0.0;i=a;return}}function kn(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0.0,s=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;k=+h[f>>3];e=c[f>>2]|0;f=e;do{if((g|0)==3){l=+uz(f,d);m=(f|0)==(c[d>>2]|0);uu(f);if(!m){n=l;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((g|0)==2){n=k;break}else if((g|0)==1){n=+(e|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if(n<=0.0|n>=1.0){a[1960]=1;e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=e+1|0;c[6354]=g;e=14296+(g*24&-1)|0;c[e>>2]=2;c[e+4>>2]=j;uE(14304+(g*24&-1)|0,0,16);i=b;return}do{if(n>0.0){if(n>=1.0){bD(75808,(v=i,i=i+8|0,c[v>>2]=205976,v)|0);bD(223272,(v=i,i=i+8|0,c[v>>2]=133496,v)|0);o=1.7976931348623157e+308;break}if(n>.8646647167633873){p=0;q=1.0-n}else{p=1;q=n}if(q>.1353352832366127){k=q+-.5;l=k*k;o=(k+k*(l*(l*(l*(l*(l*-59.96335010141079+98.00107541859997)-56.67628574690703)+13.931260938727968)-1.2391658386738125)/(l*(l*(l*(l*(l*(l*(l*(l+1.9544885833814176)+4.676279128988815)+86.36024213908905)-225.46268785411937)+200.26021238006066)-82.03722561683334)+15.90562251262117)-1.1833162112133)))*2.5066282746310007;break}l=+Q(+(+_(+q)*-2.0));k=l- +_(+l)/l;r=1.0/l;if(l<8.0){s=r*(r*(r*(r*(r*(r*(r*(r*(r*4.0554489230596245+31.525109459989388)+57.16281922464213)+44.08050738932008)+14.684956192885803)+2.1866330685079025)+-.1402560791713545)+-.03504246268278482)+-.0008574567851546854)/(r*(r*(r*(r*(r*(r*(r*(r+15.779988325646675)+45.39076351288792)+41.3172038254672)+15.04253856929075)+2.504649462083094)+-.14218292285478779)+-.03808064076915783)+-.0009332594808954574)}else{s=r*(r*(r*(r*(r*(r*(r*(r*(r*3.2377489177694603+6.915228890689842)+3.9388102529247444)+1.3330346081580755)+.20148538954917908)+.012371663481782003)+.00030158155350823543)+26580697468673755.0e-22)+6.239745391849833e-9)/(r*(r*(r*(r*(r*(r*(r*(r+6.02427039364742)+3.6798356385616087)+1.3770209948908132)+.21623699359449663)+.013420400608854318)+.00032801446468212774)+28924786474538068.0e-22)+6.790194080099813e-9)}r=k-s;if((p|0)==0){o=r;break}o=-0.0-r}else{bD(75808,(v=i,i=i+8|0,c[v>>2]=205976,v)|0);bD(223272,(v=i,i=i+8|0,c[v>>2]=133496,v)|0);o=-1.7976931348623157e+308}}while(0);p=c[6354]|0;if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=p+1|0;c[6354]=g;p=14296+(g*24&-1)|0;c[p>>2]=2;c[p+4>>2]=j;h[14304+(g*24&-1)>>3]=o;h[14312+(g*24&-1)>>3]=0.0;i=b;return}function ko(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0.0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;k=+h[f>>3];e=c[f>>2]|0;f=e;do{if((g|0)==3){l=+uz(f,d);m=(f|0)==(c[d>>2]|0);uu(f);if(!m){n=l;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((g|0)==2){n=k;break}else if((g|0)==1){n=+(e|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if(+P(+n)>=1.0){a[1960]=1;e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=e+1|0;c[6354]=g;e=14296+(g*24&-1)|0;c[e>>2]=2;c[e+4>>2]=j;uE(14304+(g*24&-1)|0,0,16);i=b;return}do{if(n<-1.0|n>1.0){bD(137432,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);o=+_(-1.0)}else{if(n==-1.0|n==1.0){o=+_(0.0)*(-0.0-n);break}do{if(n>-1.0&n<-.7){k=+Q(+(-0.0- +_(+((n+1.0)*.5))));p=(-0.0-(k*(k*(k*1.641345311+3.429567803)-1.624906493)-1.970840454))/(k*(k*1.6370678+3.5438892)+1.0)}else{if(!(n<-.7|n>.7)){k=n*n;p=n*(k*(k*(k*-.140543331+.914624893)-1.645349621)+.886226899)/(k*(k*(k*(k*.012229801+.012229801)+1.442710462)-2.118377725)+1.0);break}if(!(n>.7&n<1.0)){p=0.0;break}k=+Q(+(-0.0- +_(+((1.0-n)*.5))));p=(k*(k*(k*1.641345311+3.429567803)-1.624906493)-1.970840454)/(k*(k*1.6370678+3.5438892)+1.0)}}while(0);k=+b3(+p)-n;l=p*(-0.0-p);if(l<-706.893623549172){q=0.0}else{q=+Z(+l)*1.1283791670955126}l=p-k/q;k=+b3(+l)-n;r=l*(-0.0-l);if(r<-706.893623549172){s=0.0}else{s=+Z(+r)*1.1283791670955126}r=l-k/s;k=+b3(+r)-n;l=r*(-0.0-r);if(l<-706.893623549172){t=0.0}else{t=+Z(+l)*1.1283791670955126}l=r-k/t;k=+b3(+l)-n;r=l*(-0.0-l);if(r<-706.893623549172){u=0.0}else{u=+Z(+r)*1.1283791670955126}o=l-k/u}}while(0);g=c[6354]|0;if((g|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=g+1|0;c[6354]=e;g=14296+(e*24&-1)|0;c[g>>2]=2;c[g+4>>2]=j;h[14304+(e*24&-1)>>3]=o;h[14312+(e*24&-1)>>3]=0.0;i=b;return}function kp(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;k=+h[f>>3];e=c[f>>2]|0;f=e;do{if((g|0)==3){l=+uz(f,d);m=(f|0)==(c[d>>2]|0);uu(f);if(!m){n=l;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((g|0)==2){n=k;break}else if((g|0)==1){n=+(e|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);L12315:do{if(n<-.36787944117144233){o=-1.0;p=9032}else{if(+P(+n)>1.1920928955078125e-7){if(n<1.0){k=+Q(+((n*2.718281828459045+1.0)*2.0));q=k+-1.0-k*k/3.0+k*k*k*.1527777777777778}else{q=+_(+n)}if(n>3.0){r=0;s=q- +_(+q)}else{r=0;s=q}while(1){if((r|0)>=20){o=-1.0;p=9032;break L12315}if(s<-706.893623549172){t=0.0}else{t=+Z(+s)}k=s*t-n;l=s+1.0;u=k/(l*t-(s+2.0)*.5*k/l);l=s-u;k=+P(+u);if(k<(+P(+l)+1.0)*1.1920928955078125e-7){w=l;break}else{r=r+1|0;s=l}}}else{w=n}if(w>-1.0){x=w}else{o=w;p=9032}}}while(0);if((p|0)==9032){a[1960]=1;x=o}p=c[6354]|0;if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{r=p+1|0;c[6354]=r;p=14296+(r*24&-1)|0;c[p>>2]=2;c[p+4>>2]=j;h[14304+(r*24&-1)>>3]=x;h[14312+(r*24&-1)>>3]=0.0;i=b;return}}function kq(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];d=c[e>>2]|0;e=d;do{if((f|0)==3){k=+uz(e,b);l=(e|0)==(c[b>>2]|0);uu(e);if(!l){m=k;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){m=j;break}else if((f|0)==1){m=+(d|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);j=+P(+m);k=+R(+j,3.0);if(m<0.0){n=+Q(+(k+.37));o=+R(+j,1.5)*.6666666666666666;j=n*.3989422;p=(j+-.043883564)*+S(+o);q=p/+R(+n,1.1666666666666667)+0.0;p=m*(-.013883003-j)*+T(+o);r=q+p/(o*+R(+n,.8333333333333334)*1.5)}else{n=+Q(+(k+.0425));k=(n*.326662423+-.002800908)/+R(+n,1.1666666666666667)+0.0;o=k+m*(-.007232251-n*.044567423)/+R(+n,1.8333333333333333);r=o*+Z(+(n*-.6666666666666666))}d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{f=d+1|0;c[6354]=f;d=14296+(f*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(f*24&-1)>>3]=r;h[14312+(f*24&-1)>>3]=0.0;i=a;return}}function kr(a,b,c){a=+a;b=+b;c=+c;var d=0.0,e=0.0,f=0.0,g=0.0,h=0.0,i=0.0,j=0.0,k=0.0,l=0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0,t=0.0,u=0;d=a+b;e=+b2(+d);f=e+ +_(+c)*a;e=f+ +_(+(1.0-c))*b;f=e- +b2(+(a+1.0));e=f- +b2(+b);if(e<-706.893623549172){g=1.0;h=1.0;i=0.0;j=0.0;k=0.0;l=0;m=1}else{f=+Z(+e);g=1.0;h=1.0;i=f;j=f;k=0.0;l=0;m=1}while(1){f=+(m|0);e=+(l|0);n=e+(f+a);o=(d+e)*(-0.0-(e+a))*c/n/(n+-1.0);e=f*(b-f)*c/n/(n+1.0);n=j+k*o;f=g+h*o;o=n+j*e;p=f+g*e;e=+P(+p)<1.1920928955078125e-7?0.0:p;if(e!=0.0){p=o/e;q=+P(+(p-i));if(q<+P(+p)*1.1920928955078125e-7){r=p;s=9057;break}else{t=p}}else{t=i}u=l+1|0;if((u|0)<201){g=e;h=f;i=t;j=o;k=n;l=u;m=m+1|0}else{r=-1.0;s=9056;break}}if((s|0)==9056){return+r}else if((s|0)==9057){return+r}return 0.0}function ks(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0,p=0.0,q=0,r=0.0,s=0.0,t=0,u=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0;b=i;i=i+24|0;d=b|0;e=b+8|0;f=b+16|0;g=c[6354]|0;if((g|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=g-1|0;c[6354]=j;k=c[14296+(g*24&-1)>>2]|0;l=14304+(g*24&-1)|0;m=+h[l>>3];g=c[l>>2]|0;l=g;do{if((k|0)==3){n=+uz(l,f);o=(l|0)==(c[f>>2]|0);uu(l);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{p=n;q=c[6354]|0;break}}else{if((k|0)==1){p=+(g|0);q=j;break}else if((k|0)==2){p=m;q=j;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q-1;j=14296+(q*24&-1)|0;k=c[j>>2]|0;g=c[j+4>>2]|0;j=14304+(q*24&-1)|0;m=+h[j>>3];q=c[j>>2]|0;j=q;do{if((k|0)==3){n=+uz(j,e);l=(j|0)==(c[e>>2]|0);uu(j);if(!l){r=n;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((k|0)==2){r=m;break}else if((k|0)==1){r=+(q|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if(r<0.0|p<0.0){s=-1.0;t=9095}else{if(+bj(+r,d|0)!=0.0){s=-1.0;t=9095;break}q=p==0.0;if(q&r<2.0){s=-1.0;t=9095;break}do{if(r==0.0){u=+Z(+(-0.0-p))/p}else{if(q){u=1.0/(r+-1.0);break}if(p>3.0){m=1.0;n=1.0;w=p;x=0.0;y=1.0;k=0;while(1){z=r+ +(k|0);A=x*z+m;B=w+z*n;j=k+1|0;z=+(j|0);C=z*m+p*A;D=z*w+p*B;E=C/D;if(E==y){break}if(D<3.4028234663852886e+38){F=A;G=D;H=B;I=C}else{F=A/3.4028234663852886e+38;G=D/3.4028234663852886e+38;H=B/3.4028234663852886e+38;I=C/3.4028234663852886e+38}if((j|0)<333){m=I;n=H;w=G;x=F;y=E;k=j}else{break}}u=E*+Z(+(-0.0-p));break}y=-.5772156649015329- +_(+p);x=0.0;w=1.0;k=1;while(1){n=+(k|0);m=p*(-0.0-w)/n;J=y-m/n;if(J==x){break}j=k+1|0;if((j|0)<333){y=J;x=J;w=m;k=j}else{break}}if(r<=1.0){u=J;break}w=+Z(+(-0.0-p));x=J;y=1.0;while(1){m=(w-p*x)/y;n=y+1.0;if(n<r){x=m;y=n}else{u=m;break}}}}while(0);if(u>-1.0){K=u}else{s=u;t=9095}}}while(0);if((t|0)==9095){a[1960]=1;K=s}t=c[6354]|0;if((t|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{d=t+1|0;c[6354]=d;t=14296+(d*24&-1)|0;c[t>>2]=2;c[t+4>>2]=g;h[14304+(d*24&-1)>>3]=K;h[14312+(d*24&-1)>>3]=0.0;i=b;return}}function kt(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0.0,l=0.0,m=0,n=0.0,o=0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(d*24&-1)|0;k=+h[f>>3];d=c[f>>2]|0;f=d;do{if((g|0)==3){l=+uz(f,b);m=(f|0)==(c[b>>2]|0);uu(f);if(m){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{n=l;o=c[6354]|0;break}}else{if((g|0)==1){n=+(d|0);o=e;break}else if((g|0)==2){n=k;o=e;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((o|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{e=o+1|0;c[6354]=e;o=14296+(e*24&-1)|0;c[o>>2]=2;c[o+4>>2]=j;h[14304+(e*24&-1)>>3]=n;h[14312+(e*24&-1)>>3]=0.0;i=a;return}}function ku(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0.0,m=0,n=0,o=0.0,p=0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=c[14304+(d*24&-1)>>2]|0;do{if((g|0)==3){uz(f,b);k=(f|0)==(c[b>>2]|0);uu(f);if(k){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{l=0.0;m=c[6354]|0;n=9118;break}}else{if((g|0)==2){l=+h[14312+(d*24&-1)>>3];m=e;n=9118;break}else if((g|0)==1){o=0.0;p=e;break}uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if((n|0)==9118){o=l;p=m}if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{m=p+1|0;c[6354]=m;p=14296+(m*24&-1)|0;c[p>>2]=2;c[p+4>>2]=j;h[14304+(m*24&-1)>>3]=o;h[14312+(m*24&-1)>>3]=0.0;i=a;return}}function kv(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0.0,l=0.0,m=0.0,n=0,o=0.0,p=0,q=0,r=0.0,s=0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(d*24&-1)|0;k=+h[f>>3];l=+h[14312+(d*24&-1)>>3];d=c[f>>2]|0;f=d;do{if((g|0)==3){m=+uz(f,b);n=(f|0)==(c[b>>2]|0);uu(f);if(n){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{o=m;p=c[6354]|0;q=9132;break}}else{if((g|0)==1){r=(d|0)>-1?0.0:3.141592653589793;s=e;break}else if((g|0)==2){if(l==0.0){o=k;p=e;q=9132;break}r=+Y(+l,+k);s=e;break}else{uf(-1,210664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)==9132){r=o<0.0?3.141592653589793:0.0;s=p}if((s|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{o=r/+h[9040];p=s+1|0;c[6354]=p;s=14296+(p*24&-1)|0;c[s>>2]=2;c[s+4>>2]=j;h[14304+(p*24&-1)>>3]=o;h[14312+(p*24&-1)>>3]=0.0;i=a;return}}function kw(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0.0,l=0,m=0.0,n=0,o=0.0,p=0.0,q=0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(d*24&-1)|0;k=+h[f>>3];l=c[f>>2]|0;f=l;do{if((g|0)==3){m=+uz(f,b);n=(f|0)==(c[b>>2]|0);uu(f);if(n){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{o=0.0;p=m;q=c[6354]|0;break}}else{if((g|0)==2){o=+h[14312+(d*24&-1)>>3];p=k;q=e;break}else if((g|0)==1){o=0.0;p=+(l|0);q=e;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{e=q+1|0;c[6354]=e;q=14296+(e*24&-1)|0;c[q>>2]=2;c[q+4>>2]=j;h[14304+(e*24&-1)>>3]=p;h[14312+(e*24&-1)>>3]=-0.0-o;i=a;return}}function kx(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0.0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(m){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{n=0.0;o=+h[9040];p=l;q=9159;break}}else{l=+h[9040];if((f|0)==2){n=+h[14312+(d*24&-1)>>3];o=l;p=j;q=9159;break}else if((f|0)==1){r=+(k|0);s=+T(+(l*r));t=s*+a4(+(l*0.0));l=+h[9040];u=0.0;w=t;x=l;y=l*r;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)==9159){j=+T(+(o*p));r=j*+a4(+(n*o));o=+h[9040];u=n;w=r;x=o;y=o*p}p=+bz(+(x*u));q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{u=+S(+y)*p;k=q+1|0;c[6354]=k;q=14296+(k*24&-1)|0;c[q>>2]=2;c[q+4>>2]=g;h[14304+(k*24&-1)>>3]=w;h[14312+(k*24&-1)>>3]=u;i=a;return}}function ky(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0.0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(m){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{n=0.0;o=+h[9040];p=l;q=9172;break}}else{l=+h[9040];if((f|0)==2){n=+h[14312+(d*24&-1)>>3];o=l;p=j;q=9172;break}else if((f|0)==1){r=+(k|0);s=+S(+(l*r));t=s*+a4(+(l*0.0));l=+h[9040];u=0.0;w=t;x=l;y=l*r;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)==9172){j=+S(+(o*p));r=j*+a4(+(n*o));o=+h[9040];u=n;w=r;x=o;y=o*p}p=+bz(+(x*u));q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{u=p*(-0.0- +T(+y));k=q+1|0;c[6354]=k;q=14296+(k*24&-1)|0;c[q>>2]=2;c[q+4>>2]=g;h[14304+(k*24&-1)>>3]=w;h[14312+(k*24&-1)>>3]=u;i=a;return}}function kz(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0.0,m=0.0,n=0.0,o=0,p=0.0,q=0.0,r=0,s=0.0,t=0.0,u=0,w=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=c[g>>2]|0;k=c[g+4>>2]|0;g=14304+(e*24&-1)|0;l=+h[g>>3];m=+h[14312+(e*24&-1)>>3];e=c[g>>2]|0;g=e;do{if((j|0)==3){n=+uz(g,d);o=(g|0)==(c[d>>2]|0);uu(g);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{p=n;q=+h[9040];r=c[6354]|0;break}}else{if((j|0)==1){p=+(e|0);q=+h[9040];r=f;break}else if((j|0)==2){n=+h[9040];if(m==0.0){p=l;q=n;r=f;break}s=n*2.0;n=+S(+(s*l));t=n+ +a4(+(m*s));if(t==0.0){a[1960]=1;o=c[6354]|0;if((o|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}u=o+1|0;c[6354]=u;o=14296+(u*24&-1)|0;c[o>>2]=2;c[o+4>>2]=k;h[14304+(u*24&-1)>>3]=l;h[14312+(u*24&-1)>>3]=m;i=b;return}else{s=+h[9040]*2.0;n=+bz(+(m*s));u=c[6354]|0;if((u|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}w=+T(+(s*l))/t;o=u+1|0;c[6354]=o;u=14296+(o*24&-1)|0;c[u>>2]=2;c[u+4>>2]=k;h[14304+(o*24&-1)>>3]=w;h[14312+(o*24&-1)>>3]=n/t;i=b;return}}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((r|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=+U(+(q*p));f=r+1|0;c[6354]=f;r=14296+(f*24&-1)|0;c[r>>2]=2;c[r+4>>2]=k;h[14304+(f*24&-1)>>3]=l;h[14312+(f*24&-1)>>3]=0.0;i=b;return}function kA(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=+h[14312+(d*24&-1)>>3];d=c[e>>2]|0;e=d;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(!m){n=l;o=0.0;p=9209;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==1){n=+(d|0);o=0.0;p=9209;break}else if((f|0)==2){if(k==0.0){n=j;o=k;p=9209;break}else{q=j;r=k;break}}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((p|0)==9209){if(+P(+n)>1.0){q=n;r=o;break}f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=+h[9040];j=+W(+n)/k;d=f+1|0;c[6354]=d;f=14296+(d*24&-1)|0;c[f>>2]=2;c[f+4>>2]=g;h[14304+(d*24&-1)>>3]=j;h[14312+(d*24&-1)>>3]=0.0;i=a;return}}while(0);if(q==0.0){p=c[6354]|0;if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=+h[9040];o=(-0.0- +_(+(+Q(+(r*r+1.0))-r)))/n;d=p+1|0;c[6354]=d;p=14296+(d*24&-1)|0;c[p>>2]=2;c[p+4>>2]=g;h[14304+(d*24&-1)>>3]=0.0;h[14312+(d*24&-1)>>3]=o;i=a;return}else{o=q+1.0;n=r*r;j=+Q(+(n+o*o))*.5;o=q+-1.0;q=+Q(+(n+o*o))*.5;o=j-q;n=j+q;q=+h[9040];d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=+W(+(o>1.0?1.0:o))/q;o=(r>=0.0?1.0:-1.0)*+_(+(n+ +Q(+(n*n+-1.0))))/q;p=d+1|0;c[6354]=p;d=14296+(p*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(p*24&-1)>>3]=j;h[14312+(p*24&-1)>>3]=o;i=a;return}}function kB(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0.0,s=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=+h[14312+(d*24&-1)>>3];d=c[e>>2]|0;e=d;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(!m){n=l;o=0.0;p=9233;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){if(k==0.0){n=j;o=k;p=9233;break}else{q=j;r=k;break}}else if((f|0)==1){n=+(d|0);o=0.0;p=9233;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((p|0)==9233){if(+P(+n)>1.0){q=n;r=o;break}d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=+h[9040];j=+V(+n)/k;f=d+1|0;c[6354]=f;d=14296+(f*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(f*24&-1)>>3]=j;h[14312+(f*24&-1)>>3]=0.0;i=a;return}}while(0);n=q+1.0;o=r*r;j=+Q(+(o+n*n))*.5;n=q+-1.0;q=+Q(+(o+n*n))*.5;n=j+q;o=j-q;do{if(o>1.0){s=1.0}else{if(o>=-1.0){s=o;break}s=-1.0}}while(0);o=+h[9040];p=c[6354]|0;if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}q=+V(+s)/o;s=+_(+(n+ +Q(+(n*n+-1.0))))*(-0.0-(r>=0.0?1.0:-1.0))/o;f=p+1|0;c[6354]=f;p=14296+(f*24&-1)|0;c[p>>2]=2;c[p+4>>2]=g;h[14304+(f*24&-1)>>3]=q;h[14312+(f*24&-1)>>3]=s;i=a;return}function kC(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0.0,q=0,r=0,s=0.0,t=0,u=0.0,w=0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=g|0;k=c[j>>2]|0;l=g+4|0;g=c[l>>2]|0;m=14304+(e*24&-1)|0;n=+h[m>>3];o=14312+(e*24&-1)|0;p=+h[o>>3];q=c[m>>2]|0;r=q;do{if((k|0)==3){s=+uz(r,d);t=(r|0)==(c[d>>2]|0);uu(r);if(t){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{u=s;w=c[6354]|0;break}}else{if((k|0)==2){if(p==0.0){u=n;w=f;break}do{if(n==0.0){if(+P(+p)<1.0){break}a[1960]=1;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e;c[j>>2]=2;c[l>>2]=g;uE(m|0,0,16);i=b;return}}while(0);if(n<0.0){x=-0.0-p;y=-0.0-n}else{x=p;y=n}s=y*y;z=+X(+(y*2.0/(1.0-s-x*x)));A=x+1.0;B=x+-1.0;C=+_(+((s+A*A)/(s+B*B)))*.25;if(z<0.0){D=z+3.141592653589793}else{D=z}if(n<0.0){E=-0.0-D;F=-0.0-C}else{E=D;F=C}if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}C=E*.5/+h[9040];c[6354]=e;c[j>>2]=2;c[l>>2]=g;h[m>>3]=C;h[o>>3]=F;i=b;return}else if((k|0)==1){u=+(q|0);w=f;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((w|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}F=+h[9040];E=+X(+u)/F;f=w+1|0;c[6354]=f;w=14296+(f*24&-1)|0;c[w>>2]=2;c[w+4>>2]=g;h[14304+(f*24&-1)>>3]=E;h[14312+(f*24&-1)>>3]=0.0;i=b;return}function kD(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0,p=0.0,q=0,r=0.0,s=0.0,t=0.0,u=0;b=i;i=i+24|0;d=b|0;e=b+8|0;f=b+16|0;g=c[6354]|0;if((g|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=g-1|0;c[6354]=j;k=c[14296+(g*24&-1)>>2]|0;l=14304+(g*24&-1)|0;m=+h[l>>3];g=c[l>>2]|0;h[f>>3]=m;l=g;g=l;do{if((k|0)==3){n=+uz(g,e);o=(g|0)==(c[e>>2]|0);uu(g);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[f>>3]=n;p=n;q=c[6354]|0;break}}else{if((k|0)==2){p=m;q=j;break}else if((k|0)==1){p=+(l|0);q=j;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=q-1;j=14296+(q*24&-1)|0;l=c[j>>2]|0;k=c[j+4>>2]|0;j=14304+(q*24&-1)|0;m=+h[j>>3];g=c[j>>2]|0;h[f>>3]=m;n=+h[14312+(q*24&-1)>>3];q=g;g=q;do{if((l|0)==3){r=+uz(g,d);j=(g|0)==(c[d>>2]|0);uu(g);if(j){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[f>>3]=r;s=r;t=0.0;break}}else{if((l|0)==2){s=m;t=n;break}else if((l|0)==1){s=+(q|0);t=n;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if(p==0.0&s==0.0){a[1960]=1;c[f>>2]=0;q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{l=q+1|0;c[6354]=l;q=14296+(l*24&-1)|0;c[q>>2]=1;c[q+4>>2]=k;h[14304+(l*24&-1)>>3]=+h[f>>3];h[14312+(l*24&-1)>>3]=t;u=l;break}}else{u=c[6354]|0}}while(0);t=+Y(+s,+p);p=t/+h[9040];h[f>>3]=p;if((u|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{f=u+1|0;c[6354]=f;u=14296+(f*24&-1)|0;c[u>>2]=2;c[u+4>>2]=k;h[14304+(f*24&-1)>>3]=p;h[14312+(f*24&-1)>>3]=0.0;i=b;return}}function kE(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(!m){n=0.0;o=l;p=9311;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{l=+h[14312+(d*24&-1)>>3];if((f|0)==1){q=+(k|0);r=+bz(+q);s=0.0;t=r;u=+a4(+q);break}else if((f|0)==2){n=l;o=j;p=9311;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((p|0)==9311){j=+bz(+o);l=j*+S(+n);s=n;t=l;u=+a4(+o)}p=c[6354]|0;if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{o=u*+T(+s);f=p+1|0;c[6354]=f;p=14296+(f*24&-1)|0;c[p>>2]=2;c[p+4>>2]=g;h[14304+(f*24&-1)>>3]=t;h[14312+(f*24&-1)>>3]=o;i=a;return}}function kF(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(!m){n=0.0;o=l;p=9323;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{l=+h[14312+(d*24&-1)>>3];if((f|0)==1){q=+(k|0);r=+a4(+q);s=0.0;t=r;u=+bz(+q);break}else if((f|0)==2){n=l;o=j;p=9323;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((p|0)==9323){j=+a4(+o);l=j*+S(+n);s=n;t=l;u=+bz(+o)}p=c[6354]|0;if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{o=u*+T(+s);f=p+1|0;c[6354]=f;p=14296+(f*24&-1)|0;c[p>>2]=2;c[p+4>>2]=g;h[14304+(f*24&-1)>>3]=t;h[14312+(f*24&-1)>>3]=o;i=a;return}}function kG(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0,n=0.0,o=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(!m){n=0.0;o=l;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){n=+h[14312+(d*24&-1)>>3];o=j;break}else if((f|0)==1){n=0.0;o=+(k|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);j=o*2.0;o=n*2.0;if(+P(+j)>706.893623549172){k=c[6354]|0;if((k|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=k+1|0;c[6354]=f;k=14296+(f*24&-1)|0;c[k>>2]=2;c[k+4>>2]=g;h[14304+(f*24&-1)>>3]=j<0.0?-1.0:1.0;h[14312+(f*24&-1)>>3]=0.0;i=a;return}else{n=+a4(+j);l=n+ +S(+o);n=+bz(+j);f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}j=+T(+o)/l;k=f+1|0;c[6354]=k;f=14296+(k*24&-1)|0;c[f>>2]=2;c[f+4>>2]=g;h[14304+(k*24&-1)>>3]=n/l;h[14312+(k*24&-1)>>3]=j;i=a;return}}function kH(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0,m=0.0,n=0,o=0.0,p=0.0,q=0,r=0.0,s=0.0,t=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;k=+h[f>>3];l=c[f>>2]|0;f=l;do{if((g|0)==3){m=+uz(f,d);n=(f|0)==(c[d>>2]|0);uu(f);if(!n){o=0.0;p=m;q=9353;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((g|0)==2){o=+h[14312+(e*24&-1)>>3];p=k;q=9353;break}else if((g|0)==1){r=+(l|0);s=0.0;t=-0.0;break}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)==9353){r=p;s=o;t=-0.0-o}if(r==0.0){q=+P(+t)>1.0;l=c[6354]|0;g=(l|0)==249;if(q){if(g){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}q=l+1|0;c[6354]=q;e=14296+(q*24&-1)|0;c[e>>2]=2;c[e+4>>2]=j;uE(14304+(q*24&-1)|0,0,16);a[1960]=1;i=b;return}else{if(g){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=+h[9040];p=(-0.0- +W(+t))/o;g=l+1|0;c[6354]=g;l=14296+(g*24&-1)|0;c[l>>2]=2;c[l+4>>2]=j;h[14304+(g*24&-1)>>3]=0.0;h[14312+(g*24&-1)>>3]=p;i=b;return}}else{if(s==-0.0){g=c[6354]|0;if((g|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=r+ +Q(+(r*r+1.0));o=+h[9040];t=+_(+p)/o;l=g+1|0;c[6354]=l;g=14296+(l*24&-1)|0;c[g>>2]=2;c[g+4>>2]=j;h[14304+(l*24&-1)>>3]=t;h[14312+(l*24&-1)>>3]=0.0;i=b;return}else{t=1.0-s;o=r*r;p=+Q(+(t*t+o))*.5;t=-1.0-s;s=+Q(+(t*t+o))*.5;o=p+s;t=+h[9040];l=c[6354]|0;if((l|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=(r>=0.0?1.0:-1.0)*+_(+(+Q(+(o*o+-1.0))+o))/t;o=(-0.0- +W(+(p-s)))/t;g=l+1|0;c[6354]=g;l=14296+(g*24&-1)|0;c[l>>2]=2;c[l+4>>2]=j;h[14304+(g*24&-1)>>3]=k;h[14312+(g*24&-1)>>3]=o;i=b;return}}}function kI(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0.0,p=0,q=0,r=0.0,s=0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=f|0;j=c[g>>2]|0;k=f+4|0;f=c[k>>2]|0;l=14304+(d*24&-1)|0;m=+h[l>>3];n=14312+(d*24&-1)|0;o=+h[n>>3];p=c[l>>2]|0;q=p;do{if((j|0)==3){r=+uz(q,b);s=(q|0)==(c[b>>2]|0);uu(q);if(!s){t=r;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((j|0)==2){if(o==0.0){t=m;break}r=m+1.0;u=o*o;w=+Q(+(u+r*r))*.5;r=m+-1.0;x=+Q(+(u+r*r))*.5;r=w+x;u=+h[9040];if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}y=+_(+(+Q(+(r*r+-1.0))+r))/u;r=(o<0.0?-1.0:1.0)*+V(+(w-x))/u;c[6354]=d;c[g>>2]=2;c[k>>2]=f;h[l>>3]=y;h[n>>3]=r;i=a;return}else if((j|0)==1){t=+(p|0);break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);p=+P(+t)>1.0;j=c[6354]|0;n=(j|0)==249;if(p){if(n){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=t+ +Q(+(t*t+-1.0));m=+h[9040];r=+_(+o)/m;p=j+1|0;c[6354]=p;l=14296+(p*24&-1)|0;c[l>>2]=2;c[l+4>>2]=f;h[14304+(p*24&-1)>>3]=r;h[14312+(p*24&-1)>>3]=0.0;i=a;return}else{if(n){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}r=+h[9040];m=+V(+t)/r;n=j+1|0;c[6354]=n;j=14296+(n*24&-1)|0;c[j>>2]=2;c[j+4>>2]=f;h[14304+(n*24&-1)>>3]=0.0;h[14312+(n*24&-1)>>3]=m;i=a;return}}function kJ(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0,m=0.0,n=0,o=0.0,p=0.0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;k=+h[f>>3];l=c[f>>2]|0;f=l;do{if((g|0)==3){m=+uz(f,d);n=(f|0)==(c[d>>2]|0);uu(f);if(!n){o=0.0;p=m;q=9405;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((g|0)==2){o=+h[14312+(e*24&-1)>>3];p=k;q=9405;break}else if((g|0)==1){r=+(l|0);s=0.0;t=-0.0;break}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if((q|0)==9405){r=p;s=o;t=-0.0-o}if(r==0.0){q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=+h[9040];p=(-0.0- +X(+t))/o;l=q+1|0;c[6354]=l;q=14296+(l*24&-1)|0;c[q>>2]=2;c[q+4>>2]=j;h[14304+(l*24&-1)>>3]=0.0;h[14312+(l*24&-1)>>3]=p;i=b;return}do{if(s==-0.0){if(+P(+r)<1.0){break}a[1960]=1;l=c[6354]|0;if((l|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}q=l+1|0;c[6354]=q;l=14296+(q*24&-1)|0;c[l>>2]=2;c[l+4>>2]=j;uE(14304+(q*24&-1)|0,0,16);i=b;return}}while(0);if(s>-0.0){u=-0.0-r;w=s}else{u=r;w=t}t=w*w;r=+X(+(w*2.0/(1.0-t-u*u)));w=u+1.0;p=u+-1.0;u=+_(+((t+w*w)/(t+p*p)))*.25;if(r<0.0){x=r+3.141592653589793}else{x=r}if(s>-0.0){y=-0.0-x;z=-0.0-u}else{y=x;z=u}q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}u=y*-.5/+h[9040];l=q+1|0;c[6354]=l;q=14296+(l*24&-1)|0;c[q>>2]=2;c[q+4>>2]=j;h[14304+(l*24&-1)>>3]=z;h[14312+(l*24&-1)>>3]=u;i=b;return}function kK(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0.0,m=0.0,n=0.0,o=0,p=0.0,q=0,r=0.0,s=0,t=0.0,u=0.0,w=0,x=0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;l=+h[f>>3];m=+h[14312+(e*24&-1)>>3];e=c[f>>2]|0;f=e;do{if((g|0)==3){n=+uz(f,d);o=(f|0)==(c[d>>2]|0);uu(f);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=0.0;q=c[k>>2]|0;r=n;s=9435;break}}else{if((g|0)==2){p=m;q=e;r=l;s=9435;break}else if((g|0)==1){t=0.0;u=m;w=1;x=e;y=l;break}uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if((s|0)==9435){t=p;u=p;w=2;x=q;y=r}r=+P(+t);if(r>+h[11]){uf(-1,136968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((w|0)==1){z=+(x|0)}else if((w|0)==2){z=y}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}r=(1.0-z)*(z+1.0);if(r>0.0){A=1.0;B=r;C=0.0}else{x=c[6354]|0;if((x|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}q=x+1|0;c[6354]=q;x=14296+(q*24&-1)|0;c[x>>2]=w;c[x+4>>2]=j;h[14304+(q*24&-1)>>3]=y;h[14312+(q*24&-1)>>3]=u;a[1960]=1;i=b;return}while(1){u=+Q(+C);y=+Q(+B);r=+Q(+A);z=y*r+u*(y+r);r=(C+z)*.25;y=(B+z)*.25;u=(A+z)*.25;D=(u+(r+y))*.3333333333333333;E=(D-r)/D;F=(D-y)/D;G=(D-u)/D;if(+P(+E)>.0025){A=u;B=y;C=r;continue}if(+P(+F)>.0025){A=u;B=y;C=r;continue}if(+P(+G)>.0025){A=u;B=y;C=r}else{break}}C=E*F;F=C-G*G;E=C*G;q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}G=(E*.07142857142857142+(F*(F*.041666666666666664+-.1-E*.06818181818181818)+1.0))/+Q(+D);x=q+1|0;c[6354]=x;q=14296+(x*24&-1)|0;c[q>>2]=2;c[q+4>>2]=j;h[14304+(x*24&-1)>>3]=G;h[14312+(x*24&-1)>>3]=0.0;i=b;return}function kL(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0.0,m=0.0,n=0.0,o=0,p=0.0,q=0,r=0.0,s=0,t=0.0,u=0.0,w=0,x=0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;l=+h[f>>3];m=+h[14312+(e*24&-1)>>3];e=c[f>>2]|0;f=e;do{if((g|0)==3){n=+uz(f,d);o=(f|0)==(c[d>>2]|0);uu(f);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=0.0;q=c[k>>2]|0;r=n;s=9462;break}}else{if((g|0)==1){t=0.0;u=m;w=1;x=e;y=l;break}else if((g|0)==2){p=m;q=e;r=l;s=9462;break}uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);if((s|0)==9462){t=p;u=p;w=2;x=q;y=r}r=+P(+t);if(r>+h[11]){uf(-1,136968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((w|0)==1){z=+(x|0)}else if((w|0)==2){z=y}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}r=(1.0-z)*(z+1.0);if(r>0.0){A=1.0;B=r;C=0.0}else{if(r<0.0){a[1960]=1;x=c[6354]|0;if((x|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}q=x+1|0;c[6354]=q;x=14296+(q*24&-1)|0;c[x>>2]=w;c[x+4>>2]=j;h[14304+(q*24&-1)>>3]=y;h[14312+(q*24&-1)>>3]=u;i=b;return}else{q=c[6354]|0;if((q|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}x=q+1|0;c[6354]=x;q=14296+(x*24&-1)|0;c[q>>2]=2;c[q+4>>2]=j;h[14304+(x*24&-1)>>3]=1.0;h[14312+(x*24&-1)>>3]=0.0;i=b;return}}while(1){u=+Q(+C);y=+Q(+B);t=+Q(+A);p=y*t+u*(y+t);t=(C+p)*.25;y=(B+p)*.25;u=(A+p)*.25;D=(u+(t+y))*.3333333333333333;E=(D-t)/D;F=(D-y)/D;G=(D-u)/D;if(+P(+E)>.0025){A=u;B=y;C=t;continue}if(+P(+F)>.0025){A=u;B=y;C=t;continue}if(+P(+G)>.0025){A=u;B=y;C=t}else{break}}C=E*F;F=C-G*G;E=C*G;G=(E*.07142857142857142+(F*(F*.041666666666666664+-.1-E*.06818181818181818)+1.0))/+Q(+D);D=1.0;E=1.0;F=r;r=0.0;C=0.0;while(1){B=+Q(+r);A=+Q(+F);t=+Q(+E);y=A*t+B*(A+t);A=E+y;H=C+D/(t*A);I=D*.25;t=(r+y)*.25;B=(F+y)*.25;y=A*.25;J=(t+B+y*3.0)*.2;K=(J-t)/J;L=(J-B)/J;M=(J-y)/J;if(+P(+K)>.0015){D=I;E=y;F=B;r=t;C=H;continue}if(+P(+L)>.0015){D=I;E=y;F=B;r=t;C=H;continue}if(+P(+M)>.0015){D=I;E=y;F=B;r=t;C=H}else{break}}C=K*L;L=M*M;K=C-L;r=C-L*6.0;L=K+(K+r);x=c[6354]|0;if((x|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}F=G-z*z*(H*3.0+I*(M*(M*(C*M*.11538461538461539+K*-.4090909090909091)+L*.16666666666666666)+(r*(r*.10227272727272728+-.21428571428571427-M*.17307692307692307*L)+1.0))/(J*+Q(+J)))/3.0;q=x+1|0;c[6354]=q;x=14296+(q*24&-1)|0;c[x>>2]=2;c[x+4>>2]=j;h[14304+(q*24&-1)>>3]=F;h[14312+(q*24&-1)>>3]=0.0;i=b;return}function kM(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0,m=0,n=0.0,o=0.0,p=0.0,q=0,r=0,s=0.0,t=0,u=0,w=0.0,x=0,y=0,z=0.0,A=0,B=0.0,C=0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,R=0.0,S=0.0,T=0.0,U=0.0,V=0.0,W=0.0,X=0.0,Y=0.0,Z=0.0,_=0.0,$=0.0,aa=0.0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0.0,ai=0.0,aj=0.0,ak=0.0,al=0.0,am=0.0,an=0.0,ao=0.0,ap=0.0,aq=0.0,ar=0.0,as=0.0;b=i;i=i+16|0;d=b|0;e=b+8|0;f=c[6354]|0;if((f|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=f-1|0;c[6354]=g;j=14296+(f*24&-1)|0;l=c[j>>2]|0;m=c[j+4>>2]|0;j=14304+(f*24&-1)|0;n=+h[j>>3];o=+h[14312+(f*24&-1)>>3];f=c[j>>2]|0;j=f;do{if((l|0)==3){p=+uz(j,e);q=(j|0)==(c[e>>2]|0);uu(j);if(q){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=p;q=c[k>>2]|0;r=2;s=0.0;t=c[6354]|0;u=q;w=p;x=q;break}}else{r=l;s=o;t=g;u=f;w=n;x=j}}while(0);if((t|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=t-1;j=14296+(t*24&-1)|0;f=c[j>>2]|0;g=c[j+4>>2]|0;j=14304+(t*24&-1)|0;n=+h[j>>3];o=+h[14312+(t*24&-1)>>3];t=c[j>>2]|0;j=t;do{if((f|0)==3){p=+uz(j,d);l=(j|0)==(c[d>>2]|0);uu(j);if(l){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=p;l=c[k>>2]|0;y=2;z=0.0;A=l;B=p;C=l;break}}else{y=f;z=o;A=t;B=n;C=j}}while(0);if((r|0)==2){D=s}else if((r|0)==1){D=0.0}else if((r|0)==3){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}n=+P(+D);D=+h[11];if(n>D){uf(-1,136968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((y|0)==1){E=0.0}else if((y|0)==3){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((y|0)==2){E=z}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(+P(+E)>D){uf(-1,136968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((r|0)==1){F=+(u|0)}else if((r|0)==2){F=w}else if((r|0)==3){F=+uz(x,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((y|0)==3){G=+uz(C,0)}else if((y|0)==2){G=B}else if((y|0)==1){G=+(A|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}B=(1.0-F)*(F+1.0);if(B>0.0&G<1.0){H=1.0;I=B;J=0.0}else{a[1960]=1;A=c[6354]|0;if((A|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}y=A+1|0;c[6354]=y;A=14296+(y*24&-1)|0;c[A>>2]=r;c[A+4>>2]=m;h[14304+(y*24&-1)>>3]=w;h[14312+(y*24&-1)>>3]=s;h[k>>3]=w;y=c[k>>2]|0;if((r|0)!=3|(y|0)==0){i=b;return}r=bP(y|0)|0;c[14304+((c[6354]|0)*24&-1)>>2]=r;i=b;return}while(1){w=+Q(+J);s=+Q(+I);F=+Q(+H);D=s*F+w*(s+F);F=(J+D)*.25;s=(I+D)*.25;w=(H+D)*.25;K=(w+(F+s))*.3333333333333333;L=(K-F)/K;M=(K-s)/K;N=(K-w)/K;if(+P(+L)>.0025){H=w;I=s;J=F;continue}if(+P(+M)>.0025){H=w;I=s;J=F;continue}if(+P(+N)>.0025){H=w;I=s;J=F}else{break}}J=L*M;M=J-N*N;L=J*N;N=(L*.07142857142857142+(M*(M*.041666666666666664+-.1-L*.06818181818181818)+1.0))/+Q(+K);K=1.0-G;if(K>0.0){O=1.0;R=B;S=0.0;T=0.0;U=K;V=0.0}else{L=B<0.0?0.0:B;M=L>1.0?L:1.0;L=B+0.0+1.0-M;B=1.0/(L-K);J=L*(M-L)*B;I=L+J;H=M*0.0/L;F=K*I/L;if(F>0.0){W=1.0;X=F;Y=H}else{s=H-F;w=+Q(+H);W=w/+Q(+s);X=-0.0-F;Y=s}s=X;X=Y;do{Y=+Q(+X)*2.0;F=s+Y*+Q(+s);X=(X+F)*.25;s=(s+F)*.25;Z=(s+(X+s))*.3333333333333333;_=(s-Z)/Z;}while(+P(+_)>.0012);O=M;R=L;S=B;T=J;U=I;V=W*(_*_*(_*(_*(_*.4090909090909091+.375)+.14285714285714285)+.3)+1.0)/+Q(+Z)}Z=O;O=R;R=0.0;_=0.0;W=1.0;I=U;while(1){U=+Q(+R);J=+Q(+O);B=+Q(+Z);L=J*B+U*(J+B);M=U*J*B+I*(U+J+B);B=M*M;M=I+L;J=I*M*M;if(J>0.0){$=1.0;aa=J;ab=B}else{U=B-J;s=+Q(+B);$=s/+Q(+U);aa=-0.0-J;ab=U}U=aa;J=ab;do{s=+Q(+J)*2.0;B=U+s*+Q(+U);J=(J+B)*.25;U=(U+B)*.25;ac=(U+(J+U))*.3333333333333333;ad=(U-ac)/ac;}while(+P(+ad)>.0012);ae=_+W*($*(ad*ad*(ad*(ad*(ad*.4090909090909091+.375)+.14285714285714285)+.3)+1.0)/+Q(+ac));af=W*.25;ag=(R+L)*.25;ah=(O+L)*.25;ai=(Z+L)*.25;U=M*.25;aj=(U+(U+(ai+(ag+ah))))*.2;ak=(aj-ag)/aj;al=(aj-ah)/aj;am=(aj-ai)/aj;an=(aj-U)/aj;if(+P(+ak)>.0015){Z=ai;O=ah;R=ag;_=ae;W=af;I=U;continue}if(+P(+al)>.0015){Z=ai;O=ah;R=ag;_=ae;W=af;I=U;continue}if(+P(+am)>.0015){Z=ai;O=ah;R=ag;_=ae;W=af;I=U;continue}if(+P(+an)>.0015){Z=ai;O=ah;R=ag;_=ae;W=af;I=U}else{break}}I=al*am+ak*(al+am);W=ak*al*am;am=an*an;al=I-am*3.0;ak=ae*3.0+af*((.3333333333333333-an*.13636363636363635)*an*I+(W*(an*(an*.11538461538461539+-.2727272727272727)+.16666666666666666)+(al*(al*.10227272727272727+-.21428571428571427-(W+an*2.0*(I-am))*.17307692307692307)+1.0))-an*.3333333333333333*am)/(aj*+Q(+aj));if(K>0.0){ao=ak}else{K=ai;ai=ah;ah=ag;while(1){ag=+Q(+ah);aj=+Q(+ai);am=+Q(+K);an=aj*am+ag*(aj+am);am=(ah+an)*.25;aj=(ai+an)*.25;ag=(K+an)*.25;ap=(ag+(am+aj))*.3333333333333333;aq=(ap-am)/ap;ar=(ap-aj)/ap;as=(ap-ag)/ap;if(+P(+aq)>.0025){K=ag;ai=aj;ah=am;continue}if(+P(+ar)>.0025){K=ag;ai=aj;ah=am;continue}if(+P(+as)>.0025){K=ag;ai=aj;ah=am}else{break}}ah=aq*ar;ar=ah-as*as;aq=ah*as;ao=S*(T*ak+(V-(aq*.07142857142857142+(ar*(ar*.041666666666666664+-.1-aq*.06818181818181818)+1.0))/+Q(+ap))*3.0)}r=c[6354]|0;if((r|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}y=r+1|0;c[6354]=y;r=14296+(y*24&-1)|0;c[r>>2]=2;c[r+4>>2]=g;h[14304+(y*24&-1)>>3]=N+G*ao/3.0;h[14312+(y*24&-1)>>3]=0.0;i=b;return}function kN(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0;a=i;i=i+16|0;b=a|0;d=a+8|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=c[g>>2]|0;k=c[g+4>>2]|0;g=14304+(e*24&-1)|0;l=+h[g>>3];m=c[g>>2]|0;h[d>>3]=l;n=+h[14312+(e*24&-1)>>3];e=m;m=e;do{if((j|0)==3){o=+uz(m,b);g=(m|0)==(c[b>>2]|0);uu(m);if(g){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[d>>3]=o;p=o;q=0.0;r=c[6354]|0;break}}else{if((j|0)==2){p=l;q=n;r=f;break}else if((j|0)==1){p=+(e|0);q=n;r=f;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);c[d>>2]=~~p;if((r|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{f=r+1|0;c[6354]=f;r=14296+(f*24&-1)|0;c[r>>2]=1;c[r+4>>2]=k;h[14304+(f*24&-1)>>3]=+h[d>>3];h[14312+(f*24&-1)>>3]=q;i=a;return}}function kO(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0,q=0.0,r=0,s=0.0,t=0,u=0.0,w=0.0,x=0.0;a=i;i=i+16|0;b=a|0;d=a+8|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=g|0;k=c[j>>2]|0;l=g+4|0;g=c[l>>2]|0;m=14304+(e*24&-1)|0;n=+h[m>>3];o=c[m>>2]|0;h[d>>3]=n;p=14312+(e*24&-1)|0;q=+h[p>>3];r=o;o=r;do{if((k|0)==3){s=+uz(o,b);t=(o|0)==(c[b>>2]|0);uu(o);if(t){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[d>>3]=s;u=0.0;w=s;break}}else{if((k|0)==2){u=q;w=n;break}else if((k|0)!=1){uf(-1,205760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[d>>2]=(r|0)>-1?r:-r|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e;c[j>>2]=1;c[l>>2]=g;h[m>>3]=+h[d>>3];h[p>>3]=q;i=a;return}}while(0);q=+P(+w);w=+P(+u);do{if(u==0.0){x=q}else{if(q>w){n=w/q;x=q*+Q(+(n*n+1.0));break}else{n=q/w;x=w*+Q(+(n*n+1.0));break}}}while(0);h[d>>3]=x;d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=d+1|0;c[6354]=p;d=14296+(p*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(p*24&-1)>>3]=x;h[14312+(p*24&-1)>>3]=0.0;i=a;return}function kP(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0,q=0.0,r=0,s=0.0,t=0,u=0.0,w=0.0,x=0;a=i;i=i+16|0;b=a|0;d=a+8|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=g|0;k=c[j>>2]|0;l=g+4|0;g=c[l>>2]|0;m=14304+(e*24&-1)|0;n=+h[m>>3];o=c[m>>2]|0;h[d>>3]=n;p=14312+(e*24&-1)|0;q=+h[p>>3];r=o;o=r;do{if((k|0)==3){s=+uz(o,b);t=(o|0)==(c[b>>2]|0);uu(o);if(t){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[d>>3]=s;u=0.0;w=s;break}}else{if((k|0)==2){u=q;w=n;break}else if((k|0)!=1){uf(-1,205760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[d>>2]=(r|0)>0?1:r>>31;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e;c[j>>2]=1;c[l>>2]=g;h[m>>3]=+h[d>>3];h[p>>3]=q;i=a;return}}while(0);if(w>0.0){x=1}else{x=(w<0.0)<<31>>31}c[d>>2]=x;x=c[6354]|0;if((x|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=x+1|0;c[6354]=p;x=14296+(p*24&-1)|0;c[x>>2]=1;c[x+4>>2]=g;h[14304+(p*24&-1)>>3]=+h[d>>3];h[14312+(p*24&-1)>>3]=u;i=a;return}function kQ(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0.0,s=0.0,t=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(!m){n=0.0;o=l;p=9618;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==2){n=+h[14312+(d*24&-1)>>3];o=j;p=9618;break}else if((f|0)==1){q=+(k|0);r=+Q(+(+(((k|0)>-1?k:-k|0)|0)));break}else{uf(-1,211136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((p|0)==9618){j=+P(+o);l=+P(+n);k=n==0.0;if(k){q=o;r=+Q(+j);break}if(j>l){s=l/j;t=+Q(+(j*+Q(+(s*s+1.0))))}else{s=j/l;j=+Q(+(l*+Q(+(s*s+1.0))));if(k){q=o;r=j;break}else{t=j}}j=+Y(+n,+o)*.5;k=c[6354]|0;if((k|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}s=t*+S(+j);l=t*+T(+j);f=k+1|0;c[6354]=f;k=14296+(f*24&-1)|0;c[k>>2]=2;c[k+4>>2]=g;h[14304+(f*24&-1)>>3]=s;h[14312+(f*24&-1)>>3]=l;i=a;return}}while(0);p=c[6354]|0;f=(p|0)==249;if(q<0.0){if(f){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=p+1|0;c[6354]=k;d=14296+(k*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(k*24&-1)>>3]=0.0;h[14312+(k*24&-1)>>3]=r;i=a;return}else{if(f){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=p+1|0;c[6354]=f;p=14296+(f*24&-1)|0;c[p>>2]=2;c[p+4>>2]=g;h[14304+(f*24&-1)>>3]=r;h[14312+(f*24&-1)>>3]=0.0;i=a;return}}function kR(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0.0,m=0,n=0.0,o=0.0,p=0,q=0.0,r=0.0;a=i;i=i+8|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=+h[14312+(d*24&-1)>>3];d=c[e>>2]|0;e=d;do{if((f|0)==3){l=+uz(e,b);m=(e|0)==(c[b>>2]|0);uu(e);if(!m){n=l;o=0.0;p=2;break}uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{if((f|0)==1){n=+(d|0);o=k;p=1;break}else if((f|0)==2){n=j;o=k;p=2;break}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);if(n<-706.893623549172){q=0.0}else{q=+Z(+n)}if((p|0)==2){r=o}else if((p|0)==1){r=0.0}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=c[6354]|0;if((p|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{o=q*+S(+r);n=q*+T(+r);f=p+1|0;c[6354]=f;p=14296+(f*24&-1)|0;c[p>>2]=2;c[p+4>>2]=g;h[14304+(f*24&-1)>>3]=o;h[14312+(f*24&-1)>>3]=n;i=a;return}}function kS(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0.0,m=0.0,n=0.0,o=0,p=0.0,q=0.0,r=0,s=0,t=0.0,u=0.0,w=0,x=0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;l=+h[f>>3];m=+h[14312+(e*24&-1)>>3];e=c[f>>2]|0;f=e;do{if((g|0)==3){n=+uz(f,d);o=(f|0)==(c[d>>2]|0);uu(f);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=0.0;q=n;r=c[k>>2]|0;s=9662;break}}else{if((g|0)==1){t=+(((e|0)>-1?e:-e|0)|0);u=m;w=1;x=e;y=l;break}else if((g|0)==2){p=m;q=l;r=e;s=9662;break}else{uf(-1,211136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((s|0)==9662){l=+P(+q);m=+P(+p);if(p==0.0){t=l;u=p;w=2;x=r;y=q;break}if(l>m){n=m/l;t=l*+Q(+(n*n+1.0));u=p;w=2;x=r;y=q;break}else{n=l/m;t=m*+Q(+(n*n+1.0));u=p;w=2;x=r;y=q;break}}}while(0);if(t==0.0){a[1960]=1;r=c[6354]|0;if((r|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}s=r+1|0;c[6354]=s;r=14296+(s*24&-1)|0;c[r>>2]=w;c[r+4>>2]=j;h[14304+(s*24&-1)>>3]=y;h[14312+(s*24&-1)>>3]=u;i=b;return}L13201:do{if((w|0)==1){z=(x|0)>-1?0.0:3.141592653589793;A=+_(+(+(((x|0)>-1?x:-x|0)|0)))/2.302585092994046}else if((w|0)==2){t=+P(+y);q=+P(+u);s=u==0.0;do{if(s){B=+_(+t)/2.302585092994046}else{if(t>q){p=q/t;C=+_(+(t*+Q(+(p*p+1.0))))/2.302585092994046}else{p=t/q;n=+_(+(q*+Q(+(p*p+1.0))))/2.302585092994046;if(s){B=n;break}else{C=n}}z=+Y(+u,+y);A=C;break L13201}}while(0);z=y<0.0?3.141592653589793:0.0;A=B}else{uf(-1,211136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);w=c[6354]|0;if((w|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}x=w+1|0;c[6354]=x;w=14296+(x*24&-1)|0;c[w>>2]=2;c[w+4>>2]=j;h[14304+(x*24&-1)>>3]=A;h[14312+(x*24&-1)>>3]=z/2.302585092994046;i=b;return}function kT(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,l=0.0,m=0.0,n=0.0,o=0,p=0.0,q=0.0,r=0,s=0,t=0.0,u=0.0,w=0,x=0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0;b=i;i=i+8|0;d=b|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e-1;f=14296+(e*24&-1)|0;g=c[f>>2]|0;j=c[f+4>>2]|0;f=14304+(e*24&-1)|0;l=+h[f>>3];m=+h[14312+(e*24&-1)>>3];e=c[f>>2]|0;f=e;do{if((g|0)==3){n=+uz(f,d);o=(f|0)==(c[d>>2]|0);uu(f);if(o){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[k>>3]=n;p=0.0;q=n;r=c[k>>2]|0;s=9695;break}}else{if((g|0)==2){p=m;q=l;r=e;s=9695;break}else if((g|0)==1){t=+(((e|0)>-1?e:-e|0)|0);u=m;w=1;x=e;y=l;break}else{uf(-1,211136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);do{if((s|0)==9695){l=+P(+q);m=+P(+p);if(p==0.0){t=l;u=p;w=2;x=r;y=q;break}if(l>m){n=m/l;t=l*+Q(+(n*n+1.0));u=p;w=2;x=r;y=q;break}else{n=l/m;t=m*+Q(+(n*n+1.0));u=p;w=2;x=r;y=q;break}}}while(0);if(t==0.0){a[1960]=1;r=c[6354]|0;if((r|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}s=r+1|0;c[6354]=s;r=14296+(s*24&-1)|0;c[r>>2]=w;c[r+4>>2]=j;h[14304+(s*24&-1)>>3]=y;h[14312+(s*24&-1)>>3]=u;i=b;return}L13245:do{if((w|0)==1){z=(x|0)>-1?0.0:3.141592653589793;A=+_(+(+(((x|0)>-1?x:-x|0)|0)))}else if((w|0)==2){t=+P(+y);q=+P(+u);s=u==0.0;do{if(s){B=+_(+t)}else{if(t>q){p=q/t;C=+_(+(t*+Q(+(p*p+1.0))))}else{p=t/q;n=+_(+(q*+Q(+(p*p+1.0))));if(s){B=n;break}else{C=n}}z=+Y(+u,+y);A=C;break L13245}}while(0);z=y<0.0?3.141592653589793:0.0;A=B}else{uf(-1,211136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);w=c[6354]|0;if((w|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}x=w+1|0;c[6354]=x;w=14296+(x*24&-1)|0;c[w>>2]=2;c[w+4>>2]=j;h[14304+(x*24&-1)>>3]=A;h[14312+(x*24&-1)>>3]=z;i=b;return}function kU(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0,q=0.0,r=0,s=0.0,t=0,u=0.0,w=0.0,x=0;a=i;i=i+16|0;b=a|0;d=a+8|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=g|0;k=c[j>>2]|0;l=g+4|0;g=c[l>>2]|0;m=14304+(e*24&-1)|0;n=+h[m>>3];o=c[m>>2]|0;h[d>>3]=n;p=14312+(e*24&-1)|0;q=+h[p>>3];r=o;o=r;do{if((k|0)==3){s=+uz(o,b);t=(o|0)==(c[b>>2]|0);uu(o);if(t){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[d>>3]=s;u=0.0;w=s;x=c[6354]|0;break}}else{if((k|0)==2){u=q;w=n;x=f;break}else if((k|0)!=1){uf(-1,205760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[d>>2]=~~+O(+(+(r|0)));if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e;c[j>>2]=1;c[l>>2]=g;h[m>>3]=+h[d>>3];h[p>>3]=q;i=a;return}}while(0);c[d>>2]=~~+O(+w);if((x|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=x+1|0;c[6354]=p;x=14296+(p*24&-1)|0;c[x>>2]=1;c[x+4>>2]=g;h[14304+(p*24&-1)>>3]=+h[d>>3];h[14312+(p*24&-1)>>3]=u;i=a;return}function kV(a){a=+a;var b=0.0,c=0.0,d=0.0,e=0.0,f=0.0;if(a>0.0){b=a}else{b=-0.0-a}if(b<8.0){a=b*b;c=(a*(a*(a*(a*(a*(a*(a*(a*26857.86856980015-40504123.71833133)+25071582855.36882)-8085222034853.794)+DOT$ZERO(0x51889c8a118fa))+-136762035308817140.0)+6382059341072356000.0)+-117915762910761060000.0)+49337872517941336.0e4)/(a*(a*(a*(a*(a*(a*(a*(a+1363.0636523289706)+1114636.0984629854)+669998767.298224)+312304311494.12134)+112775673967979.84)+30246356167094628.0)+5428918384092285000.0)+49337872517941336.0e4);return+c}else{a=+Q(+(.6366197723675814/b));d=8.0/b;e=d*d;f=b+-.7853981633974483;b=(e*(e*(e*(e*(e*.8896154842421046+153.76201909008356)+3480.648644324927)+21170.523380864943)+41345.38663958076)+22779.090197304686)/(e*(e*(e*(e*(e+157.11159858080893)+3502.8735138235606)+21215.350561880117)+41370.41249551042)+22779.090197304686)*+S(+f);c=a*(b-d*((e*(e*(e*(e*(e*-.008803330304868075-1.244102674583564)-22.300261666214197)-111.83429920482737)-185.91953644342993)-89.22660020080009)/(e*(e*(e*(e*(e+90.59376959499312)+1488.7231232283757)+7264.278016921102)+11951.131543434614)+5710.502412851206))*+T(+f));return+c}return 0.0}function kW(a){a=+a;var b=0,c=0.0,d=0.0,e=0.0,f=0.0,g=0.0,h=0.0;b=a<0.0;if(b){c=-0.0-a}else{c=a}if(c<8.0){d=c*c;e=(d*(d*(d*(d*(d*(d*(d*(d*2701.1227108923235-4695753.530642996)+3413234182.3017006)-1322983480332.1265)+290879526383477.56)+-35888175699101060.0)+2316433580634002400.0)+-6672106568924916.0e4)+5811993540016061.0e5)/(d*(d*(d*(d*(d*(d*(d*(d+1606.9315734814877)+1501793.5949985855)+1013863514.358674)+524371026216.76495)+208166122130760.75)+60920613989175220.0)+11857707121903210000.0)+0x3f0385ca46d07a0000)*a;return+e}a=+Q(+(.6366197723675814/c));d=8.0/c;f=d*d;g=c-2.356194490192345;c=(f*(f*(f*(f*(f*1.2571716929145342+211.15291828539623)+4985.4832060594335)+31353.963110915956)+62758.84524716128)+35224.66491336798)/(f*(f*(f*(f*(f+203.07751891347593)+4930.396490181089)+31240.406381904104)+62694.34695935605)+35224.66491336798)*+S(+g);h=a*(c-d*((f*(f*(f*(f*(f*.03532840052740124+4.568171629551227)+83.18989576738508)+425.98730116544425)+721.0391804904475)+351.17519143035526)/(f*(f*(f*(f*(f+103.81875854621337)+1811.1867005523513)+9152.231701516992)+15414.177339265098)+7491.737417180912))*+T(+g));if(!b){e=h;return+e}e=-0.0-h;return+e}function kX(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0,q=0.0,r=0,s=0.0,t=0,u=0.0,w=0.0,x=0;a=i;i=i+16|0;b=a|0;d=a+8|0;e=c[6354]|0;if((e|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e-1|0;c[6354]=f;g=14296+(e*24&-1)|0;j=g|0;k=c[j>>2]|0;l=g+4|0;g=c[l>>2]|0;m=14304+(e*24&-1)|0;n=+h[m>>3];o=c[m>>2]|0;h[d>>3]=n;p=14312+(e*24&-1)|0;q=+h[p>>3];r=o;o=r;do{if((k|0)==3){s=+uz(o,b);t=(o|0)==(c[b>>2]|0);uu(o);if(t){uf(-1,208864,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{h[d>>3]=s;u=0.0;w=s;x=c[6354]|0;break}}else{if((k|0)==2){u=q;w=n;x=f;break}else if((k|0)!=1){uf(-1,205760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[d>>2]=~~+$(+(+(r|0)));if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=e;c[j>>2]=1;c[l>>2]=g;h[m>>3]=+h[d>>3];h[p>>3]=q;i=a;return}}while(0);c[d>>2]=~~+$(+w);if((x|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}p=x+1|0;c[6354]=p;x=14296+(p*24&-1)|0;c[x>>2]=1;c[x+4>>2]=g;h[14304+(p*24&-1)>>3]=+h[d>>3];h[14312+(p*24&-1)>>3]=u;i=a;return}function kY(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;b=i;i=i+16|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=d-1|0;c[6354]=e;f=14296+(d*24&-1)|0;g=f;h=f|0;f=c[h>>2]|0;j=g+4|0;k=c[j>>2]|0;l=14304+(d*24&-1)|0;m=c[l>>2]|0;n=g+12|0;g=b|0;c[g>>2]=c[n>>2];c[g+4>>2]=c[n+4>>2];c[g+8>>2]=c[n+8>>2];if((f|0)==3){f=m;m=e6(f)|0;uu(f);f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=(a[m+8|0]&1^1)&255;m=f+1|0;c[6354]=m;f=14296+(m*24&-1)|0;p=f;c[f>>2]=1;c[p+4>>2]=k;c[14304+(m*24&-1)>>2]=o;o=p+12|0;c[o>>2]=c[g>>2];c[o+4>>2]=c[g+4>>2];c[o+8>>2]=c[g+8>>2];i=b;return}else{if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d;c[h>>2]=1;c[j>>2]=k;c[l>>2]=0;c[n>>2]=c[g>>2];c[n+4>>2]=c[g+4>>2];c[n+8>>2]=c[g+8>>2];i=b;return}}function kZ(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0.0,n=0.0,o=0;a=i;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=14296+(b*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(b*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;if((f|0)==2){l=+h[14312+(b*24&-1)>>3]}else if((f|0)==3){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((f|0)==1){l=0.0}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=+P(+l);if(m>+h[11]){uf(-1,151424,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((f|0)==1){n=+(k|0);o=d}else if((f|0)==2){n=j;o=d}else if((f|0)==3){j=+uz(e,0);n=j;o=c[6354]|0}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((o|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=+kV(n);e=o+1|0;c[6354]=e;o=14296+(e*24&-1)|0;c[o>>2]=2;c[o+4>>2]=g;h[14304+(e*24&-1)>>3]=j;h[14312+(e*24&-1)>>3]=0.0;i=a;return}}function k_(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0.0,n=0.0,o=0;a=i;b=c[6354]|0;if((b|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=b-1|0;c[6354]=d;e=14296+(b*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(b*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;if((f|0)==1){l=0.0}else if((f|0)==3){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((f|0)==2){l=+h[14312+(b*24&-1)>>3]}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=+P(+l);if(m>+h[11]){uf(-1,151424,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((f|0)==3){m=+uz(e,0);n=m;o=c[6354]|0}else if((f|0)==1){n=+(k|0);o=d}else if((f|0)==2){n=j;o=d}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((o|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=+kW(n);d=o+1|0;c[6354]=d;o=14296+(d*24&-1)|0;c[o>>2]=2;c[o+4>>2]=g;h[14304+(d*24&-1)>>3]=j;h[14312+(d*24&-1)>>3]=0.0;i=a;return}}function k$(b){b=b|0;var d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0.0,n=0.0,o=0,q=0.0,r=0.0,s=0.0;b=i;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;if((f|0)==2){l=+h[14312+(d*24&-1)>>3]}else if((f|0)==3){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((f|0)==1){l=0.0}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=+P(+l);if(m>+h[11]){uf(-1,151424,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((f|0)==1){n=+(k|0)}else if((f|0)==2){n=j}else if((f|0)==3){n=+uz(e,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(n<=0.0){d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=d+1|0;c[6354]=o;d=14296+(o*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;uE(14304+(o*24&-1)|0,0,16);a[1960]=1;i=b;return}if((f|0)==2){q=j}else if((f|0)==1){q=+(k|0)}else if((f|0)==3){q=+uz(e,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if(q<0.0){r=p}else{if(q<8.0){j=q*q;n=+kV(q)*.6366197723675814;r=(j*(j*(j*(j*(j*(j*(j*(j*-41370.35497933149+59152134.6568689)-34363712229.790405)+10255208596863.943)-1648605817185729.5)+137562431639934400.0)+-5247065581112765000.0)+6587473275719555.0e4)+-27502866786291098000.0)/(j*(j*(j*(j*(j*(j*(j*(j+1282.452772478994)+1001702.6412889062)+579512264.070073)+261306575504.10812)+91620380340751.86)+DOT$ZERO(0x550324fdf7f63c))+419241704341084.0e4)+3726458838986166.0e5)+n*+_(+q);break}else{n=+Q(+(.6366197723675814/q));j=8.0/q;m=j*j;l=q+-.7853981633974483;s=(m*(m*(m*(m*(m*.8896154842421046+153.76201909008356)+3480.648644324927)+21170.523380864943)+41345.38663958076)+22779.090197304686)/(m*(m*(m*(m*(m+157.11159858080893)+3502.8735138235606)+21215.350561880117)+41370.41249551042)+22779.090197304686)*+T(+l);r=n*(s+j*((m*(m*(m*(m*(m*-.008803330304868075-1.244102674583564)-22.300261666214197)-111.83429920482737)-185.91953644342993)-89.22660020080009)/(m*(m*(m*(m*(m+90.59376959499312)+1488.7231232283757)+7264.278016921102)+11951.131543434614)+5710.502412851206))*+S(+l));break}}}while(0);e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e+1|0;c[6354]=f;e=14296+(f*24&-1)|0;c[e>>2]=2;c[e+4>>2]=g;h[14304+(f*24&-1)>>3]=r;h[14312+(f*24&-1)>>3]=0.0;i=b;return}function k0(b){b=b|0;var d=0,e=0,f=0,g=0,j=0.0,k=0,l=0.0,m=0.0,n=0.0,o=0,q=0.0,r=0.0,s=0.0;b=i;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];k=c[e>>2]|0;e=k;if((f|0)==3){uh(-1,212704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uf(-1,212032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((f|0)==1){l=0.0}else if((f|0)==2){l=+h[14312+(d*24&-1)>>3]}else{uf(-1,211552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=+P(+l);if(m>+h[11]){uf(-1,151424,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((f|0)==2){n=j}else if((f|0)==3){n=+uz(e,0)}else if((f|0)==1){n=+(k|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(n<=0.0){d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=d+1|0;c[6354]=o;d=14296+(o*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;uE(14304+(o*24&-1)|0,0,16);a[1960]=1;i=b;return}if((f|0)==1){q=+(k|0)}else if((f|0)==2){q=j}else if((f|0)==3){q=+uz(e,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if(q>0.0){if(q<8.0){j=q*q;n=+kW(q);r=q*((j*(j*(j*(j*(j*(j*(j*(j*0.0+355692.4009830526)-428947196.88552487)+204969667374.56622)-48633169425671.75)+5915160760490071.0)+-344104806308411460.0)+774852068218684.0e4)+-29238219615329624000.0)/(j*(j*(j*(j*(j*(j*(j*(j+1072.6961437789255)+726914.7307198885)+371666079.86219305)+150022169915.6709)+47551735888881.375)+DOT$ZERO(0x2834334232332e))+1818662841706135000.0)+149131151130292030000.0))+(n*+_(+q)-1.0/q)*.6366197723675814;break}else{n=+Q(+(.6366197723675814/q));j=8.0/q;m=j*j;l=q-2.356194490192345;s=(m*(m*(m*(m*(m*1.2571716929145342+211.15291828539623)+4985.4832060594335)+31353.963110915956)+62758.84524716128)+35224.66491336798)/(m*(m*(m*(m*(m+203.07751891347593)+4930.396490181089)+31240.406381904104)+62694.34695935605)+35224.66491336798)*+T(+l);r=n*(s+j*((m*(m*(m*(m*(m*.03532840052740124+4.568171629551227)+83.18989576738508)+425.98730116544425)+721.0391804904475)+351.17519143035526)/(m*(m*(m*(m*(m+103.81875854621337)+1811.1867005523513)+9152.231701516992)+15414.177339265098)+7491.737417180912))*+S(+l));break}}else{r=p}}while(0);e=c[6354]|0;if((e|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=e+1|0;c[6354]=f;e=14296+(f*24&-1)|0;c[e>>2]=2;c[e+4>>2]=g;h[14304+(f*24&-1)>>3]=r;h[14312+(f*24&-1)>>3]=0.0;i=b;return}function k1(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0;a=i;i=i+48|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];d=c[e>>2]|0;if((f|0)==1){k=+(d|0)}else if((f|0)==3){k=+uz(d,0)}else if((f|0)==2){k=j}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(b,k);f=c[6354]|0;if((f|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{k=+(c[b>>2]|0);b=f+1|0;c[6354]=b;f=14296+(b*24&-1)|0;c[f>>2]=2;c[f+4>>2]=g;h[14304+(b*24&-1)>>3]=k;h[14312+(b*24&-1)>>3]=0.0;i=a;return}}function k2(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0;a=i;i=i+48|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;d=c[e>>2]|0;if((f|0)==2){j=+h[e>>3]}else if((f|0)==3){j=+uz(d,0)}else if((f|0)==1){j=+(d|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(b,j);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=+(c[b+4>>2]|0);b=d+1|0;c[6354]=b;d=14296+(b*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(b*24&-1)>>3]=j;h[14312+(b*24&-1)>>3]=0.0;i=a;return}}function k3(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0;a=i;i=i+48|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;d=c[e>>2]|0;if((f|0)==2){j=+h[e>>3]}else if((f|0)==3){j=+uz(d,0)}else if((f|0)==1){j=+(d|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(b,j);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=+(c[b+8>>2]|0);b=d+1|0;c[6354]=b;d=14296+(b*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(b*24&-1)>>3]=j;h[14312+(b*24&-1)>>3]=0.0;i=a;return}}function k4(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0;a=i;i=i+48|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;d=c[e>>2]|0;if((f|0)==2){j=+h[e>>3]}else if((f|0)==1){j=+(d|0)}else if((f|0)==3){j=+uz(d,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(b,j);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=+(c[b+12>>2]|0);b=d+1|0;c[6354]=b;d=14296+(b*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(b*24&-1)>>3]=j;h[14312+(b*24&-1)>>3]=0.0;i=a;return}}function k5(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0;a=i;i=i+48|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;d=c[e>>2]|0;if((f|0)==2){j=+h[e>>3]}else if((f|0)==1){j=+(d|0)}else if((f|0)==3){j=+uz(d,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(b,j);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=+(c[b+16>>2]|0);b=d+1|0;c[6354]=b;d=14296+(b*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(b*24&-1)>>3]=j;h[14312+(b*24&-1)>>3]=0.0;i=a;return}}function k6(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0.0;a=i;i=i+48|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;j=+h[e>>3];d=c[e>>2]|0;if((f|0)==1){k=+(d|0)}else if((f|0)==2){k=j}else if((f|0)==3){k=+uz(d,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(b,k);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{k=+(c[b+20>>2]|0);b=d+1|0;c[6354]=b;d=14296+(b*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(b*24&-1)>>3]=k;h[14312+(b*24&-1)>>3]=0.0;i=a;return}}function k7(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0;a=i;i=i+48|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;d=c[e>>2]|0;if((f|0)==2){j=+h[e>>3]}else if((f|0)==1){j=+(d|0)}else if((f|0)==3){j=+uz(d,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(b,j);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=+(c[b+24>>2]|0);b=d+1|0;c[6354]=b;d=14296+(b*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(b*24&-1)>>3]=j;h[14312+(b*24&-1)>>3]=0.0;i=a;return}}function k8(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0;a=i;i=i+48|0;b=a|0;d=c[6354]|0;if((d|0)<0){uf(-1,209224,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[6354]=d-1;e=14296+(d*24&-1)|0;f=c[e>>2]|0;g=c[e+4>>2]|0;e=14304+(d*24&-1)|0;d=c[e>>2]|0;if((f|0)==2){j=+h[e>>3]}else if((f|0)==1){j=+(d|0)}else if((f|0)==3){j=+uz(d,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}t3(b,j);d=c[6354]|0;if((d|0)==249){uf(-1,208304,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{j=+(c[b+28>>2]|0);b=d+1|0;c[6354]=b;d=14296+(b*24&-1)|0;c[d>>2]=2;c[d+4>>2]=g;h[14304+(b*24&-1)>>3]=j;h[14312+(b*24&-1)>>3]=0.0;i=a;return}}function k9(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0.0,am=0.0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aG=0,aH=0.0,aJ=0.0,aK=0.0,aL=0.0,aM=0.0,aN=0.0,aO=0,aP=0.0,aQ=0.0,aR=0.0,aS=0.0,aT=0.0,aU=0.0,aV=0.0,aW=0.0,aX=0,aZ=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0;b=i;i=i+720|0;d=b|0;e=b+112|0;f=b+144|0;j=b+176|0;k=b+208|0;l=b+320|0;o=b+432|0;p=b+456|0;q=b+480|0;r=b+496|0;s=b+608|0;c[13898]=(c[13898]|0)+1;c[16506]=c[16507];h[8255]=+h[8257];h[8256]=+h[8258];h[8261]=8.988465674311579e+307;h[8262]=-8.988465674311579e+307;c[16334]=c[16335];h[8169]=+h[8171];h[8170]=+h[8172];h[8175]=8.988465674311579e+307;h[8176]=-8.988465674311579e+307;dq(2);dq(1);uu(c[3562]|0);uu(c[3560]|0);t=ut(32768)|0;do{if((t|0)==0){gk();u=ut(32768)|0;if((u|0)!=0){x=u;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{x=t}}while(0);c[3562]=x;x=ut(32768)|0;do{if((x|0)==0){gk();t=ut(32768)|0;if((t|0)!=0){y=t;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215008,v)|0)}else{y=x}}while(0);c[3560]=y;if(!((c[3562]|0)!=0&(y|0)!=0)){uf(-1,135152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}uu(c[3558]|0);y=c[13898]|0;L13568:do{if((y|0)<(c[8272]|0)){x=c[1054]|0;L13570:do{if((a[x+(y*40&-1)|0]&1)!=0){t=c[x+(y*40&-1)+36>>2]|0;u=x+(y*40&-1)+32|0;z=c[10036]|0;A=0;while(1){if((A|0)>=(t|0)){break}if((a[z+((c[u>>2]|0)+A|0)|0]|0)==(a[A+103664|0]|0)){A=A+1|0}else{break L13570}}if((A|0)==1){break L13568}}}while(0);a[14176]=1;is(p);a[14176]=0;if((c[p>>2]|0)!=3){c[13898]=y;break}x=c[p+8>>2]|0;c[3558]=x;u=c[13898]|0;if((x|0)==0){B=u;uf(B,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}z=c[1054]|0;t=c[z+(u*40&-1)+36>>2]|0;L13583:do{if((a[z+(u*40&-1)|0]&1)!=0&(t|0)>0){C=c[10036]|0;D=0;E=0;F=c[z+(u*40&-1)+32>>2]|0;while(1){if((a[D+151232|0]|0)==(a[C+(D+F|0)|0]|0)){G=F;H=E}else{if((D|0)!=3){I=9981;break L13583}G=F-1|0;H=1}J=D+1|0;if((J|0)<(H+t|0)){D=J;E=H;F=G}else{break}}if((H|0)==0){if(!((D|0)==6|(D|0)==2)){I=9981;break}}ey(x,3,0);F=(c[11864]|0)-1|0;E=c[12172]|0;C=c[E+(F*232&-1)+144>>2]|0;A=aa(C,c[E+(F*232&-1)+140>>2]|0);J=c[E+(F*232&-1)+224>>2]|0;if((A|0)<1){c[3562]=0;uf(-1,133128,(v=i,i=i+8|0,c[v>>2]=A,v)|0)}F=A<<3;E=db(c[3562]|0,F,215008)|0;c[3562]=E;if((E|0)==0){uf(-1,133128,(v=i,i=i+8|0,c[v>>2]=A,v)|0)}E=db(c[3560]|0,F,215008)|0;F=E;c[3560]=F;if((E|0)==0){uf(-1,133128,(v=i,i=i+8|0,c[v>>2]=A,v)|0)}if((A|0)>0){E=0;do{h[F+(E<<3)>>3]=+g[J+(E<<2)>>2];E=E+1|0;}while((E|0)<(A|0))}ez();K=1;L=C;M=A;N=0;O=0;Q=0;R=0}else{I=9981}}while(0);if((I|0)==9981){t=ey(x,2,0)|0;if((t|0)<0){uf(-1,120240,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((t|0)>2){uf(c[13898]|0,104256,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}u=c[8270]|0;do{if((u|0)!=0){if((aY(u|0,139696)|0)==0){break}bA(4,u|0)}}while(0);if((a[66164]&1)!=0){uf(-1,91264,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[65476]&1)!=0){uf(-1,91264,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((c[16546]|0)==1|(c[16374]|0)==1){uf(-1,83576,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[30528]&1)!=0){uf(-1,75664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((a[32936]&1)!=0){uf(-1,223048,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}u=q|0;x=eC(u,2)|0;do{if((x|0)==-1){ez();S=2;T=0;U=0;V=0;W=0;X=0;I=10029}else{z=q+8|0;E=4096;J=0;F=0;D=0;Y=0;Z=0;_=2;$=x;L13639:while(1){ab=($|0)>(_|0)?$:_;if((Z|0)<(E|0)){ac=E}else{ad=E*3&-1;ae=(ad|0)/2&-1;if((ad|0)<2){I=10002;break}ad=ae<<3;af=db(c[3562]|0,ad,215008)|0;c[3562]=af;if((af|0)==0){I=10358;break}af=db(c[3560]|0,ad,215008)|0;c[3560]=af;if((af|0)==0){I=10356;break}else{ac=ae}}L13646:do{if(($|0)==(-5|0)|($|0)==(-2|0)){ag=Z;ah=Y;ai=D;aj=F;ak=J+1|0}else if(($|0)==(-3|0)){ag=Z;ah=Y;ai=D;aj=F+1|0;ak=J}else if(($|0)==(-4|0)){ag=Z;ah=Y;ai=D+1|0;aj=F+1|0;ak=J}else if(($|0)==0){I=10011;break L13639}else if(($|0)==1){al=+h[u>>3];af=c[16334]&3;if((af|0)==3){I=10016}else if((af|0)==1){if(+h[8170]<al){I=10017}else{I=10016}}else if((af|0)==2){if(+h[8169]>al){I=10017}else{I=10016}}else if((af|0)==0){if(+h[8170]<al|+h[8169]>al){I=10017}else{I=10016}}else{I=10017}if((I|0)==10016){I=0;h[(c[3560]|0)+(Z<<3)>>3]=al;ag=Z+1|0;ah=Y;ai=D;aj=F;ak=J;break}else if((I|0)==10017){I=0;ag=Z;ah=Y+1|0;ai=D;aj=F;ak=J;break}}else if(($|0)==2){al=+h[u>>3];af=c[16506]&3;if((af|0)==0){if(!(+h[8256]<al|+h[8255]>al)){I=10022}}else if((af|0)==3){I=10022}else if((af|0)==1){if(+h[8256]>=al){I=10022}}else if((af|0)==2){if(+h[8255]<=al){I=10022}}do{if((I|0)==10022){I=0;am=+h[z>>3];af=c[16334]&3;if((af|0)==1){if(+h[8170]<am){break}}else if((af|0)==2){if(+h[8169]>am){break}}else if((af|0)==0){if(+h[8170]<am|+h[8169]>am){break}}else if((af|0)!=3){break}h[(c[3562]|0)+(Z<<3)>>3]=al;h[(c[3560]|0)+(Z<<3)>>3]=+h[z>>3];ag=Z+1|0;ah=Y;ai=D;aj=F;ak=J;break L13646}}while(0);ag=Z;ah=Y+1|0;ai=D;aj=F;ak=J}else{ag=Z;ah=Y;ai=D;aj=F;ak=J}}while(0);af=eC(u,2)|0;if((af|0)==-1){I=10028;break}else{E=ac;J=ak;F=aj;D=ai;Y=ah;Z=ag;_=ab;$=af}}if((I|0)==10356){ez();uf(-1,133128,(v=i,i=i+8|0,c[v>>2]=ae,v)|0)}else if((I|0)==10358){ez();uf(-1,133128,(v=i,i=i+8|0,c[v>>2]=ae,v)|0)}else if((I|0)==10011){$=c[11932]|0;uf(-1,216224,(v=i,i=i+16|0,c[v>>2]=c[11900],c[v+8>>2]=($|0)!=0?$:179864,v)|0)}else if((I|0)==10002){c[3562]=0;ez();uf(-1,133128,(v=i,i=i+8|0,c[v>>2]=ae,v)|0)}else if((I|0)==10028){ez();if((ag|0)<1){S=ab;T=ag;U=ah;V=ai;W=aj;X=ak;I=10029;break}$=ag<<3;c[3562]=db(c[3562]|0,$,215008)|0;an=db(c[3560]|0,$,215008)|0;ao=ab;ap=ag;aq=ah;ar=ai;as=aj;at=ak;break}}}while(0);if((I|0)==10029){c[3562]=0;an=0;ao=S;ap=T;aq=U;ar=V;as=W;at=X}c[3560]=an;K=(t|0)==0?ao:t;L=0;M=ap;N=aq;O=ar;Q=as;R=at}u=c[8270]|0;do{if((u|0)!=0){if((aY(u|0,139696)|0)==0){break}bA(4,139696)}}while(0);if((M|0)==0){if((N|0)>0){uf(-1,204632,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uf(-1,201008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}u=o|0;t=o+8|0;x=1;L13694:while(1){L13696:while(1){au=c[13898]|0;if((au|0)>=(c[8272]|0)){I=10098;break L13694}$=c[1054]|0;_=(a[$+(au*40&-1)|0]&1)==0;if(_){I=10351;break L13694}Z=c[$+(au*40&-1)+36>>2]|0;Y=$+(au*40&-1)+32|0;$=c[10036]|0;D=0;while(1){if((D|0)>=(Z|0)){I=10045;break}if((a[$+((c[Y>>2]|0)+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{break}}if((I|0)==10045){I=0;if((D|0)==1){I=10098;break L13694}}$=c[Y>>2]|0;if(_){I=10345;break L13694}do{if((Z|0)>0){F=c[10036]|0;J=0;E=0;z=$;while(1){if((a[J+184704|0]|0)==(a[F+(J+z|0)|0]|0)){av=z;aw=E}else{if((J|0)!=3){break}av=z-1|0;aw=1}A=J+1|0;if((A|0)<(aw+Z|0)){J=A;E=aw;z=av}else{I=10053;break}}if((I|0)==10053){I=0;if((aw|0)!=0){ax=1;break L13696}if((J|0)==6|(J|0)==2){ax=1;break L13696}}if(_){I=10346;break L13694}if((Z|0)<=0){I=10076;break}z=c[10036]|0;E=0;F=0;A=$;while(1){if((a[E+179552|0]|0)==(a[z+(E+A|0)|0]|0)){ay=A;az=F}else{if((E|0)!=5){break}ay=A-1|0;az=1}C=E+1|0;if((C|0)<(az+Z|0)){E=C;F=az;A=ay}else{I=10064;break}}if((I|0)==10064){I=0;if((az|0)!=0){ax=0;break L13696}if((E|0)==8|(E|0)==4){ax=0;break L13696}}if(_){I=10350;break L13694}if((Z|0)<=0){I=10076;break}A=c[10036]|0;F=0;z=0;J=$;while(1){if((a[F+175408|0]|0)==(a[A+(F+J|0)|0]|0)){aA=J;aB=z}else{if((F|0)!=3){break}aA=J-1|0;aB=1}C=F+1|0;if((C|0)<(aB+Z|0)){F=C;z=aB;J=aA}else{I=10073;break}}if((I|0)==10073){I=0;if((aB|0)!=0){break}if((F|0)==2|(F|0)==6){break}}if(_){I=10348;break L13694}else{I=10076}}else{I=10076}}while(0);if((I|0)==10076){I=0;_=c[10036]|0;Y=0;while(1){if((Y|0)>=(Z|0)){break}if((a[_+($+Y|0)|0]|0)==(a[Y+218432|0]|0)){Y=Y+1|0}else{I=10349;break L13694}}if((Y|0)!=4){I=10347;break L13694}}c[13898]=au+1;uu(c[3556]|0);aC=c[13898]|0;if((aC|0)>=(c[8272]|0)){break L13694}$=c[1054]|0;L13751:do{if((a[$+(aC*40&-1)|0]&1)!=0){_=c[$+(aC*40&-1)+36>>2]|0;Z=$+(aC*40&-1)+32|0;D=c[10036]|0;J=0;while(1){if((J|0)>=(_|0)){break}if((a[D+((c[Z>>2]|0)+J|0)|0]|0)==(a[J+103664|0]|0)){J=J+1|0}else{break L13751}}if((J|0)==1){break L13694}}}while(0);a[14176]=1;is(o);a[14176]=0;if((c[u>>2]|0)!=3){I=10087;break L13694}$=c[t>>2]|0;c[3556]=$;if(($|0)==0){I=10341;break L13694}Y=a[$]|0;if(Y<<24>>24==0){I=10342;break L13694}if(((Y<<24>>24)-48|0)>>>0<10){I=10343;break L13694}else{aD=$;aE=Y}do{if((bs(aE&255|0)|0)==0){if((a[aD]|0)!=95){I=10339;break L13694}}aD=aD+1|0;aE=a[aD]|0;}while(aE<<24>>24!=0);if((aY(167320,c[3556]|0)|0)==0){I=10340;break L13694}}c[13898]=au+1;x=ax}if((I|0)==10098){t=c[3556]|0;if((t|0)==0){u=bP(150920)|0;c[3556]=u;aG=u}else{aG=t}t=uA(aG|0)|0;if((a[aG+(t-1|0)|0]|0)!=95){u=db(aG,t+2|0,149064)|0;c[3556]=u;t=u+(uA(u|0)|0)|0;w=95;a[t]=w&255;w=w>>8;a[t+1|0]=w&255}t=O+1|0;u=(K|0)==1;do{if(u){lc(s,c[3560]|0,M,L);aH=0.0;aJ=0.0;aK=0.0;aL=0.0;aM=0.0;aN=0.0;aO=0}else{if((K|0)!=2){aH=0.0;aJ=0.0;aK=0.0;aL=0.0;aM=0.0;aN=0.0;aO=0;break}lc(r,c[3562]|0,M,0);lc(s,c[3560]|0,M,0);Y=c[3562]|0;$=c[3560]|0;al=+h[r+8>>3];am=+h[r+16>>3];aP=+h[r+24>>3];aQ=+h[r+32>>3];aR=+h[s+8>>3];aS=+h[s+16>>3];aT=+h[s+24>>3];Z=c[s+48>>2]|0;D=c[s+64>>2]|0;if((M|0)>0){_=0;aU=0.0;while(1){aV=aU+ +h[Y+(_<<3)>>3]*+h[$+(_<<3)>>3];F=_+1|0;if((F|0)<(M|0)){_=F;aU=aV}else{aW=aV;break}}}else{aW=0.0}aU=+(M|0);aV=(aW-aP*aT/aU)/(aQ-aP*aP/aU);aH=aW;aJ=aV;aK=aR-al*aV;aL=am*aV/aS;aM=+h[Y+(Z<<3)>>3];aN=+h[Y+(D<<3)>>3];aO=1}}while(0);_=c[3556]|0;$=c[10810]|0;if(($|0)==0){aX=_}else{F=$;do{$=c[F+4>>2]|0;do{if((a_(_|0,$|0,uA(_|0)|0)|0)==0){a[F+8|0]=1;z=F+16|0;if((c[z>>2]|0)!=3){break}uu(c[F+24>>2]|0);c[z>>2]=1}}while(0);F=c[F>>2]|0;}while((F|0)!=0);aX=c[3556]|0}aS=+(M|0);F=(aX|0)!=0?aX:179864;_=(uA(F|0)|0)+8|0;$=ut(_)|0;do{if(($|0)==0){gk();D=ut(_)|0;if((D|0)!=0){aZ=D;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{aZ=$}}while(0);be(aZ|0,172512,(v=i,i=i+24|0,c[v>>2]=F,c[v+8>>2]=83376,c[v+16>>2]=179864,v)|0);$=e6(aZ)|0;c[$+16>>2]=2;h[$+24>>3]=aS;h[$+32>>3]=0.0;a[$+8|0]=0;uu(aZ);am=+(R|0);$=(uA(F|0)|0)+8|0;_=ut($)|0;do{if((_|0)==0){gk();D=ut($)|0;if((D|0)!=0){a$=D;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a$=_}}while(0);be(a$|0,172512,(v=i,i=i+24|0,c[v>>2]=F,c[v+8>>2]=82464,c[v+16>>2]=179864,v)|0);_=e6(a$)|0;c[_+16>>2]=2;h[_+24>>3]=am;h[_+32>>3]=0.0;a[_+8|0]=0;uu(a$);al=+(Q|0);_=(uA(F|0)|0)+6|0;$=ut(_)|0;do{if(($|0)==0){gk();D=ut(_)|0;if((D|0)!=0){a0=D;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a0=$}}while(0);be(a0|0,172512,(v=i,i=i+24|0,c[v>>2]=F,c[v+8>>2]=224608,c[v+16>>2]=179864,v)|0);$=e6(a0)|0;c[$+16>>2]=2;h[$+24>>3]=al;h[$+32>>3]=0.0;a[$+8|0]=0;uu(a0);am=+(t|0);$=(uA(F|0)|0)+7|0;_=ut($)|0;do{if((_|0)==0){gk();D=ut($)|0;if((D|0)!=0){a1=D;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a1=_}}while(0);be(a1|0,172512,(v=i,i=i+24|0,c[v>>2]=F,c[v+8>>2]=79624,c[v+16>>2]=179864,v)|0);_=e6(a1)|0;c[_+16>>2]=2;h[_+24>>3]=am;h[_+32>>3]=0.0;a[_+8|0]=0;uu(a1);al=+(N|0);_=(uA(F|0)|0)+11|0;$=ut(_)|0;do{if(($|0)==0){gk();D=ut(_)|0;if((D|0)!=0){a2=D;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a2=$}}while(0);be(a2|0,172512,(v=i,i=i+24|0,c[v>>2]=F,c[v+8>>2]=78880,c[v+16>>2]=179864,v)|0);$=e6(a2)|0;c[$+16>>2]=2;h[$+24>>3]=al;h[$+32>>3]=0.0;a[$+8|0]=0;uu(a2);if(u){ld(s,c[3556]|0,179864)}if(aO){ld(r,c[3556]|0,144920);ld(s,c[3556]|0,143296);$=c[3556]|0;_=($|0)!=0?$:179864;$=(uA(_|0)|0)+6|0;D=ut($)|0;do{if((D|0)==0){gk();Y=ut($)|0;if((Y|0)!=0){a3=Y;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a3=D}}while(0);be(a3|0,172512,(v=i,i=i+24|0,c[v>>2]=_,c[v+8>>2]=140096,c[v+16>>2]=179864,v)|0);D=e6(a3)|0;c[D+16>>2]=2;h[D+24>>3]=aJ;h[D+32>>3]=0.0;a[D+8|0]=0;uu(a3);D=(uA(_|0)|0)+10|0;$=ut(D)|0;do{if(($|0)==0){gk();F=ut(D)|0;if((F|0)!=0){a4=F;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a4=$}}while(0);be(a4|0,172512,(v=i,i=i+24|0,c[v>>2]=_,c[v+8>>2]=138e3,c[v+16>>2]=179864,v)|0);$=e6(a4)|0;c[$+16>>2]=2;h[$+24>>3]=aK;h[$+32>>3]=0.0;a[$+8|0]=0;uu(a4);$=(uA(_|0)|0)+12|0;D=ut($)|0;do{if((D|0)==0){gk();F=ut($)|0;if((F|0)!=0){a5=F;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a5=D}}while(0);be(a5|0,172512,(v=i,i=i+24|0,c[v>>2]=_,c[v+8>>2]=136336,c[v+16>>2]=179864,v)|0);D=e6(a5)|0;c[D+16>>2]=2;h[D+24>>3]=aL;h[D+32>>3]=0.0;a[D+8|0]=0;uu(a5);D=(uA(_|0)|0)+6|0;$=ut(D)|0;do{if(($|0)==0){gk();F=ut(D)|0;if((F|0)!=0){a6=F;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a6=$}}while(0);be(a6|0,172512,(v=i,i=i+24|0,c[v>>2]=_,c[v+8>>2]=134616,c[v+16>>2]=179864,v)|0);$=e6(a6)|0;c[$+16>>2]=2;h[$+24>>3]=aH;h[$+32>>3]=0.0;a[$+8|0]=0;uu(a6);$=(uA(_|0)|0)+10|0;D=ut($)|0;do{if((D|0)==0){gk();F=ut($)|0;if((F|0)!=0){a7=F;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a7=D}}while(0);be(a7|0,172512,(v=i,i=i+24|0,c[v>>2]=_,c[v+8>>2]=75120,c[v+16>>2]=179864,v)|0);D=e6(a7)|0;c[D+16>>2]=2;h[D+24>>3]=aM;h[D+32>>3]=0.0;a[D+8|0]=0;uu(a7);D=(uA(_|0)|0)+10|0;$=ut(D)|0;do{if(($|0)==0){gk();F=ut(D)|0;if((F|0)!=0){a8=F;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{a8=$}}while(0);be(a8|0,172512,(v=i,i=i+24|0,c[v>>2]=_,c[v+8>>2]=74616,c[v+16>>2]=179864,v)|0);$=e6(a8)|0;c[$+16>>2]=2;h[$+24>>3]=aN;h[$+32>>3]=0.0;a[$+8|0]=0;uu(a8)}if(!x){a9=c[3562]|0;ba=a9;uu(ba);bb=c[3560]|0;bc=bb;uu(bc);c[3562]=0;c[3560]=0;bd=c[3558]|0;uu(bd);c[3558]=0;bf=c[3556]|0;uu(bf);c[3556]=0;i=b;return}$=(M|0)>0;if($){bg=~~+bQ(+aS)+1|0}else{bg=3}D=c[7262]|0;F=c[m>>2]|0;do{if((D|0)==0){c[7262]=F;bh=F;I=10158}else{if((D|0)==(c[n>>2]|0)|(D|0)==(F|0)){bh=D;I=10158;break}cf(D|0,84208,(v=i,i=i+16|0,c[v>>2]=83376,c[v+8>>2]=M,v)|0);Y=c[7262]|0;cf(Y|0,84208,(v=i,i=i+16|0,c[v>>2]=82464,c[v+8>>2]=R,v)|0);Y=c[7262]|0;cf(Y|0,84208,(v=i,i=i+16|0,c[v>>2]=80984,c[v+8>>2]=Q,v)|0);Y=c[7262]|0;cf(Y|0,84208,(v=i,i=i+16|0,c[v>>2]=79624,c[v+8>>2]=t,v)|0);Y=c[7262]|0;cf(Y|0,84208,(v=i,i=i+16|0,c[v>>2]=78880,c[v+8>>2]=N,v)|0)}}while(0);if((I|0)==10158){aF(10,bh|0);D=c[7262]|0;aI(78344,9,1,D|0);D=c[7262]|0;cf(D|0,77936,(v=i,i=i+16|0,c[v>>2]=bg,c[v+8>>2]=M,v)|0);D=c[7262]|0;cf(D|0,77320,(v=i,i=i+16|0,c[v>>2]=bg,c[v+8>>2]=N,v)|0);D=c[7262]|0;cf(D|0,76744,(v=i,i=i+16|0,c[v>>2]=bg,c[v+8>>2]=R,v)|0);D=c[7262]|0;cf(D|0,76144,(v=i,i=i+16|0,c[v>>2]=bg,c[v+8>>2]=Q,v)|0);D=c[7262]|0;cf(D|0,75560,(v=i,i=i+16|0,c[v>>2]=bg,c[v+8>>2]=t,v)|0)}if(u){uD(d|0,s|0,112);D=e|0;F=f|0;if($){bi=~~+bQ(+aS)+1|0}else{bi=1}x=c[7262]|0;_=c[m>>2]|0;do{if((x|0)==0){c[7262]=_;bj=_}else{if((x|0)==(c[n>>2]|0)|(x|0)==(_|0)){bj=x;break}lb(d,143296);a9=c[3562]|0;ba=a9;uu(ba);bb=c[3560]|0;bc=bb;uu(bc);c[3562]=0;c[3560]=0;bd=c[3558]|0;uu(bd);c[3558]=0;bf=c[3556]|0;uu(bf);c[3556]=0;i=b;return}}while(0);aF(10,bj|0);x=c[d>>2]|0;_=(x|0)>0;u=c[7262]|0;if(_){t=c[d+4>>2]|0;cf(u|0,94136,(v=i,i=i+16|0,c[v>>2]=x,c[v+8>>2]=t,v)|0)}else{aI(93512,11,1,u|0)}u=c[7262]|0;al=+h[d+8>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(u|0,92672,(v=i,i=i+8|0,c[v>>2]=D,v)|0);t=c[7262]|0;al=+h[d+16>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(t|0,91840,(v=i,i=i+8|0,c[v>>2]=D,v)|0);u=c[7262]|0;al=+h[d+24>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(u|0,91184,(v=i,i=i+8|0,c[v>>2]=D,v)|0);t=c[7262]|0;al=+h[d+32>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(t|0,90616,(v=i,i=i+8|0,c[v>>2]=D,v)|0);u=c[7262]|0;aF(10,u|0);u=c[7262]|0;al=+h[d+40>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);t=c[d+48>>2]|0;if(_){Y=(t|0)/(x|0)&-1;Z=(t|0)%(x|0)&-1;cf(u|0,90112,(v=i,i=i+32|0,c[v>>2]=D,c[v+8>>2]=bi,c[v+16>>2]=Y,c[v+24>>2]=Z,v)|0);Z=c[7262]|0;al=+h[d+56>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);_=c[d+64>>2]|0;Y=(_|0)/(x|0)&-1;z=(_|0)%(x|0)&-1;cf(Z|0,89384,(v=i,i=i+32|0,c[v>>2]=D,c[v+8>>2]=bi,c[v+16>>2]=Y,c[v+24>>2]=z,v)|0);z=c[7262]|0;al=+h[d+96>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);al=+h[d+104>>3];am=+P(+al);do{if(am<1.0e-14){be(F|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(F|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(F|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(z|0,88904,(v=i,i=i+16|0,c[v>>2]=D,c[v+8>>2]=F,v)|0);a9=c[3562]|0;ba=a9;uu(ba);bb=c[3560]|0;bc=bb;uu(bc);c[3562]=0;c[3560]=0;bd=c[3558]|0;uu(bd);c[3558]=0;bf=c[3556]|0;uu(bf);c[3556]=0;i=b;return}cf(u|0,88336,(v=i,i=i+24|0,c[v>>2]=D,c[v+8>>2]=bi,c[v+16>>2]=t,v)|0);Z=c[7262]|0;al=+h[d+56>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);t=c[d+64>>2]|0;cf(Z|0,87048,(v=i,i=i+24|0,c[v>>2]=D,c[v+8>>2]=bi,c[v+16>>2]=t,v)|0);t=c[7262]|0;al=+h[d+80>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(t|0,86e3,(v=i,i=i+8|0,c[v>>2]=D,v)|0);Z=c[7262]|0;al=+h[d+72>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(Z|0,84768,(v=i,i=i+8|0,c[v>>2]=D,v)|0);t=c[7262]|0;al=+h[d+88>>3];am=+P(+al);do{if(am<1.0e-14){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(D|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(D|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(t|0,86e3,(v=i,i=i+8|0,c[v>>2]=D,v)|0);Z=c[7262]|0;aF(10,Z|0);a9=c[3562]|0;ba=a9;uu(ba);bb=c[3560]|0;bc=bb;uu(bc);c[3562]=0;c[3560]=0;bd=c[3558]|0;uu(bd);c[3558]=0;bf=c[3556]|0;uu(bf);c[3556]=0;i=b;return}uD(l|0,r|0,112);uD(k|0,s|0,112);Z=e|0;u=f|0;F=j|0;if($){bk=~~+bQ(+aS)+1|0}else{bk=1}z=c[7262]|0;if(!((z|0)==(c[n>>2]|0)|(z|0)==(c[m>>2]|0))){lb(l,144920);lb(k,143296);z=c[7262]|0;cf(z|0,141768,(v=i,i=i+16|0,c[v>>2]=140096,h[v+8>>3]=aJ,v)|0);z=c[7262]|0;cf(z|0,141768,(v=i,i=i+16|0,c[v>>2]=138e3,h[v+8>>3]=aK,v)|0);z=c[7262]|0;cf(z|0,141768,(v=i,i=i+16|0,c[v>>2]=136336,h[v+8>>3]=aL,v)|0);z=c[7262]|0;cf(z|0,141768,(v=i,i=i+16|0,c[v>>2]=134616,h[v+8>>3]=aH,v)|0);a9=c[3562]|0;ba=a9;uu(ba);bb=c[3560]|0;bc=bb;uu(bc);c[3562]=0;c[3560]=0;bd=c[3558]|0;uu(bd);c[3558]=0;bf=c[3556]|0;uu(bf);c[3556]=0;i=b;return}z=bk+4|0;uF(F|0,132920,z|0);a[j+z|0]=0;z=c[7262]|0;if((z|0)==0){x=c[m>>2]|0;c[7262]=x;bl=x}else{bl=z}aF(10,bl|0);z=c[7262]|0;aI(130456,11,1,z|0);z=c[7262]|0;al=+h[l+8>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);al=+h[k+8>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(z|0,129328,(v=i,i=i+24|0,c[v>>2]=Z,c[v+8>>2]=F,c[v+16>>2]=u,v)|0);$=c[7262]|0;al=+h[l+16>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);al=+h[k+16>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf($|0,128056,(v=i,i=i+24|0,c[v>>2]=Z,c[v+8>>2]=F,c[v+16>>2]=u,v)|0);z=c[7262]|0;al=+h[l+24>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);al=+h[k+24>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(z|0,126736,(v=i,i=i+24|0,c[v>>2]=Z,c[v+8>>2]=F,c[v+16>>2]=u,v)|0);$=c[7262]|0;al=+h[l+32>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);al=+h[k+32>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf($|0,125656,(v=i,i=i+24|0,c[v>>2]=Z,c[v+8>>2]=F,c[v+16>>2]=u,v)|0);z=c[7262]|0;aF(10,z|0);z=c[7262]|0;al=+h[l+40>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);$=c[l+48>>2]|0;al=+h[k+40>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);D=c[k+48>>2]|0;cf(z|0,124424,(v=i,i=i+48|0,c[v>>2]=Z,c[v+8>>2]=bk,c[v+16>>2]=$,c[v+24>>2]=u,c[v+32>>2]=bk,c[v+40>>2]=D,v)|0);D=c[7262]|0;al=+h[l+56>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);$=c[l+64>>2]|0;al=+h[k+56>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);z=c[k+64>>2]|0;cf(D|0,122968,(v=i,i=i+48|0,c[v>>2]=Z,c[v+8>>2]=bk,c[v+16>>2]=$,c[v+24>>2]=u,c[v+32>>2]=bk,c[v+40>>2]=z,v)|0);z=c[7262]|0;al=+h[l+80>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);al=+h[k+80>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(z|0,121824,(v=i,i=i+24|0,c[v>>2]=Z,c[v+8>>2]=F,c[v+16>>2]=u,v)|0);$=c[7262]|0;al=+h[l+72>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);al=+h[k+72>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf($|0,119880,(v=i,i=i+24|0,c[v>>2]=Z,c[v+8>>2]=F,c[v+16>>2]=u,v)|0);z=c[7262]|0;al=+h[l+88>>3];am=+P(+al);do{if(am<1.0e-14){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(Z|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(Z|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);al=+h[k+88>>3];am=+P(+al);do{if(am<1.0e-14){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else{if(+P(+(+bQ(+am)))<6.0){be(u|0,109728,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}else{be(u|0,107744,(v=i,i=i+8|0,h[v>>3]=al,v)|0);break}}}while(0);cf(z|0,121824,(v=i,i=i+24|0,c[v>>2]=Z,c[v+8>>2]=F,c[v+16>>2]=u,v)|0);$=c[7262]|0;aF(10,$|0);$=c[7262]|0;if(aK<0.0){al=-0.0-aK;cf($|0,116720,(v=i,i=i+16|0,h[v>>3]=aJ,h[v+8>>3]=al,v)|0)}else{cf($|0,115856,(v=i,i=i+16|0,h[v>>3]=aJ,h[v+8>>3]=aK,v)|0)}$=c[7262]|0;cf($|0,114952,(v=i,i=i+8|0,h[v>>3]=aL,v)|0);$=c[7262]|0;cf($|0,113880,(v=i,i=i+8|0,h[v>>3]=aH,v)|0);$=c[7262]|0;aF(10,$|0);a9=c[3562]|0;ba=a9;uu(ba);bb=c[3560]|0;bc=bb;uu(bc);c[3562]=0;c[3560]=0;bd=c[3558]|0;uu(bd);c[3558]=0;bf=c[3556]|0;uu(bf);c[3556]=0;i=b;return}else if((I|0)==10339){bm=c[13898]|0;bn=bm-1|0;c[13898]=bn;uf(bn,162880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10340){bm=c[13898]|0;bn=bm-1|0;c[13898]=bn;uf(bn,162880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10341){bm=c[13898]|0;bn=bm-1|0;c[13898]=bn;uf(bn,162880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10342){bm=c[13898]|0;bn=bm-1|0;c[13898]=bn;uf(bn,162880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10343){bm=c[13898]|0;bn=bm-1|0;c[13898]=bn;uf(bn,162880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10345){uf(au,154024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10346){uf(au,154024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10347){uf(au,154024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10348){uf(au,154024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10349){uf(au,154024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10350){uf(au,154024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10351){uf(au,154024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((I|0)==10087){c[13898]=aC}c[3556]=0;bm=c[13898]|0;bn=bm-1|0;c[13898]=bn;uf(bn,162880,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[3558]=0;B=y;uf(B,205112,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}function la(a,b){a=a|0;b=b|0;var c=0.0,d=0.0,e=0;c=+h[a>>3];d=+h[b>>3];if(c<d){e=-1;return e|0}e=c>d&1;return e|0}function lb(a,b){a=a|0;b=b|0;var d=0,e=0,f=0.0,g=0,j=0,k=0,l=0,m=0;d=i;e=a;a=i;i=i+112|0;uD(a,e,112);f=+h[a+8>>3];cf(c[7262]|0,106776,(v=i,i=i+24|0,c[v>>2]=105792,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);f=+h[a+16>>3];cf(c[7262]|0,106776,(v=i,i=i+24|0,c[v>>2]=104736,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);f=+h[a+24>>3];cf(c[7262]|0,106776,(v=i,i=i+24|0,c[v>>2]=104152,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);f=+h[a+32>>3];cf(c[7262]|0,106776,(v=i,i=i+24|0,c[v>>2]=103792,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);f=+h[a+40>>3];cf(c[7262]|0,106776,(v=i,i=i+24|0,c[v>>2]=103256,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);e=c[a>>2]|0;if((e|0)==0){g=c[7262]|0;f=+h[a+80>>3];cf(g|0,106776,(v=i,i=i+24|0,c[v>>2]=101216,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);g=c[7262]|0;f=+h[a+72>>3];cf(g|0,106776,(v=i,i=i+24|0,c[v>>2]=100616,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);g=c[7262]|0;f=+h[a+88>>3];cf(g|0,106776,(v=i,i=i+24|0,c[v>>2]=99968,c[v+8>>2]=b,h[v+16>>3]=f,v)|0)}f=+h[a+56>>3];cf(c[7262]|0,106776,(v=i,i=i+24|0,c[v>>2]=99528,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);g=c[7262]|0;j=c[a+48>>2]|0;if((e|0)>0){k=(j|0)/(e|0)&-1;cf(g|0,99104,(v=i,i=i+24|0,c[v>>2]=98432,c[v+8>>2]=b,c[v+16>>2]=k,v)|0);k=c[7262]|0;l=(j|0)%(e|0)&-1;cf(k|0,99104,(v=i,i=i+24|0,c[v>>2]=97816,c[v+8>>2]=b,c[v+16>>2]=l,v)|0);l=c[7262]|0;k=c[a+64>>2]|0;m=(k|0)/(e|0)&-1;cf(l|0,99104,(v=i,i=i+24|0,c[v>>2]=97216,c[v+8>>2]=b,c[v+16>>2]=m,v)|0);m=c[7262]|0;l=(k|0)%(e|0)&-1;cf(m|0,99104,(v=i,i=i+24|0,c[v>>2]=96656,c[v+8>>2]=b,c[v+16>>2]=l,v)|0);l=c[7262]|0;f=+h[a+96>>3];cf(l|0,106776,(v=i,i=i+24|0,c[v>>2]=96240,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);l=c[7262]|0;f=+h[a+104>>3];cf(l|0,106776,(v=i,i=i+24|0,c[v>>2]=95776,c[v+8>>2]=b,h[v+16>>3]=f,v)|0);i=d;return}else{cf(g|0,99104,(v=i,i=i+24|0,c[v>>2]=95288,c[v+8>>2]=b,c[v+16>>2]=j,v)|0);j=c[7262]|0;g=c[a+64>>2]|0;cf(j|0,99104,(v=i,i=i+24|0,c[v>>2]=94824,c[v+8>>2]=b,c[v+16>>2]=g,v)|0);i=d;return}}function lc(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0;f=i;i=i+32|0;g=f|0;j=d<<4;k=ut(j)|0;do{if((k|0)==0){gk();l=ut(j)|0;if((l|0)!=0){m=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=223576,v)|0)}else{m=k}}while(0);k=m;j=(e|0)>0;if(j){n=e;o=(d|0)/(e|0)&-1}else{n=0;o=d}e=(d|0)>0;if(e){l=0;p=0.0;q=0.0;r=0.0;s=0.0;while(1){t=+h[b+(l<<3)>>3];u=p+t;w=q+t*t;if(j){x=s+t*+((l|0)/(n|0)&-1|0);y=r+t*+((l|0)%(n|0)&-1|0)}else{x=s;y=r}z=l+1|0;if((z|0)<(d|0)){l=z;p=u;q=w;r=y;s=x}else{A=u;B=w;C=y;D=x;break}}}else{A=0.0;B=0.0;C=0.0;D=0.0}x=+(d|0);y=A/x;s=+Q(+(B/x-y*y));if(e){e=0;do{h[k+(e<<4)>>3]=+h[b+(e<<3)>>3];c[k+(e<<4)+8>>2]=e;e=e+1|0;}while((e|0)<(d|0))}bM(m|0,d|0,16,4);e=g;c[e>>2]=c[m>>2];c[e+4>>2]=c[m+4>>2];c[e+8>>2]=c[m+8>>2];c[e+12>>2]=c[m+12>>2];b=d-1|0;l=k+(b<<4)|0;j=g+16|0;c[j>>2]=c[l>>2];c[j+4>>2]=c[l+4>>2];c[j+8>>2]=c[l+8>>2];c[j+12>>2]=c[l+12>>2];if((d&1|0)==0){l=(d|0)/2&-1;E=(+h[k+(l-1<<4)>>3]+ +h[k+(l<<4)>>3])*.5}else{E=+h[k+(((b|0)/2&-1)<<4)>>3]}if((d&3|0)==0){b=(d|0)/4&-1;l=d-b|0;F=(+h[k+(l<<4)>>3]+ +h[k+(l-1<<4)>>3])*.5;G=(+h[k+(b-1<<4)>>3]+ +h[k+(b<<4)>>3])*.5}else{b=(d+3|0)/4&-1;F=+h[k+(d-b<<4)>>3];G=+h[k+(b-1<<4)>>3]}if(C==0.0&D==0.0){H=0.0;I=0.0}else{H=D/A;I=C/A}uu(m);c[a>>2]=n;c[a+4>>2]=o;h[a+8>>3]=y;h[a+16>>3]=s;h[a+24>>3]=A;h[a+32>>3]=B;o=a+40|0;c[o>>2]=c[e>>2];c[o+4>>2]=c[e+4>>2];c[o+8>>2]=c[e+8>>2];c[o+12>>2]=c[e+12>>2];c[o+16>>2]=c[e+16>>2];c[o+20>>2]=c[e+20>>2];c[o+24>>2]=c[e+24>>2];c[o+28>>2]=c[e+28>>2];h[a+72>>3]=E;h[a+80>>3]=G;h[a+88>>3]=F;h[a+96>>3]=I;h[a+104>>3]=H;i=f;return}function ld(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0.0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0;f=i;g=b;b=i;i=i+112|0;uD(b,g,112);j=+h[b+8>>3];g=(d|0)!=0?d:179864;d=(e|0)!=0?e:179864;e=uA(g|0)|0;k=(e+5|0)+(uA(d|0)|0)|0;e=ut(k)|0;do{if((e|0)==0){gk();l=ut(k)|0;if((l|0)!=0){m=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{m=e}}while(0);be(m|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=105792,c[v+16>>2]=d,v)|0);e=e6(m)|0;c[e+16>>2]=2;h[e+24>>3]=j;h[e+32>>3]=0.0;a[e+8|0]=0;uu(m);j=+h[b+16>>3];m=uA(g|0)|0;e=(m+7|0)+(uA(d|0)|0)|0;m=ut(e)|0;do{if((m|0)==0){gk();k=ut(e)|0;if((k|0)!=0){n=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{n=m}}while(0);be(n|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=104736,c[v+16>>2]=d,v)|0);m=e6(n)|0;c[m+16>>2]=2;h[m+24>>3]=j;h[m+32>>3]=0.0;a[m+8|0]=0;uu(n);j=+h[b+24>>3];n=uA(g|0)|0;m=(n+4|0)+(uA(d|0)|0)|0;n=ut(m)|0;do{if((n|0)==0){gk();e=ut(m)|0;if((e|0)!=0){o=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{o=n}}while(0);be(o|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=104152,c[v+16>>2]=d,v)|0);n=e6(o)|0;c[n+16>>2]=2;h[n+24>>3]=j;h[n+32>>3]=0.0;a[n+8|0]=0;uu(o);j=+h[b+32>>3];o=uA(g|0)|0;n=(o+6|0)+(uA(d|0)|0)|0;o=ut(n)|0;do{if((o|0)==0){gk();m=ut(n)|0;if((m|0)!=0){p=m;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{p=o}}while(0);be(p|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=73440,c[v+16>>2]=d,v)|0);o=e6(p)|0;c[o+16>>2]=2;h[o+24>>3]=j;h[o+32>>3]=0.0;a[o+8|0]=0;uu(p);j=+h[b+40>>3];p=uA(g|0)|0;o=(p+4|0)+(uA(d|0)|0)|0;p=ut(o)|0;do{if((p|0)==0){gk();n=ut(o)|0;if((n|0)!=0){q=n;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{q=p}}while(0);be(q|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=103256,c[v+16>>2]=d,v)|0);p=e6(q)|0;c[p+16>>2]=2;h[p+24>>3]=j;h[p+32>>3]=0.0;a[p+8|0]=0;uu(q);j=+h[b+56>>3];q=uA(g|0)|0;p=(q+4|0)+(uA(d|0)|0)|0;q=ut(p)|0;do{if((q|0)==0){gk();o=ut(p)|0;if((o|0)!=0){r=o;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{r=q}}while(0);be(r|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=99528,c[v+16>>2]=d,v)|0);q=e6(r)|0;c[q+16>>2]=2;h[q+24>>3]=j;h[q+32>>3]=0.0;a[q+8|0]=0;uu(r);r=c[b>>2]|0;if((r|0)>0){q=c[b+48>>2]|0;j=+((q|0)/(r|0)&-1|0);p=uA(g|0)|0;o=(p+12|0)+(uA(d|0)|0)|0;p=ut(o)|0;do{if((p|0)==0){gk();n=ut(o)|0;if((n|0)!=0){s=n;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{s=p}}while(0);be(s|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=98432,c[v+16>>2]=d,v)|0);p=e6(s)|0;c[p+16>>2]=2;h[p+24>>3]=j;h[p+32>>3]=0.0;a[p+8|0]=0;uu(s);j=+((q|0)%(r|0)&-1|0);q=uA(g|0)|0;s=(q+12|0)+(uA(d|0)|0)|0;q=ut(s)|0;do{if((q|0)==0){gk();p=ut(s)|0;if((p|0)!=0){t=p;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{t=q}}while(0);be(t|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=97816,c[v+16>>2]=d,v)|0);q=e6(t)|0;c[q+16>>2]=2;h[q+24>>3]=j;h[q+32>>3]=0.0;a[q+8|0]=0;uu(t);t=c[b+64>>2]|0;j=+((t|0)/(r|0)&-1|0);q=uA(g|0)|0;s=(q+12|0)+(uA(d|0)|0)|0;q=ut(s)|0;do{if((q|0)==0){gk();p=ut(s)|0;if((p|0)!=0){u=p;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{u=q}}while(0);be(u|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=97216,c[v+16>>2]=d,v)|0);q=e6(u)|0;c[q+16>>2]=2;h[q+24>>3]=j;h[q+32>>3]=0.0;a[q+8|0]=0;uu(u);j=+((t|0)%(r|0)&-1|0);r=uA(g|0)|0;t=(r+12|0)+(uA(d|0)|0)|0;r=ut(t)|0;do{if((r|0)==0){gk();u=ut(t)|0;if((u|0)!=0){w=u;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{w=r}}while(0);be(w|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=96656,c[v+16>>2]=d,v)|0);r=e6(w)|0;c[r+16>>2]=2;h[r+24>>3]=j;h[r+32>>3]=0.0;a[r+8|0]=0;uu(w);i=f;return}j=+h[b+72>>3];w=uA(g|0)|0;r=(w+7|0)+(uA(d|0)|0)|0;w=ut(r)|0;do{if((w|0)==0){gk();t=ut(r)|0;if((t|0)!=0){x=t;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{x=w}}while(0);be(x|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=100616,c[v+16>>2]=d,v)|0);w=e6(x)|0;c[w+16>>2]=2;h[w+24>>3]=j;h[w+32>>3]=0.0;a[w+8|0]=0;uu(x);j=+h[b+80>>3];x=uA(g|0)|0;w=(x+12|0)+(uA(d|0)|0)|0;x=ut(w)|0;do{if((x|0)==0){gk();r=ut(w)|0;if((r|0)!=0){y=r;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{y=x}}while(0);be(y|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=101216,c[v+16>>2]=d,v)|0);x=e6(y)|0;c[x+16>>2]=2;h[x+24>>3]=j;h[x+32>>3]=0.0;a[x+8|0]=0;uu(y);j=+h[b+88>>3];y=uA(g|0)|0;x=(y+12|0)+(uA(d|0)|0)|0;y=ut(x)|0;do{if((y|0)==0){gk();w=ut(x)|0;if((w|0)!=0){z=w;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{z=y}}while(0);be(z|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=99968,c[v+16>>2]=d,v)|0);y=e6(z)|0;c[y+16>>2]=2;h[y+24>>3]=j;h[y+32>>3]=0.0;a[y+8|0]=0;uu(z);j=+(c[b+48>>2]|0);z=uA(g|0)|0;y=(z+10|0)+(uA(d|0)|0)|0;z=ut(y)|0;do{if((z|0)==0){gk();x=ut(y)|0;if((x|0)!=0){A=x;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{A=z}}while(0);be(A|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=72824,c[v+16>>2]=d,v)|0);z=e6(A)|0;c[z+16>>2]=2;h[z+24>>3]=j;h[z+32>>3]=0.0;a[z+8|0]=0;uu(A);j=+(c[b+64>>2]|0);b=uA(g|0)|0;A=(b+10|0)+(uA(d|0)|0)|0;b=ut(A)|0;do{if((b|0)==0){gk();z=ut(A)|0;if((z|0)!=0){B=z;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74192,v)|0)}else{B=b}}while(0);be(B|0,172512,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=225064,c[v+16>>2]=d,v)|0);d=e6(B)|0;c[d+16>>2]=2;h[d+24>>3]=j;h[d+32>>3]=0.0;a[d+8|0]=0;uu(B);i=f;return}function le(b,d,e){b=+b;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0.0;f=i;i=i+48|0;g=f|0;do{if((c[64808+(d*688&-1)>>2]|0)==1){j=64864+(d*688&-1)|0;if((aY(j|0,132824)|0)==0){ud(e,150,74984,1.0,b);break}a[e]=34;k=e+1|0;t3(g,b);l=b- +O(+b);t4(k,149,j,g,l);j=a8(e|0,10)|0;if((j|0)!=0){k=j;do{a[k]=32;k=a8(e|0,10)|0;}while((k|0)!=0)}k=e+(uA(e|0)|0)|0;w=34;a[k]=w&255;w=w>>8;a[k+1|0]=w&255}else{if((a[64788+(d*688&-1)|0]&1)==0){ud(e,150,64864+(d*688&-1)|0,1.0,b);break}else{ud(e,150,64864+(d*688&-1)|0,1.0,+R(+(+h[64792+(d*688&-1)>>3]),+b));break}}}while(0);aK(e|0,c[8248]|0);aF(32,c[8248]|0);i=f;return}function lf(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0.0,H=0,I=0,J=0;e=i;f=ut(150)|0;do{if((f|0)==0){gk();g=ut(150)|0;if((g|0)!=0){j=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=126456,v)|0)}else{j=f}}while(0);f=c[3526]|0;g=(f|0)!=0?f:c[10030]|0;c[8248]=g;if((d|0)>0){k=0;l=b;n=g}else{o=g;p=aD(o|0)|0;uu(j);i=e;return}L14252:while(1){g=l+300|0;b=c[g>>2]|0;cf(n|0,203280,(v=i,i=i+24|0,c[v>>2]=k,c[v+8>>2]=d,c[v+16>>2]=b,v)|0);b=c[l+16>>2]|0;do{if((b|0)!=0){if((a[b]|0)==0){break}f=uA(b|0)<<1;q=ut(f)|0;if((q|0)==0){gk();r=ut(f)|0;if((r|0)==0){s=10464;break L14252}else{t=r}}else{t=q}q=t;r=b;while(1){f=a[r]|0;if(f<<24>>24==10){u=q+1|0;a[q]=92;a[u]=110;w=u}else{a[q]=f;w=q}if((a[r]|0)==0){break}else{q=w+1|0;r=r+1|0}}cf(c[8248]|0,15e4,(v=i,i=i+8|0,c[v>>2]=t,v)|0);uu(t)}}while(0);aI(132264,6,1,c[8248]|0);b=l+12|0;r=c[b>>2]|0;do{if((r|0)==137|(r|0)==118){q=c[8248]|0;aI(96928,22,1,q|0)}else if((r|0)==252){aI(75296,29,1,c[8248]|0)}else if((r|0)==416|(r|0)==432){aI(204480,22,1,c[8248]|0)}else if((r|0)==400){aI(210472,7,1,c[8248]|0)}else if((r|0)==257){aI(82936,23,1,c[8248]|0)}else if((r|0)==153|(r|0)==86){aI(119232,11,1,c[8248]|0)}else if((r|0)==345){aI(90824,4,1,c[8248]|0)}else if((r|0)==169|(r|0)==102){aI(103984,11,1,c[8248]|0)}else if((r|0)==225){aI(215896,16,1,c[8248]|0)}else if((r|0)==368){aI(222792,6,1,c[8248]|0)}else if(!((r|0)==1|(r|0)==18|(r|0)==51|(r|0)==64|(r|0)==33|(r|0)==177|(r|0)==193|(r|0)==209)){if((a[37400]&1)==0){break}cf(c[m>>2]|0,200512,(v=i,i=i+8|0,c[v>>2]=(r|0)==392?195192:184488,v)|0)}}while(0);r=l+316|0;if((c[r>>2]|0)!=0){q=c[8248]|0;aI(179312,7,1,q|0)}aI(175152,6,1,c[8248]|0);q=c[b>>2]|0;do{if((q|0)==392){x=(c[c[l+276>>2]>>2]|0)==4?102:392;s=10499}else if((q|0)==368){f=c[c[l+224>>2]>>2]|0;if((f|0)==0){break}u=l+304|0;y=l+308|0;z=f;do{f=c[z+60>>2]|0;A=uA(f|0)<<1;B=ut(A)|0;if((B|0)==0){gk();C=ut(A)|0;if((C|0)==0){s=10491;break L14252}else{D=C}}else{D=B}B=D;C=f;while(1){f=a[C]|0;if(f<<24>>24==10){A=B+1|0;a[B]=92;a[A]=110;E=A}else{a[B]=f;E=B}if((a[C]|0)==0){break}else{B=E+1|0;C=C+1|0}}le(+h[z+24>>3],c[u>>2]|0,j);le(+h[z+32>>3],c[y>>2]|0,j);cf(c[8248]|0,171376,(v=i,i=i+8|0,c[v>>2]=D,v)|0);uu(D);z=c[z>>2]|0;}while((z|0)!=0)}else{x=q;s=10499}}while(0);do{if((s|0)==10499){s=0;q=l+320|0;if((c[g>>2]|0)<=0){break}b=l+304|0;z=l+308|0;y=l+72|0;u=l+64|0;C=l+28|0;B=c[q>>2]|0;f=0;while(1){if((uG(B|0,56440,64)|0)==0){A=c[8248]|0;aF(10,A|0)}else{A=B+8|0;le(+h[A>>3],c[b>>2]|0,j);F=B+16|0;le(+h[F>>3],c[z>>2]|0,j);if((x|0)==345){le(+h[B+40>>3],c[z>>2]|0,j)}else if((x|0)==169|(x|0)==102){s=10506}else if((x|0)==400){cf(c[8248]|0,184536,(v=i,i=i+8|0,h[v>>3]=+h[B+24>>3],v)|0)}else if((x|0)==416|(x|0)==432){cf(c[8248]|0,153776,(v=i,i=i+8|0,c[v>>2]=~~+h[B+40>>3],v)|0);cf(c[8248]|0,153776,(v=i,i=i+8|0,c[v>>2]=~~+h[B+48>>3],v)|0);cf(c[8248]|0,153776,(v=i,i=i+8|0,c[v>>2]=~~+h[B+56>>3],v)|0);cf(c[8248]|0,153776,(v=i,i=i+8|0,c[v>>2]=~~+h[B+32>>3],v)|0)}else if((x|0)==153|(x|0)==86){le(+h[B+48>>3],c[b>>2]|0,j);le(+h[B+56>>3],c[b>>2]|0,j)}else if((x|0)==137|(x|0)==118){le(+h[B+48>>3],c[b>>2]|0,j);le(+h[B+56>>3],c[b>>2]|0,j);s=10506}else if((x|0)==257){le(+h[B+32>>3],c[z>>2]|0,j);le(+h[B+40>>3],c[z>>2]|0,j);le(+h[B+24>>3],c[z>>2]|0,j)}else if((x|0)==252){le(+h[B+32>>3],c[z>>2]|0,j);le(+h[B+40>>3],c[z>>2]|0,j);le(+h[B+24>>3],c[z>>2]|0,j);le((+h[A>>3]- +h[B+48>>3])*2.0,c[b>>2]|0,j)}else if((x|0)==225){le(+h[B+56>>3]- +h[A>>3],c[b>>2]|0,j);le(+h[B+40>>3]- +h[F>>3],c[z>>2]|0,j)}if((s|0)==10506){s=0;le(+h[B+32>>3],c[z>>2]|0,j);le(+h[B+40>>3],c[z>>2]|0,j)}F=c[r>>2]|0;do{if((F|0)!=0){G=+h[F+(f<<3)>>3];A=c[u>>2]|0;if(+h[y>>3]<0.0&(A|0)==3){H=c[8248]|0;I=~~G;cf(H|0,150832,(v=i,i=i+8|0,c[v>>2]=I,v)|0);break}if((A|0)==6){le(G,3,j);break}if((c[C>>2]|0)!=-6){break}le(G,3,j)}}while(0);F=c[8248]|0;A=c[(c[q>>2]|0)+(f<<6)>>2]|0;if((A|0)==0){J=105}else{J=(A|0)==1?111:117}cf(F|0,148928,(v=i,i=i+8|0,c[v>>2]=J,v)|0)}F=f+1|0;if((F|0)<(c[g>>2]|0)){B=B+64|0;f=F}else{break}}}}while(0);aF(10,c[8248]|0);g=k+1|0;r=c[8248]|0;if((g|0)<(d|0)){k=g;l=c[l>>2]|0;n=r}else{o=r;s=10527;break}}if((s|0)==10527){p=aD(o|0)|0;uu(j);i=e;return}else if((s|0)==10464){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=134528,v)|0)}else if((s|0)==10491){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=134528,v)|0)}}function lg(){var b=0.0,d=0,e=0.0,f=0.0,i=0,j=0.0,k=0.0,l=0.0,m=0,n=0.0,o=0.0;if((a[33656]&1)==0){return}b=+h[4211];d=c[8417]|0;e=+(d|0);g[178]=b/e;f=+h[4212];i=c[8416]|0;j=+(i|0);k=f/j;g[38]=k;l=+(c[8420]|0)/e;g[184]=l;m=c[8419]|0;if((a[33673]&1)==0){n=+(m|0)/j}else{n=1.0- +(m+1|0)/j}j=n;g[44]=j;if((c[8453]|0)==0){o=j}else{n=1.0- +h[4243];g[38]=n*k;k=n*j;g[44]=k;o=k}g[184]=+h[4213]+(l-(b+-1.0)/+(d<<1|0));g[44]=+h[4214]+(o-(f+-1.0)/+(i<<1|0));return}function lh(){var b=0;if((a[14080]&1)==0){return}cM[c[(c[3524]|0)+168>>2]&511](5);do{if((a[33512]&1)==0){cS[c[(c[3524]|0)+44>>2]&511]();a[14088]=0}else{c[8415]=(c[8415]|0)+1;if((a[33656]&1)==0){break}do{if((a[33672]&1)==0){b=(c[8420]|0)+1|0;c[8420]=b;if((b|0)!=(c[8417]|0)){break}c[8420]=0;b=(c[8419]|0)+1|0;c[8419]=b;if((b|0)!=(c[8416]|0)){break}c[8420]=0}else{b=(c[8419]|0)+1|0;c[8419]=b;if((b|0)!=(c[8416]|0)){break}c[8419]=0;b=(c[8420]|0)+1|0;c[8420]=b;if((b|0)!=(c[8417]|0)){break}c[8420]=0}}while(0);lg()}}while(0);aD(c[10030]|0);hN(c[8494]|0,c[8492]|0,27760,27744,27752,27736);hM();hW();return}function li(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0;d=i;e=ut(150)|0;do{if((e|0)==0){gk();f=ut(150)|0;if((f|0)!=0){g=f;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=146952,v)|0)}else{g=e}}while(0);e=c[3526]|0;f=(e|0)!=0?e:c[10030]|0;c[8248]=f;if((b|0)>0){j=0;k=43312;l=f}else{n=f;o=aD(n|0)|0;uu(g);i=d;return}L14377:while(1){f=c[k>>2]|0;cf(l|0,144784,(v=i,i=i+16|0,c[v>>2]=j,c[v+8>>2]=b,v)|0);e=c[f+16>>2]|0;do{if((e|0)!=0){if((a[e]|0)==0){break}p=uA(e|0)<<1;q=ut(p)|0;if((q|0)==0){gk();r=ut(p)|0;if((r|0)==0){s=10562;break L14377}else{t=r}}else{t=q}q=t;r=e;while(1){p=a[r]|0;if(p<<24>>24==10){u=q+1|0;a[q]=92;a[u]=110;w=u}else{a[q]=p;w=q}if((a[r]|0)==0){break}else{q=w+1|0;r=r+1|0}}cf(c[8248]|0,15e4,(v=i,i=i+8|0,c[v>>2]=t,v)|0);uu(t)}}while(0);e=f+12|0;r=c[e>>2]|0;do{if((r|0)==368){q=c[c[f+224>>2]>>2]|0;if((q|0)==0){break}else{x=q}do{q=c[x+60>>2]|0;p=uA(q|0)<<1;u=ut(p)|0;if((u|0)==0){gk();y=ut(p)|0;if((y|0)==0){s=10573;break L14377}else{z=y}}else{z=u}u=z;y=q;while(1){q=a[y]|0;if(q<<24>>24==10){p=u+1|0;a[u]=92;a[p]=110;A=p}else{a[u]=q;A=u}if((a[y]|0)==0){break}else{u=A+1|0;y=y+1|0}}le(+h[x+24>>3],2,g);le(+h[x+32>>3],1,g);le(+h[x+40>>3],0,g);cf(c[8248]|0,171376,(v=i,i=i+8|0,c[v>>2]=z,v)|0);uu(z);x=c[x>>2]|0;}while((x|0)!=0)}else if((r|0)==1|(r|0)==18|(r|0)==33|(r|0)==64|(r|0)==225|(r|0)==400){if((a[46752]&1)!=0){y=c[f+260>>2]|0;L14410:do{if((y|0)==0){s=10584}else{u=f+252|0;q=0;p=y;while(1){B=c[8248]|0;if((q|0)>=(c[u>>2]|0)){C=B;break L14410}D=p+8|0;E=c[D>>2]|0;cf(B|0,141688,(v=i,i=i+16|0,c[v>>2]=q,c[v+8>>2]=E,v)|0);if((c[e>>2]|0)==225){E=c[(c[p>>2]|0)+12>>2]|0;B=c[8248]|0;aI(139920,24,1,B|0);F=E}else{F=0}aI(175152,6,1,c[8248]|0);if((c[D>>2]|0)>0){E=c[p+12>>2]|0;B=F;G=0;while(1){H=E+8|0;le(+h[H>>3],2,g);I=E+16|0;le(+h[I>>3],1,g);J=E+24|0;le(+h[J>>3],0,g);K=c[e>>2]|0;if((K|0)==225){le(+h[B+8>>3]- +h[H>>3],2,g);le(+h[B+16>>3]- +h[I>>3],1,g);le(+h[B+24>>3]- +h[J>>3],0,g);L=B+64|0}else if((K|0)==400){cf(c[8248]|0,184536,(v=i,i=i+8|0,h[v>>3]=+h[E+32>>3],v)|0);L=B}else{L=B}K=c[E>>2]|0;if((K|0)==0){M=105}else{M=(K|0)==1?111:117}cf(c[8248]|0,137904,(v=i,i=i+8|0,c[v>>2]=M,v)|0);K=G+1|0;if((K|0)<(c[D>>2]|0)){E=E+64|0;B=L;G=K}else{break}}}G=c[p>>2]|0;if((G|0)==0){s=10584;break}else{q=q+1|0;p=G}}}}while(0);if((s|0)==10584){s=0;C=c[8248]|0}aF(10,C|0)}if((c[11690]|0)==0){break}y=c[f+256>>2]|0;if((y|0)==0){break}else{N=0;O=y}while(1){y=c[O+44>>2]|0;p=c[O+4>>2]|0;if((a[O+8|0]|0)==0){P=N}else{q=c[8248]|0;u=N+1|0;G=O+9|0;cf(q|0,136176,(v=i,i=i+16|0,c[v>>2]=N,c[v+8>>2]=G,v)|0);P=u}if((y|0)>0){u=y;y=p;while(1){p=u-1|0;le(+h[y+8>>3],2,g);le(+h[y+16>>3],1,g);le(+h[y+24>>3],0,g);aF(10,c[8248]|0);if((p|0)>0){u=p;y=y+64|0}else{break}}}aF(10,c[8248]|0);y=c[O>>2]|0;if((y|0)==0){break}else{N=P;O=y}}}else{aI(143080,53,1,c[m>>2]|0)}}while(0);e=j+1|0;r=c[8248]|0;if((e|0)<(b|0)){j=e;k=f|0;l=r}else{n=r;s=10608;break}}if((s|0)==10562){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=134528,v)|0)}else if((s|0)==10608){o=aD(n|0)|0;uu(g);i=d;return}else if((s|0)==10573){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=134528,v)|0)}}function lj(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;e=(b|0)==0;if((c[8244]|0)==(b|0)&(e^1)){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=141656,v)|0)}if((a[33512]&1)!=0){f=c[m>>2]|0;aI(139872,46,1,f|0);i=d;return}f=c[3524]|0;do{if((f|0)!=0){if((a[14080]&1)==0){break}cS[c[f+40>>2]&511]();a[14080]=0;c[10028]=0}}while(0);if(e){a[33072]=0;if((c[8244]|0)==0){i=d;return}e=c[10030]|0;do{if(a[32984]|0){a2(e|0);a[32984]=0}else{if((e|0)==(c[10028]|0)){break}az(e|0)}}while(0);c[10030]=c[n>>2];uu(c[8244]|0);c[8244]=0;e=c[10028]|0;if((e|0)!=0){az(e|0)}c[10028]=0;i=d;return}do{if((a[b]|0)==124){if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=bt(b+1|0,137896)|0;if((e|0)==0){uj(c[13898]|0,136136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{a[32984]=1;g=e;break}}else{e=c[3524]|0;do{if((e|0)==0){h=10634}else{if((c[e+96>>2]&4|0)==0){h=10634;break}j=bF(b|0,134520)|0}}while(0);if((h|0)==10634){j=bF(b|0,137896)|0}if((j|0)!=0){g=j;break}uj(c[13898]|0,132784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);a[33072]=0;if((c[8244]|0)!=0){j=c[10030]|0;do{if(a[32984]|0){a2(j|0);a[32984]=0}else{if((j|0)==(c[10028]|0)){break}az(j|0)}}while(0);c[10030]=c[n>>2];uu(c[8244]|0);c[8244]=0;j=c[10028]|0;if((j|0)!=0){az(j|0)}c[10028]=0}c[10030]=g;c[8244]=b;b=c[3524]|0;if((b|0)==0){k=0}else{k=(c[b+96>>2]|0)>>>2&1}a[33072]=k;i=d;return}function lk(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;b=i;d=c[3524]|0;if((d|0)==0){uf(-1,131560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=c[8244]|0;L14513:do{if((e|0)!=0){do{if((c[d+96>>2]&64|0)==0){f=e}else{if((a[37400]&1)==0){g=e}else{h=c[m>>2]|0;cf(h|0,130376,(v=i,i=i+8|0,c[v>>2]=e,v)|0);g=c[8244]|0}a[33072]=0;if((g|0)==0){break L14513}h=c[10030]|0;do{if(a[32984]|0){a2(h|0);a[32984]=0}else{if((h|0)==(c[10028]|0)){break}az(h|0)}}while(0);c[10030]=c[n>>2];uu(c[8244]|0);c[8244]=0;h=c[10028]|0;if((h|0)==0){c[10028]=0;break L14513}else{az(h|0);h=c[8244]|0;c[10028]=0;if((h|0)==0){break L14513}else{f=h;break}}}}while(0);h=(a[33072]&1)==0;if((c[(c[3524]|0)+96>>2]&4|0)==0){if(h){break}}else{if(!h){break}}h=(uA(f|0)|0)+1|0;j=ut(h)|0;do{if((j|0)==0){gk();k=ut(h)|0;if((k|0)!=0){l=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=129080,v)|0)}else{l=j}}while(0);j=c[8244]|0;uB(l|0,j|0);lj(l);if((l|0)==(c[8244]|0)|(l|0)==0){break}uu(l)}}while(0);if((a[14080]&1)!=0){i=b;return}cS[c[(c[3524]|0)+36>>2]&511]();a[14080]=1;i=b;return}function ll(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0.0,ai=0,aj=0,ak=0,al=0,am=0.0,an=0,ao=0,ap=0,aq=0,ar=0,as=0.0,at=0,au=0,av=0.0,aw=0.0,ax=0,ay=0.0,az=0,aA=0,aB=0,aC=0.0;b=i;i=i+208|0;d=b|0;e=b+24|0;f=b+48|0;j=b+72|0;k=b+96|0;l=b+120|0;m=b+144|0;n=b+168|0;o=b+192|0;p=b+200|0;c[13898]=(c[13898]|0)+1;if((a[33512]&1)!=0){lm()}if((a[14080]&1)==0){lk()}do{if(a[14088]|0){if((a[33512]&1)==0){break}if(!(a[13032]|0)){break}q=c[(c[3524]|0)+104>>2]|0;if((q|0)!=0){cS[q&511]()}a[13032]=0}else{cS[c[(c[3524]|0)+52>>2]&511]();a[14088]=1}}while(0);cM[c[(c[3524]|0)+168>>2]&511](0);q=c[3524]|0;if((c[q+96>>2]&16|0)!=0){c[7271]=-1}c[13880]=0;c[13881]=(c[q+8>>2]|0)-1;c[13882]=0;c[13883]=(c[q+12>>2]|0)-1;a[33656]=0;c[8415]=0;a[33936]=0;q=c[13898]|0;r=c[8272]|0;L14563:do{if((q|0)<(r|0)){s=m|0;t=m+8|0;u=j|0;w=j+8|0;x=f|0;y=f+8|0;z=e|0;A=e+8|0;B=d|0;C=d+8|0;D=l|0;E=l+8|0;F=k|0;G=k+8|0;H=n|0;I=n+8|0;J=q;K=r;L14565:while(1){L=c[1054]|0;M=(a[L+(J*40&-1)|0]&1)==0;N=c[L+(J*40&-1)+36>>2]|0;O=L+(J*40&-1)+32|0;L14567:do{if(M){P=c[O>>2]|0;Q=10719}else{R=c[10036]|0;S=0;while(1){if((S|0)>=(N|0)){Q=10700;break}if((a[R+((c[O>>2]|0)+S|0)|0]|0)==(a[S+103664|0]|0)){S=S+1|0}else{break}}if((Q|0)==10700){Q=0;if((S|0)==1){break L14563}}R=c[O>>2]|0;if(!((N|0)>0&(M^1))){P=R;Q=10719;break}T=c[10036]|0;U=0;V=0;W=R;while(1){if((a[U+125568|0]|0)==(a[T+(U+W|0)|0]|0)){X=W;Y=V}else{if((U|0)!=2){P=R;Q=10719;break L14567}X=W-1|0;Y=1}Z=U+1|0;if((Z|0)<(Y+N|0)){U=Z;V=Y;W=X}else{break}}if((Y|0)==0){if(!((U|0)==5|(U|0)==1)){P=R;Q=10719;break}}W=J+1|0;c[13898]=W;if((W|0)>=(K|0)){break}L14588:do{if((a[L+(W*40&-1)|0]&1)!=0){V=c[L+(W*40&-1)+36>>2]|0;T=L+(W*40&-1)+32|0;S=c[10036]|0;Z=0;while(1){if((Z|0)>=(V|0)){break}if((a[S+((c[T>>2]|0)+Z|0)|0]|0)==(a[Z+103664|0]|0)){Z=Z+1|0}else{break L14588}}if((Z|0)==1){break L14567}}}while(0);a[14176]=1;is(n);a[14176]=0;if((c[H>>2]|0)!=3){c[13898]=W;break}R=c[I>>2]|0;if((R|0)==0){break}uu(c[8453]|0);c[8453]=R}}while(0);L14599:do{if((Q|0)==10719){Q=0;L14601:do{if((K|0)>(J|0)){if(M){Q=10765;break}O=c[10036]|0;R=0;while(1){if((R|0)>=(N|0)){break}if((a[O+(P+R|0)|0]|0)==(a[R+124352|0]|0)){R=R+1|0}else{Q=10735;break L14601}}if((R|0)!=4){Q=10735;break}Z=J+1|0;c[13898]=Z;if((Z|0)>=(K|0)){break L14599}L14610:do{if((a[L+(Z*40&-1)|0]&1)!=0){U=c[L+(Z*40&-1)+36>>2]|0;T=L+(Z*40&-1)+32|0;S=0;while(1){if((S|0)>=(U|0)){break}if((a[O+((c[T>>2]|0)+S|0)|0]|0)==(a[S+103664|0]|0)){S=S+1|0}else{break L14610}}if((S|0)==1){break L14599}}}while(0);a[14176]=1;is(m);a[14176]=0;if((c[s>>2]|0)!=3){c[13898]=Z;break L14599}O=c[t>>2]|0;if((O|0)==0){break L14599}uu(c[8454]|0);c[8454]=O;break L14599}else{Q=10735}}while(0);L14621:do{if((Q|0)==10735){Q=0;if(M){Q=10765;break}do{if((N|0)>0){W=c[10036]|0;O=0;R=0;T=P;while(1){if((a[O+122880|0]|0)==(a[W+(O+T|0)|0]|0)){_=T;$=R}else{if((O|0)!=3){break}_=T-1|0;$=1}U=O+1|0;if((U|0)<($+N|0)){O=U;R=$;T=_}else{Q=10742;break}}do{if((Q|0)==10742){Q=0;if(($|0)==0){if(!((O|0)==2|(O|0)==8)){break}}a[33936]=0;c[13898]=J+1;break L14599}}while(0);if(M){Q=10765;break L14621}L14639:do{if((N|0)>0){O=c[10036]|0;T=0;R=0;W=P;while(1){if((a[T+121536|0]|0)==(a[O+(T+W|0)|0]|0)){ab=W;ac=R}else{if((T|0)!=5){break L14639}ab=W-1|0;ac=1}S=T+1|0;if((S|0)<(ac+N|0)){T=S;R=ac;W=ab}else{break}}if((ac|0)==0){if(!((T|0)==4|(T|0)==10)){break}}a[33936]=1;c[13898]=J+1;break L14599}}while(0);if(M){Q=10765;break L14621}if((N|0)<=0){break}W=c[10036]|0;R=0;O=0;S=P;while(1){if((a[R+119784|0]|0)==(a[W+(R+S|0)|0]|0)){ad=S;ae=O}else{if((R|0)!=3){Q=10765;break L14621}ad=S-1|0;ae=1}af=R+1|0;if((af|0)<(ae+N|0)){R=af;O=ae;S=ad}else{break}}do{if((ae|0)!=0|(af|0)==3){if((a[33656]&1)!=0){Q=10925;break L14565}}else{S=(a[33656]&1)!=0;if((af|0)==7){if(S){Q=10926;break L14565}else{break}}else{if(S){break L14621}else{Q=10920;break L14565}}}}while(0);a[33656]=1;ag=J+1|0;c[13898]=ag;if((ag|0)>=(K|0)){Q=10923;break L14565}L14669:do{if((a[L+(ag*40&-1)|0]&1)!=0){S=c[L+(ag*40&-1)+36>>2]|0;O=L+(ag*40&-1)+32|0;R=c[10036]|0;W=0;while(1){if((W|0)>=(S|0)){break}if((a[R+((c[O>>2]|0)+W|0)|0]|0)==(a[W+103664|0]|0)){W=W+1|0}else{break L14669}}if((W|0)==1){Q=10924;break L14565}}}while(0);O=is(l)|0;R=c[O>>2]|0;if((R|0)==1){ah=+(c[O+8>>2]|0)}else if((R|0)==2){ah=+h[O+8>>3]}else if((R|0)==3){ah=+uz(c[O+8>>2]|0,0)}else{Q=10781;break L14565}if((c[D>>2]|0)==3){uu(c[E>>2]|0);c[D>>2]=1}c[8416]=~~ah;ai=c[13898]|0;O=c[8272]|0;if((ai|0)>=(O|0)){Q=10939;break L14565}R=c[1054]|0;S=(a[R+(ai*40&-1)|0]&1)==0;if(S){Q=10934;break L14565}T=c[R+(ai*40&-1)+36>>2]|0;U=R+(ai*40&-1)+32|0;V=c[10036]|0;aj=0;while(1){if((aj|0)>=(T|0)){Q=10789;break}if((a[V+((c[U>>2]|0)+aj|0)|0]|0)==(a[aj+103664|0]|0)){aj=aj+1|0}else{Q=10790;break}}if((Q|0)==10790){Q=0;if(S){Q=10935;break L14565}}else if((Q|0)==10789){Q=0;if((aj|0)==1|S){Q=10936;break L14565}}U=c[R+(ai*40&-1)+36>>2]|0;V=R+(ai*40&-1)+32|0;T=c[10036]|0;ak=0;while(1){if((ak|0)>=(U|0)){break}if((a[T+((c[V>>2]|0)+ak|0)|0]|0)==(a[ak+148464|0]|0)){ak=ak+1|0}else{Q=10937;break L14565}}if((ak|0)!=1){Q=10938;break L14565}al=ai+1|0;c[13898]=al;if((al|0)>=(O|0)){Q=10929;break L14565}L14699:do{if((a[R+(al*40&-1)|0]&1)!=0){V=c[R+(al*40&-1)+36>>2]|0;U=R+(al*40&-1)+32|0;S=0;while(1){if((S|0)>=(V|0)){break}if((a[T+((c[U>>2]|0)+S|0)|0]|0)==(a[S+103664|0]|0)){S=S+1|0}else{break L14699}}if((S|0)==1){Q=10930;break L14565}}}while(0);T=is(k)|0;R=c[T>>2]|0;if((R|0)==2){am=+h[T+8>>3]}else if((R|0)==3){am=+uz(c[T+8>>2]|0,0)}else if((R|0)==1){am=+(c[T+8>>2]|0)}else{Q=10807;break L14565}if((c[F>>2]|0)==3){uu(c[G>>2]|0);c[F>>2]=1}c[8417]=~~am;h[4215]=+g[178];h[4216]=+g[38];h[4217]=+g[184];h[4218]=+g[44];c[8419]=0;c[8420]=0;break L14599}}while(0);if((a[33656]&1)==0){Q=10922;break L14565}}}while(0);if((Q|0)==10765){Q=0;if((a[33656]&1)==0){Q=10921;break L14565}}Z=(N|0)>0;T=c[10036]|0;R=23840;O=119784;L14718:while(1){L14720:do{if(!M){if(Z){ak=0;U=0;V=P;while(1){W=a[O+ak|0]|0;if(W<<24>>24==(a[T+(ak+V|0)|0]|0)){an=V;ao=U}else{if(W<<24>>24!=36){break L14720}an=V-1|0;ao=1}ap=ak+1|0;if((ap|0)<(ao+N|0)){ak=ap;U=ao;V=an}else{break}}if((ao|0)==0){aq=ap}else{ar=R;break L14718}}else{aq=0}V=a[O+aq|0]|0;if((V<<24>>24|0)==36|(V<<24>>24|0)==0){ar=R;break L14718}}}while(0);V=R+8|0;U=c[V>>2]|0;if((U|0)==0){ar=V;break}else{R=V;O=U}}O=c[ar+4>>2]|0;if((O|0)==1){a[33672]=1;c[13898]=J+1;break}else if((O|0)==6){c[13898]=J+1;R=is(e)|0;T=c[R>>2]|0;if((T|0)==1){as=+(c[R+8>>2]|0)}else if((T|0)==2){as=+h[R+8>>3]}else if((T|0)==3){as=+uz(c[R+8>>2]|0,0)}else{Q=10865;break L14565}if((c[z>>2]|0)==3){uu(c[A>>2]|0);c[z>>2]=1}h[4213]=as;h[4214]=as;R=c[13898]|0;T=c[8272]|0;if((R|0)>=(T|0)){break}Z=c[1054]|0;U=(a[Z+(R*40&-1)|0]&1)==0;if(U){break}V=c[Z+(R*40&-1)+36>>2]|0;ak=Z+(R*40&-1)+32|0;W=c[10036]|0;aj=0;while(1){if((aj|0)>=(V|0)){Q=10873;break}if((a[W+((c[ak>>2]|0)+aj|0)|0]|0)==(a[aj+103664|0]|0)){aj=aj+1|0}else{Q=10874;break}}if((Q|0)==10873){Q=0;if((aj|0)==1|U){break}}else if((Q|0)==10874){Q=0;if(U){break}}ak=c[Z+(R*40&-1)+36>>2]|0;W=Z+(R*40&-1)+32|0;V=c[10036]|0;at=0;while(1){if((at|0)>=(ak|0)){break}if((a[V+((c[W>>2]|0)+at|0)|0]|0)==(a[at+148464|0]|0)){at=at+1|0}else{break L14599}}if((at|0)!=1){break}au=R+1|0;c[13898]=au;if((au|0)>=(T|0)){Q=10927;break L14565}L14759:do{if((a[Z+(au*40&-1)|0]&1)!=0){W=c[Z+(au*40&-1)+36>>2]|0;ak=Z+(au*40&-1)+32|0;U=0;while(1){if((U|0)>=(W|0)){break}if((a[V+((c[ak>>2]|0)+U|0)|0]|0)==(a[U+103664|0]|0)){U=U+1|0}else{break L14759}}if((U|0)==1){Q=10928;break L14565}}}while(0);V=is(d)|0;Z=c[V>>2]|0;if((Z|0)==1){av=+(c[V+8>>2]|0)}else if((Z|0)==2){av=+h[V+8>>3]}else if((Z|0)==3){av=+uz(c[V+8>>2]|0,0)}else{Q=10890;break L14565}if((c[B>>2]|0)==3){uu(c[C>>2]|0);c[B>>2]=1}h[4214]=av;break}else if((O|0)==2){a[33672]=0;c[13898]=J+1;break}else if((O|0)==4){a[33673]=1;c[13898]=J+1;break}else if((O|0)==5){a[33673]=0;c[13898]=J+1;break}else if((O|0)==3){c[13898]=J+1;V=is(j)|0;Z=c[V>>2]|0;if((Z|0)==1){aw=+(c[V+8>>2]|0)}else if((Z|0)==2){aw=+h[V+8>>3]}else if((Z|0)==3){aw=+uz(c[V+8>>2]|0,0)}else{Q=10832;break L14565}if((c[u>>2]|0)==3){uu(c[w>>2]|0);c[u>>2]=1}h[4211]=aw;h[4212]=aw;V=c[13898]|0;Z=c[8272]|0;if((V|0)>=(Z|0)){break}T=c[1054]|0;R=(a[T+(V*40&-1)|0]&1)==0;if(R){break}at=c[T+(V*40&-1)+36>>2]|0;ak=T+(V*40&-1)+32|0;W=c[10036]|0;aj=0;while(1){if((aj|0)>=(at|0)){Q=10840;break}if((a[W+((c[ak>>2]|0)+aj|0)|0]|0)==(a[aj+103664|0]|0)){aj=aj+1|0}else{Q=10841;break}}if((Q|0)==10840){Q=0;if((aj|0)==1|R){break}}else if((Q|0)==10841){Q=0;if(R){break}}ak=c[T+(V*40&-1)+36>>2]|0;W=T+(V*40&-1)+32|0;at=c[10036]|0;O=0;while(1){if((O|0)>=(ak|0)){break}if((a[at+((c[W>>2]|0)+O|0)|0]|0)==(a[O+148464|0]|0)){O=O+1|0}else{break L14599}}if((O|0)!=1){break}ax=V+1|0;c[13898]=ax;if((ax|0)>=(Z|0)){Q=10940;break L14565}L14801:do{if((a[T+(ax*40&-1)|0]&1)!=0){W=c[T+(ax*40&-1)+36>>2]|0;ak=T+(ax*40&-1)+32|0;R=0;while(1){if((R|0)>=(W|0)){break}if((a[at+((c[ak>>2]|0)+R|0)|0]|0)==(a[R+103664|0]|0)){R=R+1|0}else{break L14801}}if((R|0)==1){Q=10941;break L14565}}}while(0);at=is(f)|0;T=c[at>>2]|0;if((T|0)==2){ay=+h[at+8>>3]}else if((T|0)==3){ay=+uz(c[at+8>>2]|0,0)}else if((T|0)==1){ay=+(c[at+8>>2]|0)}else{Q=10857;break L14565}if((c[x>>2]|0)==3){uu(c[y>>2]|0);c[x>>2]=1}h[4212]=ay;break}else{Q=10894;break L14565}}}while(0);N=c[13898]|0;M=c[8272]|0;if((N|0)<(M|0)){J=N;K=M}else{break L14563}}if((Q|0)==10926){uf(J,116664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10927){uf(au,105736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10928){uf(au,105736,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10894){uf(J,104688,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10920){uf(J,107656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10921){uf(J,107656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10922){uf(J,107656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10923){uf(ag,115776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10924){uf(ag,115776,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10925){uf(J,116664,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10929){uf(al,109336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10930){uf(al,109336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10934){uf(ai,113800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10935){uf(ai,113800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10936){uf(ai,113800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10937){uf(ai,113800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10941){uf(ax,106712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10938){uf(ai,113800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10939){uf(ai,113800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10940){uf(ax,106712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10890){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10832){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10781){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10857){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10865){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==10807){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);a[33512]=1;Q=e6(104120)|0;if((Q|0)!=0){a[Q+8|0]=0;c[Q+16>>2]=1;c[Q+24>>2]=1}Q=c[8453]|0;if((Q|0)==0){h[4243]=0.0;lg();hM();i=b;return}fN(33896,o,p,103768);ax=c[3524]|0;ai=~~(+h[o>>3]+ +((c[ax+8>>2]|0)>>>1>>>0>>>0));o=~~(+h[p>>3]+ +(((c[ax+12>>2]|0)-(c[ax+16>>2]|0)|0)>>>0>>>0));p=a[33936]&1;do{if(p<<24>>24==0){az=ax}else{if((a[37456]&1)!=0){az=ax;break}a[37456]=1;al=c[ax+88>>2]|0;if((al|0)==0){az=ax;break}cO[al&255](179864);az=c[3524]|0}}while(0);a[37456]=p;fn(33824,az);ln(ai,o,c[8453]|0,1,0,0,c[8454]|0);if((c[8456]|0)!=0){cM[c[(c[3524]|0)+64>>2]&511](-2)}a[37456]=0;o=Q;Q=1;while(1){ai=a[o]|0;if((ai<<24>>24|0)==0){break}else if((ai<<24>>24|0)==10){aA=Q+1|0}else{aA=Q}o=o+1|0;Q=aA}aA=c[8454]|0;do{if((aA|0)==0){aB=0}else{if((a[aA]|0)==0){aB=aA;break}o=c[(c[3524]|0)+88>>2]|0;if((o|0)==0){aB=aA;break}cO[o&255](aA);aB=c[8454]|0}}while(0);aA=c[3524]|0;ay=+(aa(c[aA+16>>2]|0,Q)>>>0>>>0);aw=ay/+((c[aA+12>>2]|0)>>>0>>>0);h[4243]=aw;do{if((aB|0)==0){aC=aw}else{if((a[aB]|0)==0){aC=aw;break}Q=c[aA+88>>2]|0;if((Q|0)==0){aC=aw;break}cO[Q&255](179864);aC=+h[4243]}}while(0);if(aC<=.9){lg();hM();i=b;return}h[4243]=.05;lg();hM();i=b;return}function lm(){var b=0;if((a[33512]&1)==0){return}if(a[13032]|0){b=c[(c[3524]|0)+104>>2]|0;if((b|0)!=0){cS[b&511]()}a[13032]=0}a[33512]=0;b=e6(104120)|0;if((b|0)!=0){a[b+8|0]=0;c[b+16>>2]=1;c[b+24>>2]=0}if((a[33656]&1)!=0){g[178]=+h[4215];g[38]=+h[4216];g[184]=+h[4217];g[44]=+h[4218]}a[33656]=0;h[4212]=1.0;h[4211]=1.0;uE(33704,0,16);b=c[8453]|0;if((b|0)!=0){uu(b);c[8453]=0}lh();hM();return}function ln(b,d,e,f,g,h,i){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;j=c[3524]|0;if((e|0)==0){return}k=(i|0)!=0;do{if(k){if((a[i]|0)==0){break}l=c[j+88>>2]|0;if((l|0)==0){break}cO[l&255](i)}}while(0);do{if((g|0)==0){m=b;n=e;o=d;p=j+16|0}else{l=a[e]|0;if(l<<24>>24==0){q=e;r=0}else{s=e;t=0;u=l;while(1){l=s+1|0;v=(u<<24>>24==10&1)+t|0;w=a[l]|0;if(w<<24>>24==0){q=l;r=v;break}else{s=l;t=v;u=w}}}u=aa(r,g);t=j+16|0;s=aa(u,c[t>>2]|0)>>>1;if((h|0)==0){m=b;n=q;o=s+d|0;p=t;break}else{m=b-s|0;n=q;o=d;p=t;break}}}while(0);d=j+76|0;q=j+20|0;b=(h|0)==0;g=j+68|0;r=m;m=n;n=o;o=e;while(1){do{if((o|0)==0){x=m}else{e=a8(o|0,10)|0;if((e|0)==0){x=0;break}a[e]=0;x=e}}while(0);do{if((cO[c[d>>2]&255](f)|0)==0){e=aa(c[q>>2]|0,f);t=aa(e,lp(o)|0)>>>1;if(b){e=r-t|0;s=c[3524]|0;if((c[s+96>>2]&128|0)==0){if((e|0)<=0){break}if(!((c[s+8>>2]|0)>>>0>e>>>0&(n|0)>0)){break}if((c[s+12>>2]|0)>>>0<=n>>>0){break}}cR[c[g>>2]&127](e,n,o);break}else{e=n-t|0;t=c[3524]|0;if((c[t+96>>2]&128|0)==0){if((r|0)<=0){break}if(!((c[t+8>>2]|0)>>>0>r>>>0&(e|0)>0)){break}if((c[t+12>>2]|0)>>>0<=e>>>0){break}}cR[c[g>>2]&127](r,e,o);break}}else{e=c[3524]|0;if((c[e+96>>2]&128|0)==0){if((r|0)<=0){break}if(!((c[e+8>>2]|0)>>>0>r>>>0&(n|0)>0)){break}if((c[e+12>>2]|0)>>>0<=n>>>0){break}}cR[c[g>>2]&127](r,n,o)}}while(0);do{if((h|0)==(-270|0)|(h|0)==90){y=(c[p>>2]|0)+r|0;z=n}else{e=c[p>>2]|0;if((h|0)==(-90|0)|(h|0)==270){y=r-e|0;z=n;break}else{y=r;z=n-e|0;break}}}while(0);if((x|0)==0){break}a[x]=10;r=y;m=x;n=z;o=x+1|0}if(!k){return}if((a[i]|0)==0){return}i=c[j+88>>2]|0;if((i|0)==0){return}cO[i&255](179864);return}function lo(){var b=0;c[8026]=0;if((a[14080]&1)==0){return}if(a[13032]|0){b=c[(c[3524]|0)+104>>2]|0;if((b|0)!=0){cS[b&511]()}a[13032]=0}if(a[14088]|0){cS[c[(c[3524]|0)+44>>2]&511]();a[14088]=0}if((a[14080]&1)==0){return}cS[c[(c[3524]|0)+40>>2]&511]();a[14080]=0;c[10028]=0;return}function lp(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;d=c[3524]|0;e=c[d+96>>2]|0;if((e&8192|0)==0){do{if((a8(b|0,10)|0)==0){if((e&32|0)!=0){break}if((c[11252]|0)!=15){f=uA(b|0)|0;return f|0}g=a[b]|0;if(g<<24>>24==0){f=0;return f|0}else{h=0;i=0;j=g}while(1){if((j&-64)<<24>>24==-128){k=i}else{k=((j&255)>226?2:1)+i|0}g=h+1|0;l=a[b+g|0]|0;if(l<<24>>24==0){f=k;break}else{h=g;i=k;j=l}}return f|0}}while(0);c[3524]=238024;cR[c[59523]&127](0,0,b);j=c[(c[3524]|0)+8>>2]|0;c[3524]=d;f=j;return f|0}if((cy(b|0,203320)|0)==0){f=uA(b|0)|0;return f|0}j=a[b]|0;if(j<<24>>24==0){f=0;return f|0}else{m=b;n=0;o=j}L15011:while(1){j=m;b=o;L15013:while(1){d=b<<24>>24;L15015:do{if((d|0)==91){if(b<<24>>24==0){p=j}else{k=b;i=j;while(1){h=i+1|0;if(k<<24>>24==93){q=h;break L15015}e=a[h]|0;if(e<<24>>24==0){p=h;break}else{k=e;i=h}}}q=p+1|0}else if((d|0)==92){r=j;s=11030;break L15013}else if((d|0)==123|(d|0)==125|(d|0)==36|(d|0)==95|(d|0)==94){q=j+1|0}else{s=11034;break L15013}}while(0);d=a[q]|0;if(d<<24>>24==0){f=n;s=11046;break L15011}else{j=q;b=d}}L15024:do{if((s|0)==11030){while(1){s=0;b=r+1|0;d=a[b]|0;if(d<<24>>24==0){t=b;break L15024}if((bO(d<<24>>24|0)|0)==0){t=b;break}else{r=b;s=11030}}}else if((s|0)==11034){s=0;t=j+1|0}}while(0);j=n+1|0;b=a[t]|0;if(b<<24>>24==0){f=j;s=11049;break}else{m=t;n=j;o=b}}if((s|0)==11049){return f|0}else if((s|0)==11046){return f|0}return 0}function lq(a,b,d,e,f,g){a=a|0;b=b|0;d=+d;e=+e;f=+f;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;h=i;i=i+3048|0;j=h|0;k=h+8|0;l=h+16|0;m=h+24|0;n=h+32|0;o=h+3032|0;p=h+3040|0;if(e<0.0){q=e;while(1){r=q+360.0;if(r<0.0){q=r}else{s=r;break}}}else{s=e}if(f>360.0){e=f;while(1){q=e+-360.0;if(q>360.0){e=q}else{t=q;break}}}else{t=f}if(t<s){f=t;while(1){e=f+360.0;if(e<s){f=e}else{u=e;break}}}else{u=t}t=u-s;v=~~(t/3.0);w=(v|0)<1?1:v;v=c[3524]|0;f=+((c[v+24>>2]|0)>>>0>>>0)/+((c[v+28>>2]|0)>>>0>>>0);v=n|0;c[n+8>>2]=g;e=+(a>>>0>>>0);q=+(b>>>0>>>0);if((w|0)>0){x=0;y=0;while(1){r=(s+ +(x|0)*3.0)*.017453292519943295;z=n+(y*12&-1)|0;c[z>>2]=~~(e+ +S(+r)*d);A=n+(y*12&-1)+4|0;c[A>>2]=~~(q+f*+T(+r)*d);c[o>>2]=a;c[p>>2]=b;B=((fl(o,p,z,A)|0)!=0&1)+y|0;A=x+1|0;if((A|0)<(w|0)){x=A;y=B}else{C=B;break}}}else{C=0}s=u*.017453292519943295;y=n+(C*12&-1)|0;c[y>>2]=~~(e+ +S(+s)*d);x=n+(C*12&-1)+4|0;c[x>>2]=~~(q+f*+T(+s)*d);w=(((fl(o,p,y,x)|0)==0)<<31>>31)+C|0;d=+P(+t);if(d>.1&d<359.9){C=w+1|0;c[n+(C*12&-1)>>2]=a;c[n+(C*12&-1)+4>>2]=b;b=w+2|0;c[n+(b*12&-1)>>2]=c[n>>2];c[n+(b*12&-1)+4>>2]=c[n+4>>2];D=b}else{D=w}if((g|0)!=0){g=c[(c[3524]|0)+148>>2]|0;if((g|0)==0){i=h;return}cN[g&255](D+1|0,v);i=h;return}if((D|0)<=0){i=h;return}v=0;while(1){g=c[n+(v*12&-1)+4>>2]|0;w=v+1|0;b=c[n+(w*12&-1)>>2]|0;C=c[n+(w*12&-1)+4>>2]|0;c[j>>2]=c[n+(v*12&-1)>>2];c[k>>2]=g;c[l>>2]=b;c[m>>2]=C;C=c[3524]|0;if((fl(j,k,l,m)|0)!=0){cN[c[C+56>>2]&255](c[j>>2]|0,c[k>>2]|0);cN[c[C+60>>2]&255](c[l>>2]|0,c[m>>2]|0)}if((w|0)<(D|0)){v=w}else{break}}i=h;return}function lr(a,b){a=a|0;b=b|0;return uK(c[6224+((c[a>>2]|0)*184&-1)>>2]|0,c[6224+((c[b>>2]|0)*184&-1)>>2]|0)|0}function ls(){var a=0,b=0,d=0,e=0,f=0,g=0,h=0,j=0;a=i;i=i+152|0;b=a|0;d=ut(1024)|0;do{if((d|0)==0){gk();e=ut(1024)|0;if((e|0)!=0){f=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=75488,v)|0)}else{f=d}}while(0);d=0;do{c[b+(d<<2)>>2]=d;d=d+1|0;}while(d>>>0<37);bM(b|0,37,4,2);gq();uD(f|0,75064,28);d=c[8250]|0;if((d|0)==(c[m>>2]|0)){if((c[8238]|0)<((c[6318]|0)-2|0)){g=d}else{aI(125440,23,1,d|0);do{e=a1(c[o>>2]|0)|0}while(!((e|0)==(-1|0)|(e|0)==10));c[8238]=0;g=c[m>>2]|0}aK(f|0,g|0);c[8238]=(c[8238]|0)+1;h=0}else{aK(f|0,d|0);h=0}do{d=c[b+(h<<2)>>2]|0;g=c[6228+(d*184&-1)>>2]|0;be(f|0,74576,(v=i,i=i+16|0,c[v>>2]=c[6224+(d*184&-1)>>2],c[v+8>>2]=g,v)|0);g=c[8250]|0;if((g|0)==(c[m>>2]|0)){if((c[8238]|0)<((c[6318]|0)-2|0)){j=g}else{aI(125440,23,1,g|0);do{d=a1(c[o>>2]|0)|0}while(!((d|0)==(-1|0)|(d|0)==10));c[8238]=0;j=c[m>>2]|0}aK(f|0,j|0);c[8238]=(c[8238]|0)+1}else{aK(f|0,g|0)}h=h+1|0;}while(h>>>0<37);h=c[8250]|0;if((h|0)==(c[m>>2]|0)){uu(f);i=a;return}a2(h|0);uu(f);i=a;return}function lt(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0;b=i;i=i+152|0;d=b|0;e=ut(555)|0;do{if((e|0)==0){gk();f=ut(555)|0;if((f|0)!=0){g=f;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=74144,v)|0);return 0}else{g=e}}while(0);e=0;do{c[d+(e<<2)>>2]=e;e=e+1|0;}while(e>>>0<37);bM(d|0,37,4,2);e=g;w=32;a[e]=w&255;w=w>>8;a[e+1|0]=w&255;e=0;f=uA(g|0)|0;do{be(g+f|0,73368,(v=i,i=i+8|0,c[v>>2]=c[6224+((c[d+(e<<2)>>2]|0)*184&-1)>>2],v)|0);e=e+1|0;f=uA(g|0)|0}while(e>>>0<37);e=f+1|0;f=ut(e)|0;if((f|0)!=0){h=f;j=uB(h|0,g|0)|0;uu(g);i=b;return h|0}gk();f=ut(e)|0;if((f|0)==0){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=72784,v)|0);return 0}else{h=f;j=uB(h|0,g|0)|0;uu(g);i=b;return h|0}return 0}function lu(a,b){a=+a;b=+b;return 0}function lv(a){a=a|0;return(a|0)==0&1|0}function lw(a){a=a|0;return(a|0)==0&1|0}function lx(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0;e=i;f=(a_(b|0,223552,d|0)|0)==0;g=f?3:d;d=f?222936:b;b=0;f=0;j=0;while(1){k=c[6224+(b*184&-1)>>2]|0;if((a_(d|0,k|0,g|0)|0)==0){l=6224+(b*184&-1)|0;if((g|0)==(uA(k|0)|0)){n=0;o=l;break}else{p=j|(f|0)!=0;q=l}}else{p=j;q=f}l=b+1|0;if(l>>>0<37){b=l;f=q;j=p}else{n=p;o=q;break}}if((o|0)==0|n){r=0;i=e;return r|0}c[3524]=o;a[14080]=0;if((c[o+48>>2]|0)==2){s=o}else{n=c[m>>2]|0;aI(222416,73,1,n|0);s=c[3524]|0}n=s+72|0;if((c[n>>2]|0)==0){c[n>>2]=12;t=c[3524]|0}else{t=s}s=t+76|0;if((c[s>>2]|0)==0){c[s>>2]=124;u=c[3524]|0}else{u=t}t=u+80|0;if((c[t>>2]|0)==0){c[t>>2]=60;w=c[3524]|0}else{w=u}u=w+84|0;if((c[u>>2]|0)==0){c[u>>2]=48;x=c[3524]|0}else{x=w}w=x+92|0;if((c[w>>2]|0)==0){c[w>>2]=10;y=c[3524]|0}else{y=x}x=y+112|0;if((c[x>>2]|0)==0){c[x>>2]=34;z=c[3524]|0}else{z=y}y=z+168|0;if((c[y>>2]|0)==0){c[y>>2]=144;A=c[3524]|0}else{A=z}z=A+176|0;if(+h[z>>3]<=0.0){h[z>>3]=1.0}if((a[37400]&1)!=0){z=c[m>>2]|0;y=c[A>>2]|0;cf(z|0,221648,(v=i,i=i+8|0,c[v>>2]=y,v)|0)}c[7271]=-1;r=o;i=e;return r|0}function ly(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0.0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;e=c[3524]|0;if((d|0)<0){cN[c[e+56>>2]&255](a,b);cN[c[e+60>>2]&255](a,b);return}f=(d|0)%6&-1;g=+h[1630];d=~~(g*+((c[e+28>>2]|0)>>>0>>>0)*.5);i=~~(g*+((c[e+24>>2]|0)>>>0>>>0)*.5);if((f|0)==0){j=e+56|0;k=a-d|0;cN[c[j>>2]&255](k,b);l=e+60|0;cN[c[l>>2]&255](k,b);cN[c[l>>2]&255](d+a|0,b);k=b-i|0;cN[c[j>>2]&255](a,k);cN[c[l>>2]&255](a,k);cN[c[l>>2]&255](a,i+b|0);return}else if((f|0)==3){l=e+56|0;k=a-d|0;j=b-i|0;cN[c[l>>2]&255](k,j);m=e+60|0;cN[c[m>>2]&255](k,j);n=d+a|0;cN[c[m>>2]&255](n,j);o=i+b|0;cN[c[m>>2]&255](n,o);cN[c[m>>2]&255](k,o);cN[c[m>>2]&255](k,j);cN[c[l>>2]&255](a,b);cN[c[m>>2]&255](a,b);return}else if((f|0)==2){m=e+56|0;l=a-d|0;cN[c[m>>2]&255](l,b);j=e+60|0;cN[c[j>>2]&255](l,b);k=d+a|0;cN[c[j>>2]&255](k,b);o=b-i|0;cN[c[m>>2]&255](a,o);cN[c[j>>2]&255](a,o);n=i+b|0;cN[c[j>>2]&255](a,n);cN[c[m>>2]&255](l,o);cN[c[j>>2]&255](l,o);cN[c[j>>2]&255](k,n);cN[c[m>>2]&255](l,n);cN[c[j>>2]&255](l,n);cN[c[j>>2]&255](k,o);return}else if((f|0)==4){o=e+56|0;k=a-d|0;cN[c[o>>2]&255](k,b);j=e+60|0;cN[c[j>>2]&255](a,b-i|0);cN[c[j>>2]&255](d+a|0,b);cN[c[j>>2]&255](a,i+b|0);cN[c[j>>2]&255](k,b);cN[c[o>>2]&255](a,b);cN[c[j>>2]&255](a,b);return}else if((f|0)==5){j=e+56|0;o=((i<<2|0)/3&-1)+b|0;cN[c[j>>2]&255](a,o);k=e+60|0;n=(d<<2|0)/3&-1;l=b-((i<<1|0)/3&-1)|0;cN[c[k>>2]&255](a-n|0,l);cN[c[k>>2]&255](n+a|0,l);cN[c[k>>2]&255](a,o);cN[c[j>>2]&255](a,b);cN[c[k>>2]&255](a,b);return}else if((f|0)==1){f=e+56|0;k=a-d|0;j=b-i|0;cN[c[f>>2]&255](k,j);o=e+60|0;cN[c[o>>2]&255](k,j);e=d+a|0;a=i+b|0;cN[c[o>>2]&255](e,a);cN[c[f>>2]&255](k,a);cN[c[o>>2]&255](k,a);cN[c[o>>2]&255](e,j);return}else{return}}function lz(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0;b=i;i=i+24|0;d=b|0;e=c[13898]|0;if((e|0)>=(c[8272]|0)){f=lx(225016,7)|0;g=c[13898]|0;h=g-1|0;uf(h,224456,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}j=c[1054]|0;L15188:do{if((a[j+(e*40&-1)|0]&1)==0){k=c[10036]|0;l=c[j+(e*40&-1)+36>>2]|0;m=j+(e*40&-1)+32|0}else{n=c[j+(e*40&-1)+36>>2]|0;o=j+(e*40&-1)+32|0;p=c[10036]|0;q=0;while(1){if((q|0)>=(n|0)){break}if((a[p+((c[o>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{k=p;l=n;m=o;break L15188}}if((q|0)!=1){k=p;l=n;m=o;break}f=lx(225016,7)|0;g=c[13898]|0;h=g-1|0;uf(h,224456,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);e=lx(k+(c[m>>2]|0)|0,l)|0;l=c[13898]|0;L15197:do{if((e|0)==0){m=c[1054]|0;k=(a[m+(l*40&-1)|0]&1)==0;if(k){r=11176}else{j=c[10036]|0;s=a[j+(c[m+(l*40&-1)+32>>2]|0)|0]|0;if((s<<24>>24|0)==39|(s<<24>>24|0)==34){t=j}else{r=11176}}if((r|0)==11176){j=c[10810]|0;if((j|0)==0){r=11196;break}s=c[8272]|0;u=m+(l*40&-1)+36|0;w=m+(l*40&-1)+32|0;x=c[10036]|0;y=j;L15204:while(1){j=c[y+4>>2]|0;L15206:do{if(!((s|0)<=(l|0)|k)){z=c[u>>2]|0;A=0;while(1){if((A|0)>=(z|0)){break}if((a[x+((c[w>>2]|0)+A|0)|0]|0)==(a[j+A|0]|0)){A=A+1|0}else{break L15206}}if((a[j+A|0]|0)==0){break L15204}}}while(0);j=c[y>>2]|0;if((j|0)==0){r=11196;break L15197}else{y=j}}if((a[y+8|0]&1)!=0){r=11196;break}if((c[y+16>>2]|0)==3){t=x}else{r=11196;break}}L15216:do{if((l|0)<(c[8272]|0)){L15218:do{if(!k){w=c[m+(l*40&-1)+36>>2]|0;u=m+(l*40&-1)+32|0;s=0;while(1){if((s|0)>=(w|0)){break}if((a[t+((c[u>>2]|0)+s|0)|0]|0)==(a[s+103664|0]|0)){s=s+1|0}else{break L15218}}if((s|0)==1){B=0;break L15216}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[d>>2]|0)==3){B=c[d+8>>2]|0;break}else{c[13898]=l;B=0;break}}else{B=0}}while(0);m=lx(B,uA(B|0)|0)|0;uu(B);C=m}else{r=11196}}while(0);if((r|0)==11196){c[13898]=l+1;C=e}if((C|0)==0){f=lx(225016,7)|0;g=c[13898]|0;h=g-1|0;uf(h,224456,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{i=b;return C|0}return 0}function lA(a){a=+a;return}function lB(a){a=a|0;return}function lC(a){a=+a;h[1630]=a>=0.0?a:1.0;return}function lD(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,R=0,V=0,W=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0.0,ar=0.0,as=0.0,at=0.0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0.0,aE=0.0,aF=0.0,aG=0,aH=0.0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0.0,aS=0.0,aT=0.0,aU=0.0,aV=0.0,aW=0.0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0;g=i;i=i+480|0;j=g|0;k=g+8|0;l=g+16|0;m=g+24|0;n=g+32|0;o=g+40|0;p=g+48|0;q=g+56|0;r=g+64|0;s=g+72|0;t=g+80|0;u=g+88|0;v=g+96|0;w=g+104|0;x=g+112|0;y=g+120|0;z=g+128|0;A=g+136|0;B=g+144|0;C=g+152|0;D=g+160|0;E=g+168|0;F=g+176|0;G=g+184|0;H=g+192|0;I=g+200|0;J=g+208|0;K=g+216|0;L=g+224|0;M=g+232|0;N=g+240|0;O=g+248|0;R=g+256|0;V=g+264|0;W=g+272|0;Z=g+280|0;_=g+288|0;$=g+296|0;aa=g+304|0;ab=g+312|0;ac=g+320|0;ad=g+328|0;ae=g+336|0;af=g+344|0;ag=g+352|0;ah=g+360|0;ai=g+368|0;aj=g+376|0;ak=g+384|0;al=g+392|0;am=g+400|0;an=g+408|0;ao=g+416|0;ap=c[3524]|0;aq=+(((c[ap+24>>2]|0)+(c[ap+28>>2]|0)|0)>>>0>>>0)*.5;ar=+(a-d|0);as=+(b-e|0);at=+Q(+(ar*ar+as*as));au=(f|0)<0?-f|0:f;av=c[13542]|0;aw=(c[ap+96>>2]&128|0)==0;ax=aw?55520:0;c[13542]=ax;do{if((f|0)==0){ay=0;az=0}else{L15241:do{if(+P(+at)<2.220446049250313e-16){aA=0;aB=0}else{aC=c[12910]|0;if((aC|0)<1){aD=aq;aE=aD*.3/at;aF=aD*2.0/at;aD=aF>.3?.3:aF;aF=aE>aD?aE:aD;aD=ar*.96593;aE=as*.25882;aG=~~((aD-aE)*aF);aH=as*.96593;aI=~~((ar*.25882+aH)*aF);aJ=~~((aD+aE)*aF);aK=~~((ar*-.25882+aH)*aF);aL=(aG+aJ|0)/2&-1;aM=(aI+aK|0)/2&-1;aN=aG;aO=aK;aP=aJ;aQ=aI}else{aF=+h[6458]*.017453292519943295;aH=+h[6457]*.017453292519943295;aE=+Y(+(-0.0-as),+(-0.0-ar));aD=+(aC|0);aR=at*.5;if(aD>aR){aS=aD/aR;aT=+X(+(+U(+aF)*aS));aU=aR;aV=+X(+(aS*+U(+aH)));aW=aT}else{aU=aD;aV=aH;aW=aF}aF=+T(+aW);aH=aF/+T(+aV);aF=aW-aE;aC=-~~(aU*+S(+aF))|0;aI=~~(aU*+T(+aF));aF=-0.0-aU;aD=aE+aW;aT=+S(+aD)*aF;aS=+T(+aD)*aF;aF=aU*aH;aH=aE+aV;aJ=~~(aT+aF*+S(+aH));aL=aJ;aM=~~(aS+aF*+T(+aH));aN=aC;aO=~~aS;aP=~~aT;aQ=aI}do{if((au&1|0)!=0){do{if((c[12912]|0)==2){if(aw){aI=(c[ax>>2]|0)>(d|0)&1;aC=(c[ax+4>>2]|0)<(d|0)?aI|2:aI;aI=(c[ax+8>>2]|0)>(e|0)?aC|4:aC;if((((c[ax+12>>2]|0)<(e|0)?aI|8:aI)|0)!=0){aX=ax;break}}aI=aL+d|0;c[ao>>2]=aI;aC=aM+e|0;c[ao+4>>2]=aC;c[ao+12>>2]=aN+d;c[ao+16>>2]=aQ+e;c[ao+24>>2]=d;c[ao+28>>2]=e;c[ao+36>>2]=aP+d;c[ao+40>>2]=aO+e;c[ao+48>>2]=aI;c[ao+52>>2]=aC;c[ao+8>>2]=1601;aC=c[ap+148>>2]|0;if((aC|0)==0){aX=ax;break}cN[aC&255](5,ao|0);aX=c[13542]|0}else{aX=ax}}while(0);if((aX|0)!=0){aC=(c[aX>>2]|0)>(d|0)&1;aI=(c[aX+4>>2]|0)<(d|0)?aC|2:aC;aC=(c[aX+8>>2]|0)>(e|0)?aI|4:aI;if((((c[aX+12>>2]|0)<(e|0)?aC|8:aC)|0)!=0){break}}if((c[12912]|0)==0){c[L>>2]=aN+d;c[M>>2]=aQ+e;c[N>>2]=d;c[O>>2]=e;aC=c[3524]|0;if((fl(L,M,N,O)|0)!=0){cN[c[aC+56>>2]&255](c[L>>2]|0,c[M>>2]|0);cN[c[aC+60>>2]&255](c[N>>2]|0,c[O>>2]|0)}c[H>>2]=d;c[I>>2]=e;c[J>>2]=aP+d;c[K>>2]=aO+e;aC=c[3524]|0;if((fl(H,I,J,K)|0)==0){break}cN[c[aC+56>>2]&255](c[H>>2]|0,c[I>>2]|0);cN[c[aC+60>>2]&255](c[J>>2]|0,c[K>>2]|0);break}aC=aL+d|0;aI=aM+e|0;aJ=aN+d|0;aK=aQ+e|0;c[ag>>2]=aC;c[ah>>2]=aI;c[ai>>2]=aJ;c[aj>>2]=aK;aG=c[3524]|0;if((fl(ag,ah,ai,aj)|0)!=0){cN[c[aG+56>>2]&255](c[ag>>2]|0,c[ah>>2]|0);cN[c[aG+60>>2]&255](c[ai>>2]|0,c[aj>>2]|0)}c[ac>>2]=aJ;c[ad>>2]=aK;c[ae>>2]=d;c[af>>2]=e;aK=c[3524]|0;if((fl(ac,ad,ae,af)|0)!=0){cN[c[aK+56>>2]&255](c[ac>>2]|0,c[ad>>2]|0);cN[c[aK+60>>2]&255](c[ae>>2]|0,c[af>>2]|0)}aK=aP+d|0;aJ=aO+e|0;c[_>>2]=d;c[$>>2]=e;c[aa>>2]=aK;c[ab>>2]=aJ;aG=c[3524]|0;if((fl(_,$,aa,ab)|0)!=0){cN[c[aG+56>>2]&255](c[_>>2]|0,c[$>>2]|0);cN[c[aG+60>>2]&255](c[aa>>2]|0,c[ab>>2]|0)}c[R>>2]=aK;c[V>>2]=aJ;c[W>>2]=aC;c[Z>>2]=aI;aI=c[3524]|0;if((fl(R,V,W,Z)|0)==0){break}cN[c[aI+56>>2]&255](c[R>>2]|0,c[V>>2]|0);cN[c[aI+60>>2]&255](c[W>>2]|0,c[Z>>2]|0)}}while(0);if((au&2|0)==0){aA=aL;aB=aM;break}aI=c[13542]|0;if((aI|0)!=0){aC=(c[aI>>2]|0)>(a|0)&1;aJ=(c[aI+4>>2]|0)<(a|0)?aC|2:aC;aC=(c[aI+8>>2]|0)>(b|0)?aJ|4:aJ;if((((c[aI+12>>2]|0)<(b|0)?aC|8:aC)|0)!=0){aA=aL;aB=aM;break}}aC=c[12912]|0;do{if((aC|0)==2){aI=a-aL|0;c[ao>>2]=aI;aJ=b-aM|0;c[ao+4>>2]=aJ;c[ao+12>>2]=a-aN;c[ao+16>>2]=b-aQ;c[ao+24>>2]=a;c[ao+28>>2]=b;c[ao+36>>2]=a-aP;c[ao+40>>2]=b-aO;c[ao+48>>2]=aI;c[ao+52>>2]=aJ;c[ao+8>>2]=1601;aJ=c[ap+148>>2]|0;if((aJ|0)==0){break}cN[aJ&255](5,ao|0);aY=c[12912]|0;aZ=11240}else{aY=aC;aZ=11240}}while(0);do{if((aZ|0)==11240){if((aY|0)!=0){break}c[n>>2]=a-aP;c[o>>2]=b-aO;c[p>>2]=a;c[q>>2]=b;aC=c[3524]|0;if((fl(n,o,p,q)|0)!=0){cN[c[aC+56>>2]&255](c[n>>2]|0,c[o>>2]|0);cN[c[aC+60>>2]&255](c[p>>2]|0,c[q>>2]|0)}c[j>>2]=a;c[k>>2]=b;c[l>>2]=a-aN;c[m>>2]=b-aQ;aC=c[3524]|0;if((fl(j,k,l,m)|0)==0){aA=aL;aB=aM;break L15241}cN[c[aC+56>>2]&255](c[j>>2]|0,c[k>>2]|0);cN[c[aC+60>>2]&255](c[l>>2]|0,c[m>>2]|0);aA=aL;aB=aM;break L15241}}while(0);aC=a-aL|0;aJ=b-aM|0;aI=a-aP|0;aK=b-aO|0;c[D>>2]=aC;c[E>>2]=aJ;c[F>>2]=aI;c[G>>2]=aK;aG=c[3524]|0;if((fl(D,E,F,G)|0)!=0){cN[c[aG+56>>2]&255](c[D>>2]|0,c[E>>2]|0);cN[c[aG+60>>2]&255](c[F>>2]|0,c[G>>2]|0)}c[z>>2]=aI;c[A>>2]=aK;c[B>>2]=a;c[C>>2]=b;aK=c[3524]|0;if((fl(z,A,B,C)|0)!=0){cN[c[aK+56>>2]&255](c[z>>2]|0,c[A>>2]|0);cN[c[aK+60>>2]&255](c[B>>2]|0,c[C>>2]|0)}aK=a-aN|0;aI=b-aQ|0;c[v>>2]=a;c[w>>2]=b;c[x>>2]=aK;c[y>>2]=aI;aG=c[3524]|0;if((fl(v,w,x,y)|0)!=0){cN[c[aG+56>>2]&255](c[v>>2]|0,c[w>>2]|0);cN[c[aG+60>>2]&255](c[x>>2]|0,c[y>>2]|0)}c[r>>2]=aK;c[s>>2]=aI;c[t>>2]=aC;c[u>>2]=aJ;aJ=c[3524]|0;if((fl(r,s,t,u)|0)==0){aA=aL;aB=aM;break}cN[c[aJ+56>>2]&255](c[r>>2]|0,c[s>>2]|0);cN[c[aJ+60>>2]&255](c[t>>2]|0,c[u>>2]|0);aA=aL;aB=aM}}while(0);if((f|0)>-1){ay=aB;az=aA;break}c[13542]=av;i=g;return}}while(0);do{if((au&2|0)==0){a_=a;a$=b}else{aA=+P(+at)>=2.220446049250313e-16;if(!(aA&(c[12912]|0)!=0)){a_=a;a$=b;break}a_=a-az|0;a$=b-ay|0}}while(0);do{if((au&1|0)==0){a0=d;a1=e}else{b=+P(+at)>=2.220446049250313e-16;if(!(b&(c[12912]|0)!=0)){a0=d;a1=e;break}a0=az+d|0;a1=ay+e|0}}while(0);c[ak>>2]=a_;c[al>>2]=a$;c[am>>2]=a0;c[an>>2]=a1;a1=c[3524]|0;if((fl(ak,al,am,an)|0)==0){c[13542]=av;i=g;return}cN[c[a1+56>>2]&255](c[ak>>2]|0,c[al>>2]|0);cN[c[a1+60>>2]&255](c[am>>2]|0,c[an>>2]|0);c[13542]=av;i=g;return}function lE(){var b=0,d=0,e=0,f=0,g=0,h=0;b=i;d=bU(215528)|0;do{if((d|0)!=0){if((a[d]|0)==0){break}e=uA(d|0)|0;f=e6(215528)|0;g=bP(d|0)|0;c[f+16>>2]=3;c[f+24>>2]=g;a[f+8|0]=0;f=a8(d|0,32)|0;if((f|0)==0){h=e}else{h=f-d|0}if((lx(d,h)|0)==0){f=c[m>>2]|0;cf(f|0,220472,(v=i,i=i+8|0,c[v>>2]=d,v)|0);break}f=c[3524]|0;if((aY(c[f>>2]|0,222936)|0)==0){i=b;return}cS[c[f+32>>2]&511]();i=b;return}}while(0);lx(225016,7);i=b;return}function lF(b){b=b|0;var d=0;d=c[11232]|0;c[11232]=d+1;a[d]=b&255;return}function lG(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0.0,I=0.0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0.0,T=0,U=0,V=0,W=0.0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0.0,ag=0,ah=0.0,ai=0.0,aj=0.0,ak=0;b=i;i=i+224|0;d=b|0;e=b+16|0;f=b+72|0;j=b+136|0;k=c[3524]|0;l=k+96|0;m=(c[l>>2]&32|0)!=0;if(!m){dL(bP(218776)|0)}if((a[14080]&1)==0){lk()}do{if(a[14088]|0){if((a[33512]&1)==0){break}if(!(a[13032]|0)){break}n=c[(c[3524]|0)+104>>2]|0;if((n|0)!=0){cS[n&511]()}a[13032]=0}else{cS[c[(c[3524]|0)+52>>2]&511]();a[14088]=1}}while(0);cM[c[(c[3524]|0)+168>>2]&511](0);n=c[3524]|0;if((c[n+96>>2]&16|0)!=0){c[7271]=-1}c[13880]=0;c[13881]=(c[n+8>>2]|0)-1;c[13882]=0;c[13883]=(c[n+12>>2]|0)-1;a[25280]=0;n=~~(+((c[k+8>>2]|0)>>>0>>>0)*+g[178]);o=~~(+((c[k+12>>2]|0)>>>0>>>0)*+g[38]);p=+h[3817];q=k+28|0;r=~~(p*+((c[q>>2]|0)>>>0>>>0));s=k+24|0;t=~~(p*+((c[s>>2]|0)>>>0>>>0)*1.25);u=k+16|0;w=c[u>>2]|0;x=t>>>0<w>>>0?w:t;cM[c[k+168>>2]&511](2);t=k+112|0;cK[c[t>>2]&63](1.0);w=k+64|0;cM[c[w>>2]&511](-2);y=k+56|0;cN[c[y>>2]&255](0,0);z=k+60|0;A=n-1|0;cN[c[z>>2]&255](A,0);B=o-1|0;cN[c[z>>2]&255](A,B);cN[c[z>>2]&255](0,B);cN[c[z>>2]&255](0,0);cM[c[w>>2]&511](0);C=c[c[3524]>>2]|0;if((aY(C|0,225016)|0)==0){uf(-1,218360,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}D=f|0;uB(D|0,C|0);uD(f+(uA(D|0)|0)|0,217760,16);f=k+76|0;cO[c[f>>2]&255](0);C=k+68|0;E=k+20|0;p=+(o>>>0>>>0);cR[c[C>>2]&127](c[E>>2]<<1,~~(p- +((c[u>>2]|0)>>>0>>>0)*.5),D);if((c[k+124>>2]|0)!=0){cR[c[C>>2]&127]((c[E>>2]|0)*5&-1,((c[u>>2]|0)*-3&-1)+o|0,217152)}cM[c[w>>2]&511](-2);cM[c[w>>2]&511](-1);D=n>>>1;cN[c[y>>2]&255](D,0);cN[c[z>>2]&255](D,B);F=o>>>1;cN[c[y>>2]&255](0,F);cN[c[z>>2]&255](A,F);cM[c[w>>2]&511](3);cN[c[y>>2]&255](((c[E>>2]|0)*-10&-1)+D|0,((c[u>>2]|0)>>>1)+F|0);cN[c[z>>2]&255](((c[E>>2]|0)*10&-1)+D|0,((c[u>>2]|0)>>>1)+F|0);cN[c[z>>2]&255](((c[E>>2]|0)*10&-1)+D|0,F-((c[u>>2]|0)>>>1)|0);cN[c[z>>2]&255](((c[E>>2]|0)*-10&-1)+D|0,F-((c[u>>2]|0)>>>1)|0);cN[c[z>>2]&255](((c[E>>2]|0)*-10&-1)+D|0,((c[u>>2]|0)>>>1)+F|0);cR[c[C>>2]&127](((c[E>>2]|0)*-10&-1)+D|0,F,216528);cR[c[C>>2]&127](((c[E>>2]|0)*-10&-1)+D|0,~~(+(F>>>0>>>0)+ +((c[u>>2]|0)>>>0>>>0)*1.4),216048);cM[c[w>>2]&511](-2);do{if((c[l>>2]&32|0)!=0){A=bP(215568)|0;cR[c[C>>2]&127](~~(+(n>>>0>>>0)*.5),~~(p*.4),A);uu(A);if(m){break}dL(bP(215048)|0)}}while(0);cO[c[f>>2]&255](0);cR[c[C>>2]&127](D,((c[u>>2]|0)*6&-1)+F|0,214488);m=(cO[c[f>>2]&255](1)|0)==0;l=c[C>>2]|0;if(m){cR[l&127](D-(((c[E>>2]|0)*13&-1)>>>1)|0,((c[u>>2]|0)*5&-1)+F|0,213832)}else{cR[l&127](D,((c[u>>2]|0)*5&-1)+F|0,213832)}l=(cO[c[f>>2]&255](2)|0)==0;m=c[C>>2]|0;if(l){cR[m&127](((c[E>>2]|0)*-15&-1)+D|0,(c[u>>2]<<2)+F|0,213336)}else{cR[m&127](D,(c[u>>2]<<2)+F|0,213336)}cM[c[w>>2]&511](1);m=k+72|0;l=(cO[c[m>>2]&255](-270)|0)==0;A=c[f>>2]|0;if(l){cO[A&255](0);cR[c[C>>2]&127](c[E>>2]<<1,F-(c[u>>2]<<1)|0,211168)}else{l=(cO[A&255](1)|0)==0;A=c[C>>2]|0;G=c[u>>2]|0;if(l){cR[A&127](G,F-(((c[E>>2]|0)*21&-1)>>>1)|0,212752)}else{cR[A&127](G,F,212752)}G=c[f>>2]|0;cO[G&255](0);G=c[m>>2]|0;cO[G&255](45);cR[c[C>>2]&127]((c[u>>2]|0)*3&-1,F,212104);G=c[f>>2]|0;cO[G&255](0);G=c[m>>2]|0;cO[G&255](-45);cR[c[C>>2]&127](c[u>>2]<<1,F,211576)}cO[c[f>>2]&255](0);cO[c[m>>2]&255](0);cM[c[w>>2]&511](-2);cM[c[w>>2]&511](4);H=+(D>>>0>>>0);cN[c[y>>2]&255](~~(H+ +((c[q>>2]|0)>>>0>>>0)*(+h[8304]+1.0)),B);I=+h[8304];cN[c[z>>2]&255](~~(H+ +((c[q>>2]|0)>>>0>>>0)*(I+1.0)),~~(p-I*+((c[s>>2]|0)>>>0>>>0)));cN[c[y>>2]&255](D,~~(p- +((c[s>>2]|0)>>>0>>>0)*(+h[8304]+1.0)));I=+h[8304];cN[c[z>>2]&255](~~(H+I*+((c[q>>2]|0)>>>0>>>0)),~~(p-(I+1.0)*+((c[s>>2]|0)>>>0>>>0)));B=(cO[c[f>>2]&255](2)|0)==0;m=c[C>>2]|0;F=c[E>>2]|0;if(B){cR[m&127]((F*-14&-1)+D|0,(o-(c[s>>2]<<1)|0)-((c[u>>2]|0)>>>1)|0,210688)}else{cR[m&127](D-F|0,(o-(c[s>>2]<<1)|0)-((c[u>>2]|0)>>>1)|0,210688)}cO[c[f>>2]&255](0);cM[c[w>>2]&511](-2);F=(n-r|0)+((c[E>>2]|0)*-6&-1)|0;cK[c[k+92>>2]&63](+h[3817]);D=o-x|0;if((D|0)>(x|0)){m=d|0;B=d+4|0;G=d+8|0;A=e|0;l=k+80|0;J=(r|0)/2&-1;r=-2;K=D;while(1){D=r+1|0;L=c[8798]|0;M=(L|0)>0;N=D;L15390:while(1){O=43264;while(1){P=c[O>>2]|0;if((P|0)==0){break}if((c[P+4>>2]|0)==(N|0)){Q=11322;break L15390}else{O=P|0}}O=N-1|0;if(!((N|0)>(L|0)&M)){R=O;S=1.0;T=0;U=1;V=O;W=0.0;break}N=((O|0)%(L|0)&-1)+1|0}do{if((Q|0)==11322){Q=0;L=c[P+12>>2]|0;I=+h[P+24>>3];M=c[P+48>>2]|0;O=c[P+52>>2]|0;H=+h[P+56>>3];if((c[(c[3524]|0)+96>>2]&1024|0)!=0){R=N;S=I;T=0;U=M;V=O;W=H;break}X=a[P+40|0]&1;Y=X<<24>>24==0;R=L;S=I;T=X;U=Y?1:M;V=Y?L:O;W=H}}while(0);c[m>>2]=U;c[B>>2]=V;h[G>>3]=W;cK[c[(c[3524]|0)+112>>2]&63](S);N=c[(c[3524]|0)+64>>2]|0;if((R|0)<-5){cM[N&511](-2)}else{cM[N&511](R)}N=c[3524]|0;do{if(T<<24>>24==0){if((c[N+96>>2]&1024|0)!=0){break}c[m>>2]=1;c[B>>2]=R;Q=11332}else{Q=11332}}while(0);if((Q|0)==11332){Q=0;fn(d,N)}be(A|0,21e4,(v=i,i=i+8|0,c[v>>2]=D,v)|0);O=(cO[c[f>>2]&255](2)|0)==0;L=c[C>>2]|0;if(O){O=uA(A|0)|0;Y=F-aa(c[E>>2]|0,O)|0;cR[L&127](Y,K,A)}else{cR[L&127](F,K,A)}cN[c[y>>2]&255]((c[E>>2]|0)+F|0,K);cN[c[z>>2]&255]((c[E>>2]<<2)+F|0,K);if((r|0)>-2){cR[c[l>>2]&127]((((c[E>>2]|0)*5&-1)+F|0)+J|0,K,r)}L=K-x|0;if((L|0)>(x|0)){r=D;K=L}else{Z=A;break}}}else{Z=e|0}cK[c[t>>2]&63](1.0);cM[c[w>>2]&511](0);S=+(n>>>0>>>0);e=~~(S*.375);A=~~(p*.25);K=(c[q>>2]|0)*7&-1;r=(c[s>>2]|0)*7&-1;x=c[12912]|0;c[12912]=0;J=k+84|0;cI[c[J>>2]&63](e,A,K+e|0,A,1);c[12912]=1;cI[c[J>>2]&63](e,A,e-K|0,A,1);c[12912]=2;cI[c[J>>2]&63](e,A,e,r+A|0,1);c[12912]=1;cI[c[J>>2]&63](e,A,e,A-r|0,1);c[12912]=x;x=(c[q>>2]|0)*5&-1;q=(c[s>>2]|0)*5&-1;s=e-x|0;r=A-q|0;K=x+e|0;x=q+A|0;cI[c[J>>2]&63](s,r,K,x,3);cI[c[J>>2]&63](s,x,e,A,0);c[12912]=1;cI[c[J>>2]&63](e,A,K,r,2);cO[c[f>>2]&255](0);r=(o>>>0)/25>>>0;K=~~(S*.075);A=((n>>>0)/10>>>0)+K|0;e=1;J=r;while(1){cK[c[t>>2]&63](+(e|0));cM[c[w>>2]&511](-2);cN[c[y>>2]&255](K,J);cN[c[z>>2]&255](A,J);be(Z|0,209288,(v=i,i=i+16|0,c[v>>2]=e,c[v+8>>2]=0,v)|0);cR[c[C>>2]&127](A,J,Z);x=e+1|0;if((x|0)<7){e=x;J=J+r|0}else{break}}cR[c[C>>2]&127](K,r*7&-1,208960);r=~~(S*.5);K=(n>>>0)/40>>>0;J=o>>>3;cK[c[t>>2]&63](1.0);cM[c[w>>2]&511](-2);cO[c[f>>2]&255](1);W=+(J|0);cR[c[C>>2]&127]((K*7&-1)+r|0,~~(W+ +((c[u>>2]|0)>>>0>>>0)*1.5),208328);t=k+108|0;o=(n>>>0)/80>>>0;H=+(K|0)*1.5;e=0;A=r;while(1){r=c[t>>2]|0;if((r|0)!=0){cI[r&63](e<<4|2,A,0,K,J)}cN[c[y>>2]&255](A,0);cN[c[z>>2]&255](A,J);r=A+K|0;cN[c[z>>2]&255](r,J);cN[c[z>>2]&255](r,0);cN[c[z>>2]&255](A,0);be(Z|0,207744,(v=i,i=i+8|0,c[v>>2]=e,v)|0);cR[c[C>>2]&127](o+A|0,~~(W+ +((c[u>>2]|0)>>>0>>>0)*.5),Z);r=e+1|0;if((r|0)<10){e=r;A=~~(H+ +(A|0))}else{break}}A=~~(S*.7);e=~~(p*.83);Z=(n>>>0)/20>>>0;if((c[k+148>>2]|0)==0){_=206240}else{p=+(Z|0);k=j|0;n=j|0;o=j+72|0;z=j+4|0;J=j+76|0;K=j+8|0;y=0;while(1){t=aa(y,Z);S=+(t+A|0);H=+(e-((t|0)/2&-1)|0);t=~~(S+p);c[n>>2]=t;r=~~(H+p*0.0);c[z>>2]=r;c[j+12>>2]=~~(S+p*.5000000000000001);c[j+16>>2]=~~(H+p*.8660254037844386);c[j+24>>2]=~~(S+p*-.4999999999999998);c[j+28>>2]=~~(H+p*.8660254037844387);c[j+36>>2]=~~(S+p*-1.0);c[j+40>>2]=~~(H+p*1.2246467991473532e-16);c[j+48>>2]=~~(S+p*-.5000000000000004);c[j+52>>2]=~~(H+p*-.8660254037844384);c[j+60>>2]=~~(S+p*.5000000000000001);c[j+64>>2]=~~(H+p*-.8660254037844386);c[o>>2]=t;c[J>>2]=r;r=c[w>>2]|0;if((y|0)==0){cM[r&511](2);$=1601}else{cM[r&511](1);$=804}c[K>>2]=$;cN[c[(c[3524]|0)+148>>2]&255](7,k);r=y+1|0;if((r|0)<2){y=r}else{_=206944;break}}}cM[c[w>>2]&511](-2);if((cO[c[f>>2]&255](1)|0)!=0){ab=0;ac=c[C>>2]|0;ad=ab+A|0;ae=Z+e|0;af=+(ae|0);ag=c[u>>2]|0;ah=+(ag>>>0>>>0);ai=ah*.5;aj=af+ai;ak=~~aj;cR[ac&127](ad,ak,_);lh();i=b;return}f=c[E>>2]|0;ab=aa(uA(_|0)|0,f)>>>1;ac=c[C>>2]|0;ad=ab+A|0;ae=Z+e|0;af=+(ae|0);ag=c[u>>2]|0;ah=+(ag>>>0>>>0);ai=ah*.5;aj=af+ai;ak=~~aj;cR[ac&127](ad,ak,_);lh();i=b;return}function lH(b,d,e,f,g,i){b=b|0;d=+d;e=+e;f=f|0;g=g|0;i=i|0;var j=0.0;if((i|0)==4){h[29743]=+h[29742];h[29741]=+h[29740];return}else if((i|0)==3){h[29742]=+h[29743];h[29740]=+h[29741];return}else{if(a[237976]|0){return}a[237976]=1;h[29750]=0.0;h[29751]=d>2.0?1.0:d;h[29752]=e;j=d+e;if(+h[29749]<j){h[29749]=j}if(+h[29748]>e){h[29748]=e}c[59492]=i;a[237952]=f&1;return}}function lI(){var b=0.0,d=0.0,e=0.0;b=+h[29750];if(!(a[237976]|0)){return}h[29750]=0.0;do{if((a[237952]&1)==0){d=+h[29743]}else{if((c[59492]|0)==1){e=b*.5+ +h[29743];h[29743]=e;d=e;break}else{e=b+ +h[29743];h[29743]=e;d=e;break}}}while(0);b=+h[29745];h[29745]=b>d?b:d;a[237976]=0;return}function lJ(b,d,e,f,g,j,k,l){b=b|0;d=d|0;e=e|0;f=+f;g=+g;j=j|0;k=k|0;l=l|0;var m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0,u=0.0,w=0,x=0,y=0,z=0,A=0,B=0.0,C=0,D=0,E=0,F=0,G=0.0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0.0,P=0.0,Q=0.0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0.0,_=0,$=0.0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0;m=i;i=i+48|0;n=m|0;o=m+8|0;p=m+16|0;q=m+40|0;r=q;c[n>>2]=b;cS[c[(c[3524]|0)+160>>2]&511]();s=f+g;if(s>+h[5612]){h[5612]=s}if(+h[5611]>g){h[5611]=g}b=c[n>>2]|0;t=a[b]|0;L15478:do{if(t<<24>>24!=0){s=f*.8;u=f;w=q;x=r+1|0;y=r+2|0;z=m+24|0;A=l;B=g;C=b;D=t;L15480:while(1){E=c[11252]|0;do{if(D<<24>>24<0){if(!((E|0)==15|(E|0)==0)){F=11393;break}c[p>>2]=C;cJ[c[(c[3524]|0)+156>>2]&15](e,f,B,j,k,A);if(!(ug(o,p)|0)){cM[c[(c[3524]|0)+164>>2]&511](a[c[n>>2]|0]|0);G=B;H=A;break}I=c[n>>2]|0;J=c[p>>2]|0;if(I>>>0<J>>>0){K=I;while(1){L=c[(c[3524]|0)+164>>2]|0;c[n>>2]=K+1;cM[L&511](a[K]|0);L=c[n>>2]|0;if(L>>>0<J>>>0){K=L}else{M=L;break}}}else{M=I}c[n>>2]=M-1;G=B;H=A}else{F=11393}}while(0);L15492:do{if((F|0)==11393){F=0;K=D<<24>>24;if((K&128|0)!=0&(E|0)==14){cJ[c[(c[3524]|0)+156>>2]&15](e,f,B,j,k,A);J=c[(c[3524]|0)+164>>2]|0;L=c[n>>2]|0;c[n>>2]=L+1;cM[J&511](a[L]|0);cM[c[(c[3524]|0)+164>>2]&511](a[c[n>>2]|0]|0);G=B;H=A;break}do{if((K|0)==125){F=11396;break L15480}else if((K|0)==64){cS[c[(c[3524]|0)+160>>2]&511]();cJ[c[(c[3524]|0)+156>>2]&15](e,f,B,j,k,3);L=(c[n>>2]|0)+1|0;c[n>>2]=L;c[n>>2]=lJ(L,0,e,f,B,j,k,A)|0;cJ[c[(c[3524]|0)+156>>2]&15](e,f,B,j,k,4);G=B;H=A;break L15492}else if((K|0)==38){cS[c[(c[3524]|0)+160>>2]&511]();L=(c[n>>2]|0)+1|0;c[n>>2]=L;c[n>>2]=lJ(L,0,e,f,B,j,0,A)|0;G=B;H=A;break L15492}else if((K|0)==40|(K|0)==41){cJ[c[(c[3524]|0)+156>>2]&15](e,f,B,j,k,A);L=c[3524]|0;if((c[L+96>>2]&16|0)==0){N=L}else{cM[c[L+164>>2]&511](92);N=c[3524]|0}cM[c[N+164>>2]&511](a[c[n>>2]|0]|0);G=B;H=A;break L15492}else if((K|0)==123){L=C;do{L=L+1|0;c[n>>2]=L;}while((a[L]|0)==32);do{if((A|0)==2){O=+uz(L,n);P=u*O;if((c[(c[3524]|0)+96>>2]&16|0)!=0){Q=P;break}Q=B+P}else{Q=B}}while(0);L=c[n>>2]|0;if((a[L]|0)==47){J=L;while(1){R=J+1|0;c[n>>2]=R;S=a[R]|0;if((S<<24>>24|0)==45){T=R;F=11405;break}else if((S<<24>>24|0)==32){J=R}else{U=R;V=S;break}}if((F|0)==11405){while(1){F=0;J=T+1|0;c[n>>2]=J;S=a[J]|0;if(S<<24>>24==32){T=J;F=11405}else{U=J;V=S;break}}}L15518:do{if(V<<24>>24<33){W=U;X=V}else{S=V;J=U;while(1){if((S<<24>>24|0)==61|(S<<24>>24|0)==42|(S<<24>>24|0)==125){W=J;X=S;break L15518}R=J+1|0;c[n>>2]=R;Y=a[R]|0;if(Y<<24>>24<33){W=R;X=Y;break}else{S=Y;J=R}}}}while(0);do{if((X<<24>>24|0)==61){c[n>>2]=W+1;a[W]=0;P=+uz(c[n>>2]|0,n);if(P==0.0){Z=u;break}Z=P*+h[5613]}else if((X<<24>>24|0)==125){uh(-1,204816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);J=c[n>>2]|0;c[n>>2]=J+1;a[J]=0;Z=u}else if((X<<24>>24|0)==42){c[n>>2]=W+1;a[W]=0;P=+uz(c[n>>2]|0,n);if(P==0.0){Z=u;break}Z=P*f}else{c[n>>2]=W+1;a[W]=0;Z=u}}while(0);J=c[n>>2]|0;if((a[J]|0)==32){S=J;while(1){R=S+1|0;c[n>>2]=R;if((a[R]|0)==32){S=R}else{_=R;break}}}else{_=J}$=Z;aa=(a[U]|0)==0?e:U;ab=X;ac=W;ad=_}else{$=u;aa=e;ab=0;ac=0;ad=L}c[n>>2]=lJ(ad,1,aa,$,Q,j,k,A)|0;cS[c[(c[3524]|0)+160>>2]&511]();if((ac|0)==0){G=Q;H=A;break L15492}a[ac]=ab;G=Q;H=A;break L15492}else if((K|0)==95|(K|0)==94){cS[c[(c[3524]|0)+160>>2]&511]();c[n>>2]=lJ((c[n>>2]|0)+1|0,0,e,s,B+(D<<24>>24==94?.5:-.30000001192092896)*f,j,k,A)|0;G=B;H=A;break L15492}else if((K|0)==126){cS[c[(c[3524]|0)+160>>2]&511]();S=(c[n>>2]|0)+1|0;c[n>>2]=S;c[n>>2]=lJ(S,0,e,f,B,j,k,1)|0;cS[c[(c[3524]|0)+160>>2]&511]();S=c[n>>2]|0;if((a[S]|0)==0){G=B;H=A;break L15492}R=S+1|0;c[n>>2]=R;c[n>>2]=lJ(R,0,e,f,B,0,k,2)|0;G=B;H=0;break L15492}else if((K|0)==92){R=a[C+1|0]|0;if((R-48&255)<8){c[q>>2]=0;cJ[c[(c[3524]|0)+156>>2]&15](e,f,B,j,k,A);S=c[n>>2]|0;Y=S+1|0;c[n>>2]=Y;a[w]=a[Y]|0;Y=S+2|0;do{if(((a[Y]|0)-48&255)<8){c[n>>2]=Y;a[x]=a[Y]|0;ae=S+3|0;if(((a[ae]|0)-48&255)>=8){break}c[n>>2]=ae;a[y]=a[ae]|0}}while(0);S=aE(w|0,0,8)|0;be(z|0,44912,(v=i,i=i+8|0,c[v>>2]=S,v)|0);S=a[z]|0;if(S<<24>>24==0){G=B;H=A;break L15492}else{af=z;ag=S}while(1){cM[c[(c[3524]|0)+164>>2]&511](ag<<24>>24);S=af+1|0;Y=a[S]|0;if(Y<<24>>24==0){G=B;H=A;break L15492}else{af=S;ag=Y}}}Y=c[3524]|0;do{if((c[Y+96>>2]&16|0)==0){ah=C}else{if((R<<24>>24|0)==92|(R<<24>>24|0)==40|(R<<24>>24|0)==41){cJ[c[Y+156>>2]&15](e,f,B,j,k,A);cM[c[(c[3524]|0)+164>>2]&511](92);ah=c[n>>2]|0;break}if((aZ(204560,R<<24>>24|0,8)|0)!=0){ah=C;break}cJ[c[Y+156>>2]&15](e,f,B,j,k,A);cM[c[(c[3524]|0)+164>>2]&511](92);cM[c[(c[3524]|0)+164>>2]&511](92);G=B;H=A;break L15492}}while(0);Y=ah+1|0;c[n>>2]=Y;R=a[Y]|0;if(R<<24>>24==0){uh(-1,204288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);G=B;H=A;break L15492}if(!(R<<24>>24==38&(c[11252]|0)==0)){break}R=c[3524]|0;if((aY(c[R>>2]|0,204032)|0)!=0){break}cJ[c[R+156>>2]&15](e,f,B,j,k,A);cM[c[(c[3524]|0)+164>>2]&511](-2);G=B;H=A;break L15492}}while(0);cJ[c[(c[3524]|0)+156>>2]&15](e,f,B,j,k,A);cM[c[(c[3524]|0)+164>>2]&511](a[c[n>>2]|0]|0);G=B;H=A}}while(0);if(!d){break}E=c[n>>2]|0;if((a[E]|0)==0){break L15478}K=E+1|0;c[n>>2]=K;E=a[K]|0;if(E<<24>>24==0){break L15478}else{A=H;B=G;C=K;D=E}}do{if((F|0)==11396){if(d){ai=C;i=m;return ai|0}else{uh(-1,205424,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);break}}}while(0);cS[c[(c[3524]|0)+160>>2]&511]();ai=c[n>>2]|0;i=m;return ai|0}}while(0);cS[c[(c[3524]|0)+160>>2]&511]();ai=c[n>>2]|0;i=m;return ai|0}function lK(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;e=b|0;f=c[e>>2]|0;g=43280;while(1){h=c[g>>2]|0;if((h|0)==0){break}if((c[h+4>>2]|0)==(d|0)){i=11457;break}else{g=h|0}}if((i|0)==11457){g=b;j=h+8|0;c[g>>2]=c[j>>2];c[g+4>>2]=c[j+4>>2];c[g+8>>2]=c[j+8>>2];c[g+12>>2]=c[j+12>>2];c[g+16>>2]=c[j+16>>2];c[g+20>>2]=c[j+20>>2];c[g+24>>2]=c[j+24>>2];c[g+28>>2]=c[j+28>>2];c[g+32>>2]=c[j+32>>2];c[g+36>>2]=c[j+36>>2];c[g+40>>2]=c[j+40>>2];c[g+44>>2]=c[j+44>>2];c[g+48>>2]=c[j+48>>2];c[g+52>>2]=c[j+52>>2];c[e>>2]=f;if((a[b+32|0]&1)!=0){return}c[b+40>>2]=1;c[b+44>>2]=c[b+4>>2];return}j=c[8798]|0;g=(j|0)>0;h=d;L15586:while(1){d=43264;while(1){k=c[d>>2]|0;if((k|0)==0){break}if((c[k+4>>2]|0)==(h|0)){break L15586}else{d=k|0}}l=h-1|0;if(!((h|0)>(j|0)&g)){i=11469;break}h=((l|0)%(j|0)&-1)+1|0}if((i|0)==11469){c[b+4>>2]=l;c[b+40>>2]=1;c[b+44>>2]=l;c[b+8>>2]=l;return}l=b;i=k+8|0;c[l>>2]=c[i>>2];c[l+4>>2]=c[i+4>>2];c[l+8>>2]=c[i+8>>2];c[l+12>>2]=c[i+12>>2];c[l+16>>2]=c[i+16>>2];c[l+20>>2]=c[i+20>>2];c[l+24>>2]=c[i+24>>2];c[l+28>>2]=c[i+28>>2];c[l+32>>2]=c[i+32>>2];c[l+36>>2]=c[i+36>>2];c[l+40>>2]=c[i+40>>2];c[l+44>>2]=c[i+44>>2];c[l+48>>2]=c[i+48>>2];c[l+52>>2]=c[i+52>>2];c[e>>2]=f;if((c[(c[3524]|0)+96>>2]&1024|0)!=0){c[b+4>>2]=h;a[b+32|0]=0;return}if((a[b+32|0]&1)!=0){return}c[b+40>>2]=1;c[b+44>>2]=c[b+4>>2];return}function lL(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0.0,l=0,m=0,n=0;f=i;h[29751]=1.0;a[237976]=0;h[29749]=1.0;h[29748]=0.0;h[29745]=0.0;uF(44912,202200,16);do{if((a[37456]&1)==0){if((cy(e|0,201600)|0)==0){break}h[29743]=+(b>>>0>>>0);h[29741]=+(d>>>0>>>0);g=e;do{j=lJ(g,1,179864,+h[29751],0.0,1,1,0)|0;if((a[j]|0)==0){break}cS[c[(c[3524]|0)+160>>2]&511]();if((a[j]|0)==125){uh(-1,203744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uh(-1,203528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=j+1|0;}while((a[g]|0)!=0);k=+h[29743];if(k>0.0&k<1.0){h[29743]=1.0}c[(c[3524]|0)+8>>2]=~~+h[29745];c[(c[3524]|0)+12>>2]=~~((+h[29749]- +h[29748])*10.0+.5);i=f;return}}while(0);if((c[11252]|0)==15){d=a[e]|0;if(d<<24>>24==0){l=0}else{b=0;g=0;j=d;while(1){if((j&-64)<<24>>24==-128){m=g}else{m=((j&255)>226?2:1)+g|0}d=b+1|0;n=a[e+d|0]|0;if(n<<24>>24==0){l=m;break}else{b=d;g=m;j=n}}}c[(c[3524]|0)+8>>2]=l}else{l=uA(e|0)|0;c[(c[3524]|0)+8>>2]=l}c[(c[3524]|0)+12>>2]=10;i=f;return}function lM(){a[13048|0]=0;return}function lN(){return}function lO(a,b){a=a|0;b=b|0;return}function lP(a){a=a|0;return}function lQ(a,b,c){a=a|0;b=b|0;c=c|0;return}function lR(a){a=a|0;c[59236]=a;return 1}function lS(a){a=a|0;c[56732]=a;return 1}function lT(b){b=b|0;var d=0.0,e=0.0,f=0.0;if((b|0)==10){d=+h[29750];if(a[237976]|0){h[29750]=0.0;do{if((a[237952]&1)==0){e=+h[29743]}else{if((c[59492]|0)==1){f=d*.5+ +h[29743];h[29743]=f;e=f;break}else{f=d+ +h[29743];h[29743]=f;e=f;break}}}while(0);d=+h[29745];h[29745]=d>e?d:e;a[237976]=0}a[237976]=1;e=+h[29751];h[29748]=+h[29748]-e;h[29752]=+h[29752]-e;h[29743]=0.0}if((c[11252]|0)!=15){h[29750]=+h[29751]+ +h[29750];return}if((b&192|0)==128){return}e=+h[29751];d=e+ +h[29750];h[29750]=d;if(b>>>0<=235){return}h[29750]=d+e;return}function lU(a){a=a|0;var b=0;b=(a|0)<-2?-2:a;c[59226]=b>>>0>3?b&3:b;return}function lV(){c[59226]=3;aI(152264,54,1,c[10030]|0);return}function lW(){aD(c[10030]|0);return}function lX(){var a=0;if((c[59234]|0)>0){a=c[10030]|0;aI(152912,16,1,a|0);c[59234]=0}aI(152336,4,1,c[10030]|0);return}function lY(){var a=0,b=0,d=0.0;a=i;b=c[3524]|0;d=+((c[b+12>>2]|0)>>>0>>>0)/300.0;cf(c[10030]|0,152464,(v=i,i=i+16|0,h[v>>3]=+((c[b+8>>2]|0)>>>0>>>0)/300.0,h[v+8>>3]=d,v)|0);d=+g[59222];cf(c[10030]|0,152432,(v=i,i=i+16|0,h[v>>3]=+g[59224],h[v+8>>3]=d,v)|0);i=a;return}function lZ(a,b){a=a|0;b=b|0;var d=0,e=0;d=i;if((c[59234]|0)>0){e=c[10030]|0;aI(152912,16,1,e|0);c[59234]=0}cf(c[10030]|0,152480,(v=i,i=i+16|0,h[v>>3]=+(a>>>0>>>0)/300.0,h[v+8>>3]=+(b>>>0>>>0)/300.0,v)|0);c[59234]=1;i=d;return}function l_(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0.0,j=0.0,k=0,l=0;d=i;e=c[10030]|0;if((c[59234]|0)==1){f=c[236912+((c[59226]|0)+2<<2)>>2]|0;g=+(a>>>0>>>0)/300.0;j=+(b>>>0>>>0)/300.0;cf(e|0,152664,(v=i,i=i+24|0,c[v>>2]=f,h[v+8>>3]=g,h[v+16>>3]=j,v)|0);k=c[59234]|0;l=k+1|0;c[59234]=l;i=d;return}else{j=+(a>>>0>>>0)/300.0;g=+(b>>>0>>>0)/300.0;cf(e|0,152624,(v=i,i=i+16|0,h[v>>3]=j,h[v+8>>3]=g,v)|0);k=c[59234]|0;l=k+1|0;c[59234]=l;i=d;return}}function l$(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0;e=i;if((c[59234]|0)>0){f=c[10030]|0;aI(152912,16,1,f|0);c[59234]=0}cf(c[10030]|0,103640,(v=i,i=i+8|0,c[v>>2]=d,v)|0);d=c[59236]|0;if((d|0)==1){f=c[10030]|0;aF(32,f|0)}else if((d|0)==0){aI(152848,6,1,c[10030]|0)}else if((d|0)==2){aI(152840,6,1,c[10030]|0)}cf(c[10030]|0,152712,(v=i,i=i+16|0,h[v>>3]=+(a>>>0>>>0)/300.0,h[v+8>>3]=+(b>>>0>>>0)/300.0,v)|0);i=e;return}function l0(a){a=a|0;if((c[59234]|0)<=0){return 0}aI(152912,16,1,c[10030]|0);c[59234]=0;return 0}function l1(a,b,d){a=a|0;b=b|0;d=d|0;cM[c[(c[3524]|0)+64>>2]&511](0);ly(a,b,d);return}function l2(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0.0,l=0.0,m=0.0,n=0.0;g=i;if((c[59234]|0)>0){j=c[10030]|0;aI(152912,16,1,j|0);c[59234]=0}j=c[10030]|0;k=+(a>>>0>>>0)/300.0;l=+(b>>>0>>>0)/300.0;m=+(d>>>0>>>0)/300.0;n=+(e>>>0>>>0)/300.0;if((f|0)==0){cf(j|0,152984,(v=i,i=i+32|0,h[v>>3]=k,h[v+8>>3]=l,h[v+16>>3]=m,h[v+24>>3]=n,v)|0);i=g;return}else{cf(j|0,153024,(v=i,i=i+32|0,h[v>>3]=k,h[v+8>>3]=l,h[v+16>>3]=m,h[v+24>>3]=n,v)|0);i=g;return}}function l3(){aI(153080,10,1,c[10030]|0);return}function l4(){aI(153112,11,1,c[10030]|0);return}function l5(){aI(153144,9,1,c[10030]|0);return}function l6(){aI(153304,23,1,c[10030]|0);c[57146]=0;aI(153392,9,1,c[10030]|0);return}function l7(a,b){a=a|0;b=b|0;var d=0;d=i;cf(c[10030]|0,153328,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=439-b,v)|0);i=d;return}function l8(a,b){a=a|0;b=b|0;var d=0;d=i;cf(c[10030]|0,153344,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=439-b,v)|0);i=d;return}function l9(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;b=i;d=(a|0)<-2?-2:a;if((c[57208]|0)==0){if((d|0)>8){e=(d|0)%9&-1}else{e=d}a=c[10030]|0;f=e+2|0;e=c[228712+(f<<2)>>2]|0;cf(a|0,153376,(v=i,i=i+8|0,c[v>>2]=e,v)|0);e=c[10030]|0;a=c[228592+(f<<2)>>2]|0;cf(e|0,153368,(v=i,i=i+8|0,c[v>>2]=a,v)|0);i=b;return}else{if((d|0)>14){g=(d|0)%15&-1}else{g=d}d=c[10030]|0;a=g+2|0;g=c[228760+(a<<2)>>2]|0;cf(d|0,153376,(v=i,i=i+8|0,c[v>>2]=g,v)|0);g=c[10030]|0;d=c[228640+(a<<2)>>2]|0;cf(g|0,153368,(v=i,i=i+8|0,c[v>>2]=d,v)|0);i=b;return}}function ma(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;f=i;if((c[57146]|0)==1){g=b-11|0;h=c[10030]|0;j=439-d|0;cf(h|0,153328,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=j,v)|0)}else{j=c[10030]|0;g=430-d|0;cf(j|0,153328,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=g,v)|0)}aF(84,c[10030]|0);aF(39,c[10030]|0);g=a[e]|0;if(g<<24>>24==0){k=c[10030]|0;l=aF(39,k|0)|0;m=c[10030]|0;n=aF(10,m|0)|0;i=f;return}else{o=e;p=g}do{aF(p<<24>>24|0,c[10030]|0);if((a[o]|0)==39){g=c[10030]|0;aF(39,g|0)}o=o+1|0;p=a[o]|0;}while(p<<24>>24!=0);k=c[10030]|0;l=aF(39,k|0)|0;m=c[10030]|0;n=aF(10,m|0)|0;i=f;return}function mb(a){a=a|0;var b=0;b=(a|0)!=0;c[57146]=b&1;a=c[10030]|0;if(b){aI(153416|0,10,1,a|0);return 1}else{aI(153392|0,9,1,a|0);return 1}return 0}function mc(){aI(153552,24,1,c[10030]|0);aI(153520,20,1,c[10030]|0);aD(c[10030]|0);return}function md(){aI(153584,13,1,c[10030]|0);aD(c[10030]|0);return}function me(){aI(153704,9,1,c[10030]|0);aD(c[10030]|0);return}function mf(){aI(153720,11,1,c[10030]|0);aD(c[10030]|0);return}function mg(a,b){a=a|0;b=b|0;aI(153736,3,1,c[10030]|0);t0(a,b);aD(c[10030]|0);return}function mh(a,b){a=a|0;b=b|0;aI(153760,3,1,c[10030]|0);t0(a,b);aD(c[10030]|0);return}function mi(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;b=(a|0)<-2?-2:a;if((b|0)==(-1|0)){a=c[10030]|0;aI(153824,4,1,a|0)}else if((b|0)==(-2|0)){aI(153800,4,1,c[10030]|0)}else{aI(153792,3,1,c[10030]|0);a=(b|0)%14&-1;d=a+2|0;e=(d|0)>-1?d:-2-a|0;a=e>>>10;f=e>>>4&63|64;g=e&15|(d>>31)+48&-16;if((a|0)==0){if((f|0)!=64){h=11606}}else{d=a+64|0;a=c[10030]|0;aF(d|0,a|0);h=11606}if((h|0)==11606){h=c[10030]|0;aF(f|0,h|0)}aF(g|0,c[10030]|0)}aI(153784,3,1,c[10030]|0);g=(b|0)>0?(b|0)%8&-1:0;b=(g|0)>-1?g:-g|0;h=b>>>10;f=b>>>4&63|64;a=b&15|(g>>31)+48&-16;do{if((h|0)==0){if((f|0)!=64){break}i=c[10030]|0;j=aF(a|0,i|0)|0;k=c[10030]|0;l=aD(k|0)|0;return}else{g=h+64|0;b=c[10030]|0;aF(g|0,b|0)}}while(0);aF(f|0,c[10030]|0);i=c[10030]|0;j=aF(a|0,i|0)|0;k=c[10030]|0;l=aD(k|0)|0;return}function mj(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0;if((c[56732]|0)==0){e=b-29|0;f=c[10030]|0;aI(153736,3,1,f|0);t0(a,e);e=c[10030]|0;aD(e|0);e=c[10030]|0;aI(154080,5,1,e|0)}else{e=a+19|0;a=c[10030]|0;aI(153736,3,1,a|0);t0(e,b);b=c[10030]|0;aD(b|0);b=c[10030]|0;aI(153952,6,1,b|0)}aI(153920,3,1,c[10030]|0);b=uA(d|0)|0;e=(b|0)>-1?b:-b|0;a=e>>>10;f=e>>>4&63|64;g=e&15|(b>>31)+48&-16;do{if((a|0)==0){if((f|0)!=64){break}h=c[10030]|0;i=aF(g|0,h|0)|0;j=c[10030]|0;k=aK(d|0,j|0)|0;l=c[10030]|0;m=aD(l|0)|0;return}else{b=a+64|0;e=c[10030]|0;aF(b|0,e|0)}}while(0);aF(f|0,c[10030]|0);h=c[10030]|0;i=aF(g|0,h|0)|0;j=c[10030]|0;k=aK(d|0,j|0)|0;l=c[10030]|0;m=aD(l|0)|0;return}function mk(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0.0,o=0.0,p=0,q=0,r=0.0;b=i;i=i+24|0;d=b|0;g[59224]=0.0;g[59222]=0.0;e=c[13898]|0;L15781:do{if((e|0)<(c[8272]|0)){f=c[1054]|0;L15783:do{if((a[f+(e*40&-1)|0]&1)!=0){j=c[f+(e*40&-1)+36>>2]|0;k=f+(e*40&-1)+32|0;l=c[10036]|0;m=0;while(1){if((m|0)>=(j|0)){break}if((a[l+((c[k>>2]|0)+m|0)|0]|0)==(a[m+103664|0]|0)){m=m+1|0}else{break L15783}}if((m|0)==1){break L15781}}}while(0);f=is(d)|0;k=c[f>>2]|0;if((k|0)==2){n=+h[f+8>>3]}else if((k|0)==1){n=+(c[f+8>>2]|0)}else if((k|0)==3){n=+uz(c[f+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}o=n;f=c[13898]|0;if((f|0)>=(c[8272]|0)){break}k=c[1054]|0;L15797:do{if((a[k+(f*40&-1)|0]&1)!=0){l=c[k+(f*40&-1)+36>>2]|0;j=k+(f*40&-1)+32|0;p=c[10036]|0;q=0;while(1){if((q|0)>=(l|0)){break}if((a[p+((c[j>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break L15797}}if((q|0)==1){break L15781}}}while(0);f=is(d)|0;k=c[f>>2]|0;if((k|0)==3){r=+uz(c[f+8>>2]|0,0)}else if((k|0)==2){r=+h[f+8>>3]}else if((k|0)==1){r=+(c[f+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g[59224]=o;g[59222]=r}}while(0);r=+g[59222];be(13048,152224,(v=i,i=i+16|0,h[v>>3]=+g[59224],h[v+8>>3]=r,v)|0);i=b;return}function ml(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0;b=i;i=i+24|0;d=b|0;e=c[13898]|0;L15812:do{if((e|0)<(c[8272]|0)){f=c[1054]|0;L15814:do{if((a[f+(e*40&-1)|0]&1)!=0){g=c[f+(e*40&-1)+36>>2]|0;j=f+(e*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(g|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{break L15814}}if((l|0)==1){m=0;break L15812}}}while(0);f=is(d)|0;j=c[f>>2]|0;if((j|0)==2){n=+h[f+8>>3]}else if((j|0)==1){n=+(c[f+8>>2]|0)}else if((j|0)==3){n=+uz(c[f+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=(~~n|0)==16}else{m=0}}while(0);c[57208]=m&1;d=m?153072:170216;uD(13048,d|0,(uA(d|0)|0)+1|0);i=b;return}function mm(){return}function mn(){return}function mo(a){a=a|0;var b=0;b=(a|0)<0?0:a&7;c[60208]=240840+(b<<2);c[60222]=b;return}function mp(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;aI(154096,3,1,c[10030]|0);e=(d|0)>0?(d|0)%11&-1:0;d=(e|0)>-1?e:-e|0;f=d>>>10;g=d>>>4&63|64;h=d&15|(e>>31)+48&-16;if((f|0)==0){if((g|0)!=64){i=11668}}else{e=f+64|0;f=c[10030]|0;aF(e|0,f|0);i=11668}if((i|0)==11668){i=c[10030]|0;aF(g|0,i|0)}aF(h|0,c[10030]|0);aI(154088,3,1,c[10030]|0);t0(a,b);aD(c[10030]|0);return}function mq(){aF(29,c[10030]|0);aF(32,c[10030]|0);aF(108,c[10030]|0);aF(32,c[10030]|0);aF(64,c[10030]|0);aF(31,c[10030]|0);return}function mr(){aI(154304,2,1,c[10030]|0);aD(c[10030]|0);aB(1);return}function ms(a,b){a=a|0;b=b|0;aF(29,c[10030]|0);aF(b>>>5&31|32|0,c[10030]|0);aF(b&31|96|0,c[10030]|0);aF(a>>>5&31|32|0,c[10030]|0);aF(a&31|64|0,c[10030]|0);c[60220]=a;c[60218]=b;return}function mt(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;if((c[60222]|0)<1){d=b>>>5&31|32;e=c[10030]|0;aF(d|0,e|0);e=b&31|96;d=c[10030]|0;aF(e|0,d|0);d=a>>>5&31|32;e=c[10030]|0;aF(d|0,e|0);e=a&31|64;d=c[10030]|0;aF(e|0,d|0);c[60220]=a;c[60218]=b;return}d=c[60220]|0;e=c[60218]|0;f=a-d|0;g=b-e|0;h=(f|0)>-1;i=(h?f:-f|0)<<1;j=(g|0)>-1;k=(j?g:-g|0)<<1;l=f>>31|1;f=g>>31|1;do{if((i|0)<(k|0)){if((e|0)==(b|0)){m=d;n=b;break}g=j^1;o=e;p=d;q=i-(k>>1)|0;while(1){t$(p,o,0);if((q|0)<1&((q|0)!=0|g)){r=q;s=p}else{r=q-k|0;s=p+l|0}t=o+f|0;if((t|0)==(b|0)){m=s;n=b;break}else{o=t;p=s;q=r+i|0}}}else{if((d|0)==(a|0)){m=a;n=e;break}q=h^1;p=e;o=d;g=k-(i>>1)|0;while(1){t$(o,p,0);if((g|0)<1&((g|0)!=0|q)){u=g;v=p}else{u=g-i|0;v=p+f|0}t=o+l|0;if((t|0)==(a|0)){m=a;n=v;break}else{p=v;o=t;g=u+k|0}}}}while(0);t$(m,n,1);c[60220]=a;c[60218]=b;return}function mu(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0;e=i;f=b-11|0;aF(29,c[10030]|0);aF(f>>>5&31|32|0,c[10030]|0);aF(f&31|96|0,c[10030]|0);aF(a>>>5&31|32|0,c[10030]|0);aF(a&31|64|0,c[10030]|0);cf(c[10030]|0,154112,(v=i,i=i+8|0,c[v>>2]=d,v)|0);i=e;return}function mv(){aI(154432,6,1,c[10030]|0);aD(c[10030]|0);aB(1);return}function mw(){aI(154120,6,1,c[10030]|0);aD(c[10030]|0);aB(1);return}function mx(a,b){a=a|0;b=b|0;aF(29,c[10030]|0);aF(b>>>5&31|32|0,c[10030]|0);aF(b&31|96|0,c[10030]|0);aF(a>>>5&31|32|0,c[10030]|0);aF(a&31|64|0,c[10030]|0);return}function my(a,b){a=a|0;b=b|0;aF(b>>>5&31|32|0,c[10030]|0);aF(b&31|96|0,c[10030]|0);aF(a>>>5&31|32|0,c[10030]|0);aF(a&31|64|0,c[10030]|0);return}function mz(b){b=b|0;var d=0,e=0,f=0;d=i;e=(b|0)<-2?-2:b;if((e|0)>9){f=(e|0)%10&-1}else{f=e}cf(c[10030]|0,154424,(v=i,i=i+8|0,c[v>>2]=a[154144+(f+2|0)|0]|0,v)|0);c[8836]=f;i=d;return}function mA(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0;f=i;g=c[8836]|0;cf(c[10030]|0,154424,(v=i,i=i+8|0,c[v>>2]=96,v)|0);c[8836]=0;h=d-11|0;aF(29,c[10030]|0);aF(h>>>5&31|32|0,c[10030]|0);aF(h&31|96|0,c[10030]|0);aF(b>>>5&31|32|0,c[10030]|0);aF(b&31|64|0,c[10030]|0);cf(c[10030]|0,154112,(v=i,i=i+8|0,c[v>>2]=e,v)|0);e=(g|0)<-2?-2:g;if((e|0)>9){j=(e|0)%10&-1}else{j=e}cf(c[10030]|0,154424,(v=i,i=i+8|0,c[v>>2]=a[154144+(j+2|0)|0]|0,v)|0);c[8836]=j;i=f;return}function mB(){aI(154160,3,1,c[10030]|0);return}function mC(){aI(154432,6,1,c[10030]|0);aI(154304,2,1,c[10030]|0);return}function mD(b){b=b|0;var e=0,f=0,g=0;e=i;do{if((b|0)==0){f=0}else{g=uA(b|0)|0;if((g|0)==0){f=0;break}f=((d[b+(g-1|0)|0]|0)<<24)-822083584>>24}}while(0);cf(c[10030]|0,154424,(v=i,i=i+8|0,c[v>>2]=a[154344+((f-1|0)>>>0<3?f:0)|0]|0,v)|0);i=e;return 1}function mE(){aI(154432,6,1,c[10030]|0);return}function mF(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0.0,an=0.0,ao=0,ap=0.0,aq=0,ar=0,as=0,at=0.0,au=0.0,av=0.0,aw=0.0,ax=0.0,ay=0,az=0,aA=0;b=i;i=i+240|0;d=b|0;e=b+24|0;f=b+48|0;g=b+72|0;j=b+96|0;k=b+120|0;l=b+144|0;m=b+168|0;n=b+192|0;o=b+216|0;if((c[13898]|0)==2){p=2}else{h[30427]=10.0;h[6936]=10.0;h[6935]=1.0;a[243104]=0;a[243312]=0;uu(c[60826]|0);c[60826]=0;uu(c[60774]|0);c[60774]=0;uu(c[60778]|0);c[60778]=0;h[6931]=1.0;a[55512]=0;h[6938]=1.0;a[243424]=0;p=c[13898]|0}q=c[8272]|0;L15888:do{if((p|0)<(q|0)){r=n|0;s=n+8|0;t=m|0;u=m+8|0;w=l|0;x=l+8|0;y=k|0;z=k+8|0;A=j|0;B=j+8|0;C=g|0;D=g+8|0;E=f|0;F=f+8|0;G=e|0;H=e+8|0;I=d|0;J=d+8|0;K=o|0;L=o+8|0;M=0;N=p;O=q;L15890:while(1){P=c[1054]|0;Q=P+(N*40&-1)|0;L15892:do{if((a[Q]&1)!=0){R=c[P+(N*40&-1)+36>>2]|0;S=P+(N*40&-1)+32|0;T=c[10036]|0;U=0;while(1){if((U|0)>=(R|0)){break}if((a[T+((c[S>>2]|0)+U|0)|0]|0)==(a[U+103664|0]|0)){U=U+1|0}else{break L15892}}if((U|0)==1){V=M;break L15888}}}while(0);W=N+1|0;c[13898]=W;S=c[60782]|0;L15899:do{if((S|0)==0){X=243128}else{T=c[P+(N*40&-1)+36>>2]|0;R=c[P+(N*40&-1)+32>>2]|0;Y=(a[Q]&1)==0;Z=(T|0)>0;_=c[10036]|0;$=243128;aa=S;while(1){L15903:do{if(!Y){if(Z){ab=0;ac=0;ad=R;while(1){ae=a[aa+ab|0]|0;if(ae<<24>>24==(a[_+(ab+ad|0)|0]|0)){af=ad;ag=ac}else{if(ae<<24>>24!=36){break L15903}af=ad-1|0;ag=1}ah=ab+1|0;if((ah|0)<(ag+T|0)){ab=ah;ac=ag;ad=af}else{break}}if((ag|0)==0){ai=ah}else{X=$;break L15899}}else{ai=0}ad=a[aa+ai|0]|0;if((ad<<24>>24|0)==36|(ad<<24>>24|0)==0){X=$;break L15899}}}while(0);ad=$+8|0;ac=c[ad>>2]|0;if((ac|0)==0){X=ad;break}else{$=ad;aa=ac}}}}while(0);S=c[X+4>>2]|0;do{if((S|0)==3){if((W|0)>=(O|0)){aj=11782;break L15890}L15919:do{if((a[P+(W*40&-1)|0]&1)!=0){Q=c[P+(W*40&-1)+36>>2]|0;aa=P+(W*40&-1)+32|0;$=c[10036]|0;T=0;while(1){if((T|0)>=(Q|0)){break}if((a[$+((c[aa>>2]|0)+T|0)|0]|0)==(a[T+103664|0]|0)){T=T+1|0}else{break L15919}}if((T|0)==1){aj=11782;break L15890}}}while(0);a[14176]=1;is(k);a[14176]=0;if((c[y>>2]|0)!=3){aj=11781;break L15890}aa=c[z>>2]|0;c[60826]=aa;if((aa|0)==0){aj=11784;break L15890}if((a[aa+(b4(aa|0,218288)|0)|0]|0)!=0){aj=11787;break L15890}a[243104]=1;ak=M}else if((S|0)==7){a[243312]=1;ak=M}else if((S|0)==8){L15932:do{if((W|0)<(O|0)){L15934:do{if((a[P+(W*40&-1)|0]&1)!=0){aa=c[P+(W*40&-1)+36>>2]|0;$=P+(W*40&-1)+32|0;Q=c[10036]|0;_=0;while(1){if((_|0)>=(aa|0)){break}if((a[Q+((c[$>>2]|0)+_|0)|0]|0)==(a[_+103664|0]|0)){_=_+1|0}else{break L15934}}if((_|0)==1){al=0;break L15932}}}while(0);a[14176]=1;is(f);a[14176]=0;if((c[E>>2]|0)==3){al=c[F>>2]|0;break}else{c[13898]=W;al=0;break}}else{al=0}}while(0);c[60778]=al;ak=M}else if((S|0)==9){c[(c[3524]|0)+68>>2]=46;T=(c[3524]|0)+96|0;c[T>>2]=c[T>>2]|32;ak=M}else if((S|0)==5){if((W|0)>=(O|0)){aj=11770;break L15890}L15948:do{if((a[P+(W*40&-1)|0]&1)!=0){T=c[P+(W*40&-1)+36>>2]|0;$=P+(W*40&-1)+32|0;Q=c[10036]|0;aa=0;while(1){if((aa|0)>=(T|0)){break}if((a[Q+((c[$>>2]|0)+aa|0)|0]|0)==(a[aa+103664|0]|0)){aa=aa+1|0}else{break L15948}}if((aa|0)==1){aj=11770;break L15890}}}while(0);a[14176]=1;is(l);a[14176]=0;if((c[w>>2]|0)!=3){aj=11769;break L15890}$=c[x>>2]|0;c[60774]=$;if(($|0)==0){aj=11772;break L15890}else{ak=M}}else if((S|0)==13){a[55512]=1;ak=M}else if((S|0)==14){$=is(o)|0;Q=c[$>>2]|0;if((Q|0)==2){am=+h[$+8>>3]}else if((Q|0)==3){am=+uz(c[$+8>>2]|0,0)}else if((Q|0)==1){am=+(c[$+8>>2]|0)}else{aj=11854;break L15890}if((c[K>>2]|0)==3){uu(c[L>>2]|0);c[K>>2]=1}h[6938]=am;if(am>.2){ak=M;break}h[6938]=1.0;ak=M}else if((S|0)==0){L15968:do{if((W|0)<(O|0)){L15970:do{if((a[P+(W*40&-1)|0]&1)!=0){$=c[P+(W*40&-1)+36>>2]|0;Q=P+(W*40&-1)+32|0;T=c[10036]|0;R=0;while(1){if((R|0)>=($|0)){break}if((a[T+((c[Q>>2]|0)+R|0)|0]|0)==(a[R+103664|0]|0)){R=R+1|0}else{break L15970}}if((R|0)==1){aj=11735;break L15968}}}while(0);aa=is(n)|0;Q=c[aa>>2]|0;if((Q|0)==3){an=+uz(c[aa+8>>2]|0,0)}else if((Q|0)==1){an=+(c[aa+8>>2]|0)}else if((Q|0)==2){an=+h[aa+8>>3]}else{aj=11740;break L15890}if((c[r>>2]|0)==3){uu(c[s>>2]|0);c[r>>2]=1}aa=~~(+(~~an|0)*10.0);c[13836]=aa;Q=c[13898]|0;L15985:do{if((c[8272]|0)>(Q|0)){T=c[1054]|0;if((a[T+(Q*40&-1)|0]&1)==0){ao=aa;break}$=c[T+(Q*40&-1)+36>>2]|0;_=T+(Q*40&-1)+32|0;T=c[10036]|0;Z=0;while(1){if((Z|0)>=($|0)){break}if((a[T+((c[_>>2]|0)+Z|0)|0]|0)==(a[Z+148464|0]|0)){Z=Z+1|0}else{ao=aa;break L15985}}if((Z|0)!=1){ao=aa;break}c[13898]=Q+1;_=is(m)|0;T=c[_>>2]|0;if((T|0)==2){ap=+h[_+8>>3]}else if((T|0)==3){ap=+uz(c[_+8>>2]|0,0)}else if((T|0)==1){ap=+(c[_+8>>2]|0)}else{aj=11753;break L15890}if((c[t>>2]|0)==3){uu(c[u>>2]|0);c[t>>2]=1}c[13832]=~~(+(~~ap|0)*10.0);ao=c[13836]|0}else{ao=aa}}while(0);if((ao|0)<1){c[13836]=6e3;aq=6e3}else{aq=ao}if((c[13832]|0)>=1){ar=aq;break}c[13832]=4e3;ar=aq}else{aj=11735}}while(0);if((aj|0)==11735){aj=0;c[13836]=6e3;c[13832]=4e3;ar=6e3}c[(c[3524]|0)+8>>2]=ar;c[(c[3524]|0)+12>>2]=c[13832];ak=M}else if((S|0)==10){c[(c[3524]|0)+68>>2]=118;aa=(c[3524]|0)+96|0;c[aa>>2]=c[aa>>2]&-33;ak=M}else if((S|0)==4){a[243104]=0;ak=M}else if((S|0)==1){uu(c[13874]|0);as=c[13898]|0;if((as|0)>=(c[8272]|0)){aj=11798;break L15890}aa=c[1054]|0;L16013:do{if((a[aa+(as*40&-1)|0]&1)!=0){Q=c[aa+(as*40&-1)+36>>2]|0;_=aa+(as*40&-1)+32|0;T=c[10036]|0;$=0;while(1){if(($|0)>=(Q|0)){break}if((a[T+((c[_>>2]|0)+$|0)|0]|0)==(a[$+103664|0]|0)){$=$+1|0}else{break L16013}}if(($|0)==1){aj=11798;break L15890}}}while(0);a[14176]=1;is(j);a[14176]=0;if((c[A>>2]|0)!=3){aj=11797;break L15890}aa=c[B>>2]|0;c[13874]=aa;if((aa|0)==0){aj=11800;break L15890}do{if((a[aa]|0)==0){at=+h[30427];h[6936]=at;au=at}else{_=bk(aa|0,148464)|0;if((a[aa+_|0]|0)!=44){au=+h[6936];break}T=aa+(_+1|0)|0;ca(T|0,148448,(v=i,i=i+8|0,c[v>>2]=55488,v)|0);at=+h[6936];if(at>0.0){au=at;break}at=+h[30427];h[6936]=at;au=at}}while(0);c[(c[3524]|0)+16>>2]=~~(au*+h[6935]*10.0);c[(c[3524]|0)+20>>2]=~~(+h[6936]*+h[6935]*.8*10.0);ak=M}else if((S|0)==15){a[55440]=1;ak=M}else if((S|0)==16){a[55440]=0;ak=M}else if((S|0)==12){a[55512]=0;ak=M}else if((S|0)==2){aa=is(g)|0;T=c[aa>>2]|0;if((T|0)==2){av=+h[aa+8>>3]}else if((T|0)==3){av=+uz(c[aa+8>>2]|0,0)}else if((T|0)==1){av=+(c[aa+8>>2]|0)}else{aj=11813;break L15890}if((c[C>>2]|0)==3){uu(c[D>>2]|0);c[C>>2]=1}at=av>0.0?av:10.0;h[30427]=at;h[6936]=at;ak=M}else if((S|0)==17){aa=hK()|0;be(243424,164384,(v=i,i=i+24|0,c[v>>2]=aa>>>16&255,c[v+8>>2]=aa>>>8&255,c[v+16>>2]=aa&255,v)|0);ak=aa}else if((S|0)==11){aa=is(d)|0;T=c[aa>>2]|0;if((T|0)==1){aw=+(c[aa+8>>2]|0)}else if((T|0)==2){aw=+h[aa+8>>3]}else if((T|0)==3){aw=+uz(c[aa+8>>2]|0,0)}else{aj=11843;break L15890}if((c[I>>2]|0)==3){uu(c[J>>2]|0);c[I>>2]=1}h[6935]=aw;if(aw>0.0){ak=M;break}h[6935]=1.0;ak=M}else if((S|0)==6){aa=is(e)|0;T=c[aa>>2]|0;if((T|0)==2){ax=+h[aa+8>>3]}else if((T|0)==3){ax=+uz(c[aa+8>>2]|0,0)}else if((T|0)==1){ax=+(c[aa+8>>2]|0)}else{aj=11834;break L15890}if((c[G>>2]|0)==3){uu(c[H>>2]|0);c[G>>2]=1}h[6931]=ax;if(ax>0.0){ak=M;break}h[6931]=1.0;ak=M}else{uh(N,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);ak=M}}while(0);S=c[13898]|0;P=c[8272]|0;if((S|0)<(P|0)){M=ak;N=S;O=P}else{V=ak;break L15888}}if((aj|0)==11843){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11800){ay=c[13898]|0;uf(ay,154936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11781){c[13898]=W;aj=11782}else if((aj|0)==11769){c[13898]=W;aj=11770}else if((aj|0)==11813){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11854){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11787){uf((c[13898]|0)-1|0,154976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11797){c[13898]=as;aj=11798}else if((aj|0)==11834){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11784){az=c[13898]|0;uf(az,155080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11740){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11772){aA=c[13898]|0;uf(aA,155136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11753){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((aj|0)==11798){c[13874]=0;ay=as;uf(ay,154936,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11782){c[60826]=0;az=W;uf(az,155080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((aj|0)==11770){c[60774]=0;aA=W;uf(aA,155136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{V=0}}while(0);c[(c[3524]|0)+16>>2]=~~(+h[6936]*+h[6935]*10.0);c[(c[3524]|0)+20>>2]=~~(+h[6936]*+h[6935]*.8*10.0);be(13048,(a[55512]|0?95248:154912)|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);ax=+h[6938];if(a[55512]&ax!=1.0){aA=13048+(uA(13048)|0)|0;be(aA|0,154888,(v=i,i=i+8|0,h[v>>3]=ax,v)|0)}aA=13048+(uA(13048)|0)|0;be(aA|0,(a[55440]|0?154872:154864)|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);aA=13048+(uA(13048)|0)|0;W=c[3524]|0;aj=~~(+((c[W+12>>2]|0)>>>0>>>0)/10.0);be(aA|0,154848,(v=i,i=i+16|0,c[v>>2]=~~(+((c[W+8>>2]|0)>>>0>>>0)/10.0),c[v+8>>2]=aj,v)|0);aj=13048+(uA(13048)|0)|0;ax=+h[6936];aw=+h[6931];be(aj|0,154816,(v=i,i=i+24|0,c[v>>2]=(c[(c[3524]|0)+68>>2]|0)==46?154784:179864,h[v+8>>3]=ax,h[v+16>>3]=aw,v)|0);aj=13048+(uA(13048)|0)|0;be(aj|0,154712,(v=i,i=i+8|0,h[v>>3]=+h[6935],v)|0);if((a[243424]|0)!=0){aj=13048+(uA(13048)|0)|0;be(aj|0,170616,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}V=c[60826]|0;aj=13048+(uA(13048)|0)|0;do{if((V|0)==0){uD(aj|0,154600,12);if(a[243312]|0){uD(13048+(uA(13048)|0)|0,154568,9)}W=c[60774]|0;if((W|0)==0){break}aA=13048+(uA(13048)|0)|0;be(aA|0,154544,(v=i,i=i+8|0,c[v>>2]=W,v)|0)}else{be(aj|0,154624,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}}while(0);V=c[60778]|0;if((V|0)==0){i=b;return}be(13048+(uA(13048)|0)|0,154512,(v=i,i=i+8|0,c[v>>2]=V,v)|0);i=b;return}function mG(){return}function mH(){return}function mI(a){a=a|0;c[13840]=-a;return 1}function mJ(){var b=0,d=0,e=0,f=0,g=0,j=0.0,k=0,l=0,m=0,n=0;b=i;if(a[55472]|0){d=c[10030]|0;aI(165112,14,1,d|0);if(!(a[72328]|0)){d=c[10030]|0;aI(165528,17,1,d|0)}a[55472]=0;a[72328]=1}aI(159504,62,1,c[10030]|0);cf(c[10030]|0,159472,(v=i,i=i+8|0,c[v>>2]=~~(+((c[(c[3524]|0)+8>>2]|0)>>>0>>>0)/10.0),v)|0);cf(c[10030]|0,159440,(v=i,i=i+8|0,c[v>>2]=~~(+((c[(c[3524]|0)+12>>2]|0)>>>0>>>0)/10.0),v)|0);cf(c[10030]|0,159408,(v=i,i=i+8|0,h[v>>3]=+(c[7938]|0)/10.0,v)|0);cf(c[10030]|0,159320,(v=i,i=i+8|0,h[v>>3]=+(c[7939]|0)/10.0,v)|0);cf(c[10030]|0,159248,(v=i,i=i+8|0,h[v>>3]=+(((c[(c[3524]|0)+12>>2]|0)-(c[7940]|0)|0)>>>0>>>0)/10.0,v)|0);cf(c[10030]|0,159216,(v=i,i=i+8|0,h[v>>3]=+(((c[(c[3524]|0)+12>>2]|0)-(c[7941]|0)|0)>>>0>>>0)/10.0,v)|0);cf(c[10030]|0,159176,(v=i,i=i+8|0,h[v>>3]=+((c[7939]|0)-(c[7938]|0)|0)/10.0,v)|0);cf(c[10030]|0,159016,(v=i,i=i+8|0,h[v>>3]=+((c[7941]|0)-(c[7940]|0)|0)/10.0,v)|0);e7(1);do{if((c[16546]|0)!=1){d=e6(72800)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==1){f=c[10030]|0;g=c[d+24>>2]|0;cf(f|0,158984,(v=i,i=i+16|0,c[v>>2]=158952,c[v+8>>2]=g,v)|0);break}else if((e|0)==2){j=+h[d+24>>3];cf(c[10030]|0,158936,(v=i,i=i+16|0,c[v>>2]=158952,h[v+8>>3]=j,v)|0);break}else{break}}}while(0);d=e6(72544)|0;if((d|0)==0){break}e=c[d+16>>2]|0;if((e|0)==1){g=c[10030]|0;f=c[d+24>>2]|0;cf(g|0,158984,(v=i,i=i+16|0,c[v>>2]=158912,c[v+8>>2]=f,v)|0);break}else if((e|0)==2){j=+h[d+24>>3];cf(c[10030]|0,158936,(v=i,i=i+16|0,c[v>>2]=158912,h[v+8>>3]=j,v)|0);break}else{break}}}while(0);d=e6(225256)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==1){f=c[10030]|0;g=c[d+24>>2]|0;cf(f|0,158984,(v=i,i=i+16|0,c[v>>2]=158856,c[v+8>>2]=g,v)|0);break}else if((e|0)==2){j=+h[d+24>>3];cf(c[10030]|0,158936,(v=i,i=i+16|0,c[v>>2]=158856,h[v+8>>3]=j,v)|0);break}else{break}}}while(0);d=e6(225200)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==1){g=c[10030]|0;f=c[d+24>>2]|0;cf(g|0,158984,(v=i,i=i+16|0,c[v>>2]=158832,c[v+8>>2]=f,v)|0);break}else if((e|0)==2){j=+h[d+24>>3];cf(c[10030]|0,158936,(v=i,i=i+16|0,c[v>>2]=158832,h[v+8>>3]=j,v)|0);break}else{break}}}while(0);if((a[30528]&1)!=0){d=c[10030]|0;j=(c[17366]&1|0)==0?+h[8687]:0.0;cf(d|0,158760,(v=i,i=i+8|0,h[v>>3]=j,v)|0)}do{if((c[17261]&3|0)==0){d=c[10030]|0;aI(158560,33,1,d|0)}else{d=e6(224936)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==1){f=c[10030]|0;g=c[d+24>>2]|0;cf(f|0,158984,(v=i,i=i+16|0,c[v>>2]=158736,c[v+8>>2]=g,v)|0);break}else if((e|0)==2){j=+h[d+24>>3];cf(c[10030]|0,158936,(v=i,i=i+16|0,c[v>>2]=158736,h[v+8>>3]=j,v)|0);break}else{break}}}while(0);d=e6(224872)|0;if((d|0)==0){break}e=c[d+16>>2]|0;if((e|0)==1){g=c[10030]|0;f=c[d+24>>2]|0;cf(g|0,158984,(v=i,i=i+16|0,c[v>>2]=158664,c[v+8>>2]=f,v)|0);break}else if((e|0)==2){j=+h[d+24>>3];cf(c[10030]|0,158936,(v=i,i=i+16|0,c[v>>2]=158664,h[v+8>>3]=j,v)|0);break}else{break}}}while(0);do{if((c[17089]&3|0)==0){d=c[10030]|0;aI(158424,33,1,d|0)}else{d=e6(224640)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==1){f=c[10030]|0;g=c[d+24>>2]|0;cf(f|0,158984,(v=i,i=i+16|0,c[v>>2]=158520,c[v+8>>2]=g,v)|0);break}else if((e|0)==2){j=+h[d+24>>3];cf(c[10030]|0,158936,(v=i,i=i+16|0,c[v>>2]=158520,h[v+8>>3]=j,v)|0);break}else{break}}}while(0);d=e6(224432)|0;if((d|0)==0){break}e=c[d+16>>2]|0;if((e|0)==2){g=c[10030]|0;j=+h[d+24>>3];cf(g|0,158936,(v=i,i=i+16|0,c[v>>2]=158480,h[v+8>>3]=j,v)|0);break}else if((e|0)==1){e=c[d+24>>2]|0;cf(c[10030]|0,158984,(v=i,i=i+16|0,c[v>>2]=158480,c[v+8>>2]=e,v)|0);break}else{break}}}while(0);cf(c[10030]|0,158392,(v=i,i=i+8|0,c[v>>2]=a[66164]&1,v)|0);cf(c[10030]|0,158360,(v=i,i=i+8|0,c[v>>2]=a[65476]&1,v)|0);if((a[30528]&1)!=0){e=c[10030]|0;d=a[69604]&1;cf(e|0,158328,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}aI(158192,75,1,c[10030]|0);aI(158112,76,1,c[10030]|0);d=c[10030]|0;if((c[16546]|0)==1){j=+h[8255];cf(d|0,158064,(v=i,i=i+8|0,h[v>>3]=j,v)|0);e=c[10030]|0;j=+h[8256];cf(e|0,157952,(v=i,i=i+8|0,h[v>>3]=j,v)|0);e=c[10030]|0;g=c[8510]|0;do{if((g|0)==0){f=c[8506]|0;if((f|0)==4){k=223560;break}k=(f|0)==5?223536:223520}else{k=g}}while(0);cf(e|0,157896,(v=i,i=i+8|0,c[v>>2]=k,v)|0)}else{aI(157856,30,1,d|0)}aI(86144,2,1,c[10030]|0);if(a[243104]|0){l=c[10030]|0;m=aD(l|0)|0;i=b;return}d=c[60778]|0;cf(c[10030]|0,157664,(v=i,i=i+8|0,c[v>>2]=(d|0)!=0?d:179864,v)|0);if(a[243312]|0){d=c[10030]|0;k=c[60778]|0;e=(k|0)!=0?k:179864;cf(d|0,156600,(v=i,i=i+40|0,c[v>>2]=e,c[v+8>>2]=e,c[v+16>>2]=e,c[v+24>>2]=e,c[v+32>>2]=e,v)|0);e=c[13858]|0;d=e+5|0;if((d-((d|0)%6&-1)|0)>=1){d=1;k=e;do{e=(d|0)%6&-1;if((e|0)==1){g=c[10030]|0;aI(156592,6,1,g|0);n=c[13858]|0}else{n=k}g=c[10030]|0;if((d|0)>(n|0)){aI(156256,28,1,g|0)}else{cf(g|0,156408,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0)}if((e|0)==0){e=c[10030]|0;aI(156248,7,1,e|0)}d=d+1|0;k=c[13858]|0;e=k+5|0;}while((d|0)<=(e-((e|0)%6&-1)|0))}d=c[10030]|0;aI(156152,72,1,d|0);d=c[10030]|0;aI(155832,262,1,d|0);if((c[17261]&3|0)!=0){d=c[10030]|0;aI(155680,105,1,d|0)}if((c[17089]&3|0)!=0){d=c[10030]|0;aI(155568,105,1,d|0)}d=c[10030]|0;aI(155536,28,1,d|0);d=c[10030]|0;aI(155504,10,1,d|0)}d=c[3524]|0;k=~~(+((c[d+12>>2]|0)>>>0>>>0)/10.0);cf(c[10030]|0,155296,(v=i,i=i+16|0,c[v>>2]=~~(+((c[d+8>>2]|0)>>>0>>>0)/10.0),c[v+8>>2]=k,v)|0);if(a[243312]|0){k=c[10030]|0;aI(155248,19,1,k|0)}aI(155168,24,1,c[10030]|0);l=c[10030]|0;m=aD(l|0)|0;i=b;return}function mK(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0;b=i;c[13864]=-5;c[13840]=0;a[55472]=0;c[13842]=-1;a[55390]=0;a[55408]=0;uD(55372,163848,17);c[13858]=0;d=c[60778]|0;if((d|0)==0){e=bP(221464)|0;c[60778]=e;f=e}else{f=d}d=uA(f|0)|0;do{if((a[f]|0)!=0){if((a[f+(d-1|0)|0]|0)==47){break}e=db(f,d+2|0,221440)|0;c[60778]=e;g=e+(uA(e|0)|0)|0;w=47;a[g]=w&255;w=w>>8;a[g+1|0]=w&255}}while(0);d=c[10030]|0;if(a[243104]|0){f=c[60826]|0;cf(d|0,161488,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=f,v)|0);f=c[10030]|0;g=c[60826]|0;e=(a[30528]&1)!=0?225e3:224992;j=c[3524]|0;k=~~(+((c[j+8>>2]|0)>>>0>>>0)/10.0);l=~~(+((c[j+12>>2]|0)>>>0>>>0)/10.0);cf(f|0,160752,(v=i,i=i+64|0,c[v>>2]=g,c[v+8>>2]=g,c[v+16>>2]=g,c[v+24>>2]=g,c[v+32>>2]=g,c[v+40>>2]=e,c[v+48>>2]=k,c[v+56>>2]=l,v)|0)}else{l=c[60774]|0;k=(l|0)!=0?l:162656;cf(d|0,162680,(v=i,i=i+8|0,c[v>>2]=k,v)|0);k=c[11252]|0;if((k|0)==15|(k|0)==0){d=c[10030]|0;aI(162584,68,1,d|0);m=c[11252]|0}else{m=k}k=c[10030]|0;d=c[60778]|0;l=(m|0)==15?162360:162344;cf(k|0,162424,(v=i,i=i+24|0,c[v>>2]=d,c[v+8>>2]=l,c[v+16>>2]=d,v)|0);if(a[55512]|0){d=c[10030]|0;l=c[60778]|0;cf(d|0,162272,(v=i,i=i+8|0,c[v>>2]=l,v)|0)}l=c[10030]|0;if(a[243312]|0){d=c[60778]|0;cf(l|0,162144,(v=i,i=i+8|0,c[v>>2]=d,v)|0);d=c[10030]|0;k=c[60778]|0;cf(d|0,162048,(v=i,i=i+8|0,c[v>>2]=k,v)|0)}else{aI(161928,70,1,l|0)}l=c[10030]|0;aI(161664,257,1,l|0)}cf(c[10030]|0,160720,(v=i,i=i+16|0,c[v>>2]=40152,c[v+8>>2]=40160,v)|0);aI(159864,807,1,c[10030]|0);if(a[55512]|0){l=c[10030]|0;k=~~(+h[6938]*200.0);cf(l|0,159832,(v=i,i=i+8|0,c[v>>2]=k,v)|0)}k=a[55440]|0;cf(c[10030]|0,159776,(v=i,i=i+16|0,c[v>>2]=k?131936:137232,c[v+8>>2]=k?131936:75184,v)|0);if((a[243424]|0)==0){n=c[10030]|0;o=+h[6931];p=cf(n|0,159584,(v=i,i=i+8|0,h[v>>3]=o,v)|0)|0;i=b;return}k=c[3524]|0;l=~~(+((c[k+8>>2]|0)>>>0>>>0)/10.0);d=~~(+((c[k+12>>2]|0)>>>0>>>0)/10.0);cf(c[10030]|0,159696,(v=i,i=i+24|0,c[v>>2]=243424,c[v+8>>2]=l,c[v+16>>2]=d,v)|0);n=c[10030]|0;o=+h[6931];p=cf(n|0,159584,(v=i,i=i+8|0,h[v>>3]=o,v)|0)|0;i=b;return}function mL(b,d){b=b|0;d=d|0;var e=0,f=0;e=i;f=a[55472]|0;if(f&(c[13838]|0)==(b|0)&(c[13834]|0)==(d|0)){i=e;return}if(!f){f=c[10030]|0;aI(162736,17,1,f|0);a[55472]=1;a[72328]=0}f=(c[13832]|0)-d|0;cf(c[10030]|0,162768,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=f,v)|0);c[13838]=b;c[13834]=d;i=e;return}function mM(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;f=c[13838]|0;g=c[13834]|0;if((f|0)==(b|0)&(g|0)==(d|0)){i=e;return}if(!(a[55472]|0)){h=c[10030]|0;aI(162736,17,1,h|0);a[55472]=1;a[72328]=0;h=c[10030]|0;j=(c[13832]|0)-g|0;cf(h|0,162768,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=j,v)|0);c[13838]=f;c[13834]=g}g=(c[13832]|0)-d|0;cf(c[10030]|0,162808,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=g,v)|0);c[13838]=b;c[13834]=d;i=e;return}function mN(b){b=b|0;var d=0,e=0,f=0;d=i;if((c[13864]|0)==(b|0)){i=d;return}c[13864]=b;if(a[55472]|0){e=c[10030]|0;aI(165112,14,1,e|0);if(!(a[72328]|0)){e=c[10030]|0;aI(165528,17,1,e|0)}a[55472]=0;a[72328]=1}if((b|0)>13){f=(b|0)%14&-1}else{f=b}b=(f|0)>-3;e=b?f:-3;if(b|(a[243424]|0)==0){b=c[243320+(e+3<<2)>>2]|0;uB(55372,b|0)}else{uB(55372,243424)}if((aY(55372,55390)|0)!=0){b=c[10030]|0;cf(b|0,164320,(v=i,i=i+8|0,c[v>>2]=55372,v)|0);uB(55390,55372)}if(!(a[55512]|0)){i=d;return}if((e-1|0)>>>0<4){b=c[10030]|0;f=e+1|0;cf(b|0,162968,(v=i,i=i+8|0,c[v>>2]=f,v)|0);i=d;return}f=c[10030]|0;if((e|0)==-1){aI(162920,26,1,f|0);i=d;return}else{aI(164808,19,1,f|0);i=d;return}}function mO(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0.0,m=0;f=i;if((e|0)==0){i=f;return}if((a[e]|0)==0){i=f;return}if(a[55472]|0){g=c[10030]|0;aI(165112,14,1,g|0);if(!(a[72328]|0)){g=c[10030]|0;aI(165528,17,1,g|0)}a[55472]=0;a[72328]=1}g=c[13840]|0;j=c[10030]|0;k=~~(+(c[13832]|0)+50.0- +(d>>>0>>>0));l=+h[6936]*+h[6935];d=c[13866]|0;if((g|0)==0){cf(j|0,164024,(v=i,i=i+32|0,c[v>>2]=b,c[v+8>>2]=k,h[v+16>>3]=l,c[v+24>>2]=d,v)|0)}else{cf(j|0,164152,(v=i,i=i+40|0,c[v>>2]=b,c[v+8>>2]=k,c[v+16>>2]=g,h[v+24>>3]=l,c[v+32>>2]=d,v)|0)}d=e;g=a[e]|0;do{if((g<<24>>24|0)==34|(g<<24>>24|0)==92){e=c[10030]|0;aF(92,e|0);m=a[d]|0}else{m=g}d=d+1|0;aF(m<<24>>24|0,c[10030]|0);g=a[d]|0;}while(g<<24>>24!=0);aI(163984,4,1,c[10030]|0);i=f;return}function mP(a){a=a|0;if((a|0)==2){c[13866]=164216}else if((a|0)==1){c[13866]=173184}else{c[13866]=179864}return 1}function mQ(a){a=a|0;return 0}function mR(a){a=+a;h[30390]=a<0.0?1.0:a;return}function mS(b,d,e,f,g,i){b=b|0;d=+d;e=+e;f=f|0;g=g|0;i=i|0;if((i|0)==3){c[59636]=c[13838];c[59634]=c[13834];return}else if((i|0)==4){c[13838]=c[59636];c[13834]=c[59634];return}else{if(a[238512]|0){return}a[238512]=1;c[11232]=43856;h[29815]=d;h[29816]=e*10.0;a[238496]=g&1;a[238480]=f&1;a[238504]=(i|0)!=0&1;return}}function mT(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0.0,j=0;f=i;g=+h[30390]*.6*100.0;j=(e|0)%9&-1;if(g<=0.0&(j|0)>-1){i=f;return}if(a[55472]|0){e=c[10030]|0;aI(165112,14,1,e|0);if(!(a[72328]|0)){e=c[10030]|0;aI(165528,17,1,e|0)}a[55472]=0;a[72328]=1}do{if((j|0)==4|(j|0)==6|(j|0)==8){if((aY(55408,55372)|0)==0){break}e=c[10030]|0;cf(e|0,164776,(v=i,i=i+8|0,c[v>>2]=55372,v)|0);uB(55408,55372)}else if(!((j|0)==0|(j|0)==1|(j|0)==2|(j|0)==3|(j|0)==5|(j|0)==7)){e=(c[13832]|0)-d|0;cf(c[10030]|0,164256,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=e,v)|0);i=f;return}}while(0);e=(c[13832]|0)-d|0;cf(c[10030]|0,164232,(v=i,i=i+32|0,c[v>>2]=j,c[v+8>>2]=b,c[v+16>>2]=e,h[v+24>>3]=g,v)|0);i=f;return}function mU(b){b=b|0;var d=0,e=0,f=0,g=0.0,j=0,k=0.0;d=i;do{if((b|0)==0){e=12072}else{if((a[b]|0)==0){e=12072;break}f=bk(b|0,148464)|0;if((a[b+f|0]|0)!=44){g=+h[6936];break}j=b+(f+1|0)|0;ca(j|0,148448,(v=i,i=i+8|0,c[v>>2]=55488,v)|0);k=+h[6936];if(k>0.0){g=k;break}k=+h[30427];h[6936]=k;g=k}}while(0);if((e|0)==12072){k=+h[30427];h[6936]=k;g=k}c[(c[3524]|0)+16>>2]=~~(g*+h[6935]*10.0);c[(c[3524]|0)+20>>2]=~~(+h[6936]*+h[6935]*.8*10.0);i=d;return 1}function mV(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0;h=i;tW(b);if(a[55512]|0){b=c[10030]|0;aI(164808,19,1,b|0);c[13864]=-5}if((aY(55408,243392)|0)!=0){b=c[10030]|0;cf(b|0,164776,(v=i,i=i+8|0,c[v>>2]=243392,v)|0);uB(55408,243392)}b=(c[13832]|0)-(g+e|0)|0;cf(c[10030]|0,164272,(v=i,i=i+32|0,c[v>>2]=d,c[v+8>>2]=b,c[v+16>>2]=f,c[v+24>>2]=g,v)|0);i=h;return}function mW(b){b=+b;var d=0,e=0;d=i;if(a[55472]|0){e=c[10030]|0;aI(165112,14,1,e|0);if(!(a[72328]|0)){e=c[10030]|0;aI(165528,17,1,e|0)}a[55472]=0;a[72328]=1}if(+(c[13842]|0)==b){i=d;return}cf(c[10030]|0,164296,(v=i,i=i+8|0,h[v>>3]=+h[6931]*b,v)|0);c[13842]=~~b;i=d;return}function mX(b){b=b|0;var e=0,f=0,g=0;e=i;i=i+8|0;f=e|0;g=c[b>>2]|0;if((g|0)==5){fr(+h[b+8>>3],f)}else if((g|0)==1){mN(c[b+4>>2]|0);i=e;return}else if((g|0)==3){g=c[b+4>>2]|0;a[f|0]=g>>>16&255;a[f+1|0]=g>>>8&255;a[f+2|0]=g&255}else{i=e;return}if(a[55472]|0){g=c[10030]|0;aI(165112,14,1,g|0);if(!(a[72328]|0)){g=c[10030]|0;aI(165528,17,1,g|0)}a[55472]=0;a[72328]=1}g=d[f+1|0]|0;b=d[f+2|0]|0;be(55372,164384,(v=i,i=i+24|0,c[v>>2]=d[f|0]|0,c[v+8>>2]=g,c[v+16>>2]=b,v)|0);if((aY(55372,55390)|0)!=0){b=c[10030]|0;cf(b|0,164320,(v=i,i=i+8|0,c[v>>2]=55372,v)|0);uB(55390,55372)}c[13864]=-5;i=e;return}function mY(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;if(a[55472]|0){f=c[10030]|0;aI(165112,14,1,f|0);if(!(a[72328]|0)){f=c[10030]|0;aI(165528,17,1,f|0)}a[55472]=0;a[72328]=1}if(a[55512]|0){f=c[10030]|0;aI(164808,19,1,f|0);c[13864]=-5}f=d+8|0;g=c[f>>2]|0;do{if(!((g|0)==1601|(g|0)==3)){tW(g);if((aY(55408,243392)|0)==0){break}h=c[10030]|0;cf(h|0,164776,(v=i,i=i+8|0,c[v>>2]=243392,v)|0);uB(55408,243392)}}while(0);g=(c[13832]|0)-(c[d+4>>2]|0)|0;cf(c[10030]|0,164760,(v=i,i=i+16|0,c[v>>2]=c[d>>2],c[v+8>>2]=g,v)|0);if((b|0)>1){g=1;do{h=(c[13832]|0)-(c[d+(g*12&-1)+4>>2]|0)|0;cf(c[10030]|0,164744,(v=i,i=i+16|0,c[v>>2]=c[d+(g*12&-1)>>2],c[v+8>>2]=h,v)|0);g=g+1|0;}while((g|0)<(b|0))}b=c[f>>2]|0;if((b|0)==1601|(b|0)==3){b=c[10030]|0;aI(164680,8,1,b|0);i=e;return}else{b=c[10030]|0;aI(164736,7,1,b|0);i=e;return}}function mZ(){var b=0.0,d=0,e=0,f=0,g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0.0,u=0.0,v=0.0;if(!(a[238512]|0)){return}a[238512]=0;a[c[11232]|0]=0;b=+h[6936];d=c[13838]|0;e=c[13834]|0;f=43856+(uA(43856)|0)|0;g=a[43856]|0;L16425:do{if(g<<24>>24==0){i=0}else{j=(c[11252]|0)==15;k=0;l=43856;m=g;while(1){n=m<<24>>24;if((n&128|0)==0){do{if((aZ(165104,n|0,5)|0)==0){if((aZ(165088,n|0,9)|0)!=0){o=k+10|0;break}if((aZ(165080,n|0,4)|0)!=0){o=k+12|0;break}if((aZ(165072,n|0,8)|0)!=0){o=k+14|0;break}if((aZ(165032,n|0,15)|0)!=0){o=k+16|0;break}if((aZ(165008,n|0,18)|0)!=0){o=k+18|0;break}if((aZ(164944,n|0,10)|0)!=0){o=k+24|0;break}if((aZ(164840,n|0,2)|0)==0){o=k+20|0;break}else{o=k+30|0;break}}else{o=k+8|0}}while(0);p=l+1|0;q=o}else{do{if(j){if((n&224|0)==192){r=l+2|0;break}if((n&240|0)==224){r=l+3|0;break}else{r=l+4|0;break}}else{r=l+1|0}}while(0);n=k+18|0;if(r>>>0>f>>>0){i=n;break L16425}else{p=r;q=n}}n=a[p]|0;if(n<<24>>24==0){i=q;break}else{k=q;l=p;m=n}}}}while(0);s=+h[29815];t=s*+(i|0)*10.0/25.0;h[6936]=s;s=+(c[13840]|0)*1.5707963267948966/90.0;u=+T(+s);v=+h[29816];i=~~(+(d|0)+u*v);d=~~(+(e|0)+v*+S(+s));do{if((a[238496]&1)!=0){if(a[238488]|0){break}mO(i,d,43856)}}while(0);do{if((a[238504]&1)==0){if((a[238480]&1)==0){break}s=+(c[13840]|0)*1.5707963267948966/90.0;v=t*+S(+s);c[13838]=~~(v+ +(c[13838]|0));v=t*+T(+s);c[13834]=~~(+(c[13834]|0)-v)}else{v=+(c[13840]|0)*1.5707963267948966/90.0;s=t*+S(+v)*.5;c[13838]=~~(+(c[13838]|0)+s);s=t*+T(+v)*.5;c[13834]=~~(+(c[13834]|0)-s)}}while(0);h[6936]=b;return}function m_(b){b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;e=c[60826]|0;f=(e|0)!=0?e:165512;if((b|0)==0|(b|0)==10){c[13858]=0;i=d;return}else if((b|0)==6){e=(c[13858]|0)+1|0;c[13858]=e;if(a[55472]|0){g=c[10030]|0;aI(165112,14,1,g|0);if(!(a[72328]|0)){g=c[10030]|0;aI(165528,17,1,g|0)}a[55472]=0;a[72328]=1;h=c[13858]|0}else{h=e}cf(c[10030]|0,165424,(v=i,i=i+32|0,c[v>>2]=f,c[v+8>>2]=h,c[v+16>>2]=f,c[v+24>>2]=h,v)|0);i=d;return}else if((b|0)==7){if(a[55472]|0){h=c[10030]|0;aI(165112,14,1,h|0);if(!(a[72328]|0)){h=c[10030]|0;aI(165528,17,1,h|0)}a[55472]=0;a[72328]=1}h=c[13858]|0;cf(c[10030]|0,165376,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=h,v)|0);c[13864]=-5;c[13842]=-1;a[55390]=0;a[55408]=0;i=d;return}else if((b|0)==3){aI(165224,94,1,c[10030]|0);i=d;return}else if((b|0)==4){aI(165160,43,1,c[10030]|0);i=d;return}else{i=d;return}}function m$(b){b=b|0;if((b|0)!=1){return}aI(165528,17,1,c[10030]|0);a[72328]=1;return}function m0(){var b=0,d=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0.0,Y=0.0,Z=0,_=0,$=0,ab=0.0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0.0,ao=0;b=i;i=i+120|0;d=b|0;f=b+24|0;g=b+48|0;j=b+72|0;k=b+96|0;l=b+104|0;m=b+112|0;if((c[13898]|0)==2){n=2}else{tV();n=c[13898]|0}o=c[8272]|0;L16513:do{if((n|0)<(o|0)){p=d|0;q=d+8|0;r=f|0;s=f+8|0;t=j|0;u=j+8|0;w=g|0;x=g+8|0;y=n;z=o;L16515:while(1){A=c[1054]|0;B=a[A+(y*40&-1)|0]|0;C=(B&1)==0;L16517:do{if(!C){D=c[A+(y*40&-1)+36>>2]|0;E=A+(y*40&-1)+32|0;F=c[10036]|0;G=0;while(1){if((G|0)>=(D|0)){break}if((a[F+((c[E>>2]|0)+G|0)|0]|0)==(a[G+103664|0]|0)){G=G+1|0}else{break L16517}}if((G|0)==1){break L16513}}}while(0);E=c[60494]|0;L16524:do{if((E|0)==0){H=241976}else{F=c[A+(y*40&-1)+36>>2]|0;D=c[A+(y*40&-1)+32>>2]|0;I=(F|0)>0;J=c[10036]|0;K=241976;L=E;while(1){L16528:do{if(!C){if(I){M=0;N=0;O=D;while(1){P=a[L+M|0]|0;if(P<<24>>24==(a[J+(M+O|0)|0]|0)){Q=O;R=N}else{if(P<<24>>24!=36){break L16528}Q=O-1|0;R=1}S=M+1|0;if((S|0)<(R+F|0)){M=S;N=R;O=Q}else{break}}if((R|0)==0){T=S}else{H=K;break L16524}}else{T=0}O=a[L+T|0]|0;if((O<<24>>24|0)==36|(O<<24>>24|0)==0){H=K;break L16524}}}while(0);O=K+8|0;N=c[O>>2]|0;if((N|0)==0){H=O;break}else{K=O;L=N}}}}while(0);E=c[H+4>>2]|0;L16541:do{if((E|0)==12){c[13898]=y+1;L=hK()|0;c[13828]=L;if((c[13554]|0)==0){c[13554]=1;K=ut(16)|0;if((K|0)==0){gk();F=ut(16)|0;if((F|0)==0){U=12256;break L16515}else{V=F}}else{V=K}K=V;c[13550]=K;c[K>>2]=0;W=c[13828]|0}else{W=L}c[(c[13550]|0)+4>>2]=W>>>16&255;c[(c[13550]|0)+8>>2]=(c[13828]|0)>>>8&255;c[(c[13550]|0)+12>>2]=c[13828]&255}else if((E|0)==7){a[54576]=0;c[13898]=y+1}else if((E|0)==8){a[54576]=1;c[13898]=y+1}else if((E|0)==1){a[54400]=0;c[13898]=y+1}else if((E|0)==4){a[54472]=0;L=(c[3524]|0)+96|0;c[L>>2]=c[L>>2]&-1025;c[13898]=(c[13898]|0)+1}else if((E|0)==0){a[54400]=1;c[13898]=y+1}else if((E|0)==5){a[54336]=0;c[13898]=y+1}else if((E|0)==9){L=y+1|0;c[13898]=L;if((L|0)>=(z|0)){break}L16557:do{if((a[A+(L*40&-1)|0]&1)!=0){K=c[A+(L*40&-1)+36>>2]|0;F=A+(L*40&-1)+32|0;J=c[10036]|0;D=0;while(1){if((D|0)>=(K|0)){break}if((a[J+((c[F>>2]|0)+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{break L16557}}if((D|0)==1){break L16541}}}while(0);L=is(j)|0;F=c[L>>2]|0;if((F|0)==2){X=+h[L+8>>3]}else if((F|0)==3){X=+uz(c[L+8>>2]|0,0)}else if((F|0)==1){X=+(c[L+8>>2]|0)}else{U=12233;break L16515}if((c[t>>2]|0)==3){uu(c[u>>2]|0);c[t>>2]=1}L=~~X;c[13622]=L;if(!((L|0)==0|L>>>0>1e4)){break}uh(c[13898]|0,166032,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[13622]=1}else if((E|0)==2){tV();c[13898]=(c[13898]|0)+1}else if((E|0)==10){L=y+1|0;c[13898]=L;if((L|0)>=(z|0)){break}L16666:do{if((a[A+(L*40&-1)|0]&1)!=0){F=c[A+(L*40&-1)+36>>2]|0;J=A+(L*40&-1)+32|0;K=c[10036]|0;I=0;while(1){if((I|0)>=(F|0)){break}if((a[K+((c[J>>2]|0)+I|0)|0]|0)==(a[I+103664|0]|0)){I=I+1|0}else{break L16666}}if((I|0)==1){break L16541}}}while(0);L=is(g)|0;J=c[L>>2]|0;if((J|0)==2){Y=+h[L+8>>3]}else if((J|0)==1){Y=+(c[L+8>>2]|0)}else if((J|0)==3){Y=+uz(c[L+8>>2]|0,0)}else{U=12248;break L16515}if((c[w>>2]|0)==3){uu(c[x>>2]|0);c[w>>2]=1}L=~~Y;c[13604]=L;if(L>>>0<=1e4){break}uh(c[13898]|0,166008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);c[13604]=432}else if((E|0)==6){a[54336]=1;c[13898]=y+1}else if((E|0)==11){a[54424]=1;c[13898]=y+1}else if((E|0)==3){a[54472]=1;L=(c[3524]|0)+96|0;c[L>>2]=c[L>>2]|1024;c[13898]=(c[13898]|0)+1}else{L=c[10036]|0;J=c[A+(y*40&-1)+32>>2]|0;K=L+J|0;if((a[K]|0)==120){if((ca(K|0,165992,(v=i,i=i+24|0,c[v>>2]=k,c[v+8>>2]=l,c[v+16>>2]=m,v)|0)|0)!=3){U=12261;break L16515}K=c[13554]|0;F=c[13552]|0;if((K|0)<(F|0)){Z=K}else{K=(F<<1)+4|0;c[13552]=K;F=db(c[13550]|0,K*12&-1|4,167368)|0;c[13550]=F;c[F>>2]=0;Z=c[13554]|0}c[(c[13550]|0)+((Z*3&-1)+1<<2)>>2]=e[k>>1]|0;c[(c[13550]|0)+(((c[13554]|0)*3&-1)+2<<2)>>2]=e[l>>1]|0;c[(c[13550]|0)+(((c[13554]|0)*3&-1)+3<<2)>>2]=e[m>>1]|0;c[13554]=(c[13554]|0)+1;c[13898]=(c[13898]|0)+1;break}L16582:do{if((z|0)<=(y|0)|C){_=y;$=B}else{F=c[A+(y*40&-1)+36>>2]|0;K=0;while(1){if((K|0)>=(F|0)){break}if((a[L+(J+K|0)|0]|0)==(a[K+124352|0]|0)){K=K+1|0}else{_=y;$=B;break L16582}}if((K|0)!=4){_=y;$=B;break}F=y+1|0;c[13898]=F;_=F;$=a[A+(F*40&-1)|0]|0}}while(0);J=($&1)==0;if(J){U=12273}else{F=a[L+(c[A+(_*40&-1)+32>>2]|0)|0]|0;if(!((F<<24>>24|0)==39|(F<<24>>24|0)==34)){U=12273}}L16592:do{if((U|0)==12273){U=0;F=c[10810]|0;L16594:do{if((F|0)!=0){I=A+(_*40&-1)+36|0;D=A+(_*40&-1)+32|0;G=F;L16596:while(1){N=c[G+4>>2]|0;L16598:do{if(!((z|0)<=(_|0)|J)){O=c[I>>2]|0;M=0;while(1){if((M|0)>=(O|0)){break}if((a[L+((c[D>>2]|0)+M|0)|0]|0)==(a[N+M|0]|0)){M=M+1|0}else{break L16598}}if((a[N+M|0]|0)==0){break L16596}}}while(0);N=c[G>>2]|0;if((N|0)==0){break L16594}else{G=N}}if((a[G+8|0]&1)!=0){break}if((c[G+16>>2]|0)==3){break L16592}}}while(0);F=is(d)|0;K=c[F>>2]|0;if((K|0)==2){ab=+h[F+8>>3]}else if((K|0)==3){ab=+uz(c[F+8>>2]|0,0)}else if((K|0)==1){ab=+(c[F+8>>2]|0)}else{U=12321;break L16515}if((c[p>>2]|0)==3){uu(c[q>>2]|0);c[p>>2]=1}c[13628]=~~ab;break L16541}}while(0);L16617:do{if((_|0)<(z|0)){L16619:do{if(!J){F=c[A+(_*40&-1)+36>>2]|0;K=A+(_*40&-1)+32|0;D=0;while(1){if((D|0)>=(F|0)){break}if((a[L+((c[K>>2]|0)+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{break L16619}}if((D|0)==1){ac=0;break L16617}}}while(0);a[14176]=1;is(f);a[14176]=0;if((c[r>>2]|0)==3){ac=c[s>>2]|0;break}else{c[13898]=_;ac=0;break}}else{ac=0}}while(0);L=a8(ac|0,44)|0;do{if((L|0)!=0){if((ca(L+1|0,21e4,(v=i,i=i+8|0,c[v>>2]=54512,v)|0)|0)!=1){break}a[L]=0}}while(0);L16634:do{if((a[ac]|0)==0){L=uA(54528)|0;J=c[13630]|0;K=c[J>>2]|0;if((K|0)==0){ad=J;U=12305;break}else{ae=0;af=K}while(1){if((uA(af|0)|0)==(L|0)){if((uJ(54528,af|0,L|0)|0)==0){ag=ae;ah=J;U=12304;break L16634}}K=ae+1|0;F=c[J+(K<<4)>>2]|0;if((F|0)==0){ad=J;U=12305;break}else{ae=K;af=F}}}else{J=uA(ac|0)|0;L=c[13630]|0;F=c[L>>2]|0;if((F|0)==0){ad=L;U=12305;break}else{ai=0;aj=F}while(1){if((uA(aj|0)|0)==(J|0)){if((uJ(ac|0,aj|0,J|0)|0)==0){ag=ai;ah=L;U=12304;break L16634}}F=ai+1|0;K=c[L+(F<<4)>>2]|0;if((K|0)==0){ad=L;U=12305;break}else{ai=F;aj=K}}}}while(0);do{if((U|0)==12304){U=0;if((ag|0)==-1){ad=ah;U=12305;break}uu(ac);ak=ag;al=c[13630]|0}}while(0);if((U|0)==12305){U=0;L=0;while(1){if((c[ad+(L<<4)>>2]|0)==0){break}else{L=L+1|0}}J=(L<<4)+32|0;K=ut(J)|0;if((K|0)==0){gk();F=ut(J)|0;if((F|0)==0){U=12309;break L16515}else{am=F}}else{am=K}K=am;c[am>>2]=ac;h[am+8>>3]=1.0;if((L|0)>=0){F=c[13630]|0;J=0;while(1){G=J+1|0;I=K+(G<<4)|0;N=F+(J<<4)|0;c[I>>2]=c[N>>2];c[I+4>>2]=c[N+4>>2];c[I+8>>2]=c[N+8>>2];c[I+12>>2]=c[N+12>>2];if((G|0)>(L|0)){break}else{J=G}}}c[13630]=K;ak=0;al=K}uF(54528,c[al+(ak<<4)>>2]|0,32)}}while(0);y=c[13898]|0;z=c[8272]|0;if((y|0)>=(z|0)){break L16513}}if((U|0)==12321){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((U|0)==12233){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((U|0)==12256){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=167368,v)|0)}else if((U|0)==12248){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((U|0)==12261){uf(c[13898]|0,165952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((U|0)==12309){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=166248,v)|0)}}}while(0);U=(c[3524]|0)+8|0;if(a[54400]|0){c[U>>2]=23478;c[(c[3524]|0)+12>>2]=32585}else{c[U>>2]=32585;c[(c[3524]|0)+12>>2]=23478}U=uA(54528)|0;ak=c[13630]|0;al=c[ak>>2]|0;L16698:do{if((al|0)==0){an=1.0}else{am=0;ac=al;while(1){if((uA(ac|0)|0)==(U|0)){if((uJ(54528,ac|0,U|0)|0)==0){break}}ad=am+1|0;ag=c[ak+(ad<<4)>>2]|0;if((ag|0)==0){an=1.0;break L16698}else{am=ad;ac=ag}}an=+h[ak+(am<<4)+8>>3]}}while(0);ak=c[3524]|0;c[ak+16>>2]=aa((((c[ak+8>>2]|0)+182|0)>>>0)/((c[13604]|0)>>>0)>>>0,c[13628]|0);ak=c[3524]|0;c[ak+20>>2]=~~(an*+(aa((((c[ak+8>>2]|0)+182|0)>>>0)/((c[13604]|0)>>>0)>>>0,c[13628]|0)>>>0>>>0)*.527);ak=c[13628]|0;be(242752,165944,(v=i,i=i+16|0,c[v>>2]=54528,c[v+8>>2]=ak,v)|0);ak=a[54472]|0?116408:116440;U=a[54336]|0?185368:185408;al=a[54576]|0?131504:131584;ac=a[54424]|0?165816:179864;ag=c[13604]|0;ad=c[13622]|0;ah=c[13628]|0;be(13048,165832,(v=i,i=i+72|0,c[v>>2]=a[54400]|0?78720:78704,c[v+8>>2]=ak,c[v+16>>2]=U,c[v+24>>2]=al,c[v+32>>2]=ac,c[v+40>>2]=ag,c[v+48>>2]=ad,c[v+56>>2]=54528,c[v+64>>2]=ah,v)|0);ah=c[13554]|0;do{if((ah|0)>0){ad=0;ag=ah;while(1){ac=uA(13048)|0;if((ac+9|0)>>>0>=1024){ao=ag;break}al=ad*3&-1;U=c[13550]|0;ak=c[U+(al+2<<2)>>2]|0;aj=c[U+(al+3<<2)>>2]|0;be(ac+13048|0,165744,(v=i,i=i+24|0,c[v>>2]=c[U+(al+1<<2)>>2],c[v+8>>2]=ak,c[v+16>>2]=aj,v)|0);aj=ad+1|0;ak=c[13554]|0;if((aj|0)<(ak|0)){ad=aj;ag=ak}else{ao=ak;break}}if((ao|0)<96){break}i=b;return}}while(0);ao=db(c[13550]|0,1156,167368)|0;c[13550]=ao;c[ao>>2]=0;ao=c[13554]|0;if((ao|0)<96){ah=ao;ag=ao*3&-1;do{c[(c[13550]|0)+(ag+1<<2)>>2]=(c[(c[7642]|0)+(ah<<3)+4>>2]|0)>>>16&255;c[(c[13550]|0)+(ag+2<<2)>>2]=(c[(c[7642]|0)+(ah<<3)+4>>2]|0)>>>8&255;ag=ag+3|0;c[(c[13550]|0)+(ag<<2)>>2]=c[(c[7642]|0)+(ah<<3)+4>>2]&255;ah=ah+1|0;}while((ah|0)<96)}c[13554]=96;i=b;return}function m1(){c[13596]=0;c[13598]=0;uu(c[13602]|0);return}function m2(){var a=0,b=0,d=0,e=0;a=i;c[13596]=0;c[13598]=0;c[13626]=0;h[6804]=0.0;c[13616]=1;c[13617]=1;b=ut(65528)|0;if((b|0)!=0){d=b;e=d;c[13602]=e;i=a;return}gk();b=ut(65528)|0;if((b|0)==0){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=166104,v)|0)}else{d=b;e=d;c[13602]=e;i=a;return}}function m3(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;b=i;i=i+24|0;d=b|0;e=b+8|0;f=b+16|0;g=c[13656]|0;if((g|0)!=0){h=c[13602]|0;tY(4,1,g<<1);L16730:do{if((g|0)>0){j=d;k=d+1|0;l=0;while(1){m=c[h+(l<<2)>>2]|0;if((m|0)<=-32769){n=12355;break}if((m|0)>=32768){n=12357;break}a[j]=m>>>8&255;a[k]=m&255;aI(j|0,1,2,c[10030]|0);l=l+1|0;if((l|0)>=(g|0)){break L16730}}if((n|0)==12357){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((n|0)==12355){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}n=f;a[n]=0;a[f+1|0]=-96;aI(n|0,1,2,c[10030]|0);n=e;a[n]=0;a[e+1|0]=64;aI(n|0,1,2,c[10030]|0);i=b;return}function m4(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0.0;b=i;i=i+832|0;d=b|0;e=b+8|0;f=b+16|0;g=b+24|0;j=b+32|0;k=b+40|0;l=b+48|0;m=b+56|0;n=b+64|0;o=b+72|0;p=b+80|0;q=b+88|0;r=b+96|0;s=b+104|0;t=b+112|0;u=b+120|0;w=b+128|0;x=b+136|0;y=b+144|0;z=b+152|0;A=b+160|0;B=b+168|0;C=b+176|0;D=b+184|0;E=b+192|0;F=b+200|0;G=b+208|0;H=b+216|0;I=b+224|0;J=b+232|0;K=b+240|0;L=b+248|0;M=b+256|0;N=b+320|0;O=b+328|0;P=b+336|0;Q=b+344|0;R=b+352|0;S=b+360|0;T=b+368|0;U=b+376|0;V=b+384|0;W=b+392|0;X=b+400|0;Y=b+408|0;Z=b+416|0;_=b+424|0;$=b+432|0;ab=b+440|0;ac=b+448|0;ad=b+456|0;ae=b+464|0;af=b+472|0;ag=b+480|0;ah=b+488|0;ai=b+496|0;aj=b+504|0;ak=b+512|0;al=b+520|0;am=b+784|0;an=c[3524]|0;ao=c[8244]|0;if((ao|0)==0){t_(0,1,1,0)}else{t_(0,1,(uA(ao|0)|0)+1|0,ao)}ao=L;a[ao]=16;a[L+1|0]=34;aI(ao|0,1,2,c[10030]|0);ao=al;L=c[60528]|0;if((L|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((L|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[ao]=L>>>8&255;a[al+1|0]=L&255;aI(ao|0,1,2,c[10030]|0);ao=b+528|0;L=a[54472]|0?0:2;be(ao|0,166264,(v=i,i=i+24|0,c[v>>2]=40152,c[v+8>>2]=40160,c[v+16>>2]=L,v)|0);t_(1,2,uA(ao|0)|0,ao);c[60558]=54;ao=K;a[ao]=17;a[K+1|0]=127;aI(ao|0,1,2,c[10030]|0);ao=J;a[ao]=0;a[J+1|0]=-38;aI(ao|0,1,2,c[10030]|0);ao=ak;J=ak+1|0;ak=0;while(1){K=c[242232+(ak<<2)>>2]|0;if((K|0)<=-32769){ap=12370;break}if((K|0)>=32768){ap=12372;break}a[ao]=K>>>8&255;a[J]=K&255;aI(ao|0,1,2,c[10030]|0);K=ak+1|0;if((K|0)<109){ak=K}else{ap=12374;break}}if((ap|0)==12374){ak=I;a[ak]=16;a[I+1|0]=98;I=c[10030]|0;aI(ak|0,1,2,I|0);I=aj;ak=c[60530]|0;if((ak|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ak|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[I]=ak>>>8&255;a[aj+1|0]=ak&255;ak=c[10030]|0;aI(I|0,1,2,ak|0);ak=H;a[ak]=16;a[H+1|0]=-126;H=c[10030]|0;aI(ak|0,1,2,H|0);H=ai;ak=c[60552]|0;if((ak|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ak|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[H]=ak>>>8&255;a[ai+1|0]=ak&255;ak=c[10030]|0;aI(H|0,1,2,ak|0);ak=G;a[ak]=16;a[G+1|0]=-90;G=c[10030]|0;aI(ak|0,1,2,G|0);G=ah;ak=ah+1|0;ah=c[60540]|0;if((ah|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ah|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[G]=ah>>>8&255;a[ak]=ah&255;ah=c[10030]|0;aI(G|0,1,2,ah|0);ah=c[60541]|0;if((ah|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ah|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[G]=ah>>>8&255;a[ak]=ah&255;ah=c[10030]|0;aI(G|0,1,2,ah|0);ah=c[60542]|0;if((ah|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ah|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[G]=ah>>>8&255;a[ak]=ah&255;ah=c[10030]|0;aI(G|0,1,2,ah|0);ah=F;a[ah]=16;a[F+1|0]=-62;F=c[10030]|0;aI(ah|0,1,2,F|0);F=ag;ah=c[60554]|0;if((ah|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ah|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[F]=ah>>>8&255;a[ag+1|0]=ah&255;ah=c[10030]|0;aI(F|0,1,2,ah|0);ah=E;a[ah]=16;a[E+1|0]=-30;E=c[10030]|0;aI(ah|0,1,2,E|0);E=af;ah=c[60678]|0;if((ah|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ah|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[E]=ah>>>8&255;a[af+1|0]=ah&255;ah=c[10030]|0;aI(E|0,1,2,ah|0);ah=D;a[ah]=17;a[D+1|0]=2;D=c[10030]|0;aI(ah|0,1,2,D|0);D=ae;ah=c[60680]|0;if((ah|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ah|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[D]=ah>>>8&255;a[ae+1|0]=ah&255;ah=c[10030]|0;aI(D|0,1,2,ah|0);ah=C;a[ah]=17;a[C+1|0]=34;C=c[10030]|0;aI(ah|0,1,2,C|0);C=ad;ah=c[13620]|0;if((ah|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ah|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[C]=ah>>>8&255;a[ad+1|0]=ah&255;ah=c[10030]|0;aI(C|0,1,2,ah|0);ah=B;a[ah]=17;a[B+1|0]=76;B=c[10030]|0;aI(ah|0,1,2,B|0);B=ac;ah=ac+1|0;ac=c[60670]|0;if((ac|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ac|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[B]=ac>>>8&255;a[ah]=ac&255;ac=c[10030]|0;aI(B|0,1,2,ac|0);ac=c[60671]|0;if((ac|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ac|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[B]=ac>>>8&255;a[ah]=ac&255;ac=c[10030]|0;aI(B|0,1,2,ac|0);ac=c[60672]|0;if((ac|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ac|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[B]=ac>>>8&255;a[ah]=ac&255;ac=c[10030]|0;aI(B|0,1,2,ac|0);ac=c[60673]|0;if((ac|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ac|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[B]=ac>>>8&255;a[ah]=ac&255;ac=c[10030]|0;aI(B|0,1,2,ac|0);ac=c[60674]|0;if((ac|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ac|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[B]=ac>>>8&255;a[ah]=ac&255;ac=c[10030]|0;aI(B|0,1,2,ac|0);ac=c[60675]|0;if((ac|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ac|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[B]=ac>>>8&255;a[ah]=ac&255;ac=c[10030]|0;aI(B|0,1,2,ac|0);if(!(a[54424]|0)){ac=c[13630]|0;B=c[ac>>2]|0;if((B|0)==0){aq=0}else{ah=0;C=0;ad=B;while(1){B=(C+1|0)+(uA(ad|0)|0)|0;D=ah+1|0;ae=c[ac+(D<<4)>>2]|0;if((ae|0)==0){aq=B;break}else{ah=D;C=B;ad=ae}}}ad=aq+1|0;C=ut(ad)|0;do{if((C|0)==0){gk();ah=ut(ad)|0;if((ah|0)!=0){ar=ah;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=166248,v)|0)}else{ar=C}}while(0);C=c[c[13630]>>2]|0;if((C|0)!=0){ad=0;ah=ar;ac=C;while(1){C=uA(ac|0)|0;a[ah]=C&255;uB(ah+1|0,c[(c[13630]|0)+(ad<<4)>>2]|0);ae=ad+1|0;B=c[(c[13630]|0)+(ae<<4)>>2]|0;if((B|0)==0){break}else{ad=ae;ah=ah+(C+1|0)|0;ac=B}}}ac=aq&1;tY(1,13,aq);ah=c[10030]|0;aI(ar|0,1,aq|0,ah|0);if((ac|0)!=0){ac=c[10030]|0;ah=a[241912]|0;aF(ah|0,ac|0)}uu(ar)}ar=A;a[ar]=0;a[A+1|0]=105;A=c[10030]|0;aI(ar|0,1,2,A|0);A=c[10030]|0;aF(8,A|0);A=c[10030]|0;aI(166208,1,8,A|0);A=c[10030]|0;ar=a[241896]|0;aF(ar|0,A|0);A=z;a[A]=32;a[z+1|0]=38;z=c[10030]|0;aI(A|0,1,2,z|0);z=ab;A=ab+1|0;ab=c[60536]|0;if((ab|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ab|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[z]=ab>>>8&255;a[A]=ab&255;ab=c[10030]|0;aI(z|0,1,2,ab|0);ab=c[60537]|0;if((ab|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ab|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[z]=ab>>>8&255;a[A]=ab&255;ab=c[10030]|0;aI(z|0,1,2,ab|0);ab=c[60538]|0;if((ab|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ab|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[z]=ab>>>8&255;a[A]=ab&255;ab=c[10030]|0;aI(z|0,1,2,ab|0);ab=y;a[ab]=32;a[y+1|0]=66;y=c[10030]|0;aI(ab|0,1,2,y|0);y=$;ab=c[60676]|0;if((ab|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ab|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[y]=ab>>>8&255;a[$+1|0]=ab&255;ab=c[10030]|0;aI(y|0,1,2,ab|0);ab=x;a[ab]=32;a[x+1|0]=98;x=c[10030]|0;aI(ab|0,1,2,x|0);x=_;ab=c[60546]|0;if((ab|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ab|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[x]=ab>>>8&255;a[_+1|0]=ab&255;ab=c[10030]|0;aI(x|0,1,2,ab|0);ab=w;a[ab]=32;a[w+1|0]=-126;w=c[10030]|0;aI(ab|0,1,2,w|0);w=Z;ab=c[60544]|0;if((ab|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ab|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[w]=ab>>>8&255;a[Z+1|0]=ab&255;ab=c[10030]|0;aI(w|0,1,2,ab|0);ab=u;a[ab]=32;a[u+1|0]=-94;u=c[10030]|0;aI(ab|0,1,2,u|0);u=Y;ab=c[60668]|0;if((ab|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((ab|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[u]=ab>>>8&255;a[Y+1|0]=ab&255;ab=c[10030]|0;aI(u|0,1,2,ab|0);ab=an+8|0;c[60534]=(c[ab>>2]|0)+182;u=an+12|0;c[60535]=(c[u>>2]|0)+182;Y=t;a[Y]=32;a[t+1|0]=-56;t=c[10030]|0;aI(Y|0,1,2,t|0);t=X;Y=X+1|0;X=c[60532]|0;if((X|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((X|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[t]=X>>>8&255;a[Y]=X&255;X=c[10030]|0;aI(t|0,1,2,X|0);X=c[60533]|0;if((X|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((X|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[t]=X>>>8&255;a[Y]=X&255;X=c[10030]|0;aI(t|0,1,2,X|0);X=c[60534]|0;if((X|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((X|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[t]=X>>>8&255;a[Y]=X&255;X=c[10030]|0;aI(t|0,1,2,X|0);X=c[60535]|0;if((X|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((X|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[t]=X>>>8&255;a[Y]=X&255;X=c[10030]|0;aI(t|0,1,2,X|0);X=W;a[X]=0;a[W+1|0]=-128;W=c[10030]|0;aI(X|0,1,2,W|0);L16932:do{if(!(a[54472]|0)){W=((c[13554]|0)*12&-1)+4|0;X=c[13550]|0;tY(5,34,W>>>1);t=W>>>2;if((t|0)==0){break}W=V;Y=V+1|0;w=0;while(1){Z=c[X+(w<<2)>>2]|0;if((Z|0)<=-32769){ap=12445;break}if((Z|0)>=32768){ap=12447;break}a[W]=Z>>>8&255;a[Y]=Z&255;aI(W|0,1,2,c[10030]|0);w=w+1|0;if((w|0)>=(t|0)){break L16932}}if((ap|0)==12447){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((ap|0)==12445){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);V=s;a[V]=80;a[s+1|0]=66;s=c[10030]|0;aI(V|0,1,2,s|0);s=U;V=c[60548]|0;if((V|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((V|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[s]=V>>>8&255;a[U+1|0]=V&255;V=c[10030]|0;aI(s|0,1,2,V|0);c[13624]=aa((((c[(c[3524]|0)+8>>2]|0)+182|0)>>>0)/((c[13604]|0)>>>0)>>>0,c[13622]|0);V=r;a[V]=80;a[r+1|0]=98;r=c[10030]|0;aI(V|0,1,2,r|0);r=T;V=c[13624]|0;if((V|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((V|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[r]=V>>>8&255;a[T+1|0]=V&255;V=c[10030]|0;aI(r|0,1,2,V|0);V=q;a[V]=83;a[q+1|0]=-126;q=c[10030]|0;aI(V|0,1,2,q|0);q=S;V=c[13624]|0;if((V|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((V|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[q]=V>>>8&255;a[S+1|0]=V&255;V=c[10030]|0;aI(q|0,1,2,V|0);V=p;a[V]=83;a[p+1|0]=98;p=c[10030]|0;aI(V|0,1,2,p|0);p=R;V=c[60548]|0;if((V|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((V|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[p]=V>>>8&255;a[R+1|0]=V&255;V=c[10030]|0;aI(p|0,1,2,V|0);do{if(a[54472]|0){c[13658]=1}else{if((c[13658]|0)==3){break}c[13658]=3;c[13615]=3;V=c[13656]|0;if((V|0)!=0){p=c[13602]|0;tY(4,1,V<<1);L16972:do{if((V|0)>0){R=g;q=g+1|0;S=0;while(1){r=c[p+(S<<2)>>2]|0;if((r|0)<=-32769){ap=12472;break}if((r|0)>=32768){ap=12474;break}a[R]=r>>>8&255;a[q]=r&255;aI(R|0,1,2,c[10030]|0);S=S+1|0;if((S|0)>=(V|0)){break L16972}}if((ap|0)==12472){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}else if((ap|0)==12474){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13656]=0}V=f;a[V]=80;a[f+1|0]=-126;p=c[10030]|0;aI(V|0,1,2,p|0);p=o;V=c[13658]|0;if((V|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((V|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[p]=V>>>8&255;a[o+1|0]=V&255;V=c[10030]|0;aI(p|0,1,2,V|0);V=n;a[V]=81;a[n+1|0]=-62;p=c[10030]|0;aI(V|0,1,2,p|0);p=Q;V=c[13658]|0;if((V|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((V|0)<32768){a[p]=V>>>8&255;a[Q+1|0]=V&255;V=c[10030]|0;aI(p|0,1,2,V|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13646]=c[13586];c[13647]=c[13587];c[13648]=c[13588];c[13649]=c[13589];c[13650]=c[13590];c[13651]=c[13591];c[13652]=c[13592];c[13653]=c[13593];c[13654]=c[13594];c[13655]=c[13595];c[13611]=c[an+16>>2];an=m;a[an]=82;a[m+1|0]=-62;m=c[10030]|0;aI(an|0,1,2,m|0);m=P;an=c[60550]|0;if((an|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((an|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[m]=an>>>8&255;a[P+1|0]=an&255;an=c[10030]|0;aI(m|0,1,2,an|0);an=l;a[an]=83;a[l+1|0]=2;l=c[10030]|0;aI(an|0,1,2,l|0);l=O;an=c[60556]|0;if((an|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((an|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[l]=an>>>8&255;a[O+1|0]=an&255;an=c[10030]|0;aI(l|0,1,2,an|0);an=am|0;am=c[13628]|0;be(an|0,166136,(v=i,i=i+16|0,c[v>>2]=54528,c[v+8>>2]=am,v)|0);nc(an);as=+h[3817];an=~~((as<0.0?1.0:as)*+((c[(c[3524]|0)+28>>2]|0)>>>0>>>0)*.5);c[13568]=an;c[13558]=(an*12&-1|0)/17&-1;c[13556]=(an*13&-1|0)/15&-1;c[13562]=(an|0)/2&-1;c[13564]=(an*36&-1|0)/29&-1;c[13566]=(an*14&-1|0)/13&-1;c[13560]=(an*18&-1|0)/29&-1;an=a[54472]|0;if(!((c[13554]|0)>0&(an^1))){i=b;return}do{if(!(an|(c[13658]|0)==0)){c[13658]=0;c[13615]=0;am=c[13656]|0;if((am|0)!=0){l=c[13602]|0;tY(4,1,am<<1);L17016:do{if((am|0)>0){O=e;m=e+1|0;P=0;while(1){Q=c[l+(P<<2)>>2]|0;if((Q|0)<=-32769){ap=12500;break}if((Q|0)>=32768){ap=12502;break}a[O]=Q>>>8&255;a[m]=Q&255;aI(O|0,1,2,c[10030]|0);P=P+1|0;if((P|0)>=(am|0)){break L17016}}if((ap|0)==12500){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}else if((ap|0)==12502){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13656]=0}am=d;a[am]=80;a[d+1|0]=-126;l=c[10030]|0;aI(am|0,1,2,l|0);l=k;am=c[13658]|0;if((am|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((am|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[l]=am>>>8&255;a[k+1|0]=am&255;am=c[10030]|0;aI(l|0,1,2,am|0);am=j;a[am]=81;a[j+1|0]=-62;l=c[10030]|0;aI(am|0,1,2,l|0);l=N;am=c[13658]|0;if((am|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((am|0)<32768){a[l]=am>>>8&255;a[N+1|0]=am&255;am=c[10030]|0;aI(l|0,1,2,am|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);N=c[ab>>2]|0;ab=c[u>>2]|0;c[M>>2]=0;c[M+4>>2]=0;c[M+12>>2]=N;c[M+16>>2]=0;c[M+24>>2]=N;c[M+28>>2]=ab;c[M+36>>2]=0;c[M+40>>2]=ab;c[M+48>>2]=0;c[M+52>>2]=0;c[M+8>>2]=1;no(5,M|0);i=b;return}else if((ap|0)==12370){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}else if((ap|0)==12372){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}function m5(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;e=i;i=i+8|0;f=e|0;g=c[3524]|0;h=c[g+8>>2]|0;j=h>>>0>b>>>0?b:h;h=c[g+12>>2]|0;g=h>>>0>d>>>0?d:h;if((j|0)==(c[13598]|0)&(g|0)==(c[13596]|0)){i=e;return}h=c[13656]|0;if((h|0)!=0){d=c[13602]|0;tY(4,1,h<<1);L17049:do{if((h|0)>0){b=f;k=f+1|0;l=0;while(1){m=c[d+(l<<2)>>2]|0;if((m|0)<=-32769){n=12579;break}if((m|0)>=32768){n=12581;break}a[b]=m>>>8&255;a[k]=m&255;aI(b|0,1,2,c[10030]|0);l=l+1|0;if((l|0)>=(h|0)){break L17049}}if((n|0)==12579){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}else if((n|0)==12581){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13656]=0}c[13598]=j;c[13596]=g;i=e;return}function m6(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;e=i;i=i+16|0;f=e|0;g=e+8|0;h=c[3524]|0;j=c[h+8>>2]|0;k=j>>>0>b>>>0?b:j;j=c[h+12>>2]|0;h=j>>>0>d>>>0?d:j;j=c[13598]|0;if((k|0)==(j|0)&(h|0)==(c[13596]|0)){i=e;return}d=c[13656]|0;do{if((d|0)>16380){b=c[13602]|0;l=d<<1;L17067:do{if(a[54560]|0){tY(4,7,l);m=g;n=g+1|0;o=0;while(1){p=c[b+(o<<2)>>2]|0;if((p|0)<=-32769){q=12593;break}if((p|0)>=32768){q=12595;break}a[m]=p>>>8&255;a[n]=p&255;aI(m|0,1,2,c[10030]|0);o=o+1|0;if((o|0)>=(d|0)){break L17067}}if((q|0)==12595){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((q|0)==12593){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}else{tY(4,1,l);o=f;m=f+1|0;n=0;while(1){p=c[b+(n<<2)>>2]|0;if((p|0)<=-32769){q=12599;break}if((p|0)>=32768){q=12601;break}a[o]=p>>>8&255;a[m]=p&255;aI(o|0,1,2,c[10030]|0);n=n+1|0;if((n|0)>=(d|0)){break L17067}}if((q|0)==12601){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((q|0)==12599){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);b=c[13598]|0;c[13656]=1;c[c[13602]>>2]=b;b=(c[13596]|0)+182|0;l=c[13656]|0;c[13656]=l+1;c[(c[13602]|0)+(l<<2)>>2]=b}else{if((d|0)!=0){break}c[13656]=1;c[c[13602]>>2]=j;b=(c[13596]|0)+182|0;l=c[13656]|0;c[13656]=l+1;c[(c[13602]|0)+(l<<2)>>2]=b}}while(0);j=c[13656]|0;c[13656]=j+1;c[(c[13602]|0)+(j<<2)>>2]=k;j=c[13656]|0;c[13656]=j+1;c[(c[13602]|0)+(j<<2)>>2]=h+182;c[13598]=k;c[13596]=h;i=e;return}function m7(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;d=i;i=i+40|0;e=d|0;f=d+8|0;g=d+16|0;h=d+24|0;j=d+32|0;k=(b|0)<-3?-3:b;if((k|0)==(c[13626]|0)){i=d;return}c[13626]=k;do{if((k|0)>-1){l=(k>>>0)%9>>>0;m=12616}else{if((k|0)==-4){if(a[54472]|0){break}else{n=0;m=12619;break}}else{if((k|0)<-2){break}else{l=k;m=12616;break}}}}while(0);do{if((m|0)==12616){if(a[54472]|0){c[13658]=1;n=1;m=12619;break}else{n=l+3|0;m=12619;break}}}while(0);do{if((m|0)==12619){if((n|0)==(c[13658]|0)){break}c[13658]=n;c[13615]=n;l=c[13656]|0;if((l|0)!=0){b=c[13602]|0;tY(4,1,l<<1);L17109:do{if((l|0)>0){o=f;p=f+1|0;q=0;while(1){r=c[b+(q<<2)>>2]|0;if((r|0)<=-32769){m=12624;break}if((r|0)>=32768){m=12626;break}a[o]=r>>>8&255;a[p]=r&255;aI(o|0,1,2,c[10030]|0);q=q+1|0;if((q|0)>=(l|0)){break L17109}}if((m|0)==12626){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((m|0)==12624){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}l=e;a[l]=80;a[e+1|0]=-126;b=c[10030]|0;aI(l|0,1,2,b|0);b=h;l=c[13658]|0;if((l|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((l|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[b]=l>>>8&255;a[h+1|0]=l&255;l=c[10030]|0;aI(b|0,1,2,l|0);l=g;a[l]=81;a[g+1|0]=-62;b=c[10030]|0;aI(l|0,1,2,b|0);b=j;l=c[13658]|0;if((l|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((l|0)<32768){a[b]=l>>>8&255;a[j+1|0]=l&255;l=c[10030]|0;aI(b|0,1,2,l|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);if(a[54576]|0){tT((k|0)==-1?2:0);i=d;return}else{tT(k);i=d;return}}function m8(a){a=a|0;c[13612]=a;return 1}function m9(b){b=b|0;var c=0;if(a[54336]|0){c=(b|0)==0&1;return c|0}else{h[6804]=+(b|0)*1.5707963267948966/90.0;c=1;return c|0}return 0}function na(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0.0,C=0.0,D=0.0;f=i;i=i+88|0;g=f|0;j=f+8|0;k=f+16|0;l=f+24|0;m=f+32|0;n=f+40|0;o=f+48|0;p=f+56|0;q=f+64|0;r=f+72|0;s=f+80|0;if(b>>>0>32767|d>>>0>32767){i=f;return}else{t=e}while(1){u=a[t]|0;if(u<<24>>24==0){w=12726;break}if((aM(u&255|0)|0)==0){break}else{t=t+1|0}}if((w|0)==12726){i=f;return}t=c[13656]|0;if((t|0)!=0){u=c[13602]|0;tY(4,1,t<<1);L17156:do{if((t|0)>0){x=m;y=m+1|0;z=0;while(1){A=c[u+(z<<2)>>2]|0;if((A|0)<=-32769){w=12659;break}if((A|0)>=32768){w=12661;break}a[x]=A>>>8&255;a[y]=A&255;aI(x|0,1,2,c[10030]|0);z=z+1|0;if((z|0)>=(t|0)){break L17156}}if((w|0)==12661){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((w|0)==12659){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}w=c[13610]|0;do{if((c[13648]|0)!=(w|0)){c[13648]=w;t=l;a[t]=81;a[l+1|0]=66;u=c[10030]|0;aI(t|0,1,2,u|0);u=s;t=c[13610]|0;if((t|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((t|0)<32768){a[u]=t>>>8&255;a[s+1|0]=t&255;t=c[10030]|0;aI(u|0,1,2,t|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);s=c[13612]|0;do{if((c[13650]|0)!=(s|0)){c[13650]=s;if((s|0)==2){c[60488]=3}else if((s|0)==1){c[60488]=2}else if((s|0)==0){c[60488]=1}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=103960,v)|0)}l=k;a[l]=82;a[k+1|0]=76;w=c[10030]|0;aI(l|0,1,2,w|0);w=r;l=r+1|0;t=c[60488]|0;if((t|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((t|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[w]=t>>>8&255;a[l]=t&255;t=c[10030]|0;aI(w|0,1,2,t|0);t=c[60489]|0;if((t|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((t|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[w]=t>>>8&255;a[l]=t&255;t=c[10030]|0;aI(w|0,1,2,t|0);t=c[60490]|0;if((t|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((t|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[w]=t>>>8&255;a[l]=t&255;t=c[10030]|0;aI(w|0,1,2,t|0);t=c[60491]|0;if((t|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((t|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[w]=t>>>8&255;a[l]=t&255;t=c[10030]|0;aI(w|0,1,2,t|0);t=c[60492]|0;if((t|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((t|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[w]=t>>>8&255;a[l]=t&255;t=c[10030]|0;aI(w|0,1,2,t|0);t=c[60493]|0;if((t|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((t|0)<32768){a[w]=t>>>8&255;a[l]=t&255;t=c[10030]|0;aI(w|0,1,2,t|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);r=c[13611]|0;do{if((c[13649]|0)!=(r|0)){c[13649]=r;k=r<<1;s=(k|0)/3&-1;t=j;a[t]=81;a[j+1|0]=-30;w=c[10030]|0;aI(t|0,1,2,w|0);w=q;if((k|0)<=-98307){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((k|0)<98304){a[w]=s>>>8&255;a[q+1|0]=s&255;s=c[10030]|0;aI(w|0,1,2,s|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);B=+h[6804];do{if(+h[6823]!=B){h[6823]=B;C=+(c[13611]|0);D=B+1.5707963267948966;c[60484]=~~(C*+S(+D));c[60485]=~~(C*+T(+D));c[60486]=~~(C*+S(+B));c[60487]=~~(C*+T(+B));q=g;a[q]=82;a[g+1|0]=8;j=c[10030]|0;aI(q|0,1,2,j|0);j=p;q=p+1|0;r=c[60484]|0;if((r|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((r|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[j]=r>>>8&255;a[q]=r&255;r=c[10030]|0;aI(j|0,1,2,r|0);r=c[60485]|0;if((r|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((r|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[j]=r>>>8&255;a[q]=r&255;r=c[10030]|0;aI(j|0,1,2,r|0);r=c[60486]|0;if((r|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((r|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[j]=r>>>8&255;a[q]=r&255;r=c[10030]|0;aI(j|0,1,2,r|0);r=c[60487]|0;if((r|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((r|0)<32768){a[j]=r>>>8&255;a[q]=r&255;r=c[10030]|0;aI(j|0,1,2,r|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[60480]=b;c[60481]=d+182;d=uA(e|0)|0;b=((d|0)>254?3:1)+d|0;p=(b&1|0)==0;tY(4,4,b+6|0);b=o;g=o+1|0;o=c[60480]|0;if((o|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((o|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[b]=o>>>8&255;a[g]=o&255;aI(b|0,1,2,c[10030]|0);o=c[60481]|0;if((o|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((o|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[b]=o>>>8&255;a[g]=o&255;aI(b|0,1,2,c[10030]|0);o=c[60482]|0;if((o|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((o|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[b]=o>>>8&255;a[g]=o&255;aI(b|0,1,2,c[10030]|0);b=c[10030]|0;do{if((d|0)<255){o=d<<24>>24;aF(o|0,b|0)}else{o=a[241888]|0;aF(o|0,b|0);o=n;if((d|0)<32768){a[o]=d>>>8&255;a[n+1|0]=d&255;g=c[10030]|0;aI(o|0,1,2,g|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);aI(e|0,1,d|0,c[10030]|0);if(!p){p=c[10030]|0;d=a[241880]|0;aF(d|0,p|0)}c[13596]=-2e3;c[13598]=-2e3;i=f;return}function nb(a){a=+a;var b=0;b=~~((a<0.0?1.0:a)*+((c[(c[3524]|0)+28>>2]|0)>>>0>>>0)*.5);c[13568]=b;c[13558]=(b*12&-1|0)/17&-1;c[13556]=(b*13&-1|0)/15&-1;c[13562]=(b|0)/2&-1;c[13564]=(b*36&-1|0)/29&-1;c[13566]=(b*14&-1|0)/13&-1;c[13560]=(b*18&-1|0)/29&-1;return}function nc(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0,q=0,r=0,s=0,t=0;d=i;i=i+8|0;e=d|0;f=c[3524]|0;g=a8(b|0,44)|0;if((b|0)==0){j=12758}else{if((a[b]|0)==0){j=12758}else{k=b}}if((j|0)==12758){k=242752}j=(g|0)!=0;if(j){l=g-k|0}else{l=uA(k|0)|0}b=c[13630]|0;m=c[b>>2]|0;L17297:do{if((m|0)==0){n=0;o=1.0}else{p=0;q=m;while(1){if((uA(q|0)|0)==(l|0)){if((uJ(k|0,q|0,l|0)|0)==0){break}}r=p+1|0;s=c[b+(r<<4)>>2]|0;if((s|0)==0){n=0;o=1.0;break L17297}else{p=r;q=s}}n=p+1|0;o=+h[b+(p<<4)+8>>3]}}while(0);l=(n|0)==0?1:n;c[13610]=l;n=c[b+(l-1<<4)>>2]|0;l=uA(n|0)|0;b=(l|0)>31?31:l;uF(54528,n|0,b|0);a[b+54528|0]=0;b=c[13628]|0;c[e>>2]=b;if(j){j=g+1|0;ca(j|0,21e4,(v=i,i=i+8|0,c[v>>2]=e,v)|0);t=c[e>>2]|0}else{t=b}b=aa((((c[(c[3524]|0)+8>>2]|0)+182|0)>>>0)/((c[13604]|0)>>>0)>>>0,t);c[f+16>>2]=b;c[f+20>>2]=~~(o*+(aa((((c[(c[3524]|0)+8>>2]|0)+182|0)>>>0)/((c[13604]|0)>>>0)>>>0,c[e>>2]|0)>>>0>>>0)*.527);c[13611]=b;i=d;return 1}function nd(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0;g=i;i=i+64|0;h=g|0;c[h>>2]=b;c[h+4>>2]=d;j=e+b|0;c[h+12>>2]=j;c[h+16>>2]=d;c[h+24>>2]=j;j=f+d|0;c[h+28>>2]=j;c[h+36>>2]=b;c[h+40>>2]=j;c[h+48>>2]=b;c[h+52>>2]=d;c[h+8>>2]=a;no(5,h|0);i=g;return}function ne(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;f=i;i=i+112|0;g=f|0;h=f+8|0;j=f+16|0;k=f+24|0;l=f+32|0;m=f+40|0;n=f+48|0;o=f+56|0;p=f+64|0;q=f+72|0;r=f+80|0;s=f+88|0;t=f+96|0;u=f+104|0;if((e|0)<0){m5(b,d);m6(b+1|0,d);i=f;return}w=(e|0)%13&-1;e=c[13656]|0;if((e|0)!=0){x=c[13602]|0;tY(4,1,e<<1);L17316:do{if((e|0)>0){y=q;z=q+1|0;A=0;while(1){B=c[x+(A<<2)>>2]|0;if((B|0)<=-32769){C=12777;break}if((B|0)>=32768){C=12779;break}a[y]=B>>>8&255;a[z]=B&255;aI(y|0,1,2,c[10030]|0);A=A+1|0;if((A|0)>=(e|0)){break L17316}}if((C|0)==12777){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}else if((C|0)==12779){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13656]=0}e=c[13642]|0;tT(0);if((w|0)>2){c[13616]=1}if((w|0)==12|(w|0)==10|(w|0)==8|(w|0)==6|(w|0)==4){c[13613]=0;c[13615]=c[13658];D=c[13616]|0}else{c[13613]=1;c[13616]=0;c[13614]=c[13658];D=0}do{if((c[13654]|0)!=(D|0)){c[13654]=D;x=p;a[x]=82;a[p+1|0]=-62;q=c[10030]|0;aI(x|0,1,2,q|0);q=u;x=c[13616]|0;if((x|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((x|0)<32768){a[q]=x>>>8&255;a[u+1|0]=x&255;x=c[10030]|0;aI(q|0,1,2,x|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);u=c[13615]|0;do{if((c[13653]|0)!=(u|0)){c[13653]=u;p=o;a[p]=82;a[o+1|0]=-30;D=c[10030]|0;aI(p|0,1,2,D|0);D=t;p=c[13615]|0;if((p|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((p|0)<32768){a[D]=p>>>8&255;a[t+1|0]=p&255;p=c[10030]|0;aI(D|0,1,2,p|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);t=c[13651]|0;o=c[13613]|0;do{if((t|0)==(o|0)){E=t}else{c[13651]=o;u=n;a[u]=83;a[n+1|0]=-62;p=c[10030]|0;aI(u|0,1,2,p|0);p=s;u=c[13651]|0;if((u|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((u|0)<32768){a[p]=u>>>8&255;a[s+1|0]=u&255;u=c[10030]|0;aI(p|0,1,2,u|0);E=c[13651]|0;break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);do{if((E|0)!=0){s=c[13614]|0;if((c[13652]|0)==(s|0)){break}c[13652]=s;s=m;a[s]=83;a[m+1|0]=-94;n=c[10030]|0;aI(s|0,1,2,n|0);n=r;s=c[13652]|0;if((s|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((s|0)<32768){a[n]=s>>>8&255;a[r+1|0]=s&255;s=c[10030]|0;aI(n|0,1,2,s|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);do{if((w|0)==0){m5(b-(c[13568]|0)|0,d);m6((c[13568]|0)+b|0,d);m5(b,d-(c[13568]|0)|0);m6(b,(c[13568]|0)+d|0)}else if((w|0)==2){m5(b,d-(c[13568]|0)|0);m6(b,(c[13568]|0)+d|0);m5((c[13556]|0)+b|0,d-(c[13562]|0)|0);m6(b-(c[13556]|0)|0,(c[13562]|0)+d|0);m5((c[13556]|0)+b|0,(c[13562]|0)+d|0);m6(b-(c[13556]|0)|0,d-(c[13562]|0)|0)}else if((w|0)==1){r=c[13558]|0;m5(b-r|0,d-r|0);r=c[13558]|0;m6(r+b|0,r+d|0);r=c[13558]|0;m5(b-r|0,r+d|0);r=c[13558]|0;m6(r+b|0,d-r|0)}else if((w|0)==3|(w|0)==4){r=c[13558]|0;m5(b-r|0,d-r|0);r=c[13558]|0;m6(r+b|0,d-r|0);r=c[13558]|0;m6(r+b|0,r+d|0);r=c[13558]|0;m6(b-r|0,r+d|0);r=c[13656]|0;if((r|0)==0){break}m=c[13602]|0;tY(4,7,r<<1);L17377:do{if((r|0)>0){E=l;s=l+1|0;n=0;while(1){o=c[m+(n<<2)>>2]|0;if((o|0)<=-32769){C=12820;break}if((o|0)>=32768){C=12822;break}a[E]=o>>>8&255;a[s]=o&255;aI(E|0,1,2,c[10030]|0);n=n+1|0;if((n|0)>=(r|0)){break L17377}}if((C|0)==12822){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((C|0)==12820){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}else if((w|0)==5|(w|0)==6){m5(b,d-(c[13568]|0)|0);m6((c[13562]|0)+b|0,d-(c[13556]|0)|0);m6((c[13556]|0)+b|0,d-(c[13562]|0)|0);m6((c[13568]|0)+b|0,d);m6((c[13556]|0)+b|0,(c[13562]|0)+d|0);m6((c[13562]|0)+b|0,(c[13556]|0)+d|0);m6(b,(c[13568]|0)+d|0);m6(b-(c[13562]|0)|0,(c[13556]|0)+d|0);m6(b-(c[13556]|0)|0,(c[13562]|0)+d|0);m6(b-(c[13568]|0)|0,d);m6(b-(c[13556]|0)|0,d-(c[13562]|0)|0);m6(b-(c[13562]|0)|0,d-(c[13556]|0)|0);r=c[13656]|0;if((r|0)==0){break}m=c[13602]|0;tY(4,7,r<<1);L17389:do{if((r|0)>0){n=k;E=k+1|0;s=0;while(1){o=c[m+(s<<2)>>2]|0;if((o|0)<=-32769){C=12829;break}if((o|0)>=32768){C=12831;break}a[n]=o>>>8&255;a[E]=o&255;aI(n|0,1,2,c[10030]|0);s=s+1|0;if((s|0)>=(r|0)){break L17389}}if((C|0)==12829){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}else if((C|0)==12831){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13656]=0}else if((w|0)==7|(w|0)==8){m5(b,(c[13564]|0)+d|0);m6(b-(c[13566]|0)|0,d-(c[13560]|0)|0);m6((c[13566]|0)+b|0,d-(c[13560]|0)|0);r=c[13656]|0;if((r|0)==0){break}m=c[13602]|0;tY(4,7,r<<1);L17401:do{if((r|0)>0){s=j;n=j+1|0;E=0;while(1){o=c[m+(E<<2)>>2]|0;if((o|0)<=-32769){C=12838;break}if((o|0)>=32768){C=12840;break}a[s]=o>>>8&255;a[n]=o&255;aI(s|0,1,2,c[10030]|0);E=E+1|0;if((E|0)>=(r|0)){break L17401}}if((C|0)==12840){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((C|0)==12838){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}else if((w|0)==11|(w|0)==12){m5(b-(c[13568]|0)|0,d);m6(b,d-(c[13568]|0)|0);m6((c[13568]|0)+b|0,d);m6(b,(c[13568]|0)+d|0);r=c[13656]|0;if((r|0)==0){break}m=c[13602]|0;tY(4,7,r<<1);L17413:do{if((r|0)>0){E=g;s=g+1|0;n=0;while(1){o=c[m+(n<<2)>>2]|0;if((o|0)<=-32769){C=12856;break}if((o|0)>=32768){C=12858;break}a[E]=o>>>8&255;a[s]=o&255;aI(E|0,1,2,c[10030]|0);n=n+1|0;if((n|0)>=(r|0)){break L17413}}if((C|0)==12858){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((C|0)==12856){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}else if((w|0)==9|(w|0)==10){m5(b,d-(c[13564]|0)|0);m6(b-(c[13566]|0)|0,(c[13560]|0)+d|0);m6((c[13566]|0)+b|0,(c[13560]|0)+d|0);r=c[13656]|0;if((r|0)==0){break}m=c[13602]|0;tY(4,7,r<<1);L17425:do{if((r|0)>0){n=h;E=h+1|0;s=0;while(1){o=c[m+(s<<2)>>2]|0;if((o|0)<=-32769){C=12847;break}if((o|0)>=32768){C=12849;break}a[n]=o>>>8&255;a[E]=o&255;aI(n|0,1,2,c[10030]|0);s=s+1|0;if((s|0)>=(r|0)){break L17425}}if((C|0)==12849){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((C|0)==12847){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}}while(0);tT(e);i=f;return}function nf(b){b=+b;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;d=i;i=i+24|0;e=d|0;f=d+8|0;g=d+16|0;h=~~((b>0.0?b:.5)*+((c[13622]|0)>>>0>>>0)*+((((c[(c[3524]|0)+8>>2]|0)+182|0)>>>0)/((c[13604]|0)>>>0)>>>0>>>0>>>0));if((h|0)==(c[13624]|0)){i=d;return}j=c[13656]|0;if((j|0)!=0){k=c[13602]|0;tY(4,1,j<<1);L17443:do{if((j|0)>0){l=f;m=f+1|0;n=0;while(1){o=c[k+(n<<2)>>2]|0;if((o|0)<=-32769){p=12870;break}if((o|0)>=32768){p=12872;break}a[l]=o>>>8&255;a[m]=o&255;aI(l|0,1,2,c[10030]|0);n=n+1|0;if((n|0)>=(j|0)){break L17443}}if((p|0)==12872){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((p|0)==12870){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}c[13624]=h;h=e;a[h]=80;a[e+1|0]=98;aI(h|0,1,2,c[10030]|0);h=g;e=c[13624]|0;if((e|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((e|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[h]=e>>>8&255;a[g+1|0]=e&255;aI(h|0,1,2,c[10030]|0);tT(c[13642]|0);i=d;return}function ng(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;d=i;i=i+8|0;e=d|0;if((b|0)==0){f=(c[13620]|0)-96|0;i=d;return f|0}g=c[b+28>>2]|0;c[13582]=g;j=g+96|0;if((j|0)>(c[13552]|0)){c[13552]=j;c[13550]=db(c[13550]|0,(j*12&-1)+4|0,167368)|0;k=c[13582]|0}else{k=g}L17469:do{if((k|0)>0){g=b+32|0;j=0;l=289;while(1){c[(c[13550]|0)+(l<<2)>>2]=~~(+h[(c[g>>2]|0)+(j*24&-1)>>3]*255.9);c[(c[13550]|0)+(l+1<<2)>>2]=~~(+h[(c[g>>2]|0)+(j*24&-1)+8>>3]*255.9);c[(c[13550]|0)+(l+2<<2)>>2]=~~(+h[(c[g>>2]|0)+(j*24&-1)+16>>3]*255.9);m=j+1|0;n=c[13582]|0;if((m|0)>=(n|0)){o=n;break L17469}j=m;l=l+3|0}}else{o=k}}while(0);k=o+96|0;c[13554]=k;o=(k*12&-1)+4|0;k=c[13550]|0;tY(5,34,o>>>1);b=o>>>2;if((b|0)==0){f=0;i=d;return f|0}o=e;l=e+1|0;e=0;while(1){j=c[k+(e<<2)>>2]|0;if((j|0)<=-32769){p=12893;break}if((j|0)>=32768){p=12895;break}a[o]=j>>>8&255;a[l]=j&255;aI(o|0,1,2,c[10030]|0);j=e+1|0;if((j|0)<(b|0)){e=j}else{f=0;p=12901;break}}if((p|0)==12893){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0);return 0}else if((p|0)==12895){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0);return 0}else if((p|0)==12901){i=d;return f|0}return 0}function nh(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0.0,L=0;d=i;i=i+80|0;e=d|0;f=d+8|0;g=d+16|0;j=d+24|0;k=d+32|0;l=d+40|0;m=d+48|0;n=d+56|0;o=d+64|0;p=d+72|0;q=c[b>>2]|0;L17487:do{if((q|0)==1){r=b+4|0;s=c[r>>2]|0;do{if((s|0)>-1){t=(s>>>0)%9>>>0;u=12908}else{if((s|0)==-4){if(a[54472]|0){break}else{w=0;u=12911;break}}else{if((s|0)<-2){break}else{t=s;u=12908;break}}}}while(0);do{if((u|0)==12908){if(a[54472]|0){c[13658]=1;w=1;u=12911;break}else{w=t+3|0;u=12911;break}}}while(0);do{if((u|0)==12911){if((w|0)==(c[13658]|0)){break}c[13658]=w;c[13615]=w;s=c[13656]|0;if((s|0)!=0){x=c[13602]|0;tY(4,1,s<<1);L17506:do{if((s|0)>0){y=f;z=f+1|0;A=0;while(1){B=c[x+(A<<2)>>2]|0;if((B|0)<=-32769){u=12916;break}if((B|0)>=32768){u=12918;break}a[y]=B>>>8&255;a[z]=B&255;aI(y|0,1,2,c[10030]|0);A=A+1|0;if((A|0)>=(s|0)){break L17506}}if((u|0)==12916){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}else if((u|0)==12918){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13656]=0}s=e;a[s]=80;a[e+1|0]=-126;x=c[10030]|0;aI(s|0,1,2,x|0);x=m;s=c[13658]|0;if((s|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((s|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[x]=s>>>8&255;a[m+1|0]=s&255;s=c[10030]|0;aI(x|0,1,2,s|0);s=l;a[s]=81;a[l+1|0]=-62;x=c[10030]|0;aI(s|0,1,2,x|0);x=p;s=c[13658]|0;if((s|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((s|0)<32768){a[x]=s>>>8&255;a[p+1|0]=s&255;s=c[10030]|0;aI(x|0,1,2,s|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13626]=c[r>>2];i=d;return}else if((q|0)==3){s=c[b+4>>2]|0;x=s>>>16&255;A=s>>>8&255;y=s&255;s=c[13554]|0;if((s|0)<=0){C=0;break}z=c[13550]|0;B=0;D=1;E=65536;F=0;while(1){G=(c[z+(D<<2)>>2]|0)-x|0;H=(c[z+(D+1<<2)>>2]|0)-A|0;I=(c[z+(D+2<<2)>>2]|0)-y|0;J=aa(G,G);G=aa(H,H)+J|0;J=G+aa(I,I)|0;I=(J|0)<(E|0);G=I?B:F;if((J|0)<32){C=G;break L17487}H=B+1|0;if((H|0)<(s|0)){B=H;D=D+3|0;E=I?J:E;F=G}else{C=G;break}}}else if((q|0)==5){K=+h[b+8>>3];F=c[13582]|0;if(K>0.0){L=~~(K*+(F|0))}else{L=0}E=(L|0)<(F|0)?L:F-1|0;c[13615]=E;C=E+96|0}else{i=d;return}}while(0);c[13615]=C;if((c[13658]|0)==(C|0)){i=d;return}c[13658]=C;c[13626]=C;C=c[13656]|0;if((C|0)!=0){L=c[13602]|0;tY(4,1,C<<1);L17546:do{if((C|0)>0){b=k;q=k+1|0;p=0;while(1){l=c[L+(p<<2)>>2]|0;if((l|0)<=-32769){u=12943;break}if((l|0)>=32768){u=12945;break}a[b]=l>>>8&255;a[q]=l&255;aI(b|0,1,2,c[10030]|0);p=p+1|0;if((p|0)>=(C|0)){break L17546}}if((u|0)==12945){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}else if((u|0)==12943){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}}}while(0);c[13656]=0}u=j;a[u]=80;a[j+1|0]=-126;aI(u|0,1,2,c[10030]|0);u=o;j=c[13658]|0;if((j|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((j|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[u]=j>>>8&255;a[o+1|0]=j&255;aI(u|0,1,2,c[10030]|0);u=g;a[u]=81;a[g+1|0]=-62;aI(u|0,1,2,c[10030]|0);u=n;g=c[13658]|0;if((g|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((g|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[u]=g>>>8&255;a[n+1|0]=g&255;aI(u|0,1,2,c[10030]|0);i=d;return}function ni(){c[13330]=0;a[53312]=0;return}function nj(){var a=0,b=0,d=0;a=i;b=~~(+((c[13326]|0)>>>0>>>0)/10.0+.5+0.0);d=~~(+((c[13324]|0)>>>0>>>0)/10.0+.5+0.0);cf(c[10030]|0,167816,(v=i,i=i+64|0,c[v>>2]=0,c[v+8>>2]=0,c[v+16>>2]=b,c[v+24>>2]=d,c[v+32>>2]=0,c[v+40>>2]=0,c[v+48>>2]=b,c[v+56>>2]=d,v)|0);i=a;return}function nk(){aI(199192,10,1,c[10030]|0);return}function nl(){if(!(a[53312]|0)){c[13330]=0;return}aI(169392,2,1,c[10030]|0);a[53312]=0;c[13330]=0;return}function nm(b,d){b=b|0;d=d|0;var e=0,f=0;e=i;if(a[53312]|0){f=c[10030]|0;aI(169392,2,1,f|0)}cf(c[10030]|0,167984,(v=i,i=i+16|0,h[v>>3]=+(b>>>0>>>0)/10.0,h[v+8>>3]=+(d>>>0>>>0)/10.0,v)|0);c[13330]=(c[13330]|0)+1;a[53312]=1;i=e;return}function nn(b,d){b=b|0;d=d|0;var e=0,f=0.0,g=0.0;e=i;f=+(b>>>0>>>0)/10.0;g=+(d>>>0>>>0)/10.0;cf(c[10030]|0,168040,(v=i,i=i+16|0,h[v>>3]=f,h[v+8>>3]=g,v)|0);d=(c[13330]|0)+1|0;c[13330]=d;a[53312]=1;if((d|0)<=399){i=e;return}cf(c[10030]|0,168016,(v=i,i=i+16|0,h[v>>3]=f,h[v+8>>3]=g,v)|0);c[13330]=0;i=e;return}function no(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;e=i;i=i+72|0;f=e|0;g=e+8|0;h=e+16|0;j=e+24|0;k=e+32|0;l=e+40|0;m=e+48|0;n=e+56|0;o=e+64|0;p=c[d+8>>2]|0;q=(p>>4|0)%6&-1;r=p&15;do{if((r|0)==1|(r|0)==4){c[13616]=1;s=1}else if((r|0)==2|(r|0)==5){if((q|0)==0){c[13616]=0;s=0;break}else if((q|0)==3){c[13616]=1;s=1;break}else{c[13616]=3;c[13617]=c[242728+(q<<2)>>2];s=3;break}}else{c[13615]=0;c[13616]=1;s=1}}while(0);do{if((c[13654]|0)!=(s|0)){c[13654]=s;q=k;a[q]=82;a[k+1|0]=-62;r=c[10030]|0;aI(q|0,1,2,r|0);r=o;q=c[13616]|0;if((q|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((q|0)<32768){a[r]=q>>>8&255;a[o+1|0]=q&255;q=c[10030]|0;aI(r|0,1,2,q|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);o=c[13615]|0;do{if((c[13653]|0)!=(o|0)){c[13653]=o;k=j;a[k]=82;a[j+1|0]=-30;s=c[10030]|0;aI(k|0,1,2,s|0);s=n;k=c[13615]|0;if((k|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((k|0)<32768){a[s]=k>>>8&255;a[n+1|0]=k&255;k=c[10030]|0;aI(s|0,1,2,k|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);n=c[13617]|0;do{if((c[13655]|0)!=(n|0)&(c[13616]|0)==3){c[13655]=n;j=h;a[j]=83;a[h+1|0]=2;o=c[10030]|0;aI(j|0,1,2,o|0);o=m;j=c[13617]|0;if((j|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((j|0)<32768){a[o]=j>>>8&255;a[m+1|0]=j&255;j=c[10030]|0;aI(o|0,1,2,j|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13613]=0;do{if((c[13651]|0)!=0){c[13651]=0;m=g;a[m]=83;a[g+1|0]=-62;h=c[10030]|0;aI(m|0,1,2,h|0);h=l;m=c[13651]|0;if((m|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((m|0)<32768){a[h]=m>>>8&255;a[l+1|0]=m&255;m=c[10030]|0;aI(h|0,1,2,m|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);m5(c[d>>2]|0,c[d+4>>2]|0);a[54560]=1;if((b|0)>1){l=1;do{m6(c[d+(l*12&-1)>>2]|0,c[d+(l*12&-1)+4>>2]|0);l=l+1|0;}while((l|0)<(b|0))}b=c[13656]|0;if((b|0)==0){a[54560]=0;i=e;return}l=c[13602]|0;tY(4,7,b<<1);L17640:do{if((b|0)>0){d=f;g=f+1|0;m=0;while(1){h=c[l+(m<<2)>>2]|0;if((h|0)<=-32769){t=13015;break}if((h|0)>=32768){t=13017;break}a[d]=h>>>8&255;a[g]=h&255;aI(d|0,1,2,c[10030]|0);m=m+1|0;if((m|0)>=(b|0)){break L17640}}if((t|0)==13015){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}else if((t|0)==13017){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);c[13656]=0;a[54560]=0;i=e;return}function np(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0.0,M=0,N=0,O=0.0,P=0,Q=0.0,R=0,S=0,T=0,U=0,V=0.0,W=0.0,X=0,Y=0.0,Z=0.0,_=0.0,$=0.0,aa=0.0,ab=0,ac=0.0;b=i;i=i+24|0;d=b|0;e=c[13898]|0;f=c[8272]|0;L17652:do{if((e|0)<(f|0)){j=e;k=f;L17653:while(1){l=c[1054]|0;m=(a[l+(j*40&-1)|0]&1)==0;L17655:do{if(!m){n=c[l+(j*40&-1)+36>>2]|0;o=l+(j*40&-1)+32|0;p=c[10036]|0;q=0;while(1){if((q|0)>=(n|0)){break}if((a[p+((c[o>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break L17655}}if((q|0)==1){r=k;s=j;break L17652}}}while(0);o=c[60232]|0;L17662:do{if((o|0)==0){t=240928}else{p=c[l+(j*40&-1)+36>>2]|0;n=c[l+(j*40&-1)+32>>2]|0;u=(p|0)>0;w=c[10036]|0;x=240928;y=o;while(1){L17666:do{if(!m){if(u){z=0;A=0;B=n;while(1){C=a[y+z|0]|0;if(C<<24>>24==(a[w+(z+B|0)|0]|0)){D=B;E=A}else{if(C<<24>>24!=36){break L17666}D=B-1|0;E=1}F=z+1|0;if((F|0)<(E+p|0)){z=F;A=E;B=D}else{break}}if((E|0)==0){G=F}else{t=x;break L17662}}else{G=0}B=a[y+G|0]|0;if((B<<24>>24|0)==36|(B<<24>>24|0)==0){t=x;break L17662}}}while(0);B=x+8|0;A=c[B>>2]|0;if((A|0)==0){t=B;break}else{x=B;y=A}}}}while(0);o=c[t+4>>2]|0;L17679:do{if((o|0)==0){a[53408]=0;uD(53352,167792,17);c[13336]=22;g[13332]=12.0;c[13326]=5960;c[13324]=7200;y=j+1|0;c[13898]=y;H=y}else if((o|0)==2){a[53408]=1;y=j+1|0;c[13898]=y;H=y}else if((o|0)==1){a[53408]=0;y=j+1|0;c[13898]=y;H=y}else{do{if(!m){y=l+(j*40&-1)+32|0;x=c[y>>2]|0;p=c[10036]|0;w=a[p+x|0]|0;if(!((w<<24>>24|0)==39|(w<<24>>24|0)==34)){break}w=(c[l+(j*40&-1)+36>>2]|0)-2|0;n=(w|0)>49?49:w;if((n|0)>0){w=x;x=0;while(1){u=w+1|0;q=x+1|0;a[x+53352|0]=a[p+u|0]|0;if((q|0)==(n|0)){I=n;break}else{w=u;x=q}}}else{I=0}a[I+53352|0]=0;if((a[p+(c[y>>2]|0)|0]|0)==34){ua(53352)}else{x=53352;w=53352;while(1){n=a[w]|0;if((n<<24>>24|0)==0){break}else if((n<<24>>24|0)==39){q=w+1|0;u=(a[q]|0)==39?q:w;J=u;K=a[u]|0}else{J=w;K=n}a[x]=K;x=x+1|0;w=J+1|0}a[x]=0}w=(c[13898]|0)+1|0;c[13898]=w;H=w;break L17679}}while(0);w=is(d)|0;y=c[w>>2]|0;if((y|0)==2){L=+h[w+8>>3]}else if((y|0)==3){L=+uz(c[w+8>>2]|0,0)}else if((y|0)==1){L=+(c[w+8>>2]|0)}else{break L17653}w=~~L;c[13336]=w;c[13898]=(c[13898]|0)+1;c[(c[3524]|0)+16>>2]=~~(+(w|0)*10.0);c[(c[3524]|0)+20>>2]=~~(+(c[13336]|0)*10.0*6.0/10.0);H=c[13898]|0}}while(0);l=c[8272]|0;if((H|0)<(l|0)){j=H;k=l}else{r=l;s=H;break L17652}}uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{r=f;s=e}}while(0);L17709:do{if((s|0)<(r|0)){e=c[1054]|0;L17711:do{if((a[e+(s*40&-1)|0]&1)!=0){f=c[e+(s*40&-1)+36>>2]|0;H=e+(s*40&-1)+32|0;J=c[10036]|0;K=0;while(1){if((K|0)>=(f|0)){break}if((a[J+((c[H>>2]|0)+K|0)|0]|0)==(a[K+103664|0]|0)){K=K+1|0}else{break L17711}}if((K|0)==1){M=s;N=r;break L17709}}}while(0);e=is(d)|0;H=c[e>>2]|0;if((H|0)==3){O=+uz(c[e+8>>2]|0,0)}else if((H|0)==2){O=+h[e+8>>3]}else if((H|0)==1){O=+(c[e+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}e=~~(O*720.0);c[13326]=e;H=(c[13898]|0)+1|0;c[13898]=H;L17724:do{if((H|0)<(c[8272]|0)){J=c[1054]|0;L17726:do{if((a[J+(H*40&-1)|0]&1)!=0){f=c[J+(H*40&-1)+36>>2]|0;I=J+(H*40&-1)+32|0;t=c[10036]|0;G=0;while(1){if((G|0)>=(f|0)){break}if((a[t+((c[I>>2]|0)+G|0)|0]|0)==(a[G+103664|0]|0)){G=G+1|0}else{break L17726}}if((G|0)==1){P=e;break L17724}}}while(0);J=is(d)|0;K=c[J>>2]|0;if((K|0)==3){Q=+uz(c[J+8>>2]|0,0)}else if((K|0)==1){Q=+(c[J+8>>2]|0)}else if((K|0)==2){Q=+h[J+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}c[13324]=~~(Q*720.0);c[13898]=(c[13898]|0)+1;P=c[13326]|0}else{P=e}}while(0);c[(c[3524]|0)+8>>2]=P;c[(c[3524]|0)+12>>2]=c[13324];c[(c[3524]|0)+24>>2]=((c[13324]|0)>>>0)/80>>>0;c[(c[3524]|0)+28>>2]=((c[13324]|0)>>>0)/80>>>0;M=c[13898]|0;N=c[8272]|0}else{M=s;N=r}}while(0);if((M|0)>=(N|0)){R=a[53408]|0;S=R?116440:116408;T=c[13336]|0;U=c[13326]|0;V=+(U>>>0>>>0);W=V/720.0;X=c[13324]|0;Y=+(X>>>0>>>0);Z=Y/720.0;_=+g[13332];$=_;aa=$/10.0;ab=be(13048,167736,(v=i,i=i+48|0,c[v>>2]=S,c[v+8>>2]=53352,c[v+16>>2]=T,h[v+24>>3]=W,h[v+32>>3]=Z,h[v+40>>3]=aa,v)|0)|0;i=b;return}N=c[1054]|0;L17744:do{if((a[N+(M*40&-1)|0]&1)!=0){r=c[N+(M*40&-1)+36>>2]|0;s=N+(M*40&-1)+32|0;P=c[10036]|0;e=0;while(1){if((e|0)>=(r|0)){break}if((a[P+((c[s>>2]|0)+e|0)|0]|0)==(a[e+103664|0]|0)){e=e+1|0}else{break L17744}}if((e|0)!=1){break}R=a[53408]|0;S=R?116440:116408;T=c[13336]|0;U=c[13326]|0;V=+(U>>>0>>>0);W=V/720.0;X=c[13324]|0;Y=+(X>>>0>>>0);Z=Y/720.0;_=+g[13332];$=_;aa=$/10.0;ab=be(13048,167736,(v=i,i=i+48|0,c[v>>2]=S,c[v+8>>2]=53352,c[v+16>>2]=T,h[v+24>>3]=W,h[v+32>>3]=Z,h[v+40>>3]=aa,v)|0)|0;i=b;return}}while(0);M=is(d)|0;d=c[M>>2]|0;if((d|0)==1){ac=+(c[M+8>>2]|0)}else if((d|0)==2){ac=+h[M+8>>3]}else if((d|0)==3){ac=+uz(c[M+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g[13332]=ac*10.0;c[13898]=(c[13898]|0)+1;R=a[53408]|0;S=R?116440:116408;T=c[13336]|0;U=c[13326]|0;V=+(U>>>0>>>0);W=V/720.0;X=c[13324]|0;Y=+(X>>>0>>>0);Z=Y/720.0;_=+g[13332];$=_;aa=$/10.0;ab=be(13048,167736,(v=i,i=i+48|0,c[v>>2]=S,c[v+8>>2]=53352,c[v+16>>2]=T,h[v+24>>3]=W,h[v+32>>3]=Z,h[v+40>>3]=aa,v)|0)|0;i=b;return}function nq(a){a=a|0;c[13354]=a;return 1}function nr(a){a=a|0;c[13334]=a;return 1}function ns(a,b){a=a|0;b=b|0;c[11678]=a;c[11674]=b;return}function nt(){var b=0,d=0,e=0,f=0;b=aa(c[11672]|0,c[11676]|0);if((b|0)<=0){return}d=b;b=c[11684]|0;e=c[11680]|0;while(1){a[b]=32;a[e]=0;f=d-1|0;if((f|0)>0){d=f;b=b+1|0;e=e+1|0}else{break}}return}function nu(b){b=b|0;var d=0,e=0,f=0.0;d=i;if(a[53312]|0){e=c[10030]|0;aI(169392,2,1,e|0);a[53312]=0}do{if((b|0)==3){e=c[10030]|0;f=+g[13332]/10.0;cf(e|0,168928,(v=i,i=i+8|0,h[v>>3]=f,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168520,10,1,e|0);break}else{aI(168496,20,1,e|0);break}}else if((b|0)==8){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168120,16,1,e|0);break}else{aI(168088,30,1,e|0);break}}else if((b|0)==6){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168912,10,1,e|0);break}else{aI(168288,22,1,e|0);break}}else if((b|0)==5){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168432,10,1,e|0);break}else{aI(168368,22,1,e|0);break}}else if((b|0)==1){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168768,10,1,e|0);break}else{aI(168696,18,1,e|0);break}}else if((b|0)==(-2|0)){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168912,10,1,e|0);break}else{aI(168896,15,1,e|0);break}}else if((b|0)==4){cf(c[10030]|0,168488,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168472,10,1,e|0);break}else{aI(168448,22,1,e|0);break}}else if((b|0)==(-1|0)){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168912,10,1,e|0);break}else{aI(168824,18,1,e|0);break}}else if((b|0)==0){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168808,10,1,e|0);break}else{aI(168784,15,1,e|0);break}}else if((b|0)==2){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168616,10,1,e|0);break}else{aI(168576,18,1,e|0);break}}else if((b|0)==7){cf(c[10030]|0,168928,(v=i,i=i+8|0,h[v>>3]=+g[13332]/10.0,v)|0);e=c[10030]|0;if(a[53408]|0){aI(168232,12,1,e|0);break}else{aI(168184,26,1,e|0);break}}}while(0);c[13330]=0;i=d;return}function nv(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0.0,m=0.0;f=i;if(a[53312]|0){g=c[10030]|0;aI(169392,2,1,g|0);a[53312]=0}g=c[13334]|0;if((g|0)==2){j=c[10030]|0;k=c[13336]|0;cf(j|0,169240,(v=i,i=i+24|0,c[v>>2]=53352,c[v+8>>2]=k,c[v+16>>2]=k,v)|0)}else if((g|0)==0){k=c[13336]|0;cf(c[10030]|0,169368,(v=i,i=i+24|0,c[v>>2]=53352,c[v+8>>2]=k,c[v+16>>2]=k,v)|0)}else if((g|0)==1){g=c[13336]|0;cf(c[10030]|0,169264,(v=i,i=i+24|0,c[v>>2]=53352,c[v+8>>2]=g,c[v+16>>2]=g,v)|0)}g=c[10030]|0;l=+(b>>>0>>>0)/10.0;if((c[13354]|0)==0){m=+(d>>>0>>>0)/10.0- +(c[13336]|0)/3.0;cf(g|0,169064,(v=i,i=i+16|0,h[v>>3]=l,h[v+8>>3]=m,v)|0)}else{m=l- +(c[13336]|0)/3.0;l=+(d>>>0>>>0)/10.0;cf(g|0,168968,(v=i,i=i+16|0,h[v>>3]=m,h[v+8>>3]=l,v)|0)}aF(40,c[10030]|0);g=e;while(1){e=a[g]|0;d=g+1|0;b=e<<24>>24;if((e<<24>>24|0)==40|(e<<24>>24|0)==41|(e<<24>>24|0)==92){k=c[10030]|0;aF(92,k|0)}else if((e<<24>>24|0)==0){break}aF(b|0,c[10030]|0);g=d}aI(168944,5,1,c[10030]|0);c[13330]=0;i=f;return}function nw(){var a=0;a=c[11684]|0;if((a|0)!=0){uu(a)}c[11684]=0;return}function nx(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;aF(12,c[10030]|0);b=c[11672]|0;if((b|0)>0){d=b}else{e=c[10030]|0;f=aD(e|0)|0;return}do{d=d-1|0;b=c[11676]|0;g=aa(b,d);h=c[11684]|0;i=b;while(1){if((i|0)<=0){break}j=i-1|0;if((a[h+(g+j|0)|0]|0)==32){i=j}else{k=0;l=b;m=h;n=13169;break}}L17850:do{if((n|0)==13169){while(1){n=0;h=a[m+(aa(l,d)+k|0)|0]|0;aF(h|0,c[10030]|0);h=k+1|0;if((h|0)>=(i|0)){break L17850}k=h;l=c[11676]|0;m=c[11684]|0;n=13169}}}while(0);i=(d|0)>0;if(i|a[46744]^1){h=c[10030]|0;aF(10,h|0)}}while(i);e=c[10030]|0;f=aD(e|0)|0;return}function ny(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0.0,J=0.0;b=i;i=i+48|0;d=b|0;e=b+24|0;f=c[13898]|0;L17860:do{if((f|0)<(c[8272]|0)){g=d|0;j=d+8|0;k=e|0;l=e+8|0;m=f;L17862:while(1){n=c[1054]|0;o=(a[n+(m*40&-1)|0]&1)==0;L17864:do{if(!o){p=c[n+(m*40&-1)+36>>2]|0;q=n+(m*40&-1)+32|0;r=c[10036]|0;s=0;while(1){if((s|0)>=(p|0)){break}if((a[r+((c[q>>2]|0)+s|0)|0]|0)==(a[s+103664|0]|0)){s=s+1|0}else{break L17864}}if((s|0)==1){break L17860}}}while(0);q=c[60192]|0;L17871:do{if((q|0)==0){t=240768}else{r=c[n+(m*40&-1)+36>>2]|0;p=c[n+(m*40&-1)+32>>2]|0;u=(r|0)>0;w=c[10036]|0;x=240768;y=q;while(1){L17875:do{if(!o){if(u){z=0;A=0;B=p;while(1){C=a[y+z|0]|0;if(C<<24>>24==(a[w+(z+B|0)|0]|0)){D=B;E=A}else{if(C<<24>>24!=36){break L17875}D=B-1|0;E=1}F=z+1|0;if((F|0)<(E+r|0)){z=F;A=E;B=D}else{break}}if((E|0)==0){G=F}else{t=x;break L17871}}else{G=0}B=a[y+G|0]|0;if((B<<24>>24|0)==36|(B<<24>>24|0)==0){t=x;break L17871}}}while(0);B=x+8|0;A=c[B>>2]|0;if((A|0)==0){t=B;break}else{x=B;y=A}}}}while(0);o=c[t+4>>2]|0;if((o|0)==4){c[13898]=m+1;H=13201}else if((o|0)==0){c[13898]=m+1;a[46744]=0}else if((o|0)==3){c[13898]=m+1;c[(c[3524]|0)+68>>2]=104;q=(c[3524]|0)+96|0;c[q>>2]=c[q>>2]&-33}else if((o|0)==1){c[13898]=m+1;a[46744]=1}else if((o|0)==2){c[13898]=m+1;c[(c[3524]|0)+68>>2]=74;o=(c[3524]|0)+96|0;c[o>>2]=c[o>>2]|32}else{H=13201}L17894:do{if((H|0)==13201){H=0;o=is(d)|0;q=c[o>>2]|0;if((q|0)==2){I=+h[o+8>>3]}else if((q|0)==3){I=+uz(c[o+8>>2]|0,0)}else if((q|0)==1){I=+(c[o+8>>2]|0)}else{H=13205;break L17862}if((c[g>>2]|0)==3){uu(c[j>>2]|0);c[g>>2]=1}o=~~I;q=c[13898]|0;if((q|0)>=(c[8272]|0)){break}n=c[1054]|0;y=(a[n+(q*40&-1)|0]&1)==0;L17905:do{if(!y){x=c[n+(q*40&-1)+36>>2]|0;r=n+(q*40&-1)+32|0;w=c[10036]|0;p=0;while(1){if((p|0)>=(x|0)){H=13213;break}if((a[w+((c[r>>2]|0)+p|0)|0]|0)==(a[p+103664|0]|0)){p=p+1|0}else{break}}if((H|0)==13213){H=0;if((p|0)==1){break L17894}}if(y){break}r=c[n+(q*40&-1)+36>>2]|0;w=n+(q*40&-1)+32|0;x=c[10036]|0;u=0;while(1){if((u|0)>=(r|0)){break}if((a[x+((c[w>>2]|0)+u|0)|0]|0)==(a[u+148464|0]|0)){u=u+1|0}else{break L17905}}if((u|0)!=1){break}c[13898]=q+1}}while(0);q=is(e)|0;n=c[q>>2]|0;if((n|0)==1){J=+(c[q+8>>2]|0)}else if((n|0)==2){J=+h[q+8>>3]}else if((n|0)==3){J=+uz(c[q+8>>2]|0,0)}else{H=13224;break L17862}if((c[k>>2]|0)==3){uu(c[l>>2]|0);c[k>>2]=1}q=~~J;c[(c[3524]|0)+8>>2]=o;c[11676]=o;c[(c[3524]|0)+12>>2]=q;c[11672]=q}}while(0);m=c[13898]|0;if((m|0)>=(c[8272]|0)){break L17860}}if((H|0)==13224){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((H|0)==13205){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);H=(c[(c[3524]|0)+68>>2]|0)==74?217456:179864;e=c[11676]|0;d=c[11672]|0;be(13048,169488,(v=i,i=i+32|0,c[v>>2]=a[46744]|0?136208:179864,c[v+8>>2]=H,c[v+16>>2]=e,c[v+24>>2]=d,v)|0);i=b;return}function nz(){var a=0,b=0,d=0,e=0,f=0;a=i;b=c[11684]|0;if((b|0)!=0){uu(b)}b=aa((c[11676]<<1)+2|0,(c[11672]|0)+1|0);d=ut(b)|0;do{if((d|0)==0){gk();e=ut(b)|0;if((e|0)!=0){f=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=169528,v)|0)}else{f=d}}while(0);c[11684]=f;c[11680]=f+aa(c[11672]|0,c[11676]|0);i=a;return}function nA(){c[59832]=0;c[59834]=0;c[11664]=0;g[11668]=0.0;a[920]=0;return}function nB(){c[59832]=0;c[59834]=0;return}function nC(a){a=a|0;c[11666]=a;return 1}function nD(b,d,e,f,g,i){b=b|0;d=+d;e=+e;f=f|0;g=g|0;i=i|0;if((i|0)==3){c[59572]=c[11678];c[59570]=c[11674];return}else if((i|0)==4){b=c[59570]|0;c[11678]=c[59572];c[11674]=b;return}else{if(a[238320]|0){return}a[238320]=1;c[11232]=43856;h[29791]=e*2.0;a[238304]=g&1;c[59578]=i;a[238296]=f&1;return}}function nE(a){a=a|0;c[11664]=(((a|0)>-1?a:-a|0)|0)%7&-1;return}function nF(a){a=a|0;g[11668]=(a|0)!=0?90.0:0.0;return 1}function nG(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0;e=c[11674]|0;f=d-e|0;g=(f|0)>-1?f:-f|0;f=c[11678]|0;h=b-f|0;i=(h|0)>-1?h:-h|0;if((g|0)>(i|0)){j=a[46728]|0;k=j<<24>>24;if((k|0)==1){l=1;m=43;n=58}else if((k|0)==2){l=2;m=43;n=124}else{l=3;m=j;n=j}j=c[11676]|0;do{if(j>>>0<f>>>0|(c[11672]|0)>>>0<e>>>0){o=e}else{k=aa(j,e)+f|0;if((a[(c[11680]|0)+k|0]|0|0)>=(l|0)){o=e;break}a[(c[11684]|0)+k|0]=m;k=aa(c[11676]|0,e)+f|0;a[(c[11680]|0)+k|0]=l&255;o=c[11674]|0}}while(0);j=d-o|0;k=(j|0)>-1?j:-j|0;if((k|0)>1){p=l&255;q=1;r=o;o=j;j=k;while(1){k=c[11678]|0;s=~~(+(q|0)*+(b-k|0)/+(j|0)+.5)+k|0;k=aa(o>>31|1,q)+r|0;t=c[11676]|0;do{if(t>>>0<s>>>0|(c[11672]|0)>>>0<k>>>0){u=r}else{v=s+aa(t,k)|0;if((a[(c[11680]|0)+v|0]|0|0)>=(l|0)){u=r;break}a[(c[11684]|0)+v|0]=n;v=aa(c[11676]|0,k)+s|0;a[(c[11680]|0)+v|0]=p;u=c[11674]|0}}while(0);s=q+1|0;k=d-u|0;t=(k|0)>-1?k:-k|0;if((s|0)<(t|0)){q=s;r=u;o=k;j=t}else{break}}}j=c[11676]|0;if(j>>>0<b>>>0|(c[11672]|0)>>>0<d>>>0){c[11678]=b;c[11674]=d;return}o=aa(j,d)+b|0;if((a[(c[11680]|0)+o|0]|0|0)>=(l|0)){c[11678]=b;c[11674]=d;return}a[(c[11684]|0)+o|0]=m;m=aa(c[11676]|0,d)+b|0;a[(c[11680]|0)+m|0]=l&255;c[11678]=b;c[11674]=d;return}l=a[46728]|0;m=l<<24>>24;if((i|0)<=(g|0)){g=(m-1|0)>>>0<2;o=g?m:3;if((i|0)<0){c[11678]=b;c[11674]=d;return}i=g?43:l;g=o&255;j=0;u=f;r=h;h=e;while(1){q=aa(r>>31|1,j)+u|0;p=aa(d-h>>31|1,j)+h|0;n=c[11676]|0;do{if(n>>>0<q>>>0|(c[11672]|0)>>>0<p>>>0){w=u}else{t=aa(p,n)+q|0;if((a[(c[11680]|0)+t|0]|0|0)>=(o|0)){w=u;break}a[(c[11684]|0)+t|0]=i;t=aa(c[11676]|0,p)+q|0;a[(c[11680]|0)+t|0]=g;w=c[11678]|0}}while(0);q=j+1|0;p=b-w|0;if((q|0)>(((p|0)>-1?p:-p|0)|0)){break}j=q;u=w;r=p;h=c[11674]|0}c[11678]=b;c[11674]=d;return}if((m|0)==2){x=2;y=43;z=45}else if((m|0)==1){x=1;y=43;z=46}else{x=3;y=l;z=l}l=c[11676]|0;do{if(l>>>0<f>>>0|(c[11672]|0)>>>0<e>>>0){A=f}else{m=aa(l,e)+f|0;if((a[(c[11680]|0)+m|0]|0|0)>=(x|0)){A=f;break}a[(c[11684]|0)+m|0]=y;m=aa(c[11676]|0,e)+f|0;a[(c[11680]|0)+m|0]=x&255;A=c[11678]|0}}while(0);f=b-A|0;e=(f|0)>-1?f:-f|0;if((e|0)>1){l=x&255;m=1;h=A;A=f;f=e;while(1){e=aa(A>>31|1,m)+h|0;r=c[11674]|0;w=~~(+(m|0)*+(d-r|0)/+(f|0)+.5)+r|0;r=c[11676]|0;do{if(r>>>0<e>>>0|(c[11672]|0)>>>0<w>>>0){B=h}else{u=aa(w,r)+e|0;if((a[(c[11680]|0)+u|0]|0|0)>=(x|0)){B=h;break}a[(c[11684]|0)+u|0]=z;u=aa(c[11676]|0,w)+e|0;a[(c[11680]|0)+u|0]=l;B=c[11678]|0}}while(0);e=m+1|0;w=b-B|0;r=(w|0)>-1?w:-w|0;if((e|0)<(r|0)){m=e;h=B;A=w;f=r}else{break}}}f=c[11676]|0;if(f>>>0<b>>>0|(c[11672]|0)>>>0<d>>>0){c[11678]=b;c[11674]=d;return}A=aa(f,d)+b|0;if((a[(c[11680]|0)+A|0]|0|0)>=(x|0)){c[11678]=b;c[11674]=d;return}a[(c[11684]|0)+A|0]=y;y=aa(c[11676]|0,d)+b|0;a[(c[11680]|0)+y|0]=x&255;c[11678]=b;c[11674]=d;return}function nH(b){b=b|0;if((b|0)==(-1|0)){a[46728]=1;return}else if((b|0)==(-2|0)){a[46728]=2;return}else{if((b|0)<-2){a[46728]=32;return}else{a[46728]=a[240816+((b|0)%7&-1)|0]|0;return}}}function nI(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0;if((e|0)==-1){f=46}else{f=((e|0)%26&-1)+65&255}e=c[11676]|0;if(e>>>0<b>>>0|(c[11672]|0)>>>0<d>>>0){return}g=aa(e,d)+b|0;if((a[(c[11680]|0)+g|0]|0)>=4){return}a[(c[11684]|0)+g|0]=f;f=aa(c[11676]|0,d)+b|0;a[(c[11680]|0)+f|0]=4;return}function nJ(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;f=uA(e|0)|0;g=c[11676]|0;if((f+b|0)>>>0>g>>>0){h=g-f|0;i=(h|0)<0?0:h}else{i=b}if(i>>>0<g>>>0){j=e;k=i;l=g}else{return}while(1){g=a[j]|0;if(g<<24>>24==0){m=13328;break}do{if(l>>>0<k>>>0|(c[11672]|0)>>>0<d>>>0){n=l}else{i=aa(l,d)+k|0;if((a[(c[11680]|0)+i|0]|0)>=5){n=l;break}a[(c[11684]|0)+i|0]=g;i=aa(c[11676]|0,d)+k|0;a[(c[11680]|0)+i|0]=5;n=c[11676]|0}}while(0);g=k+1|0;if(g>>>0<n>>>0){j=j+1|0;k=g;l=n}else{m=13329;break}}if((m|0)==13329){return}else if((m|0)==13328){return}}function nK(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,i=0;g=a[46728]|0;h=c[11678]|0;i=c[11674]|0;a[46728]=62;c[11678]=b;c[11674]=d;nG(e,f);a[46728]=g;c[11678]=h<<24>>24;c[11674]=i<<24>>24;return}function nL(){var b=0,d=0,e=0,f=0,g=0,i=0,j=0,k=0,l=0,m=0;if(!(a[238320]|0)){return}b=c[11678]|0;a[c[11232]|0]=0;d=uA(43856)|0;e=c[11676]|0;L18067:do{if((b|0)<(e|0)){f=b;g=43856;i=e;while(1){j=a[g]|0;if(j<<24>>24==0){break L18067}do{if((a[238304]&1)==0){k=i}else{l=~~(+(c[11674]|0)+ +h[29791]);if(i>>>0<f>>>0|(c[11672]|0)>>>0<l>>>0){k=i;break}m=aa(l,i)+f|0;if((a[(c[11680]|0)+m|0]|0)>=5){k=i;break}a[(c[11684]|0)+m|0]=j;m=aa(c[11676]|0,l)+f|0;a[(c[11680]|0)+m|0]=5;k=c[11676]|0}}while(0);j=f+1|0;if((j|0)<(k|0)){f=j;g=g+1|0;i=k}else{break}}}}while(0);do{if((a[238296]&1)!=0){if((c[59578]|0)==1){c[11678]=(c[11678]|0)+((d|0)/2&-1);break}else{c[11678]=(c[11678]|0)+d;break}}}while(0);a[238320]=0;return}function nM(){var b=0;if(a[920]|0){b=c[10030]|0;aI(170576,11,1,b|0)}aI(169584,19,1,c[10030]|0);return}function nN(){var a=0,b=0,d=0.0,e=0.0;a=i;b=c[3524]|0;d=+((c[b+8>>2]|0)>>>0>>>0)/60.0;e=+((c[b+12>>2]|0)>>>0>>>0)/60.0;cf(c[10030]|0,240336,(v=i,i=i+72|0,h[v>>3]=d,h[v+8>>3]=e,h[v+16>>3]=d,h[v+24>>3]=e,c[v+32>>2]=170312,h[v+40>>3]=1.456,h[v+48>>3]=.0351,h[v+56>>3]=1.0,c[v+64>>2]=169624,v)|0);cf(c[10030]|0,239344,(v=i,i=i+8|0,c[v>>2]=7,v)|0);cf(c[10030]|0,169824,(v=i,i=i+8|0,c[v>>2]=7,v)|0);cf(c[10030]|0,169776,(v=i,i=i+24|0,c[v>>2]=103960,c[v+8>>2]=169624,c[v+16>>2]=170112,v)|0);cf(c[10030]|0,169776,(v=i,i=i+24|0,c[v>>2]=170288,c[v+8>>2]=170288,c[v+16>>2]=170104,v)|0);cf(c[10030]|0,169776,(v=i,i=i+24|0,c[v>>2]=170224,c[v+8>>2]=170224,c[v+16>>2]=170080,v)|0);cf(c[10030]|0,169776,(v=i,i=i+24|0,c[v>>2]=40160,c[v+8>>2]=40160,c[v+16>>2]=170008,v)|0);cf(c[10030]|0,169776,(v=i,i=i+24|0,c[v>>2]=170216,c[v+8>>2]=170216,c[v+16>>2]=169992,v)|0);cf(c[10030]|0,169776,(v=i,i=i+24|0,c[v>>2]=170208,c[v+8>>2]=170208,c[v+16>>2]=169976,v)|0);cf(c[10030]|0,169776,(v=i,i=i+24|0,c[v>>2]=174448,c[v+8>>2]=174448,c[v+16>>2]=169952,v)|0);aI(169632,79,1,c[10030]|0);i=a;return}function nO(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0;e=i;c[59834]=b;c[59832]=d;if(a[920]|0){f=c[10030]|0;aI(170576,11,1,f|0);g=c[59834]|0;j=c[59832]|0}else{g=b;j=d}a[920]=0;d=c[11664]|0;b=c[35224+(d<<2)>>2]|0;f=c[35256+(d<<2)>>2]|0;cf(c[10030]|0,169856,(v=i,i=i+48|0,c[v>>2]=b,c[v+8>>2]=f,c[v+16>>2]=b,c[v+24>>2]=f,h[v+32>>3]=+(g>>>0>>>0)/60.0,h[v+40>>3]=+(j>>>0>>>0)/60.0,v)|0);i=e;return}function nP(b,d){b=b|0;d=d|0;var e=0,f=0,g=0;e=i;c[59834]=b;c[59832]=d;a[920]=1;f=c[11664]|0;g=c[35256+(f<<2)>>2]|0;cf(c[10030]|0,170144,(v=i,i=i+32|0,c[v>>2]=c[35224+(f<<2)>>2],c[v+8>>2]=g,h[v+16>>3]=+(b>>>0>>>0)/60.0,h[v+24>>3]=+(d>>>0>>>0)/60.0,v)|0);i=e;return}function nQ(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0;f=i;j=a[e]|0;if(j<<24>>24==0){i=f;return}else{k=0;l=j}while(1){j=k+1|0;if(l<<24>>24==0){break}k=j;l=a[e+j|0]|0}if(a[920]|0){l=c[10030]|0;aI(170576,11,1,l|0)}a[920]=0;cf(c[10030]|0,170552,(v=i,i=i+8|0,c[v>>2]=103960,v)|0);m=+g[11668];l=c[11666]|0;do{if(m!=90.0){if((l|0)==0){n=+(d>>>0>>>0)-31.2;o=n;p=+(b>>>0>>>0)+ +(k|0)*70.55999999999999;q=n;r=+(b>>>0>>>0);break}else if((l|0)==2){n=+(d>>>0>>>0)-31.2;o=n;p=+(b>>>0>>>0);q=n;r=+(b>>>0>>>0)- +(k|0)*70.55999999999999;break}else if((l|0)==1){n=+(d>>>0>>>0)-31.2;o=n;p=+(b>>>0>>>0);q=n;r=+(b>>>0>>>0)- +(k|0)*70.55999999999999*.5;break}else{o=1.0;p=1.0;q=1.0;r=1.0;break}}else{if((l|0)==2){n=+(b>>>0>>>0)+31.2;o=+(d>>>0>>>0);p=n;q=+(d>>>0>>>0)- +(k|0)*100.8;r=n;break}else if((l|0)==0){n=+(b>>>0>>>0)+31.2;o=+(d>>>0>>>0)+ +(k|0)*70.55999999999999;p=n;q=+(d>>>0>>>0);r=n;break}else if((l|0)==1){n=+(b>>>0>>>0)+31.2;o=+(d>>>0>>>0);p=n;q=+(d>>>0>>>0)- +(k|0)*70.55999999999999*.5;r=n;break}else{o=1.0;p=1.0;q=1.0;r=1.0;break}}}while(0);cf(c[10030]|0,170480,(v=i,i=i+48|0,h[v>>3]=r/60.0,h[v+8>>3]=q/60.0,h[v+16>>3]=1.456,c[v+24>>2]=e,h[v+32>>3]=m,c[v+40>>2]=170312,v)|0);e=c[11666]|0;if((e|0)==0){i=f;return}cf(c[10030]|0,170424,(v=i,i=i+24|0,c[v>>2]=e,h[v+8>>3]=p/60.0,h[v+16>>3]=o/60.0,v)|0);i=f;return}function nR(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0.0,aA=0.0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0.0,aV=0,aW=0,aX=0.0,aY=0,aZ=0,a_=0,a$=0.0,a0=0.0,a1=0.0,a2=0;b=i;i=i+192|0;d=b|0;e=b+24|0;f=b+48|0;j=b+72|0;k=b+96|0;l=b+120|0;m=b+144|0;n=b+168|0;o=c[13898]|0;p=o-1|0;q=c[1054]|0;r=c[q+(p*40&-1)+36>>2]|0;L18121:do{if((a[q+(p*40&-1)|0]&1)!=0&(r|0)>0){s=c[10036]|0;t=0;u=0;w=c[q+(p*40&-1)+32>>2]|0;while(1){if((a[t+171032|0]|0)==(a[s+(t+w|0)|0]|0)){x=w;y=u}else{if((t|0)!=7){z=13384;break L18121}x=w-1|0;y=1}A=t+1|0;if((A|0)<(y+r|0)){t=A;u=y;w=x}else{break}}if((y|0)!=0){B=o;break}if((t|0)==6|(t|0)==10){B=o}else{z=13384}}else{z=13384}}while(0);if((z|0)==13384){c[(c[3524]|0)+8>>2]=27002;c[(c[3524]|0)+12>>2]=20252;a[46232]=0;a[45624]=0;c[11564]=16777215;a[45056]=0;B=c[13898]|0}o=c[8272]|0;L18134:do{if((B|0)<(o|0)){y=f|0;x=f+8|0;r=l|0;p=l+8|0;q=m|0;w=m+8|0;u=e|0;s=e+8|0;A=d|0;C=d+8|0;D=j|0;E=j+8|0;F=k|0;G=k+8|0;H=0;I=B;J=o;L18136:while(1){K=c[1054]|0;L=a[K+(I*40&-1)|0]|0;M=(L&1)==0;N=c[K+(I*40&-1)+36>>2]|0;O=K+(I*40&-1)+32|0;L18138:do{if(M){P=c[O>>2]|0;z=13456}else{Q=c[10036]|0;R=0;while(1){if((R|0)>=(N|0)){z=13392;break}if((a[Q+((c[O>>2]|0)+R|0)|0]|0)==(a[R+103664|0]|0)){R=R+1|0}else{break}}if((z|0)==13392){z=0;if((R|0)==1){S=H;T=I;break L18134}}Q=c[O>>2]|0;if(!((N|0)>0&(M^1))){P=Q;z=13456;break}U=c[10036]|0;V=0;W=0;X=Q;while(1){if((a[V+170976|0]|0)==(a[U+(V+X|0)|0]|0)){Y=X;Z=W}else{if((V|0)!=2){break}Y=X-1|0;Z=1}_=V+1|0;if((_|0)<(Z+N|0)){V=_;W=Z;X=Y}else{z=13399;break}}do{if((z|0)==13399){z=0;if((Z|0)==0){if(!((V|0)==1|(V|0)==7)){break}}a[45952]=a[216600]|0;a[45953|0]=a[216601|0]|0;a[45954|0]=a[216602|0]|0;a[45955|0]=a[216603|0]|0;a[45956|0]=a[216604|0]|0;a[45957|0]=a[216605|0]|0;g[11486]=12.0;a[45624]=0;a[46232]=0;X=(c[3524]|0)+96|0;c[X>>2]=c[X>>2]&-1025;c[13898]=(c[13898]|0)+1;$=H;break L18138}}while(0);if(M){P=Q;z=13456;break}L18160:do{if((N|0)>0){V=c[10036]|0;X=0;W=0;U=Q;while(1){if((a[X+184472|0]|0)==(a[V+(X+U|0)|0]|0)){aa=U;ab=W}else{if((X|0)!=1){break L18160}aa=U-1|0;ab=1}R=X+1|0;if((R|0)<(ab+N|0)){X=R;W=ab;U=aa}else{break}}if((ab|0)==0){if(!((X|0)==0|(X|0)==10)){break}}a[45624]=1;U=(c[3524]|0)+96|0;c[U>>2]=c[U>>2]|1024;c[13898]=(c[13898]|0)+1;$=H;break L18138}}while(0);if(!((N|0)>0&(M^1))){P=Q;z=13456;break}U=c[10036]|0;W=0;V=0;R=Q;while(1){if((a[W+115600|0]|0)==(a[U+(W+R|0)|0]|0)){ac=R;ad=V}else{if((W|0)!=1){z=13421;break}ac=R-1|0;ad=1}_=W+1|0;if((_|0)<(ad+N|0)){W=_;V=ad;R=ac}else{z=13419;break}}do{if((z|0)==13419){z=0;if((ad|0)!=0){break}if(!((W|0)==0|(W|0)==5)){z=13421}}}while(0);L18183:do{if((z|0)==13421){z=0;if(M){P=Q;z=13456;break L18138}L18186:do{if((N|0)>0){W=c[10036]|0;R=0;V=0;U=Q;while(1){if((a[R+115536|0]|0)==(a[W+(R+U|0)|0]|0)){ae=U;af=V}else{if((R|0)!=1){break L18186}ae=U-1|0;af=1}_=R+1|0;if((_|0)<(af+N|0)){R=_;V=af;U=ae}else{break}}if((af|0)!=0){break L18183}if((R|0)==0|(R|0)==6){break L18183}}}while(0);if(!((N|0)>0&(M^1))){P=Q;z=13456;break L18138}X=c[10036]|0;U=0;V=0;W=Q;while(1){if((a[U+129104|0]|0)==(a[X+(U+W|0)|0]|0)){ag=W;ah=V}else{if((U|0)!=2){break}ag=W-1|0;ah=1}_=U+1|0;if((_|0)<(ah+N|0)){U=_;V=ah;W=ag}else{z=13437;break}}do{if((z|0)==13437){z=0;if((ah|0)==0){if(!((U|0)==1|(U|0)==6)){break}}a[46232]=1;c[13898]=I+1;$=H;break L18138}}while(0);if(M){P=Q;z=13456;break L18138}L18211:do{if((N|0)>0){U=c[10036]|0;W=0;V=0;X=Q;while(1){if((a[W+217776|0]|0)==(a[U+(W+X|0)|0]|0)){ai=X;aj=V}else{if((W|0)!=5){break L18211}ai=X-1|0;aj=1}_=W+1|0;if((_|0)<(aj+N|0)){W=_;V=aj;X=ai}else{break}}if((aj|0)==0){if(!((W|0)==4|(W|0)==7)){break}}a[45616]=1;c[13898]=I+1;$=H;break L18138}}while(0);if(!((N|0)>0&(M^1))){P=Q;z=13456;break L18138}X=c[10036]|0;V=0;do{if((a[V+137232|0]|0)!=(a[X+(V+Q|0)|0]|0)){P=Q;z=13456;break L18138}V=V+1|0;}while((V|0)<(N|0));if((V|0)!=4){P=Q;z=13456;break L18138}a[45616]=0;c[13898]=I+1;$=H;break L18138}}while(0);a[45624]=0;Q=(c[3524]|0)+96|0;c[Q>>2]=c[Q>>2]&-1025;c[13898]=(c[13898]|0)+1;$=H}}while(0);L18232:do{if((z|0)==13456){z=0;O=(J|0)>(I|0);L18234:do{if(O){if(M){ak=I;al=K;am=N;an=P;ao=L;break}Q=c[10036]|0;X=0;while(1){if((X|0)>=(N|0)){break}if((a[Q+(P+X|0)|0]|0)==(a[X+128480|0]|0)){X=X+1|0}else{z=13462;break L18234}}if((X|0)==2){z=13470}else{z=13462}}else{z=13462}}while(0);L18241:do{if((z|0)==13462){z=0;L18243:do{if((N|0)>0&(M^1)){Q=c[10036]|0;V=0;U=0;R=P;while(1){if((a[V+128560|0]|0)==(a[Q+(V+R|0)|0]|0)){ap=R;aq=U}else{if((V|0)!=5){break}ap=R-1|0;aq=1}_=V+1|0;if((_|0)<(aq+N|0)){V=_;U=aq;R=ap}else{z=13468;break}}if((z|0)==13468){z=0;if((aq|0)!=0){z=13470;break L18241}if((V|0)==4|(V|0)==10){z=13470;break L18241}}if(!((N|0)>0&(M^1))){break}R=c[10036]|0;U=0;Q=0;W=P;while(1){if((a[U+115496|0]|0)==(a[R+(U+W|0)|0]|0)){ar=W;as=Q}else{if((U|0)!=1){break L18243}ar=W-1|0;as=1}_=U+1|0;if((_|0)<(as+N|0)){U=_;Q=as;W=ar}else{break}}if((as|0)==0){if(!((U|0)==0|(U|0)==5)){break}}a[46232]=0;c[13898]=I+1;$=H;break L18232}}while(0);L18267:do{if(O){if(M){ak=I;al=K;am=N;an=P;ao=L;break L18241}X=c[10036]|0;W=0;while(1){if((W|0)>=(N|0)){break}if((a[X+(P+W|0)|0]|0)==(a[W+128296|0]|0)){W=W+1|0}else{z=13494;break L18267}}if((W|0)!=2){z=13494}}else{z=13494}}while(0);do{if((z|0)==13494){z=0;if(!((N|0)>0&(M^1))){ak=I;al=K;am=N;an=P;ao=L;break L18241}X=c[10036]|0;U=0;Q=0;R=P;while(1){if((a[U+128400|0]|0)==(a[X+(U+R|0)|0]|0)){at=R;au=Q}else{if((U|0)!=5){break}at=R-1|0;au=1}V=U+1|0;if((V|0)<(au+N|0)){U=V;Q=au;R=at}else{z=13500;break}}if((z|0)==13500){z=0;if((au|0)!=0){break}if((U|0)==4|(U|0)==9){break}}if(M){ak=I;al=K;am=N;an=P;ao=L;break L18241}L18288:do{if((N|0)>0){R=c[10036]|0;Q=0;X=0;W=P;while(1){if((a[Q+122880|0]|0)==(a[R+(Q+W|0)|0]|0)){av=W;aw=X}else{if((Q|0)!=3){break L18288}av=W-1|0;aw=1}V=Q+1|0;if((V|0)<(aw+N|0)){Q=V;X=aw;W=av}else{break}}if((aw|0)==0){if(!((Q|0)==8|(Q|0)==2)){break}}c[13898]=I+1;c[(c[3524]|0)+68>>2]=124;W=(c[3524]|0)+96|0;c[W>>2]=c[W>>2]|32;$=H;break L18232}}while(0);if(!((N|0)>0&(M^1))){ak=I;al=K;am=N;an=P;ao=L;break L18241}U=c[10036]|0;W=0;X=0;R=P;while(1){if((a[W+121536|0]|0)==(a[U+(W+R|0)|0]|0)){ax=R;ay=X}else{if((W|0)!=5){ak=I;al=K;am=N;an=P;ao=L;break L18241}ax=R-1|0;ay=1}V=W+1|0;if((V|0)<(ay+N|0)){W=V;X=ay;R=ax}else{break}}if((ay|0)==0){if(!((W|0)==4|(W|0)==10)){ak=I;al=K;am=N;an=P;ao=L;break L18241}}c[13898]=I+1;c[(c[3524]|0)+68>>2]=102;R=(c[3524]|0)+96|0;c[R>>2]=c[R>>2]&-33;R=c[13898]|0;X=c[1054]|0;ak=R;al=X;am=c[X+(R*40&-1)+36>>2]|0;an=c[X+(R*40&-1)+32>>2]|0;ao=a[X+(R*40&-1)|0]|0;break L18241}}while(0);c[13898]=I+1;R=is(j)|0;X=c[R>>2]|0;if((X|0)==1){az=+(c[R+8>>2]|0)}else if((X|0)==2){az=+h[R+8>>3]}else if((X|0)==3){az=+uz(c[R+8>>2]|0,0)}else{z=13506;break L18136}if((c[D>>2]|0)==3){uu(c[E>>2]|0);c[D>>2]=1}h[5704]=az;if(az>=.1){$=H;break L18232}h[5704]=1.0;$=H;break L18232}}while(0);if((z|0)==13470){z=0;c[13898]=I+1;O=is(k)|0;R=c[O>>2]|0;if((R|0)==2){aA=+h[O+8>>3]}else if((R|0)==1){aA=+(c[O+8>>2]|0)}else if((R|0)==3){aA=+uz(c[O+8>>2]|0,0)}else{z=13474;break L18136}if((c[F>>2]|0)==3){uu(c[G>>2]|0);c[F>>2]=1}h[5778]=aA;if(aA>=.5){$=H;break}h[5778]=1.0;$=H;break}L18334:do{if((ao&1)!=0&(am|0)>0){O=c[10036]|0;R=0;X=0;U=an;while(1){if((a[R+170936|0]|0)==(a[O+(R+U|0)|0]|0)){aB=U;aC=X}else{if((R|0)!=4){aD=H;aE=ak;aF=al;aG=am;aH=an;aI=ao;break L18334}aB=U-1|0;aC=1}V=R+1|0;if((V|0)<(aC+am|0)){R=V;X=aC;U=aB}else{break}}if((aC|0)==0){if(!((R|0)==3|(R|0)==10)){aD=H;aE=ak;aF=al;aG=am;aH=an;aI=ao;break}}c[13898]=ak+1;U=hK()|0;c[11564]=U>>>16&255|U&65280|U<<16&16711680;X=c[13898]|0;O=c[1054]|0;aD=U;aE=X;aF=O;aG=c[O+(X*40&-1)+36>>2]|0;aH=c[O+(X*40&-1)+32>>2]|0;aI=a[O+(X*40&-1)|0]|0}else{aD=H;aE=ak;aF=al;aG=am;aH=an;aI=ao}}while(0);L18347:do{if((aI&1)!=0&(aG|0)>0){X=c[10036]|0;O=0;U=0;V=aH;while(1){if((a[O+170920|0]|0)==(a[X+(O+V|0)|0]|0)){aJ=V;aK=U}else{if((O|0)!=5){aL=aE;aM=aG;aN=aH;aO=aI;break L18347}aJ=V-1|0;aK=1}_=O+1|0;if((_|0)<(aK+aG|0)){O=_;U=aK;V=aJ}else{break}}if((aK|0)==0){if(!((O|0)==4|(O|0)==14)){aL=aE;aM=aG;aN=aH;aO=aI;break}}V=aE+1|0;c[13898]=V;a[45056]=1;aL=V;aM=c[aF+(V*40&-1)+36>>2]|0;aN=c[aF+(V*40&-1)+32>>2]|0;aO=a[aF+(V*40&-1)|0]|0}else{aL=aE;aM=aG;aN=aH;aO=aI}}while(0);V=(aO&1)==0;L18360:do{if((aM|0)>0&(V^1)){U=c[10036]|0;X=0;R=0;_=aN;while(1){if((a[X+90488|0]|0)==(a[U+(X+_|0)|0]|0)){aP=_;aQ=R}else{if((X|0)!=2){break L18360}aP=_-1|0;aQ=1}aR=X+1|0;if((aR|0)<(aQ+aM|0)){X=aR;R=aQ;_=aP}else{break}}if((aQ|0)==0){if(!((X|0)==4|(X|0)==1)){break}}_=aL+1|0;c[13898]=_;L18372:do{if((_|0)<(c[8272]|0)){L18374:do{if((a[aF+(_*40&-1)|0]&1)!=0){R=c[aF+(_*40&-1)+36>>2]|0;U=aF+(_*40&-1)+32|0;O=c[10036]|0;aR=0;while(1){if((aR|0)>=(R|0)){break}if((a[O+((c[U>>2]|0)+aR|0)|0]|0)==(a[aR+103664|0]|0)){aR=aR+1|0}else{break L18374}}if((aR|0)==1){aS=1024;aT=768;z=13584;break L18372}}}while(0);W=is(e)|0;U=c[W>>2]|0;if((U|0)==1){aU=+(c[W+8>>2]|0)}else if((U|0)==2){aU=+h[W+8>>3]}else if((U|0)==3){aU=+uz(c[W+8>>2]|0,0)}else{z=13566;break L18136}if((c[u>>2]|0)==3){uu(c[s>>2]|0);c[u>>2]=1}W=~~aU;U=c[13898]|0;L18389:do{if((c[8272]|0)>(U|0)){O=c[1054]|0;if((a[O+(U*40&-1)|0]&1)==0){aV=768;break}R=c[O+(U*40&-1)+36>>2]|0;Q=O+(U*40&-1)+32|0;O=c[10036]|0;aW=0;while(1){if((aW|0)>=(R|0)){break}if((a[O+((c[Q>>2]|0)+aW|0)|0]|0)==(a[aW+148464|0]|0)){aW=aW+1|0}else{aV=768;break L18389}}if((aW|0)!=1){aV=768;break}c[13898]=U+1;Q=is(d)|0;O=c[Q>>2]|0;if((O|0)==3){aX=+uz(c[Q+8>>2]|0,0)}else if((O|0)==1){aX=+(c[Q+8>>2]|0)}else if((O|0)==2){aX=+h[Q+8>>3]}else{z=13579;break L18136}if((c[A>>2]|0)==3){uu(c[C>>2]|0);c[A>>2]=1}aV=~~aX}else{aV=768}}while(0);if((W|0)>0){aS=W;aT=aV;z=13584}else{aY=aV}}else{aS=1024;aT=768;z=13584}}while(0);if((z|0)==13584){z=0;c[(c[3524]|0)+8>>2]=~~(+(aS|0)*26.37);aY=aT}if((aY|0)>0){c[(c[3524]|0)+12>>2]=~~(+(aY|0)*26.37)}_=c[3524]|0;c[_+28>>2]=((c[_+8>>2]|0)>>>0)/160>>>0;_=c[3524]|0;c[_+24>>2]=c[_+28>>2];$=aD;break L18232}}while(0);_=c[8272]|0;X=(_|0)>(aL|0);L18413:do{if(V|X^1){aZ=aL}else{U=c[10036]|0;Q=0;while(1){if((Q|0)>=(aM|0)){z=13592;break}if((a[U+(aN+Q|0)|0]|0)==(a[Q+128096|0]|0)){Q=Q+1|0}else{break}}do{if((z|0)==13592){z=0;if((Q|0)!=9){break}O=aL+1|0;c[13898]=O;L18421:do{if((O|0)<(_|0)){L18423:do{if((a[aF+(O*40&-1)|0]&1)!=0){R=c[aF+(O*40&-1)+36>>2]|0;aR=aF+(O*40&-1)+32|0;a_=0;while(1){if((a_|0)>=(R|0)){break}if((a[U+((c[aR>>2]|0)+a_|0)|0]|0)==(a[a_+103664|0]|0)){a_=a_+1|0}else{break L18423}}if((a_|0)==1){z=13606;break L18421}}}while(0);aW=is(f)|0;aR=c[aW>>2]|0;if((aR|0)==1){a$=+(c[aW+8>>2]|0)}else if((aR|0)==2){a$=+h[aW+8>>3]}else if((aR|0)==3){a$=+uz(c[aW+8>>2]|0,0)}else{z=13603;break L18136}if((c[y>>2]|0)==3){uu(c[x>>2]|0);c[y>>2]=1}h[5710]=a$;if(a$>0.0){$=aD;break L18232}}else{z=13606}}while(0);if((z|0)==13606){z=0;h[5710]=-1.0}h[5710]=1.0;$=aD;break L18232}}while(0);if(V|X^1){aZ=aL;break}U=c[10036]|0;Q=0;while(1){if((Q|0)>=(aM|0)){break}if((a[U+(aN+Q|0)|0]|0)==(a[Q+124352|0]|0)){Q=Q+1|0}else{aZ=aL;break L18413}}if((Q|0)!=4){aZ=aL;break}U=aL+1|0;c[13898]=U;aZ=U}}while(0);if((aZ|0)>=(_|0)){S=aD;T=aZ;break L18134}L18450:do{if((a[aF+(aZ*40&-1)|0]&1)!=0){X=c[aF+(aZ*40&-1)+36>>2]|0;V=aF+(aZ*40&-1)+32|0;U=c[10036]|0;O=0;while(1){if((O|0)>=(X|0)){break}if((a[U+((c[V>>2]|0)+O|0)|0]|0)==(a[O+103664|0]|0)){O=O+1|0}else{break L18450}}if((O|0)==1){S=aD;T=aZ;break L18134}}}while(0);a[14176]=1;is(l);a[14176]=0;if((c[r>>2]|0)!=3){z=13622;break L18136}_=c[p>>2]|0;if((_|0)==0){z=13624;break L18136}V=bh(_|0,44)|0;do{if((V|0)!=0){if((ca(V+1|0,170912,(v=i,i=i+8|0,c[v>>2]=45944,v)|0)|0)!=1){break}a[V]=0}}while(0);if((a[_]|0)!=0){uF(45952,_|0,255)}uu(_);if((a[(c[1054]|0)+((c[13898]|0)*40&-1)|0]&1)!=0){$=aD;break}V=is(m)|0;U=c[V>>2]|0;if((U|0)==2){a0=+h[V+8>>3]}else if((U|0)==3){a0=+uz(c[V+8>>2]|0,0)}else if((U|0)==1){a0=+(c[V+8>>2]|0)}else{z=13635;break L18136}if((c[q>>2]|0)==3){uu(c[w>>2]|0);c[q>>2]=1}g[11486]=+(~~a0|0);$=aD}}while(0);L=c[13898]|0;N=c[8272]|0;if((L|0)<(N|0)){H=$;I=L;J=N}else{S=$;T=L;break L18134}}if((z|0)==13603){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==13622){c[13898]=aZ;S=aD;T=aZ;break}else if((z|0)==13579){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==13624){S=aD;T=c[13898]|0;break}else if((z|0)==13474){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==13566){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==13506){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((z|0)==13635){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{S=0;T=B}}while(0);L18486:do{if((T|0)<(c[8272]|0)){B=c[1054]|0;L18488:do{if((a[B+(T*40&-1)|0]&1)!=0){z=c[B+(T*40&-1)+36>>2]|0;aD=B+(T*40&-1)+32|0;aZ=c[10036]|0;$=0;while(1){if(($|0)>=(z|0)){break}if((a[aZ+((c[aD>>2]|0)+$|0)|0]|0)==(a[$+103664|0]|0)){$=$+1|0}else{break L18488}}if(($|0)==1){break L18486}}}while(0);B=is(n)|0;aD=c[B>>2]|0;if((aD|0)==1){a1=+(c[B+8>>2]|0)}else if((aD|0)==2){a1=+h[B+8>>3]}else if((aD|0)==3){a1=+uz(c[B+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}B=n|0;if((c[B>>2]|0)==3){uu(c[n+8>>2]|0);c[B>>2]=1}g[11486]=+(~~a1|0)}}while(0);n4(0);n=a[46232]|0?131584:131504;T=a[45616]|0?137776:137232;a1=+g[11486];be(13048,170872,(v=i,i=i+40|0,c[v>>2]=a[45624]|0?116408:116440,c[v+8>>2]=n,c[v+16>>2]=T,c[v+24>>2]=45952,h[v+32>>3]=a1,v)|0);T=c[3524]|0;if((c[T+96>>2]&32|0)!=0){uD(13048+(uA(13048)|0)|0,170856,11)}a1=+h[5710];if(a1!=1.0){n=13048+(uA(13048)|0)|0;be(n|0,170824,(v=i,i=i+8|0,h[v>>3]=a1,v)|0);a2=c[3524]|0}else{a2=T}T=c[a2+8>>2]|0;n=c[a2+12>>2]|0;if(!((T|0)==27002&(n|0)==20252)){a2=13048+(uA(13048)|0)|0;B=~~(+(T>>>0>>>0)/26.37+.5);T=~~(+(n>>>0>>>0)/26.37+.5);be(a2|0,170728,(v=i,i=i+16|0,c[v>>2]=B,c[v+8>>2]=T,v)|0)}a1=+h[5704];if(a1!=1.0){T=13048+(uA(13048)|0)|0;be(T|0,170680,(v=i,i=i+8|0,h[v>>3]=a1,v)|0)}a1=+h[5778];if(a1!=1.0){T=13048+(uA(13048)|0)|0;be(T|0,170640,(v=i,i=i+8|0,h[v>>3]=a1,v)|0)}if((S|0)==0){i=b;return}be(13048+(uA(13048)|0)|0,170616,(v=i,i=i+8|0,c[v>>2]=S,v)|0);i=b;return}function nS(){c[11296]=0;c[11298]=0;c[11412]=0;c[11262]=0;a[45664]=0;return}function nT(){c[11296]=0;c[11298]=0;a[45664]=0;return}function nU(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0;b=i;i=i+8|0;d=b|0;e=d;tN();a[45664]=0;f=d;g=e+3|0;a[g]=0;h=e+2|0;a[h]=0;j=e+1|0;a[j]=0;a[f]=27;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=16;aI(f|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;e=c[11300]|0;a[g]=e>>>24&255;a[h]=e>>>16&255;a[j]=e>>>8&255;a[f]=e&255;aI(f|0,1,4,c[10030]|0);e=(c[(c[3524]|0)+12>>2]|0)-(c[11301]|0)|0;a[g]=e>>>24&255;a[h]=e>>>16&255;a[j]=e>>>8&255;a[f]=e&255;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=37;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=12;aI(f|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[g]=-128;a[h]=0;a[j]=0;a[f]=10;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=40;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=12;aI(f|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[g]=0;a[h]=0;a[j]=0;a[f]=2;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=37;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=12;aI(f|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[g]=-128;a[h]=0;a[j]=0;a[f]=7;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=40;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=12;aI(f|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[g]=0;a[h]=0;a[j]=0;a[f]=1;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=37;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=12;aI(f|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[g]=-128;a[h]=0;a[j]=0;a[f]=0;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=40;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=12;aI(f|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[g]=0;a[h]=0;a[j]=0;a[f]=3;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=14;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=20;aI(f|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;c[d>>2]=0;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=16;aI(f|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[f]=20;aI(f|0,1,4,c[10030]|0);d=bc(c[10030]|0)|0;cp(c[10030]|0,48,0);a[g]=d>>>24&255;a[h]=d>>>16&255;a[j]=d>>>8&255;a[f]=d&255;aI(f|0,1,4,c[10030]|0);d=c[11294]|0;a[g]=d>>>24&255;a[h]=d>>>16&255;a[j]=d>>>8&255;a[f]=d&255;aI(f|0,1,4,c[10030]|0);cp(c[10030]|0,0,0);i=b;return}function nV(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0.0,k=0,l=0.0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;b=i;i=i+64|0;d=b|0;e=b+48|0;f=b+56|0;g=f;h=c[3524]|0;j=+((c[h+8>>2]|0)>>>0>>>0)/26.37;k=~~(j+.5);l=+((c[h+12>>2]|0)>>>0>>>0)/26.37;h=~~(l+.5);m=~~(j*.263671875+.5);n=~~(l*.2604166666666667+.5);c[11294]=0;o=f;p=g+3|0;a[p]=0;q=g+2|0;a[q]=0;r=g+1|0;a[r]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=100;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);g=~~(+((c[(c[3524]|0)+8>>2]|0)>>>0>>>0)/26.37);a[p]=g>>>24&255;a[q]=g>>>16&255;a[r]=g>>>8&255;a[o]=g&255;aI(o|0,1,4,c[10030]|0);g=~~(+((c[(c[3524]|0)+12>>2]|0)>>>0>>>0)/26.37);a[p]=g>>>24&255;a[q]=g>>>16&255;a[r]=g>>>8&255;a[o]=g&255;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);g=c[(c[3524]|0)+8>>2]|0;a[p]=g>>>24&255;a[q]=g>>>16&255;a[r]=g>>>8&255;a[o]=g&255;aI(o|0,1,4,c[10030]|0);g=c[(c[3524]|0)+12>>2]|0;a[p]=g>>>24&255;a[q]=g>>>16&255;a[r]=g>>>8&255;a[o]=g&255;aI(o|0,1,4,c[10030]|0);a[p]=70;a[q]=77;a[r]=69;a[o]=32;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=1;a[r]=0;a[o]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);g=e|0;s=e+1|0;a[s]=0;a[g]=4;aI(g|0,1,2,c[10030]|0);a[s]=0;a[g]=0;aI(g|0,1,2,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);a[p]=k>>>24&255;a[q]=k>>>16&255;a[r]=k>>>8&255;a[o]=k&255;aI(o|0,1,4,c[10030]|0);a[p]=h>>>24&255;a[q]=h>>>16&255;a[r]=h>>>8&255;a[o]=h&255;aI(o|0,1,4,c[10030]|0);a[p]=m>>>24&255;a[q]=m>>>16&255;a[r]=m>>>8&255;a[o]=m&255;aI(o|0,1,4,c[10030]|0);a[p]=n>>>24&255;a[q]=n>>>16&255;a[r]=n>>>8&255;a[o]=n&255;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);a[45664]=1;a[p]=0;a[q]=0;a[r]=0;a[o]=17;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[p]=0;a[q]=0;a[r]=0;a[o]=8;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=9;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=16;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;n=c[(c[3524]|0)+8>>2]|0;a[p]=n>>>24&255;a[q]=n>>>16&255;a[r]=n>>>8&255;a[o]=n&255;aI(o|0,1,4,c[10030]|0);n=c[(c[3524]|0)+12>>2]|0;a[p]=n>>>24&255;a[q]=n>>>16&255;a[r]=n>>>8&255;a[o]=n&255;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=11;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=16;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;n=~~(+((c[(c[3524]|0)+8>>2]|0)>>>0>>>0)/26.37);a[p]=n>>>24&255;a[q]=n>>>16&255;a[r]=n>>>8&255;a[o]=n&255;aI(o|0,1,4,c[10030]|0);n=~~(+((c[(c[3524]|0)+12>>2]|0)>>>0>>>0)/26.37);a[p]=n>>>24&255;a[q]=n>>>16&255;a[r]=n>>>8&255;a[o]=n&255;aI(o|0,1,4,c[10030]|0);if((c[11564]|0)!=16777215){n=c[3524]|0;m=c[n+8>>2]|0;h=c[n+12>>2]|0;c[d>>2]=0;c[d+4>>2]=0;c[d+12>>2]=m;c[d+16>>2]=0;c[d+24>>2]=m;c[d+28>>2]=h;c[d+36>>2]=0;c[d+40>>2]=h;c[d+8>>2]=0;n9(4,d|0)}a[p]=0;a[q]=0;a[r]=0;a[o]=38;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=28;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[p]=0;a[q]=0;a[r]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);d=a[45616]|0;a[p]=0;a[q]=0;a[r]=d?0:34;a[o]=0;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=37;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[p]=0;a[q]=0;a[r]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=18;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[p]=0;a[q]=0;a[r]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=39;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=24;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[p]=0;a[q]=0;a[r]=0;a[o]=3;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=37;aI(o|0,1,4,c[10030]|0);a[p]=0;a[q]=0;a[r]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[p]=0;a[q]=0;a[r]=0;a[o]=3;aI(o|0,1,4,c[10030]|0);n4(0);c[11280]=-5;c[11562]=-5;i=b;return}function nW(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=i;e=c[3524]|0;if((c[e+8>>2]|0)>>>0>a>>>0){if((c[e+12>>2]|0)>>>0>b>>>0){f=b;g=a}else{h=13676}}else{h=13676}if((h|0)==13676){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);h=c[3524]|0;e=c[h+8>>2]|0;j=c[h+12>>2]|0;f=j>>>0>b>>>0?b:j;g=e>>>0>a>>>0?a:e}if((g|0)==(c[11298]|0)&(f|0)==(c[11296]|0)){i=d;return}tN();c[11298]=g;c[11296]=f;i=d;return}function nX(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;e=c[3524]|0;if((c[e+8>>2]|0)>>>0>a>>>0){if((c[e+12>>2]|0)>>>0<=b>>>0){f=13684}}else{f=13684}if((f|0)==13684){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0)}if((c[11298]|0)==(a|0)&(c[11296]|0)==(b|0)){i=d;return}f=c[11560]|0;if((f<<1|0)>102){tN();g=c[11560]|0}else{g=f}if((g|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;h=1}else{h=g}g=h<<1;c[45200+(g<<2)>>2]=a;c[11298]=a;c[45200+((g|1)<<2)>>2]=b;c[11296]=b;c[11560]=h+1;i=d;return}function nY(b){b=b|0;var d=0,e=0,f=0;tN();d=(b|0)==-3?-4:b;c[11412]=d;if((d|0)==-4){e=46256}else{if((d|0)<0|a[45624]){f=7}else{f=(d|0)%15&-1}e=238816+(f<<2)|0}c[11562]=c[e>>2];tN();tL(d);return}function nZ(a){a=a|0;do{if((a|0)==0){if((c[11262]|0)==0){break}c[11262]=0;tO()}else if((a|0)==(-270|0)){if((c[11262]|0)==900){break}c[11262]=900;tO()}else{c[11262]=a*10&-1;tO()}}while(0);return 1}function n_(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=i;i=i+8|0;e=d|0;c[11414]=b;if((b|0)==2){f=10}else if((b|0)==1){f=14}else{f=8}b=e|0;g=e+3|0;a[g]=0;h=e+2|0;a[h]=0;j=e+1|0;a[j]=0;a[b]=22;aI(b|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[b]=12;aI(b|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[g]=0;a[h]=0;a[j]=0;a[b]=f;aI(b|0,1,4,c[10030]|0);i=d;return 1}function n$(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0.0,z=0,A=0,B=0;f=i;i=i+8|0;g=f|0;h=g;j=i;i=i+4|0;i=i+7>>3<<3;k=i;i=i+4|0;i=i+7>>3<<3;l=i;i=i+4|0;i=i+7>>3<<3;m=i;i=i+4|0;i=i+7>>3<<3;n=uA(e|0)|0;do{if(((c[11252]|0)-14|0)>>>0<2){c[l>>2]=e;o=uA(e|0)|0;c[k>>2]=o;p=(o<<1)+2|0;c[j>>2]=p;q=ut(p)|0;do{if((q|0)==0){gk();r=ut(p)|0;if((r|0)!=0){s=r;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=171392,v)|0)}else{s=q}}while(0);c[m>>2]=s;q=bi(171336,((c[11252]|0)==15?171384:171360)|0)|0;if((q|0)==-1){uh(-1,171312,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);t=s;u=o;w=n;break}if((aG(q|0,l|0,k|0,m|0,j|0)|0)==-1){uh(-1,171280,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}cz(q|0);q=p-(c[j>>2]|0)|0;t=s;u=(q|0)/2&-1;w=q}else{t=0;u=n;w=n}}while(0);if((w|0)<1){i=f;return}tN();if((c[11280]|0)!=(c[11562]|0)){n=g;s=h+3|0;a[s]=0;j=h+2|0;a[j]=0;m=h+1|0;a[m]=0;a[n]=24;k=c[10030]|0;aI(n|0,1,4,k|0);a[s]=0;a[j]=0;a[m]=0;a[n]=12;k=c[10030]|0;aI(n|0,1,4,k|0);c[11294]=(c[11294]|0)+1;k=c[11562]|0;a[s]=k>>>24&255;a[j]=k>>>16&255;a[m]=k>>>8&255;a[n]=k&255;k=c[10030]|0;aI(n|0,1,4,k|0);c[11280]=c[11562]}k=(w|0)%4&-1;if((k|0)==0){x=w}else{x=(w+4|0)-k|0}k=((c[11252]|0)-14|0)>>>0<2;n=g;m=h+3|0;a[m]=0;j=h+2|0;a[j]=0;s=h+1|0;a[s]=0;if(k){a[n]=84;k=c[10030]|0;aI(n|0,1,4,k|0);k=((u<<2)+76|0)+x|0;a[m]=k>>>24&255;a[j]=k>>>16&255;a[s]=k>>>8&255;a[n]=k&255;k=c[10030]|0;aI(n|0,1,4,k|0)}else{a[n]=83;k=c[10030]|0;aI(n|0,1,4,k|0);k=((u<<2)+76|0)+x|0;a[m]=k>>>24&255;a[j]=k>>>16&255;a[s]=k>>>8&255;a[n]=k&255;k=c[10030]|0;aI(n|0,1,4,k|0)}c[11294]=(c[11294]|0)+1;c[g>>2]=0;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);a[m]=0;a[j]=0;a[s]=0;a[n]=1;aI(n|0,1,4,c[10030]|0);a[m]=65;a[j]=-46;a[s]=-11;a[n]=-61;aI(n|0,1,4,c[10030]|0);a[m]=65;a[j]=-46;a[s]=-11;a[n]=-61;aI(n|0,1,4,c[10030]|0);y=+((c[(c[3524]|0)+16>>2]|0)>>>1>>>0>>>0);k=~~(y*+T(+(+(c[11262]|0)*.0017453292519944445)))+b|0;a[m]=k>>>24&255;a[j]=k>>>16&255;a[s]=k>>>8&255;a[n]=k&255;aI(n|0,1,4,c[10030]|0);k=c[3524]|0;b=(c[k+12>>2]|0)-d|0;y=+((c[k+16>>2]|0)>>>1>>>0>>>0);k=~~(y*+S(+(+(c[11262]|0)*.0017453292519944445)))+b|0;a[m]=k>>>24&255;a[j]=k>>>16&255;a[s]=k>>>8&255;a[n]=k&255;aI(n|0,1,4,c[10030]|0);a[m]=u>>>24&255;a[j]=u>>>16&255;a[s]=u>>>8&255;a[n]=u&255;aI(n|0,1,4,c[10030]|0);a[m]=0;a[j]=0;a[s]=0;a[n]=76;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);c[g>>2]=0;aI(n|0,1,4,c[10030]|0);g=(x|0)>0;do{if(((c[11252]|0)-14|0)>>>0<2){if(g){k=0;do{if((k|0)<(w|0)){z=a[t+k|0]|0}else{z=0}aF(z|0,c[10030]|0);k=k+1|0;}while((k|0)<(x|0))}uu(t)}else{if(g){A=0}else{break}do{if((A|0)<(w|0)){B=a[e+A|0]|0}else{B=0}aF(B|0,c[10030]|0);A=A+1|0;}while((A|0)<(x|0))}}while(0);if((u|0)>0){x=0;do{a[m]=0;a[j]=0;a[s]=1;a[n]=44;aI(n|0,1,4,c[10030]|0);x=x+1|0;}while((x|0)<(u|0))}c[11296]=-2e3;c[11298]=-2e3;i=f;return}function n0(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aY=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0,bA=0,bB=0,bC=0,bD=0,bE=0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bM=0,bN=0,bO=0,bP=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0;f=i;i=i+8|0;g=f|0;h=g;j=i;i=i+144|0;k=j|0;c[j+8>>2]=1601;tN();l=c[11554]|0;c[11554]=0;c[11552]=(c[11552]|0)+1;if((e-69|0)>>>0<5){m=c[11562]|0;c[11562]=c[11564];if((e|0)==71){n0(b,d,8);c[11562]=m;n0(b,d,7);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((e|0)==73){n0(b,d,12);c[11562]=m;n0(b,d,11);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((e|0)==69){n0(b,d,4);c[11562]=m;n0(b,d,3);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((e|0)==70){n0(b,d,6);c[11562]=m;n0(b,d,5);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((e|0)==72){n0(b,d,10);c[11562]=m;n0(b,d,9);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else{c[11562]=m;c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}}m=c[3524]|0;if((c[m+8>>2]|0)>>>0>b>>>0){if((c[m+12>>2]|0)>>>0>d>>>0){p=d;q=b;r=m}else{s=13753}}else{s=13753}if((s|0)==13753){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);m=c[3524]|0;t=c[m+8>>2]|0;u=c[m+12>>2]|0;p=u>>>0>d>>>0?d:u;q=t>>>0>b>>>0?b:t;r=m}if((q|0)==(c[11298]|0)&(p|0)==(c[11296]|0)){w=r}else{tN();c[11298]=q;c[11296]=p;w=c[3524]|0}r=b+1|0;if((c[w+8>>2]|0)>>>0>r>>>0){if((c[w+12>>2]|0)>>>0>d>>>0){x=q;y=p}else{s=13758}}else{s=13758}if((s|0)==13758){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=r,c[v+8>>2]=d,v)|0);x=c[11298]|0;y=c[11296]|0}if(!((x|0)==(r|0)&(y|0)==(d|0))){y=c[11560]|0;if((y<<1|0)>102){tN();z=c[11560]|0}else{z=y}if((z|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;A=1}else{A=z}z=A<<1;c[45200+(z<<2)>>2]=r;c[11298]=r;c[45200+((z|1)<<2)>>2]=d;c[11296]=d;c[11560]=A+1}A=(e|0)%13&-1;if((A|0)==10){c[j>>2]=b;c[j+4>>2]=d-(c[11274]|0);e=c[11276]|0;c[j+12>>2]=b-e;z=(c[11270]|0)+d|0;c[j+16>>2]=z;c[j+24>>2]=e+b;c[j+28>>2]=z;n9(3,k);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==11){z=b-(c[11278]|0)|0;e=c[3524]|0;if((c[e+8>>2]|0)>>>0>z>>>0){if((c[e+12>>2]|0)>>>0>d>>>0){B=d;C=z;D=r;E=d;F=e}else{s=13949}}else{s=13949}if((s|0)==13949){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=z,c[v+8>>2]=d,v)|0);e=c[3524]|0;y=c[e+8>>2]|0;x=c[e+12>>2]|0;B=x>>>0>d>>>0?d:x;C=y>>>0>z>>>0?z:y;D=c[11298]|0;E=c[11296]|0;F=e}if((C|0)==(D|0)&(B|0)==(E|0)){G=F;H=D;I=E}else{tN();c[11298]=C;c[11296]=B;G=c[3524]|0;H=C;I=B}B=d-(c[11278]|0)|0;if((c[G+8>>2]|0)>>>0>b>>>0){if((c[G+12>>2]|0)>>>0>B>>>0){J=H;K=I}else{s=13954}}else{s=13954}if((s|0)==13954){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=B,v)|0);J=c[11298]|0;K=c[11296]|0}if(!((J|0)==(b|0)&(K|0)==(B|0))){K=c[11560]|0;if((K<<1|0)>102){tN();L=c[11560]|0}else{L=K}if((L|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;M=1}else{M=L}L=M<<1;c[45200+(L<<2)>>2]=b;c[11298]=b;c[45200+((L|1)<<2)>>2]=B;c[11296]=B;c[11560]=M+1}M=(c[11278]|0)+b|0;L=c[3524]|0;if((c[L+8>>2]|0)>>>0>M>>>0){if((c[L+12>>2]|0)>>>0>d>>>0){N=b;O=B}else{s=13963}}else{s=13963}if((s|0)==13963){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=M,c[v+8>>2]=d,v)|0);N=c[11298]|0;O=c[11296]|0}if(!((N|0)==(M|0)&(O|0)==(d|0))){O=c[11560]|0;if((O<<1|0)>102){tN();P=c[11560]|0}else{P=O}if((P|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;Q=1}else{Q=P}P=Q<<1;c[45200+(P<<2)>>2]=M;c[11298]=M;c[45200+((P|1)<<2)>>2]=d;c[11296]=d;c[11560]=Q+1}Q=(c[11278]|0)+d|0;P=c[3524]|0;if((c[P+8>>2]|0)>>>0>b>>>0){if((c[P+12>>2]|0)>>>0>Q>>>0){R=M;S=d}else{s=13972}}else{s=13972}if((s|0)==13972){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=Q,v)|0);R=c[11298]|0;S=c[11296]|0}if(!((R|0)==(b|0)&(S|0)==(Q|0))){S=c[11560]|0;if((S<<1|0)>102){tN();T=c[11560]|0}else{T=S}if((T|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;U=1}else{U=T}T=U<<1;c[45200+(T<<2)>>2]=b;c[11298]=b;c[45200+((T|1)<<2)>>2]=Q;c[11296]=Q;c[11560]=U+1}tP();c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==12){U=c[11278]|0;c[j>>2]=b-U;c[j+4>>2]=d;c[j+12>>2]=b;c[j+16>>2]=d-U;c[j+24>>2]=U+b;c[j+28>>2]=d;c[j+36>>2]=b;c[j+40>>2]=U+d;n9(4,k);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==4){U=c[11268]|0;Q=b-U|0;c[j>>2]=Q;T=d-U|0;c[j+4>>2]=T;S=U+b|0;c[j+12>>2]=S;c[j+16>>2]=T;c[j+24>>2]=S;S=U+d|0;c[j+28>>2]=S;c[j+36>>2]=Q;c[j+40>>2]=S;n9(4,k);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==5){S=(c[(c[3524]|0)+12>>2]|0)-d|0;Q=g;U=h+3|0;a[U]=0;T=h+2|0;a[T]=0;R=h+1|0;a[R]=0;a[Q]=37;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=12;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[U]=-128;a[T]=0;a[R]=0;a[Q]=7;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=37;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=12;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[U]=-128;a[T]=0;a[R]=0;a[Q]=0;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=40;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=12;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[U]=0;a[T]=0;a[R]=0;a[Q]=3;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=39;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=24;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[U]=0;a[T]=0;a[R]=0;a[Q]=3;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=1;aI(Q|0,1,4,c[10030]|0);c[g>>2]=0;aI(Q|0,1,4,c[10030]|0);c[g>>2]=0;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=37;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=12;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[U]=0;a[T]=0;a[R]=0;a[Q]=3;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=40;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=12;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[U]=0;a[T]=0;a[R]=0;a[Q]=1;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=38;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=28;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[U]=0;a[T]=0;a[R]=0;a[Q]=1;aI(Q|0,1,4,c[10030]|0);M=a[45616]|0;a[U]=0;a[T]=0;a[R]=M?0:34;a[Q]=0;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=1;aI(Q|0,1,4,c[10030]|0);c[g>>2]=0;aI(Q|0,1,4,c[10030]|0);M=c[11562]|0;a[U]=M>>>24&255;a[T]=M>>>16&255;a[R]=M>>>8&255;a[Q]=M&255;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=37;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=12;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[U]=0;a[T]=0;a[R]=0;a[Q]=1;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=42;aI(Q|0,1,4,c[10030]|0);a[U]=0;a[T]=0;a[R]=0;a[Q]=24;aI(Q|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;M=b-(c[11278]|0)|0;a[U]=M>>>24&255;a[T]=M>>>16&255;a[R]=M>>>8&255;a[Q]=M&255;aI(Q|0,1,4,c[10030]|0);M=S-(c[11278]|0)|0;a[U]=M>>>24&255;a[T]=M>>>16&255;a[R]=M>>>8&255;a[Q]=M&255;aI(Q|0,1,4,c[10030]|0);M=(c[11278]|0)+b|0;a[U]=M>>>24&255;a[T]=M>>>16&255;a[R]=M>>>8&255;a[Q]=M&255;aI(Q|0,1,4,c[10030]|0);M=(c[11278]|0)+S|0;a[U]=M>>>24&255;a[T]=M>>>16&255;a[R]=M>>>8&255;a[Q]=M&255;aI(Q|0,1,4,c[10030]|0);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==6){Q=(c[(c[3524]|0)+12>>2]|0)-d|0;M=g;R=h+3|0;a[R]=0;T=h+2|0;a[T]=0;U=h+1|0;a[U]=0;a[M]=37;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=12;aI(M|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[R]=-128;a[T]=0;a[U]=0;a[M]=0;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=40;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=12;aI(M|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[R]=0;a[T]=0;a[U]=0;a[M]=3;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=39;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=24;aI(M|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[R]=0;a[T]=0;a[U]=0;a[M]=3;aI(M|0,1,4,c[10030]|0);c[g>>2]=0;aI(M|0,1,4,c[10030]|0);h=c[11562]|0;a[R]=h>>>24&255;a[T]=h>>>16&255;a[U]=h>>>8&255;a[M]=h&255;aI(M|0,1,4,c[10030]|0);c[g>>2]=0;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=37;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=12;aI(M|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[R]=0;a[T]=0;a[U]=0;a[M]=3;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=42;aI(M|0,1,4,c[10030]|0);a[R]=0;a[T]=0;a[U]=0;a[M]=24;aI(M|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;g=b-(c[11278]|0)|0;a[R]=g>>>24&255;a[T]=g>>>16&255;a[U]=g>>>8&255;a[M]=g&255;aI(M|0,1,4,c[10030]|0);g=Q-(c[11278]|0)|0;a[R]=g>>>24&255;a[T]=g>>>16&255;a[U]=g>>>8&255;a[M]=g&255;aI(M|0,1,4,c[10030]|0);g=(c[11278]|0)+b|0;a[R]=g>>>24&255;a[T]=g>>>16&255;a[U]=g>>>8&255;a[M]=g&255;aI(M|0,1,4,c[10030]|0);g=(c[11278]|0)+Q|0;a[R]=g>>>24&255;a[T]=g>>>16&255;a[U]=g>>>8&255;a[M]=g&255;aI(M|0,1,4,c[10030]|0);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==7){M=(c[11274]|0)+d|0;g=c[3524]|0;if((c[g+8>>2]|0)>>>0>b>>>0){if((c[g+12>>2]|0)>>>0>M>>>0){V=b;W=M;s=13902}else{s=13901}}else{s=13901}if((s|0)==13901){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=M,v)|0);g=c[3524]|0;U=c[g+8>>2]|0;T=U>>>0>b>>>0?b:U;U=c[g+12>>2]|0;R=U>>>0>M>>>0?M:U;U=c[11298]|0;M=c[11296]|0;if((T|0)==(U|0)&(R|0)==(M|0)){X=g;Y=U;Z=M}else{V=T;W=R;s=13902}}if((s|0)==13902){tN();c[11298]=V;c[11296]=W;X=c[3524]|0;Y=V;Z=W}W=b-(c[11276]|0)|0;V=d-(c[11270]|0)|0;if((c[X+8>>2]|0)>>>0>W>>>0){if((c[X+12>>2]|0)>>>0>V>>>0){_=Y;$=Z}else{s=13905}}else{s=13905}if((s|0)==13905){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=W,c[v+8>>2]=V,v)|0);_=c[11298]|0;$=c[11296]|0}if(!((_|0)==(W|0)&($|0)==(V|0))){$=c[11560]|0;if(($<<1|0)>102){tN();aa=c[11560]|0}else{aa=$}if((aa|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;ab=1}else{ab=aa}aa=ab<<1;c[45200+(aa<<2)>>2]=W;c[11298]=W;c[45200+((aa|1)<<2)>>2]=V;c[11296]=V;c[11560]=ab+1}ab=(c[11276]|0)+b|0;aa=d-(c[11270]|0)|0;$=c[3524]|0;if((c[$+8>>2]|0)>>>0>ab>>>0){if((c[$+12>>2]|0)>>>0>aa>>>0){ac=W;ad=V}else{s=13914}}else{s=13914}if((s|0)==13914){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=ab,c[v+8>>2]=aa,v)|0);ac=c[11298]|0;ad=c[11296]|0}if(!((ac|0)==(ab|0)&(ad|0)==(aa|0))){ad=c[11560]|0;if((ad<<1|0)>102){tN();ae=c[11560]|0}else{ae=ad}if((ae|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;af=1}else{af=ae}ae=af<<1;c[45200+(ae<<2)>>2]=ab;c[11298]=ab;c[45200+((ae|1)<<2)>>2]=aa;c[11296]=aa;c[11560]=af+1}tP();c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==1){af=c[11268]|0;aa=b-af|0;ae=d-af|0;af=c[3524]|0;if((c[af+8>>2]|0)>>>0>aa>>>0){if((c[af+12>>2]|0)>>>0>ae>>>0){ag=ae;ah=aa;ai=r;aj=d;ak=af}else{s=13796}}else{s=13796}if((s|0)==13796){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=aa,c[v+8>>2]=ae,v)|0);af=c[3524]|0;ab=c[af+8>>2]|0;ad=c[af+12>>2]|0;ag=ad>>>0>ae>>>0?ae:ad;ah=ab>>>0>aa>>>0?aa:ab;ai=c[11298]|0;aj=c[11296]|0;ak=af}if((ah|0)==(ai|0)&(ag|0)==(aj|0)){al=ak;am=ai;an=aj}else{tN();c[11298]=ah;c[11296]=ag;al=c[3524]|0;am=ah;an=ag}ag=c[11268]|0;ah=ag+b|0;aj=ag+d|0;if((c[al+8>>2]|0)>>>0>ah>>>0){if((c[al+12>>2]|0)>>>0>aj>>>0){ao=am;ap=an}else{s=13801}}else{s=13801}if((s|0)==13801){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=ah,c[v+8>>2]=aj,v)|0);ao=c[11298]|0;ap=c[11296]|0}if(!((ao|0)==(ah|0)&(ap|0)==(aj|0))){ap=c[11560]|0;if((ap<<1|0)>102){tN();aq=c[11560]|0}else{aq=ap}if((aq|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;ar=1}else{ar=aq}aq=ar<<1;c[45200+(aq<<2)>>2]=ah;c[11298]=ah;c[45200+((aq|1)<<2)>>2]=aj;c[11296]=aj;c[11560]=ar+1}ar=c[11268]|0;aq=b-ar|0;ap=ar+d|0;ar=c[3524]|0;if((c[ar+8>>2]|0)>>>0>aq>>>0){if((c[ar+12>>2]|0)>>>0>ap>>>0){as=ap;at=aq;au=ah;av=aj;aw=ar}else{s=13810}}else{s=13810}if((s|0)==13810){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=aq,c[v+8>>2]=ap,v)|0);ar=c[3524]|0;aj=c[ar+8>>2]|0;ah=c[ar+12>>2]|0;as=ah>>>0>ap>>>0?ap:ah;at=aj>>>0>aq>>>0?aq:aj;au=c[11298]|0;av=c[11296]|0;aw=ar}if((at|0)==(au|0)&(as|0)==(av|0)){ax=aw;ay=au;az=av}else{tN();c[11298]=at;c[11296]=as;ax=c[3524]|0;ay=at;az=as}as=c[11268]|0;at=as+b|0;av=d-as|0;if((c[ax+8>>2]|0)>>>0>at>>>0){if((c[ax+12>>2]|0)>>>0>av>>>0){aA=ay;aB=az}else{s=13815}}else{s=13815}if((s|0)==13815){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=at,c[v+8>>2]=av,v)|0);aA=c[11298]|0;aB=c[11296]|0}if((aA|0)==(at|0)&(aB|0)==(av|0)){c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}aB=c[11560]|0;if((aB<<1|0)>102){tN();aC=c[11560]|0}else{aC=aB}if((aC|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;aD=1}else{aD=aC}aC=aD<<1;c[45200+(aC<<2)>>2]=at;c[11298]=at;c[45200+((aC|1)<<2)>>2]=av;c[11296]=av;c[11560]=aD+1;c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==8){c[j>>2]=b;c[j+4>>2]=(c[11274]|0)+d;aD=c[11276]|0;c[j+12>>2]=b-aD;av=d-(c[11270]|0)|0;c[j+16>>2]=av;c[j+24>>2]=aD+b;c[j+28>>2]=av;n9(3,k);c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==9){k=d-(c[11274]|0)|0;av=c[3524]|0;if((c[av+8>>2]|0)>>>0>b>>>0){if((c[av+12>>2]|0)>>>0>k>>>0){aE=b;aF=k;s=13926}else{s=13925}}else{s=13925}if((s|0)==13925){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=k,v)|0);av=c[3524]|0;j=c[av+8>>2]|0;aD=j>>>0>b>>>0?b:j;j=c[av+12>>2]|0;aC=j>>>0>k>>>0?k:j;j=c[11298]|0;k=c[11296]|0;if((aD|0)==(j|0)&(aC|0)==(k|0)){aG=av;aH=j;aJ=k}else{aE=aD;aF=aC;s=13926}}if((s|0)==13926){tN();c[11298]=aE;c[11296]=aF;aG=c[3524]|0;aH=aE;aJ=aF}aF=b-(c[11276]|0)|0;aE=(c[11270]|0)+d|0;if((c[aG+8>>2]|0)>>>0>aF>>>0){if((c[aG+12>>2]|0)>>>0>aE>>>0){aK=aH;aL=aJ}else{s=13929}}else{s=13929}if((s|0)==13929){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=aF,c[v+8>>2]=aE,v)|0);aK=c[11298]|0;aL=c[11296]|0}if(!((aK|0)==(aF|0)&(aL|0)==(aE|0))){aL=c[11560]|0;if((aL<<1|0)>102){tN();aM=c[11560]|0}else{aM=aL}if((aM|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;aN=1}else{aN=aM}aM=aN<<1;c[45200+(aM<<2)>>2]=aF;c[11298]=aF;c[45200+((aM|1)<<2)>>2]=aE;c[11296]=aE;c[11560]=aN+1}aN=(c[11276]|0)+b|0;aM=(c[11270]|0)+d|0;aL=c[3524]|0;if((c[aL+8>>2]|0)>>>0>aN>>>0){if((c[aL+12>>2]|0)>>>0>aM>>>0){aO=aF;aP=aE}else{s=13938}}else{s=13938}if((s|0)==13938){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=aN,c[v+8>>2]=aM,v)|0);aO=c[11298]|0;aP=c[11296]|0}if(!((aO|0)==(aN|0)&(aP|0)==(aM|0))){aP=c[11560]|0;if((aP<<1|0)>102){tN();aQ=c[11560]|0}else{aQ=aP}if((aQ|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;aR=1}else{aR=aQ}aQ=aR<<1;c[45200+(aQ<<2)>>2]=aN;c[11298]=aN;c[45200+((aQ|1)<<2)>>2]=aM;c[11296]=aM;c[11560]=aR+1}tP();c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==2){aR=d-(c[11278]|0)|0;aM=c[3524]|0;if((c[aM+8>>2]|0)>>>0>b>>>0){if((c[aM+12>>2]|0)>>>0>aR>>>0){aS=b;aT=aR;s=13825}else{s=13824}}else{s=13824}if((s|0)==13824){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=aR,v)|0);aM=c[3524]|0;aQ=c[aM+8>>2]|0;aN=aQ>>>0>b>>>0?b:aQ;aQ=c[aM+12>>2]|0;aP=aQ>>>0>aR>>>0?aR:aQ;aQ=c[11298]|0;aR=c[11296]|0;if((aN|0)==(aQ|0)&(aP|0)==(aR|0)){aU=aM;aV=aQ;aW=aR}else{aS=aN;aT=aP;s=13825}}if((s|0)==13825){tN();c[11298]=aS;c[11296]=aT;aU=c[3524]|0;aV=aS;aW=aT}aT=(c[11278]|0)+d|0;if((c[aU+8>>2]|0)>>>0>b>>>0){if((c[aU+12>>2]|0)>>>0>aT>>>0){aX=aV;aY=aW}else{s=13828}}else{s=13828}if((s|0)==13828){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=aT,v)|0);aX=c[11298]|0;aY=c[11296]|0}if(!((aX|0)==(b|0)&(aY|0)==(aT|0))){aY=c[11560]|0;if((aY<<1|0)>102){tN();aZ=c[11560]|0}else{aZ=aY}if((aZ|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;a_=1}else{a_=aZ}aZ=a_<<1;c[45200+(aZ<<2)>>2]=b;c[11298]=b;c[45200+((aZ|1)<<2)>>2]=aT;c[11296]=aT;c[11560]=a_+1}a_=(c[11266]|0)+b|0;aZ=d-(c[11272]|0)|0;aY=c[3524]|0;if((c[aY+8>>2]|0)>>>0>a_>>>0){if((c[aY+12>>2]|0)>>>0>aZ>>>0){a$=aZ;a0=a_;a1=b;a2=aT;a3=aY}else{s=13837}}else{s=13837}if((s|0)==13837){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=a_,c[v+8>>2]=aZ,v)|0);aY=c[3524]|0;aT=c[aY+8>>2]|0;aX=c[aY+12>>2]|0;a$=aX>>>0>aZ>>>0?aZ:aX;a0=aT>>>0>a_>>>0?a_:aT;a1=c[11298]|0;a2=c[11296]|0;a3=aY}if((a0|0)==(a1|0)&(a$|0)==(a2|0)){a4=a3;a5=a1;a6=a2}else{tN();c[11298]=a0;c[11296]=a$;a4=c[3524]|0;a5=a0;a6=a$}a$=b-(c[11266]|0)|0;a0=(c[11272]|0)+d|0;if((c[a4+8>>2]|0)>>>0>a$>>>0){if((c[a4+12>>2]|0)>>>0>a0>>>0){a7=a5;a8=a6}else{s=13842}}else{s=13842}if((s|0)==13842){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=a$,c[v+8>>2]=a0,v)|0);a7=c[11298]|0;a8=c[11296]|0}if(!((a7|0)==(a$|0)&(a8|0)==(a0|0))){a8=c[11560]|0;if((a8<<1|0)>102){tN();a9=c[11560]|0}else{a9=a8}if((a9|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;ba=1}else{ba=a9}a9=ba<<1;c[45200+(a9<<2)>>2]=a$;c[11298]=a$;c[45200+((a9|1)<<2)>>2]=a0;c[11296]=a0;c[11560]=ba+1}ba=(c[11266]|0)+b|0;a9=(c[11272]|0)+d|0;a8=c[3524]|0;if((c[a8+8>>2]|0)>>>0>ba>>>0){if((c[a8+12>>2]|0)>>>0>a9>>>0){bb=a9;bc=ba;bd=a$;be=a0;bf=a8}else{s=13851}}else{s=13851}if((s|0)==13851){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=ba,c[v+8>>2]=a9,v)|0);a8=c[3524]|0;a0=c[a8+8>>2]|0;a$=c[a8+12>>2]|0;bb=a$>>>0>a9>>>0?a9:a$;bc=a0>>>0>ba>>>0?ba:a0;bd=c[11298]|0;be=c[11296]|0;bf=a8}if((bc|0)==(bd|0)&(bb|0)==(be|0)){bg=bf;bh=bd;bi=be}else{tN();c[11298]=bc;c[11296]=bb;bg=c[3524]|0;bh=bc;bi=bb}bb=b-(c[11266]|0)|0;bc=d-(c[11272]|0)|0;if((c[bg+8>>2]|0)>>>0>bb>>>0){if((c[bg+12>>2]|0)>>>0>bc>>>0){bj=bh;bk=bi}else{s=13856}}else{s=13856}if((s|0)==13856){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=bb,c[v+8>>2]=bc,v)|0);bj=c[11298]|0;bk=c[11296]|0}if((bj|0)==(bb|0)&(bk|0)==(bc|0)){c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}bk=c[11560]|0;if((bk<<1|0)>102){tN();bl=c[11560]|0}else{bl=bk}if((bl|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;bm=1}else{bm=bl}bl=bm<<1;c[45200+(bl<<2)>>2]=bb;c[11298]=bb;c[45200+((bl|1)<<2)>>2]=bc;c[11296]=bc;c[11560]=bm+1;c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==0){bm=b-(c[11278]|0)|0;bc=c[3524]|0;if((c[bc+8>>2]|0)>>>0>bm>>>0){if((c[bc+12>>2]|0)>>>0>d>>>0){bn=d;bo=bm;bp=r;bq=d;br=bc}else{s=13768}}else{s=13768}if((s|0)==13768){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=bm,c[v+8>>2]=d,v)|0);bc=c[3524]|0;bl=c[bc+8>>2]|0;bb=c[bc+12>>2]|0;bn=bb>>>0>d>>>0?d:bb;bo=bl>>>0>bm>>>0?bm:bl;bp=c[11298]|0;bq=c[11296]|0;br=bc}if((bo|0)==(bp|0)&(bn|0)==(bq|0)){bs=br;bt=bp;bu=bq}else{tN();c[11298]=bo;c[11296]=bn;bs=c[3524]|0;bt=bo;bu=bn}bn=(c[11278]|0)+b|0;if((c[bs+8>>2]|0)>>>0>bn>>>0){if((c[bs+12>>2]|0)>>>0>d>>>0){bv=bt;bw=bu}else{s=13773}}else{s=13773}if((s|0)==13773){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=bn,c[v+8>>2]=d,v)|0);bv=c[11298]|0;bw=c[11296]|0}if(!((bv|0)==(bn|0)&(bw|0)==(d|0))){bw=c[11560]|0;if((bw<<1|0)>102){tN();bx=c[11560]|0}else{bx=bw}if((bx|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;by=1}else{by=bx}bx=by<<1;c[45200+(bx<<2)>>2]=bn;c[11298]=bn;c[45200+((bx|1)<<2)>>2]=d;c[11296]=d;c[11560]=by+1}by=d-(c[11278]|0)|0;bx=c[3524]|0;if((c[bx+8>>2]|0)>>>0>b>>>0){if((c[bx+12>>2]|0)>>>0>by>>>0){bz=by;bA=b;bB=bn;bC=d;bD=bx}else{s=13782}}else{s=13782}if((s|0)==13782){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=by,v)|0);bx=c[3524]|0;bn=c[bx+8>>2]|0;bw=c[bx+12>>2]|0;bz=bw>>>0>by>>>0?by:bw;bA=bn>>>0>b>>>0?b:bn;bB=c[11298]|0;bC=c[11296]|0;bD=bx}if((bA|0)==(bB|0)&(bz|0)==(bC|0)){bE=bD;bF=bB;bG=bC}else{tN();c[11298]=bA;c[11296]=bz;bE=c[3524]|0;bF=bA;bG=bz}bz=(c[11278]|0)+d|0;if((c[bE+8>>2]|0)>>>0>b>>>0){if((c[bE+12>>2]|0)>>>0>bz>>>0){bH=bF;bI=bG}else{s=13787}}else{s=13787}if((s|0)==13787){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=bz,v)|0);bH=c[11298]|0;bI=c[11296]|0}if((bH|0)==(b|0)&(bI|0)==(bz|0)){c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}bI=c[11560]|0;if((bI<<1|0)>102){tN();bJ=c[11560]|0}else{bJ=bI}if((bJ|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;bK=1}else{bK=bJ}bJ=bK<<1;c[45200+(bJ<<2)>>2]=b;c[11298]=b;c[45200+((bJ|1)<<2)>>2]=bz;c[11296]=bz;c[11560]=bK+1;c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else if((A|0)==3){A=c[11268]|0;bK=b-A|0;bz=d-A|0;A=c[3524]|0;if((c[A+8>>2]|0)>>>0>bK>>>0){if((c[A+12>>2]|0)>>>0>bz>>>0){bL=bz;bM=bK;bN=r;bO=d;bP=A}else{s=13865}}else{s=13865}if((s|0)==13865){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=bK,c[v+8>>2]=bz,v)|0);A=c[3524]|0;r=c[A+8>>2]|0;bJ=c[A+12>>2]|0;bL=bJ>>>0>bz>>>0?bz:bJ;bM=r>>>0>bK>>>0?bK:r;bN=c[11298]|0;bO=c[11296]|0;bP=A}if((bM|0)==(bN|0)&(bL|0)==(bO|0)){bQ=bP;bR=bN;bS=bO}else{tN();c[11298]=bM;c[11296]=bL;bQ=c[3524]|0;bR=bM;bS=bL}bL=c[11268]|0;bM=bL+b|0;bO=d-bL|0;if((c[bQ+8>>2]|0)>>>0>bM>>>0){if((c[bQ+12>>2]|0)>>>0>bO>>>0){bT=bR;bU=bS}else{s=13870}}else{s=13870}if((s|0)==13870){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=bM,c[v+8>>2]=bO,v)|0);bT=c[11298]|0;bU=c[11296]|0}if(!((bT|0)==(bM|0)&(bU|0)==(bO|0))){bU=c[11560]|0;if((bU<<1|0)>102){tN();bV=c[11560]|0}else{bV=bU}if((bV|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;bW=1}else{bW=bV}bV=bW<<1;c[45200+(bV<<2)>>2]=bM;c[11298]=bM;c[45200+((bV|1)<<2)>>2]=bO;c[11296]=bO;c[11560]=bW+1}bW=c[11268]|0;bV=bW+b|0;bU=bW+d|0;bW=c[3524]|0;if((c[bW+8>>2]|0)>>>0>bV>>>0){if((c[bW+12>>2]|0)>>>0>bU>>>0){bX=bM;bY=bO}else{s=13879}}else{s=13879}if((s|0)==13879){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=bV,c[v+8>>2]=bU,v)|0);bX=c[11298]|0;bY=c[11296]|0}if(!((bX|0)==(bV|0)&(bY|0)==(bU|0))){bY=c[11560]|0;if((bY<<1|0)>102){tN();bZ=c[11560]|0}else{bZ=bY}if((bZ|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;b_=1}else{b_=bZ}bZ=b_<<1;c[45200+(bZ<<2)>>2]=bV;c[11298]=bV;c[45200+((bZ|1)<<2)>>2]=bU;c[11296]=bU;c[11560]=b_+1}b_=c[11268]|0;bZ=b-b_|0;b=b_+d|0;d=c[3524]|0;if((c[d+8>>2]|0)>>>0>bZ>>>0){if((c[d+12>>2]|0)>>>0>b>>>0){b$=bV;b0=bU}else{s=13888}}else{s=13888}if((s|0)==13888){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=bZ,c[v+8>>2]=b,v)|0);b$=c[11298]|0;b0=c[11296]|0}if(!((b$|0)==(bZ|0)&(b0|0)==(b|0))){b0=c[11560]|0;if((b0<<1|0)>102){tN();b1=c[11560]|0}else{b1=b0}if((b1|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;b2=1}else{b2=b1}b1=b2<<1;c[45200+(b1<<2)>>2]=bZ;c[11298]=bZ;c[45200+((b1|1)<<2)>>2]=b;c[11296]=b;c[11560]=b2+1}tP();c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}else{c[11554]=l;n=c[11552]|0;o=n+1|0;c[11552]=o;i=f;return}}function n1(a){a=a|0;return 0}function n2(){return}function n3(b,d,e,f,i,j){b=b|0;d=+d;e=+e;f=f|0;i=i|0;j=j|0;if((j|0)==3){c[59568]=c[11298];c[59566]=c[11296];return}else if((j|0)==4){c[11298]=c[59568];c[11296]=c[59566];return}else{if(a[238232]|0){return}a[238232]=1;c[11232]=43856;c[59562]=b;g[59560]=d;g[59564]=+h[5710]*e;a[238216]=i&1;c[59556]=j;return}}function n4(b){b=b|0;var d=0,e=0,f=0,j=0,k=0,l=0.0;d=i;i=i+8|0;e=d|0;L19029:do{if((b|0)!=45688){do{if((b|0)!=0){if((a[b]|0)==0){break}f=bk(b|0,148464)|0;do{if((f|0)>0){j=f+1|0;k=(j|0)<32?j:32;uF(45688,b|0,k|0);if((uA(b|0)|0)>>>0<k>>>0){break}a[45688+((k|0)==0?0:k-1|0)|0]=0}}while(0);if(f>>>0>=(uA(b|0)|0)>>>0){break L19029}if((ca(b+(f+1|0)|0,205104,(v=i,i=i+8|0,c[v>>2]=e,v)|0)|0)==0){break L19029}g[11418]=+g[e>>2];break L19029}}while(0);uB(45688,45952);g[11418]=+g[11486]}}while(0);do{if((aY(238560,45688)|0)==0){if(+g[59638]!=+g[11418]){break}i=d;return 1}}while(0);uF(238560,45688,256);if((uA(45688)|0)>>>0>=256){a[238815]=0}l=+g[11418];g[59638]=l;c[(c[3524]|0)+20>>2]=~~(+h[5710]*l*35.28*.6);c[(c[3524]|0)+16>>2]=~~(+h[5710]*+g[11418]*35.28*1.3);tO();i=d;return 1}function n5(a){a=+a;var b=0;b=~~((a<0.0?1.0:a)*+((c[(c[3524]|0)+28>>2]|0)>>>0>>>0));c[11278]=b;c[11268]=~~+O(+(+(b|0)*.707+.5));c[11266]=(b*13&-1|0)/15&-1;c[11272]=(b|0)/2&-1;c[11274]=(b*36&-1|0)/29&-1;c[11276]=(b*14&-1|0)/13&-1;c[11270]=(b*18&-1|0)/29&-1;return}function n6(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0;g=i;i=i+48|0;h=g|0;c[h>>2]=b;c[h+4>>2]=d;j=e+b|0;c[h+12>>2]=j;c[h+16>>2]=d;c[h+24>>2]=j;j=f+d|0;c[h+28>>2]=j;c[h+36>>2]=b;c[h+40>>2]=j;c[h+8>>2]=a;n9(4,h|0);i=g;return}function n7(a){a=+a;var b=0.0;tN();b=+h[5704]*a;if(b==+h[5705]){return}h[5705]=b;c[11552]=(c[11552]|0)+1;return}function n8(b){b=b|0;var e=0,f=0,g=0,j=0,k=0;e=i;i=i+8|0;f=e|0;tN();g=c[b>>2]|0;if((g|0)==5){fr(+h[b+8>>3],f);c[11562]=(d[f+1|0]|0)<<8|(d[f|0]|0)|(d[f+2|0]|0)<<16}else if((g|0)==3){f=c[b+4>>2]|0;c[11562]=f>>>16&255|f&65280|f<<16&16711680}else if((g|0)==1){g=c[b+4>>2]|0;if((g|0)==-4){j=46256}else{if((g|0)<0|a[45624]){k=7}else{k=(g|0)%15&-1}j=238816+(k<<2)|0}c[11562]=c[j>>2];tN()}c[11554]=c[11412];c[11552]=(c[11552]|0)+1;i=e;return}function n9(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0.0,z=0;e=i;i=i+8|0;f=e|0;g=f;h=c[11562]|0;j=c[d+8>>2]|0;k=j>>4;l=j&15;j=h&255;m=h>>>8;n=m&255;o=h>>>16;p=o&255;q=h>>>24&255;if((l|0)==0){r=c[11564]|0;s=r>>>24&255;t=r>>>16&255;u=r>>>8&255;v=r&255}else if((l|0)==2){w=k*12&-1;x=14054}else if((l|0)==1){w=k;x=14054}else{s=q;t=p;u=n;v=j}do{if((x|0)==14054){if(w>>>0>=100){s=q;t=p;u=n;v=j;break}y=+(w|0)/100.0;k=((~~(y*+(h&255|0))+(~~((1.0-y)*255.0)*65793&-1)|0)+(~~(y*+(m&255|0))<<8)|0)+(~~(y*+(o&255|0))<<16)|0;s=k>>>24&255;t=k>>>16&255;u=k>>>8&255;v=k&255}}while(0);if((c[11552]|0)>0){tL(c[11554]|0);c[11552]=0}o=f;m=g+3|0;a[m]=0;h=g+2|0;a[h]=0;w=g+1|0;a[w]=0;a[o]=37;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[m]=-128;a[h]=0;a[w]=0;a[o]=7;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=37;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[m]=-128;a[h]=0;a[w]=0;a[o]=0;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=40;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[m]=0;a[h]=0;a[w]=0;a[o]=3;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=39;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=24;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[m]=0;a[h]=0;a[w]=0;a[o]=3;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);a[m]=s;a[h]=t;a[w]=u;a[o]=v;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=37;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[m]=0;a[h]=0;a[w]=0;a[o]=3;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=40;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[m]=0;a[h]=0;a[w]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=38;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=28;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[m]=0;a[h]=0;a[w]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);g=a[45616]|0;a[m]=0;a[h]=0;a[w]=g?0:34;a[o]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);a[m]=s;a[h]=t;a[w]=u;a[o]=v;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=37;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=12;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[m]=0;a[h]=0;a[w]=0;a[o]=1;aI(o|0,1,4,c[10030]|0);a[m]=0;a[h]=0;a[w]=0;a[o]=3;aI(o|0,1,4,c[10030]|0);v=(b<<3)+28|0;a[m]=v>>>24&255;a[h]=v>>>16&255;a[w]=v>>>8&255;a[o]=v&255;aI(o|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);c[f>>2]=0;aI(o|0,1,4,c[10030]|0);a[m]=b>>>24&255;a[h]=b>>>16&255;a[w]=b>>>8&255;a[o]=b&255;aI(o|0,1,4,c[10030]|0);if((b|0)>0){z=0}else{c[11412]=-5;c[11554]=-5;i=e;return}do{f=c[d+(z*12&-1)>>2]|0;a[m]=f>>>24&255;a[h]=f>>>16&255;a[w]=f>>>8&255;a[o]=f&255;aI(o|0,1,4,c[10030]|0);f=(c[(c[3524]|0)+12>>2]|0)-(c[d+(z*12&-1)+4>>2]|0)|0;a[m]=f>>>24&255;a[h]=f>>>16&255;a[w]=f>>>8&255;a[o]=f&255;aI(o|0,1,4,c[10030]|0);z=z+1|0;}while((z|0)<(b|0));c[11412]=-5;c[11554]=-5;i=e;return}function oa(){var b=0,d=0,e=0,f=0.0,h=0,j=0,k=0.0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0,u=0,v=0,w=0,x=0,y=0;b=i;i=i+256|0;if(!(a[238232]|0)){i=b;return}a[c[11232]|0]=0;a[238232]=0;d=c[11298]|0;e=c[11296]|0;f=+g[11418];h=b|0;uB(h|0,45688);g[11418]=+g[59560];n4(c[59562]|0);g[11418]=f;uB(45688,h|0);h=b4(43856,73808)|0;if((h|0)>0){f=+(aa(c[(c[3524]|0)+20>>2]|0,h)>>>0>>>0)*.8;j=c[11262]|0;k=+(j|0)*.0017453292519944445;l=~~(+(d>>>0>>>0)+ +S(+k)*f);m=~~(+(e>>>0>>>0)+ +T(+k)*f);c[11298]=l;c[11296]=m;n=l;o=m;p=h+43856|0;q=j}else{n=d;o=e;p=43856;q=c[11262]|0}f=+(q|0)*.0017453292519944445;k=+T(+f);r=+g[59564];q=~~(k*r*26.37);e=~~(+S(+f)*r*26.37);do{if((a[238216]&1)!=0){if(a[238208]|0){break}n$(n-q|0,e+o|0,p)}}while(0);r=+((uA(p|0)|0)>>>0>>>0);if(a[45056]|0){s=r*.8*+((c[(c[3524]|0)+20>>2]|0)>>>0>>>0)}else{e=a[p]|0;L19099:do{if(e<<24>>24==0){t=0;u=0}else{q=uA(p|0)|0;d=1;j=0;h=0;m=e;while(1){l=m<<24>>24;v=((aZ(171640,l|0,18)|0)!=0&1)+j|0;if((m-65&255)<26){w=14075}else{if((aZ(171632,l|0,5)|0)==0){x=h}else{w=14075}}if((w|0)==14075){w=0;x=h+1|0}y=v+((aZ(171608,l|0,10)|0)!=0&1)|0;if(d>>>0>=q>>>0){t=y;u=x;break L19099}l=a[p+d|0]|0;d=d+1|0;j=y;h=x;m=l}}}while(0);s=(+(-t|0)*.2+(r*.8+ +(u|0)*.3))*+((c[(c[3524]|0)+20>>2]|0)>>>0>>>0)}r=+(~~s|0);s=+(c[11262]|0)*.0017453292519944445;f=+S(+s);u=~~(+(n>>>0>>>0)+r*f);c[11298]=u;k=+T(+s);n=~~(+(o>>>0>>>0)+r*k);c[11296]=n;if((c[59556]|0)!=1){i=b;return}s=r*.5;c[11298]=~~(+(u|0)-s*f);c[11296]=~~(+(n|0)-s*k);i=b;return}function ob(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0.0,F=0.0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0.0,P=0.0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0.0,ac=0.0,ad=0,ae=0,af=0.0,ag=0,ah=0.0,ai=0,aj=0.0,ak=0,al=0,am=0,an=0,ao=0,ap=0.0,aq=0.0,ar=0,as=0.0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0.0,aA=0.0,aB=0.0,aC=0.0,aD=0.0,aE=0.0,aF=0.0,aG=0,aH=0,aI=0,aJ=0;b=i;i=i+424|0;d=b|0;e=b+24|0;f=b+48|0;g=b+72|0;j=b+96|0;k=b+120|0;l=b+144|0;m=b+168|0;n=c[13898]|0;o=c[8272]|0;L19117:do{if((n|0)<(o|0)){p=l|0;q=l+8|0;r=k|0;s=k+8|0;t=j|0;u=j+8|0;w=g|0;x=g+8|0;y=f|0;z=f+8|0;A=e|0;B=e+8|0;C=d|0;D=d+8|0;E=0.0;F=0.0;G=n;H=o;L19119:while(1){I=c[1054]|0;J=(a[I+(G*40&-1)|0]&1)==0;L19121:do{if(!J){K=c[I+(G*40&-1)+36>>2]|0;L=I+(G*40&-1)+32|0;M=c[10036]|0;N=0;while(1){if((N|0)>=(K|0)){break}if((a[M+((c[L>>2]|0)+N|0)|0]|0)==(a[N+103664|0]|0)){N=N+1|0}else{break L19121}}if((N|0)==1){O=E;P=F;break L19117}}}while(0);L=c[59286]|0;L19128:do{if((L|0)==0){Q=237144}else{M=c[I+(G*40&-1)+36>>2]|0;K=c[I+(G*40&-1)+32>>2]|0;R=(M|0)>0;S=c[10036]|0;T=237144;U=L;while(1){L19132:do{if(!J){if(R){V=0;W=0;X=K;while(1){Y=a[U+V|0]|0;if(Y<<24>>24==(a[S+(V+X|0)|0]|0)){Z=X;_=W}else{if(Y<<24>>24!=36){break L19132}Z=X-1|0;_=1}$=V+1|0;if(($|0)<(_+M|0)){V=$;W=_;X=Z}else{break}}if((_|0)==0){aa=$}else{Q=T;break L19128}}else{aa=0}X=a[U+aa|0]|0;if((X<<24>>24|0)==36|(X<<24>>24|0)==0){Q=T;break L19128}}}while(0);X=T+8|0;W=c[X>>2]|0;if((W|0)==0){Q=X;break}else{T=X;U=W}}}}while(0);J=c[Q+4>>2]|0;do{if((J|0)==0){a[237032]=0;c[13898]=G+1;ab=F;ac=E}else if((J|0)==1){a[237032]=1;c[13898]=G+1;ab=F;ac=E}else if((J|0)==17){c[59264]=c[59264]&-7|2;c[59444]=0;c[13898]=G+1;ab=F;ac=E}else if((J|0)==16){c[59264]=4;c[13898]=G+1;ab=F;ac=E}else if((J|0)==19){c[59264]=c[59264]|1;c[13898]=G+1;ab=F;ac=E}else if((J|0)==3){a[237464]=1;c[13898]=G+1;ab=F;ac=E}else if((J|0)==7){a[237096]=0;c[13898]=G+1;ab=F;ac=E}else if((J|0)==8){ad=G+1|0;c[13898]=ad;if((ad|0)>=(H|0)){ae=14288;break L19119}L19155:do{if((a[I+(ad*40&-1)|0]&1)!=0){L=c[I+(ad*40&-1)+36>>2]|0;U=I+(ad*40&-1)+32|0;T=c[10036]|0;M=0;while(1){if((M|0)>=(L|0)){break}if((a[T+((c[U>>2]|0)+M|0)|0]|0)==(a[M+103664|0]|0)){M=M+1|0}else{break L19155}}if((M|0)==1){ae=14289;break L19119}}}while(0);U=is(l)|0;T=c[U>>2]|0;if((T|0)==1){af=+(c[U+8>>2]|0)}else if((T|0)==2){af=+h[U+8>>3]}else if((T|0)==3){af=+uz(c[U+8>>2]|0,0)}else{ae=14130;break L19119}if((c[p>>2]|0)==3){uu(c[q>>2]|0);c[p>>2]=1}U=c[13898]|0;T=c[8272]|0;L19170:do{if((T|0)>(U|0)){L=c[1054]|0;if((a[L+(U*40&-1)|0]&1)==0){ag=U;break}S=c[L+(U*40&-1)+36>>2]|0;K=L+(U*40&-1)+32|0;L=c[10036]|0;R=0;while(1){if((R|0)>=(S|0)){break}if((a[L+((c[K>>2]|0)+R|0)|0]|0)==(a[R+148464|0]|0)){R=R+1|0}else{ag=U;break L19170}}if((R|0)!=1){ag=U;break}K=U+1|0;c[13898]=K;ag=K}else{ag=U}}while(0);if((ag|0)>=(T|0)){ae=14286;break L19119}U=c[1054]|0;L19180:do{if((a[U+(ag*40&-1)|0]&1)!=0){K=c[U+(ag*40&-1)+36>>2]|0;L=U+(ag*40&-1)+32|0;S=c[10036]|0;M=0;while(1){if((M|0)>=(K|0)){break}if((a[S+((c[L>>2]|0)+M|0)|0]|0)==(a[M+103664|0]|0)){M=M+1|0}else{break L19180}}if((M|0)==1){ae=14287;break L19119}}}while(0);U=is(k)|0;T=c[U>>2]|0;if((T|0)==1){ah=+(c[U+8>>2]|0)}else if((T|0)==2){ah=+h[U+8>>3]}else if((T|0)==3){ah=+uz(c[U+8>>2]|0,0)}else{ae=14151;break L19119}if((c[r>>2]|0)==3){uu(c[s>>2]|0);c[r>>2]=1}if(!(af<2.0|ah<2.0|af>99.0|ah>99.0)){ab=af;ac=ah;break}if(af!=0.0|ah!=0.0){ae=14156;break L19119}else{ab=0.0;ac=0.0}}else if((J|0)==18){c[59264]=c[59264]|8;c[13898]=G+1;ab=F;ac=E}else if((J|0)==12){ai=G+1|0;c[13898]=ai;if((ai|0)>=(H|0)){ae=14290;break L19119}L19199:do{if((a[I+(ai*40&-1)|0]&1)!=0){U=c[I+(ai*40&-1)+36>>2]|0;T=I+(ai*40&-1)+32|0;L=c[10036]|0;S=0;while(1){if((S|0)>=(U|0)){break}if((a[L+((c[T>>2]|0)+S|0)|0]|0)==(a[S+103664|0]|0)){S=S+1|0}else{break L19199}}if((S|0)==1){ae=14291;break L19119}}}while(0);T=is(e)|0;L=c[T>>2]|0;if((L|0)==1){aj=+(c[T+8>>2]|0)}else if((L|0)==2){aj=+h[T+8>>3]}else if((L|0)==3){aj=+uz(c[T+8>>2]|0,0)}else{ae=14214;break L19119}if((c[A>>2]|0)==3){uu(c[B>>2]|0);c[A>>2]=1}T=~~aj;c[59446]=T;if(T>>>0>99){ae=14218;break L19119}c[59344]=T;ab=F;ac=E}else if((J|0)==2){a[237464]=0;c[13898]=G+1;ab=F;ac=E}else if((J|0)==15){a[237072]=0;c[13898]=G+1;ab=F;ac=E}else if((J|0)==20){ak=G+1|0;c[13898]=ak;if((ak|0)>=(H|0)){ae=14284;break L19119}T=c[I+(ak*40&-1)+36>>2]|0;L19219:do{if((a[I+(ak*40&-1)|0]&1)==0){al=c[10036]|0;am=I+(ak*40&-1)+32|0}else{L=I+(ak*40&-1)+32|0;U=c[10036]|0;M=0;while(1){if((M|0)>=(T|0)){break}if((a[U+((c[L>>2]|0)+M|0)|0]|0)==(a[M+103664|0]|0)){M=M+1|0}else{al=U;am=L;break L19219}}if((M|0)==1){ae=14285;break L19119}else{al=U;am=L}}}while(0);S=(T|0)>49?49:T;K=c[am>>2]|0;R=0;while(1){N=R+1|0;a[R+236976|0]=a[al+K|0]|0;if((N|0)==(S|0)){break}else{K=K+1|0;R=N}}a[S+236976|0]=0;an=G+2|0;c[13898]=an;if((aY(236976,173280)|0)==0){ab=F;ac=E;break}if((aY(236976,173088)|0)==0){ab=F;ac=E}else{ae=14249;break L19119}}else if((J|0)==14){a[237072]=1;c[13898]=G+1;ab=F;ac=E}else if((J|0)==13){ao=G+1|0;c[13898]=ao;if((ao|0)>=(H|0)){ae=14297;break L19119}L19234:do{if((a[I+(ao*40&-1)|0]&1)!=0){R=c[I+(ao*40&-1)+36>>2]|0;K=I+(ao*40&-1)+32|0;T=c[10036]|0;N=0;while(1){if((N|0)>=(R|0)){break}if((a[T+((c[K>>2]|0)+N|0)|0]|0)==(a[N+103664|0]|0)){N=N+1|0}else{break L19234}}if((N|0)==1){ae=14296;break L19119}}}while(0);S=is(d)|0;K=c[S>>2]|0;if((K|0)==2){ap=+h[S+8>>3]}else if((K|0)==3){ap=+uz(c[S+8>>2]|0,0)}else if((K|0)==1){ap=+(c[S+8>>2]|0)}else{ae=14231;break L19119}if((c[C>>2]|0)==3){uu(c[D>>2]|0);c[C>>2]=1}S=~~ap;if((S-2|0)>>>0>=99999){ae=14236;break L19119}c[59278]=S-1;ab=F;ac=E}else if((J|0)==10){c[13898]=G+1;S=is(g)|0;K=c[S>>2]|0;if((K|0)==1){aq=+(c[S+8>>2]|0)}else if((K|0)==2){aq=+h[S+8>>3]}else if((K|0)==3){aq=+uz(c[S+8>>2]|0,0)}else{ae=14182;break L19119}if((c[w>>2]|0)==3){uu(c[x>>2]|0);c[w>>2]=1}c[59442]=~~aq;ab=F;ac=E}else if((J|0)==11){ar=G+1|0;c[13898]=ar;if((ar|0)>=(H|0)){ae=14295;break L19119}L19261:do{if((a[I+(ar*40&-1)|0]&1)!=0){S=c[I+(ar*40&-1)+36>>2]|0;K=I+(ar*40&-1)+32|0;T=c[10036]|0;R=0;while(1){if((R|0)>=(S|0)){break}if((a[T+((c[K>>2]|0)+R|0)|0]|0)==(a[R+103664|0]|0)){R=R+1|0}else{break L19261}}if((R|0)==1){ae=14294;break L19119}}}while(0);K=is(f)|0;T=c[K>>2]|0;if((T|0)==1){as=+(c[K+8>>2]|0)}else if((T|0)==2){as=+h[K+8>>3]}else if((T|0)==3){as=+uz(c[K+8>>2]|0,0)}else{ae=14197;break L19119}if((c[y>>2]|0)==3){uu(c[z>>2]|0);c[y>>2]=1}K=~~as;c[59448]=K;if((K-1|0)>>>0>9){ae=14202;break L19119}else{ab=F;ac=E}}else if((J|0)==9){at=G+1|0;c[13898]=at;if((at|0)>=(H|0)){au=at;ae=14282;break L19119}K=(a[I+(at*40&-1)|0]&1)==0;L19278:do{if(!K){T=c[I+(at*40&-1)+36>>2]|0;S=I+(at*40&-1)+32|0;N=c[10036]|0;L=0;while(1){if((L|0)>=(T|0)){ae=14162;break}if((a[N+((c[S>>2]|0)+L|0)|0]|0)==(a[L+103664|0]|0)){L=L+1|0}else{break}}if((ae|0)==14162){ae=0;if((L|0)==1){au=at;ae=14281;break L19119}}if(K){break}S=c[I+(at*40&-1)+36>>2]|0;N=I+(at*40&-1)+32|0;T=c[10036]|0;R=0;while(1){if((R|0)>=(S|0)){break}if((a[T+((c[N>>2]|0)+R|0)|0]|0)==(a[R+103664|0]|0)){R=R+1|0}else{break L19278}}if((R|0)==1){au=at;ae=14279;break L19119}}}while(0);a[14176]=1;is(j);a[14176]=0;if((c[t>>2]|0)!=3){ae=14169;break L19119}K=c[u>>2]|0;if((K|0)==0){ae=14171;break L19119}N=bk(K|0,148464)|0;ca(K+(N+1|0)|0,21e4,(v=i,i=i+8|0,c[v>>2]=237768,v)|0);a[K+N|0]=0;N=237480;while(1){T=c[N>>2]|0;if((T|0)==0){av=-1;break}if((a_(K|0,T|0,uA(K|0)|0)|0)==0){ae=14176;break}else{N=N+8|0}}if((ae|0)==14176){ae=0;av=c[N+4>>2]|0}c[59444]=(av|0)<0?0:av;uu(K);ab=F;ac=E}else if((J|0)==5){a[237472]=1;c[13898]=G+1;ab=F;ac=E}else if((J|0)==6){a[237096]=1;c[13898]=G+1;ab=F;ac=E}else if((J|0)==4){a[237472]=0;c[13898]=G+1;ab=F;ac=E}else{ae=14250;break L19119}}while(0);J=c[13898]|0;I=c[8272]|0;if((J|0)<(I|0)){E=ac;F=ab;G=J;H=I}else{O=ac;P=ab;break L19117}}if((ae|0)==14171){au=c[13898]|0;uf(au,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14294){uf(ar,172856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14295){uf(ar,172856,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14296){uf(ao,172696,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14291){uf(ai,172784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14297){uf(ao,172696,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14130){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14202){uf((c[13898]|0)-1|0,172816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14285){uf(ak,172624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14286){uf(ag,172904,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14287){uf(ag,172904,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14279){uf(au,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14281){uf(au,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14249){uf(an,172552,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14250){uf(G,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14214){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14288){uf(ad,172904,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14289){uf(ad,172904,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14290){uf(ai,172784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14197){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14236){uf(c[13898]|0,172656,(v=i,i=i+8|0,c[v>>2]=1e5,v)|0)}else if((ae|0)==14282){uf(au,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14284){uf(ak,172624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14182){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14151){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14156){uf(c[13898]|0,130744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14231){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14169){c[13898]=at;au=at;uf(au,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((ae|0)==14218){uf((c[13898]|0)-1|0,172744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{O=0.0;P=0.0}}while(0);ae=c[59264]|0;au=m|0;if((ae|0)==4){uD(au|0,172520,12)}else{m=(ae&2|0)!=0?172480:179864;at=(ae&8|0)!=0?172440:179864;ak=(ae&1|0)!=0?172408:179864;be(au|0,172512,(v=i,i=i+24|0,c[v>>2]=m,c[v+8>>2]=at,c[v+16>>2]=ak,v)|0)}ak=a[237464]|0;at=(c[59278]|0)+1|0;m=a[237096]|0?78720:78704;ae=a[237472]|0?172248:172264;ai=a[237072]|0?131504:131584;ad=c[237480+(c[59444]<<3)>>2]|0;an=c[59442]|0;if((an|0)>0){aw=an}else{aw=ak?16:10}an=c[59448]|0;ag=c[59446]|0;be(13048,172304,(v=i,i=i+136|0,c[v>>2]=a[237032]|0?116440:116408,c[v+8>>2]=ak?172296:172288,c[v+16>>2]=172272,c[v+24>>2]=at,c[v+32>>2]=m,c[v+40>>2]=ae,c[v+48>>2]=ai,c[v+56>>2]=au,c[v+64>>2]=124352,c[v+72>>2]=ad,c[v+80>>2]=aw,c[v+88>>2]=208960,c[v+96>>2]=an,c[v+104>>2]=172240,c[v+112>>2]=ag,c[v+120>>2]=172200,c[v+128>>2]=236976,v)|0);ag=P>0.0&O>0.0;L19344:do{if(ag){an=~~P;do{if(P- +(an|0)==0.0){aw=~~O;if(O- +(aw|0)!=0.0){break}ad=13048+(uA(13048)|0)|0;be(ad|0,172160,(v=i,i=i+16|0,c[v>>2]=an,c[v+8>>2]=aw,v)|0);break L19344}}while(0);an=13048+(uA(13048)|0)|0;be(an|0,172120,(v=i,i=i+16|0,h[v>>3]=P,h[v+8>>3]=O,v)|0)}}while(0);an=(c[59442]|0)<1;if(a[237464]|0){if(an){c[59442]=16}G=a[237472]|0;c[(c[3524]|0)+8>>2]=G?9e3:9600;c[(c[3524]|0)+12>>2]=G?6750:6e3;aw=G?90:105;c[(c[3524]|0)+24>>2]=aw;c[(c[3524]|0)+28>>2]=aw;ax=G;ay=G?900:1200}else{if(an){c[59442]=10}an=a[237472]|0;c[(c[3524]|0)+8>>2]=an?5400:6e3;c[(c[3524]|0)+12>>2]=3600;G=an?67:75;c[(c[3524]|0)+24>>2]=G;c[(c[3524]|0)+28>>2]=G;ax=an;ay=an?900:1200}c[59242]=ay;c[59240]=ax?900:1200;if(a[237096]|0){ay=c[3524]|0;an=ay+8|0;G=c[an>>2]|0;c[an>>2]=c[ay+12>>2];c[(c[3524]|0)+12>>2]=G}if(ag){ab=ax?450.0:1200.0;c[(c[3524]|0)+8>>2]=~~(P*ab);c[(c[3524]|0)+12>>2]=~~(O*ab)}ag=c[59442]|0;if(ax){az=+(ag*450&-1|0)*2.54}else{az=+(ag*1200&-1|0)}c[(c[3524]|0)+16>>2]=~~(az/72.0*3.0*.25);ag=c[59442]|0;if(ax){aA=+(ag*450&-1|0)*2.54;aB=aA/72.0;aC=aB*3.0;aD=aC*.25;aE=aD*6.0;aF=aE/10.0;aG=~~aF;aH=c[3524]|0;aI=aH+20|0;c[aI>>2]=aG;aJ=c[59448]|0;c[59262]=aJ;i=b;return}else{aA=+(ag*1200&-1|0);aB=aA/72.0;aC=aB*3.0;aD=aC*.25;aE=aD*6.0;aF=aE/10.0;aG=~~aF;aH=c[3524]|0;aI=aH+20|0;c[aI>>2]=aG;aJ=c[59448]|0;c[59262]=aJ;i=b;return}}function oc(){c[59270]=0;c[59272]=0;a[237104]=0;return}function od(a){a=a|0;var b=0;if((a|0)==1){c[59364]=1;b=1}else if((a|0)==0){c[59364]=0;b=1}else if((a|0)==2){c[59364]=2;b=1}else{c[59364]=0;b=0}return b|0}function oe(a){a=a|0;g[59456]=+(a|0)*1.5707963267948966/90.0;return 1}function of(a){a=+a;var b=0.0;h[29725]=a<0.0?1.0:a;b=+(c[59442]|0)*a/10.0;h[1630]=b>=0.0?b:1.0;return}function og(){var b=0,d=0,e=0,f=0;b=i;c[59270]=0;c[59272]=0;a[237104]=0;ol(-1);c[59364]=0;g[59456]=0.0;a[237136]=0;a[237432]=1;c[59356]=0;c[59354]=c[59262];a[237392]=1;c[59346]=c[59344];g[59360]=0.0;if((aY(236976,173280)|0)==0){d=c[10030]|0;e=a[237096]|0?198816:198832;f=a[237472]|0?173096:173160;cf(d|0,173232,(v=i,i=i+40|0,c[v>>2]=e,c[v+8>>2]=173184,c[v+16>>2]=f,c[v+24>>2]=1200,c[v+32>>2]=2,v)|0);i=b;return}if((aY(236976,173088)|0)!=0){i=b;return}f=a[237472]|0;cf(c[10030]|0,173048,(v=i,i=i+72|0,c[v>>2]=a[237096]|0?198816:198832,c[v+8>>2]=173184,c[v+16>>2]=f?173096:173160,c[v+24>>2]=f?173032:173040,h[v+32>>3]=100.0,c[v+40>>2]=172944,c[v+48>>2]=-2,c[v+56>>2]=1200,c[v+64>>2]=2,v)|0);i=b;return}function oh(){tK(a[237104]&1);c[59270]=0;c[59272]=0;aD(c[10030]|0);return}function oi(){tK(a[237104]&1);c[59270]=0;c[59272]=0;aD(c[10030]|0);return}function oj(b,d){b=b|0;d=d|0;var e=0,f=0;e=c[59272]|0;f=c[59270]|0;c[59272]=b;c[59270]=d;if((e|0)==(b|0)&(f|0)==(d|0)){return}tK(a[237104]&1);return}function ok(b,d){b=b|0;d=d|0;var e=0,f=0;if(a[237104]|0){e=(c[59280]<<3)+8|0}else{f=c[59454]|0;c[59352]=f;c[59350]=f;c[59356]=c[59260];g[59360]=+g[59266];c[59346]=c[59344];c[59354]=c[59262];c[59280]=0;f=db(c[59282]|0,8,173288)|0;c[59282]=f;c[f+(c[59280]<<3)>>2]=(c[59272]|0)+(c[59242]|0);c[(c[59282]|0)+(c[59280]<<3)+4>>2]=((c[59240]|0)+(c[(c[3524]|0)+12>>2]|0)|0)-(c[59270]|0);c[59280]=1;a[237104]=1;e=16}f=db(c[59282]|0,e,173288)|0;c[59282]=f;c[f+(c[59280]<<3)>>2]=(c[59242]|0)+b;c[(c[59282]|0)+(c[59280]<<3)+4>>2]=((c[(c[3524]|0)+12>>2]|0)-d|0)+(c[59240]|0);f=(c[59280]|0)+1|0;c[59280]=f;if((f|0)<=(c[59278]|0)){c[59272]=b;c[59270]=d;return}tK(a[237104]&1);c[59272]=b;c[59270]=d;return}function ol(b){b=b|0;var d=0,e=0,f=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0;d=c[59260]|0;e=~~+g[59266];f=c[59454]|0;i=c[59344]|0;j=c[59262]|0;k=c[59446]|0;c[59344]=k;l=~~(+h[29726]*+(c[59448]|0));m=(l|0)<1?1:l;c[59262]=m;c[59454]=-1;n=(b|0)<-2?-2:b;do{if((n|0)==0|(n|0)==(-2|0)){c[59260]=0;g[59266]=0.0;if(!(a[237032]&(n|0)==0)){o=0;p=0.0;q=-1;r=k;s=m;break}c[59454]=4;o=0;p=0.0;q=4;r=k;s=m}else if((n|0)==(-1|0)){c[59260]=2;if(a[237072]|0){c[59260]=0;t=0}else{t=2}g[59266]=4.0;if(!(a[237032]|0)){o=t;p=4.0;q=-1;r=k;s=m;break}c[59454]=0;o=t;p=4.0;q=0;r=k;s=m}else{b=((n|0)/1e3&-1)+k|0;c[59344]=b;u=(n|0)%1e3&-1;v=(u+99|0)>>>0<199?l:(u|0)/100&-1;w=(v|0)<1?1:v;c[59262]=w;v=(u|0)%100&-1;if(a[237032]|0){u=(v|0)>8&1;c[59260]=u;x=c[237336+(((v|0)%9&-1)<<2)>>2]|0;c[59454]=x;y=(v|0)/9&-1;z=u;A=x}else{if((v|0)==0){B=0}else{B=((v|0)%2&-1)+1|0}c[59260]=B;y=(v+1|0)/2&-1;z=B;A=-1}C=+(y*3&-1|0);g[59266]=C;if(!(a[237072]|0)){o=z;p=C;q=A;r=b;s=w;break}c[59260]=0;o=0;p=C;q=A;r=b;s=w}}while(0);do{if((o|0)==(d|0)){if(!(p==+(e|0)&(q|0)==(f|0)&(r|0)==(i|0)&(s|0)==(j|0))){break}return}}while(0);tK(a[237104]&1);return}function om(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0,t=0,u=0,w=0;f=i;c[59260]=0;if((((e|0)%100&-1)-49|0)>>>0<50){j=e}else{k=(e|0)%13&-1;if((k|0)==12){l=78}else if((k|0)==8){l=88}else if((k|0)==4){l=68}else if((k|0)==10){l=98}else if((k|0)==6){c[59260]=0;tK(a[237104]&1);e=(c[59344]|0)-1|0;m=c[59454]|0;n=(c[59242]|0)+b|0;o=c[3524]|0;p=((c[o+12>>2]|0)-d|0)+(c[59240]|0)|0;q=~~(+h[29725]*+((c[o+16>>2]|0)>>>0>>>0)*.25+1.0);o=c[59262]|0;r=+g[59266];cf(c[10030]|0,173528,(v=i,i=i+128|0,c[v>>2]=c[59260],c[v+8>>2]=o,c[v+16>>2]=m,c[v+24>>2]=m,c[v+32>>2]=(e|0)<0?0:e,c[v+40>>2]=0,c[v+48>>2]=20,h[v+56>>3]=r,c[v+64>>2]=n,c[v+72>>2]=p,c[v+80>>2]=q,c[v+88>>2]=q,c[v+96>>2]=n,c[v+104>>2]=p,c[v+112>>2]=n,c[v+120>>2]=p-q,v)|0);i=f;return}else if((k|0)==7){l=84}else if((k|0)==11){l=74}else if((k|0)==3){c[59260]=0;tK(a[237104]&1);q=(c[59344]|0)-1|0;p=c[59454]|0;n=(c[59242]|0)+b|0;e=((c[(c[3524]|0)+12>>2]|0)-d|0)+(c[59240]|0)|0;m=c[59262]|0;r=+g[59266];cf(c[10030]|0,173472,(v=i,i=i+64|0,c[v>>2]=c[59260],c[v+8>>2]=m,c[v+16>>2]=p,c[v+24>>2]=p,c[v+32>>2]=(q|0)<0?0:q,c[v+40>>2]=0,c[v+48>>2]=-1,h[v+56>>3]=r,v)|0);q=~~(+h[29725]*+((c[(c[3524]|0)+16>>2]|0)>>>0>>>0)*.25+1.0);p=n-q|0;m=e-q|0;o=q+e|0;e=q+n|0;cf(c[10030]|0,173424,(v=i,i=i+80|0,c[v>>2]=p,c[v+8>>2]=m,c[v+16>>2]=p,c[v+24>>2]=o,c[v+32>>2]=e,c[v+40>>2]=o,c[v+48>>2]=e,c[v+56>>2]=m,c[v+64>>2]=p,c[v+72>>2]=m,v)|0);i=f;return}else if((k|0)==9){l=94}else if((k|0)==5){c[59260]=0;tK(a[237104]&1);m=(c[59344]|0)-1|0;p=c[59454]|0;e=(c[59242]|0)+b|0;o=c[3524]|0;n=((c[o+12>>2]|0)-d|0)+(c[59240]|0)|0;q=~~(+h[29725]*+((c[o+16>>2]|0)>>>0>>>0)*.25+1.0);o=c[59262]|0;r=+g[59266];cf(c[10030]|0,173528,(v=i,i=i+128|0,c[v>>2]=c[59260],c[v+8>>2]=o,c[v+16>>2]=p,c[v+24>>2]=p,c[v+32>>2]=(m|0)<0?0:m,c[v+40>>2]=0,c[v+48>>2]=-1,h[v+56>>3]=r,c[v+64>>2]=e,c[v+72>>2]=n,c[v+80>>2]=q,c[v+88>>2]=q,c[v+96>>2]=e,c[v+104>>2]=n,c[v+112>>2]=e,c[v+120>>2]=n-q,v)|0);i=f;return}else{ly(b,d,k);i=f;return}c[59260]=0;j=l}tK(a[237104]&1);if((j|0)>1e3){s=((j|0)/1e3&-1)+(c[59446]|0)|0}else{s=c[59344]|0}l=s-1|0;s=(j|0)%1e3&-1;j=(l|0)<0?0:l;do{if((s|0)<100){t=c[59454]|0}else{if(a[237032]|0){t=((s|0)/100&-1)-1|0;break}else{t=(s|0)>699?7:-1;break}}}while(0);l=(s|0)%100&-1;s=(l+1|0)%10&-1;if((s|0)<5){u=(a[237032]^1)<<31>>31}else{u=c[59454]|0}if((s|0)==5|(s|0)==0){w=-1}else{w=((s|0)%5&-1)*5&-1}s=(c[59242]|0)+b|0;b=c[3524]|0;k=((c[b+12>>2]|0)-d|0)+(c[59240]|0)|0;if((l-40|0)>>>0<19){d=~~(+h[29725]*+((c[b+16>>2]|0)>>>0>>>0)*.25+1.0);b=c[10030]|0;q=c[59260]|0;n=c[59262]|0;r=+g[59266];e=k-d|0;cf(b|0,173528,(v=i,i=i+128|0,c[v>>2]=q,c[v+8>>2]=n,c[v+16>>2]=u,c[v+24>>2]=t,c[v+32>>2]=j,c[v+40>>2]=0,c[v+48>>2]=w,h[v+56>>3]=r,c[v+64>>2]=s,c[v+72>>2]=k,c[v+80>>2]=d,c[v+88>>2]=d,c[v+96>>2]=s,c[v+104>>2]=k,c[v+112>>2]=s,c[v+120>>2]=e,v)|0);i=f;return}e=c[59262]|0;r=+g[59266];cf(c[10030]|0,173472,(v=i,i=i+64|0,c[v>>2]=c[59260],c[v+8>>2]=e,c[v+16>>2]=u,c[v+24>>2]=t,c[v+32>>2]=j,c[v+40>>2]=0,c[v+48>>2]=w,h[v+56>>3]=r,v)|0);if((l-59|0)>>>0<10){w=~~(+h[29725]*+((c[(c[3524]|0)+16>>2]|0)>>>0>>>0)*.25+1.0);j=c[10030]|0;t=s-w|0;u=k-w|0;e=w+k|0;d=w+s|0;cf(j|0,173424,(v=i,i=i+80|0,c[v>>2]=t,c[v+8>>2]=u,c[v+16>>2]=t,c[v+24>>2]=e,c[v+32>>2]=d,c[v+40>>2]=e,c[v+48>>2]=d,c[v+56>>2]=u,c[v+64>>2]=t,c[v+72>>2]=u,v)|0);i=f;return}if((l-69|0)>>>0<10){u=~~(+h[29725]*+((c[(c[3524]|0)+16>>2]|0)>>>0>>>0)/3.0+1.0);t=c[10030]|0;d=s-u|0;e=u+k|0;j=u+s|0;w=k-u|0;cf(t|0,173424,(v=i,i=i+80|0,c[v>>2]=d,c[v+8>>2]=k,c[v+16>>2]=s,c[v+24>>2]=e,c[v+32>>2]=j,c[v+40>>2]=k,c[v+48>>2]=s,c[v+56>>2]=w,c[v+64>>2]=d,c[v+72>>2]=k,v)|0);i=f;return}if((l-79|0)>>>0<10){d=~~(+h[29725]*+((c[(c[3524]|0)+16>>2]|0)>>>0>>>0)/3.0+1.0);w=(d<<2|0)/7&-1;j=c[10030]|0;e=s-d|0;t=w+k|0;u=k-(w<<1)|0;w=d+s|0;cf(j|0,173392,(v=i,i=i+64|0,c[v>>2]=e,c[v+8>>2]=t,c[v+16>>2]=s,c[v+24>>2]=u,c[v+32>>2]=w,c[v+40>>2]=t,c[v+48>>2]=e,c[v+56>>2]=t,v)|0);i=f;return}if((l-89|0)>>>0>=10){i=f;return}l=~~(+h[29725]*+((c[(c[3524]|0)+16>>2]|0)>>>0>>>0)/3.0+1.0);t=(l<<2|0)/7&-1;e=s-l|0;w=k-t|0;cf(c[10030]|0,173392,(v=i,i=i+64|0,c[v>>2]=e,c[v+8>>2]=w,c[v+16>>2]=s,c[v+24>>2]=(t<<1)+k,c[v+32>>2]=l+s,c[v+40>>2]=w,c[v+48>>2]=e,c[v+56>>2]=w,v)|0);i=f;return}function on(b,d,e,f,j){b=b|0;d=d|0;e=e|0;f=f|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0,u=0,w=0.0,x=0.0,y=0.0,z=0.0,A=0;k=i;tK(a[237104]&1);l=(c[59356]|0)==2&1;m=a[237432]&1;n=c[59260]|0;o=c[59262]|0;p=c[59454]|0;q=c[59344]|0;r=a[237392]<<31>>31;s=+g[59266];t=(j|0)!=0;u=(j|0)==2;cf(c[10030]|0,173752,(v=i,i=i+128|0,c[v>>2]=2,c[v+8>>2]=m,c[v+16>>2]=n,c[v+24>>2]=o,c[v+32>>2]=p,c[v+40>>2]=p,c[v+48>>2]=q,c[v+56>>2]=0,c[v+64>>2]=r,h[v+72>>3]=s,c[v+80>>2]=0,c[v+88>>2]=l,c[v+96>>2]=0,c[v+104>>2]=t&1,c[v+112>>2]=u&1,c[v+120>>2]=2,v)|0);do{if(t){l=c[12910]|0;if((l|0)==0){r=c[(c[3524]|0)+28>>2]|0;w=+(r>>>0>>>0);x=+(((r>>>1)+1|0)>>>0>>>0)}else{s=+(l|0);y=+h[6458]*3.141592653589793/180.0;z=s*2.0*+T(+y);w=s*+S(+y);x=z}z=+h[6457];if(z<70.0){A=2}else{A=z>110.0?3:1}l=(c[12912]|0)==2&1;r=c[10030]|0;cf(r|0,173640,(v=i,i=i+40|0,c[v>>2]=A,c[v+8>>2]=l,h[v+16>>3]=1.0,h[v+24>>3]=x,h[v+32>>3]=w,v)|0);if(!u){break}r=c[10030]|0;cf(r|0,173640,(v=i,i=i+40|0,c[v>>2]=A,c[v+8>>2]=l,h[v+16>>3]=1.0,h[v+24>>3]=x,h[v+32>>3]=w,v)|0)}}while(0);A=c[59240]|0;u=(c[(c[3524]|0)+12>>2]|0)+A|0;cf(c[10030]|0,173608,(v=i,i=i+32|0,c[v>>2]=(c[59242]|0)+b,c[v+8>>2]=u-d,c[v+16>>2]=A+e,c[v+24>>2]=u-f,v)|0);c[59272]=e;c[59270]=f;i=k;return}function oo(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0,s=0,t=0,u=0.0,w=0.0;f=i;if((a[e]|0)==0){i=f;return}j=uA(e|0)<<1|1;k=ut(j)|0;do{if((k|0)==0){gk();l=ut(j)|0;if((l|0)!=0){m=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=173376,v)|0)}else{m=k}}while(0);k=m;j=e;while(1){l=a[j]|0;if(l<<24>>24==92){a[k]=92;n=k+1|0;o=a[j]|0}else{n=k;o=l}a[n]=o;if((a[j]|0)==0){break}else{k=n+1|0;j=j+1|0}}tK(a[237104]&1);p=+g[59456];q=p;j=c[3524]|0;n=c[j+16>>2]|0;if(p==0.0){r=b;s=n>>>1}else{p=+(n>>>0>>>0);k=~~(p*+T(+q)*.25)+b|0;r=k;s=~~(p*+S(+q)*.25)}k=c[10030]|0;b=c[59364]|0;o=c[59454]|0;l=c[59444]|0;p=+(c[59442]|0);t=c[59264]|0;u=+((c[j+20>>2]|0)>>>0>>>0);w=u*+((uA(e|0)|0)>>>0>>>0);e=(c[59242]|0)+r|0;r=((s-d|0)+(c[j+12>>2]|0)|0)+(c[59240]|0)|0;cf(k|0,173312,(v=i,i=i+112|0,c[v>>2]=4,c[v+8>>2]=b,c[v+16>>2]=o,c[v+24>>2]=0,c[v+32>>2]=-1,c[v+40>>2]=l,h[v+48>>3]=p,h[v+56>>3]=q,c[v+64>>2]=t,h[v+72>>3]=+(n>>>0>>>0),h[v+80>>3]=w,c[v+88>>2]=e,c[v+96>>2]=r,c[v+104>>2]=m,v)|0);uu(m);i=f;return}function op(a){a=+a;h[29726]=a;return}function oq(b,d,e,f,j){b=b|0;d=d|0;e=e|0;f=f|0;j=j|0;var k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0;k=i;tK(a[237104]&1);l=c[59454]|0;c[59352]=l;m=b&15;do{if((m|0)==1|(m|0)==4){n=b>>4;o=(n|0)<0?0:n;n=(o|0)>100?20:(o|0)/5&-1;if((l|0)==(-1|0)|(l|0)==0){p=n;q=l;break}p=40-n|0;q=l}else if((m|0)==2|(m|0)==5){n=b>>4;p=(n|0)<0?41:n+41|0;q=7}else{p=20;q=7}}while(0);b=(c[59242]|0)+d|0;d=((c[(c[3524]|0)+12>>2]|0)-e|0)+(c[59240]|0)|0;e=a[237432]&1;m=c[59356]|0;n=c[59354]|0;o=c[59346]|0;r=+g[59360];s=b+f|0;f=d-j|0;cf(c[10030]|0,173808,(v=i,i=i+208|0,c[v>>2]=2,c[v+8>>2]=e,c[v+16>>2]=m,c[v+24>>2]=n,c[v+32>>2]=l,c[v+40>>2]=q,c[v+48>>2]=o,c[v+56>>2]=0,c[v+64>>2]=p,h[v+72>>3]=r,c[v+80>>2]=0,c[v+88>>2]=0,c[v+96>>2]=0,c[v+104>>2]=0,c[v+112>>2]=0,c[v+120>>2]=5,c[v+128>>2]=b,c[v+136>>2]=d,c[v+144>>2]=s,c[v+152>>2]=d,c[v+160>>2]=s,c[v+168>>2]=f,c[v+176>>2]=b,c[v+184>>2]=f,c[v+192>>2]=b,c[v+200>>2]=d,v)|0);i=k;return}function or(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0;d=i;if((b|0)==0){e=128;i=d;return e|0}tK(a[237104]&1);if(a[237136]|0){f=c[m>>2]|0;aI(173976,34,1,f|0);e=0;i=d;return e|0}a[237136]=1;f=a[237032]^1;g=c[5163]|0;if(!((g|0)==103|f)){if((c[5169]|0)<=0){e=0;i=d;return e|0}j=b+32|0;b=0;while(1){k=c[j>>2]|0;l=~~(+h[k+(b*24&-1)>>3]*255.0+.5);n=~~(+h[k+(b*24&-1)+8>>3]*255.0+.5);o=~~(+h[k+(b*24&-1)+16>>3]*255.0+.5);cf(c[10030]|0,174016,(v=i,i=i+40|0,c[v>>2]=0,c[v+8>>2]=b+32,c[v+16>>2]=l,c[v+24>>2]=n,c[v+32>>2]=o,v)|0);o=b+1|0;if((o|0)<(c[5169]|0)){b=o}else{e=0;break}}i=d;return e|0}if((g|0)==114&f){f=c[m>>2]|0;aI(174040,57,1,f|0)}f=c[5169]|0;if((f|0)>0){p=0;q=f}else{e=0;i=d;return e|0}while(1){f=~~(+(p|0)*255.0/+(q-1|0)+.5);cf(c[10030]|0,174016,(v=i,i=i+40|0,c[v>>2]=0,c[v+8>>2]=p+32,c[v+16>>2]=f,c[v+24>>2]=f,c[v+32>>2]=f,v)|0);f=p+1|0;g=c[5169]|0;if((f|0)<(g|0)){p=f;q=g}else{e=0;break}}i=d;return e|0}function os(b){b=b|0;var d=0.0,e=0,f=0,g=0;d=+h[b+8>>3];e=c[b>>2]|0;if((e|0)==5){if(d>0.0){f=~~(d*+(c[5169]|0))}else{f=0}if(!(a[237136]|0)){g=c[m>>2]|0;aI(174104,29,1,g|0)}g=(f|0)>127?159:f+32|0;if((c[59454]|0)==(g|0)){return}tK(a[237104]&1);c[59454]=g;return}else if((e|0)==1){ol(c[b+4>>2]|0);return}else{return}}function ot(b,d){b=b|0;d=d|0;var e=0,f=0,j=0,k=0,l=0.0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0;e=i;tK(a[237104]&1);f=c[59356]|0;j=c[59454]|0;k=c[59346]|0;l=+g[59360];cf(c[10030]|0,174184,(v=i,i=i+128|0,c[v>>2]=2,c[v+8>>2]=3,c[v+16>>2]=f,c[v+24>>2]=0,c[v+32>>2]=j,c[v+40>>2]=j,c[v+48>>2]=k,c[v+56>>2]=0,c[v+64>>2]=20,h[v+72>>3]=l,c[v+80>>2]=0,c[v+88>>2]=0,c[v+96>>2]=0,c[v+104>>2]=0,c[v+112>>2]=0,c[v+120>>2]=b+1,v)|0);k=c[10030]|0;j=c[59242]|0;if((b|0)>0){f=b-1|0;m=0;n=0;o=k;p=j;while(1){q=((c[59240]|0)+(c[(c[3524]|0)+12>>2]|0)|0)-(c[d+(m*12&-1)+4>>2]|0)|0;cf(o|0,174136,(v=i,i=i+16|0,c[v>>2]=(c[d+(m*12&-1)>>2]|0)+p,c[v+8>>2]=q,v)|0);if((n|0)<5|(m|0)==(f|0)){r=n+1|0}else{q=c[10030]|0;aI(84248,2,1,q|0);r=0}q=m+1|0;s=c[10030]|0;t=c[59242]|0;if((q|0)<(b|0)){m=q;n=r;o=s;p=t}else{u=r;w=s;x=t;break}}}else{u=0;w=k;x=j}j=((c[59240]|0)+(c[(c[3524]|0)+12>>2]|0)|0)-(c[d+4>>2]|0)|0;cf(w|0,174136,(v=i,i=i+16|0,c[v>>2]=(c[d>>2]|0)+x,c[v+8>>2]=j,v)|0);if((u|0)==-1){i=e;return}aF(10,c[10030]|0);i=e;return}function ou(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;if((b|0)==6){tK(a[237104]&1);e=c[10030]|0;aF(54,e|0);e=c[10030]|0;f=c[59242]|0;g=(c[7938]|0)+f|0;h=(c[59240]|0)+(c[(c[3524]|0)+12>>2]|0)|0;j=h-(c[7941]|0)|0;k=(c[7939]|0)+f|0;f=h-(c[7940]|0)|0;cf(e|0,174424,(v=i,i=i+32|0,c[v>>2]=g,c[v+8>>2]=j,c[v+16>>2]=k,c[v+24>>2]=f,v)|0);f=c[10030]|0;k=(c[59362]|0)+1|0;c[59362]=k;cf(f|0,174400,(v=i,i=i+8|0,c[v>>2]=k,v)|0);i=d;return}else if((b|0)==7){tK(a[237104]&1);cf(c[10030]|0,174320,(v=i,i=i+8|0,c[v>>2]=c[59362],v)|0);aI(174256,3,1,c[10030]|0);i=d;return}else if((b|0)==0){c[59362]=0;i=d;return}else{i=d;return}}function ov(){var a=0,b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0;a=i;b=ut(28)|0;do{if((b|0)==0){gk();d=ut(28)|0;if((d|0)!=0){e=d;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{e=b}}while(0);b=e;c[e>>2]=0;c[e+12>>2]=7;c[e+4>>2]=2048;c[e+8>>2]=0;d=ut(8192)|0;do{if((d|0)==0){gk();f=ut(8192)|0;if((f|0)!=0){g=f;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{g=d}}while(0);d=e+16|0;c[d>>2]=g;g=ut(8192)|0;do{if((g|0)==0){gk();f=ut(8192)|0;if((f|0)!=0){h=f;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{h=g}}while(0);g=e+20|0;c[g>>2]=h;h=ut(2048)|0;do{if((h|0)==0){gk();f=ut(2048)|0;if((f|0)!=0){j=f;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{j=h}}while(0);c[e+24>>2]=j;do{if((c[d>>2]|0)==0){k=0}else{if((c[g>>2]|0)==0){k=0;break}uE(j|0,0,2048);k=b}}while(0);c[59112]=k;c[59113]=k;k=ut(28)|0;do{if((k|0)==0){gk();b=ut(28)|0;if((b|0)!=0){l=b;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{l=k}}while(0);k=l;c[l>>2]=2;c[l+12>>2]=1;c[l+4>>2]=3072;c[l+8>>2]=0;b=ut(12288)|0;do{if((b|0)==0){gk();j=ut(12288)|0;if((j|0)!=0){m=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{m=b}}while(0);b=l+16|0;c[b>>2]=m;m=ut(12288)|0;do{if((m|0)==0){gk();j=ut(12288)|0;if((j|0)!=0){n=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{n=m}}while(0);m=l+20|0;c[m>>2]=n;n=ut(3072)|0;do{if((n|0)==0){gk();j=ut(3072)|0;if((j|0)!=0){o=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{o=n}}while(0);c[l+24>>2]=o;do{if((c[b>>2]|0)==0){p=0}else{if((c[m>>2]|0)==0){p=0;break}uE(o|0,0,3072);p=k}}while(0);c[59114]=p;c[59115]=c[59112];p=ut(28)|0;do{if((p|0)==0){gk();k=ut(28)|0;if((k|0)!=0){q=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{q=p}}while(0);p=q;c[q>>2]=4;c[q+12>>2]=5;c[q+4>>2]=1024;c[q+8>>2]=0;k=ut(4096)|0;do{if((k|0)==0){gk();o=ut(4096)|0;if((o|0)!=0){r=o;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{r=k}}while(0);k=q+16|0;c[k>>2]=r;r=ut(4096)|0;do{if((r|0)==0){gk();o=ut(4096)|0;if((o|0)!=0){s=o;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{s=r}}while(0);r=q+20|0;c[r>>2]=s;s=ut(1024)|0;do{if((s|0)==0){gk();o=ut(1024)|0;if((o|0)!=0){t=o;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{t=s}}while(0);c[q+24>>2]=t;do{if((c[k>>2]|0)==0){u=0}else{if((c[r>>2]|0)==0){u=0;break}uE(t|0,0,1024);u=p}}while(0);c[59116]=u;u=ut(28)|0;do{if((u|0)==0){gk();p=ut(28)|0;if((p|0)!=0){w=p;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{w=u}}while(0);u=w;c[w>>2]=5;c[w+12>>2]=6;c[w+4>>2]=256;c[w+8>>2]=0;p=ut(1024)|0;do{if((p|0)==0){gk();t=ut(1024)|0;if((t|0)!=0){x=t;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{x=p}}while(0);p=w+16|0;c[p>>2]=x;x=ut(1024)|0;do{if((x|0)==0){gk();t=ut(1024)|0;if((t|0)!=0){y=t;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{y=x}}while(0);x=w+20|0;c[x>>2]=y;y=ut(256)|0;do{if((y|0)==0){gk();t=ut(256)|0;if((t|0)!=0){z=t;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{z=y}}while(0);c[w+24>>2]=z;do{if((c[p>>2]|0)==0){A=0}else{if((c[x>>2]|0)==0){A=0;break}uE(z|0,0,256);A=u}}while(0);c[59117]=A;A=ut(28)|0;do{if((A|0)==0){gk();u=ut(28)|0;if((u|0)!=0){B=u;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{B=A}}while(0);A=B;c[B>>2]=6;c[B+12>>2]=8;c[B+4>>2]=256;c[B+8>>2]=0;u=ut(1024)|0;do{if((u|0)==0){gk();z=ut(1024)|0;if((z|0)!=0){C=z;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{C=u}}while(0);u=B+16|0;c[u>>2]=C;C=ut(1024)|0;do{if((C|0)==0){gk();z=ut(1024)|0;if((z|0)!=0){D=z;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{D=C}}while(0);C=B+20|0;c[C>>2]=D;D=ut(256)|0;do{if((D|0)==0){gk();z=ut(256)|0;if((z|0)!=0){E=z;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{E=D}}while(0);c[B+24>>2]=E;do{if((c[u>>2]|0)==0){F=0}else{if((c[C>>2]|0)==0){F=0;break}uE(E|0,0,256);F=A}}while(0);c[59118]=F;F=ut(28)|0;do{if((F|0)==0){gk();A=ut(28)|0;if((A|0)!=0){G=A;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{G=F}}while(0);F=G;c[G>>2]=7;c[G+12>>2]=9;c[G+4>>2]=128;c[G+8>>2]=0;A=ut(512)|0;do{if((A|0)==0){gk();E=ut(512)|0;if((E|0)!=0){H=E;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{H=A}}while(0);A=G+16|0;c[A>>2]=H;H=ut(512)|0;do{if((H|0)==0){gk();E=ut(512)|0;if((E|0)!=0){I=E;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{I=H}}while(0);H=G+20|0;c[H>>2]=I;I=ut(128)|0;do{if((I|0)==0){gk();E=ut(128)|0;if((E|0)!=0){J=E;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{J=I}}while(0);c[G+24>>2]=J;do{if((c[A>>2]|0)==0){K=0}else{if((c[H>>2]|0)==0){K=0;break}uE(J|0,0,128);K=F}}while(0);c[59119]=K;K=ut(28)|0;do{if((K|0)==0){gk();F=ut(28)|0;if((F|0)!=0){L=F;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{L=K}}while(0);K=L;c[L>>2]=8;c[L+12>>2]=10;c[L+4>>2]=128;c[L+8>>2]=0;F=ut(512)|0;do{if((F|0)==0){gk();J=ut(512)|0;if((J|0)!=0){M=J;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{M=F}}while(0);F=L+16|0;c[F>>2]=M;M=ut(512)|0;do{if((M|0)==0){gk();J=ut(512)|0;if((J|0)!=0){N=J;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{N=M}}while(0);M=L+20|0;c[M>>2]=N;N=ut(128)|0;do{if((N|0)==0){gk();J=ut(128)|0;if((J|0)!=0){O=J;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{O=N}}while(0);c[L+24>>2]=O;do{if((c[F>>2]|0)==0){P=0}else{if((c[M>>2]|0)==0){P=0;break}uE(O|0,0,128);P=K}}while(0);c[59120]=P;P=ut(28)|0;do{if((P|0)==0){gk();K=ut(28)|0;if((K|0)!=0){Q=K;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{Q=P}}while(0);P=Q;c[Q>>2]=9;c[Q+12>>2]=6;c[Q+4>>2]=64;c[Q+8>>2]=0;K=ut(256)|0;do{if((K|0)==0){gk();O=ut(256)|0;if((O|0)!=0){R=O;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{R=K}}while(0);K=Q+16|0;c[K>>2]=R;R=ut(256)|0;do{if((R|0)==0){gk();O=ut(256)|0;if((O|0)!=0){S=O;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{S=R}}while(0);R=Q+20|0;c[R>>2]=S;S=ut(64)|0;do{if((S|0)==0){gk();O=ut(64)|0;if((O|0)!=0){T=O;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{T=S}}while(0);c[Q+24>>2]=T;do{if((c[K>>2]|0)==0){U=0}else{if((c[R>>2]|0)==0){U=0;break}uE(T|0,0,64);U=P}}while(0);c[59121]=U;U=ut(28)|0;do{if((U|0)==0){gk();P=ut(28)|0;if((P|0)!=0){V=P;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{V=U}}while(0);U=V;c[V>>2]=10;c[V+12>>2]=4;c[V+4>>2]=64;c[V+8>>2]=0;P=ut(256)|0;do{if((P|0)==0){gk();T=ut(256)|0;if((T|0)!=0){W=T;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{W=P}}while(0);P=V+16|0;c[P>>2]=W;W=ut(256)|0;do{if((W|0)==0){gk();T=ut(256)|0;if((T|0)!=0){X=T;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{X=W}}while(0);W=V+20|0;c[W>>2]=X;X=ut(64)|0;do{if((X|0)==0){gk();T=ut(64)|0;if((T|0)!=0){Y=T;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{Y=X}}while(0);c[V+24>>2]=Y;do{if((c[P>>2]|0)==0){Z=0}else{if((c[W>>2]|0)==0){Z=0;break}uE(Y|0,0,64);Z=U}}while(0);c[59122]=Z;Z=ut(28)|0;do{if((Z|0)==0){gk();U=ut(28)|0;if((U|0)!=0){_=U;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{_=Z}}while(0);Z=_;c[_>>2]=11;c[_+12>>2]=11;c[_+4>>2]=512;c[_+8>>2]=0;U=ut(2048)|0;do{if((U|0)==0){gk();Y=ut(2048)|0;if((Y|0)!=0){$=Y;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{$=U}}while(0);U=_+16|0;c[U>>2]=$;$=ut(2048)|0;do{if(($|0)==0){gk();Y=ut(2048)|0;if((Y|0)!=0){aa=Y;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{aa=$}}while(0);$=_+20|0;c[$>>2]=aa;aa=ut(512)|0;do{if((aa|0)==0){gk();Y=ut(512)|0;if((Y|0)!=0){ab=Y;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{ab=aa}}while(0);c[_+24>>2]=ab;if((c[U>>2]|0)==0){ac=0;c[59123]=ac;ad=c[59122]|0;c[59128]=ad;c[59108]=0;ae=c[10030]|0;af=aI(174480,10,1,ae|0)|0;ag=c[10030]|0;ah=aD(ag|0)|0;i=a;return}if((c[$>>2]|0)==0){ac=0;c[59123]=ac;ad=c[59122]|0;c[59128]=ad;c[59108]=0;ae=c[10030]|0;af=aI(174480,10,1,ae|0)|0;ag=c[10030]|0;ah=aD(ag|0)|0;i=a;return}uE(ab|0,0,512);ac=Z;c[59123]=ac;ad=c[59122]|0;c[59128]=ad;c[59108]=0;ae=c[10030]|0;af=aI(174480,10,1,ae|0)|0;ag=c[10030]|0;ah=aD(ag|0)|0;i=a;return}function ow(a){a=a|0;var b=0,d=0;b=(a|0)<-2?-2:a;if((b|0)>8){d=(b|0)%9&-1}else{d=b}b=c[236448+(d+2<<2)>>2]|0;if((b|0)==(c[59128]|0)){c[59106]=(c[59106]|0)+1;return}else{c[59128]=b;return}}function ox(){var a=0,b=0;a=2;do{b=c[236448+(a<<2)>>2]|0;uu(c[b+16>>2]|0);uu(c[b+20>>2]|0);uu(c[b+24>>2]|0);uu(b);a=a+1|0;}while((a|0)<12);return}function oy(){var a=0,b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;a=c[59112]|0;b=c[59108]|0;if((c[a+12>>2]|0)==(b|0)){tJ(a);d=c[59108]|0}else{d=b}b=c[59113]|0;if((c[b+12>>2]|0)==(d|0)){tJ(b);e=c[59108]|0}else{e=d}d=c[59114]|0;if((c[d+12>>2]|0)==(e|0)){tJ(d);f=c[59108]|0}else{f=e}e=c[59115]|0;if((c[e+12>>2]|0)==(f|0)){tJ(e);g=c[59108]|0}else{g=f}f=c[59116]|0;if((c[f+12>>2]|0)==(g|0)){tJ(f);h=c[59108]|0}else{h=g}g=c[59117]|0;if((c[g+12>>2]|0)==(h|0)){tJ(g);i=c[59108]|0}else{i=h}h=c[59118]|0;if((c[h+12>>2]|0)==(i|0)){tJ(h);j=c[59108]|0}else{j=i}i=c[59119]|0;if((c[i+12>>2]|0)==(j|0)){tJ(i);k=c[59108]|0}else{k=j}j=c[59120]|0;if((c[j+12>>2]|0)==(k|0)){tJ(j);l=c[59108]|0}else{l=k}k=c[59121]|0;if((c[k+12>>2]|0)==(l|0)){tJ(k);m=c[59108]|0}else{m=l}l=c[59122]|0;if((c[l+12>>2]|0)==(m|0)){tJ(l);n=c[59108]|0}else{n=m}m=c[59123]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m);o=0}else{o=0}do{m=236448+(o<<2)|0;tJ(c[m>>2]|0);n=c[(c[m>>2]|0)+12>>2]|0;m=c[59112]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59113]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59114]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59115]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59116]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59117]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59118]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59119]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59120]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59121]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59122]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}m=c[59123]|0;if((c[m+12>>2]|0)==(n|0)){tJ(m)}o=o+1|0;}while((o|0)<12);aI(174496,5,1,c[10030]|0);aD(c[10030]|0);return}function oz(){aI(174504,8,1,c[10030]|0);c[59096]=-200;c[59098]=-200;c[59124]=800;c[59126]=800;a[236440]=1;c[59104]=0;c[59102]=0;c[59106]=0;c[59100]=0;return}function oA(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0;e=c[59128]|0;f=c[e+8>>2]|0;do{if((f|0)>0){g=f-1|0;h=(c[e+16>>2]|0)+(g<<2)|0;do{if((c[h>>2]|0)==(b|0)){if((c[(c[e+20>>2]|0)+(g<<2)>>2]|0)!=(d|0)){break}c[59104]=(c[59104]|0)+1;return}}while(0);if((a[(c[e+24>>2]|0)+g|0]&1)==0){break}c[h>>2]=b;i=c[59128]|0;c[(c[i+20>>2]|0)+((c[i+8>>2]|0)-1<<2)>>2]=d;c[59104]=(c[59104]|0)+1;return}}while(0);if((f|0)==(c[e+4>>2]|0)){tI();i=c[59128]|0;j=i;k=c[i+8>>2]|0}else{j=e;k=f}c[(c[j+16>>2]|0)+(k<<2)>>2]=b;b=c[59128]|0;c[(c[b+20>>2]|0)+(c[b+8>>2]<<2)>>2]=d;d=c[59128]|0;a[(c[d+24>>2]|0)+(c[d+8>>2]|0)|0]=1;d=(c[59128]|0)+8|0;c[d>>2]=(c[d>>2]|0)+1;return}function oB(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0;e=c[59128]|0;f=c[e+8>>2]|0;do{if((f|0)>2){g=f-1|0;if((c[(c[e+16>>2]|0)+(g<<2)>>2]|0)!=(b|0)){break}if((c[(c[e+20>>2]|0)+(g<<2)>>2]|0)!=(d|0)){break}if((a[(c[e+24>>2]|0)+g|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;return}}while(0);if((f|0)==(c[e+4>>2]|0)){tI();g=c[59128]|0;h=g;i=c[g+8>>2]|0}else{h=e;i=f}c[(c[h+16>>2]|0)+(i<<2)>>2]=b;b=c[59128]|0;c[(c[b+20>>2]|0)+(c[b+8>>2]<<2)>>2]=d;d=c[59128]|0;a[(c[d+24>>2]|0)+(c[d+8>>2]|0)|0]=0;d=(c[59128]|0)+8|0;c[d>>2]=(c[d>>2]|0)+1;return}function oC(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0;f=i;i=i+32|0;if((a[e]|0)==0){i=f;return}aI(174736,3,1,c[10030]|0);if(!(a[236440]|0)){g=c[10030]|0;aF(115,g|0);a[236440]=1}g=f+16|0;h=d-(c[59124]|0)|0;be(g|0,174720,(v=i,i=i+16|0,c[v>>2]=b-(c[59126]|0),c[v+8>>2]=h,v)|0);h=f|0;be(h|0,174712,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);j=uA(g|0)|0;k=j>>>0<(uA(h|0)|0)>>>0;j=c[10030]|0;if(k){aK(g|0,j|0)}else{aK(h|0,j|0)}aK(e|0,c[10030]|0);c[59126]=b;c[59098]=b;c[59124]=d;c[59096]=d;i=f;return}function oD(a){a=a|0;var b=0;b=i;cf(c[10030]|0,174744,(v=i,i=i+8|0,c[v>>2]=(a|0)!=0?2:1,v)|0);i=b;return 1}function oE(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0;f=c[59128]|0;g=c[f+12>>2]|0;h=(g|0)!=1;do{if(h){i=c[59114]|0;if((i|0)==(f|0)){c[59106]=(c[59106]|0)+1;j=g;k=f;break}else{c[59128]=i;j=g;k=i;break}}else{j=0;k=f}}while(0);if((e|0)<0){f=c[59123]|0;c[59128]=f;g=c[f+8>>2]|0;do{if((g|0)>2){i=g-1|0;if((c[(c[f+16>>2]|0)+(i<<2)>>2]|0)!=(b|0)){l=14737;break}if((c[(c[f+20>>2]|0)+(i<<2)>>2]|0)!=(d|0)){l=14737;break}if((a[(c[f+24>>2]|0)+i|0]&1)!=0){l=14737;break}c[59102]=(c[59102]|0)+1}else{l=14737}}while(0);if((l|0)==14737){if((g|0)==(c[f+4>>2]|0)){tI();i=c[59128]|0;m=i;n=c[i+8>>2]|0}else{m=f;n=g}c[(c[m+16>>2]|0)+(n<<2)>>2]=b;n=c[59128]|0;c[(c[n+20>>2]|0)+(c[n+8>>2]<<2)>>2]=d;n=c[59128]|0;a[(c[n+24>>2]|0)+(c[n+8>>2]|0)|0]=0;n=(c[59128]|0)+8|0;c[n>>2]=(c[n>>2]|0)+1}c[59128]=k}k=(e|0)%9&-1;L19947:do{if((k|0)==6){e=b+2|0;n=d+2|0;oA(e,n);m=b-2|0;g=d-2|0;f=c[59128]|0;i=c[f+8>>2]|0;do{if((i|0)>2){o=i-1|0;if((c[(c[f+16>>2]|0)+(o<<2)>>2]|0)!=(m|0)){l=14891;break}if((c[(c[f+20>>2]|0)+(o<<2)>>2]|0)!=(g|0)){l=14891;break}if((a[(c[f+24>>2]|0)+o|0]&1)!=0){l=14891;break}c[59102]=(c[59102]|0)+1}else{l=14891}}while(0);if((l|0)==14891){if((i|0)==(c[f+4>>2]|0)){tI();o=c[59128]|0;p=o;q=c[o+8>>2]|0}else{p=f;q=i}c[(c[p+16>>2]|0)+(q<<2)>>2]=m;o=c[59128]|0;c[(c[o+20>>2]|0)+(c[o+8>>2]<<2)>>2]=g;o=c[59128]|0;a[(c[o+24>>2]|0)+(c[o+8>>2]|0)|0]=0;o=(c[59128]|0)+8|0;c[o>>2]=(c[o>>2]|0)+1}oA(m,n);o=c[59128]|0;r=c[o+8>>2]|0;do{if((r|0)>2){s=r-1|0;if((c[(c[o+16>>2]|0)+(s<<2)>>2]|0)!=(e|0)){break}if((c[(c[o+20>>2]|0)+(s<<2)>>2]|0)!=(g|0)){break}if((a[(c[o+24>>2]|0)+s|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;break L19947}}while(0);if((r|0)==(c[o+4>>2]|0)){tI();n=c[59128]|0;t=n;u=c[n+8>>2]|0}else{t=o;u=r}c[(c[t+16>>2]|0)+(u<<2)>>2]=e;n=c[59128]|0;c[(c[n+20>>2]|0)+(c[n+8>>2]<<2)>>2]=g;n=c[59128]|0;a[(c[n+24>>2]|0)+(c[n+8>>2]|0)|0]=0;n=(c[59128]|0)+8|0;c[n>>2]=(c[n>>2]|0)+1}else if((k|0)==2){n=b-2|0;oA(n,d);m=b+2|0;i=d+2|0;f=c[59128]|0;s=c[f+8>>2]|0;do{if((s|0)>2){v=s-1|0;if((c[(c[f+16>>2]|0)+(v<<2)>>2]|0)!=(m|0)){l=14795;break}if((c[(c[f+20>>2]|0)+(v<<2)>>2]|0)!=(i|0)){l=14795;break}if((a[(c[f+24>>2]|0)+v|0]&1)!=0){l=14795;break}c[59102]=(c[59102]|0)+1;w=f}else{l=14795}}while(0);if((l|0)==14795){if((s|0)==(c[f+4>>2]|0)){tI();g=c[59128]|0;x=g;y=c[g+8>>2]|0}else{x=f;y=s}c[(c[x+16>>2]|0)+(y<<2)>>2]=m;g=c[59128]|0;c[(c[g+20>>2]|0)+(c[g+8>>2]<<2)>>2]=i;g=c[59128]|0;a[(c[g+24>>2]|0)+(c[g+8>>2]|0)|0]=0;g=(c[59128]|0)+8|0;c[g>>2]=(c[g>>2]|0)+1;w=c[59128]|0}g=d-2|0;e=c[w+8>>2]|0;do{if((e|0)>2){r=e-1|0;if((c[(c[w+16>>2]|0)+(r<<2)>>2]|0)!=(m|0)){l=14803;break}if((c[(c[w+20>>2]|0)+(r<<2)>>2]|0)!=(g|0)){l=14803;break}if((a[(c[w+24>>2]|0)+r|0]&1)!=0){l=14803;break}c[59102]=(c[59102]|0)+1;z=w}else{l=14803}}while(0);if((l|0)==14803){if((e|0)==(c[w+4>>2]|0)){tI();i=c[59128]|0;A=i;B=c[i+8>>2]|0}else{A=w;B=e}c[(c[A+16>>2]|0)+(B<<2)>>2]=m;i=c[59128]|0;c[(c[i+20>>2]|0)+(c[i+8>>2]<<2)>>2]=g;i=c[59128]|0;a[(c[i+24>>2]|0)+(c[i+8>>2]|0)|0]=0;i=(c[59128]|0)+8|0;c[i>>2]=(c[i>>2]|0)+1;z=c[59128]|0}i=c[z+8>>2]|0;do{if((i|0)>2){s=i-1|0;if((c[(c[z+16>>2]|0)+(s<<2)>>2]|0)!=(n|0)){break}if((c[(c[z+20>>2]|0)+(s<<2)>>2]|0)!=(d|0)){break}if((a[(c[z+24>>2]|0)+s|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;break L19947}}while(0);if((i|0)==(c[z+4>>2]|0)){tI();g=c[59128]|0;C=g;D=c[g+8>>2]|0}else{C=z;D=i}c[(c[C+16>>2]|0)+(D<<2)>>2]=n;g=c[59128]|0;c[(c[g+20>>2]|0)+(c[g+8>>2]<<2)>>2]=d;g=c[59128]|0;a[(c[g+24>>2]|0)+(c[g+8>>2]|0)|0]=0;g=(c[59128]|0)+8|0;c[g>>2]=(c[g>>2]|0)+1}else if((k|0)==3){g=b+2|0;oA(g,d);m=b-2|0;e=d+2|0;s=c[59128]|0;f=c[s+8>>2]|0;do{if((f|0)>2){r=f-1|0;if((c[(c[s+16>>2]|0)+(r<<2)>>2]|0)!=(m|0)){l=14819;break}if((c[(c[s+20>>2]|0)+(r<<2)>>2]|0)!=(e|0)){l=14819;break}if((a[(c[s+24>>2]|0)+r|0]&1)!=0){l=14819;break}c[59102]=(c[59102]|0)+1;E=s}else{l=14819}}while(0);if((l|0)==14819){if((f|0)==(c[s+4>>2]|0)){tI();n=c[59128]|0;F=n;G=c[n+8>>2]|0}else{F=s;G=f}c[(c[F+16>>2]|0)+(G<<2)>>2]=m;n=c[59128]|0;c[(c[n+20>>2]|0)+(c[n+8>>2]<<2)>>2]=e;n=c[59128]|0;a[(c[n+24>>2]|0)+(c[n+8>>2]|0)|0]=0;n=(c[59128]|0)+8|0;c[n>>2]=(c[n>>2]|0)+1;E=c[59128]|0}n=d-2|0;i=c[E+8>>2]|0;do{if((i|0)>2){r=i-1|0;if((c[(c[E+16>>2]|0)+(r<<2)>>2]|0)!=(m|0)){l=14827;break}if((c[(c[E+20>>2]|0)+(r<<2)>>2]|0)!=(n|0)){l=14827;break}if((a[(c[E+24>>2]|0)+r|0]&1)!=0){l=14827;break}c[59102]=(c[59102]|0)+1;H=E}else{l=14827}}while(0);if((l|0)==14827){if((i|0)==(c[E+4>>2]|0)){tI();e=c[59128]|0;I=e;J=c[e+8>>2]|0}else{I=E;J=i}c[(c[I+16>>2]|0)+(J<<2)>>2]=m;e=c[59128]|0;c[(c[e+20>>2]|0)+(c[e+8>>2]<<2)>>2]=n;e=c[59128]|0;a[(c[e+24>>2]|0)+(c[e+8>>2]|0)|0]=0;e=(c[59128]|0)+8|0;c[e>>2]=(c[e>>2]|0)+1;H=c[59128]|0}e=c[H+8>>2]|0;do{if((e|0)>2){f=e-1|0;if((c[(c[H+16>>2]|0)+(f<<2)>>2]|0)!=(g|0)){break}if((c[(c[H+20>>2]|0)+(f<<2)>>2]|0)!=(d|0)){break}if((a[(c[H+24>>2]|0)+f|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;break L19947}}while(0);if((e|0)==(c[H+4>>2]|0)){tI();n=c[59128]|0;K=n;L=c[n+8>>2]|0}else{K=H;L=e}c[(c[K+16>>2]|0)+(L<<2)>>2]=g;n=c[59128]|0;c[(c[n+20>>2]|0)+(c[n+8>>2]<<2)>>2]=d;n=c[59128]|0;a[(c[n+24>>2]|0)+(c[n+8>>2]|0)|0]=0;n=(c[59128]|0)+8|0;c[n>>2]=(c[n>>2]|0)+1}else if((k|0)==0){n=b-2|0;m=d-2|0;oA(n,m);i=d+2|0;f=c[59128]|0;s=c[f+8>>2]|0;do{if((s|0)>2){r=s-1|0;if((c[(c[f+16>>2]|0)+(r<<2)>>2]|0)!=(b|0)){l=14747;break}if((c[(c[f+20>>2]|0)+(r<<2)>>2]|0)!=(i|0)){l=14747;break}if((a[(c[f+24>>2]|0)+r|0]&1)!=0){l=14747;break}c[59102]=(c[59102]|0)+1;M=f}else{l=14747}}while(0);if((l|0)==14747){if((s|0)==(c[f+4>>2]|0)){tI();g=c[59128]|0;N=g;O=c[g+8>>2]|0}else{N=f;O=s}c[(c[N+16>>2]|0)+(O<<2)>>2]=b;g=c[59128]|0;c[(c[g+20>>2]|0)+(c[g+8>>2]<<2)>>2]=i;g=c[59128]|0;a[(c[g+24>>2]|0)+(c[g+8>>2]|0)|0]=0;g=(c[59128]|0)+8|0;c[g>>2]=(c[g>>2]|0)+1;M=c[59128]|0}g=b+2|0;e=c[M+8>>2]|0;do{if((e|0)>2){r=e-1|0;if((c[(c[M+16>>2]|0)+(r<<2)>>2]|0)!=(g|0)){l=14755;break}if((c[(c[M+20>>2]|0)+(r<<2)>>2]|0)!=(m|0)){l=14755;break}if((a[(c[M+24>>2]|0)+r|0]&1)!=0){l=14755;break}c[59102]=(c[59102]|0)+1;P=M}else{l=14755}}while(0);if((l|0)==14755){if((e|0)==(c[M+4>>2]|0)){tI();i=c[59128]|0;Q=i;R=c[i+8>>2]|0}else{Q=M;R=e}c[(c[Q+16>>2]|0)+(R<<2)>>2]=g;i=c[59128]|0;c[(c[i+20>>2]|0)+(c[i+8>>2]<<2)>>2]=m;i=c[59128]|0;a[(c[i+24>>2]|0)+(c[i+8>>2]|0)|0]=0;i=(c[59128]|0)+8|0;c[i>>2]=(c[i>>2]|0)+1;P=c[59128]|0}i=c[P+8>>2]|0;do{if((i|0)>2){s=i-1|0;if((c[(c[P+16>>2]|0)+(s<<2)>>2]|0)!=(n|0)){break}if((c[(c[P+20>>2]|0)+(s<<2)>>2]|0)!=(m|0)){break}if((a[(c[P+24>>2]|0)+s|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;break L19947}}while(0);if((i|0)==(c[P+4>>2]|0)){tI();g=c[59128]|0;S=g;T=c[g+8>>2]|0}else{S=P;T=i}c[(c[S+16>>2]|0)+(T<<2)>>2]=n;g=c[59128]|0;c[(c[g+20>>2]|0)+(c[g+8>>2]<<2)>>2]=m;g=c[59128]|0;a[(c[g+24>>2]|0)+(c[g+8>>2]|0)|0]=0;g=(c[59128]|0)+8|0;c[g>>2]=(c[g>>2]|0)+1}else if((k|0)==5){oA(b,d+2|0);g=d-2|0;e=c[59128]|0;s=c[e+8>>2]|0;do{if((s|0)>2){f=s-1|0;if((c[(c[e+16>>2]|0)+(f<<2)>>2]|0)!=(b|0)){l=14875;break}if((c[(c[e+20>>2]|0)+(f<<2)>>2]|0)!=(g|0)){l=14875;break}if((a[(c[e+24>>2]|0)+f|0]&1)!=0){l=14875;break}c[59102]=(c[59102]|0)+1}else{l=14875}}while(0);if((l|0)==14875){if((s|0)==(c[e+4>>2]|0)){tI();m=c[59128]|0;U=m;V=c[m+8>>2]|0}else{U=e;V=s}c[(c[U+16>>2]|0)+(V<<2)>>2]=b;m=c[59128]|0;c[(c[m+20>>2]|0)+(c[m+8>>2]<<2)>>2]=g;m=c[59128]|0;a[(c[m+24>>2]|0)+(c[m+8>>2]|0)|0]=0;m=(c[59128]|0)+8|0;c[m>>2]=(c[m>>2]|0)+1}oA(b-2|0,d);m=b+2|0;n=c[59128]|0;i=c[n+8>>2]|0;do{if((i|0)>2){f=i-1|0;if((c[(c[n+16>>2]|0)+(f<<2)>>2]|0)!=(m|0)){break}if((c[(c[n+20>>2]|0)+(f<<2)>>2]|0)!=(d|0)){break}if((a[(c[n+24>>2]|0)+f|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;break L19947}}while(0);if((i|0)==(c[n+4>>2]|0)){tI();g=c[59128]|0;W=g;X=c[g+8>>2]|0}else{W=n;X=i}c[(c[W+16>>2]|0)+(X<<2)>>2]=m;g=c[59128]|0;c[(c[g+20>>2]|0)+(c[g+8>>2]<<2)>>2]=d;g=c[59128]|0;a[(c[g+24>>2]|0)+(c[g+8>>2]|0)|0]=0;g=(c[59128]|0)+8|0;c[g>>2]=(c[g>>2]|0)+1}else if((k|0)==1){g=b-2|0;s=d+2|0;oA(g,s);e=d-2|0;f=c[59128]|0;r=c[f+8>>2]|0;do{if((r|0)>2){o=r-1|0;if((c[(c[f+16>>2]|0)+(o<<2)>>2]|0)!=(b|0)){l=14771;break}if((c[(c[f+20>>2]|0)+(o<<2)>>2]|0)!=(e|0)){l=14771;break}if((a[(c[f+24>>2]|0)+o|0]&1)!=0){l=14771;break}c[59102]=(c[59102]|0)+1;Y=f}else{l=14771}}while(0);if((l|0)==14771){if((r|0)==(c[f+4>>2]|0)){tI();m=c[59128]|0;Z=m;_=c[m+8>>2]|0}else{Z=f;_=r}c[(c[Z+16>>2]|0)+(_<<2)>>2]=b;m=c[59128]|0;c[(c[m+20>>2]|0)+(c[m+8>>2]<<2)>>2]=e;m=c[59128]|0;a[(c[m+24>>2]|0)+(c[m+8>>2]|0)|0]=0;m=(c[59128]|0)+8|0;c[m>>2]=(c[m>>2]|0)+1;Y=c[59128]|0}m=b+2|0;i=c[Y+8>>2]|0;do{if((i|0)>2){n=i-1|0;if((c[(c[Y+16>>2]|0)+(n<<2)>>2]|0)!=(m|0)){l=14779;break}if((c[(c[Y+20>>2]|0)+(n<<2)>>2]|0)!=(s|0)){l=14779;break}if((a[(c[Y+24>>2]|0)+n|0]&1)!=0){l=14779;break}c[59102]=(c[59102]|0)+1;$=Y}else{l=14779}}while(0);if((l|0)==14779){if((i|0)==(c[Y+4>>2]|0)){tI();e=c[59128]|0;aa=e;ab=c[e+8>>2]|0}else{aa=Y;ab=i}c[(c[aa+16>>2]|0)+(ab<<2)>>2]=m;e=c[59128]|0;c[(c[e+20>>2]|0)+(c[e+8>>2]<<2)>>2]=s;e=c[59128]|0;a[(c[e+24>>2]|0)+(c[e+8>>2]|0)|0]=0;e=(c[59128]|0)+8|0;c[e>>2]=(c[e>>2]|0)+1;$=c[59128]|0}e=c[$+8>>2]|0;do{if((e|0)>2){r=e-1|0;if((c[(c[$+16>>2]|0)+(r<<2)>>2]|0)!=(g|0)){break}if((c[(c[$+20>>2]|0)+(r<<2)>>2]|0)!=(s|0)){break}if((a[(c[$+24>>2]|0)+r|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;break L19947}}while(0);if((e|0)==(c[$+4>>2]|0)){tI();m=c[59128]|0;ac=m;ad=c[m+8>>2]|0}else{ac=$;ad=e}c[(c[ac+16>>2]|0)+(ad<<2)>>2]=g;m=c[59128]|0;c[(c[m+20>>2]|0)+(c[m+8>>2]<<2)>>2]=s;m=c[59128]|0;a[(c[m+24>>2]|0)+(c[m+8>>2]|0)|0]=0;m=(c[59128]|0)+8|0;c[m>>2]=(c[m>>2]|0)+1}else if((k|0)==4){m=b-2|0;i=d-2|0;oA(m,i);r=d+2|0;f=c[59128]|0;n=c[f+8>>2]|0;do{if((n|0)>2){o=n-1|0;if((c[(c[f+16>>2]|0)+(o<<2)>>2]|0)!=(m|0)){l=14843;break}if((c[(c[f+20>>2]|0)+(o<<2)>>2]|0)!=(r|0)){l=14843;break}if((a[(c[f+24>>2]|0)+o|0]&1)!=0){l=14843;break}c[59102]=(c[59102]|0)+1;ae=f}else{l=14843}}while(0);if((l|0)==14843){if((n|0)==(c[f+4>>2]|0)){tI();s=c[59128]|0;af=s;ag=c[s+8>>2]|0}else{af=f;ag=n}c[(c[af+16>>2]|0)+(ag<<2)>>2]=m;s=c[59128]|0;c[(c[s+20>>2]|0)+(c[s+8>>2]<<2)>>2]=r;s=c[59128]|0;a[(c[s+24>>2]|0)+(c[s+8>>2]|0)|0]=0;s=(c[59128]|0)+8|0;c[s>>2]=(c[s>>2]|0)+1;ae=c[59128]|0}s=b+2|0;g=c[ae+8>>2]|0;do{if((g|0)>2){e=g-1|0;if((c[(c[ae+16>>2]|0)+(e<<2)>>2]|0)!=(s|0)){l=14851;break}if((c[(c[ae+20>>2]|0)+(e<<2)>>2]|0)!=(r|0)){l=14851;break}if((a[(c[ae+24>>2]|0)+e|0]&1)!=0){l=14851;break}c[59102]=(c[59102]|0)+1;ah=ae}else{l=14851}}while(0);if((l|0)==14851){if((g|0)==(c[ae+4>>2]|0)){tI();n=c[59128]|0;ai=n;aj=c[n+8>>2]|0}else{ai=ae;aj=g}c[(c[ai+16>>2]|0)+(aj<<2)>>2]=s;n=c[59128]|0;c[(c[n+20>>2]|0)+(c[n+8>>2]<<2)>>2]=r;n=c[59128]|0;a[(c[n+24>>2]|0)+(c[n+8>>2]|0)|0]=0;n=(c[59128]|0)+8|0;c[n>>2]=(c[n>>2]|0)+1;ah=c[59128]|0}n=c[ah+8>>2]|0;do{if((n|0)>2){f=n-1|0;if((c[(c[ah+16>>2]|0)+(f<<2)>>2]|0)!=(s|0)){l=14859;break}if((c[(c[ah+20>>2]|0)+(f<<2)>>2]|0)!=(i|0)){l=14859;break}if((a[(c[ah+24>>2]|0)+f|0]&1)!=0){l=14859;break}c[59102]=(c[59102]|0)+1;ak=ah}else{l=14859}}while(0);if((l|0)==14859){if((n|0)==(c[ah+4>>2]|0)){tI();r=c[59128]|0;al=r;am=c[r+8>>2]|0}else{al=ah;am=n}c[(c[al+16>>2]|0)+(am<<2)>>2]=s;r=c[59128]|0;c[(c[r+20>>2]|0)+(c[r+8>>2]<<2)>>2]=i;r=c[59128]|0;a[(c[r+24>>2]|0)+(c[r+8>>2]|0)|0]=0;r=(c[59128]|0)+8|0;c[r>>2]=(c[r>>2]|0)+1;ak=c[59128]|0}r=c[ak+8>>2]|0;do{if((r|0)>2){g=r-1|0;if((c[(c[ak+16>>2]|0)+(g<<2)>>2]|0)!=(m|0)){break}if((c[(c[ak+20>>2]|0)+(g<<2)>>2]|0)!=(i|0)){break}if((a[(c[ak+24>>2]|0)+g|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;break L19947}}while(0);if((r|0)==(c[ak+4>>2]|0)){tI();s=c[59128]|0;an=s;ao=c[s+8>>2]|0}else{an=ak;ao=r}c[(c[an+16>>2]|0)+(ao<<2)>>2]=m;s=c[59128]|0;c[(c[s+20>>2]|0)+(c[s+8>>2]<<2)>>2]=i;s=c[59128]|0;a[(c[s+24>>2]|0)+(c[s+8>>2]|0)|0]=0;s=(c[59128]|0)+8|0;c[s>>2]=(c[s>>2]|0)+1}else{s=d-2|0;oA(b,s);n=b-2|0;g=c[59128]|0;f=c[g+8>>2]|0;do{if((f|0)>2){e=f-1|0;if((c[(c[g+16>>2]|0)+(e<<2)>>2]|0)!=(n|0)){l=14907;break}if((c[(c[g+20>>2]|0)+(e<<2)>>2]|0)!=(d|0)){l=14907;break}if((a[(c[g+24>>2]|0)+e|0]&1)!=0){l=14907;break}c[59102]=(c[59102]|0)+1;ap=g}else{l=14907}}while(0);if((l|0)==14907){if((f|0)==(c[g+4>>2]|0)){tI();i=c[59128]|0;aq=i;ar=c[i+8>>2]|0}else{aq=g;ar=f}c[(c[aq+16>>2]|0)+(ar<<2)>>2]=n;i=c[59128]|0;c[(c[i+20>>2]|0)+(c[i+8>>2]<<2)>>2]=d;i=c[59128]|0;a[(c[i+24>>2]|0)+(c[i+8>>2]|0)|0]=0;i=(c[59128]|0)+8|0;c[i>>2]=(c[i>>2]|0)+1;ap=c[59128]|0}i=d+2|0;m=c[ap+8>>2]|0;do{if((m|0)>2){r=m-1|0;if((c[(c[ap+16>>2]|0)+(r<<2)>>2]|0)!=(b|0)){l=14915;break}if((c[(c[ap+20>>2]|0)+(r<<2)>>2]|0)!=(i|0)){l=14915;break}if((a[(c[ap+24>>2]|0)+r|0]&1)!=0){l=14915;break}c[59102]=(c[59102]|0)+1;as=ap}else{l=14915}}while(0);if((l|0)==14915){if((m|0)==(c[ap+4>>2]|0)){tI();n=c[59128]|0;at=n;au=c[n+8>>2]|0}else{at=ap;au=m}c[(c[at+16>>2]|0)+(au<<2)>>2]=b;n=c[59128]|0;c[(c[n+20>>2]|0)+(c[n+8>>2]<<2)>>2]=i;n=c[59128]|0;a[(c[n+24>>2]|0)+(c[n+8>>2]|0)|0]=0;n=(c[59128]|0)+8|0;c[n>>2]=(c[n>>2]|0)+1;as=c[59128]|0}n=b+2|0;f=c[as+8>>2]|0;do{if((f|0)>2){g=f-1|0;if((c[(c[as+16>>2]|0)+(g<<2)>>2]|0)!=(n|0)){l=14923;break}if((c[(c[as+20>>2]|0)+(g<<2)>>2]|0)!=(d|0)){l=14923;break}if((a[(c[as+24>>2]|0)+g|0]&1)!=0){l=14923;break}c[59102]=(c[59102]|0)+1;av=as}else{l=14923}}while(0);if((l|0)==14923){if((f|0)==(c[as+4>>2]|0)){tI();i=c[59128]|0;aw=i;ax=c[i+8>>2]|0}else{aw=as;ax=f}c[(c[aw+16>>2]|0)+(ax<<2)>>2]=n;i=c[59128]|0;c[(c[i+20>>2]|0)+(c[i+8>>2]<<2)>>2]=d;i=c[59128]|0;a[(c[i+24>>2]|0)+(c[i+8>>2]|0)|0]=0;i=(c[59128]|0)+8|0;c[i>>2]=(c[i>>2]|0)+1;av=c[59128]|0}i=c[av+8>>2]|0;do{if((i|0)>2){m=i-1|0;if((c[(c[av+16>>2]|0)+(m<<2)>>2]|0)!=(b|0)){break}if((c[(c[av+20>>2]|0)+(m<<2)>>2]|0)!=(s|0)){break}if((a[(c[av+24>>2]|0)+m|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;break L19947}}while(0);if((i|0)==(c[av+4>>2]|0)){tI();n=c[59128]|0;ay=n;az=c[n+8>>2]|0}else{ay=av;az=i}c[(c[ay+16>>2]|0)+(az<<2)>>2]=b;n=c[59128]|0;c[(c[n+20>>2]|0)+(c[n+8>>2]<<2)>>2]=s;n=c[59128]|0;a[(c[n+24>>2]|0)+(c[n+8>>2]|0)|0]=0;n=(c[59128]|0)+8|0;c[n>>2]=(c[n>>2]|0)+1}}while(0);if(!h){return}h=(j|0)<-2?-2:j;if((h|0)>8){aA=(h|0)%9&-1}else{aA=h}h=c[236448+(aA+2<<2)>>2]|0;if((h|0)==(c[59128]|0)){c[59106]=(c[59106]|0)+1;return}else{c[59128]=h;return}}function oF(){return}function oG(){return}function oH(){return}function oI(){return}function oJ(){aI(174776,14,1,c[10030]|0);return}function oK(){aI(174800,6,1,c[10030]|0);return}function oL(a,b){a=a|0;b=b|0;var d=0;d=i;cf(c[10030]|0,174824,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);i=d;return}function oM(a,b){a=a|0;b=b|0;var d=0;d=i;cf(c[10030]|0,174848,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);i=d;return}function oN(a){a=a|0;var b=0;b=i;cf(c[10030]|0,174880,(v=i,i=i+8|0,c[v>>2]=c[236584+(((a+2|0)%11&-1)<<2)>>2],v)|0);i=b;return}function oO(a,b,d){a=a|0;b=b|0;d=d|0;var e=0;e=i;cf(c[10030]|0,174824,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b-5,v)|0);cf(c[10030]|0,174976,(v=i,i=i+8|0,c[v>>2]=d,v)|0);i=e;return}function oP(a){a=a|0;var b=0;b=i;cf(c[10030]|0,175008,(v=i,i=i+8|0,c[v>>2]=(a|0)!=0?2:1,v)|0);i=b;return 1}function oQ(){var b=0;b=c[10030]|0;if(a[235320]|0){aI(175320|0,13,1,b|0);c[58826]=1;return}else{aI(175360|0,10,1,b|0);c[58826]=1;return}}function oR(){var a=0,b=0,d=0;a=i;aI(175544,29,1,c[10030]|0);b=c[11252]|0;if((b|0)==6){d=175480}else{d=(b|0)==1?175480:179864}cf(c[10030]|0,175512,(v=i,i=i+40|0,c[v>>2]=d,c[v+8>>2]=1e4,c[v+16>>2]=7500,h[v+24>>3]=.2,h[v+32>>3]=.4,v)|0);c[58832]=0;i=a;return}function oS(a,b){a=a|0;b=b|0;var d=0;d=i;if((c[58822]|0)==(a|0)&(c[58820]|0)==(b|0)){i=d;return}cf(c[10030]|0,175584,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);c[58826]=1;c[58822]=a;c[58820]=b;i=d;return}function oT(a,b){a=a|0;b=b|0;var d=0,e=0;d=i;e=c[10030]|0;if((c[58826]|0)==0){cf(e|0,175600,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);c[58822]=a;c[58820]=b;i=d;return}else{cf(e|0,175616,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);c[58826]=0;c[58822]=a;c[58820]=b;i=d;return}}function oU(a){a=a|0;var b=0,d=0;b=i;d=((((a|0)<-2?0:a+2|0)|0)%(c[58828]|0)&-1)+1|0;if((c[58824]|0)==(d|0)){i=b;return}cf(c[10030]|0,175648,(v=i,i=i+8|0,c[v>>2]=d,v)|0);c[58824]=d;c[58826]=1;i=b;return}function oV(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;do{if((c[58832]|0)==1){g=b+15|0;if((c[58822]|0)==(g|0)&(c[58820]|0)==(d|0)){break}h=c[10030]|0;cf(h|0,175584,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=d,v)|0);c[58826]=1;c[58822]=g;c[58820]=d}else{g=d-15|0;if((c[58822]|0)==(b|0)&(c[58820]|0)==(g|0)){break}h=c[10030]|0;cf(h|0,175584,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=g,v)|0);c[58826]=1;c[58822]=b;c[58820]=g}}while(0);b=c[11252]|0;if((b|0)==6){d=c[10030]|0;aI(175688,2,1,d|0);d=a[e]|0;if(d<<24>>24!=0){g=e;h=d;do{d=h&255;do{if(h<<24>>24<0){j=38152+(d-128<<2)|0;if((a[j]|0)==0){k=14990;break}l=c[10030]|0;aK(j|0,l|0)}else{k=14990}}while(0);if((k|0)==14990){k=0;l=c[10030]|0;aF(d|0,l|0)}g=g+1|0;h=a[g]|0;}while(h<<24>>24!=0)}h=c[10030]|0;aI(175664,2,1,h|0);i=f;return}h=c[10030]|0;if((b|0)!=1){cf(h|0,177872,(v=i,i=i+8|0,c[v>>2]=e,v)|0);i=f;return}aI(175688,2,1,h|0);h=a[e]|0;if(h<<24>>24!=0){b=e;e=h;do{h=e&255;do{if(e<<24>>24<0){g=37640+(h-128<<2)|0;if((a[g]|0)==0){k=14998;break}l=c[10030]|0;aK(g|0,l|0)}else{k=14998}}while(0);if((k|0)==14998){k=0;d=c[10030]|0;aF(h|0,d|0)}b=b+1|0;e=a[b]|0;}while(e<<24>>24!=0)}aI(175664,2,1,c[10030]|0);i=f;return}function oW(a){a=a|0;var b=0;if((a|0)==(-90|0)|(a|0)==270){c[58832]=-1;b=c[10030]|0;aI(175768,8,1,b|0);return 1}b=(a|0)!=0;c[58832]=b&1;a=c[10030]|0;if(b){aI(175720,7,1,a|0);return 1}else{aI(175816,7,1,a|0);return 1}return 0}function oX(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0;b=i;c[58828]=6;a[235320]=0;d=c[13898]|0;e=c[8272]|0;L20300:do{if((d|0)<(e|0)){f=d;g=6;j=e;L20301:while(1){k=c[1054]|0;l=(a[k+(f*40&-1)|0]&1)==0;L20303:do{if(l){m=15032}else{n=c[k+(f*40&-1)+36>>2]|0;o=k+(f*40&-1)+32|0;p=c[10036]|0;q=0;while(1){if((q|0)>=(n|0)){m=15020;break}if((a[p+((c[o>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break}}if((m|0)==15020){m=0;if((q|0)==1){r=g;break L20300}}if(l){m=15032;break}if((n|0)<=0){m=15044;break L20301}p=c[10036]|0;s=0;t=0;u=c[o>>2]|0;while(1){if((a[s+175248|0]|0)==(a[p+(s+u|0)|0]|0)){w=u;x=t}else{if((s|0)!=3){break}w=u-1|0;x=1}y=s+1|0;if((y|0)<(x+n|0)){s=y;t=x;u=w}else{m=15028;break}}do{if((m|0)==15028){m=0;if((x|0)==0){if(!((s|0)==2|(s|0)==5)){break}}a[235320]=1;z=g;A=f;B=j;break L20303}}while(0);if(l){m=15032}else{m=15043;break L20301}}}while(0);if((m|0)==15032){m=0;l=c[k+(f*40&-1)+8>>2]|0;if((l|0)==1){C=+(c[k+(f*40&-1)+16>>2]|0)}else if((l|0)==3){C=+uz(c[k+(f*40&-1)+16>>2]|0,0)}else if((l|0)==2){C=+h[k+(f*40&-1)+16>>3]}else{m=15036;break}l=~~C;c[58828]=l;if((l|0)<1){m=15039;break}z=l;A=c[13898]|0;B=c[8272]|0}l=A+1|0;c[13898]=l;if((l|0)<(B|0)){f=l;g=z;j=B}else{r=z;break L20300}}if((m|0)==15036){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((m|0)==15043){uf(f,175176,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((m|0)==15039){c[58828]=6;uf(c[13898]|0,175216,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((m|0)==15044){uf(f,175176,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{r=6}}while(0);m=a[235320]|0?175144:175024;be(13048,175160,(v=i,i=i+16|0,c[v>>2]=r,c[v+8>>2]=m,v)|0);i=b;return}function oY(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0.0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0.0,at=0.0,au=0.0,av=0,aw=0,ax=0,ay=0,az=0;b=i;d=c[13898]|0;e=c[8272]|0;L20341:do{if((d|0)<(e|0)){f=c[1054]|0;g=(a[f+(d*40&-1)|0]&1)==0;if(g){j=d;break}k=c[f+(d*40&-1)+36>>2]|0;l=f+(d*40&-1)+32|0;m=c[10036]|0;n=0;while(1){if((n|0)>=(k|0)){o=15050;break}if((a[m+((c[l>>2]|0)+n|0)|0]|0)==(a[n+103664|0]|0)){n=n+1|0}else{break}}if((o|0)==15050){if((n|0)==1){j=d;break}}if(!((k|0)>0&(g^1))){j=d;break}m=c[10036]|0;p=0;q=0;r=c[l>>2]|0;while(1){if((a[p+176888|0]|0)==(a[m+(p+r|0)|0]|0)){s=r;t=q}else{if((p|0)!=1){j=d;break L20341}s=r-1|0;t=1}u=p+1|0;if((u|0)<(t+k|0)){p=u;q=t;r=s}else{break}}if((t|0)==0){if(!((p|0)==0|(p|0)==4)){j=d;break}}r=d+1|0;c[13898]=r;if((r|0)>=(e|0)){uf(r,176848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}q=(a[f+(r*40&-1)|0]&1)==0;k=c[f+(r*40&-1)+36>>2]|0;L20364:do{if(q){w=c[10036]|0;x=f+(r*40&-1)+32|0}else{m=f+(r*40&-1)+32|0;l=c[10036]|0;g=0;while(1){if((g|0)>=(k|0)){break}if((a[l+((c[m>>2]|0)+g|0)|0]|0)==(a[g+103664|0]|0)){g=g+1|0}else{w=l;x=m;break L20364}}if((g|0)!=1){w=l;x=m;break}uf(r,176848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);f=(k|0)>0;p=c[58058]|0;L20373:do{if((p|0)==0){o=15075}else{if(q){o=15075;break}if(f){n=0;u=0;y=c[x>>2]|0;while(1){z=a[p+n|0]|0;if(z<<24>>24==(a[w+(n+y|0)|0]|0)){A=y;B=u}else{if(z<<24>>24!=36){o=15075;break L20373}A=y-1|0;B=1}C=n+1|0;if((C|0)<(B+k|0)){n=C;u=B;y=A}else{break}}if((B|0)==0){D=C}else{E=0;break}}else{D=0}y=a[p+D|0]|0;if((y<<24>>24|0)==36|(y<<24>>24|0)==0){E=0}else{o=15075}}}while(0);L20385:do{if((o|0)==15075){p=c[58063]|0;if((p|0)==0){uf(r,176848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(q){uf(r,176848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}do{if(f){y=0;u=0;n=c[x>>2]|0;while(1){m=a[p+y|0]|0;if(m<<24>>24==(a[w+(y+n|0)|0]|0)){F=n;G=u}else{if(m<<24>>24!=36){o=15225;break}F=n-1|0;G=1}H=y+1|0;if((H|0)<(G+k|0)){y=H;u=G;n=F}else{o=15223;break}}if((o|0)==15225){uf(r,176848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15223){if((G|0)==0){I=H;break}else{E=1;break L20385}}}else{I=0}}while(0);n=a[p+I|0]|0;if((n<<24>>24|0)==36|(n<<24>>24|0)==0){E=1;break}uf(r,176848,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[58068]=232232+(E*20&-1);r=d+2|0;c[13898]=r;j=r}else{j=d}}while(0);d=(j|0)<(e|0);L20407:do{if(d){e=c[1054]|0;E=(a[e+(j*40&-1)|0]&1)==0;L20409:do{if(!E){I=c[e+(j*40&-1)+36>>2]|0;H=e+(j*40&-1)+32|0;G=c[10036]|0;F=0;while(1){if((F|0)>=(I|0)){o=15084;break}if((a[G+((c[H>>2]|0)+F|0)|0]|0)==(a[F+103664|0]|0)){F=F+1|0}else{break}}if((o|0)==15084){if((F|0)==1){break}}if(!((I|0)>0&(E^1))){break}G=c[10036]|0;p=0;w=0;x=c[H>>2]|0;while(1){if((a[p+176824|0]|0)==(a[G+(p+x|0)|0]|0)){J=x;K=w}else{if((p|0)!=3){break L20409}J=x-1|0;K=1}D=p+1|0;if((D|0)<(K+I|0)){p=D;w=K;x=J}else{break}}if((K|0)==0){if(!((p|0)==2|(p|0)==8)){break}}c[(c[58068]|0)+12>>2]=52e3;c[(c[58068]|0)+16>>2]=33940;c[58070]=176808;c[13898]=(c[13898]|0)+1;break L20407}}while(0);if(!d){break}E=c[1054]|0;e=(a[E+(j*40&-1)|0]&1)==0;L20430:do{if(!e){x=c[E+(j*40&-1)+36>>2]|0;w=E+(j*40&-1)+32|0;I=c[10036]|0;G=0;while(1){if((G|0)>=(x|0)){o=15099;break}if((a[I+((c[w>>2]|0)+G|0)|0]|0)==(a[G+103664|0]|0)){G=G+1|0}else{break}}if((o|0)==15099){if((G|0)==1){break}}if(!((x|0)>0&(e^1))){break}I=c[10036]|0;p=0;H=0;F=c[w>>2]|0;while(1){if((a[p+176760|0]|0)==(a[I+(p+F|0)|0]|0)){L=F;M=H}else{if((p|0)!=5){break L20430}L=F-1|0;M=1}D=p+1|0;if((D|0)<(M+x|0)){p=D;H=M;F=L}else{break}}if((M|0)==0){if(!((p|0)==4|(p|0)==10)){break}}c[(c[58068]|0)+12>>2]=45333;c[(c[58068]|0)+16>>2]=33940;c[58070]=176704;c[13898]=(c[13898]|0)+1;break L20407}}while(0);if(!d){break}e=c[1054]|0;E=(a[e+(j*40&-1)|0]&1)==0;L20451:do{if(!E){F=c[e+(j*40&-1)+36>>2]|0;H=e+(j*40&-1)+32|0;x=c[10036]|0;I=0;while(1){if((I|0)>=(F|0)){o=15114;break}if((a[x+((c[H>>2]|0)+I|0)|0]|0)==(a[I+103664|0]|0)){I=I+1|0}else{break}}if((o|0)==15114){if((I|0)==1){break}}if(!((F|0)>0&(E^1))){break}x=c[10036]|0;p=0;w=0;G=c[H>>2]|0;while(1){if((a[p+176680|0]|0)==(a[x+(p+G|0)|0]|0)){N=G;O=w}else{if((p|0)!=3){break L20451}N=G-1|0;O=1}D=p+1|0;if((D|0)<(O+F|0)){p=D;w=O;G=N}else{break}}if((O|0)==0){if(!((p|0)==2|(p|0)==5)){break}}c[(c[58068]|0)+12>>2]=13e3;c[(c[58068]|0)+16>>2]=7440;c[58070]=176664;c[13898]=(c[13898]|0)+1;break L20407}}while(0);if(!d){break}E=c[1054]|0;e=(a[E+(j*40&-1)|0]&1)==0;if(e){break}G=c[E+(j*40&-1)+36>>2]|0;w=E+(j*40&-1)+32|0;E=c[10036]|0;F=0;while(1){if((F|0)>=(G|0)){o=15129;break}if((a[E+((c[w>>2]|0)+F|0)|0]|0)==(a[F+103664|0]|0)){F=F+1|0}else{break}}if((o|0)==15129){if((F|0)==1){break}}if(!((G|0)>0&(e^1))){break}E=c[10036]|0;x=0;H=0;I=c[w>>2]|0;while(1){if((a[x+176656|0]|0)==(a[E+(x+I|0)|0]|0)){P=I;Q=H}else{if((x|0)!=3){break L20407}P=I-1|0;Q=1}D=x+1|0;if((D|0)<(Q+G|0)){x=D;H=Q;I=P}else{break}}if((Q|0)==0){if(!((x|0)==2|(x|0)==6)){break}}c[(c[58068]|0)+12>>2]=1e4;c[(c[58068]|0)+16>>2]=7440;c[58070]=176648;c[13898]=(c[13898]|0)+1}}while(0);Q=c[58070]|0;be(13048,84616,(v=i,i=i+16|0,c[v>>2]=c[(c[58068]|0)+4>>2],c[v+8>>2]=Q,v)|0);Q=c[3524]|0;P=c[13898]|0;j=c[8272]|0;if((P|0)>=(j|0)){R=uA(13048)|0;S=R+13048|0;T=a[236376]|0;U=T?116408:116440;V=c[58868]|0;W=T?1:V;X=a[235336]|0;Y=X?131584:131504;Z=c[59090]|0;_=Z+4|0;$=c[_>>2]|0;aa=Z+12|0;ab=c[aa>>2]|0;ac=(ab|0)!=0;ad=ac?176184:176112;ae=Z+24|0;af=Z+16|0;ag=ac?ae:af;ah=+h[ag>>3];ai=a[28152]|0;aj=ai?176072:176024;ak=be(S|0,176232,(v=i,i=i+56|0,c[v>>2]=U,c[v+8>>2]=W,c[v+16>>2]=Y,c[v+24>>2]=$,c[v+32>>2]=ad,h[v+40>>3]=ah,c[v+48>>2]=aj,v)|0)|0;i=b;return}d=Q+16|0;O=Q+20|0;Q=P;P=j;L20494:while(1){j=c[1054]|0;N=(a[j+(Q*40&-1)|0]&1)==0;L20496:do{if(!N){M=c[j+(Q*40&-1)+36>>2]|0;L=j+(Q*40&-1)+32|0;K=c[10036]|0;J=0;while(1){if((J|0)>=(M|0)){break}if((a[K+((c[L>>2]|0)+J|0)|0]|0)==(a[J+103664|0]|0)){J=J+1|0}else{break L20496}}if((J|0)==1){o=15233;break L20494}}}while(0);x=c[58848]|0;L20503:do{if((x|0)==0){al=235392}else{L=c[j+(Q*40&-1)+36>>2]|0;K=c[j+(Q*40&-1)+32>>2]|0;M=(L|0)>0;p=c[10036]|0;I=235392;H=x;while(1){L20507:do{if(!N){if(M){G=0;E=0;w=K;while(1){e=a[H+G|0]|0;if(e<<24>>24==(a[p+(G+w|0)|0]|0)){am=w;an=E}else{if(e<<24>>24!=36){break L20507}am=w-1|0;an=1}ao=G+1|0;if((ao|0)<(an+L|0)){G=ao;E=an;w=am}else{break}}if((an|0)==0){ap=ao}else{al=I;break L20503}}else{ap=0}w=a[H+ap|0]|0;if((w<<24>>24|0)==36|(w<<24>>24|0)==0){al=I;break L20503}}}while(0);w=I+8|0;E=c[w>>2]|0;if((E|0)==0){al=w;break}else{I=w;H=E}}}}while(0);N=c[al+4>>2]|0;L20520:do{if((N|0)==2){a[235336]=0;x=Q+1|0;c[13898]=x;aq=x}else if((N|0)==5){ar=Q+1|0;c[13898]=ar;if((ar|0)>=(P|0)){o=15237;break L20494}L20524:do{if((a[j+(ar*40&-1)|0]&1)!=0){x=c[j+(ar*40&-1)+36>>2]|0;H=j+(ar*40&-1)+32|0;I=c[10036]|0;L=0;while(1){if((L|0)>=(x|0)){break}if((a[I+((c[H>>2]|0)+L|0)|0]|0)==(a[L+103664|0]|0)){L=L+1|0}else{break L20524}}if((L|0)==1){o=15236;break L20494}}}while(0);H=c[j+(ar*40&-1)+8>>2]|0;if((H|0)==2){as=+h[j+(ar*40&-1)+16>>3]}else if((H|0)==3){as=+uz(c[j+(ar*40&-1)+16>>2]|0,0)}else if((H|0)==1){as=+(c[j+(ar*40&-1)+16>>2]|0)}else{o=15205;break L20494}h[29422]=as;if(as<=0.0){o=15210;break L20494}h[29421]=as;H=~~(as*1016.0/72.0);c[d>>2]=H;c[O>>2]=(H<<1>>>0)/3>>>0;H=c[59090]|0;at=+h[29422];if((c[H+12>>2]|0)==0){h[H+16>>3]=216.0/(at*2.0)}else{h[H+24>>3]=at}H=(c[13898]|0)+1|0;c[13898]=H;aq=H}else if((N|0)==0){a[236376]=1;H=Q+1|0;c[13898]=H;aq=H}else if((N|0)==1){a[236376]=0;H=Q+1|0;c[13898]=H;if((H|0)>=(P|0)){aq=H;break}if((a[j+(H*40&-1)|0]&1)!=0){I=c[j+(H*40&-1)+36>>2]|0;x=j+(H*40&-1)+32|0;p=c[10036]|0;K=0;while(1){if((K|0)>=(I|0)){aq=H;break L20520}if((a[p+((c[x>>2]|0)+K|0)|0]|0)==(a[K+103664|0]|0)){K=K+1|0}else{aq=H;break L20520}}}K=c[j+(H*40&-1)+8>>2]|0;if((K|0)==2){au=+h[j+(H*40&-1)+16>>3]}else if((K|0)==1){au=+(c[j+(H*40&-1)+16>>2]|0)}else if((K|0)==3){au=+uz(c[j+(H*40&-1)+16>>2]|0,0)}else{o=15169;break L20494}K=~~au;c[58868]=K;if((K|0)<1){o=15171;break L20494}K=(c[13898]|0)+1|0;c[13898]=K;aq=K}else if((N|0)==7){K=Q+1|0;c[13898]=K;a[28152]=1;aq=K}else if((N|0)==8){K=Q+1|0;c[13898]=K;aq=K}else if((N|0)==3){a[235336]=1;K=Q+1|0;c[13898]=K;aq=K}else if((N|0)==4){av=Q+1|0;c[13898]=av;if((av|0)>=(P|0)){o=15232;break L20494}K=(a[j+(av*40&-1)|0]&1)==0;x=c[j+(av*40&-1)+36>>2]|0;p=j+(av*40&-1)+32|0;L20561:do{if(!K){I=c[10036]|0;M=0;while(1){if((M|0)>=(x|0)){break}if((a[I+((c[p>>2]|0)+M|0)|0]|0)==(a[M+103664|0]|0)){M=M+1|0}else{break L20561}}if((M|0)==1){o=15231;break L20494}}}while(0);H=(x|0)>0;I=c[10036]|0;L=0;L20568:while(1){J=c[235528+(L*48&-1)>>2]|0;L20570:do{if(!((J|0)==0|K)){if(H){E=0;w=0;G=c[p>>2]|0;while(1){e=a[J+E|0]|0;if(e<<24>>24==(a[I+(E+G|0)|0]|0)){aw=G;ax=w}else{if(e<<24>>24!=36){break L20570}aw=G-1|0;ax=1}ay=E+1|0;if((ay|0)<(ax+x|0)){E=ay;w=ax;G=aw}else{break}}if((ax|0)==0){az=ay}else{break L20568}}else{az=0}G=a[J+az|0]|0;if((G<<24>>24|0)==36|(G<<24>>24|0)==0){break L20568}}}while(0);J=L+1|0;if(J>>>0<17){L=J}else{o=15193;break L20494}}c[59090]=235528+(L*48&-1);c[59088]=L;c[59086]=L;x=Q+2|0;c[13898]=x;aq=x}else if((N|0)==6){x=Q+1|0;c[13898]=x;a[28152]=0;aq=x}else{aq=Q}}while(0);N=c[8272]|0;if((aq|0)<(N|0)){Q=aq;P=N}else{o=15235;break}}if((o|0)==15231){uf(av,176384,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15232){uf(av,176384,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15233){R=uA(13048)|0;S=R+13048|0;T=a[236376]|0;U=T?116408:116440;V=c[58868]|0;W=T?1:V;X=a[235336]|0;Y=X?131584:131504;Z=c[59090]|0;_=Z+4|0;$=c[_>>2]|0;aa=Z+12|0;ab=c[aa>>2]|0;ac=(ab|0)!=0;ad=ac?176184:176112;ae=Z+24|0;af=Z+16|0;ag=ac?ae:af;ah=+h[ag>>3];ai=a[28152]|0;aj=ai?176072:176024;ak=be(S|0,176232,(v=i,i=i+56|0,c[v>>2]=U,c[v+8>>2]=W,c[v+16>>2]=Y,c[v+24>>2]=$,c[v+32>>2]=ad,h[v+40>>3]=ah,c[v+48>>2]=aj,v)|0)|0;i=b;return}else if((o|0)==15193){uf(av,176384,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15235){R=uA(13048)|0;S=R+13048|0;T=a[236376]|0;U=T?116408:116440;V=c[58868]|0;W=T?1:V;X=a[235336]|0;Y=X?131584:131504;Z=c[59090]|0;_=Z+4|0;$=c[_>>2]|0;aa=Z+12|0;ab=c[aa>>2]|0;ac=(ab|0)!=0;ad=ac?176184:176112;ae=Z+24|0;af=Z+16|0;ag=ac?ae:af;ah=+h[ag>>3];ai=a[28152]|0;aj=ai?176072:176024;ak=be(S|0,176232,(v=i,i=i+56|0,c[v>>2]=U,c[v+8>>2]=W,c[v+16>>2]=Y,c[v+24>>2]=$,c[v+32>>2]=ad,h[v+40>>3]=ah,c[v+48>>2]=aj,v)|0)|0;i=b;return}else if((o|0)==15205){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15171){c[58868]=6;uf(c[13898]|0,176608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15210){uf(c[13898]|0,176336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15169){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15236){uf(ar,176336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((o|0)==15237){uf(ar,176336,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function oZ(){var a=0,b=0;a=i;b=c[3524]|0;cf(c[10030]|0,176968,(v=i,i=i+8|0,c[v>>2]=c[(c[58068]|0)+8>>2],v)|0);c[b+8>>2]=c[(c[58068]|0)+12>>2];c[b+12>>2]=c[(c[58068]|0)+16>>2];aI(177432,4,1,c[10030]|0);i=a;return}function o_(){aI(177008,7,1,c[10030]|0);return}function o$(){var b=0;if(a[235520]|0){b=c[10030]|0;aI(125976,2,1,b|0);a[235520]=0}aI(177024,10,1,c[10030]|0);return}function o0(){var b=0,d=0,e=0,f=0.0,g=0;b=i;aI(177432,4,1,c[10030]|0);d=c[59090]|0;e=c[d+12>>2]|0;cf(c[10030]|0,177408,(v=i,i=i+16|0,c[v>>2]=c[d+8>>2],c[v+8>>2]=e,v)|0);e=c[59090]|0;d=c[10030]|0;if((c[e+12>>2]|0)==0){f=+h[e+16>>3];cf(d|0,180656,(v=i,i=i+8|0,h[v>>3]=f,v)|0)}else{f=+h[e+24>>3];cf(d|0,180672,(v=i,i=i+8|0,h[v>>3]=f,v)|0)}d=c[59090]|0;e=c[d+36>>2]|0;g=c[d+40>>2]|0;cf(c[10030]|0,180632,(v=i,i=i+24|0,c[v>>2]=c[d+32>>2],c[v+8>>2]=e,c[v+16>>2]=g,v)|0);if(!(a[235336]|0)){c[58832]=0;a[235520]=0;a[235512]=1;c[58826]=1;i=b;return}aI(177112,255,1,c[10030]|0);c[58832]=0;a[235520]=0;a[235512]=1;c[58826]=1;i=b;return}function o1(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;if(a[235520]|0){e=b-(c[58822]|0)|0;f=d-(c[58820]|0)|0;g=c[10030]|0;aF(60,g|0);h=f;i=e}else{e=c[10030]|0;aI(177464,3,1,e|0);if(a[235512]|0){a[235512]=0;e=c[10030]|0;aF(61,e|0);j=d;k=b}else{j=d-(c[58820]|0)|0;k=b-(c[58822]|0)|0}a[235520]=1;h=j;i=k}k=i<<1;i=(k|0)<0?1-k|0:k;k=i&63;j=i>>6;if((j|0)>0){i=k;e=j;while(1){aF(i+63|0,c[10030]|0);j=e&63;f=e>>6;if((f|0)>0){i=j;e=f}else{l=j;break}}}else{l=k}aF((l<<24)-1090519040>>24|0,c[10030]|0);l=h<<1;h=(l|0)<0?1-l|0:l;l=h&63;k=h>>6;if((k|0)>0){m=l;n=k}else{o=l;p=o<<24;q=p-1090519040|0;r=q>>24;s=c[10030]|0;t=aF(r|0,s|0)|0;u=c[10030]|0;v=aF(10,u|0)|0;c[58822]=b;c[58820]=d;return}while(1){aF(m+63|0,c[10030]|0);l=n&63;k=n>>6;if((k|0)>0){m=l;n=k}else{o=l;break}}p=o<<24;q=p-1090519040|0;r=q>>24;s=c[10030]|0;t=aF(r|0,s|0)|0;u=c[10030]|0;v=aF(10,u|0)|0;c[58822]=b;c[58820]=d;return}function o2(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;if(a[235520]|0){e=d-(c[58820]|0)|0;f=b-(c[58822]|0)|0}else{g=c[10030]|0;aI(177472,2,1,g|0);if(a[235512]|0){a[235512]=0;g=c[10030]|0;aF(61,g|0);h=d;i=b}else{h=d-(c[58820]|0)|0;i=b-(c[58822]|0)|0}a[235520]=1;e=h;f=i}i=f<<1;f=(i|0)<0?1-i|0:i;i=f&63;h=f>>6;if((h|0)>0){f=i;g=h;while(1){aF(f+63|0,c[10030]|0);h=g&63;j=g>>6;if((j|0)>0){f=h;g=j}else{k=h;break}}}else{k=i}aF((k<<24)-1090519040>>24|0,c[10030]|0);k=e<<1;e=(k|0)<0?1-k|0:k;k=e&63;i=e>>6;if((i|0)>0){l=k;m=i}else{n=k;o=n<<24;p=o-1090519040|0;q=p>>24;r=c[10030]|0;s=aF(q|0,r|0)|0;t=c[10030]|0;u=aF(10,t|0)|0;c[58822]=b;c[58820]=d;return}while(1){aF(l+63|0,c[10030]|0);k=m&63;i=m>>6;if((i|0)>0){l=k;m=i}else{n=k;break}}o=n<<24;p=o-1090519040|0;q=p>>24;r=c[10030]|0;s=aF(q|0,r|0)|0;t=c[10030]|0;u=aF(10,t|0)|0;c[58822]=b;c[58820]=d;return}function o3(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;d=i;if(a[235520]|0){e=c[10030]|0;aI(125976,2,1,e|0);a[235520]=0}e=(b|0)>-1;if(a[236376]|0){if(e){if(a[235336]|0){c[59092]=(b&7)+1}f=(b>>>0)%24>>>0}else{f=b}if((f|0)==(c[58846]|0)){i=d;return}do{if((f|0)>-1){g=c[10030]|0;h=c[235344+(f>>>3<<2)>>2]|0;if(a[235336]|0){j=c[59092]|0;k=c[235480+((f&7)<<2)>>2]|0;cf(g|0,177704,(v=i,i=i+24|0,c[v>>2]=h,c[v+8>>2]=j,c[v+16>>2]=k,v)|0);break}else{cf(g|0,177696,(v=i,i=i+8|0,c[v>>2]=h,v)|0);break}}else{if((f|0)==(-2|0)){h=c[10030]|0;aI(177680,6,1,h|0);break}else if((f|0)==(-1|0)){aI(177664,11,1,c[10030]|0);break}else{break}}}while(0);c[58846]=f;i=d;return}else{if(e){if(a[235336]|0){c[59092]=(b&7)+1}l=((b+2|0)%(c[58868]|0)&-1)+1|0}else{l=b}if((l|0)==(c[58846]|0)){i=d;return}do{if((l|0)>-1){b=c[10030]|0;if(a[235336]|0){e=c[59092]|0;cf(b|0,177848,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=e,v)|0);break}else{cf(b|0,177768,(v=i,i=i+8|0,c[v>>2]=l,v)|0);break}}else{if((l|0)==(-2|0)){b=c[10030]|0;aI(177744,15,1,b|0);break}else if((l|0)==(-1|0)){aI(177720,20,1,c[10030]|0);break}else{break}}}while(0);c[58826]=1;c[58846]=l;i=d;return}}function o4(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0;f=i;g=(c[(c[3524]|0)+16>>2]|0)>>>2;if((c[58832]|0)==1){o1(g+b|0,d)}else{o1(b,d-g|0)}if(a[235520]|0){g=c[10030]|0;aI(125976,2,1,g|0);a[235520]=0}cf(c[10030]|0,177872,(v=i,i=i+8|0,c[v>>2]=e,v)|0);a[235512]=1;i=f;return}function o5(b){b=b|0;var d=0,e=0;if((b|0)==(-90|0)|(b|0)==270){d=-1}else{d=(b|0)!=0&1}c[58832]=d;if(a[235520]|0){b=c[10030]|0;aF(59,b|0);a[235520]=0;e=c[58832]|0}else{e=d}if((e|0)==1){d=c[10030]|0;aI(177968,5,1,d|0);return 1}d=c[10030]|0;if((e|0)==-1){aI(177960,6,1,d|0);return 1}else{aI(177912,5,1,d|0);return 1}return 0}function o6(b){b=b|0;var d=0,e=0;if(a[235520]|0){d=c[10030]|0;aI(125976,2,1,d|0);a[235520]=0}if((b|0)==2){d=c[10030]|0;aI(177984,3,1,d|0);e=1;return e|0}else if((b|0)==1){aI(177992,3,1,c[10030]|0);e=1;return e|0}else if((b|0)==0){aI(178e3,3,1,c[10030]|0);e=1;return e|0}else{e=0;return e|0}return 0}function o7(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0;f=i;g=(e|0)<0;if(!(a[28152]|0)){if(g){tH(b,d,e);i=f;return}j=~~(+h[29420]*106.0*.5);k=(e|0)%6&-1;if((k|0)==4){l=(j<<2|0)/3&-1;m=l+d|0;o1(b,m);n=d-((j<<1|0)/3&-1)|0;o2(b-l|0,n);o2(l+b|0,n);o2(b,m);o1(b,d);o2(b,d);i=f;return}else if((k|0)==5){m=b-j|0;o1(m,d);o2(m,d);n=j+b|0;o2(n,d);l=d-j|0;o1(b,l);o2(b,l);o=j+d|0;o2(b,o);o1(m,l);o2(m,l);o2(n,o);o1(m,o);o2(m,o);o2(n,l);i=f;return}else if((k|0)==2){l=b-j|0;n=d-j|0;o1(l,n);o2(l,n);o=j+b|0;o2(o,n);m=j+d|0;o2(o,m);o2(l,m);o2(l,n);o1(b,d);o2(b,d);i=f;return}else if((k|0)==3){n=b-j|0;l=d-j|0;o1(n,l);o2(n,l);m=j+b|0;o=j+d|0;o2(m,o);o1(n,o);o2(n,o);o2(m,l);i=f;return}else if((k|0)==0){l=b-j|0;o1(l,d);o2(b,d-j|0);o2(j+b|0,d);o2(b,j+d|0);o2(l,d);o1(b,d);o2(b,d);i=f;return}else if((k|0)==1){k=b-j|0;o1(k,d);o2(k,d);o2(j+b|0,d);k=d-j|0;o1(b,k);o2(b,k);o2(b,j+d|0);i=f;return}else{i=f;return}}if(g){tH(b,d,e);i=f;return}g=~~(+h[29420]*106.0*.5);j=(e|0)%75&-1;if((j|0)==52){e=(g|0)/2&-1;k=b-e|0;l=e+d|0;o1(k,l);o2(b-g|0,d);m=d-e|0;o2(k,m);o=c[10030]|0;aI(125976,2,1,o|0);a[235520]=0;o=c[10030]|0;aI(179152,5,1,o|0);o2(b,d);o=e+b|0;o2(o,m);o2(b,d-g|0);o2(k,m);e=c[10030]|0;aI(125976,2,1,e|0);a[235520]=0;e=c[10030]|0;aI(179120,11,1,e|0);o1(o,m);o2(g+b|0,d);o2(o,l);m=c[10030]|0;aI(125976,2,1,m|0);a[235520]=0;m=c[10030]|0;aI(179152,5,1,m|0);o2(b,g+d|0);o2(k,l);o2(b,d);o2(o,l);l=c[10030]|0;aI(125976,2,1,l|0);a[235520]=0;l=c[10030]|0;aI(179120,11,1,l|0);i=f;return}else if((j|0)==13){l=((g*3&-1|0)/4&-1)+d|0;o1(b,l);p=+(b>>>0>>>0);q=+(g|0);r=q*2.8531695488854605*.25;s=+(d>>>0>>>0);o=~~(s+q*.9270509831248421*.25);o2(~~(p-r),o);t=q*1.7633557568774194*.25;k=~~(s-q*2.4270509831248424*.25);o2(~~(p-t),k);o2(~~(p+t),k);o2(~~(p+r),o);o2(b,l);o1(b,d);o2(b,d);i=f;return}else if((j|0)==38){l=d-g|0;o1(b,l);o=g+b|0;o2(o,l);o2(o,d);o1(b-g|0,l);aI(125976,2,1,c[10030]|0);a[235520]=0;l=g+d|0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=l,v)|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=o,c[v+8>>2]=l,v)|0);i=f;return}else if((j|0)==14){l=((g*3&-1|0)/4&-1)+d|0;o1(b,l);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);r=+(b>>>0>>>0);p=+(g|0);t=p*2.8531695488854605*.25;q=+(d>>>0>>>0);o=~~(q+p*.9270509831248421*.25);o2(~~(r-t),o);s=p*1.7633557568774194*.25;k=~~(q-p*2.4270509831248424*.25);o2(~~(r-s),k);o2(~~(r+s),k);o2(~~(r+t),o);o2(b,l);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==68){l=((g*3&-1|0)/4&-1)+d|0;o1(b,l);t=+(b>>>0>>>0);r=+(g|0);s=r*2.8531695488854605*.25;p=+(d>>>0>>>0);o=~~(p+r*.9270509831248421*.25);o2(~~(t-s),o);q=r*1.7633557568774194*.25;k=~~(p-r*2.4270509831248424*.25);o2(~~(t-q),k);o2(~~(t+q),k);o2(~~(t+s),o);o2(b,l);i=f;return}else if((j|0)==33){l=b-g|0;o1(l,d);o=d-g|0;o2(l,o);k=g+b|0;o2(k,o);o=g+d|0;o2(k,o);o2(b,o);o1(l,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=o,v)|0);i=f;return}else if((j|0)==46){o1(b-g|0,d-g|0);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=g+b,c[v+8>>2]=g+d,v)|0);i=f;return}else if((j|0)==22){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178624,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==34){o=b-g|0;o1(o,d);l=d-g|0;o2(o,l);k=g+b|0;o2(k,l);o2(k,d);o1(o,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=g+d,v)|0);i=f;return}else if((j|0)==37){k=d-g|0;o1(b,k);o=g+b|0;o2(o,k);l=g+d|0;o2(o,l);o2(b,l);o1(b-g|0,k);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=l,v)|0);i=f;return}else if((j|0)==3){l=(g*3&-1|0)/4&-1;k=b-l|0;o=d-l|0;o1(k,o);m=l+b|0;o2(m,o);e=l+d|0;o2(m,e);o2(k,e);o2(k,o);o1(b,d);o2(b,d);i=f;return}else if((j|0)==5){o1(b,d);o2(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=+(g|0)*3.0*.25,v)|0);i=f;return}else if((j|0)==10){o=g*3&-1;k=d-((o|0)/4&-1)|0;o1(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);s=+(b>>>0>>>0);t=+(g|0)*5.196152422706632*.125;e=((o|0)/8&-1)+d|0;o2(~~(s-t),e);o2(~~(s+t),e);o2(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==58){k=(g|0)/2&-1;e=b-k|0;o=d-k|0;o1(e,o);o2(b,d-g|0);m=k+b|0;o2(m,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d);o2(e,o);o2(b-g|0,d);o2(b,g+d|0);o2(g+b|0,d);o2(m,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==51){o=(g|0)/2&-1;m=o+b|0;e=d-o|0;o1(m,e);o2(g+b|0,d);o2(b,g+d|0);o2(b-g|0,d);k=b-o|0;o2(k,e);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d);o2(m,e);o2(b,d-g|0);o2(k,e);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==36){e=d-g|0;o1(b,e);k=g+b|0;o2(k,e);o2(k,d);m=g+d|0;o1(b,m);o=b-g|0;o2(o,m);o2(o,d);o1(o,e);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=m,v)|0);i=f;return}else if((j|0)==42){m=b-g|0;o1(m,d);k=d-g|0;o2(m,k);o2(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;k=g+d|0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=g+b,c[v+8>>2]=k,v)|0);o1(m,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=k,v)|0);i=f;return}else if((j|0)==67){k=(g*3&-1|0)/4&-1;m=b-k|0;o1(m,d);o2(b,d-k|0);o2(k+b|0,d);o2(b,k+d|0);o2(m,d);i=f;return}else if((j|0)==71){m=g*3&-1;k=((m|0)/4&-1)+d|0;o1(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);t=+(b>>>0>>>0);s=+(g|0)*5.196152422706632*.125;e=d-((m|0)/8&-1)|0;o2(~~(t-s),e);o2(~~(t+s),e);o2(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(178056,22,1,c[10030]|0);i=f;return}else if((j|0)==63){k=(g*3&-1|0)/4&-1;e=b-k|0;m=d-k|0;o1(e,m);o=k+b|0;o2(o,m);l=k+d|0;o2(o,l);o2(e,l);o2(e,m);i=f;return}else if((j|0)==27){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178328,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==15){o1(b,g+d|0);o2(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179104,(v=i,i=i+8|0,c[v>>2]=g,v)|0);i=f;return}else if((j|0)==60){m=(g|0)/2&-1;e=b-m|0;l=m+d|0;o1(e,l);o2(b-g|0,d);o=d-m|0;o2(e,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d-g|0);o2(g+b|0,d);o2(b,g+d|0);o2(e,l);o2(b,d);o2(e,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==29){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178264,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==69){o1(b-g|0,d-g|0);aI(125976,2,1,c[10030]|0);a[235520]=0;o=(g*3&-1|0)/4&-1;cf(c[10030]|0,178192,(v=i,i=i+16|0,c[v>>2]=o+b,c[v+8>>2]=o+d,v)|0);i=f;return}else if((j|0)==59){o=(g|0)/2&-1;e=o+b|0;l=o+d|0;o1(e,l);o2(b,g+d|0);o2(b-g|0,d);m=b-o|0;k=d-o|0;o2(m,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(e,l);o2(g+b|0,d);o2(b,d-g|0);o2(m,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==31){k=b-g|0;m=d-g|0;o1(k,m);l=g+b|0;o2(l,m);e=g+d|0;o2(l,e);o2(k,e);o2(k,m);o1(b,e);o2(b,d);i=f;return}else if((j|0)==0){e=b-g|0;o1(e,d);o2(e,d);o2(g+b|0,d);e=d-g|0;o1(b,e);o2(b,e);o2(b,g+d|0);i=f;return}else if((j|0)==9){e=g*3&-1;m=d-((e|0)/4&-1)|0;o1(b,m);s=+(b>>>0>>>0);t=+(g|0)*5.196152422706632*.125;k=((e|0)/8&-1)+d|0;o2(~~(s-t),k);o2(~~(s+t),k);o2(b,m);o1(b,d);o2(b,d);i=f;return}else if((j|0)==61){m=(g|0)/2&-1;k=m+b|0;e=m+d|0;o1(k,e);o2(b,g+d|0);l=b-m|0;o2(l,e);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b-g|0,d);o2(b,d-g|0);o2(g+b|0,d);o2(k,e);o2(b,d);o2(l,e);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==48){e=(g|0)/2&-1;l=b-e|0;k=e+d|0;o1(l,k);o2(b-g|0,d);o2(b,d-g|0);o2(g+b|0,d);m=e+b|0;o2(m,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,g+d|0);o2(l,k);o2(b,d);o2(m,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==16){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179008,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==1){k=b-g|0;m=d-g|0;o1(k,m);o2(k,m);l=g+b|0;e=g+d|0;o2(l,e);o1(k,e);o2(k,e);o2(l,m);i=f;return}else if((j|0)==55){m=(g|0)/2&-1;l=m+b|0;e=m+d|0;o1(l,e);o2(b,g+d|0);o2(b-g|0,d);o2(b,d-g|0);k=d-m|0;o2(l,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d);o2(l,e);o2(g+b|0,d);o2(l,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==7){k=g*3&-1;l=((k|0)/4&-1)+d|0;o1(b,l);t=+(b>>>0>>>0);s=+(g|0)*5.196152422706632*.125;e=d-((k|0)/8&-1)|0;o2(~~(t-s),e);o2(~~(t+s),e);o2(b,l);o1(b,d);o2(b,d);i=f;return}else if((j|0)==39){l=g+b|0;o1(l,d);e=g+d|0;o2(l,e);k=b-g|0;o2(k,e);e=d-g|0;o2(k,e);o2(b,e);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=d,v)|0);i=f;return}else if((j|0)==28){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178296,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==20){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178752,(v=i,i=i+32|0,c[v>>2]=g,c[v+8>>2]=g,c[v+16>>2]=g,c[v+24>>2]=g,v)|0);i=f;return}else if((j|0)==64){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=+(g|0)*3.0*.25,v)|0);i=f;return}else if((j|0)==65){l=g*3&-1;e=((l|0)/4&-1)+d|0;o1(b,e);s=+(b>>>0>>>0);t=+(g|0)*5.196152422706632*.125;k=d-((l|0)/8&-1)|0;o2(~~(s-t),k);o2(~~(s+t),k);o2(b,e);i=f;return}else if((j|0)==66){e=g*3&-1;k=d-((e|0)/4&-1)|0;o1(b,k);t=+(b>>>0>>>0);s=+(g|0)*5.196152422706632*.125;l=((e|0)/8&-1)+d|0;o2(~~(t-s),l);o2(~~(t+s),l);o2(b,k);i=f;return}else if((j|0)==62){k=b-g|0;o1(k,d);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d-g|0);o2(g+b|0,d);o2(b,g+d|0);o2(k,d);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==8){k=g*3&-1;l=((k|0)/4&-1)+d|0;o1(b,l);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);s=+(b>>>0>>>0);t=+(g|0)*5.196152422706632*.125;e=d-((k|0)/8&-1)|0;o2(~~(s-t),e);o2(~~(s+t),e);o2(b,l);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==11){l=(g*3&-1|0)/4&-1;e=b-l|0;o1(e,d);o2(b,d-l|0);o2(l+b|0,d);o2(b,l+d|0);o2(e,d);o1(b,d);o2(b,d);i=f;return}else if((j|0)==70){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178096,(v=i,i=i+8|0,h[v>>3]=+(g|0)*3.0*.25,v)|0);i=f;return}else if((j|0)==21){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178720,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==44){e=g+d|0;o1(b,e);l=b-g|0;o2(l,e);o2(l,d);o1(l,d-g|0);aI(125976,2,1,c[10030]|0);a[235520]=0;l=g+b|0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=d,v)|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=e,v)|0);i=f;return}else if((j|0)==45){e=g+b|0;o1(e,d);l=g+d|0;o2(e,l);o2(b,l);k=b-g|0;o1(k,d-g|0);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=e,c[v+8>>2]=d,v)|0);o1(k,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=l,v)|0);i=f;return}else if((j|0)==43){l=g+b|0;o1(l,d);k=g+d|0;o2(l,k);e=b-g|0;o2(e,k);o2(e,d);o1(e,d-g|0);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=d,v)|0);i=f;return}else if((j|0)==53){l=(g|0)/2&-1;e=l+b|0;k=d-l|0;o1(e,k);o2(g+b|0,d);o2(b,g+d|0);m=b-l|0;o=l+d|0;o2(m,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b-g|0,d);o2(b,d-g|0);o2(e,k);o2(m,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==56){o=(g|0)/2&-1;m=b-o|0;k=o+d|0;o1(m,k);o2(b-g|0,d);o2(b,d-g|0);e=o+b|0;l=d-o|0;o2(e,l);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(m,k);o2(b,g+d|0);o2(g+b|0,d);o2(e,l);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==18){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178904,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==73){l=(g*3&-1|0)/4&-1;e=b-l|0;o1(e,d);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d-l|0);o2(l+b|0,d);o2(b,l+d|0);o2(e,d);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(178056,22,1,c[10030]|0);i=f;return}else if((j|0)==50){e=(g|0)/2&-1;l=b-e|0;k=d-e|0;o1(l,k);o2(b,d-g|0);o2(g+b|0,d);m=e+b|0;o=e+d|0;o2(m,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(l,k);o2(b-g|0,d);o2(b,g+d|0);o2(m,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==2){o=b-g|0;o1(o,d);o2(o,d);m=g+b|0;o2(m,d);k=d-g|0;o1(b,k);o2(b,k);l=g+d|0;o2(b,l);o1(o,k);o2(o,k);o2(m,l);o1(o,l);o2(o,l);o2(m,k);i=f;return}else if((j|0)==12){k=(g*3&-1|0)/4&-1;m=b-k|0;o1(m,d);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d-k|0);o2(k+b|0,d);o2(b,k+d|0);o2(m,d);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==17){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178960,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==4){m=(g*3&-1|0)/4&-1;o1(b-m|0,d-m|0);aI(125976,2,1,c[10030]|0);a[235520]=0;t=+(m|0);cf(c[10030]|0,179264,(v=i,i=i+16|0,h[v>>3]=+(b>>>0>>>0)+t,h[v+8>>3]=+(d>>>0>>>0)+t,v)|0);i=f;return}else if((j|0)==72){m=g*3&-1;k=d-((m|0)/4&-1)|0;o1(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);t=+(b>>>0>>>0);s=+(g|0)*5.196152422706632*.125;l=((m|0)/8&-1)+d|0;o2(~~(t-s),l);o2(~~(t+s),l);o2(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(178056,22,1,c[10030]|0);i=f;return}else if((j|0)==35){k=d-g|0;o1(b,k);l=g+b|0;o2(l,k);m=g+d|0;o2(l,m);l=b-g|0;o2(l,m);o2(l,d);o1(l,k);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);i=f;return}else if((j|0)==32){k=g+d|0;o1(b,k);l=b-g|0;o2(l,k);m=d-g|0;o2(l,m);l=g+b|0;o2(l,m);o2(l,d);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=k,v)|0);i=f;return}else if((j|0)==47){k=b-g|0;o1(k,d);o2(b,d-g|0);o2(g+b|0,d);o2(b,g+d|0);o2(k,d);k=(g|0)/2&-1;o1(b-k|0,k+d|0);o2(b,d);i=f;return}else if((j|0)==40){k=g+d|0;o1(b,k);l=b-g|0;o2(l,k);m=d-g|0;o2(l,m);o2(b,m);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=g+b,c[v+8>>2]=k,v)|0);i=f;return}else if((j|0)==24){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178496,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==6){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179192,(v=i,i=i+8|0,h[v>>3]=+(g|0)*3.0*.25,v)|0);i=f;return}else if((j|0)==41){k=b-g|0;o1(k,d);m=d-g|0;o2(k,m);o2(b,m);l=g+b|0;o1(l,d);o=g+d|0;o2(l,o);o2(b,o);o1(k,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=o,v)|0);o1(b,m);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178224,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=d,v)|0);i=f;return}else if((j|0)==25){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178392,(v=i,i=i+32|0,c[v>>2]=g,c[v+8>>2]=g,c[v+16>>2]=g,c[v+24>>2]=g,v)|0);i=f;return}else if((j|0)==19){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178872,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==23){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178536,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==26){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178360,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=g,v)|0);i=f;return}else if((j|0)==30){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,178240,(v=i,i=i+8|0,c[v>>2]=g,v)|0);i=f;return}else if((j|0)==49){l=(g|0)/2&-1;m=b-l|0;o=d-l|0;o1(m,o);o2(b,d-g|0);o2(g+b|0,d);o2(b,g+d|0);k=l+d|0;o2(m,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b-g|0,d);o2(m,o);o2(b,d);o2(m,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==74){k=((g*3&-1|0)/4&-1)+d|0;o1(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);s=+(b>>>0>>>0);t=+(g|0);q=t*2.8531695488854605*.25;r=+(d>>>0>>>0);m=~~(r+t*.9270509831248421*.25);o2(~~(s-q),m);p=t*1.7633557568774194*.25;o=~~(r-t*2.4270509831248424*.25);o2(~~(s-p),o);o2(~~(s+p),o);o2(~~(s+q),m);o2(b,k);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(178056,22,1,c[10030]|0);i=f;return}else if((j|0)==57){k=(g|0)/2&-1;m=k+b|0;o=k+d|0;o1(m,o);o2(b,g+d|0);l=b-k|0;o2(l,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d);e=d-k|0;o2(l,e);o2(b-g|0,d);o2(l,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);o1(l,e);o2(b,d-g|0);o2(m,e);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,d);o2(m,o);o2(g+b|0,d);o2(m,e);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else if((j|0)==54){j=(g|0)/2&-1;e=j+b|0;m=d-j|0;o1(e,m);o2(g+b|0,d);o=j+d|0;o2(e,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179152,5,1,c[10030]|0);o2(b,g+d|0);o2(b-g|0,d);o2(b,d-g|0);o2(e,m);o2(b,d);o2(e,o);aI(125976,2,1,c[10030]|0);a[235520]=0;aI(179120,11,1,c[10030]|0);i=f;return}else{i=f;return}}function o8(a){a=+a;h[29420]=a>=0.0?a:1.0;return}function o9(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0.0;d=i;i=i+64|0;e=d|0;f=d+56|0;g=c[3524]|0;if(a[235520]|0){j=c[10030]|0;aI(125976,2,1,j|0);a[235520]=0}j=bk(b|0,148464)|0;k=e|0;uF(k|0,b|0,j|0);a[e+j|0]=0;e=0;while(1){if(e>>>0>=17){break}l=c[235528+(e*48&-1)>>2]|0;if((a_(k|0,l|0,bk(l|0,180784)|0)|0)==0){break}else{e=e+1|0}}k=e>>>0>16?c[59088]|0:e;c[f>>2]=0;ca(b+(j+1|0)|0,21e4,(v=i,i=i+8|0,c[v>>2]=f,v)|0);j=c[f>>2]|0;if((j|0)>0){m=+(j|0)}else{m=+h[29422]}if(m==+h[29421]&(k|0)==(c[59086]|0)){n=0;i=d;return n|0}c[59090]=235528+(k*48&-1);c[59086]=k;h[29421]=m;k=~~(m*1016.0/72.0);c[g+16>>2]=k;c[g+20>>2]=(k<<1>>>0)/3>>>0;k=c[59090]|0;g=c[k+12>>2]|0;cf(c[10030]|0,180696,(v=i,i=i+16|0,c[v>>2]=c[k+8>>2],c[v+8>>2]=g,v)|0);g=c[59090]|0;m=+h[29421];if((c[g+12>>2]|0)==0){o=216.0/(m*2.0);h[g+16>>3]=o;k=c[10030]|0;cf(k|0,180656,(v=i,i=i+8|0,h[v>>3]=o,v)|0)}else{h[g+24>>3]=m;g=c[10030]|0;cf(g|0,180672,(v=i,i=i+8|0,h[v>>3]=m,v)|0)}g=c[59090]|0;k=c[g+36>>2]|0;j=c[g+40>>2]|0;cf(c[10030]|0,180632,(v=i,i=i+24|0,c[v>>2]=c[g+32>>2],c[v+8>>2]=k,c[v+16>>2]=j,v)|0);n=1;i=d;return n|0}function pa(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0.0,N=0,O=0,P=0.0,Q=0.0;b=i;i=i+24|0;d=b|0;e=c[13898]|0;f=c[8272]|0;if((e|0)<(f|0)){g=e;j=f}else{k=c[58772]|0;l=a[234984]|0;m=l?78720:78704;n=c[58574]|0;o=c[58572]|0;p=be(13048,180808,(v=i,i=i+32|0,c[v>>2]=k,c[v+8>>2]=m,c[v+16>>2]=n,c[v+24>>2]=o,v)|0)|0;i=b;return}L20934:while(1){f=c[1054]|0;e=(a[f+(g*40&-1)|0]&1)==0;L20936:do{if(!e){q=c[f+(g*40&-1)+36>>2]|0;r=f+(g*40&-1)+32|0;s=c[10036]|0;t=0;while(1){if((t|0)>=(q|0)){break}if((a[s+((c[r>>2]|0)+t|0)|0]|0)==(a[t+103664|0]|0)){t=t+1|0}else{break L20936}}if((t|0)==1){u=15625;break L20934}}}while(0);r=c[58760]|0;L20943:do{if((r|0)==0){w=235040}else{s=c[f+(g*40&-1)+36>>2]|0;q=c[f+(g*40&-1)+32>>2]|0;x=(s|0)>0;y=c[10036]|0;z=235040;A=r;while(1){L20947:do{if(!e){if(x){B=0;C=0;D=q;while(1){E=a[A+B|0]|0;if(E<<24>>24==(a[y+(B+D|0)|0]|0)){F=D;G=C}else{if(E<<24>>24!=36){break L20947}F=D-1|0;G=1}H=B+1|0;if((H|0)<(G+s|0)){B=H;C=G;D=F}else{break}}if((G|0)==0){I=H}else{w=z;break L20943}}else{I=0}D=a[A+I|0]|0;if((D<<24>>24|0)==36|(D<<24>>24|0)==0){w=z;break L20943}}}while(0);D=z+8|0;C=c[D>>2]|0;if((C|0)==0){w=D;break}else{z=D;A=C}}}}while(0);r=c[w+4>>2]|0;L20960:do{if((r|0)==0){a[234984]=1;a[235e3]=1;a[234992]=1;a[235272]=1;a[235264]=1;c[13898]=g+1}else if((r|0)==1){a[234984]=0;c[13898]=g+1}else{L20963:do{if(!((j|0)<=(g|0)|e)){A=c[f+(g*40&-1)+36>>2]|0;z=f+(g*40&-1)+32|0;s=c[10036]|0;y=0;while(1){if((y|0)>=(A|0)){break}if((a[s+((c[z>>2]|0)+y|0)|0]|0)==(a[y+78280|0]|0)){y=y+1|0}else{break L20963}}if((y|0)!=1){break}J=g+1|0;c[13898]=J;z=(j|0)>(J|0);if(!z){u=15629;break L20934}if((a[f+(J*40&-1)|0]&1)!=0){A=c[f+(J*40&-1)+36>>2]|0;q=f+(J*40&-1)+32|0;x=0;while(1){if((x|0)>=(A|0)){u=15562;break}if((a[s+((c[q>>2]|0)+x|0)|0]|0)==(a[x+78864|0]|0)){x=x+1|0}else{break}}do{if((u|0)==15562){u=0;if((x|0)!=1){break}if((c[58576]|0)!=0){tG()}q=c[10030]|0;aF(219,q|0);c[13898]=(c[13898]|0)+1;break L20960}}while(0);if(!z){u=15627;break L20934}}x=(a[f+(J*40&-1)|0]&1)==0;L20984:do{if(x){u=15578}else{q=c[f+(J*40&-1)+36>>2]|0;A=f+(J*40&-1)+32|0;y=0;while(1){if((y|0)>=(q|0)){u=15571;break}if((a[s+((c[A>>2]|0)+y|0)|0]|0)==(a[y+103664|0]|0)){y=y+1|0}else{break}}if((u|0)==15571){u=0;if((y|0)==1){u=15628;break L20934}}if(x){u=15578;break}A=c[f+(J*40&-1)+36>>2]|0;q=f+(J*40&-1)+32|0;t=0;while(1){if((t|0)>=(A|0)){break}if((a[s+((c[q>>2]|0)+t|0)|0]|0)==(a[t+148464|0]|0)){t=t+1|0}else{u=15578;break L20984}}if((t|0)==1){K=J;L=j}else{u=15578}}}while(0);if((u|0)==15578){u=0;s=is(d)|0;x=c[s>>2]|0;if((x|0)==2){M=+h[s+8>>3]}else if((x|0)==1){M=+(c[s+8>>2]|0)}else if((x|0)==3){M=+uz(c[s+8>>2]|0,0)}else{u=15582;break L20934}c[58574]=~~M;K=c[13898]|0;L=c[8272]|0}if((L|0)<=(K|0)){u=15631;break L20934}s=c[1054]|0;if((a[s+(K*40&-1)|0]&1)==0){u=15632;break L20934}x=c[s+(K*40&-1)+36>>2]|0;z=s+(K*40&-1)+32|0;q=c[10036]|0;A=0;while(1){if((A|0)>=(x|0)){break}if((a[q+((c[z>>2]|0)+A|0)|0]|0)==(a[A+148464|0]|0)){A=A+1|0}else{u=15633;break L20934}}if((A|0)!=1){u=15630;break L20934}z=K+1|0;c[13898]=z;L21012:do{if((L|0)>(z|0)){if((a[s+(z*40&-1)|0]&1)==0){u=15597;break}x=c[s+(z*40&-1)+36>>2]|0;y=s+(z*40&-1)+32|0;C=0;while(1){if((C|0)>=(x|0)){break}if((a[q+((c[y>>2]|0)+C|0)|0]|0)==(a[C+78864|0]|0)){C=C+1|0}else{u=15597;break L21012}}if((C|0)==1){N=z;O=L}else{u=15597}}else{u=15597}}while(0);if((u|0)==15597){u=0;z=is(d)|0;q=c[z>>2]|0;if((q|0)==3){P=+uz(c[z+8>>2]|0,0)}else if((q|0)==2){P=+h[z+8>>3]}else if((q|0)==1){P=+(c[z+8>>2]|0)}else{u=15601;break L20934}c[58572]=~~P;N=c[13898]|0;O=c[8272]|0}if((O|0)<=(N|0)){u=15621;break L20934}z=c[1054]|0;if((a[z+(N*40&-1)|0]&1)==0){u=15623;break L20934}q=c[z+(N*40&-1)+36>>2]|0;s=z+(N*40&-1)+32|0;z=c[10036]|0;A=0;while(1){if((A|0)>=(q|0)){break}if((a[z+((c[s>>2]|0)+A|0)|0]|0)==(a[A+78864|0]|0)){A=A+1|0}else{u=15622;break L20934}}if((A|0)!=1){u=15620;break L20934}c[13898]=N+1;break L20960}}while(0);s=is(d)|0;z=c[s>>2]|0;if((z|0)==2){Q=+h[s+8>>3]}else if((z|0)==3){Q=+uz(c[s+8>>2]|0,0)}else if((z|0)==1){Q=+(c[s+8>>2]|0)}else{u=15616;break L20934}s=~~Q;z=(s|0)<8?8:s;c[58772]=z;if((z|0)<=15){break}c[58772]=15}}while(0);f=c[13898]|0;e=c[8272]|0;if((f|0)<(e|0)){g=f;j=e}else{u=15624;break}}if((u|0)==15633){uf(K,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15632){uf(K,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15625){k=c[58772]|0;l=a[234984]|0;m=l?78720:78704;n=c[58574]|0;o=c[58572]|0;p=be(13048,180808,(v=i,i=i+32|0,c[v>>2]=k,c[v+8>>2]=m,c[v+16>>2]=n,c[v+24>>2]=o,v)|0)|0;i=b;return}else if((u|0)==15627){uf(J,78976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15628){uf(J,78976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15582){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15621){uf(N,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15622){uf(N,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15601){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15623){uf(N,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15624){k=c[58772]|0;l=a[234984]|0;m=l?78720:78704;n=c[58574]|0;o=c[58572]|0;p=be(13048,180808,(v=i,i=i+32|0,c[v>>2]=k,c[v+8>>2]=m,c[v+16>>2]=n,c[v+24>>2]=o,v)|0)|0;i=b;return}else if((u|0)==15629){uf(J,78976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15630){uf(K,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15616){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15631){uf(K,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((u|0)==15620){uf(N,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function pb(){return}function pc(){var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0;d=i;i=i+24|0;e=d|0;f=c[3524]|0;g=a[235272]|0?2340:3300;c[58758]=((a[234992]|0?2490:3480)-g|0)/2&-1;h=a[235264]|0?3300:2340;c[58756]=((a[235e3]|0?3480:2490)-h|0)/2&-1;j=(g|0)/(c[58574]|0)&-1;c[58742]=j;c[58738]=j;g=(h|0)/(c[58572]|0)&-1;c[58740]=g;c[58736]=g;c[f+8>>2]=j;c[f+12>>2]=c[58736];aI(180888,37,1,c[10030]|0);if(a[234984]|0){f=c[10030]|0;aF(137,f|0);f=c[10030]|0;aF(13,f|0);f=c[10030]|0;aF(3520,f|0)}aF(205,c[10030]|0);f=a[234984]|0;aF((f?120:24)|(f?0:5)|0,c[10030]|0);f=32;do{b[234432+(f<<1)>>1]=f&65535;f=f+1|0;}while((f|0)<127);b[117217]=225;b[117218]=233;b[117219]=-3800;b[117220]=241;b[117221]=249;b[117222]=-3672;b[117223]=162;b[117224]=163;b[117225]=164;b[117226]=165;b[117227]=167;b[117228]=171;b[117229]=182;b[117230]=-4190;b[117231]=191;b[117232]=187;b[117233]=188;b[117234]=189;b[117235]=190;b[117236]=210;b[117237]=211;b[117238]=251;b[117239]=-4304;b[117240]=212;b[117241]=137;b[117242]=176;b[117243]=161;b[117244]=139;b[117245]=133;b[117246]=140;b[117247]=-4287;b[117248]=8738;b[117250]=186;b[117252]=164;b[117255]=185;b[117343]=-4250;f=32;do{a[f+234688|0]=f&255;f=f+1|0;}while((f|0)<128);uE(234816,-128|0,126);a[234849]=27;a[234850]=7;a[234851]=8;a[234852]=120;a[234853]=10;a[234854]=124;a[234855]=11;a[234856]=25;a[234857]=21;a[234858]=45;a[234859]=12;a[234860]=83;a[234861]=45;a[234862]=20;a[234863]=126;a[234864]=26;a[234865]=12;a[234866]=1;a[234867]=2;a[234868]=29;a[234869]=52;a[234870]=13;a[234871]=5;a[234872]=28;a[234873]=3;a[234874]=45;a[234875]=16;a[234876]=17;a[234877]=18;a[234878]=19;a[234879]=15;uE(234880,65,5);a[234885]=3;a[234886]=1;a[234887]=67;w=1162167621;a[234888]=w&255;w=w>>8;a[234889|0]=w&255;w=w>>8;a[234890|0]=w&255;w=w>>8;a[234891|0]=w&255;w=1229539657;a[234892]=w&255;w=w>>8;a[234893|0]=w&255;w=w>>8;a[234894|0]=w&255;w=w>>8;a[234895|0]=w&255;a[234896]=68;a[234897]=78;uE(234898,79,5);a[234903]=13;a[234904]=2;w=1431655765;a[234905]=w&255;w=w>>8;a[234906|0]=w&255;w=w>>8;a[234907|0]=w&255;w=w>>8;a[234908|0]=w&255;a[234909]=89;a[234910]=32;a[234911]=22;uE(234912,97,5);a[234917]=6;a[234918]=4;a[234919]=99;w=1701143909;a[234920]=w&255;w=w>>8;a[234921|0]=w&255;w=w>>8;a[234922|0]=w&255;w=w>>8;a[234923|0]=w&255;w=1768515945;a[234924]=w&255;w=w>>8;a[234925|0]=w&255;w=w>>8;a[234926|0]=w&255;w=w>>8;a[234927|0]=w&255;a[234928]=100;a[234929]=110;uE(234930,111,5);a[234935]=10;a[234936]=5;w=1970632053;a[234937]=w&255;w=w>>8;a[234938|0]=w&255;w=w>>8;a[234939|0]=w&255;w=w>>8;a[234940|0]=w&255;a[234941]=121;a[234942]=32;a[234943]=121;f=0;j=0;do{j=((b[234432+(f<<1)>>1]|0)!=0&1)+j|0;f=f+1|0;}while((f|0)<127);aF(222,c[10030]|0);aF(1,c[10030]|0);aF(j&255|0,c[10030]|0);j=0;do{f=b[234432+(j<<1)>>1]|0;if(f<<16>>16!=0){g=(f&65535)>>>8;h=c[10030]|0;aF(j|0,h|0);h=g&65535;g=c[10030]|0;aF(h|0,g|0);g=f&255;f=c[10030]|0;aF(g|0,f|0);f=c[10030]|0;aF(1,f|0)}j=j+1|0;}while((j|0)<127);j=c[58772]|0;f=e|0;e=235096+(j<<2)|0;if((c[e>>2]|0)==0){be(f|0,180880,(v=i,i=i+16|0,c[v>>2]=180824,c[v+8>>2]=j,v)|0);g=c[10030]|0;aF(221,g|0);g=c[10030]|0;aF(j|0,g|0);g=c[10030]|0;aF(1,g|0);g=c[10030]|0;aF(1,g|0);g=c[10030]|0;aK(f|0,g|0);g=c[10030]|0;aF(0,g|0);c[e>>2]=j}e=j*5&-1;c[58770]=e;c[58744]=(e|0)/2&-1;c[58812]=(e|0)/3&-1;c[(c[3524]|0)+16>>2]=e;c[(c[3524]|0)+20>>2]=c[58744];aF(207,c[10030]|0);aF(j|0,c[10030]|0);aF(210,c[10030]|0);j=c[58744]|0;aF(j>>>8|0,c[10030]|0);aF(j|0,c[10030]|0);aF(208,c[10030]|0);j=c[58770]|0;aF(j>>>8|0,c[10030]|0);aF(j|0,c[10030]|0);if((c[58576]|0)!=0){tG()}if((c[58814]|0)==0){k=c[10030]|0;l=aF(135,k|0)|0;m=c[10030]|0;n=aF(0,m|0)|0;o=c[10030]|0;p=aF(0,o|0)|0;q=c[10030]|0;r=aF(137,q|0)|0;s=c[10030]|0;t=aF(0,s|0)|0;u=c[10030]|0;x=aF(0,u|0)|0;ph(-1);a[234312]=1;c[58576]=0;i=d;return}c[58814]=0;aF(206,c[10030]|0);aF(0,c[10030]|0);k=c[10030]|0;l=aF(135,k|0)|0;m=c[10030]|0;n=aF(0,m|0)|0;o=c[10030]|0;p=aF(0,o|0)|0;q=c[10030]|0;r=aF(137,q|0)|0;s=c[10030]|0;t=aF(0,s|0)|0;u=c[10030]|0;x=aF(0,u|0)|0;ph(-1);a[234312]=1;c[58576]=0;i=d;return}function pd(){if((c[58576]|0)!=0){tG()}aF(255,c[10030]|0);return}function pe(){var a=0,b=0,d=0,e=0,f=0,g=0,h=0,i=0;if((c[58576]|0)!=0){tG()}a=c[58580]|0;b=c[58574]|0;d=c[58572]|0;if((a|0)<(aa(d,b)|0)){e=a;f=b;g=d}else{if((c[58576]|0)!=0){tG()}d=c[10030]|0;aF(219,d|0);c[58580]=0;e=0;f=c[58574]|0;g=c[58572]|0}c[58580]=e+1;d=c[58758]|0;b=c[58742]|0;a=aa(b,(e|0)%(f|0)&-1)+d|0;d=c[58756]|0;h=c[58740]|0;i=aa((g-1|0)-((e|0)/(f|0)&-1)|0,h)+d|0;c[58754]=a+((b-(c[58738]|0)|0)/2&-1);c[58752]=i+((h-(c[58736]|0)|0)/2&-1);return}function pf(a,b){a=a|0;b=b|0;if((c[58576]|0)!=0){tG()}c[58472]=(c[58754]|0)+a;c[58372]=(c[58752]|0)+b;c[58576]=1;return}function pg(b,d){b=b|0;d=d|0;var e=0,f=0;e=c[58576]|0;c[233888+(e<<2)>>2]=(c[58754]|0)+b;c[233488+(e<<2)>>2]=(c[58752]|0)+d;f=e+1|0;c[58576]=f;if(f>>>0<(a[234312]|0?100:0)>>>0){return}tG();c[58472]=(c[58754]|0)+b;c[58372]=(c[58752]|0)+d;c[58576]=1;return}function ph(b){b=b|0;var d=0,e=0,f=0,g=0,h=0;if((c[58576]|0)!=0){tG()}d=(b|0)<-2?-2:b;if((d|0)==-2){e=4}else{b=((d|0)/8&-1)<<1;e=(b|0)<1?1:b}b=((d|0)%8&-1)+2|0;if((c[58766]|0)==(b|0)){return}c[58766]=b;aF(232,c[10030]|0);aF(e|0,c[10030]|0);aF(237,c[10030]|0);aF(0,c[10030]|0);aF(237,c[10030]|0);d=234328+(b*10&-1)|0;f=uA(d|0)|0;aF(f|0,c[10030]|0);f=a[d]|0;if(f<<24>>24==0){return}g=aa(f<<24>>24,e);aF(g>>>8|0,c[10030]|0);aF(g|0,c[10030]|0);if((uA(d|0)|0)>>>0>1){h=1}else{return}while(1){g=h+1|0;f=aa(a[234328+(b*10&-1)+h|0]|0,e);aF(f>>>8|0,c[10030]|0);aF(f|0,c[10030]|0);if(g>>>0<(uA(d|0)|0)>>>0){h=g}else{break}}return}function pi(a){a=a|0;if((c[58576]|0)!=0){tG()}if((c[58814]|0)==(a|0)){return 1}c[58814]=a;aF(206,c[10030]|0);aF(((a|0)==0?0:7)|0,c[10030]|0);return 1}function pj(a){a=a|0;if((c[58576]|0)!=0){tG()}c[58768]=a;return 1}function pk(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;b=i;d=c[13898]|0;e=c[8272]|0;L21144:do{if((d|0)<(e|0)){f=d;g=e;while(1){h=c[1054]|0;j=(a[h+(f*40&-1)|0]&1)==0;L21147:do{if(!j){k=c[h+(f*40&-1)+36>>2]|0;l=h+(f*40&-1)+32|0;n=c[10036]|0;o=0;while(1){if((o|0)>=(k|0)){break}if((a[n+((c[l>>2]|0)+o|0)|0]|0)==(a[o+103664|0]|0)){o=o+1|0}else{break L21147}}if((o|0)==1){break L21144}}}while(0);l=c[58164]|0;L21154:do{if((l|0)==0){p=232656}else{n=c[h+(f*40&-1)+36>>2]|0;k=c[h+(f*40&-1)+32>>2]|0;q=(n|0)>0;r=c[10036]|0;s=232656;t=l;while(1){L21158:do{if(!j){if(q){u=0;w=0;x=k;while(1){y=a[t+u|0]|0;if(y<<24>>24==(a[r+(u+x|0)|0]|0)){z=x;A=w}else{if(y<<24>>24!=36){break L21158}z=x-1|0;A=1}B=u+1|0;if((B|0)<(A+n|0)){u=B;w=A;x=z}else{break}}if((A|0)==0){C=B}else{p=s;break L21154}}else{C=0}x=a[t+C|0]|0;if((x<<24>>24|0)==36|(x<<24>>24|0)==0){p=s;break L21154}}}while(0);x=s+8|0;w=c[x>>2]|0;if((w|0)==0){p=x;break}else{s=x;t=w}}}}while(0);j=c[p+4>>2]|0;if((j|0)==0){a[34768]=1;l=f+1|0;c[13898]=l;D=l;E=g}else if((j|0)==3){a[34256]=0;l=f+1|0;c[13898]=l;D=l;E=g}else if((j|0)==2){a[34256]=1;l=f+1|0;c[13898]=l;D=l;E=g}else if((j|0)==1){a[34768]=0;j=f+1|0;c[13898]=j;D=j;E=g}else{aI(181208,337,1,c[m>>2]|0);j=(c[13898]|0)+1|0;c[13898]=j;D=j;E=c[8272]|0}if((D|0)<(E|0)){f=D;g=E}else{break}}}}while(0);E=a[34256]|0?181144:181176;be(13048,84616,(v=i,i=i+16|0,c[v>>2]=a[34768]|0?116408:181192,c[v+8>>2]=E,v)|0);i=b;return}function pl(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0;f=i;if((c[58576]|0)!=0){tG()}g=c[58810]|0;if((g|0)!=0){uu(g)}g=(uA(e|0)|0)+2|0;h=ut(g)|0;do{if((h|0)==0){gk();j=ut(g)|0;if((j|0)!=0){k=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=180928,v)|0)}else{k=h}}while(0);c[58810]=k;h=a[e]|0;if(h<<24>>24==0){l=k;m=1}else{g=e;e=k;k=1;j=h;while(1){h=j&255;do{if((h|0)==92){n=g+1|0;o=a[n]|0;if(o<<24>>24!=92){p=n;q=o;r=15734;break}a[e]=10;s=k+1|0;t=n}else if((h|0)==32){a[e]=-128;s=k;t=g}else{p=g;q=j;r=15734}}while(0);if((r|0)==15734){r=0;a[e]=a[234688+(q&255)|0]|0;s=k;t=p}h=e+1|0;n=t+1|0;o=a[n]|0;if(o<<24>>24==0){l=h;m=s;break}else{g=n;e=h;k=s;j=o}}}a[l]=10;a[l+1|0]=0;l=(c[58814]|0)==0;j=aa(c[58770]|0,m);m=c[58810]|0;if(l){u=m;w=b;x=(((j|0)/2&-1)+d|0)-(c[58770]|0)|0}else{u=m;w=(((j|0)/-2&-1)+b|0)+(c[58770]|0)|0;x=d}L21204:while(1){d=u;b=w;while(1){j=a8(d|0,10)|0;if((j|0)==0){break L21204}a[j]=0;m=(c[58814]|0)==0;l=c[58812]|0;s=b-(m?0:l)|0;k=(m?l:0)+x|0;l=uA(d|0)|0;e=aa(c[58744]|0,l);l=c[58768]|0;do{if((l|0)==1){g=(e|0)/2&-1;if(m){y=k;z=b-g|0;break}else{y=x-g|0;z=s;break}}else if((l|0)==2){if(m){y=k;z=b-e|0;break}else{y=x-e|0;z=s;break}}else{y=k;z=s}}while(0);aF(135,c[10030]|0);s=(c[58754]|0)+z|0;aF(s>>>8|0,c[10030]|0);aF(s|0,c[10030]|0);aF(137,c[10030]|0);s=(c[58752]|0)+y|0;aF(s>>>8|0,c[10030]|0);aF(s|0,c[10030]|0);aF(209,c[10030]|0);s=c[58754]|0;if((c[58814]|0)==1){k=s+y|0;e=k>>>8;m=c[10030]|0;aF(e|0,m|0);m=c[10030]|0;aF(k|0,m|0)}else{m=s+z|0;s=m>>>8;k=c[10030]|0;aF(s|0,k|0);k=c[10030]|0;aF(m|0,k|0)}aK(d|0,c[10030]|0);A=j+1|0;B=c[58770]|0;if((c[58814]|0)==0){break}else{d=A;b=B+b|0}}u=A;w=b;x=x-B|0}i=f;return}function pm(a){a=a|0;c[8546]=a;return 1}function pn(){var b=0,d=0,e=0,f=0,h=0;b=i;if(a[34544]|a[34552]){i=b;return}a[34544]=1;a[34552]=0;d=c[8619]|0;if((d|0)==34464){e=1;f=0}else{h=d;do{d=c[h+12>>2]|0;c[8619]=d;c[d+8>>2]=34464;uu(h);h=c[8619]|0;}while((h|0)!=34464);e=a[34544]|0;f=a[34552]|0}c[8619]=34464;c[8618]=34464;g[8616]=0.0;g[8617]=9.99899959564209;a[34336]=0;a[34272]=0;a[34192]=0;if(e&f){uD(34480,183216,21)}uE(34560,0,160);cf(c[10030]|0,181648,(v=i,i=i+48|0,c[v>>2]=40152,c[v+8>>2]=40160,c[v+16>>2]=72,c[v+24>>2]=9,c[v+32>>2]=40,c[v+40>>2]=56,v)|0);i=b;return}function po(){if(!(a[34544]&(a[34552]^1))){return}aI(182144,19,1,c[10030]|0);a[34544]=0;c[8680]=-1;return}function pp(){var b=0,d=0,e=0,f=0;b=i;if(!(a[34544]&a[34552])){i=b;return}if(!(a[34256]|0)){tF()}aI(182248,117,1,c[10030]|0);d=0;do{e=c[34564+(d<<3)>>2]|0;do{if((e|0)!=0){if((c[34560+(d<<3)>>2]|0)!=1){break}f=c[10030]|0;cf(f|0,182216,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}}while(0);d=d+1|0;}while((d|0)<20);cf(c[10030]|0,182168,(v=i,i=i+8|0,c[v>>2]=c[8680],v)|0);a[34552]=0;i=b;return}function pq(){var b=0,d=0,e=0,f=0,j=0;b=i;if(!(a[34544]&(a[34552]^1))){i=b;return}a[34552]=1;c[8680]=(c[8680]|0)+1;d=c[8619]|0;if((d|0)==34464){e=1;f=1}else{j=d;do{d=c[j+12>>2]|0;c[8619]=d;c[d+8>>2]=34464;uu(j);j=c[8619]|0;}while((j|0)!=34464);e=a[34544]|0;f=a[34552]|0}c[8619]=34464;c[8618]=34464;g[8616]=0.0;g[8617]=9.99899959564209;a[34336]=0;a[34272]=0;a[34192]=0;if(e&f){uD(34480,183216,21)}uE(34560,0,160);f=c[8680]|0;cf(c[10030]|0,182384,(v=i,i=i+32|0,c[v>>2]=f,h[v+8>>3]=+(f|0)*10.1,h[v+16>>3]=15.0,h[v+24>>3]=10.0,v)|0);i=b;return}function pr(b,c){b=b|0;c=c|0;if(!(a[34544]&a[34552])){return}if(!(a[34256]|0)){tF()}g[8616]=+(b>>>0>>>0)/1.0e3;g[8617]=+(9999-c|0)/1.0e3;return}function ps(b){b=b|0;var d=0,e=0,f=0;d=i;if(!(a[34544]&a[34552])){i=d;return}if(!(a[34256]|0)){tF()}do{if((b|0)<0){do{if((b|0)==-1){c[8566]=24;if(a[34768]|0){break}uD(34192,182904,17)}else{c[8566]=16;if(a[34768]|0){break}uD(34192,182904,17)}}while(0);uD(34272,182856,20);uD(34120,182824,25)}else{e=(b|0)%16&-1;c[8566]=e;uD(34272,182800,20);if(a[34768]|0){break}f=((e|0)%6&-1)+2|0;be(34192,182776,(v=i,i=i+8|0,c[v>>2]=f,v)|0);if(a[34768]|0){break}f=((c[8566]|0)%6&-1)+2|0;be(34120,182664,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}}while(0);be(34336,182648,(v=i,i=i+8|0,c[v>>2]=c[34400+(((c[8566]|0)%16&-1)<<2)>>2],v)|0);i=d;return}function pt(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0.0,m=0,n=0,o=0;f=i;if(!(a[34544]&a[34552])){i=f;return}if(a[34256]|0){j=1;k=1}else{tF();j=a[34544]|0;k=a[34552]|0}if(j&k){if(!(a[34256]|0)){tF()}g[8616]=+(b>>>0>>>0)/1.0e3;g[8617]=+(10063-d|0)/1.0e3}if((a[e]|0)==0){i=f;return}cf(c[10030]|0,183024,(v=i,i=i+40|0,c[v>>2]=(c[8566]|0)+1,c[v+8>>2]=34336,c[v+16>>2]=34272,c[v+24>>2]=34192,c[v+32>>2]=34120,v)|0);if((c[8682]|0)!=72){d=c[10030]|0;cf(d|0,183328,(v=i,i=i+8|0,c[v>>2]=72,v)|0);c[8682]=72}l=+g[8617];d=c[8546]|0;cf(c[10030]|0,182960,(v=i,i=i+40|0,h[v>>3]=+g[8616],h[v+8>>3]=l,c[v+16>>2]=34480,c[v+24>>2]=d,c[v+32>>2]=e,v)|0);e=(c[8566]|0)+1|0;L21319:do{if(a[34544]&a[34552]){d=0;while(1){if((d|0)>=20){break L21319}m=34564+(d<<3)|0;b=c[m>>2]|0;n=(b|0)==0;o=(b|0)==(e|0);if(n|o){break}else{d=d+1|0}}b=34560+(d<<3)|0;if(n){c[m>>2]=e;c[b>>2]=0;break}if(!o){break}c[b>>2]=1}}while(0);a[34336]=0;a[34272]=0;a[34192]=0;a[34480]=0;i=f;return}function pu(b){b=b|0;var c=0;do{if(a[34544]&a[34552]){if((b|0)==0){uD(34480,183216,21);c=1;break}else if((b|0)==2){uD(34480,183136,22);c=1;break}else if((b|0)==1){uD(34480,183184,23);c=1;break}else{c=0;break}}else{c=0}}while(0);return c|0}function pv(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0,s=0,t=0;f=i;if((e|0)<0){j=-1}else{j=(e|0)%6&-1}if(!(a[34544]&a[34552])){i=f;return}if(a[34256]|0){k=1;l=1}else{tF();k=a[34544]|0;l=a[34552]|0}if(k&l){if(a[34256]|0){m=1;n=1}else{tF();m=a[34544]|0;n=a[34552]|0}g[8616]=+(b>>>0>>>0)/1.0e3;g[8617]=+(9999-d|0)/1.0e3;o=m;p=n}else{o=k;p=l}if(o&p){uD(34480,183184,23)}cf(c[10030]|0,183424,(v=i,i=i+16|0,c[v>>2]=(c[8566]|0)+1,c[v+8>>2]=34120,v)|0);p=c[33548+(j<<4)>>2]|0;if((c[8682]|0)!=(p|0)){o=c[10030]|0;cf(o|0,183328,(v=i,i=i+8|0,c[v>>2]=p,v)|0);c[8682]=p}q=+g[8617]+ +g[33544+(j<<4)>>2];p=c[33536+(j<<4)>>2]|0;cf(c[10030]|0,183368,(v=i,i=i+32|0,h[v>>3]=+g[8616]+ +g[33540+(j<<4)>>2],h[v+8>>3]=q,c[v+16>>2]=34480,c[v+24>>2]=p,v)|0);aI(183360,3,1,c[10030]|0);p=(c[8566]|0)+1|0;L21360:do{if(a[34544]&a[34552]){j=0;while(1){if((j|0)>=20){break L21360}r=34564+(j<<3)|0;o=c[r>>2]|0;s=(o|0)==0;t=(o|0)==(p|0);if(s|t){break}else{j=j+1|0}}o=34560+(j<<3)|0;if(s){c[r>>2]=p;c[o>>2]=0;break}if(!t){break}c[o>>2]=1}}while(0);a[34480]=0;i=f;return}function pw(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;j=i;k=b&15;if((k|0)==1){l=0}else if((k|0)==2){l=c[34736+(((b>>4|0)%8&-1)<<2)>>2]|0}else{l=7}cf(c[10030]|0,183520,(v=i,i=i+16|0,c[v>>2]=(c[8566]|0)+1,c[v+8>>2]=34192,v)|0);cf(c[10030]|0,183696,(v=i,i=i+8|0,c[v>>2]=l,v)|0);cf(c[10030]|0,183464,(v=i,i=i+32|0,h[v>>3]=+(d>>>0>>>0)/1.0e3,h[v+8>>3]=+(((9999-e|0)-g|0)>>>0>>>0)/1.0e3,h[v+16>>3]=+(f>>>0>>>0)/1.0e3,h[v+24>>3]=+(g>>>0>>>0)/1.0e3,v)|0);aI(183600,4,1,c[10030]|0);g=(c[8566]|0)+1|0;if(a[34544]&a[34552]){m=0}else{i=j;return}while(1){if((m|0)>=20){n=15879;break}o=34564+(m<<3)|0;f=c[o>>2]|0;p=(f|0)==0;q=(f|0)==(g|0);if(p|q){break}else{m=m+1|0}}if((n|0)==15879){i=j;return}n=34560+(m<<3)|0;if(p){c[o>>2]=g;c[n>>2]=0;i=j;return}if(!q){i=j;return}c[n>>2]=1;i=j;return}function px(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0.0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;e=i;cf(c[10030]|0,183712,(v=i,i=i+8|0,c[v>>2]=(c[8566]|0)+1,v)|0);cf(c[10030]|0,183696,(v=i,i=i+8|0,c[v>>2]=0,v)|0);cf(c[10030]|0,183648,(v=i,i=i+8|0,c[v>>2]=b,v)|0);f=c[10030]|0;if((b|0)>0){g=0;j=f;while(1){k=+(9999-(c[d+(g*12&-1)+4>>2]|0)|0)/1.0e3;cf(j|0,183624,(v=i,i=i+16|0,h[v>>3]=+(c[d+(g*12&-1)>>2]|0)/1.0e3,h[v+8>>3]=k,v)|0);l=g+1|0;m=c[10030]|0;if((l|0)<(b|0)){g=l;j=m}else{n=m;break}}}else{n=f}aI(183600,4,1,n|0);n=(c[8566]|0)+1|0;if(a[34544]&a[34552]){o=0}else{i=e;return}while(1){if((o|0)>=20){p=15896;break}q=34564+(o<<3)|0;f=c[q>>2]|0;r=(f|0)==0;s=(f|0)==(n|0);if(r|s){break}else{o=o+1|0}}if((p|0)==15896){i=e;return}p=34560+(o<<3)|0;if(r){c[q>>2]=n;c[p>>2]=0;i=e;return}if(!s){i=e;return}c[p>>2]=1;i=e;return}function py(b,d){b=b|0;d=d|0;var e=0,f=0.0,h=0.0,j=0;e=i;if(!(a[34544]&a[34552])){i=e;return}f=+(b>>>0>>>0)/1.0e3;h=+(9999-d|0)/1.0e3;d=ut(16)|0;do{if((d|0)==0){gk();b=ut(16)|0;if((b|0)!=0){j=b;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=182608,v)|0)}else{j=d}}while(0);c[(c[8619]|0)+8>>2]=j;c[(c[(c[8619]|0)+8>>2]|0)+8>>2]=34464;j=c[8619]|0;c[(c[j+8>>2]|0)+12>>2]=j;j=c[(c[8619]|0)+8>>2]|0;c[8619]=j;g[j>>2]=f;g[(c[8619]|0)+4>>2]=h;if(!(a[34256]|0)){i=e;return}tF();i=e;return}function pz(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0,aZ=0,a_=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0.0,bf=0.0,bg=0.0,bi=0.0,bj=0.0,bk=0.0,bl=0.0,bm=0,bn=0,bo=0,bp=0,bq=0.0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0,bA=0,bB=0,bC=0,bD=0,bE=0,bF=0,bG=0,bH=0,bI=0,bJ=0,bK=0,bL=0,bM=0,bN=0,bO=0,bP=0,bQ=0,bR=0,bS=0,bT=0,bU=0,bV=0,bW=0,bX=0,bY=0,bZ=0,b_=0,b$=0,b0=0,b1=0,b2=0,b3=0,b4=0,b5=0,b6=0,b7=0,b8=0,b9=0,cb=0,cc=0,cd=0,ce=0,cf=0,cg=0,ch=0,ci=0,cj=0,ck=0,cl=0,cm=0,cn=0,co=0,cp=0,cq=0,cr=0,cs=0,ct=0,cu=0,cv=0,cw=0,cx=0,cz=0,cA=0,cB=0,cC=0,cD=0,cE=0,cF=0,cG=0,cH=0,cI=0.0,cJ=0.0,cK=0,cL=0;b=i;i=i+1272|0;d=b|0;e=b+24|0;f=b+48|0;j=b+72|0;k=b+96|0;l=b+120|0;m=b+144|0;n=b+168|0;o=b+192|0;p=b+1248|0;q=b+1256|0;r=b+1264|0;s=b+216|0;uE(s|0,0,1025);if((c[13898]|0)!=2){a[28824]=0}t=c[3524]|0;u=c[t>>2]|0;do{if((aY(u|0,168936)|0)==0){c[7118]=28e3;w=28e3}else{if((aY(u|0,169984)|0)==0){c[7118]=28304;w=28304;break}if((aY(u|0,170664)|0)==0){c[7118]=43680;w=43680;break}else{c[7118]=30304;w=30304;break}}}while(0);if((c[w>>2]|0)==3){w=c[7112]|0;if((w|0)!=0){uu(w)}c[7112]=0}else{c[t+88>>2]=90}t=c[13898]|0;w=c[8272]|0;L21446:do{if((t|0)<(w|0)){u=c[1054]|0;x=(a[u+(t*40&-1)|0]&1)==0;L21448:do{if(!x){y=c[u+(t*40&-1)+36>>2]|0;z=u+(t*40&-1)+32|0;A=c[10036]|0;B=0;while(1){if((B|0)>=(y|0)){break}if((a[A+((c[z>>2]|0)+B|0)|0]|0)==(a[B+103664|0]|0)){B=B+1|0}else{break L21448}}if((B|0)==1){C=t;D=w;break L21446}}}while(0);z=c[57330]|0;L21455:do{if((z|0)==0){E=229320}else{A=c[u+(t*40&-1)+36>>2]|0;y=c[u+(t*40&-1)+32>>2]|0;F=(A|0)>0;G=c[10036]|0;H=229320;I=z;while(1){L21459:do{if(!x){if(F){J=0;K=0;L=y;while(1){M=a[I+J|0]|0;if(M<<24>>24==(a[G+(J+L|0)|0]|0)){N=L;O=K}else{if(M<<24>>24!=36){break L21459}N=L-1|0;O=1}Q=J+1|0;if((Q|0)<(O+A|0)){J=Q;K=O;L=N}else{break}}if((O|0)==0){R=Q}else{E=H;break L21455}}else{R=0}L=a[I+R|0]|0;if((L<<24>>24|0)==36|(L<<24>>24|0)==0){E=H;break L21455}}}while(0);L=H+8|0;K=c[L>>2]|0;if((K|0)==0){E=L;break}else{H=L;I=K}}}}while(0);if((c[E+4>>2]|0)!=3){C=t;D=w;break}x=c[7118]|0;z=c[x>>2]|0;if((z|0)==2){uD(x|0,43536,144)}else if((z|0)==0){uD(x|0,27856,144)}else if((z|0)==3){u=x+32|0;I=c[u>>2]|0;if((I|0)==0){S=x}else{H=u;u=I;while(1){c[H>>2]=c[u>>2];uu(c[u+4>>2]|0);uu(c[u+8>>2]|0);uu(c[u+12>>2]|0);uu(u);I=c[7118]|0;A=I+32|0;G=c[A>>2]|0;if((G|0)==0){S=I;break}else{H=A;u=G}}}uD(S|0,30160,144)}else if((z|0)==1){uD(x|0,28160,144)}u=(c[3524]|0)+96|0;c[u>>2]=c[u>>2]&-33;u=(c[13898]|0)+1|0;c[13898]=u;H=c[8272]|0;if((u|0)>=(H|0)){C=u;D=H;break}G=c[1054]|0;if((a[G+(u*40&-1)|0]&1)==0){T=c[3524]|0;U=T|0;V=c[U>>2]|0;uf(u,134056,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}A=c[G+(u*40&-1)+36>>2]|0;I=G+(u*40&-1)+32|0;G=c[10036]|0;y=0;while(1){if((y|0)>=(A|0)){break}if((a[G+((c[I>>2]|0)+y|0)|0]|0)==(a[y+103664|0]|0)){y=y+1|0}else{W=16364;break}}if((W|0)==16364){T=c[3524]|0;U=T|0;V=c[U>>2]|0;uf(u,134056,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}if((y|0)==1){C=u;D=H;break}T=c[3524]|0;U=T|0;V=c[U>>2]|0;uf(u,134056,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}else{C=t;D=w}}while(0);L21495:do{if((C|0)<(D|0)){w=e|0;t=e+8|0;V=d|0;U=d+8|0;T=o|0;S=o+8|0;E=n|0;R=n+8|0;Q=m|0;O=m+8|0;N=l|0;I=l+8|0;G=k|0;A=k+8|0;x=j|0;z=j+8|0;F=f|0;B=f+8|0;K=0;L=0;J=0;M=0;X=0;Y=0;Z=0;_=0;$=0;aa=0;ab=0;ac=0;ad=0;ae=0;af=0;ag=0;ah=0;ai=0;aj=0;ak=0;al=C;am=D;L21497:while(1){an=c[1054]|0;ao=(a[an+(al*40&-1)|0]&1)==0;L21499:do{if(!ao){ap=c[an+(al*40&-1)+36>>2]|0;aq=an+(al*40&-1)+32|0;ar=c[10036]|0;as=0;while(1){if((as|0)>=(ap|0)){break}if((a[ar+((c[aq>>2]|0)+as|0)|0]|0)==(a[as+103664|0]|0)){as=as+1|0}else{break L21499}}if((as|0)==1){break L21495}}}while(0);aq=c[57330]|0;L21506:do{if((aq|0)==0){at=229320}else{ar=c[an+(al*40&-1)+36>>2]|0;ap=c[an+(al*40&-1)+32>>2]|0;au=(ar|0)>0;av=c[10036]|0;aw=229320;ax=aq;while(1){L21510:do{if(!ao){if(au){ay=0;az=0;aA=ap;while(1){aB=a[ax+ay|0]|0;if(aB<<24>>24==(a[av+(ay+aA|0)|0]|0)){aC=aA;aD=az}else{if(aB<<24>>24!=36){break L21510}aC=aA-1|0;aD=1}aE=ay+1|0;if((aE|0)<(aD+ar|0)){ay=aE;az=aD;aA=aC}else{break}}if((aD|0)==0){aF=aE}else{at=aw;break L21506}}else{aF=0}aA=a[ax+aF|0]|0;if((aA<<24>>24|0)==36|(aA<<24>>24|0)==0){at=aw;break L21506}}}while(0);aA=aw+8|0;az=c[aA>>2]|0;if((az|0)==0){at=aA;break}else{aw=aA;ax=az}}}}while(0);ao=c[at+4>>2]|0;L21523:do{if((ao|0)==12){if(aa){W=16377;break L21497}aq=c[7118]|0;if(((c[aq>>2]|0)-2|0)>>>0>=2){W=16378;break L21497}a[aq+18|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=1;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==1){if(ak){W=16375;break L21497}aq=c[7118]|0;if((c[aq>>2]|0)!=3){W=16376;break L21497}c[aq+12>>2]=2;c[13898]=(c[13898]|0)+1;aG=1;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==10){if(J){W=16058;break L21497}a[(c[7118]|0)+17|0]=1;aq=(c[3524]|0)+96|0;c[aq>>2]=c[aq>>2]&-1025;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=1;aZ=L;a_=K}else if((ao|0)==24){c[p>>2]=0;a$=al+1|0;c[13898]=a$;if((c[c[7118]>>2]|0)!=3){W=16139;break L21497}aq=a[an+(a$*40&-1)|0]|0;L21534:do{if((am|0)>(a$|0)){if((aq&1)==0){W=16145;break}ax=c[an+(a$*40&-1)+36>>2]|0;aw=an+(a$*40&-1)+32|0;ar=c[10036]|0;av=0;while(1){if((av|0)>=(ax|0)){break}if((a[ar+((c[aw>>2]|0)+av|0)|0]|0)==(a[av+186440|0]|0)){av=av+1|0}else{W=16145;break L21534}}if((av|0)!=3){a0=ax;a1=aw;W=16148;break}ar=al+2|0;c[13898]=ar;a2=0;a3=ar}else{W=16145}}while(0);if((W|0)==16145){W=0;a0=c[an+(a$*40&-1)+36>>2]|0;a1=an+(a$*40&-1)+32|0;W=16148}L21544:do{if((W|0)==16148){W=0;if(!((aq&1)!=0&(a0|0)>0)){a2=0;a3=a$;break}ar=c[10036]|0;ap=0;au=0;as=c[a1>>2]|0;while(1){if((a[ap+186432|0]|0)==(a[ar+(ap+as|0)|0]|0)){a4=as;a5=au}else{if((ap|0)!=3){a2=0;a3=a$;break L21544}a4=as-1|0;a5=1}az=ap+1|0;if((az|0)<(a5+a0|0)){ap=az;au=a5;as=a4}else{break}}if((a5|0)==0){if(!((ap|0)==2|(ap|0)==6)){a2=0;a3=a$;break}}as=al+2|0;c[13898]=as;a2=1;a3=as}}while(0);if((a3|0)>=(am|0)){W=16165;break L21497}L21559:do{if((a[an+(a3*40&-1)|0]&1)!=0){aq=c[an+(a3*40&-1)+36>>2]|0;as=an+(a3*40&-1)+32|0;au=c[10036]|0;ar=0;while(1){if((ar|0)>=(aq|0)){break}if((a[au+((c[as>>2]|0)+ar|0)|0]|0)==(a[ar+103664|0]|0)){ar=ar+1|0}else{break L21559}}if((ar|0)==1){W=16165;break L21497}}}while(0);a[14176]=1;is(k);a[14176]=0;if((c[G>>2]|0)!=3){W=16164;break L21497}as=c[A>>2]|0;c[p>>2]=as;if((as|0)==0){W=16167;break L21497}as=c[(c[7118]|0)+32>>2]|0;au=ut(16)|0;if((au|0)==0){gk();aq=ut(16)|0;if((aq|0)==0){W=16171;break L21497}else{a6=aq}}else{a6=au}au=a6;aq=c[(c[1054]|0)+((c[13898]|0)*40&-1)+36>>2]|0;ap=ut(aq)|0;if((ap|0)==0){gk();aw=ut(aq)|0;if((aw|0)==0){W=16174;break L21497}else{a7=aw}}else{a7=ap}a8=a6+4|0;c[a8>>2]=a7;iQ(p);ap=c[p>>2]|0;c[a8>>2]=ap;c[a6+12>>2]=0;do{if(a2){c[a6>>2]=0}else{if((a[ap]|0)==60){c[a6+8>>2]=0;c[a6>>2]=0}else{aw=hB(ap)|0;c[a6+8>>2]=aw;if((aw|0)==0){W=16178;break L21497}c[a6>>2]=0}aw=c[8804]|0;if((aw|0)==0){W=16184}else{aq=aw;while(1){aw=c[aq+48>>2]|0;if((aw|0)==0){break}else{aq=aw}}if((a[aq+12|0]&1)==0){W=16184}}if((W|0)==16184){W=0;if((a[37400]&1)==0){break}}tD(au,0)}}while(0);ap=(c[7118]|0)+32|0;if((c[ap>>2]|0)==0){if(a2){uh((c[13898]|0)-1|0,186216,(v=i,i=i+8|0,c[v>>2]=c[a8>>2],v)|0);aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}else{c[ap>>2]=au;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}}do{if((as|0)==0){a9=0}else{ar=c[a8>>2]|0;aw=a2^1;ax=0;av=as;az=0;while(1){ba=av+4|0;aA=(aY(c[ba>>2]|0,ar|0)|0)!=0;if(!(aA|aw)){break}bb=ax|aA^1;aA=c[av>>2]|0;if((aA|0)==0){W=16196;break}else{ax=bb;az=av;av=aA}}if((W|0)==16196){W=0;if(bb){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break L21523}else{a9=av;break}}ax=c[av>>2]|0;if((az|0)==0){c[ap>>2]=ax}else{c[az>>2]=ax}uu(c[ba>>2]|0);uu(c[av+8>>2]|0);uu(c[av+12>>2]|0);uu(av);aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break L21523}}while(0);if(a2){uh((c[13898]|0)-1|0,186216,(v=i,i=i+8|0,c[v>>2]=c[a8>>2],v)|0);aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}else{c[a9>>2]=au;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}}else if((ao|0)==13){if(M){W=16069;break L21497}a[(c[7118]|0)+19|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=1;aX=J;aZ=L;a_=K}else if((ao|0)==33){if(ae){W=16332;break L21497}ap=c[7118]|0;if(((c[ap>>2]|0)-2|0)>>>0<2){W=16333;break L21497}a[ap+96|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=1;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==34){if(ae){W=16373;break L21497}ap=c[7118]|0;if(((c[ap>>2]|0)-2|0)>>>0<2){W=16374;break L21497}a[ap+96|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=1;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==21){if(Z){W=16112;break L21497}a[(c[7118]|0)+30|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=1;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==23){if(_){W=16118;break L21497}a[(c[7118]|0)+31|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=1;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==22){if(_){W=16115;break L21497}a[(c[7118]|0)+31|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=1;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==25){ap=c[7118]|0;if((c[ap>>2]|0)!=3){W=16205;break L21497}as=ap+32|0;ap=c[as>>2]|0;if((ap|0)==0){bc=al}else{ax=as;as=ap;do{c[ax>>2]=c[as>>2];uu(c[as+4>>2]|0);uu(c[as+8>>2]|0);uu(c[as+12>>2]|0);uu(as);ax=(c[7118]|0)+32|0;as=c[ax>>2]|0;}while((as|0)!=0);bc=c[13898]|0}c[13898]=bc+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==41){a[(c[7118]|0)+114|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==42){c[13898]=al+1;as=hK()|0;ax=c[7118]|0;h[ax+120>>3]=+(as>>>16&255|0)/255.0;h[ax+128>>3]=+(as>>>8&255|0)/255.0;h[ax+136>>3]=+(as&255|0)/255.0;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==17){if(K){W=16338;break L21497}as=c[7118]|0;if((c[as>>2]|0)!=3){W=16339;break L21497}a[as+29|0]=0;a[(c[7118]|0)+28|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=1}else if((ao|0)==5){if(aj){W=16344;break L21497}if((c[c[7118]>>2]|0)!=3){W=16345;break L21497}c[(c[3524]|0)+68>>2]=116;c[(c[3524]|0)+88>>2]=90;as=(c[3524]|0)+96|0;c[as>>2]=c[as>>2]&-33;c[13898]=(c[13898]|0)+1;aG=ak;aH=1;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==2){if(ak){W=16334;break L21497}as=c[7118]|0;if((c[as>>2]|0)!=3){W=16335;break L21497}c[as+12>>2]=0;c[13898]=(c[13898]|0)+1;aG=1;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==19){if(K){W=16348;break L21497}as=c[7118]|0;if((c[as>>2]|0)!=3){W=16349;break L21497}a[as+28|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=1}else if((ao|0)==20){if(Z){W=16109;break L21497}a[(c[7118]|0)+30|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=1;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==28){if(L){W=15990;break L21497}a[(c[7118]|0)+16|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=1;a_=K}else if((ao|0)==3){W=15992;break L21497}else if((ao|0)==4){if(aj){W=16347;break L21497}if((c[c[7118]>>2]|0)!=3){W=16346;break L21497}c[(c[3524]|0)+68>>2]=86;c[(c[3524]|0)+88>>2]=126;as=(c[3524]|0)+96|0;c[as>>2]=c[as>>2]|32;c[13898]=(c[13898]|0)+1;aG=ak;aH=1;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==38){if(ac){W=16379;break L21497}if((c[c[7118]>>2]|0)!=2){W=16380;break L21497}uu(c[10956]|0);c[10956]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=1;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==16){if(Y){W=16086;break L21497}c[13898]=al+1;as=is(m)|0;ax=c[as>>2]|0;if((ax|0)==1){bd=+(c[as+8>>2]|0)}else if((ax|0)==2){bd=+h[as+8>>3]}else if((ax|0)==3){bd=+uz(c[as+8>>2]|0,0)}else{W=16091;break L21497}if((c[Q>>2]|0)==3){uu(c[O>>2]|0);c[Q>>2]=1}g[(c[7118]|0)+24>>2]=bd;as=(c[7118]|0)+24|0;if(+g[as>>2]>0.0){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=1;aV=X;aW=M;aX=J;aZ=L;a_=K;break}g[as>>2]=1.0;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=1;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==30){if(ai){W=16121;break L21497}as=al+1|0;c[13898]=as;L21670:do{if((as|0)<(am|0)){L21672:do{if((a[an+(as*40&-1)|0]&1)!=0){ax=c[an+(as*40&-1)+36>>2]|0;au=an+(as*40&-1)+32|0;ap=c[10036]|0;aw=0;while(1){if((aw|0)>=(ax|0)){break}if((a[ap+((c[au>>2]|0)+aw|0)|0]|0)==(a[aw+103664|0]|0)){aw=aw+1|0}else{break L21672}}if((aw|0)==1){bf=-1.0;break L21670}}}while(0);av=is(l)|0;az=c[av>>2]|0;if((az|0)==3){bg=+uz(c[av+8>>2]|0,0)}else if((az|0)==1){bg=+(c[av+8>>2]|0)}else if((az|0)==2){bg=+h[av+8>>3]}else{W=16132;break L21497}if((c[N>>2]|0)==3){uu(c[I>>2]|0);c[N>>2]=1}bf=bg}else{bf=-1.0}}while(0);g[(c[7118]|0)+92>>2]=bf;as=(c[7118]|0)+92|0;if(+g[as>>2]>=0.0){aG=ak;aH=aj;aI=1;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}g[as>>2]=1.0;aG=ak;aH=aj;aI=1;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==43){c[13898]=al+1;as=c[7118]|0;h[as+136>>3]=-1.0;h[as+128>>3]=-1.0;h[as+120>>3]=-1.0;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==26){if($){W=16214;break L21497}c[13898]=al+1;as=is(j)|0;av=c[as>>2]|0;if((av|0)==1){bi=+(c[as+8>>2]|0)}else if((av|0)==2){bi=+h[as+8>>3]}else if((av|0)==3){bi=+uz(c[as+8>>2]|0,0)}else{W=16219;break L21497}if((c[x>>2]|0)==3){uu(c[z>>2]|0);c[x>>2]=1}c[(c[7118]|0)+100>>2]=~~bi;as=(c[7118]|0)+100|0;if((c[as>>2]|0)<2){c[as>>2]=2}as=c[13898]|0;if((as|0)>=(c[8272]|0)){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=1;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}av=c[1054]|0;az=(a[av+(as*40&-1)|0]&1)==0;if(az){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=1;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}au=c[av+(as*40&-1)+36>>2]|0;ap=av+(as*40&-1)+32|0;ax=c[10036]|0;ar=0;while(1){if((ar|0)>=(au|0)){W=16229;break}if((a[ax+((c[ap>>2]|0)+ar|0)|0]|0)==(a[ar+103664|0]|0)){ar=ar+1|0}else{break}}if((W|0)==16229){W=0;if((ar|0)==1){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=1;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}}if(az){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=1;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}ap=c[av+(as*40&-1)+36>>2]|0;ax=av+(as*40&-1)+32|0;au=c[10036]|0;aq=0;while(1){if((aq|0)>=(ap|0)){break}if((a[au+((c[ax>>2]|0)+aq|0)|0]|0)==(a[aq+148464|0]|0)){aq=aq+1|0}else{aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=1;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break L21523}}if((aq|0)!=1){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=1;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break}c[13898]=as+1;ax=is(f)|0;au=c[ax>>2]|0;if((au|0)==1){bj=+(c[ax+8>>2]|0)}else if((au|0)==2){bj=+h[ax+8>>3]}else if((au|0)==3){bj=+uz(c[ax+8>>2]|0,0)}else{W=16239;break L21497}if((c[F>>2]|0)==3){uu(c[B>>2]|0);c[F>>2]=1}bk=+P(+bj);h[(c[7118]|0)+104>>3]=bk;if(bk<1.0){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=1;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else{W=16243;break L21497}}else if((ao|0)==14){if(M){W=16072;break L21497}a[(c[7118]|0)+19|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=1;aX=J;aZ=L;a_=K}else if((ao|0)==15){if(X){W=16075;break L21497}c[13898]=al+1;ax=is(n)|0;au=c[ax>>2]|0;if((au|0)==1){bl=+(c[ax+8>>2]|0)}else if((au|0)==2){bl=+h[ax+8>>3]}else if((au|0)==3){bl=+uz(c[ax+8>>2]|0,0)}else{W=16080;break L21497}if((c[E>>2]|0)==3){uu(c[R>>2]|0);c[E>>2]=1}g[(c[7118]|0)+20>>2]=bl;ax=(c[7118]|0)+20|0;if(+g[ax>>2]>0.0){aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=1;aW=M;aX=J;aZ=L;a_=K;break}g[ax>>2]=1.0;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=1;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==35){if(af){W=16352;break L21497}ax=c[7118]|0;if((c[ax>>2]|0)==3){W=16353;break L21497}a[ax+112|0]=1;ax=c[7118]|0;if((c[ax>>2]|0)==2){a[ax+30|0]=1}c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=1;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==18){if(K){W=16340;break L21497}ax=c[7118]|0;if((c[ax>>2]|0)!=3){W=16341;break L21497}a[ax+29|0]=1;a[(c[7118]|0)+28|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=1}else if((ao|0)==31){if(ad){W=16343;break L21497}ax=c[7118]|0;if((c[ax>>2]|0)>>>0>=2){W=16342;break L21497}a[ax+97|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=1;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==9){if(J){W=16055;break L21497}a[(c[7118]|0)+17|0]=0;ax=(c[3524]|0)+96|0;c[ax>>2]=c[ax>>2]|1024;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=1;aZ=L;a_=K}else if((ao|0)==27){if(L){W=15987;break L21497}a[(c[7118]|0)+16|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=1;a_=K}else if((ao|0)==8){if(ab){W=16358;break L21497}ax=c[7118]|0;if((c[ax>>2]|0)!=2){W=16359;break L21497}a[ax+113|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=1;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==39){c[13898]=al+1;a[28824]=1;c[7204]=s4(q,r,1)|0;c[(c[3524]|0)+8>>2]=~~(+g[q>>2]*10.0*72.0/+(c[10032]|0));c[(c[3524]|0)+12>>2]=~~(+g[r>>2]*10.0*72.0/+(c[10032]|0));ax=c[3524]|0;c[10962]=c[ax+8>>2]<<1;c[10960]=c[ax+12>>2]<<1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==29){ax=al+1|0;c[13898]=ax;bm=ax;W=16246}else if((ao|0)==7){if(ab){W=16355;break L21497}ax=c[7118]|0;if((c[ax>>2]|0)!=2){W=16354;break L21497}a[ax+113|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=1;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==37){if(ac){W=16369;break L21497}if((c[c[7118]>>2]|0)!=2){W=16370;break L21497}c[13898]=al+1;uu(c[10956]|0);c[10956]=0;bn=c[13898]|0;if((bn|0)>=(c[8272]|0)){W=16020;break L21497}ax=c[1054]|0;L21766:do{if((a[ax+(bn*40&-1)|0]&1)!=0){au=c[ax+(bn*40&-1)+36>>2]|0;ap=ax+(bn*40&-1)+32|0;av=c[10036]|0;az=0;while(1){if((az|0)>=(au|0)){break}if((a[av+((c[ap>>2]|0)+az|0)|0]|0)==(a[az+103664|0]|0)){az=az+1|0}else{break L21766}}if((az|0)==1){W=16020;break L21497}}}while(0);a[14176]=1;is(o);a[14176]=0;if((c[T>>2]|0)!=3){W=16019;break L21497}ax=c[S>>2]|0;c[10956]=ax;if((ax|0)==0){W=16022;break L21497}else{aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=1;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}}else if((ao|0)==0){if(ak){W=16337;break L21497}ax=c[7118]|0;if((c[ax>>2]|0)!=3){W=16336;break L21497}c[ax+12>>2]=1;c[13898]=(c[13898]|0)+1;aG=1;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==32){if(ad){W=16367;break L21497}ax=c[7118]|0;if((c[ax>>2]|0)>>>0>=2){W=16368;break L21497}a[ax+97|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=1;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==11){if(aa){W=16372;break L21497}ax=c[7118]|0;if(((c[ax>>2]|0)-2|0)>>>0>=2){W=16371;break L21497}a[ax+18|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=1;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==36){if(af){W=16357;break L21497}ax=c[7118]|0;if((c[ax>>2]|0)==3){W=16356;break L21497}a[ax+112|0]=0;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=1;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else if((ao|0)==40){a[(c[7118]|0)+114|0]=1;c[13898]=(c[13898]|0)+1;aG=ak;aH=aj;aI=ai;aJ=ah;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}else{bm=al;W=16246}}while(0);L21787:do{if((W|0)==16246){W=0;L21789:do{if((bm|0)<(am|0)){L21791:do{if((a[an+(bm*40&-1)|0]&1)!=0){ao=c[an+(bm*40&-1)+36>>2]|0;ax=an+(bm*40&-1)+32|0;as=c[10036]|0;aq=0;while(1){if((aq|0)>=(ao|0)){break}if((a[as+((c[ax>>2]|0)+aq|0)|0]|0)==(a[aq+103664|0]|0)){aq=aq+1|0}else{break L21791}}if((aq|0)==1){break L21789}}}while(0);a[14176]=1;is(e);a[14176]=0;if((c[w>>2]|0)!=3){c[13898]=bm;break}az=c[t>>2]|0;if((az|0)==0){break}if(ag){W=16256;break L21497}ax=c[7118]|0;if(((c[ax>>2]|0)-2|0)>>>0>=2){W=16268;break L21497}as=bh(az|0,44)|0;do{if((as|0)==0){bo=ah}else{if((ca(as+1|0,205104,(v=i,i=i+8|0,c[v>>2]=ax+88,v)|0)|0)!=1){bo=ah;break}a[as]=0;bo=1}}while(0);if((a[az]|0)!=0){do{if((cy(az|0,186120)|0)!=0){uh((c[13898]|0)-1|0,185976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);uh(-1,185920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);as=cy(az|0,186120)|0;if((as|0)==0){break}else{bp=as}do{a[bp]=45;bp=cy(az|0,186120)|0;}while((bp|0)!=0)}}while(0);a[(c[7118]|0)+86|0]=0;aq=(c[7118]|0)+36|0;uF(aq|0,az|0,50)}uu(az);aG=ak;aH=aj;aI=ai;aJ=bo;aK=1;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K;break L21787}}while(0);if(ah){W=16270;break L21497}aq=is(d)|0;as=c[aq>>2]|0;if((as|0)==1){bq=+(c[aq+8>>2]|0)}else if((as|0)==2){bq=+h[aq+8>>3]}else if((as|0)==3){bq=+uz(c[aq+8>>2]|0,0)}else{W=16275;break L21497}if((c[V>>2]|0)==3){uu(c[U>>2]|0);c[V>>2]=1}g[(c[7118]|0)+88>>2]=bq;aG=ak;aH=aj;aI=ai;aJ=1;aK=ag;aL=af;aM=ae;aN=ad;aO=ac;aP=ab;aQ=aa;aR=$;aS=_;aT=Z;aU=Y;aV=X;aW=M;aX=J;aZ=L;a_=K}}while(0);an=c[13898]|0;aq=c[8272]|0;if((an|0)<(aq|0)){K=a_;L=aZ;J=aX;M=aW;X=aV;Y=aU;Z=aT;_=aS;$=aR;aa=aQ;ab=aP;ac=aO;ad=aN;ae=aM;af=aL;ag=aK;ah=aJ;ai=aI;aj=aH;ak=aG;al=an;am=aq}else{break L21495}}if((W|0)==16352){br=c[3524]|0;bs=br|0;bt=c[bs>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bt,v)|0)}else if((W|0)==16353){br=c[3524]|0;bs=br|0;bt=c[bs>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bt,v)|0)}else if((W|0)==16354){bu=c[3524]|0;bv=bu|0;bw=c[bv>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bw,v)|0)}else if((W|0)==16355){bu=c[3524]|0;bv=bu|0;bw=c[bv>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bw,v)|0)}else if((W|0)==16356){bx=c[3524]|0;by=bx|0;bz=c[by>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bz,v)|0)}else if((W|0)==16357){bx=c[3524]|0;by=bx|0;bz=c[by>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bz,v)|0)}else if((W|0)==16358){bA=c[3524]|0;bB=bA|0;bC=c[bB>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bC,v)|0)}else if((W|0)==16359){bA=c[3524]|0;bB=bA|0;bC=c[bB>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bC,v)|0)}else if((W|0)==16367){bD=c[3524]|0;bE=bD|0;bF=c[bE>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bF,v)|0)}else if((W|0)==16368){bD=c[3524]|0;bE=bD|0;bF=c[bE>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bF,v)|0)}else if((W|0)==16369){bG=c[3524]|0;bH=bG|0;bI=c[bH>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bI,v)|0)}else if((W|0)==16370){bG=c[3524]|0;bH=bG|0;bI=c[bH>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bI,v)|0)}else if((W|0)==16371){bJ=c[3524]|0;bK=bJ|0;bL=c[bK>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bL,v)|0)}else if((W|0)==16372){bJ=c[3524]|0;bK=bJ|0;bL=c[bK>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bL,v)|0)}else if((W|0)==16373){bM=c[3524]|0;bN=bM|0;bO=c[bN>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bO,v)|0)}else if((W|0)==16374){bM=c[3524]|0;bN=bM|0;bO=c[bN>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bO,v)|0)}else if((W|0)==16375){bP=c[3524]|0;bQ=bP|0;bR=c[bQ>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bR,v)|0)}else if((W|0)==16376){bP=c[3524]|0;bQ=bP|0;bR=c[bQ>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bR,v)|0)}else if((W|0)==16377){bS=c[3524]|0;bT=bS|0;bU=c[bT>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bU,v)|0)}else if((W|0)==16378){bS=c[3524]|0;bT=bS|0;bU=c[bT>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bU,v)|0)}else if((W|0)==16379){bV=c[3524]|0;bW=bV|0;bX=c[bW>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bX,v)|0)}else if((W|0)==16380){bV=c[3524]|0;bW=bV|0;bX=c[bW>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=bX,v)|0)}else if((W|0)==16256){uf(c[13898]|0,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16332){bY=c[3524]|0;bZ=bY|0;b_=c[bZ>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=b_,v)|0)}else if((W|0)==16333){bY=c[3524]|0;bZ=bY|0;b_=c[bZ>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=b_,v)|0)}else if((W|0)==16334){b$=c[3524]|0;b0=b$|0;b1=c[b0>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=b1,v)|0)}else if((W|0)==16335){b$=c[3524]|0;b0=b$|0;b1=c[b0>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=b1,v)|0)}else if((W|0)==16336){b2=c[3524]|0;b3=b2|0;b4=c[b3>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=b4,v)|0)}else if((W|0)==16337){b2=c[3524]|0;b3=b2|0;b4=c[b3>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=b4,v)|0)}else if((W|0)==16338){b5=c[3524]|0;b6=b5|0;b7=c[b6>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=b7,v)|0)}else if((W|0)==16339){b5=c[3524]|0;b6=b5|0;b7=c[b6>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=b7,v)|0)}else if((W|0)==16340){b8=c[3524]|0;b9=b8|0;cb=c[b9>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=cb,v)|0)}else if((W|0)==16341){b8=c[3524]|0;b9=b8|0;cb=c[b9>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=cb,v)|0)}else if((W|0)==16342){cc=c[3524]|0;cd=cc|0;ce=c[cd>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=ce,v)|0)}else if((W|0)==16343){cc=c[3524]|0;cd=cc|0;ce=c[cd>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=ce,v)|0)}else if((W|0)==16344){cf=c[3524]|0;cg=cf|0;ch=c[cg>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=ch,v)|0)}else if((W|0)==16345){cf=c[3524]|0;cg=cf|0;ch=c[cg>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=ch,v)|0)}else if((W|0)==16346){ci=c[3524]|0;cj=ci|0;ck=c[cj>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=ck,v)|0)}else if((W|0)==16347){ci=c[3524]|0;cj=ci|0;ck=c[cj>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=ck,v)|0)}else if((W|0)==16348){cl=c[3524]|0;cm=cl|0;cn=c[cm>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=cn,v)|0)}else if((W|0)==16349){cl=c[3524]|0;cm=cl|0;cn=c[cm>>2]|0;uf(al,134056,(v=i,i=i+8|0,c[v>>2]=cn,v)|0)}else if((W|0)==16139){uf(a$,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16112){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16115){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16091){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==16109){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16205){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16178){uf((c[13898]|0)-1|0,219072,(v=i,i=i+8|0,c[v>>2]=c[a8>>2],v)|0)}else if((W|0)==16275){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==16219){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==16080){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==16019){c[13898]=bn;W=16020}else if((W|0)==16239){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==16132){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==15992){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16086){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16121){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16214){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16072){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16268){uf((c[13898]|0)-1|0,185864,(v=i,i=i+16|0,c[v>>2]=c[c[3524]>>2],c[v+8>>2]=185832,v)|0)}else if((W|0)==16270){uf(c[13898]|0,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16075){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16058){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16055){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16069){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==15987){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16243){uf((c[13898]|0)-1|0,186184,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==16171){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=186328,v)|0)}else if((W|0)==16022){co=c[13898]|0;uf(co,134392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==15990){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((W|0)==16174){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=186296,v)|0)}else if((W|0)==16164){c[13898]=a3;W=16165}else if((W|0)==16167){cp=c[13898]|0;uf(cp,217824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==16118){uf(al,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}if((W|0)==16020){c[10956]=0;co=bn;uf(co,134392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((W|0)==16165){c[p>>2]=0;cp=a3;uf(cp,217824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);cp=c[7118]|0;a3=c[cp>>2]|0;do{if((a3|0)==3){g[7202]=+g[cp+88>>2]}else if((a3|0)==2){g[7202]=+g[cp+88>>2]*2.0}else if((a3|0)==1|(a3|0)==0){bq=+g[cp+88>>2];if(bq>0.0){g[7202]=bq*2.0;break}else{g[7202]=20.0;break}}}while(0);c[(c[3524]|0)+16>>2]=~~(+g[7202]*+g[cp+92>>2]*10.0);cp=c[7118]|0;bq=+g[7202]*+g[cp+92>>2]*10.0;if((a[cp+112|0]&1)==0){c[(c[3524]|0)+20>>2]=~~(bq*6.0/10.0)}else{c[(c[3524]|0)+20>>2]=~~(bq*5.0/10.0)}bq=+g[7202];be(229744,185824,(v=i,i=i+16|0,c[v>>2]=(c[7118]|0)+36,h[v+8>>3]=bq,v)|0);cp=c[7118]|0;a3=c[cp>>2]|0;do{if((a3|0)==3){p=c[cp+32>>2]|0;if((p|0)==0){cq=cp;cr=0;W=16298;break}else{cs=p;ct=0}do{ct=(ct+12|0)+(uA(c[cs+4>>2]|0)|0)|0;cs=c[cs>>2]|0;}while((cs|0)!=0);al=ct+1|0;co=ut(al)|0;do{if((co|0)==0){gk();bn=ut(al)|0;if((bn|0)!=0){cu=bn;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=185800,v)|0)}else{cu=co}}while(0);co=p;al=cu;do{be(al|0,185784,(v=i,i=i+8|0,c[v>>2]=c[co+4>>2],v)|0);al=al+(uA(al|0)|0)|0;co=c[co>>2]|0;}while((co|0)!=0);co=c[7118]|0;cv=cu;cw=co;cx=c[co>>2]|0;W=16297}else{cv=0;cw=cp;cx=a3;W=16297}}while(0);do{if((W|0)==16297){if((cx|0)==3){cq=cw;cr=cv;W=16298;break}else if((cx|0)==2){a[13048]=0;cz=cv;break}else{a3=(a[cw+96|0]&1)!=0?185320:179864;be(13048,199128,(v=i,i=i+16|0,c[v>>2]=(a[cw+97|0]&1)!=0?185408:185368,c[v+8>>2]=a3,v)|0);cz=cv;break}}}while(0);if((W|0)==16298){W=c[cq+12>>2]|0;if((W|0)==0){cA=199032}else{cA=(W|0)==1?78720:78704}W=(c[(c[3524]|0)+68>>2]|0)==86?217456:185688;if((a[cq+28|0]&1)==0){cB=185496}else{cB=(a[cq+29|0]&1)!=0?185600:185528}be(13048,185752,(v=i,i=i+24|0,c[v>>2]=cA,c[v+8>>2]=W,c[v+16>>2]=cB,v)|0);cz=cr}cr=c[7118]|0;cB=(a[cr+17|0]&1)!=0?116440:116408;W=(a[cr+18|0]&1)!=0?185096:185080;cA=(a[cr+19|0]&1)!=0?131504:131584;bq=+g[cr+20>>2];bl=+g[cr+24>>2];cq=(a[cr+30|0]&1)!=0?137776:137232;cv=(a[cr+31|0]&1)!=0?185032:185008;be(s|0,185256,(v=i,i=i+64|0,c[v>>2]=(a[cr+16|0]&1)!=0?185184:185112,c[v+8>>2]=cB,c[v+16>>2]=W,c[v+24>>2]=cA,h[v+32>>3]=bq,h[v+40>>3]=bl,c[v+48>>2]=cq,c[v+56>>2]=cv,v)|0);uC(13048,s|0);cv=c[7118]|0;bl=+h[cv+120>>3];if(bl<0.0){uD(13048+(uA(13048)|0)|0,184912,19);cC=cv}else{cq=~~(bl*255.0);cA=~~(+h[cv+128>>3]*255.0);W=~~(+h[cv+136>>3]*255.0);be(s|0,184960,(v=i,i=i+24|0,c[v>>2]=cq,c[v+8>>2]=cA,c[v+16>>2]=W,v)|0);uC(13048,s|0);cC=c[7118]|0}bl=+h[cC+104>>3];be(s|0,184848,(v=i,i=i+16|0,c[v>>2]=c[cC+100>>2],h[v+8>>3]=bl,v)|0);uC(13048,s|0);cC=c[7118]|0;W=c[cC>>2]|0;if(W>>>0<2){cA=(a[cC+97|0]&1)!=0?185408:185368;cq=(a[cC+96|0]&1)!=0?184832:184816;be(s|0,184840,(v=i,i=i+16|0,c[v>>2]=cA,c[v+8>>2]=cq,v)|0);uC(13048,s|0);cq=c[7118]|0;cD=cq;cE=c[cq>>2]|0}else{cD=cC;cE=W}if((cE|0)==2){cE=(a[cD+113|0]&1)!=0?133784:133640;be(s|0,73368,(v=i,i=i+8|0,c[v>>2]=cE,v)|0);cE=c[10956]|0;if((cE|0)==0){uD(s|0,184720,10)}else{be(s|0,184800,(v=i,i=i+8|0,c[v>>2]=cE,v)|0)}uC(13048,s|0)}do{if((c[11252]|0)==15){cE=c[7118]|0;if((c[cE>>2]|0)!=3){break}cD=(a[cE+114|0]&1)!=0?179864:136208;be(s|0,184664,(v=i,i=i+8|0,c[v>>2]=cD,v)|0);uC(13048,s|0)}}while(0);if(a[28824]|0){cD=c[3524]|0;bl=+((c[cD+8>>2]|0)>>>0>>>0);if((c[7204]|0)==2){bq=bl*2.54/720.0;bj=+((c[cD+12>>2]|0)>>>0>>>0)*2.54/720.0;be(s|0,90672,(v=i,i=i+16|0,h[v>>3]=bq,h[v+8>>3]=bj,v)|0)}else{bj=bl/720.0;bl=+((c[cD+12>>2]|0)>>>0>>>0)/720.0;be(s|0,90632,(v=i,i=i+16|0,h[v>>3]=bj,h[v+8>>3]=bl,v)|0)}uC(13048,s|0)}cD=c[7118]|0;cE=c[cD>>2]|0;do{if((cE|0)==3){W=cD+36|0;bl=+g[cD+88>>2];cC=(cz|0)!=0?cz:179864;be(s|0,184608,(v=i,i=i+24|0,c[v>>2]=W,h[v+8>>3]=bl,c[v+16>>2]=cC,v)|0)}else if((cE|0)==2){bl=+g[cD+88>>2];be(s|0,184552,(v=i,i=i+16|0,c[v>>2]=cD+36,h[v+8>>3]=bl,v)|0)}else{bl=+g[cD+88>>2];if(bl!=0.0){bj=bl;be(s|0,184536,(v=i,i=i+8|0,h[v>>3]=bj,v)|0);break}else{a[s]=0;break}}}while(0);if((cz|0)==0){cF=uC(13048,s|0)|0;cG=c[7118]|0;cH=cG+92|0;cI=+g[cH>>2];cJ=cI;cK=be(s|0,184512,(v=i,i=i+8|0,h[v>>3]=cJ,v)|0)|0;cL=uC(13048,s|0)|0;i=b;return}uu(cz);cF=uC(13048,s|0)|0;cG=c[7118]|0;cH=cG+92|0;cI=+g[cH>>2];cJ=cI;cK=be(s|0,184512,(v=i,i=i+8|0,h[v>>3]=cJ,v)|0)|0;cL=uC(13048,s|0)|0;i=b;return}function pA(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,n=0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,x=0.0,y=0.0,z=0.0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,aA=0;b=i;i=i+4400|0;d=b|0;e=b+8|0;f=b+40|0;j=b+296|0;k=b+304|0;l=c[(c[7118]|0)+12>>2]|0;if((l|0)==1){if(!(a[28824]|0)){c[(c[3524]|0)+8>>2]=5040;c[(c[3524]|0)+12>>2]=7200}n=c[3524]|0;o=+((c[n+8>>2]|0)>>>0>>>0);p=+g[184];q=o*(p+ +g[178])/10.0;r=+((c[n+12>>2]|0)>>>0>>>0);s=+g[44];t=r*(s+ +g[38])/10.0;h[n+176>>3]=10.0;u=t;x=q;y=r*s/10.0;z=o*p/10.0}else if((l|0)==0){do{if(a[28824]|0){c[(c[3524]|0)+8>>2]=c[10962];c[(c[3524]|0)+12>>2]=c[10960]}else{c[(c[3524]|0)+8>>2]=7200;n=(c[3524]|0)+12|0;if((a[(c[7118]|0)+112|0]&1)==0){c[n>>2]=5040;break}else{c[n>>2]=4320;break}}}while(0);n=c[3524]|0;p=+((c[n+8>>2]|0)>>>0>>>0);o=+g[184];s=p*(o+ +g[178])/20.0;r=+((c[n+12>>2]|0)>>>0>>>0);q=+g[44];t=r*(q+ +g[38])/20.0;h[n+176>>3]=20.0;u=t;x=s;y=r*q/20.0;z=p*o/20.0}else if((l|0)==2){if(!(a[28824]|0)){c[(c[3524]|0)+8>>2]=7200;c[(c[3524]|0)+12>>2]=5040}l=c[3524]|0;o=+((c[l+8>>2]|0)>>>0>>>0);p=+g[184];q=o*(p+ +g[178])/10.0;r=+((c[l+12>>2]|0)>>>0>>>0);s=+g[44];t=r*(1.0- +g[38]-s)/10.0;h[l+176>>3]=10.0;u=q;x=r*(1.0-s)/10.0;y=o*p/10.0;z=t}else{uf(-1,199080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=~~u;n=~~x;A=~~y;B=~~z;uB(28840,(c[7118]|0)+36|0);g[7208]=+g[7202];C=c[7118]|0;D=c[C>>2]|0;do{if((D|0)==2){E=k|0;F=c[8244]|0;if((F|0)==0){break}G=uA(F|0)|0;H=G-4|0;if((bh(F|0,46)|0)!=(F+H|0)){uf(-1,187368,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}F=G+5|0;G=ut(F)|0;do{if((G|0)==0){gk();I=ut(F)|0;if((I|0)!=0){J=I;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=187344,v)|0)}else{J=G}}while(0);G=c[8244]|0;uB(J|0,G|0);G=(c[8244]|0)+H|0;do{if((a_(G|0,187336,4)|0)==0){K=16417}else{if((a_(G|0,187312,4)|0)==0){K=16417;break}if((a[(c[7118]|0)+113|0]&1)==0){be(E|0,186952,(v=i,i=i+8|0,c[v>>2]=199032,v)|0)}else{be(E|0,186960,(v=i,i=i+8|0,c[v>>2]=199032,v)|0)}F=J+H|0;I=(uA(E|0)|0)+1|0;uF(F|0,E|0,I|0);I=bF(J|0,134520)|0;c[10028]=I;L=I}}while(0);do{if((K|0)==16417){if((a[(c[7118]|0)+113|0]&1)!=0){uf(-1,187264,(v=i,i=i+8|0,c[v>>2]=187208,v)|0)}E=G;w=2019914798;a[E]=w&255;w=w>>8;a[E+1|0]=w&255;w=w>>8;a[E+2|0]=w&255;w=w>>8;a[E+3|0]=w&255;c[10028]=c[10030];c[10030]=bF(c[8244]|0,137896)|0;uh(-1,187016,(v=i,i=i+16|0,c[v>>2]=c[8244],c[v+8>>2]=J,v)|0);if((c[10030]|0)==0){uf(-1,186984,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{L=c[10028]|0;break}}}while(0);if((L|0)==0){uf(-1,186896,(v=i,i=i+8|0,c[v>>2]=J,v)|0)}G=(uA(J|0)|0)-3|0;E=ut(G)|0;do{if((E|0)==0){gk();H=ut(G)|0;if((H|0)!=0){M=H;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=186872,v)|0)}else{M=E}}while(0);c[7112]=M;E=(uA(J|0)|0)-4|0;uF(M|0,J|0,E|0);E=(uA(J|0)|0)-4|0;a[(c[7112]|0)+E|0]=0;uu(J)}else if((D|0)==3){c[10028]=c[10030]}else{E=c[8244]|0;if((E|0)==0){if((a[C+96|0]&1)!=0){G=c[m>>2]|0;aI(186464,61,1,G|0);a[(c[7118]|0)+96|0]=0}c[10028]=c[10030];break}G=bh(E|0,46)|0;if((G|0)==0){N=a8(E|0,0)|0}else{N=G}if((a[C+96|0]&1)==0){c[10028]=c[10030];break}G=N;H=db(c[7112]|0,(5-E|0)+G|0,186784)|0;c[7112]=H;if((H|0)==0){E=c[m>>2]|0;I=c[8244]|0;cf(E|0,186600,(v=i,i=i+8|0,c[v>>2]=I,v)|0);I=c[m>>2]|0;aI(186560,27,1,I|0);a[(c[7118]|0)+96|0]=0;c[10028]=c[10030];break}I=c[8244]|0;uF(H|0,I|0,(G+1|0)-I|0);I=(c[7112]|0)+(G-(c[8244]|0)|0)|0;w=7565358;a[I]=w&255;w=w>>8;a[I+1|0]=w&255;w=w>>8;a[I+2|0]=w&255;w=w>>8;a[I+3|0]=w&255;I=bF(c[7112]|0,137896)|0;c[10028]=I;if((I|0)!=0){break}cf(c[m>>2]|0,186696,(v=i,i=i+8|0,c[v>>2]=c[7112],v)|0);uu(c[7112]|0);c[7112]=0;a[(c[7118]|0)+96|0]=0;c[10028]=c[10030]}}while(0);N=c[7118]|0;C=(c[N>>2]|0)==3;D=c[N+4>>2]|0;J=c[N+8>>2]|0;N=c[3524]|0;M=(c[N+68>>2]|0)==86;L=M?238344:0;a[28912]=C&1;c[7226]=D;c[7224]=J;c[7120]=0;cG(j|0);k=bg(aN(j|0)|0)|0;a[k+((uA(k|0)|0)-1|0)|0]=0;j=c[c[7118]>>2]|0;do{if((j|0)==2){do{if((c[10030]|0)==0){I=(uA(c[8244]|0)|0)+1|0;G=ut(I)|0;do{if((G|0)==0){gk();H=ut(I)|0;if((H|0)!=0){O=H;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=129080,v)|0)}else{O=G}}while(0);G=c[8244]|0;uB(O|0,G|0);lj(O);G=c[8244]|0;if((O|0)==(G|0)|(O|0)==0){P=G;break}uu(O);K=16439}else{K=16439}}while(0);if((K|0)==16439){P=c[8244]|0}if((P|0)==0){uj(c[13898]|0,193824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}G=c[10030]|0;if((G|0)==0){Q=0;R=0;S=0}else{cf(G|0,193768,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);G=c[11252]|0;if((G|0)==1){T=193704}else if((G|0)==2){T=193640}else if((G|0)==3){T=193624}else if((G|0)==4){T=193616}else if((G|0)==5){T=193560}else if((G|0)==6){T=96912}else if((G|0)==7){T=90808}else if((G|0)==9){T=75288}else if((G|0)==10){T=222760}else if((G|0)==12){T=193552}else if((G|0)==13){T=193480}else if((G|0)==16){uf(-1,193408,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{T=0}c[1402]=-1;c[10882]=0;G=(c[7118]|0)+36|0;do{if((a[G]|0)==0){U=0;V=0;W=0}else{if((aY(G|0,126024)|0)==0){U=0;V=0;W=0;break}I=(uA(G|0)|0)+1|0;H=ut(I)|0;do{if((H|0)==0){gk();E=ut(I)|0;if((E|0)!=0){X=E;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=193376,v)|0)}else{X=H}}while(0);H=(uA((c[7118]|0)+36|0)|0)+1|0;I=ut(H)|0;do{if((I|0)==0){gk();E=ut(H)|0;if((E|0)!=0){Y=E;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=193376,v)|0)}else{Y=I}}while(0);I=(uA((c[7118]|0)+36|0)|0)+1|0;H=ut(I)|0;do{if((H|0)==0){gk();E=ut(I)|0;if((E|0)!=0){Z=E;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=193376,v)|0)}else{Z=H}}while(0);H=(c[7118]|0)+36|0;uB(X|0,H|0);a[Y]=0;a[Z]=0;H=a8(X|0,44)|0;if((H|0)==0){U=Z;V=Y;W=X;break}a[H]=0;I=H+1|0;uB(Y|0,I|0);I=a8(Y|0,44)|0;if((I|0)==0){U=Z;V=Y;W=X;break}a[I]=0;H=I+1|0;uB(Z|0,H|0);U=Z;V=Y;W=X}}while(0);G=c[7118]|0;if((a[G+113|0]&1)!=0){H=c[10030]|0;z=+g[G+88>>2];G=~~(z+-10.0);I=~~z;cf(H|0,192864,(v=i,i=i+24|0,c[v>>2]=G,c[v+8>>2]=I,c[v+16>>2]=I,v)|0);do{if((W|0)!=0){if((a[W]|0)==0){break}I=c[10030]|0;cf(I|0,192816,(v=i,i=i+8|0,c[v>>2]=W,v)|0)}}while(0);do{if((V|0)!=0){if((a[V]|0)==0){break}I=c[10030]|0;cf(I|0,192784,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}}while(0);do{if((U|0)!=0){if((a[U]|0)==0){break}I=c[10030]|0;cf(I|0,192752,(v=i,i=i+8|0,c[v>>2]=U,v)|0)}}while(0);I=c[10030]|0;aI(192648,57,1,I|0);if((T|0)!=0){I=c[10030]|0;cf(I|0,192544,(v=i,i=i+8|0,c[v>>2]=T,v)|0)}I=c[10030]|0;G=c[3524]|0;z=+((c[G+8>>2]|0)>>>0>>>0)*+g[178]/20.0;y=+((c[G+12>>2]|0)>>>0>>>0)*+g[38]/20.0;G=c[10956]|0;H=(G|0)!=0?G:191528;cf(I|0,191600,(v=i,i=i+40|0,h[v>>3]=z,h[v+8>>3]=y,h[v+16>>3]=z,h[v+24>>3]=y,c[v+32>>2]=H,v)|0)}H=c[10030]|0;aI(191384,12,1,H|0);if((T|0)!=0){H=c[10030]|0;cf(H|0,191184,(v=i,i=i+16|0,c[v>>2]=T,c[v+8>>2]=T,v)|0)}do{if((a[(c[7118]|0)+113|0]&1)==0){H=(W|0)!=0;do{if(H){if((a[W]|0)==0){break}I=c[10030]|0;cf(I|0,191152,(v=i,i=i+8|0,c[v>>2]=W,v)|0)}}while(0);I=(V|0)!=0;do{if(I){if((a[V]|0)==0){break}G=c[10030]|0;cf(G|0,191104,(v=i,i=i+8|0,c[v>>2]=V,v)|0)}}while(0);do{if((U|0)==0){if(H|I){K=16496}}else{if((a[U]|0)==0){K=16496;break}G=c[10030]|0;cf(G|0,191080,(v=i,i=i+8|0,c[v>>2]=U,v)|0);K=16496}}while(0);if((K|0)==16496){I=c[10030]|0;aI(191064,14,1,I|0)}I=c[10956]|0;if((I|0)==0){break}H=c[10030]|0;cf(H|0,154696,(v=i,i=i+8|0,c[v>>2]=I,v)|0)}}while(0);I=c[10030]|0;H=c[7118]|0;G=(a[H+17|0]&1)!=0?225e3:224992;E=(a[H+18|0]&1)!=0?225e3:224992;cf(I|0,189944,(v=i,i=i+16|0,c[v>>2]=G,c[v+8>>2]=E,v)|0);E=c[10030]|0;aI(189536,396,1,E|0);E=c[10030]|0;if((a[(c[7118]|0)+112|0]&1)==0){aI(188600,593,1,E|0)}else{aI(189272,260,1,E|0)}E=c[10030]|0;aI(187688,837,1,E|0);E=c[10030]|0;G=c[3524]|0;y=+((c[G+8>>2]|0)>>>0>>>0)*+g[178];z=+((c[G+12>>2]|0)>>>0>>>0)*+g[38];cf(E|0,187608,(v=i,i=i+24|0,h[v>>3]=.05,h[v+8>>3]=y,h[v+16>>3]=z,v)|0);Q=U;R=V;S=W}E=c[7118]|0;z=+h[E+120>>3];if(z>=0.0){G=c[10030]|0;y=+h[E+128>>3];x=+h[E+136>>3];cf(G|0,187528,(v=i,i=i+24|0,h[v>>3]=z,h[v+8>>3]=y,h[v+16>>3]=x,v)|0);G=c[10030]|0;E=c[3524]|0;x=+((c[E+8>>2]|0)>>>0>>>0)*+g[178];y=+((c[E+12>>2]|0)>>>0>>>0)*+g[38];cf(G|0,187432,(v=i,i=i+16|0,h[v>>3]=x,h[v+8>>3]=y,v)|0)}if((S|0)!=0){uu(S)}if((R|0)!=0){uu(R)}if((Q|0)==0){break}uu(Q)}else if((j|0)==1){cf(c[10030]|0,195248,(v=i,i=i+8|0,h[v>>3]=.05,v)|0);G=c[3524]|0;E=~~(+g[38]*+((c[G+12>>2]|0)>>>0>>>0));cf(c[10030]|0,195216,(v=i,i=i+16|0,c[v>>2]=~~(+g[178]*+((c[G+8>>2]|0)>>>0>>>0)),c[v+8>>2]=E,v)|0);K=16513}else if((j|0)==0){cf(c[10030]|0,194104,(v=i,i=i+8|0,h[v>>3]=.05,v)|0);E=c[3524]|0;G=~~(+g[38]*+((c[E+12>>2]|0)>>>0>>>0));cf(c[10030]|0,194064,(v=i,i=i+16|0,c[v>>2]=~~(+g[178]*+((c[E+8>>2]|0)>>>0>>>0)),c[v+8>>2]=G,v)|0);K=16513}}while(0);if((K|0)==16513){j=c[10028]|0;Q=c[10030]|0;if((j|0)==(Q|0)){aI(193952,21,1,j|0)}else{j=c[3524]|0;R=~~(+g[178]*+((c[j+8>>2]|0)>>>0>>>0)/20.0+.5);S=~~(+g[38]*+((c[j+12>>2]|0)>>>0>>>0)/20.0+.5);j=c[7112]|0;W=bh(j|0,47)|0;V=(W|0)==0?j:W+1|0;W=R*10&-1;cf(Q|0,194008,(v=i,i=i+32|0,c[v>>2]=V,c[v+8>>2]=R,c[v+16>>2]=S,c[v+24>>2]=W,v)|0)}c[7036]=0}W=c[10028]|0;if((c[(c[7118]|0)+12>>2]|0)==0){aI(198992,24,1,W|0)}else{aI(198968,15,1,W|0)}W=c[8244]|0;if((W|0)!=0){S=c[10028]|0;cf(S|0,198936,(v=i,i=i+8|0,c[v>>2]=W,v)|0)}cf(c[10028]|0,230952,(v=i,i=i+32|0,c[v>>2]=40152,c[v+8>>2]=40160,c[v+16>>2]=k,c[v+24>>2]=C?198928:179864,v)|0);W=D+B|0;B=J+A|0;A=D+n|0;n=J+l|0;cf(c[10028]|0,198896,(v=i,i=i+32|0,c[v>>2]=W,c[v+8>>2]=B,c[v+16>>2]=A,c[v+24>>2]=n,v)|0);l=c[7118]|0;do{if((c[l>>2]|0)==3){J=c[l+12>>2]|0;if((J|0)==0){_=l;break}D=c[10028]|0;S=(J|0)==2?198832:198816;cf(D|0,198872,(v=i,i=i+8|0,c[v>>2]=S,v)|0);_=c[7118]|0}else{_=l}}while(0);if((c[_+12>>2]|0)==0){$=_;aa=224992}else{_=c[10028]|0;aI(198792,17,1,_|0);_=c[7118]|0;$=_;aa=(c[_+12>>2]|0)==2?225e3:224992}_=(a[$+18|0]&1)!=0?225e3:224992;l=(a[$+19|0]&1)!=0?225e3:224992;y=+g[$+20>>2];S=(a[$+16|0]&1)!=0?225e3:224992;D=(a[$+30|0]&1)!=0?225e3:224992;J=(a[$+31|0]&1)!=0?225e3:224992;x=+g[$+24>>2]*5.0;z=+h[$+120>>3];u=+h[$+128>>3];t=+h[$+136>>3];R=(c[N+16>>2]|0)/-3&-1;cf(c[10028]|0,230144,(v=i,i=i+136|0,c[v>>2]=(a[$+17|0]&1)!=0?225e3:224992,c[v+8>>2]=_,c[v+16>>2]=l,h[v+24>>3]=y,c[v+32>>2]=aa,c[v+40>>2]=S,c[v+48>>2]=D,c[v+56>>2]=J,h[v+64>>3]=x,h[v+72>>3]=z,h[v+80>>3]=u,h[v+88>>3]=t,c[v+96>>2]=R,h[v+104>>3]=10.0,h[v+112>>3]=10.0,h[v+120>>3]=31.5,h[v+128>>3]=31.5,v)|0);cf(c[10028]|0,198632,(v=i,i=i+64|0,c[v>>2]=W,c[v+8>>2]=B,c[v+16>>2]=A,c[v+24>>2]=B,c[v+32>>2]=A,c[v+40>>2]=n,c[v+48>>2]=W,c[v+56>>2]=n,v)|0);n=f|0;f=tE(198600)|0;if((f|0)!=0){if((a6(n|0,256,f|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,f|0)|0)!=0)}az(f|0)}do{if(C){f=c[11252]|0;if((f|0)==12){W=tE(198296)|0;if((W|0)==0){break}if((a6(n|0,256,W|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,W|0)|0)!=0)}az(W|0);break}else if((f|0)==9){A=tE(198384)|0;if((A|0)==0){break}if((a6(n|0,256,A|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,A|0)|0)!=0)}az(A|0);break}else if((f|0)==15){W=tE(198232)|0;if((W|0)!=0){if((a6(n|0,256,W|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,W|0)|0)!=0)}az(W|0)}if((c[18090]|0)!=0){break}A=e|0;c[d>>2]=0;B=tE(195944)|0;if((B|0)==0){break}if((a6(n|0,256,B|0)|0)!=0){do{R=a[n]|0;do{if(!((R<<24>>24|0)==35|(R<<24>>24|0)==10)){J=aE(n|0,d|0,16)|0;if(J>>>0>255){if((a[(c[7118]|0)+114|0]&1)==0){break}}D=(c[d>>2]|0)+1|0;c[d>>2]=D;S=(a8(D|0,59)|0)-D|0;uF(A|0,D|0,S|0);a[e+S|0]=0;S=c[18088]|0;if(((c[18086]|0)+8|0)>>>0>S>>>0){D=S+2048|0;c[18088]=D;S=db(c[18090]|0,D,195936)|0;c[18090]=S;ab=S}else{ab=c[18090]|0}c[ab+(c[7114]<<3)>>2]=J;J=bP(A|0)|0;c[(c[18090]|0)+(c[7114]<<3)+4>>2]=J;c[18086]=(c[18086]|0)+8;c[7114]=(c[7114]|0)+1}}while(0);}while((a6(n|0,256,B|0)|0)!=0)}az(B|0);break}else if((f|0)==10){A=tE(198328)|0;if((A|0)==0){break}if((a6(n|0,256,A|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,A|0)|0)!=0)}az(A|0);break}else if((f|0)==1){B=tE(198552)|0;if((B|0)==0){break}if((a6(n|0,256,B|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,B|0)|0)!=0)}az(B|0);break}else if((f|0)==2){A=tE(198488)|0;if((A|0)==0){break}if((a6(n|0,256,A|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,A|0)|0)!=0)}az(A|0);break}else if((f|0)==11|(f|0)==3){B=tE(198472)|0;if((B|0)==0){break}if((a6(n|0,256,B|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,B|0)|0)!=0)}az(B|0);break}else if((f|0)==4){A=tE(198456)|0;if((A|0)==0){break}if((a6(n|0,256,A|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,A|0)|0)!=0)}az(A|0);break}else if((f|0)==5){B=tE(198440)|0;if((B|0)==0){break}if((a6(n|0,256,B|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,B|0)|0)!=0)}az(B|0);break}else if((f|0)==6){A=tE(198424)|0;if((A|0)==0){break}if((a6(n|0,256,A|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,A|0)|0)!=0)}az(A|0);break}else if((f|0)==7){B=tE(198408)|0;if((B|0)==0){break}if((a6(n|0,256,B|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,B|0)|0)!=0)}az(B|0);break}else if((f|0)==13){A=tE(198272)|0;if((A|0)==0){break}if((a6(n|0,256,A|0)|0)!=0){do{aK(n|0,c[10028]|0);}while((a6(n|0,256,A|0)|0)!=0)}az(A|0);break}else{break}}}while(0);n=c[7118]|0;do{if((c[n>>2]|0)==2){if((a[n+112|0]&1)==0){ac=n;break}ab=c[10028]|0;aI(196392,51,1,ab|0);ab=c[10028]|0;aI(196344,29,1,ab|0);ab=c[10028]|0;aI(196312,28,1,ab|0);ab=c[10028]|0;aI(196216,66,1,ab|0);ab=c[10028]|0;aI(196136,28,1,ab|0);ab=c[10028]|0;aI(196080,39,1,ab|0);ab=c[10028]|0;aI(196032,39,1,ab|0);ab=c[10028]|0;aI(195960,53,1,ab|0);ac=c[7118]|0}else{ac=n}}while(0);if((a[ac+28|0]&1)!=0){n=c[10028]|0;ab=(a[ac+29|0]&1)!=0?225e3:224992;cf(n|0,198176,(v=i,i=i+8|0,c[v>>2]=ab,v)|0)}do{if(M){ab=c[L>>2]|0;if((ab|0)==0){break}else{ad=L;ae=ab}do{ad=ad+4|0;aK(ae|0,c[10028]|0);ae=c[ad>>2]|0;}while((ae|0)!=0)}}while(0);if(C){C=c[7118]|0;ae=c[C+32>>2]|0;if((ae|0)==0){af=C}else{C=ae;do{tD(C,1);ae=c[C+12>>2]|0;if((ae|0)!=0){tz(ae)}C=c[C>>2]|0;}while((C|0)!=0);af=c[7118]|0}tz(af+36|0)}af=bU(131496)|0;if((af|0)==0){C=bU(130312)|0;if((C|0)==0){ag=0;ah=0;ai=1}else{aj=C;K=16610}}else{aj=af;K=16610}do{if((K|0)==16610){af=bP(aj|0)|0;if((af|0)==0){ag=0;ah=0;ai=1;break}if((a[af]|0)==0){ag=0;ah=af;ai=0;break}C=uA(af|0)<<1|1;ae=ut(C)|0;do{if((ae|0)==0){gk();ad=ut(C)|0;if((ad|0)!=0){ak=ad;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=197984,v)|0)}else{ak=ae}}while(0);ae=a[af]|0;if(ae<<24>>24==0){al=ak}else{C=af;ad=ak;L=ae;while(1){if((aZ(198168,L<<24>>24|0,4)|0)==0){am=ad;an=L}else{a[ad]=92;am=ad+1|0;an=a[C]|0}ae=C+1|0;M=am+1|0;a[am]=an;A=a[ae]|0;if(A<<24>>24==0){al=M;break}else{C=ae;ad=M;L=A}}}a[al]=0;ag=ak;ah=af;ai=0}}while(0);ak=c[8244]|0;do{if((ak|0)==0){ao=0}else{if((a[ak]|0)==0){ao=0;break}al=uA(ak|0)<<1|1;an=ut(al)|0;do{if((an|0)==0){gk();am=ut(al)|0;if((am|0)!=0){ap=am;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=197984,v)|0)}else{ap=an}}while(0);an=a[ak]|0;if(an<<24>>24==0){aq=ap}else{al=ak;af=ap;am=an;while(1){if((aZ(198168,am<<24>>24|0,4)|0)==0){ar=af;as=am}else{a[af]=92;ar=af+1|0;as=a[al]|0}an=al+1|0;aj=ar+1|0;a[ar]=as;K=a[an]|0;if(K<<24>>24==0){aq=aj;break}else{al=an;af=aj;am=K}}}a[aq]=0;ao=ap}}while(0);ap=(ao|0)!=0;aq=(ag|0)!=0;cf(c[10028]|0,229800,(v=i,i=i+40|0,c[v>>2]=ap?ao:179864,c[v+8>>2]=40152,c[v+16>>2]=40160,c[v+24>>2]=aq?ag:179864,c[v+32>>2]=k,v)|0);if(!ai){uu(ah)}if(aq){uu(ag)}if(!ap){at=c[10028]|0;au=aI(198144,16,1,at|0)|0;av=c[3524]|0;aw=av+68|0;ax=c[aw>>2]|0;ay=(ax|0)==86;aA=ay?2:1;c[59584]=aA;i=b;return}uu(ao);at=c[10028]|0;au=aI(198144,16,1,at|0)|0;av=c[3524]|0;aw=av+68|0;ax=c[aw>>2]|0;ay=(ax|0)==86;aA=ay?2:1;c[59584]=aA;i=b;return}function pB(a){a=a|0;c[7230]=a;return 1}function pC(a){a=a|0;c[7198]=a;return 1}function pD(){var b=0,d=0,e=0;b=i;aI(199192,10,1,c[10028]|0);do{if((a[28912]&1)!=0){d=c[10028]|0;aI(199160,17,1,d|0);d=c[57760]|0;if((d|0)==0){break}else{e=d}while(1){d=c[e+4>>2]|0;cf(c[10028]|0,199128,(v=i,i=i+16|0,c[v>>2]=c[e>>2],c[v+8>>2]=(d|0)!=0?73808:140136,v)|0);uu(c[c[57760]>>2]|0);uu(c[57760]|0);c[57760]=d;if((d|0)==0){break}else{e=d}}}}while(0);if((c[(c[7118]|0)+12>>2]|0)==0){i=b;return}cf(c[10028]|0,199112,(v=i,i=i+8|0,c[v>>2]=c[7120],v)|0);i=b;return}function pE(){c[7116]=0;aI(199208,29,1,c[10028]|0);return}function pF(){var b=0,d=0,e=0,f=0,j=0,k=0.0,l=0.0;b=i;d=c[3524]|0;e=(c[7120]|0)+1|0;c[7120]=e;cf(c[10028]|0,202176,(v=i,i=i+16|0,c[v>>2]=e,c[v+8>>2]=e,v)|0);if((c[(c[3524]|0)+68>>2]|0)==86&(c[59584]|0)==1){e=c[59586]|0;if((e|0)!=0){f=238344;j=e;do{f=f+4|0;aK(j|0,c[10028]|0);j=c[f>>2]|0;}while((j|0)!=0)}j=c[m>>2]|0;aI(202120,53,1,j|0);c[59584]=2}j=c[7224]|0;k=((c[(c[7118]|0)+12>>2]|0)==0?.5:1.0)/10.0;cf(c[10028]|0,202048,(v=i,i=i+32|0,c[v>>2]=c[7226],c[v+8>>2]=j,h[v+16>>3]=k,h[v+24>>3]=k,v)|0);if((c[(c[7118]|0)+12>>2]|0)==2){j=c[10028]|0;f=-(c[(c[3524]|0)+12>>2]|0)|0;cf(j|0,202016,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}aI(201976,18,1,c[10028]|0);if((a[28912]&1)!=0){f=c[10028]|0;j=(c[7118]|0)+36|0;e=c[d+16>>2]|0;cf(f|0,201872,(v=i,i=i+16|0,c[v>>2]=j,c[v+8>>2]=e,v)|0)}c[7116]=0;a[228944]=0;c[57326]=-4e3;c[57328]=-4e3;c[57234]=0;c[57432]=-5;h[28715]=-5.0;h[28714]=-5.0;g[7200]=-1.0;if((c[c[7118]>>2]|0)==2){i=b;return}aI(201808,56,1,c[10028]|0);e=c[10028]|0;if((c[(c[7118]|0)+12>>2]|0)==0){j=c[3524]|0;k=+((c[j+8>>2]|0)>>>0>>>0)*+g[178];l=+((c[j+12>>2]|0)>>>0>>>0)*+g[38];cf(e|0,201752,(v=i,i=i+16|0,h[v>>3]=k,h[v+8>>3]=l,v)|0)}else{aI(201632,46,1,e|0)}aI(201616,5,1,c[10028]|0);i=b;return}function pG(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;e=i;i=i+64|0;f=e|0;g=e+32|0;h=c[57328]|0;j=b-h|0;k=c[57326]|0;l=d-k|0;do{if((h|0)==(b|0)&(k|0)==(d|0)){if(!(a[228944]|0)){break}i=e;return}}while(0);k=f|0;be(k|0,202376|0,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);f=g|0;be(f|0,202240|0,(v=i,i=i+16|0,c[v>>2]=j,c[v+8>>2]=l,v)|0);L22391:do{if(a[229704]|0){l=c[10028]|0;cf(l|0,202208|0,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);a[229704]=0}else{l=uA(f|0)|0;do{if(l>>>0<(uA(k|0)|0)>>>0){if(!(a[228944]|0)){break}j=c[10028]|0;aK(f|0,j|0);c[57234]=(c[57234]|0)+1;break L22391}}while(0);l=c[10028]|0;aK(k|0,l|0)}}while(0);a[228944]=1;c[7116]=(c[7116]|0)+1;c[57328]=b;c[57326]=d;i=e;return}function pH(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;e=i;i=i+64|0;f=c[57328]|0;g=c[57326]|0;if((f|0)==(b|0)&(g|0)==(d|0)){i=e;return}h=e|0;be(h|0,202608,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);j=e+32|0;be(j|0,202560,(v=i,i=i+16|0,c[v>>2]=b-f,c[v+8>>2]=d-g,v)|0);if(!(a[228944]|0)){pG(c[57328]|0,c[57326]|0)}g=uA(j|0)|0;f=g>>>0<(uA(h|0)|0)>>>0;g=c[10028]|0;do{if(f){aK(j|0,g|0);c[57234]=(c[57234]|0)+1;k=(c[7116]|0)+1|0;c[7116]=k;if((k|0)<=104){break}k=c[10028]|0;cf(k|0,202480,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);c[7116]=1}else{aK(h|0,g|0);c[7116]=1}}while(0);a[228944]=1;c[57328]=b;c[57326]=d;i=e;return}function pI(b){b=b|0;var d=0,e=0,f=0,g=0;d=i;e=c[7118]|0;do{if((c[e>>2]|0)==2){if((a[e+112|0]&1)==0){f=16692;break}g=(b|0)%4&-1}else{f=16692}}while(0);if((f|0)==16692){g=(b|0)%9&-1}b=g+3|0;g=(b|0)<0?0:b;if((c[57432]|0)==(g|0)){i=d;return}a[228944]=0;if((c[7116]|0)!=0){b=c[10028]|0;aI(215952,7,1,b|0);c[7116]=0;a[228944]=0}c[57432]=g;h[28714]=+h[28715];cf(c[10028]|0,202624,(v=i,i=i+8|0,c[v>>2]=a[g+93672|0]|0,v)|0);c[7116]=0;i=d;return}function pJ(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;g=c[7118]|0;do{if((c[g>>2]|0)==2){if((a[g+112|0]&1)==0){break}if((e|0)<0){h=0}else{h=(e&7)+1|0}j=c[10028]|0;k=c[228960+(h<<2)>>2]|0;cf(j|0,202896,(v=i,i=i+24|0,c[v>>2]=b,c[v+8>>2]=d,c[v+16>>2]=k,v)|0);a[228944]=0;c[7116]=0;c[57432]=-5;i=f;return}}while(0);if((e|0)<0){l=0}else{l=((e>>>0)%75>>>0)+1|0}e=c[229e3+(l<<2)>>2]|0;cf(c[10028]|0,202896,(v=i,i=i+24|0,c[v>>2]=b,c[v+8>>2]=d,c[v+16>>2]=e,v)|0);a[228944]=0;c[7116]=0;c[57432]=-5;i=f;return}function pK(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0;lD(b,d,e,f,g);if((a[(c[7118]|0)+19|0]&1)!=0|(g|0)==0){return}if((c[7116]|0)!=0){h=c[10028]|0;aI(215952,7,1,h|0);c[7116]=0;a[228944]=0}aI(204880,19,1,c[10028]|0);lD(b,d,e,f,-g|0);if((c[7116]|0)!=0){g=c[10028]|0;aI(215952,7,1,g|0);c[7116]=0;a[228944]=0}aI(212688,9,1,c[10028]|0);return}function pL(b){b=b|0;var d=0,e=0,f=0,j=0,k=0,l=0,m=0.0,n=0.0;d=i;i=i+8|0;e=d|0;if((b|0)==0){f=16722}else{if((a[b]|0)==0){f=16722}else{j=b}}if((f|0)==16722){j=229744}f=bk(j|0,148464)|0;g[e>>2]=+g[7202];if((a[j+f|0]|0)==44){b=j+(f+1|0)|0;ca(b|0,205104,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}do{if((f|0)==0){b=bP(229744)|0;k=bk(b|0,148464)|0;l=b}else{if((j|0)==0){k=f;l=0;break}k=f;l=bP(j|0)|0}}while(0);a[l+k|0]=0;if((k|0)!=0){j=0;do{f=l+j|0;if((a[f]|0)==32){a[f]=45}j=j+1|0;}while(j>>>0<k>>>0)}if((c[c[7118]>>2]|0)==3){tz(l);k=c[10028]|0;m=+g[e>>2]*+g[(c[7118]|0)+92>>2]*10.0;cf(k|0,205064,(v=i,i=i+16|0,c[v>>2]=l,h[v+8>>3]=m,v)|0);m=+g[e>>2];if(m!=+g[7200]){k=c[10028]|0;j=-~~(m*+g[(c[7118]|0)+92>>2]*10.0/3.0)|0;cf(k|0,205040,(v=i,i=i+8|0,c[v>>2]=j,v)|0);n=+g[e>>2]}else{n=m}g[7200]=n}uu(l);c[(c[3524]|0)+16>>2]=~~(+g[e>>2]*+g[(c[7118]|0)+92>>2]*10.0);c[(c[3524]|0)+20>>2]=~~(+g[e>>2]*+g[(c[7118]|0)+92>>2]*10.0*6.0/10.0);i=d;return 1}function pM(a){a=+a;var b=0;b=i;cf(c[10028]|0,205136,(v=i,i=i+8|0,h[v>>3]=a,v)|0);i=b;return}function pN(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0.0,n=0.0;j=i;if((c[7116]|0)!=0){k=c[10028]|0;aI(215952,7,1,k|0);c[7116]=0;a[228944]=0}k=b&15;if((k|0)==3){l=c[10028]|0;cf(l|0,205808,(v=i,i=i+32|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else if((k|0)==5){aI(205528,32,1,c[10028]|0)}else if((k|0)==1|(k|0)==4){m=+(b>>4|0)/100.0;n=m<0.0?0.0:m;cf(c[10028]|0,205672,(v=i,i=i+40|0,h[v>>3]=n>1.0?1.0:n,c[v+8>>2]=d,c[v+16>>2]=e,c[v+24>>2]=f,c[v+32>>2]=g,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else if((k|0)!=2){cf(c[10028]|0,205464,(v=i,i=i+32|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,v)|0);a[228944]=0;c[57432]=-5;i=j;return}k=(b>>4|0)%8&-1;if((k|0)==7){b=c[10028]|0;cf(b|0,205184,(v=i,i=i+48|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,c[v+32>>2]=40,c[v+40>>2]=-30,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else if((k|0)==3){cf(c[10028]|0,205240,(v=i,i=i+32|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else if((k|0)==1){cf(c[10028]|0,205352,(v=i,i=i+48|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,c[v+32>>2]=80,c[v+40>>2]=-45,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else if((k|0)==2){cf(c[10028]|0,205312,(v=i,i=i+48|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,c[v+32>>2]=40,c[v+40>>2]=45,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else if((k|0)==5){cf(c[10028]|0,205184,(v=i,i=i+48|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,c[v+32>>2]=80,c[v+40>>2]=-45,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else if((k|0)==6){cf(c[10028]|0,205184,(v=i,i=i+48|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,c[v+32>>2]=40,c[v+40>>2]=30,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else if((k|0)==4){cf(c[10028]|0,205184,(v=i,i=i+48|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,c[v+32>>2]=80,c[v+40>>2]=45,v)|0);a[228944]=0;c[57432]=-5;i=j;return}else{cf(c[10028]|0,205464,(v=i,i=i+32|0,c[v>>2]=d,c[v+8>>2]=e,c[v+16>>2]=f,c[v+24>>2]=g,v)|0);a[228944]=0;c[57432]=-5;i=j;return}}function pO(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0;f=i;i=i+16|0;g=f|0;h=f+8|0;c[g>>2]=e;if((e|0)==0){b7()}pG(b,d);d=c[7230]|0;if((d|0)!=0){b=c[10028]|0;cf(b|0,202848,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}L22516:do{if((c[11252]|0)==15){d=e;while(1){b=a[d]|0;if(b<<24>>24==0){j=16796;break L22516}if(b<<24>>24<0){break}else{d=d+1|0}}aF(91,c[10028]|0);ug(h,g);d=c[h>>2]|0;do{if((d|0)!=0){b=0;k=d;while(1){l=(b|0)==1;do{if(k>>>0<256){if(l){m=k}else{n=c[10028]|0;aF(40,n|0);m=c[h>>2]|0}if((m-40|0)>>>0<2|(m|0)==92){n=c[10028]|0;aF(92,n|0);o=c[h>>2]|0}else{o=m}n=o<<24>>24;p=c[10028]|0;aF(n|0,p|0);q=1}else{if(l){p=c[10028]|0;aF(41,p|0)}p=c[10028]|0;aF(47,p|0);p=c[7114]|0;n=c[18090]|0;r=c[h>>2]|0;s=0;while(1){if((s|0)>=(p|0)){t=p;break}if((c[n+(s<<3)>>2]|0)==(r|0)){j=16786;break}else{s=s+1|0}}if((j|0)==16786){j=0;r=c[n+(s<<3)+4>>2]|0;p=c[10028]|0;aK(r|0,p|0);t=c[7114]|0}if((s|0)<(t|0)){q=2;break}p=c[h>>2]|0;cf(c[10028]|0,(p>>>0>65535?215776:215728)|0,(v=i,i=i+8|0,c[v>>2]=p,v)|0);q=2}}while(0);ug(h,g);l=c[h>>2]|0;if((l|0)==0){break}else{b=q;k=l}}if((q|0)!=1){break}aF(41,c[10028]|0)}}while(0);d=c[7198]|0;if((d|0)==1){k=c[10028]|0;aI(202768,9,1,k|0);break}else if((d|0)==0){aI(202832,9,1,c[10028]|0);break}else if((d|0)==2){aI(202736,9,1,c[10028]|0);break}else{break}}else{j=16796}}while(0);do{if((j|0)==16796){q=c[10028]|0;aF(40,q|0);q=e+1|0;c[g>>2]=q;t=a[e]|0;o=t<<24>>24;c[h>>2]=o;if(t<<24>>24!=0){m=t;t=q;q=o;while(1){if((m-40&255)<2|m<<24>>24==92){o=c[10028]|0;aF(92,o|0);u=c[h>>2]|0}else{u=q}aF(u<<24>>24|0,c[10028]|0);w=t+1|0;o=a[t]|0;d=o<<24>>24;c[h>>2]=d;if(o<<24>>24==0){break}else{m=o;t=w;q=d}}c[g>>2]=w}q=c[7198]|0;if((q|0)==2){t=c[10028]|0;aI(202632,8,1,t|0);break}else if((q|0)==1){aI(202648,8,1,c[10028]|0);break}else if((q|0)==0){aI(202680,8,1,c[10028]|0);break}else{break}}}while(0);if((c[7230]|0)==0){c[7116]=0;a[228944]=0;i=f;return}aI(212688,9,1,c[10028]|0);c[7116]=0;a[228944]=0;i=f;return}function pP(b){b=+b;var d=0,e=0;d=i;e=c[7116]|0;if((e|0)!=0&+h[28714]==b){i=d;return}if((e|0)!=0){e=c[10028]|0;aI(215952,7,1,e|0);c[7116]=0;a[228944]=0}h[28715]=b;c[57432]=-5;cf(c[10028]|0,205832,(v=i,i=i+8|0,h[v>>3]=b,v)|0);i=d;return}function pQ(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0;d=i;i=i+8|0;e=d|0;if((b|0)==0){i=d;return 0}aI(210952,29,1,c[10028]|0);cf(c[10028]|0,210928,(v=i,i=i+8|0,c[v>>2]=c[5168],v)|0);f=0;do{aK(c[34936+(f<<2)>>2]|0,c[10028]|0);f=f+1|0;}while((f|0)!=27);f=c[5163]|0;L22585:do{if((f|0)==100){g=c[10028]|0;aI(210800,28,1,g|0);ty();g=c[b+44>>2]|0;j=c[b+40>>2]|0;tA(209040,g,j,0);tA(209e3,g,j,8);tA(208976,g,j,16);tA(208856,g,j,24)}else if((f|0)==99|(f|0)==102){c[e>>2]=0;aI(210800,28,1,c[10028]|0);ty();j=c[7118]|0;g=fs(0,c[j+100>>2]|0,+h[j+104>>3],e)|0;j=c[e>>2]|0;tA(209040,g,j,0);tA(209e3,g,j,8);tA(208976,g,j,16);tA(208856,g,j,24);uu(g)}else if((f|0)==103){aI(210856,29,1,c[10028]|0)}else if((f|0)==114){aI(210856,29,1,c[10028]|0);if((a[20684]|0)!=0){g=c[7122]|0;if((a[g]|0)==0){break}else{k=0;l=0;n=g}while(1){g=c[28488+((l|1)<<2)>>2]|0;cf(c[10028]|0,207920,(v=i,i=i+24|0,c[v>>2]=k,c[v+8>>2]=n,c[v+16>>2]=g,v)|0);k=k+1|0;l=k<<1;n=c[28488+(l<<2)>>2]|0;if((a[n]|0)==0){break L22585}}}g=c[5164]|0;j=(g|0)>-1?g:-g|0;g=j<<1;o=c[28488+(g<<2)>>2]|0;p=c[28488+((g|1)<<2)>>2]|0;cf(c[10028]|0,207920,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=o,c[v+16>>2]=p,v)|0);p=c[5165]|0;o=(p|0)>-1?p:-p|0;p=c[5164]|0;if((o|0)==(((p|0)>-1?p:-p|0)|0)){q=p}else{p=c[10028]|0;j=o<<1;g=c[28488+(j<<2)>>2]|0;r=c[28488+((j|1)<<2)>>2]|0;cf(p|0,207920,(v=i,i=i+24|0,c[v>>2]=o,c[v+8>>2]=g,c[v+16>>2]=r,v)|0);q=c[5164]|0}r=c[5166]|0;g=(r|0)>-1?r:-r|0;if((g|0)==(((q|0)>-1?q:-q|0)|0)){break}r=c[5165]|0;if((g|0)==(((r|0)>-1?r:-r|0)|0)){break}r=g<<1;o=c[28488+(r<<2)>>2]|0;p=c[28488+((r|1)<<2)>>2]|0;cf(c[10028]|0,207920,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=o,c[v+16>>2]=p,v)|0)}else{cf(c[m>>2]|0,210704,(v=i,i=i+24|0,c[v>>2]=210640,c[v+8>>2]=2883,c[v+16>>2]=f<<24>>24,v)|0)}}while(0);aI(210600,37,1,c[10028]|0);aI(210528,67,1,c[10028]|0);cf(c[10028]|0,210480,(v=i,i=i+8|0,h[v>>3]=+h[2705],v)|0);aI(209304,12,1,c[10028]|0);f=c[b+48>>2]|0;if((f|0)==121){b=c[10028]|0;aI(209160,5,1,b|0)}else if((f|0)==114){aI(209216,5,1,c[10028]|0)}else if((f|0)==120){aI(209152,5,1,c[10028]|0)}else if((f|0)==99){aI(209200,5,1,c[10028]|0)}else if((f|0)==104){aI(209208,5,1,c[10028]|0)}else{cf(c[m>>2]|0,209064,(v=i,i=i+24|0,c[v>>2]=210640,c[v+8>>2]=2794,c[v+16>>2]=f<<24>>24,v)|0);aI(209216,5,1,c[10028]|0)}aI(209056,5,1,c[10028]|0);f=c[10028]|0;if((c[5163]|0)==103){aI(210440,30,1,f|0)}else{aI(210368,51,1,f|0)}aI(210312,55,1,c[10028]|0);aI(210184,53,1,c[10028]|0);aI(210040,42,1,c[10028]|0);aI(210008,5,1,c[10028]|0);aI(209864,27,1,c[10028]|0);f=c[5164]|0;if((f|0)<0){b=c[10028]|0;aI(209784,11,1,b|0);s=c[5164]|0}else{s=f}cf(c[10028]|0,209696,(v=i,i=i+8|0,c[v>>2]=(s|0)>-1?s:-s|0,v)|0);s=c[5165]|0;if((s|0)<0){f=c[10028]|0;aI(209784,11,1,f|0);t=c[5165]|0}else{t=s}cf(c[10028]|0,209664,(v=i,i=i+8|0,c[v>>2]=(t|0)>-1?t:-t|0,v)|0);t=c[5166]|0;if((c[5165]|c[5164]|t|0)<0){s=c[10028]|0;aI(84248,2,1,s|0);u=c[5166]|0}else{u=t}if((u|0)<0){t=c[10028]|0;aI(209784,11,1,t|0);w=c[5166]|0}else{w=u}cf(c[10028]|0,209648,(v=i,i=i+8|0,c[v>>2]=(w|0)>-1?w:-w|0,v)|0);aI(209584,42,1,c[10028]|0);aI(209568,11,1,c[10028]|0);aI(209552,3,1,c[10028]|0);aI(209344,55,1,c[10028]|0);aI(213784,9,1,c[10028]|0);i=d;return 0}function pR(){var b=0;if((c[7116]|0)!=0){b=c[10028]|0;aI(215952,7,1,b|0);c[7116]=0;a[228944]=0}aI(211040,30,1,c[10028]|0);return}function pS(b){b=b|0;var d=0,e=0.0,f=0,g=0,j=0,k=0.0,l=0.0,m=0,n=0,o=0,p=0;d=i;c[57432]=-5;e=+h[28715];if(+h[28714]!=e){h[28714]=e;f=c[10028]|0;aI(211272,3,1,f|0)}f=b|0;g=c[f>>2]|0;if((g|0)==3){j=c[b+4>>2]|0;e=+(j>>>16&255|0)/255.0;k=+(j>>>8&255|0)/255.0;l=+(j&255|0)/255.0;if((c[7116]|0)!=0){j=c[10028]|0;aI(215952,7,1,j|0);c[7116]=0;a[228944]=0}j=c[10028]|0;cf(j|0,211112,(v=i,i=i+24|0,h[v>>3]=e,h[v+8>>3]=k,h[v+16>>3]=l,v)|0);m=16871}else if((g|0)==1){j=c[b+4>>2]|0;if((c[7116]|0)!=0){n=c[10028]|0;aI(215952,7,1,n|0);c[7116]=0;a[228944]=0}n=c[7118]|0;do{if((c[n>>2]|0)==2){if((a[n+112|0]&1)==0){m=16866;break}o=(j|0)%4&-1}else{m=16866}}while(0);if((m|0)==16866){o=(j|0)%9&-1}j=o+3|0;cf(c[10028]|0,211192,(v=i,i=i+8|0,c[v>>2]=a[93672+((j|0)<0?0:j)|0]|0,v)|0);m=16871}else{p=g}if((m|0)==16871){p=c[f>>2]|0}if((p|0)!=5){i=d;return}l=+h[b+8>>3];do{if(l>0.0){b=c[10028]|0;if(l<1.0){k=+(~~(l*1.0e4+.5)|0)*1.0e-4;be(25336,141840,(v=i,i=i+8|0,h[v>>3]=k,v)|0);p=(a[25336]|0)==48&(a[25337]|0)==46?25337:25336;cf(b|0,211072,(v=i,i=i+8|0,c[v>>2]=p,v)|0);break}else{aI(211080,4,1,b|0);break}}else{b=c[10028]|0;aI(211104,4,1,b|0)}}while(0);a[228944]=0;i=d;return}function pT(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0.0,t=0.0;e=i;f=c[d+8>>2]|0;if(a[228944]&(c[7116]|0)!=0){g=c[10028]|0;aI(215952,7,1,g|0);c[7116]=0;a[228944]=0}if((b|0)==4&(f|0)==1601){g=c[10028]|0;j=d|0;k=c[j>>2]|0;l=d+4|0;m=c[l>>2]|0;cf(g|0,211680,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=m,v)|0);m=c[10028]|0;k=c[d+24>>2]|0;g=(c[d+36>>2]|0)-k|0;n=c[d+28>>2]|0;o=(c[d+40>>2]|0)-n|0;p=c[d+12>>2]|0;q=k-p|0;k=c[d+16>>2]|0;r=n-k|0;n=p-(c[j>>2]|0)|0;j=k-(c[l>>2]|0)|0;cf(m|0,211600,(v=i,i=i+48|0,c[v>>2]=g,c[v+8>>2]=o,c[v+16>>2]=q,c[v+24>>2]=r,c[v+32>>2]=n,c[v+40>>2]=j,v)|0);a[228944]=0;i=e;return}j=f>>4;n=f&15;aI(211544,6,1,c[10028]|0);f=c[d+4>>2]|0;cf(c[10028]|0,211680,(v=i,i=i+16|0,c[v>>2]=c[d>>2],c[v+8>>2]=f,v)|0);if((b|0)>1){f=1;do{r=c[10028]|0;q=c[d+(f*12&-1)>>2]|0;if(((f|0)%105&-1|0)==0){o=c[d+(f*12&-1)+4>>2]|0;cf(r|0,211480,(v=i,i=i+16|0,c[v>>2]=q,c[v+8>>2]=o,v)|0)}else{o=f-1|0;g=q-(c[d+(o*12&-1)>>2]|0)|0;q=(c[d+(f*12&-1)+4>>2]|0)-(c[d+(o*12&-1)+4>>2]|0)|0;cf(r|0,211528,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=q,v)|0)}f=f+1|0;}while((f|0)<(b|0))}if((n|0)==1|(n|0)==4){s=+(j|0)/100.0;t=s<0.0?0.0:s;b=c[10028]|0;if(t<1.0){s=t;cf(b|0,211448,(v=i,i=i+8|0,h[v>>3]=s,v)|0);a[228944]=0;i=e;return}else{aI(211464,12,1,b|0);a[228944]=0;i=e;return}}else if((n|0)==5){aI(211416,31,1,c[10028]|0)}else if((n|0)!=2){aI(211464,12,1,c[10028]|0);a[228944]=0;i=e;return}n=(j|0)%8&-1;j=c[10028]|0;if((n|0)==0){cf(j|0,211400,(v=i,i=i+8|0,h[v>>3]=.5,v)|0);a[228944]=0;i=e;return}else{cf(j|0,211360,(v=i,i=i+8|0,c[v>>2]=n,v)|0);a[228944]=0;i=e;return}}function pU(){if(!(a[237912]|0)){return}aI(215624,3,1,c[10028]|0);a[237912]=0;return}function pV(b,e,f,g,j){b=b|0;e=e|0;f=f|0;g=g|0;j=j|0;var k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0.0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0.0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aG=0,aH=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0,aR=0,aS=0,aT=0,aU=0,aV=0,aW=0,aX=0;k=i;i=i+24|0;l=k|0;n=k+8|0;o=k+16|0;p=c[5168]|0;q=(p|0)>0?p:256;do{if((q|0)>1){p=1;r=0;do{r=r+1&65535;p=p<<1;}while((p|0)<(q|0));if(r<<16>>16==0|(r&65535)>12){s=r;break}do{if((r&65535)>8){t=12}else{if((r&65535)>4){t=8;break}t=(r&65535)>2?4:r}}while(0);r=(j|0)==1;do{if(r){u=16922}else{p=c[5163]|0;if((p|0)==103){u=16922;break}else if((p|0)!=114){w=1.0;x=0;break}if((a[(c[7118]|0)+17|0]&1)==0){u=16922}else{w=1.0;x=0}}}while(0);if((u|0)==16922){w=+((1<<(t&65535))-1|0)/+(q-1|0);x=1}p=t&65535;y=a[(c[7118]|0)+16|0]&1;z=l|0;A=aa(aa(aa(r?3:1,b),p)+7|0,e);if(y<<24>>24==0){y=((A>>>5)*5&-1)+7|0;B=((y|0)/78&-1)+y|0}else{y=(A>>>2)+1|0;B=((y|0)/78&-1)+y|0}y=ut(B)|0;do{if((y|0)==0){gk();A=ut(B)|0;if((A|0)!=0){C=A;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=211936,v)|0)}else{C=y}}while(0);y=aa(e,b);if(r){D=y*3&-1;E=b*3&-1}else{D=y;E=b}L22728:do{if((D|0)==0){F=C}else{G=+(q|0);A=q-1|0;H=A&65535;I=l+1|0;J=l+2|0;K=l+3|0;L=l+4|0;M=32;N=0;O=0;P=0;Q=C;R=78;S=f;T=D;while(1){U=S+8|0;V=~~(G*+h[S>>3]);W=~~(w*+((((V&65535|0)>(A|0)?H:V)&65535)>>>0));V=M&65535;if((M&65535)<(t&65535)){X=p-V|0;Y=(W&65535)>>>((X&65535)>>>0)|O<<V;Z=X&65535;_=0}else{Y=W&65535|O<<p;Z=N;_=M-t&65535}X=P+1|0;if((X|0)==(E|0)){V=_&7;$=0;ab=Y<<(V&65535);ac=_-V&65535}else{$=X;ab=Y;ac=_}if(ac<<16>>16==0){do{if((a[(c[7118]|0)+16|0]&1)==0){if((ab|0)!=0){X=(ab>>>0)/52200625>>>0;V=X&255;a[z]=V;ad=aa(X,-52200625)+ab|0;X=(ad>>>0)/614125>>>0;a[I]=X&255;ae=((X&255)*-614125&-1)+ad|0;ad=(ae>>>0)/7225>>>0;a[J]=ad&255;X=ae+((ad&255)*-7225&-1)|0;ad=(X>>>0)/85>>>0;a[K]=ad&255;a[L]=X+((ad&255)*-85&-1)&255;ad=Q+1|0;a[Q]=V+33&255;V=R<<16>>16==1;X=V?Q+2|0:ad;a[ad]=V?10:0;ad=X+1|0;a[X]=(a[I]|0)+33&255;ae=V?77:R-2&65535;V=ae<<16>>16==0;af=V?X+2|0:ad;a[ad]=V?10:0;ad=af+1|0;a[af]=(a[J]|0)+33&255;X=V?77:ae-1&65535;ae=X<<16>>16==0;V=ae?af+2|0:ad;a[ad]=ae?10:0;ad=V+1|0;a[V]=(a[K]|0)+33&255;af=ae?77:X-1&65535;X=af<<16>>16==0;ae=X?V+2|0:ad;a[ad]=X?10:0;ad=ae+1|0;a[ae]=(a[L]|0)+33&255;V=X?77:af-1&65535;af=V<<16>>16==0;a[ad]=af?10:0;ag=af?78:V;ah=af?ae+2|0:ad;break}ad=Q+1|0;a[Q]=122;ae=R-1&65535;if(ae<<16>>16!=0){ag=ae;ah=ad;break}a[ad]=10;ag=78;ah=Q+2|0}else{ad=ab>>>4;ae=ab>>>8;af=ab>>>12;V=ab>>>16;X=ab>>>20;ai=ab>>>24;aj=ab>>>28;ak=Q+1|0;be(Q|0,211896,(v=i,i=i+8|0,c[v>>2]=aj,v)|0);aj=R-1&65535;if(aj<<16>>16==0){a[ak]=10;al=78;am=Q+2|0}else{al=aj;am=ak}ak=am+1|0;aj=ai&15;be(am|0,211896,(v=i,i=i+8|0,c[v>>2]=aj,v)|0);aj=al-1&65535;if(aj<<16>>16==0){a[ak]=10;an=78;ao=am+2|0}else{an=aj;ao=ak}ak=ao+1|0;aj=X&15;be(ao|0,211896,(v=i,i=i+8|0,c[v>>2]=aj,v)|0);aj=an-1&65535;if(aj<<16>>16==0){a[ak]=10;ap=78;aq=ao+2|0}else{ap=aj;aq=ak}ak=aq+1|0;aj=V&15;be(aq|0,211896,(v=i,i=i+8|0,c[v>>2]=aj,v)|0);aj=ap-1&65535;if(aj<<16>>16==0){a[ak]=10;ar=78;as=aq+2|0}else{ar=aj;as=ak}ak=as+1|0;aj=af&15;be(as|0,211896,(v=i,i=i+8|0,c[v>>2]=aj,v)|0);aj=ar-1&65535;if(aj<<16>>16==0){a[ak]=10;at=78;au=as+2|0}else{at=aj;au=ak}ak=au+1|0;aj=ae&15;be(au|0,211896,(v=i,i=i+8|0,c[v>>2]=aj,v)|0);aj=at-1&65535;if(aj<<16>>16==0){a[ak]=10;av=78;aw=au+2|0}else{av=aj;aw=ak}ak=aw+1|0;aj=ad&15;be(aw|0,211896,(v=i,i=i+8|0,c[v>>2]=aj,v)|0);aj=av-1&65535;if(aj<<16>>16==0){a[ak]=10;ax=78;ay=aw+2|0}else{ax=aj;ay=ak}ak=ay+1|0;aj=ab&15;be(ay|0,211896,(v=i,i=i+8|0,c[v>>2]=aj,v)|0);aj=ax-1&65535;if(aj<<16>>16!=0){ag=aj;ah=ak;break}a[ak]=10;ag=78;ah=ay+2|0}}while(0);if(Z<<16>>16==0){az=0}else{ak=W&65535;aj=Z&65535;az=ak-(ak>>>(aj>>>0)<<aj)|0}aA=ag;aB=ah;aC=az;aD=0;aE=32-Z&65535}else{aA=R;aB=Q;aC=ab;aD=Z;aE=ac}aj=T-1|0;if((aj|0)==0){break}else{M=aE;N=aD;O=aC;P=$;Q=aB;R=aA;S=U;T=aj}}T=aE&65535;if((aE&65535)>=32){F=aB;break}S=4-(T>>>3)|0;if((a[(c[7118]|0)+16|0]&1)==0){R=aC<<T;T=(R>>>0)/52200625>>>0;Q=T&255;a[z]=Q;P=aa(T,-52200625)+R|0;R=(P>>>0)/614125>>>0;a[I]=R&255;T=((R&255)*-614125&-1)+P|0;P=(T>>>0)/7225>>>0;a[J]=P&255;R=T+((P&255)*-7225&-1)|0;P=(R>>>0)/85>>>0;a[K]=P&255;a[L]=R+((P&255)*-85&-1)&255;if((S|0)<0){F=aB;break}P=aB+1|0;a[aB]=Q+33&255;Q=aA-1&65535;R=Q<<16>>16==0;T=R?aB+2|0:P;a[P]=R?10:0;if((S|0)<1){F=T;break}else{aG=1;aH=Q;aJ=R;aK=T}while(1){T=aG+1|0;R=aK+1|0;a[aK]=(a[l+aG|0]|0)+33&255;Q=aJ?77:aH-1&65535;P=Q<<16>>16==0;O=P?aK+2|0:R;a[R]=P?10:0;if((T|0)>(S|0)){F=O;break L22728}else{aG=T;aH=Q;aJ=P;aK=O}}}L=S<<1;if((L|0)>0){aL=aC;aM=L}else{F=aB;break}while(1){K=aM-1|0;a[n+K|0]=aL&15;if((K|0)>0){aL=aL>>>4;aM=K}else{aN=aB;aO=aA;aP=0;break}}while(1){S=aN+1|0;be(aN|0,211896,(v=i,i=i+8|0,c[v>>2]=d[n+aP|0]|0,v)|0);K=aO-1&65535;if(K<<16>>16==0){a[S]=10;aQ=78;aR=aN+2|0}else{aQ=K;aR=S}S=aP+1|0;if((S|0)<(L|0)){aN=aR;aO=aQ;aP=S}else{F=aR;break}}}}while(0);if((a[(c[7118]|0)+16|0]&1)==0){a[F]=a[211872]|0;a[F+1|0]=a[211873|0]|0;a[F+2|0]=a[211874|0]|0;aS=F+2|0}else{aS=F}z=aS-C|0;if((z|0)>(B|0)){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=211696,v)|0)}aI(214464,15,1,c[10028]|0);r=c[g+24>>2]|0;L=c[g+28>>2]|0;S=c[g+40>>2]|0;K=c[g+36>>2]|0;cf(c[10028]|0,214416,(v=i,i=i+64|0,c[v>>2]=r,c[v+8>>2]=L,c[v+16>>2]=r,c[v+24>>2]=S,c[v+32>>2]=K,c[v+40>>2]=S,c[v+48>>2]=K,c[v+56>>2]=L,v)|0);do{if(x){if((a[(c[7118]|0)+16|0]&1)==0){L=c[10028]|0;aI(214344,62,1,L|0);L=c[10028]|0;K=g|0;S=c[K>>2]|0;r=g+4|0;J=c[r>>2]|0;cf(L|0,214312,(v=i,i=i+16|0,c[v>>2]=S,c[v+8>>2]=J,v)|0);J=c[10028]|0;S=g+12|0;L=(c[S>>2]|0)-(c[K>>2]|0)|0;cf(J|0,214296,(v=i,i=i+8|0,c[v>>2]=L,v)|0);L=c[10028]|0;J=(c[g+16>>2]|0)-(c[r>>2]|0)|0;cf(L|0,214272,(v=i,i=i+8|0,c[v>>2]=J,v)|0);J=c[10028]|0;L=(c[K>>2]|0)-(c[S>>2]|0)|0;cf(J|0,214296,(v=i,i=i+8|0,c[v>>2]=L,v)|0);L=c[10028]|0;J=c[K>>2]|0;K=c[r>>2]|0;cf(L|0,214256,(v=i,i=i+16|0,c[v>>2]=J,c[v+8>>2]=K,v)|0);K=c[10028]|0;aI(214128,67,1,K|0);K=c[10028]|0;cf(K|0,213920,(v=i,i=i+8|0,c[v>>2]=z,v)|0);K=c[10028]|0;aI(213848,38,1,K|0);tx(b,e,g,j,t);K=c[10028]|0;aI(213784,9,1,K|0);break}else{tx(b,e,g,j,t);break}}else{K=q-1|0;G=1.0/+(K|0);if((a[(c[7118]|0)+16|0]&1)==0){J=c[10028]|0;aI(214344,62,1,J|0);J=c[10028]|0;L=g|0;r=c[L>>2]|0;S=g+4|0;I=c[S>>2]|0;cf(J|0,214312,(v=i,i=i+16|0,c[v>>2]=r,c[v+8>>2]=I,v)|0);I=c[10028]|0;r=g+12|0;J=(c[r>>2]|0)-(c[L>>2]|0)|0;cf(I|0,214296,(v=i,i=i+8|0,c[v>>2]=J,v)|0);J=c[10028]|0;I=g+16|0;O=(c[I>>2]|0)-(c[S>>2]|0)|0;cf(J|0,214272,(v=i,i=i+8|0,c[v>>2]=O,v)|0);O=c[10028]|0;J=(c[L>>2]|0)-(c[r>>2]|0)|0;cf(O|0,214296,(v=i,i=i+8|0,c[v>>2]=J,v)|0);J=c[10028]|0;O=c[L>>2]|0;P=c[S>>2]|0;cf(J|0,214256,(v=i,i=i+16|0,c[v>>2]=O,c[v+8>>2]=P,v)|0);P=c[10028]|0;cf(P|0,213648,(v=i,i=i+8|0,c[v>>2]=z,v)|0);P=c[10028]|0;aI(213608,4,1,P|0);aT=L;aU=S;aV=r;aW=I}else{aT=g|0;aU=g+4|0;aV=g+12|0;aW=g+16|0}I=c[10028]|0;aI(213600,6,1,I|0);I=c[10028]|0;r=c[aT>>2]|0;S=c[aU>>2]|0;cf(I|0,213576,(v=i,i=i+16|0,c[v>>2]=r,c[v+8>>2]=S,v)|0);S=c[10028]|0;r=(c[aV>>2]|0)-(c[aT>>2]|0)|0;I=(c[aW>>2]|0)-(c[aU>>2]|0)|0;cf(S|0,213528,(v=i,i=i+16|0,c[v>>2]=r,c[v+8>>2]=I,v)|0);I=c[10028]|0;aI(213504,17,1,I|0);I=c[10028]|0;cf(I|0,213456,(v=i,i=i+8|0,c[v>>2]=K,v)|0);K=o|0;I=o+1|0;r=o+2|0;S=0;L=0;while(1){fr(G*+(S|0),o);if(L<<16>>16==0){P=c[10028]|0;aI(152656,3,1,P|0);aX=8}else{aX=L}P=d[I]|0;O=d[r]|0;cf(c[10028]|0,213376,(v=i,i=i+24|0,c[v>>2]=d[K]|0,c[v+8>>2]=P,c[v+16>>2]=O,v)|0);O=S+1|0;if((O|0)<(q|0)){S=O;L=aX-1&65535}else{break}}aI(213312,21,1,c[10028]|0);aI(213272,15,1,c[10028]|0);cf(c[10028]|0,213216,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=e,v)|0);cf(c[10028]|0,213144,(v=i,i=i+24|0,c[v>>2]=p,c[v+8>>2]=b,c[v+16>>2]=e,v)|0);cf(c[10028]|0,213120,(v=i,i=i+8|0,c[v>>2]=(1<<p)-1,v)|0);L=c[10028]|0;if((a[(c[7118]|0)+16|0]&1)==0){aI(212968,48,1,L|0)}else{S=(aa(p,y)+7|0)>>>3;cf(L|0,213920,(v=i,i=i+8|0,c[v>>2]=S,v)|0);S=c[10028]|0;aI(213056,55,1,S|0)}aI(212936,29,1,c[10028]|0);aI(212896,30,1,c[10028]|0);if((a[(c[7118]|0)+16|0]&1)!=0){break}aI(213784,9,1,c[10028]|0)}}while(0);if((z|0)>0){y=0;p=C;while(1){aF(a[p]|0|0,c[10028]|0);S=y+1|0;if((S|0)<(z|0)){y=S;p=p+1|0}else{break}}}p=c[10028]|0;if((a[(c[7118]|0)+16|0]&1)==0){aI(212776,39,1,p|0)}else{aI(212832,10,1,p|0)}aI(212688,9,1,c[10028]|0);aI(212656,13,1,c[10028]|0);uu(C);i=k;return}else{s=0}}while(0);cf(c[m>>2]|0,214504,(v=i,i=i+8|0,c[v>>2]=s&65535,v)|0);i=k;return}function pW(b,d,e,f,g,j){b=b|0;d=+d;e=+e;f=f|0;g=g|0;j=j|0;var k=0,l=0,m=0,n=0,o=0;k=i;if((j|0)==4){l=c[10028]|0;aI(215544,10,1,l|0);i=k;return}else if((j|0)==3){aI(215600,7,1,c[10028]|0);i=k;return}else{if(a[237912]|0){i=k;return}l=(uA(b|0)|0)+40|0;uu(c[59476]|0);m=ut(l)|0;do{if((m|0)==0){gk();n=ut(l)|0;if((n|0)!=0){o=n;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215504,v)|0)}else{o=m}}while(0);c[59476]=o;tz(b);a0(c[59476]|0,l|0,215400,(v=i,i=i+48|0,c[v>>2]=b,h[v+8>>3]=d,h[v+16>>3]=e,c[v+24>>2]=f?225e3:224992,c[v+32>>2]=g?225e3:224992,c[v+40>>2]=j,v)|0);cf(c[10028]|0,215368,(v=i,i=i+8|0,c[v>>2]=c[59476],v)|0);a[237912]=1;i=k;return}}function pX(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;d=i;i=i+16|0;e=d|0;f=d+8|0;g=c[11252]|0;if((g|0)!=15){h=(g|0)==14;g=c[10028]|0;aF(b|0,g|0);if(!h){i=d;return}if(a[238472]|0){a[238472]=0;if((b|0)!=92){i=d;return}h=c[10028]|0;aF(92,h|0);i=d;return}else{if((b&128|0)==0){i=d;return}a[238472]=1;i=d;return}}if((b&128|0)==0){h=c[10028]|0;aF(b|0,h|0);i=d;return}h=c[59616]|0;if((h|0)==0){do{if((b&224|0)==192){j=2;k=17022}else{if((b&240|0)==224){j=3;k=17022;break}g=(b&248|0)==240;c[59614]=g?4:0;if(g){break}g=c[10028]|0;aF(b|0,g|0);i=d;return}}while(0);if((k|0)==17022){c[59614]=j}c[59616]=1;a[238448]=b&255;i=d;return}j=h+1|0;c[59616]=j;a[h+238448|0]=b&255;b=c[59614]|0;if((j|0)<(b|0)){i=d;return}c[e>>2]=0;c[f>>2]=238448;a[b+238448|0]=0;c[59616]=0;ug(e,f);f=c[e>>2]|0;if(f>>>0<256){b=c[10028]|0;aF(f|0,b|0);i=d;return}if(a[237912]|0){b=c[10028]|0;aI(215624,3,1,b|0);a[237912]=0}cf(c[10028]|0,215784,(v=i,i=i+8|0,c[v>>2]=c[59476],v)|0);b=c[7114]|0;f=c[18090]|0;j=c[e>>2]|0;h=0;while(1){if((h|0)>=(b|0)){l=b;break}if((c[f+(h<<3)>>2]|0)==(j|0)){k=17034;break}else{h=h+1|0}}if((k|0)==17034){k=c[f+(h<<3)+4>>2]|0;f=c[10028]|0;aK(k|0,f|0);l=c[7114]|0}if((h|0)>=(l|0)){l=c[10028]|0;h=c[e>>2]|0;e=h>>>0>65535?215776:215728;cf(l|0,e|0,(v=i,i=i+8|0,c[v>>2]=h,v)|0)}aI(89968,2,1,c[10028]|0);a[237912]=0;i=d;return}function pY(a){a=a|0;var b=0,d=0,e=0;b=i;if((a|0)==13){d=c[10028]|0;cf(d|0,93784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=b;return}else if((a|0)==7){cf(c[10028]|0,215840,(v=i,i=i+8|0,c[v>>2]=c[57434],v)|0);i=b;return}else if((a|0)==0){c[57434]=0;i=b;return}else if((a|0)==6){d=c[10028]|0;e=(c[57434]|0)+1|0;c[57434]=e;cf(d|0,215920,(v=i,i=i+8|0,c[v>>2]=e,v)|0);i=b;return}else if((a|0)==12){cf(c[10028]|0,93800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=b;return}else{i=b;return}}function pZ(b){b=b|0;var d=0;if((b|0)==0){if((c[7116]|0)!=0){d=c[10028]|0;aI(215952,7,1,d|0);c[7116]=0;a[228944]=0}a[229704]=1;return}else if((b|0)==1){aI(78256,2,1,c[10028]|0);if((c[7116]|0)==0){return}aI(215952,7,1,c[10028]|0);c[7116]=0;a[228944]=0;return}else{return}}function p_(){aI(215984,51,1,c[10030]|0);return}function p$(){aI(216040,6,1,c[10030]|0);return}function p0(){aI(216080,9,1,c[10030]|0);return}function p1(){aI(216176,5,1,c[10030]|0);return}function p2(a,b){a=a|0;b=b|0;var d=0;d=i;cf(c[10030]|0,216272,(v=i,i=i+16|0,c[v>>2]=a+1e3,c[v+8>>2]=6999-b,v)|0);i=d;return}function p3(a,b){a=a|0;b=b|0;var d=0;d=i;cf(c[10030]|0,216304,(v=i,i=i+16|0,c[v>>2]=a+1e3,c[v+8>>2]=6999-b,v)|0);i=d;return}function p4(a){a=a|0;var b=0,d=0;b=i;c[6954]=a;if((a|0)>8){d=(a|0)%9&-1}else{d=a}a=d+2|0;cf(c[10030]|0,216464,(v=i,i=i+8|0,c[v>>2]=c[228840+(a<<2)>>2],v)|0);d=c[228888+(a<<2)>>2]|0;if((d|0)==1){a=c[10030]|0;aI(216400,16,1,a|0);i=b;return}else if((d|0)==3){aI(216352,16,1,c[10030]|0);i=b;return}else if((d|0)==2){aI(216376,16,1,c[10030]|0);i=b;return}else{aI(216344,4,1,c[10030]|0);i=b;return}}function p5(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=i;cf(c[10030]|0,216272,(v=i,i=i+16|0,c[v>>2]=b+1e3,c[v+8>>2]=6959-d,v)|0);aI(216552,5,1,c[10030]|0);d=e;while(1){e=a[d]|0;b=d+1|0;g=e<<24>>24;if((e<<24>>24|0)==94){h=c[10030]|0;aF(94,h|0)}else if((e<<24>>24|0)==0){break}aF(g|0,c[10030]|0);d=b}aI(216504,6,1,c[10030]|0);p4(c[6954]|0);i=f;return}function p6(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0.0,_=0,$=0.0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0.0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aI=0,aJ=0,aK=0,aL=0.0,aM=0,aN=0;b=i;i=i+200|0;d=b|0;e=b+24|0;f=b+48|0;g=b+72|0;j=b+96|0;k=b+120|0;l=b+144|0;m=b+168|0;n=b+192|0;if((c[13898]|0)==2){o=2}else{c[56738]=6e3;c[56734]=4800;a[228464]=0;uu(c[57106]|0);c[57106]=bP(216600)|0;h[28551]=12.0;p=c[57122]|0;if((p|0)!=0){uu(p)}c[57122]=0;a[228312]=0;a[226968]=0;uu(c[57076]|0);c[57076]=0;uu(c[56744]|0);c[56744]=0;a[228360]=0;a[228336]=0;o=c[13898]|0}p=c[8272]|0;L22958:do{if((o|0)<(p|0)){q=k|0;r=k+8|0;s=j|0;t=j+8|0;u=g|0;w=g+8|0;x=d|0;y=d+8|0;z=e|0;A=e+8|0;B=f|0;C=f+8|0;D=m|0;E=m+8|0;F=l|0;G=l+8|0;H=o;I=p;L22960:while(1){J=c[1054]|0;K=(a[J+(H*40&-1)|0]&1)==0;L=c[J+(H*40&-1)+36>>2]|0;M=J+(H*40&-1)+32|0;L22962:do{if(K){N=c[M>>2]|0;O=17164}else{P=c[10036]|0;Q=0;while(1){if((Q|0)>=(L|0)){O=17114;break}if((a[P+((c[M>>2]|0)+Q|0)|0]|0)==(a[Q+103664|0]|0)){Q=Q+1|0}else{break}}if((O|0)==17114){O=0;if((Q|0)==1){break L22958}}P=c[M>>2]|0;if(!((L|0)>0&(K^1))){N=P;O=17164;break}R=c[10036]|0;S=0;T=0;U=P;while(1){if((a[S+218680|0]|0)==(a[R+(S+U|0)|0]|0)){V=U;W=T}else{if((S|0)!=1){N=P;O=17164;break L22962}V=U-1|0;W=1}X=S+1|0;if((X|0)<(W+L|0)){S=X;T=W;U=V}else{break}}if((W|0)==0){if(!((S|0)==4|(S|0)==0)){N=P;O=17164;break}}Y=H+1|0;c[13898]=Y;if((Y|0)>=(I|0)){O=17424;break L22960}L22982:do{if((a[J+(Y*40&-1)|0]&1)!=0){U=c[J+(Y*40&-1)+36>>2]|0;T=J+(Y*40&-1)+32|0;R=c[10036]|0;Q=0;while(1){if((Q|0)>=(U|0)){break}if((a[R+((c[T>>2]|0)+Q|0)|0]|0)==(a[Q+103664|0]|0)){Q=Q+1|0}else{break L22982}}if((Q|0)==1){O=17425;break L22960}}}while(0);P=is(m)|0;S=c[P>>2]|0;if((S|0)==1){Z=+(c[P+8>>2]|0)}else if((S|0)==3){Z=+uz(c[P+8>>2]|0,0)}else if((S|0)==2){Z=+h[P+8>>3]}else{O=17134;break L22960}if((c[D>>2]|0)==3){uu(c[E>>2]|0);c[D>>2]=1}if(Z<2.0){O=17138;break L22960}c[56738]=~~(Z*10.0);P=c[13898]|0;S=c[8272]|0;L22998:do{if((S|0)>(P|0)){T=c[1054]|0;if((a[T+(P*40&-1)|0]&1)==0){_=P;break}R=c[T+(P*40&-1)+36>>2]|0;U=T+(P*40&-1)+32|0;T=c[10036]|0;X=0;while(1){if((X|0)>=(R|0)){break}if((a[T+((c[U>>2]|0)+X|0)|0]|0)==(a[X+148464|0]|0)){X=X+1|0}else{_=P;break L22998}}if((X|0)!=1){_=P;break}U=P+1|0;c[13898]=U;_=U}else{_=P}}while(0);if((_|0)>=(S|0)){O=17439;break L22960}P=c[1054]|0;L23008:do{if((a[P+(_*40&-1)|0]&1)!=0){U=c[P+(_*40&-1)+36>>2]|0;T=P+(_*40&-1)+32|0;R=c[10036]|0;Q=0;while(1){if((Q|0)>=(U|0)){break}if((a[R+((c[T>>2]|0)+Q|0)|0]|0)==(a[Q+103664|0]|0)){Q=Q+1|0}else{break L23008}}if((Q|0)==1){O=17440;break L22960}}}while(0);P=is(l)|0;S=c[P>>2]|0;if((S|0)==2){$=+h[P+8>>3]}else if((S|0)==1){$=+(c[P+8>>2]|0)}else if((S|0)==3){$=+uz(c[P+8>>2]|0,0)}else{O=17157;break L22960}if((c[F>>2]|0)==3){uu(c[G>>2]|0);c[F>>2]=1}if($<2.0){O=17161;break L22960}c[56734]=~~($*10.0)}}while(0);L23025:do{if((O|0)==17164){O=0;M=(I|0)>(H|0);L23027:do{if(M){if(K){O=17427;break L22960}P=c[10036]|0;S=0;while(1){if((S|0)>=(L|0)){break}if((a[P+(N+S|0)|0]|0)==(a[S+218536|0]|0)){S=S+1|0}else{O=17170;break L23027}}if((S|0)!=5){O=17170}}else{O=17170}}while(0);L23034:do{if((O|0)==17170){O=0;L23036:do{if((L|0)>0&(K^1)){P=c[10036]|0;Q=0;T=0;R=N;while(1){if((a[Q+218504|0]|0)==(a[P+(Q+R|0)|0]|0)){aa=R;ab=T}else{if((Q|0)!=4){break}aa=R-1|0;ab=1}U=Q+1|0;if((U|0)<(ab+L|0)){Q=U;T=ab;R=aa}else{O=17176;break}}if((O|0)==17176){O=0;if((ab|0)!=0){break L23034}if((Q|0)==3|(Q|0)==7){break L23034}}if(!((L|0)>0&(K^1))){break}R=c[10036]|0;T=0;P=0;U=N;while(1){if((a[T+130272|0]|0)==(a[R+(T+U|0)|0]|0)){ac=U;ad=P}else{if((T|0)!=5){break L23036}ac=U-1|0;ad=1}X=T+1|0;if((X|0)<(ad+L|0)){T=X;P=ad;U=ac}else{break}}if((ad|0)==0){if(!((T|0)==10|(T|0)==4)){break}}c[13898]=H+1;a[226968]=1;break L23025}}while(0);L23060:do{if(M){if(K){O=17431;break L22960}S=c[10036]|0;U=0;while(1){if((U|0)>=(L|0)){O=17193;break}if((a[S+(N+U|0)|0]|0)==(a[U+218432|0]|0)){U=U+1|0}else{break}}do{if((O|0)==17193){O=0;if((U|0)!=4){break}ae=H+1|0;c[13898]=ae;if((ae|0)>=(I|0)){O=17202;break L22960}L23070:do{if((a[J+(ae*40&-1)|0]&1)!=0){T=c[J+(ae*40&-1)+36>>2]|0;P=J+(ae*40&-1)+32|0;R=0;while(1){if((R|0)>=(T|0)){break}if((a[S+((c[P>>2]|0)+R|0)|0]|0)==(a[R+103664|0]|0)){R=R+1|0}else{break L23070}}if((R|0)==1){O=17202;break L22960}}}while(0);a[14176]=1;is(k);a[14176]=0;if((c[q>>2]|0)!=3){O=17201;break L22960}P=c[r>>2]|0;c[57076]=P;if((P|0)==0){O=17204;break L22960}if((a[P+(b4(P|0,218288)|0)|0]|0)==0){break L23025}else{O=17207;break L22960}}}while(0);if(!M){break}if(K){O=17430;break L22960}S=c[10036]|0;U=0;while(1){if((U|0)>=(L|0)){break}if((a[S+(N+U|0)|0]|0)==(a[U+221440|0]|0)){U=U+1|0}else{break L23060}}if((U|0)!=5){break}P=H+1|0;c[13898]=P;L23087:do{if((P|0)<(I|0)){L23089:do{if((a[J+(P*40&-1)|0]&1)!=0){T=c[J+(P*40&-1)+36>>2]|0;Q=J+(P*40&-1)+32|0;X=0;while(1){if((X|0)>=(T|0)){break}if((a[S+((c[Q>>2]|0)+X|0)|0]|0)==(a[X+103664|0]|0)){X=X+1|0}else{break L23089}}if((X|0)==1){af=0;break L23087}}}while(0);a[14176]=1;is(j);a[14176]=0;if((c[s>>2]|0)==3){af=c[t>>2]|0;break}else{c[13898]=P;af=0;break}}else{af=0}}while(0);c[56744]=af;break L23025}}while(0);L23101:do{if((L|0)>0&(K^1)){P=c[10036]|0;S=0;U=0;Q=N;while(1){if((a[S+218224|0]|0)==(a[P+(S+Q|0)|0]|0)){ag=Q;ah=U}else{if((S|0)!=1){break}ag=Q-1|0;ah=1}T=S+1|0;if((T|0)<(ah+L|0)){S=T;U=ah;Q=ag}else{O=17230;break}}do{if((O|0)==17230){O=0;if((ah|0)==0){if(!((S|0)==7|(S|0)==0)){break}}c[13898]=H+1;a[228464]=1;break L23025}}while(0);if(!((L|0)>0&(K^1))){O=17352;break}S=c[10036]|0;Q=0;U=0;P=N;while(1){if((a[Q+218152|0]|0)==(a[S+(Q+P|0)|0]|0)){ai=P;aj=U}else{if((Q|0)!=2){break}ai=P-1|0;aj=1}T=Q+1|0;if((T|0)<(aj+L|0)){Q=T;U=aj;P=ai}else{O=17239;break}}do{if((O|0)==17239){O=0;if((aj|0)==0){if(!((Q|0)==5|(Q|0)==1)){break}}c[13898]=H+1;a[228464]=0;break L23025}}while(0);if(!((L|0)>0&(K^1))){O=17352;break}Q=c[10036]|0;P=0;U=0;S=N;while(1){if((a[P+122880|0]|0)==(a[Q+(P+S|0)|0]|0)){ak=S;al=U}else{if((P|0)!=3){break}ak=S-1|0;al=1}T=P+1|0;if((T|0)<(al+L|0)){P=T;U=al;S=ak}else{O=17248;break}}do{if((O|0)==17248){O=0;if((al|0)==0){if(!((P|0)==8|(P|0)==2)){break}}c[13898]=H+1;c[(c[3524]|0)+68>>2]=2;S=(c[3524]|0)+96|0;c[S>>2]=c[S>>2]|32;break L23025}}while(0);if(!((L|0)>0&(K^1))){O=17352;break}P=c[10036]|0;S=0;U=0;Q=N;while(1){if((a[S+121536|0]|0)==(a[P+(S+Q|0)|0]|0)){am=Q;an=U}else{if((S|0)!=5){break}am=Q-1|0;an=1}T=S+1|0;if((T|0)<(an+L|0)){S=T;U=an;Q=am}else{O=17257;break}}do{if((O|0)==17257){O=0;if((an|0)==0){if(!((S|0)==10|(S|0)==4)){break}}c[13898]=H+1;c[(c[3524]|0)+68>>2]=68;Q=(c[3524]|0)+96|0;c[Q>>2]=c[Q>>2]&-33;break L23025}}while(0);if(!((L|0)>0&(K^1))){O=17352;break}S=c[10036]|0;Q=0;U=0;P=N;while(1){if((a[Q+218144|0]|0)==(a[S+(Q+P|0)|0]|0)){ao=P;ap=U}else{if((Q|0)!=2){O=17268;break}ao=P-1|0;ap=1}T=Q+1|0;if((T|0)<(ap+L|0)){Q=T;U=ap;P=ao}else{O=17266;break}}do{if((O|0)==17266){O=0;if((ap|0)!=0){break}if(!((Q|0)==1|(Q|0)==5)){O=17268}}}while(0);L23164:do{if((O|0)==17268){O=0;if(K){O=17352;break L23101}L23167:do{if((L|0)>0){Q=c[10036]|0;P=0;do{if((a[P+124352|0]|0)!=(a[Q+(P+N|0)|0]|0)){break L23167}P=P+1|0;}while((P|0)<(L|0));if((P|0)==4){break L23164}}}while(0);if(!((L|0)>0&(K^1))){O=17352;break L23101}Q=c[10036]|0;X=0;U=0;S=N;while(1){if((a[X+218024|0]|0)==(a[Q+(X+S|0)|0]|0)){aq=S;ar=U}else{if((X|0)!=2){break}aq=S-1|0;ar=1}T=X+1|0;if((T|0)<(ar+L|0)){X=T;U=ar;S=aq}else{O=17306;break}}do{if((O|0)==17306){O=0;if((ar|0)==0){if(!((X|0)==5|(X|0)==1)){break}}as=H+1|0;c[13898]=as;if((as|0)>=(I|0)){O=17438;break L22960}L23187:do{if((a[J+(as*40&-1)|0]&1)!=0){S=c[J+(as*40&-1)+36>>2]|0;U=J+(as*40&-1)+32|0;Q=c[10036]|0;T=0;while(1){if((T|0)>=(S|0)){break}if((a[Q+((c[U>>2]|0)+T|0)|0]|0)==(a[T+103664|0]|0)){T=T+1|0}else{break L23187}}if((T|0)==1){O=17437;break L22960}}}while(0);P=is(e)|0;U=c[P>>2]|0;if((U|0)==1){at=+(c[P+8>>2]|0)}else if((U|0)==2){at=+h[P+8>>3]}else if((U|0)==3){at=+uz(c[P+8>>2]|0,0)}else{O=17319;break L22960}if((c[z>>2]|0)==3){uu(c[A>>2]|0);c[z>>2]=1}h[28551]=at;break L23025}}while(0);if(!((L|0)>0&(K^1))){O=17352;break L23101}X=c[10036]|0;P=0;while(1){if((a[P+217952|0]|0)!=(a[X+(P+N|0)|0]|0)){break}au=P+1|0;if((au|0)<(L|0)){P=au}else{O=17327;break}}do{if((O|0)==17327){O=0;if((au|0)!=8){break}av=H+1|0;c[13898]=av;if((av|0)>=(I|0)){O=17336;break L22960}L23211:do{if((a[J+(av*40&-1)|0]&1)!=0){P=c[J+(av*40&-1)+36>>2]|0;X=J+(av*40&-1)+32|0;U=c[10036]|0;Q=0;while(1){if((Q|0)>=(P|0)){break}if((a[U+((c[X>>2]|0)+Q|0)|0]|0)==(a[Q+103664|0]|0)){Q=Q+1|0}else{break L23211}}if((Q|0)==1){O=17336;break L22960}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[x>>2]|0)!=3){O=17335;break L22960}X=c[y>>2]|0;c[n>>2]=X;if((X|0)==0){O=17338;break L22960}iQ(n);aw=c[n>>2]|0;if((a[aw]|0)==60){ax=aw}else{ax=hB(aw)|0}c[57122]=ax;if((ax|0)==0){O=17343;break L22960}else{break L23025}}}while(0);if(!((L|0)>0&(K^1))){O=17352;break L23101}X=c[10036]|0;U=0;P=0;T=N;while(1){if((a[U+128400|0]|0)==(a[X+(U+T|0)|0]|0)){ay=T;az=P}else{if((U|0)!=5){O=17352;break L23101}ay=T-1|0;az=1}S=U+1|0;if((S|0)<(az+L|0)){U=S;P=az;T=ay}else{break}}if((az|0)!=0){break L23101}if((U|0)==4|(U|0)==9){break L23101}else{O=17352;break L23101}}}while(0);aA=H+1|0;c[13898]=aA;if((aA|0)>=(I|0)){aB=aA;O=17445;break L22960}L23235:do{if((a[J+(aA*40&-1)|0]&1)!=0){T=c[J+(aA*40&-1)+36>>2]|0;P=J+(aA*40&-1)+32|0;X=c[10036]|0;S=0;while(1){if((S|0)>=(T|0)){break}if((a[X+((c[P>>2]|0)+S|0)|0]|0)==(a[S+103664|0]|0)){S=S+1|0}else{break L23235}}if((S|0)==1){aB=aA;O=17446;break L22960}}}while(0);a[14176]=1;is(f);a[14176]=0;if((c[B>>2]|0)!=3){O=17281;break L22960}P=c[C>>2]|0;if((P|0)==0){O=17283;break L22960}X=bh(P|0,44)|0;do{if((X|0)!=0){if((ca(X+1|0,148448,(v=i,i=i+8|0,c[v>>2]=228408,v)|0)|0)!=1){break}a[X]=0}}while(0);if((a[P]|0)==0){uu(P);break L23025}X=aQ(P|0,75096)|0;if((X|0)==0){aC=aQ(P|0,75048)|0}else{aC=X}X=aQ(P|0,74992)|0;if((X|0)==0){aD=aQ(P|0,74976)|0}else{aD=X}uu(c[57106]|0);c[57106]=P;if((aD|0)==0){c[57098]=77432}else{c[57098]=74936;X=uA(P|0)|0;a[P+(X-(uA(aD|0)|0)|0)|0]=0}if((aC|0)==0){c[57094]=77432;break L23025}else{c[57094]=74928;X=uA(P|0)|0;T=X-(uA(aC|0)|0)|0;a[(c[57106]|0)+T|0]=0;break L23025}}else{O=17352}}while(0);L23264:do{if((O|0)==17352){O=0;L23266:do{if(M){if(K){O=17429;break L22960}T=c[10036]|0;X=0;while(1){if((X|0)>=(L|0)){break}if((a[T+(N+X|0)|0]|0)==(a[X+128296|0]|0)){X=X+1|0}else{break L23266}}if((X|0)==2){break L23264}}}while(0);L23274:do{if((L|0)>0&(K^1)){P=c[10036]|0;T=0;S=0;U=N;while(1){if((a[T+217776|0]|0)==(a[P+(T+U|0)|0]|0)){aE=U;aF=S}else{if((T|0)!=5){break L23274}aE=U-1|0;aF=1}R=T+1|0;if((R|0)<(aF+L|0)){T=R;S=aF;U=aE}else{break}}if((aF|0)==0){if(!((T|0)==7|(T|0)==4)){break}}c[13898]=H+1;a[226984]=1;break L23025}}while(0);L23287:do{if(M){L23289:do{if(!K){U=c[10036]|0;S=0;while(1){if((S|0)>=(L|0)){break}if((a[U+(N+S|0)|0]|0)==(a[S+137232|0]|0)){S=S+1|0}else{break L23289}}if((S|0)!=4){break}c[13898]=H+1;a[226984]=0;break L23025}}while(0);if(!M){break}if(K){O=17428;break L22960}T=c[10036]|0;U=0;while(1){if((U|0)>=(L|0)){break}if((a[T+(N+U|0)|0]|0)==(a[U+131504|0]|0)){U=U+1|0}else{break L23287}}if((U|0)!=5){break}c[13898]=H+1;a[228520]=0;break L23025}}while(0);if(!((L|0)>0&(K^1))){O=17434;break L22960}T=c[10036]|0;P=0;X=0;R=N;while(1){if((a[P+217752|0]|0)==(a[T+(P+R|0)|0]|0)){aG=R;aH=X}else{if((P|0)!=4){break}aG=R-1|0;aH=1}aI=P+1|0;if((aI|0)<(aH+L|0)){P=aI;X=aH;R=aG}else{O=17396;break}}do{if((O|0)==17396){O=0;if((aH|0)==0){if(!((P|0)==6|(P|0)==3)){break}}c[13898]=H+1;a[228520]=1;break L23025}}while(0);if(!((L|0)>0&(K^1))){O=17432;break L22960}P=c[10036]|0;R=0;X=0;T=N;while(1){if((a[R+217736|0]|0)==(a[P+(R+T|0)|0]|0)){aJ=T;aK=X}else{if((R|0)!=5){O=17426;break L22960}aJ=T-1|0;aK=1}aI=R+1|0;if((aI|0)<(aK+L|0)){R=aI;X=aK;T=aJ}else{break}}if((aK|0)==0){if(!((R|0)==10|(R|0)==4)){O=17433;break L22960}}c[13898]=H+1;c[57136]=hK()|0;break L23025}}while(0);c[13898]=H+1;T=is(g)|0;X=c[T>>2]|0;if((X|0)==3){aL=+uz(c[T+8>>2]|0,0)}else if((X|0)==1){aL=+(c[T+8>>2]|0)}else if((X|0)==2){aL=+h[T+8>>3]}else{O=17362;break L22960}if((c[u>>2]|0)==3){uu(c[w>>2]|0);c[u>>2]=1}h[28540]=aL;if(aL>0.0){break L23025}h[28540]=1.0;break L23025}}while(0);c[13898]=H+1;a[228312]=1}}while(0);L=c[13898]|0;K=c[8272]|0;if((L|0)<(K|0)){H=L;I=K}else{break L22958}}if((O|0)==17343){uf(c[13898]|0,219072,(v=i,i=i+8|0,c[v>>2]=aw,v)|0)}else if((O|0)==17134){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17434){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17437){uf(as,217992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17438){uf(as,217992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17440){uf(_,218600,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17445){uf(aB,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17362){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17161){uf(c[13898]|0,218544,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17424){uf(Y,218656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17425){uf(Y,218656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17426){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17427){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17428){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17439){uf(_,218600,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17335){c[13898]=av;O=17336}else if((O|0)==17446){uf(aB,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17281){c[13898]=aA;aB=aA;uf(aB,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17283){aB=c[13898]|0;uf(aB,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17338){aM=c[13898]|0;uf(aM,217824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17429){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17430){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17431){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17432){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17433){uf(H,217704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17319){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17157){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17207){uf((c[13898]|0)-1|0,218240,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17138){uf(c[13898]|0,218632,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17201){c[13898]=ae;O=17202}else if((O|0)==17204){aN=c[13898]|0;uf(aN,218392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((O|0)==17336){c[n>>2]=0;aM=av;uf(aM,217824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((O|0)==17202){c[57076]=0;aN=ae;uf(aN,218392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);qj(179864);aN=~~(+((c[56734]|0)>>>0>>>0)/10.0);ae=a[228464]|0?217584:217656;O=(c[(c[3524]|0)+68>>2]|0)==2?217456:179864;aM=c[57108]|0;aL=+h[28552];be(13048,217664,(v=i,i=i+48|0,c[v>>2]=~~(+((c[56738]|0)>>>0>>>0)/10.0),c[v+8>>2]=aN,c[v+16>>2]=ae,c[v+24>>2]=O,c[v+32>>2]=aM,h[v+40>>3]=aL,v)|0);if(a[228312]|0){uD(13048+(uA(13048)|0)|0,217392,9)}if(a[226968]|0){uD(13048+(uA(13048)|0)|0,217272,12)}aM=c[57076]|0;if((aM|0)!=0){O=13048+(uA(13048)|0)|0;be(O|0,217200,(v=i,i=i+8|0,c[v>>2]=aM,v)|0)}aM=c[57122]|0;if((aM|0)!=0){O=13048+(uA(13048)|0)|0;be(O|0,217096,(v=i,i=i+8|0,c[v>>2]=aM,v)|0)}aM=13048+(uA(13048)|0)|0;be(aM|0,(a[226984]|0?132112:217056)|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);aM=13048+(uA(13048)|0)|0;be(aM|0,(a[228520]|0?217032:217e3)|0,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);aL=+h[28540];if(aL!=1.0){aM=13048+(uA(13048)|0)|0;be(aM|0,216912,(v=i,i=i+8|0,h[v>>3]=aL,v)|0)}aM=c[57136]|0;if((aM|0)<=-1){i=b;return}be(13048+(uA(13048)|0)|0,216792,(v=i,i=i+8|0,c[v>>2]=aM,v)|0);i=b;return}function p7(){aI(222944,8,1,c[10030]|0);return}function p8(){var b=0,d=0,e=0,f=0.0,g=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0.0,z=0.0,A=0,B=0,C=0,D=0,E=0;b=i;i=i+512|0;d=b|0;e=b+256|0;f=+h[28571];h[28376]=f;a[227016]=a[84376]|0;a[227017|0]=a[84377|0]|0;a[227018|0]=a[84378|0]|0;a[227019|0]=a[84379|0]|0;a[227020|0]=a[84380|0]|0;a[227021|0]=a[84381|0]|0;h[28378]=f;a[227032]=a[89416]|0;a[227033|0]=a[89417|0]|0;a[227034|0]=a[89418|0]|0;a[227035|0]=a[89419|0]|0;a[227036|0]=a[89420|0]|0;a[227037|0]=a[89421|0]|0;h[28380]=f;a[227048]=a[89352]|0;a[227049|0]=a[89353|0]|0;a[227050|0]=a[89354|0]|0;a[227051|0]=a[89355|0]|0;a[227052|0]=a[89356|0]|0;h[28382]=f;c[56766]=6579570;h[28384]=f;a[227080]=a[89296]|0;a[227081|0]=a[89297|0]|0;a[227082|0]=a[89298|0]|0;a[227083|0]=a[89299|0]|0;a[227084|0]=a[89300|0]|0;a[227085|0]=a[89301|0]|0;h[28386]=f;a[227096]=a[89328]|0;a[227097|0]=a[89329|0]|0;a[227098|0]=a[89330|0]|0;a[227099|0]=a[89331|0]|0;a[227100|0]=a[89332|0]|0;h[28388]=f;a[227112]=a[89248]|0;a[227113|0]=a[89249|0]|0;a[227114|0]=a[89250|0]|0;a[227115|0]=a[89251|0]|0;a[227116|0]=a[89252|0]|0;h[28390]=f;be(227128,222904,(v=i,i=i+24|0,c[v>>2]=21,c[v+8>>2]=117,c[v+16>>2]=69,v)|0);h[28392]=+h[28571];be(227144,222904,(v=i,i=i+24|0,c[v>>2]=0,c[v+8>>2]=0,c[v+16>>2]=148,v)|0);h[28394]=+h[28571];be(227160,222904,(v=i,i=i+24|0,c[v>>2]=255,c[v+8>>2]=153,c[v+16>>2]=0,v)|0);h[28396]=+h[28571];be(227176,222904,(v=i,i=i+24|0,c[v>>2]=0,c[v+8>>2]=153,c[v+16>>2]=161,v)|0);h[28398]=+h[28571];be(227192,222904,(v=i,i=i+24|0,c[v>>2]=214,c[v+8>>2]=214,c[v+16>>2]=69,v)|0);h[28400]=+h[28571];be(227208,222904,(v=i,i=i+24|0,c[v>>2]=163,c[v+8>>2]=145,c[v+16>>2]=255,v)|0);h[28402]=+h[28571];be(227224,222904,(v=i,i=i+24|0,c[v>>2]=255,c[v+8>>2]=204,c[v+16>>2]=0,v)|0);h[28404]=+h[28571];be(227240,222904,(v=i,i=i+24|0,c[v>>2]=214,c[v+8>>2]=0,c[v+16>>2]=120,v)|0);h[28406]=+h[28571];be(227256,222904,(v=i,i=i+24|0,c[v>>2]=171,c[v+8>>2]=214,c[v+16>>2]=0,v)|0);g=c[57136]|0;if((g|0)>-1){j=g>>>16&255;k=g>>>8&255;l=g&255;be(227016,222904,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=k,c[v+16>>2]=l,v)|0)}c[57144]=-3;c[(c[3524]|0)+8>>2]=c[56738];c[(c[3524]|0)+12>>2]=c[56734];f=+h[28552];c[57114]=~~(f*10.0);l=~~(f*.25*10.0);c[57112]=l;c[57110]=l;c[(c[3524]|0)+20>>2]=~~(f*.7*10.0);c[(c[3524]|0)+16>>2]=((c[57112]|0)+(c[57114]|0)|0)+(c[57110]|0);l=c[3524]|0;c[l+28>>2]=(c[l+16>>2]|0)>>>1;l=c[3524]|0;c[l+24>>2]=(c[l+16>>2]|0)>>>1;l=c[11252]|0;if((l|0)==6){n=222720}else if((l|0)==14){n=222200}else if((l|0)==13){n=222296}else if((l|0)==1){n=222880}else if((l|0)==9){n=222584}else if((l|0)==12){n=222392}else if((l|0)==2){n=222832}else if((l|0)==4){n=222768}else if((l|0)==10){n=222496}else if((l|0)==8){n=222656}else if((l|0)==7){n=222696}else if((l|0)==3){n=222800}else if((l|0)==5){n=179864}else{n=222152}cf(c[10030]|0,222e3,(v=i,i=i+8|0,c[v>>2]=n,v)|0);if(a[228312]|0){n=c[10030]|0;aI(221896,69,1,n|0)}if(!(a[228464]|0)){n=c[10030]|0;l=c[3524]|0;k=~~(+((c[l+8>>2]|0)>>>0>>>0)/10.0);j=~~(+((c[l+12>>2]|0)>>>0>>>0)/10.0);cf(n|0,221864,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=j,v)|0)}j=c[3524]|0;k=~~(+((c[j+12>>2]|0)>>>0>>>0)/10.0);cf(c[10030]|0,221816,(v=i,i=i+16|0,c[v>>2]=~~(+((c[j+8>>2]|0)>>>0>>>0)/10.0),c[v+8>>2]=k,v)|0);aI(221760,36,1,c[10030]|0);aI(221680,44,1,c[10030]|0);aI(221632,3,1,c[10030]|0);k=c[57076]|0;cf(c[10030]|0,221608,(v=i,i=i+8|0,c[v>>2]=(k|0)!=0?k:221552,v)|0);cf(c[10030]|0,221496,(v=i,i=i+16|0,c[v>>2]=40152,c[v+8>>2]=40160,v)|0);k=c[56744]|0;if((k|0)==0){j=bP(221464)|0;c[56744]=j;o=j}else{o=k}k=uA(o|0)|0;do{if((a[o]|0)!=0){if((a[o+(k-1|0)|0]|0)==47){break}j=db(o,k+2|0,221440)|0;c[56744]=j;n=j+(uA(j|0)|0)|0;w=47;a[n]=w&255;w=w>>8;a[n+1|0]=w&255}}while(0);do{if(a[228312]|0){if(a[226968]|0){k=(uA(c[56744]|0)|0)+18|0;o=ut(k)|0;do{if((o|0)==0){gk();n=ut(k)|0;if((n|0)!=0){p=n;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=221224,v)|0)}else{p=o}}while(0);o=c[56744]|0;uB(p|0,o|0);o=uA(p|0)|0;k=(o|0)==0?0:o-1|0;o=a[p+k|0]|0;if(!((o<<24>>24|0)==47|(o<<24>>24|0)==0)){a[p+(k+1|0)|0]=47;a[p+(k+2|0)|0]=0}uD(p+(uA(p|0)|0)|0,221256,15);k=bF(p|0,193632)|0;if((k|0)==0){uh(-1,221176,(v=i,i=i+8|0,c[v>>2]=p,v)|0)}else{o=c[10030]|0;aI(221096,65,1,o|0);o=e|0;n=(a6(o|0,256,k|0)|0)==0;j=c[10030]|0;if(n){q=j}else{n=j;while(1){aK(o|0,n|0);j=(a6(o|0,256,k|0)|0)==0;l=c[10030]|0;if(j){q=l;break}else{n=l}}}aI(223416,14,1,q|0);az(k|0)}uu(p)}else{n=c[10030]|0;o=c[56744]|0;cf(n|0,221312,(v=i,i=i+8|0,c[v>>2]=o,v)|0)}o=c[10030]|0;if(!(a[228312]|0)){r=o;s=17490;break}aI(221032,57,1,o|0);o=c[10030]|0;n=c[3524]|0;l=~~(+((c[n+8>>2]|0)>>>0>>>0)/10.0);j=~~(+((c[n+12>>2]|0)>>>0>>>0)/10.0);cf(o|0,220984,(v=i,i=i+32|0,c[v>>2]=0,c[v+8>>2]=0,c[v+16>>2]=l,c[v+24>>2]=j,v)|0);j=c[10030]|0;l=c[57136]|0;o=(l|0)>0?l:16777215;cf(j|0,220920,(v=i,i=i+8|0,c[v>>2]=o,v)|0);o=c[10030]|0;aI(220824,89,1,o|0);o=c[10030]|0;aI(220712,56,1,o|0);o=c[10030]|0;aI(220560,111,1,o|0)}else{r=c[10030]|0;s=17490}}while(0);if((s|0)==17490){aI(220520,25,1,r|0);r=c[10030]|0;s=c[3524]|0;p=~~(+((c[s+8>>2]|0)>>>0>>>0)/10.0);q=~~(+((c[s+12>>2]|0)>>>0>>>0)/10.0);cf(r|0,220984,(v=i,i=i+32|0,c[v>>2]=0,c[v+8>>2]=0,c[v+16>>2]=p,c[v+24>>2]=q,v)|0);q=c[57136]|0;p=c[10030]|0;if((q|0)>-1){cf(p|0,220448,(v=i,i=i+8|0,c[v>>2]=q,v)|0)}else{aI(77208,12,1,p|0)}p=c[10030]|0;aI(220440,3,1,p|0)}aI(220400,7,1,c[10030]|0);p=c[57122]|0;if((p|0)==0){t=c[3524]|0;u=t+28|0;x=c[u>>2]|0;y=+(x>>>0>>>0);z=20.0/y;A=c[10030]|0;B=cf(A|0,219192,(v=i,i=i+56|0,h[v>>3]=z,h[v+8>>3]=z,h[v+16>>3]=z,h[v+24>>3]=z,h[v+32>>3]=z,h[v+40>>3]=z,h[v+48>>3]=z,v)|0)|0;i=b;return}q=d|0;do{if((a[p]|0)==60){if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=p+1|0;r=bt(d|0,193632)|0;if((r|0)!=0){C=1;D=r;break}uf(-1,219160,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}else{d=bF(p|0,193632)|0;if((d|0)!=0){C=0;D=d;break}uf(-1,219072,(v=i,i=i+8|0,c[v>>2]=p,v)|0)}}while(0);if((a6(q|0,255,D|0)|0)==0){E=0}else{d=0;while(1){r=aQ(q|0,219056)|0;if((r|0)!=0){s=c[m>>2]|0;cf(s|0,218992,(v=i,i=i+16|0,c[v>>2]=p,c[v+8>>2]=r,v)|0)}aK(q|0,c[10030]|0);r=d+1|0;if((a6(q|0,255,D|0)|0)==0){E=r;break}else{d=r}}}do{if(C){d=a2(D|0)|0;if((d|0)==0){break}uf(-1,218832,(v=i,i=i+16|0,c[v>>2]=p+1,c[v+8>>2]=d,v)|0)}else{az(D|0)}}while(0);if((E|0)!=0){t=c[3524]|0;u=t+28|0;x=c[u>>2]|0;y=+(x>>>0>>>0);z=20.0/y;A=c[10030]|0;B=cf(A|0,219192,(v=i,i=i+56|0,h[v>>3]=z,h[v+8>>3]=z,h[v+16>>3]=z,h[v+24>>3]=z,h[v+32>>3]=z,h[v+40>>3]=z,h[v+48>>3]=z,v)|0)|0;i=b;return}if(C){uf(-1,218792,(v=i,i=i+8|0,c[v>>2]=p+1,v)|0)}else{uf(-1,218744,(v=i,i=i+8|0,c[v>>2]=p,v)|0)}}function p9(a){a=a|0;c[57140]=a;return 1}function qa(a){a=a|0;c[57138]=a;return 1}function qb(){var b=0,d=0,e=0,f=0.0,g=0,j=0,k=0;b=i;if(a[227272]|0){if(a[228352]|0){d=c[10030]|0;aI(78232,6,1,d|0);a[228352]=0}d=c[10030]|0;aI(78240,9,1,d|0);c[56816]=0;a[227272]=0}if(a[228352]|0){d=c[10030]|0;aI(78232,6,1,d|0);a[228352]=0}if(a[228344]|0){d=c[10030]|0;aI(77776,5,1,d|0);a[228344]=0;c[57120]=-1}if(a[228312]|0){d=c[10030]|0;aI(73376,42,1,d|0);d=c[10030]|0;aI(73296,61,1,d|0);d=c[10030]|0;e=~~(+((c[(c[3524]|0)+8>>2]|0)>>>0>>>0)/10.0);cf(d|0,73248,(v=i,i=i+8|0,c[v>>2]=e,v)|0);e=c[10030]|0;d=~~(+((c[(c[3524]|0)+12>>2]|0)>>>0>>>0)/10.0);cf(e|0,73192,(v=i,i=i+8|0,c[v>>2]=d,v)|0);d=c[10030]|0;f=+(c[7938]|0)/10.0;cf(d|0,73160,(v=i,i=i+8|0,h[v>>3]=f,v)|0);d=c[10030]|0;f=+(c[7939]|0)/10.0;cf(d|0,73120,(v=i,i=i+8|0,h[v>>3]=f,v)|0);d=c[10030]|0;f=+(((c[(c[3524]|0)+12>>2]|0)-(c[7940]|0)|0)>>>0>>>0)/10.0;cf(d|0,73088,(v=i,i=i+8|0,h[v>>3]=f,v)|0);d=c[10030]|0;f=+(((c[(c[3524]|0)+12>>2]|0)-(c[7941]|0)|0)>>>0>>>0)/10.0;cf(d|0,73056,(v=i,i=i+8|0,h[v>>3]=f,v)|0);d=c[10030]|0;f=+((c[7939]|0)-(c[7938]|0)|0)/10.0;cf(d|0,72960,(v=i,i=i+8|0,h[v>>3]=f,v)|0);d=c[10030]|0;f=+((c[7941]|0)-(c[7940]|0)|0)/10.0;cf(d|0,72872,(v=i,i=i+8|0,h[v>>3]=f,v)|0);e7(1);do{if((c[16546]|0)!=1){d=e6(72800)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==2){g=c[10030]|0;f=+h[d+24>>3];cf(g|0,72560,(v=i,i=i+16|0,c[v>>2]=72688,h[v+8>>3]=f,v)|0);break}else if((e|0)==1){e=c[d+24>>2]|0;cf(c[10030]|0,72752,(v=i,i=i+16|0,c[v>>2]=72688,c[v+8>>2]=e,v)|0);break}else{break}}}while(0);d=e6(72544)|0;if((d|0)==0){break}e=c[d+16>>2]|0;if((e|0)==1){g=c[10030]|0;j=c[d+24>>2]|0;cf(g|0,72752,(v=i,i=i+16|0,c[v>>2]=72528,c[v+8>>2]=j,v)|0);break}else if((e|0)==2){f=+h[d+24>>3];cf(c[10030]|0,72560,(v=i,i=i+16|0,c[v>>2]=72528,h[v+8>>3]=f,v)|0);break}else{break}}}while(0);d=e6(225256)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==2){j=c[10030]|0;f=+h[d+24>>3];cf(j|0,72560,(v=i,i=i+16|0,c[v>>2]=225240,h[v+8>>3]=f,v)|0);break}else if((e|0)==1){e=c[d+24>>2]|0;cf(c[10030]|0,72752,(v=i,i=i+16|0,c[v>>2]=225240,c[v+8>>2]=e,v)|0);break}else{break}}}while(0);d=e6(225200)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==2){j=c[10030]|0;f=+h[d+24>>3];cf(j|0,72560,(v=i,i=i+16|0,c[v>>2]=225128,h[v+8>>3]=f,v)|0);break}else if((e|0)==1){e=c[d+24>>2]|0;cf(c[10030]|0,72752,(v=i,i=i+16|0,c[v>>2]=225128,c[v+8>>2]=e,v)|0);break}else{break}}}while(0);d=c[10030]|0;e=(a[30528]&1)!=0?225e3:224992;cf(d|0,225024,(v=i,i=i+8|0,c[v>>2]=e,v)|0);if((a[30528]&1)!=0){e=c[10030]|0;f=(c[17366]&1|0)==0?+h[8687]:0.0;cf(e|0,224952,(v=i,i=i+8|0,h[v>>3]=f,v)|0)}do{if((c[17261]&3|0)==0){e=c[10030]|0;aI(224720,37,1,e|0)}else{e=e6(224936)|0;do{if((e|0)!=0){d=c[e+16>>2]|0;if((d|0)==1){j=c[10030]|0;g=c[e+24>>2]|0;cf(j|0,72752,(v=i,i=i+16|0,c[v>>2]=224920,c[v+8>>2]=g,v)|0);break}else if((d|0)==2){f=+h[e+24>>3];cf(c[10030]|0,72560,(v=i,i=i+16|0,c[v>>2]=224920,h[v+8>>3]=f,v)|0);break}else{break}}}while(0);e=e6(224872)|0;if((e|0)==0){break}d=c[e+16>>2]|0;if((d|0)==2){g=c[10030]|0;f=+h[e+24>>3];cf(g|0,72560,(v=i,i=i+16|0,c[v>>2]=224832,h[v+8>>3]=f,v)|0);break}else if((d|0)==1){d=c[e+24>>2]|0;cf(c[10030]|0,72752,(v=i,i=i+16|0,c[v>>2]=224832,c[v+8>>2]=d,v)|0);break}else{break}}}while(0);do{if((c[17089]&3|0)==0){d=c[10030]|0;aI(224096,37,1,d|0)}else{d=e6(224640)|0;do{if((d|0)!=0){e=c[d+16>>2]|0;if((e|0)==1){g=c[10030]|0;j=c[d+24>>2]|0;cf(g|0,72752,(v=i,i=i+16|0,c[v>>2]=224528,c[v+8>>2]=j,v)|0);break}else if((e|0)==2){f=+h[d+24>>3];cf(c[10030]|0,72560,(v=i,i=i+16|0,c[v>>2]=224528,h[v+8>>3]=f,v)|0);break}else{break}}}while(0);d=e6(224432)|0;if((d|0)==0){break}e=c[d+16>>2]|0;if((e|0)==1){j=c[10030]|0;g=c[d+24>>2]|0;cf(j|0,72752,(v=i,i=i+16|0,c[v>>2]=224320,c[v+8>>2]=g,v)|0);break}else if((e|0)==2){f=+h[d+24>>3];cf(c[10030]|0,72560,(v=i,i=i+16|0,c[v>>2]=224320,h[v+8>>3]=f,v)|0);break}else{break}}}while(0);d=c[10030]|0;e=a[66164]&1;cf(d|0,224040,(v=i,i=i+8|0,c[v>>2]=e,v)|0);e=c[10030]|0;d=a[65476]&1;cf(e|0,223976,(v=i,i=i+8|0,c[v>>2]=d,v)|0);if((a[30528]&1)!=0){d=c[10030]|0;e=a[69604]&1;cf(d|0,223896,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}e=c[10030]|0;if((c[16546]|0)==1){f=+h[8255];cf(e|0,223808,(v=i,i=i+8|0,h[v>>3]=f,v)|0);d=c[10030]|0;f=+h[8256];cf(d|0,223728,(v=i,i=i+8|0,h[v>>3]=f,v)|0);d=c[10030]|0;g=c[8510]|0;do{if((g|0)==0){j=c[8506]|0;if((j|0)==4){k=223560;break}k=(j|0)==5?223536:223520}else{k=g}}while(0);cf(d|0,223648,(v=i,i=i+8|0,c[v>>2]=k,v)|0)}else{aI(223432,34,1,e|0)}e=c[10030]|0;aI(223416,14,1,e|0)}aI(77776,5,1,c[10030]|0);if(!(a[228312]|0)){i=b;return}aI(223344,67,1,c[10030]|0);aI(223288,37,1,c[10030]|0);aI(223240,31,1,c[10030]|0);if(!(a[228312]|0)){i=b;return}if(!(a[228336]|0)){i=b;return}cf(c[10030]|0,223112,(v=i,i=i+8|0,c[v>>2]=~~(+((c[(c[3524]|0)+12>>2]|0)>>>0>>>0)/10.0)-26,v)|0);aI(222992,42,1,c[10030]|0);i=b;return}function qc(){tu();c[57120]=-1;c[57118]=0;a[228352]=0;a[228528]=0;a[227272]=0;c[56736]=-1;c[56740]=-1;return}function qd(a,b){a=a|0;b=b|0;if((c[56740]|0)==(a|0)&(c[56736]|0)==(b|0)){return}tv(a,b);return}function qe(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0.0;e=i;f=c[56740]|0;g=c[56736]|0;if((f|0)==(b|0)&(g|0)==(d|0)){i=e;return}if(!(a[227272]|0)){tv(f,g)}j=+((c[(c[3524]|0)+12>>2]|0)-d|0)/10.0;cf(c[10030]|0,73912,(v=i,i=i+16|0,h[v>>3]=+(b>>>0>>>0)/10.0,h[v+8>>3]=j,v)|0);g=(c[56816]|0)+1|0;c[56816]=g;if((g&7|0)==0){g=c[10030]|0;aI(73904,3,1,g|0)}c[56740]=b;c[56736]=d;i=e;return}function qf(b){b=b|0;var d=0;a[228528]=0;if((c[57144]|0)==(b|0)){return}if(a[227272]|0){if(a[228352]|0){d=c[10030]|0;aI(78232,6,1,d|0);a[228352]=0}d=c[10030]|0;aI(78240,9,1,d|0);c[56816]=0;a[227272]=0}if(a[228352]|0){d=c[10030]|0;aI(78232,6,1,d|0);a[228352]=0}if(a[228344]|0){d=c[10030]|0;aI(77776,5,1,d|0);a[228344]=0;c[57120]=-1}c[57144]=b;tu();return}function qg(b,e,f){b=b|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0.0,n=0,o=0;g=i;if(a[227272]|0){if(a[228352]|0){j=c[10030]|0;aI(78232,6,1,j|0);a[228352]=0}j=c[10030]|0;aI(78240,9,1,j|0);c[56816]=0;a[227272]=0}j=c[57138]|0;if((j|0)==1){k=74560}else if((j|0)==0){k=74592}else{k=74552}j=((c[57140]|0)%180&-1|0)==0;l=((c[57114]|0)-(c[57112]|0)|0)/2&-1;m=+(((j?l:0)-e|0)+(c[(c[3524]|0)+12>>2]|0)|0)/10.0;cf(c[10030]|0,74504,(v=i,i=i+16|0,h[v>>3]=+((j?0:l)+b|0)/10.0,h[v+8>>3]=m,v)|0);b=c[57140]|0;if((b|0)!=0){l=c[10030]|0;j=-b|0;cf(l|0,74488,(v=i,i=i+8|0,c[v>>2]=j,v)|0)}aI(74456,27,1,c[10030]|0);j=a[228528]|0;do{if(j<<24>>24==3){l=c[10030]|0;b=d[226992]|0;e=d[228368]|0;n=d[228536]|0;cf(l|0,74424,(v=i,i=i+24|0,c[v>>2]=b,c[v+8>>2]=e,c[v+16>>2]=n,v)|0)}else{n=c[10030]|0;if(j<<24>>24==1){e=c[57082]|0;aK(e|0,n|0);break}e=c[57144]|0;if((e|0)>12){o=(e|0)%13&-1}else{o=e}e=o+3|0;b=227016+(((e|0)<0?0:e<<16>>16)<<4)|0;aK(b|0,n|0)}}while(0);m=+h[28552];cf(c[10030]|0,74384,(v=i,i=i+16|0,c[v>>2]=c[57108],h[v+8>>3]=m,v)|0);o=c[57096]|0;do{if((o|0)!=0){if((aY(o|0,77432)|0)==0){break}j=c[10030]|0;cf(j|0,74280,(v=i,i=i+8|0,c[v>>2]=o,v)|0)}}while(0);o=c[57100]|0;do{if((o|0)!=0){if((aY(o|0,77432)|0)==0){break}j=c[10030]|0;cf(j|0,74256,(v=i,i=i+8|0,c[v>>2]=o,v)|0)}}while(0);cf(c[10030]|0,74160,(v=i,i=i+8|0,c[v>>2]=k,v)|0);k=(aQ(f|0,74128)|0)==0;o=c[10030]|0;if(k){aI(74024,8,1,o|0)}else{aI(74096,29,1,o|0)}if(a[237856]|0){i=g;return}o=a[f]|0;if(o<<24>>24!=0){k=f;f=o;do{o=f<<24>>24;L23666:do{if((o|0)==60){j=c[10030]|0;aI(74016,4,1,j|0)}else if((o|0)==38){do{if((a[k+1|0]|0)==35){if((a[k+2|0]|0)!=120){break}j=c[10030]|0;aF(38,j|0);break L23666}}while(0);aI(74008,5,1,c[10030]|0)}else{aF(o|0,c[10030]|0)}}while(0);k=k+1|0;f=a[k]|0;}while(f<<24>>24!=0)}aI(73992,14,1,c[10030]|0);i=g;return}function qh(b,e,f){b=b|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0.0,r=0.0;g=i;i=i+64|0;j=g|0;k=a[228528]|0;do{if(k<<24>>24==3){l=j|0;m=d[226992]|0;n=d[228368]|0;o=d[228536]|0;be(l|0,74880,(v=i,i=i+24|0,c[v>>2]=m,c[v+8>>2]=n,c[v+16>>2]=o,v)|0)}else{o=j|0;if(k<<24>>24==1){n=c[57082]|0;be(o|0,74864,(v=i,i=i+8|0,c[v>>2]=n,v)|0);break}else{a[o]=0;break}}}while(0);if(a[227272]|0){if(a[228352]|0){k=c[10030]|0;aI(78232,6,1,k|0);a[228352]=0}k=c[10030]|0;aI(78240,9,1,k|0);c[56816]=0;a[227272]=0}k=c[10030]|0;if((f|0)<0){p=+(b>>>0>>>0)/10.0;q=+((c[(c[3524]|0)+12>>2]|0)-e|0)/10.0;o=j|0;cf(k|0,74760,(v=i,i=i+24|0,h[v>>3]=p,h[v+8>>3]=q,c[v+16>>2]=o,v)|0);c[56740]=b;c[56736]=e;i=g;return}else{o=(f|0)%13&-1;q=+(b>>>0>>>0)/10.0;f=c[3524]|0;p=+((c[f+12>>2]|0)-e|0)/10.0;r=+h[1630]*+((c[f+28>>2]|0)>>>0>>>0)/20.0;f=j|0;cf(k|0,74656,(v=i,i=i+40|0,c[v>>2]=o,h[v+8>>3]=q,h[v+16>>3]=p,h[v+24>>3]=r,c[v+32>>2]=f,v)|0);c[56740]=b;c[56736]=e;i=g;return}}function qi(b){b=b|0;var d=0;c[59472]=(c[59472]|0)+1;do{if((b|0)==38){d=c[11232]|0;c[11232]=d+1;a[d]=38;d=c[11232]|0;c[11232]=d+1;a[d]=97;d=c[11232]|0;c[11232]=d+1;a[d]=109;d=c[11232]|0;c[11232]=d+1;a[d]=112;d=c[11232]|0;c[11232]=d+1;a[d]=59;return}else if((b|0)==60){d=c[11232]|0;c[11232]=d+1;a[d]=38;d=c[11232]|0;c[11232]=d+1;a[d]=108;d=c[11232]|0;c[11232]=d+1;a[d]=116;d=c[11232]|0;c[11232]=d+1;a[d]=59;return}else if((b|0)==(-2|0)){if((c[11252]|0)!=0){break}d=c[11232]|0;c[11232]=d+1;a[d]=38;return}}while(0);d=c[11232]|0;c[11232]=d+1;a[d]=b&255;return}function qj(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0;d=i;do{if((b|0)==0){e=17680}else{if((a[b]|0)==0){e=17680;break}f=aQ(b|0,75096)|0;if((f|0)==0){g=aQ(b|0,75048)|0}else{g=f}f=aQ(b|0,74992)|0;if((f|0)==0){j=aQ(b|0,74976)|0}else{j=f}f=bk(b|0,148464)|0;if((f|0)>0){uu(c[57108]|0);k=bP(b|0)|0;c[57108]=k;if((j|0)==0){c[57100]=77432}else{c[57100]=74936;l=uA(b|0)|0;a[k+(l-(uA(j|0)|0)|0)|0]=0}if((g|0)==0){c[57096]=77432}else{c[57096]=74928;l=uA(b|0)|0;k=l-(uA(g|0)|0)|0;a[(c[57108]|0)+k|0]=0}a[(c[57108]|0)+f|0]=0}if((a[b+f|0]|0)!=44){break}k=b+(f+1|0)|0;ca(k|0,148448,(v=i,i=i+8|0,c[v>>2]=228416,v)|0)}}while(0);if((e|0)==17680){uu(c[57108]|0);e=c[57106]|0;if((e|0)==0){m=0}else{m=bP(e|0)|0}c[57108]=m;h[28552]=+h[28551];c[57100]=c[57098];c[57096]=c[57094]}n=+h[28552];c[57114]=~~(n*10.0);m=~~(n*.25*10.0);c[57112]=m;c[57110]=m;c[(c[3524]|0)+20>>2]=~~(n*.7*10.0);c[(c[3524]|0)+16>>2]=((c[57112]|0)+(c[57114]|0)|0)+(c[57110]|0);i=d;return 1}function qk(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0;g=i;i=i+48|0;h=g|0;c[h>>2]=b;c[h+4>>2]=d;j=e+b|0;c[h+12>>2]=j;c[h+16>>2]=d;c[h+24>>2]=j;j=f+d|0;c[h+28>>2]=j;c[h+36>>2]=b;c[h+40>>2]=j;c[h+8>>2]=a;qp(4,h|0);i=g;return}function ql(b){b=+b;var d=0;if(+h[28571]==b){return}h[28571]=b;h[28376]=b;h[28378]=b;h[28380]=b;h[28382]=b;h[28384]=b;h[28386]=b;h[28388]=b;h[28390]=b;h[28392]=b;h[28394]=b;h[28396]=b;h[28398]=b;h[28400]=b;h[28402]=b;h[28404]=b;h[28406]=b;if(a[227272]|0){if(a[228352]|0){d=c[10030]|0;aI(78232|0,6,1,d|0);a[228352]=0}d=c[10030]|0;aI(78240|0,9,1,d|0);c[56816]=0;a[227272]=0}if(a[228352]|0){d=c[10030]|0;aI(78232|0,6,1,d|0);a[228352]=0}if(a[228344]|0){d=c[10030]|0;aI(77776|0,5,1,d|0);a[228344]=0;c[57120]=-1}tu();return}function qm(b){b=b|0;var d=0;if(a[228352]|0){d=c[10030]|0;aI(78232,6,1,d|0);a[228352]=0}if((b|0)==0){return 0}c[56821]=c[b+4>>2];c[56822]=c[b+8>>2];c[56823]=c[b+12>>2];c[56824]=c[b+16>>2];a[227300]=a[b+20|0]|0;return 0}function qn(){if(!(a[228352]|0)){return}aI(78232,6,1,c[10030]|0);a[228352]=0;return}function qo(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0;d=i;i=i+8|0;e=d|0;f=c[b>>2]|0;if((f|0)==3){g=c[b+4>>2]|0;j=g>>>16&255;a[e|0]=j;a[e+1|0]=g>>>8&255;a[e+2|0]=g&255;k=j}else if((f|0)==1){j=c[b+4>>2]|0;if((j|0)>12){l=(j|0)%13&-1}else{l=j}j=l+3|0;c[57082]=227016+(((j|0)<0?0:j<<16>>16)<<4);a[228528]=1;i=d;return}else if((f|0)==5){fr(+h[b+8>>3],e);k=a[e|0]|0}else{i=d;return}a[228528]=3;b=e|0;do{if(k<<24>>24==(a[226992]|0)){if((a[e+1|0]|0)!=(a[228368]|0)){break}if((a[e+2|0]|0)!=(a[228536]|0)){break}i=d;return}}while(0);if(a[227272]|0){if(a[228352]|0){k=c[10030]|0;aI(78232,6,1,k|0);a[228352]=0}k=c[10030]|0;aI(78240,9,1,k|0);c[56816]=0;a[227272]=0}a[226992]=a[b]|0;a[228368]=a[e+1|0]|0;a[228536]=a[e+2|0]|0;i=d;return}function qp(b,e){b=b|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0.0;f=i;g=e+8|0;j=c[g>>2]|0;k=j>>4;l=j&15;c[g>>2]=l;do{if((l|0)==5|(l|0)==2){g=(k|0)%8&-1;if((g|0)==(c[57120]|0)){break}c[57120]=g;if(a[227272]|0){if(a[228352]|0){j=c[10030]|0;aI(78232,6,1,j|0);a[228352]=0}j=c[10030]|0;aI(78240,9,1,j|0);c[56816]=0;a[227272]=0}j=(c[57118]|0)+1|0;c[57118]=j;m=c[10030]|0;cf(m|0,76176,(v=i,i=i+8|0,c[v>>2]=j,v)|0);if((g|0)==7){n=76328;o=75816}else if((g|0)==5){n=76328;o=75896}else if((g|0)==3){n=76584;o=76e3}else if((g|0)==2){n=76328;o=76032}else if((g|0)==6){n=76328;o=75856}else if((g|0)==4){n=76328;o=75920}else if((g|0)==1){n=76328;o=76104}else{n=76328;o=179864}do{if((a[o]|0)!=0){g=(aY(n|0,76584)|0)==0?75712:75792;j=a[228528]|0;if(j<<24>>24==3){m=c[10030]|0;p=d[226992]|0;q=d[228368]|0;r=d[228536]|0;cf(m|0,75592,(v=i,i=i+48|0,c[v>>2]=g,c[v+8>>2]=n,c[v+16>>2]=p,c[v+24>>2]=q,c[v+32>>2]=r,c[v+40>>2]=o,v)|0);break}r=c[10030]|0;if(j<<24>>24==1){j=c[57082]|0;cf(r|0,75504,(v=i,i=i+32|0,c[v>>2]=g,c[v+8>>2]=n,c[v+16>>2]=j,c[v+24>>2]=o,v)|0);break}else{cf(r|0,75424,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=n,c[v+16>>2]=o,v)|0);break}}}while(0);g=c[10030]|0;aI(75400,22,1,g|0)}}while(0);if(!(a[228352]|0)){if(a[227272]|0){o=c[10030]|0;aI(78240,9,1,o|0);c[56816]=0;a[227272]=0}o=c[10030]|0;aI(76352,55,1,o|0);a[228352]=1}aI(77136,11,1,c[10030]|0);do{if((l|0)==0){o=c[10030]|0;cf(o|0,77072,(v=i,i=i+8|0,c[v>>2]=227016,v)|0)}else if((l|0)==2|(l|0)==5){cf(c[10030]|0,76808,(v=i,i=i+8|0,c[v>>2]=c[57118],v)|0)}else if((l|0)==1|(l|0)==4){o=a[228528]|0;do{if(o<<24>>24==3){n=c[10030]|0;g=d[226992]|0;r=d[228368]|0;j=d[228536]|0;cf(n|0,76552,(v=i,i=i+32|0,c[v>>2]=76584,c[v+8>>2]=g,c[v+16>>2]=r,c[v+24>>2]=j,v)|0)}else{j=c[10030]|0;if(o<<24>>24==1){r=c[57082]|0;cf(j|0,76480,(v=i,i=i+16|0,c[v>>2]=76584,c[v+8>>2]=r,v)|0);break}else{cf(j|0,76448,(v=i,i=i+8|0,c[v>>2]=76584,v)|0);break}}}while(0);if(k>>>0>=100){break}cf(c[10030]|0,76904,(v=i,i=i+8|0,h[v>>3]=+(k|0)*.01,v)|0)}else{o=a[228528]|0;if(o<<24>>24==3){j=c[10030]|0;r=d[226992]|0;g=d[228368]|0;n=d[228536]|0;cf(j|0,76552,(v=i,i=i+32|0,c[v>>2]=76584,c[v+8>>2]=r,c[v+16>>2]=g,c[v+24>>2]=n,v)|0);break}n=c[10030]|0;if(o<<24>>24==1){o=c[57082]|0;cf(n|0,76480,(v=i,i=i+16|0,c[v>>2]=76584,c[v+8>>2]=o,v)|0);break}else{cf(n|0,76448,(v=i,i=i+8|0,c[v>>2]=76584,v)|0);break}}}while(0);aI(76704,11,1,c[10030]|0);k=c[10030]|0;if((b|0)>0){s=0;t=k}else{u=k;w=aI(76624,4,1,u|0)|0;i=f;return}while(1){x=+((c[(c[3524]|0)+12>>2]|0)-(c[e+(s*12&-1)+4>>2]|0)|0)/10.0;cf(t|0,76648,(v=i,i=i+24|0,h[v>>3]=+(c[e+(s*12&-1)>>2]|0)/10.0,h[v+8>>3]=x,c[v+16>>2]=((s|0)%16&-1|0)==15?140136:73808,v)|0);k=s+1|0;l=c[10030]|0;if((k|0)<(b|0)){s=k;t=l}else{u=l;break}}w=aI(76624,4,1,u|0)|0;i=f;return}function qq(b,d,e,f,g,j){b=b|0;d=+d;e=+e;f=f|0;g=g|0;j=j|0;var k=0,l=0.0,m=0.0,n=0;f=i;if((j|0)==2){if(a[237880]|0){a[237880]=0;a[c[11232]|0]=0;k=c[10030]|0;cf(k|0,77712,(v=i,i=i+8|0,c[v>>2]=43856,v)|0)}k=c[10030]|0;l=+(c[59472]|0)*.5;m=+h[29737]-e;cf(k|0,77608,(v=i,i=i+16|0,h[v>>3]=l,h[v+8>>3]=m,v)|0);h[29737]=e;h[29731]=0.0;c[11232]=43856;c[59472]=0;a[237880]=1;i=f;return}else if((j|0)==4){h[29731]=+(c[59472]|0)*-.5;h[29737]=+h[29737]-e;c[59472]=0;i=f;return}else if((j|0)==3){c[59472]=0;i=f;return}else{if(a[237880]|0){i=f;return}a[237880]=1;c[11232]=43856;aI(77600,6,1,c[10030]|0);j=c[57108]|0;if((aY(j|0,b|0)|0)!=0){uu(j);if((b|0)==0){n=0}else{n=bP(b|0)|0}c[57108]=n;b=c[10030]|0;cf(b|0,77568,(v=i,i=i+8|0,c[v>>2]=n,v)|0)}if(+h[28552]!=d){h[28552]=d;n=c[10030]|0;cf(n|0,77544,(v=i,i=i+8|0,h[v>>3]=d,v)|0)}n=c[57096]|0;do{if((n|0)!=0){if((aY(n|0,77432)|0)==0){break}b=c[10030]|0;cf(b|0,77368,(v=i,i=i+8|0,c[v>>2]=n,v)|0)}}while(0);n=c[57100]|0;do{if((n|0)!=0){if((aY(n|0,77432)|0)==0){break}b=c[10030]|0;cf(b|0,77288,(v=i,i=i+8|0,c[v>>2]=n,v)|0)}}while(0);d=+h[29731];if(d!=0.0){n=c[10030]|0;cf(n|0,77256,(v=i,i=i+8|0,h[v>>3]=d,v)|0);h[29731]=0.0}d=+h[29737];if(d!=e){n=c[10030]|0;m=d-e;cf(n|0,77240,(v=i,i=i+8|0,h[v>>3]=m,v)|0);h[29737]=e}if(!g){g=c[10030]|0;aI(77208,12,1,g|0)}if(a[237872]|0){g=c[10030]|0;aI(77184,21,1,g|0)}aF(62,c[10030]|0);i=f;return}}function qr(){var b=0;b=i;if(!(a[237880]|0)){i=b;return}a[237880]=0;a[c[11232]|0]=0;cf(c[10030]|0,77712,(v=i,i=i+8|0,c[v>>2]=43856,v)|0);i=b;return}function qs(d){d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;i=i+8|0;f=e|0;b[f>>1]=0;if((d|0)==0|(d|0)==10){c[56750]=0;i=e;return}else if((d|0)==6){if(a[227272]|0){if(a[228352]|0){g=c[10030]|0;aI(78232,6,1,g|0);a[228352]=0}g=c[10030]|0;aI(78240,9,1,g|0);c[56816]=0;a[227272]=0}if(a[228352]|0){g=c[10030]|0;aI(78232,6,1,g|0);a[228352]=0}if(a[228344]|0){g=c[10030]|0;aI(77776,5,1,g|0);a[228344]=0;c[57120]=-1}g=(c[56750]|0)+1|0;c[56750]=g;h=c[57076]|0;j=(h|0)!=0?h:78208;do{if((a[33512]&1)!=0){h=c[8415]|0;if((h|0)>=26){break}a[f]=h+97&255}}while(0);cf(c[10030]|0,78184,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=g,c[v+16>>2]=f,v)|0);g=c[56750]|0;cf(c[10030]|0,78072,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=g,c[v+16>>2]=f,v)|0);c[57144]=-5;i=e;return}else if((d|0)==9){if(!(a[228312]|0)){i=e;return}if(a[227272]|0){if(a[228352]|0){g=c[10030]|0;aI(78232,6,1,g|0);a[228352]=0}g=c[10030]|0;aI(78240,9,1,g|0);c[56816]=0;a[227272]=0}if(a[228352]|0){g=c[10030]|0;aI(78232,6,1,g|0);a[228352]=0}aI(78232,6,1,c[10030]|0);c[57144]=-5;i=e;return}else if((d|0)==7){if(a[227272]|0){if(a[228352]|0){g=c[10030]|0;aI(78232,6,1,g|0);a[228352]=0}g=c[10030]|0;aI(78240,9,1,g|0);c[56816]=0;a[227272]=0}if(a[228352]|0){g=c[10030]|0;aI(78232,6,1,g|0);a[228352]=0}if(a[228344]|0){g=c[10030]|0;aI(77776,5,1,g|0);a[228344]=0;c[57120]=-1}aI(78232,6,1,c[10030]|0);c[57144]=-5;i=e;return}else if((d|0)==3){a[228360]=1;a[228336]=1;i=e;return}else if((d|0)==4){a[228360]=0;i=e;return}else if((d|0)==8){if(!(a[228312]|0)){i=e;return}if(a[227272]|0){if(a[228352]|0){d=c[10030]|0;aI(78232,6,1,d|0);a[228352]=0}d=c[10030]|0;aI(78240,9,1,d|0);c[56816]=0;a[227272]=0}if(a[228352]|0){d=c[10030]|0;aI(78232,6,1,d|0);a[228352]=0}d=c[57076]|0;g=(d|0)!=0?d:78208;do{if((a[33512]&1)!=0){d=c[8415]|0;if((d|0)>=26){break}a[f]=d+97&255}}while(0);aI(77992,25,1,c[10030]|0);d=c[56750]|0;cf(c[10030]|0,77856,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=d,c[v+16>>2]=f,v)|0);aI(77816,2,1,c[10030]|0);i=e;return}else{i=e;return}}function qt(b){b=b|0;if((b|0)!=1){return}aI(78256,2,1,c[10030]|0);if(!(a[227272]|0)){return}if(a[228352]|0){b=c[10030]|0;aI(78232,6,1,b|0);a[228352]=0}aI(78240,9,1,c[10030]|0);c[56816]=0;a[227272]=0;return}function qu(){var b=0,d=0,e=0,f=0;b=i;if((a[33512]&1)==0){d=c[766]|0}else{c[772]=100;if((c[520]|0)>>>0>1){e=c[m>>2]|0;aI(79400,34,1,e|0);c[520]=1}c[766]=1;d=1}e=c[772]|0;f=c[778]|0;cf(c[10030]|0,79224,(v=i,i=i+56|0,c[v>>2]=a[225808]&1,c[v+8>>2]=e,c[v+16>>2]=d,c[v+24>>2]=25680,c[v+32>>2]=f,c[v+40>>2]=40152,c[v+48>>2]=40160,v)|0);a[46624]=0;c[9372]=0;c[524]=0;i=b;return}function qv(){var a=0;tt();c[9372]=0;c[524]=0;c[522]=1;c[526]=1;a=c[10030]|0;if((a|0)==0){return}aD(a|0);return}function qw(){tt();return}function qx(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0.0,P=0,Q=0,R=0.0,S=0,T=0,U=0.0,V=0.0,W=0;b=i;i=i+80|0;d=b|0;e=b+24|0;f=b+48|0;j=b+72|0;k=c[13898]|0;l=c[8272]|0;L23998:do{if((k|0)<(l|0)){m=d|0;n=d+8|0;o=k;p=l;L24000:while(1){q=c[1054]|0;r=(a[q+(o*40&-1)|0]&1)==0;L24002:do{if(!r){s=c[q+(o*40&-1)+36>>2]|0;t=q+(o*40&-1)+32|0;u=c[10036]|0;w=0;while(1){if((w|0)>=(s|0)){break}if((a[u+((c[t>>2]|0)+w|0)|0]|0)==(a[w+103664|0]|0)){w=w+1|0}else{break L24002}}if((w|0)==1){break L23998}}}while(0);t=c[56580]|0;L24009:do{if((t|0)==0){x=226320}else{u=c[q+(o*40&-1)+36>>2]|0;s=c[q+(o*40&-1)+32>>2]|0;y=(u|0)>0;z=c[10036]|0;A=226320;B=t;while(1){L24013:do{if(!r){if(y){C=0;D=0;E=s;while(1){F=a[B+C|0]|0;if(F<<24>>24==(a[z+(C+E|0)|0]|0)){G=E;H=D}else{if(F<<24>>24!=36){break L24013}G=E-1|0;H=1}I=C+1|0;if((I|0)<(H+u|0)){C=I;D=H;E=G}else{break}}if((H|0)==0){J=I}else{x=A;break L24009}}else{J=0}E=a[B+J|0]|0;if((E<<24>>24|0)==36|(E<<24>>24|0)==0){x=A;break L24009}}}while(0);E=A+8|0;D=c[E>>2]|0;if((D|0)==0){x=E;break}else{A=E;B=D}}}}while(0);r=c[x+4>>2]|0;if((r|0)==4){a[225808]=1;c[13898]=o+1}else if((r|0)==5){K=o+1|0;c[13898]=K;if((K|0)>=(p|0)){L=18029;break}t=(a[q+(K*40&-1)|0]&1)==0;L24030:do{if(t){L=17953}else{B=c[q+(K*40&-1)+36>>2]|0;A=q+(K*40&-1)+32|0;u=c[10036]|0;z=0;while(1){if((z|0)>=(B|0)){L=17946;break}if((a[u+((c[A>>2]|0)+z|0)|0]|0)==(a[z+103664|0]|0)){z=z+1|0}else{break}}if((L|0)==17946){L=0;if((z|0)==1){L=18030;break L24000}}if(t){L=17953;break}A=c[q+(K*40&-1)+36>>2]|0;u=q+(K*40&-1)+32|0;B=c[10036]|0;s=0;while(1){if((s|0)>=(A|0)){break}if((a[B+((c[u>>2]|0)+s|0)|0]|0)==(a[s+148464|0]|0)){s=s+1|0}else{L=17953;break L24030}}if((s|0)==1){M=K;N=p}else{L=17953}}}while(0);if((L|0)==17953){L=0;t=is(e)|0;u=c[t>>2]|0;if((u|0)==1){O=+(c[t+8>>2]|0)}else if((u|0)==3){O=+uz(c[t+8>>2]|0,0)}else if((u|0)==2){O=+h[t+8>>3]}else{L=17957;break}c[518]=~~O;M=c[13898]|0;N=c[8272]|0}if((N|0)<=(M|0)){L=18025;break}t=c[1054]|0;if((a[t+(M*40&-1)|0]&1)==0){L=18028;break}u=c[t+(M*40&-1)+36>>2]|0;B=t+(M*40&-1)+32|0;A=c[10036]|0;z=0;while(1){if((z|0)>=(u|0)){break}if((a[A+((c[B>>2]|0)+z|0)|0]|0)==(a[z+148464|0]|0)){z=z+1|0}else{L=18026;break L24000}}if((z|0)!=1){L=18027;break}B=M+1|0;c[13898]=B;L24058:do{if((N|0)>(B|0)){if((a[t+(B*40&-1)|0]&1)==0){L=17972;break}u=c[t+(B*40&-1)+36>>2]|0;y=t+(B*40&-1)+32|0;w=0;while(1){if((w|0)>=(u|0)){break}if((a[A+((c[y>>2]|0)+w|0)|0]|0)==(a[w+78864|0]|0)){w=w+1|0}else{L=17972;break L24058}}if((w|0)==1){P=B;Q=N}else{L=17972}}else{L=17972}}while(0);if((L|0)==17972){L=0;B=is(e)|0;A=c[B>>2]|0;if((A|0)==1){R=+(c[B+8>>2]|0)}else if((A|0)==2){R=+h[B+8>>3]}else if((A|0)==3){R=+uz(c[B+8>>2]|0,0)}else{L=17976;break}c[516]=~~R;P=c[13898]|0;Q=c[8272]|0}if((Q|0)<=(P|0)){L=18031;break}B=c[1054]|0;if((a[B+(P*40&-1)|0]&1)==0){L=18032;break}A=c[B+(P*40&-1)+36>>2]|0;t=B+(P*40&-1)+32|0;B=c[10036]|0;z=0;while(1){if((z|0)>=(A|0)){break}if((a[B+((c[t>>2]|0)+z|0)|0]|0)==(a[z+78864|0]|0)){z=z+1|0}else{L=18033;break L24000}}if((z|0)!=1){L=18034;break}c[13898]=P+1;c[520]=aa(c[518]|0,c[516]|0)}else if((r|0)==10){uD(25680,90584,10);uD(25424,90584,10);c[778]=18;c[(c[3524]|0)+16>>2]=18;c[(c[3524]|0)+20>>2]=(((c[778]|0)*6&-1)>>>0)/10>>>0;a[225808]=0;a[225792]=0;t=(c[3524]|0)+96|0;c[t>>2]=c[t>>2]&-1025;a[225800]=0;c[520]=1;c[522]=1;c[526]=1;c[518]=1;c[516]=1;c[772]=100;h[6448]=1.0;h[6449]=1.0;c[13898]=(c[13898]|0)+1}else if((r|0)==0){a[225792]=1;t=(c[3524]|0)+96|0;c[t>>2]=c[t>>2]|1024;c[13898]=(c[13898]|0)+1}else if((r|0)==1){a[225792]=0;t=(c[3524]|0)+96|0;c[t>>2]=c[t>>2]&-1025;c[13898]=(c[13898]|0)+1}else if((r|0)==8){t=o+1|0;c[13898]=t;S=t;L=17989}else if((r|0)==3){a[225808]=0;c[13898]=o+1}else if((r|0)==6){a[225800]=1;c[13898]=o+1}else if((r|0)==7){a[225800]=0;c[13898]=o+1}else if((r|0)==2){T=o+1|0;c[13898]=T;if((T|0)>=(p|0)){L=18024;break}L24089:do{if((a[q+(T*40&-1)|0]&1)!=0){t=c[q+(T*40&-1)+36>>2]|0;B=q+(T*40&-1)+32|0;A=c[10036]|0;y=0;while(1){if((y|0)>=(t|0)){break}if((a[A+((c[B>>2]|0)+y|0)|0]|0)==(a[y+103664|0]|0)){y=y+1|0}else{break L24089}}if((y|0)==1){L=18023;break L24000}}}while(0);z=is(e)|0;B=c[z>>2]|0;if((B|0)==1){U=+(c[z+8>>2]|0)}else if((B|0)==2){U=+h[z+8>>3]}else if((B|0)==3){U=+uz(c[z+8>>2]|0,0)}else{L=17936;break}h[6449]=U;if(U<=0.0){L=17938;break}}else{S=o;L=17989}L24101:do{if((L|0)==17989){L=0;L24103:do{if((S|0)<(p|0)){L24105:do{if((a[q+(S*40&-1)|0]&1)!=0){z=c[q+(S*40&-1)+36>>2]|0;B=q+(S*40&-1)+32|0;A=c[10036]|0;t=0;while(1){if((t|0)>=(z|0)){break}if((a[A+((c[B>>2]|0)+t|0)|0]|0)==(a[t+103664|0]|0)){t=t+1|0}else{break L24105}}if((t|0)==1){break L24103}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[m>>2]|0)!=3){c[13898]=S;break}B=c[n>>2]|0;if((B|0)==0){break}A=bk(B|0,148464)|0;do{if((a[B+A|0]|0)==44){if((ca(B+(A+1|0)|0,148448,(v=i,i=i+8|0,c[v>>2]=j,v)|0)|0)!=1){break}c[778]=~~(+h[j>>3]+.5)}}while(0);if((A|0)>0){uF(25680,B|0,255);a[A+25680|0]=0}uB(25424,25680);uu(B);break L24101}}while(0);if((r|0)==8){L=18006;break L24000}y=is(f)|0;z=c[y>>2]|0;if((z|0)==2){V=+h[y+8>>3]}else if((z|0)==3){V=+uz(c[y+8>>2]|0,0)}else if((z|0)==1){V=+(c[y+8>>2]|0)}else{L=18011;break L24000}y=~~V;c[778]=y;c[(c[3524]|0)+16>>2]=y;c[(c[3524]|0)+20>>2]=(((c[778]|0)*6&-1)>>>0)/10>>>0}}while(0);o=c[13898]|0;p=c[8272]|0;if((o|0)>=(p|0)){break L23998}}if((L|0)==18023){uf(T,79152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18033){uf(P,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18024){uf(T,79152,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18025){uf(M,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18028){uf(M,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==17976){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18030){uf(K,78976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18034){uf(P,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18029){uf(K,78976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18031){uf(P,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18006){uf(c[13898]|0,78800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18027){uf(M,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==17938){uf((c[13898]|0)-1|0,79096,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18026){uf(M,78920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18032){uf(P,78824,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==17936){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==18011){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==17957){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);L=a[225808]|0;P=c[518]|0;V=+g[178]*+(((P*1030&-1)-80|0)>>>0>>>0);do{if(L){U=14.0e4/V;M=c[516]|0;R=95.0e3/(+g[38]*+(((M*714&-1)-80|0)>>>0>>>0));c[772]=~~(U<R?U:R);if((M|0)==1){c[766]=0;W=1;break}else if((M|0)==2){c[766]=1;W=2;break}else{c[766]=2;W=M;break}}else{R=95.0e3/V;M=c[516]|0;U=14.0e4/(+g[38]*+(((M*714&-1)-80|0)>>>0>>>0));c[772]=~~(R<U?R:U);if((M|0)==1){c[766]=0;W=1;break}else if((M|0)==2){c[766]=1;W=2;break}else{c[766]=2;W=M;break}}}while(0);be(13048+(uA(13048)|0)|0,78736,(v=i,i=i+24|0,c[v>>2]=L?78704:78720,c[v+8>>2]=P,c[v+16>>2]=W,v)|0);uC(13048,(a[225792]|0?78656:95304)|0);W=13048+(uA(13048)|0)|0;be(W|0,78504,(v=i,i=i+8|0,h[v>>3]=+h[6449],v)|0);W=13048+(uA(13048)|0)|0;P=c[778]|0;be(W|0,78416,(v=i,i=i+24|0,c[v>>2]=a[225800]|0?131504:131584,c[v+8>>2]=25680,c[v+16>>2]=P,v)|0);i=b;return}function qy(a){a=a|0;if((a|0)==180|(a|0)==(-180|0)){c[780]=2}else if((a|0)==(-270|0)|(a|0)==90){c[780]=3}else if((a|0)==(-90|0)|(a|0)==270){c[780]=1}else{c[780]=0}return 1}function qz(a){a=a|0;c[776]=a;return 1}function qA(){var b=0,d=0,e=0.0,f=0,h=0,i=0;tt();if((a[33512]&1)==0){b=c[524]|0;if(b>>>0>=(c[520]|0)>>>0){d=c[m>>2]|0;aI(79440,40,1,d|0);return}d=b+1|0;c[524]=d;e=+(5e3/((c[772]|0)>>>0)>>>0>>>0>>>0);b=c[526]|0;f=~~(e+ +g[178]*+((b-1|0)>>>0>>>0)*1030.0);c[514]=f;h=c[522]|0;i=~~(e+ +g[38]*+(((h*714&-1)-80|0)>>>0>>>0));c[512]=i;if(((d>>>0)%((c[518]|0)>>>0)>>>0|0)==0){c[526]=1;c[522]=h+1}else{c[526]=b+1}c[554]=f;c[552]=i}else{i=a[225808]|0;c[(c[3524]|0)+8>>2]=i?1400:950;c[(c[3524]|0)+12>>2]=i?950:1400;c[554]=50;f=i?1e3:1450;c[552]=f;c[512]=f;c[514]=50}c[9372]=0;c[774]=0;c[780]=0;c[768]=1;c[770]=0;c[776]=0;a[25936]=a[89416]|0;a[25937|0]=a[89417|0]|0;a[25938|0]=a[89418|0]|0;a[25939|0]=a[89419|0]|0;a[25940|0]=a[89420|0]|0;a[25941|0]=a[89421|0]|0;a[26448]=a[28976]|0;a[26449|0]=a[28977|0]|0;a[26450|0]=a[28978|0]|0;return}function qB(b,d){b=b|0;d=d|0;c[554]=(c[514]|0)+b;c[552]=(c[512]|0)-d;if(!(a[46624]|0)){return}tt();return}function qC(b,d){b=b|0;d=d|0;var e=0,f=0;e=(c[514]|0)+b|0;b=(c[512]|0)-d|0;if(a[46624]|0){f=c[9372]|0}else{c[666]=c[554];c[566]=c[552];c[9372]=1;a[46624]=1;f=1}c[2664+(f<<2)>>2]=e;c[2264+(f<<2)>>2]=b;c[554]=e;c[552]=b;b=f+1|0;c[9372]=b;a[46624]=1;if((b|0)!=100){return}tt();return}function qD(b){b=b|0;var d=0;tt();if((b|0)>-1){d=((b|0)%10&-1)+2|0}else{d=(b|0)<-2?0:b+2|0}if(a[225792]|0){a[25936]=a[89416]|0;a[25937|0]=a[89417|0]|0;a[25938|0]=a[89418|0]|0;a[25939|0]=a[89419|0]|0;a[25940|0]=a[89420|0]|0;a[25941|0]=a[89421|0]|0;a[26448]=a[28976]|0;a[26449|0]=a[28977|0]|0;a[26450|0]=a[28978|0]|0}else{b=c[28928+(d<<2)>>2]|0;uB(25936,b|0);b=28976+(d*3&-1)|0;a[26448]=a[b]|0;a[26449|0]=a[b+1|0]|0;a[26450|0]=a[b+2|0]|0}c[768]=~~(+h[6448]*+h[6449]*+((c[2112+(d<<2)>>2]|0)>>>0>>>0)+.5);if(!(a[225800]|0)){c[770]=c[2160+(d<<2)>>2];return}if((d|0)==1){c[770]=5;return}else{c[770]=0;return}}function qE(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0;f=i;g=c[3524]|0;tt();if((a[e]|0)==0){i=f;return}h=((c[512]|0)-d|0)-((c[g+16>>2]|0)>>>1)|0;g=(c[514]|0)+b|0;b=c[778]|0;d=c[780]|0;j=(d|0)!=0?1:c[776]|0;k=c[774]|0;cf(c[10030]|0,79720,(v=i,i=i+72|0,c[v>>2]=25936,c[v+8>>2]=g,c[v+16>>2]=h,c[v+24>>2]=25680,c[v+32>>2]=b,c[v+40>>2]=j,c[v+48>>2]=d,c[v+56>>2]=k,c[v+64>>2]=e,v)|0);c[774]=(c[774]|0)+2;i=f;return}function qF(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0.0,g=0.0,j=0.0,k=0.0,l=0.0,m=0.0,n=0.0,o=0,p=0,q=0.0,r=0.0;e=i;f=+(((c[514]|0)+a|0)>>>0>>>0);g=+(((c[512]|0)-b|0)>>>0>>>0);j=+h[3817]*8.0;k=j==0.0?.1:j;tt();do{if((d|0)!=-1){b=(d|0)%63&-1;if((b|0)==27){a=c[10030]|0;aI(83312,8,1,a|0);a=c[10030]|0;j=f-k;l=g-k;m=f+k;n=g+k;o=c[774]|0;c[774]=o+1;cf(a|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=l,h[v+24>>3]=m,h[v+32>>3]=n,c[v+40>>2]=o,v)|0);o=c[10030]|0;n=k*2.0;a=c[774]|0;c[774]=a+1;cf(o|0,80304,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=l,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=j,h[v+48>>3]=g,h[v+56>>3]=m,h[v+64>>3]=g,h[v+72>>3]=n,h[v+80>>3]=n,c[v+88>>2]=a,v)|0);a=c[774]|0;o=a+2|0;p=c[10030]|0;c[774]=a+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o,v)|0);i=e;return}else if((b|0)==50){aI(83312,8,1,c[10030]|0);n=g+k;m=g-k;j=f-k;o=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=n,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=m,h[v+56>>3]=j,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=n,c[v+88>>2]=o,v)|0);n=k*.5;o=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=g,h[v+24>>3]=f,h[v+32>>3]=m,h[v+40>>3]=f+n,h[v+48>>3]=g-n,h[v+56>>3]=f-n,h[v+64>>3]=g+n,h[v+72>>3]=j,h[v+80>>3]=g,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==10){j=g+k;n=g-k*.75;o=c[774]|0;cf(c[10030]|0,82472,(v=i,i=i+80|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=f-k,h[v+32>>3]=n,h[v+40>>3]=f+k,h[v+48>>3]=n,h[v+56>>3]=f,h[v+64>>3]=j,c[v+72>>2]=o,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==39){aI(83312,8,1,c[10030]|0);o=c[10030]|0;j=f+k;n=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=g-k,h[v+24>>3]=j,h[v+32>>3]=n,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=g,h[v+24>>3]=j,h[v+32>>3]=n,c[v+40>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==26){aI(83312,8,1,c[10030]|0);o=c[10030]|0;n=f-k;j=g-k;m=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=j,h[v+24>>3]=f+k,h[v+32>>3]=m,c[v+40>>2]=p,v)|0);p=c[10030]|0;l=k*2.0;o=c[774]|0;c[774]=o+1;cf(p|0,80448,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=m,h[v+56>>3]=n,h[v+64>>3]=g,h[v+72>>3]=l,h[v+80>>3]=l,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==1){l=f-k;n=g-k;m=f+k;j=g+k;o=c[774]|0;cf(c[10030]|0,83432,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=n,h[v+24>>3]=m,h[v+32>>3]=j,c[v+40>>2]=o+1,c[v+48>>2]=25936,h[v+56>>3]=l,h[v+64>>3]=j,h[v+72>>3]=m,h[v+80>>3]=n,c[v+88>>2]=o+2,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+5;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+4,v)|0);i=e;return}else if((b|0)==41){aI(83312,8,1,c[10030]|0);o=c[10030]|0;n=f-k;m=g-k;j=f+k;l=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=m,h[v+24>>3]=j,h[v+32>>3]=l,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=m,h[v+24>>3]=f,h[v+32>>3]=g,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=g,h[v+24>>3]=j,h[v+32>>3]=l,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+4;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+3,v)|0);i=e;return}else if((b|0)==31){aI(83312,8,1,c[10030]|0);p=c[10030]|0;l=g-k;o=c[774]|0;c[774]=o+1;cf(p|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=l,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=o,v)|0);o=c[10030]|0;j=k*.25;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-j,h[v+16>>3]=l,h[v+24>>3]=f+j,h[v+32>>3]=g,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==57){aI(83312,8,1,c[10030]|0);j=g+k;l=f+k;m=f-k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=l,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=m,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=j,c[v+88>>2]=p,v)|0);j=k*.5;n=f-j;q=g-j;r=g+j;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=g,h[v+24>>3]=n,h[v+32>>3]=q,h[v+40>>3]=f,h[v+48>>3]=g,h[v+56>>3]=n,h[v+64>>3]=r,h[v+72>>3]=m,h[v+80>>3]=g,c[v+88>>2]=p,v)|0);m=f+j;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=g,h[v+24>>3]=m,h[v+32>>3]=q,h[v+40>>3]=l,h[v+48>>3]=g,h[v+56>>3]=m,h[v+64>>3]=r,h[v+72>>3]=f,h[v+80>>3]=g,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+4;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+3,v)|0);i=e;return}else if((b|0)==43){aI(83312,8,1,c[10030]|0);p=c[10030]|0;r=f-k;m=f+k;l=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=r,h[v+16>>3]=g-k,h[v+24>>3]=m,h[v+32>>3]=l,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=r,h[v+16>>3]=g,h[v+24>>3]=m,h[v+32>>3]=l,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==12){l=g+k;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=l,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=l,c[v+88>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==52){aI(83312,8,1,c[10030]|0);l=g+k;m=g-k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=l,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=m,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=l,c[v+88>>2]=p,v)|0);r=k*.5;q=f-r;j=g-r;n=f+r;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=m,h[v+40>>3]=n,h[v+48>>3]=j,h[v+56>>3]=f,h[v+64>>3]=g,h[v+72>>3]=q,h[v+80>>3]=j,c[v+88>>2]=p,v)|0);j=g+r;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=n,h[v+48>>3]=j,h[v+56>>3]=f,h[v+64>>3]=l,h[v+72>>3]=q,h[v+80>>3]=j,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+4;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+3,v)|0);i=e;return}else if((b|0)==8){j=g-k;q=g+k*.75;p=c[774]|0;cf(c[10030]|0,82472,(v=i,i=i+80|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=f+k,h[v+32>>3]=q,h[v+40>>3]=f-k,h[v+48>>3]=q,h[v+56>>3]=f,h[v+64>>3]=j,c[v+72>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==29){aI(83312,8,1,c[10030]|0);p=c[10030]|0;j=f-k;q=g-k;l=f+k;o=c[774]|0;c[774]=o+1;cf(p|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=q,h[v+24>>3]=l,h[v+32>>3]=g+k,c[v+40>>2]=o,v)|0);o=c[10030]|0;n=k*2.0;p=c[774]|0;c[774]=p+1;cf(o|0,80048,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=q,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=q,h[v+56>>3]=l,h[v+64>>3]=g,h[v+72>>3]=n,h[v+80>>3]=n,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==45){aI(83312,8,1,c[10030]|0);p=c[10030]|0;n=f-k;l=g-k;q=f+k;j=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=l,h[v+24>>3]=q,h[v+32>>3]=j,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=l,h[v+24>>3]=f,h[v+32>>3]=g,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=g,h[v+24>>3]=q,h[v+32>>3]=j,c[v+40>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+4;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+3,v)|0);i=e;return}else if((b|0)==49){aI(83312,8,1,c[10030]|0);j=g+k;q=f-k;o=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=q,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=j,c[v+88>>2]=o,v)|0);j=k*.5;n=f-j;o=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=g,h[v+24>>3]=n,h[v+32>>3]=g-j,h[v+40>>3]=f,h[v+48>>3]=g,h[v+56>>3]=n,h[v+64>>3]=g+j,h[v+72>>3]=q,h[v+80>>3]=g,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==14){q=g-k;j=g-k*.25;n=k*.625;l=g+k;o=c[774]|0;cf(c[10030]|0,81840,(v=i,i=i+112|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=q,h[v+24>>3]=f+k,h[v+32>>3]=j,h[v+40>>3]=f+n,h[v+48>>3]=l,h[v+56>>3]=f-n,h[v+64>>3]=l,h[v+72>>3]=f-k,h[v+80>>3]=j,h[v+88>>3]=f,h[v+96>>3]=q,c[v+104>>2]=o,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==47){aI(83312,8,1,c[10030]|0);q=g+k;o=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=q,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=q,c[v+88>>2]=o,v)|0);q=k*.625;j=f-q;l=k*.375;n=g-l;r=k*.125;o=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=n,h[v+24>>3]=f-l,h[v+32>>3]=g-q,h[v+40>>3]=f+r,h[v+48>>3]=g-r,h[v+56>>3]=f-r,h[v+64>>3]=g+r,h[v+72>>3]=j,h[v+80>>3]=n,c[v+88>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p,v)|0);c[774]=(c[774]|0)+2;i=e;return}else if((b|0)==30){p=c[774]|0;cf(c[10030]|0,82736,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=g-k,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==4){p=c[774]|0;cf(c[10030]|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=g-k,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==23){aI(83312,8,1,c[10030]|0);p=c[10030]|0;n=f-k;j=g-k;r=f+k;q=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=j,h[v+24>>3]=r,h[v+32>>3]=q,c[v+40>>2]=o,v)|0);o=c[10030]|0;l=k*2.0;p=c[774]|0;c[774]=p+1;cf(o|0,80720,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=q,h[v+56>>3]=r,h[v+64>>3]=g,h[v+72>>3]=l,h[v+80>>3]=l,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==11){l=g+k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=l,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=l,c[v+88>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==56){aI(83312,8,1,c[10030]|0);l=g+k;r=f+k;q=g-k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=l,h[v+24>>3]=r,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=q,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=l,c[v+88>>2]=p,v)|0);l=k*.5;j=f-l;n=g-l;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=n,h[v+24>>3]=f,h[v+32>>3]=q,h[v+40>>3]=r,h[v+48>>3]=g,h[v+56>>3]=f+l,h[v+64>>3]=g+l,h[v+72>>3]=j,h[v+80>>3]=n,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==20){aI(83312,8,1,c[10030]|0);p=c[10030]|0;n=f-k;j=g-k;l=f+k;r=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=j,h[v+24>>3]=l,h[v+32>>3]=r,c[v+40>>2]=o,v)|0);o=c[10030]|0;q=k*2.0;p=c[774]|0;c[774]=p+1;cf(o|0,81608,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=l,h[v+48>>3]=g,h[v+56>>3]=f,h[v+64>>3]=j,h[v+72>>3]=q,h[v+80>>3]=q,c[v+88>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,81160,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=n,h[v+48>>3]=g,h[v+56>>3]=f,h[v+64>>3]=r,h[v+72>>3]=q,h[v+80>>3]=q,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+4;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+3,v)|0);i=e;return}else if((b|0)==28){aI(83312,8,1,c[10030]|0);o=c[10030]|0;q=f-k;r=g-k;p=c[774]|0;c[774]=p+1;cf(o|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=r,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);p=c[10030]|0;n=k*2.0;o=c[774]|0;c[774]=o+1;cf(p|0,80200,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=r,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=q,h[v+48>>3]=g,h[v+56>>3]=f,h[v+64>>3]=r,h[v+72>>3]=n,h[v+80>>3]=n,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==19){aI(83312,8,1,c[10030]|0);o=c[10030]|0;n=f-k;r=g-k;q=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=r,h[v+24>>3]=f+k,h[v+32>>3]=q,c[v+40>>2]=p,v)|0);p=c[10030]|0;j=k*2.0;o=c[774]|0;c[774]=o+1;cf(p|0,81160,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=n,h[v+16>>3]=r,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=n,h[v+48>>3]=g,h[v+56>>3]=f,h[v+64>>3]=q,h[v+72>>3]=j,h[v+80>>3]=j,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==17){aI(83312,8,1,c[10030]|0);o=c[10030]|0;j=f-k;q=g-k;p=c[774]|0;c[774]=p+1;cf(o|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=q,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);p=c[10030]|0;n=k*2.0;o=c[774]|0;c[774]=o+1;cf(p|0,81472,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=q,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=q,h[v+56>>3]=j,h[v+64>>3]=g,h[v+72>>3]=n,h[v+80>>3]=n,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==59){aI(83312,8,1,c[10030]|0);n=g+k;j=f+k;o=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=n,h[v+24>>3]=j,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=n,c[v+88>>2]=o,v)|0);q=k*.5;r=f-q;l=g+q;o=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=r,h[v+16>>3]=l,h[v+24>>3]=f+q,h[v+32>>3]=g-q,h[v+40>>3]=j,h[v+48>>3]=g,h[v+56>>3]=f,h[v+64>>3]=n,h[v+72>>3]=r,h[v+80>>3]=l,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==7){l=g-k;r=g+k*.75;o=c[774]|0;cf(c[10030]|0,82600,(v=i,i=i+80|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=l,h[v+24>>3]=f+k,h[v+32>>3]=r,h[v+40>>3]=f-k,h[v+48>>3]=r,h[v+56>>3]=f,h[v+64>>3]=l,c[v+72>>2]=o,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==60){aI(83312,8,1,c[10030]|0);l=g+k;r=f+k;n=g-k;o=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=l,h[v+24>>3]=r,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=n,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=l,c[v+88>>2]=o,v)|0);j=k*.5;q=f-j;m=g-j;o=c[774]|0;cf(c[10030]|0,79928,(v=i,i=i+128|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=m,h[v+24>>3]=f,h[v+32>>3]=n,h[v+40>>3]=r,h[v+48>>3]=g,h[v+56>>3]=f,h[v+64>>3]=l,h[v+72>>3]=q,h[v+80>>3]=g+j,h[v+88>>3]=f,h[v+96>>3]=g,h[v+104>>3]=q,h[v+112>>3]=m,c[v+120>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==35){aI(83312,8,1,c[10030]|0);o=c[10030]|0;m=f-k;q=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=g-k,h[v+24>>3]=f+k,h[v+32>>3]=q,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=g,h[v+24>>3]=f,h[v+32>>3]=q,c[v+40>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==61){aI(83312,8,1,c[10030]|0);q=g+k;m=f+k;j=f-k;o=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=q,h[v+24>>3]=m,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=j,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=q,c[v+88>>2]=o,v)|0);l=k*.5;r=g-l;o=c[774]|0;cf(c[10030]|0,79928,(v=i,i=i+128|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=g,h[v+24>>3]=f-l,h[v+32>>3]=r,h[v+40>>3]=f,h[v+48>>3]=g,h[v+56>>3]=f+l,h[v+64>>3]=r,h[v+72>>3]=m,h[v+80>>3]=g,h[v+88>>3]=f,h[v+96>>3]=q,h[v+104>>3]=j,h[v+112>>3]=g,c[v+120>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==54){aI(83312,8,1,c[10030]|0);j=g+k;q=g-k;m=f-k;o=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=q,h[v+56>>3]=m,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=j,c[v+88>>2]=o,v)|0);r=k*.5;l=f+r;o=c[774]|0;cf(c[10030]|0,79928,(v=i,i=i+128|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=g,h[v+24>>3]=f,h[v+32>>3]=q,h[v+40>>3]=l,h[v+48>>3]=g-r,h[v+56>>3]=f,h[v+64>>3]=g,h[v+72>>3]=l,h[v+80>>3]=g+r,h[v+88>>3]=f,h[v+96>>3]=j,h[v+104>>3]=m,h[v+112>>3]=g,c[v+120>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==6){o=c[774]|0;cf(c[10030]|0,82736,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=g-k,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=o,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==40){aI(83312,8,1,c[10030]|0);o=c[10030]|0;m=g-k;j=f+k;r=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=m,h[v+24>>3]=j,h[v+32>>3]=r,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=m,h[v+24>>3]=j,h[v+32>>3]=r,c[v+40>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==15){aI(83312,8,1,c[10030]|0);o=c[10030]|0;r=f-k;j=g-k;p=c[774]|0;c[774]=p+1;cf(o|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=r,h[v+16>>3]=j,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);p=c[10030]|0;m=k*.342;l=g-k*.94;q=k*2.0;o=c[774]|0;c[774]=o+1;cf(p|0,81704,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=r,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f+m,h[v+48>>3]=l,h[v+56>>3]=f-m,h[v+64>>3]=l,h[v+72>>3]=q,h[v+80>>3]=q,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==2){aI(83312,8,1,c[10030]|0);o=c[10030]|0;q=g-k;l=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,83184,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=q+-1.0,h[v+24>>3]=f,h[v+32>>3]=l+1.0,c[v+40>>2]=p,v)|0);p=c[10030]|0;m=f-k;j=f+k;o=c[774]|0;c[774]=o+1;cf(p|0,83112,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m+-1.0,h[v+16>>3]=g,h[v+24>>3]=j+1.0,h[v+32>>3]=g,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,83024,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=q,h[v+24>>3]=j,h[v+32>>3]=l,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,83024,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=l,h[v+24>>3]=j,h[v+32>>3]=q,c[v+40>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+5;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+4,v)|0);i=e;return}else if((b|0)==18){aI(83312,8,1,c[10030]|0);o=c[10030]|0;q=f-k;j=g-k;l=f+k;p=c[774]|0;c[774]=p+1;cf(o|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=j,h[v+24>>3]=l,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);p=c[10030]|0;m=k*2.0;o=c[774]|0;c[774]=o+1;cf(p|0,81368,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=l,h[v+48>>3]=g,h[v+56>>3]=q,h[v+64>>3]=g,h[v+72>>3]=m,h[v+80>>3]=m,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==38){aI(83312,8,1,c[10030]|0);o=c[10030]|0;m=f-k;q=g-k;l=f+k;j=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=q,h[v+24>>3]=l,h[v+32>>3]=j,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=q,h[v+24>>3]=l,h[v+32>>3]=g,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=g,h[v+24>>3]=f,h[v+32>>3]=j,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+4;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+3,v)|0);i=e;return}else if((b|0)==51){aI(83312,8,1,c[10030]|0);j=g+k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=j,c[v+88>>2]=p,v)|0);m=k*.5;l=f-m;q=g+m;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=q,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f+m,h[v+48>>3]=q,h[v+56>>3]=f,h[v+64>>3]=j,h[v+72>>3]=l,h[v+80>>3]=q,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==34){aI(83312,8,1,c[10030]|0);p=c[10030]|0;q=f-k;l=g-k;j=f+k;o=c[774]|0;c[774]=o+1;cf(p|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=l,h[v+24>>3]=j,h[v+32>>3]=g+k,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=l,h[v+24>>3]=j,h[v+32>>3]=g,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==0){p=c[774]|0;cf(c[10030]|0,83736,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=g-k+-1.0,h[v+24>>3]=f,h[v+32>>3]=g+k+1.0,c[v+40>>2]=p+1,c[v+48>>2]=25936,h[v+56>>3]=f-k+-1.0,h[v+64>>3]=g,h[v+72>>3]=f+k+1.0,h[v+80>>3]=g,c[v+88>>2]=p+2,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+5;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+4,v)|0);i=e;return}else if((b|0)==9){j=g+k;l=g-k*.75;p=c[774]|0;cf(c[10030]|0,82600,(v=i,i=i+80|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=f-k,h[v+32>>3]=l,h[v+40>>3]=f+k,h[v+48>>3]=l,h[v+56>>3]=f,h[v+64>>3]=j,c[v+72>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==3){p=c[774]|0;cf(c[10030]|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=g-k,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==46){p=c[774]|0;cf(c[10030]|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=g-k,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==55){aI(83312,8,1,c[10030]|0);j=g+k;l=f+k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=l,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=j,c[v+88>>2]=p,v)|0);j=k*.5;q=f+j;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=g,h[v+24>>3]=q,h[v+32>>3]=g-j,h[v+40>>3]=l,h[v+48>>3]=g,h[v+56>>3]=q,h[v+64>>3]=g+j,h[v+72>>3]=f,h[v+80>>3]=g,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==37){aI(83312,8,1,c[10030]|0);p=c[10030]|0;j=f-k;q=g-k;l=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=q,h[v+24>>3]=f+k,h[v+32>>3]=l,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=q,h[v+24>>3]=f,h[v+32>>3]=l,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==22){aI(83312,8,1,c[10030]|0);p=c[10030]|0;l=f-k;q=g-k;j=f+k;m=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=q,h[v+24>>3]=j,h[v+32>>3]=m,c[v+40>>2]=o,v)|0);o=c[10030]|0;r=k*2.0;p=c[774]|0;c[774]=p+1;cf(o|0,80872,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=q,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=j,h[v+48>>3]=g,h[v+56>>3]=f,h[v+64>>3]=m,h[v+72>>3]=r,h[v+80>>3]=r,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==48){aI(83312,8,1,c[10030]|0);r=g+k;m=g-k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=r,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=m,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=r,c[v+88>>2]=p,v)|0);r=k*.5;j=f-r;q=g-r;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=j,h[v+16>>3]=q,h[v+24>>3]=f,h[v+32>>3]=m,h[v+40>>3]=f+r,h[v+48>>3]=q,h[v+56>>3]=f,h[v+64>>3]=g,h[v+72>>3]=j,h[v+80>>3]=q,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==21){aI(83312,8,1,c[10030]|0);p=c[10030]|0;q=f-k;j=g-k;r=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=j,h[v+24>>3]=f+k,h[v+32>>3]=r,c[v+40>>2]=o,v)|0);o=c[10030]|0;m=k*2.0;p=c[774]|0;c[774]=p+1;cf(o|0,81040,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=j,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=j,h[v+56>>3]=f,h[v+64>>3]=r,h[v+72>>3]=m,h[v+80>>3]=m,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==5){p=c[774]|0;cf(c[10030]|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=g-k,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==25){aI(83312,8,1,c[10030]|0);p=c[10030]|0;m=f-k;r=g-k;j=f+k;q=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=r,h[v+24>>3]=j,h[v+32>>3]=q,c[v+40>>2]=o,v)|0);o=c[10030]|0;l=k*2.0;p=c[774]|0;c[774]=p+1;cf(o|0,81472,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=r,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=r,h[v+56>>3]=m,h[v+64>>3]=g,h[v+72>>3]=l,h[v+80>>3]=l,c[v+88>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,80720,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=r,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=q,h[v+56>>3]=j,h[v+64>>3]=g,h[v+72>>3]=l,h[v+80>>3]=l,c[v+88>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+4;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+3,v)|0);i=e;return}else if((b|0)==42){aI(83312,8,1,c[10030]|0);o=c[10030]|0;l=f-k;j=g-k;q=f+k;r=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=j,h[v+24>>3]=q,h[v+32>>3]=r,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=j,h[v+24>>3]=q,h[v+32>>3]=g,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=g,h[v+24>>3]=q,h[v+32>>3]=r,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+4;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+3,v)|0);i=e;return}else if((b|0)==58){aI(83312,8,1,c[10030]|0);r=g+k;q=f+k;j=g-k;l=f-k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=r,h[v+24>>3]=q,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=j,h[v+56>>3]=l,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=r,c[v+88>>2]=p,v)|0);r=k*.5;m=g+r;p=c[774]|0;cf(c[10030]|0,79928,(v=i,i=i+128|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=g,h[v+24>>3]=f,h[v+32>>3]=j,h[v+40>>3]=q,h[v+48>>3]=g,h[v+56>>3]=f+r,h[v+64>>3]=m,h[v+72>>3]=f,h[v+80>>3]=g,h[v+88>>3]=f-r,h[v+96>>3]=m,h[v+104>>3]=l,h[v+112>>3]=g,c[v+120>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==32){aI(83312,8,1,c[10030]|0);p=c[10030]|0;l=g-k;m=f+k;o=c[774]|0;c[774]=o+1;cf(p|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f-k,h[v+16>>3]=l,h[v+24>>3]=m,h[v+32>>3]=g+k,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=l,h[v+24>>3]=m,h[v+32>>3]=g,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==(-1|0)){break}else if((b|0)==13){m=g-k;l=g-k*.25;r=k*.625;q=g+k;p=c[774]|0;cf(c[10030]|0,82080,(v=i,i=i+112|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=m,h[v+24>>3]=f+k,h[v+32>>3]=l,h[v+40>>3]=f+r,h[v+48>>3]=q,h[v+56>>3]=f-r,h[v+64>>3]=q,h[v+72>>3]=f-k,h[v+80>>3]=l,h[v+88>>3]=f,h[v+96>>3]=m,c[v+104>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==24){aI(83312,8,1,c[10030]|0);p=c[10030]|0;m=f-k;l=g-k;q=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=l,h[v+24>>3]=f+k,h[v+32>>3]=q,c[v+40>>2]=o,v)|0);o=c[10030]|0;r=k*2.0;p=c[774]|0;c[774]=p+1;cf(o|0,80616,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=l,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=q,h[v+56>>3]=f,h[v+64>>3]=l,h[v+72>>3]=r,h[v+80>>3]=r,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==62){r=g+k;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=r,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=f-k,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=r,c[v+88>>2]=p,v)|0);c[774]=(c[774]|0)+1;i=e;return}else if((b|0)==53){aI(83312,8,1,c[10030]|0);r=g+k;l=f-k;p=c[774]|0;cf(c[10030]|0,82320,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=r,h[v+24>>3]=f+k,h[v+32>>3]=g,h[v+40>>3]=f,h[v+48>>3]=g-k,h[v+56>>3]=l,h[v+64>>3]=g,h[v+72>>3]=f,h[v+80>>3]=r,c[v+88>>2]=p,v)|0);q=k*.5;p=c[774]|0;cf(c[10030]|0,82216,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=g,h[v+24>>3]=f-q,h[v+32>>3]=g-q,h[v+40>>3]=f+q,h[v+48>>3]=g+q,h[v+56>>3]=f,h[v+64>>3]=r,h[v+72>>3]=l,h[v+80>>3]=g,c[v+88>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+3;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+2,v)|0);i=e;return}else if((b|0)==44){aI(83312,8,1,c[10030]|0);p=c[10030]|0;l=f-k;r=g-k;q=f+k;m=g+k;o=c[774]|0;c[774]=o+1;cf(p|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=r,h[v+24>>3]=q,h[v+32>>3]=m,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=r,h[v+24>>3]=q,h[v+32>>3]=g,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=l,h[v+16>>3]=g,h[v+24>>3]=q,h[v+32>>3]=m,c[v+40>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+4;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+3,v)|0);i=e;return}else if((b|0)==33){aI(83312,8,1,c[10030]|0);o=c[10030]|0;m=f-k;q=g-k;p=c[774]|0;c[774]=p+1;cf(o|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=q,h[v+24>>3]=f+k,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=m,h[v+16>>3]=q,h[v+24>>3]=f,h[v+32>>3]=g,c[v+40>>2]=o,v)|0);o=c[774]|0;p=c[10030]|0;c[774]=o+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=o+2,v)|0);i=e;return}else if((b|0)==36){aI(83312,8,1,c[10030]|0);o=c[10030]|0;q=f-k;m=g-k;l=f+k;r=g+k;p=c[774]|0;c[774]=p+1;cf(o|0,82960,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=m,h[v+24>>3]=l,h[v+32>>3]=r,c[v+40>>2]=p,v)|0);p=c[10030]|0;o=c[774]|0;c[774]=o+1;cf(p|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=m,h[v+24>>3]=l,h[v+32>>3]=g,c[v+40>>2]=o,v)|0);o=c[10030]|0;p=c[774]|0;c[774]=p+1;cf(o|0,82880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=q,h[v+16>>3]=g,h[v+24>>3]=f,h[v+32>>3]=r,c[v+40>>2]=p,v)|0);p=c[774]|0;o=c[10030]|0;c[774]=p+4;cf(o|0,83632,(v=i,i=i+8|0,c[v>>2]=p+3,v)|0);i=e;return}else if((b|0)==16){aI(83312,8,1,c[10030]|0);b=c[10030]|0;r=f-k;q=g-k;l=f+k;p=c[774]|0;c[774]=p+1;cf(b|0,82792,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=r,h[v+16>>3]=q,h[v+24>>3]=l,h[v+32>>3]=g+k,c[v+40>>2]=p,v)|0);p=c[10030]|0;m=k*2.0;b=c[774]|0;c[774]=b+1;cf(p|0,81608,(v=i,i=i+96|0,c[v>>2]=25936,h[v+8>>3]=r,h[v+16>>3]=q,h[v+24>>3]=f,h[v+32>>3]=g,h[v+40>>3]=l,h[v+48>>3]=g,h[v+56>>3]=f,h[v+64>>3]=q,h[v+72>>3]=m,h[v+80>>3]=m,c[v+88>>2]=b,v)|0);b=c[774]|0;p=c[10030]|0;c[774]=b+3;cf(p|0,83632,(v=i,i=i+8|0,c[v>>2]=b+2,v)|0);i=e;return}else{i=e;return}}}while(0);d=c[10030]|0;b=c[774]|0;c[774]=b+1;cf(d|0,83880,(v=i,i=i+48|0,c[v>>2]=25936,h[v+8>>3]=f,h[v+16>>3]=g,h[v+24>>3]=f+1.0,h[v+32>>3]=g+1.0,c[v+40>>2]=b,v)|0);i=e;return}function qG(a){a=+a;return}function qH(a){a=a|0;return 0}function qI(){return}function qJ(){return}function qK(){return}function qL(a){a=+a;h[6448]=a<0.0?1.0:a;return}function qM(a,b){a=a|0;b=b|0;c[1082]=a;c[1080]=1e3-b;return}function qN(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0;f=i;tt();g=c[514]|0;h=g+d|0;d=c[512]|0;j=d-e|0;e=c[768]|0;k=c[774]|0;l=c[770]|0;cf(c[10030]|0,83952,(v=i,i=i+72|0,c[v>>2]=25936,c[v+8>>2]=2,c[v+16>>2]=g+a,c[v+24>>2]=d-b,c[v+32>>2]=h,c[v+40>>2]=j,c[v+48>>2]=e,c[v+56>>2]=k,c[v+64>>2]=l,v)|0);c[774]=(c[774]|0)+1;c[554]=h;c[552]=j;i=f;return}function qO(b){b=b|0;var d=0,e=0,f=0;d=i;if((b|0)==0){e=18229}else{if((a[b]|0)==0){e=18229}else{f=b}}if((e|0)==18229){f=25424}e=bk(f|0,148464)|0;if((e|0)>0){uF(25680,f|0,e|0);a[e+25680|0]=0}ca(f+(e+1|0)|0,21e4,(v=i,i=i+8|0,c[v>>2]=3112,v)|0);i=d;return 1}function qP(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0;g=i;i=i+48|0;h=g|0;c[h>>2]=b;c[h+4>>2]=d;j=e+b|0;c[h+12>>2]=j;c[h+16>>2]=d;c[h+24>>2]=j;j=f+d|0;c[h+28>>2]=j;c[h+36>>2]=b;c[h+40>>2]=j;c[h+8>>2]=a;qR(4,h|0);i=g;return}function qQ(b){b=b|0;var e=0,f=0,g=0,j=0;e=i;f=c[b>>2]|0;if((f|0)==1){g=c[b+4>>2]|0;tt();if((g|0)>-1){j=((g|0)%10&-1)+2|0}else{j=(g|0)<-2?0:g+2|0}if(a[225792]|0){a[25936]=a[89416]|0;a[25937|0]=a[89417|0]|0;a[25938|0]=a[89418|0]|0;a[25939|0]=a[89419|0]|0;a[25940|0]=a[89420|0]|0;a[25941|0]=a[89421|0]|0;a[26448]=a[28976]|0;a[26449|0]=a[28977|0]|0;a[26450|0]=a[28978|0]|0}else{g=c[28928+(j<<2)>>2]|0;uB(25936,g|0);g=28976+(j*3&-1)|0;a[26448]=a[g]|0;a[26449|0]=a[g+1|0]|0;a[26450|0]=a[g+2|0]|0}c[768]=~~(+h[6448]*+h[6449]*+((c[2112+(j<<2)>>2]|0)>>>0>>>0)+.5);if(!(a[225800]|0)){c[770]=c[2160+(j<<2)>>2];i=e;return}if((j|0)==1){c[770]=5;i=e;return}else{c[770]=0;i=e;return}}else if((f|0)==5){fr(+h[b+8>>3],26448);j=d[26449]|0;g=d[26450]|0;be(25936,84104,(v=i,i=i+24|0,c[v>>2]=d[26448]|0,c[v+8>>2]=j,c[v+16>>2]=g,v)|0);i=e;return}else if((f|0)==3){f=c[b+4>>2]|0;b=f>>>16;a[26448]=b&255;g=f>>>8;a[26449]=g&255;a[26450]=f&255;be(25936,84104,(v=i,i=i+24|0,c[v>>2]=b&255,c[v+8>>2]=g&255,c[v+16>>2]=f&255,v)|0);i=e;return}else{i=e;return}}function qR(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0.0,m=0.0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0;f=i;i=i+256|0;g=f|0;h=c[e+8>>2]|0;j=h>>4;k=h&15;if(a[46624]|0){tt()}if((k|0)==4|(k|0)==1){h=g|0;l=+(100-j|0)*.01;do{if(l<0.0){m=0.0}else{if(l<=1.0){m=l;break}m=1.0}}while(0);n=d[26448]|0;o=~~(+(n|0)+m*+(n^255|0));n=d[26449]|0;p=~~(+(n|0)+m*+(n^255|0));n=d[26450]|0;q=~~(+(n|0)+m*+(n^255|0));be(h|0,84104,(v=i,i=i+24|0,c[v>>2]=o,c[v+8>>2]=p,c[v+16>>2]=q,v)|0);r=1}else if((k|0)==0){q=g|0;a[q]=a[84376]|0;a[q+1|0]=a[84377|0]|0;a[q+2|0]=a[84378|0]|0;a[q+3|0]=a[84379|0]|0;a[q+4|0]=a[84380|0]|0;a[q+5|0]=a[84381|0]|0;r=2}else if((k|0)==5|(k|0)==2){k=c[2224+(((j|0)%10&-1)<<2)>>2]|0;uB(g|0,25936);r=k}else{uB(g|0,25936);r=1}cf(c[10030]|0,84328,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=b+1,v)|0);g=e|0;k=e+4|0;j=(c[512]|0)-(c[k>>2]|0)|0;cf(c[10030]|0,84280,(v=i,i=i+16|0,c[v>>2]=(c[514]|0)+(c[g>>2]|0),c[v+8>>2]=j,v)|0);j=c[10030]|0;if((b|0)>1){s=1;t=j}else{u=j;w=c[g>>2]|0;x=c[514]|0;y=x+w|0;z=c[512]|0;A=c[k>>2]|0;B=z-A|0;C=cf(u|0,84160,(v=i,i=i+16|0,c[v>>2]=y,c[v+8>>2]=B,v)|0)|0;D=c[10030]|0;E=c[774]|0;F=cf(D|0,84120,(v=i,i=i+16|0,c[v>>2]=r,c[v+8>>2]=E,v)|0)|0;G=c[774]|0;H=G+1|0;c[774]=H;i=f;return}while(1){j=(c[512]|0)-(c[e+(s*12&-1)+4>>2]|0)|0;cf(t|0,84280,(v=i,i=i+16|0,c[v>>2]=(c[514]|0)+(c[e+(s*12&-1)>>2]|0),c[v+8>>2]=j,v)|0);j=s+1|0;if((j&7|0)==0){q=c[10030]|0;aI(84248,2,1,q|0)}q=c[10030]|0;if((j|0)<(b|0)){s=j;t=q}else{u=q;break}}w=c[g>>2]|0;x=c[514]|0;y=x+w|0;z=c[512]|0;A=c[k>>2]|0;B=z-A|0;C=cf(u|0,84160,(v=i,i=i+16|0,c[v>>2]=y,c[v+8>>2]=B,v)|0)|0;D=c[10030]|0;E=c[774]|0;F=cf(D|0,84120,(v=i,i=i+16|0,c[v>>2]=r,c[v+8>>2]=E,v)|0)|0;G=c[774]|0;H=G+1|0;c[774]=H;i=f;return}function qS(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0;b=i;a[4304]=0;a[4336]=0;d=c[13898]|0;e=c[8272]|0;L24421:do{if((d|0)<(e|0)){f=c[1054]|0;g=d;h=0;j=0;while(1){k=(a[f+(g*40&-1)|0]&1)==0;L24425:do{if(!k){l=c[f+(g*40&-1)+36>>2]|0;m=f+(g*40&-1)+32|0;n=c[10036]|0;o=0;while(1){if((o|0)>=(l|0)){break}if((a[n+((c[m>>2]|0)+o|0)|0]|0)==(a[o+103664|0]|0)){o=o+1|0}else{break L24425}}if((o|0)==1){p=h;q=j;break L24421}}}while(0);m=c[56574]|0;L24432:do{if((m|0)==0){r=226296}else{n=c[f+(g*40&-1)+36>>2]|0;l=c[f+(g*40&-1)+32>>2]|0;s=(n|0)>0;t=c[10036]|0;u=226296;w=m;while(1){L24436:do{if(!k){if(s){x=0;y=0;z=l;while(1){A=a[w+x|0]|0;if(A<<24>>24==(a[t+(x+z|0)|0]|0)){B=z;C=y}else{if(A<<24>>24!=36){break L24436}B=z-1|0;C=1}D=x+1|0;if((D|0)<(C+n|0)){x=D;y=C;z=B}else{break}}if((C|0)==0){E=D}else{r=u;break L24432}}else{E=0}z=a[w+E|0]|0;if((z<<24>>24|0)==36|(z<<24>>24|0)==0){r=u;break L24432}}}while(0);z=u+8|0;y=c[z>>2]|0;if((y|0)==0){r=z;break}else{u=z;w=y}}}}while(0);k=c[r+4>>2]|0;if((k|0)==0){a[4304]=1;m=g+1|0;c[13898]=m;F=1;G=j;H=m}else if((k|0)==1){a[4336]=1;k=g+1|0;c[13898]=k;F=h;G=1;H=k}else{k=g+1|0;c[13898]=k;F=h;G=j;H=k}if((H|0)<(e|0)){g=H;h=F;j=G}else{p=F;q=G;break}}}else{p=0;q=0}}while(0);be(13048,84616,(v=i,i=i+16|0,c[v>>2]=p?84608:179864,c[v+8>>2]=q?84576:179864,v)|0);i=b;return}function qT(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0;b=i;d=c[10030]|0;if(a[4304]|0){aI(87648,3,1,d|0);if((a[37384]&1)!=0){e=c[10030]|0;f=aD(e|0)|0;i=b;return}g=c[10030]|0;aI(87560,23,1,g|0);g=c[10030]|0;j=c[7938]|0;k=c[7939]|0;l=1e3-(c[7941]|0)|0;m=1e3-(c[7940]|0)|0;cf(g|0,87528,(v=i,i=i+32|0,c[v>>2]=j,c[v+8>>2]=k,c[v+16>>2]=l,c[v+24>>2]=m,v)|0);m=c[10030]|0;aI(87648,3,1,m|0);m=c[10030]|0;aI(87440,25,1,m|0);m=c[10030]|0;n=+h[8255];o=+h[8256];p=+h[8169];q=+h[8170];r=+h[8599];s=+h[8600];t=+h[8513];u=+h[8514];cf(m|0,87360,(v=i,i=i+64|0,h[v>>3]=n,h[v+8>>3]=o,h[v+16>>3]=p,h[v+24>>3]=q,h[v+32>>3]=r,h[v+40>>3]=s,h[v+48>>3]=t,h[v+56>>3]=u,v)|0);m=c[10030]|0;aI(87648,3,1,m|0);if(!(a[4336]|0)){e=c[10030]|0;f=aD(e|0)|0;i=b;return}m=c[10030]|0;aI(87336,17,1,m|0);m=c[10030]|0;aI(87136,88,1,m|0);m=c[10030]|0;aI(87072,41,1,m|0);m=c[10030]|0;aI(86960,43,1,m|0);m=c[10030]|0;aI(86840,107,1,m|0);m=c[10030]|0;aI(86816,9,1,m|0);m=c[10030]|0;aI(86600,72,1,m|0);m=c[10030]|0;aI(86520,72,1,m|0);m=c[10030]|0;aI(86384,72,1,m|0);m=c[10030]|0;aI(86248,72,1,m|0);m=c[10030]|0;aI(86232,11,1,m|0);m=c[10030]|0;aI(86144,2,1,m|0);m=c[10030]|0;aI(87648,3,1,m|0);e=c[10030]|0;f=aD(e|0)|0;i=b;return}else{aI(86144,2,1,d|0);if((a[37384]&1)!=0){e=c[10030]|0;f=aD(e|0)|0;i=b;return}d=c[10030]|0;aI(86024,27,1,d|0);d=c[10030]|0;m=c[7938]|0;l=c[7939]|0;k=1e3-(c[7941]|0)|0;j=1e3-(c[7940]|0)|0;cf(d|0,85936,(v=i,i=i+32|0,c[v>>2]=m,c[v+8>>2]=l,c[v+16>>2]=k,c[v+24>>2]=j,v)|0);j=c[10030]|0;aI(86144,2,1,j|0);j=c[10030]|0;aI(85896,29,1,j|0);j=c[10030]|0;u=+h[8255];t=+h[8256];s=+h[8169];r=+h[8170];q=+h[8599];p=+h[8600];o=+h[8513];n=+h[8514];cf(j|0,85856,(v=i,i=i+64|0,h[v>>3]=u,h[v+8>>3]=t,h[v+16>>3]=s,h[v+24>>3]=r,h[v+32>>3]=q,h[v+40>>3]=p,h[v+48>>3]=o,h[v+56>>3]=n,v)|0);j=c[10030]|0;aI(86144,2,1,j|0);if(!(a[4336]|0)){e=c[10030]|0;f=aD(e|0)|0;i=b;return}j=c[10030]|0;aI(85712,72,1,j|0);j=c[10030]|0;aI(85648,60,1,j|0);j=c[10030]|0;aI(85552,35,1,j|0);j=c[10030]|0;aI(85408,94,1,j|0);j=c[10030]|0;aI(86816,9,1,j|0);j=c[10030]|0;aI(85232,103,1,j|0);j=c[10030]|0;aI(84968,103,1,j|0);j=c[10030]|0;aI(84840,103,1,j|0);j=c[10030]|0;aI(84632,81,1,j|0);j=c[10030]|0;aI(86144,2,1,j|0);j=c[10030]|0;aI(86144,2,1,j|0);e=c[10030]|0;f=aD(e|0)|0;i=b;return}}function qU(){var b=0;b=c[10030]|0;if(a[4304]|0){aI(87976|0,314,1,b|0)}else{aI(87656|0,305,1,b|0)}c[1094]=0;c[1080]=0;c[1082]=0;return}function qV(a){a=a|0;c[1094]=(a|0)<-2?0:a+2&7;return}function qW(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0,p=0.0,q=0.0,r=0,s=0,t=0,u=0.0,w=0.0,x=0,y=0.0,z=0,A=0,B=0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,P=0,Q=0,S=0,T=0.0,U=0.0,V=0.0,W=0,X=0,Y=0,Z=0.0,_=0.0,$=0.0,aa=0.0,ab=0.0,ac=0.0,ad=0.0,ae=0.0,af=0.0,ag=0.0,ah=0.0,ai=0.0;e=i;do{if(a[4336]|0){if((a[37384]&1)!=0){break}f=c[10030]|0;if(a[4304]|0){aI(89232,11,1,f|0);break}else{aI(89104,12,1,f|0);break}}}while(0);f=1e3-d|0;g=c[10030]|0;j=c[1082]|0;k=c[1080]|0;l=c[4344+(c[1094]<<2)>>2]|0;m=+h[539];if(a[4304]|0){cf(g|0,88928,(v=i,i=i+48|0,c[v>>2]=j,c[v+8>>2]=k,c[v+16>>2]=b,c[v+24>>2]=f,c[v+32>>2]=l,h[v+40>>3]=m,v)|0)}else{cf(g|0,88712,(v=i,i=i+48|0,c[v>>2]=j,c[v+8>>2]=k,c[v+16>>2]=b,c[v+24>>2]=f,c[v+32>>2]=l,h[v+40>>3]=m,v)|0)}do{if(a[4336]|0){if((a[37384]&1)!=0){break}l=c[10030]|0;k=a[66164]&1;j=k<<24>>24!=0;if(a[4304]|0){if(j){m=+h[8255];g=c[1082]|0;n=c[7938]|0;o=c[7939]|0;p=+h[8256];q=+R(+(+h[8271]),+(m+ +(g-n|0)/+(o-n|0)*(p-m)));r=g;s=n;t=o;u=m;w=p}else{p=+h[8255];o=c[1082]|0;n=c[7938]|0;g=c[7939]|0;m=+h[8256];q=p+ +(o-n|0)/+(g-n|0)*(m-p);r=o;s=n;t=g;u=p;w=m}g=(a[65476]&1)==0;if(g){m=+h[8169];n=c[1080]|0;o=c[7940]|0;x=c[7941]|0;p=+h[8170];y=m+ +((1e3-n|0)-o|0)/+(x-o|0)*(p-m);z=n;A=o;B=x;C=m;D=p}else{p=+h[8169];x=c[1080]|0;o=c[7940]|0;n=c[7941]|0;m=+h[8170];y=+R(+(+h[8185]),+(p+ +((1e3-x|0)-o|0)/+(n-o|0)*(m-p)));z=x;A=o;B=n;C=p;D=m}n=(a[68916]&1)==0;if(n){m=+h[8599];p=+h[8600];E=m+ +(r-s|0)/+(t-s|0)*(p-m);F=m;G=p}else{p=+h[8599];m=+h[8600];E=+R(+(+h[8615]),+(p+ +(r-s|0)/+(t-s|0)*(m-p)));F=p;G=m}o=(a[68228]&1)==0;if(o){m=+h[8513];p=+h[8514];H=m+ +((1e3-z|0)-A|0)/+(B-A|0)*(p-m);I=m;J=p}else{p=+h[8513];m=+h[8514];H=+R(+(+h[8529]),+(p+ +((1e3-z|0)-A|0)/+(B-A|0)*(m-p)));I=p;J=m}if(k<<24>>24==0){K=u+ +((b-s|0)>>>0>>>0)/+(t-s|0)*(w-u)}else{K=+R(+(+h[8271]),+(u+ +((b-s|0)>>>0>>>0)/+(t-s|0)*(w-u)))}if(g){L=C+ +((d-A|0)>>>0>>>0)/+(B-A|0)*(D-C)}else{L=+R(+(+h[8185]),+(C+ +((d-A|0)>>>0>>>0)/+(B-A|0)*(D-C)))}if(n){M=F+ +((b-s|0)>>>0>>>0)/+(t-s|0)*(G-F)}else{M=+R(+(+h[8615]),+(F+ +((b-s|0)>>>0>>>0)/+(t-s|0)*(G-F)))}if(o){N=I+ +((d-A|0)>>>0>>>0)/+(B-A|0)*(J-I)}else{N=+R(+(+h[8529]),+(I+ +((d-A|0)>>>0>>>0)/+(B-A|0)*(J-I)))}cf(l|0,88632,(v=i,i=i+64|0,h[v>>3]=q,h[v+8>>3]=y,h[v+16>>3]=E,h[v+24>>3]=H,h[v+32>>3]=K,h[v+40>>3]=L,h[v+48>>3]=M,h[v+56>>3]=N,v)|0);o=c[10030]|0;if((a[66164]&1)==0){aI(88544,4,1,o|0)}else{m=+h[8255];n=c[7938]|0;p=+R(+(+h[8271]),+(m+(+(((c[1082]|0)+b|0)>>>0>>>0)*.5- +(n|0))/+((c[7939]|0)-n|0)*(+h[8256]-m)));cf(o|0,88624,(v=i,i=i+8|0,h[v>>3]=p,v)|0)}o=c[10030]|0;if((a[65476]&1)==0){aI(88544,4,1,o|0)}else{p=+h[8169];n=c[7940]|0;m=+R(+(+h[8185]),+(p+(1.0e3- +(((c[1080]|0)+f|0)>>>0>>>0)*.5- +(n|0))/+((c[7941]|0)-n|0)*(+h[8170]-p)));cf(o|0,88624,(v=i,i=i+8|0,h[v>>3]=m,v)|0)}o=c[10030]|0;if((a[68916]&1)==0){aI(88544,4,1,o|0)}else{m=+h[8599];n=c[7938]|0;p=+R(+(+h[8615]),+(m+(+(((c[1082]|0)+b|0)>>>0>>>0)*.5- +(n|0))/+((c[7939]|0)-n|0)*(+h[8600]-m)));cf(o|0,88624,(v=i,i=i+8|0,h[v>>3]=p,v)|0)}o=c[10030]|0;if((a[68228]&1)==0){aI(88528,3,1,o|0)}else{p=+h[8513];n=c[7940]|0;m=+R(+(+h[8529]),+(p+(1.0e3- +(((c[1080]|0)+f|0)>>>0>>>0)*.5- +(n|0))/+((c[7941]|0)-n|0)*(+h[8514]-p)));cf(o|0,88536,(v=i,i=i+8|0,h[v>>3]=m,v)|0)}o=c[10030]|0;aI(88480,4,1,o|0);c[1082]=b;c[1080]=f;i=e;return}else{if(j){m=+h[8255];j=c[1082]|0;o=c[7938]|0;n=c[7939]|0;p=+h[8256];O=+R(+(+h[8271]),+(m+ +(j-o|0)/+(n-o|0)*(p-m)));P=j;Q=o;S=n;T=m;U=p}else{p=+h[8255];n=c[1082]|0;o=c[7938]|0;j=c[7939]|0;m=+h[8256];O=p+ +(n-o|0)/+(j-o|0)*(m-p);P=n;Q=o;S=j;T=p;U=m}j=(a[65476]&1)==0;if(j){m=+h[8169];o=c[1080]|0;n=c[7940]|0;g=c[7941]|0;p=+h[8170];V=m+ +((1e3-o|0)-n|0)/+(g-n|0)*(p-m);W=o;X=n;Y=g;Z=m;_=p}else{p=+h[8169];g=c[1080]|0;n=c[7940]|0;o=c[7941]|0;m=+h[8170];V=+R(+(+h[8185]),+(p+ +((1e3-g|0)-n|0)/+(o-n|0)*(m-p)));W=g;X=n;Y=o;Z=p;_=m}o=(a[68916]&1)==0;if(o){m=+h[8599];p=+h[8600];$=m+ +(P-Q|0)/+(S-Q|0)*(p-m);aa=m;ab=p}else{p=+h[8599];m=+h[8600];$=+R(+(+h[8615]),+(p+ +(P-Q|0)/+(S-Q|0)*(m-p)));aa=p;ab=m}n=(a[68228]&1)==0;if(n){m=+h[8513];p=+h[8514];ac=m+ +((1e3-W|0)-X|0)/+(Y-X|0)*(p-m);ad=m;ae=p}else{p=+h[8513];m=+h[8514];ac=+R(+(+h[8529]),+(p+ +((1e3-W|0)-X|0)/+(Y-X|0)*(m-p)));ad=p;ae=m}if(k<<24>>24==0){af=T+ +((b-Q|0)>>>0>>>0)/+(S-Q|0)*(U-T)}else{af=+R(+(+h[8271]),+(T+ +((b-Q|0)>>>0>>>0)/+(S-Q|0)*(U-T)))}if(j){ag=Z+ +((d-X|0)>>>0>>>0)/+(Y-X|0)*(_-Z)}else{ag=+R(+(+h[8185]),+(Z+ +((d-X|0)>>>0>>>0)/+(Y-X|0)*(_-Z)))}if(o){ah=aa+ +((b-Q|0)>>>0>>>0)/+(S-Q|0)*(ab-aa)}else{ah=+R(+(+h[8615]),+(aa+ +((b-Q|0)>>>0>>>0)/+(S-Q|0)*(ab-aa)))}if(n){ai=ad+ +((d-X|0)>>>0>>>0)/+(Y-X|0)*(ae-ad)}else{ai=+R(+(+h[8529]),+(ad+ +((d-X|0)>>>0>>>0)/+(Y-X|0)*(ae-ad)))}cf(l|0,88424,(v=i,i=i+64|0,h[v>>3]=O,h[v+8>>3]=V,h[v+16>>3]=$,h[v+24>>3]=ac,h[v+32>>3]=af,h[v+40>>3]=ag,h[v+48>>3]=ah,h[v+56>>3]=ai,v)|0);l=c[10030]|0;if((a[66164]&1)==0){aI(88384,3,1,l|0)}else{m=+h[8255];n=c[7938]|0;p=+R(+(+h[8271]),+(m+(+(((c[1082]|0)+b|0)>>>0>>>0)*.5- +(n|0))/+((c[7939]|0)-n|0)*(+h[8256]-m)));cf(l|0,88536,(v=i,i=i+8|0,h[v>>3]=p,v)|0)}l=c[10030]|0;if((a[65476]&1)==0){aI(88384,3,1,l|0)}else{p=+h[8169];n=c[7940]|0;m=+R(+(+h[8185]),+(p+(1.0e3- +(((c[1080]|0)+f|0)>>>0>>>0)*.5- +(n|0))/+((c[7941]|0)-n|0)*(+h[8170]-p)));cf(l|0,88536,(v=i,i=i+8|0,h[v>>3]=m,v)|0)}l=c[10030]|0;if((a[68916]&1)==0){aI(88384,3,1,l|0)}else{m=+h[8599];n=c[7938]|0;p=+R(+(+h[8615]),+(m+(+(((c[1082]|0)+b|0)>>>0>>>0)*.5- +(n|0))/+((c[7939]|0)-n|0)*(+h[8600]-m)));cf(l|0,88536,(v=i,i=i+8|0,h[v>>3]=p,v)|0)}l=c[10030]|0;if((a[68228]&1)==0){aI(88384,3,1,l|0)}else{p=+h[8513];n=c[7940]|0;m=+R(+(+h[8529]),+(p+(1.0e3- +(((c[1080]|0)+f|0)>>>0>>>0)*.5- +(n|0))/+((c[7941]|0)-n|0)*(+h[8514]-p)));cf(l|0,88536,(v=i,i=i+8|0,h[v>>3]=m,v)|0)}l=c[10030]|0;aI(88360,2,1,l|0);c[1082]=b;c[1080]=f;i=e;return}}}while(0);if(!(a[4304]|0)){c[1082]=b;c[1080]=f;i=e;return}aI(125976,2,1,c[10030]|0);c[1082]=b;c[1080]=f;i=e;return}function qX(a){a=+a;h[539]=a;return}function qY(){c[58300]=0;c[58302]=0;a[233344]=0;return}function qZ(c){c=c|0;var d=0;if((c|0)==1){a[4384]=a[89896]|0;a[4385|0]=a[89897|0]|0;a[4386|0]=a[89898|0]|0;a[4387|0]=a[89899|0]|0;a[4388|0]=a[89900|0]|0;a[4389|0]=a[89901|0]|0;a[4390|0]=a[89902|0]|0;d=1;return d|0}else if((c|0)==0){b[2192]=119;d=1;return d|0}else if((c|0)==2){b[2192]=101;d=1;return d|0}else{b[2192]=119;d=0;return d|0}return 0}function q_(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;i=i+8|0;e=d|0;do{if((b|0)!=0){if((a[b]|0)==0){break}c[e>>2]=0;f=bk(b|0,148464)|0;g=f+1|0;h=ut(g)|0;if((h|0)==0){j=0;i=d;return j|0}uF(h|0,b|0,f|0);a[h+f|0]=0;if(f>>>0<(uA(b|0)|0)>>>0){f=b+g|0;ca(f|0,21e4,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}f=c[10030]|0;if(a[4304]|0){cf(f|0,90232,(v=i,i=i+8|0,c[v>>2]=h,v)|0);g=c[e>>2]|0;if((g|0)!=0){k=c[10030]|0;cf(k|0,90144,(v=i,i=i+8|0,c[v>>2]=g,v)|0)}g=c[10030]|0;aI(90072,5,1,g|0)}else{cf(f|0,90024,(v=i,i=i+8|0,c[v>>2]=h,v)|0);f=c[e>>2]|0;if((f|0)!=0){g=c[10030]|0;cf(g|0,90008,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}f=c[10030]|0;aI(89968,2,1,f|0)}uu(h);j=1;i=d;return j|0}}while(0);e=c[10030]|0;if(a[4304]|0){aI(90408,13,1,e|0);j=1;i=d;return j|0}else{aI(90384,20,1,e|0);j=1;i=d;return j|0}return 0}function q$(){var b=0;b=i;a[45024]=0;c[58300]=0;c[58302]=0;cf(c[10030]|0,90952,(v=i,i=i+8|0,h[v>>3]=.24089999999999998,v)|0);q9(-1);g[58284]=0.0;i=b;return}function q0(){var b=0,d=0,e=0,f=0;b=i;s7(2,0.0,0.0,0.0,0.0);if((a[233336]&1)!=0){d=c[10030]|0;e=c[58302]|0;f=c[58300]|0;cf(d|0,99e3,(v=i,i=i+24|0,c[v>>2]=e,c[v+8>>2]=f,c[v+16>>2]=100896,v)|0)}a[233336]=0;aI(99040,14,1,c[10030]|0);c[58300]=0;c[58302]=0;a[233344]=0;i=b;return}function q1(){var b=0,d=0,e=0;b=i;if(!(a[35312]|0)){c[(c[3524]|0)+8>>2]=1500;c[(c[3524]|0)+12>>2]=900}d=c[3524]|0;e=~~(+g[38]*+((c[d+12>>2]|0)>>>0>>>0));cf(c[10030]|0,91320,(v=i,i=i+16|0,c[v>>2]=~~(+g[178]*+((c[d+8>>2]|0)>>>0>>>0)),c[v+8>>2]=e,v)|0);if((aY(233408,91216)|0)==0){i=b;return}e=c[58350]|0;cf(c[10030]|0,91112,(v=i,i=i+16|0,c[v>>2]=233408,c[v+8>>2]=e,v)|0);i=b;return}function q2(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;if((a[233336]&1)!=0){f=c[10030]|0;g=c[58302]|0;h=c[58300]|0;cf(f|0,99e3,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=h,c[v+16>>2]=100896,v)|0)}a[233336]=0;c[58302]=b;c[58300]=d;a[233344]=0;i=e;return}function q3(b,d){b=b|0;d=d|0;var e=0,f=0.0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0,E=0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0;e=i;f=+g[58366];if(f==0.0){j=c[58302]|0;k=c[58300]|0;if(!(a[45024]|0)){s6(j,b,k,d);c[58302]=b;c[58300]=d;i=e;return}if(!(a[233344]|0)){l=c[10030]|0;cf(l|0,100208,(v=i,i=i+16|0,c[v>>2]=j,c[v+8>>2]=k,v)|0)}if(!((j|0)==(b|0)&(k|0)==(d|0))){k=c[10030]|0;cf(k|0,100168,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0)}c[58302]=b;c[58300]=d;a[233344]=1;c[58302]=b;c[58300]=d;i=e;return}k=c[58302]|0;j=c[58300]|0;l=a[233344]|0;if(!l){g[58370]=1.0}m=(k|0)==(b|0);do{if(m&(j|0)==(d|0)){if(l){break}n=c[10030]|0;cf(n|0,100920,(v=i,i=i+24|0,c[v>>2]=b,c[v+8>>2]=d,c[v+16>>2]=100896,v)|0)}else{o=f/.24089999999999998;do{if(m){if((d-j|0)>0){p=o;q=0.0;break}p=-0.0-o;q=0.0}else{r=(+(d|0)- +(j|0))/(+(b|0)- +(k|0));s=+(b-k>>31|1|0)*(o/+Q(+(r*r+1.0)));p=r*s;q=s}}while(0);o=1.0- +g[58370];s=+(k|0)+q*o;r=+(d|0);t=+(b|0);do{if(q*(t-s)<0.0){u=-1.0;w=-1.0}else{x=+(j|0)+p*o;y=s;z=-1.0;A=-1.0;n=0;while(1){if(p*(r-x)<0.0){B=z;C=A;D=n;break}E=n+1|0;F=q+y;if(q*(t-F)<0.0){B=y;C=x;D=E;break}else{A=x;x=p+x;z=y;y=F;n=E}}if((D|0)==1){n=c[10030]|0;y=B;z=C;cf(n|0,91472,(v=i,i=i+24|0,h[v>>3]=y,h[v+8>>3]=z,c[v+16>>2]=100896,v)|0);u=C;w=B;break}if((D|0)<=0){u=C;w=B;break}cf(c[10030]|0,91432,(v=i,i=i+48|0,c[v>>2]=k,c[v+8>>2]=j,h[v+16>>3]=q,h[v+24>>3]=p,c[v+32>>2]=D,c[v+40>>2]=100896,v)|0);u=C;w=B}}while(0);if(q!=0.0){if(w<0.0){n=b-k|0;if(q<0.0){G=-0.0-q}else{G=q}g[58370]=+(((n|0)>-1?n:-n|0)|0)/G+ +g[58370];break}s=t-w;if(s<0.0){H=-0.0-s}else{H=s}if(q<0.0){I=-0.0-q}else{I=q}g[58370]=H/I;break}else{if(u<0.0){n=d-j|0;if(p<0.0){J=-0.0-p}else{J=p}g[58370]=+(((n|0)>-1?n:-n|0)|0)/J+ +g[58370];break}s=r-u;if(s<0.0){K=-0.0-s}else{K=s}if(p<0.0){L=-0.0-p}else{L=p}g[58370]=K/L;break}}}while(0);a[233336]=+g[58370]>0.0&1;a[233344]=1;c[58302]=b;c[58300]=d;i=e;return}function q4(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;f=i;g=1e3-d|0;if(a[4304]|0){d=c[10030]|0;h=c[4344+(c[1094]<<2)>>2]|0;cf(d|0,89728,(v=i,i=i+40|0,c[v>>2]=b,c[v+8>>2]=g,c[v+16>>2]=e,c[v+24>>2]=h,c[v+32>>2]=4384,v)|0);i=f;return}else{j=0;k=0}while(1){h=a[e+k|0]|0;if((h<<24>>24|0)==0){break}else if((h<<24>>24|0)==91|(h<<24>>24|0)==93){l=j+1|0}else{l=j}j=l+1|0;k=k+1|0}k=j+1|0;j=ut(k)|0;do{if((j|0)==0){gk();l=ut(k)|0;if((l|0)!=0){m=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=89696,v)|0)}else{m=j}}while(0);j=0;k=0;while(1){l=e+k|0;h=a[l]|0;if((h<<24>>24|0)==0){break}else if((h<<24>>24|0)==91|(h<<24>>24|0)==93){a[m+j|0]=92;n=j+1|0;o=a[l]|0}else{n=j;o=h}a[m+n|0]=o;j=n+1|0;k=k+1|0}a[m+j|0]=0;j=c[4344+(c[1094]<<2)>>2]|0;cf(c[10030]|0,89472,(v=i,i=i+40|0,c[v>>2]=b,c[v+8>>2]=g,c[v+16>>2]=m,c[v+24>>2]=j,c[v+32>>2]=4384,v)|0);uu(m);i=f;return}function q5(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0.0,J=0.0,K=0.0;b=i;i=i+40|0;d=b|0;e=b+24|0;f=b+32|0;a[35312]=0;j=c[13898]|0;L24745:do{if((j|0)<(c[8272]|0)){k=d|0;l=d+8|0;m=j;while(1){n=c[1054]|0;o=(a[n+(m*40&-1)|0]&1)==0;L24749:do{if(!o){p=c[n+(m*40&-1)+36>>2]|0;q=n+(m*40&-1)+32|0;r=c[10036]|0;s=0;while(1){if((s|0)>=(p|0)){break}if((a[r+((c[q>>2]|0)+s|0)|0]|0)==(a[s+103664|0]|0)){s=s+1|0}else{break L24749}}if((s|0)==1){break L24745}}}while(0);q=c[58320]|0;L24756:do{if((q|0)==0){t=233280}else{r=c[n+(m*40&-1)+36>>2]|0;p=c[n+(m*40&-1)+32>>2]|0;u=(r|0)>0;x=c[10036]|0;y=233280;z=q;while(1){L24760:do{if(!o){if(u){A=0;B=0;C=p;while(1){D=a[z+A|0]|0;if(D<<24>>24==(a[x+(A+C|0)|0]|0)){E=C;F=B}else{if(D<<24>>24!=36){break L24760}E=C-1|0;F=1}G=A+1|0;if((G|0)<(F+r|0)){A=G;B=F;C=E}else{break}}if((F|0)==0){H=G}else{t=y;break L24756}}else{H=0}C=a[z+H|0]|0;if((C<<24>>24|0)==36|(C<<24>>24|0)==0){t=y;break L24756}}}while(0);C=y+8|0;B=c[C>>2]|0;if((B|0)==0){t=C;break}else{y=C;z=B}}}}while(0);o=c[t+4>>2]|0;if((o|0)==5){a[35288]=0;c[13898]=m+1}else if((o|0)==2){c[58352]=6516580;c[13898]=m+1}else if((o|0)==4){a[35288]=1;c[13898]=m+1}else if((o|0)==1){c[58352]=7499107;c[13898]=m+1}else if((o|0)==0){a[233408]=a[90944]|0;a[233409|0]=a[90945|0]|0;a[233410|0]=a[90946|0]|0;a[233411|0]=a[90947|0]|0;a[233412|0]=a[90948|0]|0;c[13898]=m+1}else if((o|0)==3){g[e>>2]=5.0;g[f>>2]=3.0;c[13898]=m+1;a[35312]=1;c[8826]=s4(e,f,1)|0;c[(c[3524]|0)+8>>2]=~~(+g[e>>2]*300.0/72.0);c[(c[3524]|0)+12>>2]=~~(+g[f>>2]*300.0/72.0)}else{o=is(d)|0;q=c[o>>2]|0;if((q|0)==3){I=+uz(c[o+8>>2]|0,0)}else if((q|0)==1){I=+(c[o+8>>2]|0)}else if((q|0)==2){I=+h[o+8>>3]}else{break}if((c[k>>2]|0)==3){uu(c[l>>2]|0);c[k>>2]=1}c[58350]=~~I}m=c[13898]|0;if((m|0)>=(c[8272]|0)){break L24745}}uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);c[(c[3524]|0)+16>>2]=((c[58350]|0)*300&-1|0)/72&-1;c[(c[3524]|0)+20>>2]=((c[58350]|0)*300&-1|0)/144&-1;if((aY(233408,91216)|0)==0){uF(13048,90848,1024)}else{d=(a[233410]|0)==116?90800:90792;f=c[58350]|0;be(13048,90816,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=f,v)|0)}do{if(a[35312]|0){f=(c[8826]|0)==2;d=13048+(uA(13048)|0)|0;e=c[3524]|0;I=+((c[e+8>>2]|0)>>>0>>>0);if(f){J=I*2.54/300.0;K=+((c[e+12>>2]|0)>>>0>>>0)*2.54/300.0;be(d|0,90672,(v=i,i=i+16|0,h[v>>3]=J,h[v+8>>3]=K,v)|0);break}else{K=I/300.0;I=+((c[e+12>>2]|0)>>>0>>>0)/300.0;be(d|0,90632,(v=i,i=i+16|0,h[v>>3]=K,h[v+8>>3]=I,v)|0);break}}}while(0);d=a[35288]|0;e=13048+(uA(13048)|0)|0;if(d){d=e;f=d|0;w=1953460768;a[f]=w&255;w=w>>8;a[f+1|0]=w&255;w=w>>8;a[f+2|0]=w&255;w=w>>8;a[f+3|0]=w&255;f=d+4|0;w=6648929;a[f]=w&255;w=w>>8;a[f+1|0]=w&255;w=w>>8;a[f+2|0]=w&255;w=w>>8;a[f+3|0]=w&255;i=b;return}else{uD(e|0,90568,10);i=b;return}}function q6(a){a=a|0;c[8830]=a;return 1}function q7(a){a=a|0;c[8824]=a;return 1}function q8(){a[45024]=0;c[58300]=0;c[58302]=0;return}function q9(b){b=b|0;var d=0,e=0,f=0,j=0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0;d=i;if((b|0)>5){e=(b|0)%6&-1}else{e=b}if(!(a[45024]|0)){s7(2,0.0,0.0,0.0,0.0)}if((a[233336]&1)!=0){b=c[10030]|0;f=c[58302]|0;j=c[58300]|0;cf(b|0,99e3,(v=i,i=i+24|0,c[v>>2]=f,c[v+8>>2]=j,c[v+16>>2]=100896,v)|0)}a[233336]=0;j=(e|0)>-1;if(j){k=+g[233352+(e<<3)>>2]}else{k=.4000000059604645}do{if(k!=+g[58284]){f=c[10030]|0;l=k*-.5;m=k;cf(f|0,91544,(v=i,i=i+24|0,h[v>>3]=l,h[v+8>>3]=m,h[v+16>>3]=m,v)|0);if(!(a[45024]|0)){n=m;break}f=c[10030]|0;cf(f|0,91496,(v=i,i=i+8|0,h[v>>3]=m,v)|0);n=m}else{n=k}}while(0);g[58284]=k;g[58368]=n/.24089999999999998;if(!j){o=0.0;g[58366]=o;a[233344]=0;i=d;return}o=+g[233356+(e<<3)>>2];g[58366]=o;a[233344]=0;i=d;return}function ra(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0;f=i;g=a[e]|0;if(g<<24>>24==0){i=f;return}if(a[35288]|0){h=0}else{j=0;k=g;while(1){g=j+1|0;if(k<<24>>24==92){l=j+2|0;m=(a[e+g|0]|0)==92}else{l=g;m=0}g=a[e+l|0]|0;if(g<<24>>24==0|m){h=m;break}else{j=l;k=g}}}cf(c[10030]|0,99752,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);d=c[8830]|0;if(a[35288]&(d|0)!=0){b=c[10030]|0;cf(b|0,91600,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}d=a[e]|0;do{if((d<<24>>24|0)==123|(d<<24>>24|0)==91){b=c[10030]|0;cf(b|0,99720,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}else{b=c[10030]|0;k=c[233184+(c[8824]<<2)>>2]|0;if(h){cf(b|0,99536,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=e,v)|0);break}else{cf(b|0,99696,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=e,v)|0);break}}}while(0);if(!(a[35288]&(c[8830]|0)!=0)){i=f;return}aF(125,c[10030]|0);i=f;return}function rb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0;f=i;if((a[233336]&1)!=0){g=c[10030]|0;h=c[58302]|0;j=c[58300]|0;cf(g|0,99e3,(v=i,i=i+24|0,c[v>>2]=h,c[v+8>>2]=j,c[v+16>>2]=100896,v)|0)}a[233336]=0;c[58302]=b;c[58300]=d;a[233344]=0;if((e|0)<0){k=92632}else{k=c[233216+(((e|0)%15&-1)<<2)>>2]|0}cf(c[10030]|0,99e3,(v=i,i=i+24|0,c[v>>2]=b,c[v+8>>2]=d,c[v+16>>2]=k,v)|0);i=f;return}function rc(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;s5(a,b,d,e,1,f);c[58302]=d;c[58300]=e;return}function rd(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0;g=i;j=a&15;do{if((j|0)==1){if((a>>4|0)>=50){break}i=g;return}else if((j|0)==0){i=g;return}else if((j|0)==2){if((a&16|0)!=0){break}i=g;return}}while(0);cf(c[10030]|0,92704,(v=i,i=i+32|0,c[v>>2]=b,c[v+8>>2]=d,h[v+16>>3]=+(e>>>0>>>0)*.24089999999999998,h[v+24>>3]=+(f>>>0>>>0)*.24089999999999998,v)|0);i=g;return}function re(){var b=0;b=i;a[45024]=1;c[58300]=0;c[58302]=0;cf(c[10030]|0,92776,(v=i,i=i+8|0,h[v>>3]=.24089999999999998,v)|0);q9(-1);i=b;return}function rf(){aI(99040,14,1,c[10030]|0);return}function rg(){var b=0,d=0,e=0;b=i;d=c[c[7118]>>2]|0;do{if((d|0)==1){e=c[10030]|0;aI(93200,35,1,e|0)}else if((d|0)==0){aI(93096,39,1,c[10030]|0)}else if((d|0)==2){pD();e=c[10030]|0;if((e|0)==0){break}cf(e|0,93272,(v=i,i=i+8|0,c[v>>2]=c[7112],v)|0);if((a[(c[7118]|0)+113|0]&1)==0){break}aI(93240,15,1,c[10030]|0)}}while(0);d=c[7112]|0;if((d|0)!=0){uu(d);c[7112]=0}d=c[10028]|0;if((d|0)==0|(d|0)==(c[10030]|0)){i=b;return}az(d|0);c[10028]=0;i=b;return}function rh(b){b=b|0;var d=0,e=0,f=0,g=0,j=0;d=i;i=i+16|0;e=d|0;f=e;c[f>>2]=c[59458];c[f+4>>2]=c[59459];c[f+8>>2]=c[59460];c[f+12>>2]=c[59461];c[e+4>>2]=b;f=c[7118]|0;do{if((c[f>>2]|0)==2){if((a[f+112|0]&1)==0){g=18637;break}j=(b|0)%4&-1}else{g=18637}}while(0);if((g|0)==18637){j=(b|0)%9&-1}b=j+3|0;j=(b|0)<0?0:b;if((c[57432]|0)==(j|0)){rj(e);i=d;return}a[228944]=0;if((c[7116]|0)!=0){b=c[10028]|0;aI(215952,7,1,b|0);c[7116]=0;a[228944]=0}c[57432]=j;h[28714]=+h[28715];cf(c[10028]|0,202624,(v=i,i=i+8|0,c[v>>2]=a[j+93672|0]|0,v)|0);c[7116]=0;rj(e);i=d;return}function ri(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=i;g=c[10030]|0;if((g|0)==0){i=f;return}if(a[5688]|0){h=g}else{aK(5624,g|0);a[5688]=1;h=c[10030]|0}cf(h|0,93440,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);d=c[7230]|0;if((d|0)!=0){b=c[10030]|0;cf(b|0,93424,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}d=a[e]|0;do{if((d<<24>>24|0)==123|(d<<24>>24|0)==91){b=c[10030]|0;cf(b|0,94528,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}else{b=c[7198]|0;if((b|0)==2){h=c[10030]|0;cf(h|0,94296,(v=i,i=i+8|0,c[v>>2]=e,v)|0);break}else if((b|0)==0){cf(c[10030]|0,94496,(v=i,i=i+8|0,c[v>>2]=e,v)|0);break}else if((b|0)==1){cf(c[10030]|0,94392,(v=i,i=i+8|0,c[v>>2]=e,v)|0);break}else{break}}}while(0);if((c[7230]|0)!=0){e=c[10030]|0;aF(125,e|0)}aI(94160,3,1,c[10030]|0);i=f;return}function rj(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0,r=0,s=0,t=0,u=0.0;d=i;i=i+24|0;e=d|0;f=b;if((uG(5608,f|0,16)|0)==0){i=d;return}c[1402]=c[f>>2];c[1403]=c[f+4>>2];c[1404]=c[f+8>>2];c[1405]=c[f+12>>2];f=b|0;do{if((c[f>>2]|0)==1){g=c[7118]|0;if((a[g+17|0]&1)!=0){j=18673;break}k=c[b+4>>2]|0;do{if((c[g>>2]|0)==2){if((a[g+112|0]&1)==0){j=18668;break}l=(k|0)%4&-1}else{j=18668}}while(0);if((j|0)==18668){l=(k|0)%9&-1}g=l+3|0;m=(g|0)<0?0:g;if((c[57432]|0)==(m|0)){break}a[228944]=0;if((c[7116]|0)!=0){g=c[10028]|0;aI(215952,7,1,g|0);c[7116]=0;a[228944]=0}c[57432]=m;h[28714]=+h[28715];g=c[10028]|0;n=a[m+93672|0]|0;cf(g|0,202624,(v=i,i=i+8|0,c[v>>2]=n,v)|0);c[7116]=0}else{j=18673}}while(0);if((j|0)==18673){pS(b)}a[5688]=0;j=c[f>>2]|0;if((j|0)==3){l=c[b+4>>2]|0;o=+(l>>>16&255|0)/255.0;p=+(l>>>8&255|0)/255.0;q=+(l&255|0)/255.0;be(5624,93720,(v=i,i=i+24|0,h[v>>3]=o,h[v+8>>3]=p,h[v+16>>3]=q,v)|0);r=c[f>>2]|0}else{r=j}if((r|0)==1){j=c[b+4>>2]|0;if((a[(c[7118]|0)+112|0]&1)==0){s=(j|0)%9&-1}else{s=(j|0)%4&-1}j=a[93672+(s+3|0)|0]|0;be(5624,93688,(v=i,i=i+8|0,c[v>>2]=j,v)|0);t=c[f>>2]|0}else{t=r}if((t|0)!=5){i=d;return}q=+h[b+8>>3];if((a[(c[7118]|0)+18|0]&1)==0){fq(q,e);p=+h[e>>3];o=+h[e+8>>3];u=+h[e+16>>3];be(5624,93720,(v=i,i=i+24|0,h[v>>3]=p,h[v+8>>3]=o,h[v+16>>3]=u,v)|0);i=d;return}if(q<=0.0){be(5624,93592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=d;return}if(q<1.0){u=+(~~(q*1.0e4+.5)|0)*1.0e-4;be(25336,141840,(v=i,i=i+8|0,h[v>>3]=u,v)|0);e=(a[25336]|0)==48&(a[25337]|0)==46?25337:25336;be(5624,93464,(v=i,i=i+8|0,c[v>>2]=e,v)|0);i=d;return}else{be(5624,93528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);i=d;return}}function rk(b){b=b|0;var d=0,e=0;d=i;do{if((b|0)==0){c[10882]=0}else if((b|0)==2){e=c[10882]|0;if((e|0)==2){break}else if((e|0)==1){aI(94096,7,1,c[10030]|0)}c[10882]=2;aI(93984,35,1,c[10030]|0);a[5688]=0}else if((b|0)==12){e=c[10028]|0;if((e|0)==0|(e|0)==(c[10030]|0)){break}cf(e|0,93800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((b|0)==1){e=c[10882]|0;if((e|0)==1){break}else if((e|0)==2){aI(94096,7,1,c[10030]|0)}c[10882]=1;aI(94024,34,1,c[10030]|0);a[5688]=0}else if((b|0)==5){if(((c[10882]|0)-1|0)>>>0<2){e=c[10030]|0;aI(94096,7,1,e|0)}c[10882]=0}else if((b|0)==13){e=c[10028]|0;if((e|0)==0|(e|0)==(c[10030]|0)){break}cf(e|0,93784,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);i=d;return}function rl(){var b=0,d=0,e=0.0,f=0,j=0.0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;b=i;c[7116]=0;aI(199208,29,1,c[10028]|0);d=c[10028]|0;if((d|0)==(c[10030]|0)){aI(94992,6,1,d|0)}d=c[7118]|0;e=+g[d+88>>2];do{if(e!=0.0){if((c[d>>2]|0)!=1){break}f=c[10030]|0;j=e;cf(f|0,94888,(v=i,i=i+8|0,h[v>>3]=j,v)|0)}}while(0);d=c[7036]|0;if((d|0)==0){i=b;return}else{k=d}do{d=c[k+4>>2]|0;cf(c[10030]|0,94840,(v=i,i=i+16|0,c[v>>2]=c[k>>2],c[v+8>>2]=d,v)|0);d=c[7118]|0;do{if((a[d+97|0]&1)==0){l=d}else{f=c[k+8>>2]|0;if((f|0)==0){l=d;break}m=c[10030]|0;n=360-f|0;cf(m|0,94696,(v=i,i=i+8|0,c[v>>2]=n,v)|0);l=c[7118]|0}}while(0);d=(c[l>>2]|0)==1;do{if(d){n=c[k+16>>2]|0;m=a[n]|0;if(!((m<<24>>24|0)==123|(m<<24>>24|0)==91)){o=18724;break}m=c[10030]|0;cf(m|0,94528,(v=i,i=i+8|0,c[v>>2]=n,v)|0)}else{o=18724}}while(0);do{if((o|0)==18724){o=0;n=c[k+12>>2]|0;if((n|0)==0){m=c[10030]|0;f=d?94496:94424;p=c[k+16>>2]|0;cf(m|0,f|0,(v=i,i=i+8|0,c[v>>2]=p,v)|0);break}else if((n|0)==2){cf(c[10030]|0,(d?94296:94272)|0,(v=i,i=i+8|0,c[v>>2]=c[k+16>>2],v)|0);break}else if((n|0)==1){cf(c[10030]|0,(d?94392:94328)|0,(v=i,i=i+8|0,c[v>>2]=c[k+16>>2],v)|0);break}else{break}}}while(0);do{if((a[(c[7118]|0)+97|0]&1)!=0){if((c[k+8>>2]|0)==0){break}d=c[10030]|0;aI(94208,50,1,d|0)}}while(0);aI(94160,3,1,c[10030]|0);k=c[k+20>>2]|0;}while((k|0)!=0);k=c[7036]|0;if((k|0)==0){i=b;return}else{q=k}while(1){k=c[q+20>>2]|0;uu(c[q+16>>2]|0);uu(c[7036]|0);c[7036]=k;if((k|0)==0){break}else{q=k}}i=b;return}function rm(){var b=0;b=i;c[59762]=0;c[59764]=0;c[59760]=0;c[59828]=0;a[239320]=0;c[11600]=0;cf(c[10030]|0,95608,(v=i,i=i+8|0,h[v>>3]=.12,v)|0);i=b;return}function rn(){var b=0;if(a[239320]|0){b=c[10030]|0;aF(10,b|0);a[239320]=0}c[59762]=0;c[59764]=0;return}function ro(){var b=0;if(a[239320]|0){b=c[10030]|0;aF(10,b|0);a[239320]=0}aI(99040,14,1,c[10030]|0);return}function rp(){var b=0,d=0,e=0.0,f=0.0,j=0,k=0.0;b=i;d=c[3524]|0;e=+((c[d+8>>2]|0)>>>0>>>0);f=+((c[d+12>>2]|0)>>>0>>>0);j=~~(f*+g[38]);k=+g[184];cf(c[10030]|0,95784,(v=i,i=i+32|0,c[v>>2]=~~(e*+g[178]),c[v+8>>2]=j,c[v+16>>2]=~~(e*k),c[v+24>>2]=~~(f*k),v)|0);if(a[46352]|0){k=+((c[d+16>>2]|0)>>>0>>>0)*.12+.5;d=c[10030]|0;j=~~k;f=k*1.2;cf(d|0,95712,(v=i,i=i+16|0,c[v>>2]=j,h[v+8>>3]=f,v)|0);i=b;return}else{j=c[10030]|0;aI(95688,14,1,j|0);i=b;return}}function rq(b,d){b=b|0;d=d|0;var e=0;if(a[239320]|0){e=c[10030]|0;aF(10,e|0);a[239320]=0}c[59764]=b;c[59762]=d;return}function rr(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;do{if(a[239320]|0){f=c[59828]|0;c[59828]=f+1;if((f|0)<=49){break}f=c[10030]|0;g=c[239216+(c[11584]<<5)+((c[59760]|0)+2<<2)>>2]|0;h=c[59764]|0;j=c[59762]|0;cf(f|0,96248,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=h,c[v+16>>2]=j,v)|0);c[59828]=1}else{a[239320]=1;j=c[10030]|0;h=c[239216+(c[11584]<<5)+((c[59760]|0)+2<<2)>>2]|0;g=c[59764]|0;f=c[59762]|0;cf(j|0,96272,(v=i,i=i+24|0,c[v>>2]=h,c[v+8>>2]=g,c[v+16>>2]=f,v)|0);c[59828]=1}}while(0);cf(c[10030]|0,96200,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);c[59764]=b;c[59762]=d;i=e;return}function rs(b){b=b|0;var d=0,e=0;if(a[239320]|0){d=c[10030]|0;aF(10,d|0);a[239320]=0}c[59760]=(b|0)%((c[46304+(c[11584]<<2)>>2]|0)-2|0)&-1;if(!(a[46392]|0)){return}c[11600]=b;if((b|0)<0){e=0}else{e=((b|0)%6&-1)+1|0}c[11600]=e;aK(c[46360+(e<<2)>>2]|0,c[10030]|0);return}function rt(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;if((a[e]|0)==0){i=f;return}g=c[c[3524]>>2]|0;h=ut(24)|0;do{if((h|0)==0){gk();j=ut(24)|0;if((j|0)!=0|(g|0)==0){k=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=g,v)|0)}else{k=h}}while(0);h=k;c[k>>2]=b;c[k+4>>2]=d;d=(uA(e|0)|0)+1|0;b=c[c[3524]>>2]|0;g=ut(d)|0;do{if((g|0)==0){gk();j=ut(d)|0;if((j|0)!=0|(b|0)==0){l=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=b,v)|0)}else{l=g}}while(0);c[k+16>>2]=l;uB(l|0,e|0);c[k+12>>2]=c[7198];c[k+8>>2]=c[7230];c[k+20>>2]=c[7036];c[7036]=h;i=f;return}function ru(){var b=0,e=0,f=0,j=0,k=0.0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0.0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0.0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0.0,U=0.0,V=0.0,W=0;b=i;i=i+24|0;e=b|0;a[46288]=0;a[46392]=0;c[11584]=0;c[11574]=0;f=c[13898]|0;j=c[8272]|0;L25079:do{if((f|0)<(j|0)){k=0.0;l=1;m=f;n=j;L25080:while(1){o=c[1054]|0;p=(a[o+(m*40&-1)|0]&1)==0;q=c[o+(m*40&-1)+36>>2]|0;r=o+(m*40&-1)+32|0;L25082:do{if(p){s=c[r>>2]|0;t=18835}else{o=c[10036]|0;u=0;while(1){if((u|0)>=(q|0)){t=18785;break}if((a[o+((c[r>>2]|0)+u|0)|0]|0)==(a[u+103664|0]|0)){u=u+1|0}else{break}}if((t|0)==18785){t=0;if((u|0)==1){w=k;x=l;break L25079}}o=c[r>>2]|0;if(!((q|0)>0&(p^1))){s=o;t=18835;break}y=c[10036]|0;z=0;A=0;B=o;while(1){if((a[z+184456|0]|0)==(a[y+(z+B|0)|0]|0)){C=B;D=A}else{if((z|0)!=3){break}C=B-1|0;D=1}E=z+1|0;if((E|0)<(D+q|0)){z=E;A=D;B=C}else{t=18792;break}}do{if((t|0)==18792){t=0;if((D|0)==0){if(!((z|0)==2|(z|0)==7)){break}}a[46352]=0;a[46392]=0;a[46288]=0;c[11584]=0;c[11574]=0;c[(c[3524]|0)+8>>2]=3e3;c[(c[3524]|0)+12>>2]=1800;a[239208]=0;c[13898]=(c[13898]|0)+1;F=1;G=k;break L25082}}while(0);if(p){s=o;t=18835;break}L25105:do{if((q|0)>0){z=c[10036]|0;B=0;A=0;y=o;while(1){if((a[B+115600|0]|0)==(a[z+(B+y|0)|0]|0)){H=y;I=A}else{if((B|0)!=1){t=18804;break L25105}H=y-1|0;I=1}u=B+1|0;if((u|0)<(I+q|0)){B=u;A=I;y=H}else{break}}if((I|0)!=0){break}if(!((B|0)==0|(B|0)==5)){t=18804}}else{t=18804}}while(0);do{if((t|0)==18804){t=0;if(!((q|0)>0&(p^1))){s=o;t=18835;break L25082}y=c[10036]|0;A=0;z=0;u=o;while(1){if((a[A+115536|0]|0)==(a[y+(A+u|0)|0]|0)){J=u;K=z}else{if((A|0)!=1){break}J=u-1|0;K=1}E=A+1|0;if((E|0)<(K+q|0)){A=E;z=K;u=J}else{t=18810;break}}if((t|0)==18810){t=0;if((K|0)!=0){break}if((A|0)==0|(A|0)==6){break}}if(p){s=o;t=18835;break L25082}L25129:do{if((q|0)>0){u=c[10036]|0;z=0;y=0;B=o;while(1){if((a[z+95544|0]|0)==(a[u+(z+B|0)|0]|0)){L=B;M=y}else{if((z|0)!=1){break L25129}L=B-1|0;M=1}E=z+1|0;if((E|0)<(M+q|0)){z=E;y=M;B=L}else{break}}if((M|0)==0){if(!((z|0)==0|(z|0)==6)){break}}a[46288]=1;c[13898]=m+1;F=l;G=k;break L25082}}while(0);if(!((q|0)>0&(p^1))){s=o;t=18835;break L25082}A=c[10036]|0;B=0;y=0;u=o;while(1){if((a[B+129104|0]|0)==(a[A+(B+u|0)|0]|0)){N=u;O=y}else{if((B|0)!=2){s=o;t=18835;break L25082}N=u-1|0;O=1}E=B+1|0;if((E|0)<(O+q|0)){B=E;y=O;u=N}else{break}}if((O|0)==0){if(!((B|0)==1|(B|0)==6)){s=o;t=18835;break L25082}}if(!(a[46392]|0)){c[11584]=1}c[13898]=m+1;F=l;G=k;break L25082}}while(0);a[46392]=1;c[11584]=2;c[13898]=m+1;F=l;G=k}}while(0);L25157:do{if((t|0)==18835){t=0;L25159:do{if((n|0)>(m|0)){if(p){break}r=c[10036]|0;o=0;while(1){if((o|0)>=(q|0)){break}if((a[r+(s+o|0)|0]|0)==(a[o+130496|0]|0)){o=o+1|0}else{t=18842;break L25159}}if((o|0)!=4){t=18842;break}c[13898]=m+1;r=s4(46280,46272,1)|0;c[(c[3524]|0)+8>>2]=~~(+g[11570]/.12);c[(c[3524]|0)+12>>2]=~~(+g[11568]/.12);a[239208]=1;F=r;G=k;break L25157}else{t=18842}}while(0);L25167:do{if((t|0)==18842){t=0;if(!((q|0)>0&(p^1))){break}r=c[10036]|0;B=0;u=0;y=s;while(1){if((a[B+95520|0]|0)==(a[r+(B+y|0)|0]|0)){P=y;Q=u}else{if((B|0)!=1){break}P=y-1|0;Q=1}A=B+1|0;if((A|0)<(Q+q|0)){B=A;u=Q;y=P}else{t=18848;break}}do{if((t|0)==18848){t=0;if((Q|0)==0){if(!((B|0)==0|(B|0)==5)){break}}c[11574]=1;c[13898]=m+1;F=l;G=k;break L25157}}while(0);if(!((q|0)>0&(p^1))){break}B=c[10036]|0;y=0;u=0;r=s;while(1){if((a[y+95512|0]|0)==(a[B+(y+r|0)|0]|0)){R=r;S=u}else{if((y|0)!=1){break L25167}R=r-1|0;S=1}o=y+1|0;if((o|0)<(S+q|0)){y=o;u=S;r=R}else{break}}if((S|0)==0){if(!((y|0)==0|(y|0)==4)){break}}c[11574]=2;c[13898]=m+1;F=l;G=k;break L25157}}while(0);if(((d[(c[10036]|0)+s|0]|0)-48|0)>>>0>=10){t=18869;break L25080}r=is(e)|0;u=c[r>>2]|0;if((u|0)==3){T=+uz(c[r+8>>2]|0,0)}else if((u|0)==1){T=+(c[r+8>>2]|0)}else if((u|0)==2){T=+h[r+8>>3]}else{t=18865;break L25080}U=T;if(U<1.0|U>100.0){t=18867;break L25080}a[46352]=1;V=U/.12;c[(c[3524]|0)+16>>2]=~~V;c[(c[3524]|0)+20>>2]=~~(V*.5);F=l;G=U}}while(0);q=c[13898]|0;p=c[8272]|0;if((q|0)<(p|0)){k=G;l=F;m=q;n=p}else{w=G;x=F;break L25079}}if((t|0)==18869){uf(m,95448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==18865){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((t|0)==18867){uf(c[13898]|0,95472,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}else{w=0.0;x=1}}while(0);t=(c[11584]|0)==1?95248:179864;F=a[46288]|0?95224:179864;e=c[11574]|0;if((e|0)==1){W=95216}else{W=(e|0)==2?95152:179864}be(13048,95392,(v=i,i=i+32|0,c[v>>2]=a[46392]|0?95304:179864,c[v+8>>2]=t,c[v+16>>2]=F,c[v+24>>2]=W,v)|0);if(a[46352]|0){W=13048+(uA(13048)|0)|0;F=~~w;be(W|0,95144,(v=i,i=i+8|0,c[v>>2]=F,v)|0)}if(!(a[239208]|0)){i=b;return}F=13048+(uA(13048)|0)|0;W=c[3524]|0;w=+((c[W+8>>2]|0)>>>0>>>0);if((x|0)==2){G=w*2.54/600.0;T=+((c[W+12>>2]|0)>>>0>>>0)*2.54/600.0;be(F|0,95024,(v=i,i=i+16|0,h[v>>3]=G,h[v+8>>3]=T,v)|0);i=b;return}else{T=w/600.0;w=+((c[W+12>>2]|0)>>>0>>>0)/600.0;be(F|0,95e3,(v=i,i=i+16|0,h[v>>3]=T,h[v+8>>3]=w,v)|0);i=b;return}}function rv(a){a=a|0;c[11586]=a;return 1}function rw(a){a=a|0;c[1044]=a;return 1}function rx(a){a=a|0;c[11602]=(a|0)!=0&1;return 1}function ry(a){a=a|0;c[1050]=(a|0)!=0&1;return 1}function rz(){var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;b=c[13898]|0;if((b|0)>=(c[8272]|0)){return}d=c[1054]|0;e=(a[d+(b*40&-1)|0]&1)==0;if(e){return}f=c[d+(b*40&-1)+36>>2]|0;g=d+(b*40&-1)+32|0;d=c[10036]|0;h=0;while(1){if((h|0)>=(f|0)){i=18891;break}if((a[d+((c[g>>2]|0)+h|0)|0]|0)==(a[h+103664|0]|0)){h=h+1|0}else{break}}do{if((i|0)==18891){if((h|0)!=1){break}return}}while(0);h=c[g>>2]|0;if(!((f|0)>0&(e^1))){return}g=c[10036]|0;d=0;j=0;k=h;while(1){if((a[d+101280|0]|0)==(a[g+(d+k|0)|0]|0)){l=k;m=j}else{if((d|0)!=2){break}l=k-1|0;m=1}n=d+1|0;if((n|0)<(m+f|0)){d=n;j=m;k=l}else{i=18898;break}}do{if((i|0)==18898){if((m|0)==0){if(!((d|0)==1|(d|0)==10)){break}}a[231056]=1;c[13898]=b+1;return}}while(0);if(!((f|0)>0&(e^1))){return}e=c[10036]|0;d=0;m=0;l=h;while(1){if((a[d+101232|0]|0)==(a[e+(d+l|0)|0]|0)){o=l;p=m}else{if((d|0)!=1){i=18918;break}o=l-1|0;p=1}h=d+1|0;if((h|0)<(p+f|0)){d=h;m=p;l=o}else{break}}if((i|0)==18918){return}do{if((p|0)==0){if((d|0)==0|(d|0)==4){break}return}}while(0);a[231048]=1;c[13898]=b+1;return}function rA(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0;f=i;if(a[239320]|0){g=c[10030]|0;aF(10,g|0);a[239320]=0}cf(c[10030]|0,99752,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);d=a[e]|0;if((d<<24>>24|0)==123|(d<<24>>24|0)==91){d=c[10030]|0;cf(d|0,99720,(v=i,i=i+8|0,c[v>>2]=e,v)|0);i=f;return}d=c[11602]|0;if((d|0)==0){b=c[11586]|0;if((b|0)==0){g=c[10030]|0;aI(97304,18,1,g|0)}else if((b|0)==2){aI(97152,18,1,c[10030]|0)}else if((b|0)==1){aI(97240,15,1,c[10030]|0)}b=c[10030]|0;cf(b|0,97128,(v=i,i=i+8|0,c[v>>2]=e,v)|0);i=f;return}else if((d|0)==1){d=c[11586]|0;if(a[46288]|0){if((d|0)==1){b=c[10030]|0;aI(97024,43,1,b|0)}else if((d|0)==0){aI(97080,44,1,c[10030]|0)}else if((d|0)==2){aI(96952,44,1,c[10030]|0)}b=c[10030]|0;cf(b|0,96920,(v=i,i=i+8|0,c[v>>2]=e,v)|0);i=f;return}if((d|0)==1){b=c[10030]|0;aI(96824,30,1,b|0)}else if((d|0)==0){aI(96856,31,1,c[10030]|0)}else if((d|0)==2){aI(96720,31,1,c[10030]|0)}d=(uA(e|0)|0)-1|0;if((d|0)>0){b=0;do{cf(c[10030]|0,96672,(v=i,i=i+8|0,c[v>>2]=a[e+b|0]|0,v)|0);b=b+1|0;}while((b|0)<(d|0))}aF(a[e+d|0]|0|0,c[10030]|0);aI(96616,4,1,c[10030]|0);i=f;return}else{i=f;return}}function rB(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=i;if(a[239320]|0){g=c[10030]|0;aF(10,g|0);a[239320]=0}c[59764]=b;c[59762]=d;if((e|0)<0){h=100128}else{g=c[11574]|0;h=c[239064+(g*48&-1)+(((e|0)%(c[46320+(g<<2)>>2]|0)&-1)<<2)>>2]|0}cf(c[10030]|0,99e3,(v=i,i=i+24|0,c[v>>2]=b,c[v+8>>2]=d,c[v+16>>2]=h,v)|0);i=f;return}function rC(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;s5(a,b,d,e,2,f);c[59764]=d;c[59762]=e;return}function rD(){var b=0,d=0,e=0,f=0,g=0.0;b=i;a[3280]=1;a[3648]=1;c[814]=0;c[816]=0;ts();c[916]=-1;d=a[3648]|0?39:-2;e=c[818]|0;f=c[914]|0;g=+h[523];cf(c[10030]|0,225816,(v=i,i=i+48|0,c[v>>2]=a[3280]|0?30:0,c[v+8>>2]=d,c[v+16>>2]=e,c[v+24>>2]=f,h[v+32>>3]=g,h[v+40>>3]=.001,v)|0);i=b;return}function rE(){ts();c[814]=0;c[816]=0;return}function rF(){ts();aI(99040,14,1,c[10030]|0);return}function rG(){var a=0,b=0,d=0,e=0,f=0;a=i;b=c[3524]|0;d=b+8|0;e=c[d>>2]|0;f=b+12|0;b=c[f>>2]|0;cf(c[10030]|0,226256,(v=i,i=i+48|0,c[v>>2]=179864,c[v+8>>2]=e,c[v+16>>2]=b,c[v+24>>2]=0,c[v+32>>2]=0,c[v+40>>2]=179864,v)|0);b=(c[d>>2]|0)-876|0;e=c[f>>2]|0;cf(c[10030]|0,226256,(v=i,i=i+48|0,c[v>>2]=99408,c[v+8>>2]=b,c[v+16>>2]=e,c[v+24>>2]=876,c[v+32>>2]=0,c[v+40>>2]=99360,v)|0);e=(c[d>>2]|0)-215|0;b=c[f>>2]|0;cf(c[10030]|0,226256,(v=i,i=i+48|0,c[v>>2]=99408,c[v+8>>2]=e,c[v+16>>2]=b,c[v+24>>2]=0,c[v+32>>2]=0,c[v+40>>2]=99336,v)|0);b=(c[d>>2]|0)-1091|0;e=c[f>>2]|0;cf(c[10030]|0,226256,(v=i,i=i+48|0,c[v>>2]=99408,c[v+8>>2]=b,c[v+16>>2]=e,c[v+24>>2]=876,c[v+32>>2]=0,c[v+40>>2]=99288,v)|0);e=c[d>>2]|0;b=(c[f>>2]|0)-379|0;cf(c[10030]|0,226256,(v=i,i=i+48|0,c[v>>2]=99408,c[v+8>>2]=e,c[v+16>>2]=b,c[v+24>>2]=0,c[v+32>>2]=0,c[v+40>>2]=99272,v)|0);b=c[d>>2]|0;e=(c[f>>2]|0)-533|0;cf(c[10030]|0,226256,(v=i,i=i+48|0,c[v>>2]=99408,c[v+8>>2]=b,c[v+16>>2]=e,c[v+24>>2]=0,c[v+32>>2]=533,c[v+40>>2]=99176,v)|0);e=c[d>>2]|0;d=(c[f>>2]|0)-912|0;cf(c[10030]|0,226256,(v=i,i=i+48|0,c[v>>2]=99408,c[v+8>>2]=e,c[v+16>>2]=d,c[v+24>>2]=0,c[v+32>>2]=533,c[v+40>>2]=99120,v)|0);aI(99064,15,1,c[10030]|0);i=a;return}function rH(a,b){a=a|0;b=b|0;ts();c[816]=a;c[814]=b;return}function rI(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;do{if(a[4192]|0){f=c[918]|0;if((f|0)<=99){g=f;break}ts();a[4192]=1;f=c[3680+(((c[916]|0)+2|0)*12&-1)>>2]|0;if((f|0)==2){h=(c[914]|0)*3&-1}else if((f|0)==3){h=(c[914]|0)*5&-1}else if((f|0)==1){h=c[914]|0}else{h=1}f=c[10030]|0;cf(f|0,99928,(v=i,i=i+8|0,c[v>>2]=h,v)|0);f=c[814]|0;c[8030]=c[816];c[8031]=f;c[918]=1;g=1}else{a[4192]=1;f=c[3680+(((c[916]|0)+2|0)*12&-1)>>2]|0;if((f|0)==1){j=c[914]|0}else if((f|0)==3){j=(c[914]|0)*5&-1}else if((f|0)==2){j=(c[914]|0)*3&-1}else{j=1}f=c[10030]|0;cf(f|0,99928,(v=i,i=i+8|0,c[v>>2]=j,v)|0);f=c[814]|0;c[8030]=c[816];c[8031]=f;c[918]=1;g=1}}while(0);c[32120+(g<<3)>>2]=b;c[32124+(g<<3)>>2]=d;c[918]=g+1;c[816]=b;c[814]=d;i=e;return}function rJ(b){b=b|0;var d=0,e=0;ts();d=(b|0)<-2?-2:b;b=a[3648]|0?39:-2;if((d|0)<(b|0)){e=d;c[916]=e;return}e=(d|0)%(b|0)&-1;c[916]=e;return}function rK(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=i;ts();cf(c[10030]|0,99752,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);d=a[e]|0;if((d<<24>>24|0)==123|(d<<24>>24|0)==91){d=c[10030]|0;cf(d|0,99720,(v=i,i=i+8|0,c[v>>2]=e,v)|0);i=f;return}d=c[1050]|0;if((d|0)==1){b=c[1044]|0;if((b|0)==0){g=99688}else if((b|0)==2){g=99616}else if((b|0)==1){g=104096}else{g=0}b=c[10030]|0;cf(b|0,99536,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=e,v)|0);i=f;return}else if((d|0)==0){d=c[1044]|0;if((d|0)==0){h=104096}else if((d|0)==1){h=179864}else if((d|0)==2){h=104088}else{h=0}cf(c[10030]|0,99696,(v=i,i=i+16|0,c[v>>2]=h,c[v+8>>2]=e,v)|0);i=f;return}else{i=f;return}}function rL(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0;e=i;ts();c[816]=a;c[814]=b;cf(c[10030]|0,100152,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);if((d|0)<0){b=c[10030]|0;aI(100128,17,1,b|0);f=c[10030]|0;g=aI(100056,4,1,f|0)|0;i=e;return}b=(d|0)%30&-1;d=c[3288+(b*12&-1)>>2]|0;if((d|0)==3){a=c[914]|0;j=c[10030]|0;cf(j|0,99928,(v=i,i=i+8|0,c[v>>2]=a,v)|0);a=c[10030]|0;cf(a|0,99984,(v=i,i=i+8|0,h[v>>3]=1.0,v)|0)}else if((d|0)==2){cf(c[10030]|0,99928,(v=i,i=i+8|0,c[v>>2]=c[914],v)|0);cf(c[10030]|0,99984,(v=i,i=i+8|0,h[v>>3]=.5,v)|0)}else if((d|0)==1){cf(c[10030]|0,99928,(v=i,i=i+8|0,c[v>>2]=c[914],v)|0);cf(c[10030]|0,99984,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0)}else if((d|0)==0){cf(c[10030]|0,99928,(v=i,i=i+8|0,c[v>>2]=(c[914]|0)*3&-1,v)|0)}d=c[3292+(b*12&-1)>>2]|0;if((d|0)==3){k=~~(+(c[818]|0)*2.0)}else if((d|0)==2){k=~~(+(c[818]|0)*1.4142)}else if((d|0)==1){k=c[818]|0}else{k=0}cM[c[3296+(b*12&-1)>>2]&511](k);f=c[10030]|0;g=aI(100056,4,1,f|0)|0;i=e;return}function rM(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;s5(a,b,d,e,1,f);c[816]=d;c[814]=e;return}function rN(){var b=0;g[57768]=0.0;g[57770]=0.0;if(a[231184]|0){b=c[10030]|0;aF(10,b|0);a[231184]=0}c[57766]=-1;cp(c[10030]|0,0,0);aI(101352,47,1,c[10030]|0);a[231136]=0;return}function rO(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0,o=0.0,p=0.0;b=i;i=i+24|0;d=b|0;e=c[13898]|0;L25387:do{if((e|0)<(c[8272]|0)){f=c[1054]|0;L25389:do{if((a[f+(e*40&-1)|0]&1)!=0){g=c[f+(e*40&-1)+36>>2]|0;j=f+(e*40&-1)+32|0;k=c[10036]|0;l=0;while(1){if((l|0)>=(g|0)){break}if((a[k+((c[j>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{break L25389}}if((l|0)==1){break L25387}}}while(0);f=is(d)|0;j=c[f>>2]|0;if((j|0)==1){m=+(c[f+8>>2]|0)}else if((j|0)==2){m=+h[f+8>>3]}else if((j|0)==3){m=+uz(c[f+8>>2]|0,0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=~~m;if((f|0)<=0){break}c[818]=f}}while(0);e=c[13898]|0;L25404:do{if((e|0)<(c[8272]|0)){f=c[1054]|0;L25406:do{if((a[f+(e*40&-1)|0]&1)!=0){j=c[f+(e*40&-1)+36>>2]|0;k=f+(e*40&-1)+32|0;g=c[10036]|0;n=0;while(1){if((n|0)>=(j|0)){break}if((a[g+((c[k>>2]|0)+n|0)|0]|0)==(a[n+103664|0]|0)){n=n+1|0}else{break L25406}}if((n|0)==1){break L25404}}}while(0);f=is(d)|0;k=c[f>>2]|0;if((k|0)==2){o=+h[f+8>>3]}else if((k|0)==3){o=+uz(c[f+8>>2]|0,0)}else if((k|0)==1){o=+(c[f+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}f=~~o;if((f|0)<=0){break}c[914]=f}}while(0);e=c[13898]|0;L25421:do{if((e|0)<(c[8272]|0)){f=c[1054]|0;L25423:do{if((a[f+(e*40&-1)|0]&1)!=0){k=c[f+(e*40&-1)+36>>2]|0;g=f+(e*40&-1)+32|0;j=c[10036]|0;l=0;while(1){if((l|0)>=(k|0)){break}if((a[j+((c[g>>2]|0)+l|0)|0]|0)==(a[l+103664|0]|0)){l=l+1|0}else{break L25423}}if((l|0)==1){break L25421}}}while(0);f=is(d)|0;g=c[f>>2]|0;if((g|0)==3){p=+uz(c[f+8>>2]|0,0)}else if((g|0)==1){p=+(c[f+8>>2]|0)}else if((g|0)==2){p=+h[f+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(p<=0.0){break}h[523]=p}}while(0);d=c[914]|0;p=+h[523];be(13048,99024,(v=i,i=i+24|0,c[v>>2]=c[818],c[v+8>>2]=d,h[v+16>>3]=p,v)|0);i=b;return}function rP(a){a=a|0;c[57794]=a;return 1}function rQ(a){a=a|0;c[56722]=a;return 1}function rR(a){a=a|0;c[56636]=a;return 1}function rS(a){a=a|0;c[58056]=(a|0)!=0&1;return 1}function rT(a,b){a=a|0;b=b|0;c[58246]=a;c[58244]=b;c[58254]=0;h[29126]=+(c[232736+((c[58242]|0)*24&-1)>>2]|0);return}function rU(){var b=0;if(a[231184]|0){b=c[10030]|0;aF(10,b|0);a[231184]=0}g[57768]=0.0;g[57770]=0.0;return}function rV(){var b=0;if(a[231184]|0){b=c[10030]|0;aF(10,b|0);a[231184]=0}aI(101400,32,1,c[10030]|0);return}function rW(){var b=0,d=0,e=0.0,f=0.0,j=0.0,k=0.0;b=i;aI(103264,160,1,c[10030]|0);aI(102784,449,1,c[10030]|0);aI(102488,280,1,c[10030]|0);aI(101736,747,1,c[10030]|0);aI(101616,83,1,c[10030]|0);if(!(a[231048]|0)){d=c[10030]|0;aI(101512,43,1,d|0)}e=+g[184];f=+g[44];j=e+ +g[178];k=f+ +g[38];cf(c[10030]|0,101448,(v=i,i=i+32|0,h[v>>3]=e,h[v+8>>3]=f,h[v+16>>3]=j,h[v+24>>3]=k,v)|0);i=b;return}function rX(b,d){b=b|0;d=d|0;var e=0;if(a[231184]|0){e=c[10030]|0;aF(10,e|0);a[231184]=0}g[57770]=+(b>>>0>>>0)/1.0e4;g[57768]=+(d>>>0>>>0)/1.0e4;return}function rY(b,d){b=b|0;d=d|0;var e=0,f=0,j=0,k=0.0,l=0.0;e=i;do{if(a[231184]|0){f=c[57792]|0;c[57792]=f+1;if((f|0)<=99){break}f=c[10030]|0;j=c[231144+((c[57766]|0)+2<<2)>>2]|0;k=+g[57770];l=+g[57768];cf(f|0,104992,(v=i,i=i+24|0,c[v>>2]=j,h[v+8>>3]=k,h[v+16>>3]=l,v)|0);c[57792]=1}else{a[231184]=1;j=c[10030]|0;f=c[231144+((c[57766]|0)+2<<2)>>2]|0;l=+g[57770];k=+g[57768];cf(j|0,104992,(v=i,i=i+24|0,c[v>>2]=f,h[v+8>>3]=l,h[v+16>>3]=k,v)|0);c[57792]=1}}while(0);k=+(b>>>0>>>0)/1.0e4;g[57770]=k;l=+(d>>>0>>>0)/1.0e4;g[57768]=l;cf(c[10030]|0,103688,(v=i,i=i+16|0,h[v>>3]=k,h[v+8>>3]=l,v)|0);i=e;return}function rZ(b){b=b|0;var d=0,e=0;if(a[231184]|0){d=c[10030]|0;aF(10,d|0);a[231184]=0}if((b|0)>3){e=(b|0)%4&-1}else{e=b}c[57766]=(e|0)<-2?-2:e;return}function r_(d,e,f){d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0.0;g=i;i=i+8|0;j=g|0;if(a[231184]|0){k=c[10030]|0;aF(10,k|0);a[231184]=0}if((a[f]|0)==0){i=g;return}aI(104112,5,1,c[10030]|0);k=c[57794]|0;if((k|0)==0){l=c[10030]|0;aI(104096,3,1,l|0)}else if((k|0)==2){aI(104088,3,1,c[10030]|0)}if((c[58056]|0)==1){k=c[10030]|0;aI(104040,3,1,k|0)}cf(c[10030]|0,104e3,(v=i,i=i+16|0,h[v>>3]=+(d>>>0>>>0)/1.0e4,h[v+8>>3]=+(e>>>0>>>0)/1.0e4,v)|0);if(a[231056]|0){e=c[10030]|0;cf(e|0,103976,(v=i,i=i+8|0,c[v>>2]=f,v)|0);i=g;return}uz(f,j);e=c[j>>2]|0;if((e|0)==(f|0)){uB(231192,f|0)}else{d=e-f|0;e=d+1|0;uF(231192,f|0,e|0);if((uA(f|0)|0)>>>0>=e>>>0){a[231192+((e|0)==0?0:d)|0]=0}d=a8(231192,101)|0;if((d|0)==0){e=a8(231192,69)|0;if((e|0)!=0){m=e;n=19097}}else{m=d;n=19097}do{if((n|0)==19097){a[m]=0;o=+uz(231192,0);d=bW(m+1|0)|0;if(o==0.0){b[115596]=48;break}if(o==1.0){be(231192,103944,(v=i,i=i+8|0,c[v>>2]=d,v)|0);break}e=~~o;f=(d|0)==1;if(o==+(e|0)){if(f){be(231192,103848,(v=i,i=i+8|0,c[v>>2]=e,v)|0);break}else{be(231192,103800,(v=i,i=i+16|0,c[v>>2]=e,c[v+8>>2]=d,v)|0);break}}else{if(f){be(231192,103760,(v=i,i=i+8|0,h[v>>3]=o,v)|0);break}else{be(231192,103728,(v=i,i=i+16|0,h[v>>3]=o,c[v+8>>2]=d,v)|0);break}}}}while(0);m=c[j>>2]|0;uC(231192,m|0)}cf(c[10030]|0,103976,(v=i,i=i+8|0,c[v>>2]=231192,v)|0);i=g;return}function r$(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0.0,l=0.0;f=i;if(a[231184]|0){j=c[10030]|0;aF(10,j|0);a[231184]=0}k=+(b>>>0>>>0)/1.0e4;g[57770]=k;l=+(d>>>0>>>0)/1.0e4;g[57768]=l;d=c[10030]|0;if((e|0)<0){cf(d|0,105032,(v=i,i=i+24|0,h[v>>3]=k,h[v+8>>3]=l,h[v+16>>3]=25.0e-5,v)|0);i=f;return}else{b=c[231088+(((e|0)%12&-1)<<2)>>2]|0;cf(d|0,104992,(v=i,i=i+24|0,c[v>>2]=b,h[v+8>>3]=k,h[v+16>>3]=l,v)|0);i=f;return}}function r0(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var j=0,k=0.0,l=0.0;j=i;k=+(d>>>0>>>0)/1.0e4;l=+(e>>>0>>>0)/1.0e4;cf(c[10030]|0,105176,(v=i,i=i+40|0,c[v>>2]=(f|0)!=0?105128:179864,h[v+8>>3]=+(a>>>0>>>0)/1.0e4,h[v+16>>3]=+(b>>>0>>>0)/1.0e4,h[v+24>>3]=k,h[v+32>>3]=l,v)|0);g[57770]=k;g[57768]=l;i=j;return}function r1(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0,o=0.0,p=0.0;d=i;if((b|0)==0){e=128;i=d;return e|0}L25532:do{if(!(a[231136]|0)){a[231136]=1;f=c[5163]|0;if((f|0)==103){g=c[5169]|0;if((g|0)>0){j=0;k=g}else{break}while(1){cf(c[10030]|0,105632,(v=i,i=i+16|0,c[v>>2]=j,h[v+8>>3]=+(~~(+(j|0)/+(k-1|0)*1.0e3)|0)*.001,v)|0);j=j+1|0;k=c[5169]|0;if((j|0)>=(k|0)){break L25532}}}else if((f|0)!=114){break}if((c[5169]|0)<=0){break}g=b+32|0;l=0;do{m=c[g>>2]|0;n=+(~~(+h[m+(l*24&-1)>>3]*1.0e3)|0)*.001;o=+(~~(+h[m+(l*24&-1)+8>>3]*1.0e3)|0)*.001;p=+(~~(+h[m+(l*24&-1)+16>>3]*1.0e3)|0)*.001;cf(c[10030]|0,105552,(v=i,i=i+32|0,c[v>>2]=l,h[v+8>>3]=n,h[v+16>>3]=o,h[v+24>>3]=p,v)|0);l=l+1|0;}while((l|0)<(c[5169]|0))}}while(0);aI(105272,85,1,c[10030]|0);e=0;i=d;return e|0}function r2(b){b=b|0;var d=0.0,e=0;d=+h[b+8>>3];if((c[b>>2]|0)!=5){return}if(d>0.0){e=~~(d*+(c[5169]|0))}else{e=0}if(!(a[231136]|0)){b=c[m>>2]|0;aI(105680,35,1,b|0)}c[58054]=(e|0)>127?127:e;return}function r3(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0;d=i;cf(c[10030]|0,105856,(v=i,i=i+8|0,c[v>>2]=c[58054],v)|0);if((a|0)>0){e=0}else{f=c[10030]|0;g=aF(10,f|0)|0;i=d;return}do{if(((e|0)%8&-1|0)==7){j=c[10030]|0;aF(10,j|0)}k=+(c[b+(e*12&-1)+4>>2]|0)/1.0e4;cf(c[10030]|0,105760,(v=i,i=i+16|0,h[v>>3]=+(c[b+(e*12&-1)>>2]|0)/1.0e4,h[v+8>>3]=k,v)|0);e=e+1|0;}while((e|0)<(a|0));f=c[10030]|0;g=aF(10,f|0)|0;i=d;return}function r4(){var b=0;c[56608]=0;c[56610]=0;if(a[226552]|0){b=c[10030]|0;aF(10,b|0);a[226552]=0}c[56606]=-1;aI(106128,39,1,c[10030]|0);return}function r5(){var b=0;if(a[226552]|0){b=c[10030]|0;aF(10,b|0);a[226552]=0}c[56608]=0;c[56610]=0;return}function r6(){var b=0;if(a[226552]|0){b=c[10030]|0;aF(10,b|0);a[226552]=0}aI(106248,14,1,c[10030]|0);return}function r7(){var a=0;a=i;cf(c[10030]|0,226560,(v=i,i=i+16|0,h[v>>3]=.24089999496936798,c[v+8>>2]=3,v)|0);c[56632]=0;c[56606]=0;i=a;return}function r8(b,d){b=b|0;d=d|0;var e=0;if(a[226552]|0){e=c[10030]|0;aF(10,e|0);a[226552]=0}c[56610]=b;c[56608]=d;return}function r9(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0;e=i;do{if(a[226552]|0){f=c[56630]|0;c[56630]=f+1;if((f|0)<=4){break}f=c[10030]|0;aI(106424,8,1,f|0);c[56630]=1}else{a[226552]=1;f=c[56606]|0;g=c[56632]|0;if((f|0)!=(g|0)){h=c[226496+(f+2<<2)>>2]|0;if((h|0)==(c[226496+(g+2<<2)>>2]|0)){j=f}else{f=c[10030]|0;cf(f|0,106544,(v=i,i=i+8|0,c[v>>2]=h,v)|0);j=c[56606]|0}c[56632]=j}h=c[10030]|0;f=c[56610]|0;g=c[56608]|0;cf(h|0,106504,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=g,v)|0);c[56630]=1}}while(0);cf(c[10030]|0,106360,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);c[56610]=b;c[56608]=d;i=e;return}function sa(b){b=b|0;var d=0,e=0;if(a[226552]|0){d=c[10030]|0;aF(10,d|0);a[226552]=0}if((b|0)>2){e=(b|0)%3&-1}else{e=b}c[56606]=(e|0)>-2?e:-2;return}function sb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0;f=i;if(a[226552]|0){g=c[10030]|0;aF(10,g|0);a[226552]=0}cf(c[10030]|0,106848,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);d=(c[56722]|0)==0?104:118;b=c[56636]|0;do{if((c[56634]|0)!=(b|0)){c[56634]=b;if((b|0)==1){g=c[10030]|0;aI(108272,17,1,g|0);break}else if((b|0)==2){aI(106656,17,1,c[10030]|0);break}else if((b|0)==0){aI(106736,17,1,c[10030]|0);break}else{break}}}while(0);cf(c[10030]|0,106624,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=e,v)|0);i=f;return}function sc(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=i;if(a[226552]|0){g=c[10030]|0;aF(10,g|0);a[226552]=0}c[56610]=b;c[56608]=d;cf(c[10030]|0,108368,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);if((c[56634]|0)!=1){d=c[10030]|0;aI(108272,17,1,d|0);c[56634]=1}if((e|0)<0){h=108192}else{h=c[226448+(((e|0)%12&-1)<<2)>>2]|0}cf(c[10030]|0,154696,(v=i,i=i+8|0,c[v>>2]=h,v)|0);i=f;return}function sd(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0;g=i;cf(c[10030]|0,108416,(v=i,i=i+40|0,c[v>>2]=a,c[v+8>>2]=b,c[v+16>>2]=(f|0)==0?108:97,c[v+24>>2]=d,c[v+32>>2]=e,v)|0);c[56610]=d;c[56608]=e;i=g;return}function se(){c[58256]=0;a[233032]=0;aI(113088,165,1,c[10030]|0);aI(112768,208,1,c[10030]|0);aI(111168,1548,1,c[10030]|0);aI(110792,238,1,c[10030]|0);aI(109768,808,1,c[10030]|0);aI(109360,328,1,c[10030]|0);aI(108752,519,1,c[10030]|0);aI(108504,212,1,c[10030]|0);return}function sf(){aI(122176,5,1,c[10030]|0);return}function sg(){aI(113352,9,1,c[10030]|0);return}function sh(){var b=0,d=0,e=0;b=i;d=c[3524]|0;cf(c[10030]|0,113672,(v=i,i=i+24|0,c[v>>2]=c[58256],h[v+8>>3]=5.0,h[v+16>>3]=3.0,v)|0);c[58256]=(c[58256]|0)+1;e=c[d+12>>2]|0;cf(c[10030]|0,113504,(v=i,i=i+16|0,c[v>>2]=c[d+8>>2],c[v+8>>2]=e,v)|0);a[232720]=0;i=b;return}function si(a){a=a|0;c[58248]=a;return 1}function sj(b){b=b|0;a[233032]=(b|0)>0;return 1}function sk(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0,x=0,y=0.0,z=0.0,A=0,B=0,C=0,D=0.0,E=0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0,L=0,M=0;d=i;e=c[58246]|0;f=c[58244]|0;g=(e|0)==(a|0)&(f|0)==(b|0);if((c[58250]|0)!=0){j=c[10030]|0;if(g){cf(j|0,113832,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);c[58246]=a;c[58244]=b;i=d;return}else{cf(j|0,113728,(v=i,i=i+32|0,c[v>>2]=e,c[v+8>>2]=f,c[v+16>>2]=a,c[v+24>>2]=b,v)|0);c[58246]=a;c[58244]=b;i=d;return}}if(g){if((c[58254]&1|0)!=0){c[58246]=a;c[58244]=b;i=d;return}g=c[10030]|0;cf(g|0,113832,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);c[58246]=a;c[58244]=b;i=d;return}k=+(e>>>0>>>0);l=+(f>>>0>>>0);m=+(a>>>0>>>0);n=m-k;o=+(b>>>0>>>0);p=o-l;q=+Q(+(n*n+p*p));r=n/q;n=p/q;p=+h[29126];g=c[58254]|0;if(p<q){s=l;t=k;u=q;q=p;j=g;w=e;x=f;while(1){y=t+r*q;z=s+n*q;A=~~+O(+(y+.5));B=~~+O(+(z+.5));if((j&1|0)==0){C=c[10030]|0;cf(C|0,113728,(v=i,i=i+32|0,c[v>>2]=w,c[v+8>>2]=x,c[v+16>>2]=A,c[v+24>>2]=B,v)|0);D=+h[29126];E=c[58254]|0}else{D=q;E=j}c[58246]=A;c[58244]=B;F=u-D;C=E+1&3;c[58254]=C;G=+(c[232736+((c[58242]|0)*24&-1)+(C<<2)>>2]|0);h[29126]=G;if(G<F){s=z;t=y;u=F;q=G;j=C;w=A;x=B}else{H=z;I=y;J=G;K=A;L=B;M=C;break}}}else{H=l;I=k;J=p;K=e;L=f;M=g}p=m-I;I=o-H;h[29126]=J- +Q(+(I*I+p*p));if((M&1|0)!=0){c[58246]=a;c[58244]=b;i=d;return}M=c[10030]|0;if((K|0)==(a|0)&(L|0)==(b|0)){cf(M|0,113832,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);c[58246]=a;c[58244]=b;i=d;return}else{cf(M|0,113728,(v=i,i=i+32|0,c[v>>2]=K,c[v+8>>2]=L,c[v+16>>2]=a,c[v+24>>2]=b,v)|0);c[58246]=a;c[58244]=b;i=d;return}}function sl(b){b=b|0;var d=0,e=0,f=0.0,j=0.0;d=i;if((b|0)>7){e=(b|0)%8&-1}else{e=b}b=e+2|0;e=(b|0)<0?0:b;f=+g[232732+(e*24&-1)>>2];if(!(f==+g[232732+((c[58242]|0)*24&-1)>>2]&a[232720])){b=c[10030]|0;j=f;cf(b|0,113976,(v=i,i=i+8|0,h[v>>3]=j,v)|0);a[232720]=1}c[58242]=e;c[58254]=0;h[29126]=+(c[232736+(e*24&-1)>>2]|0);c[58250]=c[232728+(e*24&-1)>>2];i=d;return}function sm(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;if((e|0)==0){i=f;return}if((a[e]|0)==0){i=f;return}g=bP(e|0)|0;e=a[g]|0;L25673:do{if(e<<24>>24!=0){h=0;j=e;while(1){if(j<<24>>24==34){a[g+h|0]=39}k=h+1|0;if(k>>>0>=(uA(g|0)|0)>>>0){break L25673}h=k;j=a[g+k|0]|0}}}while(0);e=c[58248]|0;if((e|0)==2){l=3}else if((e|0)==0){l=1}else if((e|0)==1){l=2}else{l=0}e=a[233032]|0?90:0;cf(c[10030]|0,114256,(v=i,i=i+40|0,c[v>>2]=g,c[v+8>>2]=b,c[v+16>>2]=d,c[v+24>>2]=e,c[v+32>>2]=l,v)|0);uu(g);i=f;return}function sn(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0;g=i;c[58246]=a;c[58244]=b;c[58254]=0;h[29126]=+(c[232736+((c[58242]|0)*24&-1)>>2]|0);sk(d,e);if((f|0)==0){i=g;return}cf(c[10030]|0,114408,(v=i,i=i+32|0,c[v>>2]=d-a,c[v+8>>2]=e-b,c[v+16>>2]=d,c[v+24>>2]=e,v)|0);i=g;return}function so(){var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0.0,L=0,M=0,N=0,O=0.0,P=0.0,Q=0,R=0;b=i;i=i+56|0;d=b|0;e=b+24|0;f=b+48|0;g=c[13898]|0;if((g|0)!=2){a[232616]=0;a[232304]=0;c[58074]=1;a[232648]=0;a[232640]=0;c[58078]=0;h[29067]=10.0;h[29036]=1.0;c[58080]=-1;a[232544]=a[116880]|0;a[232545|0]=a[116881|0]|0;a[232546|0]=a[116882|0]|0;a[232547|0]=a[116883|0]|0;a[232548|0]=a[116884|0]|0;a[232549|0]=a[116885|0]|0}j=c[8272]|0;L25696:do{if((g|0)<(j|0)){k=d|0;l=d+8|0;m=g;n=j;L25698:while(1){o=c[1054]|0;p=(a[o+(m*40&-1)|0]&1)==0;L25700:do{if(!p){q=c[o+(m*40&-1)+36>>2]|0;r=o+(m*40&-1)+32|0;s=c[10036]|0;t=0;while(1){if((t|0)>=(q|0)){break}if((a[s+((c[r>>2]|0)+t|0)|0]|0)==(a[t+103664|0]|0)){t=t+1|0}else{break L25700}}if((t|0)==1){break L25696}}}while(0);r=c[58086]|0;L25707:do{if((r|0)==0){u=232344}else{s=c[o+(m*40&-1)+36>>2]|0;q=c[o+(m*40&-1)+32>>2]|0;w=(s|0)>0;x=c[10036]|0;y=232344;z=r;while(1){L25711:do{if(!p){if(w){A=0;B=0;C=q;while(1){D=a[z+A|0]|0;if(D<<24>>24==(a[x+(A+C|0)|0]|0)){E=C;F=B}else{if(D<<24>>24!=36){break L25711}E=C-1|0;F=1}G=A+1|0;if((G|0)<(F+s|0)){A=G;B=F;C=E}else{break}}if((F|0)==0){H=G}else{u=y;break L25707}}else{H=0}C=a[z+H|0]|0;if((C<<24>>24|0)==36|(C<<24>>24|0)==0){u=y;break L25707}}}while(0);C=y+8|0;B=c[C>>2]|0;if((B|0)==0){u=C;break}else{y=C;z=B}}}}while(0);p=c[u+4>>2]|0;L25724:do{if((p|0)==14){r=m+1|0;c[13898]=r;if((r|0)>=(n|0)){break}L25727:do{if((a[o+(r*40&-1)|0]&1)==0){I=c[10036]|0;J=o+(r*40&-1)+32|0}else{z=c[o+(r*40&-1)+36>>2]|0;y=o+(r*40&-1)+32|0;s=c[10036]|0;x=0;while(1){if((x|0)>=(z|0)){break}if((a[s+((c[y>>2]|0)+x|0)|0]|0)==(a[x+103664|0]|0)){x=x+1|0}else{I=s;J=y;break L25727}}if((x|0)==1){break L25724}else{I=s;J=y}}}while(0);if((ca(I+(c[J>>2]|0)|0,21e4,(v=i,i=i+8|0,c[v>>2]=f,v)|0)|0)==1){c[58080]=c[f>>2]}c[13898]=(c[13898]|0)+1}else if((p|0)==4){c[58074]=0;a[232544]=a[116696]|0;a[232545|0]=a[116697|0]|0;a[232546|0]=a[116698|0]|0;a[232547|0]=a[116699|0]|0;a[232548|0]=a[116700|0]|0;a[232549|0]=a[116701|0]|0;a[232550|0]=a[116702|0]|0;c[13898]=m+1}else if((p|0)==0){a[232616]=0;c[13898]=m+1}else if((p|0)==9){c[58074]=2;c[58078]=1;c[13898]=m+1}else if((p|0)==3){a[232304]=0;c[13898]=m+1}else if((p|0)==1){a[232616]=1;c[13898]=m+1}else if((p|0)==8){c[58074]=2;c[58078]=2;c[13898]=m+1}else if((p|0)==7){c[58074]=2;a[232648]=1;c[13898]=m+1}else if((p|0)==15){c[58080]=-1;c[13898]=m+1}else if((p|0)==2){a[232304]=1;c[13898]=m+1}else if((p|0)==5){c[58074]=1;c[13898]=m+1}else if((p|0)==6){c[58074]=2;c[13898]=m+1}else if((p|0)==16){r=m+1|0;c[13898]=r;if((r|0)>=(n|0)){break}L25751:do{if((a[o+(r*40&-1)|0]&1)!=0){z=c[o+(r*40&-1)+36>>2]|0;q=o+(r*40&-1)+32|0;w=c[10036]|0;t=0;while(1){if((t|0)>=(z|0)){break}if((a[w+((c[q>>2]|0)+t|0)|0]|0)==(a[t+103664|0]|0)){t=t+1|0}else{break L25751}}if((t|0)==1){break L25724}}}while(0);r=is(e)|0;q=c[r>>2]|0;if((q|0)==3){K=+uz(c[r+8>>2]|0,0)}else if((q|0)==1){K=+(c[r+8>>2]|0)}else if((q|0)==2){K=+h[r+8>>3]}else{L=19304;break L25698}h[29036]=K}else if((p|0)==10){c[58078]=0;c[13898]=m+1}else if((p|0)==11){c[58074]=2;a[232640]=1;c[13898]=m+1}else if((p|0)==12){r=m+1|0;c[13898]=r;M=r;L=19307}else{M=m;L=19307}}while(0);L25766:do{if((L|0)==19307){L=0;L25768:do{if((M|0)<(n|0)){L25770:do{if((a[o+(M*40&-1)|0]&1)!=0){r=c[o+(M*40&-1)+36>>2]|0;q=o+(M*40&-1)+32|0;w=c[10036]|0;z=0;while(1){if((z|0)>=(r|0)){break}if((a[w+((c[q>>2]|0)+z|0)|0]|0)==(a[z+103664|0]|0)){z=z+1|0}else{break L25770}}if((z|0)==1){N=M;break L25768}}}while(0);a[14176]=1;is(d);a[14176]=0;if((c[k>>2]|0)!=3){c[13898]=M;N=M;break}t=c[l>>2]|0;if((t|0)==0){N=c[13898]|0;break}q=bk(t|0,148464)|0;if((q|0)>0){uF(232544,t|0,51);a[q+232544|0]=0}if((a[t+q|0]|0)==44){w=t+(q+1|0)|0;ca(w|0,148448,(v=i,i=i+8|0,c[v>>2]=232536,v)|0)}uu(t);break L25766}else{N=M}}while(0);if((p|0)==12){L=19324;break L25698}if((N|0)>=(c[8272]|0)){break}t=c[1054]|0;L25792:do{if((a[t+(N*40&-1)|0]&1)!=0){w=c[t+(N*40&-1)+36>>2]|0;q=t+(N*40&-1)+32|0;r=c[10036]|0;y=0;while(1){if((y|0)>=(w|0)){break}if((a[r+((c[q>>2]|0)+y|0)|0]|0)==(a[y+103664|0]|0)){y=y+1|0}else{break L25792}}if((y|0)==1){break L25766}}}while(0);t=is(e)|0;q=c[t>>2]|0;if((q|0)==1){O=+(c[t+8>>2]|0)}else if((q|0)==2){O=+h[t+8>>3]}else if((q|0)==3){O=+uz(c[t+8>>2]|0,0)}else{L=19335;break L25698}h[29067]=O;c[13898]=(c[13898]|0)+1}}while(0);m=c[13898]|0;n=c[8272]|0;if((m|0)>=(n|0)){break L25696}}if((L|0)==19304){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==19324){uf(N,116592,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((L|0)==19335){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);O=+h[29067];do{if(O<5.0){h[29067]=5.0;P=12.0e3}else{if(O>99.99){h[29067]=99.99;P=239976.0;break}else{P=O*2400.0;break}}}while(0);c[(c[3524]|0)+16>>2]=~~(P*+h[29036]*11.0/720.0);P=+h[29067]*2400.0*+h[29036];if((c[58074]|0)==0){c[(c[3524]|0)+20>>2]=~~(P*6.0/720.0+.5)}else{c[(c[3524]|0)+20>>2]=~~(P*5.3/720.0+.5)}L=c[58078]|0;N=a[232616]|0?116440:116408;e=a[232304]|0?131504:131584;M=c[58074]|0;d=(M|0)==0;if((L|0)==0){if(d){Q=136208}else{Q=(M|0)==2?116352:179864}f=a[232648]|0?116296:179864;J=a[232640]|0?116192:179864;P=+h[29036];O=+h[29067];I=c[58080]|0;u=(I|0)>-1?179864:136208;be(13048,116512,(v=i,i=i+80|0,c[v>>2]=N,c[v+8>>2]=e,c[v+16>>2]=Q,c[v+24>>2]=f,c[v+32>>2]=J,h[v+40>>3]=P,c[v+48>>2]=232544,h[v+56>>3]=O,c[v+64>>2]=u,c[v+72>>2]=I,v)|0);i=b;return}else{if(d){R=136208}else{R=(M|0)==2?116352:179864}M=a[232648]|0?116296:179864;d=a[232640]|0?116192:179864;O=+h[29036];I=(L|0)==1?115960:115816;L=c[58080]|0;u=(L|0)>-1?179864:136208;be(13048,116072,(v=i,i=i+72|0,c[v>>2]=N,c[v+8>>2]=e,c[v+16>>2]=R,c[v+24>>2]=M,c[v+32>>2]=d,h[v+40>>3]=O,c[v+48>>2]=I,c[v+56>>2]=u,c[v+64>>2]=L,v)|0);i=b;return}}function sp(a){a=a|0;c[58158]=a;return 1}function sq(a){a=a|0;c[58130]=a;return 1}function sr(a){a=a|0;return 0}function ss(){return}function st(){var b=0,d=0,e=0,f=0,g=0.0,j=0,k=0;b=i;i=i+8|0;d=b|0;cG(d|0);c[58082]=0;c[58084]=0;e=c[10030]|0;f=bg(aN(d|0)|0)|0;cf(e|0,122032,(v=i,i=i+8|0,c[v>>2]=f,v)|0);f=c[58080]|0;if((f|0)>-1){e=c[10030]|0;cf(e|0,121880,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}if((c[58074]|0)==2){f=c[10030]|0;aI(121552,247,1,f|0);f=c[10030]|0;if(a[232648]|0){aI(121472,33,1,f|0)}else{aI(121408,24,1,f|0)}f=c[58078]|0;if((f|0)==2){e=c[10030]|0;aI(120992,174,1,e|0)}else if((f|0)==1){aI(121240,124,1,c[10030]|0)}if(a[232640]|0){f=c[10030]|0;aI(120888,54,1,f|0)}f=c[10030]|0;aI(120824,31,1,f|0)}aI(120464,227,1,c[10030]|0);cf(c[10030]|0,120328,(v=i,i=i+8|0,h[v>>3]=+h[29036],v)|0);aI(119968,246,1,c[10030]|0);f=c[58074]|0;if((f|0)==0){e=c[10030]|0;g=+h[29067];cf(e|0,119792,(v=i,i=i+16|0,c[v>>2]=232544,h[v+8>>3]=g,v)|0)}else if((f|0)!=2){aI(119656,82,1,c[10030]|0);g=+h[29067];cf(c[10030]|0,119576,(v=i,i=i+16|0,c[v>>2]=232544,h[v+8>>3]=g,v)|0)}aI(119448,91,1,c[10030]|0);f=c[10030]|0;if(a[232616]|0){aI(119328,18,1,f|0)}else{aI(119248,19,1,f|0)}f=c[10030]|0;if(a[232304]|0){aI(119168,20,1,f|0);j=c[10030]|0;k=aI(117096,1968,1,j|0)|0;i=b;return}else{aI(119128,19,1,f|0);j=c[10030]|0;k=aI(117096,1968,1,j|0)|0;i=b;return}}function su(){var a=0,b=0;if((c[58074]|0)!=2){a=c[10030]|0;b=aI(122176,5,1,a|0)|0;return}aI(122728,10,1,c[10030]|0);aI(122648,12,1,c[10030]|0);aI(122520,16,1,c[10030]|0);aI(122424,5,1,c[10030]|0);aI(122312,10,1,c[10030]|0);a=c[10030]|0;b=aI(122176,5,1,a|0)|0;return}function sv(){var b=0;if(a[232528]|0){a[232528]=0;b=c[10030]|0;aI(125976,2,1,b|0)}aI(122792,8,1,c[10030]|0);return}function sw(){var b=0,d=0,e=0.0;b=i;c[58126]=-2;h[29062]=1.0;h[29061]=+h[3817];cf(c[10030]|0,123376,(v=i,i=i+24|0,c[v>>2]=c[58156],h[v+8>>3]=5.0,h[v+16>>3]=3.0,v)|0);d=c[3524]|0;e=+((c[d+12>>2]|0)>>>0>>>0)/10.0;cf(c[10030]|0,123080,(v=i,i=i+16|0,h[v>>3]=+((c[d+8>>2]|0)>>>0>>>0)/10.0,h[v+8>>3]=e,v)|0);cf(c[10030]|0,122896,(v=i,i=i+8|0,h[v>>3]=+h[3817],v)|0);c[58156]=(c[58156]|0)+1;a[232608]=0;i=b;return}function sx(b,d){b=b|0;d=d|0;var e=0;if((c[58084]|0)==(b|0)&(c[58082]|0)==(d|0)){return}if(a[232528]|0){a[232528]=0;e=c[10030]|0;aI(125976,2,1,e|0)}c[58084]=b;c[58082]=d;return}function sy(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0.0,l=0.0;e=i;f=c[58084]|0;g=c[58082]|0;if((f|0)==(b|0)&(g|0)==(d|0)){i=e;return}do{if(a[232528]|0){j=c[58128]|0;c[58128]=j+1;if((j|0)<=4){break}j=c[10030]|0;aF(10,j|0);c[58128]=1}else{a[232528]=1;j=c[10030]|0;k=+(f|0)/10.0;l=+(g|0)/10.0;cf(j|0,123728,(v=i,i=i+16|0,h[v>>3]=k,h[v+8>>3]=l,v)|0);c[58128]=2}}while(0);c[58084]=b;c[58082]=d;cf(c[10030]|0,123552,(v=i,i=i+16|0,h[v>>3]=+(b|0)/10.0,h[v+8>>3]=+(d|0)/10.0,v)|0);i=e;return}function sz(b){b=b|0;var d=0,e=0;d=i;if((b|0)>7){e=(b|0)%8&-1}else{e=b}if(a[232528]|0){a[232528]=0;b=c[10030]|0;aI(125976,2,1,b|0)}do{if(a[232608]|0){c[58126]=e+1;a[232608]=0}else{if((c[58126]|0)!=(e|0)){break}i=d;return}}while(0);cf(c[10030]|0,123808,(v=i,i=i+8|0,c[v>>2]=e,v)|0);c[58126]=e;i=d;return}function sA(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0;f=i;if((e|0)==0){i=f;return}if((a[e]|0)==0){i=f;return}g=bP(e|0)|0;if(a[232528]|0){a[232528]=0;e=c[10030]|0;aI(125976,2,1,e|0)}e=c[58130]|0;if((e|0)==0){j=1}else if((e|0)==1){j=2}else if((e|0)==2){j=3}else{j=0}e=c[58074]|0;do{if((e|0)==0){k=a[g]|0;L25932:do{if(k<<24>>24!=0){l=0;m=k;while(1){if(m<<24>>24==34){a[g+l|0]=39}n=l+1|0;if(n>>>0>=(uA(g|0)|0)>>>0){break L25932}l=n;m=a[g+n|0]|0}}}while(0);k=c[10030]|0;if(a[232600]|0){o=+h[29067];p=+(b>>>0>>>0)/10.0;q=+(d>>>0>>>0)/10.0;m=c[58158]|0;cf(k|0,124232,(v=i,i=i+56|0,c[v>>2]=g,c[v+8>>2]=232544,h[v+16>>3]=o,h[v+24>>3]=p,h[v+32>>3]=q,c[v+40>>2]=m,c[v+48>>2]=j,v)|0);break}else{q=+(b>>>0>>>0)/10.0;p=+(d>>>0>>>0)/10.0;m=c[58158]|0;cf(k|0,124152,(v=i,i=i+40|0,c[v>>2]=g,h[v+8>>3]=q,h[v+16>>3]=p,c[v+24>>2]=m,c[v+32>>2]=j,v)|0);break}}else{if(!(a[232600]|0)){m=c[10030]|0;p=+(b>>>0>>>0)/10.0;q=+(d>>>0>>>0)/10.0;k=c[58158]|0;cf(m|0,123912,(v=i,i=i+40|0,c[v>>2]=g,h[v+8>>3]=p,h[v+16>>3]=q,c[v+24>>2]=k,c[v+32>>2]=j,v)|0);break}k=c[10030]|0;if((e|0)==2){q=+(b>>>0>>>0)/10.0;p=+(d>>>0>>>0)/10.0;m=c[58158]|0;cf(k|0,123912,(v=i,i=i+40|0,c[v>>2]=g,h[v+8>>3]=q,h[v+16>>3]=p,c[v+24>>2]=m,c[v+32>>2]=j,v)|0);break}else{p=+h[29067];q=+(b>>>0>>>0)/10.0;o=+(d>>>0>>>0)/10.0;m=c[58158]|0;cf(k|0,123992,(v=i,i=i+56|0,c[v>>2]=232544,h[v+8>>3]=p,c[v+16>>2]=g,h[v+24>>3]=q,h[v+32>>3]=o,c[v+40>>2]=m,c[v+48>>2]=j,v)|0);break}}}while(0);uu(g);i=f;return}function sB(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0;f=i;if(a[232528]|0){a[232528]=0;g=c[10030]|0;aI(125976,2,1,g|0)}if((e|0)>9){j=(e|0)%10&-1}else{j=e}cf(c[10030]|0,124360,(v=i,i=i+24|0,c[v>>2]=j,h[v+8>>3]=+(b>>>0>>>0)/10.0,h[v+16>>3]=+(d>>>0>>>0)/10.0,v)|0);i=f;return}function sC(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0;j=i;if(!((c[58084]|0)==(b|0)&(c[58082]|0)==(d|0))){if(a[232528]|0){a[232528]=0;k=c[10030]|0;aI(125976,2,1,k|0)}c[58084]=b;c[58082]=d}if((g|0)!=0){k=c[10030]|0;l=(g|0)==1?124720:124632;m=+(b>>>0>>>0)/10.0;n=+(d>>>0>>>0)/10.0;o=+(e>>>0>>>0)/10.0;p=+(f>>>0>>>0)/10.0;cf(k|0,124776,(v=i,i=i+40|0,c[v>>2]=l,h[v+8>>3]=m,h[v+16>>3]=n,h[v+24>>3]=o,h[v+32>>3]=p,v)|0);c[58084]=e;c[58082]=f;i=j;return}if((b|0)==(e|0)&(d|0)==(f|0)){c[58084]=e;c[58082]=f;i=j;return}cf(c[10030]|0,124504,(v=i,i=i+32|0,h[v>>3]=+(b>>>0>>>0)/10.0,h[v+8>>3]=+(d>>>0>>>0)/10.0,h[v+16>>3]=+(e>>>0>>>0)/10.0,h[v+24>>3]=+(f>>>0>>>0)/10.0,v)|0);c[58084]=e;c[58082]=f;i=j;return}function sD(b){b=b|0;var d=0,e=0,f=0,g=0,j=0.0;d=i;do{if((a[b]|0)==0){e=0}else{f=bk(b|0,148464)|0;if(f>>>0<51){uF(232544,b|0,51)}g=b+(f+1|0)|0;ca(g|0,148448,(v=i,i=i+8|0,c[v>>2]=232536,v)|0);j=+h[29067];if(j<5.0){h[29067]=5.0;e=1;break}if(j<100.0){e=1;break}h[29067]=99.99;e=1}}while(0);a[232600]=e;i=d;return 1}function sE(b){b=+b;var d=0,e=0.0,f=0;d=i;e=b<0.0?1.0:b;if(+h[29061]==e){i=d;return}if(a[232528]|0){a[232528]=0;f=c[10030]|0;aI(125976,2,1,f|0)}cf(c[10030]|0,124896,(v=i,i=i+8|0,h[v>>3]=e,v)|0);h[29061]=e;i=d;return}function sF(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0,l=0,m=0.0,n=0.0,o=0.0,p=0.0,q=0;j=i;k=b>>4;l=b&15;if(a[232528]|0){a[232528]=0;b=c[10030]|0;aI(125976,2,1,b|0)}if((l|0)==0){b=c[10030]|0;m=+(d>>>0>>>0)/10.0;n=+(e>>>0>>>0)/10.0;o=+((f+d|0)>>>0>>>0)/10.0;p=+((g+e|0)>>>0>>>0)/10.0;cf(b|0,125216,(v=i,i=i+64|0,h[v>>3]=m,h[v+8>>3]=n,h[v+16>>3]=o,h[v+24>>3]=n,h[v+32>>3]=o,h[v+40>>3]=p,h[v+48>>3]=m,h[v+56>>3]=p,v)|0);i=j;return}else if((l|0)==2|(l|0)==5){q=k*12&-1}else{q=k}if((q|0)<100){p=+(100-q|0)*.01;q=c[10030]|0;m=1.0-p;cf(q|0,126664,(v=i,i=i+16|0,h[v>>3]=m,h[v+8>>3]=p,v)|0);a[232608]=1}else{q=c[10030]|0;aI(126536,25,1,q|0)}p=+(d>>>0>>>0)/10.0;m=+(e>>>0>>>0)/10.0;o=+((f+d|0)>>>0>>>0)/10.0;n=+((g+e|0)>>>0>>>0)/10.0;cf(c[10030]|0,124968,(v=i,i=i+64|0,h[v>>3]=p,h[v+8>>3]=m,h[v+16>>3]=o,h[v+24>>3]=m,h[v+32>>3]=o,h[v+40>>3]=n,h[v+48>>3]=p,h[v+56>>3]=n,v)|0);i=j;return}function sG(b){b=+b;var d=0,e=0;d=i;if(+h[29062]==b){i=d;return}if(a[232528]|0){a[232528]=0;e=c[10030]|0;aI(125976,2,1,e|0)}cf(c[10030]|0,125384,(v=i,i=i+8|0,h[v>>3]=b,v)|0);h[29062]=b;i=d;return}function sH(b){b=b|0;var d=0,e=0,f=0.0,g=0,j=0.0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0,r=0.0;d=i;i=i+24|0;e=d|0;f=+h[b+8>>3];a[232608]=1;if(a[232528]|0){a[232528]=0;g=c[10030]|0;aI(125976,2,1,g|0)}if(!(a[232616]|0)){j=f<.001?0.0:f;g=c[10030]|0;cf(g|0,125848,(v=i,i=i+8|0,h[v>>3]=j,v)|0);i=d;return}g=b|0;k=c[g>>2]|0;if((k|0)==1){l=c[b+4>>2]|0;if((l|0)>7){m=(l|0)%8&-1}else{m=l}do{if((m|0)==-1){l=c[10030]|0;aI(125736,21,1,l|0)}else{if((m|0)<=-1){break}l=c[10030]|0;cf(l|0,125576,(v=i,i=i+8|0,c[v>>2]=m,v)|0)}}while(0);n=c[g>>2]|0}else{n=k}if((n|0)==5){k=c[5169]|0;do{if((k|0)==0){o=f}else{j=+(k|0);if(f>=+(k-1|0)/j){o=1.0;break}o=+O(+(f*j))/j}}while(0);fq(o,e);p=+h[e>>3]}else if((n|0)==3){n=c[b+4>>2]|0;o=+(n>>>16&255|0)/255.0;h[e>>3]=o;h[e+8>>3]=+(n>>>8&255|0)/255.0;h[e+16>>3]=+(n&255|0)/255.0;p=o}else{i=d;return}n=e|0;if(p<1.0e-4){h[n>>3]=0.0}b=e+8|0;p=+h[b>>3];if(p<1.0e-4){h[b>>3]=0.0;q=0.0}else{q=p}b=e+16|0;p=+h[b>>3];if(p<1.0e-4){h[b>>3]=0.0;r=0.0}else{r=p}cf(c[10030]|0,125464,(v=i,i=i+24|0,h[v>>3]=+h[n>>3],h[v+8>>3]=q,h[v+16>>3]=r,v)|0);i=d;return}function sI(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0,p=0,q=0;e=i;f=c[d+8>>2]|0;g=f>>4;j=f&15;if(a[232528]|0){a[232528]=0;f=c[10030]|0;aI(125976,2,1,f|0)}if((j|0)==0){f=c[10030]|0;aI(126768,23,1,f|0)}else if((j|0)==1|(j|0)==4){k=g;l=19531}else if((j|0)==2|(j|0)==5){k=g*12&-1;l=19531}do{if((l|0)==19531){if((k|0)<100){m=+(100-k|0)*.01;g=c[10030]|0;n=1.0-m;cf(g|0,126664,(v=i,i=i+16|0,h[v>>3]=n,h[v+8>>3]=m,v)|0);break}else{g=c[10030]|0;aI(126536,25,1,g|0);break}}}while(0);aI(126432,5,1,c[10030]|0);k=c[10030]|0;if((b|0)<=0){o=k;p=aI(126072,27,1,o|0)|0;i=e;return}l=b-1|0;g=0;j=k;while(1){m=+(c[d+(g*12&-1)+4>>2]|0)/10.0;k=g+1|0;if((g|0)<(l|0)){q=((k|0)%5&-1|0)==0?126232:126152}else{q=126152}cf(j|0,126296,(v=i,i=i+24|0,h[v>>3]=+(c[d+(g*12&-1)>>2]|0)/10.0,h[v+8>>3]=m,c[v+16>>2]=q,v)|0);f=c[10030]|0;if((k|0)<(b|0)){g=k;j=f}else{o=f;break}}p=aI(126072,27,1,o|0)|0;i=e;return}function sJ(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0.0,T=0,U=0,V=0,W=0,X=0.0,Y=0.0,Z=0.0,_=0.0,$=0.0,aa=0.0,ab=0.0,ac=0,ad=0,ae=0,af=0;b=i;i=i+1184|0;d=b|0;e=b+8|0;f=b+32|0;j=b+56|0;k=b+1112|0;l=b+1120|0;m=b+1128|0;n=b+80|0;uE(n|0,0,1025);o=c[13898]|0;p=c[8272]|0;L26069:do{if((o|0)<(p|0)){q=f|0;r=f+8|0;s=m|0;t=e|0;u=e+8|0;w=m+50|0;x=o;y=p;L26071:while(1){z=c[1054]|0;A=(a[z+(x*40&-1)|0]&1)==0;L26073:do{if(!A){B=c[z+(x*40&-1)+36>>2]|0;C=z+(x*40&-1)+32|0;D=c[10036]|0;E=0;while(1){if((E|0)>=(B|0)){break}if((a[D+((c[C>>2]|0)+E|0)|0]|0)==(a[E+103664|0]|0)){E=E+1|0}else{break L26073}}if((E|0)==1){break L26069}}}while(0);C=c[60336]|0;L26080:do{if((C|0)==0){F=241344}else{D=c[z+(x*40&-1)+36>>2]|0;B=c[z+(x*40&-1)+32>>2]|0;G=(D|0)>0;H=c[10036]|0;I=241344;J=C;while(1){L26084:do{if(!A){if(G){K=0;L=0;M=B;while(1){N=a[J+K|0]|0;if(N<<24>>24==(a[H+(K+M|0)|0]|0)){O=M;P=L}else{if(N<<24>>24!=36){break L26084}O=M-1|0;P=1}Q=K+1|0;if((Q|0)<(P+D|0)){K=Q;L=P;M=O}else{break}}if((P|0)==0){R=Q}else{F=I;break L26080}}else{R=0}M=a[J+R|0]|0;if((M<<24>>24|0)==36|(M<<24>>24|0)==0){F=I;break L26080}}}while(0);M=I+8|0;L=c[M>>2]|0;if((L|0)==0){F=M;break}else{I=M;J=L}}}}while(0);A=c[F+4>>2]|0;L26097:do{if((A|0)==21){c[13898]=x+1;C=is(j)|0;J=c[C>>2]|0;if((J|0)==2){S=+h[C+8>>3]}else if((J|0)==3){S=+uz(c[C+8>>2]|0,0)}else if((J|0)==1){S=+(c[C+8>>2]|0)}else{T=19615;break L26071}h[30158]=S}else if((A|0)==7){U=x+1|0;c[13898]=U;if((U|0)>=(y|0)){V=U;T=19660;break L26071}L26106:do{if((a[z+(U*40&-1)|0]&1)!=0){C=c[z+(U*40&-1)+36>>2]|0;J=z+(U*40&-1)+32|0;I=c[10036]|0;D=0;while(1){if((D|0)>=(C|0)){break}if((a[I+((c[J>>2]|0)+D|0)|0]|0)==(a[D+103664|0]|0)){D=D+1|0}else{break L26106}}if((D|0)==1){V=U;T=19662;break L26071}}}while(0);a[14176]=1;is(f);a[14176]=0;if((c[q>>2]|0)!=3){T=19578;break L26071}J=c[r>>2]|0;if((J|0)==0){T=19580;break L26071}I=c[60308]|0;if((I|0)!=0){uu(I);c[60308]=0}if((a[J]|0)==0){uu(J);break}else{c[60308]=J;break}}else if((A|0)==4){c[13898]=x+1;a[241228]=1}else if((A|0)==27){uE(s|0,0,51);J=x+1|0;c[13898]=J;if((J|0)>=(y|0)){break}L26124:do{if((a[z+(J*40&-1)|0]&1)!=0){I=c[z+(J*40&-1)+36>>2]|0;C=z+(J*40&-1)+32|0;H=c[10036]|0;B=0;while(1){if((B|0)>=(I|0)){break}if((a[H+((c[C>>2]|0)+B|0)|0]|0)==(a[B+103664|0]|0)){B=B+1|0}else{break L26124}}if((B|0)==1){break L26097}}}while(0);a[14176]=1;is(e);a[14176]=0;if((c[t>>2]|0)!=3){c[13898]=J;break}C=c[u>>2]|0;if((C|0)==0){break}h[d>>3]=0.0;H=bh(C|0,44)|0;if((H|0)==0){W=uA(C|0)|0;X=0.0}else{I=H+1|0;ca(I|0,148448,(v=i,i=i+8|0,c[v>>2]=d,v)|0);W=H-C|0;X=+h[d>>3]}H=W>>>0>50?50:W;uD(s|0,C|0,H);a[m+H|0]=0;uF(241280,s|0,51);a[w]=0;uu(C);if(X>0.0){h[30167]=X;break}if(X>=0.0){break}h[30167]=12.0}else if((A|0)==11){c[13898]=x+1;a[241237]=1}else if((A|0)==16){c[13898]=x+1;c[60311]=0}else if((A|0)==18){c[13898]=x+1;c[60311]=2}else if((A|0)==1){c[13898]=x+1;C=s4(k,l,2)|0;Y=+(c[10032]|0);Z=+g[k>>2]/Y;h[30151]=Z;_=+g[l>>2]/Y;h[30152]=_;c[60306]=C;if((C|0)!=2){break}h[30151]=Z*2.54;h[30152]=_*2.54}else if((A|0)==19){c[13898]=x+1;C=is(j)|0;H=c[C>>2]|0;if((H|0)==3){$=+uz(c[C+8>>2]|0,0)}else if((H|0)==1){$=+(c[C+8>>2]|0)}else if((H|0)==2){$=+h[C+8>>3]}else{T=19603;break L26071}h[30156]=$}else if((A|0)==0){c[13898]=x+1;C=c[60308]|0;if((C|0)!=0){uu(C);c[60308]=0}uD(241208,241072,136)}else if((A|0)==25){uh(x,134168,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((A|0)==10){c[13898]=x+1;a[241236]=0;C=(c[3524]|0)+96|0;c[C>>2]=c[C>>2]|1024}else if((A|0)==22){c[13898]=x+1;c[60318]=0}else if((A|0)==17){c[13898]=x+1;c[60311]=1}else if((A|0)==2){c[13898]=x+1;h[30151]=5.0;h[30152]=3.0;c[60306]=1}else if((A|0)==3){c[13898]=x+1;a[241228]=0}else if((A|0)==26){c[13898]=x+1;a[241280]=0;h[30167]=12.0}else if((A|0)==8){c[13898]=x+1;C=c[60308]|0;if((C|0)==0){break}uu(C);c[60308]=0}else if((A|0)==14){c[13898]=x+1;c[60310]=1}else if((A|0)==13){c[13898]=x+1;c[60310]=0}else if((A|0)==6){c[13898]=x+1;a[241229]=0}else if((A|0)==20){c[13898]=x+1;C=is(j)|0;H=c[C>>2]|0;if((H|0)==1){aa=+(c[C+8>>2]|0)}else if((H|0)==3){aa=+uz(c[C+8>>2]|0,0)}else if((H|0)==2){aa=+h[C+8>>3]}else{T=19609;break L26071}h[30157]=aa}else if((A|0)==12){c[13898]=x+1;a[241237]=0}else if((A|0)==23){c[13898]=x+1;c[60318]=1}else if((A|0)==5){c[13898]=x+1;a[241229]=1}else if((A|0)==24){c[13898]=x+1;c[60319]=0}else if((A|0)==9){c[13898]=x+1;a[241236]=1;C=(c[3524]|0)+96|0;c[C>>2]=c[C>>2]&-1025}else if((A|0)==15){c[13898]=x+1;c[60310]=2}else{T=19639;break L26071}}while(0);A=c[13898]|0;z=c[8272]|0;if((A|0)<(z|0)){x=A;y=z}else{break L26069}}if((T|0)==19578){c[13898]=U;V=U;uf(V,134392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((T|0)==19639){uf(x,134056,(v=i,i=i+8|0,c[v>>2]=c[c[3524]>>2],v)|0)}else if((T|0)==19603){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((T|0)==19660){uf(V,134392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((T|0)==19662){uf(V,134392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((T|0)==19615){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((T|0)==19580){V=c[13898]|0;uf(V,134392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((T|0)==19609){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}}while(0);aa=+h[30167];h[30211]=aa;c[(c[3524]|0)+16>>2]=~~(aa*2540.0/72.27*+h[30158]+.5);T=c[3524]|0;c[T+20>>2]=~~(+((c[T+16>>2]|0)>>>0>>>0)*.4895+.5);aa=+h[30151];if((c[60306]|0)==1){c[(c[3524]|0)+8>>2]=~~(aa*2540.0+.5);c[(c[3524]|0)+12>>2]=~~(+h[30152]*2540.0+.5)}else{c[(c[3524]|0)+8>>2]=~~(aa*1.0e3+.5);c[(c[3524]|0)+12>>2]=~~(+h[30152]*1.0e3+.5)}T=(c[60306]|0)==1?145600:145440;aa=+h[30152];V=(a[241228]&1)!=0?133784:133640;U=(a[241229]&1)!=0?133504:133320;j=(c[60308]|0)==0?133016:132832;a0(13048,1025,133912,(v=i,i=i+56|0,h[v>>3]=+h[30151],c[v+8>>2]=T,h[v+16>>3]=aa,c[v+24>>2]=T,c[v+32>>2]=V,c[v+40>>2]=U,c[v+48>>2]=j,v)|0);if((c[60308]|0)!=0){j=1025-(uA(13048)|0)|0;ck(13048,132696,j|0);j=c[60308]|0;U=1025-(uA(13048)|0)|0;ck(13048,j|0,U|0);U=1025-(uA(13048)|0)|0;ck(13048,132584,U|0)}U=(a[241236]&1)!=0?132472:132384;ck(13048,U|0,1025-(uA(13048)|0)|0);U=c[60310]|0;if((U|0)==1){j=1025-(uA(13048)|0)|0;ck(13048,132112,j|0)}else if((U|0)==0){ck(13048,132272,1025-(uA(13048)|0)|0)}else if((U|0)==2){ck(13048,132032,1025-(uA(13048)|0)|0)}U=c[60311]|0;if((U|0)==0){j=1025-(uA(13048)|0)|0;ck(13048,137232,j|0)}else if((U|0)==2){ck(13048,137168,1025-(uA(13048)|0)|0)}else if((U|0)==1){ck(13048,131936,1025-(uA(13048)|0)|0)}aa=+h[30156];$=+h[30157];X=+h[30158];a0(n|0,1025,131664,(v=i,i=i+32|0,c[v>>2]=(a[241237]&1)!=0?131584:131504,h[v+8>>3]=aa,h[v+16>>3]=$,h[v+24>>3]=X,v)|0);ck(13048,n|0,1025-(uA(13048)|0)|0);U=c[60318]|0;if((U|0)==1){j=1025-(uA(13048)|0)|0;ck(13048,131408,j|0);ab=+h[30167];ac=a0(n|0,1025,131176,(v=i,i=i+16|0,c[v>>2]=241280,h[v+8>>3]=ab,v)|0)|0;ad=uA(13048)|0;ae=1025-ad|0;af=ck(13048,n|0,ae|0)|0;i=b;return}else if((U|0)==0){ck(13048,131240,1025-(uA(13048)|0)|0);ab=+h[30167];ac=a0(n|0,1025,131176,(v=i,i=i+16|0,c[v>>2]=241280,h[v+8>>3]=ab,v)|0)|0;ad=uA(13048)|0;ae=1025-ad|0;af=ck(13048,n|0,ae|0)|0;i=b;return}else{ab=+h[30167];ac=a0(n|0,1025,131176,(v=i,i=i+16|0,c[v>>2]=241280,h[v+8>>3]=ab,v)|0)|0;ad=uA(13048)|0;ae=1025-ad|0;af=ck(13048,n|0,ae|0)|0;i=b;return}}function sK(a){a=a|0;c[60468]=a;return 1}function sL(a){a=a|0;c[60416]=a;return 1}function sM(a){a=a|0;if((a|0)!=0){c[60410]=a}return 0}function sN(){var b=0,d=0,e=0,f=0,g=0.0,j=0.0;b=i;i=i+112|0;d=b|0;cG(d|0);c[60256]=0;c[60258]=0;c[60266]=0;a[241056]=0;c[60464]=0;c[60418]=0;cf(c[10030]|0,140680,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);do{if((a[241229]&1)!=0){e=b+8|0;if((bw(e|0,100,140512,aN(d|0)|0)|0)==0){break}f=c[10030]|0;cf(f|0,140320,(v=i,i=i+8|0,c[v>>2]=e,v)|0)}}while(0);aF(10,c[10030]|0);cf(c[10030]|0,139952,(v=i,i=i+40|0,c[v>>2]=40152,c[v+8>>2]=40160,c[v+16>>2]=240968,c[v+24>>2]=103960,c[v+32>>2]=240976,v)|0);cf(c[10030]|0,139760,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);if((a[241228]&1)==0){i=b;return}if((c[11252]|0)==15){d=c[10030]|0;aI(139560,25,1,d|0)}aI(139312,24,1,c[10030]|0);if((a[241236]&1)!=0){d=c[10030]|0;aI(139136,30,1,d|0)}d=c[60308]|0;if((d|0)!=0){e=c[10030]|0;cf(e|0,154696,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}g=+h[30167];if(g==12.0){j=g}else{d=c[10030]|0;cf(d|0,138800,(v=i,i=i+8|0,h[v>>3]=g,v)|0);j=+h[30167]}d=(a[241280]|0)!=0?148464:179864;cf(c[10030]|0,138568,(v=i,i=i+24|0,c[v>>2]=241280,c[v+8>>2]=d,h[v+16>>3]=j,v)|0);aI(138336,39,1,c[10030]|0);aI(138080,9,1,c[10030]|0);d=c[60310]|0;if((d|0)==1){e=c[10030]|0;aI(137776,7,1,e|0)}else if((d|0)==2){aI(137632,7,1,c[10030]|0)}else if((d|0)==0){aI(137912,7,1,c[10030]|0)}cf(c[10030]|0,137496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);aI(137328,12,1,c[10030]|0);d=c[60311]|0;if((d|0)==1){e=c[10030]|0;aI(137776,7,1,e|0)}else if((d|0)==2){aI(137168,7,1,c[10030]|0)}else if((d|0)==0){aI(137232,4,1,c[10030]|0)}cf(c[10030]|0,137008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,136744,(v=i,i=i+8|0,c[v>>2]=(a[241237]&1)!=0?136424:136208,v)|0);cf(c[10030]|0,136008,(v=i,i=i+8|0,h[v>>3]=+h[30156],v)|0);cf(c[10030]|0,135744,(v=i,i=i+8|0,h[v>>3]=+h[30157],v)|0);cf(c[10030]|0,135608,(v=i,i=i+8|0,h[v>>3]=+h[30158],v)|0);cf(c[10030]|0,135456,(v=i,i=i+8|0,c[v>>2]=(c[60318]|0)==0?135312:135192,v)|0);cf(c[10030]|0,134944,(v=i,i=i+8|0,c[v>>2]=(c[60418]|0)==0?134816:134640,v)|0);aI(134536,13,1,c[10030]|0);i=b;return}function sO(){var b=0;b=c[10030]|0;if((a[241228]&1)==0){aI(140840,10,1,b|0);return}else{aI(140968,10,1,b|0);return}}function sP(){var b=0;if((c[60266]|0)!=0){s2()}aI(141472,55,1,c[10030]|0);b=c[10030]|0;if((a[241228]&1)==0){aI(141112,20,1,b|0);return}else{aI(141320,17,1,b|0);return}}function sQ(){var b=0,d=0,e=0,f=0,g=0.0,j=0.0,k=0.0,l=0,m=0;b=i;d=(a[241228]&1)==0;e=c[10030]|0;f=(c[60464]|0)+1|0;c[60464]=f;if(d){cf(e|0,146344,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}else{cf(e|0,146528,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}cf(c[10030]|0,146160,(v=i,i=i+8|0,c[v>>2]=40152,v)|0);cf(c[10030]|0,145992,(v=i,i=i+8|0,c[v>>2]=240968,v)|0);g=+h[30151];f=(c[60306]|0)==1;j=f?2.54:1.0;k=+h[30152];e=f?145600:145440;cf(c[10030]|0,145792,(v=i,i=i+48|0,h[v>>3]=g*j,h[v+8>>3]=k*j,h[v+16>>3]=g,c[v+24>>2]=e,h[v+32>>3]=k,c[v+40>>2]=e,v)|0);e=c[10030]|0;if((c[60418]|0)==0){cf(e|0,145080,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{cf(e|0,144816,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}cf(c[10030]|0,144624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,144392,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,144624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,144136,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,144008,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,143912,(v=i,i=i+8|0,h[v>>3]=+h[30158],v)|0);cf(c[10030]|0,143832,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,143688,(v=i,i=i+8|0,h[v>>3]=+h[30204],v)|0);cf(c[10030]|0,143472,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,143328,(v=i,i=i+8|0,h[v>>3]=+h[30206],v)|0);cf(c[10030]|0,143184,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);cf(c[10030]|0,144624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);if(!(a[31104]|0)){c[60414]=-3;c[60256]=0;c[60258]=0;c[60266]=0;a[241056]=0;i=b;return}e=c[60410]|0;if((e|0)==0){c[60414]=-3;c[60256]=0;c[60258]=0;c[60266]=0;a[241056]=0;i=b;return}aI(143016,16,1,c[10030]|0);f=c[5163]|0;if((f|0)==103){d=c[10030]|0;aI(142832,16,1,d|0)}else if((f|0)==114){d=c[5165]|0;l=c[5166]|0;cf(c[10030]|0,142624,(v=i,i=i+24|0,c[v>>2]=c[5164],c[v+8>>2]=d,c[v+16>>2]=l,v)|0)}else if((f|0)==100){aI(142352,21,1,c[10030]|0);l=c[e+44>>2]|0;d=c[e+40>>2]|0;aI(142248,7,1,c[10030]|0);if((d|0)>0){e=0;do{if((e|0)>0){m=c[10030]|0;aF(44,m|0)}k=+h[l+(e<<5)+16>>3];g=+h[l+(e<<5)+24>>3];cf(c[10030]|0,142144,(v=i,i=i+24|0,h[v>>3]=+h[l+(e<<5)+8>>3],h[v+8>>3]=k,h[v+16>>3]=g,v)|0);e=e+1|0;}while((e|0)<(d|0));aI(142e3,12,1,c[10030]|0);e=0;do{if((e|0)>0){m=c[10030]|0;aF(44,m|0)}cf(c[10030]|0,141840,(v=i,i=i+8|0,h[v>>3]=+h[l+(e<<5)>>3],v)|0);e=e+1|0;}while((e|0)<(d|0))}else{aI(142e3,12,1,c[10030]|0)}aF(41,c[10030]|0)}else if((f|0)==102){aI(142512,21,1,c[10030]|0)}aI(150696,3,1,c[10030]|0);c[60414]=-3;c[60256]=0;c[60258]=0;c[60266]=0;a[241056]=0;i=b;return}function sR(a,b){a=a|0;b=b|0;if((c[60258]|0)==(a|0)&(c[60256]|0)==(b|0)){return}if((c[60266]|0)!=0){s2()}c[60258]=a;c[60256]=b;return}function sS(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0.0,m=0.0,n=0,o=0;e=i;f=c[60258]|0;g=c[60256]|0;j=c[60266]|0;k=(j|0)==0;if((f|0)==(b|0)&(g|0)==(d|0)){if(!k){i=e;return}a[241056]=1;i=e;return}if(k){a[241056]=0;c[60262]=f;c[60260]=g;c[60266]=2;k=c[10030]|0;l=+(f|0)*.001;m=+(g|0)*.001;cf(k|0,146712,(v=i,i=i+16|0,h[v>>3]=l,h[v+8>>3]=m,v)|0)}else{if((j&3|0)==2){j=c[10030]|0;aI(152656,3,1,j|0);c[60266]=2;n=c[60258]|0;o=c[60256]|0}else{n=f;o=g}g=c[10030]|0;m=+(n|0)*.001;l=+(o|0)*.001;cf(g|0,152248,(v=i,i=i+16|0,h[v>>3]=m,h[v+8>>3]=l,v)|0)}c[60258]=b;c[60256]=d;c[60266]=(c[60266]|0)+1;i=e;return}function sT(b){b=b|0;var d=0;d=i;do{if((c[60414]|0)==(b|0)){if(a[241864]|0){break}i=d;return}}while(0);if((c[60266]|0)!=0){s2()}cf(c[10030]|0,146984,(v=i,i=i+8|0,c[v>>2]=b,v)|0);c[60414]=b;a[241864]=0;i=d;return}function sU(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0;f=i;i=i+32|0;g=f|0;uD(g|0,240992,30);if((e|0)==0){i=f;return}if((a[e]|0)==0){i=f;return}if((c[60266]|0)!=0){s2()}j=c[60416]|0;if((j|0)==2){k=2}else if((j|0)==0){k=1}else{k=0}j=e;while(1){if((a[j]|0)!=32){break}j=j+1|0}cf(c[10030]|0,148128,(v=i,i=i+16|0,h[v>>3]=+(b>>>0>>>0)*.001,h[v+8>>3]=+(d>>>0>>>0)*.001,v)|0);d=c[60468]|0;if((d|0)!=0){b=c[10030]|0;cf(b|0,147984,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}cf(c[10030]|0,147776,(v=i,i=i+8|0,c[v>>2]=g+(k*10&-1),v)|0);if((a[241696]|0)!=0){k=c[10030]|0;cf(k|0,147528,(v=i,i=i+8|0,c[v>>2]=241696,v)|0)}cf(c[10030]|0,147256,(v=i,i=i+8|0,c[v>>2]=j,v)|0);i=f;return}function sV(a,b,d){a=a|0;b=b|0;d=d|0;var e=0;e=i;if((c[60266]|0)!=0){s2()}cf(c[10030]|0,148360,(v=i,i=i+24|0,h[v>>3]=+(a>>>0>>>0)*.001,h[v+8>>3]=+(b>>>0>>>0)*.001,c[v+16>>2]=d,v)|0);i=e;return}function sW(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;lD(a,b,c,d,e);return}function sX(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0.0,m=0;d=i;i=i+64|0;e=d|0;f=d+8|0;g=f|0;uE(g|0,0,51);h[e>>3]=0.0;j=bh(b|0,44)|0;if((j|0)==0){k=uA(b|0)|0;l=0.0}else{m=j+1|0;ca(m|0,148448,(v=i,i=i+8|0,c[v>>2]=e,v)|0);k=j-b|0;l=+h[e>>3]}h[30210]=l;e=k>>>0>50?50:k;uD(241800,b|0,e);a[e+241800|0]=0;uF(241696,241800,101);l=+h[30210];do{if(l>0.0){h[30211]=l;a0(g|0,51,148688,(v=i,i=i+8|0,h[v>>3]=l,v)|0);e=101-(uA(241696)|0)|0;ck(241696,g|0,e|0);a[f+50|0]=0}else{if(l!=0.0){break}h[30211]=+h[30167]}}while(0);c[(c[3524]|0)+16>>2]=~~(+h[30211]*2540.0/72.27*+h[30158]+.5);f=c[3524]|0;c[f+20>>2]=~~(+((c[f+16>>2]|0)>>>0>>>0)*.4895+.5);l=+h[30151];if((c[60306]|0)==1){c[(c[3524]|0)+8>>2]=~~(l*2540.0+.5);c[(c[3524]|0)+12>>2]=~~(+h[30152]*2540.0+.5);i=d;return 1}else{c[(c[3524]|0)+8>>2]=~~(l*1.0e3+.5);c[(c[3524]|0)+12>>2]=~~(+h[30152]*1.0e3+.5);i=d;return 1}return 0}function sY(a){a=+a;var b=0;b=i;if(+h[30204]==a){i=b;return}if((c[60266]|0)!=0){s2()}cf(c[10030]|0,148808,(v=i,i=i+8|0,h[v>>3]=a,v)|0);h[30204]=a;i=b;return}function sZ(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0;g=i;if((c[60266]|0)!=0){s2()}cf(c[10030]|0,148936,(v=i,i=i+32|0,h[v>>3]=+(e>>>0>>>0)*.001,h[v+8>>3]=+(f>>>0>>>0)*.001,h[v+16>>3]=+(b>>>0>>>0)*.001,h[v+24>>3]=+(d>>>0>>>0)*.001,v)|0);s3(a);i=g;return}function s_(a){a=+a;var b=0,d=0.0;b=i;d=a<0.0?1.0:a;if(+h[30206]==d){i=b;return}if((c[60266]|0)!=0){s2()}cf(c[10030]|0,149264,(v=i,i=i+8|0,h[v>>3]=d,v)|0);h[30206]=d;i=b;return}function s$(b){b=b|0;var d=0,e=0,f=0,g=0,j=0.0,k=0.0,l=0.0,m=0.0,n=0,o=0,p=0,q=0,r=0.0,s=0,t=0.0;d=i;i=i+24|0;e=d|0;if((c[60266]|0)!=0){s2()}f=c[b>>2]|0;if((f|0)==3){g=c[b+4>>2]|0;j=+(g>>>16&255|0)/255.0;k=+(g>>>8&255|0)/255.0;l=+(g&255|0)/255.0;g=c[10030]|0;cf(g|0,149808,(v=i,i=i+24|0,h[v>>3]=j,h[v+8>>3]=k,h[v+16>>3]=l,v)|0);a[241864]=1;i=d;return}else if((f|0)==5){l=+h[b+8>>3];k=l<0.0?0.0:l;l=k>1.0?1.0:k;cf(c[10030]|0,149664,(v=i,i=i+8|0,h[v>>3]=l,v)|0);g=c[5168]|0;L26414:do{if((g|0)==0){m=l}else{k=+O(+(+(g|0)*l))/+(g-1|0);if((c[5163]|0)!=100){m=k;break}n=c[5172]|0;if((n|0)<3&k==0.0){m=k;break}o=c[5173]|0;p=0;j=k;while(1){if((p|0)>=(n|0)){m=j;break L26414}k=+h[o+(p<<5)>>3];q=p+1|0;r=+h[o+(q<<5)>>3];s=r>l;do{if(k<=l&s){if(!(j<k|j>r)){t=j;break}t=(k+r)*.5}else{t=j}}while(0);if(s){m=t;break}else{p=q;j=t}}}}while(0);fq(m,e);m=+h[e+8>>3];t=+h[e+16>>3];cf(c[10030]|0,149808,(v=i,i=i+24|0,h[v>>3]=+h[e>>3],h[v+8>>3]=m,h[v+16>>3]=t,v)|0);a[241864]=1;i=d;return}else if((f|0)==1){cf(c[10030]|0,150024,(v=i,i=i+8|0,c[v>>2]=c[b+4>>2],v)|0);a[241864]=1;i=d;return}else{uh(-1,149512,(v=i,i=i+8|0,c[v>>2]=f,v)|0);i=d;return}}function s0(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0.0;d=i;if((a|0)<3){i=d;return}if((c[60266]|0)!=0){s2()}e=b|0;f=a-1|0;g=b+4|0;if((c[e>>2]|0)==(c[b+(f*12&-1)>>2]|0)){j=(c[g>>2]|0)==(c[b+(f*12&-1)+4>>2]|0)?f:a}else{j=a}aI(153544,5,1,c[10030]|0);k=+(c[g>>2]|0)*.001;cf(c[10030]|0,153096,(v=i,i=i+16|0,h[v>>3]=+(c[e>>2]|0)*.001,h[v+8>>3]=k,v)|0);if((j|0)>1){e=1;do{if((e&3|0)==0){g=c[10030]|0;aI(152656,3,1,g|0)}k=+(c[b+(e*12&-1)+4>>2]|0)*.001;cf(c[10030]|0,152248,(v=i,i=i+16|0,h[v>>3]=+(c[b+(e*12&-1)>>2]|0)*.001,h[v+8>>3]=k,v)|0);e=e+1|0;}while((e|0)<(j|0))}aI(151984,9,1,c[10030]|0);s3(c[b+8>>2]|0);i=d;return}function s1(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0.0,x=0.0,y=0.0,z=0,A=0,B=0,C=0.0,D=0,E=0.0,F=0,G=0.0,H=0,I=0;g=i;i=i+24|0;j=g|0;k=e+24|0;l=e|0;do{if((c[k>>2]|0)>(c[l>>2]|0)){m=19853}else{if((c[e+4>>2]|0)>(c[e+28>>2]|0)){m=19853;break}if((c[e+12>>2]|0)>(c[e+36>>2]|0)){m=19853;break}if((c[e+40>>2]|0)>(c[e+16>>2]|0)){m=19853}else{n=0}}}while(0);if((m|0)==19853){n=1}if((c[60418]|0)==1){i=g;return}do{if((f|0)==0){m=c[10030]|0;cf(m|0,159568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);m=aa(b,a);if((m|0)!=0){o=j|0;p=j+8|0;q=j+16|0;r=0;s=0;while(1){if((s|0)>15){t=c[10030]|0;cf(t|0,158976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);u=1}else{u=s+1|0}w=+h[d+(r<<3)>>3];t=c[5168]|0;L26467:do{if((t|0)==0){x=w}else{y=+O(+(w*+(t|0)))/+(t-1|0);if((c[5163]|0)!=100){x=y;break}z=c[5172]|0;if((z|0)<3&y==0.0){x=y;break}A=c[5173]|0;B=0;C=y;while(1){if((B|0)>=(z|0)){x=C;break L26467}y=+h[A+(B<<5)>>3];D=B+1|0;E=+h[A+(D<<5)>>3];F=E>w;do{if(y<=w&F){if(!(C<y|C>E)){G=C;break}G=(y+E)*.5}else{G=C}}while(0);if(F){x=G;break}else{B=D;C=G}}}}while(0);fq(x,j);t=~~(+h[p>>3]*255.0)&255;B=~~(+h[q>>3]*255.0)&255;cf(c[10030]|0,158504,(v=i,i=i+24|0,c[v>>2]=~~(+h[o>>3]*255.0)&255,c[v+8>>2]=t,c[v+16>>2]=B,v)|0);B=r+1|0;if(B>>>0<m>>>0){r=B;s=u}else{break}}}s=c[10030]|0;aI(157888,3,1,s|0)}else{s=(f|0)==2;r=s?4:3;m=c[10030]|0;cf(m|0,159568,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);m=aa(b,a);o=(m|0)==0;if(!o){q=0;p=0;while(1){if((p|0)>15){B=c[10030]|0;cf(B|0,158976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);H=1}else{H=p+1|0}B=aa(q,r);cf(c[10030]|0,155792,(v=i,i=i+8|0,c[v>>2]=~~(+h[d+(B<<3)>>3]*255.0)&255,v)|0);cf(c[10030]|0,155792,(v=i,i=i+8|0,c[v>>2]=~~(+h[d+(B+1<<3)>>3]*255.0)&255,v)|0);cf(c[10030]|0,155792,(v=i,i=i+8|0,c[v>>2]=~~(+h[d+(B+2<<3)>>3]*255.0)&255,v)|0);B=q+1|0;if(B>>>0<m>>>0){q=B;p=H}else{break}}}p=c[10030]|0;aI(157888,3,1,p|0);if(!s){break}p=c[10030]|0;cf(p|0,154920,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);if(!o){p=0;q=0;while(1){if((q|0)>47){B=c[10030]|0;cf(B|0,158976,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);I=1}else{I=q+1|0}B=c[10030]|0;t=~~(+h[d+(aa(p,r)+3<<3)>>3]*255.0)&255;cf(B|0,155792,(v=i,i=i+8|0,c[v>>2]=t,v)|0);t=p+1|0;if(t>>>0<m>>>0){p=t;q=I}else{break}}}q=c[10030]|0;aI(157888,3,1,q|0)}}while(0);if(n){I=c[10030]|0;aI(154584,14,1,I|0)}I=c[l>>2]|0;x=+((c[e+12>>2]|0)-I|0)*.001;l=c[e+16>>2]|0;G=+((c[e+4>>2]|0)-l|0)*.001;cf(c[10030]|0,154224,(v=i,i=i+48|0,c[v>>2]=b,c[v+8>>2]=a,h[v+16>>3]=x,h[v+24>>3]=G,h[v+32>>3]=+(I|0)*.001,h[v+40>>3]=+(l|0)*.001,v)|0);if(!n){i=g;return}n=c[k>>2]|0;k=c[e+40>>2]|0;G=+((c[e+28>>2]|0)-k|0)*.001;cf(c[10030]|0,153832,(v=i,i=i+32|0,h[v>>3]=+((c[e+36>>2]|0)-n|0)*.001,h[v+8>>3]=G,h[v+16>>3]=+(n|0)*.001,h[v+24>>3]=+(k|0)*.001,v)|0);i=g;return}function s2(){var b=0,d=0,e=0.0,f=0.0,g=0,j=0;b=i;if(a[241056]|0){d=c[10030]|0;e=+(c[60258]|0)*.001;f=+(c[60256]|0)*.001;cf(d|0,150512,(v=i,i=i+16|0,h[v>>3]=e,h[v+8>>3]=f,v)|0);a[241056]=0;c[60266]=0;i=b;return}d=c[60258]|0;g=c[60256]|0;j=c[10030]|0;if((d|0)==(c[60262]|0)&(g|0)==(c[60260]|0)){aI(150352,21,1,j|0);c[60266]=0;i=b;return}else{f=+(d|0)*.001;e=+(g|0)*.001;cf(j|0,150176,(v=i,i=i+16|0,h[v>>3]=f,h[v+8>>3]=e,v)|0);c[60266]=0;i=b;return}}function s3(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0.0;b=i;d=a>>4;e=(d|0)<0?0:d;f=(e|0)>100?100:e;aI(151736,9,1,c[10030]|0);e=a&15;if((e|0)==1){g=19900}else if((e|0)==5){aI(151568,12,1,c[10030]|0);g=19903}else if((e|0)==2){g=19903}else if((e|0)==4){aI(151568,12,1,c[10030]|0);g=19900}else if((e|0)!=3){aI(150840,11,1,c[10030]|0)}do{if((g|0)==19900){if((f|0)>=100){break}e=c[10030]|0;j=+(f|0)*.01;cf(e|0,151328,(v=i,i=i+8|0,h[v>>3]=j,v)|0)}else if((g|0)==19903){cf(c[10030]|0,151024,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}}while(0);aI(150696,3,1,c[10030]|0);i=b;return}function s4(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0,u=0,w=0,x=0,y=0,z=0,A=0.0,B=0.0,C=0,D=0.0,E=0,F=0,G=0.0,H=0.0,I=0,J=0.0;f=i;i=i+48|0;j=f|0;k=f+24|0;l=c[13898]|0;if((l|0)>=(c[8272]|0)){uf(l,131024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}m=c[1054]|0;L26528:do{if((a[m+(l*40&-1)|0]&1)!=0){n=c[m+(l*40&-1)+36>>2]|0;o=m+(l*40&-1)+32|0;p=c[10036]|0;q=0;while(1){if((q|0)>=(n|0)){break}if((a[p+((c[o>>2]|0)+q|0)|0]|0)==(a[q+103664|0]|0)){q=q+1|0}else{break L26528}}if((q|0)!=1){break}uf(l,131024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}}while(0);l=is(k)|0;m=c[l>>2]|0;if((m|0)==1){r=+(c[l+8>>2]|0)}else if((m|0)==3){r=+uz(c[l+8>>2]|0,0)}else if((m|0)==2){r=+h[l+8>>3]}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}l=k|0;if((c[l>>2]|0)==3){uu(c[k+8>>2]|0);c[l>>2]=1}s=r;g[b>>2]=s;l=c[13898]|0;k=c[1054]|0;m=c[k+(l*40&-1)+36>>2]|0;o=c[k+(l*40&-1)+32>>2]|0;p=(a[k+(l*40&-1)|0]&1)==0;L26545:do{if((m|0)>0&(p^1)){k=c[10036]|0;n=0;t=0;u=o;while(1){if((a[n+130912|0]|0)==(a[k+(n+u|0)|0]|0)){w=u;x=t}else{if((n|0)!=2){y=19929;break L26545}w=u-1|0;x=1}z=n+1|0;if((z|0)<(x+m|0)){n=z;t=x;u=w}else{break}}if((x|0)==0){if(!((n|0)==6|(n|0)==1)){y=19929;break}}c[13898]=l+1;A=+g[b>>2];y=19936}else{y=19929}}while(0);do{if((y|0)==19929){L26559:do{if((c[8272]|0)<=(l|0)|p){y=19935}else{x=c[10036]|0;w=0;while(1){if((w|0)>=(m|0)){break}if((a[x+(o+w|0)|0]|0)==(a[w+145440|0]|0)){w=w+1|0}else{y=19935;break L26559}}if((w|0)!=2){y=19935;break}c[13898]=l+1;B=+g[b>>2]}}while(0);if((y|0)==19935){if((e|0)==1){A=s;y=19936;break}else if((e|0)==2){B=s}else{C=e;break}}g[b>>2]=+(c[10032]|0)/2.54*B;C=2}}while(0);if((y|0)==19936){g[b>>2]=A*+(c[10032]|0);C=1}e=c[13898]|0;l=e+1|0;c[13898]=l;if((c[8272]|0)<=(e|0)){uf(l,131024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}o=c[1054]|0;if((a[o+(e*40&-1)|0]&1)==0){uf(l,131024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}m=c[o+(e*40&-1)+36>>2]|0;p=o+(e*40&-1)+32|0;e=c[10036]|0;o=0;while(1){if((o|0)>=(m|0)){break}if((a[e+((c[p>>2]|0)+o|0)|0]|0)==(a[o+148464|0]|0)){o=o+1|0}else{y=19976;break}}if((y|0)==19976){uf(l,131024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((o|0)!=1){uf(l,131024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}l=is(j)|0;o=c[l>>2]|0;if((o|0)==2){D=+h[l+8>>3]}else if((o|0)==3){D=+uz(c[l+8>>2]|0,0)}else if((o|0)==1){D=+(c[l+8>>2]|0)}else{uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}l=j|0;if((c[l>>2]|0)==3){uu(c[j+8>>2]|0);c[l>>2]=1}A=D;g[d>>2]=A;l=c[13898]|0;j=c[1054]|0;o=c[j+(l*40&-1)+36>>2]|0;p=c[j+(l*40&-1)+32>>2]|0;e=(a[j+(l*40&-1)|0]&1)==0;L26596:do{if((o|0)>0&(e^1)){j=c[10036]|0;m=0;n=0;x=p;while(1){if((a[m+130912|0]|0)==(a[j+(m+x|0)|0]|0)){E=x;F=n}else{if((m|0)!=2){y=19961;break L26596}E=x-1|0;F=1}u=m+1|0;if((u|0)<(F+o|0)){m=u;n=F;x=E}else{break}}if((F|0)==0){if(!((m|0)==6|(m|0)==1)){y=19961;break}}c[13898]=l+1;G=+g[d>>2];y=19968}else{y=19961}}while(0);do{if((y|0)==19961){L26610:do{if((c[8272]|0)<=(l|0)|e){y=19967}else{F=c[10036]|0;E=0;while(1){if((E|0)>=(o|0)){break}if((a[F+(p+E|0)|0]|0)==(a[E+145440|0]|0)){E=E+1|0}else{y=19967;break L26610}}if((E|0)!=2){y=19967;break}c[13898]=l+1;H=+g[d>>2]}}while(0);if((y|0)==19967){if((C|0)==1){G=A;y=19968;break}else if((C|0)==2){H=A}else{I=C;J=A;break}}D=+(c[10032]|0)/2.54*H;g[d>>2]=D;I=2;J=D}}while(0);if((y|0)==19968){H=G*+(c[10032]|0);g[d>>2]=H;I=1;J=H}if(+g[b>>2]<1.0|J<1.0){uf(c[13898]|0,130744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{i=f;return I|0}return 0}function s5(b,d,e,f,g,h){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0.0,s=0.0,t=0,u=0.0,w=0,x=0.0,y=0;j=i;k=e-b|0;l=f-d|0;if((e|0)==(b|0)){m=c[10030]|0;n=(h|0)!=0?101096:101088;o=l>>31|1;p=(l|0)>-1?l:-l|0;cf(m|0,101128,(v=i,i=i+40|0,c[v>>2]=e,c[v+8>>2]=d,c[v+16>>2]=n,c[v+24>>2]=o,c[v+32>>2]=p,v)|0);i=j;return}if((f|0)==(d|0)){p=c[10030]|0;o=(h|0)!=0?101096:101088;n=k>>31|1;m=(k|0)>-1?k:-k|0;cf(p|0,101032,(v=i,i=i+40|0,c[v>>2]=b,c[v+8>>2]=f,c[v+16>>2]=o,c[v+24>>2]=n,c[v+32>>2]=m,v)|0);i=j;return}q=+(l|0)/+(k|0);if(q<0.0){r=-0.0-q}else{r=q}q=0.0;m=233040;n=0;o=1;while(1){s=r- +(c[m+4>>2]|0)/+(o|0);if((n|0)==0){t=19990}else{if(s<0.0){u=-0.0-s}else{u=s}if(u<q){t=19990}else{w=n;x=q}}do{if((t|0)==19990){t=0;if(s>=0.0){w=m;x=s;break}w=m;x=-0.0-s}}while(0);p=m+8|0;y=c[p>>2]|0;if((y|0)==0){break}else{q=x;m=p;n=w;o=y}}if(x==0.0){o=c[10030]|0;n=(h|0)!=0?101096:101088;m=aa(c[w>>2]|0,k>>31|1);t=aa(c[w+4>>2]|0,l>>31|1);y=(k|0)>-1?k:-k|0;cf(o|0,100984,(v=i,i=i+48|0,c[v>>2]=b,c[v+8>>2]=d,c[v+16>>2]=n,c[v+24>>2]=m,c[v+32>>2]=t,c[v+40>>2]=y,v)|0);i=j;return}do{if(a[45024]|0){q9(0);if(!(a[233344]|0)){y=c[10030]|0;cf(y|0,100208,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0)}y=c[10030]|0;cf(y|0,100168,(v=i,i=i+16|0,c[v>>2]=e,c[v+8>>2]=f,v)|0);c[58302]=e;c[58300]=f;a[233344]=1}else{if((g|0)==1){q9(0);s6(b,e,d,f);break}if(a[239320]|0){y=c[10030]|0;aF(10,y|0);a[239320]=0}c[59764]=b;c[59762]=d;rr(e,f)}}while(0);if((h|0)==0){i=j;return}h=c[10030]|0;d=aa(c[w>>2]|0,k>>31|1);k=aa(c[w+4>>2]|0,l>>31|1);cf(h|0,100952,(v=i,i=i+32|0,c[v>>2]=e,c[v+8>>2]=f,c[v+16>>2]=d,c[v+24>>2]=k,v)|0);i=j;return}function s6(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var j=0,k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,P=0.0,Q=0.0,R=0.0,S=0.0,T=0.0,U=0.0,V=0.0,W=0.0,X=0.0,Y=0.0,Z=0.0,_=0.0,$=0.0,aa=0.0,ab=0.0,ac=0.0,ad=0.0;j=i;k=(b|0)==(d|0);l=(e|0)==(f|0);m=a[233344]|0;if(k&l){if(m){i=j;return}s7(2,0.0,0.0,0.0,0.0);n=c[10030]|0;cf(n|0,100920,(v=i,i=i+24|0,c[v>>2]=b,c[v+8>>2]=e,c[v+16>>2]=100896,v)|0);i=j;return}n=m&1;a[233344]=1;if(k){o=+(e|0);s7(n,+(b|0),o,+g[58368],+(f|0)-o);i=j;return}if(l){o=+(b|0);s7(n,o,+(e|0),+(d|0)-o,+g[58368]);i=j;return}o=+(d|0);p=+(b|0);q=o-p;r=+(f|0);s=+(e|0);t=r-s;u=t/q;n=u>=0.0;if(n){w=u}else{w=-0.0-u}if(w<=1.0){l=q>=0.0;if(l){x=q}else{x=-0.0-q}if(n){y=u}else{y=-0.0-u}w=+g[58368];z=w;if(x<(1.0/y+.25)*z){if(l){A=q}else{A=-0.0-q}B=A}else{if(n){C=u}else{C=-0.0-u}B=(1.0/C+.25)*z}C=B;k=t>=0.0;if(k){D=t}else{D=-0.0-t}m=w<D;if(m|k){E=m?w:t}else{E=-0.0-t}D=(k?1.0:-1.0)*E;E=t-D;B=C*.24089999999999998;if(l){F=q}else{F=-0.0-q}do{if(C==F){G=20046}else{k=E>=0.0;if(k){H=E}else{H=-0.0-E}if(H*2.0+.5<1.0){G=20046;break}if(k){I=E}else{I=-0.0-E}k=~~(I*2.0+.5);if((k|0)==1){G=20046;break}A=E/+(k|0);y=(q-(l?1.0:-1.0)*C)/+(k-1|0);m=c[10030]|0;x=y;if(y<0.0){J=p-C}else{J=p}K=J;if(A<0.0){L=-0.0-A}else{L=A}M=s-(L-D)*.5;N=A;A=B;O=L*.24089999999999998;cf(m|0,100624,(v=i,i=i+56|0,h[v>>3]=K,h[v+8>>3]=M,h[v+16>>3]=x,h[v+24>>3]=N,c[v+32>>2]=k,h[v+40>>3]=A,h[v+48>>3]=O,v)|0);P=y}}while(0);if((G|0)==20046){l=c[10030]|0;k=(d|0)>=(b|0)?b:d;L=(s+r-w)*.5;w=B;B=z*.24089999999999998;cf(l|0,100728,(v=i,i=i+32|0,c[v>>2]=k,h[v+8>>3]=L,h[v+16>>3]=w,h[v+24>>3]=B,v)|0);P=q}B=C*.5;C=q-B*(P>=0.0?1.0:-1.0);k=c[10030]|0;P=C;if(C<0.0){Q=p-B}else{Q=p}C=Q;Q=+g[58368];w=s-Q*.5;L=t;z=B*.24089999999999998;B=Q*.24089999999999998;cf(k|0,100464,(v=i,i=i+48|0,h[v>>3]=C,h[v+8>>3]=w,h[v+16>>3]=P,h[v+24>>3]=L,h[v+32>>3]=z,h[v+40>>3]=B,v)|0);a[233344]=0;i=j;return}k=t>=0.0;if(k){R=t}else{R=-0.0-t}if(n){S=u}else{S=-0.0-u}B=+g[58368];z=B;if(R<(S+.25)*z){if(k){T=t}else{T=-0.0-t}U=T}else{if(n){V=u}else{V=-0.0-u}U=(V+.25)*z}V=U;n=q>=0.0;if(n){W=q}else{W=-0.0-q}l=B<W;if(l|n){X=l?B:q}else{X=-0.0-q}W=(n?1.0:-1.0)*X;X=q-W;U=V*.24089999999999998;if(k){Y=t}else{Y=-0.0-t}do{if(V==Y){G=20079}else{n=X>=0.0;if(n){Z=X}else{Z=-0.0-X}if(Z*2.0+.5<1.0){G=20079;break}if(n){_=X}else{_=-0.0-X}n=~~(_*2.0+.5);if((n|0)==1){G=20079;break}u=X/+(n|0);T=(t-(k?1.0:-1.0)*V)/+(n-1|0);l=c[10030]|0;d=u>=0.0;if(d){$=u}else{$=-0.0-u}S=p-($-W)*.5;if(T<0.0){aa=s-V}else{aa=s}R=aa;L=u;P=T;if(d){ab=u}else{ab=-0.0-u}u=ab*.24089999999999998;w=U;cf(l|0,100624,(v=i,i=i+56|0,h[v>>3]=S,h[v+8>>3]=R,h[v+16>>3]=L,h[v+24>>3]=P,c[v+32>>2]=n,h[v+40>>3]=u,h[v+48>>3]=w,v)|0);ac=T}}while(0);if((G|0)==20079){G=c[10030]|0;ab=(p+o-B)*.5;k=(f|0)>=(e|0)?e:f;B=z*.24089999999999998;z=U;cf(G|0,100408,(v=i,i=i+32|0,h[v>>3]=ab,c[v+8>>2]=k,h[v+16>>3]=B,h[v+24>>3]=z,v)|0);ac=t}z=V*.5;V=t-z*(ac>=0.0?1.0:-1.0);ac=+g[58368];if(V<0.0){ad=s-z}else{ad=s}cf(c[10030]|0,100464,(v=i,i=i+48|0,h[v>>3]=p-ac*.5,h[v+8>>3]=ad,h[v+16>>3]=q,h[v+24>>3]=V,h[v+32>>3]=ac*.24089999999999998,h[v+40>>3]=z*.24089999999999998,v)|0);a[233344]=0;i=j;return}function s7(b,d,e,f,j){b=b|0;d=+d;e=+e;f=+f;j=+j;var k=0,l=0,m=0,n=0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0;k=i;l=(b|0)==2;do{if(l){if(a[233176]|0){m=20111}}else{if(f==0.0|j==0.0){i=k;return}n=a[233176]|0;if((b|0)!=1|n^1){if(n){m=20111;break}else{m=20118;break}}do{if((~~+g[58288]|0)==(~~d|0)){if(+g[58290]!=f){m=20105;break}o=+g[58292];if(o*j<0.0){break}g[58292]=o+j;i=k;return}else{m=20105}}while(0);do{if((m|0)==20105){if((~~+g[58286]|0)!=(~~e|0)){break}if(+g[58292]!=j){break}o=+g[58290];if(o*f<0.0){break}g[58290]=o+f;i=k;return}}while(0);if(n){m=20111}else{m=20118}}}while(0);do{if((m|0)==20111){o=+g[58290];if(o<0.0){g[58288]=o+ +g[58288];p=-0.0-o;g[58290]=p;q=p}else{q=o}o=+g[58292];if(o<0.0){g[58286]=o+ +g[58286];p=-0.0-o;g[58292]=p;r=p}else{r=o}o=+g[58368];b=c[10030]|0;p=+g[58288];s=+g[58286];if(q<o|r<o){cf(b|0,100384|0,(v=i,i=i+24|0,h[v>>3]=p,h[v+8>>3]=s,c[v+16>>2]=100896,v)|0);m=20118;break}else{t=(-0.0-o)*.24089999999999998*.5;o=q*.24089999999999998;u=r*.24089999999999998;cf(b|0,100288|0,(v=i,i=i+40|0,h[v>>3]=p,h[v+8>>3]=s,h[v+16>>3]=t,h[v+24>>3]=o,h[v+32>>3]=u,v)|0);m=20118;break}}}while(0);do{if((m|0)==20118){if(l){break}g[58288]=d;g[58286]=e;g[58290]=f;g[58292]=j;a[233176]=1;i=k;return}}while(0);a[233176]=0;i=k;return}function s8(a){a=a|0;var b=0,d=0;b=i;d=~~(+(a|0)*1.4142);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=d,v)|0);a=-d|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=0,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=0,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function s9(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=a,v)|0);d=-a|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=0,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=0,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function ta(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);d=-a|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function tb(a){a=a|0;var b=0,d=0;b=i;d=~~(+(a|0)/1.4142);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0);a=-d|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);aI(99872,14,1,c[10030]|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function tc(a){a=a|0;var b=0,d=0,e=0;b=i;d=~~(+(a|0)/1.6119);a=~~(+(d|0)*1.7321);e=-d<<1;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=e,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=-a,c[v+8>>2]=d,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=e,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function td(a){a=a|0;var b=0,d=0,e=0,f=0;b=i;d=(a|0)/2&-1;a=~~(+(d|0)*1.7321);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=d<<1,v)|0);e=-d|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=e<<1,v)|0);aI(99872,14,1,c[10030]|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);f=-a|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=e,v)|0);aI(99872,14,1,c[10030]|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=e,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function te(a){a=a|0;var b=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);i=b;return}function tf(a){a=a|0;var b=0,d=0,e=0,f=0,g=0;b=i;d=(a|0)/2&-1;a=~~(+(d|0)*1.7321);e=d<<1;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=e,v)|0);f=-a|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=d,v)|0);g=-d|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=f,c[v+8>>2]=g,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=g<<1,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=g,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=e,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function tg(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99984,(v=i,i=i+8|0,h[v>>3]=0.0,v)|0);d=(a|0)/2&-1;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0);i=b;return}function th(a){a=a|0;var b=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=-a,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function ti(a){a=a|0;var b=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=0,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=-a,c[v+8>>2]=0,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function tj(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=a,v)|0);d=-a|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=0,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=0,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function tk(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);d=~~(+(a|0)/1.4142);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0);a=-d|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);aI(99872,14,1,c[10030]|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function tl(a){a=a|0;var b=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);td(a);i=b;return}function tm(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99984,(v=i,i=i+8|0,h[v>>3]=1.0,v)|0);d=(a|0)/2&-1;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0);i=b;return}function tn(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99984,(v=i,i=i+8|0,h[v>>3]=1.0,v)|0);d=~~(+(~~(+(a|0)/1.5)|0)*1.4142);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=d,v)|0);a=-d|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=0,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=0,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function to(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99984,(v=i,i=i+8|0,h[v>>3]=1.0,v)|0);d=~~(+(a|0)/1.5);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0);a=-d|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function tp(a){a=a|0;var b=0,d=0,e=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99984,(v=i,i=i+8|0,h[v>>3]=1.0,v)|0);d=~~(+(~~(+(a|0)/1.5)|0)/1.6119);a=~~(+(d|0)*1.7321);e=-d<<1;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=e,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=-a,c[v+8>>2]=d,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=e,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function tq(a){a=a|0;var b=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99984,(v=i,i=i+8|0,h[v>>3]=1.0,v)|0);tf(~~(+(a|0)/1.2));i=b;return}function tr(a){a=a|0;var b=0,d=0;b=i;cf(c[10030]|0,99888,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=a,v)|0);d=-a|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=0,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=0,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=0,v)|0);aI(99872,14,1,c[10030]|0);d=~~(+(a|0)/1.4142);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=d,v)|0);a=-d|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=a,v)|0);aI(99872,14,1,c[10030]|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=d,c[v+8>>2]=a,v)|0);cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=d,v)|0);aI(99872,14,1,c[10030]|0);i=b;return}function ts(){var b=0,d=0,e=0,f=0.0,g=0;b=i;if(!(a[4192]|0)){i=b;return}if((c[918]|0)>0){d=0;do{e=-(c[32124+(d<<3)>>2]|0)|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=c[32120+(d<<3)>>2],c[v+8>>2]=e,v)|0);aI(99440,2,1,c[10030]|0);d=d+1|0;}while((d|0)<(c[918]|0))}d=(c[916]|0)+2|0;e=c[3684+(d*12&-1)>>2]|0;if((e|0)==1){f=+h[523]}else if((e|0)==3){f=+h[523]*3.0}else if((e|0)==2){f=+h[523]*2.0}else{f=1.0}e=c[3688+(d*12&-1)>>2]|0;if((e|0)==1){d=c[10030]|0;cf(d|0,99448,(v=i,i=i+8|0,h[v>>3]=f,v)|0)}else if((e|0)==3){cf(c[10030]|0,99480,(v=i,i=i+8|0,h[v>>3]=f,v)|0);if((c[918]|0)>0){d=0;do{g=-(c[32124+(d<<3)>>2]|0)|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=c[32120+(d<<3)>>2],c[v+8>>2]=g,v)|0);aI(99440,2,1,c[10030]|0);d=d+1|0;}while((d|0)<(c[918]|0))}cf(c[10030]|0,99448,(v=i,i=i+8|0,h[v>>3]=f*.5,v)|0)}else if((e|0)==0){aI(99872,14,1,c[10030]|0)}else if((e|0)==4){cf(c[10030]|0,99480,(v=i,i=i+8|0,h[v>>3]=f,v)|0);if((c[918]|0)>0){d=0;do{g=-(c[32124+(d<<3)>>2]|0)|0;cf(c[10030]|0,99784,(v=i,i=i+16|0,c[v>>2]=c[32120+(d<<3)>>2],c[v+8>>2]=g,v)|0);aI(99440,2,1,c[10030]|0);d=d+1|0;}while((d|0)<(c[918]|0))}cf(c[10030]|0,99448,(v=i,i=i+8|0,h[v>>3]=f/3.0,v)|0)}else if((e|0)==2){cf(c[10030]|0,99480,(v=i,i=i+8|0,h[v>>3]=f,v)|0)}a[4192]=0;i=b;return}function tt(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;b=i;if(!(a[46624]|0)){i=b;return}d=c[9372]|0;cf(c[10030]|0,84056,(v=i,i=i+16|0,c[v>>2]=25936,c[v+8>>2]=d,v)|0);d=(c[9372]|0)-1|0;e=c[10030]|0;if((d|0)>0){f=0;g=e;while(1){h=c[2264+(f<<2)>>2]|0;cf(g|0,84280,(v=i,i=i+16|0,c[v>>2]=c[2664+(f<<2)>>2],c[v+8>>2]=h,v)|0);h=f+1|0;if((h&7|0)==0){j=c[10030]|0;aI(84248,2,1,j|0)}j=(c[9372]|0)-1|0;k=c[10030]|0;if((h|0)<(j|0)){f=h;g=k}else{l=j;m=k;break}}}else{l=d;m=e}e=c[2264+(l<<2)>>2]|0;d=c[768]|0;g=c[774]|0;f=c[770]|0;cf(m|0,84016,(v=i,i=i+40|0,c[v>>2]=c[2664+(l<<2)>>2],c[v+8>>2]=e,c[v+16>>2]=d,c[v+24>>2]=g,c[v+32>>2]=f,v)|0);c[774]=(c[774]|0)+1;a[46624]=0;c[9372]=0;f=c[10030]|0;if((f|0)==0){i=b;return}aD(f|0);i=b;return}function tu(){var b=0,e=0,f=0,g=0,j=0,k=0,l=0;b=i;if(a[228352]|0){e=c[10030]|0;aI(78232,6,1,e|0);a[228352]=0}if(a[228344]|0){i=b;return}e=c[57144]|0;if((e|0)>12){f=(e|0)%13&-1}else{f=e}e=f+3|0;cf(c[10030]|0,75352,(v=i,i=i+8|0,c[v>>2]=227016+(((e|0)<0?0:e<<16>>16)<<4),v)|0);e=a[228528]|0;do{if(e<<24>>24==3){f=c[10030]|0;g=d[226992]|0;j=d[228368]|0;k=d[228536]|0;cf(f|0,75328,(v=i,i=i+24|0,c[v>>2]=g,c[v+8>>2]=j,c[v+16>>2]=k,v)|0)}else{k=c[10030]|0;if(e<<24>>24==1){j=c[57082]|0;aK(j|0,k|0);break}else{aI(75272,12,1,k|0);break}}}while(0);e=c[57144]|0;if((e|0)>12){l=(e|0)%13&-1}else{l=e}e=l+3|0;l=a[226984]|0;cf(c[10030]|0,75208,(v=i,i=i+24|0,h[v>>3]=+h[227008+(((e|0)<0?0:e<<16>>16)<<4)>>3]*+h[28540],c[v+8>>2]=l?131936:137232,c[v+16>>2]=l?131936:75184,v)|0);aI(75104,3,1,c[10030]|0);a[228344]=1;i=b;return}function tv(b,e){b=b|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0.0;f=i;g=a[227272]|0;do{if((c[56816]|0)>>>0>512){if(!g){j=20201;break}if(a[228352]|0){k=c[10030]|0;aI(78232,6,1,k|0);a[228352]=0}k=c[10030]|0;aI(78240,9,1,k|0);c[56816]=0;a[227272]=0;j=20201}else{if(!g){j=20201}}}while(0);if((j|0)==20201){if(a[228352]|0){g=c[10030]|0;aI(78232,6,1,g|0);a[228352]=0}g=c[10030]|0;aI(73816,7,1,g|0);g=a[228528]|0;if((g<<24>>24|0)==3){k=c[10030]|0;l=d[226992]|0;m=d[228368]|0;n=d[228536]|0;cf(k|0,73768,(v=i,i=i+24|0,c[v>>2]=l,c[v+8>>2]=m,c[v+16>>2]=n,v)|0)}else if((g<<24>>24|0)==1){cf(c[10030]|0,73752,(v=i,i=i+8|0,c[v>>2]=c[57082],v)|0)}g=c[57144]|0;do{if(a[228520]|0){n=(g|0)%5&-1;if((n|0)<=0){j=20209;break}m=c[10030]|0;l=c[228496+(n<<2)>>2]|0;cf(m|0,73720,(v=i,i=i+8|0,c[v>>2]=l,v)|0)}else{j=20209}}while(0);do{if((j|0)==20209){if((g|0)!=-1){break}l=c[10030]|0;cf(l|0,73720,(v=i,i=i+8|0,c[v>>2]=73656,v)|0)}}while(0);if(a[228360]|0){g=c[10030]|0;aI(73696,17,1,g|0)}g=c[10030]|0;aI(73688,4,1,g|0);a[227272]=1}o=+((c[(c[3524]|0)+12>>2]|0)-e|0)/10.0;cf(c[10030]|0,73840,(v=i,i=i+16|0,h[v>>3]=+(b>>>0>>>0)/10.0,h[v+8>>3]=o,v)|0);g=(c[56816]|0)+1|0;c[56816]=g;if((g&7|0)!=0){c[56740]=b;c[56736]=e;i=f;return}aI(73904,3,1,c[10030]|0);c[56740]=b;c[56736]=e;i=f;return}function tw(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0.0,j=0,k=0,l=0.0;f=i;g=+h[28552];uu(c[59466]|0);j=c[57108]|0;if((j|0)==0){k=0}else{k=bP(j|0)|0}c[59466]=k;if((a[37456]&1)!=0){a[237856]=0;qg(b,d,e);i=f;return}a[237856]=1;qg(b,d,e);a[237856]=0;if((aQ(e|0,74128)|0)!=0){a[237872]=1}c[59472]=0;h[5613]=1.0;uF(44912,216760,16);d=e;do{e=lJ(d,1,c[59466]|0,g,0.0,1,1,0)|0;if((a[e]|0)==0){break}cS[c[(c[3524]|0)+160>>2]&511]();if((a[e]|0)==125){uh(-1,203744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uh(-1,203528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=e+1|0;}while((a[d]|0)!=0);uu(c[57108]|0);c[57108]=c[59466];c[59466]=0;l=+h[29737];if(+h[28552]!=g|l!=0.0){d=c[10030]|0;cf(d|0,216688,(v=i,i=i+16|0,h[v>>3]=g,h[v+8>>3]=l,v)|0);h[28552]=g;h[29737]=0.0}a[237872]=0;aI(73992,14,1,c[10030]|0);i=f;return}function tx(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0;h=i;j=(a[(c[7118]|0)+16|0]&1)!=0?179864:74128;cf(c[10028]|0,212640,(v=i,i=i+8|0,c[v>>2]=j,v)|0);if((c[5163]|0)==103){k=c[10028]|0;cf(k|0,212480,(v=i,i=i+8|0,c[v>>2]=j,v)|0)}k=e|0;l=c[k>>2]|0;m=e+4|0;n=c[m>>2]|0;cf(c[10028]|0,212456,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=l,c[v+16>>2]=n,v)|0);n=(c[e+12>>2]|0)-(c[k>>2]|0)|0;k=(c[e+16>>2]|0)-(c[m>>2]|0)|0;cf(c[10028]|0,212400,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=n,c[v+16>>2]=k,v)|0);k=g&65535;cf(c[10028]|0,212384,(v=i,i=i+32|0,c[v>>2]=j,c[v+8>>2]=b,c[v+16>>2]=d,c[v+24>>2]=k,v)|0);cf(c[10028]|0,212328,(v=i,i=i+24|0,c[v>>2]=j,c[v+8>>2]=b,c[v+16>>2]=d,v)|0);g=c[10028]|0;if((a[(c[7118]|0)+16|0]&1)==0){aI(211992,36,1,g|0)}else{n=(aa(aa(aa(d,b),k),(f|0)==1?3:1)+7|0)>>>3;cf(g|0,212216,(v=i,i=i+8|0,c[v>>2]=n,v)|0);n=c[10028]|0;aI(212128,41,1,n|0)}n=c[10028]|0;if((f|0)==1){cf(n|0,211968,(v=i,i=i+16|0,c[v>>2]=j,c[v+8>>2]=j,v)|0);i=h;return}else{cf(n|0,211952,(v=i,i=i+8|0,c[v>>2]=j,v)|0);i=h;return}}function ty(){aK(208808,c[10028]|0);aK(208736,c[10028]|0);aK(208680,c[10028]|0);aK(208648,c[10028]|0);aK(208592,c[10028]|0);aK(208408,c[10028]|0);aK(208344,c[10028]|0);aK(208256,c[10028]|0);aK(208200,c[10028]|0);aK(208152,c[10028]|0);aK(208136,c[10028]|0);aK(208080,c[10028]|0);aK(208024,c[10028]|0);aK(207952,c[10028]|0);return}function tz(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;e=(aY(b|0,215352)|0)==0?77280:b;b=231040;while(1){f=c[b>>2]|0;if((f|0)==0){break}if((aY(c[f>>2]|0,e|0)|0)==0){g=20276;break}else{b=f+4|0}}if((g|0)==20276){i=d;return}if((cy(e|0,215312)|0)!=0){i=d;return}g=ut(8)|0;do{if((g|0)==0){gk();b=ut(8)|0;if((b|0)!=0){h=b;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=215288,v)|0)}else{h=g}}while(0);if((e|0)==0){j=0}else{j=bP(e|0)|0}e=h;c[e>>2]=j;c[h+4>>2]=c[57760];c[57760]=h;h=c[11252]|0;if((h|0)==5){k=214984}else if((h|0)==13){k=214760}else if((h|0)==9){k=214840}else if((h|0)==1|(h|0)==15){k=215232}else if((h|0)==10){k=214816}else if((h|0)==4){k=215016}else if((h|0)==2){k=215152}else if((h|0)==7){k=214920}else if((h|0)==6){k=214944}else if((h|0)==3|(h|0)==11){k=215072}else if((h|0)==12){k=214864}else{i=d;return}if(a[237912]|0){h=c[10028]|0;aI(215624,3,1,h|0);a[237912]=0}cf(c[10028]|0,214648,(v=i,i=i+16|0,c[v>>2]=c[e>>2],c[v+8>>2]=k,v)|0);i=d;return}function tA(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;g=i;cf(c[10028]|0,208848,(v=i,i=i+8|0,c[v>>2]=b,v)|0);if((e|0)<=0){j=c[10028]|0;k=aI(208840,6,1,j|0)|0;i=g;return}l=(uA(b|0)|0)+4|0;b=0;while(1){be(25336,141840,(v=i,i=i+8|0,h[v>>3]=+(~~(+h[(d+(b<<5)|0)+f>>3]*1.0e4+.5)|0)*1.0e-4,v)|0);m=(a[25336]|0)==48&(a[25337]|0)==46?25337:25336;n=(l+1|0)+(uA(m|0)|0)|0;if((n|0)>77){o=c[10028]|0;aI(152656,3,1,o|0);p=(uA(m|0)|0)+3|0}else{p=n}cf(c[10028]|0,73368,(v=i,i=i+8|0,c[v>>2]=m,v)|0);m=b+1|0;if((m|0)<(e|0)){l=p;b=m}else{break}}j=c[10028]|0;k=aI(208840,6,1,j|0)|0;i=g;return}function tB(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0.0;f=i;if((a[37456]&1)!=0){pO(b,d,e);i=f;return}if((a[e]|0)==0){i=f;return}if((c[7116]|0)!=0){j=c[10028]|0;aI(215952,7,1,j|0);c[7116]=0;a[228944]=0}pG(b,d);d=c[7230]|0;if((d|0)!=0){b=c[10028]|0;cf(b|0,199456,(v=i,i=i+8|0,c[v>>2]=d,v)|0)}aI(199448,2,1,c[10028]|0);h[5612]=-1.0e3;h[5611]=1.0e3;h[5613]=+g[(c[7118]|0)+92>>2]*10.0;uF(44912,199440,16);a[237912]=0;d=e;do{e=lJ(d,1,28840,+g[7208]*+g[(c[7118]|0)+92>>2]*10.0,0.0,1,1,0)|0;b=a[e]|0;if(b<<24>>24==0){break}if(a[237912]|0){j=c[10028]|0;aI(215624,3,1,j|0);a[237912]=0;k=a[e]|0}else{k=b}if(k<<24>>24==125){uh(-1,203744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uh(-1,203528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}d=e+1|0;}while((a[d]|0)!=0);l=+h[5611]+ +h[5612];h[5612]=l;cf(c[10028]|0,199384,(v=i,i=i+8|0,h[v>>3]=(-0.0-l)/3.0,v)|0);d=c[7198]|0;if((d|0)==1){k=c[10028]|0;aI(199288,7,1,k|0)}else if((d|0)==0){aI(199360,7,1,c[10028]|0)}else if((d|0)==2){aI(199240,7,1,c[10028]|0)}if((c[7230]|0)!=0){d=c[10028]|0;aI(212688,9,1,d|0)}c[7116]=0;a[228944]=0;i=f;return}function tC(b){b=b|0;var d=0,e=0,f=0.0,h=0.0,j=0,k=0.0;d=i;if((a[37456]&1)!=0){pL(b);i=d;return 1}if((a[b]|0)==0){e=(c[7118]|0)+36|0;uB(28840,e|0);f=+g[7202];g[7208]=f;h=f}else{e=bk(b|0,148464)|0;if((e|0)!=0&e>>>0<51){uF(28840,b|0,e|0);a[e+28840|0]=0;tz(28840)}g[7208]=+g[7202];j=b+(e+1|0)|0;ca(j|0,205104,(v=i,i=i+8|0,c[v>>2]=28832,v)|0);h=+g[7208]}f=+g[(c[7118]|0)+92>>2];k=h*f;c[(c[3524]|0)+16>>2]=~~(k*f*10.0);c[(c[3524]|0)+20>>2]=~~(k*+g[(c[7118]|0)+92>>2]*10.0*6.0/10.0);i=d;return 1}function tD(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0;e=i;i=i+520|0;f=e|0;g=e+256|0;if((b|0)==0){i=e;return}h=g|0;a[h]=0;j=e+264|0;a[j]=0;if(d){k=c[10028]|0;l=b+4|0;n=c[l>>2]|0;cf(k|0,197960,(v=i,i=i+8|0,c[v>>2]=n,v)|0);o=l}else{o=b+4|0}l=c[o>>2]|0;do{if((a[l]|0)!=60){n=uA(l|0)|0;if(n>>>0>3){k=l+(n-3|0)|0;uB(h|0,k|0)}else{uB(h|0,l|0)}a[h]=uI(a[h]|0|0)&255;k=g+1|0;a[k]=uI(a[k]|0|0)&255;k=g+2|0;a[k]=uI(a[k]|0|0)&255;if((c[b+8>>2]|0)!=0){break}uf(-1,219072,(v=i,i=i+8|0,c[v>>2]=c[o>>2],v)|0)}}while(0);L27092:do{if((a[h]|0)==0){if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=(c[o>>2]|0)+1|0;uB(j|0,g|0);g=bt(j|0,193632)|0;if((g|0)!=0){p=g;q=1;break}uf(-1,219160,(v=i,i=i+8|0,c[v>>2]=(c[o>>2]|0)+1,v)|0)}else{do{if((aY(h|0,197944)|0)!=0){if((aY(h|0,197896)|0)==0){break}if((aY(h|0,197648)|0)!=0){if((aY(h|0,197488)|0)!=0){uh(-1,197360,(v=i,i=i+8|0,c[v>>2]=c[o>>2],v)|0)}g=bF(c[b+8>>2]|0,193632)|0;if((g|0)!=0){p=g;q=0;break L27092}uf(-1,219072,(v=i,i=i+8|0,c[v>>2]=c[o>>2],v)|0)}if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=bU(197624)|0;l=c[b+8>>2]|0;if((g|0)==0){be(j|0,197560,(v=i,i=i+8|0,c[v>>2]=l,v)|0)}else{be(j|0,g|0,(v=i,i=i+8|0,c[v>>2]=l,v)|0)}if((a[j]|0)==0){uf(-1,197496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=bt(j|0,193632)|0;if((l|0)!=0){p=l;q=1;break L27092}uf(-1,197672,(v=i,i=i+8|0,c[v>>2]=j,v)|0)}}while(0);if(!(a[14168]|0)){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=bU(197856)|0;g=c[b+8>>2]|0;if((l|0)==0){be(j|0,197784,(v=i,i=i+8|0,c[v>>2]=g,v)|0)}else{be(j|0,l|0,(v=i,i=i+8|0,c[v>>2]=g,v)|0)}if((a[j]|0)==0){uf(-1,197704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}g=bt(j|0,193632)|0;if((g|0)!=0){p=g;q=1;break}uf(-1,197672,(v=i,i=i+8|0,c[v>>2]=j,v)|0)}}while(0);h=f|0;L27133:do{if((a6(h|0,255,p|0)|0)==0){r=0;s=0;t=20384}else{g=f+1|0;l=b+12|0;k=b+8|0;n=0;u=0;L27135:while(1){do{if((u|0)==0){if((a_(h|0,197328,14)|0)==0){break}if((a_(h|0,197304,11)|0)==0){break}if(q){uh(-1,197240,(v=i,i=i+8|0,c[v>>2]=j,v)|0);break}else{uh(-1,197192,(v=i,i=i+8|0,c[v>>2]=c[o>>2],v)|0);break}}}while(0);do{if((a_(h|0,197176,9)|0)==0){w=(uA(h|0)|0)-9|0;x=ut(w)|0;if((x|0)==0){gk();y=ut(w)|0;if((y|0)==0){break L27135}else{z=y}}else{z=x}x=(a8(g|0,47)|0)+1|0;uB(z|0,x|0);a[a8(z|0,32)|0]=0;if((z|0)==0){A=0}else{A=bP(z|0)|0}c[l>>2]=A;if(d){B=z;t=20382;break}x=c[k>>2]|0;y=c[m>>2]|0;w=c[o>>2]|0;if((x|0)==0){cf(y|0,197016,(v=i,i=i+16|0,c[v>>2]=w,c[v+8>>2]=z,v)|0)}else{cf(y|0,197104,(v=i,i=i+24|0,c[v>>2]=w,c[v+8>>2]=z,c[v+16>>2]=x,v)|0)}if(q){C=z;t=20381}else{D=z;E=u;t=20387;break L27133}}else{C=n;t=20381}}while(0);if((t|0)==20381){t=0;if(d){B=C;t=20382}else{F=C}}if((t|0)==20382){t=0;x=c[10028]|0;aK(h|0,x|0);F=B}x=u+1|0;if((a6(h|0,255,p|0)|0)==0){r=F;s=x;t=20384;break L27133}else{n=F;u=x}}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=197160,v)|0)}}while(0);do{if((t|0)==20384){if(!q){D=r;E=s;t=20387;break}F=a2(p|0)|0;if((F|0)==0){G=r;H=s;break}uf(-1,196840,(v=i,i=i+16|0,c[v>>2]=j,c[v+8>>2]=F,v)|0)}}while(0);if((t|0)==20387){az(p|0);G=D;H=E}if((H|0)==0){if(q){uf(-1,218792,(v=i,i=i+8|0,c[v>>2]=j,v)|0)}else{uf(-1,218744,(v=i,i=i+8|0,c[v>>2]=c[o>>2],v)|0)}}do{if(d){o=c[10028]|0;aI(196784,13,1,o|0);if((G|0)==0){i=e;return}if((aY(G|0,196752)|0)!=0){break}o=c[10028]|0;aI(196648,32,1,o|0);o=c[10028]|0;aI(196568,57,1,o|0);o=c[10028]|0;aI(196488,70,1,o|0);o=c[10028]|0;aI(196456,31,1,o|0);o=c[10028]|0;aI(196784,13,1,o|0);t=20396}else{t=20396}}while(0);do{if((t|0)==20396){if((G|0)!=0){break}i=e;return}}while(0);uu(G);i=e;return}function tE(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0;d=i;e=bP(195896|0)|0;f=c[57238]|0;if((f|0)==0){g=20410}else{h=uA(f|0)|0;f=(h+4|0)+(uA(b|0)|0)|0;h=ut(f)|0;do{if((h|0)==0){gk();j=ut(f)|0;if((j|0)!=0){k=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=195784,v)|0);return 0}else{k=h}}while(0);h=c[57238]|0;uB(k|0,h|0);h=uA(k|0)|0;f=(h|0)==0?0:h-1|0;h=a[k+f|0]|0;if(!((h<<24>>24|0)==47|(h<<24>>24|0)==0)){a[k+(f+1|0)|0]=47;a[k+(f+2|0)|0]=0}uC(k|0,b|0);f=bF(k|0,193632|0)|0;uu(k);if((f|0)==0){g=20410}else{l=f}}do{if((g|0)==20410){f=bU(195768|0)|0;if((f|0)!=0){k=uA(f|0)|0;h=(k+4|0)+(uA(b|0)|0)|0;k=ut(h)|0;do{if((k|0)==0){gk();j=ut(h)|0;if((j|0)!=0){n=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=195784,v)|0);return 0}else{n=k}}while(0);uB(n|0,f|0);k=uA(n|0)|0;h=(k|0)==0?0:k-1|0;k=a[n+h|0]|0;if(!((k<<24>>24|0)==47|(k<<24>>24|0)==0)){a[n+(h+1|0)|0]=47;a[n+(h+2|0)|0]=0}uC(n|0,b|0);h=bF(n|0,193632|0)|0;uu(n);if((h|0)!=0){l=h;break}}h=uA(e|0)|0;k=(h+4|0)+(uA(b|0)|0)|0;h=ut(k)|0;do{if((h|0)==0){gk();j=ut(k)|0;if((j|0)!=0){o=j;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=195784,v)|0);return 0}else{o=h}}while(0);uB(o|0,e|0);h=uA(o|0)|0;k=(h|0)==0?0:h-1|0;h=a[o+k|0]|0;if(!((h<<24>>24|0)==47|(h<<24>>24|0)==0)){a[o+(k+1|0)|0]=47;a[o+(k+2|0)|0]=0}uC(o|0,b|0);k=bF(o|0,193632|0)|0;uu(o);uu(e);if((k|0)!=0){p=k;i=d;return p|0}k=hA(b,193632)|0;if((k|0)==0){h=c[m>>2]|0;cf(h|0,195704|0,(v=i,i=i+8|0,c[v>>2]=b,v)|0);uq(2,0);h=c[m>>2]|0;cf(h|0,195600|0,(v=i,i=i+8|0,c[v>>2]=b,v)|0);h=c[m>>2]|0;aI(195528|0,49,1,h|0);h=c[m>>2]|0;aI(195448|0,34,1,h|0);uf(-1,195432,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{p=k;i=d;return p|0}}}while(0);uu(e);p=l;i=d;return p|0}function tF(){var b=0,d=0,e=0,f=0,j=0,k=0.0,l=0,m=0,n=0;b=i;if(!(a[34544]&a[34552])){i=b;return}d=c[8618]|0;if((d|0)==34464){i=b;return}else{e=1;f=d}while(1){j=e+1|0;d=c[f+8>>2]|0;if((d|0)==34464){break}else{e=j;f=d}}if((e|0)<=0){i=b;return}cf(c[10030]|0,183280,(v=i,i=i+32|0,c[v>>2]=(c[8566]|0)+1,c[v+8>>2]=34336,c[v+16>>2]=34272,c[v+24>>2]=34192,v)|0);cf(c[10030]|0,183648,(v=i,i=i+8|0,c[v>>2]=j,v)|0);e=0;f=34464;while(1){if((e&3|0)==0){d=c[10030]|0;aI(73904,3,1,d|0)}k=+g[f+4>>2];cf(c[10030]|0,183624,(v=i,i=i+16|0,h[v>>3]=+g[f>>2],h[v+8>>3]=k,v)|0);d=e+1|0;if((d|0)<(j|0)){e=d;f=c[f+8>>2]|0}else{break}}aI(183600,4,1,c[10030]|0);f=(c[8566]|0)+1|0;L27243:do{if(a[34544]&a[34552]){e=0;while(1){if((e|0)>=20){break L27243}l=34564+(e<<3)|0;j=c[l>>2]|0;m=(j|0)==0;n=(j|0)==(f|0);if(m|n){break}else{e=e+1|0}}j=34560+(e<<3)|0;if(m){c[l>>2]=f;c[j>>2]=0;break}if(!n){break}c[j>>2]=1}}while(0);a[34336]=0;a[34272]=0;a[34192]=0;n=c[8619]|0;g[8616]=+g[n>>2];g[8617]=+g[n+4>>2];if((n|0)!=34464){f=n;do{n=c[f+12>>2]|0;c[8619]=n;c[n+8>>2]=34464;uu(f);f=c[8619]|0;}while((f|0)!=34464)}c[8619]=34464;c[8618]=34464;i=b;return}function tG(){var a=0,b=0,d=0,e=0,f=0,g=0;aF(230,c[10030]|0);a=c[58576]|0;aF(a>>>8|0,c[10030]|0);aF(a|0,c[10030]|0);if((c[58576]|0)==0){c[58576]=0;b=c[10030]|0;d=aF(234,b|0)|0;e=c[10030]|0;f=aF(15,e|0)|0;return}else{g=0}do{a=c[233888+(g<<2)>>2]|0;aF(a>>>8|0,c[10030]|0);aF(a|0,c[10030]|0);a=c[233488+(g<<2)>>2]|0;aF(a>>>8|0,c[10030]|0);aF(a|0,c[10030]|0);g=g+1|0;}while(g>>>0<(c[58576]|0)>>>0);c[58576]=0;b=c[10030]|0;d=aF(234,b|0)|0;e=c[10030]|0;f=aF(15,e|0)|0;return}function tH(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0.0,l=0,m=0.0,n=0.0,o=0,p=0.0,q=0,r=0.0,s=0,t=0,u=0;f=i;g=~~(+h[29420]*106.0*.5);if((e|0)==(-3|0)){o1(b,d);j=c[10030]|0;aI(125976,2,1,j|0);a[235520]=0;j=c[10030]|0;k=+(g|0)*3.0*.25;cf(j|0,179248,(v=i,i=i+8|0,h[v>>3]=k,v)|0);i=f;return}else if((e|0)==(-15|0)){j=b-g|0;l=d-g|0;o1(j,l);k=+(b>>>0>>>0);m=+(g|0);n=m*4.242640687119286*.125;o=~~(k-n);p=+(d>>>0>>>0);q=~~(p-n);o2(o,q);r=m*1.4142135623730951*.5;o1(~~(k+r),~~(p-r));s=~~(k+n);o2(s,q);q=g+b|0;t=g+d|0;o1(q,t);u=~~(p+n);o2(s,u);o1(~~(k-r),~~(p+r));o2(o,u);o1(j,d);j=(g*3&-1|0)/4&-1;o2(b-j|0,d);o1(j+b|0,d);o2(q,d);o1(b,l);o2(b,d-j|0);o1(b,j+d|0);o2(b,t);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179192,(v=i,i=i+8|0,h[v>>3]=m*3.0*.25,v)|0);i=f;return}else if((e|0)==(-7|0)){m=+(b>>>0>>>0);r=+(g|0);p=r*1.4142135623730951*.5;t=~~(m-p);k=+(d>>>0>>>0);j=~~(k-p);o1(t,j);n=r*4.242640687119286*.125;l=~~(m-n);q=~~(k-n);o2(l,q);u=~~(m+p);o1(u,j);j=~~(m+n);o2(j,q);q=~~(k+p);o1(u,q);u=~~(k+n);o2(j,u);o1(t,q);o2(l,u);o1(b-g|0,d);u=(g*3&-1|0)/4&-1;o2(b-u|0,d);o1(u+b|0,d);o2(g+b|0,d);o1(b,d-g|0);o2(b,d-u|0);o1(b,u+d|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179192,(v=i,i=i+8|0,h[v>>3]=r*3.0*.25,v)|0);i=f;return}else if((e|0)==(-12|0)){r=+(b>>>0>>>0);n=+(g|0);k=n*1.4142135623730951*.5;p=+(d>>>0>>>0);u=~~(p+k);o1(~~(r+k),u);m=n*4.242640687119286*.125;l=~~(p+m);o2(~~(r+m),l);o1(~~(r-k),u);o2(~~(r-m),l);o1(b-g|0,d);l=(g*3&-1|0)/4&-1;o2(b-l|0,d);o1(l+b|0,d);o2(g+b|0,d);o1(b,d-g|0);o2(b,d-l|0);o1(b,l+d|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;m=n*3.0*.25;cf(c[10030]|0,178032,(v=i,i=i+8|0,h[v>>3]=m,v)|0);cf(c[10030]|0,178008,(v=i,i=i+8|0,h[v>>3]=m,v)|0);i=f;return}else if((e|0)==(-19|0)){l=d-g|0;o1(b,l);u=(g|0)/2&-1;o2(b,d-u|0);o1(b,u+d|0);o2(b,g+d|0);o1(b,l);u=(g|0)/4&-1;q=d-((g*3&-1|0)/4&-1)|0;o2(b-u|0,q);o1(b,l);o2(u+b|0,q);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=+(g|0)*.5,v)|0);i=f;return}else if((e|0)==(-16|0)){q=b-g|0;o1(q,d-g|0);m=+(b>>>0>>>0);n=+(g|0);r=n*4.242640687119286*.125;u=~~(m-r);k=+(d>>>0>>>0);o2(u,~~(k-r));l=g+b|0;t=g+d|0;o1(l,t);j=~~(k+r);o2(~~(m+r),j);r=n*1.4142135623730951*.5;o1(~~(m-r),~~(k+r));o2(u,j);o1(q,d);q=(g*3&-1|0)/4&-1;o2(b-q|0,d);o1(q+b|0,d);o2(l,d);o1(b,q+d|0);o2(b,t);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179192,(v=i,i=i+8|0,h[v>>3]=n*3.0*.25,v)|0);i=f;return}else if((e|0)==(-10|0)){o1(b-g|0,d);t=(g*3&-1|0)/4&-1;o2(b-t|0,d);o1(t+b|0,d);o2(g+b|0,d);o1(b,d-g|0);o2(b,d-t|0);o1(b,t+d|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;n=+(g|0)*3.0*.25;cf(c[10030]|0,178032,(v=i,i=i+8|0,h[v>>3]=n,v)|0);cf(c[10030]|0,178008,(v=i,i=i+8|0,h[v>>3]=n,v)|0);i=f;return}else if((e|0)==(-2|0)){o1(b-g|0,d);t=(g*3&-1|0)/4&-1;q=b-t|0;o2(q,d);l=t+b|0;o1(l,d);o2(g+b|0,d);o1(b,d-g|0);j=d-t|0;o2(b,j);u=t+d|0;o1(b,u);o2(b,g+d|0);o1(q,j);o2(l,j);o2(l,u);o2(q,u);o2(q,j);j=(g|0)/2&-1;q=j+d|0;o1(b-j|0,q);o2(b,d-j|0);o2(j+b|0,q);i=f;return}else if((e|0)==(-20|0)){o1(b-g|0,d-g|0);o2(g+b|0,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=+(g|0)*3.0*.25,v)|0);i=f;return}else if((e|0)==(-13|0)){o1(b-g|0,d-g|0);n=+(b>>>0>>>0);r=+(g|0);k=r*4.242640687119286*.125;m=+(d>>>0>>>0);o2(~~(n-k),~~(m-k));o1(g+b|0,g+d|0);o2(~~(n+k),~~(m+k));o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179192,(v=i,i=i+8|0,h[v>>3]=r*3.0*.25,v)|0);i=f;return}else if((e|0)==(-14|0)){q=b-g|0;j=d-g|0;o1(q,j);r=+(b>>>0>>>0);k=+(g|0);m=k*4.242640687119286*.125;u=~~(r-m);n=+(d>>>0>>>0);l=~~(n-m);o2(u,l);p=k*1.4142135623730951*.5;o1(~~(r+p),~~(n-p));t=~~(r+m);o2(t,l);l=g+b|0;o=g+d|0;o1(l,o);s=~~(n+m);o2(t,s);o1(~~(r-p),~~(n+p));o2(u,s);o1(q,d);q=(g*3&-1|0)/4&-1;o2(b-q|0,d);o1(q+b|0,d);o2(l,d);o1(b,j);o2(b,d-q|0);o1(b,q+d|0);o2(b,o);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=k*3.0*.25,v)|0);i=f;return}else if((e|0)==(-4|0)){o1(b-g|0,d);o=(g*3&-1|0)/4&-1;o2(b-o|0,d);o1(o+b|0,d);o2(g+b|0,d);o1(b,d-g|0);o2(b,d-o|0);o1(b,o+d|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=+(g|0)*3.0*.25,v)|0);i=f;return}else if((e|0)==(-6|0)){k=+(b>>>0>>>0);p=+(g|0);n=p*1.4142135623730951*.5;o=~~(k-n);r=+(d>>>0>>>0);q=~~(r-n);o1(o,q);m=p*4.242640687119286*.125;j=~~(k-m);l=~~(r-m);o2(j,l);s=~~(k+n);o1(s,q);q=~~(k+m);o2(q,l);l=~~(r+n);o1(s,l);s=~~(r+m);o2(q,s);o1(o,l);o2(j,s);o1(b-g|0,d);s=(g*3&-1|0)/4&-1;o2(b-s|0,d);o1(s+b|0,d);o2(g+b|0,d);o1(b,d-g|0);o2(b,d-s|0);o1(b,s+d|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=p*3.0*.25,v)|0);i=f;return}else if((e|0)==(-5|0)){o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179192,(v=i,i=i+8|0,h[v>>3]=+(g|0)*3.0*.25,v)|0);i=f;return}else if((e|0)==(-9|0)){p=+(b>>>0>>>0);m=+(g|0);r=m*1.4142135623730951*.5;s=~~(p-r);n=+(d>>>0>>>0);j=~~(n-r);o1(s,j);k=m*4.242640687119286*.125;l=~~(p-k);o=~~(n-k);o2(l,o);q=~~(p+r);o1(q,j);j=~~(p+k);o2(j,o);o=~~(n+r);o1(q,o);q=~~(n+k);o2(j,q);o1(s,o);o2(l,q);o1(b-g|0,d);q=(g*3&-1|0)/4&-1;o2(b-q|0,d);o1(q+b|0,d);o2(g+b|0,d);o1(b,d-g|0);o2(b,d-q|0);o1(b,q+d|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;k=m*3.0*.25;cf(c[10030]|0,178032,(v=i,i=i+8|0,h[v>>3]=k,v)|0);cf(c[10030]|0,178008,(v=i,i=i+8|0,h[v>>3]=k,v)|0);i=f;return}else if((e|0)==(-18|0)){o1(b-g|0,d);o2(g+b|0,d);o1(b,d-g|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=+(g|0)*3.0*.25,v)|0);i=f;return}else if((e|0)==(-17|0)){q=b-g|0;l=d-g|0;o1(q,l);k=+(b>>>0>>>0);m=+(g|0);n=m*4.242640687119286*.125;o=~~(k-n);r=+(d>>>0>>>0);s=~~(r-n);o2(o,s);p=m*1.4142135623730951*.5;o1(~~(k+p),~~(r-p));j=~~(k+n);o2(j,s);s=g+b|0;u=g+d|0;o1(s,u);t=~~(r+n);o2(j,t);o1(~~(k-p),~~(r+p));o2(o,t);o1(q,d);q=(g*3&-1|0)/4&-1;o2(b-q|0,d);o1(q+b|0,d);o2(s,d);o1(b,l);o2(b,d-q|0);o1(b,q+d|0);o2(b,u);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;p=m*3.0*.25;cf(c[10030]|0,178032,(v=i,i=i+8|0,h[v>>3]=p,v)|0);cf(c[10030]|0,178008,(v=i,i=i+8|0,h[v>>3]=p,v)|0);i=f;return}else if((e|0)==(-8|0)){p=+(b>>>0>>>0);m=+(g|0);r=m*1.4142135623730951*.5;k=+(d>>>0>>>0);u=~~(k+r);o1(~~(p+r),u);n=m*4.242640687119286*.125;q=~~(k+n);o2(~~(p+n),q);o1(~~(p-r),u);o2(~~(p-n),q);o1(b-g|0,d);q=(g*3&-1|0)/4&-1;o2(b-q|0,d);o1(q+b|0,d);o2(g+b|0,d);o1(b,q+d|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179192,(v=i,i=i+8|0,h[v>>3]=m*3.0*.25,v)|0);i=f;return}else if((e|0)==(-11|0)){m=+(b>>>0>>>0);n=+(g|0);p=n*1.4142135623730951*.5;r=+(d>>>0>>>0);e=~~(r+p);o1(~~(m+p),e);k=n*4.242640687119286*.125;q=~~(r+k);o2(~~(m+k),q);o1(~~(m-p),e);o2(~~(m-k),q);o1(b-g|0,d);q=(g*3&-1|0)/4&-1;o2(b-q|0,d);o1(q+b|0,d);o2(g+b|0,d);o1(b,d-g|0);o2(b,d-q|0);o1(b,q+d|0);o2(b,g+d|0);o1(b,d);aI(125976,2,1,c[10030]|0);a[235520]=0;cf(c[10030]|0,179248,(v=i,i=i+8|0,h[v>>3]=n*3.0*.25,v)|0);i=f;return}else{o1(b,d);o2(b,d);i=f;return}}function tI(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;b=i;d=c[59128]|0;e=(c[d+8>>2]|0)-1|0;f=c[(c[d+16>>2]|0)+(e<<2)>>2]|0;g=c[(c[d+20>>2]|0)+(e<<2)>>2]|0;tJ(d);d=c[59128]|0;e=c[d>>2]|0;h=c[d+4>>2]|0;j=h<<1;k=c[d+12>>2]|0;d=ut(28)|0;do{if((d|0)==0){gk();l=ut(28)|0;if((l|0)!=0){m=l;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{m=d}}while(0);d=m;l=m;c[l>>2]=e;c[m+12>>2]=k;c[m+4>>2]=j;c[m+8>>2]=0;k=h<<3;h=ut(k)|0;do{if((h|0)==0){gk();e=ut(k)|0;if((e|0)!=0){n=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{n=h}}while(0);h=m+16|0;c[h>>2]=n;n=ut(k)|0;do{if((n|0)==0){gk();e=ut(k)|0;if((e|0)!=0){o=e;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{o=n}}while(0);n=m+20|0;c[n>>2]=o;o=ut(j)|0;do{if((o|0)==0){gk();k=ut(j)|0;if((k|0)!=0){p=k;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=174680,v)|0)}else{p=o}}while(0);c[m+24>>2]=p;do{if((c[h>>2]|0)!=0){if((c[n>>2]|0)==0){break}uE(p|0,0,j|0);if((m|0)==0){break}o=c[59128]|0;c[236448+(c[l>>2]<<2)>>2]=d;if((c[l>>2]|0)==0){c[59113]=d;c[59115]=d}c[59128]=d;uu(c[o+16>>2]|0);uu(c[o+20>>2]|0);uu(c[o+24>>2]|0);uu(o)}}while(0);c[c[(c[59128]|0)+16>>2]>>2]=f;c[c[(c[59128]|0)+20>>2]>>2]=g;a[c[(c[59128]|0)+24>>2]|0]=1;c[(c[59128]|0)+8>>2]=1;c[59100]=(c[59100]|0)+1;i=b;return}function tJ(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;d=i;e=b+8|0;if((c[e>>2]|0)==0){i=d;return}f=c[b+16>>2]|0;g=c[b+20>>2]|0;h=c[b+24>>2]|0;if(a[236440]|0){j=c[10030]|0;aI(174600,4,1,j|0);a[236440]=0}j=b+12|0;b=c[j>>2]|0;do{if((c[59108]|0)!=(b|0)){if((c[e>>2]|0)<=1){if((a[h]&1)!=0){break}}k=c[10030]|0;cf(k|0,174584,(v=i,i=i+8|0,c[v>>2]=b,v)|0);c[59108]=c[j>>2]}}while(0);aI(174520,3,1,c[10030]|0);j=(c[f>>2]|0)-(c[59098]|0)|0;b=(c[g>>2]|0)-(c[59096]|0)|0;k=c[10030]|0;if((j|0)>-17&(b|0)>-17&(j|0)<16&(b|0)<16){aF(106,k|0);l=1}else{aF(105,k|0);l=0}k=c[e>>2]|0;if((k|0)>0){m=b;b=j;j=0;n=l;l=k;while(1){k=(j|0)>0;if(k){o=j-1|0;p=(c[f+(j<<2)>>2]|0)-(c[f+(o<<2)>>2]|0)|0;q=(c[g+(j<<2)>>2]|0)-(c[g+(o<<2)>>2]|0)|0}else{p=b;q=m}L27354:do{if((p|q|0)==0){do{if(k){if((a[h+(j-1|0)|0]&1)!=0){break}if((a[h+j|0]&1)!=0){break}c[59102]=(c[59102]|0)+1;r=n;s=p;t=q;break L27354}}while(0);if((a[h+j|0]&1)==0){u=p;w=q;x=20552;break}c[59104]=(c[59104]|0)+1;r=n;s=p;t=q}else{do{if(k){o=j+1|0;if((o|0)>=(l|0)){break}if((a[h+j|0]&1)==0){break}if((a[h+o|0]&1)==0){break}c[59104]=(c[59104]|0)+1;r=n;s=p;t=q;break L27354}}while(0);if((p|0)>-17&(q|0)>-17&(p|0)<16&(q|0)<16|k^1){u=p;w=q;x=20552;break}o=j+2|0;if((o|0)>=(l|0)){u=p;w=q;x=20552;break}if((a[h+j|0]&1)==0){u=p;w=q;x=20552;break}y=j+1|0;if((a[h+y|0]&1)!=0){u=p;w=q;x=20552;break}if((a[h+o|0]&1)==0){u=p;w=q;x=20552;break}o=f+(y<<2)|0;z=c[o>>2]|0;A=j-1|0;B=f+(A<<2)|0;C=z-(c[B>>2]|0)|0;if((C|0)<=-17){u=p;w=q;x=20552;break}D=g+(y<<2)|0;y=g+(A<<2)|0;A=(c[D>>2]|0)-(c[y>>2]|0)|0;if(!((A|0)>-17&(C|0)<16&(A|0)<16)){u=p;w=q;x=20552;break}A=f+(j<<2)|0;c[o>>2]=c[A>>2];c[A>>2]=z;z=c[D>>2]|0;o=g+(j<<2)|0;c[D>>2]=c[o>>2];c[o>>2]=z;u=(c[A>>2]|0)-(c[B>>2]|0)|0;w=z-(c[y>>2]|0)|0;x=20552}}while(0);do{if((x|0)==20552){x=0;if((u|0)>-17&(w|0)>-17&(u|0)<16&(w|0)<16){if(!n){k=c[10030]|0;aF(106,k|0)}if((a[h+j|0]&1)!=0){k=c[10030]|0;aF(97,k|0)}k=a[236552+(u+16|0)|0]|0;y=c[10030]|0;aF(k|0,y|0);y=a[236552+(w+16|0)|0]|0;k=c[10030]|0;aF(y|0,k|0);r=1;s=u;t=w;break}k=((u|0)>0?1:-1)+u|0;y=(k|0)/2&-1;z=((w|0)>0?1:-1)+w|0;B=(z|0)/2&-1;if(n&(k|0)>-34&(z|0)>-34&(k|0)<32&(z|0)<32){z=h+j|0;if((a[z]&1)!=0){k=c[10030]|0;aF(97,k|0)}k=a[236552+(y+16|0)|0]|0;A=c[10030]|0;aF(k|0,A|0);A=a[236552+(B+16|0)|0]|0;k=c[10030]|0;aF(A|0,k|0);if((a[z]&1)!=0){z=c[10030]|0;aF(97,z|0)}z=a[236552+((u+16|0)-y|0)|0]|0;y=c[10030]|0;aF(z|0,y|0);y=a[236552+((w+16|0)-B|0)|0]|0;B=c[10030]|0;aF(y|0,B|0);r=1;s=u;t=w;break}else{if(n){B=c[10030]|0;aF(105,B|0)}if((a[h+j|0]&1)!=0){B=c[10030]|0;aF(97,B|0)}B=f+(j<<2)|0;y=a[236520+(c[B>>2]>>5)|0]|0;z=c[10030]|0;aF(y|0,z|0);z=a[236520+(c[B>>2]&31)|0]|0;B=c[10030]|0;aF(z|0,B|0);B=g+(j<<2)|0;z=a[236520+(c[B>>2]>>5)|0]|0;y=c[10030]|0;aF(z|0,y|0);y=a[236520+(c[B>>2]&31)|0]|0;B=c[10030]|0;aF(y|0,B|0);r=0;s=u;t=w;break}}}while(0);B=j+1|0;y=c[e>>2]|0;if((B|0)<(y|0)){m=t;b=s;j=B;n=r;l=y}else{break}}}aF(90,c[10030]|0);c[59098]=c[f+((c[e>>2]|0)-1<<2)>>2];c[59096]=c[g+((c[e>>2]|0)-1<<2)>>2];c[e>>2]=0;i=d;return}function tK(b){b=b|0;var d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0;d=i;if((b|0)!=1){a[237104]=0;i=d;return}b=c[59356]|0;e=a[237432]&1;f=c[59354]|0;j=c[59352]|0;k=c[59350]|0;l=c[59346]|0;m=a[237392]<<31>>31;n=+g[59360];o=c[59280]|0;cf(c[10030]|0,174184,(v=i,i=i+128|0,c[v>>2]=2,c[v+8>>2]=e,c[v+16>>2]=b,c[v+24>>2]=f,c[v+32>>2]=j,c[v+40>>2]=k,c[v+48>>2]=l,c[v+56>>2]=0,c[v+64>>2]=m,h[v+72>>3]=n,c[v+80>>2]=0,c[v+88>>2]=(b|0)==2&1,c[v+96>>2]=0,c[v+104>>2]=0,c[v+112>>2]=0,c[v+120>>2]=o,v)|0);do{if((c[59280]|0)>0){o=0;b=0;while(1){m=c[59282]|0;l=c[m+(b<<3)+4>>2]|0;cf(c[10030]|0,174136,(v=i,i=i+16|0,c[v>>2]=c[m+(b<<3)>>2],c[v+8>>2]=l,v)|0);l=o+1|0;do{if((o|0)>4){if((b|0)==((c[59280]|0)-1|0)){p=l;break}m=c[10030]|0;aI(84248,2,1,m|0);p=0}else{p=l}}while(0);l=b+1|0;if((l|0)<(c[59280]|0)){o=p;b=l}else{break}}if((p|0)==0){break}aF(10,c[10030]|0)}}while(0);uu(c[59282]|0);c[59282]=0;a[237104]=0;i=d;return}function tL(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0.0;d=i;i=i+8|0;e=d|0;f=e;c[11554]=b;if((b|0)>-1){g=(b|0)%5&-1}else{g=b}b=(g|0)==-1?2:g;g=(b|0)>0&a[46232];j=e;k=f+3|0;a[k]=0;l=f+2|0;a[l]=0;m=f+1|0;a[m]=0;a[j]=37;aI(j|0,1,4,c[10030]|0);a[k]=0;a[l]=0;a[m]=0;a[j]=12;aI(j|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[k]=-128;a[l]=0;a[m]=0;a[j]=7;aI(j|0,1,4,c[10030]|0);a[k]=0;a[l]=0;a[m]=0;a[j]=40;aI(j|0,1,4,c[10030]|0);a[k]=0;a[l]=0;a[m]=0;a[j]=12;aI(j|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[k]=0;a[l]=0;a[m]=0;a[j]=1;aI(j|0,1,4,c[10030]|0);a[k]=0;a[l]=0;a[m]=0;a[j]=38;aI(j|0,1,4,c[10030]|0);a[k]=0;a[l]=0;a[m]=0;a[j]=28;aI(j|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[k]=0;a[l]=0;a[m]=0;a[j]=1;aI(j|0,1,4,c[10030]|0);f=a[45616]|0;a[k]=0;a[l]=0;a[m]=f?0:34;a[j]=0;aI(j|0,1,4,c[10030]|0);f=~~(+h[5705]*26.37);a[k]=f>>>24&255;a[l]=f>>>16&255;a[m]=f>>>8&255;a[j]=f&255;aI(j|0,1,4,c[10030]|0);c[e>>2]=0;aI(j|0,1,4,c[10030]|0);e=c[11562]|0;a[k]=e>>>24&255;a[l]=e>>>16&255;a[m]=e>>>8&255;a[j]=e&255;aI(j|0,1,4,c[10030]|0);a[k]=0;a[l]=0;a[m]=0;a[j]=37;aI(j|0,1,4,c[10030]|0);a[k]=0;a[l]=0;a[m]=0;a[j]=12;aI(j|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[k]=0;a[l]=0;a[m]=0;a[j]=1;aI(j|0,1,4,c[10030]|0);j=(c[3524]|0)+60|0;if(g){c[j>>2]=78;g=b<<3;n=+h[5778];c[11282]=~~(+(c[238880+(g-8<<2)>>2]|0)*n*26.37);b=~~(+(c[238880+(g-7<<2)>>2]|0)*n*26.37);c[11283]=b;c[11284]=~~(+(c[238880+(g-6<<2)>>2]|0)*n*26.37);c[11285]=~~(+(c[238880+(g-5<<2)>>2]|0)*n*26.37);c[11286]=~~(+(c[238880+(g-4<<2)>>2]|0)*n*26.37);c[11287]=~~(+(c[238880+(g-3<<2)>>2]|0)*n*26.37);c[11288]=~~(+(c[238880+(g-2<<2)>>2]|0)*n*26.37);c[11289]=~~(+(c[238880+(g-1<<2)>>2]|0)*n*26.37);c[11292]=b;c[11290]=1;c[11552]=0;i=d;return}else{c[j>>2]=48;c[11552]=0;i=d;return}}function tM(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,ab=0;d=i;e=c[3524]|0;if((c[e+8>>2]|0)>>>0>a>>>0){if((c[e+12>>2]|0)>>>0<=b>>>0){f=20596}}else{f=20596}if((f|0)==20596){uh(-1,171568,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0)}e=c[11298]|0;g=a-e|0;h=c[11296]|0;j=b-h|0;k=(g|0)>-1?g:-g|0;l=j*10&-1;m=(l|0)>-1?l:-l|0;l=k*10&-1;do{if((l|0)<(m|0)){n=((((aa((k*25&-1|0)/(m|0)&-1,k)|0)/6&-1)*5&-1)+((m|0)/2&-1)|0)/5&-1}else{if((k|0)==0){i=d;return}else{n=(aa((m|0)/(k|0)&-1,(m|0)/24&-1)+l|0)/10&-1;break}}}while(0);l=c[11292]|0;m=c[11290]|0;if((n|0)>(l|0)){k=n;o=l;l=m;p=e;q=h;r=e;s=h;while(1){t=k-o|0;u=a-((aa(t,g)|0)/(n|0)&-1)|0;w=b-((aa(t,j)|0)/(n|0)&-1)|0;x=c[3524]|0;y=(c[x+8>>2]|0)>>>0>u>>>0;do{if((l&1|0)==0){if(y){if((c[x+12>>2]|0)>>>0>w>>>0){z=w;A=u;B=p;C=q;D=r;E=s}else{f=20614}}else{f=20614}if((f|0)==20614){f=0;uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=u,c[v+8>>2]=w,v)|0);F=c[3524]|0;G=c[F+8>>2]|0;H=c[F+12>>2]|0;F=c[11298]|0;I=c[11296]|0;z=H>>>0>w>>>0?w:H;A=G>>>0>u>>>0?u:G;B=F;C=I;D=F;E=I}if((A|0)==(B|0)&(z|0)==(C|0)){J=B;K=C;L=D;M=E;break}tN();c[11298]=A;c[11296]=z;J=A;K=z;L=A;M=z}else{if(y){if((c[x+12>>2]|0)>>>0>w>>>0){N=r;O=s}else{f=20605}}else{f=20605}if((f|0)==20605){f=0;uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=u,c[v+8>>2]=w,v)|0);N=c[11298]|0;O=c[11296]|0}if((N|0)==(u|0)&(O|0)==(w|0)){J=u;K=w;L=u;M=w;break}I=c[11560]|0;if((I<<1|0)>102){tN();P=c[11560]|0}else{P=I}if((P|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;Q=1}else{Q=P}I=Q<<1;c[45200+(I<<2)>>2]=u;c[11298]=u;c[45200+((I|1)<<2)>>2]=w;c[11296]=w;c[11560]=Q+1;J=u;K=w;L=u;M=w}}while(0);w=(c[11290]|0)+1|0;u=(w|0)>7?0:w;c[11290]=u;w=c[45128+(u<<2)>>2]|0;c[11292]=w;if((t|0)>(w|0)){k=t;o=w;l=u;p=J;q=K;r=L;s=M}else{R=t;S=J;T=K;U=u;break}}}else{R=n;S=e;T=h;U=m}m=c[3524]|0;h=(c[m+8>>2]|0)>>>0>a>>>0;do{if((U&1|0)==0){if(h){if((c[m+12>>2]|0)>>>0>b>>>0){V=b;W=a;X=S;Y=T}else{f=20630}}else{f=20630}if((f|0)==20630){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);e=c[3524]|0;n=c[e+8>>2]|0;K=c[e+12>>2]|0;V=K>>>0>b>>>0?b:K;W=n>>>0>a>>>0?a:n;X=c[11298]|0;Y=c[11296]|0}if((W|0)==(X|0)&(V|0)==(Y|0)){break}tN();c[11298]=W;c[11296]=V}else{if(h){if((c[m+12>>2]|0)>>>0>b>>>0){Z=S;_=T}else{f=20621}}else{f=20621}if((f|0)==20621){uh(-1,171144,(v=i,i=i+16|0,c[v>>2]=a,c[v+8>>2]=b,v)|0);Z=c[11298]|0;_=c[11296]|0}if((Z|0)==(a|0)&(_|0)==(b|0)){break}n=c[11560]|0;if((n<<1|0)>102){tN();$=c[11560]|0}else{$=n}if(($|0)==0){c[11300]=c[11298];c[11301]=c[11296];c[11560]=1;ab=1}else{ab=$}n=ab<<1;c[45200+(n<<2)>>2]=a;c[11298]=a;c[45200+((n|1)<<2)>>2]=b;c[11296]=b;c[11560]=ab+1}}while(0);c[11292]=(c[11292]|0)-R;i=d;return}function tN(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;b=i;i=i+8|0;d=b|0;e=c[11560]|0;if((e|0)==0){i=b;return}if((c[11552]|0)>0){tL(c[11554]|0);c[11552]=0;f=c[11560]|0}else{f=e}e=d|0;g=d+3|0;a[g]=0;h=d+2|0;a[h]=0;j=d+1|0;a[j]=0;a[e]=27;aI(e|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[e]=16;aI(e|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;d=c[11300]|0;a[g]=d>>>24&255;a[h]=d>>>16&255;a[j]=d>>>8&255;a[e]=d&255;aI(e|0,1,4,c[10030]|0);d=(c[(c[3524]|0)+12>>2]|0)-(c[11301]|0)|0;a[g]=d>>>24&255;a[h]=d>>>16&255;a[j]=d>>>8&255;a[e]=d&255;aI(e|0,1,4,c[10030]|0);do{if((f|0)<3){a[g]=0;a[h]=0;a[j]=0;a[e]=54;d=c[10030]|0;aI(e|0,1,4,d|0);a[g]=0;a[h]=0;a[j]=0;a[e]=16;d=c[10030]|0;aI(e|0,1,4,d|0);c[11294]=(c[11294]|0)+1;d=c[11302]|0;a[g]=d>>>24&255;a[h]=d>>>16&255;a[j]=d>>>8&255;a[e]=d&255;d=c[10030]|0;aI(e|0,1,4,d|0);d=(c[(c[3524]|0)+12>>2]|0)-(c[11303]|0)|0;a[g]=d>>>24&255;a[h]=d>>>16&255;a[j]=d>>>8&255;a[e]=d&255;d=c[10030]|0;aI(e|0,1,4,d|0)}else{if((c[11560]<<1|0)>2){k=2}else{break}while(1){a[g]=0;a[h]=0;a[j]=0;a[e]=54;aI(e|0,1,4,c[10030]|0);a[g]=0;a[h]=0;a[j]=0;a[e]=16;aI(e|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;d=c[45200+(k<<2)>>2]|0;a[g]=d>>>24&255;a[h]=d>>>16&255;a[j]=d>>>8&255;a[e]=d&255;aI(e|0,1,4,c[10030]|0);d=k+2|0;l=(c[(c[3524]|0)+12>>2]|0)-(c[45200+((k|1)<<2)>>2]|0)|0;a[g]=l>>>24&255;a[h]=l>>>16&255;a[j]=l>>>8&255;a[e]=l&255;aI(e|0,1,4,c[10030]|0);if((d|0)<(c[11560]<<1|0)){k=d}else{break}}}}while(0);c[11560]=0;i=b;return}function tO(){var b=0,d=0,e=0,f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0;b=i;i=i+8|0;d=b|0;e=d;f=i;i=i+32|0;if(!(a[45664]|0)){i=b;return}j=uA(45688)|0;k=j>>>0<31?j:31;j=aQ(45688,75096)|0;if((j|0)==0){l=aQ(45688,75048)|0;if((l|0)==0){m=1;n=-112;o=k}else{p=l;q=20651}}else{p=j;q=20651}if((q|0)==20651){j=p-45688|0;m=2;n=-68;o=(j|0)<(k|0)?j:k}k=aQ(45688,74992)|0;if((k|0)==0){j=aQ(45688,74976)|0;if((j|0)==0){r=0;s=o}else{t=j;q=20654}}else{t=k;q=20654}if((q|0)==20654){k=t-45688|0;r=1;s=(k|0)<(o|0)?k:o}o=aQ(45688,171536)|0;if((o|0)==0){k=aQ(45688,171480)|0;if((k|0)==0){u=0;v=s}else{w=k;q=20657}}else{w=o;q=20657}if((q|0)==20657){o=w-45688|0;u=1;v=(o|0)<(s|0)?o:s}s=aQ(45688,171448)|0;do{if((s|0)==0){o=aQ(45688,171424)|0;if((o|0)!=0){x=o;q=20661;break}o=aQ(45688,171408)|0;if((o|0)==0){y=0;z=v}else{x=o;q=20661}}else{x=s;q=20661}}while(0);if((q|0)==20661){q=x-45688|0;y=1;z=(q|0)<(v|0)?q:v}v=f|0;q=z+1|0;uF(v|0,45688,q|0);if((uA(45688)|0)>>>0>=q>>>0){a[f+((q|0)==0?0:z)|0]=0}z=d;q=e+3|0;a[q]=0;x=e+2|0;a[x]=0;s=e+1|0;a[s]=0;a[z]=37;aI(z|0,1,4,c[10030]|0);a[q]=0;a[x]=0;a[s]=0;a[z]=12;aI(z|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[q]=-128;a[x]=0;a[s]=0;a[z]=10;aI(z|0,1,4,c[10030]|0);a[q]=0;a[x]=0;a[s]=0;a[z]=40;aI(z|0,1,4,c[10030]|0);a[q]=0;a[x]=0;a[s]=0;a[z]=12;aI(z|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[q]=0;a[x]=0;a[s]=0;a[z]=2;aI(z|0,1,4,c[10030]|0);a[q]=0;a[x]=0;a[s]=0;a[z]=82;aI(z|0,1,4,c[10030]|0);a[q]=0;a[x]=0;a[s]=1;a[z]=76;aI(z|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[q]=0;a[x]=0;a[s]=0;a[z]=2;aI(z|0,1,4,c[10030]|0);e=~~(+h[5710]*(-0.0- +g[11418])*35.28);a[q]=e>>>24&255;a[x]=e>>>16&255;a[s]=e>>>8&255;a[z]=e&255;aI(z|0,1,4,c[10030]|0);c[d>>2]=0;aI(z|0,1,4,c[10030]|0);e=c[11262]|0;a[q]=e>>>24&255;a[x]=e>>>16&255;a[s]=e>>>8&255;a[z]=e&255;aI(z|0,1,4,c[10030]|0);e=c[11262]|0;a[q]=e>>>24&255;a[x]=e>>>16&255;a[s]=e>>>8&255;a[z]=e&255;aI(z|0,1,4,c[10030]|0);a[q]=0;a[x]=0;a[s]=m;a[z]=n;aI(z|0,1,4,c[10030]|0);aF(r|0,c[10030]|0);aF(u|0,c[10030]|0);aF(y|0,c[10030]|0);y=c[11252]|0;if((y|0)==9|(y|0)==2){u=c[10030]|0;aF(-18|0,u|0)}else if((y|0)==8){aF(-120|0,c[10030]|0)}else if((y|0)==11|(y|0)==3){aF(-94|0,c[10030]|0)}else if((y|0)==12|(y|0)==13){aF(-14|0,c[10030]|0)}else{aF(1,c[10030]|0)}aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);y=0;while(1){if(y>>>0<(uA(v|0)|0)>>>0){A=a[f+y|0]|0}else{A=0}aF(A|0,c[10030]|0);aF(0,c[10030]|0);u=y+1|0;if((u|0)<32){y=u}else{B=0;break}}while(1){if(B>>>0<(uA(v|0)|0)>>>0){C=a[f+B|0]|0}else{C=0}aF(C|0,c[10030]|0);aF(0,c[10030]|0);y=B+1|0;if((y|0)<64){B=y}else{D=0;break}}do{aF(0,c[10030]|0);aF(0,c[10030]|0);D=D+1|0;}while((D|0)<32);c[d>>2]=0;aI(z|0,1,4,c[10030]|0);c[d>>2]=0;aI(z|0,1,4,c[10030]|0);c[d>>2]=0;aI(z|0,1,4,c[10030]|0);c[d>>2]=0;aI(z|0,1,4,c[10030]|0);c[d>>2]=0;aI(z|0,1,4,c[10030]|0);c[d>>2]=0;aI(z|0,1,4,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);aF(0,c[10030]|0);a[q]=0;a[x]=0;a[s]=0;a[z]=37;aI(z|0,1,4,c[10030]|0);a[q]=0;a[x]=0;a[s]=0;a[z]=12;aI(z|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;a[q]=0;a[x]=0;a[s]=0;a[z]=2;aI(z|0,1,4,c[10030]|0);i=b;return}function tP(){var b=0,d=0,e=0,f=0,g=0,h=0,j=0;b=i;i=i+8|0;d=b|0;if((c[11560]|0)==0){i=b;return}if((c[11552]|0)>0){tL(c[11554]|0);c[11552]=0}e=d|0;f=d+3|0;a[f]=0;g=d+2|0;a[g]=0;h=d+1|0;a[h]=0;a[e]=27;aI(e|0,1,4,c[10030]|0);a[f]=0;a[g]=0;a[h]=0;a[e]=16;aI(e|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;d=c[11300]|0;a[f]=d>>>24&255;a[g]=d>>>16&255;a[h]=d>>>8&255;a[e]=d&255;aI(e|0,1,4,c[10030]|0);d=(c[(c[3524]|0)+12>>2]|0)-(c[11301]|0)|0;a[f]=d>>>24&255;a[g]=d>>>16&255;a[h]=d>>>8&255;a[e]=d&255;aI(e|0,1,4,c[10030]|0);d=2;while(1){j=(d|0)<(c[11560]<<1|0);a[f]=0;a[g]=0;a[h]=0;a[e]=54;aI(e|0,1,4,c[10030]|0);a[f]=0;a[g]=0;a[h]=0;a[e]=16;aI(e|0,1,4,c[10030]|0);c[11294]=(c[11294]|0)+1;if(!j){break}j=c[45200+(d<<2)>>2]|0;a[f]=j>>>24&255;a[g]=j>>>16&255;a[h]=j>>>8&255;a[e]=j&255;aI(e|0,1,4,c[10030]|0);j=(c[(c[3524]|0)+12>>2]|0)-(c[45200+((d|1)<<2)>>2]|0)|0;a[f]=j>>>24&255;a[g]=j>>>16&255;a[h]=j>>>8&255;a[e]=j&255;aI(e|0,1,4,c[10030]|0);d=d+2|0}d=c[11300]|0;a[f]=d>>>24&255;a[g]=d>>>16&255;a[h]=d>>>8&255;a[e]=d&255;aI(e|0,1,4,c[10030]|0);d=(c[(c[3524]|0)+12>>2]|0)-(c[11301]|0)|0;a[f]=d>>>24&255;a[g]=d>>>16&255;a[h]=d>>>8&255;a[e]=d&255;aI(e|0,1,4,c[10030]|0);c[11560]=0;i=b;return}function tQ(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;f=i;i=i+8|0;j=f|0;if((e|0)==0){i=f;return}if((a[e]|0)==0){i=f;return}do{if((a[37456]&1)==0){if((cy(e|0,170608)|0)==0){break}k=c[3524]|0;if((c[k+8>>2]|0)>>>0>b>>>0){if((c[k+12>>2]|0)>>>0>d>>>0){l=d;m=b}else{n=20699}}else{n=20699}if((n|0)==20699){uh(-1,171048,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=d,v)|0);k=c[3524]|0;o=c[k+8>>2]|0;p=c[k+12>>2]|0;l=p>>>0>d>>>0?d:p;m=o>>>0>b>>>0?b:o}if(!((m|0)==(c[11298]|0)&(l|0)==(c[11296]|0))){tN();c[11298]=m;c[11296]=l}if((c[11280]|0)!=(c[11562]|0)){o=j|0;p=j+3|0;a[p]=0;k=j+2|0;a[k]=0;q=j+1|0;a[q]=0;a[o]=24;r=c[10030]|0;aI(o|0,1,4,r|0);a[p]=0;a[k]=0;a[q]=0;a[o]=12;r=c[10030]|0;aI(o|0,1,4,r|0);c[11294]=(c[11294]|0)+1;r=c[11562]|0;a[p]=r>>>24&255;a[k]=r>>>16&255;a[q]=r>>>8&255;a[o]=r&255;r=c[10030]|0;aI(o|0,1,4,r|0);c[11280]=c[11562]}h[5613]=1.0;uF(44912,170592,16);a[238232]=0;c[59556]=0;c[59562]=45688;g[59560]=+g[11418];if(((c[11414]|0)-1|0)>>>0<2){a[238208]=1}r=j|0;o=j+3|0;a[o]=0;q=j+2|0;a[q]=0;k=j+1|0;a[k]=0;a[r]=22;p=c[10030]|0;aI(r|0,1,4,p|0);a[o]=0;a[q]=0;a[k]=0;a[r]=12;p=c[10030]|0;aI(r|0,1,4,p|0);c[11294]=(c[11294]|0)+1;a[o]=0;a[q]=0;a[k]=0;a[r]=24;k=c[10030]|0;aI(r|0,1,4,k|0);k=e;do{r=lJ(k,1,c[59562]|0,+g[59560],0.0,1,1,0)|0;if((a[r]|0)==0){break}cS[c[(c[3524]|0)+160>>2]&511]();if((a[r]|0)==125){uh(-1,203744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uh(-1,203528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}k=r+1|0;}while((a[k]|0)!=0);k=c[11414]|0;if((k-1|0)>>>0<2){r=(c[11298]|0)-b|0;if((c[11262]|0)==0){s=0}else{s=(c[11296]|0)-d|0}c[11414]=0;a[238208]=0;if((k|0)==2){tQ(b-r|0,d-s|0,e)}else if((k|0)==1){tQ(b-((r|0)/2&-1)|0,d-((s|0)/2&-1)|0,e)}c[11414]=k}tO();g[59564]=0.0;i=f;return}}while(0);n$(b,d,e);i=f;return}function tR(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;f=i;do{if((a[37456]&1)==0){if((cy(e|0,170608)|0)==0){break}h[5613]=1.0;a[238320]=0;uF(44912,216760,16);c[11678]=b;c[11674]=d;g=e;while(1){j=lJ(g,1,179864,1.0,0.0,1,1,0)|0;if((a[j]|0)==0){k=20745;break}cS[c[(c[3524]|0)+160>>2]&511]();if((a[j]|0)==125){uh(-1,203744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uh(-1,203528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}l=j+1|0;if((a[l]|0)==0){k=20743;break}else{g=l}}if((k|0)==20745){i=f;return}else if((k|0)==20743){i=f;return}}}while(0);g=uA(e|0)|0;l=c[11676]|0;if((g+b|0)>>>0>l>>>0){j=l-g|0;m=(j|0)<0?0:j}else{m=b}if(m>>>0<l>>>0){n=e;o=m;p=l}else{i=f;return}while(1){l=a[n]|0;if(l<<24>>24==0){k=20742;break}do{if(p>>>0<o>>>0|(c[11672]|0)>>>0<d>>>0){q=p}else{m=aa(p,d)+o|0;if((a[(c[11680]|0)+m|0]|0)>=5){q=p;break}a[(c[11684]|0)+m|0]=l;m=aa(c[11676]|0,d)+o|0;a[(c[11680]|0)+m|0]=5;q=c[11676]|0}}while(0);l=o+1|0;if(l>>>0<q>>>0){n=n+1|0;o=l;p=q}else{k=20744;break}}if((k|0)==20742){i=f;return}else if((k|0)==20744){i=f;return}}function tS(){var a=0,b=0,d=0;a=c[13656]|0;if((a|0)==0){return}b=c[13602]|0;tY(4,1,a<<1);if((a|0)>0){d=0;do{tZ(c[b+(d<<2)>>2]|0);d=d+1|0;}while((d|0)<(a|0))}c[13656]=0;return}function tT(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;if((c[13642]|0)==(a|0)){return}c[13642]=a;tS();if((a|0)>8){b=(a|0)%9&-1}else{b=a}a=(c[3524]|0)+60|0;if((b|0)<1){c[a>>2]=106;return}c[a>>2]=80;a=b<<3;b=c[13624]|0;d=c[242808+(a-8<<2)>>2]|0;if((d|0)==0){e=0}else{e=(aa(d<<1,b)>>>0)/3>>>0}c[13570]=e;e=c[242808+(a-7<<2)>>2]|0;if((e|0)==0){f=0}else{f=(aa(e<<1,b)>>>0)/3>>>0}c[13571]=f;e=c[242808+(a-6<<2)>>2]|0;if((e|0)==0){g=0}else{g=(aa(e<<1,b)>>>0)/3>>>0}c[13572]=g;g=c[242808+(a-5<<2)>>2]|0;if((g|0)==0){h=0}else{h=(aa(g<<1,b)>>>0)/3>>>0}c[13573]=h;h=c[242808+(a-4<<2)>>2]|0;if((h|0)==0){i=0}else{i=(aa(h<<1,b)>>>0)/3>>>0}c[13574]=i;i=c[242808+(a-3<<2)>>2]|0;if((i|0)==0){j=0}else{j=(aa(i<<1,b)>>>0)/3>>>0}c[13575]=j;j=c[242808+(a-2<<2)>>2]|0;if((j|0)==0){k=0}else{k=(aa(j<<1,b)>>>0)/3>>>0}c[13576]=k;k=c[242808+(a-1<<2)>>2]|0;if((k|0)==0){l=0}else{l=(aa(k<<1,b)>>>0)/3>>>0}c[13577]=l;c[13580]=f;c[13578]=1;return}function tU(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;d=c[3524]|0;e=c[d+8>>2]|0;f=e>>>0>a>>>0?a:e;e=c[d+12>>2]|0;d=e>>>0>b>>>0?b:e;e=f-(c[13598]|0)|0;b=d-(c[13596]|0)|0;a=(e|0)>-1?e:-e|0;g=b*10&-1;h=(g|0)>-1?g:-g|0;g=a*10&-1;do{if((g|0)<(h|0)){i=((((aa((a*25&-1|0)/(h|0)&-1,a)|0)/6&-1)*5&-1)+((h|0)/2&-1)|0)/5&-1}else{if((a|0)==0){return}else{i=(aa((h|0)/(a|0)&-1,(h|0)/24&-1)+g|0)/10&-1;break}}}while(0);g=c[13580]|0;h=c[13578]|0;if((i|0)>(g|0)){a=i;j=g;g=h;while(1){k=a-j|0;l=f-((aa(k,e)|0)/(i|0)&-1)|0;m=d-((aa(k,b)|0)/(i|0)&-1)|0;if((g&1|0)==0){m5(l,m)}else{m6(l,m)}m=(c[13578]|0)+1|0;l=(m|0)>7?0:m;c[13578]=l;m=c[54280+(l<<2)>>2]|0;c[13580]=m;if((k|0)>(m|0)){a=k;j=m;g=l}else{n=k;o=l;break}}}else{n=i;o=h}if((o&1|0)==0){m5(f,d)}else{m6(f,d)}c[13580]=(c[13580]|0)-n;return}function tV(){var b=0,d=0,e=0,f=0.0,g=0,i=0,j=0,k=0;uD(54528,89360,15);b=uA(54528)|0;d=c[13630]|0;e=c[d>>2]|0;L27708:do{if((e|0)==0){f=1.0}else{g=0;i=e;while(1){if((uA(i|0)|0)==(b|0)){if((uJ(54528,i|0,b|0)|0)==0){break}}j=g+1|0;k=c[d+(j<<4)>>2]|0;if((k|0)==0){f=1.0;break L27708}else{g=j;i=k}}f=+h[d+(g<<4)+8>>3]}}while(0);c[13628]=12;d=c[3524]|0;c[d+16>>2]=((((c[d+8>>2]|0)+182|0)>>>0)/((c[13604]|0)>>>0)>>>0)*12&-1;d=c[3524]|0;c[d+20>>2]=~~(f*+(aa((((c[d+8>>2]|0)+182|0)>>>0)/((c[13604]|0)>>>0)>>>0,c[13628]|0)>>>0>>>0)*.527);c[13622]=1;a[54472]=0;c[13604]=432;a[54400]=0;a[54336]=0;a[54576]=0;a[54424]=0;c[13646]=c[13586];c[13647]=c[13587];c[13648]=c[13588];c[13649]=c[13589];c[13650]=c[13590];c[13651]=c[13591];c[13652]=c[13592];c[13653]=c[13593];c[13654]=c[13594];c[13655]=c[13595];c[13554]=0;return}function tW(a){a=a|0;var b=0,d=0,e=0.0,f=0,g=0.0,j=0;b=i;d=a>>4;e=+(d|0)/100.0;f=a&15;do{if((f|0)==0){uD(243392,164560,23);i=b;return}else if((f|0)==2|(f|0)==5){a=(d|0)%5&-1;if((a|0)==4){g=.5;j=20811;break}else if((a|0)==2){g=.25;j=20811;break}else if((a|0)==1){j=20810;break}else if((a|0)==0){g=0.0;j=20811;break}else if((a|0)==3){g=.75;j=20811;break}else{j=20809;break}}else if((f|0)==4){be(243392,164584,(v=i,i=i+24|0,c[v>>2]=55376,h[v+8>>3]=e,c[v+16>>2]=0,v)|0);i=b;return}else if((f|0)==1){j=20809}else{be(243392,164424,(v=i,i=i+16|0,c[v>>2]=55372,c[v+8>>2]=0,v)|0);i=b;return}}while(0);if((j|0)==20809){if(e==1.0){j=20810}else{g=e;j=20811}}if((j|0)==20810){uB(243392,55372);i=b;return}else if((j|0)==20811){j=bW(55376)|0;f=bW(55380)|0;e=(1.0-g)*255.0;d=~~(e+g*+(bW(55384)|0));be(243392,164536,(v=i,i=i+32|0,c[v>>2]=~~(e+g*+(j|0)),c[v+8>>2]=~~(e+g*+(f|0)),c[v+16>>2]=d,c[v+24>>2]=0,v)|0);i=b;return}}function tX(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0.0,j=0,k=0,l=0,m=0,n=0;f=i;g=+h[6936];if((a[e]|0)==0){i=f;return}do{if((a[37456]&1)==0){if((cy(e|0,170608)|0)==0){break}j=a[55472]|0;if(!(j&(c[13838]|0)==(b|0)&(c[13834]|0)==(d|0))){if(!j){j=c[10030]|0;aI(162736,17,1,j|0);a[55472]=1;a[72328]=0}j=c[10030]|0;k=(c[13832]|0)-d|0;cf(j|0,162768,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=k,v)|0);c[13838]=b;c[13834]=d}h[5613]=1.0;uF(44912,216760,16);a[238512]=0;h[29815]=+h[6936];k=c[13866]|0;if((aY(k|0,164216)|0)==0){l=20829}else{if((aY(k|0,173184)|0)==0){l=20829}else{m=e}}if((l|0)==20829){a[238488]=1;m=e}do{k=lJ(m,1,179864,g,0.0,1,1,0)|0;if((a[k]|0)==0){break}cS[c[(c[3524]|0)+160>>2]&511]();if((a[k]|0)==125){uh(-1,203744,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else{uh(-1,203528,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}m=k+1|0;}while((a[m]|0)!=0);k=c[13866]|0;if((aY(k|0,164216)|0)==0){l=20837}else{if((aY(k|0,173184)|0)==0){l=20837}}if((l|0)==20837){j=(c[13838]|0)-b|0;if((c[13840]|0)==0){n=0}else{n=(c[13834]|0)-d|0}c[13866]=179864;a[238488]=0;do{if((aY(k|0,164216)|0)==0){tX(b-j|0,d-n|0,e)}else{if((aY(k|0,173184)|0)!=0){break}tX(b-((j|0)/2&-1)|0,d-((n|0)/2&-1)|0,e)}}while(0);c[13866]=k}h[6936]=g;h[29816]=0.0;i=f;return}}while(0);mO(b,d,e);i=f;return}function tY(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0;f=i;i=i+24|0;g=f|0;h=f+8|0;j=f+16|0;if(b>>>0>=16){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167440,v)|0)}if(d>>>0>=128){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167408,v)|0)}if((e|0)<=-1){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167384,v)|0)}k=d<<5&4064|b<<12&61440;if((e|0)<31){b=k|e&31;d=j;if(b>>>0>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[d]=k>>>8&255;a[j+1|0]=b&255;b=c[10030]|0;aI(d|0,1,2,b|0);i=f;return}b=k|31;d=h;if(b>>>0>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[d]=k>>>8&255;a[h+1|0]=b&255;aI(d|0,1,2,c[10030]|0);d=g;if((e|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((e|0)>=32768){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}a[d]=e>>>8&255;a[g+1|0]=e&255;aI(d|0,1,2,c[10030]|0);i=f;return}function tZ(b){b=b|0;var d=0,e=0,f=0;d=i;i=i+8|0;e=d|0;if((b|0)<=-32769){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167584,v)|0)}if((b|0)<32768){f=e;a[f]=b>>>8&255;a[e+1|0]=b&255;b=c[10030]|0;aI(f|0,1,2,b|0);i=d;return}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}function t_(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0;g=i;i=i+8|0;h=g|0;j=((e|0)>254?3:1)+e|0;k=j&1;l=(k|0)==0;tY(b,d,j);j=c[10030]|0;do{if((e|0)<255){d=e<<24>>24;aF(d|0,j|0)}else{d=a[241904]|0;aF(d|0,j|0);d=h;if((e|0)<32768){a[d]=e>>>8&255;a[h+1|0]=e&255;b=c[10030]|0;aI(d|0,1,2,b|0);break}else{uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=167472,v)|0)}}}while(0);do{if((f|0)==0){h=k+e|0;if((h|0)>0){m=0}else{break}do{aF(0,c[10030]|0);m=m+1|0;}while((m|0)<(h|0))}else{aI(f|0,1,e|0,c[10030]|0)}}while(0);if(l){i=g;return}aF(a[241896]|0|0,c[10030]|0);i=g;return}function t$(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0;f=c[60208]|0;g=c[f>>2]|0;h=a[240824]|0;if((g&1|0)==0){if(h){i=c[60230]|0;j=c[60226]|0;k=c[10030]|0;aF(29,k|0);k=j>>>5&31|32;l=c[10030]|0;aF(k|0,l|0);l=j&31|96;j=c[10030]|0;aF(l|0,j|0);j=i>>>5&31|32;l=c[10030]|0;aF(j|0,l|0);l=i&31|64;i=c[10030]|0;aF(l|0,i|0);i=c[60228]|0;l=c[60224]|0;j=l>>>5&31|32;k=c[10030]|0;aF(j|0,k|0);k=l&31|96;l=c[10030]|0;aF(k|0,l|0);l=i>>>5&31|32;k=c[10030]|0;aF(l|0,k|0);k=i&31|64;i=c[10030]|0;aF(k|0,i|0);a[240824]=0;i=c[60208]|0;m=i;n=c[i>>2]|0}else{m=f;n=g}c[m>>2]=n>>>1;return}c[60228]=b;if(h){c[60224]=d}else{c[60230]=b;c[60224]=d;c[60226]=d;a[240824]=1}c[f>>2]=(c[f>>2]|0)>>>1|-2147483648;if((e|0)==0){return}e=c[60230]|0;f=c[60226]|0;aF(29,c[10030]|0);aF(f>>>5&31|32|0,c[10030]|0);aF(f&31|96|0,c[10030]|0);aF(e>>>5&31|32|0,c[10030]|0);aF(e&31|64|0,c[10030]|0);e=c[60228]|0;f=c[60224]|0;aF(f>>>5&31|32|0,c[10030]|0);aF(f&31|96|0,c[10030]|0);aF(e>>>5&31|32|0,c[10030]|0);aF(e&31|64|0,c[10030]|0);a[240824]=0;return}function t0(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;e=b>>>0>4095?4095:b;b=d>>>0>3131?3131:d;d=(e>>>7)+32|0;f=e>>>2&31|64;g=(b>>>7)+32|0;h=b>>>2&31|96;i=e&3|b<<2&12|96;if((a[226904]|0|0)!=(g|0)){b=c[10030]|0;aF(g|0,b|0)}if((a[226920]|0|0)!=(i|0)){b=c[10030]|0;aF(i|0,b|0)}do{if((a[226896]|0|0)==(h|0)){if((a[226920]|0|0)!=(i|0)){j=20908;break}b=a[226912]|0;if((b<<24>>24|0)==(d|0)){k=b}else{j=20908}}else{j=20908}}while(0);if((j|0)==20908){j=c[10030]|0;aF(h|0,j|0);k=a[226912]|0}if((k<<24>>24|0)==(d|0)){l=c[10030]|0;m=aF(f|0,l|0)|0;n=d&255;a[226912]=n;o=g&255;a[226904]=o;p=h&255;a[226896]=p;q=i&255;a[226920]=q;return}aF(d|0,c[10030]|0);l=c[10030]|0;m=aF(f|0,l|0)|0;n=d&255;a[226912]=n;o=g&255;a[226904]=o;p=h&255;a[226896]=p;q=i&255;a[226920]=q;return}function t1(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0;g=i;i=i+8|0;j=g|0;c[j>>2]=b;b=e+12|0;c[b>>2]=1;k=e|0;c[k>>2]=0;l=e+4|0;c[l>>2]=0;m=e+8|0;c[m>>2]=0;n=e+16|0;c[n>>2]=0;o=e+20|0;c[o>>2]=2e3;h[f>>3]=0.0;c[e+24>>2]=-1;p=e+28|0;c[p>>2]=-1;q=d;d=0;r=0;L27863:while(1){s=q;L27865:while(1){t=a[s]|0;do{if((t<<24>>24|0)==37){break L27865}else if((t<<24>>24|0)==0){break L27863}else if((t<<24>>24|0)==32){u=c[j>>2]|0;if((a[u]|0)==32){w=u}else{break}do{w=w+1|0;c[j>>2]=w;}while((a[w]|0)==32)}else{u=c[j>>2]|0;if(t<<24>>24!=(a[u]|0)){break L27863}c[j>>2]=u+1}}while(0);s=s+1|0}t=a[s+1|0]|0;do{if((t|0)==109){u=c[j>>2]|0;x=a[u]|0;do{if((x-48&255)<10){y=u+1|0;z=(x<<24>>24)-48|0;A=a[y]|0;if((A-48&255)>=10){B=y;C=z;break}B=u+2|0;C=((z*10&-1)-48|0)+(A<<24>>24)|0}else{B=u;C=0}}while(0);c[j>>2]=B;c[n>>2]=C-1;D=d;E=r+1|0}else if((t|0)==66){u=c[j>>2]|0;x=0;while(1){if((x|0)>=12){F=20933;break}A=40552+(x<<5)|0;G=uA(A|0)|0;if((uJ(u|0,A|0,G|0)|0)==0){F=20932;break}else{x=x+1|0}}if((F|0)==20932){F=0;c[j>>2]=u+G;H=x}else if((F|0)==20933){F=0;uh(-2,199248,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);H=0}c[n>>2]=H;D=d;E=r}else if((t|0)==106){A=c[j>>2]|0;z=a[A]|0;do{if((z-48&255)<10){y=A+1|0;I=(z<<24>>24)-48|0;J=a[y]|0;if((J-48&255)>=10){K=y;L=I;break}y=A+2|0;M=((I*10&-1)-48|0)+(J<<24>>24)|0;J=a[y]|0;if((J-48&255)>=10){K=y;L=M;break}K=A+3|0;L=((M*10&-1)-48|0)+(J<<24>>24)|0}else{K=A;L=0}}while(0);c[j>>2]=K;c[p>>2]=L-1;D=d+1|0;E=r+1|0}else if((t|0)==100){A=c[j>>2]|0;z=a[A]|0;do{if((z-48&255)<10){x=A+1|0;u=(z<<24>>24)-48|0;J=a[x]|0;if((J-48&255)>=10){N=x;O=u;break}N=A+2|0;O=((u*10&-1)-48|0)+(J<<24>>24)|0}else{N=A;O=0}}while(0);c[b>>2]=O;c[j>>2]=N;D=d;E=r+1|0}else if((t|0)==115){t3(e,+uz(c[j>>2]|0,j)+-946684800.0);D=d;E=r}else if((t|0)==98){A=c[j>>2]|0;z=0;while(1){if((z|0)>=12){F=20928;break}J=72368+(z<<3)|0;P=uA(J|0)|0;if((uJ(A|0,J|0,P|0)|0)==0){F=20927;break}else{z=z+1|0}}if((F|0)==20928){F=0;uh(-2,115608,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);Q=0}else if((F|0)==20927){F=0;c[j>>2]=A+P;Q=z}c[n>>2]=Q;D=d;E=r}else if((t|0)==77){J=c[j>>2]|0;u=a[J]|0;do{if((u-48&255)<10){x=J+1|0;M=(u<<24>>24)-48|0;y=a[x]|0;if((y-48&255)>=10){R=x;S=M;break}R=J+2|0;S=((M*10&-1)-48|0)+(y<<24>>24)|0}else{R=J;S=0}}while(0);c[l>>2]=S;c[j>>2]=R;D=d;E=r}else if((t|0)==89){J=c[j>>2]|0;u=a[J]|0;do{if((u-48&255)<10){z=J+1|0;A=(u<<24>>24)-48|0;y=a[z]|0;if((y-48&255)>=10){T=z;U=A;break}z=J+2|0;M=((A*10&-1)-48|0)+(y<<24>>24)|0;y=a[z]|0;if((y-48&255)>=10){T=z;U=M;break}z=J+3|0;A=((M*10&-1)-48|0)+(y<<24>>24)|0;y=a[z]|0;if((y-48&255)>=10){T=z;U=A;break}T=J+4|0;U=((A*10&-1)-48|0)+(y<<24>>24)|0}else{T=J;U=0}}while(0);c[o>>2]=U;c[j>>2]=T;D=d;E=r+1|0}else if((t|0)==83){J=c[j>>2]|0;u=a[J]|0;do{if((u-48&255)<10){y=J+1|0;A=(u<<24>>24)-48|0;z=a[y]|0;if((z-48&255)>=10){V=y;W=A;break}V=J+2|0;W=((A*10&-1)-48|0)+(z<<24>>24)|0}else{V=J;W=0}}while(0);c[k>>2]=W;c[j>>2]=V;J=a[V]|0;if(J<<24>>24!=46){u=c[12886]|0;if((u|0)==0){D=d;E=r;break}if(J<<24>>24!=(a[u]|0)){D=d;E=r;break}}h[f>>3]=+uz(V,0);D=d;E=r}else if((t|0)==72){u=c[j>>2]|0;J=a[u]|0;do{if((J-48&255)<10){z=u+1|0;A=(J<<24>>24)-48|0;y=a[z]|0;if((y-48&255)>=10){X=z;Y=A;break}X=u+2|0;Y=((A*10&-1)-48|0)+(y<<24>>24)|0}else{X=u;Y=0}}while(0);c[m>>2]=Y;c[j>>2]=X;D=d;E=r}else if((t|0)==121){u=c[j>>2]|0;J=a[u]|0;if((J-48&255)<10){y=u+1|0;A=(J<<24>>24)-48|0;J=a[y]|0;if((J-48&255)<10){Z=u+2|0;_=((A*10&-1)-48|0)+(J<<24>>24)|0}else{Z=y;_=A}c[o>>2]=_;c[j>>2]=Z;if((_|0)<69){$=_;F=20945}else{aa=_}}else{c[o>>2]=0;c[j>>2]=u;$=0;F=20945}if((F|0)==20945){F=0;u=$+100|0;c[o>>2]=u;aa=u}c[o>>2]=aa+1900;D=d;E=r+1|0}else{uh(-2,148656,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);D=d;E=r}}while(0);q=s+2|0;d=D;r=E}E=c[k>>2]|0;if((E|0)>59){c[l>>2]=(c[l>>2]|0)+((E|0)/60&-1);c[k>>2]=(E|0)%60&-1}E=c[l>>2]|0;if((E|0)>59){c[m>>2]=(c[m>>2]|0)+((E|0)/60&-1);c[l>>2]=(E|0)%60&-1}E=c[m>>2]|0;if((E|0)>23){l=(E|0)/24&-1;if((d|0)!=0){c[p>>2]=(c[p>>2]|0)+l}c[b>>2]=(c[b>>2]|0)+l;c[m>>2]=(E|0)%24&-1}if((r|0)==0){ab=c[j>>2]|0;i=g;return ab|0}do{if((d|0)==0){r=c[n>>2]|0;if((r|0)<0){uf(-2,116496,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{ac=r;ad=c[b>>2]|0;break}}else{r=c[p>>2]|0;if((r|0)<0){uf(-2,131384,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else{c[n>>2]=0;E=r+1|0;c[b>>2]=E;ac=0;ad=E;break}}}while(0);if((ad|0)<1){uf(-2,103704,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}if((ac|0)>11){c[o>>2]=(c[o>>2]|0)+((ac|0)/12&-1);p=(ac|0)%12&-1;c[n>>2]=p;ae=p;af=ad}else{ae=ac;af=ad}while(1){ad=c[34056+(ae<<2)>>2]|0;do{if((ae|0)==1){ac=c[o>>2]|0;if((ac&3|0)!=0){ag=0;break}ag=(((ac|0)%100&-1|0)!=0|((ac|0)%400&-1|0)==0)&1}else{ag=0}}while(0);s=ag+ad|0;if((af|0)<=(s|0)){break}ac=ae+1|0;c[n>>2]=ac;if((ac|0)==12){c[o>>2]=(c[o>>2]|0)+1;c[n>>2]=0;ah=0}else{ah=ac}ac=af-s|0;c[b>>2]=ac;ae=ah;af=ac}ab=c[j>>2]|0;i=g;return ab|0}function t2(a){a=a|0;var b=0,d=0,e=0.0,f=0.0,g=0.0,h=0,i=0.0,j=0,k=0.0,l=0.0,m=0.0,n=0,o=0.0,p=0.0;b=c[a+20>>2]|0;do{if((b|0)<2e3){d=b;e=0.0;while(1){if((d&3|0)==0){f=((d|0)%100&-1|0)!=0|((d|0)%400&-1|0)==0?366.0:365.0}else{f=365.0}g=e-f;h=d+1|0;if((h|0)<2e3){d=h;e=g}else{i=g;break}}}else{if((b|0)>2e3){j=2e3;k=0.0}else{i=0.0;break}while(1){if((j&3|0)==0){l=((j|0)%100&-1|0)!=0|((j|0)%400&-1|0)==0?366.0:365.0}else{l=365.0}e=k+l;d=j+1|0;if((d|0)<(b|0)){j=d;k=e}else{i=e;break}}}}while(0);j=c[a+12>>2]|0;if((j|0)>0){d=c[a+16>>2]|0;if((d|0)>0){h=0;k=i;while(1){l=+(c[34056+(h<<2)>>2]|0);do{if((h|0)==1){if((b&3|0)!=0){m=0.0;break}m=+((((b|0)%100&-1|0)!=0|((b|0)%400&-1|0)==0)&1|0)}else{m=0.0}}while(0);f=k+(l+m);n=h+1|0;if((n|0)<(d|0)){h=n;k=f}else{o=f;break}}}else{o=i}p=o+(+(j|0)+-1.0)}else{p=i+ +(c[a+28>>2]|0)}return+(+(c[a>>2]|0)+(+(c[a+4>>2]|0)+(p*24.0+ +(c[a+8>>2]|0))*60.0)*60.0)}function t3(a,b){a=a|0;b=+b;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=0,q=0,r=0.0,s=0,t=0,u=0.0,w=0,x=0,y=0,z=0.0,A=0,B=0;d=i;if(+P(+b)>1.0e12){uh(-1,96560,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);e=-1;i=d;return e|0}f=a+20|0;c[f>>2]=2e3;g=a|0;h=a+4|0;j=a+8|0;k=a+16|0;l=a+28|0;c[l>>2]=0;m=a+12|0;uE(a|0,0,20);L28020:do{if(b<0.0){n=6;o=b;p=2e3;do{p=p-1|0;if((p&3|0)==0){q=((p|0)%100&-1|0)!=0|((p|0)%400&-1|0)==0?366:365}else{q=365}o=o+ +(q|0)*86400.0;n=(n+371|0)-q|0;}while(o<0.0);c[f>>2]=p;r=o;s=n;t=p}else{u=b;w=6;x=2e3;while(1){if((x&3|0)==0){y=((x|0)%100&-1|0)!=0|((x|0)%400&-1|0)==0?366:365}else{y=365}z=+(y|0)*86400.0;if(u<z){r=u;s=w;t=x;break L28020}A=x+1|0;c[f>>2]=A;u=u-z;w=(w-364|0)+y|0;x=A}}}while(0);y=~~(r/86400.0);c[l>>2]=y;b=r- +(y|0)*86400.0;l=(~~b|0)/3600&-1;c[j>>2]=l;r=b- +(l*3600&-1|0);l=(~~r|0)/60&-1;c[h>>2]=l;c[g>>2]=~~(r- +(l*60&-1|0));c[a+24>>2]=(y+s|0)%7&-1;s=y;y=0;while(1){a=c[34056+(y<<2)>>2]|0;do{if((y|0)==1){if((t&3|0)!=0){B=0;break}B=(((t|0)%100&-1|0)!=0|((t|0)%400&-1|0)==0)&1}else{B=0}}while(0);l=B+a|0;if((s|0)<(l|0)){break}g=y+1|0;c[k>>2]=g;s=s-l|0;y=g}c[m>>2]=s+1;e=0;i=d;return e|0}function t4(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=+g;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,P=0,Q=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0.0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0;j=i;uE(b|0,0,d|0);k=f+16|0;l=f+24|0;m=f+12|0;n=f+8|0;o=f+28|0;p=f+4|0;q=f|0;r=f+20|0;s=e;e=0;t=b;L28044:while(1){b=a[s]|0;do{if((b<<24>>24|0)==0){u=e;w=21166;break L28044}else if((b<<24>>24|0)==37){x=s+1|0;y=(a[x]|0)==48;z=y&1;A=y?s+2|0:x;x=a[A]|0;if((x-48&255)<10){B=0;C=A;D=x;while(1){E=((B*10&-1)-48|0)+(D<<24>>24)|0;F=C+1|0;G=a[F]|0;if((G-48&255)<10){B=E;C=F;D=G}else{H=E;I=F;J=G;break}}}else{H=0;I=A;J=x}if(J<<24>>24==46){D=I+1|0;C=a[D]|0;if((C-48&255)<10){B=0;G=D;F=C;while(1){E=((B*10&-1)-48|0)+(F<<24>>24)|0;K=G+1|0;L=a[K]|0;if((L-48&255)<10){B=E;G=K;F=L}else{M=E;N=K;P=L;break}}}else{M=0;N=D;P=C}Q=N;S=(M|0)>6?6:M;T=P}else{Q=I;S=0;T=J}F=T<<24>>24;do{if((F|0)==77){if((H|0)==0){U=y?z:1;V=2}else{U=z;V=H}if((V+e|0)>>>0>d>>>0){u=0;w=21167;break L28044}G=(U|0)!=0?90536:82208;B=c[p>>2]|0;be(t|0,G|0,(v=i,i=i+16|0,c[v>>2]=V,c[v+8>>2]=B,v)|0);w=21140}else if((F|0)==84){if((t4(t,d-e|0,179088,f,0.0)|0)==0){u=0;w=21143;break L28044}else{w=21140}}else if((F|0)==72){if((H|0)==0){W=y?z:1;X=2}else{W=z;X=H}if((X+e|0)>>>0>d>>>0){u=0;w=21171;break L28044}B=c[n>>2]|0;be(t|0,((W|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=X,c[v+8>>2]=B,v)|0);w=21140}else if((F|0)==37){if((e+1|0)>>>0>d>>>0){u=0;w=21144;break L28044}a[t]=37;Y=t;Z=e}else if((F|0)==87){B=c[o>>2]|0;G=c[l>>2]|0;do{if((B|0)>(G|0)){x=B-G|0;A=(G|0)>0?x+7|0:x;x=(A|0)/7&-1;if(((A|0)%7&-1|0)<=2){_=x;break}_=x+1|0}else{_=(B|0)==(G|0)&(G|0)==0?52:((c[m>>2]|0)-B|0)>4?52:1}}while(0);if((H|0)==0){$=y?z:1;aa=2}else{$=z;aa=H}if((aa+e|0)>>>0>d>>>0){u=0;w=21154;break L28044}be(t|0,(($|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=aa,c[v+8>>2]=_,v)|0);w=21140}else if((F|0)==108){B=(H|0)==0?2:H;if((B+e|0)>>>0>d>>>0){u=0;w=21147;break L28044}G=(((c[n>>2]|0)+11|0)%12&-1)+1|0;be(t|0,(y?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=B,c[v+8>>2]=G,v)|0);w=21140}else if((F|0)==121){if((H|0)==0){ab=y?z:1;ac=2}else{ab=z;ac=H}if((ac+e|0)>>>0>d>>>0){u=0;w=21148;break L28044}G=(c[r>>2]|0)%100&-1;be(t|0,((ab|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=ac,c[v+8>>2]=G,v)|0);w=21140}else if((F|0)==98|(F|0)==104){G=72368+(c[k>>2]<<3)|0;if(((uA(G|0)|0)+e|0)>>>0>d>>>0){u=0;w=21149;break L28044}uB(t|0,G|0);w=21140}else if((F|0)==73){if((H|0)==0){ad=y?z:1;ae=2}else{ad=z;ae=H}if((ae+e|0)>>>0>d>>>0){u=0;w=21155;break L28044}G=(((c[n>>2]|0)+11|0)%12&-1)+1|0;be(t|0,((ad|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=ae,c[v+8>>2]=G,v)|0);w=21140}else if((F|0)==65){G=40936+(c[l>>2]<<5)|0;if(((uA(G|0)|0)+e|0)>>>0>d>>>0){u=0;w=21150;break L28044}uB(t|0,G|0);w=21140}else if((F|0)==112){if((e+2|0)>>>0>d>>>0){u=0;w=21151;break L28044}G=(c[n>>2]|0)<12?215536:209856;a[t]=a[G]|0;a[t+1|0]=a[G+1|0]|0;a[t+2|0]=a[G+2|0]|0;w=21140}else if((F|0)==100){if((H|0)==0){af=y?z:1;ag=2}else{af=z;ag=H}if((ag+e|0)>>>0>d>>>0){u=0;w=21168;break L28044}G=c[m>>2]|0;be(t|0,((af|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=ag,c[v+8>>2]=G,v)|0);w=21140}else if((F|0)==89){if((H|0)==0){ah=y?z:1;ai=4}else{ah=z;ai=H}if((ai+e|0)>>>0>d>>>0){u=0;w=21169;break L28044}G=c[r>>2]|0;be(t|0,((ah|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=ai,c[v+8>>2]=G,v)|0);w=21140}else if((F|0)==107){G=(H|0)==0?2:H;if((G+e|0)>>>0>d>>>0){u=0;w=21152;break L28044}B=c[n>>2]|0;be(t|0,(y?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=G,c[v+8>>2]=B,v)|0);w=21140}else if((F|0)==109){if((H|0)==0){aj=y?z:1;ak=2}else{aj=z;ak=H}if((ak+e|0)>>>0>d>>>0){u=0;w=21161;break L28044}B=(c[k>>2]|0)+1|0;be(t|0,((aj|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=ak,c[v+8>>2]=B,v)|0);w=21140}else if((F|0)==114){if((t4(t,d-e|0,204248,f,0.0)|0)==0){u=0;w=21156;break L28044}else{w=21140}}else if((F|0)==66){B=40552+(c[k>>2]<<5)|0;if(((uA(B|0)|0)+e|0)>>>0>d>>>0){u=0;w=21157;break L28044}uB(t|0,B|0);w=21140}else if((F|0)==68){if((t4(t,d-e|0,75032,f,0.0)|0)==0){u=0;w=21158;break L28044}else{w=21140}}else if((F|0)==82){if((t4(t,d-e|0,199880,f,0.0)|0)==0){u=0;w=21159;break L28044}else{w=21140}}else if((F|0)==115){if((e+12|0)>>>0>d>>>0){u=0;w=21160;break L28044}al=+t2(f);be(t|0,74984,(v=i,i=i+8|0,h[v>>3]=al,v)|0);w=21140}else if((F|0)==106){if((H|0)==0){am=y?z:1;an=3}else{am=z;an=H}if((an+e|0)>>>0>d>>>0){u=0;w=21146;break L28044}B=(c[o>>2]|0)+1|0;be(t|0,((am|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=an,c[v+8>>2]=B,v)|0);w=21140}else if((F|0)==119){if((H|0)==0){ao=y?z:1;ap=2}else{ao=z;ap=H}if((ap+e|0)>>>0>d>>>0){u=0;w=21165;break L28044}B=c[l>>2]|0;be(t|0,((ao|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=ap,c[v+8>>2]=B,v)|0);w=21140}else if((F|0)==97){B=72464+(c[l>>2]<<3)|0;if(((uA(B|0)|0)+e|0)>>>0>d>>>0){u=0;w=21164;break L28044}uB(t|0,B|0);w=21140}else if((F|0)==85){B=c[o>>2]|0;G=c[l>>2]|0;do{if((B|0)>(G|0)){x=(G>>31|6)+(B-G|0)|0;A=(x|0)/7&-1;if(((x|0)%7&-1|0)<=1){aq=A;break}aq=A+1|0}else{aq=((c[m>>2]|0)-B|0)>4?52:1}}while(0);if((H|0)==0){ar=y?z:1;as=2}else{ar=z;as=H}if((as+e|0)>>>0>d>>>0){u=0;w=21153;break L28044}be(t|0,((ar|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=as,c[v+8>>2]=aq,v)|0);w=21140}else if((F|0)==83){if((H|0)==0){at=y?z:1;au=2}else{at=z;au=H}if((au+e|0)>>>0>d>>>0){u=0;w=21163;break L28044}B=c[q>>2]|0;be(t|0,((at|0)!=0?90536:82208)|0,(v=i,i=i+16|0,c[v>>2]=au,c[v+8>>2]=B,v)|0);if((S|0)<=0){w=21140;break}al=+R(10.0,+(+(S|0)));B=~~+O(+(al*g+.5));if(((e+1|0)+S|0)>>>0>d>>>0){u=0;w=21162;break L28044}G=~~al;be(t+(uA(t|0)|0)|0,184248,(v=i,i=i+16|0,c[v>>2]=S,c[v+8>>2]=(B|0)<(G|0)?B:G-1|0,v)|0);w=21140}else if((F|0)==70){if((t4(t,d-e|0,222376,f,0.0)|0)==0){u=0;w=21170;break L28044}else{w=21140}}else{w=21140}}while(0);if((w|0)==21140){w=0;if((a[t]|0)==0){av=Q;aw=e;ax=t;break}else{Y=t;Z=e}}while(1){F=Y+1|0;z=Z+1|0;if((a[F]|0)==0){av=Q;aw=z;ax=F;break}else{Y=F;Z=z}}}else{if(e>>>0>=d>>>0){u=0;w=21145;break L28044}a[t]=b;av=s;aw=e+1|0;ax=t+1|0}}while(0);s=av+1|0;e=aw;t=ax}if((w|0)==21169){i=j;return u|0}else if((w|0)==21170){i=j;return u|0}else if((w|0)==21154){i=j;return u|0}else if((w|0)==21155){i=j;return u|0}else if((w|0)==21156){i=j;return u|0}else if((w|0)==21171){i=j;return u|0}else if((w|0)==21166){i=j;return u|0}else if((w|0)==21167){i=j;return u|0}else if((w|0)==21168){i=j;return u|0}else if((w|0)==21147){i=j;return u|0}else if((w|0)==21148){i=j;return u|0}else if((w|0)==21157){i=j;return u|0}else if((w|0)==21158){i=j;return u|0}else if((w|0)==21149){i=j;return u|0}else if((w|0)==21150){i=j;return u|0}else if((w|0)==21151){i=j;return u|0}else if((w|0)==21143){i=j;return u|0}else if((w|0)==21152){i=j;return u|0}else if((w|0)==21153){i=j;return u|0}else if((w|0)==21162){i=j;return u|0}else if((w|0)==21163){i=j;return u|0}else if((w|0)==21159){i=j;return u|0}else if((w|0)==21160){i=j;return u|0}else if((w|0)==21161){i=j;return u|0}else if((w|0)==21144){i=j;return u|0}else if((w|0)==21145){i=j;return u|0}else if((w|0)==21146){i=j;return u|0}else if((w|0)==21164){i=j;return u|0}else if((w|0)==21165){i=j;return u|0}return 0}function t5(){var d=0,e=0,f=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0,aH=0,aJ=0,aK=0,aL=0,aM=0,aN=0,aO=0,aP=0,aQ=0.0,aR=0,aS=0,aT=0.0,aU=0,aV=0,aW=0,aX=0,aZ=0,a$=0,a0=0,a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,ba=0,bb=0,bc=0,bd=0.0,be=0,bf=0,bg=0,bh=0,bi=0,bj=0,bk=0.0,bl=0,bm=0,bn=0,bo=0,bp=0,bq=0,br=0,bs=0,bt=0,bu=0,bv=0,bw=0,bx=0,by=0,bz=0,bB=0.0,bC=0,bD=0,bE=0,bF=0,bG=0;d=i;i=i+176|0;e=d|0;f=d+32|0;j=d+56|0;k=d+64|0;l=d+88|0;n=d+112|0;o=d+136|0;p=d+152|0;c[13898]=(c[13898]|0)+1;c[6074]=iw()|0;q=c[13898]|0;r=c[1054]|0;s=c[r+(q*40&-1)+36>>2]|0;t=c[r+(q*40&-1)+32>>2]|0;u=(a[r+(q*40&-1)|0]&1)==0;r=(s|0)>0;x=c[10036]|0;y=21960;z=96232;L28193:while(1){L28195:do{if(!u){if(r){A=0;B=0;C=t;while(1){D=a[z+A|0]|0;if(D<<24>>24==(a[x+(A+C|0)|0]|0)){E=C;F=B}else{if(D<<24>>24!=36){break L28195}E=C-1|0;F=1}G=A+1|0;if((G|0)<(F+s|0)){A=G;B=F;C=E}else{break}}if((F|0)==0){H=G}else{I=y;break L28193}}else{H=0}C=a[z+H|0]|0;if((C<<24>>24|0)==36|(C<<24>>24|0)==0){I=y;break L28193}}}while(0);C=y+8|0;B=c[C>>2]|0;if((B|0)==0){I=C;break}else{y=C;z=B}}z=c[I+4>>2]|0;if((z|0)==0){J=q}else{I=q+1|0;c[13898]=I;J=I}I=p|0;q=p+8|0;y=o|0;H=o+2|0;G=o+1|0;F=o+6|0;E=o+5|0;s=o+3|0;x=o+7|0;t=n|0;r=n+8|0;u=l|0;B=l+8|0;C=k|0;A=k+8|0;D=f|0;K=f+8|0;L=j|0;j=e|0;L28211:while(1){L28213:do{if((z|0)==11){L28215:do{if((J|0)<(c[8272]|0)){e=c[1054]|0;if((a[e+(J*40&-1)|0]&1)==0){M=21687;break L28211}N=c[e+(J*40&-1)+36>>2]|0;O=e+(J*40&-1)+32|0;e=c[10036]|0;P=0;while(1){if((P|0)>=(N|0)){M=21252;break}Q=c[O>>2]|0;if((a[e+(Q+P|0)|0]|0)==(a[P+103664|0]|0)){P=P+1|0}else{R=Q;break}}if((M|0)==21252){M=0;if((P|0)==1){M=21254;break}R=c[O>>2]|0}if((N|0)>0){S=0;T=0;U=R}else{M=21686;break L28211}while(1){if((a[S+144616|0]|0)==(a[e+(S+U|0)|0]|0)){V=U;W=T}else{if((S|0)!=1){X=0;Y=0;Z=R;break}V=U-1|0;W=1}Q=S+1|0;if((Q|0)<(W+N|0)){S=Q;T=W;U=V}else{M=21260;break}}do{if((M|0)==21260){M=0;if((W|0)==0){if(!((S|0)==0|(S|0)==6)){X=0;Y=0;Z=R;break}}a[54144]=0;break L28215}}while(0);while(1){if((a[X+143008|0]|0)==(a[e+(X+Z|0)|0]|0)){_=Z;$=Y}else{if((X|0)!=1){aa=0;ab=0;ac=R;break}_=Z-1|0;$=1}O=X+1|0;if((O|0)<($+N|0)){X=O;Y=$;Z=_}else{M=21267;break}}do{if((M|0)==21267){M=0;if(($|0)==0){if(!((X|0)==0|(X|0)==3)){aa=0;ab=0;ac=R;break}}a[54160]=0;break L28215}}while(0);while(1){if((a[aa+141464|0]|0)==(a[e+(aa+ac|0)|0]|0)){ad=ac;ae=ab}else{if((aa|0)!=1){M=21685;break L28211}ad=ac-1|0;ae=1}O=aa+1|0;if((O|0)<(ae+N|0)){aa=O;ab=ae;ac=ad}else{break}}if((ae|0)==0){if(!((aa|0)==0|(aa|0)==3)){M=21684;break L28211}}a[54152]=0}else{M=21254}}while(0);if((M|0)==21254){M=0;a[54144]=0;a[54160]=0;a[54152]=0}c[13898]=J+1}else if((z|0)==36){a[43472]=0}else if((z|0)==37){c[8732]=0}else if((z|0)==42){c[14100]=4;h[7052]=-1.0}else if((z|0)==39){c[8786]=4;h[4395]=-1.0}else if((z|0)==40){c[6598]=4;h[3301]=-1.0}else if((z|0)==59){if((a[32936]&1)==0){break}a[32936]=0;if((a[30528]&1)!=0){break}b[12272]=120;w=121;a[24595]=w&255;w=w>>8;a[24596|0]=w&255;if((a[37400]&1)==0){break}aI(184192,51,1,c[m>>2]|0)}else if((z|0)==89){t6(11)}else if((z|0)==90){uh(J,222320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);h[8132]=1.0;h[8133]=.5;h[8218]=1.0;h[8219]=.5;h[8304]=1.0;h[8305]=.5;h[8390]=1.0;h[8391]=.5;h[8476]=1.0;h[8477]=.5;h[8562]=1.0;h[8563]=.5;h[8648]=1.0;h[8649]=.5;h[8734]=1.0;h[8735]=.5;h[8820]=1.0;h[8821]=.5;h[8906]=1.0;h[8907]=.5;h[8992]=1.0;h[8993]=.5}else if((z|0)==91|(z|0)==119){h[77]=.5;a[624]=0}else if((z|0)==92){L28273:do{if((J|0)<(c[8272]|0)){N=c[1054]|0;e=(a[N+(J*40&-1)|0]&1)==0;O=c[N+(J*40&-1)+36>>2]|0;L28275:do{if(e){af=c[10036]|0;ag=N+(J*40&-1)+32|0}else{P=N+(J*40&-1)+32|0;Q=c[10036]|0;ah=0;while(1){if((ah|0)>=(O|0)){break}if((a[Q+((c[P>>2]|0)+ah|0)|0]|0)==(a[ah+103664|0]|0)){ah=ah+1|0}else{af=Q;ag=P;break L28275}}if((ah|0)==1){break L28273}else{af=Q;ag=P}}}while(0);N=c[ag>>2]|0;ai=(O|0)>0;aj=56720;ak=77416;L28283:while(1){L28285:do{if(!e){if(ai){al=0;am=0;an=N;while(1){ao=a[ak+al|0]|0;if(ao<<24>>24==(a[af+(al+an|0)|0]|0)){ap=an;aq=am}else{if(ao<<24>>24!=36){break L28285}ap=an-1|0;aq=1}ar=al+1|0;if((ar|0)<(aq+O|0)){al=ar;am=aq;an=ap}else{break}}if((aq|0)==0){as=ar}else{at=aj;break L28283}}else{as=0}an=a[ak+as|0]|0;if((an<<24>>24|0)==36|(an<<24>>24|0)==0){at=aj;break L28283}}}while(0);P=aj+8|0;Q=c[P>>2]|0;if((Q|0)==0){at=P;break}else{aj=P;ak=Q}}ak=c[at+4>>2]|0;if((ak|0)>-1){uD(64813+(ak*688&-1)|0,82192,15);c[13898]=J+1;break L28213}else{uh(J,75e3,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);break L28213}}}while(0);uD(64813,82192,15);uD(65501,82192,15);uD(66189,82192,15);uD(66877,82192,15);uD(67565,82192,15);uD(68253,82192,15);uD(68941,82192,15);uD(69629,82192,15);uD(70317,82192,15);uD(71005,82192,15);uD(71693,82192,15)}else if((z|0)==61){c[7662]=0;if((c[12890]|0)==352){c[12890]=18}if((c[10058]|0)!=352){break}c[10058]=1}else if((z|0)==21){ak=c[10568]|0;if((ak|0)!=0){uu(ak)}c[10568]=0;a[42568]=0}else if((z|0)==102|(z|0)==105){c[16574]=1}else if((z|0)==50){c[17120]=0;h[8561]=10.0}else if((z|0)==127){t6(5)}else if((z|0)==121|(z|0)==124){c[16574]=1}else if((z|0)==54){c[16260]=0;h[8131]=10.0}else if((z|0)==145){t6(0)}else if((z|0)==139|(z|0)==142){c[16574]=1}else if((z|0)==68){c[16776]=0;h[8389]=10.0}else if((z|0)==66){t6(3)}else if((z|0)==71|(z|0)==73){c[16574]=1}else if((z|0)==110){c[16546]=0}else if((z|0)==129){c[16374]=0}else if((z|0)==93){uE(j|0,0,28);uu(c[1183]|0);c[1183]=0;uu(c[1184]|0);c[1184]=0;c[1204]=4;c[1205]=4;c[1206]=4;c[1207]=c[j>>2];c[4832>>2]=c[j+4>>2];c[4836>>2]=c[j+8>>2];c[4840>>2]=c[j+12>>2];c[4844>>2]=c[j+16>>2];c[4848>>2]=c[j+20>>2];c[4852>>2]=c[j+24>>2];c[1186]=0;c[1164]=0;c[1166]=1}else if((z|0)==94){uE(j|0,0,28);uu(c[1119]|0);c[1119]=0;uu(c[1120]|0);c[1120]=0;c[1140]=4;c[1141]=4;c[1142]=4;c[1143]=c[j>>2];c[4576>>2]=c[j+4>>2];c[4580>>2]=c[j+8>>2];c[4584>>2]=c[j+12>>2];c[4588>>2]=c[j+16>>2];c[4592>>2]=c[j+20>>2];c[4596>>2]=c[j+24>>2];c[1122]=0;}else if((z|0)==99){if(a[20368]|0){a[20368]=0;g[3538]=+g[5090];g[3536]=+g[5088];g[3534]=+g[5086];c[16336]=c[16336]^2;c[17024]=c[17024]^2}c[5094]=0;c[18072]=0;g[3536]=30.0;g[3538]=60.0;g[3534]=1.0;g[3540]=0.0;g[3532]=1.0}else if((z|0)==151){h[11]=1.0e-8}else if((z|0)==138){c[16202]=0}else if((z|0)==70){c[16718]=0}else if((z|0)==101){c[17234]=0}else if((z|0)==120){c[17062]=0}else if((z|0)==113){uu(c[16629]|0);c[16629]=0;uu(c[16630]|0);uD(66456,51296,192)}else if((z|0)==132){uu(c[16457]|0);c[16457]=0;uu(c[16458]|0);uD(65768,51296,192);c[16455]=-270}else if((z|0)==141){uu(c[16285]|0);c[16285]=0;uu(c[16286]|0);uD(65080,51296,192)}else if((z|0)==147){t6(7)}else if((z|0)==65){c[16680]=0}else if((z|0)==149){c[17368]=0}else if((z|0)==95){c[17540]=0}else if((z|0)==96){c[17712]=0}else if((z|0)==100){c[17884]=0}else if((z|0)==150){a[27776]=0;c[13898]=J+1}else if((z|0)==153){c[16664]=c[12782];c[66660>>2]=c[12783];c[66664>>2]=c[12784];c[66668>>2]=c[12785];c[66672>>2]=c[12786];c[66676>>2]=c[12787];c[66680>>2]=c[12788];c[66684>>2]=c[12789];c[66688>>2]=c[12790];c[66692>>2]=c[12791];c[66696>>2]=c[12792];c[66700>>2]=c[12793];c[66704>>2]=c[12794];c[66708>>2]=c[12795]}else if((z|0)==155){c[16492]=c[12782];c[65972>>2]=c[12783];c[65976>>2]=c[12784];c[65980>>2]=c[12785];c[65984>>2]=c[12786];c[65988>>2]=c[12787];c[65992>>2]=c[12788];c[65996>>2]=c[12789];c[66e3>>2]=c[12790];c[66004>>2]=c[12791];c[66008>>2]=c[12792];c[66012>>2]=c[12793];c[66016>>2]=c[12794];c[66020>>2]=c[12795]}else if((z|0)==157){c[16320]=c[12782];c[65284>>2]=c[12783];c[65288>>2]=c[12784];c[65292>>2]=c[12785];c[65296>>2]=c[12786];c[65300>>2]=c[12787];c[65304>>2]=c[12788];c[65308>>2]=c[12789];c[65312>>2]=c[12790];c[65316>>2]=c[12791];c[65320>>2]=c[12792];c[65324>>2]=c[12793];c[65328>>2]=c[12794];c[65332>>2]=c[12795]}else if((z|0)==154){c[17352]=c[12782];c[69412>>2]=c[12783];c[69416>>2]=c[12784];c[69420>>2]=c[12785];c[69424>>2]=c[12786];c[69428>>2]=c[12787];c[69432>>2]=c[12788];c[69436>>2]=c[12789];c[69440>>2]=c[12790];c[69444>>2]=c[12791];c[69448>>2]=c[12792];c[69452>>2]=c[12793];c[69456>>2]=c[12794];c[69460>>2]=c[12795]}else if((z|0)==156){c[17180]=c[12782];c[68724>>2]=c[12783];c[68728>>2]=c[12784];c[68732>>2]=c[12785];c[68736>>2]=c[12786];c[68740>>2]=c[12787];c[68744>>2]=c[12788];c[68748>>2]=c[12789];c[68752>>2]=c[12790];c[68756>>2]=c[12791];c[68760>>2]=c[12792];c[68764>>2]=c[12793];c[68768>>2]=c[12794];c[68772>>2]=c[12795]}else if((z|0)==152){c[16320]=c[12782];c[65284>>2]=c[12783];c[65288>>2]=c[12784];c[65292>>2]=c[12785];c[65296>>2]=c[12786];c[65300>>2]=c[12787];c[65304>>2]=c[12788];c[65308>>2]=c[12789];c[65312>>2]=c[12790];c[65316>>2]=c[12791];c[65320>>2]=c[12792];c[65324>>2]=c[12793];c[65328>>2]=c[12794];c[65332>>2]=c[12795];c[16492]=c[12782];c[65972>>2]=c[12783];c[65976>>2]=c[12784];c[65980>>2]=c[12785];c[65984>>2]=c[12786];c[65988>>2]=c[12787];c[65992>>2]=c[12788];c[65996>>2]=c[12789];c[66e3>>2]=c[12790];c[66004>>2]=c[12791];c[66008>>2]=c[12792];c[66012>>2]=c[12793];c[66016>>2]=c[12794];c[66020>>2]=c[12795];c[16664]=c[12782];c[66660>>2]=c[12783];c[66664>>2]=c[12784];c[66668>>2]=c[12785];c[66672>>2]=c[12786];c[66676>>2]=c[12787];c[66680>>2]=c[12788];c[66684>>2]=c[12789];c[66688>>2]=c[12790];c[66692>>2]=c[12791];c[66696>>2]=c[12792];c[66700>>2]=c[12793];c[66704>>2]=c[12794];c[66708>>2]=c[12795];c[16836]=c[12782];c[67348>>2]=c[12783];c[67352>>2]=c[12784];c[67356>>2]=c[12785];c[67360>>2]=c[12786];c[67364>>2]=c[12787];c[67368>>2]=c[12788];c[67372>>2]=c[12789];c[67376>>2]=c[12790];c[67380>>2]=c[12791];c[67384>>2]=c[12792];c[67388>>2]=c[12793];c[67392>>2]=c[12794];c[67396>>2]=c[12795];c[17008]=c[12782];c[68036>>2]=c[12783];c[68040>>2]=c[12784];c[68044>>2]=c[12785];c[68048>>2]=c[12786];c[68052>>2]=c[12787];c[68056>>2]=c[12788];c[68060>>2]=c[12789];c[68064>>2]=c[12790];c[68068>>2]=c[12791];c[68072>>2]=c[12792];c[68076>>2]=c[12793];c[68080>>2]=c[12794];c[68084>>2]=c[12795];c[17180]=c[12782];c[68724>>2]=c[12783];c[68728>>2]=c[12784];c[68732>>2]=c[12785];c[68736>>2]=c[12786];c[68740>>2]=c[12787];c[68744>>2]=c[12788];c[68748>>2]=c[12789];c[68752>>2]=c[12790];c[68756>>2]=c[12791];c[68760>>2]=c[12792];c[68764>>2]=c[12793];c[68768>>2]=c[12794];c[68772>>2]=c[12795];c[17352]=c[12782];c[69412>>2]=c[12783];c[69416>>2]=c[12784];c[69420>>2]=c[12785];c[69424>>2]=c[12786];c[69428>>2]=c[12787];c[69432>>2]=c[12788];c[69436>>2]=c[12789];c[69440>>2]=c[12790];c[69444>>2]=c[12791];c[69448>>2]=c[12792];c[69452>>2]=c[12793];c[69456>>2]=c[12794];c[69460>>2]=c[12795];c[17524]=c[12782];c[70100>>2]=c[12783];c[70104>>2]=c[12784];c[70108>>2]=c[12785];c[70112>>2]=c[12786];c[70116>>2]=c[12787];c[70120>>2]=c[12788];c[70124>>2]=c[12789];c[70128>>2]=c[12790];c[70132>>2]=c[12791];c[70136>>2]=c[12792];c[70140>>2]=c[12793];c[70144>>2]=c[12794];c[70148>>2]=c[12795];c[17696]=c[12782];c[70788>>2]=c[12783];c[70792>>2]=c[12784];c[70796>>2]=c[12785];c[70800>>2]=c[12786];c[70804>>2]=c[12787];c[70808>>2]=c[12788];c[70812>>2]=c[12789];c[70816>>2]=c[12790];c[70820>>2]=c[12791];c[70824>>2]=c[12792];c[70828>>2]=c[12793];c[70832>>2]=c[12794];c[70836>>2]=c[12795];c[17868]=c[12782];c[71476>>2]=c[12783];c[71480>>2]=c[12784];c[71484>>2]=c[12785];c[71488>>2]=c[12786];c[71492>>2]=c[12787];c[71496>>2]=c[12788];c[71500>>2]=c[12789];c[71504>>2]=c[12790];c[71508>>2]=c[12791];c[71512>>2]=c[12792];c[71516>>2]=c[12793];c[71520>>2]=c[12794];c[71524>>2]=c[12795];c[18040]=c[12782];c[72164>>2]=c[12783];c[72168>>2]=c[12784];c[72172>>2]=c[12785];c[72176>>2]=c[12786];c[72180>>2]=c[12787];c[72184>>2]=c[12788];c[72188>>2]=c[12789];c[72192>>2]=c[12790];c[72196>>2]=c[12791];c[72200>>2]=c[12792];c[72204>>2]=c[12793];c[72208>>2]=c[12794];c[72212>>2]=c[12795]}else if((z|0)==5){L28351:do{if((J|0)<(c[8272]|0)){ak=c[1054]|0;aj=(a[ak+(J*40&-1)|0]&1)==0;O=c[ak+(J*40&-1)+36>>2]|0;L28353:do{if(aj){au=c[10036]|0;av=ak+(J*40&-1)+32|0}else{N=ak+(J*40&-1)+32|0;ai=c[10036]|0;e=0;while(1){if((e|0)>=(O|0)){M=21222;break}if((a[ai+((c[N>>2]|0)+e|0)|0]|0)==(a[e+103664|0]|0)){e=e+1|0}else{aw=0;break}}if((M|0)==21222){M=0;if((e|0)==1){break L28351}else{aw=0}}while(1){if((aw|0)>=(O|0)){M=21226;break}if((a[ai+((c[N>>2]|0)+aw|0)|0]|0)==(a[aw+150688|0]|0)){aw=aw+1|0}else{ax=0;M=21227;break}}if((M|0)==21226){M=0;if((aw|0)!=2){ax=0;M=21227}}if((M|0)==21227){while(1){M=0;if((ax|0)>=(O|0)){break}if((a[ai+((c[N>>2]|0)+ax|0)|0]|0)==(a[ax+137768|0]|0)){ax=ax+1|0;M=21227}else{au=ai;av=N;break L28353}}if((ax|0)!=3){au=ai;av=N;break}}c[16335]=0;c[16507]=0;c[13898]=J+1;break L28213}}while(0);ak=c[av>>2]|0;e=(O|0)>0;Q=56720;P=77416;L28373:while(1){L28375:do{if(!aj){if(e){ah=0;an=0;am=ak;while(1){al=a[P+ah|0]|0;if(al<<24>>24==(a[au+(ah+am|0)|0]|0)){ay=am;aA=an}else{if(al<<24>>24!=36){break L28375}ay=am-1|0;aA=1}aB=ah+1|0;if((aB|0)<(aA+O|0)){ah=aB;an=aA;am=ay}else{break}}if((aA|0)==0){aC=aB}else{aD=Q;break L28373}}else{aC=0}am=a[P+aC|0]|0;if((am<<24>>24|0)==36|(am<<24>>24|0)==0){aD=Q;break L28373}}}while(0);N=Q+8|0;ai=c[N>>2]|0;if((ai|0)==0){aD=N;break}else{Q=N;P=ai}}P=c[aD+4>>2]|0;if((P|0)<=-1){break L28213}c[64652+(P*688&-1)>>2]=0;c[13898]=J+1;break L28213}}while(0);c[16163]=0;c[16335]=0;c[16507]=0;c[16679]=0;c[16851]=0;c[17023]=0;c[17195]=0;c[17367]=0;c[17539]=0;c[17711]=0;c[17883]=0}else if((z|0)==62){uD(54016,50688,112);a[54016]=110}else if((z|0)==77){h[3818]=1.0}else if((z|0)==78){h[3817]=1.0}else if((z|0)==79){if((a[30528]&1)==0){break}a[30528]=0;P=(a[32936]&1)==0;if(!(P|(c[17539]|0)==0)){h[8773]=-5.0;h[8774]=-5.0}if(!P){break}b[12272]=120;if((a[37400]&1)==0){break}aI(199840,33,1,c[m>>2]|0)}else if((z|0)==87){P=43240;while(1){Q=c[P>>2]|0;if((Q|0)==0){aE=0;break}if((aY(215528,c[Q+4>>2]|0)|0)==0){aE=Q;break}else{P=Q|0}}if((a[33512]&1)!=0){lm()}c[8026]=0;do{if((a[14080]&1)!=0){if(a[13032]|0){P=c[(c[3524]|0)+104>>2]|0;if((P|0)!=0){cS[P&511]()}a[13032]=0}if(a[14088]|0){cS[c[(c[3524]|0)+44>>2]&511]();a[14088]=0}if((a[14080]&1)==0){break}cS[c[(c[3524]|0)+40>>2]&511]();a[14080]=0;c[10028]=0}}while(0);if((aE|0)!=0){P=c[aE+24>>2]|0;c[3524]=lx(P,uA(P|0)|0)|0}a[25280]=0}else if((z|0)==12){c[13366]=5;c[13378]=0;c[13368]=4;c[13374]=5;c[13372]=0}else if((z|0)==60){c[13898]=J+1;aI(193712,29,1,c[m>>2]|0)}else if((z|0)==23){uE(y|0,0,11);P=c[1054]|0;Q=c[P+(J*40&-1)+36>>2]|0;O=c[P+(J*40&-1)+32>>2]|0;ak=(a[P+(J*40&-1)|0]&1)==0;e=(Q|0)>0;aj=c[10036]|0;ai=56720;N=77416;L28426:while(1){L28428:do{if(!ak){if(e){am=0;an=0;ah=O;while(1){al=a[N+am|0]|0;if(al<<24>>24==(a[aj+(am+ah|0)|0]|0)){aF=ah;aG=an}else{if(al<<24>>24!=36){break L28428}aF=ah-1|0;aG=1}aH=am+1|0;if((aH|0)<(aG+Q|0)){am=aH;an=aG;ah=aF}else{break}}if((aG|0)==0){aJ=aH}else{aK=ai;break L28426}}else{aJ=0}ah=a[N+aJ|0]|0;if((ah<<24>>24|0)==36|(ah<<24>>24|0)==0){aK=ai;break L28426}}}while(0);ah=ai+8|0;an=c[ah>>2]|0;if((an|0)==0){aK=ah;break}else{ai=ah;N=an}}N=c[aK+4>>2]|0;L28441:do{if((N|0)>-1){a[o+N|0]=1;aL=J;aM=c[8272]|0}else{ai=c[8272]|0;e=(ai|0)>(J|0);L28443:do{if(e){if(ak){aL=J;aM=ai;break L28441}else{aN=0}while(1){if((aN|0)>=(Q|0)){M=21306;break}if((a[aj+(aN+O|0)|0]|0)==(a[aN+150688|0]|0)){aN=aN+1|0}else{aO=0;M=21307;break}}if((M|0)==21306){M=0;if((aN|0)!=2){aO=0;M=21307}}if((M|0)==21307){while(1){M=0;if((aO|0)>=(Q|0)){break}if((a[aj+(aO+O|0)|0]|0)==(a[aO+148800|0]|0)){aO=aO+1|0;M=21307}else{break L28443}}if((aO|0)!=2){break}}a[G]=1;a[H]=1;an=J+1|0;c[13898]=an;aL=an;aM=ai;break L28441}}while(0);if(ak){M=21313}else{an=a[aj+O|0]|0;if(!((an<<24>>24|0)==39|(an<<24>>24|0)==34)){M=21313}}do{if((M|0)==21313){M=0;if(!e){break}if(ak){aL=J;aM=ai;break L28441}else{aP=0}while(1){if((aP|0)>=(Q|0)){break}if((a[aj+(aP+O|0)|0]|0)==(a[aP+103664|0]|0)){aP=aP+1|0}else{aL=J;aM=ai;break L28441}}if((aP|0)!=1){aL=J;aM=ai;break L28441}}}while(0);uE(y|0,1,11);aL=J;aM=ai}}while(0);if((aL|0)<(aM|0)){if((a[P+(aL*40&-1)|0]&1)==0){break}O=c[P+(aL*40&-1)+36>>2]|0;Q=P+(aL*40&-1)+32|0;ak=0;while(1){if((ak|0)>=(O|0)){break}if((a[aj+((c[Q>>2]|0)+ak|0)|0]|0)==(a[ak+103664|0]|0)){ak=ak+1|0}else{break L28213}}if((ak|0)!=1){break}}if((a[H]&1)!=0){c[16560]=6758437;a[66188]=1}if((a[G]&1)!=0){c[16388]=6758437;a[65500]=1}if((a[y]&1)!=0){c[16216]=6758437;a[64812]=1}if((a[F]&1)!=0){c[17248]=6758437;a[68940]=1}if((a[E]&1)!=0){c[17076]=6758437;a[68252]=1}if((a[s]&1)!=0){c[16732]=6758437;a[66876]=1}if((a[x]&1)==0){break}c[17420]=6758437;a[69628]=1}else if((z|0)==3){h[9040]=1.0}else if((z|0)==48){c[16604]=0;h[8303]=10.0}else if((z|0)==6){h[7077]=0.0}else if((z|0)==8){c[11692]=0}else if((z|0)==32){if((c[8272]|0)<=(J|0)){break}Q=c[1054]|0;aj=(a[Q+(J*40&-1)|0]&1)==0;L28502:do{if(!aj){O=c[Q+(J*40&-1)+36>>2]|0;P=Q+(J*40&-1)+32|0;N=c[10036]|0;e=0;while(1){if((e|0)>=(O|0)){M=21386;break}if((a[N+((c[P>>2]|0)+e|0)|0]|0)==(a[e+166752|0]|0)){e=e+1|0}else{break}}do{if((M|0)==21386){M=0;if((e|0)!=5){break}c[8798]=0;c[13898]=J+1;break L28213}}while(0);if(aj){break}e=c[Q+(J*40&-1)+36>>2]|0;P=Q+(J*40&-1)+32|0;N=c[10036]|0;O=0;while(1){if((O|0)>=(e|0)){break}if((a[N+((c[P>>2]|0)+O|0)|0]|0)==(a[O+103664|0]|0)){O=O+1|0}else{break L28502}}if((O|0)==1){break L28213}}}while(0);Q=is(l)|0;aj=c[Q>>2]|0;if((aj|0)==1){aQ=+(c[Q+8>>2]|0)}else if((aj|0)==2){aQ=+h[Q+8>>3]}else if((aj|0)==3){aQ=+uz(c[Q+8>>2]|0,0)}else{M=21397;break L28211}if((c[u>>2]|0)==3){uu(c[B>>2]|0);c[u>>2]=1}Q=~~aQ;aj=0;ak=43264;while(1){aR=c[ak>>2]|0;if((aR|0)==0){break L28213}aS=aR|0;if((c[aR+4>>2]|0)==(Q|0)){break}else{aj=aR;ak=aS}}ak=c[aS>>2]|0;if((c[10816]|0)==(aR|0)){c[10816]=ak}else{c[aj>>2]=ak}uu(aR)}else if((z|0)==20){ak=c[12886]|0;if((ak|0)!=0){uu(ak)}c[12886]=0;uu(c[8270]|0);c[8270]=0}else if((z|0)==27){iY(c[10828]|0);c[10828]=0;c[9344]=10;c[9342]=10}else if((z|0)==30){L28539:do{if((J|0)<(c[8272]|0)){ak=c[1054]|0;L28541:do{if((a[ak+(J*40&-1)|0]&1)!=0){Q=c[ak+(J*40&-1)+36>>2]|0;P=ak+(J*40&-1)+32|0;N=c[10036]|0;e=0;while(1){if((e|0)>=(Q|0)){break}if((a[N+((c[P>>2]|0)+e|0)|0]|0)==(a[e+103664|0]|0)){e=e+1|0}else{break L28541}}if((e|0)==1){break L28539}}}while(0);ak=is(n)|0;O=c[ak>>2]|0;if((O|0)==1){aT=+(c[ak+8>>2]|0)}else if((O|0)==2){aT=+h[ak+8>>3]}else if((O|0)==3){aT=+uz(c[ak+8>>2]|0,0)}else{M=21361;break L28211}if((c[t>>2]|0)==3){uu(c[r>>2]|0);c[t>>2]=1}ak=~~aT;aU=c[13898]|0;if((aU|0)<(c[8272]|0)){O=c[1054]|0;if((a[O+(aU*40&-1)|0]&1)==0){M=21683;break L28211}P=c[O+(aU*40&-1)+36>>2]|0;N=O+(aU*40&-1)+32|0;O=c[10036]|0;Q=0;while(1){if((Q|0)>=(P|0)){break}if((a[O+((c[N>>2]|0)+Q|0)|0]|0)==(a[Q+103664|0]|0)){Q=Q+1|0}else{M=21681;break L28211}}if((Q|0)==1){aV=43288;aW=0}else{M=21682;break L28211}}else{aV=43288;aW=0}while(1){aX=c[aV>>2]|0;if((aX|0)==0){break L28213}aZ=aX|0;if((c[aX+4>>2]|0)==(ak|0)){break}else{aV=aZ;aW=aX}}ak=c[aZ>>2]|0;if((aW|0)==0){c[10822]=ak}else{c[aW>>2]=ak}ak=c[aX+60>>2]|0;if((ak|0)!=0){uu(ak)}ak=c[aX+64>>2]|0;if((ak|0)!=0){uu(ak)}uu(aX);break L28213}}while(0);aj=c[10822]|0;if((aj|0)==0){break}else{a$=aj}do{c[10822]=c[a$>>2];aj=c[a$+60>>2]|0;if((aj|0)!=0){uu(aj)}aj=c[a$+64>>2]|0;if((aj|0)!=0){uu(aj)}uu(a$);a$=c[10822]|0;}while((a$|0)!=0)}else if((z|0)==33){uq(32,0)}else if((z|0)==34){uu(c[12908]|0);bA(5,179864);bA(2,179864);aj=bA(5,0)|0;if((aj|0)==0){a0=0}else{a0=bP(aj|0)|0}c[12908]=a0}else if((z|0)==35){L28592:do{if((J|0)<(c[8272]|0)){aj=c[1054]|0;ak=c[aj+(J*40&-1)+36>>2]|0;L28594:do{if((a[aj+(J*40&-1)|0]&1)!=0){Q=aj+(J*40&-1)+32|0;N=c[10036]|0;O=0;while(1){if((O|0)>=(ak|0)){break}if((a[N+((c[Q>>2]|0)+O|0)|0]|0)==(a[O+103664|0]|0)){O=O+1|0}else{break L28594}}if((O|0)==1){a1=0;M=21418;break L28592}}}while(0);if((ak|0)>0){Q=0;N=J;e=aj;while(1){a2=e+(N*40&-1)+32|0;a3=(c[a2>>2]|0)+Q|0;P=(c[10036]|0)+a3|0;ai=a[P]|0;do{if(ai<<24>>24==114){an=(a[69604]&1)==0;a[69604]=0;h[8701]=0.0;if(an){a4=N;a5=e;a6=7;break}ju();a4=c[13898]|0;a5=c[1054]|0;a6=7}else{do{if((a_(P|0,108208,2)|0)==0){a7=6}else{if((a_(P|0,108120,2)|0)==0){a7=5;break}if((a_(P|0,124768,2)|0)==0){a7=4;break}if((a_(P|0,88416,2)|0)==0){a7=3;break}if(ai<<24>>24==120){a7=2;break}if(ai<<24>>24==121){a7=1;break}if(ai<<24>>24==122){a7=0}else{M=21423;break L28211}}}while(0);an=c[56724+(a7<<3)>>2]|0;a[64788+(an*688&-1)|0]=0;h[64792+(an*688&-1)>>3]=0.0;a4=N;a5=e;a6=a7}}while(0);ai=(uA(c[56720+(a6<<3)>>2]|0)|0)+Q|0;if((ai|0)<(c[a5+(a4*40&-1)+36>>2]|0)){Q=ai;N=a4;e=a5}else{a8=a4;break}}}else{a8=J}c[13898]=a8+1}else{a1=0;M=21418}}while(0);if((M|0)==21418){while(1){M=0;if((a1|0)!=7){a[64788+(a1*688&-1)|0]=0;h[64792+(a1*688&-1)>>3]=0.0;e=a1+1|0;if((e|0)<11){a1=e;M=21418;continue}else{break}}e=(a[69604]&1)==0;a[69604]=0;h[8701]=0.0;if(e){a1=8;M=21418;continue}ju();a1=8;M=21418}}c[6928]=0}else if((z|0)==17){c[11732]=10;c[11756]=10;c[11734]=1;c[11752]=1;h[5865]=1.0;h[5864]=1.0;a[47032]=0}else if((z|0)==18){b[12272]=120;w=121;a[24595]=w&255;w=w>>8;a[24596|0]=w&255}else if((z|0)==19){c[11252]=0}else if((z|0)==84){a[46752]=0}else if((z|0)==86){e=c[3526]|0;if((e|0)!=0){az(e|0)}c[3526]=0;a[14112]=0}else if((z|0)==24){a[65036]=0;a[65037]=0;a[65724]=0;a[65725]=0;a[66412]=0;a[66413]=0;a[67100]=0;a[67101]=0;a[67788]=0;a[67789]=0;a[68476]=0;a[68477]=0;a[69164]=0;a[69165]=0;a[69852]=0;a[69853]=0;a[70540]=0;a[70541]=0;a[71228]=0;a[71229]=0;a[71916]=0;a[71917]=0}else if((z|0)==25){a[38984]=0}else if((z|0)==26){M=21341;break L28211}else if((z|0)==13){c[11690]=0}else if((z|0)==28){a[36120]=0}else if((z|0)==29){a[36288]=0}else if((z|0)==44){c[8496]=0;hM()}else if((z|0)==45){lm()}else if((z|0)==41){c[1066]=4;h[535]=-1.0}else if((z|0)==15){e=c[1054]|0;N=c[e+(J*40&-1)+36>>2]|0;Q=c[e+(J*40&-1)+32>>2]|0;L28643:do{if((a[e+(J*40&-1)|0]&1)==0){M=21452}else{if((N|0)<=0){a[47712]=0;break}aj=c[10036]|0;ak=0;ai=0;P=Q;while(1){if((a[ak+115448|0]|0)==(a[aj+(ak+P|0)|0]|0)){a9=P;ba=ai}else{if((ak|0)!=4){M=21452;break L28643}a9=P-1|0;ba=1}bb=ak+1|0;if((bb|0)<(ba+N|0)){ak=bb;ai=ba;P=a9}else{break}}if((ba|0)!=0|(bb|0)==4){a[47712]=0}else{a[47712]=0;if((bb|0)!=8){break}}c[13898]=J+1;break L28213}}while(0);if((M|0)==21452){M=0;a[47712]=0}uu(c[8528]|0);c[8528]=0;a[47120]=0;uu(c[11948]|0);c[11948]=bP(199184)|0;N=c[12168]|0;Q=c[11862]|0;if((Q|0)>0){e=0;P=Q;while(1){Q=N+(e*232&-1)+224|0;ai=c[Q>>2]|0;if((ai|0)==0){bc=P}else{uu(ai);c[Q>>2]=0;bc=c[11862]|0}Q=e+1|0;if((Q|0)<(bc|0)){e=Q;P=bc}else{break}}}c[11862]=0;c[12202]=c[12200];c[12206]=0}else if((z|0)==9){h[7030]=-1.0;a[56232]=1}else if((z|0)==10){a[35888]=0}else if((z|0)==4){L28672:do{if((J|0)<(c[8272]|0)){P=c[1054]|0;L28674:do{if((a[P+(J*40&-1)|0]&1)!=0){e=c[P+(J*40&-1)+36>>2]|0;N=P+(J*40&-1)+32|0;Q=c[10036]|0;ai=0;while(1){if((ai|0)>=(e|0)){break}if((a[Q+((c[N>>2]|0)+ai|0)|0]|0)==(a[ai+103664|0]|0)){ai=ai+1|0}else{break L28674}}if((ai|0)==1){break L28672}}}while(0);P=is(p)|0;N=c[P>>2]|0;if((N|0)==2){bd=+h[P+8>>3]}else if((N|0)==3){bd=+uz(c[P+8>>2]|0,0)}else if((N|0)==1){bd=+(c[P+8>>2]|0)}else{M=21199;break L28211}if((c[I>>2]|0)==3){uu(c[q>>2]|0);c[I>>2]=1}P=~~bd;be=c[13898]|0;if((be|0)<(c[8272]|0)){N=c[1054]|0;if((a[N+(be*40&-1)|0]&1)==0){M=21690;break L28211}Q=c[N+(be*40&-1)+36>>2]|0;e=N+(be*40&-1)+32|0;N=c[10036]|0;ak=0;while(1){if((ak|0)>=(Q|0)){break}if((a[N+((c[e>>2]|0)+ak|0)|0]|0)==(a[ak+103664|0]|0)){ak=ak+1|0}else{M=21691;break L28211}}if((ak|0)==1){bf=43304;bg=0}else{M=21692;break L28211}}else{bf=43304;bg=0}while(1){bh=c[bf>>2]|0;if((bh|0)==0){M=21215;break L28211}bi=bh|0;if((c[bh+4>>2]|0)==(P|0)){break}else{bf=bi;bg=bh}}P=c[bi>>2]|0;if((bg|0)==0){c[10826]=P}else{c[bg>>2]=P}uu(bh);break L28213}}while(0);P=c[10826]|0;if((P|0)==0){break}else{bj=P}do{c[10826]=c[bj>>2];uu(bj);bj=c[10826]|0;}while((bj|0)!=0)}else if((z|0)==64){uu(c[16801]|0);c[16801]=0;uu(c[16802]|0);uD(67144,51296,192);c[16799]=-270}else if((z|0)==104){uu(c[17317]|0);c[17317]=0;uu(c[17318]|0);uD(69208,51296,192)}else if((z|0)==123){uu(c[17145]|0);c[17145]=0;uu(c[17146]|0);uD(68520,51296,192);c[17143]=-270}else if((z|0)==116){c[16508]=0}else if((z|0)==107){c[17196]=0}else if((z|0)==135){c[16336]=0}else if((z|0)==126){c[17024]=0}else if((z|0)==144){c[16164]=0}else if((z|0)==80){ea(0,0)}else if((z|0)==81){uu(c[57238]|0);c[57238]=0}else if((z|0)==75){L28718:do{if((J|0)<(c[8272]|0)){P=c[1054]|0;L28720:do{if((a[P+(J*40&-1)|0]&1)!=0){ak=c[P+(J*40&-1)+36>>2]|0;e=P+(J*40&-1)+32|0;N=c[10036]|0;Q=0;while(1){if((Q|0)>=(ak|0)){break}if((a[N+((c[e>>2]|0)+Q|0)|0]|0)==(a[Q+103664|0]|0)){Q=Q+1|0}else{break L28720}}if((Q|0)==1){break L28718}}}while(0);P=is(k)|0;e=c[P>>2]|0;if((e|0)==1){bk=+(c[P+8>>2]|0)}else if((e|0)==2){bk=+h[P+8>>3]}else if((e|0)==3){bk=+uz(c[P+8>>2]|0,0)}else{M=21503;break L28211}if((c[C>>2]|0)==3){uu(c[A>>2]|0);c[C>>2]=1}P=~~bk;bl=c[13898]|0;if((bl|0)<(c[8272]|0)){e=c[1054]|0;if((a[e+(bl*40&-1)|0]&1)==0){M=21694;break L28211}N=c[e+(bl*40&-1)+36>>2]|0;ak=e+(bl*40&-1)+32|0;e=c[10036]|0;ai=0;while(1){if((ai|0)>=(N|0)){break}if((a[e+((c[ak>>2]|0)+ai|0)|0]|0)==(a[ai+103664|0]|0)){ai=ai+1|0}else{M=21693;break L28211}}if((ai|0)==1){bm=43272;bn=0}else{M=21695;break L28211}}else{bm=43272;bn=0}while(1){bo=c[bm>>2]|0;if((bo|0)==0){break L28213}bp=bo|0;if((c[bo+4>>2]|0)==(P|0)){break}else{bm=bp;bn=bo}}P=c[bp>>2]|0;if((bn|0)==0){c[10818]=P}else{c[bn>>2]=P}if((c[bo+12>>2]|0)==4){uu(c[(bo+104|0)+4>>2]|0)}uu(bo);break L28213}}while(0);P=c[10818]|0;if((P|0)==0){break}else{bq=P}do{c[10818]=c[bq>>2];if((c[bq+12>>2]|0)==4){uu(c[(bq+104|0)+4>>2]|0)}uu(bq);bq=c[10818]|0;}while((bq|0)!=0)}else if((z|0)==117){t6(2)}else if((z|0)==111|(z|0)==114){c[16574]=1}else if((z|0)==52){c[16432]=0;h[8217]=10.0}else if((z|0)==136){t6(1)}else if((z|0)==130|(z|0)==133){c[16574]=1}else if((z|0)==46){c[17292]=0;h[8647]=10.0}else if((z|0)==108){t6(6)}else if((z|0)==56){h[3296]=0.0;h[4384]=0.0;h[7048]=0.0;h[531]=0.0}else if((z|0)==57){g[184]=0.0;g[44]=0.0}else if((z|0)==58){if((a[33512]&1)!=0){M=21466;break L28211}lj(0);P=c[8244]|0;if((P|0)==0){break}uu(P);c[8244]=0}else if((z|0)==82){iO(c[10814]|0);c[10814]=0;iY(c[10828]|0);c[10828]=0;c[6352]=100;c[6350]=100}else if((z|0)==83){g[178]=1.0;g[38]=1.0;g[2]=1.0}else if((z|0)==85){P=c[8272]|0;L28774:do{if((J|0)<(P|0)){ai=c[1054]|0;ak=(a[ai+(J*40&-1)|0]&1)==0;e=c[ai+(J*40&-1)+36>>2]|0;L28776:do{if(ak){br=c[10036]|0;bs=ai+(J*40&-1)+32|0}else{N=ai+(J*40&-1)+32|0;aj=c[10036]|0;O=0;while(1){if((O|0)>=(e|0)){break}if((a[aj+((c[N>>2]|0)+O|0)|0]|0)==(a[O+103664|0]|0)){O=O+1|0}else{br=aj;bs=N;break L28776}}if((O|0)==1){break L28774}else{br=aj;bs=N}}}while(0);Q=c[bs>>2]|0;an=(e|0)>0;ah=21680;am=116616;L28784:while(1){L28786:do{if(!ak){if(an){al=0;ao=0;bt=Q;while(1){bu=a[am+al|0]|0;if(bu<<24>>24==(a[br+(al+bt|0)|0]|0)){bv=bt;bw=ao}else{if(bu<<24>>24!=36){break L28786}bv=bt-1|0;bw=1}bx=al+1|0;if((bx|0)<(bw+e|0)){al=bx;ao=bw;bt=bv}else{break}}if((bw|0)==0){by=bx}else{bz=ah;break L28784}}else{by=0}bt=a[am+by|0]|0;if((bt<<24>>24|0)==36|(bt<<24>>24|0)==0){bz=ah;break L28784}}}while(0);N=ah+8|0;aj=c[N>>2]|0;if((aj|0)==0){bz=N;break}else{ah=N;am=aj}}am=c[bz+4>>2]|0;if((am|0)==4){c[12372]=0;c[12373]=100;c[12374]=0;c[12376]=0;c[13898]=J+1;break L28213}else if((am|0)==10){c[9670]=3;c[9671]=2;c[13898]=J+1;break L28213}else if((am|0)==5){t8(c[10824]|0);c[10824]=0;c[13898]=(c[13898]|0)+1;break L28213}else if((am|0)==8){uD(49160,1144,272);c[13898]=J+1;break L28213}else if((am|0)==1){c[12890]=18;c[13898]=J+1;break L28213}else if((am|0)==2){c[10058]=1;c[13898]=J+1;break L28213}else if((am|0)==3){ah=J+1|0;c[13898]=ah;L28807:do{if((ah|0)<(P|0)){L28809:do{if((a[ai+(ah*40&-1)|0]&1)!=0){e=c[ai+(ah*40&-1)+36>>2]|0;Q=ai+(ah*40&-1)+32|0;an=0;while(1){if((an|0)>=(e|0)){break}if((a[br+((c[Q>>2]|0)+an|0)|0]|0)==(a[an+103664|0]|0)){an=an+1|0}else{break L28809}}if((an|0)==1){break L28807}}}while(0);Q=is(f)|0;e=c[Q>>2]|0;if((e|0)==2){bB=+h[Q+8>>3]}else if((e|0)==3){bB=+uz(c[Q+8>>2]|0,0)}else if((e|0)==1){bB=+(c[Q+8>>2]|0)}else{M=21560;break L28211}if((c[D>>2]|0)==3){uu(c[K>>2]|0);c[D>>2]=1}Q=~~bB;e=0;ak=43280;while(1){bC=c[ak>>2]|0;if((bC|0)==0){break L28213}bD=bC|0;if((c[bC+4>>2]|0)==(Q|0)){break}else{e=bC;ak=bD}}ak=c[bD>>2]|0;if((c[10820]|0)==(bC|0)){c[10820]=ak}else{c[e>>2]=ak}uu(bC);break L28213}}while(0);ah=c[10820]|0;if((ah|0)==0){break L28213}else{bE=ah}while(1){c[10820]=c[bE>>2];uu(bE);bE=c[10820]|0;if((bE|0)==0){break L28213}}}else if((am|0)==6){uD(50800,1688,272);c[13898]=J+1;break L28213}else if((am|0)==7){uD(49520,1416,272);c[13898]=J+1;break L28213}else if((am|0)==11){uE(L|0,0,3);ah=56248;c[ah>>2]=0;c[ah+4>>2]=0;h[7032]=1.5;a[56264]=1;a[56265]=a[L]|0;a[56266|0]=a[L+1|0]|0;a[56267|0]=a[L+2|0]|0;c[14067]=6;c[14068]=252;c[14069]=0;h[7035]=1.0;c[14072]=1;c[14073]=0;c[13898]=J+1;break L28213}else{M=21577;break L28211}}}while(0);c[12890]=18;c[10058]=1;P=c[10820]|0;if((P|0)==0){bF=J}else{ah=P;do{c[10820]=c[ah>>2];uu(ah);ah=c[10820]|0;}while((ah|0)!=0);bF=c[13898]|0}c[12372]=0;c[12373]=100;c[12374]=0;c[12376]=0;uD(49160,1144,272);uD(50800,1688,272);uD(49520,1416,272);c[9670]=3;c[9671]=2;uE(L|0,0,3);ah=56248;c[ah>>2]=0;c[ah+4>>2]=0;h[7032]=1.5;a[56264]=1;a[56265]=a[L]|0;a[56266|0]=a[L+1|0]|0;a[56267|0]=a[L+2|0]|0;c[14067]=6;c[14068]=252;c[14069]=0;h[7035]=1.0;c[14072]=1;c[14073]=0;c[13898]=bF+1}else{M=21675;break L28211}}while(0);if(!(ix(c[6074]|0)|0)){M=21678;break}c[13898]=J}if((M|0)==21692){uf(be,135968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21693){uf(bl,170992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21694){uf(bl,170992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21695){uf(bl,170992,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21215){uf(be,134376,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21675){uf(J,148616,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21678){e7(0);bl=c[6074]|0;if((bl|0)==0){c[6074]=0;i=d;return}else{bG=bl}while(1){bl=c[bG>>2]|0;uu(c[bG+12>>2]|0);uu(bG);if((bl|0)==0){break}else{bG=bl}}c[6074]=0;i=d;return}else if((M|0)==21503){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21560){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21577){uf(J,209800,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21423){c[a2>>2]=a3;uf(c[13898]|0,174808,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21341){uf(J,153432,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21361){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21466){uf(J,179040,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21199){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21397){uf(-1,213288,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21681){uf(aU,161624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21682){uf(aU,161624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21683){uf(aU,161624,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21684){uf(J,139712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21685){uf(J,139712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21686){uf(J,139712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21687){uf(J,139712,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21690){uf(be,135968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((M|0)==21691){uf(be,135968,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function t6(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0;d=i;i=i+32|0;e=d|0;uE(e|0,0,28);f=b>>>0<11;g=f?b:0;j=f?b+1|0:11;if(g>>>0<j>>>0){k=g}else{i=d;return}do{c[64916+(k*688&-1)>>2]=0;g=64924+(k*688&-1)|0;b=c[g>>2]|0;if((b|0)!=0){uu(b);c[g>>2]=0}uE(64928+(k*688&-1)|0,0,16);c[64984+(k*688&-1)>>2]=4;c[64988+(k*688&-1)>>2]=4;c[64992+(k*688&-1)>>2]=4;g=(64648+(k*688&-1)|0)+348|0;c[g>>2]=c[e>>2];c[g+4>>2]=c[e+4>>2];c[g+8>>2]=c[e+8>>2];c[g+12>>2]=c[e+12>>2];c[g+16>>2]=c[e+16>>2];c[g+20>>2]=c[e+20>>2];c[g+24>>2]=c[e+24>>2];a[65024+(k*688&-1)|0]=0;c[65032+(k*688&-1)>>2]=0;h[65056+(k*688&-1)>>3]=1.0;h[65064+(k*688&-1)>>3]=.5;a[65072+(k*688&-1)|0]=1;a[65272+(k*688&-1)|0]=0;g=64944+(k*688&-1)|0;b=c[g>>2]|0;if((b|0)!=0){f=b;while(1){b=c[f+16>>2]|0;l=c[f+8>>2]|0;if((l|0)!=0){uu(l)}uu(f);if((b|0)==0){break}else{f=b}}}c[g>>2]=0;k=k+1|0;}while(k>>>0<j>>>0);i=d;return}function t7(){var d=0,e=0,f=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0.0,K=0;d=i;i=i+40|0;e=d|0;f=d+8|0;j=a[37400]&1;c[13898]=(c[13898]|0)+1;e7(4);k=c[13898]|0;l=c[1054]|0;n=c[l+(k*40&-1)+36>>2]|0;o=c[l+(k*40&-1)+32>>2]|0;p=(a[l+(k*40&-1)|0]&1)==0;L28894:do{if((n|0)>0&(p^1)){l=c[10036]|0;q=0;r=0;s=o;while(1){if((a[q+131368|0]|0)==(a[l+(q+s|0)|0]|0)){t=s;u=r}else{if((q|0)!=3){break L28894}t=s-1|0;u=1}x=q+1|0;if((x|0)<(u+n|0)){q=x;r=u;s=t}else{break}}if((u|0)==0){if(!((q|0)==10|(q|0)==2)){break}}c[13898]=k+1;i=d;return}}while(0);L28908:do{if(!((c[8272]|0)<=(k|0)|p)){u=c[10036]|0;t=0;while(1){if((t|0)>=(n|0)){break}if((a[u+(o+t|0)|0]|0)==(a[t+116488|0]|0)){t=t+1|0}else{break L28908}}if((t|0)!=4){break}u=c[14128]|0;if((u|0)==0){y=k}else{q=u;while(1){u=q+12|0;s=c[u>>2]|0;if((s|0)!=0){uu(s);c[u>>2]=0}u=c[q+24>>2]|0;uu(q);if((u|0)==0){break}else{q=u}}y=c[13898]|0}c[14128]=0;c[13898]=y+1;i=d;return}}while(0);a[37400]=0;iO(c[10814]|0);c[10814]=0;iY(c[10828]|0);c[10828]=0;c[6352]=100;c[6350]=100;iY(0);c[10828]=0;c[9344]=10;c[9342]=10;y=c[10826]|0;if((y|0)!=0){k=y;do{c[10826]=c[k>>2];uu(k);k=c[10826]|0;}while((k|0)!=0)}t8(c[10824]|0);c[10824]=0;k=c[10822]|0;if((k|0)!=0){y=k;do{c[10822]=c[y>>2];k=c[y+60>>2]|0;if((k|0)!=0){uu(k)}k=c[y+64>>2]|0;if((k|0)!=0){uu(k)}uu(y);y=c[10822]|0;}while((y|0)!=0)}y=c[10820]|0;if((y|0)!=0){k=y;do{c[10820]=c[k>>2];uu(k);k=c[10820]|0;}while((k|0)!=0)}k=c[10818]|0;if((k|0)!=0){y=k;do{c[10818]=c[y>>2];if((c[y+12>>2]|0)==4){uu(c[(y+104|0)+4>>2]|0)}uu(y);y=c[10818]|0;}while((y|0)!=0)}uD(49160,1144,272);uD(50800,1688,272);uD(49520,1416,272);do{if((a[30528]&1)!=0){a[30528]=0;y=(a[32936]&1)==0;if(!(y|(c[17539]|0)==0)){h[8773]=-5.0;h[8774]=-5.0}if(!y){break}b[12272]=120;if((a[37400]&1)==0){break}y=c[m>>2]|0;aI(199840,33,1,y|0)}}while(0);do{if((a[32936]&1)!=0){a[32936]=0;if((a[30528]&1)!=0){break}b[12272]=120;w=121;a[24595]=w&255;w=w>>8;a[24596|0]=w&255;if((a[37400]&1)==0){break}y=c[m>>2]|0;aI(184192,51,1,y|0)}}while(0);b[12272]=120;w=121;a[24595]=w&255;w=w>>8;a[24596|0]=w&255;y=f|0;uE(y|0,0,28);uu(c[1119]|0);c[1119]=0;uu(c[1120]|0);c[1120]=0;c[1140]=4;c[1141]=4;c[1142]=4;c[1143]=c[y>>2];c[4576>>2]=c[y+4>>2];c[4580>>2]=c[y+8>>2];c[4584>>2]=c[y+12>>2];c[4588>>2]=c[y+16>>2];c[4592>>2]=c[y+20>>2];c[4596>>2]=c[y+24>>2];c[1122]=0;uD(36120,26456,1240);a[36288]=0;f=c[13898]|0;L28963:do{if((f|0)<(c[8272]|0)){k=c[1054]|0;o=(a[k+(f*40&-1)|0]&1)==0;n=c[k+(f*40&-1)+36>>2]|0;L28965:do{if(o){z=c[10036]|0;A=k+(f*40&-1)+32|0}else{p=k+(f*40&-1)+32|0;q=c[10036]|0;t=0;while(1){if((t|0)>=(n|0)){break}if((a[q+((c[p>>2]|0)+t|0)|0]|0)==(a[t+103664|0]|0)){t=t+1|0}else{z=q;A=p;break L28965}}if((t|0)==1){B=21758;break L28963}else{z=q;A=p}}}while(0);k=c[A>>2]|0;u=(n|0)>0;s=56720;r=77416;L28973:while(1){L28975:do{if(!o){if(u){l=0;x=0;C=k;while(1){D=a[r+l|0]|0;if(D<<24>>24==(a[z+(l+C|0)|0]|0)){E=C;F=x}else{if(D<<24>>24!=36){break L28975}E=C-1|0;F=1}G=l+1|0;if((G|0)<(F+n|0)){l=G;x=F;C=E}else{break}}if((F|0)==0){H=G}else{I=s;break L28973}}else{H=0}C=a[r+H|0]|0;if((C<<24>>24|0)==36|(C<<24>>24|0)==0){I=s;break L28973}}}while(0);p=s+8|0;q=c[p>>2]|0;if((q|0)==0){I=p;break}else{s=p;r=q}}r=c[I+4>>2]|0;if((r|0)>-1){uD(64813+(r*688&-1)|0,82192,15);c[13898]=f+1;break}else{uh(f,75e3,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);break}}else{B=21758}}while(0);if((B|0)==21758){uD(64813,82192,15);uD(65501,82192,15);uD(66189,82192,15);uD(66877,82192,15);uD(67565,82192,15);uD(68253,82192,15);uD(68941,82192,15);uD(69629,82192,15);uD(70317,82192,15);uD(71005,82192,15);uD(71693,82192,15)}if(a[20368]|0){a[20368]=0;g[3538]=+g[5090];g[3536]=+g[5088];g[3534]=+g[5086];c[16336]=c[16336]^2;c[17024]=c[17024]^2}c[5094]=0;c[18072]=0;g[3536]=30.0;g[3538]=60.0;g[3534]=1.0;g[3540]=0.0;g[3532]=1.0;B=0;L28997:do{c[64864+(B*688&-1)>>2]=6758437;a[64812+(B*688&-1)|0]=1;c[64808+(B*688&-1)>>2]=0;f=65280+(B*688&-1)|0;c[f>>2]=c[12782];c[f+4>>2]=c[12783];c[f+8>>2]=c[12784];c[f+12>>2]=c[12785];c[f+16>>2]=c[12786];c[f+20>>2]=c[12787];c[f+24>>2]=c[12788];c[f+28>>2]=c[12789];c[f+32>>2]=c[12790];c[f+36>>2]=c[12791];c[f+40>>2]=c[12792];c[f+44>>2]=c[12793];c[f+48>>2]=c[12794];c[f+52>>2]=c[12795];c[64656+(B*688&-1)>>2]=0;f=65140+(B*688&-1)|0;uu(c[f>>2]|0);c[f>>2]=0;f=65144+(B*688&-1)|0;uu(c[f>>2]|0);c[f>>2]=0;c[65224+(B*688&-1)>>2]=4;c[65228+(B*688&-1)>>2]=4;c[65232+(B*688&-1)>>2]=4;c[65152+(B*688&-1)>>2]=0;uD(65080+(B*688&-1)|0,51296,192);if((B|0)==5|(B|0)==3|(B|0)==1){c[65132+(B*688&-1)>>2]=-270}c[64652+(B*688&-1)>>2]=3;J=+h[56816+(B*24&-1)>>3];h[64680+(B*688&-1)>>3]=J;h[64696+(B*688&-1)>>3]=J;J=+h[56824+(B*24&-1)>>3];h[64688+(B*688&-1)>>3]=J;h[64704+(B*688&-1)>>3]=J;c[64728+(B*688&-1)>>2]=0;c[64732+(B*688&-1)>>2]=0;t6(B);c[64916+(B*688&-1)>>2]=c[56836+(B*24&-1)>>2];h[65048+(B*688&-1)>>3]=10.0;uD(64920+(B*688&-1)|0,51184,112);c[65040+(B*688&-1)>>2]=1;do{if((B|0)==7){f=(a[69604]&1)==0;a[69604]=0;h[8701]=0.0;if(f){B=B+1|0;continue L28997}else{ju();break}}else{a[64788+(B*688&-1)|0]=0;h[64792+(B*688&-1)>>3]=0.0}}while(0);B=B+1|0;}while(B>>>0<11);a[27776]=1;B=e|0;uE(B|0,0,3);e=56248;c[e>>2]=0;c[e+4>>2]=0;h[7032]=1.5;a[56264]=1;a[56265]=a[B]|0;a[56266|0]=a[B+1|0]|0;a[56267|0]=a[B+2|0]|0;c[14067]=6;c[14068]=252;c[14069]=0;h[7035]=1.0;c[14072]=1;c[14073]=0;h[7030]=-1.0;a[56232]=1;a[54144]=0;a[54160]=1;a[54152]=0;c[14074]=c[12768];c[14075]=c[12769];c[14076]=c[12770];c[14077]=c[12771];c[14078]=c[12772];c[14079]=c[12773];c[14080]=c[12774];c[14081]=c[12775];c[14082]=c[12776];c[14083]=c[12777];c[14084]=c[12778];c[14085]=c[12779];c[14086]=c[12780];c[14087]=c[12781];c[11692]=31;a[46752]=1;c[12890]=18;c[10058]=1;c[10859]=0;c[10851]=0;h[7077]=1.0;c[14156]=1;a[65036]=0;a[65037]=0;a[65724]=0;a[65725]=0;a[66412]=0;a[66413]=0;a[67100]=0;a[67101]=0;a[67788]=0;a[67789]=0;a[68476]=0;a[68477]=0;a[69164]=0;a[69165]=0;a[69852]=0;a[69853]=0;a[70540]=0;a[70541]=0;a[71228]=0;a[71229]=0;a[71916]=0;a[71917]=0;c[10012]=c[12358];c[10013]=c[12359];c[10014]=c[12360];c[10015]=c[12361];c[10016]=c[12362];c[10017]=c[12363];c[10018]=c[12364];c[10019]=c[12365];c[10020]=c[12366];c[10021]=c[12367];c[10022]=c[12368];c[10023]=c[12369];c[10024]=c[12370];c[10025]=c[12371];c[8694]=c[12358];c[8695]=c[12359];c[8696]=c[12360];c[8697]=c[12361];c[8698]=c[12362];c[8699]=c[12363];c[8700]=c[12364];c[8701]=c[12365];c[8702]=c[12366];c[8703]=c[12367];c[8704]=c[12368];c[8705]=c[12369];c[8706]=c[12370];c[8707]=c[12371];h[3815]=0.0;c[10026]=-1;c[6928]=0;c[9742]=1;c[9734]=3;c[9738]=1;a[38944]=0;a[38960]=0;c[9744]=0;a[38984]=0;a[35888]=1;a[53520]=a[96552]|0;a[53521|0]=a[96553|0]|0;a[53522|0]=a[96554|0]|0;a[53523|0]=a[96555|0]|0;a[53524|0]=a[96556|0]|0;a[53525|0]=a[96557|0]|0;h[9040]=1.0;c[8732]=0;g[178]=1.0;g[38]=1.0;g[2]=1.0;g[18074]=0.0;g[184]=0.0;g[44]=0.0;uE(y|0,0,28);uu(c[1183]|0);c[1183]=0;uu(c[1184]|0);c[1184]=0;c[1204]=4;c[1205]=4;c[1206]=4;c[1207]=c[y>>2];c[4832>>2]=c[y+4>>2];c[4836>>2]=c[y+8>>2];c[4840>>2]=c[y+12>>2];c[4844>>2]=c[y+16>>2];c[4848>>2]=c[y+20>>2];c[4852>>2]=c[y+24>>2];c[1186]=0;c[1164]=0;c[1166]=1;h[3296]=0.0;h[4384]=0.0;h[7048]=0.0;h[531]=0.0;c[11690]=0;c[13366]=5;c[13378]=0;c[13368]=4;c[13374]=5;c[13372]=0;h[11]=1.0e-8;c[11732]=10;c[11756]=10;c[11734]=1;c[11752]=1;h[5865]=1.0;h[5864]=1.0;a[47032]=0;h[77]=.5;a[624]=0;c[14100]=4;h[7052]=-1.0;c[8786]=4;h[4395]=-1.0;c[6598]=4;h[3301]=-1.0;c[1066]=4;h[535]=-1.0;h[3817]=1.0;h[3818]=1.0;b[15316]=115;a[30639]=98;a[30640]=0;a[30641]=97;a[30642]=52;c[7661]=0;c[7662]=0;c[7663]=4;c[7664]=1;c[7665]=1;uD(54016,50688,112);if((c[11254]|0)!=0){c[5163]=114;c[5164]=7;c[5165]=5;c[5166]=15;a[20668]=112;a[20684]=0;c[5168]=0;c[5172]=0;uu(c[5173]|0);c[5173]=0;uu(c[5170]|0);c[5170]=0;c[5174]=114;h[2705]=1.5;c[7640]=48}y=c[12168]|0;B=c[11862]|0;if((B|0)>0){e=0;f=B;while(1){B=y+(e*232&-1)+224|0;I=c[B>>2]|0;if((I|0)==0){K=f}else{uu(I);c[B>>2]=0;K=c[11862]|0}B=e+1|0;if((B|0)<(K|0)){e=B;f=K}else{break}}}c[11862]=0;c[12202]=c[12200];c[12206]=0;c[12372]=0;c[12373]=100;c[12374]=0;c[12376]=0;c[9670]=3;c[9671]=2;uu(c[8528]|0);c[8528]=0;a[47120]=0;uu(c[11948]|0);c[11948]=bP(199184)|0;K=c[10568]|0;if((K|0)!=0){uu(K)}c[10568]=0;a[42568]=0;e7(0);a[37400]=j;i=d;return}function t8(a){a=a|0;if((a|0)==0){return}else{t8(c[a>>2]|0);uu(a);return}}function t9(b){b=b|0;var e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;e=c[1054]|0;L29029:do{if((a[e+(b*40&-1)|0]&1)==0){f=e}else{if((bO(d[(c[10036]|0)+(c[e+(b*40&-1)+32>>2]|0)|0]|0|0)|0)==0){g=c[1054]|0;if((a[(c[10036]|0)+(c[g+(b*40&-1)+32>>2]|0)|0]|0)!=95){f=g;break}}g=b+1|0;h=c[1054]|0;if((c[8272]|0)<=(g|0)){f=h;break}if((a[h+(g*40&-1)|0]&1)==0){f=h;break}i=c[h+(g*40&-1)+36>>2]|0;j=h+(g*40&-1)+32|0;g=c[10036]|0;k=0;while(1){if((k|0)>=(i|0)){break}if((a[g+((c[j>>2]|0)+k|0)|0]|0)==(a[k+115e3|0]|0)){k=k+1|0}else{f=h;break L29029}}if((k|0)==1){l=1}else{f=h;break}return l|0}}while(0);if((a[f+(b*40&-1)|0]&1)==0){l=0;return l|0}do{if((bO(d[(c[10036]|0)+(c[f+(b*40&-1)+32>>2]|0)|0]|0|0)|0)==0){if((a[(c[10036]|0)+(c[(c[1054]|0)+(b*40&-1)+32>>2]|0)|0]|0)==95){break}else{l=0}return l|0}}while(0);f=b+1|0;if((c[8272]|0)<=(f|0)){l=0;return l|0}e=c[1054]|0;if((a[e+(f*40&-1)|0]&1)==0){l=0;return l|0}j=c[e+(f*40&-1)+36>>2]|0;g=e+(f*40&-1)+32|0;f=c[10036]|0;i=0;while(1){if((i|0)>=(j|0)){break}if((a[f+((c[g>>2]|0)+i|0)|0]|0)==(a[i+199040|0]|0)){i=i+1|0}else{l=0;m=21849;break}}if((m|0)==21849){return l|0}if((i|0)!=1){l=0;return l|0}i=b+2|0;if((a[e+(i*40&-1)|0]&1)==0){l=0;return l|0}do{if((bO(d[f+(c[e+(i*40&-1)+32>>2]|0)|0]|0|0)|0)==0){if((a[(c[10036]|0)+(c[(c[1054]|0)+(i*40&-1)+32>>2]|0)|0]|0)==95){break}else{l=0}return l|0}}while(0);i=b+3|0;b=c[8272]|0;L29071:do{if((b|0)>(i|0)){e=i;f=b;while(1){g=c[1054]|0;if((a[g+(e*40&-1)|0]&1)==0){n=e;o=f;break L29071}j=c[g+(e*40&-1)+36>>2]|0;p=g+(e*40&-1)+32|0;q=c[10036]|0;r=0;while(1){if((r|0)>=(j|0)){break}if((a[q+((c[p>>2]|0)+r|0)|0]|0)==(a[r+148464|0]|0)){r=r+1|0}else{n=e;o=f;break L29071}}if((r|0)!=1){n=e;o=f;break L29071}p=e+1|0;if((a[g+(p*40&-1)|0]&1)==0){l=0;m=21856;break}if((bO(d[q+(c[g+(p*40&-1)+32>>2]|0)|0]|0|0)|0)==0){if((a[(c[10036]|0)+(c[(c[1054]|0)+(p*40&-1)+32>>2]|0)|0]|0)!=95){l=0;m=21859;break}}p=e+2|0;j=c[8272]|0;if((j|0)>(p|0)){e=p;f=j}else{n=p;o=j;break L29071}}if((m|0)==21859){return l|0}else if((m|0)==21856){return l|0}}else{n=i;o=b}}while(0);if((o|0)<=(n|0)){l=0;return l|0}b=c[1054]|0;if((a[b+(n*40&-1)|0]&1)==0){l=0;return l|0}i=c[b+(n*40&-1)+36>>2]|0;f=b+(n*40&-1)+32|0;e=c[10036]|0;h=0;while(1){if((h|0)>=(i|0)){break}if((a[e+((c[f>>2]|0)+h|0)|0]|0)==(a[h+131272|0]|0)){h=h+1|0}else{l=0;m=21858;break}}if((m|0)==21858){return l|0}if((h|0)!=1){l=0;return l|0}h=n+1|0;if((o|0)<=(h|0)){l=0;return l|0}if((a[b+(h*40&-1)|0]&1)==0){l=0;return l|0}o=c[b+(h*40&-1)+36>>2]|0;n=b+(h*40&-1)+32|0;h=0;while(1){if((h|0)>=(o|0)){break}if((a[e+((c[n>>2]|0)+h|0)|0]|0)==(a[h+115e3|0]|0)){h=h+1|0}else{l=0;m=21851;break}}if((m|0)==21851){return l|0}l=(h|0)==1&1;return l|0}function ua(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0;d=i;i=i+16|0;e=d|0;f=d+8|0;g=b;h=b;L29117:while(1){b=a[g]|0;if((b<<24>>24|0)==0){break}else if((b<<24>>24|0)!=92){j=g+1|0;do{if((a[47120]|0)!=0&b<<24>>24==34){if((a[j]|0)!=34){break}a[h]=34;g=g+2|0;h=h+1|0;continue L29117}}while(0);a[h]=b;g=j;h=h+1|0;continue}k=g+1|0;l=a[k]|0;if((l<<24>>24|0)==34){a[h]=34;g=g+2|0;h=h+1|0;continue}else if((l<<24>>24|0)==114){a[h]=13;g=g+2|0;h=h+1|0;continue}else if((l<<24>>24|0)==92){a[h]=92;g=g+2|0;h=h+1|0;continue}else if((l<<24>>24|0)==116){a[h]=9;g=g+2|0;h=h+1|0;continue}else if((l<<24>>24|0)==110){a[h]=10;g=g+2|0;h=h+1|0;continue}else{if((l-48&255)>=8){g=k;h=h;continue}if((ca(k|0,(l<<24>>24==48?134368:132688)|0,(v=i,i=i+16|0,c[v>>2]=e,c[v+8>>2]=f,v)|0)|0)>0){a[h]=c[e>>2]&255;g=g+((c[f>>2]|0)+1|0)|0;h=h+1|0;continue}else{a[h]=92;a[h+1|0]=a[k]|0;g=g+2|0;h=h+2|0;continue}}}a[h]=0;i=d;return}function ub(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;f=c[1054]|0;g=c[f+(e*40&-1)+32>>2]|0;h=c[f+(e*40&-1)+36>>2]|0;e=(g-1|0)+h|0;i=db(c[b>>2]|0,(g+h|0)-(c[f+(d*40&-1)+32>>2]|0)|0,116456)|0;c[b>>2]=i;f=(c[(c[1054]|0)+(d*40&-1)+32>>2]|0)+1|0;L29139:do{if((f|0)<(e|0)){h=i;g=f;while(1){j=a[(c[10036]|0)+g|0]|0;if(j<<24>>24==0){k=h;break L29139}l=h+1|0;a[h]=j;j=g+1|0;if((j|0)<(e|0)){h=l;g=j}else{k=l;break}}}else{k=i}}while(0);a[k]=0;k=c[b>>2]|0;if((a[(c[10036]|0)+(c[(c[1054]|0)+(d*40&-1)+32>>2]|0)|0]|0)==34){ua(k);return}else{m=k;n=k}while(1){k=a[n]|0;if((k<<24>>24|0)==39){d=n+1|0;b=(a[d]|0)==39?d:n;o=b;p=a[b]|0}else if((k<<24>>24|0)==0){break}else{o=n;p=k}a[m]=p;m=m+1|0;n=o+1|0}a[m]=0;return}function uc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;e=uA(a|0)|0;f=(e+1|0)+(uA(b|0)|0)|0;e=ut(f)|0;do{if((e|0)==0){gk();g=ut(f)|0;if((g|0)!=0){h=g;break}uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=96488,v)|0);return 0}else{h=e}}while(0);uB(h|0,a|0);uC(h|0,b|0);i=d;return h|0}function ud(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=+f;g=+g;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0.0,F=0,G=0.0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0.0,Z=0,_=0.0,$=0,aa=0,ab=0.0,ac=0,ad=0,ae=0,af=0,ag=0,ai=0;j=i;i=i+2144|0;k=j|0;l=j+1032|0;m=j+2064|0;n=j+2072|0;o=j+2080|0;p=j+2088|0;q=j+2096|0;r=j+2104|0;s=j+2112|0;t=j+2120|0;u=j+2128|0;w=j+2136|0;c[m>>2]=0;x=k|0;y=k+1024|0;a[x]=0;k=c[8270]|0;do{if((k|0)!=0){if((aY(k|0,139696)|0)==0){break}bA(4,k|0)}}while(0);k=l|0;z=l+1|0;A=l+2|0;l=y;B=~~g;C=~~g>>>0;D=ah(O(g/4294967296.0),4294967295.0)>>>0;E=g/3.141592653589793;F=e;e=0;G=0.0;H=x;I=0;L29164:while(1){J=F;K=H;while(1){L=a[J]|0;M=J+1|0;if(L<<24>>24!=37){N=K+1|0;a[K]=L;if(L<<24>>24==0|(y|0)==(N|0)){Q=21902;break L29164}else{J=M;K=N;continue}}if((a[M]|0)!=37){break}a[K]=37;J=J+2|0;K=K+1|0}a[k]=37;if((a[M]|0)==35){a[z]=35;R=M;S=A;T=1}else{R=J;S=z;T=I}N=R;L=S;L29176:while(1){U=N+1|0;V=a[U]|0;do{if(V<<24>>24==46){W=46}else{if(((V&255)-48|0)>>>0<10){W=V;break}if((V<<24>>24|0)==45|(V<<24>>24|0)==43|(V<<24>>24|0)==32|(V<<24>>24|0)==39){W=V}else{break L29176}}}while(0);a[L]=W;N=U;L=L+1|0}J=V<<24>>24;do{if((J|0)==108){a[L]=102;a[L+1|0]=0;ue(f,g,0,n,m,k);X=l-K|0;Y=+h[n>>3];a0(K|0,X|0,k|0,(v=i,i=i+8|0,h[v>>3]=Y,v)|0);Z=1;_=f}else if((J|0)==115){a[L]=102;a[L+1|0]=0;ue(1.0,g,1,p,m,k);a0(K|0,l-K|0,k|0,(v=i,i=i+8|0,h[v>>3]=+h[p>>3],v)|0);Z=1;_=1.0}else if((J|0)==84){a[L]=100;a[L+1|0]=0;if(e){if(G!=1.0){Q=21934;break L29164}X=c[m>>2]|0;c[s>>2]=X;$=X}else{ue(1.0,g,0,0,s,74984);$=c[s>>2]|0}a0(K|0,l-K|0,k|0,(v=i,i=i+8|0,c[v>>2]=$,v)|0);Z=e;_=G}else if((J|0)==83){a[L]=100;a[L+1|0]=0;if(e){if(G!=1.0){Q=21940;break L29164}X=c[m>>2]|0;c[t>>2]=X;aa=X}else{ue(1.0,g,1,0,t,74984);aa=c[t>>2]|0}a0(K|0,l-K|0,k|0,(v=i,i=i+8|0,c[v>>2]=aa,v)|0);Z=e;_=G}else if((J|0)==98){a[L]=102;a[L+1|0]=0;ue(3.010299956639812,g,0,q,m,k);a0(K|0,l-K|0,k|0,(v=i,i=i+8|0,h[v>>3]=+h[q>>3],v)|0);Z=1;_=3.010299956639812}else if((J|0)==76){a[L]=100;a[L+1|0]=0;if(e){if(G!=f){Q=21929;break L29164}c[r>>2]=c[m>>2];ab=G}else{ab=f}ue(f,g,0,0,r,74984);a0(K|0,l-K|0,k|0,(v=i,i=i+8|0,c[v>>2]=c[r>>2],v)|0);Z=e;_=ab}else if((J|0)==101|(J|0)==69|(J|0)==102|(J|0)==70|(J|0)==103|(J|0)==71){a[L]=V;a[L+1|0]=0;a0(K|0,l-K|0,k|0,(v=i,i=i+8|0,h[v>>3]=g,v)|0);Z=e;_=G}else if((J|0)==99){a[L]=99;a[L+1|0]=0;if(e){if(G!=1.0){Q=21946;break L29164}X=c[m>>2]|0;c[u>>2]=X;ac=X}else{ue(1.0,g,1,0,u,74984);ac=c[u>>2]|0}X=ac+24|0;if(X>>>0<49){ad=(X|0)/3&-1;c[u>>2]=ad;X=l-K|0;ae=a[ad+204216|0]|0;a0(K|0,X|0,k|0,(v=i,i=i+8|0,c[v>>2]=ae,v)|0);Z=e;_=G;break}else{ae=l-K|0;a0(K|0,ae|0,199792,(v=i,i=i+8|0,c[v>>2]=ac,v)|0);Z=e;_=G;break}}else if((J|0)==116){a[L]=102;a[L+1|0]=0;ue(1.0,g,0,o,m,k);a0(K|0,l-K|0,k|0,(v=i,i=i+8|0,h[v>>3]=+h[o>>3],v)|0);Z=1;_=1.0}else if((J|0)==120|(J|0)==88|(J|0)==111|(J|0)==79){if(+P(+g)<2147483647.0){a[L]=V;a[L+1|0]=0;ae=l-K|0;a0(K|0,ae|0,k|0,(v=i,i=i+8|0,c[v>>2]=B,v)|0);Z=e;_=G;break}else{a[L]=108;a[L+1|0]=108;a[L+2|0]=a[U]|0;a[L+3|0]=0;ae=l-K|0;a0(K|0,ae|0,k|0,(v=i,i=i+16|0,c[v>>2]=C,c[v+8>>2]=D,v)|0);Z=e;_=G;break}}else if((J|0)==80){a[L]=102;a[L+1|0]=0;a0(K|0,l-K|0,k|0,(v=i,i=i+8|0,h[v>>3]=E,v)|0);Z=e;_=G}else if((J|0)==66){a[L]=99;a[L+1|0]=105;a[L+2|0]=0;if(e){if(G!=3.010299956639812){Q=21954;break L29164}ae=c[m>>2]|0;c[w>>2]=ae;af=ae}else{ue(3.010299956639812,g,0,0,w,74984);af=c[w>>2]|0}if((af-1|0)>>>0<8){ae=l-K|0;X=a[af+184160|0]|0;a0(K|0,ae|0,k|0,(v=i,i=i+8|0,c[v>>2]=X,v)|0);Z=e;_=G;break}if((af|0)>8){X=l-K|0;ae=af-8|0;a0(K|0,X|0,178992,(v=i,i=i+8|0,c[v>>2]=ae,v)|0);Z=e;_=G;break}if((af|0)>=0){Z=e;_=G;break}a0(K|0,l-K|0,174792,(v=i,i=i+8|0,c[v>>2]=af*10&-1,v)|0);Z=e;_=G}else{Q=21963;break L29164}}while(0);if(T){if((U|0)!=(cy(U|0,166744)|0)){Q=21969;break}}L=c[12886]|0;do{if((L|0)!=0){J=uA(L|0)|0;ae=a[c[b$()>>2]|0]|0;X=a8(K|0,ae|0)|0;if((X|0)==0){break}ad=K;ag=X;do{X=uA(ag|0)|0;ai=ag+J|0;if(((ai-ad|0)+X|0)>>>0>d>>>0){Q=21977;break L29164}uH(ai|0,ag+1|0,X|0);uD(ag|0,c[12886]|0,J);ag=a8(ai|0,ae|0)|0;}while((ag|0)!=0)}}while(0);F=N+2|0;e=Z;G=_;H=K+(uA(K|0)|0)|0;I=T}if((Q|0)==21902){uF(b|0,x|0,d|0);if((uA(x|0)|0)>>>0>=d>>>0){a[b+((d|0)==0?0:d-1|0)|0]=0}d=c[8270]|0;if((d|0)==0){i=j;return}if((aY(d|0,139696)|0)==0){i=j;return}bA(4,139696);i=j;return}else if((Q|0)==21940){uf(-1,215448,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==21946){uf(-1,209728,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==21929){uf(-1,82024,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==21954){uf(-1,193648,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==21969){j=c[8270]|0;if((j|0)==0){uf(-1,170952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((aY(j|0,139696)|0)==0){uf(-1,170952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}bA(4,139696);uf(-1,170952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==21977){uf(-1,161576,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==21963){j=c[8270]|0;if((j|0)==0){uf(-1,170952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((aY(j|0,139696)|0)==0){uf(-1,170952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}bA(4,139696);uf(-1,170952,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((Q|0)==21934){uf(-1,222240,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}function ue(a,b,d,e,f,g){a=+a;b=+b;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0.0,l=0.0,m=0,n=0,o=0,p=0,q=0.0,r=0,s=0,t=0.0,u=0,w=0.0,x=0,y=0.0,z=0;j=i;if(b==0.0){if((e|0)!=0){h[e>>3]=0.0}if((f|0)==0){i=j;return}c[f>>2]=0;i=j;return}if(b<0.0){k=-0.0-b;l=-1.0}else{k=b;l=1.0}b=+bQ(+k)/a;m=~~+O(+b);k=+R(10.0,+((b- +(m|0))*a));if(d){if(a!=1.0){uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=127848,v)|0)}n=(m|0)%3&-1;if((n|0)==2){o=m;p=22001}else if((n|0)==(-1|0)){o=m-3|0;p=22001}else if((n|0)==0){q=k;r=m}else if((n|0)==1){s=m;p=22003}else if((n|0)==(-2|0)){s=m-3|0;p=22003}else{uf(-1,126488,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if((p|0)==22001){q=k*100.0;r=o}else if((p|0)==22003){q=k*10.0;r=s}t=q;u=r-((r|0)%3&-1)|0}else{t=k;u=m}do{if((g|0)==0){w=t;x=u}else{if(d){y=1.0e3}else{y=+R(10.0,+a)}m=a8(g|0,46)|0;if((m|0)==0){z=0}else{z=aE(m+1|0,0,10)|0}if(t+ +R(10.0,+(+(-z|0)))*.5<y){w=t;x=u;break}w=t/y;x=u+(d?3:1)|0}}while(0);if((e|0)!=0){h[e>>3]=l*w}if((f|0)==0){i=j;return}c[f>>2]=x;i=j;return}function uf(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0;f=i;i=i+144|0;g=f|0;h=f+16|0;uE(h|0,0,128);do{if((b|0)==(-2|0)){f=c[11932]|0;j=c[8802]|0;if(!((c[12892]|0)!=0&(f|0)!=0&(j|0)!=0)){break}k=c[m>>2]|0;l=(uA(j|0)|0)>>>0>77?76168:179864;n=c[11900]|0;cf(k|0,76792,(v=i,i=i+32|0,c[v>>2]=j,c[v+8>>2]=l,c[v+16>>2]=f,c[v+24>>2]=n,v)|0)}else if((b|0)!=(-1|0)){if((a[25280]&1)==0){n=c[m>>2]|0;f=c[12906]|0;l=(f|0)!=0?f:179864;f=c[10036]|0;cf(n|0,153408,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=f,v)|0)}f=c[12906]|0;do{if((f|0)!=0){if((a[f]|0)==0){break}else{o=f}do{aF(32,c[m>>2]|0);o=o+1|0;}while((a[o]|0)!=0)}}while(0);if((c[(c[1054]|0)+(b*40&-1)+32>>2]|0)>0){f=0;do{aF(((a[(c[10036]|0)+f|0]|0)==9?9:32)|0,c[m>>2]|0);f=f+1|0;}while((f|0)<(c[(c[1054]|0)+(b*40&-1)+32>>2]|0))}aI(148792,2,1,c[m>>2]|0)}}while(0);b=c[12906]|0;do{if((b|0)!=0){if((a[b]|0)==0){break}else{p=b}do{aF(32,c[m>>2]|0);p=p+1|0;}while((a[p]|0)!=0)}}while(0);L29346:do{if((a[37400]&1)==0){p=c[8804]|0;do{if((p|0)!=0){b=c[p+4>>2]|0;if((b|0)==0){break}o=c[m>>2]|0;f=c[9354]|0;cf(o|0,146696,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=f,v)|0);break L29346}}while(0);p=c[m>>2]|0;f=c[9354]|0;cf(p|0,144600,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}}while(0);f=g;c[f>>2]=e;c[f+4>>2]=0;b9(h|0,128,d|0,g|0);cf(c[m>>2]|0,141456,(v=i,i=i+8|0,c[v>>2]=h,v)|0);aI(139704,2,1,c[m>>2]|0);g=c[8270]|0;do{if((g|0)!=0){if((aY(g|0,139696)|0)==0){break}bA(4,139696)}}while(0);a[43504]=0;c[6932]=0;c[13544]=0;a[14176]=0;a[25288]=0;a[37408]=0;e7(2);g=e6(137752)|0;if((g|0)==0){iL()}d=g+8|0;do{if((a[d]&1)==0){if((aY(c[g+24>>2]|0,h|0)|0)==0){iL()}f=g+16|0;if((c[f>>2]|0)!=3){q=f;break}uu(c[g+24>>2]|0);c[f>>2]=1;q=f}else{a[d]=0;q=g+16|0}}while(0);d=bP(h|0)|0;c[q>>2]=3;c[g+24>>2]=d;iL()}function ug(a,b){a=a|0;b=b|0;var e=0,f=0,g=0,h=0,i=0,j=0;e=c[b>>2]|0;c[b>>2]=e+1;f=d[e]|0;if((f&128|0)==0){c[a>>2]=f;g=1;return g|0}if((f&224|0)==192){e=f&31;c[a>>2]=e;h=d[c[b>>2]|0]|0;if((h&192|0)!=128){c[a>>2]=65535;g=0;return g|0}c[a>>2]=e<<6|h&63;c[b>>2]=(c[b>>2]|0)+1;if((c[a>>2]|0)>>>0>=128){g=1;return g|0}c[a>>2]=65535;g=0;return g|0}if((f&240|0)==224){h=f&15;c[a>>2]=h;e=d[c[b>>2]|0]|0;do{if((e&192|0)==128){c[a>>2]=h<<6|e&63;i=(c[b>>2]|0)+1|0;c[b>>2]=i;j=d[i]|0;if((j&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|j&63;c[b>>2]=(c[b>>2]|0)+1;if((c[a>>2]|0)>>>0>=2048){g=1;return g|0}c[a>>2]=65535;g=0;return g|0}}while(0);c[a>>2]=65535;g=0;return g|0}if((f&248|0)==240){e=f&7;c[a>>2]=e;h=d[c[b>>2]|0]|0;do{if((h&192|0)==128){c[a>>2]=e<<6|h&63;j=(c[b>>2]|0)+1|0;c[b>>2]=j;i=d[j]|0;if((i&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|i&63;i=(c[b>>2]|0)+1|0;c[b>>2]=i;j=d[i]|0;if((j&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|j&63;c[b>>2]=(c[b>>2]|0)+1;if((c[a>>2]|0)>>>0>=65536){g=1;return g|0}c[a>>2]=65535;g=0;return g|0}}while(0);c[a>>2]=65535;g=0;return g|0}if((f&252|0)==248){h=f&3;c[a>>2]=h;e=d[c[b>>2]|0]|0;do{if((e&192|0)==128){c[a>>2]=h<<6|e&63;j=(c[b>>2]|0)+1|0;c[b>>2]=j;i=d[j]|0;if((i&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|i&63;i=(c[b>>2]|0)+1|0;c[b>>2]=i;j=d[i]|0;if((j&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|j&63;j=(c[b>>2]|0)+1|0;c[b>>2]=j;i=d[j]|0;if((i&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|i&63;c[b>>2]=(c[b>>2]|0)+1;if((c[a>>2]|0)>>>0>=2097152){g=1;return g|0}c[a>>2]=65535;g=0;return g|0}}while(0);c[a>>2]=65535;g=0;return g|0}if((f&254|0)!=252){c[a>>2]=65535;g=0;return g|0}e=f&1;c[a>>2]=e;f=d[c[b>>2]|0]|0;do{if((f&192|0)==128){c[a>>2]=e<<6|f&63;h=(c[b>>2]|0)+1|0;c[b>>2]=h;i=d[h]|0;if((i&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|i&63;i=(c[b>>2]|0)+1|0;c[b>>2]=i;h=d[i]|0;if((h&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|h&63;h=(c[b>>2]|0)+1|0;c[b>>2]=h;i=d[h]|0;if((i&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|i&63;i=(c[b>>2]|0)+1|0;c[b>>2]=i;h=d[i]|0;if((h&192|0)!=128){break}c[a>>2]=c[a>>2]<<6|h&63;c[b>>2]=(c[b>>2]|0)+1;if((c[a>>2]|0)>>>0>=67108864){g=1;return g|0}c[a>>2]=65535;g=0;return g|0}}while(0);c[a>>2]=65535;g=0;return g|0}function uh(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0;f=i;i=i+16|0;g=f|0;do{if((b|0)==(-2|0)){h=c[11932]|0;j=c[8802]|0;if(!((c[12892]|0)!=0&(h|0)!=0&(j|0)!=0)){break}k=c[m>>2]|0;l=(uA(j|0)|0)>>>0>77?76168:179864;n=c[11900]|0;cf(k|0,76792,(v=i,i=i+32|0,c[v>>2]=j,c[v+8>>2]=l,c[v+16>>2]=h,c[v+24>>2]=n,v)|0)}else if((b|0)!=(-1|0)){if((a[25280]&1)==0){n=c[m>>2]|0;h=c[12906]|0;l=(h|0)!=0?h:179864;h=c[10036]|0;cf(n|0,153408,(v=i,i=i+16|0,c[v>>2]=l,c[v+8>>2]=h,v)|0)}h=c[12906]|0;do{if((h|0)!=0){if((a[h]|0)==0){break}else{o=h}do{aF(32,c[m>>2]|0);o=o+1|0;}while((a[o]|0)!=0)}}while(0);if((c[(c[1054]|0)+(b*40&-1)+32>>2]|0)>0){h=0;do{aF(((a[(c[10036]|0)+h|0]|0)==9?9:32)|0,c[m>>2]|0);h=h+1|0;}while((h|0)<(c[(c[1054]|0)+(b*40&-1)+32>>2]|0))}aI(148792,2,1,c[m>>2]|0)}}while(0);b=c[12906]|0;do{if((b|0)!=0){if((a[b]|0)==0){break}else{p=b}do{aF(32,c[m>>2]|0);p=p+1|0;}while((a[p]|0)!=0)}}while(0);L29463:do{if((a[37400]&1)==0){p=c[8804]|0;do{if((p|0)!=0){b=c[p+4>>2]|0;if((b|0)==0){break}o=c[m>>2]|0;h=c[9354]|0;cf(o|0,146696,(v=i,i=i+16|0,c[v>>2]=b,c[v+8>>2]=h,v)|0);break L29463}}while(0);p=c[m>>2]|0;h=c[9354]|0;cf(p|0,144600,(v=i,i=i+8|0,c[v>>2]=h,v)|0)}}while(0);aI(135952,9,1,c[m>>2]|0);h=g;c[h>>2]=e;c[h+4>>2]=0;bX(c[m>>2]|0,d|0,g|0);aF(10,c[m>>2]|0);i=f;return}function ui(b,c){b=b|0;c=c|0;var d=0,e=0,f=0,g=0,h=0,i=0;d=b;while(1){if((aM(a[d]|0|0)|0)==0){e=c;break}else{d=d+1|0}}while(1){if((aM(a[e]|0|0)|0)==0){break}else{e=e+1|0}}c=uA(d|0)|0;b=uA(e|0)|0;f=c;while(1){g=f-1|0;if((aM(a[d+g|0]|0|0)|0)==0){h=b;break}else{f=g}}do{h=h-1|0;}while((aM(a[e+h|0]|0|0)|0)!=0);if((g|0)!=(h|0)){i=0;return i|0}i=(a_(d|0,e|0,f|0)|0)==0;return i|0}function uj(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0;f=i;i=i+16|0;g=f|0;do{if((b|0)==(-2|0)){f=c[11932]|0;h=c[8802]|0;if(!((c[12892]|0)!=0&(f|0)!=0&(h|0)!=0)){break}j=c[m>>2]|0;k=(uA(h|0)|0)>>>0>77?76168:179864;l=c[11900]|0;cf(j|0,76792,(v=i,i=i+32|0,c[v>>2]=h,c[v+8>>2]=k,c[v+16>>2]=f,c[v+24>>2]=l,v)|0)}else if((b|0)!=(-1|0)){if((a[25280]&1)==0){l=c[m>>2]|0;f=c[12906]|0;k=(f|0)!=0?f:179864;f=c[10036]|0;cf(l|0,153408,(v=i,i=i+16|0,c[v>>2]=k,c[v+8>>2]=f,v)|0)}f=c[12906]|0;do{if((f|0)!=0){if((a[f]|0)==0){break}else{n=f}do{aF(32,c[m>>2]|0);n=n+1|0;}while((a[n]|0)!=0)}}while(0);if((c[(c[1054]|0)+(b*40&-1)+32>>2]|0)>0){f=0;do{aF(((a[(c[10036]|0)+f|0]|0)==9?9:32)|0,c[m>>2]|0);f=f+1|0;}while((f|0)<(c[(c[1054]|0)+(b*40&-1)+32>>2]|0))}aI(148792,2,1,c[m>>2]|0)}}while(0);b=c[12906]|0;do{if((b|0)!=0){if((a[b]|0)==0){break}else{o=b}do{aF(32,c[m>>2]|0);o=o+1|0;}while((a[o]|0)!=0)}}while(0);o=g;c[o>>2]=e;c[o+4>>2]=0;bX(c[m>>2]|0,d|0,g|0);aF(10,c[m>>2]|0);g=c[12906]|0;do{if((g|0)!=0){if((a[g]|0)==0){break}else{p=g}do{aF(32,c[m>>2]|0);p=p+1|0;}while((a[p]|0)!=0)}}while(0);if((a[37400]&1)!=0){aS(143e3);q=c[m>>2]|0;r=aF(10,q|0)|0;a[25288]=0;iL()}p=c[8804]|0;do{if((p|0)!=0){g=c[p+4>>2]|0;if((g|0)==0){break}d=c[m>>2]|0;o=c[9354]|0;cf(d|0,146696,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=o,v)|0);aS(143e3);q=c[m>>2]|0;r=aF(10,q|0)|0;a[25288]=0;iL()}}while(0);cf(c[m>>2]|0,144600,(v=i,i=i+8|0,c[v>>2]=c[9354],v)|0);aS(143e3);q=c[m>>2]|0;r=aF(10,q|0)|0;a[25288]=0;iL()}function uk(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;i=i+16|0;f=e|0;a[33512]=0;lh();e=f|0;g=f;c[g>>2]=d;c[g+4>>2]=0;g=c[12906]|0;do{if((g|0)!=0){if((a[g]|0)==0){break}else{h=g}do{aF(32,c[m>>2]|0);h=h+1|0;}while((a[h]|0)!=0)}}while(0);L29528:do{if((a[37400]&1)==0){h=c[8804]|0;do{if((h|0)!=0){g=c[h+4>>2]|0;if((g|0)==0){break}d=c[m>>2]|0;f=c[9354]|0;cf(d|0,146696,(v=i,i=i+16|0,c[v>>2]=g,c[v+8>>2]=f,v)|0);break L29528}}while(0);h=c[m>>2]|0;f=c[9354]|0;cf(h|0,144600,(v=i,i=i+8|0,c[v>>2]=f,v)|0)}}while(0);bX(c[m>>2]|0,b|0,e|0);aI(139704,2,1,c[m>>2]|0);iL()}function ul(a,b,d,e,f,g){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var j=0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0.0,t=0.0,u=0.0,w=0,x=0.0,y=0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0,I=0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0.0,P=0,Q=0.0,R=0.0,S=0.0,T=0.0,U=0,V=0.0,W=0.0,X=0.0,Y=0.0,Z=0.0,_=0.0,$=0.0,aa=0.0;j=i;k=+h[a+(b<<6)+8>>3];l=+h[a+(b<<6)+16>>3];m=+h[a+(b<<6)+24>>3];n=+h[a+(d<<6)+8>>3];o=+h[a+(d<<6)+16>>3];p=+h[a+(d<<6)+24>>3];d=(c[a+(b<<6)>>2]|0)==0;q=d?n:k;r=d?o:l;s=d?p:m;t=d?k:n;n=d?l:o;o=d?m:p;d=t==-8.988465674311579e+307;b=n==-8.988465674311579e+307;a=(b?d?2:1:d&1)+(o==-8.988465674311579e+307&1)|0;if((a|0)>1){h[e>>3]=q;h[f>>3]=r;h[g>>3]=s;i=j;return}if((a|0)==1){h[e>>3]=q;h[f>>3]=r;h[g>>3]=s;if(d){h[e>>3]=(c[16508]&2|0)==0?+h[8255]:+h[8256];i=j;return}if(b){h[f>>3]=(c[16336]&2|0)==0?+h[8169]:+h[8170];i=j;return}else{h[g>>3]=(c[16164]&2|0)==0?+h[8083]:+h[8084];i=j;return}}do{if(q==t){if(r==n){h[e>>3]=q;h[f>>3]=r;b=s<o;d=c[16164]&2;a=(d|0)!=0;p=+h[8083];m=+h[8084];l=a?p:m;do{if(b){if(l<s){u=a?m:p;w=20;break}else{if(((d|0)==0?m:p)>o){w=19;break}else{w=18;break}}}else{if(l<o){x=a?m:p;w=22;break}else{if(((d|0)==0?m:p)>s){w=19;break}else{w=18;break}}}}while(0);if((w|0)==18){h[g>>3]=(d|0)==0?m:p;i=j;return}else if((w|0)==19){l=a?m:p;if(b){u=l;w=20}else{x=l;w=22}}do{if((w|0)==20){if(u<s){uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(((d|0)==0?p:m)<=o){break}uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((w|0)==22){if(x<o){uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(((d|0)==0?p:m)<=s){break}uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);h[g>>3]=(d|0)==0?p:m;i=j;return}if(s==o){h[e>>3]=q;h[g>>3]=s;b=r<n;a=c[16336]&2;y=(a|0)!=0;l=+h[8169];k=+h[8170];z=y?l:k;do{if(b){if(z<r){A=y?k:l;w=36;break}else{if(((a|0)==0?k:l)>n){w=35;break}else{w=34;break}}}else{if(z<n){B=y?k:l;w=38;break}else{if(((a|0)==0?k:l)>r){w=35;break}else{w=34;break}}}}while(0);if((w|0)==34){h[f>>3]=(a|0)==0?k:l;i=j;return}else if((w|0)==35){z=y?k:l;if(b){A=z;w=36}else{B=z;w=38}}do{if((w|0)==36){if(A<r){uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(((a|0)==0?l:k)<=n){break}uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((w|0)==38){if(B<n){uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(((a|0)==0?l:k)<=r){break}uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);h[f>>3]=(a|0)==0?l:k;i=j;return}b=r<n;y=c[16336]&2;d=(y|0)!=0;z=+h[8170];m=+h[8169];p=d?z:m;do{if(b){if(p<r){C=d?m:z;w=55;break}else{if(((y|0)==0?m:z)>n){w=54;break}else{w=49;break}}}else{if(p<n){D=d?m:z;w=57;break}else{if(((y|0)==0?m:z)>r){w=54;break}else{w=49;break}}}}while(0);do{if((w|0)==49){p=(y|0)==0?m:z;if(!(p!=r&p!=n)){w=54;break}k=s+(p-r)*((o-s)/(n-r));p=+h[8083];l=+h[8084];if(p<l){if(k<p|k>l){w=54;break}}else{if(k<l|k>p){w=54;break}}h[e>>3]=q;h[f>>3]=(c[16336]&2|0)==0?+h[8169]:+h[8170];h[g>>3]=k;i=j;return}}while(0);if((w|0)==54){k=d?m:z;if(b){C=k;w=55}else{D=k;w=57}}do{if((w|0)==55){if(C<r){break}if(((y|0)==0?z:m)<=n){w=59}}else if((w|0)==57){if(D<n){break}if(((y|0)==0?z:m)<=r){w=59}}}while(0);do{if((w|0)==59){k=(y|0)==0?z:m;if(!(k!=r&k!=n)){break}p=s+(k-r)*((o-s)/(n-r));k=+h[8083];l=+h[8084];if(k<l){if(p<k|p>l){break}}else{if(p<l|p>k){break}}h[e>>3]=q;h[f>>3]=(c[16336]&2|0)==0?+h[8170]:+h[8169];h[g>>3]=p;i=j;return}}while(0);y=s<o;b=c[16164]&2;d=(b|0)!=0;p=+h[8084];k=+h[8083];l=d?p:k;do{if(y){if(l<s){E=d?k:p;w=77;break}else{if(((b|0)==0?k:p)>o){w=76;break}else{w=71;break}}}else{if(l<o){F=d?k:p;w=79;break}else{if(((b|0)==0?k:p)>s){w=76;break}else{w=71;break}}}}while(0);do{if((w|0)==71){l=(b|0)==0?k:p;if(!(l!=s&l!=o)){w=76;break}G=r+(l-s)*((n-r)/(o-s));if(m<z){if(G<m|G>z){w=76;break}}else{if(G<z|G>m){w=76;break}}h[e>>3]=q;h[f>>3]=G;h[g>>3]=(c[16164]&2|0)==0?+h[8083]:+h[8084];i=j;return}}while(0);if((w|0)==76){G=d?k:p;if(y){E=G;w=77}else{F=G;w=79}}if((w|0)==77){if(E<s){break}if(((b|0)==0?p:k)>o){break}}else if((w|0)==79){if(F<o){break}if(((b|0)==0?p:k)>s){break}}G=(b|0)==0?p:k;if(!(G!=s&G!=o)){break}l=r+(G-s)*((n-r)/(o-s));if(m<z){if(l<m|l>z){break}}else{if(l<z|l>m){break}}h[e>>3]=q;h[f>>3]=l;h[g>>3]=(c[16164]&2|0)==0?+h[8084]:+h[8083];i=j;return}}while(0);do{if(r==n){if(o==s){h[f>>3]=r;h[g>>3]=s;a=q<t;H=c[16508]&2;I=(H|0)!=0;F=+h[8255];E=+h[8256];D=I?F:E;do{if(a){if(D<q){J=I?E:F;w=97;break}else{if(((H|0)==0?E:F)>t){w=96;break}else{w=95;break}}}else{if(D<t){K=I?E:F;w=99;break}else{if(((H|0)==0?E:F)>q){w=96;break}else{w=95;break}}}}while(0);if((w|0)==95){h[e>>3]=(H|0)==0?E:F;i=j;return}else if((w|0)==96){D=I?E:F;if(a){J=D;w=97}else{K=D;w=99}}do{if((w|0)==97){if(J<q){uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(((H|0)==0?F:E)<=t){break}uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}else if((w|0)==99){if(K<t){uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}if(((H|0)==0?F:E)<=q){break}uk(113944,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0)}}while(0);h[e>>3]=(H|0)==0?F:E;i=j;return}a=q<t;I=c[16508]&2;b=(I|0)!=0;D=+h[8256];m=+h[8255];z=b?D:m;do{if(a){if(z<q){L=b?m:D;w=116;break}else{if(((I|0)==0?m:D)>t){w=115;break}else{w=110;break}}}else{if(z<t){M=b?m:D;w=118;break}else{if(((I|0)==0?m:D)>q){w=115;break}else{w=110;break}}}}while(0);do{if((w|0)==110){z=(I|0)==0?m:D;if(!(z!=q&z!=t)){w=115;break}E=s+(z-q)*((o-s)/(t-q));F=+h[8083];k=+h[8084];if(F<k){if(E<F|E>k){w=115;break}}else{if(E<k|E>F){w=115;break}}h[e>>3]=z;h[f>>3]=r;h[g>>3]=E;i=j;return}}while(0);if((w|0)==115){E=b?m:D;if(a){L=E;w=116}else{M=E;w=118}}do{if((w|0)==116){if(L<q){break}if(((I|0)==0?D:m)<=t){w=120}}else if((w|0)==118){if(M<t){break}if(((I|0)==0?D:m)<=q){w=120}}}while(0);do{if((w|0)==120){E=(I|0)==0?D:m;if(!(E!=q&E!=t)){break}z=s+(E-q)*((o-s)/(t-q));F=+h[8083];k=+h[8084];if(F<k){if(z<F|z>k){break}}else{if(z<k|z>F){break}}h[e>>3]=E;h[f>>3]=r;h[g>>3]=z;i=j;return}}while(0);I=s<o;a=c[16164]&2;b=(a|0)!=0;z=+h[8084];E=+h[8083];F=b?z:E;do{if(I){if(F<s){N=b?E:z;w=138;break}else{if(((a|0)==0?E:z)>o){w=137;break}else{w=132;break}}}else{if(F<o){O=b?E:z;w=140;break}else{if(((a|0)==0?E:z)>s){w=137;break}else{w=132;break}}}}while(0);do{if((w|0)==132){F=(a|0)==0?E:z;if(!(F!=s&F!=o)){w=137;break}k=q+(F-s)*((t-q)/(o-s));if(m<D){if(k<m|k>D){w=137;break}}else{if(k<D|k>m){w=137;break}}h[e>>3]=k;h[f>>3]=r;h[g>>3]=(c[16164]&2|0)==0?+h[8083]:+h[8084];i=j;return}}while(0);if((w|0)==137){k=b?E:z;if(I){N=k;w=138}else{O=k;w=140}}if((w|0)==138){if(N<s){break}if(((a|0)==0?z:E)>o){break}}else if((w|0)==140){if(O<o){break}if(((a|0)==0?z:E)>s){break}}k=(a|0)==0?z:E;if(!(k!=s&k!=o)){break}F=q+(k-s)*((t-q)/(o-s));if(m<D){if(F<m|F>D){break}}else{if(F<D|F>m){break}}h[e>>3]=F;h[f>>3]=r;h[g>>3]=(c[16164]&2|0)==0?+h[8084]:+h[8083];i=j;return}}while(0);do{if(s==o){H=q<t;y=c[16508]|0;d=y&2;P=(d|0)!=0;O=+h[8256];N=+h[8255];M=P?O:N;do{if(H){if(M<q){Q=P?N:O;w=162;break}else{if(((d|0)==0?N:O)>t){w=161;break}else{w=156;break}}}else{if(M<t){R=P?N:O;w=164;break}else{if(((d|0)==0?N:O)>q){w=161;break}else{w=156;break}}}}while(0);do{if((w|0)==156){M=(d|0)==0?N:O;if(!(M!=q&M!=t)){w=161;break}m=r+(M-q)*((n-r)/(t-q));D=+h[8169];E=+h[8170];if(D<E){if(m<D|m>E){w=161;break}}else{if(m<E|m>D){w=161;break}}h[e>>3]=M;h[f>>3]=m;h[g>>3]=s;i=j;return}}while(0);if((w|0)==161){m=P?N:O;if(H){Q=m;w=162}else{R=m;w=164}}do{if((w|0)==162){if(Q<q){break}if(((d|0)==0?O:N)<=t){w=166}}else if((w|0)==164){if(R<t){break}if(((d|0)==0?O:N)<=q){w=166}}}while(0);do{if((w|0)==166){m=(d|0)==0?O:N;if(!(m!=q&m!=t)){break}M=r+(m-q)*((n-r)/(t-q));D=+h[8169];E=+h[8170];if(D<E){if(M<D|M>E){break}}else{if(M<E|M>D){break}}h[e>>3]=m;h[f>>3]=M;h[g>>3]=s;i=j;return}}while(0);d=r<n;H=c[16336]&2;P=(H|0)!=0;M=+h[8170];m=+h[8169];D=P?M:m;do{if(d){if(D<r){S=P?m:M;w=184;break}else{if(((H|0)==0?m:M)>n){w=183;break}else{w=178;break}}}else{if(D<n){T=P?m:M;w=186;break}else{if(((H|0)==0?m:M)>r){w=183;break}else{w=178;break}}}}while(0);do{if((w|0)==178){D=(H|0)==0?m:M;if(!(D!=r&D!=n)){w=183;break}E=q+(D-r)*((t-q)/(n-r));if(N<O){if(E<N|E>O){w=183;break}}else{if(E<O|E>N){w=183;break}}h[e>>3]=E;h[f>>3]=(c[16336]&2|0)==0?+h[8169]:+h[8170];h[g>>3]=s;i=j;return}}while(0);if((w|0)==183){E=P?m:M;if(d){S=E;w=184}else{T=E;w=186}}if((w|0)==184){if(S<r){U=y;V=O;W=N;break}if(((H|0)==0?M:m)>n){U=y;V=O;W=N;break}}else if((w|0)==186){if(T<n){U=y;V=O;W=N;break}if(((H|0)==0?M:m)>r){U=y;V=O;W=N;break}}E=(H|0)==0?M:m;if(!(E!=r&E!=n)){U=y;V=O;W=N;break}D=q+(E-r)*((t-q)/(n-r));if(N<O){if(D<N|D>O){U=y;V=O;W=N;break}}else{if(D<O|D>N){U=y;V=O;W=N;break}}h[e>>3]=D;h[f>>3]=(c[16336]&2|0)==0?+h[8170]:+h[8169];h[g>>3]=s;i=j;return}else{U=c[16508]|0;V=+h[8256];W=+h[8255]}}while(0);a=q<t;I=U&2;U=(I|0)!=0;T=U?V:W;do{if(a){if(T<q){X=U?W:V;w=209;break}else{if(((I|0)==0?W:V)>t){w=208;break}else{w=200;break}}}else{if(T<t){Y=U?W:V;w=211;break}else{if(((I|0)==0?W:V)>q){w=208;break}else{w=200;break}}}}while(0);do{if((w|0)==200){T=(I|0)==0?W:V;if(!(T!=q&T!=t)){w=208;break}S=T-q;R=t-q;Q=r+S*((n-r)/R);D=s+S*((o-s)/R);R=+h[8169];S=+h[8170];if(R<S){if(Q<R|Q>S){w=208;break}}else{if(Q<S|Q>R){w=208;break}}R=+h[8083];S=+h[8084];if(R<S){if(D<R|D>S){w=208;break}}else{if(D<S|D>R){w=208;break}}h[e>>3]=T;h[f>>3]=Q;h[g>>3]=D;i=j;return}}while(0);if((w|0)==208){D=U?W:V;if(a){X=D;w=209}else{Y=D;w=211}}do{if((w|0)==209){if(X<q){break}if(((I|0)==0?V:W)<=t){w=213}}else if((w|0)==211){if(Y<t){break}if(((I|0)==0?V:W)<=q){w=213}}}while(0);do{if((w|0)==213){Y=(I|0)==0?V:W;if(!(Y!=q&Y!=t)){break}X=Y-q;D=t-q;Q=r+X*((n-r)/D);T=s+X*((o-s)/D);D=+h[8169];X=+h[8170];if(D<X){if(Q<D|Q>X){break}}else{if(Q<X|Q>D){break}}D=+h[8083];X=+h[8084];if(D<X){if(T<D|T>X){break}}else{if(T<X|T>D){break}}h[e>>3]=Y;h[f>>3]=Q;h[g>>3]=T;i=j;return}}while(0);I=r<n;a=c[16336]&2;U=(a|0)!=0;T=+h[8170];Q=+h[8169];Y=U?T:Q;do{if(I){if(Y<r){Z=U?Q:T;w=237;break}else{if(((a|0)==0?Q:T)>n){w=236;break}else{w=228;break}}}else{if(Y<n){_=U?Q:T;w=239;break}else{if(((a|0)==0?Q:T)>r){w=236;break}else{w=228;break}}}}while(0);do{if((w|0)==228){Y=(a|0)==0?Q:T;if(!(Y!=r&Y!=n)){w=236;break}D=Y-r;Y=n-r;X=q+D*((t-q)/Y);R=s+D*((o-s)/Y);if(W<V){if(X<W|X>V){w=236;break}}else{if(X<V|X>W){w=236;break}}Y=+h[8083];D=+h[8084];if(Y<D){if(R<Y|R>D){w=236;break}}else{if(R<D|R>Y){w=236;break}}h[e>>3]=X;h[f>>3]=(c[16336]&2|0)==0?+h[8169]:+h[8170];h[g>>3]=R;i=j;return}}while(0);if((w|0)==236){R=U?Q:T;if(I){Z=R;w=237}else{_=R;w=239}}do{if((w|0)==237){if(Z<r){break}if(((a|0)==0?T:Q)<=n){w=241}}else if((w|0)==239){if(_<n){break}if(((a|0)==0?T:Q)<=r){w=241}}}while(0);do{if((w|0)==241){_=(a|0)==0?T:Q;if(!(_!=r&_!=n)){break}Z=_-r;_=n-r;R=q+Z*((t-q)/_);X=s+Z*((o-s)/_);if(W<V){if(R<W|R>V){break}}else{if(R<V|R>W){break}}_=+h[8083];Z=+h[8084];if(_<Z){if(X<_|X>Z){break}}else{if(X<Z|X>_){break}}h[e>>3]=R;h[f>>3]=(c[16336]&2|0)==0?+h[8170]:+h[8169];h[g>>3]=X;i=j;return}}while(0);a=s<o;I=c[16164]&2;U=(I|0)!=0;X=+h[8084];R=+h[8083];_=U?X:R;do{if(a){if(_<s){$=U?R:X;w=265;break}else{if(((I|0)==0?R:X)>o){w=264;break}else{w=256;break}}}else{if(_<o){aa=U?R:X;w=267;break}else{if(((I|0)==0?R:X)>s){w=264;break}else{w=256;break}}}}while(0);do{if((w|0)==256){_=(I|0)==0?R:X;if(!(_!=s&_!=o)){w=264;break}Z=_-s;_=o-s;Y=q+Z*((t-q)/_);D=r+Z*((n-r)/_);if(W<V){if(Y<W|Y>V){w=264;break}}else{if(Y<V|Y>W){w=264;break}}if(Q<T){if(D<Q|D>T){w=264;break}}else{if(D<T|D>Q){w=264;break}}h[e>>3]=Y;h[f>>3]=D;h[g>>3]=(c[16164]&2|0)==0?+h[8083]:+h[8084];i=j;return}}while(0);if((w|0)==264){D=U?R:X;if(a){$=D;w=265}else{aa=D;w=267}}do{if((w|0)==265){if($<s){break}if(((I|0)==0?X:R)<=o){w=269}}else if((w|0)==267){if(aa<o){break}if(((I|0)==0?X:R)<=s){w=269}}}while(0);do{if((w|0)==269){aa=(I|0)==0?X:R;if(!(aa!=s&aa!=o)){break}$=aa-s;aa=o-s;D=q+$*((t-q)/aa);Y=r+$*((n-r)/aa);if(W<V){if(D<W|D>V){break}}else{if(D<V|D>W){break}}if(Q<T){if(Y<Q|Y>T){break}}else{if(Y<T|Y>Q){break}}h[e>>3]=D;h[f>>3]=Y;h[g>>3]=(c[16164]&2|0)==0?+h[8084]:+h[8083];i=j;return}}while(0);h[e>>3]=q;h[f>>3]=r;h[g>>3]=s;i=j;return}function um(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;var g=0.0,i=0.0,j=0.0,k=0.0,l=0.0,m=0.0,n=0,o=0,p=0,q=0,r=0.0,s=0.0,t=0.0,u=0.0,v=0.0,w=0.0,x=0.0,y=0.0,z=0.0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0,G=0.0,H=0.0,I=0.0;g=+h[a+(b<<6)+8>>3];i=+h[a+(b<<6)+16>>3];j=+h[a+(b<<6)+24>>3];k=+h[a+(c<<6)+8>>3];l=+h[a+(c<<6)+16>>3];m=+h[a+(c<<6)+24>>3];c=g==-8.988465674311579e+307;a=k==-8.988465674311579e+307;b=i==-8.988465674311579e+307;n=l==-8.988465674311579e+307;o=j==-8.988465674311579e+307;p=m==-8.988465674311579e+307;if((((((o&1)+(b&1)|0)+(a?c?2:1:c&1)|0)+(n&1)|0)+(p&1)|0)>1){q=0;return q|0}if(a|c){r=c?l:i;s=c?m:j;if((c?k:g)<=+h[8256]){q=0;return q|0}t=+h[8169];u=+h[8170];do{if(t<u){if(r<t|r>u){q=0}else{break}return q|0}else{if(r<u|r>t){q=0}else{break}return q|0}}while(0);t=+h[8083];u=+h[8084];do{if(t<u){if(s<t|s>u){q=0}else{break}return q|0}else{if(s<u|s>t){q=0}else{break}return q|0}}while(0);h[d>>3]=+h[8255];h[e>>3]=r;h[f>>3]=s;h[d+8>>3]=+h[8256];h[e+8>>3]=r;h[f+8>>3]=s;q=1;return q|0}if(n|b){s=b?k:g;r=b?m:j;if((b?l:i)<=+h[8170]){q=0;return q|0}t=+h[8255];u=+h[8256];do{if(t<u){if(s<t|s>u){q=0}else{break}return q|0}else{if(s<u|s>t){q=0}else{break}return q|0}}while(0);t=+h[8083];u=+h[8084];do{if(t<u){if(r<t|r>u){q=0}else{break}return q|0}else{if(r<u|r>t){q=0}else{break}return q|0}}while(0);h[d>>3]=s;h[e>>3]=+h[8169];h[f>>3]=r;h[d+8>>3]=s;h[e+8>>3]=+h[8170];h[f+8>>3]=r;q=1;return q|0}if(p|o){r=o?k:g;s=o?l:i;if((o?m:j)<=+h[8084]){q=0;return q|0}t=+h[8255];u=+h[8256];do{if(t<u){if(r<t|r>u){q=0}else{break}return q|0}else{if(r<u|r>t){q=0}else{break}return q|0}}while(0);t=+h[8169];u=+h[8170];do{if(t<u){if(s<t|s>u){q=0}else{break}return q|0}else{if(s<u|s>t){q=0}else{break}return q|0}}while(0);h[d>>3]=r;h[e>>3]=s;h[f>>3]=+h[8083];h[d+8>>3]=r;h[e+8>>3]=s;h[f+8>>3]=+h[8084];q=1;return q|0}s=+h[8083];if((j>m?j:m)<s){q=0;return q|0}o=j<m;r=+h[8084];if((o?j:m)>r){q=0;return q|0}t=+h[8255];if((g>k?g:k)<t){q=0;return q|0}p=g<k;u=+h[8256];if((p?g:k)>u){q=0;return q|0}v=+h[8169];if((i>l?i:l)<v){q=0;return q|0}b=i<l;w=+h[8170];if((b?i:l)>w){q=0;return q|0}n=g==k;c=i==l;a=j==m;if(n&c&a){q=0;return q|0}if(n){n=t<u;if(c){do{if(n){if(g<t|g>u){q=0}else{break}return q|0}else{if(g<u|g>t){q=0}else{break}return q|0}}while(0);do{if(v<w){if(i<v|i>w){q=0}else{break}return q|0}else{if(i<w|i>v){q=0}else{break}return q|0}}while(0);do{if(o){if(s<j|s>m){q=0}else{break}return q|0}else{if(s<m|s>j){q=0}else{break}return q|0}}while(0);h[d>>3]=g;h[e>>3]=i;h[f>>3]=+h[8083];h[d+8>>3]=g;h[e+8>>3]=i;h[f+8>>3]=+h[8084];q=1;return q|0}if(a){do{if(n){if(g<t|g>u){q=0}else{break}return q|0}else{if(g<u|g>t){q=0}else{break}return q|0}}while(0);do{if(s<r){if(j<s|j>r){q=0}else{break}return q|0}else{if(j<r|j>s){q=0}else{break}return q|0}}while(0);do{if(b){if(v<i|v>l){q=0}else{break}return q|0}else{if(v<l|v>i){q=0}else{break}return q|0}}while(0);h[d>>3]=g;h[e>>3]=+h[8169];h[f>>3]=j;h[d+8>>3]=g;h[e+8>>3]=+h[8170];h[f+8>>3]=j;q=1;return q|0}else{do{if(n){if(k<t|k>u){q=0}else{break}return q|0}else{if(k<u|k>t){q=0}else{break}return q|0}}while(0);x=l-i;y=(v-i)/x;z=(w-i)/x;n=y>z;A=n?y:z;B=n?z:y;y=m-j;z=(s-j)/y;C=(r-j)/y;n=z>C;D=n?z:C;E=n?C:z;z=B>E?B:E;E=z>0.0?z:0.0;z=A<D?A:D;D=z<1.0?z:1.0;if(E>D){q=0;return q|0}h[d>>3]=g;h[e>>3]=i+x*E;h[f>>3]=j+y*E;h[d+8>>3]=g;h[e+8>>3]=i+x*D;h[f+8>>3]=j+y*D;D=+h[8169];y=+h[8170];x=+h[e>>3];if(D<y){if(!(x<D|x>y)){F=385}}else{if(!(x<y|x>D)){F=385}}do{if((F|0)==385){D=+h[8083];x=+h[8084];y=+h[f>>3];if(D<x){if(y<D|y>x){break}else{q=1}return q|0}else{if(y<x|y>D){break}else{q=1}return q|0}}}while(0);q=0;return q|0}}if(!c){if(!a){D=k-g;y=(t-g)/D;x=(u-g)/D;c=y>x;E=c?y:x;z=c?x:y;y=l-i;x=(v-i)/y;A=(w-i)/y;c=x>A;B=c?x:A;C=c?A:x;x=m-j;A=(s-j)/x;G=(r-j)/x;c=A>G;H=c?A:G;I=c?G:A;A=z>C?z:C;c=I>0.0;n=A>(c?I:0.0);C=n|c^1?n?A:0.0:I;I=E<B?E:B;n=H<1.0;c=I<(n?H:1.0);B=c|n^1?c?I:1.0:H;if(C>B){q=0;return q|0}h[d>>3]=g+D*C;h[e>>3]=i+y*C;h[f>>3]=j+x*C;h[d+8>>3]=g+D*B;h[e+8>>3]=i+y*B;h[f+8>>3]=j+x*B;B=+h[8255];x=+h[8256];y=+h[d>>3];if(B<x){if(!(y<B|y>x)){F=428}}else{if(!(y<x|y>B)){F=428}}do{if((F|0)==428){B=+h[8169];y=+h[8170];x=+h[e>>3];if(B<y){if(x<B|x>y){break}}else{if(x<y|x>B){break}}B=+h[8083];x=+h[8084];y=+h[f>>3];if(B<x){if(y<B|y>x){break}else{q=1}return q|0}else{if(y<x|y>B){break}else{q=1}return q|0}}}while(0);q=0;return q|0}do{if(s<r){if(m<s|m>r){q=0}else{break}return q|0}else{if(m<r|m>s){q=0}else{break}return q|0}}while(0);B=k-g;y=(t-g)/B;x=(u-g)/B;c=y>x;D=c?y:x;C=c?x:y;y=l-i;x=(v-i)/y;H=(w-i)/y;c=x>H;I=c?x:H;E=c?H:x;x=C>E?C:E;E=x>0.0?x:0.0;x=D<I?D:I;I=x<1.0?x:1.0;if(E>I){q=0;return q|0}h[d>>3]=g+B*E;h[e>>3]=i+y*E;h[f>>3]=j;h[d+8>>3]=g+B*I;h[e+8>>3]=i+y*I;h[f+8>>3]=j;I=+h[8255];y=+h[8256];B=+h[d>>3];if(I<y){if(!(B<I|B>y)){F=420}}else{if(!(B<y|B>I)){F=420}}do{if((F|0)==420){I=+h[8169];B=+h[8170];y=+h[e>>3];if(I<B){if(y<I|y>B){break}else{q=1}return q|0}else{if(y<B|y>I){break}else{q=1}return q|0}}}while(0);q=0;return q|0}c=v<w;if(a){do{if(c){if(i<v|i>w){q=0}else{break}return q|0}else{if(i<w|i>v){q=0}else{break}return q|0}}while(0);do{if(s<r){if(j<s|j>r){q=0}else{break}return q|0}else{if(j<r|j>s){q=0}else{break}return q|0}}while(0);do{if(p){if(t<g|t>k){q=0}else{break}return q|0}else{if(t<k|t>g){q=0}else{break}return q|0}}while(0);h[d>>3]=t;h[e>>3]=i;h[f>>3]=j;h[d+8>>3]=+h[8256];h[e+8>>3]=i;h[f+8>>3]=j;q=1;return q|0}else{do{if(c){if(l<v|l>w){q=0}else{break}return q|0}else{if(l<w|l>v){q=0}else{break}return q|0}}while(0);v=k-g;k=(t-g)/v;t=(u-g)/v;c=k>t;u=c?k:t;l=c?t:k;k=m-j;m=(s-j)/k;s=(r-j)/k;c=m>s;r=c?m:s;t=c?s:m;m=l>t?l:t;t=m>0.0?m:0.0;m=u<r?u:r;r=m<1.0?m:1.0;if(t>r){q=0;return q|0}h[d>>3]=g+v*t;h[e>>3]=i;h[f>>3]=j+k*t;h[d+8>>3]=g+v*r;h[e+8>>3]=i;h[f+8>>3]=j+k*r;r=+h[8255];k=+h[8256];j=+h[d>>3];if(r<k){if(!(j<r|j>k)){F=408}}else{if(!(j<k|j>r)){F=408}}do{if((F|0)==408){r=+h[8083];j=+h[8084];k=+h[f>>3];if(r<j){if(k<r|k>j){break}else{q=1}return q|0}else{if(k<j|k>r){break}else{q=1}return q|0}}}while(0);q=0;return q|0}return 0}function un(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0.0,u=0.0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0.0,D=0.0,E=0.0;g=i;i=i+64|0;j=f;f=i;i=i+16|0;c[f>>2]=c[j>>2];c[f+4>>2]=c[j+4>>2];c[f+8>>2]=c[j+8>>2];c[f+12>>2]=c[j+12>>2];j=g|0;k=g+16|0;l=g+24|0;m=g+32|0;n=g+40|0;o=g+48|0;p=c[e>>2]|0;q=c[e+4>>2]|0;r=e+8|0;s=e+16|0;t=+h[s>>3];u=+h[e+24>>3];v=e+32|0;w=a[v]|0;if((d|0)==0){x=~~(+h[b>>3]*+(c[180]|0))+(c[186]|0)|0;y=~~(+h[b+8>>3]*+(c[40]|0))+(c[46]|0)|0;h[e+48>>3]=+h[b+32>>3];z=j;A=e+40|0;c[z>>2]=c[A>>2];c[z+4>>2]=c[A+4>>2];c[z+8>>2]=c[A+8>>2];c[z+12>>2]=c[A+12>>2];do{if((p|0)!=0){A=c[(c[3524]|0)+92>>2]|0;if(u<0.0){cK[A&63](+h[3817]);break}else{cK[A&63](u);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[s>>3]);s=c[(c[3524]|0)+64>>2]|0;if((q|0)<-5){cM[s&511](-2)}else{cM[s&511](q)}s=c[3524]|0;do{if((a[v]&1)==0){if((c[s+96>>2]&1024|0)!=0){break}c[j>>2]=1;c[j+4>>2]=q;B=516}else{B=516}}while(0);if((B|0)==516){fn(j,s)}s=c[13542]|0;do{if((s|0)!=0){j=(c[s>>2]|0)>(x|0)&1;v=(c[s+4>>2]|0)<(x|0)?j|2:j;j=(c[s+8>>2]|0)>(y|0)?v|4:v;if((((c[s+12>>2]|0)<(y|0)?j|8:j)|0)==0){break}i=g;return}}while(0);cR[c[(c[3524]|0)+80>>2]&127](x,y,c[r>>2]|0);i=g;return}C=+(c[180]|0);y=c[186]|0;x=~~(+h[b>>3]*C)+y|0;D=+(c[40]|0);s=c[46]|0;j=~~(+h[b+8>>3]*D)+s|0;v=~~(C*+h[d>>3])+y|0;y=~~(D*+h[d+8>>3])+s|0;s=c[f>>2]|0;A=c[f+4>>2]|0;if((s|0)==6){E=(+h[b+32>>3]+ +h[d+32>>3])*.5}else{E=+h[f+8>>3]}f=o|0;c[f>>2]=s;s=o+4|0;c[s>>2]=A;h[o+8>>3]=E;do{if((p|0)!=0){A=c[(c[3524]|0)+92>>2]|0;if(u<0.0){cK[A&63](+h[3817]);break}else{cK[A&63](u);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](t);p=c[(c[3524]|0)+64>>2]|0;if((q|0)<-5){cM[p&511](-2)}else{cM[p&511](q)}p=c[3524]|0;do{if((w&1)==0){if((c[p+96>>2]&1024|0)!=0){break}c[f>>2]=1;c[s>>2]=q;B=532}else{B=532}}while(0);if((B|0)==532){fn(o,p)}p=c[r>>2]|0;if((p|0)==(-10|0)){fm(x,j,v,y,1);i=g;return}else if((p|0)==(-11|0)){fm(x,j,v,y,2);i=g;return}else{c[k>>2]=x;c[l>>2]=j;c[m>>2]=v;c[n>>2]=y;y=c[3524]|0;if((fl(k,l,m,n)|0)==0){i=g;return}cN[c[y+56>>2]&255](c[k>>2]|0,c[l>>2]|0);cN[c[y+60>>2]&255](c[m>>2]|0,c[n>>2]|0);i=g;return}}function uo(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0.0,m=0;e=i;i=i+16|0;f=e|0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(b,0,d);i=e;return}}while(0);g=~~(+h[b>>3]*+(c[180]|0))+(c[186]|0)|0;j=~~(+h[b+8>>3]*+(c[40]|0))+(c[46]|0)|0;h[d+48>>3]=+h[b+32>>3];b=f;k=d+40|0;c[b>>2]=c[k>>2];c[b+4>>2]=c[k+4>>2];c[b+8>>2]=c[k+8>>2];c[b+12>>2]=c[k+12>>2];k=c[d+4>>2]|0;do{if((c[d>>2]|0)!=0){l=+h[d+24>>3];b=c[(c[3524]|0)+92>>2]|0;if(l<0.0){cK[b&63](+h[3817]);break}else{cK[b&63](l);break}}}while(0);cK[c[(c[3524]|0)+112>>2]&63](+h[d+16>>3]);b=c[(c[3524]|0)+64>>2]|0;if((k|0)<-5){cM[b&511](-2)}else{cM[b&511](k)}b=c[3524]|0;do{if((a[d+32|0]&1)==0){if((c[b+96>>2]&1024|0)!=0){break}c[f>>2]=1;c[f+4>>2]=k;m=558}else{m=558}}while(0);if((m|0)==558){fn(f,b)}b=c[13542]|0;do{if((b|0)!=0){f=(c[b>>2]|0)>(g|0)&1;m=(c[b+4>>2]|0)<(g|0)?f|2:f;f=(c[b+8>>2]|0)>(j|0)?m|4:m;if((((c[b+12>>2]|0)<(j|0)?f|8:f)|0)==0){break}i=e;return}}while(0);cR[c[(c[3524]|0)+80>>2]&127](g,j,c[d+8>>2]|0);i=e;return}function up(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0.0,n=0.0,o=0,p=0,q=0;e=i;i=i+32|0;f=e|0;g=e+8|0;j=e+16|0;k=e+24|0;do{if((a[38984]&1)!=0){if((a[46752]&1)==0){break}gt(30448,b,d);l=b;c[7612]=c[l>>2];c[7613]=c[l+4>>2];c[7614]=c[l+8>>2];c[7615]=c[l+12>>2];c[7616]=c[l+16>>2];c[7617]=c[l+20>>2];c[7618]=c[l+24>>2];c[7619]=c[l+28>>2];c[7620]=c[l+32>>2];c[7621]=c[l+36>>2];c[7622]=c[l+40>>2];c[7623]=c[l+44>>2];i=e;return}}while(0);if((a[d+32|0]&1)!=0){un(30448,b,d,d+40|0);d=b;c[7612]=c[d>>2];c[7613]=c[d+4>>2];c[7614]=c[d+8>>2];c[7615]=c[d+12>>2];c[7616]=c[d+16>>2];c[7617]=c[d+20>>2];c[7618]=c[d+24>>2];c[7619]=c[d+28>>2];c[7620]=c[d+32>>2];c[7621]=c[d+36>>2];c[7622]=c[d+40>>2];c[7623]=c[d+44>>2];i=e;return}m=+(c[180]|0);d=c[186]|0;n=+(c[40]|0);l=c[46]|0;o=~~(+h[3807]*n)+l|0;p=~~(m*+h[b>>3])+d|0;q=~~(n*+h[b+8>>3])+l|0;c[f>>2]=~~(+h[3806]*m)+d;c[g>>2]=o;c[j>>2]=p;c[k>>2]=q;q=c[3524]|0;if((fl(f,g,j,k)|0)!=0){cN[c[q+56>>2]&255](c[f>>2]|0,c[g>>2]|0);cN[c[q+60>>2]&255](c[j>>2]|0,c[k>>2]|0)}k=b;c[7612]=c[k>>2];c[7613]=c[k+4>>2];c[7614]=c[k+8>>2];c[7615]=c[k+12>>2];c[7616]=c[k+16>>2];c[7617]=c[k+20>>2];c[7618]=c[k+24>>2];c[7619]=c[k+28>>2];c[7620]=c[k+32>>2];c[7621]=c[k+36>>2];c[7622]=c[k+40>>2];c[7623]=c[k+44>>2];i=e;return}function uq(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0;e=i;do{if((b|0)==32){uu(c[8776]|0);c[8780]=0;c[8774]=0;c[8776]=0;c[8778]=0;f=582}else if((b|0)==1){if((c[8776]|0)==0){f=582;break}uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=198504,v)|0);return 0}else if((b|0)==4){if((d|0)==0){break}if((a[d]|0)==0){break}g=c[8782]|0;h=(c[8780]|0)-g|0;j=uA(d|0)|0;k=c[8776]|0;l=h+1|0;if((k|0)!=0&(g|0)!=0){uH(k|0,g|0,l|0);n=c[8776]|0}else{n=k}k=j+1|0;g=db(n,k+l|0,131160)|0;c[8776]=g;uH(g+k|0,g|0,l|0);uB(c[8776]|0,d|0);a[(c[8776]|0)+j|0]=58;j=c[8776]|0;c[8782]=j+k;c[8780]=j+(k+h|0);h=a8(j|0,58)|0;if((h|0)==0){break}else{o=h}do{a[o]=0;o=a8(o+1|0,58)|0;}while((o|0)!=0)}else if((b|0)==2){h=c[m>>2]|0;do{if((c[8776]|0)==0){aI(90464,19,1,h|0)}else{aI(116392,13,1,h|0);j=c[8776]|0;k=c[m>>2]|0;if(j>>>0<(c[8782]|0)>>>0){l=j;j=k;while(1){cf(j|0,103640,(v=i,i=i+8|0,c[v>>2]=l,v)|0);g=l+((uA(l|0)|0)+1|0)|0;p=c[m>>2]|0;if(g>>>0<(c[8782]|0)>>>0){l=g;j=p}else{q=p;break}}}else{q=k}aF(10,q|0);if((c[8782]|0)==0){break}j=c[m>>2]|0;aI(96440,30,1,j|0);j=c[8782]|0;l=c[m>>2]|0;if(j>>>0<(c[8780]|0)>>>0){p=j;j=l;while(1){cf(j|0,103640,(v=i,i=i+8|0,c[v>>2]=p,v)|0);g=p+((uA(p|0)|0)+1|0)|0;r=c[m>>2]|0;if(g>>>0<(c[8780]|0)>>>0){p=g;j=r}else{s=r;break}}}else{s=l}aF(10,s|0)}}while(0);cf(c[m>>2]|0,81808,(v=i,i=i+8|0,c[v>>2]=74944,v)|0)}else if((b|0)==16){c[8778]=c[8782];f=603}else if((b|0)==8){f=603}}while(0);if((f|0)==582){b=bU(148328)|0;do{if((b|0)==0){t=0}else{s=uA(b|0)|0;q=bP(b|0)|0;c[8776]=q;c[8780]=q+s;s=a8(q|0,58)|0;if((s|0)==0){t=q;break}else{u=s}do{a[u]=0;u=a8(u+1|0,58)|0;}while((u|0)!=0);t=c[8776]|0}}while(0);c[8782]=t}else if((f|0)==603){f=c[8776]|0;if((f|0)==0){w=0;i=e;return w|0}t=c[8774]|0;do{if((t|0)==0){c[8774]=f;u=c[8778]|0;if((u|0)!=0){x=f;y=u;break}u=c[8780]|0;c[8778]=u;x=f;y=u}else{u=t+((uA(t|0)|0)+1|0)|0;c[8774]=u;x=u;y=c[8778]|0}}while(0);if(x>>>0<y>>>0){w=x;i=e;return w|0}c[8774]=0;c[8778]=0;w=0;i=e;return w|0}w=c[8776]|0;i=e;return w|0}function ur(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0;e=i;i=i+256|0;f=e|0;if(!(a[42184]|0)){a[42184]=1;ur(1,0)}do{if((b|0)==32){uu(c[10554]|0);c[10552]=0;c[10548]=0;c[10554]=0;c[10550]=0;g=622}else if((b|0)==1){if((c[10554]|0)==0){g=622;break}uf(-1,113648,(v=i,i=i+8|0,c[v>>2]=222176,v)|0);return 0}else if((b|0)==4){if((d|0)==0){break}if((a[d]|0)==0){break}h=c[10556]|0;j=(c[10552]|0)-h|0;k=uA(d|0)|0;l=c[10554]|0;n=j+1|0;if((l|0)!=0&(h|0)!=0){uH(l|0,h|0,n|0);o=c[10554]|0}else{o=l}l=k+1|0;h=db(o,l+n|0,199664)|0;c[10554]=h;uH(h+l|0,h|0,n|0);uB(c[10554]|0,d|0);a[(c[10554]|0)+k|0]=58;k=c[10554]|0;c[10556]=k+l;c[10552]=k+(l+j|0);j=a8(k|0,58)|0;if((j|0)==0){break}else{p=j}do{a[p]=0;p=a8(p+1|0,58)|0;}while((p|0)!=0)}else if((b|0)==2){j=c[m>>2]|0;if((c[10554]|0)==0){aI(174752,19,1,j|0);break}aI(184128,13,1,j|0);j=c[10554]|0;k=c[m>>2]|0;if(j>>>0<(c[10556]|0)>>>0){l=j;j=k;while(1){cf(j|0,103640,(v=i,i=i+8|0,c[v>>2]=l,v)|0);n=l+((uA(l|0)|0)+1|0)|0;h=c[m>>2]|0;if(n>>>0<(c[10556]|0)>>>0){l=n;j=h}else{q=h;break}}}else{q=k}aF(10,q|0);if((c[10556]|0)==0){break}aI(178936,20,1,c[m>>2]|0);j=c[10556]|0;l=c[m>>2]|0;if(j>>>0<(c[10552]|0)>>>0){h=j;j=l;while(1){cf(j|0,103640,(v=i,i=i+8|0,c[v>>2]=h,v)|0);n=h+((uA(h|0)|0)+1|0)|0;r=c[m>>2]|0;if(n>>>0<(c[10552]|0)>>>0){h=n;j=r}else{s=r;break}}}else{s=l}aF(10,s|0)}else if((b|0)==16){c[10550]=c[10556];g=683}else if((b|0)==8){g=683}}while(0);if((g|0)==683){b=c[10554]|0;if((b|0)==0){t=0;i=e;return t|0}s=c[10548]|0;do{if((s|0)==0){c[10548]=b;q=c[10550]|0;if((q|0)!=0){u=b;w=q;break}q=c[10552]|0;c[10550]=q;u=b;w=q}else{q=s+((uA(s|0)|0)+1|0)|0;c[10548]=q;u=q;w=c[10550]|0}}while(0);if(u>>>0<w>>>0){t=u;i=e;return t|0}c[10548]=0;c[10550]=0;t=0;i=e;return t|0}if((g|0)==622){u=bU(215376)|0;do{if((u|0)==0){w=f|0;s=42160;b=137720;L863:while(1){q=bP(b|0)|0;while(1){p=aQ(q|0,209688)|0;if((p|0)!=0){d=p+2|0;o=a8(d|0,41)|0;a[o]=0;j=bU(d|0)|0;a[o]=41;d=(j|0)!=0;if(d){x=uA(j|0)|0}else{x=0}h=((p+((uA(q|0)|0)+x|0)|0)-o|0)+1|0;k=ut(h)|0;if((k|0)==0){gk();r=ut(h)|0;if((r|0)==0){g=633;break L863}else{y=r}}else{y=k}k=p-q|0;uF(y|0,q|0,k|0);if(d){d=y+k|0;uB(d|0,j|0)}j=y+(x+k|0)|0;k=o+1|0;uB(j|0,k|0);uu(q);q=y;continue}k=aQ(q|0,204200)|0;if((k|0)==0){break}j=k+2|0;o=a8(j|0,96)|0;a[o]=0;if(!(a[14168]|0)){g=638;break L863}d=bt(j|0,193632)|0;if((d|0)!=0){a6(w|0,255,d|0);j=f+((uA(w|0)|0)-1|0)|0;if((a[j]|0)==10){a[j]=0}a2(d|0)}a[o]=96;d=uA(w|0)|0;j=((k+((uA(q|0)|0)+d|0)|0)-o|0)+1|0;p=ut(j)|0;if((p|0)==0){gk();r=ut(j)|0;if((r|0)==0){g=645;break L863}else{z=r}}else{z=p}p=k-q|0;uF(z|0,q|0,p|0);if((a[w]|0)!=0){k=z+p|0;uB(k|0,w|0)}uB(z+(d+p|0)|0,o+1|0);uu(q);q=z}o=q+((uA(q|0)|0)-1|0)|0;if((a[o]|0)==33){a[o]=0;A=1}else{A=0}o=aW(q|0)|0;if((o|0)!=0){bR(o|0);if(A){a[q+(uA(q|0)|0)|0]=33}o=uA(q|0)|0;p=c[10554]|0;if((p|0)==0){d=o+1|0;k=ut(d)|0;if((k|0)==0){gk();r=ut(d)|0;if((r|0)==0){g=658;break}else{B=r}}else{B=k}c[10554]=B;c[10552]=B;C=B}else{k=uA(p|0)|0;r=db(p,(o+2|0)+k|0,199664)|0;c[10554]=r;p=r+k|0;c[10552]=p;a[p]=58;p=(c[10552]|0)+1|0;c[10552]=p;a[p]=0;C=c[10552]|0}uB(C|0,q|0);c[10552]=(c[10552]|0)+o}o=s+4|0;if((q|0)!=0){uu(q)}p=c[o>>2]|0;if((p|0)==0){g=664;break}else{s=o;b=p}}if((g|0)==633){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=199664,v)|0);return 0}else if((g|0)==638){uf(-1,145320,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}else if((g|0)==645){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=199664,v)|0);return 0}else if((g|0)==658){uf(-1,225296,(v=i,i=i+8|0,c[v>>2]=199664,v)|0);return 0}else if((g|0)==664){b=c[10554]|0;if((b|0)==0){break}s=a8(b|0,58)|0;if((s|0)==0){break}else{D=s}do{a[D]=0;D=a8(D+1|0,58)|0;}while((D|0)!=0)}}else{s=uA(u|0)|0;b=bP(u|0)|0;c[10554]=b;c[10552]=b+s;s=a8(b|0,58)|0;if((s|0)==0){break}else{E=s}do{a[E]=0;E=a8(E+1|0,58)|0;}while((E|0)!=0)}}while(0);c[10556]=c[10554]}t=c[10554]|0;i=e;return t|0}function us(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=i;i=i+48|0;e=d|0;if((a|0)==32|(a|0)==1){uu(c[12908]|0);bA(5,179864);bA(2,179864);f=bA(5,0)|0;if((f|0)==0){g=0}else{g=bP(f|0)|0}c[12908]=g;h=c[12908]|0;i=d;return h|0}else if((a|0)==4){if((bA(5,b|0)|0)==0){uf(c[13898]|0,166696,(v=i,i=i+1|0,i=i+7>>3<<3,c[v>>2]=0,v)|0);return 0}uu(c[12908]|0);b=bA(5,0)|0;if((b|0)==0){j=0}else{j=bP(b|0)|0}c[12908]=j;j=e+24|0;uE(e|0,0,44);bw(40936,32,161480,e|0);bw(72464,8,153384,e|0);c[j>>2]=1;bw(40968,32,161480,e|0);bw(72472,8,153384,e|0);c[j>>2]=2;bw(41e3,32,161480,e|0);bw(72480,8,153384,e|0);c[j>>2]=3;bw(41032,32,161480,e|0);bw(72488,8,153384,e|0);c[j>>2]=4;bw(41064,32,161480,e|0);bw(72496,8,153384,e|0);c[j>>2]=5;bw(41096,32,161480,e|0);bw(72504,8,153384,e|0);c[j>>2]=6;bw(41128,32,161480,e|0);bw(72512,8,153384,e|0);j=e+16|0;b=0;do{c[j>>2]=b;bw(40552+(b<<5)|0,32,150680,e|0);bw(72368+(b<<3)|0,8,148784,e|0);b=b+1|0;}while((b|0)<12);h=c[12908]|0;i=d;return h|0}else if((a|0)==2){a=c[m>>2]|0;b=bA(2,0)|0;cf(a|0,146672,(v=i,i=i+8|0,c[v>>2]=b,v)|0);cf(c[m>>2]|0,144576,(v=i,i=i+8|0,c[v>>2]=c[44936+(c[11252]<<2)>>2],v)|0);b=c[m>>2]|0;a=bA(5,0)|0;cf(b|0,142976,(v=i,i=i+8|0,c[v>>2]=a,v)|0);a=c[8270]|0;cf(c[m>>2]|0,141432,(v=i,i=i+8|0,c[v>>2]=(a|0)!=0?a:139696,v)|0);h=c[12908]|0;i=d;return h|0}else{h=c[12908]|0;i=d;return h|0}return 0}
+function ut(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ab=0,ac=0,ad=0,ae=0,af=0,ag=0,ah=0,ai=0,aj=0,ak=0,al=0,am=0,an=0,ao=0,ap=0,aq=0,ar=0,as=0,at=0,au=0,av=0,aw=0,ax=0,ay=0,az=0,aA=0,aB=0,aC=0,aD=0,aE=0,aF=0,aG=0;do{if(a>>>0<245){if(a>>>0<11){b=16}else{b=a+11&-8}d=b>>>3;e=c[56330]|0;f=e>>>(d>>>0);if((f&3|0)!=0){g=(f&1^1)+d|0;h=g<<1;i=225360+(h<<2)|0;j=225360+(h+2<<2)|0;h=c[j>>2]|0;k=h+8|0;l=c[k>>2]|0;do{if((i|0)==(l|0)){c[56330]=e&(1<<g^-1)}else{if(l>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}m=l+12|0;if((c[m>>2]|0)==(h|0)){c[m>>2]=i;c[j>>2]=l;break}else{ce();return 0;return 0}}}while(0);l=g<<3;c[h+4>>2]=l|3;j=h+(l|4)|0;c[j>>2]=c[j>>2]|1;n=k;return n|0}if(b>>>0<=(c[56332]|0)>>>0){o=b;break}if((f|0)!=0){j=2<<d;l=f<<d&(j|-j);j=(l&-l)-1|0;l=j>>>12&16;i=j>>>(l>>>0);j=i>>>5&8;m=i>>>(j>>>0);i=m>>>2&4;p=m>>>(i>>>0);m=p>>>1&2;q=p>>>(m>>>0);p=q>>>1&1;r=(j|l|i|m|p)+(q>>>(p>>>0))|0;p=r<<1;q=225360+(p<<2)|0;m=225360+(p+2<<2)|0;p=c[m>>2]|0;i=p+8|0;l=c[i>>2]|0;do{if((q|0)==(l|0)){c[56330]=e&(1<<r^-1)}else{if(l>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}j=l+12|0;if((c[j>>2]|0)==(p|0)){c[j>>2]=q;c[m>>2]=l;break}else{ce();return 0;return 0}}}while(0);l=r<<3;m=l-b|0;c[p+4>>2]=b|3;q=p;e=q+b|0;c[q+(b|4)>>2]=m|1;c[q+l>>2]=m;l=c[56332]|0;if((l|0)!=0){q=c[56335]|0;d=l>>>3;l=d<<1;f=225360+(l<<2)|0;k=c[56330]|0;h=1<<d;do{if((k&h|0)==0){c[56330]=k|h;s=f;t=225360+(l+2<<2)|0}else{d=225360+(l+2<<2)|0;g=c[d>>2]|0;if(g>>>0>=(c[56334]|0)>>>0){s=g;t=d;break}ce();return 0;return 0}}while(0);c[t>>2]=q;c[s+12>>2]=q;c[q+8>>2]=s;c[q+12>>2]=f}c[56332]=m;c[56335]=e;n=i;return n|0}l=c[56331]|0;if((l|0)==0){o=b;break}h=(l&-l)-1|0;l=h>>>12&16;k=h>>>(l>>>0);h=k>>>5&8;p=k>>>(h>>>0);k=p>>>2&4;r=p>>>(k>>>0);p=r>>>1&2;d=r>>>(p>>>0);r=d>>>1&1;g=c[225624+((h|l|k|p|r)+(d>>>(r>>>0))<<2)>>2]|0;r=g;d=g;p=(c[g+4>>2]&-8)-b|0;while(1){g=c[r+16>>2]|0;if((g|0)==0){k=c[r+20>>2]|0;if((k|0)==0){break}else{u=k}}else{u=g}g=(c[u+4>>2]&-8)-b|0;k=g>>>0<p>>>0;r=u;d=k?u:d;p=k?g:p}r=d;i=c[56334]|0;if(r>>>0<i>>>0){ce();return 0;return 0}e=r+b|0;m=e;if(r>>>0>=e>>>0){ce();return 0;return 0}e=c[d+24>>2]|0;f=c[d+12>>2]|0;do{if((f|0)==(d|0)){q=d+20|0;g=c[q>>2]|0;if((g|0)==0){k=d+16|0;l=c[k>>2]|0;if((l|0)==0){v=0;break}else{w=l;x=k}}else{w=g;x=q}while(1){q=w+20|0;g=c[q>>2]|0;if((g|0)!=0){w=g;x=q;continue}q=w+16|0;g=c[q>>2]|0;if((g|0)==0){break}else{w=g;x=q}}if(x>>>0<i>>>0){ce();return 0;return 0}else{c[x>>2]=0;v=w;break}}else{q=c[d+8>>2]|0;if(q>>>0<i>>>0){ce();return 0;return 0}g=q+12|0;if((c[g>>2]|0)!=(d|0)){ce();return 0;return 0}k=f+8|0;if((c[k>>2]|0)==(d|0)){c[g>>2]=f;c[k>>2]=q;v=f;break}else{ce();return 0;return 0}}}while(0);L1026:do{if((e|0)!=0){f=d+28|0;i=225624+(c[f>>2]<<2)|0;do{if((d|0)==(c[i>>2]|0)){c[i>>2]=v;if((v|0)!=0){break}c[56331]=c[56331]&(1<<c[f>>2]^-1);break L1026}else{if(e>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}q=e+16|0;if((c[q>>2]|0)==(d|0)){c[q>>2]=v}else{c[e+20>>2]=v}if((v|0)==0){break L1026}}}while(0);if(v>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}c[v+24>>2]=e;f=c[d+16>>2]|0;do{if((f|0)!=0){if(f>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[v+16>>2]=f;c[f+24>>2]=v;break}}}while(0);f=c[d+20>>2]|0;if((f|0)==0){break}if(f>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[v+20>>2]=f;c[f+24>>2]=v;break}}}while(0);if(p>>>0<16){e=p+b|0;c[d+4>>2]=e|3;f=r+(e+4|0)|0;c[f>>2]=c[f>>2]|1}else{c[d+4>>2]=b|3;c[r+(b|4)>>2]=p|1;c[r+(p+b|0)>>2]=p;f=c[56332]|0;if((f|0)!=0){e=c[56335]|0;i=f>>>3;f=i<<1;q=225360+(f<<2)|0;k=c[56330]|0;g=1<<i;do{if((k&g|0)==0){c[56330]=k|g;y=q;z=225360+(f+2<<2)|0}else{i=225360+(f+2<<2)|0;l=c[i>>2]|0;if(l>>>0>=(c[56334]|0)>>>0){y=l;z=i;break}ce();return 0;return 0}}while(0);c[z>>2]=e;c[y+12>>2]=e;c[e+8>>2]=y;c[e+12>>2]=q}c[56332]=p;c[56335]=m}f=d+8|0;if((f|0)==0){o=b;break}else{n=f}return n|0}else{if(a>>>0>4294967231){o=-1;break}f=a+11|0;g=f&-8;k=c[56331]|0;if((k|0)==0){o=g;break}r=-g|0;i=f>>>8;do{if((i|0)==0){A=0}else{if(g>>>0>16777215){A=31;break}f=(i+1048320|0)>>>16&8;l=i<<f;h=(l+520192|0)>>>16&4;j=l<<h;l=(j+245760|0)>>>16&2;B=(14-(h|f|l)|0)+(j<<l>>>15)|0;A=g>>>((B+7|0)>>>0)&1|B<<1}}while(0);i=c[225624+(A<<2)>>2]|0;L1074:do{if((i|0)==0){C=0;D=r;E=0}else{if((A|0)==31){F=0}else{F=25-(A>>>1)|0}d=0;m=r;p=i;q=g<<F;e=0;while(1){B=c[p+4>>2]&-8;l=B-g|0;if(l>>>0<m>>>0){if((B|0)==(g|0)){C=p;D=l;E=p;break L1074}else{G=p;H=l}}else{G=d;H=m}l=c[p+20>>2]|0;B=c[p+16+(q>>>31<<2)>>2]|0;j=(l|0)==0|(l|0)==(B|0)?e:l;if((B|0)==0){C=G;D=H;E=j;break}else{d=G;m=H;p=B;q=q<<1;e=j}}}}while(0);if((E|0)==0&(C|0)==0){i=2<<A;r=k&(i|-i);if((r|0)==0){o=g;break}i=(r&-r)-1|0;r=i>>>12&16;e=i>>>(r>>>0);i=e>>>5&8;q=e>>>(i>>>0);e=q>>>2&4;p=q>>>(e>>>0);q=p>>>1&2;m=p>>>(q>>>0);p=m>>>1&1;I=c[225624+((i|r|e|q|p)+(m>>>(p>>>0))<<2)>>2]|0}else{I=E}if((I|0)==0){J=D;K=C}else{p=I;m=D;q=C;while(1){e=(c[p+4>>2]&-8)-g|0;r=e>>>0<m>>>0;i=r?e:m;e=r?p:q;r=c[p+16>>2]|0;if((r|0)!=0){p=r;m=i;q=e;continue}r=c[p+20>>2]|0;if((r|0)==0){J=i;K=e;break}else{p=r;m=i;q=e}}}if((K|0)==0){o=g;break}if(J>>>0>=((c[56332]|0)-g|0)>>>0){o=g;break}q=K;m=c[56334]|0;if(q>>>0<m>>>0){ce();return 0;return 0}p=q+g|0;k=p;if(q>>>0>=p>>>0){ce();return 0;return 0}e=c[K+24>>2]|0;i=c[K+12>>2]|0;do{if((i|0)==(K|0)){r=K+20|0;d=c[r>>2]|0;if((d|0)==0){j=K+16|0;B=c[j>>2]|0;if((B|0)==0){L=0;break}else{M=B;N=j}}else{M=d;N=r}while(1){r=M+20|0;d=c[r>>2]|0;if((d|0)!=0){M=d;N=r;continue}r=M+16|0;d=c[r>>2]|0;if((d|0)==0){break}else{M=d;N=r}}if(N>>>0<m>>>0){ce();return 0;return 0}else{c[N>>2]=0;L=M;break}}else{r=c[K+8>>2]|0;if(r>>>0<m>>>0){ce();return 0;return 0}d=r+12|0;if((c[d>>2]|0)!=(K|0)){ce();return 0;return 0}j=i+8|0;if((c[j>>2]|0)==(K|0)){c[d>>2]=i;c[j>>2]=r;L=i;break}else{ce();return 0;return 0}}}while(0);L1124:do{if((e|0)!=0){i=K+28|0;m=225624+(c[i>>2]<<2)|0;do{if((K|0)==(c[m>>2]|0)){c[m>>2]=L;if((L|0)!=0){break}c[56331]=c[56331]&(1<<c[i>>2]^-1);break L1124}else{if(e>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}r=e+16|0;if((c[r>>2]|0)==(K|0)){c[r>>2]=L}else{c[e+20>>2]=L}if((L|0)==0){break L1124}}}while(0);if(L>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}c[L+24>>2]=e;i=c[K+16>>2]|0;do{if((i|0)!=0){if(i>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[L+16>>2]=i;c[i+24>>2]=L;break}}}while(0);i=c[K+20>>2]|0;if((i|0)==0){break}if(i>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[L+20>>2]=i;c[i+24>>2]=L;break}}}while(0);do{if(J>>>0<16){e=J+g|0;c[K+4>>2]=e|3;i=q+(e+4|0)|0;c[i>>2]=c[i>>2]|1}else{c[K+4>>2]=g|3;c[q+(g|4)>>2]=J|1;c[q+(J+g|0)>>2]=J;i=J>>>3;if(J>>>0<256){e=i<<1;m=225360+(e<<2)|0;r=c[56330]|0;j=1<<i;do{if((r&j|0)==0){c[56330]=r|j;O=m;P=225360+(e+2<<2)|0}else{i=225360+(e+2<<2)|0;d=c[i>>2]|0;if(d>>>0>=(c[56334]|0)>>>0){O=d;P=i;break}ce();return 0;return 0}}while(0);c[P>>2]=k;c[O+12>>2]=k;c[q+(g+8|0)>>2]=O;c[q+(g+12|0)>>2]=m;break}e=p;j=J>>>8;do{if((j|0)==0){Q=0}else{if(J>>>0>16777215){Q=31;break}r=(j+1048320|0)>>>16&8;i=j<<r;d=(i+520192|0)>>>16&4;B=i<<d;i=(B+245760|0)>>>16&2;l=(14-(d|r|i)|0)+(B<<i>>>15)|0;Q=J>>>((l+7|0)>>>0)&1|l<<1}}while(0);j=225624+(Q<<2)|0;c[q+(g+28|0)>>2]=Q;c[q+(g+20|0)>>2]=0;c[q+(g+16|0)>>2]=0;m=c[56331]|0;l=1<<Q;if((m&l|0)==0){c[56331]=m|l;c[j>>2]=e;c[q+(g+24|0)>>2]=j;c[q+(g+12|0)>>2]=e;c[q+(g+8|0)>>2]=e;break}if((Q|0)==31){R=0}else{R=25-(Q>>>1)|0}l=J<<R;m=c[j>>2]|0;while(1){if((c[m+4>>2]&-8|0)==(J|0)){break}S=m+16+(l>>>31<<2)|0;j=c[S>>2]|0;if((j|0)==0){T=862;break}else{l=l<<1;m=j}}if((T|0)==862){if(S>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[S>>2]=e;c[q+(g+24|0)>>2]=m;c[q+(g+12|0)>>2]=e;c[q+(g+8|0)>>2]=e;break}}l=m+8|0;j=c[l>>2]|0;i=c[56334]|0;if(m>>>0<i>>>0){ce();return 0;return 0}if(j>>>0<i>>>0){ce();return 0;return 0}else{c[j+12>>2]=e;c[l>>2]=e;c[q+(g+8|0)>>2]=j;c[q+(g+12|0)>>2]=m;c[q+(g+24|0)>>2]=0;break}}}while(0);q=K+8|0;if((q|0)==0){o=g;break}else{n=q}return n|0}}while(0);K=c[56332]|0;if(o>>>0<=K>>>0){S=K-o|0;J=c[56335]|0;if(S>>>0>15){R=J;c[56335]=R+o;c[56332]=S;c[R+(o+4|0)>>2]=S|1;c[R+K>>2]=S;c[J+4>>2]=o|3}else{c[56332]=0;c[56335]=0;c[J+4>>2]=K|3;S=J+(K+4|0)|0;c[S>>2]=c[S>>2]|1}n=J+8|0;return n|0}J=c[56333]|0;if(o>>>0<J>>>0){S=J-o|0;c[56333]=S;J=c[56336]|0;K=J;c[56336]=K+o;c[K+(o+4|0)>>2]=S|1;c[J+4>>2]=o|3;n=J+8|0;return n|0}do{if((c[8408]|0)==0){J=cb(8)|0;if((J-1&J|0)==0){c[8410]=J;c[8409]=J;c[8411]=-1;c[8412]=2097152;c[8413]=0;c[56441]=0;c[8408]=cG(0)&-16^1431655768;break}else{ce();return 0;return 0}}}while(0);J=o+48|0;S=c[8410]|0;K=o+47|0;R=S+K|0;Q=-S|0;S=R&Q;if(S>>>0<=o>>>0){n=0;return n|0}O=c[56440]|0;do{if((O|0)!=0){P=c[56438]|0;L=P+S|0;if(L>>>0<=P>>>0|L>>>0>O>>>0){n=0}else{break}return n|0}}while(0);L1216:do{if((c[56441]&4|0)==0){O=c[56336]|0;L1218:do{if((O|0)==0){T=892}else{L=O;P=225768;while(1){U=P|0;M=c[U>>2]|0;if(M>>>0<=L>>>0){V=P+4|0;if((M+(c[V>>2]|0)|0)>>>0>L>>>0){break}}M=c[P+8>>2]|0;if((M|0)==0){T=892;break L1218}else{P=M}}if((P|0)==0){T=892;break}L=R-(c[56333]|0)&Q;if(L>>>0>=2147483647){W=0;break}m=b_(L|0)|0;e=(m|0)==((c[U>>2]|0)+(c[V>>2]|0)|0);X=e?m:-1;Y=e?L:0;Z=m;_=L;T=901}}while(0);do{if((T|0)==892){O=b_(0)|0;if((O|0)==-1){W=0;break}g=O;L=c[8409]|0;m=L-1|0;if((m&g|0)==0){$=S}else{$=(S-g|0)+(m+g&-L)|0}L=c[56438]|0;g=L+$|0;if(!($>>>0>o>>>0&$>>>0<2147483647)){W=0;break}m=c[56440]|0;if((m|0)!=0){if(g>>>0<=L>>>0|g>>>0>m>>>0){W=0;break}}m=b_($|0)|0;g=(m|0)==(O|0);X=g?O:-1;Y=g?$:0;Z=m;_=$;T=901}}while(0);L1238:do{if((T|0)==901){m=-_|0;if((X|0)!=-1){aa=Y;ab=X;T=912;break L1216}do{if((Z|0)!=-1&_>>>0<2147483647&_>>>0<J>>>0){g=c[8410]|0;O=(K-_|0)+g&-g;if(O>>>0>=2147483647){ac=_;break}if((b_(O|0)|0)==-1){b_(m|0);W=Y;break L1238}else{ac=O+_|0;break}}else{ac=_}}while(0);if((Z|0)==-1){W=Y}else{aa=ac;ab=Z;T=912;break L1216}}}while(0);c[56441]=c[56441]|4;ad=W;T=909}else{ad=0;T=909}}while(0);do{if((T|0)==909){if(S>>>0>=2147483647){break}W=b_(S|0)|0;Z=b_(0)|0;if(!((Z|0)!=-1&(W|0)!=-1&W>>>0<Z>>>0)){break}ac=Z-W|0;Z=ac>>>0>(o+40|0)>>>0;Y=Z?W:-1;if((Y|0)!=-1){aa=Z?ac:ad;ab=Y;T=912}}}while(0);do{if((T|0)==912){ad=(c[56438]|0)+aa|0;c[56438]=ad;if(ad>>>0>(c[56439]|0)>>>0){c[56439]=ad}ad=c[56336]|0;L1258:do{if((ad|0)==0){S=c[56334]|0;if((S|0)==0|ab>>>0<S>>>0){c[56334]=ab}c[56442]=ab;c[56443]=aa;c[56445]=0;c[56339]=c[8408];c[56338]=-1;S=0;do{Y=S<<1;ac=225360+(Y<<2)|0;c[225360+(Y+3<<2)>>2]=ac;c[225360+(Y+2<<2)>>2]=ac;S=S+1|0;}while(S>>>0<32);S=ab+8|0;if((S&7|0)==0){ae=0}else{ae=-S&7}S=(aa-40|0)-ae|0;c[56336]=ab+ae;c[56333]=S;c[ab+(ae+4|0)>>2]=S|1;c[ab+(aa-36|0)>>2]=40;c[56337]=c[8412]}else{S=225768;while(1){af=c[S>>2]|0;ag=S+4|0;ah=c[ag>>2]|0;if((ab|0)==(af+ah|0)){T=924;break}ac=c[S+8>>2]|0;if((ac|0)==0){break}else{S=ac}}do{if((T|0)==924){if((c[S+12>>2]&8|0)!=0){break}ac=ad;if(!(ac>>>0>=af>>>0&ac>>>0<ab>>>0)){break}c[ag>>2]=ah+aa;ac=c[56336]|0;Y=(c[56333]|0)+aa|0;Z=ac;W=ac+8|0;if((W&7|0)==0){ai=0}else{ai=-W&7}W=Y-ai|0;c[56336]=Z+ai;c[56333]=W;c[Z+(ai+4|0)>>2]=W|1;c[Z+(Y+4|0)>>2]=40;c[56337]=c[8412];break L1258}}while(0);if(ab>>>0<(c[56334]|0)>>>0){c[56334]=ab}S=ab+aa|0;Y=225768;while(1){aj=Y|0;if((c[aj>>2]|0)==(S|0)){T=934;break}Z=c[Y+8>>2]|0;if((Z|0)==0){break}else{Y=Z}}do{if((T|0)==934){if((c[Y+12>>2]&8|0)!=0){break}c[aj>>2]=ab;S=Y+4|0;c[S>>2]=(c[S>>2]|0)+aa;S=ab+8|0;if((S&7|0)==0){ak=0}else{ak=-S&7}S=ab+(aa+8|0)|0;if((S&7|0)==0){al=0}else{al=-S&7}S=ab+(al+aa|0)|0;Z=S;W=ak+o|0;ac=ab+W|0;_=ac;K=(S-(ab+ak|0)|0)-o|0;c[ab+(ak+4|0)>>2]=o|3;do{if((Z|0)==(c[56336]|0)){J=(c[56333]|0)+K|0;c[56333]=J;c[56336]=_;c[ab+(W+4|0)>>2]=J|1}else{if((Z|0)==(c[56335]|0)){J=(c[56332]|0)+K|0;c[56332]=J;c[56335]=_;c[ab+(W+4|0)>>2]=J|1;c[ab+(J+W|0)>>2]=J;break}J=aa+4|0;X=c[ab+(J+al|0)>>2]|0;if((X&3|0)==1){$=X&-8;V=X>>>3;L1293:do{if(X>>>0<256){U=c[ab+((al|8)+aa|0)>>2]|0;Q=c[ab+((aa+12|0)+al|0)>>2]|0;R=225360+(V<<1<<2)|0;do{if((U|0)!=(R|0)){if(U>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}if((c[U+12>>2]|0)==(Z|0)){break}ce();return 0;return 0}}while(0);if((Q|0)==(U|0)){c[56330]=c[56330]&(1<<V^-1);break}do{if((Q|0)==(R|0)){am=Q+8|0}else{if(Q>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}m=Q+8|0;if((c[m>>2]|0)==(Z|0)){am=m;break}ce();return 0;return 0}}while(0);c[U+12>>2]=Q;c[am>>2]=U}else{R=S;m=c[ab+((al|24)+aa|0)>>2]|0;P=c[ab+((aa+12|0)+al|0)>>2]|0;do{if((P|0)==(R|0)){O=al|16;g=ab+(J+O|0)|0;L=c[g>>2]|0;if((L|0)==0){e=ab+(O+aa|0)|0;O=c[e>>2]|0;if((O|0)==0){an=0;break}else{ao=O;ap=e}}else{ao=L;ap=g}while(1){g=ao+20|0;L=c[g>>2]|0;if((L|0)!=0){ao=L;ap=g;continue}g=ao+16|0;L=c[g>>2]|0;if((L|0)==0){break}else{ao=L;ap=g}}if(ap>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[ap>>2]=0;an=ao;break}}else{g=c[ab+((al|8)+aa|0)>>2]|0;if(g>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}L=g+12|0;if((c[L>>2]|0)!=(R|0)){ce();return 0;return 0}e=P+8|0;if((c[e>>2]|0)==(R|0)){c[L>>2]=P;c[e>>2]=g;an=P;break}else{ce();return 0;return 0}}}while(0);if((m|0)==0){break}P=ab+((aa+28|0)+al|0)|0;U=225624+(c[P>>2]<<2)|0;do{if((R|0)==(c[U>>2]|0)){c[U>>2]=an;if((an|0)!=0){break}c[56331]=c[56331]&(1<<c[P>>2]^-1);break L1293}else{if(m>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}Q=m+16|0;if((c[Q>>2]|0)==(R|0)){c[Q>>2]=an}else{c[m+20>>2]=an}if((an|0)==0){break L1293}}}while(0);if(an>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}c[an+24>>2]=m;R=al|16;P=c[ab+(R+aa|0)>>2]|0;do{if((P|0)!=0){if(P>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[an+16>>2]=P;c[P+24>>2]=an;break}}}while(0);P=c[ab+(J+R|0)>>2]|0;if((P|0)==0){break}if(P>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[an+20>>2]=P;c[P+24>>2]=an;break}}}while(0);aq=ab+(($|al)+aa|0)|0;ar=$+K|0}else{aq=Z;ar=K}J=aq+4|0;c[J>>2]=c[J>>2]&-2;c[ab+(W+4|0)>>2]=ar|1;c[ab+(ar+W|0)>>2]=ar;J=ar>>>3;if(ar>>>0<256){V=J<<1;X=225360+(V<<2)|0;P=c[56330]|0;m=1<<J;do{if((P&m|0)==0){c[56330]=P|m;as=X;at=225360+(V+2<<2)|0}else{J=225360+(V+2<<2)|0;U=c[J>>2]|0;if(U>>>0>=(c[56334]|0)>>>0){as=U;at=J;break}ce();return 0;return 0}}while(0);c[at>>2]=_;c[as+12>>2]=_;c[ab+(W+8|0)>>2]=as;c[ab+(W+12|0)>>2]=X;break}V=ac;m=ar>>>8;do{if((m|0)==0){au=0}else{if(ar>>>0>16777215){au=31;break}P=(m+1048320|0)>>>16&8;$=m<<P;J=($+520192|0)>>>16&4;U=$<<J;$=(U+245760|0)>>>16&2;Q=(14-(J|P|$)|0)+(U<<$>>>15)|0;au=ar>>>((Q+7|0)>>>0)&1|Q<<1}}while(0);m=225624+(au<<2)|0;c[ab+(W+28|0)>>2]=au;c[ab+(W+20|0)>>2]=0;c[ab+(W+16|0)>>2]=0;X=c[56331]|0;Q=1<<au;if((X&Q|0)==0){c[56331]=X|Q;c[m>>2]=V;c[ab+(W+24|0)>>2]=m;c[ab+(W+12|0)>>2]=V;c[ab+(W+8|0)>>2]=V;break}if((au|0)==31){av=0}else{av=25-(au>>>1)|0}Q=ar<<av;X=c[m>>2]|0;while(1){if((c[X+4>>2]&-8|0)==(ar|0)){break}aw=X+16+(Q>>>31<<2)|0;m=c[aw>>2]|0;if((m|0)==0){T=1007;break}else{Q=Q<<1;X=m}}if((T|0)==1007){if(aw>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[aw>>2]=V;c[ab+(W+24|0)>>2]=X;c[ab+(W+12|0)>>2]=V;c[ab+(W+8|0)>>2]=V;break}}Q=X+8|0;m=c[Q>>2]|0;$=c[56334]|0;if(X>>>0<$>>>0){ce();return 0;return 0}if(m>>>0<$>>>0){ce();return 0;return 0}else{c[m+12>>2]=V;c[Q>>2]=V;c[ab+(W+8|0)>>2]=m;c[ab+(W+12|0)>>2]=X;c[ab+(W+24|0)>>2]=0;break}}}while(0);n=ab+(ak|8)|0;return n|0}}while(0);Y=ad;W=225768;while(1){ax=c[W>>2]|0;if(ax>>>0<=Y>>>0){ay=c[W+4>>2]|0;az=ax+ay|0;if(az>>>0>Y>>>0){break}}W=c[W+8>>2]|0}W=ax+(ay-39|0)|0;if((W&7|0)==0){aA=0}else{aA=-W&7}W=ax+((ay-47|0)+aA|0)|0;ac=W>>>0<(ad+16|0)>>>0?Y:W;W=ac+8|0;_=ab+8|0;if((_&7|0)==0){aB=0}else{aB=-_&7}_=(aa-40|0)-aB|0;c[56336]=ab+aB;c[56333]=_;c[ab+(aB+4|0)>>2]=_|1;c[ab+(aa-36|0)>>2]=40;c[56337]=c[8412];c[ac+4>>2]=27;c[W>>2]=c[56442];c[W+4>>2]=c[225772>>2];c[W+8>>2]=c[225776>>2];c[W+12>>2]=c[225780>>2];c[56442]=ab;c[56443]=aa;c[56445]=0;c[56444]=W;W=ac+28|0;c[W>>2]=7;if((ac+32|0)>>>0<az>>>0){_=W;while(1){W=_+4|0;c[W>>2]=7;if((_+8|0)>>>0<az>>>0){_=W}else{break}}}if((ac|0)==(Y|0)){break}_=ac-ad|0;W=Y+(_+4|0)|0;c[W>>2]=c[W>>2]&-2;c[ad+4>>2]=_|1;c[Y+_>>2]=_;W=_>>>3;if(_>>>0<256){K=W<<1;Z=225360+(K<<2)|0;S=c[56330]|0;m=1<<W;do{if((S&m|0)==0){c[56330]=S|m;aC=Z;aD=225360+(K+2<<2)|0}else{W=225360+(K+2<<2)|0;Q=c[W>>2]|0;if(Q>>>0>=(c[56334]|0)>>>0){aC=Q;aD=W;break}ce();return 0;return 0}}while(0);c[aD>>2]=ad;c[aC+12>>2]=ad;c[ad+8>>2]=aC;c[ad+12>>2]=Z;break}K=ad;m=_>>>8;do{if((m|0)==0){aE=0}else{if(_>>>0>16777215){aE=31;break}S=(m+1048320|0)>>>16&8;Y=m<<S;ac=(Y+520192|0)>>>16&4;W=Y<<ac;Y=(W+245760|0)>>>16&2;Q=(14-(ac|S|Y)|0)+(W<<Y>>>15)|0;aE=_>>>((Q+7|0)>>>0)&1|Q<<1}}while(0);m=225624+(aE<<2)|0;c[ad+28>>2]=aE;c[ad+20>>2]=0;c[ad+16>>2]=0;Z=c[56331]|0;Q=1<<aE;if((Z&Q|0)==0){c[56331]=Z|Q;c[m>>2]=K;c[ad+24>>2]=m;c[ad+12>>2]=ad;c[ad+8>>2]=ad;break}if((aE|0)==31){aF=0}else{aF=25-(aE>>>1)|0}Q=_<<aF;Z=c[m>>2]|0;while(1){if((c[Z+4>>2]&-8|0)==(_|0)){break}aG=Z+16+(Q>>>31<<2)|0;m=c[aG>>2]|0;if((m|0)==0){T=1042;break}else{Q=Q<<1;Z=m}}if((T|0)==1042){if(aG>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[aG>>2]=K;c[ad+24>>2]=Z;c[ad+12>>2]=ad;c[ad+8>>2]=ad;break}}Q=Z+8|0;_=c[Q>>2]|0;m=c[56334]|0;if(Z>>>0<m>>>0){ce();return 0;return 0}if(_>>>0<m>>>0){ce();return 0;return 0}else{c[_+12>>2]=K;c[Q>>2]=K;c[ad+8>>2]=_;c[ad+12>>2]=Z;c[ad+24>>2]=0;break}}}while(0);ad=c[56333]|0;if(ad>>>0<=o>>>0){break}_=ad-o|0;c[56333]=_;ad=c[56336]|0;Q=ad;c[56336]=Q+o;c[Q+(o+4|0)>>2]=_|1;c[ad+4>>2]=o|3;n=ad+8|0;return n|0}}while(0);c[b0()>>2]=12;n=0;return n|0}function uu(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0;if((a|0)==0){return}b=a-8|0;d=b;e=c[56334]|0;if(b>>>0<e>>>0){ce()}f=c[a-4>>2]|0;g=f&3;if((g|0)==1){ce()}h=f&-8;i=a+(h-8|0)|0;j=i;L1475:do{if((f&1|0)==0){k=c[b>>2]|0;if((g|0)==0){return}l=-8-k|0;m=a+l|0;n=m;o=k+h|0;if(m>>>0<e>>>0){ce()}if((n|0)==(c[56335]|0)){p=a+(h-4|0)|0;if((c[p>>2]&3|0)!=3){q=n;r=o;break}c[56332]=o;c[p>>2]=c[p>>2]&-2;c[a+(l+4|0)>>2]=o|1;c[i>>2]=o;return}p=k>>>3;if(k>>>0<256){k=c[a+(l+8|0)>>2]|0;s=c[a+(l+12|0)>>2]|0;t=225360+(p<<1<<2)|0;do{if((k|0)!=(t|0)){if(k>>>0<e>>>0){ce()}if((c[k+12>>2]|0)==(n|0)){break}ce()}}while(0);if((s|0)==(k|0)){c[56330]=c[56330]&(1<<p^-1);q=n;r=o;break}do{if((s|0)==(t|0)){u=s+8|0}else{if(s>>>0<e>>>0){ce()}v=s+8|0;if((c[v>>2]|0)==(n|0)){u=v;break}ce()}}while(0);c[k+12>>2]=s;c[u>>2]=k;q=n;r=o;break}t=m;p=c[a+(l+24|0)>>2]|0;v=c[a+(l+12|0)>>2]|0;do{if((v|0)==(t|0)){w=a+(l+20|0)|0;x=c[w>>2]|0;if((x|0)==0){y=a+(l+16|0)|0;z=c[y>>2]|0;if((z|0)==0){A=0;break}else{B=z;C=y}}else{B=x;C=w}while(1){w=B+20|0;x=c[w>>2]|0;if((x|0)!=0){B=x;C=w;continue}w=B+16|0;x=c[w>>2]|0;if((x|0)==0){break}else{B=x;C=w}}if(C>>>0<e>>>0){ce()}else{c[C>>2]=0;A=B;break}}else{w=c[a+(l+8|0)>>2]|0;if(w>>>0<e>>>0){ce()}x=w+12|0;if((c[x>>2]|0)!=(t|0)){ce()}y=v+8|0;if((c[y>>2]|0)==(t|0)){c[x>>2]=v;c[y>>2]=w;A=v;break}else{ce()}}}while(0);if((p|0)==0){q=n;r=o;break}v=a+(l+28|0)|0;m=225624+(c[v>>2]<<2)|0;do{if((t|0)==(c[m>>2]|0)){c[m>>2]=A;if((A|0)!=0){break}c[56331]=c[56331]&(1<<c[v>>2]^-1);q=n;r=o;break L1475}else{if(p>>>0<(c[56334]|0)>>>0){ce()}k=p+16|0;if((c[k>>2]|0)==(t|0)){c[k>>2]=A}else{c[p+20>>2]=A}if((A|0)==0){q=n;r=o;break L1475}}}while(0);if(A>>>0<(c[56334]|0)>>>0){ce()}c[A+24>>2]=p;t=c[a+(l+16|0)>>2]|0;do{if((t|0)!=0){if(t>>>0<(c[56334]|0)>>>0){ce()}else{c[A+16>>2]=t;c[t+24>>2]=A;break}}}while(0);t=c[a+(l+20|0)>>2]|0;if((t|0)==0){q=n;r=o;break}if(t>>>0<(c[56334]|0)>>>0){ce()}else{c[A+20>>2]=t;c[t+24>>2]=A;q=n;r=o;break}}else{q=d;r=h}}while(0);d=q;if(d>>>0>=i>>>0){ce()}A=a+(h-4|0)|0;e=c[A>>2]|0;if((e&1|0)==0){ce()}do{if((e&2|0)==0){if((j|0)==(c[56336]|0)){B=(c[56333]|0)+r|0;c[56333]=B;c[56336]=q;c[q+4>>2]=B|1;if((q|0)==(c[56335]|0)){c[56335]=0;c[56332]=0}if(B>>>0<=(c[56337]|0)>>>0){return}uw(0);return}if((j|0)==(c[56335]|0)){B=(c[56332]|0)+r|0;c[56332]=B;c[56335]=q;c[q+4>>2]=B|1;c[d+B>>2]=B;return}B=(e&-8)+r|0;C=e>>>3;L1581:do{if(e>>>0<256){u=c[a+h>>2]|0;g=c[a+(h|4)>>2]|0;b=225360+(C<<1<<2)|0;do{if((u|0)!=(b|0)){if(u>>>0<(c[56334]|0)>>>0){ce()}if((c[u+12>>2]|0)==(j|0)){break}ce()}}while(0);if((g|0)==(u|0)){c[56330]=c[56330]&(1<<C^-1);break}do{if((g|0)==(b|0)){D=g+8|0}else{if(g>>>0<(c[56334]|0)>>>0){ce()}f=g+8|0;if((c[f>>2]|0)==(j|0)){D=f;break}ce()}}while(0);c[u+12>>2]=g;c[D>>2]=u}else{b=i;f=c[a+(h+16|0)>>2]|0;t=c[a+(h|4)>>2]|0;do{if((t|0)==(b|0)){p=a+(h+12|0)|0;v=c[p>>2]|0;if((v|0)==0){m=a+(h+8|0)|0;k=c[m>>2]|0;if((k|0)==0){E=0;break}else{F=k;G=m}}else{F=v;G=p}while(1){p=F+20|0;v=c[p>>2]|0;if((v|0)!=0){F=v;G=p;continue}p=F+16|0;v=c[p>>2]|0;if((v|0)==0){break}else{F=v;G=p}}if(G>>>0<(c[56334]|0)>>>0){ce()}else{c[G>>2]=0;E=F;break}}else{p=c[a+h>>2]|0;if(p>>>0<(c[56334]|0)>>>0){ce()}v=p+12|0;if((c[v>>2]|0)!=(b|0)){ce()}m=t+8|0;if((c[m>>2]|0)==(b|0)){c[v>>2]=t;c[m>>2]=p;E=t;break}else{ce()}}}while(0);if((f|0)==0){break}t=a+(h+20|0)|0;u=225624+(c[t>>2]<<2)|0;do{if((b|0)==(c[u>>2]|0)){c[u>>2]=E;if((E|0)!=0){break}c[56331]=c[56331]&(1<<c[t>>2]^-1);break L1581}else{if(f>>>0<(c[56334]|0)>>>0){ce()}g=f+16|0;if((c[g>>2]|0)==(b|0)){c[g>>2]=E}else{c[f+20>>2]=E}if((E|0)==0){break L1581}}}while(0);if(E>>>0<(c[56334]|0)>>>0){ce()}c[E+24>>2]=f;b=c[a+(h+8|0)>>2]|0;do{if((b|0)!=0){if(b>>>0<(c[56334]|0)>>>0){ce()}else{c[E+16>>2]=b;c[b+24>>2]=E;break}}}while(0);b=c[a+(h+12|0)>>2]|0;if((b|0)==0){break}if(b>>>0<(c[56334]|0)>>>0){ce()}else{c[E+20>>2]=b;c[b+24>>2]=E;break}}}while(0);c[q+4>>2]=B|1;c[d+B>>2]=B;if((q|0)!=(c[56335]|0)){H=B;break}c[56332]=B;return}else{c[A>>2]=e&-2;c[q+4>>2]=r|1;c[d+r>>2]=r;H=r}}while(0);r=H>>>3;if(H>>>0<256){d=r<<1;e=225360+(d<<2)|0;A=c[56330]|0;E=1<<r;do{if((A&E|0)==0){c[56330]=A|E;I=e;J=225360+(d+2<<2)|0}else{r=225360+(d+2<<2)|0;h=c[r>>2]|0;if(h>>>0>=(c[56334]|0)>>>0){I=h;J=r;break}ce()}}while(0);c[J>>2]=q;c[I+12>>2]=q;c[q+8>>2]=I;c[q+12>>2]=e;return}e=q;I=H>>>8;do{if((I|0)==0){K=0}else{if(H>>>0>16777215){K=31;break}J=(I+1048320|0)>>>16&8;d=I<<J;E=(d+520192|0)>>>16&4;A=d<<E;d=(A+245760|0)>>>16&2;r=(14-(E|J|d)|0)+(A<<d>>>15)|0;K=H>>>((r+7|0)>>>0)&1|r<<1}}while(0);I=225624+(K<<2)|0;c[q+28>>2]=K;c[q+20>>2]=0;c[q+16>>2]=0;r=c[56331]|0;d=1<<K;do{if((r&d|0)==0){c[56331]=r|d;c[I>>2]=e;c[q+24>>2]=I;c[q+12>>2]=q;c[q+8>>2]=q}else{if((K|0)==31){L=0}else{L=25-(K>>>1)|0}A=H<<L;J=c[I>>2]|0;while(1){if((c[J+4>>2]&-8|0)==(H|0)){break}M=J+16+(A>>>31<<2)|0;E=c[M>>2]|0;if((E|0)==0){N=1221;break}else{A=A<<1;J=E}}if((N|0)==1221){if(M>>>0<(c[56334]|0)>>>0){ce()}else{c[M>>2]=e;c[q+24>>2]=J;c[q+12>>2]=q;c[q+8>>2]=q;break}}A=J+8|0;B=c[A>>2]|0;E=c[56334]|0;if(J>>>0<E>>>0){ce()}if(B>>>0<E>>>0){ce()}else{c[B+12>>2]=e;c[A>>2]=e;c[q+8>>2]=B;c[q+12>>2]=J;c[q+24>>2]=0;break}}}while(0);q=(c[56338]|0)-1|0;c[56338]=q;if((q|0)==0){O=225776}else{return}while(1){q=c[O>>2]|0;if((q|0)==0){break}else{O=q+8|0}}c[56338]=-1;return}function uv(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;if((a|0)==0){d=ut(b)|0;return d|0}if(b>>>0>4294967231){c[b0()>>2]=12;d=0;return d|0}if(b>>>0<11){e=16}else{e=b+11&-8}f=ux(a-8|0,e)|0;if((f|0)!=0){d=f+8|0;return d|0}f=ut(b)|0;if((f|0)==0){d=0;return d|0}e=c[a-4>>2]|0;g=(e&-8)-((e&3|0)==0?8:4)|0;uD(f|0,a|0,g>>>0<b>>>0?g:b);uu(a);d=f;return d|0}function uw(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0;do{if((c[8408]|0)==0){b=cb(8)|0;if((b-1&b|0)==0){c[8410]=b;c[8409]=b;c[8411]=-1;c[8412]=2097152;c[8413]=0;c[56441]=0;c[8408]=cG(0)&-16^1431655768;break}else{ce();return 0;return 0}}}while(0);if(a>>>0>=4294967232){d=0;return d|0}b=c[56336]|0;if((b|0)==0){d=0;return d|0}e=c[56333]|0;do{if(e>>>0>(a+40|0)>>>0){f=c[8410]|0;g=aa(((((((-40-a|0)-1|0)+e|0)+f|0)>>>0)/(f>>>0)>>>0)-1|0,f);h=b;i=225768;while(1){j=c[i>>2]|0;if(j>>>0<=h>>>0){if((j+(c[i+4>>2]|0)|0)>>>0>h>>>0){k=i;break}}j=c[i+8>>2]|0;if((j|0)==0){k=0;break}else{i=j}}if((c[k+12>>2]&8|0)!=0){break}i=b_(0)|0;h=k+4|0;if((i|0)!=((c[k>>2]|0)+(c[h>>2]|0)|0)){break}j=b_(-(g>>>0>2147483646?-2147483648-f|0:g)|0)|0;l=b_(0)|0;if(!((j|0)!=-1&l>>>0<i>>>0)){break}j=i-l|0;if((i|0)==(l|0)){break}c[h>>2]=(c[h>>2]|0)-j;c[56438]=(c[56438]|0)-j;h=c[56336]|0;m=(c[56333]|0)-j|0;j=h;n=h+8|0;if((n&7|0)==0){o=0}else{o=-n&7}n=m-o|0;c[56336]=j+o;c[56333]=n;c[j+(o+4|0)>>2]=n|1;c[j+(m+4|0)>>2]=40;c[56337]=c[8412];d=(i|0)!=(l|0)&1;return d|0}}while(0);if((c[56333]|0)>>>0<=(c[56337]|0)>>>0){d=0;return d|0}c[56337]=-1;d=0;return d|0}function ux(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0;d=a+4|0;e=c[d>>2]|0;f=e&-8;g=a;h=g+f|0;i=h;j=c[56334]|0;if(g>>>0<j>>>0){ce();return 0;return 0}k=e&3;if(!((k|0)!=1&g>>>0<h>>>0)){ce();return 0;return 0}l=g+(f|4)|0;m=c[l>>2]|0;if((m&1|0)==0){ce();return 0;return 0}if((k|0)==0){if(b>>>0<256){n=0;return n|0}do{if(f>>>0>=(b+4|0)>>>0){if((f-b|0)>>>0>c[8410]<<1>>>0){break}else{n=a}return n|0}}while(0);n=0;return n|0}if(f>>>0>=b>>>0){k=f-b|0;if(k>>>0<=15){n=a;return n|0}c[d>>2]=e&1|b|2;c[g+(b+4|0)>>2]=k|3;c[l>>2]=c[l>>2]|1;uy(g+b|0,k);n=a;return n|0}if((i|0)==(c[56336]|0)){k=(c[56333]|0)+f|0;if(k>>>0<=b>>>0){n=0;return n|0}l=k-b|0;c[d>>2]=e&1|b|2;c[g+(b+4|0)>>2]=l|1;c[56336]=g+b;c[56333]=l;n=a;return n|0}if((i|0)==(c[56335]|0)){l=(c[56332]|0)+f|0;if(l>>>0<b>>>0){n=0;return n|0}k=l-b|0;if(k>>>0>15){c[d>>2]=e&1|b|2;c[g+(b+4|0)>>2]=k|1;c[g+l>>2]=k;o=g+(l+4|0)|0;c[o>>2]=c[o>>2]&-2;p=g+b|0;q=k}else{c[d>>2]=e&1|l|2;e=g+(l+4|0)|0;c[e>>2]=c[e>>2]|1;p=0;q=0}c[56332]=q;c[56335]=p;n=a;return n|0}if((m&2|0)!=0){n=0;return n|0}p=(m&-8)+f|0;if(p>>>0<b>>>0){n=0;return n|0}q=p-b|0;e=m>>>3;L1801:do{if(m>>>0<256){l=c[g+(f+8|0)>>2]|0;k=c[g+(f+12|0)>>2]|0;o=225360+(e<<1<<2)|0;do{if((l|0)!=(o|0)){if(l>>>0<j>>>0){ce();return 0;return 0}if((c[l+12>>2]|0)==(i|0)){break}ce();return 0;return 0}}while(0);if((k|0)==(l|0)){c[56330]=c[56330]&(1<<e^-1);break}do{if((k|0)==(o|0)){r=k+8|0}else{if(k>>>0<j>>>0){ce();return 0;return 0}s=k+8|0;if((c[s>>2]|0)==(i|0)){r=s;break}ce();return 0;return 0}}while(0);c[l+12>>2]=k;c[r>>2]=l}else{o=h;s=c[g+(f+24|0)>>2]|0;t=c[g+(f+12|0)>>2]|0;do{if((t|0)==(o|0)){u=g+(f+20|0)|0;v=c[u>>2]|0;if((v|0)==0){w=g+(f+16|0)|0;x=c[w>>2]|0;if((x|0)==0){y=0;break}else{z=x;A=w}}else{z=v;A=u}while(1){u=z+20|0;v=c[u>>2]|0;if((v|0)!=0){z=v;A=u;continue}u=z+16|0;v=c[u>>2]|0;if((v|0)==0){break}else{z=v;A=u}}if(A>>>0<j>>>0){ce();return 0;return 0}else{c[A>>2]=0;y=z;break}}else{u=c[g+(f+8|0)>>2]|0;if(u>>>0<j>>>0){ce();return 0;return 0}v=u+12|0;if((c[v>>2]|0)!=(o|0)){ce();return 0;return 0}w=t+8|0;if((c[w>>2]|0)==(o|0)){c[v>>2]=t;c[w>>2]=u;y=t;break}else{ce();return 0;return 0}}}while(0);if((s|0)==0){break}t=g+(f+28|0)|0;l=225624+(c[t>>2]<<2)|0;do{if((o|0)==(c[l>>2]|0)){c[l>>2]=y;if((y|0)!=0){break}c[56331]=c[56331]&(1<<c[t>>2]^-1);break L1801}else{if(s>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}k=s+16|0;if((c[k>>2]|0)==(o|0)){c[k>>2]=y}else{c[s+20>>2]=y}if((y|0)==0){break L1801}}}while(0);if(y>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}c[y+24>>2]=s;o=c[g+(f+16|0)>>2]|0;do{if((o|0)!=0){if(o>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[y+16>>2]=o;c[o+24>>2]=y;break}}}while(0);o=c[g+(f+20|0)>>2]|0;if((o|0)==0){break}if(o>>>0<(c[56334]|0)>>>0){ce();return 0;return 0}else{c[y+20>>2]=o;c[o+24>>2]=y;break}}}while(0);if(q>>>0<16){c[d>>2]=p|c[d>>2]&1|2;y=g+(p|4)|0;c[y>>2]=c[y>>2]|1;n=a;return n|0}else{c[d>>2]=c[d>>2]&1|b|2;c[g+(b+4|0)>>2]=q|3;d=g+(p|4)|0;c[d>>2]=c[d>>2]|1;uy(g+b|0,q);n=a;return n|0}return 0}function uy(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0;d=a;e=d+b|0;f=e;g=c[a+4>>2]|0;L1877:do{if((g&1|0)==0){h=c[a>>2]|0;if((g&3|0)==0){return}i=d+(-h|0)|0;j=i;k=h+b|0;l=c[56334]|0;if(i>>>0<l>>>0){ce()}if((j|0)==(c[56335]|0)){m=d+(b+4|0)|0;if((c[m>>2]&3|0)!=3){n=j;o=k;break}c[56332]=k;c[m>>2]=c[m>>2]&-2;c[d+(4-h|0)>>2]=k|1;c[e>>2]=k;return}m=h>>>3;if(h>>>0<256){p=c[d+(8-h|0)>>2]|0;q=c[d+(12-h|0)>>2]|0;r=225360+(m<<1<<2)|0;do{if((p|0)!=(r|0)){if(p>>>0<l>>>0){ce()}if((c[p+12>>2]|0)==(j|0)){break}ce()}}while(0);if((q|0)==(p|0)){c[56330]=c[56330]&(1<<m^-1);n=j;o=k;break}do{if((q|0)==(r|0)){s=q+8|0}else{if(q>>>0<l>>>0){ce()}t=q+8|0;if((c[t>>2]|0)==(j|0)){s=t;break}ce()}}while(0);c[p+12>>2]=q;c[s>>2]=p;n=j;o=k;break}r=i;m=c[d+(24-h|0)>>2]|0;t=c[d+(12-h|0)>>2]|0;do{if((t|0)==(r|0)){u=16-h|0;v=d+(u+4|0)|0;w=c[v>>2]|0;if((w|0)==0){x=d+u|0;u=c[x>>2]|0;if((u|0)==0){y=0;break}else{z=u;A=x}}else{z=w;A=v}while(1){v=z+20|0;w=c[v>>2]|0;if((w|0)!=0){z=w;A=v;continue}v=z+16|0;w=c[v>>2]|0;if((w|0)==0){break}else{z=w;A=v}}if(A>>>0<l>>>0){ce()}else{c[A>>2]=0;y=z;break}}else{v=c[d+(8-h|0)>>2]|0;if(v>>>0<l>>>0){ce()}w=v+12|0;if((c[w>>2]|0)!=(r|0)){ce()}x=t+8|0;if((c[x>>2]|0)==(r|0)){c[w>>2]=t;c[x>>2]=v;y=t;break}else{ce()}}}while(0);if((m|0)==0){n=j;o=k;break}t=d+(28-h|0)|0;l=225624+(c[t>>2]<<2)|0;do{if((r|0)==(c[l>>2]|0)){c[l>>2]=y;if((y|0)!=0){break}c[56331]=c[56331]&(1<<c[t>>2]^-1);n=j;o=k;break L1877}else{if(m>>>0<(c[56334]|0)>>>0){ce()}i=m+16|0;if((c[i>>2]|0)==(r|0)){c[i>>2]=y}else{c[m+20>>2]=y}if((y|0)==0){n=j;o=k;break L1877}}}while(0);if(y>>>0<(c[56334]|0)>>>0){ce()}c[y+24>>2]=m;r=16-h|0;t=c[d+r>>2]|0;do{if((t|0)!=0){if(t>>>0<(c[56334]|0)>>>0){ce()}else{c[y+16>>2]=t;c[t+24>>2]=y;break}}}while(0);t=c[d+(r+4|0)>>2]|0;if((t|0)==0){n=j;o=k;break}if(t>>>0<(c[56334]|0)>>>0){ce()}else{c[y+20>>2]=t;c[t+24>>2]=y;n=j;o=k;break}}else{n=a;o=b}}while(0);a=c[56334]|0;if(e>>>0<a>>>0){ce()}y=d+(b+4|0)|0;z=c[y>>2]|0;do{if((z&2|0)==0){if((f|0)==(c[56336]|0)){A=(c[56333]|0)+o|0;c[56333]=A;c[56336]=n;c[n+4>>2]=A|1;if((n|0)!=(c[56335]|0)){return}c[56335]=0;c[56332]=0;return}if((f|0)==(c[56335]|0)){A=(c[56332]|0)+o|0;c[56332]=A;c[56335]=n;c[n+4>>2]=A|1;c[n+A>>2]=A;return}A=(z&-8)+o|0;s=z>>>3;L1976:do{if(z>>>0<256){g=c[d+(b+8|0)>>2]|0;t=c[d+(b+12|0)>>2]|0;h=225360+(s<<1<<2)|0;do{if((g|0)!=(h|0)){if(g>>>0<a>>>0){ce()}if((c[g+12>>2]|0)==(f|0)){break}ce()}}while(0);if((t|0)==(g|0)){c[56330]=c[56330]&(1<<s^-1);break}do{if((t|0)==(h|0)){B=t+8|0}else{if(t>>>0<a>>>0){ce()}m=t+8|0;if((c[m>>2]|0)==(f|0)){B=m;break}ce()}}while(0);c[g+12>>2]=t;c[B>>2]=g}else{h=e;m=c[d+(b+24|0)>>2]|0;l=c[d+(b+12|0)>>2]|0;do{if((l|0)==(h|0)){i=d+(b+20|0)|0;p=c[i>>2]|0;if((p|0)==0){q=d+(b+16|0)|0;v=c[q>>2]|0;if((v|0)==0){C=0;break}else{D=v;E=q}}else{D=p;E=i}while(1){i=D+20|0;p=c[i>>2]|0;if((p|0)!=0){D=p;E=i;continue}i=D+16|0;p=c[i>>2]|0;if((p|0)==0){break}else{D=p;E=i}}if(E>>>0<a>>>0){ce()}else{c[E>>2]=0;C=D;break}}else{i=c[d+(b+8|0)>>2]|0;if(i>>>0<a>>>0){ce()}p=i+12|0;if((c[p>>2]|0)!=(h|0)){ce()}q=l+8|0;if((c[q>>2]|0)==(h|0)){c[p>>2]=l;c[q>>2]=i;C=l;break}else{ce()}}}while(0);if((m|0)==0){break}l=d+(b+28|0)|0;g=225624+(c[l>>2]<<2)|0;do{if((h|0)==(c[g>>2]|0)){c[g>>2]=C;if((C|0)!=0){break}c[56331]=c[56331]&(1<<c[l>>2]^-1);break L1976}else{if(m>>>0<(c[56334]|0)>>>0){ce()}t=m+16|0;if((c[t>>2]|0)==(h|0)){c[t>>2]=C}else{c[m+20>>2]=C}if((C|0)==0){break L1976}}}while(0);if(C>>>0<(c[56334]|0)>>>0){ce()}c[C+24>>2]=m;h=c[d+(b+16|0)>>2]|0;do{if((h|0)!=0){if(h>>>0<(c[56334]|0)>>>0){ce()}else{c[C+16>>2]=h;c[h+24>>2]=C;break}}}while(0);h=c[d+(b+20|0)>>2]|0;if((h|0)==0){break}if(h>>>0<(c[56334]|0)>>>0){ce()}else{c[C+20>>2]=h;c[h+24>>2]=C;break}}}while(0);c[n+4>>2]=A|1;c[n+A>>2]=A;if((n|0)!=(c[56335]|0)){F=A;break}c[56332]=A;return}else{c[y>>2]=z&-2;c[n+4>>2]=o|1;c[n+o>>2]=o;F=o}}while(0);o=F>>>3;if(F>>>0<256){z=o<<1;y=225360+(z<<2)|0;C=c[56330]|0;b=1<<o;do{if((C&b|0)==0){c[56330]=C|b;G=y;H=225360+(z+2<<2)|0}else{o=225360+(z+2<<2)|0;d=c[o>>2]|0;if(d>>>0>=(c[56334]|0)>>>0){G=d;H=o;break}ce()}}while(0);c[H>>2]=n;c[G+12>>2]=n;c[n+8>>2]=G;c[n+12>>2]=y;return}y=n;G=F>>>8;do{if((G|0)==0){I=0}else{if(F>>>0>16777215){I=31;break}H=(G+1048320|0)>>>16&8;z=G<<H;b=(z+520192|0)>>>16&4;C=z<<b;z=(C+245760|0)>>>16&2;o=(14-(b|H|z)|0)+(C<<z>>>15)|0;I=F>>>((o+7|0)>>>0)&1|o<<1}}while(0);G=225624+(I<<2)|0;c[n+28>>2]=I;c[n+20>>2]=0;c[n+16>>2]=0;o=c[56331]|0;z=1<<I;if((o&z|0)==0){c[56331]=o|z;c[G>>2]=y;c[n+24>>2]=G;c[n+12>>2]=n;c[n+8>>2]=n;return}if((I|0)==31){J=0}else{J=25-(I>>>1)|0}I=F<<J;J=c[G>>2]|0;while(1){if((c[J+4>>2]&-8|0)==(F|0)){break}K=J+16+(I>>>31<<2)|0;G=c[K>>2]|0;if((G|0)==0){L=1527;break}else{I=I<<1;J=G}}if((L|0)==1527){if(K>>>0<(c[56334]|0)>>>0){ce()}c[K>>2]=y;c[n+24>>2]=J;c[n+12>>2]=n;c[n+8>>2]=n;return}K=J+8|0;L=c[K>>2]|0;I=c[56334]|0;if(J>>>0<I>>>0){ce()}if(L>>>0<I>>>0){ce()}c[L+12>>2]=y;c[K>>2]=y;c[n+8>>2]=L;c[n+12>>2]=J;c[n+24>>2]=0;return}function uz(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0,s=0,t=0,u=0,v=0.0,w=0,x=0,y=0,z=0.0,A=0.0,B=0,C=0,D=0,E=0.0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0.0,O=0,P=0,Q=0.0,R=0.0,S=0.0;e=b;while(1){f=e+1|0;if((aM(a[e]|0|0)|0)==0){break}else{e=f}}g=a[e]|0;if((g<<24>>24|0)==45){i=f;j=1}else if((g<<24>>24|0)==43){i=f;j=0}else{i=e;j=0}e=-1;f=0;g=i;while(1){k=a[g]|0;if(((k<<24>>24)-48|0)>>>0<10){l=e}else{if(k<<24>>24!=46|(e|0)>-1){break}else{l=f}}e=l;f=f+1|0;g=g+1|0}l=g+(-f|0)|0;i=(e|0)<0;m=((i^1)<<31>>31)+f|0;n=(m|0)>18;o=(n?-18:-m|0)+(i?f:e)|0;e=n?18:m;do{if((e|0)==0){p=b;q=0.0}else{if((e|0)>9){m=l;n=e;f=0;while(1){i=a[m]|0;r=m+1|0;if(i<<24>>24==46){s=a[r]|0;t=m+2|0}else{s=i;t=r}u=((f*10&-1)-48|0)+(s<<24>>24)|0;r=n-1|0;if((r|0)>9){m=t;n=r;f=u}else{break}}v=+(u|0)*1.0e9;w=9;x=t;y=1575}else{if((e|0)>0){v=0.0;w=e;x=l;y=1575}else{z=0.0;A=0.0}}if((y|0)==1575){f=x;n=w;m=0;while(1){r=a[f]|0;i=f+1|0;if(r<<24>>24==46){B=a[i]|0;C=f+2|0}else{B=r;C=i}D=((m*10&-1)-48|0)+(B<<24>>24)|0;i=n-1|0;if((i|0)>0){f=C;n=i;m=D}else{break}}z=+(D|0);A=v}E=A+z;do{if((k<<24>>24|0)==69|(k<<24>>24|0)==101){m=g+1|0;n=a[m]|0;if((n<<24>>24|0)==45){F=g+2|0;G=1}else if((n<<24>>24|0)==43){F=g+2|0;G=0}else{F=m;G=0}m=a[F]|0;if(((m<<24>>24)-48|0)>>>0<10){H=F;I=0;J=m}else{K=0;L=F;M=G;break}while(1){m=((I*10&-1)-48|0)+(J<<24>>24)|0;n=H+1|0;f=a[n]|0;if(((f<<24>>24)-48|0)>>>0<10){H=n;I=m;J=f}else{K=m;L=n;M=G;break}}}else{K=0;L=g;M=0}}while(0);n=o+((M|0)==0?K:-K|0)|0;m=(n|0)<0?-n|0:n;if((m|0)>511){c[b0()>>2]=34;N=1.0;O=30088;P=511;y=1592}else{if((m|0)==0){Q=1.0}else{N=1.0;O=30088;P=m;y=1592}}if((y|0)==1592){while(1){y=0;if((P&1|0)==0){R=N}else{R=N*+h[O>>3]}m=P>>1;if((m|0)==0){Q=R;break}else{N=R;O=O+8|0;P=m;y=1592}}}if((n|0)>-1){p=L;q=E*Q;break}else{p=L;q=E/Q;break}}}while(0);if((d|0)!=0){c[d>>2]=p}if((j|0)==0){S=q;return+S}S=-0.0-q;return+S}function uA(b){b=b|0;var c=0;c=b;while(a[c]|0){c=c+1|0}return c-b|0}function uB(b,c){b=b|0;c=c|0;var d=0;do{a[b+d|0]=a[c+d|0];d=d+1|0}while(a[c+(d-1)|0]|0);return b|0}function uC(b,c){b=b|0;c=c|0;var d=0,e=0;d=b+(uA(b)|0)|0;do{a[d+e|0]=a[c+e|0];e=e+1|0}while(a[c+(e-1)|0]|0);return b|0}function uD(b,d,e){b=b|0;d=d|0;e=e|0;var f=0;f=b|0;if((b&3)==(d&3)){while(b&3){if((e|0)==0)return f|0;a[b]=a[d]|0;b=b+1|0;d=d+1|0;e=e-1|0}while((e|0)>=4){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0;e=e-4|0}}while((e|0)>0){a[b]=a[d]|0;b=b+1|0;d=d+1|0;e=e-1|0}return f|0}function uE(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;f=b+e|0;if((e|0)>=20){d=d&255;e=b&3;g=d|d<<8|d<<16|d<<24;h=f&~3;if(e){e=b+4-e|0;while((b|0)<(e|0)){a[b]=d;b=b+1|0}}while((b|0)<(h|0)){c[b>>2]=g;b=b+4|0}}while((b|0)<(f|0)){a[b]=d;b=b+1|0}}function uF(b,c,d){b=b|0;c=c|0;d=d|0;var e=0,f=0;while((e|0)<(d|0)){a[b+e|0]=f?0:a[c+e|0]|0;f=f?1:(a[c+e|0]|0)==0;e=e+1|0}return b|0}function uG(a,b,c){a=a|0;b=b|0;c=c|0;var e=0,f=0,g=0;while((e|0)<(c|0)){f=d[a+e|0]|0;g=d[b+e|0]|0;if((f|0)!=(g|0))return((f|0)>(g|0)?1:-1)|0;e=e+1|0}return 0}function uH(b,c,d){b=b|0;c=c|0;d=d|0;if((c|0)<(b|0)&(b|0)<(c+d|0)){c=c+d|0;b=b+d|0;while((d|0)>0){b=b-1|0;c=c-1|0;d=d-1|0;a[b]=a[c]|0}}else{uD(b,c,d)}}function uI(a){a=a|0;if((a|0)<65)return a|0;if((a|0)>90)return a|0;return a-65+97|0}function uJ(b,c,d){b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0;while(e>>>0<d>>>0){f=uI(a[b+e|0]|0)|0;g=uI(a[c+e|0]|0)|0;if((f|0)==(g|0)&(f|0)==0)return 0;if((f|0)==0)return-1;if((g|0)==0)return 1;if((f|0)==(g|0)){e=e+1|0;continue}else{return(f>>>0>g>>>0?1:-1)|0}}return 0}function uK(a,b){a=a|0;b=b|0;return uJ(a,b,-1)|0}function uL(a,b,d){a=a|0;b=b|0;d=d|0;var e=0;t=t+1|0;c[a>>2]=t;while((e|0)<40){if((c[d+(e<<2)>>2]|0)==0){c[d+(e<<2)>>2]=t;c[d+((e<<2)+4)>>2]=b;c[d+((e<<2)+8)>>2]=0;return 0}e=e+2|0}br(116);br(111);br(111);br(32);br(109);br(97);br(110);br(121);br(32);br(115);br(101);br(116);br(106);br(109);br(112);br(115);br(32);br(105);br(110);br(32);br(97);br(32);br(102);br(117);br(110);br(99);br(116);br(105);br(111);br(110);br(32);br(99);br(97);br(108);br(108);br(44);br(32);br(98);br(117);br(105);br(108);br(100);br(32);br(119);br(105);br(116);br(104);br(32);br(97);br(32);br(104);br(105);br(103);br(104);br(101);br(114);br(32);br(118);br(97);br(108);br(117);br(101);br(32);br(102);br(111);br(114);br(32);br(77);br(65);br(88);br(95);br(83);br(69);br(84);br(74);br(77);br(80);br(83);br(10);ab(0);return 0}function uM(a,b){a=a|0;b=b|0;var d=0,e=0;while((d|0)<20){e=c[b+(d<<2)>>2]|0;if((e|0)==0)break;if((e|0)==(a|0)){return c[b+((d<<2)+4)>>2]|0}d=d+2|0}return 0}function uN(){return b0()|0}function uO(a){a=a|0;bd(a|0)}function uP(a,b){a=a|0;b=b|0;cA(a|0,b|0)}function uQ(a){a=a|0;return bS(a|0)|0}function uR(a){a=a|0;return aR(a|0)|0}function uS(a){a=a|0;return a5(a|0)|0}function uT(a){a=a|0;return aA(a|0)|0}function uU(a){a=a|0;return bP(a|0)|0}function uV(a,b){a=a|0;b=b|0;return aY(a|0,b|0)|0}function uW(a,b){a=a|0;b=b|0;return bA(a|0,b|0)|0}function uX(a,b){a=a|0;b=b|0;return bD(a|0,b|0)|0}function uY(a,b,c){a=a|0;b=b|0;c=c|0;return a_(a|0,b|0,c|0)|0}function uZ(a,b,c){a=a|0;b=b|0;c=c|0;return cf(a|0,b|0,c|0)|0}function u_(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return cH(a|0,b|0,c|0,d|0)|0}function u$(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return aI(a|0,b|0,c|0,d|0)|0}function u0(a,b){a=a|0;b=b|0;return+aV(a|0,b|0)}function u1(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;cI[a&63](b|0,c|0,d|0,e|0,f|0)}function u2(a,b,c,d,e,f,g){a=a|0;b=b|0;c=+c;d=+d;e=e|0;f=f|0;g=g|0;cJ[a&15](b|0,+c,+d,e|0,f|0,g|0)}function u3(a,b){a=a|0;b=+b;cK[a&63](+b)}function u4(a){a=a|0;return cL[a&7]()|0}function u5(a,b){a=a|0;b=b|0;cM[a&511](b|0)}function u6(a,b,c){a=a|0;b=b|0;c=c|0;cN[a&255](b|0,c|0)}function u7(a,b){a=a|0;b=b|0;return cO[a&255](b|0)|0}function u8(a,b,c){a=a|0;b=+b;c=+c;return cP[a&3](+b,+c)|0}function u9(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return cQ[a&7](b|0,c|0,d|0)|0}function va(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;cR[a&127](b|0,c|0,d|0)}function vb(a){a=a|0;cS[a&511]()}function vc(a,b,c,d,e,f){a=a|0;b=b|0;c=+c;d=d|0;e=e|0;f=f|0;cT[a&15](b|0,+c,d|0,e|0,f|0)}function vd(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;return cU[a&7](b|0,c|0,d|0,e|0)|0}function ve(a,b,c){a=a|0;b=b|0;c=c|0;return+cV[a&7](b|0,c|0)}function vf(a,b,c){a=a|0;b=b|0;c=c|0;return cW[a&63](b|0,c|0)|0}function vg(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;ab(0)}function vh(a,b,c,d,e,f){a=a|0;b=+b;c=+c;d=d|0;e=e|0;f=f|0;ab(1)}function vi(a){a=+a;ab(2)}function vj(){ab(3);return 0}function vk(a){a=a|0;ab(4)}function vl(a,b){a=a|0;b=b|0;ab(5)}function vm(a){a=a|0;ab(6);return 0}function vn(a,b){a=+a;b=+b;ab(7);return 0}function vo(a,b,c){a=a|0;b=b|0;c=c|0;ab(8);return 0}function vp(a,b,c){a=a|0;b=b|0;c=c|0;ab(9)}function vq(){ab(10)}function vr(a,b,c,d,e){a=a|0;b=+b;c=c|0;d=d|0;e=e|0;ab(11)}function vs(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;ab(12);return 0}function vt(a,b){a=a|0;b=b|0;ab(13);return 0.0}function vu(a,b){a=a|0;b=b|0;ab(14);return 0}
+// EMSCRIPTEN_END_FUNCS
+var cI=[vg,vg,sZ,vg,rC,vg,pN,vg,rM,vg,qN,vg,s1,vg,l2,vg,rd,vg,sn,vg,rc,vg,sC,vg,qP,vg,sF,vg,qk,vg,sW,vg,n6,vg,pV,vg,sd,vg,oq,vg,r0,vg,nK,vg,pw,vg,on,vg,lD,vg,nd,vg,mV,vg,pK,vg,vg,vg,vg,vg,vg,vg,vg,vg];var cJ=[vh,vh,nD,vh,mS,vh,qq,vh,n3,vh,pW,vh,lH,vh,vh,vh];var cK=[vi,vi,qG,vi,s_,vi,pM,vi,n7,vi,lC,vi,sE,vi,ql,vi,o8,vi,sG,vi,n5,vi,qX,vi,of,vi,nf,vi,sY,vi,mR,vi,mW,vi,lA,vi,op,vi,pP,vi,nb,vi,qL,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi,vi];var cL=[vj,vj,hy,vj,uN,vj,dI,vj];var cM=[vk,vk,g5,vk,iP,vk,kT,vk,th,vk,hg,vk,ku,vk,tm,vk,tn,vk,p4,vk,qf,vk,rk,vk,m_,vk,tb,vk,nu,vk,e0,vk,rj,vk,rZ,vk,kP,vk,mi,vk,mz,vk,hd,vk,kh,vk,sT,vk,rJ,vk,k7,vk,ff,vk,n8,vk,mX,vk,tc,vk,kE,vk,rs,vk,ks,vk,kF,vk,gX,vk,kG,vk,g1,vk,gY,vk,kM,vk,kn,vk,ou,vk,ke,vk,g6,vk,pZ,vk,ta,vk,lP,vk,iN,vk,tk,vk,te,vk,uu,vk,gL,vk,lF,vk,eW,vk,kS,vk,tr,vk,kx,vk,kU,vk,hj,vk,eI,vk,kI,vk,o3,vk,kR,vk,gZ,vk,kB,vk,gK,vk,m$,vk,tp,vk,kC,vk,e5,vk,qQ,vk,mN,vk,sa,vk,lB,vk,nH,vk,r2,vk,dL,vk,kv,vk,ki,vk,oN,vk,l9,vk,q9,vk,kY,vk,kj,vk,g0,vk,ph,vk,gM,vk,gW,vk,lU,vk,eJ,vk,s$,vk,qD,vk,tl,vk,kz,vk,kg,vk,gJ,vk,k5,vk,g$,vk,qi,vk,kQ,vk,g9,vk,e7,vk,kf,vk,sz,vk,g_,vk,g7,vk,gN,vk,sH,vk,eZ,vk,g4,vk,kl,vk,nY,vk,gP,vk,kZ,vk,k_,vk,qs,vk,m7,vk,gS,vk,e_,vk,qV,vk,hb,vk,eG,vk,pI,vk,kq,vk,g2,vk,he,vk,ky,vk,gV,vk,k3,vk,pY,vk,eK,vk,qo,vk,e$,vk,kX,vk,gG,vk,kk,vk,k1,vk,jI,vk,ps,vk,kD,vk,hf,vk,to,vk,s8,vk,gR,vk,pS,vk,gE,vk,rh,vk,gI,vk,ti,vk,g8,vk,nh,vk,k0,vk,k$,vk,td,vk,k6,vk,kJ,vk,tj,vk,gU,vk,km,vk,hc,vk,gH,vk,gF,vk,kH,vk,kO,vk,nE,vk,gO,vk,eF,vk,kL,vk,gQ,vk,gT,vk,hi,vk,oU,vk,k8,vk,k2,vk,kK,vk,hh,vk,s9,vk,g3,vk,kt,vk,uO,vk,qt,vk,eH,vk,mo,vk,os,vk,kN,vk,kw,vk,ha,vk,gD,vk,tq,vk,sl,vk,ko,vk,k4,vk,kp,vk,ol,vk,kA,vk,ow,vk,tf,vk,pX,vk,lT,vk,tg,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk,vk];var cN=[vl,vl,sS,vl,oT,vl,rH,vl,pT,vl,ok,vl,pr,vl,qp,vl,oB,vl,nG,vl,pG,vl,oA,vl,px,vl,oS,vl,nW,vl,p2,vl,qM,vl,l_,vl,r3,vl,oL,vl,m5,vl,my,vl,rr,vl,oM,vl,nX,vl,l7,vl,pf,vl,nn,vl,p3,vl,sy,vl,mt,vl,qe,vl,n9,vl,rY,vl,pg,vl,sR,vl,rI,vl,qd,vl,qR,vl,tM,vl,tU,vl,lZ,vl,py,vl,mh,vl,uP,vl,rX,vl,o2,vl,qB,vl,l8,vl,mY,vl,mL,vl,mg,vl,nO,vl,m6,vl,ns,vl,r9,vl,sx,vl,s0,vl,rq,vl,pH,vl,sI,vl,oj,vl,nP,vl,lO,vl,ms,vl,mM,vl,q3,vl,q2,vl,mx,vl,rT,vl,sk,vl,qC,vl,nm,vl,r8,vl,ot,vl,no,vl,o1,vl,qW,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl,vl];var cO=[vm,vm,o5,vm,e6,vm,qZ,vm,nF,vm,pQ,vm,lv,vm,h2,vm,pm,vm,uQ,vm,mP,vm,oW,vm,im,vm,pB,vm,oP,vm,q7,vm,ia,vm,mI,vm,nZ,vm,sX,vm,h4,vm,qm,vm,id,vm,ij,vm,r1,vm,m8,vm,q_,vm,n1,vm,ut,vm,uR,vm,mU,vm,l0,vm,rx,vm,h8,vm,sD,vm,sj,vm,sp,vm,hP,vm,pi,vm,ie,vm,pu,vm,oe,vm,o6,vm,h5,vm,ry,vm,pL,vm,ic,vm,od,vm,m9,vm,or,vm,h9,vm,uS,vm,h6,vm,nc,vm,uT,vm,qa,vm,sL,vm,q6,vm,rS,vm,oD,vm,h3,vm,hX,vm,lw,vm,tC,vm,rv,vm,rQ,vm,p9,vm,ik,vm,mQ,vm,nq,vm,sM,vm,o9,vm,qj,vm,nr,vm,qz,vm,ii,vm,mD,vm,pC,vm,h7,vm,lR,vm,lS,vm,uU,vm,mb,vm,qH,vm,pj,vm,rR,vm,h1,vm,il,vm,qy,vm,sr,vm,n4,vm,h0,vm,n_,vm,rP,vm,sK,vm,rw,vm,ng,vm,nC,vm,ig,vm,si,vm,sq,vm,ib,vm,qO,vm,ih,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm,vm];var cP=[vn,vn,lu,vn];var cQ=[vo,vo,uY,vo,uZ,vo,vo,vo];var cR=[vp,vp,tw,vp,pt,vp,uf,vp,lL,vp,sA,vp,rt,vp,r$,vp,ri,vp,o4,vp,rA,vp,pl,vp,p5,vp,qF,vp,na,vp,qE,vp,o7,vp,sB,vp,rB,vp,nv,vp,l$,vp,ma,vp,oE,vp,tX,vp,sU,vp,n0,vp,oO,vp,l1,vp,rb,vp,ne,vp,ly,vp,mA,vp,nQ,vp,r_,vp,qg,vp,mT,vp,mp,vp,tR,vp,hw,vp,mu,vp,mj,vp,rL,vp,sc,vp,tB,vp,lQ,vp,ra,vp,sV,vp,oo,vp,q4,vp,oC,vp,rK,vp,n$,vp,nJ,vp,sm,vp,oV,vp,nI,vp,om,vp,pJ,vp,pO,vp,mO,vp,pv,vp,qh,vp,tQ,vp,sb,vp];var cS=[vq,vq,m3,vq,p7,vq,mC,vq,mc,vq,dR,vq,pb,vq,rl,vq,pR,vq,qv,vq,mk,vq,ee,vq,np,vq,ei,vq,nL,vq,dX,vq,nU,vq,p8,vq,d9,vq,jy,vq,nz,vq,pA,vq,dH,vq,ru,vq,l3,vq,mw,vq,mf,vq,sQ,vq,oc,vq,o_,vq,d3,vq,nk,vq,es,vq,mB,vq,pa,vq,pe,vq,ox,vq,sN,vq,qT,vq,ob,vq,mE,vq,qr,vq,rm,vq,nt,vq,pd,vq,nN,vq,lo,vq,sh,vq,dV,vq,l5,vq,me,vq,ml,vq,oX,vq,m0,vq,lE,vq,sP,vq,eb,vq,se,vq,sJ,vq,d1,vq,lY,vq,r6,vq,er,vq,su,vq,eh,vq,qU,vq,pU,vq,oK,vq,n2,vq,t7,vq,fd,vq,dv,vq,dt,vq,pE,vq,mG,vq,m2,vq,qA,vq,rG,vq,qb,vq,q8,vq,rE,vq,pF,vq,rn,vq,d0,vq,m1,vq,mq,vq,mr,vq,eq,vq,r7,vq,oR,vq,qn,vq,nM,vq,en,vq,rg,vq,q5,vq,lN,vq,ov,vq,t5,vq,ny,vq,pq,vq,qx,vq,ex,vq,oY,vq,mF,vq,eg,vq,pn,vq,qc,vq,d_,vq,oF,vq,l4,vq,oz,vq,mH,vq,rF,vq,r4,vq,p_,vq,qS,vq,dQ,vq,mv,vq,dZ,vq,oa,vq,d6,vq,nw,vq,qK,vq,nS,vq,qu,vq,oJ,vq,sw,vq,nB,vq,pp,vq,dw,vq,oI,vq,og,vq,oH,vq,rO,vq,l6,vq,re,vq,oQ,vq,lX,vq,mm,vq,ec,vq,pk,vq,q1,vq,nj,vq,o$,vq,ej,vq,nx,vq,dN,vq,jo,vq,mn,vq,pD,vq,rW,vq,ni,vq,lM,vq,nR,vq,ro,vq,r5,vq,nl,vq,sO,vq,st,vq,p1,vq,po,vq,oi,vq,rU,vq,p0,vq,dW,vq,nV,vq,dY,vq,lI,vq,pc,vq,qI,vq,ek,vq,oy,vq,oG,vq,sg,vq,rN,vq,d5,vq,dE,vq,em,vq,qJ,vq,rV,vq,md,vq,q$,vq,ef,vq,oZ,vq,el,vq,ed,vq,mJ,vq,p$,vq,mZ,vq,pz,vq,rp,vq,nA,vq,dG,vq,sf,vq,d2,vq,sv,vq,so,vq,rz,vq,qw,vq,lV,vq,d4,vq,rf,vq,nT,vq,d7,vq,rD,vq,o0,vq,ss,vq,lW,vq,dS,vq,iK,vq,ds,vq,d$,vq,gk,vq,d8,vq,p6,vq,q0,vq,m4,vq,oh,vq,mK,vq,qY,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq,vq];var cT=[vr,vr,f6,vr,f7,vr,fJ,vr,dC,vr,fH,vr,fI,vr,di,vr];var cU=[vs,vs,u_,vs,u$,vs,vs,vs];var cV=[vt,vt,u0,vt,uz,vt,vt,vt];var cW=[vu,vu,lr,vu,la,vu,gn,vu,uV,vu,uW,vu,f5,vu,go,vu,i2,vu,hm,vu,hA,vu,iV,vu,uq,vu,uX,vu,uB,vu,f4,vu,gl,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu,vu];return{_memcmp:uG,_saveSetjmp:uL,_strcat:uC,_free:uu,_main:iM,_testSetjmp:uM,_strncpy:uF,_memmove:uH,_tolower:uI,_memset:uE,_malloc:ut,_memcpy:uD,_strcasecmp:uK,_strlen:uA,_realloc:uv,_strncasecmp:uJ,_strcpy:uB,stackAlloc:cX,stackSave:cY,stackRestore:cZ,setThrew:c_,setTempRet0:c$,setTempRet1:c0,setTempRet2:c1,setTempRet3:c2,setTempRet4:c3,setTempRet5:c4,setTempRet6:c5,setTempRet7:c6,setTempRet8:c7,setTempRet9:c8,dynCall_viiiii:u1,dynCall_viffiii:u2,dynCall_vf:u3,dynCall_i:u4,dynCall_vi:u5,dynCall_vii:u6,dynCall_ii:u7,dynCall_iff:u8,dynCall_iiii:u9,dynCall_viii:va,dynCall_v:vb,dynCall_vifiii:vc,dynCall_iiiii:vd,dynCall_fii:ve,dynCall_iii:vf}})
+// EMSCRIPTEN_END_ASM
+({ "Math": Math, "Int8Array": Int8Array, "Int16Array": Int16Array, "Int32Array": Int32Array, "Uint8Array": Uint8Array, "Uint16Array": Uint16Array, "Uint32Array": Uint32Array, "Float32Array": Float32Array, "Float64Array": Float64Array }, { "abort": abort, "assert": assert, "asmPrintInt": asmPrintInt, "asmPrintFloat": asmPrintFloat, "copyTempDouble": copyTempDouble, "copyTempFloat": copyTempFloat, "min": Math_min, "invoke_viiiii": invoke_viiiii, "invoke_viffiii": invoke_viffiii, "invoke_vf": invoke_vf, "invoke_i": invoke_i, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_ii": invoke_ii, "invoke_iff": invoke_iff, "invoke_iiii": invoke_iiii, "invoke_viii": invoke_viii, "invoke_v": invoke_v, "invoke_vifiii": invoke_vifiii, "invoke_iiiii": invoke_iiiii, "invoke_fii": invoke_fii, "invoke_iii": invoke_iii, "_lseek": _lseek, "__scanString": __scanString, "_fclose": _fclose, "_uname": _uname, "_sleep": _sleep, "__isFloat": __isFloat, "_fflush": _fflush, "_strtol": _strtol, "_fputc": _fputc, "_iconv": _iconv, "___signgam": ___signgam, "_fwrite": _fwrite, "_send": _send, "_fputs": _fputs, "_tmpnam": _tmpnam, "_isspace": _isspace, "_localtime": _localtime, "_read": _read, "_ceil": _ceil, "_strstr": _strstr, "_fileno": _fileno, "_perror": _perror, "_ctime": _ctime, "_fsync": _fsync, "_signal": _signal, "_opendir": _opendir, "_fmod": _fmod, "_strcmp": _strcmp, "_memchr": _memchr, "_strncmp": _strncmp, "_tmpfile": _tmpfile, "_snprintf": _snprintf, "_fgetc": _fgetc, "_pclose": _pclose, "_readdir": _readdir, "_cosh": _cosh, "_atexit": _atexit, "_fgets": _fgets, "_close": _close, "_strchr": _strchr, "_asin": _asin, "_llvm_lifetime_start": _llvm_lifetime_start, "___setErrNo": ___setErrNo, "_ftell": _ftell, "_exit": _exit, "_sprintf": _sprintf, "_llvm_lifetime_end": _llvm_lifetime_end, "_asctime": _asctime, "_strrchr": _strrchr, "_iconv_open": _iconv_open, "_modf": _modf, "_strcspn": _strcspn, "_getcwd": _getcwd, "_gmtime": _gmtime, "_localtime_r": _localtime_r, "_asctime_r": _asctime_r, "_recv": _recv, "_cos": _cos, "_putchar": _putchar, "_isalnum": _isalnum, "_popen": _popen, "_erfc": _erfc, "__exit": __exit, "_strftime": _strftime, "_llvm_va_end": _llvm_va_end, "_tzset": _tzset, "_sinh": _sinh, "_setlocale": _setlocale, "_isprint": _isprint, "_toupper": _toupper, "_printf": _printf, "_pread": _pread, "_fopen": _fopen, "_open": _open, "_usleep": _usleep, "_log": _log, "_puts": _puts, "_mktime": _mktime, "_fdopen": _fdopen, "_qsort": _qsort, "_system": _system, "_isalpha": _isalpha, "_strdup": _strdup, "_log10": _log10, "_closedir": _closedir, "_isatty": _isatty, "__formatString": __formatString, "_getenv": _getenv, "_gettimeofday": _gettimeofday, "_atoi": _atoi, "_vfprintf": _vfprintf, "_chdir": _chdir, "_llvm_pow_f64": _llvm_pow_f64, "_sbrk": _sbrk, "_localeconv": _localeconv, "___errno_location": ___errno_location, "_strerror": _strerror, "_lgamma": _lgamma, "_erf": _erf, "_strspn": _strspn, "__parseInt": __parseInt, "_ungetc": _ungetc, "_llvm_trap": _llvm_trap, "_rename": _rename, "_vsnprintf": _vsnprintf, "_sscanf": _sscanf, "_sysconf": _sysconf, "_acos": _acos, "_fread": _fread, "_abort": _abort, "_fprintf": _fprintf, "___fpclassifyf": ___fpclassifyf, "_tan": _tan, "___buildEnvironment": ___buildEnvironment, "_feof": _feof, "_strncat": _strncat, "_gmtime_r": _gmtime_r, "_fabs": _fabs, "_floor": _floor, "__reallyNegative": __reallyNegative, "_fseek": _fseek, "_sqrt": _sqrt, "_write": _write, "_rewind": _rewind, "_sin": _sin, "_stat": _stat, "_longjmp": _longjmp, "_atan": _atan, "_readdir_r": _readdir_r, "_strpbrk": _strpbrk, "_iconv_close": _iconv_close, "_setbuf": _setbuf, "_nl_langinfo": _nl_langinfo, "_pwrite": _pwrite, "_strerror_r": _strerror_r, "_atan2": _atan2, "_exp": _exp, "_time": _time, "_setvbuf": _setvbuf, "STACKTOP": STACKTOP, "STACK_MAX": STACK_MAX, "tempDoublePtr": tempDoublePtr, "ABORT": ABORT, "NaN": NaN, "Infinity": Infinity, "_stderr": _stderr, "_stdout": _stdout, "_stdin": _stdin }, buffer);
+var _memcmp = Module["_memcmp"] = asm["_memcmp"];
+var _saveSetjmp = Module["_saveSetjmp"] = asm["_saveSetjmp"];
+var _strcat = Module["_strcat"] = asm["_strcat"];
+var _free = Module["_free"] = asm["_free"];
+var _main = Module["_main"] = asm["_main"];
+var _testSetjmp = Module["_testSetjmp"] = asm["_testSetjmp"];
+var _strncpy = Module["_strncpy"] = asm["_strncpy"];
+var _memmove = Module["_memmove"] = asm["_memmove"];
+var _tolower = Module["_tolower"] = asm["_tolower"];
+var _memset = Module["_memset"] = asm["_memset"];
+var _malloc = Module["_malloc"] = asm["_malloc"];
+var _memcpy = Module["_memcpy"] = asm["_memcpy"];
+var _strcasecmp = Module["_strcasecmp"] = asm["_strcasecmp"];
+var _strlen = Module["_strlen"] = asm["_strlen"];
+var _realloc = Module["_realloc"] = asm["_realloc"];
+var _strncasecmp = Module["_strncasecmp"] = asm["_strncasecmp"];
+var _strcpy = Module["_strcpy"] = asm["_strcpy"];
+var dynCall_viiiii = Module["dynCall_viiiii"] = asm["dynCall_viiiii"];
+var dynCall_viffiii = Module["dynCall_viffiii"] = asm["dynCall_viffiii"];
+var dynCall_vf = Module["dynCall_vf"] = asm["dynCall_vf"];
+var dynCall_i = Module["dynCall_i"] = asm["dynCall_i"];
+var dynCall_vi = Module["dynCall_vi"] = asm["dynCall_vi"];
+var dynCall_vii = Module["dynCall_vii"] = asm["dynCall_vii"];
+var dynCall_ii = Module["dynCall_ii"] = asm["dynCall_ii"];
+var dynCall_iff = Module["dynCall_iff"] = asm["dynCall_iff"];
+var dynCall_iiii = Module["dynCall_iiii"] = asm["dynCall_iiii"];
+var dynCall_viii = Module["dynCall_viii"] = asm["dynCall_viii"];
+var dynCall_v = Module["dynCall_v"] = asm["dynCall_v"];
+var dynCall_vifiii = Module["dynCall_vifiii"] = asm["dynCall_vifiii"];
+var dynCall_iiiii = Module["dynCall_iiiii"] = asm["dynCall_iiiii"];
+var dynCall_fii = Module["dynCall_fii"] = asm["dynCall_fii"];
+var dynCall_iii = Module["dynCall_iii"] = asm["dynCall_iii"];
+Runtime.stackAlloc = function(size) { return asm['stackAlloc'](size) };
+Runtime.stackSave = function() { return asm['stackSave']() };
+Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
+// Warning: printing of i64 values may be slightly rounded! No deep i64 math used, so precise i64 code not included
+var i64Math = null;
+// === Auto-generated postamble setup entry stuff ===
+Module['callMain'] = function callMain(args) {
+ assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)');
+ assert(!Module['preRun'] || Module['preRun'].length == 0, 'cannot call main when preRun functions remain to be called');
+ args = args || [];
+ ensureInitRuntime();
+ var argc = args.length+1;
+ function pad() {
+ for (var i = 0; i < 4-1; i++) {
+ argv.push(0);
+ }
+ }
+ var argv = [allocate(intArrayFromString("/bin/this.program"), 'i8', ALLOC_NORMAL) ];
+ pad();
+ for (var i = 0; i < argc-1; i = i + 1) {
+ argv.push(allocate(intArrayFromString(args[i]), 'i8', ALLOC_NORMAL));
+ pad();
+ }
+ argv.push(0);
+ argv = allocate(argv, 'i32', ALLOC_NORMAL);
+ var ret;
+ var initialStackTop = STACKTOP;
+ try {
+ ret = Module['_main'](argc, argv, 0);
+ }
+ catch(e) {
+ if (e.name == 'ExitStatus') {
+ return e.status;
+ } else if (e == 'SimulateInfiniteLoop') {
+ Module['noExitRuntime'] = true;
+ } else {
+ throw e;
+ }
+ } finally {
+ STACKTOP = initialStackTop;
+ }
+ return ret;
+}
+function run(args) {
+ args = args || Module['arguments'];
+ if (runDependencies > 0) {
+ Module.printErr('run() called, but dependencies remain, so not running');
+ return 0;
+ }
+ if (Module['preRun']) {
+ if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
+ var toRun = Module['preRun'];
+ Module['preRun'] = [];
+ for (var i = toRun.length-1; i >= 0; i--) {
+ toRun[i]();
+ }
+ if (runDependencies > 0) {
+ // a preRun added a dependency, run will be called later
+ return 0;
+ }
+ }
+ function doRun() {
+ ensureInitRuntime();
+ preMain();
+ var ret = 0;
+ calledRun = true;
+ if (Module['_main'] && shouldRunNow) {
+ ret = Module['callMain'](args);
+ if (!Module['noExitRuntime']) {
+ exitRuntime();
+ }
+ }
+ if (Module['postRun']) {
+ if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
+ while (Module['postRun'].length > 0) {
+ Module['postRun'].pop()();
+ }
+ }
+ return ret;
+ }
+ if (Module['setStatus']) {
+ Module['setStatus']('Running...');
+ setTimeout(function() {
+ setTimeout(function() {
+ Module['setStatus']('');
+ }, 1);
+ if (!ABORT) doRun();
+ }, 1);
+ return 0;
+ } else {
+ return doRun();
+ }
+}
+Module['run'] = Module.run = run;
+// {{PRE_RUN_ADDITIONS}}
+if (Module['preInit']) {
+ if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']];
+ while (Module['preInit'].length > 0) {
+ Module['preInit'].pop()();
+ }
+}
+// shouldRunNow refers to calling main(), not run().
+var shouldRunNow = false;
+if (Module['noInitialRun']) {
+ shouldRunNow = false;
+}
+run();
+// {{POST_RUN_ADDITIONS}}
+ // {{MODULE_ADDITIONS}}
+ shouldRunNow = true;
+ self['Runtime'] = Runtime;
+ self['FS'] = FS;
+};
+gnuplot_create();
+// This is to avoid name mangling from closure compilers.
+self['FS'] = FS;
+self['FS']['root'] = FS.root;
+self['FS']['deleteFile'] = FS.deleteFile;
+self['FS']['findObject'] = FS.findObject;
+self['FS']['createDataFile'] = FS.createDataFile;
+self['FS']['getFileContents'] = function(name) {
+ var file = FS.findObject(name);
+ if (!file) return null;
+ return file.contents;
+};
diff --git a/chromium/v8/tools/profviz/profviz.css b/chromium/v8/tools/profviz/profviz.css
new file mode 100644
index 00000000000..c583b8943c7
--- /dev/null
+++ b/chromium/v8/tools/profviz/profviz.css
@@ -0,0 +1,138 @@
+/*
+Copyright 2013 the V8 project authors. 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.
+*/
+
+body {
+ background-color: #ddd;
+}
+
+#content {
+ background-color: #fff;
+ width: 1200px;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 25px;
+}
+
+textarea {
+ width: 1200px;
+ resize: none;
+ font-family: monospace;
+ font-size: 12px;
+ color: #000;
+ border: 1px dotted #aaa;
+ padding: 10px;
+ box-sizing: border-box;
+}
+
+textarea.log {
+ background-color: #ffe;
+}
+
+.display {
+ width: 1200px;
+ height: 600px;
+ background-color: #fff;
+ display: block;
+ box-sizing: border-box;
+}
+
+table {
+ width: 1200px;
+}
+
+button {
+ width: 100px;
+ height: 20px;
+ border: 1px solid #000;
+ border-color: #aaa;
+ font-family: Verdana;
+ font-size: 12px;
+ background-color: #ddd;
+}
+
+button:hover {
+ background-color: #eee;
+}
+
+#file {
+ width: 200px;
+ height: 20px;
+ border: none;
+ font-family: Verdana;
+ font-size: 12px;
+}
+
+input.range {
+ width: 70px;
+ height: 16px;
+ text-align: right;
+ padding-right: 5px;
+ border: 0px;
+ background-color: #eee;
+ font-family: Verdana;
+ font-size: 12px;
+}
+
+label {
+ height: 20px;
+ font-family: Verdana;
+ font-size: 12px;
+}
+
+.tooltip {
+ border-bottom: 1px dotted #000;
+}
+
+h1 {
+ font-family: Verdana;
+ font-size: 14px;
+ font-weight: bold;
+}
+
+.text {
+ font-family: Verdana;
+ font-size: 12px;
+}
+
+.tt {
+ font-family: monospace;
+ font-size: 12px;
+ color: #822;
+}
+
+a {
+ font-family: Verdana;
+ font-size: 12px;
+ text-decoration: none;
+ color: #282;
+}
+
+a.unroll {
+ border-bottom: 1px dotted #000;
+ color: #222;
+}
diff --git a/chromium/v8/tools/profviz/profviz.html b/chromium/v8/tools/profviz/profviz.html
new file mode 100644
index 00000000000..30494f80fe1
--- /dev/null
+++ b/chromium/v8/tools/profviz/profviz.html
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<!-- Copyright 2013 the V8 project authors. 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. -->
+
+<html lang="en-us">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>V8 profile log plotter</title>
+ <link rel="stylesheet" type="text/css" href="profviz.css">
+ <script src="profviz.js"></script>
+</head>
+
+<body onload="onload()">
+ <div id="content">
+
+ <img src="" id="plot" type="image/svg+xml" class="display"
+ width="1200" height="600" class="float-right"/>
+
+ <textarea id="prof" class="display" disabled=true></textarea>
+ <br/>
+
+ <table width="1200">
+ <tr>
+ <td width="330">
+ <button id="start" onclick="start()">
+ Start
+ </button>
+ <button id="reset" onclick="ui.reset(); worker.reset();">
+ Reset
+ </button>
+ <button id="toggledisplay" onclick="ui.toggle();">
+ Show profile
+ </button>
+ </td>
+ <td width="220">
+ <input type="file" id="file" onchange="ui.reset();"/>
+ </td>
+ <td width="300">
+ <label title="You can manually choose the range
+to plot only part of the log file.">
+ <span class="tooltip">Range</span>:
+ </label>
+ <input type="text" id="range_start" class="range"/>
+ <label>to</label>
+ <input type="text" id="range_end" class="range"/>
+ </td>
+ <td width="350">
+ <label title="We model profiling overhead by accounting a constant
+execution delay to each log entry. Adjust to better suit
+your computer's performance.">
+ <span class="tooltip">Delay per log entry</span>:
+ </label>
+ <input type="text" id="distortion" class="range" value="4500"/>
+ <label>picoseconds</label>
+ </td>
+ </tr>
+ </table>
+
+ <br/>
+ <textarea class="log" id="log" rows="8" disabled=true></textarea>
+
+ <div class="text">
+ <h1>
+ <a href="javascript:ui.info('instructions');" class="unroll">
+ Instructions
+ </a>
+ </h1>
+ <div id="instructions">
+ <ol>
+ <li>
+ Run V8 with
+ <span class="tt">--prof --log-timer-events</span>,
+ or alternatively,<br/>
+ Chrome with
+ <span class="tt">
+ --no-sandbox --js-flags="--prof --noprof-lazy --log-timer-events
+ </span> to produce <span class="tt">v8.log</span>.
+ </li>
+ <li>
+ Open
+ <span class="tt">v8.log</span>
+ on this page. Don't worry, it won't be uploaded anywhere.
+ </li>
+ <li>
+ Click "Start" to start number crunching. This will take a while.
+ </li>
+ <li>
+ Click "Show plot/profile" to switch between the statistical profile and
+ the timeline plot.<br/>
+ C++ items are missing in the statistical profile because symbol
+ information is not available.<br>
+ Consider using the
+ <a href="https://code.google.com/p/v8/wiki/V8Profiler">
+ command-line utility
+ </a> instead.
+ </li>
+ </div>
+ </div>
+
+ <div class="text">
+ <h1>
+ <a href="javascript:ui.info('credits');" class="unroll">
+ Credits
+ </a>
+ </h1>
+ <div id="credits">
+ <ul>
+ <li>
+ Christian Huettig for the
+ <a href="http://gnuplot.respawned.com/">Javascript port</a>
+ of Gnuplot 4.6.3.
+ </li>
+ <li>
+ The
+ <a href="https://github.com/kripken/emscripten">Emscripten compiler</a>
+ that made the port possible.
+ </li>
+ <li>
+ The <a href="http://www.gnuplot.info/">Gnuplot project</a>.
+ </li>
+ <li>
+ The <a href="https://developers.google.com/v8/">V8 project</a>.
+ </li>
+ </ul>
+ </div>
+ </div>
+
+</div>
+</body>
+</html>
diff --git a/chromium/v8/tools/profviz/profviz.js b/chromium/v8/tools/profviz/profviz.js
new file mode 100644
index 00000000000..7af12adc7e9
--- /dev/null
+++ b/chromium/v8/tools/profviz/profviz.js
@@ -0,0 +1,287 @@
+// Copyright 2013 the V8 project authors. 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 worker_scripts = [
+ "../csvparser.js",
+ "../splaytree.js",
+ "../codemap.js",
+ "../consarray.js",
+ "../profile.js",
+ "../profile_view.js",
+ "../logreader.js",
+ "../tickprocessor.js",
+ "composer.js",
+ "gnuplot-4.6.3-emscripten.js"
+];
+
+
+function plotWorker() {
+ var worker = null;
+
+ var delegateList = {
+ "log" : log,
+ "error" : logError,
+ "displayplot" : displayplot,
+ "displayprof" : displayprof,
+ "range" : setRange,
+ "script" : scriptLoaded
+ }
+
+ function initialize() {
+ ui.freeze();
+ worker = new Worker("worker.js");
+ running = false;
+
+ worker.postMessage({ "call" : "load scripts",
+ "args" : worker_scripts });
+
+ worker.addEventListener("message", function(event) {
+ var call = delegateList[event.data["call"]];
+ call(event.data["args"]);
+ });
+ }
+
+ function scriptLoaded() {
+ ui.thaw();
+ }
+
+ // Public methods.
+ this.run = function(filename,
+ resx, resy,
+ distortion,
+ range_start, range_end) {
+ var args = {
+ 'file' : filename,
+ 'resx' : resx,
+ 'resy' : resy,
+ 'distortion' : distortion,
+ 'range_start' : range_start,
+ 'range_end' : range_end
+ }
+ worker.postMessage({ 'call' : 'run', 'args' : args });
+ }
+
+ this.reset = function() {
+ if (worker) worker.terminate();
+ initialize();
+ }
+}
+
+
+function UIWrapper() {
+ var input_elements = ["range_start",
+ "range_end",
+ "distortion",
+ "start",
+ "file"];
+
+ var other_elements = ["log",
+ "plot",
+ "prof",
+ "instructions",
+ "credits",
+ "toggledisplay"];
+
+ for (var i in input_elements) {
+ var id = input_elements[i];
+ this[id] = document.getElementById(id);
+ }
+
+ for (var i in other_elements) {
+ var id = other_elements[i];
+ this[id] = document.getElementById(id);
+ }
+
+ this.freeze = function() {
+ this.plot.style.webkitFilter = "grayscale(1)";
+ this.prof.style.color = "#bbb";
+ for (var i in input_elements) {
+ this[input_elements[i]].disabled = true;
+ }
+ }
+
+ this.thaw = function() {
+ this.plot.style.webkitFilter = "";
+ this.prof.style.color = "#000";
+ for (var i in input_elements) {
+ this[input_elements[i]].disabled = false;
+ }
+ }
+
+ this.reset = function() {
+ this.thaw();
+ this.log.value = "";
+ this.range_start.value = "automatic";
+ this.range_end.value = "automatic";
+ this.toggle("plot");
+ this.plot.src = "";
+ this.prof.value = "";
+ }
+
+ this.toggle = function(mode) {
+ if (mode) this.toggledisplay.next_mode = mode;
+ if (this.toggledisplay.next_mode == "plot") {
+ this.toggledisplay.next_mode = "prof";
+ this.plot.style.display = "block";
+ this.prof.style.display = "none";
+ this.toggledisplay.innerHTML = "Show profile";
+ } else {
+ this.toggledisplay.next_mode = "plot";
+ this.plot.style.display = "none";
+ this.prof.style.display = "block";
+ this.toggledisplay.innerHTML = "Show plot";
+ }
+ }
+
+ this.info = function(field) {
+ var down_arrow = "\u25bc";
+ var right_arrow = "\u25b6";
+ if (field && this[field].style.display != "none") field = null; // Toggle.
+ this.credits.style.display = "none";
+ this.instructions.style.display = "none";
+ if (!field) return;
+ this[field].style.display = "block";
+ }
+}
+
+
+function log(text) {
+ ui.log.value += text;
+ ui.log.scrollTop = ui.log.scrollHeight;
+}
+
+
+function logError(text) {
+ if (ui.log.value.length > 0 &&
+ ui.log.value[ui.log.value.length-1] != "\n") {
+ ui.log.value += "\n";
+ }
+ ui.log.value += "ERROR: " + text + "\n";
+ ui.log.scrollTop = ui.log.scrollHeight;
+ error_logged = true;
+}
+
+
+function displayplot(args) {
+ if (error_logged) {
+ log("Plot failed.\n\n");
+ } else {
+ log("Displaying plot. Total time: " +
+ (Date.now() - timer) / 1000 + "ms.\n\n");
+ var blob = new Blob([new Uint8Array(args.contents).buffer],
+ { "type" : "image\/svg+xml" });
+ window.URL = window.URL || window.webkitURL;
+ ui.plot.src = window.URL.createObjectURL(blob);
+ }
+
+ ui.thaw();
+ ui.toggle("plot");
+}
+
+
+function displayprof(args) {
+ if (error_logged) return;
+ ui.prof.value = args;
+ this.prof.style.color = "";
+ ui.toggle("prof");
+}
+
+
+function start(event) {
+ error_logged = false;
+ ui.freeze();
+
+ try {
+ var file = getSelectedFile();
+ var distortion = getDistortion();
+ var range = getRange();
+ } catch (e) {
+ logError(e.message);
+ display();
+ return;
+ }
+
+ timer = Date.now();
+ worker.run(file, kResX, kResY, distortion, range[0], range[1]);
+}
+
+
+function getSelectedFile() {
+ var file = ui.file.files[0];
+ if (!file) throw Error("No valid file selected.");
+ if (!file.type.toString().match(/text/)) {
+ throw Error("'" + escape(file.name) + "' is not a text file.");
+ }
+ return file;
+}
+
+
+function getDistortion() {
+ var input_distortion =
+ parseInt(ui.distortion.value, 10);
+ if (isNaN(input_distortion)) {
+ input_distortion = ui.distortion.value = 4500;
+ }
+ return input_distortion / 1000000;
+}
+
+
+function getRange() {
+ var input_start =
+ parseInt(ui.range_start.value, 10);
+ if (isNaN(input_start)) input_start = undefined;
+ var input_end =
+ parseInt(ui.range_end.value, 10);
+ if (isNaN(input_end)) input_end = undefined;
+ return [input_start, input_end];
+}
+
+
+function setRange(args) {
+ ui.range_start.value = args.start.toFixed(1);
+ ui.range_end.value = args.end.toFixed(1);
+}
+
+
+function onload() {
+ kResX = 1200;
+ kResY = 600;
+ error_logged = false;
+ ui = new UIWrapper();
+ ui.reset();
+ ui.info(null);
+ worker = new plotWorker();
+ worker.reset();
+}
+
+
+var kResX;
+var kResY;
+var error_logged;
+var ui;
+var worker;
+var timer;
diff --git a/chromium/v8/tools/profviz/stdio.js b/chromium/v8/tools/profviz/stdio.js
new file mode 100644
index 00000000000..e8001494c9d
--- /dev/null
+++ b/chromium/v8/tools/profviz/stdio.js
@@ -0,0 +1,52 @@
+// Copyright 2013 the V8 project authors. 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 processor = new ArgumentsProcessor(arguments);
+var distortion_per_entry = 0;
+var range_start_override = undefined;
+var range_end_override = undefined;
+
+if (!processor.parse()) processor.printUsageAndExit();;
+var result = processor.result();
+var distortion = parseInt(result.distortion);
+if (isNaN(distortion)) processor.printUsageAndExit();;
+// Convert picoseconds to milliseconds.
+distortion_per_entry = distortion / 1000000;
+var rangelimits = result.range.split(",");
+var range_start = parseInt(rangelimits[0]);
+var range_end = parseInt(rangelimits[1]);
+if (!isNaN(range_start)) range_start_override = range_start;
+if (!isNaN(range_end)) range_end_override = range_end;
+
+var kResX = 1600;
+var kResY = 600;
+var psc = new PlotScriptComposer(kResX, kResY);
+psc.collectData(readline, distortion_per_entry);
+psc.findPlotRange(range_start_override, range_end_override);
+print("set terminal pngcairo size " + kResX + "," + kResY +
+ " enhanced font 'Helvetica,10'");
+psc.assembleOutput(print);
diff --git a/chromium/v8/tools/profviz/worker.js b/chromium/v8/tools/profviz/worker.js
new file mode 100644
index 00000000000..60a557f982d
--- /dev/null
+++ b/chromium/v8/tools/profviz/worker.js
@@ -0,0 +1,167 @@
+// Copyright 2013 the V8 project authors. 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 delegateList = {
+ "load scripts" : load_scripts,
+ "run" : run,
+}
+
+self.addEventListener("message", function(event) {
+ var call = delegateList[event.data["call"]];
+ var result = call(event.data["args"]);
+}, false);
+
+
+function log(text) {
+ self.postMessage({ "call" : "log", "args" : text });
+}
+
+
+function displayplot(content) {
+ self.postMessage({ "call" : "displayplot", "args" : content});
+}
+
+
+function displayprof(content) {
+ self.postMessage({ "call" : "displayprof", "args" : content});
+}
+
+
+function setRange(start, end) {
+ self.postMessage({ "call" : "range",
+ "args" : { "start" : start, "end" : end } });
+}
+
+
+function time(name, fun) {
+ log(name + "...");
+ var start = Date.now();
+ fun();
+ log(" took " + (Date.now() - start) / 1000 + "s.\n");
+}
+
+
+function load_scripts(scripts) {
+ time("Loading scripts",
+ function() { for (var i in scripts) importScripts(scripts[i]); });
+ self.postMessage({ "call" : "script" });
+}
+
+
+function run(args) {
+ var file = args["file"];
+ var resx = args["resx"];
+ var resy = args["resy"];
+ var distortion = args["distortion"];
+ var range_start_override = args["range_start"];
+ var range_end_override = args["range_end"];
+
+ var reader = new FileReaderSync();
+ var content_lines;
+
+ time("Reading log file (" + (file.size / 1024).toFixed(1) + " kB)",
+ function() {
+ var content = reader.readAsText(file);
+ content_lines = content.split("\n");
+ });
+
+ time("Producing statistical profile",
+ function() {
+ var profile = "";
+ print = function(text) { profile += text + "\n"; };
+ // Dummy entries provider, as we cannot call nm.
+ var entriesProvider = new UnixCppEntriesProvider("", "");
+ var targetRootFS = "";
+ var separateIc = false;
+ var callGraphSize = 5;
+ var ignoreUnknown = true;
+ var stateFilter = null;
+ var snapshotLogProcessor = null;
+ var range = range_start_override + "," + range_end_override;
+
+ var tickProcessor = new TickProcessor(entriesProvider,
+ separateIc,
+ callGraphSize,
+ ignoreUnknown,
+ stateFilter,
+ snapshotLogProcessor,
+ distortion,
+ range);
+ for (var i = 0; i < content_lines.length; i++) {
+ tickProcessor.processLogLine(content_lines[i]);
+ }
+ tickProcessor.printStatistics();
+ displayprof(profile);
+ });
+
+ var input_file_name = "input_temp";
+ var output_file_name = "output.svg";
+
+ var psc = new PlotScriptComposer(resx, resy);
+ var objects = 0;
+
+ time("Collecting events (" + content_lines.length + " entries)",
+ function() {
+ var line_cursor = 0;
+ var input = function() { return content_lines[line_cursor++]; };
+ psc.collectData(input, distortion);
+ psc.findPlotRange(range_start_override,
+ range_end_override,
+ setRange);
+ });
+
+ time("Assembling plot script",
+ function() {
+ var plot_script = "";
+ var output = function(text) { plot_script += text + "\n"; };
+ output("set terminal svg size " + resx + "," + resy +
+ " enhanced font \"Helvetica,10\"");
+ output("set output \""+ output_file_name + "\"");
+ objects = psc.assembleOutput(output);
+ if (FS.findObject(input_file_name)) {
+ FS.deleteFile(input_file_name);
+ }
+ var arrc = Module["intArrayFromString"](plot_script, true);
+ FS.createDataFile("/", input_file_name, arrc);
+ });
+
+ time("Running gnuplot (" + objects + " objects)",
+ function() { Module.run([input_file_name]); });
+
+ displayplot(FS.findObject(output_file_name));
+}
+
+
+var Module = {
+ "noInitialRun": true,
+ print: function(text) {
+ self.postMessage({"call": "error", "args": text});
+ },
+ printErr: function(text) {
+ self.postMessage({"call": "error", "args": text});
+ },
+};
diff --git a/chromium/v8/tools/push-to-trunk.sh b/chromium/v8/tools/push-to-trunk.sh
new file mode 100755
index 00000000000..8512d128778
--- /dev/null
+++ b/chromium/v8/tools/push-to-trunk.sh
@@ -0,0 +1,411 @@
+#!/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-push
+TRUNKBRANCH=trunk-push
+PERSISTFILE_BASENAME=/tmp/v8-push-to-trunk-tempfile
+CHROME_PATH=
+
+########## Function definitions
+
+source $(dirname $BASH_SOURCE)/common-includes.sh
+
+usage() {
+cat << EOF
+usage: $0 OPTIONS
+
+Performs the necessary steps for a V8 push to trunk. Only works for \
+git checkouts.
+
+OPTIONS:
+ -h Show this message
+ -s Specify the step where to start work. Default: 0.
+ -l Manually specify the git commit ID of the last push to trunk.
+ -c Specify the path to your Chromium src/ directory to automate the
+ V8 roll.
+EOF
+}
+
+########## Option parsing
+
+while getopts ":hs:l:c:" OPTION ; do
+ case $OPTION in
+ h) usage
+ exit 0
+ ;;
+ s) START_STEP=$OPTARG
+ ;;
+ l) LASTPUSH=$OPTARG
+ ;;
+ c) CHROME_PATH=$OPTARG
+ ;;
+ ?) echo "Illegal option: -$OPTARG"
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+
+########## Regular workflow
+
+initial_environment_checks
+
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Preparation"
+ common_prepare
+ delete_branch $TRUNKBRANCH
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create a fresh branch."
+ git checkout -b $BRANCHNAME svn/bleeding_edge \
+ || die "Creating branch $BRANCHNAME failed."
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Detect commit ID of last push to trunk."
+ [[ -n "$LASTPUSH" ]] || LASTPUSH=$(git log -1 --format=%H ChangeLog)
+ LOOP=1
+ while [ $LOOP -eq 1 ] ; do
+ # Print assumed commit, circumventing git's pager.
+ git log -1 $LASTPUSH | cat
+ confirm "Is the commit printed above the last push to trunk?"
+ if [ $? -eq 0 ] ; then
+ LOOP=0
+ else
+ LASTPUSH=$(git log -1 --format=%H $LASTPUSH^ ChangeLog)
+ fi
+ done
+ persist "LASTPUSH"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Prepare raw ChangeLog entry."
+ # These version numbers are used again later for the trunk commit.
+ read_and_persist_version
+
+ DATE=$(date +%Y-%m-%d)
+ persist "DATE"
+ echo "$DATE: Version $MAJOR.$MINOR.$BUILD" > "$CHANGELOG_ENTRY_FILE"
+ echo "" >> "$CHANGELOG_ENTRY_FILE"
+ COMMITS=$(git log $LASTPUSH..HEAD --format=%H)
+ 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 and convert them to
+ # "(issue xxxx)".
+ git log -1 $commit --format="%B" \
+ | grep "^BUG=" | grep -v "BUG=$" | grep -v "BUG=none$" \
+ | sed -e 's/^/ /' \
+ | sed -e 's/BUG=v8:\(.*\)$/(issue \1)/' \
+ | sed -e 's/BUG=chromium:\(.*\)$/(Chromium 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"
+ done
+ echo " Performance and stability improvements on all platforms." \
+ >> "$CHANGELOG_ENTRY_FILE"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Edit ChangeLog entry."
+ echo -n "Please press <Return> to have your EDITOR open the ChangeLog entry, \
+then edit its contents to your liking. When you're done, save the file and \
+exit your EDITOR. "
+ read ANSWER
+ $EDITOR "$CHANGELOG_ENTRY_FILE"
+ NEWCHANGELOG=$(mktemp)
+ # Eliminate any trailing newlines by going through a shell variable.
+ # 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.
+ echo "" >> "$NEWCHANGELOG"
+ cat ChangeLog >> "$NEWCHANGELOG"
+ mv "$NEWCHANGELOG" ChangeLog
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Increment version number."
+ restore_if_unset "BUILD"
+ NEWBUILD=$(($BUILD + 1))
+ confirm "Automatically increment BUILD_NUMBER? (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 BUILD_NUMBER/s/[0-9]*$/$NEWBUILD/" \
+ -i "$VERSION_FILE"
+ else
+ $EDITOR "$VERSION_FILE"
+ fi
+ read_and_persist_version "NEW"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to local branch."
+ restore_version_if_unset "NEW"
+ 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
+
+upload_step
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to the repository."
+ wait_for_lgtm
+ # Re-read the ChangeLog entry (to pick up possible changes).
+ cat ChangeLog | awk --posix '{
+ if ($0 ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}:/) {
+ if (in_firstblock == 1) {
+ exit 0;
+ } else {
+ in_firstblock = 1;
+ }
+ };
+ print $0;
+ }' > "$CHANGELOG_ENTRY_FILE"
+ git cl dcommit || die "'git cl dcommit' failed, please try again."
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Fetch straggler commits that sneaked in \
+since this script was started."
+ 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
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Squash commits into one."
+ # Instead of relying on "git rebase -i", we'll just create a diff, because
+ # that's easier to automate.
+ 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
+ # - merge paragraphs into single long lines, keeping empty lines between them.
+ restore_if_unset "DATE"
+ CHANGELOGENTRY=$(cat "$CHANGELOG_ENTRY_FILE")
+ echo "$CHANGELOGENTRY" \
+ | sed -e "s/^$DATE: //" \
+ | sed -e 's/^ *//' \
+ | awk '{
+ if (need_space == 1) {
+ printf(" ");
+ };
+ printf("%s", $0);
+ if ($0 ~ /^$/) {
+ printf("\n\n");
+ need_space = 0;
+ } else {
+ need_space = 1;
+ }
+ }' > "$COMMITMSG_FILE" || die "Commit message editing failed."
+ rm -f "$CHANGELOG_ENTRY_FILE"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create a new branch from trunk."
+ git checkout -b $TRUNKBRANCH svn/trunk \
+ || die "Checking out a new branch '$TRUNKBRANCH' failed."
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Apply squashed changes."
+ rm -f "$TOUCHED_FILES_FILE"
+ apply_patch "$PATCH_FILE"
+ rm -f "$PATCH_FILE"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Set correct version for trunk."
+ restore_version_if_unset
+ sed -e "/#define MAJOR_VERSION/s/[0-9]*$/$MAJOR/" \
+ -e "/#define MINOR_VERSION/s/[0-9]*$/$MINOR/" \
+ -e "/#define BUILD_NUMBER/s/[0-9]*$/$BUILD/" \
+ -e "/#define PATCH_LEVEL/s/[0-9]*$/0/" \
+ -e "/#define IS_CANDIDATE_VERSION/s/[0-9]*$/0/" \
+ -i "$VERSION_FILE" || die "Patching $VERSION_FILE failed."
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to local trunk branch."
+ git add "$VERSION_FILE"
+ git commit -F "$COMMITMSG_FILE" || die "'git commit' failed."
+ rm -f "$COMMITMSG_FILE"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Sanity check."
+ confirm "Please check if your local checkout is sane: Inspect $VERSION_FILE, \
+compile, run tests. Do you want to commit this new trunk revision to the \
+repository?"
+ [[ $? -eq 0 ]] || die "Execution canceled."
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to SVN."
+ git svn dcommit 2>&1 | tee >(grep -E "^Committed r[0-9]+" \
+ | sed -e 's/^Committed r\([0-9]\+\)/\1/' \
+ > "$TRUNK_REVISION_FILE") \
+ || die "'git svn dcommit' failed."
+ TRUNK_REVISION=$(cat "$TRUNK_REVISION_FILE")
+ # Sometimes grepping for the revision fails. No idea why. If you figure
+ # out why it is flaky, please do fix it properly.
+ if [ -z "$TRUNK_REVISION" ] ; then
+ echo "Sorry, grepping for the SVN revision failed. Please look for it in \
+the last command's output above and provide it manually (just the number, \
+without the leading \"r\")."
+ while [ -z "$TRUNK_REVISION" ] ; do
+ echo -n "> "
+ read TRUNK_REVISION
+ done
+ fi
+ persist "TRUNK_REVISION"
+ rm -f "$TRUNK_REVISION_FILE"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Tag the new revision."
+ restore_version_if_unset
+ git svn tag $MAJOR.$MINOR.$BUILD -m "Tagging version $MAJOR.$MINOR.$BUILD" \
+ || die "'git svn tag' failed."
+fi
+
+if [ -z "$CHROME_PATH" ] ; then
+ echo ">>> (asking for Chromium checkout)"
+ echo -n "Do you have a \"NewGit\" Chromium checkout and want this script \
+to automate creation of the roll CL? If yes, enter the path to (and including) \
+the \"src\" directory here, otherwise just press <Return>: "
+ read CHROME_PATH
+fi
+
+if [ -n "$CHROME_PATH" ] ; then
+
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Switch to Chromium checkout."
+ V8_PATH=$(pwd)
+ persist "V8_PATH"
+ cd "$CHROME_PATH"
+ initial_environment_checks
+ # Check for a clean workdir.
+ [[ -z "$(git status -s -uno)" ]] \
+ || die "Workspace is not clean. Please commit or undo your changes."
+ # Assert that the DEPS file is there.
+ [[ -w "DEPS" ]] || die "DEPS file not present or not writable; \
+current directory is: $(pwd)."
+ fi
+
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Update the checkout and create a new branch."
+ git checkout master || die "'git checkout master' failed."
+ git pull || die "'git pull' failed, please try again."
+ restore_if_unset "TRUNK_REVISION"
+ git checkout -b "v8-roll-$TRUNK_REVISION" \
+ || die "Failed to checkout a new branch."
+ fi
+
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create and upload CL."
+ # Patch DEPS file.
+ sed -r -e "/\"v8_revision\": /s/\"[0-9]+\"/\"$TRUNK_REVISION\"/" \
+ -i DEPS
+ restore_version_if_unset
+ echo -n "Please enter the email address of a reviewer for the roll CL: "
+ read REVIEWER
+ git commit -am "Update V8 to version $MAJOR.$MINOR.$BUILD.
+
+TBR=$REVIEWER" || die "'git commit' failed."
+ git cl upload --send-mail \
+ || die "'git cl upload' failed, please try again."
+ echo "CL uploaded."
+ fi
+
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Returning to V8 checkout."
+ restore_if_unset "V8_PATH"
+ cd "$V8_PATH"
+ fi
+fi # if [ -n "$CHROME_PATH" ]
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Done!"
+ restore_version_if_unset
+ restore_if_unset "TRUNK_REVISION"
+ if [ -n "$CHROME_PATH" ] ; then
+ echo "Congratulations, you have successfully created the trunk revision \
+$MAJOR.$MINOR.$BUILD and rolled it into Chromium. Please don't forget to \
+update the v8rel spreadsheet:"
+ else
+ echo "Congratulations, you have successfully created the trunk revision \
+$MAJOR.$MINOR.$BUILD. Please don't forget to roll this new version into \
+Chromium, and to update the v8rel spreadsheet:"
+ fi
+ echo -e "$MAJOR.$MINOR.$BUILD\ttrunk\t$TRUNK_REVISION"
+ common_cleanup
+ [[ "$TRUNKBRANCH" != "$CURRENT_BRANCH" ]] && git branch -D $TRUNKBRANCH
+fi
diff --git a/chromium/v8/tools/run-deopt-fuzzer.py b/chromium/v8/tools/run-deopt-fuzzer.py
new file mode 100755
index 00000000000..d554a989f12
--- /dev/null
+++ b/chromium/v8/tools/run-deopt-fuzzer.py
@@ -0,0 +1,467 @@
+#!/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.
+
+
+import json
+import math
+import multiprocessing
+import optparse
+import os
+from os.path import join
+import random
+import shlex
+import subprocess
+import sys
+import time
+
+from testrunner.local import execution
+from testrunner.local import progress
+from testrunner.local import testsuite
+from testrunner.local import utils
+from testrunner.local import verbose
+from testrunner.objects import context
+
+
+ARCH_GUESS = utils.DefaultArch()
+DEFAULT_TESTS = ["mjsunit", "webkit"]
+TIMEOUT_DEFAULT = 60
+TIMEOUT_SCALEFACTOR = {"debug" : 4,
+ "release" : 1 }
+
+MODE_FLAGS = {
+ "debug" : ["--nobreak-on-abort", "--nodead-code-elimination",
+ "--nofold-constants", "--enable-slow-asserts",
+ "--debug-code", "--verify-heap",
+ "--noparallel-recompilation"],
+ "release" : ["--nobreak-on-abort", "--nodead-code-elimination",
+ "--nofold-constants", "--noparallel-recompilation"]}
+
+SUPPORTED_ARCHS = ["android_arm",
+ "android_ia32",
+ "arm",
+ "ia32",
+ "mipsel",
+ "nacl_ia32",
+ "nacl_x64",
+ "x64"]
+# Double the timeout for these:
+SLOW_ARCHS = ["android_arm",
+ "android_ia32",
+ "arm",
+ "mipsel",
+ "nacl_ia32",
+ "nacl_x64"]
+MAX_DEOPT = 1000000000
+DISTRIBUTION_MODES = ["smooth", "random"]
+
+
+class RandomDistribution:
+ def __init__(self, seed=None):
+ seed = seed or random.randint(1, sys.maxint)
+ print "Using random distribution with seed %d" % seed
+ self._random = random.Random(seed)
+
+ def Distribute(self, n, m):
+ if n > m:
+ n = m
+ return self._random.sample(xrange(1, m + 1), n)
+
+
+class SmoothDistribution:
+ """Distribute n numbers into the interval [1:m].
+ F1: Factor of the first derivation of the distribution function.
+ F2: Factor of the second derivation of the distribution function.
+ With F1 and F2 set to 0, the distribution will be equal.
+ """
+ def __init__(self, factor1=2.0, factor2=0.2):
+ self._factor1 = factor1
+ self._factor2 = factor2
+
+ def Distribute(self, n, m):
+ if n > m:
+ n = m
+ if n <= 1:
+ return [ 1 ]
+
+ result = []
+ x = 0.0
+ dx = 1.0
+ ddx = self._factor1
+ dddx = self._factor2
+ for i in range(0, n):
+ result += [ x ]
+ x += dx
+ dx += ddx
+ ddx += dddx
+
+ # Project the distribution into the interval [0:M].
+ result = [ x * m / result[-1] for x in result ]
+
+ # Equalize by n. The closer n is to m, the more equal will be the
+ # distribution.
+ for (i, x) in enumerate(result):
+ # The value of x if it was equally distributed.
+ equal_x = i / float(n - 1) * float(m - 1) + 1
+
+ # Difference factor between actual and equal distribution.
+ diff = 1 - (x / equal_x)
+
+ # Equalize x dependent on the number of values to distribute.
+ result[i] = int(x + (i + 1) * diff)
+ return result
+
+
+def Distribution(options):
+ if options.distribution_mode == "random":
+ return RandomDistribution(options.seed)
+ if options.distribution_mode == "smooth":
+ return SmoothDistribution(options.distribution_factor1,
+ options.distribution_factor2)
+
+
+def BuildOptions():
+ result = optparse.OptionParser()
+ result.add_option("--arch",
+ help=("The architecture to run tests for, "
+ "'auto' or 'native' for auto-detect"),
+ default="ia32,x64,arm")
+ result.add_option("--arch-and-mode",
+ help="Architecture and mode in the format 'arch.mode'",
+ default=None)
+ result.add_option("--buildbot",
+ help="Adapt to path structure used on buildbots",
+ default=False, action="store_true")
+ result.add_option("--command-prefix",
+ help="Prepended to each shell command used to run a test",
+ default="")
+ result.add_option("--coverage", help=("Exponential test coverage "
+ "(range 0.0, 1.0) -- 0.0: one test, 1.0 all tests (slow)"),
+ default=0.4, type="float")
+ result.add_option("--coverage-lift", help=("Lifts test coverage for tests "
+ "with a small number of deopt points (range 0, inf)"),
+ default=20, type="int")
+ result.add_option("--download-data", help="Download missing test suite data",
+ default=False, action="store_true")
+ result.add_option("--distribution-factor1", help=("Factor of the first "
+ "derivation of the distribution function"), default=2.0,
+ type="float")
+ result.add_option("--distribution-factor2", help=("Factor of the second "
+ "derivation of the distribution function"), default=0.7,
+ type="float")
+ result.add_option("--distribution-mode", help=("How to select deopt points "
+ "for a given test (smooth|random)"),
+ default="smooth")
+ result.add_option("--dump-results-file", help=("Dump maximum number of "
+ "deopt points per test to a file"))
+ result.add_option("--extra-flags",
+ help="Additional flags to pass to each test command",
+ default="")
+ result.add_option("--isolates", help="Whether to test isolates",
+ default=False, action="store_true")
+ result.add_option("-j", help="The number of parallel tasks to run",
+ default=0, type="int")
+ result.add_option("-m", "--mode",
+ help="The test modes in which to run (comma-separated)",
+ default="release,debug")
+ result.add_option("--outdir", help="Base directory with compile output",
+ default="out")
+ result.add_option("-p", "--progress",
+ help=("The style of progress indicator"
+ " (verbose, dots, color, mono)"),
+ choices=progress.PROGRESS_INDICATORS.keys(),
+ default="mono")
+ result.add_option("--shard-count",
+ help="Split testsuites into this number of shards",
+ default=1, type="int")
+ result.add_option("--shard-run",
+ help="Run this shard from the split up tests.",
+ default=1, type="int")
+ result.add_option("--shell-dir", help="Directory containing executables",
+ default="")
+ result.add_option("--seed", help="The seed for the random distribution",
+ type="int")
+ result.add_option("-t", "--timeout", help="Timeout in seconds",
+ default= -1, type="int")
+ result.add_option("-v", "--verbose", help="Verbose output",
+ default=False, action="store_true")
+ return result
+
+
+def ProcessOptions(options):
+ global VARIANT_FLAGS
+
+ # Architecture and mode related stuff.
+ if options.arch_and_mode:
+ tokens = options.arch_and_mode.split(".")
+ options.arch = tokens[0]
+ options.mode = tokens[1]
+ options.mode = options.mode.split(",")
+ for mode in options.mode:
+ if not mode.lower() in ["debug", "release"]:
+ print "Unknown mode %s" % mode
+ return False
+ if options.arch in ["auto", "native"]:
+ options.arch = ARCH_GUESS
+ options.arch = options.arch.split(",")
+ for arch in options.arch:
+ if not arch in SUPPORTED_ARCHS:
+ print "Unknown architecture %s" % arch
+ return False
+
+ # Special processing of other options, sorted alphabetically.
+ options.command_prefix = shlex.split(options.command_prefix)
+ options.extra_flags = shlex.split(options.extra_flags)
+ if options.j == 0:
+ options.j = multiprocessing.cpu_count()
+ if not options.distribution_mode in DISTRIBUTION_MODES:
+ print "Unknown distribution mode %s" % options.distribution_mode
+ return False
+ if options.distribution_factor1 < 0.0:
+ print ("Distribution factor1 %s is out of range. Defaulting to 0.0"
+ % options.distribution_factor1)
+ options.distribution_factor1 = 0.0
+ if options.distribution_factor2 < 0.0:
+ print ("Distribution factor2 %s is out of range. Defaulting to 0.0"
+ % options.distribution_factor2)
+ options.distribution_factor2 = 0.0
+ if options.coverage < 0.0 or options.coverage > 1.0:
+ print ("Coverage %s is out of range. Defaulting to 0.4"
+ % options.coverage)
+ options.coverage = 0.4
+ if options.coverage_lift < 0:
+ print ("Coverage lift %s is out of range. Defaulting to 0"
+ % options.coverage_lift)
+ options.coverage_lift = 0
+ return True
+
+
+def ShardTests(tests, shard_count, shard_run):
+ if shard_count < 2:
+ return tests
+ if shard_run < 1 or shard_run > shard_count:
+ print "shard-run not a valid number, should be in [1:shard-count]"
+ print "defaulting back to running all tests"
+ return tests
+ count = 0
+ shard = []
+ for test in tests:
+ if count % shard_count == shard_run - 1:
+ shard.append(test)
+ count += 1
+ return shard
+
+
+def Main():
+ parser = BuildOptions()
+ (options, args) = parser.parse_args()
+ if not ProcessOptions(options):
+ parser.print_help()
+ return 1
+
+ exit_code = 0
+ workspace = os.path.abspath(join(os.path.dirname(sys.argv[0]), ".."))
+
+ suite_paths = utils.GetSuitePaths(join(workspace, "test"))
+
+ if len(args) == 0:
+ suite_paths = [ s for s in suite_paths if s in DEFAULT_TESTS ]
+ else:
+ args_suites = set()
+ for arg in args:
+ suite = arg.split(os.path.sep)[0]
+ if not suite in args_suites:
+ args_suites.add(suite)
+ suite_paths = [ s for s in suite_paths if s in args_suites ]
+
+ suites = []
+ for root in suite_paths:
+ suite = testsuite.TestSuite.LoadTestSuite(
+ os.path.join(workspace, "test", root))
+ if suite:
+ suites.append(suite)
+
+ if options.download_data:
+ for s in suites:
+ s.DownloadData()
+
+ for mode in options.mode:
+ for arch in options.arch:
+ code = Execute(arch, mode, args, options, suites, workspace)
+ exit_code = exit_code or code
+ return exit_code
+
+
+def CalculateNTests(m, options):
+ """Calculates the number of tests from m deopt points with exponential
+ coverage.
+ The coverage is expected to be between 0.0 and 1.0.
+ The 'coverage lift' lifts the coverage for tests with smaller m values.
+ """
+ c = float(options.coverage)
+ l = float(options.coverage_lift)
+ return int(math.pow(m, (m * c + l) / (m + l)))
+
+
+def Execute(arch, mode, args, options, suites, workspace):
+ print(">>> Running tests for %s.%s" % (arch, mode))
+
+ dist = Distribution(options)
+
+ shell_dir = options.shell_dir
+ if not shell_dir:
+ if options.buildbot:
+ shell_dir = os.path.join(workspace, options.outdir, mode)
+ mode = mode.lower()
+ else:
+ shell_dir = os.path.join(workspace, options.outdir,
+ "%s.%s" % (arch, mode))
+ shell_dir = os.path.relpath(shell_dir)
+
+ # Populate context object.
+ mode_flags = MODE_FLAGS[mode]
+ timeout = options.timeout
+ if timeout == -1:
+ # Simulators are slow, therefore allow a longer default timeout.
+ if arch in SLOW_ARCHS:
+ timeout = 2 * TIMEOUT_DEFAULT;
+ else:
+ timeout = TIMEOUT_DEFAULT;
+
+ timeout *= TIMEOUT_SCALEFACTOR[mode]
+ ctx = context.Context(arch, mode, shell_dir,
+ mode_flags, options.verbose,
+ timeout, options.isolates,
+ options.command_prefix,
+ options.extra_flags)
+
+ # Find available test suites and read test cases from them.
+ variables = {
+ "mode": mode,
+ "arch": arch,
+ "system": utils.GuessOS(),
+ "isolates": options.isolates,
+ "deopt_fuzzer": True,
+ }
+ all_tests = []
+ num_tests = 0
+ test_id = 0
+
+ # Remember test case prototypes for the fuzzing phase.
+ test_backup = dict((s, []) for s in suites)
+
+ for s in suites:
+ s.ReadStatusFile(variables)
+ s.ReadTestCases(ctx)
+ if len(args) > 0:
+ s.FilterTestCasesByArgs(args)
+ all_tests += s.tests
+ s.FilterTestCasesByStatus(False)
+ test_backup[s] = s.tests
+ analysis_flags = ["--deopt-every-n-times", "%d" % MAX_DEOPT,
+ "--print-deopt-stress"]
+ s.tests = [ t.CopyAddingFlags(analysis_flags) for t in s.tests ]
+ num_tests += len(s.tests)
+ for t in s.tests:
+ t.id = test_id
+ test_id += 1
+
+ if num_tests == 0:
+ print "No tests to run."
+ return 0
+
+ try:
+ print(">>> Collection phase")
+ progress_indicator = progress.PROGRESS_INDICATORS[options.progress]()
+ runner = execution.Runner(suites, progress_indicator, ctx)
+
+ exit_code = runner.Run(options.j)
+ if runner.terminate:
+ return exit_code
+
+ except KeyboardInterrupt:
+ return 1
+
+ print(">>> Analysis phase")
+ num_tests = 0
+ test_id = 0
+ for s in suites:
+ test_results = {}
+ for t in s.tests:
+ for line in t.output.stdout.splitlines():
+ if line.startswith("=== Stress deopt counter: "):
+ test_results[t.path] = MAX_DEOPT - int(line.split(" ")[-1])
+ for t in s.tests:
+ if t.path not in test_results:
+ print "Missing results for %s" % t.path
+ if options.dump_results_file:
+ results_dict = dict((t.path, n) for (t, n) in test_results.iteritems())
+ with file("%s.%d.txt" % (dump_results_file, time.time()), "w") as f:
+ f.write(json.dumps(results_dict))
+
+ # Reset tests and redistribute the prototypes from the collection phase.
+ s.tests = []
+ if options.verbose:
+ print "Test distributions:"
+ for t in test_backup[s]:
+ max_deopt = test_results.get(t.path, 0)
+ if max_deopt == 0:
+ continue
+ n_deopt = CalculateNTests(max_deopt, options)
+ distribution = dist.Distribute(n_deopt, max_deopt)
+ if options.verbose:
+ print "%s %s" % (t.path, distribution)
+ for i in distribution:
+ fuzzing_flags = ["--deopt-every-n-times", "%d" % i]
+ s.tests.append(t.CopyAddingFlags(fuzzing_flags))
+ num_tests += len(s.tests)
+ for t in s.tests:
+ t.id = test_id
+ test_id += 1
+
+ if num_tests == 0:
+ print "No tests to run."
+ return 0
+
+ try:
+ print(">>> Deopt fuzzing phase (%d test cases)" % num_tests)
+ progress_indicator = progress.PROGRESS_INDICATORS[options.progress]()
+ runner = execution.Runner(suites, progress_indicator, ctx)
+
+ exit_code = runner.Run(options.j)
+ if runner.terminate:
+ return exit_code
+
+ except KeyboardInterrupt:
+ return 1
+
+ return exit_code
+
+
+if __name__ == "__main__":
+ sys.exit(Main())
diff --git a/chromium/v8/tools/run-llprof.sh b/chromium/v8/tools/run-llprof.sh
new file mode 100755
index 00000000000..d526170d1fa
--- /dev/null
+++ b/chromium/v8/tools/run-llprof.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright 2012 the V8 project authors. 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
+
+# Ensure that <your CPU clock> / $SAMPLE_EVERY_N_CYCLES < $MAXIMUM_SAMPLE_RATE.
+MAXIMUM_SAMPLE_RATE=10000000
+SAMPLE_EVERY_N_CYCLES=10000
+SAMPLE_RATE_CONFIG_FILE="/proc/sys/kernel/perf_event_max_sample_rate"
+KERNEL_MAP_CONFIG_FILE="/proc/sys/kernel/kptr_restrict"
+
+########## Usage
+
+usage() {
+cat << EOF
+usage: $0 <benchmark_command>
+
+Executes <benchmark_command> under observation by the kernel's "perf" \
+framework, then calls the low level tick processor to analyze the results.
+EOF
+}
+
+if [ $# -eq 0 ] || [ "$1" == "-h" ] || [ "$1" == "--help" ] ; then
+ usage
+ exit 1
+fi
+
+########## Actual script execution
+
+ACTUAL_SAMPLE_RATE=$(cat $SAMPLE_RATE_CONFIG_FILE)
+if [ "$ACTUAL_SAMPLE_RATE" -lt "$MAXIMUM_SAMPLE_RATE" ] ; then
+ echo "Setting appropriate maximum sample rate..."
+ echo $MAXIMUM_SAMPLE_RATE | sudo tee $SAMPLE_RATE_CONFIG_FILE
+fi
+
+ACTUAL_KERNEL_MAP_RESTRICTION=$(cat $KERNEL_MAP_CONFIG_FILE)
+if [ "$ACTUAL_KERNEL_MAP_RESTRICTION" -ne "0" ] ; then
+ echo "Disabling kernel address map restriction..."
+ echo 0 | sudo tee $KERNEL_MAP_CONFIG_FILE
+fi
+
+echo "Running benchmark..."
+perf record -R -e cycles -c $SAMPLE_EVERY_N_CYCLES -f -i $@ --ll-prof
diff --git a/chromium/v8/tools/run-tests.py b/chromium/v8/tools/run-tests.py
new file mode 100755
index 00000000000..48682d4444e
--- /dev/null
+++ b/chromium/v8/tools/run-tests.py
@@ -0,0 +1,392 @@
+#!/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.
+
+
+import multiprocessing
+import optparse
+import os
+from os.path import join
+import shlex
+import subprocess
+import sys
+import time
+
+from testrunner.local import execution
+from testrunner.local import progress
+from testrunner.local import testsuite
+from testrunner.local import utils
+from testrunner.local import verbose
+from testrunner.network import network_execution
+from testrunner.objects import context
+
+
+ARCH_GUESS = utils.DefaultArch()
+DEFAULT_TESTS = ["mjsunit", "cctest", "message", "preparser"]
+TIMEOUT_DEFAULT = 60
+TIMEOUT_SCALEFACTOR = {"debug" : 4,
+ "release" : 1 }
+
+# Use this to run several variants of the tests.
+VARIANT_FLAGS = [[],
+ ["--stress-opt", "--always-opt"],
+ ["--nocrankshaft"]]
+MODE_FLAGS = {
+ "debug" : ["--nobreak-on-abort", "--nodead-code-elimination",
+ "--nofold-constants", "--enable-slow-asserts",
+ "--debug-code", "--verify-heap"],
+ "release" : ["--nobreak-on-abort", "--nodead-code-elimination",
+ "--nofold-constants"]}
+
+SUPPORTED_ARCHS = ["android_arm",
+ "android_ia32",
+ "arm",
+ "ia32",
+ "mipsel",
+ "nacl_ia32",
+ "nacl_x64",
+ "x64"]
+# Double the timeout for these:
+SLOW_ARCHS = ["android_arm",
+ "android_ia32",
+ "arm",
+ "mipsel",
+ "nacl_ia32",
+ "nacl_x64"]
+
+
+def BuildOptions():
+ result = optparse.OptionParser()
+ result.add_option("--arch",
+ help=("The architecture to run tests for, "
+ "'auto' or 'native' for auto-detect"),
+ default="ia32,x64,arm")
+ result.add_option("--arch-and-mode",
+ help="Architecture and mode in the format 'arch.mode'",
+ default=None)
+ result.add_option("--buildbot",
+ help="Adapt to path structure used on buildbots",
+ default=False, action="store_true")
+ result.add_option("--cat", help="Print the source of the tests",
+ default=False, action="store_true")
+ result.add_option("--flaky-tests",
+ help="Regard tests marked as flaky (run|skip|dontcare)",
+ default="dontcare")
+ result.add_option("--command-prefix",
+ help="Prepended to each shell command used to run a test",
+ default="")
+ result.add_option("--download-data", help="Download missing test suite data",
+ default=False, action="store_true")
+ result.add_option("--extra-flags",
+ help="Additional flags to pass to each test command",
+ default="")
+ result.add_option("--isolates", help="Whether to test isolates",
+ default=False, action="store_true")
+ result.add_option("-j", help="The number of parallel tasks to run",
+ default=0, type="int")
+ result.add_option("-m", "--mode",
+ help="The test modes in which to run (comma-separated)",
+ default="release,debug")
+ result.add_option("--no-network", "--nonetwork",
+ help="Don't distribute tests on the network",
+ default=(utils.GuessOS() != "linux"),
+ dest="no_network", action="store_true")
+ result.add_option("--no-presubmit", "--nopresubmit",
+ help='Skip presubmit checks',
+ default=False, dest="no_presubmit", action="store_true")
+ result.add_option("--no-stress", "--nostress",
+ help="Don't run crankshaft --always-opt --stress-op test",
+ default=False, dest="no_stress", action="store_true")
+ result.add_option("--outdir", help="Base directory with compile output",
+ default="out")
+ result.add_option("-p", "--progress",
+ help=("The style of progress indicator"
+ " (verbose, dots, color, mono)"),
+ choices=progress.PROGRESS_INDICATORS.keys(), default="mono")
+ result.add_option("--report", help="Print a summary of the tests to be run",
+ default=False, action="store_true")
+ result.add_option("--shard-count",
+ help="Split testsuites into this number of shards",
+ default=1, type="int")
+ result.add_option("--shard-run",
+ help="Run this shard from the split up tests.",
+ default=1, type="int")
+ result.add_option("--shell", help="DEPRECATED! use --shell-dir", default="")
+ result.add_option("--shell-dir", help="Directory containing executables",
+ default="")
+ result.add_option("--stress-only",
+ help="Only run tests with --always-opt --stress-opt",
+ default=False, action="store_true")
+ result.add_option("--time", help="Print timing information after running",
+ default=False, action="store_true")
+ result.add_option("-t", "--timeout", help="Timeout in seconds",
+ default= -1, type="int")
+ result.add_option("-v", "--verbose", help="Verbose output",
+ default=False, action="store_true")
+ result.add_option("--valgrind", help="Run tests through valgrind",
+ default=False, action="store_true")
+ result.add_option("--warn-unused", help="Report unused rules",
+ default=False, action="store_true")
+ result.add_option("--junitout", help="File name of the JUnit output")
+ result.add_option("--junittestsuite",
+ help="The testsuite name in the JUnit output file",
+ default="v8tests")
+ return result
+
+
+def ProcessOptions(options):
+ global VARIANT_FLAGS
+
+ # Architecture and mode related stuff.
+ if options.arch_and_mode:
+ tokens = options.arch_and_mode.split(".")
+ options.arch = tokens[0]
+ options.mode = tokens[1]
+ options.mode = options.mode.split(",")
+ for mode in options.mode:
+ if not mode.lower() in ["debug", "release"]:
+ print "Unknown mode %s" % mode
+ return False
+ if options.arch in ["auto", "native"]:
+ options.arch = ARCH_GUESS
+ options.arch = options.arch.split(",")
+ for arch in options.arch:
+ if not arch in SUPPORTED_ARCHS:
+ print "Unknown architecture %s" % arch
+ return False
+
+ # Special processing of other options, sorted alphabetically.
+
+ if options.buildbot:
+ # Buildbots run presubmit tests as a separate step.
+ options.no_presubmit = True
+ options.no_network = True
+ if options.command_prefix:
+ print("Specifying --command-prefix disables network distribution, "
+ "running tests locally.")
+ options.no_network = True
+ options.command_prefix = shlex.split(options.command_prefix)
+ options.extra_flags = shlex.split(options.extra_flags)
+ if options.j == 0:
+ options.j = multiprocessing.cpu_count()
+ if options.no_stress:
+ VARIANT_FLAGS = [[], ["--nocrankshaft"]]
+ if not options.shell_dir:
+ if options.shell:
+ print "Warning: --shell is deprecated, use --shell-dir instead."
+ options.shell_dir = os.path.dirname(options.shell)
+ if options.stress_only:
+ VARIANT_FLAGS = [["--stress-opt", "--always-opt"]]
+ if options.valgrind:
+ run_valgrind = os.path.join("tools", "run-valgrind.py")
+ # This is OK for distributed running, so we don't need to set no_network.
+ options.command_prefix = (["python", "-u", run_valgrind] +
+ options.command_prefix)
+ if not options.flaky_tests in ["run", "skip", "dontcare"]:
+ print "Unknown flaky test mode %s" % options.flaky_tests
+ return False
+ return True
+
+
+def ShardTests(tests, shard_count, shard_run):
+ if shard_count < 2:
+ return tests
+ if shard_run < 1 or shard_run > shard_count:
+ print "shard-run not a valid number, should be in [1:shard-count]"
+ print "defaulting back to running all tests"
+ return tests
+ count = 0
+ shard = []
+ for test in tests:
+ if count % shard_count == shard_run - 1:
+ shard.append(test)
+ count += 1
+ return shard
+
+
+def Main():
+ parser = BuildOptions()
+ (options, args) = parser.parse_args()
+ if not ProcessOptions(options):
+ parser.print_help()
+ return 1
+
+ exit_code = 0
+ workspace = os.path.abspath(join(os.path.dirname(sys.argv[0]), ".."))
+ if not options.no_presubmit:
+ print ">>> running presubmit tests"
+ code = subprocess.call(
+ [sys.executable, join(workspace, "tools", "presubmit.py")])
+ exit_code = code
+
+ suite_paths = utils.GetSuitePaths(join(workspace, "test"))
+
+ if len(args) == 0:
+ suite_paths = [ s for s in suite_paths if s in DEFAULT_TESTS ]
+ else:
+ args_suites = set()
+ for arg in args:
+ suite = arg.split(os.path.sep)[0]
+ if not suite in args_suites:
+ args_suites.add(suite)
+ suite_paths = [ s for s in suite_paths if s in args_suites ]
+
+ suites = []
+ for root in suite_paths:
+ suite = testsuite.TestSuite.LoadTestSuite(
+ os.path.join(workspace, "test", root))
+ if suite:
+ suites.append(suite)
+
+ if options.download_data:
+ for s in suites:
+ s.DownloadData()
+
+ for mode in options.mode:
+ for arch in options.arch:
+ code = Execute(arch, mode, args, options, suites, workspace)
+ exit_code = exit_code or code
+ return exit_code
+
+
+def Execute(arch, mode, args, options, suites, workspace):
+ print(">>> Running tests for %s.%s" % (arch, mode))
+
+ shell_dir = options.shell_dir
+ if not shell_dir:
+ if options.buildbot:
+ shell_dir = os.path.join(workspace, options.outdir, mode)
+ mode = mode.lower()
+ else:
+ shell_dir = os.path.join(workspace, options.outdir,
+ "%s.%s" % (arch, mode))
+ shell_dir = os.path.relpath(shell_dir)
+
+ # Populate context object.
+ mode_flags = MODE_FLAGS[mode]
+ timeout = options.timeout
+ if timeout == -1:
+ # Simulators are slow, therefore allow a longer default timeout.
+ if arch in SLOW_ARCHS:
+ timeout = 2 * TIMEOUT_DEFAULT;
+ else:
+ timeout = TIMEOUT_DEFAULT;
+
+ timeout *= TIMEOUT_SCALEFACTOR[mode]
+ ctx = context.Context(arch, mode, shell_dir,
+ mode_flags, options.verbose,
+ timeout, options.isolates,
+ options.command_prefix,
+ options.extra_flags)
+
+ # Find available test suites and read test cases from them.
+ variables = {
+ "mode": mode,
+ "arch": arch,
+ "system": utils.GuessOS(),
+ "isolates": options.isolates,
+ "deopt_fuzzer": False,
+ }
+ all_tests = []
+ num_tests = 0
+ test_id = 0
+ for s in suites:
+ s.ReadStatusFile(variables)
+ s.ReadTestCases(ctx)
+ if len(args) > 0:
+ s.FilterTestCasesByArgs(args)
+ all_tests += s.tests
+ s.FilterTestCasesByStatus(options.warn_unused, options.flaky_tests)
+ if options.cat:
+ verbose.PrintTestSource(s.tests)
+ continue
+ variant_flags = s.VariantFlags() or VARIANT_FLAGS
+ s.tests = [ t.CopyAddingFlags(v) for t in s.tests for v in variant_flags ]
+ s.tests = ShardTests(s.tests, options.shard_count, options.shard_run)
+ num_tests += len(s.tests)
+ for t in s.tests:
+ t.id = test_id
+ test_id += 1
+
+ if options.cat:
+ return 0 # We're done here.
+
+ if options.report:
+ verbose.PrintReport(all_tests)
+
+ if num_tests == 0:
+ print "No tests to run."
+ return 0
+
+ # Run the tests, either locally or distributed on the network.
+ try:
+ start_time = time.time()
+ progress_indicator = progress.PROGRESS_INDICATORS[options.progress]()
+ if options.junitout:
+ progress_indicator = progress.JUnitTestProgressIndicator(
+ progress_indicator, options.junitout, options.junittestsuite)
+
+ run_networked = not options.no_network
+ if not run_networked:
+ print("Network distribution disabled, running tests locally.")
+ elif utils.GuessOS() != "linux":
+ print("Network distribution is only supported on Linux, sorry!")
+ run_networked = False
+ peers = []
+ if run_networked:
+ peers = network_execution.GetPeers()
+ if not peers:
+ print("No connection to distribution server; running tests locally.")
+ run_networked = False
+ elif len(peers) == 1:
+ print("No other peers on the network; running tests locally.")
+ run_networked = False
+ elif num_tests <= 100:
+ print("Less than 100 tests, running them locally.")
+ run_networked = False
+
+ if run_networked:
+ runner = network_execution.NetworkedRunner(suites, progress_indicator,
+ ctx, peers, workspace)
+ else:
+ runner = execution.Runner(suites, progress_indicator, ctx)
+
+ exit_code = runner.Run(options.j)
+ if runner.terminate:
+ return exit_code
+ overall_duration = time.time() - start_time
+ except KeyboardInterrupt:
+ return 1
+
+ if options.time:
+ verbose.PrintTestDurations(suites, overall_duration)
+ return exit_code
+
+
+if __name__ == "__main__":
+ sys.exit(Main())
diff --git a/chromium/v8/tools/run-valgrind.py b/chromium/v8/tools/run-valgrind.py
new file mode 100755
index 00000000000..f25f7a113c5
--- /dev/null
+++ b/chromium/v8/tools/run-valgrind.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# 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.
+
+# Simple wrapper for running valgrind and checking the output on
+# stderr for memory leaks.
+
+import subprocess
+import sys
+import re
+
+VALGRIND_ARGUMENTS = [
+ 'valgrind',
+ '--error-exitcode=1',
+ '--leak-check=full',
+ '--smc-check=all'
+]
+
+# Compute the command line.
+command = VALGRIND_ARGUMENTS + sys.argv[1:]
+
+# Run valgrind.
+process = subprocess.Popen(command, stderr=subprocess.PIPE)
+code = process.wait();
+errors = process.stderr.readlines();
+
+# If valgrind produced an error, we report that to the user.
+if code != 0:
+ sys.stderr.writelines(errors)
+ sys.exit(code)
+
+# Look through the leak details and make sure that we don't
+# have any definitely, indirectly, and possibly lost bytes.
+LEAK_RE = r"(?:definitely|indirectly|possibly) lost: "
+LEAK_LINE_MATCHER = re.compile(LEAK_RE)
+LEAK_OKAY_MATCHER = re.compile(r"lost: 0 bytes in 0 blocks")
+leaks = []
+for line in errors:
+ if LEAK_LINE_MATCHER.search(line):
+ leaks.append(line)
+ if not LEAK_OKAY_MATCHER.search(line):
+ sys.stderr.writelines(errors)
+ sys.exit(1)
+
+# Make sure we found between 2 and 3 leak lines.
+if len(leaks) < 2 or len(leaks) > 3:
+ sys.stderr.writelines(errors)
+ sys.stderr.write('\n\n#### Malformed valgrind output.\n#### Exiting.\n')
+ sys.exit(1)
+
+# No leaks found.
+sys.exit(0)
diff --git a/chromium/v8/tools/splaytree.js b/chromium/v8/tools/splaytree.js
new file mode 100644
index 00000000000..d272a9e1827
--- /dev/null
+++ b/chromium/v8/tools/splaytree.js
@@ -0,0 +1,327 @@
+// 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.
+
+
+/**
+ * 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 minimum key value.
+ */
+SplayTree.prototype.findMin = function() {
+ if (this.isEmpty()) {
+ return null;
+ }
+ var current = this.root_;
+ while (current.left) {
+ current = current.left;
+ }
+ return current;
+};
+
+
+/**
+ * @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 or equal to 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 values of tree's nodes paired
+ * with keys.
+ */
+SplayTree.prototype.exportKeysAndValues = function() {
+ var result = [];
+ this.traverse_(function(node) { result.push([node.key, node.value]); });
+ return result;
+};
+
+
+/**
+ * @return {Array<*>} An array containing all the values of tree's nodes.
+ */
+SplayTree.prototype.exportValues = function() {
+ var result = [];
+ this.traverse_(function(node) { result.push(node.value); });
+ 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;
+};
+
+
+/**
+ * Performs a preorder traversal of the tree.
+ *
+ * @param {function(SplayTree.Node)} f Visitor function.
+ * @private
+ */
+SplayTree.prototype.traverse_ = function(f) {
+ var nodesToVisit = [this.root_];
+ while (nodesToVisit.length > 0) {
+ var node = nodesToVisit.shift();
+ if (node == null) {
+ continue;
+ }
+ f(node);
+ nodesToVisit.push(node.left);
+ nodesToVisit.push(node.right);
+ }
+};
+
+
+/**
+ * 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;
diff --git a/chromium/v8/tools/stats-viewer.py b/chromium/v8/tools/stats-viewer.py
new file mode 100755
index 00000000000..ab8e28767d1
--- /dev/null
+++ b/chromium/v8/tools/stats-viewer.py
@@ -0,0 +1,472 @@
+#!/usr/bin/env python
+#
+# Copyright 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.
+
+
+"""A cross-platform execution counter viewer.
+
+The stats viewer reads counters from a binary file and displays them
+in a window, re-reading and re-displaying with regular intervals.
+"""
+
+import mmap
+import optparse
+import os
+import re
+import struct
+import sys
+import time
+import Tkinter
+
+
+# The interval, in milliseconds, between ui updates
+UPDATE_INTERVAL_MS = 100
+
+
+# Mapping from counter prefix to the formatting to be used for the counter
+COUNTER_LABELS = {"t": "%i ms.", "c": "%i"}
+
+
+# The magic numbers used to check if a file is not a counters file
+COUNTERS_FILE_MAGIC_NUMBER = 0xDEADFACE
+CHROME_COUNTERS_FILE_MAGIC_NUMBER = 0x13131313
+
+
+class StatsViewer(object):
+ """The main class that keeps the data used by the stats viewer."""
+
+ def __init__(self, data_name, name_filter):
+ """Creates a new instance.
+
+ Args:
+ data_name: the name of the file containing the counters.
+ name_filter: The regexp filter to apply to counter names.
+ """
+ self.data_name = data_name
+ self.name_filter = name_filter
+
+ # The handle created by mmap.mmap to the counters file. We need
+ # this to clean it up on exit.
+ self.shared_mmap = None
+
+ # A mapping from counter names to the ui element that displays
+ # them
+ self.ui_counters = {}
+
+ # The counter collection used to access the counters file
+ self.data = None
+
+ # The Tkinter root window object
+ self.root = None
+
+ def Run(self):
+ """The main entry-point to running the stats viewer."""
+ try:
+ self.data = self.MountSharedData()
+ # OpenWindow blocks until the main window is closed
+ self.OpenWindow()
+ finally:
+ self.CleanUp()
+
+ def MountSharedData(self):
+ """Mount the binary counters file as a memory-mapped file. If
+ something goes wrong print an informative message and exit the
+ program."""
+ if not os.path.exists(self.data_name):
+ maps_name = "/proc/%s/maps" % self.data_name
+ if not os.path.exists(maps_name):
+ print "\"%s\" is neither a counter file nor a PID." % self.data_name
+ sys.exit(1)
+ maps_file = open(maps_name, "r")
+ try:
+ self.data_name = None
+ for m in re.finditer(r"/dev/shm/\S*", maps_file.read()):
+ if os.path.exists(m.group(0)):
+ self.data_name = m.group(0)
+ break
+ if self.data_name is None:
+ print "Can't find counter file in maps for PID %s." % self.data_name
+ sys.exit(1)
+ finally:
+ maps_file.close()
+ data_file = open(self.data_name, "r")
+ size = os.fstat(data_file.fileno()).st_size
+ fileno = data_file.fileno()
+ self.shared_mmap = mmap.mmap(fileno, size, access=mmap.ACCESS_READ)
+ data_access = SharedDataAccess(self.shared_mmap)
+ if data_access.IntAt(0) == COUNTERS_FILE_MAGIC_NUMBER:
+ return CounterCollection(data_access)
+ elif data_access.IntAt(0) == CHROME_COUNTERS_FILE_MAGIC_NUMBER:
+ return ChromeCounterCollection(data_access)
+ print "File %s is not stats data." % self.data_name
+ sys.exit(1)
+
+ def CleanUp(self):
+ """Cleans up the memory mapped file if necessary."""
+ if self.shared_mmap:
+ self.shared_mmap.close()
+
+ def UpdateCounters(self):
+ """Read the contents of the memory-mapped file and update the ui if
+ necessary. If the same counters are present in the file as before
+ we just update the existing labels. If any counters have been added
+ or removed we scrap the existing ui and draw a new one.
+ """
+ changed = False
+ counters_in_use = self.data.CountersInUse()
+ if counters_in_use != len(self.ui_counters):
+ self.RefreshCounters()
+ changed = True
+ else:
+ for i in xrange(self.data.CountersInUse()):
+ counter = self.data.Counter(i)
+ name = counter.Name()
+ if name in self.ui_counters:
+ value = counter.Value()
+ ui_counter = self.ui_counters[name]
+ counter_changed = ui_counter.Set(value)
+ changed = (changed or counter_changed)
+ else:
+ self.RefreshCounters()
+ changed = True
+ break
+ if changed:
+ # The title of the window shows the last time the file was
+ # changed.
+ self.UpdateTime()
+ self.ScheduleUpdate()
+
+ def UpdateTime(self):
+ """Update the title of the window with the current time."""
+ self.root.title("Stats Viewer [updated %s]" % time.strftime("%H:%M:%S"))
+
+ def ScheduleUpdate(self):
+ """Schedules the next ui update."""
+ self.root.after(UPDATE_INTERVAL_MS, lambda: self.UpdateCounters())
+
+ def RefreshCounters(self):
+ """Tear down and rebuild the controls in the main window."""
+ counters = self.ComputeCounters()
+ self.RebuildMainWindow(counters)
+
+ def ComputeCounters(self):
+ """Group the counters by the suffix of their name.
+
+ Since the same code-level counter (for instance "X") can result in
+ several variables in the binary counters file that differ only by a
+ two-character prefix (for instance "c:X" and "t:X") counters are
+ grouped by suffix and then displayed with custom formatting
+ depending on their prefix.
+
+ Returns:
+ A mapping from suffixes to a list of counters with that suffix,
+ sorted by prefix.
+ """
+ names = {}
+ for i in xrange(self.data.CountersInUse()):
+ counter = self.data.Counter(i)
+ name = counter.Name()
+ names[name] = counter
+
+ # By sorting the keys we ensure that the prefixes always come in the
+ # same order ("c:" before "t:") which looks more consistent in the
+ # ui.
+ sorted_keys = names.keys()
+ sorted_keys.sort()
+
+ # Group together the names whose suffix after a ':' are the same.
+ groups = {}
+ for name in sorted_keys:
+ counter = names[name]
+ if ":" in name:
+ name = name[name.find(":")+1:]
+ if not name in groups:
+ groups[name] = []
+ groups[name].append(counter)
+
+ return groups
+
+ def RebuildMainWindow(self, groups):
+ """Tear down and rebuild the main window.
+
+ Args:
+ groups: the groups of counters to display
+ """
+ # Remove elements in the current ui
+ self.ui_counters.clear()
+ for child in self.root.children.values():
+ child.destroy()
+
+ # Build new ui
+ index = 0
+ sorted_groups = groups.keys()
+ sorted_groups.sort()
+ for counter_name in sorted_groups:
+ counter_objs = groups[counter_name]
+ if self.name_filter.match(counter_name):
+ name = Tkinter.Label(self.root, width=50, anchor=Tkinter.W,
+ text=counter_name)
+ name.grid(row=index, column=0, padx=1, pady=1)
+ count = len(counter_objs)
+ for i in xrange(count):
+ counter = counter_objs[i]
+ name = counter.Name()
+ var = Tkinter.StringVar()
+ if self.name_filter.match(name):
+ value = Tkinter.Label(self.root, width=15, anchor=Tkinter.W,
+ textvariable=var)
+ value.grid(row=index, column=(1 + i), padx=1, pady=1)
+
+ # If we know how to interpret the prefix of this counter then
+ # add an appropriate formatting to the variable
+ if (":" in name) and (name[0] in COUNTER_LABELS):
+ format = COUNTER_LABELS[name[0]]
+ else:
+ format = "%i"
+ ui_counter = UiCounter(var, format)
+ self.ui_counters[name] = ui_counter
+ ui_counter.Set(counter.Value())
+ index += 1
+ self.root.update()
+
+ def OpenWindow(self):
+ """Create and display the root window."""
+ self.root = Tkinter.Tk()
+
+ # Tkinter is no good at resizing so we disable it
+ self.root.resizable(width=False, height=False)
+ self.RefreshCounters()
+ self.ScheduleUpdate()
+ self.root.mainloop()
+
+
+class UiCounter(object):
+ """A counter in the ui."""
+
+ def __init__(self, var, format):
+ """Creates a new ui counter.
+
+ Args:
+ var: the Tkinter string variable for updating the ui
+ format: the format string used to format this counter
+ """
+ self.var = var
+ self.format = format
+ self.last_value = None
+
+ def Set(self, value):
+ """Updates the ui for this counter.
+
+ Args:
+ value: The value to display
+
+ Returns:
+ True if the value had changed, otherwise False. The first call
+ always returns True.
+ """
+ if value == self.last_value:
+ return False
+ else:
+ self.last_value = value
+ self.var.set(self.format % value)
+ return True
+
+
+class SharedDataAccess(object):
+ """A utility class for reading data from the memory-mapped binary
+ counters file."""
+
+ def __init__(self, data):
+ """Create a new instance.
+
+ Args:
+ data: A handle to the memory-mapped file, as returned by mmap.mmap.
+ """
+ self.data = data
+
+ def ByteAt(self, index):
+ """Return the (unsigned) byte at the specified byte index."""
+ return ord(self.CharAt(index))
+
+ def IntAt(self, index):
+ """Return the little-endian 32-byte int at the specified byte index."""
+ word_str = self.data[index:index+4]
+ result, = struct.unpack("I", word_str)
+ return result
+
+ def CharAt(self, index):
+ """Return the ascii character at the specified byte index."""
+ return self.data[index]
+
+
+class Counter(object):
+ """A pointer to a single counter withing a binary counters file."""
+
+ def __init__(self, data, offset):
+ """Create a new instance.
+
+ Args:
+ data: the shared data access object containing the counter
+ offset: the byte offset of the start of this counter
+ """
+ self.data = data
+ self.offset = offset
+
+ def Value(self):
+ """Return the integer value of this counter."""
+ return self.data.IntAt(self.offset)
+
+ def Name(self):
+ """Return the ascii name of this counter."""
+ result = ""
+ index = self.offset + 4
+ current = self.data.ByteAt(index)
+ while current:
+ result += chr(current)
+ index += 1
+ current = self.data.ByteAt(index)
+ return result
+
+
+class CounterCollection(object):
+ """An overlay over a counters file that provides access to the
+ individual counters contained in the file."""
+
+ def __init__(self, data):
+ """Create a new instance.
+
+ Args:
+ data: the shared data access object
+ """
+ self.data = data
+ self.max_counters = data.IntAt(4)
+ self.max_name_size = data.IntAt(8)
+
+ def CountersInUse(self):
+ """Return the number of counters in active use."""
+ return self.data.IntAt(12)
+
+ def Counter(self, index):
+ """Return the index'th counter."""
+ return Counter(self.data, 16 + index * self.CounterSize())
+
+ def CounterSize(self):
+ """Return the size of a single counter."""
+ return 4 + self.max_name_size
+
+
+class ChromeCounter(object):
+ """A pointer to a single counter withing a binary counters file."""
+
+ def __init__(self, data, name_offset, value_offset):
+ """Create a new instance.
+
+ Args:
+ data: the shared data access object containing the counter
+ name_offset: the byte offset of the start of this counter's name
+ value_offset: the byte offset of the start of this counter's value
+ """
+ self.data = data
+ self.name_offset = name_offset
+ self.value_offset = value_offset
+
+ def Value(self):
+ """Return the integer value of this counter."""
+ return self.data.IntAt(self.value_offset)
+
+ def Name(self):
+ """Return the ascii name of this counter."""
+ result = ""
+ index = self.name_offset
+ current = self.data.ByteAt(index)
+ while current:
+ result += chr(current)
+ index += 1
+ current = self.data.ByteAt(index)
+ return result
+
+
+class ChromeCounterCollection(object):
+ """An overlay over a counters file that provides access to the
+ individual counters contained in the file."""
+
+ _HEADER_SIZE = 4 * 4
+ _COUNTER_NAME_SIZE = 64
+ _THREAD_NAME_SIZE = 32
+
+ def __init__(self, data):
+ """Create a new instance.
+
+ Args:
+ data: the shared data access object
+ """
+ self.data = data
+ self.max_counters = data.IntAt(8)
+ self.max_threads = data.IntAt(12)
+ self.counter_names_offset = \
+ self._HEADER_SIZE + self.max_threads * (self._THREAD_NAME_SIZE + 2 * 4)
+ self.counter_values_offset = \
+ self.counter_names_offset + self.max_counters * self._COUNTER_NAME_SIZE
+
+ def CountersInUse(self):
+ """Return the number of counters in active use."""
+ for i in xrange(self.max_counters):
+ name_offset = self.counter_names_offset + i * self._COUNTER_NAME_SIZE
+ if self.data.ByteAt(name_offset) == 0:
+ return i
+ return self.max_counters
+
+ def Counter(self, i):
+ """Return the i'th counter."""
+ name_offset = self.counter_names_offset + i * self._COUNTER_NAME_SIZE
+ value_offset = self.counter_values_offset + i * self.max_threads * 4
+ return ChromeCounter(self.data, name_offset, value_offset)
+
+
+def Main(data_file, name_filter):
+ """Run the stats counter.
+
+ Args:
+ data_file: The counters file to monitor.
+ name_filter: The regexp filter to apply to counter names.
+ """
+ StatsViewer(data_file, name_filter).Run()
+
+
+if __name__ == "__main__":
+ parser = optparse.OptionParser("usage: %prog [--filter=re] "
+ "<stats data>|<test_shell pid>")
+ parser.add_option("--filter",
+ default=".*",
+ help=("regexp filter for counter names "
+ "[default: %default]"))
+ (options, args) = parser.parse_args()
+ if len(args) != 1:
+ parser.print_help()
+ sys.exit(1)
+ Main(args[0], re.compile(options.filter))
diff --git a/chromium/v8/tools/status-file-converter.py b/chromium/v8/tools/status-file-converter.py
new file mode 100755
index 00000000000..ba063ee8c74
--- /dev/null
+++ b/chromium/v8/tools/status-file-converter.py
@@ -0,0 +1,39 @@
+#!/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.
+
+
+import sys
+from testrunner.local import old_statusfile
+
+if len(sys.argv) != 2:
+ print "Usage: %s foo.status" % sys.argv[0]
+ print "Will read foo.status and print the converted version to stdout."
+ sys.exit(1)
+
+print old_statusfile.ConvertNotation(sys.argv[1]).GetOutput()
diff --git a/chromium/v8/tools/test-server.py b/chromium/v8/tools/test-server.py
new file mode 100755
index 00000000000..ab927de75f4
--- /dev/null
+++ b/chromium/v8/tools/test-server.py
@@ -0,0 +1,215 @@
+#!/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.
+
+
+import os
+import subprocess
+import sys
+
+
+PIDFILE = "/tmp/v8-distributed-testing-server.pid"
+ROOT = os.path.abspath(os.path.dirname(sys.argv[0]))
+
+
+def _PrintUsage():
+ print("""Usage: python %s COMMAND
+
+Where COMMAND can be any of:
+ start Starts the server. Forks to the background.
+ stop Stops the server.
+ restart Stops, then restarts the server.
+ setup Creates or updates the environment for the server to run.
+ update Alias for "setup".
+ trust <keyfile> Adds the given public key to the list of trusted keys.
+ help Displays this help text.
+ """ % sys.argv[0])
+
+
+def _IsDaemonRunning():
+ return os.path.exists(PIDFILE)
+
+
+def _Cmd(cmd):
+ code = subprocess.call(cmd, shell=True)
+ if code != 0:
+ print("Command '%s' returned error code %d" % (cmd, code))
+ sys.exit(code)
+
+
+def Update():
+ # Create directory for private data storage.
+ data_dir = os.path.join(ROOT, "data")
+ if not os.path.exists(data_dir):
+ os.makedirs(data_dir)
+
+ # Create directory for trusted public keys of peers (and self).
+ trusted_dir = os.path.join(ROOT, "trusted")
+ if not os.path.exists(trusted_dir):
+ os.makedirs(trusted_dir)
+
+ # Install UltraJSON. It is much faster than Python's builtin json.
+ try:
+ import ujson #@UnusedImport
+ except ImportError:
+ # Install pip if it doesn't exist.
+ code = subprocess.call("which pip > /dev/null", shell=True)
+ if code != 0:
+ apt_get_code = subprocess.call("which apt-get > /dev/null", shell=True)
+ if apt_get_code == 0:
+ print("Installing pip...")
+ _Cmd("sudo apt-get install python-pip")
+ else:
+ print("Please install pip on your machine. You can get it at: "
+ "http://www.pip-installer.org/en/latest/installing.html "
+ "or via your distro's package manager.")
+ sys.exit(1)
+ print("Using pip to install UltraJSON...")
+ _Cmd("sudo pip install ujson")
+
+ # Make sure we have a key pair for signing binaries.
+ privkeyfile = os.path.expanduser("~/.ssh/v8_dtest")
+ if not os.path.exists(privkeyfile):
+ _Cmd("ssh-keygen -t rsa -f %s -N '' -q" % privkeyfile)
+ fingerprint = subprocess.check_output("ssh-keygen -lf %s" % privkeyfile,
+ shell=True)
+ fingerprint = fingerprint.split(" ")[1].replace(":", "")[:16]
+ pubkeyfile = os.path.join(trusted_dir, "%s.pem" % fingerprint)
+ if (not os.path.exists(pubkeyfile) or
+ os.path.getmtime(pubkeyfile) < os.path.getmtime(privkeyfile)):
+ _Cmd("openssl rsa -in %s -out %s -pubout" % (privkeyfile, pubkeyfile))
+ with open(pubkeyfile, "a") as f:
+ f.write(fingerprint + "\n")
+ datafile = os.path.join(data_dir, "mypubkey")
+ with open(datafile, "w") as f:
+ f.write(fingerprint + "\n")
+
+ # Check out or update the server implementation in the current directory.
+ testrunner_dir = os.path.join(ROOT, "testrunner")
+ if os.path.exists(os.path.join(testrunner_dir, "server/daemon.py")):
+ _Cmd("cd %s; svn up" % testrunner_dir)
+ else:
+ path = ("http://v8.googlecode.com/svn/branches/bleeding_edge/"
+ "tools/testrunner")
+ _Cmd("svn checkout --force %s %s" % (path, testrunner_dir))
+
+ # Update this very script.
+ path = ("http://v8.googlecode.com/svn/branches/bleeding_edge/"
+ "tools/test-server.py")
+ scriptname = os.path.abspath(sys.argv[0])
+ _Cmd("svn cat %s > %s" % (path, scriptname))
+
+ # Check out or update V8.
+ v8_dir = os.path.join(ROOT, "v8")
+ if os.path.exists(v8_dir):
+ _Cmd("cd %s; git fetch" % v8_dir)
+ else:
+ _Cmd("git clone git://github.com/v8/v8.git %s" % v8_dir)
+
+ print("Finished.")
+
+
+# Handle "setup" here, because when executing that we can't import anything
+# else yet.
+if __name__ == "__main__" and len(sys.argv) == 2:
+ if sys.argv[1] in ("setup", "update"):
+ if _IsDaemonRunning():
+ print("Please stop the server before updating. Exiting.")
+ sys.exit(1)
+ Update()
+ sys.exit(0)
+ # Other parameters are handled below.
+
+
+#==========================================================
+# At this point we can assume that the implementation is available,
+# so we can import it.
+try:
+ from testrunner.server import constants
+ from testrunner.server import local_handler
+ from testrunner.server import main
+except Exception, e:
+ print(e)
+ print("Failed to import implementation. Have you run 'setup'?")
+ sys.exit(1)
+
+
+def _StartDaemon(daemon):
+ if not os.path.isdir(os.path.join(ROOT, "v8")):
+ print("No 'v8' working directory found. Have you run 'setup'?")
+ sys.exit(1)
+ daemon.start()
+
+
+if __name__ == "__main__":
+ if len(sys.argv) == 2:
+ arg = sys.argv[1]
+ if arg == "start":
+ daemon = main.Server(PIDFILE, ROOT)
+ _StartDaemon(daemon)
+ elif arg == "stop":
+ daemon = main.Server(PIDFILE, ROOT)
+ daemon.stop()
+ elif arg == "restart":
+ daemon = main.Server(PIDFILE, ROOT)
+ daemon.stop()
+ _StartDaemon(daemon)
+ elif arg in ("help", "-h", "--help"):
+ _PrintUsage()
+ elif arg == "status":
+ if not _IsDaemonRunning():
+ print("Server not running.")
+ else:
+ print(local_handler.LocalQuery([constants.REQUEST_STATUS]))
+ else:
+ print("Unknown command")
+ _PrintUsage()
+ sys.exit(2)
+ elif len(sys.argv) == 3:
+ arg = sys.argv[1]
+ if arg == "approve":
+ filename = sys.argv[2]
+ if not os.path.exists(filename):
+ print("%s does not exist.")
+ sys.exit(1)
+ filename = os.path.abspath(filename)
+ if _IsDaemonRunning():
+ response = local_handler.LocalQuery([constants.ADD_TRUSTED, filename])
+ else:
+ daemon = main.Server(PIDFILE, ROOT)
+ response = daemon.CopyToTrusted(filename)
+ print("Added certificate %s to trusted certificates." % response)
+ else:
+ print("Unknown command")
+ _PrintUsage()
+ sys.exit(2)
+ else:
+ print("Unknown command")
+ _PrintUsage()
+ sys.exit(2)
+ sys.exit(0)
diff --git a/chromium/v8/tools/testrunner/README b/chromium/v8/tools/testrunner/README
new file mode 100644
index 00000000000..8f0c01f52ab
--- /dev/null
+++ b/chromium/v8/tools/testrunner/README
@@ -0,0 +1,174 @@
+Test suite runner for V8, including support for distributed running.
+====================================================================
+
+
+Local usage instructions:
+=========================
+
+Run the main script with --help to get detailed usage instructions:
+
+$ tools/run-tests.py --help
+
+The interface is mostly the same as it was for the old test runner.
+You'll likely want something like this:
+
+$ tools/run-tests.py --nonetwork --arch ia32 --mode release
+
+--nonetwork is the default on Mac and Windows. If you don't specify --arch
+and/or --mode, all available values will be used and run in turn (e.g.,
+omitting --mode from the above example will run ia32 in both Release and Debug
+modes).
+
+
+Networked usage instructions:
+=============================
+
+Networked running is only supported on Linux currently. Make sure that all
+machines participating in the cluster are binary-compatible (e.g. mixing
+Ubuntu Lucid and Precise doesn't work).
+
+Setup:
+------
+
+1.) Copy tools/test-server.py to a new empty directory anywhere on your hard
+ drive (preferably not inside your V8 checkout just to keep things clean).
+ Please do create a copy, not just a symlink.
+
+2.) Navigate to the new directory and let the server setup itself:
+
+$ ./test-server.py setup
+
+ This will install PIP and UltraJSON, create a V8 working directory, and
+ generate a keypair.
+
+3.) Swap public keys with someone who's already part of the networked cluster.
+
+$ cp trusted/`cat data/mypubkey`.pem /where/peers/can/see/it/myname.pem
+$ ./test-server.py approve /wherever/they/put/it/yourname.pem
+
+
+Usage:
+------
+
+1.) Start your server:
+
+$ ./test-server.py start
+
+2.) (Optionally) inspect the server's status:
+
+$ ./test-server.py status
+
+3.) From your regular V8 working directory, run tests:
+
+$ tool/run-tests.py --arch ia32 --mode debug
+
+4.) (Optionally) enjoy the speeeeeeeeeeeeeeeed
+
+
+Architecture overview:
+======================
+
+Code organization:
+------------------
+
+This section is written from the point of view of the tools/ directory.
+
+./run-tests.py:
+ Main script. Parses command-line options and drives the test execution
+ procedure from a high level. Imports the actual implementation of all
+ steps from the testrunner/ directory.
+
+./test-server.py:
+ Interface to interact with the server. Contains code to setup the server's
+ working environment and can start and stop server daemon processes.
+ Imports some stuff from the testrunner/server/ directory.
+
+./testrunner/local/*:
+ Implementation needed to run tests locally. Used by run-tests.py. Inspired by
+ (and partly copied verbatim from) the original test.py script.
+
+./testrunner/local/old_statusfile.py:
+ Provides functionality to read an old-style <testsuite>.status file and
+ convert it to new-style syntax. This can be removed once the new-style
+ syntax becomes authoritative (and old-style syntax is no longer supported).
+ ./status-file-converter.py provides a stand-alone interface to this.
+
+./testrunner/objects/*:
+ A bunch of data container classes, used by the scripts in the various other
+ directories; serializable for transmission over the network.
+
+./testrunner/network/*:
+ Equivalents and extensions of some of the functionality in ./testrunner/local/
+ as required when dispatching tests to peers on the network.
+
+./testrunner/network/network_execution.py:
+ Drop-in replacement for ./testrunner/local/execution that distributes
+ test jobs to network peers instead of running them locally.
+
+./testrunner/network/endpoint.py:
+ Receiving end of a network distributed job, uses the implementation
+ in ./testrunner/local/execution.py for actually running the tests.
+
+./testrunner/server/*:
+ Implementation of the daemon that accepts and runs test execution jobs from
+ peers on the network. Should ideally have no dependencies on any of the other
+ directories, but that turned out to be impractical, so there are a few
+ exceptions.
+
+./testrunner/server/compression.py:
+ Defines a wrapper around Python TCP sockets that provides JSON based
+ serialization, gzip based compression, and ensures message completeness.
+
+
+Networking architecture:
+------------------------
+
+The distribution stuff is designed to be a layer between deciding which tests
+to run on the one side, and actually running them on the other. The frontend
+that the user interacts with is the same for local and networked execution,
+and the actual test execution and result gathering code is the same too.
+
+The server daemon starts four separate servers, each listening on another port:
+- "Local": Communication with a run-tests.py script running on the same host.
+ The test driving script e.g. needs to ask for available peers. It then talks
+ to those peers directly (one of them will be the locally running server).
+- "Work": Listens for test job requests from run-tests.py scripts on the network
+ (including localhost). Accepts an arbitrary number of connections at the
+ same time, but only works on them in a serialized fashion.
+- "Status": Used for communication with other servers on the network, e.g. for
+ exchanging trusted public keys to create the transitive trust closure.
+- "Discovery": Used to detect presence of other peers on the network.
+ In contrast to the other three, this uses UDP (as opposed to TCP).
+
+
+Give us a diagram! We love diagrams!
+------------------------------------
+ .
+ Machine A . Machine B
+ .
++------------------------------+ .
+| run-tests.py | .
+| with flag: | .
+|--nonetwork --network | .
+| | / | | .
+| | / | | .
+| v / v | .
+|BACKEND / distribution | .
++--------- / --------| \ ------+ .
+ / | \_____________________
+ / | . \
+ / | . \
++----- v ----------- v --------+ . +---- v -----------------------+
+| LocalHandler | WorkHandler | . | WorkHandler | LocalHandler |
+| | | | . | | | |
+| | v | . | v | |
+| | BACKEND | . | BACKEND | |
+|------------- +---------------| . |---------------+--------------|
+| Discovery | StatusHandler <----------> StatusHandler | Discovery |
++---- ^ -----------------------+ . +-------------------- ^ -------+
+ | . |
+ +---------------------------------------------------------+
+
+Note that the three occurrences of "BACKEND" are the same code
+(testrunner/local/execution.py and its imports), but running from three
+distinct directories (and on two different machines).
diff --git a/chromium/v8/tools/testrunner/__init__.py b/chromium/v8/tools/testrunner/__init__.py
new file mode 100644
index 00000000000..202a262709c
--- /dev/null
+++ b/chromium/v8/tools/testrunner/__init__.py
@@ -0,0 +1,26 @@
+# Copyright 2012 the V8 project authors. 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.
diff --git a/chromium/v8/tools/testrunner/local/__init__.py b/chromium/v8/tools/testrunner/local/__init__.py
new file mode 100644
index 00000000000..202a262709c
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/__init__.py
@@ -0,0 +1,26 @@
+# Copyright 2012 the V8 project authors. 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.
diff --git a/chromium/v8/tools/testrunner/local/commands.py b/chromium/v8/tools/testrunner/local/commands.py
new file mode 100644
index 00000000000..01f170dc872
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/commands.py
@@ -0,0 +1,153 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import os
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+
+from ..local import utils
+from ..objects import output
+
+
+def KillProcessWithID(pid):
+ if utils.IsWindows():
+ os.popen('taskkill /T /F /PID %d' % pid)
+ else:
+ os.kill(pid, signal.SIGTERM)
+
+
+MAX_SLEEP_TIME = 0.1
+INITIAL_SLEEP_TIME = 0.0001
+SLEEP_TIME_FACTOR = 1.25
+
+SEM_INVALID_VALUE = -1
+SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h
+
+
+def Win32SetErrorMode(mode):
+ prev_error_mode = SEM_INVALID_VALUE
+ try:
+ import ctypes
+ prev_error_mode = \
+ ctypes.windll.kernel32.SetErrorMode(mode) #@UndefinedVariable
+ except ImportError:
+ pass
+ return prev_error_mode
+
+
+def RunProcess(verbose, timeout, args, **rest):
+ if verbose: print "#", " ".join(args)
+ popen_args = args
+ prev_error_mode = SEM_INVALID_VALUE
+ if utils.IsWindows():
+ popen_args = subprocess.list2cmdline(args)
+ # Try to change the error mode to avoid dialogs on fatal errors. Don't
+ # touch any existing error mode flags by merging the existing error mode.
+ # See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx.
+ error_mode = SEM_NOGPFAULTERRORBOX
+ prev_error_mode = Win32SetErrorMode(error_mode)
+ Win32SetErrorMode(error_mode | prev_error_mode)
+ process = subprocess.Popen(
+ shell=utils.IsWindows(),
+ args=popen_args,
+ **rest
+ )
+ if (utils.IsWindows() and prev_error_mode != SEM_INVALID_VALUE):
+ Win32SetErrorMode(prev_error_mode)
+ # Compute the end time - if the process crosses this limit we
+ # consider it timed out.
+ if timeout is None: end_time = None
+ else: end_time = time.time() + timeout
+ timed_out = False
+ # Repeatedly check the exit code from the process in a
+ # loop and keep track of whether or not it times out.
+ exit_code = None
+ sleep_time = INITIAL_SLEEP_TIME
+ try:
+ while exit_code is None:
+ if (not end_time is None) and (time.time() >= end_time):
+ # Kill the process and wait for it to exit.
+ KillProcessWithID(process.pid)
+ exit_code = process.wait()
+ timed_out = True
+ else:
+ exit_code = process.poll()
+ time.sleep(sleep_time)
+ sleep_time = sleep_time * SLEEP_TIME_FACTOR
+ if sleep_time > MAX_SLEEP_TIME:
+ sleep_time = MAX_SLEEP_TIME
+ return (exit_code, timed_out)
+ except KeyboardInterrupt:
+ raise
+
+
+def PrintError(string):
+ sys.stderr.write(string)
+ sys.stderr.write("\n")
+
+
+def CheckedUnlink(name):
+ # On Windows, when run with -jN in parallel processes,
+ # OS often fails to unlink the temp file. Not sure why.
+ # Need to retry.
+ # Idea from https://bugs.webkit.org/attachment.cgi?id=75982&action=prettypatch
+ retry_count = 0
+ while retry_count < 30:
+ try:
+ os.unlink(name)
+ return
+ except OSError, e:
+ retry_count += 1
+ time.sleep(retry_count * 0.1)
+ PrintError("os.unlink() " + str(e))
+
+
+def Execute(args, verbose=False, timeout=None):
+ args = [ c for c in args if c != "" ]
+ (fd_out, outname) = tempfile.mkstemp()
+ (fd_err, errname) = tempfile.mkstemp()
+ try:
+ (exit_code, timed_out) = RunProcess(
+ verbose,
+ timeout,
+ args=args,
+ stdout=fd_out,
+ stderr=fd_err
+ )
+ except:
+ raise
+ os.close(fd_out)
+ os.close(fd_err)
+ out = file(outname).read()
+ errors = file(errname).read()
+ CheckedUnlink(outname)
+ CheckedUnlink(errname)
+ return output.Output(exit_code, timed_out, out, errors)
diff --git a/chromium/v8/tools/testrunner/local/execution.py b/chromium/v8/tools/testrunner/local/execution.py
new file mode 100644
index 00000000000..4453c08451f
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/execution.py
@@ -0,0 +1,183 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import multiprocessing
+import os
+import threading
+import time
+
+from . import commands
+from . import utils
+
+
+BREAK_NOW = -1
+EXCEPTION = -2
+
+
+class Job(object):
+ def __init__(self, command, dep_command, test_id, timeout, verbose):
+ self.command = command
+ self.dep_command = dep_command
+ self.id = test_id
+ self.timeout = timeout
+ self.verbose = verbose
+
+
+def RunTest(job):
+ try:
+ start_time = time.time()
+ if job.dep_command is not None:
+ dep_output = commands.Execute(job.dep_command, job.verbose, job.timeout)
+ # TODO(jkummerow): We approximate the test suite specific function
+ # IsFailureOutput() by just checking the exit code here. Currently
+ # only cctests define dependencies, for which this simplification is
+ # correct.
+ if dep_output.exit_code != 0:
+ return (job.id, dep_output, time.time() - start_time)
+ output = commands.Execute(job.command, job.verbose, job.timeout)
+ return (job.id, output, time.time() - start_time)
+ except KeyboardInterrupt:
+ return (-1, BREAK_NOW, 0)
+ except Exception, e:
+ print(">>> EXCEPTION: %s" % e)
+ return (-1, EXCEPTION, 0)
+
+
+class Runner(object):
+
+ def __init__(self, suites, progress_indicator, context):
+ self.tests = [ t for s in suites for t in s.tests ]
+ self._CommonInit(len(self.tests), progress_indicator, context)
+
+ def _CommonInit(self, num_tests, progress_indicator, context):
+ self.indicator = progress_indicator
+ progress_indicator.runner = self
+ self.context = context
+ self.succeeded = 0
+ self.total = num_tests
+ self.remaining = num_tests
+ self.failed = []
+ self.crashed = 0
+ self.terminate = False
+ self.lock = threading.Lock()
+
+ def Run(self, jobs):
+ self.indicator.Starting()
+ self._RunInternal(jobs)
+ self.indicator.Done()
+ if self.failed or self.remaining:
+ return 1
+ return 0
+
+ def _RunInternal(self, jobs):
+ pool = multiprocessing.Pool(processes=jobs)
+ test_map = {}
+ queue = []
+ queued_exception = None
+ for test in self.tests:
+ assert test.id >= 0
+ test_map[test.id] = test
+ try:
+ command = self.GetCommand(test)
+ except Exception, e:
+ # If this failed, save the exception and re-raise it later (after
+ # all other tests have had a chance to run).
+ queued_exception = e
+ continue
+ timeout = self.context.timeout
+ if ("--stress-opt" in test.flags or
+ "--stress-opt" in self.context.mode_flags or
+ "--stress-opt" in self.context.extra_flags):
+ timeout *= 4
+ if test.dependency is not None:
+ dep_command = [ c.replace(test.path, test.dependency) for c in command ]
+ else:
+ dep_command = None
+ job = Job(command, dep_command, test.id, timeout, self.context.verbose)
+ queue.append(job)
+ try:
+ kChunkSize = 1
+ it = pool.imap_unordered(RunTest, queue, kChunkSize)
+ for result in it:
+ test_id = result[0]
+ if test_id < 0:
+ if result[1] == BREAK_NOW:
+ self.terminate = True
+ else:
+ continue
+ if self.terminate:
+ pool.terminate()
+ pool.join()
+ raise BreakNowException("User pressed Ctrl+C or IO went wrong")
+ test = test_map[test_id]
+ self.indicator.AboutToRun(test)
+ test.output = result[1]
+ test.duration = result[2]
+ has_unexpected_output = test.suite.HasUnexpectedOutput(test)
+ if has_unexpected_output:
+ self.failed.append(test)
+ if test.output.HasCrashed():
+ self.crashed += 1
+ else:
+ self.succeeded += 1
+ self.remaining -= 1
+ self.indicator.HasRun(test, has_unexpected_output)
+ except KeyboardInterrupt:
+ pool.terminate()
+ pool.join()
+ raise
+ except Exception, e:
+ print("Exception: %s" % e)
+ pool.terminate()
+ pool.join()
+ raise
+ if queued_exception:
+ raise queued_exception
+ return
+
+
+ def GetCommand(self, test):
+ d8testflag = []
+ shell = test.suite.shell()
+ if shell == "d8":
+ d8testflag = ["--test"]
+ if utils.IsWindows():
+ shell += ".exe"
+ cmd = (self.context.command_prefix +
+ [os.path.abspath(os.path.join(self.context.shell_dir, shell))] +
+ d8testflag +
+ test.suite.GetFlagsForTestCase(test, self.context) +
+ self.context.extra_flags)
+ return cmd
+
+
+class BreakNowException(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
diff --git a/chromium/v8/tools/testrunner/local/junit_output.py b/chromium/v8/tools/testrunner/local/junit_output.py
new file mode 100644
index 00000000000..437adb17893
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/junit_output.py
@@ -0,0 +1,49 @@
+# Copyright 2013 the V8 project authors. 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.
+
+
+import xml.etree.ElementTree as xml
+
+
+class JUnitTestOutput:
+ def __init__(self, test_suite_name):
+ self.root = xml.Element("testsuite")
+ self.root.attrib["name"] = test_suite_name
+
+ def HasRunTest(self, test_name, test_duration, test_failure):
+ testCaseElement = xml.Element("testcase")
+ testCaseElement.attrib["name"] = " ".join(test_name)
+ testCaseElement.attrib["time"] = str(round(test_duration, 3))
+ if len(test_failure):
+ failureElement = xml.Element("failure")
+ failureElement.text = test_failure
+ testCaseElement.append(failureElement)
+ self.root.append(testCaseElement)
+
+ def FinishAndWrite(self, file):
+ xml.ElementTree(self.root).write(file, "UTF-8")
+
diff --git a/chromium/v8/tools/testrunner/local/old_statusfile.py b/chromium/v8/tools/testrunner/local/old_statusfile.py
new file mode 100644
index 00000000000..d634e3ec955
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/old_statusfile.py
@@ -0,0 +1,462 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import cStringIO
+import re
+
+# These outcomes can occur in a TestCase's outcomes list:
+SKIP = 'SKIP'
+FAIL = 'FAIL'
+PASS = 'PASS'
+OKAY = 'OKAY'
+TIMEOUT = 'TIMEOUT'
+CRASH = 'CRASH'
+SLOW = 'SLOW'
+FLAKY = 'FLAKY'
+# These are just for the status files and are mapped below in DEFS:
+FAIL_OK = 'FAIL_OK'
+PASS_OR_FAIL = 'PASS_OR_FAIL'
+
+KEYWORDS = {SKIP: SKIP,
+ FAIL: FAIL,
+ PASS: PASS,
+ OKAY: OKAY,
+ TIMEOUT: TIMEOUT,
+ CRASH: CRASH,
+ SLOW: SLOW,
+ FLAKY: FLAKY,
+ FAIL_OK: FAIL_OK,
+ PASS_OR_FAIL: PASS_OR_FAIL}
+
+class Expression(object):
+ pass
+
+
+class Constant(Expression):
+
+ def __init__(self, value):
+ self.value = value
+
+ def Evaluate(self, env, defs):
+ return self.value
+
+
+class Variable(Expression):
+
+ def __init__(self, name):
+ self.name = name
+
+ def GetOutcomes(self, env, defs):
+ if self.name in env: return set([env[self.name]])
+ else: return set([])
+
+ def Evaluate(self, env, defs):
+ return env[self.name]
+
+ def __str__(self):
+ return self.name
+
+ def string(self, logical):
+ return self.__str__()
+
+
+class Outcome(Expression):
+
+ def __init__(self, name):
+ self.name = name
+
+ def GetOutcomes(self, env, defs):
+ if self.name in defs:
+ return defs[self.name].GetOutcomes(env, defs)
+ else:
+ return set([self.name])
+
+ def __str__(self):
+ if self.name in KEYWORDS:
+ return "%s" % KEYWORDS[self.name]
+ return "'%s'" % self.name
+
+ def string(self, logical):
+ if logical:
+ return "%s" % self.name
+ return self.__str__()
+
+
+class Operation(Expression):
+
+ def __init__(self, left, op, right):
+ self.left = left
+ self.op = op
+ self.right = right
+
+ def Evaluate(self, env, defs):
+ if self.op == '||' or self.op == ',':
+ return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs)
+ elif self.op == 'if':
+ return False
+ elif self.op == '==':
+ return not self.left.GetOutcomes(env, defs).isdisjoint(self.right.GetOutcomes(env, defs))
+ elif self.op == '!=':
+ return self.left.GetOutcomes(env, defs).isdisjoint(self.right.GetOutcomes(env, defs))
+ else:
+ assert self.op == '&&'
+ return self.left.Evaluate(env, defs) and self.right.Evaluate(env, defs)
+
+ def GetOutcomes(self, env, defs):
+ if self.op == '||' or self.op == ',':
+ return self.left.GetOutcomes(env, defs) | self.right.GetOutcomes(env, defs)
+ elif self.op == 'if':
+ if self.right.Evaluate(env, defs): return self.left.GetOutcomes(env, defs)
+ else: return set([])
+ else:
+ assert self.op == '&&'
+ return self.left.GetOutcomes(env, defs) & self.right.GetOutcomes(env, defs)
+
+ def __str__(self):
+ return self.string(False)
+
+ def string(self, logical=False):
+ if self.op == 'if':
+ return "['%s', %s]" % (self.right.string(True), self.left.string(logical))
+ elif self.op == "||" or self.op == ",":
+ if logical:
+ return "%s or %s" % (self.left.string(True), self.right.string(True))
+ else:
+ return "%s, %s" % (self.left, self.right)
+ elif self.op == "&&":
+ return "%s and %s" % (self.left.string(True), self.right.string(True))
+ return "%s %s %s" % (self.left.string(logical), self.op,
+ self.right.string(logical))
+
+
+def IsAlpha(string):
+ for char in string:
+ if not (char.isalpha() or char.isdigit() or char == '_'):
+ return False
+ return True
+
+
+class Tokenizer(object):
+ """A simple string tokenizer that chops expressions into variables,
+ parens and operators"""
+
+ def __init__(self, expr):
+ self.index = 0
+ self.expr = expr
+ self.length = len(expr)
+ self.tokens = None
+
+ def Current(self, length=1):
+ if not self.HasMore(length): return ""
+ return self.expr[self.index:self.index + length]
+
+ def HasMore(self, length=1):
+ return self.index < self.length + (length - 1)
+
+ def Advance(self, count=1):
+ self.index = self.index + count
+
+ def AddToken(self, token):
+ self.tokens.append(token)
+
+ def SkipSpaces(self):
+ while self.HasMore() and self.Current().isspace():
+ self.Advance()
+
+ def Tokenize(self):
+ self.tokens = [ ]
+ while self.HasMore():
+ self.SkipSpaces()
+ if not self.HasMore():
+ return None
+ if self.Current() == '(':
+ self.AddToken('(')
+ self.Advance()
+ elif self.Current() == ')':
+ self.AddToken(')')
+ self.Advance()
+ elif self.Current() == '$':
+ self.AddToken('$')
+ self.Advance()
+ elif self.Current() == ',':
+ self.AddToken(',')
+ self.Advance()
+ elif IsAlpha(self.Current()):
+ buf = ""
+ while self.HasMore() and IsAlpha(self.Current()):
+ buf += self.Current()
+ self.Advance()
+ self.AddToken(buf)
+ elif self.Current(2) == '&&':
+ self.AddToken('&&')
+ self.Advance(2)
+ elif self.Current(2) == '||':
+ self.AddToken('||')
+ self.Advance(2)
+ elif self.Current(2) == '==':
+ self.AddToken('==')
+ self.Advance(2)
+ elif self.Current(2) == '!=':
+ self.AddToken('!=')
+ self.Advance(2)
+ else:
+ return None
+ return self.tokens
+
+
+class Scanner(object):
+ """A simple scanner that can serve out tokens from a given list"""
+
+ def __init__(self, tokens):
+ self.tokens = tokens
+ self.length = len(tokens)
+ self.index = 0
+
+ def HasMore(self):
+ return self.index < self.length
+
+ def Current(self):
+ return self.tokens[self.index]
+
+ def Advance(self):
+ self.index = self.index + 1
+
+
+def ParseAtomicExpression(scan):
+ if scan.Current() == "true":
+ scan.Advance()
+ return Constant(True)
+ elif scan.Current() == "false":
+ scan.Advance()
+ return Constant(False)
+ elif IsAlpha(scan.Current()):
+ name = scan.Current()
+ scan.Advance()
+ return Outcome(name)
+ elif scan.Current() == '$':
+ scan.Advance()
+ if not IsAlpha(scan.Current()):
+ return None
+ name = scan.Current()
+ scan.Advance()
+ return Variable(name.lower())
+ elif scan.Current() == '(':
+ scan.Advance()
+ result = ParseLogicalExpression(scan)
+ if (not result) or (scan.Current() != ')'):
+ return None
+ scan.Advance()
+ return result
+ else:
+ return None
+
+
+BINARIES = ['==', '!=']
+def ParseOperatorExpression(scan):
+ left = ParseAtomicExpression(scan)
+ if not left: return None
+ while scan.HasMore() and (scan.Current() in BINARIES):
+ op = scan.Current()
+ scan.Advance()
+ right = ParseOperatorExpression(scan)
+ if not right:
+ return None
+ left = Operation(left, op, right)
+ return left
+
+
+def ParseConditionalExpression(scan):
+ left = ParseOperatorExpression(scan)
+ if not left: return None
+ while scan.HasMore() and (scan.Current() == 'if'):
+ scan.Advance()
+ right = ParseOperatorExpression(scan)
+ if not right:
+ return None
+ left = Operation(left, 'if', right)
+ return left
+
+
+LOGICALS = ["&&", "||", ","]
+def ParseLogicalExpression(scan):
+ left = ParseConditionalExpression(scan)
+ if not left: return None
+ while scan.HasMore() and (scan.Current() in LOGICALS):
+ op = scan.Current()
+ scan.Advance()
+ right = ParseConditionalExpression(scan)
+ if not right:
+ return None
+ left = Operation(left, op, right)
+ return left
+
+
+def ParseCondition(expr):
+ """Parses a logical expression into an Expression object"""
+ tokens = Tokenizer(expr).Tokenize()
+ if not tokens:
+ print "Malformed expression: '%s'" % expr
+ return None
+ scan = Scanner(tokens)
+ ast = ParseLogicalExpression(scan)
+ if not ast:
+ print "Malformed expression: '%s'" % expr
+ return None
+ if scan.HasMore():
+ print "Malformed expression: '%s'" % expr
+ return None
+ return ast
+
+
+class Section(object):
+ """A section of the configuration file. Sections are enabled or
+ disabled prior to running the tests, based on their conditions"""
+
+ def __init__(self, condition):
+ self.condition = condition
+ self.rules = [ ]
+
+ def AddRule(self, rule):
+ self.rules.append(rule)
+
+
+class Rule(object):
+ """A single rule that specifies the expected outcome for a single
+ test."""
+
+ def __init__(self, raw_path, path, value):
+ self.raw_path = raw_path
+ self.path = path
+ self.value = value
+
+ def GetOutcomes(self, env, defs):
+ return self.value.GetOutcomes(env, defs)
+
+ def Contains(self, path):
+ if len(self.path) > len(path):
+ return False
+ for i in xrange(len(self.path)):
+ if not self.path[i].match(path[i]):
+ return False
+ return True
+
+
+HEADER_PATTERN = re.compile(r'\[([^]]+)\]')
+RULE_PATTERN = re.compile(r'\s*([^: ]*)\s*:(.*)')
+DEF_PATTERN = re.compile(r'^def\s*(\w+)\s*=(.*)$')
+PREFIX_PATTERN = re.compile(r'^\s*prefix\s+([\w\_\.\-\/]+)$')
+
+
+class ConvertNotation(object):
+ def __init__(self, path):
+ self.path = path
+ self.indent = ""
+ self.comment = []
+ self.init = False
+ self.section = False
+ self.out = cStringIO.StringIO()
+
+ def OpenGlobal(self):
+ if self.init: return
+ self.WriteComment()
+ print >> self.out, "["
+ self.init = True
+
+ def CloseGlobal(self):
+ if not self.init: self.OpenGlobal()
+ print >> self.out, "]"
+ self.init = False
+
+ def OpenSection(self, condition="ALWAYS"):
+ if self.section: return
+ self.OpenGlobal()
+ if type(condition) != str:
+ condition = "'%s'" % condition.string(True)
+ print >> self.out, "%s[%s, {" % (self.indent, condition)
+ self.indent += " " * 2
+ self.section = condition
+
+ def CloseSection(self):
+ if not self.section: return
+ self.indent = self.indent[:-2]
+ print >> self.out, "%s}], # %s" % (self.indent, self.section)
+ self.section = False
+
+ def WriteComment(self):
+ if not self.comment: return
+ for c in self.comment:
+ if len(c.strip()) == 0:
+ print >> self.out, ""
+ else:
+ print >> self.out, "%s%s" % (self.indent, c),
+ self.comment = []
+
+ def GetOutput(self):
+ with open(self.path) as f:
+ for line in f:
+ if line[0] == '#':
+ self.comment += [line]
+ continue
+ if len(line.strip()) == 0:
+ self.comment += [line]
+ continue
+ header_match = HEADER_PATTERN.match(line)
+ if header_match:
+ condition = ParseCondition(header_match.group(1).strip())
+ self.CloseSection()
+ self.WriteComment()
+ self.OpenSection(condition)
+ continue
+ rule_match = RULE_PATTERN.match(line)
+ if rule_match:
+ self.OpenSection()
+ self.WriteComment()
+ path = rule_match.group(1).strip()
+ value_str = rule_match.group(2).strip()
+ comment = ""
+ if '#' in value_str:
+ pos = value_str.find('#')
+ comment = " %s" % value_str[pos:].strip()
+ value_str = value_str[:pos].strip()
+ value = ParseCondition(value_str)
+ print >> self.out, ("%s'%s': [%s],%s" %
+ (self.indent, path, value, comment))
+ continue
+ def_match = DEF_PATTERN.match(line)
+ if def_match:
+ # Custom definitions are deprecated.
+ continue
+ prefix_match = PREFIX_PATTERN.match(line)
+ if prefix_match:
+ continue
+ print "Malformed line: '%s'." % line
+ self.CloseSection()
+ self.CloseGlobal()
+ result = self.out.getvalue()
+ self.out.close()
+ return result
diff --git a/chromium/v8/tools/testrunner/local/progress.py b/chromium/v8/tools/testrunner/local/progress.py
new file mode 100644
index 00000000000..a663be23eba
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/progress.py
@@ -0,0 +1,284 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import sys
+import time
+
+from . import junit_output
+
+def EscapeCommand(command):
+ parts = []
+ for part in command:
+ if ' ' in part:
+ # Escape spaces. We may need to escape more characters for this
+ # to work properly.
+ parts.append('"%s"' % part)
+ else:
+ parts.append(part)
+ return " ".join(parts)
+
+
+class ProgressIndicator(object):
+
+ def __init__(self):
+ self.runner = None
+
+ def Starting(self):
+ pass
+
+ def Done(self):
+ pass
+
+ def AboutToRun(self, test):
+ pass
+
+ def HasRun(self, test, has_unexpected_output):
+ pass
+
+ def PrintFailureHeader(self, test):
+ if test.suite.IsNegativeTest(test):
+ negative_marker = '[negative] '
+ else:
+ negative_marker = ''
+ print "=== %(label)s %(negative)s===" % {
+ 'label': test.GetLabel(),
+ 'negative': negative_marker
+ }
+
+
+class SimpleProgressIndicator(ProgressIndicator):
+ """Abstract base class for {Verbose,Dots}ProgressIndicator"""
+
+ def Starting(self):
+ print 'Running %i tests' % self.runner.total
+
+ def Done(self):
+ print
+ for failed in self.runner.failed:
+ self.PrintFailureHeader(failed)
+ if failed.output.stderr:
+ print "--- stderr ---"
+ print failed.output.stderr.strip()
+ if failed.output.stdout:
+ print "--- stdout ---"
+ print failed.output.stdout.strip()
+ print "Command: %s" % EscapeCommand(self.runner.GetCommand(failed))
+ if failed.output.HasCrashed():
+ print "--- CRASHED ---"
+ if failed.output.HasTimedOut():
+ print "--- TIMEOUT ---"
+ if len(self.runner.failed) == 0:
+ print "==="
+ print "=== All tests succeeded"
+ print "==="
+ else:
+ print
+ print "==="
+ print "=== %i tests failed" % len(self.runner.failed)
+ if self.runner.crashed > 0:
+ print "=== %i tests CRASHED" % self.runner.crashed
+ print "==="
+
+
+class VerboseProgressIndicator(SimpleProgressIndicator):
+
+ def AboutToRun(self, test):
+ print 'Starting %s...' % test.GetLabel()
+ sys.stdout.flush()
+
+ def HasRun(self, test, has_unexpected_output):
+ if has_unexpected_output:
+ if test.output.HasCrashed():
+ outcome = 'CRASH'
+ else:
+ outcome = 'FAIL'
+ else:
+ outcome = 'pass'
+ print 'Done running %s: %s' % (test.GetLabel(), outcome)
+
+
+class DotsProgressIndicator(SimpleProgressIndicator):
+
+ def HasRun(self, test, has_unexpected_output):
+ total = self.runner.succeeded + len(self.runner.failed)
+ if (total > 1) and (total % 50 == 1):
+ sys.stdout.write('\n')
+ if has_unexpected_output:
+ if test.output.HasCrashed():
+ sys.stdout.write('C')
+ sys.stdout.flush()
+ elif test.output.HasTimedOut():
+ sys.stdout.write('T')
+ sys.stdout.flush()
+ else:
+ sys.stdout.write('F')
+ sys.stdout.flush()
+ else:
+ sys.stdout.write('.')
+ sys.stdout.flush()
+
+
+class CompactProgressIndicator(ProgressIndicator):
+ """Abstract base class for {Color,Monochrome}ProgressIndicator"""
+
+ def __init__(self, templates):
+ super(CompactProgressIndicator, self).__init__()
+ self.templates = templates
+ self.last_status_length = 0
+ self.start_time = time.time()
+
+ def Done(self):
+ self.PrintProgress('Done')
+ print "" # Line break.
+
+ def AboutToRun(self, test):
+ self.PrintProgress(test.GetLabel())
+
+ def HasRun(self, test, has_unexpected_output):
+ if has_unexpected_output:
+ self.ClearLine(self.last_status_length)
+ self.PrintFailureHeader(test)
+ stdout = test.output.stdout.strip()
+ if len(stdout):
+ print self.templates['stdout'] % stdout
+ stderr = test.output.stderr.strip()
+ if len(stderr):
+ print self.templates['stderr'] % stderr
+ print "Command: %s" % EscapeCommand(self.runner.GetCommand(test))
+ if test.output.HasCrashed():
+ print "exit code: %d" % test.output.exit_code
+ print "--- CRASHED ---"
+ if test.output.HasTimedOut():
+ print "--- TIMEOUT ---"
+
+ def Truncate(self, string, length):
+ if length and (len(string) > (length - 3)):
+ return string[:(length - 3)] + "..."
+ else:
+ return string
+
+ def PrintProgress(self, name):
+ self.ClearLine(self.last_status_length)
+ elapsed = time.time() - self.start_time
+ status = self.templates['status_line'] % {
+ 'passed': self.runner.succeeded,
+ 'remaining': (((self.runner.total - self.runner.remaining) * 100) //
+ self.runner.total),
+ 'failed': len(self.runner.failed),
+ 'test': name,
+ 'mins': int(elapsed) / 60,
+ 'secs': int(elapsed) % 60
+ }
+ status = self.Truncate(status, 78)
+ self.last_status_length = len(status)
+ print status,
+ sys.stdout.flush()
+
+
+class ColorProgressIndicator(CompactProgressIndicator):
+
+ def __init__(self):
+ templates = {
+ 'status_line': ("[%(mins)02i:%(secs)02i|"
+ "\033[34m%%%(remaining) 4d\033[0m|"
+ "\033[32m+%(passed) 4d\033[0m|"
+ "\033[31m-%(failed) 4d\033[0m]: %(test)s"),
+ 'stdout': "\033[1m%s\033[0m",
+ 'stderr': "\033[31m%s\033[0m",
+ }
+ super(ColorProgressIndicator, self).__init__(templates)
+
+ def ClearLine(self, last_line_length):
+ print "\033[1K\r",
+
+
+class MonochromeProgressIndicator(CompactProgressIndicator):
+
+ def __init__(self):
+ templates = {
+ 'status_line': ("[%(mins)02i:%(secs)02i|%%%(remaining) 4d|"
+ "+%(passed) 4d|-%(failed) 4d]: %(test)s"),
+ 'stdout': '%s',
+ 'stderr': '%s',
+ }
+ super(MonochromeProgressIndicator, self).__init__(templates)
+
+ def ClearLine(self, last_line_length):
+ print ("\r" + (" " * last_line_length) + "\r"),
+
+
+class JUnitTestProgressIndicator(ProgressIndicator):
+
+ def __init__(self, progress_indicator, junitout, junittestsuite):
+ self.progress_indicator = progress_indicator
+ self.outputter = junit_output.JUnitTestOutput(junittestsuite)
+ if junitout:
+ self.outfile = open(junitout, "w")
+ else:
+ self.outfile = sys.stdout
+
+ def Starting(self):
+ self.progress_indicator.runner = self.runner
+ self.progress_indicator.Starting()
+
+ def Done(self):
+ self.progress_indicator.Done()
+ self.outputter.FinishAndWrite(self.outfile)
+ if self.outfile != sys.stdout:
+ self.outfile.close()
+
+ def AboutToRun(self, test):
+ self.progress_indicator.AboutToRun(test)
+
+ def HasRun(self, test, has_unexpected_output):
+ self.progress_indicator.HasRun(test, has_unexpected_output)
+ fail_text = ""
+ if has_unexpected_output:
+ stdout = test.output.stdout.strip()
+ if len(stdout):
+ fail_text += "stdout:\n%s\n" % stdout
+ stderr = test.output.stderr.strip()
+ if len(stderr):
+ fail_text += "stderr:\n%s\n" % stderr
+ fail_text += "Command: %s" % EscapeCommand(self.runner.GetCommand(test))
+ if test.output.HasCrashed():
+ fail_text += "exit code: %d\n--- CRASHED ---" % test.output.exit_code
+ if test.output.HasTimedOut():
+ fail_text += "--- TIMEOUT ---"
+ self.outputter.HasRunTest(
+ [test.GetLabel()] + self.runner.context.mode_flags + test.flags,
+ test.duration,
+ fail_text)
+
+
+PROGRESS_INDICATORS = {
+ 'verbose': VerboseProgressIndicator,
+ 'dots': DotsProgressIndicator,
+ 'color': ColorProgressIndicator,
+ 'mono': MonochromeProgressIndicator
+}
diff --git a/chromium/v8/tools/testrunner/local/statusfile.py b/chromium/v8/tools/testrunner/local/statusfile.py
new file mode 100644
index 00000000000..1d30fe3d3c1
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/statusfile.py
@@ -0,0 +1,150 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+# These imports are required for the on-demand conversion from
+# old to new status file format.
+from os.path import exists
+from os.path import getmtime
+
+from . import old_statusfile
+
+
+# These outcomes can occur in a TestCase's outcomes list:
+SKIP = "SKIP"
+FAIL = "FAIL"
+PASS = "PASS"
+OKAY = "OKAY"
+TIMEOUT = "TIMEOUT"
+CRASH = "CRASH"
+SLOW = "SLOW"
+FLAKY = "FLAKY"
+# These are just for the status files and are mapped below in DEFS:
+FAIL_OK = "FAIL_OK"
+PASS_OR_FAIL = "PASS_OR_FAIL"
+
+ALWAYS = "ALWAYS"
+
+KEYWORDS = {}
+for key in [SKIP, FAIL, PASS, OKAY, TIMEOUT, CRASH, SLOW, FLAKY, FAIL_OK,
+ PASS_OR_FAIL, ALWAYS]:
+ KEYWORDS[key] = key
+
+DEFS = {FAIL_OK: [FAIL, OKAY],
+ PASS_OR_FAIL: [PASS, FAIL]}
+
+# Support arches, modes to be written as keywords instead of strings.
+VARIABLES = {ALWAYS: True}
+for var in ["debug", "release", "android_arm", "android_ia32", "arm", "ia32",
+ "mipsel", "x64", "nacl_ia32", "nacl_x64"]:
+ VARIABLES[var] = var
+
+
+def DoSkip(outcomes):
+ return SKIP in outcomes or SLOW in outcomes
+
+
+def IsFlaky(outcomes):
+ return FLAKY in outcomes
+
+
+def IsPassOrFail(outcomes):
+ return ((PASS in outcomes) and (FAIL in outcomes) and
+ (not CRASH in outcomes) and (not OKAY in outcomes))
+
+
+def IsFailOk(outcomes):
+ return (FAIL in outcomes) and (OKAY in outcomes)
+
+
+def _AddOutcome(result, new):
+ global DEFS
+ if new in DEFS:
+ mapped = DEFS[new]
+ if type(mapped) == list:
+ for m in mapped:
+ _AddOutcome(result, m)
+ elif type(mapped) == str:
+ _AddOutcome(result, mapped)
+ else:
+ result.add(new)
+
+
+def _ParseOutcomeList(rule, outcomes, target_dict, variables):
+ result = set([])
+ if type(outcomes) == str:
+ outcomes = [outcomes]
+ for item in outcomes:
+ if type(item) == str:
+ _AddOutcome(result, item)
+ elif type(item) == list:
+ if not eval(item[0], variables): continue
+ for outcome in item[1:]:
+ assert type(outcome) == str
+ _AddOutcome(result, outcome)
+ else:
+ assert False
+ if len(result) == 0: return
+ if rule in target_dict:
+ target_dict[rule] |= result
+ else:
+ target_dict[rule] = result
+
+
+def ReadStatusFile(path, variables):
+ # As long as the old-format .status files are authoritative, just
+ # create the converted version on demand and cache it to speed up
+ # subsequent runs.
+ if path.endswith(".status"):
+ newpath = path + "2"
+ if not exists(newpath) or getmtime(newpath) < getmtime(path):
+ print "Converting status file."
+ converted = old_statusfile.ConvertNotation(path).GetOutput()
+ with open(newpath, 'w') as f:
+ f.write(converted)
+ path = newpath
+
+ with open(path) as f:
+ global KEYWORDS
+ contents = eval(f.read(), KEYWORDS)
+
+ rules = {}
+ wildcards = {}
+ variables.update(VARIABLES)
+ for section in contents:
+ assert type(section) == list
+ assert len(section) == 2
+ if not eval(section[0], variables): continue
+ section = section[1]
+ assert type(section) == dict
+ for rule in section:
+ assert type(rule) == str
+ if rule[-1] == '*':
+ _ParseOutcomeList(rule, section[rule], wildcards, variables)
+ else:
+ _ParseOutcomeList(rule, section[rule], rules, variables)
+ return rules, wildcards
diff --git a/chromium/v8/tools/testrunner/local/testsuite.py b/chromium/v8/tools/testrunner/local/testsuite.py
new file mode 100644
index 00000000000..b0372e7f739
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/testsuite.py
@@ -0,0 +1,194 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import imp
+import os
+
+from . import statusfile
+from . import utils
+
+class TestSuite(object):
+
+ @staticmethod
+ def LoadTestSuite(root):
+ name = root.split(os.path.sep)[-1]
+ f = None
+ try:
+ (f, pathname, description) = imp.find_module("testcfg", [root])
+ module = imp.load_module("testcfg", f, pathname, description)
+ suite = module.GetSuite(name, root)
+ finally:
+ if f:
+ f.close()
+ return suite
+
+ def __init__(self, name, root):
+ self.name = name # string
+ self.root = root # string containing path
+ self.tests = None # list of TestCase objects
+ self.rules = None # dictionary mapping test path to list of outcomes
+ self.wildcards = None # dictionary mapping test paths to list of outcomes
+ self.total_duration = None # float, assigned on demand
+
+ def shell(self):
+ return "d8"
+
+ def suffix(self):
+ return ".js"
+
+ def status_file(self):
+ return "%s/%s.status" % (self.root, self.name)
+
+ # Used in the status file and for stdout printing.
+ def CommonTestName(self, testcase):
+ if utils.IsWindows():
+ return testcase.path.replace("\\", "/")
+ else:
+ return testcase.path
+
+ def ListTests(self, context):
+ raise NotImplementedError
+
+ def VariantFlags(self):
+ return None
+
+ def DownloadData(self):
+ pass
+
+ def ReadStatusFile(self, variables):
+ (self.rules, self.wildcards) = \
+ statusfile.ReadStatusFile(self.status_file(), variables)
+
+ def ReadTestCases(self, context):
+ self.tests = self.ListTests(context)
+
+ @staticmethod
+ def _FilterFlaky(flaky, mode):
+ return (mode == "run" and not flaky) or (mode == "skip" and flaky)
+
+ def FilterTestCasesByStatus(self, warn_unused_rules, flaky_tests="dontcare"):
+ filtered = []
+ used_rules = set()
+ for t in self.tests:
+ flaky = False
+ testname = self.CommonTestName(t)
+ if testname in self.rules:
+ used_rules.add(testname)
+ # Even for skipped tests, as the TestCase object stays around and
+ # PrintReport() uses it.
+ t.outcomes = self.rules[testname]
+ if statusfile.DoSkip(t.outcomes):
+ continue # Don't add skipped tests to |filtered|.
+ flaky = statusfile.IsFlaky(t.outcomes)
+ skip = False
+ for rule in self.wildcards:
+ assert rule[-1] == '*'
+ if testname.startswith(rule[:-1]):
+ used_rules.add(rule)
+ t.outcomes = self.wildcards[rule]
+ if statusfile.DoSkip(t.outcomes):
+ skip = True
+ break # "for rule in self.wildcards"
+ flaky = flaky or statusfile.IsFlaky(t.outcomes)
+ if skip or self._FilterFlaky(flaky, flaky_tests):
+ continue # "for t in self.tests"
+ filtered.append(t)
+ self.tests = filtered
+
+ if not warn_unused_rules:
+ return
+
+ for rule in self.rules:
+ if rule not in used_rules:
+ print("Unused rule: %s -> %s" % (rule, self.rules[rule]))
+ for rule in self.wildcards:
+ if rule not in used_rules:
+ print("Unused rule: %s -> %s" % (rule, self.wildcards[rule]))
+
+ def FilterTestCasesByArgs(self, args):
+ filtered = []
+ filtered_args = []
+ for a in args:
+ argpath = a.split(os.path.sep)
+ if argpath[0] != self.name:
+ continue
+ if len(argpath) == 1 or (len(argpath) == 2 and argpath[1] == '*'):
+ return # Don't filter, run all tests in this suite.
+ path = os.path.sep.join(argpath[1:])
+ if path[-1] == '*':
+ path = path[:-1]
+ filtered_args.append(path)
+ for t in self.tests:
+ for a in filtered_args:
+ if t.path.startswith(a):
+ filtered.append(t)
+ break
+ self.tests = filtered
+
+ def GetFlagsForTestCase(self, testcase, context):
+ raise NotImplementedError
+
+ def GetSourceForTest(self, testcase):
+ return "(no source available)"
+
+ def IsFailureOutput(self, output, testpath):
+ return output.exit_code != 0
+
+ def IsNegativeTest(self, testcase):
+ return False
+
+ def HasFailed(self, testcase):
+ execution_failed = self.IsFailureOutput(testcase.output, testcase.path)
+ if self.IsNegativeTest(testcase):
+ return not execution_failed
+ else:
+ return execution_failed
+
+ def HasUnexpectedOutput(self, testcase):
+ if testcase.output.HasCrashed():
+ outcome = statusfile.CRASH
+ elif testcase.output.HasTimedOut():
+ outcome = statusfile.TIMEOUT
+ elif self.HasFailed(testcase):
+ outcome = statusfile.FAIL
+ else:
+ outcome = statusfile.PASS
+ if not testcase.outcomes:
+ return outcome != statusfile.PASS
+ return not outcome in testcase.outcomes
+
+ def StripOutputForTransmit(self, testcase):
+ if not self.HasUnexpectedOutput(testcase):
+ testcase.output.stdout = ""
+ testcase.output.stderr = ""
+
+ def CalculateTotalDuration(self):
+ self.total_duration = 0.0
+ for t in self.tests:
+ self.total_duration += t.duration
+ return self.total_duration
diff --git a/chromium/v8/tools/testrunner/local/utils.py b/chromium/v8/tools/testrunner/local/utils.py
new file mode 100644
index 00000000000..b7caa121f3a
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/utils.py
@@ -0,0 +1,108 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import os
+from os.path import exists
+from os.path import isdir
+from os.path import join
+import platform
+import re
+
+
+def GetSuitePaths(test_root):
+ def IsSuite(path):
+ return isdir(path) and exists(join(path, 'testcfg.py'))
+ return [ f for f in os.listdir(test_root) if IsSuite(join(test_root, f)) ]
+
+
+# Reads a file into an array of strings
+def ReadLinesFrom(name):
+ lines = []
+ with open(name) as f:
+ for line in f:
+ if line.startswith('#'): continue
+ if '#' in line:
+ line = line[:line.find('#')]
+ line = line.strip()
+ if not line: continue
+ lines.append(line)
+ return lines
+
+
+def GuessOS():
+ system = platform.system()
+ if system == 'Linux':
+ return 'linux'
+ elif system == 'Darwin':
+ return 'macos'
+ elif system.find('CYGWIN') >= 0:
+ return 'cygwin'
+ elif system == 'Windows' or system == 'Microsoft':
+ # On Windows Vista platform.system() can return 'Microsoft' with some
+ # versions of Python, see http://bugs.python.org/issue1082
+ return 'win32'
+ elif system == 'FreeBSD':
+ return 'freebsd'
+ elif system == 'OpenBSD':
+ return 'openbsd'
+ elif system == 'SunOS':
+ return 'solaris'
+ elif system == 'NetBSD':
+ return 'netbsd'
+ else:
+ return None
+
+
+# This will default to building the 32 bit VM even on machines that are
+# capable of running the 64 bit VM.
+def DefaultArch():
+ machine = platform.machine()
+ machine = machine.lower() # Windows 7 capitalizes 'AMD64'.
+ if machine.startswith('arm'):
+ return 'arm'
+ elif (not machine) or (not re.match('(x|i[3-6])86$', machine) is None):
+ return 'ia32'
+ elif machine == 'i86pc':
+ return 'ia32'
+ elif machine == 'x86_64':
+ return 'ia32'
+ elif machine == 'amd64':
+ return 'ia32'
+ else:
+ return None
+
+
+def GuessWordsize():
+ if '64' in platform.machine():
+ return '64'
+ else:
+ return '32'
+
+
+def IsWindows():
+ return GuessOS() == 'win32'
diff --git a/chromium/v8/tools/testrunner/local/verbose.py b/chromium/v8/tools/testrunner/local/verbose.py
new file mode 100644
index 00000000000..00c330d2d9c
--- /dev/null
+++ b/chromium/v8/tools/testrunner/local/verbose.py
@@ -0,0 +1,99 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import sys
+import time
+
+from . import statusfile
+
+
+REPORT_TEMPLATE = (
+"""Total: %(total)i tests
+ * %(skipped)4d tests will be skipped
+ * %(timeout)4d tests are expected to timeout sometimes
+ * %(nocrash)4d tests are expected to be flaky but not crash
+ * %(pass)4d tests are expected to pass
+ * %(fail_ok)4d tests are expected to fail that we won't fix
+ * %(fail)4d tests are expected to fail that we should fix""")
+
+
+def PrintReport(tests):
+ total = len(tests)
+ skipped = timeout = nocrash = passes = fail_ok = fail = 0
+ for t in tests:
+ if "outcomes" not in dir(t) or not t.outcomes:
+ passes += 1
+ continue
+ o = t.outcomes
+ if statusfile.DoSkip(o):
+ skipped += 1
+ continue
+ if statusfile.TIMEOUT in o: timeout += 1
+ if statusfile.IsPassOrFail(o): nocrash += 1
+ if list(o) == [statusfile.PASS]: passes += 1
+ if statusfile.IsFailOk(o): fail_ok += 1
+ if list(o) == [statusfile.FAIL]: fail += 1
+ print REPORT_TEMPLATE % {
+ "total": total,
+ "skipped": skipped,
+ "timeout": timeout,
+ "nocrash": nocrash,
+ "pass": passes,
+ "fail_ok": fail_ok,
+ "fail": fail
+ }
+
+
+def PrintTestSource(tests):
+ for test in tests:
+ suite = test.suite
+ source = suite.GetSourceForTest(test).strip()
+ if len(source) > 0:
+ print "--- begin source: %s/%s ---" % (suite.name, test.path)
+ print source
+ print "--- end source: %s/%s ---" % (suite.name, test.path)
+
+
+def FormatTime(d):
+ millis = round(d * 1000) % 1000
+ return time.strftime("%M:%S.", time.gmtime(d)) + ("%03i" % millis)
+
+
+def PrintTestDurations(suites, overall_time):
+ # Write the times to stderr to make it easy to separate from the
+ # test output.
+ print
+ sys.stderr.write("--- Total time: %s ---\n" % FormatTime(overall_time))
+ timed_tests = [ t for s in suites for t in s.tests
+ if t.duration is not None ]
+ timed_tests.sort(lambda a, b: cmp(b.duration, a.duration))
+ index = 1
+ for entry in timed_tests[:20]:
+ t = FormatTime(entry.duration)
+ sys.stderr.write("%4i (%s) %s\n" % (index, t, entry.GetLabel()))
+ index += 1
diff --git a/chromium/v8/tools/testrunner/network/__init__.py b/chromium/v8/tools/testrunner/network/__init__.py
new file mode 100644
index 00000000000..202a262709c
--- /dev/null
+++ b/chromium/v8/tools/testrunner/network/__init__.py
@@ -0,0 +1,26 @@
+# Copyright 2012 the V8 project authors. 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.
diff --git a/chromium/v8/tools/testrunner/network/distro.py b/chromium/v8/tools/testrunner/network/distro.py
new file mode 100644
index 00000000000..9d5a471d444
--- /dev/null
+++ b/chromium/v8/tools/testrunner/network/distro.py
@@ -0,0 +1,90 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+class Shell(object):
+ def __init__(self, shell):
+ self.shell = shell
+ self.tests = []
+ self.total_duration = 0.0
+
+ def AddSuite(self, suite):
+ self.tests += suite.tests
+ self.total_duration += suite.total_duration
+
+ def SortTests(self):
+ self.tests.sort(cmp=lambda x, y: cmp(x.duration, y.duration))
+
+
+def Assign(suites, peers):
+ total_work = 0.0
+ for s in suites:
+ total_work += s.CalculateTotalDuration()
+
+ total_power = 0.0
+ for p in peers:
+ p.assigned_work = 0.0
+ total_power += p.jobs * p.relative_performance
+ for p in peers:
+ p.needed_work = total_work * p.jobs * p.relative_performance / total_power
+
+ shells = {}
+ for s in suites:
+ shell = s.shell()
+ if not shell in shells:
+ shells[shell] = Shell(shell)
+ shells[shell].AddSuite(s)
+ # Convert |shells| to list and sort it, shortest total_duration first.
+ shells = [ shells[s] for s in shells ]
+ shells.sort(cmp=lambda x, y: cmp(x.total_duration, y.total_duration))
+ # Sort tests within each shell, longest duration last (so it's
+ # pop()'ed first).
+ for s in shells: s.SortTests()
+ # Sort peers, least needed_work first.
+ peers.sort(cmp=lambda x, y: cmp(x.needed_work, y.needed_work))
+ index = 0
+ for shell in shells:
+ while len(shell.tests) > 0:
+ while peers[index].needed_work <= 0:
+ index += 1
+ if index == len(peers):
+ print("BIG FAT WARNING: Assigning tests to peers failed. "
+ "Remaining tests: %d. Going to slow mode." % len(shell.tests))
+ # Pick the least-busy peer. Sorting the list for each test
+ # is terribly slow, but this is just an emergency fallback anyway.
+ peers.sort(cmp=lambda x, y: cmp(x.needed_work, y.needed_work))
+ peers[0].ForceAddOneTest(shell.tests.pop(), shell)
+ # If the peer already has a shell assigned and would need this one
+ # and then yet another, try to avoid it.
+ peer = peers[index]
+ if (shell.total_duration < peer.needed_work and
+ len(peer.shells) > 0 and
+ index < len(peers) - 1 and
+ shell.total_duration <= peers[index + 1].needed_work):
+ peers[index + 1].AddTests(shell)
+ else:
+ peer.AddTests(shell)
diff --git a/chromium/v8/tools/testrunner/network/endpoint.py b/chromium/v8/tools/testrunner/network/endpoint.py
new file mode 100644
index 00000000000..d0950cf5a6b
--- /dev/null
+++ b/chromium/v8/tools/testrunner/network/endpoint.py
@@ -0,0 +1,124 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import multiprocessing
+import os
+import Queue
+import threading
+import time
+
+from ..local import execution
+from ..local import progress
+from ..local import testsuite
+from ..local import utils
+from ..server import compression
+
+
+class EndpointProgress(progress.ProgressIndicator):
+ def __init__(self, sock, server, ctx):
+ super(EndpointProgress, self).__init__()
+ self.sock = sock
+ self.server = server
+ self.context = ctx
+ self.results_queue = [] # Accessors must synchronize themselves.
+ self.sender_lock = threading.Lock()
+ self.senderthread = threading.Thread(target=self._SenderThread)
+ self.senderthread.start()
+
+ def HasRun(self, test, has_unexpected_output):
+ # The runners that call this have a lock anyway, so this is safe.
+ self.results_queue.append(test)
+
+ def _SenderThread(self):
+ keep_running = True
+ tests = []
+ self.sender_lock.acquire()
+ while keep_running:
+ time.sleep(0.1)
+ # This should be "atomic enough" without locking :-)
+ # (We don't care which list any new elements get appended to, as long
+ # as we don't lose any and the last one comes last.)
+ current = self.results_queue
+ self.results_queue = []
+ for c in current:
+ if c is None:
+ keep_running = False
+ else:
+ tests.append(c)
+ if keep_running and len(tests) < 1:
+ continue # Wait for more results.
+ if len(tests) < 1: break # We're done here.
+ result = []
+ for t in tests:
+ result.append(t.PackResult())
+ try:
+ compression.Send(result, self.sock)
+ except:
+ self.runner.terminate = True
+ for t in tests:
+ self.server.CompareOwnPerf(t, self.context.arch, self.context.mode)
+ tests = []
+ self.sender_lock.release()
+
+
+def Execute(workspace, ctx, tests, sock, server):
+ suite_paths = utils.GetSuitePaths(os.path.join(workspace, "test"))
+ suites = []
+ for root in suite_paths:
+ suite = testsuite.TestSuite.LoadTestSuite(
+ os.path.join(workspace, "test", root))
+ if suite:
+ suites.append(suite)
+
+ suites_dict = {}
+ for s in suites:
+ suites_dict[s.name] = s
+ s.tests = []
+ for t in tests:
+ suite = suites_dict[t.suite]
+ t.suite = suite
+ suite.tests.append(t)
+
+ suites = [ s for s in suites if len(s.tests) > 0 ]
+ for s in suites:
+ s.DownloadData()
+
+ progress_indicator = EndpointProgress(sock, server, ctx)
+ runner = execution.Runner(suites, progress_indicator, ctx)
+ try:
+ runner.Run(server.jobs)
+ except IOError, e:
+ if e.errno == 2:
+ message = ("File not found: %s, maybe you forgot to 'git add' it?" %
+ e.filename)
+ else:
+ message = "%s" % e
+ compression.Send([[-1, message]], sock)
+ progress_indicator.HasRun(None, None) # Sentinel to signal the end.
+ progress_indicator.sender_lock.acquire() # Released when sending is done.
+ progress_indicator.sender_lock.release()
diff --git a/chromium/v8/tools/testrunner/network/network_execution.py b/chromium/v8/tools/testrunner/network/network_execution.py
new file mode 100644
index 00000000000..0f53a6bb645
--- /dev/null
+++ b/chromium/v8/tools/testrunner/network/network_execution.py
@@ -0,0 +1,254 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import os
+import socket
+import subprocess
+import threading
+import time
+
+from . import distro
+from . import perfdata
+from ..local import execution
+from ..objects import peer
+from ..objects import workpacket
+from ..server import compression
+from ..server import constants
+from ..server import local_handler
+from ..server import signatures
+
+
+def GetPeers():
+ data = local_handler.LocalQuery([constants.REQUEST_PEERS])
+ if not data: return []
+ return [ peer.Peer.Unpack(p) for p in data ]
+
+
+class NetworkedRunner(execution.Runner):
+ def __init__(self, suites, progress_indicator, context, peers, workspace):
+ self.suites = suites
+ num_tests = 0
+ datapath = os.path.join("out", "testrunner_data")
+ self.perf_data_manager = perfdata.PerfDataManager(datapath)
+ self.perfdata = self.perf_data_manager.GetStore(context.arch, context.mode)
+ for s in suites:
+ for t in s.tests:
+ t.duration = self.perfdata.FetchPerfData(t) or 1.0
+ num_tests += len(s.tests)
+ self._CommonInit(num_tests, progress_indicator, context)
+ self.tests = [] # Only used if we need to fall back to local execution.
+ self.tests_lock = threading.Lock()
+ self.peers = peers
+ self.pubkey_fingerprint = None # Fetched later.
+ self.base_rev = subprocess.check_output(
+ "cd %s; git log -1 --format=%%H --grep=git-svn-id" % workspace,
+ shell=True).strip()
+ self.base_svn_rev = subprocess.check_output(
+ "cd %s; git log -1 %s" # Get commit description.
+ " | grep -e '^\s*git-svn-id:'" # Extract "git-svn-id" line.
+ " | awk '{print $2}'" # Extract "repository@revision" part.
+ " | sed -e 's/.*@//'" % # Strip away "repository@".
+ (workspace, self.base_rev), shell=True).strip()
+ self.patch = subprocess.check_output(
+ "cd %s; git diff %s" % (workspace, self.base_rev), shell=True)
+ self.binaries = {}
+ self.initialization_lock = threading.Lock()
+ self.initialization_lock.acquire() # Released when init is done.
+ self._OpenLocalConnection()
+ self.local_receiver_thread = threading.Thread(
+ target=self._ListenLocalConnection)
+ self.local_receiver_thread.daemon = True
+ self.local_receiver_thread.start()
+ self.initialization_lock.acquire()
+ self.initialization_lock.release()
+
+ def _OpenLocalConnection(self):
+ self.local_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ code = self.local_socket.connect_ex(("localhost", constants.CLIENT_PORT))
+ if code != 0:
+ raise RuntimeError("Failed to connect to local server")
+ compression.Send([constants.REQUEST_PUBKEY_FINGERPRINT], self.local_socket)
+
+ def _ListenLocalConnection(self):
+ release_lock_countdown = 1 # Pubkey.
+ self.local_receiver = compression.Receiver(self.local_socket)
+ while not self.local_receiver.IsDone():
+ data = self.local_receiver.Current()
+ if data[0] == constants.REQUEST_PUBKEY_FINGERPRINT:
+ pubkey = data[1]
+ if not pubkey: raise RuntimeError("Received empty public key")
+ self.pubkey_fingerprint = pubkey
+ release_lock_countdown -= 1
+ if release_lock_countdown == 0:
+ self.initialization_lock.release()
+ release_lock_countdown -= 1 # Prevent repeated triggering.
+ self.local_receiver.Advance()
+
+ def Run(self, jobs):
+ self.indicator.Starting()
+ need_libv8 = False
+ for s in self.suites:
+ shell = s.shell()
+ if shell not in self.binaries:
+ path = os.path.join(self.context.shell_dir, shell)
+ # Check if this is a shared library build.
+ try:
+ ldd = subprocess.check_output("ldd %s | grep libv8\\.so" % (path),
+ shell=True)
+ ldd = ldd.strip().split(" ")
+ assert ldd[0] == "libv8.so"
+ assert ldd[1] == "=>"
+ need_libv8 = True
+ binary_needs_libv8 = True
+ libv8 = signatures.ReadFileAndSignature(ldd[2])
+ except:
+ binary_needs_libv8 = False
+ binary = signatures.ReadFileAndSignature(path)
+ if binary[0] is None:
+ print("Error: Failed to create signature.")
+ assert binary[1] != 0
+ return binary[1]
+ binary.append(binary_needs_libv8)
+ self.binaries[shell] = binary
+ if need_libv8:
+ self.binaries["libv8.so"] = libv8
+ distro.Assign(self.suites, self.peers)
+ # Spawn one thread for each peer.
+ threads = []
+ for p in self.peers:
+ thread = threading.Thread(target=self._TalkToPeer, args=[p])
+ threads.append(thread)
+ thread.start()
+ try:
+ for thread in threads:
+ # Use a timeout so that signals (Ctrl+C) will be processed.
+ thread.join(timeout=10000000)
+ self._AnalyzePeerRuntimes()
+ except KeyboardInterrupt:
+ self.terminate = True
+ raise
+ except Exception, _e:
+ # If there's an exception we schedule an interruption for any
+ # remaining threads...
+ self.terminate = True
+ # ...and then reraise the exception to bail out.
+ raise
+ compression.Send(constants.END_OF_STREAM, self.local_socket)
+ self.local_socket.close()
+ if self.tests:
+ self._RunInternal(jobs)
+ self.indicator.Done()
+ return not self.failed
+
+ def _TalkToPeer(self, peer):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.settimeout(self.context.timeout + 10)
+ code = sock.connect_ex((peer.address, constants.PEER_PORT))
+ if code == 0:
+ try:
+ peer.runtime = None
+ start_time = time.time()
+ packet = workpacket.WorkPacket(peer=peer, context=self.context,
+ base_revision=self.base_svn_rev,
+ patch=self.patch,
+ pubkey=self.pubkey_fingerprint)
+ data, test_map = packet.Pack(self.binaries)
+ compression.Send(data, sock)
+ compression.Send(constants.END_OF_STREAM, sock)
+ rec = compression.Receiver(sock)
+ while not rec.IsDone() and not self.terminate:
+ data_list = rec.Current()
+ for data in data_list:
+ test_id = data[0]
+ if test_id < 0:
+ # The peer is reporting an error.
+ with self.lock:
+ print("\nPeer %s reports error: %s" % (peer.address, data[1]))
+ continue
+ test = test_map.pop(test_id)
+ test.MergeResult(data)
+ try:
+ self.perfdata.UpdatePerfData(test)
+ except Exception, e:
+ print("UpdatePerfData exception: %s" % e)
+ pass # Just keep working.
+ with self.lock:
+ perf_key = self.perfdata.GetKey(test)
+ compression.Send(
+ [constants.INFORM_DURATION, perf_key, test.duration,
+ self.context.arch, self.context.mode],
+ self.local_socket)
+ self.indicator.AboutToRun(test)
+ has_unexpected_output = test.suite.HasUnexpectedOutput(test)
+ if has_unexpected_output:
+ self.failed.append(test)
+ if test.output.HasCrashed():
+ self.crashed += 1
+ else:
+ self.succeeded += 1
+ self.remaining -= 1
+ self.indicator.HasRun(test, has_unexpected_output)
+ rec.Advance()
+ peer.runtime = time.time() - start_time
+ except KeyboardInterrupt:
+ sock.close()
+ raise
+ except Exception, e:
+ print("Got exception: %s" % e)
+ pass # Fall back to local execution.
+ else:
+ compression.Send([constants.UNRESPONSIVE_PEER, peer.address],
+ self.local_socket)
+ sock.close()
+ if len(test_map) > 0:
+ # Some tests have not received any results. Run them locally.
+ print("\nNo results for %d tests, running them locally." % len(test_map))
+ self._EnqueueLocally(test_map)
+
+ def _EnqueueLocally(self, test_map):
+ with self.tests_lock:
+ for test in test_map:
+ self.tests.append(test_map[test])
+
+ def _AnalyzePeerRuntimes(self):
+ total_runtime = 0.0
+ total_work = 0.0
+ for p in self.peers:
+ if p.runtime is None:
+ return
+ total_runtime += p.runtime
+ total_work += p.assigned_work
+ for p in self.peers:
+ p.assigned_work /= total_work
+ p.runtime /= total_runtime
+ perf_correction = p.assigned_work / p.runtime
+ old_perf = p.relative_performance
+ p.relative_performance = (old_perf + perf_correction) / 2.0
+ compression.Send([constants.UPDATE_PERF, p.address,
+ p.relative_performance],
+ self.local_socket)
diff --git a/chromium/v8/tools/testrunner/network/perfdata.py b/chromium/v8/tools/testrunner/network/perfdata.py
new file mode 100644
index 00000000000..2979dc48661
--- /dev/null
+++ b/chromium/v8/tools/testrunner/network/perfdata.py
@@ -0,0 +1,120 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import os
+import shelve
+import threading
+
+
+class PerfDataEntry(object):
+ def __init__(self):
+ self.avg = 0.0
+ self.count = 0
+
+ def AddResult(self, result):
+ kLearnRateLimiter = 99 # Greater value means slower learning.
+ # We use an approximation of the average of the last 100 results here:
+ # The existing average is weighted with kLearnRateLimiter (or less
+ # if there are fewer data points).
+ effective_count = min(self.count, kLearnRateLimiter)
+ self.avg = self.avg * effective_count + result
+ self.count = effective_count + 1
+ self.avg /= self.count
+
+
+class PerfDataStore(object):
+ def __init__(self, datadir, arch, mode):
+ filename = os.path.join(datadir, "%s.%s.perfdata" % (arch, mode))
+ self.database = shelve.open(filename, protocol=2)
+ self.closed = False
+ self.lock = threading.Lock()
+
+ def __del__(self):
+ self.close()
+
+ def close(self):
+ if self.closed: return
+ self.database.close()
+ self.closed = True
+
+ def GetKey(self, test):
+ """Computes the key used to access data for the given testcase."""
+ flags = "".join(test.flags)
+ return str("%s.%s.%s" % (test.suitename(), test.path, flags))
+
+ def FetchPerfData(self, test):
+ """Returns the observed duration for |test| as read from the store."""
+ key = self.GetKey(test)
+ if key in self.database:
+ return self.database[key].avg
+ return None
+
+ def UpdatePerfData(self, test):
+ """Updates the persisted value in the store with test.duration."""
+ testkey = self.GetKey(test)
+ self.RawUpdatePerfData(testkey, test.duration)
+
+ def RawUpdatePerfData(self, testkey, duration):
+ with self.lock:
+ if testkey in self.database:
+ entry = self.database[testkey]
+ else:
+ entry = PerfDataEntry()
+ entry.AddResult(duration)
+ self.database[testkey] = entry
+
+
+class PerfDataManager(object):
+ def __init__(self, datadir):
+ self.datadir = os.path.abspath(datadir)
+ if not os.path.exists(self.datadir):
+ os.makedirs(self.datadir)
+ self.stores = {} # Keyed by arch, then mode.
+ self.closed = False
+ self.lock = threading.Lock()
+
+ def __del__(self):
+ self.close()
+
+ def close(self):
+ if self.closed: return
+ for arch in self.stores:
+ modes = self.stores[arch]
+ for mode in modes:
+ store = modes[mode]
+ store.close()
+ self.closed = True
+
+ def GetStore(self, arch, mode):
+ with self.lock:
+ if not arch in self.stores:
+ self.stores[arch] = {}
+ modes = self.stores[arch]
+ if not mode in modes:
+ modes[mode] = PerfDataStore(self.datadir, arch, mode)
+ return modes[mode]
diff --git a/chromium/v8/tools/testrunner/objects/__init__.py b/chromium/v8/tools/testrunner/objects/__init__.py
new file mode 100644
index 00000000000..202a262709c
--- /dev/null
+++ b/chromium/v8/tools/testrunner/objects/__init__.py
@@ -0,0 +1,26 @@
+# Copyright 2012 the V8 project authors. 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.
diff --git a/chromium/v8/tools/testrunner/objects/context.py b/chromium/v8/tools/testrunner/objects/context.py
new file mode 100644
index 00000000000..3ea215a708d
--- /dev/null
+++ b/chromium/v8/tools/testrunner/objects/context.py
@@ -0,0 +1,50 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+class Context():
+ def __init__(self, arch, mode, shell_dir, mode_flags, verbose, timeout,
+ isolates, command_prefix, extra_flags):
+ self.arch = arch
+ self.mode = mode
+ self.shell_dir = shell_dir
+ self.mode_flags = mode_flags
+ self.verbose = verbose
+ self.timeout = timeout
+ self.isolates = isolates
+ self.command_prefix = command_prefix
+ self.extra_flags = extra_flags
+
+ def Pack(self):
+ return [self.arch, self.mode, self.mode_flags, self.timeout, self.isolates,
+ self.command_prefix, self.extra_flags]
+
+ @staticmethod
+ def Unpack(packed):
+ # For the order of the fields, refer to Pack() above.
+ return Context(packed[0], packed[1], None, packed[2], False,
+ packed[3], packed[4], packed[5], packed[6])
diff --git a/chromium/v8/tools/testrunner/objects/output.py b/chromium/v8/tools/testrunner/objects/output.py
new file mode 100644
index 00000000000..87b4c84e199
--- /dev/null
+++ b/chromium/v8/tools/testrunner/objects/output.py
@@ -0,0 +1,60 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import signal
+
+from ..local import utils
+
+class Output(object):
+
+ def __init__(self, exit_code, timed_out, stdout, stderr):
+ self.exit_code = exit_code
+ self.timed_out = timed_out
+ self.stdout = stdout
+ self.stderr = stderr
+
+ def HasCrashed(self):
+ if utils.IsWindows():
+ return 0x80000000 & self.exit_code and not (0x3FFFFF00 & self.exit_code)
+ else:
+ # Timed out tests will have exit_code -signal.SIGTERM.
+ if self.timed_out:
+ return False
+ return (self.exit_code < 0 and
+ self.exit_code != -signal.SIGABRT)
+
+ def HasTimedOut(self):
+ return self.timed_out
+
+ def Pack(self):
+ return [self.exit_code, self.timed_out, self.stdout, self.stderr]
+
+ @staticmethod
+ def Unpack(packed):
+ # For the order of the fields, refer to Pack() above.
+ return Output(packed[0], packed[1], packed[2], packed[3])
diff --git a/chromium/v8/tools/testrunner/objects/peer.py b/chromium/v8/tools/testrunner/objects/peer.py
new file mode 100644
index 00000000000..18a6bec7a8a
--- /dev/null
+++ b/chromium/v8/tools/testrunner/objects/peer.py
@@ -0,0 +1,80 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+class Peer(object):
+ def __init__(self, address, jobs, rel_perf, pubkey):
+ self.address = address # string: IP address
+ self.jobs = jobs # integer: number of CPUs
+ self.relative_performance = rel_perf
+ self.pubkey = pubkey # string: pubkey's fingerprint
+ self.shells = set() # set of strings
+ self.needed_work = 0
+ self.assigned_work = 0
+ self.tests = [] # list of TestCase objects
+ self.trusting_me = False # This peer trusts my public key.
+ self.trusted = False # I trust this peer's public key.
+
+ def __str__(self):
+ return ("Peer at %s, jobs: %d, performance: %.2f, trust I/O: %s/%s" %
+ (self.address, self.jobs, self.relative_performance,
+ self.trusting_me, self.trusted))
+
+ def AddTests(self, shell):
+ """Adds tests from |shell| to this peer.
+
+ Stops when self.needed_work reaches zero, or when all of shell's tests
+ are assigned."""
+ assert self.needed_work > 0
+ if shell.shell not in self.shells:
+ self.shells.add(shell.shell)
+ while len(shell.tests) > 0 and self.needed_work > 0:
+ t = shell.tests.pop()
+ self.needed_work -= t.duration
+ self.assigned_work += t.duration
+ shell.total_duration -= t.duration
+ self.tests.append(t)
+
+ def ForceAddOneTest(self, test, shell):
+ """Forcibly adds another test to this peer, disregarding needed_work."""
+ if shell.shell not in self.shells:
+ self.shells.add(shell.shell)
+ self.needed_work -= test.duration
+ self.assigned_work += test.duration
+ shell.total_duration -= test.duration
+ self.tests.append(test)
+
+
+ def Pack(self):
+ """Creates a JSON serializable representation of this Peer."""
+ return [self.address, self.jobs, self.relative_performance]
+
+ @staticmethod
+ def Unpack(packed):
+ """Creates a Peer object built from a packed representation."""
+ pubkey_dummy = "" # Callers of this don't care (only the server does).
+ return Peer(packed[0], packed[1], packed[2], pubkey_dummy)
diff --git a/chromium/v8/tools/testrunner/objects/testcase.py b/chromium/v8/tools/testrunner/objects/testcase.py
new file mode 100644
index 00000000000..cfc522ea738
--- /dev/null
+++ b/chromium/v8/tools/testrunner/objects/testcase.py
@@ -0,0 +1,83 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+from . import output
+
+class TestCase(object):
+ def __init__(self, suite, path, flags=[], dependency=None):
+ self.suite = suite # TestSuite object
+ self.path = path # string, e.g. 'div-mod', 'test-api/foo'
+ self.flags = flags # list of strings, flags specific to this test case
+ self.dependency = dependency # |path| for testcase that must be run first
+ self.outcomes = None
+ self.output = None
+ self.id = None # int, used to map result back to TestCase instance
+ self.duration = None # assigned during execution
+
+ def CopyAddingFlags(self, flags):
+ copy = TestCase(self.suite, self.path, self.flags + flags, self.dependency)
+ copy.outcomes = self.outcomes
+ return copy
+
+ def PackTask(self):
+ """
+ Extracts those parts of this object that are required to run the test
+ and returns them as a JSON serializable object.
+ """
+ assert self.id is not None
+ return [self.suitename(), self.path, self.flags,
+ self.dependency, list(self.outcomes or []), self.id]
+
+ @staticmethod
+ def UnpackTask(task):
+ """Creates a new TestCase object based on packed task data."""
+ # For the order of the fields, refer to PackTask() above.
+ test = TestCase(str(task[0]), task[1], task[2], task[3])
+ test.outcomes = set(task[4])
+ test.id = task[5]
+ return test
+
+ def SetSuiteObject(self, suites):
+ self.suite = suites[self.suite]
+
+ def PackResult(self):
+ """Serializes the output of the TestCase after it has run."""
+ self.suite.StripOutputForTransmit(self)
+ return [self.id, self.output.Pack(), self.duration]
+
+ def MergeResult(self, result):
+ """Applies the contents of a Result to this object."""
+ assert result[0] == self.id
+ self.output = output.Output.Unpack(result[1])
+ self.duration = result[2]
+
+ def suitename(self):
+ return self.suite.name
+
+ def GetLabel(self):
+ return self.suitename() + "/" + self.suite.CommonTestName(self)
diff --git a/chromium/v8/tools/testrunner/objects/workpacket.py b/chromium/v8/tools/testrunner/objects/workpacket.py
new file mode 100644
index 00000000000..d07efe76ec5
--- /dev/null
+++ b/chromium/v8/tools/testrunner/objects/workpacket.py
@@ -0,0 +1,90 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+from . import context
+from . import testcase
+
+class WorkPacket(object):
+ def __init__(self, peer=None, context=None, tests=None, binaries=None,
+ base_revision=None, patch=None, pubkey=None):
+ self.peer = peer
+ self.context = context
+ self.tests = tests
+ self.binaries = binaries
+ self.base_revision = base_revision
+ self.patch = patch
+ self.pubkey_fingerprint = pubkey
+
+ def Pack(self, binaries_dict):
+ """
+ Creates a JSON serializable object containing the data of this
+ work packet.
+ """
+ need_libv8 = False
+ binaries = []
+ for shell in self.peer.shells:
+ prefetched_binary = binaries_dict[shell]
+ binaries.append({"name": shell,
+ "blob": prefetched_binary[0],
+ "sign": prefetched_binary[1]})
+ if prefetched_binary[2]:
+ need_libv8 = True
+ if need_libv8:
+ libv8 = binaries_dict["libv8.so"]
+ binaries.append({"name": "libv8.so",
+ "blob": libv8[0],
+ "sign": libv8[1]})
+ tests = []
+ test_map = {}
+ for t in self.peer.tests:
+ test_map[t.id] = t
+ tests.append(t.PackTask())
+ result = {
+ "binaries": binaries,
+ "pubkey": self.pubkey_fingerprint,
+ "context": self.context.Pack(),
+ "base_revision": self.base_revision,
+ "patch": self.patch,
+ "tests": tests
+ }
+ return result, test_map
+
+ @staticmethod
+ def Unpack(packed):
+ """
+ Creates a WorkPacket object from the given packed representation.
+ """
+ binaries = packed["binaries"]
+ pubkey_fingerprint = packed["pubkey"]
+ ctx = context.Context.Unpack(packed["context"])
+ base_revision = packed["base_revision"]
+ patch = packed["patch"]
+ tests = [ testcase.TestCase.UnpackTask(t) for t in packed["tests"] ]
+ return WorkPacket(context=ctx, tests=tests, binaries=binaries,
+ base_revision=base_revision, patch=patch,
+ pubkey=pubkey_fingerprint)
diff --git a/chromium/v8/tools/testrunner/server/__init__.py b/chromium/v8/tools/testrunner/server/__init__.py
new file mode 100644
index 00000000000..202a262709c
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/__init__.py
@@ -0,0 +1,26 @@
+# Copyright 2012 the V8 project authors. 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.
diff --git a/chromium/v8/tools/testrunner/server/compression.py b/chromium/v8/tools/testrunner/server/compression.py
new file mode 100644
index 00000000000..d5ed4159766
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/compression.py
@@ -0,0 +1,111 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import cStringIO as StringIO
+try:
+ import ujson as json
+except ImportError:
+ import json
+import os
+import struct
+import zlib
+
+from . import constants
+
+def Send(obj, sock):
+ """
+ Sends a JSON encodable object over the specified socket (zlib-compressed).
+ """
+ obj = json.dumps(obj)
+ compression_level = 2 # 1 = fastest, 9 = best compression
+ compressed = zlib.compress(obj, compression_level)
+ payload = struct.pack('>i', len(compressed)) + compressed
+ sock.sendall(payload)
+
+
+class Receiver(object):
+ def __init__(self, sock):
+ self.sock = sock
+ self.data = StringIO.StringIO()
+ self.datalength = 0
+ self._next = self._GetNext()
+
+ def IsDone(self):
+ return self._next == None
+
+ def Current(self):
+ return self._next
+
+ def Advance(self):
+ try:
+ self._next = self._GetNext()
+ except:
+ raise
+
+ def _GetNext(self):
+ try:
+ while self.datalength < constants.SIZE_T:
+ try:
+ chunk = self.sock.recv(8192)
+ except:
+ raise
+ if not chunk: return None
+ self._AppendData(chunk)
+ size = self._PopData(constants.SIZE_T)
+ size = struct.unpack(">i", size)[0]
+ while self.datalength < size:
+ try:
+ chunk = self.sock.recv(8192)
+ except:
+ raise
+ if not chunk: return None
+ self._AppendData(chunk)
+ result = self._PopData(size)
+ result = zlib.decompress(result)
+ result = json.loads(result)
+ if result == constants.END_OF_STREAM:
+ return None
+ return result
+ except:
+ raise
+
+ def _AppendData(self, new):
+ self.data.seek(0, os.SEEK_END)
+ self.data.write(new)
+ self.datalength += len(new)
+
+ def _PopData(self, length):
+ self.data.seek(0)
+ chunk = self.data.read(length)
+ remaining = self.data.read()
+ self.data.close()
+ self.data = StringIO.StringIO()
+ self.data.write(remaining)
+ assert self.datalength - length == len(remaining)
+ self.datalength = len(remaining)
+ return chunk
diff --git a/chromium/v8/tools/testrunner/server/constants.py b/chromium/v8/tools/testrunner/server/constants.py
new file mode 100644
index 00000000000..5aefcbad0d3
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/constants.py
@@ -0,0 +1,51 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+CLIENT_PORT = 9991 # Port for the local client to connect to.
+PEER_PORT = 9992 # Port for peers on the network to connect to.
+PRESENCE_PORT = 9993 # Port for presence daemon.
+STATUS_PORT = 9994 # Port for network requests not related to workpackets.
+
+END_OF_STREAM = "end of dtest stream" # Marker for end of network requests.
+SIZE_T = 4 # Number of bytes used for network request size header.
+
+# Messages understood by the local request handler.
+ADD_TRUSTED = "add trusted"
+INFORM_DURATION = "inform about duration"
+REQUEST_PEERS = "get peers"
+UNRESPONSIVE_PEER = "unresponsive peer"
+REQUEST_PUBKEY_FINGERPRINT = "get pubkey fingerprint"
+REQUEST_STATUS = "get status"
+UPDATE_PERF = "update performance"
+
+# Messages understood by the status request handler.
+LIST_TRUSTED_PUBKEYS = "list trusted pubkeys"
+GET_SIGNED_PUBKEY = "pass on signed pubkey"
+NOTIFY_NEW_TRUSTED = "new trusted peer"
+TRUST_YOU_NOW = "trust you now"
+DO_YOU_TRUST = "do you trust"
diff --git a/chromium/v8/tools/testrunner/server/daemon.py b/chromium/v8/tools/testrunner/server/daemon.py
new file mode 100644
index 00000000000..baa66fbea91
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/daemon.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+
+# This code has been written by Sander Marechal and published at:
+# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
+# where the author has placed it in the public domain (see comment #6 at
+# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/#c6
+# ).
+# Some minor modifications have been made by the V8 authors. The work remains
+# in the public domain.
+
+import atexit
+import os
+from signal import SIGTERM
+from signal import SIGINT
+import sys
+import time
+
+
+class Daemon(object):
+ """
+ A generic daemon class.
+
+ Usage: subclass the Daemon class and override the run() method
+ """
+ def __init__(self, pidfile, stdin='/dev/null',
+ stdout='/dev/null', stderr='/dev/null'):
+ self.stdin = stdin
+ self.stdout = stdout
+ self.stderr = stderr
+ self.pidfile = pidfile
+
+ def daemonize(self):
+ """
+ do the UNIX double-fork magic, see Stevens' "Advanced
+ Programming in the UNIX Environment" for details (ISBN 0201563177)
+ http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
+ """
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # exit first parent
+ sys.exit(0)
+ except OSError, e:
+ sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ # decouple from parent environment
+ os.chdir("/")
+ os.setsid()
+ os.umask(0)
+
+ # do second fork
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # exit from second parent
+ sys.exit(0)
+ except OSError, e:
+ sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ # redirect standard file descriptors
+ sys.stdout.flush()
+ sys.stderr.flush()
+ si = file(self.stdin, 'r')
+ so = file(self.stdout, 'a+')
+ se = file(self.stderr, 'a+', 0)
+ # TODO: (debug) re-enable this!
+ #os.dup2(si.fileno(), sys.stdin.fileno())
+ #os.dup2(so.fileno(), sys.stdout.fileno())
+ #os.dup2(se.fileno(), sys.stderr.fileno())
+
+ # write pidfile
+ atexit.register(self.delpid)
+ pid = str(os.getpid())
+ file(self.pidfile, 'w+').write("%s\n" % pid)
+
+ def delpid(self):
+ os.remove(self.pidfile)
+
+ def start(self):
+ """
+ Start the daemon
+ """
+ # Check for a pidfile to see if the daemon already runs
+ try:
+ pf = file(self.pidfile, 'r')
+ pid = int(pf.read().strip())
+ pf.close()
+ except IOError:
+ pid = None
+
+ if pid:
+ message = "pidfile %s already exist. Daemon already running?\n"
+ sys.stderr.write(message % self.pidfile)
+ sys.exit(1)
+
+ # Start the daemon
+ self.daemonize()
+ self.run()
+
+ def stop(self):
+ """
+ Stop the daemon
+ """
+ # Get the pid from the pidfile
+ try:
+ pf = file(self.pidfile, 'r')
+ pid = int(pf.read().strip())
+ pf.close()
+ except IOError:
+ pid = None
+
+ if not pid:
+ message = "pidfile %s does not exist. Daemon not running?\n"
+ sys.stderr.write(message % self.pidfile)
+ return # not an error in a restart
+
+ # Try killing the daemon process
+ try:
+ # Give the process a one-second chance to exit gracefully.
+ os.kill(pid, SIGINT)
+ time.sleep(1)
+ while 1:
+ os.kill(pid, SIGTERM)
+ time.sleep(0.1)
+ except OSError, err:
+ err = str(err)
+ if err.find("No such process") > 0:
+ if os.path.exists(self.pidfile):
+ os.remove(self.pidfile)
+ else:
+ print str(err)
+ sys.exit(1)
+
+ def restart(self):
+ """
+ Restart the daemon
+ """
+ self.stop()
+ self.start()
+
+ def run(self):
+ """
+ You should override this method when you subclass Daemon. It will be
+ called after the process has been daemonized by start() or restart().
+ """
diff --git a/chromium/v8/tools/testrunner/server/local_handler.py b/chromium/v8/tools/testrunner/server/local_handler.py
new file mode 100644
index 00000000000..3b3ac495d0c
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/local_handler.py
@@ -0,0 +1,119 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import socket
+import SocketServer
+import StringIO
+
+from . import compression
+from . import constants
+
+
+def LocalQuery(query):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ code = sock.connect_ex(("localhost", constants.CLIENT_PORT))
+ if code != 0: return None
+ compression.Send(query, sock)
+ compression.Send(constants.END_OF_STREAM, sock)
+ rec = compression.Receiver(sock)
+ data = None
+ while not rec.IsDone():
+ data = rec.Current()
+ assert data[0] == query[0]
+ data = data[1]
+ rec.Advance()
+ sock.close()
+ return data
+
+
+class LocalHandler(SocketServer.BaseRequestHandler):
+ def handle(self):
+ rec = compression.Receiver(self.request)
+ while not rec.IsDone():
+ data = rec.Current()
+ action = data[0]
+
+ if action == constants.REQUEST_PEERS:
+ with self.server.daemon.peer_list_lock:
+ response = [ p.Pack() for p in self.server.daemon.peers
+ if p.trusting_me ]
+ compression.Send([action, response], self.request)
+
+ elif action == constants.UNRESPONSIVE_PEER:
+ self.server.daemon.DeletePeer(data[1])
+
+ elif action == constants.REQUEST_PUBKEY_FINGERPRINT:
+ compression.Send([action, self.server.daemon.pubkey_fingerprint],
+ self.request)
+
+ elif action == constants.REQUEST_STATUS:
+ compression.Send([action, self._GetStatusMessage()], self.request)
+
+ elif action == constants.ADD_TRUSTED:
+ fingerprint = self.server.daemon.CopyToTrusted(data[1])
+ compression.Send([action, fingerprint], self.request)
+
+ elif action == constants.INFORM_DURATION:
+ test_key = data[1]
+ test_duration = data[2]
+ arch = data[3]
+ mode = data[4]
+ self.server.daemon.AddPerfData(test_key, test_duration, arch, mode)
+
+ elif action == constants.UPDATE_PERF:
+ address = data[1]
+ perf = data[2]
+ self.server.daemon.UpdatePeerPerformance(data[1], data[2])
+
+ rec.Advance()
+ compression.Send(constants.END_OF_STREAM, self.request)
+
+ def _GetStatusMessage(self):
+ sio = StringIO.StringIO()
+ sio.write("Peers:\n")
+ with self.server.daemon.peer_list_lock:
+ for p in self.server.daemon.peers:
+ sio.write("%s\n" % p)
+ sio.write("My own jobs: %d, relative performance: %.2f\n" %
+ (self.server.daemon.jobs, self.server.daemon.relative_perf))
+ # Low-priority TODO: Return more information. Ideas:
+ # - currently running anything,
+ # - time since last job,
+ # - time since last repository fetch
+ # - number of workpackets/testcases handled since startup
+ # - slowest test(s)
+ result = sio.getvalue()
+ sio.close()
+ return result
+
+
+class LocalSocketServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+ def __init__(self, daemon):
+ SocketServer.TCPServer.__init__(self, ("localhost", constants.CLIENT_PORT),
+ LocalHandler)
+ self.daemon = daemon
diff --git a/chromium/v8/tools/testrunner/server/main.py b/chromium/v8/tools/testrunner/server/main.py
new file mode 100644
index 00000000000..1000713ca93
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/main.py
@@ -0,0 +1,245 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import multiprocessing
+import os
+import shutil
+import subprocess
+import threading
+import time
+
+from . import daemon
+from . import local_handler
+from . import presence_handler
+from . import signatures
+from . import status_handler
+from . import work_handler
+from ..network import perfdata
+
+
+class Server(daemon.Daemon):
+
+ def __init__(self, pidfile, root, stdin="/dev/null",
+ stdout="/dev/null", stderr="/dev/null"):
+ super(Server, self).__init__(pidfile, stdin, stdout, stderr)
+ self.root = root
+ self.local_handler = None
+ self.local_handler_thread = None
+ self.work_handler = None
+ self.work_handler_thread = None
+ self.status_handler = None
+ self.status_handler_thread = None
+ self.presence_daemon = None
+ self.presence_daemon_thread = None
+ self.peers = []
+ self.jobs = multiprocessing.cpu_count()
+ self.peer_list_lock = threading.Lock()
+ self.perf_data_lock = None
+ self.presence_daemon_lock = None
+ self.datadir = os.path.join(self.root, "data")
+ pubkey_fingerprint_filename = os.path.join(self.datadir, "mypubkey")
+ with open(pubkey_fingerprint_filename) as f:
+ self.pubkey_fingerprint = f.read().strip()
+ self.relative_perf_filename = os.path.join(self.datadir, "myperf")
+ if os.path.exists(self.relative_perf_filename):
+ with open(self.relative_perf_filename) as f:
+ try:
+ self.relative_perf = float(f.read())
+ except:
+ self.relative_perf = 1.0
+ else:
+ self.relative_perf = 1.0
+
+ def run(self):
+ os.nice(20)
+ self.ip = presence_handler.GetOwnIP()
+ self.perf_data_manager = perfdata.PerfDataManager(self.datadir)
+ self.perf_data_lock = threading.Lock()
+
+ self.local_handler = local_handler.LocalSocketServer(self)
+ self.local_handler_thread = threading.Thread(
+ target=self.local_handler.serve_forever)
+ self.local_handler_thread.start()
+
+ self.work_handler = work_handler.WorkSocketServer(self)
+ self.work_handler_thread = threading.Thread(
+ target=self.work_handler.serve_forever)
+ self.work_handler_thread.start()
+
+ self.status_handler = status_handler.StatusSocketServer(self)
+ self.status_handler_thread = threading.Thread(
+ target=self.status_handler.serve_forever)
+ self.status_handler_thread.start()
+
+ self.presence_daemon = presence_handler.PresenceDaemon(self)
+ self.presence_daemon_thread = threading.Thread(
+ target=self.presence_daemon.serve_forever)
+ self.presence_daemon_thread.start()
+
+ self.presence_daemon.FindPeers()
+ time.sleep(0.5) # Give those peers some time to reply.
+
+ with self.peer_list_lock:
+ for p in self.peers:
+ if p.address == self.ip: continue
+ status_handler.RequestTrustedPubkeys(p, self)
+
+ while True:
+ try:
+ self.PeriodicTasks()
+ time.sleep(60)
+ except Exception, e:
+ print("MAIN LOOP EXCEPTION: %s" % e)
+ self.Shutdown()
+ break
+ except KeyboardInterrupt:
+ self.Shutdown()
+ break
+
+ def Shutdown(self):
+ with open(self.relative_perf_filename, "w") as f:
+ f.write("%s" % self.relative_perf)
+ self.presence_daemon.shutdown()
+ self.presence_daemon.server_close()
+ self.local_handler.shutdown()
+ self.local_handler.server_close()
+ self.work_handler.shutdown()
+ self.work_handler.server_close()
+ self.status_handler.shutdown()
+ self.status_handler.server_close()
+
+ def PeriodicTasks(self):
+ # If we know peers we don't trust, see if someone else trusts them.
+ with self.peer_list_lock:
+ for p in self.peers:
+ if p.trusted: continue
+ if self.IsTrusted(p.pubkey):
+ p.trusted = True
+ status_handler.ITrustYouNow(p)
+ continue
+ for p2 in self.peers:
+ if not p2.trusted: continue
+ status_handler.TryTransitiveTrust(p2, p.pubkey, self)
+ # TODO: Ping for more peers waiting to be discovered.
+ # TODO: Update the checkout (if currently idle).
+
+ def AddPeer(self, peer):
+ with self.peer_list_lock:
+ for p in self.peers:
+ if p.address == peer.address:
+ return
+ self.peers.append(peer)
+ if peer.trusted:
+ status_handler.ITrustYouNow(peer)
+
+ def DeletePeer(self, peer_address):
+ with self.peer_list_lock:
+ for i in xrange(len(self.peers)):
+ if self.peers[i].address == peer_address:
+ del self.peers[i]
+ return
+
+ def MarkPeerAsTrusting(self, peer_address):
+ with self.peer_list_lock:
+ for p in self.peers:
+ if p.address == peer_address:
+ p.trusting_me = True
+ break
+
+ def UpdatePeerPerformance(self, peer_address, performance):
+ with self.peer_list_lock:
+ for p in self.peers:
+ if p.address == peer_address:
+ p.relative_performance = performance
+
+ def CopyToTrusted(self, pubkey_filename):
+ with open(pubkey_filename, "r") as f:
+ lines = f.readlines()
+ fingerprint = lines[-1].strip()
+ target_filename = self._PubkeyFilename(fingerprint)
+ shutil.copy(pubkey_filename, target_filename)
+ with self.peer_list_lock:
+ for peer in self.peers:
+ if peer.address == self.ip: continue
+ if peer.pubkey == fingerprint:
+ status_handler.ITrustYouNow(peer)
+ else:
+ result = self.SignTrusted(fingerprint)
+ status_handler.NotifyNewTrusted(peer, result)
+ return fingerprint
+
+ def _PubkeyFilename(self, pubkey_fingerprint):
+ return os.path.join(self.root, "trusted", "%s.pem" % pubkey_fingerprint)
+
+ def IsTrusted(self, pubkey_fingerprint):
+ return os.path.exists(self._PubkeyFilename(pubkey_fingerprint))
+
+ def ListTrusted(self):
+ path = os.path.join(self.root, "trusted")
+ if not os.path.exists(path): return []
+ return [ f[:-4] for f in os.listdir(path) if f.endswith(".pem") ]
+
+ def SignTrusted(self, pubkey_fingerprint):
+ if not self.IsTrusted(pubkey_fingerprint):
+ return []
+ filename = self._PubkeyFilename(pubkey_fingerprint)
+ result = signatures.ReadFileAndSignature(filename) # Format: [key, sig].
+ return [pubkey_fingerprint, result[0], result[1], self.pubkey_fingerprint]
+
+ def AcceptNewTrusted(self, data):
+ # The format of |data| matches the return value of |SignTrusted()|.
+ if not data: return
+ fingerprint = data[0]
+ pubkey = data[1]
+ signature = data[2]
+ signer = data[3]
+ if not self.IsTrusted(signer):
+ return
+ if self.IsTrusted(fingerprint):
+ return # Already trust this guy.
+ filename = self._PubkeyFilename(fingerprint)
+ signer_pubkeyfile = self._PubkeyFilename(signer)
+ if not signatures.VerifySignature(filename, pubkey, signature,
+ signer_pubkeyfile):
+ return
+ return # Nothing more to do.
+
+ def AddPerfData(self, test_key, duration, arch, mode):
+ data_store = self.perf_data_manager.GetStore(arch, mode)
+ data_store.RawUpdatePerfData(str(test_key), duration)
+
+ def CompareOwnPerf(self, test, arch, mode):
+ data_store = self.perf_data_manager.GetStore(arch, mode)
+ observed = data_store.FetchPerfData(test)
+ if not observed: return
+ own_perf_estimate = observed / test.duration
+ with self.perf_data_lock:
+ kLearnRateLimiter = 9999
+ self.relative_perf *= kLearnRateLimiter
+ self.relative_perf += own_perf_estimate
+ self.relative_perf /= (kLearnRateLimiter + 1)
diff --git a/chromium/v8/tools/testrunner/server/presence_handler.py b/chromium/v8/tools/testrunner/server/presence_handler.py
new file mode 100644
index 00000000000..1dc2ef163ab
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/presence_handler.py
@@ -0,0 +1,120 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import socket
+import SocketServer
+import threading
+try:
+ import ujson as json
+except:
+ import json
+
+from . import constants
+from ..objects import peer
+
+
+STARTUP_REQUEST = "V8 test peer starting up"
+STARTUP_RESPONSE = "Let's rock some tests!"
+EXIT_REQUEST = "V8 testing peer going down"
+
+
+def GetOwnIP():
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(("8.8.8.8", 80))
+ ip = s.getsockname()[0]
+ s.close()
+ return ip
+
+
+class PresenceHandler(SocketServer.BaseRequestHandler):
+
+ def handle(self):
+ data = json.loads(self.request[0].strip())
+
+ if data[0] == STARTUP_REQUEST:
+ jobs = data[1]
+ relative_perf = data[2]
+ pubkey_fingerprint = data[3]
+ trusted = self.server.daemon.IsTrusted(pubkey_fingerprint)
+ response = [STARTUP_RESPONSE, self.server.daemon.jobs,
+ self.server.daemon.relative_perf,
+ self.server.daemon.pubkey_fingerprint, trusted]
+ response = json.dumps(response)
+ self.server.SendTo(self.client_address[0], response)
+ p = peer.Peer(self.client_address[0], jobs, relative_perf,
+ pubkey_fingerprint)
+ p.trusted = trusted
+ self.server.daemon.AddPeer(p)
+
+ elif data[0] == STARTUP_RESPONSE:
+ jobs = data[1]
+ perf = data[2]
+ pubkey_fingerprint = data[3]
+ p = peer.Peer(self.client_address[0], jobs, perf, pubkey_fingerprint)
+ p.trusted = self.server.daemon.IsTrusted(pubkey_fingerprint)
+ p.trusting_me = data[4]
+ self.server.daemon.AddPeer(p)
+
+ elif data[0] == EXIT_REQUEST:
+ self.server.daemon.DeletePeer(self.client_address[0])
+ if self.client_address[0] == self.server.daemon.ip:
+ self.server.shutdown_lock.release()
+
+
+class PresenceDaemon(SocketServer.ThreadingMixIn, SocketServer.UDPServer):
+ def __init__(self, daemon):
+ self.daemon = daemon
+ address = (daemon.ip, constants.PRESENCE_PORT)
+ SocketServer.UDPServer.__init__(self, address, PresenceHandler)
+ self.shutdown_lock = threading.Lock()
+
+ def shutdown(self):
+ self.shutdown_lock.acquire()
+ self.SendToAll(json.dumps([EXIT_REQUEST]))
+ self.shutdown_lock.acquire()
+ self.shutdown_lock.release()
+ SocketServer.UDPServer.shutdown(self)
+
+ def SendTo(self, target, message):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.sendto(message, (target, constants.PRESENCE_PORT))
+ sock.close()
+
+ def SendToAll(self, message):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ ip = self.daemon.ip.split(".")
+ for i in range(1, 254):
+ ip[-1] = str(i)
+ sock.sendto(message, (".".join(ip), constants.PRESENCE_PORT))
+ sock.close()
+
+ def FindPeers(self):
+ request = [STARTUP_REQUEST, self.daemon.jobs, self.daemon.relative_perf,
+ self.daemon.pubkey_fingerprint]
+ request = json.dumps(request)
+ self.SendToAll(request)
diff --git a/chromium/v8/tools/testrunner/server/signatures.py b/chromium/v8/tools/testrunner/server/signatures.py
new file mode 100644
index 00000000000..9957a18a267
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/signatures.py
@@ -0,0 +1,63 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import base64
+import os
+import subprocess
+
+
+def ReadFileAndSignature(filename):
+ with open(filename, "rb") as f:
+ file_contents = base64.b64encode(f.read())
+ signature_file = filename + ".signature"
+ if (not os.path.exists(signature_file) or
+ os.path.getmtime(signature_file) < os.path.getmtime(filename)):
+ private_key = "~/.ssh/v8_dtest"
+ code = subprocess.call("openssl dgst -out %s -sign %s %s" %
+ (signature_file, private_key, filename),
+ shell=True)
+ if code != 0: return [None, code]
+ with open(signature_file) as f:
+ signature = base64.b64encode(f.read())
+ return [file_contents, signature]
+
+
+def VerifySignature(filename, file_contents, signature, pubkeyfile):
+ with open(filename, "wb") as f:
+ f.write(base64.b64decode(file_contents))
+ signature_file = filename + ".foreign_signature"
+ with open(signature_file, "wb") as f:
+ f.write(base64.b64decode(signature))
+ code = subprocess.call("openssl dgst -verify %s -signature %s %s" %
+ (pubkeyfile, signature_file, filename),
+ shell=True)
+ matched = (code == 0)
+ if not matched:
+ os.remove(signature_file)
+ os.remove(filename)
+ return matched
diff --git a/chromium/v8/tools/testrunner/server/status_handler.py b/chromium/v8/tools/testrunner/server/status_handler.py
new file mode 100644
index 00000000000..3f2271dc697
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/status_handler.py
@@ -0,0 +1,112 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import socket
+import SocketServer
+
+from . import compression
+from . import constants
+
+
+def _StatusQuery(peer, query):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ code = sock.connect_ex((peer.address, constants.STATUS_PORT))
+ if code != 0:
+ # TODO(jkummerow): disconnect (after 3 failures?)
+ return
+ compression.Send(query, sock)
+ compression.Send(constants.END_OF_STREAM, sock)
+ rec = compression.Receiver(sock)
+ data = None
+ while not rec.IsDone():
+ data = rec.Current()
+ assert data[0] == query[0]
+ data = data[1]
+ rec.Advance()
+ sock.close()
+ return data
+
+
+def RequestTrustedPubkeys(peer, server):
+ pubkey_list = _StatusQuery(peer, [constants.LIST_TRUSTED_PUBKEYS])
+ for pubkey in pubkey_list:
+ if server.IsTrusted(pubkey): continue
+ result = _StatusQuery(peer, [constants.GET_SIGNED_PUBKEY, pubkey])
+ server.AcceptNewTrusted(result)
+
+
+def NotifyNewTrusted(peer, data):
+ _StatusQuery(peer, [constants.NOTIFY_NEW_TRUSTED] + data)
+
+
+def ITrustYouNow(peer):
+ _StatusQuery(peer, [constants.TRUST_YOU_NOW])
+
+
+def TryTransitiveTrust(peer, pubkey, server):
+ if _StatusQuery(peer, [constants.DO_YOU_TRUST, pubkey]):
+ result = _StatusQuery(peer, [constants.GET_SIGNED_PUBKEY, pubkey])
+ server.AcceptNewTrusted(result)
+
+
+class StatusHandler(SocketServer.BaseRequestHandler):
+ def handle(self):
+ rec = compression.Receiver(self.request)
+ while not rec.IsDone():
+ data = rec.Current()
+ action = data[0]
+
+ if action == constants.LIST_TRUSTED_PUBKEYS:
+ response = self.server.daemon.ListTrusted()
+ compression.Send([action, response], self.request)
+
+ elif action == constants.GET_SIGNED_PUBKEY:
+ response = self.server.daemon.SignTrusted(data[1])
+ compression.Send([action, response], self.request)
+
+ elif action == constants.NOTIFY_NEW_TRUSTED:
+ self.server.daemon.AcceptNewTrusted(data[1:])
+ pass # No response.
+
+ elif action == constants.TRUST_YOU_NOW:
+ self.server.daemon.MarkPeerAsTrusting(self.client_address[0])
+ pass # No response.
+
+ elif action == constants.DO_YOU_TRUST:
+ response = self.server.daemon.IsTrusted(data[1])
+ compression.Send([action, response], self.request)
+
+ rec.Advance()
+ compression.Send(constants.END_OF_STREAM, self.request)
+
+
+class StatusSocketServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+ def __init__(self, daemon):
+ address = (daemon.ip, constants.STATUS_PORT)
+ SocketServer.TCPServer.__init__(self, address, StatusHandler)
+ self.daemon = daemon
diff --git a/chromium/v8/tools/testrunner/server/work_handler.py b/chromium/v8/tools/testrunner/server/work_handler.py
new file mode 100644
index 00000000000..6bf7d43cf94
--- /dev/null
+++ b/chromium/v8/tools/testrunner/server/work_handler.py
@@ -0,0 +1,150 @@
+# Copyright 2012 the V8 project authors. 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.
+
+
+import os
+import SocketServer
+import stat
+import subprocess
+import threading
+
+from . import compression
+from . import constants
+from . import signatures
+from ..network import endpoint
+from ..objects import workpacket
+
+
+class WorkHandler(SocketServer.BaseRequestHandler):
+
+ def handle(self):
+ rec = compression.Receiver(self.request)
+ while not rec.IsDone():
+ data = rec.Current()
+ with self.server.job_lock:
+ self._WorkOnWorkPacket(data)
+ rec.Advance()
+
+ def _WorkOnWorkPacket(self, data):
+ server_root = self.server.daemon.root
+ v8_root = os.path.join(server_root, "v8")
+ os.chdir(v8_root)
+ packet = workpacket.WorkPacket.Unpack(data)
+ self.ctx = packet.context
+ self.ctx.shell_dir = os.path.join("out",
+ "%s.%s" % (self.ctx.arch, self.ctx.mode))
+ if not os.path.isdir(self.ctx.shell_dir):
+ os.makedirs(self.ctx.shell_dir)
+ for binary in packet.binaries:
+ if not self._UnpackBinary(binary, packet.pubkey_fingerprint):
+ return
+
+ if not self._CheckoutRevision(packet.base_revision):
+ return
+
+ if not self._ApplyPatch(packet.patch):
+ return
+
+ tests = packet.tests
+ endpoint.Execute(v8_root, self.ctx, tests, self.request, self.server.daemon)
+ self._SendResponse()
+
+ def _SendResponse(self, error_message=None):
+ try:
+ if error_message:
+ compression.Send([[-1, error_message]], self.request)
+ compression.Send(constants.END_OF_STREAM, self.request)
+ return
+ except Exception, e:
+ pass # Peer is gone. There's nothing we can do.
+ # Clean up.
+ self._Call("git checkout -f")
+ self._Call("git clean -f -d")
+ self._Call("rm -rf %s" % self.ctx.shell_dir)
+
+ def _UnpackBinary(self, binary, pubkey_fingerprint):
+ binary_name = binary["name"]
+ if binary_name == "libv8.so":
+ libdir = os.path.join(self.ctx.shell_dir, "lib.target")
+ if not os.path.exists(libdir): os.makedirs(libdir)
+ target = os.path.join(libdir, binary_name)
+ else:
+ target = os.path.join(self.ctx.shell_dir, binary_name)
+ pubkeyfile = "../trusted/%s.pem" % pubkey_fingerprint
+ if not signatures.VerifySignature(target, binary["blob"],
+ binary["sign"], pubkeyfile):
+ self._SendResponse("Signature verification failed")
+ return False
+ os.chmod(target, stat.S_IRWXU)
+ return True
+
+ def _CheckoutRevision(self, base_svn_revision):
+ get_hash_cmd = (
+ "git log -1 --format=%%H --remotes --grep='^git-svn-id:.*@%s'" %
+ base_svn_revision)
+ try:
+ base_revision = subprocess.check_output(get_hash_cmd, shell=True)
+ if not base_revision: raise ValueError
+ except:
+ self._Call("git fetch")
+ try:
+ base_revision = subprocess.check_output(get_hash_cmd, shell=True)
+ if not base_revision: raise ValueError
+ except:
+ self._SendResponse("Base revision not found.")
+ return False
+ code = self._Call("git checkout -f %s" % base_revision)
+ if code != 0:
+ self._SendResponse("Error trying to check out base revision.")
+ return False
+ code = self._Call("git clean -f -d")
+ if code != 0:
+ self._SendResponse("Failed to reset checkout")
+ return False
+ return True
+
+ def _ApplyPatch(self, patch):
+ if not patch: return True # Just skip if the patch is empty.
+ patchfilename = "_dtest_incoming_patch.patch"
+ with open(patchfilename, "w") as f:
+ f.write(patch)
+ code = self._Call("git apply %s" % patchfilename)
+ if code != 0:
+ self._SendResponse("Error applying patch.")
+ return False
+ return True
+
+ def _Call(self, cmd):
+ return subprocess.call(cmd, shell=True)
+
+
+class WorkSocketServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+ def __init__(self, daemon):
+ address = (daemon.ip, constants.PEER_PORT)
+ SocketServer.TCPServer.__init__(self, address, WorkHandler)
+ self.job_lock = threading.Lock()
+ self.daemon = daemon
diff --git a/chromium/v8/tools/tick-processor.html b/chromium/v8/tools/tick-processor.html
new file mode 100644
index 00000000000..bc9f636cb7f
--- /dev/null
+++ b/chromium/v8/tools/tick-processor.html
@@ -0,0 +1,168 @@
+<!DOCTYPE html>
+<!-- Copyright 2012 the V8 project authors. 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. -->
+
+<html lang="en">
+<head>
+ <meta charset="utf-8"/>
+ <title>V8 Tick Processor</title>
+
+ <style type="text/css">
+ body {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 10pt;
+ }
+ h4 {
+ margin-bottom: 0px;
+ }
+ p {
+ margin-top: 0px;
+ }
+ </style>
+
+ <script src="splaytree.js"></script>
+ <script src="codemap.js"></script>
+ <script src="csvparser.js"></script>
+ <script src="consarray.js"></script>
+ <script src="profile.js"></script>
+ <script src="profile_view.js"></script>
+ <script src="logreader.js"></script>
+ <script src="tickprocessor.js"></script>
+
+ <script type="text/javascript">
+
+var v8log_content;
+var textout;
+
+function load_logfile(evt) {
+ textout.value = "";
+ var f = evt.target.files[0];
+ if (f) {
+ var reader = new FileReader();
+ reader.onload = function(event) {
+ v8log_content = event.target.result;
+ start_process();
+ };
+ reader.onerror = function(event) {
+ console.error("File could not be read! Code " + event.target.error.code);
+ };
+ reader.readAsText(f);
+ } else {
+ alert("Failed to load file");
+ }
+}
+
+function print(arg) {
+ textout.value+=arg+"\n";
+}
+
+function start_process() {
+ ArgumentsProcessor.DEFAULTS = {
+ logFileName: 'v8.log',
+ snapshotLogFileName: null,
+ platform: 'unix',
+ stateFilter: null,
+ callGraphSize: 5,
+ ignoreUnknown: false,
+ separateIc: false,
+ targetRootFS: '',
+ nm: 'nm'
+ };
+
+ var entriesProviders = {
+ 'unix': UnixCppEntriesProvider,
+ 'windows': WindowsCppEntriesProvider,
+ 'mac': MacCppEntriesProvider
+ };
+
+ var snapshotLogProcessor; // not used
+
+ var tickProcessor = new TickProcessor(
+ new (entriesProviders[ArgumentsProcessor.DEFAULTS.platform])(
+ ArgumentsProcessor.DEFAULTS.nm,
+ ArgumentsProcessor.DEFAULTS.targetRootFS),
+ ArgumentsProcessor.DEFAULTS.separateIc,
+ ArgumentsProcessor.DEFAULTS.callGraphSize,
+ ArgumentsProcessor.DEFAULTS.ignoreUnknown,
+ ArgumentsProcessor.DEFAULTS.stateFilter,
+ snapshotLogProcessor);
+
+ tickProcessor.processLogChunk(v8log_content);
+ tickProcessor.printStatistics();
+}
+
+function Load() {
+ document.getElementById('fileinput').addEventListener(
+ 'change', load_logfile, false);
+ textout = document.getElementById('textout');
+}
+</script>
+</head>
+<body onLoad="Load()">
+
+<h3 style="margin-top: 2px;">
+ Chrome V8 profiling log processor
+</h3>
+<p>
+Process V8's profiling information log (sampling profiler tick information)
+in your browser. Particularly useful if you don't have the V8 shell (d8)
+at hand on your system. You still have to run Chrome with the appropriate
+<a href="https://code.google.com/p/v8/wiki/ProfilingChromiumWithV8">
+ command line flags</a>
+to produce the profiling log.
+</p>
+<h4>Usage:</h4>
+<p>
+Click on the button and browse to the profiling log file (usually, v8.log).
+Process will start automatically and the output will be visible in the below
+text area.
+</p>
+<h4>Limitations and disclaimer:</h4>
+<p>
+This page offers a subset of the functionalities of the command-line tick
+processor utility in the V8 repository. In particular, this page cannot
+access the command-line utility that provides library symbol information,
+hence the [C++] section of the output stays empty. Also consider that this
+web-based tool is provided only for convenience and quick reference, you
+should refer to the
+<a href="https://code.google.com/p/v8/wiki/V8Profiler">
+ command-line</a>
+version for full output.
+</p>
+<p>
+<input type="file" id="fileinput" />
+</p>
+<p>
+<textarea name="myTextArea" cols="120" rows="40" wrap="off" id="textout"
+ readonly="yes"></textarea>
+</p>
+<p style="font-style:italic;">
+Copyright the V8 Authors - Last change to this page: 12/12/2012
+</p>
+
+
+</body>
+</html>
diff --git a/chromium/v8/tools/tickprocessor-driver.js b/chromium/v8/tools/tickprocessor-driver.js
new file mode 100644
index 00000000000..02cb81a55fc
--- /dev/null
+++ b/chromium/v8/tools/tickprocessor-driver.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.
+
+
+// Tick Processor's code flow.
+
+function processArguments(args) {
+ var processor = new ArgumentsProcessor(args);
+ if (processor.parse()) {
+ return processor.result();
+ } else {
+ processor.printUsageAndExit();
+ }
+}
+
+var entriesProviders = {
+ 'unix': UnixCppEntriesProvider,
+ 'windows': WindowsCppEntriesProvider,
+ 'mac': MacCppEntriesProvider
+};
+
+var params = processArguments(arguments);
+var snapshotLogProcessor;
+if (params.snapshotLogFileName) {
+ snapshotLogProcessor = new SnapshotLogProcessor();
+ snapshotLogProcessor.processLogFile(params.snapshotLogFileName);
+}
+var tickProcessor = new TickProcessor(
+ new (entriesProviders[params.platform])(params.nm, params.targetRootFS),
+ params.separateIc,
+ params.callGraphSize,
+ params.ignoreUnknown,
+ params.stateFilter,
+ snapshotLogProcessor,
+ params.distortion,
+ params.range);
+tickProcessor.processLogFile(params.logFileName);
+tickProcessor.printStatistics();
diff --git a/chromium/v8/tools/tickprocessor.js b/chromium/v8/tools/tickprocessor.js
new file mode 100644
index 00000000000..f1a11ccc948
--- /dev/null
+++ b/chromium/v8/tools/tickprocessor.js
@@ -0,0 +1,913 @@
+// Copyright 2012 the V8 project authors. 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.
+
+
+function inherits(childCtor, parentCtor) {
+ childCtor.prototype.__proto__ = parentCtor.prototype;
+};
+
+
+function V8Profile(separateIc) {
+ Profile.call(this);
+ if (!separateIc) {
+ this.skipThisFunction = function(name) { return V8Profile.IC_RE.test(name); };
+ }
+};
+inherits(V8Profile, Profile);
+
+
+V8Profile.IC_RE =
+ /^(?:CallIC|LoadIC|StoreIC)|(?:Builtin: (?:Keyed)?(?:Call|Load|Store)IC_)/;
+
+
+/**
+ * A thin wrapper around shell's 'read' function showing a file name on error.
+ */
+function readFile(fileName) {
+ try {
+ return read(fileName);
+ } catch (e) {
+ print(fileName + ': ' + (e.message || e));
+ throw e;
+ }
+}
+
+
+/**
+ * Parser for dynamic code optimization state.
+ */
+function parseState(s) {
+ switch (s) {
+ case "": return Profile.CodeState.COMPILED;
+ case "~": return Profile.CodeState.OPTIMIZABLE;
+ case "*": return Profile.CodeState.OPTIMIZED;
+ }
+ throw new Error("unknown code state: " + s);
+}
+
+
+function SnapshotLogProcessor() {
+ LogReader.call(this, {
+ 'code-creation': {
+ parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'],
+ processor: this.processCodeCreation },
+ 'code-move': { parsers: [parseInt, parseInt],
+ processor: this.processCodeMove },
+ 'code-delete': { parsers: [parseInt],
+ processor: this.processCodeDelete },
+ 'function-creation': null,
+ 'function-move': null,
+ 'function-delete': null,
+ 'sfi-move': null,
+ 'snapshot-pos': { parsers: [parseInt, parseInt],
+ processor: this.processSnapshotPosition }});
+
+ V8Profile.prototype.handleUnknownCode = function(operation, addr) {
+ var op = Profile.Operation;
+ switch (operation) {
+ case op.MOVE:
+ print('Snapshot: Code move event for unknown code: 0x' +
+ addr.toString(16));
+ break;
+ case op.DELETE:
+ print('Snapshot: Code delete event for unknown code: 0x' +
+ addr.toString(16));
+ break;
+ }
+ };
+
+ this.profile_ = new V8Profile();
+ this.serializedEntries_ = [];
+}
+inherits(SnapshotLogProcessor, LogReader);
+
+
+SnapshotLogProcessor.prototype.processCodeCreation = function(
+ type, kind, start, size, name, maybe_func) {
+ if (maybe_func.length) {
+ var funcAddr = parseInt(maybe_func[0]);
+ var state = parseState(maybe_func[1]);
+ this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
+ } else {
+ this.profile_.addCode(type, name, start, size);
+ }
+};
+
+
+SnapshotLogProcessor.prototype.processCodeMove = function(from, to) {
+ this.profile_.moveCode(from, to);
+};
+
+
+SnapshotLogProcessor.prototype.processCodeDelete = function(start) {
+ this.profile_.deleteCode(start);
+};
+
+
+SnapshotLogProcessor.prototype.processSnapshotPosition = function(addr, pos) {
+ this.serializedEntries_[pos] = this.profile_.findEntry(addr);
+};
+
+
+SnapshotLogProcessor.prototype.processLogFile = function(fileName) {
+ var contents = readFile(fileName);
+ this.processLogChunk(contents);
+};
+
+
+SnapshotLogProcessor.prototype.getSerializedEntryName = function(pos) {
+ var entry = this.serializedEntries_[pos];
+ return entry ? entry.getRawName() : null;
+};
+
+
+function TickProcessor(
+ cppEntriesProvider,
+ separateIc,
+ callGraphSize,
+ ignoreUnknown,
+ stateFilter,
+ snapshotLogProcessor,
+ distortion,
+ range) {
+ LogReader.call(this, {
+ 'shared-library': { parsers: [null, parseInt, parseInt],
+ processor: this.processSharedLibrary },
+ 'code-creation': {
+ parsers: [null, parseInt, parseInt, parseInt, null, 'var-args'],
+ processor: this.processCodeCreation },
+ 'code-move': { parsers: [parseInt, parseInt],
+ processor: this.processCodeMove },
+ 'code-delete': { parsers: [parseInt],
+ processor: this.processCodeDelete },
+ 'sfi-move': { parsers: [parseInt, parseInt],
+ processor: this.processFunctionMove },
+ 'snapshot-pos': { parsers: [parseInt, parseInt],
+ processor: this.processSnapshotPosition },
+ 'tick': {
+ parsers: [parseInt, parseInt, parseInt,
+ parseInt, parseInt, 'var-args'],
+ processor: this.processTick },
+ 'heap-sample-begin': { parsers: [null, null, parseInt],
+ processor: this.processHeapSampleBegin },
+ 'heap-sample-end': { parsers: [null, null],
+ processor: this.processHeapSampleEnd },
+ 'timer-event-start' : { parsers: [null, null, null],
+ processor: this.advanceDistortion },
+ 'timer-event-end' : { parsers: [null, null, null],
+ processor: this.advanceDistortion },
+ // Ignored events.
+ 'profiler': null,
+ 'function-creation': null,
+ 'function-move': null,
+ 'function-delete': null,
+ 'heap-sample-item': null,
+ // Obsolete row types.
+ 'code-allocate': null,
+ 'begin-code-region': null,
+ 'end-code-region': null });
+
+ this.cppEntriesProvider_ = cppEntriesProvider;
+ this.callGraphSize_ = callGraphSize;
+ this.ignoreUnknown_ = ignoreUnknown;
+ this.stateFilter_ = stateFilter;
+ this.snapshotLogProcessor_ = snapshotLogProcessor;
+ this.deserializedEntriesNames_ = [];
+ var ticks = this.ticks_ =
+ { total: 0, unaccounted: 0, excluded: 0, gc: 0 };
+
+ distortion = parseInt(distortion);
+ // Convert picoseconds to nanoseconds.
+ this.distortion_per_entry = isNaN(distortion) ? 0 : (distortion / 1000);
+ this.distortion = 0;
+ var rangelimits = range ? range.split(",") : [];
+ var range_start = parseInt(rangelimits[0]);
+ var range_end = parseInt(rangelimits[1]);
+ // Convert milliseconds to nanoseconds.
+ this.range_start = isNaN(range_start) ? -Infinity : (range_start * 1000);
+ this.range_end = isNaN(range_end) ? Infinity : (range_end * 1000)
+
+ V8Profile.prototype.handleUnknownCode = function(
+ operation, addr, opt_stackPos) {
+ var op = Profile.Operation;
+ switch (operation) {
+ case op.MOVE:
+ print('Code move event for unknown code: 0x' + addr.toString(16));
+ break;
+ case op.DELETE:
+ print('Code delete event for unknown code: 0x' + addr.toString(16));
+ break;
+ case op.TICK:
+ // Only unknown PCs (the first frame) are reported as unaccounted,
+ // otherwise tick balance will be corrupted (this behavior is compatible
+ // with the original tickprocessor.py script.)
+ if (opt_stackPos == 0) {
+ ticks.unaccounted++;
+ }
+ break;
+ }
+ };
+
+ this.profile_ = new V8Profile(separateIc);
+ this.codeTypes_ = {};
+ // Count each tick as a time unit.
+ this.viewBuilder_ = new ViewBuilder(1);
+ this.lastLogFileName_ = null;
+
+ this.generation_ = 1;
+ this.currentProducerProfile_ = null;
+};
+inherits(TickProcessor, LogReader);
+
+
+TickProcessor.VmStates = {
+ JS: 0,
+ GC: 1,
+ COMPILER: 2,
+ OTHER: 3,
+ EXTERNAL: 4,
+ IDLE: 5
+};
+
+
+TickProcessor.CodeTypes = {
+ CPP: 0,
+ SHARED_LIB: 1
+};
+// Otherwise, this is JS-related code. We are not adding it to
+// codeTypes_ map because there can be zillions of them.
+
+
+TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0;
+
+TickProcessor.CALL_GRAPH_SIZE = 5;
+
+/**
+ * @override
+ */
+TickProcessor.prototype.printError = function(str) {
+ print(str);
+};
+
+
+TickProcessor.prototype.setCodeType = function(name, type) {
+ this.codeTypes_[name] = TickProcessor.CodeTypes[type];
+};
+
+
+TickProcessor.prototype.isSharedLibrary = function(name) {
+ return this.codeTypes_[name] == TickProcessor.CodeTypes.SHARED_LIB;
+};
+
+
+TickProcessor.prototype.isCppCode = function(name) {
+ return this.codeTypes_[name] == TickProcessor.CodeTypes.CPP;
+};
+
+
+TickProcessor.prototype.isJsCode = function(name) {
+ return !(name in this.codeTypes_);
+};
+
+
+TickProcessor.prototype.processLogFile = function(fileName) {
+ this.lastLogFileName_ = fileName;
+ var line;
+ while (line = readline()) {
+ this.processLogLine(line);
+ }
+};
+
+
+TickProcessor.prototype.processLogFileInTest = function(fileName) {
+ // Hack file name to avoid dealing with platform specifics.
+ this.lastLogFileName_ = 'v8.log';
+ var contents = readFile(fileName);
+ this.processLogChunk(contents);
+};
+
+
+TickProcessor.prototype.processSharedLibrary = function(
+ name, startAddr, endAddr) {
+ var entry = this.profile_.addLibrary(name, startAddr, endAddr);
+ this.setCodeType(entry.getName(), 'SHARED_LIB');
+
+ var self = this;
+ var libFuncs = this.cppEntriesProvider_.parseVmSymbols(
+ name, startAddr, endAddr, function(fName, fStart, fEnd) {
+ self.profile_.addStaticCode(fName, fStart, fEnd);
+ self.setCodeType(fName, 'CPP');
+ });
+};
+
+
+TickProcessor.prototype.processCodeCreation = function(
+ type, kind, start, size, name, maybe_func) {
+ name = this.deserializedEntriesNames_[start] || name;
+ if (maybe_func.length) {
+ var funcAddr = parseInt(maybe_func[0]);
+ var state = parseState(maybe_func[1]);
+ this.profile_.addFuncCode(type, name, start, size, funcAddr, state);
+ } else {
+ this.profile_.addCode(type, name, start, size);
+ }
+};
+
+
+TickProcessor.prototype.processCodeMove = function(from, to) {
+ this.profile_.moveCode(from, to);
+};
+
+
+TickProcessor.prototype.processCodeDelete = function(start) {
+ this.profile_.deleteCode(start);
+};
+
+
+TickProcessor.prototype.processFunctionMove = function(from, to) {
+ this.profile_.moveFunc(from, to);
+};
+
+
+TickProcessor.prototype.processSnapshotPosition = function(addr, pos) {
+ if (this.snapshotLogProcessor_) {
+ this.deserializedEntriesNames_[addr] =
+ this.snapshotLogProcessor_.getSerializedEntryName(pos);
+ }
+};
+
+
+TickProcessor.prototype.includeTick = function(vmState) {
+ return this.stateFilter_ == null || this.stateFilter_ == vmState;
+};
+
+TickProcessor.prototype.processTick = function(pc,
+ ns_since_start,
+ is_external_callback,
+ tos_or_external_callback,
+ vmState,
+ stack) {
+ this.distortion += this.distortion_per_entry;
+ ns_since_start -= this.distortion;
+ if (ns_since_start < this.range_start || ns_since_start > this.range_end) {
+ return;
+ }
+ this.ticks_.total++;
+ if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
+ if (!this.includeTick(vmState)) {
+ this.ticks_.excluded++;
+ return;
+ }
+ if (is_external_callback) {
+ // Don't use PC when in external callback code, as it can point
+ // inside callback's code, and we will erroneously report
+ // that a callback calls itself. Instead we use tos_or_external_callback,
+ // as simply resetting PC will produce unaccounted ticks.
+ pc = tos_or_external_callback;
+ tos_or_external_callback = 0;
+ } else if (tos_or_external_callback) {
+ // Find out, if top of stack was pointing inside a JS function
+ // meaning that we have encountered a frameless invocation.
+ var funcEntry = this.profile_.findEntry(tos_or_external_callback);
+ if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) {
+ tos_or_external_callback = 0;
+ }
+ }
+
+ this.profile_.recordTick(this.processStack(pc, tos_or_external_callback, stack));
+};
+
+
+TickProcessor.prototype.advanceDistortion = function() {
+ this.distortion += this.distortion_per_entry;
+}
+
+
+TickProcessor.prototype.processHeapSampleBegin = function(space, state, ticks) {
+ if (space != 'Heap') return;
+ this.currentProducerProfile_ = new CallTree();
+};
+
+
+TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
+ if (space != 'Heap' || !this.currentProducerProfile_) return;
+
+ print('Generation ' + this.generation_ + ':');
+ var tree = this.currentProducerProfile_;
+ tree.computeTotalWeights();
+ var producersView = this.viewBuilder_.buildView(tree);
+ // Sort by total time, desc, then by name, desc.
+ producersView.sort(function(rec1, rec2) {
+ return rec2.totalTime - rec1.totalTime ||
+ (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
+ this.printHeavyProfile(producersView.head.children);
+
+ this.currentProducerProfile_ = null;
+ this.generation_++;
+};
+
+
+TickProcessor.prototype.printStatistics = function() {
+ print('Statistical profiling result from ' + this.lastLogFileName_ +
+ ', (' + this.ticks_.total +
+ ' ticks, ' + this.ticks_.unaccounted + ' unaccounted, ' +
+ this.ticks_.excluded + ' excluded).');
+
+ if (this.ticks_.total == 0) return;
+
+ // Print the unknown ticks percentage if they are not ignored.
+ if (!this.ignoreUnknown_ && this.ticks_.unaccounted > 0) {
+ this.printHeader('Unknown');
+ this.printCounter(this.ticks_.unaccounted, this.ticks_.total);
+ }
+
+ var flatProfile = this.profile_.getFlatProfile();
+ var flatView = this.viewBuilder_.buildView(flatProfile);
+ // Sort by self time, desc, then by name, desc.
+ flatView.sort(function(rec1, rec2) {
+ return rec2.selfTime - rec1.selfTime ||
+ (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
+ var totalTicks = this.ticks_.total;
+ if (this.ignoreUnknown_) {
+ totalTicks -= this.ticks_.unaccounted;
+ }
+ // Our total time contains all the ticks encountered,
+ // while profile only knows about the filtered ticks.
+ flatView.head.totalTime = totalTicks;
+
+ // Count library ticks
+ var flatViewNodes = flatView.head.children;
+ var self = this;
+ var libraryTicks = 0;
+ this.processProfile(flatViewNodes,
+ function(name) { return self.isSharedLibrary(name); },
+ function(rec) { libraryTicks += rec.selfTime; });
+ var nonLibraryTicks = totalTicks - libraryTicks;
+
+ this.printHeader('Shared libraries');
+ this.printEntries(flatViewNodes, null,
+ function(name) { return self.isSharedLibrary(name); });
+
+ this.printHeader('JavaScript');
+ this.printEntries(flatViewNodes, nonLibraryTicks,
+ function(name) { return self.isJsCode(name); });
+
+ this.printHeader('C++');
+ this.printEntries(flatViewNodes, nonLibraryTicks,
+ function(name) { return self.isCppCode(name); });
+
+ this.printHeader('GC');
+ this.printCounter(this.ticks_.gc, totalTicks);
+
+ this.printHeavyProfHeader();
+ var heavyProfile = this.profile_.getBottomUpProfile();
+ var heavyView = this.viewBuilder_.buildView(heavyProfile);
+ // To show the same percentages as in the flat profile.
+ heavyView.head.totalTime = totalTicks;
+ // Sort by total time, desc, then by name, desc.
+ heavyView.sort(function(rec1, rec2) {
+ return rec2.totalTime - rec1.totalTime ||
+ (rec2.internalFuncName < rec1.internalFuncName ? -1 : 1); });
+ this.printHeavyProfile(heavyView.head.children);
+};
+
+
+function padLeft(s, len) {
+ s = s.toString();
+ if (s.length < len) {
+ var padLength = len - s.length;
+ if (!(padLength in padLeft)) {
+ padLeft[padLength] = new Array(padLength + 1).join(' ');
+ }
+ s = padLeft[padLength] + s;
+ }
+ return s;
+};
+
+
+TickProcessor.prototype.printHeader = function(headerTitle) {
+ print('\n [' + headerTitle + ']:');
+ print(' ticks total nonlib name');
+};
+
+
+TickProcessor.prototype.printHeavyProfHeader = function() {
+ print('\n [Bottom up (heavy) profile]:');
+ print(' Note: percentage shows a share of a particular caller in the ' +
+ 'total\n' +
+ ' amount of its parent calls.');
+ print(' Callers occupying less than ' +
+ TickProcessor.CALL_PROFILE_CUTOFF_PCT.toFixed(1) +
+ '% are not shown.\n');
+ print(' ticks parent name');
+};
+
+
+TickProcessor.prototype.printCounter = function(ticksCount, totalTicksCount) {
+ var pct = ticksCount * 100.0 / totalTicksCount;
+ print(' ' + padLeft(ticksCount, 5) + ' ' + padLeft(pct.toFixed(1), 5) + '%');
+};
+
+
+TickProcessor.prototype.processProfile = function(
+ profile, filterP, func) {
+ for (var i = 0, n = profile.length; i < n; ++i) {
+ var rec = profile[i];
+ if (!filterP(rec.internalFuncName)) {
+ continue;
+ }
+ func(rec);
+ }
+};
+
+
+TickProcessor.prototype.printEntries = function(
+ profile, nonLibTicks, filterP) {
+ this.processProfile(profile, filterP, function (rec) {
+ if (rec.selfTime == 0) return;
+ var nonLibPct = nonLibTicks != null ?
+ rec.selfTime * 100.0 / nonLibTicks : 0.0;
+ print(' ' + padLeft(rec.selfTime, 5) + ' ' +
+ padLeft(rec.selfPercent.toFixed(1), 5) + '% ' +
+ padLeft(nonLibPct.toFixed(1), 5) + '% ' +
+ rec.internalFuncName);
+ });
+};
+
+
+TickProcessor.prototype.printHeavyProfile = function(profile, opt_indent) {
+ var self = this;
+ var indent = opt_indent || 0;
+ var indentStr = padLeft('', indent);
+ this.processProfile(profile, function() { return true; }, function (rec) {
+ // Cut off too infrequent callers.
+ if (rec.parentTotalPercent < TickProcessor.CALL_PROFILE_CUTOFF_PCT) return;
+ print(' ' + padLeft(rec.totalTime, 5) + ' ' +
+ padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' +
+ indentStr + rec.internalFuncName);
+ // Limit backtrace depth.
+ if (indent < 2 * self.callGraphSize_) {
+ self.printHeavyProfile(rec.children, indent + 2);
+ }
+ // Delimit top-level functions.
+ if (indent == 0) {
+ print('');
+ }
+ });
+};
+
+
+function CppEntriesProvider() {
+};
+
+
+CppEntriesProvider.prototype.parseVmSymbols = function(
+ libName, libStart, libEnd, processorFunc) {
+ this.loadSymbols(libName);
+
+ var prevEntry;
+
+ function addEntry(funcInfo) {
+ // Several functions can be mapped onto the same address. To avoid
+ // creating zero-sized entries, skip such duplicates.
+ // Also double-check that function belongs to the library address space.
+ if (prevEntry && !prevEntry.end &&
+ prevEntry.start < funcInfo.start &&
+ prevEntry.start >= libStart && funcInfo.start <= libEnd) {
+ processorFunc(prevEntry.name, prevEntry.start, funcInfo.start);
+ }
+ if (funcInfo.end &&
+ (!prevEntry || prevEntry.start != funcInfo.start) &&
+ funcInfo.start >= libStart && funcInfo.end <= libEnd) {
+ processorFunc(funcInfo.name, funcInfo.start, funcInfo.end);
+ }
+ prevEntry = funcInfo;
+ }
+
+ while (true) {
+ var funcInfo = this.parseNextLine();
+ if (funcInfo === null) {
+ continue;
+ } else if (funcInfo === false) {
+ break;
+ }
+ if (funcInfo.start < libStart && funcInfo.start < libEnd - libStart) {
+ funcInfo.start += libStart;
+ }
+ if (funcInfo.size) {
+ funcInfo.end = funcInfo.start + funcInfo.size;
+ }
+ addEntry(funcInfo);
+ }
+ addEntry({name: '', start: libEnd});
+};
+
+
+CppEntriesProvider.prototype.loadSymbols = function(libName) {
+};
+
+
+CppEntriesProvider.prototype.parseNextLine = function() {
+ return false;
+};
+
+
+function UnixCppEntriesProvider(nmExec, targetRootFS) {
+ this.symbols = [];
+ this.parsePos = 0;
+ this.nmExec = nmExec;
+ this.targetRootFS = targetRootFS;
+ this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ([0-9a-fA-F]{8,16} )?[tTwW] (.*)$/;
+};
+inherits(UnixCppEntriesProvider, CppEntriesProvider);
+
+
+UnixCppEntriesProvider.prototype.loadSymbols = function(libName) {
+ this.parsePos = 0;
+ libName = this.targetRootFS + libName;
+ try {
+ this.symbols = [
+ os.system(this.nmExec, ['-C', '-n', '-S', libName], -1, -1),
+ os.system(this.nmExec, ['-C', '-n', '-S', '-D', libName], -1, -1)
+ ];
+ } catch (e) {
+ // If the library cannot be found on this system let's not panic.
+ this.symbols = ['', ''];
+ }
+};
+
+
+UnixCppEntriesProvider.prototype.parseNextLine = function() {
+ if (this.symbols.length == 0) {
+ return false;
+ }
+ var lineEndPos = this.symbols[0].indexOf('\n', this.parsePos);
+ if (lineEndPos == -1) {
+ this.symbols.shift();
+ this.parsePos = 0;
+ return this.parseNextLine();
+ }
+
+ var line = this.symbols[0].substring(this.parsePos, lineEndPos);
+ this.parsePos = lineEndPos + 1;
+ var fields = line.match(this.FUNC_RE);
+ var funcInfo = null;
+ if (fields) {
+ funcInfo = { name: fields[3], start: parseInt(fields[1], 16) };
+ if (fields[2]) {
+ funcInfo.size = parseInt(fields[2], 16);
+ }
+ }
+ return funcInfo;
+};
+
+
+function MacCppEntriesProvider(nmExec, targetRootFS) {
+ UnixCppEntriesProvider.call(this, nmExec, targetRootFS);
+ // Note an empty group. It is required, as UnixCppEntriesProvider expects 3 groups.
+ this.FUNC_RE = /^([0-9a-fA-F]{8,16}) ()[iItT] (.*)$/;
+};
+inherits(MacCppEntriesProvider, UnixCppEntriesProvider);
+
+
+MacCppEntriesProvider.prototype.loadSymbols = function(libName) {
+ this.parsePos = 0;
+ libName = this.targetRootFS + libName;
+ try {
+ this.symbols = [os.system(this.nmExec, ['-n', '-f', libName], -1, -1), ''];
+ } catch (e) {
+ // If the library cannot be found on this system let's not panic.
+ this.symbols = '';
+ }
+};
+
+
+function WindowsCppEntriesProvider(_ignored_nmExec, targetRootFS) {
+ this.targetRootFS = targetRootFS;
+ this.symbols = '';
+ this.parsePos = 0;
+};
+inherits(WindowsCppEntriesProvider, CppEntriesProvider);
+
+
+WindowsCppEntriesProvider.FILENAME_RE = /^(.*)\.([^.]+)$/;
+
+
+WindowsCppEntriesProvider.FUNC_RE =
+ /^\s+0001:[0-9a-fA-F]{8}\s+([_\?@$0-9a-zA-Z]+)\s+([0-9a-fA-F]{8}).*$/;
+
+
+WindowsCppEntriesProvider.IMAGE_BASE_RE =
+ /^\s+0000:00000000\s+___ImageBase\s+([0-9a-fA-F]{8}).*$/;
+
+
+// This is almost a constant on Windows.
+WindowsCppEntriesProvider.EXE_IMAGE_BASE = 0x00400000;
+
+
+WindowsCppEntriesProvider.prototype.loadSymbols = function(libName) {
+ libName = this.targetRootFS + libName;
+ var fileNameFields = libName.match(WindowsCppEntriesProvider.FILENAME_RE);
+ if (!fileNameFields) return;
+ var mapFileName = fileNameFields[1] + '.map';
+ this.moduleType_ = fileNameFields[2].toLowerCase();
+ try {
+ this.symbols = read(mapFileName);
+ } catch (e) {
+ // If .map file cannot be found let's not panic.
+ this.symbols = '';
+ }
+};
+
+
+WindowsCppEntriesProvider.prototype.parseNextLine = function() {
+ var lineEndPos = this.symbols.indexOf('\r\n', this.parsePos);
+ if (lineEndPos == -1) {
+ return false;
+ }
+
+ var line = this.symbols.substring(this.parsePos, lineEndPos);
+ this.parsePos = lineEndPos + 2;
+
+ // Image base entry is above all other symbols, so we can just
+ // terminate parsing.
+ var imageBaseFields = line.match(WindowsCppEntriesProvider.IMAGE_BASE_RE);
+ if (imageBaseFields) {
+ var imageBase = parseInt(imageBaseFields[1], 16);
+ if ((this.moduleType_ == 'exe') !=
+ (imageBase == WindowsCppEntriesProvider.EXE_IMAGE_BASE)) {
+ return false;
+ }
+ }
+
+ var fields = line.match(WindowsCppEntriesProvider.FUNC_RE);
+ return fields ?
+ { name: this.unmangleName(fields[1]), start: parseInt(fields[2], 16) } :
+ null;
+};
+
+
+/**
+ * Performs very simple unmangling of C++ names.
+ *
+ * Does not handle arguments and template arguments. The mangled names have
+ * the form:
+ *
+ * ?LookupInDescriptor@JSObject@internal@v8@@...arguments info...
+ */
+WindowsCppEntriesProvider.prototype.unmangleName = function(name) {
+ // Empty or non-mangled name.
+ if (name.length < 1 || name.charAt(0) != '?') return name;
+ var nameEndPos = name.indexOf('@@');
+ var components = name.substring(1, nameEndPos).split('@');
+ components.reverse();
+ return components.join('::');
+};
+
+
+function ArgumentsProcessor(args) {
+ this.args_ = args;
+ this.result_ = ArgumentsProcessor.DEFAULTS;
+
+ this.argsDispatch_ = {
+ '-j': ['stateFilter', TickProcessor.VmStates.JS,
+ 'Show only ticks from JS VM state'],
+ '-g': ['stateFilter', TickProcessor.VmStates.GC,
+ 'Show only ticks from GC VM state'],
+ '-c': ['stateFilter', TickProcessor.VmStates.COMPILER,
+ 'Show only ticks from COMPILER VM state'],
+ '-o': ['stateFilter', TickProcessor.VmStates.OTHER,
+ 'Show only ticks from OTHER VM state'],
+ '-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
+ 'Show only ticks from EXTERNAL VM state'],
+ '--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE,
+ 'Set the call graph size'],
+ '--ignore-unknown': ['ignoreUnknown', true,
+ 'Exclude ticks of unknown code entries from processing'],
+ '--separate-ic': ['separateIc', true,
+ 'Separate IC entries'],
+ '--unix': ['platform', 'unix',
+ 'Specify that we are running on *nix platform'],
+ '--windows': ['platform', 'windows',
+ 'Specify that we are running on Windows platform'],
+ '--mac': ['platform', 'mac',
+ 'Specify that we are running on Mac OS X platform'],
+ '--nm': ['nm', 'nm',
+ 'Specify the \'nm\' executable to use (e.g. --nm=/my_dir/nm)'],
+ '--target': ['targetRootFS', '',
+ 'Specify the target root directory for cross environment'],
+ '--snapshot-log': ['snapshotLogFileName', 'snapshot.log',
+ 'Specify snapshot log file to use (e.g. --snapshot-log=snapshot.log)'],
+ '--range': ['range', 'auto,auto',
+ 'Specify the range limit as [start],[end]'],
+ '--distortion': ['distortion', 0,
+ 'Specify the logging overhead in picoseconds']
+ };
+ this.argsDispatch_['--js'] = this.argsDispatch_['-j'];
+ this.argsDispatch_['--gc'] = this.argsDispatch_['-g'];
+ this.argsDispatch_['--compiler'] = this.argsDispatch_['-c'];
+ this.argsDispatch_['--other'] = this.argsDispatch_['-o'];
+ this.argsDispatch_['--external'] = this.argsDispatch_['-e'];
+};
+
+
+ArgumentsProcessor.DEFAULTS = {
+ logFileName: 'v8.log',
+ snapshotLogFileName: null,
+ platform: 'unix',
+ stateFilter: null,
+ callGraphSize: 5,
+ ignoreUnknown: false,
+ separateIc: false,
+ targetRootFS: '',
+ nm: 'nm',
+ range: 'auto,auto',
+ distortion: 0
+};
+
+
+ArgumentsProcessor.prototype.parse = function() {
+ while (this.args_.length) {
+ var arg = this.args_[0];
+ if (arg.charAt(0) != '-') {
+ break;
+ }
+ this.args_.shift();
+ var userValue = null;
+ var eqPos = arg.indexOf('=');
+ if (eqPos != -1) {
+ userValue = arg.substr(eqPos + 1);
+ arg = arg.substr(0, eqPos);
+ }
+ if (arg in this.argsDispatch_) {
+ var dispatch = this.argsDispatch_[arg];
+ this.result_[dispatch[0]] = userValue == null ? dispatch[1] : userValue;
+ } else {
+ return false;
+ }
+ }
+
+ if (this.args_.length >= 1) {
+ this.result_.logFileName = this.args_.shift();
+ }
+ return true;
+};
+
+
+ArgumentsProcessor.prototype.result = function() {
+ return this.result_;
+};
+
+
+ArgumentsProcessor.prototype.printUsageAndExit = function() {
+
+ function padRight(s, len) {
+ s = s.toString();
+ if (s.length < len) {
+ s = s + (new Array(len - s.length + 1).join(' '));
+ }
+ return s;
+ }
+
+ print('Cmdline args: [options] [log-file-name]\n' +
+ 'Default log file name is "' +
+ ArgumentsProcessor.DEFAULTS.logFileName + '".\n');
+ print('Options:');
+ for (var arg in this.argsDispatch_) {
+ var synonims = [arg];
+ var dispatch = this.argsDispatch_[arg];
+ for (var synArg in this.argsDispatch_) {
+ if (arg !== synArg && dispatch === this.argsDispatch_[synArg]) {
+ synonims.push(synArg);
+ delete this.argsDispatch_[synArg];
+ }
+ }
+ print(' ' + padRight(synonims.join(', '), 20) + dispatch[2]);
+ }
+ quit(2);
+};
+
diff --git a/chromium/v8/tools/v8-info.sh b/chromium/v8/tools/v8-info.sh
new file mode 100755
index 00000000000..1f25d147a57
--- /dev/null
+++ b/chromium/v8/tools/v8-info.sh
@@ -0,0 +1,161 @@
+#!/bin/bash
+# Copyright 2013 the V8 project authors. 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
+
+BASE_URL="https://code.google.com/p/v8/source/list"
+VERSION="src/version.cc"
+MAJOR="MAJOR_VERSION"
+MINOR="MINOR_VERSION"
+BUILD="BUILD_NUMBER"
+PATCH="PATCH_LEVEL"
+
+V8="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+
+########## Function definitions
+
+cd $V8
+
+usage() {
+cat << EOF
+usage: $0 OPTIONS
+
+Fetches V8 revision information from a git-svn checkout.
+
+OPTIONS:
+ -h Show this message.
+
+ -i Print revision info for all branches matching the V8 version.
+ Example usage: $0 -i 3.19.10$
+ Output format: [Git hash] [SVN revision] [V8 version]
+
+ -v Print the V8 version tag for a trunk SVN revision.
+ Example usage: $0 -v 14981
+ Output format: [V8 version]
+
+ -m Print all patches that were merged to the specified V8 branch.
+ Example usage: $0 -m 3.18
+ Output format: [V8 version] [SVN revision] [SVN patch merged]*.
+
+ -p Print all patches merged to a specific V8 point-release.
+ Example usage: $0 -p 3.19.12.1
+ Output format: [SVN patch merged]*
+
+ -u Print a link to all SVN revisions between two V8 revision tags.
+ Example usage: $0 -u 3.19.10:3.19.11
+EOF
+}
+
+tags() {
+ git for-each-ref --format="%(objectname) %(refname:short)" refs/remotes/svn
+}
+
+tag_revision() {
+ cut -d" " -f1
+}
+
+tag_log() {
+ git log --format="%h %ci %ce %s" -1 $1
+}
+
+v8_hash() {
+ tags | grep "svn/tags/$1$" | tag_revision
+}
+
+point_merges() {
+ echo $1 | grep -o "r[0-9]\+"
+}
+
+hash_to_svn() {
+ git svn log -1 --oneline $1 | cut -d" " -f1
+}
+
+tag_version() {
+ tags | grep svn/tags/$1 | while read tag; do
+ id=$(echo $tag | grep -o "[^/]*$")
+ rev=$(echo $tag | tag_revision)
+ svn=$(hash_to_svn $rev)
+ echo $rev $svn $id
+ done
+}
+
+svn_rev() {
+ git svn find-rev $2 svn/$1
+}
+
+v8_rev() {
+ cd $(git rev-parse --show-toplevel)
+ rev=$(git show $1:$VERSION \
+ | grep "#define" \
+ | grep "$MAJOR\|$MINOR\|$BUILD\|$PATCH" \
+ | grep -o "[0-9]\+$" \
+ | tr "\\n" ".")
+ echo ${rev%?}
+}
+
+merges_to_branch() {
+ git cherry -v svn/trunk svn/$1 | while read merge; do
+ h=$(echo $merge | cut -d" " -f2)
+ svn=$(svn_rev $1 $h)
+ merges=$(echo $merge | grep -o "r[0-9]\+")
+ rev=$(v8_rev $h)
+ echo $rev r$svn $merges
+ done
+}
+
+url_for() {
+ first=$(svn_rev trunk $(v8_hash $(echo $1 | cut -d":" -f1)))
+ last=$(svn_rev trunk $(v8_hash $(echo $1 | cut -d":" -f2)))
+ num=$[ $last - $first]
+ echo "$BASE_URL?num=$num&start=$last"
+}
+
+########## Option parsing
+
+while getopts ":hi:v:m:p:u:" OPTION ; do
+ case $OPTION in
+ h) usage
+ exit 0
+ ;;
+ i) tag_version $OPTARG
+ ;;
+ v) v8_rev $(svn_rev trunk r$OPTARG)
+ ;;
+ m) merges_to_branch $OPTARG
+ ;;
+ p) echo $(point_merges "$(tag_log $(v8_hash $OPTARG)^1)")
+ ;;
+ u) url_for $OPTARG
+ ;;
+ ?) echo "Illegal option: -$OPTARG"
+ usage
+ exit 1
+ ;;
+ esac
+done
diff --git a/chromium/v8/tools/v8-rolls.sh b/chromium/v8/tools/v8-rolls.sh
new file mode 100755
index 00000000000..590e05c1f9c
--- /dev/null
+++ b/chromium/v8/tools/v8-rolls.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+# Copyright 2013 the V8 project authors. 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
+
+DEPS_STRING='"v8_revision":'
+INFO=tools/v8-info.sh
+
+V8="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+
+########## Function definitions
+
+usage() {
+cat << EOF
+usage: $0 OPTIONS
+
+Run in chromium/src to get information about V8 rolls.
+
+OPTIONS:
+ -h Show this message.
+ -n Number of rolls to print information about.
+ -s Chromium git hash to start printing V8 information about.
+EOF
+}
+
+v8_line() {
+ git show $1:DEPS | grep -n $DEPS_STRING | cut -d":" -f1
+}
+
+v8_info() {
+ git blame -L$(v8_line $1),+1 $1 DEPS | grep $DEPS_STRING
+}
+
+v8_svn() {
+ sed -e 's/^.*"\([0-9]\+\)",$/\1/'
+}
+
+v8_roll() {
+ cut -d" " -f1
+}
+
+find_rev() {
+ git svn find-rev $1
+}
+
+msg() {
+ msg=$(git log --format="%h %ci %ce" -1 $1)
+ h=$(echo $msg | cut -d" " -f1)
+ d=$(echo $msg | cut -d" " -f2)
+ t=$(echo $msg | cut -d" " -f3)
+ a=$(echo $msg | cut -d" " -f5)
+ a1=$(echo $a | cut -d"@" -f1)
+ a2=$(echo $a | cut -d"@" -f2)
+ echo $h $d $t $a1@$a2
+}
+
+v8_revision() {
+ cd $V8
+ $INFO -v $1
+}
+
+rolls() {
+ roll=$2
+ for i in $(seq 1 $1); do
+ info=$(v8_info $roll)
+ roll=$(echo $info | v8_roll $roll)
+ trunk=$(echo $info | v8_svn $roll)
+ echo "$(v8_revision $trunk) $trunk $(find_rev $roll) $(msg $roll)"
+ roll=$roll^1
+ done
+}
+
+########## Option parsing
+
+REVISIONS=1
+START=HEAD
+
+while getopts ":hn:s:" OPTION ; do
+ case $OPTION in
+ h) usage
+ exit 0
+ ;;
+ n) REVISIONS=$OPTARG
+ ;;
+ s) START=$OPTARG
+ ;;
+ ?) echo "Illegal option: -$OPTARG"
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+rolls $REVISIONS $START
diff --git a/chromium/v8/tools/v8.xcodeproj/README.txt b/chromium/v8/tools/v8.xcodeproj/README.txt
new file mode 100644
index 00000000000..e064ff6bf14
--- /dev/null
+++ b/chromium/v8/tools/v8.xcodeproj/README.txt
@@ -0,0 +1,11 @@
+The Xcode project for V8 has been retired. If an Xcode project
+is needed for building on a Mac there is the option of using GYP to
+generate it. Please look in the build directory in the root of the
+V8 project. It contains the required infrastructure and a README.txt
+file explaining how to get started.
+
+Generating Xcode projects using GYP is how the Chromium
+project integrated V8 into the Mac build.
+
+The main build system for V8 is still SCons, see
+http://code.google.com/apis/v8/build.html for details.
diff --git a/chromium/v8/tools/v8heapconst.py b/chromium/v8/tools/v8heapconst.py
new file mode 100644
index 00000000000..57a1f595ace
--- /dev/null
+++ b/chromium/v8/tools/v8heapconst.py
@@ -0,0 +1,255 @@
+# Copyright 2013 the V8 project authors. 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 is automatically generated from the V8 source and should not
+# be modified manually, run 'make grokdump' instead to update this file.
+
+# List of known V8 instance types.
+INSTANCE_TYPES = {
+ 64: "STRING_TYPE",
+ 68: "ASCII_STRING_TYPE",
+ 65: "CONS_STRING_TYPE",
+ 69: "CONS_ASCII_STRING_TYPE",
+ 67: "SLICED_STRING_TYPE",
+ 71: "SLICED_ASCII_STRING_TYPE",
+ 66: "EXTERNAL_STRING_TYPE",
+ 70: "EXTERNAL_ASCII_STRING_TYPE",
+ 74: "EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE",
+ 82: "SHORT_EXTERNAL_STRING_TYPE",
+ 86: "SHORT_EXTERNAL_ASCII_STRING_TYPE",
+ 90: "SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE",
+ 0: "INTERNALIZED_STRING_TYPE",
+ 4: "ASCII_INTERNALIZED_STRING_TYPE",
+ 1: "CONS_INTERNALIZED_STRING_TYPE",
+ 5: "CONS_ASCII_INTERNALIZED_STRING_TYPE",
+ 2: "EXTERNAL_INTERNALIZED_STRING_TYPE",
+ 6: "EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE",
+ 10: "EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE",
+ 18: "SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE",
+ 22: "SHORT_EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE",
+ 26: "SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE",
+ 128: "SYMBOL_TYPE",
+ 129: "MAP_TYPE",
+ 130: "CODE_TYPE",
+ 131: "ODDBALL_TYPE",
+ 132: "CELL_TYPE",
+ 133: "PROPERTY_CELL_TYPE",
+ 134: "HEAP_NUMBER_TYPE",
+ 135: "FOREIGN_TYPE",
+ 136: "BYTE_ARRAY_TYPE",
+ 137: "FREE_SPACE_TYPE",
+ 138: "EXTERNAL_BYTE_ARRAY_TYPE",
+ 139: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE",
+ 140: "EXTERNAL_SHORT_ARRAY_TYPE",
+ 141: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE",
+ 142: "EXTERNAL_INT_ARRAY_TYPE",
+ 143: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE",
+ 144: "EXTERNAL_FLOAT_ARRAY_TYPE",
+ 145: "EXTERNAL_DOUBLE_ARRAY_TYPE",
+ 146: "EXTERNAL_PIXEL_ARRAY_TYPE",
+ 148: "FILLER_TYPE",
+ 149: "DECLARED_ACCESSOR_DESCRIPTOR_TYPE",
+ 150: "DECLARED_ACCESSOR_INFO_TYPE",
+ 151: "EXECUTABLE_ACCESSOR_INFO_TYPE",
+ 152: "ACCESSOR_PAIR_TYPE",
+ 153: "ACCESS_CHECK_INFO_TYPE",
+ 154: "INTERCEPTOR_INFO_TYPE",
+ 155: "CALL_HANDLER_INFO_TYPE",
+ 156: "FUNCTION_TEMPLATE_INFO_TYPE",
+ 157: "OBJECT_TEMPLATE_INFO_TYPE",
+ 158: "SIGNATURE_INFO_TYPE",
+ 159: "TYPE_SWITCH_INFO_TYPE",
+ 161: "ALLOCATION_MEMENTO_TYPE",
+ 160: "ALLOCATION_SITE_TYPE",
+ 162: "SCRIPT_TYPE",
+ 163: "CODE_CACHE_TYPE",
+ 164: "POLYMORPHIC_CODE_CACHE_TYPE",
+ 165: "TYPE_FEEDBACK_INFO_TYPE",
+ 166: "ALIASED_ARGUMENTS_ENTRY_TYPE",
+ 167: "BOX_TYPE",
+ 170: "FIXED_ARRAY_TYPE",
+ 147: "FIXED_DOUBLE_ARRAY_TYPE",
+ 171: "SHARED_FUNCTION_INFO_TYPE",
+ 172: "JS_MESSAGE_OBJECT_TYPE",
+ 175: "JS_VALUE_TYPE",
+ 176: "JS_DATE_TYPE",
+ 177: "JS_OBJECT_TYPE",
+ 178: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
+ 179: "JS_GENERATOR_OBJECT_TYPE",
+ 180: "JS_MODULE_TYPE",
+ 181: "JS_GLOBAL_OBJECT_TYPE",
+ 182: "JS_BUILTINS_OBJECT_TYPE",
+ 183: "JS_GLOBAL_PROXY_TYPE",
+ 184: "JS_ARRAY_TYPE",
+ 185: "JS_ARRAY_BUFFER_TYPE",
+ 186: "JS_TYPED_ARRAY_TYPE",
+ 187: "JS_DATA_VIEW_TYPE",
+ 174: "JS_PROXY_TYPE",
+ 188: "JS_SET_TYPE",
+ 189: "JS_MAP_TYPE",
+ 190: "JS_WEAK_MAP_TYPE",
+ 191: "JS_WEAK_SET_TYPE",
+ 192: "JS_REGEXP_TYPE",
+ 193: "JS_FUNCTION_TYPE",
+ 173: "JS_FUNCTION_PROXY_TYPE",
+ 168: "DEBUG_INFO_TYPE",
+ 169: "BREAK_POINT_INFO_TYPE",
+}
+
+# List of known V8 maps.
+KNOWN_MAPS = {
+ 0x08081: (136, "ByteArrayMap"),
+ 0x080a9: (129, "MetaMap"),
+ 0x080d1: (131, "OddballMap"),
+ 0x080f9: (4, "AsciiInternalizedStringMap"),
+ 0x08121: (170, "FixedArrayMap"),
+ 0x08149: (134, "HeapNumberMap"),
+ 0x08171: (137, "FreeSpaceMap"),
+ 0x08199: (148, "OnePointerFillerMap"),
+ 0x081c1: (148, "TwoPointerFillerMap"),
+ 0x081e9: (132, "CellMap"),
+ 0x08211: (133, "GlobalPropertyCellMap"),
+ 0x08239: (171, "SharedFunctionInfoMap"),
+ 0x08261: (170, "NativeContextMap"),
+ 0x08289: (130, "CodeMap"),
+ 0x082b1: (170, "ScopeInfoMap"),
+ 0x082d9: (170, "FixedCOWArrayMap"),
+ 0x08301: (147, "FixedDoubleArrayMap"),
+ 0x08329: (170, "HashTableMap"),
+ 0x08351: (128, "SymbolMap"),
+ 0x08379: (64, "StringMap"),
+ 0x083a1: (68, "AsciiStringMap"),
+ 0x083c9: (65, "ConsStringMap"),
+ 0x083f1: (69, "ConsAsciiStringMap"),
+ 0x08419: (67, "SlicedStringMap"),
+ 0x08441: (71, "SlicedAsciiStringMap"),
+ 0x08469: (66, "ExternalStringMap"),
+ 0x08491: (74, "ExternalStringWithOneByteDataMap"),
+ 0x084b9: (70, "ExternalAsciiStringMap"),
+ 0x084e1: (82, "ShortExternalStringMap"),
+ 0x08509: (90, "ShortExternalStringWithOneByteDataMap"),
+ 0x08531: (0, "InternalizedStringMap"),
+ 0x08559: (1, "ConsInternalizedStringMap"),
+ 0x08581: (5, "ConsAsciiInternalizedStringMap"),
+ 0x085a9: (2, "ExternalInternalizedStringMap"),
+ 0x085d1: (10, "ExternalInternalizedStringWithOneByteDataMap"),
+ 0x085f9: (6, "ExternalAsciiInternalizedStringMap"),
+ 0x08621: (18, "ShortExternalInternalizedStringMap"),
+ 0x08649: (26, "ShortExternalInternalizedStringWithOneByteDataMap"),
+ 0x08671: (22, "ShortExternalAsciiInternalizedStringMap"),
+ 0x08699: (86, "ShortExternalAsciiStringMap"),
+ 0x086c1: (64, "UndetectableStringMap"),
+ 0x086e9: (68, "UndetectableAsciiStringMap"),
+ 0x08711: (138, "ExternalByteArrayMap"),
+ 0x08739: (139, "ExternalUnsignedByteArrayMap"),
+ 0x08761: (140, "ExternalShortArrayMap"),
+ 0x08789: (141, "ExternalUnsignedShortArrayMap"),
+ 0x087b1: (142, "ExternalIntArrayMap"),
+ 0x087d9: (143, "ExternalUnsignedIntArrayMap"),
+ 0x08801: (144, "ExternalFloatArrayMap"),
+ 0x08829: (145, "ExternalDoubleArrayMap"),
+ 0x08851: (146, "ExternalPixelArrayMap"),
+ 0x08879: (170, "NonStrictArgumentsElementsMap"),
+ 0x088a1: (170, "FunctionContextMap"),
+ 0x088c9: (170, "CatchContextMap"),
+ 0x088f1: (170, "WithContextMap"),
+ 0x08919: (170, "BlockContextMap"),
+ 0x08941: (170, "ModuleContextMap"),
+ 0x08969: (170, "GlobalContextMap"),
+ 0x08991: (172, "JSMessageObjectMap"),
+ 0x089b9: (135, "ForeignMap"),
+ 0x089e1: (177, "NeanderMap"),
+ 0x08a09: (161, "AllocationMementoMap"),
+ 0x08a31: (160, "AllocationSiteMap"),
+ 0x08a59: (164, "PolymorphicCodeCacheMap"),
+ 0x08a81: (162, "ScriptMap"),
+ 0x08ad1: (177, "ExternalMap"),
+ 0x08af9: (167, "BoxMap"),
+ 0x08b21: (149, "DeclaredAccessorDescriptorMap"),
+ 0x08b49: (150, "DeclaredAccessorInfoMap"),
+ 0x08b71: (151, "ExecutableAccessorInfoMap"),
+ 0x08b99: (152, "AccessorPairMap"),
+ 0x08bc1: (153, "AccessCheckInfoMap"),
+ 0x08be9: (154, "InterceptorInfoMap"),
+ 0x08c11: (155, "CallHandlerInfoMap"),
+ 0x08c39: (156, "FunctionTemplateInfoMap"),
+ 0x08c61: (157, "ObjectTemplateInfoMap"),
+ 0x08c89: (158, "SignatureInfoMap"),
+ 0x08cb1: (159, "TypeSwitchInfoMap"),
+ 0x08cd9: (163, "CodeCacheMap"),
+ 0x08d01: (165, "TypeFeedbackInfoMap"),
+ 0x08d29: (166, "AliasedArgumentsEntryMap"),
+ 0x08d51: (168, "DebugInfoMap"),
+ 0x08d79: (169, "BreakPointInfoMap"),
+}
+
+# List of known V8 objects.
+KNOWN_OBJECTS = {
+ ("OLD_POINTER_SPACE", 0x08081): "NullValue",
+ ("OLD_POINTER_SPACE", 0x08091): "UndefinedValue",
+ ("OLD_POINTER_SPACE", 0x080a1): "TheHoleValue",
+ ("OLD_POINTER_SPACE", 0x080b1): "TrueValue",
+ ("OLD_POINTER_SPACE", 0x080c1): "FalseValue",
+ ("OLD_POINTER_SPACE", 0x080d1): "UninitializedValue",
+ ("OLD_POINTER_SPACE", 0x080e1): "NoInterceptorResultSentinel",
+ ("OLD_POINTER_SPACE", 0x080f1): "ArgumentsMarker",
+ ("OLD_POINTER_SPACE", 0x08101): "NumberStringCache",
+ ("OLD_POINTER_SPACE", 0x08909): "SingleCharacterStringCache",
+ ("OLD_POINTER_SPACE", 0x08d11): "StringSplitCache",
+ ("OLD_POINTER_SPACE", 0x09119): "RegExpMultipleCache",
+ ("OLD_POINTER_SPACE", 0x09521): "TerminationException",
+ ("OLD_POINTER_SPACE", 0x09531): "MessageListeners",
+ ("OLD_POINTER_SPACE", 0x0954d): "CodeStubs",
+ ("OLD_POINTER_SPACE", 0x0a9d9): "NonMonomorphicCache",
+ ("OLD_POINTER_SPACE", 0x0afed): "PolymorphicCodeCache",
+ ("OLD_POINTER_SPACE", 0x0aff5): "NativesSourceCache",
+ ("OLD_POINTER_SPACE", 0x0b035): "EmptyScript",
+ ("OLD_POINTER_SPACE", 0x0b06d): "IntrinsicFunctionNames",
+ ("OLD_POINTER_SPACE", 0x0e089): "ObservationState",
+ ("OLD_POINTER_SPACE", 0x0e095): "FrozenSymbol",
+ ("OLD_POINTER_SPACE", 0x0e0a1): "ElementsTransitionSymbol",
+ ("OLD_POINTER_SPACE", 0x0e0ad): "EmptySlowElementDictionary",
+ ("OLD_POINTER_SPACE", 0x0e249): "ObservedSymbol",
+ ("OLD_POINTER_SPACE", 0x274e9): "StringTable",
+ ("OLD_DATA_SPACE", 0x08099): "EmptyDescriptorArray",
+ ("OLD_DATA_SPACE", 0x080a1): "EmptyFixedArray",
+ ("OLD_DATA_SPACE", 0x080a9): "NanValue",
+ ("OLD_DATA_SPACE", 0x08141): "EmptyByteArray",
+ ("OLD_DATA_SPACE", 0x08269): "EmptyExternalByteArray",
+ ("OLD_DATA_SPACE", 0x08275): "EmptyExternalUnsignedByteArray",
+ ("OLD_DATA_SPACE", 0x08281): "EmptyExternalShortArray",
+ ("OLD_DATA_SPACE", 0x0828d): "EmptyExternalUnsignedShortArray",
+ ("OLD_DATA_SPACE", 0x08299): "EmptyExternalIntArray",
+ ("OLD_DATA_SPACE", 0x082a5): "EmptyExternalUnsignedIntArray",
+ ("OLD_DATA_SPACE", 0x082b1): "EmptyExternalFloatArray",
+ ("OLD_DATA_SPACE", 0x082bd): "EmptyExternalDoubleArray",
+ ("OLD_DATA_SPACE", 0x082c9): "EmptyExternalPixelArray",
+ ("OLD_DATA_SPACE", 0x082d5): "InfinityValue",
+ ("OLD_DATA_SPACE", 0x082e1): "MinusZeroValue",
+ ("CODE_SPACE", 0x10d01): "JsConstructEntryCode",
+ ("CODE_SPACE", 0x183c1): "JsEntryCode",
+}
diff --git a/chromium/v8/tools/v8heapconst.py.tmpl b/chromium/v8/tools/v8heapconst.py.tmpl
new file mode 100644
index 00000000000..a773f47c8b9
--- /dev/null
+++ b/chromium/v8/tools/v8heapconst.py.tmpl
@@ -0,0 +1,30 @@
+# Copyright 2013 the V8 project authors. 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 is automatically generated from the V8 source and should not
+# be modified manually, run 'make grokdump' instead to update this file.
+
diff --git a/chromium/v8/tools/visual_studio/README.txt b/chromium/v8/tools/visual_studio/README.txt
new file mode 100644
index 00000000000..b199e181fcd
--- /dev/null
+++ b/chromium/v8/tools/visual_studio/README.txt
@@ -0,0 +1,12 @@
+The Microsoft Visual Studio project files for including V8 in a Visual
+Studio/Visual C++ Express solution has been retired. If a Visual
+Studio project/solution is needed there is the option of using GYP to
+generate these. Please look in the build directory in the root of the
+V8 project. It contains the required infrastructure and a README.txt
+file explaining how to get started.
+
+Generating Visual Studio projects using GYP is how the Chromium
+project integrated V8 into the Windows build.
+
+The main build system for V8 is still SCons, see the V8 wiki page
+http://code.google.com/p/v8/wiki/BuildingOnWindows for details.
diff --git a/chromium/v8/tools/windows-tick-processor.bat b/chromium/v8/tools/windows-tick-processor.bat
new file mode 100755
index 00000000000..d67f0471fea
--- /dev/null
+++ b/chromium/v8/tools/windows-tick-processor.bat
@@ -0,0 +1,30 @@
+@echo off
+
+SET tools_dir=%~dp0
+IF 1%D8_PATH% == 1 (SET D8_PATH=%tools_dir%..)
+
+SET log_file=v8.log
+
+rem find the name of the log file to process, it must not start with a dash.
+rem we prepend cmdline args with a number (in fact, any letter or number)
+rem to cope with empty arguments.
+SET arg1=1%1
+IF NOT %arg1:~0,2% == 1 (IF NOT %arg1:~0,2% == 1- SET log_file=%1)
+SET arg2=2%2
+IF NOT %arg2:~0,2% == 2 (IF NOT %arg2:~0,2% == 2- SET log_file=%2)
+SET arg3=3%3
+IF NOT %arg3:~0,2% == 3 (IF NOT %arg3:~0,2% == 3- SET log_file=%3)
+SET arg4=4%4
+IF NOT %arg4:~0,2% == 4 (IF NOT %arg4:~0,2% == 4- SET log_file=%4)
+SET arg5=5%5
+IF NOT %arg5:~0,2% == 5 (IF NOT %arg5:~0,2% == 5- SET log_file=%5)
+SET arg6=6%6
+IF NOT %arg6:~0,2% == 6 (IF NOT %arg6:~0,2% == 6- SET log_file=%6)
+SET arg7=7%7
+IF NOT %arg7:~0,2% == 7 (IF NOT %arg7:~0,2% == 7- SET log_file=%7)
+SET arg8=8%8
+IF NOT %arg8:~0,2% == 8 (IF NOT %arg8:~0,2% == 8- SET log_file=%8)
+SET arg9=9%9
+IF NOT %arg9:~0,2% == 9 (IF NOT %arg9:~0,2% == 9- SET log_file=%9)
+
+type %log_file% | %D8_PATH%\d8 %tools_dir%splaytree.js %tools_dir%codemap.js %tools_dir%csvparser.js %tools_dir%consarray.js %tools_dir%profile.js %tools_dir%profile_view.js %tools_dir%logreader.js %tools_dir%tickprocessor.js %tools_dir%tickprocessor-driver.js -- --windows %*